summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945
Print done when done.
-rw-r--r--indra/lib/python/indra/__init__.py6
-rw-r--r--indra/linux_crash_logger/linux_crash_logger.cpp553
-rw-r--r--indra/llaudio/llaudiodecodemgr.cpp616
-rw-r--r--indra/llaudio/llaudiodecodemgr.h47
-rw-r--r--indra/llcharacter/llanimationstates.cpp319
-rw-r--r--indra/llcharacter/llanimationstates.h227
-rw-r--r--indra/llcharacter/llbvhloader.cpp1489
-rw-r--r--indra/llcharacter/llbvhloader.h282
-rw-r--r--indra/llcharacter/llcharacter.cpp472
-rw-r--r--indra/llcharacter/llcharacter.h252
-rw-r--r--indra/llcharacter/lleditingmotion.cpp234
-rw-r--r--indra/llcharacter/lleditingmotion.h115
-rw-r--r--indra/llcharacter/llgesture.cpp356
-rw-r--r--indra/llcharacter/llgesture.h96
-rw-r--r--indra/llcharacter/llhandmotion.cpp202
-rw-r--r--indra/llcharacter/llhandmotion.h119
-rw-r--r--indra/llcharacter/llheadrotmotion.cpp505
-rw-r--r--indra/llcharacter/llheadrotmotion.h194
-rw-r--r--indra/llcharacter/lljoint.cpp504
-rw-r--r--indra/llcharacter/lljoint.h163
-rw-r--r--indra/llcharacter/lljointsolverrp3.cpp366
-rw-r--r--indra/llcharacter/lljointsolverrp3.h158
-rw-r--r--indra/llcharacter/lljointstate.h106
-rw-r--r--indra/llcharacter/llkeyframefallmotion.cpp123
-rw-r--r--indra/llcharacter/llkeyframefallmotion.h60
-rw-r--r--indra/llcharacter/llkeyframemotion.cpp2112
-rw-r--r--indra/llcharacter/llkeyframemotion.h437
-rw-r--r--indra/llcharacter/llkeyframemotionparam.cpp442
-rw-r--r--indra/llcharacter/llkeyframemotionparam.h138
-rw-r--r--indra/llcharacter/llkeyframestandmotion.cpp320
-rw-r--r--indra/llcharacter/llkeyframestandmotion.h98
-rw-r--r--indra/llcharacter/llkeyframewalkmotion.cpp379
-rw-r--r--indra/llcharacter/llkeyframewalkmotion.h158
-rw-r--r--indra/llcharacter/llmotion.cpp131
-rw-r--r--indra/llcharacter/llmotion.h237
-rw-r--r--indra/llcharacter/llmotioncontroller.cpp927
-rw-r--r--indra/llcharacter/llmotioncontroller.h207
-rw-r--r--indra/llcharacter/llmultigesture.cpp478
-rw-r--r--indra/llcharacter/llmultigesture.h213
-rw-r--r--indra/llcharacter/llpose.cpp544
-rw-r--r--indra/llcharacter/llpose.h120
-rw-r--r--indra/llcharacter/llstatemachine.cpp378
-rw-r--r--indra/llcharacter/llstatemachine.h130
-rw-r--r--indra/llcharacter/lltargetingmotion.cpp151
-rw-r--r--indra/llcharacter/lltargetingmotion.h98
-rw-r--r--indra/llcharacter/llvisualparam.cpp266
-rw-r--r--indra/llcharacter/llvisualparam.h131
-rw-r--r--indra/llcommon/bitpack.cpp148
-rw-r--r--indra/llcommon/bitpack.h190
-rw-r--r--indra/llcommon/ctype_workaround.h36
-rw-r--r--indra/llcommon/doublelinkedlist.h1379
-rw-r--r--indra/llcommon/imageids.h79
-rw-r--r--indra/llcommon/indra_constants.h338
-rw-r--r--indra/llcommon/linden_common.h53
-rw-r--r--indra/llcommon/linked_lists.h919
-rw-r--r--indra/llcommon/llagentconstants.h141
-rw-r--r--indra/llcommon/llapp.cpp616
-rw-r--r--indra/llcommon/llapp.h259
-rw-r--r--indra/llcommon/llapr.cpp201
-rw-r--r--indra/llcommon/llapr.h129
-rw-r--r--indra/llcommon/llassettype.cpp219
-rw-r--r--indra/llcommon/llassettype.h149
-rw-r--r--indra/llcommon/llassoclist.h278
-rw-r--r--indra/llcommon/llavatarconstants.h23
-rw-r--r--indra/llcommon/llboost.h25
-rw-r--r--indra/llcommon/llchat.h69
-rw-r--r--indra/llcommon/llclickaction.h20
-rw-r--r--indra/llcommon/llcommon.cpp43
-rw-r--r--indra/llcommon/llcommon.h28
-rw-r--r--indra/llcommon/llcriticaldamp.cpp72
-rw-r--r--indra/llcommon/llcriticaldamp.h35
-rw-r--r--indra/llcommon/lldarray.h192
-rw-r--r--indra/llcommon/lldarrayptr.h18
-rw-r--r--indra/llcommon/lldate.cpp174
-rw-r--r--indra/llcommon/lldate.h102
-rw-r--r--indra/llcommon/lldefs.h185
-rw-r--r--indra/llcommon/lldepthstack.h81
-rw-r--r--indra/llcommon/lldlinked.h77
-rw-r--r--indra/llcommon/lldqueueptr.h334
-rw-r--r--indra/llcommon/llendianswizzle.h76
-rw-r--r--indra/llcommon/llenum.h60
-rw-r--r--indra/llcommon/llerror.cpp40
-rw-r--r--indra/llcommon/llerror.h218
-rw-r--r--indra/llcommon/llerrorthread.cpp189
-rw-r--r--indra/llcommon/llerrorthread.h28
-rw-r--r--indra/llcommon/llevent.cpp285
-rw-r--r--indra/llcommon/llevent.h178
-rw-r--r--indra/llcommon/lleventemitter.h84
-rw-r--r--indra/llcommon/llfasttimer.h187
-rw-r--r--indra/llcommon/llfile.cpp251
-rw-r--r--indra/llcommon/llfile.h151
-rw-r--r--indra/llcommon/llfixedbuffer.cpp71
-rw-r--r--indra/llcommon/llfixedbuffer.h44
-rw-r--r--indra/llcommon/llframetimer.cpp69
-rw-r--r--indra/llcommon/llframetimer.h122
-rw-r--r--indra/llcommon/llhash.h45
-rw-r--r--indra/llcommon/llindexedqueue.h137
-rw-r--r--indra/llcommon/lllinkedqueue.h291
-rw-r--r--indra/llcommon/lllivefile.cpp74
-rw-r--r--indra/llcommon/lllivefile.h34
-rw-r--r--indra/llcommon/lllocalidhashmap.h877
-rw-r--r--indra/llcommon/lllslconstants.h139
-rw-r--r--indra/llcommon/llmap.h231
-rw-r--r--indra/llcommon/llmemory.cpp293
-rw-r--r--indra/llcommon/llmemory.h300
-rw-r--r--indra/llcommon/llmemorystream.cpp52
-rw-r--r--indra/llcommon/llmemorystream.h63
-rw-r--r--indra/llcommon/llmemtype.h135
-rw-r--r--indra/llcommon/llmortician.cpp51
-rw-r--r--indra/llcommon/llmortician.h33
-rw-r--r--indra/llcommon/llnametable.h87
-rw-r--r--indra/llcommon/llpreprocessor.h99
-rw-r--r--indra/llcommon/llpriqueuemap.h127
-rw-r--r--indra/llcommon/llprocessor.cpp2125
-rw-r--r--indra/llcommon/llprocessor.h158
-rw-r--r--indra/llcommon/llptrskiplist.h704
-rw-r--r--indra/llcommon/llptrskipmap.h1219
-rw-r--r--indra/llcommon/llqueuedthread.cpp491
-rw-r--r--indra/llcommon/llqueuedthread.h202
-rw-r--r--indra/llcommon/llrun.cpp156
-rw-r--r--indra/llcommon/llrun.h144
-rw-r--r--indra/llcommon/llsd.cpp731
-rw-r--r--indra/llcommon/llsd.h366
-rw-r--r--indra/llcommon/llsdserialize.cpp1621
-rw-r--r--indra/llcommon/llsdserialize.h592
-rw-r--r--indra/llcommon/llsdserialize_xml.cpp706
-rw-r--r--indra/llcommon/llsdserialize_xml.h17
-rw-r--r--indra/llcommon/llsdutil.cpp177
-rw-r--r--indra/llcommon/llsdutil.h50
-rw-r--r--indra/llcommon/llsecondlifeurls.cpp63
-rw-r--r--indra/llcommon/llsecondlifeurls.h64
-rw-r--r--indra/llcommon/llsimplehash.h137
-rw-r--r--indra/llcommon/llskiplist.h502
-rw-r--r--indra/llcommon/llskipmap.h1004
-rw-r--r--indra/llcommon/llstack.h30
-rw-r--r--indra/llcommon/llstat.cpp803
-rw-r--r--indra/llcommon/llstat.h182
-rw-r--r--indra/llcommon/llstatenums.h40
-rw-r--r--indra/llcommon/llstl.h452
-rw-r--r--indra/llcommon/llstreamtools.cpp551
-rw-r--r--indra/llcommon/llstreamtools.h98
-rw-r--r--indra/llcommon/llstrider.h38
-rw-r--r--indra/llcommon/llstring.cpp835
-rw-r--r--indra/llcommon/llstring.h1281
-rw-r--r--indra/llcommon/llstringtable.cpp323
-rw-r--r--indra/llcommon/llstringtable.h208
-rw-r--r--indra/llcommon/llsys.cpp534
-rw-r--r--indra/llcommon/llsys.h91
-rw-r--r--indra/llcommon/llthread.cpp330
-rw-r--r--indra/llcommon/llthread.h164
-rw-r--r--indra/llcommon/lltimer.cpp517
-rw-r--r--indra/llcommon/lltimer.h142
-rw-r--r--indra/llcommon/lluri.cpp579
-rw-r--r--indra/llcommon/lluri.h72
-rw-r--r--indra/llcommon/lluuidhashmap.h557
-rw-r--r--indra/llcommon/llworkerthread.cpp284
-rw-r--r--indra/llcommon/llworkerthread.h168
-rw-r--r--indra/llcommon/metaclass.cpp60
-rw-r--r--indra/llcommon/metaclass.h64
-rw-r--r--indra/llcommon/metaclasst.h42
-rw-r--r--indra/llcommon/metaproperty.cpp35
-rw-r--r--indra/llcommon/metaproperty.h55
-rw-r--r--indra/llcommon/metapropertyt.h165
-rw-r--r--indra/llcommon/reflective.cpp20
-rw-r--r--indra/llcommon/reflective.h24
-rw-r--r--indra/llcommon/reflectivet.h30
-rw-r--r--indra/llcommon/roles_constants.h168
-rw-r--r--indra/llcommon/stdenums.h115
-rw-r--r--indra/llcommon/stdtypes.h89
-rw-r--r--indra/llcommon/string_table.h8
-rw-r--r--indra/llcommon/timer.h8
-rw-r--r--indra/llcommon/timing.cpp7
-rw-r--r--indra/llcommon/timing.h26
-rw-r--r--indra/llcommon/u64.cpp91
-rw-r--r--indra/llcommon/u64.h19
-rw-r--r--indra/llimage/llimage.cpp1772
-rw-r--r--indra/llimage/llimage.h298
-rw-r--r--indra/llimage/llimagebmp.cpp624
-rw-r--r--indra/llimage/llimagebmp.h45
-rw-r--r--indra/llimage/llimagedxt.cpp475
-rw-r--r--indra/llimage/llimagedxt.h120
-rw-r--r--indra/llimage/llimagej2c.cpp249
-rw-r--r--indra/llimage/llimagej2c.h77
-rw-r--r--indra/llimage/llimagejpeg.cpp602
-rw-r--r--indra/llimage/llimagejpeg.h63
-rw-r--r--indra/llimage/llimagetga.cpp1090
-rw-r--r--indra/llimage/llimagetga.h89
-rw-r--r--indra/llimage/llmapimagetype.h22
-rw-r--r--indra/llinventory/llcategory.cpp161
-rw-r--r--indra/llinventory/llcategory.h80
-rw-r--r--indra/llinventory/lleconomy.cpp227
-rw-r--r--indra/llinventory/lleconomy.h116
-rw-r--r--indra/llinventory/llinventory.cpp1773
-rw-r--r--indra/llinventory/llinventory.h411
-rw-r--r--indra/llinventory/llnotecard.cpp286
-rw-r--r--indra/llinventory/llnotecard.h41
-rw-r--r--indra/llinventory/llparcel.cpp1813
-rw-r--r--indra/llinventory/llparcel.h576
-rw-r--r--indra/llinventory/llparcelflags.h105
-rw-r--r--indra/llinventory/llpermissions.cpp1171
-rw-r--r--indra/llinventory/llpermissions.h426
-rw-r--r--indra/llinventory/llpermissionsflags.h78
-rw-r--r--indra/llinventory/llsaleinfo.cpp356
-rw-r--r--indra/llinventory/llsaleinfo.h110
-rw-r--r--indra/llinventory/lltransactionflags.cpp43
-rw-r--r--indra/llinventory/lltransactionflags.h27
-rw-r--r--indra/llinventory/lltransactiontypes.h93
-rw-r--r--indra/llinventory/lluserrelations.cpp89
-rw-r--r--indra/llinventory/lluserrelations.h154
-rw-r--r--indra/llmath/camera.h9
-rw-r--r--indra/llmath/coordframe.h9
-rw-r--r--indra/llmath/llbboxlocal.cpp37
-rw-r--r--indra/llmath/llbboxlocal.h50
-rw-r--r--indra/llmath/llcamera.cpp591
-rw-r--r--indra/llmath/llcamera.h169
-rw-r--r--indra/llmath/llcoord.h78
-rw-r--r--indra/llmath/llcoordframe.cpp729
-rw-r--r--indra/llmath/llcoordframe.h156
-rw-r--r--indra/llmath/llinterp.h400
-rw-r--r--indra/llmath/llmath.h402
-rw-r--r--indra/llmath/lloctree.h693
-rw-r--r--indra/llmath/llperlin.cpp276
-rw-r--r--indra/llmath/llperlin.h28
-rw-r--r--indra/llmath/llplane.h49
-rw-r--r--indra/llmath/llquantize.h103
-rw-r--r--indra/llmath/llquaternion.cpp830
-rw-r--r--indra/llmath/llquaternion.h442
-rw-r--r--indra/llmath/llrect.cpp10
-rw-r--r--indra/llmath/llrect.h270
-rw-r--r--indra/llmath/lltreenode.h161
-rw-r--r--indra/llmath/llvolume.cpp4557
-rw-r--r--indra/llmath/llvolume.h882
-rw-r--r--indra/llmath/llvolumemgr.cpp295
-rw-r--r--indra/llmath/llvolumemgr.h82
-rw-r--r--indra/llmath/m3math.cpp530
-rw-r--r--indra/llmath/m3math.h198
-rw-r--r--indra/llmath/m4math.cpp794
-rw-r--r--indra/llmath/m4math.h365
-rw-r--r--indra/llmath/raytrace.cpp1256
-rw-r--r--indra/llmath/raytrace.h214
-rw-r--r--indra/llmath/v2math.cpp92
-rw-r--r--indra/llmath/v2math.h307
-rw-r--r--indra/llmath/v3color.cpp44
-rw-r--r--indra/llmath/v3color.h362
-rw-r--r--indra/llmath/v3dmath.cpp129
-rw-r--r--indra/llmath/v3dmath.h409
-rw-r--r--indra/llmath/v3math.cpp213
-rw-r--r--indra/llmath/v3math.h446
-rw-r--r--indra/llmath/v4color.cpp561
-rw-r--r--indra/llmath/v4color.h539
-rw-r--r--indra/llmath/v4coloru.cpp102
-rw-r--r--indra/llmath/v4coloru.h501
-rw-r--r--indra/llmath/v4math.cpp124
-rw-r--r--indra/llmath/v4math.h385
-rw-r--r--indra/llmath/xform.cpp96
-rw-r--r--indra/llmath/xform.h269
-rw-r--r--indra/llmessage/llassetstorage.cpp1116
-rw-r--r--indra/llmessage/llassetstorage.h328
-rw-r--r--indra/llmessage/llbuffer.cpp746
-rw-r--r--indra/llmessage/llbuffer.h493
-rw-r--r--indra/llmessage/llbufferstream.cpp265
-rw-r--r--indra/llmessage/llbufferstream.h134
-rw-r--r--indra/llmessage/llcachename.cpp763
-rw-r--r--indra/llmessage/llcachename.h90
-rw-r--r--indra/llmessage/llchainio.cpp70
-rw-r--r--indra/llmessage/llchainio.h117
-rw-r--r--indra/llmessage/llcircuit.cpp1382
-rw-r--r--indra/llmessage/llcircuit.h315
-rw-r--r--indra/llmessage/llclassifiedflags.cpp49
-rw-r--r--indra/llmessage/llclassifiedflags.h31
-rw-r--r--indra/llmessage/lldatapacker.cpp1866
-rw-r--r--indra/llmessage/lldatapacker.h398
-rw-r--r--indra/llmessage/lldbstrings.h208
-rw-r--r--indra/llmessage/lldispatcher.cpp126
-rw-r--r--indra/llmessage/lldispatcher.h95
-rw-r--r--indra/llmessage/lleventflags.h17
-rw-r--r--indra/llmessage/llfiltersd2xmlrpc.cpp728
-rw-r--r--indra/llmessage/llfiltersd2xmlrpc.h253
-rw-r--r--indra/llmessage/llfollowcamparams.h43
-rw-r--r--indra/llmessage/llhost.cpp216
-rw-r--r--indra/llmessage/llhost.h133
-rw-r--r--indra/llmessage/llhttpassetstorage.cpp996
-rw-r--r--indra/llmessage/llhttpassetstorage.h116
-rw-r--r--indra/llmessage/llhttpclient.cpp278
-rw-r--r--indra/llmessage/llhttpclient.h83
-rw-r--r--indra/llmessage/llhttpnode.cpp449
-rw-r--r--indra/llmessage/llhttpnode.h306
-rw-r--r--indra/llmessage/llinstantmessage.cpp299
-rw-r--r--indra/llmessage/llinstantmessage.h308
-rw-r--r--indra/llmessage/llinvite.h15
-rw-r--r--indra/llmessage/lliobuffer.cpp96
-rw-r--r--indra/llmessage/lliobuffer.h117
-rw-r--r--indra/llmessage/lliohttpserver.cpp833
-rw-r--r--indra/llmessage/lliohttpserver.h95
-rw-r--r--indra/llmessage/lliopipe.cpp93
-rw-r--r--indra/llmessage/lliopipe.h291
-rw-r--r--indra/llmessage/lliosocket.cpp596
-rw-r--r--indra/llmessage/lliosocket.h355
-rw-r--r--indra/llmessage/llioutil.cpp76
-rw-r--r--indra/llmessage/llioutil.h154
-rw-r--r--indra/llmessage/llloginflags.h37
-rw-r--r--indra/llmessage/llmail.cpp285
-rw-r--r--indra/llmessage/llmail.h66
-rw-r--r--indra/llmessage/llmessagethrottle.cpp135
-rw-r--r--indra/llmessage/llmessagethrottle.h62
-rw-r--r--indra/llmessage/llmime.cpp613
-rw-r--r--indra/llmessage/llmime.h274
-rw-r--r--indra/llmessage/llnamevalue.cpp2141
-rw-r--r--indra/llmessage/llnamevalue.h186
-rw-r--r--indra/llmessage/llnullcipher.cpp40
-rw-r--r--indra/llmessage/llpacketack.h143
-rw-r--r--indra/llmessage/llpacketbuffer.cpp75
-rw-r--r--indra/llmessage/llpacketbuffer.h37
-rw-r--r--indra/llmessage/llpacketring.cpp293
-rw-r--r--indra/llmessage/llpacketring.h74
-rw-r--r--indra/llmessage/llpartdata.cpp307
-rw-r--r--indra/llmessage/llpartdata.h217
-rw-r--r--indra/llmessage/llpumpio.cpp1006
-rw-r--r--indra/llmessage/llpumpio.h406
-rw-r--r--indra/llmessage/llqueryflags.h32
-rw-r--r--indra/llmessage/llregionflags.h157
-rw-r--r--indra/llmessage/llregionhandle.h110
-rw-r--r--indra/llmessage/llsdappservices.cpp259
-rw-r--r--indra/llmessage/llsdappservices.h40
-rw-r--r--indra/llmessage/llsdhttpserver.cpp132
-rw-r--r--indra/llmessage/llsdhttpserver.h33
-rw-r--r--indra/llmessage/llsdrpcclient.cpp230
-rw-r--r--indra/llmessage/llsdrpcclient.h291
-rw-r--r--indra/llmessage/llsdrpcserver.cpp322
-rw-r--r--indra/llmessage/llsdrpcserver.h342
-rw-r--r--indra/llmessage/llservice.cpp93
-rw-r--r--indra/llmessage/llservice.h167
-rw-r--r--indra/llmessage/lltaskname.h42
-rw-r--r--indra/llmessage/llteleportflags.h43
-rw-r--r--indra/llmessage/llthrottle.cpp542
-rw-r--r--indra/llmessage/llthrottle.h81
-rw-r--r--indra/llmessage/lltransfermanager.cpp1270
-rw-r--r--indra/llmessage/lltransfermanager.h466
-rw-r--r--indra/llmessage/lltransfersourceasset.cpp235
-rw-r--r--indra/llmessage/lltransfersourceasset.h62
-rw-r--r--indra/llmessage/lltransfersourcefile.cpp151
-rw-r--r--indra/llmessage/lltransfersourcefile.h58
-rw-r--r--indra/llmessage/lltransfertargetfile.cpp104
-rw-r--r--indra/llmessage/lltransfertargetfile.h53
-rw-r--r--indra/llmessage/lltransfertargetvfile.cpp187
-rw-r--r--indra/llmessage/lltransfertargetvfile.h70
-rw-r--r--indra/llmessage/llurlrequest.cpp650
-rw-r--r--indra/llmessage/llurlrequest.h395
-rw-r--r--indra/llmessage/lluseroperation.cpp161
-rw-r--r--indra/llmessage/lluseroperation.h75
-rw-r--r--indra/llmessage/llvehicleparams.h105
-rw-r--r--indra/llmessage/llxfer.cpp350
-rw-r--r--indra/llmessage/llxfer.h98
-rw-r--r--indra/llmessage/llxfer_file.cpp416
-rw-r--r--indra/llmessage/llxfer_file.h68
-rw-r--r--indra/llmessage/llxfer_mem.cpp199
-rw-r--r--indra/llmessage/llxfer_mem.h61
-rw-r--r--indra/llmessage/llxfer_vfile.cpp320
-rw-r--r--indra/llmessage/llxfer_vfile.h74
-rw-r--r--indra/llmessage/llxfermanager.cpp1133
-rw-r--r--indra/llmessage/llxfermanager.h187
-rw-r--r--indra/llmessage/llxorcipher.cpp108
-rw-r--r--indra/llmessage/machine.h101
-rw-r--r--indra/llmessage/mean_collision_data.h81
-rw-r--r--indra/llmessage/message.cpp5876
-rw-r--r--indra/llmessage/message.h1253
-rw-r--r--indra/llmessage/message_prehash.cpp2964
-rw-r--r--indra/llmessage/message_prehash.h1498
-rw-r--r--indra/llmessage/message_string_table.cpp75
-rw-r--r--indra/llmessage/net.cpp516
-rw-r--r--indra/llmessage/net.h50
-rw-r--r--indra/llmessage/partsyspacket.cpp1277
-rw-r--r--indra/llmessage/partsyspacket.h243
-rw-r--r--indra/llmessage/patch_code.cpp390
-rw-r--r--indra/llmessage/patch_code.h28
-rw-r--r--indra/llmessage/patch_dct.cpp751
-rw-r--r--indra/llmessage/patch_dct.h73
-rw-r--r--indra/llmessage/patch_idct.cpp666
-rw-r--r--indra/llmessage/sound_ids.h293
-rw-r--r--indra/llprimitive/legacy_object_types.h59
-rw-r--r--indra/llprimitive/llmaterialtable.cpp657
-rw-r--r--indra/llprimitive/llmaterialtable.h116
-rw-r--r--indra/llprimitive/llprimitive.cpp1749
-rw-r--r--indra/llprimitive/llprimitive.h510
-rw-r--r--indra/llprimitive/lltextureanim.cpp221
-rw-r--r--indra/llprimitive/lltextureanim.h54
-rw-r--r--indra/llprimitive/lltextureentry.cpp348
-rw-r--r--indra/llprimitive/lltextureentry.h126
-rw-r--r--indra/llprimitive/lltreeparams.cpp187
-rw-r--r--indra/llprimitive/lltreeparams.h183
-rw-r--r--indra/llprimitive/llvolumemessage.cpp534
-rw-r--r--indra/llprimitive/llvolumemessage.h74
-rw-r--r--indra/llprimitive/llvolumexml.cpp57
-rw-r--r--indra/llprimitive/llvolumexml.h27
-rw-r--r--indra/llprimitive/material_codes.h35
-rw-r--r--indra/llrender/llfontgl.cpp1434
-rw-r--r--indra/llrender/llfontgl.h233
-rw-r--r--indra/llrender/llgldbg.cpp204
-rw-r--r--indra/llrender/llgldbg.h16
-rw-r--r--indra/llrender/llimagegl.cpp1205
-rw-r--r--indra/llrender/llimagegl.h185
-rw-r--r--indra/llui/llbutton.cpp1012
-rw-r--r--indra/llui/llbutton.h261
-rw-r--r--indra/llui/llcallbackmap.h36
-rw-r--r--indra/llui/llcheckboxctrl.cpp315
-rw-r--r--indra/llui/llcheckboxctrl.h112
-rw-r--r--indra/llui/llclipboard.cpp71
-rw-r--r--indra/llui/llclipboard.h41
-rw-r--r--indra/llui/llcombobox.cpp1133
-rw-r--r--indra/llui/llcombobox.h178
-rw-r--r--indra/llui/llctrlselectioninterface.cpp44
-rw-r--r--indra/llui/llctrlselectioninterface.h84
-rw-r--r--indra/llui/lldraghandle.cpp353
-rw-r--r--indra/llui/lldraghandle.h98
-rw-r--r--indra/llui/lleditmenuhandler.cpp107
-rw-r--r--indra/llui/lleditmenuhandler.h50
-rw-r--r--indra/llui/llfloater.cpp2933
-rw-r--r--indra/llui/llfloater.h399
-rw-r--r--indra/llui/llfocusmgr.cpp369
-rw-r--r--indra/llui/llfocusmgr.h102
-rw-r--r--indra/llui/lliconctrl.cpp139
-rw-r--r--indra/llui/lliconctrl.h55
-rw-r--r--indra/llui/llkeywords.cpp502
-rw-r--r--indra/llui/llkeywords.h91
-rw-r--r--indra/llui/lllineeditor.cpp2301
-rw-r--r--indra/llui/lllineeditor.h298
-rw-r--r--indra/llui/llmenugl.cpp4341
-rw-r--r--indra/llui/llmenugl.h728
-rw-r--r--indra/llui/llmodaldialog.cpp288
-rw-r--r--indra/llui/llmodaldialog.h60
-rw-r--r--indra/llui/llpanel.cpp1030
-rw-r--r--indra/llui/llpanel.h229
-rw-r--r--indra/llui/llradiogroup.cpp440
-rw-r--r--indra/llui/llradiogroup.h102
-rw-r--r--indra/llui/llresizebar.cpp257
-rw-r--r--indra/llui/llresizebar.h47
-rw-r--r--indra/llui/llresizehandle.cpp321
-rw-r--r--indra/llui/llresizehandle.h56
-rw-r--r--indra/llui/llresmgr.cpp447
-rw-r--r--indra/llui/llresmgr.h147
-rw-r--r--indra/llui/llscrollbar.cpp618
-rw-r--r--indra/llui/llscrollbar.h123
-rw-r--r--indra/llui/llscrollcontainer.cpp772
-rw-r--r--indra/llui/llscrollcontainer.h109
-rw-r--r--indra/llui/llscrollingpanellist.cpp150
-rw-r--r--indra/llui/llscrollingpanellist.h53
-rw-r--r--indra/llui/llscrolllistctrl.cpp2673
-rw-r--r--indra/llui/llscrolllistctrl.h527
-rw-r--r--indra/llui/llslider.cpp352
-rw-r--r--indra/llui/llslider.h79
-rw-r--r--indra/llui/llsliderctrl.cpp538
-rw-r--r--indra/llui/llsliderctrl.h124
-rw-r--r--indra/llui/llspinctrl.cpp509
-rw-r--r--indra/llui/llspinctrl.h121
-rw-r--r--indra/llui/llstyle.cpp223
-rw-r--r--indra/llui/llstyle.h75
-rw-r--r--indra/llui/lltabcontainer.cpp1469
-rw-r--r--indra/llui/lltabcontainer.h242
-rw-r--r--indra/llui/lltextbox.cpp438
-rw-r--r--indra/llui/lltextbox.h106
-rw-r--r--indra/llui/lltexteditor.cpp4144
-rw-r--r--indra/llui/lltexteditor.h483
-rw-r--r--indra/llui/llui.cpp1764
-rw-r--r--indra/llui/llui.h255
-rw-r--r--indra/llui/lluiconstants.h32
-rw-r--r--indra/llui/lluictrl.cpp341
-rw-r--r--indra/llui/lluictrl.h153
-rw-r--r--indra/llui/lluictrlfactory.cpp722
-rw-r--r--indra/llui/lluictrlfactory.h142
-rwxr-xr-xindra/llui/lluistring.cpp87
-rwxr-xr-xindra/llui/lluistring.h87
-rw-r--r--indra/llui/llundo.cpp161
-rw-r--r--indra/llui/llundo.h52
-rw-r--r--indra/llui/llview.cpp2924
-rw-r--r--indra/llui/llview.h489
-rw-r--r--indra/llui/llviewborder.cpp337
-rw-r--r--indra/llui/llviewborder.h77
-rw-r--r--indra/llui/llviewquery.cpp126
-rw-r--r--indra/llui/llviewquery.h89
-rw-r--r--indra/llvfs/lldir.cpp461
-rw-r--r--indra/llvfs/lldir.h102
-rw-r--r--indra/llvfs/lldir_linux.cpp329
-rw-r--r--indra/llvfs/lldir_linux.h41
-rw-r--r--indra/llvfs/lldir_mac.cpp362
-rw-r--r--indra/llvfs/lldir_mac.h40
-rw-r--r--indra/llvfs/lldir_win32.cpp382
-rw-r--r--indra/llvfs/lldir_win32.h37
-rw-r--r--indra/llvfs/lllfsthread.cpp308
-rw-r--r--indra/llvfs/lllfsthread.h119
-rw-r--r--indra/llvfs/llvfile.cpp415
-rw-r--r--indra/llvfs/llvfile.h75
-rw-r--r--indra/llvfs/llvfs.cpp2048
-rw-r--r--indra/llvfs/llvfs.h151
-rw-r--r--indra/llvfs/llvfsthread.cpp294
-rw-r--r--indra/llvfs/llvfsthread.h125
-rw-r--r--indra/llwindow/lldxhardware.cpp508
-rw-r--r--indra/llwindow/lldxhardware.h90
-rw-r--r--indra/llwindow/llkeyboard.cpp387
-rw-r--r--indra/llwindow/llkeyboard.h124
-rw-r--r--indra/llwindow/llkeyboardmacosx.cpp309
-rw-r--r--indra/llwindow/llkeyboardmacosx.h36
-rw-r--r--indra/llwindow/llkeyboardsdl.cpp324
-rw-r--r--indra/llwindow/llkeyboardsdl.h37
-rw-r--r--indra/llwindow/llkeyboardwin32.cpp372
-rw-r--r--indra/llwindow/llkeyboardwin32.h40
-rw-r--r--indra/llwindow/llmousehandler.h39
-rw-r--r--indra/llwindow/llwindow.cpp399
-rw-r--r--indra/llwindow/llwindow.h328
-rw-r--r--indra/llwindow/llwindowheadless.cpp32
-rw-r--r--indra/llwindow/llwindowheadless.h98
-rw-r--r--indra/llwindow/llwindowmacosx-objc.h19
-rw-r--r--indra/llwindow/llwindowmacosx-objc.mm87
-rw-r--r--indra/llwindow/llwindowmacosx.cpp2904
-rw-r--r--indra/llwindow/llwindowmacosx.h189
-rw-r--r--indra/llwindow/llwindowmesaheadless.cpp64
-rw-r--r--indra/llwindow/llwindowmesaheadless.h104
-rw-r--r--indra/llwindow/llwindowsdl.cpp2487
-rw-r--r--indra/llwindow/llwindowsdl.h198
-rw-r--r--indra/llwindow/llwindowwin32.cpp3247
-rw-r--r--indra/llwindow/llwindowwin32.h187
-rw-r--r--indra/llxml/llcontrol.cpp1401
-rw-r--r--indra/llxml/llcontrol.h255
-rw-r--r--indra/llxml/llxmlnode.cpp3008
-rw-r--r--indra/llxml/llxmlnode.h266
-rw-r--r--indra/llxml/llxmlparser.cpp398
-rw-r--r--indra/llxml/llxmlparser.h109
-rw-r--r--indra/llxml/llxmltree.cpp671
-rw-r--r--indra/llxml/llxmltree.h216
-rw-r--r--indra/lscript/lscript_alloc.h344
-rw-r--r--indra/lscript/lscript_byteconvert.h1087
-rw-r--r--indra/lscript/lscript_byteformat.h544
-rw-r--r--indra/lscript/lscript_compile/indra.l834
-rw-r--r--indra/lscript/lscript_compile/indra.y1680
-rw-r--r--indra/lscript/lscript_compile/lscript_alloc.cpp8
-rw-r--r--indra/lscript/lscript_compile/lscript_bytecode.cpp299
-rw-r--r--indra/lscript/lscript_compile/lscript_bytecode.h71
-rw-r--r--indra/lscript/lscript_compile/lscript_error.cpp77
-rw-r--r--indra/lscript/lscript_compile/lscript_error.h132
-rw-r--r--indra/lscript/lscript_compile/lscript_heap.cpp49
-rw-r--r--indra/lscript/lscript_compile/lscript_heap.h40
-rw-r--r--indra/lscript/lscript_compile/lscript_resource.cpp18
-rw-r--r--indra/lscript/lscript_compile/lscript_resource.h21
-rw-r--r--indra/lscript/lscript_compile/lscript_scope.cpp13
-rw-r--r--indra/lscript/lscript_compile/lscript_scope.h388
-rw-r--r--indra/lscript/lscript_compile/lscript_tree.cpp9998
-rw-r--r--indra/lscript/lscript_compile/lscript_tree.h2279
-rw-r--r--indra/lscript/lscript_compile/lscript_typecheck.cpp562
-rw-r--r--indra/lscript/lscript_compile/lscript_typecheck.h100
-rw-r--r--indra/lscript/lscript_execute.h364
-rw-r--r--indra/lscript/lscript_execute/lscript_execute.cpp3910
-rw-r--r--indra/lscript/lscript_execute/lscript_heapruntime.cpp501
-rw-r--r--indra/lscript/lscript_execute/lscript_heapruntime.h22
-rw-r--r--indra/lscript/lscript_execute/lscript_readlso.cpp1553
-rw-r--r--indra/lscript/lscript_execute/lscript_readlso.h147
-rw-r--r--indra/lscript/lscript_export.h16
-rw-r--r--indra/lscript/lscript_http.h27
-rw-r--r--indra/lscript/lscript_library.h382
-rw-r--r--indra/lscript/lscript_library/lscript_alloc.cpp1102
-rw-r--r--indra/lscript/lscript_library/lscript_export.cpp8
-rw-r--r--indra/lscript/lscript_library/lscript_library.cpp557
-rw-r--r--indra/lscript/lscript_rt_interface.h18
-rw-r--r--indra/mac_crash_logger/mac_crash_logger.cpp669
-rw-r--r--indra/mac_updater/AutoUpdater.nib/classes.nib4
-rw-r--r--indra/mac_updater/AutoUpdater.nib/info.nib14
-rw-r--r--indra/mac_updater/AutoUpdater.nib/objects.xib56
-rw-r--r--indra/mac_updater/mac_updater.cpp1087
-rw-r--r--indra/newview/English.lproj/InfoPlist.strings5
-rw-r--r--indra/newview/Info-SecondLife.plist39
-rw-r--r--indra/newview/Info-SecondLifeVorbis.plist28
-rw-r--r--indra/newview/SecondLife.nib/classes.nib4
-rw-r--r--indra/newview/SecondLife.nib/info.nib23
-rw-r--r--indra/newview/SecondLife.nib/objects.xib259
-rw-r--r--indra/newview/VertexCache.h87
-rw-r--r--indra/newview/VorbisFramework.h62
-rw-r--r--indra/newview/app_settings/CA.pem1811
-rw-r--r--indra/newview/app_settings/anim.ini87
-rw-r--r--indra/newview/app_settings/grass.xml47
-rw-r--r--indra/newview/app_settings/keys.ini310
-rw-r--r--indra/newview/app_settings/keywords.ini513
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl35
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl7
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl20
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl17
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/terrainF.glsl19
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/terrainV.glsl37
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/waterF.glsl22
-rw-r--r--indra/newview/app_settings/shaders/class1/environment/waterV.glsl41
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/highlightF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/interface/highlightV.glsl20
-rw-r--r--indra/newview/app_settings/shaders/class1/lighting/lightF.glsl31
-rw-r--r--indra/newview/app_settings/shaders/class1/lighting/lightV.glsl99
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/simpleF.glsl6
-rw-r--r--indra/newview/app_settings/shaders/class1/objects/simpleV.glsl21
-rw-r--r--indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl23
-rw-r--r--indra/newview/app_settings/shaders/class2/environment/waterF.glsl136
-rw-r--r--indra/newview/app_settings/shaders/class2/lighting/lightF.glsl36
-rw-r--r--indra/newview/app_settings/shaders/class2/lighting/lightV.glsl126
-rw-r--r--indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl128
-rw-r--r--indra/newview/app_settings/std_bump.ini18
-rw-r--r--indra/newview/app_settings/trees.xml24
-rw-r--r--indra/newview/app_settings/viewerart.xml504
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROW.tifbin0 -> 14336 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tifbin0 -> 14376 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_NO.tifbin0 -> 14380 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tifbin0 -> 14416 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tifbin0 -> 14336 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENS.tifbin0 -> 14344 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tifbin0 -> 14340 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tifbin0 -> 14328 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tifbin0 -> 14776 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tifbin0 -> 14356 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tifbin0 -> 14348 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tifbin0 -> 14364 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tifbin0 -> 14348 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tifbin0 -> 15144 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tifbin0 -> 14368 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tifbin0 -> 14380 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tifbin0 -> 14384 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tifbin0 -> 15176 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tifbin0 -> 14388 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tifbin0 -> 14364 bytes
-rw-r--r--indra/newview/cursors_mac/UI_CURSOR_WORKING.tifbin0 -> 14392 bytes
-rw-r--r--indra/newview/featuretable.txt170
-rw-r--r--indra/newview/featuretable_mac.txt151
-rw-r--r--indra/newview/fmod_hidden_symbols.exp140
-rw-r--r--indra/newview/fmodwrapper.cpp19
-rw-r--r--indra/newview/gpu_table.txt118
-rw-r--r--indra/newview/licenses-linux.txt437
-rw-r--r--indra/newview/licenses-mac.txt388
-rw-r--r--indra/newview/licenses-win32.txt388
-rw-r--r--indra/newview/linux_tools/client-readme.txt202
-rwxr-xr-xindra/newview/linux_tools/launch_url.sh87
-rwxr-xr-xindra/newview/linux_tools/wrapper.sh50
-rw-r--r--indra/newview/llagent.cpp7233
-rw-r--r--indra/newview/llagent.h896
-rw-r--r--indra/newview/llagentdata.cpp15
-rw-r--r--indra/newview/llagentdata.h16
-rw-r--r--indra/newview/llagentpilot.cpp250
-rw-r--r--indra/newview/llagentpilot.h77
-rw-r--r--indra/newview/llappearance.h33
-rw-r--r--indra/newview/llassetuploadresponders.cpp197
-rw-r--r--indra/newview/llassetuploadresponders.h26
-rw-r--r--indra/newview/llaudiosourcevo.cpp129
-rw-r--r--indra/newview/llaudiosourcevo.h34
-rw-r--r--indra/newview/llbox.cpp105
-rw-r--r--indra/newview/llbox.h32
-rw-r--r--indra/newview/llcallbacklist.cpp173
-rw-r--r--indra/newview/llcallbacklist.h39
-rw-r--r--indra/newview/llcallingcard.cpp797
-rw-r--r--indra/newview/llcallingcard.h224
-rw-r--r--indra/newview/llchatbar.cpp706
-rw-r--r--indra/newview/llchatbar.h98
-rw-r--r--indra/newview/llclassifiedinfo.cpp49
-rw-r--r--indra/newview/llclassifiedinfo.h31
-rw-r--r--indra/newview/llcloud.cpp555
-rw-r--r--indra/newview/llcloud.h182
-rw-r--r--indra/newview/llcolorswatch.cpp386
-rw-r--r--indra/newview/llcolorswatch.h88
-rw-r--r--indra/newview/llcompilequeue.cpp756
-rw-r--r--indra/newview/llcompilequeue.h211
-rw-r--r--indra/newview/llconfirmationmanager.cpp100
-rw-r--r--indra/newview/llconfirmationmanager.h67
-rw-r--r--indra/newview/llcurrencyuimanager.cpp497
-rw-r--r--indra/newview/llcurrencyuimanager.h72
-rw-r--r--indra/newview/llcylinder.cpp308
-rw-r--r--indra/newview/llcylinder.h67
-rw-r--r--indra/newview/lldebugmessagebox.cpp226
-rw-r--r--indra/newview/lldebugmessagebox.h72
-rw-r--r--indra/newview/lldebugview.cpp133
-rw-r--r--indra/newview/lldebugview.h46
-rw-r--r--indra/newview/lldirpicker.cpp266
-rw-r--r--indra/newview/lldirpicker.h84
-rw-r--r--indra/newview/lldrawable.cpp1320
-rw-r--r--indra/newview/lldrawable.h311
-rw-r--r--indra/newview/lldrawpool.cpp1402
-rw-r--r--indra/newview/lldrawpool.h348
-rw-r--r--indra/newview/lldrawpoolalpha.cpp584
-rw-r--r--indra/newview/lldrawpoolalpha.h64
-rw-r--r--indra/newview/lldrawpoolavatar.cpp610
-rw-r--r--indra/newview/lldrawpoolavatar.h54
-rw-r--r--indra/newview/lldrawpoolbump.cpp1135
-rw-r--r--indra/newview/lldrawpoolbump.h141
-rw-r--r--indra/newview/lldrawpoolclouds.cpp87
-rw-r--r--indra/newview/lldrawpoolclouds.h28
-rw-r--r--indra/newview/lldrawpoolground.cpp85
-rw-r--r--indra/newview/lldrawpoolground.h28
-rw-r--r--indra/newview/lldrawpoolsimple.cpp227
-rw-r--r--indra/newview/lldrawpoolsimple.h74
-rw-r--r--indra/newview/lldrawpoolsky.cpp180
-rw-r--r--indra/newview/lldrawpoolsky.h42
-rw-r--r--indra/newview/lldrawpoolterrain.cpp1091
-rw-r--r--indra/newview/lldrawpoolterrain.h49
-rw-r--r--indra/newview/lldrawpooltree.cpp342
-rw-r--r--indra/newview/lldrawpooltree.h41
-rw-r--r--indra/newview/lldrawpoolwater.cpp729
-rw-r--r--indra/newview/lldrawpoolwater.h57
-rw-r--r--indra/newview/lldriverparam.cpp435
-rw-r--r--indra/newview/lldriverparam.h90
-rw-r--r--indra/newview/lldynamictexture.cpp215
-rw-r--r--indra/newview/lldynamictexture.h67
-rw-r--r--indra/newview/llemote.cpp126
-rw-r--r--indra/newview/llemote.h104
-rw-r--r--indra/newview/lleventinfo.cpp134
-rw-r--r--indra/newview/lleventinfo.h50
-rw-r--r--indra/newview/lleventnotifier.cpp308
-rw-r--r--indra/newview/lleventnotifier.h68
-rw-r--r--indra/newview/lleventpoll.cpp166
-rw-r--r--indra/newview/lleventpoll.h37
-rw-r--r--indra/newview/llface.cpp1939
-rw-r--r--indra/newview/llface.h250
-rw-r--r--indra/newview/llface.inl222
-rw-r--r--indra/newview/llfasttimerview.cpp1084
-rw-r--r--indra/newview/llfasttimerview.h52
-rw-r--r--indra/newview/llfeaturemanager.cpp842
-rw-r--r--indra/newview/llfeaturemanager.h98
-rw-r--r--indra/newview/llfilepicker.cpp1339
-rw-r--r--indra/newview/llfilepicker.h167
-rw-r--r--indra/newview/llfirstuse.cpp196
-rw-r--r--indra/newview/llfirstuse.h83
-rw-r--r--indra/newview/llflexibleobject.cpp832
-rw-r--r--indra/newview/llflexibleobject.h154
-rw-r--r--indra/newview/llfloaterabout.cpp167
-rw-r--r--indra/newview/llfloaterabout.h28
-rw-r--r--indra/newview/llfloateranimpreview.cpp1139
-rw-r--r--indra/newview/llfloateranimpreview.h108
-rw-r--r--indra/newview/llfloaterauction.cpp307
-rw-r--r--indra/newview/llfloaterauction.h53
-rw-r--r--indra/newview/llfloateravatarpicker.cpp374
-rw-r--r--indra/newview/llfloateravatarpicker.h78
-rw-r--r--indra/newview/llfloateravatartextures.cpp180
-rw-r--r--indra/newview/llfloateravatartextures.h59
-rw-r--r--indra/newview/llfloaterbuildoptions.cpp73
-rw-r--r--indra/newview/llfloaterbuildoptions.h36
-rw-r--r--indra/newview/llfloaterbump.cpp138
-rw-r--r--indra/newview/llfloaterbump.h33
-rw-r--r--indra/newview/llfloaterbuy.cpp288
-rw-r--r--indra/newview/llfloaterbuy.h53
-rw-r--r--indra/newview/llfloaterbuycontents.cpp274
-rw-r--r--indra/newview/llfloaterbuycontents.h49
-rw-r--r--indra/newview/llfloaterbuycurrency.cpp356
-rw-r--r--indra/newview/llfloaterbuycurrency.h28
-rw-r--r--indra/newview/llfloaterbuyland.cpp1342
-rw-r--r--indra/newview/llfloaterbuyland.h28
-rw-r--r--indra/newview/llfloaterchat.cpp420
-rw-r--r--indra/newview/llfloaterchat.h61
-rw-r--r--indra/newview/llfloatercolorpicker.cpp1259
-rw-r--r--indra/newview/llfloatercolorpicker.h179
-rw-r--r--indra/newview/llfloaterfriends.cpp736
-rw-r--r--indra/newview/llfloaterfriends.h124
-rw-r--r--indra/newview/llfloatergesture.cpp396
-rw-r--r--indra/newview/llfloatergesture.h64
-rw-r--r--indra/newview/llfloatergodtools.cpp1446
-rw-r--r--indra/newview/llfloatergodtools.h262
-rw-r--r--indra/newview/llfloatergroupinvite.cpp118
-rw-r--r--indra/newview/llfloatergroupinvite.h34
-rw-r--r--indra/newview/llfloatergroups.cpp454
-rw-r--r--indra/newview/llfloatergroups.h110
-rw-r--r--indra/newview/llfloaterimagepreview.cpp679
-rw-r--r--indra/newview/llfloaterimagepreview.h83
-rw-r--r--indra/newview/llfloaterland.cpp3208
-rw-r--r--indra/newview/llfloaterland.h472
-rw-r--r--indra/newview/llfloaterlandholdings.cpp305
-rw-r--r--indra/newview/llfloaterlandholdings.h62
-rw-r--r--indra/newview/llfloatermap.cpp199
-rw-r--r--indra/newview/llfloatermap.h50
-rw-r--r--indra/newview/llfloaternamedesc.cpp202
-rw-r--r--indra/newview/llfloaternamedesc.h42
-rw-r--r--indra/newview/llfloateropenobject.cpp196
-rw-r--r--indra/newview/llfloateropenobject.h56
-rw-r--r--indra/newview/llfloaterpostcard.cpp319
-rw-r--r--indra/newview/llfloaterpostcard.h58
-rw-r--r--indra/newview/llfloaterpreference.cpp365
-rw-r--r--indra/newview/llfloaterpreference.h106
-rw-r--r--indra/newview/llfloaterproperties.cpp927
-rw-r--r--indra/newview/llfloaterproperties.h82
-rw-r--r--indra/newview/llfloaterregioninfo.cpp2947
-rw-r--r--indra/newview/llfloaterregioninfo.h387
-rw-r--r--indra/newview/llfloaterreporter.cpp819
-rw-r--r--indra/newview/llfloaterreporter.h107
-rw-r--r--indra/newview/llfloaterscriptdebug.cpp230
-rw-r--r--indra/newview/llfloaterscriptdebug.h60
-rwxr-xr-xindra/newview/llfloatersellland.cpp486
-rwxr-xr-xindra/newview/llfloatersellland.h21
-rw-r--r--indra/newview/llfloatersnapshot.cpp1523
-rw-r--r--indra/newview/llfloatersnapshot.h53
-rw-r--r--indra/newview/llfloatertelehub.cpp289
-rw-r--r--indra/newview/llfloatertelehub.h57
-rw-r--r--indra/newview/llfloatertools.cpp931
-rw-r--r--indra/newview/llfloatertools.h164
-rw-r--r--indra/newview/llfloatertopobjects.cpp441
-rw-r--r--indra/newview/llfloatertopobjects.h86
-rw-r--r--indra/newview/llfloatertos.cpp269
-rw-r--r--indra/newview/llfloatertos.h64
-rw-r--r--indra/newview/llfloaterworldmap.cpp1579
-rw-r--r--indra/newview/llfloaterworldmap.h170
-rw-r--r--indra/newview/llfolderview.cpp4899
-rw-r--r--indra/newview/llfolderview.h864
-rw-r--r--indra/newview/llfollowcam.cpp882
-rw-r--r--indra/newview/llfollowcam.h216
-rw-r--r--indra/newview/llgesturemgr.cpp1067
-rw-r--r--indra/newview/llgesturemgr.h138
-rw-r--r--indra/newview/llglsandbox.cpp1222
-rw-r--r--indra/newview/llgroupmgr.cpp1816
-rw-r--r--indra/newview/llgroupmgr.h343
-rw-r--r--indra/newview/llhudeffect.cpp115
-rw-r--r--indra/newview/llhudeffect.h62
-rw-r--r--indra/newview/llhudeffectbeam.cpp371
-rw-r--r--indra/newview/llhudeffectbeam.h51
-rw-r--r--indra/newview/llhudeffectlookat.cpp501
-rw-r--r--indra/newview/llhudeffectlookat.h76
-rw-r--r--indra/newview/llhudeffectpointat.cpp432
-rw-r--r--indra/newview/llhudeffectpointat.h65
-rw-r--r--indra/newview/llhudeffecttrail.cpp271
-rw-r--r--indra/newview/llhudeffecttrail.h76
-rw-r--r--indra/newview/llhudicon.cpp253
-rw-r--r--indra/newview/llhudicon.h72
-rw-r--r--indra/newview/llhudmanager.cpp299
-rw-r--r--indra/newview/llhudmanager.h60
-rw-r--r--indra/newview/llhudobject.cpp277
-rw-r--r--indra/newview/llhudobject.h96
-rw-r--r--indra/newview/llhudrender.cpp113
-rw-r--r--indra/newview/llhudrender.h39
-rw-r--r--indra/newview/llhudtext.cpp991
-rw-r--r--indra/newview/llhudtext.h155
-rw-r--r--indra/newview/llhudview.cpp78
-rw-r--r--indra/newview/llhudview.h37
-rw-r--r--indra/newview/llimpanel.cpp793
-rw-r--r--indra/newview/llimpanel.h155
-rw-r--r--indra/newview/llimview.cpp717
-rw-r--r--indra/newview/llimview.h150
-rwxr-xr-xindra/newview/llinventorybridge.cpp4405
-rwxr-xr-xindra/newview/llinventorybridge.h579
-rw-r--r--indra/newview/llinventoryclipboard.cpp80
-rw-r--r--indra/newview/llinventoryclipboard.h65
-rw-r--r--indra/newview/llinventorymodel.cpp3493
-rw-r--r--indra/newview/llinventorymodel.h757
-rw-r--r--indra/newview/lljoystickbutton.cpp847
-rw-r--r--indra/newview/lljoystickbutton.h168
-rw-r--r--indra/newview/lllandmarklist.cpp116
-rw-r--r--indra/newview/lllandmarklist.h51
-rw-r--r--indra/newview/lllightconstants.h15
-rw-r--r--indra/newview/lllogchat.cpp82
-rw-r--r--indra/newview/lllogchat.h21
-rw-r--r--indra/newview/llmanip.cpp581
-rw-r--r--indra/newview/llmanip.h137
-rw-r--r--indra/newview/llmaniprotate.cpp1876
-rw-r--r--indra/newview/llmaniprotate.h100
-rw-r--r--indra/newview/llmanipscale.cpp2024
-rw-r--r--indra/newview/llmanipscale.h137
-rw-r--r--indra/newview/llmaniptranslate.cpp2126
-rw-r--r--indra/newview/llmaniptranslate.h97
-rw-r--r--indra/newview/llmemoryview.cpp223
-rw-r--r--indra/newview/llmemoryview.h31
-rw-r--r--indra/newview/llmenucommands.cpp135
-rw-r--r--indra/newview/llmenucommands.h27
-rw-r--r--indra/newview/llmorphview.cpp193
-rw-r--r--indra/newview/llmorphview.h68
-rw-r--r--indra/newview/llmoveview.cpp198
-rw-r--r--indra/newview/llmoveview.h68
-rw-r--r--indra/newview/llmutelist.cpp553
-rw-r--r--indra/newview/llmutelist.h128
-rw-r--r--indra/newview/llnamebox.cpp102
-rw-r--r--indra/newview/llnamebox.h46
-rw-r--r--indra/newview/llnameeditor.cpp184
-rw-r--r--indra/newview/llnameeditor.h70
-rw-r--r--indra/newview/llnamelistctrl.cpp409
-rw-r--r--indra/newview/llnamelistctrl.h71
-rw-r--r--indra/newview/llnetmap.cpp806
-rw-r--r--indra/newview/llnetmap.h104
-rw-r--r--indra/newview/lloverlaybar.cpp573
-rw-r--r--indra/newview/lloverlaybar.h77
-rw-r--r--indra/newview/llpanelavatar.cpp2347
-rw-r--r--indra/newview/llpanelavatar.h335
-rw-r--r--indra/newview/llpanelclassified.cpp852
-rw-r--r--indra/newview/llpanelclassified.h148
-rw-r--r--indra/newview/llpanelcontents.cpp189
-rw-r--r--indra/newview/llpanelcontents.h39
-rw-r--r--indra/newview/llpanelface.cpp956
-rw-r--r--indra/newview/llpanelface.h136
-rw-r--r--indra/newview/llpanelgroup.cpp651
-rw-r--r--indra/newview/llpanelgroup.h173
-rw-r--r--indra/newview/llpanelgroupgeneral.cpp771
-rw-r--r--indra/newview/llpanelgroupgeneral.h91
-rw-r--r--indra/newview/llpanelgroupinvite.cpp392
-rw-r--r--indra/newview/llpanelgroupinvite.h32
-rw-r--r--indra/newview/llpanelgrouplandmoney.cpp1387
-rw-r--r--indra/newview/llpanelgrouplandmoney.h127
-rw-r--r--indra/newview/llpanelgroupnotices.cpp565
-rw-r--r--indra/newview/llpanelgroupnotices.h98
-rw-r--r--indra/newview/llpanelgrouproles.cpp2642
-rw-r--r--indra/newview/llpanelgrouproles.h297
-rw-r--r--indra/newview/llpanelland.cpp236
-rw-r--r--indra/newview/llpanelland.h54
-rw-r--r--indra/newview/llpanellogin.cpp748
-rw-r--r--indra/newview/llpanellogin.h83
-rw-r--r--indra/newview/llpanelobject.cpp1679
-rw-r--r--indra/newview/llpanelobject.h147
-rw-r--r--indra/newview/llpanelpermissions.cpp1028
-rw-r--r--indra/newview/llpanelpermissions.h92
-rw-r--r--indra/newview/llpanelpick.cpp494
-rw-r--r--indra/newview/llpanelpick.h99
-rw-r--r--indra/newview/llpanelplace.cpp271
-rw-r--r--indra/newview/llpanelplace.h70
-rw-r--r--indra/newview/llpanelvolume.cpp499
-rw-r--r--indra/newview/llpanelvolume.h82
-rw-r--r--indra/newview/llpatchvertexarray.cpp231
-rw-r--r--indra/newview/llpatchvertexarray.h51
-rw-r--r--indra/newview/llpolymesh.cpp1117
-rw-r--r--indra/newview/llpolymesh.h412
-rw-r--r--indra/newview/llpolymorph.cpp639
-rw-r--r--indra/newview/llpolymorph.h163
-rw-r--r--indra/newview/llpreview.cpp489
-rw-r--r--indra/newview/llpreview.h139
-rw-r--r--indra/newview/llpreviewanim.cpp197
-rw-r--r--indra/newview/llpreviewanim.h39
-rw-r--r--indra/newview/llpreviewgesture.cpp1698
-rw-r--r--indra/newview/llpreviewgesture.h149
-rw-r--r--indra/newview/llpreviewnotecard.cpp579
-rw-r--r--indra/newview/llpreviewnotecard.h84
-rw-r--r--indra/newview/llpreviewscript.cpp1984
-rw-r--r--indra/newview/llpreviewscript.h212
-rw-r--r--indra/newview/llpreviewsound.cpp87
-rw-r--r--indra/newview/llpreviewsound.h26
-rw-r--r--indra/newview/llpreviewtexture.cpp365
-rw-r--r--indra/newview/llpreviewtexture.h78
-rw-r--r--indra/newview/llprogressview.cpp326
-rw-r--r--indra/newview/llprogressview.h54
-rw-r--r--indra/newview/llregionposition.cpp76
-rw-r--r--indra/newview/llregionposition.h43
-rw-r--r--indra/newview/llsavedsettingsglue.cpp51
-rw-r--r--indra/newview/llsavedsettingsglue.h28
-rw-r--r--indra/newview/llselectmgr.cpp6732
-rw-r--r--indra/newview/llselectmgr.h625
-rw-r--r--indra/newview/llsky.cpp427
-rw-r--r--indra/newview/llsky.h101
-rw-r--r--indra/newview/llspatialpartition.cpp2255
-rw-r--r--indra/newview/llspatialpartition.h209
-rw-r--r--indra/newview/llsprite.cpp296
-rw-r--r--indra/newview/llsprite.h89
-rw-r--r--indra/newview/llstartup.cpp3959
-rw-r--r--indra/newview/llstartup.h72
-rw-r--r--indra/newview/llstatusbar.cpp612
-rw-r--r--indra/newview/llstatusbar.h108
-rw-r--r--indra/newview/llsurface.cpp1337
-rw-r--r--indra/newview/llsurface.h249
-rw-r--r--indra/newview/llsurfacepatch.cpp974
-rw-r--r--indra/newview/llsurfacepatch.h158
-rw-r--r--indra/newview/lltable.h48
-rw-r--r--indra/newview/lltexlayer.cpp2839
-rw-r--r--indra/newview/lltexlayer.h566
-rw-r--r--indra/newview/lltexturectrl.cpp1368
-rw-r--r--indra/newview/lltexturectrl.h158
-rw-r--r--indra/newview/lltexturefetch.cpp744
-rw-r--r--indra/newview/lltexturefetch.h28
-rw-r--r--indra/newview/lltextureview.cpp304
-rw-r--r--indra/newview/lltextureview.h55
-rw-r--r--indra/newview/lltool.cpp151
-rw-r--r--indra/newview/lltool.h84
-rw-r--r--indra/newview/lltoolbar.cpp436
-rw-r--r--indra/newview/lltoolbar.h76
-rw-r--r--indra/newview/lltoolbrush.cpp585
-rw-r--r--indra/newview/lltoolbrush.h88
-rw-r--r--indra/newview/lltoolcomp.cpp673
-rw-r--r--indra/newview/lltoolcomp.h200
-rw-r--r--indra/newview/lltooldraganddrop.cpp2969
-rw-r--r--indra/newview/lltooldraganddrop.h239
-rw-r--r--indra/newview/lltoolface.cpp141
-rw-r--r--indra/newview/lltoolface.h34
-rw-r--r--indra/newview/lltoolfocus.cpp442
-rw-r--r--indra/newview/lltoolfocus.h58
-rw-r--r--indra/newview/lltoolgrab.cpp875
-rw-r--r--indra/newview/lltoolgrab.h112
-rw-r--r--indra/newview/lltoolgun.cpp122
-rw-r--r--indra/newview/lltoolgun.h35
-rw-r--r--indra/newview/lltoolindividual.cpp102
-rw-r--r--indra/newview/lltoolindividual.h41
-rw-r--r--indra/newview/lltoolmgr.cpp532
-rw-r--r--indra/newview/lltoolmgr.h99
-rw-r--r--indra/newview/lltoolmorph.cpp279
-rw-r--r--indra/newview/lltoolmorph.h88
-rw-r--r--indra/newview/lltoolobjpicker.cpp162
-rw-r--r--indra/newview/lltoolobjpicker.h45
-rw-r--r--indra/newview/lltoolpie.cpp635
-rw-r--r--indra/newview/lltoolpie.h59
-rwxr-xr-xindra/newview/lltoolpipette.cpp120
-rwxr-xr-xindra/newview/lltoolpipette.h47
-rw-r--r--indra/newview/lltoolplacer.cpp225
-rw-r--r--indra/newview/lltoolplacer.h79
-rw-r--r--indra/newview/lltoolselect.cpp244
-rw-r--r--indra/newview/lltoolselect.h39
-rw-r--r--indra/newview/lltoolselectland.cpp223
-rw-r--r--indra/newview/lltoolselectland.h56
-rw-r--r--indra/newview/lltoolselectrect.cpp182
-rw-r--r--indra/newview/lltoolselectrect.h44
-rw-r--r--indra/newview/lltoolview.cpp175
-rw-r--r--indra/newview/lltoolview.h69
-rw-r--r--indra/newview/lltracker.cpp801
-rw-r--r--indra/newview/lltracker.h135
-rw-r--r--indra/newview/lluiconstants.h32
-rw-r--r--indra/newview/lluploaddialog.cpp149
-rw-r--r--indra/newview/lluploaddialog.h38
-rw-r--r--indra/newview/llurl.cpp256
-rw-r--r--indra/newview/llurl.h76
-rw-r--r--indra/newview/llurlwhitelist.cpp221
-rw-r--r--indra/newview/llurlwhitelist.h48
-rw-r--r--indra/newview/llviewchildren.cpp99
-rw-r--r--indra/newview/llviewchildren.h49
-rw-r--r--indra/newview/llviewerassetstorage.cpp196
-rw-r--r--indra/newview/llviewerassetstorage.h47
-rw-r--r--indra/newview/llviewercamera.cpp611
-rw-r--r--indra/newview/llviewercamera.h95
-rw-r--r--indra/newview/llviewercontrol.cpp465
-rw-r--r--indra/newview/llviewercontrol.h60
-rw-r--r--indra/newview/llviewerdisplay.cpp832
-rw-r--r--indra/newview/llviewerdisplay.h17
-rw-r--r--indra/newview/llviewergesture.cpp260
-rw-r--r--indra/newview/llviewergesture.h71
-rw-r--r--indra/newview/llviewerinventory.cpp705
-rw-r--r--indra/newview/llviewerinventory.h253
-rw-r--r--indra/newview/llviewerjoint.cpp607
-rw-r--r--indra/newview/llviewerjoint.h137
-rw-r--r--indra/newview/llviewerjointattachment.cpp381
-rw-r--r--indra/newview/llviewerjointattachment.h92
-rw-r--r--indra/newview/llviewerjointmesh.cpp1601
-rw-r--r--indra/newview/llviewerjointmesh.h138
-rw-r--r--indra/newview/llviewerkeyboard.cpp833
-rw-r--r--indra/newview/llviewerkeyboard.h71
-rw-r--r--indra/newview/llviewerlayer.cpp82
-rw-r--r--indra/newview/llviewerlayer.h30
-rw-r--r--indra/newview/llviewermenu.cpp8810
-rw-r--r--indra/newview/llviewermenu.h174
-rw-r--r--indra/newview/llviewermessage.cpp5017
-rw-r--r--indra/newview/llviewermessage.h203
-rw-r--r--indra/newview/llviewernetwork.cpp70
-rw-r--r--indra/newview/llviewernetwork.h52
-rw-r--r--indra/newview/llviewerobject.cpp4683
-rw-r--r--indra/newview/llviewerobject.h626
-rw-r--r--indra/newview/llviewerobjectlist.cpp1484
-rw-r--r--indra/newview/llviewerobjectlist.h256
-rw-r--r--indra/newview/llviewerparcelmgr.cpp2568
-rw-r--r--indra/newview/llviewerparcelmgr.h327
-rw-r--r--indra/newview/llviewerparceloverlay.cpp850
-rw-r--r--indra/newview/llviewerparceloverlay.h97
-rw-r--r--indra/newview/llviewerpartsim.cpp626
-rw-r--r--indra/newview/llviewerpartsim.h141
-rw-r--r--indra/newview/llviewerpartsource.cpp719
-rw-r--r--indra/newview/llviewerpartsource.h188
-rw-r--r--indra/newview/llviewerprecompiledheaders.cpp16
-rw-r--r--indra/newview/llviewerprecompiledheaders.h218
-rw-r--r--indra/newview/llviewerregion.cpp1301
-rw-r--r--indra/newview/llviewerregion.h348
-rw-r--r--indra/newview/llviewerstats.cpp301
-rw-r--r--indra/newview/llviewerstats.h167
-rw-r--r--indra/newview/llviewertexteditor.cpp1475
-rw-r--r--indra/newview/llviewertexteditor.h102
-rw-r--r--indra/newview/llviewertextureanim.cpp191
-rw-r--r--indra/newview/llviewertextureanim.h33
-rw-r--r--indra/newview/llviewerthrottle.cpp313
-rw-r--r--indra/newview/llviewerthrottle.h72
-rw-r--r--indra/newview/llviewervisualparam.cpp132
-rw-r--r--indra/newview/llviewervisualparam.h83
-rw-r--r--indra/newview/llviewerwindow.cpp4816
-rw-r--r--indra/newview/llviewerwindow.h391
-rw-r--r--indra/newview/llvlcomposition.cpp456
-rw-r--r--indra/newview/llvlcomposition.h67
-rw-r--r--indra/newview/llvlmanager.cpp147
-rw-r--r--indra/newview/llvlmanager.h61
-rw-r--r--indra/newview/llvoavatar.cpp9426
-rw-r--r--indra/newview/llvoavatar.h897
-rw-r--r--indra/newview/llvocache.cpp134
-rw-r--r--indra/newview/llvocache.h51
-rw-r--r--indra/newview/llvoclouds.cpp188
-rw-r--r--indra/newview/llvoclouds.h48
-rw-r--r--indra/newview/llvograss.cpp499
-rw-r--r--indra/newview/llvograss.h84
-rw-r--r--indra/newview/llvoground.cpp165
-rw-r--r--indra/newview/llvoground.h36
-rw-r--r--indra/newview/llvoinventorylistener.cpp45
-rw-r--r--indra/newview/llvoinventorylistener.h43
-rw-r--r--indra/newview/llvopartgroup.cpp266
-rw-r--r--indra/newview/llvopartgroup.h42
-rw-r--r--indra/newview/llvosky.cpp2429
-rw-r--r--indra/newview/llvosky.h907
-rw-r--r--indra/newview/llvosurfacepatch.cpp924
-rw-r--r--indra/newview/llvosurfacepatch.h93
-rw-r--r--indra/newview/llvotextbubble.cpp204
-rw-r--r--indra/newview/llvotextbubble.h37
-rw-r--r--indra/newview/llvotree.cpp906
-rw-r--r--indra/newview/llvotree.h118
-rw-r--r--indra/newview/llvotreenew.h200
-rw-r--r--indra/newview/llvovolume.cpp2027
-rw-r--r--indra/newview/llvovolume.h213
-rw-r--r--indra/newview/llvowater.cpp1028
-rw-r--r--indra/newview/llvowater.h240
-rw-r--r--indra/newview/llwearable.cpp952
-rw-r--r--indra/newview/llwearable.h119
-rw-r--r--indra/newview/llwearablelist.cpp325
-rw-r--r--indra/newview/llwearablelist.h50
-rw-r--r--indra/newview/llweb.cpp69
-rw-r--r--indra/newview/llweb.h31
-rw-r--r--indra/newview/llwind.cpp340
-rw-r--r--indra/newview/llwind.h50
-rw-r--r--indra/newview/llwindebug.cpp254
-rw-r--r--indra/newview/llwindebug.h24
-rw-r--r--indra/newview/llworld.cpp1141
-rw-r--r--indra/newview/llworld.h178
-rw-r--r--indra/newview/llworldmap.cpp915
-rw-r--r--indra/newview/llworldmap.h192
-rw-r--r--indra/newview/llworldmapview.cpp1940
-rw-r--r--indra/newview/llworldmapview.h173
-rw-r--r--indra/newview/llxmlrpctransaction.cpp577
-rw-r--r--indra/newview/llxmlrpctransaction.h115
-rw-r--r--indra/newview/macmain.h26
-rw-r--r--indra/newview/macutil_Prefix.h18
-rw-r--r--indra/newview/macview.r123
-rw-r--r--indra/newview/macview_Prefix.h221
-rw-r--r--indra/newview/noise.cpp66
-rw-r--r--indra/newview/noise.h334
-rw-r--r--indra/newview/pipeline.cpp5316
-rw-r--r--indra/newview/pipeline.h659
-rw-r--r--indra/newview/res-sdl/arrow.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/arrowcop.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/arrowcopmulti.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/arrowdrag.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/circleandline.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/cross.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/hand.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/ibeam.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/ll_icon.BMPbin0 -> 5174 bytes
-rw-r--r--indra/newview/res-sdl/llarrow.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llarrowdrag.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llarrowdragmulti.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/llarrowlocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llgrablocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llno.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/llnolocked.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolcamera.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolcreate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolfocus.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolgrab.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolland.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolpan.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolpipette.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/lltoolrotate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolscale.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltooltranslate.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolzoomin.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/lltoolzoomout.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizenesw.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizens.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizenwse.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/sizewe.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolbuy.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolopen.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject2.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolpickobject3.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/toolsit.BMPbin0 -> 3126 bytes
-rw-r--r--indra/newview/res-sdl/wait.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res-sdl/working.BMPbin0 -> 2102 bytes
-rw-r--r--indra/newview/res/arrow.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowcop.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowcopmulti.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/arrowdrag.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/bitmap2.bmpbin0 -> 25118 bytes
-rw-r--r--indra/newview/res/circleandline.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/install_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/res/ll_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/res/ll_icon.icobin0 -> 364590 bytes
-rw-r--r--indra/newview/res/llarrow.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowdrag.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowdragmulti.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llarrowlocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llgrablocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llno.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/llnolocked.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolcamera.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolcreate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolfocus.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolgrab.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolland.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolpan.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolpipette.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolrotate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolscale.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltooltranslate.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolzoomin.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/lltoolzoomout.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/loginbackground.bmpbin0 -> 336054 bytes
-rw-r--r--indra/newview/res/resource.h159
-rw-r--r--indra/newview/res/toolbuy.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/toolopen.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/toolpickobject.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpickobject2.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpickobject3.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolpipette.curbin0 -> 326 bytes
-rw-r--r--indra/newview/res/toolsit.curbin0 -> 2238 bytes
-rw-r--r--indra/newview/res/uninstall_icon.BMPbin0 -> 262198 bytes
-rw-r--r--indra/newview/secondlife.icnsbin0 -> 115923 bytes
-rw-r--r--indra/newview/skins/paths.xml4
-rw-r--r--indra/test/io.cpp1368
-rw-r--r--indra/test/llhttpclient_tut.cpp296
-rw-r--r--indra/test/llhttpnode_tut.cpp409
-rw-r--r--indra/test/lliohttpserver_tut.cpp284
-rw-r--r--indra/test/llpipeutil.cpp139
-rw-r--r--indra/test/llpipeutil.h125
-rw-r--r--indra/test/llsd_new_tut.cpp821
-rw-r--r--indra/test/lltut.cpp149
-rw-r--r--indra/test/lltut.h76
-rw-r--r--indra/test/lluserrelations_tut.cpp138
-rw-r--r--indra/test/test.cpp248
-rw-r--r--indra/win_crash_logger/StdAfx.cpp16
-rw-r--r--indra/win_crash_logger/StdAfx.h40
-rw-r--r--indra/win_crash_logger/ll_icon.icobin0 -> 2238 bytes
-rw-r--r--indra/win_crash_logger/resource.h44
-rw-r--r--indra/win_crash_logger/win_crash_logger.cpp915
-rw-r--r--indra/win_crash_logger/win_crash_logger.h20
-rw-r--r--indra/win_crash_logger/win_crash_logger.icobin0 -> 1078 bytes
-rw-r--r--indra/win_crash_logger/win_crash_logger.rc207
-rw-r--r--indra/win_updater/updater.cpp527
-rw-r--r--scripts/messages/message_template.msg9991
-rwxr-xr-xscripts/update_version_files.py141
1229 files changed, 501302 insertions, 0 deletions
diff --git a/indra/lib/python/indra/__init__.py b/indra/lib/python/indra/__init__.py
new file mode 100644
index 0000000000..8f5987696c
--- /dev/null
+++ b/indra/lib/python/indra/__init__.py
@@ -0,0 +1,6 @@
+"""@file __init__.py
+@brief
+
+Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+$License$
+"""
diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
new file mode 100644
index 0000000000..a8acced4cf
--- /dev/null
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -0,0 +1,553 @@
+/**
+ * @file linux_crash_logger.cpp
+ * @brief Linux crash logger implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <curl/curl.h>
+
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+#include "indra_constants.h" // CRASH_BEHAVIOR_ASK
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+
+#include "llstring.h"
+
+
+// These need to be localized.
+static const char dialog_text[] =
+"Second Life appears to have crashed.\n"
+"This crash reporter collects information about your computer's hardware, operating system, and some Second Life logs, which are used for debugging purposes only.\n"
+"Sending crash reports is the best way to help us improve the quality of Second Life.\n"
+"If you continue to experience this problem, please try one of the following:\n"
+"- Contact support by email at support@lindenlab.com\n"
+"- If you can log-in, please contact Live Help by using menu Help > Live Help.\n"
+"- Search the Second Life Knowledge Base at http://secondlife.com/knowledgebase/\n"
+"\n"
+"Send crash report?";
+
+static const char dialog_title[] =
+"Second Life Crash Logger";
+
+
+class LLFileEncoder
+{
+public:
+ LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
+
+ BOOL isValid() const { return mIsValid; }
+ LLString encodeURL(const S32 max_length = 0);
+public:
+ BOOL mIsValid;
+ LLString mFilename;
+ LLString mFormname;
+ LLString mBuf;
+};
+
+LLString encode_string(const char *formname, const LLString &str);
+
+LLString gServerResponse;
+BOOL gSendReport = FALSE;
+LLString gUserserver;
+LLString gUserText;
+BOOL gCrashInPreviousExec = FALSE;
+time_t gLaunchTime;
+
+static size_t curl_download_callback(void *data, size_t size, size_t nmemb,
+ void *user_data)
+{
+ S32 bytes = size * nmemb;
+ char *cdata = (char *) data;
+ for (int i =0; i < bytes; i += 1)
+ {
+ gServerResponse += (cdata[i]);
+ }
+ return bytes;
+}
+
+#if LL_GTK
+static void response_callback (GtkDialog *dialog,
+ gint arg1,
+ gpointer user_data)
+{
+ gint *response = (gint*)user_data;
+ *response = arg1;
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ gtk_main_quit();
+}
+#endif // LL_GTK
+
+static BOOL do_ask_dialog(void)
+{
+#if LL_GTK
+ gtk_disable_setlocale();
+ if (!gtk_init_check(NULL, NULL)) {
+ llinfos << "Could not initialize GTK for 'ask to send crash report' dialog; not sending report." << llendl;
+ return FALSE;
+ }
+
+ GtkWidget *win = NULL;
+ GtkDialogFlags flags = GTK_DIALOG_MODAL;
+ GtkMessageType messagetype = GTK_MESSAGE_QUESTION;
+ GtkButtonsType buttons = GTK_BUTTONS_YES_NO;
+ gint response = GTK_RESPONSE_NONE;
+
+ win = gtk_message_dialog_new(NULL,
+ flags, messagetype, buttons,
+ dialog_text);
+ gtk_window_set_type_hint(GTK_WINDOW(win),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+ gtk_window_set_title(GTK_WINDOW(win), dialog_title);
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+ gtk_widget_show_all (win);
+ gtk_main();
+
+ return (GTK_RESPONSE_OK == response ||
+ GTK_RESPONSE_YES == response ||
+ GTK_RESPONSE_APPLY == response);
+#else
+ return FALSE;
+#endif // LL_GTK
+}
+
+
+int main(int argc, char **argv)
+{
+ const S32 BT_MAX_SIZE = 100000; // Maximum size to transmit of the backtrace file
+ const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
+ int i;
+ S32 crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
+
+ time(&gLaunchTime);
+
+ llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
+
+ for(i=1; i<argc; i++)
+ {
+ if(!strcmp(argv[i], "-dialog"))
+ {
+ llinfos << "Show the user dialog" << llendl;
+ crash_behavior = CRASH_BEHAVIOR_ASK;
+ }
+ if(!strcmp(argv[i], "-previous"))
+ {
+ gCrashInPreviousExec = TRUE;
+ }
+ if(!strcmp(argv[i], "-user"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+ gUserserver = argv[i];
+ llinfos << "Got userserver " << gUserserver << llendl;
+ }
+ }
+ }
+
+ if( gCrashInPreviousExec )
+ {
+ llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+ }
+
+ if(!gCrashInPreviousExec)
+ {
+ // Wait a while to let the crashed client finish exiting,
+ // freeing up the screen/etc.
+ sleep(5);
+ }
+
+ // FIXME: do some dialog stuff here?
+ if (CRASH_BEHAVIOR_ALWAYS_SEND == crash_behavior)
+ {
+ gSendReport = TRUE;
+ }
+ else if (CRASH_BEHAVIOR_ASK == crash_behavior)
+ {
+ gSendReport = do_ask_dialog();
+ }
+
+ if(!gSendReport)
+ {
+ // Only send the report if the user agreed to it.
+ llinfos << "User cancelled, not sending report" << llendl;
+
+ return(0);
+ }
+
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ // Lots of silly variable, replicated for each log file.
+ LLString db_file_name;
+ LLString sl_file_name;
+ LLString bt_file_name; // stack_trace.log file
+ LLString st_file_name; // stats.log file
+ LLString si_file_name; // settings.xml file
+
+ LLFileEncoder *db_filep = NULL;
+ LLFileEncoder *sl_filep = NULL;
+ LLFileEncoder *st_filep = NULL;
+ LLFileEncoder *bt_filep = NULL;
+ LLFileEncoder *si_filep = NULL;
+
+ ///////////////////////////////////
+ //
+ // We do the parsing for the debug_info file first, as that will
+ // give us the location of the SecondLife.log file.
+ //
+
+ // Figure out the filename of the debug log
+ db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
+ db_filep = new LLFileEncoder("DB", db_file_name.c_str());
+
+ // Get the filename of the SecondLife.log file
+ //FIXME tofu - get right MAX_PATH
+#define MAX_PATH PATH_MAX
+ char tmp_sl_name[MAX_PATH];
+ tmp_sl_name[0] = '\0';
+ char tmp_space[256];
+ tmp_space[0] = '\0';
+
+ // Look for it in the debug_info.log file
+ if (db_filep->isValid())
+ {
+ // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
+ // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
+ sscanf(db_filep->mBuf.c_str(), "SL Log:%[ ]%[^\r\n]", tmp_space, tmp_sl_name);
+ }
+ else
+ {
+ delete db_filep;
+ db_filep = NULL;
+ }
+
+ // If we actually have a legitimate file name, use it.
+ if (gCrashInPreviousExec)
+ {
+ // If we froze, the crash log this time around isn't useful.
+ // Use the old one.
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
+ }
+ else if (tmp_sl_name[0])
+ {
+ sl_file_name = tmp_sl_name;
+ llinfos << "Using log file from debug log: " << sl_file_name << llendl;
+ }
+ else
+ {
+ // Figure out the filename of the second life log
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
+ }
+
+ // Now we get the SecondLife.log file if it's there, and recent enough...
+ sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
+ if (!sl_filep->isValid())
+ {
+ delete sl_filep;
+ sl_filep = NULL;
+ }
+
+ st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
+ st_filep = new LLFileEncoder("ST", st_file_name.c_str());
+ if (!st_filep->isValid())
+ {
+ delete st_filep;
+ st_filep = NULL;
+ }
+
+ si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.xml").c_str();
+ si_filep = new LLFileEncoder("SI", si_file_name.c_str());
+ if (!si_filep->isValid())
+ {
+ delete si_filep;
+ si_filep = NULL;
+ }
+
+ // encode this as if it were a 'Dr Watson' plain-text backtrace
+ bt_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stack_trace.log").c_str();
+ bt_filep = new LLFileEncoder("DW", bt_file_name.c_str());
+ if (!bt_filep->isValid())
+ {
+ delete bt_filep;
+ bt_filep = NULL;
+ }
+
+ LLString post_data;
+ LLString tmp_url_buf;
+
+ // Append the userserver
+ tmp_url_buf = encode_string("USER", gUserserver);
+ post_data += tmp_url_buf;
+ llinfos << "PostData:" << post_data << llendl;
+
+ if (gCrashInPreviousExec)
+ {
+ post_data.append("&");
+ tmp_url_buf = encode_string("EF", "Y");
+ post_data += tmp_url_buf;
+ }
+
+ if (db_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = db_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending DB log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending DB log file" << llendl;
+ }
+
+ if (sl_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending SL log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending SL log file" << llendl;
+ }
+
+ if (st_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending stats log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending stats log file" << llendl;
+ }
+
+ if (bt_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = bt_filep->encodeURL(BT_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending crash log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending crash log file" << llendl;
+ }
+
+ if (si_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = si_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending settings log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending settings.xml file" << llendl;
+ }
+
+ if (gUserText.size())
+ {
+ post_data.append("&");
+ tmp_url_buf = encode_string("UN", gUserText);
+ post_data += tmp_url_buf;
+ }
+
+ delete db_filep;
+ db_filep = NULL;
+ delete sl_filep;
+ sl_filep = NULL;
+ delete bt_filep;
+ bt_filep = NULL;
+
+ // Debugging spam
+#if 0
+ printf("Crash report post data:\n--------\n");
+ printf("%s", post_data.getString());
+ printf("\n--------\n");
+#endif
+
+ // Send the report. Yes, it's this easy.
+ {
+ CURL *curl = curl_easy_init();
+
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
+ curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2");
+
+ llinfos << "Connecting to crash report server" << llendl;
+ CURLcode result = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+
+ if(result != CURLE_OK)
+ {
+ llinfos << "Couldn't talk to crash report server" << llendl;
+ }
+ else
+ {
+ llinfos << "Response from crash report server:" << llendl;
+ llinfos << gServerResponse << llendl;
+ }
+ }
+
+ return 0;
+}
+
+LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
+{
+ mFormname = form_name;
+ mFilename = filename;
+ mIsValid = FALSE;
+
+ int res;
+
+ struct stat stat_data;
+ res = stat(mFilename.c_str(), &stat_data);
+ if (res)
+ {
+ llwarns << "File " << mFilename << " is missing!" << llendl;
+ return;
+ }
+ else
+ {
+ // Debugging spam
+// llinfos << "File " << mFilename << " is present..." << llendl;
+
+ if(!gCrashInPreviousExec && isCrashLog)
+ {
+ // Make sure the file isn't too old.
+ double age = difftime(gLaunchTime, stat_data.st_mtim.tv_sec);
+
+// llinfos << "age is " << age << llendl;
+
+ if(age > 60.0)
+ {
+ // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
+ llwarns << "File " << mFilename << " is too old!" << llendl;
+ return;
+ }
+ }
+
+ }
+
+ S32 buf_size = stat_data.st_size;
+ FILE *fp = fopen(mFilename.c_str(), "rb");
+ U8 *buf = new U8[buf_size + 1];
+ fread(buf, 1, buf_size, fp);
+ fclose(fp);
+ buf[buf_size] = 0;
+
+ mBuf = (char *)buf;
+
+ if(isCrashLog)
+ {
+ // Crash logs consist of a number of entries, one per crash.
+ // Each entry is preceeded by "**********" on a line by itself.
+ // We want only the most recent (i.e. last) one.
+ const char *sep = "**********";
+ const char *start = mBuf.c_str();
+ const char *cur = start;
+ const char *temp = strstr(cur, sep);
+
+ while(temp != NULL)
+ {
+ // Skip past the marker we just found
+ cur = temp + strlen(sep);
+
+ // and try to find another
+ temp = strstr(cur, sep);
+ }
+
+ // If there's more than one entry in the log file, strip all but the last one.
+ if(cur != start)
+ {
+ mBuf.erase(0, cur - start);
+ }
+ }
+
+ mIsValid = TRUE;
+ delete[] buf;
+}
+
+LLString LLFileEncoder::encodeURL(const S32 max_length)
+{
+ LLString result = mFormname;
+ result.append("=");
+
+ S32 i = 0;
+
+ if (max_length)
+ {
+ if ((S32)mBuf.size() > max_length)
+ {
+ i = mBuf.size() - max_length;
+ }
+ }
+
+#if 0
+ // Plain text version for debugging
+ result.append(mBuf);
+#else
+ // Not using LLString because of bad performance issues
+ S32 buf_size = mBuf.size();
+ S32 url_buf_size = 3*mBuf.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ for (; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+#endif
+ return result;
+}
+
+LLString encode_string(const char *formname, const LLString &str)
+{
+ LLString result = formname;
+ result.append("=");
+ // Not using LLString because of bad performance issues
+ S32 buf_size = str.size();
+ S32 url_buf_size = 3*str.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ S32 i;
+ for (i = 0; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", str[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp
new file mode 100644
index 0000000000..a819a9fa03
--- /dev/null
+++ b/indra/llaudio/llaudiodecodemgr.cpp
@@ -0,0 +1,616 @@
+/**
+ * @file llaudiodecodemgr.cpp
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <vector>
+#include <iterator>
+#include <algorithm>
+#include <stdio.h>
+
+#include "llaudiodecodemgr.h"
+
+#include "vorbisdecode.h"
+#include "audioengine.h"
+#include "lllfsthread.h"
+#include "llvfile.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llendianswizzle.h"
+#include "audioengine.h"
+#include "llassetstorage.h"
+
+#include "vorbis/codec.h"
+#include "vorbis/vorbisfile.h"
+
+extern LLAudioEngine *gAudiop;
+
+LLAudioDecodeMgr *gAudioDecodeMgrp = NULL;
+
+const S32 wav_header_size = 44;
+
+class LLVorbisDecodeState
+{
+public:
+ LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename);
+ virtual ~LLVorbisDecodeState();
+
+ BOOL initDecode();
+ BOOL decodeSection(); // Return TRUE if done.
+ BOOL finishDecode();
+
+ void flushBadFile();
+
+ BOOL isValid() const { return mValid; }
+ BOOL isDone() const { return mDone; }
+ const LLUUID &getUUID() const { return mUUID; }
+protected:
+ BOOL mValid;
+ BOOL mDone;
+ LLUUID mUUID;
+
+ std::vector<U8> mWAVBuffer;
+#if !defined(USE_WAV_VFILE)
+ LLString mOutFilename;
+ LLLFSThread::handle_t mFileHandle;
+#endif
+
+ LLVFile *mInFilep;
+ OggVorbis_File mVF;
+ S32 mCurrentSection;
+};
+
+void LLVorbisDecodeState::flushBadFile()
+{
+ if (mInFilep)
+ {
+ llwarns << "Flushing bad vorbis file from VFS for " << mUUID << llendl;
+ mInFilep->remove();
+ }
+}
+
+
+LLAudioDecodeMgr::LLAudioDecodeMgr()
+{
+ mCurrentDecodep = NULL;
+}
+
+LLAudioDecodeMgr::~LLAudioDecodeMgr()
+{
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+}
+
+
+void LLAudioDecodeMgr::processQueue(const F32 num_secs)
+{
+ LLUUID uuid;
+
+ LLTimer decode_timer;
+
+ BOOL done = FALSE;
+ while (!done)
+ {
+ if (mCurrentDecodep)
+ {
+ BOOL res;
+
+ // Decode in a loop until we're done or have run out of time.
+ while(!(res = mCurrentDecodep->decodeSection()) && (decode_timer.getElapsedTimeF32() < num_secs))
+ {
+ // decodeSection does all of the work above
+ }
+
+ if (mCurrentDecodep->isDone() && !mCurrentDecodep->isValid())
+ {
+ // We had an error when decoding, abort.
+ llwarns << mCurrentDecodep->getUUID() << " has invalid vorbis data, aborting decode" << llendl;
+ mCurrentDecodep->flushBadFile();
+ LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+ adp->setHasValidData(FALSE);
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ done = TRUE;
+ }
+
+ if (!res)
+ {
+ // We've used up out time slice, bail...
+ done = TRUE;
+ }
+ else if (mCurrentDecodep)
+ {
+ if (mCurrentDecodep->finishDecode())
+ {
+ // We finished!
+ if (mCurrentDecodep->isValid() && mCurrentDecodep->isDone())
+ {
+ LLAudioData *adp = gAudiop->getAudioData(mCurrentDecodep->getUUID());
+ adp->setHasDecodedData(TRUE);
+ adp->setHasValidData(TRUE);
+
+ // At this point, we could see if anyone needs this sound immediately, but
+ // I'm not sure that there's a reason to - we need to poll all of the playing
+ // sounds anyway.
+ //llinfos << "Finished the vorbis decode, now what?" << llendl;
+ }
+ else
+ {
+ llinfos << "Vorbis decode failed!!!" << llendl;
+ }
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ }
+ done = TRUE; // done for now
+ }
+ }
+
+ if (!done)
+ {
+ if (!mDecodeQueue.getLength())
+ {
+ // Nothing else on the queue.
+ done = TRUE;
+ }
+ else
+ {
+ LLUUID uuid;
+ mDecodeQueue.pop(uuid);
+ if (gAudiop->hasDecodedFile(uuid))
+ {
+ // This file has already been decoded, don't decode it again.
+ continue;
+ }
+
+ lldebugs << "Decoding " << uuid << " from audio queue!" << llendl;
+
+ char uuid_str[64]; /*Flawfinder: ignore*/
+ char d_path[LL_MAX_PATH]; /*Flawfinder: ignore*/
+
+ LLTimer timer;
+ timer.reset();
+
+ uuid.toString(uuid_str);
+ snprintf(d_path, LL_MAX_PATH, "%s.dsf", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str()); /*Flawfinder: ignore*/
+
+ mCurrentDecodep = new LLVorbisDecodeState(uuid, d_path);
+ if (!mCurrentDecodep->initDecode())
+ {
+ delete mCurrentDecodep;
+ mCurrentDecodep = NULL;
+ }
+ }
+ }
+ }
+}
+
+
+BOOL LLAudioDecodeMgr::addDecodeRequest(const LLUUID &uuid)
+{
+ if (gAudiop->hasDecodedFile(uuid))
+ {
+ // Already have a decoded version, don't need to decode it.
+ return TRUE;
+ }
+
+ if (gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND))
+ {
+ // Just put it on the decode queue.
+ gAudioDecodeMgrp->mDecodeQueue.push(uuid);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+S32 LLAudioDecodeMgr::getRequestCount()
+{
+ /*
+ S32 count = 0;
+ if (mCurrentTransfer.notNull())
+ {
+ count++;
+ }
+
+ count += mRequestQueue.getLength();
+ return count;
+ */
+ return 0;
+}
+
+
+
+
+
+
+
+
+size_t vfs_read(void *ptr, size_t size, size_t nmemb, void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ if (file->read((U8*)ptr, (S32)(size * nmemb))) /*Flawfinder: ignore*/
+ {
+ S32 read = file->getLastBytesRead();
+ return read / size; /*Flawfinder: ignore*/
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int vfs_seek(void *datasource, ogg_int64_t offset, int whence)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ // vfs has 31-bit files
+ if (offset > S32_MAX)
+ {
+ return -1;
+ }
+
+ S32 origin;
+ switch (whence) {
+ case SEEK_SET:
+ origin = 0;
+ break;
+ case SEEK_END:
+ origin = file->getSize();
+ break;
+ case SEEK_CUR:
+ origin = -1;
+ break;
+ default:
+ llerrs << "Invalid whence argument to vfs_seek" << llendl;
+ return -1;
+ }
+
+ if (file->seek((S32)offset, origin))
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+int vfs_close (void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ delete file;
+
+ return 0;
+}
+
+long vfs_tell (void *datasource)
+{
+ LLVFile *file = (LLVFile *)datasource;
+
+ return file->tell();
+}
+
+
+
+
+LLVorbisDecodeState::LLVorbisDecodeState(const LLUUID &uuid, const LLString &out_filename)
+{
+ mDone = FALSE;
+ mValid = FALSE;
+ mUUID = uuid;
+ mInFilep = NULL;
+ mCurrentSection = 0;
+#if !defined(USE_WAV_VFILE)
+ mOutFilename = out_filename;
+ mFileHandle = LLLFSThread::nullHandle();
+#endif
+ // No default value for mVF, it's an ogg structure?
+}
+
+LLVorbisDecodeState::~LLVorbisDecodeState()
+{
+ if (!mDone)
+ {
+ delete mInFilep;
+ mInFilep = NULL;
+ }
+}
+
+
+BOOL LLVorbisDecodeState::initDecode()
+{
+ ov_callbacks vfs_callbacks;
+ vfs_callbacks.read_func = vfs_read;
+ vfs_callbacks.seek_func = vfs_seek;
+ vfs_callbacks.close_func = vfs_close;
+ vfs_callbacks.tell_func = vfs_tell;
+
+ //llinfos << "Initing decode from vfile: " << mUUID << llendl;
+
+ mInFilep = new LLVFile(gVFS, mUUID, LLAssetType::AT_SOUND);
+ if (!mInFilep || !mInFilep->getSize())
+ {
+ llwarns << "unable to open vorbis source vfile for reading" << llendl;
+ delete mInFilep;
+ mInFilep = NULL;
+ return FALSE;
+ }
+
+ int r = ov_open_callbacks(mInFilep, &mVF, NULL, 0, vfs_callbacks);
+ if(r < 0)
+ {
+ llwarns << r << " Input to vorbis decode does not appear to be an Ogg bitstream: " << mUUID << llendl;
+ return(FALSE);
+ }
+
+ size_t size_guess = (size_t)ov_pcm_total(&mVF, -1);
+ vorbis_info* vi = ov_info(&mVF, -1);
+ size_guess *= vi->channels;
+ size_guess *= 2;
+ size_guess += 2048;
+ mWAVBuffer.reserve(size_guess);
+ mWAVBuffer.resize(wav_header_size);
+
+ {
+ // write the .wav format header
+ //"RIFF"
+ mWAVBuffer[0] = 0x52;
+ mWAVBuffer[1] = 0x49;
+ mWAVBuffer[2] = 0x46;
+ mWAVBuffer[3] = 0x46;
+
+ // length = datalen + 36 (to be filled in later)
+ mWAVBuffer[4] = 0x00;
+ mWAVBuffer[5] = 0x00;
+ mWAVBuffer[6] = 0x00;
+ mWAVBuffer[7] = 0x00;
+
+ //"WAVE"
+ mWAVBuffer[8] = 0x57;
+ mWAVBuffer[9] = 0x41;
+ mWAVBuffer[10] = 0x56;
+ mWAVBuffer[11] = 0x45;
+
+ // "fmt "
+ mWAVBuffer[12] = 0x66;
+ mWAVBuffer[13] = 0x6D;
+ mWAVBuffer[14] = 0x74;
+ mWAVBuffer[15] = 0x20;
+
+ // chunk size = 16
+ mWAVBuffer[16] = 0x10;
+ mWAVBuffer[17] = 0x00;
+ mWAVBuffer[18] = 0x00;
+ mWAVBuffer[19] = 0x00;
+
+ // format (1 = PCM)
+ mWAVBuffer[20] = 0x01;
+ mWAVBuffer[21] = 0x00;
+
+ // number of channels
+ mWAVBuffer[22] = 0x01;
+ mWAVBuffer[23] = 0x00;
+
+ // samples per second
+ mWAVBuffer[24] = 0x44;
+ mWAVBuffer[25] = 0xAC;
+ mWAVBuffer[26] = 0x00;
+ mWAVBuffer[27] = 0x00;
+
+ // average bytes per second
+ mWAVBuffer[28] = 0x88;
+ mWAVBuffer[29] = 0x58;
+ mWAVBuffer[30] = 0x01;
+ mWAVBuffer[31] = 0x00;
+
+ // bytes to output at a single time
+ mWAVBuffer[32] = 0x02;
+ mWAVBuffer[33] = 0x00;
+
+ // 16 bits per sample
+ mWAVBuffer[34] = 0x10;
+ mWAVBuffer[35] = 0x00;
+
+ // "data"
+ mWAVBuffer[36] = 0x64;
+ mWAVBuffer[37] = 0x61;
+ mWAVBuffer[38] = 0x74;
+ mWAVBuffer[39] = 0x61;
+
+ // these are the length of the data chunk, to be filled in later
+ mWAVBuffer[40] = 0x00;
+ mWAVBuffer[41] = 0x00;
+ mWAVBuffer[42] = 0x00;
+ mWAVBuffer[43] = 0x00;
+ }
+
+ //{
+ //char **ptr=ov_comment(&mVF,-1)->user_comments;
+// vorbis_info *vi=ov_info(&vf,-1);
+ //while(*ptr){
+ // fprintf(stderr,"%s\n",*ptr);
+ // ++ptr;
+ //}
+// fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi->channels,vi->rate);
+// fprintf(stderr,"\nDecoded length: %ld samples\n", (long)ov_pcm_total(&vf,-1));
+// fprintf(stderr,"Encoded by: %s\n\n",ov_comment(&vf,-1)->vendor);
+ //}
+ return TRUE;
+}
+
+BOOL LLVorbisDecodeState::decodeSection()
+{
+ if (!mInFilep)
+ {
+ llwarns << "No VFS file to decode in vorbis!" << llendl;
+ return TRUE;
+ }
+ if (mDone)
+ {
+// llwarns << "Already done with decode, aborting!" << llendl;
+ return TRUE;
+ }
+ char pcmout[4096]; /*Flawfinder: ignore*/
+
+ BOOL eof = FALSE;
+ long ret=ov_read(&mVF, pcmout, sizeof(pcmout), 0, 2, 1, &mCurrentSection);
+ if (ret == 0)
+ {
+ /* EOF */
+ eof = TRUE;
+ mDone = TRUE;
+ mValid = TRUE;
+// llinfos << "Vorbis EOF" << llendl;
+ }
+ else if (ret < 0)
+ {
+ /* error in the stream. Not a problem, just reporting it in
+ case we (the app) cares. In this case, we don't. */
+
+ llwarns << "BAD vorbis decode in decodeSection." << llendl;
+
+ mValid = FALSE;
+ mDone = TRUE;
+ // We're done, return TRUE.
+ return TRUE;
+ }
+ else
+ {
+// llinfos << "Vorbis read " << ret << "bytes" << llendl;
+ /* we don't bother dealing with sample rate changes, etc, but.
+ you'll have to*/
+ std::copy(pcmout, pcmout+ret, std::back_inserter(mWAVBuffer));
+ }
+ return eof;
+}
+
+BOOL LLVorbisDecodeState::finishDecode()
+{
+ if (!isValid())
+ {
+ llwarns << "Bogus vorbis decode state for " << getUUID() << ", aborting!" << llendl;
+ return TRUE; // We've finished
+ }
+
+#if !defined(USE_WAV_VFILE)
+ if (mFileHandle == LLLFSThread::nullHandle())
+#endif
+ {
+ ov_clear(&mVF);
+
+ // write "data" chunk length, in little-endian format
+ S32 data_length = mWAVBuffer.size() - wav_header_size;
+ mWAVBuffer[40] = (data_length) & 0x000000FF;
+ mWAVBuffer[41] = (data_length >> 8) & 0x000000FF;
+ mWAVBuffer[42] = (data_length >> 16) & 0x000000FF;
+ mWAVBuffer[43] = (data_length >> 24) & 0x000000FF;
+ // write overall "RIFF" length, in little-endian format
+ data_length += 36;
+ mWAVBuffer[4] = (data_length) & 0x000000FF;
+ mWAVBuffer[5] = (data_length >> 8) & 0x000000FF;
+ mWAVBuffer[6] = (data_length >> 16) & 0x000000FF;
+ mWAVBuffer[7] = (data_length >> 24) & 0x000000FF;
+
+ //
+ // FUCK!!! Vorbis encode/decode messes up loop point transitions (pop)
+ // do a cheap-and-cheesy crossfade
+ //
+ {
+ S16 *samplep;
+ S32 i;
+ S32 fade_length;
+ char pcmout[4096]; /*Flawfinder: ignore*/
+
+ fade_length = llmin((S32)128,(S32)(data_length-36)/8);
+ if (sizeof(mWAVBuffer) >= (wav_header_size + 2* fade_length))
+ {
+ memcpy(pcmout, &mWAVBuffer[wav_header_size], (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ llendianswizzle(&pcmout, 2, fade_length);
+
+ samplep = (S16 *)pcmout;
+ for (i = 0 ;i < fade_length; i++)
+ {
+ *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
+ samplep++;
+ }
+
+ llendianswizzle(&pcmout, 2, fade_length);
+ if ((wav_header_size+(2 * fade_length)) < sizeof(mWAVBuffer))
+ {
+ memcpy(&mWAVBuffer[wav_header_size], pcmout, (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ S32 near_end = mWAVBuffer.size() - (2 * fade_length);
+ if (sizeof(mWAVBuffer) >= ( near_end + 2* fade_length))
+ {
+ memcpy(pcmout, &mWAVBuffer[near_end], (2 * fade_length)); /*Flawfinder: ignore*/
+ }
+ llendianswizzle(&pcmout, 2, fade_length);
+
+ samplep = (S16 *)pcmout;
+ for (i = fade_length-1 ; i >= 0; i--)
+ {
+ *samplep = llfloor((F32)*samplep * ((F32)i/(F32)fade_length));
+ samplep++;
+ }
+
+ llendianswizzle(&pcmout, 2, fade_length);
+ if (near_end + (2 * fade_length) < sizeof(mWAVBuffer))
+ {
+ memcpy(&mWAVBuffer[near_end], pcmout, (2 * fade_length));/*Flawfinder: ignore*/
+ }
+ }
+
+ if (36 == data_length)
+ {
+ llwarns << "BAD Vorbis decode in finishDecode!" << llendl;
+ mValid = FALSE;
+ return TRUE; // we've finished
+ }
+#if !defined(USE_WAV_VFILE)
+ mFileHandle = LLLFSThread::sLocal->write(mOutFilename, &mWAVBuffer[0], 0, data_length);
+#endif
+ }
+
+ if (mFileHandle != LLLFSThread::nullHandle())
+ {
+ LLLFSThread::status_t s = LLLFSThread::sLocal->getRequestStatus(mFileHandle);
+ if (s != LLLFSThread::STATUS_COMPLETE)
+ {
+ if (s != LLLFSThread::STATUS_QUEUED && s != LLLFSThread::STATUS_INPROGRESS)
+ {
+ llerrs << "Bad file status in LLVorbisDecodeState::finishDecode: " << s << llendl;
+ }
+ return FALSE; // not finished
+ }
+ else
+ {
+ LLLFSThread::Request* req = (LLLFSThread::Request*)LLLFSThread::sLocal->getRequest(mFileHandle);
+ if (req->getBytesRead() == 0) //!= req->getBytes() // should be safe, but needs testing
+ {
+ llwarns << "Unable to write file in LLVorbisDecodeState::finishDecode" << llendl;
+ mValid = FALSE;
+ return TRUE; // we've finished
+ }
+ }
+ LLLFSThread::sLocal->completeRequest(mFileHandle);
+ }
+
+ mDone = TRUE;
+
+#if defined(USE_WAV_VFILE)
+ // write the data.
+ LLVFile output(gVFS, mUUID, LLAssetType::AT_SOUND_WAV);
+ output.write(&mWAVBuffer[0], mWAVBuffer.size());
+#endif
+ //llinfos << "Finished decode for " << getUUID() << llendl;
+
+ return TRUE;
+}
diff --git a/indra/llaudio/llaudiodecodemgr.h b/indra/llaudio/llaudiodecodemgr.h
new file mode 100644
index 0000000000..ea1358b8e9
--- /dev/null
+++ b/indra/llaudio/llaudiodecodemgr.h
@@ -0,0 +1,47 @@
+/**
+ * @file llaudiodecodemgr.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAUDIODECODEMGR_H
+#define LL_LLAUDIODECODEMG_H
+
+#include "stdtypes.h"
+
+#include "lllinkedqueue.h"
+#include "lluuid.h"
+
+#include "llassettype.h"
+#include "llframetimer.h"
+
+class LLVFS;
+class LLVorbisDecodeState;
+
+
+class LLAudioDecodeMgr
+{
+public:
+ LLAudioDecodeMgr();
+ ~LLAudioDecodeMgr();
+
+ void processQueue(const F32 num_secs = 0.005);
+
+ LLLinkedQueue<LLUUID> mDecodeQueue;
+
+ LLVorbisDecodeState *mCurrentDecodep;
+
+ BOOL addDecodeRequest(const LLUUID &uuid);
+ void addAudioRequest(const LLUUID &uuid);
+
+ S32 getRequestCount();
+
+protected:
+ LLLinkedQueue<LLUUID> mDownloadQueue;
+ LLFrameTimer mCurrentTransferAge;
+};
+
+extern LLAudioDecodeMgr *gAudioDecodeMgrp;
+
+#endif
diff --git a/indra/llcharacter/llanimationstates.cpp b/indra/llcharacter/llanimationstates.cpp
new file mode 100644
index 0000000000..58ba252e04
--- /dev/null
+++ b/indra/llcharacter/llanimationstates.cpp
@@ -0,0 +1,319 @@
+/**
+ * @file llanimationstates.cpp
+ * @brief Implementation of animation state related functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Agent Animation State
+//-----------------------------------------------------------------------------
+
+#include "linden_common.h"
+
+#include <string.h>
+
+#include "llanimationstates.h"
+#include "llstring.h"
+
+LLUUID AGENT_WALK_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_CROUCHWALK, ANIM_AGENT_TURNLEFT, ANIM_AGENT_TURNRIGHT};
+S32 NUM_AGENT_WALK_ANIMS = sizeof(AGENT_WALK_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_GUN_HOLD_ANIMS[] = {ANIM_AGENT_HOLD_RIFLE_R, ANIM_AGENT_HOLD_HANDGUN_R, ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_AGENT_HOLD_BOW_L};
+S32 NUM_AGENT_GUN_HOLD_ANIMS = sizeof(AGENT_GUN_HOLD_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_GUN_AIM_ANIMS[] = {ANIM_AGENT_AIM_RIFLE_R, ANIM_AGENT_AIM_HANDGUN_R, ANIM_AGENT_AIM_BAZOOKA_R, ANIM_AGENT_AIM_BOW_L};
+S32 NUM_AGENT_GUN_AIM_ANIMS = sizeof(AGENT_GUN_AIM_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_NO_ROTATE_ANIMS[] = {ANIM_AGENT_SIT_GROUND, ANIM_AGENT_SIT_GROUND_CONSTRAINED, ANIM_AGENT_STANDUP};
+S32 NUM_AGENT_NO_ROTATE_ANIMS = sizeof(AGENT_NO_ROTATE_ANIMS) / sizeof(LLUUID);
+
+LLUUID AGENT_STAND_ANIMS[] = {ANIM_AGENT_STAND, ANIM_AGENT_STAND_1, ANIM_AGENT_STAND_2, ANIM_AGENT_STAND_3, ANIM_AGENT_STAND_4};
+S32 NUM_AGENT_STAND_ANIMS = sizeof(AGENT_STAND_ANIMS) / sizeof(LLUUID);
+
+
+LLAnimationLibrary gAnimLibrary;
+
+//-----------------------------------------------------------------------------
+// LLAnimationLibrary()
+//-----------------------------------------------------------------------------
+LLAnimationLibrary::LLAnimationLibrary() :
+ mAnimStringTable(16384)
+{
+ //add animation names to animmap
+ mAnimMap[ANIM_AGENT_AFRAID]= mAnimStringTable.addString("express_afraid");
+ mAnimMap[ANIM_AGENT_AIM_BAZOOKA_R]= mAnimStringTable.addString("aim_r_bazooka");
+ mAnimMap[ANIM_AGENT_AIM_BOW_L]= mAnimStringTable.addString("aim_l_bow");
+ mAnimMap[ANIM_AGENT_AIM_HANDGUN_R]= mAnimStringTable.addString("aim_r_handgun");
+ mAnimMap[ANIM_AGENT_AIM_RIFLE_R]= mAnimStringTable.addString("aim_r_rifle");
+ mAnimMap[ANIM_AGENT_ANGRY]= mAnimStringTable.addString("express_anger");
+ mAnimMap[ANIM_AGENT_AWAY]= mAnimStringTable.addString("away");
+ mAnimMap[ANIM_AGENT_BACKFLIP]= mAnimStringTable.addString("backflip");
+ mAnimMap[ANIM_AGENT_BELLY_LAUGH]= mAnimStringTable.addString("express_laugh");
+ mAnimMap[ANIM_AGENT_BLOW_KISS]= mAnimStringTable.addString("blowkiss");
+ mAnimMap[ANIM_AGENT_BORED]= mAnimStringTable.addString("express_bored");
+ mAnimMap[ANIM_AGENT_BOW]= mAnimStringTable.addString("bow");
+ mAnimMap[ANIM_AGENT_BRUSH]= mAnimStringTable.addString("brush");
+ mAnimMap[ANIM_AGENT_BUSY]= mAnimStringTable.addString("busy");
+ mAnimMap[ANIM_AGENT_CLAP]= mAnimStringTable.addString("clap");
+ mAnimMap[ANIM_AGENT_COURTBOW]= mAnimStringTable.addString("courtbow");
+ mAnimMap[ANIM_AGENT_CROUCH]= mAnimStringTable.addString("crouch");
+ mAnimMap[ANIM_AGENT_CROUCHWALK]= mAnimStringTable.addString("crouchwalk");
+ mAnimMap[ANIM_AGENT_CRY]= mAnimStringTable.addString("express_cry");
+ mAnimMap[ANIM_AGENT_CUSTOMIZE]= mAnimStringTable.addString("turn_180");
+ mAnimMap[ANIM_AGENT_CUSTOMIZE_DONE]= mAnimStringTable.addString("turnback_180");
+ mAnimMap[ANIM_AGENT_DANCE1]= mAnimStringTable.addString("dance1");
+ mAnimMap[ANIM_AGENT_DANCE2]= mAnimStringTable.addString("dance2");
+ mAnimMap[ANIM_AGENT_DANCE3]= mAnimStringTable.addString("dance3");
+ mAnimMap[ANIM_AGENT_DANCE4]= mAnimStringTable.addString("dance4");
+ mAnimMap[ANIM_AGENT_DANCE5]= mAnimStringTable.addString("dance5");
+ mAnimMap[ANIM_AGENT_DANCE6]= mAnimStringTable.addString("dance6");
+ mAnimMap[ANIM_AGENT_DANCE7]= mAnimStringTable.addString("dance7");
+ mAnimMap[ANIM_AGENT_DANCE8]= mAnimStringTable.addString("dance8");
+ mAnimMap[ANIM_AGENT_DEAD]= mAnimStringTable.addString("dead");
+ mAnimMap[ANIM_AGENT_DRINK]= mAnimStringTable.addString("drink");
+ mAnimMap[ANIM_AGENT_EMBARRASSED]= mAnimStringTable.addString("express_embarrased");
+ mAnimMap[ANIM_AGENT_EXPRESS_AFRAID]= mAnimStringTable.addString("express_afraid_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_ANGER]= mAnimStringTable.addString("express_anger_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_BORED]= mAnimStringTable.addString("express_bored_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_CRY]= mAnimStringTable.addString("express_cry_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_DISDAIN]= mAnimStringTable.addString("express_disdain");
+ mAnimMap[ANIM_AGENT_EXPRESS_EMBARRASSED]= mAnimStringTable.addString("express_embarrassed_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_FROWN]= mAnimStringTable.addString("express_frown");
+ mAnimMap[ANIM_AGENT_EXPRESS_KISS]= mAnimStringTable.addString("express_kiss");
+ mAnimMap[ANIM_AGENT_EXPRESS_LAUGH]= mAnimStringTable.addString("express_laugh_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_OPEN_MOUTH]= mAnimStringTable.addString("express_open_mouth");
+ mAnimMap[ANIM_AGENT_EXPRESS_REPULSED]= mAnimStringTable.addString("express_repulsed_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SAD]= mAnimStringTable.addString("express_sad_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SHRUG]= mAnimStringTable.addString("express_shrug_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_SMILE]= mAnimStringTable.addString("express_smile");
+ mAnimMap[ANIM_AGENT_EXPRESS_SURPRISE]= mAnimStringTable.addString("express_surprise_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_TONGUE_OUT]= mAnimStringTable.addString("express_tongue_out");
+ mAnimMap[ANIM_AGENT_EXPRESS_TOOTHSMILE]= mAnimStringTable.addString("express_toothsmile");
+ mAnimMap[ANIM_AGENT_EXPRESS_WINK]= mAnimStringTable.addString("express_wink_emote");
+ mAnimMap[ANIM_AGENT_EXPRESS_WORRY]= mAnimStringTable.addString("express_worry_emote");
+ mAnimMap[ANIM_AGENT_FALLDOWN]= mAnimStringTable.addString("falldown");
+ mAnimMap[ANIM_AGENT_FEMALE_WALK]= mAnimStringTable.addString("female_walk");
+ mAnimMap[ANIM_AGENT_FINGER_WAG]= mAnimStringTable.addString("angry_fingerwag");
+ mAnimMap[ANIM_AGENT_FIST_PUMP]= mAnimStringTable.addString("fist_pump");
+ mAnimMap[ANIM_AGENT_FLY]= mAnimStringTable.addString("fly");
+ mAnimMap[ANIM_AGENT_FLYSLOW]= mAnimStringTable.addString("flyslow");
+ mAnimMap[ANIM_AGENT_HELLO]= mAnimStringTable.addString("hello");
+ mAnimMap[ANIM_AGENT_HOLD_BAZOOKA_R]= mAnimStringTable.addString("hold_r_bazooka");
+ mAnimMap[ANIM_AGENT_HOLD_BOW_L]= mAnimStringTable.addString("hold_l_bow");
+ mAnimMap[ANIM_AGENT_HOLD_HANDGUN_R]= mAnimStringTable.addString("hold_r_handgun");
+ mAnimMap[ANIM_AGENT_HOLD_RIFLE_R]= mAnimStringTable.addString("hold_r_rifle");
+ mAnimMap[ANIM_AGENT_HOLD_THROW_R]= mAnimStringTable.addString("hold_throw_r");
+ mAnimMap[ANIM_AGENT_HOVER]= mAnimStringTable.addString("hover");
+ mAnimMap[ANIM_AGENT_HOVER_DOWN]= mAnimStringTable.addString("hover_down");
+ mAnimMap[ANIM_AGENT_HOVER_UP]= mAnimStringTable.addString("hover_up");
+ mAnimMap[ANIM_AGENT_IMPATIENT]= mAnimStringTable.addString("impatient");
+ mAnimMap[ANIM_AGENT_JUMP]= mAnimStringTable.addString("jump");
+ mAnimMap[ANIM_AGENT_JUMP_FOR_JOY]= mAnimStringTable.addString("jumpforjoy");
+ mAnimMap[ANIM_AGENT_KISS_MY_BUTT]= mAnimStringTable.addString("kissmybutt");
+ mAnimMap[ANIM_AGENT_LAND]= mAnimStringTable.addString("land");
+ mAnimMap[ANIM_AGENT_LAUGH_SHORT]= mAnimStringTable.addString("laugh_short");
+ mAnimMap[ANIM_AGENT_MEDIUM_LAND]= mAnimStringTable.addString("soft_land");
+ mAnimMap[ANIM_AGENT_MOTORCYCLE_SIT]= mAnimStringTable.addString("motorcycle_sit");
+ mAnimMap[ANIM_AGENT_MUSCLE_BEACH]= mAnimStringTable.addString("musclebeach");
+ mAnimMap[ANIM_AGENT_NO]= mAnimStringTable.addString("no_head");
+ mAnimMap[ANIM_AGENT_NO_UNHAPPY]= mAnimStringTable.addString("no_unhappy");
+ mAnimMap[ANIM_AGENT_NYAH_NYAH]= mAnimStringTable.addString("nyanya");
+ mAnimMap[ANIM_AGENT_ONETWO_PUNCH]= mAnimStringTable.addString("punch_onetwo");
+ mAnimMap[ANIM_AGENT_PEACE]= mAnimStringTable.addString("peace");
+ mAnimMap[ANIM_AGENT_POINT_ME]= mAnimStringTable.addString("point_me");
+ mAnimMap[ANIM_AGENT_POINT_YOU]= mAnimStringTable.addString("point_you");
+ mAnimMap[ANIM_AGENT_PRE_JUMP]= mAnimStringTable.addString("prejump");
+ mAnimMap[ANIM_AGENT_PUNCH_LEFT]= mAnimStringTable.addString("punch_l");
+ mAnimMap[ANIM_AGENT_PUNCH_RIGHT]= mAnimStringTable.addString("punch_r");
+ mAnimMap[ANIM_AGENT_REPULSED]= mAnimStringTable.addString("express_repulsed");
+ mAnimMap[ANIM_AGENT_ROUNDHOUSE_KICK]= mAnimStringTable.addString("kick_roundhouse_r");
+ mAnimMap[ANIM_AGENT_RPS_COUNTDOWN]= mAnimStringTable.addString("rps_countdown");
+ mAnimMap[ANIM_AGENT_RPS_PAPER]= mAnimStringTable.addString("rps_paper");
+ mAnimMap[ANIM_AGENT_RPS_ROCK]= mAnimStringTable.addString("rps_rock");
+ mAnimMap[ANIM_AGENT_RPS_SCISSORS]= mAnimStringTable.addString("rps_scissors");
+ mAnimMap[ANIM_AGENT_RUN]= mAnimStringTable.addString("run");
+ mAnimMap[ANIM_AGENT_SAD]= mAnimStringTable.addString("express_sad");
+ mAnimMap[ANIM_AGENT_SALUTE]= mAnimStringTable.addString("salute");
+ mAnimMap[ANIM_AGENT_SHOOT_BOW_L]= mAnimStringTable.addString("shoot_l_bow");
+ mAnimMap[ANIM_AGENT_SHOUT]= mAnimStringTable.addString("shout");
+ mAnimMap[ANIM_AGENT_SHRUG]= mAnimStringTable.addString("express_shrug");
+ mAnimMap[ANIM_AGENT_SIT]= mAnimStringTable.addString("sit");
+ mAnimMap[ANIM_AGENT_SIT_FEMALE]= mAnimStringTable.addString("sit_female");
+ mAnimMap[ANIM_AGENT_SIT_GROUND]= mAnimStringTable.addString("sit_ground");
+ mAnimMap[ANIM_AGENT_SIT_GROUND_CONSTRAINED]= mAnimStringTable.addString("sit_ground_constrained");
+ mAnimMap[ANIM_AGENT_SIT_GENERIC]= mAnimStringTable.addString("sit_generic");
+ mAnimMap[ANIM_AGENT_SIT_TO_STAND]= mAnimStringTable.addString("sit_to_stand");
+ mAnimMap[ANIM_AGENT_SLEEP]= mAnimStringTable.addString("sleep");
+ mAnimMap[ANIM_AGENT_SMOKE_IDLE]= mAnimStringTable.addString("smoke_idle");
+ mAnimMap[ANIM_AGENT_SMOKE_INHALE]= mAnimStringTable.addString("smoke_inhale");
+ mAnimMap[ANIM_AGENT_SMOKE_THROW_DOWN]= mAnimStringTable.addString("smoke_throw_down");
+ mAnimMap[ANIM_AGENT_SNAPSHOT]= mAnimStringTable.addString("snapshot");
+ mAnimMap[ANIM_AGENT_STAND]= mAnimStringTable.addString("stand");
+ mAnimMap[ANIM_AGENT_STANDUP]= mAnimStringTable.addString("standup");
+ mAnimMap[ANIM_AGENT_STAND_1]= mAnimStringTable.addString("stand_1");
+ mAnimMap[ANIM_AGENT_STAND_2]= mAnimStringTable.addString("stand_2");
+ mAnimMap[ANIM_AGENT_STAND_3]= mAnimStringTable.addString("stand_3");
+ mAnimMap[ANIM_AGENT_STAND_4]= mAnimStringTable.addString("stand_4");
+ mAnimMap[ANIM_AGENT_STRETCH]= mAnimStringTable.addString("stretch");
+ mAnimMap[ANIM_AGENT_STRIDE]= mAnimStringTable.addString("stride");
+ mAnimMap[ANIM_AGENT_SURF]= mAnimStringTable.addString("surf");
+ mAnimMap[ANIM_AGENT_SURPRISE]= mAnimStringTable.addString("express_surprise");
+ mAnimMap[ANIM_AGENT_SWORD_STRIKE]= mAnimStringTable.addString("sword_strike_r");
+ mAnimMap[ANIM_AGENT_TALK]= mAnimStringTable.addString("talk");
+ mAnimMap[ANIM_AGENT_TANTRUM]= mAnimStringTable.addString("angry_tantrum");
+ mAnimMap[ANIM_AGENT_THROW_R]= mAnimStringTable.addString("throw_r");
+ mAnimMap[ANIM_AGENT_TRYON_SHIRT]= mAnimStringTable.addString("tryon_shirt");
+ mAnimMap[ANIM_AGENT_TURNLEFT]= mAnimStringTable.addString("turnleft");
+ mAnimMap[ANIM_AGENT_TURNRIGHT]= mAnimStringTable.addString("turnright");
+ mAnimMap[ANIM_AGENT_TYPE]= mAnimStringTable.addString("type");
+ mAnimMap[ANIM_AGENT_WALK]= mAnimStringTable.addString("walk");
+ mAnimMap[ANIM_AGENT_WHISPER]= mAnimStringTable.addString("whisper");
+ mAnimMap[ANIM_AGENT_WHISTLE]= mAnimStringTable.addString("whistle");
+ mAnimMap[ANIM_AGENT_WINK]= mAnimStringTable.addString("express_wink");
+ mAnimMap[ANIM_AGENT_WINK_HOLLYWOOD]= mAnimStringTable.addString("wink_hollywood");
+ mAnimMap[ANIM_AGENT_WORRY]= mAnimStringTable.addString("express_worry");
+ mAnimMap[ANIM_AGENT_YES]= mAnimStringTable.addString("yes_head");
+ mAnimMap[ANIM_AGENT_YES_HAPPY]= mAnimStringTable.addString("yes_happy");
+ mAnimMap[ANIM_AGENT_YOGA_FLOAT]= mAnimStringTable.addString("yoga_float");
+}
+
+//-----------------------------------------------------------------------------
+// ~LLAnimationLibrary()
+//-----------------------------------------------------------------------------
+LLAnimationLibrary::~LLAnimationLibrary()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Return the text name of an animation state
+//-----------------------------------------------------------------------------
+const char *LLAnimationLibrary::animStateToString( const LLUUID& state )
+{
+ if (state.isNull())
+ {
+ return NULL;
+ }
+ if (mAnimMap.count(state))
+ {
+ return mAnimMap[state];
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Return the animation state for a given name
+//-----------------------------------------------------------------------------
+LLUUID LLAnimationLibrary::stringToAnimState( const char *name, BOOL allow_ids )
+{
+ LLString lower_case_name(name);
+ LLString::toLower(lower_case_name);
+
+ char *true_name = mAnimStringTable.checkString(lower_case_name.c_str());
+
+ LLUUID id;
+ id.setNull();
+
+ if (true_name)
+ {
+ for (anim_map_t::iterator iter = mAnimMap.begin();
+ iter != mAnimMap.end(); iter++)
+ {
+ if (iter->second == true_name)
+ {
+ id = iter->first;
+ break;
+ }
+ }
+ }
+ else if (allow_ids)
+ {
+ // try to convert string to LLUUID
+ id.set(name, FALSE);
+ }
+
+ return id;
+}
+
+// Animation states that the user can trigger as part of a gesture
+const LLAnimStateEntry gUserAnimStates[] = {
+ LLAnimStateEntry("Afraid", "express_afraid", ANIM_AGENT_AFRAID),
+ LLAnimStateEntry("Angry", "express_anger", ANIM_AGENT_ANGRY),
+ LLAnimStateEntry("Away", "away", ANIM_AGENT_AWAY),
+ LLAnimStateEntry("Backflip", "backflip", ANIM_AGENT_BACKFLIP),
+ LLAnimStateEntry("Belly Laugh", "express_laugh", ANIM_AGENT_BELLY_LAUGH),
+ LLAnimStateEntry("BigSmile", "express_toothsmile", ANIM_AGENT_EXPRESS_TOOTHSMILE),
+ LLAnimStateEntry("Blow Kiss", "blowkiss", ANIM_AGENT_BLOW_KISS),
+ LLAnimStateEntry("Bored", "express_bored", ANIM_AGENT_BORED),
+ LLAnimStateEntry("Bow", "bow", ANIM_AGENT_BOW),
+ LLAnimStateEntry("Clap", "clap", ANIM_AGENT_CLAP),
+ LLAnimStateEntry("Court Bow", "courtbow", ANIM_AGENT_COURTBOW),
+ LLAnimStateEntry("Cry", "express_cry", ANIM_AGENT_CRY),
+ LLAnimStateEntry("Dance 1", "dance1", ANIM_AGENT_DANCE1),
+ LLAnimStateEntry("Dance 2", "dance2", ANIM_AGENT_DANCE2),
+ LLAnimStateEntry("Dance 3", "dance3", ANIM_AGENT_DANCE3),
+ LLAnimStateEntry("Dance 4", "dance4", ANIM_AGENT_DANCE4),
+ LLAnimStateEntry("Dance 5", "dance5", ANIM_AGENT_DANCE5),
+ LLAnimStateEntry("Dance 6", "dance6", ANIM_AGENT_DANCE6),
+ LLAnimStateEntry("Dance 7", "dance7", ANIM_AGENT_DANCE7),
+ LLAnimStateEntry("Dance 8", "dance8", ANIM_AGENT_DANCE8),
+ LLAnimStateEntry("Disdain", "express_disdain", ANIM_AGENT_EXPRESS_DISDAIN),
+ LLAnimStateEntry("Drink", "drink", ANIM_AGENT_DRINK),
+ LLAnimStateEntry("Embarrassed", "express_embarrased", ANIM_AGENT_EMBARRASSED),
+ LLAnimStateEntry("Finger Wag", "angry_fingerwag", ANIM_AGENT_FINGER_WAG),
+ LLAnimStateEntry("Fist Pump", "fist_pump", ANIM_AGENT_FIST_PUMP),
+ LLAnimStateEntry("Floating Yoga", "yoga_float", ANIM_AGENT_YOGA_FLOAT),
+ LLAnimStateEntry("Frown", "express_frown", ANIM_AGENT_EXPRESS_FROWN),
+ LLAnimStateEntry("Impatient", "impatient", ANIM_AGENT_IMPATIENT),
+ LLAnimStateEntry("Jump For Joy", "jumpforjoy", ANIM_AGENT_JUMP_FOR_JOY),
+ LLAnimStateEntry("Kiss My Butt", "kissmybutt", ANIM_AGENT_KISS_MY_BUTT),
+ LLAnimStateEntry("Kiss", "express_kiss", ANIM_AGENT_EXPRESS_KISS),
+ LLAnimStateEntry("Laugh", "laugh_short", ANIM_AGENT_LAUGH_SHORT),
+ LLAnimStateEntry("Muscle Beach", "musclebeach", ANIM_AGENT_MUSCLE_BEACH),
+ LLAnimStateEntry("No (Unhappy)", "no_unhappy", ANIM_AGENT_NO_UNHAPPY),
+ LLAnimStateEntry("No", "no_head", ANIM_AGENT_NO),
+ LLAnimStateEntry("Nya-nya-nya", "nyanya", ANIM_AGENT_NYAH_NYAH),
+ LLAnimStateEntry("One-Two Punch", "punch_onetwo", ANIM_AGENT_ONETWO_PUNCH),
+ LLAnimStateEntry("Open Mouth", "express_open_mouth", ANIM_AGENT_EXPRESS_OPEN_MOUTH),
+ LLAnimStateEntry("Peace", "peace", ANIM_AGENT_PEACE),
+ LLAnimStateEntry("Point at Other", "point_you", ANIM_AGENT_POINT_YOU),
+ LLAnimStateEntry("Point at Self", "point_me", ANIM_AGENT_POINT_ME),
+ LLAnimStateEntry("Punch Left", "punch_l", ANIM_AGENT_PUNCH_LEFT),
+ LLAnimStateEntry("Punch Right", "punch_r", ANIM_AGENT_PUNCH_RIGHT),
+ LLAnimStateEntry("RPS count", "rps_countdown", ANIM_AGENT_RPS_COUNTDOWN),
+ LLAnimStateEntry("RPS paper", "rps_paper", ANIM_AGENT_RPS_PAPER),
+ LLAnimStateEntry("RPS rock", "rps_rock", ANIM_AGENT_RPS_ROCK),
+ LLAnimStateEntry("RPS scissors", "rps_scissors", ANIM_AGENT_RPS_SCISSORS),
+ LLAnimStateEntry("Repulsed", "express_repulsed", ANIM_AGENT_EXPRESS_REPULSED),
+ LLAnimStateEntry("Roundhouse Kick", "kick_roundhouse_r", ANIM_AGENT_ROUNDHOUSE_KICK),
+ LLAnimStateEntry("Sad", "express_sad", ANIM_AGENT_SAD),
+ LLAnimStateEntry("Salute", "salute", ANIM_AGENT_SALUTE),
+ LLAnimStateEntry("Shout", "shout", ANIM_AGENT_SHOUT),
+ LLAnimStateEntry("Shrug", "express_shrug", ANIM_AGENT_SHRUG),
+ LLAnimStateEntry("Smile", "express_smile", ANIM_AGENT_EXPRESS_SMILE),
+ LLAnimStateEntry("Smoke Idle", "smoke_idle", ANIM_AGENT_SMOKE_IDLE),
+ LLAnimStateEntry("Smoke Inhale", "smoke_inhale", ANIM_AGENT_SMOKE_INHALE),
+ LLAnimStateEntry("Smoke Throw Down","smoke_throw_down", ANIM_AGENT_SMOKE_THROW_DOWN),
+ LLAnimStateEntry("Surprise", "express_surprise", ANIM_AGENT_SURPRISE),
+ LLAnimStateEntry("Sword Strike", "sword_strike_r", ANIM_AGENT_SWORD_STRIKE),
+ LLAnimStateEntry("Tantrum", "angry_tantrum", ANIM_AGENT_TANTRUM),
+ LLAnimStateEntry("TongueOut", "express_tongue_out", ANIM_AGENT_EXPRESS_TONGUE_OUT),
+ LLAnimStateEntry("Wave", "hello", ANIM_AGENT_HELLO),
+ LLAnimStateEntry("Whisper", "whisper", ANIM_AGENT_WHISPER),
+ LLAnimStateEntry("Whistle", "whistle", ANIM_AGENT_WHISTLE),
+ LLAnimStateEntry("Wink", "express_wink", ANIM_AGENT_WINK),
+ LLAnimStateEntry("Wink (Hollywood)","wink_hollywood", ANIM_AGENT_WINK_HOLLYWOOD),
+ LLAnimStateEntry("Worry", "express_worry", ANIM_AGENT_EXPRESS_WORRY),
+ LLAnimStateEntry("Yes (Happy)", "yes_happy", ANIM_AGENT_YES_HAPPY),
+ LLAnimStateEntry("Yes", "yes_head", ANIM_AGENT_YES),
+};
+
+const S32 gUserAnimStatesCount = sizeof(gUserAnimStates) / sizeof(gUserAnimStates[0]);
+
+
+
+// End
+
diff --git a/indra/llcharacter/llanimationstates.h b/indra/llcharacter/llanimationstates.h
new file mode 100644
index 0000000000..31fe0dfc11
--- /dev/null
+++ b/indra/llcharacter/llanimationstates.h
@@ -0,0 +1,227 @@
+/**
+ * @file llanimationstates.h
+ * @brief Implementation of animation state support.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLANIMATIONSTATES_H
+#define LL_LLANIMATIONSTATES_H
+
+#include <map>
+
+#include "string_table.h"
+#include "lluuid.h"
+
+//-----------------------------------------------------------------------------
+// These bit flags are generally used to track the animation state
+// of characters. The simulator and viewer share these flags to interpret
+// the Animation name/value attribute on agents.
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Agent Animation State
+//-----------------------------------------------------------------------------
+const S32 MAX_CONCURRENT_ANIMS = 16;
+
+
+const LLUUID ANIM_AGENT_AFRAID = LLUUID("6b61c8e8-4747-0d75-12d7-e49ff207a4ca");
+const LLUUID ANIM_AGENT_AIM_BAZOOKA_R = LLUUID("b5b4a67d-0aee-30d2-72cd-77b333e932ef");
+const LLUUID ANIM_AGENT_AIM_BOW_L = LLUUID("46bb4359-de38-4ed8-6a22-f1f52fe8f506");
+const LLUUID ANIM_AGENT_AIM_HANDGUN_R = LLUUID("3147d815-6338-b932-f011-16b56d9ac18b");
+const LLUUID ANIM_AGENT_AIM_RIFLE_R = LLUUID("ea633413-8006-180a-c3ba-96dd1d756720");
+const LLUUID ANIM_AGENT_ANGRY = LLUUID("5747a48e-073e-c331-f6f3-7c2149613d3e");
+const LLUUID ANIM_AGENT_AWAY = LLUUID("fd037134-85d4-f241-72c6-4f42164fedee");
+const LLUUID ANIM_AGENT_BACKFLIP = LLUUID("c4ca6188-9127-4f31-0158-23c4e2f93304");
+const LLUUID ANIM_AGENT_BELLY_LAUGH = LLUUID("18b3a4b5-b463-bd48-e4b6-71eaac76c515");
+const LLUUID ANIM_AGENT_BLOW_KISS = LLUUID("db84829b-462c-ee83-1e27-9bbee66bd624");
+const LLUUID ANIM_AGENT_BORED = LLUUID("b906c4ba-703b-1940-32a3-0c7f7d791510");
+const LLUUID ANIM_AGENT_BOW = LLUUID("82e99230-c906-1403-4d9c-3889dd98daba");
+const LLUUID ANIM_AGENT_BRUSH = LLUUID("349a3801-54f9-bf2c-3bd0-1ac89772af01");
+const LLUUID ANIM_AGENT_BUSY = LLUUID("efcf670c-2d18-8128-973a-034ebc806b67");
+const LLUUID ANIM_AGENT_CLAP = LLUUID("9b0c1c4e-8ac7-7969-1494-28c874c4f668");
+const LLUUID ANIM_AGENT_COURTBOW = LLUUID("9ba1c942-08be-e43a-fb29-16ad440efc50");
+const LLUUID ANIM_AGENT_CROUCH = LLUUID("201f3fdf-cb1f-dbec-201f-7333e328ae7c");
+const LLUUID ANIM_AGENT_CROUCHWALK = LLUUID("47f5f6fb-22e5-ae44-f871-73aaaf4a6022");
+const LLUUID ANIM_AGENT_CRY = LLUUID("92624d3e-1068-f1aa-a5ec-8244585193ed");
+const LLUUID ANIM_AGENT_CUSTOMIZE = LLUUID("038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53");
+const LLUUID ANIM_AGENT_CUSTOMIZE_DONE = LLUUID("6883a61a-b27b-5914-a61e-dda118a9ee2c");
+const LLUUID ANIM_AGENT_DANCE1 = LLUUID("b68a3d7c-de9e-fc87-eec8-543d787e5b0d");
+const LLUUID ANIM_AGENT_DANCE2 = LLUUID("928cae18-e31d-76fd-9cc9-2f55160ff818");
+const LLUUID ANIM_AGENT_DANCE3 = LLUUID("30047778-10ea-1af7-6881-4db7a3a5a114");
+const LLUUID ANIM_AGENT_DANCE4 = LLUUID("951469f4-c7b2-c818-9dee-ad7eea8c30b7");
+const LLUUID ANIM_AGENT_DANCE5 = LLUUID("4bd69a1d-1114-a0b4-625f-84e0a5237155");
+const LLUUID ANIM_AGENT_DANCE6 = LLUUID("cd28b69b-9c95-bb78-3f94-8d605ff1bb12");
+const LLUUID ANIM_AGENT_DANCE7 = LLUUID("a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6");
+const LLUUID ANIM_AGENT_DANCE8 = LLUUID("b0dc417c-1f11-af36-2e80-7e7489fa7cdc");
+const LLUUID ANIM_AGENT_DEAD = LLUUID("57abaae6-1d17-7b1b-5f98-6d11a6411276");
+const LLUUID ANIM_AGENT_DRINK = LLUUID("0f86e355-dd31-a61c-fdb0-3a96b9aad05f");
+const LLUUID ANIM_AGENT_EMBARRASSED = LLUUID("514af488-9051-044a-b3fc-d4dbf76377c6");
+const LLUUID ANIM_AGENT_EXPRESS_AFRAID = LLUUID("aa2df84d-cf8f-7218-527b-424a52de766e");
+const LLUUID ANIM_AGENT_EXPRESS_ANGER = LLUUID("1a03b575-9634-b62a-5767-3a679e81f4de");
+const LLUUID ANIM_AGENT_EXPRESS_BORED = LLUUID("214aa6c1-ba6a-4578-f27c-ce7688f61d0d");
+const LLUUID ANIM_AGENT_EXPRESS_CRY = LLUUID("d535471b-85bf-3b4d-a542-93bea4f59d33");
+const LLUUID ANIM_AGENT_EXPRESS_DISDAIN = LLUUID("d4416ff1-09d3-300f-4183-1b68a19b9fc1");
+const LLUUID ANIM_AGENT_EXPRESS_EMBARRASSED = LLUUID("0b8c8211-d78c-33e8-fa28-c51a9594e424");
+const LLUUID ANIM_AGENT_EXPRESS_FROWN = LLUUID("fee3df48-fa3d-1015-1e26-a205810e3001");
+const LLUUID ANIM_AGENT_EXPRESS_KISS = LLUUID("1e8d90cc-a84e-e135-884c-7c82c8b03a14");
+const LLUUID ANIM_AGENT_EXPRESS_LAUGH = LLUUID("62570842-0950-96f8-341c-809e65110823");
+const LLUUID ANIM_AGENT_EXPRESS_OPEN_MOUTH = LLUUID("d63bc1f9-fc81-9625-a0c6-007176d82eb7");
+const LLUUID ANIM_AGENT_EXPRESS_REPULSED = LLUUID("f76cda94-41d4-a229-2872-e0296e58afe1");
+const LLUUID ANIM_AGENT_EXPRESS_SAD = LLUUID("eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7");
+const LLUUID ANIM_AGENT_EXPRESS_SHRUG = LLUUID("a351b1bc-cc94-aac2-7bea-a7e6ebad15ef");
+const LLUUID ANIM_AGENT_EXPRESS_SMILE = LLUUID("b7c7c833-e3d3-c4e3-9fc0-131237446312");
+const LLUUID ANIM_AGENT_EXPRESS_SURPRISE = LLUUID("728646d9-cc79-08b2-32d6-937f0a835c24");
+const LLUUID ANIM_AGENT_EXPRESS_TONGUE_OUT = LLUUID("835965c6-7f2f-bda2-5deb-2478737f91bf");
+const LLUUID ANIM_AGENT_EXPRESS_TOOTHSMILE = LLUUID("b92ec1a5-e7ce-a76b-2b05-bcdb9311417e");
+const LLUUID ANIM_AGENT_EXPRESS_WINK = LLUUID("da020525-4d94-59d6-23d7-81fdebf33148");
+const LLUUID ANIM_AGENT_EXPRESS_WORRY = LLUUID("9c05e5c7-6f07-6ca4-ed5a-b230390c3950");
+const LLUUID ANIM_AGENT_FALLDOWN = LLUUID("666307d9-a860-572d-6fd4-c3ab8865c094");
+const LLUUID ANIM_AGENT_FEMALE_WALK = LLUUID("f5fc7433-043d-e819-8298-f519a119b688");
+const LLUUID ANIM_AGENT_FINGER_WAG = LLUUID("c1bc7f36-3ba0-d844-f93c-93be945d644f");
+const LLUUID ANIM_AGENT_FIST_PUMP = LLUUID("7db00ccd-f380-f3ee-439d-61968ec69c8a");
+const LLUUID ANIM_AGENT_FLY = LLUUID("aec4610c-757f-bc4e-c092-c6e9caf18daf");
+const LLUUID ANIM_AGENT_FLYSLOW = LLUUID("2b5a38b2-5e00-3a97-a495-4c826bc443e6");
+const LLUUID ANIM_AGENT_HELLO = LLUUID("9b29cd61-c45b-5689-ded2-91756b8d76a9");
+const LLUUID ANIM_AGENT_HOLD_BAZOOKA_R = LLUUID("ef62d355-c815-4816-2474-b1acc21094a6");
+const LLUUID ANIM_AGENT_HOLD_BOW_L = LLUUID("8b102617-bcba-037b-86c1-b76219f90c88");
+const LLUUID ANIM_AGENT_HOLD_HANDGUN_R = LLUUID("efdc1727-8b8a-c800-4077-975fc27ee2f2");
+const LLUUID ANIM_AGENT_HOLD_RIFLE_R = LLUUID("3d94bad0-c55b-7dcc-8763-033c59405d33");
+const LLUUID ANIM_AGENT_HOLD_THROW_R = LLUUID("7570c7b5-1f22-56dd-56ef-a9168241bbb6");
+const LLUUID ANIM_AGENT_HOVER = LLUUID("4ae8016b-31b9-03bb-c401-b1ea941db41d");
+const LLUUID ANIM_AGENT_HOVER_DOWN = LLUUID("20f063ea-8306-2562-0b07-5c853b37b31e");
+const LLUUID ANIM_AGENT_HOVER_UP = LLUUID("62c5de58-cb33-5743-3d07-9e4cd4352864");
+const LLUUID ANIM_AGENT_IMPATIENT = LLUUID("5ea3991f-c293-392e-6860-91dfa01278a3");
+const LLUUID ANIM_AGENT_JUMP = LLUUID("2305bd75-1ca9-b03b-1faa-b176b8a8c49e");
+const LLUUID ANIM_AGENT_JUMP_FOR_JOY = LLUUID("709ea28e-1573-c023-8bf8-520c8bc637fa");
+const LLUUID ANIM_AGENT_KISS_MY_BUTT = LLUUID("19999406-3a3a-d58c-a2ac-d72e555dcf51");
+const LLUUID ANIM_AGENT_LAND = LLUUID("7a17b059-12b2-41b1-570a-186368b6aa6f");
+const LLUUID ANIM_AGENT_LAUGH_SHORT = LLUUID("ca5b3f14-3194-7a2b-c894-aa699b718d1f");
+const LLUUID ANIM_AGENT_MEDIUM_LAND = LLUUID("f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57");
+const LLUUID ANIM_AGENT_MOTORCYCLE_SIT = LLUUID("08464f78-3a8e-2944-cba5-0c94aff3af29");
+const LLUUID ANIM_AGENT_MUSCLE_BEACH = LLUUID("315c3a41-a5f3-0ba4-27da-f893f769e69b");
+const LLUUID ANIM_AGENT_NO = LLUUID("5a977ed9-7f72-44e9-4c4c-6e913df8ae74");
+const LLUUID ANIM_AGENT_NO_UNHAPPY = LLUUID("d83fa0e5-97ed-7eb2-e798-7bd006215cb4");
+const LLUUID ANIM_AGENT_NYAH_NYAH = LLUUID("f061723d-0a18-754f-66ee-29a44795a32f");
+const LLUUID ANIM_AGENT_ONETWO_PUNCH = LLUUID("eefc79be-daae-a239-8c04-890f5d23654a");
+const LLUUID ANIM_AGENT_PEACE = LLUUID("b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9");
+const LLUUID ANIM_AGENT_POINT_ME = LLUUID("17c024cc-eef2-f6a0-3527-9869876d7752");
+const LLUUID ANIM_AGENT_POINT_YOU = LLUUID("ec952cca-61ef-aa3b-2789-4d1344f016de");
+const LLUUID ANIM_AGENT_PRE_JUMP = LLUUID("7a4e87fe-de39-6fcb-6223-024b00893244");
+const LLUUID ANIM_AGENT_PUNCH_LEFT = LLUUID("f3300ad9-3462-1d07-2044-0fef80062da0");
+const LLUUID ANIM_AGENT_PUNCH_RIGHT = LLUUID("c8e42d32-7310-6906-c903-cab5d4a34656");
+const LLUUID ANIM_AGENT_REPULSED = LLUUID("36f81a92-f076-5893-dc4b-7c3795e487cf");
+const LLUUID ANIM_AGENT_ROUNDHOUSE_KICK = LLUUID("49aea43b-5ac3-8a44-b595-96100af0beda");
+const LLUUID ANIM_AGENT_RPS_COUNTDOWN = LLUUID("35db4f7e-28c2-6679-cea9-3ee108f7fc7f");
+const LLUUID ANIM_AGENT_RPS_PAPER = LLUUID("0836b67f-7f7b-f37b-c00a-460dc1521f5a");
+const LLUUID ANIM_AGENT_RPS_ROCK = LLUUID("42dd95d5-0bc6-6392-f650-777304946c0f");
+const LLUUID ANIM_AGENT_RPS_SCISSORS = LLUUID("16803a9f-5140-e042-4d7b-d28ba247c325");
+const LLUUID ANIM_AGENT_RUN = LLUUID("05ddbff8-aaa9-92a1-2b74-8fe77a29b445");
+const LLUUID ANIM_AGENT_SAD = LLUUID("0eb702e2-cc5a-9a88-56a5-661a55c0676a");
+const LLUUID ANIM_AGENT_SALUTE = LLUUID("cd7668a6-7011-d7e2-ead8-fc69eff1a104");
+const LLUUID ANIM_AGENT_SHOOT_BOW_L = LLUUID("e04d450d-fdb5-0432-fd68-818aaf5935f8");
+const LLUUID ANIM_AGENT_SHOUT = LLUUID("6bd01860-4ebd-127a-bb3d-d1427e8e0c42");
+const LLUUID ANIM_AGENT_SHRUG = LLUUID("70ea714f-3a97-d742-1b01-590a8fcd1db5");
+const LLUUID ANIM_AGENT_SIT = LLUUID("1a5fe8ac-a804-8a5d-7cbd-56bd83184568");
+const LLUUID ANIM_AGENT_SIT_FEMALE = LLUUID("b1709c8d-ecd3-54a1-4f28-d55ac0840782");
+const LLUUID ANIM_AGENT_SIT_GENERIC = LLUUID("245f3c54-f1c0-bf2e-811f-46d8eeb386e7");
+const LLUUID ANIM_AGENT_SIT_GROUND = LLUUID("1c7600d6-661f-b87b-efe2-d7421eb93c86");
+const LLUUID ANIM_AGENT_SIT_GROUND_CONSTRAINED = LLUUID("1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e");
+const LLUUID ANIM_AGENT_SIT_TO_STAND = LLUUID("a8dee56f-2eae-9e7a-05a2-6fb92b97e21e");
+const LLUUID ANIM_AGENT_SLEEP = LLUUID("f2bed5f9-9d44-39af-b0cd-257b2a17fe40");
+const LLUUID ANIM_AGENT_SMOKE_IDLE = LLUUID("d2f2ee58-8ad1-06c9-d8d3-3827ba31567a");
+const LLUUID ANIM_AGENT_SMOKE_INHALE = LLUUID("6802d553-49da-0778-9f85-1599a2266526");
+const LLUUID ANIM_AGENT_SMOKE_THROW_DOWN = LLUUID("0a9fb970-8b44-9114-d3a9-bf69cfe804d6");
+const LLUUID ANIM_AGENT_SNAPSHOT = LLUUID("eae8905b-271a-99e2-4c0e-31106afd100c");
+const LLUUID ANIM_AGENT_STAND = LLUUID("2408fe9e-df1d-1d7d-f4ff-1384fa7b350f");
+const LLUUID ANIM_AGENT_STANDUP = LLUUID("3da1d753-028a-5446-24f3-9c9b856d9422");
+const LLUUID ANIM_AGENT_STAND_1 = LLUUID("15468e00-3400-bb66-cecc-646d7c14458e");
+const LLUUID ANIM_AGENT_STAND_2 = LLUUID("370f3a20-6ca6-9971-848c-9a01bc42ae3c");
+const LLUUID ANIM_AGENT_STAND_3 = LLUUID("42b46214-4b44-79ae-deb8-0df61424ff4b");
+const LLUUID ANIM_AGENT_STAND_4 = LLUUID("f22fed8b-a5ed-2c93-64d5-bdd8b93c889f");
+const LLUUID ANIM_AGENT_STRETCH = LLUUID("80700431-74ec-a008-14f8-77575e73693f");
+const LLUUID ANIM_AGENT_STRIDE = LLUUID("1cb562b0-ba21-2202-efb3-30f82cdf9595");
+const LLUUID ANIM_AGENT_SURF = LLUUID("41426836-7437-7e89-025d-0aa4d10f1d69");
+const LLUUID ANIM_AGENT_SURPRISE = LLUUID("313b9881-4302-73c0-c7d0-0e7a36b6c224");
+const LLUUID ANIM_AGENT_SWORD_STRIKE = LLUUID("85428680-6bf9-3e64-b489-6f81087c24bd");
+const LLUUID ANIM_AGENT_TALK = LLUUID("5c682a95-6da4-a463-0bf6-0f5b7be129d1");
+const LLUUID ANIM_AGENT_TANTRUM = LLUUID("11000694-3f41-adc2-606b-eee1d66f3724");
+const LLUUID ANIM_AGENT_THROW_R = LLUUID("aa134404-7dac-7aca-2cba-435f9db875ca");
+const LLUUID ANIM_AGENT_TRYON_SHIRT = LLUUID("83ff59fe-2346-f236-9009-4e3608af64c1");
+const LLUUID ANIM_AGENT_TURNLEFT = LLUUID("56e0ba0d-4a9f-7f27-6117-32f2ebbf6135");
+const LLUUID ANIM_AGENT_TURNRIGHT = LLUUID("2d6daa51-3192-6794-8e2e-a15f8338ec30");
+const LLUUID ANIM_AGENT_TYPE = LLUUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9");
+const LLUUID ANIM_AGENT_WALK = LLUUID("6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0");
+const LLUUID ANIM_AGENT_WHISPER = LLUUID("7693f268-06c7-ea71-fa21-2b30d6533f8f");
+const LLUUID ANIM_AGENT_WHISTLE = LLUUID("b1ed7982-c68e-a982-7561-52a88a5298c0");
+const LLUUID ANIM_AGENT_WINK = LLUUID("869ecdad-a44b-671e-3266-56aef2e3ac2e");
+const LLUUID ANIM_AGENT_WINK_HOLLYWOOD = LLUUID("c0c4030f-c02b-49de-24ba-2331f43fe41c");
+const LLUUID ANIM_AGENT_WORRY = LLUUID("9f496bd2-589a-709f-16cc-69bf7df1d36c");
+const LLUUID ANIM_AGENT_YES = LLUUID("15dd911d-be82-2856-26db-27659b142875");
+const LLUUID ANIM_AGENT_YES_HAPPY = LLUUID("b8c8b2a3-9008-1771-3bfc-90924955ab2d");
+const LLUUID ANIM_AGENT_YOGA_FLOAT = LLUUID("42ecd00b-9947-a97c-400a-bbc9174c7aeb");
+
+extern LLUUID AGENT_WALK_ANIMS[];
+extern S32 NUM_AGENT_WALK_ANIMS;
+
+extern LLUUID AGENT_GUN_HOLD_ANIMS[];
+extern S32 NUM_AGENT_GUN_HOLD_ANIMS;
+
+extern LLUUID AGENT_GUN_AIM_ANIMS[];
+extern S32 NUM_AGENT_GUN_AIM_ANIMS;
+
+extern LLUUID AGENT_NO_ROTATE_ANIMS[];
+extern S32 NUM_AGENT_NO_ROTATE_ANIMS;
+
+extern LLUUID AGENT_STAND_ANIMS[];
+extern S32 NUM_AGENT_STAND_ANIMS;
+
+class LLAnimationLibrary
+{
+private:
+ LLStringTable mAnimStringTable;
+
+ typedef std::map<LLUUID, char *> anim_map_t;
+ anim_map_t mAnimMap;
+
+public:
+ LLAnimationLibrary();
+ ~LLAnimationLibrary();
+
+ //-----------------------------------------------------------------------------
+ // Return the text name of a single animation state,
+ // Return NULL if the state is invalid
+ //-----------------------------------------------------------------------------
+ const char *animStateToString( const LLUUID& state );
+
+ //-----------------------------------------------------------------------------
+ // Return the animation state for the given name.
+ // Retun NULL if the name is invalid.
+ //-----------------------------------------------------------------------------
+ LLUUID stringToAnimState( const char *name, BOOL allow_ids = TRUE );
+};
+
+struct LLAnimStateEntry
+{
+ LLAnimStateEntry(const char* label, const char* name, const LLUUID& id)
+ : mLabel(label),
+ mName(name),
+ mID(id)
+ { }
+
+ const char* mLabel;
+ const char* mName;
+ const LLUUID mID;
+};
+
+// Animation states that the user can trigger
+extern const LLAnimStateEntry gUserAnimStates[];
+extern const S32 gUserAnimStatesCount;
+extern LLAnimationLibrary gAnimLibrary;
+
+
+#endif // LL_LLANIMATIONSTATES_H
+
+
+
diff --git a/indra/llcharacter/llbvhloader.cpp b/indra/llcharacter/llbvhloader.cpp
new file mode 100644
index 0000000000..4d4ad39080
--- /dev/null
+++ b/indra/llcharacter/llbvhloader.cpp
@@ -0,0 +1,1489 @@
+/**
+ * @file llbvhloader.cpp
+ * @brief Translates a BVH files to LindenLabAnimation format.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbvhloader.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llkeyframemotion.h"
+#include "llquantize.h"
+#include "llstl.h"
+#include "llapr.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <llquaternion.h>
+#include <boost/tokenizer.hpp>
+
+using namespace std;
+
+#define INCHES_TO_METERS 0.02540005f
+
+const F32 POSITION_KEYFRAME_THRESHOLD = 0.03f;
+const F32 ROTATION_KEYFRAME_THRESHOLD = 0.01f;
+
+const F32 POSITION_MOTION_THRESHOLD = 0.001f;
+const F32 ROTATION_MOTION_THRESHOLD = 0.001f;
+
+char gInFile[1024]; /* Flawfinder: ignore */
+char gOutFile[1024]; /* Flawfinder: ignore */
+
+//------------------------------------------------------------------------
+// Status Codes
+//------------------------------------------------------------------------
+char *LLBVHLoader::ST_OK = "Ok";
+char *LLBVHLoader::ST_EOF = "Premature end of file.";
+char *LLBVHLoader::ST_NO_CONSTRAINT = "Can't read constraint definition.";
+char *LLBVHLoader::ST_NO_FILE = "Can't open BVH file.";
+char *LLBVHLoader::ST_NO_HIER = "Invalid HIERARCHY header.";
+char *LLBVHLoader::ST_NO_JOINT = "Can't find ROOT or JOINT.";
+char *LLBVHLoader::ST_NO_NAME = "Can't get JOINT name.";
+char *LLBVHLoader::ST_NO_OFFSET = "Can't find OFFSET.";
+char *LLBVHLoader::ST_NO_CHANNELS = "Can't find CHANNELS.";
+char *LLBVHLoader::ST_NO_ROTATION = "Can't get rotation order.";
+char *LLBVHLoader::ST_NO_AXIS = "Can't get rotation axis.";
+char *LLBVHLoader::ST_NO_MOTION = "Can't find MOTION.";
+char *LLBVHLoader::ST_NO_FRAMES = "Can't get number of frames.";
+char *LLBVHLoader::ST_NO_FRAME_TIME = "Can't get frame time.";
+char *LLBVHLoader::ST_NO_POS = "Can't get position values.";
+char *LLBVHLoader::ST_NO_ROT = "Can't get rotation values.";
+char *LLBVHLoader::ST_NO_XLT_FILE = "Can't open translation file.";
+char *LLBVHLoader::ST_NO_XLT_HEADER = "Can't read translation header.";
+char *LLBVHLoader::ST_NO_XLT_NAME = "Can't read translation names.";
+char *LLBVHLoader::ST_NO_XLT_IGNORE = "Can't read translation ignore value.";
+char *LLBVHLoader::ST_NO_XLT_RELATIVE = "Can't read translation relative value.";
+char *LLBVHLoader::ST_NO_XLT_OUTNAME = "Can't read translation outname value.";
+char *LLBVHLoader::ST_NO_XLT_MATRIX = "Can't read translation matrix.";
+char *LLBVHLoader::ST_NO_XLT_MERGECHILD = "Can't get mergechild name.";
+char *LLBVHLoader::ST_NO_XLT_MERGEPARENT = "Can't get mergeparent name.";
+char *LLBVHLoader::ST_NO_XLT_PRIORITY = "Can't get priority value.";
+char *LLBVHLoader::ST_NO_XLT_LOOP = "Can't get loop value.";
+char *LLBVHLoader::ST_NO_XLT_EASEIN = "Can't get easeIn values.";
+char *LLBVHLoader::ST_NO_XLT_EASEOUT = "Can't get easeOut values.";
+char *LLBVHLoader::ST_NO_XLT_HAND = "Can't get hand morph value.";
+char *LLBVHLoader::ST_NO_XLT_EMOTE = "Can't read emote name.";
+
+//------------------------------------------------------------------------
+// find_next_whitespace()
+//------------------------------------------------------------------------
+const char *find_next_whitespace(const char *p)
+{
+ while(*p && isspace(*p)) p++;
+ while(*p && !isspace(*p)) p++;
+ return p;
+}
+
+
+//------------------------------------------------------------------------
+// bvhStringToOrder()
+//
+// XYZ order in BVH files must be passed to mayaQ() as ZYX.
+// This function reverses the input string before passing it on
+// to StringToOrder().
+//------------------------------------------------------------------------
+LLQuaternion::Order bvhStringToOrder( char *str )
+{
+ char order[4]; /* Flawfinder: ignore */
+ order[0] = str[2];
+ order[1] = str[1];
+ order[2] = str[0];
+ order[3] = 0;
+ LLQuaternion::Order retVal = StringToOrder( order );
+ return retVal;
+}
+
+//-----------------------------------------------------------------------------
+// LLBVHLoader()
+//-----------------------------------------------------------------------------
+LLBVHLoader::LLBVHLoader(const char* buffer)
+{
+ reset();
+
+ mStatus = loadTranslationTable("anim.ini");
+
+ if (mStatus == LLBVHLoader::ST_NO_XLT_FILE)
+ {
+ llwarns << "NOTE: No translation table found." << llendl;
+ return;
+ }
+ else
+ {
+ if (mStatus != LLBVHLoader::ST_OK)
+ {
+ llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl;
+ return;
+ }
+ }
+
+ char error_text[128]; /* Flawfinder: ignore */
+ S32 error_line;
+ mStatus = loadBVHFile(buffer, error_text, error_line);
+ if (mStatus != LLBVHLoader::ST_OK)
+ {
+ llwarns << "ERROR: [line: " << getLineNumber() << "] " << mStatus << llendl;
+ return;
+ }
+
+ applyTranslations();
+ optimize();
+
+ mInitialized = TRUE;
+}
+
+LLBVHLoader::~LLBVHLoader()
+{
+ std::for_each(mJoints.begin(),mJoints.end(),DeletePointer());
+}
+
+//------------------------------------------------------------------------
+// LLBVHLoader::loadTranslationTable()
+//------------------------------------------------------------------------
+LLBVHLoader::Status LLBVHLoader::loadTranslationTable(const char *fileName)
+{
+ mLineNumber = 0;
+ mTranslations.clear();
+ mConstraints.clear();
+
+ //--------------------------------------------------------------------
+ // open file
+ //--------------------------------------------------------------------
+ char path[LL_MAX_PATH]; /* Flawfinder: ignore */
+
+ snprintf( path, sizeof(path), "%s",
+ gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName).c_str()); /* Flawfinder: ignore */
+
+
+ apr_file_t *fp = ll_apr_file_open(path, LL_APR_R);
+ if (!fp)
+ return ST_NO_XLT_FILE;
+
+ llinfos << "NOTE: Loading translation table: " << fileName << llendl;
+
+ //--------------------------------------------------------------------
+ // register file to be closed on function exit
+ //--------------------------------------------------------------------
+ FileCloser fileCloser(fp);
+
+ //--------------------------------------------------------------------
+ // load header
+ //--------------------------------------------------------------------
+ if ( ! getLine(fp) )
+ return ST_EOF;
+ if ( strncmp(mLine, "Translations 1.0", 16) )
+ return ST_NO_XLT_HEADER;
+
+ //--------------------------------------------------------------------
+ // load data one line at a time
+ //--------------------------------------------------------------------
+ BOOL loadingGlobals = FALSE;
+ Translation *trans = NULL;
+ while ( getLine(fp) )
+ {
+ //----------------------------------------------------------------
+ // check the 1st token on the line to determine if it's empty or a comment
+ //----------------------------------------------------------------
+ char token[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %127s", token) != 1 )
+ continue;
+
+ if (token[0] == '#')
+ continue;
+
+ //----------------------------------------------------------------
+ // check if a [jointName] or [GLOBALS] was specified.
+ //----------------------------------------------------------------
+ if (token[0] == '[')
+ {
+ char name[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " [%127[^]]", name) != 1 )
+ return ST_NO_XLT_NAME;
+
+ if (strcmp(name, "GLOBALS")==0)
+ {
+ loadingGlobals = TRUE;
+ continue;
+ }
+ else
+ {
+ loadingGlobals = FALSE;
+ Translation &newTrans = mTranslations[ name ];
+ trans = &newTrans;
+ continue;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // check for optional emote
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "emote")==0)
+ {
+ char emote_str[1024]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %1023s", emote_str) != 1 )
+ return ST_NO_XLT_EMOTE;
+
+ mEmoteName.assign( emote_str );
+// llinfos << "NOTE: Emote: " << mEmoteName.c_str() << llendl;
+ continue;
+ }
+
+
+ //----------------------------------------------------------------
+ // check for global priority setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "priority")==0)
+ {
+ S32 priority;
+ if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
+ return ST_NO_XLT_PRIORITY;
+
+ mPriority = priority;
+// llinfos << "NOTE: Priority: " << mPriority << llendl;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global loop setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "loop")==0)
+ {
+ char trueFalse[128]; /* Flawfinder: ignore */
+ trueFalse[0] = '\0';
+
+ F32 loop_in = 0.f;
+ F32 loop_out = 1.f;
+
+ if ( sscanf(mLine, " %*s = %f %f", &loop_in, &loop_out) == 2 )
+ {
+ mLoop = TRUE;
+ }
+ else if ( sscanf(mLine, " %*s = %127s", trueFalse) == 1 )
+ {
+ mLoop = (LLString::compareInsensitive(trueFalse, "true")==0);
+ }
+ else
+ {
+ return ST_NO_XLT_LOOP;
+ }
+
+ mLoopInPoint = loop_in * mDuration;
+ mLoopOutPoint = loop_out * mDuration;
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global easeIn setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "easein")==0)
+ {
+ F32 duration;
+ char type[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 )
+ return ST_NO_XLT_EASEIN;
+
+ mEaseIn = duration;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global easeOut setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "easeout")==0)
+ {
+ F32 duration;
+ char type[128];
+ if ( sscanf(mLine, " %*s = %f %127s", &duration, type) != 2 )
+ return ST_NO_XLT_EASEOUT;
+
+ mEaseOut = duration;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for global handMorph setting
+ //----------------------------------------------------------------
+ if (loadingGlobals && LLString::compareInsensitive(token, "hand")==0)
+ {
+ S32 handMorph;
+ if (sscanf(mLine, " %*s = %d", &handMorph) != 1)
+ return ST_NO_XLT_HAND;
+
+ mHand = handMorph;
+ continue;
+ }
+
+ if (loadingGlobals && LLString::compareInsensitive(token, "constraint")==0)
+ {
+ Constraint constraint;
+
+ // try reading optional target direction
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ],
+ &constraint.mTargetDir.mV[VX],
+ &constraint.mTargetDir.mV[VY],
+ &constraint.mTargetDir.mV[VZ]) != 16)
+ {
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ]) != 13)
+ {
+ return ST_NO_CONSTRAINT;
+ }
+ }
+ else
+ {
+ // normalize direction
+ if (!constraint.mTargetDir.isExactlyZero())
+ {
+ constraint.mTargetDir.normVec();
+ }
+
+ }
+
+ constraint.mConstraintType = CONSTRAINT_TYPE_POINT;
+ mConstraints.push_back(constraint);
+ continue;
+ }
+
+ if (loadingGlobals && LLString::compareInsensitive(token, "planar_constraint")==0)
+ {
+ Constraint constraint;
+
+ // try reading optional target direction
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ],
+ &constraint.mTargetDir.mV[VX],
+ &constraint.mTargetDir.mV[VY],
+ &constraint.mTargetDir.mV[VZ]) != 16)
+ {
+ if(sscanf(
+ mLine,
+ " %*s = %d %f %f %f %f %15s %f %f %f %15s %f %f %f",
+ &constraint.mChainLength,
+ &constraint.mEaseInStart,
+ &constraint.mEaseInStop,
+ &constraint.mEaseOutStart,
+ &constraint.mEaseOutStop,
+ constraint.mSourceJointName,
+ &constraint.mSourceOffset.mV[VX],
+ &constraint.mSourceOffset.mV[VY],
+ &constraint.mSourceOffset.mV[VZ],
+ constraint.mTargetJointName,
+ &constraint.mTargetOffset.mV[VX],
+ &constraint.mTargetOffset.mV[VY],
+ &constraint.mTargetOffset.mV[VZ]) != 13)
+ {
+ return ST_NO_CONSTRAINT;
+ }
+ }
+ else
+ {
+ // normalize direction
+ if (!constraint.mTargetDir.isExactlyZero())
+ {
+ constraint.mTargetDir.normVec();
+ }
+
+ }
+
+ constraint.mConstraintType = CONSTRAINT_TYPE_PLANE;
+ mConstraints.push_back(constraint);
+ continue;
+ }
+
+
+ //----------------------------------------------------------------
+ // at this point there must be a valid trans pointer
+ //----------------------------------------------------------------
+ if ( ! trans )
+ return ST_NO_XLT_NAME;
+
+ //----------------------------------------------------------------
+ // check for ignore flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "ignore")==0 )
+ {
+ char trueFalse[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", trueFalse) != 1 )
+ return ST_NO_XLT_IGNORE;
+
+ trans->mIgnore = (LLString::compareInsensitive(trueFalse, "true")==0);
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for relativepos flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "relativepos")==0 )
+ {
+ F32 x, y, z;
+ char relpos[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %f %f %f", &x, &y, &z) == 3 )
+ {
+ trans->mRelativePosition.setVec( x, y, z );
+ }
+ else if ( sscanf(mLine, " %*s = %127s", relpos) == 1 )
+ {
+ if ( LLString::compareInsensitive(relpos, "firstkey")==0 )
+ {
+ trans->mRelativePositionKey = TRUE;
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for relativerot flag
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "relativerot")==0 )
+ {
+ //F32 x, y, z;
+ char relpos[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", relpos) == 1 )
+ {
+ if ( LLString::compareInsensitive(relpos, "firstkey")==0 )
+ {
+ trans->mRelativeRotationKey = TRUE;
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+ }
+ else
+ {
+ return ST_NO_XLT_RELATIVE;
+ }
+
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for outname value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "outname")==0 )
+ {
+ char outName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", outName) != 1 )
+ return ST_NO_XLT_OUTNAME;
+
+ trans->mOutName = outName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for frame matrix value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "frame")==0 )
+ {
+ LLMatrix3 fm;
+ if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
+ &fm.mMatrix[0][0], &fm.mMatrix[0][1], &fm.mMatrix[0][2],
+ &fm.mMatrix[1][0], &fm.mMatrix[1][1], &fm.mMatrix[1][2],
+ &fm.mMatrix[2][0], &fm.mMatrix[2][1], &fm.mMatrix[2][2] ) != 9 )
+ return ST_NO_XLT_MATRIX;
+
+ trans->mFrameMatrix = fm;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for offset matrix value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "offset")==0 )
+ {
+ LLMatrix3 om;
+ if ( sscanf(mLine, " %*s = %f %f %f, %f %f %f, %f %f %f",
+ &om.mMatrix[0][0], &om.mMatrix[0][1], &om.mMatrix[0][2],
+ &om.mMatrix[1][0], &om.mMatrix[1][1], &om.mMatrix[1][2],
+ &om.mMatrix[2][0], &om.mMatrix[2][1], &om.mMatrix[2][2] ) != 9 )
+ return ST_NO_XLT_MATRIX;
+
+ trans->mOffsetMatrix = om;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for mergeparent value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "mergeparent")==0 )
+ {
+ char mergeParentName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", mergeParentName) != 1 )
+ return ST_NO_XLT_MERGEPARENT;
+
+ trans->mMergeParentName = mergeParentName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for mergechild value
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "mergechild")==0 )
+ {
+ char mergeChildName[128]; /* Flawfinder: ignore */
+ if ( sscanf(mLine, " %*s = %127s", mergeChildName) != 1 )
+ return ST_NO_XLT_MERGECHILD;
+
+ trans->mMergeChildName = mergeChildName;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // check for per-joint priority
+ //----------------------------------------------------------------
+ if ( LLString::compareInsensitive(token, "priority")==0 )
+ {
+ S32 priority;
+ if ( sscanf(mLine, " %*s = %d", &priority) != 1 )
+ return ST_NO_XLT_PRIORITY;
+
+ trans->mPriorityModifier = priority;
+ continue;
+ }
+
+ }
+ return ST_OK;
+}
+
+
+//------------------------------------------------------------------------
+// LLBVHLoader::loadBVHFile()
+//------------------------------------------------------------------------
+LLBVHLoader::Status LLBVHLoader::loadBVHFile(const char *buffer, char* error_text, S32 &err_line)
+{
+ std::string line;
+
+ err_line = 0;
+ error_text[127] = '\0';
+
+ std::string str(buffer);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\r\n");
+ tokenizer tokens(str, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ mLineNumber = 0;
+ mJoints.clear();
+
+ std::vector<S32> parent_joints;
+
+ //--------------------------------------------------------------------
+ // consume hierarchy
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ return ST_EOF;
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "HIERARCHY") )
+ {
+// llinfos << line << llendl;
+ return ST_NO_HIER;
+ }
+
+ //--------------------------------------------------------------------
+ // consume joints
+ //--------------------------------------------------------------------
+ while (TRUE)
+ {
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ return ST_EOF;
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // consume }
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "}") )
+ {
+ if (parent_joints.size() > 0)
+ {
+ parent_joints.pop_back();
+ }
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // if MOTION, break out
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "MOTION") )
+ break;
+
+ //----------------------------------------------------------------
+ // it must be either ROOT or JOINT or EndSite
+ //----------------------------------------------------------------
+ if ( strstr(line.c_str(), "ROOT") )
+ {
+ }
+ else if ( strstr(line.c_str(), "JOINT") )
+ {
+ }
+ else if ( strstr(line.c_str(), "End Site") )
+ {
+ iter++; // {
+ iter++; // OFFSET
+ S32 depth = 0;
+ for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
+ {
+ Joint *joint = mJoints[parent_joints[j]];
+ if (depth > joint->mChildTreeMaxDepth)
+ {
+ joint->mChildTreeMaxDepth = depth;
+ }
+ depth++;
+ }
+ continue;
+ }
+ else
+ {
+ strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
+ return ST_NO_JOINT;
+ }
+
+ //----------------------------------------------------------------
+ // get the joint name
+ //----------------------------------------------------------------
+ char jointName[80]; /* Flawfinder: ignore */
+ if ( sscanf(line.c_str(), "%*s %79s", jointName) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /* Flawfinder: ignore */
+ return ST_NO_NAME;
+ }
+
+ //----------------------------------------------------------------
+ // add a set of keyframes for this joint
+ //----------------------------------------------------------------
+ mJoints.push_back( new Joint( jointName ) );
+ Joint *joint = mJoints.back();
+
+ S32 depth = 1;
+ for (S32 j = (S32)parent_joints.size() - 1; j >= 0; j--)
+ {
+ Joint *pjoint = mJoints[parent_joints[j]];
+ if (depth > pjoint->mChildTreeMaxDepth)
+ {
+ pjoint->mChildTreeMaxDepth = depth;
+ }
+ depth++;
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be {
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "{") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_OFFSET;
+ }
+ else
+ {
+ parent_joints.push_back((S32)mJoints.size() - 1);
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be OFFSET
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "OFFSET") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_OFFSET;
+ }
+
+ //----------------------------------------------------------------
+ // get next line
+ //----------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ //----------------------------------------------------------------
+ // it must be CHANNELS
+ //----------------------------------------------------------------
+ if ( !strstr(line.c_str(), "CHANNELS") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_CHANNELS;
+ }
+
+ //----------------------------------------------------------------
+ // get rotation order
+ //----------------------------------------------------------------
+ const char *p = line.c_str();
+ for (S32 i=0; i<3; i++)
+ {
+ p = strstr(p, "rotation");
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROTATION;
+ }
+
+ const char axis = *(p - 1);
+ if ((axis != 'X') && (axis != 'Y') && (axis != 'Z'))
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_AXIS;
+ }
+
+ joint->mOrder[i] = axis;
+
+ p++;
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // consume motion
+ //--------------------------------------------------------------------
+ if ( !strstr(line.c_str(), "MOTION") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_MOTION;
+ }
+
+ //--------------------------------------------------------------------
+ // get number of frames
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "Frames:") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAMES;
+ }
+
+ if ( sscanf(line.c_str(), "Frames: %d", &mNumFrames) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAMES;
+ }
+
+ //--------------------------------------------------------------------
+ // get frame time
+ //--------------------------------------------------------------------
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ if ( !strstr(line.c_str(), "Frame Time:") )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAME_TIME;
+ }
+
+ if ( sscanf(line.c_str(), "Frame Time: %f", &mFrameTime) != 1 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_FRAME_TIME;
+ }
+
+ mDuration = (F32)mNumFrames * mFrameTime;
+ if (!mLoop)
+ {
+ mLoopOutPoint = mDuration;
+ }
+
+ //--------------------------------------------------------------------
+ // load frames
+ //--------------------------------------------------------------------
+ for (S32 i=0; i<mNumFrames; i++)
+ {
+ // get next line
+ if (iter == tokens.end())
+ {
+ return ST_EOF;
+ }
+ line = (*(iter++));
+ err_line++;
+
+ // read and store values
+ const char *p = line.c_str();
+ for (U32 j=0; j<mJoints.size(); j++)
+ {
+ Joint *joint = mJoints[j];
+ joint->mKeys.push_back( Key() );
+ Key &key = joint->mKeys.back();
+
+ // get 3 pos values for root joint only
+ if (j==0)
+ {
+ if ( sscanf(p, "%f %f %f", key.mPos, key.mPos+1, key.mPos+2) != 3 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_POS;
+ }
+ }
+
+ // skip to next 3 values in the line
+ p = find_next_whitespace(p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+ p = find_next_whitespace(++p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+ p = find_next_whitespace(++p);
+ if (!p)
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+
+ // get 3 rot values for joint
+ F32 rot[3];
+ if ( sscanf(p, " %f %f %f", rot, rot+1, rot+2) != 3 )
+ {
+ strncpy(error_text, line.c_str(), 127); /*Flawfinder: ignore*/
+ return ST_NO_ROT;
+ }
+
+ p++;
+
+ key.mRot[ joint->mOrder[0]-'X' ] = rot[0];
+ key.mRot[ joint->mOrder[1]-'X' ] = rot[1];
+ key.mRot[ joint->mOrder[2]-'X' ] = rot[2];
+ }
+ }
+
+ return ST_OK;
+}
+
+
+//------------------------------------------------------------------------
+// LLBVHLoader::applyTranslation()
+//------------------------------------------------------------------------
+void LLBVHLoader::applyTranslations()
+{
+ JointVector::iterator ji;
+ for (ji = mJoints.begin(); ji != mJoints.end(); ++ji )
+ {
+ Joint *joint = *ji;
+ //----------------------------------------------------------------
+ // Look for a translation for this joint.
+ // If none, skip to next joint
+ //----------------------------------------------------------------
+ TranslationMap::iterator ti = mTranslations.find( joint->mName );
+ if ( ti == mTranslations.end() )
+ {
+ continue;
+ }
+
+ Translation &trans = ti->second;
+
+ //----------------------------------------------------------------
+ // Set the ignore flag if necessary
+ //----------------------------------------------------------------
+ if ( trans.mIgnore )
+ {
+ //llinfos << "NOTE: Ignoring " << joint->mName.c_str() << llendl;
+ joint->mIgnore = TRUE;
+ continue;
+ }
+
+ //----------------------------------------------------------------
+ // Set the output name
+ //----------------------------------------------------------------
+ if ( ! trans.mOutName.empty() )
+ {
+ //llinfos << "NOTE: Changing " << joint->mName.c_str() << " to " << trans.mOutName.c_str() << llendl;
+ joint->mOutName = trans.mOutName;
+ }
+
+ //----------------------------------------------------------------
+ // Set the ignorepos flag if necessary
+ //----------------------------------------------------------------
+ if ( joint->mOutName == std::string("mPelvis") )
+ {
+ joint->mIgnorePositions = FALSE;
+ }
+
+ //----------------------------------------------------------------
+ // Set the relativepos flags if necessary
+ //----------------------------------------------------------------
+ if ( trans.mRelativePositionKey )
+ {
+// llinfos << "NOTE: Removing 1st position offset from all keys for " << joint->mOutName.c_str() << llendl;
+ joint->mRelativePositionKey = TRUE;
+ }
+
+ if ( trans.mRelativeRotationKey )
+ {
+// llinfos << "NOTE: Removing 1st rotation from all keys for " << joint->mOutName.c_str() << llendl;
+ joint->mRelativeRotationKey = TRUE;
+ }
+
+ if ( trans.mRelativePosition.magVec() > 0.0f )
+ {
+ joint->mRelativePosition = trans.mRelativePosition;
+// llinfos << "NOTE: Removing " <<
+// joint->mRelativePosition.mV[0] << " " <<
+// joint->mRelativePosition.mV[1] << " " <<
+// joint->mRelativePosition.mV[2] <<
+// " from all position keys in " <<
+// joint->mOutName.c_str() << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Set change of coordinate frame
+ //----------------------------------------------------------------
+ joint->mFrameMatrix = trans.mFrameMatrix;
+ joint->mOffsetMatrix = trans.mOffsetMatrix;
+
+ //----------------------------------------------------------------
+ // Set mergeparent name
+ //----------------------------------------------------------------
+ if ( ! trans.mMergeParentName.empty() )
+ {
+// llinfos << "NOTE: Merging " << joint->mOutName.c_str() <<
+// " with parent " <<
+// trans.mMergeParentName.c_str() << llendl;
+ joint->mMergeParentName = trans.mMergeParentName;
+ }
+
+ //----------------------------------------------------------------
+ // Set mergechild name
+ //----------------------------------------------------------------
+ if ( ! trans.mMergeChildName.empty() )
+ {
+// llinfos << "NOTE: Merging " << joint->mName.c_str() <<
+// " with child " << trans.mMergeChildName.c_str() << llendl;
+ joint->mMergeChildName = trans.mMergeChildName;
+ }
+
+ //----------------------------------------------------------------
+ // Set joint priority
+ //----------------------------------------------------------------
+ joint->mPriority = mPriority + trans.mPriorityModifier;
+
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLBVHLoader::optimize()
+//-----------------------------------------------------------------------------
+void LLBVHLoader::optimize()
+{
+ //RN: assumes motion blend, which is the default now
+ if (!mLoop && mEaseIn + mEaseOut > mDuration && mDuration != 0.f)
+ {
+ F32 factor = mDuration / (mEaseIn + mEaseOut);
+ mEaseIn *= factor;
+ mEaseOut *= factor;
+ }
+
+ JointVector::iterator ji;
+ for (ji = mJoints.begin(); ji != mJoints.end(); ++ji)
+ {
+ Joint *joint = *ji;
+ BOOL pos_changed = FALSE;
+ BOOL rot_changed = FALSE;
+
+ if ( ! joint->mIgnore )
+ {
+ joint->mNumPosKeys = 0;
+ joint->mNumRotKeys = 0;
+ LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
+
+ KeyVector::iterator first_key = joint->mKeys.begin();
+
+ // no keys?
+ if (first_key == joint->mKeys.end())
+ {
+ joint->mIgnore = TRUE;
+ continue;
+ }
+
+ LLVector3 first_frame_pos(first_key->mPos);
+ LLQuaternion first_frame_rot = mayaQ( first_key->mRot[0], first_key->mRot[1], first_key->mRot[2], order);
+
+ // skip first key
+ KeyVector::iterator ki = joint->mKeys.begin();
+ if (joint->mKeys.size() == 1)
+ {
+ //FIXME: use single frame to move pelvis
+ // if only one keyframe force output for this joint
+ rot_changed = TRUE;
+ }
+ else
+ {
+ // if more than one keyframe, use first frame as reference and skip to second
+ first_key->mIgnorePos = TRUE;
+ first_key->mIgnoreRot = TRUE;
+ ++ki;
+ }
+
+ KeyVector::iterator ki_prev = ki;
+ KeyVector::iterator ki_last_good_pos = ki;
+ KeyVector::iterator ki_last_good_rot = ki;
+ S32 numPosFramesConsidered = 2;
+ S32 numRotFramesConsidered = 2;
+
+ F32 rot_threshold = ROTATION_KEYFRAME_THRESHOLD / llmax((F32)joint->mChildTreeMaxDepth * 0.33f, 1.f);
+
+ for (; ki != joint->mKeys.end(); ++ki)
+ {
+ if (ki_prev == ki_last_good_pos)
+ {
+ joint->mNumPosKeys++;
+ if (dist_vec(LLVector3(ki_prev->mPos), first_frame_pos) > POSITION_MOTION_THRESHOLD)
+ {
+ pos_changed = TRUE;
+ }
+ }
+ else
+ {
+ //check position for noticeable effect
+ LLVector3 test_pos(ki_prev->mPos);
+ LLVector3 last_good_pos(ki_last_good_pos->mPos);
+ LLVector3 current_pos(ki->mPos);
+ LLVector3 interp_pos = lerp(current_pos, last_good_pos, 1.f / (F32)numPosFramesConsidered);
+
+ if (dist_vec(current_pos, first_frame_pos) > POSITION_MOTION_THRESHOLD)
+ {
+ pos_changed = TRUE;
+ }
+
+ if (dist_vec(interp_pos, test_pos) < POSITION_KEYFRAME_THRESHOLD)
+ {
+ ki_prev->mIgnorePos = TRUE;
+ numPosFramesConsidered++;
+ }
+ else
+ {
+ numPosFramesConsidered = 2;
+ ki_last_good_pos = ki_prev;
+ joint->mNumPosKeys++;
+ }
+ }
+
+ if (ki_prev == ki_last_good_rot)
+ {
+ joint->mNumRotKeys++;
+ LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
+ F32 x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
+ F32 y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
+ F32 rot_test = x_delta + y_delta;
+
+ if (rot_test > ROTATION_MOTION_THRESHOLD)
+ {
+ rot_changed = TRUE;
+ }
+ }
+ else
+ {
+ //check rotation for noticeable effect
+ LLQuaternion test_rot = mayaQ( ki_prev->mRot[0], ki_prev->mRot[1], ki_prev->mRot[2], order);
+ LLQuaternion last_good_rot = mayaQ( ki_last_good_rot->mRot[0], ki_last_good_rot->mRot[1], ki_last_good_rot->mRot[2], order);
+ LLQuaternion current_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+ LLQuaternion interp_rot = lerp(1.f / (F32)numRotFramesConsidered, current_rot, last_good_rot);
+
+ F32 x_delta;
+ F32 y_delta;
+ F32 rot_test;
+
+ x_delta = dist_vec(LLVector3::x_axis * first_frame_rot, LLVector3::x_axis * test_rot);
+ y_delta = dist_vec(LLVector3::y_axis * first_frame_rot, LLVector3::y_axis * test_rot);
+ rot_test = x_delta + y_delta;
+
+ if (rot_test > ROTATION_MOTION_THRESHOLD)
+ {
+ rot_changed = TRUE;
+ }
+
+ x_delta = dist_vec(LLVector3::x_axis * interp_rot, LLVector3::x_axis * test_rot);
+ y_delta = dist_vec(LLVector3::y_axis * interp_rot, LLVector3::y_axis * test_rot);
+ rot_test = x_delta + y_delta;
+
+ if (rot_test < rot_threshold)
+ {
+ ki_prev->mIgnoreRot = TRUE;
+ numRotFramesConsidered++;
+ }
+ else
+ {
+ numRotFramesConsidered = 2;
+ ki_last_good_rot = ki_prev;
+ joint->mNumRotKeys++;
+ }
+ }
+
+ ki_prev = ki;
+ }
+ }
+
+ // don't output joints with no motion
+ if (!(pos_changed || rot_changed))
+ {
+ //llinfos << "Ignoring joint " << joint->mName << llendl;
+ joint->mIgnore = TRUE;
+ }
+ }
+}
+
+void LLBVHLoader::reset()
+{
+ mLineNumber = 0;
+ mNumFrames = 0;
+ mFrameTime = 0.0f;
+ mDuration = 0.0f;
+
+ mPriority = 2;
+ mLoop = FALSE;
+ mLoopInPoint = 0.f;
+ mLoopOutPoint = 0.f;
+ mEaseIn = 0.3f;
+ mEaseOut = 0.3f;
+ mHand = 1;
+ mInitialized = FALSE;
+
+ mEmoteName = "";
+}
+
+//------------------------------------------------------------------------
+// LLBVHLoader::getLine()
+//------------------------------------------------------------------------
+BOOL LLBVHLoader::getLine(apr_file_t* fp)
+{
+ if (apr_file_eof(fp) == APR_EOF)
+ {
+ return FALSE;
+ }
+ if ( apr_file_gets(mLine, BVH_PARSER_LINE_SIZE, fp) == APR_SUCCESS)
+ {
+ mLineNumber++;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// returns required size of output buffer
+U32 LLBVHLoader::getOutputSize()
+{
+ LLDataPackerBinaryBuffer dp;
+ serialize(dp);
+
+ return dp.getCurrentSize();
+}
+
+// writes contents to datapacker
+BOOL LLBVHLoader::serialize(LLDataPacker& dp)
+{
+ JointVector::iterator ji;
+ KeyVector::iterator ki;
+ F32 time;
+
+ // count number of non-ignored joints
+ S32 numJoints = 0;
+ for (ji=mJoints.begin(); ji!=mJoints.end(); ++ji)
+ {
+ Joint *joint = *ji;
+ if ( ! joint->mIgnore )
+ numJoints++;
+ }
+
+ // print header
+ dp.packU16(KEYFRAME_MOTION_VERSION, "version");
+ dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
+ dp.packS32(mPriority, "base_priority");
+ dp.packF32(mDuration, "duration");
+ dp.packString(mEmoteName.c_str(), "emote_name");
+ dp.packF32(mLoopInPoint, "loop_in_point");
+ dp.packF32(mLoopOutPoint, "loop_out_point");
+ dp.packS32(mLoop, "loop");
+ dp.packF32(mEaseIn, "ease_in_duration");
+ dp.packF32(mEaseOut, "ease_out_duration");
+ dp.packU32(mHand, "hand_pose");
+ dp.packU32(numJoints, "num_joints");
+
+ for ( ji = mJoints.begin();
+ ji != mJoints.end();
+ ++ji )
+ {
+ Joint *joint = *ji;
+ // if ignored, skip it
+ if ( joint->mIgnore )
+ continue;
+
+ LLQuaternion first_frame_rot;
+ LLQuaternion fixup_rot;
+
+ dp.packString(joint->mOutName.c_str(), "joint_name");
+ dp.packS32(joint->mPriority, "joint_priority");
+
+ // compute coordinate frame rotation
+ LLQuaternion frameRot( joint->mFrameMatrix );
+ LLQuaternion frameRotInv = ~frameRot;
+
+ LLQuaternion offsetRot( joint->mOffsetMatrix );
+
+ // find mergechild and mergeparent joints, if specified
+ LLQuaternion mergeParentRot;
+ LLQuaternion mergeChildRot;
+ Joint *mergeParent = NULL;
+ Joint *mergeChild = NULL;
+
+ JointVector::iterator mji;
+ for (mji=mJoints.begin(); mji!=mJoints.end(); ++mji)
+ {
+ Joint *mjoint = *mji;
+ if ( !joint->mMergeParentName.empty() && (mjoint->mName == joint->mMergeParentName) )
+ {
+ mergeParent = *mji;
+ }
+ if ( !joint->mMergeChildName.empty() && (mjoint->mName == joint->mMergeChildName) )
+ {
+ mergeChild = *mji;
+ }
+ }
+
+ dp.packS32(joint->mNumRotKeys, "num_rot_keys");
+
+ LLQuaternion::Order order = bvhStringToOrder( joint->mOrder );
+ S32 outcount = 0;
+ S32 frame = 1;
+ for ( ki = joint->mKeys.begin();
+ ki != joint->mKeys.end();
+ ++ki )
+ {
+ if ((frame == 1) && joint->mRelativeRotationKey)
+ {
+ first_frame_rot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+
+ fixup_rot.shortestArc(LLVector3::z_axis * first_frame_rot * frameRot, LLVector3::z_axis);
+ }
+
+ if (ki->mIgnoreRot)
+ {
+ frame++;
+ continue;
+ }
+
+ time = (F32)frame * mFrameTime;
+
+ if (mergeParent)
+ {
+ mergeParentRot = mayaQ( mergeParent->mKeys[frame-1].mRot[0],
+ mergeParent->mKeys[frame-1].mRot[1],
+ mergeParent->mKeys[frame-1].mRot[2],
+ bvhStringToOrder(mergeParent->mOrder) );
+ LLQuaternion parentFrameRot( mergeParent->mFrameMatrix );
+ LLQuaternion parentOffsetRot( mergeParent->mOffsetMatrix );
+ mergeParentRot = ~parentFrameRot * mergeParentRot * parentFrameRot * parentOffsetRot;
+ }
+ else
+ {
+ mergeParentRot.loadIdentity();
+ }
+
+ if (mergeChild)
+ {
+ mergeChildRot = mayaQ( mergeChild->mKeys[frame-1].mRot[0],
+ mergeChild->mKeys[frame-1].mRot[1],
+ mergeChild->mKeys[frame-1].mRot[2],
+ bvhStringToOrder(mergeChild->mOrder) );
+ LLQuaternion childFrameRot( mergeChild->mFrameMatrix );
+ LLQuaternion childOffsetRot( mergeChild->mOffsetMatrix );
+ mergeChildRot = ~childFrameRot * mergeChildRot * childFrameRot * childOffsetRot;
+
+ }
+ else
+ {
+ mergeChildRot.loadIdentity();
+ }
+
+ LLQuaternion inRot = mayaQ( ki->mRot[0], ki->mRot[1], ki->mRot[2], order);
+
+ LLQuaternion outRot = frameRotInv* mergeChildRot * inRot * mergeParentRot * ~first_frame_rot * frameRot * offsetRot;
+
+ U16 time_short = F32_to_U16(time, 0.f, mDuration);
+ dp.packU16(time_short, "time");
+ U16 x, y, z;
+ LLVector3 rot_vec = outRot.packToVector3();
+ rot_vec.quantize16(-1.f, 1.f, -1.f, 1.f);
+ x = F32_to_U16(rot_vec.mV[VX], -1.f, 1.f);
+ y = F32_to_U16(rot_vec.mV[VY], -1.f, 1.f);
+ z = F32_to_U16(rot_vec.mV[VZ], -1.f, 1.f);
+ dp.packU16(x, "rot_angle_x");
+ dp.packU16(y, "rot_angle_y");
+ dp.packU16(z, "rot_angle_z");
+ outcount++;
+ frame++;
+ }
+
+ // output position keys (only for 1st joint)
+ if ( ji == mJoints.begin() && !joint->mIgnorePositions )
+ {
+ dp.packS32(joint->mNumPosKeys, "num_pos_keys");
+
+ LLVector3 relPos = joint->mRelativePosition;
+ LLVector3 relKey;
+
+ frame = 1;
+ for ( ki = joint->mKeys.begin();
+ ki != joint->mKeys.end();
+ ++ki )
+ {
+ if ((frame == 1) && joint->mRelativePositionKey)
+ {
+ relKey.setVec(ki->mPos);
+ }
+
+ if (ki->mIgnorePos)
+ {
+ frame++;
+ continue;
+ }
+
+ time = (F32)frame * mFrameTime;
+
+ LLVector3 inPos = (LLVector3(ki->mPos) - relKey) * ~first_frame_rot;// * fixup_rot;
+ LLVector3 outPos = inPos * frameRot * offsetRot;
+
+ outPos *= INCHES_TO_METERS;
+
+ outPos -= relPos;
+ outPos.clamp(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+
+ U16 time_short = F32_to_U16(time, 0.f, mDuration);
+ dp.packU16(time_short, "time");
+
+ U16 x, y, z;
+ outPos.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ x = F32_to_U16(outPos.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ y = F32_to_U16(outPos.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ z = F32_to_U16(outPos.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ dp.packU16(x, "pos_x");
+ dp.packU16(y, "pos_y");
+ dp.packU16(z, "pos_z");
+
+ frame++;
+ }
+ }
+ else
+ {
+ dp.packS32(0, "num_pos_keys");
+ }
+ }
+
+ S32 num_constraints = (S32)mConstraints.size();
+ dp.packS32(num_constraints, "num_constraints");
+
+ for (ConstraintVector::iterator constraint_it = mConstraints.begin();
+ constraint_it != mConstraints.end();
+ constraint_it++)
+ {
+ U8 byte = constraint_it->mChainLength;
+ dp.packU8(byte, "chain_lenght");
+
+ byte = constraint_it->mConstraintType;
+ dp.packU8(byte, "constraint_type");
+ dp.packBinaryDataFixed((U8*)constraint_it->mSourceJointName, 16, "source_volume");
+ dp.packVector3(constraint_it->mSourceOffset, "source_offset");
+ dp.packBinaryDataFixed((U8*)constraint_it->mTargetJointName, 16, "target_volume");
+ dp.packVector3(constraint_it->mTargetOffset, "target_offset");
+ dp.packVector3(constraint_it->mTargetDir, "target_dir");
+ dp.packF32(constraint_it->mEaseInStart, "ease_in_start");
+ dp.packF32(constraint_it->mEaseInStop, "ease_in_stop");
+ dp.packF32(constraint_it->mEaseOutStart, "ease_out_start");
+ dp.packF32(constraint_it->mEaseOutStop, "ease_out_stop");
+ }
+
+ return TRUE;
+}
diff --git a/indra/llcharacter/llbvhloader.h b/indra/llcharacter/llbvhloader.h
new file mode 100644
index 0000000000..7e00e1d5f4
--- /dev/null
+++ b/indra/llcharacter/llbvhloader.h
@@ -0,0 +1,282 @@
+/**
+ * @file llbvhloader.h
+ * @brief Translates a BVH files to LindenLabAnimation format.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBVHLOADER_H
+#define LL_LLBVHLOADER_H
+
+#include <string>
+#include <vector>
+#include <map>
+#include <stdtypes.h>
+#include <stdio.h>
+#include "v3math.h"
+#include "m3math.h"
+#include "llmath.h"
+#include "llapr.h"
+
+const S32 BVH_PARSER_LINE_SIZE = 2048;
+const F32 MAX_ANIM_DURATION = 30.f;
+class LLDataPacker;
+
+//------------------------------------------------------------------------
+// FileCloser
+//------------------------------------------------------------------------
+class FileCloser
+{
+public:
+ FileCloser( apr_file_t *file )
+ {
+ mFile = file;
+ }
+
+ ~FileCloser()
+ {
+ apr_file_close(mFile);
+ }
+protected:
+ apr_file_t* mFile;
+};
+
+
+//------------------------------------------------------------------------
+// Key
+//------------------------------------------------------------------------
+struct Key
+{
+ Key()
+ {
+ mPos[0] = mPos[1] = mPos[2] = 0.0f;
+ mRot[0] = mRot[1] = mRot[2] = 0.0f;
+ mIgnorePos = false;
+ mIgnoreRot = false;
+ }
+
+ F32 mPos[3];
+ F32 mRot[3];
+ BOOL mIgnorePos;
+ BOOL mIgnoreRot;
+};
+
+
+//------------------------------------------------------------------------
+// KeyVector
+//------------------------------------------------------------------------
+typedef std::vector<Key> KeyVector;
+
+//------------------------------------------------------------------------
+// Joint
+//------------------------------------------------------------------------
+struct Joint
+{
+ Joint(const char *name)
+ {
+ mName = name;
+ mIgnore = FALSE;
+ mIgnorePositions = FALSE;
+ mRelativePositionKey = FALSE;
+ mRelativeRotationKey = FALSE;
+ mOutName = name;
+ mOrder[0] = 'X';
+ mOrder[1] = 'Y';
+ mOrder[2] = 'Z';
+ mOrder[3] = 0;
+ mNumPosKeys = 0;
+ mNumRotKeys = 0;
+ mChildTreeMaxDepth = 0;
+ mPriority = 0;
+ }
+
+ // Include aligned members first
+ LLMatrix3 mFrameMatrix;
+ LLMatrix3 mOffsetMatrix;
+ LLVector3 mRelativePosition;
+ //
+ std::string mName;
+ BOOL mIgnore;
+ BOOL mIgnorePositions;
+ BOOL mRelativePositionKey;
+ BOOL mRelativeRotationKey;
+ std::string mOutName;
+ std::string mMergeParentName;
+ std::string mMergeChildName;
+ char mOrder[4]; /* Flawfinder: ignore */
+ KeyVector mKeys;
+ S32 mNumPosKeys;
+ S32 mNumRotKeys;
+ S32 mChildTreeMaxDepth;
+ S32 mPriority;
+};
+
+
+typedef enum e_constraint_type
+{
+ CONSTRAINT_TYPE_POINT,
+ CONSTRAINT_TYPE_PLANE
+} EConstraintType;
+
+struct Constraint
+{
+ char mSourceJointName[16]; /* Flawfinder: ignore */
+ char mTargetJointName[16]; /* Flawfinder: ignore */
+ S32 mChainLength;
+ LLVector3 mSourceOffset;
+ LLVector3 mTargetOffset;
+ LLVector3 mTargetDir;
+ F32 mEaseInStart;
+ F32 mEaseInStop;
+ F32 mEaseOutStart;
+ F32 mEaseOutStop;
+ EConstraintType mConstraintType;
+};
+
+//------------------------------------------------------------------------
+// JointVector
+//------------------------------------------------------------------------
+typedef std::vector<Joint*> JointVector;
+
+//------------------------------------------------------------------------
+// ConstraintVector
+//------------------------------------------------------------------------
+typedef std::vector<Constraint> ConstraintVector;
+
+//------------------------------------------------------------------------
+// Translation
+//------------------------------------------------------------------------
+class Translation
+{
+public:
+ Translation()
+ {
+ mIgnore = FALSE;
+ mRelativePositionKey = FALSE;
+ mRelativeRotationKey = FALSE;
+ mPriorityModifier = 0;
+ }
+
+ std::string mOutName;
+ BOOL mIgnore;
+ BOOL mIgnorePositions;
+ BOOL mRelativePositionKey;
+ BOOL mRelativeRotationKey;
+ LLMatrix3 mFrameMatrix;
+ LLMatrix3 mOffsetMatrix;
+ LLVector3 mRelativePosition;
+ std::string mMergeParentName;
+ std::string mMergeChildName;
+ S32 mPriorityModifier;
+};
+
+//------------------------------------------------------------------------
+// TranslationMap
+//------------------------------------------------------------------------
+typedef std::map<std::string, Translation> TranslationMap;
+
+class LLBVHLoader
+{
+ friend class LLKeyframeMotion;
+public:
+ // Constructor
+ LLBVHLoader(const char* buffer);
+ ~LLBVHLoader();
+
+ // Status Codes
+ typedef char *Status;
+ static char *ST_OK;
+ static char *ST_EOF;
+ static char *ST_NO_CONSTRAINT;
+ static char *ST_NO_FILE;
+ static char *ST_NO_HIER;
+ static char *ST_NO_JOINT;
+ static char *ST_NO_NAME;
+ static char *ST_NO_OFFSET;
+ static char *ST_NO_CHANNELS;
+ static char *ST_NO_ROTATION;
+ static char *ST_NO_AXIS;
+ static char *ST_NO_MOTION;
+ static char *ST_NO_FRAMES;
+ static char *ST_NO_FRAME_TIME;
+ static char *ST_NO_POS;
+ static char *ST_NO_ROT;
+ static char *ST_NO_XLT_FILE;
+ static char *ST_NO_XLT_HEADER;
+ static char *ST_NO_XLT_NAME;
+ static char *ST_NO_XLT_IGNORE;
+ static char *ST_NO_XLT_RELATIVE;
+ static char *ST_NO_XLT_OUTNAME;
+ static char *ST_NO_XLT_MATRIX;
+ static char *ST_NO_XLT_MERGECHILD;
+ static char *ST_NO_XLT_MERGEPARENT;
+ static char *ST_NO_XLT_PRIORITY;
+ static char *ST_NO_XLT_LOOP;
+ static char *ST_NO_XLT_EASEIN;
+ static char *ST_NO_XLT_EASEOUT;
+ static char *ST_NO_XLT_HAND;
+ static char *ST_NO_XLT_EMOTE;
+
+ // Loads the specified translation table.
+ Status loadTranslationTable(const char *fileName);
+
+ // Load the specified BVH file.
+ // Returns status code.
+ Status loadBVHFile(const char *buffer, char *error_text, S32 &error_line);
+
+ // Applies translations to BVH data loaded.
+ void applyTranslations();
+
+ // Returns the number of lines scanned.
+ // Useful for error reporting.
+ S32 getLineNumber() { return mLineNumber; }
+
+ // returns required size of output buffer
+ U32 getOutputSize();
+
+ // writes contents to datapacker
+ BOOL serialize(LLDataPacker& dp);
+
+ // flags redundant keyframe data
+ void optimize();
+
+ void reset();
+
+ F32 getDuration() { return mDuration; }
+
+ BOOL isInitialized() { return mInitialized; }
+
+ Status getStatus() { return mStatus; }
+
+protected:
+ // Consumes one line of input from file.
+ BOOL getLine(apr_file_t *fp);
+
+ // parser state
+ char mLine[BVH_PARSER_LINE_SIZE]; /* Flawfinder: ignore */
+ S32 mLineNumber;
+
+ // parsed values
+ S32 mNumFrames;
+ F32 mFrameTime;
+ JointVector mJoints;
+ ConstraintVector mConstraints;
+ TranslationMap mTranslations;
+
+ S32 mPriority;
+ BOOL mLoop;
+ F32 mLoopInPoint;
+ F32 mLoopOutPoint;
+ F32 mEaseIn;
+ F32 mEaseOut;
+ S32 mHand;
+ std::string mEmoteName;
+
+ BOOL mInitialized;
+ Status mStatus;
+ // computed values
+ F32 mDuration;
+};
+
+#endif // LL_LLBVHLOADER_H
diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp
new file mode 100644
index 0000000000..ecca9a2526
--- /dev/null
+++ b/indra/llcharacter/llcharacter.cpp
@@ -0,0 +1,472 @@
+/**
+ * @file llcharacter.cpp
+ * @brief Implementation of LLCharacter class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+
+#include "linden_common.h"
+
+#include "llcharacter.h"
+#include "llstring.h"
+
+#define SKEL_HEADER "Linden Skeleton 1.0"
+
+LLStringTable LLCharacter::sVisualParamNames(1024);
+
+// helper functions
+BOOL larger_screen_area( LLCharacter* data_new, LLCharacter* data_tested )
+{
+ return data_new->getPixelArea() > data_tested->getPixelArea();
+}
+
+LLLinkedList< LLCharacter > LLCharacter::sInstances( larger_screen_area );
+
+
+//-----------------------------------------------------------------------------
+// LLCharacter()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLCharacter::LLCharacter()
+ :
+ mPreferredPelvisHeight( 0.f ),
+ mSex( SEX_FEMALE ),
+ mAppearanceSerialNum( 0 ),
+ mSkeletonSerialNum( 0 )
+{
+ mMotionController.setCharacter( this );
+ sInstances.addData(this);
+ mPauseRequest = new LLPauseRequestHandle();
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLCharacter()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLCharacter::~LLCharacter()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ delete param;
+ }
+ sInstances.removeData(this);
+}
+
+
+//-----------------------------------------------------------------------------
+// getJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLCharacter::getJoint( const std::string &name )
+{
+ LLJoint *joint = NULL;
+
+ LLJoint *root = getRootJoint();
+ if (root)
+ {
+ joint = root->findJoint(name);
+ }
+
+ if (!joint)
+ {
+ llwarns << "Failed to find joint." << llendl;
+ }
+ return joint;
+}
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::addMotion( const LLUUID& id, LLMotionConstructor create )
+{
+ return mMotionController.addMotion(id, create);
+}
+
+//-----------------------------------------------------------------------------
+// removeMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::removeMotion( const LLUUID& id )
+{
+ mMotionController.removeMotion(id);
+}
+
+//-----------------------------------------------------------------------------
+// getMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLCharacter::createMotion( const LLUUID &id )
+{
+ return mMotionController.createMotion( id );
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::startMotion(const LLUUID &id, F32 start_offset)
+{
+ return mMotionController.startMotion(id, start_offset);
+}
+
+
+//-----------------------------------------------------------------------------
+// stopMotion()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::stopMotion(const LLUUID& id, BOOL stop_immediate)
+{
+ return mMotionController.stopMotionLocally(id, stop_immediate);
+}
+
+//-----------------------------------------------------------------------------
+// isMotionActive()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::isMotionActive(const LLUUID& id)
+{
+ LLMotion *motionp = mMotionController.findMotion(id);
+ if (motionp)
+ {
+ return mMotionController.isMotionActive(motionp);
+ }
+
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// onDeactivateMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::requestStopMotion( LLMotion* motion)
+{
+// llinfos << "DEBUG: Char::onDeactivateMotion( " << motionName << " )" << llendl;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateMotion()
+//-----------------------------------------------------------------------------
+void LLCharacter::updateMotion(BOOL force_update)
+{
+ // unpause if we're forcing an update or
+ // number of outstanding pause requests has dropped
+ // to the initial one
+ if (mMotionController.isPaused() &&
+ (force_update || mPauseRequest->getNumRefs() == 1))
+ {
+ mMotionController.unpause();
+ }
+
+ mMotionController.updateMotion();
+
+ // pause once again, after forced update, if there are outstanding
+ // pause requests
+ if (force_update && mPauseRequest->getNumRefs() > 1)
+ {
+ mMotionController.pause();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// flushAllMotions()
+//-----------------------------------------------------------------------------
+void LLCharacter::flushAllMotions()
+{
+ mMotionController.flushAllMotions();
+}
+
+
+//-----------------------------------------------------------------------------
+// dumpCharacter()
+//-----------------------------------------------------------------------------
+void LLCharacter::dumpCharacter( LLJoint *joint )
+{
+ // handle top level entry into recursion
+ if (joint == NULL)
+ {
+ llinfos << "DEBUG: Dumping Character @" << this << llendl;
+ dumpCharacter( getRootJoint() );
+ llinfos << "DEBUG: Done." << llendl;
+ return;
+ }
+
+ // print joint info
+ llinfos << "DEBUG: " << joint->getName() << " (" << (joint->getParent()?joint->getParent()->getName():std::string("ROOT")) << ")" << llendl;
+
+ // recurse
+ for ( LLJoint *j = joint->mChildren.getFirstData();
+ j != NULL;
+ j = joint->mChildren.getNextData() )
+ {
+ dumpCharacter(j);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAnimationData()
+//-----------------------------------------------------------------------------
+void LLCharacter::setAnimationData(std::string name, void *data)
+{
+ if(mAnimationData.getValue(name))
+ {
+ *mAnimationData[name] = data;
+ }
+ else
+ {
+ mAnimationData.addToHead(name, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAnimationData()
+//-----------------------------------------------------------------------------
+void * LLCharacter::getAnimationData(std::string name)
+{
+ void **result = mAnimationData.getValue(name);
+ void *return_value; // Necessary to suppress VC6 warning. JC
+ if (!result)
+ {
+ return_value = NULL;
+ }
+ else
+ {
+ return_value = *result;
+ }
+
+ return return_value;
+}
+
+//-----------------------------------------------------------------------------
+// removeAnimationData()
+//-----------------------------------------------------------------------------
+void LLCharacter::removeAnimationData(std::string name)
+{
+ mAnimationData.remove(name);
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(LLVisualParam* which_param, F32 weight, BOOL set_by_user)
+{
+ S32 index = which_param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ index_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(const char* param_name, F32 weight, BOOL set_by_user)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ name_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ llwarns << "LLCharacter::setVisualParamWeight() Invalid visual parameter: " << param_name << llendl;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// setVisualParamWeight()
+//-----------------------------------------------------------------------------
+BOOL LLCharacter::setVisualParamWeight(S32 index, F32 weight, BOOL set_by_user)
+{
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ index_iter->second->setWeight(weight, set_by_user);
+ return TRUE;
+ }
+ llwarns << "LLCharacter::setVisualParamWeight() Invalid visual parameter index: " << index << llendl;
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(LLVisualParam *which_param)
+{
+ S32 index = which_param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ return index_iter->second->getWeight();
+ }
+ else
+ {
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter*, index= " << index << llendl;
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(const char* param_name)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ return name_iter->second->getWeight();
+ }
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter: " << param_name << llendl;
+ return 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParamWeight()
+//-----------------------------------------------------------------------------
+F32 LLCharacter::getVisualParamWeight(S32 index)
+{
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ if (index_iter != mVisualParamIndexMap.end())
+ {
+ return index_iter->second->getWeight();
+ }
+ else
+ {
+ llwarns << "LLCharacter::getVisualParamWeight() Invalid visual parameter index: " << index << llendl;
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearVisualParamWeights()
+//-----------------------------------------------------------------------------
+void LLCharacter::clearVisualParamWeights()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->setWeight( param->getDefaultWeight(), FALSE );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam* LLCharacter::getVisualParam(const char *param_name)
+{
+ LLString tname(param_name);
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.checkString(tname);
+ VisualParamNameMap_t::iterator name_iter = mVisualParamNameMap.find(tableptr);
+ if (name_iter != mVisualParamNameMap.end())
+ {
+ return name_iter->second;
+ }
+ llwarns << "LLCharacter::getVisualParam() Invalid visual parameter: " << param_name << llendl;
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// addSharedVisualParam()
+//-----------------------------------------------------------------------------
+void LLCharacter::addSharedVisualParam(LLVisualParam *param)
+{
+ S32 index = param->getID();
+ VisualParamIndexMap_t::iterator index_iter = mVisualParamIndexMap.find(index);
+ LLVisualParam* current_param = 0;
+ if (index_iter != mVisualParamIndexMap.end())
+ current_param = index_iter->second;
+ if( current_param )
+ {
+ LLVisualParam* next_param = current_param;
+ while(next_param->getNextParam())
+ {
+ next_param = next_param->getNextParam();
+ }
+ next_param->setNextParam(param);
+ }
+ else
+ {
+ llwarns << "Shared visual parameter " << param->getName() << " does not already exist with ID " <<
+ param->getID() << llendl;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addVisualParam()
+//-----------------------------------------------------------------------------
+void LLCharacter::addVisualParam(LLVisualParam *param)
+{
+ S32 index = param->getID();
+ // Add Index map
+ std::pair<VisualParamIndexMap_t::iterator, bool> idxres;
+ idxres = mVisualParamIndexMap.insert(VisualParamIndexMap_t::value_type(index, param));
+ if (!idxres.second)
+ {
+ llwarns << "Visual parameter " << param->getName() << " already exists with same ID as " <<
+ param->getName() << llendl;
+ VisualParamIndexMap_t::iterator index_iter = idxres.first;
+ index_iter->second = param;
+ }
+
+ if (param->getInfo())
+ {
+ // Add name map
+ LLString tname(param->getName());
+ LLString::toLower(tname);
+ char *tableptr = sVisualParamNames.addString(tname);
+ std::pair<VisualParamNameMap_t::iterator, bool> nameres;
+ nameres = mVisualParamNameMap.insert(VisualParamNameMap_t::value_type(tableptr, param));
+ if (!nameres.second)
+ {
+ // Already exists, copy param
+ VisualParamNameMap_t::iterator name_iter = nameres.first;
+ name_iter->second = param;
+ }
+ }
+ //llinfos << "Adding Visual Param '" << param->getName() << "' ( " << index << " )" << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// updateVisualParams()
+//-----------------------------------------------------------------------------
+void LLCharacter::updateVisualParams()
+{
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->isAnimating())
+ {
+ continue;
+ }
+ // only apply parameters whose effective weight has changed
+ F32 effective_weight = ( param->getSex() & mSex ) ? param->getWeight() : param->getDefaultWeight();
+ if (effective_weight != param->getLastWeight())
+ {
+ param->apply( mSex );
+ }
+ }
+}
+
+LLAnimPauseRequest LLCharacter::requestPause()
+{
+ mMotionController.pause();
+ return mPauseRequest;
+}
+
diff --git a/indra/llcharacter/llcharacter.h b/indra/llcharacter/llcharacter.h
new file mode 100644
index 0000000000..f2f106f360
--- /dev/null
+++ b/indra/llcharacter/llcharacter.h
@@ -0,0 +1,252 @@
+/**
+ * @file llcharacter.h
+ * @brief Implementation of LLCharacter class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHARACTER_H
+#define LL_LLCHARACTER_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "lljoint.h"
+#include "llmotioncontroller.h"
+#include "llassoclist.h"
+#include "llvisualparam.h"
+#include "linked_lists.h"
+#include "string_table.h"
+#include "llmemory.h"
+
+class LLPolyMesh;
+
+class LLPauseRequestHandle : public LLThreadSafeRefCount
+{
+public:
+ LLPauseRequestHandle() {};
+};
+
+typedef LLPointer<LLPauseRequestHandle> LLAnimPauseRequest;
+
+//-----------------------------------------------------------------------------
+// class LLCharacter
+//-----------------------------------------------------------------------------
+class LLCharacter
+{
+public:
+ // Constructor
+ LLCharacter();
+
+ // Destructor
+ virtual ~LLCharacter();
+
+ //-------------------------------------------------------------------------
+ // LLCharacter Interface
+ // These functions must be implemented by subclasses.
+ //-------------------------------------------------------------------------
+
+ // get the prefix to be used to lookup motion data files
+ // from the viewer data directory
+ virtual const char *getAnimationPrefix() = 0;
+
+ // get the root joint of the character
+ virtual LLJoint *getRootJoint() = 0;
+
+ // get the specified joint
+ // default implementation does recursive search,
+ // subclasses may optimize/cache results.
+ virtual LLJoint *getJoint( const std::string &name );
+
+ // get the position of the character
+ virtual LLVector3 getCharacterPosition() = 0;
+
+ // get the rotation of the character
+ virtual LLQuaternion getCharacterRotation() = 0;
+
+ // get the velocity of the character
+ virtual LLVector3 getCharacterVelocity() = 0;
+
+ // get the angular velocity of the character
+ virtual LLVector3 getCharacterAngularVelocity() = 0;
+
+ // get the height & normal of the ground under a point
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm) = 0;
+
+ // allocate an array of joints for the character skeleton
+ // this must be overloaded to support joint subclasses,
+ // and is called implicitly from buildSkeleton().
+ // Note this must handle reallocation as it will be called
+ // each time buildSkeleton() is called.
+ virtual BOOL allocateCharacterJoints( U32 num ) = 0;
+
+ // skeleton joint accessor to support joint subclasses
+ virtual LLJoint *getCharacterJoint( U32 i ) = 0;
+
+ // get the physics time dilation for the simulator
+ virtual F32 getTimeDilation() = 0;
+
+ // gets current pixel area of this character
+ virtual F32 getPixelArea() = 0;
+
+ // gets the head mesh of the character
+ virtual LLPolyMesh* getHeadMesh() = 0;
+
+ // gets the upper body mesh of the character
+ virtual LLPolyMesh* getUpperBodyMesh() = 0;
+
+ // gets global coordinates from agent local coordinates
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position) = 0;
+
+ // gets agent local coordinates from global coordinates
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position) = 0;
+
+ // updates all visual parameters for this character
+ virtual void updateVisualParams();
+
+ virtual void addDebugText( const char* text ) = 0;
+
+ virtual const LLUUID& getID() = 0;
+ //-------------------------------------------------------------------------
+ // End Interface
+ //-------------------------------------------------------------------------
+ // registers a motion with the character
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create );
+
+ void removeMotion( const LLUUID& id );
+
+ // returns an instance of a registered motion
+ LLMotion* createMotion( const LLUUID &id );
+
+ // start a motion
+ // returns true if successful, false if an error occurred
+ virtual BOOL startMotion( const LLUUID& id, F32 start_offset = 0.f);
+
+ // stop a motion
+ virtual BOOL stopMotion( const LLUUID& id, BOOL stop_immediate = FALSE );
+
+ // is this motion active?
+ BOOL isMotionActive( const LLUUID& id );
+
+ // Event handler for motion deactivation.
+ // Called when a motion has completely stopped and has been deactivated.
+ // Subclasses may optionally override this.
+ // The default implementation does nothing.
+ virtual void requestStopMotion( LLMotion* motion );
+
+ // periodic update function, steps the motion controller
+ void updateMotion(BOOL force_update = FALSE);
+
+ LLAnimPauseRequest requestPause();
+ BOOL areAnimationsPaused() { return mMotionController.isPaused(); }
+ void setAnimTimeFactor(F32 factor) { mMotionController.setTimeFactor(factor); }
+ void setTimeStep(F32 time_step) { mMotionController.setTimeStep(time_step); }
+ // Releases all motion instances which should result in
+ // no cached references to character joint data. This is
+ // useful if a character wants to rebuild it's skeleton.
+ virtual void flushAllMotions();
+
+ // dumps information for debugging
+ virtual void dumpCharacter( LLJoint *joint = NULL );
+
+ virtual F32 getPreferredPelvisHeight() { return mPreferredPelvisHeight; }
+
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset) { return LLVector3::zero; }
+
+ virtual LLJoint* findCollisionVolume(U32 volume_id) { return NULL; }
+
+ virtual S32 getCollisionVolumeID(std::string &name) { return -1; }
+
+ void setAnimationData(std::string name, void *data);
+
+ void *getAnimationData(std::string name);
+
+ void removeAnimationData(std::string name);
+
+ void addVisualParam(LLVisualParam *param);
+ void addSharedVisualParam(LLVisualParam *param);
+
+ BOOL setVisualParamWeight(LLVisualParam *which_param, F32 weight, BOOL set_by_user = FALSE );
+ BOOL setVisualParamWeight(const char* param_name, F32 weight, BOOL set_by_user = FALSE );
+ BOOL setVisualParamWeight(S32 index, F32 weight, BOOL set_by_user = FALSE );
+
+ // get visual param weight by param or name
+ F32 getVisualParamWeight(LLVisualParam *distortion);
+ F32 getVisualParamWeight(const char* param_name);
+ F32 getVisualParamWeight(S32 index);
+
+ // set all morph weights to 0
+ void clearVisualParamWeights();
+
+ // visual parameter accessors
+ LLVisualParam* getFirstVisualParam()
+ {
+ mCurIterator = mVisualParamIndexMap.begin();
+ return getNextVisualParam();
+ }
+ LLVisualParam* getNextVisualParam()
+ {
+ if (mCurIterator == mVisualParamIndexMap.end())
+ return 0;
+ return (mCurIterator++)->second;
+ }
+
+ LLVisualParam* getVisualParam(S32 id)
+ {
+ VisualParamIndexMap_t::iterator iter = mVisualParamIndexMap.find(id);
+ return (iter == mVisualParamIndexMap.end()) ? 0 : iter->second;
+ }
+ S32 getVisualParamID(LLVisualParam *id)
+ {
+ VisualParamIndexMap_t::iterator iter;
+ for (iter = mVisualParamIndexMap.begin(); iter != mVisualParamIndexMap.end(); iter++)
+ {
+ if (iter->second == id)
+ return iter->first;
+ }
+ return 0;
+ }
+ S32 getVisualParamCount() { return (S32)mVisualParamIndexMap.size(); }
+ LLVisualParam* getVisualParam(const char *name);
+
+
+ ESex getSex() { return mSex; }
+ void setSex( ESex sex ) { mSex = sex; }
+
+ U32 getAppearanceSerialNum() const { return mAppearanceSerialNum; }
+ void setAppearanceSerialNum( U32 num ) { mAppearanceSerialNum = num; }
+
+ U32 getSkeletonSerialNum() const { return mSkeletonSerialNum; }
+ void setSkeletonSerialNum( U32 num ) { mSkeletonSerialNum = num; }
+
+ static LLLinkedList< LLCharacter > sInstances;
+
+protected:
+ LLMotionController mMotionController;
+
+ LLAssocList<std::string, void *> mAnimationData;
+
+ F32 mPreferredPelvisHeight;
+ ESex mSex;
+ U32 mAppearanceSerialNum;
+ U32 mSkeletonSerialNum;
+ LLAnimPauseRequest mPauseRequest;
+
+
+private:
+ // visual parameter stuff
+ typedef std::map<S32, LLVisualParam *> VisualParamIndexMap_t;
+ VisualParamIndexMap_t mVisualParamIndexMap;
+ VisualParamIndexMap_t::iterator mCurIterator;
+ typedef std::map<char *, LLVisualParam *> VisualParamNameMap_t;
+ VisualParamNameMap_t mVisualParamNameMap;
+
+ static LLStringTable sVisualParamNames;
+};
+
+#endif // LL_LLCHARACTER_H
+
diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp
new file mode 100644
index 0000000000..f6cfc0ab80
--- /dev/null
+++ b/indra/llcharacter/lleditingmotion.cpp
@@ -0,0 +1,234 @@
+/**
+ * @file lleditingmotion.cpp
+ * @brief Implementation of LLEditingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lleditingmotion.h"
+#include "llcharacter.h"
+#include "llhandmotion.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const LLQuaternion EDIT_MOTION_WRIST_ROTATION(F_PI_BY_TWO * 0.7f, LLVector3(1.0f, 0.0f, 0.0f));
+const F32 TARGET_LAG_HALF_LIFE = 0.1f; // half-life of IK targeting
+const F32 TORSO_LAG_HALF_LIFE = 0.2f;
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+
+S32 LLEditingMotion::sHandPose = LLHandMotion::HAND_POSE_RELAXED_R;
+S32 LLEditingMotion::sHandPosePriority = 3;
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEditingMotion::LLEditingMotion( const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+
+ // create kinematic chain
+ mParentJoint.addChild( &mShoulderJoint );
+ mShoulderJoint.addChild( &mElbowJoint );
+ mElbowJoint.addChild( &mWristJoint );
+
+ mName = "editing";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEditingMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEditingMotion::~LLEditingMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEditingMotion::onInitialize(LLCharacter *character)
+{
+ // save character for future use
+ mCharacter = character;
+
+ // make sure character skeleton is copacetic
+ if (!mCharacter->getJoint("mShoulderLeft") ||
+ !mCharacter->getJoint("mElbowLeft") ||
+ !mCharacter->getJoint("mWristLeft"))
+ {
+ llwarns << "Invalid skeleton for editing motion!" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ // get the shoulder, elbow, wrist joints from the character
+ mParentState.setJoint( mCharacter->getJoint("mShoulderLeft")->getParent() );
+ mShoulderState.setJoint( mCharacter->getJoint("mShoulderLeft") );
+ mElbowState.setJoint( mCharacter->getJoint("mElbowLeft") );
+ mWristState.setJoint( mCharacter->getJoint("mWristLeft") );
+ mTorsoState.setJoint( mCharacter->getJoint("mTorso"));
+
+ if ( ! mParentState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get parent joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mWristOffset = LLVector3(0.0f, 0.2f, 0.0f);
+
+ // add joint states to the pose
+ mShoulderState.setUsage(LLJointState::ROT);
+ mElbowState.setUsage(LLJointState::ROT);
+ mTorsoState.setUsage(LLJointState::ROT);
+ mWristState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderState );
+ addJointState( &mElbowState );
+ addJointState( &mTorsoState );
+ addJointState( &mWristState );
+
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ // connect the ikSolver to the chain
+ mIKSolver.setPoleVector( LLVector3( -1.0f, 1.0f, 0.0f ) );
+ // specifying the elbow's axis will prevent bad IK for the more
+ // singular configurations, but the axis is limb-specific -- Leviathan
+ mIKSolver.setBAxis( LLVector3( -0.682683f, 0.0f, -0.730714f ) );
+ mIKSolver.setupJoints( &mShoulderJoint, &mElbowJoint, &mWristJoint, &mTarget );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEditingMotion::onActivate()
+{
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 focus_pt;
+ LLVector3* pointAtPt = (LLVector3*)mCharacter->getAnimationData("PointAtPoint");
+
+
+ BOOL result = TRUE;
+
+ if (!pointAtPt)
+ {
+ focus_pt = mLastSelectPt;
+ result = FALSE;
+ }
+ else
+ {
+ focus_pt = *pointAtPt;
+ mLastSelectPt = focus_pt;
+ }
+
+ focus_pt += mCharacter->getCharacterPosition();
+
+ // propagate joint positions to kinematic chain
+ mParentJoint.setPosition( mParentState.getJoint()->getWorldPosition() );
+ mShoulderJoint.setPosition( mShoulderState.getJoint()->getPosition() );
+ mElbowJoint.setPosition( mElbowState.getJoint()->getPosition() );
+ mWristJoint.setPosition( mWristState.getJoint()->getPosition() + mWristOffset );
+
+ // propagate current joint rotations to kinematic chain
+ mParentJoint.setRotation( mParentState.getJoint()->getWorldRotation() );
+ mShoulderJoint.setRotation( mShoulderState.getJoint()->getRotation() );
+ mElbowJoint.setRotation( mElbowState.getJoint()->getRotation() );
+
+ // update target position from character
+ LLVector3 target = focus_pt - mParentJoint.getPosition();
+ F32 target_dist = target.normVec();
+
+ LLVector3 edit_plane_normal(1.f / F_SQRT2, 1.f / F_SQRT2, 0.f);
+ edit_plane_normal.normVec();
+
+ edit_plane_normal.rotVec(mTorsoState.getJoint()->getWorldRotation());
+
+ F32 dot = edit_plane_normal * target;
+
+ if (dot < 0.f)
+ {
+ target = target + (edit_plane_normal * (dot * 2.f));
+ target.mV[VZ] += clamp_rescale(dot, 0.f, -1.f, 0.f, 5.f);
+ target.normVec();
+ }
+
+ target = target * target_dist;
+ if (!target.isFinite())
+ {
+ llerrs << "Non finite target in editing motion with target distance of " << target_dist <<
+ " and focus point " << focus_pt << llendl;
+ }
+
+ mTarget.setPosition( target + mParentJoint.getPosition());
+
+// llinfos << "Point At: " << mTarget.getPosition() << llendl;
+
+ // update the ikSolver
+ if (!mTarget.getPosition().isExactlyZero())
+ {
+ LLQuaternion shoulderRot = mShoulderJoint.getRotation();
+ LLQuaternion elbowRot = mElbowJoint.getRotation();
+ mIKSolver.solve();
+
+ // use blending...
+ F32 slerp_amt = LLCriticalDamp::getInterpolant(TARGET_LAG_HALF_LIFE);
+ shoulderRot = slerp(slerp_amt, mShoulderJoint.getRotation(), shoulderRot);
+ elbowRot = slerp(slerp_amt, mElbowJoint.getRotation(), elbowRot);
+
+ // now put blended values back into joints
+ llassert(shoulderRot.isFinite());
+ llassert(elbowRot.isFinite());
+ mShoulderState.setRotation(shoulderRot);
+ mElbowState.setRotation(elbowRot);
+ mWristState.setRotation(LLQuaternion::DEFAULT);
+ }
+
+ mCharacter->setAnimationData("Hand Pose", &sHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &sHandPosePriority);
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLEditingMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEditingMotion::onDeactivate()
+{
+}
+
+
+// End
diff --git a/indra/llcharacter/lleditingmotion.h b/indra/llcharacter/lleditingmotion.h
new file mode 100644
index 0000000000..b817a765b2
--- /dev/null
+++ b/indra/llcharacter/lleditingmotion.h
@@ -0,0 +1,115 @@
+/**
+ * @file lleditingmotion.h
+ * @brief Implementation of LLEditingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEDITINGMOTION_H
+#define LL_LLEDITINGMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lljointsolverrp3.h"
+#include "v3dmath.h"
+
+#define EDITING_EASEIN_DURATION 0.0f
+#define EDITING_EASEOUT_DURATION 0.5f
+#define EDITING_PRIORITY LLJoint::HIGH_PRIORITY
+#define MIN_REQUIRED_PIXEL_AREA_EDITING 500.f
+
+//-----------------------------------------------------------------------------
+// class LLEditingMotion
+//-----------------------------------------------------------------------------
+class LLEditingMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEditingMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEditingMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLEditingMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return EDITING_EASEIN_DURATION; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return EDITING_EASEOUT_DURATION; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return EDITING_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EDITING; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLVector3 mWristOffset;
+
+ LLJointState mParentState;
+ LLJointState mShoulderState;
+ LLJointState mElbowState;
+ LLJointState mWristState;
+ LLJointState mTorsoState;
+
+ LLJoint mParentJoint;
+ LLJoint mShoulderJoint;
+ LLJoint mElbowJoint;
+ LLJoint mWristJoint;
+ LLJoint mTarget;
+ LLJointSolverRP3 mIKSolver;
+
+ static S32 sHandPose;
+ static S32 sHandPosePriority;
+ LLVector3 mLastSelectPt;
+};
+
+#endif // LL_LLKEYFRAMEMOTION_H
+
diff --git a/indra/llcharacter/llgesture.cpp b/indra/llcharacter/llgesture.cpp
new file mode 100644
index 0000000000..028c2f7fdf
--- /dev/null
+++ b/indra/llcharacter/llgesture.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llgesture.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "indra_constants.h"
+
+#include "llgesture.h"
+#include "llendianswizzle.h"
+#include "message.h"
+#include <boost/tokenizer.hpp>
+
+// for allocating serialization buffers - these need to be updated when members change
+const S32 LLGestureList::SERIAL_HEADER_SIZE = sizeof(S32);
+const S32 LLGesture::MAX_SERIAL_SIZE = sizeof(KEY) + sizeof(MASK) + 16 + 26 + 41 + 41;
+
+LLGesture::LLGesture()
+: mKey(KEY_NONE),
+ mMask(MASK_NONE),
+ mTrigger(),
+ mTriggerLower(),
+ mSoundItemID(),
+ mAnimation(),
+ mOutputString()
+{ }
+
+LLGesture::LLGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id,
+ const std::string &animation,
+ const std::string &output_string)
+:
+ mKey(key),
+ mMask(mask),
+ mTrigger(trigger),
+ mTriggerLower(trigger),
+ mSoundItemID(sound_item_id),
+ mAnimation(animation),
+ mOutputString(output_string)
+{
+ mTriggerLower = utf8str_tolower(mTriggerLower);
+}
+
+LLGesture::LLGesture(U8 **buffer, S32 max_size)
+{
+ *buffer = deserialize(*buffer, max_size);
+}
+
+LLGesture::LLGesture(const LLGesture &rhs)
+{
+ mKey = rhs.mKey;
+ mMask = rhs.mMask;
+ mTrigger = rhs.mTrigger;
+ mTriggerLower = rhs.mTriggerLower;
+ mSoundItemID = rhs.mSoundItemID;
+ mAnimation = rhs.mAnimation;
+ mOutputString = rhs.mOutputString;
+}
+
+const LLGesture &LLGesture::operator =(const LLGesture &rhs)
+{
+ mKey = rhs.mKey;
+ mMask = rhs.mMask;
+ mTrigger = rhs.mTrigger;
+ mTriggerLower = rhs.mTriggerLower;
+ mSoundItemID = rhs.mSoundItemID;
+ mAnimation = rhs.mAnimation;
+ mOutputString = rhs.mOutputString;
+ return (*this);
+}
+
+
+BOOL LLGesture::trigger(KEY key, MASK mask)
+{
+ llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
+ return FALSE;
+}
+
+
+BOOL LLGesture::trigger(const LLString &trigger_string)
+{
+ llwarns << "Parent class trigger called: you probably didn't mean this." << llendl;
+ return FALSE;
+}
+
+// NOT endian-neutral
+U8 *LLGesture::serialize(U8 *buffer) const
+{
+ htonmemcpy(buffer, &mKey, MVT_S8, 1);
+ buffer += sizeof(mKey);
+ htonmemcpy(buffer, &mMask, MVT_U32, 4);
+ buffer += sizeof(mMask);
+ htonmemcpy(buffer, mSoundItemID.mData, MVT_LLUUID, 16);
+ buffer += 16;
+
+ memcpy(buffer, mTrigger.c_str(), mTrigger.length() + 1); /* Flawfinder: ignore */
+ buffer += mTrigger.length() + 1;
+ memcpy(buffer, mAnimation.c_str(), mAnimation.length() + 1); /* Flawfinder: ignore */
+ buffer += mAnimation.length() + 1;
+ memcpy(buffer, mOutputString.c_str(), mOutputString.length() + 1); /* Flawfinder: ignore */
+ buffer += mOutputString.length() + 1;
+
+ return buffer;
+}
+
+U8 *LLGesture::deserialize(U8 *buffer, S32 max_size)
+{
+ U8 *tmp = buffer;
+
+ if (tmp + sizeof(mKey) + sizeof(mMask) + 16 > buffer + max_size)
+ {
+ llwarns << "Attempt to read past end of buffer, bad data!!!!" << llendl;
+ return buffer;
+ }
+
+ htonmemcpy(&mKey, tmp, MVT_S8, 1);
+ tmp += sizeof(mKey);
+ htonmemcpy(&mMask, tmp, MVT_U32, 4);
+ tmp += sizeof(mMask);
+ htonmemcpy(mSoundItemID.mData, tmp, MVT_LLUUID, 16);
+ tmp += 16;
+
+ mTrigger.assign((char *)tmp);
+ mTriggerLower = mTrigger;
+ mTriggerLower = utf8str_tolower(mTriggerLower);
+ tmp += mTrigger.length() + 1;
+ mAnimation.assign((char *)tmp);
+ //RN: force animation names to lower case
+ // must do this for backwards compatibility
+ mAnimation = utf8str_tolower(mAnimation);
+ tmp += mAnimation.length() + 1;
+ mOutputString.assign((char *)tmp);
+ tmp += mOutputString.length() + 1;
+
+ if (tmp > buffer + max_size)
+ {
+ llwarns << "Read past end of buffer, bad data!!!!" << llendl;
+ return tmp;
+ }
+
+ return tmp;
+}
+
+S32 LLGesture::getMaxSerialSize()
+{
+ return MAX_SERIAL_SIZE;
+}
+
+//---------------------------------------------------------------------
+// LLGestureList
+//---------------------------------------------------------------------
+
+LLGestureList::LLGestureList()
+: mList(0)
+{
+ // add some gestures for debugging
+// LLGesture *gesture = NULL;
+/*
+ gesture = new LLGesture(KEY_F2, MASK_NONE, ":-)",
+ SND_CHIRP, "dance2", ":-)" );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F3, MASK_NONE, "/dance",
+ SND_OBJECT_CREATE, "dance3", "(dances)" );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F4, MASK_NONE, "/boogie",
+ LLUUID::null, "dance4", LLString::null );
+ mList.put(gesture);
+
+ gesture = new LLGesture(KEY_F5, MASK_SHIFT, "/tongue",
+ LLUUID::null, "Express_Tongue_Out", LLString::null );
+ mList.put(gesture);
+ */
+}
+
+LLGestureList::~LLGestureList()
+{
+ deleteAll();
+}
+
+
+void LLGestureList::deleteAll()
+{
+ S32 count = mList.count();
+ for (S32 i = 0; i < count; i++)
+ {
+ delete mList.get(i);
+ }
+ mList.reset();
+}
+
+// Iterates through space delimited tokens in string, triggering any gestures found.
+// Generates a revised string that has the found tokens replaced by their replacement strings
+// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
+BOOL LLGestureList::triggerAndReviseString(const LLString &string, LLString* revised_string)
+{
+ LLString tokenized = string;
+
+ BOOL found_gestures = FALSE;
+ BOOL first_token = TRUE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(string, sep);
+ tokenizer::iterator token_iter;
+
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ LLGesture* gesture = NULL;
+
+ if( !found_gestures ) // Only pay attention to the first gesture in the string.
+ {
+ LLString cur_token_lower = *token_iter;
+ LLString::toLower(cur_token_lower);
+
+ for (S32 i = 0; i < mList.count(); i++)
+ {
+ gesture = mList.get(i);
+ if (gesture->trigger(cur_token_lower))
+ {
+ if( !gesture->getOutputString().empty() )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+
+ // Don't muck with the user's capitalization if we don't have to.
+ const std::string& output = gesture->getOutputString();
+ LLString output_lower = LLString(output.c_str());
+ LLString::toLower(output_lower);
+ if( cur_token_lower == output_lower )
+ {
+ revised_string->append(*token_iter);
+ }
+ else
+ {
+ revised_string->append(output.c_str());
+ }
+
+ }
+ found_gestures = TRUE;
+ break;
+ }
+ gesture = NULL;
+ }
+ }
+
+ if( !gesture )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+ revised_string->append( *token_iter );
+ }
+
+ first_token = FALSE;
+ }
+ return found_gestures;
+}
+
+
+
+BOOL LLGestureList::trigger(KEY key, MASK mask)
+{
+ for (S32 i = 0; i < mList.count(); i++)
+ {
+ LLGesture* gesture = mList.get(i);
+ if( gesture )
+ {
+ if (gesture->trigger(key, mask))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ llwarns << "NULL gesture in gesture list (" << i << ")" << llendl
+ }
+ }
+ return FALSE;
+}
+
+// NOT endian-neutral
+U8 *LLGestureList::serialize(U8 *buffer) const
+{
+ // a single S32 serves as the header that tells us how many to read
+ S32 count = mList.count();
+ htonmemcpy(buffer, &count, MVT_S32, 4);
+ buffer += sizeof(count);
+
+ for (S32 i = 0; i < count; i++)
+ {
+ buffer = mList[i]->serialize(buffer);
+ }
+
+ return buffer;
+}
+
+const S32 MAX_GESTURES = 4096;
+
+U8 *LLGestureList::deserialize(U8 *buffer, S32 max_size)
+{
+ deleteAll();
+
+ S32 count;
+ U8 *tmp = buffer;
+
+ if (tmp + sizeof(count) > buffer + max_size)
+ {
+ llwarns << "Invalid max_size" << llendl;
+ return buffer;
+ }
+
+ htonmemcpy(&count, tmp, MVT_S32, 4);
+
+ if (count > MAX_GESTURES)
+ {
+ llwarns << "Unreasonably large gesture list count in deserialize: " << count << llendl;
+ return tmp;
+ }
+
+ tmp += sizeof(count);
+
+ mList.reserve_block(count);
+
+ for (S32 i = 0; i < count; i++)
+ {
+ mList[i] = create_gesture(&tmp, max_size - (S32)(tmp - buffer));
+ if (tmp - buffer > max_size)
+ {
+ llwarns << "Deserialization read past end of buffer, bad data!!!!" << llendl;
+ return tmp;
+ }
+ }
+
+ return tmp;
+}
+
+// this is a helper for deserialize
+// it gets overridden by LLViewerGestureList to create LLViewerGestures
+// overridden by child class to use local LLGesture implementation
+LLGesture *LLGestureList::create_gesture(U8 **buffer, S32 max_size)
+{
+ return new LLGesture(buffer, max_size);
+}
+
+S32 LLGestureList::getMaxSerialSize()
+{
+ return SERIAL_HEADER_SIZE + (count() * LLGesture::getMaxSerialSize());
+}
diff --git a/indra/llcharacter/llgesture.h b/indra/llcharacter/llgesture.h
new file mode 100644
index 0000000000..64c752769b
--- /dev/null
+++ b/indra/llcharacter/llgesture.h
@@ -0,0 +1,96 @@
+/**
+ * @file llgesture.h
+ * @brief A gesture is a combination of a triggering chat phrase or
+ * key, a sound, an animation, and a chat string.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGESTURE_H
+#define LL_LLGESTURE_H
+
+#include "llanimationstates.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "lldarray.h"
+
+class LLGesture
+{
+public:
+ LLGesture();
+ LLGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id, const std::string &animation,
+ const std::string &output_string);
+
+ LLGesture(U8 **buffer, S32 max_size); // deserializes, advances buffer
+ LLGesture(const LLGesture &gesture);
+ const LLGesture &operator=(const LLGesture &rhs);
+
+ virtual ~LLGesture() {};
+
+ // Accessors
+ KEY getKey() const { return mKey; }
+ MASK getMask() const { return mMask; }
+ const std::string& getTrigger() const { return mTrigger; }
+ const LLUUID& getSound() const { return mSoundItemID; }
+ const std::string& getAnimation() const { return mAnimation; }
+ const std::string& getOutputString() const { return mOutputString; }
+
+ // Triggers if a key/mask matches it
+ virtual BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if case-insensitive substring matches (assumes string is lowercase)
+ virtual BOOL trigger(const LLString &string);
+
+ // non-endian-neutral serialization
+ U8 *serialize(U8 *buffer) const;
+ U8 *deserialize(U8 *buffer, S32 max_size);
+ static S32 getMaxSerialSize();
+
+protected:
+ KEY mKey; // usually a function key
+ MASK mMask; // usually MASK_NONE, or MASK_SHIFT
+ std::string mTrigger; // string, no whitespace allowed
+ std::string mTriggerLower; // lowercase version of mTrigger
+ LLUUID mSoundItemID; // ItemID of sound to play, LLUUID::null if none
+ std::string mAnimation; // canonical name of animation or face animation
+ std::string mOutputString; // string to say
+
+ static const S32 MAX_SERIAL_SIZE;
+};
+
+class LLGestureList
+{
+public:
+ LLGestureList();
+ virtual ~LLGestureList();
+
+ // Triggers if a key/mask matches one in the list
+ BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if substring matches and generates revised string.
+ BOOL triggerAndReviseString(const LLString &string, LLString* revised_string);
+
+ // Used for construction from UI
+ S32 count() const { return mList.count(); }
+ virtual LLGesture* get(S32 i) const { return mList.get(i); }
+ virtual void put(LLGesture* gesture) { mList.put( gesture ); }
+ void deleteAll();
+
+ // non-endian-neutral serialization
+ U8 *serialize(U8 *buffer) const;
+ U8 *deserialize(U8 *buffer, S32 max_size);
+ S32 getMaxSerialSize();
+
+protected:
+ // overridden by child class to use local LLGesture implementation
+ virtual LLGesture *create_gesture(U8 **buffer, S32 max_size);
+
+protected:
+ LLDynamicArray<LLGesture*> mList;
+
+ static const S32 SERIAL_HEADER_SIZE;
+};
+
+#endif
diff --git a/indra/llcharacter/llhandmotion.cpp b/indra/llcharacter/llhandmotion.cpp
new file mode 100644
index 0000000000..d574363f83
--- /dev/null
+++ b/indra/llcharacter/llhandmotion.cpp
@@ -0,0 +1,202 @@
+/**
+ * @file llhandmotion.cpp
+ * @brief Implementation of LLHandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llhandmotion.h"
+#include "llcharacter.h"
+#include "m3math.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+const char *gHandPoseNames[LLHandMotion::NUM_HAND_POSES] = /* Flawfinder: ignore */
+{
+ "",
+ "Hands_Relaxed",
+ "Hands_Point",
+ "Hands_Fist",
+ "Hands_Relaxed_L",
+ "Hands_Point_L",
+ "Hands_Fist_L",
+ "Hands_Relaxed_R",
+ "Hands_Point_R",
+ "Hands_Fist_R",
+ "Hands_Salute_R",
+ "Hands_Typing",
+ "Hands_Peace_R",
+ "Hands_Spread_R"
+};
+
+const F32 HAND_MORPH_BLEND_TIME = 0.2f;
+
+//-----------------------------------------------------------------------------
+// LLHandMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLHandMotion::LLHandMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mLastTime = 0.f;
+ mCurrentPose = HAND_POSE_RELAXED;
+ mNewPose = HAND_POSE_RELAXED;
+ mName = "hand_motion";
+
+ //RN: flag hand joint as highest priority for now, until we implement a proper animation track
+ mJointSignature[0][LL_HAND_JOINT_NUM] = 0xff;
+ mJointSignature[1][LL_HAND_JOINT_NUM] = 0xff;
+ mJointSignature[2][LL_HAND_JOINT_NUM] = 0xff;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLHandMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLHandMotion::~LLHandMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLHandMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLHandMotion::onActivate()
+{
+ LLPolyMesh *upperBodyMesh = mCharacter->getUpperBodyMesh();
+
+ if (upperBodyMesh)
+ {
+ // Note: 0 is the default
+ for (S32 i = 1; i < LLHandMotion::NUM_HAND_POSES; i++)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[i], 0.f);
+ }
+ mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], 1.f);
+ mCharacter->updateVisualParams();
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLHandMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ eHandPose *requestedHandPose;
+
+ F32 timeDelta = time - mLastTime;
+ mLastTime = time;
+
+ requestedHandPose = (eHandPose *)mCharacter->getAnimationData("Hand Pose");
+ // check to see if requested pose has changed
+ if (!requestedHandPose)
+ {
+ if (mNewPose != HAND_POSE_RELAXED && mNewPose != mCurrentPose)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
+ }
+ mNewPose = HAND_POSE_RELAXED;
+ }
+ else
+ {
+ // this is a new morph we didn't know about before
+ if (*requestedHandPose != mNewPose && mNewPose != mCurrentPose && mNewPose != HAND_POSE_SPREAD)
+ {
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], 0.f);
+ }
+ mNewPose = *requestedHandPose;
+ }
+
+ mCharacter->removeAnimationData("Hand Pose");
+ mCharacter->removeAnimationData("Hand Pose Priority");
+
+// if (requestedHandPose)
+// llinfos << "Hand Pose " << *requestedHandPose << llendl;
+
+ // if we are still blending...
+ if (mCurrentPose != mNewPose)
+ {
+ F32 incomingWeight = 1.f;
+ F32 outgoingWeight = 0.f;
+
+ if (mNewPose != HAND_POSE_SPREAD)
+ {
+ incomingWeight = mCharacter->getVisualParamWeight(gHandPoseNames[mNewPose]);
+ incomingWeight += (timeDelta / HAND_MORPH_BLEND_TIME);
+ incomingWeight = llclamp(incomingWeight, 0.f, 1.f);
+ mCharacter->setVisualParamWeight(gHandPoseNames[mNewPose], incomingWeight);
+ }
+
+ if (mCurrentPose != HAND_POSE_SPREAD)
+ {
+ outgoingWeight = mCharacter->getVisualParamWeight(gHandPoseNames[mCurrentPose]);
+ outgoingWeight -= (timeDelta / HAND_MORPH_BLEND_TIME);
+ outgoingWeight = llclamp(outgoingWeight, 0.f, 1.f);
+ mCharacter->setVisualParamWeight(gHandPoseNames[mCurrentPose], outgoingWeight);
+ }
+
+ mCharacter->updateVisualParams();
+
+ if (incomingWeight == 1.f && outgoingWeight == 0.f)
+ {
+ mCurrentPose = mNewPose;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLHandMotion::onDeactivate()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHandMotion::getHandPoseName()
+//-----------------------------------------------------------------------------
+LLString LLHandMotion::getHandPoseName(eHandPose pose)
+{
+ if ((S32)pose < LLHandMotion::NUM_HAND_POSES && (S32)pose >= 0)
+ {
+ return gHandPoseNames[pose];
+ }
+ return "";
+}
+
+LLHandMotion::eHandPose LLHandMotion::getHandPose(LLString posename)
+{
+ for (S32 pose = 0; pose < LLHandMotion::NUM_HAND_POSES; ++pose)
+ {
+ if (gHandPoseNames[pose] == posename)
+ {
+ return (eHandPose)pose;
+ }
+ }
+ return (eHandPose)0;
+}
+
+// End
diff --git a/indra/llcharacter/llhandmotion.h b/indra/llcharacter/llhandmotion.h
new file mode 100644
index 0000000000..cef7361633
--- /dev/null
+++ b/indra/llcharacter/llhandmotion.h
@@ -0,0 +1,119 @@
+/**
+ * @file llhandmotion.h
+ * @brief Implementation of LLHandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHANDMOTION_H
+#define LL_LLHANDMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lltimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_HAND 10000.f;
+
+//-----------------------------------------------------------------------------
+// class LLHandMotion
+//-----------------------------------------------------------------------------
+class LLHandMotion :
+ public LLMotion
+{
+public:
+ typedef enum e_hand_pose
+ {
+ HAND_POSE_SPREAD,
+ HAND_POSE_RELAXED,
+ HAND_POSE_POINT,
+ HAND_POSE_FIST,
+ HAND_POSE_RELAXED_L,
+ HAND_POSE_POINT_L,
+ HAND_POSE_FIST_L,
+ HAND_POSE_RELAXED_R,
+ HAND_POSE_POINT_R,
+ HAND_POSE_FIST_R,
+ HAND_POSE_SALUTE_R,
+ HAND_POSE_TYPING,
+ HAND_POSE_PEACE_R,
+ HAND_POSE_PALM_R,
+ NUM_HAND_POSES
+ } eHandPose;
+
+ // Constructor
+ LLHandMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLHandMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLHandMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_HAND; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ static LLString getHandPoseName(eHandPose pose);
+ static eHandPose getHandPose(LLString posename);
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+
+ LLCharacter *mCharacter;
+
+ F32 mLastTime;
+ eHandPose mCurrentPose;
+ eHandPose mNewPose;
+};
+#endif // LL_LLHANDMOTION_H
+
diff --git a/indra/llcharacter/llheadrotmotion.cpp b/indra/llcharacter/llheadrotmotion.cpp
new file mode 100644
index 0000000000..4ff7e6b582
--- /dev/null
+++ b/indra/llcharacter/llheadrotmotion.cpp
@@ -0,0 +1,505 @@
+/**
+ * @file llheadrotmotion.cpp
+ * @brief Implementation of LLHeadRotMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llheadrotmotion.h"
+#include "llcharacter.h"
+#include "llrand.h"
+#include "m3math.h"
+#include "v3dmath.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 TORSO_LAG = 0.35f; // torso rotation factor
+const F32 NECK_LAG = 0.5f; // neck rotation factor
+const F32 HEAD_LOOKAT_LAG_HALF_LIFE = 0.15f; // half-life of lookat targeting for head
+const F32 TORSO_LOOKAT_LAG_HALF_LIFE = 0.27f; // half-life of lookat targeting for torso
+const F32 EYE_LOOKAT_LAG_HALF_LIFE = 0.06f; // half-life of lookat targeting for eye
+const F32 HEAD_ROTATION_CONSTRAINT = F_PI_BY_TWO * 0.8f; // limit angle for head rotation
+
+const F32 MIN_HEAD_LOOKAT_DISTANCE = 0.3f; // minimum distance from head before we turn to look at it
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 EYE_JITTER_MIN_TIME = 0.3f; // min amount of time between eye "jitter" motions
+const F32 EYE_JITTER_MAX_TIME = 2.5f; // max amount of time between eye "jitter" motions
+const F32 EYE_JITTER_MAX_YAW = 0.08f; // max yaw of eye jitter motion
+const F32 EYE_JITTER_MAX_PITCH = 0.015f; // max pitch of eye jitter motion
+const F32 EYE_LOOK_AWAY_MIN_TIME = 5.f; // min amount of time between eye "look away" motions
+const F32 EYE_LOOK_AWAY_MAX_TIME = 15.f; // max amount of time between eye "look away" motions
+const F32 EYE_LOOK_BACK_MIN_TIME = 1.f; // min amount of time before looking back after looking away
+const F32 EYE_LOOK_BACK_MAX_TIME = 5.f; // max amount of time before looking back after looking away
+const F32 EYE_LOOK_AWAY_MAX_YAW = 0.15f; // max yaw of eye look away motion
+const F32 EYE_LOOK_AWAY_MAX_PITCH = 0.12f; // max pitch of look away motion
+const F32 EYE_ROT_LIMIT_ANGLE = F_PI_BY_TWO * 0.3f; //max angle in radians for eye rotation
+
+const F32 EYE_BLINK_MIN_TIME = 0.5f; // minimum amount of time between blinks
+const F32 EYE_BLINK_MAX_TIME = 8.f; // maximum amount of time between blinks
+const F32 EYE_BLINK_CLOSE_TIME = 0.03f; // how long the eye stays closed in a blink
+const F32 EYE_BLINK_SPEED = 0.015f; // seconds it takes for a eye open/close movement
+const F32 EYE_BLINK_TIME_DELTA = 0.005f; // time between one eye starting a blink and the other following
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLHeadRotMotion::LLHeadRotMotion(const LLUUID &id) :
+ LLMotion(id),
+ mCharacter(NULL),
+ mTorsoJoint(NULL),
+ mHeadJoint(NULL)
+{
+ mName = "head_rot";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLHeadRotMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLHeadRotMotion::~LLHeadRotMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLHeadRotMotion::onInitialize(LLCharacter *character)
+{
+ if (!character)
+ return STATUS_FAILURE;
+ mCharacter = character;
+
+ mPelvisJoint = character->getJoint("mPelvis");
+ if ( ! mPelvisJoint )
+ {
+ llinfos << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mRootJoint = character->getJoint("mRoot");
+ if ( ! mRootJoint )
+ {
+ llinfos << getName() << ": Can't get root joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoJoint = character->getJoint("mTorso");
+ if ( ! mTorsoJoint )
+ {
+ llinfos << getName() << ": Can't get torso joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mHeadJoint = character->getJoint("mHead");
+ if ( ! mHeadJoint )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setJoint( character->getJoint("mTorso") );
+ if ( ! mTorsoState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get torso joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mNeckState.setJoint( character->getJoint("mNeck") );
+ if ( ! mNeckState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get neck joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mHeadState.setJoint( character->getJoint("mHead") );
+ if ( ! mHeadState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setUsage(LLJointState::ROT);
+ mNeckState.setUsage(LLJointState::ROT);
+ mHeadState.setUsage(LLJointState::ROT);
+
+ addJointState( &mTorsoState );
+ addJointState( &mNeckState );
+ addJointState( &mHeadState );
+
+ mLastHeadRot.loadIdentity();
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLHeadRotMotion::onActivate()
+{
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLHeadRotMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLQuaternion targetHeadRotWorld;
+ LLQuaternion currentRootRotWorld = mRootJoint->getWorldRotation();
+ LLQuaternion currentInvRootRotWorld = ~currentRootRotWorld;
+
+ F32 head_slerp_amt = LLCriticalDamp::getInterpolant(HEAD_LOOKAT_LAG_HALF_LIFE);
+ F32 torso_slerp_amt = LLCriticalDamp::getInterpolant(TORSO_LOOKAT_LAG_HALF_LIFE);
+
+ LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ if (targetPos)
+ {
+ LLVector3 headLookAt = *targetPos;
+
+// llinfos << "Look At: " << headLookAt + mHeadJoint->getWorldPosition() << llendl;
+
+ F32 lookatDistance = headLookAt.normVec();
+
+ if (lookatDistance < MIN_HEAD_LOOKAT_DISTANCE)
+ {
+ targetHeadRotWorld = mPelvisJoint->getWorldRotation();
+ }
+ else
+ {
+ LLVector3 root_up = LLVector3(0.f, 0.f, 1.f) * currentRootRotWorld;
+ LLVector3 left(root_up % headLookAt);
+ // if look_at has zero length, fail
+ // if look_at and skyward are parallel, fail
+ //
+ // Test both of these conditions with a cross product.
+
+ if (left.magVecSquared() < 0.15f)
+ {
+ LLVector3 root_at = LLVector3(1.f, 0.f, 0.f) * currentRootRotWorld;
+ root_at.mV[VZ] = 0.f;
+ root_at.normVec();
+
+ headLookAt = lerp(headLookAt, root_at, 0.4f);
+ headLookAt.normVec();
+
+ left = root_up % headLookAt;
+ }
+
+ // Make sure look_at and skyward and not parallel
+ // and neither are zero length
+ LLVector3 up(headLookAt % left);
+
+ targetHeadRotWorld = LLQuaternion(headLookAt, left, up);
+ }
+ }
+ else
+ {
+ targetHeadRotWorld = currentRootRotWorld;
+ }
+
+ LLQuaternion head_rot_local = targetHeadRotWorld * currentInvRootRotWorld;
+ head_rot_local.constrain(HEAD_ROTATION_CONSTRAINT);
+
+ // set final torso rotation
+ // Set torso target rotation such that it lags behind the head rotation
+ // by a fixed amount.
+ LLQuaternion torso_rot_local = nlerp(TORSO_LAG, LLQuaternion::DEFAULT, head_rot_local );
+ mTorsoState.setRotation( nlerp(torso_slerp_amt, mTorsoState.getRotation(), torso_rot_local) );
+
+ head_rot_local = nlerp(head_slerp_amt, mLastHeadRot, head_rot_local);
+ mLastHeadRot = head_rot_local;
+
+ // Set the head rotation.
+ LLQuaternion torsoRotLocal = mNeckState.getJoint()->getParent()->getWorldRotation() * currentInvRootRotWorld;
+ head_rot_local = head_rot_local * ~torsoRotLocal;
+ mNeckState.setRotation( nlerp(NECK_LAG, LLQuaternion::DEFAULT, head_rot_local) );
+ mHeadState.setRotation( nlerp(1.f - NECK_LAG, LLQuaternion::DEFAULT, head_rot_local));
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLHeadRotMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLHeadRotMotion::onDeactivate()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEyeMotion::LLEyeMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mEyeJitterTime = 0.f;
+ mEyeJitterYaw = 0.f;
+ mEyeJitterPitch = 0.f;
+
+ mEyeLookAwayTime = 0.f;
+ mEyeLookAwayYaw = 0.f;
+ mEyeLookAwayPitch = 0.f;
+
+ mEyeBlinkTime = 0.f;
+ mEyesClosed = FALSE;
+
+ mHeadJoint = NULL;
+
+ mName = "eye_rot";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEyeMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEyeMotion::~LLEyeMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEyeMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ mHeadJoint = character->getJoint("mHead");
+ if ( ! mHeadJoint )
+ {
+ llinfos << getName() << ": Can't get head joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mLeftEyeState.setJoint( character->getJoint("mEyeLeft") );
+ if ( ! mLeftEyeState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get left eyeball joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mRightEyeState.setJoint( character->getJoint("mEyeRight") );
+ if ( ! mRightEyeState.getJoint() )
+ {
+ llinfos << getName() << ": Can't get Right eyeball joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mLeftEyeState.setUsage(LLJointState::ROT);
+ mRightEyeState.setUsage(LLJointState::ROT);
+
+ addJointState( &mLeftEyeState );
+ addJointState( &mRightEyeState );
+
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEyeMotion::onActivate()
+{
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEyeMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ // Compute eye rotation.
+ LLQuaternion target_eye_rot;
+ LLVector3 eye_look_at;
+ F32 vergence;
+
+ //calculate jitter
+ if (mEyeJitterTimer.getElapsedTimeF32() > mEyeJitterTime)
+ {
+ mEyeJitterTime = EYE_JITTER_MIN_TIME + frand(EYE_JITTER_MAX_TIME - EYE_JITTER_MIN_TIME);
+ mEyeJitterYaw = (frand(2.f) - 1.f) * EYE_JITTER_MAX_YAW;
+ mEyeJitterPitch = (frand(2.f) - 1.f) * EYE_JITTER_MAX_PITCH;
+ // make sure lookaway time count gets updated, because we're resetting the timer
+ mEyeLookAwayTime -= llmax(0.f, mEyeJitterTimer.getElapsedTimeF32());
+ mEyeJitterTimer.reset();
+ }
+ else if (mEyeJitterTimer.getElapsedTimeF32() > mEyeLookAwayTime)
+ {
+ if (frand(1.f) > 0.1f)
+ {
+ // blink while moving eyes some percentage of the time
+ mEyeBlinkTime = mEyeBlinkTimer.getElapsedTimeF32();
+ }
+ if (mEyeLookAwayYaw == 0.f && mEyeLookAwayPitch == 0.f)
+ {
+ mEyeLookAwayYaw = (frand(2.f) - 1.f) * EYE_LOOK_AWAY_MAX_YAW;
+ mEyeLookAwayPitch = (frand(2.f) - 1.f) * EYE_LOOK_AWAY_MAX_PITCH;
+ mEyeLookAwayTime = EYE_LOOK_BACK_MIN_TIME + frand(EYE_LOOK_BACK_MAX_TIME - EYE_LOOK_BACK_MIN_TIME);
+ }
+ else
+ {
+ mEyeLookAwayYaw = 0.f;
+ mEyeLookAwayPitch = 0.f;
+ mEyeLookAwayTime = EYE_LOOK_AWAY_MIN_TIME + frand(EYE_LOOK_AWAY_MAX_TIME - EYE_LOOK_AWAY_MIN_TIME);
+ }
+ }
+
+ // do blinking
+ if (!mEyesClosed && mEyeBlinkTimer.getElapsedTimeF32() >= mEyeBlinkTime)
+ {
+ F32 leftEyeBlinkMorph = mEyeBlinkTimer.getElapsedTimeF32() - mEyeBlinkTime;
+ F32 rightEyeBlinkMorph = leftEyeBlinkMorph - EYE_BLINK_TIME_DELTA;
+
+ leftEyeBlinkMorph = llclamp(leftEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ rightEyeBlinkMorph = llclamp(rightEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ mCharacter->setVisualParamWeight("Blink_Left", leftEyeBlinkMorph);
+ mCharacter->setVisualParamWeight("Blink_Right", rightEyeBlinkMorph);
+ mCharacter->updateVisualParams();
+
+ if (rightEyeBlinkMorph == 1.f)
+ {
+ mEyesClosed = TRUE;
+ mEyeBlinkTime = EYE_BLINK_CLOSE_TIME;
+ mEyeBlinkTimer.reset();
+ }
+ }
+ else if (mEyesClosed)
+ {
+ if (mEyeBlinkTimer.getElapsedTimeF32() >= mEyeBlinkTime)
+ {
+ F32 leftEyeBlinkMorph = mEyeBlinkTimer.getElapsedTimeF32() - mEyeBlinkTime;
+ F32 rightEyeBlinkMorph = leftEyeBlinkMorph - EYE_BLINK_TIME_DELTA;
+
+ leftEyeBlinkMorph = 1.f - llclamp(leftEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ rightEyeBlinkMorph = 1.f - llclamp(rightEyeBlinkMorph / EYE_BLINK_SPEED, 0.f, 1.f);
+ mCharacter->setVisualParamWeight("Blink_Left", leftEyeBlinkMorph);
+ mCharacter->setVisualParamWeight("Blink_Right", rightEyeBlinkMorph);
+ mCharacter->updateVisualParams();
+
+ if (rightEyeBlinkMorph == 0.f)
+ {
+ mEyesClosed = FALSE;
+ mEyeBlinkTime = EYE_BLINK_MIN_TIME + frand(EYE_BLINK_MAX_TIME - EYE_BLINK_MIN_TIME);
+ mEyeBlinkTimer.reset();
+ }
+ }
+ }
+
+ BOOL has_eye_target = FALSE;
+ LLVector3* targetPos = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ if (targetPos)
+ {
+ LLVector3 skyward(0.f, 0.f, 1.f);
+ LLVector3 left;
+ LLVector3 up;
+
+ eye_look_at = *targetPos;
+ F32 lookAtDistance = eye_look_at.normVec();
+
+ left.setVec(skyward % eye_look_at);
+ up.setVec(eye_look_at % left);
+
+ target_eye_rot = LLQuaternion(eye_look_at, left, up);
+
+ // calculate vergence
+ F32 interocular_dist = (mLeftEyeState.getJoint()->getWorldPosition() - mRightEyeState.getJoint()->getWorldPosition()).magVec();
+ vergence = -atan2((interocular_dist / 2.f), lookAtDistance);
+ llclamp(vergence, -F_PI_BY_TWO, 0.f);
+ }
+ else
+ {
+ target_eye_rot = mHeadJoint->getWorldRotation();
+ vergence = 0.f;
+ }
+
+ //RN: subtract 4 degrees to account for foveal angular offset relative to pupil
+ vergence += 4.f * DEG_TO_RAD;
+
+ // calculate eye jitter
+ LLQuaternion eye_jitter_rot;
+
+ // vergence not too high...
+ if (vergence > -0.05f)
+ {
+ //...go ahead and jitter
+ eye_jitter_rot.setQuat(0.f, mEyeJitterPitch + mEyeLookAwayPitch, mEyeJitterYaw + mEyeLookAwayYaw);
+ }
+ else
+ {
+ //...or don't
+ eye_jitter_rot.loadIdentity();
+ }
+
+ // calculate vergence of eyes as an object gets closer to the avatar's head
+ LLQuaternion vergence_quat;
+
+ if (has_eye_target)
+ {
+ vergence_quat.setQuat(vergence, LLVector3(0.f, 0.f, 1.f));
+ }
+ else
+ {
+ vergence_quat.loadIdentity();
+ }
+
+ // calculate eye rotations
+ LLQuaternion left_eye_rot = target_eye_rot;
+ left_eye_rot = vergence_quat * eye_jitter_rot * left_eye_rot;
+
+ LLQuaternion right_eye_rot = target_eye_rot;
+ vergence_quat.transQuat();
+ right_eye_rot = vergence_quat * eye_jitter_rot * right_eye_rot;
+
+ //set final eye rotations
+ // start with left
+ LLQuaternion tQw = mLeftEyeState.getJoint()->getParent()->getWorldRotation();
+ LLQuaternion tQh = left_eye_rot * ~tQw;
+ tQh.constrain(EYE_ROT_LIMIT_ANGLE);
+ mLeftEyeState.setRotation( tQh );
+
+ // now do right eye
+ tQw = mRightEyeState.getJoint()->getParent()->getWorldRotation();
+ tQh = right_eye_rot * ~tQw;
+ tQh.constrain(EYE_ROT_LIMIT_ANGLE);
+ mRightEyeState.setRotation( tQh );
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEyeMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEyeMotion::onDeactivate()
+{
+ LLJoint* joint = mLeftEyeState.getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
+
+ joint = mRightEyeState.getJoint();
+ if (joint)
+ {
+ joint->setRotation(LLQuaternion::DEFAULT);
+ }
+}
+
+// End
diff --git a/indra/llcharacter/llheadrotmotion.h b/indra/llcharacter/llheadrotmotion.h
new file mode 100644
index 0000000000..dc327cf44b
--- /dev/null
+++ b/indra/llcharacter/llheadrotmotion.h
@@ -0,0 +1,194 @@
+/**
+ * @file llheadrotmotion.h
+ * @brief Implementation of LLHeadRotMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHEADROTMOTION_H
+#define LL_LLHEADROTMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "llframetimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_HEAD_ROT 500.f;
+#define MIN_REQUIRED_PIXEL_AREA_EYE 25000.f;
+
+//-----------------------------------------------------------------------------
+// class LLHeadRotMotion
+//-----------------------------------------------------------------------------
+class LLHeadRotMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLHeadRotMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLHeadRotMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLHeadRotMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 1.f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 1.f; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_HEAD_ROT; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ LLJoint *mTorsoJoint;
+ LLJoint *mHeadJoint;
+ LLJoint *mRootJoint;
+ LLJoint *mPelvisJoint;
+
+ LLJointState mTorsoState;
+ LLJointState mNeckState;
+ LLJointState mHeadState;
+
+ LLQuaternion mLastHeadRot;
+};
+
+//-----------------------------------------------------------------------------
+// class LLEyeMotion
+//-----------------------------------------------------------------------------
+class LLEyeMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEyeMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEyeMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create( const LLUUID &id) { return new LLEyeMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.5f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.5f; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EYE; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ LLJoint *mHeadJoint;
+ LLJointState mLeftEyeState;
+ LLJointState mRightEyeState;
+
+ LLFrameTimer mEyeJitterTimer;
+ F32 mEyeJitterTime;
+ F32 mEyeJitterYaw;
+ F32 mEyeJitterPitch;
+ F32 mEyeLookAwayTime;
+ F32 mEyeLookAwayYaw;
+ F32 mEyeLookAwayPitch;
+
+ // eye blinking
+ LLFrameTimer mEyeBlinkTimer;
+ F32 mEyeBlinkTime;
+ BOOL mEyesClosed;
+};
+
+#endif // LL_LLHEADROTMOTION_H
+
diff --git a/indra/llcharacter/lljoint.cpp b/indra/llcharacter/lljoint.cpp
new file mode 100644
index 0000000000..3924c06adc
--- /dev/null
+++ b/indra/llcharacter/lljoint.cpp
@@ -0,0 +1,504 @@
+/**
+ * @file lljoint.cpp
+ * @brief Implementation of LLJoint class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lljoint.h"
+
+#include "llmath.h"
+
+S32 LLJoint::sNumUpdates = 0;
+S32 LLJoint::sNumTouches = 0;
+
+//-----------------------------------------------------------------------------
+// LLJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLJoint::LLJoint()
+{
+ mName = "unnamed";
+ mParent = NULL;
+ mXform.setScaleChildOffset(TRUE);
+ mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
+ mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
+ mUpdateXform = TRUE;
+ mJointNum = -1;
+ touch();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLJoint::LLJoint(const std::string &name, LLJoint *parent)
+{
+ mName = "unnamed";
+ mParent = NULL;
+ mXform.setScaleChildOffset(TRUE);
+ mXform.setScale(LLVector3(1.0f, 1.0f, 1.0f));
+ mDirtyFlags = MATRIX_DIRTY | ROTATION_DIRTY | POSITION_DIRTY;
+ mJointNum = 0;
+
+ setName(name);
+ if (parent)
+ parent->addChild( this );
+
+ touch();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLJoint()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLJoint::~LLJoint()
+{
+ removeAllChildren();
+}
+
+
+//-----------------------------------------------------------------------------
+// setup()
+//-----------------------------------------------------------------------------
+void LLJoint::setup(const std::string &name, LLJoint *parent)
+{
+ setName(name);
+ if (parent)
+ parent->addChild( this );
+}
+
+//-----------------------------------------------------------------------------
+// touch()
+// Sets all dirty flags for all children, recursively.
+//-----------------------------------------------------------------------------
+void LLJoint::touch(U32 flags)
+{
+ if ((flags | mDirtyFlags) != mDirtyFlags)
+ {
+ sNumTouches++;
+ mDirtyFlags |= flags;
+ U32 child_flags = flags;
+ if (flags & ROTATION_DIRTY)
+ {
+ child_flags |= POSITION_DIRTY;
+ }
+
+ for ( LLJoint *joint = mChildren.getFirstData();
+ joint != NULL;
+ joint = mChildren.getNextData() )
+ {
+ joint->touch(child_flags);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getRoot()
+//-----------------------------------------------------------------------------
+LLJoint *LLJoint::getRoot()
+{
+ if ( getParent() == NULL )
+ {
+ return this;
+ }
+ return getParent()->getRoot();
+}
+
+
+//-----------------------------------------------------------------------------
+// findJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLJoint::findJoint( const std::string &name )
+{
+ if (name == getName())
+ return this;
+
+ for ( LLJoint *j = mChildren.getFirstData();
+ j != NULL;
+ j = mChildren.getNextData() )
+ {
+ LLJoint *found = j->findJoint(name);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------
+// addChild()
+//--------------------------------------------------------------------
+void LLJoint::addChild(LLJoint *joint)
+{
+ if (joint->mParent)
+ joint->mParent->removeChild(joint);
+
+ mChildren.addDataAtEnd(joint);
+ joint->mXform.setParent(&mXform);
+ joint->mParent = this;
+ joint->touch();
+}
+
+
+//--------------------------------------------------------------------
+// removeChild()
+//--------------------------------------------------------------------
+void LLJoint::removeChild(LLJoint *joint)
+{
+ this->mChildren.removeData(joint);
+ joint->mXform.setParent(NULL);
+ joint->mParent = NULL;
+ joint->touch();
+}
+
+
+//--------------------------------------------------------------------
+// removeAllChildren()
+//--------------------------------------------------------------------
+void LLJoint::removeAllChildren()
+{
+ for ( LLJoint *joint = mChildren.getFirstData();
+ joint != NULL;
+ joint = mChildren.getNextData() )
+ {
+ removeChild(joint);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// getPosition()
+//--------------------------------------------------------------------
+const LLVector3& LLJoint::getPosition()
+{
+ return mXform.getPosition();
+}
+
+
+//--------------------------------------------------------------------
+// setPosition()
+//--------------------------------------------------------------------
+void LLJoint::setPosition( const LLVector3& pos )
+{
+ mXform.setPosition(pos);
+ touch(MATRIX_DIRTY | POSITION_DIRTY);
+}
+
+
+//--------------------------------------------------------------------
+// getWorldPosition()
+//--------------------------------------------------------------------
+LLVector3 LLJoint::getWorldPosition()
+{
+ updateWorldPRSParent();
+ return mXform.getWorldPosition();
+}
+
+//-----------------------------------------------------------------------------
+// getLastWorldPosition()
+//-----------------------------------------------------------------------------
+LLVector3 LLJoint::getLastWorldPosition()
+{
+ return mXform.getWorldPosition();
+}
+
+
+//--------------------------------------------------------------------
+// setWorldPosition()
+//--------------------------------------------------------------------
+void LLJoint::setWorldPosition( const LLVector3& pos )
+{
+ if (mParent == NULL)
+ {
+ this->setPosition( pos );
+ return;
+ }
+
+ LLMatrix4 temp_matrix = getWorldMatrix();
+ temp_matrix.mMatrix[VW][VX] = pos.mV[VX];
+ temp_matrix.mMatrix[VW][VY] = pos.mV[VY];
+ temp_matrix.mMatrix[VW][VZ] = pos.mV[VZ];
+
+ LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
+ LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();
+
+ temp_matrix *= invParentWorldMatrix;
+
+ LLVector3 localPos( temp_matrix.mMatrix[VW][VX],
+ temp_matrix.mMatrix[VW][VY],
+ temp_matrix.mMatrix[VW][VZ] );
+
+ setPosition( localPos );
+}
+
+
+//--------------------------------------------------------------------
+// mXform.getRotation()
+//--------------------------------------------------------------------
+const LLQuaternion& LLJoint::getRotation()
+{
+ return mXform.getRotation();
+}
+
+
+//--------------------------------------------------------------------
+// setRotation()
+//--------------------------------------------------------------------
+void LLJoint::setRotation( const LLQuaternion& rot )
+{
+ if (rot.isFinite())
+ {
+ mXform.setRotation(rot);
+ touch(MATRIX_DIRTY | ROTATION_DIRTY);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// getWorldRotation()
+//--------------------------------------------------------------------
+LLQuaternion LLJoint::getWorldRotation()
+{
+ updateWorldPRSParent();
+
+ return mXform.getWorldRotation();
+}
+
+//-----------------------------------------------------------------------------
+// getLastWorldRotation()
+//-----------------------------------------------------------------------------
+LLQuaternion LLJoint::getLastWorldRotation()
+{
+ return mXform.getWorldRotation();
+}
+
+//--------------------------------------------------------------------
+// setWorldRotation()
+//--------------------------------------------------------------------
+void LLJoint::setWorldRotation( const LLQuaternion& rot )
+{
+ if (mParent == NULL)
+ {
+ this->setRotation( rot );
+ return;
+ }
+
+ LLMatrix4 temp_mat(rot);
+
+ LLMatrix4 parentWorldMatrix = mParent->getWorldMatrix();
+ parentWorldMatrix.mMatrix[VW][VX] = 0;
+ parentWorldMatrix.mMatrix[VW][VY] = 0;
+ parentWorldMatrix.mMatrix[VW][VZ] = 0;
+
+ LLMatrix4 invParentWorldMatrix = parentWorldMatrix.invert();
+
+ temp_mat *= invParentWorldMatrix;
+
+ setRotation(LLQuaternion(temp_mat));
+}
+
+
+//--------------------------------------------------------------------
+// getScale()
+//--------------------------------------------------------------------
+const LLVector3& LLJoint::getScale()
+{
+ return mXform.getScale();
+}
+
+//--------------------------------------------------------------------
+// setScale()
+//--------------------------------------------------------------------
+void LLJoint::setScale( const LLVector3& scale )
+{
+ mXform.setScale(scale);
+ touch();
+}
+
+
+
+//--------------------------------------------------------------------
+// getWorldMatrix()
+//--------------------------------------------------------------------
+const LLMatrix4 &LLJoint::getWorldMatrix()
+{
+ updateWorldMatrixParent();
+
+ return mXform.getWorldMatrix();
+}
+
+
+//--------------------------------------------------------------------
+// setWorldMatrix()
+//--------------------------------------------------------------------
+void LLJoint::setWorldMatrix( const LLMatrix4& mat )
+{
+llinfos << "WARNING: LLJoint::setWorldMatrix() not correctly implemented yet" << llendl;
+ // extract global translation
+ LLVector3 trans( mat.mMatrix[VW][VX],
+ mat.mMatrix[VW][VY],
+ mat.mMatrix[VW][VZ] );
+
+ // extract global rotation
+ LLQuaternion rot( mat );
+
+ setWorldPosition( trans );
+ setWorldRotation( rot );
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrixParent()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrixParent()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ LLJoint *parent = getParent();
+ if (parent)
+ {
+ parent->updateWorldMatrixParent();
+ }
+ updateWorldMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldPRSParent()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldPRSParent()
+{
+ if (mDirtyFlags & (ROTATION_DIRTY | POSITION_DIRTY))
+ {
+ LLJoint *parent = getParent();
+ if (parent)
+ {
+ parent->updateWorldPRSParent();
+ }
+
+ mXform.update();
+ mDirtyFlags &= ~(ROTATION_DIRTY | POSITION_DIRTY);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrixChildren()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrixChildren()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ updateWorldMatrix();
+ }
+ for (LLJoint *child = mChildren.getFirstData(); child; child = mChildren.getNextData())
+ {
+ child->updateWorldMatrixChildren();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateWorldMatrix()
+//-----------------------------------------------------------------------------
+void LLJoint::updateWorldMatrix()
+{
+ if (mDirtyFlags & MATRIX_DIRTY)
+ {
+ sNumUpdates++;
+ mXform.updateMatrix(FALSE);
+ mDirtyFlags = 0x0;
+ }
+}
+
+//--------------------------------------------------------------------
+// getSkinOffset()
+//--------------------------------------------------------------------
+const LLVector3 &LLJoint::getSkinOffset()
+{
+ return mSkinOffset;
+}
+
+
+//--------------------------------------------------------------------
+// setSkinOffset()
+//--------------------------------------------------------------------
+void LLJoint::setSkinOffset( const LLVector3& offset )
+{
+ mSkinOffset = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setConstraintSilhouette()
+//-----------------------------------------------------------------------------
+void LLJoint::setConstraintSilhouette(LLDynamicArray<LLVector3>& silhouette)
+{
+ S32 i;
+
+ mConstraintSilhouette.reset();
+ for (i = 0; i < silhouette.count(); i++)
+ {
+ if (i % 2 == 1)
+ {
+ // skip normals
+ continue;
+ }
+ mConstraintSilhouette[i / 2] = silhouette[i];
+ }
+ LLQuaternion inv_parent_rotation = LLQuaternion::DEFAULT;
+
+ if (getParent())
+ {
+ inv_parent_rotation = ~getParent()->getWorldRotation();
+ }
+
+ for (i = 0; i < mConstraintSilhouette.count(); i++)
+ {
+ LLVector3 vert = mConstraintSilhouette[i];
+
+ vert -= getWorldPosition();
+ vert.normVec();
+ vert = vert * inv_parent_rotation;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clampRotation()
+//-----------------------------------------------------------------------------
+void LLJoint::clampRotation(LLQuaternion old_rot, LLQuaternion new_rot)
+{
+ LLVector3 main_axis(1.f, 0.f, 0.f);
+
+ for (LLJoint* joint = mChildren.getFirstData(); joint; joint = mChildren.getNextData())
+ {
+ if (joint->isAnimatable())
+ {
+ main_axis = joint->getPosition();
+ main_axis.normVec();
+ // only care about first animatable child
+ break;
+ }
+ }
+
+ // 2003.03.26 - This code was just using up cpu cycles. AB
+
+// LLVector3 old_axis = main_axis * old_rot;
+// LLVector3 new_axis = main_axis * new_rot;
+
+// for (S32 i = 0; i < mConstraintSilhouette.count() - 1; i++)
+// {
+// LLVector3 vert1 = mConstraintSilhouette[i];
+// LLVector3 vert2 = mConstraintSilhouette[i + 1];
+
+ // figure out how to clamp rotation to line on 3-sphere
+
+// }
+}
+
+// End
diff --git a/indra/llcharacter/lljoint.h b/indra/llcharacter/lljoint.h
new file mode 100644
index 0000000000..2fc86e87df
--- /dev/null
+++ b/indra/llcharacter/lljoint.h
@@ -0,0 +1,163 @@
+/**
+ * @file lljoint.h
+ * @brief Implementation of LLJoint class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINT_H
+#define LL_LLJOINT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "linked_lists.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "lldarray.h"
+
+const S32 LL_CHARACTER_MAX_JOINTS_PER_MESH = 15;
+const U32 LL_CHARACTER_MAX_JOINTS = 32; // must be divisible by 4!
+const U32 LL_HAND_JOINT_NUM = 31;
+const U32 LL_FACE_JOINT_NUM = 30;
+const S32 LL_CHARACTER_MAX_PRIORITY = 7;
+const F32 LL_MAX_PELVIS_OFFSET = 5.f;
+
+//-----------------------------------------------------------------------------
+// class LLJoint
+//-----------------------------------------------------------------------------
+class LLJoint
+{
+public:
+ // priority levels, from highest to lowest
+ enum JointPriority
+ {
+ USE_MOTION_PRIORITY = -1,
+ LOW_PRIORITY = 0,
+ MEDIUM_PRIORITY,
+ HIGH_PRIORITY,
+ HIGHER_PRIORITY,
+ HIGHEST_PRIORITY,
+ ADDITIVE_PRIORITY = LL_CHARACTER_MAX_PRIORITY
+ };
+
+ enum DirtyFlags
+ {
+ MATRIX_DIRTY = 0x1 << 0,
+ ROTATION_DIRTY = 0x1 << 1,
+ POSITION_DIRTY = 0x1 << 2,
+ ALL_DIRTY = 0x7
+ };
+protected:
+ std::string mName;
+
+ // parent joint
+ LLJoint *mParent;
+
+ // explicit transformation members
+ LLXformMatrix mXform;
+
+public:
+ U32 mDirtyFlags;
+ BOOL mWorldRotationDirty;
+ BOOL mUpdateXform;
+
+ // describes the skin binding pose
+ LLVector3 mSkinOffset;
+
+ S32 mJointNum;
+
+ LLDynamicArray<LLVector3> mConstraintSilhouette;
+
+ // child joints
+ LLLinkedList<LLJoint> mChildren;
+
+ // debug statics
+ static S32 sNumTouches;
+ static S32 sNumUpdates;
+
+public:
+ LLJoint();
+ LLJoint( const std::string &name, LLJoint *parent=NULL );
+
+ virtual ~LLJoint();
+
+ // set name and parent
+ void setup( const std::string &name, LLJoint *parent=NULL );
+
+ void touch(U32 flags = ALL_DIRTY);
+
+ // get/set name
+ const std::string &getName() { return mName; }
+ void setName( const std::string &name ) { mName = name; }
+
+ // getParent
+ LLJoint *getParent() { return mParent; }
+
+ // getRoot
+ LLJoint *getRoot();
+
+ // search for child joints by name
+ LLJoint *findJoint( const std::string &name );
+
+ // add/remove children
+ void addChild( LLJoint *joint );
+ void removeChild( LLJoint *joint );
+ void removeAllChildren();
+
+ // get/set local position
+ const LLVector3& getPosition();
+ void setPosition( const LLVector3& pos );
+
+ // get/set world position
+ LLVector3 getWorldPosition();
+ LLVector3 getLastWorldPosition();
+ void setWorldPosition( const LLVector3& pos );
+
+ // get/set local rotation
+ const LLQuaternion& getRotation();
+ void setRotation( const LLQuaternion& rot );
+
+ // get/set world rotation
+ LLQuaternion getWorldRotation();
+ LLQuaternion getLastWorldRotation();
+ void setWorldRotation( const LLQuaternion& rot );
+
+ // get/set local scale
+ const LLVector3& getScale();
+ void setScale( const LLVector3& scale );
+
+ // get/set world matrix
+ const LLMatrix4 &getWorldMatrix();
+ void setWorldMatrix( const LLMatrix4& mat );
+
+ void updateWorldMatrixChildren();
+ void updateWorldMatrixParent();
+
+ void updateWorldPRSParent();
+
+ void updateWorldMatrix();
+
+ // get/set skin offset
+ const LLVector3 &getSkinOffset();
+ void setSkinOffset( const LLVector3 &offset);
+
+ LLXformMatrix *getXform() { return &mXform; }
+
+ void setConstraintSilhouette(LLDynamicArray<LLVector3>& silhouette);
+
+ void clampRotation(LLQuaternion old_rot, LLQuaternion new_rot);
+
+ virtual BOOL isAnimatable() { return TRUE; }
+
+ S32 getJointNum() { return mJointNum; }
+ void setJointNum(S32 joint_num) { mJointNum = joint_num; }
+};
+#endif // LL_LLJOINT_H
+
diff --git a/indra/llcharacter/lljointsolverrp3.cpp b/indra/llcharacter/lljointsolverrp3.cpp
new file mode 100644
index 0000000000..60b836734a
--- /dev/null
+++ b/indra/llcharacter/lljointsolverrp3.cpp
@@ -0,0 +1,366 @@
+/**
+ * @file lljointsolverrp3.cpp
+ * @brief Implementation of LLJointSolverRP3 class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lljointsolverrp3.h"
+
+#include <math.h>
+
+#include "llmath.h"
+
+#define F_EPSILON 0.00001f
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+LLJointSolverRP3::LLJointSolverRP3()
+{
+ mJointA = NULL;
+ mJointB = NULL;
+ mJointC = NULL;
+ mJointGoal = NULL;
+ mLengthAB = 1.0f;
+ mLengthBC = 1.0f;
+ mPoleVector.setVec( 1.0f, 0.0f, 0.0f );
+ mbUseBAxis = FALSE;
+ mTwist = 0.0f;
+ mFirstTime = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+/*virtual*/ LLJointSolverRP3::~LLJointSolverRP3()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// setupJoints()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setupJoints( LLJoint* jointA,
+ LLJoint* jointB,
+ LLJoint* jointC,
+ LLJoint* jointGoal )
+{
+ mJointA = jointA;
+ mJointB = jointB;
+ mJointC = jointC;
+ mJointGoal = jointGoal;
+
+ mLengthAB = mJointB->getPosition().magVec();
+ mLengthBC = mJointC->getPosition().magVec();
+
+ mJointABaseRotation = jointA->getRotation();
+ mJointBBaseRotation = jointB->getRotation();
+}
+
+
+//-----------------------------------------------------------------------------
+// getPoleVector()
+//-----------------------------------------------------------------------------
+const LLVector3& LLJointSolverRP3::getPoleVector()
+{
+ return mPoleVector;
+}
+
+
+//-----------------------------------------------------------------------------
+// setPoleVector()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setPoleVector( const LLVector3& poleVector )
+{
+ mPoleVector = poleVector;
+ mPoleVector.normVec();
+}
+
+
+//-----------------------------------------------------------------------------
+// setPoleVector()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setBAxis( const LLVector3& bAxis )
+{
+ mBAxis = bAxis;
+ mBAxis.normVec();
+ mbUseBAxis = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getTwist()
+//-----------------------------------------------------------------------------
+F32 LLJointSolverRP3::getTwist()
+{
+ return mTwist;
+}
+
+
+//-----------------------------------------------------------------------------
+// setTwist()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::setTwist( F32 twist )
+{
+ mTwist = twist;
+}
+
+
+//-----------------------------------------------------------------------------
+// solve()
+//-----------------------------------------------------------------------------
+void LLJointSolverRP3::solve()
+{
+// llinfos << llendl;
+// llinfos << "LLJointSolverRP3::solve()" << llendl;
+
+ //-------------------------------------------------------------------------
+ // setup joints in their base rotations
+ //-------------------------------------------------------------------------
+ mJointA->setRotation( mJointABaseRotation );
+ mJointB->setRotation( mJointBBaseRotation );
+
+ //-------------------------------------------------------------------------
+ // get joint positions in world space
+ //-------------------------------------------------------------------------
+ LLVector3 aPos = mJointA->getWorldPosition();
+ LLVector3 bPos = mJointB->getWorldPosition();
+ LLVector3 cPos = mJointC->getWorldPosition();
+ LLVector3 gPos = mJointGoal->getWorldPosition();
+
+// llinfos << "bPosLocal = " << mJointB->getPosition() << llendl;
+// llinfos << "cPosLocal = " << mJointC->getPosition() << llendl;
+// llinfos << "bRotLocal = " << mJointB->getRotation() << llendl;
+// llinfos << "cRotLocal = " << mJointC->getRotation() << llendl;
+
+// llinfos << "aPos : " << aPos << llendl;
+// llinfos << "bPos : " << bPos << llendl;
+// llinfos << "cPos : " << cPos << llendl;
+// llinfos << "gPos : " << gPos << llendl;
+
+ //-------------------------------------------------------------------------
+ // get the poleVector in world space
+ //-------------------------------------------------------------------------
+ LLMatrix4 worldJointAParentMat;
+ if ( mJointA->getParent() )
+ {
+ worldJointAParentMat = mJointA->getParent()->getWorldMatrix();
+ }
+ LLVector3 poleVec = rotate_vector( mPoleVector, worldJointAParentMat );
+
+ //-------------------------------------------------------------------------
+ // compute the following:
+ // vector from A to B
+ // vector from B to C
+ // vector from A to C
+ // vector from A to G (goal)
+ //-------------------------------------------------------------------------
+ LLVector3 abVec = bPos - aPos;
+ LLVector3 bcVec = cPos - bPos;
+ LLVector3 acVec = cPos - aPos;
+ LLVector3 agVec = gPos - aPos;
+
+// llinfos << "abVec : " << abVec << llendl;
+// llinfos << "bcVec : " << bcVec << llendl;
+// llinfos << "acVec : " << acVec << llendl;
+// llinfos << "agVec : " << agVec << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute needed lengths of those vectors
+ //-------------------------------------------------------------------------
+ F32 abLen = abVec.magVec();
+ F32 bcLen = bcVec.magVec();
+ F32 agLen = agVec.magVec();
+
+// llinfos << "abLen : " << abLen << llendl;
+// llinfos << "bcLen : " << bcLen << llendl;
+// llinfos << "agLen : " << agLen << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute component vector of (A->B) orthogonal to (A->C)
+ //-------------------------------------------------------------------------
+ LLVector3 abacCompOrthoVec = abVec - acVec * ((abVec * acVec)/(acVec * acVec));
+
+// llinfos << "abacCompOrthoVec : " << abacCompOrthoVec << llendl
+
+ //-------------------------------------------------------------------------
+ // compute the normal of the original ABC plane (and store for later)
+ //-------------------------------------------------------------------------
+ LLVector3 abcNorm;
+ if (!mbUseBAxis)
+ {
+ if( are_parallel(abVec, bcVec, 0.001f) )
+ {
+ // the current solution is maxed out, so we use the axis that is
+ // orthogonal to both poleVec and A->B
+ if ( are_parallel(poleVec, abVec, 0.001f) )
+ {
+ // ACK! the problem is singular
+ if ( are_parallel(poleVec, agVec, 0.001f) )
+ {
+ // the solutions is also singular
+ return;
+ }
+ else
+ {
+ abcNorm = poleVec % agVec;
+ }
+ }
+ else
+ {
+ abcNorm = poleVec % abVec;
+ }
+ }
+ else
+ {
+ abcNorm = abVec % bcVec;
+ }
+ }
+ else
+ {
+ abcNorm = mBAxis * mJointB->getWorldRotation();
+ }
+
+ //-------------------------------------------------------------------------
+ // compute rotation of B
+ //-------------------------------------------------------------------------
+ // angle between A->B and B->C
+ F32 abbcAng = angle_between(abVec, bcVec);
+
+ // vector orthogonal to A->B and B->C
+ LLVector3 abbcOrthoVec = abVec % bcVec;
+ if (abbcOrthoVec.magVecSquared() < 0.001f)
+ {
+ abbcOrthoVec = poleVec % abVec;
+ abacCompOrthoVec = poleVec;
+ }
+ abbcOrthoVec.normVec();
+
+ F32 agLenSq = agLen * agLen;
+
+ // angle arm for extension
+ F32 cosTheta = (agLenSq - abLen*abLen - bcLen*bcLen) / (2.0f * abLen * bcLen);
+ if (cosTheta > 1.0f)
+ cosTheta = 1.0f;
+ else if (cosTheta < -1.0f)
+ cosTheta = -1.0f;
+
+ F32 theta = acos(cosTheta);
+
+ LLQuaternion bRot(theta - abbcAng, abbcOrthoVec);
+
+// llinfos << "abbcAng : " << abbcAng << llendl;
+// llinfos << "abbcOrthoVec : " << abbcOrthoVec << llendl;
+// llinfos << "agLenSq : " << agLenSq << llendl;
+// llinfos << "cosTheta : " << cosTheta << llendl;
+// llinfos << "theta : " << theta << llendl;
+// llinfos << "bRot : " << bRot << llendl;
+// llinfos << "theta abbcAng theta-abbcAng: " << theta*180.0/F_PI << " " << abbcAng*180.0f/F_PI << " " << (theta - abbcAng)*180.0f/F_PI << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute rotation that rotates new A->C to A->G
+ //-------------------------------------------------------------------------
+ // rotate B->C by bRot
+ bcVec = bcVec * bRot;
+
+ // update A->C
+ acVec = abVec + bcVec;
+
+ LLQuaternion cgRot;
+ cgRot.shortestArc( acVec, agVec );
+
+// llinfos << "bcVec : " << bcVec << llendl;
+// llinfos << "acVec : " << acVec << llendl;
+// llinfos << "cgRot : " << cgRot << llendl;
+
+ // update A->B and B->C with rotation from C to G
+ abVec = abVec * cgRot;
+ bcVec = bcVec * cgRot;
+ abcNorm = abcNorm * cgRot;
+ acVec = abVec + bcVec;
+
+ //-------------------------------------------------------------------------
+ // compute the normal of the APG plane
+ //-------------------------------------------------------------------------
+ if (are_parallel(agVec, poleVec, 0.001f))
+ {
+ // the solution plane is undefined ==> we're done
+ return;
+ }
+ LLVector3 apgNorm = poleVec % agVec;
+ apgNorm.normVec();
+
+ if (!mbUseBAxis)
+ {
+ //---------------------------------------------------------------------
+ // compute the normal of the new ABC plane
+ // (only necessary if we're NOT using mBAxis)
+ //---------------------------------------------------------------------
+ if( are_parallel(abVec, bcVec, 0.001f) )
+ {
+ // G is either too close or too far away
+ // we'll use the old ABCnormal
+ }
+ else
+ {
+ abcNorm = abVec % bcVec;
+ }
+ abcNorm.normVec();
+ }
+
+ //-------------------------------------------------------------------------
+ // calcuate plane rotation
+ //-------------------------------------------------------------------------
+ LLQuaternion pRot;
+ if ( are_parallel( abcNorm, apgNorm, 0.001f) )
+ {
+ if (abcNorm * apgNorm < 0.0f)
+ {
+ // we must be PI radians off ==> rotate by PI around agVec
+ pRot.setQuat(F_PI, agVec);
+ }
+ else
+ {
+ // we're done
+ }
+ }
+ else
+ {
+ pRot.shortestArc( abcNorm, apgNorm );
+ }
+
+// llinfos << "abcNorm = " << abcNorm << llendl;
+// llinfos << "apgNorm = " << apgNorm << llendl;
+// llinfos << "pRot = " << pRot << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute twist rotation
+ //-------------------------------------------------------------------------
+ LLQuaternion twistRot( mTwist, agVec );
+
+// llinfos << "twist : " << mTwist*180.0/F_PI << llendl;
+// llinfos << "agNormVec: " << agNormVec << llendl;
+// llinfos << "twistRot : " << twistRot << llendl;
+
+ //-------------------------------------------------------------------------
+ // compute rotation of A
+ //-------------------------------------------------------------------------
+ LLQuaternion aRot = cgRot * pRot * twistRot;
+
+ //-------------------------------------------------------------------------
+ // apply the rotations
+ //-------------------------------------------------------------------------
+ mJointB->setWorldRotation( mJointB->getWorldRotation() * bRot );
+ mJointA->setWorldRotation( mJointA->getWorldRotation() * aRot );
+}
+
+
+// End
diff --git a/indra/llcharacter/lljointsolverrp3.h b/indra/llcharacter/lljointsolverrp3.h
new file mode 100644
index 0000000000..d1507351b6
--- /dev/null
+++ b/indra/llcharacter/lljointsolverrp3.h
@@ -0,0 +1,158 @@
+/**
+ * @file lljointsolverrp3.h
+ * @brief Implementation of LLJointSolverRP3 class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINTSOLVERRP3_H
+#define LL_LLJOINTSOLVERRP3_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+
+/* -some compilers don't like line continuation chars-
+//-----------------------------------------------------------------------------
+// class LLJointSolverRP3
+//
+// This class is a "poor man's" IK for simple 3 joint kinematic chains.
+// It is modeled after the 'ikRPSolver' in Maya.
+// This class takes 4 LLJoints:
+// jointA
+// jointB
+// jointC
+// jointGoal
+//
+// Such that jointA is the parent of jointB, jointB is the parent of jointC.
+// When invoked, this class modifies the rotations of jointA and jointB such
+// that the position of the jointC attempts to reach the position of jointGoal.
+//
+// At object initialization time, the distances between jointA - jointB and
+// jointB - jointC are cached. During evaluation these bone lengths are
+// preserved.
+//
+// A A
+// | |
+// | |
+// B B---CG A---B---C...G
+// \
+// \
+// CG
+//
+//
+// In addition a "poleVector" is specified that does two things:
+//
+// a) defines the plane in which the solution occurs, thus
+// reducing an infinite number of solutions, down to 2.
+//
+// b) disambiguates the resulting two solutions as follows:
+//
+// A A A--->poleVector
+// | \ \
+// | \ \
+// B vs. B ==> B
+// \ | |
+// \ | |
+// CG CG CG
+//
+// A "twist" setting allows the solution plane to be rotated about the
+// line between A and C. A handy animation feature.
+//
+// For "smarter" results for non-coplanar limbs, specify the joints axis
+// of bend in the B's local frame (see setBAxis())
+//-----------------------------------------------------------------------------
+*/
+
+class LLJointSolverRP3
+{
+protected:
+ LLJoint *mJointA;
+ LLJoint *mJointB;
+ LLJoint *mJointC;
+ LLJoint *mJointGoal;
+
+ F32 mLengthAB;
+ F32 mLengthBC;
+
+ LLVector3 mPoleVector;
+ LLVector3 mBAxis;
+ BOOL mbUseBAxis;
+
+ F32 mTwist;
+
+ BOOL mFirstTime;
+ LLMatrix4 mSavedJointAMat;
+ LLMatrix4 mSavedInvPlaneMat;
+
+ LLQuaternion mJointABaseRotation;
+ LLQuaternion mJointBBaseRotation;
+
+public:
+ //-------------------------------------------------------------------------
+ // Constructor/Destructor
+ //-------------------------------------------------------------------------
+ LLJointSolverRP3();
+ virtual ~LLJointSolverRP3();
+
+ //-------------------------------------------------------------------------
+ // setupJoints()
+ // This must be called one time to setup the solver.
+ // This must be called AFTER the skeleton has been created, all parent/child
+ // relationships are established, and after the joints are placed in
+ // a valid configuration (as distances between them will be cached).
+ //-------------------------------------------------------------------------
+ void setupJoints( LLJoint* jointA,
+ LLJoint* jointB,
+ LLJoint* jointC,
+ LLJoint* jointGoal );
+
+ //-------------------------------------------------------------------------
+ // getPoleVector()
+ // Returns the current pole vector.
+ //-------------------------------------------------------------------------
+ const LLVector3& getPoleVector();
+
+ //-------------------------------------------------------------------------
+ // setPoleVector()
+ // Sets the pole vector.
+ // The pole vector is defined relative to (in the space of) jointA's parent.
+ // The default pole vector is (1,0,0), and this is used if this function
+ // is never called.
+ // This vector is normalized when set.
+ //-------------------------------------------------------------------------
+ void setPoleVector( const LLVector3& poleVector );
+
+ //-------------------------------------------------------------------------
+ // setBAxis()
+ // Sets the joint's axis in B's local frame, and enable "smarter" solve().
+ // This allows for smarter IK when for twisted limbs.
+ //-------------------------------------------------------------------------
+ void setBAxis( const LLVector3& bAxis );
+
+ //-------------------------------------------------------------------------
+ // getTwist()
+ // Returns the current twist in radians.
+ //-------------------------------------------------------------------------
+ F32 getTwist();
+
+ //-------------------------------------------------------------------------
+ // setTwist()
+ // Sets the twist value.
+ // The default is 0.0.
+ //-------------------------------------------------------------------------
+ void setTwist( F32 twist );
+
+ //-------------------------------------------------------------------------
+ // solve()
+ // This is the "work" function.
+ // When called, the rotations of jointA and jointB will be modified
+ // such that jointC attempts to reach jointGoal.
+ //-------------------------------------------------------------------------
+ void solve();
+};
+
+#endif // LL_LLJOINTSOLVERRP3_H
+
diff --git a/indra/llcharacter/lljointstate.h b/indra/llcharacter/lljointstate.h
new file mode 100644
index 0000000000..82a2b345b0
--- /dev/null
+++ b/indra/llcharacter/lljointstate.h
@@ -0,0 +1,106 @@
+/**
+ * @file lljointstate.h
+ * @brief Implementation of LLJointState class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOINTSTATE_H
+#define LL_LLJOINTSTATE_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+
+//-----------------------------------------------------------------------------
+// class LLJointState
+//-----------------------------------------------------------------------------
+class LLJointState
+{
+public:
+ enum BlendPhase
+ {
+ INACTIVE,
+ EASE_IN,
+ ACTIVE,
+ EASE_OUT
+ };
+protected:
+ // associated joint
+ LLJoint *mJoint;
+
+ // indicates which members are used
+ U32 mUsage;
+
+ // indicates weighted effect of this joint
+ F32 mWeight;
+
+ // transformation members
+ LLVector3 mPosition; // position relative to parent joint
+ LLQuaternion mRotation; // joint rotation relative to parent joint
+ LLVector3 mScale; // scale relative to rotated frame
+ LLJoint::JointPriority mPriority; // how important this joint state is relative to others
+public:
+ // Constructor
+ LLJointState()
+ {
+ mUsage = 0;
+ mJoint = NULL;
+ mUsage = 0;
+ mWeight = 0.f;
+ mPriority = LLJoint::USE_MOTION_PRIORITY;
+ }
+
+ LLJointState(LLJoint* joint)
+ {
+ mUsage = 0;
+ mJoint = joint;
+ mUsage = 0;
+ mWeight = 0.f;
+ mPriority = LLJoint::USE_MOTION_PRIORITY;
+ }
+
+ // Destructor
+ virtual ~LLJointState()
+ {
+ }
+
+ // joint that this state is applied to
+ LLJoint *getJoint() { return mJoint; }
+ BOOL setJoint( LLJoint *joint ) { mJoint = joint; return mJoint != NULL; }
+
+ // transform type (bitwise flags can be combined)
+ // Note that these are set automatically when various
+ // member setPos/setRot/setScale functions are called.
+ enum Usage
+ {
+ POS = 1,
+ ROT = 2,
+ SCALE = 4,
+ };
+ U32 getUsage() { return mUsage; }
+ void setUsage( U32 usage ) { mUsage = usage; }
+ F32 getWeight() { return mWeight; }
+ void setWeight( F32 weight ) { mWeight = weight; }
+
+ // get/set position
+ const LLVector3& getPosition() { return mPosition; }
+ void setPosition( const LLVector3& pos ) { llassert(mUsage & POS); mPosition = pos; }
+
+ // get/set rotation
+ const LLQuaternion& getRotation() { return mRotation; }
+ void setRotation( const LLQuaternion& rot ) { llassert(mUsage & ROT); mRotation = rot; }
+
+ // get/set scale
+ const LLVector3& getScale() { return mScale; }
+ void setScale( const LLVector3& scale ) { llassert(mUsage & SCALE); mScale = scale; }
+
+ // get/set priority
+ const LLJoint::JointPriority getPriority() { return mPriority; }
+ void setPriority( const LLJoint::JointPriority priority ) { mPriority = priority; }
+};
+
+#endif // LL_LLJOINTSTATE_H
+
diff --git a/indra/llcharacter/llkeyframefallmotion.cpp b/indra/llcharacter/llkeyframefallmotion.cpp
new file mode 100644
index 0000000000..248d22c22c
--- /dev/null
+++ b/indra/llcharacter/llkeyframefallmotion.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file llkeyframefallmotion.cpp
+ * @brief Implementation of LLKeyframeFallMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframefallmotion.h"
+#include "llcharacter.h"
+#include "m3math.h"
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GO_TO_KEY_POSE 1
+#define MIN_TRACK_SPEED 0.01f
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeFallMotion::LLKeyframeFallMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mVelocityZ = 0.f;
+ mCharacter = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeFallMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeFallMotion::~LLKeyframeFallMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeFallMotion::onInitialize(LLCharacter *character)
+{
+ // save character pointer for later use
+ mCharacter = character;
+
+ // load keyframe data, setup pose and joint states
+ LLMotion::LLMotionInitStatus result = LLKeyframeMotion::onInitialize(character);
+
+ for (U32 jm=0; jm<mJointMotionList->mNumJointMotions; jm++)
+ {
+ if (!mJointStates[jm].getJoint())
+ continue;
+ if (mJointStates[jm].getJoint()->getName() == std::string("mPelvis"))
+ {
+ mPelvisStatep = &mJointStates[jm];
+ }
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeFallMotion::onActivate()
+{
+ LLVector3 ground_pos;
+ LLVector3 ground_normal;
+ LLQuaternion inverse_pelvis_rot;
+ LLVector3 fwd_axis(1.f, 0.f, 0.f);
+
+ mVelocityZ = -mCharacter->getCharacterVelocity().mV[VZ];
+ mCharacter->getGround( mCharacter->getCharacterPosition(), ground_pos, ground_normal);
+ ground_normal.normVec();
+
+ inverse_pelvis_rot = mCharacter->getCharacterRotation();
+ inverse_pelvis_rot.transQuat();
+
+ // find ground normal in pelvis space
+ ground_normal = ground_normal * inverse_pelvis_rot;
+
+ // calculate new foward axis
+ fwd_axis = fwd_axis - (ground_normal * (ground_normal * fwd_axis));
+ fwd_axis.normVec();
+ mRotationToGroundNormal = LLQuaternion(fwd_axis, ground_normal % fwd_axis, ground_normal);
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeFallMotion::onUpdate(F32 activeTime, U8* joint_mask)
+{
+ BOOL result = LLKeyframeMotion::onUpdate(activeTime, joint_mask);
+ F32 slerp_amt = clamp_rescale(activeTime / getDuration(), 0.5f, 0.75f, 0.f, 1.f);
+
+ mPelvisStatep->setRotation(mPelvisStatep->getRotation() * slerp(slerp_amt, mRotationToGroundNormal, LLQuaternion()));
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeFallMotion::getEaseInDuration()
+//-----------------------------------------------------------------------------
+F32 LLKeyframeFallMotion::getEaseInDuration()
+{
+ if (mVelocityZ == 0.f)
+ {
+ // we've already hit the ground
+ return 0.4f;
+ }
+
+ return mCharacter->getPreferredPelvisHeight() / mVelocityZ;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframefallmotion.h b/indra/llcharacter/llkeyframefallmotion.h
new file mode 100644
index 0000000000..5cc15fd38b
--- /dev/null
+++ b/indra/llcharacter/llkeyframefallmotion.h
@@ -0,0 +1,60 @@
+/**
+ * @file llkeyframefallmotion.h
+ * @brief Implementation of LLKeframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKeyframeFallMotion_H
+#define LL_LLKeyframeFallMotion_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "llcharacter.h"
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeFallMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeFallMotion :
+ public LLKeyframeMotion
+{
+public:
+ // Constructor
+ LLKeyframeFallMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeFallMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeFallMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual F32 getEaseInDuration();
+ virtual BOOL onUpdate(F32 activeTime, U8* joint_mask);
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter* mCharacter;
+ F32 mVelocityZ;
+ LLJointState* mPelvisStatep;
+ LLQuaternion mRotationToGroundNormal;
+};
+
+#endif // LL_LLKeyframeFallMotion_H
+
diff --git a/indra/llcharacter/llkeyframemotion.cpp b/indra/llcharacter/llkeyframemotion.cpp
new file mode 100644
index 0000000000..4dd976d5f1
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotion.cpp
@@ -0,0 +1,2112 @@
+/**
+ * @file llkeyframemotion.cpp
+ * @brief Implementation of LLKeyframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmath.h"
+#include "llanimationstates.h"
+#include "llassetstorage.h"
+#include "lldatapacker.h"
+#include "llcharacter.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "llendianswizzle.h"
+#include "llkeyframemotion.h"
+#include "llquantize.h"
+#include "llvfile.h"
+#include "m3math.h"
+#include "message.h"
+
+//-----------------------------------------------------------------------------
+// Static Definitions
+//-----------------------------------------------------------------------------
+LLVFS* LLKeyframeMotion::sVFS = NULL;
+LLKeyframeDataCache::LLKeyframeDataMap LLKeyframeDataCache::sKeyframeDataMap;
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+static F32 JOINT_LENGTH_K = 0.7f;
+static S32 MAX_ITERATIONS = 20;
+static S32 MIN_ITERATIONS = 1;
+static S32 MIN_ITERATION_COUNT = 2;
+static F32 MAX_PIXEL_AREA_CONSTRAINTS = 80000.f;
+static F32 MIN_PIXEL_AREA_CONSTRAINTS = 1000.f;
+static F32 MIN_ACCELERATION_SQUARED = 0.0005f * 0.0005f;
+
+static F32 MAX_CONSTRAINTS = 10;
+
+//-----------------------------------------------------------------------------
+// JointMotionList::dumpDiagInfo()
+//-----------------------------------------------------------------------------
+U32 LLKeyframeMotion::JointMotionList::dumpDiagInfo()
+{
+ S32 total_size = sizeof(JointMotionList);
+
+ for (U32 i = 0; i < mNumJointMotions; i++)
+ {
+ LLKeyframeMotion::JointMotion* joint_motion_p = &mJointMotionArray[i];
+
+ llinfos << "\tJoint " << joint_motion_p->mJointName << llendl;
+ if (joint_motion_p->mUsage & LLJointState::SCALE)
+ {
+ llinfos << "\t" << joint_motion_p->mScaleCurve.mNumKeys << " scale keys at "
+ << joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mScaleCurve.mNumKeys * sizeof(ScaleKey);
+ }
+ if (joint_motion_p->mUsage & LLJointState::ROT)
+ {
+ llinfos << "\t" << joint_motion_p->mRotationCurve.mNumKeys << " rotation keys at "
+ << joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mRotationCurve.mNumKeys * sizeof(RotationKey);
+ }
+ if (joint_motion_p->mUsage & LLJointState::POS)
+ {
+ llinfos << "\t" << joint_motion_p->mPositionCurve.mNumKeys << " position keys at "
+ << joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey) << " bytes" << llendl;
+
+ total_size += joint_motion_p->mPositionCurve.mNumKeys * sizeof(PositionKey);
+ }
+ }
+ llinfos << "Size: " << total_size << " bytes" << llendl;
+
+ return total_size;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// ****Curve classes
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// ScaleCurve::ScaleCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::ScaleCurve::ScaleCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// ScaleCurve::~ScaleCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::ScaleCurve::~ScaleCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// getValue()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::ScaleCurve::getValue(F32 time, F32 duration)
+{
+ LLVector3 value;
+ F32 index_before, index_after;
+ ScaleKey* scale_before;
+ ScaleKey* scale_after;
+
+ mKeys.getInterval(time, index_before, index_after, scale_before, scale_after);
+ if (scale_before)
+ {
+ if (!scale_after)
+ {
+ scale_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = scale_after->mScale;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *scale_before, *scale_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (scale_after)
+ {
+ value = scale_after->mScale;
+ }
+ // no keys?
+ else
+ {
+ value.clearVec();
+ }
+ }
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::ScaleCurve::interp(F32 u, ScaleKey& before, ScaleKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mScale;
+
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return lerp(before.mScale, after.mScale, u);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::RotationCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::RotationCurve::RotationCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::~RotationCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::RotationCurve::~RotationCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// RotationCurve::getValue()
+//-----------------------------------------------------------------------------
+LLQuaternion LLKeyframeMotion::RotationCurve::getValue(F32 time, F32 duration)
+{
+ LLQuaternion value;
+ F32 index_before, index_after;
+ RotationKey* rot_before;
+ RotationKey* rot_after;
+
+ mKeys.getInterval(time, index_before, index_after, rot_before, rot_after);
+
+ if (rot_before)
+ {
+ if (!rot_after)
+ {
+ rot_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = rot_after->mRotation;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *rot_before, *rot_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (rot_after)
+ {
+ value = rot_after->mRotation;
+ }
+ // no keys?
+ else
+ {
+ value = LLQuaternion::DEFAULT;
+ }
+ }
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLQuaternion LLKeyframeMotion::RotationCurve::interp(F32 u, RotationKey& before, RotationKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mRotation;
+
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return nlerp(u, before.mRotation, after.mRotation);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// PositionCurve::PositionCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::PositionCurve::PositionCurve()
+{
+ mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// PositionCurve::~PositionCurve()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::PositionCurve::~PositionCurve()
+{
+ mKeys.deleteAllData();
+ mNumKeys = 0;
+}
+
+//-----------------------------------------------------------------------------
+// PositionCurve::getValue()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::PositionCurve::getValue(F32 time, F32 duration)
+{
+ LLVector3 value;
+ F32 index_before, index_after;
+ PositionKey* pos_before;
+ PositionKey* pos_after;
+
+ mKeys.getInterval(time, index_before, index_after, pos_before, pos_after);
+
+ if (pos_before)
+ {
+ if (!pos_after)
+ {
+ pos_after = &mLoopInKey;
+ index_after = duration;
+ }
+
+ if (index_after == index_before)
+ {
+ value = pos_after->mPosition;
+ }
+ else
+ {
+ F32 u = (time - index_before) / (index_after - index_before);
+ value = interp(u, *pos_before, *pos_after);
+ }
+ }
+ else
+ {
+ // before first key
+ if (pos_after)
+ {
+ value = pos_after->mPosition;
+ }
+ // no keys?
+ else
+ {
+ value.clearVec();
+ }
+ }
+
+ llassert(value.isFinite());
+
+ return value;
+}
+
+//-----------------------------------------------------------------------------
+// interp()
+//-----------------------------------------------------------------------------
+LLVector3 LLKeyframeMotion::PositionCurve::interp(F32 u, PositionKey& before, PositionKey& after)
+{
+ switch (mInterpolationType)
+ {
+ case IT_STEP:
+ return before.mPosition;
+ default:
+ case IT_LINEAR:
+ case IT_SPLINE:
+ return lerp(before.mPosition, after.mPosition, u);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// JointMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// JointMotion::update()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::JointMotion::update(LLJointState* joint_state, F32 time, F32 duration)
+{
+ // this value being 0 is the cause of https://jira.lindenlab.com/browse/SL-22678 but I haven't
+ // managed to get a stack to see how it got here. Testing for 0 here will stop the crash.
+ if ( joint_state == 0 )
+ {
+ return;
+ };
+
+ U32 usage = joint_state->getUsage();
+
+ //-------------------------------------------------------------------------
+ // update scale component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::SCALE) && mScaleCurve.mNumKeys)
+ {
+ joint_state->setScale( mScaleCurve.getValue( time, duration ) );
+ }
+
+ //-------------------------------------------------------------------------
+ // update rotation component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::ROT) && mRotationCurve.mNumKeys)
+ {
+ joint_state->setRotation( mRotationCurve.getValue( time, duration ) );
+ }
+
+ //-------------------------------------------------------------------------
+ // update position component of joint state
+ //-------------------------------------------------------------------------
+ if ((usage & LLJointState::POS) && mPositionCurve.mNumKeys)
+ {
+ joint_state->setPosition( mPositionCurve.getValue( time, duration ) );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::LLKeyframeMotion( const LLUUID &id) : LLMotion(id)
+{
+ mJointMotionList = NULL;
+ mJointStates = NULL;
+ mLastSkeletonSerialNum = 0;
+ mLastLoopedTime = 0.f;
+ mLastUpdateTime = 0.f;
+ mAssetStatus = ASSET_UNDEFINED;
+ mPelvisp = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::~LLKeyframeMotion()
+{
+ if (mJointStates)
+ {
+ delete [] mJointStates;
+ }
+ mConstraints.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// create()
+//-----------------------------------------------------------------------------
+LLMotion *LLKeyframeMotion::create(const LLUUID &id)
+{
+ return new LLKeyframeMotion(id);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ LLUUID* character_id;
+
+ // asset already loaded?
+ switch(mAssetStatus)
+ {
+ case ASSET_NEEDS_FETCH:
+ // request asset
+ mAssetStatus = ASSET_FETCHED;
+
+ character_id = new LLUUID(mCharacter->getID());
+ gAssetStorage->getAssetData(mID,
+ LLAssetType::AT_ANIMATION,
+ onLoadComplete,
+ (void *)character_id,
+ FALSE);
+
+ return STATUS_HOLD;
+ case ASSET_FETCHED:
+ return STATUS_HOLD;
+ case ASSET_FETCH_FAILED:
+ return STATUS_FAILURE;
+ case ASSET_LOADED:
+ return STATUS_SUCCESS;
+ default:
+ // we don't know what state the asset is in yet, so keep going
+ // check keyframe cache first then static vfs then asset request
+ break;
+ }
+
+ LLKeyframeMotion::JointMotionList* joint_motion_list = LLKeyframeDataCache::getKeyframeData(getID());
+
+ if(joint_motion_list)
+ {
+ // motion already existed in cache, so grab it
+ mJointMotionList = joint_motion_list;
+
+ // don't forget to allocate joint states
+ mJointStates = new LLJointState[mJointMotionList->mNumJointMotions];
+
+ // set up joint states to point to character joints
+ for(U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ if (LLJoint *jointp = mCharacter->getJoint(mJointMotionList->mJointMotionArray[i].mJointName))
+ {
+ mJointStates[i].setJoint(jointp);
+ mJointStates[i].setUsage(mJointMotionList->mJointMotionArray[i].mUsage);
+ mJointStates[i].setPriority(joint_motion_list->mJointMotionArray[i].mPriority);
+ }
+ }
+ mAssetStatus = ASSET_LOADED;
+ setupPose();
+ return STATUS_SUCCESS;
+ }
+
+ //-------------------------------------------------------------------------
+ // Load named file by concatenating the character prefix with the motion name.
+ // Load data into a buffer to be parsed.
+ //-------------------------------------------------------------------------
+ U8 *anim_data;
+ S32 anim_file_size;
+
+ if (!sVFS)
+ {
+ llerrs << "Must call LLKeyframeMotion::setVFS() first before loading a keyframe file!" << llendl;
+ }
+
+ BOOL success = FALSE;
+ LLVFile* anim_file = new LLVFile(sVFS, mID, LLAssetType::AT_ANIMATION);
+ if (!anim_file || !anim_file->getSize())
+ {
+ delete anim_file;
+ anim_file = NULL;
+
+ // request asset over network on next call to load
+ mAssetStatus = ASSET_NEEDS_FETCH;
+
+ return STATUS_HOLD;
+ }
+ else
+ {
+ anim_file_size = anim_file->getSize();
+ anim_data = new U8[anim_file_size];
+ success = anim_file->read(anim_data, anim_file_size); /*Flawfinder: ignore*/
+ delete anim_file;
+ anim_file = NULL;
+ }
+
+ if (!success)
+ {
+ llwarns << "Can't open animation file " << mID << llendl;
+ mAssetStatus = ASSET_FETCH_FAILED;
+ return STATUS_FAILURE;
+ }
+
+ lldebugs << "Loading keyframe data for: " << getName() << ":" << getID() << " (" << anim_file_size << " bytes)" << llendl;
+
+ LLDataPackerBinaryBuffer dp(anim_data, anim_file_size);
+
+ if (!deserialize(dp))
+ {
+ llwarns << "Failed to decode asset for animation " << getName() << ":" << getID() << llendl;
+ mAssetStatus = ASSET_FETCH_FAILED;
+ return STATUS_FAILURE;
+ }
+
+ delete []anim_data;
+
+ mAssetStatus = ASSET_LOADED;
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// setupPose()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::setupPose()
+{
+ // add all valid joint states to the pose
+ U32 jm;
+ for (jm=0; jm<mJointMotionList->mNumJointMotions; jm++)
+ {
+ if ( mJointStates[jm].getJoint() )
+ {
+ addJointState( &mJointStates[jm] );
+ }
+ }
+
+ // initialize joint constraints
+ for (JointConstraintSharedData* shared_constraintp = mJointMotionList->mConstraints.getFirstData();
+ shared_constraintp;
+ shared_constraintp = mJointMotionList->mConstraints.getNextData())
+ {
+ JointConstraint* constraintp = new JointConstraint(shared_constraintp);
+ initializeConstraint(constraintp);
+ mConstraints.addData(constraintp);
+ }
+
+ if (mJointMotionList->mConstraints.getLength())
+ {
+ mPelvisp = mCharacter->getJoint("mPelvis");
+ if (!mPelvisp)
+ {
+ return FALSE;
+ }
+ }
+
+ // setup loop keys
+ setLoopIn(mJointMotionList->mLoopInPoint);
+ setLoopOut(mJointMotionList->mLoopOutPoint);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::onActivate()
+{
+ // If the keyframe anim has an associated emote, trigger it.
+ if( mEmoteName.length() > 0 )
+ {
+ mCharacter->startMotion( gAnimLibrary.stringToAnimState(mEmoteName.c_str()) );
+ }
+
+ mLastLoopedTime = 0.f;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ llassert(time >= 0.f);
+
+ if (mJointMotionList->mLoop)
+ {
+ if (mJointMotionList->mDuration == 0.0f)
+ {
+ time = 0.f;
+ mLastLoopedTime = 0.0f;
+ }
+ else if (mStopped)
+ {
+ mLastLoopedTime = llmin(mJointMotionList->mDuration, mLastLoopedTime + time - mLastUpdateTime);
+ }
+ else if (time > mJointMotionList->mLoopOutPoint)
+ {
+ if ((mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint) == 0.f)
+ {
+ mLastLoopedTime = mJointMotionList->mLoopOutPoint;
+ }
+ else
+ {
+ mLastLoopedTime = mJointMotionList->mLoopInPoint +
+ fmod(time - mJointMotionList->mLoopOutPoint,
+ mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
+ }
+ }
+ else
+ {
+ mLastLoopedTime = time;
+ }
+ }
+ else
+ {
+ mLastLoopedTime = time;
+ }
+
+ applyKeyframes(mLastLoopedTime);
+
+ applyConstraints(mLastLoopedTime, joint_mask);
+
+ mLastUpdateTime = time;
+
+ return mLastLoopedTime <= mJointMotionList->mDuration;
+}
+
+//-----------------------------------------------------------------------------
+// applyKeyframes()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyKeyframes(F32 time)
+{
+ U32 i;
+ for (i=0; i<mJointMotionList->mNumJointMotions; i++)
+ {
+ mJointMotionList->mJointMotionArray[i].update(
+ &mJointStates[i],
+ time,
+ mJointMotionList->mDuration );
+ }
+
+ LLJoint::JointPriority* pose_priority = (LLJoint::JointPriority* )mCharacter->getAnimationData("Hand Pose Priority");
+ if (pose_priority)
+ {
+ if (mJointMotionList->mMaxPriority >= *pose_priority)
+ {
+ mCharacter->setAnimationData("Hand Pose", &mJointMotionList->mHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &mJointMotionList->mMaxPriority);
+ }
+ }
+ else
+ {
+ mCharacter->setAnimationData("Hand Pose", &mJointMotionList->mHandPose);
+ mCharacter->setAnimationData("Hand Pose Priority", &mJointMotionList->mMaxPriority);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// applyConstraints()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyConstraints(F32 time, U8* joint_mask)
+{
+ //TODO: investigate replacing spring simulation with critically damped motion
+
+ // re-init constraints if skeleton has changed
+ if (mCharacter->getSkeletonSerialNum() != mLastSkeletonSerialNum)
+ {
+ mLastSkeletonSerialNum = mCharacter->getSkeletonSerialNum();
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ initializeConstraint(constraintp);
+ }
+ }
+
+ // apply constraints
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ applyConstraint(constraintp, time, joint_mask);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::onDeactivate()
+{
+ for (JointConstraint* constraintp = mConstraints.getFirstData();
+ constraintp;
+ constraintp = mConstraints.getNextData())
+ {
+ deactivateConstraint(constraintp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setStopTime()
+//-----------------------------------------------------------------------------
+// time is in seconds since character creation
+void LLKeyframeMotion::setStopTime(F32 time)
+{
+ LLMotion::setStopTime(time);
+
+ if (mJointMotionList->mLoop && mJointMotionList->mLoopOutPoint != mJointMotionList->mDuration)
+ {
+ F32 start_loop_time = mActivationTimestamp + mJointMotionList->mLoopInPoint;
+ F32 loop_fraction_time;
+ if (mJointMotionList->mLoopOutPoint == mJointMotionList->mLoopInPoint)
+ {
+ loop_fraction_time = 0.f;
+ }
+ else
+ {
+ loop_fraction_time = fmod(time - start_loop_time,
+ mJointMotionList->mLoopOutPoint - mJointMotionList->mLoopInPoint);
+ }
+ mStopTimestamp = llmax(time,
+ (time - loop_fraction_time) + (mJointMotionList->mDuration - mJointMotionList->mLoopInPoint) - getEaseOutDuration());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// initializeConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::initializeConstraint(JointConstraint* constraint)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+
+ S32 joint_num;
+ LLVector3 source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLJoint* cur_joint = mJointStates[shared_data->mJointStateIndices[0]].getJoint();
+
+ F32 source_pos_offset = dist_vec(source_pos, cur_joint->getWorldPosition());
+
+ constraint->mTotalLength = constraint->mJointLengths[0] = dist_vec(cur_joint->getParent()->getWorldPosition(), source_pos);
+
+ // grab joint lengths
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ if (!cur_joint)
+ {
+ return;
+ }
+ constraint->mJointLengths[joint_num] = dist_vec(cur_joint->getWorldPosition(), cur_joint->getParent()->getWorldPosition());
+ constraint->mTotalLength += constraint->mJointLengths[joint_num];
+ }
+
+ // store fraction of total chain length so we know how to shear the entire chain towards the goal position
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ constraint->mJointLengthFractions[joint_num] = constraint->mJointLengths[joint_num] / constraint->mTotalLength;
+ }
+
+ // add last step in chain, from final joint to constraint position
+ constraint->mTotalLength += source_pos_offset;
+
+ constraint->mSourceVolume = mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume);
+ constraint->mTargetVolume = mCharacter->findCollisionVolume(shared_data->mTargetConstraintVolume);
+}
+
+//-----------------------------------------------------------------------------
+// activateConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::activateConstraint(JointConstraint* constraint)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+ constraint->mActive = TRUE;
+ S32 joint_num;
+
+ // grab ground position if we need to
+ if (shared_data->mConstraintTargetType == TYPE_GROUND)
+ {
+ LLVector3 source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLVector3 ground_pos_agent;
+ mCharacter->getGround(source_pos, ground_pos_agent, constraint->mGroundNorm);
+ constraint->mGroundPos = mCharacter->getPosGlobalFromAgent(ground_pos_agent + shared_data->mTargetConstraintOffset);
+ }
+
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLJoint* cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ constraint->mPositions[joint_num] = (cur_joint->getWorldPosition() - mPelvisp->getWorldPosition()) * ~mPelvisp->getWorldRotation();
+ }
+
+ constraint->mWeight = 1.f;
+}
+
+//-----------------------------------------------------------------------------
+// deactivateConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::deactivateConstraint(JointConstraint *constraintp)
+{
+ if (constraintp->mSourceVolume)
+ {
+ constraintp->mSourceVolume->mUpdateXform = FALSE;
+ }
+
+ if (!constraintp->mSharedData->mConstraintTargetType == TYPE_GROUND)
+ {
+ if (constraintp->mTargetVolume)
+ {
+ constraintp->mTargetVolume->mUpdateXform = FALSE;
+ }
+ }
+ constraintp->mActive = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// applyConstraint()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::applyConstraint(JointConstraint* constraint, F32 time, U8* joint_mask)
+{
+ JointConstraintSharedData *shared_data = constraint->mSharedData;
+ if (!shared_data) return;
+
+ LLVector3 positions[MAX_CHAIN_LENGTH];
+ const F32* joint_lengths = constraint->mJointLengths;
+ LLVector3 velocities[MAX_CHAIN_LENGTH - 1];
+ LLQuaternion old_rots[MAX_CHAIN_LENGTH];
+ S32 joint_num;
+ LLJoint* cur_joint;
+
+ if (time < shared_data->mEaseInStartTime)
+ {
+ return;
+ }
+
+ if (time > shared_data->mEaseOutStopTime)
+ {
+ if (constraint->mActive)
+ {
+ deactivateConstraint(constraint);
+ }
+ return;
+ }
+
+ if (!constraint->mActive || time < shared_data->mEaseInStopTime)
+ {
+ activateConstraint(constraint);
+ }
+
+ LLJoint* root_joint = mJointStates[shared_data->mJointStateIndices[shared_data->mChainLength]].getJoint();
+ LLVector3 root_pos = root_joint->getWorldPosition();
+// LLQuaternion root_rot =
+ root_joint->getParent()->getWorldRotation();
+// LLQuaternion inv_root_rot = ~root_rot;
+
+// LLVector3 current_source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+
+ //apply underlying keyframe animation to get nominal "kinematic" joint positions
+ for (joint_num = 0; joint_num <= shared_data->mChainLength; joint_num++)
+ {
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ if (joint_mask[cur_joint->getJointNum()] >= (0xff >> (7 - getPriority())))
+ {
+ // skip constraint
+ return;
+ }
+ old_rots[joint_num] = cur_joint->getRotation();
+ cur_joint->setRotation(mJointStates[shared_data->mJointStateIndices[joint_num]].getRotation());
+ }
+
+
+ LLVector3 keyframe_source_pos = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset);
+ LLVector3 target_pos;
+
+ switch(shared_data->mConstraintTargetType)
+ {
+ case TYPE_GROUND:
+ target_pos = mCharacter->getPosAgentFromGlobal(constraint->mGroundPos);
+// llinfos << "Target Pos " << constraint->mGroundPos << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ break;
+ case TYPE_BODY:
+ target_pos = mCharacter->getVolumePos(shared_data->mTargetConstraintVolume, shared_data->mTargetConstraintOffset);
+ break;
+ default:
+ break;
+ }
+
+ LLVector3 norm;
+ LLJoint *source_jointp = NULL;
+ LLJoint *target_jointp = NULL;
+
+ if (shared_data->mConstraintType == TYPE_PLANE)
+ {
+ switch(shared_data->mConstraintTargetType)
+ {
+ case TYPE_GROUND:
+ norm = constraint->mGroundNorm;
+ break;
+ case TYPE_BODY:
+ target_jointp = mCharacter->findCollisionVolume(shared_data->mTargetConstraintVolume);
+ if (target_jointp)
+ {
+ // FIXME: do proper normal calculation for stretched spheres (inverse transpose)
+ norm = target_pos - target_jointp->getWorldPosition();
+ }
+
+ if (norm.isExactlyZero())
+ {
+ source_jointp = mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume);
+ norm = -1.f * shared_data->mSourceConstraintOffset;
+ if (source_jointp)
+ {
+ norm = norm * source_jointp->getWorldRotation();
+ }
+ }
+ norm.normVec();
+ break;
+ default:
+ norm.clearVec();
+ break;
+ }
+
+ target_pos = keyframe_source_pos + (norm * ((target_pos - keyframe_source_pos) * norm));
+ }
+
+ if (constraint->mSharedData->mChainLength != 0 &&
+ dist_vec_squared(root_pos, target_pos) * 0.95f > constraint->mTotalLength * constraint->mTotalLength)
+ {
+ constraint->mWeight = lerp(constraint->mWeight, 0.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ constraint->mWeight = lerp(constraint->mWeight, 1.f, LLCriticalDamp::getInterpolant(0.3f));
+ }
+
+ F32 weight = constraint->mWeight * ((shared_data->mEaseOutStopTime == 0.f) ? 1.f :
+ llmin(clamp_rescale(time, shared_data->mEaseInStartTime, shared_data->mEaseInStopTime, 0.f, 1.f),
+ clamp_rescale(time, shared_data->mEaseOutStartTime, shared_data->mEaseOutStopTime, 1.f, 0.f)));
+
+ LLVector3 source_to_target = target_pos - keyframe_source_pos;
+
+ S32 max_iteration_count = llround(clamp_rescale(
+ mCharacter->getPixelArea(),
+ MAX_PIXEL_AREA_CONSTRAINTS,
+ MIN_PIXEL_AREA_CONSTRAINTS,
+ (F32)MAX_ITERATIONS,
+ (F32)MIN_ITERATIONS));
+
+ if (shared_data->mChainLength)
+ {
+ LLQuaternion end_rot = mJointStates[shared_data->mJointStateIndices[0]].getJoint()->getWorldRotation();
+
+ // slam start and end of chain to the proper positions (rest of chain stays put)
+ positions[0] = lerp(keyframe_source_pos, target_pos, weight);
+ positions[shared_data->mChainLength] = root_pos;
+
+ // grab keyframe-specified positions of joints
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLVector3 kinematic_position = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->getWorldPosition() +
+ (source_to_target * constraint->mJointLengthFractions[joint_num]);
+
+ // convert intermediate joint positions to world coordinates
+ positions[joint_num] = ( constraint->mPositions[joint_num] * mPelvisp->getWorldRotation()) + mPelvisp->getWorldPosition();
+ F32 time_constant = 1.f / clamp_rescale(constraint->mFixupDistanceRMS, 0.f, 0.5f, 0.2f, 8.f);
+// llinfos << "Interpolant " << LLCriticalDamp::getInterpolant(time_constant, FALSE) << " and fixup distance " << constraint->mFixupDistanceRMS << " on " << mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ positions[joint_num] = lerp(positions[joint_num], kinematic_position,
+ LLCriticalDamp::getInterpolant(time_constant, FALSE));
+ }
+
+ S32 iteration_count;
+ for (iteration_count = 0; iteration_count < max_iteration_count; iteration_count++)
+ {
+ S32 num_joints_finished = 0;
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ // constraint to child
+ LLVector3 acceleration = (positions[joint_num - 1] - positions[joint_num]) *
+ (dist_vec(positions[joint_num], positions[joint_num - 1]) - joint_lengths[joint_num - 1]) * JOINT_LENGTH_K;
+ // constraint to parent
+ acceleration += (positions[joint_num + 1] - positions[joint_num]) *
+ (dist_vec(positions[joint_num + 1], positions[joint_num]) - joint_lengths[joint_num]) * JOINT_LENGTH_K;
+
+ if (acceleration.magVecSquared() < MIN_ACCELERATION_SQUARED)
+ {
+ num_joints_finished++;
+ }
+
+ velocities[joint_num - 1] = velocities[joint_num - 1] * 0.7f;
+ positions[joint_num] += velocities[joint_num - 1] + (acceleration * 0.5f);
+ velocities[joint_num - 1] += acceleration;
+ }
+
+ if ((iteration_count >= MIN_ITERATION_COUNT) &&
+ (num_joints_finished == shared_data->mChainLength - 1))
+ {
+// llinfos << iteration_count << " iterations on " <<
+// mCharacter->findCollisionVolume(shared_data->mSourceConstraintVolume)->getName() << llendl;
+ break;
+ }
+ }
+
+ for (joint_num = shared_data->mChainLength; joint_num > 0; joint_num--)
+ {
+ LLQuaternion parent_rot = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->getParent()->getWorldRotation();
+ cur_joint = mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint();
+ LLJoint* child_joint = mJointStates[shared_data->mJointStateIndices[joint_num - 1]].getJoint();
+
+ LLQuaternion cur_rot = cur_joint->getWorldRotation();
+ LLQuaternion fixup_rot;
+
+ LLVector3 target_at = positions[joint_num - 1] - positions[joint_num];
+ LLVector3 current_at;
+
+ // at bottom of chain, use point on collision volume, not joint position
+ if (joint_num == 1)
+ {
+ current_at = mCharacter->getVolumePos(shared_data->mSourceConstraintVolume, shared_data->mSourceConstraintOffset) -
+ cur_joint->getWorldPosition();
+ }
+ else
+ {
+ current_at = child_joint->getPosition() * cur_rot;
+ }
+ fixup_rot.shortestArc(current_at, target_at);
+
+ LLQuaternion target_rot = cur_rot * fixup_rot;
+ target_rot = target_rot * ~parent_rot;
+
+ if (weight != 1.f)
+ {
+ LLQuaternion cur_rot = mJointStates[shared_data->mJointStateIndices[joint_num]].getRotation();
+ target_rot = nlerp(weight, cur_rot, target_rot);
+ }
+
+ mJointStates[shared_data->mJointStateIndices[joint_num]].setRotation(target_rot);
+ cur_joint->setRotation(target_rot);
+ }
+
+ LLJoint* end_joint = mJointStates[shared_data->mJointStateIndices[0]].getJoint();
+ LLQuaternion end_local_rot = end_rot * ~end_joint->getParent()->getWorldRotation();
+
+ if (weight == 1.f)
+ {
+ mJointStates[shared_data->mJointStateIndices[0]].setRotation(end_local_rot);
+ }
+ else
+ {
+ LLQuaternion cur_rot = mJointStates[shared_data->mJointStateIndices[0]].getRotation();
+ mJointStates[shared_data->mJointStateIndices[0]].setRotation(nlerp(weight, cur_rot, end_local_rot));
+ }
+
+ // save simulated positions in pelvis-space and calculate total fixup distance
+ constraint->mFixupDistanceRMS = 0.f;
+ F32 delta_time = llmax(0.02f, llabs(time - mLastUpdateTime));
+ for (joint_num = 1; joint_num < shared_data->mChainLength; joint_num++)
+ {
+ LLVector3 new_pos = (positions[joint_num] - mPelvisp->getWorldPosition()) * ~mPelvisp->getWorldRotation();
+ constraint->mFixupDistanceRMS += dist_vec_squared(new_pos, constraint->mPositions[joint_num]) / delta_time;
+ constraint->mPositions[joint_num] = new_pos;
+ }
+ constraint->mFixupDistanceRMS *= 1.f / (constraint->mTotalLength * (F32)(shared_data->mChainLength - 1));
+ constraint->mFixupDistanceRMS = fsqrtf(constraint->mFixupDistanceRMS);
+
+ //reset old joint rots
+ for (joint_num = 0; joint_num <= shared_data->mChainLength; joint_num++)
+ {
+ mJointStates[shared_data->mJointStateIndices[joint_num]].getJoint()->setRotation(old_rots[joint_num]);
+ }
+ }
+ // simple positional constraint (pelvis only)
+ else if (mJointStates[shared_data->mJointStateIndices[0]].getUsage() & LLJointState::POS)
+ {
+ LLVector3 delta = source_to_target * weight;
+ LLJointState* current_joint_statep = &mJointStates[shared_data->mJointStateIndices[0]];
+ LLQuaternion parent_rot = current_joint_statep->getJoint()->getParent()->getWorldRotation();
+ delta = delta * ~parent_rot;
+ current_joint_statep->setPosition(current_joint_statep->getJoint()->getPosition() + delta);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// deserialize()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::deserialize(LLDataPacker& dp)
+{
+ BOOL old_version = FALSE;
+ mJointMotionList = new LLKeyframeMotion::JointMotionList;
+ mJointMotionList->mNumJointMotions = 0;
+
+ //-------------------------------------------------------------------------
+ // get base priority
+ //-------------------------------------------------------------------------
+ S32 temp_priority;
+ U16 version;
+ U16 sub_version;
+
+ if (!dp.unpackU16(version, "version"))
+ {
+ llwarns << "can't read version number" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackU16(sub_version, "sub_version"))
+ {
+ llwarns << "can't read sub version number" << llendl;
+ return FALSE;
+ }
+
+ if (version == 0 && sub_version == 1)
+ {
+ old_version = TRUE;
+ }
+ else if (version != KEYFRAME_MOTION_VERSION || sub_version != KEYFRAME_MOTION_SUBVERSION)
+ {
+#if LL_RELEASE
+ llwarns << "Bad animation version " << version << "." << sub_version << llendl;
+ return FALSE;
+#else
+ llerrs << "Bad animation version " << version << "." << sub_version << llendl;
+#endif
+ }
+
+ if (!dp.unpackS32(temp_priority, "base_priority"))
+ {
+ llwarns << "can't read priority" << llendl;
+ return FALSE;
+ }
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority) temp_priority;
+
+ if (mJointMotionList->mBasePriority >= LLJoint::ADDITIVE_PRIORITY)
+ {
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority)((int)LLJoint::ADDITIVE_PRIORITY-1);
+ mJointMotionList->mMaxPriority = mJointMotionList->mBasePriority;
+ }
+
+ //-------------------------------------------------------------------------
+ // get duration
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mDuration, "duration"))
+ {
+ llwarns << "can't read duration" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get emote (optional)
+ //-------------------------------------------------------------------------
+ char read_string[128];
+ if (!dp.unpackString(read_string, "emote_name"))
+ {
+ llwarns << "can't read optional_emote_animation" << llendl;
+ return FALSE;
+ }
+
+ mEmoteName.assign( read_string );
+
+ //-------------------------------------------------------------------------
+ // get loop
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mLoopInPoint, "loop_in_point"))
+ {
+ llwarns << "can't read loop point" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(mJointMotionList->mLoopOutPoint, "loop_out_point"))
+ {
+ llwarns << "can't read loop point" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackS32(mJointMotionList->mLoop, "loop"))
+ {
+ llwarns << "can't read loop" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get easeIn and easeOut
+ //-------------------------------------------------------------------------
+ if (!dp.unpackF32(mJointMotionList->mEaseInDuration, "ease_in_duration"))
+ {
+ llwarns << "can't read easeIn" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(mJointMotionList->mEaseOutDuration, "ease_out_duration"))
+ {
+ llwarns << "can't read easeOut" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // get hand pose
+ //-------------------------------------------------------------------------
+ U32 word;
+ if (!dp.unpackU32(word, "hand_pose"))
+ {
+ llwarns << "can't read hand pose" << llendl;
+ return FALSE;
+ }
+ mJointMotionList->mHandPose = (LLHandMotion::eHandPose)word;
+
+ //-------------------------------------------------------------------------
+ // get number of joint motions
+ //-------------------------------------------------------------------------
+ if (!dp.unpackU32(mJointMotionList->mNumJointMotions, "num_joints"))
+ {
+ llwarns << "can't read number of joints" << llendl;
+ return FALSE;
+ }
+
+ if (mJointMotionList->mNumJointMotions == 0)
+ {
+ llwarns << "no joints in animation" << llendl;
+ return FALSE;
+ }
+ else if (mJointMotionList->mNumJointMotions > LL_CHARACTER_MAX_JOINTS)
+ {
+ llwarns << "too many joints in animation" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray = new JointMotion[mJointMotionList->mNumJointMotions];
+ mJointStates = new LLJointState[mJointMotionList->mNumJointMotions];
+
+ if (!mJointMotionList->mJointMotionArray)
+ {
+ mJointMotionList->mDuration = 0.0f;
+ mJointMotionList->mEaseInDuration = 0.0f;
+ mJointMotionList->mEaseOutDuration = 0.0f;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize joint motions
+ //-------------------------------------------------------------------------
+ S32 k;
+ for(U32 i=0; i<mJointMotionList->mNumJointMotions; ++i)
+ {
+ if (!dp.unpackString(read_string, "joint_name"))
+ {
+ llwarns << "can't read joint name" << llendl;
+ return FALSE;
+ }
+
+ //---------------------------------------------------------------------
+ // find the corresponding joint
+ //---------------------------------------------------------------------
+ LLJoint *joint = mCharacter->getJoint( read_string );
+ if (joint)
+ {
+// llinfos << " joint: " << read_string << llendl;
+ }
+ else
+ {
+ llwarns << "joint not found: " << read_string << llendl;
+ //return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mJointName = read_string;
+ mJointStates[i].setJoint( joint );
+ mJointStates[i].setUsage( 0 );
+
+ //---------------------------------------------------------------------
+ // get joint priority
+ //---------------------------------------------------------------------
+ S32 joint_priority;
+ if (!dp.unpackS32(joint_priority, "joint_priority"))
+ {
+ llwarns << "can't read joint priority." << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mPriority = (LLJoint::JointPriority)joint_priority;
+ if (joint_priority != LLJoint::USE_MOTION_PRIORITY &&
+ joint_priority > mJointMotionList->mMaxPriority)
+ {
+ mJointMotionList->mMaxPriority = (LLJoint::JointPriority)joint_priority;
+ }
+
+ mJointStates[i].setPriority((LLJoint::JointPriority)joint_priority);
+
+ //---------------------------------------------------------------------
+ // scan rotation curve header
+ //---------------------------------------------------------------------
+ if (!dp.unpackS32(mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys, "num_rot_keys"))
+ {
+ llwarns << "can't read number of rotation keys" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mRotationCurve.mInterpolationType = IT_LINEAR;
+ if (mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys != 0)
+ {
+ mJointStates[i].setUsage(mJointStates[i].getUsage() | LLJointState::ROT );
+ }
+
+ //---------------------------------------------------------------------
+ // scan rotation curve keys
+ //---------------------------------------------------------------------
+ RotationCurve *rCurve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+
+ for (k = 0; k < mJointMotionList->mJointMotionArray[i].mRotationCurve.mNumKeys; k++)
+ {
+ F32 time;
+ U16 time_short;
+
+ if (old_version)
+ {
+ if (!dp.unpackF32(time, "time"))
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ }
+ else
+ {
+ if (!dp.unpackU16(time_short, "time"))
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ time = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+ }
+
+ RotationKey *rot_key = new RotationKey;
+ rot_key->mTime = time;
+ LLVector3 rot_angles;
+ U16 x, y, z;
+
+ BOOL success = TRUE;
+
+ if (old_version)
+ {
+ success = dp.unpackVector3(rot_angles, "rot_angles");
+
+ LLQuaternion::Order ro = StringToOrder("ZYX");
+ rot_key->mRotation = mayaQ(rot_angles.mV[VX], rot_angles.mV[VY], rot_angles.mV[VZ], ro);
+ }
+ else
+ {
+ success &= dp.unpackU16(x, "rot_angle_x");
+ success &= dp.unpackU16(y, "rot_angle_y");
+ success &= dp.unpackU16(z, "rot_angle_z");
+
+ LLVector3 rot_vec;
+ rot_vec.mV[VX] = U16_to_F32(x, -1.f, 1.f);
+ rot_vec.mV[VY] = U16_to_F32(y, -1.f, 1.f);
+ rot_vec.mV[VZ] = U16_to_F32(z, -1.f, 1.f);
+ rot_key->mRotation.unpackFromVector3(rot_vec);
+ }
+
+ if (!success)
+ {
+ llwarns << "can't read rotation key (" << k << ")" << llendl;
+ return FALSE;
+ }
+
+ rCurve->mKeys[time] = rot_key;
+ }
+
+ //---------------------------------------------------------------------
+ // scan position curve header
+ //---------------------------------------------------------------------
+ if (!dp.unpackS32(mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys, "num_pos_keys"))
+ {
+ llwarns << "can't read number of position keys" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mJointMotionArray[i].mPositionCurve.mInterpolationType = IT_LINEAR;
+ if (mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys != 0)
+ {
+ mJointStates[i].setUsage(mJointStates[i].getUsage() | LLJointState::POS );
+ }
+
+ //---------------------------------------------------------------------
+ // scan position curve keys
+ //---------------------------------------------------------------------
+ PositionCurve *pCurve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ BOOL is_pelvis = mJointMotionList->mJointMotionArray[i].mJointName == "mPelvis";
+ for (k = 0; k < mJointMotionList->mJointMotionArray[i].mPositionCurve.mNumKeys; k++)
+ {
+ U16 time_short;
+ PositionKey* pos_key = new PositionKey;
+
+ if (old_version)
+ {
+ if (!dp.unpackF32(pos_key->mTime, "time"))
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!dp.unpackU16(time_short, "time"))
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+
+ pos_key->mTime = U16_to_F32(time_short, 0.f, mJointMotionList->mDuration);
+ }
+
+ BOOL success = TRUE;
+
+ if (old_version)
+ {
+ success = dp.unpackVector3(pos_key->mPosition, "pos");
+ }
+ else
+ {
+ U16 x, y, z;
+
+ success &= dp.unpackU16(x, "pos_x");
+ success &= dp.unpackU16(y, "pos_y");
+ success &= dp.unpackU16(z, "pos_z");
+
+ pos_key->mPosition.mV[VX] = U16_to_F32(x, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key->mPosition.mV[VY] = U16_to_F32(y, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ pos_key->mPosition.mV[VZ] = U16_to_F32(z, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ }
+
+ if (!success)
+ {
+ llwarns << "can't read position key (" << k << ")" << llendl;
+ delete pos_key;
+ return FALSE;
+ }
+
+ pCurve->mKeys[pos_key->mTime] = pos_key;
+
+ if (is_pelvis)
+ {
+ mJointMotionList->mPelvisBBox.addPoint(pos_key->mPosition);
+ }
+ }
+
+ mJointMotionList->mJointMotionArray[i].mUsage = mJointStates[i].getUsage();
+ }
+
+ //-------------------------------------------------------------------------
+ // get number of constraints
+ //-------------------------------------------------------------------------
+ S32 num_constraints = 0;
+ if (!dp.unpackS32(num_constraints, "num_constraints"))
+ {
+ llwarns << "can't read number of constraints" << llendl;
+ return FALSE;
+ }
+
+ if (num_constraints > MAX_CONSTRAINTS)
+ {
+ llwarns << "Too many constraints...ignoring" << llendl;
+ }
+ else
+ {
+ //-------------------------------------------------------------------------
+ // get constraints
+ //-------------------------------------------------------------------------
+ std::string str;
+ for(S32 i = 0; i < num_constraints; ++i)
+ {
+ // read in constraint data
+ JointConstraintSharedData* constraintp = new JointConstraintSharedData;
+ U8 byte = 0;
+
+ if (!dp.unpackU8(byte, "chain_length"))
+ {
+ llwarns << "can't read constraint chain length" << llendl;
+ return FALSE;
+ }
+ constraintp->mChainLength = (S32) byte;
+
+ if (!dp.unpackU8(byte, "constraint_type"))
+ {
+ llwarns << "can't read constraint type" << llendl;
+ return FALSE;
+ }
+ constraintp->mConstraintType = (EConstraintType)byte;
+
+ if (!dp.unpackBinaryDataFixed((unsigned char*)read_string, 16, "source_volume"))
+ {
+ llwarns << "can't read source volume name" << llendl;
+ return FALSE;
+ }
+
+ str.assign(read_string);
+ constraintp->mSourceConstraintVolume = mCharacter->getCollisionVolumeID(str);
+
+ if (!dp.unpackVector3(constraintp->mSourceConstraintOffset, "source_offset"))
+ {
+ llwarns << "can't read constraint source offset" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackBinaryDataFixed((unsigned char*)read_string, 16, "target_volume"))
+ {
+ llwarns << "can't read target volume name" << llendl;
+ return FALSE;
+ }
+
+ str.assign(read_string);
+ if (str == "GROUND")
+ {
+ // constrain to ground
+ constraintp->mConstraintTargetType = TYPE_GROUND;
+ }
+ else
+ {
+ constraintp->mConstraintTargetType = TYPE_BODY;
+ constraintp->mTargetConstraintVolume = mCharacter->getCollisionVolumeID(str);
+ }
+
+ if (!dp.unpackVector3(constraintp->mTargetConstraintOffset, "target_offset"))
+ {
+ llwarns << "can't read constraint target offset" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackVector3(constraintp->mTargetConstraintDir, "target_dir"))
+ {
+ llwarns << "can't read constraint target direction" << llendl;
+ return FALSE;
+ }
+
+ if (!constraintp->mTargetConstraintDir.isExactlyZero())
+ {
+ constraintp->mUseTargetOffset = TRUE;
+ // constraintp->mTargetConstraintDir *= constraintp->mSourceConstraintOffset.magVec();
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseInStartTime, "ease_in_start"))
+ {
+ llwarns << "can't read constraint ease in start time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseInStopTime, "ease_in_stop"))
+ {
+ llwarns << "can't read constraint ease in stop time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseOutStartTime, "ease_out_start"))
+ {
+ llwarns << "can't read constraint ease out start time" << llendl;
+ return FALSE;
+ }
+
+ if (!dp.unpackF32(constraintp->mEaseOutStopTime, "ease_out_stop"))
+ {
+ llwarns << "can't read constraint ease out stop time" << llendl;
+ return FALSE;
+ }
+
+ mJointMotionList->mConstraints.addData(constraintp);
+
+ constraintp->mJointStateIndices = new S32[constraintp->mChainLength + 1];
+
+ LLJoint* joint = mCharacter->findCollisionVolume(constraintp->mSourceConstraintVolume);
+ if (!joint)
+ {
+ return FALSE;
+ }
+
+ // get joint to which this collision volume is attached
+ joint = joint->getParent();
+
+ for (S32 i = 0; i < constraintp->mChainLength + 1; i++)
+ {
+ constraintp->mJointStateIndices[i] = -1;
+ for (U32 j = 0; j < mJointMotionList->mNumJointMotions; j++)
+ {
+ if(mJointStates[j].getJoint() == joint)
+ {
+ constraintp->mJointStateIndices[i] = (S32)j;
+ break;
+ }
+ }
+ joint = joint->getParent();
+ }
+
+ }
+ }
+
+ // FIXME: support cleanup of old keyframe data
+ LLKeyframeDataCache::addKeyframeData(getID(), mJointMotionList);
+ mAssetStatus = ASSET_LOADED;
+
+ setupPose();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// serialize()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotion::serialize(LLDataPacker& dp) const
+{
+ BOOL success = TRUE;
+
+ success &= dp.packU16(KEYFRAME_MOTION_VERSION, "version");
+ success &= dp.packU16(KEYFRAME_MOTION_SUBVERSION, "sub_version");
+ success &= dp.packS32(mJointMotionList->mBasePriority, "base_priority");
+ success &= dp.packF32(mJointMotionList->mDuration, "duration");
+ success &= dp.packString(mEmoteName.c_str(), "emote_name");
+ success &= dp.packF32(mJointMotionList->mLoopInPoint, "loop_in_point");
+ success &= dp.packF32(mJointMotionList->mLoopOutPoint, "loop_out_point");
+ success &= dp.packS32(mJointMotionList->mLoop, "loop");
+ success &= dp.packF32(mJointMotionList->mEaseInDuration, "ease_in_duration");
+ success &= dp.packF32(mJointMotionList->mEaseOutDuration, "ease_out_duration");
+ success &= dp.packU32(mJointMotionList->mHandPose, "hand_pose");
+ success &= dp.packU32(mJointMotionList->mNumJointMotions, "num_joints");
+
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ JointMotion* joint_motionp = &mJointMotionList->mJointMotionArray[i];
+ success &= dp.packString(joint_motionp->mJointName.c_str(), "joint_name");
+ success &= dp.packS32(joint_motionp->mPriority, "joint_priority");
+ success &= dp.packS32(joint_motionp->mRotationCurve.mNumKeys, "num_rot_keys");
+
+ for (RotationKey* rot_keyp = joint_motionp->mRotationCurve.mKeys.getFirstData();
+ rot_keyp;
+ rot_keyp = joint_motionp->mRotationCurve.mKeys.getNextData())
+ {
+ U16 time_short = F32_to_U16(rot_keyp->mTime, 0.f, mJointMotionList->mDuration);
+ success &= dp.packU16(time_short, "time");
+
+ LLVector3 rot_angles = rot_keyp->mRotation.packToVector3();
+
+ U16 x, y, z;
+ rot_angles.quantize16(-1.f, 1.f, -1.f, 1.f);
+ x = F32_to_U16(rot_angles.mV[VX], -1.f, 1.f);
+ y = F32_to_U16(rot_angles.mV[VY], -1.f, 1.f);
+ z = F32_to_U16(rot_angles.mV[VZ], -1.f, 1.f);
+ success &= dp.packU16(x, "rot_angle_x");
+ success &= dp.packU16(y, "rot_angle_y");
+ success &= dp.packU16(z, "rot_angle_z");
+ }
+
+ success &= dp.packS32(joint_motionp->mPositionCurve.mNumKeys, "num_pos_keys");
+ for (PositionKey* pos_keyp = joint_motionp->mPositionCurve.mKeys.getFirstData();
+ pos_keyp;
+ pos_keyp = joint_motionp->mPositionCurve.mKeys.getNextData())
+ {
+ U16 time_short = F32_to_U16(pos_keyp->mTime, 0.f, mJointMotionList->mDuration);
+ success &= dp.packU16(time_short, "time");
+
+ U16 x, y, z;
+ pos_keyp->mPosition.quantize16(-LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET, -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ x = F32_to_U16(pos_keyp->mPosition.mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ y = F32_to_U16(pos_keyp->mPosition.mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ z = F32_to_U16(pos_keyp->mPosition.mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
+ success &= dp.packU16(x, "pos_x");
+ success &= dp.packU16(y, "pos_y");
+ success &= dp.packU16(z, "pos_z");
+ }
+ }
+
+ success &= dp.packS32(mJointMotionList->mConstraints.getLength(), "num_constraints");
+ for (JointConstraintSharedData* shared_constraintp = mJointMotionList->mConstraints.getFirstData();
+ shared_constraintp;
+ shared_constraintp = mJointMotionList->mConstraints.getNextData())
+ {
+ success &= dp.packU8(shared_constraintp->mChainLength, "chain_length");
+ success &= dp.packU8(shared_constraintp->mConstraintType, "constraint_type");
+ char volume_name[16];
+ snprintf(volume_name, sizeof(volume_name), "%s",
+ mCharacter->findCollisionVolume(shared_constraintp->mSourceConstraintVolume)->getName().c_str()); /* Flawfinder: ignore */
+ success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "source_volume");
+ success &= dp.packVector3(shared_constraintp->mSourceConstraintOffset, "source_offset");
+ if (shared_constraintp->mConstraintTargetType == TYPE_GROUND)
+ {
+ snprintf(volume_name,sizeof(volume_name), "%s", "GROUND"); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(volume_name, sizeof(volume_name),"%s",
+ mCharacter->findCollisionVolume(shared_constraintp->mTargetConstraintVolume)->getName().c_str()); /* Flawfinder: ignore */
+ }
+ success &= dp.packBinaryDataFixed((U8*)volume_name, 16, "target_volume");
+ success &= dp.packVector3(shared_constraintp->mTargetConstraintOffset, "target_offset");
+ success &= dp.packVector3(shared_constraintp->mTargetConstraintDir, "target_dir");
+ success &= dp.packF32(shared_constraintp->mEaseInStartTime, "ease_in_start");
+ success &= dp.packF32(shared_constraintp->mEaseInStopTime, "ease_in_stop");
+ success &= dp.packF32(shared_constraintp->mEaseOutStartTime, "ease_out_start");
+ success &= dp.packF32(shared_constraintp->mEaseOutStopTime, "ease_out_stop");
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// getFileSize()
+//-----------------------------------------------------------------------------
+U32 LLKeyframeMotion::getFileSize()
+{
+ // serialize into a dummy buffer to calculate required size
+ LLDataPackerBinaryBuffer dp;
+ serialize(dp);
+
+ return dp.getCurrentSize();
+}
+
+//-----------------------------------------------------------------------------
+// getPelvisBBox()
+//-----------------------------------------------------------------------------
+const LLBBoxLocal &LLKeyframeMotion::getPelvisBBox()
+{
+ return mJointMotionList->mPelvisBBox;
+}
+
+//-----------------------------------------------------------------------------
+// setPriority()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setPriority(S32 priority)
+{
+ if (mJointMotionList)
+ {
+ S32 priority_delta = priority - mJointMotionList->mBasePriority;
+ mJointMotionList->mBasePriority = (LLJoint::JointPriority)priority;
+ mJointMotionList->mMaxPriority = mJointMotionList->mBasePriority;
+
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ mJointMotionList->mJointMotionArray[i].mPriority = (LLJoint::JointPriority)llclamp(
+ (S32)mJointMotionList->mJointMotionArray[i].mPriority + priority_delta,
+ (S32)LLJoint::LOW_PRIORITY,
+ (S32)LLJoint::HIGHEST_PRIORITY);
+ mJointStates[i].setPriority(mJointMotionList->mJointMotionArray[i].mPriority);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEmote()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEmote(const LLUUID& emote_id)
+{
+ const char* emote_name = gAnimLibrary.animStateToString(emote_id);
+ if (emote_name)
+ {
+ mEmoteName = emote_name;
+ }
+ else
+ {
+ mEmoteName = "";
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEaseIn()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEaseIn(F32 ease_in)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mEaseInDuration = llmax(ease_in, 0.f);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setEaseOut()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setEaseOut(F32 ease_in)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mEaseOutDuration = llmax(ease_in, 0.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// flushKeyframeCache()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::flushKeyframeCache()
+{
+ LLKeyframeDataCache::clear();
+}
+
+//-----------------------------------------------------------------------------
+// setLoop()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoop(BOOL loop)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoop = loop;
+ mSendStopTimestamp = F32_MAX;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setLoopIn()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoopIn(F32 in_point)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoopInPoint = in_point;
+
+ // set up loop keys
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ PositionCurve* pos_curve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ RotationCurve* rot_curve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+ ScaleCurve* scale_curve = &mJointMotionList->mJointMotionArray[i].mScaleCurve;
+
+ pos_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+ rot_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+ scale_curve->mLoopInKey.mTime = mJointMotionList->mLoopInPoint;
+
+ pos_curve->mLoopInKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ rot_curve->mLoopInKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ scale_curve->mLoopInKey.mScale = scale_curve->getValue(mJointMotionList->mLoopInPoint, mJointMotionList->mDuration);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setLoopOut()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::setLoopOut(F32 out_point)
+{
+ if (mJointMotionList)
+ {
+ mJointMotionList->mLoopOutPoint = out_point;
+
+ // set up loop keys
+ for (U32 i = 0; i < mJointMotionList->mNumJointMotions; i++)
+ {
+ PositionCurve* pos_curve = &mJointMotionList->mJointMotionArray[i].mPositionCurve;
+ RotationCurve* rot_curve = &mJointMotionList->mJointMotionArray[i].mRotationCurve;
+ ScaleCurve* scale_curve = &mJointMotionList->mJointMotionArray[i].mScaleCurve;
+
+ pos_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+ rot_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+ scale_curve->mLoopOutKey.mTime = mJointMotionList->mLoopOutPoint;
+
+ pos_curve->mLoopOutKey.mPosition = pos_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ rot_curve->mLoopOutKey.mRotation = rot_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ scale_curve->mLoopOutKey.mScale = scale_curve->getValue(mJointMotionList->mLoopOutPoint, mJointMotionList->mDuration);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onLoadComplete()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* id = (LLUUID*)user_data;
+
+ LLCharacter* character = NULL;
+
+ for(character = LLCharacter::sInstances.getFirstData();
+ character;
+ character = LLCharacter::sInstances.getNextData())
+ {
+ if (character->getID() == *id)
+ {
+ break;
+ }
+ }
+
+ delete id;
+
+ if (!character)
+ {
+ return;
+ }
+
+ // create an instance of this motion (it may or may not already exist)
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)character->createMotion(asset_uuid);
+
+ if (0 == status && motionp)
+ {
+ if (motionp->mAssetStatus == ASSET_LOADED)
+ {
+ // asset already loaded
+ return;
+ }
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ U8* buffer = new U8[size];
+ file.read((U8*)buffer, size); /*Flawfinder: ignore*/
+
+ lldebugs << "Loading keyframe data for: " << motionp->getName() << ":" << motionp->getID() << " (" << size << " bytes)" << llendl;
+
+ LLDataPackerBinaryBuffer dp(buffer, size);
+ if (motionp->deserialize(dp))
+ {
+ motionp->mAssetStatus = ASSET_LOADED;
+ }
+ else
+ {
+ llwarns << "Failed to decode asset for animation " << motionp->getName() << ":" << motionp->getID() << llendl;
+ motionp->mAssetStatus = ASSET_FETCH_FAILED;
+ }
+
+ delete []buffer;
+
+ }
+ else
+ {
+ llwarns << "Failed to load asset for animation " << motionp->getName() << ":" << motionp->getID() << llendl;
+ motionp->mAssetStatus = ASSET_FETCH_FAILED;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// writeCAL3D()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotion::writeCAL3D(apr_file_t* fp)
+{
+// <ANIMATION VERSION="1000" DURATION="1.03333" NUMTRACKS="58">
+// <TRACK BONEID="0" NUMKEYFRAMES="31">
+// <KEYFRAME TIME="0">
+// <TRANSLATION>0 0 48.8332</TRANSLATION>
+// <ROTATION>0.0512905 0.05657 0.66973 0.738668</ROTATION>
+// </KEYFRAME>
+// </TRACK>
+// </ANIMATION>
+
+ apr_file_printf(fp, "<ANIMATION VERSION=\"1000\" DURATION=\"%.5f\" NUMTRACKS=\"%d\">\n", getDuration(), mJointMotionList->mNumJointMotions);
+ for (U32 joint_index = 0; joint_index < mJointMotionList->mNumJointMotions; joint_index++)
+ {
+ JointMotion* joint_motionp = &mJointMotionList->mJointMotionArray[joint_index];
+ LLJoint* animated_joint = mCharacter->getJoint(joint_motionp->mJointName);
+ S32 joint_num = animated_joint->mJointNum + 1;
+
+ apr_file_printf(fp, " <TRACK BONEID=\"%d\" NUMKEYFRAMES=\"%d\">\n", joint_num, joint_motionp->mRotationCurve.mNumKeys );
+ PositionKey* pos_keyp = joint_motionp->mPositionCurve.mKeys.getFirstData();
+ for (RotationKey* rot_keyp = joint_motionp->mRotationCurve.mKeys.getFirstData();
+ rot_keyp;
+ rot_keyp = joint_motionp->mRotationCurve.mKeys.getNextData())
+ {
+ apr_file_printf(fp, " <KEYFRAME TIME=\"%0.3f\">\n", rot_keyp->mTime);
+ LLVector3 nominal_pos = animated_joint->getPosition();
+ if (animated_joint->getParent())
+ {
+ nominal_pos.scaleVec(animated_joint->getParent()->getScale());
+ }
+ nominal_pos = nominal_pos * 100.f;
+
+ if (joint_motionp->mUsage & LLJointState::POS && pos_keyp)
+ {
+ LLVector3 pos_val = pos_keyp->mPosition;
+ pos_val = pos_val * 100.f;
+ pos_val += nominal_pos;
+ apr_file_printf(fp, " <TRANSLATION>%0.4f %0.4f %0.4f</TRANSLATION>\n", pos_val.mV[VX], pos_val.mV[VY], pos_val.mV[VZ]);
+ pos_keyp = joint_motionp->mPositionCurve.mKeys.getNextData();
+ }
+ else
+ {
+ apr_file_printf(fp, " <TRANSLATION>%0.4f %0.4f %0.4f</TRANSLATION>\n", nominal_pos.mV[VX], nominal_pos.mV[VY], nominal_pos.mV[VZ]);
+ }
+
+ LLQuaternion rot_val = ~rot_keyp->mRotation;
+ apr_file_printf(fp, " <ROTATION>%0.4f %0.4f %0.4f %0.4f</ROTATION>\n", rot_val.mQ[VX], rot_val.mQ[VY], rot_val.mQ[VZ], rot_val.mQ[VW]);
+ apr_file_printf(fp, " </KEYFRAME>\n");
+ }
+ apr_file_printf(fp, " </TRACK>\n");
+ }
+ apr_file_printf(fp, "</ANIMATION>\n");
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::dumpDiagInfo()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::dumpDiagInfo()
+{
+ // keep track of totals
+ U32 total_size = 0;
+
+ char buf[1024]; /* Flawfinder: ignore */
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << " Global Motion Table (DEBUG only)" << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+
+ // print each loaded mesh, and it's memory usage
+ LLKeyframeDataMap::iterator map_it;
+ for (map_it = sKeyframeDataMap.begin(); map_it != sKeyframeDataMap.end(); ++map_it)
+ {
+ U32 joint_motion_kb;
+
+ LLKeyframeMotion::JointMotionList *motion_list_p = map_it->second;
+
+ llinfos << "Motion: " << map_it->first << llendl;
+
+ joint_motion_kb = motion_list_p->dumpDiagInfo();
+
+ total_size += joint_motion_kb;
+ }
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << "Motions\tTotal Size" << llendl;
+ snprintf(buf, sizeof(buf), "%d\t\t%d bytes", (S32)sKeyframeDataMap.size(), total_size ); /* Flawfinder: ignore */
+ llinfos << buf << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+}
+
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::addKeyframeData()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList* joint_motion_listp)
+{
+ sKeyframeDataMap[id] = joint_motion_listp;
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::removeKeyframeData()
+//--------------------------------------------------------------------
+void LLKeyframeDataCache::removeKeyframeData(const LLUUID& id)
+{
+ LLKeyframeMotion::JointMotionList* joint_motion_listp = getKeyframeData(id);
+ if (joint_motion_listp)
+ {
+ delete joint_motion_listp;
+ }
+ sKeyframeDataMap.erase(id);
+}
+
+//--------------------------------------------------------------------
+// LLKeyframeDataCache::getKeyframeData()
+//--------------------------------------------------------------------
+LLKeyframeMotion::JointMotionList* LLKeyframeDataCache::getKeyframeData(const LLUUID& id)
+{
+ LLKeyframeDataMap::iterator found_data = sKeyframeDataMap.find(id);
+ if (found_data == sKeyframeDataMap.end())
+ {
+ return NULL;
+ }
+ return found_data->second;
+}
+
+//--------------------------------------------------------------------
+// ~LLKeyframeDataCache::LLKeyframeDataCache()
+//--------------------------------------------------------------------
+LLKeyframeDataCache::~LLKeyframeDataCache()
+{
+ clear();
+}
+
+//-----------------------------------------------------------------------------
+// clear()
+//-----------------------------------------------------------------------------
+void LLKeyframeDataCache::clear()
+{
+ for_each(sKeyframeDataMap.begin(), sKeyframeDataMap.end(), DeletePairedPointer());
+ sKeyframeDataMap.clear();
+}
+
+//-----------------------------------------------------------------------------
+// JointConstraint()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::JointConstraint::JointConstraint(JointConstraintSharedData* shared_data) : mSharedData(shared_data)
+{
+ mTotalLength = 0.f;
+ mActive = FALSE;
+ mSourceVolume = NULL;
+ mTargetVolume = NULL;
+ mFixupDistanceRMS = 0.f;
+}
+
+//-----------------------------------------------------------------------------
+// ~JointConstraint()
+//-----------------------------------------------------------------------------
+LLKeyframeMotion::JointConstraint::~JointConstraint()
+{
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h
new file mode 100644
index 0000000000..4e82c0672a
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotion.h
@@ -0,0 +1,437 @@
+/**
+ * @file llkeyframemotion.h
+ * @brief Implementation of LLKeframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEMOTION_H
+#define LL_LLKEYFRAMEMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+
+#include <string>
+
+#include "llassetstorage.h"
+#include "llassoclist.h"
+#include "llbboxlocal.h"
+#include "llhandmotion.h"
+#include "lljointstate.h"
+#include "llmotion.h"
+#include "llptrskipmap.h"
+#include "llquaternion.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "llapr.h"
+
+class LLKeyframeDataCache;
+class LLVFS;
+class LLDataPacker;
+
+#define MIN_REQUIRED_PIXEL_AREA_KEYFRAME (40.f)
+#define MAX_CHAIN_LENGTH (4)
+
+const S32 KEYFRAME_MOTION_VERSION = 1;
+const S32 KEYFRAME_MOTION_SUBVERSION = 0;
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeMotion :
+ public LLMotion
+{
+ friend class LLKeyframeDataCache;
+public:
+ // Constructor
+ LLKeyframeMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID& id);
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() {
+ if (mJointMotionList) return mJointMotionList->mLoop;
+ else return FALSE;
+ }
+
+ // motions must report their total duration
+ virtual F32 getDuration() {
+ if (mJointMotionList) return mJointMotionList->mDuration;
+ else return 0.f;
+ }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() {
+ if (mJointMotionList) return mJointMotionList->mEaseInDuration;
+ else return 0.f;
+ }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() {
+ if (mJointMotionList) return mJointMotionList->mEaseOutDuration;
+ else return 0.f;
+ }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() {
+ if (mJointMotionList) return mJointMotionList->mBasePriority;
+ else return LLJoint::LOW_PRIORITY;
+ }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ virtual void setStopTime(F32 time);
+
+ static void setVFS(LLVFS* vfs) { sVFS = vfs; }
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+public:
+ U32 getFileSize();
+ BOOL serialize(LLDataPacker& dp) const;
+ BOOL deserialize(LLDataPacker& dp);
+ void writeCAL3D(apr_file_t* fp);
+ BOOL isLoaded() { return mJointMotionList != NULL; }
+
+
+ // setters for modifying a keyframe animation
+ void setLoop(BOOL loop);
+
+ F32 getLoopIn() {
+ return (mJointMotionList) ? mJointMotionList->mLoopInPoint : 0.f;
+ }
+
+ F32 getLoopOut() {
+ return (mJointMotionList) ? mJointMotionList->mLoopOutPoint : 0.f;
+ }
+
+ void setLoopIn(F32 in_point);
+
+ void setLoopOut(F32 out_point);
+
+ void setHandPose(LLHandMotion::eHandPose pose) {
+ if (mJointMotionList) mJointMotionList->mHandPose = pose;
+ }
+
+ LLHandMotion::eHandPose getHandPose() {
+ return (mJointMotionList) ? mJointMotionList->mHandPose : LLHandMotion::HAND_POSE_RELAXED;
+ }
+
+ void setPriority(S32 priority);
+
+ void setEmote(const LLUUID& emote_id);
+
+ void setEaseIn(F32 ease_in);
+
+ void setEaseOut(F32 ease_in);
+
+ F32 getLastUpdateTime() { return mLastLoopedTime; }
+
+ const LLBBoxLocal& getPelvisBBox();
+
+ static void flushKeyframeCache();
+
+ typedef enum e_constraint_type
+ {
+ TYPE_POINT,
+ TYPE_PLANE
+ } EConstraintType;
+
+ typedef enum e_constraint_target_type
+ {
+ TYPE_BODY,
+ TYPE_GROUND
+ } EConstraintTargetType;
+
+protected:
+ //-------------------------------------------------------------------------
+ // JointConstraintSharedData
+ //-------------------------------------------------------------------------
+ class JointConstraintSharedData
+ {
+ public:
+ JointConstraintSharedData() :
+ mChainLength(0),
+ mEaseInStartTime(0.f),
+ mEaseInStopTime(0.f),
+ mEaseOutStartTime(0.f),
+ mEaseOutStopTime(0.f),
+ mUseTargetOffset(FALSE),
+ mConstraintType(TYPE_POINT),
+ mConstraintTargetType(TYPE_BODY) {};
+ ~JointConstraintSharedData() { delete [] mJointStateIndices; }
+
+ S32 mSourceConstraintVolume;
+ LLVector3 mSourceConstraintOffset;
+ S32 mTargetConstraintVolume;
+ LLVector3 mTargetConstraintOffset;
+ LLVector3 mTargetConstraintDir;
+ S32 mChainLength;
+ S32* mJointStateIndices;
+ F32 mEaseInStartTime;
+ F32 mEaseInStopTime;
+ F32 mEaseOutStartTime;
+ F32 mEaseOutStopTime;
+ BOOL mUseTargetOffset;
+ EConstraintType mConstraintType;
+ EConstraintTargetType mConstraintTargetType;
+ };
+
+ //-----------------------------------------------------------------------------
+ // JointConstraint()
+ //-----------------------------------------------------------------------------
+ class JointConstraint
+ {
+ public:
+ JointConstraint(JointConstraintSharedData* shared_data);
+ ~JointConstraint();
+
+ JointConstraintSharedData* mSharedData;
+ F32 mWeight;
+ F32 mTotalLength;
+ LLVector3 mPositions[MAX_CHAIN_LENGTH];
+ F32 mJointLengths[MAX_CHAIN_LENGTH];
+ F32 mJointLengthFractions[MAX_CHAIN_LENGTH];
+ BOOL mActive;
+ LLVector3d mGroundPos;
+ LLVector3 mGroundNorm;
+ LLJoint* mSourceVolume;
+ LLJoint* mTargetVolume;
+ F32 mFixupDistanceRMS;
+ };
+
+ void applyKeyframes(F32 time);
+
+ void applyConstraints(F32 time, U8* joint_mask);
+
+ void activateConstraint(JointConstraint* constraintp);
+
+ void initializeConstraint(JointConstraint* constraint);
+
+ void deactivateConstraint(JointConstraint *constraintp);
+
+ void applyConstraint(JointConstraint* constraintp, F32 time, U8* joint_mask);
+
+ BOOL setupPose();
+
+public:
+ enum AssetStatus { ASSET_LOADED, ASSET_FETCHED, ASSET_NEEDS_FETCH, ASSET_FETCH_FAILED, ASSET_UNDEFINED };
+
+ enum InterpolationType { IT_STEP, IT_LINEAR, IT_SPLINE };
+
+ //-------------------------------------------------------------------------
+ // ScaleKey
+ //-------------------------------------------------------------------------
+ class ScaleKey
+ {
+ public:
+ ScaleKey() { mTime = 0.0f; }
+ ScaleKey(F32 time, const LLVector3 &scale) { mTime = time; mScale = scale; }
+
+ F32 mTime;
+ LLVector3 mScale;
+ };
+
+ //-------------------------------------------------------------------------
+ // RotationKey
+ //-------------------------------------------------------------------------
+ class RotationKey
+ {
+ public:
+ RotationKey() { mTime = 0.0f; }
+ RotationKey(F32 time, const LLQuaternion &rotation) { mTime = time; mRotation = rotation; }
+
+ F32 mTime;
+ LLQuaternion mRotation;
+ };
+
+ //-------------------------------------------------------------------------
+ // PositionKey
+ //-------------------------------------------------------------------------
+ class PositionKey
+ {
+ public:
+ PositionKey() { mTime = 0.0f; }
+ PositionKey(F32 time, const LLVector3 &position) { mTime = time; mPosition = position; }
+
+ F32 mTime;
+ LLVector3 mPosition;
+ };
+
+ //-------------------------------------------------------------------------
+ // ScaleCurve
+ //-------------------------------------------------------------------------
+ class ScaleCurve
+ {
+ public:
+ ScaleCurve();
+ ~ScaleCurve();
+ LLVector3 getValue(F32 time, F32 duration);
+ LLVector3 interp(F32 u, ScaleKey& before, ScaleKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, ScaleKey*> mKeys;
+ ScaleKey mLoopInKey;
+ ScaleKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // RotationCurve
+ //-------------------------------------------------------------------------
+ class RotationCurve
+ {
+ public:
+ RotationCurve();
+ ~RotationCurve();
+ LLQuaternion getValue(F32 time, F32 duration);
+ LLQuaternion interp(F32 u, RotationKey& before, RotationKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, RotationKey*> mKeys;
+ RotationKey mLoopInKey;
+ RotationKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // PositionCurve
+ //-------------------------------------------------------------------------
+ class PositionCurve
+ {
+ public:
+ PositionCurve();
+ ~PositionCurve();
+ LLVector3 getValue(F32 time, F32 duration);
+ LLVector3 interp(F32 u, PositionKey& before, PositionKey& after);
+
+ InterpolationType mInterpolationType;
+ S32 mNumKeys;
+ LLPtrSkipMap<F32, PositionKey*> mKeys;
+ PositionKey mLoopInKey;
+ PositionKey mLoopOutKey;
+ };
+
+ //-------------------------------------------------------------------------
+ // JointMotion
+ //-------------------------------------------------------------------------
+ class JointMotion
+ {
+ public:
+ PositionCurve mPositionCurve;
+ RotationCurve mRotationCurve;
+ ScaleCurve mScaleCurve;
+ std::string mJointName;
+ U32 mUsage;
+ LLJoint::JointPriority mPriority;
+
+ void update(LLJointState *joint_state, F32 time, F32 duration);
+ };
+
+ //-------------------------------------------------------------------------
+ // JointMotionList
+ //-------------------------------------------------------------------------
+ class JointMotionList
+ {
+ public:
+ U32 mNumJointMotions;
+ JointMotion* mJointMotionArray;
+ F32 mDuration;
+ BOOL mLoop;
+ F32 mLoopInPoint;
+ F32 mLoopOutPoint;
+ F32 mEaseInDuration;
+ F32 mEaseOutDuration;
+ LLJoint::JointPriority mBasePriority;
+ LLHandMotion::eHandPose mHandPose;
+ LLJoint::JointPriority mMaxPriority;
+ LLLinkedList<JointConstraintSharedData> mConstraints;
+ LLBBoxLocal mPelvisBBox;
+ public:
+ JointMotionList() : mNumJointMotions(0), mJointMotionArray(NULL) {};
+ ~JointMotionList() { mConstraints.deleteAllData(); delete [] mJointMotionArray; }
+ U32 dumpDiagInfo();
+ };
+
+
+protected:
+ static LLVFS* sVFS;
+
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ JointMotionList* mJointMotionList;
+ LLJointState* mJointStates;
+ LLJoint* mPelvisp;
+ LLCharacter* mCharacter;
+ std::string mEmoteName;
+ LLLinkedList<JointConstraint> mConstraints;
+ U32 mLastSkeletonSerialNum;
+ F32 mLastUpdateTime;
+ F32 mLastLoopedTime;
+ AssetStatus mAssetStatus;
+};
+
+class LLKeyframeDataCache
+{
+public:
+ //FIXME: implement this as an actual singleton member of LLKeyframeMotion
+ LLKeyframeDataCache(){};
+ ~LLKeyframeDataCache();
+
+ typedef std::map<LLUUID, class LLKeyframeMotion::JointMotionList*> LLKeyframeDataMap;
+ static LLKeyframeDataMap sKeyframeDataMap;
+
+ static void addKeyframeData(const LLUUID& id, LLKeyframeMotion::JointMotionList*);
+ static LLKeyframeMotion::JointMotionList* getKeyframeData(const LLUUID& id);
+
+ static void removeKeyframeData(const LLUUID& id);
+
+ //print out diagnostic info
+ static void dumpDiagInfo();
+ static void clear();
+};
+
+#endif // LL_LLKEYFRAMEMOTION_H
+
diff --git a/indra/llcharacter/llkeyframemotionparam.cpp b/indra/llcharacter/llkeyframemotionparam.cpp
new file mode 100644
index 0000000000..c57079fc2b
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotionparam.cpp
@@ -0,0 +1,442 @@
+/**
+ * @file llkeyframemotionparam.cpp
+ * @brief Implementation of LLKeyframeMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframemotionparam.h"
+#include "llcharacter.h"
+#include "llmath.h"
+#include "m3math.h"
+#include "lldir.h"
+#include "llanimationstates.h"
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// sortFunc()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::sortFunc(ParameterizedMotion *new_motion, ParameterizedMotion *tested_motion)
+{
+ return (new_motion->second < tested_motion->second);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotionParam::LLKeyframeMotionParam( const LLUUID &id) : LLMotion(id)
+{
+ mJointStates = NULL;
+ mDefaultKeyframeMotion = NULL;
+ mCharacter = NULL;
+
+ mEaseInDuration = 0.f;
+ mEaseOutDuration = 0.f;
+ mDuration = 0.f;
+ mPriority = LLJoint::LOW_PRIORITY;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeMotionParam()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeMotionParam::~LLKeyframeMotionParam()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ delete paramMotion->first;
+ }
+ delete motionList;
+ }
+
+ mParameterizedMotions.removeAll();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeMotionParam::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ if (!loadMotions())
+ {
+ return STATUS_FAILURE;
+ }
+
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onInitialize(character);
+
+ if (paramMotion->first->getDuration() > mEaseInDuration)
+ {
+ mEaseInDuration = paramMotion->first->getEaseInDuration();
+ }
+
+ if (paramMotion->first->getEaseOutDuration() > mEaseOutDuration)
+ {
+ mEaseOutDuration = paramMotion->first->getEaseOutDuration();
+ }
+
+ if (paramMotion->first->getDuration() > mDuration)
+ {
+ mDuration = paramMotion->first->getDuration();
+ }
+
+ if (paramMotion->first->getPriority() > mPriority)
+ {
+ mPriority = paramMotion->first->getPriority();
+ }
+
+ LLPose *pose = paramMotion->first->getPose();
+
+ mPoseBlender.addMotion(paramMotion->first);
+ for (LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState())
+ {
+ LLPose *blendedPose = mPoseBlender.getBlendedPose();
+ blendedPose->addJointState(jsp);
+ }
+ }
+ }
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::onActivate()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->activate();
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::onUpdate(F32 time, U8* joint_mask)
+{
+ F32 weightFactor = 1.f / (F32)mParameterizedMotions.length();
+ U32 i;
+
+ // zero out all pose weights
+ for (i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+// llinfos << "Weight for pose " << paramMotion->first->getName() << " is " << paramMotion->first->getPose()->getWeight() << llendl;
+ paramMotion->first->getPose()->setWeight(0.f);
+ }
+ }
+
+
+ for (i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ std::string *paramName = mParameterizedMotions.getIndexAt(i);
+ F32* paramValue = (F32 *)mCharacter->getAnimationData(*paramName);
+ ParameterizedMotion* firstMotion = NULL;
+ ParameterizedMotion* secondMotion = NULL;
+
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onUpdate(time, joint_mask);
+
+ F32 distToParam = paramMotion->second - *paramValue;
+
+ if ( distToParam <= 0.f)
+ {
+ // keep track of the motion closest to the parameter value
+ firstMotion = paramMotion;
+ }
+ else
+ {
+ // we've passed the parameter value
+ // so store the first motion we find as the second one we want to blend...
+ if (firstMotion && !secondMotion )
+ {
+ secondMotion = paramMotion;
+ }
+ //...or, if we've seen no other motion so far, make sure we blend to this only
+ else if (!firstMotion)
+ {
+ firstMotion = paramMotion;
+ secondMotion = paramMotion;
+ }
+ }
+ }
+
+ LLPose *firstPose;
+ LLPose *secondPose;
+
+ if (firstMotion)
+ firstPose = firstMotion->first->getPose();
+ else
+ firstPose = NULL;
+
+ if (secondMotion)
+ secondPose = secondMotion->first->getPose();
+ else
+ secondPose = NULL;
+
+ // now modify weight of the subanim (only if we are blending between two motions)
+ if (firstMotion && secondMotion)
+ {
+ if (firstMotion == secondMotion)
+ {
+ firstPose->setWeight(weightFactor);
+ }
+ else if (firstMotion->second == secondMotion->second)
+ {
+ firstPose->setWeight(0.5f * weightFactor);
+ secondPose->setWeight(0.5f * weightFactor);
+ }
+ else
+ {
+ F32 first_weight = 1.f -
+ ((llclamp(*paramValue - firstMotion->second, 0.f, (secondMotion->second - firstMotion->second))) /
+ (secondMotion->second - firstMotion->second));
+ first_weight = llclamp(first_weight, 0.f, 1.f);
+
+ F32 second_weight = 1.f - first_weight;
+
+ firstPose->setWeight(first_weight * weightFactor);
+ secondPose->setWeight(second_weight * weightFactor);
+
+// llinfos << "Parameter " << *paramName << ": " << *paramValue << llendl;
+// llinfos << "Weights " << firstPose->getWeight() << " " << secondPose->getWeight() << llendl;
+ }
+ }
+ else if (firstMotion && !secondMotion)
+ {
+ firstPose->setWeight(weightFactor);
+ }
+ }
+
+ // blend poses
+ mPoseBlender.blendAndApply();
+
+ llinfos << "Param Motion weight " << mPoseBlender.getBlendedPose()->getWeight() << llendl;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotionParam::onDeactivate()
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ paramMotion->first->onDeactivate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::addKeyframeMotion()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value)
+{
+ LLMotion *newMotion = mCharacter->createMotion( id );
+
+ if (!newMotion)
+ {
+ return FALSE;
+ }
+
+ newMotion->setName(name);
+
+ // make sure a list of motions exists for this parameter
+ LLLinkedList< ParameterizedMotion > *motionList;
+ if (mParameterizedMotions.getValue(param))
+ {
+ motionList = *mParameterizedMotions.getValue(param);
+ }
+ else
+ {
+ motionList = new LLLinkedList< ParameterizedMotion >;
+ motionList->setInsertBefore(sortFunc);
+ mParameterizedMotions.addToHead(param, motionList);
+ }
+
+ // now add motion to this list
+ ParameterizedMotion *parameterizedMotion = new ParameterizedMotion(newMotion, value);
+
+ motionList->addDataSorted(parameterizedMotion);
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeMotionParam::setDefaultKeyframeMotion()
+//-----------------------------------------------------------------------------
+void LLKeyframeMotionParam::setDefaultKeyframeMotion(char *name)
+{
+ for (U32 i = 0; i < mParameterizedMotions.length(); i++)
+ {
+ LLLinkedList< ParameterizedMotion > *motionList = *mParameterizedMotions.getValueAt(i);
+ for (ParameterizedMotion* paramMotion = motionList->getFirstData(); paramMotion; paramMotion = motionList->getNextData())
+ {
+ if (paramMotion->first->getName() == name)
+ {
+ mDefaultKeyframeMotion = paramMotion->first;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadMotions()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeMotionParam::loadMotions()
+{
+ //-------------------------------------------------------------------------
+ // Load named file by concatenating the character prefix with the motion name.
+ // Load data into a buffer to be parsed.
+ //-------------------------------------------------------------------------
+ char path[LL_MAX_PATH]; /* Flawfinder: ignore */
+ snprintf( path, sizeof(path), "%s_%s.llp",
+ gDirUtilp->getExpandedFilename(LL_PATH_MOTIONS,mCharacter->getAnimationPrefix()).c_str(),
+ getName().c_str() ); /* Flawfinder: ignore */
+
+ //-------------------------------------------------------------------------
+ // open the file
+ //-------------------------------------------------------------------------
+ S32 fileSize = 0;
+ apr_file_t* fp = ll_apr_file_open(path, LL_APR_R, &fileSize);
+ if (!fp || fileSize == 0)
+ {
+ llinfos << "ERROR: can't open: " << path << llendl;
+ return FALSE;
+ }
+
+ // allocate a text buffer
+ char *text = new char[ fileSize+1 ];
+ if ( !text )
+ {
+ llinfos << "ERROR: can't allocated keyframe text buffer." << llendl;
+ apr_file_close(fp);
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // load data from file into buffer
+ //-------------------------------------------------------------------------
+ bool error = false;
+ char *p = text;
+ while ( 1 )
+ {
+ if (apr_file_eof(fp) == APR_EOF)
+ {
+ break;
+ }
+ if (apr_file_gets(p, 1024, fp) != APR_SUCCESS)
+ {
+ error = true;
+ break;
+ }
+ while ( *(++p) )
+ ;
+ }
+
+ //-------------------------------------------------------------------------
+ // close the file
+ //-------------------------------------------------------------------------
+ apr_file_close( fp );
+
+ //-------------------------------------------------------------------------
+ // check for error
+ //-------------------------------------------------------------------------
+ llassert( p <= (text+fileSize) );
+
+ if ( error )
+ {
+ llinfos << "ERROR: error while reading from " << path << llendl;
+ delete [] text;
+ return FALSE;
+ }
+
+ llinfos << "Loading parametric keyframe data for: " << getName() << llendl;
+
+ //-------------------------------------------------------------------------
+ // parse the text and build keyframe data structures
+ //-------------------------------------------------------------------------
+ p = text;
+ S32 num;
+ char strA[80]; /* Flawfinder: ignore */
+ char strB[80]; /* Flawfinder: ignore */
+ F32 floatA = 0.0f;
+
+
+ //-------------------------------------------------------------------------
+ // get priority
+ //-------------------------------------------------------------------------
+ BOOL isFirstMotion = TRUE;
+ num = sscanf(p, "%79s %79s %f", strA, strB, &floatA);
+
+ while(1)
+ {
+ if (num == 0 || num == EOF) break;
+ if ((num != 3))
+ {
+ llinfos << "WARNING: can't read parametric motion" << llendl;
+ delete [] text;
+ return FALSE;
+ }
+
+ addKeyframeMotion(strA, gAnimLibrary.stringToAnimState(strA), strB, floatA);
+ if (isFirstMotion)
+ {
+ isFirstMotion = FALSE;
+ setDefaultKeyframeMotion(strA);
+ }
+
+ p = strstr(p, "\n");
+ if (!p)
+ {
+ break;
+ }
+
+ p++;
+ num = sscanf(p, "%79s %79s %f", strA, strB, &floatA);
+ }
+
+ delete [] text;
+ return TRUE;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframemotionparam.h b/indra/llcharacter/llkeyframemotionparam.h
new file mode 100644
index 0000000000..6b23100d5b
--- /dev/null
+++ b/indra/llcharacter/llkeyframemotionparam.h
@@ -0,0 +1,138 @@
+/**
+ * @file llkeyframemotionparam.h
+ * @brief Implementation of LLKeframeMotionParam class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEMOTIONPARAM_H
+#define LL_LLKEYFRAMEMOTIONPARAM_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+
+#include <string>
+
+#include "llmotion.h"
+#include "lljointstate.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "linked_lists.h"
+#include "llkeyframemotion.h"
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeMotionParam
+//-----------------------------------------------------------------------------
+class LLKeyframeMotionParam :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLKeyframeMotionParam(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeMotionParam();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeMotionParam(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() {
+ return TRUE;
+ }
+
+ // motions must report their total duration
+ virtual F32 getDuration() {
+ return mDuration;
+ }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() {
+ return mEaseInDuration;
+ }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() {
+ return mEaseOutDuration;
+ }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() {
+ return mPriority;
+ }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_KEYFRAME; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ virtual LLPose* getPose() { return mPoseBlender.getBlendedPose();}
+
+protected:
+ //-------------------------------------------------------------------------
+ // new functions defined by this subclass
+ //-------------------------------------------------------------------------
+ typedef std::pair<LLMotion*, F32> ParameterizedMotion;
+
+ // add a motion and associated parameter triplet
+ BOOL addKeyframeMotion(char *name, const LLUUID &id, char *param, F32 value);
+
+ // set default motion for LOD and retrieving blend constants
+ void setDefaultKeyframeMotion(char *);
+
+ static BOOL sortFunc(ParameterizedMotion *new_motion, ParameterizedMotion *tested_motion);
+
+ BOOL loadMotions();
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+
+ typedef LLLinkedList < ParameterizedMotion > motion_list_t;
+ LLAssocList <std::string, motion_list_t* > mParameterizedMotions;
+ LLJointState* mJointStates;
+ LLMotion* mDefaultKeyframeMotion;
+ LLCharacter* mCharacter;
+ LLPoseBlender mPoseBlender;
+
+ F32 mEaseInDuration;
+ F32 mEaseOutDuration;
+ F32 mDuration;
+ LLJoint::JointPriority mPriority;
+
+ LLUUID mTransactionID;
+};
+
+#endif // LL_LLKEYFRAMEMOTIONPARAM_H
diff --git a/indra/llcharacter/llkeyframestandmotion.cpp b/indra/llcharacter/llkeyframestandmotion.cpp
new file mode 100644
index 0000000000..6810fb450f
--- /dev/null
+++ b/indra/llcharacter/llkeyframestandmotion.cpp
@@ -0,0 +1,320 @@
+/**
+ * @file llkeyframestandmotion.cpp
+ * @brief Implementation of LLKeyframeStandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframestandmotion.h"
+#include "llcharacter.h"
+
+//-----------------------------------------------------------------------------
+// Macros and consts
+//-----------------------------------------------------------------------------
+#define GO_TO_KEY_POSE 1
+#define MIN_TRACK_SPEED 0.01f
+const F32 ROTATION_THRESHOLD = 0.6f;
+const F32 POSITION_THRESHOLD = 0.1f;
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeStandMotion::LLKeyframeStandMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mFlipFeet = FALSE;
+ mCharacter = NULL;
+
+ // create kinematic hierarchy
+ mPelvisJoint.addChild( &mHipLeftJoint );
+ mHipLeftJoint.addChild( &mKneeLeftJoint );
+ mKneeLeftJoint.addChild( &mAnkleLeftJoint );
+ mPelvisJoint.addChild( &mHipRightJoint );
+ mHipRightJoint.addChild( &mKneeRightJoint );
+ mKneeRightJoint.addChild( &mAnkleRightJoint );
+
+ mPelvisState = NULL;
+
+ mHipLeftState = NULL;
+ mKneeLeftState = NULL;
+ mAnkleLeftState = NULL;
+
+ mHipRightState = NULL;
+ mKneeRightState = NULL;
+ mAnkleRightState = NULL;
+
+ mTrackAnkles = TRUE;
+
+ mFrameNum = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeStandMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeStandMotion::~LLKeyframeStandMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeStandMotion::onInitialize(LLCharacter *character)
+{
+ // save character pointer for later use
+ mCharacter = character;
+
+ mFlipFeet = FALSE;
+
+ // load keyframe data, setup pose and joint states
+ LLMotion::LLMotionInitStatus status = LLKeyframeMotion::onInitialize(character);
+ if ( status == STATUS_FAILURE )
+ {
+ return status;
+ }
+
+ // find the necessary joint states
+ LLPose *pose = getPose();
+ mPelvisState = pose->findJointState("mPelvis");
+
+ mHipLeftState = pose->findJointState("mHipLeft");
+ mKneeLeftState = pose->findJointState("mKneeLeft");
+ mAnkleLeftState = pose->findJointState("mAnkleLeft");
+
+ mHipRightState = pose->findJointState("mHipRight");
+ mKneeRightState = pose->findJointState("mKneeRight");
+ mAnkleRightState = pose->findJointState("mAnkleRight");
+
+ if ( !mPelvisState ||
+ !mHipLeftState ||
+ !mKneeLeftState ||
+ !mAnkleLeftState ||
+ !mHipRightState ||
+ !mKneeRightState ||
+ !mAnkleRightState )
+ {
+ llinfos << getName() << ": Can't find necessary joint states" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeStandMotion::onActivate()
+{
+ //-------------------------------------------------------------------------
+ // setup the IK solvers
+ //-------------------------------------------------------------------------
+ mIKLeft.setPoleVector( LLVector3(1.0f, 0.0f, 0.0f));
+ mIKRight.setPoleVector( LLVector3(1.0f, 0.0f, 0.0f));
+ mIKLeft.setBAxis( LLVector3(0.05f, 1.0f, 0.0f));
+ mIKRight.setBAxis( LLVector3(-0.05f, 1.0f, 0.0f));
+
+ mLastGoodPelvisRotation.loadIdentity();
+ mLastGoodPosition.clearVec();
+
+ mFrameNum = 0;
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeStandMotion::onDeactivate()
+{
+ LLKeyframeMotion::onDeactivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeStandMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeStandMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ //-------------------------------------------------------------------------
+ // let the base class update the cycle
+ //-------------------------------------------------------------------------
+ BOOL status = LLKeyframeMotion::onUpdate(time, joint_mask);
+ if (!status)
+ {
+ return FALSE;
+ }
+
+ LLVector3 root_world_pos = mPelvisState->getJoint()->getParent()->getWorldPosition();
+
+ // have we received a valid world position for this avatar?
+ if (root_world_pos.isExactlyZero())
+ {
+ return TRUE;
+ }
+
+ //-------------------------------------------------------------------------
+ // Stop tracking (start locking) ankles once ease in is done.
+ // Setting this here ensures we track until we get valid foot position.
+ //-------------------------------------------------------------------------
+ if (dot(mPelvisState->getJoint()->getWorldRotation(), mLastGoodPelvisRotation) < ROTATION_THRESHOLD)
+ {
+ mLastGoodPelvisRotation = mPelvisState->getJoint()->getWorldRotation();
+ mLastGoodPelvisRotation.normQuat();
+ mTrackAnkles = TRUE;
+ }
+ else if ((mCharacter->getCharacterPosition() - mLastGoodPosition).magVecSquared() > POSITION_THRESHOLD)
+ {
+ mLastGoodPosition = mCharacter->getCharacterPosition();
+ mTrackAnkles = TRUE;
+ }
+ else if (mPose.getWeight() < 1.f)
+ {
+ mTrackAnkles = TRUE;
+ }
+
+
+ //-------------------------------------------------------------------------
+ // propagate joint positions to internal versions
+ //-------------------------------------------------------------------------
+ mPelvisJoint.setPosition(
+ root_world_pos +
+ mPelvisState->getPosition() );
+
+ mHipLeftJoint.setPosition( mHipLeftState->getJoint()->getPosition() );
+ mKneeLeftJoint.setPosition( mKneeLeftState->getJoint()->getPosition() );
+ mAnkleLeftJoint.setPosition( mAnkleLeftState->getJoint()->getPosition() );
+
+ mHipLeftJoint.setScale( mHipLeftState->getJoint()->getScale() );
+ mKneeLeftJoint.setScale( mKneeLeftState->getJoint()->getScale() );
+ mAnkleLeftJoint.setScale( mAnkleLeftState->getJoint()->getScale() );
+
+ mHipRightJoint.setPosition( mHipRightState->getJoint()->getPosition() );
+ mKneeRightJoint.setPosition( mKneeRightState->getJoint()->getPosition() );
+ mAnkleRightJoint.setPosition( mAnkleRightState->getJoint()->getPosition() );
+
+ mHipRightJoint.setScale( mHipRightState->getJoint()->getScale() );
+ mKneeRightJoint.setScale( mKneeRightState->getJoint()->getScale() );
+ mAnkleRightJoint.setScale( mAnkleRightState->getJoint()->getScale() );
+ //-------------------------------------------------------------------------
+ // propagate joint rotations to internal versions
+ //-------------------------------------------------------------------------
+ mPelvisJoint.setRotation( mPelvisState->getJoint()->getWorldRotation() );
+
+#if GO_TO_KEY_POSE
+ mHipLeftJoint.setRotation( mHipLeftState->getRotation() );
+ mKneeLeftJoint.setRotation( mKneeLeftState->getRotation() );
+ mAnkleLeftJoint.setRotation( mAnkleLeftState->getRotation() );
+
+ mHipRightJoint.setRotation( mHipRightState->getRotation() );
+ mKneeRightJoint.setRotation( mKneeRightState->getRotation() );
+ mAnkleRightJoint.setRotation( mAnkleRightState->getRotation() );
+#else
+ mHipLeftJoint.setRotation( mHipLeftState->getJoint()->getRotation() );
+ mKneeLeftJoint.setRotation( mKneeLeftState->getJoint()->getRotation() );
+ mAnkleLeftJoint.setRotation( mAnkleLeftState->getJoint()->getRotation() );
+
+ mHipRightJoint.setRotation( mHipRightState->getJoint()->getRotation() );
+ mKneeRightJoint.setRotation( mKneeRightState->getJoint()->getRotation() );
+ mAnkleRightJoint.setRotation( mAnkleRightState->getJoint()->getRotation() );
+#endif
+
+ // need to wait for underlying keyframe motion to affect the skeleton
+ if (mFrameNum == 2)
+ {
+ mIKLeft.setupJoints( &mHipLeftJoint, &mKneeLeftJoint, &mAnkleLeftJoint, &mTargetLeft );
+ mIKRight.setupJoints( &mHipRightJoint, &mKneeRightJoint, &mAnkleRightJoint, &mTargetRight );
+ }
+ else if (mFrameNum < 2)
+ {
+ mFrameNum++;
+ return TRUE;
+ }
+
+ mFrameNum++;
+
+ //-------------------------------------------------------------------------
+ // compute target position by projecting ankles to the ground
+ //-------------------------------------------------------------------------
+ if ( mTrackAnkles )
+ {
+ mCharacter->getGround( mAnkleLeftJoint.getWorldPosition(), mPositionLeft, mNormalLeft);
+ mCharacter->getGround( mAnkleRightJoint.getWorldPosition(), mPositionRight, mNormalRight);
+
+ mTargetLeft.setPosition( mPositionLeft );
+ mTargetRight.setPosition( mPositionRight );
+ }
+
+ //-------------------------------------------------------------------------
+ // update solvers
+ //-------------------------------------------------------------------------
+ mIKLeft.solve();
+ mIKRight.solve();
+
+ //-------------------------------------------------------------------------
+ // make ankle rotation conform to the ground
+ //-------------------------------------------------------------------------
+ if ( mTrackAnkles )
+ {
+ LLVector4 dirLeft4 = mAnkleLeftJoint.getWorldMatrix().getFwdRow4();
+ LLVector4 dirRight4 = mAnkleRightJoint.getWorldMatrix().getFwdRow4();
+ LLVector3 dirLeft = vec4to3( dirLeft4 );
+ LLVector3 dirRight = vec4to3( dirRight4 );
+
+ LLVector3 up;
+ LLVector3 dir;
+ LLVector3 left;
+
+ up = mNormalLeft;
+ up.normVec();
+ if (mFlipFeet)
+ {
+ up *= -1.0f;
+ }
+ dir = dirLeft;
+ dir.normVec();
+ left = up % dir;
+ left.normVec();
+ dir = left % up;
+ mRotationLeft = LLQuaternion( dir, left, up );
+
+ up = mNormalRight;
+ up.normVec();
+ if (mFlipFeet)
+ {
+ up *= -1.0f;
+ }
+ dir = dirRight;
+ dir.normVec();
+ left = up % dir;
+ left.normVec();
+ dir = left % up;
+ mRotationRight = LLQuaternion( dir, left, up );
+ }
+ mAnkleLeftJoint.setWorldRotation( mRotationLeft );
+ mAnkleRightJoint.setWorldRotation( mRotationRight );
+
+ //-------------------------------------------------------------------------
+ // propagate joint rotations to joint states
+ //-------------------------------------------------------------------------
+ mHipLeftState->setRotation( mHipLeftJoint.getRotation() );
+ mKneeLeftState->setRotation( mKneeLeftJoint.getRotation() );
+ mAnkleLeftState->setRotation( mAnkleLeftJoint.getRotation() );
+
+ mHipRightState->setRotation( mHipRightJoint.getRotation() );
+ mKneeRightState->setRotation( mKneeRightJoint.getRotation() );
+ mAnkleRightState->setRotation( mAnkleRightJoint.getRotation() );
+
+ //llinfos << "Stand drift amount " << (mCharacter->getCharacterPosition() - mLastGoodPosition).magVec() << llendl;
+
+// llinfos << "DEBUG: " << speed << " : " << mTrackAnkles << llendl;
+ return TRUE;
+}
+
+// End
diff --git a/indra/llcharacter/llkeyframestandmotion.h b/indra/llcharacter/llkeyframestandmotion.h
new file mode 100644
index 0000000000..a82c92d07f
--- /dev/null
+++ b/indra/llcharacter/llkeyframestandmotion.h
@@ -0,0 +1,98 @@
+/**
+ * @file llkeyframestandmotion.h
+ * @brief Implementation of LLKeyframeStandMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMESTANDMOTION_H
+#define LL_LLKEYFRAMESTANDMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "lljointsolverrp3.h"
+
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeStandMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeStandMotion :
+ public LLKeyframeMotion
+{
+public:
+ // Constructor
+ LLKeyframeStandMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeStandMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeStandMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+
+ BOOL mFlipFeet;
+
+ LLJointState *mPelvisState;
+
+ LLJointState *mHipLeftState;
+ LLJointState *mKneeLeftState;
+ LLJointState *mAnkleLeftState;
+
+ LLJointState *mHipRightState;
+ LLJointState *mKneeRightState;
+ LLJointState *mAnkleRightState;
+
+ LLJoint mPelvisJoint;
+
+ LLJoint mHipLeftJoint;
+ LLJoint mKneeLeftJoint;
+ LLJoint mAnkleLeftJoint;
+ LLJoint mTargetLeft;
+
+ LLJoint mHipRightJoint;
+ LLJoint mKneeRightJoint;
+ LLJoint mAnkleRightJoint;
+ LLJoint mTargetRight;
+
+ LLJointSolverRP3 mIKLeft;
+ LLJointSolverRP3 mIKRight;
+
+ LLVector3 mPositionLeft;
+ LLVector3 mPositionRight;
+ LLVector3 mNormalLeft;
+ LLVector3 mNormalRight;
+ LLQuaternion mRotationLeft;
+ LLQuaternion mRotationRight;
+
+ LLQuaternion mLastGoodPelvisRotation;
+ LLVector3 mLastGoodPosition;
+ BOOL mTrackAnkles;
+
+ S32 mFrameNum;
+};
+
+#endif // LL_LLKEYFRAMESTANDMOTION_H
+
diff --git a/indra/llcharacter/llkeyframewalkmotion.cpp b/indra/llcharacter/llkeyframewalkmotion.cpp
new file mode 100644
index 0000000000..930427aa7a
--- /dev/null
+++ b/indra/llcharacter/llkeyframewalkmotion.cpp
@@ -0,0 +1,379 @@
+/**
+ * @file llkeyframewalkmotion.cpp
+ * @brief Implementation of LLKeyframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llkeyframewalkmotion.h"
+#include "llcharacter.h"
+#include "llmath.h"
+#include "m3math.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+const F32 MAX_WALK_PLAYBACK_SPEED = 8.f; // max m/s for which we adjust walk cycle speed
+
+const F32 MIN_WALK_SPEED = 0.1f; // minimum speed at which we use velocity for down foot detection
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 SPEED_ADJUST_MAX = 2.5f; // maximum adjustment of walk animation playback speed
+const F32 SPEED_ADJUST_MAX_SEC = 3.f; // maximum adjustment to walk animation playback speed for a second
+const F32 DRIFT_COMP_MAX_TOTAL = 0.07f;//0.55f; // maximum drift compensation overall, in any direction
+const F32 DRIFT_COMP_MAX_SPEED = 4.f; // speed at which drift compensation total maxes out
+const F32 MAX_ROLL = 0.6f;
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLKeyframeWalkMotion::LLKeyframeWalkMotion(const LLUUID &id) : LLKeyframeMotion(id)
+{
+ mRealTimeLast = 0.0f;
+ mAdjTimeLast = 0.0f;
+ mCharacter = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLKeyframeWalkMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLKeyframeWalkMotion::~LLKeyframeWalkMotion()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLKeyframeWalkMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ return LLKeyframeMotion::onInitialize(character);
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeWalkMotion::onActivate()
+{
+ mRealTimeLast = 0.0f;
+ mAdjTimeLast = 0.0f;
+
+ return LLKeyframeMotion::onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLKeyframeWalkMotion::onDeactivate()
+{
+ mCharacter->removeAnimationData("Down Foot");
+ LLKeyframeMotion::onDeactivate();
+}
+
+//-----------------------------------------------------------------------------
+// LLKeyframeWalkMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLKeyframeWalkMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ // compute time since last update
+ F32 deltaTime = time - mRealTimeLast;
+
+ void* speed_ptr = mCharacter->getAnimationData("Walk Speed");
+ F32 speed = (speed_ptr) ? *((F32 *)speed_ptr) : 1.f;
+
+ // adjust the passage of time accordingly
+ F32 adjusted_time = mAdjTimeLast + (deltaTime * speed);
+
+ // save time for next update
+ mRealTimeLast = time;
+ mAdjTimeLast = adjusted_time;
+
+ // handle wrap around
+ if (adjusted_time < 0.0f)
+ {
+ adjusted_time = getDuration() + fmod(adjusted_time, getDuration());
+ }
+
+ // let the base class update the cycle
+ return LLKeyframeMotion::onUpdate( adjusted_time, joint_mask );
+}
+
+// End
+
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLWalkAdjustMotion::LLWalkAdjustMotion(const LLUUID &id) : LLMotion(id)
+{
+ mLastTime = 0.f;
+ mName = "walk_adjust";
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLWalkAdjustMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+ mLeftAnkleJoint = mCharacter->getJoint("mAnkleLeft");
+ mRightAnkleJoint = mCharacter->getJoint("mAnkleRight");
+
+ mPelvisJoint = mCharacter->getJoint("mPelvis");
+ mPelvisState.setJoint( mPelvisJoint );
+ if ( !mPelvisJoint )
+ {
+ llwarns << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS);
+ addJointState( &mPelvisState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLWalkAdjustMotion::onActivate()
+{
+ mAvgCorrection = 0.f;
+ mSpeedAdjust = 0.f;
+ mAnimSpeed = 0.f;
+ mAvgSpeed = 0.f;
+ mRelativeDir = 1.f;
+ mPelvisState.setPosition(LLVector3::zero);
+ // store ankle positions for next frame
+ mLastLeftAnklePos = mCharacter->getPosGlobalFromAgent(mLeftAnkleJoint->getWorldPosition());
+ mLastRightAnklePos = mCharacter->getPosGlobalFromAgent(mRightAnkleJoint->getWorldPosition());
+
+ F32 leftAnkleOffset = (mLeftAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
+ F32 rightAnkleOffset = (mRightAnkleJoint->getWorldPosition() - mCharacter->getCharacterPosition()).magVec();
+ mAnkleOffset = llmax(leftAnkleOffset, rightAnkleOffset);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLWalkAdjustMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 footCorrection;
+ LLVector3 vel = mCharacter->getCharacterVelocity() * mCharacter->getTimeDilation();
+ F32 deltaTime = llclamp(time - mLastTime, 0.f, MAX_TIME_DELTA);
+ mLastTime = time;
+
+ LLQuaternion inv_rotation = ~mPelvisJoint->getWorldRotation();
+
+ // get speed and normalize velocity vector
+ LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
+ F32 speed = llmin(vel.normVec(), MAX_WALK_PLAYBACK_SPEED);
+ mAvgSpeed = lerp(mAvgSpeed, speed, LLCriticalDamp::getInterpolant(0.2f));
+
+ // calculate facing vector in pelvis-local space
+ // (either straight forward or back, depending on velocity)
+ LLVector3 localVel = vel * inv_rotation;
+ if (localVel.mV[VX] > 0.f)
+ {
+ mRelativeDir = 1.f;
+ }
+ else if (localVel.mV[VX] < 0.f)
+ {
+ mRelativeDir = -1.f;
+ }
+
+ // calculate world-space foot drift
+ LLVector3 leftFootDelta;
+ LLVector3 leftFootWorldPosition = mLeftAnkleJoint->getWorldPosition();
+ LLVector3d leftFootGlobalPosition = mCharacter->getPosGlobalFromAgent(leftFootWorldPosition);
+ leftFootDelta.setVec(mLastLeftAnklePos - leftFootGlobalPosition);
+ mLastLeftAnklePos = leftFootGlobalPosition;
+
+ LLVector3 rightFootDelta;
+ LLVector3 rightFootWorldPosition = mRightAnkleJoint->getWorldPosition();
+ LLVector3d rightFootGlobalPosition = mCharacter->getPosGlobalFromAgent(rightFootWorldPosition);
+ rightFootDelta.setVec(mLastRightAnklePos - rightFootGlobalPosition);
+ mLastRightAnklePos = rightFootGlobalPosition;
+
+ // find foot drift along velocity vector
+ if (mAvgSpeed > 0.1)
+ {
+ // walking/running
+ F32 leftFootDriftAmt = leftFootDelta * vel;
+ F32 rightFootDriftAmt = rightFootDelta * vel;
+
+ if (rightFootDriftAmt > leftFootDriftAmt)
+ {
+ footCorrection = rightFootDelta;
+ } else
+ {
+ footCorrection = leftFootDelta;
+ }
+ }
+ else
+ {
+ mAvgSpeed = ang_vel.magVec() * mAnkleOffset;
+ mRelativeDir = 1.f;
+
+ // standing/turning
+ // find the lower foot
+ if (leftFootWorldPosition.mV[VZ] < rightFootWorldPosition.mV[VZ])
+ {
+ // pivot on left foot
+ footCorrection = leftFootDelta;
+ }
+ else
+ {
+ // pivot on right foot
+ footCorrection = rightFootDelta;
+ }
+ }
+
+ // rotate into avatar coordinates
+ footCorrection = footCorrection * inv_rotation;
+
+ // calculate ideal pelvis offset so that foot is glued to ground and damp towards it
+ // the amount of foot slippage this frame + the offset applied last frame
+ mPelvisOffset = mPelvisState.getPosition() + lerp(LLVector3::zero, footCorrection, LLCriticalDamp::getInterpolant(0.2f));
+
+ // pelvis drift (along walk direction)
+ mAvgCorrection = lerp(mAvgCorrection, footCorrection.mV[VX] * mRelativeDir, LLCriticalDamp::getInterpolant(0.1f));
+
+ // calculate average velocity of foot slippage
+ F32 footSlipVelocity = (deltaTime != 0.f) ? (-mAvgCorrection / deltaTime) : 0.f;
+
+ F32 newSpeedAdjust = 0.f;
+
+ // modulate speed by dot products of facing and velocity
+ // so that if we are moving sideways, we slow down the animation
+ // and if we're moving backward, we walk backward
+
+ F32 directional_factor = localVel.mV[VX] * mRelativeDir;
+ if (speed > 0.1f)
+ {
+ // calculate ratio of desired foot velocity to detected foot velocity
+ newSpeedAdjust = llclamp(footSlipVelocity - mAvgSpeed * (1.f - directional_factor),
+ -SPEED_ADJUST_MAX, SPEED_ADJUST_MAX);
+ newSpeedAdjust = lerp(mSpeedAdjust, newSpeedAdjust, LLCriticalDamp::getInterpolant(0.2f));
+
+ F32 speedDelta = newSpeedAdjust - mSpeedAdjust;
+ speedDelta = llclamp(speedDelta, -SPEED_ADJUST_MAX_SEC * deltaTime, SPEED_ADJUST_MAX_SEC * deltaTime);
+
+ mSpeedAdjust = mSpeedAdjust + speedDelta;
+ }
+ else
+ {
+ mSpeedAdjust = lerp(mSpeedAdjust, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ mAnimSpeed = (mAvgSpeed + mSpeedAdjust) * mRelativeDir;
+// char debug_text[64];
+// sprintf(debug_text, "Foot slip vel: %.2f", footSlipVelocity);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Speed: %.2f", mAvgSpeed);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Speed Adjust: %.2f", mSpeedAdjust);
+// mCharacter->addDebugText(debug_text);
+// sprintf(debug_text, "Animation Playback Speed: %.2f", mAnimSpeed);
+// mCharacter->addDebugText(debug_text);
+ mCharacter->setAnimationData("Walk Speed", &mAnimSpeed);
+
+ // clamp pelvis offset to a 90 degree arc behind the nominal position
+ F32 drift_comp_max = llclamp(speed, 0.f, DRIFT_COMP_MAX_SPEED) / DRIFT_COMP_MAX_SPEED;
+ drift_comp_max *= DRIFT_COMP_MAX_TOTAL;
+
+ LLVector3 currentPelvisPos = mPelvisState.getJoint()->getPosition();
+
+ // NB: this is an ADDITIVE amount that is accumulated every frame, so clamping it alone won't do the trick
+ // must clamp with absolute position of pelvis in mind
+ mPelvisOffset.mV[VX] = llclamp( mPelvisOffset.mV[VX], -drift_comp_max - currentPelvisPos.mV[VX], drift_comp_max - currentPelvisPos.mV[VX] );
+ mPelvisOffset.mV[VY] = llclamp( mPelvisOffset.mV[VY], -drift_comp_max - currentPelvisPos.mV[VY], drift_comp_max - currentPelvisPos.mV[VY]);
+ mPelvisOffset.mV[VZ] = 0.f;
+
+ // set position
+ mPelvisState.setPosition(mPelvisOffset);
+
+ mCharacter->setAnimationData("Pelvis Offset", &mPelvisOffset);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLWalkAdjustMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLWalkAdjustMotion::onDeactivate()
+{
+ mCharacter->removeAnimationData("Walk Speed");
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onInitialize()
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLFlyAdjustMotion::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+
+ LLJoint* pelvisJoint = mCharacter->getJoint("mPelvis");
+ mPelvisState.setJoint( pelvisJoint );
+ if ( !pelvisJoint )
+ {
+ llwarns << getName() << ": Can't get pelvis joint." << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS | LLJointState::ROT);
+ addJointState( &mPelvisState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLFlyAdjustMotion::onActivate()
+{
+ mPelvisState.setPosition(LLVector3::zero);
+ mPelvisState.setRotation(LLQuaternion::DEFAULT);
+ mRoll = 0.f;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFlyAdjustMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLFlyAdjustMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ LLVector3 ang_vel = mCharacter->getCharacterAngularVelocity() * mCharacter->getTimeDilation();
+ F32 speed = mCharacter->getCharacterVelocity().magVec();
+
+ F32 roll_factor = clamp_rescale(speed, 7.f, 15.f, 0.f, -MAX_ROLL);
+ F32 target_roll = llclamp(ang_vel.mV[VZ], -4.f, 4.f) * roll_factor;
+
+ // roll is critically damped interpolation between current roll and angular velocity-derived target roll
+ mRoll = lerp(mRoll, target_roll, LLCriticalDamp::getInterpolant(0.1f));
+
+// llinfos << mRoll << llendl;
+
+ LLQuaternion roll(mRoll, LLVector3(0.f, 0.f, 1.f));
+ mPelvisState.setRotation(roll);
+
+// F32 lerp_amt = LLCriticalDamp::getInterpolant(0.2f);
+//
+// LLVector3 pelvis_correction = mPelvisState.getPosition() - lerp(LLVector3::zero, mPelvisState.getJoint()->getPosition() + mPelvisState.getPosition(), lerp_amt);
+// mPelvisState.setPosition(pelvis_correction);
+ return TRUE;
+}
diff --git a/indra/llcharacter/llkeyframewalkmotion.h b/indra/llcharacter/llkeyframewalkmotion.h
new file mode 100644
index 0000000000..05286456d3
--- /dev/null
+++ b/indra/llcharacter/llkeyframewalkmotion.h
@@ -0,0 +1,158 @@
+/**
+ * @file llkeyframewalkmotion.h
+ * @brief Implementation of LLKeframeWalkMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYFRAMEWALKMOTION_H
+#define LL_LLKEYFRAMEWALKMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llkeyframemotion.h"
+#include "llcharacter.h"
+#include "v3dmath.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_WALK_ADJUST (20.f)
+#define MIN_REQUIRED_PIXEL_AREA_FLY_ADJUST (20.f)
+
+//-----------------------------------------------------------------------------
+// class LLKeyframeWalkMotion
+//-----------------------------------------------------------------------------
+class LLKeyframeWalkMotion :
+ public LLKeyframeMotion
+{
+ friend class LLWalkAdjustMotion;
+public:
+ // Constructor
+ LLKeyframeWalkMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLKeyframeWalkMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLKeyframeWalkMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ F32 mCyclePhase;
+ F32 mRealTimeLast;
+ F32 mAdjTimeLast;
+ S32 mDownFoot;
+};
+
+class LLWalkAdjustMotion : public LLMotion
+{
+public:
+ // Constructor
+ LLWalkAdjustMotion(const LLUUID &id);
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLWalkAdjustMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate();
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+ virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGH_PRIORITY;}
+ virtual BOOL getLoop() { return TRUE; }
+ virtual F32 getDuration() { return 0.f; }
+ virtual F32 getEaseInDuration() { return 0.f; }
+ virtual F32 getEaseOutDuration() { return 0.f; }
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_WALK_ADJUST; }
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+public:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLJoint* mLeftAnkleJoint;
+ LLJoint* mRightAnkleJoint;
+ LLJointState mPelvisState;
+ LLJoint* mPelvisJoint;
+ LLVector3d mLastLeftAnklePos;
+ LLVector3d mLastRightAnklePos;
+ F32 mLastTime;
+ F32 mAvgCorrection;
+ F32 mSpeedAdjust;
+ F32 mAnimSpeed;
+ F32 mAvgSpeed;
+ F32 mRelativeDir;
+ LLVector3 mPelvisOffset;
+ F32 mAnkleOffset;
+};
+
+class LLFlyAdjustMotion : public LLMotion
+{
+public:
+ // Constructor
+ LLFlyAdjustMotion(const LLUUID &id) : LLMotion(id) {mName = "fly_adjust";}
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLFlyAdjustMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+ virtual BOOL onActivate();
+ virtual void onDeactivate() {};
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+ virtual LLJoint::JointPriority getPriority(){return LLJoint::HIGHER_PRIORITY;}
+ virtual BOOL getLoop() { return TRUE; }
+ virtual F32 getDuration() { return 0.f; }
+ virtual F32 getEaseInDuration() { return 0.f; }
+ virtual F32 getEaseOutDuration() { return 0.f; }
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_FLY_ADJUST; }
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+protected:
+ //-------------------------------------------------------------------------
+ // Member Data
+ //-------------------------------------------------------------------------
+ LLCharacter *mCharacter;
+ LLJointState mPelvisState;
+ F32 mRoll;
+};
+
+#endif // LL_LLKeyframeWalkMotion_H
+
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
new file mode 100644
index 0000000000..a53956223c
--- /dev/null
+++ b/indra/llcharacter/llmotion.cpp
@@ -0,0 +1,131 @@
+/**
+ * @file llmotion.cpp
+ * @brief Implementation of LLMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmotion.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotion class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotion::LLMotion( const LLUUID &id )
+{
+ mActivationTimestamp = 0.f;
+ mStopTimestamp = 0.f;
+ mSendStopTimestamp = F32_MAX;
+ mResidualWeight = 0.f;
+ mFadeWeight = 1.f;
+ mStopped = TRUE;
+ mActive = FALSE;
+ mDeactivateCallback = NULL;
+
+ memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[2][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+
+ mID = id;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotion::~LLMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// fadeOut()
+//-----------------------------------------------------------------------------
+void LLMotion::fadeOut()
+{
+ if (mFadeWeight > 0.01f)
+ {
+ mFadeWeight = lerp(mFadeWeight, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ mFadeWeight = 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// fadeIn()
+//-----------------------------------------------------------------------------
+void LLMotion::fadeIn()
+{
+ if (mFadeWeight < 0.99f)
+ {
+ mFadeWeight = lerp(mFadeWeight, 1.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ mFadeWeight = 1.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+void LLMotion::addJointState(LLJointState* jointState)
+{
+ mPose.addJointState(jointState);
+ S32 priority = jointState->getPriority();
+ if (priority == LLJoint::USE_MOTION_PRIORITY)
+ {
+ priority = getPriority();
+ }
+
+ U32 usage = jointState->getUsage();
+
+ // for now, usage is everything
+ mJointSignature[0][jointState->getJoint()->getJointNum()] = (usage & LLJointState::POS) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[1][jointState->getJoint()->getJointNum()] = (usage & LLJointState::ROT) ? (0xff >> (7 - priority)) : 0;
+ mJointSignature[2][jointState->getJoint()->getJointNum()] = (usage & LLJointState::SCALE) ? (0xff >> (7 - priority)) : 0;
+}
+
+void LLMotion::setDeactivateCallback( void (*cb)(void *), void* userdata )
+{
+ mDeactivateCallback = cb;
+ mDeactivateCallbackUserData = userdata;
+}
+
+//-----------------------------------------------------------------------------
+// activate()
+//-----------------------------------------------------------------------------
+void LLMotion::activate()
+{
+ mStopped = FALSE;
+ mActive = TRUE;
+ onActivate();
+}
+
+//-----------------------------------------------------------------------------
+// deactivate()
+//-----------------------------------------------------------------------------
+void LLMotion::deactivate()
+{
+ mActive = FALSE;
+
+ if (mDeactivateCallback) (*mDeactivateCallback)(mDeactivateCallbackUserData);
+
+ onDeactivate();
+}
+
+// End
diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h
new file mode 100644
index 0000000000..66882a2c11
--- /dev/null
+++ b/indra/llcharacter/llmotion.h
@@ -0,0 +1,237 @@
+/**
+ * @file llmotion.h
+ * @brief Implementation of LLMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOTION_H
+#define LL_LLMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "llerror.h"
+#include "llpose.h"
+#include "lluuid.h"
+
+class LLCharacter;
+
+//-----------------------------------------------------------------------------
+// class LLMotion
+//-----------------------------------------------------------------------------
+class LLMotion
+{
+public:
+ enum LLMotionBlendType
+ {
+ NORMAL_BLEND,
+ ADDITIVE_BLEND
+ };
+
+ enum LLMotionInitStatus
+ {
+ STATUS_FAILURE,
+ STATUS_SUCCESS,
+ STATUS_HOLD
+ };
+
+ // Constructor
+ LLMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return NULL; }
+
+ // get the name of this instance
+ const std::string &getName() const { return mName; }
+
+ // set the name of this instance
+ void setName(const std::string &name) { mName = name; }
+
+ const LLUUID& getID() const { return mID; }
+
+ // returns the pose associated with the current state of this motion
+ virtual LLPose* getPose() { return &mPose;}
+
+ void fadeOut();
+
+ void fadeIn();
+
+ F32 getFadeWeight() const { return mFadeWeight; }
+
+ F32 getStopTime() const { return mStopTimestamp; }
+
+ virtual void setStopTime(F32 time) { mStopTimestamp = time; mStopped = TRUE; }
+
+ BOOL isStopped() const { return mStopped; }
+
+ void setStopped(BOOL stopped) { mStopped = stopped; }
+
+ void activate();
+
+ void deactivate();
+
+ BOOL isActive() { return mActive; }
+
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() = 0;
+
+ // motions must report their total duration
+ virtual F32 getDuration() = 0;
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() = 0;
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() = 0;
+
+ // motions must report their priority level
+ virtual LLJoint::JointPriority getPriority() = 0;
+
+ // motions must report their blend type
+ virtual LLMotionBlendType getBlendType() = 0;
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() = 0;
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character) = 0;
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 activeTime, U8* joint_mask) = 0;
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() = 0;
+
+ // optional callback routine called when animation deactivated.
+ void setDeactivateCallback( void (*cb)(void *), void* userdata );
+
+protected:
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() = 0;
+
+ void addJointState(LLJointState* jointState);
+
+protected:
+ LLPose mPose;
+ BOOL mStopped; // motion has been stopped;
+ BOOL mActive; // motion is on active list (can be stopped or not stopped)
+
+public:
+ //-------------------------------------------------------------------------
+ // these are set implicitly by the motion controller and
+ // may be referenced (read only) in the above handlers.
+ //-------------------------------------------------------------------------
+ std::string mName; // instance name assigned by motion controller
+ LLUUID mID;
+
+ F32 mActivationTimestamp; // time when motion was activated
+ F32 mStopTimestamp; // time when motion was told to stop
+ F32 mSendStopTimestamp; // time when simulator should be told to stop this motion
+ F32 mResidualWeight; // blend weight at beginning of stop motion phase
+ F32 mFadeWeight; // for fading in and out based on LOD
+ U8 mJointSignature[3][LL_CHARACTER_MAX_JOINTS]; // signature of which joints are animated at what priority
+ void (*mDeactivateCallback)(void* data);
+ void* mDeactivateCallbackUserData;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTestMotion
+//-----------------------------------------------------------------------------
+class LLTestMotion : public LLMotion
+{
+public:
+ LLTestMotion(const LLUUID &id) : LLMotion(id){}
+ ~LLTestMotion() {}
+ static LLMotion *create(const LLUUID& id) { return new LLTestMotion(id); }
+ BOOL getLoop() { return FALSE; }
+ F32 getDuration() { return 0.0f; }
+ F32 getEaseInDuration() { return 0.0f; }
+ F32 getEaseOutDuration() { return 0.0f; }
+ LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+ LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+ F32 getMinPixelArea() { return 0.f; }
+
+ LLMotionInitStatus onInitialize(LLCharacter*) { llinfos << "LLTestMotion::onInitialize()" << llendl; return STATUS_SUCCESS; }
+ BOOL onActivate() { llinfos << "LLTestMotion::onActivate()" << llendl; return TRUE; }
+ BOOL onUpdate(F32 time, U8* joint_mask) { llinfos << "LLTestMotion::onUpdate(" << time << ")" << llendl; return TRUE; }
+ void onDeactivate() { llinfos << "LLTestMotion::onDeactivate()" << llendl; }
+};
+
+
+//-----------------------------------------------------------------------------
+// LLNullMotion
+//-----------------------------------------------------------------------------
+class LLNullMotion : public LLMotion
+{
+public:
+ LLNullMotion(const LLUUID &id) : LLMotion(id) {}
+ ~LLNullMotion() {}
+ static LLMotion *create(const LLUUID &id) { return new LLNullMotion(id); }
+
+ // motions must specify whether or not they loop
+ /*virtual*/ BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ /*virtual*/ F32 getDuration() { return 1.f; }
+
+ // motions must report their "ease in" duration
+ /*virtual*/ F32 getEaseInDuration() { return 0.f; }
+
+ // motions must report their "ease out" duration.
+ /*virtual*/ F32 getEaseOutDuration() { return 0.f; }
+
+ // motions must report their priority level
+ /*virtual*/ LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+
+ // motions must report their blend type
+ /*virtual*/ LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ /*virtual*/ F32 getMinPixelArea() { return 0.f; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ /*virtual*/ LLMotionInitStatus onInitialize(LLCharacter *character) { return STATUS_SUCCESS; }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ /*virtual*/ BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ /*virtual*/ BOOL onUpdate(F32 activeTime, U8* joint_mask) { return TRUE; }
+
+ // called when a motion is deactivated
+ /*virtual*/ void onDeactivate() {}
+};
+#endif // LL_LLMOTION_H
+
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
new file mode 100644
index 0000000000..7ec67b5fd4
--- /dev/null
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -0,0 +1,927 @@
+/**
+ * @file llmotioncontroller.cpp
+ * @brief Implementation of LLMotionController class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llmotioncontroller.h"
+#include "llkeyframemotion.h"
+#include "llmath.h"
+#include "lltimer.h"
+#include "llanimationstates.h"
+#include "llstl.h"
+
+const S32 NUM_JOINT_SIGNATURE_STRIDES = LL_CHARACTER_MAX_JOINTS / 4;
+const U32 MAX_MOTION_INSTANCES = 32;
+
+//-----------------------------------------------------------------------------
+// Constants and statics
+//-----------------------------------------------------------------------------
+LLMotionRegistry LLMotionController::sRegistry;
+
+//-----------------------------------------------------------------------------
+// LLMotionTableEntry()
+//-----------------------------------------------------------------------------
+LLMotionTableEntry::LLMotionTableEntry()
+{
+ mConstructor = NULL;
+ mID.setNull();
+}
+
+LLMotionTableEntry::LLMotionTableEntry(LLMotionConstructor constructor, const LLUUID& id)
+ : mConstructor(constructor), mID(id)
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// create()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionTableEntry::create(const LLUUID &id)
+{
+ LLMotion* motionp = mConstructor(id);
+
+ return motionp;
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotionRegistry class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotionRegistry()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotionRegistry::LLMotionRegistry() : mMotionTable(LLMotionTableEntry::uuidEq, LLMotionTableEntry())
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLMotionRegistry()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotionRegistry::~LLMotionRegistry()
+{
+ mMotionTable.removeAll();
+}
+
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionRegistry::addMotion( const LLUUID& id, LLMotionConstructor constructor )
+{
+// llinfos << "Registering motion: " << name << llendl;
+ if (!mMotionTable.check(id))
+ {
+ mMotionTable.set(id, LLMotionTableEntry(constructor, id));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// markBad()
+//-----------------------------------------------------------------------------
+void LLMotionRegistry::markBad( const LLUUID& id )
+{
+ mMotionTable.set(id, LLMotionTableEntry());
+}
+
+//-----------------------------------------------------------------------------
+// createMotion()
+//-----------------------------------------------------------------------------
+LLMotion *LLMotionRegistry::createMotion( const LLUUID &id )
+{
+ LLMotionTableEntry motion_entry = mMotionTable.get(id);
+ LLMotion* motion = NULL;
+
+ if ( motion_entry.getID().isNull() )
+ {
+ //FIXME - RN: need to replace with a better default scheme
+ motion = LLKeyframeMotion::create(id);
+ }
+ else
+ {
+ motion = motion_entry.create(id);
+ }
+
+ return motion;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLMotionController class
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLMotionController()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLMotionController::LLMotionController( )
+{
+ mTime = 0.f;
+ mTimeOffset = 0.f;
+ mLastTime = 0.0f;
+ mHasRunOnce = FALSE;
+ mPaused = FALSE;
+ mPauseTime = 0.f;
+ mTimeStep = 0.f;
+ mTimeStepCount = 0;
+ mLastInterp = 0.f;
+ mTimeFactor = 1.f;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLMotionController()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLMotionController::~LLMotionController()
+{
+ deleteAllMotions();
+}
+
+//-----------------------------------------------------------------------------
+// deleteAllMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::deleteAllMotions()
+{
+ mLoadingMotions.removeAllNodes();
+ mLoadedMotions.clear();
+ mActiveMotions.removeAllNodes();
+
+ for_each(mAllMotions.begin(), mAllMotions.end(), DeletePairedPointer());
+ mAllMotions.clear();
+}
+
+//-----------------------------------------------------------------------------
+// addLoadedMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::addLoadedMotion(LLMotion* motionp)
+{
+ if (mLoadedMotions.size() > MAX_MOTION_INSTANCES)
+ {
+ // too many motions active this frame, kill all blenders
+ mPoseBlender.clearBlenders();
+
+ for (U32 i = 0; i < mLoadedMotions.size(); i++)
+ {
+ LLMotion* cur_motionp = mLoadedMotions.front();
+ mLoadedMotions.pop_front();
+
+ // motion isn't playing, delete it
+ if (!isMotionActive(cur_motionp))
+ {
+ mCharacter->requestStopMotion(cur_motionp);
+ mAllMotions.erase(cur_motionp->getID());
+ delete cur_motionp;
+ if (mLoadedMotions.size() <= MAX_MOTION_INSTANCES)
+ {
+ break;
+ }
+ }
+ else
+ {
+ // put active motion on back
+ mLoadedMotions.push_back(cur_motionp);
+ }
+ }
+ }
+ mLoadedMotions.push_back(motionp);
+}
+
+//-----------------------------------------------------------------------------
+// setTimeStep()
+//-----------------------------------------------------------------------------
+void LLMotionController::setTimeStep(F32 step)
+{
+ mTimeStep = step;
+
+ if (step != 0.f)
+ {
+ // make sure timestamps conform to new quantum
+ for( LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp != NULL;
+ motionp = mActiveMotions.getNextData() )
+ {
+ motionp->mActivationTimestamp = (F32)llfloor(motionp->mActivationTimestamp / step) * step;
+ BOOL stopped = motionp->isStopped();
+ motionp->setStopTime((F32)llfloor(motionp->getStopTime() / step) * step);
+ motionp->setStopped(stopped);
+ motionp->mSendStopTimestamp = (F32)llfloor(motionp->mSendStopTimestamp / step) * step;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setTimeFactor()
+//-----------------------------------------------------------------------------
+void LLMotionController::setTimeFactor(F32 time_factor)
+{
+ mTimeOffset += mTimer.getElapsedTimeAndResetF32() * mTimeFactor;
+ mTimeFactor = time_factor;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstActiveMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::getFirstActiveMotion()
+{
+ return mActiveMotions.getFirstData();
+}
+
+//-----------------------------------------------------------------------------
+// getNextActiveMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::getNextActiveMotion()
+{
+ return mActiveMotions.getNextData();
+}
+
+
+//-----------------------------------------------------------------------------
+// setCharacter()
+//-----------------------------------------------------------------------------
+void LLMotionController::setCharacter(LLCharacter *character)
+{
+ mCharacter = character;
+}
+
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::addMotion( const LLUUID& id, LLMotionConstructor constructor )
+{
+ return sRegistry.addMotion(id, constructor);
+}
+
+//-----------------------------------------------------------------------------
+// removeMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::removeMotion( const LLUUID& id)
+{
+ LLMotion* motionp = findMotion(id);
+ if (motionp)
+ {
+ stopMotionLocally(id, TRUE);
+
+ mLoadingMotions.deleteData(motionp);
+ std::deque<LLMotion*>::iterator motion_it;
+ for (motion_it = mLoadedMotions.begin(); motion_it != mLoadedMotions.end(); ++motion_it)
+ {
+ if(*motion_it == motionp)
+ {
+ mLoadedMotions.erase(motion_it);
+ break;
+ }
+ }
+ mActiveMotions.deleteData(motionp);
+ mAllMotions.erase(id);
+ delete motionp;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// createMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLMotionController::createMotion( const LLUUID &id )
+{
+ // do we have an instance of this motion for this character?
+ LLMotion *motion = findMotion(id);
+
+ // if not, we need to create one
+ if (!motion)
+ {
+ // look up constructor and create it
+ motion = sRegistry.createMotion(id);
+ if (!motion)
+ {
+ return NULL;
+ }
+
+ // look up name for default motions
+ const char* motion_name = gAnimLibrary.animStateToString(id);
+ if (motion_name)
+ {
+ motion->setName(motion_name);
+ }
+
+ // initialize the new instance
+ LLMotion::LLMotionInitStatus stat = motion->onInitialize(mCharacter);
+ switch(stat)
+ {
+ case LLMotion::STATUS_FAILURE:
+ llinfos << "Motion " << id << " init failed." << llendl;
+ sRegistry.markBad(id);
+ delete motion;
+ return NULL;
+ case LLMotion::STATUS_HOLD:
+ mLoadingMotions.addData(motion);
+ break;
+ case LLMotion::STATUS_SUCCESS:
+ // add motion to our list
+ addLoadedMotion(motion);
+ break;
+ default:
+ llerrs << "Invalid initialization status" << llendl;
+ break;
+ }
+
+ mAllMotions[id] = motion;
+ }
+ return motion;
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::startMotion(const LLUUID &id, F32 start_offset)
+{
+ // look for motion in our list of created motions
+ LLMotion *motion = createMotion(id);
+
+ if (!motion)
+ {
+ return FALSE;
+ }
+ //if the motion is already active, then we're done
+ else if (isMotionActive(motion)) // motion is playing and...
+ {
+ if (motion->isStopped()) // motion has been stopped
+ {
+ deactivateMotion(motion);
+ }
+ else if (mTime < motion->mSendStopTimestamp) // motion is still active
+ {
+ return TRUE;
+ }
+ }
+
+// llinfos << "Starting motion " << name << llendl;
+ return activateMotion(motion, mTime - start_offset);
+}
+
+
+//-----------------------------------------------------------------------------
+// stopMotionLocally()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::stopMotionLocally(const LLUUID &id, BOOL stop_immediate)
+{
+ // if already inactive, return false
+ LLMotion *motion = findMotion(id);
+ if (!motion)
+ {
+ return FALSE;
+ }
+
+ // If on active list, stop it
+ if (isMotionActive(motion) && !motion->isStopped())
+ {
+ // when using timesteps, set stop time to last frame's time, otherwise grab current timer value
+ // FIXME: should investigate this inconsistency...hints of obscure bugs
+
+ F32 stop_time = (mTimeStep != 0.f || mPaused) ? (mTime) : mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
+ motion->setStopTime(stop_time);
+
+ if (stop_immediate)
+ {
+ deactivateMotion(motion);
+ }
+ return TRUE;
+ }
+ else if (isMotionLoading(motion))
+ {
+ motion->setStopped(TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateRegularMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateRegularMotions()
+{
+ updateMotionsByType(LLMotion::NORMAL_BLEND);
+}
+
+//-----------------------------------------------------------------------------
+// updateAdditiveMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateAdditiveMotions()
+{
+ updateMotionsByType(LLMotion::ADDITIVE_BLEND);
+}
+
+//-----------------------------------------------------------------------------
+// resetJointSignatures()
+//-----------------------------------------------------------------------------
+void LLMotionController::resetJointSignatures()
+{
+ memset(&mJointSignature[0][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+ memset(&mJointSignature[1][0], 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+}
+
+//-----------------------------------------------------------------------------
+// updateMotionsByType()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateMotionsByType(LLMotion::LLMotionBlendType anim_type)
+{
+ BOOL update_result = TRUE;
+ U8 last_joint_signature[LL_CHARACTER_MAX_JOINTS];
+
+ memset(&last_joint_signature, 0, sizeof(U8) * LL_CHARACTER_MAX_JOINTS);
+
+ // iterate through active motions in chronological order
+ for(LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp != NULL;
+ motionp = mActiveMotions.getNextData())
+ {
+ if (motionp->getBlendType() != anim_type)
+ {
+ continue;
+ }
+
+ BOOL update_motion = FALSE;
+
+ if (motionp->getPose()->getWeight() < 1.f)
+ {
+ update_motion = TRUE;
+ }
+ else
+ {
+ S32 i;
+ // NUM_JOINT_SIGNATURE_STRIDES should be multiple of 4
+ for (i = 0; i < NUM_JOINT_SIGNATURE_STRIDES; i++)
+ {
+ U32 *current_signature = (U32*)&(mJointSignature[0][i * 4]);
+ U32 test_signature = *(U32*)&(motionp->mJointSignature[0][i * 4]);
+
+ if ((*current_signature | test_signature) > (*current_signature))
+ {
+ *current_signature |= test_signature;
+ update_motion = TRUE;
+ }
+
+ *((U32*)&last_joint_signature[i * 4]) = *(U32*)&(mJointSignature[1][i * 4]);
+ current_signature = (U32*)&(mJointSignature[1][i * 4]);
+ test_signature = *(U32*)&(motionp->mJointSignature[1][i * 4]);
+
+ if ((*current_signature | test_signature) > (*current_signature))
+ {
+ *current_signature |= test_signature;
+ update_motion = TRUE;
+ }
+ }
+ }
+
+ if (!update_motion)
+ {
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ deactivateMotion(motionp);
+ }
+ else if (motionp->isStopped() && mTime > motionp->getStopTime())
+ {
+ // is this the first iteration in the ease out phase?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // store residual weight for this motion
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ }
+ else if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+ else if (mTime >= motionp->mActivationTimestamp)
+ {
+ if (mLastTime < motionp->mActivationTimestamp)
+ {
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ }
+ continue;
+ }
+
+ LLPose *posep = motionp->getPose();
+
+ // only filter by LOD after running every animation at least once (to prime the avatar state)
+ if (mHasRunOnce && motionp->getMinPixelArea() > mCharacter->getPixelArea())
+ {
+ motionp->fadeOut();
+
+ //should we notify the simulator that this motion should be stopped (check even if skipped by LOD logic)
+ if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+
+ if (motionp->getFadeWeight() < 0.01f)
+ {
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ posep->setWeight(0.f);
+ deactivateMotion(motionp);
+ }
+ continue;
+ }
+ }
+ else
+ {
+ motionp->fadeIn();
+ }
+
+ //**********************
+ // MOTION INACTIVE
+ //**********************
+ if (motionp->isStopped() && mTime > motionp->getStopTime() + motionp->getEaseOutDuration())
+ {
+ // this motion has gone on too long, deactivate it
+ // did we have a chance to stop it?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // if not, let's stop it this time through and deactivate it the next
+
+ posep->setWeight(motionp->getFadeWeight());
+ motionp->onUpdate(motionp->getStopTime() - motionp->mActivationTimestamp, last_joint_signature);
+ }
+ else
+ {
+ posep->setWeight(0.f);
+ deactivateMotion(motionp);
+ continue;
+ }
+ }
+
+ //**********************
+ // MOTION EASE OUT
+ //**********************
+ else if (motionp->isStopped() && mTime > motionp->getStopTime())
+ {
+ // is this the first iteration in the ease out phase?
+ if (mLastTime <= motionp->getStopTime())
+ {
+ // store residual weight for this motion
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+
+ if (motionp->getEaseOutDuration() == 0.f)
+ {
+ posep->setWeight(0.f);
+ }
+ else
+ {
+ posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight * cubic_step(1.f - ((mTime - motionp->getStopTime()) / motionp->getEaseOutDuration())));
+ }
+
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+
+ //**********************
+ // MOTION ACTIVE
+ //**********************
+ else if (mTime > motionp->mActivationTimestamp + motionp->getEaseInDuration())
+ {
+ posep->setWeight(motionp->getFadeWeight());
+
+ //should we notify the simulator that this motion should be stopped?
+ if (mTime > motionp->mSendStopTimestamp)
+ {
+ // notify character of timed stop event on first iteration past sendstoptimestamp
+ // this will only be called when an animation stops itself (runs out of time)
+ if (mLastTime <= motionp->mSendStopTimestamp)
+ {
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+ }
+
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+
+ //**********************
+ // MOTION EASE IN
+ //**********************
+ else if (mTime >= motionp->mActivationTimestamp)
+ {
+ if (mLastTime < motionp->mActivationTimestamp)
+ {
+ motionp->mResidualWeight = motionp->getPose()->getWeight();
+ }
+ if (motionp->getEaseInDuration() == 0.f)
+ {
+ posep->setWeight(motionp->getFadeWeight());
+ }
+ else
+ {
+ // perform motion update
+ posep->setWeight(motionp->getFadeWeight() * motionp->mResidualWeight + (1.f - motionp->mResidualWeight) * cubic_step((mTime - motionp->mActivationTimestamp) / motionp->getEaseInDuration()));
+ }
+ // perform motion update
+ update_result = motionp->onUpdate(mTime - motionp->mActivationTimestamp, last_joint_signature);
+ }
+ else
+ {
+ posep->setWeight(0.f);
+ update_result = motionp->onUpdate(0.f, last_joint_signature);
+ }
+
+ // allow motions to deactivate themselves
+ if (!update_result)
+ {
+ if (!motionp->isStopped() || motionp->getStopTime() > mTime)
+ {
+ // animation has stopped itself due to internal logic
+ // propagate this to the network
+ // as not all viewers are guaranteed to have access to the same logic
+ mCharacter->requestStopMotion( motionp );
+ stopMotionLocally(motionp->getID(), FALSE);
+ }
+
+ }
+
+ // even if onupdate returns FALSE, add this motion in to the blend one last time
+ mPoseBlender.addMotion(motionp);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// updateMotion()
+//-----------------------------------------------------------------------------
+void LLMotionController::updateMotion()
+{
+ BOOL use_quantum = (mTimeStep != 0.f);
+
+ // Update timing info for this time step.
+ if (!mPaused)
+ {
+ F32 update_time = mTimeOffset + (mTimer.getElapsedTimeF32() * mTimeFactor);
+ if (use_quantum)
+ {
+ F32 time_interval = fmodf(update_time, mTimeStep);
+
+ // always animate *ahead* of actual time
+ S32 quantum_count = llmax(0, llfloor((update_time - time_interval) / mTimeStep)) + 1;
+ if (quantum_count == mTimeStepCount)
+ {
+ // we're still in same time quantum as before, so just interpolate and exit
+ if (!mPaused)
+ {
+ F32 interp = time_interval / mTimeStep;
+ mPoseBlender.interpolate(interp - mLastInterp);
+ mLastInterp = interp;
+ }
+
+ return;
+ }
+
+ // is calculating a new keyframe pose, make sure the last one gets applied
+ mPoseBlender.interpolate(1.f);
+ mPoseBlender.clearBlenders();
+
+ mTimeStepCount = quantum_count;
+ mLastTime = mTime;
+ mTime = (F32)quantum_count * mTimeStep;
+ mLastInterp = 0.f;
+ }
+ else
+ {
+ mLastTime = mTime;
+ mTime = update_time;
+ }
+ }
+
+ // query pending motions for completion
+ LLMotion* motionp;
+
+ for ( motionp = mLoadingMotions.getFirstData();
+ motionp != NULL;
+ motionp = mLoadingMotions.getNextData() )
+ {
+ LLMotion::LLMotionInitStatus status = motionp->onInitialize(mCharacter);
+ if (status == LLMotion::STATUS_SUCCESS)
+ {
+ mLoadingMotions.removeCurrentData();
+ // add motion to our loaded motion list
+ addLoadedMotion(motionp);
+ // this motion should be playing
+ if (!motionp->isStopped())
+ {
+ activateMotion(motionp, mTime);
+ }
+ }
+ else if (status == LLMotion::STATUS_FAILURE)
+ {
+ llinfos << "Motion " << motionp->getID() << " init failed." << llendl;
+ sRegistry.markBad(motionp->getID());
+ mLoadingMotions.removeCurrentData();
+ mAllMotions.erase(motionp->getID());
+ delete motionp;
+ }
+ }
+
+ resetJointSignatures();
+
+ if (!mPaused)
+ {
+ // update additive motions
+ updateAdditiveMotions();
+ resetJointSignatures();
+
+ // update all regular motions
+ updateRegularMotions();
+
+ if (use_quantum)
+ {
+ mPoseBlender.blendAndCache(TRUE);
+ }
+ else
+ {
+ mPoseBlender.blendAndApply();
+ }
+ }
+
+ mHasRunOnce = TRUE;
+// llinfos << "Motion controller time " << motionTimer.getElapsedTimeF32() << llendl;
+}
+
+
+//-----------------------------------------------------------------------------
+// activateMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::activateMotion(LLMotion *motion, F32 time)
+{
+ if (mLoadingMotions.checkData(motion))
+ {
+ // we want to start this motion, but we can't yet, so flag it as started
+ motion->setStopped(FALSE);
+ // report pending animations as activated
+ return TRUE;
+ }
+
+ motion->mResidualWeight = motion->getPose()->getWeight();
+ motion->mActivationTimestamp = time;
+
+ // set stop time based on given duration and ease out time
+ if (motion->getDuration() != 0.f && !motion->getLoop())
+ {
+ F32 ease_out_time;
+ F32 motion_duration;
+
+ // should we stop at the end of motion duration, or a bit earlier
+ // to allow it to ease out while moving?
+ ease_out_time = motion->getEaseOutDuration();
+
+ // is the clock running when the motion is easing in?
+ // if not (POSTURE_EASE) then we need to wait that much longer before triggering the stop
+ motion_duration = llmax(motion->getDuration() - ease_out_time, 0.f);
+ motion->mSendStopTimestamp = time + motion_duration;
+ }
+ else
+ {
+ motion->mSendStopTimestamp = F32_MAX;
+ }
+
+ mActiveMotions.addData(motion);
+
+ motion->activate();
+ motion->onUpdate(0.f, mJointSignature[1]);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// deactivateMotion()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::deactivateMotion(LLMotion *motion)
+{
+ motion->deactivate();
+ mActiveMotions.removeData(motion);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// isMotionActive()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::isMotionActive(LLMotion *motion)
+{
+ if (motion && motion->isActive())
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// isMotionLoading()
+//-----------------------------------------------------------------------------
+BOOL LLMotionController::isMotionLoading(LLMotion* motion)
+{
+ return mLoadingMotions.checkData(motion);
+}
+
+
+//-----------------------------------------------------------------------------
+// findMotion()
+//-----------------------------------------------------------------------------
+LLMotion *LLMotionController::findMotion(const LLUUID& id)
+{
+ return mAllMotions[id];
+}
+
+
+//-----------------------------------------------------------------------------
+// flushAllMotions()
+//-----------------------------------------------------------------------------
+void LLMotionController::flushAllMotions()
+{
+ LLDynamicArray<LLUUID> active_motions;
+ LLDynamicArray<F32> active_motion_times;
+
+ for (LLMotion* motionp = mActiveMotions.getFirstData();
+ motionp;
+ motionp = mActiveMotions.getNextData())
+ {
+ active_motions.put(motionp->getID());
+ active_motion_times.put(mTime - motionp->mActivationTimestamp);
+ motionp->deactivate();
+ }
+
+ // delete all motion instances
+ deleteAllMotions();
+
+ // kill current hand pose that was previously called out by
+ // keyframe motion
+ mCharacter->removeAnimationData("Hand Pose");
+
+ // restart motions
+ for (S32 i = 0; i < active_motions.count(); i++)
+ {
+ startMotion(active_motions[i], active_motion_times[i]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// pause()
+//-----------------------------------------------------------------------------
+void LLMotionController::pause()
+{
+ if (!mPaused)
+ {
+ //llinfos << "Pausing animations..." << llendl;
+ mPauseTime = mTimer.getElapsedTimeF32();
+ mPaused = TRUE;
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// unpause()
+//-----------------------------------------------------------------------------
+void LLMotionController::unpause()
+{
+ if (mPaused)
+ {
+ //llinfos << "Unpausing animations..." << llendl;
+ mTimer.reset();
+ mTimer.setAge(mPauseTime);
+ mPaused = FALSE;
+ }
+}
+// End
diff --git a/indra/llcharacter/llmotioncontroller.h b/indra/llcharacter/llmotioncontroller.h
new file mode 100644
index 0000000000..d43d6d9a8f
--- /dev/null
+++ b/indra/llcharacter/llmotioncontroller.h
@@ -0,0 +1,207 @@
+/**
+ * @file llmotioncontroller.h
+ * @brief Implementation of LLMotionController class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOTIONCONTROLLER_H
+#define LL_LLMOTIONCONTROLLER_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include <string>
+#include <map>
+#include <deque>
+
+#include "linked_lists.h"
+#include "lluuidhashmap.h"
+#include "llmotion.h"
+#include "llpose.h"
+#include "llframetimer.h"
+#include "llstatemachine.h"
+#include "llstring.h"
+
+//-----------------------------------------------------------------------------
+// Class predeclaration
+// This is necessary because llcharacter.h includes this file.
+//-----------------------------------------------------------------------------
+class LLCharacter;
+
+//-----------------------------------------------------------------------------
+// LLMotionRegistry
+//-----------------------------------------------------------------------------
+typedef LLMotion*(*LLMotionConstructor)(const LLUUID &id);
+
+class LLMotionTableEntry
+{
+public:
+ LLMotionTableEntry();
+ LLMotionTableEntry(LLMotionConstructor constructor, const LLUUID& id);
+ ~LLMotionTableEntry(){};
+
+ LLMotion* create(const LLUUID& id);
+ static BOOL uuidEq(const LLUUID &uuid, const LLMotionTableEntry &id_pair)
+ {
+ if (uuid == id_pair.mID)
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ const LLUUID& getID() { return mID; }
+
+protected:
+ LLMotionConstructor mConstructor;
+ LLUUID mID;
+};
+
+class LLMotionRegistry
+{
+public:
+ // Constructor
+ LLMotionRegistry();
+
+ // Destructor
+ ~LLMotionRegistry();
+
+ // adds motion classes to the registry
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create);
+
+ // creates a new instance of a named motion
+ // returns NULL motion is not registered
+ LLMotion *createMotion( const LLUUID &id );
+
+ // initialization of motion failed, don't try to create this motion again
+ void markBad( const LLUUID& id );
+
+
+protected:
+ LLUUIDHashMap<LLMotionTableEntry, 32> mMotionTable;
+};
+
+//-----------------------------------------------------------------------------
+// class LLMotionController
+//-----------------------------------------------------------------------------
+class LLMotionController
+{
+public:
+ // Constructor
+ LLMotionController();
+
+ // Destructor
+ virtual ~LLMotionController();
+
+ // set associated character
+ // this must be called exactly once by the containing character class.
+ // this is generally done in the Character constructor
+ void setCharacter( LLCharacter *character );
+
+ // registers a motion with the controller
+ // (actually just forwards call to motion registry)
+ // returns true if successfull
+ BOOL addMotion( const LLUUID& id, LLMotionConstructor create );
+
+ // creates a motion from the registry
+ LLMotion *createMotion( const LLUUID &id );
+
+ // unregisters a motion with the controller
+ // (actually just forwards call to motion registry)
+ // returns true if successfull
+ void removeMotion( const LLUUID& id );
+
+ // start motion
+ // begins playing the specified motion
+ // returns true if successful
+ BOOL startMotion( const LLUUID &id, F32 start_offset );
+
+ // stop motion
+ // stops a playing motion
+ // in reality, it begins the ease out transition phase
+ // returns true if successful
+ BOOL stopMotionLocally( const LLUUID &id, BOOL stop_immediate );
+
+ // update motions
+ // invokes the update handlers for each active motion
+ // activates sequenced motions
+ // deactivates terminated motions`
+ void updateMotion();
+
+ // flush motions
+ // releases all motion instances
+ void flushAllMotions();
+
+ // pause and continue all motions
+ void pause();
+ void unpause();
+ BOOL isPaused() { return mPaused; }
+
+ void setTimeStep(F32 step);
+
+ void setTimeFactor(F32 time_factor);
+ F32 getTimeFactor() { return mTimeFactor; }
+
+ LLMotion* getFirstActiveMotion();
+ LLMotion* getNextActiveMotion();
+
+//protected:
+ BOOL isMotionActive( LLMotion *motion );
+ BOOL isMotionLoading( LLMotion *motion );
+ LLMotion *findMotion( const LLUUID& id );
+
+protected:
+ void deleteAllMotions();
+ void addLoadedMotion(LLMotion *motion);
+ BOOL activateMotion(LLMotion *motion, F32 time);
+ BOOL deactivateMotion(LLMotion *motion);
+ void updateRegularMotions();
+ void updateAdditiveMotions();
+ void resetJointSignatures();
+ void updateMotionsByType(LLMotion::LLMotionBlendType motion_type);
+protected:
+
+ F32 mTimeFactor;
+ static LLMotionRegistry sRegistry;
+ LLPoseBlender mPoseBlender;
+
+ LLCharacter *mCharacter;
+
+// Life cycle of an animation:
+//
+// Animations are instantiated and immediately put in the mAllMotions map for their entire lifetime.
+// If the animations depend on any asset data, the appropriate data is fetched from the data server,
+// and the animation is put on the mLoadingMotions list.
+// Once an animations is loaded, it will be initialized and put on the mLoadedMotions deque.
+// Any animation that is currently playing also sits in the mActiveMotions list.
+
+ std::map<LLUUID, LLMotion*> mAllMotions;
+
+ LLLinkedList<LLMotion> mLoadingMotions;
+ std::deque<LLMotion*> mLoadedMotions;
+ LLLinkedList<LLMotion> mActiveMotions;
+
+ LLFrameTimer mTimer;
+ F32 mTime;
+ F32 mTimeOffset;
+ F32 mLastTime;
+ BOOL mHasRunOnce;
+ BOOL mPaused;
+ F32 mTimeStep;
+ S32 mTimeStepCount;
+ F32 mLastInterp;
+ F32 mPauseTime;
+
+ U8 mJointSignature[2][LL_CHARACTER_MAX_JOINTS];
+};
+
+//-----------------------------------------------------------------------------
+// Class declaractions
+//-----------------------------------------------------------------------------
+#include "llcharacter.h"
+
+#endif // LL_LLMOTIONCONTROLLER_H
+
diff --git a/indra/llcharacter/llmultigesture.cpp b/indra/llcharacter/llmultigesture.cpp
new file mode 100644
index 0000000000..1e42352d74
--- /dev/null
+++ b/indra/llcharacter/llmultigesture.cpp
@@ -0,0 +1,478 @@
+/**
+ * @file llmultigesture.cpp
+ * @brief Gestures that are asset-based and can have multiple steps.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <algorithm>
+
+#include "stdio.h"
+
+#include "llmultigesture.h"
+
+#include "llerror.h"
+#include "lldatapacker.h"
+#include "llstl.h"
+
+const S32 GESTURE_VERSION = 2;
+
+//---------------------------------------------------------------------------
+// LLMultiGesture
+//---------------------------------------------------------------------------
+LLMultiGesture::LLMultiGesture()
+: mKey(),
+ mMask(),
+ mTrigger(),
+ mReplaceText(),
+ mSteps(),
+ mPlaying(FALSE),
+ mCurrentStep(0),
+ mDoneCallback(NULL),
+ mCallbackData(NULL)
+{
+ reset();
+}
+
+LLMultiGesture::~LLMultiGesture()
+{
+ std::for_each(mSteps.begin(), mSteps.end(), DeletePointer());
+}
+
+void LLMultiGesture::reset()
+{
+ mPlaying = FALSE;
+ mCurrentStep = 0;
+ mWaitTimer.reset();
+ mWaitingTimer = FALSE;
+ mWaitingAnimations = FALSE;
+ mWaitingAtEnd = FALSE;
+ mRequestedAnimIDs.clear();
+ mPlayingAnimIDs.clear();
+}
+
+S32 LLMultiGesture::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+
+ // ascii format, being very conservative about possible
+ // label lengths.
+ max_size += 64; // version S32
+ max_size += 64; // key U8
+ max_size += 64; // mask U32
+ max_size += 256; // trigger string
+ max_size += 256; // replace string
+
+ max_size += 64; // step count S32
+
+ std::vector<LLGestureStep*>::const_iterator it;
+ for (it = mSteps.begin(); it != mSteps.end(); ++it)
+ {
+ LLGestureStep* step = *it;
+ max_size += 64; // type S32
+ max_size += step->getMaxSerialSize();
+ }
+
+ /* binary format
+ max_size += sizeof(S32); // version
+ max_size += sizeof(mKey);
+ max_size += sizeof(mMask);
+ max_size += mTrigger.length() + 1; // for null
+
+ max_size += sizeof(S32); // step count
+
+ std::vector<LLGestureStep*>::const_iterator it;
+ for (it = mSteps.begin(); it != mSteps.end(); ++it)
+ {
+ LLGestureStep* step = *it;
+ max_size += sizeof(S32); // type
+ max_size += step->getMaxSerialSize();
+ }
+ */
+
+ return max_size;
+}
+
+BOOL LLMultiGesture::serialize(LLDataPacker& dp) const
+{
+ dp.packS32(GESTURE_VERSION, "version");
+ dp.packU8(mKey, "key");
+ dp.packU32(mMask, "mask");
+ dp.packString(mTrigger.c_str(), "trigger");
+ dp.packString(mReplaceText.c_str(), "replace");
+
+ S32 count = (S32)mSteps.size();
+ dp.packS32(count, "step_count");
+ S32 i;
+ for (i = 0; i < count; ++i)
+ {
+ LLGestureStep* step = mSteps[i];
+
+ dp.packS32(step->getType(), "step_type");
+ BOOL ok = step->serialize(dp);
+ if (!ok)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLMultiGesture::deserialize(LLDataPacker& dp)
+{
+ S32 version;
+ dp.unpackS32(version, "version");
+ if (version != GESTURE_VERSION)
+ {
+ llwarns << "Bad LLMultiGesture version " << version
+ << " should be " << GESTURE_VERSION
+ << llendl;
+ return FALSE;
+ }
+
+ dp.unpackU8(mKey, "key");
+ dp.unpackU32(mMask, "mask");
+
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "trigger");
+ mTrigger = buffer;
+
+ dp.unpackString(buffer, "replace");
+ mReplaceText = buffer;
+
+ S32 count;
+ dp.unpackS32(count, "step_count");
+ if (count < 0)
+ {
+ llwarns << "Bad LLMultiGesture step count " << count << llendl;
+ return FALSE;
+ }
+
+ S32 i;
+ for (i = 0; i < count; ++i)
+ {
+ S32 type;
+ dp.unpackS32(type, "step_type");
+
+ EStepType step_type = (EStepType)type;
+ switch(step_type)
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* step = new LLGestureStepAnimation();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* step = new LLGestureStepSound();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* step = new LLGestureStepChat();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* step = new LLGestureStepWait();
+ BOOL ok = step->deserialize(dp);
+ if (!ok) return FALSE;
+ mSteps.push_back(step);
+ break;
+ }
+ default:
+ {
+ llwarns << "Bad LLMultiGesture step type " << type << llendl;
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLMultiGesture::dump()
+{
+ llinfos << "key " << S32(mKey) << " mask " << U32(mMask)
+ << " trigger " << mTrigger
+ << " replace " << mReplaceText
+ << llendl;
+ U32 i;
+ for (i = 0; i < mSteps.size(); ++i)
+ {
+ LLGestureStep* step = mSteps[i];
+ step->dump();
+ }
+}
+
+//---------------------------------------------------------------------------
+// LLGestureStepAnimation
+//---------------------------------------------------------------------------
+LLGestureStepAnimation::LLGestureStepAnimation()
+: LLGestureStep(),
+ mAnimName("None"),
+ mAnimAssetID(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepAnimation::~LLGestureStepAnimation()
+{ }
+
+S32 LLGestureStepAnimation::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+
+ // ascii
+ max_size += 256; // anim name
+ max_size += 64; // anim asset id
+ max_size += 64; // flags
+
+ /* binary
+ max_size += mAnimName.length() + 1;
+ max_size += sizeof(mAnimAssetID);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepAnimation::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mAnimName.c_str(), "anim_name");
+ dp.packUUID(mAnimAssetID, "asset_id");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepAnimation::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "anim_name");
+ mAnimName = buffer;
+
+ // Apparently an earlier version of the gesture code added \r to the end
+ // of the animation names. Get rid of it. JC
+ if (mAnimName[mAnimName.length() - 1] == '\r')
+ {
+ // chop the last character
+ mAnimName.resize(mAnimName.length() - 1);
+ }
+
+ dp.unpackUUID(mAnimAssetID, "asset_id");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepAnimation::getLabel() const
+{
+ std::string label;
+ if (mFlags & ANIM_FLAG_STOP)
+ {
+ label = "Stop Animation: ";
+ }
+ else
+ {
+ label = "Start Animation: ";
+ }
+ label += mAnimName;
+ return label;
+}
+
+void LLGestureStepAnimation::dump()
+{
+ llinfos << "step animation " << mAnimName
+ << " id " << mAnimAssetID
+ << " flags " << mFlags
+ << llendl;
+}
+
+//---------------------------------------------------------------------------
+// LLGestureStepSound
+//---------------------------------------------------------------------------
+LLGestureStepSound::LLGestureStepSound()
+: LLGestureStep(),
+ mSoundName("None"),
+ mSoundAssetID(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepSound::~LLGestureStepSound()
+{ }
+
+S32 LLGestureStepSound::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 256; // sound name
+ max_size += 64; // sound asset id
+ max_size += 64; // flags
+ /* binary
+ max_size += mSoundName.length() + 1;
+ max_size += sizeof(mSoundAssetID);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepSound::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mSoundName.c_str(), "sound_name");
+ dp.packUUID(mSoundAssetID, "asset_id");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepSound::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "sound_name");
+ mSoundName = buffer;
+
+ dp.unpackUUID(mSoundAssetID, "asset_id");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepSound::getLabel() const
+{
+ std::string label("Sound: ");
+ label += mSoundName;
+ return label;
+}
+
+void LLGestureStepSound::dump()
+{
+ llinfos << "step sound " << mSoundName
+ << " id " << mSoundAssetID
+ << " flags " << mFlags
+ << llendl;
+}
+
+
+//---------------------------------------------------------------------------
+// LLGestureStepChat
+//---------------------------------------------------------------------------
+LLGestureStepChat::LLGestureStepChat()
+: LLGestureStep(),
+ mChatText(),
+ mFlags(0x0)
+{ }
+
+LLGestureStepChat::~LLGestureStepChat()
+{ }
+
+S32 LLGestureStepChat::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 256; // chat text
+ max_size += 64; // flags
+ /* binary
+ max_size += mChatText.length() + 1;
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepChat::serialize(LLDataPacker& dp) const
+{
+ dp.packString(mChatText.c_str(), "chat_text");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepChat::deserialize(LLDataPacker& dp)
+{
+ char buffer[256]; /* Flawfinder: ignore */
+ dp.unpackString(buffer, "chat_text");
+ mChatText = buffer;
+
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepChat::getLabel() const
+{
+ std::string label("Chat: ");
+ label += mChatText;
+ return label;
+}
+
+void LLGestureStepChat::dump()
+{
+ llinfos << "step chat " << mChatText
+ << " flags " << mFlags
+ << llendl;
+}
+
+
+//---------------------------------------------------------------------------
+// LLGestureStepWait
+//---------------------------------------------------------------------------
+LLGestureStepWait::LLGestureStepWait()
+: LLGestureStep(),
+ mWaitSeconds(0.f),
+ mFlags(0x0)
+{ }
+
+LLGestureStepWait::~LLGestureStepWait()
+{ }
+
+S32 LLGestureStepWait::getMaxSerialSize() const
+{
+ S32 max_size = 0;
+ max_size += 64; // wait seconds
+ max_size += 64; // flags
+ /* binary
+ max_size += sizeof(mWaitSeconds);
+ max_size += sizeof(mFlags);
+ */
+ return max_size;
+}
+
+BOOL LLGestureStepWait::serialize(LLDataPacker& dp) const
+{
+ dp.packF32(mWaitSeconds, "wait_seconds");
+ dp.packU32(mFlags, "flags");
+ return TRUE;
+}
+
+BOOL LLGestureStepWait::deserialize(LLDataPacker& dp)
+{
+ dp.unpackF32(mWaitSeconds, "wait_seconds");
+ dp.unpackU32(mFlags, "flags");
+ return TRUE;
+}
+
+std::string LLGestureStepWait::getLabel() const
+{
+ std::string label("--- Wait: ");
+ if (mFlags & WAIT_FLAG_TIME)
+ {
+ char buffer[64]; /* Flawfinder: ignore */
+ snprintf(buffer, sizeof(buffer), "%.1f seconds", (double)mWaitSeconds); /* Flawfinder: ignore */
+ label += buffer;
+ }
+ else if (mFlags & WAIT_FLAG_ALL_ANIM)
+ {
+ label += "until animations are done";
+ }
+
+ return label;
+}
+
+
+void LLGestureStepWait::dump()
+{
+ llinfos << "step wait " << mWaitSeconds
+ << " flags " << mFlags
+ << llendl;
+}
diff --git a/indra/llcharacter/llmultigesture.h b/indra/llcharacter/llmultigesture.h
new file mode 100644
index 0000000000..39ee30568b
--- /dev/null
+++ b/indra/llcharacter/llmultigesture.h
@@ -0,0 +1,213 @@
+/**
+ * @file llmultigesture.h
+ * @brief Gestures that are asset-based and can have multiple steps.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMULTIGESTURE_H
+#define LL_LLMULTIGESTURE_H
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "lluuid.h"
+#include "llframetimer.h"
+
+class LLDataPacker;
+class LLGestureStep;
+
+class LLMultiGesture
+{
+public:
+ LLMultiGesture();
+ virtual ~LLMultiGesture();
+
+ // Maximum number of bytes this could hold once serialized.
+ S32 getMaxSerialSize() const;
+
+ BOOL serialize(LLDataPacker& dp) const;
+ BOOL deserialize(LLDataPacker& dp);
+
+ void dump();
+
+ void reset();
+
+ const std::string& getTrigger() const { return mTrigger; }
+protected:
+ LLMultiGesture(const LLMultiGesture& gest);
+ const LLMultiGesture& operator=(const LLMultiGesture& rhs);
+
+public:
+ // name is stored at asset level
+ // desc is stored at asset level
+ KEY mKey;
+ MASK mMask;
+
+ // String, like "/foo" or "hello" that makes it play
+ std::string mTrigger;
+
+ // Replaces the trigger substring with this text
+ std::string mReplaceText;
+
+ std::vector<LLGestureStep*> mSteps;
+
+ // Is the gesture currently playing?
+ BOOL mPlaying;
+
+ // "instruction pointer" for steps
+ S32 mCurrentStep;
+
+ // We're waiting for triggered animations to stop playing
+ BOOL mWaitingAnimations;
+
+ // We're waiting a fixed amount of time
+ BOOL mWaitingTimer;
+
+ // Waiting after the last step played for all animations to complete
+ BOOL mWaitingAtEnd;
+
+ // Timer for waiting
+ LLFrameTimer mWaitTimer;
+
+ void (*mDoneCallback)(LLMultiGesture* gesture, void* data);
+ void* mCallbackData;
+
+ // Animations that we requested to start
+ std::set<LLUUID> mRequestedAnimIDs;
+
+ // Once the animation starts playing (sim says to start playing)
+ // the ID is moved from mRequestedAnimIDs to here.
+ std::set<LLUUID> mPlayingAnimIDs;
+};
+
+
+enum EStepType
+{
+ STEP_ANIMATION = 0,
+ STEP_SOUND = 1,
+ STEP_CHAT = 2,
+ STEP_WAIT = 3,
+
+ STEP_EOF = 4
+};
+
+
+class LLGestureStep
+{
+public:
+ LLGestureStep() {}
+ virtual ~LLGestureStep() {}
+
+ virtual EStepType getType() = 0;
+
+ // Return a user-readable label for this step
+ virtual std::string getLabel() const = 0;
+
+ virtual S32 getMaxSerialSize() const = 0;
+ virtual BOOL serialize(LLDataPacker& dp) const = 0;
+ virtual BOOL deserialize(LLDataPacker& dp) = 0;
+
+ virtual void dump() = 0;
+};
+
+
+// By default, animation steps start animations.
+// If the least significant bit is 1, it will stop animations.
+const U32 ANIM_FLAG_STOP = 0x01;
+
+class LLGestureStepAnimation : public LLGestureStep
+{
+public:
+ LLGestureStepAnimation();
+ virtual ~LLGestureStepAnimation();
+
+ virtual EStepType getType() { return STEP_ANIMATION; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mAnimName;
+ LLUUID mAnimAssetID;
+ U32 mFlags;
+};
+
+
+class LLGestureStepSound : public LLGestureStep
+{
+public:
+ LLGestureStepSound();
+ virtual ~LLGestureStepSound();
+
+ virtual EStepType getType() { return STEP_SOUND; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mSoundName;
+ LLUUID mSoundAssetID;
+ U32 mFlags;
+};
+
+
+class LLGestureStepChat : public LLGestureStep
+{
+public:
+ LLGestureStepChat();
+ virtual ~LLGestureStepChat();
+
+ virtual EStepType getType() { return STEP_CHAT; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ std::string mChatText;
+ U32 mFlags;
+};
+
+
+const U32 WAIT_FLAG_TIME = 0x01;
+const U32 WAIT_FLAG_ALL_ANIM = 0x02;
+
+class LLGestureStepWait : public LLGestureStep
+{
+public:
+ LLGestureStepWait();
+ virtual ~LLGestureStepWait();
+
+ virtual EStepType getType() { return STEP_WAIT; }
+
+ virtual std::string getLabel() const;
+
+ virtual S32 getMaxSerialSize() const;
+ virtual BOOL serialize(LLDataPacker& dp) const;
+ virtual BOOL deserialize(LLDataPacker& dp);
+
+ virtual void dump();
+
+public:
+ F32 mWaitSeconds;
+ U32 mFlags;
+};
+
+#endif
diff --git a/indra/llcharacter/llpose.cpp b/indra/llcharacter/llpose.cpp
new file mode 100644
index 0000000000..ff56dba585
--- /dev/null
+++ b/indra/llcharacter/llpose.cpp
@@ -0,0 +1,544 @@
+/**
+ * @file llpose.cpp
+ * @brief Implementation of LLPose class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llpose.h"
+
+#include "llmotion.h"
+#include "llmath.h"
+
+//-----------------------------------------------------------------------------
+// Static
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLPose
+//-----------------------------------------------------------------------------
+LLPose::~LLPose()
+{
+}
+
+//-----------------------------------------------------------------------------
+// getFirstJointState()
+//-----------------------------------------------------------------------------
+LLJointState *LLPose::getFirstJointState()
+{
+ mListIter = mJointMap.begin();
+ if (mListIter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return mListIter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getNextJointState()
+//-----------------------------------------------------------------------------
+LLJointState *LLPose::getNextJointState()
+{
+ mListIter++;
+ if (mListIter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return mListIter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+BOOL LLPose::addJointState(LLJointState *jointState)
+{
+ if (mJointMap.find(jointState->getJoint()->getName()) == mJointMap.end())
+ {
+ mJointMap[jointState->getJoint()->getName()] = jointState;
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeJointState()
+//-----------------------------------------------------------------------------
+BOOL LLPose::removeJointState(LLJointState *jointState)
+{
+ mJointMap.erase(jointState->getJoint()->getName());
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeAllJointStates()
+//-----------------------------------------------------------------------------
+BOOL LLPose::removeAllJointStates()
+{
+ mJointMap.clear();
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// findJointState()
+//-----------------------------------------------------------------------------
+LLJointState* LLPose::findJointState(LLJoint *joint)
+{
+ joint_map_iterator iter = mJointMap.find(joint->getName());
+
+ if (iter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// findJointState()
+//-----------------------------------------------------------------------------
+LLJointState* LLPose::findJointState(const std::string &name)
+{
+ joint_map_iterator iter = mJointMap.find(name);
+
+ if (iter == mJointMap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setWeight()
+//-----------------------------------------------------------------------------
+void LLPose::setWeight(F32 weight)
+{
+ joint_map_iterator iter;
+ for(iter = mJointMap.begin();
+ iter != mJointMap.end();
+ ++iter)
+ {
+ iter->second->setWeight(weight);
+ }
+ mWeight = weight;
+}
+
+//-----------------------------------------------------------------------------
+// getWeight()
+//-----------------------------------------------------------------------------
+F32 LLPose::getWeight() const
+{
+ return mWeight;
+}
+
+//-----------------------------------------------------------------------------
+// getNumJointStates()
+//-----------------------------------------------------------------------------
+S32 LLPose::getNumJointStates() const
+{
+ return (S32)mJointMap.size();
+}
+
+//-----------------------------------------------------------------------------
+// LLJointStateBlender
+//-----------------------------------------------------------------------------
+
+LLJointStateBlender::LLJointStateBlender()
+{
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ mPriorities[i] = S32_MIN;
+ }
+}
+
+LLJointStateBlender::~LLJointStateBlender()
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// addJointState()
+//-----------------------------------------------------------------------------
+BOOL LLJointStateBlender::addJointState(LLJointState *joint_state, S32 priority, BOOL additive_blend)
+{
+ llassert(joint_state);
+
+ if (!joint_state->getJoint())
+ // this joint state doesn't point to an actual joint, so we don't care about applying it
+ return FALSE;
+
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ if (NULL == mJointStates[i])
+ {
+ mJointStates[i] = joint_state;
+ mPriorities[i] = priority;
+ mAdditiveBlends[i] = additive_blend;
+ return TRUE;
+ }
+ else if (priority > mPriorities[i])
+ {
+ // we're at a higher priority than the current joint state in this slot
+ // so shift everyone over
+ // previous joint states (newer motions) with same priority should stay in place
+ for (S32 j = JSB_NUM_JOINT_STATES - 1; j > i; j--)
+ {
+ mJointStates[j] = mJointStates[j - 1];
+ mPriorities[j] = mPriorities[j - 1];
+ mAdditiveBlends[j] = mAdditiveBlends[j - 1];
+ }
+ // now store ourselves in this slot
+ mJointStates[i] = joint_state;
+ mPriorities[i] = priority;
+ mAdditiveBlends[i] = additive_blend;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// blendJointStates()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::blendJointStates(BOOL apply_now)
+{
+ // we need at least one joint to blend
+ // if there is one, it will be in slot zero according to insertion logic
+ // instead of resetting joint state to default, just leave it unchanged from last frame
+ if (NULL == mJointStates[0])
+ {
+ return;
+ }
+
+ LLJoint* target_joint = apply_now ? mJointStates[0]->getJoint() : &mJointCache;
+
+ const S32 POS_WEIGHT = 0;
+ const S32 ROT_WEIGHT = 1;
+ const S32 SCALE_WEIGHT = 2;
+
+ F32 sum_weights[3];
+ U32 sum_usage = 0;
+
+ LLVector3 blended_pos = target_joint->getPosition();
+ LLQuaternion blended_rot = target_joint->getRotation();
+ LLVector3 blended_scale = target_joint->getScale();
+
+ LLVector3 added_pos;
+ LLQuaternion added_rot;
+ LLVector3 added_scale;
+
+ //S32 joint_state_index;
+
+ sum_weights[POS_WEIGHT] = 0.f;
+ sum_weights[ROT_WEIGHT] = 0.f;
+ sum_weights[SCALE_WEIGHT] = 0.f;
+
+ for(S32 joint_state_index = 0;
+ joint_state_index < JSB_NUM_JOINT_STATES && mJointStates[joint_state_index] != NULL;
+ joint_state_index++)
+ {
+ U32 current_usage = mJointStates[joint_state_index]->getUsage();
+ F32 current_weight = mJointStates[joint_state_index]->getWeight();
+ LLJointState* jsp = mJointStates[joint_state_index];
+
+ if (current_weight == 0.f)
+ {
+ continue;
+ }
+
+ if (mAdditiveBlends[joint_state_index])
+ {
+ if(current_usage & LLJointState::POS)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
+
+ // add in pos for this jointstate modulated by weight
+ added_pos += jsp->getPosition() * (new_weight_sum - sum_weights[POS_WEIGHT]);
+ //sum_weights[POS_WEIGHT] = new_weight_sum;
+ }
+
+ // now do scale
+ if(current_usage & LLJointState::SCALE)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
+
+ // add in scale for this jointstate modulated by weight
+ added_scale += jsp->getScale() * (new_weight_sum - sum_weights[SCALE_WEIGHT]);
+ //sum_weights[SCALE_WEIGHT] = new_weight_sum;
+ }
+
+ if (current_usage & LLJointState::ROT)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
+
+ // add in rotation for this jointstate modulated by weight
+ added_rot = nlerp((new_weight_sum - sum_weights[ROT_WEIGHT]), added_rot, jsp->getRotation()) * added_rot;
+ //sum_weights[ROT_WEIGHT] = new_weight_sum;
+ }
+ }
+ else
+ {
+ // blend two jointstates together
+
+ // blend position
+ if(current_usage & LLJointState::POS)
+ {
+ if(sum_usage & LLJointState::POS)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[POS_WEIGHT]);
+
+ // blend positions from both
+ blended_pos = lerp(mJointStates[joint_state_index]->getPosition(), blended_pos, sum_weights[POS_WEIGHT] / new_weight_sum);
+ sum_weights[POS_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy position from current
+ blended_pos = mJointStates[joint_state_index]->getPosition();
+ sum_weights[POS_WEIGHT] = current_weight;
+ }
+ }
+
+ // now do scale
+ if(current_usage & LLJointState::SCALE)
+ {
+ if(sum_usage & LLJointState::SCALE)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[SCALE_WEIGHT]);
+
+ // blend scales from both
+ blended_scale = lerp(mJointStates[joint_state_index]->getScale(), blended_scale, sum_weights[SCALE_WEIGHT] / new_weight_sum);
+ sum_weights[SCALE_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy scale from current
+ blended_scale = mJointStates[joint_state_index]->getScale();
+ sum_weights[SCALE_WEIGHT] = current_weight;
+ }
+ }
+
+ // rotation
+ if (current_usage & LLJointState::ROT)
+ {
+ if(sum_usage & LLJointState::ROT)
+ {
+ F32 new_weight_sum = llmin(1.f, current_weight + sum_weights[ROT_WEIGHT]);
+
+ // blend rotations from both
+ blended_rot = nlerp(sum_weights[ROT_WEIGHT] / new_weight_sum, mJointStates[joint_state_index]->getRotation(), blended_rot);
+ sum_weights[ROT_WEIGHT] = new_weight_sum;
+ }
+ else
+ {
+ // copy rotation from current
+ blended_rot = mJointStates[joint_state_index]->getRotation();
+ sum_weights[ROT_WEIGHT] = current_weight;
+ }
+ }
+
+ // update resulting usage mask
+ sum_usage = sum_usage | current_usage;
+ }
+ }
+
+ // apply blended transforms
+ target_joint->setPosition(blended_pos);
+ target_joint->setScale(blended_scale);
+ target_joint->setRotation(blended_rot);
+
+ // apply additive transforms
+ target_joint->setPosition(target_joint->getPosition() + added_pos);
+ target_joint->setScale(target_joint->getScale() + added_scale);
+ target_joint->setRotation(added_rot * target_joint->getRotation());
+
+ if (apply_now)
+ {
+ // now clear joint states
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// interpolate()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::interpolate(F32 u)
+{
+ // only interpolate if we have a joint state
+ if (!mJointStates[0])
+ {
+ return;
+ }
+ LLJoint* target_joint = mJointStates[0]->getJoint();
+
+ if (!target_joint)
+ {
+ return;
+ }
+
+ target_joint->setPosition(lerp(target_joint->getPosition(), mJointCache.getPosition(), u));
+ target_joint->setScale(lerp(target_joint->getScale(), mJointCache.getScale(), u));
+ target_joint->setRotation(nlerp(u, target_joint->getRotation(), mJointCache.getRotation()));
+}
+
+//-----------------------------------------------------------------------------
+// clear()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::clear()
+{
+ // now clear joint states
+ for(S32 i = 0; i < JSB_NUM_JOINT_STATES; i++)
+ {
+ mJointStates[i] = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resetCachedJoint()
+//-----------------------------------------------------------------------------
+void LLJointStateBlender::resetCachedJoint()
+{
+ if (!mJointStates[0])
+ {
+ return;
+ }
+ LLJoint* source_joint = mJointStates[0]->getJoint();
+ mJointCache.setPosition(source_joint->getPosition());
+ mJointCache.setScale(source_joint->getScale());
+ mJointCache.setRotation(source_joint->getRotation());
+}
+
+//-----------------------------------------------------------------------------
+// LLPoseBlender
+//-----------------------------------------------------------------------------
+
+LLPoseBlender::LLPoseBlender()
+{
+}
+
+LLPoseBlender::~LLPoseBlender()
+{
+ mJointStateBlenderPool.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// addMotion()
+//-----------------------------------------------------------------------------
+BOOL LLPoseBlender::addMotion(LLMotion* motion)
+{
+ LLPose* pose = motion->getPose();
+
+ for(LLJointState *jsp = pose->getFirstJointState(); jsp; jsp = pose->getNextJointState())
+ {
+ LLJoint *jointp = jsp->getJoint();
+ LLJointStateBlender* joint_blender;
+ if (!mJointStateBlenderPool.checkData(jointp))
+ {
+ // this is the first time we are animating this joint
+ // so create new jointblender and add it to our pool
+ joint_blender = new LLJointStateBlender();
+ mJointStateBlenderPool.addData(jointp, joint_blender);
+ } else
+ {
+ joint_blender = mJointStateBlenderPool.getData(jointp);
+ }
+
+ if (jsp->getPriority() == LLJoint::USE_MOTION_PRIORITY)
+ {
+ joint_blender->addJointState(jsp, motion->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
+ }
+ else
+ {
+ joint_blender->addJointState(jsp, jsp->getPriority(), motion->getBlendType() == LLMotion::ADDITIVE_BLEND);
+ }
+
+ // add it to our list of active blenders
+ if(!mActiveBlenders.checkData(joint_blender))
+ {
+ mActiveBlenders.addData(joint_blender);
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// blendAndApply()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::blendAndApply()
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->blendJointStates();
+ }
+
+ // we're done now so there are no more active blenders for this frame
+ mActiveBlenders.removeAllNodes();
+}
+
+//-----------------------------------------------------------------------------
+// blendAndCache()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::blendAndCache(BOOL reset_cached_joints)
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ if (reset_cached_joints)
+ {
+ jsbp->resetCachedJoint();
+ }
+ jsbp->blendJointStates(FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// interpolate()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::interpolate(F32 u)
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->interpolate(u);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearBlenders()
+//-----------------------------------------------------------------------------
+void LLPoseBlender::clearBlenders()
+{
+ for (LLJointStateBlender* jsbp = mActiveBlenders.getFirstData();
+ jsbp;
+ jsbp = mActiveBlenders.getNextData())
+ {
+ jsbp->clear();
+ }
+
+ mActiveBlenders.removeAllNodes();
+}
+
diff --git a/indra/llcharacter/llpose.h b/indra/llcharacter/llpose.h
new file mode 100644
index 0000000000..e286c14e21
--- /dev/null
+++ b/indra/llcharacter/llpose.h
@@ -0,0 +1,120 @@
+/**
+ * @file llpose.h
+ * @brief Implementation of LLPose class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOSE_H
+#define LL_LLPOSE_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include <string>
+
+#include "linked_lists.h"
+#include "llmap.h"
+#include "lljointstate.h"
+#include "llassoclist.h"
+#include "lljoint.h"
+#include <map>
+
+
+//-----------------------------------------------------------------------------
+// class LLPose
+//-----------------------------------------------------------------------------
+class LLPose
+{
+ friend class LLPoseBlender;
+protected:
+ typedef std::map<std::string, LLJointState*> joint_map;
+ typedef joint_map::iterator joint_map_iterator;
+ typedef joint_map::value_type joint_map_value_type;
+
+ joint_map mJointMap;
+ F32 mWeight;
+ joint_map_iterator mListIter;
+public:
+ // Iterate through jointStates
+ LLJointState *getFirstJointState();
+ LLJointState *getNextJointState();
+ LLJointState *findJointState(LLJoint *joint);
+ LLJointState *findJointState(const std::string &name);
+public:
+ // Constructor
+ LLPose() : mWeight(0.f) {}
+ // Destructor
+ ~LLPose();
+ // add a joint state in this pose
+ BOOL addJointState(LLJointState *jointState);
+ // remove a joint state from this pose
+ BOOL removeJointState(LLJointState *jointState);
+ // removes all joint states from this pose
+ BOOL removeAllJointStates();
+ // set weight for all joint states in this pose
+ void setWeight(F32 weight);
+ // get weight for this pose
+ F32 getWeight() const;
+ // returns number of joint states stored in this pose
+ S32 getNumJointStates() const;
+};
+
+const S32 JSB_NUM_JOINT_STATES = 4;
+
+class LLJointStateBlender
+{
+protected:
+ LLJointState* mJointStates[JSB_NUM_JOINT_STATES];
+ S32 mPriorities[JSB_NUM_JOINT_STATES];
+ BOOL mAdditiveBlends[JSB_NUM_JOINT_STATES];
+public:
+ LLJointStateBlender();
+ ~LLJointStateBlender();
+ void blendJointStates(BOOL apply_now = TRUE);
+ BOOL addJointState(LLJointState *joint_state, S32 priority, BOOL additive_blend);
+ void interpolate(F32 u);
+ void clear();
+ void resetCachedJoint();
+
+public:
+ LLJoint mJointCache;
+};
+
+class LLMotion;
+
+class LLPoseBlender
+{
+protected:
+ LLMap<LLJoint*,LLJointStateBlender*> mJointStateBlenderPool;
+ LLLinkedList<LLJointStateBlender> mActiveBlenders;
+
+ S32 mNextPoseSlot;
+ LLPose mBlendedPose;
+public:
+ // Constructor
+ LLPoseBlender();
+ // Destructor
+ ~LLPoseBlender();
+
+ // request motion joint states to be added to pose blender joint state records
+ BOOL addMotion(LLMotion* motion);
+
+ // blend all joint states and apply to skeleton
+ void blendAndApply();
+
+ // removes all joint state blenders from last time
+ void clearBlenders();
+
+ // blend all joint states and cache results
+ void blendAndCache(BOOL reset_cached_joints);
+
+ // interpolate all joints towards cached values
+ void interpolate(F32 u);
+
+ LLPose* getBlendedPose() { return &mBlendedPose; }
+};
+
+#endif // LL_LLPOSE_H
+
diff --git a/indra/llcharacter/llstatemachine.cpp b/indra/llcharacter/llstatemachine.cpp
new file mode 100644
index 0000000000..d51b2f75aa
--- /dev/null
+++ b/indra/llcharacter/llstatemachine.cpp
@@ -0,0 +1,378 @@
+/**
+ * @file llstatemachine.cpp
+ * @brief LLStateMachine implementation file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstatemachine.h"
+#include "llapr.h"
+
+#define FSM_PRINT_STATE_TRANSITIONS (0)
+
+U32 LLUniqueID::sNextID = 0;
+
+bool operator==(const LLUniqueID &a, const LLUniqueID &b)
+{
+ return (a.mId == b.mId);
+}
+
+bool operator!=(const LLUniqueID &a, const LLUniqueID &b)
+{
+ return (a.mId != b.mId);
+}
+
+//-----------------------------------------------------------------------------
+// LLStateDiagram
+//-----------------------------------------------------------------------------
+LLStateDiagram::LLStateDiagram()
+{
+ mUseDefaultState = FALSE;
+}
+
+LLStateDiagram::~LLStateDiagram()
+{
+
+}
+
+// add a state to the state graph
+BOOL LLStateDiagram::addState(LLFSMState *state)
+{
+ mStates[state] = Transitions();
+ return TRUE;
+}
+
+// add a directed transition between 2 states
+BOOL LLStateDiagram::addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
+{
+ StateMap::iterator state_it;
+ state_it = mStates.find(&start_state);
+ Transitions* state_transitions = NULL;
+ if (state_it == mStates.end() )
+ {
+ addState(&start_state);
+ state_transitions = &mStates[&start_state];
+ }
+ else
+ {
+ state_transitions = &state_it->second;
+ }
+ state_it = mStates.find(&end_state);
+ if (state_it == mStates.end() )
+ {
+ addState(&end_state);
+ }
+
+ Transitions::iterator transition_it = state_transitions->find(&transition);
+ if (transition_it != state_transitions->end())
+ {
+ llerrs << "LLStateTable::addDirectedTransition() : transition already exists" << llendl;
+ return FALSE; // transition already exists
+ }
+
+ (*state_transitions)[&transition] = &end_state;
+ return TRUE;
+}
+
+// add an undirected transition between 2 states
+BOOL LLStateDiagram::addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition)
+{
+ BOOL result;
+ result = addTransition(start_state, end_state, transition);
+ if (result)
+ {
+ result = addTransition(end_state, start_state, transition);
+ }
+ return result;
+}
+
+// add a transition that exists for every state
+void LLStateDiagram::addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition)
+{
+ mDefaultTransitions[&transition] = &end_state;
+}
+
+// process a possible transition, and get the resulting state
+LLFSMState* LLStateDiagram::processTransition(LLFSMState& start_state, LLFSMTransition& transition)
+{
+ // look up transition
+ //LLFSMState** dest_state = (mStates.getValue(&start_state))->getValue(&transition);
+ LLFSMState* dest_state = NULL;
+ StateMap::iterator state_it = mStates.find(&start_state);
+ if (state_it == mStates.end())
+ {
+ return NULL;
+ }
+ Transitions::iterator transition_it = state_it->second.find(&transition);
+
+ // try default transitions if state-specific transition not found
+ if (transition_it == state_it->second.end())
+ {
+ dest_state = mDefaultTransitions[&transition];
+ }
+ else
+ {
+ dest_state = transition_it->second;
+ }
+
+ // if we have a destination state...
+ if (NULL != dest_state)
+ {
+ // ...return it...
+ return dest_state;
+ }
+ // ... otherwise ...
+ else
+ {
+ // ...look for default state...
+ if (mUseDefaultState)
+ {
+ // ...return it if we have it...
+ return mDefaultState;
+ }
+ else
+ {
+ // ...or else we're still in the same state.
+ return &start_state;
+ }
+ }
+}
+
+void LLStateDiagram::setDefaultState(LLFSMState& default_state)
+{
+ mUseDefaultState = TRUE;
+ mDefaultState = &default_state;
+}
+
+S32 LLStateDiagram::numDeadendStates()
+{
+ S32 numDeadends = 0;
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ if (state_it->second.size() == 0)
+ {
+ numDeadends++;
+ }
+ }
+ return numDeadends;
+}
+
+BOOL LLStateDiagram::stateIsValid(LLFSMState& state)
+{
+ if (mStates.find(&state) != mStates.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLFSMState* LLStateDiagram::getState(U32 state_id)
+{
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ if (state_it->first->getID() == state_id)
+ {
+ return state_it->first;
+ }
+ }
+ return NULL;
+}
+
+BOOL LLStateDiagram::saveDotFile(const char* filename)
+{
+ apr_file_t* dot_file = ll_apr_file_open(filename, LL_APR_W);
+
+ if (!dot_file)
+ {
+ llwarns << "LLStateDiagram::saveDotFile() : Couldn't open " << filename << " to save state diagram." << llendl;
+ return FALSE;
+ }
+ apr_file_printf(dot_file, "digraph StateMachine {\n\tsize=\"100,100\";\n\tfontsize=40;\n\tlabel=\"Finite State Machine\";\n\torientation=landscape\n\tratio=.77\n");
+
+ StateMap::iterator state_it;
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ apr_file_printf(dot_file, "\t\"%s\" [fontsize=28,shape=box]\n", state_it->first->getName().c_str());
+ }
+ apr_file_printf(dot_file, "\t\"All States\" [fontsize=30,style=bold,shape=box]\n");
+
+ Transitions::iterator transitions_it;
+ for(transitions_it = mDefaultTransitions.begin(); transitions_it != mDefaultTransitions.end(); ++transitions_it)
+ {
+ apr_file_printf(dot_file, "\t\"All States\" -> \"%s\" [label = \"%s\",fontsize=24];\n", transitions_it->second->getName().c_str(),
+ transitions_it->second->getName().c_str());
+ }
+
+ if (mDefaultState)
+ {
+ apr_file_printf(dot_file, "\t\"All States\" -> \"%s\";\n", mDefaultState->getName().c_str());
+ }
+
+
+ for(state_it = mStates.begin(); state_it != mStates.end(); ++state_it)
+ {
+ LLFSMState *state = state_it->first;
+
+ Transitions::iterator transitions_it;
+ for(transitions_it = state_it->second.begin();
+ transitions_it != state_it->second.end();
+ ++transitions_it)
+ {
+ std::string state_name = state->getName();
+ std::string target_name = transitions_it->second->getName();
+ std::string transition_name = transitions_it->first->getName();
+ apr_file_printf(dot_file, "\t\"%s\" -> \"%s\" [label = \"%s\",fontsize=24];\n", state->getName().c_str(),
+ target_name.c_str(),
+ transition_name.c_str());
+ }
+ }
+
+ apr_file_printf(dot_file, "}\n");
+
+ apr_file_close(dot_file);
+
+ return TRUE;
+}
+
+std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM)
+{
+ if (FSM.mDefaultState)
+ {
+ s << "Default State: " << FSM.mDefaultState->getName() << "\n";
+ }
+
+ LLStateDiagram::Transitions::iterator transitions_it;
+ for(transitions_it = FSM.mDefaultTransitions.begin();
+ transitions_it != FSM.mDefaultTransitions.end();
+ ++transitions_it)
+ {
+ s << "Any State -- " << transitions_it->first->getName()
+ << " --> " << transitions_it->second->getName() << "\n";
+ }
+
+ LLStateDiagram::StateMap::iterator state_it;
+ for(state_it = FSM.mStates.begin(); state_it != FSM.mStates.end(); ++state_it)
+ {
+ LLStateDiagram::Transitions::iterator transitions_it;
+ for(transitions_it = state_it->second.begin();
+ transitions_it != state_it->second.end();
+ ++transitions_it)
+ {
+ s << state_it->first->getName() << " -- " << transitions_it->first->getName()
+ << " --> " << transitions_it->second->getName() << "\n";
+ }
+ s << "\n";
+ }
+
+ return s;
+}
+
+//-----------------------------------------------------------------------------
+// LLStateMachine
+//-----------------------------------------------------------------------------
+
+LLStateMachine::LLStateMachine()
+{
+ // we haven't received a starting state yet
+ mCurrentState = NULL;
+ mLastState = NULL;
+ mStateDiagram = NULL;
+}
+
+LLStateMachine::~LLStateMachine()
+{
+
+}
+
+// returns current state
+LLFSMState* LLStateMachine::getCurrentState() const
+{
+ return mCurrentState;
+}
+
+// executes current state
+void LLStateMachine::runCurrentState(void *data)
+{
+ mCurrentState->execute(data);
+}
+
+// set current state
+BOOL LLStateMachine::setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry)
+{
+ llassert(mStateDiagram);
+
+ if (mStateDiagram->stateIsValid(*initial_state))
+ {
+ mLastState = mCurrentState = initial_state;
+ if (!skip_entry)
+ {
+ initial_state->onEntry(user_data);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLStateMachine::setCurrentState(U32 state_id, void* user_data, BOOL skip_entry)
+{
+ llassert(mStateDiagram);
+
+ LLFSMState* state = mStateDiagram->getState(state_id);
+
+ if (state)
+ {
+ mLastState = mCurrentState = state;
+ if (!skip_entry)
+ {
+ state->onEntry(user_data);
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLStateMachine::processTransition(LLFSMTransition& transition, void* user_data)
+{
+ llassert(mStateDiagram);
+
+ LLFSMState* new_state = mStateDiagram->processTransition(*mCurrentState, transition);
+
+ mLastTransition = &transition;
+ mLastState = mCurrentState;
+
+ if (*mCurrentState != *new_state)
+ {
+ if (mCurrentState && new_state && *mCurrentState != *new_state)
+ {
+ mCurrentState->onExit(user_data);
+ }
+ if (new_state)
+ {
+ if (!mCurrentState || *mCurrentState != *new_state)
+ {
+ mCurrentState = new_state;
+ if (mCurrentState)
+ {
+ mCurrentState->onEntry(user_data);
+ }
+ }
+ }
+#if FSM_PRINT_STATE_TRANSITIONS
+ llinfos << "Entering state " << mCurrentState->getName() <<
+ " on transition " << transition.getName() << " from state " <<
+ mLastState->getName() << llendl;
+#endif
+ }
+}
+
+void LLStateMachine::setStateDiagram(LLStateDiagram* diagram)
+{
+ mStateDiagram = diagram;
+}
diff --git a/indra/llcharacter/llstatemachine.h b/indra/llcharacter/llstatemachine.h
new file mode 100644
index 0000000000..837fc57b9b
--- /dev/null
+++ b/indra/llcharacter/llstatemachine.h
@@ -0,0 +1,130 @@
+/**
+ * @file llstatemachine.h
+ * @brief LLStateMachine class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATEMACHINE_H
+#define LL_LLSTATEMACHINE_H
+
+#include <string>
+
+#include "llassoclist.h"
+#include "llerror.h"
+#include <map>
+
+class LLUniqueID
+{
+ friend bool operator==(const LLUniqueID &a, const LLUniqueID &b);
+ friend bool operator!=(const LLUniqueID &a, const LLUniqueID &b);
+protected:
+ static U32 sNextID;
+ U32 mId;
+public:
+ LLUniqueID(){mId = sNextID++;}
+ virtual ~LLUniqueID(){}
+ U32 getID() {return mId;}
+};
+
+class LLFSMTransition : public LLUniqueID
+{
+public:
+ LLFSMTransition() : LLUniqueID(){};
+ virtual std::string getName(){ return "unnamed"; }
+};
+
+class LLFSMState : public LLUniqueID
+{
+public:
+ LLFSMState() : LLUniqueID(){};
+ virtual void onEntry(void *){};
+ virtual void onExit(void *){};
+ virtual void execute(void *){};
+ virtual std::string getName(){ return "unnamed"; }
+};
+
+class LLStateDiagram
+{
+typedef std::map<LLFSMTransition*, LLFSMState*> Transitions;
+
+friend std::ostream& operator<<(std::ostream &s, LLStateDiagram &FSM);
+friend class LLStateMachine;
+
+protected:
+ typedef std::map<LLFSMState*, Transitions> StateMap;
+ StateMap mStates;
+ Transitions mDefaultTransitions;
+ LLFSMState* mDefaultState;
+ BOOL mUseDefaultState;
+
+public:
+ LLStateDiagram();
+ virtual ~LLStateDiagram();
+
+protected:
+ // add a state to the state graph, executed implicitly when adding transitions
+ BOOL addState(LLFSMState *state);
+
+ // add a directed transition between 2 states
+ BOOL addTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition);
+
+ // add an undirected transition between 2 states
+ BOOL addUndirectedTransition(LLFSMState& start_state, LLFSMState& end_state, LLFSMTransition& transition);
+
+ // add a transition that is taken if none other exist
+ void addDefaultTransition(LLFSMState& end_state, LLFSMTransition& transition);
+
+ // process a possible transition, and get the resulting state
+ LLFSMState* processTransition(LLFSMState& start_state, LLFSMTransition& transition);
+
+ // add a transition that exists for every state
+ void setDefaultState(LLFSMState& default_state);
+
+ // return total number of states with no outgoing transitions
+ S32 numDeadendStates();
+
+ // does this state exist in the state diagram?
+ BOOL stateIsValid(LLFSMState& state);
+
+ // get a state pointer by ID
+ LLFSMState* getState(U32 state_id);
+
+public:
+ // save the graph in a DOT file for rendering and visualization
+ BOOL saveDotFile(const char* filename);
+};
+
+class LLStateMachine
+{
+protected:
+ LLFSMState* mCurrentState;
+ LLFSMState* mLastState;
+ LLFSMTransition* mLastTransition;
+ LLStateDiagram* mStateDiagram;
+
+public:
+ LLStateMachine();
+ virtual ~LLStateMachine();
+
+ // set state diagram
+ void setStateDiagram(LLStateDiagram* diagram);
+
+ // process this transition
+ void processTransition(LLFSMTransition &transition, void* user_data);
+
+ // returns current state
+ LLFSMState* getCurrentState() const;
+
+ // execute current state
+ void runCurrentState(void *data);
+
+ // set state by state pointer
+ BOOL setCurrentState(LLFSMState *initial_state, void* user_data, BOOL skip_entry = TRUE);
+
+ // set state by unique ID
+ BOOL setCurrentState(U32 state_id, void* user_data, BOOL skip_entry = TRUE);
+};
+
+#endif //_LL_LLSTATEMACHINE_H
diff --git a/indra/llcharacter/lltargetingmotion.cpp b/indra/llcharacter/lltargetingmotion.cpp
new file mode 100644
index 0000000000..0f3eaa855b
--- /dev/null
+++ b/indra/llcharacter/lltargetingmotion.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltargetingmotion.cpp
+ * @brief Implementation of LLTargetingMotion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "lltargetingmotion.h"
+#include "llcharacter.h"
+#include "v3dmath.h"
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 TORSO_TARGET_HALF_LIFE = 0.25f;
+const F32 MAX_TIME_DELTA = 2.f; //max two seconds a frame for calculating interpolation
+const F32 TARGET_PLANE_THRESHOLD_DOT = 0.6f;
+const F32 TORSO_ROT_FRACTION = 0.5f;
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLTargetingMotion::LLTargetingMotion(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+ mName = "targeting";
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLTargetingMotion()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLTargetingMotion::~LLTargetingMotion()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLTargetingMotion::onInitialize(LLCharacter *character)
+{
+ // save character for future use
+ mCharacter = character;
+
+ mPelvisJoint = mCharacter->getJoint("mPelvis");
+ mTorsoJoint = mCharacter->getJoint("mTorso");
+ mRightHandJoint = mCharacter->getJoint("mWristRight");
+
+ // make sure character skeleton is copacetic
+ if (!mPelvisJoint ||
+ !mTorsoJoint ||
+ !mRightHandJoint)
+ {
+ llwarns << "Invalid skeleton for targeting motion!" << llendl;
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setJoint( mTorsoJoint );
+
+ // add joint states to the pose
+ mTorsoState.setUsage(LLJointState::ROT);
+ addJointState( &mTorsoState );
+
+ return STATUS_SUCCESS;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLTargetingMotion::onActivate()
+{
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLTargetingMotion::onUpdate(F32 time, U8* joint_mask)
+{
+ F32 slerp_amt = LLCriticalDamp::getInterpolant(TORSO_TARGET_HALF_LIFE);
+
+ LLVector3 target;
+ LLVector3* lookAtPoint = (LLVector3*)mCharacter->getAnimationData("LookAtPoint");
+
+ BOOL result = TRUE;
+
+ if (!lookAtPoint)
+ {
+ return TRUE;
+ }
+ else
+ {
+ target = *lookAtPoint;
+ target.normVec();
+ }
+
+ //LLVector3 target_plane_normal = LLVector3(1.f, 0.f, 0.f) * mPelvisJoint->getWorldRotation();
+ //LLVector3 torso_dir = LLVector3(1.f, 0.f, 0.f) * (mTorsoJoint->getWorldRotation() * mTorsoState.getRotation());
+
+ LLVector3 skyward(0.f, 0.f, 1.f);
+ LLVector3 left(skyward % target);
+ left.normVec();
+ LLVector3 up(target % left);
+ up.normVec();
+ LLQuaternion target_aim_rot(target, left, up);
+
+ LLQuaternion cur_torso_rot = mTorsoJoint->getWorldRotation();
+
+ LLVector3 right_hand_at = LLVector3(0.f, -1.f, 0.f) * mRightHandJoint->getWorldRotation();
+ left.setVec(skyward % right_hand_at);
+ left.normVec();
+ up.setVec(right_hand_at % left);
+ up.normVec();
+ LLQuaternion right_hand_rot(right_hand_at, left, up);
+
+ LLQuaternion new_torso_rot = (cur_torso_rot * ~right_hand_rot) * target_aim_rot;
+
+ // find ideal additive rotation to make torso point in correct direction
+ new_torso_rot = new_torso_rot * ~cur_torso_rot;
+
+ // slerp from current additive rotation to ideal additive rotation
+ new_torso_rot = nlerp(slerp_amt, mTorsoState.getRotation(), new_torso_rot);
+
+ // constraint overall torso rotation
+ LLQuaternion total_rot = new_torso_rot * mTorsoJoint->getRotation();
+ total_rot.constrain(F_PI_BY_TWO * 0.8f);
+ new_torso_rot = total_rot * ~mTorsoJoint->getRotation();
+
+ mTorsoState.setRotation(new_torso_rot);
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// LLTargetingMotion::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLTargetingMotion::onDeactivate()
+{
+}
+
+
+// End
diff --git a/indra/llcharacter/lltargetingmotion.h b/indra/llcharacter/lltargetingmotion.h
new file mode 100644
index 0000000000..708dd374a2
--- /dev/null
+++ b/indra/llcharacter/lltargetingmotion.h
@@ -0,0 +1,98 @@
+/**
+ * @file lltargetingmotion.h
+ * @brief Implementation of LLTargetingMotion class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTARGETINGMOTION_H
+#define LL_LLTARGETINGMOTION_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+
+#define TARGETING_EASEIN_DURATION 0.3f
+#define TARGETING_EASEOUT_DURATION 0.5f
+#define TARGETING_PRIORITY LLJoint::HIGH_PRIORITY
+#define MIN_REQUIRED_PIXEL_AREA_TARGETING 1000.f;
+
+
+//-----------------------------------------------------------------------------
+// class LLTargetingMotion
+//-----------------------------------------------------------------------------
+class LLTargetingMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLTargetingMotion(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLTargetingMotion();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLTargetingMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return TARGETING_EASEIN_DURATION; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return TARGETING_EASEOUT_DURATION; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return TARGETING_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_TARGETING; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+public:
+
+ LLCharacter *mCharacter;
+ LLJointState mTorsoState;
+ LLJoint* mPelvisJoint;
+ LLJoint* mTorsoJoint;
+ LLJoint* mRightHandJoint;
+};
+
+#endif // LL_LLTARGETINGMOTION_H
+
diff --git a/indra/llcharacter/llvisualparam.cpp b/indra/llcharacter/llvisualparam.cpp
new file mode 100644
index 0000000000..a6fd9b974f
--- /dev/null
+++ b/indra/llcharacter/llvisualparam.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file llvisualparam.cpp
+ * @brief Implementation of LLPolyMesh class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "linden_common.h"
+
+#include "llvisualparam.h"
+
+//-----------------------------------------------------------------------------
+// LLVisualParamInfo()
+//-----------------------------------------------------------------------------
+LLVisualParamInfo::LLVisualParamInfo()
+ :
+ mID( -1 ),
+ mGroup( VISUAL_PARAM_GROUP_TWEAKABLE ),
+ mMinWeight( 0.f ),
+ mMaxWeight( 1.f ),
+ mDefaultWeight( 0.f ),
+ mSex( SEX_BOTH )
+{
+}
+
+//-----------------------------------------------------------------------------
+// parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamInfo::parseXml(LLXmlTreeNode *node)
+{
+ // attribute: id
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ node->getFastAttributeS32( id_string, mID );
+
+ // attribute: group
+ U32 group = 0;
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if( node->getFastAttributeU32( group_string, group ) )
+ {
+ if( group < NUM_VISUAL_PARAM_GROUPS )
+ {
+ mGroup = (EVisualParamGroup)group;
+ }
+ }
+
+ // attribute: value_min, value_max
+ static LLStdStringHandle value_min_string = LLXmlTree::addAttributeString("value_min");
+ static LLStdStringHandle value_max_string = LLXmlTree::addAttributeString("value_max");
+ node->getFastAttributeF32( value_min_string, mMinWeight );
+ node->getFastAttributeF32( value_max_string, mMaxWeight );
+
+ // attribute: value_default
+ F32 default_weight = 0;
+ static LLStdStringHandle value_default_string = LLXmlTree::addAttributeString("value_default");
+ if( node->getFastAttributeF32( value_default_string, default_weight ) )
+ {
+ mDefaultWeight = llclamp( default_weight, mMinWeight, mMaxWeight );
+ if( default_weight != mDefaultWeight )
+ {
+ llwarns << "value_default attribute is out of range in node " << mName << " " << default_weight << llendl;
+ }
+ }
+
+ // attribute: sex
+ LLString sex = "both";
+ static LLStdStringHandle sex_string = LLXmlTree::addAttributeString("sex");
+ node->getFastAttributeString( sex_string, sex ); // optional
+ if( sex == "both" )
+ {
+ mSex = SEX_BOTH;
+ }
+ else if( sex == "male" )
+ {
+ mSex = SEX_MALE;
+ }
+ else if( sex == "female" )
+ {
+ mSex = SEX_FEMALE;
+ }
+ else
+ {
+ llwarns << "Avatar file: <param> has invalid sex attribute: " << sex << llendl;
+ return FALSE;
+ }
+
+ // attribute: name
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ llwarns << "Avatar file: <param> is missing name attribute" << llendl;
+ return FALSE;
+ }
+
+ // attribute: label
+ static LLStdStringHandle label_string = LLXmlTree::addAttributeString("label");
+ if( !node->getFastAttributeString( label_string, mDisplayName ) )
+ {
+ mDisplayName = mName;
+ }
+
+ // JC - make sure the display name includes the capitalization in the XML file,
+ // not the lowercased version.
+ LLString::toLower(mName);
+
+ // attribute: label_min
+ static LLStdStringHandle label_min_string = LLXmlTree::addAttributeString("label_min");
+ if( !node->getFastAttributeString( label_min_string, mMinName ) )
+ {
+ mMinName = "Less";
+ }
+
+ // attribute: label_max
+ static LLStdStringHandle label_max_string = LLXmlTree::addAttributeString("label_max");
+ if( !node->getFastAttributeString( label_max_string, mMaxName ) )
+ {
+ mMaxName = "More";
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam::LLVisualParam()
+ :
+ mCurWeight( 0.f ),
+ mLastWeight( 0.f ),
+ mNext( NULL ),
+ mTargetWeight( 0.f ),
+ mIsAnimating( FALSE ),
+ mID( -1 ),
+ mInfo( 0 )
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLVisualParam()
+//-----------------------------------------------------------------------------
+LLVisualParam::~LLVisualParam()
+{
+ delete mNext;
+}
+
+/*
+//=============================================================================
+// These virtual functions should always be overridden,
+// but are included here for use as templates
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLVisualParam::setInfo(LLVisualParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParam::parseData(LLXmlTreeNode *node)
+{
+ LLVisualParamInfo *info = new LLVisualParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ return FALSE;
+
+ return TRUE;
+}
+*/
+
+//-----------------------------------------------------------------------------
+// setWeight()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ //RN: allow overshoot
+ mCurWeight = weight;
+ }
+ else if (mInfo)
+ {
+ mCurWeight = llclamp(weight, mInfo->mMinWeight, mInfo->mMaxWeight);
+ }
+ else
+ {
+ mCurWeight = weight;
+ }
+
+ if (mNext)
+ {
+ mNext->setWeight(weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAnimationTarget()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ if (getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ if (mInfo)
+ {
+ mTargetWeight = llclamp(target_value, mInfo->mMinWeight, mInfo->mMaxWeight);
+ }
+ else
+ {
+ mTargetWeight = target_value;
+ }
+ }
+ mIsAnimating = TRUE;
+
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setNextParam()
+//-----------------------------------------------------------------------------
+void LLVisualParam::setNextParam( LLVisualParam *next )
+{
+ llassert(!mNext);
+
+ mNext = next;
+}
+
+//-----------------------------------------------------------------------------
+// animate()
+//-----------------------------------------------------------------------------
+void LLVisualParam::animate( F32 delta, BOOL set_by_user )
+{
+ if (mIsAnimating)
+ {
+ F32 new_weight = ((mTargetWeight - mCurWeight) * delta) + mCurWeight;
+ setWeight(new_weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// stopAnimating()
+//-----------------------------------------------------------------------------
+void LLVisualParam::stopAnimating(BOOL set_by_user)
+{
+ if (mIsAnimating && getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ mIsAnimating = FALSE;
+ setWeight(mTargetWeight, set_by_user);
+ }
+}
diff --git a/indra/llcharacter/llvisualparam.h b/indra/llcharacter/llvisualparam.h
new file mode 100644
index 0000000000..2a8d03d431
--- /dev/null
+++ b/indra/llcharacter/llvisualparam.h
@@ -0,0 +1,131 @@
+/**
+ * @file llvisualparam.h
+ * @brief Implementation of LLPolyMesh class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVisualParam_H
+#define LL_LLVisualParam_H
+
+#include "v3math.h"
+#include "llstring.h"
+#include "llxmltree.h"
+
+class LLPolyMesh;
+class LLXmlTreeNode;
+
+enum ESex
+{
+ SEX_FEMALE = 0x01,
+ SEX_MALE = 0x02,
+ SEX_BOTH = 0x03 // values chosen to allow use as a bit field.
+};
+
+enum EVisualParamGroup
+{
+ VISUAL_PARAM_GROUP_TWEAKABLE,
+ VISUAL_PARAM_GROUP_ANIMATABLE,
+ NUM_VISUAL_PARAM_GROUPS
+};
+
+const S32 MAX_TRANSMITTED_VISUAL_PARAMS = 255;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamInfo
+// Contains shared data for VisualParams
+//-----------------------------------------------------------------------------
+class LLVisualParamInfo
+{
+ friend class LLVisualParam;
+public:
+ LLVisualParamInfo();
+ virtual ~LLVisualParamInfo() {};
+
+ virtual BOOL parseXml(LLXmlTreeNode *node);
+
+protected:
+ S32 mID; // ID associated with VisualParam
+
+ LLString mName; // name (for internal purposes)
+ LLString mDisplayName; // name displayed to the user
+ LLString mMinName; // name associated with minimum value
+ LLString mMaxName; // name associated with maximum value
+ EVisualParamGroup mGroup; // morph group for separating UI controls
+ F32 mMinWeight; // minimum weight that can be assigned to this morph target
+ F32 mMaxWeight; // maximum weight that can be assigned to this morph target
+ F32 mDefaultWeight;
+ ESex mSex; // Which gender(s) this param applies to.
+};
+
+//-----------------------------------------------------------------------------
+// LLVisualParam
+// VIRTUAL CLASS
+// An interface class for a generalized parametric modification of the avatar mesh
+// Contains data that is specific to each Avatar
+//-----------------------------------------------------------------------------
+class LLVisualParam
+{
+public:
+ LLVisualParam();
+ virtual ~LLVisualParam();
+
+ // Special: These functions are overridden by child classes
+ // (They can not be virtual because they use specific derived Info classes)
+ LLVisualParamInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLVisualParamInfo *info);
+
+ // Virtual functions
+ // Pure virtuals
+ //virtual BOOL parseData( LLXmlTreeNode *node ) = 0;
+ virtual void apply( ESex avatar_sex ) = 0;
+ // Default functions
+ virtual void setWeight(F32 weight, BOOL set_by_user);
+ virtual void setAnimationTarget( F32 target_value, BOOL set_by_user );
+ virtual void animate(F32 delta, BOOL set_by_user);
+ virtual void stopAnimating(BOOL set_by_user);
+
+ // Interface methods
+ const S32 getID() { return mID; }
+ void setID(S32 id) { llassert(!mInfo); mID = id; }
+
+ const LLString& getName() const { return mInfo->mName; }
+ const LLString& getDisplayName() const { return mInfo->mDisplayName; }
+ const LLString& getMaxDisplayName() const { return mInfo->mMaxName; }
+ const LLString& getMinDisplayName() const { return mInfo->mMinName; }
+
+ void setDisplayName(const char* s) { mInfo->mDisplayName = s; }
+ void setMaxDisplayName(const char* s) { mInfo->mMaxName = s; }
+ void setMinDisplayName(const char* s) { mInfo->mMinName = s; }
+
+ const EVisualParamGroup getGroup() { return mInfo->mGroup; }
+ F32 getMinWeight() { return mInfo->mMinWeight; }
+ F32 getMaxWeight() { return mInfo->mMaxWeight; }
+ F32 getDefaultWeight() { return mInfo->mDefaultWeight; }
+ ESex getSex() { return mInfo->mSex; }
+
+ F32 getWeight() { return mIsAnimating ? mTargetWeight : mCurWeight; }
+ F32 getCurrentWeight() { return mCurWeight; }
+ F32 getLastWeight() { return mLastWeight; }
+ BOOL isAnimating() { return mIsAnimating; }
+
+ LLVisualParam* getNextParam() { return mNext; }
+ void setNextParam( LLVisualParam *next );
+
+ virtual void setAnimating(BOOL is_animating) { mIsAnimating = is_animating; }
+ BOOL getAnimating() { return mIsAnimating; }
+
+protected:
+ F32 mCurWeight; // current weight
+ F32 mLastWeight; // last weight
+ LLVisualParam* mNext; // next param in a shared chain
+ F32 mTargetWeight; // interpolation target
+ BOOL mIsAnimating; // this value has been given an interpolation target
+
+ S32 mID; // id for storing weight/morphtarget compares compactly
+ LLVisualParamInfo *mInfo;
+};
+
+#endif // LL_LLVisualParam_H
diff --git a/indra/llcommon/bitpack.cpp b/indra/llcommon/bitpack.cpp
new file mode 100644
index 0000000000..4acd533600
--- /dev/null
+++ b/indra/llcommon/bitpack.cpp
@@ -0,0 +1,148 @@
+/**
+ * @file bitpack.cpp
+ * @brief Convert data to packed bit stream
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "bitpack.h"
+
+#if 0
+#include <stdio.h>
+#include <stdlib.h>
+#include "stdtypes.h"
+
+U8 gLoad, gUnLoad;
+U32 gLoadSize, gUnLoadSize, gTotalBits;
+
+const U32 gMaxDataBits = 8;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+void bit_pack(U8 *outbase, U32 *outptr, U8 *total_data, U32 total_dsize)
+{
+ U32 max_data_bits = gMaxDataBits;
+ U32 load_size = gLoadSize, total_bits = gTotalBits;
+ U32 dsize;
+
+ U8 data;
+ U8 load = gLoad;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > max_data_bits)
+ {
+ dsize = max_data_bits;
+ total_dsize -= max_data_bits;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ data <<= (max_data_bits - dsize);
+ while (dsize > 0)
+ {
+ if (load_size == max_data_bits)
+ {
+ *(outbase + (*outptr)++) = load;
+ load_size = 0;
+ load = 0x00;
+ }
+ load <<= 1;
+ load |= (data >> (max_data_bits - 1));
+ data <<= 1;
+ load_size++;
+ total_bits++;
+ dsize--;
+ }
+ }
+
+ gLoad = load;
+ gLoadSize = load_size;
+ gTotalBits = total_bits;
+}
+#endif
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void bit_pack_reset()
+{
+ gLoad = 0x0;
+ gLoadSize = 0;
+ gTotalBits = 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+void bit_pack_flush(U8 *outbase, U32 *outptr)
+{
+ if (gLoadSize)
+ {
+ gTotalBits += gLoadSize;
+ gLoad <<= (gMaxDataBits - gLoadSize);
+ outbase[(*outptr)++] = gLoad;
+ gLoadSize = 0;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+#if 0
+void bit_unpack(U8 *total_retval, U32 total_dsize, U8 *indata, U32 *inptr)
+{
+ U32 max_data_bits = gMaxDataBits;
+ U32 unload_size = gUnLoadSize;
+ U32 dsize;
+ U8 *retval;
+ U8 unload = gUnLoad;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > max_data_bits)
+ {
+ dsize = max_data_bits;
+ total_dsize -= max_data_bits;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ retval = total_data++;
+ *retval = 0x00;
+ while (dsize > 0)
+ {
+ if (unload_size == 0)
+ {
+ unload = indata[(*inptr)++];
+ unload_size = max_data_bits;
+ }
+ *retval <<= 1;
+ *retval |= (unload >> (max_data_bits - 1));
+ unload_size--;
+ unload <<= 1;
+ dsize--;
+ }
+ }
+
+ gUnLoad = unload;
+ gUnLoadSize = unload_size;
+}
+#endif
+
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+void bit_unpack_reset()
+{
+ gUnLoad = 0;
+ gUnLoadSize = 0;
+}
+#endif
diff --git a/indra/llcommon/bitpack.h b/indra/llcommon/bitpack.h
new file mode 100644
index 0000000000..2303aad8ea
--- /dev/null
+++ b/indra/llcommon/bitpack.h
@@ -0,0 +1,190 @@
+/**
+ * @file bitpack.h
+ * @brief Convert data to packed bit stream
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_BITPACK_H
+#define LL_BITPACK_H
+
+#include "llerror.h"
+
+const U32 MAX_DATA_BITS = 8;
+
+
+class LLBitPack
+{
+public:
+ LLBitPack(U8 *buffer, U32 max_size) : mBuffer(buffer), mBufferSize(0), mLoad(0), mLoadSize(0), mTotalBits(0), mMaxSize(max_size)
+ {
+ }
+
+ ~LLBitPack()
+ {
+ }
+
+ void resetBitPacking()
+ {
+ mLoad = 0;
+ mLoadSize = 0;
+ mTotalBits = 0;
+ mBufferSize = 0;
+ }
+
+ U32 bitPack(U8 *total_data, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 data;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ data <<= (MAX_DATA_BITS - dsize);
+ while (dsize > 0)
+ {
+ if (mLoadSize == MAX_DATA_BITS)
+ {
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ mLoad = 0x00;
+ }
+ mLoad <<= 1;
+ mLoad |= (data >> (MAX_DATA_BITS - 1));
+ data <<= 1;
+ mLoadSize++;
+ mTotalBits++;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 bitCopy(U8 *total_data, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 data;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ data = *total_data++;
+
+ while (dsize > 0)
+ {
+ if (mLoadSize == MAX_DATA_BITS)
+ {
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ mLoad = 0x00;
+ }
+ mLoad <<= 1;
+ mLoad |= (data >> (MAX_DATA_BITS - 1));
+ data <<= 1;
+ mLoadSize++;
+ mTotalBits++;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 bitUnpack(U8 *total_retval, U32 total_dsize)
+ {
+ U32 dsize;
+ U8 *retval;
+
+ while (total_dsize > 0)
+ {
+ if (total_dsize > MAX_DATA_BITS)
+ {
+ dsize = MAX_DATA_BITS;
+ total_dsize -= MAX_DATA_BITS;
+ }
+ else
+ {
+ dsize = total_dsize;
+ total_dsize = 0;
+ }
+
+ retval = total_retval++;
+ *retval = 0x00;
+ while (dsize > 0)
+ {
+ if (mLoadSize == 0)
+ {
+#ifdef _DEBUG
+ if (mBufferSize > mMaxSize)
+ {
+ llerrs << "mBufferSize exceeding mMaxSize" << llendl;
+ llerrs << mBufferSize << " > " << mMaxSize << llendl;
+ }
+#endif
+ mLoad = *(mBuffer + mBufferSize++);
+ mLoadSize = MAX_DATA_BITS;
+ }
+ *retval <<= 1;
+ *retval |= (mLoad >> (MAX_DATA_BITS - 1));
+ mLoadSize--;
+ mLoad <<= 1;
+ dsize--;
+ }
+ }
+ return mBufferSize;
+ }
+
+ U32 flushBitPack()
+ {
+ if (mLoadSize)
+ {
+ mLoad <<= (MAX_DATA_BITS - mLoadSize);
+ *(mBuffer + mBufferSize++) = mLoad;
+ if (mBufferSize > mMaxSize)
+ {
+ llerror("mBufferSize exceeding mMaxSize!", 0);
+ }
+ mLoadSize = 0;
+ }
+ return mBufferSize;
+ }
+
+ U8 *mBuffer;
+ U32 mBufferSize;
+ U8 mLoad;
+ U32 mLoadSize;
+ U32 mTotalBits;
+ U32 mMaxSize;
+};
+
+#endif
diff --git a/indra/llcommon/ctype_workaround.h b/indra/llcommon/ctype_workaround.h
new file mode 100644
index 0000000000..8c52b0290c
--- /dev/null
+++ b/indra/llcommon/ctype_workaround.h
@@ -0,0 +1,36 @@
+/**
+ * @file ctype_workaround.h
+ * @brief The workaround is to create some legacy symbols that point
+ * to the correct symbols, which avoids link errors.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef _CTYPE_WORKAROUND_H_
+#define _CTYPE_WORKAROUND_H_
+
+/**
+ * the CTYPE_WORKAROUND is needed for linux dev stations that don't
+ * have the broken libc6 packages needed by our out-of-date static
+ * libs (such as libcrypto and libcurl).
+ *
+ * -- Leviathan 20060113
+*/
+
+#include <ctype.h>
+
+__const unsigned short int *__ctype_b;
+__const __int32_t *__ctype_tolower;
+__const __int32_t *__ctype_toupper;
+
+// call this function at the beginning of main()
+void ctype_workaround()
+{
+ __ctype_b = *(__ctype_b_loc());
+ __ctype_toupper = *(__ctype_toupper_loc());
+ __ctype_tolower = *(__ctype_tolower_loc());
+}
+
+#endif
+
diff --git a/indra/llcommon/doublelinkedlist.h b/indra/llcommon/doublelinkedlist.h
new file mode 100644
index 0000000000..2546d621d0
--- /dev/null
+++ b/indra/llcommon/doublelinkedlist.h
@@ -0,0 +1,1379 @@
+/**
+ * @file doublelinkedlist.h
+ * @brief Provides a standard doubly linked list for fun and profit.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_DOUBLELINKEDLIST_H
+#define LL_DOUBLELINKEDLIST_H
+
+#include "llerror.h"
+#include "llrand.h"
+
+// node that actually contains the data
+template <class DATA_TYPE> class LLDoubleLinkedNode
+{
+public:
+ DATA_TYPE *mDatap;
+ LLDoubleLinkedNode *mNextp;
+ LLDoubleLinkedNode *mPrevp;
+
+
+public:
+ // assign the mDatap pointer
+ LLDoubleLinkedNode(DATA_TYPE *data);
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLDoubleLinkedNode();
+
+ // delete associated data and NULL out pointer
+ void deleteData();
+
+ // remove associated data and NULL out pointer
+ void removeData();
+};
+
+
+const U32 LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH = 4;
+
+template <class DATA_TYPE> class LLDoubleLinkedList
+{
+private:
+ LLDoubleLinkedNode<DATA_TYPE> mHead; // head node
+ LLDoubleLinkedNode<DATA_TYPE> mTail; // tail node
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuep; // The node in the batter's box
+ LLDoubleLinkedNode<DATA_TYPE> *mCurrentp; // The node we're talking about
+
+ // The state stack allows nested exploration of the LLDoubleLinkedList
+ // but should be used with great care
+ LLDoubleLinkedNode<DATA_TYPE> *mQueuepStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH];
+ LLDoubleLinkedNode<DATA_TYPE> *mCurrentpStack[LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH];
+ U32 mStateStackDepth;
+ U32 mCount;
+
+ // mInsertBefore is a pointer to a user-set function that returns
+ // TRUE if "first" should be located before "second"
+ // NOTE: mInsertBefore() should never return TRUE when ("first" == "second")
+ // or never-ending loops can occur
+ BOOL (*mInsertBefore)(DATA_TYPE *first, DATA_TYPE *second);
+
+public:
+ LLDoubleLinkedList();
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLDoubleLinkedList();
+
+ // put data into a node and stick it at the front of the list
+ // set mCurrentp to mQueuep
+ void addData(DATA_TYPE *data);
+
+ // put data into a node and stick it at the end of the list
+ // set mCurrentp to mQueuep
+ void addDataAtEnd(DATA_TYPE *data);
+
+ S32 getLength() const;
+ // search the list starting at mHead.mNextp and remove the link with mDatap == data
+ // set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL removeData(const DATA_TYPE *data);
+
+ // search the list starting at mHead.mNextp and delete the link with mDatap == data
+ // set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL deleteData(DATA_TYPE *data);
+
+ // remove all nodes from the list and delete the associated data
+ void deleteAllData();
+
+ // remove all nodes from the list but do not delete data
+ void removeAllNodes();
+
+ BOOL isEmpty();
+
+ // check to see if data is in list
+ // set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+ // return TRUE if found, FALSE if not found
+ BOOL checkData(const DATA_TYPE *data);
+
+ // NOTE: This next two funtions are only included here
+ // for those too familiar with the LLLinkedList template class.
+ // They are depreciated. resetList() is unecessary while
+ // getCurrentData() is identical to getNextData() and has
+ // a misleading name.
+ //
+ // The recommended way to loop through a list is as follows:
+ //
+ // datap = list.getFirstData();
+ // while (datap)
+ // {
+ // /* do stuff */
+ // datap = list.getNextData();
+ // }
+
+ // place mQueuep on mHead node
+ void resetList();
+
+ // return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ // NOTE: this function is identical to getNextData()
+ DATA_TYPE *getCurrentData();
+
+
+ // reset the list and return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ DATA_TYPE *getFirstData();
+
+
+ // reset the list and return the data at position n, set mCurentp
+ // to that node and bump mQueuep down the list
+ // Note: n=0 will behave like getFirstData()
+ DATA_TYPE *getNthData(U32 n);
+
+ // reset the list and return the last data in it,
+ // set mCurrentp to that node and bump mQueuep up the list
+ DATA_TYPE *getLastData();
+
+ // return data in mQueuep,
+ // set mCurrentp mQueuep and bump mQueuep down the list
+ DATA_TYPE *getNextData();
+
+ // return the data in mQueuep,
+ // set mCurrentp to mQueuep and bump mQueuep up the list
+ DATA_TYPE *getPreviousData();
+
+ // remove the Node at mCurrentp
+ // set mCurrentp to mQueuep
+ void removeCurrentData();
+
+ // delete the Node at mCurrentp
+ // set mCurrentp to mQueuep
+ void deleteCurrentData();
+
+ // remove the Node at mCurrentp and insert it into newlist
+ // set mCurrentp to mQueuep
+ void moveCurrentData(LLDoubleLinkedList<DATA_TYPE> *newlist);
+
+ // insert the node in front of mCurrentp
+ // set mCurrentp to mQueuep
+ void insertNode(LLDoubleLinkedNode<DATA_TYPE> *node);
+
+ // insert the data in front of mCurrentp
+ // set mCurrentp to mQueuep
+ void insertData(DATA_TYPE *data);
+
+ // if mCurrentp has a previous node then :
+ // * swaps mCurrentp with its previous
+ // * set mCurrentp to mQueuep
+ // (convenient for forward bubble-sort)
+ // otherwise does nothing
+ void swapCurrentWithPrevious();
+
+ // if mCurrentp has a next node then :
+ // * swaps mCurrentp with its next
+ // * set mCurrentp to mQueuep
+ // (convenient for backwards bubble-sort)
+ // otherwise does nothing
+ void swapCurrentWithNext();
+
+ // move mCurrentp to the front of the list
+ // set mCurrentp to mQueuep
+ void moveCurrentToFront();
+
+ // move mCurrentp to the end of the list
+ // set mCurrentp to mQueuep
+ void moveCurrentToEnd();
+
+ // set mInsertBefore
+ void setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second));
+
+ // add data in front of first node for which mInsertBefore(datap, node->mDatap) returns TRUE
+ // set mCurrentp to mQueuep
+ BOOL addDataSorted(DATA_TYPE *datap);
+
+ // sort the list using bubble-sort
+ // Yes, this is a different name than the same function in LLLinkedList.
+ // When it comes time for a name consolidation hopefully this one will win.
+ BOOL bubbleSort();
+
+ // does a single bubble sort pass on the list
+ BOOL lazyBubbleSort();
+
+ // returns TRUE if state successfully pushed (state stack not full)
+ BOOL pushState();
+
+ // returns TRUE if state successfully popped (state stack not empty)
+ BOOL popState();
+
+ // empties the state stack
+ void clearStateStack();
+
+ // randomly move the the links in the list for debug or (Discordian) purposes
+ // sets mCurrentp and mQueuep to top of list
+ void scramble();
+
+private:
+ // add node to beginning of list
+ // set mCurrentp to mQueuep
+ void addNode(LLDoubleLinkedNode<DATA_TYPE> *node);
+
+ // add node to end of list
+ // set mCurrentp to mQueuep
+ void addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *node);
+};
+
+//#endif
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+// doublelinkedlist.cpp
+// LLDoubleLinkedList template class implementation file.
+// Provides a standard doubly linked list for fun and profit.
+//
+// Copyright 2001, Linden Research, Inc.
+
+//#include "llerror.h"
+//#include "doublelinkedlist.h"
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLDoubleLinkedNode
+//////////////////////////////////////////////////////////////////////////////////////////
+
+
+// assign the mDatap pointer
+template <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::LLDoubleLinkedNode(DATA_TYPE *data) :
+ mDatap(data), mNextp(NULL), mPrevp(NULL)
+{
+}
+
+
+// destructor does not, by default, destroy associated data
+// however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+template <class DATA_TYPE>
+LLDoubleLinkedNode<DATA_TYPE>::~LLDoubleLinkedNode()
+{
+ if (mDatap)
+ {
+ llerror("Attempting to call LLDoubleLinkedNode destructor with a non-null mDatap!", 1);
+ }
+}
+
+
+// delete associated data and NULL out pointer
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::deleteData()
+{
+ delete mDatap;
+ mDatap = NULL;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedNode<DATA_TYPE>::removeData()
+{
+ mDatap = NULL;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////
+// LLDoubleLinkedList
+//////////////////////////////////////////////////////////////////////////////////////
+
+// <------- up -------
+//
+// mCurrentp
+// mQueuep |
+// | |
+// | |
+// .------. .------. .------. .------.
+// | |---->| |---->| |----->| |-----> NULL
+// NULL <-----| |<----| |<----| |<-----| |
+// _'------' '------' '------' '------:_
+// .------. /| | | |\ .------.
+// NULL <-----|mHead |/ | mQueuep \|mTail |-----> NULL
+// | | mCurrentp | |
+// '------' '------'
+// -------- down --------->
+
+template <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::LLDoubleLinkedList()
+: mHead(NULL), mTail(NULL), mQueuep(NULL)
+{
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+ mInsertBefore = NULL;
+}
+
+
+// destructor destroys list and nodes, but not data in nodes
+template <class DATA_TYPE>
+LLDoubleLinkedList<DATA_TYPE>::~LLDoubleLinkedList()
+{
+ removeAllNodes();
+}
+
+
+// put data into a node and stick it at the front of the list
+// doesn't change mCurrentp nor mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addData(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0);
+ }
+
+ // make the new node
+ LLDoubleLinkedNode<DATA_TYPE> *temp = new LLDoubleLinkedNode<DATA_TYPE> (data);
+
+ // add the node to the front of the list
+ temp->mPrevp = NULL;
+ temp->mNextp = mHead.mNextp;
+ mHead.mNextp = temp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevp = temp;
+ }
+ // otherwise, fix the tail of the list
+ else
+ {
+ mTail.mPrevp = temp;
+ }
+
+ mCount++;
+}
+
+
+// put data into a node and stick it at the end of the list
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addDataAtEnd(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addData()", 0);
+ }
+
+ // make the new node
+ LLDoubleLinkedNode<DATA_TYPE> *nodep = new LLDoubleLinkedNode<DATA_TYPE>(data);
+
+ addNodeAtEnd(nodep);
+ mCount++;
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::removeData(const DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::removeData()", 0);
+ }
+
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ // reset the list back to where it was
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+
+ return b_found;
+}
+
+
+// search the list starting at mHead.mNextp and delete the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::deleteData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::deleteData()", 0);
+ }
+
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ // reset the list back to where it was
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+
+ return b_found;
+}
+
+
+// remove all nodes from the list and delete the associated data
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::deleteAllData()
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ mQueuep = mCurrentp->mNextp;
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = mQueuep;
+ }
+
+ // reset mHead and mQueuep
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+}
+
+
+// remove all nodes from the list but do not delete associated data
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::removeAllNodes()
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ mQueuep = mCurrentp->mNextp;
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = mQueuep;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ mCount = 0;
+}
+
+template <class DATA_TYPE>
+S32 LLDoubleLinkedList<DATA_TYPE>::getLength() const
+{
+// U32 length = 0;
+// for (LLDoubleLinkedNode<DATA_TYPE>* temp = mHead.mNextp; temp != NULL; temp = temp->mNextp)
+// {
+// length++;
+// }
+ return mCount;
+}
+
+// check to see if data is in list
+// set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::checkData(const DATA_TYPE *data)
+{
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ mQueuep = mCurrentp;
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ mCurrentp = mQueuep;
+ return FALSE;
+}
+
+// NOTE: This next two funtions are only included here
+// for those too familiar with the LLLinkedList template class.
+// They are depreciated. resetList() is unecessary while
+// getCurrentData() is identical to getNextData() and has
+// a misleading name.
+//
+// The recommended way to loop through a list is as follows:
+//
+// datap = list.getFirstData();
+// while (datap)
+// {
+// /* do stuff */
+// datap = list.getNextData();
+// }
+
+ // place mCurrentp and mQueuep on first node
+ template <class DATA_TYPE>
+ void LLDoubleLinkedList<DATA_TYPE>::resetList()
+ {
+ mCurrentp = mHead.mNextp;
+ mQueuep = mHead.mNextp;
+ mStateStackDepth = 0;
+ }
+
+
+ // return the data currently pointed to,
+ // set mCurrentp to that node and bump mQueuep down the list
+ template <class DATA_TYPE>
+ DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getCurrentData()
+ {
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+
+// reset the list and return the data currently pointed to,
+// set mCurrentp to that node and bump mQueuep down the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getFirstData()
+{
+ mQueuep = mHead.mNextp;
+ mCurrentp = mQueuep;
+ if (mQueuep)
+ {
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// reset the list and return the data at position n, set mCurentp
+// to that node and bump mQueuep down the list
+// Note: n=0 will behave like getFirstData()
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getNthData(U32 n)
+{
+ mCurrentp = mHead.mNextp;
+
+ if (mCurrentp)
+ {
+ for (U32 i=0; i<n; i++)
+ {
+ mCurrentp = mCurrentp->mNextp;
+ if (!mCurrentp)
+ {
+ break;
+ }
+ }
+ }
+
+ if (mCurrentp)
+ {
+ // bump mQueuep down the list
+ mQueuep = mCurrentp->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ mQueuep = NULL;
+ return NULL;
+ }
+}
+
+
+// reset the list and return the last data in it,
+// set mCurrentp to that node and bump mQueuep up the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getLastData()
+{
+ mQueuep = mTail.mPrevp;
+ mCurrentp = mQueuep;
+ if (mQueuep)
+ {
+ mQueuep = mQueuep->mPrevp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// return the data in mQueuep,
+// set mCurrentp to mQueuep and bump mQueuep down the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getNextData()
+{
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mNextp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// return the data in mQueuep,
+// set mCurrentp to mQueuep and bump mQueuep up the list
+template <class DATA_TYPE>
+DATA_TYPE* LLDoubleLinkedList<DATA_TYPE>::getPreviousData()
+{
+ if (mQueuep)
+ {
+ mCurrentp = mQueuep;
+ mQueuep = mQueuep->mPrevp;
+ return mCurrentp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// remove the Node at mCurrentp
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::removeCurrentData()
+{
+ if (mCurrentp)
+ {
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the node
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCount--;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// delete the Node at mCurrentp
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::deleteCurrentData()
+{
+ if (mCurrentp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // remove the LLDoubleLinkedNode
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCount--;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// remove the Node at mCurrentp and insert it into newlist
+// set mCurrentp to mQueuep, or NULL if (mCurrentp == mQueuep)
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentData(LLDoubleLinkedList<DATA_TYPE> *newlist)
+{
+ if (mCurrentp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // move the node to the new list
+ newlist->addNode(mCurrentp);
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// Inserts the node previous to mCurrentp
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertNode(LLDoubleLinkedNode<DATA_TYPE> *nodep)
+{
+ // don't allow pointer to NULL to be passed
+ if (!nodep)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+ if (!nodep->mDatap)
+ {
+ llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+
+ if (mCurrentp)
+ {
+ if (mCurrentp->mPrevp)
+ {
+ nodep->mPrevp = mCurrentp->mPrevp;
+ nodep->mNextp = mCurrentp;
+ mCurrentp->mPrevp->mNextp = nodep;
+ mCurrentp->mPrevp = nodep;
+ }
+ else // at beginning of list
+ {
+ nodep->mPrevp = NULL;
+ nodep->mNextp = mCurrentp;
+ mHead.mNextp = nodep;
+ mCurrentp->mPrevp = nodep;
+ }
+ mCurrentp = mQueuep;
+ }
+ else // add to front of list
+ {
+ addNode(nodep);
+ }
+}
+
+
+// insert the data in front of mCurrentp
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::insertData(DATA_TYPE *data)
+{
+ if (!data)
+ {
+ llerror("NULL data pointer passed to LLDoubleLinkedList::insertNode()", 0);
+ }
+ LLDoubleLinkedNode<DATA_TYPE> *node = new LLDoubleLinkedNode<DATA_TYPE>(data);
+ insertNode(node);
+ mCount++;
+}
+
+
+// if mCurrentp has a previous node then :
+// * swaps mCurrentp with its previous
+// * set mCurrentp to mQueuep
+// otherwise does nothing
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::swapCurrentWithPrevious()
+{
+ if (mCurrentp)
+ {
+ if (mCurrentp->mPrevp)
+ {
+ // Pull mCurrentp out of list
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // mCurrentp was at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // Fix mCurrentp's pointers
+ mCurrentp->mNextp = mCurrentp->mPrevp;
+ mCurrentp->mPrevp = mCurrentp->mNextp->mPrevp;
+ mCurrentp->mNextp->mPrevp = mCurrentp;
+
+ if (mCurrentp->mPrevp)
+ {
+ // Fix the backward pointer of mCurrentp's new previous
+ mCurrentp->mPrevp->mNextp = mCurrentp;
+ }
+ else // mCurrentp is now at beginning of list
+ {
+ mHead.mNextp = mCurrentp;
+ }
+
+ // Set the list back to the way it was
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+// if mCurrentp has a next node then :
+// * swaps mCurrentp with its next
+// * set mCurrentp to mQueuep
+// otherwise does nothing
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::swapCurrentWithNext()
+{
+ if (mCurrentp)
+ {
+ if (mCurrentp->mNextp)
+ {
+ // Pull mCurrentp out of list
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // mCurrentp was at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // Fix mCurrentp's pointers
+ mCurrentp->mPrevp = mCurrentp->mNextp;
+ mCurrentp->mNextp = mCurrentp->mPrevp->mNextp;
+ mCurrentp->mPrevp->mNextp = mCurrentp;
+
+ if (mCurrentp->mNextp)
+ {
+ // Fix the back pointer of mCurrentp's new next
+ mCurrentp->mNextp->mPrevp = mCurrentp;
+ }
+ else // mCurrentp is now at end of list
+ {
+ mTail.mPrevp = mCurrentp;
+ }
+
+ // Set the list back to the way it was
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+// move mCurrentp to the front of the list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentToFront()
+{
+ if (mCurrentp)
+ {
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ return;
+ }
+
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list
+ {
+ mTail.mPrevp = mCurrentp->mPrevp;
+ }
+
+ // add mCurrentp to beginning of list
+ mCurrentp->mNextp = mHead.mNextp;
+ mHead.mNextp->mPrevp = mCurrentp; // mHead.mNextp MUST be valid,
+ // or the list had only one node
+ // and we would have returned already
+ mCurrentp->mPrevp = NULL;
+ mHead.mNextp = mCurrentp;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+
+}
+
+// move mCurrentp to the end of the list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::moveCurrentToEnd()
+{
+ if (mCurrentp)
+ {
+ // if there is a next one, fix it
+ if (mCurrentp->mNextp)
+ {
+ mCurrentp->mNextp->mPrevp = mCurrentp->mPrevp;
+ }
+ else // otherwise we are at end of list and we're done
+ {
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ return;
+ }
+
+ // if there is a previous one, fix it
+ if (mCurrentp->mPrevp)
+ {
+ mCurrentp->mPrevp->mNextp = mCurrentp->mNextp;
+ }
+ else // otherwise we are at beginning of list
+ {
+ mHead.mNextp = mCurrentp->mNextp;
+ }
+
+ // add mCurrentp to end of list
+ mCurrentp->mPrevp = mTail.mPrevp;
+ mTail.mPrevp->mNextp = mCurrentp; // mTail.mPrevp MUST be valid,
+ // or the list had only one node
+ // and we would have returned already
+ mCurrentp->mNextp = NULL;
+ mTail.mPrevp = mCurrentp;
+
+ // check for redundant pointing
+ if (mCurrentp == mQueuep)
+ {
+ mCurrentp = mQueuep = NULL;
+ }
+ else
+ {
+ mCurrentp = mQueuep;
+ }
+ }
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::setInsertBefore(BOOL (*insert_before)(DATA_TYPE *first, DATA_TYPE *second) )
+{
+ mInsertBefore = insert_before;
+}
+
+
+// add data in front of the first node for which mInsertBefore(datap, node->mDatap) returns TRUE
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::addDataSorted(DATA_TYPE *datap)
+{
+ // don't allow NULL to be passed to addData()
+ if (!datap)
+ {
+ llerror("NULL pointer passed to LLDoubleLinkedList::addDataSorted()", 0);
+ }
+
+ // has mInsertBefore not been set?
+ if (!mInsertBefore)
+ {
+ addData(datap);
+ return FALSE;
+ }
+
+ // is the list empty?
+ if (!mHead.mNextp)
+ {
+ addData(datap);
+ return TRUE;
+ }
+
+ // Note: this step has been added so that the behavior of LLDoubleLinkedList
+ // is as rigorous as the LLLinkedList class about adding duplicate nodes.
+ // Duplicate nodes can cause a problem when sorting if mInsertBefore(foo, foo)
+ // returns TRUE. However, if mInsertBefore(foo, foo) returns FALSE, then there
+ // shouldn't be any reason to exclude duplicate nodes (as we do here).
+ if (checkData(datap))
+ {
+ return FALSE;
+ }
+
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp)
+ {
+ // check to see if datap is already in the list
+ if (datap == mCurrentp->mDatap)
+ {
+ return FALSE;
+ }
+ else if (mInsertBefore(datap, mCurrentp->mDatap))
+ {
+ insertData(datap);
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ addDataAtEnd(datap);
+ return TRUE;
+}
+
+
+// bubble-sort until sorted and return TRUE if anything was sorted
+// leaves mQueuep pointing at last node that was swapped with its mNextp
+//
+// NOTE: if you find this function looping for really long times, then you
+// probably need to check your implementation of mInsertBefore(a,b) and make
+// sure it does not return TRUE when (a == b)!
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::bubbleSort()
+{
+ BOOL b_swapped = FALSE;
+ U32 count = 0;
+ while (lazyBubbleSort())
+ {
+ b_swapped = TRUE;
+ if (count++ > 0x7FFFFFFF)
+ {
+ llwarning("LLDoubleLinkedList::bubbleSort() : too many passes...", 1);
+ llwarning(" make sure the mInsertBefore(a, b) does not return TRUE for a == b", 1);
+ break;
+ }
+ }
+ return b_swapped;
+}
+
+
+// do a single bubble-sort pass and return TRUE if anything was sorted
+// leaves mQueuep pointing at last node that was swapped with its mNextp
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::lazyBubbleSort()
+{
+ // has mInsertBefore been set?
+ if (!mInsertBefore)
+ {
+ return FALSE;
+ }
+
+ // is list empty?
+ mCurrentp = mHead.mNextp;
+ if (!mCurrentp)
+ {
+ return FALSE;
+ }
+
+ BOOL b_swapped = FALSE;
+
+ // the sort will exit after 0x7FFFFFFF nodes or the end of the list, whichever is first
+ S32 length = 0x7FFFFFFF;
+ S32 count = 0;
+
+ while (mCurrentp && mCurrentp->mNextp && count<length)
+ {
+ if (mInsertBefore(mCurrentp->mNextp->mDatap, mCurrentp->mDatap))
+ {
+ b_swapped = TRUE;
+ mQueuep = mCurrentp;
+ swapCurrentWithNext(); // sets mCurrentp to mQueuep
+ }
+ count++;
+ mCurrentp = mCurrentp->mNextp;
+ }
+
+ return b_swapped;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::pushState()
+{
+ if (mStateStackDepth < LLDOUBLE_LINKED_LIST_STATE_STACK_DEPTH)
+ {
+ *(mQueuepStack + mStateStackDepth) = mQueuep;
+ *(mCurrentpStack + mStateStackDepth) = mCurrentp;
+ mStateStackDepth++;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::popState()
+{
+ if (mStateStackDepth > 0)
+ {
+ mStateStackDepth--;
+ mQueuep = *(mQueuepStack + mStateStackDepth);
+ mCurrentp = *(mCurrentpStack + mStateStackDepth);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::clearStateStack()
+{
+ mStateStackDepth = 0;
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// private members
+//////////////////////////////////////////////////////////////////////////////////////////
+
+// add node to beginning of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNode(LLDoubleLinkedNode<DATA_TYPE> *nodep)
+{
+ // add the node to the front of the list
+ nodep->mPrevp = NULL;
+ nodep->mNextp = mHead.mNextp;
+ mHead.mNextp = nodep;
+
+ // if there's something in the list, fix its back pointer
+ if (nodep->mNextp)
+ {
+ nodep->mNextp->mPrevp = nodep;
+ }
+ else // otherwise fix the tail node
+ {
+ mTail.mPrevp = nodep;
+ }
+
+ mCurrentp = mQueuep;
+}
+
+
+// add node to end of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::addNodeAtEnd(LLDoubleLinkedNode<DATA_TYPE> *node)
+{
+ // add the node to the end of the list
+ node->mNextp = NULL;
+ node->mPrevp = mTail.mPrevp;
+ mTail.mPrevp = node;
+
+ // if there's something in the list, fix its back pointer
+ if (node->mPrevp)
+ {
+ node->mPrevp->mNextp = node;
+ }
+ else // otherwise fix the head node
+ {
+ mHead.mNextp = node;
+ }
+
+ mCurrentp = mQueuep;
+}
+
+
+// randomly move nodes in the list for DEBUG (or Discordian) purposes
+// sets mCurrentp and mQueuep to top of list
+template <class DATA_TYPE>
+void LLDoubleLinkedList<DATA_TYPE>::scramble()
+{
+ S32 random_number;
+ DATA_TYPE *datap = getFirstData();
+ while(datap)
+ {
+ random_number = gLindenLabRandomNumber.llrand() % 5;
+
+ if (0 == random_number)
+ {
+ removeCurrentData();
+ addData(datap);
+ }
+ else if (1 == random_number)
+ {
+ removeCurrentData();
+ addDataAtEnd(datap);
+ }
+ else if (2 == random_number)
+ {
+ swapCurrentWithPrevious();
+ }
+ else if (3 == random_number)
+ {
+ swapCurrentWithNext();
+ }
+ datap = getNextData();
+ }
+ mQueuep = mHead.mNextp;
+ mCurrentp = mQueuep;
+}
+
+template <class DATA_TYPE>
+BOOL LLDoubleLinkedList<DATA_TYPE>::isEmpty()
+{
+ return (mCount == 0);
+}
+
+
+#endif
diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h
new file mode 100644
index 0000000000..8147183bec
--- /dev/null
+++ b/indra/llcommon/imageids.h
@@ -0,0 +1,79 @@
+/**
+ * @file imageids.h
+ * @brief Temporary holder for image IDs
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_IMAGEIDS_H
+#define LL_IMAGEIDS_H
+
+#include "lluuid.h"
+
+//
+// USE OF THIS FILE IS DEPRECATED
+//
+// Please use viewerart.ini and the standard
+// art import path. // indicates if file is only
+ // on dataserver, or also
+ // pre-cached on viewer
+
+// Grass Images
+//const LLUUID IMG_GRASS1 ("990c4086-46ce-49bd-8cae-afcc23a08f4e"); // dataserver
+//const LLUUID IMG_GRASS2 ("869e2dcf-21b9-402d-a36d-9a23365cf723"); // dataserver
+//const LLUUID IMG_GRASS3 ("8f97e7a7-f664-4967-9e8f-8d9e8039c1b7"); // dataserver
+
+//const LLUUID IMG_GRASS4 ("8a05131d-35b7-4812-bcfc-a989b0f954ef"); // dataserver
+
+//const LLUUID IMG_GRASS5 ("7d092acb-c69a-4122-b09b-f285e009b185"); // dataserver
+
+const LLUUID IMG_CLEAR ("11ee27f5-43c0-414e-afd5-d7f5688c351f"); // VIEWER
+const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
+
+const LLUUID IMG_DEFAULT ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
+
+//const LLUUID IMG_SAND ("0ff70ead-4562-45f9-9e8a-52b1a3286868"); // VIEWER 1.5k
+//const LLUUID IMG_GRASS ("5ab48dd5-05d0-4f1a-ace6-efd4e2fb3508"); // VIEWER 1.2k
+//const LLUUID IMG_ROCK ("402f8b24-5f9d-4905-b5f8-37baff603e88"); // VIEWER 1.2k
+//const LLUUID IMG_ROCKFACE ("9c88539c-fd04-46b8-bea2-ddf1bcffe3bd"); // VIEWER 1.2k
+const LLUUID IMG_SUN ("cce0f112-878f-4586-a2e2-a8f104bba271"); // dataserver
+const LLUUID IMG_MOON ("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver
+const LLUUID IMG_CLOUD_POOF ("fc4b9f0b-d008-45c6-96a4-01dd947ac621"); // dataserver
+const LLUUID IMG_SHOT ("35f217a3-f618-49cf-bbca-c86d486551a9"); // dataserver
+const LLUUID IMG_SPARK ("d2e75ac1-d0fb-4532-820e-a20034ac814d"); // dataserver
+const LLUUID IMG_FIRE ("aca40aa8-44cf-44ca-a0fa-93e1a2986f82"); // dataserver
+//const LLUUID IMG_WATER ("e510b068-d20d-4612-a08d-fde4d5c15789"); // VIEWER
+const LLUUID IMG_FACE_SELECT ("a85ac674-cb75-4af6-9499-df7c5aaf7a28"); // face selector
+
+//const LLUUID IMG_SHADOW ("5e1de0a8-f9f8-4237-9396-d221126a7c4a"); // dataserver
+//const LLUUID IMG_AVATARSHADOW ("c7d8bbf3-21ee-4f6e-9b20-3cf18425af1d"); // dataserver
+//const LLUUID IMG_BOXSHADOW ("8d86b8cc-4889-408a-8b72-c1961bae53d7"); // dataserver
+//const LLUUID IMG_EYE ("5e3551ae-9971-4814-af99-5117591e937b"); // dataserver
+//const LLUUID IMG_BLUE_FLAME ("d8b62059-7b31-4511-a479-1fe45117948f"); // dataserver
+
+const LLUUID IMG_DEFAULT_AVATAR ("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); // dataserver
+//const LLUUID IMG_ENERGY_BEAM ("09e7bc54-11b9-442a-ae3d-f52e599e466a"); // dataserver
+//const LLUUID IMG_ENERGY_BEAM2 ("de651394-f926-48db-b666-e49d83af1bbc"); // dataserver
+
+//const LLUUID IMG_BRICK_PATH ("a9d0019b-3783-4c7f-959c-322d301918bc"); // dataserver
+
+const LLUUID IMG_EXPLOSION ("68edcf47-ccd7-45b8-9f90-1649d7f12806"); // On dataserver
+const LLUUID IMG_EXPLOSION_2 ("21ce046c-83fe-430a-b629-c7660ac78d7c"); // On dataserver
+const LLUUID IMG_EXPLOSION_3 ("fedea30a-1be8-47a6-bc06-337a04a39c4b"); // On dataserver
+const LLUUID IMG_EXPLOSION_4 ("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); // On dataserver
+//const LLUUID IMG_EXPLOSION_5 ("60f2dec7-675b-4950-b614-85b907d552ea"); // On dataserver
+//const LLUUID IMG_SPLASH_SPRITE ("8a101f63-fe45-49e7-9f8a-e64817daa475"); // On dataserver
+const LLUUID IMG_SMOKE_POOF ("1e63e323-5fe0-452e-92f8-b98bd0f764e3"); // On dataserver
+
+const LLUUID IMG_BIG_EXPLOSION_1 ("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); // On dataserver
+const LLUUID IMG_BIG_EXPLOSION_2 ("9c8eca51-53d5-42a7-bb58-cef070395db8"); // On dataserver
+//const LLUUID IMG_BLUE_BLOOD ("8bc2e3f8-097e-4c87-b417-b0d699d07189"); // On dataserver
+
+const LLUUID IMG_BLOOM1 ("3c59f7fe-9dc8-47f9-8aaf-a9dd1fbc3bef");
+//const LLUUID IMG_BLOOM2 ("9fb76e81-eca0-4b6a-96e1-a6c5a685150b");
+//const LLUUID IMG_BLOOM3 ("fb1fecba-9585-415b-ad15-6e6e3d6c5479");
+
+const LLUUID IMG_PTT_SPEAKER ("89e9fc7c-0b16-457d-be4f-136270759c4d"); // On cache
+
+#endif
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
new file mode 100644
index 0000000000..b8df3cbfe7
--- /dev/null
+++ b/indra/llcommon/indra_constants.h
@@ -0,0 +1,338 @@
+/**
+ * @file indra_constants.h
+ * @brief some useful short term constants for Indra
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_INDRA_CONSTANTS_H
+#define LL_INDRA_CONSTANTS_H
+
+#include "lluuid.h"
+
+// Viewer object cache version, change if object update
+// format changes. JC
+const U32 INDRA_OBJECT_CACHE_VERSION = 11;
+
+// At 45 Hz collisions seem stable and objects seem
+// to settle down at a reasonable rate.
+// JC 3/18/2003
+const F32 HAVOK_TIMESTEP = 1.f / 45.f;
+
+const F32 COLLISION_TOLERANCE = 0.1f;
+
+// Time constants
+const U32 HOURS_PER_LINDEN_DAY = 4;
+const U32 DAYS_PER_LINDEN_YEAR = 11;
+
+const U32 SEC_PER_LINDEN_DAY = HOURS_PER_LINDEN_DAY * 60 * 60;
+const U32 SEC_PER_LINDEN_YEAR = DAYS_PER_LINDEN_YEAR * SEC_PER_LINDEN_DAY;
+
+const F32 REGION_WIDTH_METERS = 256.f;
+const S32 REGION_WIDTH_UNITS = 256;
+const U32 REGION_WIDTH_U32 = 256;
+
+// Bits for simulator performance query flags
+enum LAND_STAT_FLAGS
+{
+ STAT_FILTER_BY_PARCEL = 0x00000001,
+ STAT_FILTER_BY_OWNER = 0x00000002,
+ STAT_FILTER_BY_OBJECT = 0x00000004,
+ STAT_REQUEST_LAST_ENTRY = 0x80000000,
+};
+
+enum LAND_STAT_REPORT_TYPE
+{
+ STAT_REPORT_TOP_SCRIPTS = 0,
+ STAT_REPORT_TOP_COLLIDERS
+};
+
+const U32 STAT_FILTER_MASK = 0x1FFFFFFF;
+
+// Default maximum number of tasks/prims per region.
+const U32 MAX_TASKS_PER_REGION = 15000;
+
+const F32 MIN_AGENT_DEPTH = 0.30f;
+const F32 DEFAULT_AGENT_DEPTH = 0.45f;
+const F32 MAX_AGENT_DEPTH = 0.60f;
+
+const F32 MIN_AGENT_WIDTH = 0.40f;
+const F32 DEFAULT_AGENT_WIDTH = 0.60f;
+const F32 MAX_AGENT_WIDTH = 0.80f;
+
+const F32 MIN_AGENT_HEIGHT = 1.3f - 2.0f * COLLISION_TOLERANCE;
+const F32 DEFAULT_AGENT_HEIGHT = 1.9f;
+const F32 MAX_AGENT_HEIGHT = 2.65f - 2.0f * COLLISION_TOLERANCE;
+
+// For linked sets
+const S32 MAX_CHILDREN_PER_TASK = 255;
+const S32 MAX_CHILDREN_PER_PHYSICAL_TASK = 31;
+
+const S32 MAX_JOINTS_PER_OBJECT = 1; // limiting to 1 until Havok 2.x
+
+const char* const DEFAULT_DMZ_SPACE_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_USER_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_DATA_SERVER = "192.168.0.140";
+const char* const DEFAULT_DMZ_ASSET_SERVER = "http://asset.dmz.lindenlab.com:80";
+
+const char* const DEFAULT_AGNI_SPACE_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_USER_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_DATA_SERVER = "63.211.139.100";
+const char* const DEFAULT_AGNI_ASSET_SERVER = "http://asset.agni.lindenlab.com:80";
+
+// Information about what ports are for what services is in the wiki Name Space Ports page
+const char* const DEFAULT_LOCAL_ASSET_SERVER = "http://localhost:12041/asset/tmp";
+const char* const LOCAL_ASSET_URL_FORMAT = "http://%s:12041/asset";
+
+const U32 DEFAULT_LAUNCHER_PORT = 12029;
+const U32 DEFAULT_BIGBOARD_PORT = 12030;
+const U32 DEFAULT_QUERYSIM_PORT = 12031;
+const U32 DEFAULT_DATA_SERVER_PORT = 12032;
+const U32 DEFAULT_SPACE_SERVER_PORT = 12033;
+const U32 DEFAULT_VIEWER_PORT = 12034;
+const U32 DEFAULT_SIMULATOR_PORT = 12035;
+const U32 DEFAULT_USER_SERVER_PORT = 12036;
+const U32 DEFAULT_RPC_SERVER_PORT = 12037;
+const U32 DEFAULT_LOG_DATA_SERVER_PORT = 12039;
+const U32 DEFAULT_BACKBONE_PORT = 12040;
+const U32 DEFAULT_LOCAL_ASSET_PORT = 12041;
+const U32 DEFAULT_BACKBONE_CAP_PORT = 12042;
+
+// For automatic port discovery when running multiple viewers on one host
+const U32 PORT_DISCOVERY_RANGE_MIN = 13000;
+const U32 PORT_DISCOVERY_RANGE_MAX = PORT_DISCOVERY_RANGE_MIN + 50;
+
+const char LAND_LAYER_CODE = 'L';
+const char WATER_LAYER_CODE = 'W';
+const char WIND_LAYER_CODE = '7';
+const char CLOUD_LAYER_CODE = '8';
+
+// keys
+// Bit masks for various keyboard modifier keys.
+const MASK MASK_NONE = 0x0000;
+const MASK MASK_CONTROL = 0x0001;
+const MASK MASK_ALT = 0x0002;
+const MASK MASK_SHIFT = 0x0004;
+
+// Special keys go into >128
+const KEY KEY_SPECIAL = 0x80; // special keys start here
+const KEY KEY_RETURN = 0x81;
+const KEY KEY_LEFT = 0x82;
+const KEY KEY_RIGHT = 0x83;
+const KEY KEY_UP = 0x84;
+const KEY KEY_DOWN = 0x85;
+const KEY KEY_ESCAPE = 0x86;
+const KEY KEY_BACKSPACE =0x87;
+const KEY KEY_DELETE = 0x88;
+const KEY KEY_SHIFT = 0x89;
+const KEY KEY_CONTROL = 0x8A;
+const KEY KEY_ALT = 0x8B;
+const KEY KEY_HOME = 0x8C;
+const KEY KEY_END = 0x8D;
+const KEY KEY_PAGE_UP = 0x8E;
+const KEY KEY_PAGE_DOWN = 0x8F;
+const KEY KEY_HYPHEN = 0x90;
+const KEY KEY_EQUALS = 0x91;
+const KEY KEY_INSERT = 0x92;
+const KEY KEY_CAPSLOCK = 0x93;
+const KEY KEY_TAB = 0x94;
+const KEY KEY_ADD = 0x95;
+const KEY KEY_SUBTRACT =0x96;
+const KEY KEY_MULTIPLY =0x97;
+const KEY KEY_DIVIDE = 0x98;
+const KEY KEY_F1 = 0xA1;
+const KEY KEY_F2 = 0xA2;
+const KEY KEY_F3 = 0xA3;
+const KEY KEY_F4 = 0xA4;
+const KEY KEY_F5 = 0xA5;
+const KEY KEY_F6 = 0xA6;
+const KEY KEY_F7 = 0xA7;
+const KEY KEY_F8 = 0xA8;
+const KEY KEY_F9 = 0xA9;
+const KEY KEY_F10 = 0xAA;
+const KEY KEY_F11 = 0xAB;
+const KEY KEY_F12 = 0xAC;
+
+const KEY KEY_PAD_UP = 0xC0;
+const KEY KEY_PAD_DOWN = 0xC1;
+const KEY KEY_PAD_LEFT = 0xC2;
+const KEY KEY_PAD_RIGHT = 0xC3;
+const KEY KEY_PAD_HOME = 0xC4;
+const KEY KEY_PAD_END = 0xC5;
+const KEY KEY_PAD_PGUP = 0xC6;
+const KEY KEY_PAD_PGDN = 0xC7;
+const KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle
+const KEY KEY_PAD_INS = 0xC9;
+const KEY KEY_PAD_DEL = 0xCA;
+const KEY KEY_PAD_RETURN = 0xCB;
+const KEY KEY_PAD_ADD = 0xCC; // not used
+const KEY KEY_PAD_SUBTRACT = 0xCD; // not used
+const KEY KEY_PAD_MULTIPLY = 0xCE; // not used
+const KEY KEY_PAD_DIVIDE = 0xCF; // not used
+
+const KEY KEY_BUTTON0 = 0xD0;
+const KEY KEY_BUTTON1 = 0xD1;
+const KEY KEY_BUTTON2 = 0xD2;
+const KEY KEY_BUTTON3 = 0xD3;
+const KEY KEY_BUTTON4 = 0xD4;
+const KEY KEY_BUTTON5 = 0xD5;
+const KEY KEY_BUTTON6 = 0xD6;
+const KEY KEY_BUTTON7 = 0xD7;
+const KEY KEY_BUTTON8 = 0xD8;
+const KEY KEY_BUTTON9 = 0xD9;
+const KEY KEY_BUTTON10 = 0xDA;
+const KEY KEY_BUTTON11 = 0xDB;
+const KEY KEY_BUTTON12 = 0xDC;
+const KEY KEY_BUTTON13 = 0xDD;
+const KEY KEY_BUTTON14 = 0xDE;
+const KEY KEY_BUTTON15 = 0xDF;
+
+const KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only.
+
+const S32 KEY_COUNT = 256;
+
+
+const F32 DEFAULT_WATER_HEIGHT = 20.0f;
+
+// Maturity ratings for simulators
+const U8 SIM_ACCESS_MIN = 0;
+const U8 SIM_ACCESS_TRIAL = 7;
+const U8 SIM_ACCESS_PG = 13;
+const U8 SIM_ACCESS_MATURE = 21;
+const U8 SIM_ACCESS_DOWN = 254;
+const U8 SIM_ACCESS_MAX = SIM_ACCESS_MATURE;
+
+// group constants
+const S32 MAX_AGENT_GROUPS = 25;
+
+// god levels
+const U8 GOD_MAINTENANCE = 250;
+const U8 GOD_FULL = 200;
+const U8 GOD_LIAISON = 150;
+const U8 GOD_CUSTOMER_SERVICE = 100;
+const U8 GOD_LIKE = 1;
+const U8 GOD_NOT = 0;
+
+// "agent id" for things that should be done to ALL agents
+const LLUUID LL_UUID_ALL_AGENTS("44e87126-e794-4ded-05b3-7c42da3d5cdb");
+
+// Governor Linden's agent id.
+const LLUUID GOVERNOR_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1");
+const LLUUID REALESTATE_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1");
+// Maintenance's group id.
+const LLUUID MAINTENANCE_GROUP_ID("dc7b21cd-3c89-fcaa-31c8-25f9ffd224cd");
+
+// Flags for kick message
+const U32 KICK_FLAGS_DEFAULT = 0x0;
+const U32 KICK_FLAGS_FREEZE = 1 << 0;
+const U32 KICK_FLAGS_UNFREEZE = 1 << 1;
+
+const U8 UPD_NONE = 0x00;
+const U8 UPD_POSITION = 0x01;
+const U8 UPD_ROTATION = 0x02;
+const U8 UPD_SCALE = 0x04;
+const U8 UPD_LINKED_SETS = 0x08;
+const U8 UPD_UNIFORM = 0x10; // used with UPD_SCALE
+
+// Agent Update Flags (U8)
+const U8 AU_FLAGS_NONE = 0x00;
+const U8 AU_FLAGS_HIDETITLE = 0x01;
+
+// start location constants
+const U32 START_LOCATION_ID_LAST = 0;
+const U32 START_LOCATION_ID_HOME = 1;
+const U32 START_LOCATION_ID_DIRECT = 2; // for direct teleport
+const U32 START_LOCATION_ID_PARCEL = 3; // for teleports to a parcel
+const U32 START_LOCATION_ID_TELEHUB = 4; // for teleports to a spawnpoint
+const U32 START_LOCATION_ID_URL = 5;
+const U32 START_LOCATION_ID_COUNT = 6;
+
+// group constants
+const U32 GROUP_MIN_SIZE = 2;
+
+// radius within which a chat message is fully audible
+const F32 CHAT_WHISPER_RADIUS = 10.f;
+const F32 CHAT_NORMAL_RADIUS = 20.f;
+const F32 CHAT_SHOUT_RADIUS = 100.f;
+const F32 CHAT_MAX_RADIUS = CHAT_SHOUT_RADIUS;
+const F32 CHAT_MAX_RADIUS_BY_TWO = CHAT_MAX_RADIUS / 2.f;
+
+// this times above gives barely audible radius
+const F32 CHAT_BARELY_AUDIBLE_FACTOR = 2.0f;
+
+// distance in front of speaking agent the sphere is centered
+const F32 CHAT_WHISPER_OFFSET = 5.f;
+const F32 CHAT_NORMAL_OFFSET = 10.f;
+const F32 CHAT_SHOUT_OFFSET = 50.f;
+
+// first clean starts at 3 AM
+const S32 SANDBOX_FIRST_CLEAN_HOUR = 3;
+// clean every <n> hours
+const S32 SANDBOX_CLEAN_FREQ = 12;
+
+const F32 WIND_SCALE_HACK = 2.0f; // hack to make wind speeds more realistic
+
+enum ETerrainBrushType
+{
+ // the valid brush numbers cannot be reordered, because they
+ // are used in the binary LSL format as arguments to llModifyLand()
+ E_LANDBRUSH_LEVEL = 0,
+ E_LANDBRUSH_RAISE = 1,
+ E_LANDBRUSH_LOWER = 2,
+ E_LANDBRUSH_SMOOTH = 3,
+ E_LANDBRUSH_NOISE = 4,
+ E_LANDBRUSH_REVERT = 5,
+ E_LANDBRUSH_INVALID = 6
+};
+
+// media commands
+const U32 PARCEL_MEDIA_COMMAND_STOP = 0;
+const U32 PARCEL_MEDIA_COMMAND_PAUSE = 1;
+const U32 PARCEL_MEDIA_COMMAND_PLAY = 2;
+const U32 PARCEL_MEDIA_COMMAND_LOOP = 3;
+const U32 PARCEL_MEDIA_COMMAND_TEXTURE = 4;
+const U32 PARCEL_MEDIA_COMMAND_URL = 5;
+const U32 PARCEL_MEDIA_COMMAND_TIME = 6;
+const U32 PARCEL_MEDIA_COMMAND_AGENT = 7;
+const U32 PARCEL_MEDIA_COMMAND_UNLOAD = 8;
+const U32 PARCEL_MEDIA_COMMAND_AUTO_ALIGN = 9;
+
+// map item types
+const U32 MAP_ITEM_TELEHUB = 0x01;
+const U32 MAP_ITEM_PG_EVENT = 0x02;
+const U32 MAP_ITEM_MATURE_EVENT = 0x03;
+const U32 MAP_ITEM_POPULAR = 0x04;
+const U32 MAP_ITEM_AGENT_COUNT = 0x05;
+const U32 MAP_ITEM_AGENT_LOCATIONS = 0x06;
+const U32 MAP_ITEM_LAND_FOR_SALE = 0x07;
+const U32 MAP_ITEM_CLASSIFIED = 0x08;
+
+// Crash reporter behavior
+const char* const CRASH_SETTINGS_FILE = "crash_settings.xml";
+const char* const CRASH_BEHAVIOR_SETTING = "CrashBehavior";
+const S32 CRASH_BEHAVIOR_ASK = 0;
+const S32 CRASH_BEHAVIOR_ALWAYS_SEND = 1;
+const S32 CRASH_BEHAVIOR_NEVER_SEND = 2;
+
+// Export/Import return values
+const S32 EXPORT_SUCCESS = 0;
+const S32 EXPORT_ERROR_PERMISSIONS = -1;
+const S32 EXPORT_ERROR_UNKNOWN = -2;
+
+// This is how long the sim will try to teleport you before giving up.
+const F32 TELEPORT_EXPIRY = 15.0f;
+// Additional time (in seconds) to wait per attachment
+const F32 TELEPORT_EXPIRY_PER_ATTACHMENT = 3.f;
+
+// The maximum size of an object extra parameters binary (packed) block
+#define MAX_OBJECT_PARAMS_SIZE 1024
+
+const S32 CHAT_CHANNEL_DEBUG = S32_MAX;
+
+// PLEASE don't add constants here. Every dev will have to do
+// a complete rebuild. Try to find another shared header file,
+// like llregionflags.h, lllslconstants.h, llagentconstants.h,
+// or create a new one. JC
+
+#endif
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
new file mode 100644
index 0000000000..199d380809
--- /dev/null
+++ b/indra/llcommon/linden_common.h
@@ -0,0 +1,53 @@
+/**
+ * @file linden_common.h
+ * @brief Includes common headers that are always safe to include
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LINDEN_COMMON_H
+#define LL_LINDEN_COMMON_H
+
+#include "llpreprocessor.h"
+
+#include <string.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// Work around stupid Microsoft STL warning
+#ifdef LL_WINDOWS
+#pragma warning (disable : 4702) // warning C4702: unreachable code
+#endif // LL_WINDOWS
+
+#include <iostream>
+#include <fstream>
+#include <vector>
+#include <string>
+#include "llfile.h"
+
+#if LL_WINDOWS
+// Limit Windows API to small and manageable set.
+// If you get undefined symbols, find the appropriate
+// Windows header file and include that in your .cpp file.
+// Please don't take this out -- it helps with library
+// compile times. JC
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#endif // LL_WINDOWS
+
+#include "stdtypes.h"
+#include "lldefs.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "lltimer.h"
+#include "llfasttimer.h"
+#include "llsys.h"
+
+#ifdef LL_WINDOWS
+#pragma warning (3 : 4702) // we like level 3, not 4
+#endif // LL_WINDOWS
+
+#endif // not LL_LINDEN_COMMON_H
diff --git a/indra/llcommon/linked_lists.h b/indra/llcommon/linked_lists.h
new file mode 100644
index 0000000000..279b742e9f
--- /dev/null
+++ b/indra/llcommon/linked_lists.h
@@ -0,0 +1,919 @@
+/**
+ * @file linked_lists.h
+ * @brief LLLinkedList class header amd implementation file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LINKED_LISTS_H
+#define LL_LINKED_LISTS_H
+
+/**
+ * Provides a standard doubly linked list for fun and profit
+ * Utilizes a neat trick off of Flipcode where the back pointer is a
+ * pointer to a pointer, allowing easier transfer of nodes between lists, &c
+ * And a template class, of course
+ */
+
+#include "llerror.h"
+
+
+template <class DATA_TYPE> class LLLinkedList
+{
+public:
+ friend class LLLinkNode;
+ // External interface
+
+ // basic constructor
+ LLLinkedList() : mHead(NULL), mCurrentp(NULL), mInsertBefore(NULL)
+ {
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+ }
+
+ // basic constructor
+ LLLinkedList(BOOL (*insert_before)(DATA_TYPE *data_new, DATA_TYPE *data_tested)) : mHead(NULL), mCurrentp(NULL), mInsertBefore(insert_before)
+ {
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+ }
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLLinkedList()
+ {
+ removeAllNodes();
+ }
+
+ // set mInsertBefore
+ void setInsertBefore(BOOL (*insert_before)(DATA_TYPE *data_new, DATA_TYPE *data_tested))
+ {
+ mInsertBefore = insert_before;
+ }
+
+ //
+ // WARNING!!!!!!!
+ // addData and addDataSorted are NOT O(1) operations, but O(n) because they check
+ // for existence of the data in the linked list first. Why, I don't know - djs
+ // If you don't care about dupes, use addDataNoCheck
+ //
+
+ // put data into a node and stick it at the front of the list
+ inline BOOL addData(DATA_TYPE *data);
+
+ // put data into a node and sort into list by mInsertBefore()
+ // calls normal add if mInsertBefore isn't set
+ inline BOOL addDataSorted(DATA_TYPE *data);
+
+ inline BOOL addDataNoCheck(DATA_TYPE *data);
+
+ // bubbleSortList
+ // does an improved bubble sort of the list . . . works best with almost sorted data
+ // does nothing if mInsertBefore isn't set
+ // Nota Bene: Swaps are accomplished by swapping data pointers
+ inline void bubbleSortList();
+
+ // put data into a node and stick it at the end of the list
+ inline BOOL addDataAtEnd(DATA_TYPE *data);
+
+ // returns number of items in the list
+ inline S32 getLength() const;
+
+ inline BOOL isEmpty();
+
+ // search the list starting at mHead.mNextp and remove the link with mDatap == data
+ // leave mCurrentp and mCurrentOperatingp on the next entry
+ // return TRUE if found, FALSE if not found
+ inline BOOL removeData(DATA_TYPE *data);
+
+ // search the list starting at mHead.mNextp and delete the link with mDatap == data
+ // leave mCurrentp and mCurrentOperatingp on the next entry
+ // return TRUE if found, FALSE if not found
+ inline BOOL deleteData(DATA_TYPE *data);
+
+ // remove all nodes from the list and delete the associated data
+ inline void deleteAllData();
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ // check to see if data is in list
+ // if TRUE then mCurrentp and mCurrentOperatingp point to data
+ inline BOOL checkData(DATA_TYPE *data);
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE *getNextData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getFirstData();
+
+ // reset the list and return the data at position n, set mCurentOperatingp to that node and bump mCurrentp
+ // Note: n is zero-based
+ inline DATA_TYPE *getNthData( U32 n);
+
+ // reset the list and return the last data in it, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getLastData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // remove the Node at mCurentOperatingp and add it to newlist
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void moveCurrentData(LLLinkedList *newlist, BOOL b_sort);
+
+ BOOL moveData(DATA_TYPE *data, LLLinkedList *newlist, BOOL b_sort);
+
+ // delete the Node at mCurentOperatingp
+ // leave mCurrentp anf mCurentOperatingp on the next entry
+ void deleteCurrentData();
+
+private:
+ // node that actually contains the data
+ class LLLinkNode
+ {
+ public:
+ // assign the mDatap pointer
+ LLLinkNode(DATA_TYPE *data) : mDatap(data), mNextp(NULL), mPrevpp(NULL)
+ {
+ }
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLLinkNode()
+ {
+ if (mDatap)
+ {
+ llerror("Attempting to call LLLinkNode destructor with a non-null mDatap!", 1);
+ }
+ }
+
+ // delete associated data and NULL out pointer
+ void deleteData()
+ {
+ delete mDatap;
+ mDatap = NULL;
+ }
+
+ // NULL out pointer
+ void removeData()
+ {
+ mDatap = NULL;
+ }
+
+ DATA_TYPE *mDatap;
+ LLLinkNode *mNextp;
+ LLLinkNode **mPrevpp;
+ };
+
+ // add a node at the front of the list
+ void addData(LLLinkNode *node)
+ {
+ // don't allow NULL to be passed to addData
+ if (!node)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ // add the node to the front of the list
+ node->mPrevpp = &mHead.mNextp;
+ node->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (node->mNextp)
+ {
+ node->mNextp->mPrevpp = &node->mNextp;
+ }
+
+ mHead.mNextp = node;
+ }
+
+ LLLinkNode mHead; // fake head node. . . makes pointer operations faster and easier
+ LLLinkNode *mCurrentp; // mCurrentp is the Node that getCurrentData returns
+ LLLinkNode *mCurrentOperatingp; // this is the node that the various mumbleCurrentData functions act on
+ BOOL (*mInsertBefore)(DATA_TYPE *data_new, DATA_TYPE *data_tested); // user function set to allow sorted lists
+ U32 mCount;
+};
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addData(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ if ( checkData(data))
+ {
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the front of the list
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevpp = &temp->mNextp;
+ }
+
+ mHead.mNextp = temp;
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataNoCheck(DATA_TYPE *data)
+{
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the front of the list
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = mHead.mNextp;
+
+ // if there's something in the list, fix its back pointer
+ if (temp->mNextp)
+ {
+ temp->mNextp->mPrevpp = &temp->mNextp;
+ }
+
+ mHead.mNextp = temp;
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataSorted(DATA_TYPE *data)
+{
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addDataSorted", 0);
+ }
+
+ if (checkData(data))
+ {
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // mInsertBefore not set?
+ if (!mInsertBefore)
+ {
+ addData(data);
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // empty list?
+ if (!mHead.mNextp)
+ {
+ addData(data);
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return TRUE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // walk the list until mInsertBefore returns true
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp->mNextp)
+ {
+ if (mInsertBefore(data, mCurrentp->mDatap))
+ {
+ // insert before the current one
+ temp->mPrevpp = mCurrentp->mPrevpp;
+ temp->mNextp = mCurrentp;
+ *(temp->mPrevpp) = temp;
+ mCurrentp->mPrevpp = &temp->mNextp;
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+ }
+ else
+ {
+ mCurrentp = mCurrentp->mNextp;
+ }
+ }
+
+ // on the last element, add before?
+ if (mInsertBefore(data, mCurrentp->mDatap))
+ {
+ // insert before the current one
+ temp->mPrevpp = mCurrentp->mPrevpp;
+ temp->mNextp = mCurrentp;
+ *(temp->mPrevpp) = temp;
+ mCurrentp->mPrevpp = &temp->mNextp;
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ }
+ else // insert after
+ {
+ temp->mPrevpp = &mCurrentp->mNextp;
+ temp->mNextp = NULL;
+ mCurrentp->mNextp = temp;
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ }
+ mCount++;
+ return TRUE;
+}
+
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::bubbleSortList()
+{
+ // mInsertBefore not set
+ if (!mInsertBefore)
+ {
+ return;
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ BOOL b_swapped = FALSE;
+ DATA_TYPE *temp;
+
+ // Nota Bene: This will break if more than 0x7FFFFFFF members in list!
+ S32 length = 0x7FFFFFFF;
+ S32 count = 0;
+ do
+ {
+ b_swapped = FALSE;
+ mCurrentp = mHead.mNextp;
+ count = 0;
+ while ( (count + 1 < length)
+ &&(mCurrentp))
+ {
+ if (mCurrentp->mNextp)
+ {
+ if (!mInsertBefore(mCurrentp->mDatap, mCurrentp->mNextp->mDatap))
+ {
+ // swap data pointers!
+ temp = mCurrentp->mDatap;
+ mCurrentp->mDatap = mCurrentp->mNextp->mDatap;
+ mCurrentp->mNextp->mDatap = temp;
+ b_swapped = TRUE;
+ }
+ }
+ else
+ {
+ break;
+ }
+ count++;
+ mCurrentp = mCurrentp->mNextp;
+ }
+ length = count;
+ } while (b_swapped);
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::addDataAtEnd(DATA_TYPE *data)
+{
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::addData", 0);
+ }
+
+ if (checkData(data))
+ {
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return FALSE;
+ }
+
+ // make the new node
+ LLLinkNode *temp = new LLLinkNode(data);
+
+ // add the node to the end of the list
+
+ // if empty, add to the front and be done with it
+ if (!mHead.mNextp)
+ {
+ temp->mPrevpp = &mHead.mNextp;
+ temp->mNextp = NULL;
+ mHead.mNextp = temp;
+ }
+ else
+ {
+ // otherwise, walk to the end of the list
+ mCurrentp = mHead.mNextp;
+ while (mCurrentp->mNextp)
+ {
+ mCurrentp = mCurrentp->mNextp;
+ }
+ temp->mPrevpp = &mCurrentp->mNextp;
+ temp->mNextp = NULL;
+ mCurrentp->mNextp = temp;
+ }
+
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ mCount++;
+ return TRUE;
+}
+
+
+// returns number of items in the list
+template <class DATA_TYPE>
+S32 LLLinkedList<DATA_TYPE>::getLength() const
+{
+// S32 length = 0;
+// for (LLLinkNode* temp = mHead.mNextp; temp != NULL; temp = temp->mNextp)
+// {
+// length++;
+// }
+ return mCount;
+}
+
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::isEmpty()
+{
+ return (mCount == 0);
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// leave mCurrentp and mCurrentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::removeData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+
+ // if we were on the one we want to delete, bump the cached copies
+ if (mCurrentOperatingp == tcurrop)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+ else if (mCurrentOperatingp == tcurr)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->removeData();
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+// search the list starting at mHead.mNextp and delete the link with mDatap == data
+// leave mCurrentp and mCurrentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::deleteData(DATA_TYPE *data)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // delete the LLLinkNode
+ // if we were on the one we want to delete, bump the cached copies
+ if (mCurrentOperatingp == tcurrop)
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ // and delete the associated data
+ llassert(mCurrentOperatingp);
+ mCurrentp = mCurrentOperatingp->mNextp;
+ mCurrentOperatingp->deleteData();
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+ // remove all nodes from the list and delete the associated data
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::deleteAllData()
+{
+ LLLinkNode *temp;
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mNextp;
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+}
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::removeAllNodes()
+{
+ LLLinkNode *temp;
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mNextp;
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ mCount = 0;
+}
+
+// check to see if data is in list
+// if TRUE then mCurrentp and mCurrentOperatingp point to data
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::checkData(DATA_TYPE *data)
+{
+ // reset mCurrentp
+ mCurrentp = mHead.mNextp;
+
+ while (mCurrentp)
+ {
+ if (mCurrentp->mDatap == data)
+ {
+ mCurrentOperatingp = mCurrentp;
+ return TRUE;
+ }
+ mCurrentp = mCurrentp->mNextp;
+ }
+ mCurrentOperatingp = mCurrentp;
+ return FALSE;
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::resetList()
+{
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getFirstData()
+{
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mNextp;
+ return mCurrentOperatingp->mDatap;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Note: n is zero-based
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getNthData( U32 n )
+{
+ mCurrentOperatingp = mHead.mNextp;
+
+ // if empty, return NULL
+ if (!mCurrentOperatingp)
+ {
+ return NULL;
+ }
+
+ for( U32 i = 0; i < n; i++ )
+ {
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ if( !mCurrentOperatingp )
+ {
+ return NULL;
+ }
+ }
+
+ mCurrentp = mCurrentOperatingp->mNextp;
+ return mCurrentOperatingp->mDatap;
+}
+
+
+// reset the list and return the last data in it, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE>
+DATA_TYPE *LLLinkedList<DATA_TYPE>::getLastData()
+{
+ mCurrentOperatingp = mHead.mNextp;
+
+ // if empty, return NULL
+ if (!mCurrentOperatingp)
+ return NULL;
+
+ // walk until we're pointing at the last entry
+ while (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ mCurrentp = mCurrentOperatingp->mNextp;
+ return mCurrentOperatingp->mDatap;
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->removeData();
+ delete mCurrentOperatingp;
+ mCount--;
+ mCurrentOperatingp = mCurrentp;
+ }
+}
+
+// remove the Node at mCurentOperatingp and add it to newlist
+// leave mCurrentp and mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::moveCurrentData(LLLinkedList *newlist, BOOL b_sort)
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+ // move the node to the new list
+ newlist->addData(mCurrentOperatingp);
+ if (b_sort)
+ bubbleSortList();
+ mCurrentOperatingp = mCurrentp;
+ }
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedList<DATA_TYPE>::moveData(DATA_TYPE *data, LLLinkedList *newlist, BOOL b_sort)
+{
+ BOOL b_found = FALSE;
+ // don't allow NULL to be passed to addData
+ if (!data)
+ {
+ llerror("NULL pointer passed to LLLinkedList::removeData", 0);
+ }
+
+ LLLinkNode *tcurr = mCurrentp;
+ LLLinkNode *tcurrop = mCurrentOperatingp;
+
+ mCurrentp = mHead.mNextp;
+ mCurrentOperatingp = mHead.mNextp;
+
+ while (mCurrentOperatingp)
+ {
+ if (mCurrentOperatingp->mDatap == data)
+ {
+ b_found = TRUE;
+
+ // remove the node
+
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // if we were on the one we want to delete, bump the cached copies
+ if ( (mCurrentOperatingp == tcurrop)
+ ||(mCurrentOperatingp == tcurr))
+ {
+ tcurrop = tcurr = mCurrentOperatingp->mNextp;
+ }
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+ // move the node to the new list
+ newlist->addData(mCurrentOperatingp);
+ if (b_sort)
+ newlist->bubbleSortList();
+ mCurrentOperatingp = mCurrentp;
+ break;
+ }
+ mCurrentOperatingp = mCurrentOperatingp->mNextp;
+ }
+ // restore
+ mCurrentp = tcurr;
+ mCurrentOperatingp = tcurrop;
+ return b_found;
+}
+
+// delete the Node at mCurentOperatingp
+// leave mCurrentp anf mCurentOperatingp on the next entry
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+void LLLinkedList<DATA_TYPE>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ // remove the node
+ // if there is a next one, fix it
+ if (mCurrentOperatingp->mNextp)
+ {
+ mCurrentOperatingp->mNextp->mPrevpp = mCurrentOperatingp->mPrevpp;
+ }
+ *(mCurrentOperatingp->mPrevpp) = mCurrentOperatingp->mNextp;
+
+ // remove the LLLinkNode
+ mCurrentp = mCurrentOperatingp->mNextp;
+
+ mCurrentOperatingp->deleteData();
+ if (mCurrentOperatingp->mDatap)
+ llerror("This is impossible!", 0);
+ delete mCurrentOperatingp;
+ mCurrentOperatingp = mCurrentp;
+ mCount--;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llagentconstants.h b/indra/llcommon/llagentconstants.h
new file mode 100644
index 0000000000..66b4b564bf
--- /dev/null
+++ b/indra/llcommon/llagentconstants.h
@@ -0,0 +1,141 @@
+/**
+ * @file llagentconstants.h
+ * @author James Cook, Andrew Meadows, Richard Nelson
+ * @brief Shared constants through the system for agents.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTCONSTANTS_H
+#define LL_LLAGENTCONSTANTS_H
+
+const U32 CONTROL_AT_POS_INDEX = 0;
+const U32 CONTROL_AT_NEG_INDEX = 1;
+const U32 CONTROL_LEFT_POS_INDEX = 2;
+const U32 CONTROL_LEFT_NEG_INDEX = 3;
+const U32 CONTROL_UP_POS_INDEX = 4;
+const U32 CONTROL_UP_NEG_INDEX = 5;
+const U32 CONTROL_PITCH_POS_INDEX = 6;
+const U32 CONTROL_PITCH_NEG_INDEX = 7;
+const U32 CONTROL_YAW_POS_INDEX = 8;
+const U32 CONTROL_YAW_NEG_INDEX = 9;
+const U32 CONTROL_FAST_AT_INDEX = 10;
+const U32 CONTROL_FAST_LEFT_INDEX = 11;
+const U32 CONTROL_FAST_UP_INDEX = 12;
+const U32 CONTROL_FLY_INDEX = 13;
+const U32 CONTROL_STOP_INDEX = 14;
+const U32 CONTROL_FINISH_ANIM_INDEX = 15;
+const U32 CONTROL_STAND_UP_INDEX = 16;
+const U32 CONTROL_SIT_ON_GROUND_INDEX = 17;
+const U32 CONTROL_MOUSELOOK_INDEX = 18;
+const U32 CONTROL_NUDGE_AT_POS_INDEX = 19;
+const U32 CONTROL_NUDGE_AT_NEG_INDEX = 20;
+const U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21;
+const U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22;
+const U32 CONTROL_NUDGE_UP_POS_INDEX = 23;
+const U32 CONTROL_NUDGE_UP_NEG_INDEX = 24;
+const U32 CONTROL_TURN_LEFT_INDEX = 25;
+const U32 CONTROL_TURN_RIGHT_INDEX = 26;
+const U32 CONTROL_AWAY_INDEX = 27;
+const U32 CONTROL_LBUTTON_DOWN_INDEX = 28;
+const U32 CONTROL_LBUTTON_UP_INDEX = 29;
+const U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30;
+const U32 CONTROL_ML_LBUTTON_UP_INDEX = 31;
+const U32 TOTAL_CONTROLS = 32;
+
+const U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX;
+const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX;
+const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX;
+const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX;
+const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX;
+const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX;
+const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX;
+const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX;
+const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX;
+const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX;
+
+const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX;
+const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX;
+const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX;
+
+const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX;
+const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX;
+const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX;
+const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX;
+const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX;
+const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX;
+
+const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX;
+const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX;
+const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX;
+const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX;
+const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX;
+const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX;
+
+const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX;
+
+const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX;
+const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX;
+const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX;
+const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX;
+
+const U32 AGENT_CONTROL_AT = AGENT_CONTROL_AT_POS
+ | AGENT_CONTROL_AT_NEG
+ | AGENT_CONTROL_NUDGE_AT_POS
+ | AGENT_CONTROL_NUDGE_AT_NEG;
+
+const U32 AGENT_CONTROL_LEFT = AGENT_CONTROL_LEFT_POS
+ | AGENT_CONTROL_LEFT_NEG
+ | AGENT_CONTROL_NUDGE_LEFT_POS
+ | AGENT_CONTROL_NUDGE_LEFT_NEG;
+
+const U32 AGENT_CONTROL_UP = AGENT_CONTROL_UP_POS
+ | AGENT_CONTROL_UP_NEG
+ | AGENT_CONTROL_NUDGE_UP_POS
+ | AGENT_CONTROL_NUDGE_UP_NEG;
+
+const U32 AGENT_CONTROL_HORIZONTAL = AGENT_CONTROL_AT
+ | AGENT_CONTROL_LEFT;
+
+const U32 AGENT_CONTROL_NOT_USED_BY_LSL = AGENT_CONTROL_FLY
+ | AGENT_CONTROL_STOP
+ | AGENT_CONTROL_FINISH_ANIM
+ | AGENT_CONTROL_STAND_UP
+ | AGENT_CONTROL_SIT_ON_GROUND
+ | AGENT_CONTROL_MOUSELOOK
+ | AGENT_CONTROL_AWAY;
+
+const U32 AGENT_CONTROL_MOVEMENT = AGENT_CONTROL_AT
+ | AGENT_CONTROL_LEFT
+ | AGENT_CONTROL_UP;
+
+const U32 AGENT_CONTROL_ROTATION = AGENT_CONTROL_PITCH_POS
+ | AGENT_CONTROL_PITCH_NEG
+ | AGENT_CONTROL_YAW_POS
+ | AGENT_CONTROL_YAW_NEG;
+
+const U32 AGENT_CONTROL_NUDGE = AGENT_CONTROL_NUDGE_AT_POS
+ | AGENT_CONTROL_NUDGE_AT_NEG
+ | AGENT_CONTROL_NUDGE_LEFT_POS
+ | AGENT_CONTROL_NUDGE_LEFT_NEG;
+
+
+// move these up so that we can hide them in "State" for object updates
+// (for now)
+const U32 AGENT_ATTACH_OFFSET = 4;
+const U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET;
+const U32 AGENT_ATTACH_CLEAR = 0x00;
+
+// RN: this method swaps the upper and lower nibbles to maintain backward
+// compatibility with old objects that only used the upper nibble
+#define ATTACHMENT_ID_FROM_STATE(state) ((S32)((((U8)state & AGENT_ATTACH_MASK) >> 4) | (((U8)state & ~AGENT_ATTACH_MASK) << 4)))
+
+// test state for use in testing grabbing the camera
+const U32 AGENT_CAMERA_OBJECT = 0x1 << 3;
+
+const F32 MAX_ATTACHMENT_DIST = 3.5f; // meters?
+
+#endif
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
new file mode 100644
index 0000000000..50648a2d30
--- /dev/null
+++ b/indra/llcommon/llapp.cpp
@@ -0,0 +1,616 @@
+/**
+ * @file llapp.cpp
+ * @brief Implementation of the LLApp class.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapp.h"
+
+#include "llcommon.h"
+#include "llapr.h"
+#include "llerrorthread.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+//
+// Signal handling
+//
+// Windows uses structured exceptions, so it's handled a bit differently.
+//
+#if LL_WINDOWS
+LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
+#else
+#include <unistd.h> // for fork()
+void setup_signals();
+void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+const S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
+#endif
+
+// the static application instance
+LLApp* LLApp::sApplication = NULL;
+
+// Local flag for whether or not to do logging in signal handlers.
+//static
+BOOL LLApp::sLogInSignal = FALSE;
+
+// static
+LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
+LLAppErrorHandler LLApp::sErrorHandler = NULL;
+BOOL LLApp::sErrorThreadRunning = FALSE;
+#if !LL_WINDOWS
+LLApp::child_map LLApp::sChildMap;
+LLAtomicU32* LLApp::sSigChildCount = NULL;
+LLAppChildCallback LLApp::sDefaultChildCallback = NULL;
+#endif
+
+
+LLApp::LLApp() : mThreadErrorp(NULL)
+{
+ // Set our status to running
+ setStatus(APP_STATUS_RUNNING);
+
+ LLCommon::initClass();
+
+#if !LL_WINDOWS
+ // This must be initialized before the error handler.
+ sSigChildCount = new LLAtomicU32(0);
+#endif
+
+ // Setup error handling
+ setupErrorHandling();
+
+ // initialize the options structure. We need to make this an array
+ // because the structured data will not auto-allocate if we
+ // reference an invalid location with the [] operator.
+ mOptions = LLSD::emptyArray();
+ LLSD sd;
+ for(int i = 0; i < PRIORITY_COUNT; ++i)
+ {
+ mOptions.append(sd);
+ }
+
+ // Make sure we clean up APR when we exit
+ // Don't need to do this if we're cleaning up APR in the destructor
+ //atexit(ll_cleanup_apr);
+
+ // Set the application to this instance.
+ sApplication = this;
+}
+
+
+LLApp::~LLApp()
+{
+#if !LL_WINDOWS
+ delete sSigChildCount;
+ sSigChildCount = NULL;
+#endif
+ setStopped();
+ // HACK: wait for the error thread to clean itself
+ ms_sleep(20);
+ if (mThreadErrorp)
+ {
+ delete mThreadErrorp;
+ mThreadErrorp = NULL;
+ }
+
+ LLCommon::cleanupClass();
+}
+
+// static
+LLApp* LLApp::instance()
+{
+ return sApplication;
+}
+
+
+LLSD LLApp::getOption(const std::string& name) const
+{
+ LLSD rv;
+ LLSD::array_const_iterator iter = mOptions.beginArray();
+ LLSD::array_const_iterator end = mOptions.endArray();
+ for(; iter != end; ++iter)
+ {
+ rv = (*iter)[name];
+ if(rv.isDefined()) break;
+ }
+ return rv;
+}
+
+bool LLApp::parseCommandOptions(int argc, char** argv)
+{
+ LLSD commands;
+ std::string name;
+ std::string value;
+ for(int ii = 1; ii < argc; ++ii)
+ {
+ if(argv[ii][0] != '-')
+ {
+ llinfos << "Did not find option identifier while parsing token: "
+ << argv[ii] << llendl;
+ return false;
+ }
+ int offset = 1;
+ if(argv[ii][1] == '-') ++offset;
+ name.assign(&argv[ii][offset]);
+ if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
+ {
+ // we found another option after this one or we have
+ // reached the end. simply record that this option was
+ // found and continue.
+ commands[name] = true;
+ continue;
+ }
+ ++ii;
+ value.assign(argv[ii]);
+ commands[name] = value;
+ }
+ setOptionData(PRIORITY_COMMAND_LINE, commands);
+ return true;
+}
+
+bool LLApp::setOptionData(OptionPriority level, LLSD data)
+{
+ if((level < 0)
+ || (level >= PRIORITY_COUNT)
+ || (data.type() != LLSD::TypeMap))
+ {
+ return false;
+ }
+ mOptions[level] = data;
+ return true;
+}
+
+LLSD LLApp::getOptionData(OptionPriority level)
+{
+ if((level < 0) || (level >= PRIORITY_COUNT))
+ {
+ return LLSD();
+ }
+ return mOptions[level];
+}
+
+void LLApp::stepFrame()
+{
+ // Update the static frame timer.
+ LLFrameTimer::updateFrameTime();
+
+ // Run ready runnables
+ mRunner.run();
+}
+
+
+void LLApp::setupErrorHandling()
+{
+ // Error handling is done by starting up an error handling thread, which just sleeps and
+ // occasionally checks to see if the app is in an error state, and sees if it needs to be run.
+
+#if LL_WINDOWS
+ // Windows doesn't have the same signal handling mechanisms as UNIX, thus APR doesn't provide
+ // a signal handling thread implementation.
+ // What we do is install an unhandled exception handler, which will try to do the right thing
+ // in the case of an error (generate a minidump)
+
+ // Disable this until the viewer gets ported so server crashes can be JIT debugged.
+ //LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+ //prev_filter = SetUnhandledExceptionFilter(default_windows_exception_handler);
+#else
+ //
+ // Start up signal handling.
+ //
+ // There are two different classes of signals. Synchronous signals are delivered to a specific
+ // thread, asynchronous signals can be delivered to any thread (in theory)
+ //
+
+ setup_signals();
+
+#endif
+
+ //
+ // Start the error handling thread, which is responsible for taking action
+ // when the app goes into the APP_STATUS_ERROR state
+ //
+ llinfos << "LLApp::setupErrorHandling - Starting error thread" << llendl;
+ mThreadErrorp = new LLErrorThread();
+ mThreadErrorp->setUserData((void *) this);
+ mThreadErrorp->start();
+}
+
+
+void LLApp::setErrorHandler(LLAppErrorHandler handler)
+{
+ LLApp::sErrorHandler = handler;
+}
+
+// static
+void LLApp::runErrorHandler()
+{
+ if (LLApp::sErrorHandler)
+ {
+ LLApp::sErrorHandler();
+ }
+
+ //llinfos << "App status now STOPPED" << llendl;
+ LLApp::setStopped();
+}
+
+
+// static
+void LLApp::setStatus(EAppStatus status)
+{
+ sStatus = status;
+}
+
+
+// static
+void LLApp::setError()
+{
+ setStatus(APP_STATUS_ERROR);
+}
+
+
+// static
+void LLApp::setQuitting()
+{
+ if (!isExiting())
+ {
+ // If we're already exiting, we don't want to reset our state back to quitting.
+ llinfos << "Setting app state to QUITTING" << llendl;
+ setStatus(APP_STATUS_QUITTING);
+ }
+}
+
+
+// static
+void LLApp::setStopped()
+{
+ setStatus(APP_STATUS_STOPPED);
+}
+
+
+// static
+bool LLApp::isStopped()
+{
+ return (APP_STATUS_STOPPED == sStatus);
+}
+
+
+// static
+bool LLApp::isRunning()
+{
+ return (APP_STATUS_RUNNING == sStatus);
+}
+
+
+// static
+bool LLApp::isError()
+{
+ return (APP_STATUS_ERROR == sStatus);
+}
+
+
+// static
+bool LLApp::isQuitting()
+{
+ return (APP_STATUS_QUITTING == sStatus);
+}
+
+bool LLApp::isExiting()
+{
+ return isQuitting() || isError();
+}
+
+#if !LL_WINDOWS
+// static
+U32 LLApp::getSigChildCount()
+{
+ if (sSigChildCount)
+ {
+ return U32(*sSigChildCount);
+ }
+ return 0;
+}
+
+// static
+void LLApp::incSigChildCount()
+{
+ if (sSigChildCount)
+ {
+ (*sSigChildCount)++;
+ }
+}
+
+#endif
+
+
+// static
+int LLApp::getPid()
+{
+#if LL_WINDOWS
+ return 0;
+#else
+ return getpid();
+#endif
+}
+
+#if LL_WINDOWS
+LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop)
+{
+ // Translate the signals/exceptions into cross-platform stuff
+ // Windows implementation
+
+ // Make sure the user sees something to indicate that the app crashed.
+ LONG retval;
+
+ if (LLApp::isError())
+ {
+ llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
+ retval = EXCEPTION_EXECUTE_HANDLER;
+ return retval;
+ }
+
+ // Flag status to error, so thread_error starts its work
+ LLApp::setError();
+
+ // Block in the exception handler until the app has stopped
+ // This is pretty sketchy, but appears to work just fine
+ while (!LLApp::isStopped())
+ {
+ ms_sleep(10);
+ }
+
+ //
+ // Generate a minidump if we can.
+ //
+ // FIXME: This needs to be ported over form the viewer-specific LLWinDebug class
+
+ //
+ // At this point, we always want to exit the app. There's no graceful
+ // recovery for an unhandled exception.
+ //
+ // Just kill the process.
+ retval = EXCEPTION_EXECUTE_HANDLER;
+ return retval;
+}
+
+#else //!LL_WINDOWS
+void LLApp::setChildCallback(pid_t pid, LLAppChildCallback callback)
+{
+ LLChildInfo child_info;
+ child_info.mCallback = callback;
+ LLApp::sChildMap[pid] = child_info;
+}
+
+void LLApp::setDefaultChildCallback(LLAppChildCallback callback)
+{
+ LLApp::sDefaultChildCallback = callback;
+}
+
+pid_t LLApp::fork()
+{
+ pid_t pid = ::fork();
+ if( pid < 0 )
+ {
+ int system_error = errno;
+ llwarns << "Unable to fork! Operating system error code: "
+ << system_error << llendl;
+ }
+ else if (pid == 0)
+ {
+ // Sleep a bit to allow the parent to set up child callbacks.
+ ms_sleep(10);
+
+ // We need to disable signal handling, because we don't have a
+ // signal handling thread anymore.
+ setupErrorHandling();
+ }
+ else
+ {
+ llinfos << "Forked child process " << pid << llendl;
+ }
+ return pid;
+}
+
+void setup_signals()
+{
+ //
+ // Set up signal handlers that may result in program termination
+ //
+ struct sigaction act;
+ act.sa_sigaction = default_unix_signal_handler;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+ sigaction(SIGCHLD, &act, NULL);
+ sigaction(SIGUSR2, &act, NULL);
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+}
+
+void clear_signals()
+{
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+ sigaction(SIGABRT, &act, NULL);
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+ sigaction(SIGCHLD, &act, NULL);
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(SIGUSR2, &act, NULL);
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+}
+
+
+
+void default_unix_signal_handler(int signum, siginfo_t *info, void *)
+{
+ // Unix implementation of synchronous signal handler
+ // This runs in the thread that threw the signal.
+ // We do the somewhat sketchy operation of blocking in here until the error handler
+ // has gracefully stopped the app.
+
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << llendl;
+ }
+
+
+ switch (signum)
+ {
+ case SIGALRM:
+ case SIGUSR2:
+ // We don't care about these signals, ignore them
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Ignoring this signal" << llendl;
+ }
+ return;
+ case SIGCHLD:
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
+ }
+ // Check result code for all child procs for which we've registered callbacks
+ // THIS WILL NOT WORK IF SIGCHLD IS SENT w/o killing the child (Go, launcher!)
+ // FIXME: Now that we're using SIGACTION, we can actually implement the launcher behavior to determine
+ // who sent the SIGCHLD even if it doesn't result in child termination
+ if (LLApp::sChildMap.count(info->si_pid))
+ {
+ LLApp::sChildMap[info->si_pid].mGotSigChild = TRUE;
+ }
+
+ LLApp::incSigChildCount();
+
+ return;
+ case SIGABRT:
+ // Abort just results in termination of the app, no funky error handling.
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got SIGABRT, terminating" << llendl;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ case LL_SMACKDOWN_SIGNAL: // Smackdown treated just like any other app termination, for now
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Handling smackdown signal!" << llendl;
+ }
+ else
+ {
+ // Don't log anything, even errors - this is because this signal could happen anywhere.
+ gErrorStream.setLevel(LLErrorStream::NONE);
+ }
+
+ // Change the signal that we reraise to SIGABRT, so we generate a core dump.
+ signum = SIGABRT;
+ case SIGPIPE:
+ case SIGBUS:
+ case SIGSEGV:
+ case SIGQUIT:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Handling fatal signal!" << llendl;
+ }
+ if (LLApp::isError())
+ {
+ // Received second fatal signal while handling first, just die right now
+ // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
+ clear_signals();
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got another fatal signal while in the error handler, die now!" << llendl;
+ }
+ raise(signum);
+ return;
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
+ }
+ // Flag status to ERROR, so thread_error does its work.
+ LLApp::setError();
+ // Block in the signal handler until somebody says that we're done.
+ while (LLApp::sErrorThreadRunning && !LLApp::isStopped())
+ {
+ ms_sleep(10);
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - App is stopped, reraising signal" << llendl;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ case SIGINT:
+ case SIGHUP:
+ case SIGTERM:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << llendl;
+ }
+ // Graceful exit
+ // Just set our state to quitting, not error
+ if (LLApp::isQuitting() || LLApp::isError())
+ {
+ // We're already trying to die, just ignore this signal
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
+ }
+ return;
+ }
+ LLApp::setQuitting();
+ return;
+ default:
+ if (LLApp::sLogInSignal)
+ {
+ llwarns << "Signal handler - Unhandled signal, ignoring!" << llendl;
+ }
+ }
+}
+
+#endif // !WINDOWS
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
new file mode 100644
index 0000000000..da5662c54d
--- /dev/null
+++ b/indra/llcommon/llapp.h
@@ -0,0 +1,259 @@
+/**
+ * @file llapp.h
+ * @brief Declaration of the LLApp class.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPP_H
+#define LL_LLAPP_H
+
+#include <map>
+#include "llapr.h"
+#include "llrun.h"
+#include "llsd.h"
+
+// Forward declarations
+class LLErrorThread;
+class LLApp;
+
+
+typedef void (*LLAppErrorHandler)();
+typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
+
+#if !LL_WINDOWS
+extern const S32 LL_SMACKDOWN_SIGNAL;
+
+// Clear all of the signal handlers (which we want to do for the child process when we fork
+void clear_signals();
+
+class LLChildInfo
+{
+public:
+ LLChildInfo() : mGotSigChild(FALSE), mCallback(NULL) {}
+ BOOL mGotSigChild;
+ LLAppChildCallback mCallback;
+};
+#endif
+
+class LLApp
+{
+ friend class LLErrorThread;
+public:
+ typedef enum e_app_status
+ {
+ APP_STATUS_RUNNING, // The application is currently running - the default status
+ APP_STATUS_QUITTING, // The application is currently quitting - threads should listen for this and clean up
+ APP_STATUS_STOPPED, // The application is no longer running - tells the error thread it can exit
+ APP_STATUS_ERROR // The application had a fatal error occur - tells the error thread to run
+ } EAppStatus;
+
+
+ LLApp();
+ virtual ~LLApp();
+
+ /**
+ * @brief Return the static app instance if one was created.
+ */
+ static LLApp* instance();
+
+ /** @name Runtime options */
+ //@{
+ /**
+ * @brief Enumeration to specify option priorities in highest to
+ * lowest order.
+ */
+ enum OptionPriority
+ {
+ PRIORITY_RUNTIME_OVERRIDE,
+ PRIORITY_COMMAND_LINE,
+ PRIORITY_SPECIFIC_CONFIGURATION,
+ PRIORITY_GENERAL_CONFIGURATION,
+ PRIORITY_DEFAULT,
+ PRIORITY_COUNT
+ };
+
+ /**
+ * @brief Get the application option at the highest priority.
+ *
+ * If the return value is undefined, the option does not exist.
+ * @param name The name of the option.
+ * @return Returns the option data.
+ */
+ LLSD getOption(const std::string& name) const;
+
+ /**
+ * @brief Parse command line options and insert them into
+ * application command line options.
+ *
+ * The name inserted into the option will have leading option
+ * identifiers (a minus or double minus) stripped. All options
+ * with values will be stored as a string, while all options
+ * without values will be stored as true.
+ * @param argc The argc passed into main().
+ * @param argv The argv passed into main().
+ * @return Returns true if the parse succeeded.
+ */
+ bool parseCommandOptions(int argc, char** argv);
+
+ /**
+ * @brief Set the options at the specified priority.
+ *
+ * This function completely replaces the options at the priority
+ * level with the data specified. This function will make sure
+ * level and data might be valid before doing the replace.
+ * @param level The priority level of the data.
+ * @param data The data to set.
+ * @return Returns true if the option was set.
+ */
+ bool setOptionData(OptionPriority level, LLSD data);
+
+ /**
+ * @brief Get the option data at the specified priority.
+ *
+ * This method is probably not so useful except when merging
+ * information.
+ * @param level The priority level of the data.
+ * @return Returns The data (if any) at the level priority.
+ */
+ LLSD getOptionData(OptionPriority level);
+ //@}
+
+
+
+ //
+ // Main application logic
+ //
+ virtual bool init() = 0; // Override to do application initialization
+
+ //
+ // cleanup()
+ //
+ // It's currently assumed that the cleanup() method will only get
+ // called from the main thread or the error handling thread, as it will
+ // likely do thread shutdown, among other things.
+ //
+ virtual bool cleanup() = 0; // Override to do application cleanup
+
+ //
+ // mainLoop()
+ //
+ // Runs the application main loop. It's assumed that when you exit
+ // this method, the application is in one of the cleanup states, either QUITTING or ERROR
+ //
+ virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
+
+
+ //
+ // Application status
+ //
+ static void setQuitting(); // Set status to QUITTING, the app is now shutting down
+ static void setStopped(); // Set status to STOPPED, the app is done running and should exit
+ static void setError(); // Set status to ERROR, the error handler should run
+ static bool isStopped();
+ static bool isRunning();
+ static bool isQuitting();
+ static bool isError();
+ static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
+#if !LL_WINDOWS
+ static U32 getSigChildCount();
+ static void incSigChildCount();
+#endif
+ static int getPid();
+
+ //
+ // Error handling methods
+ //
+ void setErrorHandler(LLAppErrorHandler handler);
+
+#if !LL_WINDOWS
+ //
+ // Child process handling (Unix only for now)
+ //
+ // Set a callback to be run on exit of a child process
+ // WARNING! This callback is run from the signal handler due to the extreme crappiness of
+ // Linux threading requiring waitpid() to be called from the thread that spawned the process.
+ // At some point I will make this more behaved, but I'm not going to fix this right now - djs
+ void setChildCallback(pid_t pid, LLAppChildCallback callback);
+
+ // The child callback to run if no specific handler is set
+ void setDefaultChildCallback(LLAppChildCallback callback);
+
+ // Fork and do the proper signal handling/error handling mojo
+ // WARNING: You need to make sure your signal handling callback is correct after
+ // you fork, because not all threads are duplicated when you fork!
+ pid_t fork();
+#endif
+
+ /**
+ * @brief Get a reference to the application runner
+ *
+ * Please use the runner with caution. Since the Runner usage
+ * pattern is not yet clear, this method just gives access to it
+ * to add and remove runnables.
+ * @return Returns the application runner. Do not save the
+ * pointer past the caller's stack frame.
+ */
+ LLRunner& getRunner() { return mRunner; }
+
+public:
+ typedef std::map<std::string, std::string> string_map;
+ string_map mOptionMap; // Contains all command-line options and arguments in a map
+
+protected:
+
+ static void setStatus(EAppStatus status); // Use this to change the application status.
+ static EAppStatus sStatus; // Reflects current application status
+ static BOOL sErrorThreadRunning; // Set while the error thread is running
+
+#if !LL_WINDOWS
+ static LLAtomicU32* sSigChildCount; // Number of SIGCHLDs received.
+ typedef std::map<pid_t, LLChildInfo> child_map; // Map key is a PID
+ static child_map sChildMap;
+ static LLAppChildCallback sDefaultChildCallback;
+#endif
+
+ /**
+ * @brief This method is called once a frame to do once a frame tasks.
+ */
+ void stepFrame();
+
+private:
+ void setupErrorHandling(); // Do platform-specific error-handling setup (signals, structured exceptions)
+
+ static void runErrorHandler();
+
+ // FIXME: On Windows, we need a routine to reset the structured exception handler when some evil driver has taken it over for their own purposes
+
+ typedef int(*signal_handler_func)(int signum);
+ static LLAppErrorHandler sErrorHandler;
+
+ // Default application threads
+ LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
+
+ // This is the application level runnable scheduler.
+ LLRunner mRunner;
+
+ /** @name Runtime option implementation */
+ //@{
+
+ // The application options.
+ LLSD mOptions;
+
+ //@}
+
+private:
+ // the static application instance if it was created.
+ static LLApp* sApplication;
+
+
+#if !LL_WINDOWS
+ friend void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+#endif
+
+public:
+ static BOOL sLogInSignal;
+};
+
+#endif // LL_LLAPP_H
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
new file mode 100644
index 0000000000..a7fc6a40a7
--- /dev/null
+++ b/indra/llcommon/llapr.cpp
@@ -0,0 +1,201 @@
+/**
+ * @file llapr.cpp
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
+apr_thread_mutex_t *gLogMutexp = NULL;
+
+
+void ll_init_apr()
+{
+ if (!gAPRPoolp)
+ {
+ // Initialize APR and create the global pool
+ apr_initialize();
+ apr_pool_create(&gAPRPoolp, NULL);
+
+ // Initialize the logging mutex
+ apr_thread_mutex_create(&gLogMutexp, APR_THREAD_MUTEX_DEFAULT, gAPRPoolp);
+ }
+}
+
+
+void ll_cleanup_apr()
+{
+ llinfos << "Cleaning up APR" << llendl;
+
+ 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 (gAPRPoolp)
+ {
+ apr_pool_destroy(gAPRPoolp);
+ gAPRPoolp = NULL;
+ }
+ apr_terminate();
+}
+
+//
+// LLScopedLock
+//
+LLScopedLock::LLScopedLock(apr_thread_mutex_t* mutex) : mMutex(mutex)
+{
+ if(mutex)
+ {
+ if(ll_apr_warn_status(apr_thread_mutex_lock(mMutex)))
+ {
+ mLocked = false;
+ }
+ else
+ {
+ mLocked = true;
+ }
+ }
+ else
+ {
+ mLocked = false;
+ }
+}
+
+LLScopedLock::~LLScopedLock()
+{
+ unlock();
+}
+
+void LLScopedLock::unlock()
+{
+ if(mLocked)
+ {
+ if(!ll_apr_warn_status(apr_thread_mutex_unlock(mMutex)))
+ {
+ mLocked = false;
+ }
+ }
+}
+
+//
+// Misc functions
+//
+bool ll_apr_warn_status(apr_status_t status)
+{
+ if(APR_SUCCESS == status) return false;
+ char buf[MAX_STRING]; /* Flawfinder: ignore */
+ llwarns << "APR: " << apr_strerror(status, buf, MAX_STRING) << llendl;
+ return true;
+}
+
+void ll_apr_assert_status(apr_status_t status)
+{
+ llassert(ll_apr_warn_status(status) == false);
+}
+
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep)
+{
+ apr_file_t* apr_file;
+ apr_status_t s;
+ s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ if (sizep)
+ {
+ *sizep = 0;
+ }
+ return NULL;
+ }
+
+ if (sizep)
+ {
+ S32 file_size = 0;
+ apr_off_t offset = 0;
+ if (apr_file_seek(apr_file, APR_END, &offset) == APR_SUCCESS)
+ {
+ file_size = (S32)offset;
+ offset = 0;
+ apr_file_seek(apr_file, APR_SET, &offset);
+ }
+ *sizep = file_size;
+ }
+
+ return apr_file;
+}
+
+S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes)
+{
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_read(apr_file, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ return 0;
+ }
+ else
+ {
+ return (S32)sz;
+ }
+}
+
+
+S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes)
+{
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_write(apr_file, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ return 0;
+ }
+ else
+ {
+ return (S32)sz;
+ }
+}
+
+S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset)
+{
+ apr_off_t apr_offset = offset;
+ apr_status_t s = apr_file_seek(apr_file, where, &apr_offset);
+ if (s != APR_SUCCESS)
+ {
+ return -1;
+ }
+ else
+ {
+ return (S32)apr_offset;
+ }
+}
+
+bool ll_apr_file_remove(const LLString& filename)
+{
+ apr_status_t s;
+ s = apr_file_remove(filename.c_str(), gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ llwarns << "ll_apr_file_remove failed on file: " << filename << llendl;
+ return false;
+ }
+ return true;
+}
+
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname)
+{
+ apr_status_t s;
+ s = apr_file_rename(filename.c_str(), newname.c_str(), gAPRPoolp);
+ if (s != APR_SUCCESS)
+ {
+ llwarns << "ll_apr_file_rename failed on file: " << filename << llendl;
+ return false;
+ }
+ return true;
+}
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
new file mode 100644
index 0000000000..1e9e944eef
--- /dev/null
+++ b/indra/llcommon/llapr.h
@@ -0,0 +1,129 @@
+/**
+ * @file llapr.h
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPR_H
+#define LL_LLAPR_H
+
+#if LL_LINUX
+#include <sys/param.h> // Need PATH_MAX in APR headers...
+#endif
+
+#include "boost/noncopyable.hpp"
+
+#include "apr-1/apr_thread_proc.h"
+#include "apr-1/apr_thread_mutex.h"
+#include "apr-1/apr_getopt.h"
+#include "apr-1/apr_signal.h"
+#include "apr-1/apr_atomic.h"
+#include "llstring.h"
+
+
+/**
+ * @brief initialize the common apr constructs -- apr itself, the
+ * global pool, and a mutex.
+ */
+void ll_init_apr();
+
+/**
+ * @brief Cleanup those common apr constructs.
+ */
+void ll_cleanup_apr();
+
+/**
+ * @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
+ * <b>not</b> thread safe.
+ */
+class 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;
+};
+
+template <typename Type> class LLAtomic32
+{
+public:
+ LLAtomic32<Type>() {};
+ LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
+ ~LLAtomic32<Type>() {};
+
+ operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
+ Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
+ void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
+ void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
+ Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
+ Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
+
+private:
+ apr_uint32_t mData;
+};
+
+typedef LLAtomic32<U32> LLAtomicU32;
+typedef LLAtomic32<S32> LLAtomicS32;
+
+// File IO convenience functions.
+// Returns NULL if the file fails to openm sets *sizep to file size of not NULL
+// abbreviated flags
+#define LL_APR_R (APR_READ) // "r"
+#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w"
+#define LL_APR_RB (APR_READ|APR_BINARY) // "rb"
+#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
+#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
+#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
+apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep = NULL);
+// Returns actual offset, -1 if seek fails
+S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset);
+// Returns bytes read/written, 0 if read/write fails
+S32 ll_apr_file_read(apr_file_t* apr_file, void* buf, S32 nbytes);
+S32 ll_apr_file_write(apr_file_t* apr_file, const void* buf, S32 nbytes);
+bool ll_apr_file_remove(const LLString& filename);
+bool ll_apr_file_rename(const LLString& filename, const LLString& newname);
+
+/**
+ * @brief Function which approprately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+bool ll_apr_warn_status(apr_status_t status);
+
+void ll_apr_assert_status(apr_status_t status);
+
+extern "C" apr_pool_t* gAPRPoolp; // Global APR memory pool
+
+#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
new file mode 100644
index 0000000000..f6d9f166e1
--- /dev/null
+++ b/indra/llcommon/llassettype.cpp
@@ -0,0 +1,219 @@
+/**
+ * @file llassettype.cpp
+ * @brief Implementatino of LLAssetType functionality.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <time.h>
+
+#include "llassettype.h"
+#include "lltimer.h"
+
+// I added lookups for exact text of asset type enums in addition to the ones below, so shoot me. -Steve
+
+struct asset_info_t
+{
+ LLAssetType::EType type;
+ const char* desc;
+};
+
+asset_info_t asset_types[] =
+{
+ { LLAssetType::AT_TEXTURE, "TEXTURE" },
+ { LLAssetType::AT_SOUND, "SOUND" },
+ { LLAssetType::AT_CALLINGCARD, "CALLINGCARD" },
+ { LLAssetType::AT_LANDMARK, "LANDMARK" },
+ { LLAssetType::AT_SCRIPT, "SCRIPT" },
+ { LLAssetType::AT_CLOTHING, "CLOTHING" },
+ { LLAssetType::AT_OBJECT, "OBJECT" },
+ { LLAssetType::AT_NOTECARD, "NOTECARD" },
+ { LLAssetType::AT_CATEGORY, "CATEGORY" },
+ { LLAssetType::AT_ROOT_CATEGORY, "ROOT_CATEGORY" },
+ { LLAssetType::AT_LSL_TEXT, "LSL_TEXT" },
+ { LLAssetType::AT_LSL_BYTECODE, "LSL_BYTECODE" },
+ { LLAssetType::AT_TEXTURE_TGA, "TEXTURE_TGA" },
+ { LLAssetType::AT_BODYPART, "BODYPART" },
+ { LLAssetType::AT_TRASH, "TRASH" },
+ { LLAssetType::AT_SNAPSHOT_CATEGORY, "SNAPSHOT_CATEGORY" },
+ { LLAssetType::AT_LOST_AND_FOUND, "LOST_AND_FOUND" },
+ { LLAssetType::AT_SOUND_WAV, "SOUND_WAV" },
+ { LLAssetType::AT_IMAGE_TGA, "IMAGE_TGA" },
+ { LLAssetType::AT_IMAGE_JPEG, "IMAGE_JPEG" },
+ { LLAssetType::AT_ANIMATION, "ANIMATION" },
+ { LLAssetType::AT_GESTURE, "GESTURE" },
+ { LLAssetType::AT_SIMSTATE, "SIMSTATE" },
+ { LLAssetType::AT_NONE, "NONE" },
+};
+
+LLAssetType::EType LLAssetType::getType(const LLString& sin)
+{
+ LLString s = sin;
+ LLString::toUpper(s);
+ for (S32 idx = 0; ;idx++)
+ {
+ asset_info_t* info = asset_types + idx;
+ if (info->type == LLAssetType::AT_NONE)
+ break;
+ if (s == info->desc)
+ return info->type;
+ }
+ return LLAssetType::AT_NONE;
+}
+
+LLString LLAssetType::getDesc(LLAssetType::EType type)
+{
+ for (S32 idx = 0; ;idx++)
+ {
+ asset_info_t* info = asset_types + idx;
+ if (type == info->type)
+ return LLString(info->desc);
+ if (info->type == LLAssetType::AT_NONE)
+ break;
+ }
+ return LLString("BAD TYPE");
+}
+
+//============================================================================
+
+// The asset type names are limited to 8 characters.
+// static
+const char* LLAssetType::mAssetTypeNames[LLAssetType::AT_COUNT] =
+{
+ "texture",
+ "sound",
+ "callcard",
+ "landmark",
+ "script",
+ "clothing",
+ "object",
+ "notecard",
+ "category",
+ "root",
+ "lsltext",
+ "lslbyte",
+ "txtr_tga",// Intentionally spelled this way. Limited to eight characters.
+ "bodypart",
+ "trash",
+ "snapshot",
+ "lstndfnd",
+ "snd_wav",
+ "img_tga",
+ "jpeg",
+ "animatn",
+ "gesture",
+ "simstate",
+};
+
+// This table is meant for decoding to human readable form. Put any
+// and as many printable characters you want in each one.
+// See also llinventory.cpp INVENTORY_TYPE_HUMAN_NAMES
+const char* LLAssetType::mAssetTypeHumanNames[LLAssetType::AT_COUNT] =
+{
+ "texture",
+ "sound",
+ "calling card",
+ "landmark",
+ "legacy script",
+ "clothing",
+ "object",
+ "note card",
+ "folder",
+ "root",
+ "lsl2 script",
+ "lsl bytecode",
+ "tga texture",
+ "body part",
+ "trash",
+ "snapshot",
+ "lost and found",
+ "sound",
+ "targa image",
+ "jpeg image",
+ "animation",
+ "gesture",
+ "simstate",
+};
+
+///----------------------------------------------------------------------------
+/// class LLAssetType
+///----------------------------------------------------------------------------
+
+// static
+const char* LLAssetType::lookup( LLAssetType::EType type )
+{
+ if( (type >= 0) && (type < AT_COUNT ))
+ {
+ return mAssetTypeNames[ S32( type ) ];
+ }
+ else
+ {
+ return "-1";
+ }
+}
+
+// static
+LLAssetType::EType LLAssetType::lookup( const char* name )
+{
+ for( S32 i = 0; i < AT_COUNT; i++ )
+ {
+ if( 0 == strcmp(name, mAssetTypeNames[i]) )
+ {
+ // match
+ return (EType)i;
+ }
+ }
+ return AT_NONE;
+}
+
+// static
+const char* LLAssetType::lookupHumanReadable(LLAssetType::EType type)
+{
+ if( (type >= 0) && (type < AT_COUNT ))
+ {
+ return mAssetTypeHumanNames[S32(type)];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+EDragAndDropType LLAssetType::lookupDragAndDropType( EType asset )
+{
+ switch( asset )
+ {
+ case AT_TEXTURE: return DAD_TEXTURE;
+ case AT_SOUND: return DAD_SOUND;
+ case AT_CALLINGCARD: return DAD_CALLINGCARD;
+ case AT_LANDMARK: return DAD_LANDMARK;
+ case AT_SCRIPT: return DAD_NONE;
+ case AT_CLOTHING: return DAD_CLOTHING;
+ case AT_OBJECT: return DAD_OBJECT;
+ case AT_NOTECARD: return DAD_NOTECARD;
+ case AT_CATEGORY: return DAD_CATEGORY;
+ case AT_ROOT_CATEGORY: return DAD_ROOT_CATEGORY;
+ case AT_LSL_TEXT: return DAD_SCRIPT;
+ case AT_BODYPART: return DAD_BODYPART;
+ case AT_ANIMATION: return DAD_ANIMATION;
+ case AT_GESTURE: return DAD_GESTURE;
+ default: return DAD_NONE;
+ };
+}
+
+// static. Generate a good default description
+void LLAssetType::generateDescriptionFor(LLAssetType::EType type,
+ LLString& desc)
+{
+ const S32 BUF_SIZE = 30;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+ time_t now;
+ time(&now);
+ memset(time_str, '\0', BUF_SIZE);
+ strftime(time_str, BUF_SIZE - 1, "%Y-%m-%d %H:%M:%S ", localtime(&now));
+ desc.assign(time_str);
+ desc.append(LLAssetType::lookupHumanReadable(type));
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
new file mode 100644
index 0000000000..2b5aadf078
--- /dev/null
+++ b/indra/llcommon/llassettype.h
@@ -0,0 +1,149 @@
+/**
+ * @file llassettype.h
+ * @brief Declaration of LLAssetType.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSETTYPE
+#define LL_LLASSETTYPE
+
+#include "stdenums.h" // for EDragAndDropType
+
+class LLAssetType
+{
+public:
+ enum EType
+ {
+ // Used for painting the faces of geometry.
+ // Stored in typical j2c stream format
+ AT_TEXTURE = 0,
+
+ // Used to fill the aural spectrum.
+ AT_SOUND = 1,
+
+ // Links instant message access to the user on the card. eg, a
+ // card for yourself, a card for linden support, a card for
+ // the guy you were talking to in the coliseum.
+ AT_CALLINGCARD = 2,
+
+ // Links to places in the world with location and a screen
+ // shot or image saved. eg, home, linden headquarters, the
+ // coliseum, or destinations where we want to increase
+ // traffic.
+ AT_LANDMARK = 3,
+
+ // Valid scripts that can be attached to an object. eg. open a
+ // door, jump into the air.
+ AT_SCRIPT = 4,
+
+ // A collection of textures and parameters that can be worn
+ // by an avatar.
+ AT_CLOTHING = 5,
+
+ // Any combination of textures, sounds, and scripts that are
+ // associated with a fixed piece of geometry. eg, a hot tub, a
+ // house with working door.
+ AT_OBJECT = 6,
+
+ // Just text
+ AT_NOTECARD = 7,
+
+ // A category holds a collection of inventory items. It's
+ // treated as an item in the inventory, and therefore needs a
+ // type.
+ AT_CATEGORY = 8,
+
+ // A root category is a user's root inventory category. We
+ // decided to expose it visually, so it seems logical to fold
+ // it into the asset types.
+ AT_ROOT_CATEGORY = 9,
+
+ // The LSL is the brand spanking new scripting language. We've
+ // split it into a text and bytecode representation.
+ AT_LSL_TEXT = 10,
+ AT_LSL_BYTECODE = 11,
+
+ // uncompressed TGA texture
+ AT_TEXTURE_TGA = 12,
+
+ // A collection of textures and parameters that can be worn
+ // by an avatar.
+ AT_BODYPART = 13,
+
+ // This asset type is meant to only be used as a marker for a
+ // category preferred type. Using this, we can throw things in
+ // the trash before completely deleting.
+ AT_TRASH = 14,
+
+ // This is a marker for a folder meant for snapshots. No
+ // actual assets will be snapshots, though if there were, you
+ // could interpret them as textures.
+ AT_SNAPSHOT_CATEGORY = 15,
+
+ // This is used to stuff lost&found items into
+ AT_LOST_AND_FOUND = 16,
+
+ // uncompressed sound
+ AT_SOUND_WAV = 17,
+
+ // uncompressed image, non-square, and not appropriate for use
+ // as a texture.
+ AT_IMAGE_TGA = 18,
+
+ // compressed image, non-square, and not appropriate for use
+ // as a texture.
+ AT_IMAGE_JPEG = 19,
+
+ // animation
+ AT_ANIMATION = 20,
+
+ // gesture, sequence of animations, sounds, chat, wait steps
+ AT_GESTURE = 21,
+
+ // simstate file
+ AT_SIMSTATE = 22,
+
+ // +*********************************************+
+ // | TO ADD AN ELEMENT TO THIS ENUM: |
+ // +*********************************************+
+ // | 1. INSERT BEFORE AT_COUNT |
+ // | 2. INCREMENT AT_COUNT BY 1 |
+ // | 3. ADD TO LLAssetType::mAssetTypeNames |
+ // | 4. ADD TO LLAssetType::mAssetTypeHumanNames |
+ // +*********************************************+
+
+ AT_COUNT = 23,
+
+ AT_NONE = -1
+ };
+
+ // machine transation between type and strings
+ static EType lookup(const char* name);
+ static const char* lookup(EType type);
+
+ // translation from a type to a human readable form.
+ static const char* lookupHumanReadable(EType type);
+
+ static EDragAndDropType lookupDragAndDropType( EType );
+
+ // Generate a good default description. You may want to add a verb
+ // or agent name after this depending on your application.
+ static void generateDescriptionFor(LLAssetType::EType type,
+ LLString& desc);
+
+ static EType getType(const LLString& sin);
+ static LLString getDesc(EType type);
+
+private:
+ // don't instantiate or derive one of these objects
+ LLAssetType( void ) {}
+ ~LLAssetType( void ) {}
+
+private:
+ static const char* mAssetTypeNames[];
+ static const char* mAssetTypeHumanNames[];
+};
+
+#endif // LL_LLASSETTYPE
diff --git a/indra/llcommon/llassoclist.h b/indra/llcommon/llassoclist.h
new file mode 100644
index 0000000000..d90a26dc8a
--- /dev/null
+++ b/indra/llcommon/llassoclist.h
@@ -0,0 +1,278 @@
+/**
+ * @file llassoclist.h
+ * @brief LLAssocList class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSOCLIST_H
+#define LL_LLASSOCLIST_H
+
+//------------------------------------------------------------------------
+// LLAssocList is an associative list container class.
+//
+// The implementation is a single linked list.
+// Both index and value objects are stored by value (not reference).
+// If pointer values are specified for index and/or value, this
+// container does NOT assume ownership of the referenced objects,
+// and does NOT delete() them on removal or destruction of the container.
+//
+// Note that operations are generally not optimized, and may of them
+// are O(n) complexity.
+//------------------------------------------------------------------------
+
+#include <iostream>
+
+template<class INDEX_TYPE, class VALUE_TYPE>
+class LLAssocList
+{
+private:
+ // internal list node type
+ class Node
+ {
+ public:
+ Node(const INDEX_TYPE &index, const VALUE_TYPE &value, Node *next)
+ {
+ mIndex = index;
+ mValue = value;
+ mNext = next;
+ }
+ ~Node() { }
+ INDEX_TYPE mIndex;
+ VALUE_TYPE mValue;
+ Node *mNext;
+ };
+
+ // head of the linked list
+ Node *mHead;
+
+public:
+ // Constructor
+ LLAssocList()
+ {
+ mHead = NULL;
+ }
+
+ // Destructor
+ ~LLAssocList()
+ {
+ removeAll();
+ }
+
+ // Returns TRUE if list is empty.
+ BOOL isEmpty()
+ {
+ return (mHead == NULL);
+ }
+
+ // Returns the number of items in the list.
+ U32 length()
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ count++;
+ }
+ return count;
+ }
+
+ // Removes item with the specified index.
+ BOOL remove( const INDEX_TYPE &index )
+ {
+ if (!mHead)
+ return FALSE;
+
+ if (mHead->mIndex == index)
+ {
+ Node *node = mHead;
+ mHead = mHead->mNext;
+ delete node;
+ return TRUE;
+ }
+
+ for ( Node *prev = mHead;
+ prev->mNext;
+ prev = prev->mNext )
+ {
+ if (prev->mNext->mIndex == index)
+ {
+ Node *node = prev->mNext;
+ prev->mNext = prev->mNext->mNext;
+ delete node;
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // Removes all items from the list.
+ void removeAll()
+ {
+ while ( mHead )
+ {
+ Node *node = mHead;
+ mHead = mHead->mNext;
+ delete node;
+ }
+ }
+
+ // Adds a new item to the head of the list,
+ // removing any existing item with same index.
+ void addToHead( const INDEX_TYPE &index, const VALUE_TYPE &value )
+ {
+ remove(index);
+ Node *node = new Node(index, value, mHead);
+ mHead = node;
+ }
+
+ // Adds a new item to the end of the list,
+ // removing any existing item with the same index.
+ void addToTail( const INDEX_TYPE &index, const VALUE_TYPE &value )
+ {
+ remove(index);
+ Node *node = new Node(index, value, NULL);
+ if (!mHead)
+ {
+ mHead = node;
+ return;
+ }
+ for ( Node *prev=mHead;
+ prev;
+ prev=prev->mNext )
+ {
+ if (!prev->mNext)
+ {
+ prev->mNext=node;
+ return;
+ }
+ }
+ }
+
+ // Sets the value of a specified index.
+ // If index does not exist, a new value will be added only if
+ // 'addIfNotFound' is set to TRUE.
+ // Returns TRUE if successful.
+ BOOL setValue( const INDEX_TYPE &index, const VALUE_TYPE &value, BOOL addIfNotFound=FALSE )
+ {
+ VALUE_TYPE *valueP = getValue(index);
+ if (valueP)
+ {
+ *valueP = value;
+ return TRUE;
+ }
+ if (!addIfNotFound)
+ return FALSE;
+ addToTail(index, value);
+ return TRUE;
+ }
+
+ // Sets the ith value in the list.
+ // A new value will NOT be addded, if the ith value does not exist.
+ // Returns TRUE if successful.
+ BOOL setValueAt( U32 i, const VALUE_TYPE &value )
+ {
+ VALUE_TYPE *valueP = getValueAt(i);
+ if (valueP)
+ {
+ *valueP = value;
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ // Returns a pointer to the value for the specified index,
+ // or NULL if no item found.
+ VALUE_TYPE *getValue( const INDEX_TYPE &index )
+ {
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (node->mIndex == index)
+ return &node->mValue;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the ith value in the list, or
+ // NULL if i is not valid.
+ VALUE_TYPE *getValueAt( U32 i )
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (count == i)
+ return &node->mValue;
+ count++;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the index for the specified index,
+ // or NULL if no item found.
+ INDEX_TYPE *getIndex( const INDEX_TYPE &index )
+ {
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (node->mIndex == index)
+ return &node->mIndex;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the ith index in the list, or
+ // NULL if i is not valid.
+ INDEX_TYPE *getIndexAt( U32 i )
+ {
+ U32 count = 0;
+ for ( Node *node = mHead;
+ node;
+ node = node->mNext )
+ {
+ if (count == i)
+ return &node->mIndex;
+ count++;
+ }
+ return NULL;
+ }
+
+ // Returns a pointer to the value for the specified index,
+ // or NULL if no item found.
+ VALUE_TYPE *operator[](const INDEX_TYPE &index)
+ {
+ return getValue(index);
+ }
+
+ // Returns a pointer to the ith value in the list, or
+ // NULL if i is not valid.
+ VALUE_TYPE *operator[](U32 i)
+ {
+ return getValueAt(i);
+ }
+
+ // Prints the list contents to the specified stream.
+ friend std::ostream &operator<<( std::ostream &os, LLAssocList &map )
+ {
+ os << "{";
+ for ( Node *node = map.mHead;
+ node;
+ node = node->mNext )
+ {
+ os << "<" << node->mIndex << ", " << node->mValue << ">";
+ if (node->mNext)
+ os << ", ";
+ }
+ os << "}";
+
+ return os;
+ }
+};
+
+#endif // LL_LLASSOCLIST_H
diff --git a/indra/llcommon/llavatarconstants.h b/indra/llcommon/llavatarconstants.h
new file mode 100644
index 0000000000..22a088af67
--- /dev/null
+++ b/indra/llcommon/llavatarconstants.h
@@ -0,0 +1,23 @@
+/**
+ * @file indra_constants.h
+ * @brief some useful short term constants for Indra
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_AVATAR_CONSTANTS_H
+#define LL_AVATAR_CONSTANTS_H
+
+// If this string is passed to dataserver in AvatarPropertiesUpdate
+// then no change is made to user.profile_web
+const char* const BLACKLIST_PROFILE_WEB_STR = "featureWebProfilesDisabled";
+
+// If profile web pages are feature blacklisted then this URL is
+// shown in the profile instead of the user's set URL
+const char* const BLACKLIST_PROFILE_WEB_URL = "http://secondlife.com/app/webdisabled";
+
+// Maximum number of avatar picks
+const S32 MAX_AVATAR_PICKS = 10;
+
+#endif
diff --git a/indra/llcommon/llboost.h b/indra/llcommon/llboost.h
new file mode 100644
index 0000000000..7b39ed082e
--- /dev/null
+++ b/indra/llcommon/llboost.h
@@ -0,0 +1,25 @@
+/**
+ * @file llboost.h
+ * @brief helper object & functions for use with boost
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBOOST_H
+#define LL_LLBOOST_H
+
+#include <boost/tokenizer.hpp>
+
+// boost_tokenizer typedef
+/* example usage:
+ boost_tokenizer tokens(input_string, boost::char_separator<char>(" \t\n"));
+ for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ std::string tok = *token_iter;
+ process_token_string( tok );
+ }
+*/
+typedef boost::tokenizer<boost::char_separator<char> > boost_tokenizer;
+
+#endif // LL_LLBOOST_H
diff --git a/indra/llcommon/llchat.h b/indra/llcommon/llchat.h
new file mode 100644
index 0000000000..88867b2081
--- /dev/null
+++ b/indra/llcommon/llchat.h
@@ -0,0 +1,69 @@
+/**
+ * @file llchat.h
+ * @author James Cook
+ * @brief Chat constants and data structures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHAT_H
+#define LL_LLCHAT_H
+
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3math.h"
+
+// enumerations used by the chat system
+typedef enum e_chat_source_type
+{
+ CHAT_SOURCE_SYSTEM = 0,
+ CHAT_SOURCE_AGENT = 1,
+ CHAT_SOURCE_OBJECT = 2
+} EChatSourceType;
+
+typedef enum e_chat_type
+{
+ CHAT_TYPE_WHISPER = 0,
+ CHAT_TYPE_NORMAL = 1,
+ CHAT_TYPE_SHOUT = 2,
+ CHAT_TYPE_START = 4,
+ CHAT_TYPE_STOP = 5,
+ CHAT_TYPE_DEBUG_MSG = 6
+} EChatType;
+
+typedef enum e_chat_audible_level
+{
+ CHAT_AUDIBLE_NOT = -1,
+ CHAT_AUDIBLE_BARELY = 0,
+ CHAT_AUDIBLE_FULLY = 1
+} EChatAudible;
+
+// A piece of chat
+class LLChat
+{
+public:
+ LLChat(const LLString& text = LLString::null)
+ : mText(text),
+ mFromName(),
+ mFromID(),
+ mSourceType(CHAT_SOURCE_AGENT),
+ mChatType(CHAT_TYPE_NORMAL),
+ mAudible(CHAT_AUDIBLE_FULLY),
+ mMuted(FALSE),
+ mTime(0.0),
+ mPosAgent()
+ { }
+
+ LLString mText; // UTF-8 line of text
+ LLString mFromName; // agent or object name
+ LLUUID mFromID; // agent id or object id
+ EChatSourceType mSourceType;
+ EChatType mChatType;
+ EChatAudible mAudible;
+ BOOL mMuted; // pass muted chat to maintain list of chatters
+ F64 mTime; // viewer only, seconds from viewer start
+ LLVector3 mPosAgent;
+};
+
+#endif
diff --git a/indra/llcommon/llclickaction.h b/indra/llcommon/llclickaction.h
new file mode 100644
index 0000000000..538fae3658
--- /dev/null
+++ b/indra/llcommon/llclickaction.h
@@ -0,0 +1,20 @@
+/**
+ * @file llclickaction.h
+ * @author James Cook
+ * @brief Constants for single-click actions on objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLICKACTION_H
+#define LL_LLCLICKACTION_H
+
+const U8 CLICK_ACTION_NONE = 0;
+const U8 CLICK_ACTION_TOUCH = 0;
+const U8 CLICK_ACTION_SIT = 1;
+const U8 CLICK_ACTION_BUY = 2;
+const U8 CLICK_ACTION_PAY = 3;
+const U8 CLICK_ACTION_OPEN = 4;
+
+#endif
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
new file mode 100644
index 0000000000..ff810abfa9
--- /dev/null
+++ b/indra/llcommon/llcommon.cpp
@@ -0,0 +1,43 @@
+/**
+ * @file llcommon.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcommon.h"
+
+//static
+BOOL LLCommon::sAprInitialized = FALSE;
+
+//static
+void LLCommon::initClass()
+{
+ LLMemory::initClass();
+ if (!sAprInitialized)
+ {
+ ll_init_apr();
+ sAprInitialized = TRUE;
+ }
+ LLTimer::initClass();
+ LLThreadSafeRefCount::initClass();
+// LLWorkerThread::initClass();
+// LLFrameCallbackManager::initClass();
+}
+
+//static
+void LLCommon::cleanupClass()
+{
+// LLFrameCallbackManager::cleanupClass();
+// LLWorkerThread::cleanupClass();
+ LLThreadSafeRefCount::cleanupClass();
+ LLTimer::cleanupClass();
+ if (sAprInitialized)
+ {
+ ll_cleanup_apr();
+ sAprInitialized = FALSE;
+ }
+ LLMemory::cleanupClass();
+}
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
new file mode 100644
index 0000000000..c37d479ba9
--- /dev/null
+++ b/indra/llcommon/llcommon.h
@@ -0,0 +1,28 @@
+/**
+ * @file llcommon.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_COMMON_H
+#define LL_COMMON_H
+
+#include "llmemory.h"
+#include "llapr.h"
+// #include "llframecallbackmanager.h"
+#include "lltimer.h"
+#include "llworkerthread.h"
+#include "llfile.h"
+
+class LLCommon
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+private:
+ static BOOL sAprInitialized;
+};
+
+#endif
+
diff --git a/indra/llcommon/llcriticaldamp.cpp b/indra/llcommon/llcriticaldamp.cpp
new file mode 100644
index 0000000000..ee7dfecdaa
--- /dev/null
+++ b/indra/llcommon/llcriticaldamp.cpp
@@ -0,0 +1,72 @@
+/**
+ * @file llcriticaldamp.cpp
+ * @brief Implementation of the critical damping functionality.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include <math.h>
+
+#include "llcriticaldamp.h"
+
+//-----------------------------------------------------------------------------
+// static members
+//-----------------------------------------------------------------------------
+LLFrameTimer LLCriticalDamp::sInternalTimer;
+std::map<F32, F32> LLCriticalDamp::sInterpolants;
+F32 LLCriticalDamp::sTimeDelta;
+
+//-----------------------------------------------------------------------------
+// LLCriticalDamp()
+//-----------------------------------------------------------------------------
+LLCriticalDamp::LLCriticalDamp()
+{
+ sTimeDelta = 0.f;
+}
+
+// static
+//-----------------------------------------------------------------------------
+// updateInterpolants()
+//-----------------------------------------------------------------------------
+void LLCriticalDamp::updateInterpolants()
+{
+ sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32();
+
+ F32 time_constant;
+
+ for (std::map<F32, F32>::iterator iter = sInterpolants.begin();
+ iter != sInterpolants.end(); iter++)
+ {
+ time_constant = iter->first;
+ F32 new_interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
+ new_interpolant = llclamp(new_interpolant, 0.f, 1.f);
+ sInterpolants[time_constant] = new_interpolant;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getInterpolant()
+//-----------------------------------------------------------------------------
+F32 LLCriticalDamp::getInterpolant(const F32 time_constant, BOOL use_cache)
+{
+ if (time_constant == 0.f)
+ {
+ return 1.f;
+ }
+
+ if (use_cache && sInterpolants.count(time_constant))
+ {
+ return sInterpolants[time_constant];
+ }
+
+ F32 interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
+ interpolant = llclamp(interpolant, 0.f, 1.f);
+ if (use_cache)
+ {
+ sInterpolants[time_constant] = interpolant;
+ }
+
+ return interpolant;
+}
diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h
new file mode 100644
index 0000000000..3f18db9ec3
--- /dev/null
+++ b/indra/llcommon/llcriticaldamp.h
@@ -0,0 +1,35 @@
+/**
+ * @file llcriticaldamp.h
+ * @brief A lightweight class that calculates critical damping constants once
+ * per frame.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCRITICALDAMP_H
+#define LL_LLCRITICALDAMP_H
+
+#include <map>
+
+#include "llframetimer.h"
+
+class LLCriticalDamp
+{
+public:
+ LLCriticalDamp();
+
+ // MANIPULATORS
+ static void updateInterpolants();
+
+ // ACCESSORS
+ static F32 getInterpolant(const F32 time_constant, BOOL use_cache = TRUE);
+
+protected:
+ static LLFrameTimer sInternalTimer; // frame timer for calculating deltas
+
+ static std::map<F32, F32> sInterpolants;
+ static F32 sTimeDelta;
+};
+
+#endif // LL_LLCRITICALDAMP_H
diff --git a/indra/llcommon/lldarray.h b/indra/llcommon/lldarray.h
new file mode 100644
index 0000000000..c8b5b7fb14
--- /dev/null
+++ b/indra/llcommon/lldarray.h
@@ -0,0 +1,192 @@
+/**
+ * @file lldarray.h
+ * @brief Wrapped std::vector for backward compatibility.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDARRAY_H
+#define LL_LLDARRAY_H
+
+#include "llmath.h"
+#include "llerror.h"
+
+#include <vector>
+#include <map>
+
+// class LLDynamicArray<>; // = std::vector + reserves <BlockSize> elements
+// class LLDynamicArrayIndexed<>; // = std::vector + std::map if indices, only supports operator[] and begin(),end()
+
+//--------------------------------------------------------
+// LLDynamicArray declaration
+//--------------------------------------------------------
+// NOTE: BlockSize is used to reserve a minimal initial amount
+template <typename Type, int BlockSize = 32>
+class LLDynamicArray : public std::vector<Type>
+{
+public:
+ enum
+ {
+ OKAY = 0,
+ FAIL = -1
+ };
+
+ LLDynamicArray(S32 size=0) : std::vector<Type>(size) { if (size < BlockSize) std::vector<Type>::reserve(BlockSize); }
+
+ void reset() { std::vector<Type>::resize(0); }
+
+ // ACCESSORS
+ const Type& get(S32 index) const { return std::vector<Type>::operator[](index); }
+ Type& get(S32 index) { return std::vector<Type>::operator[](index); }
+ S32 find(const Type &obj) const;
+
+ S32 count() const { return std::vector<Type>::size(); }
+ S32 getLength() const { return std::vector<Type>::size(); }
+ S32 getMax() const { return std::vector<Type>::capacity(); }
+
+ // MANIPULATE
+ S32 put(const Type &obj); // add to end of array, returns index
+// Type* reserve(S32 num); // reserve a block of indices in advance
+ Type* reserve_block(U32 num); // reserve a block of indices in advance
+
+ S32 remove(S32 index); // remove by index, no bounds checking
+ S32 removeObj(const Type &obj); // remove by object
+ S32 removeLast();
+
+ void operator+=(const LLDynamicArray<Type,BlockSize> &other);
+};
+
+//--------------------------------------------------------
+// LLDynamicArray implementation
+//--------------------------------------------------------
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::find(const Type &obj) const
+{
+ typename std::vector<Type>::const_iterator iter = std::find(this->begin(), this->end(), obj);
+ if (iter != this->end())
+ {
+ return iter - this->begin();
+ }
+ return FAIL;
+}
+
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::remove(S32 i)
+{
+ // This is a fast removal by swapping with the last element
+ S32 sz = this->size();
+ if (i < 0 || i >= sz)
+ {
+ return FAIL;
+ }
+ if (i < sz-1)
+ {
+ this->operator[](i) = this->back();
+ }
+ this->pop_back();
+ return i;
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::removeObj(const Type& obj)
+{
+ typename std::vector<Type>::iterator iter = std::find(this->begin(), this->end(), obj);
+ if (iter != this->end())
+ {
+ typename std::vector<Type>::iterator last = this->end();
+ --last;
+ *iter = *last;
+ this->pop_back();
+ return iter - this->begin();
+ }
+ return FAIL;
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::removeLast()
+{
+ if (!this->empty())
+ {
+ this->pop_back();
+ return OKAY;
+ }
+ return FAIL;
+}
+
+template <typename Type,int BlockSize>
+inline Type* LLDynamicArray<Type,BlockSize>::reserve_block(U32 num)
+{
+ U32 sz = this->size();
+ this->resize(sz+num);
+ return &(this->operator[](sz));
+}
+
+template <typename Type,int BlockSize>
+inline S32 LLDynamicArray<Type,BlockSize>::put(const Type &obj)
+{
+ this->push_back(obj);
+ return this->size() - 1;
+}
+
+template <typename Type,int BlockSize>
+inline void LLDynamicArray<Type,BlockSize>::operator+=(const LLDynamicArray<Type,BlockSize> &other)
+{
+ insert(this->end(), other.begin(), other.end());
+}
+
+//--------------------------------------------------------
+// LLDynamicArrayIndexed declaration
+//--------------------------------------------------------
+
+template <typename Type, typename Key, int BlockSize = 32>
+class LLDynamicArrayIndexed
+{
+public:
+ typedef typename std::vector<Type>::iterator iterator;
+ typedef typename std::vector<Type>::const_iterator const_iterator;
+ typedef typename std::vector<Type>::reverse_iterator reverse_iterator;
+ typedef typename std::vector<Type>::const_reverse_iterator const_reverse_iterator;
+ typedef typename std::vector<Type>::size_type size_type;
+protected:
+ std::vector<Type> mVector;
+ std::map<Key, U32> mIndexMap;
+
+public:
+ LLDynamicArrayIndexed() { mVector.reserve(BlockSize); }
+
+ iterator begin() { return mVector.begin(); }
+ const_iterator begin() const { return mVector.begin(); }
+ iterator end() { return mVector.end(); }
+ const_iterator end() const { return mVector.end(); }
+
+ reverse_iterator rbegin() { return mVector.rbegin(); }
+ const_reverse_iterator rbegin() const { return mVector.rbegin(); }
+ reverse_iterator rend() { return mVector.rend(); }
+ const_reverse_iterator rend() const { return mVector.rend(); }
+
+ void reset() { mVector.resize(0); mIndexMap.resize(0); }
+ bool empty() const { return mVector.empty(); }
+ size_type size() const { return mVector.empty(); }
+
+ Type& operator[](const Key& k)
+ {
+ typename std::map<Key, U32>::iterator iter = mIndexMap.find(k);
+ if (iter == mIndexMap.end())
+ {
+ U32 n = mVector.size();
+ mIndexMap[k] = n;
+ mVector.resize(n+1);
+ return mVector[n];
+ }
+ else
+ {
+ return mVector[iter->second];
+ }
+ }
+
+};
+
+#endif
diff --git a/indra/llcommon/lldarrayptr.h b/indra/llcommon/lldarrayptr.h
new file mode 100644
index 0000000000..8fe1c5a5df
--- /dev/null
+++ b/indra/llcommon/lldarrayptr.h
@@ -0,0 +1,18 @@
+/**
+ * @file lldarrayptr.h
+ * @brief Wrapped std::vector for backward compatibility.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDARRAYPTR_H
+#define LL_LLDARRAYPTR_H
+
+#include "lldarray.h"
+
+template <class Type, int BlockSize = 32>
+class LLDynamicArrayPtr : public LLDynamicArray<Type, BlockSize>
+{
+};
+
+#endif // LL_LLDARRAYPTR_H
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp
new file mode 100644
index 0000000000..6ae2a1145f
--- /dev/null
+++ b/indra/llcommon/lldate.cpp
@@ -0,0 +1,174 @@
+/**
+ * @file lldate.cpp
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Implementation of the date class
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lldate.h"
+
+#include "apr-1/apr_time.h"
+
+#include <iomanip>
+#include <sstream>
+
+static const F64 DATE_EPOCH = 0.0;
+
+static const F64 LL_APR_USEC_PER_SEC = 1000000.0;
+ // should be APR_USEC_PER_SEC, but that relies on INT64_C which
+ // isn't defined in glib under our build set up for some reason
+
+
+LLDate::LLDate() : mSecondsSinceEpoch(DATE_EPOCH)
+{
+}
+
+LLDate::LLDate(const LLDate& date) :
+ mSecondsSinceEpoch(date.mSecondsSinceEpoch)
+{
+}
+
+LLDate::LLDate(F64 seconds_since_epoch) :
+ mSecondsSinceEpoch(seconds_since_epoch)
+{
+}
+
+LLDate::LLDate(const std::string& iso8601_date)
+{
+ if(!fromString(iso8601_date))
+ {
+ mSecondsSinceEpoch = DATE_EPOCH;
+ }
+}
+
+std::string LLDate::asString() const
+{
+ std::ostringstream stream;
+ toStream(stream);
+ return stream.str();
+}
+
+void LLDate::toStream(std::ostream& s) const
+{
+ apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC);
+
+ apr_time_exp_t exp_time;
+ if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS)
+ {
+ s << "1970-01-01T00:00:00Z";
+ return;
+ }
+
+ s << std::dec << std::setfill('0');
+#if( LL_WINDOWS || __GNUC__ > 2)
+ s << std::right;
+#else
+ s.setf(ios::right);
+#endif
+ s << std::setw(4) << (exp_time.tm_year + 1900)
+ << '-' << std::setw(2) << (exp_time.tm_mon + 1)
+ << '-' << std::setw(2) << (exp_time.tm_mday)
+ << 'T' << std::setw(2) << (exp_time.tm_hour)
+ << ':' << std::setw(2) << (exp_time.tm_min)
+ << ':' << std::setw(2) << (exp_time.tm_sec);
+ if (exp_time.tm_usec > 0)
+ {
+ s << '.' << std::setw(2)
+ << (int)(exp_time.tm_usec / (LL_APR_USEC_PER_SEC / 100));
+ }
+ s << 'Z';
+}
+
+bool LLDate::fromString(const std::string& iso8601_date)
+{
+ std::istringstream stream(iso8601_date);
+ return fromStream(stream);
+}
+
+bool LLDate::fromStream(std::istream& s)
+{
+ struct apr_time_exp_t exp_time;
+ apr_int32_t tm_part;
+ int c;
+
+ s >> tm_part;
+ exp_time.tm_year = tm_part - 1900;
+ c = s.get(); // skip the hypen
+ if (c != '-') { return false; }
+ s >> tm_part;
+ exp_time.tm_mon = tm_part - 1;
+ c = s.get(); // skip the hypen
+ if (c != '-') { return false; }
+ s >> tm_part;
+ exp_time.tm_mday = tm_part;
+
+ c = s.get(); // skip the T
+ if (c != 'T') { return false; }
+
+ s >> tm_part;
+ exp_time.tm_hour = tm_part;
+ c = s.get(); // skip the :
+ if (c != ':') { return false; }
+ s >> tm_part;
+ exp_time.tm_min = tm_part;
+ c = s.get(); // skip the :
+ if (c != ':') { return false; }
+ s >> tm_part;
+ exp_time.tm_sec = tm_part;
+
+ // zero out the unused fields
+ exp_time.tm_usec = 0;
+ exp_time.tm_wday = 0;
+ exp_time.tm_yday = 0;
+ exp_time.tm_isdst = 0;
+ exp_time.tm_gmtoff = 0;
+
+ // generate a time_t from that
+ apr_time_t time;
+ if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS)
+ {
+ return false;
+ }
+
+ F64 seconds_since_epoch = time / LL_APR_USEC_PER_SEC;
+
+ // check for fractional
+ c = s.peek();
+ if(c == '.')
+ {
+ F64 fractional = 0.0;
+ s >> fractional;
+ seconds_since_epoch += fractional;
+ }
+ s.get(); // skip the Z
+ if (c != 'Z') { return false; }
+
+ mSecondsSinceEpoch = seconds_since_epoch;
+ return true;
+}
+
+F64 LLDate::secondsSinceEpoch() const
+{
+ return mSecondsSinceEpoch;
+}
+
+void LLDate::secondsSinceEpoch(F64 seconds)
+{
+ mSecondsSinceEpoch = seconds;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLDate& date)
+{
+ date.toStream(s);
+ return s;
+}
+
+std::istream& operator>>(std::istream& s, LLDate& date)
+{
+ date.fromStream(s);
+ return s;
+}
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
new file mode 100644
index 0000000000..854613ad0b
--- /dev/null
+++ b/indra/llcommon/lldate.h
@@ -0,0 +1,102 @@
+/**
+ * @file lldate.h
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Declaration of a simple date class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDATE_H
+#define LL_LLDATE_H
+
+#include <iosfwd>
+
+#include "stdtypes.h"
+
+/**
+ * @class LLDate
+ * @brief This class represents a particular point in time in UTC.
+ *
+ * The date class represents a point in time after epoch - 1970-01-01.
+ */
+class LLDate
+{
+public:
+ /**
+ * @brief Construct a date equal to epoch.
+ */
+ LLDate();
+
+ /**
+ * @brief Construct a date equal to epoch.
+ */
+ LLDate(const LLDate& date);
+
+ /**
+ * @brief Construct a date from a seconds since epoch value.
+ *
+ * @pararm seconds_since_epoch The number of seconds since UTC epoch.
+ */
+ LLDate(F64 seconds_since_epoch);
+
+ /**
+ * @brief Construct a date from a string representation
+ *
+ * The date is constructed in the <code>fromString()</code>
+ * method. See that method for details of supported formats.
+ * If that method fails to parse the date, the date is set to epoch.
+ * @param iso8601_date An iso-8601 compatible representation of the date.
+ */
+ LLDate(const std::string& iso8601_date);
+
+ /**
+ * @brief Return the date as in ISO-8601 string.
+ *
+ * @return A string representation of the date.
+ */
+ std::string asString() const;
+ void toStream(std::ostream&) const;
+ /**
+ * @brief Set the date from an ISO-8601 string.
+ *
+ * The parser only supports strings conforming to
+ * YYYYF-MM-DDTHH:MM:SS.FFZ where Y is year, M is month, D is day,
+ * H is hour, M is minute, S is second, F is sub-second, and all
+ * other characters are literal.
+ * If this method fails to parse the date, the previous date is
+ * retained.
+ * @param iso8601_date An iso-8601 compatible representation of the date.
+ * @return Returns true if the string was successfully parsed.
+ */
+ bool fromString(const std::string& iso8601_date);
+ bool fromStream(std::istream&);
+
+ /**
+ * @brief Return the date in seconds since epoch.
+ *
+ * @return The number of seconds since epoch UTC.
+ */
+ F64 secondsSinceEpoch() const;
+
+ /**
+ * @brief Set the date in seconds since epoch.
+ *
+ * @param seconds The number of seconds since epoch UTC.
+ */
+ void secondsSinceEpoch(F64 seconds);
+
+private:
+ F64 mSecondsSinceEpoch;
+};
+
+
+// Helper function to stream out a date
+std::ostream& operator<<(std::ostream& s, const LLDate& date);
+
+// Helper function to stream in a date
+std::istream& operator>>(std::istream& s, LLDate& date);
+
+
+#endif // LL_LLDATE_H
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
new file mode 100644
index 0000000000..63322effbe
--- /dev/null
+++ b/indra/llcommon/lldefs.h
@@ -0,0 +1,185 @@
+/**
+ * @file lldefs.h
+ * @brief Various generic constant definitions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEFS_H
+#define LL_LLDEFS_H
+
+#include "stdtypes.h"
+
+// Often used array indices
+const U32 VX = 0;
+const U32 VY = 1;
+const U32 VZ = 2;
+const U32 VW = 3;
+const U32 VS = 3;
+
+const U32 VRED = 0;
+const U32 VGREEN = 1;
+const U32 VBLUE = 2;
+const U32 VALPHA = 3;
+
+const U32 INVALID_DIRECTION = 0xFFFFFFFF;
+const U32 EAST = 0;
+const U32 NORTH = 1;
+const U32 WEST = 2;
+const U32 SOUTH = 3;
+
+const U32 NORTHEAST = 4;
+const U32 NORTHWEST = 5;
+const U32 SOUTHWEST = 6;
+const U32 SOUTHEAST = 7;
+const U32 MIDDLE = 8;
+
+const U8 LL_SOUND_FLAG_NONE = 0x00;
+const U8 LL_SOUND_FLAG_LOOP = 0x01;
+const U8 LL_SOUND_FLAG_SYNC_MASTER = 0x02;
+const U8 LL_SOUND_FLAG_SYNC_SLAVE = 0x04;
+const U8 LL_SOUND_FLAG_SYNC_PENDING = 0x08;
+const U8 LL_SOUND_FLAG_SYNC_MASK = LL_SOUND_FLAG_SYNC_MASTER | LL_SOUND_FLAG_SYNC_SLAVE | LL_SOUND_FLAG_SYNC_PENDING;
+const U8 LL_SOUND_FLAG_QUEUE = 0x10;
+
+const U32 gDirOpposite[8] = {2, 3, 0, 1, 6, 7, 4, 5};
+const U32 gDirAdjacent[8][2] = {
+ {4, 7},
+ {4, 5},
+ {5, 6},
+ {6, 7},
+ {0, 1},
+ {1, 2},
+ {2, 3},
+ {0, 3}
+ };
+
+// Magnitude along the x and y axis
+const S32 gDirAxes[8][2] = {
+ { 1, 0}, // east
+ { 0, 1}, // north
+ {-1, 0}, // west
+ { 0,-1}, // south
+ { 1, 1}, // ne
+ {-1, 1}, // nw
+ {-1,-1}, // sw
+ { 1,-1}, // se
+ };
+
+const U8 EAST_MASK = 1;
+const U8 NORTH_MASK = 2;
+const U8 WEST_MASK = 4;
+const U8 SOUTH_MASK = 8;
+const U8 NORTHEAST_MASK = NORTH_MASK | EAST_MASK;
+const U8 NORTHWEST_MASK = NORTH_MASK | WEST_MASK;
+const U8 SOUTHWEST_MASK = SOUTH_MASK | WEST_MASK;
+const U8 SOUTHEAST_MASK = SOUTH_MASK | EAST_MASK;
+
+const S32 gDirMasks[8] = {
+ EAST_MASK,
+ NORTH_MASK,
+ WEST_MASK,
+ SOUTH_MASK,
+ NORTHEAST_MASK,
+ NORTHWEST_MASK,
+ SOUTHWEST_MASK,
+ SOUTHEAST_MASK
+ };
+
+// Sides of a box...
+// . Z __.Y
+// /|\ /| 0 = NO_SIDE
+// | / 1 = FRONT_SIDE = +x
+// +------|-----------+ 2 = BACK_SIDE = -x
+// /| |/ / /| 3 = LEFT_SIDE = +y
+// / | -5- |/ / | 4 = RIGHT_SIDE = -y
+// / | /| -3- / | 5 = TOP_SIDE = +z
+// +------------------+ | 6 = BOTTOM_SIDE = -z
+// | | | / | |
+// | |/| | / | |/|
+// | 2 | | *-------|-1--------> X
+// |/| | -4- |/| |
+// | +----|---------|---+
+// | / / | /
+// | / -6- | /
+// |/ / |/
+// +------------------+
+const U32 NO_SIDE = 0;
+const U32 FRONT_SIDE = 1;
+const U32 BACK_SIDE = 2;
+const U32 LEFT_SIDE = 3;
+const U32 RIGHT_SIDE = 4;
+const U32 TOP_SIDE = 5;
+const U32 BOTTOM_SIDE = 6;
+
+const U32 LL_MAX_PATH = 1024; // buffer size of maximum path + filename string length
+
+// For strings we send in messages
+const U32 STD_STRING_BUF_SIZE = 255; // Buffer size
+const U32 STD_STRING_STR_LEN = 254; // Length of the string (not including \0)
+const U32 MAX_STRING = STD_STRING_BUF_SIZE; // Buffer size
+
+const U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + 1 for good luck
+
+// C++ is our friend. . . use template functions to make life easier!
+
+// specific inlines for basic types
+//
+// defined for all:
+// llmin(a,b)
+// llmax(a,b)
+// llclamp(a,minimum,maximum)
+//
+// defined for F32, F64:
+// llclampf(a) // clamps a to [0.0 .. 1.0]
+//
+// defined for U16, U32, U64, S16, S32, S64, :
+// llclampb(a) // clamps a to [0 .. 255]
+//
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2)
+{
+ return (d1 > d2) ? d1 : d2;
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmax(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3)
+{
+ LLDATATYPE r = llmax(d1,d2);
+ return (r > d3 ? r : d3);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2)
+{
+ return (d1 < d2) ? d1 : d2;
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llmin(const LLDATATYPE& d1, const LLDATATYPE& d2, const LLDATATYPE& d3)
+{
+ LLDATATYPE r = llmin(d1,d2);
+ return (r < d3 ? r : d3);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclamp(const LLDATATYPE& a, const LLDATATYPE& minval, const LLDATATYPE& maxval)
+{
+ return llmin(llmax(a, minval), maxval);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclampf(const LLDATATYPE& a)
+{
+ return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)1);
+}
+
+template <class LLDATATYPE>
+inline LLDATATYPE llclampb(const LLDATATYPE& a)
+{
+ return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)255);
+}
+
+#endif // LL_LLDEFS_H
diff --git a/indra/llcommon/lldepthstack.h b/indra/llcommon/lldepthstack.h
new file mode 100644
index 0000000000..6b259ec9b0
--- /dev/null
+++ b/indra/llcommon/lldepthstack.h
@@ -0,0 +1,81 @@
+/**
+ * @file lldepthstack.h
+ * @brief Declaration of the LLDepthStack class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEPTHSTACK_H
+#define LL_LLDEPTHSTACK_H
+
+#include "linked_lists.h"
+
+template <class DATA_TYPE> class LLDepthStack
+{
+private:
+ LLLinkedList<DATA_TYPE> mStack;
+ U32 mCurrentDepth;
+ U32 mMaxDepth;
+
+public:
+ LLDepthStack() : mCurrentDepth(0), mMaxDepth(0) {}
+ ~LLDepthStack() {}
+
+ void setDepth(U32 depth)
+ {
+ mMaxDepth = depth;
+ }
+
+ U32 getDepth(void) const
+ {
+ return mCurrentDepth;
+ }
+
+ void push(DATA_TYPE *data)
+ {
+ if (mCurrentDepth < mMaxDepth)
+ {
+ mStack.addData(data);
+ mCurrentDepth++;
+ }
+ else
+ {
+ // the last item falls off stack and is deleted
+ mStack.getLastData();
+ mStack.deleteCurrentData();
+ mStack.addData(data);
+ }
+ }
+
+ DATA_TYPE *pop()
+ {
+ DATA_TYPE *tempp = mStack.getFirstData();
+ if (tempp)
+ {
+ mStack.removeCurrentData();
+ mCurrentDepth--;
+ }
+ return tempp;
+ }
+
+ DATA_TYPE *check()
+ {
+ DATA_TYPE *tempp = mStack.getFirstData();
+ return tempp;
+ }
+
+ void deleteAllData()
+ {
+ mCurrentDepth = 0;
+ mStack.deleteAllData();
+ }
+
+ void removeAllNodes()
+ {
+ mCurrentDepth = 0;
+ mStack.removeAllNodes();
+ }
+};
+
+#endif
diff --git a/indra/llcommon/lldlinked.h b/indra/llcommon/lldlinked.h
new file mode 100644
index 0000000000..54087848d9
--- /dev/null
+++ b/indra/llcommon/lldlinked.h
@@ -0,0 +1,77 @@
+/**
+ * @file lldlinked.h
+ * @brief Declaration of the LLDLinked class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDLINKED_H
+#define LL_LLDLINKED_H
+
+#include <stdlib.h>
+
+template <class Type> class LLDLinked
+{
+ LLDLinked* mNextp;
+ LLDLinked* mPrevp;
+public:
+
+ Type* getNext() { return (Type*)mNextp; }
+ Type* getPrev() { return (Type*)mPrevp; }
+ Type* getFirst() { return (Type*)mNextp; }
+
+ void init()
+ {
+ mNextp = mPrevp = NULL;
+ }
+
+ void unlink()
+ {
+ if (mPrevp) mPrevp->mNextp = mNextp;
+ if (mNextp) mNextp->mPrevp = mPrevp;
+ }
+
+ LLDLinked() { mNextp = mPrevp = NULL; }
+ virtual ~LLDLinked() { unlink(); }
+
+ virtual void deleteAll()
+ {
+ Type *curp = getFirst();
+ while(curp)
+ {
+ Type *nextp = curp->getNext();
+ curp->unlink();
+ delete curp;
+ curp = nextp;
+ }
+ }
+
+ void relink(Type &after)
+ {
+ LLDLinked *afterp = (LLDLinked*)&after;
+ afterp->mPrevp = this;
+ mNextp = afterp;
+ }
+
+ virtual void append(Type& after)
+ {
+ LLDLinked *afterp = (LLDLinked*)&after;
+ afterp->mPrevp = this;
+ afterp->mNextp = mNextp;
+ if (mNextp) mNextp->mPrevp = afterp;
+ mNextp = afterp;
+ }
+
+ virtual void insert(Type& before)
+ {
+ LLDLinked *beforep = (LLDLinked*)&before;
+ beforep->mNextp = this;
+ beforep->mPrevp = mPrevp;
+ if (mPrevp) mPrevp->mNextp = beforep;
+ mPrevp = beforep;
+ }
+
+ virtual void put(Type& obj) { append(obj); }
+};
+
+#endif
diff --git a/indra/llcommon/lldqueueptr.h b/indra/llcommon/lldqueueptr.h
new file mode 100644
index 0000000000..b5407d826a
--- /dev/null
+++ b/indra/llcommon/lldqueueptr.h
@@ -0,0 +1,334 @@
+/**
+ * @file lldqueueptr.h
+ * @brief LLDynamicQueuePtr declaration
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLDQUEUEPTR_H
+#define LL_LLDQUEUEPTR_H
+
+template <class Type>
+class LLDynamicQueuePtr
+{
+public:
+ enum
+ {
+ OKAY = 0,
+ FAIL = -1
+ };
+
+ LLDynamicQueuePtr(const S32 size=8);
+ ~LLDynamicQueuePtr();
+
+ void init();
+ void destroy();
+ void reset();
+ void realloc(U32 newsize);
+
+ // ACCESSORS
+ const Type& get(const S32 index) const; // no bounds checking
+ Type& get(const S32 index); // no bounds checking
+ const Type& operator [] (const S32 index) const { return get(index); }
+ Type& operator [] (const S32 index) { return get(index); }
+ S32 find(const Type &obj) const;
+
+ S32 count() const { return (mLastObj >= mFirstObj ? mLastObj - mFirstObj : mLastObj + mMaxObj - mFirstObj); }
+ S32 getMax() const { return mMaxObj; }
+ S32 getFirst() const { return mFirstObj; }
+ S32 getLast () const { return mLastObj; }
+
+ // MANIPULATE
+ S32 push(const Type &obj); // add to end of Queue, returns index from start
+ S32 pull( Type &obj); // pull from Queue, returns index from start
+
+ S32 remove (S32 index); // remove by index
+ S32 removeObj(const Type &obj); // remove by object
+
+protected:
+ S32 mFirstObj, mLastObj, mMaxObj;
+ Type* mMemory;
+
+public:
+
+ void print()
+ {
+ /*
+ Convert this to llinfos if it's intended to be used - djs 08/30/02
+
+ printf("Printing from %d to %d (of %d): ",mFirstObj, mLastObj, mMaxObj);
+
+ if (mFirstObj <= mLastObj)
+ {
+ for (S32 i=mFirstObj;i<mLastObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ }
+ else
+ {
+ for (S32 i=mFirstObj;i<mMaxObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ for (i=0;i<mLastObj;i++)
+ {
+ printf("%d ",mMemory[i]);
+ }
+ }
+ printf("\n");
+ */
+ }
+
+};
+
+
+//--------------------------------------------------------
+// LLDynamicQueuePtrPtr implementation
+//--------------------------------------------------------
+
+
+template <class Type>
+inline LLDynamicQueuePtr<Type>::LLDynamicQueuePtr(const S32 size)
+{
+ init();
+ realloc(size);
+}
+
+template <class Type>
+inline LLDynamicQueuePtr<Type>::~LLDynamicQueuePtr()
+{
+ destroy();
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::init()
+{
+ mFirstObj = 0;
+ mLastObj = 0;
+ mMaxObj = 0;
+ mMemory = NULL;
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::realloc(U32 newsize)
+{
+ if (newsize)
+ {
+ if (mFirstObj > mLastObj && newsize > mMaxObj)
+ {
+ Type* new_memory = new Type[newsize];
+
+ llassert(new_memory);
+
+ S32 _count = count();
+ S32 i, m = 0;
+ for (i=mFirstObj; i < mMaxObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+ for (i=0; i <=mLastObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+
+ delete[] mMemory;
+ mMemory = new_memory;
+
+ mFirstObj = 0;
+ mLastObj = _count;
+ }
+ else
+ {
+ Type* new_memory = new Type[newsize];
+
+ llassert(new_memory);
+
+ S32 i, m = 0;
+ for (i=0; i < mLastObj; i++)
+ {
+ new_memory[m++] = mMemory[i];
+ }
+ delete[] mMemory;
+ mMemory = new_memory;
+ }
+ }
+ else if (mMemory)
+ {
+ delete[] mMemory;
+ mMemory = NULL;
+ }
+
+ mMaxObj = newsize;
+}
+
+template <class Type>
+inline void LLDynamicQueuePtr<Type>::destroy()
+{
+ reset();
+ delete[] mMemory;
+ mMemory = NULL;
+}
+
+
+template <class Type>
+void LLDynamicQueuePtr<Type>::reset()
+{
+ for (S32 i=0; i < mMaxObj; i++)
+ {
+ get(i) = NULL; // unrefs for pointers
+ }
+
+ mFirstObj = 0;
+ mLastObj = 0;
+}
+
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::find(const Type &obj) const
+{
+ S32 i;
+ if (mFirstObj <= mLastObj)
+ {
+ for ( i = mFirstObj; i < mLastObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ }
+ else
+ {
+ for ( i = mFirstObj; i < mMaxObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ for ( i = 0; i < mLastObj; i++ )
+ {
+ if (mMemory[i] == obj)
+ {
+ return i;
+ }
+ }
+ }
+
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::remove(S32 i)
+{
+ if (mFirstObj > mLastObj)
+ {
+ if (i >= mFirstObj && i < mMaxObj)
+ {
+ while( i > mFirstObj)
+ {
+ mMemory[i] = mMemory[i-1];
+ i--;
+ }
+ mMemory[mFirstObj] = NULL;
+ mFirstObj++;
+ if (mFirstObj >= mMaxObj) mFirstObj = 0;
+
+ return count();
+ }
+ else if (i < mLastObj && i >= 0)
+ {
+ while(i < mLastObj)
+ {
+ mMemory[i] = mMemory[i+1];
+ i++;
+ }
+ mMemory[mLastObj] = NULL;
+ mLastObj--;
+ if (mLastObj < 0) mLastObj = mMaxObj-1;
+
+ return count();
+ }
+ }
+ else if (i <= mLastObj && i >= mFirstObj)
+ {
+ while(i < mLastObj)
+ {
+ mMemory[i] = mMemory[i+1];
+ i++;
+ }
+ mMemory[mLastObj] = NULL;
+ mLastObj--;
+ if (mLastObj < 0) mLastObj = mMaxObj-1;
+
+ return count();
+ }
+
+
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::removeObj(const Type& obj)
+{
+ S32 ind = find(obj);
+ if (ind >= 0)
+ {
+ return remove(ind);
+ }
+ return FAIL;
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::push(const Type &obj)
+{
+ if (mMaxObj - count() <= 1)
+ {
+ realloc(mMaxObj * 2);
+ }
+
+ mMemory[mLastObj++] = obj;
+
+ if (mLastObj >= mMaxObj)
+ {
+ mLastObj = 0;
+ }
+
+ return count();
+}
+
+template <class Type>
+inline S32 LLDynamicQueuePtr<Type>::pull(Type &obj)
+{
+ obj = NULL;
+
+ if (count() < 1) return -1;
+
+ obj = mMemory[mFirstObj];
+ mMemory[mFirstObj] = NULL;
+
+ mFirstObj++;
+
+ if (mFirstObj >= mMaxObj)
+ {
+ mFirstObj = 0;
+ }
+
+ return count();
+}
+
+template <class Type>
+inline const Type& LLDynamicQueuePtr<Type>::get(const S32 i) const
+{
+ return mMemory[i];
+}
+
+template <class Type>
+inline Type& LLDynamicQueuePtr<Type>::get(const S32 i)
+{
+ return mMemory[i];
+}
+
+
+#endif // LL_LLDQUEUEPTR_H
diff --git a/indra/llcommon/llendianswizzle.h b/indra/llcommon/llendianswizzle.h
new file mode 100644
index 0000000000..1794620b46
--- /dev/null
+++ b/indra/llcommon/llendianswizzle.h
@@ -0,0 +1,76 @@
+/**
+ * @file llendianswizzle.h
+ * @brief Functions for in-place bit swizzling
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLENDIANSWIZZLE_H
+#define LL_LLENDIANSWIZZLE_H
+
+/* This function is intended to be used for in-place swizzling, particularly after fread() of
+ binary values from a file. Such as:
+
+ numRead = fread(scale.mV, sizeof(float), 3, fp);
+ llendianswizzle(scale.mV, sizeof(float), 3);
+
+ It assumes that the values in the file are LITTLE endian, so it's a no-op on a little endian machine.
+
+ It keys off of typesize to do the correct swizzle, so make sure that typesize is the size of the native type.
+
+ 64-bit types are not yet handled.
+*/
+
+#ifdef LL_LITTLE_ENDIAN
+ // little endian is native for most things.
+ inline void llendianswizzle(void *,int,int)
+ {
+ // Nothing to do
+ }
+#endif
+
+#ifdef LL_BIG_ENDIAN
+ // big endian requires a bit of work.
+ inline void llendianswizzle(void *p,int typesize, int count)
+ {
+ int i;
+ switch(typesize)
+ {
+ case 2:
+ {
+ U16 temp;
+ for(i=count ;i!=0 ;i--)
+ {
+ temp = ((U16*)p)[0];
+ ((U16*)p)[0] = ((temp >> 8) & 0x000000FF) | ((temp << 8) & 0x0000FF00);
+ p = (void*)(((U16*)p) + 1);
+ }
+ }
+ break;
+
+ case 4:
+ {
+ U32 temp;
+ for(i=count; i!=0; i--)
+ {
+ temp = ((U32*)p)[0];
+ ((U32*)p)[0] =
+ ((temp >> 24) & 0x000000FF) |
+ ((temp >> 8) & 0x0000FF00) |
+ ((temp << 8) & 0x00FF0000) |
+ ((temp << 24) & 0xFF000000);
+ p = (void*)(((U32*)p) + 1);
+ }
+ }
+ break;
+ }
+
+ }
+#endif
+
+// Use this when working with a single integral value you want swizzled
+
+#define llendianswizzleone(x) llendianswizzle(&(x), sizeof(x), 1)
+
+#endif // LL_LLENDIANSWIZZLE_H
diff --git a/indra/llcommon/llenum.h b/indra/llcommon/llenum.h
new file mode 100644
index 0000000000..7443fe9d0f
--- /dev/null
+++ b/indra/llcommon/llenum.h
@@ -0,0 +1,60 @@
+/**
+ * @file llenum.h
+ * @author Tom Yedwab
+ * @brief Utility class for storing enum value <-> string lookup.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLENUM_H
+#define LL_LLENUM_H
+
+class LLEnum
+{
+public:
+ typedef std::pair<const std::string, const U32> enum_t;
+ enum
+ {
+ UNDEFINED = 0xffffffff,
+ };
+
+ LLEnum(const enum_t values_array[], const U32 length)
+ {
+ for (U32 i=0; i<length; ++i)
+ {
+ mEnumMap.insert(values_array[i]);
+ if (values_array[i].second >= mEnumArray.size())
+ {
+ mEnumArray.resize(values_array[i].second+1);
+ }
+ mEnumArray[values_array[i].second] = values_array[i].first;
+ }
+ }
+
+ const U32 operator[](std::string str)
+ {
+ std::map<const std::string, const U32>::iterator itor;
+ itor = mEnumMap.find(str);
+ if (itor != mEnumMap.end())
+ {
+ return itor->second;
+ }
+ return UNDEFINED;
+ }
+
+ const std::string operator[](U32 index)
+ {
+ if (index < mEnumArray.size())
+ {
+ return mEnumArray[index];
+ }
+ return "";
+ }
+
+private:
+ std::map<const std::string, const U32> mEnumMap;
+ std::vector<std::string> mEnumArray;
+};
+
+#endif // LL_LLENUM_H
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
new file mode 100644
index 0000000000..9f643fd9eb
--- /dev/null
+++ b/indra/llcommon/llerror.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file llerror.cpp
+ * @brief Function to crash.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#include "linden_common.h"
+
+#include "llerror.h"
+
+LLErrorBuffer gErrorBuffer;
+LLErrorStream gErrorStream(&gErrorBuffer);
+
+
+void _llcrash_and_loop()
+{
+ // Now, we go kaboom!
+ U32* crash = NULL;
+
+ *crash = 0;
+
+ while(TRUE)
+ {
+
+ // Loop forever, in case the crash didn't work?
+ }
+}
+
+LLScopedErrorLevel::LLScopedErrorLevel(LLErrorBuffer::ELevel error_level)
+{
+ mOrigErrorLevel = gErrorStream.getErrorLevel();
+ gErrorStream.setErrorLevel(error_level);
+}
+
+
+LLScopedErrorLevel::~LLScopedErrorLevel()
+{
+ gErrorStream.setErrorLevel(mOrigErrorLevel);
+}
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
new file mode 100644
index 0000000000..796ec4a421
--- /dev/null
+++ b/indra/llcommon/llerror.h
@@ -0,0 +1,218 @@
+/**
+ * @file llerror.h
+ * @brief Constants, functions, and macros for logging and runtime errors.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERROR_H
+#define LL_LLERROR_H
+
+#include <sstream>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "llerrorstream.h"
+#include "llerrorbuffer.h"
+
+// Specific error codes
+const S32 LL_ERR_NOERR = 0;
+const S32 LL_ERR_ASSET_REQUEST_FAILED = -1;
+//const S32 LL_ERR_ASSET_REQUEST_INVALID = -2;
+const S32 LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
+const S32 LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
+const S32 LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
+const S32 LL_ERR_EOF = -39;
+const S32 LL_ERR_CANNOT_OPEN_FILE = -42;
+const S32 LL_ERR_FILE_NOT_FOUND = -43;
+const S32 LL_ERR_FILE_EMPTY = -44;
+const S32 LL_ERR_TCP_TIMEOUT = -23016;
+const S32 LL_ERR_CIRCUIT_GONE = -23017;
+
+// Error types
+
+#define LLERR_IMAGE (1 << 1) // Image requests
+#define LLERR_MESSAGE (1 << 2) // Messaging
+#define LLERR_PERF (1 << 3) // Performance
+#define LLERR_SQL (1 << 4) // SQL statements
+#define LLERR_DOUG (1 << 5) // Doug's debugging
+#define LLERR_USER_INPUT (1 << 6) // Keyboard and mouse
+#define LLERR_TIMING (1 << 7) // Verbose time info
+#define LLERR_TASK (1 << 8) // Tracking tasks
+#define LLERR_MSG_HANDLER (1 << 9) //
+#define LLERR_CIRCUIT_INFO (1 << 10) // Message system circuit info
+#define LLERR_PHYSICS (1 << 11) // physics
+#define LLERR_VFS (1 << 12) // VFS
+const U32 LLERR_ALL = 0xffff;
+const U32 LLERR_NONE = 0x0;
+
+// Define one of these for different error levels in release...
+// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output.
+#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output
+#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output.
+
+
+//////////////////////////////////////////
+//
+// Implementation - ignore
+//
+//
+#ifdef _DEBUG
+#define SHOW_DEBUG
+#define SHOW_WARN
+#define SHOW_INFO
+#define SHOW_ASSERT
+#else // _DEBUG
+
+#ifdef RELEASE_SHOW_DEBUG
+#define SHOW_DEBUG
+#endif
+
+#ifdef RELEASE_SHOW_WARN
+#define SHOW_WARN
+#endif
+
+#ifdef RELEASE_SHOW_INFO
+#define SHOW_INFO
+#endif
+
+#ifdef RELEASE_SHOW_ASSERT
+#define SHOW_ASSERT
+#endif
+
+#endif // _DEBUG
+
+
+extern LLErrorStream gErrorStream;
+
+
+// LL Error macros
+//
+// Usage:
+//
+// llerrs << "An error, oh my!" << variable << endl;
+// llwarns << "Another error, fuck me!" << variable << endl;
+// llwarnst(LLERR_IMAGE) << "Debug, mother fucker" << endl;
+//
+// NOTE: The output format of filename(lineno): is so that MS DevStudio
+// can parse the output and automatically jump to that location
+
+inline std::string llerrno_string(int errnum)
+{
+ std::stringstream res;
+ res << "error(" << errnum << "):" << strerror(errnum) << " ";
+ return res.str();
+}
+
+inline std::string llerror_file_line(const char* file, S32 line)
+{
+ std::stringstream res;
+ res << file << "(" <<line << ")";
+ return res.str();
+}
+
+// Used to throw an error which is always causes a system halt.
+#define llerrs if (gErrorStream.isEnabledFor(LLErrorBuffer::FATAL)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::FATAL; \
+ llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : error\n"; \
+ llerror_oss << "ERROR: " << llerror_file_line(__FILE__, __LINE__) << " "
+
+// Used to show warnings
+#define llwarns if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : WARNING: "; \
+ else llerror_oss << "WARNING: "; \
+ llerror_oss
+
+// Alerts are for serious non-fatal situations that are not supposed to happen and need to alert someone
+#define llalerts if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : ALERT: "; \
+ else llerror_oss << "ALERT: "; \
+ llerror_oss
+
+// Used to show informational messages that don't get disabled
+#define llinfos if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
+ else llerror_oss << "INFO: "; \
+ llerror_oss
+
+#define llinfost(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO, type)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \
+ else llerror_oss << "INFO: [" << #type << "] "; \
+ llerror_oss
+
+// Used for general debugging output
+#define lldebugs if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
+ else llerror_oss << "DEBUG: "; \
+ llerror_oss
+
+#define lldebugst(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG, type)) \
+ { std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \
+ if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \
+ else llerror_oss << "DEBUG: [" << #type << "] "; \
+ llerror_oss
+
+#define llendl std::endl; gErrorStream.crashOnError(llerror_oss, llerror_level); }
+#define llendflush std::endl << std::flush; gErrorStream.crashOnError(llerror_oss, llerror_level); }
+#define llcont llerror_oss
+
+#define llerror(msg, num) llerrs << "Error # " << num << ": " << msg << llendl;
+
+#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
+
+#ifdef SHOW_ASSERT
+#define llassert(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llassert(func)
+#endif
+#define llassert_always(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+
+#ifdef SHOW_ASSERT
+#define llverify(func) if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl;
+#else
+#define llverify(func) (func); // get rid of warning C4189
+#endif
+
+// handy compile-time assert - enforce those template parameters!
+#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1]
+
+// Makes the app go down in flames, but on purpose!
+void _llcrash_and_loop();
+
+// Use as follows:
+// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl;
+//
+// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
+// should perhaps be replaced with boost::format.
+inline std::string llformat(const char *fmt, ...)
+{
+ char tstr[1024]; /* Flawfinder: ignore */
+ va_list va;
+ va_start(va, fmt);
+#if LL_WINDOWS
+ _vsnprintf(tstr, 1024, fmt, va);
+#else
+ vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */
+#endif
+ va_end(va);
+ return std::string(tstr);
+}
+
+// Helper class to temporarily change error level for the current scope.
+class LLScopedErrorLevel
+{
+public:
+ LLScopedErrorLevel(LLErrorBuffer::ELevel error_level);
+ ~LLScopedErrorLevel();
+
+private:
+ LLErrorBuffer::ELevel mOrigErrorLevel;
+};
+
+#endif // LL_LLERROR_H
diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp
new file mode 100644
index 0000000000..5c1ba1a64f
--- /dev/null
+++ b/indra/llcommon/llerrorthread.cpp
@@ -0,0 +1,189 @@
+/**
+ * @file llerrorthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llerrorthread.h"
+#include "llapp.h"
+
+LLErrorThread::LLErrorThread()
+ : LLThread("Error"),
+ mUserDatap(NULL)
+{
+}
+
+LLErrorThread::~LLErrorThread()
+{
+}
+
+void LLErrorThread::setUserData(void* user_data)
+{
+ mUserDatap = user_data;
+}
+
+
+void* LLErrorThread::getUserData() const
+{
+ return mUserDatap;
+}
+
+#if !LL_WINDOWS
+//
+// Various signal/error handling functions that can't be put into the class
+//
+void get_child_status(const int waitpid_status, int &process_status, bool &exited, bool do_logging)
+{
+ exited = false;
+ process_status = -1;
+ // The child process exited. Call its callback, and then clean it up
+ if (WIFEXITED(waitpid_status))
+ {
+ process_status = WEXITSTATUS(waitpid_status);
+ exited = true;
+ if (do_logging)
+ {
+ llinfos << "get_child_status - Child exited cleanly with return of " << process_status << llendl;
+ }
+ return;
+ }
+ else if (WIFSIGNALED(waitpid_status))
+ {
+ process_status = WTERMSIG(waitpid_status);
+ exited = true;
+ if (do_logging)
+ {
+ llinfos << "get_child_status - Child died because of uncaught signal " << process_status << llendl;
+#ifdef WCOREDUMP
+ if (WCOREDUMP(waitpid_status))
+ {
+ llinfos << "get_child_status - Child dumped core" << llendl;
+ }
+ else
+ {
+ llinfos << "get_child_status - Child didn't dump core" << llendl;
+ }
+#endif
+ }
+ return;
+ }
+ else if (do_logging)
+ {
+ // This is weird. I just dump the waitpid status into the status code,
+ // not that there's any way of telling what it is...
+ llinfos << "get_child_status - Got SIGCHILD but child didn't exit" << llendl;
+ process_status = waitpid_status;
+ }
+
+}
+#endif
+
+void LLErrorThread::run()
+{
+ LLApp::sErrorThreadRunning = TRUE;
+ // This thread sits and waits for the sole purpose
+ // of waiting for the signal/exception handlers to flag the
+ // application state as APP_STATUS_ERROR.
+ llinfos << "thread_error - Waiting for an error" << llendl;
+
+ S32 counter = 0;
+#if !LL_WINDOWS
+ U32 last_sig_child_count = 0;
+#endif
+ while (1)
+ {
+ if (LLApp::isError() || LLApp::isStopped())
+ {
+ // The application has stopped running, time to take action (maybe)
+ break;
+ }
+#if !LL_WINDOWS
+ // Check whether or not the main thread had a sig child we haven't handled.
+ U32 current_sig_child_count = LLApp::getSigChildCount();
+ if (last_sig_child_count != current_sig_child_count)
+ {
+ int status = 0;
+ pid_t child_pid = 0;
+ last_sig_child_count = current_sig_child_count;
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "thread_error handling SIGCHLD #" << current_sig_child_count << llendl;
+ }
+ for (LLApp::child_map::iterator iter = LLApp::sChildMap.begin(); iter != LLApp::sChildMap.end();)
+ {
+ child_pid = iter->first;
+ LLChildInfo &child_info = iter->second;
+ // check the status of *all* children, in case we missed a signal
+ if (0 != waitpid(child_pid, &status, WNOHANG))
+ {
+ bool exited = false;
+ int exit_status = -1;
+ get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
+
+ if (child_info.mCallback)
+ {
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Running child callback" << llendl;
+ }
+ child_info.mCallback(child_pid, exited, status);
+ }
+ LLApp::sChildMap.erase(iter++);
+ }
+ else
+ {
+ // Child didn't terminate, yet we got a sigchild somewhere...
+ if (child_info.mGotSigChild && child_info.mCallback)
+ {
+ child_info.mCallback(child_pid, false, 0);
+ }
+ child_info.mGotSigChild = FALSE;
+ iter++;
+ }
+ }
+
+ // check the status of *all* children, in case we missed a signal
+ // Same as above, but use the default child callback
+ while(0 < (child_pid = waitpid( -1, &status, WNOHANG )))
+ {
+ if (0 != waitpid(child_pid, &status, WNOHANG))
+ {
+ bool exited = false;
+ int exit_status = -1;
+ get_child_status(status, exit_status, exited, LLApp::sLogInSignal);
+ if (LLApp::sDefaultChildCallback)
+ {
+ if (LLApp::sLogInSignal)
+ {
+ llinfos << "Signal handler - Running default child callback" << llendl;
+ }
+ LLApp::sDefaultChildCallback(child_pid, true, status);
+ }
+ }
+ }
+ }
+
+
+#endif
+ ms_sleep(10);
+ counter++;
+ }
+ if (LLApp::isError())
+ {
+ // The app is in an error state, run the application's error handler.
+ //llinfos << "thread_error - An error has occurred, running error callback!" << llendl;
+ // Run the error handling callback
+ LLApp::runErrorHandler();
+ }
+ else
+ {
+ // Everything is okay, a clean exit.
+ //llinfos << "thread_error - Application exited cleanly" << llendl;
+ }
+
+ //llinfos << "thread_error - Exiting" << llendl;
+ LLApp::sErrorThreadRunning = FALSE;
+}
+
diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h
new file mode 100644
index 0000000000..66cbed519e
--- /dev/null
+++ b/indra/llcommon/llerrorthread.h
@@ -0,0 +1,28 @@
+/**
+ * @file llerrorthread.h
+ * @brief Specialized thread to handle runtime errors.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLERRORTHREAD_H
+#define LL_LLERRORTHREAD_H
+
+#include "llthread.h"
+
+class LLErrorThread : public LLThread
+{
+public:
+ LLErrorThread();
+ ~LLErrorThread();
+
+ /*virtual*/ void run(void);
+ void setUserData(void *user_data);
+ void *getUserData() const;
+
+protected:
+ void* mUserDatap; // User data associated with this thread
+};
+
+#endif // LL_LLERRORTHREAD_H
diff --git a/indra/llcommon/llevent.cpp b/indra/llcommon/llevent.cpp
new file mode 100644
index 0000000000..6e6fce6ec3
--- /dev/null
+++ b/indra/llcommon/llevent.cpp
@@ -0,0 +1,285 @@
+/**
+ * @file llevent.cpp
+ * @brief LLEvent and LLEventListener base classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llevent.h"
+
+/************************************************
+ Events
+************************************************/
+
+// virtual
+LLEvent::~LLEvent()
+{
+}
+
+// virtual
+bool LLEvent::accept(LLEventListener* listener)
+{
+ return true;
+}
+
+// virtual
+const std::string& LLEvent::desc()
+{
+ return mDesc;
+}
+
+/************************************************
+ Observables
+************************************************/
+
+LLObservable::LLObservable()
+ : mDispatcher(new LLEventDispatcher())
+{
+}
+
+// virtual
+LLObservable::~LLObservable()
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->disengage(this);
+ mDispatcher = NULL;
+ }
+}
+
+// virtual
+bool LLObservable::setDispatcher(LLPointer<LLEventDispatcher> dispatcher)
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->disengage(this);
+ mDispatcher = NULL;
+ }
+ if (dispatcher.notNull() || dispatcher->engage(this))
+ {
+ mDispatcher = dispatcher;
+ return true;
+ }
+ return false;
+}
+
+// Returns the current dispatcher pointer.
+// virtual
+LLEventDispatcher* LLObservable::getDispatcher()
+{
+ return mDispatcher;
+}
+
+// Notifies the dispatcher of an event being fired.
+void LLObservable::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ if (mDispatcher.notNull())
+ {
+ mDispatcher->fireEvent(event, filter);
+ }
+}
+
+/************************************************
+ Dispatchers
+************************************************/
+
+class LLEventDispatcher::Impl
+{
+public:
+ virtual ~Impl() { }
+ virtual bool engage(LLObservable* observable) { return true; }
+ virtual void disengage(LLObservable* observable) { }
+
+ virtual void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata) = 0;
+ virtual void removeListener(LLEventListener *listener) = 0;
+ virtual std::vector<LLListenerEntry> getListeners() const = 0;
+ virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter) = 0;
+};
+
+bool LLEventDispatcher::engage(LLObservable* observable)
+{
+ return impl->engage(observable);
+}
+
+void LLEventDispatcher::disengage(LLObservable* observable)
+{
+ impl->disengage(observable);
+}
+
+void LLEventDispatcher::addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata)
+{
+ impl->addListener(listener, filter, userdata);
+}
+
+void LLEventDispatcher::removeListener(LLEventListener *listener)
+{
+ impl->removeListener(listener);
+}
+
+std::vector<LLListenerEntry> LLEventDispatcher::getListeners() const
+{
+ return impl->getListeners();
+}
+
+
+bool LLEventDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ return impl->fireEvent(event, filter);
+}
+
+class LLSimpleDispatcher : public LLEventDispatcher::Impl
+{
+public:
+ LLSimpleDispatcher(LLEventDispatcher *parent) : mParent(parent) { }
+ virtual ~LLSimpleDispatcher();
+ virtual void addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata);
+ virtual void removeListener(LLEventListener* listener);
+ virtual std::vector<LLListenerEntry> getListeners() const;
+ virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+protected:
+ std::vector<LLListenerEntry> mListeners;
+ LLEventDispatcher *mParent;
+};
+
+LLSimpleDispatcher::~LLSimpleDispatcher()
+{
+ while (mListeners.size() > 0)
+ {
+ removeListener(mListeners.begin()->listener);
+ }
+}
+
+void LLSimpleDispatcher::addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata)
+{
+ if (listener == NULL) return;
+ removeListener(listener);
+ LLListenerEntry new_entry;
+ new_entry.listener = listener;
+ new_entry.filter = filter;
+ new_entry.userdata = userdata;
+ mListeners.push_back(new_entry);
+ listener->handleAttach(mParent);
+}
+
+void LLSimpleDispatcher::removeListener(LLEventListener* listener)
+{
+ std::vector<LLListenerEntry>::iterator itor;
+ for (itor=mListeners.begin(); itor!=mListeners.end();)
+ {
+ if ((*itor).listener == listener)
+ {
+ mListeners.erase(itor);
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+ listener->handleDetach(mParent);
+}
+
+std::vector<LLListenerEntry> LLSimpleDispatcher::getListeners() const
+{
+ std::vector<LLListenerEntry> ret;
+ std::vector<LLListenerEntry>::const_iterator itor;
+ for (itor=mListeners.begin(); itor!=mListeners.end(); ++itor)
+ {
+ ret.push_back(*itor);
+ }
+
+ return ret;
+}
+
+// virtual
+bool LLSimpleDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter)
+{
+ std::vector<LLListenerEntry>::iterator itor;
+ LLString filter_string = filter.asString();
+ for (itor=mListeners.begin(); itor!=mListeners.end(); ++itor)
+ {
+ LLListenerEntry& entry = *itor;
+ if (filter_string == "" || entry.filter.asString() == filter_string)
+ {
+ (entry.listener)->handleEvent(event, (*itor).userdata);
+ }
+ }
+ return true;
+}
+
+LLEventDispatcher::LLEventDispatcher()
+{
+ impl = new LLSimpleDispatcher(this);
+}
+
+LLEventDispatcher::~LLEventDispatcher()
+{
+ if (impl)
+ {
+ delete impl;
+ }
+}
+
+/************************************************
+ Listeners
+************************************************/
+
+LLEventListener::~LLEventListener()
+{
+}
+
+LLSimpleListener::~LLSimpleListener()
+{
+ clearDispatchers();
+}
+
+void LLSimpleListener::clearDispatchers()
+{
+ // Remove myself from all listening dispatchers
+ std::vector<LLEventDispatcher *>::iterator itor;
+ while (mDispatchers.size() > 0)
+ {
+ itor = mDispatchers.begin();
+ LLEventDispatcher *dispatcher = *itor;
+ dispatcher->removeListener(this);
+ itor = mDispatchers.begin();
+ if (itor != mDispatchers.end() && (*itor) == dispatcher)
+ {
+ // Somehow, the dispatcher was not removed. Remove it forcibly
+ mDispatchers.erase(itor);
+ }
+ }
+}
+
+bool LLSimpleListener::handleAttach(LLEventDispatcher *dispatcher)
+{
+ // Add dispatcher if it doesn't already exist
+ std::vector<LLEventDispatcher *>::iterator itor;
+ for (itor = mDispatchers.begin(); itor != mDispatchers.end(); ++itor)
+ {
+ if ((*itor) == dispatcher) return true;
+ }
+ mDispatchers.push_back(dispatcher);
+ return true;
+}
+
+bool LLSimpleListener::handleDetach(LLEventDispatcher *dispatcher)
+{
+ // Remove dispatcher from list
+ std::vector<LLEventDispatcher *>::iterator itor;
+ for (itor = mDispatchers.begin(); itor != mDispatchers.end(); )
+ {
+ if ((*itor) == dispatcher)
+ {
+ itor = mDispatchers.erase(itor);
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+ return true;
+}
diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h
new file mode 100644
index 0000000000..4a619ba16f
--- /dev/null
+++ b/indra/llcommon/llevent.h
@@ -0,0 +1,178 @@
+/**
+ * @file llevent.h
+ * @author Tom Yedwab
+ * @brief LLEvent and LLEventListener base classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_EVENT_H
+#define LL_EVENT_H
+
+#include "llsd.h"
+#include "llmemory.h"
+
+class LLEventListener;
+class LLEvent;
+class LLEventDispatcher;
+class LLObservable;
+
+// Abstract event. All events derive from LLEvent
+class LLEvent : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEvent();
+
+public:
+ LLEvent(LLObservable* source, const std::string& desc = "") : mSource(source), mDesc(desc) { }
+
+ LLObservable* getSource() { return mSource; }
+ virtual LLSD getValue() { return LLSD(); }
+ // Determines whether this particular listener
+ // should be notified of this event.
+ // If this function returns true, handleEvent is
+ // called on the listener with this event as the
+ // argument.
+ // Defaults to handling all events. Override this
+ // if associated with an Observable with many different listeners
+ virtual bool accept(LLEventListener* listener);
+
+ // return a string describing the event
+ virtual const std::string& desc();
+
+private:
+ LLObservable* mSource;
+ std::string mDesc;
+};
+
+// Abstract listener. All listeners derive from LLEventListener
+class LLEventListener : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEventListener();
+
+public:
+
+ // Processes the event.
+ // TODO: Make the return value less ambiguous?
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
+
+ // Called when an dispatcher starts/stops listening
+ virtual bool handleAttach(LLEventDispatcher *dispatcher) = 0;
+ virtual bool handleDetach(LLEventDispatcher *dispatcher) = 0;
+};
+
+// A listener which tracks references to it and cleans up when it's deallocated
+class LLSimpleListener : public LLEventListener
+{
+public:
+ virtual ~LLSimpleListener();
+ void clearDispatchers();
+ virtual bool handleAttach(LLEventDispatcher *dispatcher);
+ virtual bool handleDetach(LLEventDispatcher *dispatcher);
+
+protected:
+ std::vector<LLEventDispatcher *> mDispatchers;
+};
+
+class LLObservable; // defined below
+
+// A structure which stores a Listener and its metadata
+struct LLListenerEntry
+{
+ LLEventListener* listener;
+ LLSD filter;
+ LLSD userdata;
+};
+
+// Base class for a dispatcher - an object which listens
+// to events being fired and relays them to their
+// appropriate destinations.
+class LLEventDispatcher : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLEventDispatcher();
+
+public:
+ // The default constructor creates a default simple dispatcher implementation.
+ // The simple implementation has an array of listeners and fires every event to
+ // all of them.
+ LLEventDispatcher();
+
+ // This dispatcher is being attached to an observable object.
+ // If we return false, the attach fails.
+ bool engage(LLObservable* observable);
+
+ // This dispatcher is being detached from an observable object.
+ void disengage(LLObservable* observable);
+
+ // Adds a listener to this dispatcher, with a given user data
+ // that will be passed to the listener when an event is fired.
+ void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata);
+
+ // Removes a listener from this dispatcher
+ void removeListener(LLEventListener *listener);
+
+ // Gets a list of interested listeners
+ std::vector<LLListenerEntry> getListeners() const;
+
+ // Handle an event that has just been fired by communicating it
+ // to listeners, passing it across a network, etc.
+ bool fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+public:
+ class Impl;
+private:
+ Impl* impl;
+};
+
+// Interface for observable data (data that fires events)
+// In order for this class to work properly, it needs
+// an instance of an LLEventDispatcher to route events to their
+// listeners.
+class LLObservable
+{
+public:
+ // Initialize with the default Dispatcher
+ LLObservable();
+ virtual ~LLObservable();
+
+ // Replaces the existing dispatcher pointer to the new one,
+ // informing the dispatcher of the change.
+ virtual bool setDispatcher(LLPointer<LLEventDispatcher> dispatcher);
+
+ // Returns the current dispatcher pointer.
+ virtual LLEventDispatcher* getDispatcher();
+
+ void addListener(LLEventListener *listener, LLSD filter = "", const LLSD& userdata = "")
+ {
+ if (mDispatcher.notNull()) mDispatcher->addListener(listener, filter, userdata);
+ }
+ void removeListener(LLEventListener *listener)
+ {
+ if (mDispatcher.notNull()) mDispatcher->removeListener(listener);
+ }
+ // Notifies the dispatcher of an event being fired.
+ void fireEvent(LLPointer<LLEvent> event, LLSD filter);
+
+protected:
+ LLPointer<LLEventDispatcher> mDispatcher;
+};
+
+// Utility mixer class which fires & handles events
+class LLSimpleListenerObservable : public LLObservable, public LLSimpleListener
+{
+public:
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0;
+};
+
+class LLValueChangedEvent : public LLEvent
+{
+public:
+ LLValueChangedEvent(LLObservable* source, LLSD value) : LLEvent(source, "value_changed"), mValue(value) { }
+ LLSD getValue() { return mValue; }
+ LLSD mValue;
+};
+
+#endif // LL_EVENT_H
diff --git a/indra/llcommon/lleventemitter.h b/indra/llcommon/lleventemitter.h
new file mode 100644
index 0000000000..649f32df1e
--- /dev/null
+++ b/indra/llcommon/lleventemitter.h
@@ -0,0 +1,84 @@
+/**
+ * @file lleventemitter.h
+ * @brief General event emitter class
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// header guard
+#ifndef LL_EVENTEMITTER_H
+#define LL_EVENTEMITTER_H
+
+// standard headers
+#include <algorithm>
+#include <typeinfo>
+#include <iostream>
+#include <string>
+#include <list>
+
+#include "stdtypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// templatized emitter class
+template < class T >
+class eventEmitter
+{
+ public:
+ typedef typename T::EventType EventType;
+ typedef std::list< T* > ObserverContainer;
+ typedef void ( T::*observerMethod )( const EventType& );
+
+ protected:
+ ObserverContainer observers;
+
+ public:
+ eventEmitter () { };
+
+ ~eventEmitter () { };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ BOOL addObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return FALSE;
+
+ // check if observer already exists
+ if ( std::find ( observers.begin (), observers.end (), observerIn ) != observers.end () )
+ return FALSE;
+
+ // save it
+ observers.push_back ( observerIn );
+
+ return true;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ BOOL remObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return FALSE;
+
+ observers.remove ( observerIn );
+
+ return TRUE;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ void update ( observerMethod method, const EventType& msgIn )
+ {
+ typename std::list< T* >::iterator iter = observers.begin ();
+
+ while ( iter != observers.end () )
+ {
+ ( ( *iter )->*method ) ( msgIn );
+
+ ++iter;
+ };
+ };
+};
+
+#endif // lleventemitter_h
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
new file mode 100644
index 0000000000..b5d7f8adc8
--- /dev/null
+++ b/indra/llcommon/llfasttimer.h
@@ -0,0 +1,187 @@
+/**
+ * @file llfasttimer.h
+ * @brief Declaration of a fast timer.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFASTTIMER_H
+#define LL_LLFASTTIMER_H
+
+#include "lltimer.h"
+
+#define FAST_TIMER_ON 1
+
+U64 get_cpu_clock_count();
+
+class LLFastTimer
+{
+public:
+ enum EFastTimerType
+ {
+ // high level
+ FTM_FRAME,
+ FTM_UPDATE,
+ FTM_RENDER,
+ FTM_SWAP,
+ FTM_IDLE,
+ FTM_SLEEP,
+
+ // common simulate components
+ FTM_UPDATE_ANIMATION,
+ FTM_UPDATE_TERRAIN,
+ FTM_UPDATE_PRIMITIVES,
+ FTM_UPDATE_PARTICLES,
+ FTM_UPDATE_SKY,
+ FTM_UPDATE_TEXTURES,
+
+ // common render components
+ FTM_RENDER_GEOMETRY,
+ FTM_RENDER_TERRAIN,
+ FTM_RENDER_SIMPLE,
+ FTM_RENDER_SHINY,
+ FTM_RENDER_BUMP,
+ FTM_RENDER_TREES,
+ FTM_RENDER_CHARACTERS,
+ FTM_RENDER_OCCLUSION,
+ FTM_RENDER_ALPHA,
+ FTM_RENDER_CLOUDS,
+ FTM_RENDER_HUD,
+ FTM_RENDER_PARTICLES,
+ FTM_RENDER_WATER,
+ FTM_RENDER_TIMER,
+ FTM_RENDER_UI,
+ FTM_RENDER_FONTS,
+
+ // newview specific
+ FTM_MESSAGES,
+ FTM_REBUILD,
+ FTM_STATESORT,
+ FTM_POOLS,
+ FTM_POOLRENDER,
+ FTM_IDLE_CB,
+ FTM_WORLD_UPDATE,
+ FTM_UPDATE_MOVE,
+ FTM_OCTREE_BALANCE,
+ FTM_UPDATE_LIGHTS,
+ FTM_CULL,
+ FTM_CULL_REBOUND,
+ FTM_GEO_UPDATE,
+ FTM_GEO_RESERVE,
+ FTM_GEO_LIGHT,
+ FTM_GEO_SHADOW,
+ FTM_GEN_VOLUME,
+ FTM_GEN_TRIANGLES,
+ FTM_GEN_FLEX,
+ FTM_AUDIO_UPDATE,
+ FTM_RESET_DRAWORDER,
+ FTM_OBJECTLIST_UPDATE,
+ FTM_AVATAR_UPDATE,
+ FTM_JOINT_UPDATE,
+ FTM_ATTACHMENT_UPDATE,
+ FTM_LOD_UPDATE,
+ FTM_REGION_UPDATE,
+ FTM_CLEANUP,
+ FTM_NETWORK,
+ FTM_IDLE_NETWORK,
+ FTM_CREATE_OBJECT,
+ FTM_LOAD_AVATAR,
+ FTM_PROCESS_MESSAGES,
+ FTM_PROCESS_OBJECTS,
+ FTM_PROCESS_IMAGES,
+ FTM_IMAGE_UPDATE,
+ FTM_IMAGE_CREATE,
+ FTM_IMAGE_DECODE,
+ FTM_PIPELINE,
+ FTM_VFILE_WAIT,
+ FTM_FLEXIBLE_UPDATE,
+ FTM_OCCLUSION,
+ FTM_OCCLUSION_READBACK,
+ FTM_HUD_EFFECTS,
+ FTM_HUD_UPDATE,
+ FTM_INVENTORY,
+ FTM_AUTO_SELECT,
+ FTM_ARRANGE,
+ FTM_FILTER,
+ FTM_REFRESH,
+ FTM_SORT,
+
+ // Temp
+ FTM_TEMP1,
+ FTM_TEMP2,
+ FTM_TEMP3,
+ FTM_TEMP4,
+ FTM_TEMP5,
+ FTM_TEMP6,
+ FTM_TEMP7,
+ FTM_TEMP8,
+
+ FTM_OTHER, // Special, used by display code
+
+ FTM_NUM_TYPES
+ };
+ enum { FTM_HISTORY_NUM = 60 };
+ enum { FTM_MAX_DEPTH = 64 };
+
+public:
+ LLFastTimer(EFastTimerType type)
+ {
+#if FAST_TIMER_ON
+ mType = type;
+
+ // These don't get counted, because they use CPU clockticks
+ //gTimerBins[gCurTimerBin]++;
+ //LLTimer::sNumTimerCalls++;
+
+ U64 cpu_clocks = get_cpu_clock_count();
+
+ sStart[sCurDepth] = cpu_clocks;
+ sCurDepth++;
+#endif
+ };
+ ~LLFastTimer()
+ {
+#if FAST_TIMER_ON
+ U64 end,delta;
+ int i;
+
+ // These don't get counted, because they use CPU clockticks
+ //gTimerBins[gCurTimerBin]++;
+ //LLTimer::sNumTimerCalls++;
+ end = get_cpu_clock_count();
+
+ sCurDepth--;
+ delta = end - sStart[sCurDepth];
+ sCounter[mType] += delta;
+ sCalls[mType]++;
+ // Subtract delta from parents
+ for (i=0; i<sCurDepth; i++)
+ sStart[i] += delta;
+#endif
+ }
+
+ static void reset();
+ static U64 countsPerSecond();
+
+public:
+ static int sCurDepth;
+ static U64 sStart[FTM_MAX_DEPTH];
+ static U64 sCounter[FTM_NUM_TYPES];
+ static U64 sCalls[FTM_NUM_TYPES];
+ static U64 sCountAverage[FTM_NUM_TYPES];
+ static U64 sCallAverage[FTM_NUM_TYPES];
+ static U64 sCountHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
+ static U64 sCallHistory[FTM_HISTORY_NUM][FTM_NUM_TYPES];
+ static S32 sCurFrameIndex;
+ static S32 sLastFrameIndex;
+ static int sPauseHistory;
+ static int sResetHistory;
+ static F64 sCPUClockFrequency;
+
+private:
+ EFastTimerType mType;
+};
+
+
+#endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
new file mode 100644
index 0000000000..a02383c69e
--- /dev/null
+++ b/indra/llcommon/llfile.cpp
@@ -0,0 +1,251 @@
+/**
+ * @file llfile.cpp
+ * @author Michael Schlachter
+ * @date 2006-03-23
+ * @brief Implementation of cross-platform POSIX file buffer and c++
+ * stream classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llfile.h"
+#include "llstring.h"
+#include "llerror.h"
+
+using namespace std;
+
+// static
+int LLFile::mkdir(const char* dirname, int perms)
+{
+#if LL_WINDOWS
+ // permissions are ignored on Windows
+ std::string utf8dirname = dirname;
+ llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
+ return _wmkdir(utf16dirname.c_str());
+#else
+ return ::mkdir(dirname, (mode_t)perms);
+#endif
+}
+
+// static
+LLFILE* LLFile::fopen(const char* filename, const char* mode) /* Flawfinder: ignore */
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8mode = mode;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
+ return _wfopen(utf16filename.c_str(),utf16mode.c_str());
+#else
+ return ::fopen(filename,mode); /* Flawfinder: ignore */
+#endif
+}
+
+LLFILE* LLFile::_fsopen(const char* filename, const char* mode, int sharingFlag)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8mode = mode;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16mode = utf8str_to_utf16str(utf8mode);
+ return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag);
+#else
+ llassert(0);//No corresponding function on non-windows
+ return NULL;
+#endif
+}
+
+int LLFile::remove(const char* filename)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ return _wremove(utf16filename.c_str());
+#else
+ return ::remove(filename);
+#endif
+}
+
+int LLFile::rename(const char* filename, const char* newname)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ std::string utf8newname = newname;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
+ return _wrename(utf16filename.c_str(),utf16newname.c_str());
+#else
+ return ::rename(filename,newname);
+#endif
+}
+
+int LLFile::stat(const char* filename, llstat* filestatus)
+{
+#if LL_WINDOWS
+ std::string utf8filename = filename;
+ llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
+ return _wstat(utf16filename.c_str(),filestatus);
+#else
+ return ::stat(filename,filestatus);
+#endif
+}
+
+
+/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
+
+#if USE_LLFILESTREAMS
+
+LLFILE * LLFile::_Fiopen(const char *filename, std::ios::openmode mode,int) // protection currently unused
+{ // open a file
+ static const char *mods[] =
+ { // fopen mode strings corresponding to valid[i]
+ "r", "w", "w", "a", "rb", "wb", "wb", "ab",
+ "r+", "w+", "a+", "r+b", "w+b", "a+b",
+ 0};
+ static const int valid[] =
+ { // valid combinations of open flags
+ ios_base::in,
+ ios_base::out,
+ ios_base::out | ios_base::trunc,
+ ios_base::out | ios_base::app,
+ ios_base::in | ios_base::binary,
+ ios_base::out | ios_base::binary,
+ ios_base::out | ios_base::trunc | ios_base::binary,
+ ios_base::out | ios_base::app | ios_base::binary,
+ ios_base::in | ios_base::out,
+ ios_base::in | ios_base::out | ios_base::trunc,
+ ios_base::in | ios_base::out | ios_base::app,
+ ios_base::in | ios_base::out | ios_base::binary,
+ ios_base::in | ios_base::out | ios_base::trunc
+ | ios_base::binary,
+ ios_base::in | ios_base::out | ios_base::app
+ | ios_base::binary,
+ 0};
+
+ FILE *fp = 0;
+ int n;
+ ios_base::openmode atendflag = mode & ios_base::ate;
+ ios_base::openmode norepflag = mode & ios_base::_Noreplace;
+
+ if (mode & ios_base::_Nocreate)
+ mode |= ios_base::in; // file must exist
+ mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace);
+ for (n = 0; valid[n] != 0 && valid[n] != mode; ++n)
+ ; // look for a valid mode
+
+ if (valid[n] == 0)
+ return (0); // no valid mode
+ else if (norepflag && mode & (ios_base::out || ios_base::app)
+ && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */
+ { // file must not exist, close and fail
+ fclose(fp);
+ return (0);
+ }
+ else if (fp != 0 && fclose(fp) != 0)
+ return (0); // can't close after test open
+// should open with protection here, if other than default
+ else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */
+ return (0); // open failed
+
+ if (!atendflag || fseek(fp, 0, SEEK_END) == 0)
+ return (fp); // no need to seek to end, or seek succeeded
+
+ fclose(fp); // can't position at end
+ return (0);
+}
+
+/************** input file stream ********************************/
+
+void llifstream::close()
+{ // close the C stream
+ if (_Filebuffer && _Filebuffer->close() == 0)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ }
+}
+
+void llifstream::open(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot) /* Flawfinder: ignore */
+{ // open a C stream with specified mode
+
+ FILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot);
+ if(filep == NULL)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ return;
+ }
+ llassert(_Filebuffer == NULL);
+ _Filebuffer = new _Myfb(filep);
+ _Myios::init(_Filebuffer);
+}
+
+bool llifstream::is_open() const
+{ // test if C stream has been opened
+ if(_Filebuffer)
+ return (_Filebuffer->is_open());
+ return false;
+}
+llifstream::~llifstream()
+{
+ delete _Filebuffer;
+}
+
+llifstream::llifstream(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot)
+ : std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+
+{ // construct with named file and specified mode
+ open(_Filename, _Mode | ios_base::in, _Prot); /* Flawfinder: ignore */
+}
+
+
+/************** output file stream ********************************/
+
+bool llofstream::is_open() const
+{ // test if C stream has been opened
+ if(_Filebuffer)
+ return (_Filebuffer->is_open());
+ return false;
+}
+
+void llofstream::open(const char *_Filename,
+ ios_base::openmode _Mode,
+ int _Prot) /* Flawfinder: ignore */
+{ // open a C stream with specified mode
+
+ FILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot);
+ if(filep == NULL)
+ {
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ return;
+ }
+ llassert(_Filebuffer==NULL);
+ _Filebuffer = new _Myfb(filep);
+ _Myios::init(_Filebuffer);
+}
+
+void llofstream::close()
+{ // close the C stream
+ llassert(_Filebuffer);
+ if (_Filebuffer->close() == 0)
+ _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+}
+
+llofstream::llofstream(const char *_Filename,
+ std::ios_base::openmode _Mode,
+ int _Prot)
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL)
+{ // construct with named file and specified mode
+ open(_Filename, _Mode , _Prot); /* Flawfinder: ignore */
+}
+
+llofstream::~llofstream()
+{ // destroy the object
+ delete _Filebuffer;
+}
+
+#endif // #if USE_LLFILESTREAMS
+
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
new file mode 100644
index 0000000000..c7c4d2718a
--- /dev/null
+++ b/indra/llcommon/llfile.h
@@ -0,0 +1,151 @@
+/**
+ * @file llfile.h
+ * @author Michael Schlachter
+ * @date 2006-03-23
+ * @brief Declaration of cross-platform POSIX file buffer and c++
+ * stream classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFILE_H
+#define LL_LLFILE_H
+
+/**
+ * This class provides a cross platform interface to the filesystem.
+ * Attempts to mostly mirror the POSIX style IO functions.
+ */
+
+#include <string>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fstream>
+#include "stdtypes.h"
+
+typedef FILE LLFILE;
+
+#ifdef LL_WINDOWS
+#define USE_LLFILESTREAMS 1
+#else
+#define USE_LLFILESTREAMS 0
+#endif
+
+
+#if LL_WINDOWS
+// windows version of stat function and stat data structure are called _stat
+typedef struct _stat llstat;
+#else
+typedef struct stat llstat;
+#endif
+
+class LLFile
+{
+public:
+ // All these functions take UTF8 path/filenames.
+ static LLFILE* fopen(const char* filename,const char* accessmode); /* Flawfinder: ignore */
+ static LLFILE* _fsopen(const char* filename,const char* accessmode,int sharingFlag);
+
+ // perms is a permissions mask like 0777 or 0700. In most cases it will
+ // be overridden by the user's umask. It is ignored on Windows.
+ static int mkdir(const char* filename, int perms = 0700);
+
+ static int remove(const char* filename);
+ static int rename(const char* filename,const char* newname);
+ static int stat(const char* filename,llstat* file_status);
+ static LLFILE * _Fiopen(const char *filename, std::ios::openmode mode,int); // protection currently unused
+};
+
+
+#if USE_LLFILESTREAMS
+
+class llifstream : public std::basic_istream < char , std::char_traits < char > >
+{
+ // input stream associated with a C stream
+public:
+ typedef std::basic_ifstream<char,std::char_traits < char > > _Myt;
+ typedef std::basic_filebuf<char,std::char_traits< char > > _Myfb;
+ typedef std::basic_ios<char,std::char_traits< char > > _Myios;
+
+ llifstream()
+ : std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL)
+ { // construct unopened
+ }
+
+ explicit llifstream(const char *_Filename,
+ ios_base::openmode _Mode = ios_base::in,
+ int _Prot = (int)ios_base::_Openprot);
+
+ explicit llifstream(_Filet *_File)
+ : std::basic_istream<char,std::char_traits< char > >(NULL,true),
+ _Filebuffer(new _Myfb(_File))
+ { // construct with specified C stream
+ }
+ virtual ~llifstream();
+
+ _Myfb *rdbuf() const
+ { // return pointer to file buffer
+ return _Filebuffer;
+ }
+ bool is_open() const;
+ void open(const char *_Filename,
+ ios_base::openmode _Mode = ios_base::in,
+ int _Prot = (int)ios_base::_Openprot); /* Flawfinder: ignore */
+ void close();
+
+private:
+ _Myfb* _Filebuffer; // the file buffer
+};
+
+
+class llofstream : public std::basic_ostream< char , std::char_traits < char > >
+{
+public:
+ typedef std::basic_ostream< char , std::char_traits < char > > _Myt;
+ typedef std::basic_filebuf< char , std::char_traits < char > > _Myfb;
+ typedef std::basic_ios<char,std::char_traits < char > > _Myios;
+
+ llofstream()
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL)
+ { // construct unopened
+ }
+
+ explicit llofstream(const char *_Filename,
+ std::ios_base::openmode _Mode = ios_base::out,
+ int _Prot = (int)std::ios_base::_Openprot);
+
+
+ explicit llofstream(_Filet *_File)
+ : std::basic_ostream<char,std::char_traits < char > >(NULL,true),
+ _Filebuffer(new _Myfb(_File))//_File)
+ { // construct with specified C stream
+ }
+
+ virtual ~llofstream();
+
+ _Myfb *rdbuf() const
+ { // return pointer to file buffer
+ return _Filebuffer;
+ }
+
+ bool is_open() const;
+
+ void open(const char *_Filename,ios_base::openmode _Mode = ios_base::out,int _Prot = (int)ios_base::_Openprot); /* Flawfinder: ignore */
+
+ void close();
+
+private:
+ _Myfb *_Filebuffer; // the file buffer
+};
+
+
+
+#else
+//Use standard file streams on non windows platforms
+#define llifstream std::ifstream
+#define llofstream std::ofstream
+
+#endif
+
+
+#endif // not LL_LLFILE_H
diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp
new file mode 100644
index 0000000000..ca7bf1ce48
--- /dev/null
+++ b/indra/llcommon/llfixedbuffer.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file llfixedbuffer.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#include "linden_common.h"
+
+#include "llfixedbuffer.h"
+
+LLFixedBuffer::LLFixedBuffer(const U32 max_lines)
+{
+ mMaxLines = max_lines;
+ mTimer.reset();
+}
+
+
+LLFixedBuffer::~LLFixedBuffer()
+{
+ clear();
+}
+
+
+void LLFixedBuffer::clear()
+{
+ mLines.clear();
+ mAddTimes.clear();
+ mLineLengths.clear();
+
+ mTimer.reset();
+}
+
+
+void LLFixedBuffer::addLine(const LLString& utf8line)
+{
+ LLWString wstring = utf8str_to_wstring(utf8line);
+ LLFixedBuffer::addLine(wstring);
+}
+
+void LLFixedBuffer::addLine(const LLWString& line)
+{
+ if (line.empty())
+ {
+ return;
+ }
+
+ removeExtraLines();
+
+ mLines.push_back(line);
+ mLineLengths.push_back((S32)line.length());
+ mAddTimes.push_back(mTimer.getElapsedTimeF32());
+}
+
+
+void LLFixedBuffer::setMaxLines(S32 max_lines)
+{
+ mMaxLines = max_lines;
+
+ removeExtraLines();
+}
+
+
+void LLFixedBuffer::removeExtraLines()
+{
+ while ((S32)mLines.size() > llmax(0, (S32)(mMaxLines - 1)))
+ {
+ mLines.pop_front();
+ mAddTimes.pop_front();
+ mLineLengths.pop_front();
+ }
+}
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
new file mode 100644
index 0000000000..2ca21b2ded
--- /dev/null
+++ b/indra/llcommon/llfixedbuffer.h
@@ -0,0 +1,44 @@
+/**
+ * @file llfixedbuffer.h
+ * @brief A fixed size buffer of lines.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFIXEDBUFFER_H
+#define LL_LLFIXEDBUFFER_H
+
+#include "timer.h"
+#include <deque>
+#include <string>
+#include "llstring.h"
+
+// Fixed size buffer for console output and other things.
+
+class LLFixedBuffer
+{
+public:
+ LLFixedBuffer(const U32 max_lines = 20);
+ virtual ~LLFixedBuffer();
+
+ LLTimer mTimer;
+ U32 mMaxLines;
+ std::deque<LLWString> mLines;
+ std::deque<F32> mAddTimes;
+ std::deque<S32> mLineLengths;
+
+ void clear(); // Clear the buffer, and reset it.
+ virtual void addLine(const LLString& utf8line);
+ virtual void addLine(const LLWString& line);
+
+ // Get lines currently in the buffer, up to max_size chars, max_length lines
+ char *getLines(U32 max_size = 0, U32 max_length = 0);
+ void setMaxLines(S32 max_lines);
+protected:
+ virtual void removeExtraLines();
+};
+
+const U32 FIXED_BUF_MAX_LINE_LEN = 255; // Not including termnating 0
+
+#endif //LL_FIXED_BUFFER_H
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
new file mode 100644
index 0000000000..c66410651f
--- /dev/null
+++ b/indra/llcommon/llframetimer.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file llframetimer.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "u64.h"
+
+#include "llframetimer.h"
+
+// Static members
+//LLTimer LLFrameTimer::sInternalTimer;
+U64 LLFrameTimer::sStartTotalTime = totalTime();
+F64 LLFrameTimer::sFrameTime = 0.0;
+U64 LLFrameTimer::sTotalTime = 0;
+F64 LLFrameTimer::sTotalSeconds = 0.0;
+S32 LLFrameTimer::sFrameCount = 0;
+U64 LLFrameTimer::sFrameDeltaTime = 0;
+const F64 USEC_PER_SECOND = 1000000.0;
+const F64 USEC_TO_SEC_F64 = 0.000001;
+
+// static
+void LLFrameTimer::updateFrameTime()
+{
+ U64 total_time = totalTime();
+ sFrameDeltaTime = total_time - sTotalTime;
+ sTotalTime = total_time;
+ sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
+ sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
+ sFrameCount++;
+}
+
+void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
+{
+ mStartTime = sFrameTime;
+ mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
+}
+
+F64 LLFrameTimer::expiresAt() const
+{
+ F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
+ expires_at += mExpiry;
+ return expires_at;
+}
+
+BOOL LLFrameTimer::checkExpirationAndReset(F32 expiration)
+{
+ //llinfos << "LLFrameTimer::checkExpirationAndReset()" << llendl;
+ //llinfos << " mStartTime:" << mStartTime << llendl;
+ //llinfos << " sFrameTime:" << sFrameTime << llendl;
+ //llinfos << " mExpiry: " << mExpiry << llendl;
+
+ if(hasExpired())
+ {
+ reset();
+ setTimerExpirySec(expiration);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// static
+F32 LLFrameTimer::getFrameDeltaTimeF32()
+{
+ return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
+}
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
new file mode 100644
index 0000000000..05b01e6a72
--- /dev/null
+++ b/indra/llcommon/llframetimer.h
@@ -0,0 +1,122 @@
+/**
+ * @file llframetimer.h
+ * @brief A lightweight timer that measures seconds and is only
+ * updated once per frame.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFRAMETIMER_H
+#define LL_LLFRAMETIMER_H
+
+/**
+ * *NOTE: Because of limitations on linux which we do not really have
+ * time to explore, the total time is derived from the frame time
+ * and is recsynchronized on every frame.
+ */
+
+#include "lltimer.h"
+#include "timing.h"
+
+class LLFrameTimer
+{
+public:
+ LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(TRUE) {}
+
+ // Return the number of seconds since the start of this
+ // application instance.
+ static F64 getElapsedSeconds()
+ {
+ // Loses msec precision after ~4.5 hours...
+ return sFrameTime;
+ }
+
+ // Return a low precision usec since epoch
+ static U64 getTotalTime()
+ {
+ return sTotalTime ? sTotalTime : totalTime();
+ }
+
+ // Return a low precision seconds since epoch
+ static F64 getTotalSeconds()
+ {
+ return sTotalSeconds;
+ }
+
+ // Call this method once per frame to update the current frame
+ // time.
+ static void updateFrameTime();
+
+ static S32 getFrameCount() { return sFrameCount; }
+
+ static F32 getFrameDeltaTimeF32();
+
+ // MANIPULATORS
+ void start() { reset(); mStarted = TRUE; }
+ void stop() { mStarted = FALSE; }
+ void reset() { mStartTime = sFrameTime; mExpiry = sFrameTime; }
+ void setTimerExpirySec(F32 expiration) { mExpiry = expiration + mStartTime; }
+ void setExpiryAt(F64 seconds_since_epoch);
+ BOOL checkExpirationAndReset(F32 expiration);
+ F32 getElapsedTimeAndResetF32() { F32 t = F32(sFrameTime - mStartTime); reset(); return t; }
+
+ void setAge(const F64 age) { mStartTime = sFrameTime - age; }
+
+ // ACCESSORS
+ BOOL hasExpired() const { return (sFrameTime >= mExpiry); }
+ F32 getTimeToExpireF32() const { return (F32)(mExpiry - sFrameTime); }
+ F32 getElapsedTimeF32() const { return (F32)(sFrameTime - mStartTime); }
+ BOOL getStarted() const { return mStarted; }
+
+ // return the seconds since epoch when this timer will expire.
+ F64 expiresAt() const;
+
+protected:
+ // A single, high resolution timer that drives all LLFrameTimers
+ // *NOTE: no longer used.
+ //static LLTimer sInternalTimer;
+
+ //
+ // Aplication constants
+ //
+
+ // Start time of opp in usec since epoch
+ static U64 sStartTotalTime;
+
+ //
+ // Data updated per frame
+ //
+
+ // Seconds since application start
+ static F64 sFrameTime;
+
+ // Time that has elapsed since last call to updateFrameTime()
+ static U64 sFrameDeltaTime;
+
+ // Total microseconds since epoch.
+ static U64 sTotalTime;
+
+ // Seconds since epoch.
+ static F64 sTotalSeconds;
+
+ // Total number of frames elapsed in application
+ static S32 sFrameCount;
+
+ //
+ // Member data
+ //
+
+ // Number of seconds after application start when this timer was
+ // started. Set equal to sFrameTime when reset.
+ F64 mStartTime;
+
+ // Timer expires this many seconds after application start time.
+ F64 mExpiry;
+
+ // Useful bit of state usually associated with timers, but does
+ // not affect actual functionality
+ BOOL mStarted;
+};
+
+#endif // LL_LLFRAMETIMER_H
diff --git a/indra/llcommon/llhash.h b/indra/llcommon/llhash.h
new file mode 100644
index 0000000000..fbf059bbcd
--- /dev/null
+++ b/indra/llcommon/llhash.h
@@ -0,0 +1,45 @@
+/**
+ * @file llhash.h
+ * @brief Wrapper for a hash function.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHASH_H
+#define LL_LLHASH_H
+
+#include "llpreprocessor.h" // for GCC_VERSION
+
+#if (LL_WINDOWS)
+#include <hash_map>
+#include <algorithm>
+#elif LL_DARWIN || LL_LINUX
+# if GCC_VERSION >= 30400 // gcc 3.4 and up
+# include <ext/hashtable.h>
+# elif __GNUC__ >= 3
+# include <ext/stl_hashtable.h>
+# else
+# include <hashtable.h>
+# endif
+#else
+#error Please define your platform.
+#endif
+
+template<class T> inline size_t llhash(T value)
+{
+#if LL_WINDOWS
+ return stdext::hash_value<T>(value);
+#elif ( (defined _STLPORT_VERSION) || ((LL_LINUX) && (__GNUC__ <= 2)) )
+ std::hash<T> H;
+ return H(value);
+#elif LL_DARWIN || LL_LINUX
+ __gnu_cxx::hash<T> H;
+ return H(value);
+#else
+#error Please define your platform.
+#endif
+}
+
+#endif
+
diff --git a/indra/llcommon/llindexedqueue.h b/indra/llcommon/llindexedqueue.h
new file mode 100644
index 0000000000..c5ee58a4c6
--- /dev/null
+++ b/indra/llcommon/llindexedqueue.h
@@ -0,0 +1,137 @@
+/**
+ * @file llindexedqueue.h
+ * @brief An indexed FIFO queue, where only one element with each key
+ * can be in the queue.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINDEXEDQUEUE_H
+#define LL_LLINDEXEDQUEUE_H
+
+// An indexed FIFO queue, where only one element with each key can be in the queue.
+// This is ONLY used in the interest list, you'll probably want to review this code
+// carefully if you want to use it elsewhere - Doug
+
+template <typename Type>
+class LLIndexedQueue
+{
+protected:
+ typedef std::deque<Type> type_deque;
+ type_deque mQueue;
+ std::set<Type> mKeySet;
+
+public:
+ LLIndexedQueue() {}
+
+ // move_if_there is an O(n) operation
+ bool push_back(const Type &value, bool move_if_there = false)
+ {
+ if (mKeySet.find(value) != mKeySet.end())
+ {
+ // Already on the queue
+ if (move_if_there)
+ {
+ // Remove the existing entry.
+ typename type_deque::iterator it;
+ for (it = mQueue.begin(); it != mQueue.end(); ++it)
+ {
+ if (*it == value)
+ {
+ break;
+ }
+ }
+
+ // This HAS to succeed, otherwise there's a serious bug in the keyset implementation
+ // (although this isn't thread safe, at all)
+
+ mQueue.erase(it);
+ }
+ else
+ {
+ // We're not moving it, leave it alone
+ return false;
+ }
+ }
+ else
+ {
+ // Doesn't exist, add it to the key set
+ mKeySet.insert(value);
+ }
+
+ mQueue.push_back(value);
+
+ // We succeeded in adding the new element.
+ return true;
+ }
+
+ bool push_front(const Type &value, bool move_if_there = false)
+ {
+ if (mKeySet.find(value) != mKeySet.end())
+ {
+ // Already on the queue
+ if (move_if_there)
+ {
+ // Remove the existing entry.
+ typename type_deque::iterator it;
+ for (it = mQueue.begin(); it != mQueue.end(); ++it)
+ {
+ if (*it == value)
+ {
+ break;
+ }
+ }
+
+ // This HAS to succeed, otherwise there's a serious bug in the keyset implementation
+ // (although this isn't thread safe, at all)
+
+ mQueue.erase(it);
+ }
+ else
+ {
+ // We're not moving it, leave it alone
+ return false;
+ }
+ }
+ else
+ {
+ // Doesn't exist, add it to the key set
+ mKeySet.insert(value);
+ }
+
+ mQueue.push_front(value);
+ return true;
+ }
+
+ void pop()
+ {
+ Type value = mQueue.front();
+ mKeySet.erase(value);
+ mQueue.pop_front();
+ }
+
+ Type &front()
+ {
+ return mQueue.front();
+ }
+
+ S32 size() const
+ {
+ return mQueue.size();
+ }
+
+ bool empty() const
+ {
+ return mQueue.empty();
+ }
+
+ void clear()
+ {
+ // Clear out all elements on the queue
+ mQueue.clear();
+ mKeySet.clear();
+ }
+};
+
+#endif // LL_LLINDEXEDQUEUE_H
diff --git a/indra/llcommon/lllinkedqueue.h b/indra/llcommon/lllinkedqueue.h
new file mode 100644
index 0000000000..c386ca0e2b
--- /dev/null
+++ b/indra/llcommon/lllinkedqueue.h
@@ -0,0 +1,291 @@
+/**
+ * @file lllinkedqueue.h
+ * @brief Declaration of linked queue classes.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLINKEDQUEUE_H
+#define LL_LLLINKEDQUEUE_H
+
+#include "llerror.h"
+
+// node that actually contains the data
+template <class DATA_TYPE> class LLLinkedQueueNode
+{
+public:
+ DATA_TYPE mData;
+ LLLinkedQueueNode *mNextp;
+ LLLinkedQueueNode *mPrevp;
+
+
+public:
+ LLLinkedQueueNode();
+ LLLinkedQueueNode(const DATA_TYPE data);
+
+ // destructor does not, by default, destroy associated data
+ // however, the mDatap must be NULL to ensure that we aren't causing memory leaks
+ ~LLLinkedQueueNode();
+};
+
+
+
+template <class DATA_TYPE> class LLLinkedQueue
+{
+
+public:
+ LLLinkedQueue();
+
+ // destructor destroys list and nodes, but not data in nodes
+ ~LLLinkedQueue();
+
+ // Puts at end of FIFO
+ void push(const DATA_TYPE data);
+
+ // Takes off front of FIFO
+ BOOL pop(DATA_TYPE &data);
+ BOOL peek(DATA_TYPE &data);
+
+ void reset();
+
+ S32 getLength() const;
+
+ BOOL isEmpty() const;
+
+ BOOL remove(const DATA_TYPE data);
+
+ BOOL checkData(const DATA_TYPE data) const;
+
+private:
+ // add node to end of list
+ // set mCurrentp to mQueuep
+ void addNodeAtEnd(LLLinkedQueueNode<DATA_TYPE> *nodep);
+
+private:
+ LLLinkedQueueNode<DATA_TYPE> mHead; // head node
+ LLLinkedQueueNode<DATA_TYPE> mTail; // tail node
+ S32 mLength;
+};
+
+
+//
+// Nodes
+//
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::LLLinkedQueueNode() :
+ mData(), mNextp(NULL), mPrevp(NULL)
+{ }
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::LLLinkedQueueNode(const DATA_TYPE data) :
+ mData(data), mNextp(NULL), mPrevp(NULL)
+{ }
+
+template <class DATA_TYPE>
+LLLinkedQueueNode<DATA_TYPE>::~LLLinkedQueueNode()
+{ }
+
+
+//
+// Queue itself
+//
+
+template <class DATA_TYPE>
+LLLinkedQueue<DATA_TYPE>::LLLinkedQueue()
+: mHead(),
+ mTail(),
+ mLength(0)
+{ }
+
+
+// destructor destroys list and nodes, but not data in nodes
+template <class DATA_TYPE>
+LLLinkedQueue<DATA_TYPE>::~LLLinkedQueue()
+{
+ reset();
+}
+
+
+// put data into a node and stick it at the end of the list
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::push(const DATA_TYPE data)
+{
+ // make the new node
+ LLLinkedQueueNode<DATA_TYPE> *nodep = new LLLinkedQueueNode<DATA_TYPE>(data);
+
+ addNodeAtEnd(nodep);
+}
+
+
+// search the list starting at mHead.mNextp and remove the link with mDatap == data
+// set mCurrentp to mQueuep, or NULL if mQueuep points to node with mDatap == data
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::remove(const DATA_TYPE data)
+{
+ BOOL b_found = FALSE;
+
+ LLLinkedQueueNode<DATA_TYPE> *currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ if (currentp->mData == data)
+ {
+ b_found = TRUE;
+
+ // if there is a next one, fix it
+ if (currentp->mNextp)
+ {
+ currentp->mNextp->mPrevp = currentp->mPrevp;
+ }
+ else // we are at end of list
+ {
+ mTail.mPrevp = currentp->mPrevp;
+ }
+
+ // if there is a previous one, fix it
+ if (currentp->mPrevp)
+ {
+ currentp->mPrevp->mNextp = currentp->mNextp;
+ }
+ else // we are at beginning of list
+ {
+ mHead.mNextp = currentp->mNextp;
+ }
+
+ // remove the node
+ delete currentp;
+ mLength--;
+ break;
+ }
+ currentp = currentp->mNextp;
+ }
+
+ return b_found;
+}
+
+
+// remove all nodes from the list but do not delete associated data
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::reset()
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+ LLLinkedQueueNode<DATA_TYPE> *nextp;
+ currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ nextp = currentp->mNextp;
+ delete currentp;
+ currentp = nextp;
+ }
+
+ // reset mHead and mCurrentp
+ mHead.mNextp = NULL;
+ mTail.mPrevp = NULL;
+ mLength = 0;
+}
+
+template <class DATA_TYPE>
+S32 LLLinkedQueue<DATA_TYPE>::getLength() const
+{
+ return mLength;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::isEmpty() const
+{
+ return mLength <= 0;
+}
+
+// check to see if data is in list
+// set mCurrentp and mQueuep to the target of search if found, otherwise set mCurrentp to mQueuep
+// return TRUE if found, FALSE if not found
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::checkData(const DATA_TYPE data) const
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp = mHead.mNextp;
+
+ while (currentp)
+ {
+ if (currentp->mData == data)
+ {
+ return TRUE;
+ }
+ currentp = currentp->mNextp;
+ }
+ return FALSE;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::pop(DATA_TYPE &data)
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+
+ currentp = mHead.mNextp;
+ if (!currentp)
+ {
+ return FALSE;
+ }
+
+ mHead.mNextp = currentp->mNextp;
+ if (currentp->mNextp)
+ {
+ currentp->mNextp->mPrevp = currentp->mPrevp;
+ }
+ else
+ {
+ mTail.mPrevp = currentp->mPrevp;
+ }
+
+ data = currentp->mData;
+ delete currentp;
+ mLength--;
+ return TRUE;
+}
+
+template <class DATA_TYPE>
+BOOL LLLinkedQueue<DATA_TYPE>::peek(DATA_TYPE &data)
+{
+ LLLinkedQueueNode<DATA_TYPE> *currentp;
+
+ currentp = mHead.mNextp;
+ if (!currentp)
+ {
+ return FALSE;
+ }
+ data = currentp->mData;
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// private members
+//////////////////////////////////////////////////////////////////////////////////////////
+
+
+// add node to end of list
+// set mCurrentp to mQueuep
+template <class DATA_TYPE>
+void LLLinkedQueue<DATA_TYPE>::addNodeAtEnd(LLLinkedQueueNode<DATA_TYPE> *nodep)
+{
+ // add the node to the end of the list
+ nodep->mNextp = NULL;
+ nodep->mPrevp = mTail.mPrevp;
+ mTail.mPrevp = nodep;
+
+ // if there's something in the list, fix its back pointer
+ if (nodep->mPrevp)
+ {
+ nodep->mPrevp->mNextp = nodep;
+ }
+ else // otherwise fix the head node
+ {
+ mHead.mNextp = nodep;
+ }
+ mLength++;
+}
+
+#endif
diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp
new file mode 100644
index 0000000000..7dad6f82d8
--- /dev/null
+++ b/indra/llcommon/lllivefile.cpp
@@ -0,0 +1,74 @@
+/**
+ * @file lllivefile.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lllivefile.h"
+
+
+LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) :
+mForceCheck(true),
+mRefreshPeriod(refresh_period),
+mFilename(filename),
+mLastModTime(0),
+mLastExists(false)
+{
+}
+
+
+LLLiveFile::~LLLiveFile()
+{
+}
+
+
+bool LLLiveFile::checkAndReload()
+{
+ if (!mForceCheck && mRefreshTimer.getElapsedTimeF32() < mRefreshPeriod)
+ {
+ // Skip the check if not enough time has elapsed and we're not
+ // forcing a check of the file
+ return false;
+ }
+ mForceCheck = false;
+ mRefreshTimer.reset();
+
+ // Stat the file to see if it exists and when it was last modified.
+ llstat stat_data;
+ int res = LLFile::stat(mFilename.c_str(), &stat_data);
+
+ if (res)
+ {
+ // Couldn't stat the file, that means it doesn't exist or is
+ // broken somehow. Clear flags and return.
+ if (mLastExists)
+ {
+ loadFile(); // Load the file, even though it's missing to allow it to clear state.
+ mLastExists = false;
+ return true;
+ }
+ return false;
+ }
+
+ // The file exists, decide if we want to load it.
+ if (mLastExists)
+ {
+ // The file existed last time, don't read it if it hasn't changed since
+ // last time.
+ if (stat_data.st_mtime <= mLastModTime)
+ {
+ return false;
+ }
+ }
+
+ // We want to read the file. Update status info for the file.
+ mLastExists = true;
+ mLastModTime = stat_data.st_mtime;
+
+ loadFile();
+ return true;
+}
+
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h
new file mode 100644
index 0000000000..97c88a5c5c
--- /dev/null
+++ b/indra/llcommon/lllivefile.h
@@ -0,0 +1,34 @@
+/**
+ * @file lllivefile.h
+ * @brief Automatically reloads a file whenever it changes or is removed.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLIVEFILE_H
+#define LL_LLLIVEFILE_H
+
+#include "llframetimer.h"
+
+class LLLiveFile
+{
+public:
+ LLLiveFile(const std::string &filename, const F32 refresh_period = 5.f);
+ virtual ~LLLiveFile();
+
+ bool checkAndReload(); // Returns true if the file changed in any way
+
+protected:
+ virtual void loadFile() = 0; // Implement this to load your file if it changed
+
+ bool mForceCheck;
+ F32 mRefreshPeriod;
+ LLFrameTimer mRefreshTimer;
+
+ std::string mFilename;
+ time_t mLastModTime;
+ bool mLastExists;
+};
+
+#endif //LL_LLLIVEFILE_H
diff --git a/indra/llcommon/lllocalidhashmap.h b/indra/llcommon/lllocalidhashmap.h
new file mode 100644
index 0000000000..12f2b3f2d7
--- /dev/null
+++ b/indra/llcommon/lllocalidhashmap.h
@@ -0,0 +1,877 @@
+/**
+ * @file lllocalidhashmap.h
+ * @brief Map specialized for dealing with local ids
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLOCALIDHASHMAP_H
+#define LL_LLLOCALIDHASHMAP_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+
+const S32 MAX_ITERS = 4;
+// LocalID hash map
+
+//
+// LLLocalIDHashNode
+//
+
+template <class DATA, int SIZE>
+class LLLocalIDHashNode
+{
+public:
+ LLLocalIDHashNode();
+
+public:
+ S32 mCount;
+ U32 mKey[SIZE];
+ DATA mData[SIZE];
+ LLLocalIDHashNode<DATA, SIZE> *mNextNodep;
+};
+
+
+//
+// LLLocalIDHashNode implementation
+//
+template <class DATA, int SIZE>
+LLLocalIDHashNode<DATA, SIZE>::LLLocalIDHashNode()
+{
+ mCount = 0;
+ mNextNodep = NULL;
+}
+
+//
+// LLLocalIDHashMapIter
+//
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMap;
+
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMapIter
+{
+public:
+ LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ ~LLLocalIDHashMapIter();
+
+ void setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ inline void first();
+ inline void next();
+ inline DATA_TYPE& current(); // *NOTE: Deprecate? Phoenix 2005-04-15
+ inline BOOL done() const;
+ inline S32 currentBin() const;
+ inline void setBin(S32 bin);
+
+ DATA_TYPE& operator*() const
+ {
+ return mCurHashNodep->mData[mCurHashNodeKey];
+ }
+ DATA_TYPE* operator->() const
+ {
+ return &(operator*());
+ }
+
+ LLLocalIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
+
+ S32 mCurHashMapNodeNum;
+ S32 mCurHashNodeKey;
+
+ DATA_TYPE mNull;
+
+ S32 mIterID;
+};
+
+
+
+template <class DATA_TYPE, int SIZE>
+class LLLocalIDHashMap
+{
+public:
+ friend class LLLocalIDHashMapIter<DATA_TYPE, SIZE>;
+
+ LLLocalIDHashMap(); // DO NOT use this unless you explicitly setNull, or the data type constructs a "null"
+ // object by default
+ // basic constructor including sorter
+ LLLocalIDHashMap(const DATA_TYPE &null_data);
+ // Hack, this should really be a const ref, but I'm not doing it that way because the sim
+ // usually uses pointers.
+ ~LLLocalIDHashMap();
+
+ inline DATA_TYPE &get(const U32 local_id);
+ inline BOOL check(const U32 local_id) const;
+ inline DATA_TYPE &set(const U32 local_id, const DATA_TYPE data);
+ inline BOOL remove(const U32 local_id);
+ void removeAll();
+
+ void setNull(const DATA_TYPE data) { mNull = data; }
+
+ inline S32 getLength() const; // Warning, NOT O(1!)
+
+ void dumpIter();
+ void dumpBin(U32 bin);
+
+protected:
+ // Only used by the iterator.
+ void addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
+ void removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter);
+
+ // Remove the item and shift all items afterward down the list,
+ // fixing up iterators as we go.
+ BOOL removeWithShift(const U32 local_id);
+
+protected:
+ LLLocalIDHashNode<DATA_TYPE, SIZE> mNodes[256];
+
+ S32 mIterCount;
+ LLLocalIDHashMapIter<DATA_TYPE, SIZE> *mIters[MAX_ITERS];
+
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLLocalIDHashMap implementation
+//
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap()
+: mIterCount(0),
+ mNull()
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ mIters[i] = NULL;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::LLLocalIDHashMap(const DATA_TYPE &null_data)
+: mIterCount(0),
+ mNull(null_data)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ mIters[i] = NULL;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMap<DATA_TYPE, SIZE>::~LLLocalIDHashMap()
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i])
+ {
+ mIters[i]->mHashMapp = NULL;
+ mIterCount--;
+ }
+ }
+ removeAll();
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeAll()
+{
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+
+ BOOL first = TRUE;
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ nodep->mData[i] = mNull;
+ }
+
+ nodep->mCount = 0;
+ // Done with all objects in this node, go to the next.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
+ nodep = nodep->mNextNodep;
+
+ // Delete the node if it's not the first node
+ if (first)
+ {
+ first = FALSE;
+ curp->mNextNodep = NULL;
+ }
+ else
+ {
+ delete curp;
+ }
+ } while (nodep);
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpIter()
+{
+ std::cout << "Hash map with " << mIterCount << " iterators" << std::endl;
+
+ std::cout << "Hash Map Iterators:" << std::endl;
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i])
+ {
+ llinfos << i << " " << mIters[i]->mCurHashNodep << " " << mIters[i]->mCurHashNodeKey << llendl;
+ }
+ else
+ {
+ llinfos << i << "null" << llendl;
+ }
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::dumpBin(U32 bin)
+{
+ std::cout << "Dump bin " << bin << std::endl;
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ S32 node = 0;
+ do // First node guaranteed to be there.
+ {
+ std::cout << "Bin " << bin
+ << " node " << node
+ << " count " << nodep->mCount
+ << " contains " << std::flush;
+
+ S32 i;
+ for (i = 0; i < nodep->mCount; i++)
+ {
+ std::cout << nodep->mData[i] << " " << std::flush;
+ }
+
+ std::cout << std::endl;
+
+ nodep = nodep->mNextNodep;
+ node++;
+ } while (nodep);
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLLocalIDHashMap<DATA_TYPE, SIZE>::getLength() const
+{
+ S32 count = 0;
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ while (nodep)
+ {
+ count += nodep->mCount;
+ nodep = nodep->mNextNodep;
+ }
+ }
+ return count;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::get(const U32 local_id)
+{
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found it.
+ return nodep->mData[i];
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return mNull;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::check(const U32 local_id) const
+{
+ const LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ do // First node guaranteed to be there
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found it.
+ return TRUE;
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ // Didn't find anything
+ return FALSE;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMap<DATA_TYPE, SIZE>::set(const U32 local_id, const DATA_TYPE data)
+{
+ // Set is just like a normal find, except that if we find a match
+ // we replace it with the input value.
+ // If we don't find a match, we append to the end of the list.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[local_id & 0xff];
+
+ while (1)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // We found a match for this key, replace the data with
+ // the incoming data.
+ nodep->mData[i] = data;
+ return nodep->mData[i];
+ }
+ }
+ if (!nodep->mNextNodep)
+ {
+ // We've iterated through all of the keys without finding a match
+ if (i < SIZE)
+ {
+ // There's still some space on this node, append
+ // the key and data to it.
+ nodep->mKey[i] = local_id;
+ nodep->mData[i] = data;
+ nodep->mCount++;
+
+ return nodep->mData[i];
+ }
+ else
+ {
+ // This node is full, append a new node to the end.
+ nodep->mNextNodep = new LLLocalIDHashNode<DATA_TYPE, SIZE>;
+ nodep->mNextNodep->mKey[0] = local_id;
+ nodep->mNextNodep->mData[0] = data;
+ nodep->mNextNodep->mCount = 1;
+
+ return nodep->mNextNodep->mData[0];
+ }
+ }
+
+ // No match on this node, go to the next
+ nodep = nodep->mNextNodep;
+ }
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::remove(const U32 local_id)
+{
+ // Remove is the trickiest operation.
+ // What we want to do is swap the last element of the last
+ // node if we find the one that we want to remove, but we have
+ // to deal with deleting the node from the tail if it's empty, but
+ // NOT if it's the only node left.
+
+ const S32 node_index = local_id & 0xff;
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
+
+ // A modification of the standard search algorithm.
+ do // First node guaranteed to be there
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // If we're removing the item currently pointed to by one
+ // or more iterators, we can just swap in the last item
+ // and back the iterator(s) up by one.
+ // Otherwise, we need to do a slow and safe shift of all
+ // items back to one position to fill the hole and fix up
+ // all iterators we find.
+ BOOL need_shift = FALSE;
+ S32 cur_iter;
+ if (mIterCount)
+ {
+ for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
+ {
+ if (mIters[cur_iter])
+ {
+ // We only care if the hash map node is on the one
+ // that we're working on. If it's before, we've already
+ // traversed it, if it's after, changing the order doesn't
+ // matter.
+ if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
+ {
+ if ((mIters[cur_iter]->mCurHashNodep == nodep)
+ && (mIters[cur_iter]->mCurHashNodeKey == i))
+ {
+ // it's on the one we're deleting, we'll
+ // fix the iterator quickly below.
+ }
+ else
+ {
+ // We're trying to remove an item on this
+ // iterator's chain that this
+ // iterator doesn't point to! We need to do
+ // the slow remove-and-shift-down case.
+ need_shift = TRUE;
+ }
+ }
+ }
+ }
+ }
+
+ // Removing an item that isn't pointed to by all iterators
+ if (need_shift)
+ {
+ return removeWithShift(local_id);
+ }
+
+ // Fix the iterators that point to this node/i pair, the
+ // one we're deleting
+ for (cur_iter = 0; cur_iter < MAX_ITERS; cur_iter++)
+ {
+ if (mIters[cur_iter])
+ {
+ // We only care if the hash map node is on the one
+ // that we're working on. If it's before, we've already
+ // traversed it, if it's after, changing the order doesn't
+ // matter.
+ if (mIters[cur_iter]->mCurHashMapNodeNum == node_index)
+ {
+ if ((mIters[cur_iter]->mCurHashNodep == nodep)
+ && (mIters[cur_iter]->mCurHashNodeKey == i))
+ {
+ // We can handle the case where we're deleting
+ // the element we're on trivially (sort of).
+ if (nodep->mCount > 1)
+ {
+ // If we're not going to delete this node,
+ // it's OK.
+ mIters[cur_iter]->mCurHashNodeKey--;
+ }
+ else
+ {
+ // We're going to delete this node, because this
+ // is the last element on it.
+
+ // Find the next node, and then back up one.
+ mIters[cur_iter]->next();
+ mIters[cur_iter]->mCurHashNodeKey--;
+ }
+ }
+ }
+ }
+ }
+
+ // We found the node that we want to remove.
+ // Find the last (and next-to-last) node, and the index of the last
+ // element. We could conceviably start from the node we're on,
+ // but that makes it more complicated, this is easier.
+
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[node_index];
+ LLLocalIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
+
+ // Find the last and next-to-last
+ while (lastp->mNextNodep)
+ {
+ prevp = lastp;
+ lastp = lastp->mNextNodep;
+ }
+
+ // First, swap in the last to the current location.
+ nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
+ nodep->mData[i] = lastp->mData[lastp->mCount - 1];
+
+ // Now, we delete the entry
+ lastp->mCount--;
+ lastp->mData[lastp->mCount] = mNull;
+
+ if (!lastp->mCount)
+ {
+ // We deleted the last element!
+ if (lastp != &mNodes[local_id & 0xff])
+ {
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete lastp;
+ }
+ }
+
+ return TRUE;
+ }
+ }
+
+ // Iterate to the next node, we've scanned all the entries in this one.
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return FALSE;
+}
+
+template <class DATA_TYPE, int SIZE>
+BOOL LLLocalIDHashMap<DATA_TYPE, SIZE>::removeWithShift(const U32 local_id)
+{
+ const S32 node_index = local_id & 0xFF;
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[node_index];
+ LLLocalIDHashNode<DATA_TYPE, SIZE>* prevp = NULL;
+ BOOL found = FALSE;
+
+ do // First node guaranteed to be there
+ {
+ const S32 count = nodep->mCount;
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (nodep->mKey[i] == local_id)
+ {
+ // Found the item. Start shifting items from later
+ // in the list over this item.
+ found = TRUE;
+ }
+
+ if (found)
+ {
+ // If there is an iterator on this node, we need to
+ // back it up.
+ S32 cur_iter;
+ for (cur_iter = 0; cur_iter <MAX_ITERS; cur_iter++)
+ {
+ LLLocalIDHashMapIter<DATA_TYPE, SIZE>* iter;
+ iter = mIters[cur_iter];
+ // If an iterator is on this node,i pair, then back it up.
+ if (iter
+ && iter->mCurHashMapNodeNum == node_index
+ && iter->mCurHashNodep == nodep
+ && iter->mCurHashNodeKey == i)
+ {
+ if (i > 0)
+ {
+ // Don't need to move iterator nodep, since
+ // we're in the same node.
+ iter->mCurHashNodeKey--;
+ }
+ else if (prevp)
+ {
+ // need to go the previous node, last item
+ iter->mCurHashNodep = prevp;
+ iter->mCurHashNodeKey = prevp->mCount - 1;
+ }
+ else
+ {
+ // we're on the first item in the list, but
+ // need to go back anyhow.
+
+ // BUG: If this deletion empties the list,
+ // iter->done() will be wrong until
+ // iter->next() is called.
+ iter->mCurHashNodeKey = -1;
+ }
+ }
+ }
+
+ // Copy data from the next position into this position.
+ if (i < count-1)
+ {
+ // we're not on the last item in the node,
+ // so we can copy within the node
+ nodep->mKey[i] = nodep->mKey[i+1];
+ nodep->mData[i] = nodep->mData[i+1];
+ }
+ else if (nodep->mNextNodep)
+ {
+ // we're on the last item in the node,
+ // but there's a next node we can copy from
+ nodep->mKey[i] = nodep->mNextNodep->mKey[0];
+ nodep->mData[i] = nodep->mNextNodep->mData[0];
+ }
+ else
+ {
+ // We're on the last position in the list.
+ // No one to copy from. Replace with nothing.
+ nodep->mKey[i] = 0;
+ nodep->mData[i] = mNull;
+ }
+ }
+ }
+
+ // Last node in chain, so delete the last node
+ if (found
+ && !nodep->mNextNodep)
+ {
+ // delete the last item off the last node
+ nodep->mCount--;
+
+ if (nodep->mCount == 0)
+ {
+ // We deleted the last element!
+ if (nodep != &mNodes[node_index])
+ {
+ // Always have a prevp if we're not the head.
+ llassert(prevp);
+
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete nodep;
+ nodep = NULL;
+ }
+ }
+
+ // Deleted last item in chain, so we're done.
+ return found;
+ }
+
+ prevp = nodep;
+ nodep = nodep->mNextNodep;
+ } while (nodep);
+
+ return found;
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::removeIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i] == iter)
+ {
+ mIters[i] = NULL;
+ mIterCount--;
+ return;
+ }
+ }
+ llerrs << "Iterator " << iter << " not found for removal in hash map!" << llendl;
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMap<DATA_TYPE, SIZE>::addIter(LLLocalIDHashMapIter<DATA_TYPE, SIZE> *iter)
+{
+ S32 i;
+ for (i = 0; i < MAX_ITERS; i++)
+ {
+ if (mIters[i] == NULL)
+ {
+ mIters[i] = iter;
+ mIterCount++;
+ return;
+ }
+ }
+ llerrs << "More than " << MAX_ITERS << " iterating over a map simultaneously!" << llendl;
+}
+
+
+
+//
+// LLLocalIDHashMapIter Implementation
+//
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMapIter<DATA_TYPE, SIZE>::LLLocalIDHashMapIter(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ mHashMapp = NULL;
+ setMap(hash_mapp);
+}
+
+template <class DATA_TYPE, int SIZE>
+LLLocalIDHashMapIter<DATA_TYPE, SIZE>::~LLLocalIDHashMapIter()
+{
+ if (mHashMapp)
+ {
+ mHashMapp->removeIter(this);
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setMap(LLLocalIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ if (mHashMapp)
+ {
+ mHashMapp->removeIter(this);
+ }
+ mHashMapp = hash_mapp;
+ if (mHashMapp)
+ {
+ mHashMapp->addIter(this);
+ }
+
+ mCurHashNodep = NULL;
+ mCurHashMapNodeNum = -1;
+ mCurHashNodeKey = 0;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::first()
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ for (i = 0; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // Completely empty!
+ mCurHashNodep = NULL;
+ //return mNull;
+ return;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLLocalIDHashMapIter<DATA_TYPE, SIZE>::done() const
+{
+ return mCurHashNodep ? FALSE : TRUE;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLLocalIDHashMapIter<DATA_TYPE, SIZE>::currentBin() const
+{
+ if ( (mCurHashMapNodeNum > 255)
+ ||(mCurHashMapNodeNum < 0))
+ {
+ return 0;
+ }
+ else
+ {
+ return mCurHashMapNodeNum;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::setBin(S32 bin)
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ bin = llclamp(bin, 0, 255);
+ for (i = bin; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ return;
+ }
+ }
+ for (i = 0; i < bin; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ return;
+ }
+ }
+ // Completely empty!
+ mCurHashNodep = NULL;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLLocalIDHashMapIter<DATA_TYPE, SIZE>::current()
+{
+ if (!mCurHashNodep)
+ {
+ return mNull;
+ }
+ return mCurHashNodep->mData[mCurHashNodeKey];
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLLocalIDHashMapIter<DATA_TYPE, SIZE>::next()
+{
+ // No current entry, this iterator is done
+ if (!mCurHashNodep)
+ {
+ //return mNull;
+ return;
+ }
+
+ // Go to the next element
+ mCurHashNodeKey++;
+ if (mCurHashNodeKey < mCurHashNodep->mCount)
+ {
+ // We're not done with this node, return the current element
+ //return mCurHashNodep->mData[mCurHashNodeKey];
+ return;
+ }
+
+ // Done with this node, move to the next
+ mCurHashNodep = mCurHashNodep->mNextNodep;
+ if (mCurHashNodep)
+ {
+ // Return the first element
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+
+ // Find the next non-empty node (keyed on the first byte)
+ mCurHashMapNodeNum++;
+
+ S32 i;
+ for (i = mCurHashMapNodeNum; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ // We found one that wasn't empty
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // OK, we're done, nothing else to iterate
+ mCurHashNodep = NULL;
+ mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
+ //return mNull;
+ return;
+}
+
+#endif // LL_LLLOCALIDHASHMAP_H
diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h
new file mode 100644
index 0000000000..5c9017e43b
--- /dev/null
+++ b/indra/llcommon/lllslconstants.h
@@ -0,0 +1,139 @@
+/**
+ * @file lllslconstants.h
+ * @author James Cook
+ * @brief Constants used in lsl.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLSLCONSTANTS_H
+#define LL_LLLSLCONSTANTS_H
+
+// LSL: Return flags for llGetAgentInfo
+const U32 AGENT_FLYING = 0x0001;
+const U32 AGENT_ATTACHMENTS = 0x0002;
+const U32 AGENT_SCRIPTED = 0x0004;
+const U32 AGENT_MOUSELOOK = 0x0008;
+const U32 AGENT_SITTING = 0x0010;
+const U32 AGENT_ON_OBJECT = 0x0020;
+const U32 AGENT_AWAY = 0x0040;
+const U32 AGENT_WALKING = 0x0080;
+const U32 AGENT_IN_AIR = 0x0100;
+const U32 AGENT_TYPING = 0x0200;
+const U32 AGENT_CROUCHING = 0x0400;
+const U32 AGENT_BUSY = 0x0800;
+const U32 AGENT_ALWAYS_RUN = 0x1000;
+
+const S32 LSL_REMOTE_DATA_CHANNEL = 1;
+const S32 LSL_REMOTE_DATA_REQUEST = 2;
+const S32 LSL_REMOTE_DATA_REPLY = 3;
+
+// Constants used in extended LSL primitive setter and getters
+const S32 LSL_PRIM_TYPE_LEGACY = 1; // No longer supported.
+const S32 LSL_PRIM_MATERIAL = 2;
+const S32 LSL_PRIM_PHYSICS = 3;
+const S32 LSL_PRIM_TEMP_ON_REZ = 4;
+const S32 LSL_PRIM_PHANTOM = 5;
+const S32 LSL_PRIM_POSITION = 6;
+const S32 LSL_PRIM_SIZE = 7;
+const S32 LSL_PRIM_ROTATION = 8;
+const S32 LSL_PRIM_TYPE = 9; // Replacement for LSL_PRIM_TYPE_LEGACY
+const S32 LSL_PRIM_TEXTURE = 17;
+const S32 LSL_PRIM_COLOR = 18;
+const S32 LSL_PRIM_BUMP_SHINY = 19;
+const S32 LSL_PRIM_FULLBRIGHT = 20;
+const S32 LSL_PRIM_FLEXIBLE = 21;
+const S32 LSL_PRIM_TEXGEN = 22;
+const S32 LSL_PRIM_POINT_LIGHT = 23;
+const S32 LSL_PRIM_CAST_SHADOWS = 24;
+
+const S32 LSL_PRIM_TYPE_BOX = 0;
+const S32 LSL_PRIM_TYPE_CYLINDER= 1;
+const S32 LSL_PRIM_TYPE_PRISM = 2;
+const S32 LSL_PRIM_TYPE_SPHERE = 3;
+const S32 LSL_PRIM_TYPE_TORUS = 4;
+const S32 LSL_PRIM_TYPE_TUBE = 5;
+const S32 LSL_PRIM_TYPE_RING = 6;
+
+const S32 LSL_PRIM_HOLE_DEFAULT = 0x00;
+const S32 LSL_PRIM_HOLE_CIRCLE = 0x10;
+const S32 LSL_PRIM_HOLE_SQUARE = 0x20;
+const S32 LSL_PRIM_HOLE_TRIANGLE= 0x30;
+
+const S32 LSL_PRIM_MATERIAL_STONE = 0;
+const S32 LSL_PRIM_MATERIAL_METAL = 1;
+const S32 LSL_PRIM_MATERIAL_GLASS = 2;
+const S32 LSL_PRIM_MATERIAL_WOOD = 3;
+const S32 LSL_PRIM_MATERIAL_FLESH = 4;
+const S32 LSL_PRIM_MATERIAL_PLASTIC = 5;
+const S32 LSL_PRIM_MATERIAL_RUBBER = 6;
+const S32 LSL_PRIM_MATERIAL_LIGHT = 7;
+
+const S32 LSL_PRIM_SHINY_NONE = 0;
+const S32 LSL_PRIM_SHINY_LOW = 1;
+const S32 LSL_PRIM_SHINY_MEDIUM = 2;
+const S32 LSL_PRIM_SHINY_HIGH = 3;
+
+const S32 LSL_PRIM_TEXGEN_DEFAULT = 0;
+const S32 LSL_PRIM_TEXGEN_PLANAR = 1;
+
+const S32 LSL_PRIM_BUMP_NONE = 0;
+const S32 LSL_PRIM_BUMP_BRIGHT = 1;
+const S32 LSL_PRIM_BUMP_DARK = 2;
+const S32 LSL_PRIM_BUMP_WOOD = 3;
+const S32 LSL_PRIM_BUMP_BARK = 4;
+const S32 LSL_PRIM_BUMP_BRICKS = 5;
+const S32 LSL_PRIM_BUMP_CHECKER = 6;
+const S32 LSL_PRIM_BUMP_CONCRETE = 7;
+const S32 LSL_PRIM_BUMP_TILE = 8;
+const S32 LSL_PRIM_BUMP_STONE = 9;
+const S32 LSL_PRIM_BUMP_DISKS = 10;
+const S32 LSL_PRIM_BUMP_GRAVEL = 11;
+const S32 LSL_PRIM_BUMP_BLOBS = 12;
+const S32 LSL_PRIM_BUMP_SIDING = 13;
+const S32 LSL_PRIM_BUMP_LARGETILE = 14;
+const S32 LSL_PRIM_BUMP_STUCCO = 15;
+const S32 LSL_PRIM_BUMP_SUCTION = 16;
+const S32 LSL_PRIM_BUMP_WEAVE = 17;
+
+const S32 LSL_ALL_SIDES = -1;
+const S32 LSL_LINK_ROOT = 1;
+const S32 LSL_LINK_FIRST_CHILD = 2;
+const S32 LSL_LINK_SET = -1;
+const S32 LSL_LINK_ALL_OTHERS = -2;
+const S32 LSL_LINK_ALL_CHILDREN = -3;
+const S32 LSL_LINK_THIS = -4;
+
+// LSL constants for llSetForSell
+const S32 SELL_NOT = 0;
+const S32 SELL_ORIGINAL = 1;
+const S32 SELL_COPY = 2;
+const S32 SELL_CONTENTS = 3;
+
+// LSL constants for llSetPayPrice
+const S32 PAY_PRICE_HIDE = -1;
+const S32 PAY_PRICE_DEFAULT = -2;
+const S32 MAX_PAY_BUTTONS = 4;
+const S32 PAY_BUTTON_DEFAULT_0 = 1;
+const S32 PAY_BUTTON_DEFAULT_1 = 5;
+const S32 PAY_BUTTON_DEFAULT_2 = 10;
+const S32 PAY_BUTTON_DEFAULT_3 = 20;
+
+// lsl email registration.
+const S32 EMAIL_REG_SUBSCRIBE_OBJECT = 0x01;
+const S32 EMAIL_REG_UNSUBSCRIBE_OBJECT = 0x02;
+const S32 EMAIL_REG_UNSUBSCRIBE_SIM = 0x04;
+
+const S32 LIST_STAT_RANGE = 0;
+const S32 LIST_STAT_MIN = 1;
+const S32 LIST_STAT_MAX = 2;
+const S32 LIST_STAT_MEAN = 3;
+const S32 LIST_STAT_MEDIAN = 4;
+const S32 LIST_STAT_STD_DEV = 5;
+const S32 LIST_STAT_SUM = 6;
+const S32 LIST_STAT_SUM_SQUARES = 7;
+const S32 LIST_STAT_NUM_COUNT = 8;
+const S32 LIST_STAT_GEO_MEAN = 9;
+
+#endif
diff --git a/indra/llcommon/llmap.h b/indra/llcommon/llmap.h
new file mode 100644
index 0000000000..fc958421da
--- /dev/null
+++ b/indra/llcommon/llmap.h
@@ -0,0 +1,231 @@
+/**
+ * @file llmap.h
+ * @brief LLMap class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMAP_H
+#define LL_LLMAP_H
+
+#include <stdio.h>
+#include <utility>
+#include <map>
+
+// llmap uses the fast stl library code in a manner consistant with LLSkipMap, et. al.
+
+template<class INDEX_TYPE, class MAPPED_TYPE> class LLMap
+{
+private:
+ typedef typename std::map<INDEX_TYPE, MAPPED_TYPE> stl_map_t;
+ typedef typename stl_map_t::iterator stl_iter_t;
+ typedef typename stl_map_t::value_type stl_value_t;
+
+ stl_map_t mStlMap;
+ stl_iter_t mCurIter; // *iterator = pair<const INDEX_TYPE, MAPPED_TYPE>
+ MAPPED_TYPE dummy_data;
+ INDEX_TYPE dummy_index;
+
+public:
+ LLMap() : mStlMap()
+ {
+ memset((void*)(&dummy_data), 0x0, sizeof(MAPPED_TYPE));
+ memset((void*)(&dummy_index), 0x0, sizeof(INDEX_TYPE));
+ mCurIter = mStlMap.begin();
+ }
+ ~LLMap()
+ {
+ mStlMap.clear();
+ }
+
+ // use these functions to itterate through a list
+ void resetMap()
+ {
+ mCurIter = mStlMap.begin();
+ }
+
+ // get the current data and bump mCurrentp
+ // This is kind of screwy since it returns a reference;
+ // We have to have a dummy value for when we reach the end
+ // or in case we have an empty list. Presumably, this value
+ // will initialize to some NULL value that will end the iterator.
+ // We really shouldn't be using getNextData() or getNextKey() anyway...
+ MAPPED_TYPE &getNextData()
+ {
+ if (mCurIter == mStlMap.end())
+ {
+ return dummy_data;
+ }
+ else
+ {
+ return (*mCurIter++).second;
+ }
+ }
+
+ const INDEX_TYPE &getNextKey()
+ {
+ if (mCurIter == mStlMap.end())
+ {
+ return dummy_index;
+ }
+ else
+ {
+ return (*mCurIter++).first;
+ }
+ }
+
+ MAPPED_TYPE &getFirstData()
+ {
+ resetMap();
+ return getNextData();
+ }
+
+ const INDEX_TYPE &getFirstKey()
+ {
+ resetMap();
+ return getNextKey();
+ }
+
+ S32 getLength()
+ {
+ return mStlMap.size();
+ }
+
+ void addData(const INDEX_TYPE &index, MAPPED_TYPE pointed_to)
+ {
+ mStlMap.insert(stl_value_t(index, pointed_to));
+ }
+
+ void addData(const INDEX_TYPE &index)
+ {
+ mStlMap.insert(stl_value_t(index, dummy_data));
+ }
+
+ // if index doesn't exist, then insert a new node and return it
+ MAPPED_TYPE &getData(const INDEX_TYPE &index)
+ {
+ std::pair<stl_iter_t, bool> res;
+ res = mStlMap.insert(stl_value_t(index, dummy_data));
+ return res.first->second;
+ }
+
+ // if index doesn't exist, then insert a new node, return it, and set b_new_entry to true
+ MAPPED_TYPE &getData(const INDEX_TYPE &index, BOOL &b_new_entry)
+ {
+ std::pair<stl_iter_t, bool> res;
+ res = mStlMap.insert(stl_value_t(index, dummy_data));
+ b_new_entry = res.second;
+ return res.first->second;
+ }
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ MAPPED_TYPE getIfThere(const INDEX_TYPE &index)
+ {
+ stl_iter_t iter;
+ iter = mStlMap.find(index);
+ if (iter == mStlMap.end())
+ {
+ return (MAPPED_TYPE)0;
+ }
+ else
+ {
+ return (*iter).second;
+ }
+ }
+
+
+ // if index doesn't exist, then make a new node and return it
+ MAPPED_TYPE &operator[](const INDEX_TYPE &index)
+ {
+ return getData(index);
+ }
+
+ // do a reverse look-up, return NULL if failed
+ INDEX_TYPE reverseLookup(const MAPPED_TYPE data)
+ {
+ stl_iter_t iter;
+ stl_iter_t end_iter;
+ iter = mStlMap.begin();
+ end_iter = mStlMap.end();
+ while (iter != end_iter)
+ {
+ if ((*iter).second == data)
+ return (*iter).first;
+ iter++;
+ }
+ return (INDEX_TYPE)0;
+ }
+
+ BOOL removeData(const INDEX_TYPE &index)
+ {
+ mCurIter = mStlMap.find(index);
+ if (mCurIter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ stl_iter_t iter = mCurIter++; // incrament mCurIter to the next element
+ mStlMap.erase(iter);
+ return TRUE;
+ }
+ }
+
+ // does this index exist?
+ BOOL checkData(const INDEX_TYPE &index)
+ {
+ stl_iter_t iter;
+ iter = mStlMap.find(index);
+ if (iter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ mCurIter = iter;
+ return TRUE;
+ }
+ }
+
+ BOOL deleteData(const INDEX_TYPE &index)
+ {
+ mCurIter = mStlMap.find(index);
+ if (mCurIter == mStlMap.end())
+ {
+ return FALSE;
+ }
+ else
+ {
+ stl_iter_t iter = mCurIter++; // incrament mCurIter to the next element
+ delete (*iter).second;
+ mStlMap.erase(iter);
+ return TRUE;
+ }
+ }
+
+ void deleteAllData()
+ {
+ stl_iter_t iter;
+ stl_iter_t end_iter;
+ iter = mStlMap.begin();
+ end_iter = mStlMap.end();
+ while (iter != end_iter)
+ {
+ delete (*iter).second;
+ iter++;
+ }
+ mStlMap.clear();
+ mCurIter = mStlMap.end();
+ }
+
+ void removeAllData()
+ {
+ mStlMap.clear();
+ }
+};
+
+
+#endif
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
new file mode 100644
index 0000000000..fd2c408fd4
--- /dev/null
+++ b/indra/llcommon/llmemory.cpp
@@ -0,0 +1,293 @@
+/**
+ * @file llmemory.cpp
+ * @brief Very special memory allocation/deallocation stuff here
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmemory.h"
+
+//----------------------------------------------------------------------------
+
+//static
+char* LLMemory::reserveMem = 0;
+
+//static
+void LLMemory::initClass()
+{
+ if (!reserveMem)
+ {
+ reserveMem = new char[16*1024]; // reserve 16K for out of memory error handling
+ }
+}
+
+//static
+void LLMemory::cleanupClass()
+{
+ delete [] reserveMem;
+ reserveMem = NULL;
+}
+
+//static
+void LLMemory::freeReserve()
+{
+ delete [] reserveMem;
+ reserveMem = NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+//static
+#if MEM_TRACK_TYPE
+S32 LLMemType::sCurDepth = 0;
+S32 LLMemType::sCurType = LLMemType::MTYPE_INIT;
+S32 LLMemType::sType[LLMemType::MTYPE_MAX_DEPTH];
+S32 LLMemType::sMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
+S32 LLMemType::sMaxMemCount[LLMemType::MTYPE_NUM_TYPES] = { 0 };
+S32 LLMemType::sOverheadMem = 0;
+
+const char* LLMemType::sTypeDesc[LLMemType::MTYPE_NUM_TYPES] =
+{
+ "INIT",
+ "STARTUP",
+ "MAIN",
+
+ "IMAGEBASE",
+ "IMAGERAW",
+ "IMAGEFORMATTED",
+
+ "APPFMTIMAGE",
+ "APPRAWIMAGE",
+ "APPAUXRAWIMAGE",
+
+ "DRAWABLE",
+ "OBJECT",
+ "PIPELINE",
+ "AVATAR",
+ "PARTICLES",
+ "REGIONS",
+ "INVENTORY",
+ "ANIMATION",
+ "NETWORK",
+ "PHYSICS",
+ "INTERESTLIST",
+
+ "SCRIPT",
+ "SCRIPT_RUN",
+ "SCRIPT_BYTECODE",
+
+ "IO_PUMP",
+ "IO_TCP",
+ "IO_BUFFER",
+ "IO_HTTP_SERVER"
+ "IO_SD_SERVER",
+ "IO_SD_CLIENT",
+ "IO_URL_REQUEST",
+
+ "TEMP1",
+ "TEMP2",
+ "TEMP3",
+ "TEMP4",
+ "TEMP5",
+ "TEMP6",
+ "TEMP7",
+ "TEMP8",
+ "TEMP9"
+};
+
+#endif
+S32 LLMemType::sTotalMem = 0;
+S32 LLMemType::sMaxTotalMem = 0;
+
+//static
+void LLMemType::printMem()
+{
+ S32 misc_mem = sTotalMem;
+#if MEM_TRACK_TYPE
+ for (S32 i=0; i<MTYPE_NUM_TYPES; i++)
+ {
+ if (sMemCount[i])
+ {
+ llinfos << llformat("MEM: % 20s %03d MB (%03d MB)",sTypeDesc[i],sMemCount[i]>>20,sMaxMemCount[i]>>20) << llendl;
+ }
+ misc_mem -= sMemCount[i];
+ }
+#endif
+ llinfos << llformat("MEM: % 20s %03d MB","MISC",misc_mem>>20) << llendl;
+ llinfos << llformat("MEM: % 20s %03d MB (Max=%d MB)","TOTAL",sTotalMem>>20,sMaxTotalMem>>20) << llendl;
+}
+
+#if MEM_TRACK_MEM
+
+void* ll_allocate (size_t size)
+{
+ if (size == 0)
+ {
+ llwarns << "Null allocation" << llendl;
+ }
+
+ size = (size+3)&~3;
+ S32 alloc_size = size + 4;
+#if MEM_TRACK_TYPE
+ alloc_size += 4;
+#endif
+ char* p = (char*)malloc(alloc_size);
+ if (p == NULL)
+ {
+ LLMemory::freeReserve();
+ llerrs << "Out of memory Error" << llendl;
+ }
+ LLMemType::sTotalMem += size;
+ LLMemType::sMaxTotalMem = llmax(LLMemType::sTotalMem, LLMemType::sMaxTotalMem);
+ LLMemType::sOverheadMem += 4;
+ *(size_t*)p = size;
+ p += 4;
+#if MEM_TRACK_TYPE
+ if (LLMemType::sCurType < 0 || LLMemType::sCurType >= LLMemType::MTYPE_NUM_TYPES)
+ {
+ llerrs << "Memory Type Error: new" << llendl;
+ }
+ LLMemType::sOverheadMem += 4;
+ *(S32*)p = LLMemType::sCurType;
+ p += 4;
+ LLMemType::sMemCount[LLMemType::sCurType] += size;
+ if (LLMemType::sMemCount[LLMemType::sCurType] > LLMemType::sMaxMemCount[LLMemType::sCurType])
+ {
+ LLMemType::sMaxMemCount[LLMemType::sCurType] = LLMemType::sMemCount[LLMemType::sCurType];
+ }
+#endif
+ return (void*)p;
+}
+
+void ll_release (void *pin)
+{
+ if (!pin)
+ {
+ return;
+ }
+ char* p = (char*)pin;
+#if MEM_TRACK_TYPE
+ p -= 4;
+ S32 type = *(S32*)p;
+ if (type < 0 || type >= LLMemType::MTYPE_NUM_TYPES)
+ {
+ llerrs << "Memory Type Error: delete" << llendl;
+ }
+#endif
+ p -= 4;
+ S32 size = *(size_t*)p;
+ LLMemType::sOverheadMem -= 4;
+#if MEM_TRACK_TYPE
+ LLMemType::sMemCount[type] -= size;
+ LLMemType::sOverheadMem -= 4;
+#endif
+ LLMemType::sTotalMem -= size;
+ free(p);
+}
+
+#else
+
+void* ll_allocate (size_t size)
+{
+ if (size == 0)
+ {
+ llwarns << "Null allocation" << llendl;
+ }
+ void *p = malloc(size);
+ if (p == NULL)
+ {
+ LLMemory::freeReserve();
+ llerrs << "Out of memory Error" << llendl;
+ }
+ return p;
+}
+
+void ll_release (void *p)
+{
+ free(p);
+}
+
+#endif
+
+#if MEM_TRACK_MEM
+
+void* operator new (size_t size)
+{
+ return ll_allocate(size);
+}
+
+void* operator new[] (size_t size)
+{
+ return ll_allocate(size);
+}
+
+void operator delete (void *p)
+{
+ ll_release(p);
+}
+
+void operator delete[] (void *p)
+{
+ ll_release(p);
+}
+
+#endif
+
+//----------------------------------------------------------------------------
+
+//static
+LLMutex* LLThreadSafeRefCount::sMutex = 0;
+
+//static
+void LLThreadSafeRefCount::initClass()
+{
+ if (!sMutex)
+ {
+ sMutex = new LLMutex(0);
+ }
+}
+
+//static
+void LLThreadSafeRefCount::cleanupClass()
+{
+ delete sMutex;
+ sMutex = NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+LLThreadSafeRefCount::LLThreadSafeRefCount() :
+ mRef(0)
+{
+}
+
+LLThreadSafeRefCount::~LLThreadSafeRefCount()
+{
+ if (mRef != 0)
+ {
+ llerrs << "deleting non-zero reference" << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+LLRefCount::LLRefCount() :
+ mRef(0)
+{
+}
+
+LLRefCount::~LLRefCount()
+{
+ if (mRef != 0)
+ {
+ llerrs << "deleting non-zero reference" << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
new file mode 100644
index 0000000000..905d05254a
--- /dev/null
+++ b/indra/llcommon/llmemory.h
@@ -0,0 +1,300 @@
+/**
+ * @file llmemory.h
+ * @brief Memory allocation/deallocation header-stuff goes here.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_MEMORY_H
+#define LL_MEMORY_H
+
+#include <new>
+#include <cstdlib>
+
+#include "llerror.h"
+#include "llthread.h"
+#include "llmemtype.h"
+
+extern S32 gTotalDAlloc;
+extern S32 gTotalDAUse;
+extern S32 gDACount;
+
+const U32 LLREFCOUNT_SENTINEL_VALUE = 0xAAAAAAAA;
+
+//----------------------------------------------------------------------------
+
+class LLMemory
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+ static void freeReserve();
+private:
+ static char* reserveMem;
+};
+
+//----------------------------------------------------------------------------
+// RefCount objects should generally only be accessed by way of LLPointer<>'s
+// NOTE: LLPointer<LLFoo> x = new LLFoo(); MAY NOT BE THREAD SAFE
+// if LLFoo::LLFoo() does anything like put itself in an update queue.
+// The queue may get accessed before it gets assigned to x.
+// The correct implementation is:
+// LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting
+// x->instantiate(); // does stuff like place x into an update queue
+
+class LLThreadSafeRefCount
+{
+public:
+ static void initClass(); // creates sMutex
+ static void cleanupClass(); // destroys sMutex
+
+private:
+ static LLMutex* sMutex;
+
+private:
+ LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented
+ LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented
+
+protected:
+ virtual ~LLThreadSafeRefCount(); // use unref()
+
+public:
+ LLThreadSafeRefCount();
+
+ void ref()
+ {
+ if (sMutex) sMutex->lock();
+ mRef++;
+ if (sMutex) sMutex->unlock();
+ }
+
+ S32 unref()
+ {
+ llassert(mRef >= 1);
+ if (sMutex) sMutex->lock();
+ S32 res = --mRef;
+ if (sMutex) sMutex->unlock();
+ if (0 == res)
+ {
+ delete this;
+ res = 0;
+ }
+ return res;
+ }
+ S32 getNumRefs() const
+ {
+ return mRef;
+ }
+
+private:
+ S32 mRef;
+};
+
+//----------------------------------------------------------------------------
+
+class LLRefCount
+{
+protected:
+ LLRefCount(const LLRefCount&); // not implemented
+private:
+ LLRefCount&operator=(const LLRefCount&); // not implemented
+
+protected:
+ virtual ~LLRefCount(); // use unref()
+
+public:
+ LLRefCount();
+
+ void ref()
+ {
+ mRef++;
+ }
+
+ S32 unref()
+ {
+ llassert(mRef >= 1);
+ if (0 == --mRef)
+ {
+ delete this;
+ return 0;
+ }
+ return mRef;
+ }
+ S32 getNumRefs() const
+ {
+ return mRef;
+ }
+
+private:
+ S32 mRef;
+};
+
+//----------------------------------------------------------------------------
+
+template <class Type> class LLPointer
+{
+public:
+
+ LLPointer() :
+ mPointer(NULL)
+ {
+ }
+
+ LLPointer(Type* ptr) :
+ mPointer(ptr)
+ {
+ ref();
+ }
+
+ LLPointer(const LLPointer<Type>& ptr) :
+ mPointer(ptr.mPointer)
+ {
+ ref();
+ }
+
+ // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
+ template<typename Subclass>
+ LLPointer(const LLPointer<Subclass>& ptr) :
+ mPointer(ptr.get())
+ {
+ ref();
+ }
+
+ ~LLPointer()
+ {
+ unref();
+ }
+
+ Type* get() const { return mPointer; }
+ const Type* operator->() const { return mPointer; }
+ Type* operator->() { return mPointer; }
+ const Type& operator*() const { return *mPointer; }
+ Type& operator*() { return *mPointer; }
+
+ operator BOOL() const { return (mPointer != NULL); }
+ operator bool() const { return (mPointer != NULL); }
+ bool operator!() const { return (mPointer == NULL); }
+ bool isNull() const { return (mPointer == NULL); }
+ bool notNull() const { return (mPointer != NULL); }
+
+ operator Type*() const { return mPointer; }
+ operator const Type*() const { return mPointer; }
+ bool operator !=(Type* ptr) const { return (mPointer != ptr); }
+ bool operator ==(Type* ptr) const { return (mPointer == ptr); }
+ bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); }
+ bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
+ bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
+
+ LLPointer<Type>& operator =(Type* ptr)
+ {
+ if( mPointer != ptr )
+ {
+ unref();
+ mPointer = ptr;
+ ref();
+ }
+
+ return *this;
+ }
+
+ LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
+ {
+ if( mPointer != ptr.mPointer )
+ {
+ unref();
+ mPointer = ptr.mPointer;
+ ref();
+ }
+ return *this;
+ }
+
+ // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
+ template<typename Subclass>
+ LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr)
+ {
+ if( mPointer != ptr.get() )
+ {
+ unref();
+ mPointer = ptr.get();
+ ref();
+ }
+ return *this;
+ }
+
+protected:
+ void ref()
+ {
+ if (mPointer)
+ {
+ mPointer->ref();
+ }
+ }
+
+ void unref()
+ {
+ if (mPointer)
+ {
+ Type *tempp = mPointer;
+ mPointer = NULL;
+ tempp->unref();
+ if (mPointer != NULL)
+ {
+ llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl;
+ unref();
+ }
+ }
+ }
+
+protected:
+ Type* mPointer;
+};
+
+// LLInitializedPointer is just a pointer with a default constructor that initializes it to NULL
+// NOT a smart pointer like LLPointer<>
+// Useful for example in std::map<int,LLInitializedPointer<LLFoo> >
+// (std::map uses the default constructor for creating new entries)
+template <typename T> class LLInitializedPointer
+{
+public:
+ LLInitializedPointer() : mPointer(NULL) {}
+ ~LLInitializedPointer() { delete mPointer; }
+
+ const T* operator->() const { return mPointer; }
+ T* operator->() { return mPointer; }
+ const T& operator*() const { return *mPointer; }
+ T& operator*() { return *mPointer; }
+ operator const T*() const { return mPointer; }
+ operator T*() { return mPointer; }
+ T* operator=(T* x) { return (mPointer = x); }
+ operator bool() const { return mPointer != NULL; }
+ bool operator!() const { return mPointer == NULL; }
+ bool operator==(T* rhs) { return mPointer == rhs; }
+ bool operator==(const LLInitializedPointer<T>* rhs) { return mPointer == rhs.mPointer; }
+
+protected:
+ T* mPointer;
+};
+
+//----------------------------------------------------------------------------
+
+// LLSingleton implements the getInstance() method part of the Singleton pattern. It can't make
+// the derived class constructors protected, though, so you have to do that yourself.
+// The proper way to use LLSingleton is to inherit from it while using the typename that you'd
+// like to be static as the template parameter, like so:
+// class FooBar: public LLSingleton<FooBar>
+// As currently written, it is not thread-safe.
+template <typename T>
+class LLSingleton
+{
+public:
+ static T* getInstance()
+ {
+ static T instance;
+ return &instance;
+ }
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmemorystream.cpp b/indra/llcommon/llmemorystream.cpp
new file mode 100644
index 0000000000..dc0ad5aadb
--- /dev/null
+++ b/indra/llcommon/llmemorystream.cpp
@@ -0,0 +1,52 @@
+/**
+ * @file llmemorystream.cpp
+ * @author Phoenix
+ * @date 2005-06-03
+ * @brief Buffer and stream for a fixed linear memory segment.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmemorystream.h"
+
+LLMemoryStreamBuf::LLMemoryStreamBuf(const U8* start, S32 length)
+{
+ reset(start, length);
+}
+
+LLMemoryStreamBuf::~LLMemoryStreamBuf()
+{
+}
+
+void LLMemoryStreamBuf::reset(const U8* start, S32 length)
+{
+ setg((char*)start, (char*)start, (char*)start + length);
+}
+
+int LLMemoryStreamBuf::underflow()
+{
+ //lldebugs << "LLMemoryStreamBuf::underflow()" << llendl;
+ if(gptr() < egptr())
+ {
+ return *gptr();
+ }
+ return EOF;
+}
+
+/**
+ * @class LLMemoryStreamBuf
+ */
+
+LLMemoryStream::LLMemoryStream(const U8* start, S32 length) :
+ std::istream(&mStreamBuf),
+ mStreamBuf(start, length)
+{
+}
+
+LLMemoryStream::~LLMemoryStream()
+{
+}
+
+
diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h
new file mode 100644
index 0000000000..7553c4a425
--- /dev/null
+++ b/indra/llcommon/llmemorystream.h
@@ -0,0 +1,63 @@
+/**
+ * @file llmemorystream.h
+ * @author Phoenix
+ * @date 2005-06-03
+ * @brief Implementation of a simple fixed memory stream
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMEMORYSTREAM_H
+#define LL_LLMEMORYSTREAM_H
+
+/**
+ * This is a simple but effective optimization when you want to treat
+ * a chunk of memory as an istream. I wrote this to avoid turing a
+ * buffer into a string, and then throwing the string into an
+ * iostringstream just to parse it into another datatype, eg, LLSD.
+ */
+
+#include <iostream>
+
+/**
+ * @class LLMemoryStreamBuf
+ * @brief This implements a wrapper around a piece of memory for istreams
+ *
+ * The memory passed in is NOT owned by an instance. The caller must
+ * be careful to always pass in a valid memory location that exists
+ * for at least as long as this streambuf.
+ */
+class LLMemoryStreamBuf : public std::streambuf
+{
+public:
+ LLMemoryStreamBuf(const U8* start, S32 length);
+ ~LLMemoryStreamBuf();
+
+ void reset(const U8* start, S32 length);
+
+protected:
+ int underflow();
+ //std::streamsize xsgetn(char* dest, std::streamsize n);
+};
+
+
+/**
+ * @class LLMemoryStream
+ * @brief This implements a wrapper around a piece of memory for istreams
+ *
+ * The memory passed in is NOT owned by an instance. The caller must
+ * be careful to always pass in a valid memory location that exists
+ * for at least as long as this streambuf.
+ */
+class LLMemoryStream : public std::istream
+{
+public:
+ LLMemoryStream(const U8* start, S32 length);
+ ~LLMemoryStream();
+
+protected:
+ LLMemoryStreamBuf mStreamBuf;
+};
+
+#endif // LL_LLMEMORYSTREAM_H
diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h
new file mode 100644
index 0000000000..17afaa6a8a
--- /dev/null
+++ b/indra/llcommon/llmemtype.h
@@ -0,0 +1,135 @@
+/**
+ * @file llmemtype.h
+ * @brief Runtime memory usage debugging utilities.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MEMTYPE_H
+#define LL_MEMTYPE_H
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+class LLMemType;
+
+extern void* ll_allocate (size_t size);
+extern void ll_release (void *p);
+
+#define MEM_TRACK_MEM 0
+#define MEM_TRACK_TYPE (1 && MEM_TRACK_MEM)
+
+#if MEM_TRACK_TYPE
+#define MEM_TYPE_NEW(T) \
+static void* operator new(size_t s) { LLMemType mt(T); return ll_allocate(s); } \
+static void operator delete(void* p) { ll_release(p); }
+
+#else
+#define MEM_TYPE_NEW(T)
+#endif // MEM_TRACK_TYPE
+
+
+//----------------------------------------------------------------------------
+
+class LLMemType
+{
+public:
+ // Also update sTypeDesc in llmemory.cpp
+ enum EMemType
+ {
+ MTYPE_INIT,
+ MTYPE_STARTUP,
+ MTYPE_MAIN,
+
+ MTYPE_IMAGEBASE,
+ MTYPE_IMAGERAW,
+ MTYPE_IMAGEFORMATTED,
+
+ MTYPE_APPFMTIMAGE,
+ MTYPE_APPRAWIMAGE,
+ MTYPE_APPAUXRAWIMAGE,
+
+ MTYPE_DRAWABLE,
+ MTYPE_OBJECT,
+ MTYPE_SPACE_PARTITION,
+ MTYPE_PIPELINE,
+ MTYPE_AVATAR,
+ MTYPE_PARTICLES,
+ MTYPE_REGIONS,
+ MTYPE_INVENTORY,
+ MTYPE_ANIMATION,
+ MTYPE_NETWORK,
+ MTYPE_PHYSICS,
+ MTYPE_INTERESTLIST,
+
+ MTYPE_SCRIPT,
+ MTYPE_SCRIPT_RUN,
+ MTYPE_SCRIPT_BYTECODE,
+
+ MTYPE_IO_PUMP,
+ MTYPE_IO_TCP,
+ MTYPE_IO_BUFFER,
+ MTYPE_IO_HTTP_SERVER,
+ MTYPE_IO_SD_SERVER,
+ MTYPE_IO_SD_CLIENT,
+ MTYPE_IO_URL_REQUEST,
+
+ MTYPE_TEMP1,
+ MTYPE_TEMP2,
+ MTYPE_TEMP3,
+ MTYPE_TEMP4,
+ MTYPE_TEMP5,
+ MTYPE_TEMP6,
+ MTYPE_TEMP7,
+ MTYPE_TEMP8,
+ MTYPE_TEMP9,
+
+ MTYPE_OTHER, // Special, used by display code
+
+ MTYPE_NUM_TYPES
+ };
+ enum { MTYPE_MAX_DEPTH = 64 };
+
+public:
+ LLMemType(EMemType type)
+ {
+#if MEM_TRACK_TYPE
+ if (type < 0 || type >= MTYPE_NUM_TYPES)
+ llerrs << "LLMemType error" << llendl;
+ if (sCurDepth < 0 || sCurDepth >= MTYPE_MAX_DEPTH)
+ llerrs << "LLMemType error" << llendl;
+ sType[sCurDepth] = sCurType;
+ sCurDepth++;
+ sCurType = type;
+#endif
+ }
+ ~LLMemType()
+ {
+#if MEM_TRACK_TYPE
+ sCurDepth--;
+ sCurType = sType[sCurDepth];
+#endif
+ }
+
+ static void reset();
+ static void printMem();
+
+public:
+#if MEM_TRACK_TYPE
+ static S32 sCurDepth;
+ static S32 sCurType;
+ static S32 sType[MTYPE_MAX_DEPTH];
+ static S32 sMemCount[MTYPE_NUM_TYPES];
+ static S32 sMaxMemCount[MTYPE_NUM_TYPES];
+ static S32 sOverheadMem;
+ static const char* sTypeDesc[MTYPE_NUM_TYPES];
+#endif
+ static S32 sTotalMem;
+ static S32 sMaxTotalMem;
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmortician.cpp b/indra/llcommon/llmortician.cpp
new file mode 100644
index 0000000000..eddfbb559e
--- /dev/null
+++ b/indra/llcommon/llmortician.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file llmortician.cpp
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llmortician.h"
+
+#include <list>
+
+std::list<LLMortician*> gGraveyard;
+
+BOOL LLMortician::sDestroyImmediate = FALSE;
+
+LLMortician::~LLMortician()
+{
+ gGraveyard.remove(this);
+}
+
+void LLMortician::updateClass()
+{
+ while (!gGraveyard.empty())
+ {
+ LLMortician* dead = gGraveyard.front();
+ delete dead;
+ }
+}
+
+void LLMortician::die()
+{
+ // It is valid to call die() more than once on something that hasn't died yet
+ if (sDestroyImmediate)
+ {
+ //HACK: we need to do this to ensure destruction order on shutdown
+ mIsDead = TRUE;
+ delete this;
+ return;
+ }
+ else if (!mIsDead)
+ {
+ mIsDead = TRUE;
+ gGraveyard.push_back(this);
+ }
+}
+
+// static
+void LLMortician::setZealous(BOOL b)
+{
+ sDestroyImmediate = b;
+}
diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h
new file mode 100644
index 0000000000..6e69f1af0b
--- /dev/null
+++ b/indra/llcommon/llmortician.h
@@ -0,0 +1,33 @@
+/**
+ * @file llmortician.h
+ * @brief Base class for delayed deletions.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLMORTICIAN_H
+#define LLMORTICIAN_H
+
+#include "stdtypes.h"
+
+class LLMortician
+{
+public:
+ LLMortician() { mIsDead = FALSE; }
+ static void updateClass();
+ virtual ~LLMortician();
+ void die();
+ BOOL isDead() { return mIsDead; }
+
+ // sets destroy immediate true
+ static void setZealous(BOOL b);
+
+private:
+ static BOOL sDestroyImmediate;
+
+ BOOL mIsDead;
+};
+
+#endif
+
diff --git a/indra/llcommon/llnametable.h b/indra/llcommon/llnametable.h
new file mode 100644
index 0000000000..d05885402d
--- /dev/null
+++ b/indra/llcommon/llnametable.h
@@ -0,0 +1,87 @@
+/**
+ * @file llnametable.h
+ * @brief LLNameTable class is a table to associate pointers with string names
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMETABLE_H
+#define LL_LLNAMETABLE_H
+
+#include <map>
+
+#include "string_table.h"
+
+template <class DATA>
+class LLNameTable
+{
+public:
+ LLNameTable()
+ : mNameMap()
+ {
+ }
+
+ ~LLNameTable()
+ {
+ }
+
+ void addEntry(const std::string& name, DATA data)
+ {
+ addEntry(name.c_str(), data);
+ }
+
+ void addEntry(const char *name, DATA data)
+ {
+ char *tablename = gStringTable.addString(name);
+ mNameMap[tablename] = data;
+ }
+
+ BOOL checkName(const std::string& name) const
+ {
+ return checkName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ BOOL checkName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ return mNameMap.count(tablename) ? TRUE : FALSE;
+ }
+
+ DATA resolveName(const std::string& name) const
+ {
+ return resolveName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ DATA resolveName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ const_iter_t iter = mNameMap.find(tablename);
+ if (iter != mNameMap.end())
+ return iter->second;
+ else
+ return 0;
+ }
+
+ // O(N)! (currently only used in one place... (newsim/llstate.cpp))
+ const char *resolveData(const DATA &data) const
+ {
+ const_iter_t iter = mNameMap.begin();
+ const_iter_t end = mNameMap.end();
+ for (; iter != end; ++iter)
+ {
+ if (iter->second == data)
+ return iter->first;
+ }
+ return NULL;
+ }
+
+ typedef std::map<const char *, DATA> name_map_t;
+ typedef typename std::map<const char *,DATA>::iterator iter_t;
+ typedef typename std::map<const char *,DATA>::const_iterator const_iter_t;
+ name_map_t mNameMap;
+};
+
+#endif
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
new file mode 100644
index 0000000000..829ae0de21
--- /dev/null
+++ b/indra/llcommon/llpreprocessor.h
@@ -0,0 +1,99 @@
+/**
+ * @file llpreprocessor.h
+ * @brief This file should be included in all Linden Lab files and
+ * should only contain special preprocessor directives
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLPREPROCESSOR_H
+#define LLPREPROCESSOR_H
+
+// Figure out endianness of platform
+#ifdef LL_LINUX
+#define __ENABLE_WSTRING
+#include <endian.h>
+#endif // LL_LINUX
+
+#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)))
+#define LL_LITTLE_ENDIAN 1
+#else
+#define LL_BIG_ENDIAN 1
+#endif
+
+// Per-OS feature switches.
+
+#if LL_DARWIN
+ #define LL_QUICKTIME_ENABLED 1
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 1
+#elif LL_WINDOWS
+ #define LL_QUICKTIME_ENABLED 1
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 1
+#elif LL_LINUX
+ #define LL_QUICKTIME_ENABLED 0
+ #define LL_MOZILLA_ENABLED 0
+ #define LL_LIBXUL_ENABLED 0
+#endif
+
+#if LL_LIBXUL_ENABLED && !defined(MOZILLA_INTERNAL_API)
+ // Without this, nsTAString.h errors out with:
+ // "Cannot use internal string classes without MOZILLA_INTERNAL_API defined. Use the frozen header nsStringAPI.h instead."
+ // It might be worth our while to figure out if we can use the frozen apis at some point...
+ #define MOZILLA_INTERNAL_API 1
+#endif
+
+// Deal with minor differences on Unixy OSes.
+#if LL_DARWIN || LL_LINUX
+ #define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+
+ // Different name, same functionality.
+ #define stricmp strcasecmp
+ #define strnicmp strncasecmp
+
+ // Not sure why this is different, but...
+ #ifndef MAX_PATH
+ #define MAX_PATH PATH_MAX
+ #endif // not MAX_PATH
+
+#endif
+
+// Deal with the differeneces on Windows
+#if defined(LL_WINDOWS)
+#define snprintf _snprintf
+#endif // LL_WINDOWS
+
+// Static linking with apr on windows needs to be declared.
+#ifdef LL_WINDOWS
+#ifndef APR_DECLARE_STATIC
+#define APR_DECLARE_STATIC // For APR on Windows
+#endif
+#ifndef APU_DECLARE_STATIC
+#define APU_DECLARE_STATIC // For APR util on Windows
+#endif
+#endif
+
+#if defined(LL_WINDOWS)
+#define BOOST_REGEX_NO_LIB 1
+#define CURL_STATICLIB 1
+#endif // LL_WINDOWS
+
+
+// Deal with VC6 problems
+#if defined(LL_WINDOWS)
+#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4.
+//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
+#pragma warning( disable : 4786 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation.
+#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
+#endif // LL_WINDOWS
+
+#endif // not LL_LINDEN_PREPROCESSOR_H
diff --git a/indra/llcommon/llpriqueuemap.h b/indra/llcommon/llpriqueuemap.h
new file mode 100644
index 0000000000..68fad0d6df
--- /dev/null
+++ b/indra/llcommon/llpriqueuemap.h
@@ -0,0 +1,127 @@
+/**
+ * @file llpriqueuemap.h
+ * @brief Priority queue implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLPRIQUEUEMAP_H
+#define LL_LLPRIQUEUEMAP_H
+
+#include <map>
+
+//
+// Priority queue, implemented under the hood as a
+// map. Needs to be done this way because none of the
+// standard STL containers provide a representation
+// where it's easy to reprioritize.
+//
+
+template <class DATA>
+class LLPQMKey
+{
+public:
+ LLPQMKey(const F32 priority, DATA data) : mPriority(priority), mData(data)
+ {
+ }
+
+ bool operator<(const LLPQMKey &b) const
+ {
+ if (mPriority > b.mPriority)
+ {
+ return TRUE;
+ }
+ if (mPriority < b.mPriority)
+ {
+ return FALSE;
+ }
+ if (mData > b.mData)
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ F32 mPriority;
+ DATA mData;
+};
+
+template <class DATA_TYPE>
+class LLPriQueueMap
+{
+public:
+ typedef typename std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE>::iterator pqm_iter;
+ typedef std::pair<LLPQMKey<DATA_TYPE>, DATA_TYPE> pqm_pair;
+ typedef void (*set_pri_fn)(DATA_TYPE &data, const F32 priority);
+ typedef F32 (*get_pri_fn)(DATA_TYPE &data);
+
+
+ LLPriQueueMap(set_pri_fn set_pri, get_pri_fn get_pri) : mSetPriority(set_pri), mGetPriority(get_pri)
+ {
+ }
+
+ void push(const F32 priority, DATA_TYPE data)
+ {
+#ifdef _DEBUG
+ pqm_iter iter = mMap.find(LLPQMKey<DATA_TYPE>(priority, data));
+ if (iter != mMap.end())
+ {
+ llerrs << "Pushing already existing data onto queue!" << llendl;
+ }
+#endif
+ mMap.insert(pqm_pair(LLPQMKey<DATA_TYPE>(priority, data), data));
+ }
+
+ BOOL pop(DATA_TYPE *datap)
+ {
+ pqm_iter iter;
+ iter = mMap.begin();
+ if (iter == mMap.end())
+ {
+ return FALSE;
+ }
+ *datap = (*(iter)).second;
+ mMap.erase(iter);
+
+ return TRUE;
+ }
+
+ void reprioritize(const F32 new_priority, DATA_TYPE data)
+ {
+ pqm_iter iter;
+ F32 cur_priority = mGetPriority(data);
+ LLPQMKey<DATA_TYPE> cur_key(cur_priority, data);
+ iter = mMap.find(cur_key);
+ if (iter == mMap.end())
+ {
+ llwarns << "Data not on priority queue!" << llendl;
+ // OK, try iterating through all of the data and seeing if we just screwed up the priority
+ // somehow.
+ for (iter = mMap.begin(); iter != mMap.end(); iter++)
+ {
+ if ((*(iter)).second == data)
+ {
+ llerrs << "Data on priority queue but priority not matched!" << llendl;
+ }
+ }
+ return;
+ }
+
+ mMap.erase(iter);
+ mSetPriority(data, new_priority);
+ push(new_priority, data);
+ }
+
+ S32 getLength() const
+ {
+ return (S32)mMap.size();
+ }
+
+ // Hack: public for use by the transfer manager, ugh.
+ std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE> mMap;
+protected:
+ void (*mSetPriority)(DATA_TYPE &data, const F32 priority);
+ F32 (*mGetPriority)(DATA_TYPE &data);
+};
+
+#endif // LL_LLPRIQUEUEMAP_H
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
new file mode 100644
index 0000000000..04e1bc0839
--- /dev/null
+++ b/indra/llcommon/llprocessor.cpp
@@ -0,0 +1,2125 @@
+/**
+ * @file llprocessor.cpp
+ * @brief Code to figure out the processor. Originally by Benjamin Jurke.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Filename: Processor.cpp
+// =======================
+// Author: Benjamin Jurke
+// File history: 27.02.2002 - File created. Support for Intel and AMD processors
+// 05.03.2002 - Fixed the CPUID bug: On Pre-Pentium CPUs the CPUID
+// command is not available
+// - The CProcessor::WriteInfoTextFile function do not
+// longer use Win32 file functions (-> os independend)
+// - Optional include of the windows.h header which is
+// still need for CProcessor::GetCPUFrequency.
+// 06.03.2002 - My birthday (18th :-))
+// - Replaced the '\r\n' line endings in function
+// CProcessor::CPUInfoToText by '\n'
+// - Replaced unsigned __int64 by signed __int64 for
+// solving some compiler conversion problems
+// - Fixed a bug at family=6, model=6 (Celeron -> P2)
+//////////////////////////////////////////////////////////////////////////////////
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <memory.h>
+#include "processor.h"
+
+#if !LL_DARWIN
+
+#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+// We need the QueryPerformanceCounter and Sleep functions
+#define FORCEINLINE __forceinline
+#else
+#define FORCEINLINE
+#endif
+
+
+// Some macros we often need
+////////////////////////////
+#define CheckBit(var, bit) ((var & (1 << bit)) ? true : false)
+
+#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+// Delays for the specified amount of milliseconds
+static void _Delay(unsigned int ms)
+{
+ LARGE_INTEGER freq, c1, c2;
+ __int64 x;
+
+ // Get High-Res Timer frequency
+ if (!QueryPerformanceFrequency(&freq))
+ return;
+
+ // Convert ms to High-Res Timer value
+ x = freq.QuadPart/1000*ms;
+
+ // Get first snapshot of High-Res Timer value
+ QueryPerformanceCounter(&c1);
+ do
+ {
+ // Get second snapshot
+ QueryPerformanceCounter(&c2);
+ }while(c2.QuadPart-c1.QuadPart < x);
+ // Loop while (second-first < x)
+}
+#endif
+
+// CProcessor::CProcessor
+// ======================
+// Class constructor:
+/////////////////////////
+CProcessor::CProcessor()
+{
+ uqwFrequency = 0;
+ memset(&CPUInfo, 0, sizeof(CPUInfo));
+}
+
+// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+// =========================================================================
+// Function to measure the current CPU frequency
+////////////////////////////////////////////////////////////////////////////
+F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+{
+#ifndef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+ return 0;
+#else
+ // If there are invalid measure time parameters, zero msecs for example,
+ // we've to exit the function
+ if (uiMeasureMSecs < 1)
+ {
+ // If theres already a measured frequency available, we return it
+ if (uqwFrequency > 0)
+ return uqwFrequency;
+ else
+ return 0;
+ }
+
+ // Now we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return 0;
+
+ // First we get the CPUID standard level 0x00000001
+ unsigned long reg;
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov reg, edx
+ }
+
+ // Then we check, if the RDTSC (Real Date Time Stamp Counter) is available.
+ // This function is necessary for our measure process.
+ if (!(reg & (1 << 4)))
+ return 0;
+
+ // After that we declare some vars and check the frequency of the high
+ // resolution timer for the measure process.
+ // If there's no high-res timer, we exit.
+ __int64 starttime, endtime, timedif, freq, start, end, dif;
+ if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq))
+ return 0;
+
+ // Now we can init the measure process. We set the process and thread priority
+ // to the highest available level (Realtime priority). Also we focus the
+ // first processor in the multiprocessor system.
+ HANDLE hProcess = GetCurrentProcess();
+ HANDLE hThread = GetCurrentThread();
+ unsigned long dwCurPriorityClass = GetPriorityClass(hProcess);
+ int iCurThreadPriority = GetThreadPriority(hThread);
+ unsigned long dwProcessMask, dwSystemMask, dwNewMask = 1;
+ GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask);
+
+ SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS);
+ SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL);
+ SetProcessAffinityMask(hProcess, dwNewMask);
+
+ // Now we call a CPUID to ensure, that all other prior called functions are
+ // completed now (serialization)
+ __asm cpuid
+
+ // We ask the high-res timer for the start time
+ QueryPerformanceCounter((LARGE_INTEGER *) &starttime);
+
+ // Then we get the current cpu clock and store it
+ __asm
+ {
+ rdtsc
+ mov dword ptr [start+4], edx
+ mov dword ptr [start], eax
+ }
+
+ // Now we wart for some msecs
+ _Delay(uiMeasureMSecs);
+// Sleep(uiMeasureMSecs);
+
+ // We ask for the end time
+ QueryPerformanceCounter((LARGE_INTEGER *) &endtime);
+
+ // And also for the end cpu clock
+ __asm
+ {
+ rdtsc
+ mov dword ptr [end+4], edx
+ mov dword ptr [end], eax
+ }
+
+ // Now we can restore the default process and thread priorities
+ SetProcessAffinityMask(hProcess, dwProcessMask);
+ SetThreadPriority(hThread, iCurThreadPriority);
+ SetPriorityClass(hProcess, dwCurPriorityClass);
+
+ // Then we calculate the time and clock differences
+ dif = end - start;
+ timedif = endtime - starttime;
+
+ // And finally the frequency is the clock difference divided by the time
+ // difference.
+ uqwFrequency = (F64)dif / (((F64)timedif) / freq);
+
+ // At last we just return the frequency that is also stored in the call
+ // member var uqwFrequency
+ return uqwFrequency;
+#endif
+}
+
+// bool CProcessor::AnalyzeIntelProcessor()
+// ========================================
+// Private class function for analyzing an Intel processor
+//////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeIntelProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, edxreg;
+
+ // First we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return false;
+
+ // Now we get the CPUID standard level 0x00000001
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then get the cpu model, family, type, stepping and brand id by masking
+ // the eax and ebx register
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+ CPUInfo.uiBrandID = ebxreg & 0xF;
+
+ // Now we can translate the type number to a more understandable string format
+ switch (CPUInfo.uiType)
+ {
+ case 0: // Type = 0: Original OEM processor
+ strcpy(CPUInfo.strType, "Original OEM"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 1: // Type = 1: Overdrive processor
+ strcpy(CPUInfo.strType, "Overdrive"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 2: // Type = 2: Dual-capable processor
+ strcpy(CPUInfo.strType, "Dual-capable"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /* Flawfinder: ignore */
+ break;
+ case 3: // Type = 3: Reserved for future use
+ strcpy(CPUInfo.strType, "Reserved"); /* Flawfinder: ignore */
+ break;
+ default: // This should be never called, cause we just mask 2 bits --> [0..3]
+ strcpy(CPUInfo.strType, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Then we translate the brand id:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 0: // Brand id = 0: Brand id not supported on this processor
+ strcpy(CPUInfo.strBrandID, "Not supported"); /* Flawfinder: ignore */
+ break;
+ case 1: // Brand id = 1: Intel Celeron (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Celeron"); /* Flawfinder: ignore */
+ break;
+ case 2: // Brand id = 2: Intel Pentium III (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III"); /* Flawfinder: ignore */
+ break;
+ case 3: // Brand id = 3: Model dependent
+ if (CPUInfo.uiModel == 6) // If the cpu model is Celeron (well, I'm NOT SURE!!!)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Celeron"); /* Flawfinder: ignore */
+ else
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III Xeon"); /* Flawfinder: ignore */
+ break;
+ case 4: // Brand id = 4: Intel Pentium III Tualatin (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium III"); /* Flawfinder: ignore */
+ break;
+ case 6: // Brand id = 6: Intel Pentium III mobile (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium III mobile"); /* Flawfinder: ignore */
+ break;
+ case 7: // Brand id = 7: Intel Celeron mobile (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Celeron mobile"); /* Flawfinder: ignore */
+ break;
+ case 8: // Brand id = 8: Intel Pentium 4 Willamette (0.18 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 9: // Brand id = 9: Intel Pentium 4 Northwood (0.13 micron) processor
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 0xA: // Brand id = 0xA: Intel Pentium 4 Northwood (0.13 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4"); /* Flawfinder: ignore */
+ break; // No idea, where the difference to id=9 is
+ case 0xB: // Brand id = 0xB: Intel Pentium 4 Northwood Xeon (0.13 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.13 micron Intel Pentium 4 Xeon"); /* Flawfinder: ignore */
+ break;
+ case 0xE: // Brand id = 0xE: Intel Pentium 4 Willamette Xeon (0.18 micron processor)
+ strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium 4 Xeon"); /* Flawfinder: ignore */
+ break;
+ default: // Should be never called, but sure is sure
+ strcpy(CPUInfo.strBrandID, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Then we translate the cpu family
+ switch (CPUInfo.uiFamily)
+ {
+ case 3: // Family = 3: i386 (80386) processor family
+ strcpy(CPUInfo.strFamily, "Intel i386"); /* Flawfinder: ignore */
+ break;
+ case 4: // Family = 4: i486 (80486) processor family
+ strcpy(CPUInfo.strFamily, "Intel i486"); /* Flawfinder: ignore */
+ break;
+ case 5: // Family = 5: Pentium (80586) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium"); /* Flawfinder: ignore */
+ break;
+ case 6: // Family = 6: Pentium Pro (80686) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium Pro"); /* Flawfinder: ignore */
+ break;
+ case 15: // Family = 15: Extended family specific
+ // Masking the extended family
+ CPUInfo.uiExtendedFamily = (eaxreg >> 20) & 0xFF;
+ switch (CPUInfo.uiExtendedFamily)
+ {
+ case 0: // Family = 15, Ext. Family = 0: Pentium 4 (80786 ??) processor family
+ strcpy(CPUInfo.strFamily, "Intel Pentium 4"); /* Flawfinder: ignore */
+ break;
+ case 1: // Family = 15, Ext. Family = 1: McKinley (64-bit) processor family
+ strcpy(CPUInfo.strFamily, "Intel McKinley (IA-64)"); /* Flawfinder: ignore */
+ break;
+ default: // Sure is sure
+ strcpy(CPUInfo.strFamily, "Unknown Intel Pentium 4+"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // Failsave
+ strcpy(CPUInfo.strFamily, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we come to the big deal, the exact model name
+ switch (CPUInfo.uiFamily)
+ {
+ case 3: // i386 (80386) processor family
+ strcpy(CPUInfo.strModel, "Unknown Intel i386"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i386", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 4: // i486 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: i486 DX-25/33 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX-25/33"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX-25/33", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 1: // Model = 1: i486 DX-50 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX-50"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX-50", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: i486 SX processor model
+ strcpy(CPUInfo.strModel, "Intel i486 SX"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SX", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: i486 DX2 (with i487 numeric coprocessor) processor model
+ strcpy(CPUInfo.strModel, "Intel i486 487/DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX2 with i487 numeric coprocessor", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 4: // Model = 4: i486 SL processor model (never heard ?!?)
+ strcpy(CPUInfo.strModel, "Intel i486 SL"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SL", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 5: // Model = 5: i486 SX2 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 SX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 SX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: i486 write-back enhanced DX2 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 write-back enhanced DX2", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: i486 DX4 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: i486 write-back enhanced DX4 processor model
+ strcpy(CPUInfo.strModel, "Intel i486 write-back enhanced DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 DX4", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown Intel i486"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel i486 (Unknown model)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Pentium (80586) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium (P5 A-Step) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium (P5 A-Step)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium (P5 A-Step core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break; // Famous for the DIV bug, as far as I know
+ case 1: // Model = 1: Pentium 60/66 processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium 60/66 (P5)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 60/66 (P5 core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: Pentium 75-200 (P54C) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium 75-200 (P54C)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 75-200 (P54C core)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: Pentium overdrive for 486 systems processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium for 486 system (P24T Overdrive)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium for 486 (P24T overdrive core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 4: // Model = 4: Pentium MMX processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium MMX (P55C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 7: Pentium processor model (don't know difference to Model=2)
+ strcpy(CPUInfo.strModel, "Intel Pentium (P54C)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium (P54C core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 8: // Model = 8: Pentium MMX (0.25 micron) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium MMX (P55C), 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium MMX (P55C core), 0.25 micron", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium (Unknown P5-model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 6: // Pentium Pro (80686) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium Pro (P6 A-Step) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6 A-Step)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (P6 A-Step core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 1: // Model = 1: Pentium Pro
+ strcpy(CPUInfo.strModel, "Intel Pentium Pro (P6)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (P6 core)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 3: Pentium II (66 MHz FSB, I think) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium II Model 3, 0.28 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II (Model 3 core, 0.28 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 5: // Model = 5: Pentium II/Xeon/Celeron (0.25 micron) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium II Model 5/Xeon/Celeron, 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II/Xeon/Celeron (Model 5 core, 0.25 micron process)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 6: // Model = 6: Pentium II with internal L2 cache
+ strcpy(CPUInfo.strModel, "Intel Pentium II - internal L2 cache"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium II with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 7: Pentium III/Xeon (extern L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Pentium III Xeon - external L2 cache, 0.25 micron"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium III/Pentium III Xeon (0.25 micron process) with external L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 8: // Model = 8: Pentium III/Xeon/Celeron (256 KB on-die L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/
+ // We want to know it exactly:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 1: // Model = 8, Brand id = 1: Celeron (on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 2: // Model = 8, Brand id = 2: Pentium III (on-die L2 cache) processor model (my current cpu :-))
+ strncat(strCPUName, "Intel Pentium III (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 8, Brand id = 3: Pentium III Xeon (on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Pentium III Xeon (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // ...²
+ strncat(strCPUName, "Intel Pentium III core (unknown model, 0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 0xA: // Model = 0xA: Pentium III/Xeon/Celeron (1 or 2 MB on-die L2 cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.18 micron"); /*Flawfinder: ignore*/
+ // Exact detection:
+ switch (CPUInfo.uiBrandID)
+ {
+ case 1: // Model = 0xA, Brand id = 1: Celeron (1 or 2 MB on-die L2 cache (does it exist??)) processor model
+ strncat(strCPUName, "Intel Celeron (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 2: // Model = 0xA, Brand id = 2: Pentium III (1 or 2 MB on-die L2 cache (never seen...)) processor model
+ strncat(strCPUName, "Intel Pentium III (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 3: // Model = 0xA, Brand id = 3: Pentium III Xeon (1 or 2 MB on-die L2 cache) processor model
+ strncat(strCPUName, "Intel Pentium III Xeon (0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // Getting bored of this............
+ strncat(strCPUName, "Intel Pentium III core (unknown model, 0.18 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 0xB: // Model = 0xB: Pentium III/Xeon/Celeron (Tualatin core, on-die cache) processor model
+ strcpy(CPUInfo.strModel, "Intel Pentium III/Celeron/Pentium III Xeon - internal L2 cache, 0.13 micron"); /*Flawfinder: ignore*/
+ // Omniscient: ;-)
+ switch (CPUInfo.uiBrandID)
+ {
+ case 3: // Model = 0xB, Brand id = 3: Celeron (Tualatin core) processor model
+ strncat(strCPUName, "Intel Celeron (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 4: // Model = 0xB, Brand id = 4: Pentium III (Tualatin core) processor model
+ strncat(strCPUName, "Intel Pentium III (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ case 7: // Model = 0xB, Brand id = 7: Celeron mobile (Tualatin core) processor model
+ strncat(strCPUName, "Intel Celeron mobile (Tualatin core, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ default: // *bored*
+ strncat(strCPUName, "Intel Pentium III Tualatin core (unknown model, 0.13 micron process) with internal L2 cache", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ default: // *more bored*
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium Pro"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium Pro (Unknown model)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ break;
+ }
+ break;
+ case 15: // Extended processor family
+ // Masking the extended model
+ CPUInfo.uiExtendedModel = (eaxreg >> 16) & 0xFF;
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: Pentium 4 Willamette (A-Step) core
+ if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette (A-Step)"); /*Flawfinder: ignore*/
+ strncat(strCPUName, "Intel Pentium 4 Willamette (A-Step)", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
+ }
+ else // else Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon (A-Step)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon (A-Step)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ case 1: // Model = 1: Pentium 4 Willamette core
+ if ((CPUInfo.uiBrandID) == 8) // Brand id = 8: P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ else // else Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Willamette Xeon"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Willamette Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ case 2: // Model = 2: Pentium 4 Northwood core
+ if (((CPUInfo.uiBrandID) == 9) || ((CPUInfo.uiBrandID) == 0xA)) // P4 Willamette
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Northwood", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ else // Xeon
+ {
+ strcpy(CPUInfo.strModel, "Intel Pentium 4 Northwood Xeon"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 Northwood Xeon", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ }
+ break;
+ default: // Silly stupid never used failsave option
+ strcpy(CPUInfo.strModel, "Unknown Intel Pentium 4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel Pentium 4 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // *grmpf*
+ strcpy(CPUInfo.strModel, "Unknown Intel model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "Intel (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) - 1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // After the long processor model block we now come to the processors serial
+ // number.
+ // First of all we check if the processor supports the serial number
+ if (CPUInfo.MaxSupportedLevel >= 3)
+ {
+ // If it supports the serial number CPUID level 0x00000003 we read the data
+ unsigned long sig1, sig2, sig3;
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov sig1, eax
+ mov eax, 3
+ cpuid
+ mov sig2, ecx
+ mov sig3, edx
+ }
+ // Then we convert the data to a readable string
+ snprintf(
+ CPUInfo.strProcessorSerial,
+ sizeof(CPUInfo.strProcessorSerial),
+ "%04lX-%04lX-%04lX-%04lX-%04lX-%04lX",
+ sig1 >> 16,
+ sig1 & 0xFFFF,
+ sig3 >> 16,
+ sig3 & 0xFFFF,
+ sig2 >> 16, sig2 & 0xFFFF); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // If there's no serial number support we just put "No serial number"
+ snprintf(
+ CPUInfo.strProcessorSerial,
+ sizeof(CPUInfo.strProcessorSerial),
+ "No Processor Serial Number"); /* Flawfinder: ignore */
+ }
+
+ // Now we get the standard processor extensions
+ GetStandardProcessorExtensions();
+
+ // And finally the processor configuration (caches, TLBs, ...) and translate
+ // the data to readable strings
+ GetStandardProcessorConfiguration();
+ TranslateProcessorConfiguration();
+
+ // At last...
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::AnalyzeAMDProcessor()
+// ======================================
+// Private class function for analyzing an AMD processor
+////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeAMDProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // First of all we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return 0;
+
+ // Now we get the CPUID standard level 0x00000001
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then we mask the model, family, stepping and type (AMD does not support brand id)
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+
+ // After that, we translate the processor type (see CProcessor::AnalyzeIntelProcessor()
+ // for further comments on this)
+ switch (CPUInfo.uiType)
+ {
+ case 0:
+ strcpy(CPUInfo.strType, "Original OEM"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 1:
+ strcpy(CPUInfo.strType, "Overdrive"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 2:
+ strcpy(CPUInfo.strType, "Dual-capable"); /* Flawfinder: ignore */
+ strcpy(strCPUName, CPUInfo.strType); /* Flawfinder: ignore */
+ strcat(strCPUName, " "); /*Flawfinder: ignore*/
+ break;
+ case 3:
+ strcpy(CPUInfo.strType, "Reserved"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strType, "Unknown"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we check if the processor supports the brand id string extended CPUID level
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000004)
+ {
+ // If it supports the extended CPUID level 0x80000004 we read the data
+ char tmp[52]; /* Flawfinder: ignore */
+ memset(tmp, 0, sizeof(tmp));
+ __asm
+ {
+ mov eax, 0x80000002
+ cpuid
+ mov dword ptr [tmp], eax
+ mov dword ptr [tmp+4], ebx
+ mov dword ptr [tmp+8], ecx
+ mov dword ptr [tmp+12], edx
+ mov eax, 0x80000003
+ cpuid
+ mov dword ptr [tmp+16], eax
+ mov dword ptr [tmp+20], ebx
+ mov dword ptr [tmp+24], ecx
+ mov dword ptr [tmp+28], edx
+ mov eax, 0x80000004
+ cpuid
+ mov dword ptr [tmp+32], eax
+ mov dword ptr [tmp+36], ebx
+ mov dword ptr [tmp+40], ecx
+ mov dword ptr [tmp+44], edx
+ }
+ // And copy it to the brand id string
+ strncpy(CPUInfo.strBrandID, tmp,sizeof(CPUInfo.strBrandID-1)); /* Flawfinder: ignore */
+ CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID-1)]='\0';
+ }
+ else
+ {
+ // Or just tell there is no brand id string support
+ strcpy(CPUInfo.strBrandID, "Not supported"); /* Flawfinder: ignore */
+ }
+
+ // After that we translate the processor family
+ switch(CPUInfo.uiFamily)
+ {
+ case 4: // Family = 4: 486 (80486) or 5x86 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 3: // Thanks to AMD for this nice form of family
+ case 7: // detection.... *grmpf*
+ case 8:
+ case 9:
+ strcpy(CPUInfo.strFamily, "AMD 80486"); /* Flawfinder: ignore */
+ break;
+ case 0xE:
+ case 0xF:
+ strcpy(CPUInfo.strFamily, "AMD 5x86"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Family = 5: K5 or K6 processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ strcpy(CPUInfo.strFamily, "AMD K5"); /* Flawfinder: ignore */
+ break;
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ strcpy(CPUInfo.strFamily, "AMD K6"); /* Flawfinder: ignore */
+ break;
+ default:
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 6: // Family = 6: K7 (Athlon, ...) processor family
+ strcpy(CPUInfo.strFamily, "AMD K7"); /* Flawfinder: ignore */
+ break;
+ default: // For security
+ strcpy(CPUInfo.strFamily, "Unknown family"); /* Flawfinder: ignore */
+ break;
+ }
+
+ // After the family detection we come to the specific processor model
+ // detection
+ switch (CPUInfo.uiFamily)
+ {
+ case 4: // Family = 4: 486 (80486) or 5x85 (80486) processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 3: // Model = 3: 80486 DX2
+ strcpy(CPUInfo.strModel, "AMD 80486 DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: 80486 write-back enhanced DX2
+ strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 write-back enhanced DX2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: 80486 DX4
+ strcpy(CPUInfo.strModel, "AMD 80486 DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: 80486 write-back enhanced DX4
+ strcpy(CPUInfo.strModel, "AMD 80486 write-back enhanced DX4"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 write-back enhanced DX4", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xE: // Model = 0xE: 5x86
+ strcpy(CPUInfo.strModel, "AMD 5x86"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 5x86", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xF: // Model = 0xF: 5x86 write-back enhanced (oh my god.....)
+ strcpy(CPUInfo.strModel, "AMD 5x86 write-back enhanced"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 5x86 write-back enhanced", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD 80486 or 5x86 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD 80486 or 5x86 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 5: // Family = 5: K5 / K6 processor family
+ switch (CPUInfo.uiModel)
+ {
+ case 0: // Model = 0: K5 SSA 5 (Pentium Rating *ggg* 75, 90 and 100 Mhz)
+ strcpy(CPUInfo.strModel, "AMD K5 SSA5 (PR75, PR90, PR100)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 SSA5 (PR75, PR90, PR100)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 1: // Model = 1: K5 5k86 (PR 120 and 133 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR120, PR133)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR120, PR133)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 2: // Model = 2: K5 5k86 (PR 166 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR166)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR166)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 3: // Model = 3: K5 5k86 (PR 200 MHz)
+ strcpy(CPUInfo.strModel, "AMD K5 5k86 (PR200)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 5k86 (PR200)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 6: // Model = 6: K6
+ strcpy(CPUInfo.strModel, "AMD K6 (0.30 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6 (0.30 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Model = 7: K6 (0.25 micron)
+ strcpy(CPUInfo.strModel, "AMD K6 (0.25 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6 (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 8: // Model = 8: K6-2
+ strcpy(CPUInfo.strModel, "AMD K6-2"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-2", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 9: // Model = 9: K6-III
+ strcpy(CPUInfo.strModel, "AMD K6-III"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-III", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 0xD: // Model = 0xD: K6-2+ / K6-III+
+ strcpy(CPUInfo.strModel, "AMD K6-2+ or K6-III+ (0.18 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K6-2+ or K6-III+ (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1);
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD K5 or K6 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K5 or K6 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ case 6: // Family = 6: K7 processor family (AMDs first good processors)
+ switch (CPUInfo.uiModel)
+ {
+ case 1: // Athlon
+ strcpy(CPUInfo.strModel, "AMD Athlon (0.25 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (0.25 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 2: // Athlon (0.18 micron)
+ strcpy(CPUInfo.strModel, "AMD Athlon (0.18 micron)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (0.18 micron)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 3: // Duron (Spitfire core)
+ strcpy(CPUInfo.strModel, "AMD Duron (Spitfire)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Duron (Spitfire core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 4: // Athlon (Thunderbird core)
+ strcpy(CPUInfo.strModel, "AMD Athlon (Thunderbird)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon (Thunderbird core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 6: // Athlon MP / Mobile Athlon (Palomino core)
+ strcpy(CPUInfo.strModel, "AMD Athlon MP/Mobile Athlon (Palomino)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Athlon MP/Mobile Athlon (Palomino core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ case 7: // Mobile Duron (Morgan core)
+ strcpy(CPUInfo.strModel, "AMD Mobile Duron (Morgan)"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD Mobile Duron (Morgan core)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD K7 model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD K7 (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+ break;
+ default: // ...
+ strcpy(CPUInfo.strModel, "Unknown AMD model"); /* Flawfinder: ignore */
+ strncat(strCPUName, "AMD (Unknown model)", sizeof(strCPUName) - strlen(strCPUName) -1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // Now we read the standard processor extension that are stored in the same
+ // way the Intel standard extensions are
+ GetStandardProcessorExtensions();
+
+ // Then we check if theres an extended CPUID level support
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000001)
+ {
+ // If we can access the extended CPUID level 0x80000001 we get the
+ // edx register
+ __asm
+ {
+ mov eax, 0x80000001
+ cpuid
+ mov edxreg, edx
+ }
+
+ // Now we can mask some AMD specific cpu extensions
+ CPUInfo._Ext.EMMX_MultimediaExtensions = CheckBit(edxreg, 22);
+ CPUInfo._Ext.AA64_AMD64BitArchitecture = CheckBit(edxreg, 29);
+ CPUInfo._Ext._E3DNOW_InstructionExtensions = CheckBit(edxreg, 30);
+ CPUInfo._Ext._3DNOW_InstructionExtensions = CheckBit(edxreg, 31);
+ }
+
+ // After that we check if the processor supports the ext. CPUID level
+ // 0x80000006
+ if (CPUInfo.MaxSupportedExtendedLevel >= 0x80000006)
+ {
+ // If it's present, we read it out
+ __asm
+ {
+ mov eax, 0x80000005
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ mov edxreg, edx
+ }
+
+ // Then we mask the L1 Data TLB information
+ if ((ebxreg >> 16) && (eaxreg >> 16))
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF;
+ }
+ else if (eaxreg >> 16)
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (eaxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (eaxreg >> 16) & 0xFF;
+ }
+ else if (ebxreg >> 16)
+ {
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = (ebxreg >> 24) & 0xFF;
+ CPUInfo._Data.uiEntries = (ebxreg >> 16) & 0xFF;
+ }
+ if (CPUInfo._Data.uiAssociativeWays == 0xFF)
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+
+ // Now the L1 Instruction/Code TLB information
+ if ((ebxreg & 0xFFFF) && (eaxreg & 0xFFFF))
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = eaxreg & 0xFF;
+ }
+ else if (eaxreg & 0xFFFF)
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "2 MB / 4MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (eaxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = eaxreg & 0xFF;
+ }
+ else if (ebxreg & 0xFFFF)
+ {
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = (ebxreg >> 8) & 0xFF;
+ CPUInfo._Instruction.uiEntries = ebxreg & 0xFF;
+ }
+ if (CPUInfo._Instruction.uiAssociativeWays == 0xFF)
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+
+ // Then we read the L1 data cache information
+ if ((ecxreg >> 24) > 0)
+ {
+ CPUInfo._L1.Data.bPresent = true;
+ snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", ecxreg >> 24); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = (ecxreg >> 15) & 0xFF;
+ CPUInfo._L1.Data.uiLineSize = ecxreg & 0xFF;
+ }
+ // After that we read the L2 instruction/code cache information
+ if ((edxreg >> 24) > 0)
+ {
+ CPUInfo._L1.Instruction.bPresent = true;
+ snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", edxreg >> 24); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = (edxreg >> 15) & 0xFF;
+ CPUInfo._L1.Instruction.uiLineSize = edxreg & 0xFF;
+ }
+
+ // Note: I'm not absolutely sure that the L1 page size code (the
+ // 'if/else if/else if' structs above) really detects the real page
+ // size for the TLB. Somebody should check it....
+
+ // Now we read the ext. CPUID level 0x80000006
+ __asm
+ {
+ mov eax, 0x80000006
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ }
+
+ // We only mask the unified L2 cache masks (never heard of an
+ // L2 cache that is divided in data and code parts)
+ if (((ecxreg >> 12) & 0xF) > 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", ecxreg >> 16); /*Flawfinder: ignore*/
+ switch ((ecxreg >> 12) & 0xF)
+ {
+ case 1:
+ CPUInfo._L2.uiAssociativeWays = 1;
+ break;
+ case 2:
+ CPUInfo._L2.uiAssociativeWays = 2;
+ break;
+ case 4:
+ CPUInfo._L2.uiAssociativeWays = 4;
+ break;
+ case 6:
+ CPUInfo._L2.uiAssociativeWays = 8;
+ break;
+ case 8:
+ CPUInfo._L2.uiAssociativeWays = 16;
+ break;
+ case 0xF:
+ CPUInfo._L2.uiAssociativeWays = (unsigned int) -1;
+ break;
+ default:
+ CPUInfo._L2.uiAssociativeWays = 0;
+ break;
+ }
+ CPUInfo._L2.uiLineSize = ecxreg & 0xFF;
+ }
+ }
+ else
+ {
+ // If we could not detect the ext. CPUID level 0x80000006 we
+ // try to read the standard processor configuration.
+ GetStandardProcessorConfiguration();
+ }
+ // After reading we translate the configuration to strings
+ TranslateProcessorConfiguration();
+
+ // And finally exit
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::AnalyzeUnknownProcessor()
+// ==========================================
+// Private class function to analyze an unknown (No Intel or AMD) processor
+///////////////////////////////////////////////////////////////////////////
+bool CProcessor::AnalyzeUnknownProcessor()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg;
+
+ // We check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return false;
+
+ // First of all we read the standard CPUID level 0x00000001
+ // This level should be available on every x86-processor clone
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ }
+ // Then we mask the processor model, family, type and stepping
+ CPUInfo.uiStepping = eaxreg & 0xF;
+ CPUInfo.uiModel = (eaxreg >> 4) & 0xF;
+ CPUInfo.uiFamily = (eaxreg >> 8) & 0xF;
+ CPUInfo.uiType = (eaxreg >> 12) & 0x3;
+
+ // To have complete information we also mask the brand id
+ CPUInfo.uiBrandID = ebxreg & 0xF;
+
+ // Then we get the standard processor extensions
+ GetStandardProcessorExtensions();
+
+ // Now we mark everything we do not know as unknown
+ strcpy(strCPUName, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo._Data.strTLB, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._Instruction.strTLB, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo._Trace.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L1.Data.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L1.Instruction.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L2.strCache, "Unknown"); /*Flawfinder: ignore*/
+ strcpy(CPUInfo._L3.strCache, "Unknown"); /*Flawfinder: ignore*/
+
+ strcpy(CPUInfo.strProcessorSerial, "Unknown / Not supported"); /*Flawfinder: ignore*/
+
+ // For the family, model and brand id we can only print the numeric value
+ snprintf(CPUInfo.strBrandID, sizeof(CPUInfo.strBrandID), "Brand-ID number %d", CPUInfo.uiBrandID); /*Flawfinder: ignore*/
+ snprintf(CPUInfo.strFamily, sizeof(CPUInfo.strFamily), "Family number %d", CPUInfo.uiFamily); /*Flawfinder: ignore*/
+ snprintf(CPUInfo.strModel, sizeof(CPUInfo.strModel), "Model number %d", CPUInfo.uiModel); /*Flawfinder: ignore*/
+
+ // Nevertheless we can determine the processor type
+ switch (CPUInfo.uiType)
+ {
+ case 0:
+ strcpy(CPUInfo.strType, "Original OEM"); /*Flawfinder: ignore*/
+ break;
+ case 1:
+ strcpy(CPUInfo.strType, "Overdrive"); /*Flawfinder: ignore*/
+ break;
+ case 2:
+ strcpy(CPUInfo.strType, "Dual-capable"); /*Flawfinder: ignore*/
+ break;
+ case 3:
+ strcpy(CPUInfo.strType, "Reserved"); /*Flawfinder: ignore*/
+ break;
+ default:
+ strcpy(CPUInfo.strType, "Unknown"); /*Flawfinder: ignore*/
+ break;
+ }
+
+ // And thats it
+ return true;
+#else
+ return FALSE;
+#endif
+}
+
+// bool CProcessor::CheckCPUIDPresence()
+// =====================================
+// This function checks if the CPUID command is available on the current
+// processor
+////////////////////////////////////////////////////////////////////////
+bool CProcessor::CheckCPUIDPresence()
+{
+#if LL_WINDOWS
+ unsigned long BitChanged;
+
+ // We've to check if we can toggle the flag register bit 21
+ // If we can't the processor does not support the CPUID command
+ __asm
+ {
+ pushfd
+ pop eax
+ mov ebx, eax
+ xor eax, 0x00200000
+ push eax
+ popfd
+ pushfd
+ pop eax
+ xor eax,ebx
+ mov BitChanged, eax
+ }
+
+ return ((BitChanged) ? true : false);
+#else
+ return FALSE;
+#endif
+}
+
+// void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
+// ===============================================================
+// This function (or switch ?!) just translates a one-byte processor configuration
+// byte to understandable values
+//////////////////////////////////////////////////////////////////////////////////
+void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
+{
+ // First we ensure that there's only one single byte
+ cfg &= 0xFF;
+
+ // Then we do a big switch
+ switch(cfg)
+ {
+ case 0: // cfg = 0: Unused
+ break;
+ case 0x1: // cfg = 0x1: code TLB present, 4 KB pages, 4 ways, 32 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = 4;
+ CPUInfo._Instruction.uiEntries = 32;
+ break;
+ case 0x2: // cfg = 0x2: code TLB present, 4 MB pages, fully associative, 2 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 MB"); /*Flawfinder: ignore*/
+ CPUInfo._Instruction.uiAssociativeWays = 4;
+ CPUInfo._Instruction.uiEntries = 2;
+ break;
+ case 0x3: // cfg = 0x3: data TLB present, 4 KB pages, 4 ways, 64 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = 4;
+ CPUInfo._Data.uiEntries = 64;
+ break;
+ case 0x4: // cfg = 0x4: data TLB present, 4 MB pages, 4 ways, 8 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 MB"); /*Flawfinder: ignore*/
+ CPUInfo._Data.uiAssociativeWays = 4;
+ CPUInfo._Data.uiEntries = 8;
+ break;
+ case 0x6: // cfg = 0x6: code L1 cache present, 8 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Instruction.bPresent = true;
+ strcpy(CPUInfo._L1.Instruction.strSize, "8 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = 4;
+ CPUInfo._L1.Instruction.uiLineSize = 32;
+ break;
+ case 0x8: // cfg = 0x8: code L1 cache present, 16 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Instruction.bPresent = true;
+ strcpy(CPUInfo._L1.Instruction.strSize, "16 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Instruction.uiAssociativeWays = 4;
+ CPUInfo._L1.Instruction.uiLineSize = 32;
+ break;
+ case 0xA: // cfg = 0xA: data L1 cache present, 8 KB, 2 ways, 32 byte lines
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = 2;
+ CPUInfo._L1.Data.uiLineSize = 32;
+ break;
+ case 0xC: // cfg = 0xC: data L1 cache present, 16 KB, 4 ways, 32 byte lines
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 32;
+ break;
+ case 0x22: // cfg = 0x22: code and data L3 cache present, 512 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "512 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 4;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x23: // cfg = 0x23: code and data L3 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "1024 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x25: // cfg = 0x25: code and data L3 cache present, 2048 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "2048 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x29: // cfg = 0x29: code and data L3 cache present, 4096 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L3.bPresent = true;
+ strcpy(CPUInfo._L3.strSize, "4096 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L3.uiAssociativeWays = 8;
+ CPUInfo._L3.uiLineSize = 64;
+ CPUInfo._L3.bSectored = true;
+ break;
+ case 0x40: // cfg = 0x40: no integrated L2 cache (P6 core) or L3 cache (P4 core)
+ break;
+ case 0x41: // cfg = 0x41: code and data L2 cache present, 128 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x42: // cfg = 0x42: code and data L2 cache present, 256 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /*Flawfinder: ignore*/
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x43: // cfg = 0x43: code and data L2 cache present, 512 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x44: // cfg = 0x44: code and data L2 cache present, 1024 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x45: // cfg = 0x45: code and data L2 cache present, 2048 KB, 4 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 4;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x50: // cfg = 0x50: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 64 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 64;
+ break;
+ case 0x51: // cfg = 0x51: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 128 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 128;
+ break;
+ case 0x52: // cfg = 0x52: code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 256 entries
+ CPUInfo._Instruction.bPresent = true;
+ strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Instruction.uiEntries = 256;
+ break;
+ case 0x5B: // cfg = 0x5B: data TLB present, 4 KB / 4 MB pages, fully associative, 64 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 64;
+ break;
+ case 0x5C: // cfg = 0x5C: data TLB present, 4 KB / 4 MB pages, fully associative, 128 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 128;
+ break;
+ case 0x5d: // cfg = 0x5D: data TLB present, 4 KB / 4 MB pages, fully associative, 256 entries
+ CPUInfo._Data.bPresent = true;
+ strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB"); /* Flawfinder: ignore */
+ CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
+ CPUInfo._Data.uiEntries = 256;
+ break;
+ case 0x66: // cfg = 0x66: data L1 cache present, 8 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "8 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x67: // cfg = 0x67: data L1 cache present, 16 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "16 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x68: // cfg = 0x68: data L1 cache present, 32 KB, 4 ways, 64 byte lines, sectored
+ CPUInfo._L1.Data.bPresent = true;
+ strcpy(CPUInfo._L1.Data.strSize, "32 KB"); /* Flawfinder: ignore */
+ CPUInfo._L1.Data.uiAssociativeWays = 4;
+ CPUInfo._L1.Data.uiLineSize = 64;
+ break;
+ case 0x70: // cfg = 0x70: trace L1 cache present, 12 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "12 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x71: // cfg = 0x71: trace L1 cache present, 16 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "16 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x72: // cfg = 0x72: trace L1 cache present, 32 KµOPs, 4 ways
+ CPUInfo._Trace.bPresent = true;
+ strcpy(CPUInfo._Trace.strSize, "32 K-micro-ops"); /* Flawfinder: ignore */
+ CPUInfo._Trace.uiAssociativeWays = 4;
+ break;
+ case 0x79: // cfg = 0x79: code and data L2 cache present, 128 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7A: // cfg = 0x7A: code and data L2 cache present, 256 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7B: // cfg = 0x7B: code and data L2 cache present, 512 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x7C: // cfg = 0x7C: code and data L2 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 64;
+ CPUInfo._L2.bSectored = true;
+ break;
+ case 0x81: // cfg = 0x81: code and data L2 cache present, 128 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "128 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x82: // cfg = 0x82: code and data L2 cache present, 256 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "256 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x83: // cfg = 0x83: code and data L2 cache present, 512 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "512 KB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x84: // cfg = 0x84: code and data L2 cache present, 1024 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "1 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ case 0x85: // cfg = 0x85: code and data L2 cache present, 2048 KB, 8 ways, 32 byte lines
+ CPUInfo._L2.bPresent = true;
+ strcpy(CPUInfo._L2.strSize, "2 MB"); /* Flawfinder: ignore */
+ CPUInfo._L2.uiAssociativeWays = 8;
+ CPUInfo._L2.uiLineSize = 32;
+ break;
+ }
+}
+
+FORCEINLINE static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
+{
+ // We define 0xFFFFFFFF (= -1) as fully associative
+ if (uiWays == ((unsigned int) -1))
+ strcpy(buf, "fully associative"); /* Flawfinder: ignore */
+ else
+ {
+ if (uiWays == 1) // A one way associative cache is just direct mapped
+ strcpy(buf, "direct mapped"); /* Flawfinder: ignore */
+ else if (uiWays == 0) // This should not happen...
+ strcpy(buf, "unknown associative ways"); /* Flawfinder: ignore */
+ else // The x-way associative cache
+ sprintf(buf, "%d ways associative", uiWays); /* Flawfinder: ignore */
+ }
+ // To ease the function use we return the buffer
+ return buf;
+}
+FORCEINLINE static void TranslateTLB(ProcessorTLB *tlb)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the TLB is present
+ if (tlb->bPresent)
+ snprintf(tlb->strTLB,sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries); /* Flawfinder: ignore */
+ else
+ strcpy(tlb->strTLB, "Not present"); /* Flawfinder: ignore */
+}
+FORCEINLINE static void TranslateCache(ProcessorCache *cache)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the cache is present
+ if (cache->bPresent)
+ {
+ // If present we construct the string
+ snprintf(cache->strCache, sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize); /* Flawfinder: ignore */
+ if (cache->bSectored)
+ strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // Else we just say "Not present"
+ strcpy(cache->strCache, "Not present"); /* Flawfinder: ignore */
+ }
+}
+
+// void CProcessor::TranslateProcessorConfiguration()
+// ==================================================
+// Private class function to translate the processor configuration values
+// to strings
+/////////////////////////////////////////////////////////////////////////
+void CProcessor::TranslateProcessorConfiguration()
+{
+ // We just call the small functions defined above
+ TranslateTLB(&CPUInfo._Data);
+ TranslateTLB(&CPUInfo._Instruction);
+
+ TranslateCache(&CPUInfo._Trace);
+
+ TranslateCache(&CPUInfo._L1.Instruction);
+ TranslateCache(&CPUInfo._L1.Data);
+ TranslateCache(&CPUInfo._L2);
+ TranslateCache(&CPUInfo._L3);
+}
+
+// void CProcessor::GetStandardProcessorConfiguration()
+// ====================================================
+// Private class function to read the standard processor configuration
+//////////////////////////////////////////////////////////////////////
+void CProcessor::GetStandardProcessorConfiguration()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // We check if the CPUID function is available
+ if (!CheckCPUIDPresence())
+ return;
+
+ // First we check if the processor supports the standard
+ // CPUID level 0x00000002
+ if (CPUInfo.MaxSupportedLevel >= 2)
+ {
+ // Now we go read the std. CPUID level 0x00000002 the first time
+ unsigned long count, num = 255;
+ for (count = 0; count < num; count++)
+ {
+ __asm
+ {
+ mov eax, 2
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov ecxreg, ecx
+ mov edxreg, edx
+ }
+ // We have to repeat this reading for 'num' times
+ num = eaxreg & 0xFF;
+
+ // Then we call the big decode switch function
+ DecodeProcessorConfiguration(eaxreg >> 8);
+ DecodeProcessorConfiguration(eaxreg >> 16);
+ DecodeProcessorConfiguration(eaxreg >> 24);
+
+ // If ebx contains additional data we also decode it
+ if ((ebxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(ebxreg);
+ DecodeProcessorConfiguration(ebxreg >> 8);
+ DecodeProcessorConfiguration(ebxreg >> 16);
+ DecodeProcessorConfiguration(ebxreg >> 24);
+ }
+ // And also the ecx register
+ if ((ecxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(ecxreg);
+ DecodeProcessorConfiguration(ecxreg >> 8);
+ DecodeProcessorConfiguration(ecxreg >> 16);
+ DecodeProcessorConfiguration(ecxreg >> 24);
+ }
+ // At last the edx processor register
+ if ((edxreg & 0x80000000) == 0)
+ {
+ DecodeProcessorConfiguration(edxreg);
+ DecodeProcessorConfiguration(edxreg >> 8);
+ DecodeProcessorConfiguration(edxreg >> 16);
+ DecodeProcessorConfiguration(edxreg >> 24);
+ }
+ }
+ }
+#endif
+}
+
+// void CProcessor::GetStandardProcessorExtensions()
+// =================================================
+// Private class function to read the standard processor extensions
+///////////////////////////////////////////////////////////////////
+void CProcessor::GetStandardProcessorExtensions()
+{
+#if LL_WINDOWS
+ unsigned long ebxreg, edxreg;
+
+ // We check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return;
+ // We just get the standard CPUID level 0x00000001 which should be
+ // available on every x86 processor
+ __asm
+ {
+ mov eax, 1
+ cpuid
+ mov ebxreg, ebx
+ mov edxreg, edx
+ }
+
+ // Then we mask some bits
+ CPUInfo._Ext.FPU_FloatingPointUnit = CheckBit(edxreg, 0);
+ CPUInfo._Ext.VME_Virtual8086ModeEnhancements = CheckBit(edxreg, 1);
+ CPUInfo._Ext.DE_DebuggingExtensions = CheckBit(edxreg, 2);
+ CPUInfo._Ext.PSE_PageSizeExtensions = CheckBit(edxreg, 3);
+ CPUInfo._Ext.TSC_TimeStampCounter = CheckBit(edxreg, 4);
+ CPUInfo._Ext.MSR_ModelSpecificRegisters = CheckBit(edxreg, 5);
+ CPUInfo._Ext.PAE_PhysicalAddressExtension = CheckBit(edxreg, 6);
+ CPUInfo._Ext.MCE_MachineCheckException = CheckBit(edxreg, 7);
+ CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = CheckBit(edxreg, 8);
+ CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController = CheckBit(edxreg, 9);
+ CPUInfo._Ext.APIC_ID = (ebxreg >> 24) & 0xFF;
+ CPUInfo._Ext.SEP_FastSystemCall = CheckBit(edxreg, 11);
+ CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters = CheckBit(edxreg, 12);
+ CPUInfo._Ext.PGE_PTE_GlobalFlag = CheckBit(edxreg, 13);
+ CPUInfo._Ext.MCA_MachineCheckArchitecture = CheckBit(edxreg, 14);
+ CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions = CheckBit(edxreg, 15);
+ CPUInfo._Ext.FGPAT_PageAttributeTable = CheckBit(edxreg, 16);
+ CPUInfo._Ext.PSE36_36bitPageSizeExtension = CheckBit(edxreg, 17);
+ CPUInfo._Ext.PN_ProcessorSerialNumber = CheckBit(edxreg, 18);
+ CPUInfo._Ext.CLFSH_CFLUSH_Instruction = CheckBit(edxreg, 19);
+ CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize = (ebxreg >> 8) & 0xFF;
+ CPUInfo._Ext.DS_DebugStore = CheckBit(edxreg, 21);
+ CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl = CheckBit(edxreg, 22);
+ CPUInfo._Ext.MMX_MultimediaExtensions = CheckBit(edxreg, 23);
+ CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = CheckBit(edxreg, 24);
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions = CheckBit(edxreg, 25);
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = CheckBit(edxreg, 26);
+ CPUInfo._Ext.SS_SelfSnoop = CheckBit(edxreg, 27);
+ CPUInfo._Ext.HT_HyperThreading = CheckBit(edxreg, 28);
+ CPUInfo._Ext.HT_HyterThreadingSiblings = (ebxreg >> 16) & 0xFF;
+ CPUInfo._Ext.TM_ThermalMonitor = CheckBit(edxreg, 29);
+ CPUInfo._Ext.IA64_Intel64BitArchitecture = CheckBit(edxreg, 30);
+#endif
+}
+
+// const ProcessorInfo *CProcessor::GetCPUInfo()
+// =============================================
+// Calls all the other detection function to create an detailed
+// processor information
+///////////////////////////////////////////////////////////////
+const ProcessorInfo *CProcessor::GetCPUInfo()
+{
+#if LL_WINDOWS
+ unsigned long eaxreg, ebxreg, ecxreg, edxreg;
+
+ // First of all we check if the CPUID command is available
+ if (!CheckCPUIDPresence())
+ return NULL;
+
+ // We read the standard CPUID level 0x00000000 which should
+ // be available on every x86 processor
+ __asm
+ {
+ mov eax, 0
+ cpuid
+ mov eaxreg, eax
+ mov ebxreg, ebx
+ mov edxreg, edx
+ mov ecxreg, ecx
+ }
+ // Then we connect the single register values to the vendor string
+ *((unsigned long *) CPUInfo.strVendor) = ebxreg;
+ *((unsigned long *) (CPUInfo.strVendor+4)) = edxreg;
+ *((unsigned long *) (CPUInfo.strVendor+8)) = ecxreg;
+
+ // We can also read the max. supported standard CPUID level
+ CPUInfo.MaxSupportedLevel = eaxreg & 0xFFFF;
+
+ // Then we read the ext. CPUID level 0x80000000
+ __asm
+ {
+ mov eax, 0x80000000
+ cpuid
+ mov eaxreg, eax
+ }
+ // ...to check the max. supportted extended CPUID level
+ CPUInfo.MaxSupportedExtendedLevel = eaxreg;
+
+ // Then we switch to the specific processor vendors
+ switch (ebxreg)
+ {
+ case 0x756E6547: // GenuineIntel
+ AnalyzeIntelProcessor();
+ break;
+ case 0x68747541: // AuthenticAMD
+ AnalyzeAMDProcessor();
+ break;
+ case 0x69727943: // CyrixInstead
+ // I really do not know anyone owning such a piece of crab
+ // So we analyze it as an unknown processor *ggggg*
+ default:
+ AnalyzeUnknownProcessor();
+ break;
+ }
+
+#endif
+ // After all we return the class CPUInfo member var
+ return (&CPUInfo);
+}
+
+#else
+// LL_DARWIN
+
+#include <mach/machine.h>
+#include <sys/sysctl.h>
+
+static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
+{
+ // We define 0xFFFFFFFF (= -1) as fully associative
+ if (uiWays == ((unsigned int) -1))
+ strcpy(buf, "fully associative"); /* Flawfinder: ignore */
+ else
+ {
+ if (uiWays == 1) // A one way associative cache is just direct mapped
+ strcpy(buf, "direct mapped"); /* Flawfinder: ignore */
+ else if (uiWays == 0) // This should not happen...
+ strcpy(buf, "unknown associative ways"); /* Flawfinder: ignore */
+ else // The x-way associative cache
+ sprintf(buf, "%d ways associative", uiWays); /* Flawfinder: ignore */
+ }
+ // To ease the function use we return the buffer
+ return buf;
+}
+static void TranslateTLB(ProcessorTLB *tlb)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the TLB is present
+ if (tlb->bPresent)
+ snprintf(tlb->strTLB, sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries); /* Flawfinder: ignore */
+ else
+ strcpy(tlb->strTLB, "Not present"); /* Flawfinder: ignore */
+}
+static void TranslateCache(ProcessorCache *cache)
+{
+ char buf[64]; /* Flawfinder: ignore */
+
+ // We just check if the cache is present
+ if (cache->bPresent)
+ {
+ // If present we construct the string
+ snprintf(cache->strCache,sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize); /* Flawfinder: ignore */
+ if (cache->bSectored)
+ strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ // Else we just say "Not present"
+ strcpy(cache->strCache, "Not present"); /* Flawfinder: ignore */
+ }
+}
+
+// void CProcessor::TranslateProcessorConfiguration()
+// ==================================================
+// Private class function to translate the processor configuration values
+// to strings
+/////////////////////////////////////////////////////////////////////////
+void CProcessor::TranslateProcessorConfiguration()
+{
+ // We just call the small functions defined above
+ TranslateTLB(&CPUInfo._Data);
+ TranslateTLB(&CPUInfo._Instruction);
+
+ TranslateCache(&CPUInfo._Trace);
+
+ TranslateCache(&CPUInfo._L1.Instruction);
+ TranslateCache(&CPUInfo._L1.Data);
+ TranslateCache(&CPUInfo._L2);
+ TranslateCache(&CPUInfo._L3);
+}
+
+// CProcessor::CProcessor
+// ======================
+// Class constructor:
+/////////////////////////
+CProcessor::CProcessor()
+{
+ uqwFrequency = 0;
+ memset(&CPUInfo, 0, sizeof(CPUInfo));
+}
+
+// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
+// =========================================================================
+// Function to query the current CPU frequency
+////////////////////////////////////////////////////////////////////////////
+F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/)
+{
+ U64 frequency = 0;
+ size_t len = sizeof(frequency);
+
+ if(sysctlbyname("hw.cpufrequency", &frequency, &len, NULL, 0) == 0)
+ {
+ uqwFrequency = (F64)frequency;
+ }
+
+ return uqwFrequency;
+}
+
+static bool hasFeature(const char *name)
+{
+ bool result = false;
+ int val = 0;
+ size_t len = sizeof(val);
+
+ if(sysctlbyname(name, &val, &len, NULL, 0) == 0)
+ {
+ if(val != 0)
+ result = true;
+ }
+
+ return result;
+}
+
+// const ProcessorInfo *CProcessor::GetCPUInfo()
+// =============================================
+// Calls all the other detection function to create an detailed
+// processor information
+///////////////////////////////////////////////////////////////
+const ProcessorInfo *CProcessor::GetCPUInfo()
+{
+ int pagesize = 0;
+ int cachelinesize = 0;
+ int l1icachesize = 0;
+ int l1dcachesize = 0;
+ int l2settings = 0;
+ int l2cachesize = 0;
+ int l3settings = 0;
+ int l3cachesize = 0;
+ int ncpu = 0;
+ int cpusubtype = 0;
+
+ // sysctl knows all.
+ int mib[2];
+ size_t len;
+ mib[0] = CTL_HW;
+
+ mib[1] = HW_PAGESIZE;
+ len = sizeof(pagesize);
+ sysctl(mib, 2, &pagesize, &len, NULL, 0);
+
+ mib[1] = HW_CACHELINE;
+ len = sizeof(cachelinesize);
+ sysctl(mib, 2, &cachelinesize, &len, NULL, 0);
+
+ mib[1] = HW_L1ICACHESIZE;
+ len = sizeof(l1icachesize);
+ sysctl(mib, 2, &l1icachesize, &len, NULL, 0);
+
+ mib[1] = HW_L1DCACHESIZE;
+ len = sizeof(l1dcachesize);
+ sysctl(mib, 2, &l1dcachesize, &len, NULL, 0);
+
+ mib[1] = HW_L2SETTINGS;
+ len = sizeof(l2settings);
+ sysctl(mib, 2, &l2settings, &len, NULL, 0);
+
+ mib[1] = HW_L2CACHESIZE;
+ len = sizeof(l2cachesize);
+ sysctl(mib, 2, &l2cachesize, &len, NULL, 0);
+
+ mib[1] = HW_L3SETTINGS;
+ len = sizeof(l3settings);
+ sysctl(mib, 2, &l3settings, &len, NULL, 0);
+
+ mib[1] = HW_L3CACHESIZE;
+ len = sizeof(l3cachesize);
+ sysctl(mib, 2, &l3cachesize, &len, NULL, 0);
+
+ mib[1] = HW_NCPU;
+ len = sizeof(ncpu);
+ sysctl(mib, 2, &ncpu, &len, NULL, 0);
+
+ sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0);
+
+ strCPUName[0] = 0;
+
+ if((ncpu == 0) || (ncpu == 1))
+ {
+ // Uhhh...
+ }
+ else if(ncpu == 2)
+ {
+ strncat(strCPUName, "Dual ", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(strCPUName, sizeof(strCPUName), "%d x ", ncpu); /* Flawfinder: ignore */
+ }
+
+#if __ppc__
+ switch(cpusubtype)
+ {
+ case CPU_SUBTYPE_POWERPC_601:// ((cpu_subtype_t) 1)
+ strncat(strCPUName, "PowerPC 601", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+
+ break;
+ case CPU_SUBTYPE_POWERPC_602:// ((cpu_subtype_t) 2)
+ strncat(strCPUName, "PowerPC 602", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603:// ((cpu_subtype_t) 3)
+ strncat(strCPUName, "PowerPC 603", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603e:// ((cpu_subtype_t) 4)
+ strncat(strCPUName, "PowerPC 603e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_603ev:// ((cpu_subtype_t) 5)
+ strncat(strCPUName, "PowerPC 603ev", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_604:// ((cpu_subtype_t) 6)
+ strncat(strCPUName, "PowerPC 604", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_604e:// ((cpu_subtype_t) 7)
+ strncat(strCPUName, "PowerPC 604e", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_620:// ((cpu_subtype_t) 8)
+ strncat(strCPUName, "PowerPC 620", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_750:// ((cpu_subtype_t) 9)
+ strncat(strCPUName, "PowerPC 750", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G3", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_7400:// ((cpu_subtype_t) 10)
+ strncat(strCPUName, "PowerPC 7400", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_7450:// ((cpu_subtype_t) 11)
+ strncat(strCPUName, "PowerPC 7450", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G4", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+ case CPU_SUBTYPE_POWERPC_970:// ((cpu_subtype_t) 100)
+ strncat(strCPUName, "PowerPC 970", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ strncat(CPUInfo.strFamily, "PowerPC G5", sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1); /* Flawfinder: ignore */
+ break;
+
+ default:
+ strncat(strCPUName, "PowerPC (Unknown)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+
+ // It's kinda like MMX or SSE...
+ CPUInfo._Ext.EMMX_MultimediaExtensions =
+ CPUInfo._Ext.MMX_MultimediaExtensions =
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions =
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.altivec");
+
+#endif
+
+#if __i386__
+ // MBW -- XXX -- TODO -- make this call AnalyzeIntelProcessor()?
+ switch(cpusubtype)
+ {
+ default:
+ strncat(strCPUName, "i386 (Unknown)", sizeof(strCPUName)-strlen(strCPUName)-1); /* Flawfinder: ignore */
+ break;
+ }
+
+ CPUInfo._Ext.EMMX_MultimediaExtensions = hasFeature("hw.optional.mmx"); // MBW -- XXX -- this may be wrong...
+ CPUInfo._Ext.MMX_MultimediaExtensions = hasFeature("hw.optional.mmx");
+ CPUInfo._Ext.SSE_StreamingSIMD_Extensions = hasFeature("hw.optional.sse");
+ CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = hasFeature("hw.optional.sse2");
+ CPUInfo._Ext.AA64_AMD64BitArchitecture = hasFeature("hw.optional.x86_64");
+
+#endif
+
+ // Terse CPU info uses this string...
+ strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1); /* Flawfinder: ignore */
+ CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
+
+ // Fun cache config stuff...
+
+ if(l1dcachesize != 0)
+ {
+ CPUInfo._L1.Data.bPresent = true;
+ snprintf(CPUInfo._L1.Data.strSize, sizeof(CPUInfo._L1.Data.strSize), "%d KB", l1dcachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L1.Data.uiAssociativeWays = ???;
+ CPUInfo._L1.Data.uiLineSize = cachelinesize;
+ }
+
+ if(l1icachesize != 0)
+ {
+ CPUInfo._L1.Instruction.bPresent = true;
+ snprintf(CPUInfo._L1.Instruction.strSize, sizeof(CPUInfo._L1.Instruction.strSize), "%d KB", l1icachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L1.Instruction.uiAssociativeWays = ???;
+ CPUInfo._L1.Instruction.uiLineSize = cachelinesize;
+ }
+
+ if(l2cachesize != 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l2cachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L2.uiAssociativeWays = ???;
+ CPUInfo._L2.uiLineSize = cachelinesize;
+ }
+
+ if(l3cachesize != 0)
+ {
+ CPUInfo._L2.bPresent = true;
+ snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", l3cachesize / 1024); /* Flawfinder: ignore */
+// CPUInfo._L2.uiAssociativeWays = ???;
+ CPUInfo._L2.uiLineSize = cachelinesize;
+ }
+
+ CPUInfo._Ext.FPU_FloatingPointUnit = hasFeature("hw.optional.floatingpoint");
+
+// printf("pagesize = 0x%x\n", pagesize);
+// printf("cachelinesize = 0x%x\n", cachelinesize);
+// printf("l1icachesize = 0x%x\n", l1icachesize);
+// printf("l1dcachesize = 0x%x\n", l1dcachesize);
+// printf("l2settings = 0x%x\n", l2settings);
+// printf("l2cachesize = 0x%x\n", l2cachesize);
+// printf("l3settings = 0x%x\n", l3settings);
+// printf("l3cachesize = 0x%x\n", l3cachesize);
+
+ // After reading we translate the configuration to strings
+ TranslateProcessorConfiguration();
+
+ // After all we return the class CPUInfo member var
+ return (&CPUInfo);
+}
+
+#endif // LL_DARWIN
+
+// bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen)
+// ======================================================================
+// Gets the frequency and processor information and writes it to a string
+/////////////////////////////////////////////////////////////////////////
+bool CProcessor::CPUInfoToText(char *strBuffer, unsigned int uiMaxLen)
+{
+#define LENCHECK len = (unsigned int) strlen(buf); if (len >= uiMaxLen) return false; strcpy(strBuffer, buf); strBuffer += len; /*Flawfinder: ignore*/
+#define COPYADD(str) strcpy(buf, str); LENCHECK; /* Flawfinder: ignore */
+#define FORMATADD(format, var) sprintf(buf, format, var); LENCHECK; /* Flawfinder: ignore */
+#define BOOLADD(str, boolvar) COPYADD(str); if (boolvar) { COPYADD(" Yes\n"); } else { COPYADD(" No\n"); }
+
+ char buf[1024]; /* Flawfinder: ignore */
+ unsigned int len;
+
+ // First we have to get the frequency
+ GetCPUFrequency(50);
+
+ // Then we get the processor information
+ GetCPUInfo();
+
+ // Now we construct the string (see the macros at function beginning)
+ strBuffer[0] = 0;
+
+ COPYADD("// CPU General Information\n//////////////////////////\n");
+ FORMATADD("Processor name: %s\n", strCPUName);
+ FORMATADD("Frequency: %.2f MHz\n\n", (float) uqwFrequency / 1000000.0f);
+ FORMATADD("Vendor: %s\n", CPUInfo.strVendor);
+ FORMATADD("Family: %s\n", CPUInfo.strFamily);
+ FORMATADD("Extended family: %d\n", CPUInfo.uiExtendedFamily);
+ FORMATADD("Model: %s\n", CPUInfo.strModel);
+ FORMATADD("Extended model: %d\n", CPUInfo.uiExtendedModel);
+ FORMATADD("Type: %s\n", CPUInfo.strType);
+ FORMATADD("Brand ID: %s\n", CPUInfo.strBrandID);
+ if (CPUInfo._Ext.PN_ProcessorSerialNumber)
+ {
+ FORMATADD("Processor Serial: %s\n", CPUInfo.strProcessorSerial);
+ }
+ else
+ {
+ COPYADD("Processor Serial: Disabled\n");
+ }
+
+ COPYADD("\n\n// CPU Configuration\n////////////////////\n");
+ FORMATADD("L1 instruction cache: %s\n", CPUInfo._L1.Instruction.strCache);
+ FORMATADD("L1 data cache: %s\n", CPUInfo._L1.Data.strCache);
+ FORMATADD("L2 cache: %s\n", CPUInfo._L2.strCache);
+ FORMATADD("L3 cache: %s\n", CPUInfo._L3.strCache);
+ FORMATADD("Trace cache: %s\n", CPUInfo._Trace.strCache);
+ FORMATADD("Instruction TLB: %s\n", CPUInfo._Instruction.strTLB);
+ FORMATADD("Data TLB: %s\n", CPUInfo._Data.strTLB);
+ FORMATADD("Max Supported CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedLevel);
+ FORMATADD("Max Supported Ext. CPUID-Level: 0x%08lX\n", CPUInfo.MaxSupportedExtendedLevel);
+
+ COPYADD("\n\n// CPU Extensions\n/////////////////\n");
+ BOOLADD("AA64 AMD 64-bit Architecture: ", CPUInfo._Ext.AA64_AMD64BitArchitecture);
+ BOOLADD("ACPI Thermal Monitor And Clock Control: ", CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl);
+ BOOLADD("APIC Advanced Programmable Interrupt Controller: ", CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController);
+ FORMATADD(" APIC-ID: %d\n", CPUInfo._Ext.APIC_ID);
+ BOOLADD("CLFSH CLFLUSH Instruction Presence: ", CPUInfo._Ext.CLFSH_CFLUSH_Instruction);
+ FORMATADD(" CLFLUSH Instruction Cache Line Size: %d\n", CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize);
+ BOOLADD("CMOV Conditional Move And Compare Instructions: ", CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions);
+ BOOLADD("CX8 COMPXCHG8B Instruction: ", CPUInfo._Ext.CX8_COMPXCHG8B_Instruction);
+ BOOLADD("DE Debugging Extensions: ", CPUInfo._Ext.DE_DebuggingExtensions);
+ BOOLADD("DS Debug Store: ", CPUInfo._Ext.DS_DebugStore);
+ BOOLADD("FGPAT Page Attribute Table: ", CPUInfo._Ext.FGPAT_PageAttributeTable);
+ BOOLADD("FPU Floating Point Unit: ", CPUInfo._Ext.FPU_FloatingPointUnit);
+ BOOLADD("FXSR Fast Streaming SIMD Extensions Save/Restore:", CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore);
+ BOOLADD("HT Hyper Threading: ", CPUInfo._Ext.HT_HyperThreading);
+ BOOLADD("IA64 Intel 64-Bit Architecture: ", CPUInfo._Ext.IA64_Intel64BitArchitecture);
+ BOOLADD("MCA Machine Check Architecture: ", CPUInfo._Ext.MCA_MachineCheckArchitecture);
+ BOOLADD("MCE Machine Check Exception: ", CPUInfo._Ext.MCE_MachineCheckException);
+ BOOLADD("MMX Multimedia Extensions: ", CPUInfo._Ext.MMX_MultimediaExtensions);
+ BOOLADD("MMX+ Multimedia Extensions: ", CPUInfo._Ext.EMMX_MultimediaExtensions);
+ BOOLADD("MSR Model Specific Registers: ", CPUInfo._Ext.MSR_ModelSpecificRegisters);
+ BOOLADD("MTRR Memory Type Range Registers: ", CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters);
+ BOOLADD("PAE Physical Address Extension: ", CPUInfo._Ext.PAE_PhysicalAddressExtension);
+ BOOLADD("PGE PTE Global Flag: ", CPUInfo._Ext.PGE_PTE_GlobalFlag);
+ if (CPUInfo._Ext.PN_ProcessorSerialNumber)
+ {
+ FORMATADD("PN Processor Serial Number: %s\n", CPUInfo.strProcessorSerial);
+ }
+ else
+ {
+ COPYADD("PN Processor Serial Number: Disabled\n");
+ }
+ BOOLADD("PSE Page Size Extensions: ", CPUInfo._Ext.PSE_PageSizeExtensions);
+ BOOLADD("PSE36 36-bit Page Size Extension: ", CPUInfo._Ext.PSE36_36bitPageSizeExtension);
+ BOOLADD("SEP Fast System Call: ", CPUInfo._Ext.SEP_FastSystemCall);
+ BOOLADD("SS Self Snoop: ", CPUInfo._Ext.SS_SelfSnoop);
+ BOOLADD("SSE Streaming SIMD Extensions: ", CPUInfo._Ext.SSE_StreamingSIMD_Extensions);
+ BOOLADD("SSE2 Streaming SIMD 2 Extensions: ", CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions);
+ BOOLADD("TM Thermal Monitor: ", CPUInfo._Ext.TM_ThermalMonitor);
+ BOOLADD("TSC Time Stamp Counter: ", CPUInfo._Ext.TSC_TimeStampCounter);
+ BOOLADD("VME Virtual 8086 Mode Enhancements: ", CPUInfo._Ext.VME_Virtual8086ModeEnhancements);
+ BOOLADD("3DNow! Instructions: ", CPUInfo._Ext._3DNOW_InstructionExtensions);
+ BOOLADD("Enhanced 3DNow! Instructions: ", CPUInfo._Ext._E3DNOW_InstructionExtensions);
+
+ // Yippie!!!
+ return true;
+}
+
+// bool CProcessor::WriteInfoTextFile(const char *strFilename)
+// ===========================================================
+// Takes use of CProcessor::CPUInfoToText and saves the string to a
+// file
+///////////////////////////////////////////////////////////////////
+bool CProcessor::WriteInfoTextFile(const char *strFilename)
+{
+ char buf[16384]; /* Flawfinder: ignore */
+
+ // First we get the string
+ if (!CPUInfoToText(buf, 16383))
+ return false;
+
+ // Then we create a new file (CREATE_ALWAYS)
+ FILE *file = LLFile::fopen(strFilename, "w"); /* Flawfinder: ignore */
+ if (!file)
+ return false;
+
+ // After that we write the string to the file
+ unsigned long dwBytesToWrite, dwBytesWritten;
+ dwBytesToWrite = (unsigned long) strlen(buf); /*Flawfinder: ignore*/
+ dwBytesWritten = (unsigned long) fwrite(buf, 1, dwBytesToWrite, file);
+ fclose(file);
+ if (dwBytesToWrite != dwBytesWritten)
+ return false;
+
+ // Done
+ return true;
+}
diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h
new file mode 100644
index 0000000000..ad44e2ccb3
--- /dev/null
+++ b/indra/llcommon/llprocessor.h
@@ -0,0 +1,158 @@
+/**
+ * @file llprocessor.h
+ * @brief Code to figure out the processor. Originally by Benjamin Jurke.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Author: Benjamin Jurke
+// File history: 27.02.2002 File created.
+///////////////////////////////////////////
+
+
+#ifndef PROCESSOR_H
+#define PROCESSOR_H
+
+// Options:
+///////////
+#if LL_WINDOWS
+#define PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
+#endif
+// Includes <windows.h> --> code gets os-dependend (Win32)
+
+
+typedef struct ProcessorExtensions
+{
+ bool FPU_FloatingPointUnit;
+ bool VME_Virtual8086ModeEnhancements;
+ bool DE_DebuggingExtensions;
+ bool PSE_PageSizeExtensions;
+ bool TSC_TimeStampCounter;
+ bool MSR_ModelSpecificRegisters;
+ bool PAE_PhysicalAddressExtension;
+ bool MCE_MachineCheckException;
+ bool CX8_COMPXCHG8B_Instruction;
+ bool APIC_AdvancedProgrammableInterruptController;
+ unsigned int APIC_ID;
+ bool SEP_FastSystemCall;
+ bool MTRR_MemoryTypeRangeRegisters;
+ bool PGE_PTE_GlobalFlag;
+ bool MCA_MachineCheckArchitecture;
+ bool CMOV_ConditionalMoveAndCompareInstructions;
+ bool FGPAT_PageAttributeTable;
+ bool PSE36_36bitPageSizeExtension;
+ bool PN_ProcessorSerialNumber;
+ bool CLFSH_CFLUSH_Instruction;
+ unsigned int CLFLUSH_InstructionCacheLineSize;
+ bool DS_DebugStore;
+ bool ACPI_ThermalMonitorAndClockControl;
+ bool EMMX_MultimediaExtensions;
+ bool MMX_MultimediaExtensions;
+ bool FXSR_FastStreamingSIMD_ExtensionsSaveRestore;
+ bool SSE_StreamingSIMD_Extensions;
+ bool SSE2_StreamingSIMD2_Extensions;
+ bool SS_SelfSnoop;
+ bool HT_HyperThreading;
+ unsigned int HT_HyterThreadingSiblings;
+ bool TM_ThermalMonitor;
+ bool IA64_Intel64BitArchitecture;
+ bool _3DNOW_InstructionExtensions;
+ bool _E3DNOW_InstructionExtensions;
+ bool AA64_AMD64BitArchitecture;
+} ProcessorExtensions;
+
+typedef struct ProcessorCache
+{
+ bool bPresent;
+ char strSize[32]; /* Flawfinder: ignore */
+ unsigned int uiAssociativeWays;
+ unsigned int uiLineSize;
+ bool bSectored;
+ char strCache[128]; /* Flawfinder: ignore */
+} ProcessorCache;
+
+typedef struct ProcessorL1Cache
+{
+ ProcessorCache Instruction;
+ ProcessorCache Data;
+} ProcessorL1Cache;
+
+typedef struct ProcessorTLB
+{
+ bool bPresent;
+ char strPageSize[32]; /* Flawfinder: ignore */
+ unsigned int uiAssociativeWays;
+ unsigned int uiEntries;
+ char strTLB[128]; /* Flawfinder: ignore */
+} ProcessorTLB;
+
+typedef struct ProcessorInfo
+{
+ char strVendor[16]; /* Flawfinder: ignore */
+ unsigned int uiFamily;
+ unsigned int uiExtendedFamily;
+ char strFamily[64]; /* Flawfinder: ignore */
+ unsigned int uiModel;
+ unsigned int uiExtendedModel;
+ char strModel[128]; /* Flawfinder: ignore */
+ unsigned int uiStepping;
+ unsigned int uiType;
+ char strType[64]; /* Flawfinder: ignore */
+ unsigned int uiBrandID;
+ char strBrandID[64]; /* Flawfinder: ignore */
+ char strProcessorSerial[64]; /* Flawfinder: ignore */
+ unsigned long MaxSupportedLevel;
+ unsigned long MaxSupportedExtendedLevel;
+ ProcessorExtensions _Ext;
+ ProcessorL1Cache _L1;
+ ProcessorCache _L2;
+ ProcessorCache _L3;
+ ProcessorCache _Trace;
+ ProcessorTLB _Instruction;
+ ProcessorTLB _Data;
+} ProcessorInfo;
+
+
+// CProcessor
+// ==========
+// Class for detecting the processor name, type and available
+// extensions as long as it's speed.
+/////////////////////////////////////////////////////////////
+class CProcessor
+{
+// Constructor / Destructor:
+////////////////////////////
+public:
+ CProcessor();
+
+// Private vars:
+////////////////
+public:
+ F64 uqwFrequency;
+ char strCPUName[128]; /* Flawfinder: ignore */
+ ProcessorInfo CPUInfo;
+
+// Private functions:
+/////////////////////
+private:
+ bool AnalyzeIntelProcessor();
+ bool AnalyzeAMDProcessor();
+ bool AnalyzeUnknownProcessor();
+ bool CheckCPUIDPresence();
+ void DecodeProcessorConfiguration(unsigned int cfg);
+ void TranslateProcessorConfiguration();
+ void GetStandardProcessorConfiguration();
+ void GetStandardProcessorExtensions();
+
+// Public functions:
+////////////////////
+public:
+ F64 GetCPUFrequency(unsigned int uiMeasureMSecs);
+ const ProcessorInfo *GetCPUInfo();
+ bool CPUInfoToText(char *strBuffer, unsigned int uiMaxLen);
+ bool WriteInfoTextFile(const char *strFilename);
+};
+
+
+#endif
diff --git a/indra/llcommon/llptrskiplist.h b/indra/llcommon/llptrskiplist.h
new file mode 100644
index 0000000000..fd4dcdf87b
--- /dev/null
+++ b/indra/llcommon/llptrskiplist.h
@@ -0,0 +1,704 @@
+/**
+ * @file llptrskiplist.h
+ * @brief Skip list implementation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPTRSKIPLIST_H
+#define LL_LLPTRSKIPLIST_H
+
+#include "llerror.h"
+//#include "vmath.h"
+
+/////////////////////////////////////////////
+//
+// LLPtrSkipList implementation - skip list for pointers to objects
+//
+
+template <class DATA_TYPE, S32 BINARY_DEPTH = 8>
+class LLPtrSkipList
+{
+public:
+ friend class LLPtrSkipNode;
+
+ // basic constructor
+ LLPtrSkipList();
+ // basic constructor including sorter
+ LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
+ BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second));
+ ~LLPtrSkipList();
+
+ inline void setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second));
+ inline void setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second));
+
+ inline BOOL addData(DATA_TYPE *data);
+
+ inline BOOL checkData(const DATA_TYPE *data);
+
+ inline S32 getLength(); // returns number of items in the list - NOT constant time!
+
+ inline BOOL removeData(const DATA_TYPE *data);
+
+ // note that b_sort is ignored
+ inline BOOL moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort);
+
+ inline BOOL moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort);
+
+ // resort -- use when the value we're sorting by changes
+ /* IW 12/6/02 - This doesn't work!
+ Instead, remove the data BEFORE you change it
+ Then re-insert it after you change it
+ BOOL resortData(DATA_TYPE *data)
+ */
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ inline BOOL deleteData(const DATA_TYPE *data);
+
+ // remove all nodes from the list and delete data
+ inline void deleteAllData();
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE *getNextData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // delete the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE *getFirstData();
+
+ // TRUE if nodes are not in sorted order
+ inline BOOL corrupt();
+
+protected:
+ class LLPtrSkipNode
+ {
+ public:
+ LLPtrSkipNode()
+ : mData(NULL)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ LLPtrSkipNode(DATA_TYPE *data)
+ : mData(data)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ ~LLPtrSkipNode()
+ {
+ if (mData)
+ {
+ llerror("Attempting to call LLPtrSkipNode destructor with a non-null mDatap!", 1);
+ }
+ }
+
+ // delete associated data and NULLs out pointer
+ void deleteData()
+ {
+ delete mData;
+ mData = NULL;
+ }
+
+ // NULLs out pointer
+ void removeData()
+ {
+ mData = NULL;
+ }
+
+ DATA_TYPE *mData;
+ LLPtrSkipNode *mForward[BINARY_DEPTH];
+ };
+
+ static BOOL defaultEquals(const DATA_TYPE *first, const DATA_TYPE *second)
+ {
+ return first == second;
+ }
+
+
+ LLPtrSkipNode mHead;
+ LLPtrSkipNode *mUpdate[BINARY_DEPTH];
+ LLPtrSkipNode *mCurrentp;
+ LLPtrSkipNode *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const DATA_TYPE *first, const DATA_TYPE *second);
+ BOOL (*mEquals)(const DATA_TYPE *first, const DATA_TYPE *second);
+};
+
+
+// basic constructor
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList()
+ : mInsertFirst(NULL), mEquals(defaultEquals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// basic constructor including sorter
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::LLPtrSkipList(BOOL (*insert_first)(DATA_TYPE *first, DATA_TYPE *second),
+ BOOL (*equals)(DATA_TYPE *first, DATA_TYPE *second))
+ :mInsertFirst(insert_first), mEquals(equals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::~LLPtrSkipList()
+{
+ removeAllNodes();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const DATA_TYPE *first, const DATA_TYPE *second))
+{
+ mInsertFirst = insert_first;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const DATA_TYPE *first, const DATA_TYPE *second))
+{
+ mEquals = equals;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::addData(DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipNode *snode = new LLPtrSkipNode(data);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return TRUE;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mData, data);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// returns number of items in the list
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ for (LLPtrSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+ return length;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ current->removeData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// note that b_sort is ignored
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE *data, LLPtrSkipList *newlist, BOOL b_sort)
+{
+ BOOL removed = removeData(data);
+ BOOL added = newlist->addData(data);
+ return removed && added;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::moveCurrentData(LLPtrSkipList *newlist, BOOL b_sort)
+{
+ if (mCurrentOperatingp)
+ {
+ mCurrentp = mCurrentOperatingp->mForward[0];
+ BOOL removed = removeData(mCurrentOperatingp);
+ BOOL added = newlist->addData(mCurrentOperatingp);
+ mCurrentOperatingp = mCurrentp;
+ return removed && added;
+ }
+ return FALSE;
+}
+
+// resort -- use when the value we're sorting by changes
+/* IW 12/6/02 - This doesn't work!
+ Instead, remove the data BEFORE you change it
+ Then re-insert it after you change it
+BOOL resortData(DATA_TYPE *data)
+{
+ removeData(data);
+ addData(data);
+}
+*/
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
+{
+ LLPtrSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteData(const DATA_TYPE *data)
+{
+ S32 level;
+ LLPtrSkipNode *current = &mHead;
+ LLPtrSkipNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = current->mForward[0];
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = current->mForward[0];
+ }
+
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ current->deleteData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// remove all nodes from the list and delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteAllData()
+{
+ LLPtrSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = *mCurrentp->mForward;
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = *mCurrentp->mForward;
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mData);
+ }
+}
+
+// delete the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mData);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE *LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return 0; // equivalent, but no warning
+ }
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipList<DATA_TYPE, BINARY_DEPTH>::corrupt()
+{
+ LLPtrSkipNode *previous = mHead.mForward[0];
+
+ // Empty lists are not corrupt.
+ if (!previous) return FALSE;
+
+ LLPtrSkipNode *current = previous->mForward[0];
+ while(current)
+ {
+ if (!mInsertFirst(previous->mData, current->mData))
+ {
+ // prev shouldn't be in front of cur!
+ return TRUE;
+ }
+ current = current->mForward[0];
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/indra/llcommon/llptrskipmap.h b/indra/llcommon/llptrskipmap.h
new file mode 100644
index 0000000000..63668c34fe
--- /dev/null
+++ b/indra/llcommon/llptrskipmap.h
@@ -0,0 +1,1219 @@
+/**
+ * @file llptrskipmap.h
+ * @brief Just like a LLSkipMap, but since it's pointers, you can call
+ * deleteAllData
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLPTRSKIPMAP_H
+#define LL_LLPTRSKIPMAP_H
+
+#include "llerror.h"
+#include "llrand.h"
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH = 8>
+class LLPtrSkipMapNode
+{
+public:
+ LLPtrSkipMapNode()
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mIndex;
+
+ for (i = 0; i < (S32)sizeof(INDEX_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+
+ zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLPtrSkipMapNode(const INDEX_T &index)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_T); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLPtrSkipMapNode(const INDEX_T &index, DATA_T datap)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ mData = datap;
+ }
+
+ ~LLPtrSkipMapNode()
+ {
+ }
+
+ // delete associated data and NULLs out pointer
+ void deleteData()
+ {
+ delete mData;
+ mData = 0;
+ }
+
+ // NULLs out pointer
+ void removeData()
+ {
+ mData = 0;
+ }
+
+ INDEX_T mIndex;
+ DATA_T mData;
+ LLPtrSkipMapNode *mForward[BINARY_DEPTH];
+
+private:
+ // Disallow copying of LLPtrSkipMapNodes by not implementing these methods.
+ LLPtrSkipMapNode(const LLPtrSkipMapNode &);
+ LLPtrSkipMapNode &operator=(const LLPtrSkipMapNode &rhs);
+};
+
+//---------------------------------------------------------------------------
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH = 8>
+class LLPtrSkipMap
+{
+public:
+ typedef BOOL (*compare)(const DATA_T& first, const DATA_T& second);
+ typedef compare insert_func;
+ typedef compare equals_func;
+
+ void init();
+
+ // basic constructor
+ LLPtrSkipMap();
+
+ // basic constructor including sorter
+ LLPtrSkipMap(insert_func insert_first, equals_func equals);
+
+ ~LLPtrSkipMap();
+
+ void setInsertFirst(insert_func insert_first);
+ void setEquals(equals_func equals);
+
+ DATA_T &addData(const INDEX_T &index, DATA_T datap);
+ DATA_T &addData(const INDEX_T &index);
+ DATA_T &getData(const INDEX_T &index);
+ DATA_T &operator[](const INDEX_T &index);
+
+ // If index present, returns data.
+ // If index not present, adds <index,NULL> and returns NULL.
+ DATA_T &getData(const INDEX_T &index, BOOL &b_new_entry);
+
+ // returns data entry before and after index
+ BOOL getInterval(const INDEX_T &index, INDEX_T &index_before, INDEX_T &index_after,
+ DATA_T &data_before, DATA_T &data_after );
+
+ // Returns TRUE if data present in map.
+ BOOL checkData(const INDEX_T &index);
+
+ // Returns TRUE if key is present in map. This is useful if you
+ // are potentially storing NULL pointers in the map
+ BOOL checkKey(const INDEX_T &index);
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ DATA_T getIfThere(const INDEX_T &index);
+
+ INDEX_T reverseLookup(const DATA_T datap);
+
+ // returns number of items in the list
+ S32 getLength(); // WARNING! getLength is O(n), not O(1)!
+
+ BOOL removeData(const INDEX_T &index);
+ BOOL deleteData(const INDEX_T &index);
+
+ // remove all nodes from the list but do not delete data
+ void removeAllData();
+ void deleteAllData();
+
+ // place mCurrentp on first node
+ void resetList();
+
+ // return the data currently pointed to
+ DATA_T getCurrentDataWithoutIncrement();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_T getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ DATA_T getNextData();
+
+ INDEX_T getNextKey();
+
+ // return the key currently pointed to
+ INDEX_T getCurrentKeyWithoutIncrement();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void removeCurrentData();
+
+ void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_T getFirstData();
+
+ INDEX_T getFirstKey();
+
+ static BOOL defaultEquals(const INDEX_T &first, const INDEX_T &second)
+ {
+ return first == second;
+ }
+
+private:
+ // don't generate implicit copy constructor or copy assignment
+ LLPtrSkipMap(const LLPtrSkipMap &);
+ LLPtrSkipMap &operator=(const LLPtrSkipMap &);
+
+private:
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mUpdate[BINARY_DEPTH];
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mCurrentp;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const INDEX_T &first, const INDEX_T &second);
+ BOOL (*mEquals)(const INDEX_T &first, const INDEX_T &second);
+ S32 mNumberOfSteps;
+};
+
+//////////////////////////////////////////////////
+//
+// LLPtrSkipMap implementation
+//
+//
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::LLPtrSkipMap()
+ : mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::LLPtrSkipMap(insert_func insert_first,
+ equals_func equals)
+: mInsertFirst(insert_first),
+ mEquals(equals)
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::~LLPtrSkipMap()
+{
+ removeAllData();
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
+{
+ mInsertFirst = insert_first;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::setEquals(equals_func equals)
+{
+ mEquals = equals;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::addData(const INDEX_T &index, DATA_T datap)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // replace the existing data if a node is already there
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+ current->mData = datap;
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ {
+ break;
+ }
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index, datap);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::addData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+ mNumberOfSteps++;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *snode
+ = new LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+
+ return snode->mData;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getInterval(const INDEX_T &index,
+ INDEX_T &index_before,
+ INDEX_T &index_after,
+ DATA_T &data_before,
+ DATA_T &data_after)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ BOOL both_found = TRUE;
+
+ if (current != &mHead)
+ {
+ index_before = current->mIndex;
+ data_before = current->mData;
+ }
+ else
+ {
+ data_before = 0;
+ index_before = 0;
+ both_found = FALSE;
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ data_after = current->mData;
+ index_after = current->mIndex;
+ }
+ else
+ {
+ data_after = 0;
+ index_after = 0;
+ both_found = FALSE;
+ }
+
+ return both_found;
+}
+
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::operator[](const INDEX_T &index)
+{
+
+ return getData(index);
+}
+
+// If index present, returns data.
+// If index not present, adds <index,NULL> and returns NULL.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T &LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getData(const INDEX_T &index, BOOL &b_new_entry)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+ b_new_entry = TRUE;
+ addData(index);
+
+ return current->mData;
+}
+
+// Returns TRUE if data present in map.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::checkData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ // Gets rid of some compiler ambiguity for the LLPointer<> templated class.
+ if (current->mData)
+ {
+ return mEquals(current->mIndex, index);
+ }
+ }
+
+ return FALSE;
+}
+
+// Returns TRUE if key is present in map. This is useful if you
+// are potentially storing NULL pointers in the map
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::checkKey(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mIndex, index);
+ }
+
+ return FALSE;
+}
+
+// If there, returns the data.
+// If not, returns NULL.
+// Never adds entries to the map.
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getIfThere(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ if (mEquals(current->mIndex, index))
+ {
+ return current->mData;
+ }
+ }
+
+ // Avoid Linux compiler warning on returning NULL.
+ return (DATA_T)0;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::reverseLookup(const DATA_T datap)
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+
+ while (current)
+ {
+ if (datap == current->mData)
+ {
+
+ return current->mIndex;
+ }
+ current = *current->mForward;
+ }
+
+ // not found! return NULL
+ return INDEX_T();
+}
+
+// returns number of items in the list
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline S32 LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH>* temp;
+ for (temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+
+ return length;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ // clean up cuurent
+ current->removeData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline BOOL LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteData(const INDEX_T &index)
+{
+ S32 level;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *current = &mHead;
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ // clean up cuurent
+ current->deleteData();
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+// remove all nodes from the list but do not delete data
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeAllData()
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->removeData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteAllData()
+{
+ LLPtrSkipMapNode<INDEX_T, DATA_T, BINARY_DEPTH> *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ mCurrentp->deleteData();
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// return the data currently pointed to
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentDataWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes warning
+ return (DATA_T)0; // equivalent, but no warning
+ }
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes warning
+ return (DATA_T)0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_T)0; // equivalent, but removes warning
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getNextKey()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+// return the key currently pointed to
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getCurrentKeyWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (INDEX_T)0; // equivalent, but removes warning
+ }
+}
+
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mIndex);
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline void LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mIndex);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline DATA_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_T)0; // equivalent, but removes warning
+ }
+}
+
+template <class INDEX_T, class DATA_T, S32 BINARY_DEPTH>
+inline INDEX_T LLPtrSkipMap<INDEX_T, DATA_T, BINARY_DEPTH>::getFirstKey()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
new file mode 100644
index 0000000000..bc41927d38
--- /dev/null
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -0,0 +1,491 @@
+/**
+ * @file llqueuedthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llqueuedthread.h"
+#include "llstl.h"
+
+//============================================================================
+
+// MAIN THREAD
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runalways) :
+ LLThread(name),
+ mThreaded(threaded),
+ mRunAlways(runalways),
+ mIdleThread(TRUE),
+ mNextHandle(0)
+{
+ if (mThreaded)
+ {
+ start();
+ }
+}
+
+// MAIN THREAD
+LLQueuedThread::~LLQueuedThread()
+{
+ setQuitting();
+
+ unpause(); // MAIN THREAD
+ if (mThreaded)
+ {
+ S32 timeout = 100;
+ for ( ; timeout>0; timeout--)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ ms_sleep(100);
+ LLThread::yield();
+ }
+ if (timeout == 0)
+ {
+ llwarns << "~LLQueuedThread (" << mName << ") timed out!" << llendl;
+ }
+ }
+ else
+ {
+ mStatus = STOPPED;
+ }
+
+ QueuedRequest* req;
+ while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
+ {
+ req->deleteRequest();
+ }
+
+ // ~LLThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+// MAIN THREAD
+void LLQueuedThread::update(U32 ms_elapsed)
+{
+ updateQueue(0);
+}
+
+void LLQueuedThread::updateQueue(S32 inc)
+{
+ // If mRunAlways == TRUE, unpause the thread whenever we put something into the queue.
+ // If mRunAlways == FALSE, we only unpause the thread when updateQueue() is called from the main loop (i.e. between rendered frames)
+
+ if (inc == 0) // Frame Update
+ {
+ if (mThreaded)
+ {
+ unpause();
+ wake(); // Wake the thread up if necessary.
+ }
+ else
+ {
+ while (processNextRequest() > 0)
+ ;
+ }
+ }
+ else
+ {
+ // Something has been added to the queue
+ if (mRunAlways)
+ {
+ if (mThreaded)
+ {
+ wake(); // Wake the thread up if necessary.
+ }
+ else
+ {
+ while(processNextRequest() > 0)
+ ;
+ }
+ }
+ }
+}
+
+//virtual
+// May be called from any thread
+S32 LLQueuedThread::getPending(bool child_thread)
+{
+ S32 res;
+ lockData();
+ res = mRequestQueue.size();
+ unlockData();
+ return res;
+}
+
+// MAIN thread
+void LLQueuedThread::waitOnPending()
+{
+ while(1)
+ {
+ updateQueue(0);
+
+ if (mIdleThread)
+ {
+ break;
+ }
+ if (mThreaded)
+ {
+ yield();
+ }
+ }
+ return;
+}
+
+// MAIN thread
+void LLQueuedThread::printQueueStats()
+{
+ lockData();
+ if (!mRequestQueue.empty())
+ {
+ QueuedRequest *req = *mRequestQueue.begin();
+ llinfos << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << llendl;
+ }
+ else
+ {
+ llinfos << "Queued Thread Idle" << llendl;
+ }
+ unlockData();
+}
+
+// MAIN thread
+LLQueuedThread::handle_t LLQueuedThread::generateHandle()
+{
+ lockData();
+ while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
+ {
+ mNextHandle++;
+ }
+ unlockData();
+ return mNextHandle++;
+}
+
+// MAIN thread
+bool LLQueuedThread::addRequest(QueuedRequest* req)
+{
+ if (mStatus == QUITTING)
+ {
+ return false;
+ }
+
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+ mRequestQueue.insert(req);
+ mRequestHash.insert(req);
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Added req [%08d]",handle) << llendl;
+#endif
+ unlockData();
+
+ updateQueue(1);
+
+ return true;
+}
+
+// MAIN thread
+bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
+{
+ llassert (handle != nullHandle())
+ bool res = false;
+ bool waspaused = isPaused();
+ bool done = false;
+ while(!done)
+ {
+ updateQueue(0); // unpauses
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (!req)
+ {
+ done = true; // request does not exist
+ }
+ else if (req->getStatus() == STATUS_COMPLETE)
+ {
+ res = true;
+ if (auto_complete)
+ {
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ }
+ done = true;
+ }
+ unlockData();
+
+ if (!done && mThreaded)
+ {
+ yield();
+ }
+ }
+ if (waspaused)
+ {
+ pause();
+ }
+ return res;
+}
+
+// MAIN thread
+LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
+{
+ if (handle == nullHandle())
+ {
+ return 0;
+ }
+ lockData();
+ QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle);
+ unlockData();
+ return res;
+}
+
+LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->getStatus();
+ }
+ unlockData();
+ return res;
+}
+
+LLQueuedThread::status_t LLQueuedThread::abortRequest(handle_t handle, U32 flags)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->abortRequest(flags);
+ if ((flags & AUTO_COMPLETE) && (res == STATUS_COMPLETE))
+ {
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ }
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Aborted req [%08d]",handle) << llendl;
+#endif
+ }
+ unlockData();
+ return res;
+}
+
+// MAIN thread
+LLQueuedThread::status_t LLQueuedThread::setFlags(handle_t handle, U32 flags)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->setFlags(flags);
+ }
+ unlockData();
+ return res;
+}
+
+void LLQueuedThread::setPriority(handle_t handle, U32 priority)
+{
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req && (req->getStatus() == STATUS_QUEUED))
+ {
+ llverify(mRequestQueue.erase(req) == 1);
+ req->setPriority(priority);
+ mRequestQueue.insert(req);
+ }
+ unlockData();
+}
+
+bool LLQueuedThread::completeRequest(handle_t handle)
+{
+ bool res = false;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ llassert(req->getStatus() != STATUS_QUEUED && req->getStatus() != STATUS_ABORT);
+#if _DEBUG
+// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
+#endif
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ res = true;
+ }
+ unlockData();
+ return res;
+}
+
+bool LLQueuedThread::check()
+{
+#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging
+ for (int i=0; i<REQUEST_HASH_SIZE; i++)
+ {
+ LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i);
+ while (entry)
+ {
+ if (entry->getHashKey() > mNextHandle)
+ {
+ llerrs << "Hash Error" << llendl;
+ return false;
+ }
+ entry = entry->getNextEntry();
+ }
+ }
+#endif
+ return true;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+int LLQueuedThread::processNextRequest()
+{
+ QueuedRequest *req = 0;
+ // Get next request from pool
+ lockData();
+ while(1)
+ {
+ if (!mRequestQueue.empty())
+ {
+ req = *mRequestQueue.begin();
+ mRequestQueue.erase(mRequestQueue.begin());
+ }
+ if (req && req->getStatus() == STATUS_ABORT)
+ {
+ req->setStatus(STATUS_ABORTED);
+ req = 0;
+ }
+ else
+ {
+ llassert (!req || req->getStatus() == STATUS_QUEUED)
+ break;
+ }
+ }
+ if (req)
+ {
+ req->setStatus(STATUS_INPROGRESS);
+ }
+ unlockData();
+
+ // This is the only place we will cal req->setStatus() after
+ // it has initially been seet to STATUS_QUEUED, so it is
+ // safe to access req.
+ if (req)
+ {
+ // process request
+ bool complete = processRequest(req);
+
+ if (complete)
+ {
+ lockData();
+ req->setStatus(STATUS_COMPLETE);
+ req->finishRequest();
+ if (req->getFlags() & AUTO_COMPLETE)
+ {
+ llverify(mRequestHash.erase(req))
+ req->deleteRequest();
+// check();
+ }
+ unlockData();
+ }
+ else
+ {
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+ mRequestQueue.insert(req);
+ unlockData();
+ }
+ }
+
+ int res;
+ if (getPending(true) == 0)
+ {
+ if (isQuitting())
+ {
+ res = -1; // exit thread
+ }
+ else
+ {
+ res = 0;
+ }
+ }
+ else
+ {
+ res = 1;
+ }
+ return res;
+}
+
+bool LLQueuedThread::runCondition()
+{
+ // mRunCondition must be locked here
+ return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE;
+}
+
+void LLQueuedThread::run()
+{
+ llinfos << "QUEUED THREAD STARTING" << llendl;
+
+ while (1)
+ {
+ // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
+ checkPause();
+
+ if(isQuitting())
+ break;
+
+ //llinfos << "QUEUED THREAD RUNNING, queue size = " << mRequestQueue.size() << llendl;
+
+ mIdleThread = FALSE;
+
+ int res = processNextRequest();
+ if (res == 0)
+ {
+ mIdleThread = TRUE;
+ }
+
+ if (res < 0) // finished working and want to exit
+ {
+ break;
+ }
+ }
+
+ llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
+}
+
+//============================================================================
+
+LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
+ LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
+ mStatus(STATUS_UNKNOWN),
+ mPriority(priority),
+ mFlags(flags)
+{
+}
+
+LLQueuedThread::QueuedRequest::~QueuedRequest()
+{
+ if (mStatus != STATUS_DELETE)
+ {
+ llerrs << "Attemt to directly delete a LLQueuedThread::QueuedRequest; use deleteRequest()" << llendl;
+ }
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::finishRequest()
+{
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::deleteRequest()
+{
+ setStatus(STATUS_DELETE);
+ delete this;
+}
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
new file mode 100644
index 0000000000..beff473f52
--- /dev/null
+++ b/indra/llcommon/llqueuedthread.h
@@ -0,0 +1,202 @@
+/**
+ * @file llqueuedthread.h
+ * @brief
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUEUEDTHREAD_H
+#define LL_LLQUEUEDTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llapr.h"
+
+#include "llthread.h"
+#include "llsimplehash.h"
+
+//============================================================================
+// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
+// It is assumed that LLQueuedThreads are rarely created/destroyed.
+
+class LLQueuedThread : public LLThread
+{
+ //------------------------------------------------------------------------
+public:
+ enum priority_t {
+ PRIORITY_IMMEDIATE = 0x7FFFFFFF,
+ PRIORITY_URGENT = 0x40000000,
+ PRIORITY_HIGH = 0x30000000,
+ PRIORITY_NORMAL = 0x20000000,
+ PRIORITY_LOW = 0x10000000,
+ PRIORITY_LOWBITS = 0x0FFFFFFF
+ };
+ enum status_t {
+ STATUS_EXPIRED = -1,
+ STATUS_UNKNOWN = 0,
+ STATUS_QUEUED = 1,
+ STATUS_INPROGRESS = 2,
+ STATUS_COMPLETE = 3,
+ STATUS_ABORT = 4,
+ STATUS_ABORTED = 5,
+ STATUS_DELETE = 6
+ };
+ enum flags_t {
+ AUTO_COMPLETE = 1,
+ AUTO_DELETE = 2 // child-class dependent
+ };
+
+ typedef U32 handle_t;
+
+ //------------------------------------------------------------------------
+public:
+
+ class QueuedRequest : public LLSimpleHashEntry<handle_t>
+ {
+ friend class LLQueuedThread;
+
+ protected:
+ ~QueuedRequest(); // use deleteRequest()
+
+ public:
+ QueuedRequest(handle_t handle, U32 priority, U32 flags = 0);
+
+ status_t getStatus()
+ {
+ return mStatus;
+ }
+ U32 getPriority() const
+ {
+ return mPriority;
+ }
+ U32 getFlags() const
+ {
+ return mFlags;
+ }
+ bool higherPriority(const QueuedRequest& second) const
+ {
+ if ( mPriority == second.mPriority)
+ return mHashKey < second.mHashKey;
+ else
+ return mPriority > second.mPriority;
+ }
+
+ protected:
+ status_t setStatus(status_t newstatus)
+ {
+ status_t oldstatus = mStatus;
+ mStatus = newstatus;
+ return oldstatus;
+ }
+ status_t abortRequest(U32 flags)
+ {
+ // NOTE: flags are |'d
+ if (mStatus == STATUS_QUEUED)
+ {
+ setStatus(STATUS_ABORT);
+ }
+ mFlags |= flags;
+ status_t status = mStatus;
+ return status;
+ }
+ status_t setFlags(U32 flags)
+ {
+ // NOTE: flags are |'d
+ mFlags |= flags;
+ status_t status = mStatus;
+ return status;
+ }
+
+ virtual void finishRequest(); // Always called when after has been processed
+ virtual void deleteRequest(); // Only method to delete a request
+
+ void setPriority(U32 pri)
+ {
+ // Only do this on a request that is not in a queued list!
+ mPriority = pri;
+ };
+
+ protected:
+ LLAtomic32<status_t> mStatus;
+ U32 mPriority;
+ U32 mFlags;
+ };
+
+protected:
+ struct queued_request_less
+ {
+ bool operator()(const QueuedRequest* lhs, const QueuedRequest* rhs) const
+ {
+ return lhs->higherPriority(*rhs); // higher priority in front of queue (set)
+ }
+ };
+
+ //------------------------------------------------------------------------
+
+public:
+ static handle_t nullHandle() { return handle_t(0); }
+
+public:
+ LLQueuedThread(const std::string& name, bool threaded = TRUE, bool runalways = TRUE);
+ virtual ~LLQueuedThread();
+
+private:
+ // No copy constructor or copy assignment
+ LLQueuedThread(const LLQueuedThread&);
+ LLQueuedThread& operator=(const LLQueuedThread&);
+
+ virtual bool runCondition(void);
+ virtual void run(void);
+
+protected:
+ handle_t generateHandle();
+ bool addRequest(QueuedRequest* req);
+ int processNextRequest(void);
+
+ virtual bool processRequest(QueuedRequest* req) = 0;
+
+public:
+ bool waitForResult(handle_t handle, bool auto_complete = true);
+
+ void update(U32 ms_elapsed);
+ void updateQueue(S32 inc);
+ void waitOnPending();
+ void printQueueStats();
+
+ S32 getPending(bool child_thread = false);
+ bool getThreaded() { return mThreaded ? true : false; }
+ bool getRunAlways() { return mRunAlways ? true : false; }
+
+ // Request accessors
+ status_t getRequestStatus(handle_t handle);
+ status_t abortRequest(handle_t handle, U32 flags = 0);
+ status_t setFlags(handle_t handle, U32 flags);
+ void setPriority(handle_t handle, U32 priority);
+ bool completeRequest(handle_t handle);
+ // This is public for support classes like LLWorkerThread,
+ // but generally the methods above should be used.
+ QueuedRequest* getRequest(handle_t handle);
+
+ // debug (see source)
+ bool check();
+
+protected:
+ BOOL mThreaded; // if false, run on main thread and do updates during update()
+ BOOL mRunAlways; // if false, only wake the threads when updateClass() is called
+ LLAtomic32<BOOL> mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
+
+ typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
+ request_queue_t mRequestQueue;
+
+ enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2
+ typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t;
+ request_hash_t mRequestHash;
+
+ handle_t mNextHandle;
+};
+
+#endif // LL_LLQUEUEDTHREAD_H
diff --git a/indra/llcommon/llrun.cpp b/indra/llcommon/llrun.cpp
new file mode 100644
index 0000000000..0e6a80e766
--- /dev/null
+++ b/indra/llcommon/llrun.cpp
@@ -0,0 +1,156 @@
+/**
+ * @file llrun.cpp
+ * @author Phoenix
+ * @date 2006-02-16
+ * @brief Implementation of the LLRunner and related classes
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llrun.h"
+
+#include "llframetimer.h"
+
+static const LLRunner::run_handle_t INVALID_RUN_HANDLE = 0;
+
+/**
+ * LLRunner
+ */
+LLRunner::LLRunner() :
+ mNextHandle(1)
+{
+}
+
+LLRunner::~LLRunner()
+{
+ mRunOnce.clear();
+ mRunEvery.clear();
+}
+
+S32 LLRunner::run()
+{
+ // We collect all of the runnables which should be run. Since the
+ // runnables are allowed to adjust the run list, we need to copy
+ // them into a temporary structure which then iterates over them
+ // to call out of this method into the runnables.
+ F64 now = LLFrameTimer::getTotalSeconds();
+ run_list_t run_now;
+
+ // Collect the run once. We erase the matching ones now because
+ // it's easier. If we find a reason to keep them around for a
+ // while, we can restructure this method.
+ LLRunner::run_list_t::iterator iter = mRunOnce.begin();
+ for( ; iter != mRunOnce.end(); )
+ {
+ if(now > (*iter).mNextRunAt)
+ {
+ run_now.push_back(*iter);
+ iter = mRunOnce.erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ // Collect the ones that repeat.
+ iter = mRunEvery.begin();
+ LLRunner::run_list_t::iterator end = mRunEvery.end();
+ for( ; iter != end; ++iter )
+ {
+ if(now > (*iter).mNextRunAt)
+ {
+ (*iter).mNextRunAt = now + (*iter).mIncrement;
+ run_now.push_back(*iter);
+ }
+ }
+
+ // Now, run them.
+ iter = run_now.begin();
+ end = run_now.end();
+ for( ; iter != end; ++iter )
+ {
+ (*iter).mRunnable->run(this, (*iter).mHandle);
+ }
+ return run_now.size();
+}
+
+LLRunner::run_handle_t LLRunner::addRunnable(
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 seconds)
+{
+ if(!runnable) return INVALID_RUN_HANDLE;
+ run_handle_t handle = mNextHandle++;
+ F64 next_run = LLFrameTimer::getTotalSeconds() + seconds;
+ LLRunInfo info(handle, runnable, schedule, next_run, seconds);
+ switch(schedule)
+ {
+ case RUN_IN:
+ // We could optimize this a bit by sorting this on entry.
+ mRunOnce.push_back(info);
+ break;
+ case RUN_EVERY:
+ mRunEvery.push_back(info);
+ break;
+ default:
+ handle = INVALID_RUN_HANDLE;
+ break;
+ }
+ return handle;
+}
+
+LLRunner::run_ptr_t LLRunner::removeRunnable(LLRunner::run_handle_t handle)
+{
+ LLRunner::run_ptr_t rv;
+ LLRunner::run_list_t::iterator iter = mRunOnce.begin();
+ LLRunner::run_list_t::iterator end = mRunOnce.end();
+ for( ; iter != end; ++iter)
+ {
+ if((*iter).mHandle == handle)
+ {
+ rv = (*iter).mRunnable;
+ mRunOnce.erase(iter);
+ return rv;
+ }
+ }
+
+ iter = mRunEvery.begin();
+ end = mRunEvery.end();
+ for( ; iter != end; ++iter)
+ {
+ if((*iter).mHandle == handle)
+ {
+ rv = (*iter).mRunnable;
+ mRunEvery.erase(iter);
+ return rv;
+ }
+ }
+ return rv;
+}
+
+/**
+ * LLRunner::LLRunInfo
+ */
+LLRunner::LLRunInfo::LLRunInfo(
+ run_handle_t handle,
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 next_run_after,
+ F64 increment) :
+ mHandle(handle),
+ mRunnable(runnable),
+ mSchedule(schedule),
+ mNextRunAt(next_run_after),
+ mIncrement(increment)
+{
+}
+
+LLRunnable::LLRunnable()
+{ }
+
+// virtual
+LLRunnable::~LLRunnable()
+{ }
diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h
new file mode 100644
index 0000000000..8aa747ebab
--- /dev/null
+++ b/indra/llcommon/llrun.h
@@ -0,0 +1,144 @@
+/**
+ * @file llrun.h
+ * @author Phoenix
+ * @date 2006-02-16
+ * @brief Declaration of LLRunner and LLRunnable classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLRUN_H
+#define LL_LLRUN_H
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+class LLRunnable;
+
+/**
+ * @class LLRunner
+ * @brief This class manages a set of LLRunnable objects.
+ *
+ * An instance of this class has a collection of LLRunnable objects
+ * which are scheduled to run on a repeating or one time basis.
+ * @see LLRunnable
+ */
+class LLRunner
+{
+public:
+ /**
+ * @brief The pointer to a runnable.
+ */
+ typedef boost::shared_ptr<LLRunnable> run_ptr_t;
+
+ /**
+ * @brief The handle for use in the API.
+ */
+ typedef S64 run_handle_t;
+
+ /**
+ * @brief Constructor.
+ */
+ LLRunner();
+
+ /**
+ * @brief Destructor.
+ */
+ ~LLRunner();
+
+ /**
+ * @brief Enumeration which specifies when to run.
+ */
+ enum ERunSchedule
+ {
+ // The runnable will run in N seconds
+ RUN_IN,
+
+ // The run every N seconds
+ RUN_EVERY,
+
+ // A count of the run types
+ RUN_SCHEDULE_COUNT
+ };
+
+ /**
+ * @brief Run the runnables which are scheduled to run
+ *
+ * @return Returns the number of runnables run.
+ */
+ S32 run();
+
+ /**
+ * @brief Add a runnable to the run list.
+ *
+ * The handle of the runnable is unique to each addition. If the
+ * same runnable is added a second time with the same or different
+ * schedule, this method will return a new handle.
+ * @param runnable The runnable to run() on schedule.
+ * @param schedule Specifies the run schedule.
+ * @param seconds When to run the runnable as interpreted by schedule.
+ * @return Returns the handle to the runnable. handle == 0 means failure.
+ */
+ run_handle_t addRunnable(
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 seconds);
+
+ /**
+ * @brief Remove the specified runnable.
+ *
+ * @param handle The handle of the runnable to remove.
+ * @return Returns the pointer to the runnable removed which may
+ * be empty.
+ */
+ run_ptr_t removeRunnable(run_handle_t handle);
+
+protected:
+ struct LLRunInfo
+ {
+ run_handle_t mHandle;
+ run_ptr_t mRunnable;
+ ERunSchedule mSchedule;
+ F64 mNextRunAt;
+ F64 mIncrement;
+ LLRunInfo(
+ run_handle_t handle,
+ run_ptr_t runnable,
+ ERunSchedule schedule,
+ F64 next_run_at,
+ F64 increment);
+ };
+ typedef std::vector<LLRunInfo> run_list_t;
+ run_list_t mRunOnce;
+ run_list_t mRunEvery;
+ run_handle_t mNextHandle;
+};
+
+
+/**
+ * @class LLRunnable
+ * @brief Abstract base class for running some scheduled process.
+ *
+ * Users of the LLRunner class are expected to derive a concrete
+ * implementation of this class which overrides the run() method to do
+ * something useful.
+ * @see LLRunner
+ */
+class LLRunnable
+{
+public:
+ LLRunnable();
+ virtual ~LLRunnable();
+
+ /**
+ * @brief Do the process.
+ *
+ * This method will be called from the LLRunner according to
+ * @param runner The Runner which call run().
+ * @param handle The handle this run instance is run under.
+ */
+ virtual void run(LLRunner* runner, S64 handle) = 0;
+};
+
+#endif // LL_LLRUN_H
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
new file mode 100644
index 0000000000..25bd7ceac8
--- /dev/null
+++ b/indra/llcommon/llsd.cpp
@@ -0,0 +1,731 @@
+/**
+ * @file llsd.cpp
+ * @brief LLSD flexible data system
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llsd.h"
+
+#include <math.h>
+#include "../llmath/llmath.h"
+
+namespace {
+ class ImplMap;
+ class ImplArray;
+}
+
+class LLSD::Impl
+ /**< This class is the abstract base class of the implementation of LLSD
+ It provides the reference counting implementation, and the default
+ implementation of most methods for most data types. It also serves
+ as a working implementation of the Undefined type.
+
+ */
+{
+private:
+ U32 mUseCount;
+
+protected:
+ Impl();
+
+ enum StaticAllocationMarker { STATIC };
+ Impl(StaticAllocationMarker);
+ ///< This constructor is used for static objects and causes the
+ // suppresses adjusting the debugging counters when they are
+ // finally initialized.
+
+ virtual ~Impl();
+
+ bool shared() const { return mUseCount > 1; }
+
+public:
+ static void reset(Impl*& var, Impl* impl);
+ ///< safely set var to refer to the new impl (possibly shared)
+
+ static Impl& safe( Impl*);
+ static const Impl& safe(const Impl*);
+ ///< since a NULL Impl* is used for undefined, this ensures there is
+ // always an object you call virtual member functions on
+
+ virtual ImplMap& makeMap(Impl*& var);
+ virtual ImplArray& makeArray(Impl*& var);
+ ///< sure var is a modifiable, non-shared map or array
+
+ virtual LLSD::Type type() const { return LLSD::TypeUndefined; }
+
+ static void assignUndefined(LLSD::Impl*& var);
+ static void assign(LLSD::Impl*& var, const LLSD::Impl* other);
+
+ virtual void assign(Impl*& var, LLSD::Boolean);
+ virtual void assign(Impl*& var, LLSD::Integer);
+ virtual void assign(Impl*& var, LLSD::Real);
+ virtual void assign(Impl*& var, const LLSD::String&);
+ virtual void assign(Impl*& var, const LLSD::UUID&);
+ virtual void assign(Impl*& var, const LLSD::Date&);
+ virtual void assign(Impl*& var, const LLSD::URI&);
+ virtual void assign(Impl*& var, const LLSD::Binary&);
+ ///< If the receiver is the right type and unshared, these are simple
+ // data assignments, othewise the default implementation handless
+ // constructing the proper Impl subclass
+
+ virtual Boolean asBoolean() const { return false; }
+ virtual Integer asInteger() const { return 0; }
+ virtual Real asReal() const { return 0.0; }
+ virtual String asString() const { return std::string(); }
+ virtual UUID asUUID() const { return LLUUID(); }
+ virtual Date asDate() const { return LLDate(); }
+ virtual URI asURI() const { return LLURI(); }
+ virtual Binary asBinary() const { return std::vector<U8>(); }
+
+ virtual bool has(const String&) const { return false; }
+ virtual LLSD get(const String&) const { return LLSD(); }
+ virtual void erase(const String&) { }
+ virtual const LLSD& ref(const String&) const{ return undef(); }
+
+ virtual int size() const { return 0; }
+ virtual LLSD get(Integer) const { return LLSD(); }
+ virtual void erase(Integer) { }
+ virtual const LLSD& ref(Integer) const { return undef(); }
+
+ virtual LLSD::map_const_iterator beginMap() const { return LLSD::map_const_iterator(); }
+ virtual LLSD::map_const_iterator endMap() const { return LLSD::map_const_iterator(); }
+ virtual LLSD::array_const_iterator beginArray() const { return LLSD::array_const_iterator(); }
+ virtual LLSD::array_const_iterator endArray() const { return LLSD::array_const_iterator(); }
+
+ static const LLSD& undef();
+
+ static U32 sAllocationCount;
+ static U32 sOutstandingCount;
+};
+
+namespace {
+ template<LLSD::Type T, class Data, class DataRef = Data>
+ class ImplBase : public LLSD::Impl
+ ///< This class handles most of the work for a subclass of Impl
+ // for a given simple data type. Subclasses of this provide the
+ // conversion functions and a constructor.
+ {
+ protected:
+ Data mValue;
+
+ typedef ImplBase Base;
+
+ public:
+ ImplBase(DataRef value) : mValue(value) { }
+
+ virtual LLSD::Type type() const { return T; }
+
+ virtual void assign(LLSD::Impl*& var, DataRef value) {
+ if (shared())
+ {
+ Impl::assign(var, value);
+ }
+ else
+ {
+ mValue = value;
+ }
+ }
+ };
+
+
+ class ImplBoolean
+ : public ImplBase<LLSD::TypeBoolean, LLSD::Boolean>
+ {
+ public:
+ ImplBoolean(LLSD::Boolean v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return mValue; }
+ virtual LLSD::Integer asInteger() const { return mValue ? 1 : 0; }
+ virtual LLSD::Real asReal() const { return mValue ? 1 : 0; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::String ImplBoolean::asString() const
+ { return mValue ? "true" : ""; }
+
+
+ class ImplInteger
+ : public ImplBase<LLSD::TypeInteger, LLSD::Integer>
+ {
+ public:
+ ImplInteger(LLSD::Integer v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return mValue != 0; }
+ virtual LLSD::Integer asInteger() const { return mValue; }
+ virtual LLSD::Real asReal() const { return mValue; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::String ImplInteger::asString() const
+ { return llformat("%d", mValue); }
+
+
+ class ImplReal
+ : public ImplBase<LLSD::TypeReal, LLSD::Real>
+ {
+ public:
+ ImplReal(LLSD::Real v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const;
+ virtual LLSD::Integer asInteger() const;
+ virtual LLSD::Real asReal() const { return mValue; }
+ virtual LLSD::String asString() const;
+ };
+
+ LLSD::Boolean ImplReal::asBoolean() const
+ { return !llisnan(mValue) && mValue != 0.0; }
+
+ LLSD::Integer ImplReal::asInteger() const
+ { return !llisnan(mValue) ? (LLSD::Integer)mValue : 0; }
+
+ LLSD::String ImplReal::asString() const
+ { return llformat("%lg", mValue); }
+
+
+ class ImplString
+ : public ImplBase<LLSD::TypeString, LLSD::String, const LLSD::String&>
+ {
+ public:
+ ImplString(const LLSD::String& v) : Base(v) { }
+
+ virtual LLSD::Boolean asBoolean() const { return !mValue.empty(); }
+ virtual LLSD::Integer asInteger() const;
+ virtual LLSD::Real asReal() const;
+ virtual LLSD::String asString() const { return mValue; }
+ virtual LLSD::UUID asUUID() const { return LLUUID(mValue); }
+ virtual LLSD::Date asDate() const { return LLDate(mValue); }
+ virtual LLSD::URI asURI() const { return LLURI(mValue); }
+ };
+
+ LLSD::Integer ImplString::asInteger() const
+ {
+ // This must treat "1.23" not as an error, but as a number, which is
+ // then truncated down to an integer. Hence, this code doesn't call
+ // std::istringstream::operator>>(int&), which would not consume the
+ // ".23" portion.
+
+ return (int)asReal();
+ }
+
+ LLSD::Real ImplString::asReal() const
+ {
+ F64 v = 0.0;
+ std::istringstream i_stream(mValue);
+ i_stream >> v;
+
+ // we would probably like to ignore all trailing whitespace as
+ // well, but for now, simply eat the next character, and make
+ // sure we reached the end of the string.
+ // *NOTE: gcc 2.95 does not generate an eof() event on the
+ // stream operation above, so we manually get here to force it
+ // across platforms.
+ int c = i_stream.get();
+ return ((EOF ==c) ? v : 0.0);
+ }
+
+
+ class ImplUUID
+ : public ImplBase<LLSD::TypeUUID, LLSD::UUID, const LLSD::UUID&>
+ {
+ public:
+ ImplUUID(const LLSD::UUID& v) : Base(v) { }
+
+ virtual LLSD::String asString() const{ return mValue.getString(); }
+ virtual LLSD::UUID asUUID() const { return mValue; }
+ };
+
+
+ class ImplDate
+ : public ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>
+ {
+ public:
+ ImplDate(const LLSD::Date& v)
+ : ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>(v)
+ { }
+
+ virtual LLSD::Integer asInteger() const
+ {
+ return (LLSD::Integer)(mValue.secondsSinceEpoch());
+ }
+ virtual LLSD::Real asReal() const
+ {
+ return mValue.secondsSinceEpoch();
+ }
+ virtual LLSD::String asString() const{ return mValue.asString(); }
+ virtual LLSD::Date asDate() const { return mValue; }
+ };
+
+
+ class ImplURI
+ : public ImplBase<LLSD::TypeURI, LLSD::URI, const LLSD::URI&>
+ {
+ public:
+ ImplURI(const LLSD::URI& v) : Base(v) { }
+
+ virtual LLSD::String asString() const{ return mValue.asString(); }
+ virtual LLSD::URI asURI() const { return mValue; }
+ };
+
+
+ class ImplBinary
+ : public ImplBase<LLSD::TypeBinary, LLSD::Binary, const LLSD::Binary&>
+ {
+ public:
+ ImplBinary(const LLSD::Binary& v) : Base(v) { }
+
+ virtual LLSD::Binary asBinary() const{ return mValue; }
+ };
+
+
+ class ImplMap : public LLSD::Impl
+ {
+ private:
+ typedef std::map<LLSD::String, LLSD> DataMap;
+
+ DataMap mData;
+
+ protected:
+ ImplMap(const DataMap& data) : mData(data) { }
+
+ public:
+ ImplMap() { }
+
+ virtual ImplMap& makeMap(LLSD::Impl*&);
+
+ virtual LLSD::Type type() const { return LLSD::TypeMap; }
+
+ virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+
+ virtual bool has(const LLSD::String&) const;
+ virtual LLSD get(const LLSD::String&) const;
+ void insert(const LLSD::String& k, const LLSD& v);
+ virtual void erase(const LLSD::String&);
+ LLSD& ref(const LLSD::String&);
+ virtual const LLSD& ref(const LLSD::String&) const;
+
+ virtual int size() const { return mData.size(); }
+
+ LLSD::map_iterator beginMap() { return mData.begin(); }
+ LLSD::map_iterator endMap() { return mData.end(); }
+ virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); }
+ virtual LLSD::map_const_iterator endMap() const { return mData.end(); }
+ };
+
+ ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
+ {
+ if (shared())
+ {
+ ImplMap* i = new ImplMap(mData);
+ Impl::assign(var, i);
+ return *i;
+ }
+ else
+ {
+ return *this;
+ }
+ }
+
+ bool ImplMap::has(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.find(k);
+ return i != mData.end();
+ }
+
+ LLSD ImplMap::get(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.find(k);
+ return (i != mData.end()) ? i->second : LLSD();
+ }
+
+ void ImplMap::insert(const LLSD::String& k, const LLSD& v)
+ {
+ mData.insert(DataMap::value_type(k, v));
+ }
+
+ void ImplMap::erase(const LLSD::String& k)
+ {
+ mData.erase(k);
+ }
+
+ LLSD& ImplMap::ref(const LLSD::String& k)
+ {
+ return mData[k];
+ }
+
+ const LLSD& ImplMap::ref(const LLSD::String& k) const
+ {
+ DataMap::const_iterator i = mData.lower_bound(k);
+ if (i == mData.end() || mData.key_comp()(k, i->first))
+ {
+ return undef();
+ }
+
+ return i->second;
+ }
+
+ class ImplArray : public LLSD::Impl
+ {
+ private:
+ typedef std::vector<LLSD> DataVector;
+
+ DataVector mData;
+
+ protected:
+ ImplArray(const DataVector& data) : mData(data) { }
+
+ public:
+ ImplArray() { }
+
+ virtual ImplArray& makeArray(Impl*&);
+
+ virtual LLSD::Type type() const { return LLSD::TypeArray; }
+
+ virtual LLSD::Boolean asBoolean() const { return !mData.empty(); }
+
+ virtual int size() const;
+ virtual LLSD get(LLSD::Integer) const;
+ void set(LLSD::Integer, const LLSD&);
+ void insert(LLSD::Integer, const LLSD&);
+ void append(const LLSD&);
+ virtual void erase(LLSD::Integer);
+ LLSD& ref(LLSD::Integer);
+ virtual const LLSD& ref(LLSD::Integer) const;
+
+ LLSD::array_iterator beginArray() { return mData.begin(); }
+ LLSD::array_iterator endArray() { return mData.end(); }
+ virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); }
+ virtual LLSD::array_const_iterator endArray() const { return mData.end(); }
+ };
+
+ ImplArray& ImplArray::makeArray(Impl*& var)
+ {
+ if (shared())
+ {
+ ImplArray* i = new ImplArray(mData);
+ Impl::assign(var, i);
+ return *i;
+ }
+ else
+ {
+ return *this;
+ }
+ }
+
+ int ImplArray::size() const { return mData.size(); }
+
+ LLSD ImplArray::get(LLSD::Integer i) const
+ {
+ if (i < 0) { return LLSD(); }
+ DataVector::size_type index = i;
+
+ return (index < mData.size()) ? mData[index] : LLSD();
+ }
+
+ void ImplArray::set(LLSD::Integer i, const LLSD& v)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ mData.resize(index + 1);
+ }
+
+ mData[index] = v;
+ }
+
+ void ImplArray::insert(LLSD::Integer i, const LLSD& v)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ mData.resize(index + 1);
+ }
+
+ mData.insert(mData.begin() + index, v);
+ }
+
+ void ImplArray::append(const LLSD& v)
+ {
+ mData.push_back(v);
+ }
+
+ void ImplArray::erase(LLSD::Integer i)
+ {
+ if (i < 0) { return; }
+ DataVector::size_type index = i;
+
+ if (index < mData.size())
+ {
+ mData.erase(mData.begin() + index);
+ }
+ }
+
+ LLSD& ImplArray::ref(LLSD::Integer i)
+ {
+ DataVector::size_type index = i >= 0 ? i : 0;
+
+ if (index >= mData.size())
+ {
+ mData.resize(i + 1);
+ }
+
+ return mData[index];
+ }
+
+ const LLSD& ImplArray::ref(LLSD::Integer i) const
+ {
+ if (i < 0) { return undef(); }
+ DataVector::size_type index = i;
+
+ if (index >= mData.size())
+ {
+ return undef();
+ }
+
+ return mData[index];
+ }
+}
+
+LLSD::Impl::Impl()
+ : mUseCount(0)
+{
+ ++sAllocationCount;
+ ++sOutstandingCount;
+}
+
+LLSD::Impl::Impl(StaticAllocationMarker)
+ : mUseCount(0)
+{
+}
+
+LLSD::Impl::~Impl()
+{
+ --sOutstandingCount;
+}
+
+void LLSD::Impl::reset(Impl*& var, Impl* impl)
+{
+ if (impl) ++impl->mUseCount;
+ if (var && --var->mUseCount == 0)
+ {
+ delete var;
+ }
+ var = impl;
+}
+
+LLSD::Impl& LLSD::Impl::safe(Impl* impl)
+{
+ static Impl theUndefined(STATIC);
+ return impl ? *impl : theUndefined;
+}
+
+const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
+{
+ static Impl theUndefined(STATIC);
+ return impl ? *impl : theUndefined;
+}
+
+ImplMap& LLSD::Impl::makeMap(Impl*& var)
+{
+ ImplMap* im = new ImplMap;
+ reset(var, im);
+ return *im;
+}
+
+ImplArray& LLSD::Impl::makeArray(Impl*& var)
+{
+ ImplArray* ia = new ImplArray;
+ reset(var, ia);
+ return *ia;
+}
+
+
+void LLSD::Impl::assign(Impl*& var, const Impl* other)
+{
+ reset(var, const_cast<Impl*>(other));
+}
+
+void LLSD::Impl::assignUndefined(Impl*& var)
+{
+ reset(var, 0);
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Boolean v)
+{
+ reset(var, new ImplBoolean(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Integer v)
+{
+ reset(var, new ImplInteger(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, LLSD::Real v)
+{
+ reset(var, new ImplReal(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::String& v)
+{
+ reset(var, new ImplString(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::UUID& v)
+{
+ reset(var, new ImplUUID(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::Date& v)
+{
+ reset(var, new ImplDate(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::URI& v)
+{
+ reset(var, new ImplURI(v));
+}
+
+void LLSD::Impl::assign(Impl*& var, const LLSD::Binary& v)
+{
+ reset(var, new ImplBinary(v));
+}
+
+
+const LLSD& LLSD::Impl::undef()
+{
+ static const LLSD immutableUndefined;
+ return immutableUndefined;
+}
+
+U32 LLSD::Impl::sAllocationCount = 0;
+U32 LLSD::Impl::sOutstandingCount = 0;
+
+
+
+namespace {
+ inline LLSD::Impl& safe(LLSD::Impl* impl)
+ { return LLSD::Impl::safe(impl); }
+
+ inline const LLSD::Impl& safe(const LLSD::Impl* impl)
+ { return LLSD::Impl::safe(impl); }
+
+ inline ImplMap& makeMap(LLSD::Impl*& var)
+ { return safe(var).makeMap(var); }
+
+ inline ImplArray& makeArray(LLSD::Impl*& var)
+ { return safe(var).makeArray(var); }
+}
+
+
+LLSD::LLSD() : impl(0) { }
+LLSD::~LLSD() { Impl::reset(impl, 0); }
+
+LLSD::LLSD(const LLSD& other) : impl(0) { assign(other); }
+void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); }
+
+
+void LLSD::clear() { Impl::assignUndefined(impl); }
+
+LLSD::Type LLSD::type() const { return safe(impl).type(); }
+
+// Scaler Constructors
+LLSD::LLSD(Boolean v) : impl(0) { assign(v); }
+LLSD::LLSD(Integer v) : impl(0) { assign(v); }
+LLSD::LLSD(Real v) : impl(0) { assign(v); }
+LLSD::LLSD(const UUID& v) : impl(0) { assign(v); }
+LLSD::LLSD(const String& v) : impl(0) { assign(v); }
+LLSD::LLSD(const Date& v) : impl(0) { assign(v); }
+LLSD::LLSD(const URI& v) : impl(0) { assign(v); }
+LLSD::LLSD(const Binary& v) : impl(0) { assign(v); }
+
+// Convenience Constructors
+LLSD::LLSD(F32 v) : impl(0) { assign((Real)v); }
+
+// Scalar Assignment
+void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); }
+void LLSD::assign(Integer v) { safe(impl).assign(impl, v); }
+void LLSD::assign(Real v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const String& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const UUID& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const Date& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const URI& v) { safe(impl).assign(impl, v); }
+void LLSD::assign(const Binary& v) { safe(impl).assign(impl, v); }
+
+// Scalar Accessors
+LLSD::Boolean LLSD::asBoolean() const { return safe(impl).asBoolean(); }
+LLSD::Integer LLSD::asInteger() const { return safe(impl).asInteger(); }
+LLSD::Real LLSD::asReal() const { return safe(impl).asReal(); }
+LLSD::String LLSD::asString() const { return safe(impl).asString(); }
+LLSD::UUID LLSD::asUUID() const { return safe(impl).asUUID(); }
+LLSD::Date LLSD::asDate() const { return safe(impl).asDate(); }
+LLSD::URI LLSD::asURI() const { return safe(impl).asURI(); }
+LLSD::Binary LLSD::asBinary() const { return safe(impl).asBinary(); }
+
+// const char * helpers
+LLSD::LLSD(const char* v) : impl(0) { assign(v); }
+void LLSD::assign(const char* v)
+{
+ if(v) assign(std::string(v));
+ else assign(std::string());
+}
+
+
+LLSD LLSD::emptyMap()
+{
+ LLSD v;
+ makeMap(v.impl);
+ return v;
+}
+
+bool LLSD::has(const String& k) const { return safe(impl).has(k); }
+LLSD LLSD::get(const String& k) const { return safe(impl).get(k); }
+
+void LLSD::insert(const String& k, const LLSD& v)
+ { makeMap(impl).insert(k, v); }
+void LLSD::erase(const String& k) { makeMap(impl).erase(k); }
+
+LLSD& LLSD::operator[](const String& k)
+ { return makeMap(impl).ref(k); }
+const LLSD& LLSD::operator[](const String& k) const
+ { return safe(impl).ref(k); }
+
+
+LLSD LLSD::emptyArray()
+{
+ LLSD v;
+ makeArray(v.impl);
+ return v;
+}
+
+int LLSD::size() const { return safe(impl).size(); }
+
+LLSD LLSD::get(Integer i) const { return safe(impl).get(i); }
+void LLSD::set(Integer i, const LLSD& v){ makeArray(impl).set(i, v); }
+
+void LLSD::insert(Integer i, const LLSD& v)
+ { makeArray(impl).insert(i, v); }
+void LLSD::append(const LLSD& v) { makeArray(impl).append(v); }
+void LLSD::erase(Integer i) { makeArray(impl).erase(i); }
+
+LLSD& LLSD::operator[](Integer i)
+ { return makeArray(impl).ref(i); }
+const LLSD& LLSD::operator[](Integer i) const
+ { return safe(impl).ref(i); }
+
+U32 LLSD::allocationCount() { return Impl::sAllocationCount; }
+U32 LLSD::outstandingCount() { return Impl::sOutstandingCount; }
+
+LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); }
+LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); }
+LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); }
+LLSD::map_const_iterator LLSD::endMap() const { return safe(impl).endMap(); }
+
+LLSD::array_iterator LLSD::beginArray() { return makeArray(impl).beginArray(); }
+LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); }
+LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); }
+LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); }
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
new file mode 100644
index 0000000000..97bd6fe01c
--- /dev/null
+++ b/indra/llcommon/llsd.h
@@ -0,0 +1,366 @@
+/**
+ * @file llsd.h
+ * @brief LLSD flexible data system.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSD_NEW_H
+#define LL_LLSD_NEW_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "stdtypes.h"
+
+#include "lldate.h"
+#include "lluri.h"
+#include "../llmath/lluuid.h"
+
+/**
+ LLSD provides a flexible data system similar to the data facilities of
+ dynamic languages like Perl and Python. It is created to support exchange
+ of structured data between loosly coupled systems. (Here, "loosly coupled"
+ means not compiled together into the same module.)
+
+ Data in such exchanges must be highly tollerant of changes on either side
+ such as:
+ - recompilation
+ - implementation in a different langauge
+ - addition of extra parameters
+ - execution of older versions (with fewer parameters)
+
+ To this aim, the C++ API of LLSD strives to be very easy to use, and to
+ default to "the right thing" whereever possible. It is extremely tollerant
+ of errors and unexpected situations.
+
+ The fundimental class is LLSD. LLSD is a value holding object. It holds
+ one value that is either undefined, one of the scalar types, or a map or an
+ array. LLSD objects have value semantics (copying them copies the value,
+ though it can be considered efficient, due to shareing.), and mutable.
+
+ Undefined is the singular value given to LLSD objects that are not
+ initialized with any data. It is also used as the return value for
+ operations that return an LLSD,
+
+ The sclar data types are:
+ - Boolean - true or false
+ - Integer - a 32 bit signed integer
+ - Real - a 64 IEEE 754 floating point value
+ - UUID - a 128 unique value
+ - String - a sequence of zero or more Unicode chracters
+ - Date - an absolute point in time, UTC,
+ with resolution to the second
+ - URI - a String that is a URI
+ - Binary - a sequence of zero or more octets (unsigned bytes)
+
+ A map is a dictionary mapping String keys to LLSD values. The keys are
+ unique within a map, and have only one value (though that value could be
+ an LLSD array).
+
+ An array is a sequence of zero or more LLSD values.
+
+ @nosubgrouping
+*/
+
+class LLSD
+{
+public:
+ LLSD(); ///< initially Undefined
+ ~LLSD(); ///< this class may NOT be subclassed
+
+ /** @name Copyable and Assignable */
+ //@{
+ LLSD(const LLSD&);
+ void assign(const LLSD& other);
+ LLSD& operator=(const LLSD& other) { assign(other); return *this; }
+
+ //@}
+
+ void clear(); ///< resets to Undefined
+
+
+ /** @name Scalar Types
+ The scalar types, and how they map onto C++
+ */
+ //@{
+ typedef bool Boolean;
+ typedef S32 Integer;
+ typedef F64 Real;
+ typedef std::string String;
+ typedef LLUUID UUID;
+ typedef LLDate Date;
+ typedef LLURI URI;
+ typedef std::vector<U8> Binary;
+ //@}
+
+ /** @name Scalar Constructors */
+ //@{
+ LLSD(Boolean);
+ LLSD(Integer);
+ LLSD(Real);
+ LLSD(const String&);
+ LLSD(const UUID&);
+ LLSD(const Date&);
+ LLSD(const URI&);
+ LLSD(const Binary&);
+ //@}
+
+ /** @name Convenience Constructors */
+ //@{
+ LLSD(F32); // F32 -> Real
+ //@}
+
+ /** @name Scalar Assignment */
+ //@{
+ void assign(Boolean);
+ void assign(Integer);
+ void assign(Real);
+ void assign(const String&);
+ void assign(const UUID&);
+ void assign(const Date&);
+ void assign(const URI&);
+ void assign(const Binary&);
+
+ LLSD& operator=(Boolean v) { assign(v); return *this; }
+ LLSD& operator=(Integer v) { assign(v); return *this; }
+ LLSD& operator=(Real v) { assign(v); return *this; }
+ LLSD& operator=(const String& v) { assign(v); return *this; }
+ LLSD& operator=(const UUID& v) { assign(v); return *this; }
+ LLSD& operator=(const Date& v) { assign(v); return *this; }
+ LLSD& operator=(const URI& v) { assign(v); return *this; }
+ LLSD& operator=(const Binary& v) { assign(v); return *this; }
+ //@}
+
+ /**
+ @name Scalar Accessors
+ @brief Fetch a scalar value, converting if needed and possible
+
+ Conversion among the basic types, Boolean, Integer, Real and String, is
+ fully defined. Each type can be converted to another with a reasonable
+ interpretation. These conversions can be used as a convenience even
+ when you know the data is in one format, but you want it in another. Of
+ course, many of these conversions lose information.
+
+ Note: These conversions are not the same as Perl's. In particular, when
+ converting a String to a Boolean, only the empty string converts to
+ false. Converting the String "0" to Boolean results in true.
+
+ Conversion to and from UUID, Date, and URI is only defined to and from
+ String. Conversion is defined to be information preserving for valid
+ values of those types. These conversions can be used when one needs to
+ convert data to or from another system that cannot handle these types
+ natively, but can handle strings.
+
+ Conversion to and from Binary isn't defined.
+
+ Conversion of the Undefined value to any scalar type results in a
+ reasonable null or zero value for the type.
+ */
+ //@{
+ Boolean asBoolean() const;
+ Integer asInteger() const;
+ Real asReal() const;
+ String asString() const;
+ UUID asUUID() const;
+ Date asDate() const;
+ URI asURI() const;
+ Binary asBinary() const;
+
+ operator Boolean() const { return asBoolean(); }
+ operator Integer() const { return asInteger(); }
+ operator Real() const { return asReal(); }
+ operator String() const { return asString(); }
+ operator UUID() const { return asUUID(); }
+ operator Date() const { return asDate(); }
+ operator URI() const { return asURI(); }
+ operator Binary() const { return asBinary(); }
+
+ // This is needed because most platforms do not automatically
+ // convert the boolean negation as a bool in an if statement.
+ bool operator!() const {return !asBoolean();}
+ //@}
+
+ /** @name Character Pointer Helpers
+ These are helper routines to make working with char* the same as easy as
+ working with strings.
+ */
+ //@{
+ LLSD(const char*);
+ void assign(const char*);
+ LLSD& operator=(const char* v) { assign(v); return *this; }
+ //@}
+
+ /** @name Map Values */
+ //@{
+ static LLSD emptyMap();
+
+ bool has(const String&) const;
+ LLSD get(const String&) const;
+ void insert(const String&, const LLSD&);
+ void erase(const String&);
+
+ LLSD& operator[](const String&);
+ LLSD& operator[](const char* c) { return (*this)[String(c)]; }
+ const LLSD& operator[](const String&) const;
+ const LLSD& operator[](const char* c) const { return (*this)[String(c)]; }
+ //@}
+
+ /** @name Array Values */
+ //@{
+ static LLSD emptyArray();
+
+ LLSD get(Integer) const;
+ void set(Integer, const LLSD&);
+ void insert(Integer, const LLSD&);
+ void append(const LLSD&);
+ void erase(Integer);
+
+ const LLSD& operator[](Integer) const;
+ LLSD& operator[](Integer);
+ //@}
+
+ /** @name Iterators */
+ //@{
+ int size() const;
+
+ typedef std::map<String, LLSD>::iterator map_iterator;
+ typedef std::map<String, LLSD>::const_iterator map_const_iterator;
+
+ map_iterator beginMap();
+ map_iterator endMap();
+ map_const_iterator beginMap() const;
+ map_const_iterator endMap() const;
+
+ typedef std::vector<LLSD>::iterator array_iterator;
+ typedef std::vector<LLSD>::const_iterator array_const_iterator;
+
+ array_iterator beginArray();
+ array_iterator endArray();
+ array_const_iterator beginArray() const;
+ array_const_iterator endArray() const;
+ //@}
+
+ /** @name Type Testing */
+ //@{
+ enum Type {
+ TypeUndefined,
+ TypeBoolean,
+ TypeInteger,
+ TypeReal,
+ TypeString,
+ TypeUUID,
+ TypeDate,
+ TypeURI,
+ TypeBinary,
+ TypeMap,
+ TypeArray
+ };
+
+ Type type() const;
+
+ bool isUndefined() const { return type() == TypeUndefined; }
+ bool isDefined() const { return type() != TypeUndefined; }
+ bool isBoolean() const { return type() == TypeBoolean; }
+ bool isInteger() const { return type() == TypeInteger; }
+ bool isReal() const { return type() == TypeReal; }
+ bool isString() const { return type() == TypeString; }
+ bool isUUID() const { return type() == TypeUUID; }
+ bool isDate() const { return type() == TypeDate; }
+ bool isURI() const { return type() == TypeURI; }
+ bool isBinary() const { return type() == TypeBinary; }
+ bool isMap() const { return type() == TypeMap; }
+ bool isArray() const { return type() == TypeArray; }
+ //@}
+
+ /** @name Automatic Cast Protection
+ These are not implemented on purpose. Without them, C++ can perform
+ some conversions that are clearly not what the programmer intended.
+
+ If you get a linker error about these being missing, you have made
+ mistake in your code. DO NOT IMPLEMENT THESE FUNCTIONS as a fix.
+
+ All of thse problems stem from trying to support char* in LLSD or in
+ std::string. There are too many automatic casts that will lead to
+ using an arbitrary pointer or scalar type to std::string.
+ */
+ //@{
+ LLSD(const void*); ///< construct from aribrary pointers
+ void assign(const void*); ///< assign from arbitrary pointers
+ LLSD& operator=(const void*); ///< assign from arbitrary pointers
+
+ bool has(Integer) const; ///< has only works for Maps
+ //@}
+
+ /** @name Implementation */
+ //@{
+public:
+ class Impl;
+private:
+ Impl* impl;
+ //@}
+
+ /** @name Unit Testing Interface */
+ //@{
+public:
+ static U32 allocationCount(); ///< how many Impls have been made
+ static U32 outstandingCount(); ///< how many Impls are still alive
+ //@}
+};
+
+struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>
+{
+ LLSD::Boolean operator()(const LLSD& sd) const
+ {
+ return sd.asBoolean();
+ }
+};
+struct llsd_select_integer : public std::unary_function<LLSD, LLSD::Integer>
+{
+ LLSD::Integer operator()(const LLSD& sd) const
+ {
+ return sd.asInteger();
+ }
+};
+struct llsd_select_real : public std::unary_function<LLSD, LLSD::Real>
+{
+ LLSD::Real operator()(const LLSD& sd) const
+ {
+ return sd.asReal();
+ }
+};
+struct llsd_select_float : public std::unary_function<LLSD, F32>
+{
+ F32 operator()(const LLSD& sd) const
+ {
+ return (F32)sd.asReal();
+ }
+};
+struct llsd_select_uuid : public std::unary_function<LLSD, LLSD::UUID>
+{
+ LLSD::UUID operator()(const LLSD& sd) const
+ {
+ return sd.asUUID();
+ }
+};
+struct llsd_select_string : public std::unary_function<LLSD, LLSD::String>
+{
+ LLSD::String operator()(const LLSD& sd) const
+ {
+ return sd.asString();
+ }
+};
+
+
+/** QUESTIONS & TO DOS
+ - Would Binary be more convenient as usigned char* buffer semantics?
+ - Should Binary be convertable to/from String, and if so how?
+ - as UTF8 encoded strings (making not like UUID<->String)
+ - as Base64 or Base96 encoded (making like UUID<->String)
+ - Conversions to std::string and LLUUID do not result in easy assignment
+ to std::string, LLString or LLUUID due to non-unique conversion paths
+*/
+
+#endif // LL_LLSD_NEW_H
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
new file mode 100644
index 0000000000..fb8efc91ca
--- /dev/null
+++ b/indra/llcommon/llsdserialize.cpp
@@ -0,0 +1,1621 @@
+/**
+ * @file llsdserialize.cpp
+ * @author Phoenix
+ * @date 2006-03-05
+ * @brief Implementation of LLSD parsers and formatters
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize.h"
+#include "llmemory.h"
+#include "llstreamtools.h" // for fullread
+
+#include <iostream>
+#include "apr-1/apr_base64.h"
+
+#if !LL_WINDOWS
+#include <netinet/in.h> // htonl & ntohl
+#endif
+
+#include "lldate.h"
+#include "llsd.h"
+#include "lluri.h"
+
+// File constants
+static const int MAX_HDR_LEN = 20;
+static const char LEGACY_NON_HEADER[] = "<llsd>";
+
+//static
+const char* LLSDSerialize::LLSDBinaryHeader = "LLSD/Binary";
+
+//static
+const char* LLSDSerialize::LLSDXMLHeader = "LLSD/XML";
+
+// virtual
+LLSDParser::~LLSDParser()
+{ }
+
+// virtual
+LLSDNotationParser::~LLSDNotationParser()
+{ }
+
+
+// static
+void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, U32 options)
+{
+ LLPointer<LLSDFormatter> f = NULL;
+
+ switch (type)
+ {
+ case LLSD_BINARY:
+ str << "<? " << LLSDBinaryHeader << " ?>\n";
+ f = new LLSDBinaryFormatter;
+ break;
+
+ case LLSD_XML:
+ str << "<? " << LLSDXMLHeader << " ?>\n";
+ f = new LLSDXMLFormatter;
+ break;
+
+ default:
+ llwarns << "serialize request for unkown ELLSD_Serialize" << llendl;
+ }
+
+ if (f.notNull())
+ {
+ f->format(sd, str, options);
+ }
+}
+
+// static
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str)
+{
+ LLPointer<LLSDParser> p = NULL;
+ char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
+ int i;
+ int inbuf = 0;
+ bool legacy_no_header = false;
+ bool fail_if_not_legacy = false;
+ std::string header = "";
+
+ /*
+ * Get the first line before anything.
+ */
+ str.get(hdr_buf, MAX_HDR_LEN, '\n');
+ if (str.fail())
+ {
+ str.clear();
+ fail_if_not_legacy = true;
+ }
+
+ if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ {
+ legacy_no_header = true;
+ inbuf = str.gcount();
+ }
+ else
+ {
+ if (fail_if_not_legacy)
+ goto fail;
+ /*
+ * Remove the newline chars
+ */
+ for (i = 0; i < MAX_HDR_LEN; i++)
+ {
+ if (hdr_buf[i] == 0 || hdr_buf[i] == '\r' ||
+ hdr_buf[i] == '\n')
+ {
+ hdr_buf[i] = 0;
+ break;
+ }
+ }
+ header = hdr_buf;
+
+ std::string::size_type start = std::string::npos;
+ std::string::size_type end = std::string::npos;
+ start = header.find_first_not_of("<? ");
+ if (start != std::string::npos)
+ {
+ end = header.find_first_of(" ?", start);
+ }
+ if ((start == std::string::npos) || (end == std::string::npos))
+ goto fail;
+
+ header = header.substr(start, end - start);
+ ws(str);
+ }
+ /*
+ * Create the parser as appropriate
+ */
+ if (legacy_no_header)
+ {
+ LLSDXMLParser *x = new LLSDXMLParser;
+ x->parsePart(hdr_buf, inbuf);
+ p = x;
+ }
+ else if (header == LLSDBinaryHeader)
+ {
+ p = new LLSDBinaryParser;
+ }
+ else if (header == LLSDXMLHeader)
+ {
+ p = new LLSDXMLParser;
+ }
+ else
+ {
+ llwarns << "deserialize request for unknown ELLSD_Serialize" << llendl;
+ }
+
+ if (p.notNull())
+ {
+ p->parse(str, sd);
+ return true;
+ }
+
+fail:
+ llwarns << "deserialize LLSD parse failure" << llendl;
+ return false;
+}
+
+/**
+ * Endian handlers
+ */
+#if LL_BIG_ENDIAN
+U64 ll_htonll(U64 hostlonglong) { return hostlonglong; }
+U64 ll_ntohll(U64 netlonglong) { return netlonglong; }
+F64 ll_htond(F64 hostlonglong) { return hostlonglong; }
+F64 ll_ntohd(F64 netlonglong) { return netlonglong; }
+#else
+// I read some comments one a indicating that doing an integer add
+// here would be faster than a bitwise or. For now, the or has
+// programmer clarity, since the intended outcome matches the
+// operation.
+U64 ll_htonll(U64 hostlonglong)
+{
+ return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32));
+}
+U64 ll_ntohll(U64 netlonglong)
+{
+ return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32));
+}
+union LLEndianSwapper
+{
+ F64 d;
+ U64 i;
+};
+F64 ll_htond(F64 hostdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = hostdouble;
+ tmp.i = ll_htonll(tmp.i);
+ return tmp.d;
+}
+F64 ll_ntohd(F64 netdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = netdouble;
+ tmp.i = ll_ntohll(tmp.i);
+ return tmp.d;
+}
+#endif
+
+/**
+ * Local functions.
+ */
+bool deserialize_string(std::istream& str, std::string& value);
+bool deserialize_string_delim(std::istream& str, std::string& value, char d);
+bool deserialize_string_raw(std::istream& str, std::string& value);
+void serialize_string(const std::string& value, std::ostream& str);
+
+/**
+ * Local constants.
+ */
+static const std::string NOTATION_TRUE_SERIAL("true");
+static const std::string NOTATION_FALSE_SERIAL("false");
+
+static const char BINARY_TRUE_SERIAL = '1';
+static const char BINARY_FALSE_SERIAL = '0';
+
+static const S32 NOTATION_PARSE_FAILURE = -1;
+
+/**
+ * LLSDParser
+ */
+LLSDParser::LLSDParser()
+{
+}
+
+/**
+ * LLSDNotationParser
+ */
+// virtual
+S32 LLSDNotationParser::parse(std::istream& istr, LLSD& data) const
+{
+ // map: { string:object, string:object }
+ // array: [ object, object, object ]
+ // undef: !
+ // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
+ // integer: i####
+ // real: r####
+ // uuid: u####
+ // string: "g'day" | 'have a "nice" day' | s(size)"raw data"
+ // uri: l"escaped"
+ // date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
+ // binary: b##"ff3120ab1" | b(size)"raw data"
+ char c;
+ c = istr.peek();
+ while(isspace(c))
+ {
+ // pop the whitespace.
+ c = istr.get();
+ c = istr.peek();
+ continue;
+ }
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading map." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading array." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case '!':
+ c = istr.get();
+ data.clear();
+ break;
+
+ case '0':
+ c = istr.get();
+ data = false;
+ break;
+
+ case 'F':
+ case 'f':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = false;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case '1':
+ c = istr.get();
+ data = true;
+ break;
+
+ case 'T':
+ case 't':
+ do
+ {
+ istr.ignore();
+ c = istr.peek();
+ } while (isalpha(c));
+ data = true;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading boolean." << llendl;
+ }
+ break;
+
+ case 'i':
+ {
+ c = istr.get();
+ S32 integer = 0;
+ istr >> integer;
+ data = integer;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ c = istr.get();
+ F64 real = 0.0;
+ istr >> real;
+ data = real;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ c = istr.get();
+ LLUUID id;
+ istr >> id;
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\"':
+ case '\'':
+ case 's':
+ parseString(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading string." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ case 'l':
+ {
+ c = istr.get(); // pop the 'l'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLURI(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ c = istr.get(); // pop the 'd'
+ c = istr.get(); // pop the delimiter
+ std::string str;
+ deserialize_string_delim(istr, str, c);
+ data = LLDate(str);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ parseBinary(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading data." << llendl;
+ }
+ if(data.isUndefined())
+ {
+ parse_count = NOTATION_PARSE_FAILURE;
+ }
+ break;
+
+ default:
+ data.clear();
+ parse_count = NOTATION_PARSE_FAILURE;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDNotationParser::parse(std::istream& istr)
+{
+ LLSDNotationParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDNotationParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ // map: { string:object, string:object }
+ map = LLSD::emptyMap();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '{')
+ {
+ // eat commas, white
+ bool found_name = false;
+ std::string name;
+ c = istr.get();
+ while(c != '}' && istr.good())
+ {
+ if(!found_name)
+ {
+ if((c == '\"') || (c == '\'') || (c == 's'))
+ {
+ istr.putback(c);
+ found_name = true;
+ deserialize_string(istr, name);
+ }
+ c = istr.get();
+ }
+ else
+ {
+ if(isspace(c) || (c == ':'))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ LLSD child;
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ map.insert(name, child);
+ }
+ else
+ {
+ map.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ found_name = false;
+ c = istr.get();
+ }
+ }
+ }
+ return parse_count;
+}
+
+S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ // array: [ object, object, object ]
+ array = LLSD::emptyArray();
+ S32 parse_count = 0;
+ char c = istr.get();
+ if(c == '[')
+ {
+ // eat commas, white
+ c = istr.get();
+ while((c != ']') && istr.good())
+ {
+ LLSD child;
+ if(isspace(c) || (c == ','))
+ {
+ c = istr.get();
+ continue;
+ }
+ istr.putback(c);
+ S32 count = parse(istr, child);
+ if(count > 0)
+ {
+ parse_count += count;
+ array.append(child);
+ }
+ else
+ {
+ array.clear();
+ return NOTATION_PARSE_FAILURE;
+ }
+ c = istr.get();
+ }
+ }
+ return parse_count;
+}
+
+void LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
+{
+ std::string value;
+ if(deserialize_string(istr, value))
+ {
+ data = value;
+ }
+ else
+ {
+ // failed to parse.
+ data.clear();
+ }
+}
+
+void LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
+{
+ // binary: b##"ff3120ab1"
+ // or: b(len)"..."
+
+ // I want to manually control those values here to make sure the
+ // parser doesn't break when someone changes a constant somewhere
+ // else.
+ const U32 BINARY_BUFFER_SIZE = 256;
+ const U32 STREAM_GET_COUNT = 255;
+
+ // need to read the base out.
+ char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ char c = istr.get();
+ if((c == '"') && (0 == strncmp("b(", buf, 2)))
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ S32 len = strtol(buf + 2, NULL, 0);
+ std::vector<U8> value;
+ if(len)
+ {
+ value.resize(len);
+ fullread(istr, (char *)&value[0], len);
+ }
+ c = istr.get(); // strip off the trailing double-quote
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b64", buf, 3)))
+ {
+ // *FIX: A bit inefficient, but works for now. To make the
+ // format better, I would need to add a hint into the
+ // serialization format that indicated how long it was.
+ std::stringstream coded_stream;
+ istr.get(*(coded_stream.rdbuf()), '\"');
+ c = istr.get();
+ std::string encoded(coded_stream.str());
+ S32 len = apr_base64_decode_len(encoded.c_str());
+ std::vector<U8> value;
+ value.resize(len);
+ len = apr_base64_decode_binary(&value[0], encoded.c_str());
+ value.resize(len);
+ data = value;
+ }
+ else if((c == '"') && (0 == strncmp("b16", buf, 3)))
+ {
+ // yay, base 16. We pop the next character which is either a
+ // double quote or base 16 data. If it's a double quote, we're
+ // done parsing. If it's not, put the data back, and read the
+ // stream until the next double quote.
+ char* read; /*Flawfinder: ignore*/
+ U8 byte;
+ U8 byte_buffer[BINARY_BUFFER_SIZE];
+ U8* write;
+ std::vector<U8> value;
+ c = istr.get();
+ while(c != '"')
+ {
+ istr.putback(c);
+ read = buf;
+ write = byte_buffer;
+ istr.get(buf, STREAM_GET_COUNT, '"');
+ c = istr.get();
+ while(*read != '\0') /*Flawfinder: ignore*/
+ {
+ byte = hex_as_nybble(*read++);
+ byte = byte << 4;
+ byte |= hex_as_nybble(*read++);
+ *write++ = byte;
+ }
+ // copy the data out of the byte buffer
+ value.insert(value.end(), byte_buffer, write);
+ }
+ data = value;
+ }
+ else
+ {
+ data.clear();
+ }
+}
+
+
+/**
+ * LLSDBinaryParser
+ */
+LLSDBinaryParser::LLSDBinaryParser()
+{
+}
+
+// virtual
+LLSDBinaryParser::~LLSDBinaryParser()
+{
+}
+
+// virtual
+S32 LLSDBinaryParser::parse(std::istream& istr, LLSD& data) const
+{
+/**
+ * Undefined: '!'<br>
+ * Boolean: 't' for true 'f' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * strings also secretly support the notation format
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as s + 4 byte integer size + string or in the
+ * notation format.
+ */
+ char c;
+ c = istr.get();
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ parse_count += parseMap(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary map." << llendl;
+ }
+ break;
+
+ case '[':
+ parse_count += parseArray(istr, data);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary array." << llendl;
+ }
+ break;
+
+ case '!':
+ data.clear();
+ break;
+
+ case '0':
+ data = false;
+ break;
+
+ case '1':
+ data = true;
+ break;
+
+ case 'i':
+ {
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ data = (S32)ntohl(value_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary integer." << llendl;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ F64 real_nbo = 0.0;
+ istr.read((char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
+ data = ll_ntohd(real_nbo);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary real." << llendl;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ LLUUID id;
+ istr.read((char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
+ data = id;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary uuid." << llendl;
+ }
+ break;
+ }
+
+ case '\'':
+ case '"':
+ {
+ std::string value;
+ deserialize_string_delim(istr, value, c);
+ data = value;
+ break;
+ }
+
+ case 's':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary string." << llendl;
+ }
+ break;
+ }
+
+ case 'l':
+ {
+ std::string value;
+ parseString(istr, value);
+ data = LLURI(value);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary link." << llendl;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ F64 real = 0.0;
+ istr.read((char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
+ data = LLDate(real);
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary date." << llendl;
+ }
+ break;
+ }
+
+ case 'b':
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ U32 size_nbo = 0;
+ istr.read((char*)&size_nbo, sizeof(U32));
+ S32 size = (S32)ntohl(size_nbo);
+ std::vector<U8> value;
+ if(size)
+ {
+ value.resize(size);
+ istr.read((char*)&value[0], size); /*Flawfinder: ignore*/
+ }
+ data = value;
+ if(istr.fail())
+ {
+ llinfos << "STREAM FAILURE reading binary." << llendl;
+ }
+ break;
+ }
+
+ default:
+ --parse_count;
+ llinfos << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << llendl;
+ break;
+ }
+ return parse_count;
+}
+
+// static
+LLSD LLSDBinaryParser::parse(std::istream& istr)
+{
+ LLSDBinaryParser parser;
+ LLSD rv;
+ S32 count = parser.parse(istr, rv);
+ lldebugs << "LLSDBinaryParser::parse parsed " << count << " objects."
+ << llendl;
+ return rv;
+}
+
+S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map) const
+{
+ map = LLSD::emptyMap();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.get();
+ while(c != '}' && (count < size) && istr.good())
+ {
+ std::string name;
+ switch(c)
+ {
+ case 'k':
+ parseString(istr, name);
+ break;
+ case '\'':
+ case '"':
+ deserialize_string_delim(istr, name, c);
+ break;
+ }
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ map.insert(name, child);
+ }
+ ++count;
+ c = istr.get();
+ }
+ return parse_count;
+}
+
+S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array) const
+{
+ array = LLSD::emptyArray();
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+
+ // *FIX: This would be a good place to reserve some space in the
+ // array...
+
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.peek();
+ while((c != ']') && (count < size) && istr.good())
+ {
+ LLSD child;
+ S32 child_count = parse(istr, child);
+ if(child_count)
+ {
+ parse_count += child_count;
+ array.append(child);
+ }
+ ++count;
+ c = istr.peek();
+ }
+ c = istr.get();
+ return parse_count;
+}
+
+void LLSDBinaryParser::parseString(
+ std::istream& istr,
+ std::string& value) const
+{
+ // *FIX: This is memory inefficient.
+ U32 value_nbo = 0;
+ istr.read((char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ std::vector<char> buf;
+ buf.resize(size);
+ istr.read(&buf[0], size); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+}
+
+
+/**
+ * LLSDFormatter
+ */
+LLSDFormatter::LLSDFormatter() :
+ mBoolAlpha(false)
+{
+}
+
+// virtual
+LLSDFormatter::~LLSDFormatter()
+{ }
+
+void LLSDFormatter::boolalpha(bool alpha)
+{
+ mBoolAlpha = alpha;
+}
+
+void LLSDFormatter::realFormat(const std::string& format)
+{
+ mRealFormat = format;
+}
+
+void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const
+{
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, mRealFormat.c_str(), real);
+ ostr << buffer;
+}
+
+/**
+ * LLSDNotationFormatter
+ */
+LLSDNotationFormatter::LLSDNotationFormatter()
+{
+}
+
+// virtual
+LLSDNotationFormatter::~LLSDNotationFormatter()
+{ }
+
+// static
+std::string LLSDNotationFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream ostr;
+ serialize_string(in, ostr);
+ return ostr.str();
+}
+
+// virtual
+S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr << "{";
+ bool need_comma = false;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ ostr << '\'';
+ serialize_string((*iter).first, ostr);
+ ostr << "':";
+ format_count += format((*iter).second, ostr);
+ }
+ ostr << "}";
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr << "[";
+ bool need_comma = false;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ format_count += format(*iter, ostr);
+ }
+ ostr << "]";
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr << "!";
+ break;
+
+ case LLSD::TypeBoolean:
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean()
+ ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL);
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << "i" << data.asInteger();
+ break;
+
+ case LLSD::TypeReal:
+ ostr << "r";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ break;
+
+ case LLSD::TypeUUID:
+ ostr << "u" << data.asUUID();
+ break;
+
+ case LLSD::TypeString:
+ ostr << '\'';
+ serialize_string(data.asString(), ostr);
+ ostr << '\'';
+ break;
+
+ case LLSD::TypeDate:
+ ostr << "d\"" << data.asDate() << "\"";
+ break;
+
+ case LLSD::TypeURI:
+ ostr << "l\"";
+ serialize_string(data.asString(), ostr);
+ ostr << "\"";
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ std::vector<U8> buffer = data.asBinary();
+ ostr << "b(" << buffer.size() << ")\"";
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ ostr << "\"";
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr << "!";
+ break;
+ }
+ return format_count;
+}
+
+
+/**
+ * LLSDBinaryFormatter
+ */
+LLSDBinaryFormatter::LLSDBinaryFormatter()
+{
+}
+
+// virtual
+LLSDBinaryFormatter::~LLSDBinaryFormatter()
+{ }
+
+// virtual
+S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr.put('{');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr.put('k');
+ formatString((*iter).first, ostr);
+ format_count += format((*iter).second, ostr);
+ }
+ ostr.put('}');
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr.put('[');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format(*iter, ostr);
+ }
+ ostr.put(']');
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr.put('!');
+ break;
+
+ case LLSD::TypeBoolean:
+ if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL);
+ else ostr.put(BINARY_FALSE_SERIAL);
+ break;
+
+ case LLSD::TypeInteger:
+ {
+ ostr.put('i');
+ U32 value_nbo = htonl(data.asInteger());
+ ostr.write((const char*)(&value_nbo), sizeof(U32));
+ break;
+ }
+
+ case LLSD::TypeReal:
+ {
+ ostr.put('r');
+ F64 value_nbo = ll_htond(data.asReal());
+ ostr.write((const char*)(&value_nbo), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeUUID:
+ ostr.put('u');
+ ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES);
+ break;
+
+ case LLSD::TypeString:
+ ostr.put('s');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeDate:
+ {
+ ostr.put('d');
+ F64 value = data.asReal();
+ ostr.write((const char*)(&value), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeURI:
+ ostr.put('l');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ ostr.put('b');
+ std::vector<U8> buffer = data.asBinary();
+ U32 size_nbo = htonl(buffer.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr.put('!');
+ break;
+ }
+ return format_count;
+}
+
+void LLSDBinaryFormatter::formatString(
+ const std::string& string,
+ std::ostream& ostr) const
+{
+ U32 size_nbo = htonl(string.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ ostr.write(string.c_str(), string.size());
+}
+
+/**
+ * local functions
+ */
+bool deserialize_string(std::istream& str, std::string& value)
+{
+ char c = str.get();
+ if (str.fail())
+ {
+ // No data in stream, bail out
+ return false;
+ }
+
+ bool rv = false;
+ switch(c)
+ {
+ case '\'':
+ case '"':
+ rv = deserialize_string_delim(str, value, c);
+ break;
+ case 's':
+ rv = deserialize_string_raw(str, value);
+ break;
+ default:
+ break;
+ }
+ return rv;
+}
+
+bool deserialize_string_delim(
+ std::istream& str,
+ std::string& value,
+ char delim)
+{
+ std::ostringstream write_buffer;
+ bool found_escape = false;
+ bool found_hex = false;
+ bool found_digit = false;
+ U8 byte = 0;
+
+ while (true)
+ {
+ char next_char = str.get();
+
+ if(str.fail())
+ {
+ // If our stream is empty, break out
+ value = write_buffer.str();
+ return false;
+ }
+
+ if(found_escape)
+ {
+ // next character(s) is a special sequence.
+ if(found_hex)
+ {
+ if(found_digit)
+ {
+ found_digit = false;
+ found_hex = false;
+ found_escape = false;
+ byte = byte << 4;
+ byte |= hex_as_nybble(next_char);
+ write_buffer << byte;
+ byte = 0;
+ }
+ else
+ {
+ // next character is the first nybble of
+ //
+ found_digit = true;
+ byte = hex_as_nybble(next_char);
+ }
+ }
+ else if(next_char == 'x')
+ {
+ found_hex = true;
+ }
+ else
+ {
+ switch(next_char)
+ {
+ case 'a':
+ write_buffer << '\a';
+ break;
+ case 'b':
+ write_buffer << '\b';
+ break;
+ case 'f':
+ write_buffer << '\f';
+ break;
+ case 'n':
+ write_buffer << '\n';
+ break;
+ case 'r':
+ write_buffer << '\r';
+ break;
+ case 't':
+ write_buffer << '\t';
+ break;
+ case 'v':
+ write_buffer << '\v';
+ break;
+ default:
+ write_buffer << next_char;
+ break;
+ }
+ found_escape = false;
+ }
+ }
+ else if(next_char == '\\')
+ {
+ found_escape = true;
+ }
+ else if(next_char == delim)
+ {
+ break;
+ }
+ else
+ {
+ write_buffer << next_char;
+ }
+ }
+
+ value = write_buffer.str();
+ return true;
+}
+
+bool deserialize_string_raw(std::istream& str, std::string& value)
+{
+ bool ok = false;
+ const S32 BUF_LEN = 20;
+ char buf[BUF_LEN]; /* Flawfinder: ignore */
+ str.get(buf, BUF_LEN - 1, ')');
+ char c = str.get();
+ c = str.get();
+ if(((c == '"') || (c == '\'')) && (buf[0] == '('))
+ {
+ // We probably have a valid raw string. determine
+ // the size, and read it.
+ // *FIX: Should we set a maximum size?
+ // *FIX: This is memory inefficient.
+ S32 len = strtol(buf + 1, NULL, 0);
+ std::vector<char> buf;
+ buf.resize(len);
+ str.read(&buf[0], len); /*Flawfinder: ignore*/
+ value.assign(buf.begin(), buf.end());
+ c = str.get();
+ if((c == '"') || (c == '\''))
+ {
+ ok = true;
+ }
+ }
+ return ok;
+}
+
+static const char* NOTATION_STRING_CHARACTERS[256] =
+{
+ "\\x00", // 0
+ "\\x01", // 1
+ "\\x02", // 2
+ "\\x03", // 3
+ "\\x04", // 4
+ "\\x05", // 5
+ "\\x06", // 6
+ "\\a", // 7
+ "\\b", // 8
+ "\\t", // 9
+ "\\n", // 10
+ "\\v", // 11
+ "\\f", // 12
+ "\\r", // 13
+ "\\x0e", // 14
+ "\\x0f", // 15
+ "\\x10", // 16
+ "\\x11", // 17
+ "\\x12", // 18
+ "\\x13", // 19
+ "\\x14", // 20
+ "\\x15", // 21
+ "\\x16", // 22
+ "\\x17", // 23
+ "\\x18", // 24
+ "\\x19", // 25
+ "\\x1a", // 26
+ "\\x1b", // 27
+ "\\x1c", // 28
+ "\\x1d", // 29
+ "\\x1e", // 30
+ "\\x1f", // 31
+ " ", // 32
+ "!", // 33
+ "\"", // 34
+ "#", // 35
+ "$", // 36
+ "%", // 37
+ "&", // 38
+ "\\'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "<", // 60
+ "=", // 61
+ ">", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "[", // 91
+ "\\\\", // 92
+ "]", // 93
+ "^", // 94
+ "_", // 95
+ "`", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "{", // 123
+ "|", // 124
+ "}", // 125
+ "~", // 126
+ "\\x7f", // 127
+ "\\x80", // 128
+ "\\x81", // 129
+ "\\x82", // 130
+ "\\x83", // 131
+ "\\x84", // 132
+ "\\x85", // 133
+ "\\x86", // 134
+ "\\x87", // 135
+ "\\x88", // 136
+ "\\x89", // 137
+ "\\x8a", // 138
+ "\\x8b", // 139
+ "\\x8c", // 140
+ "\\x8d", // 141
+ "\\x8e", // 142
+ "\\x8f", // 143
+ "\\x90", // 144
+ "\\x91", // 145
+ "\\x92", // 146
+ "\\x93", // 147
+ "\\x94", // 148
+ "\\x95", // 149
+ "\\x96", // 150
+ "\\x97", // 151
+ "\\x98", // 152
+ "\\x99", // 153
+ "\\x9a", // 154
+ "\\x9b", // 155
+ "\\x9c", // 156
+ "\\x9d", // 157
+ "\\x9e", // 158
+ "\\x9f", // 159
+ "\\xa0", // 160
+ "\\xa1", // 161
+ "\\xa2", // 162
+ "\\xa3", // 163
+ "\\xa4", // 164
+ "\\xa5", // 165
+ "\\xa6", // 166
+ "\\xa7", // 167
+ "\\xa8", // 168
+ "\\xa9", // 169
+ "\\xaa", // 170
+ "\\xab", // 171
+ "\\xac", // 172
+ "\\xad", // 173
+ "\\xae", // 174
+ "\\xaf", // 175
+ "\\xb0", // 176
+ "\\xb1", // 177
+ "\\xb2", // 178
+ "\\xb3", // 179
+ "\\xb4", // 180
+ "\\xb5", // 181
+ "\\xb6", // 182
+ "\\xb7", // 183
+ "\\xb8", // 184
+ "\\xb9", // 185
+ "\\xba", // 186
+ "\\xbb", // 187
+ "\\xbc", // 188
+ "\\xbd", // 189
+ "\\xbe", // 190
+ "\\xbf", // 191
+ "\\xc0", // 192
+ "\\xc1", // 193
+ "\\xc2", // 194
+ "\\xc3", // 195
+ "\\xc4", // 196
+ "\\xc5", // 197
+ "\\xc6", // 198
+ "\\xc7", // 199
+ "\\xc8", // 200
+ "\\xc9", // 201
+ "\\xca", // 202
+ "\\xcb", // 203
+ "\\xcc", // 204
+ "\\xcd", // 205
+ "\\xce", // 206
+ "\\xcf", // 207
+ "\\xd0", // 208
+ "\\xd1", // 209
+ "\\xd2", // 210
+ "\\xd3", // 211
+ "\\xd4", // 212
+ "\\xd5", // 213
+ "\\xd6", // 214
+ "\\xd7", // 215
+ "\\xd8", // 216
+ "\\xd9", // 217
+ "\\xda", // 218
+ "\\xdb", // 219
+ "\\xdc", // 220
+ "\\xdd", // 221
+ "\\xde", // 222
+ "\\xdf", // 223
+ "\\xe0", // 224
+ "\\xe1", // 225
+ "\\xe2", // 226
+ "\\xe3", // 227
+ "\\xe4", // 228
+ "\\xe5", // 229
+ "\\xe6", // 230
+ "\\xe7", // 231
+ "\\xe8", // 232
+ "\\xe9", // 233
+ "\\xea", // 234
+ "\\xeb", // 235
+ "\\xec", // 236
+ "\\xed", // 237
+ "\\xee", // 238
+ "\\xef", // 239
+ "\\xf0", // 240
+ "\\xf1", // 241
+ "\\xf2", // 242
+ "\\xf3", // 243
+ "\\xf4", // 244
+ "\\xf5", // 245
+ "\\xf6", // 246
+ "\\xf7", // 247
+ "\\xf8", // 248
+ "\\xf9", // 249
+ "\\xfa", // 250
+ "\\xfb", // 251
+ "\\xfc", // 252
+ "\\xfd", // 253
+ "\\xfe", // 254
+ "\\xff" // 255
+};
+
+void serialize_string(const std::string& value, std::ostream& str)
+{
+ std::string::const_iterator it = value.begin();
+ std::string::const_iterator end = value.end();
+ U8 c;
+ for(; it != end; ++it)
+ {
+ c = (U8)(*it);
+ str << NOTATION_STRING_CHARACTERS[c];
+ }
+}
+
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
new file mode 100644
index 0000000000..bc6817c732
--- /dev/null
+++ b/indra/llcommon/llsdserialize.h
@@ -0,0 +1,592 @@
+/**
+ * @file llsdserialize.h
+ * @author Phoenix
+ * @date 2006-02-26
+ * @brief Declaration of parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDSERIALIZE_H
+#define LL_LLSDSERIALIZE_H
+
+#include <iosfwd>
+#include "llsd.h"
+#include "llmemory.h"
+
+/**
+ * @class LLSDParser
+ * @brief Abstract base class for simple LLSD parsers.
+ */
+class LLSDParser : public LLRefCount
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const = 0;
+
+protected:
+
+};
+
+/**
+ * @class LLSDNotationParser
+ * @brief Parser which handles the original notation format for LLSD.
+ */
+class LLSDNotationParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDNotationParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDNotationParser() {}
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data. Undefined on failure.
+ * @return Returns the number of LLSD objects parsed into
+ * data. Returns -1 on parse failure.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Simple notation parse.
+ *
+ * This simplified parser cannot not distinguish between a failed
+ * parse and a parse which yields a single undefined LLSD. You can
+ * use this if error checking will be implicit in the use of the
+ * results of the parse.
+ * @param istr The input stream.
+ * @return Returns the parsed LLSD object.
+ */
+ static LLSD parse(std::istream& istr);
+
+private:
+ /**
+ * @brief Parse a map from the istream
+ *
+ * @param istr The input stream.
+ * @param map The map to add the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseMap(std::istream& istr, LLSD& map) const;
+
+ /**
+ * @brief Parse an array from the istream.
+ *
+ * @param istr The input stream.
+ * @param array The array to append the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseArray(std::istream& istr, LLSD& array) const;
+
+ /**
+ * @brief Parse a string from the istream and assign it to data.
+ *
+ * @param istr The input stream.
+ * @param data[out] The data to assign.
+ */
+ void parseString(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Parse binary data from the stream.
+ *
+ * @param istr The input stream.
+ * @param data[out] The data to assign.
+ */
+ void parseBinary(std::istream& istr, LLSD& data) const;
+};
+
+/**
+ * @class LLSDXMLParser
+ * @brief Parser which handles XML format LLSD.
+ */
+class LLSDXMLParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDXMLParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDXMLParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns the number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+private:
+ class Impl;
+ Impl& impl;
+
+ void parsePart(const char *buf, int len);
+ friend class LLSDSerialize;
+};
+
+/**
+ * @class LLSDBinaryParser
+ * @brief Parser which handles binary formatted LLSD.
+ */
+class LLSDBinaryParser : public LLSDParser
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDBinaryParser();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDBinaryParser();
+
+ /**
+ * @brief Call this method to parse a stream for LLSD.
+ *
+ * This method parses the istream for a structured data. This
+ * method assumes that the istream is a complete llsd object --
+ * for example an opened and closed map with an arbitrary nesting
+ * of elements. This method will return after reading one data
+ * object, allowing continued reading from the stream by the
+ * caller.
+ * @param istr The input stream.
+ * @param data[out] The newly parse structured data.
+ * @return Returns the number of LLSD objects parsed into data.
+ */
+ virtual S32 parse(std::istream& istr, LLSD& data) const;
+
+ /**
+ * @brief Simple notation parse.
+ *
+ * This simplified parser cannot not distinguish between a failed
+ * parse and a parse which yields a single undefined LLSD. You can
+ * use this if error checking will be implicit in the use of the
+ * results of the parse.
+ * @param istr The input stream.
+ * @return Returns the parsed LLSD object.
+ */
+ static LLSD parse(std::istream& istr);
+
+private:
+ /**
+ * @brief Parse a map from the istream
+ *
+ * @param istr The input stream.
+ * @param map The map to add the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseMap(std::istream& istr, LLSD& map) const;
+
+ /**
+ * @brief Parse an array from the istream.
+ *
+ * @param istr The input stream.
+ * @param array The array to append the parsed data.
+ * @return Returns The number of LLSD objects parsed into data.
+ */
+ S32 parseArray(std::istream& istr, LLSD& array) const;
+
+ /**
+ * @brief Parse a string from the istream and assign it to data.
+ *
+ * @param istr The input stream.
+ * @param value[out] The string to assign.
+ */
+ void parseString(std::istream& istr, std::string& value) const;
+};
+
+
+/**
+ * @class LLSDFormatter
+ * @brief Abstract base class for formatting LLSD.
+ */
+class LLSDFormatter : public LLRefCount
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDFormatter();
+
+public:
+ /**
+ * Options for output
+ */
+ enum e_formatter_options_type
+ {
+ OPTIONS_NONE = 0,
+ OPTIONS_PRETTY = 1
+ } EFormatterOptions;
+
+ /**
+ * @brief Constructor
+ */
+ LLSDFormatter();
+
+ /**
+ * @brief Set the boolean serialization format.
+ *
+ * @param alpha Serializes boolean as alpha if true.
+ */
+ void boolalpha(bool alpha);
+
+ /**
+ * @brief Set the real format
+ *
+ * By default, the formatter will use default double serialization
+ * which is frequently frustrating for many applications. You can
+ * set the precision on the stream independently, but that still
+ * might not work depending on the value.
+ * EXAMPLES:<br>
+ * %.2f<br>
+ * @param format A format string which follows the printf format
+ * rules. Specify an empty string to return to default formatting.
+ */
+ void realFormat(const std::string& format);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const = 0;
+
+protected:
+ /**
+ * @brief Helper method which appropriately obeys the real format.
+ *
+ * @param real The real value to format.
+ * @param ostr The destination stream for the data.
+ */
+ void formatReal(LLSD::Real real, std::ostream& ostr) const;
+
+protected:
+ bool mBoolAlpha;
+ std::string mRealFormat;
+};
+
+
+/**
+ * @class LLSDNotationFormatter
+ * @brief Formatter which outputs the original notation format for LLSD.
+ */
+class LLSDNotationFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDNotationFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDNotationFormatter();
+
+ /**
+ * @brief Helper static method to return a notation escaped string
+ *
+ * This method will return the notation escaped string, but not
+ * the surrounding serialization identifiers such as a double or
+ * single quote. It will be up to the caller to embed those as
+ * appropriate.
+ * @param in The raw, unescaped string.
+ * @return Returns an escaped string appropriate for serialization.
+ */
+ static std::string escapeString(const std::string& in);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+};
+
+
+/**
+ * @class LLSDXMLFormatter
+ * @brief Formatter which outputs the LLSD as XML.
+ */
+class LLSDXMLFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDXMLFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDXMLFormatter();
+
+ /**
+ * @brief Helper static method to return an xml escaped string
+ *
+ * @param in A valid UTF-8 string.
+ * @return Returns an escaped string appropriate for serialization.
+ */
+ static std::string escapeString(const std::string& in);
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+
+protected:
+
+ /**
+ * @brief Implementation to format the data. This is called recursively.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ S32 format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const;
+};
+
+
+/**
+ * @class LLSDBinaryFormatter
+ * @brief Formatter which outputs the LLSD as a binary notation format.
+ *
+ * The binary format is a compact and efficient representation of
+ * structured data useful for when transmitting over a small data pipe
+ * or when transmission frequency is very high.<br>
+ *
+ * The normal boolalpha and real format commands are ignored.<br>
+ *
+ * All integers are transmitted in network byte order. The format is:<br>
+ * Undefined: '!'<br>
+ * Boolean: character '1' for true character '0' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as 'k' + 4 byte integer size + string
+ */
+class LLSDBinaryFormatter : public LLSDFormatter
+{
+protected:
+ /**
+ * @brief Destructor
+ */
+ virtual ~LLSDBinaryFormatter();
+
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDBinaryFormatter();
+
+ /**
+ * @brief Call this method to format an LLSD to a stream.
+ *
+ * @param data The data to write.
+ * @param ostr The destination stream for the data.
+ * @return Returns The number of LLSD objects fomatted out
+ */
+ virtual S32 format(const LLSD& data, std::ostream& ostr, U32 options = LLSDFormatter::OPTIONS_NONE) const;
+
+protected:
+ /**
+ * @brief Helper method to serialize strings
+ *
+ * This method serializes a network byte order size and the raw
+ * string contents.
+ * @param string The string to write.
+ * @param ostr The destination stream for the data.
+ */
+ void formatString(const std::string& string, std::ostream& ostr) const;
+};
+
+
+/**
+ * @class LLSDNotationStreamFormatter
+ * @brief Formatter which is specialized for use on streams which
+ * outputs the original notation format for LLSD.
+ *
+ * This class is useful for doing inline stream operations. For example:
+ *
+ * <code>
+ * LLSD sd;<br>
+ * sd["foo"] = "bar";<br>
+ * std::stringstream params;<br>
+ * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd)
+ * << "]";
+ * </code>
+ */
+template <class Formatter>
+class LLSDOStreamer : public Formatter
+{
+public:
+ /**
+ * @brief Constructor
+ */
+ LLSDOStreamer(const LLSD& data, U32 options = LLSDFormatter::OPTIONS_NONE) :
+ mSD(data), mOptions(options) {}
+
+ /**
+ * @brief Stream operator.
+ *
+ * Use this inline during construction during a stream operation.
+ * @param str The destination stream for serialized output.
+ * @param The formatter which will output it's LLSD.
+ * @return Returns the stream passed in after streaming mSD.
+ */
+ friend std::ostream& operator<<(
+ std::ostream& str,
+ const LLSDOStreamer<Formatter>& formatter)
+ {
+ formatter.format(formatter.mSD, str, formatter.mOptions);
+ return str;
+ }
+
+protected:
+ LLSD mSD;
+ U32 mOptions;
+};
+
+typedef LLSDOStreamer<LLSDNotationFormatter> LLSDNotationStreamer;
+typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer;
+
+/**
+ * @class LLSDSerialize
+ * @Serializer / deserializer for the various LLSD formats
+ */
+class LLSDSerialize
+{
+public:
+ enum ELLSD_Serialize
+ {
+ LLSD_BINARY, LLSD_XML
+ };
+
+ /*
+ * Generic in/outs
+ */
+ static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize,
+ U32 options = LLSDFormatter::OPTIONS_NONE);
+ static bool deserialize(LLSD& sd, std::istream& str);
+
+ /*
+ * Notation Methods
+ */
+ static S32 toNotation(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 fromNotation(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDNotationParser> p = new LLSDNotationParser;
+ return p->parse(str, sd);
+ }
+
+ /*
+ * XML Methods
+ */
+ static S32 toXML(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 toPrettyXML(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY);
+ }
+ static S32 fromXML(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDXMLParser> p = new LLSDXMLParser;
+ return p->parse(str, sd);
+ }
+
+ /*
+ * Binary Methods
+ */
+ static S32 toBinary(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDBinaryFormatter> f = new LLSDBinaryFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
+ }
+ static S32 fromBinary(LLSD& sd, std::istream& str)
+ {
+ LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser;
+ return p->parse(str, sd);
+ }
+private:
+ static const char *LLSDBinaryHeader;
+ static const char *LLSDXMLHeader;
+};
+
+#endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
new file mode 100644
index 0000000000..2824d0f73c
--- /dev/null
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -0,0 +1,706 @@
+/**
+ * @file llsdserialize_xml.cpp
+ * @brief XML parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize_xml.h"
+
+#include <iostream>
+#include <deque>
+
+#include "apr-1/apr_base64.h"
+
+extern "C"
+{
+#include "expat/expat.h"
+}
+
+/**
+ * LLSDXMLFormatter
+ */
+LLSDXMLFormatter::LLSDXMLFormatter()
+{
+}
+
+// virtual
+LLSDXMLFormatter::~LLSDXMLFormatter()
+{
+}
+
+// virtual
+S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
+{
+ std::streamsize old_precision = ostr.precision(25);
+
+ LLString post = "";
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ post = "\n";
+ }
+ ostr << "<llsd>" << post;
+ S32 rv = format_impl(data, ostr, options, 1);
+ ostr << "</llsd>\n";
+
+ ostr.precision(old_precision);
+ return rv;
+}
+
+S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const
+{
+ S32 format_count = 1;
+ LLString pre = "";
+ LLString post = "";
+
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ for (U32 i = 0; i < level; i++)
+ {
+ pre += " ";
+ }
+ post = "\n";
+ }
+
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ if(0 == data.size())
+ {
+ ostr << pre << "<map />" << post;
+ }
+ else
+ {
+ ostr << pre << "<map>" << post;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr << pre << "<key>" << escapeString((*iter).first) << "</key>" << post;
+ format_count += format_impl((*iter).second, ostr, options, level + 1);
+ }
+ ostr << pre << "</map>" << post;
+ }
+ break;
+
+ case LLSD::TypeArray:
+ if(0 == data.size())
+ {
+ ostr << pre << "<array />" << post;
+ }
+ else
+ {
+ ostr << pre << "<array>" << post;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format_impl(*iter, ostr, options, level + 1);
+ }
+ ostr << pre << "</array>" << post;
+ }
+ break;
+
+ case LLSD::TypeUndefined:
+ ostr << pre << "<undef />" << post;
+ break;
+
+ case LLSD::TypeBoolean:
+ ostr << pre << "<boolean>";
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean() ? "true" : "false");
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ ostr << "</boolean>" << post;
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << pre << "<integer>" << data.asInteger() << "</integer>" << post;
+ break;
+
+ case LLSD::TypeReal:
+ ostr << pre << "<real>";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ ostr << "</real>" << post;
+ break;
+
+ case LLSD::TypeUUID:
+ if(data.asUUID().isNull()) ostr << pre << "<uuid />" << post;
+ else ostr << pre << "<uuid>" << data.asUUID() << "</uuid>" << post;
+ break;
+
+ case LLSD::TypeString:
+ if(data.asString().empty()) ostr << pre << "<string />" << post;
+ else ostr << pre << "<string>" << escapeString(data.asString()) <<"</string>" << post;
+ break;
+
+ case LLSD::TypeDate:
+ ostr << pre << "<date>" << data.asDate() << "</date>" << post;
+ break;
+
+ case LLSD::TypeURI:
+ ostr << pre << "<uri>" << escapeString(data.asString()) << "</uri>" << post;
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ LLSD::Binary buffer = data.asBinary();
+ if(buffer.empty())
+ {
+ ostr << pre << "<binary />" << post;
+ }
+ else
+ {
+ // *FIX: memory inefficient.
+ ostr << pre << "<binary encoding=\"base64\">";
+ int b64_buffer_length = apr_base64_encode_len(buffer.size());
+ char* b64_buffer = new char[b64_buffer_length];
+ b64_buffer_length = apr_base64_encode_binary(
+ b64_buffer,
+ &buffer[0],
+ buffer.size());
+ ostr.write(b64_buffer, b64_buffer_length - 1);
+ delete[] b64_buffer;
+ ostr << "</binary>" << post;
+ }
+ break;
+ }
+ default:
+ // *NOTE: This should never happen.
+ ostr << pre << "<undef />" << post;
+ break;
+ }
+ return format_count;
+}
+
+// static
+std::string LLSDXMLFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream out;
+ std::string::const_iterator it = in.begin();
+ std::string::const_iterator end = in.end();
+ for(; it != end; ++it)
+ {
+ switch((*it))
+ {
+ case '<':
+ out << "&lt;";
+ break;
+ case '>':
+ out << "&gt;";
+ break;
+ case '&':
+ out << "&amp;";
+ break;
+ case '\'':
+ out << "&apos;";
+ break;
+ case '"':
+ out << "&quot;";
+ break;
+ default:
+ out << (*it);
+ break;
+ }
+ }
+ return out.str();
+}
+
+
+
+class LLSDXMLParser::Impl
+{
+public:
+ Impl();
+ ~Impl();
+
+ LLSD parse(std::istream& input);
+
+ void parsePart(const char *buf, int len);
+
+private:
+ void reset();
+
+ void startElementHandler(const XML_Char* name, const XML_Char** attributes);
+ void endElementHandler(const XML_Char* name);
+ void characterDataHandler(const XML_Char* data, int length);
+
+ static void sStartElementHandler(
+ void* userData, const XML_Char* name, const XML_Char** attributes);
+ static void sEndElementHandler(
+ void* userData, const XML_Char* name);
+ static void sCharacterDataHandler(
+ void* userData, const XML_Char* data, int length);
+
+ void startSkipping();
+
+ enum Element {
+ ELEMENT_LLSD,
+ ELEMENT_UNDEF,
+ ELEMENT_BOOL,
+ ELEMENT_INTEGER,
+ ELEMENT_REAL,
+ ELEMENT_STRING,
+ ELEMENT_UUID,
+ ELEMENT_DATE,
+ ELEMENT_URI,
+ ELEMENT_BINARY,
+ ELEMENT_MAP,
+ ELEMENT_ARRAY,
+ ELEMENT_KEY,
+ ELEMENT_UNKNOWN
+ };
+ static Element readElement(const XML_Char* name);
+
+ static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs);
+
+
+ XML_Parser mParser;
+
+ LLSD mResult;
+
+ bool mInLLSDElement;
+ bool mGracefullStop;
+
+ typedef std::deque<LLSD*> LLSDRefStack;
+ LLSDRefStack mStack;
+
+ int mDepth;
+ bool mSkipping;
+ int mSkipThrough;
+
+ std::string mCurrentKey;
+ std::ostringstream mCurrentContent;
+
+ bool mPreStaged;
+};
+
+
+LLSDXMLParser::Impl::Impl()
+{
+ mParser = XML_ParserCreate(NULL);
+ mPreStaged = false;
+ reset();
+}
+
+LLSDXMLParser::Impl::~Impl()
+{
+ XML_ParserFree(mParser);
+}
+
+bool is_eol(char c)
+{
+ return (c == '\n' || c == '\r');
+}
+
+void clear_eol(std::istream& input)
+{
+ char c = input.peek();
+ while (input.good() && is_eol(c))
+ {
+ input.get(c);
+ c = input.peek();
+ }
+}
+
+static unsigned get_till_eol(std::istream& input, char *buf, unsigned bufsize)
+{
+ unsigned count = 0;
+ while (count < bufsize && input.good())
+ {
+ input.get(buf[count]);
+ count++;
+ if (is_eol(buf[count - 1]))
+ break;
+ }
+ return count;
+}
+
+LLSD LLSDXMLParser::Impl::parse(std::istream& input)
+{
+ reset();
+ XML_Status status;
+
+ static const int BUFFER_SIZE = 1024;
+ void* buffer = NULL;
+ int count = 0;
+ while (input.good() && !input.eof())
+ {
+ buffer = XML_GetBuffer(mParser, BUFFER_SIZE);
+
+ /*
+ * If we happened to end our last buffer right at the end of the llsd, but the
+ * stream is still going we will get a null buffer here. Check for mGracefullStop.
+ */
+ if (!buffer)
+ {
+ break;
+ }
+ count = get_till_eol(input, (char *)buffer, BUFFER_SIZE);
+ if (!count)
+ {
+ break;
+ }
+ status = XML_ParseBuffer(mParser, count, false);
+
+ if (status == XML_STATUS_ERROR)
+ {
+ break;
+ }
+ }
+
+ // FIXME: This code is buggy - if the stream was empty or not good, there
+ // is not buffer to parse, both the call to XML_ParseBuffer and the buffer
+ // manipulations are illegal
+ // futhermore, it isn't clear that the expat buffer semantics are preserved
+
+ status = XML_ParseBuffer(mParser, 0, true);
+ if (status == XML_STATUS_ERROR && !mGracefullStop)
+ {
+ ((char*) buffer)[count? count - 1 : 0] = '\0';
+ llinfos << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << llendl;
+ return LLSD();
+ }
+
+ clear_eol(input);
+ return mResult;
+}
+
+void LLSDXMLParser::Impl::reset()
+{
+ if (mPreStaged)
+ {
+ mPreStaged = false;
+ return;
+ }
+
+ mResult.clear();
+
+ mInLLSDElement = false;
+ mDepth = 0;
+
+ mGracefullStop = false;
+
+ mStack.clear();
+
+ mSkipping = false;
+
+#if( LL_WINDOWS || __GNUC__ > 2)
+ mCurrentKey.clear();
+#else
+ mCurrentKey = std::string();
+#endif
+
+
+ XML_ParserReset(mParser, "utf-8");
+ XML_SetUserData(mParser, this);
+ XML_SetElementHandler(mParser, sStartElementHandler, sEndElementHandler);
+ XML_SetCharacterDataHandler(mParser, sCharacterDataHandler);
+}
+
+
+void LLSDXMLParser::Impl::startSkipping()
+{
+ mSkipping = true;
+ mSkipThrough = mDepth;
+}
+
+const XML_Char*
+LLSDXMLParser::Impl::findAttribute(const XML_Char* name, const XML_Char** pairs)
+{
+ while (NULL != pairs && NULL != *pairs)
+ {
+ if(0 == strcmp(name, *pairs))
+ {
+ return *(pairs + 1);
+ }
+ pairs += 2;
+ }
+ return NULL;
+}
+
+void LLSDXMLParser::Impl::parsePart(const char *buf, int len)
+{
+ void * buffer = XML_GetBuffer(mParser, len);
+ if (buffer != NULL && buf != NULL)
+ {
+ memcpy(buffer, buf, len);
+ }
+ XML_ParseBuffer(mParser, len, false);
+
+ mPreStaged = true;
+}
+
+void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Char** attributes)
+{
+ mDepth += 1;
+ if (mSkipping)
+ {
+ return;
+ }
+
+ Element element = readElement(name);
+ mCurrentContent.str("");
+
+ switch (element)
+ {
+ case ELEMENT_LLSD:
+ if (mInLLSDElement) { return startSkipping(); }
+ mInLLSDElement = true;
+ return;
+
+ case ELEMENT_KEY:
+ if (mStack.empty() || !(mStack.back()->isMap()))
+ {
+ return startSkipping();
+ }
+ return;
+
+ case ELEMENT_BINARY:
+ {
+ const XML_Char* encoding = findAttribute("encoding", attributes);
+ if(encoding && strcmp("base64", encoding) != 0) { return startSkipping(); }
+ break;
+ }
+
+ default:
+ // all rest are values, fall through
+ ;
+ }
+
+
+ if (!mInLLSDElement) { return startSkipping(); }
+
+ if (mStack.empty())
+ {
+ mStack.push_back(&mResult);
+ }
+ else if (mStack.back()->isMap())
+ {
+ if (mCurrentKey.empty()) { return startSkipping(); }
+
+ LLSD& map = *mStack.back();
+ LLSD& newElement = map[mCurrentKey];
+ mStack.push_back(&newElement);
+
+#if( LL_WINDOWS || __GNUC__ > 2)
+ mCurrentKey.clear();
+#else
+ mCurrentKey = std::string();
+#endif
+ }
+ else if (mStack.back()->isArray())
+ {
+ LLSD& array = *mStack.back();
+ array.append(LLSD());
+ LLSD& newElement = array[array.size()-1];
+ mStack.push_back(&newElement);
+ }
+ else {
+ // improperly nested value in a non-structure
+ return startSkipping();
+ }
+
+ switch (element)
+ {
+ case ELEMENT_MAP:
+ *mStack.back() = LLSD::emptyMap();
+ break;
+
+ case ELEMENT_ARRAY:
+ *mStack.back() = LLSD::emptyArray();
+ break;
+
+ default:
+ // all the other values will be set in the end element handler
+ ;
+ }
+}
+
+void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
+{
+ mDepth -= 1;
+ if (mSkipping)
+ {
+ if (mDepth < mSkipThrough)
+ {
+ mSkipping = false;
+ }
+ return;
+ }
+
+ Element element = readElement(name);
+
+ switch (element)
+ {
+ case ELEMENT_LLSD:
+ if (mInLLSDElement)
+ {
+ mInLLSDElement = false;
+ mGracefullStop = true;
+ XML_StopParser(mParser, false);
+ }
+ return;
+
+ case ELEMENT_KEY:
+ mCurrentKey = mCurrentContent.str();
+ return;
+
+ default:
+ // all rest are values, fall through
+ ;
+ }
+
+ if (!mInLLSDElement) { return; }
+
+ LLSD& value = *mStack.back();
+ mStack.pop_back();
+
+ std::string content = mCurrentContent.str();
+ mCurrentContent.str("");
+
+ switch (element)
+ {
+ case ELEMENT_UNDEF:
+ value.clear();
+ break;
+
+ case ELEMENT_BOOL:
+ value = content == "true" || content == "1";
+ break;
+
+ case ELEMENT_INTEGER:
+ value = LLSD(content).asInteger();
+ break;
+
+ case ELEMENT_REAL:
+ value = LLSD(content).asReal();
+ break;
+
+ case ELEMENT_STRING:
+ value = content;
+ break;
+
+ case ELEMENT_UUID:
+ value = LLSD(content).asUUID();
+ break;
+
+ case ELEMENT_DATE:
+ value = LLSD(content).asDate();
+ break;
+
+ case ELEMENT_URI:
+ value = LLSD(content).asURI();
+ break;
+
+ case ELEMENT_BINARY:
+ {
+ S32 len = apr_base64_decode_len(content.c_str());
+ std::vector<U8> data;
+ data.resize(len);
+ len = apr_base64_decode_binary(&data[0], content.c_str());
+ data.resize(len);
+ value = data;
+ break;
+ }
+
+ case ELEMENT_UNKNOWN:
+ value.clear();
+ break;
+
+ default:
+ // other values, map and array, have already been set
+ break;
+ }
+}
+
+void LLSDXMLParser::Impl::characterDataHandler(const XML_Char* data, int length)
+{
+ mCurrentContent.write(data, length);
+}
+
+
+void LLSDXMLParser::Impl::sStartElementHandler(
+ void* userData, const XML_Char* name, const XML_Char** attributes)
+{
+ ((LLSDXMLParser::Impl*)userData)->startElementHandler(name, attributes);
+}
+
+void LLSDXMLParser::Impl::sEndElementHandler(
+ void* userData, const XML_Char* name)
+{
+ ((LLSDXMLParser::Impl*)userData)->endElementHandler(name);
+}
+
+void LLSDXMLParser::Impl::sCharacterDataHandler(
+ void* userData, const XML_Char* data, int length)
+{
+ ((LLSDXMLParser::Impl*)userData)->characterDataHandler(data, length);
+}
+
+
+LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* name)
+{
+ if (strcmp(name, "llsd") == 0) { return ELEMENT_LLSD; }
+ if (strcmp(name, "undef") == 0) { return ELEMENT_UNDEF; }
+ if (strcmp(name, "boolean") == 0) { return ELEMENT_BOOL; }
+ if (strcmp(name, "integer") == 0) { return ELEMENT_INTEGER; }
+ if (strcmp(name, "real") == 0) { return ELEMENT_REAL; }
+ if (strcmp(name, "string") == 0) { return ELEMENT_STRING; }
+ if (strcmp(name, "uuid") == 0) { return ELEMENT_UUID; }
+ if (strcmp(name, "date") == 0) { return ELEMENT_DATE; }
+ if (strcmp(name, "uri") == 0) { return ELEMENT_URI; }
+ if (strcmp(name, "binary") == 0) { return ELEMENT_BINARY; }
+ if (strcmp(name, "map") == 0) { return ELEMENT_MAP; }
+ if (strcmp(name, "array") == 0) { return ELEMENT_ARRAY; }
+ if (strcmp(name, "key") == 0) { return ELEMENT_KEY; }
+
+ return ELEMENT_UNKNOWN;
+}
+
+
+
+
+
+
+
+LLSDXMLParser::LLSDXMLParser()
+ : impl(* new Impl)
+{
+}
+
+LLSDXMLParser::~LLSDXMLParser()
+{
+ delete &impl;
+}
+
+void LLSDXMLParser::parsePart(const char *buf, int len)
+{
+ impl.parsePart(buf, len);
+}
+
+// virtual
+S32 LLSDXMLParser::parse(std::istream& input, LLSD& data) const
+{
+ data = impl.parse(input);
+ return 0;
+}
diff --git a/indra/llcommon/llsdserialize_xml.h b/indra/llcommon/llsdserialize_xml.h
new file mode 100644
index 0000000000..f4aa7b92a4
--- /dev/null
+++ b/indra/llcommon/llsdserialize_xml.h
@@ -0,0 +1,17 @@
+/**
+ * @file llsdserialize_xml.h
+ * @brief XML parsers and formatters for LLSD
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDSERIALIZE_XML_H
+#define LL_LLSDSERIALIZE_XML_H
+
+#include "llsdserialize.h"
+
+// all the XML class definitions are in llsdserialze.h for now
+
+#endif // LL_LLSDSERIALIZE_XML_H
+
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
new file mode 100644
index 0000000000..9305f82e05
--- /dev/null
+++ b/indra/llcommon/llsdutil.cpp
@@ -0,0 +1,177 @@
+/**
+ * @file llsdutil.cpp
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Implementation of classes, functions, etc, for using structured data.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_LINUX
+#include <netinet/in.h>
+#endif
+#if LL_DARWIN
+#include <arpa/inet.h>
+#endif
+
+#include "linden_common.h"
+#include "llsdutil.h"
+
+// vector3
+LLSD ll_sd_from_vector3(const LLVector3& vec)
+{
+ LLSD rv;
+ rv.append((F64)vec.mV[VX]);
+ rv.append((F64)vec.mV[VY]);
+ rv.append((F64)vec.mV[VZ]);
+ return rv;
+}
+
+LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index)
+{
+ LLVector3 rv;
+ rv.mV[VX] = (F32)sd[start_index].asReal();
+ rv.mV[VY] = (F32)sd[++start_index].asReal();
+ rv.mV[VZ] = (F32)sd[++start_index].asReal();
+ return rv;
+}
+
+// vector3d
+LLSD ll_sd_from_vector3d(const LLVector3d& vec)
+{
+ LLSD rv;
+ rv.append(vec.mdV[VX]);
+ rv.append(vec.mdV[VY]);
+ rv.append(vec.mdV[VZ]);
+ return rv;
+}
+
+LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index)
+{
+ LLVector3d rv;
+ rv.mdV[VX] = sd[start_index].asReal();
+ rv.mdV[VY] = sd[++start_index].asReal();
+ rv.mdV[VZ] = sd[++start_index].asReal();
+ return rv;
+}
+
+//vector2
+LLSD ll_sd_from_vector2(const LLVector2& vec)
+{
+ LLSD rv;
+ rv.append((F64)vec.mV[VX]);
+ rv.append((F64)vec.mV[VY]);
+ return rv;
+}
+
+LLVector2 ll_vector2_from_sd(const LLSD& sd)
+{
+ LLVector2 rv;
+ rv.mV[VX] = (F32)sd[0].asReal();
+ rv.mV[VY] = (F32)sd[1].asReal();
+ return rv;
+}
+
+// Quaternion
+LLSD ll_sd_from_quaternion(const LLQuaternion& quat)
+{
+ LLSD rv;
+ rv.append((F64)quat.mQ[VX]);
+ rv.append((F64)quat.mQ[VY]);
+ rv.append((F64)quat.mQ[VZ]);
+ rv.append((F64)quat.mQ[VW]);
+ return rv;
+}
+
+LLQuaternion ll_quaternion_from_sd(const LLSD& sd)
+{
+ LLQuaternion quat;
+ quat.mQ[VX] = (F32)sd[0].asReal();
+ quat.mQ[VY] = (F32)sd[1].asReal();
+ quat.mQ[VZ] = (F32)sd[2].asReal();
+ quat.mQ[VW] = (F32)sd[3].asReal();
+ return quat;
+}
+
+// color4
+LLSD ll_sd_from_color4(const LLColor4& c)
+{
+ LLSD rv;
+ rv.append(c.mV[0]);
+ rv.append(c.mV[1]);
+ rv.append(c.mV[2]);
+ rv.append(c.mV[3]);
+ return rv;
+}
+
+LLColor4 ll_color4_from_sd(const LLSD& sd)
+{
+ LLColor4 c;
+ c.mV[0] = (F32)sd[0].asReal();
+ c.mV[1] = (F32)sd[1].asReal();
+ c.mV[2] = (F32)sd[2].asReal();
+ c.mV[3] = (F32)sd[3].asReal();
+ return c;
+}
+
+// U32
+LLSD ll_sd_from_U32(const U32 val)
+{
+ std::vector<U8> v;
+ U32 net_order = htonl(val);
+
+ v.resize(4);
+ memcpy(&(v[0]), &net_order, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U32 ll_U32_from_sd(const LLSD& sd)
+{
+ U32 ret;
+ std::vector<U8> v = sd.asBinary();
+ if (v.size() < 4)
+ {
+ return 0;
+ }
+ memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */
+ ret = ntohl(ret);
+ return ret;
+}
+
+//U64
+LLSD ll_sd_from_U64(const U64 val)
+{
+ std::vector<U8> v;
+ U32 high, low;
+
+ high = (U32)(val >> 32);
+ low = (U32)val;
+ high = htonl(high);
+ low = htonl(low);
+
+ v.resize(8);
+ memcpy(&(v[0]), &high, 4); /* Flawfinder: ignore */
+ memcpy(&(v[4]), &low, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U64 ll_U64_from_sd(const LLSD& sd)
+{
+ U32 high, low;
+ std::vector<U8> v = sd.asBinary();
+
+ if (v.size() < 8)
+ {
+ return 0;
+ }
+
+ memcpy(&high, &(v[0]), 4); /* Flawfinder: ignore */
+ memcpy(&low, &(v[4]), 4); /* Flawfinder: ignore */
+ high = ntohl(high);
+ low = ntohl(low);
+
+ return ((U64)high) << 32 | low;
+}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
new file mode 100644
index 0000000000..9369f1a39f
--- /dev/null
+++ b/indra/llcommon/llsdutil.h
@@ -0,0 +1,50 @@
+/**
+ * @file llsdutil.h
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Utility classes, functions, etc, for using structured data.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDUTIL_H
+#define LL_LLSDUTIL_H
+
+#include "llsd.h"
+#include "../llmath/v3math.h"
+#include "../llmath/v3dmath.h"
+#include "../llmath/v2math.h"
+#include "../llmath/llquaternion.h"
+#include "../llmath/v4color.h"
+#include "../llprimitive/lltextureanim.h"
+
+// vector3
+LLSD ll_sd_from_vector3(const LLVector3& vec);
+LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0);
+
+// vector3d (double)
+LLSD ll_sd_from_vector3d(const LLVector3d& vec);
+LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0);
+
+// vector2
+LLSD ll_sd_from_vector2(const LLVector2& vec);
+LLVector2 ll_vector2_from_sd(const LLSD& sd);
+
+// Quaternion
+LLSD ll_sd_from_quaternion(const LLQuaternion& quat);
+LLQuaternion ll_quaternion_from_sd(const LLSD& sd);
+
+// color4
+LLSD ll_sd_from_color4(const LLColor4& c);
+LLColor4 ll_color4_from_sd(const LLSD& sd);
+
+// U32
+LLSD ll_sd_from_U32(const U32);
+U32 ll_U32_from_sd(const LLSD& sd);
+
+// U64
+LLSD ll_sd_from_U64(const U64);
+U64 ll_U64_from_sd(const LLSD& sd);
+
+#endif // LL_LLSDUTIL_H
diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp
new file mode 100644
index 0000000000..9d5395ad07
--- /dev/null
+++ b/indra/llcommon/llsecondlifeurls.cpp
@@ -0,0 +1,63 @@
+/**
+ * @file llsecondlifeurls.cpp
+ * @brief Urls used in the product
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsecondlifeurls.h"
+
+const char CREATE_ACCOUNT_URL[] =
+ "http://secondlife.com/registration/";
+
+const char MANAGE_ACCOUNT[] =
+ "http://secondlife.com/account/";
+
+const char AUCTION_URL[] =
+ "http://secondlife.com/auctions/auction-detail.php?id=";
+
+const char EVENTS_URL[] =
+ "http://secondlife.com/events/";
+
+const char TIER_UP_URL[] =
+ "http://secondlife.com/app/landtier";
+
+const char LAND_URL[] =
+ "http://secondlife.com/app/landtier";
+
+const char UPGRADE_TO_PREMIUM_URL[] =
+ "http://secondlife.com/app/upgrade/";
+
+const char DIRECTX_9_URL[] =
+ "http://secondlife.com/support/";
+
+const char AMD_AGP_URL[] =
+ "http://secondlife.com/support/";
+
+const char VIA_URL[] =
+ "http://secondlife.com/support/";
+
+const char INTEL_CHIPSET_URL[] =
+ "http://secondlife.com/support/";
+
+const char SIS_CHIPSET_URL[] =
+ "http://secondlife.com/support/";
+
+const char BLOGS_URL[] =
+ "http://blog.secondlife.com/";
+
+const char BUY_CURRENCY_URL[] =
+ "http://secondlife.com/app/currency/";
+
+const char LSL_DOC_URL[] =
+ "http://secondlife.com/app/lsldoc/";
+
+const char SL_KB_URL[] =
+ "http://secondlife.com/knowledgebase/";
+
+const char ACCOUNT_TRANSACTIONS_URL[] =
+ "https://secondlife.com/account/transactions.php";
+
+const char RELEASE_NOTES[] = "releasenotes.txt";
diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h
new file mode 100644
index 0000000000..86d18d0eab
--- /dev/null
+++ b/indra/llcommon/llsecondlifeurls.h
@@ -0,0 +1,64 @@
+/**
+ * @file llsecondlifeurls.h
+ * @brief Global URLs to pages on our web site
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSECONDLIFEURLS_H
+#define LL_LLSECONDLIFEURLS_H
+
+// Account registration web page
+extern const char CREATE_ACCOUNT_URL[];
+
+// Manage Account
+extern const char MANAGE_ACCOUNT[];
+
+extern const char AUCTION_URL[];
+
+extern const char EVENTS_URL[];
+
+// Tier up to a new land level.
+extern const char TIER_UP_URL[];
+
+// Tier up to a new land level.
+extern const char LAND_URL[];
+
+// Upgrade from basic membership to premium membership
+extern const char UPGRADE_TO_PREMIUM_URL[];
+
+// How to get DirectX 9
+extern const char DIRECTX_9_URL[];
+
+// On AMD with bad AGP controller
+extern const char AMD_AGP_URL[];
+
+// Out of date VIA chipset
+extern const char VIA_URL[];
+
+// Out of date intel chipset driver
+extern const char INTEL_CHIPSET_URL[];
+
+// Out of date SiS chipset driver
+extern const char SIS_CHIPSET_URL[];
+
+// Linden Blogs page
+extern const char BLOGS_URL[];
+
+// Currency page
+extern const char BUY_CURRENCY_URL[];
+
+// LSL script wiki
+extern const char LSL_DOC_URL[];
+
+// SL KnowledgeBase page
+extern const char SL_KB_URL[];
+
+// Account transactions
+extern const char ACCOUNT_TRANSACTIONS_URL[];
+
+// Local Url Release Notes
+extern const char RELEASE_NOTES[];
+
+#endif
diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h
new file mode 100644
index 0000000000..0acac99c45
--- /dev/null
+++ b/indra/llcommon/llsimplehash.h
@@ -0,0 +1,137 @@
+/**
+ * @file llsimplehash.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSIMPLEHASH_H
+#define LL_LLSIMPLEHASH_H
+
+#include "llstl.h"
+
+template <typename HASH_KEY_TYPE>
+class LLSimpleHashEntry
+{
+protected:
+ HASH_KEY_TYPE mHashKey;
+ LLSimpleHashEntry<HASH_KEY_TYPE>* mNextEntry;
+public:
+ LLSimpleHashEntry(HASH_KEY_TYPE key) :
+ mHashKey(key),
+ mNextEntry(0)
+ {
+ }
+ virtual ~LLSimpleHashEntry()
+ {
+ }
+ HASH_KEY_TYPE getHashKey() const
+ {
+ return mHashKey;
+ }
+ LLSimpleHashEntry<HASH_KEY_TYPE>* getNextEntry() const
+ {
+ return mNextEntry;
+ }
+ void setNextEntry(LLSimpleHashEntry<HASH_KEY_TYPE>* next)
+ {
+ mNextEntry = next;
+ }
+};
+
+template <typename HASH_KEY_TYPE, int TABLE_SIZE>
+class LLSimpleHash
+{
+public:
+ LLSimpleHash()
+ {
+ llassert(TABLE_SIZE);
+ llassert((TABLE_SIZE ^ (TABLE_SIZE-1)) == (TABLE_SIZE | (TABLE_SIZE-1))); // power of 2
+ memset(mEntryTable, 0, sizeof(mEntryTable));
+ }
+ virtual ~LLSimpleHash()
+ {
+ }
+
+ virtual int getIndex(HASH_KEY_TYPE key)
+ {
+ return key & (TABLE_SIZE-1);
+ }
+
+ bool insert(LLSimpleHashEntry<HASH_KEY_TYPE>* entry)
+ {
+ llassert(entry->getNextEntry() == 0);
+ int index = getIndex(entry->getHashKey());
+ entry->setNextEntry(mEntryTable[index]);
+ mEntryTable[index] = entry;
+ return true;
+ }
+ LLSimpleHashEntry<HASH_KEY_TYPE>* find(HASH_KEY_TYPE key)
+ {
+ int index = getIndex(key);
+ LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index];
+ while(res && (res->getHashKey() != key))
+ {
+ res = res->getNextEntry();
+ }
+ return res;
+ }
+ bool erase(LLSimpleHashEntry<HASH_KEY_TYPE>* entry)
+ {
+ return erase(entry->getHashKey());
+ }
+ bool erase(HASH_KEY_TYPE key)
+ {
+ int index = getIndex(key);
+ LLSimpleHashEntry<HASH_KEY_TYPE>* prev = 0;
+ LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index];
+ while(res && (res->getHashKey() != key))
+ {
+ prev = res;
+ res = res->getNextEntry();
+ }
+ if (res)
+ {
+ LLSimpleHashEntry<HASH_KEY_TYPE>* next = res->getNextEntry();
+ if (prev)
+ {
+ prev->setNextEntry(next);
+ }
+ else
+ {
+ mEntryTable[index] = next;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ // Removes and returns an arbitrary ("first") element from the table
+ // Used for deleting the entire table.
+ LLSimpleHashEntry<HASH_KEY_TYPE>* pop_element()
+ {
+ for (int i=0; i<TABLE_SIZE; i++)
+ {
+ LLSimpleHashEntry<HASH_KEY_TYPE>* entry = mEntryTable[i];
+ if (entry)
+ {
+ mEntryTable[i] = entry->getNextEntry();
+ return entry;
+ }
+ }
+ return 0;
+ }
+ // debugging
+ LLSimpleHashEntry<HASH_KEY_TYPE>* get_element_at_index(S32 index) const
+ {
+ return mEntryTable[index];
+ }
+
+
+private:
+ LLSimpleHashEntry<HASH_KEY_TYPE>* mEntryTable[TABLE_SIZE];
+};
+
+#endif // LL_LLSIMPLEHASH_H
diff --git a/indra/llcommon/llskiplist.h b/indra/llcommon/llskiplist.h
new file mode 100644
index 0000000000..a86eb05d46
--- /dev/null
+++ b/indra/llcommon/llskiplist.h
@@ -0,0 +1,502 @@
+/**
+ * @file llskiplist.h
+ * @brief skip list implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_LLSKIPLIST_H
+#define LL_LLSKIPLIST_H
+
+#include "llerror.h"
+//#include "vmath.h"
+
+// NOTA BENE: Insert first needs to be < NOT <=
+
+template <class DATA_TYPE, S32 BINARY_DEPTH = 10>
+class LLSkipList
+{
+public:
+ typedef BOOL (*compare)(const DATA_TYPE& first, const DATA_TYPE& second);
+ typedef compare insert_func;
+ typedef compare equals_func;
+
+ void init();
+
+ // basic constructor
+ LLSkipList();
+
+ // basic constructor including sorter
+ LLSkipList(insert_func insert_first, equals_func equals);
+ ~LLSkipList();
+
+ inline void setInsertFirst(insert_func insert_first);
+ inline void setEquals(equals_func equals);
+
+ inline BOOL addData(const DATA_TYPE& data);
+ inline BOOL checkData(const DATA_TYPE& data);
+
+ // returns number of items in the list
+ inline S32 getLength() const; // NOT a constant time operation, traverses entire list!
+
+ inline BOOL moveData(const DATA_TYPE& data, LLSkipList *newlist);
+
+ inline BOOL removeData(const DATA_TYPE& data);
+
+ // remove all nodes from the list but do not delete data
+ inline void removeAllNodes();
+
+ // place mCurrentp on first node
+ inline void resetList();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ inline DATA_TYPE getNextData();
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ inline void removeCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ inline DATA_TYPE getFirstData();
+
+ class LLSkipNode
+ {
+ public:
+ LLSkipNode()
+ : mData(0)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ LLSkipNode(DATA_TYPE data)
+ : mData(data)
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+ }
+
+ ~LLSkipNode()
+ {
+ }
+
+ DATA_TYPE mData;
+ LLSkipNode *mForward[BINARY_DEPTH];
+
+ private:
+ // Disallow copying of LLSkipNodes by not implementing these methods.
+ LLSkipNode(const LLSkipNode &);
+ LLSkipNode &operator=(const LLSkipNode &);
+ };
+
+ static BOOL defaultEquals(const DATA_TYPE& first, const DATA_TYPE& second)
+ {
+ return first == second;
+ }
+
+private:
+ LLSkipNode mHead;
+ LLSkipNode *mUpdate[BINARY_DEPTH];
+ LLSkipNode *mCurrentp;
+ LLSkipNode *mCurrentOperatingp;
+ S32 mLevel;
+ insert_func mInsertFirst;
+ equals_func mEquals;
+
+private:
+ // Disallow copying of LLSkipNodes by not implementing these methods.
+ LLSkipList(const LLSkipList &);
+ LLSkipList &operator=(const LLSkipList &);
+};
+
+
+///////////////////////
+//
+// Implementation
+//
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::init()
+{
+ if (BINARY_DEPTH < 2)
+ {
+ llerrs << "Trying to create skip list with too little depth, "
+ "must be 2 or greater" << llendl;
+ }
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// basic constructor
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList()
+: mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ init();
+}
+
+// basic constructor including sorter
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::LLSkipList(insert_func insert,
+ equals_func equals)
+: mInsertFirst(insert),
+ mEquals(equals)
+{
+ init();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipList<DATA_TYPE, BINARY_DEPTH>::~LLSkipList()
+{
+ removeAllNodes();
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setInsertFirst(insert_func insert_first)
+{
+ mInsertFirst = insert_first;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::setEquals(equals_func equals)
+{
+ mEquals = equals;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::addData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (frand(1.f) < 0.5f)
+ break;
+ }
+
+ LLSkipNode *snode = new LLSkipNode(data);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return TRUE;
+}
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::checkData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+
+ if (current)
+ {
+ return mEquals(current->mData, data);
+ }
+
+ return FALSE;
+}
+
+// returns number of items in the list
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLSkipList<DATA_TYPE, BINARY_DEPTH>::getLength() const
+{
+ U32 length = 0;
+ for (LLSkipNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+ return length;
+}
+
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::moveData(const DATA_TYPE& data, LLSkipList *newlist)
+{
+ BOOL removed = removeData(data);
+ BOOL added = newlist->addData(data);
+ return removed && added;
+}
+
+
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeData(const DATA_TYPE& data)
+{
+ S32 level;
+ LLSkipNode *current = &mHead;
+ LLSkipNode *temp;
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mData, data)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mData < data))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mData, data))
+ {
+ // nope!
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = current->mForward[0];
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = current->mForward[0];
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (mUpdate[level]->mForward[level] != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ mUpdate[level]->mForward[level] = current->mForward[level];
+ }
+
+ // clean up cuurent
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!mHead.mForward[mLevel - 1]))
+ {
+ mLevel--;
+ }
+ }
+ return TRUE;
+}
+
+// remove all nodes from the list but do not delete data
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeAllNodes()
+{
+ LLSkipNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// place mCurrentp on first node
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mData);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipList<DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ //return NULL; // causes compile warning
+ return (DATA_TYPE)0; // equivalent, but no warning
+ }
+}
+
+
+#endif
diff --git a/indra/llcommon/llskipmap.h b/indra/llcommon/llskipmap.h
new file mode 100644
index 0000000000..52270d89ba
--- /dev/null
+++ b/indra/llcommon/llskipmap.h
@@ -0,0 +1,1004 @@
+/**
+ * @file llskipmap.h
+ * @brief Associative container based on the skiplist algorithm.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSKIPMAP_H
+#define LL_LLSKIPMAP_H
+
+#include "llerror.h"
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH = 8>
+class LLSkipMap
+{
+public:
+ // basic constructor
+ LLSkipMap();
+
+ // basic constructor including sorter
+ LLSkipMap(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second),
+ BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+
+ ~LLSkipMap();
+
+ void setInsertFirst(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+ void setEquals(BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second));
+
+ DATA_TYPE &addData(const INDEX_TYPE &index, DATA_TYPE datap);
+ DATA_TYPE &addData(const INDEX_TYPE &index);
+ DATA_TYPE &getData(const INDEX_TYPE &index);
+ DATA_TYPE &operator[](const INDEX_TYPE &index);
+
+ // If index present, returns data.
+ // If index not present, adds <index,NULL> and returns NULL.
+ DATA_TYPE &getData(const INDEX_TYPE &index, BOOL &b_new_entry);
+
+ // Returns TRUE if data present in map.
+ BOOL checkData(const INDEX_TYPE &index);
+
+ // Returns TRUE if key is present in map. This is useful if you
+ // are potentially storing NULL pointers in the map
+ BOOL checkKey(const INDEX_TYPE &index);
+
+ // If there, returns the data.
+ // If not, returns NULL.
+ // Never adds entries to the map.
+ DATA_TYPE getIfThere(const INDEX_TYPE &index);
+
+ INDEX_TYPE reverseLookup(const DATA_TYPE datap);
+
+ // returns number of items in the list
+ S32 getLength(); // WARNING! getLength is O(n), not O(1)!
+
+ BOOL removeData(const INDEX_TYPE &index);
+
+ // remove all nodes from the list but do not delete data
+ void removeAllData();
+
+ // place mCurrentp on first node
+ void resetList();
+
+ // return the data currently pointed to
+ DATA_TYPE getCurrentDataWithoutIncrement();
+
+ // return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_TYPE getCurrentData();
+
+ // same as getCurrentData() but a more intuitive name for the operation
+ DATA_TYPE getNextData();
+
+ INDEX_TYPE getNextKey();
+
+ // return the key currently pointed to
+ INDEX_TYPE getCurrentKeyWithoutIncrement();
+
+ // The internal iterator is at the end of the list.
+ BOOL notDone() const;
+
+ // remove the Node at mCurentOperatingp
+ // leave mCurrentp and mCurentOperatingp on the next entry
+ void removeCurrentData();
+
+ void deleteCurrentData();
+
+ // reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+ DATA_TYPE getFirstData();
+
+ INDEX_TYPE getFirstKey();
+
+ class LLSkipMapNode
+ {
+ public:
+ LLSkipMapNode()
+ {
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mIndex;
+
+ for (i = 0; i < (S32)sizeof(INDEX_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+
+ zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLSkipMapNode(const INDEX_TYPE &index)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ U8 *zero = (U8 *)&mData;
+
+ for (i = 0; i < (S32)sizeof(DATA_TYPE); i++)
+ {
+ *(zero + i) = 0;
+ }
+ }
+
+ LLSkipMapNode(const INDEX_TYPE &index, DATA_TYPE datap)
+ : mIndex(index)
+ {
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mForward[i] = NULL;
+ }
+
+ mData = datap;
+ }
+
+ ~LLSkipMapNode()
+ {
+ }
+
+
+ INDEX_TYPE mIndex;
+ DATA_TYPE mData;
+ LLSkipMapNode *mForward[BINARY_DEPTH];
+
+ private:
+ // Disallow copying of LLSkipMapNodes by not implementing these methods.
+ LLSkipMapNode(const LLSkipMapNode &);
+ LLSkipMapNode &operator=(const LLSkipMapNode &rhs);
+ };
+
+ static BOOL defaultEquals(const INDEX_TYPE &first, const INDEX_TYPE &second)
+ {
+ return first == second;
+ }
+
+private:
+ // don't generate implicit copy constructor or copy assignment
+ LLSkipMap(const LLSkipMap &);
+ LLSkipMap &operator=(const LLSkipMap &);
+
+private:
+ LLSkipMapNode mHead;
+ LLSkipMapNode *mUpdate[BINARY_DEPTH];
+ LLSkipMapNode *mCurrentp;
+ LLSkipMapNode *mCurrentOperatingp;
+ S32 mLevel;
+ BOOL (*mInsertFirst)(const INDEX_TYPE &first, const INDEX_TYPE &second);
+ BOOL (*mEquals)(const INDEX_TYPE &first, const INDEX_TYPE &second);
+ S32 mNumberOfSteps;
+};
+
+//////////////////////////////////////////////////
+//
+// LLSkipMap implementation
+//
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::LLSkipMap()
+ : mInsertFirst(NULL),
+ mEquals(defaultEquals)
+{
+ // Skipmaps must have binary depth of at least 2
+ cassert(BINARY_DEPTH >= 2);
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mUpdate[i] = NULL;
+ }
+ mLevel = 1;
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::LLSkipMap(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second),
+ BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+ : mInsertFirst(insert_first),
+ mEquals(equals)
+{
+ // Skipmaps must have binary depth of at least 2
+ cassert(BINARY_DEPTH >= 2);
+
+ mLevel = 1;
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::~LLSkipMap()
+{
+ removeAllData();
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::setInsertFirst(BOOL (*insert_first)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+{
+ mInsertFirst = insert_first;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::setEquals(BOOL (*equals)(const INDEX_TYPE &first, const INDEX_TYPE &second))
+{
+ mEquals = equals;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::addData(const INDEX_TYPE &index, DATA_TYPE datap)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // replace the existing data if a node is already there
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+ current->mData = datap;
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ {
+ break;
+ }
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index, datap);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::addData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ break;
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+ mNumberOfSteps++;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+
+ // now add the new node
+ S32 newlevel;
+ for (newlevel = 1; newlevel <= mLevel && newlevel < BINARY_DEPTH; newlevel++)
+ {
+ if (rand() & 1)
+ break;
+ }
+
+ LLSkipMapNode *snode = new LLSkipMapNode(index);
+
+ if (newlevel > mLevel)
+ {
+ mHead.mForward[mLevel] = NULL;
+ mUpdate[mLevel] = &mHead;
+ mLevel = newlevel;
+ }
+
+ for (level = 0; level < newlevel; level++)
+ {
+ snode->mForward[level] = mUpdate[level]->mForward[level];
+ mUpdate[level]->mForward[level] = snode;
+ }
+
+ return snode->mData;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::operator[](const INDEX_TYPE &index)
+{
+
+ return getData(index);
+}
+
+// If index present, returns data.
+// If index not present, adds <index,NULL> and returns NULL.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE &LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getData(const INDEX_TYPE &index, BOOL &b_new_entry)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if ( (current)
+ &&(mEquals(current->mIndex, index)))
+ {
+
+ return current->mData;
+ }
+ b_new_entry = TRUE;
+ addData(index);
+
+ return current->mData;
+}
+
+// Returns TRUE if data present in map.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::checkData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ // Gets rid of some compiler ambiguity for the LLPointer<> templated class.
+ if (current->mData)
+ {
+ return mEquals(current->mIndex, index);
+ }
+ }
+
+ return FALSE;
+}
+
+// Returns TRUE if key is present in map. This is useful if you
+// are potentially storing NULL pointers in the map
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::checkKey(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (current)
+ {
+ return mEquals(current->mIndex, index);
+ }
+
+ return FALSE;
+}
+
+// If there, returns the data.
+// If not, returns NULL.
+// Never adds entries to the map.
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getIfThere(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ mNumberOfSteps = 0;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ mNumberOfSteps++;
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ mNumberOfSteps++;
+ current = *current->mForward;
+
+ if (current)
+ {
+ if (mEquals(current->mIndex, index))
+ {
+ return current->mData;
+ }
+ }
+
+ // Avoid Linux compiler warning on returning NULL.
+ return DATA_TYPE();
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::reverseLookup(const DATA_TYPE datap)
+{
+ LLSkipMapNode *current = &mHead;
+
+ while (current)
+ {
+ if (datap == current->mData)
+ {
+
+ return current->mIndex;
+ }
+ current = *current->mForward;
+ }
+
+ // not found! return NULL
+ return INDEX_TYPE();
+}
+
+// returns number of items in the list
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline S32 LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getLength()
+{
+ U32 length = 0;
+ for (LLSkipMapNode* temp = *(mHead.mForward); temp != NULL; temp = temp->mForward[0])
+ {
+ length++;
+ }
+
+ return length;
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeData(const INDEX_TYPE &index)
+{
+ S32 level;
+ LLSkipMapNode *current = &mHead;
+ LLSkipMapNode *temp;
+
+ // find the pointer one in front of the one we want
+ if (mInsertFirst)
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(mInsertFirst(temp->mIndex, index)))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+ else
+ {
+ for (level = mLevel - 1; level >= 0; level--)
+ {
+ temp = *(current->mForward + level);
+ while ( (temp)
+ &&(temp->mIndex < index))
+ {
+ current = temp;
+ temp = *(current->mForward + level);
+ }
+ *(mUpdate + level) = current;
+ }
+ }
+
+ // we're now just in front of where we want to be . . . take one step forward
+ current = *current->mForward;
+
+ if (!current)
+ {
+ // empty list or beyond the end!
+
+ return FALSE;
+ }
+
+ // is this the one we want?
+ if (!mEquals(current->mIndex, index))
+ {
+ // nope!
+
+ return FALSE;
+ }
+ else
+ {
+ // do we need to fix current or currentop?
+ if (current == mCurrentp)
+ {
+ mCurrentp = *current->mForward;
+ }
+
+ if (current == mCurrentOperatingp)
+ {
+ mCurrentOperatingp = *current->mForward;
+ }
+ // yes it is! change pointers as required
+ for (level = 0; level < mLevel; level++)
+ {
+ if (*((*(mUpdate + level))->mForward + level) != current)
+ {
+ // cool, we've fixed all the pointers!
+ break;
+ }
+ *((*(mUpdate + level))->mForward + level) = *(current->mForward + level);
+ }
+
+ delete current;
+
+ // clean up mHead
+ while ( (mLevel > 1)
+ &&(!*(mHead.mForward + mLevel - 1)))
+ {
+ mLevel--;
+ }
+ }
+
+ return TRUE;
+}
+
+
+// remove all nodes from the list but do not delete data
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeAllData()
+{
+ LLSkipMapNode *temp;
+ // reset mCurrentp
+ mCurrentp = *(mHead.mForward);
+
+ while (mCurrentp)
+ {
+ temp = mCurrentp->mForward[0];
+ delete mCurrentp;
+ mCurrentp = temp;
+ }
+
+ S32 i;
+ for (i = 0; i < BINARY_DEPTH; i++)
+ {
+ mHead.mForward[i] = NULL;
+ mUpdate[i] = NULL;
+ }
+
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// place mCurrentp on first node
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::resetList()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+}
+
+
+// return the data currently pointed to
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentDataWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ return DATA_TYPE();
+ }
+}
+
+// return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // Basic types, like int, have default constructors that initialize
+ // them to zero. g++ 2.95 supports this. "int()" is zero.
+ // This also is nice for LLUUID()
+ return DATA_TYPE();
+ }
+}
+
+// same as getCurrentData() but a more intuitive name for the operation
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getNextData()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // Basic types, like int, have default constructors that initialize
+ // them to zero. g++ 2.95 supports this. "int()" is zero.
+ // This also is nice for LLUUID()
+ return DATA_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getNextKey()
+{
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+// return the key currently pointed to
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getCurrentKeyWithoutIncrement()
+{
+ if (mCurrentOperatingp)
+ {
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ // See comment for getNextData()
+ return INDEX_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline BOOL LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::notDone() const
+{
+ if (mCurrentOperatingp)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// remove the Node at mCurentOperatingp
+// leave mCurrentp and mCurentOperatingp on the next entry
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::removeCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ removeData(mCurrentOperatingp->mIndex);
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline void LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::deleteCurrentData()
+{
+ if (mCurrentOperatingp)
+ {
+ deleteData(mCurrentOperatingp->mIndex);
+ }
+}
+
+// reset the list and return the data currently pointed to, set mCurentOperatingp to that node and bump mCurrentp
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline DATA_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getFirstData()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mData;
+ }
+ else
+ {
+ // See comment for getNextData()
+ return DATA_TYPE();
+ }
+}
+
+template <class INDEX_TYPE, class DATA_TYPE, S32 BINARY_DEPTH>
+inline INDEX_TYPE LLSkipMap<INDEX_TYPE, DATA_TYPE, BINARY_DEPTH>::getFirstKey()
+{
+ mCurrentp = *(mHead.mForward);
+ mCurrentOperatingp = *(mHead.mForward);
+ if (mCurrentp)
+ {
+ mCurrentOperatingp = mCurrentp;
+ mCurrentp = mCurrentp->mForward[0];
+ return mCurrentOperatingp->mIndex;
+ }
+ else
+ {
+ return mHead.mIndex;
+ }
+}
+
+#endif
diff --git a/indra/llcommon/llstack.h b/indra/llcommon/llstack.h
new file mode 100644
index 0000000000..024acf149e
--- /dev/null
+++ b/indra/llcommon/llstack.h
@@ -0,0 +1,30 @@
+/**
+ * @file llstack.h
+ * @brief LLStack template class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTACK_H
+#define LL_LLSTACK_H
+
+#include "linked_lists.h"
+
+template <class DATA_TYPE> class LLStack
+{
+private:
+ LLLinkedList<DATA_TYPE> mStack;
+
+public:
+ LLStack() {}
+ ~LLStack() {}
+
+ void push(DATA_TYPE *data) { mStack.addData(data); }
+ DATA_TYPE *pop() { DATA_TYPE *tempp = mStack.getFirstData(); mStack.removeCurrentData(); return tempp; }
+ void deleteAllData() { mStack.deleteAllData(); }
+ void removeAllNodes() { mStack.removeAllNodes(); }
+};
+
+#endif
+
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
new file mode 100644
index 0000000000..ef707d3497
--- /dev/null
+++ b/indra/llcommon/llstat.cpp
@@ -0,0 +1,803 @@
+/**
+ * @file llstat.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstat.h"
+#include "llframetimer.h"
+#include "timing.h"
+
+class LLStatAccum::impl
+{
+public:
+ static const TimeScale IMPL_NUM_SCALES = (TimeScale)(SCALE_TWO_MINUTE + 1);
+ static U64 sScaleTimes[IMPL_NUM_SCALES];
+
+ BOOL mUseFrameTimer;
+
+ BOOL mRunning;
+ U64 mLastTime;
+
+ struct Bucket
+ {
+ F64 accum;
+ U64 endTime;
+
+ BOOL lastValid;
+ F64 lastAccum;
+ };
+
+ Bucket mBuckets[IMPL_NUM_SCALES];
+
+ BOOL mLastSampleValid;
+ F64 mLastSampleValue;
+
+
+ impl(bool useFrameTimer);
+
+ void reset(U64 when);
+
+ void sum(F64 value);
+ void sum(F64 value, U64 when);
+
+ F32 meanValue(TimeScale scale) const;
+
+ U64 getCurrentUsecs() const;
+ // Get current microseconds based on timer type
+};
+
+
+U64 LLStatAccum::impl::sScaleTimes[IMPL_NUM_SCALES] =
+{
+ USEC_PER_SEC * 1, // seconds
+ USEC_PER_SEC * 60, // minutes
+ USEC_PER_SEC * 60 * 2 // minutes
+#if 0
+ // enable these when more time scales are desired
+ USEC_PER_SEC * 60*60, // hours
+ USEC_PER_SEC * 24*60*60, // days
+ USEC_PER_SEC * 7*24*60*60, // weeks
+#endif
+};
+
+
+LLStatAccum::impl::impl(bool useFrameTimer)
+{
+ mUseFrameTimer = useFrameTimer;
+ mRunning = FALSE;
+ mLastSampleValid = FALSE;
+}
+
+void LLStatAccum::impl::reset(U64 when)
+{
+ mRunning = TRUE;
+ mLastTime = when;
+
+ for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ {
+ mBuckets[i].accum = 0.0;
+ mBuckets[i].endTime = when + sScaleTimes[i];
+ mBuckets[i].lastValid = FALSE;
+ }
+}
+
+void LLStatAccum::impl::sum(F64 value)
+{
+ sum(value, getCurrentUsecs());
+}
+
+void LLStatAccum::impl::sum(F64 value, U64 when)
+{
+ if (!mRunning)
+ {
+ reset(when);
+ return;
+ }
+ if (when < mLastTime)
+ {
+ llwarns << "LLStatAccum::sum clock has gone backwards from "
+ << mLastTime << " to " << when << ", resetting" << llendl;
+
+ reset(when);
+ return;
+ }
+
+ for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ {
+ Bucket& bucket = mBuckets[i];
+
+ if (when < bucket.endTime)
+ {
+ bucket.accum += value;
+ }
+ else
+ {
+ U64 timeScale = sScaleTimes[i];
+
+ U64 timeSpan = when - mLastTime;
+ // how long is this value for
+ U64 timeLeft = when - bucket.endTime;
+ // how much time is left after filling this bucket
+
+ if (timeLeft < timeScale)
+ {
+ F64 valueLeft = value * timeLeft / timeSpan;
+
+ bucket.lastValid = TRUE;
+ bucket.lastAccum = bucket.accum + (value - valueLeft);
+ bucket.accum = valueLeft;
+ bucket.endTime += timeScale;
+ }
+ else
+ {
+ U64 timeTail = timeLeft % timeScale;
+
+ bucket.lastValid = TRUE;
+ bucket.lastAccum = value * timeScale / timeSpan;
+ bucket.accum = value * timeTail / timeSpan;
+ bucket.endTime += (timeLeft - timeTail) + timeScale;
+ }
+ }
+ }
+
+ mLastTime = when;
+}
+
+
+F32 LLStatAccum::impl::meanValue(TimeScale scale) const
+{
+ if (!mRunning)
+ {
+ return 0.0;
+ }
+ if (scale < 0 || scale >= IMPL_NUM_SCALES)
+ {
+ llwarns << "llStatAccum::meanValue called for unsupported scale: "
+ << scale << llendl;
+ return 0.0;
+ }
+
+ const Bucket& bucket = mBuckets[scale];
+
+ F64 value = bucket.accum;
+ U64 timeLeft = bucket.endTime - mLastTime;
+ U64 scaleTime = sScaleTimes[scale];
+
+ if (bucket.lastValid)
+ {
+ value += bucket.lastAccum * timeLeft / scaleTime;
+ }
+ else if (timeLeft < scaleTime)
+ {
+ value *= scaleTime / (scaleTime - timeLeft);
+ }
+ else
+ {
+ value = 0.0;
+ }
+
+ return (F32)(value / scaleTime);
+}
+
+
+U64 LLStatAccum::impl::getCurrentUsecs() const
+{
+ if (mUseFrameTimer)
+ {
+ return LLFrameTimer::getTotalTime();
+ }
+ else
+ {
+ return totalTime();
+ }
+}
+
+
+
+
+
+LLStatAccum::LLStatAccum(bool useFrameTimer)
+ : m(* new impl(useFrameTimer))
+{
+}
+
+LLStatAccum::~LLStatAccum()
+{
+ delete &m;
+}
+
+F32 LLStatAccum::meanValue(TimeScale scale) const
+{
+ return m.meanValue(scale);
+}
+
+
+
+LLStatMeasure::LLStatMeasure(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatMeasure::sample(F64 value)
+{
+ U64 when = m.getCurrentUsecs();
+
+ if (m.mLastSampleValid)
+ {
+ F64 avgValue = (value + m.mLastSampleValue) / 2.0;
+ F64 interval = (F64)(when - m.mLastTime);
+
+ m.sum(avgValue * interval, when);
+ }
+ else
+ {
+ m.reset(when);
+ }
+
+ m.mLastSampleValid = TRUE;
+ m.mLastSampleValue = value;
+}
+
+
+LLStatRate::LLStatRate(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatRate::count(U32 value)
+{
+ m.sum((F64)value * impl::sScaleTimes[SCALE_SECOND]);
+}
+
+
+LLStatTime::LLStatTime(bool use_frame_timer)
+ : LLStatAccum(use_frame_timer)
+{
+}
+
+void LLStatTime::start()
+{
+ m.sum(0.0);
+}
+
+void LLStatTime::stop()
+{
+ U64 endTime = m.getCurrentUsecs();
+ m.sum((F64)(endTime - m.mLastTime), endTime);
+}
+
+
+
+LLTimer LLStat::sTimer;
+LLFrameTimer LLStat::sFrameTimer;
+
+LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
+{
+ llassert(num_bins > 0);
+ U32 i;
+ mUseFrameTimer = use_frame_timer;
+ mNumValues = 0;
+ mLastValue = 0.f;
+ mLastTime = 0.f;
+ mNumBins = num_bins;
+ mCurBin = (mNumBins-1);
+ mNextBin = 0;
+ mBins = new F32[mNumBins];
+ mBeginTime = new F64[mNumBins];
+ mTime = new F64[mNumBins];
+ mDT = new F32[mNumBins];
+ for (i = 0; i < mNumBins; i++)
+ {
+ mBins[i] = 0.f;
+ mBeginTime[i] = 0.0;
+ mTime[i] = 0.0;
+ mDT[i] = 0.f;
+ }
+}
+
+LLStat::~LLStat()
+{
+ delete[] mBins;
+ delete[] mBeginTime;
+ delete[] mTime;
+ delete[] mDT;
+}
+
+void LLStat::reset()
+{
+ U32 i;
+
+ mNumValues = 0;
+ mLastValue = 0.f;
+ mCurBin = (mNumBins-1);
+ delete[] mBins;
+ delete[] mBeginTime;
+ delete[] mTime;
+ delete[] mDT;
+ mBins = new F32[mNumBins];
+ mBeginTime = new F64[mNumBins];
+ mTime = new F64[mNumBins];
+ mDT = new F32[mNumBins];
+ for (i = 0; i < mNumBins; i++)
+ {
+ mBins[i] = 0.f;
+ mBeginTime[i] = 0.0;
+ mTime[i] = 0.0;
+ mDT[i] = 0.f;
+ }
+}
+
+void LLStat::setBeginTime(const F64 time)
+{
+ mBeginTime[mNextBin] = time;
+}
+
+void LLStat::addValueTime(const F64 time, const F32 value)
+{
+ if (mNumValues < mNumBins)
+ {
+ mNumValues++;
+ }
+
+ // Increment the bin counters.
+ mCurBin++;
+ if ((U32)mCurBin == mNumBins)
+ {
+ mCurBin = 0;
+ }
+ mNextBin++;
+ if ((U32)mNextBin == mNumBins)
+ {
+ mNextBin = 0;
+ }
+
+ mBins[mCurBin] = value;
+ mTime[mCurBin] = time;
+ mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
+ //this value is used to prime the min/max calls
+ mLastTime = mTime[mCurBin];
+ mLastValue = value;
+
+ // Set the begin time for the next stat segment.
+ mBeginTime[mNextBin] = mTime[mCurBin];
+ mTime[mNextBin] = mTime[mCurBin];
+ mDT[mNextBin] = 0.f;
+}
+
+void LLStat::start()
+{
+ if (mUseFrameTimer)
+ {
+ mBeginTime[mNextBin] = sFrameTimer.getElapsedSeconds();
+ }
+ else
+ {
+ mBeginTime[mNextBin] = sTimer.getElapsedTimeF64();
+ }
+}
+
+void LLStat::addValue(const F32 value)
+{
+ if (mNumValues < mNumBins)
+ {
+ mNumValues++;
+ }
+
+ // Increment the bin counters.
+ mCurBin++;
+ if ((U32)mCurBin == mNumBins)
+ {
+ mCurBin = 0;
+ }
+ mNextBin++;
+ if ((U32)mNextBin == mNumBins)
+ {
+ mNextBin = 0;
+ }
+
+ mBins[mCurBin] = value;
+ if (mUseFrameTimer)
+ {
+ mTime[mCurBin] = sFrameTimer.getElapsedSeconds();
+ }
+ else
+ {
+ mTime[mCurBin] = sTimer.getElapsedTimeF64();
+ }
+ mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
+
+ //this value is used to prime the min/max calls
+ mLastTime = mTime[mCurBin];
+ mLastValue = value;
+
+ // Set the begin time for the next stat segment.
+ mBeginTime[mNextBin] = mTime[mCurBin];
+ mTime[mNextBin] = mTime[mCurBin];
+ mDT[mNextBin] = 0.f;
+}
+
+
+F32 LLStat::getMax() const
+{
+ U32 i;
+ F32 current_max = mLastValue;
+ if (mNumBins == 0)
+ {
+ current_max = 0.f;
+ }
+ else
+ {
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ if (mBins[i] > current_max)
+ {
+ current_max = mBins[i];
+ }
+ }
+ }
+ return current_max;
+}
+
+F32 LLStat::getMean() const
+{
+ U32 i;
+ F32 current_mean = 0.f;
+ U32 samples = 0;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ current_mean += mBins[i];
+ samples++;
+ }
+
+ // There will be a wrap error at 2^32. :)
+ if (samples != 0)
+ {
+ current_mean /= samples;
+ }
+ else
+ {
+ current_mean = 0.f;
+ }
+ return current_mean;
+}
+
+F32 LLStat::getMin() const
+{
+ U32 i;
+ F32 current_min = mLastValue;
+
+ if (mNumBins == 0)
+ {
+ current_min = 0.f;
+ }
+ else
+ {
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ if (mBins[i] < current_min)
+ {
+ current_min = mBins[i];
+ }
+ }
+ }
+ return current_min;
+}
+
+F32 LLStat::getSum() const
+{
+ U32 i;
+ F32 sum = 0.f;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ sum += mBins[i];
+ }
+
+ return sum;
+}
+
+F32 LLStat::getSumDuration() const
+{
+ U32 i;
+ F32 sum = 0.f;
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ sum += mDT[i];
+ }
+
+ return sum;
+}
+
+F32 LLStat::getPrev(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+ return mBins[bin];
+}
+
+F32 LLStat::getPrevPerSec(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+ return mBins[bin] / mDT[bin];
+}
+
+F64 LLStat::getPrevBeginTime(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+
+ return mBeginTime[bin];
+}
+
+F64 LLStat::getPrevTime(S32 age) const
+{
+ S32 bin;
+ bin = mCurBin - age;
+
+ while (bin < 0)
+ {
+ bin += mNumBins;
+ }
+
+ if (bin == mNextBin)
+ {
+ // Bogus for bin we're currently working on.
+ return 0.f;
+ }
+
+ return mTime[bin];
+}
+
+F32 LLStat::getBin(S32 bin) const
+{
+ return mBins[bin];
+}
+
+F32 LLStat::getBinPerSec(S32 bin) const
+{
+ return mBins[bin] / mDT[bin];
+}
+
+F64 LLStat::getBinBeginTime(S32 bin) const
+{
+ return mBeginTime[bin];
+}
+
+F64 LLStat::getBinTime(S32 bin) const
+{
+ return mTime[bin];
+}
+
+F32 LLStat::getCurrent() const
+{
+ return mBins[mCurBin];
+}
+
+F32 LLStat::getCurrentPerSec() const
+{
+ return mBins[mCurBin] / mDT[mCurBin];
+}
+
+F64 LLStat::getCurrentBeginTime() const
+{
+ return mBeginTime[mCurBin];
+}
+
+F64 LLStat::getCurrentTime() const
+{
+ return mTime[mCurBin];
+}
+
+F32 LLStat::getCurrentDuration() const
+{
+ return mDT[mCurBin];
+}
+
+F32 LLStat::getMeanPerSec() const
+{
+ U32 i;
+ F32 value = 0.f;
+ F32 dt = 0.f;
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value += mBins[i];
+ dt += mDT[i];
+ }
+
+ if (dt > 0.f)
+ {
+ return value/dt;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLStat::getMeanDuration() const
+{
+ F32 dur = 0.0f;
+ U32 count = 0;
+ for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ dur += mDT[i];
+ count++;
+ }
+
+ if (count > 0)
+ {
+ dur /= F32(count);
+ return dur;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLStat::getMaxPerSec() const
+{
+ U32 i;
+ F32 value;
+
+ if (mNextBin != 0)
+ {
+ value = mBins[0]/mDT[0];
+ }
+ else if (mNumValues > 0)
+ {
+ value = mBins[1]/mDT[1];
+ }
+ else
+ {
+ value = 0.f;
+ }
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value = llmax(value, mBins[i]/mDT[i]);
+ }
+ return value;
+}
+
+F32 LLStat::getMinPerSec() const
+{
+ U32 i;
+ F32 value;
+
+ if (mNextBin != 0)
+ {
+ value = mBins[0]/mDT[0];
+ }
+ else if (mNumValues > 0)
+ {
+ value = mBins[1]/mDT[1];
+ }
+ else
+ {
+ value = 0.f;
+ }
+
+ for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ // Skip the bin we're currently filling.
+ if (i == (U32)mNextBin)
+ {
+ continue;
+ }
+ value = llmin(value, mBins[i]/mDT[i]);
+ }
+ return value;
+}
+
+F32 LLStat::getMinDuration() const
+{
+ F32 dur = 0.0f;
+ for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
+ {
+ dur = llmin(dur, mDT[i]);
+ }
+ return dur;
+}
+
+U32 LLStat::getNumValues() const
+{
+ return mNumValues;
+}
+
+S32 LLStat::getNumBins() const
+{
+ return mNumBins;
+}
+
+S32 LLStat::getCurBin() const
+{
+ return mCurBin;
+}
+
+S32 LLStat::getNextBin() const
+{
+ return mNextBin;
+}
+
+F64 LLStat::getLastTime() const
+{
+ return mLastTime;
+}
diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h
new file mode 100644
index 0000000000..4c86557365
--- /dev/null
+++ b/indra/llcommon/llstat.h
@@ -0,0 +1,182 @@
+/**
+ * @file llstat.h
+ * @brief Runtime statistics accumulation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTAT_H
+#define LL_LLSTAT_H
+
+#include <deque>
+
+#include "lltimer.h"
+#include "llframetimer.h"
+
+//
+// Accumulates statistics for an arbitrary length of time.
+// Does this by maintaining a chain of accumulators, each one
+// accumulation the results of the parent. Can scale to arbitrary
+// amounts of time with very low memory cost.
+//
+
+class LLStatAccum
+{
+protected:
+ LLStatAccum(bool use_frame_timer);
+ virtual ~LLStatAccum();
+
+public:
+ enum TimeScale {
+ SCALE_SECOND,
+ SCALE_MINUTE,
+ SCALE_TWO_MINUTE,
+ SCALE_HOUR,
+ SCALE_DAY,
+ SCALE_WEEK,
+
+ NUM_SCALES
+ };
+
+ F32 meanValue(TimeScale scale) const;
+ // see the subclasses for the specific meaning of value
+
+ F32 meanValueOverLastSecond() const { return meanValue(SCALE_SECOND); }
+ F32 meanValueOverLastMinute() const { return meanValue(SCALE_MINUTE); }
+
+protected:
+ class impl;
+ impl& m;
+};
+
+class LLStatMeasure : public LLStatAccum
+ // gathers statistics about things that are measured
+ // ex.: tempature, time dilation
+{
+public:
+ LLStatMeasure(bool use_frame_timer = true);
+
+ void sample(F64);
+ void sample(S32 v) { sample((F64)v); }
+ void sample(U32 v) { sample((F64)v); }
+ void sample(S64 v) { sample((F64)v); }
+ void sample(U64 v) { sample((F64)v); }
+};
+
+
+class LLStatRate : public LLStatAccum
+ // gathers statistics about things that can be counted over time
+ // ex.: LSL instructions executed, messages sent, simulator frames completed
+ // renders it in terms of rate of thing per second
+{
+public:
+ LLStatRate(bool use_frame_timer = true);
+
+ void count(U32);
+ // used to note that n items have occured
+
+ void mark() { count(1); }
+ // used for counting the rate thorugh a point in the code
+};
+
+
+class LLTimeBlock;
+
+class LLStatTime : public LLStatAccum
+ // gathers statistics about time spent in a block of code
+ // measure average duration per second in the block
+{
+public:
+ LLStatTime(bool use_frame_timer = false);
+
+private:
+ void start();
+ void stop();
+ friend class LLTimeBlock;
+};
+
+class LLTimeBlock
+{
+public:
+ LLTimeBlock(LLStatTime& stat) : mStat(stat) { mStat.start(); }
+ ~LLTimeBlock() { mStat.stop(); }
+private:
+ LLStatTime& mStat;
+};
+
+
+
+
+
+class LLStat
+{
+public:
+ LLStat(const U32 num_bins = 32, BOOL use_frame_timer = FALSE);
+ ~LLStat();
+
+ void reset();
+
+ void start(); // Start the timer for the current "frame", otherwise uses the time tracked from
+ // the last addValue
+ void addValue(const F32 value = 1.f); // Adds the current value being tracked, and tracks the DT.
+ void addValue(const S32 value) { addValue((F32)value); }
+ void addValue(const U32 value) { addValue((F32)value); }
+
+ void setBeginTime(const F64 time);
+ void addValueTime(const F64 time, const F32 value = 1.f);
+
+ S32 getCurBin() const;
+ S32 getNextBin() const;
+
+ F32 getCurrent() const;
+ F32 getCurrentPerSec() const;
+ F64 getCurrentBeginTime() const;
+ F64 getCurrentTime() const;
+ F32 getCurrentDuration() const;
+
+ F32 getPrev(S32 age) const; // Age is how many "addValues" previously - zero is current
+ F32 getPrevPerSec(S32 age) const; // Age is how many "addValues" previously - zero is current
+ F64 getPrevBeginTime(S32 age) const;
+ F64 getPrevTime(S32 age) const;
+
+ F32 getBin(S32 bin) const;
+ F32 getBinPerSec(S32 bin) const;
+ F64 getBinBeginTime(S32 bin) const;
+ F64 getBinTime(S32 bin) const;
+
+ F32 getMax() const;
+ F32 getMaxPerSec() const;
+
+ F32 getMean() const;
+ F32 getMeanPerSec() const;
+ F32 getMeanDuration() const;
+
+ F32 getMin() const;
+ F32 getMinPerSec() const;
+ F32 getMinDuration() const;
+
+ F32 getSum() const;
+ F32 getSumDuration() const;
+
+ U32 getNumValues() const;
+ S32 getNumBins() const;
+
+ F64 getLastTime() const;
+private:
+ BOOL mUseFrameTimer;
+ U32 mNumValues;
+ U32 mNumBins;
+ F32 mLastValue;
+ F64 mLastTime;
+ F32 *mBins;
+ F64 *mBeginTime;
+ F64 *mTime;
+ F32 *mDT;
+ S32 mCurBin;
+ S32 mNextBin;
+ static LLTimer sTimer;
+ static LLFrameTimer sFrameTimer;
+};
+
+#endif // LL_STAT_
diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h
new file mode 100644
index 0000000000..07c6e0bf52
--- /dev/null
+++ b/indra/llcommon/llstatenums.h
@@ -0,0 +1,40 @@
+/**
+ * @file llstatenums.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATENUMS_H
+#define LL_LLSTATENUMS_H
+
+enum
+{
+ LL_SIM_STAT_TIME_DILATION,
+ LL_SIM_STAT_FPS,
+ LL_SIM_STAT_PHYSFPS,
+ LL_SIM_STAT_AGENTUPS,
+ LL_SIM_STAT_FRAMEMS,
+ LL_SIM_STAT_NETMS,
+ LL_SIM_STAT_SIMOTHERMS,
+ LL_SIM_STAT_SIMPHYSICSMS,
+ LL_SIM_STAT_AGENTMS,
+ LL_SIM_STAT_IMAGESMS,
+ LL_SIM_STAT_SCRIPTMS,
+ LL_SIM_STAT_NUMTASKS,
+ LL_SIM_STAT_NUMTASKSACTIVE,
+ LL_SIM_STAT_NUMAGENTMAIN,
+ LL_SIM_STAT_NUMAGENTCHILD,
+ LL_SIM_STAT_NUMSCRIPTSACTIVE,
+ LL_SIM_STAT_LSLIPS,
+ LL_SIM_STAT_INPPS,
+ LL_SIM_STAT_OUTPPS,
+ LL_SIM_STAT_PENDING_DOWNLOADS,
+ LL_SIM_STAT_PENDING_UPLOADS,
+ LL_SIM_STAT_VIRTUAL_SIZE_KB,
+ LL_SIM_STAT_RESIDENT_SIZE_KB,
+ LL_SIM_STAT_PENDING_LOCAL_UPLOADS,
+ LL_SIM_STAT_TOTAL_UNACKED_BYTES
+};
+
+#endif
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
new file mode 100644
index 0000000000..61d83f7259
--- /dev/null
+++ b/indra/llcommon/llstl.h
@@ -0,0 +1,452 @@
+/**
+ * @file llstl.h
+ * @brief helper object & functions for use with the stl.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTL_H
+#define LL_LLSTL_H
+
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <vector>
+#include <set>
+#include <deque>
+
+#include <stdio.h>
+#include <stdarg.h>
+
+// Use to compare the first element only of a pair
+// e.g. typedef std::set<std::pair<int, Data*>, compare_pair<int, Data*> > some_pair_set_t;
+template <typename T1, typename T2>
+struct compare_pair_first
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ return a.first < b.first;
+ }
+};
+
+template <typename T1, typename T2>
+struct compare_pair_greater
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ if (!(a.first < b.first))
+ return true;
+ else if (!(b.first < a.first))
+ return false;
+ else
+ return !(a.second < b.second);
+ }
+};
+
+// Use to compare the contents of two pointers (e.g. std::string*)
+template <typename T>
+struct compare_pointer_contents
+{
+ typedef const T* Tptr;
+ bool operator()(const Tptr& a, const Tptr& b) const
+ {
+ return *a < *b;
+ }
+};
+
+// DeletePointer is a simple helper for deleting all pointers in a container.
+// The general form is:
+//
+// std::for_each(cont.begin(), cont.end(), DeletePointer());
+
+struct DeletePointer
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete ptr;
+ }
+};
+struct DeletePointerArray
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete[] ptr;
+ }
+};
+
+// DeletePointer is a simple helper for deleting all pointers in a map.
+// The general form is:
+//
+// std::for_each(somemap.begin(), somemap.end(), DeletePairedPointer());
+
+struct DeletePairedPointer
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete ptr.second;
+ }
+};
+struct DeletePairedPointerArray
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete[] ptr.second;
+ }
+};
+
+
+// Alternate version of the above so that has a more cumbersome
+// syntax, but it can be used with compositional functors. *FIX: The
+// functor retuns a bool because msdev bombs during the composition if
+// you return void. Once we upgrade to a newer compiler, the second
+// unary_function template parameter can be set to void.
+//
+// Here's a snippit showing how you use this object:
+//
+// typedef std::map<int, widget*> map_type;
+// map_type widget_map;
+// ... // add elements
+// // delete them all
+// for_each(widget_map.begin(),
+// widget_map.end(),
+// llcompose1(DeletePointerFunctor<widget>(),
+// llselect2nd<map_type::value_type>()));
+
+template<typename T>
+struct DeletePointerFunctor : public std::unary_function<T*, bool>
+{
+ bool operator()(T* ptr) const
+ {
+ delete ptr;
+ return true;
+ }
+};
+
+// See notes about DeleteArray for why you should consider avoiding this.
+template<typename T>
+struct DeleteArrayFunctor : public std::unary_function<T*, bool>
+{
+ bool operator()(T* ptr) const
+ {
+ delete[] ptr;
+ return true;
+ }
+};
+
+// CopyNewPointer is a simple helper which accepts a pointer, and
+// returns a new pointer built with the copy constructor. Example:
+//
+// transform(in.begin(), in.end(), out.end(), CopyNewPointer());
+
+struct CopyNewPointer
+{
+ template<typename T> T* operator()(const T* ptr) const
+ {
+ return new T(*ptr);
+ }
+};
+
+// Simple function to help with finding pointers in maps.
+// For example:
+// typedef map_t;
+// std::map<int, const char*> foo;
+// foo[18] = "there";
+// foo[2] = "hello";
+// const char* bar = get_ptr_in_map(foo, 2); // bar -> "hello"
+// const char* baz = get_ptr_in_map(foo, 3); // baz == NULL
+template <typename K, typename T>
+inline T* get_ptr_in_map(const std::map<K,T*>& inmap, const K& key)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T*>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// helper function which returns true if key is in inmap.
+template <typename K, typename T>
+inline bool is_in_map(const std::map<K,T>& inmap, const K& key)
+{
+ typedef typename std::map<K,T>::const_iterator map_iter;
+ if(inmap.find(key) == inmap.end())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+// Similar to get_ptr_in_map, but for any type with a valid T(0) constructor.
+// To replace LLSkipMap getIfThere, use:
+// get_if_there(map, key, 0)
+// WARNING: Make sure default_value (generally 0) is not a valid map entry!
+template <typename K, typename T>
+inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return default_value;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// Useful for replacing the removeObj() functionality of LLDynamicArray
+// Example:
+// for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); )
+// {
+// if ((*iter)->isMarkedForRemoval())
+// iter = vector_replace_with_last(mList, iter);
+// else
+// ++iter;
+// }
+template <typename T, typename Iter>
+inline Iter vector_replace_with_last(std::vector<T>& invec, Iter iter)
+{
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ if (iter == invec.end())
+ {
+ return iter;
+ }
+ else if (iter == last)
+ {
+ invec.pop_back();
+ return invec.end();
+ }
+ else
+ {
+ *iter = *last;
+ invec.pop_back();
+ return iter;
+ }
+};
+
+// Useful for replacing the removeObj() functionality of LLDynamicArray
+// Example:
+// vector_replace_with_last(mList, x);
+template <typename T>
+inline bool vector_replace_with_last(std::vector<T>& invec, const T& val)
+{
+ typename std::vector<T>::iterator iter = std::find(invec.begin(), invec.end(), val);
+ if (iter != invec.end())
+ {
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ *iter = *last;
+ invec.pop_back();
+ return true;
+ }
+ return false;
+}
+
+// Append N elements to the vector and return a pointer to the first new element.
+template <typename T>
+inline T* vector_append(std::vector<T>& invec, S32 N)
+{
+ U32 sz = invec.size();
+ invec.resize(sz+N);
+ return &(invec[sz]);
+}
+
+// call function f to n members starting at first. similar to std::for_each
+template <class InputIter, class Size, class Function>
+Function ll_for_n(InputIter first, Size n, Function f)
+{
+ for ( ; n > 0; --n, ++first)
+ f(*first);
+ return f;
+}
+
+// copy first to result n times, incrementing each as we go
+template <class InputIter, class Size, class OutputIter>
+OutputIter ll_copy_n(InputIter first, Size n, OutputIter result)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = *first;
+ return result;
+}
+
+// set *result = op(*f) for n elements of f
+template <class InputIter, class OutputIter, class Size, class UnaryOp>
+OutputIter ll_transform_n(
+ InputIter first,
+ Size n,
+ OutputIter result,
+ UnaryOp op)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = op(*first);
+ return result;
+}
+
+
+
+/*
+ *
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+
+// helper to deal with the fact that MSDev does not package
+// select... with the stl. Look up usage on the sgi website.
+
+template <class _Pair>
+struct _LLSelect1st : public std::unary_function<_Pair, typename _Pair::first_type> {
+ const typename _Pair::first_type& operator()(const _Pair& __x) const {
+ return __x.first;
+ }
+};
+
+template <class _Pair>
+struct _LLSelect2nd : public std::unary_function<_Pair, typename _Pair::second_type>
+{
+ const typename _Pair::second_type& operator()(const _Pair& __x) const {
+ return __x.second;
+ }
+};
+
+template <class _Pair> struct llselect1st : public _LLSelect1st<_Pair> {};
+template <class _Pair> struct llselect2nd : public _LLSelect2nd<_Pair> {};
+
+// helper to deal with the fact that MSDev does not package
+// compose... with the stl. Look up usage on the sgi website.
+
+template <class _Operation1, class _Operation2>
+class ll_unary_compose :
+ public std::unary_function<typename _Operation2::argument_type,
+ typename _Operation1::result_type>
+{
+protected:
+ _Operation1 __op1;
+ _Operation2 __op2;
+public:
+ ll_unary_compose(const _Operation1& __x, const _Operation2& __y)
+ : __op1(__x), __op2(__y) {}
+ typename _Operation1::result_type
+ operator()(const typename _Operation2::argument_type& __x) const {
+ return __op1(__op2(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2>
+inline ll_unary_compose<_Operation1,_Operation2>
+llcompose1(const _Operation1& __op1, const _Operation2& __op2)
+{
+ return ll_unary_compose<_Operation1,_Operation2>(__op1, __op2);
+}
+
+template <class _Operation1, class _Operation2, class _Operation3>
+class ll_binary_compose
+ : public std::unary_function<typename _Operation2::argument_type,
+ typename _Operation1::result_type> {
+protected:
+ _Operation1 _M_op1;
+ _Operation2 _M_op2;
+ _Operation3 _M_op3;
+public:
+ ll_binary_compose(const _Operation1& __x, const _Operation2& __y,
+ const _Operation3& __z)
+ : _M_op1(__x), _M_op2(__y), _M_op3(__z) { }
+ typename _Operation1::result_type
+ operator()(const typename _Operation2::argument_type& __x) const {
+ return _M_op1(_M_op2(__x), _M_op3(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2, class _Operation3>
+inline ll_binary_compose<_Operation1, _Operation2, _Operation3>
+llcompose2(const _Operation1& __op1, const _Operation2& __op2,
+ const _Operation3& __op3)
+{
+ return ll_binary_compose<_Operation1,_Operation2,_Operation3>
+ (__op1, __op2, __op3);
+}
+
+// helpers to deal with the fact that MSDev does not package
+// bind... with the stl. Again, this is from sgi.
+template <class _Operation>
+class llbinder1st :
+ public std::unary_function<typename _Operation::second_argument_type,
+ typename _Operation::result_type> {
+protected:
+ _Operation op;
+ typename _Operation::first_argument_type value;
+public:
+ llbinder1st(const _Operation& __x,
+ const typename _Operation::first_argument_type& __y)
+ : op(__x), value(__y) {}
+ typename _Operation::result_type
+ operator()(const typename _Operation::second_argument_type& __x) const {
+ return op(value, __x);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline llbinder1st<_Operation>
+llbind1st(const _Operation& __oper, const _Tp& __x)
+{
+ typedef typename _Operation::first_argument_type _Arg1_type;
+ return llbinder1st<_Operation>(__oper, _Arg1_type(__x));
+}
+
+template <class _Operation>
+class llbinder2nd
+ : public std::unary_function<typename _Operation::first_argument_type,
+ typename _Operation::result_type> {
+protected:
+ _Operation op;
+ typename _Operation::second_argument_type value;
+public:
+ llbinder2nd(const _Operation& __x,
+ const typename _Operation::second_argument_type& __y)
+ : op(__x), value(__y) {}
+ typename _Operation::result_type
+ operator()(const typename _Operation::first_argument_type& __x) const {
+ return op(__x, value);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline llbinder2nd<_Operation>
+llbind2nd(const _Operation& __oper, const _Tp& __x)
+{
+ typedef typename _Operation::second_argument_type _Arg2_type;
+ return llbinder2nd<_Operation>(__oper, _Arg2_type(__x));
+}
+
+#endif // LL_LLSTL_H
diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp
new file mode 100644
index 0000000000..f9038afedc
--- /dev/null
+++ b/indra/llcommon/llstreamtools.cpp
@@ -0,0 +1,551 @@
+/**
+ * @file llstreamtools.cpp
+ * @brief some helper functions for parsing legacy simstate and asset files.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iostream>
+#include <string>
+
+#include "llstreamtools.h"
+
+
+// ----------------------------------------------------------------------------
+// some std::istream helper functions
+// ----------------------------------------------------------------------------
+
+// skips spaces and tabs
+bool skip_whitespace(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while (('\t' == c || ' ' == c) && input_stream.good())
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// skips whitespace, newlines, and carriage returns
+bool skip_emptyspace(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while ( input_stream.good()
+ && ('\t' == c || ' ' == c || '\n' == c || '\r' == c) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// skips emptyspace and lines that start with a #
+bool skip_comments_and_emptyspace(std::istream& input_stream)
+{
+ while (skip_emptyspace(input_stream))
+ {
+ char c = input_stream.peek();
+ if ('#' == c )
+ {
+ while ('\n' != c && input_stream.good())
+ {
+ c = input_stream.get();
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ return input_stream.good();
+}
+
+bool skip_line(std::istream& input_stream)
+{
+ char c;
+ do
+ {
+ c = input_stream.get();
+ } while ('\n' != c && input_stream.good());
+ return input_stream.good();
+}
+
+bool skip_to_next_word(std::istream& input_stream)
+{
+ char c = input_stream.peek();
+ while ( input_stream.good()
+ && ( (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || '_' == c ) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ while ( input_stream.good()
+ && !( (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z')
+ || (c >= '0' && c <= '9')
+ || '_' == c ) )
+ {
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream)
+{
+ int key_length = strlen(keyword); /*Flawfinder: ignore*/
+ if (0 == key_length)
+ {
+ return false;
+ }
+ while (input_stream.good())
+ {
+ skip_emptyspace(input_stream);
+ char c = input_stream.get();
+ if (keyword[0] != c)
+ {
+ skip_line(input_stream);
+ }
+ else
+ {
+ int key_index = 1;
+ while ( key_index < key_length
+ && keyword[key_index - 1] == c
+ && input_stream.good())
+ {
+ key_index++;
+ c = input_stream.get();
+ }
+
+ if (key_index == key_length
+ && keyword[key_index-1] == c)
+ {
+ c = input_stream.peek();
+ if (' ' == c || '\t' == c || '\r' == c || '\n' == c)
+ {
+ return true;
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ }
+ return false;
+}
+
+/* skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug in windows iostream
+bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stream)
+{
+ int key_length = strlen(keyword);
+ if (0 == key_length)
+ {
+ return false;
+ }
+ while (input_stream.good())
+ {
+ skip_emptyspace(input_stream);
+ char c = input_stream.get();
+ if (keyword[0] != c)
+ {
+ skip_line(input_stream);
+ }
+ else
+ {
+ int key_index = 1;
+ while ( key_index < key_length
+ && keyword[key_index - 1] == c
+ && input_stream.good())
+ {
+ key_index++;
+ c = input_stream.get();
+ }
+
+ if (key_index == key_length
+ && keyword[key_index-1] == c)
+ {
+ c = input_stream.peek();
+ if (' ' == c || '\t' == c || '\r' == c || '\n' == c)
+ {
+ // put the keyword back onto the stream
+ for (int index = key_length - 1; index >= 0; index--)
+ {
+ input_stream.putback(keyword[index]);
+ }
+ return true;
+ }
+ else
+ {
+ skip_line(input_stream);
+ break;
+ }
+ }
+ else
+ {
+ skip_line(input_stream);
+ }
+ }
+ }
+ return false;
+}
+*/
+
+bool get_word(std::string& output_string, std::istream& input_stream)
+{
+ skip_emptyspace(input_stream);
+ char c = input_stream.peek();
+ while ( !isspace(c)
+ && '\n' != c
+ && '\r' != c
+ && input_stream.good() )
+ {
+ output_string += c;
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+bool get_word(std::string& output_string, std::istream& input_stream, int n)
+{
+ skip_emptyspace(input_stream);
+ int char_count = 0;
+ char c = input_stream.peek();
+ while (!isspace(c)
+ && '\n' != c
+ && '\r' != c
+ && input_stream.good()
+ && char_count < n)
+ {
+ char_count++;
+ output_string += c;
+ input_stream.get();
+ c = input_stream.peek();
+ }
+ return input_stream.good();
+}
+
+// get everything up to and including the next newline
+bool get_line(std::string& output_string, std::istream& input_stream)
+{
+ char c = input_stream.get();
+ while (input_stream.good())
+ {
+ if ('\r' == c)
+ {
+ // skip carriage returns
+ }
+ else
+ {
+ output_string += c;
+ if ('\n' == c)
+ {
+ break;
+ }
+ }
+ c = input_stream.get();
+ }
+ return input_stream.good();
+}
+
+// get everything up to and including the next newline
+// up to the next n characters.
+// add a newline on the end if bail before actual line ending
+bool get_line(std::string& output_string, std::istream& input_stream, int n)
+{
+ int char_count = 0;
+ char c = input_stream.get();
+ while (input_stream.good() && char_count < n)
+ {
+ char_count++;
+ output_string += c;
+ if ('\r' == c)
+ {
+ // skip carriage returns
+ }
+ else
+ {
+ if ('\n' == c)
+ {
+ break;
+ }
+ if (char_count >= n)
+ {
+ output_string.append("\n");
+ break;
+ }
+ }
+ c = input_stream.get();
+ }
+ return input_stream.good();
+}
+
+/* disabled -- might tickle bug in windows iostream
+// backs up the input_stream by line_size + 1 characters
+bool unget_line(const std::string& line, std::istream& input_stream)
+{
+ input_stream.putback('\n'); // unget the newline
+ for (int line_index = line.size()-1; line_index >= 0; line_index--)
+ {
+ input_stream.putback(line[line_index]);
+ }
+ return input_stream.good();
+}
+*/
+
+// removes the last char in 'line' if it matches 'c'
+// returns true if removed last char
+bool remove_last_char(char c, std::string& line)
+{
+ int line_size = line.size();
+ if (line_size > 1
+ && c == line[line_size - 1])
+ {
+ line.replace(line_size - 1, 1, "");
+ return true;
+ }
+ return false;
+}
+
+// replaces escaped characters with the correct characters from left to right
+// "\\\\" ---> '\\' (two backslahes become one)
+// "\\n" ---> '\n' (backslash n becomes carriage return)
+void unescape_string(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size - 1)
+ {
+ if ('\\' == line[index])
+ {
+ if ('\\' == line[index + 1])
+ {
+ line.replace(index, 2, "\\");
+ line_size--;
+ }
+ else if ('n' == line[index + 1])
+ {
+ line.replace(index, 2, "\n");
+ line_size--;
+ }
+ }
+ index++;
+ }
+}
+
+// replaces unescaped characters with expanded equivalents from left to right
+// '\\' ---> "\\\\" (one backslash becomes two)
+// '\n' ---> "\\n" (carriage return becomes backslash n)
+void escape_string(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size)
+ {
+ if ('\\' == line[index])
+ {
+ line.replace(index, 1, "\\\\");
+ line_size++;
+ index++;
+ }
+ else if ('\n' == line[index])
+ {
+ line.replace(index, 1, "\\n");
+ line_size++;
+ index++;
+ }
+ index++;
+ }
+}
+
+// removes '\n' characters
+void replace_newlines_with_whitespace(std::string& line)
+{
+ int line_size = line.size();
+ int index = 0;
+ while (index < line_size)
+ {
+ if ('\n' == line[index])
+ {
+ line.replace(index, 1, " ");
+ }
+ index++;
+ }
+}
+
+// returns 1 for solitary "{"
+// returns -1 for solitary "}"
+// otherwise returns 0
+int get_brace_count(const std::string& line)
+{
+ int index = 0;
+ int line_size = line.size();
+ char c = 0;
+ while (index < line_size)
+ {
+ c = line[index];
+ index++;
+ if (!isspace(c))
+ {
+ break;
+ }
+ }
+ char brace = c;
+ // make sure the rest of the line is whitespace
+ while (index < line_size)
+ {
+ c = line[index];
+ if (!isspace(c))
+ {
+ break;
+ }
+ index++;
+ }
+ if ('\n' != c)
+ {
+ return 0;
+ }
+ if ('{' == brace)
+ {
+ return 1;
+ }
+ else if ('}' == brace)
+ {
+ return -1;
+ }
+ return 0;
+}
+
+// erases any double-quote characters in 'line'
+void remove_double_quotes(std::string& line)
+{
+ int index = 0;
+ int line_size = line.size();
+ while (index < line_size)
+ {
+ if ('"' == line[index])
+ {
+ int count = 1;
+ while (index + count < line_size
+ && '"' == line[index + count])
+ {
+ count++;
+ }
+ line.replace(index, count, "");
+ line_size -= count;
+ }
+ else
+ {
+ index++;
+ }
+ }
+}
+
+// the 'keyword' is defined as the first word on a line
+// the 'value' is everything after the keyword on the same line
+// starting at the first non-whitespace and ending right before the newline
+void get_keyword_and_value(std::string& keyword,
+ std::string& value,
+ const std::string& line)
+{
+ // skip initial whitespace
+ int line_size = line.size();
+ int line_index = 0;
+ char c;
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if (!isspace(c))
+ {
+ break;
+ }
+ line_index++;
+ }
+
+ // get the keyword
+ keyword.assign("");
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if (isspace(c) || '\r' == c || '\n' == c)
+ {
+ break;
+ }
+ keyword += c;
+ line_index++;
+ }
+
+ if (keyword.size() > 0
+ && '\r' != line[line_index]
+ && '\n' != line[line_index])
+
+ {
+ // discard initial white spaces
+ while (line_index < line_size
+ && (' ' == line[line_index]
+ || '\t' == line[line_index]) )
+ {
+ line_index++;
+ }
+
+ // get the value
+ value.assign("");
+ while (line_index < line_size)
+ {
+ c = line[line_index];
+ if ('\r' == c || '\n' == c)
+ {
+ break;
+ }
+ value += c;
+ line_index++;
+ }
+ }
+}
+
+std::istream& fullread(std::istream& str, char *buf, std::streamsize requested)
+{
+ std::streamsize got;
+ std::streamsize total = 0;
+
+ str.read(buf, requested); /*Flawfinder: ignore*/
+ got = str.gcount();
+ total += got;
+ while (got && total < requested)
+ {
+ if (str.fail())
+ str.clear();
+ str.read(buf + total, requested - total); /*Flawfinder: ignore*/
+ got = str.gcount();
+ total += got;
+ }
+ return str;
+}
+
+std::istream& operator>>(std::istream& str, const char *tocheck)
+{
+ char c;
+ const char *p;
+ p = tocheck;
+ while (*p && !str.bad())
+ {
+ str.get(c);
+ if (c != *p)
+ {
+ str.setstate(std::ios::failbit); /*Flawfinder: ignore*/
+ break;
+ }
+ p++;
+ }
+ return str;
+}
diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h
new file mode 100644
index 0000000000..e4099aac57
--- /dev/null
+++ b/indra/llcommon/llstreamtools.h
@@ -0,0 +1,98 @@
+/**
+ * @file llstreamtools.h
+ * @brief some helper functions for parsing legacy simstate and asset files.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STREAM_TOOLS_H
+#define LL_STREAM_TOOLS_H
+
+#include <iostream>
+#include <string>
+
+// unless specifed otherwise these all return input_stream.good()
+
+// skips spaces and tabs
+bool skip_whitespace(std::istream& input_stream);
+
+// skips whitespace and newlines
+bool skip_emptyspace(std::istream& input_stream);
+
+// skips emptyspace and lines that start with a #
+bool skip_comments_and_emptyspace(std::istream& input_stream);
+
+// skips to character after next newline
+bool skip_line(std::istream& input_stream);
+
+// skips to beginning of next non-emptyspace
+bool skip_to_next_word(std::istream& input_stream);
+
+// skips to character after the end of next keyword
+// a 'keyword' is defined as the first word on a line
+bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream);
+
+// skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug
+// in windows iostream
+// skips to beginning of next keyword
+// a 'keyword' is defined as the first word on a line
+//bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stream);
+
+// characters are pulled out of input_stream and appended to output_string
+bool get_word(std::string& output_string, std::istream& input_stream);
+bool get_line(std::string& output_string, std::istream& input_stream);
+
+// characters are pulled out of input_stream (up to a max of 'n')
+// and appended to output_string
+bool get_word(std::string& output_string, std::istream& input_stream, int n);
+bool get_line(std::string& output_string, std::istream& input_stream, int n);
+
+// unget_line() is disabled -- might tickle corruption bug in windows iostream
+//// backs up the input_stream by line_size + 1 characters
+//bool unget_line(const std::string& line, std::istream& input_stream);
+
+// TODO -- move these string manipulator functions to a different file
+
+// removes the last char in 'line' if it matches 'c'
+// returns true if removed last char
+bool remove_last_char(char c, std::string& line);
+
+// replaces escaped characters with the correct characters from left to right
+// "\\" ---> '\\'
+// "\n" ---> '\n'
+void unescape_string(std::string& line);
+
+// replaces unescaped characters with expanded equivalents from left to right
+// '\\' ---> "\\"
+// '\n' ---> "\n"
+void escape_string(std::string& line);
+
+// replaces each '\n' character with ' '
+void replace_newlines_with_whitespace(std::string& line);
+
+// returns 1 for solitary "{"
+// returns -1 for solitary "}"
+// otherwise returns 0
+int get_brace_count(const std::string& line);
+
+// erases any double-quote characters in line
+void remove_double_quotes(std::string& line);
+
+// the 'keyword' is defined as the first word on a line
+// the 'value' is everything after the keyword on the same line
+// starting at the first non-whitespace and ending right before the newline
+void get_keyword_and_value(std::string& keyword,
+ std::string& value,
+ const std::string& line);
+
+// continue to read from the stream until you really can't
+// read anymore or until we hit the count. Some istream
+// implimentations have a max that they will read.
+std::istream& fullread(std::istream& str, char *buf, std::streamsize requested);
+
+std::istream& operator>>(std::istream& str, const char *tocheck);
+
+#endif
+
+
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
new file mode 100644
index 0000000000..0688e43940
--- /dev/null
+++ b/indra/llcommon/llstrider.h
@@ -0,0 +1,38 @@
+/**
+ * @file llstrider.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTRIDER_H
+#define LL_LLSTRIDER_H
+
+#include "stdtypes.h"
+
+template <class Object> class LLStrider
+{
+ union
+ {
+ Object* mObjectp;
+ U8* mBytep;
+ };
+ U32 mSkip;
+public:
+
+ LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); }
+ ~LLStrider() { }
+
+ const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;}
+ void setStride (S32 skipBytes) { mSkip = (skipBytes ? skipBytes : sizeof(Object));}
+
+ void skip(const U32 index) { mBytep += mSkip*index;}
+
+ Object* get() { return mObjectp; }
+ Object* operator->() { return mObjectp; }
+ Object& operator *() { return *mObjectp; }
+ Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; }
+ Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); }
+};
+
+#endif // LL_LLSTRIDER_H
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
new file mode 100644
index 0000000000..50fd881ad7
--- /dev/null
+++ b/indra/llcommon/llstring.cpp
@@ -0,0 +1,835 @@
+/**
+ * @file llstring.cpp
+ * @brief String utility functions and the LLString class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstring.h"
+#include "llerror.h"
+
+std::string ll_safe_string(const char* in)
+{
+ if(in) return std::string(in);
+ return std::string();
+}
+
+U8 hex_as_nybble(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return (U8)(hex - '0');
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return (U8)(10 + hex - 'a');
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return (U8)(10 + hex - 'A');
+ }
+ return 0; // uh - oh, not hex any more...
+}
+
+
+// See http://www.unicode.org/Public/BETA/CVTUTF-1-2/ConvertUTF.c
+// for the Unicode implementation - this doesn't match because it was written before finding
+// it.
+
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr)
+{
+ std::string utf8_str = wstring_to_utf8str(wstr);
+ s << utf8_str;
+ return s;
+}
+
+std::string rawstr_to_utf8(const std::string& raw)
+{
+ LLWString wstr(utf8str_to_wstring(raw));
+ return wstring_to_utf8str(wstr);
+}
+
+S32 wchar_to_utf8chars(llwchar in_char, char* outchars)
+{
+ U32 cur_char = (U32)in_char;
+ char* base = outchars;
+ if (cur_char < 0x80)
+ {
+ *outchars++ = (U8)cur_char;
+ }
+ else if (cur_char < 0x800)
+ {
+ *outchars++ = 0xC0 | (cur_char >> 6);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x10000)
+ {
+ *outchars++ = 0xE0 | (cur_char >> 12);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x200000)
+ {
+ *outchars++ = 0xF0 | (cur_char >> 18);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else if (cur_char < 0x4000000)
+ {
+ *outchars++ = 0xF8 | (cur_char >> 24);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else if (cur_char < 0x80000000)
+ {
+ *outchars++ = 0xFC | (cur_char >> 30);
+ *outchars++ = 0x80 | ((cur_char >> 24) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | cur_char & 0x3F;
+ }
+ else
+ {
+ llwarns << "Invalid Unicode character " << cur_char << "!" << llendl;
+ *outchars++ = LL_UNKNOWN_CHAR;
+ }
+ return outchars - base;
+}
+
+S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
+{
+ const U16* base = inchars;
+ U16 cur_char = *inchars++;
+ llwchar char32 = cur_char;
+ if ((cur_char >= 0xD800) && (cur_char <= 0xDFFF))
+ {
+ // Surrogates
+ char32 = ((llwchar)(cur_char - 0xD800)) << 10;
+ cur_char = *inchars++;
+ char32 += (llwchar)(cur_char - 0xDC00) + 0x0010000UL;
+ }
+ else
+ {
+ char32 = (llwchar)cur_char;
+ }
+ *outchar = char32;
+ return inchars - base;
+}
+
+S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8p)
+{
+ // Get 32 bit char32
+ llwchar char32;
+ S32 nchars16 = utf16chars_to_wchar(inchars, &char32);
+ // Convert to utf8
+ S32 nchars8 = wchar_to_utf8chars(char32, outchars);
+ if (nchars8p)
+ {
+ *nchars8p = nchars8;
+ }
+ return nchars16;
+}
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len)
+{
+ llutf16string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ U32 cur_char = utf32str[i];
+ if (cur_char > 0xFFFF)
+ {
+ out += (0xD7C0 + (cur_char >> 10));
+ out += (0xDC00 | (cur_char & 0x3FF));
+ }
+ else
+ {
+ out += cur_char;
+ }
+ i++;
+ }
+ return out;
+}
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str)
+{
+ const S32 len = (S32)utf32str.length();
+ return wstring_to_utf16str(utf32str, len);
+}
+
+llutf16string utf8str_to_utf16str ( const LLString& utf8str )
+{
+ LLWString wstr = utf8str_to_wstring ( utf8str );
+ return wstring_to_utf16str ( wstr );
+}
+
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len)
+{
+ LLWString wout;
+
+ S32 i = 0;
+ // craziness to make gcc happy (llutf16string.c_str() is tweaked on linux):
+ const U16* chars16 = &(*(utf16str.begin()));
+ while (i < len)
+ {
+ llwchar cur_char;
+ i += utf16chars_to_wchar(chars16+i, &cur_char);
+ wout += cur_char;
+ }
+ return wout;
+}
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str)
+{
+ const S32 len = (S32)utf16str.length();
+ return utf16str_to_wstring(utf16str, len);
+}
+
+S32 wchar_utf8_length(const llwchar wc)
+{
+ if (wc < 0x80)
+ {
+ // This case will also catch negative values which are
+ // technically invalid.
+ return 1;
+ }
+ else if (wc < 0x800)
+ {
+ return 2;
+ }
+ else if (wc < 0x10000)
+ {
+ return 3;
+ }
+ else if (wc < 0x200000)
+ {
+ return 4;
+ }
+ else if (wc < 0x4000000)
+ {
+ return 5;
+ }
+ else
+ {
+ return 6;
+ }
+}
+
+
+S32 wstring_utf8_length(const LLWString& wstr)
+{
+ S32 len = 0;
+ for (S32 i = 0; i < (S32)wstr.length(); i++)
+ {
+ len += wchar_utf8_length(wstr[i]);
+ }
+ return len;
+}
+
+
+LLWString utf8str_to_wstring(const std::string& utf8str, S32 len)
+{
+ LLWString wout;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ llwchar unichar;
+ U8 cur_char = utf8str[i];
+
+ if (cur_char < 0x80)
+ {
+ // Ascii character, just add it
+ unichar = cur_char;
+ }
+ else
+ {
+ S32 cont_bytes = 0;
+ if ((cur_char >> 5) == 0x6) // Two byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x1F&cur_char);
+ cont_bytes = 1;
+ }
+ else if ((cur_char >> 4) == 0xe) // Three byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x0F&cur_char);
+ cont_bytes = 2;
+ }
+ else if ((cur_char >> 3) == 0x1e) // Four byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x07&cur_char);
+ cont_bytes = 3;
+ }
+ else if ((cur_char >> 2) == 0x3e) // Five byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x03&cur_char);
+ cont_bytes = 4;
+ }
+ else if ((cur_char >> 1) == 0x7e) // Six byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x01&cur_char);
+ cont_bytes = 5;
+ }
+ else
+ {
+ wout += LL_UNKNOWN_CHAR;
+ ++i;
+ continue;
+ }
+
+ // Check that this character doesn't go past the end of the string
+ S32 end = (len < (i + cont_bytes)) ? len : (i + cont_bytes);
+ do
+ {
+ ++i;
+
+ cur_char = utf8str[i];
+ if ( (cur_char >> 6) == 0x2 )
+ {
+ unichar <<= 6;
+ unichar += (0x3F&cur_char);
+ }
+ else
+ {
+ // Malformed sequence - roll back to look at this as a new char
+ unichar = LL_UNKNOWN_CHAR;
+ --i;
+ break;
+ }
+ } while(i < end);
+
+ // Handle overlong characters and NULL characters
+ if ( ((cont_bytes == 1) && (unichar < 0x80))
+ || ((cont_bytes == 2) && (unichar < 0x800))
+ || ((cont_bytes == 3) && (unichar < 0x10000))
+ || ((cont_bytes == 4) && (unichar < 0x200000))
+ || ((cont_bytes == 5) && (unichar < 0x4000000)) )
+ {
+ unichar = LL_UNKNOWN_CHAR;
+ }
+ }
+
+ wout += unichar;
+ ++i;
+ }
+ return wout;
+}
+
+LLWString utf8str_to_wstring(const std::string& utf8str)
+{
+ const S32 len = (S32)utf8str.length();
+ return utf8str_to_wstring(utf8str, len);
+}
+
+std::string wstring_to_utf8str(const LLWString& utf32str, S32 len)
+{
+ std::string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ char tchars[8]; /* Flawfinder: ignore */
+ S32 n = wchar_to_utf8chars(utf32str[i], tchars);
+ tchars[n] = 0;
+ out += tchars;
+ i++;
+ }
+ return out;
+}
+
+std::string wstring_to_utf8str(const LLWString& utf32str)
+{
+ const S32 len = (S32)utf32str.length();
+ return wstring_to_utf8str(utf32str, len);
+}
+
+std::string utf16str_to_utf8str(const llutf16string& utf16str)
+{
+ return wstring_to_utf8str(utf16str_to_wstring(utf16str));
+}
+
+std::string utf16str_to_utf8str(const llutf16string& utf16str, S32 len)
+{
+ return wstring_to_utf8str(utf16str_to_wstring(utf16str, len), len);
+}
+
+
+//LLWString wstring_truncate(const LLWString &wstr, const S32 max_len)
+//{
+// return wstr.substr(0, llmin((S32)wstr.length(), max_len));
+//}
+//
+//
+//LLWString wstring_trim(const LLWString &wstr)
+//{
+// LLWString outstr;
+// outstr = wstring_trimhead(wstr);
+// outstr = wstring_trimtail(outstr);
+// return outstr;
+//}
+//
+//
+//LLWString wstring_trimhead(const LLWString &wstr)
+//{
+// if(wstr.empty())
+// {
+// return wstr;
+// }
+//
+// S32 i = 0;
+// while((i < (S32)wstr.length()) && iswspace(wstr[i]))
+// {
+// i++;
+// }
+// return wstr.substr(i, wstr.length() - i);
+//}
+//
+//
+//LLWString wstring_trimtail(const LLWString &wstr)
+//{
+// if(wstr.empty())
+// {
+// return wstr;
+// }
+//
+// S32 len = (S32)wstr.length();
+//
+// S32 i = len - 1;
+// while (i >= 0 && iswspace(wstr[i]))
+// {
+// i--;
+// }
+//
+// if (i >= 0)
+// {
+// return wstr.substr(0, i + 1);
+// }
+// return wstr;
+//}
+//
+//
+//LLWString wstring_copyinto(const LLWString &dest, const LLWString &src, const S32 insert_offset)
+//{
+// llassert( insert_offset <= (S32)dest.length() );
+//
+// LLWString out_str = dest.substr(0, insert_offset);
+// out_str += src;
+// LLWString tail = dest.substr(insert_offset);
+// out_str += tail;
+//
+// return out_str;
+//}
+
+
+//LLWString wstring_detabify(const LLWString &wstr, const S32 num_spaces)
+//{
+// LLWString out_str;
+// // Replace tabs with spaces
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] == '\t')
+// {
+// for (S32 j = 0; j < num_spaces; j++)
+// out_str += ' ';
+// }
+// else
+// {
+// out_str += wstr[i];
+// }
+// }
+// return out_str;
+//}
+
+
+//LLWString wstring_makeASCII(const LLWString &wstr)
+//{
+// // Replace non-ASCII chars with replace_char
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// if (out_str[i] > 0x7f)
+// {
+// out_str[i] = LL_UNKNOWN_CHAR;
+// }
+// }
+// return out_str;
+//}
+
+
+//LLWString wstring_substChar(const LLWString &wstr, const llwchar target_char, const llwchar replace_char)
+//{
+// // Replace all occurences of target_char with replace_char
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// if (out_str[i] == target_char)
+// {
+// out_str[i] = replace_char;
+// }
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_tolower(const LLWString &wstr)
+//{
+// LLWString out_str = wstr;
+// for (S32 i = 0; i < (S32)out_str.length(); i++)
+// {
+// out_str[i] = towlower(out_str[i]);
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_convert_to_lf(const LLWString &wstr)
+//{
+// const llwchar CR = 13;
+// // Remove carriage returns from string with CRLF
+// LLWString out_str;
+//
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] != CR)
+// {
+// out_str += wstr[i];
+// }
+// }
+// return out_str;
+//}
+//
+//
+//LLWString wstring_convert_to_crlf(const LLWString &wstr)
+//{
+// const llwchar LF = 10;
+// const llwchar CR = 13;
+// // Remove carriage returns from string with CRLF
+// LLWString out_str;
+//
+// for (S32 i = 0; i < (S32)wstr.length(); i++)
+// {
+// if (wstr[i] == LF)
+// {
+// out_str += CR;
+// }
+// out_str += wstr[i];
+// }
+// return out_str;
+//}
+
+
+//S32 wstring_compare_insensitive(const LLWString &lhs, const LLWString &rhs)
+//{
+//
+// if (lhs == rhs)
+// {
+// return 0;
+// }
+//
+// if (lhs.empty())
+// {
+// return rhs.empty() ? 0 : 1;
+// }
+//
+// if (rhs.empty())
+// {
+// return -1;
+// }
+//
+//#ifdef LL_LINUX
+// // doesn't work because gcc 2.95 doesn't correctly implement c_str(). Sigh...
+// llerrs << "wstring_compare_insensitive doesn't work on Linux!" << llendl;
+// return 0;
+//#else
+// LLWString lhs_lower = lhs;
+// LLWString::toLower(lhs_lower);
+// std::string lhs_lower = wstring_to_utf8str(lhs_lower);
+// LLWString rhs_lower = lhs;
+// LLWString::toLower(rhs_lower);
+// std::string rhs_lower = wstring_to_utf8str(rhs_lower);
+//
+// return strcmp(lhs_lower.c_str(), rhs_lower.c_str());
+//#endif
+//}
+
+
+std::string utf8str_trim(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::trim(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+
+std::string utf8str_tolower(const std::string& utf8str)
+{
+ LLWString out_str = utf8str_to_wstring(utf8str);
+ LLWString::toLower(out_str);
+ return wstring_to_utf8str(out_str);
+}
+
+
+S32 utf8str_compare_insensitive(const std::string& lhs, const std::string& rhs)
+{
+ LLWString wlhs = utf8str_to_wstring(lhs);
+ LLWString wrhs = utf8str_to_wstring(rhs);
+ return LLWString::compareInsensitive(wlhs.c_str(), wrhs.c_str());
+}
+
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len)
+{
+ if (0 == max_len)
+ {
+ return std::string();
+ }
+ if ((S32)utf8str.length() <= max_len)
+ {
+ return utf8str;
+ }
+ else
+ {
+ S32 cur_char = max_len;
+
+ // If we're ASCII, we don't need to do anything
+ if ((U8)utf8str[cur_char] > 0x7f)
+ {
+ // If first two bits are (10), it's the tail end of a multibyte char. We need to shift back
+ // to the first character
+ while (0x80 == (0xc0 & utf8str[cur_char]))
+ {
+ cur_char--;
+ // Keep moving forward until we hit the first char;
+ if (cur_char == 0)
+ {
+ // Make sure we don't trash memory if we've got a bogus string.
+ break;
+ }
+ }
+ }
+ // The byte index we're on is one we want to get rid of, so we only want to copy up to (cur_char-1) chars
+ return utf8str.substr(0, cur_char);
+ }
+}
+
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::replaceChar(wstr, target_char, replace_char);
+ //wstr = wstring_substChar(wstr, target_char, replace_char);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string utf8str_makeASCII(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWString::_makeASCII(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string mbcsstring_makeASCII(const std::string& wstr)
+{
+ // Replace non-ASCII chars with replace_char
+ std::string out_str = wstr;
+ for (S32 i = 0; i < (S32)out_str.length(); i++)
+ {
+ if ((U8)out_str[i] > 0x7f)
+ {
+ out_str[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+ return out_str;
+}
+
+S32 LLStringOps::collate(const llwchar* a, const llwchar* b)
+{
+ #if LL_WINDOWS
+ // in Windows, wide string functions operator on 16-bit strings,
+ // not the proper 32 bit wide string
+ return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str());
+ #else
+ return wcscoll(a, b);
+ #endif
+}
+
+namespace LLStringFn
+{
+ void replace_nonprintable(std::basic_string<char>& string, char replacement)
+ {
+ const char MIN = 0x20;
+ std::basic_string<char>::size_type len = string.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if(string[ii] < MIN)
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable(
+ std::basic_string<llwchar>& string,
+ llwchar replacement)
+ {
+ const llwchar MIN = 0x20;
+ const llwchar MAX = 0x7f;
+ std::basic_string<llwchar>::size_type len = string.size();
+ for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii)
+ {
+ if((string[ii] < MIN) || (string[ii] > MAX))
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable_and_pipe(std::basic_string<char>& str,
+ char replacement)
+ {
+ const char MIN = 0x20;
+ const char PIPE = 0x7c;
+ std::basic_string<char>::size_type len = str.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if( (str[ii] < MIN) || (str[ii] == PIPE) )
+ {
+ str[ii] = replacement;
+ }
+ }
+ }
+
+ void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str,
+ llwchar replacement)
+ {
+ const llwchar MIN = 0x20;
+ const llwchar MAX = 0x7f;
+ const llwchar PIPE = 0x7c;
+ std::basic_string<llwchar>::size_type len = str.size();
+ for(std::basic_string<llwchar>::size_type ii = 0; ii < len; ++ii)
+ {
+ if( (str[ii] < MIN) || (str[ii] > MAX) || (str[ii] == PIPE) )
+ {
+ str[ii] = replacement;
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////
+// Testing
+
+#ifdef _DEBUG
+
+template<class T>
+void LLStringBase<T>::testHarness()
+{
+ LLString s1;
+
+ llassert( s1.c_str() == NULL );
+ llassert( s1.size() == 0 );
+ llassert( s1.empty() );
+
+ LLString s2( "hello");
+ llassert( !strcmp( s2.c_str(), "hello" ) );
+ llassert( s2.size() == 5 );
+ llassert( !s2.empty() );
+ LLString s3( s2 );
+
+ llassert( "hello" == s2 );
+ llassert( s2 == "hello" );
+ llassert( s2 > "gello" );
+ llassert( "gello" < s2 );
+ llassert( "gello" != s2 );
+ llassert( s2 != "gello" );
+
+ LLString s4 = s2;
+ llassert( !s4.empty() );
+ s4.empty();
+ llassert( s4.empty() );
+
+ LLString s5("");
+ llassert( s5.empty() );
+
+ llassert( isValidIndex(s5, 0) );
+ llassert( !isValidIndex(s5, 1) );
+
+ s3 = s2;
+ s4 = "hello again";
+
+ s4 += "!";
+ s4 += s4;
+ llassert( s4 == "hello again!hello again!" );
+
+
+ LLString s6 = s2 + " " + s2;
+ LLString s7 = s6;
+ llassert( s6 == s7 );
+ llassert( !( s6 != s7) );
+ llassert( !(s6 < s7) );
+ llassert( !(s6 > s7) );
+
+ llassert( !(s6 == "hi"));
+ llassert( s6 == "hello hello");
+ llassert( s6 < "hi");
+
+ llassert( s6[1] == 'e' );
+ s6[1] = 'f';
+ llassert( s6[1] == 'f' );
+
+ s2.erase( 4, 1 );
+ llassert( s2 == "hell");
+ s2.insert( 0, 'y' );
+ llassert( s2 == "yhell");
+ s2.erase( 1, 3 );
+ llassert( s2 == "yl");
+ s2.insert( 1, "awn, don't yel");
+ llassert( s2 == "yawn, don't yell");
+
+ LLString s8 = s2.substr( 6, 5 );
+ llassert( s8 == "don't" );
+
+ LLString s9 = " \t\ntest \t\t\n ";
+ trim(s9);
+ llassert( s9 == "test" );
+
+ s8 = "abc123&*(ABC";
+
+ s9 = s8;
+ toUpper(s9);
+ llassert( s9 == "ABC123&*(ABC" );
+
+ s9 = s8;
+ toLower(s9);
+ llassert( s9 == "abc123&*(abc" );
+
+
+ LLString s10( 10, 'x' );
+ llassert( s10 == "xxxxxxxxxx" );
+
+ LLString s11( "monkey in the middle", 7, 2 );
+ llassert( s11 == "in" );
+
+ LLString s12; //empty
+ s12 += "foo";
+ llassert( s12 == "foo" );
+
+ LLString s13; //empty
+ s13 += 'f';
+ llassert( s13 == "f" );
+}
+
+
+#endif // _DEBUG
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
new file mode 100644
index 0000000000..dca8ce4f3e
--- /dev/null
+++ b/indra/llcommon/llstring.h
@@ -0,0 +1,1281 @@
+/**
+ * @file llstring.h
+ * @brief String utility functions and LLString class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+#include <algorithm>
+#include <map>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#if LL_LINUX
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+const char LL_UNKNOWN_CHAR = '?';
+
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+class LLUUID;
+class LLColor4;
+class LLColor4U;
+
+#if (LL_DARWIN || (LL_LINUX && __GNUC__ > 2))
+// Template specialization of char_traits for U16s. Only necessary on Mac for now (exists on Windows, unused/broken on Linux/gcc2.95)
+namespace std
+{
+template<>
+struct char_traits<U16>
+{
+ typedef U16 char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
+
+ static size_t
+ length(const char_type* __s)
+ {
+ const char_type *cur_char = __s;
+ while (*cur_char != 0)
+ {
+ ++cur_char;
+ }
+ return cur_char - __s;
+ }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ {
+ // This isn't right.
+ //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
+
+ // I don't think there's a standard 'memset' for 16-bit values.
+ // Do this the old-fashioned way.
+
+ size_t __i;
+ for(__i = 0; __i < __n; __i++)
+ {
+ __s[__i] = __a;
+ }
+ return __s;
+ }
+
+ static char_type
+ to_char_type(const int_type& __c)
+ { return static_cast<char_type>(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c)
+ { return static_cast<int_type>(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(EOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return (__c == eof()) ? 0 : __c; }
+ };
+};
+#endif
+
+class LLStringOps
+{
+public:
+ static char toUpper(char elem) { return toupper(elem); }
+ static llwchar toUpper(llwchar elem) { return towupper(elem); }
+
+ static char toLower(char elem) { return tolower(elem); }
+ static llwchar toLower(llwchar elem) { return towlower(elem); }
+
+ static BOOL isSpace(char elem) { return isspace(elem) != 0; }
+ static BOOL isSpace(llwchar elem) { return iswspace(elem) != 0; }
+
+ static BOOL isUpper(char elem) { return isupper(elem) != 0; }
+ static BOOL isUpper(llwchar elem) { return iswupper(elem) != 0; }
+
+ static BOOL isLower(char elem) { return islower(elem) != 0; }
+ static BOOL isLower(llwchar elem) { return iswlower(elem) != 0; }
+
+ static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
+ static S32 collate(const llwchar* a, const llwchar* b);
+
+ static BOOL isDigit(char a) { return isdigit(a) != 0; }
+ static BOOL isDigit(llwchar a) { return iswdigit(a) != 0; }
+};
+
+//RN: I used a templated base class instead of a pure interface class to minimize code duplication
+// but it might be worthwhile to just go with two implementations (LLString and LLWString) of
+// an interface class, unless we can think of a good reason to have a std::basic_string polymorphic base
+
+//****************************************************************
+// NOTA BENE: do *NOT* dynamically allocate memory inside of LLStringBase as the {*()^#%*)#%W^*)#%*)STL implentation
+// of basic_string doesn't provide a virtual destructor. If we need to allocate resources specific to LLString
+// then we should either customize std::basic_string to linden::basic_string or change LLString to be a wrapper
+// that contains an instance of std::basic_string. Similarly, overriding methods defined in std::basic_string will *not*
+// be called in a polymorphic manner (passing an instance of basic_string to a particular function)
+//****************************************************************
+
+template <class T>
+class LLStringBase : public std::basic_string<T>
+{
+public:
+ typedef typename std::basic_string<T>::size_type size_type;
+
+ // naming convention follows those set for LLUUID
+// static LLStringBase null; // deprecated for std::string compliance
+// static LLStringBase zero_length; // deprecated for std::string compliance
+
+
+ // standard constructors
+ LLStringBase() : std::basic_string<T>() {}
+ LLStringBase(const LLStringBase& s): std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s) : std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s, size_type pos, size_type n = std::basic_string<T>::npos)
+ : std::basic_string<T>(s, pos, n) {}
+ LLStringBase(size_type count, const T& c) : std::basic_string<T>() { assign(count, c);}
+ // custom constructors
+ LLStringBase(const T* s);
+ LLStringBase(const T* s, size_type n);
+ LLStringBase(const T* s, size_type pos, size_type n );
+
+#if LL_LINUX
+ void clear() { assign(null); }
+
+ LLStringBase<T>& assign(const T* s);
+ LLStringBase<T>& assign(const T* s, size_type n);
+ LLStringBase<T>& assign(const LLStringBase& s);
+ LLStringBase<T>& assign(size_type n, const T& c);
+ LLStringBase<T>& assign(const T* a, const T* b);
+ LLStringBase<T>& assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2);
+ LLStringBase<T>& assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2);
+
+ // workaround for bug in gcc2 STL headers.
+ #if ((__GNUC__ <= 2) && (!defined _STLPORT_VERSION))
+ const T* c_str () const
+ {
+ if (length () == 0)
+ {
+ static const T zero = 0;
+ return &zero;
+ }
+
+ //terminate ();
+ { string_char_traits<T>::assign(const_cast<T*>(data())[length()], string_char_traits<T>::eos()); }
+
+ return data ();
+ }
+ #endif
+#endif
+
+ bool operator==(const T* _Right) const { return _Right ? (std::basic_string<T>::compare(_Right) == 0) : this->empty(); }
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static LLStringBase null;
+
+ typedef std::map<std::string, std::string> format_map_t;
+ static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
+
+ static BOOL isValidIndex(const std::basic_string<T>& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static void trimHead(std::basic_string<T>& string);
+ static void trimTail(std::basic_string<T>& string);
+ static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
+ static void truncate(std::basic_string<T>& string, size_type count);
+
+ static void toUpper(std::basic_string<T>& string);
+ static void toLower(std::basic_string<T>& string);
+
+ // True if this is the head of s.
+ static BOOL isHead( const std::basic_string<T>& string, const T* s );
+
+ static void addCRLF(std::basic_string<T>& string);
+ static void removeCRLF(std::basic_string<T>& string);
+
+ static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
+ static void replaceChar( std::basic_string<T>& string, T target, T replacement );
+
+ static BOOL containsNonprintable(const std::basic_string<T>& string);
+ static void stripNonprintable(std::basic_string<T>& string);
+
+ /**
+ * @brief Unsafe way to make ascii characters. You should probably
+ * only call this when interacting with the host operating system.
+ * The 1 byte LLString does not work correctly.
+ * The 2 and 4 byte LLString probably work, so LLWString::_makeASCII
+ * should work.
+ */
+ static void _makeASCII(std::basic_string<T>& string);
+
+ static BOOL read(std::basic_string<T>& string, const char* filename); /*Flawfinder: ignore*/
+ static BOOL write(std::basic_string<T>& string, const char* filename);
+
+ // Conversion to other data types
+ static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
+ static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
+ static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
+ static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
+ static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
+ static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
+ static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
+ static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
+ static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Utility functions for working with char*'s and strings
+
+ // Like strcmp but also handles empty strings. Uses
+ // current locale.
+ static S32 compareStrings(const T* lhs, const T* rhs);
+
+ // case insensitive version of above. Uses current locale on
+ // Win32, and falls back to a non-locale aware comparison on
+ // Linux.
+ static S32 compareInsensitive(const T* lhs, const T* rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+
+ // A replacement for strncpy.
+ // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
+ // up to dst_size-1 characters of src.
+ static void copy(T* dst, const T* src, size_type dst_size);
+
+ // Copies src into dst at a given offset.
+ static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+
+#ifdef _DEBUG
+ static void testHarness();
+#endif
+
+};
+
+template<class T> LLStringBase<T> LLStringBase<T>::null;
+
+typedef LLStringBase<char> LLString;
+typedef LLStringBase<llwchar> LLWString;
+
+struct LLDictionaryLess
+{
+public:
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ return (LLString::precedesDict(a, b) ? true : false);
+ }
+};
+
+
+/**
+ * Simple support functions
+ */
+
+/**
+ * @breif chop off the trailing characters in a string.
+ *
+ * This function works on bytes rather than glyphs, so this will
+ * incorrectly truncate non-single byte strings.
+ * Use utf8str_truncate() for utf8 strings
+ * @return a copy of in string minus the trailing count characters.
+ */
+inline std::string chop_tail_copy(
+ const std::string& in,
+ std::string::size_type count)
+{
+ return std::string(in, 0, in.length() - count);
+}
+
+/**
+ * @brief Return a string constructed from in without crashing if the
+ * pointer is NULL.
+ */
+std::string ll_safe_string(const char* in);
+
+/**
+ * @brief This translates a nybble stored as a hex value from 0-f back
+ * to a nybble in the low order bits of the return byte.
+ */
+U8 hex_as_nybble(char hex);
+
+
+/**
+ * Unicode support
+ */
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// of the data may not be recovered.
+std::string rawstr_to_utf8(const std::string& raw);
+
+//
+// We should never use UTF16 except when communicating with Win32!
+//
+typedef std::basic_string<U16> llutf16string;
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
+LLWString utf16str_to_wstring(const llutf16string &utf16str);
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
+llutf16string wstring_to_utf16str(const LLWString &utf32str);
+
+llutf16string utf8str_to_utf16str ( const LLString& utf8str, S32 len);
+llutf16string utf8str_to_utf16str ( const LLString& utf8str );
+
+LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
+LLWString utf8str_to_wstring(const std::string &utf8str);
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+// Special hack for llfilepicker.cpp:
+S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8 = 0);
+S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar);
+S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+//
+std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
+std::string wstring_to_utf8str(const LLWString &utf32str);
+
+std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
+std::string utf16str_to_utf8str(const llutf16string &utf16str);
+
+// Length of this UTF32 string in bytes when transformed to UTF8
+S32 wstring_utf8_length(const LLWString& wstr);
+
+// Length in bytes of this wide char in a UTF8 string
+S32 wchar_utf8_length(const llwchar wc);
+
+std::string utf8str_tolower(const std::string& utf8str);
+
+/**
+ * @brief Properly truncate a utf8 string to a maximum byte count.
+ *
+ * The returned string may be less than max_len if the truncation
+ * happens in the middle of a glyph. If max_len is longer than the
+ * string passed in, the return value == utf8str.
+ * @param utf8str A valid utf8 string to truncate.
+ * @param max_len The maximum number of bytes in the returne
+ * @return Returns a valid utf8 string with byte count <= max_len.
+ */
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
+
+std::string utf8str_trim(const std::string& utf8str);
+
+S32 utf8str_compare_insensitive(
+ const std::string& lhs,
+ const std::string& rhs);
+
+/**
+ * @brief Replace all occurences of target_char with replace_char
+ *
+ * @param utf8str A utf8 string to process.
+ * @param target_char The wchar to be replaced
+ * @param replace_char The wchar which is written on replace
+ */
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char);
+
+std::string utf8str_makeASCII(const std::string& utf8str);
+
+// Hack - used for evil notecards.
+std::string mbcsstring_makeASCII(const std::string& str);
+
+template <class T>
+std::ostream& operator<<(std::ostream &s, const LLStringBase<T> &str)
+{
+ s << ((std::basic_string<T>)str);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr);
+
+
+/**
+ * Many of the 'strip' and 'replace' methods of LLStringBase need
+ * specialization to work with the signed char type.
+ * Sadly, it is not possible (AFAIK) to specialize a single method of
+ * a template class.
+ * That stuff should go here.
+ */
+namespace LLStringFn
+{
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<char>& string,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * a wide string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<llwchar>& string,
+ llwchar replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<char>& str,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a wide string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement wide character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str,
+ llwchar replacement);
+}
+
+////////////////////////////////////////////////////////////
+
+// static
+template<class T>
+S32 LLStringBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map)
+{
+ typedef typename std::basic_string<T>::size_type string_size_type_t;
+ S32 res = 0;
+ for (format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ U32 fmtlen = iter->first.size();
+ string_size_type_t n = 0;
+ while (1)
+ {
+ n = s.find(iter->first, n);
+ if (n == std::basic_string<T>::npos)
+ {
+ break;
+ }
+ s.erase(n, fmtlen);
+ s.insert(n, iter->second);
+ n += fmtlen;
+ ++res;
+ }
+ }
+ return res;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareStrings(const T* lhs, const T* rhs)
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0])
+ {
+ result = -1;
+ }
+ else
+ {
+ result = LLStringOps::collate(lhs, rhs);
+ }
+ return result;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareInsensitive(const T* lhs, const T* rhs )
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0] )
+ {
+ result = -1;
+ }
+ else
+ {
+ LLStringBase<T> lhs_string(lhs);
+ LLStringBase<T> rhs_string(rhs);
+ LLStringBase<T>::toUpper(lhs_string);
+ LLStringBase<T>::toUpper(rhs_string);
+ result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+ }
+ return result;
+}
+
+
+// Case sensitive comparison with good handling of numbers. Does not use current locale.
+// a.k.a. strdictcmp()
+
+//static
+template<class T>
+S32 LLStringBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+ S32 bias = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( bias==0 ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
+ }else{
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ if( ca==cb ) ca += bias;
+ return ca-cb;
+}
+
+// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+// static
+template<class T>
+BOOL LLStringBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+{
+ if( a.size() && b.size() )
+ {
+ return (LLStringBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
+ }
+ else
+ {
+ return (!b.empty());
+ }
+}
+
+// Constructors
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s ) : std::basic_string<T>()
+{
+ if (s) assign(s);
+}
+
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type n ) : std::basic_string<T>()
+{
+ if (s) assign(s, n);
+}
+
+// Init from a substring
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type pos, size_type n ) : std::basic_string<T>()
+{
+ if( s )
+ {
+ assign(s + pos, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+}
+
+#if LL_LINUX
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s, size_type n)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const LLStringBase<T>& s)
+{
+ std::basic_string<T>::assign(s);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(size_type n, const T& c)
+{
+ std::basic_string<T>::assign(n, c);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* a, const T* b)
+{
+ if (a > b)
+ assign(LLStringBase<T>::null);
+ else
+ assign(a, (size_type) (b-a));
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+#endif
+
+//static
+template<class T>
+void LLStringBase<T>::toUpper(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::toLower(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimHead(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ size_type i = 0;
+ while( i < string.length() && LLStringOps::isSpace( string[i] ) )
+ {
+ i++;
+ }
+ string.erase(0, i);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimTail(std::basic_string<T>& string)
+{
+ if( string.size() )
+ {
+ size_type len = string.length();
+ size_type i = len;
+ while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
+ {
+ i--;
+ }
+
+ string.erase( i, len - i );
+ }
+}
+
+
+// Replace line feeds with carriage return-line feed pairs.
+//static
+template<class T>
+void LLStringBase<T>::addCRLF(std::basic_string<T>& string)
+{
+ const T LF = 10;
+ const T CR = 13;
+
+ // Count the number of line feeds
+ size_type count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len; i++ )
+ {
+ if( string[i] == LF )
+ {
+ count++;
+ }
+ }
+
+ // Insert a carriage return before each line feed
+ if( count )
+ {
+ size_type size = len + count;
+ T *t = new T[size];
+ size_type j = 0;
+ for( i = 0; i < len; ++i )
+ {
+ if( string[i] == LF )
+ {
+ t[j] = CR;
+ ++j;
+ }
+ t[j] = string[i];
+ ++j;
+ }
+
+ string.assign(t, size);
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringBase<T>::removeCRLF(std::basic_string<T>& string)
+{
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count; i++ )
+ {
+ if( string[i+cr_count] == CR )
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ for (found_pos = string.find(target, found_pos);
+ found_pos != std::basic_string<T>::npos;
+ found_pos = string.find(target, found_pos))
+ {
+ string[found_pos] = replacement;
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+{
+ const char LF = 10;
+ const S8 MIN = 32;
+// const S8 MAX = 127;
+
+ size_type len = string.size();
+ for( size_type i = 0; i < len; i++ )
+ {
+ // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
+ // which has a max value of 127.
+ if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
+ {
+ string[i] = replacement;
+ }
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab )
+{
+ llassert( spaces_per_tab >= 0 );
+
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ LLStringBase<T> out_str;
+ // Replace tabs with spaces
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] == TAB)
+ {
+ for (size_type j = 0; j < spaces_per_tab; j++)
+ out_str += SPACE;
+ }
+ else
+ {
+ out_str += string[i];
+ }
+ }
+ string = out_str;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::containsNonprintable(const std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ BOOL rv = FALSE;
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ if(string[i] < MIN)
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+template<class T>
+void LLStringBase<T>::stripNonprintable(std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ size_type j = 0;
+ if (string.empty())
+ {
+ return;
+ }
+ char* c_string = new char[string.size() + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ char* read_head = &string[i];
+ write_head = &c_string[j];
+ if(!(*read_head < MIN))
+ {
+ *write_head = *read_head;
+ ++j;
+ }
+ }
+ c_string[j]= '\0';
+ string = c_string;
+ delete []c_string;
+}
+
+template<class T>
+void LLStringBase<T>::_makeASCII(std::basic_string<T>& string)
+{
+ // Replace non-ASCII chars with LL_UNKNOWN_CHAR
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] > 0x7f)
+ {
+ string[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copy( T* dst, const T* src, size_type dst_size )
+{
+ if( dst_size > 0 )
+ {
+ size_type min_len = 0;
+ if( src )
+ {
+ min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
+ memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
+ }
+ dst[min_len] = '\0';
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+{
+ llassert( offset <= dst.length() );
+
+ // special case - append to end of string and avoid expensive (when strings are large) string manipulations
+ if ( offset == dst.length() )
+ {
+ dst += src;
+ }
+ else
+ {
+ LLWString tail = dst.substr(offset);
+
+ dst = dst.substr(0, offset);
+ dst += src;
+ dst += tail;
+ };
+}
+
+// True if this is the head of s.
+//static
+template<class T>
+BOOL LLStringBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+{
+ if( string.empty() )
+ {
+ // Early exit
+ return FALSE;
+ }
+ else
+ {
+ return (strncmp( s, string.c_str(), string.size() ) == 0);
+ }
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::read(std::basic_string<T>& string, const char* filename) /*Flawfinder: ignore*/
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::read at %s:%d\n", __FILE__, __LINE__);
+#else
+ llifstream ifs(filename, llifstream::binary);
+ if (!ifs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ std::basic_ostringstream<T> oss;
+
+ oss << ifs.rdbuf();
+
+ string = oss.str();
+
+ ifs.close();
+#endif
+ return TRUE;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::write(std::basic_string<T>& string, const char* filename)
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::write at %s:%d\n", __FILE__, __LINE__);
+#else
+ llofstream ofs(filename, llofstream::binary);
+ if (!ofs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ ofs << string;
+
+ ofs.close();
+#endif
+ return TRUE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ if(
+ (temp == "1") ||
+ (temp == "T") ||
+ (temp == "t") ||
+ (temp == "TRUE") ||
+ (temp == "true") ||
+ (temp == "True") )
+ {
+ value = TRUE;
+ return TRUE;
+ }
+ else
+ if(
+ (temp == "0") ||
+ (temp == "F") ||
+ (temp == "f") ||
+ (temp == "FALSE") ||
+ (temp == "false") ||
+ (temp == "False") )
+ {
+ value = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
+ {
+ value = (U8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
+ {
+ value = (S8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
+ {
+ value = (S16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
+ {
+ value = (U16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow reporting here
+ //if( ULONG_MAX == v )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if((LONG_MAX == v) || (LONG_MIN == v))
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+{
+ F64 value64 = 0.0;
+ BOOL success = convertToF64(string, value64);
+ if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
+ {
+ value = (F32) value64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+void LLStringBase<T>::truncate(std::basic_string<T>& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+#endif // LL_STRING_H
diff --git a/indra/llcommon/llstringtable.cpp b/indra/llcommon/llstringtable.cpp
new file mode 100644
index 0000000000..483c4fe502
--- /dev/null
+++ b/indra/llcommon/llstringtable.cpp
@@ -0,0 +1,323 @@
+/**
+ * @file llstringtable.cpp
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstringtable.h"
+#include "llstl.h"
+
+LLStringTable gStringTable(32768);
+
+LLStringTable::LLStringTable(int tablesize)
+: mUniqueEntries(0)
+{
+ S32 i;
+ if (!tablesize)
+ tablesize = 4096; // some arbitrary default
+ // Make sure tablesize is power of 2
+ for (i = 31; i>0; i--)
+ {
+ if (tablesize & (1<<i))
+ {
+ if (tablesize >= (3<<(i-1)))
+ tablesize = (1<<(i+1));
+ else
+ tablesize = (1<<i);
+ break;
+ }
+ }
+ mMaxEntries = tablesize;
+
+#if !STRING_TABLE_HASH_MAP
+ // ALlocate strings
+ mStringList = new string_list_ptr_t[mMaxEntries];
+ // Clear strings
+ for (i = 0; i < mMaxEntries; i++)
+ {
+ mStringList[i] = NULL;
+ }
+#endif
+}
+
+LLStringTable::~LLStringTable()
+{
+#if !STRING_TABLE_HASH_MAP
+ if (mStringList)
+ {
+ for (S32 i = 0; i < mMaxEntries; i++)
+ {
+ if (mStringList[i])
+ {
+ string_list_t::iterator iter;
+ for (iter = mStringList[i]->begin(); iter != mStringList[i]->end(); iter++)
+ delete *iter; // *iter = (LLStringTableEntry*)
+ }
+ delete mStringList[i];
+ }
+ delete [] mStringList;
+ mStringList = NULL;
+ }
+#else
+ // Need to clean up the string hash
+ for_each(mStringHash.begin(), mStringHash.end(), DeletePairedPointer());
+ mStringHash.clear();
+#endif
+}
+
+
+static U32 hash_my_string(const char *str, int max_entries)
+{
+ U32 retval = 0;
+#if 0
+ while (*str)
+ {
+ retval <<= 1;
+ retval += *str++;
+ }
+#else
+ while (*str)
+ {
+ retval = (retval<<4) + *str;
+ U32 x = (retval & 0xf0000000);
+ if (x) retval = retval ^ (x>>24);
+ retval = retval & (~x);
+ str++;
+ }
+#endif
+ return (retval & (max_entries-1)); // max_entries is gauranteed to be power of 2
+}
+
+char* LLStringTable::checkString(const std::string& str)
+{
+ return checkString(str.c_str());
+}
+
+char* LLStringTable::checkString(const char *str)
+{
+ LLStringTableEntry* entry = checkStringEntry(str);
+ if (entry)
+ {
+ return entry->mString;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+LLStringTableEntry* LLStringTable::checkStringEntry(const std::string& str)
+{
+ return checkStringEntry(str.c_str());
+}
+
+LLStringTableEntry* LLStringTable::checkStringEntry(const char *str)
+{
+ if (str)
+ {
+ char *ret_val;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ return entry;
+ }
+ }
+#else
+ string_list_t *strlist = mStringList[hash_value];
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ return entry;
+ }
+ }
+ }
+#endif
+ }
+ return NULL;
+}
+
+char* LLStringTable::addString(const std::string& str)
+{
+ //RN: safe to use temporary c_str since string is copied
+ return addString(str.c_str());
+}
+
+char* LLStringTable::addString(const char *str)
+{
+
+ LLStringTableEntry* entry = addStringEntry(str);
+ if (entry)
+ {
+ return entry->mString;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+LLStringTableEntry* LLStringTable::addStringEntry(const std::string& str)
+{
+ return addStringEntry(str.c_str());
+}
+
+LLStringTableEntry* LLStringTable::addStringEntry(const char *str)
+{
+ if (str)
+ {
+ char *ret_val = NULL;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ entry->incCount();
+ return entry;
+ }
+ }
+
+ // not found, so add!
+ LLStringTableEntry* newentry = new LLStringTableEntry(str);
+ ret_val = newentry->mString;
+ mStringHash.insert(string_hash_t::value_type(hash_value, newentry));
+#else
+ string_list_t *strlist = mStringList[hash_value];
+
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ entry->incCount();
+ return entry;
+ }
+ }
+ }
+ else
+ {
+ mStringList[hash_value] = new string_list_t;
+ strlist = mStringList[hash_value];
+ }
+
+ // not found, so add!
+ LLStringTableEntry *newentry = new LLStringTableEntry(str);
+ //ret_val = newentry->mString;
+ strlist->push_front(newentry);
+#endif
+ mUniqueEntries++;
+ return newentry;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLStringTable::removeString(const char *str)
+{
+ if (str)
+ {
+ char *ret_val;
+ LLStringTableEntry *entry;
+ U32 hash_value = hash_my_string(str, mMaxEntries);
+#if STRING_TABLE_HASH_MAP
+ {
+#if 1 // Microsoft
+ string_hash_t::iterator lower = mStringHash.lower_bound(hash_value);
+ string_hash_t::iterator upper = mStringHash.upper_bound(hash_value);
+#else // stlport
+ std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value);
+ string_hash_t::iterator lower = P.first;
+ string_hash_t::iterator upper = P.second;
+#endif
+ for (string_hash_t::iterator iter = lower; iter != upper; iter++)
+ {
+ entry = iter->second;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ if (!entry->decCount())
+ {
+ mUniqueEntries--;
+ if (mUniqueEntries < 0)
+ {
+ llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ }
+ delete iter->second;
+ mStringHash.erase(iter);
+ }
+ return;
+ }
+ }
+ }
+#else
+ string_list_t *strlist = mStringList[hash_value];
+
+ if (strlist)
+ {
+ string_list_t::iterator iter;
+ for (iter = strlist->begin(); iter != strlist->end(); iter++)
+ {
+ entry = *iter;
+ ret_val = entry->mString;
+ if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH))
+ {
+ if (!entry->decCount())
+ {
+ mUniqueEntries--;
+ if (mUniqueEntries < 0)
+ {
+ llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ }
+ strlist->remove(entry);
+ delete entry;
+ }
+ return;
+ }
+ }
+ }
+#endif
+ }
+}
+
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h
new file mode 100644
index 0000000000..ad428ce565
--- /dev/null
+++ b/indra/llcommon/llstringtable.h
@@ -0,0 +1,208 @@
+/**
+ * @file llstringtable.h
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STRING_TABLE_H
+#define LL_STRING_TABLE_H
+
+#include "llstl.h"
+#include <list>
+#include <set>
+
+#if LL_WINDOWS
+# if (_MSC_VER >= 1300)
+# define STRING_TABLE_HASH_MAP 1
+# pragma warning(disable : 4996)
+# endif
+#else
+//# define STRING_TABLE_HASH_MAP 1
+#endif
+
+#if STRING_TABLE_HASH_MAP
+#include <hash_map>
+#endif
+
+// string_table.h
+// LLStringTable class header file
+// Provides a _fast_ method for finding unique copies of strings
+//
+// Copyright 2001-2002, Linden Research, Inc.
+
+const U32 MAX_STRINGS_LENGTH = 256;
+
+class LLStringTableEntry
+{
+public:
+ LLStringTableEntry(const char *str)
+ : mString(NULL), mCount(1)
+ {
+ // Copy string
+ U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/
+ length = llmin(length, MAX_STRINGS_LENGTH);
+ mString = new char[length];
+ strncpy(mString, str, length); /*Flawfinder: ignore*/
+ mString[length - 1] = 0;
+ }
+ ~LLStringTableEntry()
+ {
+ delete [] mString;
+ mCount = 0;
+ }
+ void incCount() { mCount++; }
+ BOOL decCount() { return --mCount; }
+
+ char *mString;
+ S32 mCount;
+};
+
+class LLStringTable
+{
+public:
+ LLStringTable(int tablesize);
+ ~LLStringTable();
+
+ char *checkString(const char *str);
+ char *checkString(const std::string& str);
+ LLStringTableEntry *checkStringEntry(const char *str);
+ LLStringTableEntry *checkStringEntry(const std::string& str);
+
+ char *addString(const char *str);
+ char *addString(const std::string& str);
+ LLStringTableEntry *addStringEntry(const char *str);
+ LLStringTableEntry *addStringEntry(const std::string& str);
+ void removeString(const char *str);
+
+ S32 mMaxEntries;
+ S32 mUniqueEntries;
+
+#if STRING_TABLE_HASH_MAP
+ typedef std::hash_multimap<U32, LLStringTableEntry *> string_hash_t;
+ string_hash_t mStringHash;
+#else
+ typedef std::list<LLStringTableEntry *> string_list_t;
+ typedef string_list_t * string_list_ptr_t;
+ string_list_ptr_t *mStringList;
+#endif
+};
+
+extern LLStringTable gStringTable;
+
+//============================================================================
+
+// This class is designed to be used locally,
+// e.g. as a member of an LLXmlTree
+// Strings can be inserted only, then quickly looked up
+
+typedef const std::string* LLStdStringHandle;
+
+class LLStdStringTable
+{
+public:
+ LLStdStringTable(S32 tablesize = 0)
+ {
+ if (tablesize == 0)
+ {
+ tablesize = 256; // default
+ }
+ // Make sure tablesize is power of 2
+ for (S32 i = 31; i>0; i--)
+ {
+ if (tablesize & (1<<i))
+ {
+ if (tablesize >= (3<<(i-1)))
+ tablesize = (1<<(i+1));
+ else
+ tablesize = (1<<i);
+ break;
+ }
+ }
+ mTableSize = tablesize;
+ mStringList = new string_set_t[tablesize];
+ }
+ ~LLStdStringTable()
+ {
+ cleanup();
+ delete[] mStringList;
+ }
+ void cleanup()
+ {
+ // remove strings
+ for (S32 i = 0; i<mTableSize; i++)
+ {
+ string_set_t& stringset = mStringList[i];
+ for (string_set_t::iterator iter = stringset.begin(); iter != stringset.end(); iter++)
+ {
+ delete *iter;
+ }
+ stringset.clear();
+ }
+ }
+
+ LLStdStringHandle lookup(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle checkString(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle insert(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ LLStdStringHandle result = lookup(hashval, s);
+ if (result == NULL)
+ {
+ result = new std::string(s);
+ mStringList[hashval].insert(result);
+ }
+ return result;
+ }
+ LLStdStringHandle addString(const std::string& s)
+ {
+ return insert(s);
+ }
+
+private:
+ U32 makehash(const std::string& s)
+ {
+ S32 len = (S32)s.size();
+ const char* c = s.c_str();
+ U32 hashval = 0;
+ for (S32 i=0; i<len; i++)
+ {
+ hashval = ((hashval<<5) + hashval) + *c++;
+ }
+ return hashval & (mTableSize-1);
+ }
+ LLStdStringHandle lookup(U32 hashval, const std::string& s)
+ {
+ string_set_t& stringset = mStringList[hashval];
+ LLStdStringHandle handle = &s;
+ string_set_t::iterator iter = stringset.find(handle); // compares actual strings
+ if (iter != stringset.end())
+ {
+ return *iter;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+private:
+ S32 mTableSize;
+ typedef std::set<LLStdStringHandle, compare_pointer_contents<std::string> > string_set_t;
+ string_set_t* mStringList; // [mTableSize]
+};
+
+
+#endif
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
new file mode 100644
index 0000000000..65fa4a5c9c
--- /dev/null
+++ b/indra/llcommon/llsys.cpp
@@ -0,0 +1,534 @@
+/**
+ * @file llsys.cpp
+ * @brief Impelementation of the basic system query functions.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <zlib/zlib.h>
+#include "processor.h"
+
+#if LL_DARWIN
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+#elif LL_LINUX
+#include <sys/utsname.h>
+const char MEMINFO_FILE[] = "/proc/meminfo";
+const char CPUINFO_FILE[] = "/proc/cpuinfo";
+#endif
+
+
+static const S32 CPUINFO_BUFFER_SIZE = 16383;
+LLCPUInfo gSysCPU;
+
+LLOSInfo::LLOSInfo() :
+ mMajorVer(0), mMinorVer(0), mBuild(0),
+ mOSString("")
+{
+
+#if LL_WINDOWS
+ OSVERSIONINFOEX osvi;
+ BOOL bOsVersionInfoEx;
+
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
+ {
+ // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
+ osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ if(!GetVersionEx( (OSVERSIONINFO *) &osvi))
+ return;
+ }
+ mMajorVer = osvi.dwMajorVersion;
+ mMinorVer = osvi.dwMinorVersion;
+ mBuild = osvi.dwBuildNumber;
+
+ switch(osvi.dwPlatformId)
+ {
+ case VER_PLATFORM_WIN32_NT:
+ {
+ // Test for the product.
+ if(osvi.dwMajorVersion <= 4)
+ {
+ mOSString = "Microsoft Windows NT ";
+ }
+ else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0)
+ {
+ mOSString = "Microsoft Windows 2000 ";
+ }
+ else if(osvi.dwMajorVersion ==5 && osvi.dwMinorVersion == 1)
+ {
+ mOSString = "Microsoft Windows XP ";
+ }
+ else if(osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2)
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSString = "Microsoft Windows XP x64 Edition ";
+ else mOSString = "Microsoft Windows Server 2003 ";
+ }
+ else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSString = "Microsoft Windows Vista ";
+ else mOSString = "Microsoft Windows Vista Server ";
+ }
+ else // Use the registry on early versions of Windows NT.
+ {
+ HKEY hKey;
+ WCHAR szProductType[80];
+ DWORD dwBufLen;
+ RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\ProductOptions",
+ 0, KEY_QUERY_VALUE, &hKey );
+ RegQueryValueEx( hKey, L"ProductType", NULL, NULL,
+ (LPBYTE) szProductType, &dwBufLen);
+ RegCloseKey( hKey );
+ if ( lstrcmpi( L"WINNT", szProductType) == 0 )
+ {
+ mOSString += "Professional ";
+ }
+ else if ( lstrcmpi( L"LANMANNT", szProductType) == 0 )
+ {
+ mOSString += "Server ";
+ }
+ else if ( lstrcmpi( L"SERVERNT", szProductType) == 0 )
+ {
+ mOSString += "Advanced Server ";
+ }
+ }
+
+ std::string csdversion = utf16str_to_utf8str(osvi.szCSDVersion);
+ // Display version, service pack (if any), and build number.
+ char tmp[MAX_STRING]; /* Flawfinder: ignore */
+ if(osvi.dwMajorVersion <= 4)
+ {
+ snprintf(
+ tmp,
+ sizeof(tmp),
+ "version %d.%d %s (Build %d)",
+ osvi.dwMajorVersion,
+ osvi.dwMinorVersion,
+ csdversion.c_str(),
+ (osvi.dwBuildNumber & 0xffff)); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(
+ tmp,
+ sizeof(tmp),
+ "%s (Build %d)",
+ csdversion.c_str(),
+ (osvi.dwBuildNumber & 0xffff)); /*Flawfinder: ignore*/
+ }
+ mOSString += tmp;
+ }
+ break;
+
+ case VER_PLATFORM_WIN32_WINDOWS:
+ // Test for the Windows 95 product family.
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+ {
+ mOSString = "Microsoft Windows 95 ";
+ if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' )
+ {
+ mOSString += "OSR2 ";
+ }
+ }
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10)
+ {
+ mOSString = "Microsoft Windows 98 ";
+ if ( osvi.szCSDVersion[1] == 'A' )
+ {
+ mOSString += "SE ";
+ }
+ }
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90)
+ {
+ mOSString = "Microsoft Windows Millennium Edition ";
+ }
+ break;
+ }
+#else
+ struct utsname un;
+ if(0==uname(&un))
+ {
+ mOSString.append(un.sysname);
+ mOSString.append(" ");
+ mOSString.append(un.release);
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+ }
+ else
+ {
+ mOSString.append("Unable to collect OS info");
+ }
+#endif
+
+}
+
+#ifndef LL_WINDOWS
+// static
+S32 LLOSInfo::getMaxOpenFiles()
+{
+ const S32 OPEN_MAX_GUESS = 256;
+
+#ifdef OPEN_MAX
+ static S32 open_max = OPEN_MAX;
+#else
+ static S32 open_max = 0;
+#endif
+
+ if (0 == open_max)
+ {
+ // First time through.
+ errno = 0;
+ if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0)
+ {
+ if (0 == errno)
+ {
+ // Indeterminate.
+ open_max = OPEN_MAX_GUESS;
+ }
+ else
+ {
+ llerrs << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << llendl;
+ }
+ }
+ }
+ return open_max;
+}
+#endif
+
+void LLOSInfo::stream(std::ostream& s) const
+{
+ s << mOSString;
+}
+
+const std::string& LLOSInfo::getOSString() const
+{
+ return mOSString;
+}
+
+const S32 STATUS_SIZE = 8192;
+
+//static
+U32 LLOSInfo::getProcessVirtualSizeKB()
+{
+ U32 virtual_size = 0;
+#if LL_WINDOWS
+#endif
+#if LL_LINUX
+ FILE *status_filep = LLFile::fopen("/proc/self/status", "r");
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+ bzero(buff, STATUS_SIZE);
+
+ rewind(status_filep);
+ fread(buff, 1, STATUS_SIZE-2, status_filep);
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmSize:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &virtual_size);
+ }
+ fclose(status_filep);
+#endif
+ return virtual_size;
+}
+
+//static
+U32 LLOSInfo::getProcessResidentSizeKB()
+{
+ U32 resident_size = 0;
+#if LL_WINDOWS
+#endif
+#if LL_LINUX
+ FILE *status_filep = LLFile::fopen("/proc/self/status", "r");
+ if (status_filep != NULL)
+ {
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+ bzero(buff, STATUS_SIZE);
+
+ rewind(status_filep);
+ fread(buff, 1, STATUS_SIZE-2, status_filep);
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmRSS:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &resident_size);
+ }
+ fclose(status_filep);
+ }
+#endif
+ return resident_size;
+}
+
+LLCPUInfo::LLCPUInfo()
+{
+ CProcessor proc;
+ const ProcessorInfo* info = proc.GetCPUInfo();
+ mHasSSE = (info->_Ext.SSE_StreamingSIMD_Extensions != 0);
+ mHasSSE2 = (info->_Ext.SSE2_StreamingSIMD2_Extensions != 0);
+ mCPUMhz = (S32)(proc.GetCPUFrequency(50)/1000000.0);
+ mFamily.assign( info->strFamily );
+}
+
+std::string LLCPUInfo::getCPUString() const
+{
+ std::string cpu_string;
+
+#if LL_WINDOWS || LL_DARWIN
+ // gather machine information.
+ char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */
+ CProcessor proc;
+ if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE))
+ {
+ cpu_string.append(proc_buf);
+ }
+#else
+ cpu_string.append("Can't get CPU information");
+#endif
+
+ return cpu_string;
+}
+
+std::string LLCPUInfo::getCPUStringTerse() const
+{
+ std::string cpu_string;
+
+#if LL_WINDOWS || LL_DARWIN
+ CProcessor proc;
+ const ProcessorInfo *info = proc.GetCPUInfo();
+
+ cpu_string.append(info->strBrandID);
+
+ F64 freq = (F64)(S64)proc.GetCPUFrequency(50) / 1000000.f;
+
+ // cpu speed is often way wrong, do a sanity check
+ if (freq < 10000.f && freq > 200.f )
+ {
+ char tmp[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(tmp, sizeof(tmp), " (%.0f Mhz)", freq); /* Flawfinder: ignore */
+
+ cpu_string.append(tmp);
+ }
+#else
+ cpu_string.append("Can't get terse CPU information");
+#endif
+
+ return cpu_string;
+}
+
+void LLCPUInfo::stream(std::ostream& s) const
+{
+#if LL_WINDOWS || LL_DARWIN
+ // gather machine information.
+ char proc_buf[CPUINFO_BUFFER_SIZE]; /* Flawfinder: ignore */
+ CProcessor proc;
+ if(proc.CPUInfoToText(proc_buf, CPUINFO_BUFFER_SIZE))
+ {
+ s << proc_buf;
+ }
+ else
+ {
+ s << "Unable to collect processor info";
+ }
+#else
+ // *FIX: This works on linux. What will it do on other systems?
+ FILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "r"); /* Flawfinder: ignore */
+ if(cpuinfo)
+ {
+ char line[MAX_STRING]; /* Flawfinder: ignore */
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, cpuinfo))
+ {
+ line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
+ s << line;
+ }
+ fclose(cpuinfo);
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+#endif
+}
+
+LLMemoryInfo::LLMemoryInfo()
+{
+}
+
+#if LL_LINUX
+#include <unistd.h>
+#include <sys/sysinfo.h>
+#endif
+
+U32 LLMemoryInfo::getPhysicalMemory() const
+{
+#if LL_WINDOWS
+ MEMORYSTATUS state;
+ state.dwLength = sizeof(state);
+ GlobalMemoryStatus(&state);
+
+ return (U32)state.dwTotalPhys;
+
+#elif LL_DARWIN
+ // This might work on Linux as well. Someone check...
+ unsigned int phys = 0;
+ int mib[2] = { CTL_HW, HW_PHYSMEM };
+
+ size_t len = sizeof(phys);
+ sysctl(mib, 2, &phys, &len, NULL, 0);
+
+ return phys;
+#elif LL_LINUX
+
+ return getpagesize() * get_phys_pages();
+
+#else
+ return 0;
+
+#endif
+}
+
+void LLMemoryInfo::stream(std::ostream& s) const
+{
+#if LL_WINDOWS
+ MEMORYSTATUS state;
+ state.dwLength = sizeof(state);
+ GlobalMemoryStatus(&state);
+
+ s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl;
+ s << "Total Physical Kb: " << (U32)state.dwTotalPhys/1024 << std::endl;
+ s << "Avail Physical Kb: " << (U32)state.dwAvailPhys/1024 << std::endl;
+ s << "Total page Kb: " << (U32)state.dwTotalPageFile/1024 << std::endl;
+ s << "Avail page Kb: " << (U32)state.dwAvailPageFile/1024 << std::endl;
+ s << "Total Virtual Kb: " << (U32)state.dwTotalVirtual/1024 << std::endl;
+ s << "Avail Virtual Kb: " << (U32)state.dwAvailVirtual/1024 << std::endl;
+#elif LL_DARWIN
+ U64 phys = 0;
+
+ size_t len = sizeof(phys);
+
+ if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
+ {
+ s << "Total Physical Kb: " << phys/1024 << std::endl;
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+
+#else
+ // *FIX: This works on linux. What will it do on other systems?
+ FILE* meminfo = LLFile::fopen(MEMINFO_FILE,"r"); /* Flawfinder: ignore */
+ if(meminfo)
+ {
+ char line[MAX_STRING]; /* Flawfinder: ignore */
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, meminfo))
+ {
+ line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
+ s << line;
+ }
+ fclose(meminfo);
+ }
+ else
+ {
+ s << "Unable to collect memory information";
+ }
+#endif
+}
+
+std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+BOOL gunzip_file(const char *srcfile, const char *dstfile)
+{
+ char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */
+ const S32 UNCOMPRESS_BUFFER_SIZE = 32768;
+ BOOL retval = FALSE;
+ gzFile src = NULL;
+ U8 buffer[UNCOMPRESS_BUFFER_SIZE];
+ FILE *dst = NULL;
+ S32 bytes = 0;
+ (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */
+ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */
+ src = gzopen(srcfile, "rb");
+ if (! src) goto err;
+ dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ if (! dst) goto err;
+ do
+ {
+ bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE);
+ fwrite(buffer, sizeof(U8), bytes, dst);
+ } while(gzeof(src) == 0);
+ fclose(dst);
+ dst = NULL;
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = TRUE;
+err:
+ if (src != NULL) gzclose(src);
+ if (dst != NULL) fclose(dst);
+ return retval;
+}
+
+BOOL gzip_file(const char *srcfile, const char *dstfile)
+{
+ const S32 COMPRESS_BUFFER_SIZE = 32768;
+ char tmpfile[LL_MAX_PATH]; /* Flawfinder: ignore */
+ BOOL retval = FALSE;
+ U8 buffer[COMPRESS_BUFFER_SIZE];
+ gzFile dst = NULL;
+ FILE *src = NULL;
+ S32 bytes = 0;
+ (void *) strcpy(tmpfile, dstfile); /* Flawfinder: ignore */
+ (void *) strncat(tmpfile, ".t", sizeof(tmpfile) - strlen(tmpfile) -1); /* Flawfinder: ignore */
+ dst = gzopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ if (! dst) goto err;
+ src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
+ if (! src) goto err;
+
+ do
+ {
+ bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE,src);
+ gzwrite(dst, buffer, bytes);
+ } while(feof(src) == 0);
+ gzclose(dst);
+ dst = NULL;
+#if LL_WINDOWS
+ // Rename in windows needs the dstfile to not exist.
+ LLFile::remove(dstfile);
+#endif
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = TRUE;
+ err:
+ if (src != NULL) fclose(src);
+ if (dst != NULL) gzclose(dst);
+ return retval;
+}
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
new file mode 100644
index 0000000000..05c975a5fa
--- /dev/null
+++ b/indra/llcommon/llsys.h
@@ -0,0 +1,91 @@
+/**
+ * @file llsys.h
+ * @brief System information debugging classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SYS_H
+#define LL_SYS_H
+
+//
+// The LLOSInfo, LLCPUInfo, and LLMemoryInfo classes are essentially
+// the same, but query different machine subsystems. Here's how you
+// use an LLCPUInfo object:
+//
+// LLCPUInfo info;
+// llinfos << info << llendl;
+//
+
+#include <iosfwd>
+#include <string>
+
+class LLOSInfo
+{
+public:
+ LLOSInfo();
+ void stream(std::ostream& s) const;
+
+ const std::string& getOSString() const;
+
+ S32 mMajorVer;
+ S32 mMinorVer;
+ S32 mBuild;
+
+#ifndef LL_WINDOWS
+ static S32 getMaxOpenFiles();
+#endif
+
+ static U32 getProcessVirtualSizeKB();
+ static U32 getProcessResidentSizeKB();
+private:
+ std::string mOSString;
+};
+
+
+class LLCPUInfo
+{
+public:
+ LLCPUInfo();
+ void stream(std::ostream& s) const;
+
+ std::string getCPUString() const;
+ std::string getCPUStringTerse() const;
+
+ BOOL hasSSE() const { return mHasSSE; }
+ BOOL hasSSE2() const { return mHasSSE2; }
+ S32 getMhz() const { return mCPUMhz; }
+
+ // Family is "AMD Duron" or "Intel Pentium Pro"
+ const std::string& getFamily() const { return mFamily; }
+
+private:
+ BOOL mHasSSE;
+ BOOL mHasSSE2;
+ S32 mCPUMhz;
+ std::string mFamily;
+};
+
+class LLMemoryInfo
+{
+public:
+ LLMemoryInfo();
+ void stream(std::ostream& s) const;
+
+ U32 getPhysicalMemory() const;
+};
+
+
+std::ostream& operator<<(std::ostream& s, const LLOSInfo& info);
+std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info);
+std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info);
+
+// gunzip srcfile into dstfile. Returns FALSE on error.
+BOOL gunzip_file(const char *srcfile, const char *dstfile);
+// gzip srcfile into dstfile. Returns FALSE on error.
+BOOL gzip_file(const char *srcfile, const char *dstfile);
+
+extern LLCPUInfo gSysCPU;
+
+#endif // LL_LLSYS_H
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
new file mode 100644
index 0000000000..bd2dd7c8f5
--- /dev/null
+++ b/indra/llcommon/llthread.cpp
@@ -0,0 +1,330 @@
+/**
+ * @file llthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+#include "llthread.h"
+
+#include "lltimer.h"
+
+#if LL_LINUX
+#include <sched.h>
+#endif
+
+//----------------------------------------------------------------------------
+// Usage:
+// void run_func(LLThread* thread)
+// {
+// }
+// LLThread* thread = new LLThread();
+// thread->run(run_func);
+// ...
+// thread->setQuitting();
+// while(!timeout)
+// {
+// if (thread->isStopped())
+// {
+// delete thread;
+// break;
+// }
+// }
+//
+//----------------------------------------------------------------------------
+
+//
+// Handed to the APR thread creation function
+//
+void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap)
+{
+ LLThread *threadp = (LLThread *)datap;
+
+ // Set thread state to running
+ threadp->mStatus = RUNNING;
+
+ // Run the user supplied function
+ threadp->run();
+
+ llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl;
+
+ // We're done with the run function, this thread is done executing now.
+ threadp->mStatus = STOPPED;
+
+ return NULL;
+}
+
+
+LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
+ mPaused(FALSE),
+ mName(name),
+ mAPRThreadp(NULL),
+ mStatus(STOPPED)
+{
+ // Thread creation probably CAN be paranoid about APR being initialized, if necessary
+ if (poolp)
+ {
+ mIsLocalPool = FALSE;
+ mAPRPoolp = poolp;
+ }
+ else
+ {
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+ mRunCondition = new LLCondition(mAPRPoolp);
+}
+
+
+LLThread::~LLThread()
+{
+ // Warning! If you somehow call the thread destructor from itself,
+ // the thread will die in an unclean fashion!
+ if (mAPRThreadp)
+ {
+ if (!isStopped())
+ {
+ // The thread isn't already stopped
+ // First, set the flag that indicates that we're ready to die
+ setQuitting();
+
+ llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
+ // Now wait a bit for the thread to exit
+ // It's unclear whether I should even bother doing this - this destructor
+ // should netver get called unless we're already stopped, really...
+ S32 counter = 0;
+ const S32 MAX_WAIT = 600;
+ while (counter < MAX_WAIT)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ // Sleep for a tenth of a second
+ ms_sleep(100);
+ yield();
+ counter++;
+ }
+ }
+
+ if (!isStopped())
+ {
+ // This thread just wouldn't stop, even though we gave it time
+ llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+ return;
+ }
+ mAPRThreadp = NULL;
+ }
+
+ delete mRunCondition;
+
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
+}
+
+
+void LLThread::start()
+{
+ apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+
+ // We won't bother joining
+ apr_thread_detach(mAPRThreadp);
+}
+
+//============================================================================
+// Called from MAIN THREAD.
+
+// Request that the thread pause/resume.
+// The thread will pause when (and if) it calls checkPause()
+void LLThread::pause()
+{
+ if (!mPaused)
+ {
+ // this will cause the thread to stop execution as soon as checkPause() is called
+ mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread
+ }
+}
+
+void LLThread::unpause()
+{
+ if (mPaused)
+ {
+ mPaused = 0;
+ }
+
+ wake(); // wake up the thread if necessary
+}
+
+// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+bool LLThread::runCondition(void)
+{
+ // by default, always run. Handling of pause/unpause is done regardless of this function's result.
+ return true;
+}
+
+//============================================================================
+// Called from run() (CHILD THREAD).
+// Stop thread execution if requested until unpaused.
+void LLThread::checkPause()
+{
+ mRunCondition->lock();
+
+ // This is in a while loop because the pthread API allows for spurious wakeups.
+ while(shouldSleep())
+ {
+ mRunCondition->wait(); // unlocks mRunCondition
+ // mRunCondition is locked when the thread wakes up
+ }
+
+ mRunCondition->unlock();
+}
+
+//============================================================================
+
+bool LLThread::isQuitting() const
+{
+ return (QUITTING == mStatus);
+}
+
+
+bool LLThread::isStopped() const
+{
+ return (STOPPED == mStatus);
+}
+
+
+void LLThread::setQuitting()
+{
+ mRunCondition->lock();
+ if (mStatus == RUNNING)
+ {
+ mStatus = QUITTING;
+ }
+ mRunCondition->unlock();
+ wake();
+}
+
+
+// static
+void LLThread::yield()
+{
+#if LL_LINUX
+ sched_yield(); // annoyingly, apr_thread_yield is a noop on linux...
+#else
+ apr_thread_yield();
+#endif
+}
+
+void LLThread::wake()
+{
+ mRunCondition->lock();
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+ mRunCondition->unlock();
+}
+
+void LLThread::wakeLocked()
+{
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+}
+
+//============================================================================
+
+LLMutex::LLMutex(apr_pool_t *poolp) :
+ mAPRMutexp(NULL)
+{
+ if (poolp)
+ {
+ mIsLocalPool = FALSE;
+ mAPRPoolp = poolp;
+ }
+ else
+ {
+ mIsLocalPool = TRUE;
+ apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
+ }
+ apr_thread_mutex_create(&mAPRMutexp, APR_THREAD_MUTEX_DEFAULT, mAPRPoolp);
+}
+
+
+LLMutex::~LLMutex()
+{
+#if _DEBUG
+ llassert(!isLocked()); // better not be locked!
+#endif
+ apr_thread_mutex_destroy(mAPRMutexp);
+ mAPRMutexp = NULL;
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
+}
+
+
+void LLMutex::lock()
+{
+ apr_thread_mutex_lock(mAPRMutexp);
+}
+
+void LLMutex::unlock()
+{
+ apr_thread_mutex_unlock(mAPRMutexp);
+}
+
+bool LLMutex::isLocked()
+{
+ apr_status_t status = apr_thread_mutex_trylock(mAPRMutexp);
+ if (APR_STATUS_IS_EBUSY(status))
+ {
+ return true;
+ }
+ else
+ {
+ apr_thread_mutex_unlock(mAPRMutexp);
+ return false;
+ }
+}
+
+//============================================================================
+
+LLCondition::LLCondition(apr_pool_t *poolp) :
+ LLMutex(poolp)
+{
+ // base class (LLMutex) has already ensured that mAPRPoolp is set up.
+
+ apr_thread_cond_create(&mAPRCondp, mAPRPoolp);
+}
+
+
+LLCondition::~LLCondition()
+{
+ apr_thread_cond_destroy(mAPRCondp);
+ mAPRCondp = NULL;
+}
+
+
+void LLCondition::wait()
+{
+ apr_thread_cond_wait(mAPRCondp, mAPRMutexp);
+}
+
+void LLCondition::signal()
+{
+ apr_thread_cond_signal(mAPRCondp);
+}
+
+void LLCondition::broadcast()
+{
+ apr_thread_cond_broadcast(mAPRCondp);
+}
+
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
new file mode 100644
index 0000000000..f6f6bd210a
--- /dev/null
+++ b/indra/llcommon/llthread.h
@@ -0,0 +1,164 @@
+/**
+ * @file llthread.h
+ * @brief Base classes for thread, mutex and condition handling.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTHREAD_H
+#define LL_LLTHREAD_H
+
+#include "llapr.h"
+#include "llapp.h"
+
+#include "apr-1/apr_thread_cond.h"
+
+class LLThread;
+class LLMutex;
+class LLCondition;
+
+class LLThread
+{
+public:
+ typedef enum e_thread_status
+ {
+ STOPPED = 0, // The thread is not running. Not started, or has exited its run function
+ RUNNING = 1, // The thread is currently running
+ QUITTING= 2 // Someone wants this thread to quit
+ } EThreadStatus;
+
+ LLThread(const std::string& name, apr_pool_t *poolp = NULL);
+ virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state.
+
+ static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure.
+
+
+ bool isQuitting() const;
+ bool isStopped() const;
+
+ // PAUSE / RESUME functionality. See source code for important usage notes.
+public:
+ // Called from MAIN THREAD.
+ void pause();
+ void unpause();
+ bool isPaused() { return mPaused ? true : false; }
+
+ // Cause the thread to wake up and check its condition
+ void wake();
+
+ // Same as above, but to be used when the condition is already locked.
+ void wakeLocked();
+
+ // Called from run() (CHILD THREAD). Pause the thread if requested until unpaused.
+ void checkPause();
+
+ // this kicks off the apr thread
+ void start(void);
+
+ apr_pool_t *getAPRPool() { return mAPRPoolp; }
+
+private:
+ BOOL mPaused;
+
+ // static function passed to APR thread creation routine
+ static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
+
+protected:
+ std::string mName;
+ LLCondition* mRunCondition;
+
+ apr_thread_t *mAPRThreadp;
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+ EThreadStatus mStatus;
+
+ void setQuitting();
+
+ // virtual function overridden by subclass -- this will be called when the thread runs
+ virtual void run(void) = 0;
+
+ // virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+ virtual bool runCondition(void);
+
+ // Lock/Unlock Run Condition -- use around modification of any variable used in runCondition()
+ inline void lockData();
+ inline void unlockData();
+
+ // This is the predicate that decides whether the thread should sleep.
+ // It should only be called with mRunCondition locked, since the virtual runCondition() function may need to access
+ // data structures that are thread-unsafe.
+ bool shouldSleep(void) { return (mStatus == RUNNING) && (isPaused() || (!runCondition())); }
+
+ // To avoid spurious signals (and the associated context switches) when the condition may or may not have changed, you can do the following:
+ // mRunCondition->lock();
+ // if(!shouldSleep())
+ // mRunCondition->signal();
+ // mRunCondition->unlock();
+};
+
+//============================================================================
+
+class LLMutex
+{
+public:
+ LLMutex(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+ ~LLMutex();
+
+ void lock(); // blocks
+ void unlock();
+ bool isLocked(); // non-blocking, but does do a lock/unlock so not free
+
+protected:
+ apr_thread_mutex_t *mAPRMutexp;
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+};
+
+// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
+class LLCondition : public LLMutex
+{
+public:
+ LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
+ ~LLCondition();
+
+ void wait(); // blocks
+ void signal();
+ void broadcast();
+
+protected:
+ apr_thread_cond_t *mAPRCondp;
+};
+
+class LLMutexLock
+{
+public:
+ LLMutexLock(LLMutex* mutex)
+ {
+ mMutex = mutex;
+ mMutex->lock();
+ }
+ ~LLMutexLock()
+ {
+ mMutex->unlock();
+ }
+private:
+ LLMutex* mMutex;
+};
+
+//============================================================================
+
+void LLThread::lockData()
+{
+ mRunCondition->lock();
+}
+
+void LLThread::unlockData()
+{
+ mRunCondition->unlock();
+}
+
+
+//============================================================================
+
+#endif // LL_LLTHREAD_H
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
new file mode 100644
index 0000000000..bd054f02d8
--- /dev/null
+++ b/indra/llcommon/lltimer.cpp
@@ -0,0 +1,517 @@
+/**
+ * @file lltimer.cpp
+ * @brief Cross-platform objects for doing timing
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+
+#if LL_WINDOWS
+#include <time.h>
+
+#elif LL_LINUX
+#include <time.h>
+#include <sys/time.h>
+#include <sched.h>
+
+#elif LL_DARWIN
+# include <time.h>
+# include <sys/time.h>
+#else
+# error "architecture not supported"
+#endif
+
+
+#include "lltimer.h"
+#include "u64.h"
+
+//
+// Locally used constants
+//
+const U32 SEC_PER_DAY = 86400;
+const F64 SEC_TO_MICROSEC = 1000000.f;
+const U64 SEC_TO_MICROSEC_U64 = 1000000;
+const F64 USEC_TO_SEC_F64 = 0.000001;
+
+
+//---------------------------------------------------------------------------
+// Globals and statics
+//---------------------------------------------------------------------------
+
+S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
+LLTimer* LLTimer::sTimer = NULL;
+
+F64 gClockFrequency = 0.0;
+F64 gClockFrequencyInv = 0.0;
+F64 gClocksToMicroseconds = 0.0;
+U64 gTotalTimeClockCount = 0;
+U64 gLastTotalTimeClockCount = 0;
+
+//
+// Forward declarations
+//
+
+
+//---------------------------------------------------------------------------
+// Implementation
+//---------------------------------------------------------------------------
+
+#if LL_WINDOWS
+void ms_sleep(long ms)
+{
+ Sleep((U32)ms);
+}
+
+void llyield()
+{
+ SleepEx(0, TRUE); // Relinquishes time slice to any thread of equal priority, can be woken up by extended IO functions
+}
+#elif LL_LINUX
+void ms_sleep(long ms)
+{
+ struct timespec t;
+ t.tv_sec = ms / 1000;
+ t.tv_nsec = (ms % 1000) * 1000000l;
+ nanosleep(&t, NULL);
+}
+
+void llyield()
+{
+ sched_yield();
+}
+#elif LL_DARWIN
+void ms_sleep(long ms)
+{
+ struct timespec t;
+ t.tv_sec = ms / 1000;
+ t.tv_nsec = (ms % 1000) * 1000000l;
+ nanosleep(&t, NULL);
+}
+
+void llyield()
+{
+// sched_yield();
+}
+#else
+# error "architecture not supported"
+#endif
+
+//
+// CPU clock/other clock frequency and count functions
+//
+
+#if LL_WINDOWS
+U64 get_clock_count()
+{
+ static bool firstTime = true;
+ static U64 offset;
+ // ensures that callers to this function never have to deal with wrap
+
+ // QueryPerformanceCounter implementation
+ LARGE_INTEGER clock_count;
+ QueryPerformanceCounter(&clock_count);
+ if (firstTime) {
+ offset = clock_count.QuadPart;
+ firstTime = false;
+ }
+ return clock_count.QuadPart - offset;
+}
+
+F64 calc_clock_frequency(U32 uiMeasureMSecs)
+{
+ __int64 freq;
+ QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
+ return (F64)freq;
+}
+#endif // LL_WINDOWS
+
+
+#if LL_LINUX || LL_DARWIN
+// Both Linux and Mac use gettimeofday for accurate time
+F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
+{
+ return 1000000.0; // microseconds, so 1 Mhz.
+}
+
+U64 get_clock_count()
+{
+ // Linux clocks are in microseconds
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
+}
+#endif
+
+
+void update_clock_frequencies()
+{
+ gClockFrequency = calc_clock_frequency(50);
+ gClockFrequencyInv = 1.0/gClockFrequency;
+ gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+// returns a U64 number that represents the number of
+// microseconds since the unix epoch - Jan 1, 1970
+U64 totalTime()
+{
+ U64 current_clock_count = get_clock_count();
+ if (!gTotalTimeClockCount)
+ {
+ update_clock_frequencies();
+ gTotalTimeClockCount = current_clock_count;
+
+#if LL_WINDOWS
+ // Synch us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
+ // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
+ // make in the future.
+
+ gTotalTimeClockCount = (U64)(time(NULL) * gClockFrequency);
+#endif
+
+ // Update the last clock count
+ gLastTotalTimeClockCount = current_clock_count;
+ }
+ else
+ {
+ if (current_clock_count >= gLastTotalTimeClockCount)
+ {
+ // No wrapping, we're all okay.
+ gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
+ }
+ else
+ {
+ // We've wrapped. Compensate correctly
+ gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
+ }
+
+ // Update the last clock count
+ gLastTotalTimeClockCount = current_clock_count;
+ }
+
+ // Return the total clock tick count in microseconds.
+ return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+LLTimer::LLTimer()
+{
+ if (!gClockFrequency)
+ {
+ update_clock_frequencies();
+ }
+
+ mStarted = TRUE;
+ reset();
+}
+
+LLTimer::~LLTimer()
+{
+}
+
+// static
+U64 LLTimer::getTotalTime()
+{
+ // simply call into the implementation function.
+ return totalTime();
+}
+
+// static
+F64 LLTimer::getTotalSeconds()
+{
+ return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
+}
+
+void LLTimer::reset()
+{
+ mLastClockCount = get_clock_count();
+ mExpirationTicks = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+U64 LLTimer::getCurrentClockCount()
+{
+ return get_clock_count();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setLastClockCount(U64 current_count)
+{
+ mLastClockCount = current_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static
+U64 getElapsedTimeAndUpdate(U64& lastClockCount)
+{
+ U64 current_clock_count = get_clock_count();
+ U64 result;
+
+ if (current_clock_count >= lastClockCount)
+ {
+ result = current_clock_count - lastClockCount;
+ }
+ else
+ {
+ // time has gone backward
+ result = 0;
+ }
+
+ lastClockCount = current_clock_count;
+
+ return result;
+}
+
+
+F64 LLTimer::getElapsedTimeF64() const
+{
+ U64 last = mLastClockCount;
+ return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
+}
+
+F32 LLTimer::getElapsedTimeF32() const
+{
+ return (F32)getElapsedTimeF64();
+}
+
+F64 LLTimer::getElapsedTimeAndResetF64()
+{
+ return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
+}
+
+F32 LLTimer::getElapsedTimeAndResetF32()
+{
+ return (F32)getElapsedTimeAndResetF64();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setTimerExpirySec(F32 expiration)
+{
+ mExpirationTicks = get_clock_count()
+ + (U64)((F32)(expiration * gClockFrequency));
+}
+
+F32 LLTimer::getRemainingTimeF32()
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks > mExpirationTicks)
+ {
+ return 0.0f;
+ }
+ return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
+}
+
+
+BOOL LLTimer::checkExpirationAndReset(F32 expiration)
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks < mExpirationTicks)
+ {
+ return FALSE;
+ }
+
+ mExpirationTicks = cur_ticks
+ + (U64)((F32)(expiration * gClockFrequency));
+ return TRUE;
+}
+
+
+BOOL LLTimer::hasExpired()
+{
+ return (get_clock_count() >= mExpirationTicks)
+ ? TRUE : FALSE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+BOOL LLTimer::knownBadTimer()
+{
+ BOOL failed = FALSE;
+
+#if LL_WINDOWS
+ WCHAR bad_pci_list[][10] = {L"1039:0530",
+ L"1039:0620",
+ L"10B9:0533",
+ L"10B9:1533",
+ L"1106:0596",
+ L"1106:0686",
+ L"1166:004F",
+ L"1166:0050",
+ L"8086:7110",
+ L"\0"
+ };
+
+ HKEY hKey = NULL;
+ LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
+ KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
+
+ WCHAR name[1024];
+ DWORD name_len = 1024;
+ FILETIME scrap;
+
+ S32 key_num = 0;
+ WCHAR pci_id[10];
+
+ wcscpy(pci_id, L"0000:0000"); /*Flawfinder: ignore*/
+
+ while (nResult == ERROR_SUCCESS)
+ {
+ nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
+
+ if (nResult == ERROR_SUCCESS)
+ {
+ memcpy(&pci_id[0],&name[4],4); /* Flawfinder: ignore */
+ memcpy(&pci_id[5],&name[13],4); /* Flawfinder: ignore */
+
+ for (S32 check = 0; bad_pci_list[check][0]; check++)
+ {
+ if (!wcscmp(pci_id, bad_pci_list[check]))
+ {
+// llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
+ failed = TRUE;
+ break;
+ }
+ }
+// llinfo << "PCI chipset found: " << pci_id << endl;
+ name_len = 1024;
+ }
+ }
+#endif
+ return(failed);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// NON-MEMBER FUNCTIONS
+//
+///////////////////////////////////////////////////////////////////////////////
+
+U32 time_corrected()
+{
+ U32 corrected_time = (U32)time(NULL) + gUTCOffset;
+ return corrected_time;
+}
+
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+BOOL is_daylight_savings()
+{
+ time_t now = time(NULL);
+
+ // Internal buffer to local server time
+ struct tm* internal_time = localtime(&now);
+
+ // tm_isdst > 0 => daylight savings
+ // tm_isdst = 0 => not daylight savings
+ // tm_isdst < 0 => can't tell
+ return (internal_time->tm_isdst > 0);
+}
+
+
+struct tm* utc_to_pacific_time(S32 utc_time, BOOL pacific_daylight_time)
+{
+ time_t unix_time = (time_t)utc_time;
+
+ S32 pacific_offset_hours;
+ if (pacific_daylight_time)
+ {
+ pacific_offset_hours = -7;
+ }
+ else
+ {
+ pacific_offset_hours = -8;
+ }
+
+ // We subtract off the PST/PDT offset _before_ getting
+ // "UTC" time, because this will handle wrapping around
+ // for 5 AM UTC -> 10 PM PDT of the previous day.
+ unix_time += pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
+
+ // Internal buffer to PST/PDT (see above)
+ struct tm* internal_time = gmtime(&unix_time);
+
+ /*
+ // Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
+ if (pacific_daylight_time)
+ {
+ internal_time->tm_isdst = 1;
+ }
+ */
+
+ return internal_time;
+}
+
+
+void microsecondsToTimecodeString(U64 current_time, char *tcstring)
+{
+ U64 hours;
+ U64 minutes;
+ U64 seconds;
+ U64 frames;
+ U64 subframes;
+
+ hours = current_time / (U64)3600000000ul;
+ minutes = current_time / (U64)60000000;
+ minutes %= 60;
+ seconds = current_time / (U64)1000000;
+ seconds %= 60;
+ frames = current_time / (U64)41667;
+ frames %= 24;
+ subframes = current_time / (U64)42;
+ subframes %= 100;
+
+ sprintf(tcstring,"%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes); /* Flawfinder: ignore */
+}
+
+
+void secondsToTimecodeString(F32 current_time, char *tcstring)
+{
+ microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// LLEventTimer Implementation
+//
+//////////////////////////////////////////////////////////////////////////////
+
+std::list<LLEventTimer*> LLEventTimer::sActiveList;
+
+LLEventTimer::LLEventTimer(F32 period)
+: mTimer()
+{
+ mPeriod = period;
+ sActiveList.push_back(this);
+}
+
+LLEventTimer::~LLEventTimer()
+{
+ sActiveList.remove(this);
+}
+
+void LLEventTimer::updateClass()
+{
+ for (std::list<LLEventTimer*>::iterator iter = sActiveList.begin(); iter != sActiveList.end(); )
+ {
+ LLEventTimer* timer = *iter++;
+ F32 et = timer->mTimer.getElapsedTimeF32();
+ if (et > timer->mPeriod) {
+ timer->mTimer.reset();
+ timer->tick();
+ }
+ }
+}
+
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
new file mode 100644
index 0000000000..69e786c50a
--- /dev/null
+++ b/indra/llcommon/lltimer.h
@@ -0,0 +1,142 @@
+/**
+ * @file lltimer.h
+ * @brief Cross-platform objects for doing timing
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TIMER_H
+#define LL_TIMER_H
+
+#if LL_LINUX || LL_DARWIN
+# include <time.h>
+# include <sys/time.h>
+#endif
+
+#include <list>
+
+// units conversions
+#ifndef USEC_PER_SEC
+ const U32 USEC_PER_SEC = 1000000;
+#endif
+const U32 SEC_PER_MIN = 60;
+const U32 MIN_PER_HOUR = 60;
+const U32 USEC_PER_MIN = USEC_PER_SEC * SEC_PER_MIN;
+const U32 USEC_PER_HOUR = USEC_PER_MIN * MIN_PER_HOUR;
+const U32 SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR;
+const F64 SEC_PER_USEC = 1.0 / (F64) USEC_PER_SEC;
+
+class LLTimer
+{
+public:
+ static LLTimer *sTimer; // global timer
+
+protected:
+ U64 mLastClockCount;
+ U64 mExpirationTicks;
+ BOOL mStarted;
+
+public:
+ LLTimer();
+ ~LLTimer();
+
+ static void initClass() { if (!sTimer) sTimer = new LLTimer; }
+ static void cleanupClass() { delete sTimer; sTimer = NULL; }
+
+ // Return a high precision number of seconds since the start of
+ // this application instance.
+ static F64 getElapsedSeconds()
+ {
+ return sTimer->getElapsedTimeF64();
+ }
+
+ // Return a high precision usec since epoch
+ static U64 getTotalTime();
+
+ // Return a high precision seconds since epoch
+ static F64 getTotalSeconds();
+
+
+ // MANIPULATORS
+ void start() { reset(); mStarted = TRUE; }
+ void stop() { mStarted = FALSE; }
+ void reset(); // Resets the timer
+ void setLastClockCount(U64 current_count); // Sets the timer so that the next elapsed call will be relative to this time
+ void setTimerExpirySec(F32 expiration);
+ BOOL checkExpirationAndReset(F32 expiration);
+ BOOL hasExpired();
+ F32 getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
+ F64 getElapsedTimeAndResetF64();
+
+ F32 getRemainingTimeF32();
+
+ static BOOL knownBadTimer();
+
+ // ACCESSORS
+ F32 getElapsedTimeF32() const; // Returns elapsed time in seconds
+ F64 getElapsedTimeF64() const; // Returns elapsed time in seconds
+
+ BOOL getStarted() const { return mStarted; }
+
+
+ static U64 getCurrentClockCount(); // Returns the raw clockticks
+};
+
+//
+// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
+//
+U64 get_clock_count();
+F64 calc_clock_frequency(U32 msecs);
+void update_clock_frequencies();
+
+
+// Sleep for milliseconds
+void ms_sleep(long ms);
+
+// Yield
+//void llyield(); // Yield your timeslice - not implemented yet for Mac, so commented out.
+
+// Returns the correct UTC time in seconds, like time(NULL).
+// Useful on the viewer, which may have its local clock set wrong.
+U32 time_corrected();
+
+// Correction factor used by time_corrected() above.
+extern S32 gUTCOffset;
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+BOOL is_daylight_savings();
+
+// Converts internal "struct tm" time buffer to Pacific Standard/Daylight Time
+// Usage:
+// S32 utc_time;
+// utc_time = time_corrected();
+// struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight);
+struct tm* utc_to_pacific_time(S32 utc_time, BOOL pacific_daylight_time);
+
+void microsecondsToTimecodeString(U64 current_time, char *tcstring);
+void secondsToTimecodeString(F32 current_time, char *tcstring);
+
+// class for scheduling a function to be called at a given frequency (approximate, inprecise)
+class LLEventTimer
+{
+public:
+ LLEventTimer(F32 period); // period is the amount of time between each call to tick()
+ virtual ~LLEventTimer();
+
+ //function to be called at the supplied frequency
+ virtual void tick() = 0;
+
+ static void updateClass();
+
+protected:
+ LLTimer mTimer;
+ F32 mPeriod;
+
+private:
+ //list of active timers
+ static std::list<LLEventTimer*> sActiveList;
+};
+
+#endif
diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
new file mode 100644
index 0000000000..1b66173399
--- /dev/null
+++ b/indra/llcommon/lluri.cpp
@@ -0,0 +1,579 @@
+/**
+ * @file lluri.cpp
+ * @author Phoenix
+ * @date 2006-02-08
+ * @brief Implementation of the LLURI class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lluri.h"
+#include "llsd.h"
+
+// uric = reserved | unreserved | escaped
+// reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
+// unreserved = alphanum | mark
+// mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
+// escaped = "%" hex hex
+static const char* ESCAPED_CHARACTERS[256] =
+{
+ "%00", // 0
+ "%01", // 1
+ "%02", // 2
+ "%03", // 3
+ "%04", // 4
+ "%05", // 5
+ "%06", // 6
+ "%07", // 7
+ "%08", // 8
+ "%09", // 9
+ "%0a", // 10
+ "%0b", // 11
+ "%0c", // 12
+ "%0d", // 13
+ "%0e", // 14
+ "%0f", // 15
+ "%10", // 16
+ "%11", // 17
+ "%12", // 18
+ "%13", // 19
+ "%14", // 20
+ "%15", // 21
+ "%16", // 22
+ "%17", // 23
+ "%18", // 24
+ "%19", // 25
+ "%1a", // 26
+ "%1b", // 27
+ "%1c", // 28
+ "%1d", // 29
+ "%1e", // 30
+ "%1f", // 31
+ "%20", // 32
+ "!", // 33
+ "%22", // 34
+ "%23", // 35
+ "$", // 36
+ "%25", // 37
+ "&", // 38
+ "'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "%3c", // 60
+ "=", // 61
+ "%3e", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "%5b", // 91
+ "%5c", // 92
+ "%5d", // 93
+ "%5e", // 94
+ "_", // 95
+ "%60", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "%7b", // 123
+ "%7c", // 124
+ "%7d", // 125
+ "~", // 126
+ "%7f", // 127
+ "%80", // 128
+ "%81", // 129
+ "%82", // 130
+ "%83", // 131
+ "%84", // 132
+ "%85", // 133
+ "%86", // 134
+ "%87", // 135
+ "%88", // 136
+ "%89", // 137
+ "%8a", // 138
+ "%8b", // 139
+ "%8c", // 140
+ "%8d", // 141
+ "%8e", // 142
+ "%8f", // 143
+ "%90", // 144
+ "%91", // 145
+ "%92", // 146
+ "%93", // 147
+ "%94", // 148
+ "%95", // 149
+ "%96", // 150
+ "%97", // 151
+ "%98", // 152
+ "%99", // 153
+ "%9a", // 154
+ "%9b", // 155
+ "%9c", // 156
+ "%9d", // 157
+ "%9e", // 158
+ "%9f", // 159
+ "%a0", // 160
+ "%a1", // 161
+ "%a2", // 162
+ "%a3", // 163
+ "%a4", // 164
+ "%a5", // 165
+ "%a6", // 166
+ "%a7", // 167
+ "%a8", // 168
+ "%a9", // 169
+ "%aa", // 170
+ "%ab", // 171
+ "%ac", // 172
+ "%ad", // 173
+ "%ae", // 174
+ "%af", // 175
+ "%b0", // 176
+ "%b1", // 177
+ "%b2", // 178
+ "%b3", // 179
+ "%b4", // 180
+ "%b5", // 181
+ "%b6", // 182
+ "%b7", // 183
+ "%b8", // 184
+ "%b9", // 185
+ "%ba", // 186
+ "%bb", // 187
+ "%bc", // 188
+ "%bd", // 189
+ "%be", // 190
+ "%bf", // 191
+ "%c0", // 192
+ "%c1", // 193
+ "%c2", // 194
+ "%c3", // 195
+ "%c4", // 196
+ "%c5", // 197
+ "%c6", // 198
+ "%c7", // 199
+ "%c8", // 200
+ "%c9", // 201
+ "%ca", // 202
+ "%cb", // 203
+ "%cc", // 204
+ "%cd", // 205
+ "%ce", // 206
+ "%cf", // 207
+ "%d0", // 208
+ "%d1", // 209
+ "%d2", // 210
+ "%d3", // 211
+ "%d4", // 212
+ "%d5", // 213
+ "%d6", // 214
+ "%d7", // 215
+ "%d8", // 216
+ "%d9", // 217
+ "%da", // 218
+ "%db", // 219
+ "%dc", // 220
+ "%dd", // 221
+ "%de", // 222
+ "%df", // 223
+ "%e0", // 224
+ "%e1", // 225
+ "%e2", // 226
+ "%e3", // 227
+ "%e4", // 228
+ "%e5", // 229
+ "%e6", // 230
+ "%e7", // 231
+ "%e8", // 232
+ "%e9", // 233
+ "%ea", // 234
+ "%eb", // 235
+ "%ec", // 236
+ "%ed", // 237
+ "%ee", // 238
+ "%ef", // 239
+ "%f0", // 240
+ "%f1", // 241
+ "%f2", // 242
+ "%f3", // 243
+ "%f4", // 244
+ "%f5", // 245
+ "%f6", // 246
+ "%f7", // 247
+ "%f8", // 248
+ "%f9", // 249
+ "%fa", // 250
+ "%fb", // 251
+ "%fc", // 252
+ "%fd", // 253
+ "%fe", // 254
+ "%ff" // 255
+};
+
+LLURI::LLURI()
+{
+}
+
+LLURI::LLURI(const std::string& escaped_str)
+{
+ std::string::size_type delim_pos, delim_pos2;
+ delim_pos = escaped_str.find(':');
+ std::string temp;
+ if (delim_pos == std::string::npos)
+ {
+ mScheme = "";
+ mEscapedOpaque = escaped_str;
+ }
+ else
+ {
+ mScheme = escaped_str.substr(0, delim_pos);
+ mEscapedOpaque = escaped_str.substr(delim_pos+1);
+ }
+
+ if (mScheme == "http" || mScheme == "https" || mScheme == "ftp")
+ {
+ if (mEscapedOpaque.substr(0,2) != "//")
+ {
+ return;
+ }
+
+ delim_pos = mEscapedOpaque.find('/', 2);
+ delim_pos2 = mEscapedOpaque.find('?', 2);
+ // no path, no query
+ if (delim_pos == std::string::npos &&
+ delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2);
+ mEscapedPath = "";
+ }
+ // path exist, no query
+ else if (delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ // no path, only query
+ else if (delim_pos == std::string::npos ||
+ delim_pos2 < delim_pos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos2);
+ }
+ // path and query
+ else
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ }
+
+ delim_pos = mEscapedPath.find('?');
+ if (delim_pos != std::string::npos)
+ {
+ mEscapedQuery = mEscapedPath.substr(delim_pos+1);
+ mEscapedPath = mEscapedPath.substr(0,delim_pos);
+ }
+}
+
+LLURI::~LLURI()
+{
+}
+
+
+LLURI LLURI::buildHTTP(const std::string& host_port,
+ const LLSD& path)
+{
+ LLURI result;
+ result.mScheme = "HTTP";
+ // TODO: deal with '/' '?' '#' in host_port
+ result.mEscapedAuthority = "//" + escape(host_port);
+ if (path.isArray())
+ {
+ // break out and escape each path component
+ for (LLSD::array_const_iterator it = path.beginArray();
+ it != path.endArray();
+ ++it)
+ {
+ lldebugs << "PATH: inserting " << it->asString() << llendl;
+ result.mEscapedPath += "/" + escape(it->asString());
+ }
+ }
+ result.mEscapedOpaque = result.mEscapedAuthority +
+ result.mEscapedPath;
+ return result;
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& host_port,
+ const LLSD& path,
+ const LLSD& query)
+{
+ LLURI result = buildHTTP(host_port, path);
+ // break out and escape each query component
+ if (query.isMap())
+ {
+ for (LLSD::map_const_iterator it = query.beginMap();
+ it != query.endMap();
+ it++)
+ {
+ result.mEscapedQuery += escape(it->first) +
+ (it->second.isUndefined() ? "" : "=" + it->second.asString()) +
+ "&";
+ }
+ if (query.size() > 0)
+ {
+ result.mEscapedOpaque += "?" + result.mEscapedQuery;
+ }
+ }
+ return result;
+}
+
+std::string LLURI::asString() const
+{
+ if (mScheme.empty())
+ {
+ return mEscapedOpaque;
+ }
+ else
+ {
+ return mScheme + ":" + mEscapedOpaque;
+ }
+}
+
+std::string LLURI::scheme() const
+{
+ return mScheme;
+}
+
+std::string LLURI::opaque() const
+{
+ return unescape(mEscapedOpaque);
+}
+
+std::string LLURI::authority() const
+{
+ return unescape(mEscapedAuthority);
+}
+
+
+namespace {
+ void findAuthorityParts(const std::string& authority,
+ std::string& user,
+ std::string& host,
+ std::string& port)
+ {
+ std::string::size_type start_pos = authority.find('@');
+ if (start_pos == std::string::npos)
+ {
+ user = "";
+ start_pos = 0;
+ }
+ else
+ {
+ user = authority.substr(0, start_pos);
+ start_pos += 1;
+ }
+
+ std::string::size_type end_pos = authority.find(':', start_pos);
+ if (end_pos == std::string::npos)
+ {
+ host = authority.substr(start_pos);
+ port = "";
+ }
+ else
+ {
+ host = authority.substr(start_pos, end_pos - start_pos);
+ port = authority.substr(end_pos + 1);
+ }
+ }
+}
+
+std::string LLURI::hostName() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ return unescape(host);
+}
+
+U16 LLURI::hostPort() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ if (port.empty())
+ {
+ if (mScheme == "http")
+ return 80;
+ if (mScheme == "https")
+ return 443;
+ if (mScheme == "ftp")
+ return 21;
+ return 0;
+ }
+ return atoi(port.c_str());
+}
+
+std::string LLURI::path() const
+{
+ return unescape(mEscapedPath);
+}
+
+std::string LLURI::query() const
+{
+ return unescape(mEscapedQuery);
+}
+
+LLSD LLURI::queryMap() const
+{
+ return queryMap(mEscapedQuery);
+}
+
+// static
+LLSD LLURI::queryMap(std::string escaped_query_string)
+{
+ lldebugs << "LLURI::queryMap query params: " << escaped_query_string << llendl;
+
+ LLSD result = LLSD::emptyArray();
+ while(!escaped_query_string.empty())
+ {
+ // get tuple first
+ std::string tuple;
+ std::string::size_type tuple_begin = escaped_query_string.find('&');
+ if (tuple_begin != std::string::npos)
+ {
+ tuple = escaped_query_string.substr(0, tuple_begin);
+ escaped_query_string = escaped_query_string.substr(tuple_begin+1);
+ }
+ else
+ {
+ tuple = escaped_query_string;
+ escaped_query_string = "";
+ }
+ if (tuple.empty()) continue;
+
+ // parse tuple
+ std::string::size_type key_end = tuple.find('=');
+ if (key_end != std::string::npos)
+ {
+ std::string key = unescape(tuple.substr(0,key_end));
+ std::string value = unescape(tuple.substr(key_end+1));
+ lldebugs << "inserting key " << key << " value " << value << llendl;
+ result[key] = value;
+ }
+ else
+ {
+ lldebugs << "inserting key " << unescape(tuple) << " value true" << llendl;
+ result[unescape(tuple)] = true;
+ }
+ }
+ return result;
+}
+
+// static
+std::string LLURI::escape(const std::string& str)
+{
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ S32 c;
+ for(; it != end; ++it)
+ {
+ c = (S32)(*it);
+ ostr << ESCAPED_CHARACTERS[c];
+ }
+ return ostr.str();
+}
+
+// static
+std::string LLURI::unescape(const std::string& str)
+{
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ for(; it != end; ++it)
+ {
+ if((*it) == '%')
+ {
+ ++it;
+ if(it == end) break;
+ U8 c = hex_as_nybble(*it++);
+ c = c << 4;
+ if (it == end) break;
+ c |= hex_as_nybble(*it);
+ ostr.put((char)c);
+ }
+ else
+ {
+ ostr.put(*it);
+ }
+ }
+ return ostr.str();
+}
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
new file mode 100644
index 0000000000..3fc62aeb98
--- /dev/null
+++ b/indra/llcommon/lluri.h
@@ -0,0 +1,72 @@
+/**
+ * @file lluri.h
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Declaration of the URI class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURI_H
+#define LL_LLURI_H
+
+#include <string>
+
+class LLSD;
+
+/**
+ *
+ * LLURI instances are immutable
+ * See: http://www.ietf.org/rfc/rfc3986.txt
+ *
+ */
+class LLURI
+{
+public:
+ LLURI();
+ LLURI(const std::string& escaped_str);
+ // construct from escaped string, as would be transmitted on the net
+
+ ~LLURI();
+
+ static LLURI buildHTTP(const std::string& host_port,
+ const LLSD& path);
+ static LLURI buildHTTP(const std::string& host_port,
+ const LLSD& path,
+ const LLSD& query);
+
+ std::string asString() const;
+ // the whole URI, escaped as needed
+
+ // Parts of a URI
+ // These functions return parts of the decoded URI. The returned
+ // strings are un-escaped as needed
+
+ // for all schemes
+ std::string scheme() const; // ex.: "http", note lack of colon
+ std::string opaque() const; // everything after the colon
+
+ // for schemes that follow path like syntax (http, https, ftp)
+ std::string authority() const; // ex.: "bob@host.com:80"
+ std::string hostName() const; // ex.: "host.com"
+ U16 hostPort() const; // ex.: 80, will include implicit port
+ std::string path() const; // ex.: "/abc/def", includes leading slash
+// LLSD pathArray() const; // above decoded into an array of strings
+ std::string query() const; // ex.: "x=34", section after "?"
+ LLSD queryMap() const; // above decoded into a map
+ static LLSD queryMap(std::string escaped_query_string);
+
+ // Escaping Utilities
+ static std::string escape(const std::string& str);
+ static std::string unescape(const std::string& str);
+
+private:
+ std::string mScheme;
+ std::string mEscapedOpaque;
+ std::string mEscapedAuthority;
+ std::string mEscapedPath;
+ std::string mEscapedQuery;
+};
+
+#endif // LL_LLURI_H
diff --git a/indra/llcommon/lluuidhashmap.h b/indra/llcommon/lluuidhashmap.h
new file mode 100644
index 0000000000..f7d32b1fe0
--- /dev/null
+++ b/indra/llcommon/lluuidhashmap.h
@@ -0,0 +1,557 @@
+/**
+ * @file lluuidhashmap.h
+ * @brief A uuid based hash map.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUUIDHASHMAP_H
+#define LL_LLUUIDHASHMAP_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+#include "lluuid.h"
+
+// UUID hash map
+
+ /*
+ LLUUIDHashMap<uuid_pair, 32> foo(test_equals);
+ LLUUIDHashMapIter<uuid_pair, 32> bar(&foo);
+
+ LLDynamicArray<LLUUID> source_ids;
+ const S32 COUNT = 100000;
+ S32 q;
+ for (q = 0; q < COUNT; q++)
+ {
+ llinfos << "Creating" << llendl;
+ LLUUID id;
+ id.generate();
+ //llinfos << q << ":" << id << llendl;
+ uuid_pair pair;
+ pair.mUUID = id;
+ pair.mValue = q;
+ foo.set(id, pair);
+ source_ids.put(id);
+ //ms_sleep(1);
+ }
+
+ uuid_pair cur;
+ llinfos << "Iterating" << llendl;
+ for (cur = bar.first(); !bar.done(); cur = bar.next())
+ {
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value iterated!" << llendl;
+ }
+ //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
+ //ms_sleep(1);
+ }
+
+ llinfos << "Finding" << llendl;
+ for (q = 0; q < COUNT; q++)
+ {
+ cur = foo.get(source_ids[q]);
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value found!" << llendl;
+ }
+ //llinfos << res.mValue << ":" << res.mUUID << llendl;
+ //ms_sleep(1);
+ }
+
+ llinfos << "Removing" << llendl;
+ for (q = 0; q < COUNT/2; q++)
+ {
+ if (!foo.remove(source_ids[q]))
+ {
+ llerrs << "Remove failed!" << llendl;
+ }
+ //ms_sleep(1);
+ }
+
+ llinfos << "Iterating" << llendl;
+ for (cur = bar.first(); !bar.done(); cur = bar.next())
+ {
+ if (source_ids[cur.mValue] != cur.mUUID)
+ {
+ llerrs << "Incorrect value found!" << llendl;
+ }
+ //llinfos << cur.mValue << ":" << cur.mUUID << llendl;
+ //ms_sleep(1);
+ }
+ llinfos << "Done with UUID map test" << llendl;
+
+ return 0;
+ */
+
+
+//
+// LLUUIDHashNode
+//
+
+template <class DATA, int SIZE>
+class LLUUIDHashNode
+{
+public:
+ LLUUIDHashNode();
+
+public:
+ S32 mCount;
+ U8 mKey[SIZE];
+ DATA mData[SIZE];
+ LLUUIDHashNode<DATA, SIZE> *mNextNodep;
+};
+
+
+//
+// LLUUIDHashNode implementation
+//
+template <class DATA, int SIZE>
+LLUUIDHashNode<DATA, SIZE>::LLUUIDHashNode()
+{
+ mCount = 0;
+ mNextNodep = NULL;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+class LLUUIDHashMap
+{
+public:
+ // basic constructor including sorter
+ LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
+ const DATA_TYPE &null_data);
+ ~LLUUIDHashMap();
+
+ inline DATA_TYPE &get(const LLUUID &uuid);
+ inline BOOL check(const LLUUID &uuid) const;
+ inline DATA_TYPE &set(const LLUUID &uuid, const DATA_TYPE &type);
+ inline BOOL remove(const LLUUID &uuid);
+ void removeAll();
+
+ inline S32 getLength() const; // Warning, NOT O(1!)
+public:
+ BOOL (*mEquals)(const LLUUID &uuid, const DATA_TYPE &data);
+ LLUUIDHashNode<DATA_TYPE, SIZE> mNodes[256];
+
+ S32 mIterCount;
+protected:
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLUUIDHashMap implementation
+//
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMap<DATA_TYPE, SIZE>::LLUUIDHashMap(BOOL (*equals)(const LLUUID &uuid, const DATA_TYPE &data),
+ const DATA_TYPE &null_data)
+: mEquals(equals),
+ mIterCount(0),
+ mNull(null_data)
+{ }
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMap<DATA_TYPE, SIZE>::~LLUUIDHashMap()
+{
+ removeAll();
+}
+
+template <class DATA_TYPE, int SIZE>
+void LLUUIDHashMap<DATA_TYPE, SIZE>::removeAll()
+{
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+
+ BOOL first = TRUE;
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ nodep->mData[i] = mNull;
+ }
+
+ nodep->mCount = 0;
+ // Done with all objects in this node, go to the next.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE>* curp = nodep;
+ nodep = nodep->mNextNodep;
+
+ // Delete the node if it's not the first node
+ if (first)
+ {
+ first = FALSE;
+ curp->mNextNodep = NULL;
+ }
+ else
+ {
+ delete curp;
+ }
+ }
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline S32 LLUUIDHashMap<DATA_TYPE, SIZE>::getLength() const
+{
+ S32 count = 0;
+ S32 bin;
+ for (bin = 0; bin < 256; bin++)
+ {
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[bin];
+ while (nodep)
+ {
+ count += nodep->mCount;
+ nodep = nodep->mNextNodep;
+ }
+ }
+ return count;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::get(const LLUUID &uuid)
+{
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ // Grab the second byte of the UUID, which is the key for the node data
+ const S32 second_byte = uuid.mData[1];
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // The second byte matched, and our equality test passed.
+ // We found it.
+ return nodep->mData[i];
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ }
+ return mNull;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::check(const LLUUID &uuid) const
+{
+ const LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ // Grab the second byte of the UUID, which is the key for the node data
+ const S32 second_byte = uuid.mData[1];
+ while (nodep)
+ {
+ S32 i;
+ const S32 count = nodep->mCount;
+
+ // Iterate through all members of this node
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // The second byte matched, and our equality test passed.
+ // We found it.
+ return TRUE;
+ }
+ }
+
+ // Done with all objects in this node, go to the next.
+ nodep = nodep->mNextNodep;
+ }
+
+ // Didn't find anything
+ return FALSE;
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline DATA_TYPE &LLUUIDHashMap<DATA_TYPE, SIZE>::set(const LLUUID &uuid, const DATA_TYPE &data)
+{
+ // Set is just like a normal find, except that if we find a match
+ // we replace it with the input value.
+ // If we don't find a match, we append to the end of the list.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE>* nodep = &mNodes[uuid.mData[0]];
+
+ const S32 second_byte = uuid.mData[1];
+ while (1)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // We found a match for this key, replace the data with
+ // the incoming data.
+ nodep->mData[i] = data;
+ return nodep->mData[i];
+ }
+ }
+ if (!nodep->mNextNodep)
+ {
+ // We've iterated through all of the keys without finding a match
+ if (i < SIZE)
+ {
+ // There's still some space on this node, append
+ // the key and data to it.
+ nodep->mKey[i] = second_byte;
+ nodep->mData[i] = data;
+ nodep->mCount++;
+
+ return nodep->mData[i];
+ }
+ else
+ {
+ // This node is full, append a new node to the end.
+ nodep->mNextNodep = new LLUUIDHashNode<DATA_TYPE, SIZE>;
+ nodep->mNextNodep->mKey[0] = second_byte;
+ nodep->mNextNodep->mData[0] = data;
+ nodep->mNextNodep->mCount = 1;
+
+ return nodep->mNextNodep->mData[0];
+ }
+ }
+
+ // No match on this node, go to the next
+ nodep = nodep->mNextNodep;
+ }
+}
+
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMap<DATA_TYPE, SIZE>::remove(const LLUUID &uuid)
+{
+ if (mIterCount)
+ {
+ // We don't allow remove when we're iterating, it's bad karma!
+ llerrs << "Attempted remove while an outstanding iterator in LLUUIDHashMap!" << llendl;
+ }
+ // Remove is the trickiest operation.
+ // What we want to do is swap the last element of the last
+ // node if we find the one that we want to remove, but we have
+ // to deal with deleting the node from the tail if it's empty, but
+ // NOT if it's the only node left.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE> *nodep = &mNodes[uuid.mData[0]];
+
+ // Not empty, we need to search through the nodes
+ const S32 second_byte = uuid.mData[1];
+
+ // A modification of the standard search algorithm.
+ while (nodep)
+ {
+ const S32 count = nodep->mCount;
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if ((nodep->mKey[i] == second_byte) && mEquals(uuid, nodep->mData[i]))
+ {
+ // We found the node that we want to remove.
+ // Find the last (and next-to-last) node, and the index of the last
+ // element. We could conceviably start from the node we're on,
+ // but that makes it more complicated, this is easier.
+
+ LLUUIDHashNode<DATA_TYPE, SIZE> *prevp = &mNodes[uuid.mData[0]];
+ LLUUIDHashNode<DATA_TYPE, SIZE> *lastp = prevp;
+
+ // Find the last and next-to-last
+ while (lastp->mNextNodep)
+ {
+ prevp = lastp;
+ lastp = lastp->mNextNodep;
+ }
+
+ // First, swap in the last to the current location.
+ nodep->mKey[i] = lastp->mKey[lastp->mCount - 1];
+ nodep->mData[i] = lastp->mData[lastp->mCount - 1];
+
+ // Now, we delete the entry
+ lastp->mCount--;
+ lastp->mData[lastp->mCount] = mNull;
+
+ if (!lastp->mCount)
+ {
+ // We deleted the last element!
+ if (lastp != &mNodes[uuid.mData[0]])
+ {
+ // Only blitz the node if it's not the head
+ // Set the previous node to point to NULL, then
+ // blitz the empty last node
+ prevp->mNextNodep = NULL;
+ delete lastp;
+ }
+ }
+ return TRUE;
+ }
+ }
+
+ // Iterate to the next node, we've scanned all the entries in this one.
+ nodep = nodep->mNextNodep;
+ }
+ return FALSE;
+}
+
+
+//
+// LLUUIDHashMapIter
+//
+
+template <class DATA_TYPE, int SIZE>
+class LLUUIDHashMapIter
+{
+public:
+ LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp);
+ ~LLUUIDHashMapIter();
+
+
+ inline void first();
+ inline void next();
+ inline BOOL done() const;
+
+ DATA_TYPE& operator*() const
+ {
+ return mCurHashNodep->mData[mCurHashNodeKey];
+ }
+ DATA_TYPE* operator->() const
+ {
+ return &(operator*());
+ }
+
+protected:
+ LLUUIDHashMap<DATA_TYPE, SIZE> *mHashMapp;
+ LLUUIDHashNode<DATA_TYPE, SIZE> *mCurHashNodep;
+
+ S32 mCurHashMapNodeNum;
+ S32 mCurHashNodeKey;
+
+ DATA_TYPE mNull;
+};
+
+
+//
+// LLUUIDHashMapIter Implementation
+//
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMapIter<DATA_TYPE, SIZE>::LLUUIDHashMapIter(LLUUIDHashMap<DATA_TYPE, SIZE> *hash_mapp)
+{
+ mHashMapp = hash_mapp;
+ mCurHashNodep = NULL;
+ mCurHashMapNodeNum = 0;
+ mCurHashNodeKey = 0;
+}
+
+template <class DATA_TYPE, int SIZE>
+LLUUIDHashMapIter<DATA_TYPE, SIZE>::~LLUUIDHashMapIter()
+{
+ if (mCurHashNodep)
+ {
+ // We're partway through an iteration, we can clean up now
+ mHashMapp->mIterCount--;
+ }
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::first()
+{
+ // Iterate through until we find the first non-empty node;
+ S32 i;
+ for (i = 0; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ if (!mCurHashNodep)
+ {
+ // Increment, since it's no longer safe for us to do a remove
+ mHashMapp->mIterCount++;
+ }
+
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // Completely empty!
+ mCurHashNodep = NULL;
+ //return mNull;
+ return;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline BOOL LLUUIDHashMapIter<DATA_TYPE, SIZE>::done() const
+{
+ return mCurHashNodep ? FALSE : TRUE;
+}
+
+template <class DATA_TYPE, int SIZE>
+inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::next()
+{
+ // No current entry, this iterator is done
+ if (!mCurHashNodep)
+ {
+ //return mNull;
+ return;
+ }
+
+ // Go to the next element
+ mCurHashNodeKey++;
+ if (mCurHashNodeKey < mCurHashNodep->mCount)
+ {
+ // We're not done with this node, return the current element
+ //return mCurHashNodep->mData[mCurHashNodeKey];
+ return;
+ }
+
+ // Done with this node, move to the next
+ mCurHashNodep = mCurHashNodep->mNextNodep;
+ if (mCurHashNodep)
+ {
+ // Return the first element
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+
+ // Find the next non-empty node (keyed on the first byte)
+ mCurHashMapNodeNum++;
+
+ S32 i;
+ for (i = mCurHashMapNodeNum; i < 256; i++)
+ {
+ if (mHashMapp->mNodes[i].mCount)
+ {
+ // We found one that wasn't empty
+ mCurHashNodep = &mHashMapp->mNodes[i];
+ mCurHashMapNodeNum = i;
+ mCurHashNodeKey = 0;
+ //return mCurHashNodep->mData[0];
+ return;
+ }
+ }
+
+ // OK, we're done, nothing else to iterate
+ mCurHashNodep = NULL;
+ mHashMapp->mIterCount--; // Decrement since we're safe to do removes now
+ //return mNull;
+}
+
+#endif // LL_LLUUIDHASHMAP_H
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
new file mode 100644
index 0000000000..a9370c8f6d
--- /dev/null
+++ b/indra/llcommon/llworkerthread.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file llworkerthread.cpp
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llworkerthread.h"
+#include "llstl.h"
+
+#if USE_FRAME_CALLBACK_MANAGER
+#include "llframecallbackmanager.h"
+#endif
+
+//============================================================================
+
+/*static*/ LLWorkerThread* LLWorkerThread::sLocal = NULL;
+/*static*/ std::set<LLWorkerThread*> LLWorkerThread::sThreadList;
+
+//============================================================================
+// Run on MAIN thread
+
+//static
+void LLWorkerThread::initClass(bool local_is_threaded, bool local_run_always)
+{
+ if (!sLocal)
+ {
+ sLocal = new LLWorkerThread(local_is_threaded, local_run_always);
+ }
+}
+
+//static
+void LLWorkerThread::cleanupClass()
+{
+ if (sLocal)
+ {
+ while (sLocal->getPending())
+ {
+ sLocal->update(0);
+ }
+ delete sLocal;
+ sLocal = NULL;
+ llassert(sThreadList.size() == 0);
+ }
+}
+
+//static
+S32 LLWorkerThread::updateClass(U32 ms_elapsed)
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->update(ms_elapsed);
+ }
+ return getAllPending();
+}
+
+//static
+S32 LLWorkerThread::getAllPending()
+{
+ S32 res = 0;
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ res += (*iter)->getPending();
+ }
+ return res;
+}
+
+//static
+void LLWorkerThread::pauseAll()
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->pause();
+ }
+}
+
+//static
+void LLWorkerThread::waitOnAllPending()
+{
+ for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++)
+ {
+ (*iter)->waitOnPending();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+LLWorkerThread::LLWorkerThread(bool threaded, bool runalways) :
+ LLQueuedThread("Worker", threaded, runalways)
+{
+ sThreadList.insert(this);
+}
+
+LLWorkerThread::~LLWorkerThread()
+{
+ llverify(sThreadList.erase(this) == 1);
+ // ~LLQueuedThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+
+LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 param, U32 priority)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, priority, workerclass, param);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "add called after LLWorkerThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+bool LLWorkerThread::processRequest(QueuedRequest* qreq)
+{
+ Request *req = (Request*)qreq;
+
+ req->getWorkerClass()->setWorking(true);
+
+ bool complete = req->getWorkerClass()->doWork(req->getParam());
+
+ req->getWorkerClass()->setWorking(false);
+
+ LLThread::yield(); // worker thread should yield after each request
+
+ return complete;
+}
+
+//============================================================================
+
+LLWorkerThread::Request::Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
+ LLQueuedThread::QueuedRequest(handle, priority),
+ mWorkerClass(workerclass),
+ mParam(param)
+{
+}
+
+void LLWorkerThread::Request::deleteRequest()
+{
+ LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+//============================================================================
+// LLWorkerClass:: operates in main thread
+
+LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name)
+ : mWorkerThread(workerthread),
+ mWorkerClassName(name),
+ mWorkHandle(LLWorkerThread::nullHandle()),
+ mWorkFlags(0)
+{
+ if (!mWorkerThread)
+ {
+ mWorkerThread = LLWorkerThread::sLocal;
+ }
+}
+LLWorkerClass::~LLWorkerClass()
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
+ if (!workreq)
+ {
+ llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
+ }
+ if (workreq->getStatus() != LLWorkerThread::STATUS_ABORT &&
+ workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
+ workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
+ {
+ llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
+ }
+ }
+}
+
+void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl;
+ }
+ mWorkerThread = workerthread;
+}
+
+//----------------------------------------------------------------------------
+
+bool LLWorkerClass::yield()
+{
+ llassert(mWorkFlags & WCF_WORKING);
+ LLThread::yield();
+ mWorkerThread->checkPause();
+ return (getFlags() & WCF_ABORT_REQUESTED) ? true : false;
+}
+
+//----------------------------------------------------------------------------
+
+// calls startWork, adds doWork() to queue
+void LLWorkerClass::addWork(S32 param, U32 priority)
+{
+ if (mWorkHandle != LLWorkerThread::nullHandle())
+ {
+ llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl;
+ }
+#if _DEBUG
+// llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl;
+#endif
+ startWork(param);
+ mWorkHandle = mWorkerThread->add(this, param, priority);
+}
+
+void LLWorkerClass::abortWork()
+{
+#if _DEBUG
+// LLWorkerThread::Request* workreq = mWorkerThread->getRequest(mWorkHandle);
+// if (workreq)
+// llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
+#endif
+ mWorkerThread->abortRequest(mWorkHandle);
+ setFlags(WCF_ABORT_REQUESTED);
+}
+
+// if doWork is complete or aborted, call endWork() and return true
+bool LLWorkerClass::checkWork()
+{
+ bool complete = false, abort = false;
+ LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle);
+ llassert(workreq);
+ if (getFlags(WCF_ABORT_REQUESTED) || workreq->getStatus() == LLWorkerThread::STATUS_ABORTED)
+ {
+ complete = true;
+ abort = true;
+ }
+ else if (workreq->getStatus() == LLWorkerThread::STATUS_COMPLETE)
+ {
+ complete = true;
+ }
+ if (complete)
+ {
+#if _DEBUG
+// llinfos << "endWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
+#endif
+ endWork(workreq->getParam(), abort);
+ mWorkerThread->completeRequest(mWorkHandle);
+ mWorkHandle = LLWorkerThread::nullHandle();
+ }
+ return complete;
+}
+
+void LLWorkerClass::killWork()
+{
+ if (haveWork())
+ {
+ abortWork();
+ bool paused = mWorkerThread->isPaused();
+ while (!checkWork())
+ {
+ mWorkerThread->updateQueue(0);
+ }
+ if (paused)
+ {
+ mWorkerThread->pause();
+ }
+ }
+}
+
+void LLWorkerClass::setPriority(U32 priority)
+{
+ if (haveWork())
+ {
+ mWorkerThread->setPriority(mWorkHandle, priority);
+ }
+}
+
+//============================================================================
+
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
new file mode 100644
index 0000000000..bf5887e797
--- /dev/null
+++ b/indra/llcommon/llworkerthread.h
@@ -0,0 +1,168 @@
+/**
+ * @file llworkerthread.h
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORKERTHREAD_H
+#define LL_LLWORKERTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llqueuedthread.h"
+
+#define USE_FRAME_CALLBACK_MANAGER 0
+
+//============================================================================
+
+class LLWorkerClass;
+
+//============================================================================
+// Note: ~LLWorkerThread is O(N) N=# of worker threads, assumed to be small
+// It is assumed that LLWorkerThreads are rarely created/destroyed.
+
+class LLWorkerThread : public LLQueuedThread
+{
+public:
+ class Request : public LLQueuedThread::QueuedRequest
+ {
+ protected:
+ ~Request() {}; // use deleteRequest()
+
+ public:
+ Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param);
+
+ S32 getParam()
+ {
+ return mParam;
+ }
+ LLWorkerClass* getWorkerClass()
+ {
+ return mWorkerClass;
+ }
+
+ /*virtual*/ void deleteRequest();
+
+ private:
+ LLWorkerClass* mWorkerClass;
+ S32 mParam;
+ };
+
+public:
+ LLWorkerThread(bool threaded = true, bool runalways = true);
+ ~LLWorkerThread();
+
+protected:
+ /*virtual*/ bool processRequest(QueuedRequest* req);
+
+public:
+ handle_t add(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
+
+ static void initClass(bool local_is_threaded = true, bool local_run_always = true); // Setup sLocal
+ static S32 updateClass(U32 ms_elapsed);
+ static S32 getAllPending();
+ static void pauseAll();
+ static void waitOnAllPending();
+ static void cleanupClass(); // Delete sLocal
+
+public:
+ static LLWorkerThread* sLocal; // Default worker thread
+ static std::set<LLWorkerThread*> sThreadList; // array of threads (includes sLocal)
+};
+
+//============================================================================
+
+// This is a base class which any class with worker functions should derive from.
+// Example Usage:
+// LLMyWorkerClass* foo = new LLMyWorkerClass();
+// foo->fetchData(); // calls addWork()
+// while(1) // main loop
+// {
+// if (foo->hasData()) // calls checkWork()
+// foo->processData();
+// }
+//
+// WorkerClasses only have one set of work functions. If they need to do multiple
+// background tasks, use 'param' to switch amnong them.
+// Only one background task can be active at a time (per instance).
+// i.e. don't call addWork() if haveWork() returns true
+
+class LLWorkerClass
+{
+public:
+ typedef LLWorkerThread::handle_t handle_t;
+ enum FLAGS
+ {
+ WCF_WORKING = 0x01,
+ WCF_ABORT_REQUESTED = 0x80
+ };
+
+public:
+ LLWorkerClass(LLWorkerThread* workerthread, const std::string& name);
+ virtual ~LLWorkerClass();
+
+ // pure virtual, called from WORKER THREAD, returns TRUE if done
+ virtual bool doWork(S32 param)=0; // Called from LLWorkerThread::processRequest()
+
+ // called from WORKER THREAD
+ void setWorking(bool working) { working ? setFlags(WCF_WORKING) : clearFlags(WCF_WORKING); }
+
+ bool isWorking() { return getFlags(WCF_WORKING); }
+ bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
+
+ const std::string& getName() const { return mWorkerClassName; }
+
+protected:
+ // Call from doWork only to avoid eating up cpu time.
+ // Returns true if work has been aborted
+ // yields the current thread and calls mWorkerThread->checkPause()
+ bool yield();
+
+ void setWorkerThread(LLWorkerThread* workerthread);
+
+ // addWork(): calls startWork, adds doWork() to queue
+ void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL);
+
+ // abortWork(): requests that work be aborted
+ void abortWork();
+
+ // checkWork(): if doWork is complete or aborted, call endWork() and return true
+ bool checkWork();
+
+ // haveWork(): return true if mWorkHandle != null
+ bool haveWork() { return mWorkHandle != LLWorkerThread::nullHandle(); }
+
+ // killWork(): aborts work and waits for the abort to process
+ void killWork();
+
+ // setPriority(): changes the priority of a request
+ void setPriority(U32 priority);
+
+private:
+ void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; }
+ void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; }
+ U32 getFlags() { return mWorkFlags; }
+ bool getFlags(U32 flags) { return mWorkFlags & flags ? true : false; }
+
+private:
+ // pure virtuals
+ virtual void startWork(S32 param)=0; // called from addWork() (MAIN THREAD)
+ virtual void endWork(S32 param, bool aborted)=0; // called from doWork() (MAIN THREAD)
+
+protected:
+ LLWorkerThread* mWorkerThread;
+ std::string mWorkerClassName;
+ handle_t mWorkHandle;
+
+private:
+ LLAtomicU32 mWorkFlags;
+};
+
+//============================================================================
+
+
+#endif // LL_LLWORKERTHREAD_H
diff --git a/indra/llcommon/metaclass.cpp b/indra/llcommon/metaclass.cpp
new file mode 100644
index 0000000000..29ad20e6b6
--- /dev/null
+++ b/indra/llcommon/metaclass.cpp
@@ -0,0 +1,60 @@
+/**
+ * @file metaclass.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLMetaClass
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "metaclass.h"
+#include "metaproperty.h"
+#include "reflective.h"
+
+LLMetaClass::LLMetaClass()
+{
+}
+
+//virtual
+LLMetaClass::~LLMetaClass()
+{
+}
+
+const LLMetaProperty* LLMetaClass::findProperty(const std::string& name) const
+{
+ PropertyIterator iter = mProperties.find(name);
+ if(iter == mProperties.end())
+ {
+ return NULL;
+ }
+ return (*iter).second;
+}
+
+void LLMetaClass::addProperty(const LLMetaProperty* property)
+{
+ mProperties.insert(std::make_pair(property->getName(), property));
+}
+
+U32 LLMetaClass::getPropertyCount() const
+{
+ return mProperties.size();
+}
+
+LLMetaClass::PropertyIterator LLMetaClass::beginProperties() const
+{
+ return mProperties.begin();
+}
+
+LLMetaClass::PropertyIterator LLMetaClass::endProperties() const
+{
+ return mProperties.end();
+}
+
+bool LLMetaClass::isInstance(const LLReflective* object) const
+{
+ // TODO: Babbage: Search through super classes of objects MetaClass.
+ const LLMetaClass* object_meta_class = &(object->getMetaClass());
+ return (object_meta_class == this);
+}
+
diff --git a/indra/llcommon/metaclass.h b/indra/llcommon/metaclass.h
new file mode 100644
index 0000000000..3424cea087
--- /dev/null
+++ b/indra/llcommon/metaclass.h
@@ -0,0 +1,64 @@
+/**
+ * @file metaclass.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Reflective meta information describing a class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METACLASS_H
+#define LL_METACLASS_H
+
+#include <string>
+#include <map>
+
+#include "stdtypes.h"
+
+class LLReflective;
+class LLMetaProperty;
+class LLMetaMethod;
+class LLMetaClass
+{
+public:
+
+ LLMetaClass();
+ virtual ~LLMetaClass();
+
+ // Create instance of this MetaClass. NULL if class is abstract.
+ // Gives ownership of returned object.
+ // virtual LLReflective* create() const = 0;
+
+ // Returns named property or NULL.
+ const LLMetaProperty* findProperty(const std::string& name) const;
+
+ // Add property to metaclass. Takes ownership of given property.
+ void addProperty(const LLMetaProperty* property);
+
+ typedef std::map<std::string, const LLMetaProperty*>::const_iterator PropertyIterator;
+
+ U32 getPropertyCount() const;
+
+ PropertyIterator beginProperties() const;
+ PropertyIterator endProperties() const;
+
+ // Returns named property or NULL.
+ // const LLMetaMethod* findMethod(const std::string& name) const;
+
+ // Add method to metaclass. Takes ownership of given method.
+ // void addMethod(const LLMetaMethod* method);
+
+ // Find MetaClass by name. NULL if name is unknown.
+ // static LLMetaClass* findClass(const std::string& name);
+
+ // True if object is instance of this meta class.
+ bool isInstance(const LLReflective* object) const;
+
+private:
+
+ typedef std::map<std::string, const LLMetaProperty*> PropertyMap;
+ PropertyMap mProperties;
+};
+
+#endif // LL_METACLASS_H
diff --git a/indra/llcommon/metaclasst.h b/indra/llcommon/metaclasst.h
new file mode 100644
index 0000000000..8ae9016b57
--- /dev/null
+++ b/indra/llcommon/metaclasst.h
@@ -0,0 +1,42 @@
+/**
+ * @file metaclasst.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METACLASST_H
+#define LL_METACLASST_H
+
+#include "metaclass.h"
+
+template<class TObject>
+class LLMetaClassT : public LLMetaClass
+{
+ public:
+
+ virtual ~LLMetaClassT() {;}
+
+ static const LLMetaClassT& instance()
+ {
+ static const LLMetaClassT& instance = buildMetaClass();
+ return instance;
+ }
+
+ private:
+
+ static const LLMetaClassT& buildMetaClass()
+ {
+ LLMetaClassT& meta_class = *(new LLMetaClassT());
+ reflectProperties(meta_class);
+ return meta_class;
+ }
+
+ LLMetaClassT() {;}
+
+ static void reflectProperties(LLMetaClass&)
+ {
+ }
+};
+
+#endif // LL_METACLASST_H
diff --git a/indra/llcommon/metaproperty.cpp b/indra/llcommon/metaproperty.cpp
new file mode 100644
index 0000000000..befee61a8a
--- /dev/null
+++ b/indra/llcommon/metaproperty.cpp
@@ -0,0 +1,35 @@
+/**
+ * @file metaproperty.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLMetaProperty.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "metaproperty.h"
+#include "metaclass.h"
+
+LLMetaProperty::LLMetaProperty(const std::string& name, const LLMetaClass& object_class) :
+ mName(name), mObjectClass(object_class)
+{
+}
+
+//virtual
+LLMetaProperty::~LLMetaProperty()
+{
+}
+
+const LLMetaClass& LLMetaProperty::getObjectMetaClass() const
+{
+ return mObjectClass;
+}
+
+void LLMetaProperty::checkObjectClass(const LLReflective* object) const
+{
+ if(! mObjectClass.isInstance(object))
+ {
+ throw "class cast exception";
+ }
+}
diff --git a/indra/llcommon/metaproperty.h b/indra/llcommon/metaproperty.h
new file mode 100644
index 0000000000..be615f2c67
--- /dev/null
+++ b/indra/llcommon/metaproperty.h
@@ -0,0 +1,55 @@
+/**
+ * @file metaproperty.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Reflective meta information describing a property of a class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METAPROPERTY_H
+#define LL_METAPROPERTY_H
+
+#include "stdtypes.h"
+#include "llsd.h"
+#include "reflective.h"
+
+class LLMetaClass;
+class LLReflective;
+class LLMetaProperty
+{
+public:
+ LLMetaProperty(const std::string& name, const LLMetaClass& object_class);
+ virtual ~LLMetaProperty();
+
+ // Get property name.
+ const std::string& getName() const {return mName;}
+
+ // Get value of this property.
+ virtual const LLReflective* get(const LLReflective* object) const = 0;
+
+ // Set value of this property.
+ // virtual void set(LLReflective* object, const LLReflective* value) = 0;
+
+ // Get value of this property as LLSD. Default returns undefined LLSD.
+ virtual LLSD getLLSD(const LLReflective* object) const = 0;
+
+ // Get the MetaClass of legal values of this property.
+ // const LLMetaClass& getValueMetaClass();
+
+ // Get the meta class that this property is a member of.
+ const LLMetaClass& getObjectMetaClass() const;
+
+protected:
+
+ // Check object is instance of object class, throw exception if not.
+ void checkObjectClass(const LLReflective* object) const;
+
+private:
+
+ std::string mName;
+ const LLMetaClass& mObjectClass;
+};
+
+#endif // LL_METAPROPERTY_H
diff --git a/indra/llcommon/metapropertyt.h b/indra/llcommon/metapropertyt.h
new file mode 100644
index 0000000000..056e422221
--- /dev/null
+++ b/indra/llcommon/metapropertyt.h
@@ -0,0 +1,165 @@
+/**
+ * @file metapropertyt.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_METAPROPERTYT_H
+#define LL_METAPROPERTYT_H
+
+#include "llsd.h"
+#include "llstring.h"
+#include "metaclasst.h"
+#include "metaproperty.h"
+#include "reflectivet.h"
+
+template<class TProperty>
+class LLMetaPropertyT : public LLMetaProperty
+{
+public:
+
+ virtual ~LLMetaPropertyT() {;}
+
+ // Get value of this property. Gives ownership of returned value.
+ virtual const LLReflective* get(const LLReflective* object) const
+ {
+ checkObjectClass(object);
+ return getProperty(object);
+ }
+
+ // Set value of this property.
+ /*virtual void set(LLReflective* object, const LLReflective* value)
+ {
+ // TODO: Babbage: Check types.
+ ref(object) = static_cast<const LLReflectiveT<TProperty>* >(value)->getValue();
+ }*/
+
+ // Get value of this property as LLSD.
+ virtual LLSD getLLSD(const LLReflective* object) const
+ {
+ return LLSD();
+ }
+
+protected:
+
+ LLMetaPropertyT(const std::string& name, const LLMetaClass& object_class) : LLMetaProperty(name, object_class) {;}
+
+ virtual const TProperty* getProperty(const LLReflective* object) const = 0;
+};
+
+template <>
+inline const LLReflective* LLMetaPropertyT<S32>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<std::string>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<LLString>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline const LLReflective* LLMetaPropertyT<LLUUID>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
+inline LLSD LLMetaPropertyT<S32>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<std::string>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<LLString>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template <>
+inline LLSD LLMetaPropertyT<LLUUID>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
+template<class TObject, class TProperty>
+class LLMetaPropertyTT : public LLMetaPropertyT<TProperty>
+{
+public:
+
+ LLMetaPropertyTT(const std::string& name, const LLMetaClass& object_class, TProperty (TObject::*property)) :
+ LLMetaPropertyT<TProperty>(name, object_class), mProperty(property) {;}
+
+protected:
+
+ // Get void* to property.
+ virtual const TProperty* getProperty(const LLReflective* object) const
+ {
+ const TObject* typed_object = static_cast<const TObject*>(object);
+ return &(typed_object->*mProperty);
+ };
+
+private:
+
+ TProperty (TObject::*mProperty);
+};
+
+template<class TObject, class TProperty>
+class LLMetaPropertyPtrTT : public LLMetaPropertyT<TProperty>
+{
+public:
+
+ LLMetaPropertyPtrTT(const std::string& name, const LLMetaClass& object_class, TProperty* (TObject::*property)) :
+ LLMetaPropertyT<TProperty>(name, object_class), mProperty(property) {;}
+
+protected:
+
+ // Get void* to property.
+ virtual const TProperty* getProperty(const LLReflective* object) const
+ {
+ const TObject* typed_object = static_cast<const TObject*>(object);
+ return typed_object->*mProperty;
+ };
+
+private:
+
+ TProperty* (TObject::*mProperty);
+};
+
+// Utility function to simplify the registration of members.
+template<class TObject, class TProperty>
+void reflectProperty(LLMetaClass& meta_class, const std::string& name, TProperty (TObject::*property))
+{
+ typedef LLMetaPropertyTT<TObject, TProperty> PropertyType;
+ const LLMetaProperty* meta_property = new PropertyType(name, meta_class, property);
+ meta_class.addProperty(meta_property);
+}
+
+// Utility function to simplify the registration of ptr properties.
+template<class TObject, class TProperty>
+void reflectPtrProperty(LLMetaClass& meta_class, const std::string& name, TProperty* (TObject::*property))
+{
+ typedef LLMetaPropertyPtrTT<TObject, TProperty> PropertyType;
+ const LLMetaProperty* meta_property = new PropertyType(name, meta_class, property);
+ meta_class.addProperty(meta_property);
+}
+
+#endif // LL_METAPROPERTYT_H
diff --git a/indra/llcommon/reflective.cpp b/indra/llcommon/reflective.cpp
new file mode 100644
index 0000000000..039a500575
--- /dev/null
+++ b/indra/llcommon/reflective.cpp
@@ -0,0 +1,20 @@
+/**
+ * @file reflective.cpp
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Implementation of LLReflective.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "reflective.h"
+
+LLReflective::LLReflective()
+{
+}
+
+//virtual
+LLReflective::~LLReflective()
+{
+}
diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h
new file mode 100644
index 0000000000..1e3501390e
--- /dev/null
+++ b/indra/llcommon/reflective.h
@@ -0,0 +1,24 @@
+/**
+ * @file reflective.h
+ * @author Babbage
+ * @date 2006-05-15
+ * @brief Interface that must be implemented by all reflective classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_REFLECTIVE_H
+#define LL_REFLECTIVE_H
+
+class LLMetaClass;
+class LLReflective
+{
+public:
+ LLReflective();
+ virtual ~LLReflective();
+
+ virtual const LLMetaClass& getMetaClass() const = 0;
+};
+
+#endif // LL_REFLECTIVE_H
diff --git a/indra/llcommon/reflectivet.h b/indra/llcommon/reflectivet.h
new file mode 100644
index 0000000000..e16b4386ed
--- /dev/null
+++ b/indra/llcommon/reflectivet.h
@@ -0,0 +1,30 @@
+/**
+ * @file reflectivet.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_REFLECTIVET_H
+#define LL_REFLECTIVET_H
+
+#include "reflective.h"
+
+template <class T>
+class LLReflectiveT : public LLReflective
+{
+public:
+
+ LLReflectiveT(const T& value) : mValue(value) {;}
+ virtual ~LLReflectiveT() {;}
+
+ virtual const LLMetaClass& getMetaClass() const {return LLMetaClassT<LLReflectiveT<T> >::instance();}
+
+ const T& getValue() const {return mValue;}
+
+private:
+
+ T mValue;
+};
+
+#endif
diff --git a/indra/llcommon/roles_constants.h b/indra/llcommon/roles_constants.h
new file mode 100644
index 0000000000..a4771dfeb6
--- /dev/null
+++ b/indra/llcommon/roles_constants.h
@@ -0,0 +1,168 @@
+/**
+ * @file roles_constants.h
+ * @brief General Roles Constants
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_ROLES_CONSTANTS_H
+#define LL_ROLES_CONSTANTS_H
+
+// This value includes the everyone group.
+const S32 MAX_ROLES = 10;
+
+enum LLRoleMemberChangeType
+{
+ RMC_ADD,
+ RMC_REMOVE,
+ RMC_NONE
+};
+
+enum LLRoleChangeType
+{
+ RC_UPDATE_NONE,
+ RC_UPDATE_DATA,
+ RC_UPDATE_POWERS,
+ RC_UPDATE_ALL,
+ RC_CREATE,
+ RC_DELETE
+};
+
+//
+// Powers
+//
+
+// KNOWN HOLES:
+// bit 0x1 << 37 (GP_OBJECT_RETURN)
+
+// These powers were removed to make group roles simpler
+// bit 0x1 << 27 (GP_LAND_ALLOW_SCRIPTS)
+// bit 0x1 << 16 (GP_LAND_VIEW_OWNED)
+// bit 0x1 << 41 (GP_ACCOUNTING_VIEW)
+// bit 0x1 << 46 (GP_PROPOSAL_VIEW)
+
+const U64 GP_NO_POWERS = 0x0;
+const U64 GP_ALL_POWERS = 0xFFFFFFFFFFFFFFFFLL;
+
+// Membership
+const U64 GP_MEMBER_INVITE = 0x1 << 1; // Invite member
+const U64 GP_MEMBER_EJECT = 0x1 << 2; // Eject member from group
+const U64 GP_MEMBER_OPTIONS = 0x1 << 3; // Toggle "Open enrollment" and change "Signup Fee"
+const U64 GP_MEMBER_VISIBLE_IN_DIR = 0x1LL << 47;
+
+// Roles
+const U64 GP_ROLE_CREATE = 0x1 << 4; // Create new roles
+const U64 GP_ROLE_DELETE = 0x1 << 5; // Delete roles
+const U64 GP_ROLE_PROPERTIES = 0x1 << 6; // Change Role Names, Titles, and Descriptions (Of roles the user is in, only, or any role in group?)
+const U64 GP_ROLE_ASSIGN_MEMBER_LIMITED = 0x1 << 7; // Assign Member to a Role that the assigner is in
+const U64 GP_ROLE_ASSIGN_MEMBER = 0x1 << 8; // Assign Member to Role
+const U64 GP_ROLE_REMOVE_MEMBER = 0x1 << 9; // Remove Member from Role
+const U64 GP_ROLE_CHANGE_ACTIONS = 0x1 << 10; // Change actions a role can perform
+
+// Group Identity
+const U64 GP_GROUP_CHANGE_IDENTITY = 0x1 << 11; // Charter, insignia, 'Show In Group List', 'Publish on the web', 'Mature', all 'Show Member In Group Profile' checkboxes
+
+// Parcel Management
+const U64 GP_LAND_DEED = 0x1 << 12; // Deed Land and Buy Land for Group
+const U64 GP_LAND_RELEASE = 0x1 << 13; // Release Land (to Gov. Linden)
+const U64 GP_LAND_SET_SALE_INFO = 0x1 << 14; // Set for sale info (Toggle "For Sale", Set Price, Set Target, Toggle "Sell objects with the land")
+const U64 GP_LAND_DIVIDE_JOIN = 0x1 << 15; // Divide and Join Parcels
+
+// Parcel Identity
+const U64 GP_LAND_FIND_PLACES = 0x1 << 17; // Toggle "Show in Find Places" and Set Category.
+const U64 GP_LAND_CHANGE_IDENTITY = 0x1 << 18; // Change Parcel Identity: Parcel Name, Parcel Description, Snapshot, 'Publish on the web', and 'Mature' checkbox
+const U64 GP_LAND_SET_LANDING_POINT = 0x1 << 19; // Set Landing Point
+
+// Parcel Settings
+const U64 GP_LAND_CHANGE_MEDIA = 0x1 << 20; // Change Media Settings
+const U64 GP_LAND_EDIT = 0x1 << 21; // Toggle Edit Land
+const U64 GP_LAND_OPTIONS = 0x1 << 22; // Toggle Set Home Point, Fly, Outside Scripts, Create/Edit Objects, Landmark, and Damage checkboxes
+
+// Parcel Powers
+const U64 GP_LAND_ALLOW_EDIT_LAND = 0x1 << 23; // Bypass Edit Land Restriction
+const U64 GP_LAND_ALLOW_FLY = 0x1 << 24; // Bypass Fly Restriction
+const U64 GP_LAND_ALLOW_CREATE = 0x1 << 25; // Bypass Create/Edit Objects Restriction
+const U64 GP_LAND_ALLOW_LANDMARK = 0x1 << 26; // Bypass Landmark Restriction
+const U64 GP_LAND_ALLOW_SET_HOME = 0x1 << 28; // Bypass Set Home Point Restriction
+
+// Parcel Access
+const U64 GP_LAND_MANAGE_ALLOWED = 0x1 << 29; // Manage Allowed List
+const U64 GP_LAND_MANAGE_BANNED = 0x1 << 30; // Manage Banned List
+const U64 GP_LAND_MANAGE_PASSES = 0x1LL << 31; // Change Sell Pass Settings
+const U64 GP_LAND_ADMIN = 0x1LL << 32; // Eject and Freeze Users on the land
+
+// Parcel Content
+const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48; // Return objects on parcel that are owned by the group
+const U64 GP_LAND_RETURN_GROUP_SET = 0x1LL << 33; // Return objects on parcel that are set to group
+const U64 GP_LAND_RETURN_NON_GROUP = 0x1LL << 34; // Return objects on parcel that are not set to group
+// Select a power-bit based on an object's relationship to a parcel.
+const U64 GP_LAND_RETURN = GP_LAND_RETURN_GROUP_OWNED
+ | GP_LAND_RETURN_GROUP_SET
+ | GP_LAND_RETURN_NON_GROUP;
+const U64 GP_LAND_GARDENING = 0x1LL << 35; // Parcel Gardening - plant and move linden trees
+
+// Object Management
+const U64 GP_OBJECT_DEED = 0x1LL << 36; // Deed Object
+// HOLE -- 0x1LL << 37
+const U64 GP_OBJECT_MANIPULATE = 0x1LL << 38; // Manipulate Group Owned Objects (Move, Copy, Mod)
+const U64 GP_OBJECT_SET_SALE = 0x1LL << 39; // Set Group Owned Object for Sale
+
+// Accounting
+const U64 GP_ACCOUNTING_ACCOUNTABLE = 0x1LL << 40; // Pay Group Liabilities and Receive Group Dividends
+
+// Notices
+const U64 GP_NOTICES_SEND = 0x1LL << 42; // Send Notices
+const U64 GP_NOTICES_RECEIVE = 0x1LL << 43; // Receive Notices and View Notice History
+
+// Proposals
+const U64 GP_PROPOSAL_START = 0x1LL << 44; // Start Proposal
+const U64 GP_PROPOSAL_VOTE = 0x1LL << 45; // Vote on Proposal
+
+const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE
+ | GP_LAND_ALLOW_SET_HOME
+ | GP_NOTICES_RECEIVE
+ | GP_PROPOSAL_START
+ | GP_PROPOSAL_VOTE
+ ;
+
+const U64 GP_DEFAULT_OFFICER = GP_ACCOUNTING_ACCOUNTABLE
+ | GP_GROUP_CHANGE_IDENTITY
+ | GP_LAND_ADMIN
+ | GP_LAND_ALLOW_EDIT_LAND
+ | GP_LAND_ALLOW_FLY
+ | GP_LAND_ALLOW_CREATE
+ | GP_LAND_ALLOW_LANDMARK
+ | GP_LAND_ALLOW_SET_HOME
+ | GP_LAND_CHANGE_IDENTITY
+ | GP_LAND_CHANGE_MEDIA
+ | GP_LAND_DEED
+ | GP_LAND_DIVIDE_JOIN
+ | GP_LAND_EDIT
+ | GP_LAND_FIND_PLACES
+ | GP_LAND_GARDENING
+ | GP_LAND_MANAGE_ALLOWED
+ | GP_LAND_MANAGE_BANNED
+ | GP_LAND_MANAGE_PASSES
+ | GP_LAND_OPTIONS
+ | GP_LAND_RELEASE
+ | GP_LAND_RETURN_GROUP_OWNED
+ | GP_LAND_RETURN_GROUP_SET
+ | GP_LAND_RETURN_NON_GROUP
+ | GP_LAND_SET_LANDING_POINT
+ | GP_LAND_SET_SALE_INFO
+ | GP_MEMBER_EJECT
+ | GP_MEMBER_INVITE
+ | GP_MEMBER_OPTIONS
+ | GP_MEMBER_VISIBLE_IN_DIR
+ | GP_NOTICES_RECEIVE
+ | GP_NOTICES_SEND
+ | GP_OBJECT_DEED
+ | GP_OBJECT_MANIPULATE
+ | GP_OBJECT_SET_SALE
+ | GP_PROPOSAL_START
+ | GP_PROPOSAL_VOTE
+ | GP_ROLE_ASSIGN_MEMBER_LIMITED
+ | GP_ROLE_PROPERTIES
+ ;
+#endif
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
new file mode 100644
index 0000000000..12fb932a0a
--- /dev/null
+++ b/indra/llcommon/stdenums.h
@@ -0,0 +1,115 @@
+/**
+ * @file stdenums.h
+ * @brief Enumerations for indra.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_STDENUMS_H
+#define LL_STDENUMS_H
+
+//----------------------------------------------------------------------------
+// DEPRECATED - create new, more specific files for shared enums/constants
+//----------------------------------------------------------------------------
+
+// this enum is used by the llview.h (viewer) and the llassetstorage.h (viewer and sim)
+enum EDragAndDropType
+{
+ DAD_NONE = 0,
+ DAD_TEXTURE = 1,
+ DAD_SOUND = 2,
+ DAD_CALLINGCARD = 3,
+ DAD_LANDMARK = 4,
+ DAD_SCRIPT = 5,
+ DAD_CLOTHING = 6,
+ DAD_OBJECT = 7,
+ DAD_NOTECARD = 8,
+ DAD_CATEGORY = 9,
+ DAD_ROOT_CATEGORY = 10,
+ DAD_BODYPART = 11,
+ DAD_ANIMATION = 12,
+ DAD_GESTURE = 13,
+ DAD_COUNT = 14, // number of types in this enum
+};
+
+// Reasons for drags to be denied.
+// ordered by priority for multi-drag
+enum EAcceptance
+{
+ ACCEPT_POSTPONED, // we are asynchronously determining acceptance
+ ACCEPT_NO, // Uninformative, general purpose denial.
+ ACCEPT_NO_LOCKED, // Operation would be valid, but permissions are set to disallow it.
+ ACCEPT_YES_COPY_SINGLE, // We'll take a copy of a single item
+ ACCEPT_YES_SINGLE, // Accepted. OK to drag and drop single item here.
+ ACCEPT_YES_COPY_MULTI, // We'll take a copy of multiple items
+ ACCEPT_YES_MULTI // Accepted. OK to drag and drop multiple items here.
+};
+
+// This is used by the DeRezObject message to determine where to put
+// derezed tasks.
+enum EDeRezDestination
+{
+ DRD_SAVE_INTO_AGENT_INVENTORY = 0,
+ DRD_ACQUIRE_TO_AGENT_INVENTORY = 1, // try to leave copy in world
+ DRD_SAVE_INTO_TASK_INVENTORY = 2,
+ DRD_ATTACHMENT = 3,
+ DRD_TAKE_INTO_AGENT_INVENTORY = 4, // delete from world
+ DRD_FORCE_TO_GOD_INVENTORY = 5, // force take copy
+ DRD_TRASH = 6,
+ DRD_ATTACHMENT_TO_INV = 7,
+ DRD_ATTACHMENT_EXISTS = 8,
+ DRD_RETURN_TO_OWNER = 9, // back to owner's inventory
+ DRD_RETURN_TO_LAST_OWNER = 10, // deeded object back to last owner's inventory
+
+ DRD_COUNT = 11
+};
+
+
+// This is used by the return to owner code to determine the reason
+// that this object is being returned.
+enum EReturnReason
+{
+ RR_GENERIC = 0,
+ RR_SANDBOX = 1,
+ RR_PARCEL_OWNER = 2,
+ RR_PARCEL_AUTO = 3,
+ RR_PARCEL_FULL = 4,
+ RR_OFF_WORLD = 5,
+
+ RR_COUNT = 6
+};
+
+// This is used for filling in the first byte of the ExtraID field of
+// the ObjectProperties message.
+enum EObjectPropertiesExtraID
+{
+ OPEID_NONE = 0,
+ OPEID_ASSET_ID = 1,
+ OPEID_FROM_TASK_ID = 2,
+
+ OPEID_COUNT = 3
+};
+
+enum EAddPosition
+{
+ ADD_TOP,
+ ADD_SORTED,
+ ADD_BOTTOM
+};
+
+enum LLGroupChange
+{
+ GC_PROPERTIES,
+ GC_MEMBER_DATA,
+ GC_ROLE_DATA,
+ GC_ROLE_MEMBER_DATA,
+ GC_TITLES,
+ GC_ALL
+};
+
+//----------------------------------------------------------------------------
+// DEPRECATED - create new, more specific files for shared enums/constants
+//----------------------------------------------------------------------------
+
+#endif
diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h
new file mode 100644
index 0000000000..b898475683
--- /dev/null
+++ b/indra/llcommon/stdtypes.h
@@ -0,0 +1,89 @@
+/**
+ * @file stdtypes.h
+ * @brief Basic type declarations for cross platform compatibility.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+#ifndef LL_STDTYPES_H
+#define LL_STDTYPES_H
+
+#include <limits.h>
+#include <float.h>
+
+typedef signed char S8;
+typedef unsigned char U8;
+typedef signed short S16;
+typedef unsigned short U16;
+typedef signed int S32;
+typedef unsigned int U32;
+
+#if LL_WINDOWS
+// Windows wchar_t is 16-bit
+typedef U32 llwchar;
+#else
+typedef wchar_t llwchar;
+#endif
+
+#if LL_WINDOWS
+typedef signed __int64 S64;
+// probably should be 'hyper' or similiar
+#define S64L(a) (a)
+typedef unsigned __int64 U64;
+#define U64L(a) (a)
+#else
+typedef long long int S64;
+typedef long long unsigned int U64;
+#if LL_DARWIN || LL_LINUX
+#define S64L(a) (a##LL)
+#define U64L(a) (a##ULL)
+#endif
+#endif
+
+typedef float F32;
+typedef double F64;
+
+typedef S32 BOOL;
+typedef U8 KEY;
+typedef U32 MASK;
+typedef U32 TPACKETID;
+
+// Use #define instead of consts to avoid conversion headaches
+#define S8_MAX (SCHAR_MAX)
+#define U8_MAX (UCHAR_MAX)
+#define S16_MAX (SHRT_MAX)
+#define U16_MAX (USHRT_MAX)
+#define S32_MAX (INT_MAX)
+#define U32_MAX (UINT_MAX)
+#define F32_MAX (FLT_MAX)
+#define F64_MAX (DBL_MAX)
+
+#define S8_MIN (SCHAR_MIN)
+#define U8_MIN (0)
+#define S16_MIN (SHRT_MIN)
+#define U16_MIN (0)
+#define S32_MIN (INT_MIN)
+#define U32_MIN (0)
+#define F32_MIN (FLT_MIN)
+#define F64_MIN (DBL_MIN)
+
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+typedef U8 LLPCode;
+
+#if LL_LINUX && __GNUC__ <= 2
+typedef int intptr_t;
+#endif
+
+#endif
diff --git a/indra/llcommon/string_table.h b/indra/llcommon/string_table.h
new file mode 100644
index 0000000000..02ea4c34aa
--- /dev/null
+++ b/indra/llcommon/string_table.h
@@ -0,0 +1,8 @@
+/**
+ * @file string_table.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+#include "llstringtable.h"
diff --git a/indra/llcommon/timer.h b/indra/llcommon/timer.h
new file mode 100644
index 0000000000..e4b799ee30
--- /dev/null
+++ b/indra/llcommon/timer.h
@@ -0,0 +1,8 @@
+/**
+ * @file timer.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+#include "lltimer.h"
diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp
new file mode 100644
index 0000000000..2c8a214b01
--- /dev/null
+++ b/indra/llcommon/timing.cpp
@@ -0,0 +1,7 @@
+/**
+ * @file timing.cpp
+ * @brief This file will be deprecated in the future.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h
new file mode 100644
index 0000000000..f09fb0f92b
--- /dev/null
+++ b/indra/llcommon/timing.h
@@ -0,0 +1,26 @@
+/**
+ * @file timing.h
+ * @brief Cross-platform routines for doing timing.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TIMING_H
+#define LL_TIMING_H
+
+#include <time.h>
+
+#if LL_LINUX || LL_DARWIN
+# include <sys/time.h>
+#endif
+
+
+const F32 SEC_TO_MICROSEC = 1000000.f;
+const U64 SEC_TO_MICROSEC_U64 = 1000000;
+const U32 SEC_PER_DAY = 86400;
+
+// This is just a stub, implementation in lltimer.cpp. This file will be deprecated in the future.
+U64 totalTime(); // Returns current system time in microseconds
+
+#endif
diff --git a/indra/llcommon/u64.cpp b/indra/llcommon/u64.cpp
new file mode 100644
index 0000000000..c8b8bc4a28
--- /dev/null
+++ b/indra/llcommon/u64.cpp
@@ -0,0 +1,91 @@
+/**
+ * @file u64.cpp
+ * @brief Utilities to deal with U64s.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "u64.h"
+
+
+U64 str_to_U64(const char *str)
+{
+ U64 result = 0;
+ char *aptr = strpbrk(str,"0123456789");
+
+ if (!aptr)
+ {
+ llwarns << "str_to_U64: Bad string to U64 conversion attempt: format\n" << llendl;
+ }
+ else
+ {
+ while ((*aptr >= '0') && (*aptr <= '9'))
+ {
+ result = result*10 + (*aptr++ - '0');
+ }
+ }
+ return (result);
+}
+
+
+char* U64_to_str(U64 value, char* result, S32 result_size)
+{
+ U32 part1,part2,part3;
+
+ part3 = (U32)(value % (U64)10000000);
+
+ value /= 10000000;
+ part2 = (U32)(value % (U64)10000000);
+
+ value /= 10000000;
+ part1 = (U32)(value % (U64)10000000);
+
+ // three cases to avoid leading zeroes unless necessary
+
+ if (part1)
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u%07u%07u",
+ part1,part2,part3); /* Flawfinder: ignore */
+ }
+ else if (part2)
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u%07u",
+ part2,part3); /* Flawfinder: ignore */
+ }
+ else
+ {
+ snprintf(
+ result,
+ result_size,
+ "%u",
+ part3); /* Flawfinder: ignore */
+ }
+ return (result);
+}
+
+F64 U64_to_F64(const U64 value)
+{
+ S64 top_bits = (S64)(value >> 1);
+ F64 result = (F64)top_bits;
+ result *= 2.f;
+ result += (U32)(value & 0x01);
+ return result;
+}
+
+U64 llstrtou64(const char* str, char** end, S32 base)
+{
+#ifdef LL_WINDOWS
+ return _strtoui64(str,end,base);
+#else
+ return strtoull(str,end,base);
+#endif
+}
diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h
new file mode 100644
index 0000000000..e659be189f
--- /dev/null
+++ b/indra/llcommon/u64.h
@@ -0,0 +1,19 @@
+/**
+ * @file u64.h
+ * @brief Utilities to deal with U64s.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_U64_H
+#define LL_U64_H
+
+U64 str_to_U64(const char* str);
+char* U64_to_str(U64 value, char* result, S32 result_size);
+
+F64 U64_to_F64(const U64 value);
+
+U64 llstrtou64(const char* str, char** end, S32 base);
+
+#endif
diff --git a/indra/llimage/llimage.cpp b/indra/llimage/llimage.cpp
new file mode 100644
index 0000000000..89b4a6d1cc
--- /dev/null
+++ b/indra/llimage/llimage.cpp
@@ -0,0 +1,1772 @@
+/**
+ * @file llimage.cpp
+ * @brief Base class for images.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <algorithm>
+#include <iostream>
+
+#include "llmath.h"
+#include "stdtypes.h"
+#include "v4coloru.h"
+#include "llmemory.h"
+
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagej2c.h"
+#if JPEG_SUPPORT
+#include "llimagejpeg.h"
+#endif
+#include "llimagedxt.h"
+
+//---------------------------------------------------------------------------
+// LLImageBase
+//---------------------------------------------------------------------------
+
+LLImageBase::LLImageBase()
+ : mData(NULL),
+ mDataSize(0),
+ mWidth(0),
+ mHeight(0),
+ mComponents(0),
+ mMemType(LLMemType::MTYPE_IMAGEBASE)
+{
+}
+
+// virtual
+LLImageBase::~LLImageBase()
+{
+ deleteData(); // virtual
+}
+
+// virtual
+void LLImageBase::dump()
+{
+ llinfos << "LLImageBase mComponents " << mComponents
+ << " mData " << mData
+ << " mDataSize " << mDataSize
+ << " mWidth " << mWidth
+ << " mHeight " << mHeight
+ << llendl;
+}
+
+// virtual
+void LLImageBase::sanityCheck()
+{
+ if (mWidth > MAX_IMAGE_SIZE
+ || mHeight > MAX_IMAGE_SIZE
+ || mDataSize > (S32)MAX_IMAGE_DATA_SIZE
+ || mComponents > (S8)MAX_IMAGE_COMPONENTS
+ )
+ {
+ llerrs << "Failed LLImageBase::sanityCheck "
+ << "width " << mWidth
+ << "height " << mHeight
+ << "datasize " << mDataSize
+ << "components " << mComponents
+ << "data " << mData
+ << llendl;
+ }
+}
+
+LLString LLImageBase::sLastErrorMessage;
+BOOL LLImageBase::sSizeOverride = FALSE;
+
+BOOL LLImageBase::setLastError(const LLString& message, const LLString& filename)
+{
+ sLastErrorMessage = message;
+ if (filename != "")
+ {
+ sLastErrorMessage += LLString(" FILE:");
+ sLastErrorMessage += filename;
+ }
+ llwarns << sLastErrorMessage << llendl;
+ return FALSE;
+}
+
+// virtual
+void LLImageBase::deleteData()
+{
+ delete[] mData;
+ mData = NULL;
+ mDataSize = 0;
+}
+
+// virtual
+U8* LLImageBase::allocateData(S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+
+ if (size < 0)
+ {
+ size = mWidth * mHeight * mComponents;
+ if (size <= 0)
+ {
+ llerrs << llformat("LLImageBase::allocateData called with bad dimentions: %dx%dx%d",mWidth,mHeight,mComponents) << llendl;
+ }
+ }
+ else if ((size <= 0 || size > 4096*4096*16) && sSizeOverride == FALSE)
+ {
+ llerrs << "LLImageBase::allocateData: bad size: " << size << llendl;
+ }
+
+ resetLastError();
+
+ if (!mData || size != mDataSize)
+ {
+ deleteData(); // virtual
+ mData = new U8[size];
+ if (!mData)
+ {
+ llerrs << "allocate image data: " << size << llendl;
+ }
+ mDataSize = size;
+ }
+
+ return mData;
+}
+
+// virtual
+U8* LLImageBase::reallocateData(S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ U8 *new_datap = new U8[size];
+ if (!new_datap)
+ {
+ llwarns << "Out of memory in LLImageBase::reallocateData" << llendl;
+ return 0;
+ }
+ if (mData)
+ {
+ S32 bytes = llmin(mDataSize, size);
+ memcpy(new_datap, mData, bytes);
+ delete[] mData;
+ }
+ mData = new_datap;
+ mDataSize = size;
+ return mData;
+}
+
+
+void LLImageBase::setSize(S32 width, S32 height, S32 ncomponents)
+{
+ mWidth = width;
+ mHeight = height;
+ mComponents = ncomponents;
+}
+
+U8* LLImageBase::allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size)
+{
+ setSize(width, height, ncomponents);
+ return allocateData(size); // virtual
+}
+
+//---------------------------------------------------------------------------
+// LLImageRaw
+//---------------------------------------------------------------------------
+
+S32 LLImageRaw::sGlobalRawMemory = 0;
+S32 LLImageRaw::sRawImageCount = 0;
+
+LLImageRaw::LLImageRaw()
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(U16 width, U16 height, S8 components)
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ llassert( S32(width) * S32(height) * S32(components) <= MAX_IMAGE_DATA_SIZE );
+ allocateDataSize(width, height, components);
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(U8 *data, U16 width, U16 height, S8 components)
+ : LLImageBase()
+{
+ mMemType = LLMemType::MTYPE_IMAGERAW;
+ copyData(data, width, height, components);
+ ++sRawImageCount;
+}
+
+LLImageRaw::LLImageRaw(const LLString &filename, bool j2c_lowest_mip_only)
+ : LLImageBase()
+{
+ createFromFile(filename, j2c_lowest_mip_only);
+}
+
+LLImageRaw::~LLImageRaw()
+{
+ // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
+ // NOT LLImageRaw::deleteData()
+ deleteData();
+ --sRawImageCount;
+}
+
+// virtual
+U8* LLImageRaw::allocateData(S32 size)
+{
+ U8* res = LLImageBase::allocateData(size);
+ sGlobalRawMemory += getDataSize();
+ return res;
+}
+
+// virtual
+U8* LLImageRaw::reallocateData(S32 size)
+{
+ sGlobalRawMemory -= getDataSize();
+ U8* res = LLImageBase::reallocateData(size);
+ sGlobalRawMemory += getDataSize();
+ return res;
+}
+
+// virtual
+void LLImageRaw::deleteData()
+{
+ sGlobalRawMemory -= getDataSize();
+ LLImageBase::deleteData();
+}
+
+BOOL LLImageRaw::copyData(U8 *data, U16 width, U16 height, S8 components)
+{
+ if (!resize(width, height, components))
+ {
+ return FALSE;
+ }
+ memcpy(getData(), data, width*height*components);
+ return TRUE;
+}
+
+BOOL LLImageRaw::resize(U16 width, U16 height, S8 components)
+{
+ if ((getWidth() == width) && (getHeight() == height) && (getComponents() == components))
+ {
+ return TRUE;
+ }
+ // Reallocate the data buffer.
+ deleteData();
+
+ allocateDataSize(width,height,components);
+
+ return TRUE;
+}
+
+U8 * LLImageRaw::getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ U8 *data = new U8[width*height*getComponents()];
+
+ // Should do some simple bounds checking
+
+ U32 i;
+ for (i = y_pos; i < y_pos+height; i++)
+ {
+ memcpy(data + i*width*getComponents(),
+ getData() + ((y_pos + i)*getWidth() + x_pos)*getComponents(), getComponents()*width);
+ }
+ return data;
+}
+
+BOOL LLImageRaw::setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
+ const U8 *data, U32 stride, BOOL reverse_y)
+{
+ if (!getData())
+ {
+ return FALSE;
+ }
+ if (!data)
+ {
+ return FALSE;
+ }
+
+ // Should do some simple bounds checking
+
+ U32 i;
+ U32 to_offset;
+ U32 from_offset;
+ if (!reverse_y)
+ {
+ for (i = 0; i < height; i++)
+ {
+ to_offset = (y_pos + i)*getWidth() + x_pos;
+ if (stride != 0)
+ {
+ from_offset = i*stride;
+ }
+ else
+ {
+ from_offset = i*width*getComponents();
+ }
+ memcpy(getData() + to_offset*getComponents(),
+ data + from_offset, getComponents()*width);
+ }
+ }
+ else
+ {
+ for (i = 0; i < height; i++)
+ {
+ to_offset = (y_pos + i)*getWidth() + x_pos;
+ if (stride != 0)
+ {
+ from_offset = (height - 1 - i)*stride;
+ }
+ else
+ {
+ from_offset = (height - 1 - i)*width*getComponents();
+ }
+ memcpy(getData() + to_offset*getComponents(),
+ data + from_offset, getComponents()*width);
+ }
+ }
+ return TRUE;
+}
+
+void LLImageRaw::clear(U8 r, U8 g, U8 b, U8 a)
+{
+ llassert( getComponents() <= 4 );
+ // This is fairly bogus, but it'll do for now.
+ U8 *pos = getData();
+ U32 x, y;
+ for (x = 0; x < getWidth(); x++)
+ {
+ for (y = 0; y < getHeight(); y++)
+ {
+ *pos = r;
+ pos++;
+ if (getComponents() == 1)
+ {
+ continue;
+ }
+ *pos = g;
+ pos++;
+ if (getComponents() == 2)
+ {
+ continue;
+ }
+ *pos = b;
+ pos++;
+ if (getComponents() == 3)
+ {
+ continue;
+ }
+ *pos = a;
+ pos++;
+ }
+ }
+}
+
+// Reverses the order of the rows in the image
+void LLImageRaw::verticalFlip()
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ S32 row_bytes = getWidth() * getComponents();
+ U8* line_buffer = new U8[row_bytes];
+ S32 mid_row = getHeight() / 2;
+ for( S32 row = 0; row < mid_row; row++ )
+ {
+ U8* row_a_data = getData() + row * row_bytes;
+ U8* row_b_data = getData() + (getHeight() - 1 - row) * row_bytes;
+ memcpy( line_buffer, row_a_data, row_bytes );
+ memcpy( row_a_data, row_b_data, row_bytes );
+ memcpy( row_b_data, line_buffer, row_bytes );
+ }
+ delete[] line_buffer;
+}
+
+
+void LLImageRaw::expandToPowerOfTwo(S32 max_dim, BOOL scale_image)
+{
+ // Find new sizes
+ S32 new_width = MIN_IMAGE_SIZE;
+ S32 new_height = MIN_IMAGE_SIZE;
+
+ while( (new_width < getWidth()) && (new_width < max_dim) )
+ {
+ new_width <<= 1;
+ }
+
+ while( (new_height < getHeight()) && (new_height < max_dim) )
+ {
+ new_height <<= 1;
+ }
+
+ scale( new_width, new_height, scale_image );
+}
+
+void LLImageRaw::contractToPowerOfTwo(S32 max_dim, BOOL scale_image)
+{
+ // Find new sizes
+ S32 new_width = max_dim;
+ S32 new_height = max_dim;
+
+ while( (new_width > getWidth()) && (new_width > MIN_IMAGE_SIZE) )
+ {
+ new_width >>= 1;
+ }
+
+ while( (new_height > getHeight()) && (new_height > MIN_IMAGE_SIZE) )
+ {
+ new_height >>= 1;
+ }
+
+ scale( new_width, new_height, scale_image );
+}
+
+void LLImageRaw::biasedScaleToPowerOfTwo(S32 max_dim)
+{
+ // Strong bias towards rounding down (to save bandwidth)
+ // No bias would mean THRESHOLD == 1.5f;
+ const F32 THRESHOLD = 1.75f;
+
+ // Find new sizes
+ S32 larger_w = max_dim; // 2^n >= mWidth
+ S32 smaller_w = max_dim; // 2^(n-1) <= mWidth
+ while( (smaller_w > getWidth()) && (smaller_w > MIN_IMAGE_SIZE) )
+ {
+ larger_w = smaller_w;
+ smaller_w >>= 1;
+ }
+ S32 new_width = ( (F32)getWidth() / smaller_w > THRESHOLD ) ? larger_w : smaller_w;
+
+
+ S32 larger_h = max_dim; // 2^m >= mHeight
+ S32 smaller_h = max_dim; // 2^(m-1) <= mHeight
+ while( (smaller_h > getHeight()) && (smaller_h > MIN_IMAGE_SIZE) )
+ {
+ larger_h = smaller_h;
+ smaller_h >>= 1;
+ }
+ S32 new_height = ( (F32)getHeight() / smaller_h > THRESHOLD ) ? larger_h : smaller_h;
+
+
+ scale( new_width, new_height );
+}
+
+
+
+
+// Calculates (U8)(255*(a/255.f)*(b/255.f) + 0.5f). Thanks, Jim Blinn!
+inline U8 LLImageRaw::fastFractionalMult( U8 a, U8 b )
+{
+ U32 i = a * b + 128;
+ return U8((i + (i>>8)) >> 8);
+}
+
+
+void LLImageRaw::composite( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (3 == dst->getComponents()) || (4 == dst->getComponents()) );
+
+ if( 3 == dst->getComponents() )
+ {
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ // No scaling needed
+ if( 3 == src->getComponents() )
+ {
+ copyUnscaled( src ); // alpha is one so just copy the data.
+ }
+ else
+ {
+ compositeUnscaled4onto3( src );
+ }
+ }
+ else
+ {
+ if( 3 == src->getComponents() )
+ {
+ copyScaled( src ); // alpha is one so just copy the data.
+ }
+ else
+ {
+ compositeScaled4onto3( src );
+ }
+ }
+ }
+ else
+ {
+ // 4 == dst->mComponents
+ llassert(0); // not implemented yet.
+ }
+}
+
+// Src and dst can be any size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::compositeScaled4onto3(LLImageRaw* src)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ llinfos << "compositeScaled4onto3" << llendl;
+
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (4 == src->getComponents()) && (3 == dst->getComponents()) );
+
+ // Vertical: scale but no composite
+ S32 temp_data_size = src->getWidth() * dst->getHeight() * src->getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < src->getWidth(); col++ )
+ {
+ copyLineScaled( src->getData() + (src->getComponents() * col), temp_buffer + (src->getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() );
+ }
+
+ // Horizontal: scale and composite
+ for( S32 row = 0; row < dst->getHeight(); row++ )
+ {
+ compositeRowScaled4onto3( temp_buffer + (src->getComponents() * src->getWidth() * row), dst->getData() + (dst->getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth() );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+}
+
+
+// Src and dst are same size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::compositeUnscaled4onto3( LLImageRaw* src )
+{
+ /*
+ //test fastFractionalMult()
+ {
+ U8 i = 255;
+ U8 j = 255;
+ do
+ {
+ do
+ {
+ llassert( fastFractionalMult(i, j) == (U8)(255*(i/255.f)*(j/255.f) + 0.5f) );
+ } while( j-- );
+ } while( i-- );
+ }
+ */
+
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ S32 pixels = getWidth() * getHeight();
+ while( pixels-- )
+ {
+ U8 alpha = src_data[3];
+ if( alpha )
+ {
+ if( 255 == alpha )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ }
+ else
+ {
+
+ U8 transparency = 255 - alpha;
+ dst_data[0] = fastFractionalMult( dst_data[0], transparency ) + fastFractionalMult( src_data[0], alpha );
+ dst_data[1] = fastFractionalMult( dst_data[1], transparency ) + fastFractionalMult( src_data[1], alpha );
+ dst_data[2] = fastFractionalMult( dst_data[2], transparency ) + fastFractionalMult( src_data[2], alpha );
+ }
+ }
+
+ src_data += 4;
+ dst_data += 3;
+ }
+}
+
+// Fill the buffer with a constant color
+void LLImageRaw::fill( const LLColor4U& color )
+{
+ S32 pixels = getWidth() * getHeight();
+ if( 4 == getComponents() )
+ {
+ U32* data = (U32*) getData();
+ for( S32 i = 0; i < pixels; i++ )
+ {
+ data[i] = color.mAll;
+ }
+ }
+ else
+ if( 3 == getComponents() )
+ {
+ U8* data = getData();
+ for( S32 i = 0; i < pixels; i++ )
+ {
+ data[0] = color.mV[0];
+ data[1] = color.mV[1];
+ data[2] = color.mV[2];
+ data += 3;
+ }
+ }
+}
+
+
+
+
+// Src and dst can be any size. Src and dst can each have 3 or 4 components.
+void LLImageRaw::copy(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( (3 == dst->getComponents()) || (4 == dst->getComponents()) );
+
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ // No scaling needed
+ if( src->getComponents() == dst->getComponents() )
+ {
+ copyUnscaled( src );
+ }
+ else
+ if( 3 == src->getComponents() )
+ {
+ copyUnscaled3onto4( src );
+ }
+ else
+ {
+ // 4 == src->getComponents()
+ copyUnscaled4onto3( src );
+ }
+ }
+ else
+ {
+ // Scaling needed
+ // No scaling needed
+ if( src->getComponents() == dst->getComponents() )
+ {
+ copyScaled( src );
+ }
+ else
+ if( 3 == src->getComponents() )
+ {
+ copyScaled3onto4( src );
+ }
+ else
+ {
+ // 4 == src->getComponents()
+ copyScaled4onto3( src );
+ }
+ }
+}
+
+// Src and dst are same size. Src and dst have same number of components.
+void LLImageRaw::copyUnscaled(LLImageRaw* src)
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( src->getComponents() == dst->getComponents() );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() );
+}
+
+
+// Src and dst can be any size. Src has 3 components. Dst has 4 components.
+void LLImageRaw::copyScaled3onto4(LLImageRaw* src)
+{
+ llassert( (3 == src->getComponents()) && (4 == getComponents()) );
+
+ // Slow, but simple. Optimize later if needed.
+ LLImageRaw temp( src->getWidth(), src->getHeight(), 4);
+ temp.copyUnscaled3onto4( src );
+ copyScaled( &temp );
+}
+
+
+// Src and dst can be any size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::copyScaled4onto3(LLImageRaw* src)
+{
+ llassert( (4 == src->getComponents()) && (3 == getComponents()) );
+
+ // Slow, but simple. Optimize later if needed.
+ LLImageRaw temp( src->getWidth(), src->getHeight(), 3);
+ temp.copyUnscaled4onto3( src );
+ copyScaled( &temp );
+}
+
+
+// Src and dst are same size. Src has 4 components. Dst has 3 components.
+void LLImageRaw::copyUnscaled4onto3( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == dst->getComponents()) && (4 == src->getComponents()) );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ S32 pixels = getWidth() * getHeight();
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ for( S32 i=0; i<pixels; i++ )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ src_data += 4;
+ dst_data += 3;
+ }
+}
+
+
+// Src and dst are same size. Src has 3 components. Dst has 4 components.
+void LLImageRaw::copyUnscaled3onto4( LLImageRaw* src )
+{
+ LLImageRaw* dst = this; // Just for clarity.
+ llassert( 3 == src->getComponents() );
+ llassert( 4 == dst->getComponents() );
+ llassert( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) );
+
+ S32 pixels = getWidth() * getHeight();
+ U8* src_data = src->getData();
+ U8* dst_data = dst->getData();
+ for( S32 i=0; i<pixels; i++ )
+ {
+ dst_data[0] = src_data[0];
+ dst_data[1] = src_data[1];
+ dst_data[2] = src_data[2];
+ dst_data[3] = 255;
+ src_data += 3;
+ dst_data += 4;
+ }
+}
+
+
+// Src and dst can be any size. Src and dst have same number of components.
+void LLImageRaw::copyScaled( LLImageRaw* src )
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ LLImageRaw* dst = this; // Just for clarity.
+
+ llassert( (3 == src->getComponents()) || (4 == src->getComponents()) );
+ llassert( src->getComponents() == dst->getComponents() );
+
+ if( (src->getWidth() == dst->getWidth()) && (src->getHeight() == dst->getHeight()) )
+ {
+ memcpy( dst->getData(), src->getData(), getWidth() * getHeight() * getComponents() );
+ return;
+ }
+
+ // Vertical
+ S32 temp_data_size = src->getWidth() * dst->getHeight() * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < src->getWidth(); col++ )
+ {
+ copyLineScaled( src->getData() + (getComponents() * col), temp_buffer + (getComponents() * col), src->getHeight(), dst->getHeight(), src->getWidth(), src->getWidth() );
+ }
+
+ // Horizontal
+ for( S32 row = 0; row < dst->getHeight(); row++ )
+ {
+ copyLineScaled( temp_buffer + (getComponents() * src->getWidth() * row), dst->getData() + (getComponents() * dst->getWidth() * row), src->getWidth(), dst->getWidth(), 1, 1 );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+}
+
+
+void LLImageRaw::scale( S32 new_width, S32 new_height, BOOL scale_image_data )
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ llassert( (3 == getComponents()) || (4 == getComponents()) );
+
+ S32 old_width = getWidth();
+ S32 old_height = getHeight();
+
+ if( (old_width == new_width) && (old_height == new_height) )
+ {
+ return; // Nothing to do.
+ }
+
+ // Reallocate the data buffer.
+
+ if (scale_image_data)
+ {
+ // Vertical
+ S32 temp_data_size = old_width * new_height * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ for( S32 col = 0; col < old_width; col++ )
+ {
+ copyLineScaled( getData() + (getComponents() * col), temp_buffer + (getComponents() * col), old_height, new_height, old_width, old_width );
+ }
+
+ deleteData();
+
+ U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
+
+ // Horizontal
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ copyLineScaled( temp_buffer + (getComponents() * old_width * row), new_buffer + (getComponents() * new_width * row), old_width, new_width, 1, 1 );
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+ }
+ else
+ {
+ // copy out existing image data
+ S32 temp_data_size = old_width * old_height * getComponents();
+ U8* temp_buffer = new U8[ temp_data_size ];
+ memcpy(temp_buffer, getData(), temp_data_size);
+
+ // allocate new image data, will delete old data
+ U8* new_buffer = allocateDataSize(new_width, new_height, getComponents());
+
+ for( S32 row = 0; row < new_height; row++ )
+ {
+ if (row < old_height)
+ {
+ memcpy(new_buffer + (new_width * row * getComponents()), temp_buffer + (old_width * row * getComponents()), getComponents() * llmin(old_width, new_width));
+ if (old_width < new_width)
+ {
+ // pad out rest of row with black
+ memset(new_buffer + (getComponents() * ((new_width * row) + old_width)), 0, getComponents() * (new_width - old_width));
+ }
+ }
+ else
+ {
+ // pad remaining rows with black
+ memset(new_buffer + (new_width * row * getComponents()), 0, new_width * getComponents());
+ }
+ }
+
+ // Clean up
+ delete[] temp_buffer;
+ }
+}
+
+void LLImageRaw::copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step )
+{
+ const S32 components = getComponents();
+ llassert( components >= 1 && components <= 4 );
+
+ const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new
+ const F32 norm_factor = 1.f / ratio;
+
+ S32 goff = components >= 2 ? 1 : 0;
+ S32 boff = components >= 3 ? 2 : 0;
+ for( S32 x = 0; x < out_pixel_len; x++ )
+ {
+ // Sample input pixels in range from sample0 to sample1.
+ // Avoid floating point accumulation error... don't just add ratio each time. JC
+ const F32 sample0 = x * ratio;
+ const F32 sample1 = (x+1) * ratio;
+ const S32 index0 = llfloor(sample0); // left integer (floor)
+ const S32 index1 = llfloor(sample1); // right integer (floor)
+ const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left
+ const F32 fract1 = sample1 - F32(index1); // spill-over on right
+
+ if( index0 == index1 )
+ {
+ // Interval is embedded in one input pixel
+ S32 t0 = x * out_pixel_step * components;
+ S32 t1 = index0 * in_pixel_step * components;
+ U8* outp = out + t0;
+ U8* inp = in + t1;
+ for (S32 i = 0; i < components; ++i)
+ {
+ *outp = *inp;
+ ++outp;
+ ++inp;
+ }
+ }
+ else
+ {
+ // Left straddle
+ S32 t1 = index0 * in_pixel_step * components;
+ F32 r = in[t1 + 0] * fract0;
+ F32 g = in[t1 + goff] * fract0;
+ F32 b = in[t1 + boff] * fract0;
+ F32 a = 0;
+ if( components == 4)
+ {
+ a = in[t1 + 3] * fract0;
+ }
+
+ // Central interval
+ if (components < 4)
+ {
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * in_pixel_step * components;
+ r += in[t2 + 0];
+ g += in[t2 + goff];
+ b += in[t2 + boff];
+ }
+ }
+ else
+ {
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * in_pixel_step * components;
+ r += in[t2 + 0];
+ g += in[t2 + 1];
+ b += in[t2 + 2];
+ a += in[t2 + 3];
+ }
+ }
+
+ // right straddle
+ // Watch out for reading off of end of input array.
+ if( fract1 && index1 < in_pixel_len )
+ {
+ S32 t3 = index1 * in_pixel_step * components;
+ if (components < 4)
+ {
+ U8 in0 = in[t3 + 0];
+ U8 in1 = in[t3 + goff];
+ U8 in2 = in[t3 + boff];
+ r += in0 * fract1;
+ g += in1 * fract1;
+ b += in2 * fract1;
+ }
+ else
+ {
+ U8 in0 = in[t3 + 0];
+ U8 in1 = in[t3 + 1];
+ U8 in2 = in[t3 + 2];
+ U8 in3 = in[t3 + 3];
+ r += in0 * fract1;
+ g += in1 * fract1;
+ b += in2 * fract1;
+ a += in3 * fract1;
+ }
+ }
+
+ r *= norm_factor;
+ g *= norm_factor;
+ b *= norm_factor;
+ a *= norm_factor; // skip conditional
+
+ S32 t4 = x * out_pixel_step * components;
+ out[t4 + 0] = U8(llround(r));
+ if (components >= 2)
+ out[t4 + 1] = U8(llround(g));
+ if (components >= 3)
+ out[t4 + 2] = U8(llround(b));
+ if( components == 4)
+ out[t4 + 3] = U8(llround(a));
+ }
+ }
+}
+
+void LLImageRaw::compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len )
+{
+ llassert( getComponents() == 3 );
+
+ const S32 IN_COMPONENTS = 4;
+ const S32 OUT_COMPONENTS = 3;
+
+ const F32 ratio = F32(in_pixel_len) / out_pixel_len; // ratio of old to new
+ const F32 norm_factor = 1.f / ratio;
+
+ for( S32 x = 0; x < out_pixel_len; x++ )
+ {
+ // Sample input pixels in range from sample0 to sample1.
+ // Avoid floating point accumulation error... don't just add ratio each time. JC
+ const F32 sample0 = x * ratio;
+ const F32 sample1 = (x+1) * ratio;
+ const S32 index0 = S32(sample0); // left integer (floor)
+ const S32 index1 = S32(sample1); // right integer (floor)
+ const F32 fract0 = 1.f - (sample0 - F32(index0)); // spill over on left
+ const F32 fract1 = sample1 - F32(index1); // spill-over on right
+
+ U8 in_scaled_r;
+ U8 in_scaled_g;
+ U8 in_scaled_b;
+ U8 in_scaled_a;
+
+ if( index0 == index1 )
+ {
+ // Interval is embedded in one input pixel
+ S32 t1 = index0 * IN_COMPONENTS;
+ in_scaled_r = in[t1 + 0];
+ in_scaled_g = in[t1 + 0];
+ in_scaled_b = in[t1 + 0];
+ in_scaled_a = in[t1 + 0];
+ }
+ else
+ {
+ // Left straddle
+ S32 t1 = index0 * IN_COMPONENTS;
+ F32 r = in[t1 + 0] * fract0;
+ F32 g = in[t1 + 1] * fract0;
+ F32 b = in[t1 + 2] * fract0;
+ F32 a = in[t1 + 3] * fract0;
+
+ // Central interval
+ for( S32 u = index0 + 1; u < index1; u++ )
+ {
+ S32 t2 = u * IN_COMPONENTS;
+ r += in[t2 + 0];
+ g += in[t2 + 1];
+ b += in[t2 + 2];
+ a += in[t2 + 3];
+ }
+
+ // right straddle
+ // Watch out for reading off of end of input array.
+ if( fract1 && index1 < in_pixel_len )
+ {
+ S32 t3 = index1 * IN_COMPONENTS;
+ r += in[t3 + 0] * fract1;
+ g += in[t3 + 1] * fract1;
+ b += in[t3 + 2] * fract1;
+ a += in[t3 + 3] * fract1;
+ }
+
+ r *= norm_factor;
+ g *= norm_factor;
+ 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));
+ }
+
+ if( in_scaled_a )
+ {
+ if( 255 == in_scaled_a )
+ {
+ out[0] = in_scaled_r;
+ out[1] = in_scaled_g;
+ out[2] = in_scaled_b;
+ }
+ else
+ {
+ U8 transparency = 255 - in_scaled_a;
+ out[0] = fastFractionalMult( out[0], transparency ) + fastFractionalMult( in_scaled_r, in_scaled_a );
+ out[1] = fastFractionalMult( out[1], transparency ) + fastFractionalMult( in_scaled_g, in_scaled_a );
+ out[2] = fastFractionalMult( out[2], transparency ) + fastFractionalMult( in_scaled_b, in_scaled_a );
+ }
+ }
+ out += OUT_COMPONENTS;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+static struct
+{
+ const char* exten;
+ S8 codec;
+}
+file_extensions[] =
+{
+ { "bmp", IMG_CODEC_BMP },
+ { "tga", IMG_CODEC_TGA },
+ { "j2c", IMG_CODEC_J2C },
+ { "jp2", IMG_CODEC_J2C },
+ { "texture", IMG_CODEC_J2C },
+ { "jpg", IMG_CODEC_JPEG },
+ { "jpeg", IMG_CODEC_JPEG },
+ { "mip", IMG_CODEC_DXT },
+ { "dxt", IMG_CODEC_DXT }
+};
+#define NUM_FILE_EXTENSIONS sizeof(file_extensions)/sizeof(file_extensions[0])
+
+static LLString find_file(LLString &name, S8 *codec)
+{
+ LLString tname;
+ for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
+ {
+ tname = name + "." + LLString(file_extensions[i].exten);
+ llifstream ifs(tname.c_str(), llifstream::binary);
+ if (ifs.is_open())
+ {
+ ifs.close();
+ if (codec)
+ *codec = file_extensions[i].codec;
+ return LLString(file_extensions[i].exten);
+ }
+ }
+ return LLString("");
+}
+
+static S8 get_codec(const LLString& exten)
+{
+ for (int i=0; i<(int)(NUM_FILE_EXTENSIONS); i++)
+ {
+ if (exten == file_extensions[i].exten)
+ return file_extensions[i].codec;
+ }
+ return IMG_CODEC_INVALID;
+}
+
+bool LLImageRaw::createFromFile(const LLString &filename, bool j2c_lowest_mip_only)
+{
+ LLString name = filename;
+ size_t dotidx = name.rfind('.');
+ S8 codec = IMG_CODEC_INVALID;
+ LLString exten;
+
+ deleteData(); // delete any existing data
+
+ if (dotidx != LLString::npos)
+ {
+ exten = name.substr(dotidx+1);
+ LLString::toLower(exten);
+ codec = get_codec(exten);
+ }
+ else
+ {
+ exten = find_file(name, &codec);
+ name = name + "." + exten;
+ }
+ if (codec == IMG_CODEC_INVALID)
+ {
+ return false; // format not recognized
+ }
+
+ llifstream ifs(name.c_str(), llifstream::binary);
+ if (!ifs.is_open())
+ {
+ // SJB: changed from llinfos to lldebugs to reduce spam
+ lldebugs << "Unable to open image file: " << name << llendl;
+ return false;
+ }
+
+ ifs.seekg (0, std::ios::end);
+ int length = ifs.tellg();
+ if (j2c_lowest_mip_only && length > 2048)
+ {
+ length = 2048;
+ }
+ ifs.seekg (0, std::ios::beg);
+
+ if (!length)
+ {
+ llinfos << "Zero length file file: " << name << llendl;
+ return false;
+ }
+
+ LLPointer<LLImageFormatted> image;
+ switch(codec)
+ {
+ //case IMG_CODEC_RGB:
+ case IMG_CODEC_BMP:
+ image = new LLImageBMP();
+ break;
+ case IMG_CODEC_TGA:
+ image = new LLImageTGA();
+ break;
+#if JPEG_SUPPORT
+ case IMG_CODEC_JPEG:
+ image = new LLImageJPEG();
+ break;
+#endif
+ case IMG_CODEC_J2C:
+ image = new LLImageJ2C();
+ break;
+ case IMG_CODEC_DXT:
+ image = new LLImageDXT();
+ break;
+ default:
+ return false;
+ }
+ llassert(image.notNull());
+
+ U8 *buffer = image->allocateData(length);
+ ifs.read ((char*)buffer, length);
+ ifs.close();
+
+ image->updateData();
+
+ if (j2c_lowest_mip_only && codec == IMG_CODEC_J2C)
+ {
+ S32 width = image->getWidth();
+ S32 height = image->getHeight();
+ S32 discard_level = 0;
+ while (width > 1 && height > 1 && discard_level < MAX_DISCARD_LEVEL)
+ {
+ width >>= 1;
+ height >>= 1;
+ discard_level++;
+ }
+ ((LLImageJ2C *)((LLImageFormatted*)image))->setDiscardLevel(discard_level);
+ }
+
+ BOOL success = image->decode(this, 100000.0f);
+ image = NULL; // deletes image
+
+ if (!success)
+ {
+ deleteData();
+ llwarns << "Unable to decode image" << name << llendl;
+ return false;
+ }
+
+ return true;
+}
+
+//---------------------------------------------------------------------------
+// LLImageFormatted
+//---------------------------------------------------------------------------
+
+//static
+S32 LLImageFormatted::sGlobalFormattedMemory = 0;
+
+//static
+LLWorkerThread* LLImageFormatted::sWorkerThread = NULL;
+
+//static
+void LLImageFormatted::initClass(bool threaded, bool run_always)
+{
+ sWorkerThread = new LLWorkerThread(threaded, run_always);
+}
+
+//static
+void LLImageFormatted::cleanupClass()
+{
+ delete sWorkerThread;
+ sWorkerThread = NULL;
+}
+
+
+LLImageFormatted::LLImageFormatted(S8 codec)
+ : LLImageBase(), LLWorkerClass(sWorkerThread, "ImageFormatted"),
+ mCodec(codec),
+ mDecoding(0),
+ mDecoded(0),
+ mDiscardLevel(0)
+{
+ mMemType = LLMemType::MTYPE_IMAGEFORMATTED;
+}
+
+// virtual
+LLImageFormatted::~LLImageFormatted()
+{
+ // NOTE: ~LLimageBase() call to deleteData() calls LLImageBase::deleteData()
+ // NOT LLImageFormatted::deleteData()
+ deleteData();
+ releaseDecodedData();
+}
+
+//----------------------------------------------------------------------------
+
+//virtual
+void LLImageFormatted::startWork(S32 param)
+{
+ if (mDecoding) llerrs << "WTF?" << llendl;
+}
+
+bool LLImageFormatted::doWork(S32 param)
+{
+ if (!(isWorking())) llerrs << "WTF?" << llendl;
+ llassert(mDecodedImage.notNull());
+ if (param == 0)
+ {
+ // Decode primary channels
+ mDecoded = decode(mDecodedImage, .001f); // 1ms
+ }
+ else
+ {
+ // Decode aux channel
+ mDecoded = decode(mDecodedImage, .001f, param, param); // 1ms
+ }
+ if (mDecoded)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void LLImageFormatted::endWork(S32 param, bool aborted)
+{
+ if (mDecoding) llerrs << "WTF?" << llendl;
+ if (!mDecoded) llerrs << "WTF?" << llendl;
+}
+
+//----------------------------------------------------------------------------
+
+// static
+LLImageFormatted* LLImageFormatted::createFromExtension(const LLString& instring)
+{
+ LLString exten;
+ size_t dotidx = instring.rfind('.');
+ if (dotidx != LLString::npos)
+ {
+ exten = instring.substr(dotidx+1);
+ }
+ else
+ {
+ exten = instring;
+ }
+ S8 codec = get_codec(exten);
+ LLPointer<LLImageFormatted> image;
+ switch(codec)
+ {
+ case IMG_CODEC_BMP:
+ image = new LLImageBMP();
+ break;
+ case IMG_CODEC_TGA:
+ image = new LLImageTGA();
+ break;
+#if JPEG_SUPPORT
+ case IMG_CODEC_JPEG:
+ image = new LLImageJPEG();
+ break;
+#endif
+ case IMG_CODEC_J2C:
+ image = new LLImageJ2C();
+ break;
+ case IMG_CODEC_DXT:
+ image = new LLImageDXT();
+ break;
+ default:
+ break;
+ }
+ return image;
+}
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::dump()
+{
+ LLImageBase::dump();
+
+ llinfos << "LLImageFormatted"
+ << " mDecoding " << mDecoding
+ << " mCodec " << S32(mCodec)
+ << " mDecoded " << mDecoded
+ << llendl;
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageFormatted::readHeader(U8* data, S32 size)
+{
+ if (size <= 0)
+ {
+ size = calcHeaderSize();
+ }
+ copyData(data, size); // calls updateData()
+}
+
+S32 LLImageFormatted::calcDataSize(S32 discard_level)
+{
+ if (discard_level < 0)
+ {
+ discard_level = mDiscardLevel;
+ }
+ S32 w = getWidth() >> discard_level;
+ S32 h = getHeight() >> discard_level;
+ w = llmax(w, 1);
+ h = llmax(h, 1);
+ return w * h * getComponents();
+}
+
+S32 LLImageFormatted::calcDiscardLevelBytes(S32 bytes)
+{
+ llassert(bytes >= 0);
+ S32 discard_level = 0;
+ while (1)
+ {
+ S32 bytes_needed = calcDataSize(discard_level); // virtual
+ if (bytes_needed <= bytes)
+ {
+ break;
+ }
+ discard_level++;
+ if (discard_level > MAX_IMAGE_MIP)
+ {
+ return -1;
+ }
+ }
+ return discard_level;
+}
+
+
+//----------------------------------------------------------------------------
+
+// Subclasses that can handle more than 4 channels should override this function.
+BOOL LLImageFormatted::decode(LLImageRaw* raw_image,F32 decode_time, S32 first_channel, S32 max_channel)
+{
+ llassert( (first_channel == 0) && (max_channel == 4) );
+ return decode( raw_image, decode_time ); // Loads first 4 channels by default.
+}
+
+// virtual
+BOOL LLImageFormatted::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32 decode_time)
+{
+ llassert(getData() && getDataSize());
+ // For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
+ if (discard >= 0 && discard != mDiscardLevel)
+ {
+ llerrs << "Request for invalid discard level" << llendl;
+ }
+ if (haveWork())
+ {
+ checkWork();
+ }
+ if (!mDecoded)
+ {
+ if (!haveWork())
+ {
+ llassert(!mDecoding);
+ mDecodedImage = new LLImageRaw(getWidth(), getHeight(), getComponents());
+ addWork(0);
+ }
+ return FALSE;
+ }
+ else
+ {
+ llassert(mDecodedImage.notNull());
+ llassert(!mDecoding);
+ raw = mDecodedImage;
+ return TRUE;
+ }
+}
+
+BOOL LLImageFormatted::requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel,
+ S32 discard, F32 decode_time)
+{
+ llassert(getData() && getDataSize());
+ // For most codecs, only mDiscardLevel data is available. (see LLImageDXT for exception)
+ if (discard >= 0 && discard != mDiscardLevel)
+ {
+ llerrs << "Request for invalid discard level" << llendl;
+ }
+ if (haveWork())
+ {
+ checkWork();
+ }
+ if (!mDecoded)
+ {
+ if (!haveWork())
+ {
+ llassert(!mDecoding);
+ mDecodedImage = new LLImageRaw(getWidth(), getHeight(), 1);
+ addWork(channel);
+ }
+ return FALSE;
+ }
+ else
+ {
+ llassert(mDecodedImage.notNull());
+ llassert(!mDecoding);
+ raw = mDecodedImage;
+ return TRUE;
+ }
+}
+
+
+// virtual
+void LLImageFormatted::releaseDecodedData()
+{
+ if (mDecoded || mDecoding)
+ {
+ mDecodedImage = NULL; // deletes image
+ mDecoded = FALSE;
+ mDecoding = FALSE;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+U8* LLImageFormatted::allocateData(S32 size)
+{
+ U8* res = LLImageBase::allocateData(size); // calls deleteData()
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+U8* LLImageFormatted::reallocateData(S32 size)
+{
+ sGlobalFormattedMemory -= getDataSize();
+ U8* res = LLImageBase::reallocateData(size);
+ sGlobalFormattedMemory += getDataSize();
+ return res;
+}
+
+// virtual
+void LLImageFormatted::deleteData()
+{
+ sGlobalFormattedMemory -= getDataSize();
+ LLImageBase::deleteData();
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageFormatted::sanityCheck()
+{
+ LLImageBase::sanityCheck();
+
+ if (mCodec >= IMG_CODEC_EOF)
+ {
+ llerrs << "Failed LLImageFormatted::sanityCheck "
+ << "decoding " << S32(mDecoding)
+ << "decoded " << S32(mDecoded)
+ << "codec " << S32(mCodec)
+ << llendl;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLImageFormatted::copyData(U8 *data, S32 size)
+{
+ if (data && data != getData())
+ {
+ deleteData();
+ allocateData(size);
+ memcpy(getData(), data, size);
+ }
+ updateData(); // virtual
+
+ return TRUE;
+}
+
+BOOL LLImageFormatted::appendData(U8 *data, S32 size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ S32 old_size = getDataSize();
+ U8* old_data = getData();
+ S32 new_size = old_size + size;
+ U8* new_data = new U8[new_size];
+ // resize the image
+ setDataAndSize(new_data, new_size);
+ // copy the old data and delete it
+ memcpy(new_data, old_data, old_size);
+ delete old_data;
+ // if we have new data, copy it and call updateData()
+ if (data)
+ {
+ memcpy(new_data + old_size, data, size);
+ updateData(); // virtual
+ }
+ return TRUE;
+}
+
+BOOL LLImageFormatted::setData(U8 *data, S32 size)
+{
+ if (data && data != getData())
+ {
+ deleteData();
+ setDataAndSize(data, size); // Access private LLImageBase members
+ sGlobalFormattedMemory += getDataSize();
+ }
+ return updateData(); // virtual
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLImageFormatted::load(const LLString &filename)
+{
+ resetLastError();
+
+ S32 file_size = 0;
+ apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_RB, &file_size);
+ if (!apr_file)
+ {
+ setLastError("Unable to open file for reading", filename);
+ return FALSE;
+ }
+ if (file_size == 0)
+ {
+ setLastError("File is empty",filename);
+ apr_file_close(apr_file);
+ return FALSE;
+ }
+
+ BOOL res;
+ U8 *data = allocateData(file_size);
+ apr_size_t bytes_read = file_size;
+ apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
+ if (s != APR_SUCCESS || (S32) bytes_read != file_size)
+ {
+ deleteData();
+ setLastError("Unable to read entire file",filename);
+ res = FALSE;
+ }
+ else
+ {
+ res = updateData();
+ }
+ apr_file_close(apr_file);
+
+ return res;
+}
+
+BOOL LLImageFormatted::save(const LLString &filename)
+{
+ resetLastError();
+
+ apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_WB);
+ if (!apr_file)
+ {
+ setLastError("Unable to open file for reading", filename);
+ return FALSE;
+ }
+
+ ll_apr_file_write(apr_file, getData(), getDataSize());
+ apr_file_close(apr_file);
+
+ return TRUE;
+}
+
+// BOOL LLImageFormatted::save(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+// Depricated to remove VFS dependency.
+// Use:
+// LLVFile::writeFile(image->getData(), image->getDataSize(), vfs, uuid, type);
+
+//----------------------------------------------------------------------------
+
+S8 LLImageFormatted::getCodec() const
+{
+ return mCodec;
+}
+
+//============================================================================
+
+//----------------------------------------------------------------------------
+
+static void avg4_colors4(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+ dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2);
+ dst[3] = (U8)(((U32)(a[3]) + b[3] + c[3] + d[3])>>2);
+}
+
+static void avg4_colors3(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+ dst[2] = (U8)(((U32)(a[2]) + b[2] + c[2] + d[2])>>2);
+}
+
+static void avg4_colors2(const U8* a, const U8* b, const U8* c, const U8* d, U8* dst)
+{
+ dst[0] = (U8)(((U32)(a[0]) + b[0] + c[0] + d[0])>>2);
+ dst[1] = (U8)(((U32)(a[1]) + b[1] + c[1] + d[1])>>2);
+}
+
+//static
+void LLImageBase::generateMip(const U8* indata, U8* mipdata, S32 width, S32 height, S32 nchannels)
+{
+ llassert(width > 0 && height > 0);
+ U8* data = mipdata;
+ S32 in_width = width*2;
+ for (S32 h=0; h<height; h++)
+ {
+ for (S32 w=0; w<width; w++)
+ {
+ switch(nchannels)
+ {
+ case 4:
+ avg4_colors4(indata, indata+4, indata+4*in_width, indata+4*in_width+4, data);
+ break;
+ case 3:
+ avg4_colors3(indata, indata+3, indata+3*in_width, indata+3*in_width+3, data);
+ break;
+ case 2:
+ avg4_colors2(indata, indata+2, indata+2*in_width, indata+2*in_width+2, data);
+ break;
+ case 1:
+ *(U8*)data = (U8)(((U32)(indata[0]) + indata[1] + indata[in_width] + indata[in_width+1])>>2);
+ break;
+ default:
+ llerrs << "generateMmip called with bad num channels" << llendl;
+ }
+ indata += nchannels*2;
+ data += nchannels;
+ }
+ indata += nchannels*in_width; // skip odd lines
+ }
+}
+
+
+//============================================================================
+
+//static
+F32 LLImageBase::calc_download_priority(F32 virtual_size, F32 visible_pixels, S32 bytes_sent)
+{
+ F32 w_priority;
+
+ F32 bytes_weight = 1.f;
+ if (!bytes_sent)
+ {
+ bytes_weight = 20.f;
+ }
+ else if (bytes_sent < 1000)
+ {
+ bytes_weight = 1.f;
+ }
+ else if (bytes_sent < 2000)
+ {
+ bytes_weight = 1.f/1.5f;
+ }
+ else if (bytes_sent < 4000)
+ {
+ bytes_weight = 1.f/3.f;
+ }
+ else if (bytes_sent < 8000)
+ {
+ bytes_weight = 1.f/6.f;
+ }
+ else if (bytes_sent < 16000)
+ {
+ bytes_weight = 1.f/12.f;
+ }
+ else if (bytes_sent < 32000)
+ {
+ bytes_weight = 1.f/20.f;
+ }
+ else if (bytes_sent < 64000)
+ {
+ bytes_weight = 1.f/32.f;
+ }
+ else
+ {
+ bytes_weight = 1.f/64.f;
+ }
+ bytes_weight *= bytes_weight;
+
+
+ //llinfos << "VS: " << virtual_size << llendl;
+ F32 virtual_size_factor = virtual_size / (10.f*10.f);
+
+ // The goal is for weighted priority to be <= 0 when we've reached a point where
+ // we've sent enough data.
+ //llinfos << "BytesSent: " << bytes_sent << llendl;
+ //llinfos << "BytesWeight: " << bytes_weight << llendl;
+ //llinfos << "PreLog: " << bytes_weight * virtual_size_factor << llendl;
+ w_priority = (F32)log10(bytes_weight * virtual_size_factor);
+
+ //llinfos << "PreScale: " << w_priority << llendl;
+
+ // We don't want to affect how MANY bytes we send based on the visible pixels, but the order
+ // in which they're sent. We post-multiply so we don't change the zero point.
+ if (w_priority > 0.f)
+ {
+ F32 pixel_weight = (F32)log10(visible_pixels + 1)*3.0f;
+ w_priority *= pixel_weight;
+ }
+
+ return w_priority;
+}
diff --git a/indra/llimage/llimage.h b/indra/llimage/llimage.h
new file mode 100644
index 0000000000..13ea916654
--- /dev/null
+++ b/indra/llimage/llimage.h
@@ -0,0 +1,298 @@
+/**
+ * @file llimage.h
+ * @brief Object for managing images and their textures.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGE_H
+#define LL_LLIMAGE_H
+
+#include "stdtypes.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "llmemory.h"
+#include "llworkerthread.h"
+
+const S32 MIN_IMAGE_MIP = 2; // 4x4, only used for expand/contract power of 2
+const S32 MAX_IMAGE_MIP = 11; // 2048x2048
+const S32 MAX_DISCARD_LEVEL = 5;
+
+const S32 MIN_IMAGE_SIZE = (1<<MIN_IMAGE_MIP); // 4, only used for expand/contract power of 2
+const S32 MAX_IMAGE_SIZE = (1<<MAX_IMAGE_MIP); // 2048
+const S32 MIN_IMAGE_AREA = MIN_IMAGE_SIZE * MIN_IMAGE_SIZE;
+const S32 MAX_IMAGE_AREA = MAX_IMAGE_SIZE * MAX_IMAGE_SIZE;
+const S32 MAX_IMAGE_COMPONENTS = 8;
+const S32 MAX_IMAGE_DATA_SIZE = MAX_IMAGE_AREA * MAX_IMAGE_COMPONENTS;
+
+// Note! These CANNOT be changed without invalidating the viewer VFS files, I think?
+const S32 FIRST_PACKET_SIZE = 600;
+const S32 MAX_IMG_PACKET_SIZE = 1000;
+
+// Base classes for images.
+// There are two major parts for the image:
+// The compressed representation, and the decompressed representation.
+
+class LLImageFormatted;
+class LLImageRaw;
+class LLColor4U;
+
+enum
+{
+ IMG_CODEC_INVALID = 0,
+ IMG_CODEC_RGB = 1,
+ IMG_CODEC_J2C = 2,
+ IMG_CODEC_BMP = 3,
+ IMG_CODEC_TGA = 4,
+ IMG_CODEC_JPEG = 5,
+ IMG_CODEC_DXT = 6,
+ IMG_CODEC_EOF = 7
+};
+
+//============================================================================
+
+class LLImageBase : public LLThreadSafeRefCount
+{
+protected:
+ virtual ~LLImageBase();
+
+public:
+ LLImageBase();
+
+ enum
+ {
+ TYPE_NORMAL = 0,
+ TYPE_AVATAR_BAKE = 1,
+ };
+
+ virtual void deleteData();
+ virtual U8* allocateData(S32 size = -1);
+ virtual U8* reallocateData(S32 size = -1);
+
+ virtual void dump();
+ virtual void sanityCheck();
+
+ U16 getWidth() const { return mWidth; }
+ U16 getHeight() const { return mHeight; }
+ S8 getComponents() const { return mComponents; }
+ S32 getDataSize() const { return mDataSize; }
+
+ const U8 *getData() const { return mData; } // read only
+ U8 *getData() { return mData; }
+
+ void setSize(S32 width, S32 height, S32 ncomponents);
+ U8* allocateDataSize(S32 width, S32 height, S32 ncomponents, S32 size = -1); // setSize() + allocateData()
+
+protected:
+ // special accessor to allow direct setting of mData and mDataSize by LLImageFormatted
+ void setDataAndSize(U8 *data, S32 size) { mData = data; mDataSize = size; };
+
+public:
+ static const LLString& getLastError() {return sLastErrorMessage;};
+ static void resetLastError() {sLastErrorMessage = LLString("No Error"); };
+ static BOOL setLastError(const LLString& message, const LLString& filename = ""); // returns FALSE
+
+ static void generateMip(const U8 *indata, U8* mipdata, int width, int height, S32 nchannels);
+
+ // Function for calculating the download priority for textes
+ // <= 0 priority means that there's no need for more data.
+ static F32 calc_download_priority(F32 virtual_size, F32 visible_area, S32 bytes_sent);
+
+ static void setSizeOverride(BOOL enabled) { sSizeOverride = enabled; }
+
+private:
+ U8 *mData;
+ S32 mDataSize;
+
+ U16 mWidth;
+ U16 mHeight;
+
+ S8 mComponents;
+
+public:
+ S16 mMemType; // debug
+
+ static LLString sLastErrorMessage;
+
+ static BOOL sSizeOverride;
+};
+
+// Raw representation of an image (used for textures, and other uncompressed formats
+class LLImageRaw : public LLImageBase
+{
+protected:
+ /*virtual*/ ~LLImageRaw();
+
+public:
+ LLImageRaw();
+ LLImageRaw(U16 width, U16 height, S8 components);
+ LLImageRaw(U8 *data, U16 width, U16 height, S8 components);
+ // Construct using createFromFile (used by tools)
+ LLImageRaw(const LLString& filename, bool j2c_lowest_mip_only = false);
+
+ /*virtual*/ void deleteData();
+ /*virtual*/ U8* allocateData(S32 size = -1);
+ /*virtual*/ U8* reallocateData(S32 size);
+
+ BOOL copyData(U8 *data, U16 width, U16 height, S8 components);
+
+ BOOL resize(U16 width, U16 height, S8 components);
+
+ U8 * getSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height) const;
+ BOOL setSubImage(U32 x_pos, U32 y_pos, U32 width, U32 height,
+ const U8 *data, U32 stride = 0, BOOL reverse_y = FALSE);
+
+ void clear(U8 r=0, U8 g=0, U8 b=0, U8 a=255);
+
+ void verticalFlip();
+
+ void expandToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
+ void contractToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE, BOOL scale_image = TRUE);
+ void biasedScaleToPowerOfTwo(S32 max_dim = MAX_IMAGE_SIZE);
+ void scale( S32 new_width, S32 new_height, BOOL scale_image = TRUE );
+
+ // Fill the buffer with a constant color
+ void fill( const LLColor4U& color );
+
+ // Copy operations
+
+ // Src and dst can be any size. Src and dst can each have 3 or 4 components.
+ void copy( LLImageRaw* src );
+
+ // Src and dst are same size. Src and dst have same number of components.
+ void copyUnscaled( LLImageRaw* src );
+
+ // Src and dst are same size. Src has 4 components. Dst has 3 components.
+ void copyUnscaled4onto3( LLImageRaw* src );
+
+ // Src and dst are same size. Src has 3 components. Dst has 4 components.
+ void copyUnscaled3onto4( LLImageRaw* src );
+
+ // Src and dst can be any size. Src and dst have same number of components.
+ void copyScaled( LLImageRaw* src );
+
+ // Src and dst can be any size. Src has 3 components. Dst has 4 components.
+ void copyScaled3onto4( LLImageRaw* src );
+
+ // Src and dst can be any size. Src has 4 components. Dst has 3 components.
+ void copyScaled4onto3( LLImageRaw* src );
+
+
+ // Composite operations
+
+ // Src and dst can be any size. Src and dst can each have 3 or 4 components.
+ void composite( LLImageRaw* src );
+
+ // Src and dst can be any size. Src has 4 components. Dst has 3 components.
+ void compositeScaled4onto3( LLImageRaw* src );
+
+ // Src and dst are same size. Src has 4 components. Dst has 3 components.
+ void compositeUnscaled4onto3( LLImageRaw* src );
+
+protected:
+ // Create an image from a local file (generally used in tools)
+ bool createFromFile(const LLString& filename, bool j2c_lowest_mip_only = false);
+
+ void copyLineScaled( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len, S32 in_pixel_step, S32 out_pixel_step );
+ void compositeRowScaled4onto3( U8* in, U8* out, S32 in_pixel_len, S32 out_pixel_len );
+
+ U8 fastFractionalMult(U8 a,U8 b);
+
+public:
+ static S32 sGlobalRawMemory;
+ static S32 sRawImageCount;
+};
+
+// Compressed representation of image.
+// Subclass from this class for the different representations (J2C, bmp)
+class LLImageFormatted : public LLImageBase, public LLWorkerClass
+{
+public:
+ static void initClass(bool threaded = true, bool run_always = true);
+ static void cleanupClass();
+ static LLImageFormatted* createFromExtension(const LLString& instring);
+
+protected:
+ /*virtual*/ ~LLImageFormatted();
+
+public:
+ LLImageFormatted(S8 codec);
+
+ // LLImageBase
+public:
+ /*virtual*/ void deleteData();
+ /*virtual*/ U8* allocateData(S32 size = -1);
+ /*virtual*/ U8* reallocateData(S32 size);
+
+ /*virtual*/ void dump();
+ /*virtual*/ void sanityCheck();
+
+ // LLWorkerThread
+public:
+ // called from WORKER THREAD, returns TRUE if done
+ /*virtual*/ bool doWork(S32 param);
+private:
+ // called from MAIN THREAD
+ /*virtual*/ void startWork(S32 param); // called from addWork()
+ /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork()
+
+ // New methods
+public:
+ // calcHeaderSize() returns the maximum size of header;
+ // 0 indicates we don't know have a header and have to lead the entire file
+ virtual S32 calcHeaderSize() { return 0; };
+ // readHeader() reads size bytes into mData, and sets width/height/ncomponents
+ virtual void readHeader(U8* data, S32 size);
+ // calcDataSize() returns how many bytes to read to load discard_level (including header)
+ virtual S32 calcDataSize(S32 discard_level);
+ // calcDiscardLevelBytes() returns the smallest valid discard level based on the number of input bytes
+ virtual S32 calcDiscardLevelBytes(S32 bytes);
+ // getRawDiscardLevel()by default returns mDiscardLevel, but may be overridden (LLImageJ2C)
+ virtual S8 getRawDiscardLevel() { return mDiscardLevel; }
+
+ BOOL load(const LLString& filename);
+ BOOL save(const LLString& filename);
+// BOOL save(LLVFS *vfs, const LLUUID &uuid, const LLAssetType::EType type);
+// Depricated to remove VFS dependency (see .cpp for replacement):
+
+ virtual BOOL updateData() = 0; // pure virtual
+ BOOL copyData(U8 *data, S32 size); // calls updateData()
+ BOOL setData(U8 *data, S32 size); // calls updateData()
+ BOOL appendData(U8 *data, S32 size); // use if some data (e.g header) is already loaded, calls updateData()
+
+ // Loads first 4 channels.
+ virtual BOOL decode(LLImageRaw* raw_image, F32 decode_time=0.0) = 0;
+ // Subclasses that can handle more than 4 channels should override this function.
+ virtual BOOL decode(LLImageRaw* raw_image, F32 decode_time, S32 first_channel, S32 max_channel);
+
+ // Decode methods to return a pointer to raw data for purposes of passing to
+ // opengl or such. This class tracks the decoded data and keeps it alive until
+ // destroyed or releaseDecodedData() is called.
+ virtual BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard = -1, F32 decode_time=0.0);
+ virtual BOOL requestDecodedAuxData(LLPointer<LLImageRaw>& raw, S32 channel,
+ S32 discard = -1, F32 decode_time=0.0);
+ virtual void releaseDecodedData();
+
+ virtual BOOL encode(const LLImageRaw* raw_image, F32 encode_time=0.0) = 0;
+
+ S8 getCodec() const;
+ BOOL isDecoding() const { return mDecoding ? TRUE : FALSE; }
+ BOOL isDecoded() const { return mDecoded ? TRUE : FALSE; }
+ void setDiscardLevel(S8 discard_level) { mDiscardLevel = discard_level; }
+ S8 getDiscardLevel() const { return mDiscardLevel; }
+
+protected:
+ S8 mCodec;
+ S8 mDecoding;
+ S8 mDecoded;
+ S8 mDiscardLevel;
+
+ LLPointer<LLImageRaw> mDecodedImage;
+
+public:
+ static S32 sGlobalFormattedMemory;
+ static LLWorkerThread* sWorkerThread;
+};
+
+#endif
diff --git a/indra/llimage/llimagebmp.cpp b/indra/llimage/llimagebmp.cpp
new file mode 100644
index 0000000000..94aaa5c19e
--- /dev/null
+++ b/indra/llimage/llimagebmp.cpp
@@ -0,0 +1,624 @@
+/**
+ * @file llimagebmp.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llimagebmp.h"
+#include "llerror.h"
+
+#include "llendianswizzle.h"
+
+
+/**
+ * @struct LLBMPHeader
+ *
+ * This struct helps deal with bmp files.
+ */
+struct LLBMPHeader
+{
+ S32 mSize;
+ S32 mWidth;
+ S32 mHeight;
+ S16 mPlanes;
+ S16 mBitsPerPixel;
+ S16 mCompression;
+ S16 mAlignmentPadding; // pads out to next word boundary
+ S32 mImageSize;
+ S32 mHorzPelsPerMeter;
+ S32 mVertPelsPerMeter;
+ S32 mNumColors;
+ S32 mNumColorsImportant;
+};
+
+/**
+ * @struct Win95BmpHeaderExtension
+ */
+struct Win95BmpHeaderExtension
+{
+ U32 mReadMask;
+ U32 mGreenMask;
+ U32 mBlueMask;
+ U32 mAlphaMask;
+ U32 mColorSpaceType;
+ U16 mRed[3]; // Red CIE endpoint
+ U16 mGreen[3]; // Green CIE endpoint
+ U16 mBlue[3]; // Blue CIE endpoint
+ U32 mGamma[3]; // Gamma scale for r g and b
+};
+
+/**
+ * LLImageBMP
+ */
+LLImageBMP::LLImageBMP()
+ :
+ LLImageFormatted(IMG_CODEC_BMP),
+ mColorPaletteColors( 0 ),
+ mColorPalette( NULL ),
+ mBitmapOffset( 0 ),
+ mBitsPerPixel( 0 ),
+ mOriginAtTop( FALSE )
+{
+ mBitfieldMask[0] = 0;
+ mBitfieldMask[1] = 0;
+ mBitfieldMask[2] = 0;
+ mBitfieldMask[3] = 0;
+}
+
+LLImageBMP::~LLImageBMP()
+{
+ delete[] mColorPalette;
+}
+
+
+BOOL LLImageBMP::updateData()
+{
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ U8* mdata = getData();
+ if (!mdata || (0 == getDataSize()))
+ {
+ setLastError("Uninitialized instance of LLImageBMP");
+ return FALSE;
+ }
+
+ // Read the bitmap headers in order to get all the useful info
+ // about this image
+
+ ////////////////////////////////////////////////////////////////////
+ // Part 1: "File Header"
+ // 14 bytes consisting of
+ // 2 bytes: either BM or BA
+ // 4 bytes: file size in bytes
+ // 4 bytes: reserved (always 0)
+ // 4 bytes: bitmap offset (starting position of image data in bytes)
+ const S32 FILE_HEADER_SIZE = 14;
+ if ((mdata[0] != 'B') || (mdata[1] != 'M'))
+ {
+ if ((mdata[0] != 'B') || (mdata[1] != 'A'))
+ {
+ setLastError("OS/2 bitmap array BMP files are not supported");
+ return FALSE;
+ }
+ else
+ {
+ setLastError("Does not appear to be a bitmap file");
+ return FALSE;
+ }
+ }
+
+ mBitmapOffset = mdata[13];
+ mBitmapOffset <<= 8; mBitmapOffset += mdata[12];
+ mBitmapOffset <<= 8; mBitmapOffset += mdata[11];
+ mBitmapOffset <<= 8; mBitmapOffset += mdata[10];
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Part 2: "Bitmap Header"
+ const S32 BITMAP_HEADER_SIZE = 40;
+ LLBMPHeader header;
+ llassert( sizeof( header ) == BITMAP_HEADER_SIZE );
+
+ memcpy((void *)&header, mdata + FILE_HEADER_SIZE, BITMAP_HEADER_SIZE);
+
+ // convert BMP header from little endian (no-op on little endian builds)
+ llendianswizzleone(header.mSize);
+ llendianswizzleone(header.mWidth);
+ llendianswizzleone(header.mHeight);
+ llendianswizzleone(header.mPlanes);
+ llendianswizzleone(header.mBitsPerPixel);
+ llendianswizzleone(header.mCompression);
+ llendianswizzleone(header.mAlignmentPadding);
+ llendianswizzleone(header.mImageSize);
+ llendianswizzleone(header.mHorzPelsPerMeter);
+ llendianswizzleone(header.mVertPelsPerMeter);
+ llendianswizzleone(header.mNumColors);
+ llendianswizzleone(header.mNumColorsImportant);
+
+ BOOL windows_nt_version = FALSE;
+ BOOL windows_95_version = FALSE;
+ if( 12 == header.mSize )
+ {
+ setLastError("Windows 2.x and OS/2 1.x BMP files are not supported");
+ return FALSE;
+ }
+ else
+ if( 40 == header.mSize )
+ {
+ if( 3 == header.mCompression )
+ {
+ // Windows NT
+ windows_nt_version = TRUE;
+ }
+ else
+ {
+ // Windows 3.x
+ }
+ }
+ else
+ if( 12 <= header.mSize && 64 <= header.mSize )
+ {
+ setLastError("OS/2 2.x BMP files are not supported");
+ return FALSE;
+ }
+ else
+ if( 108 == header.mSize )
+ {
+ // BITMAPV4HEADER
+ windows_95_version = TRUE;
+ }
+ else
+ if( 108 < header.mSize )
+ {
+ // BITMAPV5HEADER or greater
+ // Should work as long at Microsoft maintained backwards compatibility (which they did in V4 and V5)
+ windows_95_version = TRUE;
+ }
+
+ S32 width = header.mWidth;
+ S32 height = header.mHeight;
+ if (height < 0)
+ {
+ mOriginAtTop = TRUE;
+ height = -height;
+ }
+ else
+ {
+ mOriginAtTop = FALSE;
+ }
+
+ mBitsPerPixel = header.mBitsPerPixel;
+ S32 components;
+ switch( mBitsPerPixel )
+ {
+ case 8:
+ components = 1;
+ break;
+ case 24:
+ case 32:
+ components = 3;
+ break;
+ case 1:
+ case 4:
+ case 16: // Started work on 16, but doesn't work yet
+ // These are legal, but we don't support them yet.
+ setLastError("Unsupported bit depth");
+ return FALSE;
+ default:
+ setLastError("Unrecognized bit depth");
+ return FALSE;
+ }
+
+ setSize(width, height, components);
+
+ switch( header.mCompression )
+ {
+ case 0:
+ // Uncompressed
+ break;
+
+ case 1:
+ setLastError("8 bit RLE compression not supported.");
+ return FALSE;
+
+ case 2:
+ setLastError("4 bit RLE compression not supported.");
+ return FALSE;
+
+ case 3:
+ // Windows NT or Windows 95
+ break;
+
+ default:
+ setLastError("Unsupported compression format.");
+ return FALSE;
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Part 3: Bitfield Masks and other color data
+ S32 extension_size = 0;
+ if( windows_nt_version )
+ {
+ if( (16 != header.mBitsPerPixel) && (32 != header.mBitsPerPixel) )
+ {
+ setLastError("Bitfield encoding requires 16 or 32 bits per pixel.");
+ return FALSE;
+ }
+
+ if( 0 != header.mNumColors )
+ {
+ setLastError("Bitfield encoding is not compatible with a color table.");
+ return FALSE;
+ }
+
+
+ extension_size = 4 * 3;
+ memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, extension_size);
+ }
+ else
+ if( windows_95_version )
+ {
+ Win95BmpHeaderExtension win_95_extension;
+ extension_size = sizeof( win_95_extension );
+
+ llassert( sizeof( win_95_extension ) + BITMAP_HEADER_SIZE == 108 );
+ memcpy( &win_95_extension, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, sizeof( win_95_extension ) );
+
+ if( 3 == header.mCompression )
+ {
+ memcpy( mBitfieldMask, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE, 4 * 4);
+ }
+
+ // Color correction ignored for now
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ // Part 4: Color Palette (optional)
+ // Note: There's no color palette if there are 16 or more bits per pixel
+ S32 color_palette_size = 0;
+ mColorPaletteColors = 0;
+ if( header.mBitsPerPixel < 16 )
+ {
+ if( 0 == header.mNumColors )
+ {
+ mColorPaletteColors = (1 << header.mBitsPerPixel);
+ }
+ else
+ {
+ mColorPaletteColors = header.mNumColors;
+ }
+ }
+ color_palette_size = mColorPaletteColors * 4;
+
+ if( 0 != mColorPaletteColors )
+ {
+ mColorPalette = new U8[color_palette_size];
+ memcpy( mColorPalette, mdata + FILE_HEADER_SIZE + BITMAP_HEADER_SIZE + extension_size, color_palette_size );
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageBMP::decode(LLImageRaw* raw_image, F32 decode_time)
+{
+ llassert_always(raw_image);
+
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ U8* mdata = getData();
+ if (!mdata || (0 == getDataSize()))
+ {
+ setLastError("llimagebmp trying to decode an image with no data!");
+ return FALSE;
+ }
+
+ raw_image->resize(getWidth(), getHeight(), 3);
+
+ U8* src = mdata + mBitmapOffset;
+ U8* dst = raw_image->getData();
+
+ BOOL success = FALSE;
+
+ switch( mBitsPerPixel )
+ {
+ case 8:
+ if( mColorPaletteColors >= 256 )
+ {
+ success = decodeColorTable8( dst, src );
+ }
+ break;
+
+ case 16:
+ success = decodeColorMask16( dst, src );
+ break;
+
+ case 24:
+ success = decodeTruecolor24( dst, src );
+ break;
+
+ case 32:
+ success = decodeColorMask32( dst, src );
+ break;
+ }
+
+ if( success && mOriginAtTop )
+ {
+ raw_image->verticalFlip();
+ }
+
+ return success;
+}
+
+U32 LLImageBMP::countTrailingZeros( U32 m )
+{
+ U32 shift_count = 0;
+ while( !(m & 1) )
+ {
+ shift_count++;
+ m >>= 1;
+ }
+ return shift_count;
+}
+
+
+BOOL LLImageBMP::decodeColorMask16( U8* dst, U8* src )
+{
+ llassert( 16 == mBitsPerPixel );
+
+ if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] )
+ {
+ // Use default values
+ mBitfieldMask[0] = 0x00007C00;
+ mBitfieldMask[1] = 0x000003E0;
+ mBitfieldMask[2] = 0x0000001F;
+ }
+
+ S32 src_row_span = getWidth() * 2;
+ S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
+
+ U32 r_shift = countTrailingZeros( mBitfieldMask[2] );
+ U32 g_shift = countTrailingZeros( mBitfieldMask[1] );
+ U32 b_shift = countTrailingZeros( mBitfieldMask[0] );
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( S32 col = 0; col < getWidth(); col++ )
+ {
+ U32 value = *((U16*)src);
+ dst[0] = U8((value & mBitfieldMask[2]) >> r_shift); // Red
+ dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
+ dst[2] = U8((value & mBitfieldMask[0]) >> b_shift); // Blue
+ src += 2;
+ dst += 3;
+ }
+ src += alignment_bytes;
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageBMP::decodeColorMask32( U8* dst, U8* src )
+{
+ // Note: alpha is not supported
+
+ llassert( 32 == mBitsPerPixel );
+
+ if( !mBitfieldMask[0] && !mBitfieldMask[1] && !mBitfieldMask[2] )
+ {
+ // Use default values
+ mBitfieldMask[0] = 0x00FF0000;
+ mBitfieldMask[1] = 0x0000FF00;
+ mBitfieldMask[2] = 0x000000FF;
+ }
+
+
+ S32 src_row_span = getWidth() * 4;
+ S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
+
+ U32 r_shift = countTrailingZeros( mBitfieldMask[0] );
+ U32 g_shift = countTrailingZeros( mBitfieldMask[1] );
+ U32 b_shift = countTrailingZeros( mBitfieldMask[2] );
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( S32 col = 0; col < getWidth(); col++ )
+ {
+ U32 value = *((U32*)src);
+ dst[0] = U8((value & mBitfieldMask[0]) >> r_shift); // Red
+ dst[1] = U8((value & mBitfieldMask[1]) >> g_shift); // Green
+ dst[2] = U8((value & mBitfieldMask[2]) >> b_shift); // Blue
+ src += 4;
+ dst += 3;
+ }
+ src += alignment_bytes;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLImageBMP::decodeColorTable8( U8* dst, U8* src )
+{
+ llassert( (8 == mBitsPerPixel) && (mColorPaletteColors >= 256) );
+
+ S32 src_row_span = getWidth() * 1;
+ S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( S32 col = 0; col < getWidth(); col++ )
+ {
+ S32 index = 4 * src[0];
+ dst[0] = mColorPalette[index + 2]; // Red
+ dst[1] = mColorPalette[index + 1]; // Green
+ dst[2] = mColorPalette[index + 0]; // Blue
+ src++;
+ dst += 3;
+ }
+ src += alignment_bytes;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLImageBMP::decodeTruecolor24( U8* dst, U8* src )
+{
+ llassert( 24 == mBitsPerPixel );
+ llassert( 3 == getComponents() );
+ S32 src_row_span = getWidth() * 3;
+ S32 alignment_bytes = (3 * src_row_span) % 4; // round up to nearest multiple of 4
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( S32 col = 0; col < getWidth(); col++ )
+ {
+ dst[0] = src[2]; // Red
+ dst[1] = src[1]; // Green
+ dst[2] = src[0]; // Blue
+ src += 3;
+ dst += 3;
+ }
+ src += alignment_bytes;
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageBMP::encode(const LLImageRaw* raw_image, F32 encode_time)
+{
+ llassert_always(raw_image);
+
+ resetLastError();
+
+ S32 src_components = raw_image->getComponents();
+ S32 dst_components = ( src_components < 3 ) ? 1 : 3;
+
+ if( (2 == src_components) || (4 == src_components) )
+ {
+ llinfos << "Dropping alpha information during BMP encoding" << llendl;
+ }
+
+ setSize(raw_image->getWidth(), raw_image->getHeight(), dst_components);
+
+ U8 magic[14];
+ LLBMPHeader header;
+ int header_bytes = 14+sizeof(header);
+ llassert(header_bytes == 54);
+ if (getComponents() == 1)
+ {
+ header_bytes += 1024; // Need colour LUT.
+ }
+ int line_bytes = getComponents() * getWidth();
+ int alignment_bytes = (3 * line_bytes) % 4;
+ line_bytes += alignment_bytes;
+ int file_bytes = line_bytes*getHeight() + header_bytes;
+
+ // Allocate the new buffer for the data.
+ allocateData(file_bytes);
+
+ magic[0] = 'B'; magic[1] = 'M';
+ magic[2] = (U8) file_bytes;
+ magic[3] = (U8)(file_bytes>>8);
+ magic[4] = (U8)(file_bytes>>16);
+ magic[5] = (U8)(file_bytes>>24);
+ magic[6] = magic[7] = magic[8] = magic[9] = 0;
+ magic[10] = (U8) header_bytes;
+ magic[11] = (U8)(header_bytes>>8);
+ magic[12] = (U8)(header_bytes>>16);
+ magic[13] = (U8)(header_bytes>>24);
+ header.mSize = 40;
+ header.mWidth = getWidth();
+ header.mHeight = getHeight();
+ header.mPlanes = 1;
+ header.mBitsPerPixel = (getComponents()==1)?8:24;
+ header.mCompression = 0;
+ header.mAlignmentPadding = 0;
+ header.mImageSize = 0;
+#if LL_DARWIN
+ header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 2834; // 72dpi
+#else
+ header.mHorzPelsPerMeter = header.mVertPelsPerMeter = 0;
+#endif
+ header.mNumColors = header.mNumColorsImportant = 0;
+
+ // convert BMP header to little endian (no-op on little endian builds)
+ llendianswizzleone(header.mSize);
+ llendianswizzleone(header.mWidth);
+ llendianswizzleone(header.mHeight);
+ llendianswizzleone(header.mPlanes);
+ llendianswizzleone(header.mBitsPerPixel);
+ llendianswizzleone(header.mCompression);
+ llendianswizzleone(header.mAlignmentPadding);
+ llendianswizzleone(header.mImageSize);
+ llendianswizzleone(header.mHorzPelsPerMeter);
+ llendianswizzleone(header.mVertPelsPerMeter);
+ llendianswizzleone(header.mNumColors);
+ llendianswizzleone(header.mNumColorsImportant);
+
+ U8* mdata = getData();
+
+ // Output magic, then header, then the palette table, then the data.
+ U32 cur_pos = 0;
+ memcpy(mdata, magic, 14);
+ cur_pos += 14;
+ memcpy(mdata+cur_pos, &header, 40);
+ cur_pos += 40;
+ if (getComponents() == 1)
+ {
+ S32 n;
+ for (n=0; n < 256; n++)
+ {
+ mdata[cur_pos++] = (U8)n;
+ mdata[cur_pos++] = (U8)n;
+ mdata[cur_pos++] = (U8)n;
+ mdata[cur_pos++] = 0;
+ }
+ }
+
+ // Need to iterate through, because we need to flip the RGB.
+ const U8* src = raw_image->getData();
+ U8* dst = mdata + cur_pos;
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( S32 col = 0; col < getWidth(); col++ )
+ {
+ switch( src_components )
+ {
+ case 1:
+ *dst++ = *src++;
+ break;
+ case 2:
+ {
+ U32 lum = src[0];
+ U32 alpha = src[1];
+ *dst++ = (U8)(lum * alpha / 255);
+ src += 2;
+ break;
+ }
+ case 3:
+ case 4:
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ src += src_components;
+ dst += 3;
+ break;
+ }
+
+ for( S32 i = 0; i < alignment_bytes; i++ )
+ {
+ *dst++ = 0;
+ }
+ }
+ }
+
+ return TRUE;
+}
diff --git a/indra/llimage/llimagebmp.h b/indra/llimage/llimagebmp.h
new file mode 100644
index 0000000000..0c0e51e2e7
--- /dev/null
+++ b/indra/llimage/llimagebmp.h
@@ -0,0 +1,45 @@
+/**
+ * @file llimagebmp.h
+ * @brief Image implementation for BMP.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGEBMP_H
+#define LL_LLIMAGEBMP_H
+
+#include "llimage.h"
+
+// This class compresses and decompressed BMP files
+
+class LLImageBMP : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageBMP();
+
+public:
+ LLImageBMP();
+
+ /*virtual*/ BOOL updateData();
+ /*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 time=0.0);
+ /*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 time=0.0);
+
+protected:
+ BOOL decodeColorTable8( U8* dst, U8* src );
+ BOOL decodeColorMask16( U8* dst, U8* src );
+ BOOL decodeTruecolor24( U8* dst, U8* src );
+ BOOL decodeColorMask32( U8* dst, U8* src );
+
+ U32 countTrailingZeros( U32 m );
+
+protected:
+ S32 mColorPaletteColors;
+ U8* mColorPalette;
+ S32 mBitmapOffset;
+ S32 mBitsPerPixel;
+ U32 mBitfieldMask[4]; // rgba
+ BOOL mOriginAtTop;
+};
+
+#endif
diff --git a/indra/llimage/llimagedxt.cpp b/indra/llimage/llimagedxt.cpp
new file mode 100644
index 0000000000..9ddd044007
--- /dev/null
+++ b/indra/llimage/llimagedxt.cpp
@@ -0,0 +1,475 @@
+/**
+ * @file llimagedxt.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llimagedxt.h"
+
+//static
+void LLImageDXT::checkMinWidthHeight(EFileFormat format, S32& width, S32& height)
+{
+ S32 mindim = (format >= FORMAT_DXT1 && format <= FORMAT_DXR5) ? 4 : 1;
+ width = llmax(width, mindim);
+ height = llmax(height, mindim);
+}
+
+//static
+S32 LLImageDXT::formatBits(EFileFormat format)
+{
+ switch (format)
+ {
+ case FORMAT_DXT1: return 4;
+ case FORMAT_DXR1: return 4;
+ case FORMAT_I8: return 8;
+ case FORMAT_A8: return 8;
+ case FORMAT_DXT3: return 8;
+ case FORMAT_DXR3: return 8;
+ case FORMAT_DXR5: return 8;
+ case FORMAT_DXT5: return 8;
+ case FORMAT_RGB8: return 24;
+ case FORMAT_RGBA8: return 32;
+ default:
+ llerrs << "LLImageDXT::Unknown format: " << format << llendl;
+ return 0;
+ }
+};
+
+//static
+S32 LLImageDXT::formatBytes(EFileFormat format, S32 width, S32 height)
+{
+ checkMinWidthHeight(format, width, height);
+ S32 bytes = ((width*height*formatBits(format)+7)>>3);
+ S32 aligned = (bytes+3)&~3;
+ return aligned;
+}
+
+//static
+S32 LLImageDXT::formatComponents(EFileFormat format)
+{
+ switch (format)
+ {
+ case FORMAT_DXT1: return 3;
+ case FORMAT_DXR1: return 3;
+ case FORMAT_I8: return 1;
+ case FORMAT_A8: return 1;
+ case FORMAT_DXT3: return 4;
+ case FORMAT_DXR3: return 4;
+ case FORMAT_DXT5: return 4;
+ case FORMAT_DXR5: return 4;
+ case FORMAT_RGB8: return 3;
+ case FORMAT_RGBA8: return 4;
+ default:
+ llerrs << "LLImageDXT::Unknown format: " << format << llendl;
+ return 0;
+ }
+};
+
+// static
+LLImageDXT::EFileFormat LLImageDXT::getFormat(S32 fourcc)
+{
+ switch(fourcc)
+ {
+ case 0x20203849: return FORMAT_I8;
+ case 0x20203841: return FORMAT_A8;
+ case 0x20424752: return FORMAT_RGB8;
+ case 0x41424752: return FORMAT_RGBA8;
+ case 0x31525844: return FORMAT_DXR1;
+ case 0x32525844: return FORMAT_DXR2;
+ case 0x33525844: return FORMAT_DXR3;
+ case 0x34525844: return FORMAT_DXR4;
+ case 0x35525844: return FORMAT_DXR5;
+ case 0x31545844: return FORMAT_DXT1;
+ case 0x32545844: return FORMAT_DXT2;
+ case 0x33545844: return FORMAT_DXT3;
+ case 0x34545844: return FORMAT_DXT4;
+ case 0x35545844: return FORMAT_DXT5;
+ default: return FORMAT_UNKNOWN;
+ }
+}
+
+//static
+S32 LLImageDXT::getFourCC(EFileFormat format)
+{
+ switch(format)
+ {
+ case FORMAT_I8: return 0x20203849;
+ case FORMAT_A8: return 0x20203841;
+ case FORMAT_RGB8: return 0x20424752;
+ case FORMAT_RGBA8: return 0x41424752;
+ case FORMAT_DXR1: return 0x31525844;
+ case FORMAT_DXR2: return 0x32525844;
+ case FORMAT_DXR3: return 0x33525844;
+ case FORMAT_DXR4: return 0x34525844;
+ case FORMAT_DXR5: return 0x35525844;
+ case FORMAT_DXT1: return 0x31545844;
+ case FORMAT_DXT2: return 0x32545844;
+ case FORMAT_DXT3: return 0x33545844;
+ case FORMAT_DXT4: return 0x34545844;
+ case FORMAT_DXT5: return 0x35545844;
+ default: return 0x00000000;
+ }
+}
+
+//static
+void LLImageDXT::calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height)
+{
+ while (discard_level > 0 && width > 1 && height > 1)
+ {
+ discard_level--;
+ width >>= 1;
+ height >>= 1;
+ }
+ checkMinWidthHeight(format, width, height);
+}
+
+//static
+S32 LLImageDXT::calcNumMips(S32 width, S32 height)
+{
+ S32 nmips = 0;
+ while (width > 0 && height > 0)
+ {
+ width >>= 1;
+ height >>= 1;
+ nmips++;
+ }
+ return nmips;
+}
+
+//============================================================================
+
+LLImageDXT::LLImageDXT()
+ : LLImageFormatted(IMG_CODEC_DXT),
+ mFileFormat(FORMAT_UNKNOWN),
+ mHeaderSize(0)
+{
+}
+
+LLImageDXT::~LLImageDXT()
+{
+}
+
+// virtual
+BOOL LLImageDXT::updateData()
+{
+ resetLastError();
+
+ U8* data = getData();
+ S32 data_size = getDataSize();
+
+ if (!data || !data_size)
+ {
+ setLastError("LLImageDXT uninitialized");
+ return FALSE;
+ }
+
+ S32 width, height, miplevelmax;
+ dxtfile_header_t* header = (dxtfile_header_t*)data;
+ if (header->fourcc != 0x20534444)
+ {
+ dxtfile_header_old_t* oldheader = (dxtfile_header_old_t*)header;
+ mHeaderSize = sizeof(dxtfile_header_old_t);
+ mFileFormat = EFileFormat(oldheader->format);
+ miplevelmax = llmin(oldheader->maxlevel,MAX_IMAGE_MIP);
+ width = oldheader->maxwidth;
+ height = oldheader->maxheight;
+ }
+ else
+ {
+ mHeaderSize = sizeof(dxtfile_header_t);
+ mFileFormat = getFormat(header->pixel_fmt.fourcc);
+ miplevelmax = llmin(header->num_mips-1,MAX_IMAGE_MIP);
+ width = header->maxwidth;
+ height = header->maxheight;
+ }
+
+ if (data_size < mHeaderSize)
+ {
+ llerrs << "LLImageDXT: not enough data" << llendl;
+ }
+ S32 ncomponents = formatComponents(mFileFormat);
+ setSize(width, height, ncomponents);
+
+ S32 discard = calcDiscardLevelBytes(data_size);
+ discard = llmin(discard, miplevelmax);
+ setDiscardLevel(discard);
+
+ return TRUE;
+}
+
+// discard: 0 = largest (last) mip
+S32 LLImageDXT::getMipOffset(S32 discard)
+{
+ if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXT5)
+ {
+ llerrs << "getMipOffset called with old (unsupported) format" << llendl;
+ }
+ S32 width = getWidth(), height = getHeight();
+ S32 num_mips = calcNumMips(width, height);
+ discard = llclamp(discard, 0, num_mips-1);
+ S32 last_mip = num_mips-1-discard;
+ llassert(mHeaderSize > 0);
+ S32 offset = mHeaderSize;
+ for (S32 mipidx = num_mips-1; mipidx >= 0; mipidx--)
+ {
+ if (mipidx < last_mip)
+ {
+ offset += formatBytes(mFileFormat, width, height);
+ }
+ width >>= 1;
+ height >>= 1;
+ }
+ return offset;
+}
+
+void LLImageDXT::setFormat()
+{
+ S32 ncomponents = getComponents();
+ switch (ncomponents)
+ {
+ case 3: mFileFormat = FORMAT_DXR1; break;
+ case 4: mFileFormat = FORMAT_DXR3; break;
+ default: llerrs << "LLImageDXT::setFormat called with ncomponents = " << ncomponents << llendl;
+ }
+ mHeaderSize = calcHeaderSize();
+}
+
+// virtual
+BOOL LLImageDXT::decode(LLImageRaw* raw_image, F32 time)
+{
+ llassert_always(raw_image);
+
+ if (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5)
+ {
+ llwarns << "Attempt to decode compressed LLImageDXT to Raw (unsupported)" << llendl;
+ return FALSE;
+ }
+
+ S32 width = getWidth(), height = getHeight();
+ S32 ncomponents = getComponents();
+ S32 image_size = formatBytes(mFileFormat, width, height);
+ U8* data = getData() + getMipOffset(0);
+
+ if ((!getData()) || (data + image_size > getData() + getDataSize()))
+ {
+ setLastError("LLImageDXT trying to decode an image with not enough data!");
+ return FALSE;
+ }
+
+ raw_image->resize(width, height, ncomponents);
+ memcpy(raw_image->getData(), data, image_size);
+
+ return TRUE;
+}
+
+// virtual
+BOOL LLImageDXT::requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard, F32 decode_time)
+{
+ if (discard < 0)
+ {
+ discard = mDiscardLevel;
+ }
+ else if (discard < mDiscardLevel)
+ {
+ llerrs << "Request for invalid discard level" << llendl;
+ }
+ U8* data = getData() + getMipOffset(discard);
+ S32 width, height;
+ calcDiscardWidthHeight(discard, mFileFormat, width, height);
+ raw = new LLImageRaw(data, width, height, getComponents());
+ return TRUE;
+}
+
+void LLImageDXT::releaseDecodedData()
+{
+ // nothing to do
+}
+
+BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time, bool explicit_mips)
+{
+ llassert_always(raw_image);
+
+ S32 ncomponents = raw_image->getComponents();
+ EFileFormat format;
+ switch (ncomponents)
+ {
+ case 1:
+ format = FORMAT_A8;
+ break;
+ case 3:
+ format = FORMAT_RGB8;
+ break;
+ case 4:
+ format = FORMAT_RGBA8;
+ break;
+ default:
+ llerrs << "LLImageDXT::encode: Unhandled channel number: " << ncomponents << llendl;
+ return 0;
+ }
+
+ S32 width = raw_image->getWidth();
+ S32 height = raw_image->getHeight();
+
+ if (explicit_mips)
+ {
+ height = (height/3)*2;
+ }
+
+ setSize(width, height, ncomponents);
+ mHeaderSize = sizeof(dxtfile_header_t);
+ mFileFormat = format;
+
+ S32 nmips = calcNumMips(width, height);
+ S32 w = width;
+ S32 h = height;
+
+ S32 totbytes = mHeaderSize;
+ for (S32 mip=0; mip<nmips; mip++)
+ {
+ totbytes += formatBytes(format,w,h);
+ w >>= 1;
+ h >>= 1;
+ }
+
+ allocateData(totbytes);
+
+ U8* data = getData();
+ dxtfile_header_t* header = (dxtfile_header_t*)data;
+ llassert(mHeaderSize > 0);
+ memset(header, 0, mHeaderSize);
+ header->fourcc = 0x20534444;
+ header->pixel_fmt.fourcc = getFourCC(format);
+ header->num_mips = nmips;
+ header->maxwidth = width;
+ header->maxheight = height;
+
+ U8* prev_mipdata = 0;
+ w = width, h = height;
+ for (S32 mip=0; mip<nmips; mip++)
+ {
+ U8* mipdata = data + getMipOffset(mip);
+ S32 bytes = formatBytes(format, w, h);
+ if (mip==0)
+ {
+ memcpy(mipdata, raw_image->getData(), bytes);
+ }
+ else if (explicit_mips)
+ {
+ extractMip(raw_image->getData(), mipdata, width, height, w, h, format);
+ }
+ else
+ {
+ generateMip(prev_mipdata, mipdata, w, h, ncomponents);
+ }
+ w >>= 1;
+ h >>= 1;
+ checkMinWidthHeight(format, w, h);
+ prev_mipdata = mipdata;
+ }
+
+ return TRUE;
+}
+
+// virtual
+BOOL LLImageDXT::encode(const LLImageRaw* raw_image, F32 time)
+{
+ return encode(raw_image, time, false);
+}
+
+// virtual
+bool LLImageDXT::convertToDXR()
+{
+ EFileFormat newformat = FORMAT_UNKNOWN;
+ switch (mFileFormat)
+ {
+ case FORMAT_DXR1:
+ case FORMAT_DXR2:
+ case FORMAT_DXR3:
+ case FORMAT_DXR4:
+ case FORMAT_DXR5:
+ return false; // nothing to do
+ case FORMAT_DXT1: newformat = FORMAT_DXR1; break;
+ case FORMAT_DXT2: newformat = FORMAT_DXR2; break;
+ case FORMAT_DXT3: newformat = FORMAT_DXR3; break;
+ case FORMAT_DXT4: newformat = FORMAT_DXR4; break;
+ case FORMAT_DXT5: newformat = FORMAT_DXR5; break;
+ default:
+ llwarns << "convertToDXR: can not convert format: " << llformat("0x%08x",getFourCC(mFileFormat)) << llendl;
+ return false;
+ }
+ mFileFormat = newformat;
+ S32 width = getWidth(), height = getHeight();
+ S32 nmips = calcNumMips(width,height);
+ S32 total_bytes = getDataSize();
+ U8* olddata = getData();
+ U8* newdata = new U8[total_bytes];
+ llassert(total_bytes > 0);
+ memset(newdata, 0, total_bytes);
+ memcpy(newdata, olddata, mHeaderSize);
+ for (S32 mip=0; mip<nmips; mip++)
+ {
+ S32 bytes = formatBytes(mFileFormat, width, height);
+ S32 newoffset = getMipOffset(mip);
+ S32 oldoffset = mHeaderSize + (total_bytes - newoffset - bytes);
+ memcpy(newdata + newoffset, olddata + oldoffset, bytes);
+ width >>= 1;
+ height >>= 1;
+ }
+ dxtfile_header_t* header = (dxtfile_header_t*)newdata;
+ header->pixel_fmt.fourcc = getFourCC(newformat);
+ setData(newdata, total_bytes);
+ return true;
+}
+
+// virtual
+S32 LLImageDXT::calcHeaderSize()
+{
+ return llmax(sizeof(dxtfile_header_old_t), sizeof(dxtfile_header_t));
+}
+
+// virtual
+S32 LLImageDXT::calcDataSize(S32 discard_level)
+{
+ if (mFileFormat == FORMAT_UNKNOWN)
+ {
+ llerrs << "calcDataSize called with unloaded LLImageDXT" << llendl;
+ return 0;
+ }
+ if (discard_level < 0)
+ {
+ discard_level = mDiscardLevel;
+ }
+ S32 bytes = getMipOffset(discard_level); // size of header + previous mips
+ S32 w = getWidth() >> discard_level;
+ S32 h = getHeight() >> discard_level;
+ bytes += formatBytes(mFileFormat,w,h);
+ return bytes;
+}
+
+//============================================================================
+
+//static
+void LLImageDXT::extractMip(const U8 *indata, U8* mipdata, int width, int height,
+ int mip_width, int mip_height, EFileFormat format)
+{
+ int initial_offset = formatBytes(format, width, height);
+ int line_width = formatBytes(format, width, 1);
+ int mip_line_width = formatBytes(format, mip_width, 1);
+ int line_offset = 0;
+
+ for (int ww=width>>1; ww>mip_width; ww>>=1)
+ {
+ line_offset += formatBytes(format, ww, 1);
+ }
+
+ for (int h=0;h<mip_height;++h)
+ {
+ int start_offset = initial_offset + line_width * h + line_offset;
+ memcpy(mipdata + mip_line_width*h, indata + start_offset, mip_line_width);
+ }
+}
+
+//============================================================================
diff --git a/indra/llimage/llimagedxt.h b/indra/llimage/llimagedxt.h
new file mode 100644
index 0000000000..88d28a2958
--- /dev/null
+++ b/indra/llimage/llimagedxt.h
@@ -0,0 +1,120 @@
+/**
+ * @file llimagedxt.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGEDXT_H
+#define LL_LLIMAGEDXT_H
+
+#include "llimage.h"
+
+// This class decodes and encodes LL DXT files (which may unclude uncompressed RGB or RGBA mipped data)
+
+class LLImageDXT : public LLImageFormatted
+{
+public:
+ enum EFileFormat
+ {
+ FORMAT_UNKNOWN = 0,
+ FORMAT_I8 = 1,
+ FORMAT_A8,
+ FORMAT_RGB8,
+ FORMAT_RGBA8,
+ FORMAT_DXT1,
+ FORMAT_DXT2,
+ FORMAT_DXT3,
+ FORMAT_DXT4,
+ FORMAT_DXT5,
+ FORMAT_DXR1,
+ FORMAT_DXR2,
+ FORMAT_DXR3,
+ FORMAT_DXR4,
+ FORMAT_DXR5,
+ FORMAT_NOFILE = 0xff,
+ };
+
+ struct dxtfile_header_old_t
+ {
+ S32 format;
+ S32 maxlevel;
+ S32 maxwidth;
+ S32 maxheight;
+ };
+
+ struct dxtfile_header_t
+ {
+ S32 fourcc;
+ // begin DDSURFACEDESC2 struct
+ S32 header_size; // size of the header
+ S32 flags; // flags - unused
+ S32 maxheight;
+ S32 maxwidth;
+ S32 image_size; // size of the compressed image
+ S32 depth;
+ S32 num_mips;
+ S32 reserved[11];
+ struct pixel_format
+ {
+ S32 struct_size; // size of this structure
+ S32 flags;
+ S32 fourcc;
+ S32 bit_count;
+ S32 r_mask;
+ S32 g_mask;
+ S32 b_mask;
+ S32 a_mask;
+ } pixel_fmt;
+ S32 caps[4];
+ S32 reserved2;
+ };
+
+protected:
+ /*virtual*/ ~LLImageDXT();
+
+public:
+ LLImageDXT();
+
+ /*virtual*/ BOOL updateData();
+
+ /*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 time=0.0);
+ BOOL encode(const LLImageRaw* raw_image, F32 time, bool explicit_mips);
+ /*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 time=0.0);
+
+ /*virtual*/ BOOL requestDecodedData(LLPointer<LLImageRaw>& raw, S32 discard=-1, F32 decode_time=0.0);
+ /*virtual*/ void releaseDecodedData();
+
+ /*virtual*/ S32 calcHeaderSize();
+ /*virtual*/ S32 calcDataSize(S32 discard_level = 0);
+
+ void setFormat();
+ S32 getMipOffset(S32 discard);
+
+ EFileFormat getFileFormat() { return mFileFormat; }
+ bool isCompressed() { return (mFileFormat >= FORMAT_DXT1 && mFileFormat <= FORMAT_DXR5); }
+
+ bool convertToDXR(); // convert from DXT to DXR
+
+ static void checkMinWidthHeight(EFileFormat format, S32& width, S32& height);
+ static S32 formatBits(EFileFormat format);
+ static S32 formatBytes(EFileFormat format, S32 width, S32 height);
+ static S32 formatOffset(EFileFormat format, S32 width, S32 height, S32 max_width, S32 max_height);
+ static S32 formatComponents(EFileFormat format);
+
+ static EFileFormat getFormat(S32 fourcc);
+ static S32 getFourCC(EFileFormat format);
+
+ static void calcDiscardWidthHeight(S32 discard_level, EFileFormat format, S32& width, S32& height);
+ static S32 calcNumMips(S32 width, S32 height);
+
+private:
+ static void extractMip(const U8 *indata, U8* mipdata, int width, int height,
+ int mip_width, int mip_height, EFileFormat format);
+
+private:
+ EFileFormat mFileFormat;
+ S32 mHeaderSize;
+};
+
+#endif
diff --git a/indra/llimage/llimagej2c.cpp b/indra/llimage/llimagej2c.cpp
new file mode 100644
index 0000000000..ad07700a37
--- /dev/null
+++ b/indra/llimage/llimagej2c.cpp
@@ -0,0 +1,249 @@
+/**
+ * @file llimagej2c.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#ifndef LL_USE_KDU
+#define LL_USE_KDU 1
+#endif // LL_USE_KDU
+
+#include "llimagej2c.h"
+#include "llmemory.h"
+#if LL_USE_KDU
+#include "llimagej2ckdu.h"
+#endif
+
+#include "llimagej2coj.h"
+
+
+LLImageJ2C::LLImageJ2C() : LLImageFormatted(IMG_CODEC_J2C),
+ mMaxBytes(0),
+ mRawDiscardLevel(-1),
+ mRate(0.0f)
+{
+#if LL_USE_KDU
+ mImpl = new LLImageJ2CKDU();
+#else
+ mImpl = new LLImageJ2COJ();
+#endif
+}
+
+// virtual
+LLImageJ2C::~LLImageJ2C()
+{
+ delete mImpl;
+}
+
+// virtual
+S8 LLImageJ2C::getRawDiscardLevel()
+{
+ return mRawDiscardLevel;
+}
+
+BOOL LLImageJ2C::updateData()
+{
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (getDataSize() < 16))
+ {
+ setLastError("LLImageJ2C uninitialized");
+ return FALSE;
+ }
+
+ if (!mImpl->getMetadata(*this))
+ {
+ return FALSE;
+ }
+ // SJB: override discard based on mMaxBytes elsewhere
+ S32 max_bytes = getDataSize(); // mMaxBytes ? mMaxBytes : getDataSize();
+ S32 discard = calcDiscardLevelBytes(max_bytes);
+ setDiscardLevel(discard);
+
+ return TRUE;
+}
+
+
+BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time)
+{
+ return decode(raw_imagep, decode_time, 0, 4);
+}
+
+
+BOOL LLImageJ2C::decode(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count )
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (getDataSize() < 16))
+ {
+ setLastError("LLImageJ2C uninitialized");
+ return FALSE;
+ }
+
+ // Update the raw discard level
+ updateRawDiscardLevel();
+
+ return mImpl->decodeImpl(*this, *raw_imagep, decode_time, 0, 4);
+}
+
+
+BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, F32 encode_time)
+{
+ return encode(raw_imagep, NULL, encode_time);
+}
+
+
+BOOL LLImageJ2C::encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ return mImpl->encodeImpl(*this, *raw_imagep, comment_text, encode_time);
+}
+
+//static
+S32 LLImageJ2C::calcHeaderSizeJ2C()
+{
+ return 600; //2048; // ??? hack... just needs to be >= actual header size...
+}
+
+//static
+S32 LLImageJ2C::calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate)
+{
+ if (rate <= 0.f) rate = .125f;
+ while (discard_level > 0)
+ {
+ if (w < 1 || h < 1)
+ break;
+ w >>= 1;
+ h >>= 1;
+ discard_level--;
+ }
+ S32 bytes = (S32)((F32)(w*h*comp)*rate);
+ bytes = llmax(bytes, calcHeaderSizeJ2C());
+ return bytes;
+}
+
+S32 LLImageJ2C::calcHeaderSize()
+{
+ return calcHeaderSizeJ2C();
+}
+
+S32 LLImageJ2C::calcDataSize(S32 discard_level)
+{
+ return calcDataSizeJ2C(getWidth(), getHeight(), getComponents(), discard_level, mRate);
+}
+
+S32 LLImageJ2C::calcDiscardLevelBytes(S32 bytes)
+{
+ llassert(bytes >= 0);
+ S32 discard_level = 0;
+ if (bytes == 0)
+ {
+ return MAX_DISCARD_LEVEL;
+ }
+ while (1)
+ {
+ S32 bytes_needed = calcDataSize(discard_level); // virtual
+ if (bytes >= bytes_needed - (bytes_needed>>2)) // For J2c, up the res at 75% of the optimal number of bytes
+ {
+ break;
+ }
+ discard_level++;
+ if (discard_level >= MAX_DISCARD_LEVEL)
+ {
+ break;
+ }
+ }
+ return discard_level;
+}
+
+void LLImageJ2C::setRate(F32 rate)
+{
+ mRate = rate;
+}
+
+void LLImageJ2C::setMaxBytes(S32 max_bytes)
+{
+ mMaxBytes = max_bytes;
+}
+// NOT USED
+// void LLImageJ2C::setReversible(const BOOL reversible)
+// {
+// mReversible = reversible;
+// }
+
+
+BOOL LLImageJ2C::loadAndValidate(const LLString &filename)
+{
+ resetLastError();
+
+ S32 file_size = 0;
+ apr_file_t* apr_file = ll_apr_file_open(filename, LL_APR_RB, &file_size);
+ if (!apr_file)
+ {
+ setLastError("Unable to open file for reading", filename);
+ return FALSE;
+ }
+ if (file_size == 0)
+ {
+ setLastError("File is empty",filename);
+ apr_file_close(apr_file);
+ return FALSE;
+ }
+
+ U8 *data = new U8[file_size];
+ apr_size_t bytes_read = file_size;
+ apr_status_t s = apr_file_read(apr_file, data, &bytes_read); // modifies bytes_read
+ if (s != APR_SUCCESS || bytes_read != file_size)
+ {
+ delete[] data;
+ setLastError("Unable to read entire file");
+ return FALSE;
+ }
+ apr_file_close(apr_file);
+
+ return validate(data, file_size);
+}
+
+
+BOOL LLImageJ2C::validate(U8 *data, U32 file_size)
+{
+ LLMemType mt1((LLMemType::EMemType)mMemType);
+ // Taken from setData()
+
+ BOOL res = LLImageFormatted::setData(data, file_size);
+ if ( !res )
+ {
+ return FALSE;
+ }
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImageJ2C uninitialized");
+ return FALSE;
+ }
+
+ return mImpl->getMetadata(*this);
+}
+
+void LLImageJ2C::setDecodingDone(BOOL complete)
+{
+ mDecoding = FALSE;
+ mDecoded = complete;
+}
+
+void LLImageJ2C::updateRawDiscardLevel()
+{
+ mRawDiscardLevel = mMaxBytes ? calcDiscardLevelBytes(mMaxBytes) : mDiscardLevel;
+}
+
+LLImageJ2CImpl::~LLImageJ2CImpl()
+{
+}
diff --git a/indra/llimage/llimagej2c.h b/indra/llimage/llimagej2c.h
new file mode 100644
index 0000000000..ee73612bc7
--- /dev/null
+++ b/indra/llimage/llimagej2c.h
@@ -0,0 +1,77 @@
+/**
+ * @file llimagej2c.h
+ * @brief Image implmenation for jpeg2000.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGEJ2C_H
+#define LL_LLIMAGEJ2C_H
+
+#include "llimage.h"
+#include "llassettype.h"
+
+class LLImageJ2CImpl;
+
+class LLImageJ2C : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageJ2C();
+
+public:
+ LLImageJ2C();
+
+ // Base class overrides
+ /*virtual*/ BOOL updateData();
+ /*virtual*/ BOOL decode(LLImageRaw *raw_imagep, F32 decode_time=0.0);
+ /*virtual*/ BOOL decode(LLImageRaw *raw_imagep, F32 decode_time, S32 first_channel, S32 max_channel_count);
+ /*virtual*/ BOOL encode(const LLImageRaw *raw_imagep, F32 encode_time=0.0);
+ /*virtual*/ S32 calcHeaderSize();
+ /*virtual*/ S32 calcDataSize(S32 discard_level = 0);
+ /*virtual*/ S32 calcDiscardLevelBytes(S32 bytes);
+ /*virtual*/ S8 getRawDiscardLevel();
+
+ // Encode with comment text
+ BOOL encode(const LLImageRaw *raw_imagep, const char* comment_text, F32 encode_time=0.0);
+
+ BOOL validate(U8 *data, U32 file_size);
+ BOOL loadAndValidate(const LLString &filename);
+
+ // Encode accessors
+ void setReversible(const BOOL reversible); // Use non-lossy?
+ void setRate(F32 rate);
+ void setMaxBytes(S32 max_bytes);
+ S32 getMaxBytes() const { return mMaxBytes; }
+
+ static S32 calcHeaderSizeJ2C();
+ static S32 calcDataSizeJ2C(S32 w, S32 h, S32 comp, S32 discard_level, F32 rate = 0.f);
+
+protected:
+ friend class LLImageJ2CImpl;
+ friend class LLImageJ2COJ;
+ friend class LLImageJ2CKDU;
+ void setDecodingDone(BOOL complete = TRUE);
+ void updateRawDiscardLevel();
+
+ S32 mMaxBytes; // Maximum number of bytes of data to use...
+ S8 mRawDiscardLevel;
+ F32 mRate;
+ LLImageJ2CImpl *mImpl;
+};
+
+// Derive from this class to implement JPEG2000 decoding
+class LLImageJ2CImpl
+{
+protected:
+ virtual ~LLImageJ2CImpl();
+ virtual BOOL getMetadata(LLImageJ2C &base) = 0;
+ virtual BOOL decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 decode_time, S32 first_channel, S32 max_channel_count) = 0;
+ virtual BOOL encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, const char* comment_text, F32 encode_time=0.0) = 0;
+
+ friend class LLImageJ2C;
+};
+
+#define LINDEN_J2C_COMMENT_PREFIX "LL_"
+
+#endif
diff --git a/indra/llimage/llimagejpeg.cpp b/indra/llimage/llimagejpeg.cpp
new file mode 100644
index 0000000000..c75e449db5
--- /dev/null
+++ b/indra/llimage/llimagejpeg.cpp
@@ -0,0 +1,602 @@
+/**
+ * @file llimagejpeg.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "stdtypes.h"
+
+#include "llimagejpeg.h"
+
+#include "llerror.h"
+
+LLImageJPEG::LLImageJPEG()
+ :
+ LLImageFormatted(IMG_CODEC_JPEG),
+ mOutputBuffer( NULL ),
+ mOutputBufferSize( 0 ),
+ mEncodeQuality( 75 ) // on a scale from 1 to 100
+{
+}
+
+LLImageJPEG::~LLImageJPEG()
+{
+ llassert( !mOutputBuffer ); // Should already be deleted at end of encode.
+ delete[] mOutputBuffer;
+}
+
+BOOL LLImageJPEG::updateData()
+{
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("Uninitialized instance of LLImageJPEG");
+ return FALSE;
+ }
+
+ ////////////////////////////////////////
+ // Step 1: allocate and initialize JPEG decompression object
+
+ // This struct contains the JPEG decompression parameters and pointers to
+ // working space (which is allocated as needed by the JPEG library).
+ struct jpeg_decompress_struct cinfo;
+ cinfo.client_data = this;
+
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Customize with our own callbacks
+ jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
+ jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
+ jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
+
+ try
+ {
+ // Now we can initialize the JPEG decompression object.
+ jpeg_create_decompress(&cinfo);
+
+ ////////////////////////////////////////
+ // Step 2: specify data source
+ // (Code is modified version of jpeg_stdio_src();
+ if (cinfo.src == NULL)
+ {
+ cinfo.src = (struct jpeg_source_mgr *)
+ (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
+ sizeof(struct jpeg_source_mgr));
+ }
+ cinfo.src->init_source = &LLImageJPEG::decodeInitSource;
+ cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer;
+ cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData;
+ cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better.
+ cinfo.src->term_source = &LLImageJPEG::decodeTermSource;
+
+ cinfo.src->bytes_in_buffer = getDataSize();
+ cinfo.src->next_input_byte = getData();
+
+ ////////////////////////////////////////
+ // Step 3: read file parameters with jpeg_read_header()
+ jpeg_read_header( &cinfo, TRUE );
+
+ // Data set by jpeg_read_header
+ setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB)
+
+ /*
+ // More data set by jpeg_read_header
+ cinfo.num_components;
+ cinfo.jpeg_color_space; // Colorspace of image
+ cinfo.saw_JFIF_marker; // TRUE if a JFIF APP0 marker was seen
+ cinfo.JFIF_major_version; // Version information from JFIF marker
+ cinfo.JFIF_minor_version; //
+ cinfo.density_unit; // Resolution data from JFIF marker
+ cinfo.X_density;
+ cinfo.Y_density;
+ cinfo.saw_Adobe_marker; // TRUE if an Adobe APP14 marker was seen
+ cinfo.Adobe_transform; // Color transform code from Adobe marker
+ */
+ }
+ catch (int)
+ {
+ jpeg_destroy_decompress(&cinfo);
+
+ return FALSE;
+ }
+ ////////////////////////////////////////
+ // Step 4: Release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+
+ return TRUE;
+}
+
+// Initialize source --- called by jpeg_read_header
+// before any data is actually read.
+void LLImageJPEG::decodeInitSource( j_decompress_ptr cinfo )
+{
+ // no work necessary here
+}
+
+// Fill the input buffer --- called whenever buffer is emptied.
+boolean LLImageJPEG::decodeFillInputBuffer( j_decompress_ptr cinfo )
+{
+// jpeg_source_mgr* src = cinfo->src;
+// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ // Should never get here, since we provide the entire buffer up front.
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
+
+ return TRUE;
+}
+
+// Skip data --- used to skip over a potentially large amount of
+// uninteresting data (such as an APPn marker).
+//
+// Writers of suspendable-input applications must note that skip_input_data
+// is not granted the right to give a suspension return. If the skip extends
+// beyond the data currently in the buffer, the buffer can be marked empty so
+// that the next read will cause a fill_input_buffer call that can suspend.
+// Arranging for additional bytes to be discarded before reloading the input
+// buffer is the application writer's problem.
+void LLImageJPEG::decodeSkipInputData (j_decompress_ptr cinfo, long num_bytes)
+{
+ jpeg_source_mgr* src = cinfo->src;
+// LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ src->next_input_byte += (size_t) num_bytes;
+ src->bytes_in_buffer -= (size_t) num_bytes;
+}
+
+void LLImageJPEG::decodeTermSource (j_decompress_ptr cinfo)
+{
+ // no work necessary here
+}
+
+
+BOOL LLImageJPEG::decode(LLImageRaw* raw_image, F32 decode_time)
+{
+ llassert_always(raw_image);
+
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImageJPEG trying to decode an image with no data!");
+ return FALSE;
+ }
+
+ S32 row_stride = 0;
+ U8* raw_image_data = NULL;
+
+ ////////////////////////////////////////
+ // Step 1: allocate and initialize JPEG decompression object
+
+ // This struct contains the JPEG decompression parameters and pointers to
+ // working space (which is allocated as needed by the JPEG library).
+ struct jpeg_decompress_struct cinfo;
+
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Customize with our own callbacks
+ jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
+ jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
+ jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
+
+
+ try
+ {
+ // Now we can initialize the JPEG decompression object.
+ jpeg_create_decompress(&cinfo);
+
+ ////////////////////////////////////////
+ // Step 2: specify data source
+ // (Code is modified version of jpeg_stdio_src();
+ if (cinfo.src == NULL)
+ {
+ cinfo.src = (struct jpeg_source_mgr *)
+ (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
+ sizeof(struct jpeg_source_mgr));
+ }
+ cinfo.src->init_source = &LLImageJPEG::decodeInitSource;
+ cinfo.src->fill_input_buffer = &LLImageJPEG::decodeFillInputBuffer;
+ cinfo.src->skip_input_data = &LLImageJPEG::decodeSkipInputData;
+ cinfo.src->resync_to_restart = jpeg_resync_to_restart; // For now, use default method, but we should be able to do better.
+ cinfo.src->term_source = &LLImageJPEG::decodeTermSource;
+ cinfo.src->bytes_in_buffer = getDataSize();
+ cinfo.src->next_input_byte = getData();
+
+ ////////////////////////////////////////
+ // Step 3: read file parameters with jpeg_read_header()
+
+ jpeg_read_header(&cinfo, TRUE);
+
+ // We can ignore the return value from jpeg_read_header since
+ // (a) suspension is not possible with our data source, and
+ // (b) we passed TRUE to reject a tables-only JPEG file as an error.
+ // See libjpeg.doc for more info.
+
+ setSize(cinfo.image_width, cinfo.image_height, 3); // Force to 3 components (RGB)
+
+ raw_image->resize(getWidth(), getHeight(), getComponents());
+ raw_image_data = raw_image->getData();
+
+
+ ////////////////////////////////////////
+ // Step 4: set parameters for decompression
+ cinfo.out_color_components = 3;
+ cinfo.out_color_space = JCS_RGB;
+
+
+ ////////////////////////////////////////
+ // Step 5: Start decompressor
+
+ jpeg_start_decompress(&cinfo);
+ // We can ignore the return value since suspension is not possible
+ // with our data source.
+
+ // We may need to do some setup of our own at this point before reading
+ // the data. After jpeg_start_decompress() we have the correct scaled
+ // output image dimensions available, as well as the output colormap
+ // if we asked for color quantization.
+ // In this example, we need to make an output work buffer of the right size.
+
+ // JSAMPLEs per row in output buffer
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ ////////////////////////////////////////
+ // Step 6: while (scan lines remain to be read)
+ // jpeg_read_scanlines(...);
+
+ // Here we use the library's state variable cinfo.output_scanline as the
+ // loop counter, so that we don't have to keep track ourselves.
+
+ // Move pointer to last line
+ raw_image_data += row_stride * (cinfo.output_height - 1);
+
+ while (cinfo.output_scanline < cinfo.output_height)
+ {
+ // jpeg_read_scanlines expects an array of pointers to scanlines.
+ // Here the array is only one element long, but you could ask for
+ // more than one scanline at a time if that's more convenient.
+
+ jpeg_read_scanlines(&cinfo, &raw_image_data, 1);
+ raw_image_data -= row_stride; // move pointer up a line
+ }
+
+ ////////////////////////////////////////
+ // Step 7: Finish decompression
+ jpeg_finish_decompress(&cinfo);
+
+ ////////////////////////////////////////
+ // Step 8: Release JPEG decompression object
+ jpeg_destroy_decompress(&cinfo);
+ }
+
+ catch (int)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ return FALSE;
+ }
+
+ // Check to see whether any corrupt-data warnings occurred
+ if( jerr.num_warnings != 0 )
+ {
+ // TODO: extract the warning to find out what went wrong.
+ setLastError( "Unable to decode JPEG image.");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+// Initialize destination --- called by jpeg_start_compress before any data is actually written.
+// static
+void LLImageJPEG::encodeInitDestination ( j_compress_ptr cinfo )
+{
+ LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ cinfo->dest->next_output_byte = self->mOutputBuffer;
+ cinfo->dest->free_in_buffer = self->mOutputBufferSize;
+}
+
+
+// Empty the output buffer --- called whenever buffer fills up.
+//
+// In typical applications, this should write the entire output buffer
+// (ignoring the current state of next_output_byte & free_in_buffer),
+// reset the pointer & count to the start of the buffer, and return TRUE
+// indicating that the buffer has been dumped.
+//
+// In applications that need to be able to suspend compression due to output
+// overrun, a FALSE return indicates that the buffer cannot be emptied now.
+// In this situation, the compressor will return to its caller (possibly with
+// an indication that it has not accepted all the supplied scanlines). The
+// application should resume compression after it has made more room in the
+// output buffer. Note that there are substantial restrictions on the use of
+// suspension --- see the documentation.
+//
+// When suspending, the compressor will back up to a convenient restart point
+// (typically the start of the current MCU). next_output_byte & free_in_buffer
+// indicate where the restart point will be if the current call returns FALSE.
+// Data beyond this point will be regenerated after resumption, so do not
+// write it out when emptying the buffer externally.
+
+boolean LLImageJPEG::encodeEmptyOutputBuffer( j_compress_ptr cinfo )
+{
+ LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ // Should very rarely happen, since our output buffer is
+ // as large as the input to start out with.
+
+ // Double the buffer size;
+ S32 new_buffer_size = self->mOutputBufferSize * 2;
+ U8* new_buffer = new U8[ new_buffer_size ];
+ memcpy( new_buffer, self->mOutputBuffer, self->mOutputBufferSize );
+ delete[] self->mOutputBuffer;
+ self->mOutputBuffer = new_buffer;
+
+ cinfo->dest->next_output_byte = self->mOutputBuffer + self->mOutputBufferSize;
+ cinfo->dest->free_in_buffer = self->mOutputBufferSize;
+ self->mOutputBufferSize = new_buffer_size;
+
+ return TRUE;
+}
+
+// Terminate destination --- called by jpeg_finish_compress
+// after all data has been written. Usually needs to flush buffer.
+//
+// NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+// application must deal with any cleanup that should happen even
+// for error exit.
+void LLImageJPEG::encodeTermDestination( j_compress_ptr cinfo )
+{
+ LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ S32 file_bytes = (S32)(self->mOutputBufferSize - cinfo->dest->free_in_buffer);
+ self->allocateData(file_bytes);
+
+ memcpy( self->getData(), self->mOutputBuffer, file_bytes );
+}
+
+// static
+void LLImageJPEG::errorExit( j_common_ptr cinfo )
+{
+ //LLImageJPEG* self = (LLImageJPEG*) cinfo->client_data;
+
+ // Always display the message
+ (*cinfo->err->output_message)(cinfo);
+
+ // Let the memory manager delete any temp files
+ jpeg_destroy(cinfo);
+
+ // Return control to the setjmp point
+ throw 1;
+}
+
+// Decide whether to emit a trace or warning message.
+// msg_level is one of:
+// -1: recoverable corrupt-data warning, may want to abort.
+// 0: important advisory messages (always display to user).
+// 1: first level of tracing detail.
+// 2,3,...: successively more detailed tracing messages.
+// An application might override this method if it wanted to abort on warnings
+// or change the policy about which messages to display.
+// static
+void LLImageJPEG::errorEmitMessage( j_common_ptr cinfo, int msg_level )
+{
+ struct jpeg_error_mgr * err = cinfo->err;
+
+ if (msg_level < 0)
+ {
+ // It's a warning message. Since corrupt files may generate many warnings,
+ // the policy implemented here is to show only the first warning,
+ // unless trace_level >= 3.
+ if (err->num_warnings == 0 || err->trace_level >= 3)
+ {
+ (*err->output_message) (cinfo);
+ }
+ // Always count warnings in num_warnings.
+ err->num_warnings++;
+ }
+ else
+ {
+ // It's a trace message. Show it if trace_level >= msg_level.
+ if (err->trace_level >= msg_level)
+ {
+ (*err->output_message) (cinfo);
+ }
+ }
+}
+
+// static
+void LLImageJPEG::errorOutputMessage( j_common_ptr cinfo )
+{
+ // Create the message
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message) (cinfo, buffer);
+
+ ((LLImageJPEG*) cinfo->client_data)->setLastError( buffer );
+
+ BOOL is_decode = (cinfo->is_decompressor != 0);
+ llwarns << "LLImageJPEG " << (is_decode ? "decode " : "encode ") << " failed: " << buffer << llendl;
+}
+
+BOOL LLImageJPEG::encode( const LLImageRaw* raw_image, F32 encode_time )
+{
+ llassert_always(raw_image);
+
+ resetLastError();
+
+ switch( raw_image->getComponents() )
+ {
+ case 1:
+ case 3:
+ break;
+ default:
+ setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components.");
+ return FALSE;
+ }
+
+ setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
+
+ // Allocate a temporary buffer big enough to hold the entire compressed image (and then some)
+ // (Note: we make it bigger in emptyOutputBuffer() if we need to)
+ delete[] mOutputBuffer;
+ mOutputBufferSize = getWidth() * getHeight() * getComponents() + 1024;
+ mOutputBuffer = new U8[ mOutputBufferSize ];
+
+ const U8* raw_image_data = NULL;
+ S32 row_stride = 0;
+
+ ////////////////////////////////////////
+ // Step 1: allocate and initialize JPEG compression object
+
+ // This struct contains the JPEG compression parameters and pointers to
+ // working space (which is allocated as needed by the JPEG library).
+ struct jpeg_compress_struct cinfo;
+ cinfo.client_data = this;
+
+ // We have to set up the error handler first, in case the initialization
+ // step fails. (Unlikely, but it could happen if you are out of memory.)
+ // This routine fills in the contents of struct jerr, and returns jerr's
+ // address which we place into the link field in cinfo.
+ struct jpeg_error_mgr jerr;
+ cinfo.err = jpeg_std_error(&jerr);
+
+ // Customize with our own callbacks
+ jerr.error_exit = &LLImageJPEG::errorExit; // Error exit handler: does not return to caller
+ jerr.emit_message = &LLImageJPEG::errorEmitMessage; // Conditionally emit a trace or warning message
+ jerr.output_message = &LLImageJPEG::errorOutputMessage; // Routine that actually outputs a trace or error message
+
+ // Establish the setjmp return context mSetjmpBuffer. Used by library to abort.
+ if( setjmp(mSetjmpBuffer) )
+ {
+ // If we get here, the JPEG code has signaled an error.
+ // We need to clean up the JPEG object, close the input file, and return.
+ jpeg_destroy_compress(&cinfo);
+ delete[] mOutputBuffer;
+ mOutputBuffer = NULL;
+ mOutputBufferSize = 0;
+ return FALSE;
+ }
+
+ try
+ {
+
+ // Now we can initialize the JPEG compression object.
+ jpeg_create_compress(&cinfo);
+
+ ////////////////////////////////////////
+ // Step 2: specify data destination
+ // (code is a modified form of jpeg_stdio_dest() )
+ if( cinfo.dest == NULL)
+ {
+ cinfo.dest = (struct jpeg_destination_mgr *)
+ (*cinfo.mem->alloc_small) ((j_common_ptr) &cinfo, JPOOL_PERMANENT,
+ sizeof(struct jpeg_destination_mgr));
+ }
+ cinfo.dest->next_output_byte = mOutputBuffer; // => next byte to write in buffer
+ cinfo.dest->free_in_buffer = mOutputBufferSize; // # of byte spaces remaining in buffer
+ cinfo.dest->init_destination = &LLImageJPEG::encodeInitDestination;
+ cinfo.dest->empty_output_buffer = &LLImageJPEG::encodeEmptyOutputBuffer;
+ cinfo.dest->term_destination = &LLImageJPEG::encodeTermDestination;
+
+ ////////////////////////////////////////
+ // Step 3: set parameters for compression
+ //
+ // First we supply a description of the input image.
+ // Four fields of the cinfo struct must be filled in:
+
+ cinfo.image_width = getWidth(); // image width and height, in pixels
+ cinfo.image_height = getHeight();
+
+ switch( getComponents() )
+ {
+ case 1:
+ cinfo.input_components = 1; // # of color components per pixel
+ cinfo.in_color_space = JCS_GRAYSCALE; // colorspace of input image
+ break;
+ case 3:
+ cinfo.input_components = 3; // # of color components per pixel
+ cinfo.in_color_space = JCS_RGB; // colorspace of input image
+ break;
+ default:
+ setLastError("Unable to encode a JPEG image that doesn't have 1 or 3 components.");
+ return FALSE;
+ }
+
+ // Now use the library's routine to set default compression parameters.
+ // (You must set at least cinfo.in_color_space before calling this,
+ // since the defaults depend on the source color space.)
+ jpeg_set_defaults(&cinfo);
+
+ // Now you can set any non-default parameters you wish to.
+ jpeg_set_quality(&cinfo, mEncodeQuality, TRUE ); // limit to baseline-JPEG values
+
+ ////////////////////////////////////////
+ // Step 4: Start compressor
+ //
+ // TRUE ensures that we will write a complete interchange-JPEG file.
+ // Pass TRUE unless you are very sure of what you're doing.
+
+ jpeg_start_compress(&cinfo, TRUE);
+
+ ////////////////////////////////////////
+ // Step 5: while (scan lines remain to be written)
+ // jpeg_write_scanlines(...);
+
+ // Here we use the library's state variable cinfo.next_scanline as the
+ // loop counter, so that we don't have to keep track ourselves.
+ // To keep things simple, we pass one scanline per call; you can pass
+ // more if you wish, though.
+
+ row_stride = getWidth() * getComponents(); // JSAMPLEs per row in image_buffer
+
+ // NOTE: For compatibility with LLImage, we need to invert the rows.
+ raw_image_data = raw_image->getData();
+
+ const U8* last_row_data = raw_image_data + (getHeight()-1) * row_stride;
+
+ JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s]
+ while (cinfo.next_scanline < cinfo.image_height)
+ {
+ // jpeg_write_scanlines expects an array of pointers to scanlines.
+ // Here the array is only one element long, but you could pass
+ // more than one scanline at a time if that's more convenient.
+
+ //Ugly const uncast here (jpeg_write_scanlines should take a const* but doesn't)
+ //row_pointer[0] = (JSAMPROW)(raw_image_data + (cinfo.next_scanline * row_stride));
+ row_pointer[0] = (JSAMPROW)(last_row_data - (cinfo.next_scanline * row_stride));
+
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ ////////////////////////////////////////
+ // Step 6: Finish compression
+ jpeg_finish_compress(&cinfo);
+
+ // After finish_compress, we can release the temp output buffer.
+ delete[] mOutputBuffer;
+ mOutputBuffer = NULL;
+ mOutputBufferSize = 0;
+
+ ////////////////////////////////////////
+ // Step 7: release JPEG compression object
+ jpeg_destroy_compress(&cinfo);
+ }
+
+ catch(int)
+ {
+ jpeg_destroy_compress(&cinfo);
+ delete[] mOutputBuffer;
+ mOutputBuffer = NULL;
+ mOutputBufferSize = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/indra/llimage/llimagejpeg.h b/indra/llimage/llimagejpeg.h
new file mode 100644
index 0000000000..5684e3720f
--- /dev/null
+++ b/indra/llimage/llimagejpeg.h
@@ -0,0 +1,63 @@
+/**
+ * @file llimagejpeg.h
+ * @brief This class compresses and decompresses JPEG files
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGEJPEG_H
+#define LL_LLIMAGEJPEG_H
+
+#include <setjmp.h>
+
+#include "llimage.h"
+
+extern "C" {
+#include "jpeglib/jinclude.h"
+#include "jpeglib/jpeglib.h"
+#include "jpeglib/jerror.h"
+}
+
+class LLImageJPEG : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageJPEG();
+
+public:
+ LLImageJPEG();
+
+ /*virtual*/ BOOL updateData();
+ /*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 time=0.0);
+ /*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 time=0.0);
+
+ void setEncodeQuality( S32 q ) { mEncodeQuality = q; } // on a scale from 1 to 100
+ S32 getEncodeQuality() { return mEncodeQuality; }
+
+ // Callbacks registered with jpeglib
+ static void encodeInitDestination ( j_compress_ptr cinfo );
+ static boolean encodeEmptyOutputBuffer(j_compress_ptr cinfo);
+ static void encodeTermDestination(j_compress_ptr cinfo);
+
+ static void decodeInitSource(j_decompress_ptr cinfo);
+ static boolean decodeFillInputBuffer(j_decompress_ptr cinfo);
+ static void decodeSkipInputData(j_decompress_ptr cinfo, long num_bytes);
+ static void decodeTermSource(j_decompress_ptr cinfo);
+
+
+ static void errorExit(j_common_ptr cinfo);
+ static void errorEmitMessage(j_common_ptr cinfo, int msg_level);
+ static void errorOutputMessage(j_common_ptr cinfo);
+
+ static BOOL decompress(LLImageJPEG* imagep);
+
+protected:
+ U8* mOutputBuffer; // temp buffer used during encoding
+ S32 mOutputBufferSize; // bytes in mOuputBuffer
+
+ S32 mEncodeQuality; // on a scale from 1 to 100
+
+ jmp_buf mSetjmpBuffer; // To allow the library to abort.
+};
+
+#endif // LL_LLIMAGEJPEG_H
diff --git a/indra/llimage/llimagetga.cpp b/indra/llimage/llimagetga.cpp
new file mode 100644
index 0000000000..1007f8e2bb
--- /dev/null
+++ b/indra/llimage/llimagetga.cpp
@@ -0,0 +1,1090 @@
+/**
+ * @file llimagetga.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llimagetga.h"
+#include "llerror.h"
+#include "llmath.h"
+
+// For expanding 5-bit pixel values to 8-bit with best rounding
+// static
+const U8 LLImageTGA::s5to8bits[32] =
+ {
+ 0, 8, 16, 25, 33, 41, 49, 58,
+ 66, 74, 82, 90, 99, 107, 115, 123,
+ 132, 140, 148, 156, 165, 173, 181, 189,
+ 197, 206, 214, 222, 230, 239, 247, 255
+ };
+
+inline void LLImageTGA::decodeTruecolorPixel15( U8* dst, const U8* src )
+{
+ // We expand 5 bit data to 8 bit sample width.
+ // The format of the 16-bit (LSB first) input word is
+ // xRRRRRGGGGGBBBBB
+ U32 t = U32(src[0]) + (U32(src[1]) << 8);
+ dst[2] = s5to8bits[t & 0x1F]; // blue
+ t >>= 5;
+ dst[1] = s5to8bits[t & 0x1F]; // green
+ t >>= 5;
+ dst[0] = s5to8bits[t & 0x1F]; // red
+}
+
+LLImageTGA::LLImageTGA()
+ : LLImageFormatted(IMG_CODEC_TGA),
+ mColorMap( NULL ),
+ mColorMapStart( 0 ),
+ mColorMapLength( 0 ),
+ mColorMapBytesPerEntry( 0 ),
+ mIs15Bit( FALSE )
+{
+}
+
+LLImageTGA::LLImageTGA(const LLString& file_name)
+ : LLImageFormatted(IMG_CODEC_TGA),
+ mColorMap( NULL ),
+ mColorMapStart( 0 ),
+ mColorMapLength( 0 ),
+ mColorMapBytesPerEntry( 0 ),
+ mIs15Bit( FALSE )
+{
+ loadFile(file_name);
+}
+
+LLImageTGA::~LLImageTGA()
+{
+ delete mColorMap;
+}
+
+BOOL LLImageTGA::updateData()
+{
+ resetLastError();
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImageTGA uninitialized");
+ return FALSE;
+ }
+
+ // Pull image information from the header...
+ U8 flags;
+ U8 junk[256];
+
+ /****************************************************************************
+ **
+ ** For more information about the original Truevision TGA(tm) file format,
+ ** or for additional information about the new extensions to the
+ ** Truevision TGA file, refer to the "Truevision TGA File Format
+ ** Specification Version 2.0" available from Truevision or your
+ ** Truevision dealer.
+ **
+ ** FILE STRUCTURE FOR THE ORIGINAL TRUEVISION TGA FILE
+ ** FIELD 1 : NUMBER OF CHARACTERS IN ID FIELD (1 BYTES)
+ ** FIELD 2 : COLOR MAP TYPE (1 BYTES)
+ ** FIELD 3 : IMAGE TYPE CODE (1 BYTES)
+ ** = 0 NO IMAGE DATA INCLUDED
+ ** = 1 UNCOMPRESSED, COLOR-MAPPED IMAGE
+ ** = 2 UNCOMPRESSED, TRUE-COLOR IMAGE
+ ** = 3 UNCOMPRESSED, BLACK AND WHITE IMAGE
+ ** = 9 RUN-LENGTH ENCODED COLOR-MAPPED IMAGE
+ ** = 10 RUN-LENGTH ENCODED TRUE-COLOR IMAGE
+ ** = 11 RUN-LENGTH ENCODED BLACK AND WHITE IMAGE
+ ** FIELD 4 : COLOR MAP SPECIFICATION (5 BYTES)
+ ** 4.1 : COLOR MAP ORIGIN (2 BYTES)
+ ** 4.2 : COLOR MAP LENGTH (2 BYTES)
+ ** 4.3 : COLOR MAP ENTRY SIZE (2 BYTES)
+ ** FIELD 5 : IMAGE SPECIFICATION (10 BYTES)
+ ** 5.1 : X-ORIGIN OF IMAGE (2 BYTES)
+ ** 5.2 : Y-ORIGIN OF IMAGE (2 BYTES)
+ ** 5.3 : WIDTH OF IMAGE (2 BYTES)
+ ** 5.4 : HEIGHT OF IMAGE (2 BYTES)
+ ** 5.5 : IMAGE PIXEL SIZE (1 BYTE)
+ ** 5.6 : IMAGE DESCRIPTOR BYTE (1 BYTE)
+ ** FIELD 6 : IMAGE ID FIELD (LENGTH SPECIFIED BY FIELD 1)
+ ** FIELD 7 : COLOR MAP DATA (BIT WIDTH SPECIFIED BY FIELD 4.3 AND
+ ** NUMBER OF COLOR MAP ENTRIES SPECIFIED IN FIELD 4.2)
+ ** FIELD 8 : IMAGE DATA FIELD (WIDTH AND HEIGHT SPECIFIED IN
+ ** FIELD 5.3 AND 5.4)
+ ****************************************************************************/
+
+ mDataOffset = 0;
+ mIDLength = *(getData()+mDataOffset++);
+ mColorMapType = *(getData()+mDataOffset++);
+ mImageType = *(getData()+mDataOffset++);
+ mColorMapIndexLo = *(getData()+mDataOffset++);
+ mColorMapIndexHi = *(getData()+mDataOffset++);
+ mColorMapLengthLo = *(getData()+mDataOffset++);
+ mColorMapLengthHi = *(getData()+mDataOffset++);
+ mColorMapDepth = *(getData()+mDataOffset++);
+ mXOffsetLo = *(getData()+mDataOffset++);
+ mXOffsetHi = *(getData()+mDataOffset++);
+ mYOffsetLo = *(getData()+mDataOffset++);
+ mYOffsetHi = *(getData()+mDataOffset++);
+ mWidthLo = *(getData()+mDataOffset++);
+ mWidthHi = *(getData()+mDataOffset++);
+ mHeightLo = *(getData()+mDataOffset++);
+ mHeightHi = *(getData()+mDataOffset++);
+ mPixelSize = *(getData()+mDataOffset++);
+ flags = *(getData()+mDataOffset++);
+ mAttributeBits = flags & 0xf;
+ mOriginRightBit = (flags & 0x10) >> 4;
+ mOriginTopBit = (flags & 0x20) >> 5;
+ mInterleave = (flags & 0xc0) >> 6;
+
+ switch( mImageType )
+ {
+ case 0:
+ // No image data included in file
+ setLastError("Unable to load file. TGA file contains no image data.");
+ return FALSE;
+ case 1:
+ // Colormapped uncompressed
+ if( 8 != mPixelSize )
+ {
+ setLastError("Unable to load file. Colormapped images must have 8 bits per pixel.");
+ return FALSE;
+ }
+ break;
+ case 2:
+ // Truecolor uncompressed
+ break;
+ case 3:
+ // Monochrome uncompressed
+ if( 8 != mPixelSize )
+ {
+ setLastError("Unable to load file. Monochrome images must have 8 bits per pixel.");
+ return FALSE;
+ }
+ break;
+ case 9:
+ // Colormapped, RLE
+ break;
+ case 10:
+ // Truecolor, RLE
+ break;
+ case 11:
+ // Monochrome, RLE
+ if( 8 != mPixelSize )
+ {
+ setLastError("Unable to load file. Monochrome images must have 8 bits per pixel.");
+ return FALSE;
+ }
+ break;
+ default:
+ setLastError("Unable to load file. Unrecoginzed TGA image type.");
+ return FALSE;
+ }
+
+ // discard the ID field, if any
+ if (mIDLength)
+ {
+ memcpy(junk, getData()+mDataOffset, mIDLength);
+ mDataOffset += mIDLength;
+ }
+
+ // check to see if there's a colormap since even rgb files can have them
+ S32 color_map_bytes = 0;
+ if( (1 == mColorMapType) && (mColorMapDepth > 0) )
+ {
+ mColorMapStart = (S32(mColorMapIndexHi) << 8) + mColorMapIndexLo;
+ mColorMapLength = (S32(mColorMapLengthHi) << 8) + mColorMapLengthLo;
+
+ if( mColorMapDepth > 24 )
+ {
+ mColorMapBytesPerEntry = 4;
+ }
+ else
+ if( mColorMapDepth > 16 )
+ {
+ mColorMapBytesPerEntry = 3;
+ }
+ else
+ if( mColorMapDepth > 8 )
+ {
+ mColorMapBytesPerEntry = 2;
+ }
+ else
+ {
+ mColorMapBytesPerEntry = 1;
+ }
+ color_map_bytes = mColorMapLength * mColorMapBytesPerEntry;
+
+ // Note: although it's legal for TGA files to have color maps and not use them
+ // (some programs actually do this and use the color map for other ends), we'll
+ // only allocate memory for one if _we_ intend to use it.
+ if ( (1 == mImageType) || (9 == mImageType) )
+ {
+ mColorMap = new U8[ color_map_bytes ];
+ memcpy( mColorMap, getData() + mDataOffset, color_map_bytes );
+ }
+
+ mDataOffset += color_map_bytes;
+ }
+
+ // heights are read as bytes to prevent endian problems
+ S32 height = (S32(mHeightHi) << 8) + mHeightLo;
+ S32 width = (S32(mWidthHi) << 8) + mWidthLo;
+
+ // make sure that it's a pixel format that we understand
+ S32 bits_per_pixel;
+ if( mColorMap )
+ {
+ bits_per_pixel = mColorMapDepth;
+ }
+ else
+ {
+ bits_per_pixel = mPixelSize;
+ }
+
+ S32 components;
+ switch(bits_per_pixel)
+ {
+ case 24:
+ components = 3;
+ break;
+ case 32:
+ components = 4;
+// Don't enforce this. ACDSee doesn't bother to set the attributes bits correctly. Arrgh!
+// if( mAttributeBits != 8 )
+// {
+// setLastError("Unable to load file. 32 bit TGA image does not have 8 bits of alpha.");
+// return FALSE;
+// }
+ mAttributeBits = 8;
+ break;
+ case 15:
+ case 16:
+ components = 3;
+ mIs15Bit = TRUE; // 16th bit is used for Targa hardware interupts and is ignored.
+ break;
+ case 8:
+ components = 1;
+ break;
+ default:
+ setLastError("Unable to load file. Unknown pixel size.");
+ return FALSE;
+ }
+ setSize(width, height, components);
+
+ return TRUE;
+}
+
+BOOL LLImageTGA::decode(LLImageRaw* raw_image, F32 decode_time)
+{
+ llassert_always(raw_image);
+
+ // Check to make sure that this instance has been initialized with data
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImageTGA trying to decode an image with no data!");
+ return FALSE;
+ }
+
+ // Copy everything after the header.
+
+ raw_image->resize(getWidth(), getHeight(), getComponents());
+
+ if( (getComponents() != 1) &&
+ (getComponents() != 3) &&
+ (getComponents() != 4) )
+ {
+ setLastError("TGA images with a number of components other than 1, 3, and 4 are not supported.");
+ return FALSE;
+ }
+
+
+ if( mOriginRightBit )
+ {
+ setLastError("TGA images with origin on right side are not supported.");
+ return FALSE;
+ }
+
+ BOOL flipped = (mOriginTopBit != 0);
+ BOOL rle_compressed = ((mImageType & 0x08) != 0);
+
+ if( mColorMap )
+ {
+ return decodeColorMap( raw_image, rle_compressed, flipped );
+ }
+ else
+ {
+ return decodeTruecolor( raw_image, rle_compressed, flipped );
+ }
+}
+
+BOOL LLImageTGA::decodeTruecolor( LLImageRaw* raw_image, BOOL rle, BOOL flipped )
+{
+ BOOL success = FALSE;
+ BOOL alpha_opaque = FALSE;
+ if( rle )
+ {
+
+ switch( getComponents() )
+ {
+ case 1:
+ success = decodeTruecolorRle8( raw_image );
+ break;
+ case 3:
+ if( mIs15Bit )
+ {
+ success = decodeTruecolorRle15( raw_image );
+ }
+ else
+ {
+ success = decodeTruecolorRle24( raw_image );
+ }
+ break;
+ case 4:
+ success = decodeTruecolorRle32( raw_image, alpha_opaque );
+ if (alpha_opaque)
+ {
+ // alpha was entirely opaque
+ // convert to 24 bit image
+ LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3);
+ compacted_image->copy(raw_image);
+ raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3);
+ raw_image->copy(compacted_image);
+ }
+ break;
+ }
+ }
+ else
+ {
+ BOOL alpha_opaque;
+ success = decodeTruecolorNonRle( raw_image, alpha_opaque );
+ if (alpha_opaque && raw_image->getComponents() == 4)
+ {
+ // alpha was entirely opaque
+ // convert to 24 bit image
+ LLPointer<LLImageRaw> compacted_image = new LLImageRaw(raw_image->getWidth(), raw_image->getHeight(), 3);
+ compacted_image->copy(raw_image);
+ raw_image->resize(raw_image->getWidth(), raw_image->getHeight(), 3);
+ raw_image->copy(compacted_image);
+ }
+ }
+
+ if( success && flipped )
+ {
+ // This works because the Targa definition requires that RLE blocks never
+ // encode pixels from more than one scanline.
+ // (On the other hand, it's not as fast as writing separate flipped versions as
+ // we did with TruecolorNonRle.)
+ raw_image->verticalFlip();
+ }
+
+ return success;
+}
+
+
+BOOL LLImageTGA::decodeTruecolorNonRle( LLImageRaw* raw_image, BOOL &alpha_opaque )
+{
+ alpha_opaque = TRUE;
+
+ // Origin is the bottom left
+ U8* dst = raw_image->getData();
+ U8* src = getData() + mDataOffset;
+ S32 pixels = getWidth() * getHeight();
+
+ if (getComponents() == 4)
+ {
+ while( pixels-- )
+ {
+ // Our data is stored in RGBA. TGA stores them as BGRA (little-endian ARGB)
+ dst[0] = src[2]; // Red
+ dst[1] = src[1]; // Green
+ dst[2] = src[0]; // Blue
+ dst[3] = src[3]; // Alpha
+ if (dst[3] != 255)
+ {
+ alpha_opaque = FALSE;
+ }
+ dst += 4;
+ src += 4;
+ }
+ }
+ else if (getComponents() == 3)
+ {
+ if( mIs15Bit )
+ {
+ while( pixels-- )
+ {
+ decodeTruecolorPixel15( dst, src );
+ dst += 3;
+ src += 2;
+ }
+ }
+ else
+ {
+ while( pixels-- )
+ {
+ dst[0] = src[2]; // Red
+ dst[1] = src[1]; // Green
+ dst[2] = src[0]; // Blue
+ dst += 3;
+ src += 3;
+ }
+ }
+ }
+ else if (getComponents() == 1)
+ {
+ memcpy(dst, src, pixels);
+ }
+
+ return TRUE;
+}
+
+void LLImageTGA::decodeColorMapPixel8( U8* dst, const U8* src )
+{
+ S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
+ dst[0] = mColorMap[ index ];
+}
+
+void LLImageTGA::decodeColorMapPixel15( U8* dst, const U8* src )
+{
+ S32 index = llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
+ decodeTruecolorPixel15( dst, mColorMap + 2 * index );
+}
+
+void LLImageTGA::decodeColorMapPixel24( U8* dst, const U8* src )
+{
+ S32 index = 3 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
+ dst[0] = mColorMap[ index + 2 ]; // Red
+ dst[1] = mColorMap[ index + 1 ]; // Green
+ dst[2] = mColorMap[ index + 0 ]; // Blue
+}
+
+void LLImageTGA::decodeColorMapPixel32( U8* dst, const U8* src )
+{
+ S32 index = 4 * llclamp( *src - mColorMapStart, 0, mColorMapLength - 1 );
+ dst[0] = mColorMap[ index + 2 ]; // Red
+ dst[1] = mColorMap[ index + 1 ]; // Green
+ dst[2] = mColorMap[ index + 0 ]; // Blue
+ dst[3] = mColorMap[ index + 3 ]; // Alpha
+}
+
+
+BOOL LLImageTGA::decodeColorMap( LLImageRaw* raw_image, BOOL rle, BOOL flipped )
+{
+ // If flipped, origin is the top left. Need to reverse the order of the rows.
+ // Otherwise the origin is the bottom left.
+
+ if( 8 != mPixelSize )
+ {
+ return FALSE;
+ }
+
+ U8* src = getData() + mDataOffset;
+ U8* dst = raw_image->getData(); // start from the top
+
+ void (LLImageTGA::*pixel_decoder)( U8*, const U8* );
+
+ switch( mColorMapBytesPerEntry )
+ {
+ case 1: pixel_decoder = &LLImageTGA::decodeColorMapPixel8; break;
+ case 2: pixel_decoder = &LLImageTGA::decodeColorMapPixel15; break;
+ case 3: pixel_decoder = &LLImageTGA::decodeColorMapPixel24; break;
+ case 4: pixel_decoder = &LLImageTGA::decodeColorMapPixel32; break;
+ default: llassert(0); return FALSE;
+ }
+
+ if( rle )
+ {
+ U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ do
+ {
+ (this->*pixel_decoder)( dst, src );
+ dst += getComponents();
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ src++;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ (this->*pixel_decoder)( dst, src );
+ dst += getComponents();
+ src++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+
+ raw_image->verticalFlip();
+ }
+ else
+ {
+ S32 src_row_bytes = getWidth();
+ S32 dst_row_bytes = getWidth() * getComponents();
+
+ if( flipped )
+ {
+ U8* src_last_row_start = src + (getHeight() - 1) * src_row_bytes;
+ src = src_last_row_start; // start from the bottom
+ src_row_bytes *= -1;
+ }
+
+
+ S32 i;
+ S32 j;
+
+ for( S32 row = 0; row < getHeight(); row++ )
+ {
+ for( i = 0, j = 0; j < getWidth(); i += getComponents(), j++ )
+ {
+ (this->*pixel_decoder)( dst + i, src + j );
+ }
+
+ dst += dst_row_bytes;
+ src += src_row_bytes;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL LLImageTGA::encode(const LLImageRaw* raw_image, F32 encode_time)
+{
+ llassert_always(raw_image);
+
+ deleteData();
+
+ setSize(raw_image->getWidth(), raw_image->getHeight(), raw_image->getComponents());
+
+ // Data from header
+ mIDLength = 0; // Length of identifier string
+ mColorMapType = 0; // 0 = No Map
+
+ // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap
+ switch( getComponents() )
+ {
+ case 1:
+ mImageType = 3;
+ break;
+ case 2: // Interpret as intensity plus alpha
+ case 3:
+ case 4:
+ mImageType = 2;
+ break;
+ default:
+ return FALSE;
+ }
+
+ // Color map stuff (unsupported)
+ mColorMapIndexLo = 0; // First color map entry (low order byte)
+ mColorMapIndexHi = 0; // First color map entry (high order byte)
+ mColorMapLengthLo = 0; // Color map length (low order byte)
+ mColorMapLengthHi = 0; // Color map length (high order byte)
+ mColorMapDepth = 0; // Size of color map entry (15, 16, 24, or 32 bits)
+
+ // Image offset relative to origin.
+ mXOffsetLo = 0; // X offset from origin (low order byte)
+ mXOffsetHi = 0; // X offset from origin (hi order byte)
+ mYOffsetLo = 0; // Y offset from origin (low order byte)
+ mYOffsetHi = 0; // Y offset from origin (hi order byte)
+
+ // Height and width
+ mWidthLo = U8(getWidth() & 0xFF); // Width (low order byte)
+ mWidthHi = U8((getWidth() >> 8) & 0xFF); // Width (hi order byte)
+ mHeightLo = U8(getHeight() & 0xFF); // Height (low order byte)
+ mHeightHi = U8((getHeight() >> 8) & 0xFF); // Height (hi order byte)
+
+ S32 bytes_per_pixel;
+ switch( getComponents() )
+ {
+ case 1:
+ bytes_per_pixel = 1;
+ break;
+ case 3:
+ bytes_per_pixel = 3;
+ break;
+ case 2: // Interpret as intensity plus alpha. Store as RGBA.
+ case 4:
+ bytes_per_pixel = 4;
+ break;
+ default:
+ return FALSE;
+ }
+ mPixelSize = U8(bytes_per_pixel * 8); // 8, 16, 24, 32 bits per pixel
+
+ mAttributeBits = (4 == bytes_per_pixel) ? 8 : 0; // 4 bits: number of attribute bits (alpha) per pixel
+ mOriginRightBit = 0; // 1 bit: origin, 0 = left, 1 = right
+ mOriginTopBit = 0; // 1 bit: origin, 0 = bottom, 1 = top
+ mInterleave = 0; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4
+
+
+ const S32 TGA_HEADER_SIZE = 18;
+ const S32 COLOR_MAP_SIZE = 0;
+ mDataOffset = TGA_HEADER_SIZE + mIDLength + COLOR_MAP_SIZE; // Offset from start of data to the actual header.
+
+ S32 pixels = getWidth() * getHeight();
+ S32 datasize = mDataOffset + bytes_per_pixel * pixels;
+ U8* dst = allocateData(datasize);
+
+ // Write header
+ *(dst++) = mIDLength;
+ *(dst++) = mColorMapType;
+ *(dst++) = mImageType;
+ *(dst++) = mColorMapIndexLo;
+ *(dst++) = mColorMapIndexHi;
+ *(dst++) = mColorMapLengthLo;
+ *(dst++) = mColorMapLengthHi;
+ *(dst++) = mColorMapDepth;
+ *(dst++) = mXOffsetLo;
+ *(dst++) = mXOffsetHi;
+ *(dst++) = mYOffsetLo;
+ *(dst++) = mYOffsetHi;
+ *(dst++) = mWidthLo;
+ *(dst++) = mWidthHi;
+ *(dst++) = mHeightLo;
+ *(dst++) = mHeightHi;
+ *(dst++) = mPixelSize;
+ *(dst++) =
+ ((mInterleave & 3) << 5) |
+ ((mOriginTopBit & 1) << 4) |
+ ((mOriginRightBit & 1) << 3) |
+ ((mAttributeBits & 0xF) << 0);
+
+ // Write pixels
+ const U8* src = raw_image->getData();
+ llassert( dst == getData() + mDataOffset );
+ S32 i = 0;
+ S32 j = 0;
+ switch( getComponents() )
+ {
+ case 1:
+ memcpy( dst, src, bytes_per_pixel * pixels );
+ break;
+
+ case 2:
+ while( pixels-- )
+ {
+ dst[i + 0] = src[j + 0]; // intensity
+ dst[i + 1] = src[j + 0]; // intensity
+ dst[i + 2] = src[j + 0]; // intensity
+ dst[i + 3] = src[j + 1]; // alpha
+ i += 4;
+ j += 2;
+ }
+ break;
+
+ case 3:
+ while( pixels-- )
+ {
+ dst[i + 0] = src[i + 2]; // blue
+ dst[i + 1] = src[i + 1]; // green
+ dst[i + 2] = src[i + 0]; // red
+ i += 3;
+ }
+ break;
+
+ case 4:
+ while( pixels-- )
+ {
+ dst[i + 0] = src[i + 2]; // blue
+ dst[i + 1] = src[i + 1]; // green
+ dst[i + 2] = src[i + 0]; // red
+ dst[i + 3] = src[i + 3]; // alpha
+ i += 4;
+ }
+ break;
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageTGA::decodeTruecolorRle32( LLImageRaw* raw_image, BOOL &alpha_opaque )
+{
+ llassert( getComponents() == 4 );
+ alpha_opaque = TRUE;
+
+ U8* dst = raw_image->getData();
+ U32* dst_pixels = (U32*) dst;
+
+ U8* src = getData() + mDataOffset;
+
+ U32 rgba;
+ U8* rgba_byte_p = (U8*) &rgba;
+
+ U32* last_dst_pixel = dst_pixels + getHeight() * getWidth() - 1;
+ while( dst_pixels <= last_dst_pixel )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U32 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ rgba_byte_p[0] = src[2];
+ rgba_byte_p[1] = src[1];
+ rgba_byte_p[2] = src[0];
+ rgba_byte_p[3] = src[3];
+ if (rgba_byte_p[3] != 255)
+ {
+ alpha_opaque = FALSE;
+ }
+
+ src += 4;
+ register U32 value = rgba;
+ do
+ {
+ *dst_pixels = value;
+ dst_pixels++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ ((U8*)dst_pixels)[0] = src[2];
+ ((U8*)dst_pixels)[1] = src[1];
+ ((U8*)dst_pixels)[2] = src[0];
+ ((U8*)dst_pixels)[3] = src[3];
+ if (src[3] != 255)
+ {
+ alpha_opaque = FALSE;
+ }
+ src += 4;
+ dst_pixels++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageTGA::decodeTruecolorRle15( LLImageRaw* raw_image )
+{
+ llassert( getComponents() == 3 );
+ llassert( mIs15Bit );
+
+ U8* dst = raw_image->getData();
+ U8* src = getData() + mDataOffset;
+
+ U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ do
+ {
+ decodeTruecolorPixel15( dst, src ); // slow
+ dst += 3;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ src += 2;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ decodeTruecolorPixel15( dst, src );
+ dst += 3;
+ src += 2;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL LLImageTGA::decodeTruecolorRle24( LLImageRaw* raw_image )
+{
+ llassert( getComponents() == 3 );
+
+ U8* dst = raw_image->getData();
+ U8* src = getData() + mDataOffset;
+
+ U8* last_dst = dst + getComponents() * (getHeight() * getWidth() - 1);
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ do
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst += 3;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ src += 3;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst += 3;
+ src += 3;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLImageTGA::decodeTruecolorRle8( LLImageRaw* raw_image )
+{
+ llassert( getComponents() == 1 );
+
+ U8* dst = raw_image->getData();
+ U8* src = getData() + mDataOffset;
+
+ U8* last_dst = dst + getHeight() * getWidth() - 1;
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ memset( dst, *src, block_pixel_count );
+ dst += block_pixel_count;
+ src++;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ *dst = *src;
+ dst++;
+ src++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+
+ return TRUE;
+}
+
+
+// Decoded and process the image for use in avatar gradient masks.
+// Processing happens during the decode for speed.
+BOOL LLImageTGA::decodeAndProcess( LLImageRaw* raw_image, F32 domain, F32 weight )
+{
+ llassert_always(raw_image);
+
+ // "Domain" isn't really the right word. It refers to the width of the
+ // ramp portion of the function that relates input and output pixel values.
+ // A domain of 0 gives a step function.
+ //
+ // | /----------------
+ // O| / |
+ // u| / |
+ // t| / |
+ // p|------------------/ |
+ // u| | |
+ // t|<---------------->|<-->|
+ // | "offset" "domain"
+ // |
+ // --+---Input--------------------------------
+ // |
+
+ if (!getData() || (0 == getDataSize()))
+ {
+ setLastError("LLImageTGA trying to decode an image with no data!");
+ return FALSE;
+ }
+
+ // Only works for unflipped monochrome RLE images
+ if( (getComponents() != 1) || (mImageType != 11) || mOriginTopBit || mOriginRightBit )
+ {
+ llerrs << "LLImageTGA trying to alpha-gradient process an image that's not a standard RLE, one component image" << llendl;
+ return FALSE;
+ }
+
+ raw_image->resize(getWidth(), getHeight(), getComponents());
+
+ U8* dst = raw_image->getData();
+ U8* src = getData() + mDataOffset;
+ U8* last_dst = dst + getHeight() * getWidth() - 1;
+
+ if( domain > 0 )
+ {
+ // Process using a look-up table (lut)
+ const S32 LUT_LEN = 256;
+ U8 lut[LUT_LEN];
+ S32 i;
+
+ F32 scale = 1.f / domain;
+ F32 offset = (1.f - domain) * llclampf( 1.f - weight );
+ F32 bias = -(scale * offset);
+
+ for( i = 0; i < LUT_LEN; i++ )
+ {
+ lut[i] = (U8)llclampb( 255.f * ( i/255.f * scale + bias ) );
+ }
+
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ memset( dst, lut[ *src ], block_pixel_count );
+ dst += block_pixel_count;
+ src++;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ *dst = lut[ *src ];
+ dst++;
+ src++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+ }
+ else
+ {
+ // Process using a simple comparison agains a threshold
+ const U8 threshold = (U8)(0xFF * llclampf( 1.f - weight ));
+
+ while( dst <= last_dst )
+ {
+ // Read RLE block header
+ U8 block_header_byte = *src;
+ src++;
+
+ U8 block_pixel_count = (block_header_byte & 0x7F) + 1;
+ if( block_header_byte & 0x80 )
+ {
+ // Encoded (duplicate-pixel) block
+ memset( dst, ((*src >= threshold) ? 0xFF : 0), block_pixel_count );
+ dst += block_pixel_count;
+ src++;
+ }
+ else
+ {
+ // Unencoded block
+ do
+ {
+ *dst = (*src >= threshold) ? 0xFF : 0;
+ dst++;
+ src++;
+ block_pixel_count--;
+ }
+ while( block_pixel_count > 0 );
+ }
+ }
+ }
+ return TRUE;
+}
+
+// Reads a .tga file and creates an LLImageTGA with its data.
+bool LLImageTGA::loadFile( const LLString& path )
+{
+ S32 len = path.size();
+ if( len < 5 )
+ {
+ return false;
+ }
+
+ LLString extension = path.substr( len - 4, 4 );
+ LLString::toLower(extension);
+ if( ".tga" != extension )
+ {
+ return false;
+ }
+
+ FILE *file = LLFile::fopen(path.c_str(), "rb");
+ if( !file )
+ {
+ llwarns << "Couldn't open file " << path << llendl;
+ return false;
+ }
+
+ S32 file_size = 0;
+ if (!fseek(file, 0, SEEK_END))
+ {
+ file_size = ftell(file);
+ fseek(file, 0, SEEK_SET);
+ }
+
+ U8* buffer = allocateData(file_size);
+ S32 bytes_read = fread(buffer, 1, file_size, file);
+ if( bytes_read != file_size )
+ {
+ deleteData();
+ llwarns << "Couldn't read file " << path << llendl;
+ return false;
+ }
+
+ fclose( file );
+
+ if( !updateData() )
+ {
+ llwarns << "Couldn't decode file " << path << llendl;
+ deleteData();
+ return false;
+ }
+ return true;
+}
+
+
diff --git a/indra/llimage/llimagetga.h b/indra/llimage/llimagetga.h
new file mode 100644
index 0000000000..376d6dc269
--- /dev/null
+++ b/indra/llimage/llimagetga.h
@@ -0,0 +1,89 @@
+/**
+ * @file llimagetga.h
+ * @brief Image implementation to compresses and decompressed TGA files.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMAGETGA_H
+#define LL_LLIMAGETGA_H
+
+#include "llimage.h"
+
+// This class compresses and decompressed TGA (targa) files
+
+class LLImageTGA : public LLImageFormatted
+{
+protected:
+ virtual ~LLImageTGA();
+
+public:
+ LLImageTGA();
+ LLImageTGA(const LLString& file_name);
+
+ /*virtual*/ BOOL updateData();
+ /*virtual*/ BOOL decode(LLImageRaw* raw_image, F32 decode_time=0.0);
+ /*virtual*/ BOOL encode(const LLImageRaw* raw_image, F32 encode_time=0.0);
+
+ BOOL decodeAndProcess(LLImageRaw* raw_image, F32 domain, F32 weight);
+
+private:
+ BOOL decodeTruecolor( LLImageRaw* raw_image, BOOL rle, BOOL flipped );
+
+ BOOL decodeTruecolorRle8( LLImageRaw* raw_image );
+ BOOL decodeTruecolorRle15( LLImageRaw* raw_image );
+ BOOL decodeTruecolorRle24( LLImageRaw* raw_image );
+ BOOL decodeTruecolorRle32( LLImageRaw* raw_image, BOOL &alpha_opaque );
+
+ void decodeTruecolorPixel15( U8* dst, const U8* src );
+
+ BOOL decodeTruecolorNonRle( LLImageRaw* raw_image, BOOL &alpha_opaque );
+
+ BOOL decodeColorMap( LLImageRaw* raw_image, BOOL rle, BOOL flipped );
+
+ void decodeColorMapPixel8(U8* dst, const U8* src);
+ void decodeColorMapPixel15(U8* dst, const U8* src);
+ void decodeColorMapPixel24(U8* dst, const U8* src);
+ void decodeColorMapPixel32(U8* dst, const U8* src);
+
+ bool loadFile(const LLString& file_name);
+
+private:
+ // Class specific data
+ U32 mDataOffset; // Offset from start of data to the actual header.
+
+ // Data from header
+ U8 mIDLength; // Length of identifier string
+ U8 mColorMapType; // 0 = No Map
+ U8 mImageType; // Supported: 2 = Uncompressed true color, 3 = uncompressed monochrome without colormap
+ U8 mColorMapIndexLo; // First color map entry (low order byte)
+ U8 mColorMapIndexHi; // First color map entry (high order byte)
+ U8 mColorMapLengthLo; // Color map length (low order byte)
+ U8 mColorMapLengthHi; // Color map length (high order byte)
+ U8 mColorMapDepth; // Size of color map entry (15, 16, 24, or 32 bits)
+ U8 mXOffsetLo; // X offset of image (low order byte)
+ U8 mXOffsetHi; // X offset of image (hi order byte)
+ U8 mYOffsetLo; // Y offset of image (low order byte)
+ U8 mYOffsetHi; // Y offset of image (hi order byte)
+ U8 mWidthLo; // Width (low order byte)
+ U8 mWidthHi; // Width (hi order byte)
+ U8 mHeightLo; // Height (low order byte)
+ U8 mHeightHi; // Height (hi order byte)
+ U8 mPixelSize; // 8, 16, 24, 32 bits per pixel
+ U8 mAttributeBits; // 4 bits: number of attributes per pixel
+ U8 mOriginRightBit; // 1 bit: origin, 0 = left, 1 = right
+ U8 mOriginTopBit; // 1 bit: origin, 0 = bottom, 1 = top
+ U8 mInterleave; // 2 bits: interleaved flag, 0 = none, 1 = interleaved 2, 2 = interleaved 4
+
+ U8* mColorMap;
+ S32 mColorMapStart;
+ S32 mColorMapLength;
+ S32 mColorMapBytesPerEntry;
+
+ BOOL mIs15Bit;
+
+ static const U8 s5to8bits[32];
+};
+
+#endif
diff --git a/indra/llimage/llmapimagetype.h b/indra/llimage/llmapimagetype.h
new file mode 100644
index 0000000000..6b66506a28
--- /dev/null
+++ b/indra/llimage/llmapimagetype.h
@@ -0,0 +1,22 @@
+/**
+ * @file llmapimagetype.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMAPIMAGETYPE_H
+#define LL_LLMAPIMAGETYPE_H
+
+typedef enum e_map_image_type
+{
+ MIT_TERRAIN = 0,
+ MIT_POPULAR = 1,
+ MIT_OBJECTS = 2,
+ MIT_OBJECTS_FOR_SALE = 3,
+ MIT_LAND_TO_BUY = 4,
+ MIT_OBJECT_NEW = 5,
+ MIT_EOF = 6
+} EMapImageType;
+
+#endif
diff --git a/indra/llinventory/llcategory.cpp b/indra/llinventory/llcategory.cpp
new file mode 100644
index 0000000000..7a7bbf530a
--- /dev/null
+++ b/indra/llinventory/llcategory.cpp
@@ -0,0 +1,161 @@
+/**
+ * @file llcategory.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcategory.h"
+
+#include "message.h"
+
+const LLCategory LLCategory::none;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+// This is the storage of the category names. It's loosely based on a
+// heap-like structure with indices into it for faster searching and
+// so that we don't have to maintain a balanced heap. It's *VITALLY*
+// important that the CATEGORY_INDEX and CATEGORY_NAME tables are kept
+// in synch.
+
+// CATEGORY_INDEX indexes into CATEGORY_NAME at the first occurance of
+// a child. Thus, the first child of root is "Object" which is located
+// in CATEGORY_NAME[1].
+const S32 CATEGORY_INDEX[] =
+{
+ 1, // ROOT
+ 6, // object
+ 7, // clothing
+ 7, // texture
+ 7, // sound
+ 7, // landmark
+ 7, // object|component
+ 7, // off the end (required for child count calculations)
+};
+
+// The heap of names
+const char* CATEGORY_NAME[] =
+{
+ "(none)",
+ "Object", // (none)
+ "Clothing",
+ "Texture",
+ "Sound",
+ "Landmark",
+ "Component", // object
+ NULL
+};
+
+///----------------------------------------------------------------------------
+/// Class llcategory
+///----------------------------------------------------------------------------
+
+LLCategory::LLCategory()
+{
+ // this is used as a simple compile time assertion. If this code
+ // fails to compile, the depth has been changed, and we need to
+ // clean up some of the code that relies on the depth, such as the
+ // default constructor. If CATEGORY_DEPTH != 4, this code will
+ // attempt to construct a zero length array - which the compiler
+ // should balk at.
+// static const char CATEGORY_DEPTH_CHECK[(CATEGORY_DEPTH == 4)?1:0] = {' '}; // unused
+
+ // actually initialize the object.
+ mData[0] = 0;
+ mData[1] = 0;
+ mData[2] = 0;
+ mData[3] = 0;
+}
+
+void LLCategory::init(U32 value)
+{
+ U8 v;
+ for(S32 i = 0; i < CATEGORY_DEPTH; i++)
+ {
+ v = (U8)((0x000000ff) & value);
+ mData[CATEGORY_DEPTH - 1 - i] = v;
+ value >>= 8;
+ }
+}
+
+U32 LLCategory::getU32() const
+{
+ U32 rv = 0;
+ rv |= mData[0];
+ rv <<= 8;
+ rv |= mData[1];
+ rv <<= 8;
+ rv |= mData[2];
+ rv <<= 8;
+ rv |= mData[3];
+ return rv;
+}
+
+S32 LLCategory::getSubCategoryCount() const
+{
+ S32 rv = CATEGORY_INDEX[mData[0] + 1] - CATEGORY_INDEX[mData[0]];
+ return rv;
+}
+
+// This method will return a category that is the nth subcategory. If
+// you're already at the bottom of the hierarchy, then the method will
+// return a copy of this.
+LLCategory LLCategory::getSubCategory(U8 n) const
+{
+ LLCategory rv(*this);
+ for(S32 i = 0; i < (CATEGORY_DEPTH - 1); i++)
+ {
+ if(rv.mData[i] == 0)
+ {
+ rv.mData[i] = n + 1;
+ break;
+ }
+ }
+ return rv;
+}
+
+// This method will return the name of the leaf category type
+const char* LLCategory::lookupName() const
+{
+ S32 i = 0;
+ S32 index = mData[i++];
+ while((i < CATEGORY_DEPTH) && (mData[i] != 0))
+ {
+ index = CATEGORY_INDEX[index];
+ ++i;
+ }
+ return CATEGORY_NAME[index];
+}
+
+// message serialization
+void LLCategory::packMessage(LLMessageSystem* msg) const
+{
+ U32 data = getU32();
+ msg->addU32Fast(_PREHASH_Category, data);
+}
+
+// message serialization
+void LLCategory::unpackMessage(LLMessageSystem* msg, const char* block)
+{
+ U32 data;
+ msg->getU32Fast(block, _PREHASH_Category, data);
+ init(data);
+}
+
+// message serialization
+void LLCategory::unpackMultiMessage(LLMessageSystem* msg, const char* block,
+ S32 block_num)
+{
+ U32 data;
+ msg->getU32Fast(block, _PREHASH_Category, data, block_num);
+ init(data);
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/llinventory/llcategory.h b/indra/llinventory/llcategory.h
new file mode 100644
index 0000000000..a28b707d93
--- /dev/null
+++ b/indra/llinventory/llcategory.h
@@ -0,0 +1,80 @@
+/**
+ * @file llcategory.h
+ * @brief LLCategory class header file.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCATEGORY_H
+#define LL_LLCATEGORY_H
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLCategory
+//
+// An instance of the LLCategory class represents a particular
+// category in a hierarchical classification system. For now, it is 4
+// levels deep with 255 (minus 1) possible values at each level. If a
+// non zero value is found at level 4, that is the leaf category,
+// otherwise, it is the first level that has a 0 in the next depth
+// level.
+//
+// To output the names of all top level categories, you could do the
+// following:
+//
+// S32 count = LLCategory::none.getSubCategoryCount();
+// for(S32 i = 0; i < count; i++)
+// {
+// llinfos << none.getSubCategory(i).lookupNmae() << llendl;
+// }
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMessageSystem;
+
+class LLCategory
+{
+public:
+ // Nice default static const.
+ static const LLCategory none;
+
+ // construction. Since this is really a POD type, destruction,
+ // copy, and assignment are handled by the compiler.
+ LLCategory();
+ explicit LLCategory(U32 value) { init(value); }
+
+ // methods
+ void init(U32 value);
+ U32 getU32() const;
+ S32 getSubCategoryCount() const;
+
+ // This method will return a category that is the nth
+ // subcategory. If you're already at the bottom of the hierarchy,
+ // then the method will return a copy of this.
+ LLCategory getSubCategory(U8 n) const;
+
+ // This method will return the name of the leaf category type
+ const char* lookupName() const;
+
+ // This method will return the full hierarchy name in an easily
+ // interpreted (TOP)|(SUB1)|(SUB2) format. *NOTE: not implemented
+ // because we don't have anything but top level categories at the
+ // moment.
+ //const char* lookupFullName() const;
+
+ // message serialization
+ void packMessage(LLMessageSystem* msg) const;
+ void unpackMessage(LLMessageSystem* msg, const char* block);
+ void unpackMultiMessage(LLMessageSystem* msg, const char* block,
+ S32 block_num);
+protected:
+ enum
+ {
+ CATEGORY_TOP = 0,
+ CATEGORY_DEPTH = 4,
+ };
+
+ U8 mData[CATEGORY_DEPTH];
+};
+
+
+#endif // LL_LLCATEGORY_H
diff --git a/indra/llinventory/lleconomy.cpp b/indra/llinventory/lleconomy.cpp
new file mode 100644
index 0000000000..378ab8ced1
--- /dev/null
+++ b/indra/llinventory/lleconomy.cpp
@@ -0,0 +1,227 @@
+/**
+ * @file lleconomy.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lleconomy.h"
+#include "llerror.h"
+#include "message.h"
+#include "v3math.h"
+
+LLGlobalEconomy::LLGlobalEconomy()
+: mObjectCount( -1 ),
+ mObjectCapacity( -1 ),
+ mPriceObjectClaim( -1 ),
+ mPricePublicObjectDecay( -1 ),
+ mPricePublicObjectDelete( -1 ),
+ mPriceEnergyUnit( -1 ),
+ mPriceUpload( -1 ),
+ mPriceRentLight( -1 ),
+ mTeleportMinPrice( -1 ),
+ mTeleportPriceExponent( -1 ),
+ mPriceGroupCreate( -1 )
+{ }
+
+LLGlobalEconomy::~LLGlobalEconomy()
+{ }
+
+// static
+void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, void** user_data)
+{
+ S32 i;
+ F32 f;
+
+ LLGlobalEconomy *this_ptr = (LLGlobalEconomy*)user_data;
+
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_ObjectCapacity, i);
+ this_ptr->setObjectCapacity(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_ObjectCount, i);
+ this_ptr->setObjectCount(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceEnergyUnit, i);
+ this_ptr->setPriceEnergyUnit(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceObjectClaim, i);
+ this_ptr->setPriceObjectClaim(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PricePublicObjectDecay, i);
+ this_ptr->setPricePublicObjectDecay(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PricePublicObjectDelete, i);
+ this_ptr->setPricePublicObjectDelete(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceUpload, i);
+ this_ptr->setPriceUpload(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceRentLight, i);
+ this_ptr->setPriceRentLight(i);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_TeleportMinPrice, i);
+ this_ptr->setTeleportMinPrice(i);
+ msg->getF32Fast(_PREHASH_Info, _PREHASH_TeleportPriceExponent, f);
+ this_ptr->setTeleportPriceExponent(f);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceGroupCreate, i);
+ this_ptr->setPriceGroupCreate(i);
+}
+
+S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
+{
+ S32 min_cost = getTeleportMinPrice();
+ F32 exponent = getTeleportPriceExponent();
+ F32 divisor = 100.f * pow(3.f, exponent);
+ S32 cost = (U32)(distance * pow(log10(distance), exponent) / divisor);
+ if (cost < 0)
+ {
+ cost = 0;
+ }
+ else if (cost < min_cost)
+ {
+ cost = min_cost;
+ }
+
+ return cost;
+}
+
+S32 LLGlobalEconomy::calculateLightRent(const LLVector3& object_size) const
+{
+ F32 intensity_mod = llmax(object_size.magVec(), 1.f);
+ return (S32)(intensity_mod * getPriceRentLight());
+}
+
+void LLGlobalEconomy::print()
+{
+ llinfos << "Global Economy Settings: " << llendl;
+ llinfos << "Object Capacity: " << mObjectCapacity << llendl;
+ llinfos << "Object Count: " << mObjectCount << llendl;
+ llinfos << "Claim Price Per Object: " << mPriceObjectClaim << llendl;
+ llinfos << "Claim Price Per Public Object: " << mPricePublicObjectDecay << llendl;
+ llinfos << "Delete Price Per Public Object: " << mPricePublicObjectDelete << llendl;
+ llinfos << "Release Price Per Public Object: " << getPricePublicObjectRelease() << llendl;
+ llinfos << "Price Per Energy Unit: " << mPriceEnergyUnit << llendl;
+ llinfos << "Price Per Upload: " << mPriceUpload << llendl;
+ llinfos << "Light Base Price: " << mPriceRentLight << llendl;
+ llinfos << "Teleport Min Price: " << mTeleportMinPrice << llendl;
+ llinfos << "Teleport Price Exponent: " << mTeleportPriceExponent << llendl;
+ llinfos << "Price for group creation: " << mPriceGroupCreate << llendl;
+}
+
+LLRegionEconomy::LLRegionEconomy()
+: LLGlobalEconomy(),
+ mPriceObjectRent( -1.f ),
+ mPriceObjectScaleFactor( -1.f ),
+ mEnergyEfficiency( -1.f ),
+ mBasePriceParcelClaimDefault(-1),
+ mBasePriceParcelClaimActual(-1),
+ mPriceParcelClaimFactor(-1.f),
+ mBasePriceParcelRent(-1),
+ mAreaOwned(-1.f),
+ mAreaTotal(-1.f)
+{ }
+
+LLRegionEconomy::~LLRegionEconomy()
+{ }
+
+BOOL LLRegionEconomy::hasData() const
+{
+ return (mBasePriceParcelRent != -1);
+}
+
+// static
+void LLRegionEconomy::processEconomyData(LLMessageSystem *msg, void** user_data)
+{
+ S32 i;
+ F32 f;
+
+ LLGlobalEconomy::processEconomyData(msg, user_data);
+
+ LLRegionEconomy *this_ptr = (LLRegionEconomy*)user_data;
+
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceParcelClaim, i);
+ this_ptr->setBasePriceParcelClaimDefault(i);
+ msg->getF32(_PREHASH_Info, _PREHASH_PriceParcelClaimFactor, f);
+ this_ptr->setPriceParcelClaimFactor(f);
+ msg->getF32Fast(_PREHASH_Info, _PREHASH_EnergyEfficiency, f);
+ this_ptr->setEnergyEfficiency(f);
+ msg->getF32Fast(_PREHASH_Info, _PREHASH_PriceObjectRent, f);
+ this_ptr->setPriceObjectRent(f);
+ msg->getF32Fast(_PREHASH_Info, _PREHASH_PriceObjectScaleFactor, f);
+ this_ptr->setPriceObjectScaleFactor(f);
+ msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceParcelRent, i);
+ this_ptr->setBasePriceParcelRent(i);
+}
+
+// static
+void LLRegionEconomy::processEconomyDataRequest(LLMessageSystem *msg, void **user_data)
+{
+ LLRegionEconomy *this_ptr = (LLRegionEconomy*)user_data;
+
+ msg->newMessageFast(_PREHASH_EconomyData);
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addS32Fast(_PREHASH_ObjectCapacity, this_ptr->getObjectCapacity());
+ msg->addS32Fast(_PREHASH_ObjectCount, this_ptr->getObjectCount());
+ msg->addS32Fast(_PREHASH_PriceEnergyUnit, this_ptr->getPriceEnergyUnit());
+ msg->addS32Fast(_PREHASH_PriceObjectClaim, this_ptr->getPriceObjectClaim());
+ msg->addS32Fast(_PREHASH_PricePublicObjectDecay, this_ptr->getPricePublicObjectDecay());
+ msg->addS32Fast(_PREHASH_PricePublicObjectDelete, this_ptr->getPricePublicObjectDelete());
+ msg->addS32Fast(_PREHASH_PriceParcelClaim, this_ptr->mBasePriceParcelClaimActual);
+ msg->addF32Fast(_PREHASH_PriceParcelClaimFactor, this_ptr->mPriceParcelClaimFactor);
+ msg->addS32Fast(_PREHASH_PriceUpload, this_ptr->getPriceUpload());
+ msg->addS32Fast(_PREHASH_PriceRentLight, this_ptr->getPriceRentLight());
+ msg->addS32Fast(_PREHASH_TeleportMinPrice, this_ptr->getTeleportMinPrice());
+ msg->addF32Fast(_PREHASH_TeleportPriceExponent, this_ptr->getTeleportPriceExponent());
+
+ msg->addF32Fast(_PREHASH_EnergyEfficiency, this_ptr->getEnergyEfficiency());
+ msg->addF32Fast(_PREHASH_PriceObjectRent, this_ptr->getPriceObjectRent());
+ msg->addF32Fast(_PREHASH_PriceObjectScaleFactor, this_ptr->getPriceObjectScaleFactor());
+ msg->addS32Fast(_PREHASH_PriceParcelRent, this_ptr->getPriceParcelRent());
+ msg->addS32Fast(_PREHASH_PriceGroupCreate, this_ptr->getPriceGroupCreate());
+
+ msg->sendReliable(msg->getSender());
+}
+
+
+S32 LLRegionEconomy::getPriceParcelClaim() const
+{
+ //return (S32)((F32)mBasePriceParcelClaim * (mAreaTotal / (mAreaTotal - mAreaOwned)));
+ return (S32)((F32)mBasePriceParcelClaimActual * mPriceParcelClaimFactor);
+}
+
+S32 LLRegionEconomy::getPriceParcelRent() const
+{
+ return mBasePriceParcelRent;
+}
+
+
+void LLRegionEconomy::print()
+{
+ this->LLGlobalEconomy::print();
+
+ llinfos << "Region Economy Settings: " << llendl;
+ llinfos << "Land (square meters): " << mAreaTotal << llendl;
+ llinfos << "Owned Land (square meters): " << mAreaOwned << llendl;
+ llinfos << "Daily Object Rent: " << mPriceObjectRent << llendl;
+ llinfos << "Daily Land Rent (per meter): " << getPriceParcelRent() << llendl;
+ llinfos << "Energey Efficiency: " << mEnergyEfficiency << llendl;
+}
+
+
+void LLRegionEconomy::setBasePriceParcelClaimDefault(S32 val)
+{
+ mBasePriceParcelClaimDefault = val;
+ if(mBasePriceParcelClaimActual == -1)
+ {
+ mBasePriceParcelClaimActual = val;
+ }
+}
+
+void LLRegionEconomy::setBasePriceParcelClaimActual(S32 val)
+{
+ mBasePriceParcelClaimActual = val;
+}
+
+void LLRegionEconomy::setPriceParcelClaimFactor(F32 val)
+{
+ mPriceParcelClaimFactor = val;
+}
+
+void LLRegionEconomy::setBasePriceParcelRent(S32 val)
+{
+ mBasePriceParcelRent = val;
+}
diff --git a/indra/llinventory/lleconomy.h b/indra/llinventory/lleconomy.h
new file mode 100644
index 0000000000..c71fc9e6d0
--- /dev/null
+++ b/indra/llinventory/lleconomy.h
@@ -0,0 +1,116 @@
+/**
+ * @file lleconomy.h
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLECONOMY_H
+#define LL_LLECONOMY_H
+
+class LLMessageSystem;
+class LLVector3;
+
+class LLGlobalEconomy
+{
+public:
+ LLGlobalEconomy();
+ virtual ~LLGlobalEconomy();
+
+ virtual void print();
+
+ static void processEconomyData(LLMessageSystem *msg, void **user_data);
+
+ S32 calculateTeleportCost(F32 distance) const;
+ S32 calculateLightRent(const LLVector3& object_size) const;
+
+ S32 getObjectCount() const { return mObjectCount; }
+ S32 getObjectCapacity() const { return mObjectCapacity; }
+ S32 getPriceObjectClaim() const { return mPriceObjectClaim; }
+ S32 getPricePublicObjectDecay() const { return mPricePublicObjectDecay; }
+ S32 getPricePublicObjectDelete() const { return mPricePublicObjectDelete; }
+ S32 getPricePublicObjectRelease() const { return mPriceObjectClaim - mPricePublicObjectDelete; }
+ S32 getPriceEnergyUnit() const { return mPriceEnergyUnit; }
+ S32 getPriceUpload() const { return mPriceUpload; }
+ S32 getPriceRentLight() const { return mPriceRentLight; }
+ S32 getTeleportMinPrice() const { return mTeleportMinPrice; }
+ F32 getTeleportPriceExponent() const { return mTeleportPriceExponent; }
+ S32 getPriceGroupCreate() const { return mPriceGroupCreate; }
+
+
+ void setObjectCount(S32 val) { mObjectCount = val; }
+ void setObjectCapacity(S32 val) { mObjectCapacity = val; }
+ void setPriceObjectClaim(S32 val) { mPriceObjectClaim = val; }
+ void setPricePublicObjectDecay(S32 val) { mPricePublicObjectDecay = val; }
+ void setPricePublicObjectDelete(S32 val) { mPricePublicObjectDelete = val; }
+ void setPriceEnergyUnit(S32 val) { mPriceEnergyUnit = val; }
+ void setPriceUpload(S32 val) { mPriceUpload = val; }
+ void setPriceRentLight(S32 val) { mPriceRentLight = val; }
+ void setTeleportMinPrice(S32 val) { mTeleportMinPrice = val; }
+ void setTeleportPriceExponent(F32 val) { mTeleportPriceExponent = val; }
+ void setPriceGroupCreate(S32 val) { mPriceGroupCreate = val; }
+
+private:
+ S32 mObjectCount;
+ S32 mObjectCapacity;
+ S32 mPriceObjectClaim; // per primitive
+ S32 mPricePublicObjectDecay; // per primitive
+ S32 mPricePublicObjectDelete; // per primitive
+ S32 mPriceEnergyUnit;
+ S32 mPriceUpload;
+ S32 mPriceRentLight;
+ S32 mTeleportMinPrice;
+ F32 mTeleportPriceExponent;
+ S32 mPriceGroupCreate;
+};
+
+
+class LLRegionEconomy : public LLGlobalEconomy
+{
+public:
+ LLRegionEconomy();
+ ~LLRegionEconomy();
+
+ static void processEconomyData(LLMessageSystem *msg, void **user_data);
+ static void processEconomyDataRequest(LLMessageSystem *msg, void **user_data);
+
+ void print();
+
+ BOOL hasData() const;
+ F32 getPriceObjectRent() const { return mPriceObjectRent; }
+ F32 getPriceObjectScaleFactor() const {return mPriceObjectScaleFactor;}
+ F32 getEnergyEfficiency() const { return mEnergyEfficiency; }
+ S32 getPriceParcelClaim() const;
+ S32 getPriceParcelRent() const;
+ F32 getAreaOwned() const { return mAreaOwned; }
+ F32 getAreaTotal() const { return mAreaTotal; }
+ S32 getBasePriceParcelClaimActual() const { return mBasePriceParcelClaimActual; }
+
+ void setPriceObjectRent(F32 val) { mPriceObjectRent = val; }
+ void setPriceObjectScaleFactor(F32 val) { mPriceObjectScaleFactor = val; }
+ void setEnergyEfficiency(F32 val) { mEnergyEfficiency = val; }
+
+ void setBasePriceParcelClaimDefault(S32 val);
+ void setBasePriceParcelClaimActual(S32 val);
+ void setPriceParcelClaimFactor(F32 val);
+ void setBasePriceParcelRent(S32 val);
+
+ void setAreaOwned(F32 val) { mAreaOwned = val; }
+ void setAreaTotal(F32 val) { mAreaTotal = val; }
+
+private:
+ F32 mPriceObjectRent;
+ F32 mPriceObjectScaleFactor;
+ F32 mEnergyEfficiency;
+
+ S32 mBasePriceParcelClaimDefault;
+ S32 mBasePriceParcelClaimActual;
+ F32 mPriceParcelClaimFactor;
+ S32 mBasePriceParcelRent;
+
+ F32 mAreaOwned;
+ F32 mAreaTotal;
+
+};
+
+#endif
diff --git a/indra/llinventory/llinventory.cpp b/indra/llinventory/llinventory.cpp
new file mode 100644
index 0000000000..bffc4df281
--- /dev/null
+++ b/indra/llinventory/llinventory.cpp
@@ -0,0 +1,1773 @@
+/**
+ * @file llinventory.cpp
+ * @brief Implementation of the inventory system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <time.h>
+
+#include "llinventory.h"
+
+#include "lldbstrings.h"
+#include "llcrypto.h"
+#include "llsd.h"
+#include "message.h"
+#include <boost/tokenizer.hpp>
+
+#include "llsdutil.h"
+
+#include "llsdutil.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const U8 TASK_INVENTORY_ITEM_KEY = 0;
+const U8 TASK_INVENTORY_ASSET_KEY = 1;
+
+const LLUUID MAGIC_ID("3c115e51-04f4-523c-9fa6-98aff1034730");
+
+// helper function which returns true if inventory type and asset type
+// are potentially compatible. For example, an attachment must be an
+// object, but a wearable can be a bodypart or clothing asset.
+bool inventory_and_asset_types_match(
+ LLInventoryType::EType inventory_type,
+ LLAssetType::EType asset_type);
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryType
+///----------------------------------------------------------------------------
+
+// Unlike asset type names, not limited to 8 characters.
+// Need not match asset type names.
+static const char* INVENTORY_TYPE_NAMES[LLInventoryType::IT_COUNT] =
+{
+ "texture", // 0
+ "sound",
+ "callcard",
+ "landmark",
+ NULL,
+ NULL, // 5
+ "object",
+ "notecard",
+ "category",
+ "root",
+ "script", // 10
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "snapshot", // 15
+ NULL,
+ "attach",
+ "wearable",
+ "animation",
+ "gesture", // 20
+};
+
+// This table is meant for decoding to human readable form. Put any
+// and as many printable characters you want in each one.
+// See also LLAssetType::mAssetTypeHumanNames
+static const char* INVENTORY_TYPE_HUMAN_NAMES[LLInventoryType::IT_COUNT] =
+{
+ "texture", // 0
+ "sound",
+ "calling card",
+ "landmark",
+ NULL,
+ NULL, // 5
+ "object",
+ "note card",
+ "folder",
+ "root",
+ "script", // 10
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "snapshot", // 15
+ NULL,
+ "attachment",
+ "wearable",
+ "animation",
+ "gesture", // 20
+};
+
+// Maps asset types to the default inventory type for that kind of asset.
+// Thus, "Lost and Found" is a "Category"
+static const LLInventoryType::EType
+DEFAULT_ASSET_FOR_INV_TYPE[LLAssetType::AT_COUNT] =
+{
+ LLInventoryType::IT_TEXTURE, // AT_TEXTURE
+ LLInventoryType::IT_SOUND, // AT_SOUND
+ LLInventoryType::IT_CALLINGCARD, // AT_CALLINGCARD
+ LLInventoryType::IT_LANDMARK, // AT_LANDMARK
+ LLInventoryType::IT_LSL, // AT_SCRIPT
+ LLInventoryType::IT_WEARABLE, // AT_CLOTHING
+ LLInventoryType::IT_OBJECT, // AT_OBJECT
+ LLInventoryType::IT_NOTECARD, // AT_NOTECARD
+ LLInventoryType::IT_CATEGORY, // AT_CATEGORY
+ LLInventoryType::IT_ROOT_CATEGORY, // AT_ROOT_CATEGORY
+ LLInventoryType::IT_LSL, // AT_LSL_TEXT
+ LLInventoryType::IT_LSL, // AT_LSL_BYTECODE
+ LLInventoryType::IT_TEXTURE, // AT_TEXTURE_TGA
+ LLInventoryType::IT_WEARABLE, // AT_BODYPART
+ LLInventoryType::IT_CATEGORY, // AT_TRASH
+ LLInventoryType::IT_CATEGORY, // AT_SNAPSHOT_CATEGORY
+ LLInventoryType::IT_CATEGORY, // AT_LOST_AND_FOUND
+ LLInventoryType::IT_SOUND, // AT_SOUND_WAV
+ LLInventoryType::IT_NONE, // AT_IMAGE_TGA
+ LLInventoryType::IT_NONE, // AT_IMAGE_JPEG
+ LLInventoryType::IT_ANIMATION, // AT_ANIMATION
+ LLInventoryType::IT_GESTURE, // AT_GESTURE
+};
+
+static const int MAX_POSSIBLE_ASSET_TYPES = 2;
+static const LLAssetType::EType
+INVENTORY_TO_ASSET_TYPE[LLInventoryType::IT_COUNT][MAX_POSSIBLE_ASSET_TYPES] =
+{
+ { LLAssetType::AT_TEXTURE, LLAssetType::AT_NONE }, // IT_TEXTURE
+ { LLAssetType::AT_SOUND, LLAssetType::AT_NONE }, // IT_SOUND
+ { LLAssetType::AT_CALLINGCARD, LLAssetType::AT_NONE }, // IT_CALLINGCARD
+ { LLAssetType::AT_LANDMARK, LLAssetType::AT_NONE }, // IT_LANDMARK
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_OBJECT, LLAssetType::AT_NONE }, // IT_OBJECT
+ { LLAssetType::AT_NOTECARD, LLAssetType::AT_NONE }, // IT_NOTECARD
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, // IT_CATEGORY
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE }, // IT_ROOT_CATEGORY
+ { LLAssetType::AT_LSL_TEXT, LLAssetType::AT_LSL_BYTECODE }, // IT_LSL
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_TEXTURE, LLAssetType::AT_NONE }, // IT_SNAPSHOT
+ { LLAssetType::AT_NONE, LLAssetType::AT_NONE },
+ { LLAssetType::AT_OBJECT, LLAssetType::AT_NONE }, // IT_ATTACHMENT
+ { LLAssetType::AT_CLOTHING, LLAssetType::AT_BODYPART }, // IT_WEARABLE
+ { LLAssetType::AT_ANIMATION, LLAssetType::AT_NONE }, // IT_ANIMATION
+ { LLAssetType::AT_GESTURE, LLAssetType::AT_NONE }, // IT_GESTURE
+};
+
+// static
+const char* LLInventoryType::lookup(EType type)
+{
+ if((type >= 0) && (type < IT_COUNT))
+ {
+ return INVENTORY_TYPE_NAMES[S32(type)];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// static
+LLInventoryType::EType LLInventoryType::lookup(const char* name)
+{
+ for(S32 i = 0; i < IT_COUNT; ++i)
+ {
+ if((INVENTORY_TYPE_NAMES[i])
+ && (0 == strcmp(name, INVENTORY_TYPE_NAMES[i])))
+ {
+ // match
+ return (EType)i;
+ }
+ }
+ return IT_NONE;
+}
+
+// XUI:translate
+// translation from a type to a human readable form.
+// static
+const char* LLInventoryType::lookupHumanReadable(EType type)
+{
+ if((type >= 0) && (type < IT_COUNT))
+ {
+ return INVENTORY_TYPE_HUMAN_NAMES[S32(type)];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// return the default inventory for the given asset type.
+// static
+LLInventoryType::EType LLInventoryType::defaultForAssetType(LLAssetType::EType asset_type)
+{
+ if((asset_type >= 0) && (asset_type < LLAssetType::AT_COUNT))
+ {
+ return DEFAULT_ASSET_FOR_INV_TYPE[S32(asset_type)];
+ }
+ else
+ {
+ return IT_NONE;
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryObject
+///----------------------------------------------------------------------------
+
+LLInventoryObject::LLInventoryObject(
+ const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ LLAssetType::EType type,
+ const LLString& name) :
+ mUUID(uuid),
+ mParentUUID(parent_uuid),
+ mType(type),
+ mName(name)
+{
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ LLString::trim(mName);
+ LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN);
+}
+
+LLInventoryObject::LLInventoryObject() :
+ mType(LLAssetType::AT_NONE)
+{
+}
+
+LLInventoryObject::~LLInventoryObject( void )
+{
+}
+
+void LLInventoryObject::copy(const LLInventoryObject* other)
+{
+ mUUID = other->mUUID;
+ mParentUUID = other->mParentUUID;
+ mType = other->mType;
+ mName = other->mName;
+}
+
+const LLUUID& LLInventoryObject::getUUID() const
+{
+ return mUUID;
+}
+
+const LLUUID& LLInventoryObject::getParentUUID() const
+{
+ return mParentUUID;
+}
+
+const LLString& LLInventoryObject::getName() const
+{
+ return mName;
+}
+
+LLAssetType::EType LLInventoryObject::getType() const
+{
+ return mType;
+}
+
+void LLInventoryObject::setUUID(const LLUUID& new_uuid)
+{
+ mUUID = new_uuid;
+}
+
+void LLInventoryObject::rename(const LLString& n)
+{
+ LLString new_name(n);
+ LLString::replaceNonstandardASCII(new_name, ' ');
+ LLString::replaceChar(new_name, '|', ' ');
+ LLString::trim(new_name);
+ LLString::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN);
+
+ if( new_name != mName )
+ {
+ mName = new_name;
+ }
+}
+
+void LLInventoryObject::setParent(const LLUUID& new_parent)
+{
+ mParentUUID = new_parent;
+}
+
+void LLInventoryObject::setType(LLAssetType::EType type)
+{
+ mType = type;
+}
+
+
+// virtual
+BOOL LLInventoryObject::importLegacyStream(std::istream& input_stream)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(input_stream.good())
+ {
+ input_stream.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %254s %254s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("obj_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %254s %[^|]", keyword, valuestr);
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ LLString::trim(mName);
+ LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in LLInventoryObject::importLegacyStream() for object " << mUUID << llendl;
+ }
+ }
+ return TRUE;
+}
+
+// exportFile should be replaced with exportLegacyStream
+// not sure whether exportLegacyStream(llofstream(fp)) would work, fp may need to get icramented...
+BOOL LLInventoryObject::exportFile(FILE* fp, BOOL) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_object\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\tobj_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ fprintf(fp,"\t}\n");
+ return TRUE;
+}
+
+BOOL LLInventoryObject::exportLegacyStream(std::ostream& output_stream, BOOL) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ output_stream << "\tinv_object\t0\n\t{\n";
+ mUUID.toString(uuid_str);
+ output_stream << "\t\tobj_id\t" << uuid_str << "\n";
+ mParentUUID.toString(uuid_str);
+ output_stream << "\t\tparent_id\t" << uuid_str << "\n";
+ output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n";
+ output_stream << "\t\tname\t" << mName.c_str() << "|\n";
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+
+void LLInventoryObject::removeFromServer()
+{
+ // don't do nothin'
+ llwarns << "LLInventoryObject::removeFromServer() called. Doesn't do anything." << llendl;
+}
+
+void LLInventoryObject::updateParentOnServer(BOOL) const
+{
+ // don't do nothin'
+ llwarns << "LLInventoryObject::updateParentOnServer() called. Doesn't do anything." << llendl;
+}
+
+void LLInventoryObject::updateServer(BOOL) const
+{
+ // don't do nothin'
+ llwarns << "LLInventoryObject::updateServer() called. Doesn't do anything." << llendl;
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryItem
+///----------------------------------------------------------------------------
+
+LLInventoryItem::LLInventoryItem(
+ const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ const LLPermissions& permissions,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc) :
+ LLInventoryObject(uuid, parent_uuid, type, name),
+ mPermissions(permissions),
+ mAssetUUID(asset_uuid),
+ mDescription(desc),
+ mSaleInfo(sale_info),
+ mInventoryType(inv_type),
+ mFlags(flags),
+ mCreationDate(creation_date_utc)
+{
+ LLString::replaceNonstandardASCII(mDescription, ' ');
+ LLString::replaceChar(mDescription, '|', ' ');
+}
+
+LLInventoryItem::LLInventoryItem() :
+ LLInventoryObject(),
+ mPermissions(),
+ mAssetUUID(),
+ mDescription(),
+ mSaleInfo(),
+ mInventoryType(LLInventoryType::IT_NONE),
+ mFlags(0),
+ mCreationDate(0)
+{
+}
+
+LLInventoryItem::LLInventoryItem(const LLInventoryItem* other) :
+ LLInventoryObject()
+{
+ copy(other);
+}
+
+LLInventoryItem::~LLInventoryItem()
+{
+}
+
+// virtual
+void LLInventoryItem::copy(const LLInventoryItem* other)
+{
+ LLInventoryObject::copy(other);
+ mPermissions = other->mPermissions;
+ mAssetUUID = other->mAssetUUID;
+ mDescription = other->mDescription;
+ mSaleInfo = other->mSaleInfo;
+ mInventoryType = other->mInventoryType;
+ mFlags = other->mFlags;
+ mCreationDate = other->mCreationDate;
+}
+
+// As a constructor alternative, the clone() method works like a
+// copy constructor, but gens a new UUID.
+void LLInventoryItem::clone(LLPointer<LLInventoryItem>& newitem) const
+{
+ newitem = new LLInventoryItem;
+ newitem->copy(this);
+ newitem->mUUID.generate();
+}
+
+const LLPermissions& LLInventoryItem::getPermissions() const
+{
+ return mPermissions;
+}
+
+const LLUUID& LLInventoryItem::getCreatorUUID() const
+{
+ return mPermissions.getCreator();
+}
+
+const LLUUID& LLInventoryItem::getAssetUUID() const
+{
+ return mAssetUUID;
+}
+
+void LLInventoryItem::setAssetUUID(const LLUUID& asset_id)
+{
+ mAssetUUID = asset_id;
+}
+
+
+const LLString& LLInventoryItem::getDescription() const
+{
+ return mDescription;
+}
+
+S32 LLInventoryItem::getCreationDate() const
+{
+ return mCreationDate;
+}
+
+U32 LLInventoryItem::getCRC32() const
+{
+ // *FIX: Not a real crc - more of a checksum.
+ // *NOTE: We currently do not validate the name or description,
+ // but if they change in transit, it's no big deal.
+ U32 crc = mUUID.getCRC32();
+ //lldebugs << "1 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mParentUUID.getCRC32();
+ //lldebugs << "2 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mPermissions.getCRC32();
+ //lldebugs << "3 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mAssetUUID.getCRC32();
+ //lldebugs << "4 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mType;
+ //lldebugs << "5 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mInventoryType;
+ //lldebugs << "6 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mFlags;
+ //lldebugs << "7 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mSaleInfo.getCRC32();
+ //lldebugs << "8 crc: " << std::hex << crc << std::dec << llendl;
+ crc += mCreationDate;
+ //lldebugs << "9 crc: " << std::hex << crc << std::dec << llendl;
+ return crc;
+}
+
+
+void LLInventoryItem::setDescription(const LLString& d)
+{
+ LLString new_desc(d);
+ LLString::replaceNonstandardASCII(new_desc, ' ');
+ LLString::replaceChar(new_desc, '|', ' ');
+ if( new_desc != mDescription )
+ {
+ mDescription = new_desc;
+ }
+}
+
+void LLInventoryItem::setPermissions(const LLPermissions& perm)
+{
+ mPermissions = perm;
+}
+
+void LLInventoryItem::setInventoryType(LLInventoryType::EType inv_type)
+{
+ mInventoryType = inv_type;
+}
+
+void LLInventoryItem::setFlags(U32 flags)
+{
+ mFlags = flags;
+}
+
+void LLInventoryItem::setCreationDate(S32 creation_date_utc)
+{
+ mCreationDate = creation_date_utc;
+}
+
+
+const LLSaleInfo& LLInventoryItem::getSaleInfo() const
+{
+ return mSaleInfo;
+}
+
+void LLInventoryItem::setSaleInfo(const LLSaleInfo& sale_info)
+{
+ mSaleInfo = sale_info;
+}
+
+LLInventoryType::EType LLInventoryItem::getInventoryType() const
+{
+ return mInventoryType;
+}
+
+U32 LLInventoryItem::getFlags() const
+{
+ return mFlags;
+}
+
+// virtual
+void LLInventoryItem::packMessage(LLMessageSystem* msg) const
+{
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
+ mPermissions.packMessage(msg);
+ msg->addUUIDFast(_PREHASH_AssetID, mAssetUUID);
+ S8 type = static_cast<S8>(mType);
+ msg->addS8Fast(_PREHASH_Type, type);
+ type = static_cast<S8>(mInventoryType);
+ msg->addS8Fast(_PREHASH_InvType, type);
+ msg->addU32Fast(_PREHASH_Flags, mFlags);
+ mSaleInfo.packMessage(msg);
+ msg->addStringFast(_PREHASH_Name, mName);
+ msg->addStringFast(_PREHASH_Description, mDescription);
+ msg->addS32Fast(_PREHASH_CreationDate, mCreationDate);
+ U32 crc = getCRC32();
+ msg->addU32Fast(_PREHASH_CRC, crc);
+}
+
+// virtual
+BOOL LLInventoryItem::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num)
+{
+ msg->getUUIDFast(block, _PREHASH_ItemID, mUUID, block_num);
+ msg->getUUIDFast(block, _PREHASH_FolderID, mParentUUID, block_num);
+ mPermissions.unpackMessage(msg, block, block_num);
+ msg->getUUIDFast(block, _PREHASH_AssetID, mAssetUUID, block_num);
+
+ S8 type;
+ msg->getS8Fast(block, _PREHASH_Type, type, block_num);
+ mType = static_cast<LLAssetType::EType>(type);
+ msg->getS8(block, "InvType", type, block_num);
+ mInventoryType = static_cast<LLInventoryType::EType>(type);
+
+ msg->getU32Fast(block, _PREHASH_Flags, mFlags, block_num);
+
+ mSaleInfo.unpackMultiMessage(msg, block, block_num);
+
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(block, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, block_num);
+ mName.assign(name);
+ LLString::replaceNonstandardASCII(mName, ' ');
+
+ char desc[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(block, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc, block_num);
+ mDescription.assign(desc);
+ LLString::replaceNonstandardASCII(mDescription, ' ');
+
+ msg->getS32(block, "CreationDate", mCreationDate, block_num);
+
+ U32 local_crc = getCRC32();
+ U32 remote_crc = 0;
+ msg->getU32(block, "CRC", remote_crc, block_num);
+//#define CRC_CHECK
+#ifdef CRC_CHECK
+ if(local_crc == remote_crc)
+ {
+ lldebugs << "crc matches" << llendl;
+ return TRUE;
+ }
+ else
+ {
+ llwarns << "inventory crc mismatch: local=" << std::hex << local_crc
+ << " remote=" << remote_crc << std::dec << llendl;
+ return FALSE;
+ }
+#else
+ return (local_crc == remote_crc);
+#endif
+}
+
+// virtual
+BOOL LLInventoryItem::importFile(FILE* fp)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+ char junk[MAX_STRING];
+ BOOL success = TRUE;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+
+ mInventoryType = LLInventoryType::IT_NONE;
+ mAssetUUID.setNull();
+ while(success && (!feof(fp)))
+ {
+ fgets(buffer, MAX_STRING, fp);
+ sscanf(buffer, " %254s %254s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("item_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("permissions", keyword))
+ {
+ success = mPermissions.importFile(fp);
+ }
+ else if(0 == strcmp("sale_info", keyword))
+ {
+ // Sale info used to contain next owner perm. It is now in
+ // the permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it
+ // should pick up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ success = mSaleInfo.importFile(fp, has_perm_mask, perm_mask);
+ if(has_perm_mask)
+ {
+ if(perm_mask == PERM_NONE)
+ {
+ perm_mask = mPermissions.getMaskOwner();
+ }
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+ }
+ else if(0 == strcmp("shadow_id", keyword))
+ {
+ mAssetUUID.set(valuestr);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+ }
+ else if(0 == strcmp("asset_id", keyword))
+ {
+ mAssetUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("inv_type", keyword))
+ {
+ mInventoryType = LLInventoryType::lookup(valuestr);
+ }
+ else if(0 == strcmp("flags", keyword))
+ {
+ sscanf(valuestr, "%x", &mFlags);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %254s%[\t]%[^|]", keyword, junk, valuestr);
+
+ // IW: sscanf chokes and puts | in valuestr if there's no name
+ if (valuestr[0] == '|')
+ {
+ valuestr[0] = '\000';
+ }
+
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else if(0 == strcmp("desc", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr);
+
+ if (valuestr[0] == '|')
+ {
+ valuestr[0] = '\000';
+ }
+
+ mDescription.assign(valuestr);
+ LLString::replaceNonstandardASCII(mDescription, ' ');
+ /* TODO -- ask Ian about this code
+ const char *donkey = mDescription.c_str();
+ if (donkey[0] == '|')
+ {
+ llerrs << "Donkey" << llendl;
+ }
+ */
+ }
+ else if(0 == strcmp("creation_date", keyword))
+ {
+ sscanf(valuestr, "%d", &mCreationDate);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import of item " << mUUID << llendl;
+ }
+ }
+
+ // Need to convert 1.0 simstate files to a useful inventory type
+ // and potentially deal with bad inventory tyes eg, a landmark
+ // marked as a texture.
+ if((LLInventoryType::IT_NONE == mInventoryType)
+ || !inventory_and_asset_types_match(mInventoryType, mType))
+ {
+ lldebugs << "Resetting inventory type for " << mUUID << llendl;
+ mInventoryType = LLInventoryType::defaultForAssetType(mType);
+ }
+ return success;
+}
+
+BOOL LLInventoryItem::exportFile(FILE* fp, BOOL include_asset_key) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_item\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\titem_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ mPermissions.exportFile(fp);
+
+ // Check for permissions to see the asset id, and if so write it
+ // out as an asset id. Otherwise, apply our cheesy encryption.
+ if(include_asset_key)
+ {
+ U32 mask = mPermissions.getMaskBase();
+ if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ || (mAssetUUID.isNull()))
+ {
+ mAssetUUID.toString(uuid_str);
+ fprintf(fp, "\t\tasset_id\t%s\n", uuid_str);
+ }
+ else
+ {
+ LLUUID shadow_id(mAssetUUID);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.encrypt(shadow_id.mData, UUID_BYTES);
+ shadow_id.toString(uuid_str);
+ fprintf(fp, "\t\tshadow_id\t%s\n", uuid_str);
+ }
+ }
+ else
+ {
+ LLUUID::null.toString(uuid_str);
+ fprintf(fp, "\t\tasset_id\t%s\n", uuid_str);
+ }
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ const char* inv_type_str = LLInventoryType::lookup(mInventoryType);
+ if(inv_type_str) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str);
+ fprintf(fp, "\t\tflags\t%08x\n", mFlags);
+ mSaleInfo.exportFile(fp);
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ fprintf(fp, "\t\tdesc\t%s|\n", mDescription.c_str());
+ fprintf(fp, "\t\tcreation_date\t%d\n", mCreationDate);
+ fprintf(fp,"\t}\n");
+ return TRUE;
+}
+
+// virtual
+BOOL LLInventoryItem::importLegacyStream(std::istream& input_stream)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+ char junk[MAX_STRING];
+ BOOL success = TRUE;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+
+ mInventoryType = LLInventoryType::IT_NONE;
+ mAssetUUID.setNull();
+ while(success && input_stream.good())
+ {
+ input_stream.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("item_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("permissions", keyword))
+ {
+ success = mPermissions.importLegacyStream(input_stream);
+ }
+ else if(0 == strcmp("sale_info", keyword))
+ {
+ // Sale info used to contain next owner perm. It is now in
+ // the permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it
+ // should pick up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ success = mSaleInfo.importLegacyStream(input_stream, has_perm_mask, perm_mask);
+ if(has_perm_mask)
+ {
+ if(perm_mask == PERM_NONE)
+ {
+ perm_mask = mPermissions.getMaskOwner();
+ }
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+ }
+ else if(0 == strcmp("shadow_id", keyword))
+ {
+ mAssetUUID.set(valuestr);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+ }
+ else if(0 == strcmp("asset_id", keyword))
+ {
+ mAssetUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("inv_type", keyword))
+ {
+ mInventoryType = LLInventoryType::lookup(valuestr);
+ }
+ else if(0 == strcmp("flags", keyword))
+ {
+ sscanf(valuestr, "%x", &mFlags);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr);
+
+ // IW: sscanf chokes and puts | in valuestr if there's no name
+ if (valuestr[0] == '|')
+ {
+ valuestr[0] = '\000';
+ }
+
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else if(0 == strcmp("desc", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s%[\t]%[^|]", keyword, junk, valuestr);
+
+ if (valuestr[0] == '|')
+ {
+ valuestr[0] = '\000';
+ }
+
+ mDescription.assign(valuestr);
+ LLString::replaceNonstandardASCII(mDescription, ' ');
+ /* TODO -- ask Ian about this code
+ const char *donkey = mDescription.c_str();
+ if (donkey[0] == '|')
+ {
+ llerrs << "Donkey" << llendl;
+ }
+ */
+ }
+ else if(0 == strcmp("creation_date", keyword))
+ {
+ sscanf(valuestr, "%d", &mCreationDate);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import of item " << mUUID << llendl;
+ }
+ }
+
+ // Need to convert 1.0 simstate files to a useful inventory type
+ // and potentially deal with bad inventory tyes eg, a landmark
+ // marked as a texture.
+ if((LLInventoryType::IT_NONE == mInventoryType)
+ || !inventory_and_asset_types_match(mInventoryType, mType))
+ {
+ lldebugs << "Resetting inventory type for " << mUUID << llendl;
+ mInventoryType = LLInventoryType::defaultForAssetType(mType);
+ }
+ return success;
+}
+
+BOOL LLInventoryItem::exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ output_stream << "\tinv_item\t0\n\t{\n";
+ mUUID.toString(uuid_str);
+ output_stream << "\t\titem_id\t" << uuid_str << "\n";
+ mParentUUID.toString(uuid_str);
+ output_stream << "\t\tparent_id\t" << uuid_str << "\n";
+ mPermissions.exportLegacyStream(output_stream);
+
+ // Check for permissions to see the asset id, and if so write it
+ // out as an asset id. Otherwise, apply our cheesy encryption.
+ if(include_asset_key)
+ {
+ U32 mask = mPermissions.getMaskBase();
+ if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ || (mAssetUUID.isNull()))
+ {
+ mAssetUUID.toString(uuid_str);
+ output_stream << "\t\tasset_id\t" << uuid_str << "\n";
+ }
+ else
+ {
+ LLUUID shadow_id(mAssetUUID);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.encrypt(shadow_id.mData, UUID_BYTES);
+ shadow_id.toString(uuid_str);
+ output_stream << "\t\tshadow_id\t" << uuid_str << "\n";
+ }
+ }
+ else
+ {
+ LLUUID::null.toString(uuid_str);
+ output_stream << "\t\tasset_id\t" << uuid_str << "\n";
+ }
+ output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n";
+ const char* inv_type_str = LLInventoryType::lookup(mInventoryType);
+ if(inv_type_str)
+ output_stream << "\t\tinv_type\t" << inv_type_str << "\n";
+ char buffer[32];
+ sprintf(buffer, "\t\tflags\t%08x\n", mFlags);
+ output_stream << buffer;
+ mSaleInfo.exportLegacyStream(output_stream);
+ output_stream << "\t\tname\t" << mName.c_str() << "|\n";
+ output_stream << "\t\tdesc\t" << mDescription.c_str() << "|\n";
+ output_stream << "\t\tcreation_date\t" << mCreationDate << "\n";
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+LLSD LLInventoryItem::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["item_id"] = mUUID;
+ sd["parent_id"] = mParentUUID;
+ sd["permissions"] = ll_create_sd_from_permissions(mPermissions);
+
+ U32 mask = mPermissions.getMaskBase();
+ if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ || (mAssetUUID.isNull()))
+ {
+ sd["asset_id"] = mAssetUUID;
+ }
+ else
+ {
+ LLUUID shadow_id(mAssetUUID);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.encrypt(shadow_id.mData, UUID_BYTES);
+ sd["shadow_id"] = shadow_id;
+ }
+ sd["type"] = LLAssetType::lookup(mType);
+ const char* inv_type_str = LLInventoryType::lookup(mInventoryType);
+ if(inv_type_str)
+ {
+ sd["inv_type"] = inv_type_str;
+ }
+ sd["flags"] = ll_sd_from_U32(mFlags);
+ sd["sale_info"] = mSaleInfo;
+ sd["name"] = mName;
+ sd["desc"] = mDescription;
+ sd["creation_date"] = mCreationDate;
+
+ return sd;
+}
+
+bool LLInventoryItem::fromLLSD(LLSD& sd)
+{
+ mInventoryType = LLInventoryType::IT_NONE;
+ mAssetUUID.setNull();
+ const char *w;
+
+ w = "item_id";
+ if (sd.has(w))
+ {
+ mUUID = sd[w];
+ }
+ w = "parent_id";
+ if (sd.has(w))
+ {
+ mParentUUID = sd[w];
+ }
+ w = "permissions";
+ if (sd.has(w))
+ {
+ mPermissions = ll_permissions_from_sd(sd[w]);
+ }
+ w = "sale_info";
+ if (sd.has(w))
+ {
+ // Sale info used to contain next owner perm. It is now in
+ // the permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it
+ // should pick up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ if (!mSaleInfo.fromLLSD(sd[w], has_perm_mask, perm_mask))
+ {
+ goto fail;
+ }
+ if (has_perm_mask)
+ {
+ if(perm_mask == PERM_NONE)
+ {
+ perm_mask = mPermissions.getMaskOwner();
+ }
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+ }
+ w = "shadow_id";
+ if (sd.has(w))
+ {
+ mAssetUUID = sd[w];
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+ }
+ w = "asset_id";
+ if (sd.has(w))
+ {
+ mAssetUUID = sd[w];
+ }
+ w = "type";
+ if (sd.has(w))
+ {
+ mType = LLAssetType::lookup(sd[w].asString().c_str());
+ }
+ w = "inv_type";
+ if (sd.has(w))
+ {
+ mInventoryType = LLInventoryType::lookup(sd[w].asString().c_str());
+ }
+ w = "flags";
+ if (sd.has(w))
+ {
+ mFlags = ll_U32_from_sd(sd[w]);
+ }
+ w = "name";
+ if (sd.has(w))
+ {
+ mName = sd[w].asString();
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ w = "desc";
+ if (sd.has(w))
+ {
+ mDescription = sd[w].asString();
+ LLString::replaceNonstandardASCII(mDescription, ' ');
+ }
+ w = "creation_date";
+ if (sd.has(w))
+ {
+ mCreationDate = sd[w];
+ }
+
+ // Need to convert 1.0 simstate files to a useful inventory type
+ // and potentially deal with bad inventory tyes eg, a landmark
+ // marked as a texture.
+ if((LLInventoryType::IT_NONE == mInventoryType)
+ || !inventory_and_asset_types_match(mInventoryType, mType))
+ {
+ lldebugs << "Resetting inventory type for " << mUUID << llendl;
+ mInventoryType = LLInventoryType::defaultForAssetType(mType);
+ }
+
+ return true;
+fail:
+ return false;
+
+}
+
+LLXMLNode *LLInventoryItem::exportFileXML(BOOL include_asset_key) const
+{
+ LLMemType m1(LLMemType::MTYPE_INVENTORY);
+ LLXMLNode *ret = new LLXMLNode("item", FALSE);
+
+ ret->createChild("uuid", TRUE)->setUUIDValue(1, &mUUID);
+ ret->createChild("parent_uuid", TRUE)->setUUIDValue(1, &mParentUUID);
+
+ mPermissions.exportFileXML()->setParent(ret);
+
+ // Check for permissions to see the asset id, and if so write it
+ // out as an asset id. Otherwise, apply our cheesy encryption.
+ if(include_asset_key)
+ {
+ U32 mask = mPermissions.getMaskBase();
+ if(((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ || (mAssetUUID.isNull()))
+ {
+ ret->createChild("asset_id", FALSE)->setUUIDValue(1, &mAssetUUID);
+ }
+ else
+ {
+ LLUUID shadow_id(mAssetUUID);
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.encrypt(shadow_id.mData, UUID_BYTES);
+
+ ret->createChild("shadow_id", FALSE)->setUUIDValue(1, &shadow_id);
+ }
+ }
+
+ LLString type_str = LLAssetType::lookup(mType);
+ LLString inv_type_str = LLInventoryType::lookup(mInventoryType);
+
+ ret->createChild("asset_type", FALSE)->setStringValue(1, &type_str);
+ ret->createChild("inventory_type", FALSE)->setStringValue(1, &inv_type_str);
+ S32 tmp_flags = (S32) mFlags;
+ ret->createChild("flags", FALSE)->setByteValue(4, (U8*)(&tmp_flags), LLXMLNode::ENCODING_HEX);
+
+ mSaleInfo.exportFileXML()->setParent(ret);
+
+ LLString temp;
+ temp.assign(mName);
+ ret->createChild("name", FALSE)->setStringValue(1, &temp);
+ temp.assign(mDescription);
+ ret->createChild("description", FALSE)->setStringValue(1, &temp);
+ ret->createChild("creation_date", FALSE)->setIntValue(1, &mCreationDate);
+
+ return ret;
+}
+
+BOOL LLInventoryItem::importXML(LLXMLNode* node)
+{
+ BOOL success = FALSE;
+ if (node)
+ {
+ success = TRUE;
+ LLXMLNodePtr sub_node;
+ if (node->getChild("uuid", sub_node))
+ success = (1 == sub_node->getUUIDValue(1, &mUUID));
+ if (node->getChild("parent_uuid", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mParentUUID));
+ if (node->getChild("permissions", sub_node))
+ success = success && mPermissions.importXML(sub_node);
+ if (node->getChild("asset_id", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mAssetUUID));
+ if (node->getChild("shadow_id", sub_node))
+ {
+ success = success && (1 == sub_node->getUUIDValue(1, &mAssetUUID));
+ LLXORCipher cipher(MAGIC_ID.mData, UUID_BYTES);
+ cipher.decrypt(mAssetUUID.mData, UUID_BYTES);
+ }
+ if (node->getChild("asset_type", sub_node))
+ mType = LLAssetType::lookup(sub_node->getValue().c_str());
+ if (node->getChild("inventory_type", sub_node))
+ mInventoryType = LLInventoryType::lookup(sub_node->getValue().c_str());
+ if (node->getChild("flags", sub_node))
+ {
+ S32 tmp_flags = 0;
+ success = success && (1 == sub_node->getIntValue(1, &tmp_flags));
+ mFlags = (U32) tmp_flags;
+ }
+ if (node->getChild("sale_info", sub_node))
+ success = success && mSaleInfo.importXML(sub_node);
+ if (node->getChild("name", sub_node))
+ mName = sub_node->getValue();
+ if (node->getChild("description", sub_node))
+ mDescription = sub_node->getValue();
+ if (node->getChild("creation_date", sub_node))
+ success = success && (1 == sub_node->getIntValue(1, &mCreationDate));
+ if (!success)
+ {
+ lldebugs << "LLInventory::importXML() failed for node named '"
+ << node->getName() << "'" << llendl;
+ }
+ }
+ return success;
+}
+
+S32 LLInventoryItem::packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override) const
+{
+ // Figure out which permissions to use.
+ LLPermissions perm;
+ if (perm_override)
+ {
+ // Use the permissions override.
+ perm = *perm_override;
+ }
+ else
+ {
+ // Use the current permissions.
+ perm = getPermissions();
+ }
+
+ // describe the inventory item
+ char* buffer = (char*) bin_bucket;
+ char creator_id_str[UUID_STR_LENGTH];
+
+ perm.getCreator().toString(creator_id_str);
+ char owner_id_str[UUID_STR_LENGTH];
+ perm.getOwner().toString(owner_id_str);
+ char last_owner_id_str[UUID_STR_LENGTH];
+ perm.getLastOwner().toString(last_owner_id_str);
+ char group_id_str[UUID_STR_LENGTH];
+ perm.getGroup().toString(group_id_str);
+ char asset_id_str[UUID_STR_LENGTH];
+ getAssetUUID().toString(asset_id_str);
+ S32 size = sprintf(buffer,
+ "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x",
+ getType(),
+ getInventoryType(),
+ getName().c_str(),
+ creator_id_str,
+ owner_id_str,
+ last_owner_id_str,
+ group_id_str,
+ perm.getMaskBase(),
+ perm.getMaskOwner(),
+ perm.getMaskGroup(),
+ perm.getMaskEveryone(),
+ perm.getMaskNextOwner(),
+ asset_id_str,
+ getDescription().c_str(),
+ getSaleInfo().getSaleType(),
+ getSaleInfo().getSalePrice(),
+ getFlags()) + 1;
+
+ return size;
+}
+
+void LLInventoryItem::unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size)
+{
+ // Early exit on an empty binary bucket.
+ if (bin_bucket_size <= 1) return;
+
+ // Convert the bin_bucket into a string.
+ char* item_buffer = new char[bin_bucket_size+1];
+ memcpy(item_buffer, bin_bucket, bin_bucket_size);
+ item_buffer[bin_bucket_size] = '\0';
+ std::string str(item_buffer);
+
+ lldebugs << "item buffer: " << item_buffer << llendl;
+ delete[] item_buffer;
+
+ // Tokenize the string.
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ // Extract all values.
+ LLUUID item_id;
+ item_id.generate();
+ setUUID(item_id);
+
+ LLAssetType::EType type;
+ type = (LLAssetType::EType)(atoi((*(iter++)).c_str()));
+ setType( type );
+
+ LLInventoryType::EType inv_type;
+ inv_type = (LLInventoryType::EType)(atoi((*(iter++)).c_str()));
+ setInventoryType( inv_type );
+
+ LLString name((*(iter++)).c_str());
+ rename( name );
+
+ LLUUID creator_id((*(iter++)).c_str());
+ LLUUID owner_id((*(iter++)).c_str());
+ LLUUID last_owner_id((*(iter++)).c_str());
+ LLUUID group_id((*(iter++)).c_str());
+ PermissionMask mask_base = strtoul((*(iter++)).c_str(), NULL, 16);
+ PermissionMask mask_owner = strtoul((*(iter++)).c_str(), NULL, 16);
+ PermissionMask mask_group = strtoul((*(iter++)).c_str(), NULL, 16);
+ PermissionMask mask_every = strtoul((*(iter++)).c_str(), NULL, 16);
+ PermissionMask mask_next = strtoul((*(iter++)).c_str(), NULL, 16);
+ LLPermissions perm;
+ perm.init(creator_id, owner_id, last_owner_id, group_id);
+ perm.initMasks(mask_base, mask_owner, mask_group, mask_every, mask_next);
+ setPermissions(perm);
+ //lldebugs << "perm: " << perm << llendl;
+
+ LLUUID asset_id((*(iter++)).c_str());
+ setAssetUUID(asset_id);
+
+ LLString desc((*(iter++)).c_str());
+ setDescription(desc);
+
+ LLSaleInfo::EForSale sale_type;
+ sale_type = (LLSaleInfo::EForSale)(atoi((*(iter++)).c_str()));
+ S32 price = atoi((*(iter++)).c_str());
+ LLSaleInfo sale_info(sale_type, price);
+ setSaleInfo(sale_info);
+
+ U32 flags = strtoul((*(iter++)).c_str(), NULL, 16);
+ setFlags(flags);
+
+ time_t now = time(NULL);
+ setCreationDate(now);
+}
+
+// returns TRUE if a should appear before b
+BOOL item_dictionary_sort( LLInventoryItem* a, LLInventoryItem* b )
+{
+ return (LLString::compareDict( a->getName().c_str(), b->getName().c_str() ) < 0);
+}
+
+// returns TRUE if a should appear before b
+BOOL item_date_sort( LLInventoryItem* a, LLInventoryItem* b )
+{
+ return a->getCreationDate() < b->getCreationDate();
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryCategory
+///----------------------------------------------------------------------------
+
+LLInventoryCategory::LLInventoryCategory(
+ const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ LLAssetType::EType preferred_type,
+ const LLString& name) :
+ LLInventoryObject(uuid, parent_uuid, LLAssetType::AT_CATEGORY, name),
+ mPreferredType(preferred_type)
+{
+}
+
+LLInventoryCategory::LLInventoryCategory() :
+ mPreferredType(LLAssetType::AT_NONE)
+{
+ mType = LLAssetType::AT_CATEGORY;
+}
+
+LLInventoryCategory::LLInventoryCategory(const LLInventoryCategory* other) :
+ LLInventoryObject()
+{
+ copy(other);
+}
+
+LLInventoryCategory::~LLInventoryCategory()
+{
+}
+
+// virtual
+void LLInventoryCategory::copy(const LLInventoryCategory* other)
+{
+ LLInventoryObject::copy(other);
+ mPreferredType = other->mPreferredType;
+}
+
+LLAssetType::EType LLInventoryCategory::getPreferredType() const
+{
+ return mPreferredType;
+}
+
+void LLInventoryCategory::setPreferredType(LLAssetType::EType type)
+{
+ mPreferredType = type;
+}
+
+// virtual
+void LLInventoryCategory::packMessage(LLMessageSystem* msg) const
+{
+ msg->addUUIDFast(_PREHASH_FolderID, mUUID);
+ msg->addUUIDFast(_PREHASH_ParentID, mParentUUID);
+ S8 type = static_cast<S8>(mPreferredType);
+ msg->addS8Fast(_PREHASH_Type, type);
+ msg->addStringFast(_PREHASH_Name, mName);
+}
+
+// virtual
+void LLInventoryCategory::unpackMessage(LLMessageSystem* msg,
+ const char* block,
+ S32 block_num)
+{
+ msg->getUUIDFast(block, _PREHASH_FolderID, mUUID, block_num);
+ msg->getUUIDFast(block, _PREHASH_ParentID, mParentUUID, block_num);
+ S8 type;
+ msg->getS8Fast(block, _PREHASH_Type, type, block_num);
+ mPreferredType = static_cast<LLAssetType::EType>(type);
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(block, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, block_num);
+ mName.assign(name);
+ LLString::replaceNonstandardASCII(mName, ' ');
+}
+
+// virtual
+BOOL LLInventoryCategory::importFile(FILE* fp)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(!feof(fp))
+ {
+ fgets(buffer, MAX_STRING, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("cat_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("pref_type", keyword))
+ {
+ mPreferredType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s %[^|]", keyword, valuestr);
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import category " << mUUID << llendl;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLInventoryCategory::exportFile(FILE* fp, BOOL) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_category\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\tcat_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ fprintf(fp, "\t\tpref_type\t%s\n", LLAssetType::lookup(mPreferredType));
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ fprintf(fp,"\t}\n");
+ return TRUE;
+}
+
+
+// virtual
+BOOL LLInventoryCategory::importLegacyStream(std::istream& input_stream)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(input_stream.good())
+ {
+ input_stream.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("cat_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("pref_type", keyword))
+ {
+ mPreferredType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s %[^|]", keyword, valuestr);
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import category " << mUUID << llendl;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLInventoryCategory::exportLegacyStream(std::ostream& output_stream, BOOL) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ output_stream << "\tinv_category\t0\n\t{\n";
+ mUUID.toString(uuid_str);
+ output_stream << "\t\tcat_id\t" << uuid_str << "\n";
+ mParentUUID.toString(uuid_str);
+ output_stream << "\t\tparent_id\t" << uuid_str << "\n";
+ output_stream << "\t\ttype\t" << LLAssetType::lookup(mType) << "\n";
+ output_stream << "\t\tpref_type\t" << LLAssetType::lookup(mPreferredType) << "\n";
+ output_stream << "\t\tname\t" << mName.c_str() << "|\n";
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+bool inventory_and_asset_types_match(
+ LLInventoryType::EType inventory_type,
+ LLAssetType::EType asset_type)
+{
+ bool rv = false;
+ if((inventory_type >= 0) && (inventory_type < LLInventoryType::IT_COUNT))
+ {
+ for(S32 i = 0; i < MAX_POSSIBLE_ASSET_TYPES; ++i)
+ {
+ if(INVENTORY_TO_ASSET_TYPE[inventory_type][i] == asset_type)
+ {
+ rv = true;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+///----------------------------------------------------------------------------
+/// exported functions
+///----------------------------------------------------------------------------
+
+static const std::string INV_ITEM_ID_LABEL("item_id");
+static const std::string INV_FOLDER_ID_LABEL("folder_id");
+static const std::string INV_PARENT_ID_LABEL("parent_id");
+static const std::string INV_ASSET_TYPE_LABEL("asset_type");
+static const std::string INV_PREFERRED_TYPE_LABEL("preferred_type");
+static const std::string INV_INVENTORY_TYPE_LABEL("inv_type");
+static const std::string INV_NAME_LABEL("name");
+static const std::string INV_DESC_LABEL("description");
+static const std::string INV_PERMISSIONS_LABEL("permissions");
+static const std::string INV_ASSET_ID_LABEL("asset_id");
+static const std::string INV_SALE_INFO_LABEL("sale_info");
+static const std::string INV_FLAGS_LABEL("flags");
+static const std::string INV_CREATION_DATE_LABEL("created_at");
+
+LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item)
+{
+ LLSD rv;
+ if(item.isNull()) return rv;
+ if (item->getType() == LLAssetType::AT_NONE)
+ {
+ llwarns << "ll_create_sd_from_inventory_item() for item with AT_NONE"
+ << llendl;
+ return rv;
+ }
+ rv[INV_ITEM_ID_LABEL] = item->getUUID();
+ rv[INV_PARENT_ID_LABEL] = item->getParentUUID();
+ rv[INV_NAME_LABEL] = item->getName();
+ rv[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(item->getType());
+ rv[INV_ASSET_ID_LABEL] = item->getAssetUUID();
+ rv[INV_DESC_LABEL] = item->getDescription();
+ rv[INV_SALE_INFO_LABEL] = ll_create_sd_from_sale_info(item->getSaleInfo());
+ rv[INV_PERMISSIONS_LABEL] =
+ ll_create_sd_from_permissions(item->getPermissions());
+ rv[INV_INVENTORY_TYPE_LABEL] =
+ LLInventoryType::lookup(item->getInventoryType());
+ rv[INV_FLAGS_LABEL] = (S32)item->getFlags();
+ rv[INV_CREATION_DATE_LABEL] = item->getCreationDate();
+ return rv;
+}
+
+LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item)
+{
+ LLPointer<LLInventoryItem> rv = new LLInventoryItem;
+ rv->setUUID(sd_item[INV_ITEM_ID_LABEL].asUUID());
+ rv->setParent(sd_item[INV_PARENT_ID_LABEL].asUUID());
+ rv->rename(sd_item[INV_NAME_LABEL].asString());
+ rv->setType(
+ LLAssetType::lookup(sd_item[INV_ASSET_TYPE_LABEL].asString().c_str()));
+ rv->setAssetUUID(sd_item[INV_ASSET_ID_LABEL].asUUID());
+ rv->setDescription(sd_item[INV_DESC_LABEL].asString());
+ rv->setSaleInfo(ll_sale_info_from_sd(sd_item[INV_SALE_INFO_LABEL]));
+ rv->setPermissions(ll_permissions_from_sd(sd_item[INV_PERMISSIONS_LABEL]));
+ rv->setInventoryType(
+ LLInventoryType::lookup(
+ sd_item[INV_INVENTORY_TYPE_LABEL].asString().c_str()));
+ rv->setFlags((U32)(sd_item[INV_FLAGS_LABEL].asInteger()));
+ rv->setCreationDate(sd_item[INV_CREATION_DATE_LABEL].asInteger());
+ return rv;
+}
+
+LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat)
+{
+ LLSD rv;
+ if(cat.isNull()) return rv;
+ if (cat->getType() == LLAssetType::AT_NONE)
+ {
+ llwarns << "ll_create_sd_from_inventory_category() for cat with AT_NONE"
+ << llendl;
+ return rv;
+ }
+ rv[INV_FOLDER_ID_LABEL] = cat->getUUID();
+ rv[INV_PARENT_ID_LABEL] = cat->getParentUUID();
+ rv[INV_NAME_LABEL] = cat->getName();
+ rv[INV_ASSET_TYPE_LABEL] = LLAssetType::lookup(cat->getType());
+ if(LLAssetType::AT_NONE != cat->getPreferredType())
+ {
+ rv[INV_PREFERRED_TYPE_LABEL] =
+ LLAssetType::lookup(cat->getPreferredType());
+ }
+ return rv;
+}
+
+LLPointer<LLInventoryCategory> ll_create_category_from_sd(const LLSD& sd_cat)
+{
+ LLPointer<LLInventoryCategory> rv = new LLInventoryCategory;
+ rv->setUUID(sd_cat[INV_FOLDER_ID_LABEL].asUUID());
+ rv->setParent(sd_cat[INV_PARENT_ID_LABEL].asUUID());
+ rv->rename(sd_cat[INV_NAME_LABEL].asString());
+ rv->setType(
+ LLAssetType::lookup(sd_cat[INV_ASSET_TYPE_LABEL].asString().c_str()));
+ rv->setPreferredType(
+ LLAssetType::lookup(
+ sd_cat[INV_PREFERRED_TYPE_LABEL].asString().c_str()));
+ return rv;
+}
diff --git a/indra/llinventory/llinventory.h b/indra/llinventory/llinventory.h
new file mode 100644
index 0000000000..9f750c46b2
--- /dev/null
+++ b/indra/llinventory/llinventory.h
@@ -0,0 +1,411 @@
+/**
+ * @file llinventory.h
+ * @brief LLInventoryItem and LLInventoryCategory class declaration.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVENTORY_H
+#define LL_LLINVENTORY_H
+
+#include <functional>
+
+#include "llassetstorage.h"
+#include "lldarray.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llsd.h"
+#include "lluuid.h"
+#include "llxmlnode.h"
+
+// consts for Key field in the task inventory update message
+extern const U8 TASK_INVENTORY_ITEM_KEY;
+extern const U8 TASK_INVENTORY_ASSET_KEY;
+
+// anonymous enumeration to specify a max inventory buffer size for
+// use in packBinaryBucket()
+enum
+{
+ MAX_INVENTORY_BUFFER_SIZE = 1024
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryType
+//
+// Class used to encapsulate operations around inventory type.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryType
+{
+public:
+ enum EType
+ {
+ IT_TEXTURE = 0,
+ IT_SOUND = 1,
+ IT_CALLINGCARD = 2,
+ IT_LANDMARK = 3,
+ //IT_SCRIPT = 4,
+ //IT_CLOTHING = 5,
+ IT_OBJECT = 6,
+ IT_NOTECARD = 7,
+ IT_CATEGORY = 8,
+ IT_ROOT_CATEGORY = 9,
+ IT_LSL = 10,
+ //IT_LSL_BYTECODE = 11,
+ //IT_TEXTURE_TGA = 12,
+ //IT_BODYPART = 13,
+ //IT_TRASH = 14,
+ IT_SNAPSHOT = 15,
+ //IT_LOST_AND_FOUND = 16,
+ IT_ATTACHMENT = 17,
+ IT_WEARABLE = 18,
+ IT_ANIMATION = 19,
+ IT_GESTURE = 20,
+ IT_COUNT = 21,
+
+ IT_NONE = -1
+ };
+
+ // machine transation between type and strings
+ static EType lookup(const char* name);
+ static const char* lookup(EType type);
+
+ // translation from a type to a human readable form.
+ static const char* lookupHumanReadable(EType type);
+
+ // return the default inventory for the given asset type.
+ static EType defaultForAssetType(LLAssetType::EType asset_type);
+
+private:
+ // don't instantiate or derive one of these objects
+ LLInventoryType( void ) {}
+ ~LLInventoryType( void ) {}
+
+//private:
+// static const char* mInventoryTypeNames[];
+// static const char* mInventoryTypeHumanNames[];
+// static LLInventoryType::EType mInventoryAssetType[];
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryObject
+//
+// This is the base class for inventory objects that handles the
+// common code between items and categories. The 'mParentUUID' member
+// means the parent category since all inventory objects except each
+// user's root category are in some category. Each user's root
+// category will have mParentUUID==LLUUID::null.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMessageSystem;
+
+class LLInventoryObject : public LLRefCount
+{
+protected:
+ LLUUID mUUID;
+ LLUUID mParentUUID;
+ LLAssetType::EType mType;
+ LLString mName;
+
+protected:
+ virtual ~LLInventoryObject( void );
+
+public:
+ MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
+ LLInventoryObject(const LLUUID& uuid, const LLUUID& parent_uuid,
+ LLAssetType::EType type, const LLString& name);
+ LLInventoryObject();
+ virtual void copy(const LLInventoryObject* other); // LLRefCount requires custom copy
+
+ // accessors
+ const LLUUID& getUUID() const;
+ const LLUUID& getParentUUID() const;
+ const LLString& getName() const;
+ LLAssetType::EType getType() const;
+
+ // mutators - will not call updateServer();
+ void setUUID(const LLUUID& new_uuid);
+ void rename(const LLString& new_name);
+ void setParent(const LLUUID& new_parent);
+ void setType(LLAssetType::EType type);
+
+ // file support - implemented here so that a minimal information
+ // set can be transmitted between simulator and viewer.
+// virtual BOOL importFile(FILE* fp);
+ virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const;
+
+ virtual BOOL importLegacyStream(std::istream& input_stream);
+ virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const;
+
+ // virtual methods
+ virtual void removeFromServer();
+ virtual void updateParentOnServer(BOOL) const;
+ virtual void updateServer(BOOL) const;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryItem
+//
+// An inventory item represents something that the current user has in
+// their inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryItem : public LLInventoryObject
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLInventoryItem> > item_array_t;
+
+protected:
+ LLPermissions mPermissions;
+ LLUUID mAssetUUID;
+ LLString mDescription;
+ LLSaleInfo mSaleInfo;
+ LLInventoryType::EType mInventoryType;
+ U32 mFlags;
+ S32 mCreationDate; // seconds from 1/1/1970, UTC
+
+public:
+ enum
+ {
+ // The meaning of LLInventoryItem::mFlags is distinct for each
+ // inventory type.
+ II_FLAGS_NONE = 0,
+
+ // landmark flags
+ II_FLAGS_LANDMARK_VISITED = 1,
+
+ // flag to indicate that object permissions should have next
+ // owner perm be more restrictive on rez. We bump this into
+ // the second byte of the flags since the low byte is used to
+ // track attachment points.
+ II_FLAGS_OBJECT_SLAM_PERM = 0x100,
+
+ // These flags specify which permissions masks to overwrite
+ // upon rez. Normally, if no permissions slam (above) or
+ // overwrite flags are set, the asset's permissions are
+ // used and the inventory's permissions are ignored. If
+ // any of these flags are set, the inventory's permissions
+ // take precedence.
+ II_FLAGS_OBJECT_PERM_OVERWRITE_BASE = 0x010000,
+ II_FLAGS_OBJECT_PERM_OVERWRITE_OWNER = 0x020000,
+ II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP = 0x040000,
+ II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE = 0x080000,
+ II_FLAGS_OBJECT_PERM_OVERWRITE_NEXT_OWNER = 0x100000,
+
+ // wearables use the low order byte of flags to store the
+ // EWearableType enumeration found in newview/llwearable.h
+ };
+
+protected:
+ ~LLInventoryItem(); // ref counted
+
+public:
+ MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
+ LLInventoryItem(const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ const LLPermissions& permissions,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc);
+ LLInventoryItem();
+ // Create a copy of an inventory item from a pointer to another item
+ // Note: Because InventoryItems are ref counted, reference copy (a = b)
+ // is prohibited
+ LLInventoryItem(const LLInventoryItem* other);
+ virtual void copy(const LLInventoryItem* other); // LLRefCount requires custom copy
+
+ // As a constructor alternative, the clone() method works like a
+ // copy constructor, but gens a new UUID.
+ // It is up to the caller to delete (unref) the item.
+ virtual void clone(LLPointer<LLInventoryItem>& newitem) const;
+
+ // accessors
+ const LLPermissions& getPermissions() const;
+ const LLUUID& getCreatorUUID() const;
+ const LLUUID& getAssetUUID() const;
+ const LLString& getDescription() const;
+ const LLSaleInfo& getSaleInfo() const;
+ LLInventoryType::EType getInventoryType() const;
+ U32 getFlags() const;
+ S32 getCreationDate() const;
+ U32 getCRC32() const; // really more of a checksum.
+
+ // mutators - will not call updateServer(), and will never fail
+ // (though it may correct to sane values)
+ void setAssetUUID(const LLUUID& asset_id);
+ void setDescription(const LLString& new_desc);
+ void setSaleInfo(const LLSaleInfo& sale_info);
+ void setPermissions(const LLPermissions& perm);
+ void setInventoryType(LLInventoryType::EType inv_type);
+ void setFlags(U32 flags);
+ void setCreationDate(S32 creation_date_utc);
+
+ // Put this inventory item onto the current outgoing mesage. It
+ // assumes you have already called nextBlock().
+ virtual void packMessage(LLMessageSystem* msg) const;
+
+ // unpack returns TRUE if the inventory item came through the
+ // network ok. It uses a simple crc check which is defeatable, but
+ // we want to detect network mangling somehow.
+ virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+
+ // file support
+ virtual BOOL importFile(FILE* fp);
+ virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const;
+
+ virtual BOOL importLegacyStream(std::istream& input_stream);
+ virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const;
+
+ virtual LLXMLNode *exportFileXML(BOOL include_asset_key = TRUE) const;
+ BOOL importXML(LLXMLNode* node);
+
+ // helper functions
+
+ // pack all information needed to reconstruct this item into the given binary bucket.
+ // perm_override is optional
+ S32 packBinaryBucket(U8* bin_bucket, LLPermissions* perm_override = NULL) const;
+ void unpackBinaryBucket(U8* bin_bucket, S32 bin_bucket_size);
+ LLSD asLLSD() const;
+ bool fromLLSD(LLSD& sd);
+
+};
+
+BOOL item_dictionary_sort(LLInventoryItem* a,LLInventoryItem* b);
+BOOL item_date_sort(LLInventoryItem* a, LLInventoryItem* b);
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryCategory
+//
+// An instance of this class represents a category of inventory
+// items. Users come with a set of default categories, and can create
+// new ones as needed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryCategory : public LLInventoryObject
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLInventoryCategory> > cat_array_t;
+
+protected:
+ ~LLInventoryCategory();
+
+public:
+ MEM_TYPE_NEW(LLMemType::MTYPE_INVENTORY);
+ LLInventoryCategory(const LLUUID& uuid, const LLUUID& parent_uuid,
+ LLAssetType::EType preferred_type,
+ const LLString& name);
+ LLInventoryCategory();
+ LLInventoryCategory(const LLInventoryCategory* other);
+ virtual void copy(const LLInventoryCategory* other); // LLRefCount requires custom copy
+
+ // accessors and mutators
+ LLAssetType::EType getPreferredType() const;
+ void setPreferredType(LLAssetType::EType type);
+ // For messaging system support
+ virtual void packMessage(LLMessageSystem* msg) const;
+ virtual void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+
+ // file support
+ virtual BOOL importFile(FILE* fp);
+ virtual BOOL exportFile(FILE* fp, BOOL include_asset_key = TRUE) const;
+
+ virtual BOOL importLegacyStream(std::istream& input_stream);
+ virtual BOOL exportLegacyStream(std::ostream& output_stream, BOOL include_asset_key = TRUE) const;
+
+protected:
+ // The type of asset that this category was "meant" to hold
+ // (although it may in fact hold any type).
+ LLAssetType::EType mPreferredType;
+
+};
+
+
+//-----------------------------------------------------------------------------
+// Useful bits
+//-----------------------------------------------------------------------------
+
+// This functor tests if an item is transferrable and returns true if
+// it is. Derived from unary_function<> so that the object can be used
+// in stl-compliant adaptable predicates (eg, not1<>). You might want
+// to use this in std::partition() or similar logic.
+struct IsItemTransferable : public std::unary_function<LLInventoryItem*, bool>
+{
+ LLUUID mDestID;
+ IsItemTransferable(const LLUUID& dest_id) : mDestID(dest_id) {}
+ bool operator()(const LLInventoryItem* item) const
+ {
+ return (item->getPermissions().allowTransferTo(mDestID)) ? true:false;
+ }
+};
+
+// This functor is used to set the owner and group of inventory items,
+// for example, in a simple std::for_each() loop. Note that the call
+// to setOwnerAndGroup can fail if authority_id != LLUUID::null.
+struct SetItemOwnerAndGroup
+{
+ LLUUID mAuthorityID;
+ LLUUID mOwnerID;
+ LLUUID mGroupID;
+ SetItemOwnerAndGroup(const LLUUID& authority_id,
+ const LLUUID& owner_id,
+ const LLUUID& group_id) :
+ mAuthorityID(authority_id), mOwnerID(owner_id), mGroupID(group_id) {}
+ void operator()(LLInventoryItem* item) const
+ {
+ LLPermissions perm = item->getPermissions();
+ bool is_atomic = (LLAssetType::AT_OBJECT == item->getType()) ? false : true;
+ perm.setOwnerAndGroup(mAuthorityID, mOwnerID, mGroupID, is_atomic);
+ item->setPermissions(perm);
+ }
+};
+
+// This functor is used to unset the share with group, everyone perms, and
+// for sale info for objects being sold through contents.
+struct SetNotForSale
+{
+ LLUUID mAgentID;
+ LLUUID mGroupID;
+ SetNotForSale(const LLUUID& agent_id,
+ const LLUUID& group_id) :
+ mAgentID(agent_id), mGroupID(group_id) {}
+ void operator()(LLInventoryItem* item) const
+ {
+ // Clear group & everyone permissions.
+ LLPermissions perm = item->getPermissions();
+ perm.setGroupBits(mAgentID, mGroupID, FALSE, PERM_MODIFY | PERM_MOVE | PERM_COPY);
+ perm.setEveryoneBits(mAgentID, mGroupID, FALSE, PERM_MOVE | PERM_COPY);
+ item->setPermissions(perm);
+
+ // Mark group & everyone permissions for overwrite on the next
+ // rez if it is an object.
+ if(LLAssetType::AT_OBJECT == item->getType())
+ {
+ U32 flags = item->getFlags();
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ item->setFlags(flags);
+ }
+
+ // Clear for sale info.
+ item->setSaleInfo(LLSaleInfo::DEFAULT);
+ }
+};
+
+typedef std::list<LLPointer<LLInventoryObject> > InventoryObjectList;
+
+// These functions convert between structured data and an inventroy
+// item, appropriate for serialization.
+LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item);
+LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item);
+LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat);
+LLPointer<LLInventoryCategory> ll_create_category_from_sd(const LLSD& sd_cat);
+
+#endif // LL_LLINVENTORY_H
diff --git a/indra/llinventory/llnotecard.cpp b/indra/llinventory/llnotecard.cpp
new file mode 100644
index 0000000000..3e994a61aa
--- /dev/null
+++ b/indra/llinventory/llnotecard.cpp
@@ -0,0 +1,286 @@
+/**
+ * @file llnotecard.cpp
+ * @brief LLNotecard class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llinventory.h"
+#include "llnotecard.h"
+#include "llstreamtools.h"
+
+LLNotecard::LLNotecard(U32 max_text)
+: mMaxText(max_text)
+{
+}
+
+LLNotecard::~LLNotecard()
+{
+}
+
+bool LLNotecard::importEmbeddedItemsStream(std::istream& str)
+{
+ // Version 1 format:
+ // LLEmbeddedItems version 1
+ // {
+ // count <number of entries being used and not deleted>
+ // {
+ // ext char index <index>
+ // <InventoryItem chunk>
+ // }
+ // }
+
+ S32 i;
+ S32 count = 0;
+
+ str >> std::ws >> "LLEmbeddedItems version" >> mEmbeddedVersion >> "\n";
+ if (str.fail())
+ {
+ llwarns << "Invalid Linden text file header" << llendl;
+ goto import_file_failed;
+ }
+
+ if( 1 != mEmbeddedVersion )
+ {
+ llwarns << "Invalid LLEmbeddedItems version: " << mEmbeddedVersion << llendl;
+ goto import_file_failed;
+ }
+
+ str >> std::ws >> "{\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid Linden text file format: missing {" << llendl;
+ goto import_file_failed;
+ }
+
+ str >> std::ws >> "count " >> count >> "\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems count" << llendl;
+ goto import_file_failed;
+ }
+
+ if((count < 0))
+ {
+ llwarns << "Invalid LLEmbeddedItems count value: " << count << llendl;
+ goto import_file_failed;
+ }
+
+ for(i = 0; i < count; i++)
+ {
+ str >> std::ws >> "{\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: missing {" << llendl;
+ goto import_file_failed;
+ }
+
+ U32 index = 0;
+ str >> std::ws >> "ext char index " >> index >> "\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: missing ext char index" << llendl;
+ goto import_file_failed;
+ }
+
+ if( (index < 0) )
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: invalid ext char index: " << index << llendl;
+ goto import_file_failed;
+ }
+
+ str >> std::ws >> "inv_item\t0\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: missing inv_item" << llendl;
+ goto import_file_failed;
+ }
+
+ LLPointer<LLInventoryItem> item = new LLInventoryItem;
+ if (!item->importLegacyStream(str))
+ {
+ llinfos << "notecard import failed" << llendl;
+ goto import_file_failed;
+ }
+ mItems.push_back(item);
+
+ str >> std::ws >> "}\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: missing }" << llendl;
+ goto import_file_failed;
+ }
+ }
+
+ str >> std::ws >> "}\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid LLEmbeddedItems file format: missing }" << llendl;
+ goto import_file_failed;
+ }
+
+ return true;
+
+ import_file_failed:
+ return false;
+}
+
+bool LLNotecard::importStream(std::istream& str)
+{
+ // Version 1 format:
+ // Linden text version 1
+ // {
+ // <EmbeddedItemList chunk>
+ // Text length
+ // <ASCII text; 0x80 | index = embedded item>
+ // }
+
+ // Version 2 format: (NOTE: Imports identically to version 1)
+ // Linden text version 2
+ // {
+ // <EmbeddedItemList chunk>
+ // Text length
+ // <UTF8 text; FIRST_EMBEDDED_CHAR + index = embedded item>
+ // }
+
+ str >> std::ws >> "Linden text version " >> mVersion >> "\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid Linden text file header " << llendl;
+ return FALSE;
+ }
+
+ if( 1 != mVersion && 2 != mVersion)
+ {
+ llwarns << "Invalid Linden text file version: " << mVersion << llendl;
+ return FALSE;
+ }
+
+ str >> std::ws >> "{\n";
+ if(str.fail())
+ {
+ llwarns << "Invalid Linden text file format" << llendl;
+ return FALSE;
+ }
+
+ if(!importEmbeddedItemsStream(str))
+ {
+ return FALSE;
+ }
+
+ char line_buf[STD_STRING_BUF_SIZE];
+ str.getline(line_buf, STD_STRING_BUF_SIZE);
+ if(str.fail())
+ {
+ llwarns << "Invalid Linden text length field" << llendl;
+ return FALSE;
+ }
+ line_buf[STD_STRING_STR_LEN] = '\0';
+
+ U32 text_len = 0;
+ if( 1 != sscanf(line_buf, "Text length %d", &text_len) )
+ {
+ llwarns << "Invalid Linden text length field" << llendl;
+ return FALSE;
+ }
+
+ if(text_len > mMaxText)
+ {
+ llwarns << "Invalid Linden text length: " << text_len << llendl;
+ return FALSE;
+ }
+
+ BOOL success = TRUE;
+
+ char* text = new char[text_len + 1];
+ fullread(str, text, text_len);
+ if(str.fail())
+ {
+ llwarns << "Invalid Linden text: text shorter than text length: " << text_len << llendl;
+ success = FALSE;
+ }
+ text[text_len] = '\0';
+
+ if(success)
+ {
+ // Actually set the text
+ mText = text;
+ }
+
+ delete[] text;
+
+ return success;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+bool LLNotecard::exportEmbeddedItemsStream( std::ostream& out_stream )
+{
+ out_stream << "LLEmbeddedItems version 1\n";
+ out_stream << "{\n";
+
+ out_stream << llformat("count %d\n", mItems.size() );
+
+ S32 idx = 0;
+ for (std::vector<LLPointer<LLInventoryItem> >::iterator iter = mItems.begin();
+ iter != mItems.end(); ++iter)
+ {
+ LLInventoryItem* item = *iter;
+ if (item)
+ {
+ out_stream << "{\n";
+ out_stream << llformat("ext char index %d\n", idx );
+ if( !item->exportLegacyStream( out_stream ) )
+ {
+ return FALSE;
+ }
+ out_stream << "}\n";
+ }
+ ++idx;
+ }
+
+ out_stream << "}\n";
+
+ return TRUE;
+}
+
+bool LLNotecard::exportStream( std::ostream& out_stream )
+{
+ out_stream << "Linden text version 2\n";
+ out_stream << "{\n";
+
+ if( !exportEmbeddedItemsStream( out_stream ) )
+ {
+ return FALSE;
+ }
+
+ out_stream << llformat("Text length %d\n", mText.length() );
+ out_stream << mText;
+ out_stream << "}\n";
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+const std::vector<LLPointer<LLInventoryItem> >& LLNotecard::getItems() const
+{
+ return mItems;
+}
+
+LLString& LLNotecard::getText()
+{
+ return mText;
+}
+
+void LLNotecard::setItems(const std::vector<LLPointer<LLInventoryItem> >& items)
+{
+ mItems = items;
+}
+
+void LLNotecard::setText(const LLString& text)
+{
+ mText = text;
+}
diff --git a/indra/llinventory/llnotecard.h b/indra/llinventory/llnotecard.h
new file mode 100644
index 0000000000..5f510a674f
--- /dev/null
+++ b/indra/llinventory/llnotecard.h
@@ -0,0 +1,41 @@
+/**
+ * @file llnotecard.h
+ * @brief LLNotecard class declaration
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_NOTECARD_H
+#define LL_NOTECARD_H
+
+const S32 MAX_NOTECARD_SIZE = 65536;
+
+class LLNotecard
+{
+public:
+ LLNotecard(U32 max_text);
+ virtual ~LLNotecard();
+
+ bool importStream(std::istream& str);
+ bool exportStream(std::ostream& str);
+
+ const std::vector<LLPointer<LLInventoryItem> >& getItems() const;
+ LLString& getText();
+
+ void setItems(const std::vector<LLPointer<LLInventoryItem> >& items);
+ void setText(const LLString& text);
+ S32 getVersion() { return mVersion; }
+ S32 getEmbeddedVersion() { return mEmbeddedVersion; }
+
+private:
+ bool importEmbeddedItemsStream(std::istream& str);
+ bool exportEmbeddedItemsStream(std::ostream& str);
+ std::vector<LLPointer<LLInventoryItem> > mItems;
+ LLString mText;
+ U32 mMaxText;
+ S32 mVersion;
+ S32 mEmbeddedVersion;
+};
+
+#endif /* LL_NOTECARD_H */
diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp
new file mode 100644
index 0000000000..a19c2216df
--- /dev/null
+++ b/indra/llinventory/llparcel.cpp
@@ -0,0 +1,1813 @@
+/**
+ * @file llparcel.cpp
+ * @brief A land parcel.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "indra_constants.h"
+#include <iostream>
+
+#include "llparcel.h"
+#include "llstreamtools.h"
+
+#include "llmath.h"
+#include "lltransactiontypes.h"
+#include "lltransactionflags.h"
+#include "message.h"
+#include "u64.h"
+
+static const F32 SOME_BIG_NUMBER = 1000.0f;
+static const F32 SOME_BIG_NEG_NUMBER = -1000.0f;
+static const char* PARCEL_OWNERSHIP_STATUS_STRING[LLParcel::OS_COUNT] =
+{
+ "leased",
+ "lease_pending",
+ "abandoned"
+};
+
+// NOTE: Adding parcel categories also requires updating:
+// * newview/app_settings/floater_directory.xml category combobox
+// * Web site "create event" tools
+static const char* PARCEL_CATEGORY_STRING[LLParcel::C_COUNT] =
+{
+ "none",
+ "linden",
+ "adult",
+ "arts",
+ "store", // "business" legacy name
+ "educational",
+ "game", // "gaming" legacy name
+ "gather", // "hangout" legacy name
+ "newcomer",
+ "park",
+ "home", // "residential" legacy name
+ "shopping",
+ "stage",
+ "other",
+};
+static const char* PARCEL_CATEGORY_UI_STRING[LLParcel::C_COUNT + 1] =
+{
+ "None",
+ "Linden Location",
+ "Adult",
+ "Arts & Culture",
+ "Business",
+ "Educational",
+ "Gaming",
+ "Hangout",
+ "Newcomer Friendly",
+ "Parks & Nature",
+ "Residential",
+ "Shopping",
+ "Stage",
+ "Other",
+ "Any", // valid string for parcel searches
+};
+
+static const char* PARCEL_ACTION_STRING[LLParcel::A_COUNT + 1] =
+{
+ "create",
+ "release",
+ "absorb",
+ "absorbed",
+ "divide",
+ "division",
+ "acquire",
+ "relinquish",
+ "confirm",
+ "unknown"
+};
+
+// Timeouts for parcels
+// default is 21 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 1814400000000
+const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(1814400000000);
+// ***** TESTING is 10 minutes
+//const U64 DEFAULT_USEC_CONVERSION_TIMEOUT = U64L(600000000);
+
+// group is 60 days * 24h/d * 60m/h * 60s/m *1000000 usec/s = 5184000000000
+const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(5184000000000);
+// ***** TESTING is 10 minutes
+//const U64 GROUP_USEC_CONVERSION_TIMEOUT = U64L(600000000);
+
+// default sale timeout is 2 days -> 172800000000
+const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(172800000000);
+// ***** TESTING is 10 minutes
+//const U64 DEFAULT_USEC_SALE_TIMEOUT = U64L(600000000);
+
+// more grace period extensions.
+const U64 SEVEN_DAYS_IN_USEC = U64L(604800000000);
+
+// if more than 100,000s before sale revert, and no extra extension
+// has been given, go ahead and extend it more. That's about 1.2 days.
+const S32 EXTEND_GRACE_IF_MORE_THAN_SEC = 100000;
+
+
+const char* ownership_status_to_string(LLParcel::EOwnershipStatus status);
+LLParcel::EOwnershipStatus ownership_string_to_status(const char* s);
+//const char* revert_action_to_string(LLParcel::ESaleTimerExpireAction action);
+//LLParcel::ESaleTimerExpireAction revert_string_to_action(const char* s);
+const char* category_to_string(LLParcel::ECategory category);
+const char* category_to_ui_string(LLParcel::ECategory category);
+LLParcel::ECategory category_string_to_category(const char* s);
+LLParcel::ECategory category_ui_string_to_category(const char* s);
+
+LLParcel::LLParcel()
+{
+ init(LLUUID::null, TRUE, FALSE, FALSE, 0, 0, 0, 0, 0, 1.f, 0);
+}
+
+
+LLParcel::LLParcel(const LLUUID &owner_id,
+ BOOL modify, BOOL terraform, BOOL damage,
+ time_t claim_date, S32 claim_price_per_meter,
+ S32 rent_price_per_meter, S32 area, S32 sim_object_limit, F32 parcel_object_bonus,
+ BOOL is_group_owned)
+{
+ init( owner_id, modify, terraform, damage, claim_date,
+ claim_price_per_meter, rent_price_per_meter, area, sim_object_limit, parcel_object_bonus,
+ is_group_owned);
+}
+
+
+// virtual
+LLParcel::~LLParcel()
+{
+ // user list cleaned up by LLDynamicArray destructor.
+}
+
+void LLParcel::init(const LLUUID &owner_id,
+ BOOL modify, BOOL terraform, BOOL damage,
+ time_t claim_date, S32 claim_price_per_meter,
+ S32 rent_price_per_meter, S32 area, S32 sim_object_limit, F32 parcel_object_bonus,
+ BOOL is_group_owned)
+{
+ mID.setNull();
+ mOwnerID = owner_id;
+ mGroupOwned = is_group_owned;
+ mClaimDate = claim_date;
+ mClaimPricePerMeter = claim_price_per_meter;
+ mRentPricePerMeter = rent_price_per_meter;
+ mArea = area;
+ mDiscountRate = 1.0f;
+ mDrawDistance = 512.f;
+
+ mUserLookAt.setVec(0.0f, 0.f, 0.f);
+ // Default to using the parcel's landing point, if any.
+ mLandingType = L_LANDING_POINT;
+
+ // *FIX: if owner_id != null, should be owned or sale pending,
+ // investigate init callers.
+ mStatus = OS_NONE;
+ mCategory = C_NONE;
+ mAuthBuyerID.setNull();
+ //mBuyerID.setNull();
+ //mJoinNeighbors = 0x0;
+ mSaleTimerExpires.setTimerExpirySec(0);
+ mSaleTimerExpires.stop();
+ mGraceExtension = 0;
+ //mExpireAction = STEA_REVERT;
+ mRecordTransaction = FALSE;
+
+ mAuctionID = 0;
+ mIsReservedForNewbie = FALSE;
+ mInEscrow = false;
+
+ mParcelFlags = PF_DEFAULT;
+ setParcelFlag(PF_CREATE_OBJECTS, modify);
+ setParcelFlag(PF_ALLOW_TERRAFORM, terraform);
+ setParcelFlag(PF_ALLOW_DAMAGE, damage);
+
+ mSalePrice = 10000;
+ setName(NULL);
+ setDesc(NULL);
+ setMusicURL(NULL);
+ setMediaURL(NULL);
+ mMediaID.setNull();
+ mMediaAutoScale = 0;
+
+ mGroupID.setNull();
+
+ mPassPrice = PARCEL_PASS_PRICE_DEFAULT;
+ mPassHours = PARCEL_PASS_HOURS_DEFAULT;
+
+ mAABBMin.setVec(SOME_BIG_NUMBER, SOME_BIG_NUMBER, SOME_BIG_NUMBER);
+ mAABBMax.setVec(SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER, SOME_BIG_NEG_NUMBER);
+
+ mLocalID = 0;
+
+ //mSimWidePrimCorrection = 0;
+ setMaxPrimCapacity((S32)(sim_object_limit * area / (F32)(REGION_WIDTH_METERS * REGION_WIDTH_METERS)));
+ setSimWideMaxPrimCapacity(0);
+ setSimWidePrimCount(0);
+ setOwnerPrimCount(0);
+ setGroupPrimCount(0);
+ setOtherPrimCount(0);
+ setSelectedPrimCount(0);
+ setTempPrimCount(0);
+ setCleanOtherTime(0);
+ setParcelPrimBonus(parcel_object_bonus);
+
+ setPreviousOwnerID(LLUUID::null);
+ setPreviouslyGroupOwned(FALSE);
+}
+
+void LLParcel::overrideOwner(const LLUUID& owner_id, BOOL is_group_owned)
+{
+ // Override with system permission (LLUUID::null)
+ // Overridden parcels have no group
+ mOwnerID = owner_id;
+ mGroupOwned = is_group_owned;
+ if(mGroupOwned)
+ {
+ mGroupID = mOwnerID;
+ }
+ else
+ {
+ mGroupID.setNull();
+ }
+ mInEscrow = false;
+}
+
+void LLParcel::overrideParcelFlags(U32 flags)
+{
+ mParcelFlags = flags;
+}
+
+void set_std_string(const char* src, std::string& dest)
+{
+ if(src)
+ {
+ dest.assign(src);
+ }
+ else
+ {
+#if (LL_LINUX && __GNUC__ < 3)
+ dest.assign(std::string(""));
+#else
+ dest.clear();
+#endif
+ }
+}
+
+void LLParcel::setName(const char* name)
+{
+ // The escaping here must match the escaping in the database
+ // abstraction layer.
+ set_std_string(name, mName);
+ LLStringFn::replace_nonprintable(mName, LL_UNKNOWN_CHAR);
+}
+
+void LLParcel::setDesc(const char* desc)
+{
+ // The escaping here must match the escaping in the database
+ // abstraction layer.
+ set_std_string(desc, mDesc);
+ mDesc = rawstr_to_utf8(mDesc);
+}
+
+void LLParcel::setMusicURL(const char* url)
+{
+ set_std_string(url, mMusicURL);
+
+ // The escaping here must match the escaping in the database
+ // abstraction layer.
+ // This should really filter the url in some way. Other than
+ // simply requiring non-printable.
+ LLStringFn::replace_nonprintable(mMusicURL, LL_UNKNOWN_CHAR);
+}
+
+void LLParcel::setMediaURL(const char* url)
+{
+ set_std_string(url, mMediaURL);
+
+ // The escaping here must match the escaping in the database
+ // abstraction layer if it's ever added.
+ // This should really filter the url in some way. Other than
+ // simply requiring non-printable.
+ LLStringFn::replace_nonprintable(mMediaURL, LL_UNKNOWN_CHAR);
+}
+
+// virtual
+void LLParcel::setLocalID(S32 local_id)
+{
+ mLocalID = local_id;
+}
+
+void LLParcel::setParcelFlag(U32 flag, BOOL b)
+{
+ if (b)
+ {
+ mParcelFlags |= flag;
+ }
+ else
+ {
+ mParcelFlags &= ~flag;
+ }
+}
+
+
+BOOL LLParcel::allowModifyBy(const LLUUID &agent_id, const LLUUID &group_id) const
+{
+ if (agent_id == LLUUID::null)
+ {
+ // system always can enter
+ return TRUE;
+ }
+ else if (isPublic())
+ {
+ return TRUE;
+ }
+ else if (agent_id == mOwnerID)
+ {
+ // owner can always perform operations
+ return TRUE;
+ }
+ else if (mParcelFlags & PF_CREATE_OBJECTS)
+ {
+ return TRUE;
+ }
+ else if ((mParcelFlags & PF_CREATE_GROUP_OBJECTS)
+ && group_id.notNull() )
+ {
+ return (getGroupID() == group_id);
+ }
+
+ return FALSE;
+}
+
+BOOL LLParcel::allowTerraformBy(const LLUUID &agent_id) const
+{
+ if (agent_id == LLUUID::null)
+ {
+ // system always can enter
+ return TRUE;
+ }
+ else if(OS_LEASED == mStatus)
+ {
+ if(agent_id == mOwnerID)
+ {
+ // owner can modify leased land
+ return TRUE;
+ }
+ else
+ {
+ // otherwise check other people
+ return mParcelFlags & PF_ALLOW_TERRAFORM;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+bool LLParcel::isAgentBlockedFromParcel(LLParcel* parcelp,
+ const LLUUID& agent_id,
+ const std::vector<LLUUID>& group_ids,
+ const BOOL is_agent_identified,
+ const BOOL is_agent_transacted)
+{
+ S32 current_group_access = parcelp->blockAccess(agent_id, LLUUID::null, is_agent_identified, is_agent_transacted);
+ S32 count;
+ bool is_allowed = (current_group_access == BA_ALLOWED) ? true: false;
+ LLUUID group_id;
+
+ count = group_ids.size();
+ for (int i = 0; i < count && !is_allowed; i++)
+ {
+ group_id = group_ids[i];
+ current_group_access = parcelp->blockAccess(agent_id, group_id, is_agent_identified, is_agent_transacted);
+
+ if (current_group_access == BA_ALLOWED) is_allowed = true;
+ }
+
+ return !is_allowed;
+}
+
+BOOL LLParcel::isAgentBanned(const LLUUID& agent_id) const
+{
+ // Test ban list
+ if (getParcelFlag(PF_USE_BAN_LIST)
+ && (mBanList.find(agent_id) != mBanList.end()))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+S32 LLParcel::blockAccess(const LLUUID& agent_id, const LLUUID& group_id,
+ const BOOL is_agent_identified,
+ const BOOL is_agent_transacted) const
+{
+ // Test ban list
+ if (isAgentBanned(agent_id))
+ {
+ return BA_BANNED;
+ }
+
+ // Always allow owner on (unless he banned himself, useful for
+ // testing). We will also allow estate owners/managers in if they
+ // are not explicitly banned.
+ if (agent_id == mOwnerID)
+ {
+ return BA_ALLOWED;
+ }
+
+ // Special case when using pass list where group access is being restricted but not
+ // using access list. In this case group members are allowed only if they buy a pass.
+ // We return BA_NOT_IN_LIST if not in list
+ BOOL passWithGroup = getParcelFlag(PF_USE_PASS_LIST) && !getParcelFlag(PF_USE_ACCESS_LIST)
+ && getParcelFlag(PF_USE_ACCESS_GROUP) && !mGroupID.isNull() && group_id == mGroupID;
+
+
+ // Test group list
+ if (getParcelFlag(PF_USE_ACCESS_GROUP)
+ && !mGroupID.isNull()
+ && group_id == mGroupID
+ && !passWithGroup)
+ {
+ return BA_ALLOWED;
+ }
+
+ // Test access list
+ if (getParcelFlag(PF_USE_ACCESS_LIST) || passWithGroup )
+ {
+ if (mAccessList.find(agent_id) != mAccessList.end())
+ {
+ return BA_ALLOWED;
+ }
+
+ return BA_NOT_ON_LIST;
+ }
+
+ // If we're not doing any other limitations, all users
+ // can enter, unless
+ if ( !getParcelFlag(PF_USE_ACCESS_GROUP)
+ && !getParcelFlag(PF_USE_ACCESS_LIST))
+ {
+ //If the land is group owned, and you are in the group, bypass these checks
+ if(getIsGroupOwned() && group_id == mGroupID)
+ {
+ return BA_ALLOWED;
+ }
+
+ // Test for "payment" access levels
+ // Anonymous - No Payment Info on File
+ if(getParcelFlag(PF_DENY_ANONYMOUS) && !is_agent_identified && !is_agent_transacted)
+ {
+ return BA_NO_ACCESS_LEVEL;
+ }
+ // Identified - Payment Info on File
+ // Must check to make sure we're only banning Identified, since Transacted accounts
+ // also have their identified flag set
+ if(getParcelFlag(PF_DENY_IDENTIFIED) && is_agent_identified && !is_agent_transacted)
+ {
+ return BA_NO_ACCESS_LEVEL;
+ }
+ // Transacted - Payment Info Used
+ if(getParcelFlag(PF_DENY_TRANSACTED) && is_agent_transacted)
+ {
+ return BA_NO_ACCESS_LEVEL;
+ }
+ return BA_ALLOWED;
+ }
+
+ return BA_NOT_IN_GROUP;
+
+}
+
+
+void LLParcel::setArea(S32 area, S32 sim_object_limit)
+{
+ mArea = area;
+ setMaxPrimCapacity((S32)(sim_object_limit * area / (F32)(REGION_WIDTH_METERS * REGION_WIDTH_METERS)));
+}
+
+void LLParcel::setDiscountRate(F32 rate)
+{
+ // this is to make sure that the rate is at least sane - this is
+ // not intended to enforce economy rules. It only enfoces that the
+ // rate is a scaler between 0 and 1.
+ mDiscountRate = llclampf(rate);
+}
+
+
+//-----------------------------------------------------------
+// File input and output
+//-----------------------------------------------------------
+
+
+// WARNING: Area will be wrong until you calculate it.
+BOOL LLParcel::importStream(std::istream& input_stream)
+{
+ U32 setting;
+ S32 secs_until_revert = 0;
+
+ skip_to_end_of_next_keyword("{", input_stream);
+ if (!input_stream.good())
+ {
+ llwarns << "LLParcel::importStream() - bad input_stream" << llendl;
+ return FALSE;
+ }
+
+ while (input_stream.good())
+ {
+ skip_comments_and_emptyspace(input_stream);
+ LLString line, keyword, value;
+ get_line(line, input_stream, MAX_STRING);
+ get_keyword_and_value(keyword, value, line);
+
+ if ("}" == keyword)
+ {
+ break;
+ }
+ else if ("parcel_id" == keyword)
+ {
+ mID.set(value.c_str());
+ }
+ else if ("status" == keyword)
+ {
+ mStatus = ownership_string_to_status(value.c_str());
+ }
+ else if ("category" == keyword)
+ {
+ mCategory = category_string_to_category(value.c_str());
+ }
+ else if ("local_id" == keyword)
+ {
+ LLString::convertToS32(value, mLocalID);
+ }
+ else if ("name" == keyword)
+ {
+ setName( value.c_str() );
+ }
+ else if ("desc" == keyword)
+ {
+ setDesc( value.c_str() );
+ }
+ else if ("music_url" == keyword)
+ {
+ setMusicURL( value.c_str() );
+ }
+ else if ("media_url" == keyword)
+ {
+ setMediaURL( value.c_str() );
+ }
+ else if ("media_id" == keyword)
+ {
+ mMediaID.set( value.c_str() );
+ }
+ else if ("media_auto_scale" == keyword)
+ {
+ LLString::convertToU8(value, mMediaAutoScale);
+ }
+ else if ("owner_id" == keyword)
+ {
+ mOwnerID.set( value.c_str() );
+ }
+ else if ("group_owned" == keyword)
+ {
+ LLString::convertToBOOL(value, mGroupOwned);
+ }
+ else if ("clean_other_time" == keyword)
+ {
+ S32 time;
+ LLString::convertToS32(value, time);
+ setCleanOtherTime(time);
+ }
+ else if ("auth_buyer_id" == keyword)
+ {
+ mAuthBuyerID.set(value.c_str());
+ }
+ else if ("snapshot_id" == keyword)
+ {
+ mSnapshotID.set(value.c_str());
+ }
+ else if ("user_location" == keyword)
+ {
+ sscanf(value.c_str(), "%f %f %f",
+ &mUserLocation.mV[VX],
+ &mUserLocation.mV[VY],
+ &mUserLocation.mV[VZ]);
+ }
+ else if ("user_look_at" == keyword)
+ {
+ sscanf(value.c_str(), "%f %f %f",
+ &mUserLookAt.mV[VX],
+ &mUserLookAt.mV[VY],
+ &mUserLookAt.mV[VZ]);
+ }
+ else if ("landing_type" == keyword)
+ {
+ S32 landing_type = 0;
+ LLString::convertToS32(value, landing_type);
+ mLandingType = (ELandingType) landing_type;
+ }
+ else if ("join_neighbors" == keyword)
+ {
+ llinfos << "found deprecated keyword join_neighbors" << llendl;
+ }
+ else if ("revert_sale" == keyword)
+ {
+ LLString::convertToS32(value, secs_until_revert);
+ if (secs_until_revert > 0)
+ {
+ mSaleTimerExpires.start();
+ mSaleTimerExpires.setTimerExpirySec((F32)secs_until_revert);
+ }
+ }
+ else if("extended_grace" == keyword)
+ {
+ LLString::convertToS32(value, mGraceExtension);
+ }
+ else if ("user_list_type" == keyword)
+ {
+ // deprecated
+ }
+ else if("auction_id" == keyword)
+ {
+ LLString::convertToU32(value, mAuctionID);
+ }
+ else if("reserved_newbie" == keyword)
+ {
+ LLString::convertToBOOL(value, mIsReservedForNewbie);
+ }
+ else if ("allow_modify" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_CREATE_OBJECTS, setting);
+ }
+ else if ("allow_group_modify" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_CREATE_GROUP_OBJECTS, setting);
+ }
+ else if ("allow_all_object_entry" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, setting);
+ }
+ else if ("allow_group_object_entry" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, setting);
+ }
+ else if ("allow_deed_to_group" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_DEED_TO_GROUP, setting);
+ }
+ else if("contribute_with_deed" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_CONTRIBUTE_WITH_DEED, setting);
+ }
+ else if ("allow_terraform" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_TERRAFORM, setting);
+ }
+ else if ("allow_damage" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_DAMAGE, setting);
+ }
+ else if ("allow_fly" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_FLY, setting);
+ }
+ else if ("allow_landmark" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_LANDMARK, setting);
+ }
+ else if ("sound_local" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_SOUND_LOCAL, setting);
+ }
+ else if ("allow_group_scripts" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, setting);
+ }
+ else if ("allow_scripts" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, setting);
+ }
+ else if ("for_sale" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_FOR_SALE, setting);
+ }
+ else if ("sell_w_objects" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_SELL_PARCEL_OBJECTS, setting);
+ }
+ else if ("use_pass_list" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_USE_PASS_LIST, setting);
+ }
+ else if ("show_directory" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_SHOW_DIRECTORY, setting);
+ }
+ else if ("allow_publish" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_ALLOW_PUBLISH, setting);
+ }
+ else if ("mature_publish" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_MATURE_PUBLISH, setting);
+ }
+ else if ("claim_date" == keyword)
+ {
+ // BUG: This will fail when time rolls over in 2038.
+ S32 time;
+ LLString::convertToS32(value, time);
+ mClaimDate = time;
+ }
+ else if ("claim_price" == keyword)
+ {
+ LLString::convertToS32(value, mClaimPricePerMeter);
+ }
+ else if ("rent_price" == keyword)
+ {
+ LLString::convertToS32(value, mRentPricePerMeter);
+ }
+ else if ("discount_rate" == keyword)
+ {
+ LLString::convertToF32(value, mDiscountRate);
+ }
+ else if ("draw_distance" == keyword)
+ {
+ LLString::convertToF32(value, mDrawDistance);
+ }
+ else if ("sale_price" == keyword)
+ {
+ LLString::convertToS32(value, mSalePrice);
+ }
+ else if ("pass_price" == keyword)
+ {
+ LLString::convertToS32(value, mPassPrice);
+ }
+ else if ("pass_hours" == keyword)
+ {
+ LLString::convertToF32(value, mPassHours);
+ }
+ else if ("box" == keyword)
+ {
+ // deprecated
+ }
+ else if ("aabb_min" == keyword)
+ {
+ sscanf(value.c_str(), "%f %f %f",
+ &mAABBMin.mV[VX], &mAABBMin.mV[VY], &mAABBMin.mV[VZ]);
+ }
+ else if ("use_access_group" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_USE_ACCESS_GROUP, setting);
+ }
+ else if ("use_access_list" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_USE_ACCESS_LIST, setting);
+ }
+ else if ("use_ban_list" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_USE_BAN_LIST, setting);
+ }
+ else if ("group_name" == keyword)
+ {
+ llinfos << "found deprecated keyword group_name" << llendl;
+ }
+ else if ("group_id" == keyword)
+ {
+ mGroupID.set( value.c_str() );
+ }
+ // TODO: DEPRECATED FLAG
+ // Flag removed from simstate files in 1.11.1
+ // Remove at some point where we have guarenteed this flag
+ // no longer exists anywhere in simstate files.
+ else if ("require_identified" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_DENY_ANONYMOUS, setting);
+ }
+ // TODO: DEPRECATED FLAG
+ // Flag removed from simstate files in 1.11.1
+ // Remove at some point where we have guarenteed this flag
+ // no longer exists anywhere in simstate files.
+ else if ("require_transacted" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_DENY_ANONYMOUS, setting);
+ setParcelFlag(PF_DENY_IDENTIFIED, setting);
+ }
+ else if ("restrict_pushobject" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_RESTRICT_PUSHOBJECT, setting);
+ }
+ else if ("deny_anonymous" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_DENY_ANONYMOUS, setting);
+ }
+ else if ("deny_identified" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_DENY_IDENTIFIED, setting);
+ }
+ else if ("deny_transacted" == keyword)
+ {
+ LLString::convertToU32(value, setting);
+ setParcelFlag(PF_DENY_TRANSACTED, setting);
+ }
+ else if ("access_list" == keyword)
+ {
+ S32 entry_count = 0;
+ LLString::convertToS32(value, entry_count);
+ for (S32 i = 0; i < entry_count; i++)
+ {
+ LLAccessEntry entry;
+ if (importAccessEntry(input_stream, &entry))
+ {
+ mAccessList[entry.mID] = entry;
+ }
+ }
+ }
+ else if ("ban_list" == keyword)
+ {
+ S32 entry_count = 0;
+ LLString::convertToS32(value, entry_count);
+ for (S32 i = 0; i < entry_count; i++)
+ {
+ LLAccessEntry entry;
+ if (importAccessEntry(input_stream, &entry))
+ {
+ mBanList[entry.mID] = entry;
+ }
+ }
+ }
+ else if ("renter_list" == keyword)
+ {
+ /*
+ S32 entry_count = 0;
+ LLString::convertToS32(value, entry_count);
+ for (S32 i = 0; i < entry_count; i++)
+ {
+ LLAccessEntry entry;
+ if (importAccessEntry(input_stream, &entry))
+ {
+ mRenterList.put(entry);
+ }
+ }*/
+ }
+ else if ("pass_list" == keyword)
+ {
+ // legacy - put into access list
+ S32 entry_count = 0;
+ LLString::convertToS32(value, entry_count);
+ for (S32 i = 0; i < entry_count; i++)
+ {
+ LLAccessEntry entry;
+ if (importAccessEntry(input_stream, &entry))
+ {
+ mAccessList[entry.mID] = entry;
+ }
+ }
+ }
+
+ else
+ {
+ llwarns << "Unknown keyword in parcel section: <"
+ << keyword << ">" << llendl;
+ }
+ }
+
+ // this code block detects if we have loaded a 1.1 simstate file,
+ // and follows the conversion rules specified in
+ // design_docs/land/pay_for_parcel.txt.
+ F32 time_to_expire = 0.0f;
+ if(mID.isNull())
+ {
+ mID.generate();
+ mStatus = OS_LEASE_PENDING;
+ //mBuyerID = mOwnerID;
+ if(getIsGroupOwned())
+ {
+ time_to_expire += GROUP_USEC_CONVERSION_TIMEOUT / SEC_TO_MICROSEC;
+ }
+ else
+ {
+ time_to_expire += DEFAULT_USEC_CONVERSION_TIMEOUT / SEC_TO_MICROSEC;
+ }
+ //mExpireAction = STEA_PUBLIC;
+ mRecordTransaction = TRUE;
+ }
+
+ // this code block deals with giving an extension to pending
+ // parcels to the midday of 2004-01-19 if they were originally set
+ // for some time on 2004-01-12.
+ if((0 == mGraceExtension)
+ && (EXTEND_GRACE_IF_MORE_THAN_SEC < secs_until_revert))
+ {
+ const S32 NEW_CONVERSION_DATE = 1074538800; // 2004-01-19T11:00:00
+ time_t now = time(NULL); // now in epoch
+ secs_until_revert = (S32)(NEW_CONVERSION_DATE - now);
+ time_to_expire = (F32)secs_until_revert;
+ mGraceExtension = 1;
+ }
+
+ // This code block adds yet another week to the deadline. :(
+ if(1 == mGraceExtension)
+ {
+ time_to_expire += SEVEN_DAYS_IN_USEC / SEC_TO_MICROSEC;
+ mGraceExtension = 2;
+ }
+
+ if (time_to_expire > 0)
+ {
+ mSaleTimerExpires.setTimerExpirySec(time_to_expire);
+ mSaleTimerExpires.start();
+ }
+
+ // successful import
+ return TRUE;
+}
+
+
+BOOL LLParcel::importAccessEntry(std::istream& input_stream, LLAccessEntry* entry)
+{
+ skip_to_end_of_next_keyword("{", input_stream);
+ while (input_stream.good())
+ {
+ skip_comments_and_emptyspace(input_stream);
+ LLString line, keyword, value;
+ get_line(line, input_stream, MAX_STRING);
+ get_keyword_and_value(keyword, value, line);
+
+ if ("}" == keyword)
+ {
+ break;
+ }
+ else if ("id" == keyword)
+ {
+ entry->mID.set( value.c_str() );
+ }
+ else if ("name" == keyword)
+ {
+ // deprecated
+ }
+ else if ("time" == keyword)
+ {
+ S32 when;
+ LLString::convertToS32(value, when);
+ entry->mTime = when;
+ }
+ else if ("flags" == keyword)
+ {
+ U32 setting;
+ LLString::convertToU32(value, setting);
+ entry->mFlags = setting;
+ }
+ else
+ {
+ llwarns << "Unknown keyword in parcel access entry section: <"
+ << keyword << ">" << llendl;
+ }
+ }
+ return input_stream.good();
+}
+
+BOOL LLParcel::exportStream(std::ostream& output_stream)
+{
+ S32 setting;
+ char id_string[MAX_STRING];
+
+ std::ios::fmtflags old_flags = output_stream.flags();
+ output_stream.setf(std::ios::showpoint);
+ output_stream << "\t{\n";
+
+ mID.toString(id_string);
+ output_stream << "\t\t parcel_id " << id_string << "\n";
+ output_stream << "\t\t status " << ownership_status_to_string(mStatus) << "\n";
+ output_stream << "\t\t category " << category_to_string(mCategory) << "\n";
+
+ output_stream << "\t\t local_id " << mLocalID << "\n";
+
+ const char* name = (mName.empty() ? "" : mName.c_str() );
+ output_stream << "\t\t name " << name << "\n";
+
+ const char* desc = (mDesc.empty() ? "" : mDesc.c_str() );
+ output_stream << "\t\t desc " << desc << "\n";
+
+ const char* music_url = (mMusicURL.empty() ? "" : mMusicURL.c_str() );
+ output_stream << "\t\t music_url " << music_url << "\n";
+
+ const char* media_url = (mMediaURL.empty() ? "" : mMediaURL.c_str() );
+ output_stream << "\t\t media_url " << media_url << "\n";
+
+ output_stream << "\t\t media_auto_scale " << (mMediaAutoScale ? 1 : 0) << "\n";
+
+ mMediaID.toString(id_string);
+ output_stream << "\t\t media_id " << id_string << "\n";
+
+ mOwnerID.toString(id_string);
+ output_stream << "\t\t owner_id " << id_string << "\n";
+ output_stream << "\t\t group_owned " << (mGroupOwned ? 1 : 0) << "\n";
+ output_stream << "\t\t clean_other_time " << getCleanOtherTime() << "\n";
+
+ if(!mAuthBuyerID.isNull())
+ {
+ mAuthBuyerID.toString(id_string);
+ output_stream << "\t\t auth_buyer_id " << id_string << "\n";
+ }
+ if (!mSnapshotID.isNull())
+ {
+ mSnapshotID.toString(id_string);
+ output_stream << "\t\t snapshot_id " << id_string << "\n";
+ }
+ if (!mUserLocation.isExactlyZero())
+ {
+ output_stream << "\t\t user_location "
+ << (F64)mUserLocation.mV[VX]
+ << " " << (F64)mUserLocation.mV[VY]
+ << " " << (F64)mUserLocation.mV[VZ] << "\n";
+ output_stream << "\t\t user_look_at "
+ << (F64)mUserLookAt.mV[VX]
+ << " " << (F64)mUserLookAt.mV[VY]
+ << " " << (F64)mUserLookAt.mV[VZ] << "\n";
+ }
+ output_stream << "\t\t landing_type " << mLandingType << "\n";
+ //if(mJoinNeighbors)
+ //{
+ // output_stream << "\t\t join_neighbors " << mJoinNeighbors << "\n";
+ //}
+ if(mSaleTimerExpires.getStarted())
+ {
+ S32 dt_sec = (S32) mSaleTimerExpires.getRemainingTimeF32()+60; // Add a minute to prevent race conditions
+ output_stream << "\t\t revert_sale " << dt_sec << "\n";
+ //output_stream << "\t\t revert_action " << revert_action_to_string(mExpireAction) << "\n";
+ output_stream << "\t\t extended_grace " << mGraceExtension << "\n";
+ }
+
+ if(0 != mAuctionID)
+ {
+ output_stream << "\t\t auction_id " << mAuctionID << "\n";
+ }
+ if(mIsReservedForNewbie)
+ {
+ output_stream << "\t\t reserved_newbie " << mIsReservedForNewbie << "\n";
+ }
+
+ output_stream << "\t\t allow_modify " << getAllowModify() << "\n";
+ output_stream << "\t\t allow_group_modify " << getAllowGroupModify() << "\n";
+ output_stream << "\t\t allow_all_object_entry " << getAllowAllObjectEntry() << "\n";
+ output_stream << "\t\t allow_group_object_entry " << getAllowGroupObjectEntry() << "\n";
+ output_stream << "\t\t allow_terraform " << getAllowTerraform() << "\n";
+ output_stream << "\t\t allow_deed_to_group " << getAllowDeedToGroup() << "\n";
+ output_stream << "\t\t contribute_with_deed " << getContributeWithDeed() << "\n";
+ output_stream << "\t\t allow_damage " << getAllowDamage() << "\n";
+ output_stream << "\t\t claim_date " << (S32)mClaimDate << "\n";
+ output_stream << "\t\t claim_price " << mClaimPricePerMeter << "\n";
+ output_stream << "\t\t rent_price " << mRentPricePerMeter << "\n";
+ output_stream << "\t\t discount_rate " << mDiscountRate << "\n";
+ output_stream << "\t\t allow_fly " << (getAllowFly() ? 1 : 0) << "\n";
+ output_stream << "\t\t allow_landmark " << (getAllowLandmark() ? 1 : 0) << "\n";
+ output_stream << "\t\t sound_local " << (getSoundLocal() ? 1 : 0) << "\n";
+ output_stream << "\t\t allow_scripts " << (getAllowOtherScripts() ? 1 : 0) << "\n";
+ output_stream << "\t\t allow_group_scripts " << (getAllowGroupScripts() ? 1 : 0) << "\n";
+ output_stream << "\t\t for_sale " << (getForSale() ? 1 : 0) << "\n";
+ output_stream << "\t\t sell_w_objects " << (getSellWithObjects() ? 1 : 0) << "\n";
+ output_stream << "\t\t draw_distance " << mDrawDistance << "\n";
+ output_stream << "\t\t sale_price " << mSalePrice << "\n";
+
+ setting = (getParcelFlag(PF_USE_ACCESS_GROUP) ? 1 : 0);
+ output_stream << "\t\t use_access_group " << setting << "\n";
+
+ setting = (getParcelFlag(PF_USE_ACCESS_LIST) ? 1 : 0);
+ output_stream << "\t\t use_access_list " << setting << "\n";
+
+ setting = (getParcelFlag(PF_USE_BAN_LIST) ? 1 : 0);
+ output_stream << "\t\t use_ban_list " << setting << "\n";
+
+ mGroupID.toString(id_string);
+ output_stream << "\t\t group_id " << id_string << "\n";
+
+ //const char* group_name
+ // = (mGroupName.isEmpty() ? "" : mGroupName.c_str() );
+ //output_stream << "\t\t group_name " << group_name << "\n";
+
+ setting = (getParcelFlag(PF_USE_PASS_LIST) ? 1 : 0);
+ output_stream << "\t\t use_pass_list " << setting << "\n";
+
+ output_stream << "\t\t pass_price " << mPassPrice << "\n";
+ output_stream << "\t\t pass_hours " << mPassHours << "\n";
+
+ setting = (getParcelFlag(PF_SHOW_DIRECTORY) ? 1 : 0);
+ output_stream << "\t\t show_directory " << setting << "\n";
+
+ setting = (getParcelFlag(PF_ALLOW_PUBLISH) ? 1 : 0);
+ output_stream << "\t\t allow_publish " << setting << "\n";
+
+ setting = (getParcelFlag(PF_MATURE_PUBLISH) ? 1 : 0);
+ output_stream << "\t\t mature_publish " << setting << "\n";
+
+ setting = (getParcelFlag(PF_DENY_ANONYMOUS) ? 1 : 0);
+ output_stream << "\t\t deny_anonymous " << setting << "\n";
+
+ setting = (getParcelFlag(PF_DENY_IDENTIFIED) ? 1 : 0);
+ output_stream << "\t\t deny_identified " << setting << "\n";
+
+ setting = (getParcelFlag(PF_DENY_TRANSACTED) ? 1 : 0);
+ output_stream << "\t\t deny_transacted " << setting << "\n";
+
+ setting = (getParcelFlag(PF_RESTRICT_PUSHOBJECT) ? 1 : 0);
+ output_stream << "\t\t restrict_pushobject " << setting << "\n";
+
+ output_stream << "\t\t aabb_min "
+ << mAABBMin.mV[VX]
+ << " " << mAABBMin.mV[VY]
+ << " " << mAABBMin.mV[VZ] << "\n";
+
+ if (!mAccessList.empty())
+ {
+ output_stream << "\t\t access_list " << mAccessList.size() << "\n";
+ access_map_const_iterator cit = mAccessList.begin();
+ access_map_const_iterator end = mAccessList.end();
+
+ for ( ; cit != end; ++cit)
+ {
+ output_stream << "\t\t{\n";
+ const LLAccessEntry& entry = (*cit).second;
+ entry.mID.toString(id_string);
+ output_stream << "\t\t\tid " << id_string << "\n";
+ output_stream << "\t\t\ttime " << entry.mTime << "\n";
+ output_stream << "\t\t\tflags " << entry.mFlags << "\n";
+ output_stream << "\t\t}\n";
+ }
+ }
+
+ if (!mBanList.empty())
+ {
+ output_stream << "\t\t ban_list " << mBanList.size() << "\n";
+ access_map_const_iterator cit = mBanList.begin();
+ access_map_const_iterator end = mBanList.end();
+
+ for ( ; cit != end; ++cit)
+ {
+ output_stream << "\t\t{\n";
+ const LLAccessEntry& entry = (*cit).second;
+ entry.mID.toString(id_string);
+ output_stream << "\t\t\tid " << id_string << "\n";
+ output_stream << "\t\t\ttime " << entry.mTime << "\n";
+ output_stream << "\t\t\tflags " << entry.mFlags << "\n";
+ output_stream << "\t\t}\n";
+ }
+ }
+
+ /*if (mRenterList.count() > 0)
+ {
+ output_stream << "\t\t renter_list " << mRenterList.count() << "\n";
+ for (i = 0; i < mRenterList.count(); i++)
+ {
+ output_stream << "\t\t{\n";
+ const LLAccessEntry& entry = mRenterList.get(i);
+ entry.mID.toString(id_string);
+ output_stream << "\t\t\tid " << id_string << "\n";
+ output_stream << "\t\t\ttime " << entry.mTime << "\n";
+ output_stream << "\t\t\tflags " << entry.mFlags << "\n";
+ output_stream << "\t\t}\n";
+ }
+ }*/
+
+ output_stream << "\t}\n";
+ output_stream.flags(old_flags);
+
+ return TRUE;
+}
+
+
+// Assumes we are in a block "ParcelData"
+void LLParcel::packMessage(LLMessageSystem* msg)
+{
+ msg->addU32Fast( _PREHASH_ParcelFlags, getParcelFlags() );
+ msg->addS32Fast( _PREHASH_SalePrice, getSalePrice() );
+ msg->addStringFast( _PREHASH_Name, getName() );
+ msg->addStringFast( _PREHASH_Desc, getDesc() );
+ msg->addStringFast( _PREHASH_MusicURL, getMusicURL() );
+ msg->addStringFast( _PREHASH_MediaURL, getMediaURL() );
+ msg->addU8 ( "MediaAutoScale", getMediaAutoScale () );
+ msg->addUUIDFast( _PREHASH_MediaID, getMediaID() );
+ msg->addUUIDFast( _PREHASH_GroupID, getGroupID() );
+ msg->addS32Fast( _PREHASH_PassPrice, mPassPrice );
+ msg->addF32Fast( _PREHASH_PassHours, mPassHours );
+ msg->addU8Fast( _PREHASH_Category, (U8)mCategory);
+ msg->addUUIDFast( _PREHASH_AuthBuyerID, mAuthBuyerID);
+ msg->addUUIDFast( _PREHASH_SnapshotID, mSnapshotID);
+ msg->addVector3Fast(_PREHASH_UserLocation, mUserLocation);
+ msg->addVector3Fast(_PREHASH_UserLookAt, mUserLookAt);
+ msg->addU8Fast( _PREHASH_LandingType, (U8)mLandingType);
+}
+
+
+void LLParcel::unpackMessage(LLMessageSystem* msg)
+{
+ char buffer[256];
+
+ msg->getU32Fast( _PREHASH_ParcelData,_PREHASH_ParcelFlags, mParcelFlags );
+ msg->getS32Fast( _PREHASH_ParcelData,_PREHASH_SalePrice, mSalePrice );
+ msg->getStringFast( _PREHASH_ParcelData,_PREHASH_Name, 256, buffer );
+ setName(buffer);
+ msg->getStringFast( _PREHASH_ParcelData,_PREHASH_Desc, 256, buffer );
+ setDesc(buffer);
+ msg->getStringFast( _PREHASH_ParcelData,_PREHASH_MusicURL, 256, buffer );
+ setMusicURL(buffer);
+ msg->getStringFast( _PREHASH_ParcelData,_PREHASH_MediaURL, 256, buffer );
+ setMediaURL(buffer);
+
+ // non-optimized version
+ msg->getU8 ( "ParcelData", "MediaAutoScale", mMediaAutoScale );
+
+ msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_MediaID, mMediaID );
+ msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_GroupID, mGroupID );
+ msg->getS32Fast( _PREHASH_ParcelData,_PREHASH_PassPrice, mPassPrice );
+ msg->getF32Fast( _PREHASH_ParcelData,_PREHASH_PassHours, mPassHours );
+ U8 category;
+ msg->getU8Fast( _PREHASH_ParcelData,_PREHASH_Category, category);
+ mCategory = (ECategory)category;
+ msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_AuthBuyerID, mAuthBuyerID);
+ msg->getUUIDFast( _PREHASH_ParcelData,_PREHASH_SnapshotID, mSnapshotID);
+ msg->getVector3Fast(_PREHASH_ParcelData,_PREHASH_UserLocation, mUserLocation);
+ msg->getVector3Fast(_PREHASH_ParcelData,_PREHASH_UserLookAt, mUserLookAt);
+ U8 landing_type;
+ msg->getU8Fast( _PREHASH_ParcelData,_PREHASH_LandingType, landing_type);
+ mLandingType = (ELandingType)landing_type;
+}
+
+
+void LLParcel::packAccessEntries(LLMessageSystem* msg,
+ const std::map<LLUUID,LLAccessEntry>& list)
+{
+ access_map_const_iterator cit = list.begin();
+ access_map_const_iterator end = list.end();
+
+ if (cit == end)
+ {
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+ return;
+ }
+
+ for ( ; cit != end; ++cit)
+ {
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ }
+}
+
+
+void LLParcel::unpackAccessEntries(LLMessageSystem* msg,
+ std::map<LLUUID,LLAccessEntry>* list)
+{
+ LLUUID id;
+ S32 time;
+ U32 flags;
+
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_List);
+ for (i = 0; i < count; i++)
+ {
+ msg->getUUIDFast(_PREHASH_List, _PREHASH_ID, id, i);
+ msg->getS32Fast( _PREHASH_List, _PREHASH_Time, time, i);
+ msg->getU32Fast( _PREHASH_List, _PREHASH_Flags, flags, i);
+
+ if (id.notNull())
+ {
+ LLAccessEntry entry;
+ entry.mID = id;
+ entry.mTime = time;
+ entry.mFlags = flags;
+
+ (*list)[entry.mID] = entry;
+ }
+ }
+}
+
+
+void LLParcel::expirePasses(S32 now)
+{
+ access_map_iterator itor = mAccessList.begin();
+ while (itor != mAccessList.end())
+ {
+ const LLAccessEntry& entry = (*itor).second;
+
+ if (entry.mTime != 0 && entry.mTime < now)
+ {
+ mAccessList.erase(itor++);
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+}
+
+
+bool LLParcel::operator==(const LLParcel &rhs) const
+{
+ if (mOwnerID != rhs.mOwnerID)
+ return FALSE;
+
+ if (mParcelFlags != rhs.mParcelFlags)
+ return FALSE;
+
+ if (mClaimDate != rhs.mClaimDate)
+ return FALSE;
+
+ if (mClaimPricePerMeter != rhs.mClaimPricePerMeter)
+ return FALSE;
+
+ if (mRentPricePerMeter != rhs.mRentPricePerMeter)
+ return FALSE;
+
+ return TRUE;
+}
+
+// Calculate rent
+S32 LLParcel::getTotalRent() const
+{
+ return (S32)floor(0.5f + (F32)mArea * (F32)mRentPricePerMeter * (1.0f - mDiscountRate));
+}
+
+F32 LLParcel::getAdjustedRentPerMeter() const
+{
+ return ((F32)mRentPricePerMeter * (1.0f - mDiscountRate));
+}
+
+LLVector3 LLParcel::getCenterpoint() const
+{
+ LLVector3 rv;
+ rv.mV[VX] = (getAABBMin().mV[VX] + getAABBMax().mV[VX]) * 0.5f;
+ rv.mV[VY] = (getAABBMin().mV[VY] + getAABBMax().mV[VY]) * 0.5f;
+ rv.mV[VZ] = 0.0f;
+ return rv;
+}
+
+void LLParcel::extendAABB(const LLVector3& box_min, const LLVector3& box_max)
+{
+ // Patch up min corner of AABB
+ S32 i;
+ for (i=0; i<3; i++)
+ {
+ if (box_min.mV[i] < mAABBMin.mV[i])
+ {
+ mAABBMin.mV[i] = box_min.mV[i];
+ }
+ }
+
+ // Patch up max corner of AABB
+ for (i=0; i<3; i++)
+ {
+ if (box_max.mV[i] > mAABBMax.mV[i])
+ {
+ mAABBMax.mV[i] = box_max.mV[i];
+ }
+ }
+}
+
+BOOL LLParcel::addToAccessList(const LLUUID& agent_id, S32 time)
+{
+ if (!((mParcelFlags & PF_USE_ACCESS_LIST) || (mParcelFlags & PF_USE_PASS_LIST))
+ || mAccessList.size() >= (U32) PARCEL_MAX_ACCESS_LIST)
+ {
+ // Not using access list, so not a rational thing to do
+ return FALSE;
+ }
+ if (agent_id == getOwnerID())
+ {
+ // Can't add owner to these lists
+ return FALSE;
+ }
+ access_map_iterator itor = mAccessList.begin();
+ while (itor != mAccessList.end())
+ {
+ const LLAccessEntry& entry = (*itor).second;
+ if (entry.mID == agent_id)
+ {
+ if (time == 0 || (entry.mTime != 0 && entry.mTime < time))
+ {
+ mAccessList.erase(itor++);
+ }
+ else
+ {
+ // existing one expires later
+ return FALSE;
+ }
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+
+ removeFromBanList(agent_id);
+
+ LLAccessEntry new_entry;
+ new_entry.mID = agent_id;
+ new_entry.mTime = time;
+ new_entry.mFlags = 0x0;
+ mAccessList[new_entry.mID] = new_entry;
+ return TRUE;
+}
+
+BOOL LLParcel::addToBanList(const LLUUID& agent_id, S32 time)
+{
+ if (!(mParcelFlags & PF_USE_BAN_LIST)
+ || mBanList.size() >= (U32) PARCEL_MAX_ACCESS_LIST)
+ {
+ // Not using ban list, so not a rational thing to do
+ return FALSE;
+ }
+ if (agent_id == getOwnerID())
+ {
+ // Can't add owner to these lists
+ return FALSE;
+ }
+
+ access_map_iterator itor = mBanList.begin();
+ while (itor != mBanList.end())
+ {
+ const LLAccessEntry& entry = (*itor).second;
+ if (entry.mID == agent_id)
+ {
+ if (time == 0 || (entry.mTime != 0 && entry.mTime < time))
+ {
+ mBanList.erase(itor++);
+ }
+ else
+ {
+ // existing one expires later
+ return FALSE;
+ }
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+
+ removeFromAccessList(agent_id);
+
+ LLAccessEntry new_entry;
+ new_entry.mID = agent_id;
+ new_entry.mTime = time;
+ new_entry.mFlags = 0x0;
+ mBanList[new_entry.mID] = new_entry;
+ return TRUE;
+}
+
+BOOL remove_from_access_array(std::map<LLUUID,LLAccessEntry>* list,
+ const LLUUID& agent_id)
+{
+ BOOL removed = FALSE;
+ access_map_iterator itor = list->begin();
+ while (itor != list->end())
+ {
+ const LLAccessEntry& entry = (*itor).second;
+ if (entry.mID == agent_id)
+ {
+ list->erase(itor++);
+ removed = TRUE;
+ }
+ else
+ {
+ ++itor;
+ }
+ }
+ return removed;
+}
+
+BOOL LLParcel::removeFromAccessList(const LLUUID& agent_id)
+{
+ return remove_from_access_array(&mAccessList, agent_id);
+}
+
+BOOL LLParcel::removeFromBanList(const LLUUID& agent_id)
+{
+ return remove_from_access_array(&mBanList, agent_id);
+}
+
+// static
+const char* LLParcel::getOwnershipStatusString(EOwnershipStatus status)
+{
+ return ownership_status_to_string(status);
+}
+
+// static
+const char* LLParcel::getCategoryString(ECategory category)
+{
+ return category_to_string(category);
+}
+
+// static
+const char* LLParcel::getCategoryUIString(ECategory category)
+{
+ return category_to_ui_string(category);
+}
+
+// static
+LLParcel::ECategory LLParcel::getCategoryFromString(const char* string)
+{
+ return category_string_to_category(string);
+}
+
+// static
+LLParcel::ECategory LLParcel::getCategoryFromUIString(const char* string)
+{
+ return category_ui_string_to_category(string);
+}
+
+// static
+const char* LLParcel::getActionString(LLParcel::EAction action)
+{
+ S32 index = 0;
+ if((action >= 0) && (action < LLParcel::A_COUNT))
+ {
+ index = action;
+ }
+ else
+ {
+ index = A_COUNT;
+ }
+ return PARCEL_ACTION_STRING[index];
+}
+
+BOOL LLParcel::isSaleTimerExpired(const U64& time)
+{
+ if (mSaleTimerExpires.getStarted() == FALSE)
+ {
+ return FALSE;
+ }
+ BOOL expired = mSaleTimerExpires.checkExpirationAndReset(0.0);
+ if (expired)
+ {
+ mSaleTimerExpires.stop();
+ }
+ return expired;
+}
+
+
+void LLParcel::startSale(const LLUUID& buyer_id, BOOL is_buyer_group)
+{
+ // TODO -- this and all Sale related methods need to move out of the LLParcel
+ // base class and into server-side-only LLSimParcel class
+ setPreviousOwnerID(mOwnerID);
+ setPreviouslyGroupOwned(mGroupOwned);
+
+ mOwnerID = buyer_id;
+ mGroupOwned = is_buyer_group;
+ if(mGroupOwned)
+ {
+ mGroupID = mOwnerID;
+ }
+ else
+ {
+ mGroupID.setNull();
+ }
+ mSaleTimerExpires.start();
+ mSaleTimerExpires.setTimerExpirySec(DEFAULT_USEC_SALE_TIMEOUT / SEC_TO_MICROSEC);
+ mStatus = OS_LEASE_PENDING;
+ mClaimDate = time(NULL);
+ mAuctionID = 0;
+ // clear the autoreturn whenever land changes hands
+ setCleanOtherTime(0);
+}
+
+void LLParcel::expireSale(U32& type, U8& flags, LLUUID& from_id, LLUUID& to_id)
+{
+ mSaleTimerExpires.setTimerExpirySec(0.0);
+ mSaleTimerExpires.stop();
+ setPreviousOwnerID(LLUUID::null);
+ setPreviouslyGroupOwned(FALSE);
+ setSellWithObjects(FALSE);
+ type = TRANS_LAND_RELEASE;
+ mStatus = OS_NONE;
+ mIsReservedForNewbie = FALSE;
+ flags = pack_transaction_flags(mGroupOwned, FALSE);
+ mAuthBuyerID.setNull();
+ from_id = mOwnerID;
+ mOwnerID.setNull();
+ to_id.setNull();
+}
+
+void LLParcel::completeSale(U32& type, U8& flags,
+ LLUUID& to_id)
+{
+ mSaleTimerExpires.setTimerExpirySec(0.0);
+ mSaleTimerExpires.stop();
+ mStatus = OS_LEASED;
+ type = TRANS_LAND_SALE;
+ flags = pack_transaction_flags(mGroupOwned, mGroupOwned);
+ to_id = mOwnerID;
+ mAuthBuyerID.setNull();
+ mIsReservedForNewbie = FALSE;
+
+ // Purchased parcels are assumed to no longer be for sale.
+ // Otherwise someone can snipe the sale.
+ setForSale(FALSE);
+
+ // Turn off show directory, since it's a recurring fee that
+ // the buyer may not want.
+ setParcelFlag(PF_SHOW_DIRECTORY, FALSE);
+
+ //should be cleared on sale.
+ mAccessList.clear();
+ mBanList.clear();
+
+}
+
+void LLParcel::clearSale()
+{
+ mSaleTimerExpires.setTimerExpirySec(0.0);
+ mSaleTimerExpires.stop();
+ if(isPublic())
+ {
+ mStatus = OS_NONE;
+ }
+ else
+ {
+ mStatus = OS_LEASED;
+ }
+ mAuthBuyerID.setNull();
+ setForSale(FALSE);
+ setPreviousOwnerID(LLUUID::null);
+ setPreviouslyGroupOwned(FALSE);
+ setSellWithObjects(FALSE);
+ mIsReservedForNewbie = FALSE;
+}
+
+BOOL LLParcel::isPublic() const
+{
+ return (mOwnerID.isNull());
+}
+
+BOOL LLParcel::isBuyerAuthorized(const LLUUID& buyer_id) const
+{
+ if(mAuthBuyerID.isNull())
+ {
+ return TRUE;
+ }
+ return (mAuthBuyerID == buyer_id);
+}
+
+void LLParcel::clearParcel()
+{
+ overrideParcelFlags(PF_DEFAULT);
+ setName(NULL);
+ setDesc(NULL);
+ setMusicURL(NULL);
+ setMediaURL(NULL);
+ setMediaID(LLUUID::null);
+ setMediaAutoScale(0);
+ setInEscrow(FALSE);
+ setAuthorizedBuyerID(LLUUID::null);
+ setCategory(C_NONE);
+ setSnapshotID(LLUUID::null);
+ setUserLocation(LLVector3::zero);
+ setUserLookAt(LLVector3::x_axis);
+ setLandingType(L_LANDING_POINT);
+ setAuctionID(0);
+ setReservedForNewbie(FALSE);
+ setGroupID(LLUUID::null);
+ setPassPrice(0);
+ setPassHours(0.f);
+ mAccessList.clear();
+ mBanList.clear();
+ //mRenterList.reset();
+}
+
+void LLParcel::dump()
+{
+ llinfos << "parcel " << mLocalID << " area " << mArea << llendl;
+ llinfos << " name <" << mName << ">" << llendl;
+ llinfos << " desc <" << mDesc << ">" << llendl;
+}
+
+const char* ownership_status_to_string(LLParcel::EOwnershipStatus status)
+{
+ if(status >= 0 && status < LLParcel::OS_COUNT)
+ {
+ return PARCEL_OWNERSHIP_STATUS_STRING[status];
+ }
+ return "none";
+}
+
+LLParcel::EOwnershipStatus ownership_string_to_status(const char* s)
+{
+ for(S32 i = 0; i < LLParcel::OS_COUNT; ++i)
+ {
+ if(0 == strcmp(s, PARCEL_OWNERSHIP_STATUS_STRING[i]))
+ {
+ return (LLParcel::EOwnershipStatus)i;
+ }
+ }
+ return LLParcel::OS_NONE;
+}
+
+//const char* revert_action_to_string(LLParcel::ESaleTimerExpireAction action)
+//{
+// S32 index = 0;
+// if(action >= 0 && action < LLParcel::STEA_COUNT)
+// {
+// index = action;
+// }
+// return PARCEL_SALE_TIMER_ACTION[index];
+//}
+
+//LLParcel::ESaleTimerExpireAction revert_string_to_action(const char* s)
+//{
+// for(S32 i = 0; i < LLParcel::STEA_COUNT; ++i)
+// {
+// if(0 == strcmp(s, PARCEL_SALE_TIMER_ACTION[i]))
+// {
+// return (LLParcel::ESaleTimerExpireAction)i;
+// }
+// }
+// return LLParcel::STEA_REVERT;
+//}
+
+const char* category_to_string(LLParcel::ECategory category)
+{
+ S32 index = 0;
+ if((category >= 0) && (category < LLParcel::C_COUNT))
+ {
+ index = category;
+ }
+ return PARCEL_CATEGORY_STRING[index];
+}
+
+const char* category_to_ui_string(LLParcel::ECategory category)
+{
+ S32 index = 0;
+ if((category >= 0) && (category < LLParcel::C_COUNT))
+ {
+ index = category;
+ }
+ else
+ {
+ // C_ANY = -1 , but the "Any" string is at the end of the list
+ index = ((S32) LLParcel::C_COUNT) + 1;
+ }
+ return PARCEL_CATEGORY_UI_STRING[index];
+}
+
+LLParcel::ECategory category_string_to_category(const char* s)
+{
+ for(S32 i = 0; i < LLParcel::C_COUNT; ++i)
+ {
+ if(0 == strcmp(s, PARCEL_CATEGORY_STRING[i]))
+ {
+ return (LLParcel::ECategory)i;
+ }
+ }
+ llwarns << "Parcel category outside of possibilities " << s << llendl;
+ return LLParcel::C_NONE;
+}
+
+LLParcel::ECategory category_ui_string_to_category(const char* s)
+{
+ for(S32 i = 0; i < LLParcel::C_COUNT; ++i)
+ {
+ if(0 == strcmp(s, PARCEL_CATEGORY_UI_STRING[i]))
+ {
+ return (LLParcel::ECategory)i;
+ }
+ }
+ // "Any" is a valid category for searches, and
+ // is a distinct option from "None" and "Other"
+ return LLParcel::C_ANY;
+}
+
diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h
new file mode 100644
index 0000000000..16d3cb01e5
--- /dev/null
+++ b/indra/llinventory/llparcel.h
@@ -0,0 +1,576 @@
+/**
+ * @file llparcel.h
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPARCEL_H
+#define LL_LLPARCEL_H
+
+#include <time.h>
+#include <iostream>
+
+#include "lldarray.h"
+#include "lluuid.h"
+#include "llparcelflags.h"
+#include "llpermissions.h"
+#include "v3math.h"
+
+
+// Grid out of which parcels taken is stepped every 4 meters.
+const F32 PARCEL_GRID_STEP_METERS = 4.f;
+
+// Area of one "square" of parcel
+const S32 PARCEL_UNIT_AREA = 16;
+
+// Height _above_ground_ that parcel boundary ends
+const F32 PARCEL_HEIGHT = 50.f;
+
+//Height above ground which parcel boundries exist for explicitly banned avatars
+const F32 BAN_HEIGHT = 768.f;
+
+// Maximum number of entries in an access list
+const S32 PARCEL_MAX_ACCESS_LIST = 300;
+//Maximum number of entires in an update packet
+//for access/ban lists.
+const F32 PARCEL_MAX_ENTRIES_PER_PACKET = 48.f;
+
+// Weekly charge for listing a parcel in the directory
+const S32 PARCEL_DIRECTORY_FEE = 30;
+
+const S32 PARCEL_PASS_PRICE_DEFAULT = 10;
+const F32 PARCEL_PASS_HOURS_DEFAULT = 1.f;
+
+// Number of "chunks" in which parcel overlay data is sent
+// Chunk 0 = southern rows, entire width
+const S32 PARCEL_OVERLAY_CHUNKS = 4;
+
+// Bottom three bits are a color index for the land overlay
+const U8 PARCEL_COLOR_MASK = 0x07;
+const U8 PARCEL_PUBLIC = 0x00;
+const U8 PARCEL_OWNED = 0x01;
+const U8 PARCEL_GROUP = 0x02;
+const U8 PARCEL_SELF = 0x03;
+const U8 PARCEL_FOR_SALE = 0x04;
+const U8 PARCEL_AUCTION = 0x05;
+// unused 0x06
+// unused 0x07
+// flag, unused 0x08
+// flag, unused 0x10
+const U8 PARCEL_SOUND_LOCAL = 0x20;
+const U8 PARCEL_WEST_LINE = 0x40; // flag, property line on west edge
+const U8 PARCEL_SOUTH_LINE = 0x80; // flag, property line on south edge
+
+// Transmission results for parcel properties
+const S32 PARCEL_RESULT_NO_DATA = -1;
+const S32 PARCEL_RESULT_SUCCESS = 0; // got exactly one parcel
+const S32 PARCEL_RESULT_MULTIPLE = 1; // got multiple parcels
+
+const S32 SELECTED_PARCEL_SEQ_ID = -10000;
+const S32 COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID = -20000;
+const S32 COLLISION_BANNED_PARCEL_SEQ_ID = -30000;
+const S32 COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID = -40000;
+const S32 HOVERED_PARCEL_SEQ_ID = -50000;
+
+const U32 RT_NONE = 0x1 << 0;
+const U32 RT_OWNER = 0x1 << 1;
+const U32 RT_GROUP = 0x1 << 2;
+const U32 RT_OTHER = 0x1 << 3;
+const U32 RT_LIST = 0x1 << 4;
+const U32 RT_SELL = 0x1 << 5;
+
+class LLMessageSystem;
+
+class LLAccessEntry
+{
+public:
+ LLUUID mID;
+ S32 mTime;
+ U32 mFlags;
+};
+
+typedef std::map<LLUUID,LLAccessEntry>::iterator access_map_iterator;
+typedef std::map<LLUUID,LLAccessEntry>::const_iterator access_map_const_iterator;
+
+class LLParcel
+{
+public:
+ enum EOwnershipStatus
+ {
+ OS_LEASED = 0,
+ OS_LEASE_PENDING = 1,
+ OS_ABANDONED = 2,
+ OS_COUNT = 3,
+ OS_NONE = -1
+ };
+ enum ECategory
+ {
+ C_NONE = 0,
+ C_LINDEN,
+ C_ADULT,
+ C_ARTS, // "arts & culture"
+ C_BUSINESS, // was "store"
+ C_EDUCATIONAL,
+ C_GAMING, // was "game"
+ C_HANGOUT, // was "gathering place"
+ C_NEWCOMER,
+ C_PARK, // "parks & nature"
+ C_RESIDENTIAL, // was "homestead"
+ C_SHOPPING,
+ C_STAGE,
+ C_OTHER,
+ C_COUNT,
+ C_ANY = -1 // only useful in queries
+ };
+ enum EAction
+ {
+ A_CREATE = 0,
+ A_RELEASE = 1,
+ A_ABSORB = 2,
+ A_ABSORBED = 3,
+ A_DIVIDE = 4,
+ A_DIVISION = 5,
+ A_ACQUIRE = 6,
+ A_RELINQUISH = 7,
+ A_CONFIRM = 8,
+ A_COUNT = 9,
+ A_UNKNOWN = -1
+ };
+
+ enum ELandingType
+ {
+ L_NONE = 0,
+ L_LANDING_POINT = 1,
+ L_DIRECT = 2
+ };
+
+ // CREATORS
+ LLParcel();
+ LLParcel( const LLUUID &owner_id,
+ BOOL modify, BOOL terraform, BOOL damage,
+ time_t claim_date, S32 claim_price, S32 rent_price, S32 area, S32 sim_object_limit, F32 parcel_object_bonus,
+ BOOL is_group_owned = FALSE);
+ virtual ~LLParcel();
+
+ void init( const LLUUID &owner_id,
+ BOOL modify, BOOL terraform, BOOL damage,
+ time_t claim_date, S32 claim_price, S32 rent_price,
+ S32 area, S32 sim_object_limit, F32 parcel_object_bonus, BOOL is_group_owned = FALSE);
+
+ // TODO: make an actual copy constructor for this
+ void overrideParcelFlags(U32 flags);
+ // if you specify an agent id here, the group id will be zeroed
+ void overrideOwner(const LLUUID& owner_id, BOOL is_group_owned = FALSE);
+ void overrideSaleTimerExpires(F32 secs_left) { mSaleTimerExpires.setTimerExpirySec(secs_left); }
+
+ // MANIPULATORS
+ void generateNewID() { mID.generate(); }
+ void setName(const char* name);
+ void setDesc(const char* desc);
+ void setMusicURL(const char* url);
+ void setMediaURL(const char* url);
+ void setMediaID(const LLUUID& id) { mMediaID = id; }
+ void setMediaAutoScale ( U8 flagIn ) { mMediaAutoScale = flagIn; }
+ virtual void setLocalID(S32 local_id);
+
+ // blow away all the extra crap lurking in parcels, including urls, access lists, etc
+ void clearParcel();
+
+ // This value is not persisted out to the parcel file, it is only
+ // a per-process blocker for attempts to purchase.
+ void setInEscrow(bool in_escrow) { mInEscrow = in_escrow; }
+
+ void setAuthorizedBuyerID(const LLUUID& id) { mAuthBuyerID = id; }
+ //void overrideBuyerID(const LLUUID& id) { mBuyerID = id; }
+ void setCategory(ECategory category) { mCategory = category; }
+ void setSnapshotID(const LLUUID& id) { mSnapshotID = id; }
+ void setUserLocation(const LLVector3& pos) { mUserLocation = pos; }
+ void setUserLookAt(const LLVector3& rot) { mUserLookAt = rot; }
+ void setLandingType(const ELandingType type) { mLandingType = type; }
+
+ void setAuctionID(U32 auction_id) { mAuctionID = auction_id;}
+ void setReservedForNewbie(BOOL reserve) { mIsReservedForNewbie = reserve; }
+
+ void setAllParcelFlags(U32 flags) { mParcelFlags = flags; }
+ void setParcelFlag(U32 flag, BOOL b);
+
+ void setArea(S32 area, S32 sim_object_limit);
+ void setDiscountRate(F32 rate);
+
+ void setAllowModify(BOOL b) { setParcelFlag(PF_CREATE_OBJECTS, b); }
+ void setAllowGroupModify(BOOL b) { setParcelFlag(PF_CREATE_GROUP_OBJECTS, b); }
+ void setAllowAllObjectEntry(BOOL b) { setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, b); }
+ void setAllowGroupObjectEntry(BOOL b) { setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, b); }
+ void setAllowTerraform(BOOL b){setParcelFlag(PF_ALLOW_TERRAFORM, b); }
+ void setAllowDamage(BOOL b) { setParcelFlag(PF_ALLOW_DAMAGE, b); }
+ void setAllowFly(BOOL b) { setParcelFlag(PF_ALLOW_FLY, b); }
+ void setAllowLandmark(BOOL b){ setParcelFlag(PF_ALLOW_LANDMARK, b); }
+ void setAllowGroupScripts(BOOL b) { setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, b); }
+ void setAllowOtherScripts(BOOL b) { setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, b); }
+ void setAllowDeedToGroup(BOOL b) { setParcelFlag(PF_ALLOW_DEED_TO_GROUP, b); }
+ void setContributeWithDeed(BOOL b) { setParcelFlag(PF_CONTRIBUTE_WITH_DEED, b); }
+ void setForSale(BOOL b) { setParcelFlag(PF_FOR_SALE, b); }
+ void setSoundOnly(BOOL b) { setParcelFlag(PF_SOUND_LOCAL, b); }
+ void setDenyAnonymous(BOOL b) { setParcelFlag(PF_DENY_ANONYMOUS, b); }
+ void setDenyIdentified(BOOL b) { setParcelFlag(PF_DENY_IDENTIFIED, b); }
+ void setDenyTransacted(BOOL b) { setParcelFlag(PF_DENY_TRANSACTED, b); }
+ void setRestrictPushObject(BOOL b) { setParcelFlag(PF_RESTRICT_PUSHOBJECT, b); }
+
+ void setDrawDistance(F32 dist) { mDrawDistance = dist; }
+ void setSalePrice(S32 price) { mSalePrice = price; }
+ void setGroupID(const LLUUID& id) { mGroupID = id; }
+ //void setGroupName(const char* s) { mGroupName.assign(s); }
+ void setPassPrice(S32 price) { mPassPrice = price; }
+ void setPassHours(F32 hours) { mPassHours = hours; }
+
+ BOOL importStream(std::istream& input_stream);
+ BOOL importAccessEntry(std::istream& input_stream, LLAccessEntry* entry);
+ BOOL exportStream(std::ostream& output_stream);
+
+ void packMessage(LLMessageSystem* msg);
+ void unpackMessage(LLMessageSystem* msg);
+
+ void packAccessEntries(LLMessageSystem* msg,
+ const std::map<LLUUID,LLAccessEntry>& list);
+ void unpackAccessEntries(LLMessageSystem* msg,
+ std::map<LLUUID,LLAccessEntry>* list);
+
+ void setAABBMin(const LLVector3& min) { mAABBMin = min; }
+ void setAABBMax(const LLVector3& max) { mAABBMax = max; }
+
+ // Extend AABB to include rectangle from min to max.
+ void extendAABB(const LLVector3& box_min, const LLVector3& box_max);
+
+ void dump();
+
+ // Scans the pass list and removes any items with an expiration
+ // time earlier than "now".
+ void expirePasses(S32 now);
+
+ // Add to list, suppressing duplicates. Returns TRUE if added.
+ BOOL addToAccessList(const LLUUID& agent_id, S32 time);
+ BOOL addToBanList(const LLUUID& agent_id, S32 time);
+ BOOL removeFromAccessList(const LLUUID& agent_id);
+ BOOL removeFromBanList(const LLUUID& agent_id);
+
+ // ACCESSORS
+ const LLUUID& getID() { return mID; }
+ const char* getName() const { return mName.c_str(); }
+ const char* getDesc() const { return mDesc.c_str(); }
+ const char* getMusicURL() const { return mMusicURL.c_str(); }
+ const char* getMediaURL() const { return mMediaURL.c_str(); }
+ const LLUUID& getMediaID() const { return mMediaID; }
+ const U8 getMediaAutoScale() const { return mMediaAutoScale; }
+ S32 getLocalID() const { return mLocalID; }
+ const LLUUID& getOwnerID() const { return mOwnerID; }
+ const LLUUID& getGroupID() const { return mGroupID; }
+ //const char* getGroupName() const { return mGroupName.c_str(); }
+ S32 getPassPrice() const { return mPassPrice; }
+ F32 getPassHours() const { return mPassHours; }
+ BOOL getIsGroupOwned() const { return mGroupOwned; }
+
+ U32 getAuctionID() { return mAuctionID; }
+ BOOL getReservedForNewbie() { return mIsReservedForNewbie; }
+ bool isInEscrow() const { return mInEscrow; }
+
+ BOOL isPublic() const;
+
+ // Region-local user-specified position
+ const LLVector3& getUserLocation() const { return mUserLocation; }
+ const LLVector3& getUserLookAt() const { return mUserLookAt; }
+ ELandingType getLandingType() const { return mLandingType; }
+
+ // User-specified snapshot
+ const LLUUID& getSnapshotID() const { return mSnapshotID; }
+
+ // the authorized buyer id is the person who is the only
+ // agent/group that has authority to purchase. (ie, ui specified a
+ // particular agent could buy the plot).
+ const LLUUID& getAuthorizedBuyerID() const { return mAuthBuyerID; }
+
+ // helper function
+ BOOL isBuyerAuthorized(const LLUUID& buyer_id) const;
+
+ // The buyer of a plot is set when someone indicates they want to
+ // buy the plot, and the system is simply waiting for tier-up
+ // approval
+ //const LLUUID& getBuyerID() const { return mBuyerID; }
+
+ // functions to deal with ownership status.
+ EOwnershipStatus getOwnershipStatus() const { return mStatus; }
+ static const char* getOwnershipStatusString(EOwnershipStatus status);
+ void setOwnershipStatus(EOwnershipStatus status) { mStatus = status; }
+
+ // dealing with parcel category information
+ ECategory getCategory() const {return mCategory; }
+ static const char* getCategoryString(ECategory category);
+ static const char* getCategoryUIString(ECategory category);
+ static ECategory getCategoryFromString(const char* string);
+ static ECategory getCategoryFromUIString(const char* string);
+
+ // functions for parcel action (used for logging)
+ static const char* getActionString(EAction action);
+
+ // dealing with sales and parcel conversion.
+ //
+ // the isSaleTimerExpired will trivially return FALSE if there is
+ // no sale going on. Pass in the current time in usec which will
+ // be used for comparison.
+ BOOL isSaleTimerExpired(const U64& time);
+
+ F32 getSaleTimerExpires() { return mSaleTimerExpires.getRemainingTimeF32(); }
+
+ // should the parcel join on complete?
+ //U32 getJoinNeighbors() const { return mJoinNeighbors; }
+
+ // need to record a few things with the parcel when a sale
+ // starts.
+ void startSale(const LLUUID& buyer_id, BOOL is_buyer_group);
+
+ // do the expiration logic, which needs to return values usable in
+ // a money transaction.
+ void expireSale(U32& type, U8& flags, LLUUID& from_id, LLUUID& to_id);
+ void completeSale(U32& type, U8& flags, LLUUID& to_id);
+ void clearSale();
+
+ // this function returns TRUE if the parcel needs conversion to a
+ // lease from a non-owned-status state.
+ BOOL getRecordTransaction() const { return mRecordTransaction; }
+ void setRecordTransaction(BOOL record) { mRecordTransaction = record; }
+
+
+ // more accessors
+ U32 getParcelFlags() const { return mParcelFlags; }
+
+ BOOL getParcelFlag(U32 flag) const
+ { return (mParcelFlags & flag) ? TRUE : FALSE; }
+
+ // objects can be added or modified by anyone (only parcel owner if disabled)
+ BOOL getAllowModify() const
+ { return (mParcelFlags & PF_CREATE_OBJECTS) ? TRUE : FALSE; }
+
+ // objects can be added or modified by group members
+ BOOL getAllowGroupModify() const
+ { return (mParcelFlags & PF_CREATE_GROUP_OBJECTS) ? TRUE : FALSE; }
+
+ // the parcel can be deeded to the group
+ BOOL getAllowDeedToGroup() const
+ { return (mParcelFlags & PF_ALLOW_DEED_TO_GROUP) ? TRUE : FALSE; }
+
+ // Does the owner want to make a contribution along with the deed.
+ BOOL getContributeWithDeed() const
+ { return (mParcelFlags & PF_CONTRIBUTE_WITH_DEED) ? TRUE : FALSE; }
+
+ // heightfield can be modified
+ BOOL getAllowTerraform() const
+ { return (mParcelFlags & PF_ALLOW_TERRAFORM) ? TRUE : FALSE; }
+
+ // avatars can be hurt here
+ BOOL getAllowDamage() const
+ { return (mParcelFlags & PF_ALLOW_DAMAGE) ? TRUE : FALSE; }
+
+ BOOL getAllowFly() const
+ { return (mParcelFlags & PF_ALLOW_FLY) ? TRUE : FALSE; }
+
+ BOOL getAllowLandmark() const
+ { return (mParcelFlags & PF_ALLOW_LANDMARK) ? TRUE : FALSE; }
+
+ BOOL getAllowGroupScripts() const
+ { return (mParcelFlags & PF_ALLOW_GROUP_SCRIPTS) ? TRUE : FALSE; }
+
+ BOOL getAllowOtherScripts() const
+ { return (mParcelFlags & PF_ALLOW_OTHER_SCRIPTS) ? TRUE : FALSE; }
+
+ BOOL getAllowAllObjectEntry() const
+ { return (mParcelFlags & PF_ALLOW_ALL_OBJECT_ENTRY) ? TRUE : FALSE; }
+
+ BOOL getAllowGroupObjectEntry() const
+ { return (mParcelFlags & PF_ALLOW_GROUP_OBJECT_ENTRY) ? TRUE : FALSE; }
+
+ BOOL getForSale() const
+ { return (mParcelFlags & PF_FOR_SALE) ? TRUE : FALSE; }
+ BOOL getSoundLocal() const
+ { return (mParcelFlags & PF_SOUND_LOCAL) ? TRUE : FALSE; }
+ BOOL getAllowPublish() const
+ { return (mParcelFlags & PF_ALLOW_PUBLISH) ? TRUE : FALSE; }
+ BOOL getMaturePublish() const
+ { return (mParcelFlags & PF_MATURE_PUBLISH) ? TRUE : FALSE; }
+ BOOL getRestrictPushObject() const
+ { return (mParcelFlags & PF_RESTRICT_PUSHOBJECT) ? TRUE : FALSE; }
+ BOOL getRegionPushOverride() const
+ { return mRegionPushOverride; }
+ BOOL getRegionDenyAnonymousOverride() const
+ { return mRegionDenyAnonymousOverride; }
+ BOOL getRegionDenyIdentifiedOverride() const
+ { return mRegionDenyIdentifiedOverride; }
+ BOOL getRegionDenyTransactedOverride() const
+ { return mRegionDenyTransactedOverride; }
+
+ F32 getDrawDistance() const { return mDrawDistance; }
+ S32 getSalePrice() const { return mSalePrice; }
+ time_t getClaimDate() const { return mClaimDate; }
+ S32 getClaimPricePerMeter() const { return mClaimPricePerMeter; }
+ S32 getRentPricePerMeter() const { return mRentPricePerMeter; }
+
+ // Area is NOT automatically calculated. You must calculate it
+ // and store it with setArea.
+ S32 getArea() const { return mArea; }
+
+ // deprecated 12/11/2003
+ //F32 getDiscountRate() const { return mDiscountRate; }
+
+ S32 getClaimPrice() const { return mClaimPricePerMeter * mArea; }
+
+ // Can this agent create objects here?
+ BOOL allowModifyBy(const LLUUID &agent_id, const LLUUID &group_id) const;
+
+ // Can this agent change the shape of the land?
+ BOOL allowTerraformBy(const LLUUID &agent_id) const;
+
+ // Returns 0 if access is OK, otherwise a BA_ return code above.
+ S32 blockAccess(const LLUUID& agent_id, const LLUUID& group_id, const BOOL is_agent_identified, const BOOL is_agent_transacted) const;
+
+ // Only checks if the agent is explicitly banned from this parcel
+ BOOL isAgentBanned(const LLUUID& agent_id) const;
+
+ static bool isAgentBlockedFromParcel(LLParcel* parcelp,
+ const LLUUID& agent_id,
+ const std::vector<LLUUID>& group_ids,
+ const BOOL is_agent_identified,
+ const BOOL is_agent_transacted);
+
+ bool operator==(const LLParcel &rhs) const;
+
+ // Calculate rent - area * rent * discount rate
+ S32 getTotalRent() const;
+ F32 getAdjustedRentPerMeter() const;
+
+ const LLVector3& getAABBMin() const { return mAABBMin; }
+ const LLVector3& getAABBMax() const { return mAABBMax; }
+ LLVector3 getCenterpoint() const;
+
+ // simwide
+ S32 getSimWideMaxPrimCapacity() const { return mSimWideMaxPrimCapacity; }
+ S32 getSimWidePrimCount() const { return mSimWidePrimCount; }
+
+ // this parcel only (not simwide)
+ S32 getMaxPrimCapacity() const { return mMaxPrimCapacity; }
+ S32 getPrimCount() const { return mOwnerPrimCount + mGroupPrimCount + mOtherPrimCount + mSelectedPrimCount; }
+ S32 getOwnerPrimCount() const { return mOwnerPrimCount; }
+ S32 getGroupPrimCount() const { return mGroupPrimCount; }
+ S32 getOtherPrimCount() const { return mOtherPrimCount; }
+ S32 getSelectedPrimCount() const{ return mSelectedPrimCount; }
+ S32 getTempPrimCount() const { return mTempPrimCount; }
+ F32 getParcelPrimBonus() const { return mParcelPrimBonus; }
+
+ S32 getCleanOtherTime() const { return mCleanOtherTime; }
+
+ void setMaxPrimCapacity(S32 max) { mMaxPrimCapacity = max; }
+ // simwide
+ void setSimWideMaxPrimCapacity(S32 current) { mSimWideMaxPrimCapacity = current; }
+ void setSimWidePrimCount(S32 current) { mSimWidePrimCount = current; }
+
+ // this parcel only (not simwide)
+ void setOwnerPrimCount(S32 current) { mOwnerPrimCount = current; }
+ void setGroupPrimCount(S32 current) { mGroupPrimCount = current; }
+ void setOtherPrimCount(S32 current) { mOtherPrimCount = current; }
+ void setSelectedPrimCount(S32 current) { mSelectedPrimCount = current; }
+ void setTempPrimCount(S32 current) { mTempPrimCount = current; }
+ void setParcelPrimBonus(F32 bonus) { mParcelPrimBonus = bonus; }
+
+ void setCleanOtherTime(S32 time) { mCleanOtherTime = time; }
+ void setRegionPushOverride(BOOL override) {mRegionPushOverride = override; }
+ void setRegionDenyAnonymousOverride(BOOL override) { mRegionDenyAnonymousOverride = override; }
+ void setRegionDenyIdentifiedOverride(BOOL override) { mRegionDenyIdentifiedOverride = override; }
+ void setRegionDenyTransactedOverride(BOOL override) { mRegionDenyTransactedOverride = override; }
+
+ // Accessors for parcel sellWithObjects
+ void setPreviousOwnerID(LLUUID prev_owner) { mPreviousOwnerID = prev_owner; }
+ void setPreviouslyGroupOwned(BOOL b) { mPreviouslyGroupOwned = b; }
+ void setSellWithObjects(BOOL b) { setParcelFlag(PF_SELL_PARCEL_OBJECTS, b); }
+
+ LLUUID getPreviousOwnerID() const { return mPreviousOwnerID; }
+ BOOL getPreviouslyGroupOwned() const { return mPreviouslyGroupOwned; }
+ BOOL getSellWithObjects() const { return (mParcelFlags & PF_SELL_PARCEL_OBJECTS) ? TRUE : FALSE; }
+
+protected:
+ LLUUID mID;
+ LLUUID mOwnerID;
+ LLUUID mGroupID;
+ BOOL mGroupOwned; // TRUE if mOwnerID is a group_id
+ LLUUID mPreviousOwnerID;
+ BOOL mPreviouslyGroupOwned;
+
+ EOwnershipStatus mStatus;
+ ECategory mCategory;
+ LLUUID mAuthBuyerID;
+ LLUUID mSnapshotID;
+ LLVector3 mUserLocation;
+ LLVector3 mUserLookAt;
+ ELandingType mLandingType;
+ LLTimer mSaleTimerExpires;
+ S32 mGraceExtension;
+ BOOL mRecordTransaction;
+
+
+ // This value is non-zero if there is an auction associated with
+ // the parcel.
+ U32 mAuctionID;
+
+ // This value is TRUE if the land is reserved for a newbie.
+ BOOL mIsReservedForNewbie;
+
+ // value used to temporarily lock attempts to purchase the parcel.
+ bool mInEscrow;
+
+ time_t mClaimDate; // UTC Unix-format time
+ S32 mClaimPricePerMeter; // meter squared
+ S32 mRentPricePerMeter; // meter squared
+ S32 mArea; // meter squared
+ F32 mDiscountRate; // 0.0-1.0
+ F32 mDrawDistance;
+ U32 mParcelFlags;
+ S32 mSalePrice; // linden dollars
+ std::string mName;
+ std::string mDesc;
+ std::string mMusicURL;
+ std::string mMediaURL;
+ U8 mMediaAutoScale;
+ LLUUID mMediaID;
+ S32 mPassPrice;
+ F32 mPassHours;
+ LLVector3 mAABBMin;
+ LLVector3 mAABBMax;
+ S32 mMaxPrimCapacity;
+ S32 mSimWidePrimCount;
+ S32 mSimWideMaxPrimCapacity;
+ //S32 mSimWidePrimCorrection;
+ S32 mOwnerPrimCount;
+ S32 mGroupPrimCount;
+ S32 mOtherPrimCount;
+ S32 mSelectedPrimCount;
+ S32 mTempPrimCount;
+ F32 mParcelPrimBonus;
+ S32 mCleanOtherTime;
+ BOOL mRegionPushOverride;
+ BOOL mRegionDenyAnonymousOverride;
+ BOOL mRegionDenyIdentifiedOverride;
+ BOOL mRegionDenyTransactedOverride;
+
+
+public:
+ // HACK, make private
+ S32 mLocalID;
+ LLUUID mBanListTransactionID;
+ LLUUID mAccessListTransactionID;
+ std::map<LLUUID,LLAccessEntry> mAccessList;
+ std::map<LLUUID,LLAccessEntry> mBanList;
+ std::map<LLUUID,LLAccessEntry> mTempBanList;
+ std::map<LLUUID,LLAccessEntry> mTempAccessList;
+
+ //LLDynamicArray<LLAccessEntry> mRenterList;
+};
+
+
+#endif
diff --git a/indra/llinventory/llparcelflags.h b/indra/llinventory/llparcelflags.h
new file mode 100644
index 0000000000..43571abe77
--- /dev/null
+++ b/indra/llinventory/llparcelflags.h
@@ -0,0 +1,105 @@
+/**
+ * @file llparcelflags.h
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPARCEL_FLAGS_H
+#define LL_LLPARCEL_FLAGS_H
+
+//---------------------------------------------------------------------------
+// Parcel Flags (PF) constants
+//---------------------------------------------------------------------------
+const U32 PF_ALLOW_FLY = 1 << 0;// Can start flying
+const U32 PF_ALLOW_OTHER_SCRIPTS= 1 << 1;// Scripts by others can run.
+const U32 PF_FOR_SALE = 1 << 2;// Can buy this land
+const U32 PF_FOR_SALE_OBJECTS = 1 << 7;// Can buy all objects on this land
+const U32 PF_ALLOW_LANDMARK = 1 << 3;
+const U32 PF_ALLOW_TERRAFORM = 1 << 4;
+const U32 PF_ALLOW_DAMAGE = 1 << 5;
+const U32 PF_CREATE_OBJECTS = 1 << 6;
+// 7 is moved above
+const U32 PF_USE_ACCESS_GROUP = 1 << 8;
+const U32 PF_USE_ACCESS_LIST = 1 << 9;
+const U32 PF_USE_BAN_LIST = 1 << 10;
+const U32 PF_USE_PASS_LIST = 1 << 11;
+const U32 PF_SHOW_DIRECTORY = 1 << 12;
+const U32 PF_ALLOW_DEED_TO_GROUP = 1 << 13;
+const U32 PF_CONTRIBUTE_WITH_DEED = 1 << 14;
+const U32 PF_SOUND_LOCAL = 1 << 15; // Hear sounds in this parcel only
+const U32 PF_SELL_PARCEL_OBJECTS = 1 << 16; // Objects on land are included as part of the land when the land is sold
+const U32 PF_ALLOW_PUBLISH = 1 << 17; // Allow publishing of parcel information on the web
+const U32 PF_MATURE_PUBLISH = 1 << 18; // The information on this parcel is mature
+const U32 PF_URL_WEB_PAGE = 1 << 19; // The "media URL" is an HTML page
+const U32 PF_URL_RAW_HTML = 1 << 20; // The "media URL" is a raw HTML string like <H1>Foo</H1>
+const U32 PF_RESTRICT_PUSHOBJECT = 1 << 21; // Restrict push object to either on agent or on scripts owned by parcel owner
+const U32 PF_DENY_ANONYMOUS = 1 << 22; // Deny all non identified/transacted accounts
+const U32 PF_DENY_IDENTIFIED = 1 << 23; // Deny identified accounts
+const U32 PF_DENY_TRANSACTED = 1 << 24; // Deny identified accounts
+const U32 PF_ALLOW_GROUP_SCRIPTS = 1 << 25; // Allow scripts owned by group
+const U32 PF_CREATE_GROUP_OBJECTS = 1 << 26; // Allow object creation by group members or objects
+const U32 PF_ALLOW_ALL_OBJECT_ENTRY = 1 << 27; // Allow all objects to enter a parcel
+const U32 PF_ALLOW_GROUP_OBJECT_ENTRY = 1 << 28; // Only allow group (and owner) objects to enter the parcel
+
+
+const U32 PF_RESERVED = 1 << 31;
+
+// If any of these are true the parcel is restricting access in some maner.
+const U32 PF_USE_RESTRICTED_ACCESS = PF_USE_ACCESS_GROUP
+ | PF_USE_ACCESS_LIST
+ | PF_USE_BAN_LIST
+ | PF_USE_PASS_LIST
+ | PF_DENY_ANONYMOUS
+ | PF_DENY_IDENTIFIED
+ | PF_DENY_TRANSACTED;
+const U32 PF_NONE = 0x00000000;
+const U32 PF_ALL = 0x7FFFFFFF;
+const U32 PF_DEFAULT = PF_ALLOW_FLY
+ | PF_ALLOW_OTHER_SCRIPTS
+ | PF_ALLOW_GROUP_SCRIPTS
+ | PF_ALLOW_LANDMARK
+ | PF_CREATE_OBJECTS
+ | PF_CREATE_GROUP_OBJECTS
+ | PF_USE_BAN_LIST
+ | PF_ALLOW_ALL_OBJECT_ENTRY
+ | PF_ALLOW_GROUP_OBJECT_ENTRY;
+
+// Access list flags
+const U32 AL_ACCESS = (1 << 0);
+const U32 AL_BAN = (1 << 1);
+//const U32 AL_RENTER = (1 << 2);
+
+// Block access return values. BA_ALLOWED is the only success case
+// since some code in the simulator relies on that assumption. All
+// other BA_ values should be reasons why you are not allowed.
+const S32 BA_ALLOWED = 0;
+const S32 BA_NOT_IN_GROUP = 1;
+const S32 BA_NOT_ON_LIST = 2;
+const S32 BA_BANNED = 3;
+const S32 BA_NO_ACCESS_LEVEL = 4;
+
+// ParcelRelease flags
+const U32 PR_NONE = 0x0;
+const U32 PR_GOD_FORCE = (1 << 0);
+
+enum EObjectCategory
+{
+ OC_INVALID = -1,
+ OC_NONE = 0,
+ OC_TOTAL = 0, // yes zero, like OC_NONE
+ OC_OWNER,
+ OC_GROUP,
+ OC_OTHER,
+ OC_SELECTED,
+ OC_TEMP,
+ OC_COUNT
+};
+
+const S32 PARCEL_DETAILS_NAME = 0;
+const S32 PARCEL_DETAILS_DESC = 1;
+const S32 PARCEL_DETAILS_OWNER = 2;
+const S32 PARCEL_DETAILS_GROUP = 3;
+const S32 PARCEL_DETAILS_AREA = 4;
+
+#endif
diff --git a/indra/llinventory/llpermissions.cpp b/indra/llinventory/llpermissions.cpp
new file mode 100644
index 0000000000..2063ac33d6
--- /dev/null
+++ b/indra/llinventory/llpermissions.cpp
@@ -0,0 +1,1171 @@
+/**
+ * @file llpermissions.cpp
+ * @author Phoenix
+ * @brief Permissions for objects and inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpermissions.h"
+
+// library includes
+#include "message.h"
+#include "metapropertyt.h"
+
+///----------------------------------------------------------------------------
+/// Class LLPermissions
+///----------------------------------------------------------------------------
+
+const LLPermissions LLPermissions::DEFAULT;
+
+// No creator = created by system
+LLPermissions::LLPermissions()
+{
+ init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
+}
+
+
+// Default to created by system
+void LLPermissions::init(const LLUUID& creator, const LLUUID& owner, const LLUUID& last_owner, const LLUUID& group)
+{
+ mCreator = creator;
+ mOwner = owner;
+ mLastOwner = last_owner;
+ mGroup = group;
+
+ mMaskBase = PERM_ALL;
+ mMaskOwner = PERM_ALL;
+ mMaskEveryone = PERM_ALL;
+ mMaskGroup = PERM_ALL;
+ mMaskNextOwner = PERM_ALL;
+ fixOwnership();
+}
+
+
+void LLPermissions::initMasks(PermissionMask base, PermissionMask owner,
+ PermissionMask everyone, PermissionMask group,
+ PermissionMask next)
+{
+ mMaskBase = base;
+ mMaskOwner = owner;
+ mMaskEveryone = everyone;
+ mMaskGroup = group;
+ mMaskNextOwner = next;
+ fixFairUse();
+ fix();
+}
+
+BOOL LLPermissions::getOwnership(LLUUID& owner_id, BOOL& is_group_owned) const
+{
+ if(mOwner.notNull())
+ {
+ owner_id = mOwner;
+ is_group_owned = FALSE;
+ return TRUE;
+ }
+ else if(mIsGroupOwned)
+ {
+ owner_id = mGroup;
+ is_group_owned = TRUE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLUUID LLPermissions::getSafeOwner() const
+{
+ if(mOwner.notNull())
+ {
+ return mOwner;
+ }
+ else if(mIsGroupOwned)
+ {
+ return mGroup;
+ }
+ else
+ {
+ llwarns << "LLPermissions::getSafeOwner() called with no valid owner!" << llendl;
+ LLUUID unused_uuid;
+ unused_uuid.generate();
+
+ return unused_uuid;
+ }
+}
+
+U32 LLPermissions::getCRC32() const
+{
+ U32 rv = mCreator.getCRC32();
+ rv += mOwner.getCRC32();
+ rv += mLastOwner.getCRC32();
+ rv += mGroup.getCRC32();
+ rv += mMaskBase + mMaskOwner + mMaskEveryone + mMaskGroup;
+ return rv;
+}
+
+void LLPermissions::set(const LLPermissions& from)
+{
+ mCreator = from.mCreator;
+ mOwner = from.mOwner;
+ mLastOwner = from.mLastOwner;
+ mGroup = from.mGroup;
+
+ mMaskBase = from.mMaskBase;
+ mMaskOwner = from.mMaskOwner;
+ mMaskEveryone = from.mMaskEveryone;
+ mMaskGroup = from.mMaskGroup;
+ mMaskNextOwner = from.mMaskNextOwner;
+ mIsGroupOwned = from.mIsGroupOwned;
+}
+
+// Fix hierarchy of permissions.
+void LLPermissions::fix()
+{
+ mMaskOwner &= mMaskBase;
+ mMaskGroup &= mMaskOwner;
+ // next owner uses base, since you may want to sell locked objects.
+ mMaskNextOwner &= mMaskBase;
+ mMaskEveryone &= mMaskOwner;
+ mMaskEveryone &= ~PERM_MODIFY;
+ if(!(mMaskBase & PERM_TRANSFER) && !mIsGroupOwned)
+ {
+ mMaskGroup &= ~PERM_COPY;
+ mMaskEveryone &= ~PERM_COPY;
+ // Do not set mask next owner to too restrictive because if we
+ // rez an object, it may require an ownership transfer during
+ // rez, which will note the overly restrictive perms, and then
+ // fix them to allow fair use, which may be different than the
+ // original intention.
+ }
+}
+
+// Correct for fair use - you can never take away the right to move
+// stuff you own, and you can never take away the right to transfer
+// something you cannot otherwise copy.
+void LLPermissions::fixFairUse()
+{
+ mMaskBase |= PERM_MOVE;
+ if(!(mMaskBase & PERM_COPY))
+ {
+ mMaskBase |= PERM_TRANSFER;
+ }
+ // (mask next owner == PERM_NONE) iff mask base is no transfer
+ if(mMaskNextOwner != PERM_NONE)
+ {
+ mMaskNextOwner |= PERM_MOVE;
+ }
+}
+
+void LLPermissions::fixOwnership()
+{
+ if(mOwner.isNull() && mGroup.notNull())
+ {
+ mIsGroupOwned = true;
+ }
+ else
+ {
+ mIsGroupOwned = false;
+ }
+}
+
+// Allow accumulation of permissions. Results in the tightest
+// permissions possible. In the case of clashing UUIDs, it sets the ID
+// to LLUUID::null.
+void LLPermissions::accumulate(const LLPermissions& perm)
+{
+ if(perm.mCreator != mCreator)
+ {
+ mCreator = LLUUID::null;
+ }
+ if(perm.mOwner != mOwner)
+ {
+ mOwner = LLUUID::null;
+ }
+ if(perm.mLastOwner != mLastOwner)
+ {
+ mLastOwner = LLUUID::null;
+ }
+ if(perm.mGroup != mGroup)
+ {
+ mGroup = LLUUID::null;
+ }
+
+ mMaskBase &= perm.mMaskBase;
+ mMaskOwner &= perm.mMaskOwner;
+ mMaskGroup &= perm.mMaskGroup;
+ mMaskEveryone &= perm.mMaskEveryone;
+ mMaskNextOwner &= perm.mMaskNextOwner;
+ fix();
+}
+
+// saves last owner, sets current owner, and sets the group. note
+// that this function has to more cleverly apply the fair use
+// permissions.
+BOOL LLPermissions::setOwnerAndGroup(
+ const LLUUID& agent,
+ const LLUUID& owner,
+ const LLUUID& group,
+ bool is_atomic)
+{
+ BOOL allowed = FALSE;
+
+ if( agent.isNull() || mOwner.isNull()
+ || ((agent == mOwner) && ((owner == mOwner) || (mMaskOwner & PERM_TRANSFER)) ) )
+ {
+ // ...system can alway set owner
+ // ...public objects can be claimed by anyone
+ // ...otherwise, agent must own it and have transfer ability
+ allowed = TRUE;
+ }
+
+ if (allowed)
+ {
+ if(mLastOwner.isNull() || (!mOwner.isNull() && (owner != mLastOwner)))
+ {
+ mLastOwner = mOwner;
+ }
+ if((mOwner != owner)
+ || (mOwner.isNull() && owner.isNull() && (mGroup != group)))
+ {
+ mMaskBase = mMaskNextOwner;
+ mOwner = owner;
+ // this is a selective use of fair use for atomic
+ // permissions.
+ if(is_atomic && !(mMaskBase & PERM_COPY))
+ {
+ mMaskBase |= PERM_TRANSFER;
+ }
+ }
+ mGroup = group;
+ fixOwnership();
+ // if it's not atomic and we fix fair use, it blows away
+ //objects as inventory items which have different permissions
+ //than it's contents. :(
+ // fixFairUse();
+ mMaskBase |= PERM_MOVE;
+ if(mMaskNextOwner != PERM_NONE) mMaskNextOwner |= PERM_MOVE;
+ fix();
+ }
+
+ return allowed;
+}
+
+BOOL LLPermissions::deedToGroup(const LLUUID& agent, const LLUUID& group)
+{
+ if(group.notNull() && (agent.isNull() || ((group == mGroup)
+ && (mMaskOwner & PERM_TRANSFER)
+ && (mMaskGroup & PERM_MOVE))))
+ {
+ if(mOwner.notNull())
+ {
+ mLastOwner = mOwner;
+ mOwner.setNull();
+ }
+ mMaskBase = mMaskNextOwner;
+ mGroup = group;
+ mIsGroupOwned = true;
+ fixFairUse();
+ fix();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLPermissions::setBaseBits(const LLUUID& agent, BOOL set, PermissionMask bits)
+{
+ BOOL ownership = FALSE;
+ if(agent.isNull())
+ {
+ // only the system is always allowed to change base bits
+ ownership = TRUE;
+ }
+
+ if (ownership)
+ {
+ if (set)
+ {
+ mMaskBase |= bits; // turn on bits
+ }
+ else
+ {
+ mMaskBase &= ~bits; // turn off bits
+ }
+ fix();
+ }
+
+ return ownership;
+}
+
+
+// Note: If you attempt to set bits that the base bits doesn't allow,
+// the function will succeed, but those bits will not be set.
+BOOL LLPermissions::setOwnerBits(const LLUUID& agent, BOOL set, PermissionMask bits)
+{
+ BOOL ownership = FALSE;
+
+ if(agent.isNull())
+ {
+ // ...system always allowed to change things
+ ownership = TRUE;
+ }
+ else if (agent == mOwner)
+ {
+ // ...owner bits can only be set by owner
+ ownership = TRUE;
+ }
+
+ // If we have correct ownership and
+ if (ownership)
+ {
+ if (set)
+ {
+ mMaskOwner |= bits; // turn on bits
+ }
+ else
+ {
+ mMaskOwner &= ~bits; // turn off bits
+ }
+ fix();
+ }
+
+ return (ownership);
+}
+
+BOOL LLPermissions::setGroupBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
+{
+ BOOL ownership = FALSE;
+ if((agent.isNull()) || (agent == mOwner)
+ || ((group == mGroup) && (!mGroup.isNull())))
+ {
+ // The group bits can be set by the system, the owner, or a
+ // group member.
+ ownership = TRUE;
+ }
+
+ if (ownership)
+ {
+ if (set)
+ {
+ mMaskGroup |= bits;
+ }
+ else
+ {
+ mMaskGroup &= ~bits;
+ }
+ fix();
+ }
+ return ownership;
+}
+
+
+// Note: If you attempt to set bits that the creator or owner doesn't allow,
+// the function will succeed, but those bits will not be set.
+BOOL LLPermissions::setEveryoneBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
+{
+ BOOL ownership = FALSE;
+ if((agent.isNull()) || (agent == mOwner)
+ || ((group == mGroup) && (!mGroup.isNull())))
+ {
+ // The everyone bits can be set by the system, the owner, or a
+ // group member.
+ ownership = TRUE;
+ }
+ if (ownership)
+ {
+ if (set)
+ {
+ mMaskEveryone |= bits;
+ }
+ else
+ {
+ mMaskEveryone &= ~bits;
+ }
+
+ // Fix hierarchy of permissions
+ fix();
+ }
+ return ownership;
+}
+
+// Note: If you attempt to set bits that the creator or owner doesn't allow,
+// the function will succeed, but those bits will not be set.
+BOOL LLPermissions::setNextOwnerBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits)
+{
+ BOOL ownership = FALSE;
+ if((agent.isNull()) || (agent == mOwner)
+ || ((group == mGroup) && (!mGroup.isNull())))
+ {
+ // The next owner bits can be set by the system, the owner, or
+ // a group member.
+ ownership = TRUE;
+ }
+ if (ownership)
+ {
+ if (set)
+ {
+ mMaskNextOwner |= bits;
+ }
+ else
+ {
+ mMaskNextOwner &= ~bits;
+ }
+
+ // Fix-up permissions
+ if(!(mMaskNextOwner & PERM_COPY))
+ {
+ mMaskNextOwner |= PERM_TRANSFER;
+ }
+ fix();
+ }
+ return ownership;
+}
+
+BOOL LLPermissions::allowOperationBy(PermissionBit op, const LLUUID& requester, const LLUUID& group) const
+{
+ if(requester.isNull())
+ {
+ // ...system making request
+ // ...not owned
+ return TRUE;
+ }
+ else if (mIsGroupOwned && (mGroup == requester))
+ {
+ // group checking ownership permissions
+ return (mMaskOwner & op);
+ }
+ else if (!mIsGroupOwned && (mOwner == requester))
+ {
+ // ...owner making request
+ return (mMaskOwner & op);
+ }
+ else if(mGroup.notNull() && (mGroup == group))
+ {
+ // group member making request
+ return ((mMaskGroup & op) || (mMaskEveryone & op));
+ }
+ return (mMaskEveryone & op);
+}
+
+//
+// Messaging support
+//
+void LLPermissions::packMessage(LLMessageSystem* msg) const
+{
+ msg->addUUIDFast(_PREHASH_CreatorID, mCreator);
+ msg->addUUIDFast(_PREHASH_OwnerID, mOwner);
+ msg->addUUIDFast(_PREHASH_GroupID, mGroup);
+
+ msg->addU32Fast(_PREHASH_BaseMask, mMaskBase );
+ msg->addU32Fast(_PREHASH_OwnerMask, mMaskOwner );
+ msg->addU32Fast(_PREHASH_GroupMask, mMaskGroup );
+ msg->addU32Fast(_PREHASH_EveryoneMask, mMaskEveryone );
+ msg->addU32Fast(_PREHASH_NextOwnerMask, mMaskNextOwner );
+ msg->addBOOLFast(_PREHASH_GroupOwned, (BOOL)mIsGroupOwned);
+}
+
+
+void LLPermissions::unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num)
+{
+ msg->getUUIDFast(block, _PREHASH_CreatorID, mCreator, block_num);
+ msg->getUUIDFast(block, _PREHASH_OwnerID, mOwner, block_num);
+ msg->getUUIDFast(block, _PREHASH_GroupID, mGroup, block_num);
+
+ msg->getU32Fast(block, _PREHASH_BaseMask, mMaskBase, block_num );
+ msg->getU32Fast(block, _PREHASH_OwnerMask, mMaskOwner, block_num );
+ msg->getU32Fast(block, _PREHASH_GroupMask, mMaskGroup, block_num );
+ msg->getU32Fast(block, _PREHASH_EveryoneMask, mMaskEveryone, block_num );
+ msg->getU32Fast(block, _PREHASH_NextOwnerMask, mMaskNextOwner, block_num );
+ BOOL tmp;
+ msg->getBOOLFast(block, _PREHASH_GroupOwned, tmp, block_num);
+ mIsGroupOwned = (bool)tmp;
+}
+
+
+//
+// File support
+//
+
+BOOL LLPermissions::importFile(FILE *fp)
+{
+ init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
+ const S32 BUFSIZE = 16384;
+
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ char uuid_str[256];
+ U32 mask;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("creator_mask", keyword))
+ {
+ // legacy support for "creator" masks
+ sscanf(valuestr, "%x", &mask);
+ mMaskBase = mask;
+ fixFairUse();
+ }
+ else if (!strcmp("base_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskBase = mask;
+ //fixFairUse();
+ }
+ else if (!strcmp("owner_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskOwner = mask;
+ }
+ else if (!strcmp("group_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskGroup = mask;
+ }
+ else if (!strcmp("everyone_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskEveryone = mask;
+ }
+ else if (!strcmp("next_owner_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskNextOwner = mask;
+ }
+ else if (!strcmp("creator_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mCreator.set(uuid_str);
+ }
+ else if (!strcmp("owner_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mOwner.set(uuid_str);
+ }
+ else if (!strcmp("last_owner_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mLastOwner.set(uuid_str);
+ }
+ else if (!strcmp("group_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mGroup.set(uuid_str);
+ }
+ else if (!strcmp("group_owned", keyword))
+ {
+ sscanf(valuestr, "%d", &mask);
+ if(mask) mIsGroupOwned = true;
+ else mIsGroupOwned = false;
+ }
+ else
+ {
+ llinfos << "unknown keyword " << keyword << " in permissions import" << llendl;
+ }
+ }
+ fix();
+ return TRUE;
+}
+
+
+BOOL LLPermissions::exportFile(FILE *fp) const
+{
+ char uuid_str[256];
+
+ fprintf(fp, "\tpermissions 0\n");
+ fprintf(fp, "\t{\n");
+
+ fprintf(fp, "\t\tbase_mask\t%08x\n", mMaskBase);
+ fprintf(fp, "\t\towner_mask\t%08x\n", mMaskOwner);
+ fprintf(fp, "\t\tgroup_mask\t%08x\n", mMaskGroup);
+ fprintf(fp, "\t\teveryone_mask\t%08x\n", mMaskEveryone);
+ fprintf(fp, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner);
+
+ mCreator.toString(uuid_str);
+ fprintf(fp, "\t\tcreator_id\t%s\n", uuid_str);
+
+ mOwner.toString(uuid_str);
+ fprintf(fp, "\t\towner_id\t%s\n", uuid_str);
+
+ mLastOwner.toString(uuid_str);
+ fprintf(fp, "\t\tlast_owner_id\t%s\n", uuid_str);
+
+ mGroup.toString(uuid_str);
+ fprintf(fp, "\t\tgroup_id\t%s\n", uuid_str);
+
+ if(mIsGroupOwned)
+ {
+ fprintf(fp, "\t\tgroup_owned\t1\n");
+ }
+ fprintf(fp,"\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLPermissions::importLegacyStream(std::istream& input_stream)
+{
+ init(LLUUID::null, LLUUID::null, LLUUID::null, LLUUID::null);
+ const S32 BUFSIZE = 16384;
+
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ char uuid_str[256];
+ U32 mask;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("creator_mask", keyword))
+ {
+ // legacy support for "creator" masks
+ sscanf(valuestr, "%x", &mask);
+ mMaskBase = mask;
+ fixFairUse();
+ }
+ else if (!strcmp("base_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskBase = mask;
+ //fixFairUse();
+ }
+ else if (!strcmp("owner_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskOwner = mask;
+ }
+ else if (!strcmp("group_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskGroup = mask;
+ }
+ else if (!strcmp("everyone_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskEveryone = mask;
+ }
+ else if (!strcmp("next_owner_mask", keyword))
+ {
+ sscanf(valuestr, "%x", &mask);
+ mMaskNextOwner = mask;
+ }
+ else if (!strcmp("creator_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mCreator.set(uuid_str);
+ }
+ else if (!strcmp("owner_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mOwner.set(uuid_str);
+ }
+ else if (!strcmp("last_owner_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mLastOwner.set(uuid_str);
+ }
+ else if (!strcmp("group_id", keyword))
+ {
+ sscanf(valuestr, "%s", uuid_str);
+ mGroup.set(uuid_str);
+ }
+ else if (!strcmp("group_owned", keyword))
+ {
+ sscanf(valuestr, "%d", &mask);
+ if(mask) mIsGroupOwned = true;
+ else mIsGroupOwned = false;
+ }
+ else
+ {
+ llinfos << "unknown keyword " << keyword << " in permissions import" << llendl;
+ }
+ }
+ fix();
+ return TRUE;
+}
+
+
+BOOL LLPermissions::exportLegacyStream(std::ostream& output_stream) const
+{
+ char uuid_str[256];
+
+ output_stream << "\tpermissions 0\n";
+ output_stream << "\t{\n";
+
+ char buffer[256];
+ sprintf(buffer, "\t\tbase_mask\t%08x\n", mMaskBase);
+ output_stream << buffer;
+ sprintf(buffer, "\t\towner_mask\t%08x\n", mMaskOwner);
+ output_stream << buffer;
+ sprintf(buffer, "\t\tgroup_mask\t%08x\n", mMaskGroup);
+ output_stream << buffer;
+ sprintf(buffer, "\t\teveryone_mask\t%08x\n", mMaskEveryone);
+ output_stream << buffer;
+ sprintf(buffer, "\t\tnext_owner_mask\t%08x\n", mMaskNextOwner);
+ output_stream << buffer;
+
+ mCreator.toString(uuid_str);
+ output_stream << "\t\tcreator_id\t" << uuid_str << "\n";
+
+ mOwner.toString(uuid_str);
+ output_stream << "\t\towner_id\t" << uuid_str << "\n";
+
+ mLastOwner.toString(uuid_str);
+ output_stream << "\t\tlast_owner_id\t" << uuid_str << "\n";
+
+ mGroup.toString(uuid_str);
+ output_stream << "\t\tgroup_id\t" << uuid_str << "\n";
+
+ if(mIsGroupOwned)
+ {
+ output_stream << "\t\tgroup_owned\t1\n";
+ }
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+
+LLXMLNode *LLPermissions::exportFileXML() const
+{
+ LLXMLNode *ret = new LLXMLNode("permissions", FALSE);
+
+ ret->createChild("group_owned", TRUE)->setBoolValue(1, (const BOOL*)&mIsGroupOwned);
+
+ ret->createChild("base_mask", FALSE)->setByteValue(4, (U8*)&mMaskBase, LLXMLNode::ENCODING_HEX);
+ ret->createChild("owner_mask", FALSE)->setByteValue(4, (U8*)&mMaskOwner, LLXMLNode::ENCODING_HEX);
+ ret->createChild("group_mask", FALSE)->setByteValue(4, (U8*)&mMaskGroup, LLXMLNode::ENCODING_HEX);
+ ret->createChild("everyone_mask", FALSE)->setByteValue(4, (U8*)&mMaskEveryone, LLXMLNode::ENCODING_HEX);
+ ret->createChild("next_owner_mask", FALSE)->setByteValue(4, (U8*)&mMaskNextOwner, LLXMLNode::ENCODING_HEX);
+
+ ret->createChild("creator_id", FALSE)->setUUIDValue(1, &mCreator);
+ ret->createChild("owner_id", FALSE)->setUUIDValue(1, &mOwner);
+ ret->createChild("last_owner_id", FALSE)->setUUIDValue(1, &mLastOwner);
+ ret->createChild("group_id", FALSE)->setUUIDValue(1, &mGroup);
+
+ return ret;
+}
+
+bool LLPermissions::importXML(LLXMLNode* node)
+{
+ bool success = false;
+ if (node)
+ {
+ success = true;
+ LLXMLNodePtr sub_node;
+ if (node->getChild("base_mask", sub_node))
+ success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskBase));
+ if (node->getChild("owner_mask", sub_node))
+ success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskOwner));
+ if (node->getChild("group_mask", sub_node))
+ success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskGroup));
+ if (node->getChild("everyone_mask", sub_node))
+ success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskEveryone));
+ if (node->getChild("next_owner_mask", sub_node))
+ success = success && (4 == sub_node->getByteValue(4, (U8*)&mMaskNextOwner));
+
+ if (node->getChild("creator_id", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mCreator));
+ if (node->getChild("owner_id", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mOwner));
+ if (node->getChild("last_owner_id", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mLastOwner));
+ if (node->getChild("group_id", sub_node))
+ success = success && (1 == sub_node->getUUIDValue(1, &mGroup));
+ if (node->getChild("group_owned", sub_node))
+ success = success && (1 == sub_node->getBoolValue(1, (BOOL*)&mIsGroupOwned));
+ if (!success)
+ {
+ lldebugs << "LLPermissions::importXML() failed for node named '"
+ << node->getName() << "'" << llendl;
+ }
+ }
+ return success;
+}
+
+bool LLPermissions::operator==(const LLPermissions &rhs) const
+{
+ return
+ (mCreator == rhs.mCreator) &&
+ (mOwner == rhs.mOwner) &&
+ (mLastOwner == rhs.mLastOwner ) &&
+ (mGroup == rhs.mGroup ) &&
+ (mMaskBase == rhs.mMaskBase ) &&
+ (mMaskOwner == rhs.mMaskOwner ) &&
+ (mMaskGroup == rhs.mMaskGroup ) &&
+ (mMaskEveryone == rhs.mMaskEveryone ) &&
+ (mMaskNextOwner == rhs.mMaskNextOwner ) &&
+ (mIsGroupOwned == rhs.mIsGroupOwned);
+}
+
+
+bool LLPermissions::operator!=(const LLPermissions &rhs) const
+{
+ return
+ (mCreator != rhs.mCreator) ||
+ (mOwner != rhs.mOwner) ||
+ (mLastOwner != rhs.mLastOwner ) ||
+ (mGroup != rhs.mGroup ) ||
+ (mMaskBase != rhs.mMaskBase ) ||
+ (mMaskOwner != rhs.mMaskOwner ) ||
+ (mMaskGroup != rhs.mMaskGroup ) ||
+ (mMaskEveryone != rhs.mMaskEveryone ) ||
+ (mMaskNextOwner != rhs.mMaskNextOwner) ||
+ (mIsGroupOwned != rhs.mIsGroupOwned);
+}
+
+std::ostream& operator<<(std::ostream &s, const LLPermissions &perm)
+{
+ s << "{Creator=" << perm.getCreator();
+ s << ", Owner=" << perm.getOwner();
+ s << ", Group=" << perm.getGroup();
+ s << std::hex << ", BaseMask=0x" << perm.getMaskBase();
+ s << ", OwnerMask=0x" << perm.getMaskOwner();
+ s << ", EveryoneMask=0x" << perm.getMaskEveryone();
+ s << ", GroupMask=0x" << perm.getMaskGroup();
+ s << ", NextOwnerMask=0x" << perm.getMaskNextOwner() << std::dec;
+ s << "}";
+ return s;
+}
+
+template <>
+void LLMetaClassT<LLPermissions>::reflectProperties(LLMetaClass& meta_class)
+{
+ reflectProperty(meta_class, "mCreator", &LLPermissions::mCreator);
+ reflectProperty(meta_class, "mOwner", &LLPermissions::mOwner);
+}
+
+// virtual
+const LLMetaClass& LLPermissions::getMetaClass() const
+{
+ return LLMetaClassT<LLPermissions>::instance();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLAggregatePermissions
+///----------------------------------------------------------------------------
+
+const LLAggregatePermissions LLAggregatePermissions::empty;
+
+
+LLAggregatePermissions::LLAggregatePermissions()
+{
+ for(S32 i = 0; i < PI_COUNT; ++i)
+ {
+ mBits[i] = AP_EMPTY;
+ }
+}
+
+LLAggregatePermissions::EValue LLAggregatePermissions::getValue(PermissionBit bit) const
+{
+ EPermIndex idx = perm2PermIndex(bit);
+ EValue rv = AP_EMPTY;
+ if(idx != PI_END)
+ {
+ rv = (LLAggregatePermissions::EValue)(mBits[idx]);
+ }
+ return rv;
+}
+
+// returns the bits compressed into a single byte: 00TTMMCC
+// where TT = transfer, MM = modify, and CC = copy
+// LSB is to the right
+U8 LLAggregatePermissions::getU8() const
+{
+ U8 byte = mBits[PI_TRANSFER];
+ byte <<= 2;
+ byte |= mBits[PI_MODIFY];
+ byte <<= 2;
+ byte |= mBits[PI_COPY];
+ return byte;
+}
+
+BOOL LLAggregatePermissions::isEmpty() const
+{
+ for(S32 i = 0; i < PI_END; ++i)
+ {
+ if(mBits[i] != AP_EMPTY)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLAggregatePermissions::aggregate(PermissionMask mask)
+{
+ BOOL is_allowed = mask & PERM_COPY;
+ aggregateBit(PI_COPY, is_allowed);
+ is_allowed = mask & PERM_MODIFY;
+ aggregateBit(PI_MODIFY, is_allowed);
+ is_allowed = mask & PERM_TRANSFER;
+ aggregateBit(PI_TRANSFER, is_allowed);
+}
+
+void LLAggregatePermissions::aggregate(const LLAggregatePermissions& ag)
+{
+ for(S32 idx = PI_COPY; idx != PI_END; ++idx)
+ {
+ aggregateIndex((EPermIndex)idx, ag.mBits[idx]);
+ }
+}
+
+void LLAggregatePermissions::aggregateBit(EPermIndex idx, BOOL allowed)
+{
+ //if(AP_SOME == mBits[idx]) return; // P4 branch prediction optimization
+ switch(mBits[idx])
+ {
+ case AP_EMPTY:
+ mBits[idx] = allowed ? AP_ALL : AP_NONE;
+ break;
+ case AP_NONE:
+ mBits[idx] = allowed ? AP_SOME: AP_NONE;
+ break;
+ case AP_SOME:
+ // no-op
+ break;
+ case AP_ALL:
+ mBits[idx] = allowed ? AP_ALL : AP_SOME;
+ break;
+ default:
+ llwarns << "Bad aggregateBit " << (S32)idx << " "
+ << (allowed ? "true" : "false") << llendl;
+ break;
+ }
+}
+
+void LLAggregatePermissions::aggregateIndex(EPermIndex idx, U8 bits)
+{
+ switch(mBits[idx])
+ {
+ case AP_EMPTY:
+ mBits[idx] = bits;
+ break;
+ case AP_NONE:
+ switch(bits)
+ {
+ case AP_SOME:
+ case AP_ALL:
+ mBits[idx] = AP_SOME;
+ break;
+ case AP_EMPTY:
+ case AP_NONE:
+ default:
+ // no-op
+ break;
+ }
+ break;
+ case AP_SOME:
+ // no-op
+ break;
+ case AP_ALL:
+ switch(bits)
+ {
+ case AP_NONE:
+ case AP_SOME:
+ mBits[idx] = AP_SOME;
+ break;
+ case AP_EMPTY:
+ case AP_ALL:
+ default:
+ // no-op
+ break;
+ }
+ break;
+ default:
+ llwarns << "Bad aggregate index " << (S32)idx << " "
+ << (S32)bits << llendl;
+ break;
+ }
+}
+
+// static
+LLAggregatePermissions::EPermIndex LLAggregatePermissions::perm2PermIndex(PermissionBit bit)
+{
+ EPermIndex idx = PI_END; // past any good value.
+ switch(bit)
+ {
+ case PERM_COPY:
+ idx = PI_COPY;
+ break;
+ case PERM_MODIFY:
+ idx = PI_MODIFY;
+ break;
+ case PERM_TRANSFER:
+ idx = PI_TRANSFER;
+ break;
+ default:
+ break;
+ }
+ return idx;
+}
+
+
+void LLAggregatePermissions::packMessage(LLMessageSystem* msg, const char* field) const
+{
+ msg->addU8Fast(field, getU8());
+}
+
+void LLAggregatePermissions::unpackMessage(LLMessageSystem* msg, const char* block, const char* field, S32 block_num)
+{
+ const U8 TWO_BITS = 0x3; // binary 00000011
+ U8 bits = 0;
+ msg->getU8Fast(block, field, bits, block_num);
+ mBits[PI_COPY] = bits & TWO_BITS;
+ bits >>= 2;
+ mBits[PI_MODIFY] = bits & TWO_BITS;
+ bits >>= 2;
+ mBits[PI_TRANSFER] = bits & TWO_BITS;
+}
+
+const LLString AGGREGATE_VALUES[4] =
+ {
+ LLString( "Empty" ),
+ LLString( "None" ),
+ LLString( "Some" ),
+ LLString( "All" )
+ };
+
+std::ostream& operator<<(std::ostream &s, const LLAggregatePermissions &perm)
+{
+ s << "{PI_COPY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_COPY]];
+ s << ", PI_MODIFY=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_MODIFY]];
+ s << ", PI_TRANSFER=" << AGGREGATE_VALUES[perm.mBits[LLAggregatePermissions::PI_TRANSFER]];
+ s << "}";
+ return s;
+}
+
+// This converts a permissions mask into a string for debugging use.
+void mask_to_string(U32 mask, char* str)
+{
+ if (mask & PERM_MOVE)
+ {
+ *str = 'V';
+ }
+ else
+ {
+ *str = ' ';
+ }
+ str++;
+
+ if (mask & PERM_MODIFY)
+ {
+ *str = 'M';
+ }
+ else
+ {
+ *str = ' ';
+ }
+ str++;
+
+ if (mask & PERM_COPY)
+ {
+ *str = 'C';
+ }
+ else
+ {
+ *str = ' ';
+ }
+ str++;
+
+ if (mask & PERM_TRANSFER)
+ {
+ *str = 'T';
+ }
+ else
+ {
+ *str = ' ';
+ }
+ str++;
+ *str = '\0';
+}
+
+
+///----------------------------------------------------------------------------
+/// exported functions
+///----------------------------------------------------------------------------
+static const std::string PERM_CREATOR_ID_LABEL("creator_id");
+static const std::string PERM_OWNER_ID_LABEL("owner_id");
+static const std::string PERM_LAST_OWNER_ID_LABEL("last_owner_id");
+static const std::string PERM_GROUP_ID_LABEL("group_id");
+static const std::string PERM_IS_OWNER_GROUP_LABEL("is_owner_group");
+static const std::string PERM_BASE_MASK_LABEL("base_mask");
+static const std::string PERM_OWNER_MASK_LABEL("owner_mask");
+static const std::string PERM_GROUP_MASK_LABEL("group_mask");
+static const std::string PERM_EVERYONE_MASK_LABEL("everyone_mask");
+static const std::string PERM_NEXT_OWNER_MASK_LABEL("next_owner_mask");
+
+LLSD ll_create_sd_from_permissions(const LLPermissions& perm)
+{
+ LLSD rv;
+ rv[PERM_CREATOR_ID_LABEL] = perm.getCreator();
+ rv[PERM_OWNER_ID_LABEL] = perm.getOwner();
+ rv[PERM_LAST_OWNER_ID_LABEL] = perm.getLastOwner();
+ rv[PERM_GROUP_ID_LABEL] = perm.getGroup();
+ rv[PERM_IS_OWNER_GROUP_LABEL] = perm.isGroupOwned();
+ rv[PERM_BASE_MASK_LABEL] = (S32)perm.getMaskBase();
+ rv[PERM_OWNER_MASK_LABEL] = (S32)perm.getMaskOwner();
+ rv[PERM_GROUP_MASK_LABEL] = (S32)perm.getMaskGroup();
+ rv[PERM_EVERYONE_MASK_LABEL] = (S32)perm.getMaskEveryone();
+ rv[PERM_NEXT_OWNER_MASK_LABEL] = (S32)perm.getMaskNextOwner();
+ return rv;
+}
+
+LLPermissions ll_permissions_from_sd(const LLSD& sd_perm)
+{
+ LLPermissions rv;
+ rv.init(
+ sd_perm[PERM_CREATOR_ID_LABEL].asUUID(),
+ sd_perm[PERM_OWNER_ID_LABEL].asUUID(),
+ sd_perm[PERM_LAST_OWNER_ID_LABEL].asUUID(),
+ sd_perm[PERM_GROUP_ID_LABEL].asUUID());
+
+ // We do a cast to U32 here since LLSD does not attempt to
+ // represent unsigned ints.
+ PermissionMask mask;
+ mask = (U32)(sd_perm[PERM_BASE_MASK_LABEL].asInteger());
+ rv.setMaskBase(mask);
+ mask = (U32)(sd_perm[PERM_OWNER_MASK_LABEL].asInteger());
+ rv.setMaskOwner(mask);
+ mask = (U32)(sd_perm[PERM_EVERYONE_MASK_LABEL].asInteger());
+ rv.setMaskEveryone(mask);
+ mask = (U32)(sd_perm[PERM_GROUP_MASK_LABEL].asInteger());
+ rv.setMaskGroup(mask);
+ mask = (U32)(sd_perm[PERM_NEXT_OWNER_MASK_LABEL].asInteger());
+ rv.setMaskNext(mask);
+ rv.fix();
+ return rv;
+}
diff --git a/indra/llinventory/llpermissions.h b/indra/llinventory/llpermissions.h
new file mode 100644
index 0000000000..76794e1ed9
--- /dev/null
+++ b/indra/llinventory/llpermissions.h
@@ -0,0 +1,426 @@
+/**
+ * @file llpermissions.h
+ * @brief Permissions structures for objects.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPERMISSIONS_H
+#define LL_LLPERMISSIONS_H
+
+#include <stdio.h>
+#include <iostream>
+
+#include "llpermissionsflags.h"
+#include "llsd.h"
+#include "lluuid.h"
+#include "llxmlnode.h"
+#include "reflective.h"
+
+// prototypes
+class LLMessageSystem;
+extern void mask_to_string(U32 mask, char* str);
+template<class T> class LLMetaClassT;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPermissions
+//
+// Class which encapsulates object and inventory permissions/ownership/etc.
+//
+// Permissions where originally a static state creator/owner and set
+// of cap bits. Since then, it has grown to include group information,
+// last owner, masks for different people. The implementation has been
+// chosen such that a uuid is stored for each current/past owner, and
+// a bitmask is stored for the base permissions, owner permissions,
+// group permissions, and everyone else permissions.
+//
+// The base permissions represent the most permissive state that the
+// permissions can possibly be in. Thus, if the base permissions do
+// not allow copying, no one can ever copy the object. The permissions
+// also maintain a tree-like hierarchy of permissions, thus, if we
+// (for sake of discussions) denote more permissive as '>', then this
+// is invariant:
+//
+// base mask >= owner mask >= group mask
+// >= everyone mask
+// >= next owner mask
+// NOTE: the group mask does not effect everyone or next, everyone
+// does not effect group or next, etc.
+//
+// It is considered a fair use right to move or delete any object you
+// own. Another fair use right is the ability to give away anything
+// which you cannot copy. One way to look at that is that if you have
+// a unique item, you can always give that one copy you have to
+// someone else.
+//
+// Most of the bitmask is easy to understand, PERM_COPY means you can
+// copy !PERM_TRANSFER means you cannot transfer, etc. Given that we
+// now track the concept of 'next owner' inside of the permissions
+// object, we can describe some new meta-meaning to the PERM_MODIFY
+// flag. PERM_MODIFY is usually meant to note if you can change an
+// item, but since we record next owner permissions, we can interpret
+// a no-modify object as 'you cannot modify this object and you cannot
+// make derivative works.' When evaluating functionality, and
+// comparisons against permissions, keep this concept in mind for
+// logical consistency.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLPermissions : public LLReflective
+{
+private:
+ LLUUID mCreator; // null if object created by system
+ LLUUID mOwner; // null if object "unowned" (owned by system)
+ LLUUID mLastOwner; // object's last owner
+ LLUUID mGroup; // The group association
+
+ PermissionMask mMaskBase; // initially permissive, progressively AND restricted by each owner
+
+ PermissionMask mMaskOwner; // set by owner, applies to owner only, restricts lower permissions
+ PermissionMask mMaskEveryone; // set by owner, applies to everyone else
+
+ PermissionMask mMaskGroup; // set by owner, applies to group that is associated with permissions
+
+ PermissionMask mMaskNextOwner; // set by owner, applied to base on transfer.
+
+ // Usually set in the fixOwnership() method based on current uuid
+ // values.
+ bool mIsGroupOwned;
+
+ // Correct for fair use - you can never take away the right to
+ // move stuff you own, and you can never take away the right to
+ // transfer something you cannot otherwise copy.
+ void fixFairUse();
+
+ // Fix internal consistency for group/agent ownership
+ void fixOwnership();
+
+public:
+ static const LLPermissions DEFAULT;
+
+ LLPermissions(); // defaults to created by system
+ //~LLPermissions();
+
+ // base initialization code
+ void init(const LLUUID& creator, const LLUUID& owner,
+ const LLUUID& last_owner, const LLUUID& group);
+ void initMasks(PermissionMask base, PermissionMask owner,
+ PermissionMask everyone, PermissionMask group,
+ PermissionMask next);
+
+ //
+ // ACCESSORS
+ //
+
+ // return the agent_id of the agent that created the item
+ const LLUUID& getCreator() const { return mCreator; }
+
+ // return the agent_id of the owner. returns LLUUID::null if group
+ // owned or public (a really big group).
+ const LLUUID& getOwner() const { return mOwner; }
+
+ // return the group_id of the group associated with the
+ // object. group_id == owner_id if the object is group owned.
+ const LLUUID& getGroup() const { return mGroup; }
+
+ // return the agent_id of the last agent owner. Only returns
+ // LLUUID::null if there has never been a previous owner.
+ const LLUUID& getLastOwner() const { return mLastOwner; }
+
+ U32 getMaskBase() const { return mMaskBase; }
+ U32 getMaskOwner() const { return mMaskOwner; }
+ U32 getMaskGroup() const { return mMaskGroup; }
+ U32 getMaskEveryone() const { return mMaskEveryone; }
+ U32 getMaskNextOwner() const { return mMaskNextOwner; }
+
+ // return TRUE if the object has any owner
+ bool isOwned() const { return (mOwner.notNull() || mIsGroupOwned); }
+
+ // return TRUE if group_id is owner.
+ bool isGroupOwned() const { return mIsGroupOwned; }
+
+ // This API returns TRUE if the object is owned at all, and FALSE
+ // otherwise. If it is owned at all, owner id is filled with
+ // either the owner id or the group id, and the is_group_owned
+ // parameter is appropriately filled. The values of owner_id and
+ // is_group_owned are not changed if the object is not owned.
+ BOOL getOwnership(LLUUID& owner_id, BOOL& is_group_owned) const;
+
+ // Gets the 'safe' owner. This should never return LLUUID::null.
+ // If no group owned, return the agent owner id normally.
+ // If group owned, return the group id.
+ // If not owned, return a random uuid which should have no power.
+ LLUUID getSafeOwner() const;
+
+ // return a cheap crc
+ U32 getCRC32() const;
+
+
+ //
+ // MANIPULATORS
+ //
+
+ // Fix hierarchy of permissions, applies appropriate permissions
+ // at each level to ensure that base permissions are respected,
+ // and also ensures that if base cannot transfer, then group and
+ // other cannot copy.
+ void fix();
+
+ // All of these methods just do exactly what they say. There is no
+ // permissions checking to see if the operation is allowed, and do
+ // not fix the permissions hierarchy. So please only use these
+ // methods when you are know what you're doing and coding on
+ // behalf of the system - ie, acting as god.
+ void set(const LLPermissions& permissions);
+ void setMaskBase(U32 mask) { mMaskBase = mask; }
+ void setMaskOwner(U32 mask) { mMaskOwner = mask; }
+ void setMaskEveryone(U32 mask) { mMaskEveryone = mask;}
+ void setMaskGroup(U32 mask) { mMaskGroup = mask;}
+ void setMaskNext(U32 mask) { mMaskNextOwner = mask; }
+
+ // Allow accumulation of permissions. Results in the tightest
+ // permissions possible. In the case of clashing UUIDs, it sets
+ // the ID to LLUUID::null.
+ void accumulate(const LLPermissions& perm);
+
+ //
+ // CHECKED MANIPULATORS
+ //
+
+ // These functions return true on success. They return false if
+ // the given agent isn't allowed to make the change. You can pass
+ // LLUUID::null as the agent id if the change is being made by the
+ // simulator itself, not on behalf of any agent - this will always
+ // succeed. Passing in group id of LLUUID:null means no group, and
+ // does not offer special permission to do anything.
+
+ // saves last owner, sets current owner, and sets the group.
+ // set is_atomic = true means that this permission represents
+ // an atomic permission and not a collection of permissions.
+ // Currently, the only way to have a collection is when an object
+ // has inventory and is then itself rolled up into an inventory
+ // item.
+ BOOL setOwnerAndGroup(const LLUUID& agent, const LLUUID& owner, const LLUUID& group, bool is_atomic);
+
+ // saves last owner, sets owner to uuid null, sets group
+ // owned. group_id must be the group of the object (that's who it
+ // is being deeded to) and the object must be group
+ // modify. Technically, the agent id and group id are not
+ // necessary, but I wanted this function to look like the other
+ // checked manipulators (since that is how it is used.) If the
+ // agent is the system or (group == mGroup and group modify and
+ // owner transfer) then this function will deed the permissions,
+ // set the next owner mask, and return TRUE. Otherwise, no change
+ // is effected, and the function returns FALSE.
+ BOOL deedToGroup(const LLUUID& agent, const LLUUID& group);
+ // Attempt to set or clear the given bitmask. Returns TRUE if you
+ // are allowed to modify the permissions. If you attempt to turn
+ // on bits not allowed by the base bits, the function will return
+ // TRUE, but those bits will not be set.
+ BOOL setBaseBits( const LLUUID& agent, BOOL set, PermissionMask bits);
+ BOOL setOwnerBits( const LLUUID& agent, BOOL set, PermissionMask bits);
+ BOOL setGroupBits( const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits);
+ BOOL setEveryoneBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits);
+ BOOL setNextOwnerBits(const LLUUID& agent, const LLUUID& group, BOOL set, PermissionMask bits);
+
+ //
+ // METHODS
+ //
+
+ // All the allow* functions return true if the given agent or
+ // group can perform the function. Prefer using this set of
+ // operations to check permissions on an object. These return
+ // true if the given agent or group can perform the function.
+ // They also return true if the object isn't owned, or the
+ // requesting agent is a system agent. See llpermissionsflags.h
+ // for bits.
+ BOOL allowOperationBy(PermissionBit op, const LLUUID& agent, const LLUUID& group = LLUUID::null) const;
+
+ inline BOOL allowModifyBy(const LLUUID &agent_id) const;
+ inline BOOL allowCopyBy(const LLUUID& agent_id) const;
+ inline BOOL allowMoveBy(const LLUUID& agent_id) const;
+ inline BOOL allowModifyBy(const LLUUID &agent_id, const LLUUID& group) const;
+ inline BOOL allowCopyBy(const LLUUID& agent_id, const LLUUID& group) const;
+ inline BOOL allowMoveBy(const LLUUID &agent_id, const LLUUID &group) const;
+
+ // This somewhat specialized function is meant for testing if the
+ // current owner is allowed to transfer to the specified agent id.
+ inline BOOL allowTransferTo(const LLUUID &agent_id) const;
+
+ //
+ // DEPRECATED.
+ //
+ // These return true if the given agent can perform the function.
+ // They also return true if the object isn't owned, or the
+ // requesting agent is a system agent. See llpermissionsflags.h
+ // for bits.
+ //BOOL allowDeleteBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
+ //BOOL allowEditBy(const LLUUID& agent_id) const { return allowModifyBy(agent_id); }
+ // saves last owner and sets current owner
+ //BOOL setOwner(const LLUUID& agent, const LLUUID& owner);
+ // This method saves the last owner, sets the current owner to the
+ // one provided, and sets the base mask as indicated.
+ //BOOL setOwner(const LLUUID& agent, const LLUUID& owner, U32 new_base_mask);
+
+ // Attempt to set or clear the given bitmask. Returns TRUE if you
+ // are allowed to modify the permissions. If you attempt to turn
+ // on bits not allowed by the base bits, the function will return
+ // TRUE, but those bits will not be set.
+ //BOOL setGroupBits( const LLUUID& agent, BOOL set, PermissionMask bits);
+ //BOOL setEveryoneBits(const LLUUID& agent, BOOL set, PermissionMask bits);
+
+ //
+ // MISC METHODS and OPERATORS
+ //
+
+ // For messaging system support
+ void packMessage(LLMessageSystem* msg) const;
+ void unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+
+ // Load/save support
+ BOOL importFile(FILE* fp);
+ BOOL exportFile(FILE* fp) const;
+
+ BOOL importLegacyStream(std::istream& input_stream);
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+
+ LLXMLNode *exportFileXML() const;
+ bool importXML(LLXMLNode* node);
+
+ bool operator==(const LLPermissions &rhs) const;
+ bool operator!=(const LLPermissions &rhs) const;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPermissions &perm);
+
+ // Reflection.
+ friend class LLMetaClassT<LLPermissions>;
+ virtual const LLMetaClass& getMetaClass() const;
+};
+
+// Inlines
+BOOL LLPermissions::allowModifyBy(const LLUUID& agent, const LLUUID& group) const
+{
+ return allowOperationBy(PERM_MODIFY, agent, group);
+}
+
+BOOL LLPermissions::allowCopyBy(const LLUUID& agent, const LLUUID& group) const
+{
+ return allowOperationBy(PERM_COPY, agent, group);
+}
+
+
+BOOL LLPermissions::allowMoveBy(const LLUUID& agent, const LLUUID& group) const
+{
+ return allowOperationBy(PERM_MOVE, agent, group);
+}
+
+BOOL LLPermissions::allowModifyBy(const LLUUID& agent) const
+{
+ return allowOperationBy(PERM_MODIFY, agent, LLUUID::null);
+}
+
+BOOL LLPermissions::allowCopyBy(const LLUUID& agent) const
+{
+ return allowOperationBy(PERM_COPY, agent, LLUUID::null);
+}
+
+BOOL LLPermissions::allowMoveBy(const LLUUID& agent) const
+{
+ return allowOperationBy(PERM_MOVE, agent, LLUUID::null);
+}
+
+BOOL LLPermissions::allowTransferTo(const LLUUID &agent_id) const
+{
+ if (mIsGroupOwned)
+ {
+ return allowOperationBy(PERM_TRANSFER, mGroup, mGroup);
+ }
+ else
+ {
+ return ((mOwner == agent_id) ? TRUE : allowOperationBy(PERM_TRANSFER, mOwner));
+ }
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLAggregatePermissions
+//
+// Class which encapsulates object and inventory permissions,
+// ownership, etc. Currently, it only aggregates PERM_COPY,
+// PERM_MODIFY, and PERM_TRANSFER.
+//
+// Usually you will construct an instance and hand the object several
+// permissions masks to aggregate the copy, modify, and
+// transferability into a nice trinary value.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLAggregatePermissions
+{
+public:
+ enum EValue
+ {
+ AP_EMPTY = 0x00,
+ AP_NONE = 0x01,
+ AP_SOME = 0x02,
+ AP_ALL = 0x03
+ };
+
+ // construct an empty aggregate permissions
+ LLAggregatePermissions();
+
+ // pass in a PERM_COPY, PERM_TRANSFER, etc, and get out a EValue
+ // enumeration describing the current aggregate permissions.
+ EValue getValue(PermissionBit bit) const;
+
+ // returns the permissions packed into the 6 LSB of a U8:
+ // 00TTMMCC
+ // where TT = transfer, MM = modify, and CC = copy
+ // LSB is to the right
+ U8 getU8() const;
+
+ // return TRUE is the aggregate permissions are empty, otherwise FALSE.
+ BOOL isEmpty() const ;
+
+ // pass in a PERM_COPY, PERM_TRANSFER, etc, and an EValue
+ // enumeration to specifically set that value. Not implemented
+ // because I'm not sure it's a useful api.
+ //void setValue(PermissionBit bit, EValue);
+
+ // Given a mask, aggregate the useful permissions.
+ void aggregate(PermissionMask mask);
+
+ // Aggregate aggregates
+ void aggregate(const LLAggregatePermissions& ag);
+
+ // message handling
+ void packMessage(LLMessageSystem* msg, const char* field) const;
+ void unpackMessage(LLMessageSystem* msg, const char* block, const char *field, S32 block_num = 0);
+
+ static const LLAggregatePermissions empty;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLAggregatePermissions &perm);
+
+protected:
+ enum EPermIndex
+ {
+ PI_COPY = 0,
+ PI_MODIFY = 1,
+ PI_TRANSFER = 2,
+ PI_END = 3,
+ PI_COUNT = 3
+ };
+ void aggregateBit(EPermIndex idx, BOOL allowed);
+ void aggregateIndex(EPermIndex idx, U8 bits);
+ static EPermIndex perm2PermIndex(PermissionBit bit);
+
+ // structure used to store the aggregate so far.
+ U8 mBits[PI_COUNT];
+};
+
+// These functions convert between structured data and permissions as
+// appropriate for serialization. The permissions are a map of things
+// like 'creator_id', 'owner_id', etc, with the value copied from the
+// permission object.
+LLSD ll_create_sd_from_permissions(const LLPermissions& perm);
+LLPermissions ll_permissions_from_sd(const LLSD& sd_perm);
+
+#endif
diff --git a/indra/llinventory/llpermissionsflags.h b/indra/llinventory/llpermissionsflags.h
new file mode 100644
index 0000000000..f45758c501
--- /dev/null
+++ b/indra/llinventory/llpermissionsflags.h
@@ -0,0 +1,78 @@
+/**
+ * @file llpermissionsflags.h
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPERMISSIONSFLAGS_H
+#define LL_LLPERMISSIONSFLAGS_H
+
+// llpermissionsflags.h
+// Copyright 2002, Linden Research, Inc.
+//
+// Flags for various permissions bits.
+// Shared between viewer and simulator.
+
+// permission bits
+typedef U32 PermissionMask;
+typedef U32 PermissionBit;
+
+
+// Do you have permission to transfer ownership of the object or
+// item. Fair use rules dictate that if you cannot copy, you can
+// always transfer.
+const PermissionBit PERM_TRANSFER = (1 << 13); // 0x00002000
+
+// objects, scale or change textures
+// parcels, allow building on it
+const PermissionBit PERM_MODIFY = (1 << 14); // 0x00004000
+
+// objects, allow copy
+const PermissionBit PERM_COPY = (1 << 15); // 0x00008000
+
+// parcels, allow entry, deprecated
+//const PermissionBit PERM_ENTER = (1 << 16); // 0x00010000
+
+// parcels, allow terraform, deprecated
+//const PermissionBit PERM_TERRAFORM = (1 << 17); // 0x00020000
+
+// NOTA BENE: This flag is NO LONGER USED!!! However, it is possible that some
+// objects in the universe have it set so DON"T USE IT going forward.
+//const PermissionBit PERM_OWNER_DEBIT = (1 << 18); // 0x00040000
+
+// objects, can grab/translate/rotate
+const PermissionBit PERM_MOVE = (1 << 19); // 0x00080000
+
+// parcels, avatars take damage, deprecated
+//const PermissionBit PERM_DAMAGE = (1 << 20); // 0x00100000
+
+// don't use bit 31 -- printf/scanf with "%x" assume signed numbers
+const PermissionBit PERM_RESERVED = ((U32)1) << 31;
+
+const PermissionMask PERM_NONE = 0x00000000;
+const PermissionMask PERM_ALL = 0x7FFFFFFF;
+//const PermissionMask PERM_ALL_PARCEL = PERM_MODIFY | PERM_ENTER | PERM_TERRAFORM | PERM_DAMAGE;
+const PermissionMask PERM_ITEM_UNRESTRICTED = PERM_MODIFY | PERM_COPY | PERM_TRANSFER;
+
+
+// Useful stuff for transmission.
+// Which permissions field are we trying to change?
+const U8 PERM_BASE = 0x01;
+// TODO: Add another PERM_OWNER operation type for allowOperationBy DK 04/03/06
+const U8 PERM_OWNER = 0x02;
+const U8 PERM_GROUP = 0x04;
+const U8 PERM_EVERYONE = 0x08;
+const U8 PERM_NEXT_OWNER = 0x10;
+
+// This is just a quickie debugging key
+// no modify: PERM_ALL & ~PERM_MODIFY = 0x7fffbfff
+// no copy: PERM_ALL & ~PERM_COPY = 0x7fff7fff
+// no modify or copy: = 0x7fff3fff
+// no transfer: PERM_ALL & ~PERM_TRANSFER = 0x7fffdfff
+// no modify, no transfer = 0x7fff9fff
+// no copy, no transfer (INVALID!) = 0x7fff5fff
+// no modify, no copy, no transfer (INVALID!) = 0x7fff1fff
+
+
+#endif
diff --git a/indra/llinventory/llsaleinfo.cpp b/indra/llinventory/llsaleinfo.cpp
new file mode 100644
index 0000000000..7e2b293d42
--- /dev/null
+++ b/indra/llinventory/llsaleinfo.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llsaleinfo.cpp
+ * @brief
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iostream>
+#include "linden_common.h"
+
+#include "llsaleinfo.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "llsdutil.h"
+
+// use this to avoid temporary object creation
+const LLSaleInfo LLSaleInfo::DEFAULT;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const char* FOR_SALE_NAMES[] =
+{
+ "not",
+ "orig",
+ "copy",
+ "cntn"
+};
+
+///----------------------------------------------------------------------------
+/// Class llsaleinfo
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLSaleInfo::LLSaleInfo() :
+ mSaleType(LLSaleInfo::FS_NOT),
+ mSalePrice(DEFAULT_PRICE)
+{
+}
+
+LLSaleInfo::LLSaleInfo(EForSale sale_type, S32 sale_price) :
+ mSaleType(sale_type),
+ mSalePrice(sale_price)
+{
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+}
+
+BOOL LLSaleInfo::isForSale() const
+{
+ return (FS_NOT != mSaleType);
+}
+
+U32 LLSaleInfo::getCRC32() const
+{
+ U32 rv = (U32)mSalePrice;
+ rv += (mSaleType * 0x07073096);
+ return rv;
+}
+
+
+BOOL LLSaleInfo::exportFile(FILE* fp) const
+{
+ fprintf(fp, "\tsale_info\t0\n\t{\n");
+ fprintf(fp, "\t\tsale_type\t%s\n", lookup(mSaleType));
+ fprintf(fp, "\t\tsale_price\t%d\n", mSalePrice);
+ fprintf(fp,"\t}\n");
+ return TRUE;
+}
+
+BOOL LLSaleInfo::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream << "\tsale_info\t0\n\t{\n";
+ output_stream << "\t\tsale_type\t" << lookup(mSaleType) << "\n";
+ output_stream << "\t\tsale_price\t" << mSalePrice << "\n";
+ output_stream <<"\t}\n";
+ return TRUE;
+}
+
+LLSD LLSaleInfo::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["sale_type"] = lookup(mSaleType);
+ sd["sale_price"] = mSalePrice;
+ return sd;
+}
+
+bool LLSaleInfo::fromLLSD(LLSD& sd, BOOL& has_perm_mask, U32& perm_mask)
+{
+ const char *w;
+
+ mSaleType = lookup(sd["sale_type"].asString().c_str());
+ mSalePrice = llclamp(sd["sale_price"].asInteger(), 0, S32_MAX);
+ w = "perm_mask";
+ if (sd.has(w))
+ {
+ has_perm_mask = TRUE;
+ perm_mask = ll_U32_from_sd(sd[w]);
+ }
+ return true;
+}
+
+LLXMLNode *LLSaleInfo::exportFileXML() const
+{
+ LLXMLNode *ret = new LLXMLNode("sale_info", FALSE);
+ LLString type_str = lookup(mSaleType);
+ ret->createChild("type", TRUE)->setStringValue(1, &type_str);
+ ret->createChild("price", TRUE)->setIntValue(1, &mSalePrice);
+ return ret;
+}
+
+BOOL LLSaleInfo::importXML(LLXMLNode* node)
+{
+ BOOL success = FALSE;
+ if (node)
+ {
+ success = TRUE;
+ LLXMLNodePtr sub_node;
+ if (node->getChild("type", sub_node))
+ {
+ mSaleType = lookup(sub_node->getValue().c_str());
+ }
+ if (node->getChild("price", sub_node))
+ {
+ success &= (1 == sub_node->getIntValue(1, &mSalePrice));
+ }
+ if (!success)
+ {
+ lldebugs << "LLSaleInfo::importXML() failed for node named '"
+ << node->getName() << "'" << llendl;
+ }
+ }
+ return success;
+}
+
+BOOL LLSaleInfo::importFile(FILE* fp, BOOL& has_perm_mask, U32& perm_mask)
+{
+ has_perm_mask = FALSE;
+
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+ BOOL success = TRUE;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(success && (!feof(fp)))
+ {
+ fgets(buffer, MAX_STRING, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("sale_type", keyword))
+ {
+ mSaleType = lookup(valuestr);
+ }
+ else if(0 == strcmp("sale_price", keyword))
+ {
+ sscanf(valuestr, "%d", &mSalePrice);
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+ }
+ else if (!strcmp("perm_mask", keyword))
+ {
+ //llinfos << "found deprecated keyword perm_mask" << llendl;
+ has_perm_mask = TRUE;
+ sscanf(valuestr, "%x", &perm_mask);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in sale info import" << llendl;
+ }
+ }
+ return success;
+}
+
+BOOL LLSaleInfo::importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask)
+{
+ has_perm_mask = FALSE;
+
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+ BOOL success = TRUE;
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(success && input_stream.good())
+ {
+ input_stream.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("sale_type", keyword))
+ {
+ mSaleType = lookup(valuestr);
+ }
+ else if(0 == strcmp("sale_price", keyword))
+ {
+ sscanf(valuestr, "%d", &mSalePrice);
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+ }
+ else if (!strcmp("perm_mask", keyword))
+ {
+ //llinfos << "found deprecated keyword perm_mask" << llendl;
+ has_perm_mask = TRUE;
+ sscanf(valuestr, "%x", &perm_mask);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in sale info import" << llendl;
+ }
+ }
+ return success;
+}
+
+void LLSaleInfo::setSalePrice(S32 price)
+{
+ mSalePrice = price;
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+}
+
+void LLSaleInfo::packMessage(LLMessageSystem* msg) const
+{
+ U8 sale_type = static_cast<U8>(mSaleType);
+ msg->addU8Fast(_PREHASH_SaleType, sale_type);
+ msg->addS32Fast(_PREHASH_SalePrice, mSalePrice);
+ //msg->addU32Fast(_PREHASH_NextOwnerMask, mNextOwnerPermMask);
+}
+
+void LLSaleInfo::unpackMessage(LLMessageSystem* msg, const char* block)
+{
+ U8 sale_type;
+ msg->getU8Fast(block, _PREHASH_SaleType, sale_type);
+ mSaleType = static_cast<EForSale>(sale_type);
+ msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice);
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+ //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask);
+}
+
+void LLSaleInfo::unpackMultiMessage(LLMessageSystem* msg, const char* block,
+ S32 block_num)
+{
+ U8 sale_type;
+ msg->getU8Fast(block, _PREHASH_SaleType, sale_type, block_num);
+ mSaleType = static_cast<EForSale>(sale_type);
+ msg->getS32Fast(block, _PREHASH_SalePrice, mSalePrice, block_num);
+ mSalePrice = llclamp(mSalePrice, 0, S32_MAX);
+ //msg->getU32Fast(block, _PREHASH_NextOwnerMask, mNextOwnerPermMask, block_num);
+}
+
+LLSaleInfo::EForSale LLSaleInfo::lookup(const char* name)
+{
+ for(S32 i = 0; i < FS_COUNT; i++)
+ {
+ if(0 == strcmp(name, FOR_SALE_NAMES[i]))
+ {
+ // match
+ return (EForSale)i;
+ }
+ }
+ return FS_NOT;
+}
+
+const char* LLSaleInfo::lookup(EForSale type)
+{
+ if((type >= 0) && (type < FS_COUNT))
+ {
+ return FOR_SALE_NAMES[S32(type)];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Allow accumulation of sale info. The price of each is added,
+// conflict in sale type results in FS_NOT, and the permissions are
+// tightened.
+void LLSaleInfo::accumulate(const LLSaleInfo& sale_info)
+{
+ if(mSaleType != sale_info.mSaleType)
+ {
+ mSaleType = FS_NOT;
+ }
+ mSalePrice += sale_info.mSalePrice;
+ //mNextOwnerPermMask &= sale_info.mNextOwnerPermMask;
+}
+
+bool LLSaleInfo::operator==(const LLSaleInfo &rhs) const
+{
+ return (
+ (mSaleType == rhs.mSaleType) &&
+ (mSalePrice == rhs.mSalePrice)
+ );
+}
+
+bool LLSaleInfo::operator!=(const LLSaleInfo &rhs) const
+{
+ return (
+ (mSaleType != rhs.mSaleType) ||
+ (mSalePrice != rhs.mSalePrice)
+ );
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+///----------------------------------------------------------------------------
+/// exported functions
+///----------------------------------------------------------------------------
+static const std::string ST_TYPE_LABEL("sale_type");
+static const std::string ST_PRICE_LABEL("sale_price");
+
+LLSD ll_create_sd_from_sale_info(const LLSaleInfo& sale)
+{
+ LLSD rv;
+ const char* type = LLSaleInfo::lookup(sale.getSaleType());
+ if(!type) type = LLSaleInfo::lookup(LLSaleInfo::FS_NOT);
+ rv[ST_TYPE_LABEL] = type;
+ rv[ST_PRICE_LABEL] = sale.getSalePrice();
+ return rv;
+}
+
+LLSaleInfo ll_sale_info_from_sd(const LLSD& sd)
+{
+ LLSaleInfo rv;
+ rv.setSaleType(LLSaleInfo::lookup(sd[ST_TYPE_LABEL].asString().c_str()));
+ rv.setSalePrice(llclamp((S32)sd[ST_PRICE_LABEL], 0, S32_MAX));
+ return rv;
+}
diff --git a/indra/llinventory/llsaleinfo.h b/indra/llinventory/llsaleinfo.h
new file mode 100644
index 0000000000..2eceea87ef
--- /dev/null
+++ b/indra/llinventory/llsaleinfo.h
@@ -0,0 +1,110 @@
+/**
+ * @file llsaleinfo.h
+ * @brief LLSaleInfo class header file.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSALEINFO_H
+#define LL_LLSALEINFO_H
+
+#include <stdio.h>
+#include <iostream>
+
+#include "llpermissionsflags.h"
+#include "llsd.h"
+#include "llxmlnode.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLSaleInfo
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+// L$ default price for objects
+const S32 DEFAULT_PRICE = 10;
+
+class LLMessageSystem;
+
+class LLSaleInfo
+{
+public:
+ // use this to avoid temporary object creation
+ static const LLSaleInfo DEFAULT;
+
+ enum EForSale
+ {
+ // item is not to be considered for transactions
+ FS_NOT = 0,
+
+ // the origional is on sale
+ FS_ORIGINAL = 1,
+
+ // A copy is for sale
+ FS_COPY = 2,
+
+ // Valid only for tasks, the inventory is for sale
+ // at the price in this structure.
+ FS_CONTENTS = 3,
+
+ FS_COUNT
+ };
+
+protected:
+ EForSale mSaleType;
+ S32 mSalePrice;
+
+public:
+ // default constructor is fine usually
+ LLSaleInfo();
+ LLSaleInfo(EForSale sale_type, S32 sale_price);
+
+ // accessors
+ BOOL isForSale() const;
+ EForSale getSaleType() const { return mSaleType; }
+ S32 getSalePrice() const { return mSalePrice; }
+ U32 getCRC32() const;
+
+ // mutators
+ void setSaleType(EForSale type) { mSaleType = type; }
+ void setSalePrice(S32 price);
+ //void setNextOwnerPermMask(U32 mask) { mNextOwnerPermMask = mask; }
+
+
+ // file serialization
+ BOOL exportFile(FILE* fp) const;
+ BOOL importFile(FILE* fp, BOOL& has_perm_mask, U32& perm_mask);
+
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd, BOOL& has_perm_mask, U32& perm_mask);
+ BOOL importLegacyStream(std::istream& input_stream, BOOL& has_perm_mask, U32& perm_mask);
+
+ LLXMLNode *exportFileXML() const;
+ BOOL importXML(LLXMLNode* node);
+
+ // message serialization
+ void packMessage(LLMessageSystem* msg) const;
+ void unpackMessage(LLMessageSystem* msg, const char* block);
+ void unpackMultiMessage(LLMessageSystem* msg, const char* block,
+ S32 block_num);
+
+ // static functionality for determine for sale status.
+ static EForSale lookup(const char* name);
+ static const char* lookup(EForSale type);
+
+ // Allow accumulation of sale info. The price of each is added,
+ // conflict in sale type results in FS_NOT, and the permissions
+ // are tightened.
+ void accumulate(const LLSaleInfo& sale_info);
+
+ bool operator==(const LLSaleInfo &rhs) const;
+ bool operator!=(const LLSaleInfo &rhs) const;
+};
+
+// These functions convert between structured data and sale info as
+// appropriate for serialization.
+LLSD ll_create_sd_from_sale_info(const LLSaleInfo& sale);
+LLSaleInfo ll_sale_info_from_sd(const LLSD& sd);
+
+#endif // LL_LLSALEINFO_H
diff --git a/indra/llinventory/lltransactionflags.cpp b/indra/llinventory/lltransactionflags.cpp
new file mode 100644
index 0000000000..3f1aa14959
--- /dev/null
+++ b/indra/llinventory/lltransactionflags.cpp
@@ -0,0 +1,43 @@
+/**
+ * @file lltransactionflags.cpp
+ * @brief Some exported symbols and functions for dealing with
+ * transaction flags.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransactionflags.h"
+
+const U8 TRANSACTION_FLAGS_NONE = 0;
+const U8 TRANSACTION_FLAG_SOURCE_GROUP = 1;
+const U8 TRANSACTION_FLAG_DEST_GROUP = 2;
+const U8 TRANSACTION_FLAG_OWNER_GROUP = 4;
+const U8 TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION = 8;
+const U8 TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION_REMOVAL = 16;
+
+U8 pack_transaction_flags(BOOL is_source_group, BOOL is_dest_group)
+{
+ U8 rv = 0;
+ if(is_source_group) rv |= TRANSACTION_FLAG_SOURCE_GROUP;
+ if(is_dest_group) rv |= TRANSACTION_FLAG_DEST_GROUP;
+ return rv;
+}
+
+BOOL is_tf_source_group(TransactionFlags flags)
+{
+ return ((flags & TRANSACTION_FLAG_SOURCE_GROUP) == TRANSACTION_FLAG_SOURCE_GROUP);
+}
+
+BOOL is_tf_dest_group(TransactionFlags flags)
+{
+ return ((flags & TRANSACTION_FLAG_DEST_GROUP) == TRANSACTION_FLAG_DEST_GROUP);
+}
+
+BOOL is_tf_owner_group(TransactionFlags flags)
+{
+ return ((flags & TRANSACTION_FLAG_OWNER_GROUP) == TRANSACTION_FLAG_OWNER_GROUP);
+}
+
diff --git a/indra/llinventory/lltransactionflags.h b/indra/llinventory/lltransactionflags.h
new file mode 100644
index 0000000000..eaa138fef7
--- /dev/null
+++ b/indra/llinventory/lltransactionflags.h
@@ -0,0 +1,27 @@
+/**
+ * @file lltransactionflags.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSACTIONFLAGS_H
+#define LL_LLTRANSACTIONFLAGS_H
+
+typedef U8 TransactionFlags;
+
+// defined in common/llinventory/lltransactionflags.cpp
+extern const TransactionFlags TRANSACTION_FLAGS_NONE;
+extern const TransactionFlags TRANSACTION_FLAG_SOURCE_GROUP;
+extern const TransactionFlags TRANSACTION_FLAG_DEST_GROUP;
+extern const TransactionFlags TRANSACTION_FLAG_OWNER_GROUP;
+extern const TransactionFlags TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION;
+extern const TransactionFlags TRANSACTION_FLAG_SIMULTANEOUS_CONTRIBUTION_REMOVAL;
+
+// very simple helper functions
+TransactionFlags pack_transaction_flags(BOOL is_source_group, BOOL is_dest_group);
+BOOL is_tf_source_group(TransactionFlags flags);
+BOOL is_tf_dest_group(TransactionFlags flags);
+BOOL is_tf_owner_group(TransactionFlags flags);
+
+#endif // LL_LLTRANSACTIONFLAGS_H
diff --git a/indra/llinventory/lltransactiontypes.h b/indra/llinventory/lltransactiontypes.h
new file mode 100644
index 0000000000..d7894b2fdb
--- /dev/null
+++ b/indra/llinventory/lltransactiontypes.h
@@ -0,0 +1,93 @@
+/**
+ * @file lltransactiontypes.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSACTIONTYPES_H
+#define LL_LLTRANSACTIONTYPES_H
+
+// *NOTE: The constants in this file are also in the
+// transaction_description table in the database. If you add a
+// constant here, please add it to the database. eg:
+//
+// insert into transaction_description
+// set type = 1000, description = 'Object Claim';
+//
+// Also add it to the various money string lookups on the dataserver
+// in lldatamoney
+
+// Money transaction failure codes
+const U8 TRANS_FAIL_SIMULATOR_TIMEOUT = 1;
+const U8 TRANS_FAIL_DATASERVER_TIMEOUT = 2;
+
+// Codes up to 999 for error conditions
+const S32 TRANS_NULL = 0;
+
+// Codes 1000-1999 reserved for one-time charges
+const S32 TRANS_OBJECT_CLAIM = 1000;
+const S32 TRANS_LAND_CLAIM = 1001;
+const S32 TRANS_GROUP_CREATE = 1002;
+const S32 TRANS_OBJECT_PUBLIC_CLAIM = 1003;
+const S32 TRANS_GROUP_JOIN = 1004; // May be moved to group transactions eventually
+const S32 TRANS_TELEPORT_CHARGE = 1100; // FF not sure why this jumps to 1100...
+const S32 TRANS_UPLOAD_CHARGE = 1101;
+const S32 TRANS_LAND_AUCTION = 1102;
+const S32 TRANS_CLASSIFIED_CHARGE = 1103;
+
+// Codes 2000-2999 reserved for recurrent charges
+const S32 TRANS_OBJECT_TAX = 2000;
+const S32 TRANS_LAND_TAX = 2001;
+const S32 TRANS_LIGHT_TAX = 2002;
+const S32 TRANS_PARCEL_DIR_FEE = 2003;
+const S32 TRANS_GROUP_TAX = 2004; // Taxes incurred as part of group membership
+const S32 TRANS_CLASSIFIED_RENEW = 2005;
+
+// Codes 3000-3999 reserved for inventory transactions
+const S32 TRANS_GIVE_INVENTORY = 3000;
+
+// Codes 5000-5999 reserved for transfers between users
+const S32 TRANS_OBJECT_SALE = 5000;
+const S32 TRANS_GIFT = 5001;
+const S32 TRANS_LAND_SALE = 5002;
+const S32 TRANS_REFER_BONUS = 5003;
+const S32 TRANS_INVENTORY_SALE = 5004;
+const S32 TRANS_REFUND_PURCHASE = 5005;
+const S32 TRANS_LAND_PASS_SALE = 5006;
+const S32 TRANS_DWELL_BONUS = 5007;
+const S32 TRANS_PAY_OBJECT = 5008;
+const S32 TRANS_OBJECT_PAYS = 5009;
+
+// Codes 6000-6999 reserved for group transactions
+//const S32 TRANS_GROUP_JOIN = 6000; //reserved for future use
+const S32 TRANS_GROUP_LAND_DEED = 6001;
+const S32 TRANS_GROUP_OBJECT_DEED = 6002;
+const S32 TRANS_GROUP_LIABILITY = 6003;
+const S32 TRANS_GROUP_DIVIDEND = 6004;
+const S32 TRANS_MEMBERSHIP_DUES = 6005;
+
+// Codes 8000-8999 reserved for one-type credits
+const S32 TRANS_OBJECT_RELEASE = 8000;
+const S32 TRANS_LAND_RELEASE = 8001;
+const S32 TRANS_OBJECT_DELETE = 8002;
+const S32 TRANS_OBJECT_PUBLIC_DECAY = 8003;
+const S32 TRANS_OBJECT_PUBLIC_DELETE= 8004;
+
+// Code 9000-9099 reserved for usertool transactions
+const S32 TRANS_LINDEN_ADJUSTMENT = 9000;
+const S32 TRANS_LINDEN_GRANT = 9001;
+const S32 TRANS_LINDEN_PENALTY = 9002;
+const S32 TRANS_EVENT_FEE = 9003;
+const S32 TRANS_EVENT_PRIZE = 9004;
+
+// These must match entries in money_stipend table in MySQL
+// Codes 10000-10999 reserved for stipend credits
+const S32 TRANS_STIPEND_BASIC = 10000;
+const S32 TRANS_STIPEND_DEVELOPER = 10001;
+const S32 TRANS_STIPEND_ALWAYS = 10002;
+const S32 TRANS_STIPEND_DAILY = 10003;
+const S32 TRANS_STIPEND_RATING = 10004;
+const S32 TRANS_STIPEND_DELTA = 10005;
+
+#endif
diff --git a/indra/llinventory/lluserrelations.cpp b/indra/llinventory/lluserrelations.cpp
new file mode 100644
index 0000000000..c10f6f610a
--- /dev/null
+++ b/indra/llinventory/lluserrelations.cpp
@@ -0,0 +1,89 @@
+/**
+ * @file lluserrealations.cpp
+ * @author Phoenix
+ * @date 2006-10-12
+ * @brief Implementation of a simple cache of user relations.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lluserrelations.h"
+
+// static
+const U8 LLRelationship::GRANTED_VISIBLE_MASK = LLRelationship::GRANT_MODIFY_OBJECTS | LLRelationship::GRANT_MAP_LOCATION;
+const LLRelationship LLRelationship::DEFAULT_RELATIONSHIP = LLRelationship(GRANT_ONLINE_STATUS, GRANT_ONLINE_STATUS, false);
+
+LLRelationship::LLRelationship() :
+ mGrantToAgent(0),
+ mGrantFromAgent(0),
+ mIsOnline(false)
+{
+}
+
+LLRelationship::LLRelationship(S32 grant_to, S32 grant_from, bool is_online) :
+ mGrantToAgent(grant_to),
+ mGrantFromAgent(grant_from),
+ mIsOnline(is_online)
+{
+}
+
+bool LLRelationship::isOnline() const
+{
+ return mIsOnline;
+}
+
+void LLRelationship::online(bool is_online)
+{
+ mIsOnline = is_online;
+}
+
+bool LLRelationship::isRightGrantedTo(S32 rights) const
+{
+ return ((mGrantToAgent & rights) == rights);
+}
+
+bool LLRelationship::isRightGrantedFrom(S32 rights) const
+{
+ return ((mGrantFromAgent & rights) == rights);
+}
+
+S32 LLRelationship::getRightsGrantedTo() const
+{
+ return mGrantToAgent;
+}
+
+S32 LLRelationship::getRightsGrantedFrom() const
+{
+ return mGrantFromAgent;
+}
+
+void LLRelationship::grantRights(S32 to_agent, S32 from_agent)
+{
+ mGrantToAgent |= to_agent;
+ mGrantFromAgent |= from_agent;
+}
+
+void LLRelationship::revokeRights(S32 to_agent, S32 from_agent)
+{
+ mGrantToAgent &= ~to_agent;
+ mGrantFromAgent &= ~from_agent;
+}
+
+
+
+/*
+bool LLGrantedRights::getNextRights(
+ LLUUID& agent_id,
+ S32& to_agent,
+ S32& from_agent) const
+{
+ rights_map_t::const_iterator iter = mRights.upper_bound(agent_id);
+ if(iter == mRights.end()) return false;
+ agent_id = (*iter).first;
+ to_agent = (*iter).second.mToAgent;
+ from_agent = (*iter).second.mFromAgent;
+ return true;
+}
+*/
diff --git a/indra/llinventory/lluserrelations.h b/indra/llinventory/lluserrelations.h
new file mode 100644
index 0000000000..7c24254339
--- /dev/null
+++ b/indra/llinventory/lluserrelations.h
@@ -0,0 +1,154 @@
+/**
+ * @file llluserrelations.h
+ * @author Phoenix
+ * @date 2006-10-12
+ * @brief Declaration of a class for handling granted rights.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUSERRELAIONS_H
+#define LL_LLUSERRELAIONS_H
+
+#include <map>
+#include "lluuid.h"
+
+/**
+ * @class LLRelationship
+ *
+ * This class represents a relationship between two agents, where the
+ * related agent is stored and the other agent is the relationship is
+ * implicit by container ownership.
+ * This is merely a cache of this information used by the sim
+ * and viewer.
+ *
+ * You are expected to use this in a map or similar structure, eg:
+ * typedef std::map<LLUUID, LLRelationship> agent_relationship_map;
+ */
+class LLRelationship
+{
+public:
+ /**
+ * @brief Constructors.
+ */
+ LLRelationship();
+ LLRelationship(S32 grant_to, S32 grant_from, bool is_online);
+
+ static const LLRelationship DEFAULT_RELATIONSHIP;
+
+ /**
+ * @name Status functionality
+ *
+ * I thought it would be keen to have a generic status interface,
+ * but the only thing we currently cache is online status. As this
+ * assumption changes, this API may evolve.
+ */
+ //@{
+ /**
+ * @brief Does this instance believe the related agent is currently
+ * online or available.
+ *
+ * NOTE: This API may be deprecated if there is any transient status
+ * other than online status, for example, away/busy/etc.
+ *
+ * This call does not check any kind of central store or make any
+ * deep information calls - it simply checks a cache of online
+ * status.
+ * @return Returns true if this relationship believes the agent is
+ * online.
+ */
+ bool isOnline() const;
+
+ /**
+ * @brief Set the online status.
+ *
+ * NOTE: This API may be deprecated if there is any transient status
+ * other than online status.
+ * @param is_online Se the online status
+ */
+ void online(bool is_online);
+ //@}
+
+ /* @name Granted rights
+ */
+ //@{
+ /**
+ * @brief Anonymous enumeration for specifying rights.
+ */
+ enum
+ {
+ GRANT_NONE = 0x0,
+ GRANT_ONLINE_STATUS = 0x1,
+ GRANT_MAP_LOCATION = 0x2,
+ GRANT_MODIFY_OBJECTS = 0x4,
+ };
+
+ /**
+ * ???
+ */
+ static const U8 GRANTED_VISIBLE_MASK;
+
+ /**
+ * @brief Check for a set of rights granted to agent.
+ *
+ * @param rights A bitfield to check for rights.
+ * @return Returns true if all rights have been granted.
+ */
+ bool isRightGrantedTo(S32 rights) const;
+
+ /**
+ * @brief Check for a set of rights granted from an agent.
+ *
+ * @param rights A bitfield to check for rights.
+ * @return Returns true if all rights have been granted.
+ */
+ bool isRightGrantedFrom(S32 rights) const;
+
+ /**
+ * @brief Get the rights granted to the other agent.
+ *
+ * @return Returns the bitmask of granted rights.
+ */
+ S32 getRightsGrantedTo() const;
+
+ /**
+ * @brief Get the rights granted from the other agent.
+ *
+ * @return Returns the bitmask of granted rights.
+ */
+ S32 getRightsGrantedFrom() const;
+
+ void setRightsTo(S32 to_agent) { mGrantToAgent = to_agent; }
+ void setRightsFrom(S32 from_agent) { mGrantFromAgent = from_agent; }
+
+ /**
+ * @brief Grant a set of rights.
+ *
+ * Any bit which is set will grant that right if it is set in the
+ * instance. You can pass in LLGrantedRights::NONE to not change
+ * that field.
+ * @param to_agent The rights to grant to agent_id.
+ * @param from_agent The rights granted from agent_id.
+ */
+ void grantRights(S32 to_agent, S32 from_agent);
+
+ /**
+ * @brief Revoke a set of rights.
+ *
+ * Any bit which is set will revoke that right if it is set in the
+ * instance. You can pass in LLGrantedRights::NONE to not change
+ * that field.
+ * @param to_agent The rights to grant to agent_id.
+ * @param from_agent The rights granted from agent_id.
+ */
+ void revokeRights(S32 to_agent, S32 from_agent);
+ //@}
+
+protected:
+ S32 mGrantToAgent;
+ S32 mGrantFromAgent;
+ bool mIsOnline;
+};
+
+#endif // LL_LLUSERRELAIONS_H
diff --git a/indra/llmath/camera.h b/indra/llmath/camera.h
new file mode 100644
index 0000000000..f130a036a1
--- /dev/null
+++ b/indra/llmath/camera.h
@@ -0,0 +1,9 @@
+/**
+ * @file camera.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+
+#include "llcamera.h"
diff --git a/indra/llmath/coordframe.h b/indra/llmath/coordframe.h
new file mode 100644
index 0000000000..5efab4b63e
--- /dev/null
+++ b/indra/llmath/coordframe.h
@@ -0,0 +1,9 @@
+/**
+ * @file coordframe.h
+ * @brief Legacy wrapper header.
+ *
+ * Copyright (c) 2000-$CurrentYear$ Linden Research, Inc.
+ * $License$
+ */
+
+#include "llcoordframe.h"
diff --git a/indra/llmath/llbboxlocal.cpp b/indra/llmath/llbboxlocal.cpp
new file mode 100644
index 0000000000..ba0d4f38ed
--- /dev/null
+++ b/indra/llmath/llbboxlocal.cpp
@@ -0,0 +1,37 @@
+/**
+ * @file llbboxlocal.cpp
+ * @brief General purpose bounding box class (Not axis aligned).
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbboxlocal.h"
+#include "m4math.h"
+
+void LLBBoxLocal::addPoint(const LLVector3& p)
+{
+ mMin.mV[VX] = llmin( p.mV[VX], mMin.mV[VX] );
+ mMin.mV[VY] = llmin( p.mV[VY], mMin.mV[VY] );
+ mMin.mV[VZ] = llmin( p.mV[VZ], mMin.mV[VZ] );
+ mMax.mV[VX] = llmax( p.mV[VX], mMax.mV[VX] );
+ mMax.mV[VY] = llmax( p.mV[VY], mMax.mV[VY] );
+ mMax.mV[VZ] = llmax( p.mV[VZ], mMax.mV[VZ] );
+}
+
+void LLBBoxLocal::expand( F32 delta )
+{
+ mMin.mV[VX] -= delta;
+ mMin.mV[VY] -= delta;
+ mMin.mV[VZ] -= delta;
+ mMax.mV[VX] += delta;
+ mMax.mV[VY] += delta;
+ mMax.mV[VZ] += delta;
+}
+
+LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b)
+{
+ return LLBBoxLocal( a.mMin * b, a.mMax * b );
+}
diff --git a/indra/llmath/llbboxlocal.h b/indra/llmath/llbboxlocal.h
new file mode 100644
index 0000000000..c8b7fbbbc8
--- /dev/null
+++ b/indra/llmath/llbboxlocal.h
@@ -0,0 +1,50 @@
+/**
+ * @file llbboxlocal.h
+ * @brief General purpose bounding box class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_BBOXLOCAL_H
+#define LL_BBOXLOCAL_H
+
+#include "v3math.h"
+
+class LLMatrix4;
+
+class LLBBoxLocal
+{
+public:
+ LLBBoxLocal() {}
+ LLBBoxLocal( const LLVector3& min, const LLVector3& max ) : mMin( min ), mMax( max ) {}
+ // Default copy constructor is OK.
+
+ const LLVector3& getMin() const { return mMin; }
+ void setMin( const LLVector3& min ) { mMin = min; }
+
+ const LLVector3& getMax() const { return mMax; }
+ void setMax( const LLVector3& max ) { mMax = max; }
+
+ LLVector3 getCenter() const { return (mMax - mMin) * 0.5f + mMin; }
+ LLVector3 getExtent() const { return mMax - mMin; }
+
+ BOOL containsPoint(const LLVector3& p) const;
+ BOOL intersects(const LLBBoxLocal& b) const;
+
+ void addPoint(const LLVector3& p);
+ void addBBox(const LLBBoxLocal& b) { addPoint( b.mMin ); addPoint( b.mMax ); }
+
+ void expand( F32 delta );
+
+ friend LLBBoxLocal operator*(const LLBBoxLocal& a, const LLMatrix4& b);
+
+private:
+ LLVector3 mMin;
+ LLVector3 mMax;
+};
+
+LLBBoxLocal operator*(const LLBBoxLocal &a, const LLMatrix4 &b);
+
+
+#endif // LL_BBOXLOCAL_H
diff --git a/indra/llmath/llcamera.cpp b/indra/llmath/llcamera.cpp
new file mode 100644
index 0000000000..675659c68a
--- /dev/null
+++ b/indra/llmath/llcamera.cpp
@@ -0,0 +1,591 @@
+/**
+ * @file llcamera.cpp
+ * @brief Implementation of the LLCamera class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+#include "llcamera.h"
+
+// ---------------- Constructors and destructors ----------------
+
+LLCamera::LLCamera() :
+ LLCoordFrame(),
+ mView(DEFAULT_FIELD_OF_VIEW),
+ mAspect(DEFAULT_ASPECT_RATIO),
+ mViewHeightInPixels( -1 ), // invalid height
+ mNearPlane(DEFAULT_NEAR_PLANE),
+ mFarPlane(DEFAULT_FAR_PLANE),
+ mFixedDistance(-1.f)
+{
+ calculateFrustumPlanes();
+}
+
+
+LLCamera::LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane) :
+ LLCoordFrame(),
+ mView(z_field_of_view),
+ mAspect(aspect_ratio),
+ mViewHeightInPixels(view_height_in_pixels),
+ mNearPlane(near_plane),
+ mFarPlane(far_plane),
+ mFixedDistance(-1.f)
+{
+ if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
+ else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
+
+ if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
+ else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
+
+ if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
+ else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
+
+ if (mFarPlane < 0) { mFarPlane = DEFAULT_FAR_PLANE; }
+ else if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
+ else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
+
+ calculateFrustumPlanes();
+}
+
+
+
+// ---------------- LLCamera::setFoo() member functions ----------------
+
+void LLCamera::setView(F32 field_of_view)
+{
+ mView = field_of_view;
+ if (mView < MIN_FIELD_OF_VIEW) { mView = MIN_FIELD_OF_VIEW; }
+ else if (mView > MAX_FIELD_OF_VIEW) { mView = MAX_FIELD_OF_VIEW; }
+ calculateFrustumPlanes();
+}
+
+void LLCamera::setViewHeightInPixels(S32 height)
+{
+ mViewHeightInPixels = height;
+
+ // Don't really need to do this, but update the pixel meter ratio with it.
+ calculateFrustumPlanes();
+}
+
+void LLCamera::setAspect(F32 aspect_ratio)
+{
+ mAspect = aspect_ratio;
+ if (mAspect < MIN_ASPECT_RATIO) { mAspect = MIN_ASPECT_RATIO; }
+ else if (mAspect > MAX_ASPECT_RATIO) { mAspect = MAX_ASPECT_RATIO; }
+ calculateFrustumPlanes();
+}
+
+
+void LLCamera::setNear(F32 near_plane)
+{
+ mNearPlane = near_plane;
+ if (mNearPlane < MIN_NEAR_PLANE) { mNearPlane = MIN_NEAR_PLANE; }
+ else if (mNearPlane > MAX_NEAR_PLANE) { mNearPlane = MAX_NEAR_PLANE; }
+ calculateFrustumPlanes();
+}
+
+
+void LLCamera::setFar(F32 far_plane)
+{
+ mFarPlane = far_plane;
+ if (mFarPlane < MIN_FAR_PLANE) { mFarPlane = MIN_FAR_PLANE; }
+ else if (mFarPlane > MAX_FAR_PLANE) { mFarPlane = MAX_FAR_PLANE; }
+ calculateFrustumPlanes();
+}
+
+
+// ---------------- read/write to buffer ----------------
+
+size_t LLCamera::writeFrustumToBuffer(char *buffer) const
+{
+ memcpy(buffer, &mView, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mAspect, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mNearPlane, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(buffer, &mFarPlane, sizeof(F32));
+ return 4*sizeof(F32);
+}
+
+size_t LLCamera::readFrustumFromBuffer(const char *buffer)
+{
+ memcpy(&mView, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mAspect, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mNearPlane, buffer, sizeof(F32));
+ buffer += sizeof(F32);
+ memcpy(&mFarPlane, buffer, sizeof(F32));
+ return 4*sizeof(F32);
+}
+
+
+// ---------------- test methods ----------------
+
+int LLCamera::AABBInFrustum(const LLVector3 &center, const LLVector3& radius)
+{
+ static const LLVector3 scaler[] = {
+ LLVector3(-1,-1,-1),
+ LLVector3( 1,-1,-1),
+ LLVector3(-1, 1,-1),
+ LLVector3( 1, 1,-1),
+ LLVector3(-1,-1, 1),
+ LLVector3( 1,-1, 1),
+ LLVector3(-1, 1, 1),
+ LLVector3( 1, 1, 1)
+ };
+
+ U8 mask = 0;
+ S32 result = 2;
+
+ for (int i = 0; i < 6; i++)
+ {
+ mask = mAgentPlaneMask[i];
+ LLPlane p = mAgentPlanes[i];
+ LLVector3 n = LLVector3(p);
+ float d = p.mV[3];
+ LLVector3 rscale = radius.scaledVec(scaler[mask]);
+
+ LLVector3 minp = center - rscale;
+ LLVector3 maxp = center + rscale;
+
+ if (n * minp > -d)
+ {
+ return 0;
+ }
+
+ if (n * maxp > -d)
+ {
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+int LLCamera::sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius)
+{
+ LLVector3 dist = sphere_center-mFrustCenter;
+ float dsq = dist * dist;
+ float rsq = mFarPlane*0.5f + radius;
+ rsq *= rsq;
+
+ if (dsq < rsq)
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+// HACK: This version is still around because the version below doesn't work
+// unless the agent planes are initialized.
+// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
+// NOTE: 'center' is in absolute frame.
+int LLCamera::sphereInFrustumOld(const LLVector3 &sphere_center, const F32 radius) const
+{
+ // Returns 1 if sphere is in frustum, 0 if not.
+ // modified so that default view frust is along X with Z vertical
+ F32 x, y, z, rightDist, leftDist, topDist, bottomDist;
+
+ // Subtract the view position
+ //LLVector3 relative_center;
+ //relative_center = sphere_center - getOrigin();
+ LLVector3 rel_center(sphere_center);
+ rel_center -= mOrigin;
+
+ bool all_in = TRUE;
+
+ // Transform relative_center.x to camera frame
+ x = mXAxis * rel_center;
+ if (x < MIN_NEAR_PLANE - radius)
+ {
+ return 0;
+ }
+ else if (x < MIN_NEAR_PLANE + radius)
+ {
+ all_in = FALSE;
+ }
+
+ if (x > mFarPlane + radius)
+ {
+ return 0;
+ }
+ else if (x > mFarPlane - radius)
+ {
+ all_in = FALSE;
+ }
+
+ // Transform relative_center.y to camera frame
+ y = mYAxis * rel_center;
+
+ // distance to plane is the dot product of (x, y, 0) * plane_normal
+ rightDist = x * mLocalPlanes[PLANE_RIGHT][VX] + y * mLocalPlanes[PLANE_RIGHT][VY];
+ if (rightDist < -radius)
+ {
+ return 0;
+ }
+ else if (rightDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ leftDist = x * mLocalPlanes[PLANE_LEFT][VX] + y * mLocalPlanes[PLANE_LEFT][VY];
+ if (leftDist < -radius)
+ {
+ return 0;
+ }
+ else if (leftDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ // Transform relative_center.y to camera frame
+ z = mZAxis * rel_center;
+
+ topDist = x * mLocalPlanes[PLANE_TOP][VX] + z * mLocalPlanes[PLANE_TOP][VZ];
+ if (topDist < -radius)
+ {
+ return 0;
+ }
+ else if (topDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ bottomDist = x * mLocalPlanes[PLANE_BOTTOM][VX] + z * mLocalPlanes[PLANE_BOTTOM][VZ];
+ if (bottomDist < -radius)
+ {
+ return 0;
+ }
+ else if (bottomDist < radius)
+ {
+ all_in = FALSE;
+ }
+
+ if (all_in)
+ {
+ return 2;
+ }
+
+ return 1;
+}
+
+
+// HACK: This (presumably faster) version only currently works if you set up the
+// frustum planes using GL. At some point we should get those planes through another
+// mechanism, and then we can get rid of the "old" version above.
+
+// Return 1 if sphere is in frustum, 2 if fully in frustum, otherwise 0.
+// NOTE: 'center' is in absolute frame.
+int LLCamera::sphereInFrustum(const LLVector3 &sphere_center, const F32 radius) const
+{
+ // Returns 1 if sphere is in frustum, 0 if not.
+ int res = 2;
+ for (int i = 0; i < 6; i++)
+ {
+ float d = mAgentPlanes[i].dist(sphere_center);
+
+ if (d > radius)
+ {
+ return 0;
+ }
+
+ if (d > -radius)
+ {
+ res = 1;
+ }
+ }
+
+ return res;
+}
+
+
+// return height of a sphere of given radius, located at center, in pixels
+F32 LLCamera::heightInPixels(const LLVector3 &center, F32 radius ) const
+{
+ if (radius == 0.f) return 0.f;
+
+ // If height initialized
+ if (mViewHeightInPixels > -1)
+ {
+ // Convert sphere to coord system with 0,0,0 at camera
+ LLVector3 vec = center - mOrigin;
+
+ // Compute distance to sphere
+ F32 dist = vec.magVec();
+
+ // Calculate angle of whole object
+ F32 angle = 2.0f * (F32) atan2(radius, dist);
+
+ // Calculate fraction of field of view
+ F32 fraction_of_fov = angle / mView;
+
+ // Compute number of pixels tall, based on vertical field of view
+ return (fraction_of_fov * mViewHeightInPixels);
+ }
+ else
+ {
+ // return invalid height
+ return -1.0f;
+ }
+}
+
+// If pos is visible, return the distance from pos to the camera.
+// Use fudge distance to scale rad against top/bot/left/right planes
+// Otherwise, return -distance
+F32 LLCamera::visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
+{
+ if (mFixedDistance > 0)
+ {
+ return mFixedDistance;
+ }
+ LLVector3 dvec = pos - mOrigin;
+ // Check visibility
+ F32 dist = dvec.magVec();
+ if (dist > rad)
+ {
+ F32 dp,tdist;
+ dp = dvec * mXAxis;
+ if (dp < -rad)
+ return -dist;
+
+ rad *= fudgedist;
+ LLVector3 tvec(pos);
+ for (int p=0; p<PLANE_NUM; p++)
+ {
+ if (!(planemask & (1<<p)))
+ continue;
+ tdist = -(mWorldPlanes[p].dist(tvec));
+ if (tdist > rad)
+ return -dist;
+ }
+ }
+ return dist;
+}
+
+// Like visibleDistance, except uses mHorizPlanes[], which are left and right
+// planes perpindicular to (0,0,1) in world space
+F32 LLCamera::visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgedist, U32 planemask) const
+{
+ if (mFixedDistance > 0)
+ {
+ return mFixedDistance;
+ }
+ LLVector3 dvec = pos - mOrigin;
+ // Check visibility
+ F32 dist = dvec.magVec();
+ if (dist > rad)
+ {
+ rad *= fudgedist;
+ LLVector3 tvec(pos);
+ for (int p=0; p<HORIZ_PLANE_NUM; p++)
+ {
+ if (!(planemask & (1<<p)))
+ continue;
+ F32 tdist = -(mHorizPlanes[p].dist(tvec));
+ if (tdist > rad)
+ return -dist;
+ }
+ }
+ return dist;
+}
+
+// ---------------- friends and operators ----------------
+
+std::ostream& operator<<(std::ostream &s, const LLCamera &C)
+{
+ s << "{ \n";
+ s << " Center = " << C.getOrigin() << "\n";
+ s << " AtAxis = " << C.getXAxis() << "\n";
+ s << " LeftAxis = " << C.getYAxis() << "\n";
+ s << " UpAxis = " << C.getZAxis() << "\n";
+ s << " View = " << C.getView() << "\n";
+ s << " Aspect = " << C.getAspect() << "\n";
+ s << " NearPlane = " << C.mNearPlane << "\n";
+ s << " FarPlane = " << C.mFarPlane << "\n";
+ s << " TopPlane = " << C.mLocalPlanes[LLCamera::PLANE_TOP][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_TOP][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_TOP][VZ] << "\n";
+ s << " BottomPlane = " << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_BOTTOM][VZ] << "\n";
+ s << " LeftPlane = " << C.mLocalPlanes[LLCamera::PLANE_LEFT][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_LEFT][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_LEFT][VZ] << "\n";
+ s << " RightPlane = " << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VX] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VY] << " "
+ << C.mLocalPlanes[LLCamera::PLANE_RIGHT][VZ] << "\n";
+ s << "}";
+ return s;
+}
+
+
+
+// ---------------- private member functions ----------------
+
+void LLCamera::calculateFrustumPlanes()
+{
+ // The planes only change when any of the frustum descriptions change.
+ // They are not affected by changes of the position of the Frustum
+ // because they are known in the view frame and the position merely
+ // provides information on how to get from the absolute frame to the
+ // view frame.
+
+ F32 left,right,top,bottom;
+ top = mFarPlane * (F32)tanf(0.5f * mView);
+ bottom = -top;
+ left = top * mAspect;
+ right = -left;
+
+ calculateFrustumPlanes(left, right, top, bottom);
+}
+
+LLPlane planeFromPoints(LLVector3 p1, LLVector3 p2, LLVector3 p3)
+{
+ LLVector3 n = ((p2-p1)%(p3-p1));
+ n.normVec();
+
+ return LLPlane(p1, n);
+}
+
+
+void LLCamera::calcAgentFrustumPlanes(LLVector3* frust)
+{
+
+ for (int i = 0; i < 8; i++)
+ {
+ mAgentFrustum[i] = frust[i];
+ }
+
+ //frust contains the 8 points of the frustum, calculate 6 planes
+
+ //order of planes is important, keep most likely to fail in the front of the list
+
+ //near - frust[0], frust[1], frust[2]
+ mAgentPlanes[2] = planeFromPoints(frust[0], frust[1], frust[2]);
+
+ //far
+ mAgentPlanes[5] = planeFromPoints(frust[5], frust[4], frust[6]);
+
+ //left
+ mAgentPlanes[0] = planeFromPoints(frust[4], frust[0], frust[7]);
+
+ //right
+ mAgentPlanes[1] = planeFromPoints(frust[1], frust[5], frust[6]);
+
+ //top
+ mAgentPlanes[4] = planeFromPoints(frust[3], frust[2], frust[6]);
+
+ //bottom
+ mAgentPlanes[3] = planeFromPoints(frust[1], frust[0], frust[4]);
+
+ //cache plane octant facing mask for use in AABBInFrustum
+ for (int i = 0; i < 8; i++)
+ {
+ U8 mask = 0;
+ LLPlane p = mAgentPlanes[i];
+ LLVector3 n = LLVector3(p);
+
+ if (n.mV[0] >= 0)
+ {
+ mask |= 1;
+ }
+ if (n.mV[1] >= 0)
+ {
+ mask |= 2;
+ }
+ if (n.mV[2] >= 0)
+ {
+ mask |= 4;
+ }
+ mAgentPlaneMask[i] = mask;
+ }
+}
+
+void LLCamera::calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom)
+{
+ LLVector3 a, b, c;
+
+ // For each plane we need to define 3 points (LLVector3's) in camera view space.
+ // The order in which we pass the points to planeFromPoints() matters, because the
+ // plane normal has a degeneracy of 2; we want it pointing _into_ the frustum.
+
+ a.setVec(0.0f, 0.0f, 0.0f);
+ b.setVec(mFarPlane, right, top);
+ c.setVec(mFarPlane, right, bottom);
+ mLocalPlanes[PLANE_RIGHT].setVec(a, b, c);
+
+ c.setVec(mFarPlane, left, top);
+ mLocalPlanes[PLANE_TOP].setVec(a, c, b);
+
+ b.setVec(mFarPlane, left, bottom);
+ mLocalPlanes[PLANE_LEFT].setVec(a, b, c);
+
+ c.setVec(mFarPlane, right, bottom);
+ mLocalPlanes[PLANE_BOTTOM].setVec( a, c, b);
+
+ //calculate center and radius squared of frustum in world absolute coordinates
+ mFrustCenter = X_AXIS*mFarPlane*0.5f;
+ mFrustCenter = transformToAbsolute(mFrustCenter);
+ mFrustRadiusSquared = mFarPlane*0.5f;
+ mFrustRadiusSquared *= mFrustRadiusSquared * 1.05f; //pad radius squared by 5%
+}
+
+// x and y are in WINDOW space, so x = Y-Axis (left/right), y= Z-Axis(Up/Down)
+void LLCamera::calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2)
+{
+ F32 bottom, top, left, right;
+ F32 view_height = (F32)tanf(0.5f * mView) * mFarPlane;
+ F32 view_width = view_height * mAspect;
+
+ left = x1 * -2.f * view_width;
+ right = x2 * -2.f * view_width;
+ bottom = y1 * 2.f * view_height;
+ top = y2 * 2.f * view_height;
+
+ calculateFrustumPlanes(left, right, top, bottom);
+}
+
+void LLCamera::calculateWorldFrustumPlanes()
+{
+ F32 d;
+ LLVector3 center = mOrigin - mXAxis*mNearPlane;
+ mWorldPlanePos = center;
+ for (int p=0; p<4; p++)
+ {
+ LLVector3 pnorm = LLVector3(mLocalPlanes[p]);
+ LLVector3 norm = rotateToAbsolute(pnorm);
+ norm.normVec();
+ d = -(center * norm);
+ mWorldPlanes[p] = LLPlane(norm, d);
+ }
+ // horizontal planes, perpindicular to (0,0,1);
+ LLVector3 zaxis(0, 0, 1.0f);
+ F32 yaw = getYaw();
+ {
+ LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_LEFT]);
+ tnorm.rotVec(yaw, zaxis);
+ d = -(mOrigin * tnorm);
+ mHorizPlanes[HORIZ_PLANE_LEFT] = LLPlane(tnorm, d);
+ }
+ {
+ LLVector3 tnorm = LLVector3(mLocalPlanes[PLANE_RIGHT]);
+ tnorm.rotVec(yaw, zaxis);
+ d = -(mOrigin * tnorm);
+ mHorizPlanes[HORIZ_PLANE_RIGHT] = LLPlane(tnorm, d);
+ }
+}
+
+// NOTE: this is the OpenGL matrix that will transform the default OpenGL view
+// (-Z=at, Y=up) to the default view of the LLCamera class (X=at, Z=up):
+//
+// F32 cfr_transform = { 0.f, 0.f, -1.f, 0.f, // -Z becomes X
+// -1.f, 0.f, 0.f, 0.f, // -X becomes Y
+// 0.f, 1.f, 0.f, 0.f, // Y becomes Z
+// 0.f, 0.f, 0.f, 1.f };
diff --git a/indra/llmath/llcamera.h b/indra/llmath/llcamera.h
new file mode 100644
index 0000000000..685f919ac4
--- /dev/null
+++ b/indra/llmath/llcamera.h
@@ -0,0 +1,169 @@
+/**
+ * @file llcamera.h
+ * @brief Header file for the LLCamera class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_CAMERA_H
+#define LL_CAMERA_H
+
+
+#include "llmath.h"
+#include "llcoordframe.h"
+#include "llplane.h"
+
+const F32 DEFAULT_FIELD_OF_VIEW = 60.f * DEG_TO_RAD;
+const F32 DEFAULT_ASPECT_RATIO = 640.f / 480.f;
+const F32 DEFAULT_NEAR_PLANE = 0.25f;
+const F32 DEFAULT_FAR_PLANE = 64.f; // far reaches across two horizontal, not diagonal, regions
+
+const F32 MAX_FIELD_OF_VIEW = F_PI;
+const F32 MAX_ASPECT_RATIO = 50.0f;
+const F32 MAX_NEAR_PLANE = 10.f;
+const F32 MAX_FAR_PLANE = 100000.0f; //1000000.0f; // Max allowed. Not good Z precision though.
+const F32 MAX_FAR_CLIP = 1024.0f;
+
+const F32 MIN_FIELD_OF_VIEW = 0.1f;
+const F32 MIN_ASPECT_RATIO = 0.02f;
+const F32 MIN_NEAR_PLANE = 0.1f;
+const F32 MIN_FAR_PLANE = 0.2f;
+
+static const LLVector3 X_AXIS(1.f,0.f,0.f);
+static const LLVector3 Y_AXIS(0.f,1.f,0.f);
+static const LLVector3 Z_AXIS(0.f,0.f,1.f);
+
+static const LLVector3 NEG_X_AXIS(-1.f,0.f,0.f);
+static const LLVector3 NEG_Y_AXIS(0.f,-1.f,0.f);
+static const LLVector3 NEG_Z_AXIS(0.f,0.f,-1.f);
+
+
+// An LLCamera is an LLCoorFrame with a view frustum.
+// This means that it has several methods for moving it around
+// that are inherited from the LLCoordFrame() class :
+//
+// setOrigin(), setAxes()
+// translate(), rotate()
+// roll(), pitch(), yaw()
+// etc...
+
+
+class LLCamera
+: public LLCoordFrame
+{
+public:
+ enum {
+ PLANE_LEFT = 0,
+ PLANE_RIGHT = 1,
+ PLANE_BOTTOM = 2,
+ PLANE_TOP = 3,
+ PLANE_NUM = 4
+ };
+ enum {
+ PLANE_LEFT_MASK = (1<<PLANE_LEFT),
+ PLANE_RIGHT_MASK = (1<<PLANE_RIGHT),
+ PLANE_BOTTOM_MASK = (1<<PLANE_BOTTOM),
+ PLANE_TOP_MASK = (1<<PLANE_TOP),
+ PLANE_ALL_MASK = 0xf
+ };
+ enum {
+ HORIZ_PLANE_LEFT = 0,
+ HORIZ_PLANE_RIGHT = 1,
+ HORIZ_PLANE_NUM = 2
+ };
+ enum {
+ HORIZ_PLANE_LEFT_MASK = (1<<HORIZ_PLANE_LEFT),
+ HORIZ_PLANE_RIGHT_MASK = (1<<HORIZ_PLANE_RIGHT),
+ HORIZ_PLANE_ALL_MASK = 0x3
+ };
+
+protected:
+ F32 mView; // angle between top and bottom frustum planes in radians.
+ F32 mAspect; // width/height
+ S32 mViewHeightInPixels; // for ViewHeightInPixels() only
+ F32 mNearPlane;
+ F32 mFarPlane;
+ LLPlane mLocalPlanes[4];
+ F32 mFixedDistance; // Always return this distance, unless < 0
+ LLVector3 mFrustCenter; // center of frustum and radius squared for ultra-quick exclusion test
+ F32 mFrustRadiusSquared;
+
+ LLPlane mWorldPlanes[PLANE_NUM];
+ LLPlane mHorizPlanes[HORIZ_PLANE_NUM];
+ LLPlane mAgentPlanes[6]; //frustum in agent space a la gluUnproject (I'm a bastard, I know) - DaveP
+ U8 mAgentPlaneMask[6];
+ LLVector3 mWorldPlanePos; // Position of World Planes (may be offset from camera)
+public:
+ LLVector3 mAgentFrustum[8];
+
+public:
+ LLCamera();
+ LLCamera(F32 z_field_of_view, F32 aspect_ratio, S32 view_height_in_pixels, F32 near_plane, F32 far_plane);
+
+ void setView(F32 new_view);
+ void setViewHeightInPixels(S32 height);
+ void setAspect(F32 new_aspect);
+ void setNear(F32 new_near);
+ void setFar(F32 new_far);
+
+ F32 getView() const { return mView; } // vertical FOV in radians
+ S32 getViewHeightInPixels() const { return mViewHeightInPixels; }
+ F32 getAspect() const { return mAspect; } // width / height
+ F32 getNear() const { return mNearPlane; } // meters
+ F32 getFar() const { return mFarPlane; } // meters
+
+ F32 getYaw() const
+ {
+ return atan2f(mXAxis[VY], mXAxis[VX]);
+ }
+ F32 getPitch() const
+ {
+ F32 xylen = sqrtf(mXAxis[VX]*mXAxis[VX] + mXAxis[VY]*mXAxis[VY]);
+ return atan2f(mXAxis[VZ], xylen);
+ }
+
+ const LLPlane& getWorldPlane(S32 index) const { return mWorldPlanes[index]; }
+ const LLVector3& getWorldPlanePos() const { return mWorldPlanePos; }
+
+ // Copy mView, mAspect, mNearPlane, and mFarPlane to buffer.
+ // Return number of bytes copied.
+ size_t writeFrustumToBuffer(char *buffer) const;
+
+ // Copy mView, mAspect, mNearPlane, and mFarPlane from buffer.
+ // Return number of bytes copied.
+ size_t readFrustumFromBuffer(const char *buffer);
+ void calcAgentFrustumPlanes(LLVector3* frust);
+ // Returns 1 if partly in, 2 if fully in.
+ // NOTE: 'center' is in absolute frame.
+ S32 sphereInFrustumOld(const LLVector3 &center, const F32 radius) const;
+ S32 sphereInFrustum(const LLVector3 &center, const F32 radius) const;
+ S32 pointInFrustum(const LLVector3 &point) const { return sphereInFrustum(point, 0.0f); }
+ S32 sphereInFrustumFull(const LLVector3 &center, const F32 radius) const { return sphereInFrustum(center, radius); }
+ S32 AABBInFrustum(const LLVector3 &center, const LLVector3& radius);
+ //does a quick 'n dirty sphere-sphere check
+ S32 sphereInFrustumQuick(const LLVector3 &sphere_center, const F32 radius);
+
+ // Returns height of object in pixels (must be height because field of view
+ // is based on window height).
+ F32 heightInPixels(const LLVector3 &center, F32 radius ) const;
+
+ // return the distance from pos to camera if visible (-distance if not visible)
+ F32 visibleDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = PLANE_ALL_MASK) const;
+ F32 visibleHorizDistance(const LLVector3 &pos, F32 rad, F32 fudgescale = 1.0f, U32 planemask = HORIZ_PLANE_ALL_MASK) const;
+ void setFixedDistance(F32 distance) { mFixedDistance = distance; }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLCamera &C);
+
+protected:
+ void calculateFrustumPlanes();
+ void calculateFrustumPlanes(F32 left, F32 right, F32 top, F32 bottom);
+ void calculateFrustumPlanesFromWindow(F32 x1, F32 y1, F32 x2, F32 y2);
+ void calculateWorldFrustumPlanes();
+};
+
+
+#endif
+
+
+
diff --git a/indra/llmath/llcoord.h b/indra/llmath/llcoord.h
new file mode 100644
index 0000000000..4eaf86e8b1
--- /dev/null
+++ b/indra/llmath/llcoord.h
@@ -0,0 +1,78 @@
+/**
+ * @file llcoord.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOORD_H
+#define LL_LLCOORD_H
+
+// A two-dimensional pixel value
+class LLCoord
+{
+public:
+ S32 mX;
+ S32 mY;
+
+ LLCoord(): mX(0), mY(0)
+ {}
+ LLCoord(S32 x, S32 y): mX(x), mY(y)
+ {}
+ virtual ~LLCoord()
+ {}
+
+ virtual void set(S32 x, S32 y) { mX = x; mY = y; }
+};
+
+
+// GL coordinates start in the client region of a window,
+// with left, bottom = 0, 0
+class LLCoordGL : public LLCoord
+{
+public:
+ LLCoordGL() : LLCoord()
+ {}
+ LLCoordGL(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+
+// Window coords include things like window borders,
+// menu regions, etc.
+class LLCoordWindow : public LLCoord
+{
+public:
+ LLCoordWindow() : LLCoord()
+ {}
+ LLCoordWindow(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+
+// Screen coords start at left, top = 0, 0
+class LLCoordScreen : public LLCoord
+{
+public:
+ LLCoordScreen() : LLCoord()
+ {}
+ LLCoordScreen(S32 x, S32 y) : LLCoord(x, y)
+ {}
+};
+
+class LLCoordFont : public LLCoord
+{
+public:
+ F32 mZ;
+
+ LLCoordFont() : LLCoord(), mZ(0.f)
+ {}
+ LLCoordFont(S32 x, S32 y, F32 z = 0) : LLCoord(x,y), mZ(z)
+ {}
+
+ void set(S32 x, S32 y) { LLCoord::set(x,y); mZ = 0.f; }
+ void set(S32 x, S32 y, F32 z) { mX = x; mY = y; mZ = z; }
+};
+
+
+#endif
diff --git a/indra/llmath/llcoordframe.cpp b/indra/llmath/llcoordframe.cpp
new file mode 100644
index 0000000000..c8b69e57cd
--- /dev/null
+++ b/indra/llmath/llcoordframe.cpp
@@ -0,0 +1,729 @@
+/**
+ * @file llcoordframe.cpp
+ * @brief LLCoordFrame class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "m3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "llcoordframe.h"
+
+#ifndef X_AXIS
+ #define X_AXIS 1.0f,0.0f,0.0f
+ #define Y_AXIS 0.0f,1.0f,0.0f
+ #define Z_AXIS 0.0f,0.0f,1.0f
+#endif
+
+// Constructors
+
+LLCoordFrame::LLCoordFrame() :
+ mOrigin(0.f, 0.f, 0.f),
+ mXAxis(X_AXIS),
+ mYAxis(Y_AXIS),
+ mZAxis(Z_AXIS)
+{
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin) :
+ mOrigin(origin),
+ mXAxis(X_AXIS),
+ mYAxis(Y_AXIS),
+ mZAxis(Z_AXIS)
+{
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLVector3 &direction) :
+ mOrigin(origin)
+{
+ lookDir(direction);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis) :
+ mOrigin(0.f, 0.f, 0.f),
+ mXAxis(x_axis),
+ mYAxis(y_axis),
+ mZAxis(z_axis)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin,
+ const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis) :
+ mOrigin(origin),
+ mXAxis(x_axis),
+ mYAxis(y_axis),
+ mZAxis(z_axis)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin,
+ const LLMatrix3 &rotation) :
+ mOrigin(origin),
+ mXAxis(rotation.mMatrix[VX]),
+ mYAxis(rotation.mMatrix[VY]),
+ mZAxis(rotation.mMatrix[VZ])
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLQuaternion &q) :
+ mOrigin(0.f, 0.f, 0.f)
+{
+ LLMatrix3 rotation_matrix(q);
+ mXAxis.setVec(rotation_matrix.mMatrix[VX]);
+ mYAxis.setVec(rotation_matrix.mMatrix[VY]);
+ mZAxis.setVec(rotation_matrix.mMatrix[VZ]);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLVector3 &origin, const LLQuaternion &q) :
+ mOrigin(origin)
+{
+ LLMatrix3 rotation_matrix(q);
+ mXAxis.setVec(rotation_matrix.mMatrix[VX]);
+ mYAxis.setVec(rotation_matrix.mMatrix[VY]);
+ mZAxis.setVec(rotation_matrix.mMatrix[VZ]);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+LLCoordFrame::LLCoordFrame(const LLMatrix4 &mat) :
+ mOrigin(mat.mMatrix[VW]),
+ mXAxis(mat.mMatrix[VX]),
+ mYAxis(mat.mMatrix[VY]),
+ mZAxis(mat.mMatrix[VZ])
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+
+
+// The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB
+/*
+LLCoordFrame::LLCoordFrame(const F32 *origin, const F32 *rotation) :
+ mOrigin(origin),
+ mXAxis(rotation+3*VX),
+ mYAxis(rotation+3*VY),
+ mZAxis(rotation+3*VZ)
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+*/
+
+/*
+LLCoordFrame::LLCoordFrame(const F32 *origin_and_rotation) :
+ mOrigin(origin_and_rotation),
+ mXAxis(origin_and_rotation + 3*(VX+1)),
+ mYAxis(origin_and_rotation + 3*(VY+1)),
+ mZAxis(origin_and_rotation + 3*(VZ+1))
+{
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::LLCoordFrame()" << llendl;
+ }
+}
+*/
+
+
+void LLCoordFrame::reset()
+{
+ mOrigin.setVec(0.0f, 0.0f, 0.0f);
+ resetAxes();
+}
+
+
+void LLCoordFrame::resetAxes()
+{
+ mXAxis.setVec(1.0f, 0.0f, 0.0f);
+ mYAxis.setVec(0.0f, 1.0f, 0.0f);
+ mZAxis.setVec(0.0f, 0.0f, 1.0f);
+}
+
+// setOrigin() member functions set mOrigin
+
+void LLCoordFrame::setOrigin(F32 x, F32 y, F32 z)
+{
+ mOrigin.setVec(x, y, z);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const LLVector3 &new_origin)
+{
+ mOrigin = new_origin;
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const F32 *origin)
+{
+ mOrigin.mV[VX] = *(origin + VX);
+ mOrigin.mV[VY] = *(origin + VY);
+ mOrigin.mV[VZ] = *(origin + VZ);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+void LLCoordFrame::setOrigin(const LLCoordFrame &frame)
+{
+ mOrigin = frame.getOrigin();
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setOrigin()" << llendl;
+ }
+}
+
+// setAxes() member functions set the axes, and assume that
+// the arguments are orthogonal and normalized.
+
+void LLCoordFrame::setAxes(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis)
+{
+ mXAxis = x_axis;
+ mYAxis = y_axis;
+ mZAxis = z_axis;
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes(const LLMatrix3 &rotation_matrix)
+{
+ mXAxis.setVec(rotation_matrix.mMatrix[VX]);
+ mYAxis.setVec(rotation_matrix.mMatrix[VY]);
+ mZAxis.setVec(rotation_matrix.mMatrix[VZ]);
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes(const LLQuaternion &q )
+{
+ LLMatrix3 rotation_matrix(q);
+ setAxes(rotation_matrix);
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes( const F32 *rotation_matrix )
+{
+ mXAxis.mV[VX] = *(rotation_matrix + 3*VX + VX);
+ mXAxis.mV[VY] = *(rotation_matrix + 3*VX + VY);
+ mXAxis.mV[VZ] = *(rotation_matrix + 3*VX + VZ);
+ mYAxis.mV[VX] = *(rotation_matrix + 3*VY + VX);
+ mYAxis.mV[VY] = *(rotation_matrix + 3*VY + VY);
+ mYAxis.mV[VZ] = *(rotation_matrix + 3*VY + VZ);
+ mZAxis.mV[VX] = *(rotation_matrix + 3*VZ + VX);
+ mZAxis.mV[VY] = *(rotation_matrix + 3*VZ + VY);
+ mZAxis.mV[VZ] = *(rotation_matrix + 3*VZ + VZ);
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::setAxes(const LLCoordFrame &frame)
+{
+ mXAxis = frame.getXAxis();
+ mYAxis = frame.getYAxis();
+ mZAxis = frame.getZAxis();
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::setAxes()" << llendl;
+ }
+}
+
+
+// translate() member functions move mOrigin to a relative position
+
+void LLCoordFrame::translate(F32 x, F32 y, F32 z)
+{
+ mOrigin.mV[VX] += x;
+ mOrigin.mV[VY] += y;
+ mOrigin.mV[VZ] += z;
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::translate(const LLVector3 &v)
+{
+ mOrigin += v;
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::translate(const F32 *origin)
+{
+ mOrigin.mV[VX] += *(origin + VX);
+ mOrigin.mV[VY] += *(origin + VY);
+ mOrigin.mV[VZ] += *(origin + VZ);
+
+ if( !mOrigin.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::translate()" << llendl;
+ }
+}
+
+
+// Rotate move the axes to a relative rotation
+
+void LLCoordFrame::rotate(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLQuaternion q(angle, LLVector3(x,y,z));
+ rotate(q);
+}
+
+
+void LLCoordFrame::rotate(F32 angle, const LLVector3 &rotation_axis)
+{
+ LLQuaternion q(angle, rotation_axis);
+ rotate(q);
+}
+
+
+void LLCoordFrame::rotate(const LLQuaternion &q)
+{
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+}
+
+
+void LLCoordFrame::rotate(const LLMatrix3 &rotation_matrix)
+{
+ mXAxis.rotVec(rotation_matrix);
+ mYAxis.rotVec(rotation_matrix);
+ orthonormalize();
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::rotate()" << llendl;
+ }
+}
+
+
+void LLCoordFrame::roll(F32 angle)
+{
+ LLQuaternion q(angle, mXAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mYAxis.isFinite() || !mZAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::roll()" << llendl;
+ }
+}
+
+void LLCoordFrame::pitch(F32 angle)
+{
+ LLQuaternion q(angle, mYAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mXAxis.isFinite() || !mZAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::pitch()" << llendl;
+ }
+}
+
+void LLCoordFrame::yaw(F32 angle)
+{
+ LLQuaternion q(angle, mZAxis);
+ LLMatrix3 rotation_matrix(q);
+ rotate(rotation_matrix);
+
+ if( !mXAxis.isFinite() || !mYAxis.isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::yaw()" << llendl;
+ }
+}
+
+// get*() routines
+
+
+LLQuaternion LLCoordFrame::getQuaternion() const
+{
+ LLQuaternion quat(mXAxis, mYAxis, mZAxis);
+ return quat;
+}
+
+
+void LLCoordFrame::getMatrixToLocal(LLMatrix4& mat) const
+{
+ mat.setFwdCol(mXAxis);
+ mat.setLeftCol(mYAxis);
+ mat.setUpCol(mZAxis);
+
+ mat.mMatrix[3][0] = -(mOrigin * LLVector3(mat.mMatrix[0][0], mat.mMatrix[1][0], mat.mMatrix[2][0]));
+ mat.mMatrix[3][1] = -(mOrigin * LLVector3(mat.mMatrix[0][1], mat.mMatrix[1][1], mat.mMatrix[2][1]));
+ mat.mMatrix[3][2] = -(mOrigin * LLVector3(mat.mMatrix[0][2], mat.mMatrix[1][2], mat.mMatrix[2][2]));
+}
+
+
+void LLCoordFrame::getRotMatrixToParent(LLMatrix4& mat) const
+{
+ // Note: moves into CFR
+ mat.setFwdRow( -mYAxis );
+ mat.setLeftRow( mZAxis );
+ mat.setUpRow( -mXAxis );
+}
+
+size_t LLCoordFrame::writeOrientation(char *buffer) const
+{
+ memcpy(buffer, mOrigin.mV, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mXAxis.mV, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mYAxis.mV, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(buffer, mZAxis.mV, 3*sizeof(F32));
+ return 12*sizeof(F32);
+}
+
+
+size_t LLCoordFrame::readOrientation(const char *buffer)
+{
+ memcpy(mOrigin.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mXAxis.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mYAxis.mV, buffer, 3*sizeof(F32));
+ buffer += 3*sizeof(F32);
+ memcpy(mZAxis.mV, buffer, 3*sizeof(F32));
+
+ if( !isFinite() )
+ {
+ llerrs << "Non Finite in LLCoordFrame::readOrientation()" << llendl;
+ }
+
+ return 12*sizeof(F32);
+}
+
+
+// rotation and transform vectors between reference frames
+
+LLVector3 LLCoordFrame::rotateToLocal(const LLVector3 &absolute_vector) const
+{
+ LLVector3 local_vector(mXAxis * absolute_vector,
+ mYAxis * absolute_vector,
+ mZAxis * absolute_vector);
+ return local_vector;
+}
+
+
+LLVector4 LLCoordFrame::rotateToLocal(const LLVector4 &absolute_vector) const
+{
+ LLVector4 local_vector;
+ local_vector.mV[VX] = mXAxis.mV[VX] * absolute_vector.mV[VX] +
+ mXAxis.mV[VY] * absolute_vector.mV[VY] +
+ mXAxis.mV[VZ] * absolute_vector.mV[VZ];
+ local_vector.mV[VY] = mYAxis.mV[VX] * absolute_vector.mV[VX] +
+ mYAxis.mV[VY] * absolute_vector.mV[VY] +
+ mYAxis.mV[VZ] * absolute_vector.mV[VZ];
+ local_vector.mV[VZ] = mZAxis.mV[VX] * absolute_vector.mV[VX] +
+ mZAxis.mV[VY] * absolute_vector.mV[VY] +
+ mZAxis.mV[VZ] * absolute_vector.mV[VZ];
+ local_vector.mV[VW] = absolute_vector.mV[VW];
+ return local_vector;
+}
+
+
+LLVector3 LLCoordFrame::rotateToAbsolute(const LLVector3 &local_vector) const
+{
+ LLVector3 absolute_vector;
+ absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] +
+ mYAxis.mV[VX] * local_vector.mV[VY] +
+ mZAxis.mV[VX] * local_vector.mV[VZ];
+ absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] +
+ mYAxis.mV[VY] * local_vector.mV[VY] +
+ mZAxis.mV[VY] * local_vector.mV[VZ];
+ absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] +
+ mYAxis.mV[VZ] * local_vector.mV[VY] +
+ mZAxis.mV[VZ] * local_vector.mV[VZ];
+ return absolute_vector;
+}
+
+
+LLVector4 LLCoordFrame::rotateToAbsolute(const LLVector4 &local_vector) const
+{
+ LLVector4 absolute_vector;
+ absolute_vector.mV[VX] = mXAxis.mV[VX] * local_vector.mV[VX] +
+ mYAxis.mV[VX] * local_vector.mV[VY] +
+ mZAxis.mV[VX] * local_vector.mV[VZ];
+ absolute_vector.mV[VY] = mXAxis.mV[VY] * local_vector.mV[VX] +
+ mYAxis.mV[VY] * local_vector.mV[VY] +
+ mZAxis.mV[VY] * local_vector.mV[VZ];
+ absolute_vector.mV[VZ] = mXAxis.mV[VZ] * local_vector.mV[VX] +
+ mYAxis.mV[VZ] * local_vector.mV[VY] +
+ mZAxis.mV[VZ] * local_vector.mV[VZ];
+ absolute_vector.mV[VW] = local_vector[VW];
+ return absolute_vector;
+}
+
+
+void LLCoordFrame::orthonormalize()
+// Makes sure the axes are orthogonal and normalized.
+{
+ mXAxis.normVec(); // X is renormalized
+ mYAxis -= mXAxis * (mXAxis * mYAxis); // Y remains in X-Y plane
+ mYAxis.normVec(); // Y is normalized
+ mZAxis = mXAxis % mYAxis; // Z = X cross Y
+}
+
+
+LLVector3 LLCoordFrame::transformToLocal(const LLVector3 &absolute_vector) const
+{
+ return rotateToLocal(absolute_vector - mOrigin);
+}
+
+
+LLVector4 LLCoordFrame::transformToLocal(const LLVector4 &absolute_vector) const
+{
+ LLVector4 local_vector(absolute_vector);
+ local_vector.mV[VX] -= mOrigin.mV[VX];
+ local_vector.mV[VY] -= mOrigin.mV[VY];
+ local_vector.mV[VZ] -= mOrigin.mV[VZ];
+ return rotateToLocal(local_vector);
+}
+
+
+LLVector3 LLCoordFrame::transformToAbsolute(const LLVector3 &local_vector) const
+{
+ return (rotateToAbsolute(local_vector) + mOrigin);
+}
+
+
+LLVector4 LLCoordFrame::transformToAbsolute(const LLVector4 &local_vector) const
+{
+ LLVector4 absolute_vector;
+ absolute_vector = rotateToAbsolute(local_vector);
+ absolute_vector.mV[VX] += mOrigin.mV[VX];
+ absolute_vector.mV[VY] += mOrigin.mV[VY];
+ absolute_vector.mV[VZ] += mOrigin.mV[VZ];
+ return absolute_vector;
+}
+
+
+// This is how you combine a translation and rotation of a
+// coordinate frame to get an OpenGL transformation matrix:
+//
+// translation * rotation = transformation matrix
+//
+// (i)->
+// (j)| 1 0 0 0 | | a d g 0 | | a d g 0 |
+// | | 0 1 0 0 | * | b e h 0 | = | b e h 0 |
+// V | 0 0 1 0 | | c f i 0 | | c f i 0 |
+// |-x -y -z 1 | | 0 0 0 1 | |-(ax+by+cz) -(dx+ey+fz) -(gx+hy+iz) 1 |
+//
+// where {a,b,c} = x-axis
+// {d,e,f} = y-axis
+// {g,h,i} = z-axis
+// {x,y,z} = origin
+
+void LLCoordFrame::getOpenGLTranslation(F32 *ogl_matrix) const
+{
+ *(ogl_matrix + 0) = 1.0f;
+ *(ogl_matrix + 1) = 0.0f;
+ *(ogl_matrix + 2) = 0.0f;
+ *(ogl_matrix + 3) = 0.0f;
+
+ *(ogl_matrix + 4) = 0.0f;
+ *(ogl_matrix + 5) = 1.0f;
+ *(ogl_matrix + 6) = 0.0f;
+ *(ogl_matrix + 7) = 0.0f;
+
+ *(ogl_matrix + 8) = 0.0f;
+ *(ogl_matrix + 9) = 0.0f;
+ *(ogl_matrix + 10) = 1.0f;
+ *(ogl_matrix + 11) = 0.0f;
+
+ *(ogl_matrix + 12) = -mOrigin.mV[VX];
+ *(ogl_matrix + 13) = -mOrigin.mV[VY];
+ *(ogl_matrix + 14) = -mOrigin.mV[VZ];
+ *(ogl_matrix + 15) = 1.0f;
+}
+
+
+void LLCoordFrame::getOpenGLRotation(F32 *ogl_matrix) const
+{
+ *(ogl_matrix + 0) = mXAxis.mV[VX];
+ *(ogl_matrix + 4) = mXAxis.mV[VY];
+ *(ogl_matrix + 8) = mXAxis.mV[VZ];
+
+ *(ogl_matrix + 1) = mYAxis.mV[VX];
+ *(ogl_matrix + 5) = mYAxis.mV[VY];
+ *(ogl_matrix + 9) = mYAxis.mV[VZ];
+
+ *(ogl_matrix + 2) = mZAxis.mV[VX];
+ *(ogl_matrix + 6) = mZAxis.mV[VY];
+ *(ogl_matrix + 10) = mZAxis.mV[VZ];
+
+ *(ogl_matrix + 3) = 0.0f;
+ *(ogl_matrix + 7) = 0.0f;
+ *(ogl_matrix + 11) = 0.0f;
+
+ *(ogl_matrix + 12) = 0.0f;
+ *(ogl_matrix + 13) = 0.0f;
+ *(ogl_matrix + 14) = 0.0f;
+ *(ogl_matrix + 15) = 1.0f;
+}
+
+
+void LLCoordFrame::getOpenGLTransform(F32 *ogl_matrix) const
+{
+ *(ogl_matrix + 0) = mXAxis.mV[VX];
+ *(ogl_matrix + 4) = mXAxis.mV[VY];
+ *(ogl_matrix + 8) = mXAxis.mV[VZ];
+ *(ogl_matrix + 12) = -mOrigin * mXAxis;
+
+ *(ogl_matrix + 1) = mYAxis.mV[VX];
+ *(ogl_matrix + 5) = mYAxis.mV[VY];
+ *(ogl_matrix + 9) = mYAxis.mV[VZ];
+ *(ogl_matrix + 13) = -mOrigin * mYAxis;
+
+ *(ogl_matrix + 2) = mZAxis.mV[VX];
+ *(ogl_matrix + 6) = mZAxis.mV[VY];
+ *(ogl_matrix + 10) = mZAxis.mV[VZ];
+ *(ogl_matrix + 14) = -mOrigin * mZAxis;
+
+ *(ogl_matrix + 3) = 0.0f;
+ *(ogl_matrix + 7) = 0.0f;
+ *(ogl_matrix + 11) = 0.0f;
+ *(ogl_matrix + 15) = 1.0f;
+}
+
+
+// at and up_direction are presumed to be normalized
+void LLCoordFrame::lookDir(const LLVector3 &at, const LLVector3 &up_direction)
+{
+ // Make sure 'at' and 'up_direction' are not parallel
+ // and that neither are zero-length vectors
+ LLVector3 left(up_direction % at);
+ if (left.isNull())
+ {
+ //tweak lookat pos so we don't get a degenerate matrix
+ LLVector3 tempat(at[VX] + 0.01f, at[VY], at[VZ]);
+ tempat.normVec();
+ left = (up_direction % tempat);
+ }
+ left.normVec();
+
+ LLVector3 up = at % left;
+ setAxes(at, left, up);
+}
+
+void LLCoordFrame::lookDir(const LLVector3 &xuv)
+{
+ static LLVector3 up_direction(0.0f, 0.0f, 1.0f);
+ lookDir(xuv, up_direction);
+}
+
+void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up_direction)
+{
+ setOrigin(origin);
+ LLVector3 at(point_of_interest - origin);
+ at.normVec();
+ lookDir(at, up_direction);
+}
+
+void LLCoordFrame::lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest)
+{
+ static LLVector3 up_direction(0.0f, 0.0f, 1.0f);
+
+ setOrigin(origin);
+ LLVector3 at(point_of_interest - origin);
+ at.normVec();
+ lookDir(at, up_direction);
+}
+
+
+// Operators and friends
+
+std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C)
+{
+ s << "{ "
+ << " origin = " << C.mOrigin
+ << " x_axis = " << C.mXAxis
+ << " y_axis = " << C.mYAxis
+ << " z_axis = " << C.mZAxis
+ << " }";
+ return s;
+}
+
+
+
+// Private member functions
+
+
+//EOF
diff --git a/indra/llmath/llcoordframe.h b/indra/llmath/llcoordframe.h
new file mode 100644
index 0000000000..ddd20c4491
--- /dev/null
+++ b/indra/llmath/llcoordframe.h
@@ -0,0 +1,156 @@
+/**
+ * @file llcoordframe.h
+ * @brief LLCoordFrame class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_COORDFRAME_H
+#define LL_COORDFRAME_H
+
+#include "v3math.h"
+#include "v4math.h"
+#include "llerror.h"
+
+// XXX : The constructors of the LLCoordFrame class assume that all vectors
+// and quaternion being passed as arguments are normalized, and all matrix
+// arguments are unitary. VERY BAD things will happen if these assumptions fail.
+// Also, segfault hazzards exist in methods that accept F32* arguments.
+
+
+class LLCoordFrame
+{
+public:
+ LLCoordFrame(); // Inits at zero with identity rotation
+ explicit LLCoordFrame(const LLVector3 &origin); // Sets origin, and inits rotation = Identity
+ LLCoordFrame(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis); // Sets coordinate axes and inits origin at zero
+ LLCoordFrame(const LLVector3 &origin,
+ const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis); // Sets the origin and coordinate axes
+ LLCoordFrame(const LLVector3 &origin,
+ const LLMatrix3 &rotation); // Sets axes to 3x3 matrix
+ LLCoordFrame(const LLVector3 &origin,
+ const LLVector3 &direction); // Sets origin and calls lookDir(direction)
+ explicit LLCoordFrame(const LLQuaternion &q); // Sets axes using q and inits mOrigin to zero
+ LLCoordFrame(const LLVector3 &origin,
+ const LLQuaternion &q); // Uses quaternion to init axes
+ explicit LLCoordFrame(const LLMatrix4 &mat); // Extracts frame from a 4x4 matrix
+ // The folowing two constructors are dangerous due to implicit casting and have been disabled - SJB
+ //LLCoordFrame(const F32 *origin, const F32 *rotation); // Assumes "origin" is 1x3 and "rotation" is 1x9 array
+ //LLCoordFrame(const F32 *origin_and_rotation); // Assumes "origin_and_rotation" is 1x12 array
+
+ BOOL isFinite() { return mOrigin.isFinite() && mXAxis.isFinite() && mYAxis.isFinite() && mZAxis.isFinite(); }
+
+ void reset();
+ void resetAxes();
+
+ void setOrigin(F32 x, F32 y, F32 z); // Set mOrigin
+ void setOrigin(const LLVector3 &origin);
+ void setOrigin(const F32 *origin);
+ void setOrigin(const LLCoordFrame &frame);
+
+ inline void setOriginX(F32 x) { mOrigin.mV[VX] = x; }
+ inline void setOriginY(F32 y) { mOrigin.mV[VY] = y; }
+ inline void setOriginZ(F32 z) { mOrigin.mV[VZ] = z; }
+
+ void setAxes(const LLVector3 &x_axis, // Set axes
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis);
+ void setAxes(const LLMatrix3 &rotation_matrix);
+ void setAxes(const LLQuaternion &q);
+ void setAxes(const F32 *rotation_matrix);
+ void setAxes(const LLCoordFrame &frame);
+
+ void translate(F32 x, F32 y, F32 z); // Move mOrgin
+ void translate(const LLVector3 &v);
+ void translate(const F32 *origin);
+
+ void rotate(F32 angle, F32 x, F32 y, F32 z); // Move axes
+ void rotate(F32 angle, const LLVector3 &rotation_axis);
+ void rotate(const LLQuaternion &q);
+ void rotate(const LLMatrix3 &m);
+
+ void orthonormalize(); // Makes sure axes are unitary and orthogonal.
+
+ // These methods allow rotations in the LLCoordFrame's frame
+ void roll(F32 angle); // RH rotation about mXAxis, radians
+ void pitch(F32 angle); // RH rotation about mYAxis, radians
+ void yaw(F32 angle); // RH rotation about mZAxis, radians
+
+ inline const LLVector3 &getOrigin() const { return mOrigin; }
+
+ inline const LLVector3 &getXAxis() const { return mXAxis; }
+ inline const LLVector3 &getYAxis() const { return mYAxis; }
+ inline const LLVector3 &getZAxis() const { return mZAxis; }
+
+ inline const LLVector3 &getAtAxis() const { return mXAxis; }
+ inline const LLVector3 &getLeftAxis() const { return mYAxis; }
+ inline const LLVector3 &getUpAxis() const { return mZAxis; }
+
+ // These return representations of the rotation or orientation of the LLFrame
+ // it its absolute frame. That is, these rotations acting on the X-axis {1,0,0}
+ // will produce the mXAxis.
+ // LLMatrix3 getMatrix3() const; // Returns axes in 3x3 matrix
+ LLQuaternion getQuaternion() const; // Returns axes in quaternion form
+
+ // Same as above, except it also includes the translation of the LLFrame
+ // LLMatrix4 getMatrix4() const; // Returns position and axes in 4x4 matrix
+
+ // Returns matrix which expresses point in local frame in the parent frame
+ void getMatrixToParent(LLMatrix4 &mat) const;
+ // Returns matrix which expresses point in parent frame in the local frame
+ void getMatrixToLocal(LLMatrix4 &mat) const; // Returns matrix which expresses point in parent frame in the local frame
+
+ void getRotMatrixToParent(LLMatrix4 &mat) const;
+
+ // Copies mOrigin, then the three axes to buffer, returns number of bytes copied.
+ size_t writeOrientation(char *buffer) const;
+
+ // Copies mOrigin, then the three axes from buffer, returns the number of bytes copied.
+ // Assumes the data in buffer is correct.
+ size_t readOrientation(const char *buffer);
+
+ LLVector3 rotateToLocal(const LLVector3 &v) const; // Returns v' rotated to local
+ LLVector4 rotateToLocal(const LLVector4 &v) const; // Returns v' rotated to local
+ LLVector3 rotateToAbsolute(const LLVector3 &v) const; // Returns v' rotated to absolute
+ LLVector4 rotateToAbsolute(const LLVector4 &v) const; // Returns v' rotated to absolute
+
+ LLVector3 transformToLocal(const LLVector3 &v) const; // Returns v' in local coord
+ LLVector4 transformToLocal(const LLVector4 &v) const; // Returns v' in local coord
+ LLVector3 transformToAbsolute(const LLVector3 &v) const; // Returns v' in absolute coord
+ LLVector4 transformToAbsolute(const LLVector4 &v) const; // Returns v' in absolute coord
+
+ // Write coord frame orientation into provided array in OpenGL matrix format.
+ void getOpenGLTranslation(F32 *ogl_matrix) const;
+ void getOpenGLRotation(F32 *ogl_matrix) const;
+ void getOpenGLTransform(F32 *ogl_matrix) const;
+
+ // lookDir orients to (xuv, presumed normalized) and does not affect origin
+ void lookDir(const LLVector3 &xuv, const LLVector3 &up);
+ void lookDir(const LLVector3 &xuv); // up = 0,0,1
+ // lookAt orients to (point_of_interest - origin) and sets origin
+ void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest, const LLVector3 &up);
+ void lookAt(const LLVector3 &origin, const LLVector3 &point_of_interest); // up = 0,0,1
+
+ // deprecated
+ void setOriginAndLookAt(const LLVector3 &origin, const LLVector3 &up, const LLVector3 &point_of_interest)
+ {
+ lookAt(origin, point_of_interest, up);
+ }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLCoordFrame &C);
+
+ // These vectors are in absolute frame
+ LLVector3 mOrigin;
+ LLVector3 mXAxis;
+ LLVector3 mYAxis;
+ LLVector3 mZAxis;
+};
+
+
+#endif
+
diff --git a/indra/llmath/llinterp.h b/indra/llmath/llinterp.h
new file mode 100644
index 0000000000..61bf1029fb
--- /dev/null
+++ b/indra/llmath/llinterp.h
@@ -0,0 +1,400 @@
+/**
+ * @file llinterp.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINTERP_H
+#define LL_LLINTERP_H
+
+#include "math.h"
+
+// Class from which different types of interpolators can be derived
+
+class LLInterpVal
+{
+public:
+ virtual ~LLInterpVal() {}
+ virtual void interp(LLInterpVal &target, const F32 frac); // Linear interpolation for each type
+};
+
+template <typename Type>
+class LLInterp
+{
+public:
+ LLInterp();
+ virtual ~LLInterp() {}
+
+ virtual void start();
+ void update(const F32 time);
+ const Type &getCurVal() const;
+
+ void setStartVal(const Type &start_val);
+ const Type &getStartVal() const;
+
+ void setEndVal(const Type &target_val);
+ const Type &getEndVal() const;
+
+ void setStartTime(const F32 time);
+ F32 getStartTime() const;
+
+ void setEndTime(const F32 time);
+ F32 getEndTime() const;
+
+ BOOL isActive() const;
+ BOOL isDone() const;
+
+protected:
+ F32 mStartTime;
+ F32 mEndTime;
+ F32 mDuration;
+ BOOL mActive;
+ BOOL mDone;
+
+ Type mStartVal;
+ Type mEndVal;
+
+ F32 mCurTime;
+ Type mCurVal;
+};
+
+template <typename Type>
+class LLInterpLinear : public LLInterp<Type>
+{
+public:
+ /*virtual*/ void start();
+ void update(const F32 time);
+ F32 getCurFrac() const;
+protected:
+ F32 mCurFrac;
+};
+
+template <typename Type>
+class LLInterpExp : public LLInterpLinear<Type>
+{
+public:
+ void update(const F32 time);
+protected:
+};
+
+template <typename Type>
+class LLInterpAttractor : public LLInterp<Type>
+{
+public:
+ LLInterpAttractor();
+ /*virtual*/ void start();
+ void setStartVel(const Type &vel);
+ void setForce(const F32 force);
+ void update(const F32 time);
+protected:
+ F32 mForce;
+ Type mStartVel;
+ Type mVelocity;
+};
+
+template <typename Type>
+class LLInterpFunc : public LLInterp<Type>
+{
+public:
+ LLInterpFunc();
+ void update(const F32 time);
+
+ void setFunc(Type (*)(const F32, void *data), void *data);
+protected:
+ Type (*mFunc)(const F32 time, void *data);
+ void *mData;
+};
+
+
+///////////////////////////////////
+//
+// Implementation
+//
+//
+
+/////////////////////////////////
+//
+// LLInterp base class implementation
+//
+
+template <typename Type>
+LLInterp<Type>::LLInterp()
+{
+ mStartTime = 0.f;
+ mEndTime = 1.f;
+ mDuration = 1.f;
+ mCurTime = 0.f;
+ mDone = FALSE;
+ mActive = FALSE;
+}
+
+template <class Type>
+void LLInterp<Type>::setStartVal(const Type &start_val)
+{
+ mStartVal = start_val;
+}
+
+template <class Type>
+void LLInterp<Type>::start()
+{
+ mCurVal = mStartVal;
+ mCurTime = mStartTime;
+ mDone = FALSE;
+ mActive = FALSE;
+}
+
+template <class Type>
+const Type &LLInterp<Type>::getStartVal() const
+{
+ return mStartVal;
+}
+
+template <class Type>
+void LLInterp<Type>::setEndVal(const Type &end_val)
+{
+ mEndVal = end_val;
+}
+
+template <class Type>
+const Type &LLInterp<Type>::getEndVal() const
+{
+ return mEndVal;
+}
+
+template <class Type>
+const Type &LLInterp<Type>::getCurVal() const
+{
+ return mCurVal;
+}
+
+
+template <class Type>
+void LLInterp<Type>::setStartTime(const F32 start_time)
+{
+ mStartTime = start_time;
+ mDuration = mEndTime - mStartTime;
+}
+
+template <class Type>
+F32 LLInterp<Type>::getStartTime() const
+{
+ return mStartTime;
+}
+
+
+template <class Type>
+void LLInterp<Type>::setEndTime(const F32 end_time)
+{
+ mEndTime = end_time;
+ mDuration = mEndTime - mStartTime;
+}
+
+
+template <class Type>
+F32 LLInterp<Type>::getEndTime() const
+{
+ return mEndTime;
+}
+
+
+template <class Type>
+BOOL LLInterp<Type>::isDone() const
+{
+ return mDone;
+}
+
+template <class Type>
+BOOL LLInterp<Type>::isActive() const
+{
+ return mActive;
+}
+
+//////////////////////////////
+//
+// LLInterpLinear derived class implementation.
+//
+template <typename Type>
+void LLInterpLinear<Type>::start()
+{
+ LLInterp<Type>::start();
+ mCurFrac = 0.f;
+}
+
+template <typename Type>
+void LLInterpLinear<Type>::update(const F32 time)
+{
+ F32 target_frac = (time - this->mStartTime) / this->mDuration;
+ F32 dfrac = target_frac - this->mCurFrac;
+ if (target_frac >= 0.f)
+ {
+ this->mActive = TRUE;
+ }
+
+ if (target_frac > 1.f)
+ {
+ this->mCurVal = this->mEndVal;
+ this->mCurFrac = 1.f;
+ this->mCurTime = time;
+ this->mDone = TRUE;
+ return;
+ }
+
+ target_frac = llmin(1.f, target_frac);
+ target_frac = llmax(0.f, target_frac);
+
+ if (dfrac >= 0.f)
+ {
+ F32 total_frac = 1.f - this->mCurFrac;
+ F32 inc_frac = dfrac / total_frac;
+ this->mCurVal = inc_frac * this->mEndVal + (1.f - inc_frac) * this->mCurVal;
+ this->mCurTime = time;
+ }
+ else
+ {
+ F32 total_frac = this->mCurFrac - 1.f;
+ F32 inc_frac = dfrac / total_frac;
+ this->mCurVal = inc_frac * this->mStartVal + (1.f - inc_frac) * this->mCurVal;
+ this->mCurTime = time;
+ }
+ mCurFrac = target_frac;
+}
+
+template <class Type>
+F32 LLInterpLinear<Type>::getCurFrac() const
+{
+ return mCurFrac;
+}
+
+
+//////////////////////////////
+//
+// LLInterpAttractor derived class implementation.
+//
+
+
+template <class Type>
+LLInterpAttractor<Type>::LLInterpAttractor() : LLInterp<Type>()
+{
+ mForce = 0.1f;
+ mVelocity *= 0.f;
+ mStartVel *= 0.f;
+}
+
+template <class Type>
+void LLInterpAttractor<Type>::start()
+{
+ LLInterp<Type>::start();
+ mVelocity = mStartVel;
+}
+
+
+template <class Type>
+void LLInterpAttractor<Type>::setStartVel(const Type &vel)
+{
+ mStartVel = vel;
+}
+
+template <class Type>
+void LLInterpAttractor<Type>::setForce(const F32 force)
+{
+ mForce = force;
+}
+
+template <class Type>
+void LLInterpAttractor<Type>::update(const F32 time)
+{
+ if (time > this->mStartTime)
+ {
+ this->mActive = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ if (time > this->mEndTime)
+ {
+ this->mDone = TRUE;
+ return;
+ }
+
+ F32 dt = time - this->mCurTime;
+ Type dist_val = this->mEndVal - this->mCurVal;
+ Type dv = 0.5*dt*dt*this->mForce*dist_val;
+ this->mVelocity += dv;
+ this->mCurVal += this->mVelocity * dt;
+ this->mCurTime = time;
+}
+
+
+//////////////////////////////
+//
+// LLInterpFucn derived class implementation.
+//
+
+
+template <class Type>
+LLInterpFunc<Type>::LLInterpFunc() : LLInterp<Type>()
+{
+ mFunc = NULL;
+ mData = NULL;
+}
+
+template <class Type>
+void LLInterpFunc<Type>::setFunc(Type (*func)(const F32, void *data), void *data)
+{
+ mFunc = func;
+ mData = data;
+}
+
+template <class Type>
+void LLInterpFunc<Type>::update(const F32 time)
+{
+ if (time > this->mStartTime)
+ {
+ this->mActive = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ if (time > this->mEndTime)
+ {
+ this->mDone = TRUE;
+ return;
+ }
+
+ this->mCurVal = (*mFunc)(time - this->mStartTime, mData);
+ this->mCurTime = time;
+}
+
+//////////////////////////////
+//
+// LLInterpExp derived class implementation.
+//
+
+template <class Type>
+void LLInterpExp<Type>::update(const F32 time)
+{
+ F32 target_frac = (time - this->mStartTime) / this->mDuration;
+ if (target_frac >= 0.f)
+ {
+ this->mActive = TRUE;
+ }
+
+ if (target_frac > 1.f)
+ {
+ this->mCurVal = this->mEndVal;
+ this->mCurFrac = 1.f;
+ this->mCurTime = time;
+ this->mDone = TRUE;
+ return;
+ }
+
+ this->mCurFrac = 1.f - (F32)(exp(-2.f*target_frac));
+ this->mCurVal = this->mStartVal + this->mCurFrac * (this->mEndVal - this->mStartVal);
+ this->mCurTime = time;
+}
+
+#endif // LL_LLINTERP_H
+
diff --git a/indra/llmath/llmath.h b/indra/llmath/llmath.h
new file mode 100644
index 0000000000..ad8ced9e1a
--- /dev/null
+++ b/indra/llmath/llmath.h
@@ -0,0 +1,402 @@
+/**
+ * @file llmath.h
+ * @brief Useful math constants and macros.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLMATH_H
+#define LLMATH_H
+
+#include <cmath>
+#include <math.h>
+#include <stdlib.h>
+
+#include "lldefs.h"
+
+// work around for Windows & older gcc non-standard function names.
+#if LL_WINDOWS
+#define llisnan(val) _isnan(val)
+#define llfinite(val) _finite(val)
+#elif (LL_LINUX && __GNUC__ <= 2)
+#define llisnan(val) isnan(val)
+#define llfinite(val) isfinite(val)
+#else
+#define llisnan(val) std::isnan(val)
+#define llfinite(val) std::isfinite(val)
+#endif
+
+// Single Precision Floating Point Routines
+#ifndef fsqrtf
+#define fsqrtf(x) ((F32)sqrt((F64)(x)))
+#endif
+#ifndef sqrtf
+#define sqrtf(x) ((F32)sqrt((F64)(x)))
+#endif
+
+#ifndef cosf
+#define cosf(x) ((F32)cos((F64)(x)))
+#endif
+#ifndef sinf
+#define sinf(x) ((F32)sin((F64)(x)))
+#endif
+#ifndef tanf
+#define tanf(x) ((F32)tan((F64)(x)))
+#endif
+#ifndef acosf
+#define acosf(x) ((F32)acos((F64)(x)))
+#endif
+
+#ifndef powf
+#define powf(x,y) ((F32)pow((F64)(x),(F64)(y)))
+#endif
+
+const F32 GRAVITY = -9.8f;
+
+// mathematical constants
+const F32 F_PI = 3.1415926535897932384626433832795f;
+const F32 F_TWO_PI = 6.283185307179586476925286766559f;
+const F32 F_PI_BY_TWO = 1.5707963267948966192313216916398f;
+const F32 F_SQRT2 = 1.4142135623730950488016887242097f;
+const F32 F_SQRT3 = 1.73205080756888288657986402541f;
+const F32 OO_SQRT2 = 0.7071067811865475244008443621049f;
+const F32 DEG_TO_RAD = 0.017453292519943295769236907684886f;
+const F32 RAD_TO_DEG = 57.295779513082320876798154814105f;
+const F32 F_APPROXIMATELY_ZERO = 0.00001f;
+const F32 F_LN2 = 0.69314718056f;
+const F32 OO_LN2 = 1.4426950408889634073599246810019f;
+
+// BUG: Eliminate in favor of F_APPROXIMATELY_ZERO above?
+const F32 FP_MAG_THRESHOLD = 0.0000001f;
+
+// TODO: Replace with logic like is_approx_equal
+inline BOOL is_approx_zero( F32 f ) { return (-F_APPROXIMATELY_ZERO < f) && (f < F_APPROXIMATELY_ZERO); }
+
+inline BOOL is_approx_equal(F32 x, F32 y)
+{
+ const S32 COMPARE_MANTISSA_UP_TO_BIT = 0x02;
+ return (abs((S32) ((U32&)x - (U32&)y) ) < COMPARE_MANTISSA_UP_TO_BIT);
+}
+
+inline S32 llabs(const S32 a)
+{
+ return S32(labs(a));
+}
+
+inline F32 llabs(const F32 a)
+{
+ return F32(fabs(a));
+}
+
+inline F64 llabs(const F64 a)
+{
+ return F64(fabs(a));
+}
+
+inline S32 lltrunc( F32 f )
+{
+#if LL_WINDOWS && !defined( __INTEL_COMPILER )
+ // Avoids changing the floating point control word.
+ // Add or subtract 0.5 - epsilon and then round
+ const static U32 zpfp[] = { 0xBEFFFFFF, 0x3EFFFFFF };
+ S32 result;
+ __asm {
+ fld f
+ mov eax, f
+ shr eax, 29
+ and eax, 4
+ fadd dword ptr [zpfp + eax]
+ fistp result
+ }
+ return result;
+#else
+ return (S32)f;
+#endif
+}
+
+inline S32 lltrunc( F64 f )
+{
+ return (S32)f;
+}
+
+inline S32 llfloor( F32 f )
+{
+#if LL_WINDOWS && !defined( __INTEL_COMPILER )
+ // Avoids changing the floating point control word.
+ // Accurate (unlike Stereopsis version) for all values between S32_MIN and S32_MAX and slightly faster than Stereopsis version.
+ // Add -(0.5 - epsilon) and then round
+ const U32 zpfp = 0xBEFFFFFF;
+ S32 result;
+ __asm {
+ fld f
+ fadd dword ptr [zpfp]
+ fistp result
+ }
+ return result;
+#else
+ return (S32)floor(f);
+#endif
+}
+
+
+inline S32 llceil( F32 f )
+{
+ // This could probably be optimized, but this works.
+ return (S32)ceil(f);
+}
+
+
+#ifndef BOGUS_ROUND
+// Use this round. Does an arithmetic round (0.5 always rounds up)
+inline S32 llround(const F32 val)
+{
+ 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
+ {
+ fld x
+ fadd st, st (0)
+ fadd round_to_nearest
+ fistp i
+ sar i, 1
+ }
+ return (i);
+}
+#endif // BOGUS_ROUND
+
+inline F32 llround( F32 val, F32 nearest )
+{
+ return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest;
+}
+
+inline F64 llround( F64 val, F64 nearest )
+{
+ return F64(floor(val * (1.0 / nearest) + 0.5)) * nearest;
+}
+
+// these provide minimum peak error
+//
+// avg error = -0.013049
+// peak error = -31.4 dB
+// RMS error = -28.1 dB
+
+const F32 FAST_MAG_ALPHA = 0.960433870103f;
+const F32 FAST_MAG_BETA = 0.397824734759f;
+
+// these provide minimum RMS error
+//
+// avg error = 0.000003
+// peak error = -32.6 dB
+// RMS error = -25.7 dB
+//
+//const F32 FAST_MAG_ALPHA = 0.948059448969f;
+//const F32 FAST_MAG_BETA = 0.392699081699f;
+
+inline F32 fastMagnitude(F32 a, F32 b)
+{
+ a = (a > 0) ? a : -a;
+ b = (b > 0) ? b : -b;
+ return(FAST_MAG_ALPHA * llmax(a,b) + FAST_MAG_BETA * llmin(a,b));
+}
+
+
+
+////////////////////
+//
+// Fast F32/S32 conversions
+//
+// Culled from www.stereopsis.com/FPU.html
+
+const F64 LL_DOUBLE_TO_FIX_MAGIC = 68719476736.0*1.5; //2^36 * 1.5, (52-_shiftamt=36) uses limited precisicion to floor
+const S32 LL_SHIFT_AMOUNT = 16; //16.16 fixed point representation,
+
+// Endian dependent code
+#ifdef LL_LITTLE_ENDIAN
+ #define LL_EXP_INDEX 1
+ #define LL_MAN_INDEX 0
+#else
+ #define LL_EXP_INDEX 0
+ #define LL_MAN_INDEX 1
+#endif
+
+/* Deprecated: use llround(), lltrunc(), or llfloor() instead
+// ================================================================================================
+// Real2Int
+// ================================================================================================
+inline S32 F64toS32(F64 val)
+{
+ val = val + LL_DOUBLE_TO_FIX_MAGIC;
+ return ((S32*)&val)[LL_MAN_INDEX] >> LL_SHIFT_AMOUNT;
+}
+
+// ================================================================================================
+// Real2Int
+// ================================================================================================
+inline S32 F32toS32(F32 val)
+{
+ return F64toS32 ((F64)val);
+}
+*/
+
+////////////////////////////////////////////////
+//
+// Fast exp and log
+//
+
+// Implementation of fast exp() approximation (from a paper by Nicol N. Schraudolph
+// http://www.inf.ethz.ch/~schraudo/pubs/exp.pdf
+static union
+{
+ double d;
+ struct
+ {
+#ifdef LL_LITTLE_ENDIAN
+ S32 j, i;
+#else
+ S32 i, j;
+#endif
+ } n;
+} LLECO; // not sure what the name means
+
+#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)
+
+
+
+inline F32 llfastpow(const F32 x, const F32 y)
+{
+ return (F32)(LL_FAST_EXP(y * log(x)));
+}
+
+
+inline F32 snap_to_sig_figs(F32 foo, S32 sig_figs)
+{
+ // compute the power of ten
+ F32 bar = 1.f;
+ for (S32 i = 0; i < sig_figs; i++)
+ {
+ bar *= 10.f;
+ }
+
+ foo = (F32)llround(foo * bar);
+
+ // shift back
+ foo /= bar;
+ return foo;
+}
+
+inline F32 lerp(F32 a, F32 b, F32 u)
+{
+ return a + ((b - a) * u);
+}
+
+inline F32 lerp2d(F32 x00, F32 x01, F32 x10, F32 x11, F32 u, F32 v)
+{
+ F32 a = x00 + (x01-x00)*u;
+ F32 b = x10 + (x11-x10)*u;
+ F32 r = a + (b-a)*v;
+ return r;
+}
+
+inline F32 ramp(F32 x, F32 a, F32 b)
+{
+ return (a == b) ? 0.0f : ((a - x) / (a - b));
+}
+
+inline F32 rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2)
+{
+ return lerp(y1, y2, ramp(x, x1, x2));
+}
+
+inline F32 clamp_rescale(F32 x, F32 x1, F32 x2, F32 y1, F32 y2)
+{
+ if (y1 < y2)
+ {
+ return llclamp(rescale(x,x1,x2,y1,y2),y1,y2);
+ }
+ else
+ {
+ return llclamp(rescale(x,x1,x2,y1,y2),y2,y1);
+ }
+}
+
+
+inline F32 cubic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 )
+{
+ if (x <= x0)
+ return s0;
+
+ if (x >= x1)
+ return s1;
+
+ F32 f = (x - x0) / (x1 - x0);
+
+ return s0 + (s1 - s0) * (f * f) * (3.0f - 2.0f * f);
+}
+
+inline F32 cubic_step( F32 x )
+{
+ x = llclampf(x);
+
+ return (x * x) * (3.0f - 2.0f * x);
+}
+
+inline F32 quadratic_step( F32 x, F32 x0, F32 x1, F32 s0, F32 s1 )
+{
+ if (x <= x0)
+ return s0;
+
+ if (x >= x1)
+ return s1;
+
+ F32 f = (x - x0) / (x1 - x0);
+ F32 f_squared = f * f;
+
+ return (s0 * (1.f - f_squared)) + ((s1 - s0) * f_squared);
+}
+
+inline F32 llsimple_angle(F32 angle)
+{
+ while(angle <= -F_PI)
+ angle += F_TWO_PI;
+ while(angle > F_PI)
+ angle -= F_TWO_PI;
+ return angle;
+}
+
+#endif
diff --git a/indra/llmath/lloctree.h b/indra/llmath/lloctree.h
new file mode 100644
index 0000000000..69ff43452c
--- /dev/null
+++ b/indra/llmath/lloctree.h
@@ -0,0 +1,693 @@
+/**
+ * @file lloctree.h
+ * @brief Octree declaration.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLOCTREE_H
+#define LL_LLOCTREE_H
+
+#include "lltreenode.h"
+#include "v3math.h"
+#include <vector>
+#include <set>
+
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+#define OCT_ERRS llwarns
+#else
+#define OCT_ERRS llerrs
+#endif
+
+#define LL_OCTREE_PARANOIA_CHECK 0
+
+template <class T> class LLOctreeState;
+template <class T> class LLOctreeNode;
+
+template <class T>
+class LLOctreeListener: public LLTreeListener<T>
+{
+public:
+ typedef LLTreeListener<T> BaseType;
+ typedef LLOctreeNode<T> oct_node;
+
+ virtual ~LLOctreeListener() { };
+ virtual void handleChildAddition(const oct_node* parent, oct_node* child) = 0;
+ virtual void handleChildRemoval(const oct_node* parent, const oct_node* child) = 0;
+};
+
+
+template <class T>
+class LLOctreeNode : public LLTreeNode<T>
+{
+public:
+
+ typedef LLTreeNode<T> BaseType;
+ typedef LLTreeState<T> tree_state;
+ typedef LLOctreeState<T> oct_state;
+ typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeListener<T> oct_listener;
+
+ static const U8 OCTANT_POSITIVE_X = 0x01;
+ static const U8 OCTANT_POSITIVE_Y = 0x02;
+ static const U8 OCTANT_POSITIVE_Z = 0x04;
+
+ LLOctreeNode( LLVector3d center,
+ LLVector3d size,
+ tree_state* state,
+ BaseType* parent,
+ U8 octant = 255)
+ : BaseType(state),
+ mParent((oct_node*)parent),
+ mCenter(center),
+ mSize(size),
+ mOctant(octant)
+ {
+ updateMinMax();
+ if ((mOctant == 255) && mParent)
+ {
+ mOctant = ((oct_node*) mParent)->getOctant(mCenter.mdV);
+ }
+ }
+
+ virtual ~LLOctreeNode() { BaseType::destroyListeners(); delete this->mState; }
+
+ virtual const BaseType* getParent() const { return mParent; }
+ virtual void setParent(BaseType* parent) { mParent = (oct_node*) parent; }
+ virtual const LLVector3d& getCenter() const { return mCenter; }
+ virtual const LLVector3d& getSize() const { return mSize; }
+ virtual void setCenter(LLVector3d center) { mCenter = center; }
+ virtual void setSize(LLVector3d size) { mSize = size; }
+ virtual bool balance() { return getOctState()->balance(); }
+ virtual void validate() { getOctState()->validate(); }
+ virtual U32 getChildCount() const { return getOctState()->getChildCount(); }
+ virtual oct_node* getChild(U32 index) { return getOctState()->getChild(index); }
+ virtual const oct_node* getChild(U32 index) const { return getOctState()->getChild(index); }
+ virtual U32 getElementCount() const { return getOctState()->getElementCount(); }
+ virtual void removeByAddress(T* data) { getOctState()->removeByAddress(data); }
+ virtual bool hasLeafState() const { return getOctState()->isLeaf(); }
+ virtual void destroy() { getOctState()->destroy(); }
+ virtual oct_node* getNodeAt(T* data) { return getOctState()->getNodeAt(data); }
+ virtual U8 getOctant() const { return mOctant; }
+ virtual void setOctant(U8 octant) { mOctant = octant; }
+ virtual const oct_state* getOctState() const { return (const oct_state*) BaseType::mState; }
+ virtual oct_state* getOctState() { return (oct_state*) BaseType::mState; }
+ virtual const oct_node* getOctParent() const { return (const oct_node*) getParent(); }
+ virtual oct_node* getOctParent() { return (oct_node*) getParent(); }
+ virtual void deleteChild(oct_node* child) { getOctState()->deleteChild(child); }
+
+ virtual U8 getOctant(const F64 pos[]) const //get the octant pos is in
+ {
+ U8 ret = 0;
+
+ if (pos[0] > mCenter.mdV[0])
+ {
+ ret |= OCTANT_POSITIVE_X;
+ }
+ if (pos[1] > mCenter.mdV[1])
+ {
+ ret |= OCTANT_POSITIVE_Y;
+ }
+ if (pos[2] > mCenter.mdV[2])
+ {
+ ret |= OCTANT_POSITIVE_Z;
+ }
+
+ return ret;
+ }
+
+ virtual bool isInside(T* data) const
+ {
+ return data->getBinRadius() <= mSize.mdV[0]*2.0 && isInside(data->getPositionGroup());
+ }
+
+ virtual bool isInside(const LLVector3d& pos) const
+ {
+ const F64& x = pos.mdV[0];
+ const F64& y = pos.mdV[1];
+ const F64& z = pos.mdV[2];
+
+ if (x > mMax.mdV[0] || x <= mMin.mdV[0] ||
+ y > mMax.mdV[1] || y <= mMin.mdV[1] ||
+ z > mMax.mdV[2] || z <= mMin.mdV[2])
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual void updateMinMax()
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ mMax.mdV[i] = mCenter.mdV[i] + mSize.mdV[i];
+ mMin.mdV[i] = mCenter.mdV[i] - mSize.mdV[i];
+ mCenter.mdV[i] = mCenter.mdV[i];
+ }
+ }
+
+ virtual oct_listener* getOctListener(U32 index)
+ {
+ return (oct_listener*) BaseType::getListener(index);
+ }
+
+ bool contains(T* xform)
+ {
+ if (mParent == NULL)
+ { //root node contains nothing
+ return false;
+ }
+
+ F64 size = mSize.mdV[0];
+ F64 p_size = size * 2.0;
+ F64 radius = xform->getBinRadius();
+
+ return (radius <= 0.001 && size <= 0.001) ||
+ (radius <= p_size && radius > size);
+ }
+
+ static void pushCenter(LLVector3d &center, LLVector3d &size, T* data)
+ {
+ LLVector3 pos(data->getPositionGroup());
+ F64 p[] =
+ {
+ (F64) pos.mV[0],
+ (F64) pos.mV[1],
+ (F64) pos.mV[2]
+ };
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (p[i] > center.mdV[i])
+ {
+ center.mdV[i] += size.mdV[i];
+ }
+ else
+ {
+ center.mdV[i] -= size.mdV[i];
+ }
+ }
+ }
+
+protected:
+ oct_node* mParent;
+ LLVector3d mCenter;
+ LLVector3d mSize;
+ LLVector3d mMax;
+ LLVector3d mMin;
+ U8 mOctant;
+};
+
+template <class T>
+class LLOctreeTraveler : public LLTreeTraveler<T>
+{
+public:
+ virtual void traverse(const LLTreeNode<T>* node);
+ virtual void visit(const LLTreeState<T>* state) { }
+ virtual void visit(const LLOctreeState<T>* branch) = 0;
+};
+
+//will pass requests to a child, might make a new child
+template <class T>
+class LLOctreeState : public LLTreeState<T>
+{
+public:
+ typedef LLTreeState<T> BaseType;
+ typedef LLOctreeTraveler<T> oct_traveler;
+ typedef LLOctreeNode<T> oct_node;
+ typedef LLOctreeListener<T> oct_listener;
+ typedef LLTreeTraveler<T> tree_traveler;
+ typedef typename std::set<LLPointer<T> > element_list;
+ typedef typename std::set<LLPointer<T> >::iterator element_iter;
+ typedef typename std::set<LLPointer<T> >::const_iterator const_element_iter;
+ typedef typename std::vector<LLTreeListener<T>*>::iterator tree_listener_iter;
+ typedef typename std::vector<LLOctreeNode<T>* > child_list;
+
+ LLOctreeState(oct_node* node = NULL): BaseType(node) { this->clearChildren(); }
+ virtual ~LLOctreeState()
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ delete getChild(i);
+ }
+ }
+
+
+ virtual void accept(oct_traveler* visitor) { visitor->visit(this); }
+ virtual bool isLeaf() const { return mChild.empty(); }
+
+ virtual U32 getElementCount() const { return mData.size(); }
+ virtual element_list& getData() { return mData; }
+ virtual const element_list& getData() const { return mData; }
+
+ virtual U32 getChildCount() const { return mChild.size(); }
+ virtual oct_node* getChild(U32 index) { return mChild[index]; }
+ virtual const oct_node* getChild(U32 index) const { return mChild[index]; }
+ virtual child_list& getChildren() { return mChild; }
+ virtual const child_list& getChildren() const { return mChild; }
+
+ virtual void accept(tree_traveler* visitor) const { visitor->visit(this); }
+ virtual void accept(oct_traveler* visitor) const { visitor->visit(this); }
+ const oct_node* getOctNode() const { return (const oct_node*) BaseType::getNode(); }
+ oct_node* getOctNode() { return (oct_node*) BaseType::getNode(); }
+
+ virtual oct_node* getNodeAt(T* data)
+ {
+ const LLVector3d& pos = data->getPositionGroup();
+ LLOctreeNode<T>* node = getOctNode();
+
+ if (node->isInside(data))
+ {
+ //do a quick search by octant
+ U8 octant = node->getOctant(pos.mdV);
+ BOOL keep_going = TRUE;
+
+ //traverse the tree until we find a node that has no node
+ //at the appropriate octant or is smaller than the object.
+ //by definition, that node is the smallest node that contains
+ // the data
+ while (keep_going && node->getSize().mdV[0] >= data->getBinRadius())
+ {
+ keep_going = FALSE;
+ for (U32 i = 0; i < node->getChildCount() && !keep_going; i++)
+ {
+ if (node->getChild(i)->getOctant() == octant)
+ {
+ node = node->getChild(i);
+ octant = node->getOctant(pos.mdV);
+ keep_going = TRUE;
+ }
+ }
+ }
+ }
+ else if (!node->contains(data) && node->getParent())
+ { //if we got here, data does not exist in this node
+ return ((LLOctreeNode<T>*) node->getParent())->getNodeAt(data);
+ }
+
+ return node;
+ }
+
+ virtual bool insert(T* data)
+ {
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE BRANCH !!!" << llendl;
+ return false;
+ }
+ LLOctreeNode<T>* node = getOctNode();
+
+ if (data->getBinRadius() <= node->getSize().mdV[0])
+ {
+ oct_node* dest = getNodeAt(data);
+
+ if (dest != node)
+ {
+ dest->insert(data);
+ return false;
+ }
+ }
+
+ //no kid found, is it even here?
+ if (node->isInside(data))
+ {
+ if (node->contains(data))
+ { //it belongs here
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE LEAF !!!" << llendl;
+ return false;
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ //if this is a redundant insertion, error out (should never happen)
+ if (mData.find(data) != mData.end())
+ {
+ llwarns << "Redundant octree insertion detected. " << data << llendl;
+ return false;
+ }
+#endif
+
+ mData.insert(data);
+ return true;
+ }
+ else
+ {
+ //it's here, but no kids are in the right place, make a new kid
+ LLVector3d center(node->getCenter());
+ LLVector3d size(node->getSize()*0.5);
+
+ //push center in direction of data
+ LLOctreeNode<T>::pushCenter(center, size, data);
+
+#if LL_OCTREE_PARANOIA_CHECK
+ if (getChildCount() == 8)
+ {
+ //this really isn't possible, something bad has happened
+ OCT_ERRS << "Octree detected floating point error and gave up." << llendl;
+ //bool check = node->isInside(data);
+ return false;
+ }
+
+ //make sure no existing node matches this position
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if (mChild[i]->getCenter() == center)
+ {
+ OCT_ERRS << "Octree detected duplicate child center and gave up." << llendl;
+ //bool check = node->isInside(data);
+ //check = getChild(i)->isInside(data);
+ return false;
+ }
+ }
+#endif
+
+ //make the new kid
+ LLOctreeState<T>* newstate = new LLOctreeState<T>();
+ oct_node* child = new LLOctreeNode<T>(center, size, newstate, node);
+ addChild(child);
+
+ child->insert(data);
+ }
+ }
+ else
+ {
+ //it's not in here, give it to the parent
+ node->getOctParent()->insert(data);
+ }
+
+ return false;
+ }
+
+ virtual bool remove(T* data)
+ {
+ oct_node* node = getOctNode();
+
+ if (mData.find(data) != mData.end())
+ { //we have data
+ mData.erase(data);
+ node->notifyRemoval(data);
+ checkAlive();
+ return true;
+ }
+ else if (node->isInside(data))
+ {
+ oct_node* dest = getNodeAt(data);
+
+ if (dest != node)
+ {
+ return dest->remove(data);
+ }
+ }
+
+ //SHE'S GONE MISSING...
+ //none of the children have it, let's just brute force this bastard out
+ //starting with the root node (UGLY CODE COMETH!)
+ oct_node* parent = node->getOctParent();
+ while (parent != NULL)
+ {
+ node = parent;
+ parent = node->getOctParent();
+ }
+
+ //node is now root
+ llwarns << "!!! OCTREE REMOVING FACE BY ADDRESS, SEVERE PERFORMANCE PENALTY |||" << llendl;
+ node->removeByAddress(data);
+ return true;
+ }
+
+ virtual void removeByAddress(T* data)
+ {
+ if (mData.find(data) != mData.end())
+ {
+ mData.erase(data);
+ getOctNode()->notifyRemoval(data);
+ llwarns << "FOUND!" << llendl;
+ checkAlive();
+ return;
+ }
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ { //we don't contain data, so pass this guy down
+ LLOctreeNode<T>* child = (LLOctreeNode<T>*) getChild(i);
+ child->removeByAddress(data);
+ }
+ }
+
+ virtual void clearChildren()
+ {
+ mChild.clear();
+ }
+
+ virtual void validate()
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ LLOctreeNode<T>* node = this->getOctNode();
+
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->validate();
+ if (mChild[i]->getParent() != node)
+ {
+ llerrs << "Octree child has invalid parent." << llendl;
+ }
+ }
+#endif
+ }
+
+ virtual bool balance()
+ {
+ return false;
+ }
+
+ virtual void destroy()
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ mChild[i]->destroy();
+ delete mChild[i];
+ }
+ }
+
+ virtual void addChild(oct_node* child, BOOL silent = FALSE)
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if(mChild[i]->getSize() != child->getSize())
+ {
+ OCT_ERRS <<"Invalid octree child size." << llendl;
+ }
+ if (mChild[i]->getCenter() == child->getCenter())
+ {
+ OCT_ERRS <<"Duplicate octree child position." << llendl;
+ }
+ }
+
+ if (mChild.size() >= 8)
+ {
+ OCT_ERRS <<"Octree node has too many children... why?" << llendl;
+ }
+#endif
+
+ mChild.push_back(child);
+ child->setParent(getOctNode());
+
+ if (!silent)
+ {
+ oct_node* node = getOctNode();
+
+ for (U32 i = 0; i < node->getListenerCount(); i++)
+ {
+ oct_listener* listener = node->getOctListener(i);
+ listener->handleChildAddition(node, child);
+ }
+ }
+ }
+
+ virtual void removeChild(U8 index, BOOL destroy = FALSE)
+ {
+ oct_node* node = getOctNode();
+ for (U32 i = 0; i < node->getListenerCount(); i++)
+ {
+ oct_listener* listener = node->getOctListener(i);
+ listener->handleChildRemoval(node, getChild(index));
+ }
+
+ if (destroy)
+ {
+ mChild[index]->destroy();
+ delete mChild[index];
+ }
+ mChild.erase(mChild.begin() + index);
+
+ checkAlive();
+ }
+
+ virtual void checkAlive()
+ {
+ if (getChildCount() == 0 && getElementCount() == 0)
+ {
+ oct_node* node = getOctNode();
+ oct_node* parent = node->getOctParent();
+ if (parent)
+ {
+ parent->deleteChild(node);
+ }
+ }
+ }
+
+ virtual void deleteChild(oct_node* node)
+ {
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ if (getChild(i) == node)
+ {
+ removeChild(i, TRUE);
+ return;
+ }
+ }
+
+ OCT_ERRS << "Octree failed to delete requested child." << llendl;
+ }
+
+protected:
+ child_list mChild;
+ element_list mData;
+};
+
+//just like a branch, except it might expand the node it points to
+template <class T>
+class LLOctreeRoot : public LLOctreeState<T>
+{
+public:
+ typedef LLOctreeState<T> BaseType;
+ typedef LLOctreeNode<T> oct_node;
+
+ LLOctreeRoot(oct_node* node = NULL) : BaseType(node) { }
+
+ oct_node* getOctNode() { return BaseType::getOctNode(); }
+ virtual bool isLeaf() { return false; }
+
+ virtual bool balance()
+ {
+ //the cached node might be invalid, so don't reference it
+ if (this->getChildCount() == 1 &&
+ !(this->mChild[0]->hasLeafState()) &&
+ this->mChild[0]->getElementCount() == 0)
+ { //if we have only one child and that child is an empty branch, make that child the root
+ BaseType* state = this->mChild[0]->getOctState();
+ oct_node* child = this->mChild[0];
+ oct_node* root = getOctNode();
+
+ //make the root node look like the child
+ root->setCenter(this->mChild[0]->getCenter());
+ root->setSize(this->mChild[0]->getSize());
+ root->updateMinMax();
+
+ //reset root node child list
+ this->clearChildren();
+
+ //copy the child's children into the root node silently
+ //(don't notify listeners of addition)
+ for (U32 i = 0; i < state->getChildCount(); i++)
+ {
+ addChild(state->getChild(i), TRUE);
+ }
+
+ //destroy child
+ state->clearChildren();
+ delete child;
+ }
+
+ return true;
+ }
+
+ // LLOctreeRoot::insert
+ virtual bool insert(T* data)
+ {
+ if (data == NULL)
+ {
+ OCT_ERRS << "!!! INVALID ELEMENT ADDED TO OCTREE ROOT !!!" << llendl;
+ return false;
+ }
+
+ if (data->getBinRadius() > 4096.0)
+ {
+ OCT_ERRS << "!!! ELEMENT EXCEDES MAXIMUM SIZE IN OCTREE ROOT !!!" << llendl;
+ }
+
+ LLOctreeNode<T>* node = getOctNode();
+ if (node->getSize().mdV[0] > data->getBinRadius() && node->isInside(data->getPositionGroup()))
+ {
+ //we got it, just act like a branch
+ LLOctreeState<T>::insert(data);
+ }
+ else if (this->getChildCount() == 0)
+ {
+ //first object being added, just wrap it up
+ while (!(node->getSize().mdV[0] > data->getBinRadius() && node->isInside(data->getPositionGroup())))
+ {
+ LLVector3d center, size;
+ center = node->getCenter();
+ size = node->getSize();
+ LLOctreeNode<T>::pushCenter(center, size, data);
+ node->setCenter(center);
+ node->setSize(size*2);
+ node->updateMinMax();
+ }
+ LLOctreeState<T>::insert(data);
+ }
+ else
+ {
+ //the data is outside the root node, we need to grow
+ LLVector3d center(node->getCenter());
+ LLVector3d size(node->getSize());
+
+ //expand this node
+ LLVector3d newcenter(center);
+ LLOctreeNode<T>::pushCenter(newcenter, size, data);
+ node->setCenter(newcenter);
+ node->setSize(size*2);
+ node->updateMinMax();
+
+ //copy our children to a new branch
+ LLOctreeState<T>* newstate = new LLOctreeState<T>();
+ LLOctreeNode<T>* newnode = new LLOctreeNode<T>(center, size, newstate, node);
+
+ for (U32 i = 0; i < this->getChildCount(); i++)
+ {
+ LLOctreeNode<T>* child = this->getChild(i);
+ newstate->addChild(child);
+ }
+
+ //clear our children and add the root copy
+ this->clearChildren();
+ addChild(newnode);
+
+ //insert the data
+ node->insert(data);
+ }
+
+ return false;
+ }
+};
+
+
+//========================
+// LLOctreeTraveler
+//========================
+template <class T>
+void LLOctreeTraveler<T>::traverse(const LLTreeNode<T>* node)
+{
+ const LLOctreeState<T>* state = (const LLOctreeState<T>*) node->getState();
+ state->accept(this);
+ for (U32 i = 0; i < state->getChildCount(); i++)
+ {
+ traverse(state->getChild(i));
+ }
+}
+
+#endif
diff --git a/indra/llmath/llperlin.cpp b/indra/llmath/llperlin.cpp
new file mode 100644
index 0000000000..770d80a845
--- /dev/null
+++ b/indra/llmath/llperlin.cpp
@@ -0,0 +1,276 @@
+/**
+ * @file llperlin.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+
+#include "llperlin.h"
+
+#define B 0x100
+#define BM 0xff
+#define N 0x1000
+#define NF32 (4096.f)
+#define NP 12 /* 2^N */
+#define NM 0xfff
+
+static S32 p[B + B + 2];
+static F32 g3[B + B + 2][3];
+static F32 g2[B + B + 2][2];
+static F32 g1[B + B + 2];
+
+bool LLPerlinNoise::sInitialized = 0;
+
+static void normalize2(F32 v[2])
+{
+ F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+}
+
+static void normalize3(F32 v[3])
+{
+ F32 s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+ v[2] = v[2] * s;
+}
+
+static void fast_setup(F32 vec, U8 &b0, U8 &b1, F32 &r0, F32 &r1)
+{
+ S32 t_S32;
+
+ r1 = vec + NF32;
+ t_S32 = lltrunc(r1);
+ b0 = (U8)t_S32;
+ b1 = b0 + 1;
+ r0 = r1 - t_S32;
+ r1 = r0 - 1.f;
+}
+
+
+void LLPerlinNoise::init(void)
+{
+ int i, j, k;
+
+ for (i = 0 ; i < B ; i++)
+ {
+ p[i] = i;
+
+ g1[i] = (F32)((rand() % (B + B)) - B) / B;
+
+ for (j = 0 ; j < 2 ; j++)
+ g2[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize2(g2[i]);
+
+ for (j = 0 ; j < 3 ; j++)
+ g3[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize3(g3[i]);
+ }
+
+ while (--i)
+ {
+ k = p[i];
+ p[i] = p[j = rand() % B];
+ p[j] = k;
+ }
+
+ for (i = 0 ; i < B + 2 ; i++)
+ {
+ p[B + i] = p[i];
+ g1[B + i] = g1[i];
+ for (j = 0 ; j < 2 ; j++)
+ g2[B + i][j] = g2[i][j];
+ for (j = 0 ; j < 3 ; j++)
+ g3[B + i][j] = g3[i][j];
+ }
+
+ sInitialized = true;
+}
+
+
+//============================================================================
+// Noise functions
+
+#define s_curve(t) ( t * t * (3.f - 2.f * t) )
+
+#define lerp_m(t, a, b) ( a + t * (b - a) )
+
+F32 LLPerlinNoise::noise1(F32 x)
+{
+ int bx0, bx1;
+ F32 rx0, rx1, sx, t, u, v;
+
+ if (!sInitialized)
+ init();
+
+ t = x + N;
+ bx0 = (lltrunc(t)) & BM;
+ bx1 = (bx0+1) & BM;
+ rx0 = t - lltrunc(t);
+ rx1 = rx0 - 1.f;
+
+ sx = s_curve(rx0);
+
+ u = rx0 * g1[ p[ bx0 ] ];
+ v = rx1 * g1[ p[ bx1 ] ];
+
+ return lerp_m(sx, u, v);
+}
+
+static F32 fast_at2(F32 rx, F32 ry, F32 *q)
+{
+ return rx * q[0] + ry * q[1];
+}
+
+F32 LLPerlinNoise::noise2(F32 x, F32 y)
+{
+ U8 bx0, bx1, by0, by1;
+ U32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, *q, sx, sy, a, b, u, v;
+ S32 i, j;
+
+ if (!sInitialized)
+ init();
+
+ fast_setup(x, bx0, bx1, rx0, rx1);
+ fast_setup(y, by0, by1, ry0, ry1);
+
+ i = *(p + bx0);
+ j = *(p + bx1);
+
+ b00 = *(p + i + by0);
+ b10 = *(p + j + by0);
+ b01 = *(p + i + by1);
+ b11 = *(p + j + by1);
+
+ sx = s_curve(rx0);
+ sy = s_curve(ry0);
+
+
+ q = *(g2 + b00);
+ u = fast_at2(rx0, ry0, q);
+ q = *(g2 + b10);
+ v = fast_at2(rx1, ry0, q);
+ a = lerp_m(sx, u, v);
+
+ q = *(g2 + b01);
+ u = fast_at2(rx0,ry1,q);
+ q = *(g2 + b11);
+ v = fast_at2(rx1,ry1,q);
+ b = lerp_m(sx, u, v);
+
+ return lerp_m(sy, a, b);
+}
+
+static F32 fast_at3(F32 rx, F32 ry, F32 rz, F32 *q)
+{
+ return rx * q[0] + ry * q[1] + rz * q[2];
+}
+
+F32 LLPerlinNoise::noise3(F32 x, F32 y, F32 z)
+{
+ U8 bx0, bx1, by0, by1, bz0, bz1;
+ S32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
+ S32 i, j;
+
+ if (!sInitialized)
+ init();
+
+ fast_setup(x, bx0,bx1, rx0,rx1);
+ fast_setup(y, by0,by1, ry0,ry1);
+ fast_setup(z, bz0,bz1, rz0,rz1);
+
+ i = p[ bx0 ];
+ j = p[ bx1 ];
+
+ b00 = p[ i + by0 ];
+ b10 = p[ j + by0 ];
+ b01 = p[ i + by1 ];
+ b11 = p[ j + by1 ];
+
+ t = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+ q = g3[ b00 + bz0 ];
+ u = fast_at3(rx0,ry0,rz0,q);
+ q = g3[ b10 + bz0 ];
+ v = fast_at3(rx1,ry0,rz0,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz0 ];
+ u = fast_at3(rx0,ry1,rz0,q);
+ q = g3[ b11 + bz0 ];
+ v = fast_at3(rx1,ry1,rz0,q);
+ b = lerp_m(t, u, v);
+
+ c = lerp_m(sy, a, b);
+
+ q = g3[ b00 + bz1 ];
+ u = fast_at3(rx0,ry0,rz1,q);
+ q = g3[ b10 + bz1 ];
+ v = fast_at3(rx1,ry0,rz1,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz1 ];
+ u = fast_at3(rx0,ry1,rz1,q);
+ q = g3[ b11 + bz1 ];
+ v = fast_at3(rx1,ry1,rz1,q);
+ b = lerp_m(t, u, v);
+
+ d = lerp_m(sy, a, b);
+
+ return lerp_m(sz, c, d);
+}
+
+F32 LLPerlinNoise::turbulence2(F32 x, F32 y, F32 freq)
+{
+ F32 t, lx, ly;
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f)
+ {
+ lx = freq * x;
+ ly = freq * y;
+ t += noise2(lx, ly)/freq;
+ }
+ return t;
+}
+
+F32 LLPerlinNoise::turbulence3(F32 x, F32 y, F32 z, F32 freq)
+{
+ F32 t, lx, ly, lz;
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f)
+ {
+ lx = freq * x;
+ ly = freq * y;
+ lz = freq * z;
+ t += noise3(lx,ly,lz)/freq;
+// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq
+// t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq;
+ }
+ return t;
+}
+
+F32 LLPerlinNoise::clouds3(F32 x, F32 y, F32 z, F32 freq)
+{
+ F32 t, lx, ly, lz;
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f)
+ {
+ lx = freq * x;
+ ly = freq * y;
+ lz = freq * z;
+// t += noise3(lx,ly,lz)/freq;
+// t += fabs(noise3(lx,ly,lz)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(lx,ly,lz))) / freq; // Better at low freq
+ t += (noise3(lx,ly,lz)*noise3(lx,ly,lz)) / freq;
+ }
+ return t;
+}
diff --git a/indra/llmath/llperlin.h b/indra/llmath/llperlin.h
new file mode 100644
index 0000000000..75e38bb8b6
--- /dev/null
+++ b/indra/llmath/llperlin.h
@@ -0,0 +1,28 @@
+/**
+ * @file llperlin.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PERLIN_H
+#define LL_PERLIN_H
+
+#include "stdtypes.h"
+
+// namespace wrapper
+class LLPerlinNoise
+{
+public:
+ static F32 noise1(F32 x);
+ static F32 noise2(F32 x, F32 y);
+ static F32 noise3(F32 x, F32 y, F32 z);
+ static F32 turbulence2(F32 x, F32 y, F32 freq);
+ static F32 turbulence3(F32 x, F32 y, F32 z, F32 freq);
+ static F32 clouds3(F32 x, F32 y, F32 z, F32 freq);
+private:
+ static bool sInitialized;
+ static void init(void);
+};
+
+#endif // LL_PERLIN_
diff --git a/indra/llmath/llplane.h b/indra/llmath/llplane.h
new file mode 100644
index 0000000000..0db1d31d80
--- /dev/null
+++ b/indra/llmath/llplane.h
@@ -0,0 +1,49 @@
+/**
+ * @file llplane.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPLANE_H
+#define LL_LLPLANE_H
+
+#include "v3math.h"
+#include "v4math.h"
+
+// A simple way to specify a plane is to give its normal,
+// and it's nearest approach to the origin.
+//
+// Given the equation for a plane : A*x + B*y + C*z + D = 0
+// The plane normal = [A, B, C]
+// The closest approach = D / sqrt(A*A + B*B + C*C)
+
+class LLPlane : public LLVector4
+{
+public:
+ LLPlane() {}; // no default constructor
+ LLPlane(const LLVector3 &p0, F32 d) { setVec(p0, d); }
+ LLPlane(const LLVector3 &p0, const LLVector3 &n) { setVec(p0, n); }
+ void setVec(const LLVector3 &p0, F32 d) { LLVector4::setVec(p0[0], p0[1], p0[2], d); }
+ void setVec(const LLVector3 &p0, const LLVector3 &n)
+ {
+ F32 d = -(p0 * n);
+ setVec(n, d);
+ }
+ void setVec(const LLVector3 &p0, const LLVector3 &p1, const LLVector3 &p2)
+ {
+ LLVector3 u, v, w;
+ u = p1 - p0;
+ v = p2 - p0;
+ w = u % v;
+ w.normVec();
+ F32 d = -(w * p0);
+ setVec(w, d);
+ }
+ LLPlane& operator=(const LLVector4& v2) { LLVector4::setVec(v2[0],v2[1],v2[2],v2[3]); return *this;}
+ F32 dist(const LLVector3 &v2) const { return mV[0]*v2[0] + mV[1]*v2[1] + mV[2]*v2[2] + mV[3]; }
+};
+
+
+
+#endif // LL_LLPLANE_H
diff --git a/indra/llmath/llquantize.h b/indra/llmath/llquantize.h
new file mode 100644
index 0000000000..8aa03628f2
--- /dev/null
+++ b/indra/llmath/llquantize.h
@@ -0,0 +1,103 @@
+/**
+ * @file llquantize.h
+ * @brief useful routines for quantizing floats to various length ints
+ * and back out again
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUANTIZE_H
+#define LL_LLQUANTIZE_H
+
+const U16 U16MAX = 65535;
+const F32 OOU16MAX = 1.f/(F32)(U16MAX);
+
+const U8 U8MAX = 255;
+const F32 OOU8MAX = 1.f/(F32)(U8MAX);
+
+const U8 FIRSTVALIDCHAR = 54;
+const U8 MAXSTRINGVAL = U8MAX - FIRSTVALIDCHAR; //we don't allow newline or null
+
+
+inline U16 F32_to_U16(F32 val, F32 lower, F32 upper)
+{
+ val = llclamp(val, lower, upper);
+ // make sure that the value is positive and normalized to <0, 1>
+ val -= lower;
+ val /= (upper - lower);
+
+ // return the U16
+ return (U16)(llfloor(val*U16MAX));
+}
+
+inline F32 U16_to_F32(U16 ival, F32 lower, F32 upper)
+{
+ F32 val = ival*OOU16MAX;
+ F32 delta = (upper - lower);
+ val *= delta;
+ val += lower;
+
+ F32 max_error = delta*OOU16MAX;
+
+ // make sure that zero's come through as zero
+ if (fabsf(val) < max_error)
+ val = 0.f;
+
+ return val;
+}
+
+inline U8 F32_to_U8(F32 val, F32 lower, F32 upper)
+{
+ val = llclamp(val, lower, upper);
+ // make sure that the value is positive and normalized to <0, 1>
+ val -= lower;
+ val /= (upper - lower);
+
+ // return the U8
+ return (U8)(llfloor(val*U8MAX));
+}
+
+inline F32 U8_to_F32(U8 ival, F32 lower, F32 upper)
+{
+ F32 val = ival*OOU8MAX;
+ F32 delta = (upper - lower);
+ val *= delta;
+ val += lower;
+
+ F32 max_error = delta*OOU8MAX;
+
+ // make sure that zero's come through as zero
+ if (fabsf(val) < max_error)
+ val = 0.f;
+
+ return val;
+}
+
+inline U8 F32_TO_STRING(F32 val, F32 lower, F32 upper)
+{
+ val = llclamp(val, lower, upper); //[lower, upper]
+ // make sure that the value is positive and normalized to <0, 1>
+ val -= lower; //[0, upper-lower]
+ val /= (upper - lower); //[0,1]
+ val = val * MAXSTRINGVAL; //[0, MAXSTRINGVAL]
+ val = floor(val + 0.5f); //[0, MAXSTRINGVAL]
+
+ U8 stringVal = (U8)(val) + FIRSTVALIDCHAR; //[FIRSTVALIDCHAR, MAXSTRINGVAL + FIRSTVALIDCHAR]
+ return stringVal;
+}
+
+inline F32 STRING_TO_F32(U8 ival, F32 lower, F32 upper)
+{
+ // remove empty space left for NULL, newline, etc.
+ ival -= FIRSTVALIDCHAR; //[0, MAXSTRINGVAL]
+
+ F32 val = (F32)ival * (1.f / (F32)MAXSTRINGVAL); //[0, 1]
+ F32 delta = (upper - lower);
+ val *= delta; //[0, upper - lower]
+ val += lower; //[lower, upper]
+
+ return val;
+}
+
+#endif
diff --git a/indra/llmath/llquaternion.cpp b/indra/llmath/llquaternion.cpp
new file mode 100644
index 0000000000..56a3830bb3
--- /dev/null
+++ b/indra/llmath/llquaternion.cpp
@@ -0,0 +1,830 @@
+/**
+ * @file qmath.cpp
+ * @brief LLQuaternion class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llquaternion.h"
+
+#include "llmath.h" // for F_PI
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquantize.h"
+
+// WARNING: Don't use this for global const definitions! using this
+// at the top of a *.cpp file might not give you what you think.
+const LLQuaternion LLQuaternion::DEFAULT;
+
+// Constructors
+
+LLQuaternion::LLQuaternion(const LLMatrix4 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(const LLMatrix3 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(F32 angle, const LLVector4 &vec)
+{
+ LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX] * s;
+ mQ[VY] = v.mV[VY] * s;
+ mQ[VZ] = v.mV[VZ] * s;
+ mQ[VW] = c;
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(F32 angle, const LLVector3 &vec)
+{
+ LLVector3 v(vec);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX] * s;
+ mQ[VY] = v.mV[VY] * s;
+ mQ[VZ] = v.mV[VZ] * s;
+ mQ[VW] = c;
+ normQuat();
+}
+
+LLQuaternion::LLQuaternion(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis)
+{
+ LLMatrix3 mat;
+ mat.setRows(x_axis, y_axis, z_axis);
+ *this = mat.quaternion();
+ normQuat();
+}
+
+// Quatizations
+void LLQuaternion::quantize16(F32 lower, F32 upper)
+{
+ F32 x = mQ[VX];
+ F32 y = mQ[VY];
+ F32 z = mQ[VZ];
+ F32 s = mQ[VS];
+
+ x = U16_to_F32(F32_to_U16(x, lower, upper), lower, upper);
+ y = U16_to_F32(F32_to_U16(y, lower, upper), lower, upper);
+ z = U16_to_F32(F32_to_U16(z, lower, upper), lower, upper);
+ s = U16_to_F32(F32_to_U16(s, lower, upper), lower, upper);
+
+ mQ[VX] = x;
+ mQ[VY] = y;
+ mQ[VZ] = z;
+ mQ[VS] = s;
+}
+
+void LLQuaternion::quantize8(F32 lower, F32 upper)
+{
+ mQ[VX] = U8_to_F32(F32_to_U8(mQ[VX], lower, upper), lower, upper);
+ mQ[VY] = U8_to_F32(F32_to_U8(mQ[VY], lower, upper), lower, upper);
+ mQ[VZ] = U8_to_F32(F32_to_U8(mQ[VZ], lower, upper), lower, upper);
+ mQ[VS] = U8_to_F32(F32_to_U8(mQ[VS], lower, upper), lower, upper);
+}
+
+// LLVector3 Magnitude and Normalization Functions
+
+
+// Set LLQuaternion routines
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLVector3 vec(x, y, z);
+ vec.normVec();
+
+ angle *= 0.5f;
+ F32 c, s;
+ c = cosf(angle);
+ s = sinf(angle);
+
+ mQ[VX] = vec.mV[VX]*s;
+ mQ[VY] = vec.mV[VY]*s;
+ mQ[VZ] = vec.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector3 &vec)
+{
+ LLVector3 v(vec);
+ v.normVec();
+
+ angle *= 0.5f;
+ F32 c, s;
+ c = cosf(angle);
+ s = sinf(angle);
+
+ mQ[VX] = v.mV[VX]*s;
+ mQ[VY] = v.mV[VY]*s;
+ mQ[VZ] = v.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 angle, const LLVector4 &vec)
+{
+ LLVector3 v(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ v.normVec();
+
+ F32 c, s;
+ c = cosf(angle*0.5f);
+ s = sinf(angle*0.5f);
+
+ mQ[VX] = v.mV[VX]*s;
+ mQ[VY] = v.mV[VY]*s;
+ mQ[VZ] = v.mV[VZ]*s;
+ mQ[VW] = c;
+
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(F32 roll, F32 pitch, F32 yaw)
+{
+ LLMatrix3 rot_mat(roll, pitch, yaw);
+ rot_mat.orthogonalize();
+ *this = rot_mat.quaternion();
+
+ normQuat();
+ return (*this);
+//#if 1
+// // NOTE: LLQuaternion's are actually inverted with respect to
+// // the matrices, so this code also assumes inverted quaternions
+// // (-x, -y, -z, w). The result is that roll,pitch,yaw are applied
+// // in reverse order (yaw,pitch,roll).
+// F64 cosX = cos(roll);
+// F64 cosY = cos(pitch);
+// F64 cosZ = cos(yaw);
+//
+// F64 sinX = sin(roll);
+// F64 sinY = sin(pitch);
+// F64 sinZ = sin(yaw);
+//
+// mQ[VW] = (F32)sqrt(cosY*cosZ - sinX*sinY*sinZ + cosX*cosZ + cosX*cosY + 1.0)*.5;
+// if (fabs(mQ[VW]) < F_APPROXIMATELY_ZERO)
+// {
+// // null rotation, any axis will do
+// mQ[VX] = 0.0f;
+// mQ[VY] = 1.0f;
+// mQ[VZ] = 0.0f;
+// }
+// else
+// {
+// F32 inv_s = 1.0f / (4.0f * mQ[VW]);
+// mQ[VX] = (F32)-(-sinX*cosY - cosX*sinY*sinZ - sinX*cosZ) * inv_s;
+// mQ[VY] = (F32)-(-cosX*sinY*cosZ + sinX*sinZ - sinY) * inv_s;
+// mQ[VZ] = (F32)-(-cosY*sinZ - sinX*sinY*cosZ - cosX*sinZ) * inv_s;
+// }
+//
+//#else // This only works on a certain subset of roll/pitch/yaw
+//
+// F64 cosX = cosf(roll/2.0);
+// F64 cosY = cosf(pitch/2.0);
+// F64 cosZ = cosf(yaw/2.0);
+//
+// F64 sinX = sinf(roll/2.0);
+// F64 sinY = sinf(pitch/2.0);
+// F64 sinZ = sinf(yaw/2.0);
+//
+// mQ[VW] = (F32)(cosX*cosY*cosZ + sinX*sinY*sinZ);
+// mQ[VX] = (F32)(sinX*cosY*cosZ - cosX*sinY*sinZ);
+// mQ[VY] = (F32)(cosX*sinY*cosZ + sinX*cosY*sinZ);
+// mQ[VZ] = (F32)(cosX*cosY*sinZ - sinX*sinY*cosZ);
+//#endif
+//
+// normQuat();
+// return (*this);
+}
+
+// SJB: This code is correct for a logicly stored (non-transposed) matrix;
+// Our matrices are stored transposed, OpenGL style, so this generates the
+// INVERSE matrix, or the CORRECT matrix form an INVERSE quaternion.
+// Because we use similar logic in LLMatrix3::quaternion(),
+// we are internally consistant so everything works OK :)
+LLMatrix3 LLQuaternion::getMatrix3(void) const
+{
+ LLMatrix3 mat;
+ F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
+
+ xx = mQ[VX] * mQ[VX];
+ xy = mQ[VX] * mQ[VY];
+ xz = mQ[VX] * mQ[VZ];
+ xw = mQ[VX] * mQ[VW];
+
+ yy = mQ[VY] * mQ[VY];
+ yz = mQ[VY] * mQ[VZ];
+ yw = mQ[VY] * mQ[VW];
+
+ zz = mQ[VZ] * mQ[VZ];
+ zw = mQ[VZ] * mQ[VW];
+
+ mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz );
+ mat.mMatrix[0][1] = 2.f * ( xy + zw );
+ mat.mMatrix[0][2] = 2.f * ( xz - yw );
+
+ mat.mMatrix[1][0] = 2.f * ( xy - zw );
+ mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz );
+ mat.mMatrix[1][2] = 2.f * ( yz + xw );
+
+ mat.mMatrix[2][0] = 2.f * ( xz + yw );
+ mat.mMatrix[2][1] = 2.f * ( yz - xw );
+ mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy );
+
+ return mat;
+}
+
+LLMatrix4 LLQuaternion::getMatrix4(void) const
+{
+ LLMatrix4 mat;
+ F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
+
+ xx = mQ[VX] * mQ[VX];
+ xy = mQ[VX] * mQ[VY];
+ xz = mQ[VX] * mQ[VZ];
+ xw = mQ[VX] * mQ[VW];
+
+ yy = mQ[VY] * mQ[VY];
+ yz = mQ[VY] * mQ[VZ];
+ yw = mQ[VY] * mQ[VW];
+
+ zz = mQ[VZ] * mQ[VZ];
+ zw = mQ[VZ] * mQ[VW];
+
+ mat.mMatrix[0][0] = 1.f - 2.f * ( yy + zz );
+ mat.mMatrix[0][1] = 2.f * ( xy + zw );
+ mat.mMatrix[0][2] = 2.f * ( xz - yw );
+
+ mat.mMatrix[1][0] = 2.f * ( xy - zw );
+ mat.mMatrix[1][1] = 1.f - 2.f * ( xx + zz );
+ mat.mMatrix[1][2] = 2.f * ( yz + xw );
+
+ mat.mMatrix[2][0] = 2.f * ( xz + yw );
+ mat.mMatrix[2][1] = 2.f * ( yz - xw );
+ mat.mMatrix[2][2] = 1.f - 2.f * ( xx + yy );
+
+ // TODO -- should we set the translation portion to zero?
+
+ return mat;
+}
+
+
+
+
+// Other useful methods
+
+
+// calculate the shortest rotation from a to b
+void LLQuaternion::shortestArc(const LLVector3 &a, const LLVector3 &b)
+{
+ // Make a local copy of both vectors.
+ LLVector3 vec_a = a;
+ LLVector3 vec_b = b;
+
+ // Make sure neither vector is zero length. Also normalize
+ // the vectors while we are at it.
+ F32 vec_a_mag = vec_a.normVec();
+ F32 vec_b_mag = vec_b.normVec();
+ if (vec_a_mag < F_APPROXIMATELY_ZERO ||
+ vec_b_mag < F_APPROXIMATELY_ZERO)
+ {
+ // Can't calculate a rotation from this.
+ // Just return ZERO_ROTATION instead.
+ loadIdentity();
+ return;
+ }
+
+ // Create an axis to rotate around, and the cos of the angle to rotate.
+ LLVector3 axis = vec_a % vec_b;
+ F32 cos_theta = vec_a * vec_b;
+
+ // Check the angle between the vectors to see if they are parallel or anti-parallel.
+ if (cos_theta > 1.0 - F_APPROXIMATELY_ZERO)
+ {
+ // a and b are parallel. No rotation is necessary.
+ loadIdentity();
+ }
+ else if (cos_theta < -1.0 + F_APPROXIMATELY_ZERO)
+ {
+ // a and b are anti-parallel.
+ // Rotate 180 degrees around some orthogonal axis.
+ // Find the projection of the x-axis onto a, and try
+ // using the vector between the projection and the x-axis
+ // as the orthogonal axis.
+ LLVector3 proj = vec_a.mV[VX] / (vec_a * vec_a) * vec_a;
+ LLVector3 ortho_axis(1.f, 0.f, 0.f);
+ ortho_axis -= proj;
+
+ // Turn this into an orthonormal axis.
+ F32 ortho_length = ortho_axis.normVec();
+ // If the axis' length is 0, then our guess at an orthogonal axis
+ // was wrong (a is parallel to the x-axis).
+ if (ortho_length < F_APPROXIMATELY_ZERO)
+ {
+ // Use the z-axis instead.
+ ortho_axis.setVec(0.f, 0.f, 1.f);
+ }
+
+ // Construct a quaternion from this orthonormal axis.
+ mQ[VX] = ortho_axis.mV[VX];
+ mQ[VY] = ortho_axis.mV[VY];
+ mQ[VZ] = ortho_axis.mV[VZ];
+ mQ[VW] = 0.f;
+ }
+ else
+ {
+ // a and b are NOT parallel or anti-parallel.
+ // Return the rotation between these vectors.
+ F32 theta = (F32)acos(cos_theta);
+
+ setQuat(theta, axis);
+ }
+}
+
+// constrains rotation to a cone angle specified in radians
+const LLQuaternion &LLQuaternion::constrain(F32 radians)
+{
+ const F32 cos_angle_lim = cosf( radians/2 ); // mQ[VW] limit
+ const F32 sin_angle_lim = sinf( radians/2 ); // rotation axis length limit
+
+ if (mQ[VW] < 0.f)
+ {
+ mQ[VX] *= -1.f;
+ mQ[VY] *= -1.f;
+ mQ[VZ] *= -1.f;
+ mQ[VW] *= -1.f;
+ }
+
+ // if rotation angle is greater than limit (cos is less than limit)
+ if( mQ[VW] < cos_angle_lim )
+ {
+ mQ[VW] = cos_angle_lim;
+ F32 axis_len = sqrtf( mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] ); // sin(theta/2)
+ F32 axis_mult_fact = sin_angle_lim / axis_len;
+ mQ[VX] *= axis_mult_fact;
+ mQ[VY] *= axis_mult_fact;
+ mQ[VZ] *= axis_mult_fact;
+ }
+
+ return *this;
+}
+
+// Operators
+
+std::ostream& operator<<(std::ostream &s, const LLQuaternion &a)
+{
+ s << "{ "
+ << a.mQ[VX] << ", " << a.mQ[VY] << ", " << a.mQ[VZ] << ", " << a.mQ[VW]
+ << " }";
+ return s;
+}
+
+
+// Does NOT renormalize the result
+LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b)
+{
+// LLQuaternion::mMultCount++;
+
+ LLQuaternion q(
+ b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1],
+ b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2],
+ b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0],
+ b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2]
+ );
+ return q;
+}
+
+/*
+LLMatrix4 operator*(const LLMatrix4 &m, const LLQuaternion &q)
+{
+ LLMatrix4 qmat(q);
+ return (m*qmat);
+}
+*/
+
+
+
+LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot)
+{
+ F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
+ F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
+ F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
+ F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
+
+ F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
+ F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
+ F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
+
+ return LLVector4(nx, ny, nz, a.mV[VW]);
+}
+
+LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot)
+{
+ F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
+ F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
+ F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
+ F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
+
+ F32 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
+ F32 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
+ F32 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
+
+ return LLVector3(nx, ny, nz);
+}
+
+LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot)
+{
+ F64 rw = - rot.mQ[VX] * a.mdV[VX] - rot.mQ[VY] * a.mdV[VY] - rot.mQ[VZ] * a.mdV[VZ];
+ F64 rx = rot.mQ[VW] * a.mdV[VX] + rot.mQ[VY] * a.mdV[VZ] - rot.mQ[VZ] * a.mdV[VY];
+ F64 ry = rot.mQ[VW] * a.mdV[VY] + rot.mQ[VZ] * a.mdV[VX] - rot.mQ[VX] * a.mdV[VZ];
+ F64 rz = rot.mQ[VW] * a.mdV[VZ] + rot.mQ[VX] * a.mdV[VY] - rot.mQ[VY] * a.mdV[VX];
+
+ F64 nx = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
+ F64 ny = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
+ F64 nz = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
+
+ return LLVector3d(nx, ny, nz);
+}
+
+F32 dot(const LLQuaternion &a, const LLQuaternion &b)
+{
+ return a.mQ[VX] * b.mQ[VX] +
+ a.mQ[VY] * b.mQ[VY] +
+ a.mQ[VZ] * b.mQ[VZ] +
+ a.mQ[VW] * b.mQ[VW];
+}
+
+// DEMO HACK: This lerp is probably inocrrect now due intermediate normalization
+// it should look more like the lerp below
+#if 0
+// linear interpolation
+LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
+{
+ LLQuaternion r;
+ r = t * (q - p) + p;
+ r.normQuat();
+ return r;
+}
+#endif
+
+// lerp from identity to q
+LLQuaternion lerp(F32 t, const LLQuaternion &q)
+{
+ LLQuaternion r;
+ r.mQ[VX] = t * q.mQ[VX];
+ r.mQ[VY] = t * q.mQ[VY];
+ r.mQ[VZ] = t * q.mQ[VZ];
+ r.mQ[VW] = t * (q.mQ[VZ] - 1.f) + 1.f;
+ r.normQuat();
+ return r;
+}
+
+LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q)
+{
+ LLQuaternion r;
+ F32 inv_t;
+
+ inv_t = 1.f - t;
+
+ r.mQ[VX] = t * q.mQ[VX] + (inv_t * p.mQ[VX]);
+ r.mQ[VY] = t * q.mQ[VY] + (inv_t * p.mQ[VY]);
+ r.mQ[VZ] = t * q.mQ[VZ] + (inv_t * p.mQ[VZ]);
+ r.mQ[VW] = t * q.mQ[VW] + (inv_t * p.mQ[VW]);
+ r.normQuat();
+ return r;
+}
+
+
+// spherical linear interpolation
+LLQuaternion slerp( F32 u, const LLQuaternion &a, const LLQuaternion &b )
+{
+ // cosine theta = dot product of a and b
+ F32 cos_t = a.mQ[0]*b.mQ[0] + a.mQ[1]*b.mQ[1] + a.mQ[2]*b.mQ[2] + a.mQ[3]*b.mQ[3];
+
+ // if b is on opposite hemisphere from a, use -a instead
+ int bflip;
+ if (cos_t < 0.0f)
+ {
+ cos_t = -cos_t;
+ bflip = TRUE;
+ }
+ else
+ bflip = FALSE;
+
+ // if B is (within precision limits) the same as A,
+ // just linear interpolate between A and B.
+ F32 alpha; // interpolant
+ F32 beta; // 1 - interpolant
+ if (1.0f - cos_t < 0.00001f)
+ {
+ beta = 1.0f - u;
+ alpha = u;
+ }
+ else
+ {
+ F32 theta = acosf(cos_t);
+ F32 sin_t = sinf(theta);
+ beta = sinf(theta - u*theta) / sin_t;
+ alpha = sinf(u*theta) / sin_t;
+ }
+
+ if (bflip)
+ beta = -beta;
+
+ // interpolate
+ LLQuaternion ret;
+ ret.mQ[0] = beta*a.mQ[0] + alpha*b.mQ[0];
+ ret.mQ[1] = beta*a.mQ[1] + alpha*b.mQ[1];
+ ret.mQ[2] = beta*a.mQ[2] + alpha*b.mQ[2];
+ ret.mQ[3] = beta*a.mQ[3] + alpha*b.mQ[3];
+
+ return ret;
+}
+
+// lerp whenever possible
+LLQuaternion nlerp(F32 t, const LLQuaternion &a, const LLQuaternion &b)
+{
+ if (dot(a, b) < 0.f)
+ {
+ return slerp(t, a, b);
+ }
+ else
+ {
+ return lerp(t, a, b);
+ }
+}
+
+LLQuaternion nlerp(F32 t, const LLQuaternion &q)
+{
+ if (q.mQ[VW] < 0.f)
+ {
+ return slerp(t, q);
+ }
+ else
+ {
+ return lerp(t, q);
+ }
+}
+
+// slerp from identity quaternion to another quaternion
+LLQuaternion slerp(F32 t, const LLQuaternion &q)
+{
+ F32 c = q.mQ[VW];
+ if (1.0f == t || 1.0f == c)
+ {
+ // the trivial cases
+ return q;
+ }
+
+ LLQuaternion r;
+ F32 s, angle, stq, stp;
+
+ s = (F32) sqrt(1.f - c*c);
+
+ if (c < 0.0f)
+ {
+ // when c < 0.0 then theta > PI/2
+ // since quat and -quat are the same rotation we invert one of
+ // p or q to reduce unecessary spins
+ // A equivalent way to do it is to convert acos(c) as if it had been negative,
+ // and to negate stp
+ angle = (F32) acos(-c);
+ stp = -(F32) sin(angle * (1.f - t));
+ stq = (F32) sin(angle * t);
+ }
+ else
+ {
+ angle = (F32) acos(c);
+ stp = (F32) sin(angle * (1.f - t));
+ stq = (F32) sin(angle * t);
+ }
+
+ r.mQ[VX] = (q.mQ[VX] * stq) / s;
+ r.mQ[VY] = (q.mQ[VY] * stq) / s;
+ r.mQ[VZ] = (q.mQ[VZ] * stq) / s;
+ r.mQ[VW] = (stp + q.mQ[VW] * stq) / s;
+
+ return r;
+}
+
+LLQuaternion mayaQ(F32 xRot, F32 yRot, F32 zRot, LLQuaternion::Order order)
+{
+ LLQuaternion xQ( xRot*DEG_TO_RAD, LLVector3(1.0f, 0.0f, 0.0f) );
+ LLQuaternion yQ( yRot*DEG_TO_RAD, LLVector3(0.0f, 1.0f, 0.0f) );
+ LLQuaternion zQ( zRot*DEG_TO_RAD, LLVector3(0.0f, 0.0f, 1.0f) );
+ LLQuaternion ret;
+ switch( order )
+ {
+ case LLQuaternion::XYZ:
+ ret = xQ * yQ * zQ;
+ break;
+ case LLQuaternion::YZX:
+ ret = yQ * zQ * xQ;
+ break;
+ case LLQuaternion::ZXY:
+ ret = zQ * xQ * yQ;
+ break;
+ case LLQuaternion::XZY:
+ ret = xQ * zQ * yQ;
+ break;
+ case LLQuaternion::YXZ:
+ ret = yQ * xQ * zQ;
+ break;
+ case LLQuaternion::ZYX:
+ ret = zQ * yQ * xQ;
+ break;
+ }
+ return ret;
+}
+
+const char *OrderToString( const LLQuaternion::Order order )
+{
+ char *p = NULL;
+ switch( order )
+ {
+ default:
+ case LLQuaternion::XYZ:
+ p = "XYZ";
+ break;
+ case LLQuaternion::YZX:
+ p = "YZX";
+ break;
+ case LLQuaternion::ZXY:
+ p = "ZXY";
+ break;
+ case LLQuaternion::XZY:
+ p = "XZY";
+ break;
+ case LLQuaternion::YXZ:
+ p = "YXZ";
+ break;
+ case LLQuaternion::ZYX:
+ p = "ZYX";
+ break;
+ }
+ return p;
+}
+
+LLQuaternion::Order StringToOrder( const char *str )
+{
+ if (strncmp(str, "XYZ", 3)==0 || strncmp(str, "xyz", 3)==0)
+ return LLQuaternion::XYZ;
+
+ if (strncmp(str, "YZX", 3)==0 || strncmp(str, "yzx", 3)==0)
+ return LLQuaternion::YZX;
+
+ if (strncmp(str, "ZXY", 3)==0 || strncmp(str, "zxy", 3)==0)
+ return LLQuaternion::ZXY;
+
+ if (strncmp(str, "XZY", 3)==0 || strncmp(str, "xzy", 3)==0)
+ return LLQuaternion::XZY;
+
+ if (strncmp(str, "YXZ", 3)==0 || strncmp(str, "yxz", 3)==0)
+ return LLQuaternion::YXZ;
+
+ if (strncmp(str, "ZYX", 3)==0 || strncmp(str, "zyx", 3)==0)
+ return LLQuaternion::ZYX;
+
+ return LLQuaternion::XYZ;
+}
+
+const LLQuaternion& LLQuaternion::setQuat(const LLMatrix3 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+ return (*this);
+}
+
+const LLQuaternion& LLQuaternion::setQuat(const LLMatrix4 &mat)
+{
+ *this = mat.quaternion();
+ normQuat();
+ return (*this);
+}
+
+void LLQuaternion::getAngleAxis(F32* angle, LLVector3 &vec) const
+{
+ F32 cos_a = mQ[VW];
+ if (cos_a > 1.0f) cos_a = 1.0f;
+ if (cos_a < -1.0f) cos_a = -1.0f;
+
+ F32 sin_a = (F32) sqrt( 1.0f - cos_a * cos_a );
+
+ if ( fabs( sin_a ) < 0.0005f )
+ sin_a = 1.0f;
+ else
+ sin_a = 1.f/sin_a;
+
+ *angle = 2.0f * (F32) acos( cos_a );
+ vec.mV[VX] = mQ[VX] * sin_a;
+ vec.mV[VY] = mQ[VY] * sin_a;
+ vec.mV[VZ] = mQ[VZ] * sin_a;
+}
+
+
+// quaternion does not need to be normalized
+void LLQuaternion::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
+{
+ LLMatrix3 rot_mat(*this);
+ rot_mat.orthogonalize();
+ rot_mat.getEulerAngles(roll, pitch, yaw);
+
+// // NOTE: LLQuaternion's are actually inverted with respect to
+// // the matrices, so this code also assumes inverted quaternions
+// // (-x, -y, -z, w). The result is that roll,pitch,yaw are applied
+// // in reverse order (yaw,pitch,roll).
+// F32 x = -mQ[VX], y = -mQ[VY], z = -mQ[VZ], w = mQ[VW];
+// F64 m20 = 2.0*(x*z-y*w);
+// if (1.0f - fabsf(m20) < F_APPROXIMATELY_ZERO)
+// {
+// *roll = 0.0f;
+// *pitch = (F32)asin(m20);
+// *yaw = (F32)atan2(2.0*(x*y-z*w), 1.0 - 2.0*(x*x+z*z));
+// }
+// else
+// {
+// *roll = (F32)atan2(-2.0*(y*z+x*w), 1.0-2.0*(x*x+y*y));
+// *pitch = (F32)asin(m20);
+// *yaw = (F32)atan2(-2.0*(x*y+z*w), 1.0-2.0*(y*y+z*z));
+// }
+}
+
+// Saves space by using the fact that our quaternions are normalized
+LLVector3 LLQuaternion::packToVector3() const
+{
+ if( mQ[VW] >= 0 )
+ {
+ return LLVector3( mQ[VX], mQ[VY], mQ[VZ] );
+ }
+ else
+ {
+ return LLVector3( -mQ[VX], -mQ[VY], -mQ[VZ] );
+ }
+}
+
+// Saves space by using the fact that our quaternions are normalized
+void LLQuaternion::unpackFromVector3( const LLVector3& vec )
+{
+ mQ[VX] = vec.mV[VX];
+ mQ[VY] = vec.mV[VY];
+ mQ[VZ] = vec.mV[VZ];
+ F32 t = 1.f - vec.magVecSquared();
+ if( t > 0 )
+ {
+ mQ[VW] = sqrt( t );
+ }
+ else
+ {
+ // Need this to avoid trying to find the square root of a negative number due
+ // to floating point error.
+ mQ[VW] = 0;
+ }
+}
+
+BOOL LLQuaternion::parseQuat(const char* buf, LLQuaternion* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLQuaternion quat;
+ S32 count = sscanf( buf, "%f %f %f %f", quat.mQ + 0, quat.mQ + 1, quat.mQ + 2, quat.mQ + 3 );
+ if( 4 == count )
+ {
+ value->setQuat( quat );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+// End
diff --git a/indra/llmath/llquaternion.h b/indra/llmath/llquaternion.h
new file mode 100644
index 0000000000..558eeec341
--- /dev/null
+++ b/indra/llmath/llquaternion.h
@@ -0,0 +1,442 @@
+/**
+ * @file llquaternion.h
+ * @brief LLQuaternion class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLQUATERNION_H
+#define LLQUATERNION_H
+
+#include "llmath.h"
+
+class LLVector4;
+class LLVector3;
+class LLVector3d;
+class LLMatrix4;
+class LLMatrix3;
+
+// NOTA BENE: Quaternion code is written assuming Unit Quaternions!!!!
+// Moreover, it is written assuming that all vectors and matricies
+// passed as arguments are normalized and unitary respectively.
+// VERY VERY VERY VERY BAD THINGS will happen if these assumptions fail.
+
+static const U32 LENGTHOFQUAT = 4;
+
+class LLQuaternion
+{
+public:
+ F32 mQ[LENGTHOFQUAT];
+
+ static const LLQuaternion DEFAULT;
+
+ LLQuaternion(); // Initializes Quaternion to (0,0,0,1)
+ explicit LLQuaternion(const LLMatrix4 &mat); // Initializes Quaternion from Matrix4
+ explicit LLQuaternion(const LLMatrix3 &mat); // Initializes Quaternion from Matrix3
+ LLQuaternion(F32 x, F32 y, F32 z, F32 w); // Initializes Quaternion to normQuat(x, y, z, w)
+ LLQuaternion(F32 angle, const LLVector4 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec)
+ LLQuaternion(F32 angle, const LLVector3 &vec); // Initializes Quaternion to axis_angle2quat(angle, vec)
+ LLQuaternion(const F32 *q); // Initializes Quaternion to normQuat(x, y, z, w)
+ LLQuaternion(const LLVector3 &x_axis,
+ const LLVector3 &y_axis,
+ const LLVector3 &z_axis); // Initializes Quaternion from Matrix3 = [x_axis ; y_axis ; z_axis]
+
+ BOOL isIdentity() const;
+ BOOL isNotIdentity() const;
+ BOOL isFinite() const; // checks to see if all values of LLQuaternion are finite
+ void quantize16(F32 lower, F32 upper); // changes the vector to reflect quatization
+ void quantize8(F32 lower, F32 upper); // changes the vector to reflect quatization
+ void loadIdentity(); // Loads the quaternion that represents the identity rotation
+ const LLQuaternion& setQuatInit(F32 x, F32 y, F32 z, F32 w); // Sets Quaternion to normQuat(x, y, z, w)
+ const LLQuaternion& setQuat(const LLQuaternion &quat); // Copies Quaternion
+ const LLQuaternion& setQuat(const F32 *q); // Sets Quaternion to normQuat(quat[VX], quat[VY], quat[VZ], quat[VW])
+ const LLQuaternion& setQuat(const LLMatrix3 &mat); // Sets Quaternion to mat2quat(mat)
+ const LLQuaternion& setQuat(const LLMatrix4 &mat); // Sets Quaternion to mat2quat(mat)
+ const LLQuaternion& setQuat(F32 angle, F32 x, F32 y, F32 z); // Sets Quaternion to axis_angle2quat(angle, x, y, z)
+ const LLQuaternion& setQuat(F32 angle, const LLVector3 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
+ const LLQuaternion& setQuat(F32 angle, const LLVector4 &vec); // Sets Quaternion to axis_angle2quat(angle, vec)
+ const LLQuaternion& setQuat(F32 roll, F32 pitch, F32 yaw); // Sets Quaternion to euler2quat(pitch, yaw, roll)
+
+ LLMatrix4 getMatrix4(void) const; // Returns the Matrix4 equivalent of Quaternion
+ LLMatrix3 getMatrix3(void) const; // Returns the Matrix3 equivalent of Quaternion
+ void getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const; // returns rotation in radians about axis x,y,z
+ void getAngleAxis(F32* angle, LLVector3 &vec) const;
+ void getEulerAngles(F32 *roll, F32* pitch, F32 *yaw) const;
+
+ F32 normQuat(); // Normalizes Quaternion and returns magnitude
+ const LLQuaternion& conjQuat(void); // Conjugates Quaternion and returns result
+
+ // Other useful methods
+ const LLQuaternion& transQuat(); // Transpose
+ void shortestArc(const LLVector3 &a, const LLVector3 &b); // shortest rotation from a to b
+ const LLQuaternion& constrain(F32 radians); // constrains rotation to a cone angle specified in radians
+
+ // Standard operators
+ friend std::ostream& operator<<(std::ostream &s, const LLQuaternion &a); // Prints a
+ friend LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b); // Addition
+ friend LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b); // Subtraction
+ friend LLQuaternion operator-(const LLQuaternion &a); // Negation
+ friend LLQuaternion operator*(F32 a, const LLQuaternion &q); // Scale
+ friend LLQuaternion operator*(const LLQuaternion &q, F32 b); // Scale
+ friend LLQuaternion operator*(const LLQuaternion &a, const LLQuaternion &b); // Returns a * b
+ friend LLQuaternion operator~(const LLQuaternion &a); // Returns a* (Conjugate of a)
+ bool operator==(const LLQuaternion &b) const; // Returns a == b
+ bool operator!=(const LLQuaternion &b) const; // Returns a != b
+
+ friend const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b); // Returns a * b
+
+ friend LLVector4 operator*(const LLVector4 &a, const LLQuaternion &rot); // Rotates a by rot
+ friend LLVector3 operator*(const LLVector3 &a, const LLQuaternion &rot); // Rotates a by rot
+ friend LLVector3d operator*(const LLVector3d &a, const LLQuaternion &rot); // Rotates a by rot
+
+ // Non-standard operators
+ friend F32 dot(const LLQuaternion &a, const LLQuaternion &b);
+ friend LLQuaternion lerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from p to q
+ friend LLQuaternion lerp(F32 t, const LLQuaternion &q); // linear interpolation (t = 0 to 1) from identity to q
+ friend LLQuaternion slerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // spherical linear interpolation from p to q
+ friend LLQuaternion slerp(F32 t, const LLQuaternion &q); // spherical linear interpolation from identity to q
+ friend LLQuaternion nlerp(F32 t, const LLQuaternion &p, const LLQuaternion &q); // normalized linear interpolation from p to q
+ friend LLQuaternion nlerp(F32 t, const LLQuaternion &q); // normalized linear interpolation from p to q
+
+ LLVector3 packToVector3() const; // Saves space by using the fact that our quaternions are normalized
+ void unpackFromVector3(const LLVector3& vec); // Saves space by using the fact that our quaternions are normalized
+
+ enum Order {
+ XYZ = 0,
+ YZX = 1,
+ ZXY = 2,
+ XZY = 3,
+ YXZ = 4,
+ ZYX = 5
+ };
+ // Creates a quaternions from maya's rotation representation,
+ // which is 3 rotations (in DEGREES) in the specified order
+ friend LLQuaternion mayaQ(F32 x, F32 y, F32 z, Order order);
+
+ // Conversions between Order and strings like "xyz" or "ZYX"
+ friend const char *OrderToString( const Order order );
+ friend Order StringToOrder( const char *str );
+
+ static BOOL parseQuat(const char* buf, LLQuaternion* value);
+
+ // For debugging, only
+ //static U32 mMultCount;
+};
+
+// checker
+inline BOOL LLQuaternion::isFinite() const
+{
+ return (llfinite(mQ[VX]) && llfinite(mQ[VY]) && llfinite(mQ[VZ]) && llfinite(mQ[VS]));
+}
+
+inline BOOL LLQuaternion::isIdentity() const
+{
+ return
+ ( mQ[VX] == 0.f ) &&
+ ( mQ[VY] == 0.f ) &&
+ ( mQ[VZ] == 0.f ) &&
+ ( mQ[VS] == 1.f );
+}
+
+inline BOOL LLQuaternion::isNotIdentity() const
+{
+ return
+ ( mQ[VX] != 0.f ) ||
+ ( mQ[VY] != 0.f ) ||
+ ( mQ[VZ] != 0.f ) ||
+ ( mQ[VS] != 1.f );
+}
+
+
+
+inline LLQuaternion::LLQuaternion(void)
+{
+ mQ[VX] = 0.f;
+ mQ[VY] = 0.f;
+ mQ[VZ] = 0.f;
+ mQ[VS] = 1.f;
+}
+
+inline LLQuaternion::LLQuaternion(F32 x, F32 y, F32 z, F32 w)
+{
+ mQ[VX] = x;
+ mQ[VY] = y;
+ mQ[VZ] = z;
+ mQ[VS] = w;
+
+ //RN: don't normalize this case as its used mainly for temporaries during calculations
+ //normQuat();
+ /*
+ F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
+ mag -= 1.f;
+ mag = fabs(mag);
+ llassert(mag < 10.f*FP_MAG_THRESHOLD);
+ */
+}
+
+inline LLQuaternion::LLQuaternion(const F32 *q)
+{
+ mQ[VX] = q[VX];
+ mQ[VY] = q[VY];
+ mQ[VZ] = q[VZ];
+ mQ[VS] = q[VW];
+
+ normQuat();
+ /*
+ F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
+ mag -= 1.f;
+ mag = fabs(mag);
+ llassert(mag < FP_MAG_THRESHOLD);
+ */
+}
+
+
+inline void LLQuaternion::loadIdentity()
+{
+ mQ[VX] = 0.0f;
+ mQ[VY] = 0.0f;
+ mQ[VZ] = 0.0f;
+ mQ[VW] = 1.0f;
+}
+
+
+inline const LLQuaternion& LLQuaternion::setQuatInit(F32 x, F32 y, F32 z, F32 w)
+{
+ mQ[VX] = x;
+ mQ[VY] = y;
+ mQ[VZ] = z;
+ mQ[VS] = w;
+ normQuat();
+ return (*this);
+}
+
+inline const LLQuaternion& LLQuaternion::setQuat(const LLQuaternion &quat)
+{
+ mQ[VX] = quat.mQ[VX];
+ mQ[VY] = quat.mQ[VY];
+ mQ[VZ] = quat.mQ[VZ];
+ mQ[VW] = quat.mQ[VW];
+ normQuat();
+ return (*this);
+}
+
+inline const LLQuaternion& LLQuaternion::setQuat(const F32 *q)
+{
+ mQ[VX] = q[VX];
+ mQ[VY] = q[VY];
+ mQ[VZ] = q[VZ];
+ mQ[VS] = q[VW];
+ normQuat();
+ return (*this);
+}
+
+// There may be a cheaper way that avoids the sqrt.
+// Does sin_a = VX*VX + VY*VY + VZ*VZ?
+// Copied from Matrix and Quaternion FAQ 1.12
+inline void LLQuaternion::getAngleAxis(F32* angle, F32* x, F32* y, F32* z) const
+{
+ F32 cos_a = mQ[VW];
+ if (cos_a > 1.0f) cos_a = 1.0f;
+ if (cos_a < -1.0f) cos_a = -1.0f;
+
+ F32 sin_a = (F32) sqrt( 1.0f - cos_a * cos_a );
+
+ if ( fabs( sin_a ) < 0.0005f )
+ sin_a = 1.0f;
+ else
+ sin_a = 1.f/sin_a;
+
+ *angle = 2.0f * (F32) acos( cos_a );
+ *x = mQ[VX] * sin_a;
+ *y = mQ[VY] * sin_a;
+ *z = mQ[VZ] * sin_a;
+}
+
+inline const LLQuaternion& LLQuaternion::conjQuat()
+{
+ mQ[VX] *= -1.f;
+ mQ[VY] *= -1.f;
+ mQ[VZ] *= -1.f;
+ return (*this);
+}
+
+// Transpose
+inline const LLQuaternion& LLQuaternion::transQuat()
+{
+ mQ[VX] = -mQ[VX];
+ mQ[VY] = -mQ[VY];
+ mQ[VZ] = -mQ[VZ];
+ return *this;
+}
+
+
+inline LLQuaternion operator+(const LLQuaternion &a, const LLQuaternion &b)
+{
+ return LLQuaternion(
+ a.mQ[VX] + b.mQ[VX],
+ a.mQ[VY] + b.mQ[VY],
+ a.mQ[VZ] + b.mQ[VZ],
+ a.mQ[VW] + b.mQ[VW] );
+}
+
+
+inline LLQuaternion operator-(const LLQuaternion &a, const LLQuaternion &b)
+{
+ return LLQuaternion(
+ a.mQ[VX] - b.mQ[VX],
+ a.mQ[VY] - b.mQ[VY],
+ a.mQ[VZ] - b.mQ[VZ],
+ a.mQ[VW] - b.mQ[VW] );
+}
+
+
+inline LLQuaternion operator-(const LLQuaternion &a)
+{
+ return LLQuaternion(
+ -a.mQ[VX],
+ -a.mQ[VY],
+ -a.mQ[VZ],
+ -a.mQ[VW] );
+}
+
+
+inline LLQuaternion operator*(F32 a, const LLQuaternion &q)
+{
+ return LLQuaternion(
+ a * q.mQ[VX],
+ a * q.mQ[VY],
+ a * q.mQ[VZ],
+ a * q.mQ[VW] );
+}
+
+
+inline LLQuaternion operator*(const LLQuaternion &q, F32 a)
+{
+ return LLQuaternion(
+ a * q.mQ[VX],
+ a * q.mQ[VY],
+ a * q.mQ[VZ],
+ a * q.mQ[VW] );
+}
+
+inline LLQuaternion operator~(const LLQuaternion &a)
+{
+ LLQuaternion q(a);
+ q.conjQuat();
+ return q;
+}
+
+inline bool LLQuaternion::operator==(const LLQuaternion &b) const
+{
+ return ( (mQ[VX] == b.mQ[VX])
+ &&(mQ[VY] == b.mQ[VY])
+ &&(mQ[VZ] == b.mQ[VZ])
+ &&(mQ[VS] == b.mQ[VS]));
+}
+
+inline bool LLQuaternion::operator!=(const LLQuaternion &b) const
+{
+ return ( (mQ[VX] != b.mQ[VX])
+ ||(mQ[VY] != b.mQ[VY])
+ ||(mQ[VZ] != b.mQ[VZ])
+ ||(mQ[VS] != b.mQ[VS]));
+}
+
+inline const LLQuaternion& operator*=(LLQuaternion &a, const LLQuaternion &b)
+{
+#if 1
+ LLQuaternion q(
+ b.mQ[3] * a.mQ[0] + b.mQ[0] * a.mQ[3] + b.mQ[1] * a.mQ[2] - b.mQ[2] * a.mQ[1],
+ b.mQ[3] * a.mQ[1] + b.mQ[1] * a.mQ[3] + b.mQ[2] * a.mQ[0] - b.mQ[0] * a.mQ[2],
+ b.mQ[3] * a.mQ[2] + b.mQ[2] * a.mQ[3] + b.mQ[0] * a.mQ[1] - b.mQ[1] * a.mQ[0],
+ b.mQ[3] * a.mQ[3] - b.mQ[0] * a.mQ[0] - b.mQ[1] * a.mQ[1] - b.mQ[2] * a.mQ[2]
+ );
+ a = q;
+#else
+ a = a * b;
+#endif
+ return a;
+}
+
+inline F32 LLQuaternion::normQuat()
+{
+ F32 mag = sqrtf(mQ[VX]*mQ[VX] + mQ[VY]*mQ[VY] + mQ[VZ]*mQ[VZ] + mQ[VS]*mQ[VS]);
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ F32 oomag = 1.f/mag;
+ mQ[VX] *= oomag;
+ mQ[VY] *= oomag;
+ mQ[VZ] *= oomag;
+ mQ[VS] *= oomag;
+ }
+ else
+ {
+ mQ[VX] = 0.f;
+ mQ[VY] = 0.f;
+ mQ[VZ] = 0.f;
+ mQ[VS] = 1.f;
+ }
+
+ return mag;
+}
+
+LLQuaternion::Order StringToOrder( const char *str );
+
+// Some notes about Quaternions
+
+// What is a Quaternion?
+// ---------------------
+// A quaternion is a point in 4-dimensional complex space.
+// Q = { Qx, Qy, Qz, Qw }
+//
+//
+// Why Quaternions?
+// ----------------
+// The set of quaternions that make up the the 4-D unit sphere
+// can be mapped to the set of all rotations in 3-D space. Sometimes
+// it is easier to describe/manipulate rotations in quaternion space
+// than rotation-matrix space.
+//
+//
+// How Quaternions?
+// ----------------
+// In order to take advantage of quaternions we need to know how to
+// go from rotation-matricies to quaternions and back. We also have
+// to agree what variety of rotations we're generating.
+//
+// Consider the equation... v' = v * R
+//
+// There are two ways to think about rotations of vectors.
+// 1) v' is the same vector in a different reference frame
+// 2) v' is a new vector in the same reference frame
+//
+// bookmark -- which way are we using?
+//
+//
+// Quaternion from Angle-Axis:
+// ---------------------------
+// Suppose we wanted to represent a rotation of some angle (theta)
+// about some axis ({Ax, Ay, Az})...
+//
+// axis of rotation = {Ax, Ay, Az}
+// angle_of_rotation = theta
+//
+// s = sin(0.5 * theta)
+// c = cos(0.5 * theta)
+// Q = { s * Ax, s * Ay, s * Az, c }
+//
+//
+// 3x3 Matrix from Quaternion
+// --------------------------
+//
+// | |
+// | 1 - 2 * (y^2 + z^2) 2 * (x * y + z * w) 2 * (y * w - x * z) |
+// | |
+// M = | 2 * (x * y - z * w) 1 - 2 * (x^2 + z^2) 2 * (y * z + x * w) |
+// | |
+// | 2 * (x * z + y * w) 2 * (y * z - x * w) 1 - 2 * (x^2 + y^2) |
+// | |
+
+#endif
diff --git a/indra/llmath/llrect.cpp b/indra/llmath/llrect.cpp
new file mode 100644
index 0000000000..60280536eb
--- /dev/null
+++ b/indra/llmath/llrect.cpp
@@ -0,0 +1,10 @@
+/**
+ * @file llrect.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llrect.h"
diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h
new file mode 100644
index 0000000000..92e415155b
--- /dev/null
+++ b/indra/llmath/llrect.h
@@ -0,0 +1,270 @@
+/**
+ * @file llrect.h
+ * @brief A rectangle in GL coordinates, with bottom,left = 0,0
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLRECT_H
+#define LL_LLRECT_H
+
+#include <iostream>
+#include "llmath.h"
+#include "llsd.h"
+
+// Top > Bottom due to GL coords
+template <class Type> class LLRectBase
+{
+public:
+ Type mLeft;
+ Type mTop;
+ Type mRight;
+ Type mBottom;
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ Type getWidth() const { return mRight - mLeft; }
+ Type getHeight() const { return mTop - mBottom; }
+ Type getCenterX() const { return (mLeft + mRight) / 2; }
+ Type getCenterY() const { return (mTop + mBottom) / 2; }
+
+ LLRectBase(): mLeft(0), mTop(0), mRight(0), mBottom(0)
+ {}
+
+ LLRectBase(const LLRectBase &r):
+ mLeft(r.mLeft), mTop(r.mTop), mRight(r.mRight), mBottom(r.mBottom)
+ {}
+
+ LLRectBase(Type left, Type top, Type right, Type bottom):
+ mLeft(left), mTop(top), mRight(right), mBottom(bottom)
+ {}
+
+ LLRectBase(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ const LLRectBase& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mLeft = sd[0].asInteger();
+ mTop = sd[1].asInteger();
+ mRight = sd[2].asInteger();
+ mBottom = sd[3].asInteger();
+ }
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mLeft;
+ ret[1] = mTop;
+ ret[2] = mRight;
+ ret[3] = mBottom;
+ return ret;
+ }
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ BOOL pointInRect(const Type x, const Type y) const
+ {
+ return mLeft <= x && x < mRight &&
+ mBottom <= y && y < mTop;
+ }
+
+ //// Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ BOOL localPointInRect(const Type x, const Type y) const
+ {
+ return 0 <= x && x < getWidth() &&
+ 0 <= y && y < getHeight();
+ }
+
+ void clampPointToRect(Type& x, Type& y)
+ {
+ x = llclamp(x, mLeft, mRight);
+ y = llclamp(y, mBottom, mTop);
+ }
+
+ void clipPointToRect(const Type start_x, const Type start_y, Type& end_x, Type& end_y)
+ {
+ if (!pointInRect(start_x, start_y))
+ {
+ return;
+ }
+ Type clip_x = 0;
+ Type clip_y = 0;
+ Type delta_x = end_x - start_x;
+ Type delta_y = end_y - start_y;
+ if (end_x > mRight) clip_x = end_x - mRight;
+ if (end_x < mLeft) clip_x = end_x - mLeft;
+ if (end_y > mTop) clip_y = end_y - mTop;
+ if (end_y < mBottom) clip_y = end_y - mBottom;
+ // clip_? and delta_? should have same sign, since starting point is in rect
+ // so ratios will be positive
+ F32 ratio_x = ((F32)clip_x / (F32)delta_x);
+ F32 ratio_y = ((F32)clip_y / (F32)delta_y);
+ if (ratio_x > ratio_y)
+ {
+ // clip along x direction
+ end_x -= (Type)(clip_x);
+ end_y -= (Type)(delta_y * ratio_x);
+ }
+ else
+ {
+ // clip along y direction
+ end_x -= (Type)(delta_x * ratio_y);
+ end_y -= (Type)clip_y;
+ }
+ }
+
+ // Note: Does NOT follow GL_QUAD conventions: the top and right edges ARE considered part of the rect
+ // returns TRUE if any part of rect is is inside this LLRect
+ BOOL rectInRect(const LLRectBase* rect) const
+ {
+ return mLeft <= rect->mRight && rect->mLeft <= mRight &&
+ mBottom <= rect->mTop && rect->mBottom <= mTop ;
+ }
+
+ void set(Type left, Type top, Type right, Type bottom)
+ {
+ mLeft = left;
+ mTop = top;
+ mRight = right;
+ mBottom = bottom;
+ }
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ void setOriginAndSize( Type left, Type bottom, Type width, Type height)
+ {
+ mLeft = left;
+ mTop = bottom + height;
+ mRight = left + width;
+ mBottom = bottom;
+ }
+
+ // Note: follows GL_QUAD conventions: the top and right edges are not considered part of the rect
+ void setLeftTopAndSize( Type left, Type top, Type width, Type height)
+ {
+ mLeft = left;
+ mTop = top;
+ mRight = left + width;
+ mBottom = top - height;
+ }
+
+ void setCenterAndSize(Type x, Type y, Type width, Type height)
+ {
+ mLeft = x - width/2;
+ mTop = y + height/2;
+ mRight = x + width/2;
+ mBottom = y - height/2;
+ }
+
+
+ void translate(Type horiz, Type vertical)
+ {
+ mLeft += horiz;
+ mRight += horiz;
+ mTop += vertical;
+ mBottom += vertical;
+ }
+
+ void stretch( Type dx, Type dy)
+ {
+ mLeft -= dx;
+ mRight += dx;
+ mTop += dy;
+ mBottom -= dy;
+ makeValid();
+ }
+
+ void stretch( Type delta )
+ {
+ stretch(delta, delta);
+
+ }
+
+ void makeValid()
+ {
+ mLeft = llmin(mLeft, mRight);
+ mBottom = llmin(mBottom, mTop);
+ }
+
+ friend const LLRectBase& operator|=(LLRectBase &a, const LLRectBase &b) // Return rect including a & b
+ {
+ a.mLeft = llmin(a.mLeft, b.mLeft);
+ a.mRight = llmax(a.mRight, b.mRight);
+ a.mBottom = llmin(a.mBottom, b.mBottom);
+ a.mTop = llmax(a.mTop, b.mTop);
+ return a;
+ }
+
+ friend LLRectBase operator|(const LLRectBase &a, const LLRectBase &b) // Return rect including a & b
+ {
+ LLRectBase<Type> result;
+ result.mLeft = llmin(a.mLeft, b.mLeft);
+ result.mRight = llmax(a.mRight, b.mRight);
+ result.mBottom = llmin(a.mBottom, b.mBottom);
+ result.mTop = llmax(a.mTop, b.mTop);
+ return result;
+ }
+
+ friend const LLRectBase& operator&=(LLRectBase &a, const LLRectBase &b) // set a to rect where a intersects b
+ {
+ a.mLeft = llmax(a.mLeft, b.mLeft);
+ a.mRight = llmin(a.mRight, b.mRight);
+ a.mBottom = llmax(a.mBottom, b.mBottom);
+ a.mTop = llmin(a.mTop, b.mTop);
+ if (a.mLeft > a.mRight)
+ {
+ a.mLeft = a.mRight;
+ }
+ if (a.mBottom > a.mTop)
+ {
+ a.mBottom = a.mTop;
+ }
+ return a;
+ }
+
+ friend LLRectBase operator&(const LLRectBase &a, const LLRectBase &b) // Return rect where a intersects b
+ {
+ LLRectBase result = a;
+ result &= b;
+ return result;
+ }
+
+ friend std::ostream &operator<<(std::ostream &s, const LLRectBase &rect)
+ {
+ s << "{ L " << rect.mLeft << " B " << rect.mBottom
+ << " W " << rect.getWidth() << " H " << rect.getHeight() << " }";
+ return s;
+ }
+
+ bool operator==(const LLRectBase &b)
+ {
+ return ((mLeft == b.mLeft) &&
+ (mTop == b.mTop) &&
+ (mRight == b.mRight) &&
+ (mBottom == b.mBottom));
+ }
+
+ bool operator!=(const LLRectBase &b)
+ {
+ return ((mLeft != b.mLeft) ||
+ (mTop != b.mTop) ||
+ (mRight != b.mRight) ||
+ (mBottom != b.mBottom));
+ }
+
+ static LLRectBase<Type> null;
+};
+
+template <class Type> LLRectBase<Type> LLRectBase<Type>::null(0,0,0,0);
+
+typedef LLRectBase<S32> LLRect;
+typedef LLRectBase<F32> LLRectf;
+
+#endif
diff --git a/indra/llmath/lltreenode.h b/indra/llmath/lltreenode.h
new file mode 100644
index 0000000000..dd0c73c00c
--- /dev/null
+++ b/indra/llmath/lltreenode.h
@@ -0,0 +1,161 @@
+/**
+ * @file lltreenode.h
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTREENODE_H
+#define LL_LLTREENODE_H
+
+#include "stdtypes.h"
+#include "xform.h"
+#include <vector>
+
+template <class T> class LLTreeNode;
+template <class T> class LLTreeTraveler;
+template <class T> class LLTreeListener;
+
+template <class T>
+class LLTreeState
+{
+public:
+ LLTreeState(LLTreeNode<T>* node) { setNode(node); }
+ virtual ~LLTreeState() { };
+
+ virtual bool insert(T* data) = 0;
+ virtual bool remove(T* data) = 0;
+ virtual void setNode(LLTreeNode<T>* node);
+ virtual const LLTreeNode<T>* getNode() const { return mNode; }
+ virtual LLTreeNode<T>* getNode() { return mNode; }
+ virtual void accept(LLTreeTraveler<T>* traveler) const = 0;
+ virtual LLTreeListener<T>* getListener(U32 index) const;
+private:
+ LLTreeNode<T>* mNode;
+};
+
+template <class T>
+class LLTreeListener
+{
+public:
+ virtual ~LLTreeListener() { };
+ virtual void handleInsertion(const LLTreeNode<T>* node, T* data) = 0;
+ virtual void handleRemoval(const LLTreeNode<T>* node, T* data) = 0;
+ virtual void handleDestruction(const LLTreeNode<T>* node) = 0;
+ virtual void handleStateChange(const LLTreeNode<T>* node) = 0;
+};
+
+template <class T>
+class LLTreeNode
+{
+public:
+ LLTreeNode(LLTreeState<T>* state) { setState(state); }
+ virtual ~LLTreeNode();
+ virtual LLTreeState<T>* getState() { return mState; }
+ virtual const LLTreeState<T>* getState() const { return mState; }
+
+ virtual void setState(LLTreeState<T>* state);
+ virtual void insert(T* data);
+ virtual bool remove(T* data);
+ virtual void notifyRemoval(T* data);
+ virtual U32 getListenerCount() { return mListeners.size(); }
+ virtual LLTreeListener<T>* getListener(U32 index) const { return mListeners[index]; }
+ virtual void addListener(LLTreeListener<T>* listener) { mListeners.push_back(listener); }
+ virtual void removeListener(U32 index) { mListeners.erase(mListeners.begin()+index); }
+
+protected:
+ void destroyListeners()
+ {
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleDestruction(this);
+ }
+ mListeners.clear();
+ }
+
+ LLTreeState<T>* mState;
+public:
+ std::vector<LLTreeListener<T>*> mListeners;
+};
+
+template <class T>
+class LLTreeTraveler
+{
+public:
+ virtual ~LLTreeTraveler() { };
+ virtual void traverse(const LLTreeNode<T>* node) = 0;
+ virtual void visit(const LLTreeState<T>* state) = 0;
+};
+
+template <class T>
+LLTreeNode<T>::~LLTreeNode()
+{
+ destroyListeners();
+};
+
+template <class T>
+void LLTreeNode<T>::insert(T* data)
+{
+ if (mState->insert(data))
+ {
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleInsertion(this, data);
+ }
+ }
+};
+
+template <class T>
+bool LLTreeNode<T>::remove(T* data)
+{
+ if (mState->remove(data))
+ {
+ return true;
+ }
+ return false;
+};
+
+template <class T>
+void LLTreeNode<T>::notifyRemoval(T* data)
+{
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleRemoval(this, data);
+ }
+}
+
+template <class T>
+void LLTreeNode<T>::setState(LLTreeState<T>* state)
+{
+ mState = state;
+ if (state)
+ {
+ if (state->getNode() != this)
+ {
+ state->setNode(this);
+ }
+
+ for (U32 i = 0; i < mListeners.size(); i++)
+ {
+ mListeners[i]->handleStateChange(this);
+ }
+ }
+};
+
+template <class T>
+void LLTreeState<T>::setNode(LLTreeNode<T>* node)
+{
+ mNode = node;
+ if (node && node->getState() != this)
+ {
+ node->setState(this);
+ }
+};
+
+template <class T>
+LLTreeListener<T>* LLTreeState<T>::getListener(U32 index) const
+{
+ return mNode->getListener(index);
+}
+
+#endif
diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp
new file mode 100644
index 0000000000..41e01b5193
--- /dev/null
+++ b/indra/llmath/llvolume.cpp
@@ -0,0 +1,4557 @@
+/**
+ * @file llvolume.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+
+#include <set>
+
+#include "llerror.h"
+
+#include "llvolumemgr.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "lldarray.h"
+#include "llvolume.h"
+#include "llstl.h"
+
+#define DEBUG_SILHOUETTE_BINORMALS 0
+#define DEBUG_SILHOUETTE_NORMALS 0 // TomY: Use this to display normals using the silhouette
+#define DEBUG_SILHOUETTE_EDGE_MAP 0 // DaveP: Use this to display edge map using the silhouette
+
+const F32 CUT_MIN = 0.f;
+const F32 CUT_MAX = 1.f;
+const F32 MIN_CUT_DELTA = 0.02f;
+
+const F32 HOLLOW_MIN = 0.f;
+const F32 HOLLOW_MAX = 0.95f;
+const F32 HOLLOW_MAX_SQUARE = 0.7f;
+
+const F32 TWIST_MIN = -1.f;
+const F32 TWIST_MAX = 1.f;
+
+const F32 RATIO_MIN = 0.f;
+const F32 RATIO_MAX = 2.f; // Tom Y: Inverted sense here: 0 = top taper, 2 = bottom taper
+
+const F32 HOLE_X_MIN= 0.05f;
+const F32 HOLE_X_MAX= 1.0f;
+
+const F32 HOLE_Y_MIN= 0.05f;
+const F32 HOLE_Y_MAX= 0.5f;
+
+const F32 SHEAR_MIN = -0.5f;
+const F32 SHEAR_MAX = 0.5f;
+
+const F32 REV_MIN = 1.f;
+const F32 REV_MAX = 4.f;
+
+const F32 TAPER_MIN = -1.f;
+const F32 TAPER_MAX = 1.f;
+
+const F32 SKEW_MIN = -0.95f;
+const F32 SKEW_MAX = 0.95f;
+
+BOOL check_same_clock_dir( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, const LLVector3& norm)
+{
+ LLVector3 test = (pt2-pt1)%(pt3-pt2);
+
+ //answer
+ if(test * norm < 0)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+// intersect test between triangle pt1,pt2,pt3 and line from linept to linept+vect
+//returns TRUE if intersecting and moves linept to the point of intersection
+BOOL LLTriangleLineSegmentIntersect( const LLVector3& pt1, const LLVector3& pt2, const LLVector3& pt3, LLVector3& linept, const LLVector3& vect)
+{
+ LLVector3 V1 = pt2-pt1;
+ LLVector3 V2 = pt3-pt2;
+
+ LLVector3 norm = V1 % V2;
+
+ F32 dotprod = norm * vect;
+
+ if(dotprod < 0)
+ {
+ //Find point of intersect to triangle plane.
+ //find t to intersect point
+ F32 t = -(norm * (linept-pt1))/dotprod;
+
+ // if ds is neg line started past triangle so can't hit triangle.
+ if (t > 0)
+ {
+ return FALSE;
+ }
+
+ LLVector3 pt_int = linept + (vect*t);
+
+ if(check_same_clock_dir(pt1, pt2, pt_int, norm))
+ {
+ if(check_same_clock_dir(pt2, pt3, pt_int, norm))
+ {
+ if(check_same_clock_dir(pt3, pt1, pt_int, norm))
+ {
+ // answer in pt_int is insde triangle
+ linept.setVec(pt_int);
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+//-------------------------------------------------------------------
+// statics
+//-------------------------------------------------------------------
+
+
+//----------------------------------------------------
+
+LLProfile::Face* LLProfile::addCap(S16 faceID)
+{
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = 0;
+ face->mCount = mTotal;
+ face->mScaleU= 1.0f;
+ face->mCap = TRUE;
+ face->mFaceID = faceID;
+ return face;
+}
+
+LLProfile::Face* LLProfile::addFace(S32 i, S32 count, F32 scaleU, S16 faceID, BOOL flat)
+{
+ Face *face = vector_append(mFaces, 1);
+
+ face->mIndex = i;
+ face->mCount = count;
+ face->mScaleU= scaleU;
+
+ face->mFlat = flat;
+ face->mCap = FALSE;
+ face->mFaceID = faceID;
+ return face;
+}
+
+// What is the bevel parameter used for? - DJS 04/05/02
+// Bevel parameter is currently unused but presumedly would support
+// filleted and chamfered corners
+void LLProfile::genNGon(S32 sides, F32 offset, F32 bevel, F32 ang_scale, S32 split)
+{
+ // Generate an n-sided "circular" path.
+ // 0 is (1,0), and we go counter-clockwise along a circular path from there.
+ const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+ F32 scale = 0.5f;
+ F32 t, t_step, t_first, t_fraction, ang, ang_step;
+ LLVector3 pt1,pt2;
+
+ mMaxX = 0.f;
+ mMinX = 0.f;
+
+ F32 begin = mParams.getBegin();
+ F32 end = mParams.getEnd();
+
+ t_step = 1.0f / sides;
+ ang_step = 2.0f*F_PI*t_step*ang_scale;
+
+ // 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
+
+ if (total_sides < 8)
+ {
+ scale = tableScale[total_sides];
+ }
+
+ t_first = floor(begin * sides) / (F32)sides;
+
+ // pt1 is the first point on the fractional face.
+ // Starting t and ang values for the first face
+ t = t_first;
+ ang = 2.0f*F_PI*(t*ang_scale + offset);
+ pt1.setVec(cos(ang)*scale,sin(ang)*scale, t);
+
+ // Increment to the next point.
+ // pt2 is the end point on the fractional face
+ t += t_step;
+ ang += ang_step;
+ pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ t_fraction = (begin - t_first)*sides;
+
+ // Only use if it's not almost exactly on an edge.
+ if (t_fraction < 0.99f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+ F32 pt_x = new_pt.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+ mProfile.push_back(new_pt);
+ }
+
+ // There's lots of potential here for floating point error to generate unneeded extra points - DJS 04/05/02
+ while (t < end)
+ {
+ // Iterate through all the integer steps of t.
+ pt1.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ F32 pt_x = pt1.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+
+ if (mProfile.size() > 0) {
+ LLVector3 p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ mProfile.push_back(p+(pt1-p) * 1.0f/(float)(split+1) * (float)(i+1));
+ }
+ }
+ mProfile.push_back(pt1);
+
+ t += t_step;
+ ang += ang_step;
+ }
+
+ t_fraction = (end - (t - t_step))*sides;
+
+ // pt1 is the first point on the fractional face
+ // pt2 is the end point on the fractional face
+ pt2.setVec(cos(ang)*scale,sin(ang)*scale,t);
+
+ // Find the fraction that we need to add to the end point.
+ t_fraction = (end - (t - t_step))*sides;
+ if (t_fraction > 0.01f)
+ {
+ LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+ F32 pt_x = new_pt.mV[VX];
+ if (pt_x < mMinX)
+ {
+ mMinX = pt_x;
+ }
+ else if (pt_x > mMaxX)
+ {
+ mMaxX = pt_x;
+ }
+
+ if (mProfile.size() > 0) {
+ LLVector3 p = mProfile[mProfile.size()-1];
+ for (S32 i = 0; i < split && mProfile.size() > 0; i++) {
+ mProfile.push_back(p+(new_pt-p) * 1.0f/(float)(split+1) * (float)(i+1));
+ }
+ }
+ mProfile.push_back(new_pt);
+ }
+
+ // If we're sliced, the profile is open.
+ if ((end - begin)*ang_scale < 0.99f)
+ {
+ if ((end - begin)*ang_scale > 0.5f)
+ {
+ mConcave = TRUE;
+ }
+ else
+ {
+ mConcave = FALSE;
+ }
+ mOpen = TRUE;
+ if (!isHollow())
+ {
+ // put center point if not hollow.
+ mProfile.push_back(LLVector3(0,0,0));
+ }
+ }
+ else
+ {
+ // The profile isn't open.
+ mOpen = FALSE;
+ mConcave = FALSE;
+ }
+
+ mTotal = mProfile.size();
+}
+
+void LLProfile::genNormals()
+{
+ S32 count = mProfile.size();
+
+ S32 outer_count;
+ if (mTotalOut)
+ {
+ outer_count = mTotalOut;
+ }
+ else
+ {
+ outer_count = mTotal / 2;
+ }
+
+ mEdgeNormals.resize(count * 2);
+ mEdgeCenters.resize(count * 2);
+ mNormals.resize(count);
+
+ LLVector2 pt0,pt1;
+
+ BOOL hollow;
+ hollow = isHollow();
+
+ S32 i0, i1, i2, i3, i4;
+
+ // Parametrically generate normal
+ for (i2 = 0; i2 < count; i2++)
+ {
+ mNormals[i2].mV[0] = mProfile[i2].mV[0];
+ mNormals[i2].mV[1] = mProfile[i2].mV[1];
+ if (hollow && (i2 >= outer_count))
+ {
+ mNormals[i2] *= -1.f;
+ }
+ if (mNormals[i2].magVec() < 0.001)
+ {
+ // Special case for point at center, get adjacent points.
+ i1 = (i2 - 1) >= 0 ? i2 - 1 : count - 1;
+ i0 = (i1 - 1) >= 0 ? i1 - 1 : count - 1;
+ i3 = (i2 + 1) < count ? i2 + 1 : 0;
+ i4 = (i3 + 1) < count ? i3 + 1 : 0;
+
+ pt0.setVec(mProfile[i1].mV[VX] + mProfile[i1].mV[VX] - mProfile[i0].mV[VX],
+ mProfile[i1].mV[VY] + mProfile[i1].mV[VY] - mProfile[i0].mV[VY]);
+ pt1.setVec(mProfile[i3].mV[VX] + mProfile[i3].mV[VX] - mProfile[i4].mV[VX],
+ mProfile[i3].mV[VY] + mProfile[i3].mV[VY] - mProfile[i4].mV[VY]);
+
+ mNormals[i2] = pt0 + pt1;
+ mNormals[i2] *= 0.5f;
+ }
+ mNormals[i2].normVec();
+ }
+
+ S32 num_normal_sets = isConcave() ? 2 : 1;
+ for (S32 normal_set = 0; normal_set < num_normal_sets; normal_set++)
+ {
+ S32 point_num;
+ for (point_num = 0; point_num < mTotal; point_num++)
+ {
+ LLVector3 point_1 = mProfile[point_num];
+ point_1.mV[VZ] = 0.f;
+
+ LLVector3 point_2;
+
+ if (isConcave() && normal_set == 0 && point_num == (mTotal - 1) / 2)
+ {
+ point_2 = mProfile[mTotal - 1];
+ }
+ else if (isConcave() && normal_set == 1 && point_num == mTotal - 1)
+ {
+ point_2 = mProfile[(mTotal - 1) / 2];
+ }
+ else
+ {
+ LLVector3 delta_pos;
+ S32 neighbor_point = (point_num + 1) % mTotal;
+ while(delta_pos.magVecSquared() < 0.01f * 0.01f)
+ {
+ point_2 = mProfile[neighbor_point];
+ delta_pos = point_2 - point_1;
+ neighbor_point = (neighbor_point + 1) % mTotal;
+ if (neighbor_point == point_num)
+ {
+ break;
+ }
+ }
+ }
+
+ point_2.mV[VZ] = 0.f;
+ LLVector3 face_normal = (point_2 - point_1) % LLVector3::z_axis;
+ face_normal.normVec();
+ mEdgeNormals[normal_set * count + point_num] = face_normal;
+ mEdgeCenters[normal_set * count + point_num] = lerp(point_1, point_2, 0.5f);
+ }
+ }
+}
+
+
+// Hollow is percent of the original bounding box, not of this particular
+// profile's geometry. Thus, a swept triangle needs lower hollow values than
+// a swept square.
+LLProfile::Face* LLProfile::addHole(BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split)
+{
+ // Note that addHole will NOT work for non-"circular" profiles, if we ever decide to use them.
+
+ // Total add has number of vertices on outside.
+ mTotalOut = mTotal;
+
+ // Why is the "bevel" parameter -1? DJS 04/05/02
+ genNGon(llfloor(sides),offset,-1, ang_scale, split);
+
+ Face *face = addFace(mTotalOut, mTotal-mTotalOut,0,LL_FACE_INNER_SIDE, flat);
+
+ LLVector3 pt[128];
+
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ pt[i] = mProfile[i] * box_hollow;
+ }
+
+ S32 j=mTotal-1;
+ for (S32 i=mTotalOut;i<mTotal;i++)
+ {
+ mProfile[i] = pt[j--];
+ }
+
+ for (S32 i=0;i<(S32)mFaces.size();i++)
+ {
+ if (mFaces[i].mCap)
+ {
+ mFaces[i].mCount *= 2;
+ }
+ }
+
+ return face;
+}
+
+BOOL LLProfile::generate(BOOL path_open,F32 detail, S32 split)
+{
+ if (!mDirty)
+ {
+ return FALSE;
+ }
+ mDirty = FALSE;
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating profile with LOD < MIN_LOD. CLAMPING" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mProfile.clear();
+ mFaces.clear();
+
+ // Generate the face data
+ S32 i;
+ F32 begin = mParams.getBegin();
+ F32 end = mParams.getEnd();
+ F32 hollow = mParams.getHollow();
+
+ // Quick validation to eliminate some server crashes.
+ if (begin > end - 0.01f)
+ {
+ llwarns << "LLProfile::generate() assertion failed (begin >= end)" << llendl;
+ return FALSE;
+ }
+
+ S32 face_num = 0;
+
+ switch (mParams.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ genNGon(4,-0.375, 0, 1, split);
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 4.f); i < llfloor(end * 4.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
+ }
+
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 4 to generate proper tex coords.
+ mProfile[i].mV[2] *= 4.f;
+ }
+
+ if (hollow)
+ {
+ switch (mParams.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_TRIANGLE:
+ // This offset is not correct, but we can't change it now... DK 11/17/04
+ addHole(TRUE, 3, -0.375f, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Compute actual detail levels for cubes
+ addHole(FALSE, MIN_DETAIL_FACES * detail, -0.375f, hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_SQUARE:
+ default:
+ addHole(TRUE, 4, -0.375f, hollow, 1.f, split);
+ break;
+ }
+ }
+
+ if (path_open) {
+ mFaces[0].mCount = mTotal;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ {
+ genNGon(3,0, 0, 1, split);
+ for (i = 0; i <(S32) mProfile.size(); i++)
+ {
+ // Scale by 3 to generate proper tex coords.
+ mProfile[i].mV[2] *= 3.f;
+ }
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+
+ for (i = llfloor(begin * 3.f); i < llfloor(end * 3.f + .999f); i++)
+ {
+ addFace((face_num++) * (split +1), split+2, 1, LL_FACE_OUTER_SIDE_0 << i, TRUE);
+ }
+ if (hollow)
+ {
+ // Swept triangles need smaller hollowness values,
+ // because the triangle doesn't fill the bounding box.
+ F32 triangle_hollow = hollow / 2.f;
+
+ switch (mParams.getCurveType() & LL_PCODE_HOLE_MASK)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ // TODO: Actually generate level of detail for triangles
+ addHole(FALSE, MIN_DETAIL_FACES * detail, 0, triangle_hollow, 1.f);
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(TRUE, 4, 0, triangle_hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_SAME:
+ case LL_PCODE_HOLE_TRIANGLE:
+ default:
+ addHole(TRUE, 3, 0, triangle_hollow, 1.f, split);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ F32 circle_detail = MIN_DETAIL_FACES * detail;
+ if (hollow)
+ {
+ hole_type = mParams.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides,
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 4.0f) * 4.0f;
+ }
+ }
+
+ //llinfos << "(CIRCLE) detail: " << detail << "; genNGon("
+ // << llfloor(circle_detail) << ")" << llendl;
+ genNGon(llfloor(circle_detail));
+ if (path_open)
+ {
+ addCap (LL_FACE_PATH_BEGIN);
+ }
+
+ if (mOpen && !hollow)
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(TRUE, 4, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(TRUE, 3, 0, hollow, 1.f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(FALSE, circle_detail, 0, hollow, 1.f);
+ break;
+ }
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ {
+ // If this has a square hollow, we should adjust the
+ // number of faces a bit so that the geometry lines up.
+ U8 hole_type=0;
+ // Number of faces is cut in half because it's only a half-circle.
+ F32 circle_detail = MIN_DETAIL_FACES * detail * 0.5f;
+ if (hollow)
+ {
+ hole_type = mParams.getCurveType() & LL_PCODE_HOLE_MASK;
+ if (hole_type == LL_PCODE_HOLE_SQUARE)
+ {
+ // Snap to the next multiple of four sides (div 2),
+ // so that corners line up.
+ circle_detail = llceil(circle_detail / 2.0f) * 2.0f;
+ }
+ }
+ genNGon(llfloor(circle_detail), 0.5f, 0.f, 0.5f);
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_BEGIN);
+ }
+ if (mOpen && !mParams.getHollow())
+ {
+ addFace(0,mTotal-1,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+ else
+ {
+ addFace(0,mTotal,0,LL_FACE_OUTER_SIDE_0, FALSE);
+ }
+
+ if (hollow)
+ {
+ switch (hole_type)
+ {
+ case LL_PCODE_HOLE_SQUARE:
+ addHole(TRUE, 2, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ addHole(TRUE, 3, 0.5f, hollow, 0.5f, split);
+ break;
+ case LL_PCODE_HOLE_CIRCLE:
+ case LL_PCODE_HOLE_SAME:
+ default:
+ addHole(FALSE, circle_detail, 0.5f, hollow, 0.5f);
+ break;
+ }
+ }
+
+ // Special case for openness of sphere
+ if ((mParams.getEnd() - mParams.getBegin()) < 1.f)
+ {
+ mOpen = TRUE;
+ }
+ else if (!hollow)
+ {
+ mOpen = FALSE;
+ mProfile.push_back(mProfile[0]);
+ mTotal++;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile: getCurveType()=" << mParams.getCurveType() << llendl;
+ break;
+ };
+
+ if (path_open)
+ {
+ addCap(LL_FACE_PATH_END); // bottom
+ }
+
+ if ( mOpen) // interior edge caps
+ {
+ addFace(mTotal-1, 2,0.5,LL_FACE_PROFILE_BEGIN, TRUE);
+
+ if (hollow)
+ {
+ addFace(mTotalOut-1, 2,0.5,LL_FACE_PROFILE_END, TRUE);
+ }
+ else
+ {
+ addFace(mTotal-2, 2,0.5,LL_FACE_PROFILE_END, TRUE);
+ }
+ }
+
+ //genNormals();
+
+ return TRUE;
+}
+
+
+
+BOOL LLProfileParams::importFile(FILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportFile(FILE *fp) const
+{
+ fprintf(fp,"\t\tprofile 0\n");
+ fprintf(fp,"\t\t{\n");
+ fprintf(fp,"\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp,"\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp,"\t\t\tend\t%g\n", getEnd());
+ fprintf(fp,"\t\t\thollow\t%g\n", getHollow());
+ fprintf(fp, "\t\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::importLegacyStream(std::istream& input_stream)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+ F32 tempF32;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("hollow",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setHollow(tempF32);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in profile import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLProfileParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\t\tprofile 0\n";
+ output_stream <<"\t\t{\n";
+ output_stream <<"\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream <<"\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream <<"\t\t\tend\t" << getEnd() << "\n";
+ output_stream <<"\t\t\thollow\t" << getHollow() << "\n";
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLProfileParams::asLLSD() const
+{
+ LLSD sd;
+
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["hollow"] = getHollow();
+ return sd;
+}
+
+bool LLProfileParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setHollow((F32)sd["hollow"].asReal());
+ return true;
+}
+
+void LLProfileParams::copyParams(const LLProfileParams &params)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setHollow(params.getHollow());
+}
+
+
+LLPath::~LLPath()
+{
+}
+
+void LLPath::genNGon(S32 sides, F32 startOff, F32 end_scale, F32 twist_scale)
+{
+ // Generates a circular path, starting at (1, 0, 0), counterclockwise along the xz plane.
+ const F32 tableScale[] = { 1, 1, 1, 0.5f, 0.707107f, 0.53f, 0.525f, 0.5f };
+
+ F32 revolutions = mParams.getRevolutions();
+ F32 skew = mParams.getSkew();
+ F32 skew_mag = fabs(skew);
+ F32 hole_x = mParams.getScaleX() * (1.0f - skew_mag);
+ F32 hole_y = mParams.getScaleY();
+
+ // Calculate taper begin/end for x,y (Negative means taper the beginning)
+ F32 taper_x_begin = 1.0f;
+ F32 taper_x_end = 1.0f - mParams.getTaperX();
+ F32 taper_y_begin = 1.0f;
+ F32 taper_y_end = 1.0f - mParams.getTaperY();
+
+ if ( taper_x_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_x_begin = 2.0f - taper_x_end;
+ taper_x_end = 1.0f;
+ }
+ if ( taper_y_end > 1.0f )
+ {
+ // Flip tapering.
+ taper_y_begin = 2.0f - taper_y_end;
+ taper_y_end = 1.0f;
+ }
+
+ // For spheres, the radius is usually zero.
+ F32 radius_start = 0.5f;
+ if (sides < 8)
+ {
+ radius_start = tableScale[sides];
+ }
+
+ // Scale the radius to take the hole size into account.
+ radius_start *= 1.0f - hole_y;
+
+ // Now check the radius offset to calculate the start,end radius. (Negative means
+ // decrease the start radius instead).
+ F32 radius_end = radius_start;
+ F32 radius_offset = mParams.getRadiusOffset();
+ if (radius_offset < 0.f)
+ {
+ radius_start *= 1.f + radius_offset;
+ }
+ else
+ {
+ radius_end *= 1.f - radius_offset;
+ }
+
+ // Is the path NOT a closed loop?
+ mOpen = ( (mParams.getEnd()*end_scale - mParams.getBegin() < 1.0f) ||
+ (skew_mag > 0.001f) ||
+ (fabs(taper_x_end - taper_x_begin) > 0.001f) ||
+ (fabs(taper_y_end - taper_y_begin) > 0.001f) ||
+ (fabs(radius_end - radius_start) > 0.001f) );
+
+ F32 ang, c, s;
+ LLQuaternion twist, qang;
+ PathPt *pt;
+ LLVector3 path_axis (1.f, 0.f, 0.f);
+ //LLVector3 twist_axis(0.f, 0.f, 1.f);
+ F32 twist_begin = mParams.getTwistBegin() * twist_scale;
+ F32 twist_end = mParams.getTwist() * twist_scale;
+
+ // We run through this once before the main loop, to make sure
+ // the path begins at the correct cut.
+ F32 step= 1.0f / sides;
+ F32 t = mParams.getBegin();
+ pt = vector_append(mPath, 1);
+ ang = 2.0f*F_PI*revolutions * t;
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+
+
+ pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ t+=step;
+
+ // Snap to a quantized parameter, so that cut does not
+ // affect most sample points.
+ t = ((S32)(t * sides)) / (F32)sides;
+
+ // Run through the non-cut dependent points.
+ while (t < mParams.getEnd())
+ {
+ pt = vector_append(mPath, 1);
+
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ t+=step;
+ }
+
+ // Make one final pass for the end cut.
+ t = mParams.getEnd();
+ pt = vector_append(mPath, 1);
+ ang = 2.0f*F_PI*revolutions * t;
+ c = cos(ang)*lerp(radius_start, radius_end, t);
+ s = sin(ang)*lerp(radius_start, radius_end, t);
+
+ pt->mPos.setVec(0 + lerp(0,mParams.getShear().mV[0],s)
+ + lerp(-skew ,skew, t) * 0.5f,
+ c + lerp(0,mParams.getShear().mV[1],s),
+ s);
+ pt->mScale.mV[VX] = hole_x * lerp(taper_x_begin, taper_x_end, t);
+ pt->mScale.mV[VY] = hole_y * lerp(taper_y_begin, taper_y_end, t);
+ pt->mTexT = t;
+
+ // Twist rotates the path along the x,y plane (I think) - DJS 04/05/02
+ twist.setQuat (lerp(twist_begin,twist_end,t) * 2.f * F_PI - F_PI,0,0,1);
+ // Rotate the point around the circle's center.
+ qang.setQuat (ang,path_axis);
+ pt->mRot = twist * qang;
+
+ mTotal = mPath.size();
+}
+
+const LLVector2 LLPathParams::getBeginScale() const
+{
+ LLVector2 begin_scale(1.f, 1.f);
+ if (getScaleX() > 1)
+ {
+ begin_scale.mV[0] = 2-getScaleX();
+ }
+ if (getScaleY() > 1)
+ {
+ begin_scale.mV[1] = 2-getScaleY();
+ }
+ return begin_scale;
+}
+
+const LLVector2 LLPathParams::getEndScale() const
+{
+ LLVector2 end_scale(1.f, 1.f);
+ if (getScaleX() < 1)
+ {
+ end_scale.mV[0] = getScaleX();
+ }
+ if (getScaleY() < 1)
+ {
+ end_scale.mV[1] = getScaleY();
+ }
+ return end_scale;
+}
+
+BOOL LLPath::generate(F32 detail, S32 split)
+{
+ if (!mDirty)
+ {
+ return FALSE;
+ }
+
+ if (detail < MIN_LOD)
+ {
+ llinfos << "Generating path with LOD < MIN! Clamping to 1" << llendl;
+ detail = MIN_LOD;
+ }
+
+ mDirty = FALSE;
+ S32 np = 2; // hardcode for line
+
+ mPath.clear();
+ mOpen = TRUE;
+
+ // Is this 0xf0 mask really necessary? DK 03/02/05
+ switch (mParams.getCurveType() & 0xf0)
+ {
+ default:
+ case LL_PCODE_PATH_LINE:
+ {
+ // Take the begin/end twist into account for detail.
+ np = llfloor(fabs(mParams.getTwistBegin() - mParams.getTwist()) * 3.5f * (detail-0.5f)) + 2;
+ if (np < split+2)
+ {
+ np = split+2;
+ }
+
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ LLVector2 start_scale = mParams.getBeginScale();
+ LLVector2 end_scale = mParams.getEndScale();
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = lerp(mParams.getBegin(),mParams.getEnd(),(F32)i * mStep);
+ mPath[i].mPos.setVec(lerp(0,mParams.getShear().mV[0],t),
+ lerp(0,mParams.getShear().mV[1],t),
+ t - 0.5f);
+ mPath[i].mRot.setQuat(lerp(F_PI * mParams.getTwistBegin(),F_PI * mParams.getTwist(),t),0,0,1);
+ mPath[i].mScale.mV[0] = lerp(start_scale.mV[0],end_scale.mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(start_scale.mV[1],end_scale.mV[1],t);
+ mPath[i].mTexT = t;
+ }
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE:
+ {
+ // Increase the detail as the revolutions and twist increase.
+ F32 twist_mag = fabs(mParams.getTwistBegin() - mParams.getTwist());
+ genNGon(llfloor(llfloor((MIN_DETAIL_FACES * detail + twist_mag * 3.5f * (detail-0.5f))) * mParams.getRevolutions()));
+ }
+ break;
+
+ case LL_PCODE_PATH_CIRCLE2:
+ {
+ if (mParams.getEnd() - mParams.getBegin() >= 0.99f &&
+ mParams.getScaleX() >= .99f)
+ {
+ mOpen = FALSE;
+ }
+
+ //genNGon(llfloor(MIN_DETAIL_FACES * detail), 4.f, 0.f);
+ genNGon(llfloor(MIN_DETAIL_FACES * detail));
+
+ F32 t = 0.f;
+ F32 tStep = 1.0f / mPath.size();
+
+ F32 toggle = 0.5f;
+ for (S32 i=0;i<(S32)mPath.size();i++)
+ {
+ mPath[i].mPos.mV[0] = toggle;
+ if (toggle == 0.5f)
+ toggle = -0.5f;
+ else
+ toggle = 0.5f;
+ t += tStep;
+ }
+ }
+
+ break;
+
+ case LL_PCODE_PATH_TEST:
+
+ np = 5;
+ mStep = 1.0f / (np-1);
+
+ mPath.resize(np);
+
+ for (S32 i=0;i<np;i++)
+ {
+ F32 t = (F32)i * mStep;
+ mPath[i].mPos.setVec(0,
+ lerp(0, -sin(F_PI*mParams.getTwist()*t)*0.5f,t),
+ lerp(-0.5, cos(F_PI*mParams.getTwist()*t)*0.5f,t));
+ mPath[i].mScale.mV[0] = lerp(1,mParams.getScale().mV[0],t);
+ mPath[i].mScale.mV[1] = lerp(1,mParams.getScale().mV[1],t);
+ mPath[i].mTexT = t;
+ mPath[i].mRot.setQuat(F_PI * mParams.getTwist() * t,1,0,0);
+ }
+
+ break;
+ };
+
+ if (mParams.getTwist() != mParams.getTwistBegin()) mOpen = TRUE;
+
+ //if ((int(fabsf(mParams.getTwist() - mParams.getTwistBegin())*100))%100 != 0) {
+ // mOpen = TRUE;
+ //}
+
+ return TRUE;
+}
+
+BOOL LLDynamicPath::generate(F32 detail, S32 split)
+{
+ mOpen = TRUE; // Draw end caps
+ if (getPathLength() == 0)
+ {
+ // Path hasn't been generated yet.
+ // Some algorithms later assume at least TWO path points.
+ resizePath(2);
+ for (U32 i = 0; i < 2; i++)
+ {
+ mPath[i].mPos.setVec(0, 0, 0);
+ mPath[i].mRot.setQuat(0, 0, 0);
+ mPath[i].mScale.setVec(1, 1);
+ mPath[i].mTexT = 0;
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLPathParams::importFile(FILE *fp)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportFile(FILE *fp) const
+{
+ fprintf(fp, "\t\tpath 0\n");
+ fprintf(fp, "\t\t{\n");
+ fprintf(fp, "\t\t\tcurve\t%d\n", getCurveType());
+ fprintf(fp, "\t\t\tbegin\t%g\n", getBegin());
+ fprintf(fp, "\t\t\tend\t%g\n", getEnd());
+ fprintf(fp, "\t\t\tscale_x\t%g\n", getScaleX() );
+ fprintf(fp, "\t\t\tscale_y\t%g\n", getScaleY() );
+ fprintf(fp, "\t\t\tshear_x\t%g\n", getShearX() );
+ fprintf(fp, "\t\t\tshear_y\t%g\n", getShearY() );
+ fprintf(fp,"\t\t\ttwist\t%g\n", getTwist());
+
+ fprintf(fp,"\t\t\ttwist_begin\t%g\n", getTwistBegin());
+ fprintf(fp,"\t\t\tradius_offset\t%g\n", getRadiusOffset());
+ fprintf(fp,"\t\t\ttaper_x\t%g\n", getTaperX());
+ fprintf(fp,"\t\t\ttaper_y\t%g\n", getTaperY());
+ fprintf(fp,"\t\t\trevolutions\t%g\n", getRevolutions());
+ fprintf(fp,"\t\t\tskew\t%g\n", getSkew());
+
+ fprintf(fp, "\t\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLPathParams::importLegacyStream(std::istream& input_stream)
+{
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ char valuestr[256];
+ keyword[0] = 0;
+ valuestr[0] = 0;
+
+ F32 tempF32;
+ F32 x, y;
+ U32 tempU32;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("curve", keyword))
+ {
+ sscanf(valuestr,"%d",&tempU32);
+ setCurveType((U8) tempU32);
+ }
+ else if (!strcmp("begin",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setBegin(tempF32);
+ }
+ else if (!strcmp("end",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setEnd(tempF32);
+ }
+ else if (!strcmp("scale",keyword))
+ {
+ // Legacy for one dimensional scale per path
+ sscanf(valuestr,"%g",&tempF32);
+ setScale(tempF32, tempF32);
+ }
+ else if (!strcmp("scale_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setScaleX(x);
+ }
+ else if (!strcmp("scale_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setScaleY(y);
+ }
+ else if (!strcmp("shear_x", keyword))
+ {
+ sscanf(valuestr, "%g", &x);
+ setShearX(x);
+ }
+ else if (!strcmp("shear_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setShearY(y);
+ }
+ else if (!strcmp("twist",keyword))
+ {
+ sscanf(valuestr,"%g",&tempF32);
+ setTwist(tempF32);
+ }
+ else if (!strcmp("twist_begin", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTwistBegin(y);
+ }
+ else if (!strcmp("radius_offset", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRadiusOffset(y);
+ }
+ else if (!strcmp("taper_x", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperX(y);
+ }
+ else if (!strcmp("taper_y", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setTaperY(y);
+ }
+ else if (!strcmp("revolutions", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setRevolutions(y);
+ }
+ else if (!strcmp("skew", keyword))
+ {
+ sscanf(valuestr, "%g", &y);
+ setSkew(y);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << " in path import" << llendl;
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLPathParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream << "\t\tpath 0\n";
+ output_stream << "\t\t{\n";
+ output_stream << "\t\t\tcurve\t" << (S32) getCurveType() << "\n";
+ output_stream << "\t\t\tbegin\t" << getBegin() << "\n";
+ output_stream << "\t\t\tend\t" << getEnd() << "\n";
+ output_stream << "\t\t\tscale_x\t" << getScaleX() << "\n";
+ output_stream << "\t\t\tscale_y\t" << getScaleY() << "\n";
+ output_stream << "\t\t\tshear_x\t" << getShearX() << "\n";
+ output_stream << "\t\t\tshear_y\t" << getShearY() << "\n";
+ output_stream <<"\t\t\ttwist\t" << getTwist() << "\n";
+
+ output_stream <<"\t\t\ttwist_begin\t" << getTwistBegin() << "\n";
+ output_stream <<"\t\t\tradius_offset\t" << getRadiusOffset() << "\n";
+ output_stream <<"\t\t\ttaper_x\t" << getTaperX() << "\n";
+ output_stream <<"\t\t\ttaper_y\t" << getTaperY() << "\n";
+ output_stream <<"\t\t\trevolutions\t" << getRevolutions() << "\n";
+ output_stream <<"\t\t\tskew\t" << getSkew() << "\n";
+
+ output_stream << "\t\t}\n";
+ return TRUE;
+}
+
+LLSD LLPathParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["curve"] = getCurveType();
+ sd["begin"] = getBegin();
+ sd["end"] = getEnd();
+ sd["scale_x"] = getScaleX();
+ sd["scale_y"] = getScaleY();
+ sd["shear_x"] = getShearX();
+ sd["shear_y"] = getShearY();
+ sd["twist"] = getTwist();
+ sd["twist_begin"] = getTwistBegin();
+ sd["radius_offset"] = getRadiusOffset();
+ sd["taper_x"] = getTaperX();
+ sd["taper_y"] = getTaperY();
+ sd["revolutions"] = getRevolutions();
+ sd["skew"] = getSkew();
+
+ return sd;
+}
+
+bool LLPathParams::fromLLSD(LLSD& sd)
+{
+ setCurveType(sd["curve"].asInteger());
+ setBegin((F32)sd["begin"].asReal());
+ setEnd((F32)sd["end"].asReal());
+ setScaleX((F32)sd["scale_x"].asReal());
+ setScaleY((F32)sd["scale_y"].asReal());
+ setShearX((F32)sd["shear_x"].asReal());
+ setShearY((F32)sd["shear_y"].asReal());
+ setTwist((F32)sd["twist"].asReal());
+ setTwistBegin((F32)sd["twist_begin"].asReal());
+ setRadiusOffset((F32)sd["radius_offset"].asReal());
+ setTaperX((F32)sd["taper_x"].asReal());
+ setTaperY((F32)sd["taper_y"].asReal());
+ setRevolutions((F32)sd["revolutions"].asReal());
+ setSkew((F32)sd["skew"].asReal());
+ return true;
+}
+
+void LLPathParams::copyParams(const LLPathParams &params)
+{
+ setCurveType(params.getCurveType());
+ setBegin(params.getBegin());
+ setEnd(params.getEnd());
+ setScale(params.getScaleX(), params.getScaleY() );
+ setShear(params.getShearX(), params.getShearY() );
+ setTwist(params.getTwist());
+ setTwistBegin(params.getTwistBegin());
+ setRadiusOffset(params.getRadiusOffset());
+ setTaper( params.getTaperX(), params.getTaperY() );
+ setRevolutions(params.getRevolutions());
+ setSkew(params.getSkew());
+}
+
+LLProfile::~LLProfile()
+{
+
+}
+
+
+S32 LLVolume::mNumMeshPoints = 0;
+
+LLVolume::LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face, const BOOL is_unique) : mParams(params)
+{
+ mUnique = is_unique;
+ mFaceMask = 0x0;
+ mDetail = detail;
+ // set defaults
+ if (mParams.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ mPathp = new LLDynamicPath(mParams.getPathParams());
+ }
+ else
+ {
+ mPathp = new LLPath(mParams.getPathParams());
+ }
+ mProfilep = new LLProfile(mParams.getProfileParams());
+
+ mNumVolumeFaces = 0;
+ mVolumeFaces = NULL;
+ mGenerateSingleFace = generate_single_face;
+
+ generate();
+ createVolumeFaces();
+}
+
+void LLVolume::regen()
+{
+ generate();
+ createVolumeFaces();
+}
+
+LLVolume::~LLVolume()
+{
+ mNumMeshPoints -= mMesh.size();
+ delete mPathp;
+ delete mProfilep;
+ delete[] mVolumeFaces;
+
+ mPathp = NULL;
+ mProfilep = NULL;
+ mVolumeFaces = NULL;
+}
+
+BOOL LLVolume::generate()
+{
+ //Added 10.03.05 Dave Parks
+ // Split is a parameter to LLProfile::generate that tesselates edges on the profile
+ // to prevent lighting and texture interpolation errors on triangles that are
+ // stretched due to twisting or scaling on the path.
+ S32 split = (S32) ((mDetail)*0.66f);
+
+ if (mPathp->mParams.getCurveType() == LL_PCODE_PATH_LINE &&
+ (mPathp->mParams.getScale().mV[0] != 1.0f ||
+ mPathp->mParams.getScale().mV[1] != 1.0f) &&
+ (mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_SQUARE ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_ISOTRI ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_EQUALTRI ||
+ mProfilep->mParams.getCurveType() == LL_PCODE_PROFILE_RIGHTTRI))
+ {
+ split = 0;
+ }
+
+ mLODScaleBias.setVec(0.5f, 0.5f, 0.5f);
+
+ F32 profile_detail = mDetail;
+ F32 path_detail = mDetail;
+
+ U8 path_type = mPathp->mParams.getCurveType();
+ U8 profile_type = mProfilep->mParams.getCurveType();
+
+ if (path_type == LL_PCODE_PATH_LINE && profile_type == LL_PCODE_PROFILE_CIRCLE)
+ { //cylinders don't care about Z-Axis
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.0f);
+ }
+ else if (path_type == LL_PCODE_PATH_CIRCLE)
+ {
+ mLODScaleBias.setVec(0.6f, 0.6f, 0.6f);
+ }
+
+ BOOL regenPath = mPathp->generate(path_detail, split);
+ BOOL regenProf = mProfilep->generate(mPathp->isOpen(),profile_detail, split);
+
+ if (regenPath || regenProf )
+ {
+ mNumMeshPoints -= mMesh.size();
+ mMesh.resize(mProfilep->mProfile.size() * mPathp->mPath.size());
+ mNumMeshPoints += mMesh.size();
+
+ S32 s = 0, t=0;
+ S32 sizeS = mPathp->mPath.size();
+ S32 sizeT = mProfilep->mProfile.size();
+ S32 line = 0;
+
+ //generate vertex positions
+
+ // Run along the path.
+ while (s < sizeS)
+ {
+ LLVector2 scale = mPathp->mPath[s].mScale;
+ LLQuaternion rot = mPathp->mPath[s].mRot;
+
+ t = 0;
+ // Run along the profile.
+ while (t < sizeT)
+ {
+ S32 i = t + line;
+ Point& pt = mMesh[i];
+
+ pt.mPos.mV[0] = mProfilep->mProfile[t].mV[0] * scale.mV[0];
+ pt.mPos.mV[1] = mProfilep->mProfile[t].mV[1] * scale.mV[1];
+ pt.mPos.mV[2] = 0.0f;
+ pt.mPos = pt.mPos * rot;
+ pt.mPos += mPathp->mPath[s].mPos;
+ t++;
+ }
+ line += sizeT;
+ s++;
+ }
+
+ for (S32 i = 0; i < (S32)mProfilep->mFaces.size(); i++)
+ {
+ mFaceMask |= mProfilep->mFaces[i].mFaceID;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLVolume::createVolumeFaces()
+{
+ S32 i;
+
+ if (mVolumeFaces != NULL)
+ {
+ delete[] mVolumeFaces;
+ mVolumeFaces = NULL;
+ }
+
+ if (mGenerateSingleFace)
+ {
+ mNumVolumeFaces = 0;
+ }
+ else
+ {
+ S32 num_faces = getNumFaces();
+ mNumVolumeFaces = num_faces;
+ mVolumeFaces = new LLVolumeFace[num_faces];
+ // Initialize volume faces with parameter data
+ for (i = 0; i < num_faces; i++)
+ {
+ LLVolumeFace &vf = mVolumeFaces[i];
+ LLProfile::Face &face = mProfilep->mFaces[i];
+ vf.mVolumep = this;
+ vf.mBeginS = face.mIndex;
+ vf.mNumS = face.mCount;
+ vf.mBeginT = 0;
+ vf.mNumT= getPath().mPath.size();
+ vf.mID = i;
+
+ // Set the type mask bits correctly
+ if (mProfilep->isHollow())
+ {
+ vf.mTypeMask |= LLVolumeFace::HOLLOW_MASK;
+ }
+ if (mProfilep->isOpen())
+ {
+ vf.mTypeMask |= LLVolumeFace::OPEN_MASK;
+ }
+ if (face.mCap)
+ {
+ vf.mTypeMask |= LLVolumeFace::CAP_MASK;
+ if (face.mFaceID == LL_FACE_PATH_BEGIN)
+ {
+ vf.mTypeMask |= LLVolumeFace::TOP_MASK;
+ }
+ else
+ {
+ llassert(face.mFaceID == LL_FACE_PATH_END);
+ vf.mTypeMask |= LLVolumeFace::BOTTOM_MASK;
+ }
+ }
+ else if (face.mFaceID & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK | LLVolumeFace::END_MASK;
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::SIDE_MASK;
+ if (face.mFlat)
+ {
+ vf.mTypeMask |= LLVolumeFace::FLAT_MASK;
+ }
+ if (face.mFaceID & LL_FACE_INNER_SIDE)
+ {
+ vf.mTypeMask |= LLVolumeFace::INNER_MASK;
+ if (face.mFlat && vf.mNumS > 2)
+ { //flat inner faces have to copy vert normals
+ vf.mNumS = vf.mNumS*2;
+ }
+ }
+ else
+ {
+ vf.mTypeMask |= LLVolumeFace::OUTER_MASK;
+ }
+ }
+ }
+
+ for (i = 0; i < mNumVolumeFaces; i++)
+ {
+ mVolumeFaces[i].create();
+ }
+ }
+
+ mBounds[1] = LLVector3(0,0,0);
+ mBounds[0] = LLVector3(512,512,512);
+}
+
+
+BOOL LLVolume::isCap(S32 face)
+{
+ return mProfilep->mFaces[face].mCap;
+}
+
+BOOL LLVolume::isFlat(S32 face)
+{
+ return mProfilep->mFaces[face].mFlat;
+}
+
+
+bool LLVolumeParams::operator==(const LLVolumeParams &params) const
+{
+ return (getPathParams() == params.getPathParams()) &&
+ (getProfileParams() == params.getProfileParams());
+}
+
+bool LLVolumeParams::operator!=(const LLVolumeParams &params) const
+{
+ return (getPathParams() != params.getPathParams()) ||
+ (getProfileParams() != params.getProfileParams());
+}
+
+bool LLVolumeParams::operator<(const LLVolumeParams &params) const
+{
+ if( getPathParams() != params.getPathParams() )
+ {
+ return getPathParams() < params.getPathParams();
+ }
+ else
+ {
+ return getProfileParams() < params.getProfileParams();
+ }
+}
+
+void LLVolumeParams::copyParams(const LLVolumeParams &params)
+{
+ mProfileParams.copyParams(params.mProfileParams);
+ mPathParams.copyParams(params.mPathParams);
+}
+
+// return true if in range (or nearly so)
+static bool limit_range(F32& v, F32 min, F32 max)
+{
+ F32 min_delta = v - min;
+ if (min_delta < 0.f)
+ {
+ v = min;
+ if (!is_approx_zero(min_delta))
+ return false;
+ }
+ F32 max_delta = max - v;
+ if (max_delta < 0.f)
+ {
+ v = max;
+ if (!is_approx_zero(max_delta))
+ return false;
+ }
+ return true;
+}
+
+bool LLVolumeParams::setBeginAndEndS(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
+
+ // Now set them.
+ mProfileParams.setBegin(begin);
+ mProfileParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setBeginAndEndT(const F32 b, const F32 e)
+{
+ bool valid = true;
+
+ // First, clamp to valid ranges.
+ F32 begin = b;
+ valid &= limit_range(begin, 0.f, 1.f - MIN_CUT_DELTA);
+
+ F32 end = e;
+ valid &= limit_range(end, MIN_CUT_DELTA, 1.f);
+
+ valid &= limit_range(begin, 0.f, end - MIN_CUT_DELTA);
+
+ // Now set them.
+ mPathParams.setBegin(begin);
+ mPathParams.setEnd(end);
+
+ return valid;
+}
+
+bool LLVolumeParams::setHollow(const F32 h)
+{
+ // Validate the hollow based on path and profile.
+ U8 profile = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = mProfileParams.getCurveType() & LL_PCODE_HOLE_MASK;
+
+ F32 max_hollow = HOLLOW_MAX;
+
+ // Only square holes have trouble.
+ if (LL_PCODE_HOLE_SQUARE == hole_type)
+ {
+ switch(profile)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ max_hollow = HOLLOW_MAX_SQUARE;
+ }
+ }
+
+ F32 hollow = h;
+ bool valid = limit_range(hollow, HOLLOW_MIN, max_hollow);
+ mProfileParams.setHollow(hollow);
+
+ return valid;
+}
+
+bool LLVolumeParams::setTwistBegin(const F32 b)
+{
+ F32 twist_begin = b;
+ bool valid = limit_range(twist_begin, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistBegin(twist_begin);
+ return valid;
+}
+
+bool LLVolumeParams::setTwistEnd(const F32 e)
+{
+ F32 twist_end = e;
+ bool valid = limit_range(twist_end, TWIST_MIN, TWIST_MAX);
+ mPathParams.setTwistEnd(twist_end);
+ return valid;
+}
+
+bool LLVolumeParams::setRatio(const F32 x, const F32 y)
+{
+ F32 min_x = RATIO_MIN;
+ F32 max_x = RATIO_MAX;
+ F32 min_y = RATIO_MIN;
+ F32 max_y = RATIO_MAX;
+ // If this is a circular path (and not a sphere) then 'ratio' is actually hole size.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PATH_CIRCLE == path_type &&
+ LL_PCODE_PROFILE_CIRCLE_HALF != profile_type)
+ {
+ // Holes are more restricted...
+ min_x = HOLE_X_MIN;
+ max_x = HOLE_X_MAX;
+ min_y = HOLE_Y_MIN;
+ max_y = HOLE_Y_MAX;
+ }
+
+ F32 ratio_x = x;
+ bool valid = limit_range(ratio_x, min_x, max_x);
+ F32 ratio_y = y;
+ valid &= limit_range(ratio_y, min_y, max_y);
+
+ mPathParams.setScale(ratio_x, ratio_y);
+
+ return valid;
+}
+
+bool LLVolumeParams::setShear(const F32 x, const F32 y)
+{
+ F32 shear_x = x;
+ bool valid = limit_range(shear_x, SHEAR_MIN, SHEAR_MAX);
+ F32 shear_y = y;
+ valid &= limit_range(shear_y, SHEAR_MIN, SHEAR_MAX);
+ mPathParams.setShear(shear_x, shear_y);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperX(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperX(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setTaperY(const F32 v)
+{
+ F32 taper = v;
+ bool valid = limit_range(taper, TAPER_MIN, TAPER_MAX);
+ mPathParams.setTaperY(taper);
+ return valid;
+}
+
+bool LLVolumeParams::setRevolutions(const F32 r)
+{
+ F32 revolutions = r;
+ bool valid = limit_range(revolutions, REV_MIN, REV_MAX);
+ mPathParams.setRevolutions(revolutions);
+ return valid;
+}
+
+bool LLVolumeParams::setRadiusOffset(const F32 offset)
+{
+ bool valid = true;
+
+ // If this is a sphere, just set it to 0 and get out.
+ U8 path_type = mPathParams.getCurveType();
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type ||
+ LL_PCODE_PATH_CIRCLE != path_type )
+ {
+ mPathParams.setRadiusOffset(0.f);
+ return true;
+ }
+
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_offset = offset;
+ F32 taper_y = getTaperY();
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(getRatioY());
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+
+ // Enforce the maximum magnitude.
+ F32 delta = max_radius_mag - radius_mag;
+ if (delta < 0.f)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ valid = is_approx_zero(delta);
+ }
+
+ mPathParams.setRadiusOffset(radius_offset);
+ return valid;
+}
+
+bool LLVolumeParams::setSkew(const F32 skew_value)
+{
+ bool valid = true;
+
+ // Check the skew value against the revolutions.
+ F32 skew = llclamp(skew_value, SKEW_MIN, SKEW_MAX);
+ F32 skew_mag = fabs(skew);
+ F32 revolutions = getRevolutions();
+ F32 scale_x = getRatioX();
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ F32 delta = skew_mag - min_skew_mag;
+ if (delta < 0.f)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ valid = is_approx_zero(delta);
+ }
+
+ mPathParams.setSkew(skew);
+ return valid;
+}
+
+bool LLVolumeParams::setType(U8 profile, U8 path)
+{
+ bool result = true;
+ // First, check profile and path for validity.
+ U8 profile_type = profile & LL_PCODE_PROFILE_MASK;
+ U8 hole_type = (profile & LL_PCODE_HOLE_MASK) >> 4;
+ U8 path_type = path >> 4;
+
+ if (profile_type > LL_PCODE_PROFILE_MAX)
+ {
+ // Bad profile. Make it square.
+ profile = LL_PCODE_PROFILE_SQUARE;
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad profile type (" << profile_type
+ << ") to be LL_PCODE_PROFILE_SQUARE" << llendl;
+ }
+ else if (hole_type > LL_PCODE_HOLE_MAX)
+ {
+ // Bad hole. Make it the same.
+ profile = profile_type;
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad hole type (" << hole_type
+ << ") to be LL_PCODE_HOLE_SAME" << llendl;
+ }
+
+ if (path_type < LL_PCODE_PATH_MIN ||
+ path_type > LL_PCODE_PATH_MAX)
+ {
+ // Bad path. Make it linear.
+ result = false;
+ llwarns << "LLVolumeParams::setType changing bad path (" << path
+ << ") to be LL_PCODE_PATH_LINE" << llendl;
+ path = LL_PCODE_PATH_LINE;
+ }
+
+ mProfileParams.setCurveType(profile);
+ mPathParams.setCurveType(path);
+ return result;
+}
+
+// static
+bool LLVolumeParams::validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+ U8 path_curve, F32 path_begin, F32 path_end,
+ F32 scx, F32 scy, F32 shx, F32 shy,
+ F32 twistend, F32 twistbegin, F32 radiusoffset,
+ F32 tx, F32 ty, F32 revolutions, F32 skew)
+{
+ LLVolumeParams test_params;
+ if (!test_params.setType (prof_curve, path_curve))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndS (prof_begin, prof_end))
+ {
+ return false;
+ }
+ if (!test_params.setBeginAndEndT (path_begin, path_end))
+ {
+ return false;
+ }
+ if (!test_params.setHollow (hollow))
+ {
+ return false;
+ }
+ if (!test_params.setTwistBegin (twistbegin))
+ {
+ return false;
+ }
+ if (!test_params.setTwistEnd (twistend))
+ {
+ return false;
+ }
+ if (!test_params.setRatio (scx, scy))
+ {
+ return false;
+ }
+ if (!test_params.setShear (shx, shy))
+ {
+ return false;
+ }
+ if (!test_params.setTaper (tx, ty))
+ {
+ return false;
+ }
+ if (!test_params.setRevolutions (revolutions))
+ {
+ return false;
+ }
+ if (!test_params.setRadiusOffset (radiusoffset))
+ {
+ return false;
+ }
+ if (!test_params.setSkew (skew))
+ {
+ return false;
+ }
+ return true;
+}
+
+#define MAX_INDEX 10000
+S32 *LLVolume::getTriangleIndices(U32 &num_indices) const
+{
+ S32 index[MAX_INDEX];
+ S32 count = 0;
+ S32 *indices = NULL;
+
+ // Let's do this totally diffently, as we don't care about faces...
+ // Counter-clockwise triangles are forward facing...
+
+ BOOL open = getProfile().isOpen();
+ BOOL hollow = getProfile().isHollow();
+ BOOL path_open = getPath().isOpen();
+ S32 size_s, size_s_out, size_t;
+ S32 s, t, i;
+ size_s = getProfile().getTotal();
+ size_s_out = getProfile().getTotalOut();
+ size_t = getPath().mPath.size();
+
+ if (open)
+ {
+ if (hollow)
+ {
+ // Open hollow -- much like the closed solid, except we
+ // we need to stitch up the gap between s=0 and s=size_s-1
+
+ if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
+ goto noindices;
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // The outer face, first cut, and inner face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = s + t*size_s; // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = s + (t+1)*size_s; // x,y+1
+
+ index[count++] = s + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Open solid
+
+ if ( (size_t - 1) * (((size_s -1) * 6) + 6) >= MAX_INDEX)
+ goto noindices;
+
+ for (t = 0; t < size_t - 1; t++)
+ {
+ // Outer face + 1 cut face
+ for (s = 0; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+
+ // The other cut face
+ index[count++] = (size_s - 1) + (t*size_s); // x,y
+ index[count++] = 0 + t*size_s; // x+1,y
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+
+ index[count++] = (size_s - 1) + (t+1)*size_s; // x,y+1
+ index[count++] = 0 + (t*size_s); // x+1,y
+ index[count++] = 0 + (t+1)*size_s; // x+1,y+1
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ if ( count + (size_s - 2) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 0; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = size_s - 1;
+ }
+
+ // We've got a top cap
+ S32 offset = (size_t - 1)*size_s;
+ if ( count + (size_s - 2) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 0; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset + size_s - 1;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+ }
+ else if (hollow)
+ {
+ // Closed hollow
+ // Outer face
+
+ if ( (size_t - 1) * (size_s_out - 1) * 6 >= MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s_out - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Inner face
+ // Invert facing from outer face
+ if ( count + (size_t - 1) * ((size_s - 1) - size_s_out) * 6 >= MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = size_s_out; s < size_s - 1; s++)
+ {
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + 1 + size_s; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // Top cap
+ S32 pt1 = 0;
+ S32 pt2 = size_s-1;
+ S32 i = (size_t - 1)*size_s;
+
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt1 + 1 + i;
+ index[count++] = pt2 + i;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1 + i;
+ index[count++] = pt2 - 1 + i;
+ index[count++] = pt2 + i;
+ pt2--;
+ }
+ }
+
+ // Bottom cap
+ pt1 = 0;
+ pt2 = size_s-1;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = getProfile().mProfile[pt1];
+ LLVector3 p2 = getProfile().mProfile[pt2];
+ LLVector3 pa = getProfile().mProfile[pt1+1];
+ LLVector3 pb = getProfile().mProfile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ if (count + 3 >= MAX_INDEX)
+ goto noindices;
+ index[count++] = pt1;
+ index[count++] = pt2;
+ index[count++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Closed solid. Easy case.
+ if ( (size_t - 1) * (size_s - 1) * 6 > MAX_INDEX)
+ goto noindices;
+ for (t = 0; t < size_t - 1; t++)
+ {
+ for (s = 0; s < size_s - 1; s++)
+ {
+ // Should wrap properly, but for now...
+ i = s + t*size_s;
+
+ index[count++] = i; // x,y
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s; // x,y+1
+
+ index[count++] = i + size_s; // x,y+1
+ index[count++] = i + 1; // x+1,y
+ index[count++] = i + size_s + 1; // x+1,y+1
+ }
+ }
+
+ // Do the top and bottom caps, if necessary
+ if (path_open)
+ {
+ // bottom cap
+ if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 1; s < size_s - 2; s++)
+ {
+ index[count++] = s+1;
+ index[count++] = s;
+ index[count++] = 0;
+ }
+
+ // top cap
+ S32 offset = (size_t - 1)*size_s;
+ if ( count + (size_s - 2 - 1) * 3 >= MAX_INDEX)
+ goto noindices;
+ for (s = 1; s < size_s - 2; s++)
+ {
+ // Inverted ordering from bottom cap.
+ index[count++] = offset;
+ index[count++] = offset + s;
+ index[count++] = offset + s + 1;
+ }
+ }
+ }
+
+#if 0
+ S32 num_vertices = mMesh.size();
+ for (i = 0; i < count; i+=3)
+ {
+ llinfos << index[i] << ":" << index[i+1] << ":" << index[i+2] << llendl;
+ llassert(index[i] < num_vertices);
+ llassert(index[i+1] < num_vertices);
+ llassert(index[i+2] < num_vertices);
+ }
+#endif
+
+ indices = new S32[count];
+noindices:
+ if (!indices)
+ {
+ llwarns << "Couldn't allocate triangle indices" << llendl;
+ num_indices = 0;
+ return NULL;
+ }
+ num_indices = count;
+ memcpy(indices, index, count * sizeof(S32));
+ return indices;
+}
+
+//-----------------------------------------------------------------------------
+// generateSilhouetteVertices()
+//-----------------------------------------------------------------------------
+void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices,
+ std::vector<LLVector3> &normals,
+ std::vector<S32> &segments,
+ const LLVector3& obj_cam_vec,
+ const LLMatrix4& mat,
+ const LLMatrix3& norm_mat)
+{
+ vertices.clear();
+ normals.clear();
+ segments.clear();
+
+ //for each face
+ for (S32 i = 0; i < getNumFaces(); i++) {
+ LLVolumeFace face = this->getVolumeFace(i);
+
+ if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) {
+
+ }
+ else {
+
+ //==============================================
+ //DEBUG draw edge map instead of silhouette edge
+ //==============================================
+
+#if DEBUG_SILHOUETTE_EDGE_MAP
+
+ //for each triangle
+ U32 count = face.mIndices.size();
+ for (U32 j = 0; j < count/3; j++) {
+ //get vertices
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ //get current face center
+ LLVector3 cCenter = (face.mVertices[v1].mPosition +
+ face.mVertices[v2].mPosition +
+ face.mVertices[v3].mPosition) / 3.0f;
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 nIndex = face.mEdge[j*3+k];
+ if (nIndex <= -1) {
+ continue;
+ }
+
+ if (nIndex >= (S32) count/3) {
+ continue;
+ }
+ //get neighbor vertices
+ v1 = face.mIndices[nIndex*3+0];
+ v2 = face.mIndices[nIndex*3+1];
+ v3 = face.mIndices[nIndex*3+2];
+
+ //get neighbor face center
+ LLVector3 nCenter = (face.mVertices[v1].mPosition +
+ face.mVertices[v2].mPosition +
+ face.mVertices[v3].mPosition) / 3.0f;
+
+ //draw line
+ vertices.push_back(cCenter);
+ vertices.push_back(nCenter);
+ normals.push_back(LLVector3(1,1,1));
+ normals.push_back(LLVector3(1,1,1));
+ segments.push_back(vertices.size());
+ }
+ }
+
+ continue;
+
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ //==============================================
+ //DEBUG draw normals instead of silhouette edge
+ //==============================================
+#elif DEBUG_SILHOUETTE_NORMALS
+
+ //for each vertex
+ for (U32 j = 0; j < face.mVertices.size(); j++) {
+ vertices.push_back(face.mVertices[j].mPosition);
+ vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mNormal*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#if DEBUG_SILHOUETTE_BINORMALS
+ vertices.push_back(face.mVertices[j].mPosition);
+ vertices.push_back(face.mVertices[j].mPosition + face.mVertices[j].mBinormal*0.1f);
+ normals.push_back(LLVector3(0,0,1));
+ normals.push_back(LLVector3(0,0,1));
+ segments.push_back(vertices.size());
+#endif
+ }
+
+ continue;
+#else
+ //==============================================
+ //DEBUG
+ //==============================================
+
+ static const U8 AWAY = 0x01,
+ TOWARDS = 0x02;
+
+ //for each triangle
+ std::vector<U8> fFacing;
+ vector_append(fFacing, face.mIndices.size()/3);
+ for (U32 j = 0; j < face.mIndices.size()/3; j++)
+ {
+ //approximate normal
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ LLVector3 norm = (face.mVertices[v1].mPosition - face.mVertices[v2].mPosition) %
+ (face.mVertices[v2].mPosition - face.mVertices[v3].mPosition);
+
+ if (norm.magVecSquared() < 0.00000001f)
+ {
+ fFacing[j] = AWAY | TOWARDS;
+ }
+ else
+ {
+ //get view vector
+ LLVector3 view = (obj_cam_vec-face.mVertices[v1].mPosition);
+ bool away = view * norm > 0.0f;
+ if (away)
+ {
+ fFacing[j] = AWAY;
+ }
+ else
+ {
+ fFacing[j] = TOWARDS;
+ }
+ }
+ }
+
+ //for each triangle
+ for (U32 j = 0; j < face.mIndices.size()/3; j++)
+ {
+ if (fFacing[j] == (AWAY | TOWARDS))
+ { //this is a degenerate triangle
+ //take neighbor facing (degenerate faces get facing of one of their neighbors)
+ // FIXME IF NEEDED: this does not deal with neighboring degenerate faces
+ for (S32 k = 0; k < 3; k++)
+ {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1)
+ {
+ fFacing[j] = fFacing[index];
+ break;
+ }
+ }
+ continue; //skip degenerate face
+ }
+
+ //for each edge
+ for (S32 k = 0; k < 3; k++) {
+ S32 index = face.mEdge[j*3+k];
+ if (index != -1 && fFacing[index] == (AWAY | TOWARDS)) {
+ //our neighbor is degenerate, make him face our direction
+ fFacing[face.mEdge[j*3+k]] = fFacing[j];
+ continue;
+ }
+
+ if (index == -1 || //edge has no neighbor, MUST be a silhouette edge
+ (fFacing[index] & fFacing[j]) == 0) { //we found a silhouette edge
+
+ S32 v1 = face.mIndices[j*3+k];
+ S32 v2 = face.mIndices[j*3+((k+1)%3)];
+
+ vertices.push_back(face.mVertices[v1].mPosition*mat);
+ normals.push_back(face.mVertices[v1].mNormal*norm_mat);
+
+ vertices.push_back(face.mVertices[v2].mPosition*mat);
+ normals.push_back(face.mVertices[v2].mNormal*norm_mat);
+ segments.push_back(vertices.size());
+ }
+ }
+ }
+#endif
+ }
+ }
+}
+
+S32 LLVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ S32 ret = -1;
+
+ LLVector3 vec = end - start;
+
+ for (U32 i = 0; i < (U32)getNumFaces(); i++)
+ {
+ LLVolumeFace face = getVolumeFace(i);
+
+ for (U32 j = 0; j < face.mIndices.size()/3; j++)
+ {
+ //approximate normal
+ S32 v1 = face.mIndices[j*3+0];
+ S32 v2 = face.mIndices[j*3+1];
+ S32 v3 = face.mIndices[j*3+2];
+
+ LLVector3 norm = (face.mVertices[v2].mPosition - face.mVertices[v1].mPosition) %
+ (face.mVertices[v3].mPosition - face.mVertices[v2].mPosition);
+
+ if (norm.magVecSquared() >= 0.00000001f)
+ {
+ //get view vector
+ //LLVector3 view = (start-face.mVertices[v1].mPosition);
+ //if (view * norm < 0.0f)
+ {
+ if (LLTriangleLineSegmentIntersect( face.mVertices[v1].mPosition,
+ face.mVertices[v2].mPosition,
+ face.mVertices[v3].mPosition,
+ end,
+ vec))
+ {
+ vec = end-start;
+ ret = (S32) i;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+class LLVertexIndexPair
+{
+public:
+ LLVertexIndexPair(const LLVector3 &vertex, const S32 index);
+
+ LLVector3 mVertex;
+ S32 mIndex;
+};
+
+LLVertexIndexPair::LLVertexIndexPair(const LLVector3 &vertex, const S32 index)
+{
+ mVertex = vertex;
+ mIndex = index;
+}
+
+const F32 VERTEX_SLOP = 0.00001f;
+const F32 VERTEX_SLOP_SQRD = VERTEX_SLOP * VERTEX_SLOP;
+
+struct lessVertex
+{
+ bool operator()(const LLVertexIndexPair *a, const LLVertexIndexPair *b)
+ {
+ const F32 slop = VERTEX_SLOP;
+
+ if (a->mVertex.mV[0] + slop < b->mVertex.mV[0])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[0] - slop > b->mVertex.mV[0])
+ {
+ return FALSE;
+ }
+
+ if (a->mVertex.mV[1] + slop < b->mVertex.mV[1])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[1] - slop > b->mVertex.mV[1])
+ {
+ return FALSE;
+ }
+
+ if (a->mVertex.mV[2] + slop < b->mVertex.mV[2])
+ {
+ return TRUE;
+ }
+ else if (a->mVertex.mV[2] - slop > b->mVertex.mV[2])
+ {
+ return FALSE;
+ }
+
+ return FALSE;
+ }
+};
+
+struct lessTriangle
+{
+ bool operator()(const S32 *a, const S32 *b)
+ {
+ if (*a < *b)
+ {
+ return TRUE;
+ }
+ else if (*a > *b)
+ {
+ return FALSE;
+ }
+
+ if (*(a+1) < *(b+1))
+ {
+ return TRUE;
+ }
+ else if (*(a+1) > *(b+1))
+ {
+ return FALSE;
+ }
+
+ if (*(a+2) < *(b+2))
+ {
+ return TRUE;
+ }
+ else if (*(a+2) > *(b+2))
+ {
+ return FALSE;
+ }
+
+ return FALSE;
+ }
+};
+
+BOOL equalTriangle(const S32 *a, const S32 *b)
+{
+ if ((*a == *b) && (*(a+1) == *(b+1)) && ((*a+2) == (*b+2)))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLVolume::cleanupTriangleData( const S32 num_input_vertices,
+ const std::vector<Point>& input_vertices,
+ const S32 num_input_triangles,
+ S32 *input_triangles,
+ S32 &num_output_vertices,
+ LLVector3 **output_vertices,
+ S32 &num_output_triangles,
+ S32 **output_triangles)
+{
+ // Here's how we do this:
+ // Create a structure which contains the original vertex index and the
+ // LLVector3 data.
+ // "Sort" the data by the vectors
+ // Create an array the size of the old vertex list, with a mapping of
+ // old indices to new indices.
+ // Go through triangles, shift so the lowest index is first
+ // Sort triangles by first index
+ // Remove duplicate triangles
+ // Allocate and pack new triangle data.
+
+ //LLTimer cleanupTimer;
+ //llinfos << "In vertices: " << num_input_vertices << llendl;
+ //llinfos << "In triangles: " << num_input_triangles << llendl;
+
+ S32 i;
+ typedef std::multiset<LLVertexIndexPair*, lessVertex> vertex_set_t;
+ vertex_set_t vertex_list;
+
+ LLVertexIndexPair *pairp = NULL;
+ for (i = 0; i < num_input_vertices; i++)
+ {
+ LLVertexIndexPair *new_pairp = new LLVertexIndexPair(input_vertices[i].mPos, i);
+ vertex_list.insert(new_pairp);
+ }
+
+ // Generate the vertex mapping and the list of vertices without
+ // duplicates. This will crash if there are no vertices.
+ S32 *vertex_mapping = new S32[num_input_vertices];
+ LLVector3 *new_vertices = new LLVector3[num_input_vertices];
+ LLVertexIndexPair *prev_pairp = NULL;
+
+ S32 new_num_vertices;
+
+ new_num_vertices = 0;
+ for (vertex_set_t::iterator iter = vertex_list.begin(),
+ end = vertex_list.end();
+ iter != end; iter++)
+ {
+ pairp = *iter;
+ if (!prev_pairp || ((pairp->mVertex - prev_pairp->mVertex).magVecSquared() >= VERTEX_SLOP_SQRD))
+ {
+ new_vertices[new_num_vertices] = pairp->mVertex;
+ //llinfos << "Added vertex " << new_num_vertices << " : " << pairp->mVertex << llendl;
+ new_num_vertices++;
+ // Update the previous
+ prev_pairp = pairp;
+ }
+ else
+ {
+ //llinfos << "Removed duplicate vertex " << pairp->mVertex << llendl;
+ }
+ vertex_mapping[pairp->mIndex] = new_num_vertices - 1;
+ }
+
+ // Iterate through triangles and remove degenerates, re-ordering vertices
+ // along the way.
+ S32 *new_triangles = new S32[num_input_triangles * 3];
+ S32 new_num_triangles = 0;
+
+ for (i = 0; i < num_input_triangles; i++)
+ {
+ //llinfos << "Checking triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
+ input_triangles[i*3] = vertex_mapping[input_triangles[i*3]];
+ input_triangles[i*3+1] = vertex_mapping[input_triangles[i*3+1]];
+ input_triangles[i*3+2] = vertex_mapping[input_triangles[i*3+2]];
+
+ if ((input_triangles[i*3] == input_triangles[i*3+1])
+ || (input_triangles[i*3] == input_triangles[i*3+2])
+ || (input_triangles[i*3+1] == input_triangles[i*3+2]))
+ {
+ //llinfos << "Removing degenerate triangle " << input_triangles[i*3] << ":" << input_triangles[i*3+1] << ":" << input_triangles[i*3+2] << llendl;
+ // Degenerate triangle, skip
+ continue;
+ }
+
+ if (input_triangles[i*3] < input_triangles[i*3+1])
+ {
+ if (input_triangles[i*3] < input_triangles[i*3+2])
+ {
+ // (0 < 1) && (0 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3+1];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+2];
+ }
+ else
+ {
+ // (0 < 1) && (2 < 0)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+ }
+ }
+ else if (input_triangles[i*3+1] < input_triangles[i*3+2])
+ {
+ // (1 < 0) && (1 < 2)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+1];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3];
+ }
+ else
+ {
+ // (1 < 0) && (2 < 1)
+ new_triangles[new_num_triangles*3] = input_triangles[i*3+2];
+ new_triangles[new_num_triangles*3+1] = input_triangles[i*3];
+ new_triangles[new_num_triangles*3+2] = input_triangles[i*3+1];
+ }
+ new_num_triangles++;
+ }
+
+ if (new_num_triangles == 0)
+ {
+ llwarns << "Created volume object with 0 faces." << llendl;
+ return FALSE;
+ }
+
+ typedef std::set<S32*, lessTriangle> triangle_set_t;
+ triangle_set_t triangle_list;
+
+ for (i = 0; i < new_num_triangles; i++)
+ {
+ triangle_list.insert(&new_triangles[i*3]);
+ }
+
+ // Sort through the triangle list, and delete duplicates
+
+ S32 *prevp = NULL;
+ S32 *curp = NULL;
+
+ S32 *sorted_tris = new S32[new_num_triangles*3];
+ S32 cur_tri = 0;
+ for (triangle_set_t::iterator iter = triangle_list.begin(),
+ end = triangle_list.end();
+ iter != end; iter++)
+ {
+ curp = *iter;
+ if (!prevp || !equalTriangle(prevp, curp))
+ {
+ //llinfos << "Added triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ sorted_tris[cur_tri*3] = *curp;
+ sorted_tris[cur_tri*3+1] = *(curp+1);
+ sorted_tris[cur_tri*3+2] = *(curp+2);
+ cur_tri++;
+ prevp = curp;
+ }
+ else
+ {
+ //llinfos << "Skipped triangle " << *curp << ":" << *(curp+1) << ":" << *(curp+2) << llendl;
+ }
+ }
+
+ *output_vertices = new LLVector3[new_num_vertices];
+ num_output_vertices = new_num_vertices;
+ for (i = 0; i < new_num_vertices; i++)
+ {
+ (*output_vertices)[i] = new_vertices[i];
+ }
+
+ *output_triangles = new S32[cur_tri*3];
+ num_output_triangles = cur_tri;
+ memcpy(*output_triangles, sorted_tris, 3*cur_tri*sizeof(S32));
+
+ /*
+ llinfos << "Out vertices: " << num_output_vertices << llendl;
+ llinfos << "Out triangles: " << num_output_triangles << llendl;
+ for (i = 0; i < num_output_vertices; i++)
+ {
+ llinfos << i << ":" << (*output_vertices)[i] << llendl;
+ }
+ for (i = 0; i < num_output_triangles; i++)
+ {
+ llinfos << i << ":" << (*output_triangles)[i*3] << ":" << (*output_triangles)[i*3+1] << ":" << (*output_triangles)[i*3+2] << llendl;
+ }
+ */
+
+ //llinfos << "Out vertices: " << num_output_vertices << llendl;
+ //llinfos << "Out triangles: " << num_output_triangles << llendl;
+ delete[] vertex_mapping;
+ vertex_mapping = NULL;
+ delete[] new_vertices;
+ new_vertices = NULL;
+ delete[] new_triangles;
+ new_triangles = NULL;
+ delete[] sorted_tris;
+ sorted_tris = NULL;
+ triangle_list.clear();
+ std::for_each(vertex_list.begin(), vertex_list.end(), DeletePointer());
+ vertex_list.clear();
+
+ return TRUE;
+}
+
+
+BOOL LLVolumeParams::importFile(FILE *fp)
+{
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ keyword[0] = 0;
+
+ while (!feof(fp))
+ {
+ fgets(buffer, BUFSIZE, fp);
+ sscanf(buffer, " %s", keyword);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importFile(fp);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importFile(fp);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVolumeParams::exportFile(FILE *fp) const
+{
+ fprintf(fp,"\tshape 0\n");
+ fprintf(fp,"\t{\n");
+ mPathParams.exportFile(fp);
+ mProfileParams.exportFile(fp);
+ fprintf(fp, "\t}\n");
+ return TRUE;
+}
+
+
+BOOL LLVolumeParams::importLegacyStream(std::istream& input_stream)
+{
+ //llinfos << "importing volume" << llendl;
+ const S32 BUFSIZE = 16384;
+ char buffer[BUFSIZE];
+ char keyword[256];
+ keyword[0] = 0;
+
+ while (input_stream.good())
+ {
+ input_stream.getline(buffer, BUFSIZE);
+ sscanf(buffer, " %s", keyword);
+ if (!keyword)
+ {
+ continue;
+ }
+ if (!strcmp("{", keyword))
+ {
+ continue;
+ }
+ if (!strcmp("}",keyword))
+ {
+ break;
+ }
+ else if (!strcmp("profile", keyword))
+ {
+ mProfileParams.importLegacyStream(input_stream);
+ }
+ else if (!strcmp("path",keyword))
+ {
+ mPathParams.importLegacyStream(input_stream);
+ }
+ else
+ {
+ llwarns << "unknown keyword " << keyword << " in volume import" << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVolumeParams::exportLegacyStream(std::ostream& output_stream) const
+{
+ output_stream <<"\tshape 0\n";
+ output_stream <<"\t{\n";
+ mPathParams.exportLegacyStream(output_stream);
+ mProfileParams.exportLegacyStream(output_stream);
+ output_stream << "\t}\n";
+ return TRUE;
+}
+
+LLSD LLVolumeParams::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["path"] = mPathParams;
+ sd["profile"] = mProfileParams;
+ return sd;
+}
+
+bool LLVolumeParams::fromLLSD(LLSD& sd)
+{
+ mPathParams.fromLLSD(sd["path"]);
+ mProfileParams.fromLLSD(sd["profile"]);
+ return true;
+}
+
+void LLVolumeParams::reduceS(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mProfileParams.getBegin();
+ F32 b = mProfileParams.getEnd();
+ mProfileParams.setBegin(a + begin * (b - a));
+ mProfileParams.setEnd(a + end * (b - a));
+}
+
+void LLVolumeParams::reduceT(F32 begin, F32 end)
+{
+ begin = llclampf(begin);
+ end = llclampf(end);
+ if (begin > end)
+ {
+ F32 temp = begin;
+ begin = end;
+ end = temp;
+ }
+ F32 a = mPathParams.getBegin();
+ F32 b = mPathParams.getEnd();
+ mPathParams.setBegin(a + begin * (b - a));
+ mPathParams.setEnd(a + end * (b - a));
+}
+
+BOOL LLVolumeParams::isConvex() const
+{
+ // The logic for determining convexity is a little convoluted.
+
+ // Do we need to take getTwistBegin into account? DK 08/12/04
+ if ( mProfileParams.getHollow() != 0.0f
+ || mPathParams.getTwist() != mPathParams.getTwistBegin() )
+ {
+ // hollow or twist gaurantees concavity
+ return FALSE;
+ }
+
+ F32 profile_length = mProfileParams.getEnd() - mProfileParams.getBegin();
+ BOOL concave_profile = (profile_length < 1.0f) && (profile_length > 0.5f);
+ if (concave_profile)
+ {
+ // concave profile
+ return FALSE;
+ }
+
+ U8 path_type = mPathParams.getCurveType();
+ if ( LL_PCODE_PATH_LINE == path_type )
+ {
+ // straight paths with convex profile
+ return TRUE;
+ }
+
+ F32 path_length = mPathParams.getEnd() - mPathParams.getBegin();
+ BOOL concave_path = (path_length < 1.0f) && (path_length > 0.5f);
+ if (concave_path)
+ {
+ return FALSE;
+ }
+
+ // we're left with spheres, toroids and tubes
+ // only the spheres can be convex
+ U8 profile_type = mProfileParams.getCurveType() & LL_PCODE_PROFILE_MASK;
+ if ( LL_PCODE_PROFILE_CIRCLE_HALF == profile_type )
+ {
+ return TRUE;
+ }
+
+ // it's a toroid or tube
+ return FALSE;
+}
+
+LLFaceID LLVolume::generateFaceMask()
+{
+ LLFaceID new_mask = 0x0000;
+
+ switch(mProfilep->mParams.getCurveType() & LL_PCODE_PROFILE_MASK)
+ {
+ case LL_PCODE_PROFILE_CIRCLE:
+ case LL_PCODE_PROFILE_CIRCLE_HALF:
+ new_mask |= LL_FACE_OUTER_SIDE_0;
+ break;
+ case LL_PCODE_PROFILE_SQUARE:
+ {
+ for(S32 side = (S32)(mProfilep->mParams.getBegin() * 4.f); side < llceil(mProfilep->mParams.getEnd() * 4.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ case LL_PCODE_PROFILE_ISOTRI:
+ case LL_PCODE_PROFILE_EQUALTRI:
+ case LL_PCODE_PROFILE_RIGHTTRI:
+ {
+ for(S32 side = (S32)(mProfilep->mParams.getBegin() * 3.f); side < llceil(mProfilep->mParams.getEnd() * 3.f); side++)
+ {
+ new_mask |= LL_FACE_OUTER_SIDE_0 << side;
+ }
+ }
+ break;
+ default:
+ llerrs << "Unknown profile!" << llendl
+ break;
+ }
+
+ // handle hollow objects
+ if (mProfilep->isHollow())
+ {
+ new_mask |= LL_FACE_INNER_SIDE;
+ }
+
+ // handle open profile curves
+ if (mProfilep->isOpen())
+ {
+ new_mask |= LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END;
+ }
+
+ // handle open path curves
+ if (mPathp->isOpen())
+ {
+ new_mask |= LL_FACE_PATH_BEGIN | LL_FACE_PATH_END;
+ }
+
+ return new_mask;
+}
+
+BOOL LLVolume::isFaceMaskValid(LLFaceID face_mask)
+{
+ LLFaceID test_mask = 0;
+ for(S32 i = 0; i < getNumFaces(); i++)
+ {
+ test_mask |= mProfilep->mFaces[i].mFaceID;
+ }
+
+ return test_mask == face_mask;
+}
+
+BOOL LLVolume::isConvex() const
+{
+ // mParams.isConvex() may return FALSE even though the final
+ // geometry is actually convex due to LOD approximations.
+ // TODO -- provide LLPath and LLProfile with isConvex() methods
+ // that correctly determine convexity. -- Leviathan
+ return mParams.isConvex();
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params)
+{
+ s << "{type=" << (U32) profile_params.mCurveType;
+ s << ", begin=" << profile_params.mBegin;
+ s << ", end=" << profile_params.mEnd;
+ s << ", hollow=" << profile_params.mHollow;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params)
+{
+ s << "{type=" << (U32) path_params.mCurveType;
+ s << ", begin=" << path_params.mBegin;
+ s << ", end=" << path_params.mEnd;
+ s << ", twist=" << path_params.mTwistEnd;
+ s << ", scale=" << path_params.mScale;
+ s << ", shear=" << path_params.mShear;
+ s << ", twist_begin=" << path_params.mTwistBegin;
+ s << ", radius_offset=" << path_params.mRadiusOffset;
+ s << ", taper=" << path_params.mTaper;
+ s << ", revolutions=" << path_params.mRevolutions;
+ s << ", skew=" << path_params.mSkew;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params)
+{
+ s << "{profileparams = " << volume_params.mProfileParams;
+ s << ", pathparams = " << volume_params.mPathParams;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLProfile &profile)
+{
+ s << " {open=" << (U32) profile.mOpen;
+ s << ", dirty=" << profile.mDirty;
+ s << ", totalout=" << profile.mTotalOut;
+ s << ", total=" << profile.mTotal;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPath &path)
+{
+ s << "{open=" << (U32) path.mOpen;
+ s << ", dirty=" << path.mDirty;
+ s << ", step=" << path.mStep;
+ s << ", total=" << path.mTotal;
+ s << "}";
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLVolume &volume)
+{
+ s << "{params = " << volume.mParams;
+ s << ", path = " << *volume.mPathp;
+ s << ", profile = " << *volume.mProfilep;
+ s << "}";
+ return s;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLVolume *volumep)
+{
+ s << "{params = " << volumep->mParams;
+ s << ", path = " << *(volumep->mPathp);
+ s << ", profile = " << *(volumep->mProfilep);
+ s << "}";
+ return s;
+}
+
+
+LLVolumeFace::LLVolumeFace()
+{
+ mTypeMask = 0;
+ mID = 0;
+ mBeginS = 0;
+ mBeginT = 0;
+ mNumS = 0;
+ mNumT = 0;
+}
+
+
+BOOL LLVolumeFace::create()
+{
+ if (mTypeMask & CAP_MASK)
+ {
+ return createCap();
+ }
+ else if ((mTypeMask & END_MASK) || (mTypeMask & SIDE_MASK))
+ {
+ return createSide();
+ }
+ else
+ {
+ llerrs << "Unknown/uninitialized face type!" << llendl;
+ return FALSE;
+ }
+}
+
+void LerpPlanarVertex(LLVolumeFace::VertexData& v0,
+ LLVolumeFace::VertexData& v1,
+ LLVolumeFace::VertexData& v2,
+ LLVolumeFace::VertexData& vout,
+ F32 coef01,
+ F32 coef02)
+{
+ vout.mPosition = v0.mPosition + ((v1.mPosition-v0.mPosition)*coef01)+((v2.mPosition-v0.mPosition)*coef02);
+ vout.mTexCoord = v0.mTexCoord + ((v1.mTexCoord-v0.mTexCoord)*coef01)+((v2.mTexCoord-v0.mTexCoord)*coef02);
+ vout.mNormal = v0.mNormal;
+ vout.mBinormal = v0.mBinormal;
+}
+
+BOOL LLVolumeFace::createUnCutCubeCap()
+{
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+ S32 max_s = mVolumep->getProfile().getTotal();
+ S32 max_t = mVolumep->getPath().mPath.size();
+
+ // S32 i;
+ S32 num_vertices = 0, num_indices = 0;
+ S32 grid_size = (profile.size()-1)/4;
+ S32 quad_count = (grid_size * grid_size);
+
+ num_vertices = (grid_size+1)*(grid_size+1);
+ num_indices = quad_count * 4;
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ offset = (max_t-1) * max_s;
+ else
+ offset = mBeginS;
+
+ VertexData corners[4];
+ VertexData baseVert;
+ for(int t = 0; t < 4; t++){
+ corners[t].mPosition = mesh[offset + (grid_size*t)].mPos;
+ corners[t].mTexCoord.mV[0] = profile[grid_size*t].mV[0]+0.5f;
+ corners[t].mTexCoord.mV[1] = 0.5f - profile[grid_size*t].mV[1];
+ }
+ baseVert.mNormal =
+ ((corners[1].mPosition-corners[0].mPosition) %
+ (corners[2].mPosition-corners[1].mPosition));
+ baseVert.mNormal.normVec();
+ if(!(mTypeMask & TOP_MASK)){
+ baseVert.mNormal *= -1.0f;
+ }else{
+ //Swap the UVs on the U(X) axis for top face
+ LLVector2 swap;
+ swap = corners[0].mTexCoord;
+ corners[0].mTexCoord=corners[3].mTexCoord;
+ corners[3].mTexCoord=swap;
+ swap = corners[1].mTexCoord;
+ corners[1].mTexCoord=corners[2].mTexCoord;
+ corners[2].mTexCoord=swap;
+ }
+ baseVert.mBinormal = calc_binormal_from_triangle(
+ corners[0].mPosition, corners[0].mTexCoord,
+ corners[1].mPosition, corners[1].mTexCoord,
+ corners[2].mPosition, corners[2].mTexCoord);
+ for(int t = 0; t < 4; t++){
+ corners[t].mBinormal = baseVert.mBinormal;
+ corners[t].mNormal = baseVert.mNormal;
+ }
+
+ S32 vtop = mVertices.size();
+// S32 itop = mIndices.size();
+/// vector_append(mVertices,4);
+// vector_append(mIndices,4);
+// LLVector3 new_pt = lerp(pt1, pt2, t_fraction);
+#if 0
+ for(int t=0;t<4;t++){
+ VertexData vd;
+ vd.mPosition = corners[t].mPosition;
+ vd.mNormal =
+ ((corners[(t+1)%4].mPosition-corners[t].mPosition)%
+ (corners[(t+2)%4].mPosition-corners[(t+1)%4].mPosition));
+ vd.mNormal.normVec();
+
+ if (mTypeMask & TOP_MASK)
+ vd.mNormal *= -1.0f;
+ vd.mBinormal = vd.mNormal;
+ vd.mTexCoord = corners[t].mTexCoord;
+ mVertices.push_back(vd);
+ }
+ int idxs[] = {0,1,2,2,3,0};
+ if (mTypeMask & TOP_MASK){
+ for(int i=0;i<6;i++)mIndices.push_back(vtop+idxs[i]);
+ }else{
+ for(int i=5;i>=0;i--)mIndices.push_back(vtop+idxs[i]);
+ }
+#else
+ for(int gx = 0;gx<grid_size+1;gx++){
+ for(int gy = 0;gy<grid_size+1;gy++){
+ VertexData newVert;
+ LerpPlanarVertex(
+ corners[0],
+ corners[1],
+ corners[3],
+ newVert,
+ (F32)gx/(F32)grid_size,
+ (F32)gy/(F32)grid_size);
+ mVertices.push_back(newVert);
+ }
+ }
+ int idxs[] = {0,1,(grid_size+1)+1,(grid_size+1)+1,(grid_size+1),0};
+ for(int gx = 0;gx<grid_size;gx++){
+ for(int gy = 0;gy<grid_size;gy++){
+ if (mTypeMask & TOP_MASK){
+ for(int i=5;i>=0;i--)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
+ }else{
+ for(int i=0;i<6;i++)mIndices.push_back(vtop+(gy*(grid_size+1))+gx+idxs[i]);
+ }
+ }
+ }
+#endif
+ return TRUE;
+}
+
+
+BOOL LLVolumeFace::createCap()
+{
+ if (!(mTypeMask & HOLLOW_MASK) &&
+ !(mTypeMask & OPEN_MASK) &&
+ ((this->mVolumep->getParams().getPathParams().getBegin()==0.0f)&&
+ (this->mVolumep->getParams().getPathParams().getEnd()==1.0f))&&
+ (mVolumep->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ mVolumep->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
+ ){
+ return createUnCutCubeCap();
+ }
+
+ S32 i;
+ S32 num_vertices = 0, num_indices = 0;
+
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+
+ // All types of caps have the same number of vertices and indices
+ num_vertices = profile.size();
+ num_indices = (profile.size() - 2)*3;
+ vector_append(mVertices,num_vertices);
+ vector_append(mIndices,num_indices);
+
+ S32 max_s = mVolumep->getProfile().getTotal();
+ S32 max_t = mVolumep->getPath().mPath.size();
+
+ mCenter.clearVec();
+
+ S32 offset = 0;
+ if (mTypeMask & TOP_MASK)
+ {
+ offset = (max_t-1) * max_s;
+ }
+ else
+ {
+ offset = mBeginS;
+ }
+
+ // Figure out the normal, assume all caps are flat faces.
+ // Cross product to get normals.
+
+ LLVector2 cuv = LLVector2(0,0);
+
+ // Copy the vertices into the array
+ for (i = 0; i < num_vertices; i++)
+ {
+
+ if (mTypeMask & TOP_MASK)
+ {
+ mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
+ mVertices[i].mTexCoord.mV[1] = profile[i].mV[1]+0.5f;
+ }
+ else
+ {
+ // Mirror for underside.
+ mVertices[i].mTexCoord.mV[0] = profile[i].mV[0]+0.5f;
+ mVertices[i].mTexCoord.mV[1] = 0.5f - profile[i].mV[1];
+ }
+
+ if(i){
+ //Dont include the first point of the profile in the average
+ cuv += mVertices[i].mTexCoord;
+ mCenter += mVertices[i].mPosition = mesh[i + offset].mPos;
+ }
+ else mVertices[i].mPosition = mesh[i + offset].mPos;
+ //mVertices[i].mNormal = normal;
+ }
+
+ mCenter /= (F32)(num_vertices-1);
+ cuv /= (F32)(num_vertices-1);
+
+ LLVector3 binormal = calc_binormal_from_triangle(
+ mCenter, cuv,
+ mVertices[0].mPosition, mVertices[0].mTexCoord,
+ mVertices[1].mPosition, mVertices[1].mTexCoord);
+ binormal.normVec();
+
+ LLVector3 d0;
+ LLVector3 d1;
+ LLVector3 normal;
+
+ d0 = mCenter-mVertices[0].mPosition;
+ d1 = mCenter-mVertices[1].mPosition;
+
+ normal = (mTypeMask & TOP_MASK) ? (d0%d1) : (d1%d0);
+ normal.normVec();
+
+ VertexData vd;
+ vd.mPosition = mCenter;
+ vd.mNormal = normal;
+ vd.mBinormal = binormal;
+ vd.mTexCoord = cuv;
+
+ if (!(mTypeMask & HOLLOW_MASK) && !(mTypeMask & OPEN_MASK))
+ {
+ mVertices.push_back(vd);
+ num_vertices++;
+ vector_append(mIndices, 3);
+ }
+
+
+ for (i = 0; i < num_vertices; i++)
+ {
+ mVertices[i].mBinormal = binormal;
+ mVertices[i].mNormal = normal;
+ }
+
+ if (mTypeMask & HOLLOW_MASK)
+ {
+ if (mTypeMask & TOP_MASK)
+ {
+ // HOLLOW TOP
+ // Does it matter if it's open or closed? - djs
+
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+ i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt1 + 1;
+ mIndices[i++] = pt2;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2 - 1;
+ mIndices[i++] = pt2;
+ pt2--;
+ }
+ }
+ }
+ else
+ {
+ // HOLLOW BOTTOM
+ // Does it matter if it's open or closed? - djs
+
+ llassert(mTypeMask & BOTTOM_MASK);
+ S32 pt1 = 0, pt2 = num_vertices - 1;
+
+ i = 0;
+ while (pt2 - pt1 > 1)
+ {
+ // Use the profile points instead of the mesh, since you want
+ // the un-transformed profile distances.
+ LLVector3 p1 = profile[pt1];
+ LLVector3 p2 = profile[pt2];
+ LLVector3 pa = profile[pt1+1];
+ LLVector3 pb = profile[pt2-1];
+
+ p1.mV[VZ] = 0.f;
+ p2.mV[VZ] = 0.f;
+ pa.mV[VZ] = 0.f;
+ pb.mV[VZ] = 0.f;
+
+ // Use area of triangle to determine backfacing
+ F32 area_1a2, area_1ba, area_21b, area_2ab;
+ area_1a2 = (p1.mV[0]*pa.mV[1] - pa.mV[0]*p1.mV[1]) +
+ (pa.mV[0]*p2.mV[1] - p2.mV[0]*pa.mV[1]) +
+ (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]);
+
+ area_1ba = (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*pa.mV[1] - pa.mV[0]*pb.mV[1]) +
+ (pa.mV[0]*p1.mV[1] - p1.mV[0]*pa.mV[1]);
+
+ area_21b = (p2.mV[0]*p1.mV[1] - p1.mV[0]*p2.mV[1]) +
+ (p1.mV[0]*pb.mV[1] - pb.mV[0]*p1.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ area_2ab = (p2.mV[0]*pa.mV[1] - pa.mV[0]*p2.mV[1]) +
+ (pa.mV[0]*pb.mV[1] - pb.mV[0]*pa.mV[1]) +
+ (pb.mV[0]*p2.mV[1] - p2.mV[0]*pb.mV[1]);
+
+ BOOL use_tri1a2 = TRUE;
+ BOOL tri_1a2 = TRUE;
+ BOOL tri_21b = TRUE;
+
+ if (area_1a2 < 0)
+ {
+ tri_1a2 = FALSE;
+ }
+ if (area_2ab < 0)
+ {
+ // Can't use, because it contains point b
+ tri_1a2 = FALSE;
+ }
+ if (area_21b < 0)
+ {
+ tri_21b = FALSE;
+ }
+ if (area_1ba < 0)
+ {
+ // Can't use, because it contains point b
+ tri_21b = FALSE;
+ }
+
+ if (!tri_1a2)
+ {
+ use_tri1a2 = FALSE;
+ }
+ else if (!tri_21b)
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ LLVector3 d1 = p1 - pa;
+ LLVector3 d2 = p2 - pb;
+
+ if (d1.magVecSquared() < d2.magVecSquared())
+ {
+ use_tri1a2 = TRUE;
+ }
+ else
+ {
+ use_tri1a2 = FALSE;
+ }
+ }
+
+ // Flipped backfacing from top
+ if (use_tri1a2)
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt1 + 1;
+ pt1++;
+ }
+ else
+ {
+ mIndices[i++] = pt1;
+ mIndices[i++] = pt2;
+ mIndices[i++] = pt2 - 1;
+ pt2--;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Not hollow, generate the triangle fan.
+ if (mTypeMask & TOP_MASK)
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ // SOLID OPEN TOP
+ // Generate indices
+ // This is a tri-fan, so we reuse the same first point for all triangles.
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i;
+ mIndices[3*i+2] = i + 1;
+ }
+ }
+ else
+ {
+ // SOLID CLOSED TOP
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ //MSMSM fix these caps but only for the un-cut case
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i;
+ mIndices[3*i+2] = i + 1;
+ }
+ }
+ }
+ else
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ // SOLID OPEN BOTTOM
+ // Generate indices
+ // This is a tri-fan, so we reuse the same first point for all triangles.
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i + 1;
+ mIndices[3*i+2] = i;
+ }
+ }
+ else
+ {
+ // SOLID CLOSED BOTTOM
+ for (i = 0; i < (num_vertices - 2); i++)
+ {
+ //MSMSM fix these caps but only for the un-cut case
+ mIndices[3*i] = num_vertices - 1;
+ mIndices[3*i+1] = i + 1;
+ mIndices[3*i+2] = i;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLVolumeFace::createSide()
+{
+ BOOL flat = mTypeMask & FLAT_MASK;
+ S32 num_vertices, num_indices;
+
+
+ const std::vector<LLVolume::Point>& mesh = mVolumep->getMesh();
+ const std::vector<LLVector3>& profile = mVolumep->getProfile().mProfile;
+ const std::vector<LLPath::PathPt>& path_data = mVolumep->getPath().mPath;
+
+ S32 max_s = mVolumep->getProfile().getTotal();
+
+ S32 s, t, i;
+ F32 ss, tt;
+
+ num_vertices = mNumS*mNumT;
+ num_indices = (mNumS-1)*(mNumT-1)*6;
+ vector_append(mVertices,num_vertices);
+ vector_append(mIndices,num_indices);
+ vector_append(mEdge, num_indices);
+
+ mCenter.clearVec();
+
+ S32 begin_stex = llfloor( profile[mBeginS].mV[2] );
+ S32 num_s = ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2) ? mNumS/2 : mNumS;
+
+ S32 cur_vertex = 0;
+ // Copy the vertices into the array
+ for (t = mBeginT; t < mBeginT + mNumT; t++)
+ {
+ tt = path_data[t].mTexT;
+ for (s = 0; s < num_s; s++)
+ {
+ if (mTypeMask & END_MASK)
+ {
+ if (s)
+ {
+ ss = 1.f;
+ }
+ else
+ {
+ ss = 0.f;
+ }
+ }
+ else
+ {
+ // Get s value for tex-coord.
+ if (!flat)
+ {
+ ss = profile[mBeginS + s].mV[2];
+ }
+ else
+ {
+ ss = profile[mBeginS + s].mV[2] - begin_stex;
+ }
+ }
+
+ // Check to see if this triangle wraps around the array.
+ if (mBeginS + s >= max_s)
+ {
+ // We're wrapping
+ i = mBeginS + s + max_s*(t-1);
+ }
+ else
+ {
+ i = mBeginS + s + max_s*t;
+ }
+
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+
+ cur_vertex++;
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2 && s > 0)
+ {
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+ cur_vertex++;
+ }
+ }
+
+ if ((mTypeMask & INNER_MASK) && (mTypeMask & FLAT_MASK) && mNumS > 2)
+ {
+ if (mTypeMask & OPEN_MASK)
+ {
+ s = num_s-1;
+ }
+ else
+ {
+ s = 0;
+ }
+
+ i = mBeginS + s + max_s*t;
+ ss = profile[mBeginS + s].mV[2] - begin_stex;
+ mCenter += mVertices[cur_vertex].mPosition = mesh[i].mPos;
+ mVertices[cur_vertex].mTexCoord = LLVector2(ss,tt);
+
+ mVertices[cur_vertex].mNormal = LLVector3(0,0,0);
+ mVertices[cur_vertex].mBinormal = LLVector3(0,0,0);
+ cur_vertex++;
+ }
+ }
+ mCenter /= (F32)num_vertices;
+
+ S32 cur_index = 0;
+ S32 cur_edge = 0;
+ BOOL flat_face = mTypeMask & FLAT_MASK;
+
+ // Now we generate the indices.
+ for (t = 0; t < (mNumT-1); t++)
+ {
+ for (s = 0; s < (mNumS-1); s++)
+ {
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+ mIndices[cur_index++] = s + mNumS*(t+1); //top left
+ mIndices[cur_index++] = s + mNumS*t; //bottom left
+ mIndices[cur_index++] = s+1 + mNumS*t; //bottom right
+ mIndices[cur_index++] = s+1 + mNumS*(t+1); //top right
+
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2+1; //bottom left/top right neighbor face
+ if (t < mNumT-2) { //top right/top left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t+1)+s*2+1;
+ }
+ else if (mNumT <= 3 || mVolumep->getPath().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on T
+ mEdge[cur_edge++] = s*2+1;
+ }
+ if (s > 0) { //top left/bottom left neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2-1;
+ }
+ else if (flat_face || mVolumep->getProfile().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(mNumS-2)*2+1;
+ }
+
+ if (t > 0) { //bottom left/bottom right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*(t-1)+s*2;
+ }
+ else if (mNumT <= 3 || mVolumep->getPath().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on T
+ mEdge[cur_edge++] = (mNumS-1)*2*(mNumT-2)+s*2;
+ }
+ if (s < mNumS-2) { //bottom right/top right neighbor face
+ mEdge[cur_edge++] = (mNumS-1)*2*t+(s+1)*2;
+ }
+ else if (flat_face || mVolumep->getProfile().isOpen() == TRUE) { //no neighbor
+ mEdge[cur_edge++] = -1;
+ }
+ else { //wrap on S
+ mEdge[cur_edge++] = (mNumS-1)*2*t;
+ }
+ mEdge[cur_edge++] = (mNumS-1)*2*t+s*2; //top right/bottom left neighbor face
+ }
+ }
+
+
+ //generate normals
+ for (U32 i = 0; i < mIndices.size()/3; i++) { //for each triangle
+ const VertexData& v0 = mVertices[mIndices[i*3+0]];
+ const VertexData& v1 = mVertices[mIndices[i*3+1]];
+ const VertexData& v2 = mVertices[mIndices[i*3+2]];
+
+ //calculate triangle normal
+ LLVector3 norm = (v0.mPosition-v1.mPosition)%
+ (v0.mPosition-v2.mPosition);
+
+ //calculate binormal
+ LLVector3 binorm = calc_binormal_from_triangle(v0.mPosition, v0.mTexCoord,
+ v1.mPosition, v1.mTexCoord,
+ v2.mPosition, v2.mTexCoord);
+
+ for (U32 j = 0; j < 3; j++) { //add triangle normal to vertices
+ mVertices[mIndices[i*3+j]].mNormal += norm; // * (weight_sum - d[j])/weight_sum;
+ mVertices[mIndices[i*3+j]].mBinormal += binorm; // * (weight_sum - d[j])/weight_sum;
+ }
+
+ //even out quad contributions
+ if (i % 2 == 0) {
+ mVertices[mIndices[i*3+2]].mNormal += norm;
+ mVertices[mIndices[i*3+2]].mBinormal += binorm;
+ }
+ else {
+ mVertices[mIndices[i*3+1]].mNormal += norm;
+ mVertices[mIndices[i*3+1]].mBinormal += binorm;
+ }
+ }
+
+ if (mVolumep->getPath().isOpen() == FALSE) { //wrap normals on T
+ for (S32 i = 0; i < mNumS; i++) {
+ LLVector3 norm = mVertices[i].mNormal + mVertices[mNumS*(mNumT-1)+i].mNormal;
+ mVertices[i].mNormal = norm;
+ mVertices[mNumS*(mNumT-1)+i].mNormal = norm;
+ }
+ }
+
+ if (mVolumep->getProfile().isOpen() == FALSE) { //wrap normals on S
+ for (S32 i = 0; i < mNumT; i++) {
+ LLVector3 norm = mVertices[mNumS*i].mNormal + mVertices[mNumS*i+mNumS-1].mNormal;
+ mVertices[mNumS * i].mNormal = norm;
+ mVertices[mNumS * i+mNumS-1].mNormal = norm;
+ }
+ }
+
+ if (mVolumep->getPathType() == LL_PCODE_PATH_CIRCLE && ((mVolumep->getProfileType() & LL_PCODE_PROFILE_MASK) == LL_PCODE_PROFILE_CIRCLE_HALF)) {
+ if ((mVertices[0].mPosition - mVertices[mNumS*(mNumT-2)].mPosition).magVecSquared() < 0.000001f)
+ { //all lower S have same normal
+ for (S32 i = 0; i < mNumT; i++) {
+ mVertices[mNumS*i].mNormal = LLVector3(1,0,0);
+ }
+ }
+
+ if ((mVertices[mNumS-1].mPosition - mVertices[mNumS*(mNumT-2)+mNumS-1].mPosition).magVecSquared() < 0.000001f)
+ { //all upper T have same normal
+ for (S32 i = 0; i < mNumT; i++) {
+ mVertices[mNumS*i+mNumS-1].mNormal = LLVector3(-1,0,0);
+ }
+ }
+ }
+
+ //this loop would LOVE OpenMP
+ LLVector3 min = mVolumep->mBounds[0] - mVolumep->mBounds[1];
+ LLVector3 max = mVolumep->mBounds[0] + mVolumep->mBounds[1];
+
+ if (min == max && min == LLVector3(512,512,512))
+ {
+ min = max = mVertices[0].mPosition;
+ }
+
+ for (U32 i = 0; i < mVertices.size(); i++) {
+ mVertices[i].mNormal.normVec();
+ mVertices[i].mBinormal.normVec();
+
+ for (U32 j = 0; j < 3; j++) {
+ if (mVertices[i].mPosition.mV[j] > max.mV[j]) {
+ max.mV[j] = mVertices[i].mPosition.mV[j];
+ }
+ if (mVertices[i].mPosition.mV[j] < min.mV[j]) {
+ min.mV[j] = mVertices[i].mPosition.mV[j];
+ }
+ }
+ }
+
+ mVolumep->mBounds[0] = (min + max) * 0.5f; //center
+ mVolumep->mBounds[1] = (max - min) * 0.5f; //half-height
+
+ return TRUE;
+}
+
+// Static
+BOOL LLVolumeFace::updateColors(LLColor4U *old_colors, const S32 num_old, const LLVolumeFace &old_vf,
+ LLStrider<LLColor4U> &new_colors, const S32 num_new, const LLVolumeFace &new_vf)
+{
+ if (new_vf.mTypeMask & CAP_MASK)
+ {
+ // These aren't interpolated correctly. Need to fix when shadows go in...
+ F32 ratio = (F32)num_old / (F32)num_new;
+ S32 v = 0;
+ for (v = 0; v < num_new; v++)
+ {
+ new_colors[v] = old_colors[(S32)(v*ratio)];
+ }
+ return FALSE;
+ }
+ else if (new_vf.mTypeMask & END_MASK)
+ {
+ // These aren't interpolated correctly. Need to fix when shadows go in...
+ F32 ratio = (F32)num_old / (F32)num_new;
+ S32 v = 0;
+ for (v = 0; v < num_new; v++)
+ {
+ new_colors[v] = old_colors[(S32)(v*ratio)];
+ }
+ return FALSE;
+ }
+ else if (new_vf.mTypeMask & SIDE_MASK)
+ {
+ S32 s, t;
+ F32 s_ratio = (F32)old_vf.mNumS / (F32)new_vf.mNumS;
+ F32 t_ratio = (F32)old_vf.mNumT / (F32)new_vf.mNumT;
+
+ S32 v = 0;
+ for (t = 0; t < new_vf.mNumT; t++)
+ {
+ F32 t_frac = t * t_ratio;
+ S32 old_t = (S32)t_frac;
+ S32 t_target = llmin(old_t + 1, (old_vf.mNumT - 1));
+ t_frac -= old_t;
+ for (s = 0; s < new_vf.mNumS; s++)
+ {
+ F32 s_frac = s * s_ratio;
+ S32 old_s = (S32)s_frac;
+ S32 s_target = llmin(old_s + 1, (old_vf.mNumS - 1));
+ s_frac -= old_s;
+
+ // Interpolate along s, then along t.
+ LLColor4U s_interp0 = old_colors[old_t * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[old_t * old_vf.mNumS + s_target].multAll(s_frac));
+ LLColor4U s_interp1 = old_colors[t_target * old_vf.mNumS + old_s].multAll(1.f - s_frac).addClampMax(old_colors[t_target * old_vf.mNumS + s_target].multAll(s_frac));
+ new_colors[v] = s_interp0.multAll(1.f - t_frac).addClampMax(s_interp1.multAll(t_frac));
+ v++;
+ }
+ }
+ }
+ else
+ {
+ llerrs << "Unknown/uninitialized face type!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+// Finds binormal based on three vertices with texture coordinates.
+// Fills in dummy values if the triangle has degenerate texture coordinates.
+LLVector3 calc_binormal_from_triangle(
+ const LLVector3& pos0,
+ const LLVector2& tex0,
+ const LLVector3& pos1,
+ const LLVector2& tex1,
+ const LLVector3& pos2,
+ const LLVector2& tex2)
+{
+ LLVector3 rx0( pos0.mV[VX], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 rx1( pos1.mV[VX], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 rx2( pos2.mV[VX], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 ry0( pos0.mV[VY], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 ry1( pos1.mV[VY], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 ry2( pos2.mV[VY], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 rz0( pos0.mV[VZ], tex0.mV[VX], tex0.mV[VY] );
+ LLVector3 rz1( pos1.mV[VZ], tex1.mV[VX], tex1.mV[VY] );
+ LLVector3 rz2( pos2.mV[VZ], tex2.mV[VX], tex2.mV[VY] );
+
+ LLVector3 r0 = (rx0 - rx1) % (rx0 - rx2);
+ LLVector3 r1 = (ry0 - ry1) % (ry0 - ry2);
+ LLVector3 r2 = (rz0 - rz1) % (rz0 - rz2);
+
+ if( r0.mV[VX] && r1.mV[VX] && r2.mV[VX] )
+ {
+ LLVector3 binormal(
+ -r0.mV[VZ] / r0.mV[VX],
+ -r1.mV[VZ] / r1.mV[VX],
+ -r2.mV[VZ] / r2.mV[VX]);
+ //binormal.normVec();
+ return binormal;
+ }
+ else
+ {
+ return LLVector3( 0, 1 , 0 );
+ }
+}
diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h
new file mode 100644
index 0000000000..cf9ae00f21
--- /dev/null
+++ b/indra/llmath/llvolume.h
@@ -0,0 +1,882 @@
+/**
+ * @file llvolume.h
+ * @brief LLVolume base class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUME_H
+#define LL_LLVOLUME_H
+
+#include <iostream>
+
+class LLProfileParams;
+class LLPathParams;
+class LLVolumeParams;
+class LLProfile;
+class LLPath;
+class LLVolumeFace;
+class LLVolume;
+
+#include "lldarray.h"
+#include "lluuid.h"
+#include "v4color.h"
+//#include "vmath.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llstrider.h"
+#include "v4coloru.h"
+#include "llmemory.h"
+
+//============================================================================
+
+const S32 MIN_DETAIL_FACES = 6;
+const S32 MIN_LOD = 0;
+const S32 MAX_LOD = 3;
+
+// These are defined here but are not enforced at this level,
+// rather they are here for the convenience of code that uses
+// the LLVolume class.
+const F32 MIN_VOLUME_PROFILE_WIDTH = 0.05f;
+const F32 MIN_VOLUME_PATH_WIDTH = 0.05f;
+
+const F32 CUT_QUANTA = 0.005f;
+const F32 SCALE_QUANTA = 0.01f;
+const F32 SHEAR_QUANTA = 0.01f;
+const F32 TAPER_QUANTA = 0.01f;
+const F32 REV_QUANTA = 0.015f;
+
+
+//============================================================================
+
+// useful masks
+const LLPCode LL_PCODE_HOLLOW_MASK = 0x80; // has a thickness
+const LLPCode LL_PCODE_SEGMENT_MASK = 0x40; // segments (1 angle)
+const LLPCode LL_PCODE_PATCH_MASK = 0x20; // segmented segments (2 angles)
+const LLPCode LL_PCODE_HEMI_MASK = 0x10; // half-primitives get their own type per PR's dictum
+const LLPCode LL_PCODE_BASE_MASK = 0x0F;
+
+ // primitive shapes
+const LLPCode LL_PCODE_CUBE = 1;
+const LLPCode LL_PCODE_PRISM = 2;
+const LLPCode LL_PCODE_TETRAHEDRON = 3;
+const LLPCode LL_PCODE_PYRAMID = 4;
+const LLPCode LL_PCODE_CYLINDER = 5;
+const LLPCode LL_PCODE_CONE = 6;
+const LLPCode LL_PCODE_SPHERE = 7;
+const LLPCode LL_PCODE_TORUS = 8;
+const LLPCode LL_PCODE_VOLUME = 9;
+
+ // surfaces
+//const LLPCode LL_PCODE_SURFACE_TRIANGLE = 10;
+//const LLPCode LL_PCODE_SURFACE_SQUARE = 11;
+//const LLPCode LL_PCODE_SURFACE_DISC = 12;
+
+const LLPCode LL_PCODE_APP = 14; // App specific pcode (for viewer/sim side only objects)
+const LLPCode LL_PCODE_LEGACY = 15;
+
+// Pcodes for legacy objects
+//const LLPCode LL_PCODE_LEGACY_ATOR = 0x10 | LL_PCODE_LEGACY; // ATOR
+const LLPCode LL_PCODE_LEGACY_AVATAR = 0x20 | LL_PCODE_LEGACY; // PLAYER
+//const LLPCode LL_PCODE_LEGACY_BIRD = 0x30 | LL_PCODE_LEGACY; // BIRD
+//const LLPCode LL_PCODE_LEGACY_DEMON = 0x40 | LL_PCODE_LEGACY; // DEMON
+const LLPCode LL_PCODE_LEGACY_GRASS = 0x50 | LL_PCODE_LEGACY; // GRASS
+const LLPCode LL_PCODE_TREE_NEW = 0x60 | LL_PCODE_LEGACY; // new trees
+//const LLPCode LL_PCODE_LEGACY_ORACLE = 0x70 | LL_PCODE_LEGACY; // ORACLE
+const LLPCode LL_PCODE_LEGACY_PART_SYS = 0x80 | LL_PCODE_LEGACY; // PART_SYS
+const LLPCode LL_PCODE_LEGACY_ROCK = 0x90 | LL_PCODE_LEGACY; // ROCK
+//const LLPCode LL_PCODE_LEGACY_SHOT = 0xA0 | LL_PCODE_LEGACY; // BASIC_SHOT
+//const LLPCode LL_PCODE_LEGACY_SHOT_BIG = 0xB0 | LL_PCODE_LEGACY;
+//const LLPCode LL_PCODE_LEGACY_SMOKE = 0xC0 | LL_PCODE_LEGACY; // SMOKE
+//const LLPCode LL_PCODE_LEGACY_SPARK = 0xD0 | LL_PCODE_LEGACY;// SPARK
+const LLPCode LL_PCODE_LEGACY_TEXT_BUBBLE = 0xE0 | LL_PCODE_LEGACY; // TEXTBUBBLE
+const LLPCode LL_PCODE_LEGACY_TREE = 0xF0 | LL_PCODE_LEGACY; // TREE
+
+ // hemis
+const LLPCode LL_PCODE_CYLINDER_HEMI = LL_PCODE_CYLINDER | LL_PCODE_HEMI_MASK;
+const LLPCode LL_PCODE_CONE_HEMI = LL_PCODE_CONE | LL_PCODE_HEMI_MASK;
+const LLPCode LL_PCODE_SPHERE_HEMI = LL_PCODE_SPHERE | LL_PCODE_HEMI_MASK;
+const LLPCode LL_PCODE_TORUS_HEMI = LL_PCODE_TORUS | LL_PCODE_HEMI_MASK;
+
+
+// Volumes consist of a profile at the base that is swept around
+// a path to make a volume.
+// The profile code
+const U8 LL_PCODE_PROFILE_MASK = 0x0f;
+const U8 LL_PCODE_PROFILE_MIN = 0x00;
+const U8 LL_PCODE_PROFILE_CIRCLE = 0x00;
+const U8 LL_PCODE_PROFILE_SQUARE = 0x01;
+const U8 LL_PCODE_PROFILE_ISOTRI = 0x02;
+const U8 LL_PCODE_PROFILE_EQUALTRI = 0x03;
+const U8 LL_PCODE_PROFILE_RIGHTTRI = 0x04;
+const U8 LL_PCODE_PROFILE_CIRCLE_HALF = 0x05;
+const U8 LL_PCODE_PROFILE_MAX = 0x05;
+
+// Stored in the profile byte
+const U8 LL_PCODE_HOLE_MASK = 0xf0;
+const U8 LL_PCODE_HOLE_MIN = 0x00;
+const U8 LL_PCODE_HOLE_SAME = 0x00; // same as outside profile
+const U8 LL_PCODE_HOLE_CIRCLE = 0x10;
+const U8 LL_PCODE_HOLE_SQUARE = 0x20;
+const U8 LL_PCODE_HOLE_TRIANGLE = 0x30;
+const U8 LL_PCODE_HOLE_MAX = 0x03; // min/max needs to be >> 4 of real min/max
+
+const U8 LL_PCODE_PATH_IGNORE = 0x00;
+const U8 LL_PCODE_PATH_MIN = 0x01; // min/max needs to be >> 4 of real min/max
+const U8 LL_PCODE_PATH_LINE = 0x10;
+const U8 LL_PCODE_PATH_CIRCLE = 0x20;
+const U8 LL_PCODE_PATH_CIRCLE2 = 0x30;
+const U8 LL_PCODE_PATH_TEST = 0x40;
+const U8 LL_PCODE_PATH_FLEXIBLE = 0x80;
+const U8 LL_PCODE_PATH_MAX = 0x08;
+
+//============================================================================
+
+// face identifiers
+typedef U16 LLFaceID;
+
+const LLFaceID LL_FACE_PATH_BEGIN = 0x1 << 0;
+const LLFaceID LL_FACE_PATH_END = 0x1 << 1;
+const LLFaceID LL_FACE_INNER_SIDE = 0x1 << 2;
+const LLFaceID LL_FACE_PROFILE_BEGIN = 0x1 << 3;
+const LLFaceID LL_FACE_PROFILE_END = 0x1 << 4;
+const LLFaceID LL_FACE_OUTER_SIDE_0 = 0x1 << 5;
+const LLFaceID LL_FACE_OUTER_SIDE_1 = 0x1 << 6;
+const LLFaceID LL_FACE_OUTER_SIDE_2 = 0x1 << 7;
+const LLFaceID LL_FACE_OUTER_SIDE_3 = 0x1 << 8;
+
+//============================================================================
+
+class LLProfileParams
+{
+public:
+ LLProfileParams()
+ {
+ mBegin = 0;
+ mEnd = 1;
+ mHollow = 0;
+ mCurveType = LL_PCODE_PROFILE_SQUARE;
+ }
+
+ LLProfileParams(U8 curve, F32 begin, F32 end, F32 hollow)
+ : mCurveType(curve), mBegin(begin), mEnd(end), mHollow(hollow)
+ {
+ }
+
+ LLProfileParams(U8 curve, U8 begin, U8 end, U8 hollow)
+ {
+ mCurveType = curve;
+ F32 temp_f32 = begin * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mBegin = temp_f32;
+ temp_f32 = end * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mEnd = 1.f - temp_f32;
+ temp_f32 = hollow * SCALE_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ temp_f32 = 1.f;
+ }
+ mHollow = temp_f32;
+ }
+
+ bool operator==(const LLProfileParams &params) const;
+ bool operator!=(const LLProfileParams &params) const;
+ bool operator<(const LLProfileParams &params) const;
+
+ void copyParams(const LLProfileParams &params);
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *fp) const;
+
+ BOOL importLegacyStream(std::istream& input_stream);
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ const F32& getBegin () const { return mBegin; }
+ const F32& getEnd () const { return mEnd; }
+ const F32& getHollow() const { return mHollow; }
+ const U8& getCurveType () const { return mCurveType; }
+
+ void setCurveType(const U32 type) { mCurveType = type;}
+ void setBegin(const F32 begin) { mBegin = (begin >= 1.0f) ? 0.0f : ((int) (begin * 1000))/1000.0f;}
+ void setEnd(const F32 end) { mEnd = (end <= 0.0f) ? 1.0f : ((int) (end * 1000))/1000.0f;}
+ void setHollow(const F32 hollow) { mHollow = ((int) (hollow * 1000))/1000.0f;}
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfileParams &profile_params);
+
+protected:
+ // Profile params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ F32 mHollow;
+
+ U32 mCRC;
+};
+
+inline bool LLProfileParams::operator==(const LLProfileParams &params) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getHollow() == params.getHollow());
+}
+
+inline bool LLProfileParams::operator!=(const LLProfileParams &params) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getHollow() != params.getHollow());
+}
+
+
+inline bool LLProfileParams::operator<(const LLProfileParams &params) const
+{
+ if (getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if (getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if (getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ {
+ return getHollow() < params.getHollow();
+ }
+}
+
+#define U8_TO_F32(x) (F32)(*((S8 *)&x))
+
+class LLPathParams
+{
+public:
+ LLPathParams()
+ {
+ mBegin = 0;
+ mEnd = 1;
+ mScale.setVec(1,1);
+ mShear.setVec(0,0);
+ mCurveType = LL_PCODE_PATH_LINE;
+ mTwistBegin = 0;
+ mTwistEnd = 0;
+ mRadiusOffset = 0;
+ mTaper.setVec(0,0);
+ mRevolutions = 1;
+ mSkew = 0;
+ }
+
+ LLPathParams(U8 curve, F32 begin, F32 end, F32 scx, F32 scy, F32 shx, F32 shy, F32 twistend, F32 twistbegin, F32 radiusoffset, F32 tx, F32 ty, F32 revolutions, F32 skew)
+ : mCurveType(curve), mBegin(begin), mEnd(end), mTwistBegin(twistbegin), mTwistEnd(twistend),
+ mRadiusOffset(radiusoffset), mRevolutions(revolutions), mSkew(skew)
+ {
+ mScale.setVec(scx,scy);
+ mShear.setVec(shx,shy);
+ mTaper.setVec(tx,ty);
+ }
+
+ LLPathParams(U8 curve, U8 begin, U8 end, U8 scx, U8 scy, U8 shx, U8 shy, U8 twistend, U8 twistbegin, U8 radiusoffset, U8 tx, U8 ty, U8 revolutions, U8 skew)
+ {
+ mCurveType = curve;
+ mBegin = (F32)(begin * SCALE_QUANTA);
+ mEnd = (F32)(100.f - end) * SCALE_QUANTA;
+ if (mEnd > 1.f)
+ mEnd = 1.f;
+ mScale.setVec((F32) (200 - scx) * SCALE_QUANTA,(F32) (200 - scy) * SCALE_QUANTA);
+ mShear.setVec(U8_TO_F32(shx) * SHEAR_QUANTA,U8_TO_F32(shy) * SHEAR_QUANTA);
+ mTwistBegin = U8_TO_F32(twistbegin) * SCALE_QUANTA;
+ mTwistEnd = U8_TO_F32(twistend) * SCALE_QUANTA;
+ mRadiusOffset = U8_TO_F32(radiusoffset) * SCALE_QUANTA;
+ mTaper.setVec(U8_TO_F32(tx) * TAPER_QUANTA,U8_TO_F32(ty) * TAPER_QUANTA);
+ mRevolutions = ((F32)revolutions) * REV_QUANTA + 1.0f;
+ mSkew = U8_TO_F32(skew) * SCALE_QUANTA;
+ }
+
+ bool operator==(const LLPathParams &params) const;
+ bool operator!=(const LLPathParams &params) const;
+ bool operator<(const LLPathParams &params) const;
+
+ void copyParams(const LLPathParams &params);
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *fp) const;
+
+ BOOL importLegacyStream(std::istream& input_stream);
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ const F32& getBegin() const { return mBegin; }
+ const F32& getEnd() const { return mEnd; }
+ const LLVector2 &getScale() const { return mScale; }
+ const F32& getScaleX() const { return mScale.mV[0]; }
+ const F32& getScaleY() const { return mScale.mV[1]; }
+ const LLVector2 getBeginScale() const;
+ const LLVector2 getEndScale() const;
+ const LLVector2 &getShear() const { return mShear; }
+ const F32& getShearX() const { return mShear.mV[0]; }
+ const F32& getShearY() const { return mShear.mV[1]; }
+ const U8& getCurveType () const { return mCurveType; }
+
+ const F32& getTwistBegin() const { return mTwistBegin; }
+ const F32& getTwistEnd() const { return mTwistEnd; }
+ const F32& getTwist() const { return mTwistEnd; } // deprecated
+ const F32& getRadiusOffset() const { return mRadiusOffset; }
+ const LLVector2 &getTaper() const { return mTaper; }
+ const F32& getTaperX() const { return mTaper.mV[0]; }
+ const F32& getTaperY() const { return mTaper.mV[1]; }
+ const F32& getRevolutions() const { return mRevolutions; }
+ const F32& getSkew() const { return mSkew; }
+
+ void setCurveType(const U8 type) { mCurveType = type; }
+ void setBegin(const F32 begin) { mBegin = begin; }
+ void setEnd(const F32 end) { mEnd = end; }
+
+ void setScale(const F32 x, const F32 y) { mScale.setVec(x,y); }
+ void setScaleX(const F32 v) { mScale.mV[VX] = v; }
+ void setScaleY(const F32 v) { mScale.mV[VY] = v; }
+ void setShear(const F32 x, const F32 y) { mShear.setVec(x,y); }
+ void setShearX(const F32 v) { mShear.mV[VX] = v; }
+ void setShearY(const F32 v) { mShear.mV[VY] = v; }
+
+ void setTwistBegin(const F32 twist_begin) { mTwistBegin = twist_begin; }
+ void setTwistEnd(const F32 twist_end) { mTwistEnd = twist_end; }
+ void setTwist(const F32 twist) { setTwistEnd(twist); } // deprecated
+ void setRadiusOffset(const F32 radius_offset){ mRadiusOffset = radius_offset; }
+ void setTaper(const F32 x, const F32 y) { mTaper.setVec(x,y); }
+ void setTaperX(const F32 v) { mTaper.mV[VX] = v; }
+ void setTaperY(const F32 v) { mTaper.mV[VY] = v; }
+ void setRevolutions(const F32 revolutions) { mRevolutions = revolutions; }
+ void setSkew(const F32 skew) { mSkew = skew; }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPathParams &path_params);
+
+protected:
+ // Path params
+ U8 mCurveType;
+ F32 mBegin;
+ F32 mEnd;
+ LLVector2 mScale;
+ LLVector2 mShear;
+
+ F32 mTwistBegin;
+ F32 mTwistEnd;
+ F32 mRadiusOffset;
+ LLVector2 mTaper;
+ F32 mRevolutions;
+ F32 mSkew;
+
+ U32 mCRC;
+};
+
+inline bool LLPathParams::operator==(const LLPathParams &params) const
+{
+ return
+ (getCurveType() == params.getCurveType()) &&
+ (getScale() == params.getScale()) &&
+ (getBegin() == params.getBegin()) &&
+ (getEnd() == params.getEnd()) &&
+ (getShear() == params.getShear()) &&
+ (getTwist() == params.getTwist()) &&
+ (getTwistBegin() == params.getTwistBegin()) &&
+ (getRadiusOffset() == params.getRadiusOffset()) &&
+ (getTaper() == params.getTaper()) &&
+ (getRevolutions() == params.getRevolutions()) &&
+ (getSkew() == params.getSkew());
+}
+
+inline bool LLPathParams::operator!=(const LLPathParams &params) const
+{
+ return
+ (getCurveType() != params.getCurveType()) ||
+ (getScale() != params.getScale()) ||
+ (getBegin() != params.getBegin()) ||
+ (getEnd() != params.getEnd()) ||
+ (getShear() != params.getShear()) ||
+ (getTwist() != params.getTwist()) ||
+ (getTwistBegin() !=params.getTwistBegin()) ||
+ (getRadiusOffset() != params.getRadiusOffset()) ||
+ (getTaper() != params.getTaper()) ||
+ (getRevolutions() != params.getRevolutions()) ||
+ (getSkew() != params.getSkew());
+}
+
+
+inline bool LLPathParams::operator<(const LLPathParams &params) const
+{
+ if( getCurveType() != params.getCurveType())
+ {
+ return getCurveType() < params.getCurveType();
+ }
+ else
+ if( getScale() != params.getScale())
+ {
+ return getScale() < params.getScale();
+ }
+ else
+ if( getBegin() != params.getBegin())
+ {
+ return getBegin() < params.getBegin();
+ }
+ else
+ if( getEnd() != params.getEnd())
+ {
+ return getEnd() < params.getEnd();
+ }
+ else
+ if( getShear() != params.getShear())
+ {
+ return getShear() < params.getShear();
+ }
+ else
+ if( getTwist() != params.getTwist())
+ {
+ return getTwist() < params.getTwist();
+ }
+ else
+ if( getTwistBegin() != params.getTwistBegin())
+ {
+ return getTwistBegin() < params.getTwistBegin();
+ }
+ else
+ if( getRadiusOffset() != params.getRadiusOffset())
+ {
+ return getRadiusOffset() < params.getRadiusOffset();
+ }
+ else
+ if( getTaper() != params.getTaper())
+ {
+ return getTaper() < params.getTaper();
+ }
+ else
+ if( getRevolutions() != params.getRevolutions())
+ {
+ return getRevolutions() < params.getRevolutions();
+ }
+ else
+ {
+ return getSkew() < params.getSkew();
+ }
+}
+
+typedef LLVolumeParams* LLVolumeParamsPtr;
+typedef const LLVolumeParams* const_LLVolumeParamsPtr;
+
+class LLVolumeParams
+{
+public:
+ LLVolumeParams()
+ {
+ }
+
+ LLVolumeParams(LLProfileParams &profile, LLPathParams &path)
+ : mProfileParams(profile), mPathParams(path)
+ {
+ }
+
+ bool operator==(const LLVolumeParams &params) const;
+ bool operator!=(const LLVolumeParams &params) const;
+ bool operator<(const LLVolumeParams &params) const;
+
+
+ void copyParams(const LLVolumeParams &params);
+
+ const LLProfileParams &getProfileParams() const {return mProfileParams;}
+ LLProfileParams &getProfileParams() {return mProfileParams;}
+ const LLPathParams &getPathParams() const {return mPathParams;}
+ LLPathParams &getPathParams() {return mPathParams;}
+
+ BOOL importFile(FILE *fp);
+ BOOL exportFile(FILE *fp) const;
+
+ BOOL importLegacyStream(std::istream& input_stream);
+ BOOL exportLegacyStream(std::ostream& output_stream) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ bool setType(U8 profile, U8 path);
+
+ //void setBeginS(const F32 beginS) { mProfileParams.setBegin(beginS); } // range 0 to 1
+ //void setBeginT(const F32 beginT) { mPathParams.setBegin(beginT); } // range 0 to 1
+ //void setEndS(const F32 endS) { mProfileParams.setEnd(endS); } // range 0 to 1, must be greater than begin
+ //void setEndT(const F32 endT) { mPathParams.setEnd(endT); } // range 0 to 1, must be greater than begin
+
+ bool setBeginAndEndS(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
+ bool setBeginAndEndT(const F32 begin, const F32 end); // both range from 0 to 1, begin must be less than end
+
+ bool setHollow(const F32 hollow); // range 0 to 1
+ bool setRatio(const F32 x) { return setRatio(x,x); } // 0 = point, 1 = same as base
+ bool setShear(const F32 x) { return setShear(x,x); } // 0 = no movement,
+ bool setRatio(const F32 x, const F32 y); // 0 = point, 1 = same as base
+ bool setShear(const F32 x, const F32 y); // 0 = no movement
+
+ bool setTwistBegin(const F32 twist_begin); // range -1 to 1
+ bool setTwistEnd(const F32 twist_end); // range -1 to 1
+ bool setTwist(const F32 twist) { return setTwistEnd(twist); } // deprecated
+ bool setTaper(const F32 x, const F32 y) { bool pass_x = setTaperX(x); bool pass_y = setTaperY(y); return pass_x && pass_y; }
+ bool setTaperX(const F32 v); // -1 to 1
+ bool setTaperY(const F32 v); // -1 to 1
+ bool setRevolutions(const F32 revolutions); // 1 to 4
+ bool setRadiusOffset(const F32 radius_offset);
+ bool setSkew(const F32 skew);
+
+ static bool validate(U8 prof_curve, F32 prof_begin, F32 prof_end, F32 hollow,
+ U8 path_curve, F32 path_begin, F32 path_end,
+ F32 scx, F32 scy, F32 shx, F32 shy,
+ F32 twistend, F32 twistbegin, F32 radiusoffset,
+ F32 tx, F32 ty, F32 revolutions, F32 skew);
+
+ const F32& getBeginS() const { return mProfileParams.getBegin(); }
+ const F32& getBeginT() const { return mPathParams.getBegin(); }
+ const F32& getEndS() const { return mProfileParams.getEnd(); }
+ const F32& getEndT() const { return mPathParams.getEnd(); }
+
+ const F32& getHollow() const { return mProfileParams.getHollow(); }
+ const F32& getTwist() const { return mPathParams.getTwist(); }
+ const F32& getRatio() const { return mPathParams.getScaleX(); }
+ const F32& getRatioX() const { return mPathParams.getScaleX(); }
+ const F32& getRatioY() const { return mPathParams.getScaleY(); }
+ const F32& getShearX() const { return mPathParams.getShearX(); }
+ const F32& getShearY() const { return mPathParams.getShearY(); }
+
+ const F32& getTwistBegin()const { return mPathParams.getTwistBegin(); }
+ const F32& getRadiusOffset() const { return mPathParams.getRadiusOffset(); }
+ const F32& getTaper() const { return mPathParams.getTaperX(); }
+ const F32& getTaperX() const { return mPathParams.getTaperX(); }
+ const F32& getTaperY() const { return mPathParams.getTaperY(); }
+ const F32& getRevolutions() const { return mPathParams.getRevolutions(); }
+ const F32& getSkew() const { return mPathParams.getSkew(); }
+
+ BOOL isConvex() const;
+
+ // 'begin' and 'end' should be in range [0, 1] (they will be clamped)
+ // (begin, end) = (0, 1) will not change the volume
+ // (begin, end) = (0, 0.5) will reduce the volume to the first half of its profile/path (S/T)
+ void reduceS(F32 begin, F32 end);
+ void reduceT(F32 begin, F32 end);
+
+ struct compare
+ {
+ bool operator()( const const_LLVolumeParamsPtr& first, const const_LLVolumeParamsPtr& second) const
+ {
+ return (*first < *second);
+ }
+ };
+
+ friend std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+
+protected:
+ LLProfileParams mProfileParams;
+ LLPathParams mPathParams;
+};
+
+
+class LLProfile
+{
+public:
+ LLProfile(const LLProfileParams &params) : mParams(params)
+ {
+ mTotal = 2;
+ mTotalOut = 0;
+ mDirty = TRUE;
+ mConcave = FALSE;
+ }
+
+ ~LLProfile();
+
+ S32 getTotal() const { return mTotal; }
+ S32 getTotalOut() const { return mTotalOut; } // Total number of outside points
+ BOOL isHollow() const { return (mParams.getHollow() > 0); }
+ BOOL isFlat(S32 face) const { return (mFaces[face].mCount == 2); }
+ BOOL isOpen() const { return mOpen; }
+ void setDirty() { mDirty = TRUE; }
+ BOOL generate(BOOL path_open, F32 detail = 1.0f, S32 split = 0);
+ BOOL isConcave() const { return mConcave; }
+public:
+ const LLProfileParams &mParams;
+
+ struct Face
+ {
+ S32 mIndex;
+ S32 mCount;
+ F32 mScaleU;
+ BOOL mCap;
+ BOOL mFlat;
+ LLFaceID mFaceID;
+ };
+
+ std::vector<LLVector3> mProfile;
+ std::vector<LLVector2> mNormals;
+ std::vector<Face> mFaces;
+ std::vector<LLVector3> mEdgeNormals;
+ std::vector<LLVector3> mEdgeCenters;
+ F32 mMaxX;
+ F32 mMinX;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLProfile &profile);
+
+protected:
+ void genNormals();
+ void genNGon(S32 sides, F32 offset=0.0f, F32 bevel = 0.0f, F32 ang_scale = 1.f, S32 split = 0);
+
+ Face* addHole(BOOL flat, F32 sides, F32 offset, F32 box_hollow, F32 ang_scale, S32 split = 0);
+ Face* addCap (S16 faceID);
+ Face* addFace(S32 index, S32 count, F32 scaleU, S16 faceID, BOOL flat);
+
+protected:
+ BOOL mOpen;
+ BOOL mConcave;
+ BOOL mDirty;
+
+ S32 mTotalOut;
+ S32 mTotal;
+};
+
+//-------------------------------------------------------------------
+// SWEEP/EXTRUDE PATHS
+//-------------------------------------------------------------------
+
+class LLPath
+{
+public:
+ struct PathPt
+ {
+ LLVector3 mPos;
+ LLVector2 mScale;
+ LLQuaternion mRot;
+ F32 mTexT;
+ PathPt() { mPos.setVec(0,0,0); mTexT = 0; mScale.setVec(0,0); mRot.loadIdentity(); }
+ };
+
+public:
+ LLPath(const LLPathParams &params) : mParams(params)
+ {
+ mOpen = FALSE;
+ mDirty = TRUE;
+ mStep = 1;
+ }
+
+ virtual ~LLPath();
+
+ void genNGon(S32 sides, F32 offset=0.0f, F32 end_scale = 1.f, F32 twist_scale = 1.f);
+ virtual BOOL generate(F32 detail=1.0f, S32 split = 0);
+
+ BOOL isOpen() const { return mOpen; }
+ F32 getStep() const { return mStep; }
+ void setDirty() { mDirty = TRUE; }
+
+ S32 getPathLength() const { return (S32)mPath.size(); }
+
+ void resizePath(S32 length) { mPath.resize(length); }
+
+ friend std::ostream& operator<<(std::ostream &s, const LLPath &path);
+
+public:
+ const LLPathParams &mParams;
+ std::vector<PathPt> mPath;
+
+protected:
+ BOOL mOpen;
+ S32 mTotal;
+ BOOL mDirty;
+ F32 mStep;
+};
+
+class LLDynamicPath : public LLPath
+{
+public:
+ LLDynamicPath(const LLPathParams &params) : LLPath(params) { }
+ BOOL generate(F32 detail=1.0f, S32 split = 0);
+};
+
+// Yet another "face" class - caches volume-specific, but not instance-specific data for faces)
+class LLVolumeFace
+{
+public:
+ LLVolumeFace();
+ BOOL create();
+
+ class VertexData
+ {
+ public:
+ LLVector3 mPosition;
+ LLVector3 mNormal;
+ LLVector3 mBinormal;
+ LLVector2 mTexCoord;
+ };
+
+ enum
+ {
+ SINGLE_MASK = 0x0001,
+ CAP_MASK = 0x0002,
+ END_MASK = 0x0004,
+ SIDE_MASK = 0x0008,
+ INNER_MASK = 0x0010,
+ OUTER_MASK = 0x0020,
+ HOLLOW_MASK = 0x0040,
+ OPEN_MASK = 0x0080,
+ FLAT_MASK = 0x0100,
+ TOP_MASK = 0x0200,
+ BOTTOM_MASK = 0x0400
+ } TypeMask;
+
+public:
+ S32 mID;
+ U32 mTypeMask;
+ LLVector3 mCenter;
+
+ // Only used for INNER/OUTER faces
+ S32 mBeginS;
+ S32 mBeginT;
+ S32 mNumS;
+ S32 mNumT;
+
+ std::vector<VertexData> mVertices;
+ std::vector<S32> mIndices;
+ std::vector<S32> mEdge;
+ LLVolume *mVolumep; // Deliberately NOT reference counted - djs 11/20/03 - otherwise would make an annoying circular reference
+
+ // Shouldn't need num_old and num_new, really - djs
+ static BOOL updateColors(LLColor4U *old_colors, const S32 num_old, const LLVolumeFace &old_face,
+ LLStrider<LLColor4U> &new_colors, const S32 num_new, const LLVolumeFace &new_face);
+
+protected:
+ BOOL createUnCutCubeCap();
+ BOOL createCap();
+ BOOL createSide();
+};
+
+class LLVolume : public LLRefCount
+{
+ friend class LLVolumeLODGroup;
+
+private:
+ LLVolume(const LLVolume&); // Don't implement
+ ~LLVolume(); // use unref
+
+public:
+ struct Point
+ {
+ LLVector3 mPos;
+ };
+
+ struct FaceParams
+ {
+ LLFaceID mFaceID;
+ S32 mBeginS;
+ S32 mCountS;
+ S32 mBeginT;
+ S32 mCountT;
+ };
+
+ LLVolume(const LLVolumeParams &params, const F32 detail, const BOOL generate_single_face = FALSE, const BOOL is_unique = FALSE);
+
+ U8 getProfileType() const { return mProfilep->mParams.getCurveType(); }
+ U8 getPathType() const { return mPathp->mParams.getCurveType(); }
+ S32 getNumFaces() const { return (S32)mProfilep->mFaces.size(); }
+
+ const F32 getDetail() const { return mDetail; }
+ const LLVolumeParams & getParams() const { return mParams; }
+ LLVolumeParams getCopyOfParams() const { return mParams; }
+ const LLProfile& getProfile() const { return *mProfilep; }
+ LLPath& getPath() const { return *mPathp; }
+ const std::vector<Point>& getMesh() const { return mMesh; }
+ const LLVector3& getMeshPt(const U32 i) const { return mMesh[i].mPos; }
+
+ void setDirty() { mPathp->setDirty(); mProfilep->setDirty(); }
+
+ void regen();
+
+ BOOL isConvex() const;
+ BOOL isCap(S32 face);
+ BOOL isFlat(S32 face);
+ BOOL isUnique() const { return mUnique; }
+
+ S32 *getTriangleIndices(U32 &num_indices) const;
+ void generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<LLVector3> &normals, std::vector<S32> &segments, const LLVector3& view_vec,
+ const LLMatrix4& mat,
+ const LLMatrix3& norm_mat);
+
+ //get the face index of the face that intersects with the given line segment at the point
+ //closest to start. Moves end to the point of intersection. Returns -1 if no intersection.
+ //Line segment must be in volume space.
+ S32 lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+
+ // The following cleans up vertices and triangles,
+ // getting rid of degenerate triangles and duplicate vertices,
+ // and allocates new arrays with the clean data.
+ static BOOL cleanupTriangleData( const S32 num_input_vertices,
+ const std::vector<Point> &input_vertices,
+ const S32 num_input_triangles,
+ S32 *input_triangles,
+ S32 &num_output_vertices,
+ LLVector3 **output_vertices,
+ S32 &num_output_triangles,
+ S32 **output_triangles);
+ LLFaceID generateFaceMask();
+
+ BOOL isFaceMaskValid(LLFaceID face_mask);
+ static S32 mNumMeshPoints;
+
+ friend std::ostream& operator<<(std::ostream &s, const LLVolume &volume);
+ friend std::ostream& operator<<(std::ostream &s, const LLVolume *volumep); // HACK to bypass Windoze confusion over
+ // conversion if *(LLVolume*) to LLVolume&
+ const LLVolumeFace &getVolumeFace(const S32 f) const {return mVolumeFaces[f];} // DO NOT DELETE VOLUME WHILE USING THIS REFERENCE, OR HOLD A POINTER TO THIS VOLUMEFACE
+
+ U32 mFaceMask; // bit array of which faces exist in this volume
+ LLVector3 mBounds[2]; // bounding box (center, half-height)
+ LLVector3 mLODScaleBias; // vector for biasing LOD based on scale
+
+protected:
+ BOOL generate();
+ void createVolumeFaces();
+
+protected:
+ BOOL mUnique;
+ F32 mDetail;
+ LLVolumeParams mParams;
+ LLPath *mPathp;
+ LLProfile *mProfilep;
+ std::vector<Point> mMesh;
+
+ BOOL mGenerateSingleFace;
+ S32 mNumVolumeFaces;
+ LLVolumeFace *mVolumeFaces;
+};
+
+std::ostream& operator<<(std::ostream &s, const LLVolumeParams &volume_params);
+
+LLVector3 calc_binormal_from_triangle(
+ const LLVector3& pos0,
+ const LLVector2& tex0,
+ const LLVector3& pos1,
+ const LLVector2& tex1,
+ const LLVector3& pos2,
+ const LLVector2& tex2);
+
+#endif
diff --git a/indra/llmath/llvolumemgr.cpp b/indra/llmath/llvolumemgr.cpp
new file mode 100644
index 0000000000..54be916c12
--- /dev/null
+++ b/indra/llmath/llvolumemgr.cpp
@@ -0,0 +1,295 @@
+/**
+ * @file llvolumemgr.cpp
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llvolumemgr.h"
+#include "llvolume.h"
+
+
+//#define DEBUG_VOLUME
+
+LLVolumeMgr* gVolumeMgr = 0;
+
+const F32 BASE_THRESHOLD = 0.03f;
+
+//static
+F32 LLVolumeLODGroup::mDetailThresholds[NUM_LODS] = {BASE_THRESHOLD,
+ 2*BASE_THRESHOLD,
+ 8*BASE_THRESHOLD,
+ 100*BASE_THRESHOLD};
+
+//static
+F32 LLVolumeLODGroup::mDetailScales[NUM_LODS] = {1.f, 1.5f, 2.5f, 4.f};
+
+//============================================================================
+//static
+void LLVolumeMgr::initClass()
+{
+ gVolumeMgr = new LLVolumeMgr();
+}
+
+//static
+BOOL LLVolumeMgr::cleanupClass()
+{
+ BOOL res = FALSE;
+ if (gVolumeMgr) {
+ res = gVolumeMgr->cleanup();
+ delete gVolumeMgr;
+ gVolumeMgr = 0;
+ }
+ return res;
+}
+
+//============================================================================
+
+LLVolumeMgr::LLVolumeMgr()
+{
+ mDataMutex = new LLMutex(gAPRPoolp);
+// mNumVolumes = 0;
+}
+
+LLVolumeMgr::~LLVolumeMgr()
+{
+ cleanup();
+ delete mDataMutex;
+}
+
+BOOL LLVolumeMgr::cleanup()
+{
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::cleanup()" << llendl;
+ }
+ #endif
+ BOOL no_refs = TRUE;
+ mDataMutex->lock();
+ for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
+ end = mVolumeLODGroups.end();
+ iter != end; iter++)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ if (volgroupp->getNumRefs() != 1)
+ {
+ llwarns << "Volume group " << volgroupp << " has "
+ << volgroupp->getNumRefs() << " remaining refs" << llendl;
+ llwarns << volgroupp->getParams() << llendl;
+ no_refs = FALSE;
+ }
+ volgroupp->unref();// this );
+ }
+ mVolumeLODGroups.clear();
+ mDataMutex->unlock();
+ return no_refs;
+}
+
+LLVolume *LLVolumeMgr::getVolume(const LLVolumeParams &volume_params, const S32 detail)
+{
+ LLVolumeLODGroup* volgroupp;
+ mDataMutex->lock();
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(&volume_params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ volgroupp = new LLVolumeLODGroup(volume_params);
+ const LLVolumeParams* params = &(volgroupp->getParams());
+ mVolumeLODGroups[params] = volgroupp;
+ volgroupp->ref(); // initial reference
+ }
+ else
+ {
+ volgroupp = iter->second;
+ }
+ volgroupp->ref();// this );
+ mDataMutex->unlock();
+ // mNumVolumes++;
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::getVolume() " << (*this) << llendl;
+ }
+ #endif
+ return volgroupp->getLOD(detail);
+}
+
+void LLVolumeMgr::cleanupVolume(LLVolume *volumep)
+{
+ if (volumep->isUnique())
+ {
+ // TomY: Don't need to manage this volume. It is a unique instance.
+ return;
+ }
+ LLVolumeParams* params = (LLVolumeParams*) &(volumep->getParams());
+ mDataMutex->lock();
+ volume_lod_group_map_t::iterator iter = mVolumeLODGroups.find(params);
+ if( iter == mVolumeLODGroups.end() )
+ {
+ llerrs << "Warning! Tried to cleanup unknown volume type! " << *params << llendl;
+ mDataMutex->unlock();
+ return;
+ }
+ else
+ {
+ LLVolumeLODGroup* volgroupp = iter->second;
+
+ volgroupp->derefLOD(volumep);
+ volgroupp->unref();// this );
+ if (volgroupp->getNumRefs() == 1)
+ {
+ mVolumeLODGroups.erase(params);
+ volgroupp->unref();// this );
+ }
+ // mNumVolumes--;
+ }
+ mDataMutex->unlock();
+
+ #ifdef DEBUG_VOLUME
+ {
+ lldebugs << "LLVolumeMgr::cleanupVolume() " << (*this) << llendl;
+ }
+ #endif
+}
+
+void LLVolumeMgr::dump()
+{
+ F32 avg = 0.f;
+ mDataMutex->lock();
+ for (volume_lod_group_map_t::iterator iter = mVolumeLODGroups.begin(),
+ end = mVolumeLODGroups.end();
+ iter != end; iter++)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ avg += volgroupp->dump();
+ }
+ int count = (int)mVolumeLODGroups.size();
+ avg = count ? avg / (F32)count : 0.0f;
+ mDataMutex->unlock();
+ llinfos << "Average usage of LODs " << avg << llendl;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr)
+{
+ s << "{ numLODgroups=" << volume_mgr.mVolumeLODGroups.size() << ", ";
+
+ S32 total_refs = 0;
+ volume_mgr.mDataMutex->lock();
+
+ LLVolumeMgr::volume_lod_group_map_iter iter = volume_mgr.mVolumeLODGroups.begin();
+ LLVolumeMgr::volume_lod_group_map_iter end = volume_mgr.mVolumeLODGroups.end();
+ for ( ; iter != end; ++iter)
+ {
+ LLVolumeLODGroup *volgroupp = iter->second;
+ total_refs += volgroupp->getNumRefs();
+ s << ", " << (*volgroupp);
+ }
+
+ volume_mgr.mDataMutex->unlock();
+
+ s << ", total_refs=" << total_refs << " }";
+ return s;
+}
+
+LLVolumeLODGroup::LLVolumeLODGroup(const LLVolumeParams &params)
+{
+ S32 i;
+ mParams = params;
+
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ mLODRefs[i] = 0;
+ mVolumeLODs[i] = NULL;
+ mAccessCount[i] = 0;
+ }
+}
+
+LLVolumeLODGroup::~LLVolumeLODGroup()
+{
+ S32 i;
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ delete mVolumeLODs[i];
+ mVolumeLODs[i] = NULL;
+ }
+}
+
+
+LLVolume * LLVolumeLODGroup::getLOD(const S32 detail)
+{
+ llassert(detail >=0 && detail < NUM_LODS);
+ mAccessCount[detail]++;
+ mLODRefs[detail]++;
+ if (!mVolumeLODs[detail])
+ {
+ mVolumeLODs[detail] = new LLVolume(mParams, mDetailScales[detail]);
+ }
+ return mVolumeLODs[detail];
+}
+
+BOOL LLVolumeLODGroup::derefLOD(LLVolume *volumep)
+{
+ S32 i;
+ for (i = 0; i < NUM_LODS; i++)
+ {
+ if (mVolumeLODs[i] == volumep)
+ {
+ mLODRefs[i]--;
+ if (!mLODRefs[i])
+ {
+ mVolumeLODs[i] = NULL;
+ }
+ return TRUE;
+ }
+ }
+ llerrs << "Deref of non-matching LOD in volume LOD group" << llendl;
+ return FALSE;
+}
+
+S32 LLVolumeLODGroup::getDetailFromTan(const F32 tan_angle)
+{
+ S32 i = 0;
+ while (i < (NUM_LODS - 1))
+ {
+ if (tan_angle <= mDetailThresholds[i])
+ {
+ return i;
+ }
+ i++;
+ }
+ return NUM_LODS - 1;
+}
+
+F32 LLVolumeLODGroup::getVolumeScaleFromDetail(const S32 detail)
+{
+ return mDetailScales[detail];
+}
+
+F32 LLVolumeLODGroup::dump()
+{
+ char dump_str[255];
+ F32 usage = 0.f;
+ for (S32 i = 0; i < NUM_LODS; i++)
+ {
+ if (mAccessCount[i] > 0)
+ {
+ usage += 1.f;
+ }
+ }
+ usage = usage / (F32)NUM_LODS;
+
+ sprintf(dump_str, "%.3f %d %d %d %d", usage, mAccessCount[0], mAccessCount[1], mAccessCount[2], mAccessCount[3]);
+
+ llinfos << dump_str << llendl;
+ return usage;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup)
+{
+ s << "{ numRefs=" << volgroup.getNumRefs();
+ s << ", mParams=" << volgroup.mParams;
+ s << " }";
+
+ return s;
+}
+
diff --git a/indra/llmath/llvolumemgr.h b/indra/llmath/llvolumemgr.h
new file mode 100644
index 0000000000..675c8f640c
--- /dev/null
+++ b/indra/llmath/llvolumemgr.h
@@ -0,0 +1,82 @@
+/**
+ * @file llvolumemgr.h
+ * @brief LLVolumeMgr class.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUMEMGR_H
+#define LL_LLVOLUMEMGR_H
+
+#include <map>
+
+#include "llvolume.h"
+#include "llmemory.h"
+#include "llthread.h"
+
+class LLVolumeParams;
+class LLVolumeLODGroup;
+
+class LLVolumeLODGroup : public LLThreadSafeRefCount
+{
+protected:
+ ~LLVolumeLODGroup();
+
+public:
+ enum
+ {
+ NUM_LODS = 4
+ };
+
+ LLVolumeLODGroup(const LLVolumeParams &params);
+
+ BOOL derefLOD(LLVolume *volumep);
+ static S32 getDetailFromTan(const F32 tan_angle);
+ static F32 getVolumeScaleFromDetail(const S32 detail);
+
+ LLVolume *getLOD(const S32 detail);
+ const LLVolumeParams &getParams() const { return mParams; };
+
+ F32 dump();
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeLODGroup& volgroup);
+
+protected:
+ LLVolumeParams mParams;
+
+ S32 mLODRefs[NUM_LODS];
+ LLVolume *mVolumeLODs[NUM_LODS];
+ static F32 mDetailThresholds[NUM_LODS];
+ static F32 mDetailScales[NUM_LODS];
+ S32 mAccessCount[NUM_LODS];
+};
+
+class LLVolumeMgr
+{
+public:
+ static void initClass();
+ static BOOL cleanupClass();
+
+public:
+ LLVolumeMgr();
+ ~LLVolumeMgr();
+ BOOL cleanup(); // Cleanup all volumes being managed, returns TRUE if no dangling references
+ LLVolume *getVolume(const LLVolumeParams &volume_params, const S32 detail);
+ void cleanupVolume(LLVolume *volumep);
+
+ void dump();
+ friend std::ostream& operator<<(std::ostream& s, const LLVolumeMgr& volume_mgr);
+
+protected:
+ typedef std::map<const LLVolumeParams*, LLVolumeLODGroup*, LLVolumeParams::compare> volume_lod_group_map_t;
+ typedef volume_lod_group_map_t::const_iterator volume_lod_group_map_iter;
+ volume_lod_group_map_t mVolumeLODGroups;
+
+ LLMutex* mDataMutex;
+
+// S32 mNumVolumes;
+};
+
+extern LLVolumeMgr* gVolumeMgr;
+
+#endif // LL_LLVOLUMEMGR_H
diff --git a/indra/llmath/m3math.cpp b/indra/llmath/m3math.cpp
new file mode 100644
index 0000000000..523acb4dcf
--- /dev/null
+++ b/indra/llmath/m3math.cpp
@@ -0,0 +1,530 @@
+/**
+ * @file m3math.cpp
+ * @brief LLMatrix3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+
+// LLMatrix3
+
+// ji
+// LLMatrix3 = |00 01 02 |
+// |10 11 12 |
+// |20 21 22 |
+
+// LLMatrix3 = |fx fy fz | forward-axis
+// |lx ly lz | left-axis
+// |ux uy uz | up-axis
+
+
+// Constructors
+
+
+LLMatrix3::LLMatrix3(const LLQuaternion &q)
+{
+ *this = q.getMatrix3();
+}
+
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector3 &vec)
+{
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector3d &vec)
+{
+ LLVector3 vec_f;
+ vec_f.setVec(vec);
+ LLQuaternion quat(angle, vec_f);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const LLVector4 &vec)
+{
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLVector3 vec(x, y, z);
+ LLQuaternion quat(angle, vec);
+ *this = setRot(quat);
+}
+
+LLMatrix3::LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ // Rotates RH about x-axis by 'roll' then
+ // rotates RH about the old y-axis by 'pitch' then
+ // rotates RH about the original z-axis by 'yaw'.
+ // .
+ // /|\ yaw axis
+ // | __.
+ // ._ ___| /| pitch axis
+ // _||\ \\ |-. /
+ // \|| \_______\_|__\_/_______
+ // | _ _ o o o_o_o_o o /_\_ ________\ roll axis
+ // // /_______/ /__________> /
+ // /_,-' // /
+ // /__,-'
+
+ F32 cx, sx, cy, sy, cz, sz;
+ F32 cxsy, sxsy;
+
+ cx = (F32)cos(roll); //A
+ sx = (F32)sin(roll); //B
+ cy = (F32)cos(pitch); //C
+ sy = (F32)sin(pitch); //D
+ cz = (F32)cos(yaw); //E
+ sz = (F32)sin(yaw); //F
+
+ cxsy = cx * sy; //AD
+ sxsy = sx * sy; //BD
+
+ mMatrix[0][0] = cy * cz;
+ mMatrix[1][0] = -cy * sz;
+ mMatrix[2][0] = sy;
+ mMatrix[0][1] = sxsy * cz + cx * sz;
+ mMatrix[1][1] = -sxsy * sz + cx * cz;
+ mMatrix[2][1] = -sx * cy;
+ mMatrix[0][2] = -cxsy * cz + sx * sz;
+ mMatrix[1][2] = cxsy * sz + sx * cz;
+ mMatrix[2][2] = cx * cy;
+}
+
+// From Matrix and Quaternion FAQ
+void LLMatrix3::getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const
+{
+ F64 angle_x, angle_y, angle_z;
+ F64 cx, cy, cz; // cosine of angle_x, angle_y, angle_z
+ F64 sx, sz; // sine of angle_x, angle_y, angle_z
+
+ angle_y = asin(llclamp(mMatrix[2][0], -1.f, 1.f));
+ cy = cos(angle_y);
+
+ if (fabs(cy) > 0.005) // non-zero
+ {
+ // no gimbal lock
+ cx = mMatrix[2][2] / cy;
+ sx = - mMatrix[2][1] / cy;
+
+ angle_x = (F32) atan2(sx, cx);
+
+ cz = mMatrix[0][0] / cy;
+ sz = - mMatrix[1][0] / cy;
+
+ angle_z = (F32) atan2(sz, cz);
+ }
+ else
+ {
+ // yup, gimbal lock
+ angle_x = 0;
+
+ // some tricky math thereby avoided, see article
+
+ cz = mMatrix[1][1];
+ sz = mMatrix[0][1];
+
+ angle_z = atan2(sz, cz);
+ }
+
+ *roll = (F32)angle_x;
+ *pitch = (F32)angle_y;
+ *yaw = (F32)angle_z;
+}
+
+
+// Clear and Assignment Functions
+
+const LLMatrix3& LLMatrix3::identity()
+{
+ mMatrix[0][0] = 1.f;
+ mMatrix[0][1] = 0.f;
+ mMatrix[0][2] = 0.f;
+
+ mMatrix[1][0] = 0.f;
+ mMatrix[1][1] = 1.f;
+ mMatrix[1][2] = 0.f;
+
+ mMatrix[2][0] = 0.f;
+ mMatrix[2][1] = 0.f;
+ mMatrix[2][2] = 1.f;
+ return (*this);
+}
+
+const LLMatrix3& LLMatrix3::zero()
+{
+ mMatrix[0][0] = 0.f;
+ mMatrix[0][1] = 0.f;
+ mMatrix[0][2] = 0.f;
+
+ mMatrix[1][0] = 0.f;
+ mMatrix[1][1] = 0.f;
+ mMatrix[1][2] = 0.f;
+
+ mMatrix[2][0] = 0.f;
+ mMatrix[2][1] = 0.f;
+ mMatrix[2][2] = 0.f;
+ return (*this);
+}
+
+// various useful mMatrix functions
+
+const LLMatrix3& LLMatrix3::transpose()
+{
+ // transpose the matrix
+ F32 temp;
+ temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
+ temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
+ temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
+ return *this;
+}
+
+
+F32 LLMatrix3::determinant() const
+{
+ // Is this a useful method when we assume the matrices are valid rotation
+ // matrices throughout this implementation?
+ return mMatrix[0][0] * (mMatrix[1][1] * mMatrix[2][2] - mMatrix[1][2] * mMatrix[2][1]) +
+ mMatrix[0][1] * (mMatrix[1][2] * mMatrix[2][0] - mMatrix[1][0] * mMatrix[2][2]) +
+ mMatrix[0][2] * (mMatrix[1][0] * mMatrix[2][1] - mMatrix[1][1] * mMatrix[2][0]);
+}
+
+// This is identical to the transMat3() method because we assume a rotation matrix
+const LLMatrix3& LLMatrix3::invert()
+{
+ // transpose the matrix
+ F32 temp;
+ temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
+ temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
+ temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
+ return *this;
+}
+
+// does not assume a rotation matrix, and does not divide by determinant, assuming results will be renormalized
+const LLMatrix3& LLMatrix3::adjointTranspose()
+{
+ LLMatrix3 adjoint_transpose;
+ adjoint_transpose.mMatrix[VX][VX] = mMatrix[VY][VY] * mMatrix[VZ][VZ] - mMatrix[VY][VZ] * mMatrix[VZ][VY] ;
+ adjoint_transpose.mMatrix[VY][VX] = mMatrix[VY][VZ] * mMatrix[VZ][VX] - mMatrix[VY][VX] * mMatrix[VZ][VZ] ;
+ adjoint_transpose.mMatrix[VZ][VX] = mMatrix[VY][VX] * mMatrix[VZ][VY] - mMatrix[VY][VY] * mMatrix[VZ][VX] ;
+ adjoint_transpose.mMatrix[VX][VY] = mMatrix[VZ][VY] * mMatrix[VX][VZ] - mMatrix[VZ][VZ] * mMatrix[VX][VY] ;
+ adjoint_transpose.mMatrix[VY][VY] = mMatrix[VZ][VZ] * mMatrix[VX][VX] - mMatrix[VZ][VX] * mMatrix[VX][VZ] ;
+ adjoint_transpose.mMatrix[VZ][VY] = mMatrix[VZ][VX] * mMatrix[VX][VY] - mMatrix[VZ][VY] * mMatrix[VX][VX] ;
+ adjoint_transpose.mMatrix[VX][VZ] = mMatrix[VX][VY] * mMatrix[VY][VZ] - mMatrix[VX][VZ] * mMatrix[VY][VY] ;
+ adjoint_transpose.mMatrix[VY][VZ] = mMatrix[VX][VZ] * mMatrix[VY][VX] - mMatrix[VX][VX] * mMatrix[VY][VZ] ;
+ adjoint_transpose.mMatrix[VZ][VZ] = mMatrix[VX][VX] * mMatrix[VY][VY] - mMatrix[VX][VY] * mMatrix[VY][VX] ;
+
+ *this = adjoint_transpose;
+ return *this;
+}
+
+// SJB: This code is correct for a logicly stored (non-transposed) matrix;
+// Our matrices are stored transposed, OpenGL style, so this generates the
+// INVERSE quaternion (-x, -y, -z, w)!
+// Because we use similar logic in LLQuaternion::getMatrix3,
+// we are internally consistant so everything works OK :)
+LLQuaternion LLMatrix3::quaternion() const
+{
+ LLQuaternion quat;
+ F32 tr, s, q[4];
+ U32 i, j, k;
+ U32 nxt[3] = {1, 2, 0};
+
+ tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];
+
+ // check the diagonal
+ if (tr > 0.f)
+ {
+ s = (F32)sqrt (tr + 1.f);
+ quat.mQ[VS] = s / 2.f;
+ s = 0.5f / s;
+ quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
+ quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
+ quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
+ }
+ else
+ {
+ // diagonal is negative
+ i = 0;
+ if (mMatrix[1][1] > mMatrix[0][0])
+ i = 1;
+ if (mMatrix[2][2] > mMatrix[i][i])
+ i = 2;
+
+ j = nxt[i];
+ k = nxt[j];
+
+
+ s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);
+
+ q[i] = s * 0.5f;
+
+ if (s != 0.f)
+ s = 0.5f / s;
+
+ q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
+ q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
+ q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;
+
+ quat.setQuat(q);
+ }
+ return quat;
+}
+
+
+// These functions take Rotation arguments
+const LLMatrix3& LLMatrix3::setRot(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ *this = mat;
+ return *this;
+}
+
+const LLMatrix3& LLMatrix3::setRot(const F32 angle, const LLVector3 &vec)
+{
+ LLMatrix3 mat(angle, vec);
+ *this = mat;
+ return *this;
+}
+
+const LLMatrix3& LLMatrix3::setRot(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ *this = mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::setRot(const LLQuaternion &q)
+{
+ LLMatrix3 mat(q);
+ *this = mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::setRows(const LLVector3 &fwd, const LLVector3 &left, const LLVector3 &up)
+{
+ mMatrix[0][0] = fwd.mV[0];
+ mMatrix[0][1] = fwd.mV[1];
+ mMatrix[0][2] = fwd.mV[2];
+
+ mMatrix[1][0] = left.mV[0];
+ mMatrix[1][1] = left.mV[1];
+ mMatrix[1][2] = left.mV[2];
+
+ mMatrix[2][0] = up.mV[0];
+ mMatrix[2][1] = up.mV[1];
+ mMatrix[2][2] = up.mV[2];
+
+ return *this;
+}
+
+
+// Rotate exisitng mMatrix
+const LLMatrix3& LLMatrix3::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ *this *= mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::rotate(const F32 angle, const LLVector3 &vec)
+{
+ LLMatrix3 mat(angle, vec);
+ *this *= mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::rotate(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ *this *= mat;
+ return *this;
+}
+
+
+const LLMatrix3& LLMatrix3::rotate(const LLQuaternion &q)
+{
+ LLMatrix3 mat(q);
+ *this *= mat;
+ return *this;
+}
+
+
+LLVector3 LLMatrix3::getFwdRow() const
+{
+ return LLVector3(mMatrix[VX]);
+}
+
+LLVector3 LLMatrix3::getLeftRow() const
+{
+ return LLVector3(mMatrix[VY]);
+}
+
+LLVector3 LLMatrix3::getUpRow() const
+{
+ return LLVector3(mMatrix[VZ]);
+}
+
+
+
+const LLMatrix3& LLMatrix3::orthogonalize()
+{
+ LLVector3 x_axis(mMatrix[VX]);
+ LLVector3 y_axis(mMatrix[VY]);
+ LLVector3 z_axis(mMatrix[VZ]);
+
+ x_axis.normVec();
+ y_axis -= x_axis * (x_axis * y_axis);
+ y_axis.normVec();
+ z_axis = x_axis % y_axis;
+ setRows(x_axis, y_axis, z_axis);
+ return (*this);
+}
+
+
+// LLMatrix3 Operators
+
+LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b)
+{
+ U32 i, j;
+ LLMatrix3 mat;
+ for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
+ a.mMatrix[j][1] * b.mMatrix[1][i] +
+ a.mMatrix[j][2] * b.mMatrix[2][i];
+ }
+ }
+ return mat;
+}
+
+/* Not implemented to help enforce code consistency with the syntax of
+ row-major notation. This is a Good Thing.
+LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b)
+{
+ LLVector3 vec;
+ // matrix operates "from the left" on column vector
+ vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] +
+ a.mMatrix[VX][VY] * b.mV[VY] +
+ a.mMatrix[VX][VZ] * b.mV[VZ];
+
+ vec.mV[VY] = a.mMatrix[VY][VX] * b.mV[VX] +
+ a.mMatrix[VY][VY] * b.mV[VY] +
+ a.mMatrix[VY][VZ] * b.mV[VZ];
+
+ vec.mV[VZ] = a.mMatrix[VZ][VX] * b.mV[VX] +
+ a.mMatrix[VZ][VY] * b.mV[VY] +
+ a.mMatrix[VZ][VZ] * b.mV[VZ];
+ return vec;
+}
+*/
+
+
+LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b)
+{
+ // matrix operates "from the right" on row vector
+ return LLVector3(
+ a.mV[VX] * b.mMatrix[VX][VX] +
+ a.mV[VY] * b.mMatrix[VY][VX] +
+ a.mV[VZ] * b.mMatrix[VZ][VX],
+
+ a.mV[VX] * b.mMatrix[VX][VY] +
+ a.mV[VY] * b.mMatrix[VY][VY] +
+ a.mV[VZ] * b.mMatrix[VZ][VY],
+
+ a.mV[VX] * b.mMatrix[VX][VZ] +
+ a.mV[VY] * b.mMatrix[VY][VZ] +
+ a.mV[VZ] * b.mMatrix[VZ][VZ] );
+}
+
+LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b)
+{
+ // matrix operates "from the right" on row vector
+ return LLVector3d(
+ a.mdV[VX] * b.mMatrix[VX][VX] +
+ a.mdV[VY] * b.mMatrix[VY][VX] +
+ a.mdV[VZ] * b.mMatrix[VZ][VX],
+
+ a.mdV[VX] * b.mMatrix[VX][VY] +
+ a.mdV[VY] * b.mMatrix[VY][VY] +
+ a.mdV[VZ] * b.mMatrix[VZ][VY],
+
+ a.mdV[VX] * b.mMatrix[VX][VZ] +
+ a.mdV[VY] * b.mMatrix[VY][VZ] +
+ a.mdV[VZ] * b.mMatrix[VZ][VZ] );
+}
+
+bool operator==(const LLMatrix3 &a, const LLMatrix3 &b)
+{
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ if (a.mMatrix[j][i] != b.mMatrix[j][i])
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b)
+{
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ if (a.mMatrix[j][i] != b.mMatrix[j][i])
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b)
+{
+ U32 i, j;
+ LLMatrix3 mat;
+ for (i = 0; i < NUM_VALUES_IN_MAT3; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT3; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
+ a.mMatrix[j][1] * b.mMatrix[1][i] +
+ a.mMatrix[j][2] * b.mMatrix[2][i];
+ }
+ }
+ a = mat;
+ return a;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a)
+{
+ s << "{ "
+ << a.mMatrix[VX][VX] << ", " << a.mMatrix[VX][VY] << ", " << a.mMatrix[VX][VZ] << "; "
+ << a.mMatrix[VY][VX] << ", " << a.mMatrix[VY][VY] << ", " << a.mMatrix[VY][VZ] << "; "
+ << a.mMatrix[VZ][VX] << ", " << a.mMatrix[VZ][VY] << ", " << a.mMatrix[VZ][VZ]
+ << " }";
+ return s;
+}
+
diff --git a/indra/llmath/m3math.h b/indra/llmath/m3math.h
new file mode 100644
index 0000000000..ac93ed86fb
--- /dev/null
+++ b/indra/llmath/m3math.h
@@ -0,0 +1,198 @@
+/**
+ * @file m3math.h
+ * @brief LLMatrix3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_M3MATH_H
+#define LL_M3MATH_H
+
+#include "llerror.h"
+
+class LLVector4;
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+
+// NOTA BENE: Currently assuming a right-handed, z-up universe
+
+// ji
+// LLMatrix3 = | 00 01 02 |
+// | 10 11 12 |
+// | 20 21 22 |
+
+// LLMatrix3 = | fx fy fz | forward-axis
+// | lx ly lz | left-axis
+// | ux uy uz | up-axis
+
+// NOTE: The world of computer graphics uses column-vectors and matricies that
+// "operate to the left".
+
+
+static const U32 NUM_VALUES_IN_MAT3 = 3;
+#if LL_WINDOWS
+__declspec( align(16) )
+#endif
+class LLMatrix3
+{
+ public:
+ F32 mMatrix[NUM_VALUES_IN_MAT3][NUM_VALUES_IN_MAT3];
+
+ LLMatrix3(void); // Initializes Matrix to identity matrix
+ explicit LLMatrix3(const F32 *mat); // Initializes Matrix to values in mat
+ explicit LLMatrix3(const LLQuaternion &q); // Initializes Matrix with rotation q
+
+ LLMatrix3(const F32 angle, const F32 x, const F32 y, const F32 z); // Initializes Matrix with axis angle
+ LLMatrix3(const F32 angle, const LLVector3 &vec); // Initializes Matrix with axis angle
+ LLMatrix3(const F32 angle, const LLVector3d &vec); // Initializes Matrix with axis angle
+ LLMatrix3(const F32 angle, const LLVector4 &vec); // Initializes Matrix with axis angle
+ LLMatrix3(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles
+
+ //////////////////////////////
+ //
+ // Matrix initializers - these replace any existing values in the matrix
+ //
+
+ // various useful matrix functions
+ const LLMatrix3& identity(); // Load identity matrix
+ const LLMatrix3& zero(); // Clears Matrix to zero
+
+ ///////////////////////////
+ //
+ // Matrix setters - set some properties without modifying others
+ //
+
+ // These functions take Rotation arguments
+ const LLMatrix3& setRot(const F32 angle, const F32 x, const F32 y, const F32 z); // Calculate rotation matrix for rotating angle radians about (x, y, z)
+ const LLMatrix3& setRot(const F32 angle, const LLVector3 &vec); // Calculate rotation matrix for rotating angle radians about vec
+ const LLMatrix3& setRot(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles
+ const LLMatrix3& setRot(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos
+
+ const LLMatrix3& setRows(const LLVector3 &x_axis, const LLVector3 &y_axis, const LLVector3 &z_axis);
+
+ ///////////////////////////
+ //
+ // Get properties of a matrix
+ //
+ LLQuaternion quaternion() const; // Returns quaternion from mat
+ void getEulerAngles(F32 *roll, F32 *pitch, F32 *yaw) const; // Returns Euler angles, in radians
+
+ // Axis extraction routines
+ LLVector3 getFwdRow() const;
+ LLVector3 getLeftRow() const;
+ LLVector3 getUpRow() const;
+ F32 determinant() const; // Return determinant
+
+
+ ///////////////////////////
+ //
+ // Operations on an existing matrix
+ //
+ const LLMatrix3& transpose(); // Transpose MAT4
+ const LLMatrix3& invert(); // Invert MAT4
+ const LLMatrix3& orthogonalize(); // Orthogonalizes X, then Y, then Z
+ const LLMatrix3& adjointTranspose(); // returns transpose of matrix adjoint, for multiplying normals
+
+
+ // Rotate existing matrix
+ // Note: the two lines below are equivalent:
+ // foo.rotate(bar)
+ // foo = foo * bar
+ // That is, foo.rotMat3(bar) multiplies foo by bar FROM THE RIGHT
+ const LLMatrix3& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z)
+ const LLMatrix3& rotate(const F32 angle, const LLVector3 &vec); // Rotate matrix by rotating angle radians about vec
+ const LLMatrix3& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by roll (about x), pitch (about y), and yaw (about z)
+ const LLMatrix3& rotate(const LLQuaternion &q); // Transform matrix by Euler angles and translating by pos
+
+// This operator is misleading as to operation direction
+// friend LLVector3 operator*(const LLMatrix3 &a, const LLVector3 &b); // Apply rotation a to vector b
+
+ friend LLVector3 operator*(const LLVector3 &a, const LLMatrix3 &b); // Apply rotation b to vector a
+ friend LLVector3d operator*(const LLVector3d &a, const LLMatrix3 &b); // Apply rotation b to vector a
+ friend LLMatrix3 operator*(const LLMatrix3 &a, const LLMatrix3 &b); // Return a * b
+
+ friend bool operator==(const LLMatrix3 &a, const LLMatrix3 &b); // Return a == b
+ friend bool operator!=(const LLMatrix3 &a, const LLMatrix3 &b); // Return a != b
+
+ friend const LLMatrix3& operator*=(LLMatrix3 &a, const LLMatrix3 &b); // Return a * b
+
+ friend std::ostream& operator<<(std::ostream& s, const LLMatrix3 &a); // Stream a
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+inline LLMatrix3::LLMatrix3(void)
+{
+ mMatrix[0][0] = 1.f;
+ mMatrix[0][1] = 0.f;
+ mMatrix[0][2] = 0.f;
+
+ mMatrix[1][0] = 0.f;
+ mMatrix[1][1] = 1.f;
+ mMatrix[1][2] = 0.f;
+
+ mMatrix[2][0] = 0.f;
+ mMatrix[2][1] = 0.f;
+ mMatrix[2][2] = 1.f;
+}
+
+inline LLMatrix3::LLMatrix3(const F32 *mat)
+{
+ mMatrix[0][0] = mat[0];
+ mMatrix[0][1] = mat[1];
+ mMatrix[0][2] = mat[2];
+
+ mMatrix[1][0] = mat[3];
+ mMatrix[1][1] = mat[4];
+ mMatrix[1][2] = mat[5];
+
+ mMatrix[2][0] = mat[6];
+ mMatrix[2][1] = mat[7];
+ mMatrix[2][2] = mat[8];
+}
+
+
+#endif
+
+
+// Rotation matrix hints...
+
+// Inverse of Rotation Matrices
+// ----------------------------
+// If R is a rotation matrix that rotate vectors from Frame-A to Frame-B,
+// then the transpose of R will rotate vectors from Frame-B to Frame-A.
+
+
+// Creating Rotation Matricies From Object Axes
+// --------------------------------------------
+// Suppose you know the three axes of some object in some "absolute-frame".
+// If you take those three vectors and throw them into the rows of
+// a rotation matrix what do you get?
+//
+// R = | X0 X1 X2 |
+// | Y0 Y1 Y2 |
+// | Z0 Z1 Z2 |
+//
+// Yeah, but what does it mean?
+//
+// Transpose the matrix and have it operate on a vector...
+//
+// V * R_transpose = [ V0 V1 V2 ] * | X0 Y0 Z0 |
+// | X1 Y1 Z1 |
+// | X2 Y2 Z2 |
+//
+// = [ V*X V*Y V*Z ]
+//
+// = components of V that are parallel to the three object axes
+//
+// = transformation of V into object frame
+//
+// Since the transformation of a rotation matrix is its inverse, then
+// R must rotate vectors from the object-frame into the absolute-frame.
+
+
+
diff --git a/indra/llmath/m4math.cpp b/indra/llmath/m4math.cpp
new file mode 100644
index 0000000000..969c3663e6
--- /dev/null
+++ b/indra/llmath/m4math.cpp
@@ -0,0 +1,794 @@
+/**
+ * @file m4math.cpp
+ * @brief LLMatrix4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+
+
+
+
+// LLMatrix4
+
+// Constructors
+
+
+LLMatrix4::LLMatrix4(const F32 *mat)
+{
+ mMatrix[0][0] = mat[0];
+ mMatrix[0][1] = mat[1];
+ mMatrix[0][2] = mat[2];
+ mMatrix[0][3] = mat[3];
+
+ mMatrix[1][0] = mat[4];
+ mMatrix[1][1] = mat[5];
+ mMatrix[1][2] = mat[6];
+ mMatrix[1][3] = mat[7];
+
+ mMatrix[2][0] = mat[8];
+ mMatrix[2][1] = mat[9];
+ mMatrix[2][2] = mat[10];
+ mMatrix[2][3] = mat[11];
+
+ mMatrix[3][0] = mat[12];
+ mMatrix[3][1] = mat[13];
+ mMatrix[3][2] = mat[14];
+ mMatrix[3][3] = mat[15];
+}
+
+LLMatrix4::LLMatrix4(const LLMatrix3 &mat, const LLVector4 &vec)
+{
+ mMatrix[0][0] = mat.mMatrix[0][0];
+ mMatrix[0][1] = mat.mMatrix[0][1];
+ mMatrix[0][2] = mat.mMatrix[0][2];
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = mat.mMatrix[1][0];
+ mMatrix[1][1] = mat.mMatrix[1][1];
+ mMatrix[1][2] = mat.mMatrix[1][2];
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = mat.mMatrix[2][0];
+ mMatrix[2][1] = mat.mMatrix[2][1];
+ mMatrix[2][2] = mat.mMatrix[2][2];
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = vec.mV[0];
+ mMatrix[3][1] = vec.mV[1];
+ mMatrix[3][2] = vec.mV[2];
+ mMatrix[3][3] = 1.f;
+}
+
+LLMatrix4::LLMatrix4(const LLMatrix3 &mat)
+{
+ mMatrix[0][0] = mat.mMatrix[0][0];
+ mMatrix[0][1] = mat.mMatrix[0][1];
+ mMatrix[0][2] = mat.mMatrix[0][2];
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = mat.mMatrix[1][0];
+ mMatrix[1][1] = mat.mMatrix[1][1];
+ mMatrix[1][2] = mat.mMatrix[1][2];
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = mat.mMatrix[2][0];
+ mMatrix[2][1] = mat.mMatrix[2][1];
+ mMatrix[2][2] = mat.mMatrix[2][2];
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 1.f;
+}
+
+LLMatrix4::LLMatrix4(const LLQuaternion &q)
+{
+ *this = initRotation(q);
+}
+
+LLMatrix4::LLMatrix4(const LLQuaternion &q, const LLVector4 &pos)
+{
+ *this = initRotTrans(q, pos);
+}
+
+LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec, const LLVector4 &pos)
+{
+ initRotTrans(LLQuaternion(angle, vec), pos);
+}
+
+LLMatrix4::LLMatrix4(const F32 angle, const LLVector4 &vec)
+{
+ initRotation(LLQuaternion(angle, vec));
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 1.f;
+}
+
+LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ initRotTrans(LLQuaternion(mat), pos);
+}
+
+LLMatrix4::LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ initRotation(LLQuaternion(mat));
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 1.f;
+}
+
+LLMatrix4::~LLMatrix4(void)
+{
+}
+
+// Clear and Assignment Functions
+
+const LLMatrix4& LLMatrix4::zero()
+{
+ mMatrix[0][0] = 0.f;
+ mMatrix[0][1] = 0.f;
+ mMatrix[0][2] = 0.f;
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = 0.f;
+ mMatrix[1][1] = 0.f;
+ mMatrix[1][2] = 0.f;
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = 0.f;
+ mMatrix[2][1] = 0.f;
+ mMatrix[2][2] = 0.f;
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 0.f;
+ return *this;
+}
+
+
+// various useful mMatrix functions
+
+const LLMatrix4& LLMatrix4::transpose()
+{
+ LLMatrix4 mat;
+ mat.mMatrix[0][0] = mMatrix[0][0];
+ mat.mMatrix[1][0] = mMatrix[0][1];
+ mat.mMatrix[2][0] = mMatrix[0][2];
+ mat.mMatrix[3][0] = mMatrix[0][3];
+
+ mat.mMatrix[0][1] = mMatrix[1][0];
+ mat.mMatrix[1][1] = mMatrix[1][1];
+ mat.mMatrix[2][1] = mMatrix[1][2];
+ mat.mMatrix[3][1] = mMatrix[1][3];
+
+ mat.mMatrix[0][2] = mMatrix[2][0];
+ mat.mMatrix[1][2] = mMatrix[2][1];
+ mat.mMatrix[2][2] = mMatrix[2][2];
+ mat.mMatrix[3][2] = mMatrix[2][3];
+
+ mat.mMatrix[0][3] = mMatrix[3][0];
+ mat.mMatrix[1][3] = mMatrix[3][1];
+ mat.mMatrix[2][3] = mMatrix[3][2];
+ mat.mMatrix[3][3] = mMatrix[3][3];
+
+ *this = mat;
+ return *this;
+}
+
+
+F32 LLMatrix4::determinant() const
+{
+ llerrs << "Not implemented!" << llendl;
+ return 0.f;
+}
+
+// Only works for pure orthonormal, homogeneous transform matrices.
+const LLMatrix4& LLMatrix4::invert(void)
+{
+ // transpose the rotation part
+ F32 temp;
+ temp = mMatrix[VX][VY]; mMatrix[VX][VY] = mMatrix[VY][VX]; mMatrix[VY][VX] = temp;
+ temp = mMatrix[VX][VZ]; mMatrix[VX][VZ] = mMatrix[VZ][VX]; mMatrix[VZ][VX] = temp;
+ temp = mMatrix[VY][VZ]; mMatrix[VY][VZ] = mMatrix[VZ][VY]; mMatrix[VZ][VY] = temp;
+
+ // rotate the translation part by the new rotation
+ // (temporarily store in empty column of matrix)
+ U32 j;
+ for (j=0; j<3; j++)
+ {
+ mMatrix[j][VW] = mMatrix[VW][VX] * mMatrix[VX][j] +
+ mMatrix[VW][VY] * mMatrix[VY][j] +
+ mMatrix[VW][VZ] * mMatrix[VZ][j];
+ }
+
+ // negate and copy the temporary vector back to the tranlation row
+ mMatrix[VW][VX] = -mMatrix[VX][VW];
+ mMatrix[VW][VY] = -mMatrix[VY][VW];
+ mMatrix[VW][VZ] = -mMatrix[VZ][VW];
+
+ // zero the empty column again
+ mMatrix[VX][VW] = mMatrix[VY][VW] = mMatrix[VZ][VW] = 0.0f;
+
+ return *this;
+}
+
+LLVector4 LLMatrix4::getFwdRow4() const
+{
+ return LLVector4(mMatrix[VX][VX], mMatrix[VX][VY], mMatrix[VX][VZ], mMatrix[VX][VW]);
+}
+
+LLVector4 LLMatrix4::getLeftRow4() const
+{
+ return LLVector4(mMatrix[VY][VX], mMatrix[VY][VY], mMatrix[VY][VZ], mMatrix[VY][VW]);
+}
+
+LLVector4 LLMatrix4::getUpRow4() const
+{
+ return LLVector4(mMatrix[VZ][VX], mMatrix[VZ][VY], mMatrix[VZ][VZ], mMatrix[VZ][VW]);
+}
+
+// SJB: This code is correct for a logicly stored (non-transposed) matrix;
+// Our matrices are stored transposed, OpenGL style, so this generates the
+// INVERSE quaternion (-x, -y, -z, w)!
+// Because we use similar logic in LLQuaternion::getMatrix3,
+// we are internally consistant so everything works OK :)
+LLQuaternion LLMatrix4::quaternion() const
+{
+ LLQuaternion quat;
+ F32 tr, s, q[4];
+ U32 i, j, k;
+ U32 nxt[3] = {1, 2, 0};
+
+ tr = mMatrix[0][0] + mMatrix[1][1] + mMatrix[2][2];
+
+ // check the diagonal
+ if (tr > 0.f)
+ {
+ s = (F32)sqrt (tr + 1.f);
+ quat.mQ[VS] = s / 2.f;
+ s = 0.5f / s;
+ quat.mQ[VX] = (mMatrix[1][2] - mMatrix[2][1]) * s;
+ quat.mQ[VY] = (mMatrix[2][0] - mMatrix[0][2]) * s;
+ quat.mQ[VZ] = (mMatrix[0][1] - mMatrix[1][0]) * s;
+ }
+ else
+ {
+ // diagonal is negative
+ i = 0;
+ if (mMatrix[1][1] > mMatrix[0][0])
+ i = 1;
+ if (mMatrix[2][2] > mMatrix[i][i])
+ i = 2;
+
+ j = nxt[i];
+ k = nxt[j];
+
+
+ s = (F32)sqrt ((mMatrix[i][i] - (mMatrix[j][j] + mMatrix[k][k])) + 1.f);
+
+ q[i] = s * 0.5f;
+
+ if (s != 0.f)
+ s = 0.5f / s;
+
+ q[3] = (mMatrix[j][k] - mMatrix[k][j]) * s;
+ q[j] = (mMatrix[i][j] + mMatrix[j][i]) * s;
+ q[k] = (mMatrix[i][k] + mMatrix[k][i]) * s;
+
+ quat.setQuat(q);
+ }
+ return quat;
+}
+
+
+
+void LLMatrix4::initRows(const LLVector4 &row0,
+ const LLVector4 &row1,
+ const LLVector4 &row2,
+ const LLVector4 &row3)
+{
+ mMatrix[0][0] = row0.mV[0];
+ mMatrix[0][1] = row0.mV[1];
+ mMatrix[0][2] = row0.mV[2];
+ mMatrix[0][3] = row0.mV[3];
+
+ mMatrix[1][0] = row1.mV[0];
+ mMatrix[1][1] = row1.mV[1];
+ mMatrix[1][2] = row1.mV[2];
+ mMatrix[1][3] = row1.mV[3];
+
+ mMatrix[2][0] = row2.mV[0];
+ mMatrix[2][1] = row2.mV[1];
+ mMatrix[2][2] = row2.mV[2];
+ mMatrix[2][3] = row2.mV[3];
+
+ mMatrix[3][0] = row3.mV[0];
+ mMatrix[3][1] = row3.mV[1];
+ mMatrix[3][2] = row3.mV[2];
+ mMatrix[3][3] = row3.mV[3];
+}
+
+
+const LLMatrix4& LLMatrix4::initRotation(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLMatrix3 mat(angle, x, y, z);
+ return initMatrix(mat);
+}
+
+
+const LLMatrix4& LLMatrix4::initRotation(F32 angle, const LLVector4 &vec)
+{
+ LLMatrix3 mat(angle, vec);
+ return initMatrix(mat);
+}
+
+
+const LLMatrix4& LLMatrix4::initRotation(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ return initMatrix(mat);
+}
+
+
+const LLMatrix4& LLMatrix4::initRotation(const LLQuaternion &q)
+{
+ LLMatrix3 mat(q);
+ return initMatrix(mat);
+}
+
+
+// Position and Rotation
+const LLMatrix4& LLMatrix4::initRotTrans(const F32 angle, const F32 rx, const F32 ry, const F32 rz,
+ const F32 tx, const F32 ty, const F32 tz)
+{
+ LLMatrix3 mat(angle, rx, ry, rz);
+ LLVector3 translation(tx, ty, tz);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3&translation)
+{
+ LLMatrix3 mat(angle, axis);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &translation)
+{
+ LLMatrix3 mat(roll, pitch, yaw);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+
+/*
+const LLMatrix4& LLMatrix4::initRotTrans(const LLVector4 &fwd,
+ const LLVector4 &left,
+ const LLVector4 &up,
+ const LLVector4 &translation)
+{
+ LLMatrix3 mat(fwd, left, up);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+*/
+
+const LLMatrix4& LLMatrix4::initRotTrans(const LLQuaternion &q, const LLVector4 &translation)
+{
+ LLMatrix3 mat(q);
+ initMatrix(mat);
+ setTranslation(translation);
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos)
+{
+ F32 sx, sy, sz;
+ F32 xx, xy, xz, xw, yy, yz, yw, zz, zw;
+
+ sx = scale.mV[0];
+ sy = scale.mV[1];
+ sz = scale.mV[2];
+
+ xx = q.mQ[VX] * q.mQ[VX];
+ xy = q.mQ[VX] * q.mQ[VY];
+ xz = q.mQ[VX] * q.mQ[VZ];
+ xw = q.mQ[VX] * q.mQ[VW];
+
+ yy = q.mQ[VY] * q.mQ[VY];
+ yz = q.mQ[VY] * q.mQ[VZ];
+ yw = q.mQ[VY] * q.mQ[VW];
+
+ zz = q.mQ[VZ] * q.mQ[VZ];
+ zw = q.mQ[VZ] * q.mQ[VW];
+
+ mMatrix[0][0] = (1.f - 2.f * ( yy + zz )) *sx;
+ mMatrix[0][1] = ( 2.f * ( xy + zw )) *sx;
+ mMatrix[0][2] = ( 2.f * ( xz - yw )) *sx;
+
+ mMatrix[1][0] = ( 2.f * ( xy - zw )) *sy;
+ mMatrix[1][1] = (1.f - 2.f * ( xx + zz )) *sy;
+ mMatrix[1][2] = ( 2.f * ( yz + xw )) *sy;
+
+ mMatrix[2][0] = ( 2.f * ( xz + yw )) *sz;
+ mMatrix[2][1] = ( 2.f * ( yz - xw )) *sz;
+ mMatrix[2][2] = (1.f - 2.f * ( xx + yy )) *sz;
+
+ mMatrix[3][0] = pos.mV[0];
+ mMatrix[3][1] = pos.mV[1];
+ mMatrix[3][2] = pos.mV[2];
+ mMatrix[3][3] = 1.0;
+
+ // TODO -- should we set the translation portion to zero?
+ return (*this);
+}
+
+// Rotate exisitng mMatrix
+const LLMatrix4& LLMatrix4::rotate(const F32 angle, const F32 x, const F32 y, const F32 z)
+{
+ LLVector4 vec4(x, y, z);
+ LLMatrix4 mat(angle, vec4);
+ *this *= mat;
+ return *this;
+}
+
+const LLMatrix4& LLMatrix4::rotate(const F32 angle, const LLVector4 &vec)
+{
+ LLMatrix4 mat(angle, vec);
+ *this *= mat;
+ return *this;
+}
+
+const LLMatrix4& LLMatrix4::rotate(const F32 roll, const F32 pitch, const F32 yaw)
+{
+ LLMatrix4 mat(roll, pitch, yaw);
+ *this *= mat;
+ return *this;
+}
+
+const LLMatrix4& LLMatrix4::rotate(const LLQuaternion &q)
+{
+ LLMatrix4 mat(q);
+ *this *= mat;
+ return *this;
+}
+
+
+const LLMatrix4& LLMatrix4::translate(const LLVector3 &vec)
+{
+ mMatrix[3][0] += vec.mV[0];
+ mMatrix[3][1] += vec.mV[1];
+ mMatrix[3][2] += vec.mV[2];
+ return (*this);
+}
+
+
+void LLMatrix4::setFwdRow(const LLVector3 &row)
+{
+ mMatrix[VX][VX] = row.mV[VX];
+ mMatrix[VX][VY] = row.mV[VY];
+ mMatrix[VX][VZ] = row.mV[VZ];
+}
+
+void LLMatrix4::setLeftRow(const LLVector3 &row)
+{
+ mMatrix[VY][VX] = row.mV[VX];
+ mMatrix[VY][VY] = row.mV[VY];
+ mMatrix[VY][VZ] = row.mV[VZ];
+}
+
+void LLMatrix4::setUpRow(const LLVector3 &row)
+{
+ mMatrix[VZ][VX] = row.mV[VX];
+ mMatrix[VZ][VY] = row.mV[VY];
+ mMatrix[VZ][VZ] = row.mV[VZ];
+}
+
+
+void LLMatrix4::setFwdCol(const LLVector3 &col)
+{
+ mMatrix[VX][VX] = col.mV[VX];
+ mMatrix[VY][VX] = col.mV[VY];
+ mMatrix[VZ][VX] = col.mV[VZ];
+}
+
+void LLMatrix4::setLeftCol(const LLVector3 &col)
+{
+ mMatrix[VX][VY] = col.mV[VX];
+ mMatrix[VY][VY] = col.mV[VY];
+ mMatrix[VZ][VY] = col.mV[VZ];
+}
+
+void LLMatrix4::setUpCol(const LLVector3 &col)
+{
+ mMatrix[VX][VZ] = col.mV[VX];
+ mMatrix[VY][VZ] = col.mV[VY];
+ mMatrix[VZ][VZ] = col.mV[VZ];
+}
+
+
+const LLMatrix4& LLMatrix4::setTranslation(const F32 tx, const F32 ty, const F32 tz)
+{
+ mMatrix[VW][VX] = tx;
+ mMatrix[VW][VY] = ty;
+ mMatrix[VW][VZ] = tz;
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::setTranslation(const LLVector3 &translation)
+{
+ mMatrix[VW][VX] = translation.mV[VX];
+ mMatrix[VW][VY] = translation.mV[VY];
+ mMatrix[VW][VZ] = translation.mV[VZ];
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::setTranslation(const LLVector4 &translation)
+{
+ mMatrix[VW][VX] = translation.mV[VX];
+ mMatrix[VW][VY] = translation.mV[VY];
+ mMatrix[VW][VZ] = translation.mV[VZ];
+ return (*this);
+}
+
+// LLMatrix3 Extraction and Setting
+LLMatrix3 LLMatrix4::getMat3() const
+{
+ LLMatrix3 retmat;
+
+ retmat.mMatrix[0][0] = mMatrix[0][0];
+ retmat.mMatrix[0][1] = mMatrix[0][1];
+ retmat.mMatrix[0][2] = mMatrix[0][2];
+
+ retmat.mMatrix[1][0] = mMatrix[1][0];
+ retmat.mMatrix[1][1] = mMatrix[1][1];
+ retmat.mMatrix[1][2] = mMatrix[1][2];
+
+ retmat.mMatrix[2][0] = mMatrix[2][0];
+ retmat.mMatrix[2][1] = mMatrix[2][1];
+ retmat.mMatrix[2][2] = mMatrix[2][2];
+
+ return retmat;
+}
+
+const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat)
+{
+ mMatrix[0][0] = mat.mMatrix[0][0];
+ mMatrix[0][1] = mat.mMatrix[0][1];
+ mMatrix[0][2] = mat.mMatrix[0][2];
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = mat.mMatrix[1][0];
+ mMatrix[1][1] = mat.mMatrix[1][1];
+ mMatrix[1][2] = mat.mMatrix[1][2];
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = mat.mMatrix[2][0];
+ mMatrix[2][1] = mat.mMatrix[2][1];
+ mMatrix[2][2] = mat.mMatrix[2][2];
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 1.f;
+ return (*this);
+}
+
+const LLMatrix4& LLMatrix4::initMatrix(const LLMatrix3 &mat, const LLVector4 &translation)
+{
+ mMatrix[0][0] = mat.mMatrix[0][0];
+ mMatrix[0][1] = mat.mMatrix[0][1];
+ mMatrix[0][2] = mat.mMatrix[0][2];
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = mat.mMatrix[1][0];
+ mMatrix[1][1] = mat.mMatrix[1][1];
+ mMatrix[1][2] = mat.mMatrix[1][2];
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = mat.mMatrix[2][0];
+ mMatrix[2][1] = mat.mMatrix[2][1];
+ mMatrix[2][2] = mat.mMatrix[2][2];
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = translation.mV[0];
+ mMatrix[3][1] = translation.mV[1];
+ mMatrix[3][2] = translation.mV[2];
+ mMatrix[3][3] = 1.f;
+ return (*this);
+}
+
+// LLMatrix4 Operators
+
+
+/* Not implemented to help enforce code consistency with the syntax of
+ row-major notation. This is a Good Thing.
+LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b)
+{
+ // Operate "to the right" on column-vector b
+ LLVector4 vec;
+ vec.mV[VX] = a.mMatrix[VX][VX] * b.mV[VX] +
+ a.mMatrix[VY][VX] * b.mV[VY] +
+ a.mMatrix[VZ][VX] * b.mV[VZ] +
+ a.mMatrix[VW][VX] * b.mV[VW];
+
+ vec.mV[VY] = a.mMatrix[VX][VY] * b.mV[VX] +
+ a.mMatrix[VY][VY] * b.mV[VY] +
+ a.mMatrix[VZ][VY] * b.mV[VZ] +
+ a.mMatrix[VW][VY] * b.mV[VW];
+
+ vec.mV[VZ] = a.mMatrix[VX][VZ] * b.mV[VX] +
+ a.mMatrix[VY][VZ] * b.mV[VY] +
+ a.mMatrix[VZ][VZ] * b.mV[VZ] +
+ a.mMatrix[VW][VZ] * b.mV[VW];
+
+ vec.mV[VW] = a.mMatrix[VX][VW] * b.mV[VX] +
+ a.mMatrix[VY][VW] * b.mV[VY] +
+ a.mMatrix[VZ][VW] * b.mV[VZ] +
+ a.mMatrix[VW][VW] * b.mV[VW];
+ return vec;
+}
+*/
+
+
+LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b)
+{
+ // Operate "to the left" on row-vector a
+ LLVector4 vec;
+ vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
+ a.mV[VY] * b.mMatrix[VY][VX] +
+ a.mV[VZ] * b.mMatrix[VZ][VX] +
+ a.mV[VW] * b.mMatrix[VW][VX];
+
+ vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
+ a.mV[VY] * b.mMatrix[VY][VY] +
+ a.mV[VZ] * b.mMatrix[VZ][VY] +
+ a.mV[VW] * b.mMatrix[VW][VY];
+
+ vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
+ a.mV[VY] * b.mMatrix[VY][VZ] +
+ a.mV[VZ] * b.mMatrix[VZ][VZ] +
+ a.mV[VW] * b.mMatrix[VW][VZ];
+
+ vec.mV[VW] = a.mV[VX] * b.mMatrix[VX][VW] +
+ a.mV[VY] * b.mMatrix[VY][VW] +
+ a.mV[VZ] * b.mMatrix[VZ][VW] +
+ a.mV[VW] * b.mMatrix[VW][VW];
+ return vec;
+}
+
+LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b)
+{
+ // Rotates but does not translate
+ // Operate "to the left" on row-vector a
+ LLVector4 vec;
+ vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
+ a.mV[VY] * b.mMatrix[VY][VX] +
+ a.mV[VZ] * b.mMatrix[VZ][VX];
+
+ vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
+ a.mV[VY] * b.mMatrix[VY][VY] +
+ a.mV[VZ] * b.mMatrix[VZ][VY];
+
+ vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
+ a.mV[VY] * b.mMatrix[VY][VZ] +
+ a.mV[VZ] * b.mMatrix[VZ][VZ];
+
+// vec.mV[VW] = a.mV[VX] * b.mMatrix[VX][VW] +
+// a.mV[VY] * b.mMatrix[VY][VW] +
+// a.mV[VZ] * b.mMatrix[VZ][VW] +
+ vec.mV[VW] = a.mV[VW];
+ return vec;
+}
+
+LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b)
+{
+ // Rotates but does not translate
+ // Operate "to the left" on row-vector a
+ LLVector3 vec;
+ vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
+ a.mV[VY] * b.mMatrix[VY][VX] +
+ a.mV[VZ] * b.mMatrix[VZ][VX];
+
+ vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
+ a.mV[VY] * b.mMatrix[VY][VY] +
+ a.mV[VZ] * b.mMatrix[VZ][VY];
+
+ vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
+ a.mV[VY] * b.mMatrix[VY][VZ] +
+ a.mV[VZ] * b.mMatrix[VZ][VZ];
+ return vec;
+}
+
+bool operator==(const LLMatrix4 &a, const LLMatrix4 &b)
+{
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ if (a.mMatrix[j][i] != b.mMatrix[j][i])
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b)
+{
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ if (a.mMatrix[j][i] != b.mMatrix[j][i])
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+const LLMatrix4& operator*=(LLMatrix4 &a, F32 k)
+{
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ a.mMatrix[j][i] *= k;
+ }
+ }
+ return a;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a)
+{
+ s << "{ "
+ << a.mMatrix[VX][VX] << ", "
+ << a.mMatrix[VX][VY] << ", "
+ << a.mMatrix[VX][VZ] << ", "
+ << a.mMatrix[VX][VW]
+ << "; "
+ << a.mMatrix[VY][VX] << ", "
+ << a.mMatrix[VY][VY] << ", "
+ << a.mMatrix[VY][VZ] << ", "
+ << a.mMatrix[VY][VW]
+ << "; "
+ << a.mMatrix[VZ][VX] << ", "
+ << a.mMatrix[VZ][VY] << ", "
+ << a.mMatrix[VZ][VZ] << ", "
+ << a.mMatrix[VZ][VW]
+ << "; "
+ << a.mMatrix[VW][VX] << ", "
+ << a.mMatrix[VW][VY] << ", "
+ << a.mMatrix[VW][VZ] << ", "
+ << a.mMatrix[VW][VW]
+ << " }";
+ return s;
+}
+
+
diff --git a/indra/llmath/m4math.h b/indra/llmath/m4math.h
new file mode 100644
index 0000000000..4f26d875ff
--- /dev/null
+++ b/indra/llmath/m4math.h
@@ -0,0 +1,365 @@
+/**
+ * @file m4math.h
+ * @brief LLMatrix3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_M4MATH_H
+#define LL_M4MATH_H
+
+#include "v3math.h"
+
+class LLVector4;
+class LLMatrix3;
+class LLQuaternion;
+
+// NOTA BENE: Currently assuming a right-handed, x-forward, y-left, z-up universe
+
+// Us versus OpenGL:
+
+// Even though OpenGL uses column vectors and we use row vectors, we can plug our matrices
+// directly into OpenGL. This is because OpenGL numbers its matrices going columnwise:
+//
+// OpenGL indexing: Our indexing:
+// 0 4 8 12 [0][0] [0][1] [0][2] [0][3]
+// 1 5 9 13 [1][0] [1][1] [1][2] [1][3]
+// 2 6 10 14 [2][0] [2][1] [2][2] [2][3]
+// 3 7 11 15 [3][0] [3][1] [3][2] [3][3]
+//
+// So when you're looking at OpenGL related matrices online, our matrices will be
+// "transposed". But our matrices can be plugged directly into OpenGL and work fine!
+//
+
+// We're using row vectors - [vx, vy, vz, vw]
+//
+// There are several different ways of thinking of matrices, if you mix them up, you'll get very confused.
+//
+// One way to think about it is a matrix that takes the origin frame A
+// and rotates it into B': i.e. A*M = B
+//
+// Vectors:
+// f - forward axis of B expressed in A
+// l - left axis of B expressed in A
+// u - up axis of B expressed in A
+//
+// | 0: fx 1: fy 2: fz 3:0 |
+// M = | 4: lx 5: ly 6: lz 7:0 |
+// | 8: ux 9: uy 10: uz 11:0 |
+// | 12: 0 13: 0 14: 0 15:1 |
+//
+//
+//
+//
+// Another way to think of matrices is matrix that takes a point p in frame A, and puts it into frame B:
+// This is used most commonly for the modelview matrix.
+//
+// so p*M = p'
+//
+// Vectors:
+// f - forward of frame B in frame A
+// l - left of frame B in frame A
+// u - up of frame B in frame A
+// o - origin of frame frame B in frame A
+//
+// | 0: fx 1: lx 2: ux 3:0 |
+// M = | 4: fy 5: ly 6: uy 7:0 |
+// | 8: fz 9: lz 10: uz 11:0 |
+// | 12:-of 13:-ol 14:-ou 15:1 |
+//
+// of, ol, and ou mean the component of the "global" origin o in the f axis, l axis, and u axis.
+//
+
+static const U32 NUM_VALUES_IN_MAT4 = 4;
+
+#if LL_WINDOWS
+__declspec(align(16))
+#endif
+class LLMatrix4
+{
+public:
+ F32 mMatrix[NUM_VALUES_IN_MAT4][NUM_VALUES_IN_MAT4];
+
+ LLMatrix4(); // Initializes Matrix to identity matrix
+ explicit LLMatrix4(const F32 *mat); // Initializes Matrix to values in mat
+ explicit LLMatrix4(const LLMatrix3 &mat); // Initializes Matrix to valuee in mat and sets position to (0,0,0)
+ explicit LLMatrix4(const LLQuaternion &q); // Initializes Matrix with rotation q and sets position to (0,0,0)
+
+ LLMatrix4(const LLMatrix3 &mat, const LLVector4 &pos); // Initializes Matrix to values in mat and pos
+
+ // These are really, really, inefficient as implemented! - djs
+ LLMatrix4(const LLQuaternion &q, const LLVector4 &pos); // Initializes Matrix with rotation q and position pos
+ LLMatrix4(F32 angle,
+ const LLVector4 &vec,
+ const LLVector4 &pos); // Initializes Matrix with axis-angle and position
+ LLMatrix4(F32 angle, const LLVector4 &vec); // Initializes Matrix with axis-angle and sets position to (0,0,0)
+ LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw,
+ const LLVector4 &pos); // Initializes Matrix with Euler angles
+ LLMatrix4(const F32 roll, const F32 pitch, const F32 yaw); // Initializes Matrix with Euler angles
+
+ ~LLMatrix4(void); // Destructor
+
+
+ //////////////////////////////
+ //
+ // Matrix initializers - these replace any existing values in the matrix
+ //
+
+ void initRows(const LLVector4 &row0,
+ const LLVector4 &row1,
+ const LLVector4 &row2,
+ const LLVector4 &row3);
+
+ // various useful matrix functions
+ const LLMatrix4& identity(); // Load identity matrix
+ const LLMatrix4& zero(); // Clears matrix to all zeros.
+
+ const LLMatrix4& initRotation(const F32 angle, const F32 x, const F32 y, const F32 z); // Calculate rotation matrix by rotating angle radians about (x, y, z)
+ const LLMatrix4& initRotation(const F32 angle, const LLVector4 &axis); // Calculate rotation matrix for rotating angle radians about vec
+ const LLMatrix4& initRotation(const F32 roll, const F32 pitch, const F32 yaw); // Calculate rotation matrix from Euler angles
+ const LLMatrix4& initRotation(const LLQuaternion &q); // Set with Quaternion and position
+
+ // Position Only
+ const LLMatrix4& initMatrix(const LLMatrix3 &mat); //
+ const LLMatrix4& initMatrix(const LLMatrix3 &mat, const LLVector4 &translation);
+
+ // These operation create a matrix that will rotate and translate by the
+ // specified amounts.
+ const LLMatrix4& initRotTrans(const F32 angle,
+ const F32 rx, const F32 ry, const F32 rz,
+ const F32 px, const F32 py, const F32 pz);
+
+ const LLMatrix4& initRotTrans(const F32 angle, const LLVector3 &axis, const LLVector3 &translation); // Rotation from axis angle + translation
+ const LLMatrix4& initRotTrans(const F32 roll, const F32 pitch, const F32 yaw, const LLVector4 &pos); // Rotation from Euler + translation
+ const LLMatrix4& initRotTrans(const LLQuaternion &q, const LLVector4 &pos); // Set with Quaternion and position
+
+
+ // Set all
+ const LLMatrix4& initAll(const LLVector3 &scale, const LLQuaternion &q, const LLVector3 &pos);
+
+
+ ///////////////////////////
+ //
+ // Matrix setters - set some properties without modifying others
+ //
+
+ const LLMatrix4& setTranslation(const F32 x, const F32 y, const F32 z); // Sets matrix to translate by (x,y,z)
+
+ void setFwdRow(const LLVector3 &row);
+ void setLeftRow(const LLVector3 &row);
+ void setUpRow(const LLVector3 &row);
+
+ void setFwdCol(const LLVector3 &col);
+ void setLeftCol(const LLVector3 &col);
+ void setUpCol(const LLVector3 &col);
+
+ const LLMatrix4& setTranslation(const LLVector4 &translation);
+ const LLMatrix4& setTranslation(const LLVector3 &translation);
+
+ ///////////////////////////
+ //
+ // Get properties of a matrix
+ //
+
+ F32 determinant(void) const; // Return determinant
+ LLQuaternion quaternion(void) const; // Returns quaternion
+
+ LLVector4 getFwdRow4() const;
+ LLVector4 getLeftRow4() const;
+ LLVector4 getUpRow4() const;
+
+ LLMatrix3 getMat3() const;
+
+ const LLVector3& getTranslation() const { return *(LLVector3*)&mMatrix[3][0]; }
+
+ ///////////////////////////
+ //
+ // Operations on an existing matrix
+ //
+
+ const LLMatrix4& transpose(); // Transpose LLMatrix4
+ const LLMatrix4& invert(); // Invert LLMatrix4
+
+ // Rotate existing matrix
+ // These are really, really, inefficient as implemented! - djs
+ const LLMatrix4& rotate(const F32 angle, const F32 x, const F32 y, const F32 z); // Rotate matrix by rotating angle radians about (x, y, z)
+ const LLMatrix4& rotate(const F32 angle, const LLVector4 &vec); // Rotate matrix by rotating angle radians about vec
+ const LLMatrix4& rotate(const F32 roll, const F32 pitch, const F32 yaw); // Rotate matrix by Euler angles
+ const LLMatrix4& rotate(const LLQuaternion &q); // Rotate matrix by Quaternion
+
+
+ // Translate existing matrix
+ const LLMatrix4& translate(const LLVector3 &vec); // Translate matrix by (vec[VX], vec[VY], vec[VZ])
+
+
+
+
+ ///////////////////////
+ //
+ // Operators
+ //
+
+// Not implemented to enforce code that agrees with symbolic syntax
+// friend LLVector4 operator*(const LLMatrix4 &a, const LLVector4 &b); // Apply rotation a to vector b
+
+// friend inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b); // Return a * b
+ friend LLVector4 operator*(const LLVector4 &a, const LLMatrix4 &b); // Return transform of vector a by matrix b
+ friend LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b); // Return full transform of a by matrix b
+ friend LLVector4 rotate_vector(const LLVector4 &a, const LLMatrix4 &b); // Rotates a but does not translate
+ friend LLVector3 rotate_vector(const LLVector3 &a, const LLMatrix4 &b); // Rotates a but does not translate
+
+ friend bool operator==(const LLMatrix4 &a, const LLMatrix4 &b); // Return a == b
+ friend bool operator!=(const LLMatrix4 &a, const LLMatrix4 &b); // Return a != b
+
+ friend const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b); // Return a + b
+ friend const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b); // Return a - b
+ friend const LLMatrix4& operator*=(LLMatrix4 &a, const LLMatrix4 &b); // Return a * b
+ friend const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b); // Return a * b
+
+ friend std::ostream& operator<<(std::ostream& s, const LLMatrix4 &a); // Stream a
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+
+inline LLMatrix4::LLMatrix4()
+{
+ identity();
+}
+
+inline const LLMatrix4& LLMatrix4::identity()
+{
+ mMatrix[0][0] = 1.f;
+ mMatrix[0][1] = 0.f;
+ mMatrix[0][2] = 0.f;
+ mMatrix[0][3] = 0.f;
+
+ mMatrix[1][0] = 0.f;
+ mMatrix[1][1] = 1.f;
+ mMatrix[1][2] = 0.f;
+ mMatrix[1][3] = 0.f;
+
+ mMatrix[2][0] = 0.f;
+ mMatrix[2][1] = 0.f;
+ mMatrix[2][2] = 1.f;
+ mMatrix[2][3] = 0.f;
+
+ mMatrix[3][0] = 0.f;
+ mMatrix[3][1] = 0.f;
+ mMatrix[3][2] = 0.f;
+ mMatrix[3][3] = 1.f;
+ return (*this);
+}
+
+inline LLVector3 operator*(const LLVector3 &a, const LLMatrix4 &b)
+{
+ // Converts a to LLVector4 and applies full transformation
+ // Operates "to the left" on row-vector a
+ LLVector3 vec;
+ vec.mV[VX] = a.mV[VX] * b.mMatrix[VX][VX] +
+ a.mV[VY] * b.mMatrix[VY][VX] +
+ a.mV[VZ] * b.mMatrix[VZ][VX] +
+ b.mMatrix[VW][VX];
+
+ vec.mV[VY] = a.mV[VX] * b.mMatrix[VX][VY] +
+ a.mV[VY] * b.mMatrix[VY][VY] +
+ a.mV[VZ] * b.mMatrix[VZ][VY] +
+ b.mMatrix[VW][VY];
+
+ vec.mV[VZ] = a.mV[VX] * b.mMatrix[VX][VZ] +
+ a.mV[VY] * b.mMatrix[VY][VZ] +
+ a.mV[VZ] * b.mMatrix[VZ][VZ] +
+ b.mMatrix[VW][VZ];
+ return vec;
+}
+
+/*
+inline LLMatrix4 operator*(const LLMatrix4 &a, const LLMatrix4 &b)
+{
+ U32 i, j;
+ LLMatrix4 mat;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
+ a.mMatrix[j][1] * b.mMatrix[1][i] +
+ a.mMatrix[j][2] * b.mMatrix[2][i] +
+ a.mMatrix[j][3] * b.mMatrix[3][i];
+ }
+ }
+ return mat;
+}
+*/
+
+
+inline const LLMatrix4& operator*=(LLMatrix4 &a, const LLMatrix4 &b)
+{
+ U32 i, j;
+ LLMatrix4 mat;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][0] * b.mMatrix[0][i] +
+ a.mMatrix[j][1] * b.mMatrix[1][i] +
+ a.mMatrix[j][2] * b.mMatrix[2][i] +
+ a.mMatrix[j][3] * b.mMatrix[3][i];
+ }
+ }
+ a = mat;
+ return a;
+}
+
+inline const LLMatrix4& operator*=(LLMatrix4 &a, const F32 &b)
+{
+ U32 i, j;
+ LLMatrix4 mat;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][i] * b;
+ }
+ }
+ a = mat;
+ return a;
+}
+
+inline const LLMatrix4& operator+=(LLMatrix4 &a, const LLMatrix4 &b)
+{
+ LLMatrix4 mat;
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][i] + b.mMatrix[j][i];
+ }
+ }
+ a = mat;
+ return a;
+}
+
+inline const LLMatrix4& operator-=(LLMatrix4 &a, const LLMatrix4 &b)
+{
+ LLMatrix4 mat;
+ U32 i, j;
+ for (i = 0; i < NUM_VALUES_IN_MAT4; i++)
+ {
+ for (j = 0; j < NUM_VALUES_IN_MAT4; j++)
+ {
+ mat.mMatrix[j][i] = a.mMatrix[j][i] - b.mMatrix[j][i];
+ }
+ }
+ a = mat;
+ return a;
+}
+
+#endif
+
+
+
diff --git a/indra/llmath/raytrace.cpp b/indra/llmath/raytrace.cpp
new file mode 100644
index 0000000000..dfee5d09fd
--- /dev/null
+++ b/indra/llmath/raytrace.cpp
@@ -0,0 +1,1256 @@
+/**
+ * @file raytrace.cpp
+ * @brief Functions called by box object scripts.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "math.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "m3math.h"
+#include "raytrace.h"
+
+
+BOOL line_plane(const LLVector3 &line_point, const LLVector3 &line_direction,
+ const LLVector3 &plane_point, const LLVector3 plane_normal,
+ LLVector3 &intersection)
+{
+ F32 N = line_direction * plane_normal;
+ if (0.0f == N)
+ {
+ // line is perpendicular to plane normal
+ // so it is either entirely on plane, or not on plane at all
+ return FALSE;
+ }
+ // Ax + By, + Cz + D = 0
+ // D = - (plane_point * plane_normal)
+ // N = line_direction * plane_normal
+ // intersection = line_point - ((D + plane_normal * line_point) / N) * line_direction
+ intersection = line_point - ((plane_normal * line_point - plane_point * plane_normal) / N) * line_direction;
+ return TRUE;
+}
+
+
+BOOL ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &plane_point, const LLVector3 plane_normal,
+ LLVector3 &intersection)
+{
+ F32 N = ray_direction * plane_normal;
+ if (0.0f == N)
+ {
+ // ray is perpendicular to plane normal
+ // so it is either entirely on plane, or not on plane at all
+ return FALSE;
+ }
+ // Ax + By, + Cz + D = 0
+ // D = - (plane_point * plane_normal)
+ // N = ray_direction * plane_normal
+ // intersection = ray_point - ((D + plane_normal * ray_point) / N) * ray_direction
+ F32 alpha = -(plane_normal * ray_point - plane_point * plane_normal) / N;
+ if (alpha < 0.0f)
+ {
+ // ray points away from plane
+ return FALSE;
+ }
+ intersection = ray_point + alpha * ray_direction;
+ return TRUE;
+}
+
+
+BOOL ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
+ LLVector3 &intersection)
+{
+ if (ray_plane(ray_point, ray_direction, circle_center, plane_normal, intersection))
+ {
+ if (circle_radius >= (intersection - circle_center).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 side_01 = point_1 - point_0;
+ LLVector3 side_12 = point_2 - point_1;
+
+ intersection_normal = side_01 % side_12;
+ intersection_normal.normVec();
+
+ if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection))
+ {
+ LLVector3 side_20 = point_0 - point_2;
+ if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f &&
+ intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f &&
+ intersection_normal * (side_20 % (intersection - point_2)) >= 0.0f)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+// assumes a parallelogram
+BOOL ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 side_01 = point_1 - point_0;
+ LLVector3 side_12 = point_2 - point_1;
+
+ intersection_normal = side_01 % side_12;
+ intersection_normal.normVec();
+
+ if (ray_plane(ray_point, ray_direction, point_0, intersection_normal, intersection))
+ {
+ LLVector3 point_3 = point_0 + (side_12);
+ LLVector3 side_23 = point_3 - point_2;
+ LLVector3 side_30 = point_0 - point_3;
+ if (intersection_normal * (side_01 % (intersection - point_0)) >= 0.0f &&
+ intersection_normal * (side_12 % (intersection - point_1)) >= 0.0f &&
+ intersection_normal * (side_23 % (intersection - point_2)) >= 0.0f &&
+ intersection_normal * (side_30 % (intersection - point_3)) >= 0.0f)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &sphere_center, F32 sphere_radius,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_to_sphere = sphere_center - ray_point;
+ F32 dot = ray_to_sphere * ray_direction;
+
+ LLVector3 closest_approach = dot * ray_direction - ray_to_sphere;
+
+ F32 shortest_distance = closest_approach.magVecSquared();
+ F32 radius_squared = sphere_radius * sphere_radius;
+ if (shortest_distance > radius_squared)
+ {
+ return FALSE;
+ }
+
+ F32 half_chord = (F32) sqrt(radius_squared - shortest_distance);
+ closest_approach = sphere_center + closest_approach; // closest_approach now in absolute coordinates
+ intersection = closest_approach + half_chord * ray_direction;
+ dot = ray_direction * (intersection - ray_point);
+ if (dot < 0.0f)
+ {
+ // ray shoots away from sphere and is not inside it
+ return FALSE;
+ }
+
+ shortest_distance = ray_direction * ((closest_approach - half_chord * ray_direction) - ray_point);
+ if (shortest_distance > 0.0f)
+ {
+ // ray enters sphere
+ intersection = intersection - (2.0f * half_chord) * ray_direction;
+ }
+ else
+ {
+ // do nothing
+ // ray starts inside sphere and intersects as it leaves the sphere
+ }
+
+ intersection_normal = intersection - sphere_center;
+ if (sphere_radius > 0.0f)
+ {
+ intersection_normal *= 1.0f / sphere_radius;
+ }
+ else
+ {
+ intersection_normal.setVec(0.0f, 0.0f, 0.0f);
+ }
+
+ return TRUE;
+}
+
+
+BOOL ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ // calculate the centers of the cylinder caps in the absolute frame
+ LLVector3 cyl_top(0.0f, 0.0f, 0.5f * cyl_scale.mV[VZ]);
+ LLVector3 cyl_bottom(0.0f, 0.0f, -cyl_top.mV[VZ]);
+ cyl_top = (cyl_top * cyl_rotation) + cyl_center;
+ cyl_bottom = (cyl_bottom * cyl_rotation) + cyl_center;
+
+ // we only handle cylinders with circular cross-sections at the moment
+ F32 cyl_radius = 0.5f * llmax(cyl_scale.mV[VX], cyl_scale.mV[VY]); // HACK until scaled cylinders are supported
+
+ // This implementation is based on the intcyl() function from Graphics_Gems_IV, page 361
+ LLVector3 cyl_axis; // axis direction (bottom toward top)
+ LLVector3 ray_to_cyl; // ray_point to cyl_top
+ F32 shortest_distance; // shortest distance from ray to axis
+ F32 cyl_length;
+ LLVector3 shortest_direction;
+ LLVector3 temp_vector;
+
+ cyl_axis = cyl_bottom - cyl_top;
+ cyl_length = cyl_axis.normVec();
+ ray_to_cyl = ray_point - cyl_bottom;
+ shortest_direction = ray_direction % cyl_axis;
+ shortest_distance = shortest_direction.normVec(); // recycle shortest_distance
+
+ // check for ray parallel to cylinder axis
+ if (0.0f == shortest_distance)
+ {
+ // ray is parallel to cylinder axis
+ temp_vector = ray_to_cyl - (ray_to_cyl * cyl_axis) * cyl_axis;
+ shortest_distance = temp_vector.magVec();
+ if (shortest_distance <= cyl_radius)
+ {
+ shortest_distance = ray_to_cyl * cyl_axis;
+ F32 dot = ray_direction * cyl_axis;
+
+ if (shortest_distance > 0.0)
+ {
+ if (dot > 0.0f)
+ {
+ // ray points away from cylinder bottom
+ return FALSE;
+ }
+ // ray hit bottom of cylinder from outside
+ intersection = ray_point - shortest_distance * cyl_axis;
+ intersection_normal = cyl_axis;
+
+ }
+ else if (shortest_distance > -cyl_length)
+ {
+ // ray starts inside cylinder
+ if (dot < 0.0f)
+ {
+ // ray hit top from inside
+ intersection = ray_point - (cyl_length + shortest_distance) * cyl_axis;
+ intersection_normal = -cyl_axis;
+ }
+ else
+ {
+ // ray hit bottom from inside
+ intersection = ray_point - shortest_distance * cyl_axis;
+ intersection_normal = cyl_axis;
+ }
+ }
+ else
+ {
+ if (dot < 0.0f)
+ {
+ // ray points away from cylinder bottom
+ return FALSE;
+ }
+ // ray hit top from outside
+ intersection = ray_point - (shortest_distance + cyl_length) * cyl_axis;
+ intersection_normal = -cyl_axis;
+ }
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ // check for intersection with infinite cylinder
+ shortest_distance = (F32) fabs(ray_to_cyl * shortest_direction);
+ if (shortest_distance <= cyl_radius)
+ {
+ F32 dist_to_closest_point; // dist from ray_point to closest_point
+ F32 half_chord_length; // half length of intersection chord
+ F32 in, out; // distances to entering/exiting points
+ temp_vector = ray_to_cyl % cyl_axis;
+ dist_to_closest_point = - (temp_vector * shortest_direction);
+ temp_vector = shortest_direction % cyl_axis;
+ temp_vector.normVec();
+ half_chord_length = (F32) fabs( sqrt(cyl_radius*cyl_radius - shortest_distance * shortest_distance) /
+ (ray_direction * temp_vector) );
+
+ out = dist_to_closest_point + half_chord_length; // dist to exiting point
+ if (out < 0.0f)
+ {
+ // cylinder is behind the ray, so we return FALSE
+ return FALSE;
+ }
+
+ in = dist_to_closest_point - half_chord_length; // dist to entering point
+ if (in < 0.0f)
+ {
+ // ray_point is inside the cylinder
+ // so we store the exiting intersection
+ intersection = ray_point + out * ray_direction;
+ shortest_distance = out;
+ }
+ else
+ {
+ // ray hit cylinder from outside
+ // so we store the entering intersection
+ intersection = ray_point + in * ray_direction;
+ shortest_distance = in;
+ }
+
+ // calculate the normal at intersection
+ if (0.0f == cyl_radius)
+ {
+ intersection_normal.setVec(0.0f, 0.0f, 0.0f);
+ }
+ else
+ {
+ temp_vector = intersection - cyl_bottom;
+ intersection_normal = temp_vector - (temp_vector * cyl_axis) * cyl_axis;
+ intersection_normal.normVec();
+ }
+
+ // check for intersection with end caps
+ // calculate intersection of ray and top plane
+ if (line_plane(ray_point, ray_direction, cyl_top, -cyl_axis, temp_vector)) // NOTE side-effect: changing temp_vector
+ {
+ shortest_distance = (temp_vector - ray_point).magVec();
+ if ( (ray_direction * cyl_axis) > 0.0f)
+ {
+ // ray potentially enters the cylinder at top
+ if (shortest_distance > out)
+ {
+ // ray missed the finite cylinder
+ return FALSE;
+ }
+ if (shortest_distance > in)
+ {
+ // ray intersects cylinder at top plane
+ intersection = temp_vector;
+ intersection_normal = -cyl_axis;
+ return TRUE;
+ }
+ }
+ else
+ {
+ // ray potentially exits the cylinder at top
+ if (shortest_distance < in)
+ {
+ // missed the finite cylinder
+ return FALSE;
+ }
+ }
+
+ // calculate intersection of ray and bottom plane
+ line_plane(ray_point, ray_direction, cyl_bottom, cyl_axis, temp_vector); // NOTE side-effect: changing temp_vector
+ shortest_distance = (temp_vector - ray_point).magVec();
+ if ( (ray_direction * cyl_axis) < 0.0)
+ {
+ // ray potentially enters the cylinder at bottom
+ if (shortest_distance > out)
+ {
+ // ray missed the finite cylinder
+ return FALSE;
+ }
+ if (shortest_distance > in)
+ {
+ // ray intersects cylinder at bottom plane
+ intersection = temp_vector;
+ intersection_normal = cyl_axis;
+ return TRUE;
+ }
+ }
+ else
+ {
+ // ray potentially exits the cylinder at bottom
+ if (shortest_distance < in)
+ {
+ // ray missed the finite cylinder
+ return FALSE;
+ }
+ }
+
+ }
+ else
+ {
+ // ray is parallel to end cap planes
+ temp_vector = cyl_bottom - ray_point;
+ shortest_distance = temp_vector * cyl_axis;
+ if (shortest_distance < 0.0f || shortest_distance > cyl_length)
+ {
+ // ray missed finite cylinder
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+
+ // Need to rotate into box frame
+ LLQuaternion into_box_frame(box_rotation); // rotates things from box frame to absolute
+ into_box_frame.conjQuat(); // now rotates things into box frame
+ LLVector3 line_point = (ray_point - box_center) * into_box_frame;
+ LLVector3 line_direction = ray_direction * into_box_frame;
+
+ // Suppose we have a plane: Ax + By + Cz + D = 0
+ // then, assuming [A, B, C] is a unit vector:
+ //
+ // plane_normal = [A, B, C]
+ // D = - (plane_normal * plane_point)
+ //
+ // Suppose we have a line: X = line_point + alpha * line_direction
+ //
+ // the intersection of the plane and line determines alpha
+ //
+ // alpha = - (D + plane_normal * line_point) / (plane_normal * line_direction)
+
+ LLVector3 line_plane_intersection;
+
+ F32 pointX = line_point.mV[VX];
+ F32 pointY = line_point.mV[VY];
+ F32 pointZ = line_point.mV[VZ];
+
+ F32 dirX = line_direction.mV[VX];
+ F32 dirY = line_direction.mV[VY];
+ F32 dirZ = line_direction.mV[VZ];
+
+ // we'll be using the half-scales of the box
+ F32 boxX = 0.5f * box_scale.mV[VX];
+ F32 boxY = 0.5f * box_scale.mV[VY];
+ F32 boxZ = 0.5f * box_scale.mV[VZ];
+
+ // check to see if line_point is OUTSIDE the box
+ if (pointX < -boxX ||
+ pointX > boxX ||
+ pointY < -boxY ||
+ pointY > boxY ||
+ pointZ < -boxZ ||
+ pointZ > boxZ)
+ {
+ // -------------- point is OUTSIDE the box ----------------
+
+ // front
+ if (pointX > 0.0f && dirX < 0.0f)
+ {
+ // plane_normal = [ 1, 0, 0]
+ // plane_normal*line_point = pointX
+ // plane_normal*line_direction = dirX
+ // D = -boxX
+ // alpha = - (-boxX + pointX) / dirX
+ line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction;
+ if (line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation;
+ return FRONT_SIDE;
+ }
+ }
+
+ // back
+ if (pointX < 0.0f && dirX > 0.0f)
+ {
+ // plane_normal = [ -1, 0, 0]
+ // plane_normal*line_point = -pX
+ // plane_normal*line_direction = -direction.mV[VX]
+ // D = -bX
+ // alpha = - (-bX - pX) / (-dirX)
+ line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction;
+ if (line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation;
+ return BACK_SIDE;
+ }
+ }
+
+ // left
+ if (pointY > 0.0f && dirY < 0.0f)
+ {
+ // plane_normal = [0, 1, 0]
+ // plane_normal*line_point = pointY
+ // plane_normal*line_direction = dirY
+ // D = -boxY
+ // alpha = - (-boxY + pointY) / dirY
+ line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction;
+
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation;
+ return LEFT_SIDE;
+ }
+ }
+
+ // right
+ if (pointY < 0.0f && dirY > 0.0f)
+ {
+ // plane_normal = [0, -1, 0]
+ // plane_normal*line_point = -pointY
+ // plane_normal*line_direction = -dirY
+ // D = -boxY
+ // alpha = - (-boxY - pointY) / (-dirY)
+ line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation;
+ return RIGHT_SIDE;
+ }
+ }
+
+ // top
+ if (pointZ > 0.0f && dirZ < 0.0f)
+ {
+ // plane_normal = [0, 0, 1]
+ // plane_normal*line_point = pointZ
+ // plane_normal*line_direction = dirZ
+ // D = -boxZ
+ // alpha = - (-boxZ + pointZ) / dirZ
+ line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation;
+ return TOP_SIDE;
+ }
+ }
+
+ // bottom
+ if (pointZ < 0.0f && dirZ > 0.0f)
+ {
+ // plane_normal = [0, 0, -1]
+ // plane_normal*line_point = -pointZ
+ // plane_normal*line_direction = -dirZ
+ // D = -boxZ
+ // alpha = - (-boxZ - pointZ) / (-dirZ)
+ line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation;
+ return BOTTOM_SIDE;
+ }
+ }
+ return NO_SIDE;
+ }
+
+ // -------------- point is INSIDE the box ----------------
+
+ // front
+ if (dirX > 0.0f)
+ {
+ // plane_normal = [ 1, 0, 0]
+ // plane_normal*line_point = pointX
+ // plane_normal*line_direction = dirX
+ // D = -boxX
+ // alpha = - (-boxX + pointX) / dirX
+ line_plane_intersection = line_point - ((pointX - boxX) / dirX) * line_direction;
+ if (line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(1.0f, 0.0f, 0.0f) * box_rotation;
+ return FRONT_SIDE;
+ }
+ }
+
+ // back
+ if (dirX < 0.0f)
+ {
+ // plane_normal = [ -1, 0, 0]
+ // plane_normal*line_point = -pX
+ // plane_normal*line_direction = -direction.mV[VX]
+ // D = -bX
+ // alpha = - (-bX - pX) / (-dirX)
+ line_plane_intersection = line_point - ((boxX + pointX)/ dirX) * line_direction;
+ if (line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(-1.0f, 0.0f, 0.0f) * box_rotation;
+ return BACK_SIDE;
+ }
+ }
+
+ // left
+ if (dirY > 0.0f)
+ {
+ // plane_normal = [0, 1, 0]
+ // plane_normal*line_point = pointY
+ // plane_normal*line_direction = dirY
+ // D = -boxY
+ // alpha = - (-boxY + pointY) / dirY
+ line_plane_intersection = line_point + ((boxY - pointY)/dirY) * line_direction;
+
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 1.0f, 0.0f) * box_rotation;
+ return LEFT_SIDE;
+ }
+ }
+
+ // right
+ if (dirY < 0.0f)
+ {
+ // plane_normal = [0, -1, 0]
+ // plane_normal*line_point = -pointY
+ // plane_normal*line_direction = -dirY
+ // D = -boxY
+ // alpha = - (-boxY - pointY) / (-dirY)
+ line_plane_intersection = line_point - ((boxY + pointY)/dirY) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VZ] < boxZ &&
+ line_plane_intersection.mV[VZ] > -boxZ )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, -1.0f, 0.0f) * box_rotation;
+ return RIGHT_SIDE;
+ }
+ }
+
+ // top
+ if (dirZ > 0.0f)
+ {
+ // plane_normal = [0, 0, 1]
+ // plane_normal*line_point = pointZ
+ // plane_normal*line_direction = dirZ
+ // D = -boxZ
+ // alpha = - (-boxZ + pointZ) / dirZ
+ line_plane_intersection = line_point - ((pointZ - boxZ)/dirZ) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 0.0f, 1.0f) * box_rotation;
+ return TOP_SIDE;
+ }
+ }
+
+ // bottom
+ if (dirZ < 0.0f)
+ {
+ // plane_normal = [0, 0, -1]
+ // plane_normal*line_point = -pointZ
+ // plane_normal*line_direction = -dirZ
+ // D = -boxZ
+ // alpha = - (-boxZ - pointZ) / (-dirZ)
+ line_plane_intersection = line_point - ((boxZ + pointZ)/dirZ) * line_direction;
+ if (line_plane_intersection.mV[VX] < boxX &&
+ line_plane_intersection.mV[VX] > -boxX &&
+ line_plane_intersection.mV[VY] < boxY &&
+ line_plane_intersection.mV[VY] > -boxY )
+ {
+ intersection = (line_plane_intersection * box_rotation) + box_center;
+ intersection_normal = LLVector3(0.0f, 0.0f, -1.0f) * box_rotation;
+ return BOTTOM_SIDE;
+ }
+ }
+
+ // should never get here unless line instersects at tangent point on edge or corner
+ // however such cases will be EXTREMELY rare
+ return NO_SIDE;
+}
+
+
+BOOL ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ // (0) Z
+ // /| \ .
+ // (1)| \ /|\ _.Y
+ // | \ \ | /|
+ // | |\ \ | /
+ // | | \(0)\ | /
+ // | | \ \ |/
+ // | | \ \ (*)----> X
+ // |(3)---\---(2)
+ // |/ \ /
+ // (4)-------(5)
+
+ // need to calculate the points of the prism so we can run ray tests with each face
+ F32 x = prism_scale.mV[VX];
+ F32 y = prism_scale.mV[VY];
+ F32 z = prism_scale.mV[VZ];
+
+ F32 tx = x * 2.0f / 3.0f;
+ F32 ty = y * 0.5f;
+ F32 tz = z * 2.0f / 3.0f;
+
+ LLVector3 point0(tx-x, ty, tz);
+ LLVector3 point1(tx-x, -ty, tz);
+ LLVector3 point2(tx, ty, tz-z);
+ LLVector3 point3(tx-x, ty, tz-z);
+ LLVector3 point4(tx-x, -ty, tz-z);
+ LLVector3 point5(tx, -ty, tz-z);
+
+ // transform these points into absolute frame
+ point0 = (point0 * prism_rotation) + prism_center;
+ point1 = (point1 * prism_rotation) + prism_center;
+ point2 = (point2 * prism_rotation) + prism_center;
+ point3 = (point3 * prism_rotation) + prism_center;
+ point4 = (point4 * prism_rotation) + prism_center;
+ point5 = (point5 * prism_rotation) + prism_center;
+
+ // test ray intersection for each face
+ BOOL b_hit = FALSE;
+ LLVector3 face_intersection, face_normal;
+ F32 distance_squared = 0.0f;
+ F32 temp;
+
+ // face 0
+ if (ray_direction * ( (point0 - point2) % (point5 - point2)) < 0.0f &&
+ ray_quadrangle(ray_point, ray_direction, point5, point2, point0, intersection, intersection_normal))
+ {
+ distance_squared = (ray_point - intersection).magVecSquared();
+ b_hit = TRUE;
+ }
+
+ // face 1
+ if (ray_direction * ( (point0 - point3) % (point2 - point3)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 2
+ if (ray_direction * ( (point1 - point4) % (point3 - point4)) < 0.0f &&
+ ray_quadrangle(ray_point, ray_direction, point3, point4, point1, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 3
+ if (ray_direction * ( (point5 - point4) % (point1 - point4)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point1, point4, point5, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 4
+ if (ray_direction * ( (point4 - point5) % (point2 - point5)) < 0.0f &&
+ ray_quadrangle(ray_point, ray_direction, point2, point5, point4, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ return b_hit;
+}
+
+
+BOOL ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ F32 a = 0.5f * F_SQRT3; // height of unit triangle
+ F32 b = 1.0f / F_SQRT3; // distance of center of unit triangle to each point
+ F32 c = F_SQRT2 / F_SQRT3; // height of unit tetrahedron
+ F32 d = 0.5f * F_SQRT3 / F_SQRT2; // distance of center of tetrahedron to each point
+
+ // if we want the tetrahedron to have unit height (c = 1.0) then we need to divide
+ // each constant by hieght of a unit tetrahedron
+ F32 oo_c = 1.0f / c;
+ a = a * oo_c;
+ b = b * oo_c;
+ c = 1.0f;
+ d = d * oo_c;
+ F32 e = 0.5f * oo_c;
+
+ LLVector3 point0( 0.0f, 0.0f, t_scale.mV[VZ] * d);
+ LLVector3 point1(t_scale.mV[VX] * b, 0.0f, t_scale.mV[VZ] * (d-c));
+ LLVector3 point2(t_scale.mV[VX] * (b-a), e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c));
+ LLVector3 point3(t_scale.mV[VX] * (b-a), -e * t_scale.mV[VY], t_scale.mV[VZ] * (d-c));
+
+ // transform these points into absolute frame
+ point0 = (point0 * t_rotation) + t_center;
+ point1 = (point1 * t_rotation) + t_center;
+ point2 = (point2 * t_rotation) + t_center;
+ point3 = (point3 * t_rotation) + t_center;
+
+ // test ray intersection for each face
+ BOOL b_hit = FALSE;
+ LLVector3 face_intersection, face_normal;
+ F32 distance_squared = 1.0e12f;
+ F32 temp;
+
+ // face 0
+ if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point1, point2, point0, intersection, intersection_normal))
+ {
+ distance_squared = (ray_point - intersection).magVecSquared();
+ b_hit = TRUE;
+ }
+
+ // face 1
+ if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 2
+ if (ray_direction * ( (point1 - point3) % (point0 - point3)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point3, point1, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 3
+ if (ray_direction * ( (point2 - point3) % (point1 - point3)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point3, point2, point1, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ return b_hit;
+}
+
+
+BOOL ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ // center of mass of pyramid is located 1/4 its height from the base
+ F32 x = 0.5f * p_scale.mV[VX];
+ F32 y = 0.5f * p_scale.mV[VY];
+ F32 z = 0.25f * p_scale.mV[VZ];
+
+ LLVector3 point0(0.0f, 0.0f, p_scale.mV[VZ] - z);
+ LLVector3 point1( x, y, -z);
+ LLVector3 point2(-x, y, -z);
+ LLVector3 point3(-x, -y, -z);
+ LLVector3 point4( x, -y, -z);
+
+ // transform these points into absolute frame
+ point0 = (point0 * p_rotation) + p_center;
+ point1 = (point1 * p_rotation) + p_center;
+ point2 = (point2 * p_rotation) + p_center;
+ point3 = (point3 * p_rotation) + p_center;
+ point4 = (point4 * p_rotation) + p_center;
+
+ // test ray intersection for each face
+ BOOL b_hit = FALSE;
+ LLVector3 face_intersection, face_normal;
+ F32 distance_squared = 1.0e12f;
+ F32 temp;
+
+ // face 0
+ if (ray_direction * ( (point1 - point4) % (point0 - point4)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point4, point1, point0, intersection, intersection_normal))
+ {
+ distance_squared = (ray_point - intersection).magVecSquared();
+ b_hit = TRUE;
+ }
+
+ // face 1
+ if (ray_direction * ( (point2 - point1) % (point0 - point1)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point1, point2, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 2
+ if (ray_direction * ( (point3 - point2) % (point0 - point2)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point2, point3, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 3
+ if (ray_direction * ( (point4 - point3) % (point0 - point3)) < 0.0f &&
+ ray_triangle(ray_point, ray_direction, point3, point4, point0, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ distance_squared = temp;
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ distance_squared = (ray_point - face_intersection).magVecSquared();
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ // face 4
+ if (ray_direction * ( (point3 - point4) % (point2 - point4)) < 0.0f &&
+ ray_quadrangle(ray_point, ray_direction, point4, point3, point2, face_intersection, face_normal))
+ {
+ if (TRUE == b_hit)
+ {
+ temp = (ray_point - face_intersection).magVecSquared();
+ if (temp < distance_squared)
+ {
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ }
+ }
+ else
+ {
+ intersection = face_intersection;
+ intersection_normal = face_normal;
+ b_hit = TRUE;
+ }
+ }
+
+ return b_hit;
+}
+
+
+BOOL linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
+ LLVector3 &intersection)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_circle(point_a, ray_direction, circle_center, plane_normal, circle_radius, intersection))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_triangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_quadrangle(point_a, ray_direction, point_0, point_1, point_2, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &sphere_center, F32 sphere_radius,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_sphere(point_a, ray_direction, sphere_center, sphere_radius, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_cylinder(point_a, ray_direction, cyl_center, cyl_scale, cyl_rotation, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 direction = point_b - point_a;
+ if (direction.isNull())
+ {
+ return NO_SIDE;
+ }
+
+ F32 segment_length = direction.normVec();
+ U32 box_side = ray_box(point_a, direction, box_center, box_scale, box_rotation, intersection, intersection_normal);
+ if (NO_SIDE == box_side || segment_length < (intersection - point_a).magVec())
+ {
+ return NO_SIDE;
+ }
+
+ return box_side;
+}
+
+
+BOOL linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_prism(point_a, ray_direction, prism_center, prism_scale, prism_rotation, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_tetrahedron(point_a, ray_direction, t_center, t_scale, t_rotation, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal)
+{
+ LLVector3 ray_direction = point_b - point_a;
+ F32 segment_length = ray_direction.normVec();
+
+ if (ray_pyramid(point_a, ray_direction, p_center, p_scale, p_rotation, intersection, intersection_normal))
+ {
+ if (segment_length >= (point_a - intersection).magVec())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+
+
+
diff --git a/indra/llmath/raytrace.h b/indra/llmath/raytrace.h
new file mode 100644
index 0000000000..d4f93647b8
--- /dev/null
+++ b/indra/llmath/raytrace.h
@@ -0,0 +1,214 @@
+/**
+ * @file raytrace.h
+ * @brief Ray intersection tests for primitives.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_RAYTRACE_H
+#define LL_RAYTRACE_H
+
+class LLVector3;
+class LLQuaternion;
+
+// All functions produce results in the same reference frame as the arguments.
+//
+// Any arguments of the form "foo_direction" or "foo_normal" are assumed to
+// be normalized, or normalized vectors are stored in them.
+//
+// Vector arguments of the form "shape_scale" represent the scale of the
+// object along the three axes.
+//
+// All functions return the expected TRUE or FALSE, unless otherwise noted.
+// When FALSE is returned, any resulting values that might have been stored
+// are undefined.
+//
+// Rays are defined by a "ray_point" and a "ray_direction" (unit).
+//
+// Lines are defined by a "line_point" and a "line_direction" (unit).
+//
+// Line segements are defined by "point_a" and "point_b", and for intersection
+// purposes are assumed to point from "point_a" to "point_b".
+//
+// A ray is different from a line in that it starts at a point and extends
+// in only one direction.
+//
+// Intersection normals always point outside the object, normal to the object's
+// surface at the point of intersection.
+//
+// Object rotations passed as quaternions are expected to rotate from the
+// object's local frame to the absolute frame. So, if "foo" is a vector in
+// the object's local frame, then "foo * object_rotation" is in the absolute
+// frame.
+
+
+// returns TRUE iff line is not parallel to plane.
+BOOL line_plane(const LLVector3 &line_point, const LLVector3 &line_direction,
+ const LLVector3 &plane_point, const LLVector3 plane_normal,
+ LLVector3 &intersection);
+
+
+// returns TRUE iff line is not parallel to plane.
+BOOL ray_plane(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &plane_point, const LLVector3 plane_normal,
+ LLVector3 &intersection);
+
+
+BOOL ray_circle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
+ LLVector3 &intersection);
+
+// point_0 through point_2 define the plane_normal via the right-hand rule:
+// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal
+BOOL ray_triangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right
+// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right
+// ==> thumb points in direction of normal
+// assumes a parallelogram, so point_3 is determined by the other points
+BOOL ray_quadrangle(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_sphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &sphere_center, F32 sphere_radius,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom",
+// and by the cylinder radius "cyl_radius"
+BOOL ray_cylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// this function doesn't just return a BOOL because the return is currently
+// used to decide how to break up boxes that have been hit by shots...
+// a hack that will probably be changed later
+//
+// returns a number representing the side of the box that was hit by the ray,
+// or NO_SIDE if intersection test failed.
+U32 ray_box(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+/* TODO
+BOOL ray_ellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_cone(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &cone_tip, const LLVector3 &cone_bottom,
+ const LLVector3 &cone_scale, const LLQuaternion &cone_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+*/
+
+
+BOOL ray_prism(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_tetrahedron(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_pyramid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+
+/* TODO
+BOOL ray_hemiellipsoid(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &e_center, const LLVector3 &e_scale, const LLQuaternion &e_rotation,
+ const LLVector3 &e_cut_normal,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_hemisphere(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &sphere_center, F32 sphere_radius, const LLVector3 &sphere_cut_normal,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_hemicylinder(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &cyl_top, const LLVector3 &cyl_bottom, F32 cyl_radius,
+ const LLVector3 &cyl_cut_normal,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL ray_hemicone(const LLVector3 &ray_point, const LLVector3 &ray_direction,
+ const LLVector3 &cone_tip, const LLVector3 &cone_bottom,
+ const LLVector3 &cone_scale, const LLVector3 &cyl_cut_normal,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+*/
+
+
+BOOL linesegment_circle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &circle_center, const LLVector3 plane_normal, F32 circle_radius,
+ LLVector3 &intersection);
+
+// point_0 through point_2 define the plane_normal via the right-hand rule:
+// circle from point_0 to point_2 with fingers ==> thumb points in direction of normal
+BOOL linesegment_triangle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// point_0 is the lower-left corner, point_1 is the lower-right, point_2 is the upper-right
+// right-hand-rule... curl fingers from lower-left toward lower-right then toward upper-right
+// ==> thumb points in direction of normal
+// assumes a parallelogram, so point_3 is determined by the other points
+BOOL linesegment_quadrangle(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &point_0, const LLVector3 &point_1, const LLVector3 &point_2,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL linesegment_sphere(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &sphere_center, F32 sphere_radius,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// finite right cylinder is defined by end centers: "cyl_top", "cyl_bottom",
+// and by the cylinder radius "cyl_radius"
+BOOL linesegment_cylinder(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &cyl_center, const LLVector3 &cyl_scale, const LLQuaternion &cyl_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+// this function doesn't just return a BOOL because the return is currently
+// used to decide how to break up boxes that have been hit by shots...
+// a hack that will probably be changed later
+//
+// returns a number representing the side of the box that was hit by the ray,
+// or NO_SIDE if intersection test failed.
+U32 linesegment_box(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &box_center, const LLVector3 &box_scale, const LLQuaternion &box_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL linesegment_prism(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &prism_center, const LLVector3 &prism_scale, const LLQuaternion &prism_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL linesegment_tetrahedron(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &t_center, const LLVector3 &t_scale, const LLQuaternion &t_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+BOOL linesegment_pyramid(const LLVector3 &point_a, const LLVector3 &point_b,
+ const LLVector3 &p_center, const LLVector3 &p_scale, const LLQuaternion &p_rotation,
+ LLVector3 &intersection, LLVector3 &intersection_normal);
+
+
+#endif
+
diff --git a/indra/llmath/v2math.cpp b/indra/llmath/v2math.cpp
new file mode 100644
index 0000000000..d4cff60f62
--- /dev/null
+++ b/indra/llmath/v2math.cpp
@@ -0,0 +1,92 @@
+/**
+ * @file v2math.cpp
+ * @brief LLVector2 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v2math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+
+// LLVector2
+
+LLVector2 LLVector2::zero(0,0);
+
+
+// Non-member functions
+
+// Sets all values to absolute value of their original values
+// Returns TRUE if data changed
+BOOL LLVector2::abs()
+{
+ BOOL ret = FALSE;
+
+ if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = TRUE; }
+ if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = TRUE; }
+
+ return ret;
+}
+
+
+F32 angle_between(const LLVector2& a, const LLVector2& b)
+{
+ LLVector2 an = a;
+ LLVector2 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 cosine = an * bn;
+ F32 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ acos(cosine);
+ return angle;
+}
+
+BOOL are_parallel(const LLVector2 &a, const LLVector2 &b, float epsilon)
+{
+ LLVector2 an = a;
+ LLVector2 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+F32 dist_vec(const LLVector2 &a, const LLVector2 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ return fsqrtf( x*x + y*y );
+}
+
+F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ return x*x + y*y;
+}
+
+F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ return x*x + y*y;
+}
+
+LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u)
+{
+ return LLVector2(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u );
+}
diff --git a/indra/llmath/v2math.h b/indra/llmath/v2math.h
new file mode 100644
index 0000000000..c94333e2a9
--- /dev/null
+++ b/indra/llmath/v2math.h
@@ -0,0 +1,307 @@
+/**
+ * @file v2math.h
+ * @brief LLVector2 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V2MATH_H
+#define LL_V2MATH_H
+
+#include <math.h>
+
+#include "llmath.h"
+
+class LLVector4;
+class LLMatrix3;
+class LLQuaternion;
+
+// Llvector2 = |x y z w|
+
+static const U32 LENGTHOFVECTOR2 = 2;
+
+class LLVector2
+{
+ public:
+ F32 mV[LENGTHOFVECTOR2];
+
+ static LLVector2 zero;
+
+ LLVector2(); // Initializes LLVector2 to (0, 0)
+ LLVector2(F32 x, F32 y); // Initializes LLVector2 to (x. y)
+ LLVector2(const F32 *vec); // Initializes LLVector2 to (vec[0]. vec[1])
+
+ // Clears LLVector2 to (0, 0). DEPRECATED - prefer zeroVec.
+ void clearVec();
+
+ // Zero LLVector2 to (0, 0)
+ void zeroVec();
+
+ void setVec(F32 x, F32 y); // Sets LLVector2 to (x, y)
+ void setVec(const LLVector2 &vec); // Sets LLVector2 to vec
+ void setVec(const F32 *vec); // Sets LLVector2 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector2
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector2
+ F32 normVec(); // Normalizes and returns the magnitude of LLVector2
+
+ BOOL abs(); // sets all values to absolute value of original value (first octant), returns TRUE if changed
+
+ const LLVector2& scaleVec(const LLVector2& vec); // scales per component by vec
+
+ BOOL isNull(); // Returns TRUE if vector has a _very_small_ length
+ BOOL isExactlyZero() const { return !mV[VX] && !mV[VY]; }
+
+ F32 operator[](int idx) const { return mV[idx]; }
+ F32 &operator[](int idx) { return mV[idx]; }
+
+ friend bool operator<(const LLVector2 &a, const LLVector2 &b); // For sorting. x is "more significant" than y
+ friend LLVector2 operator+(const LLVector2 &a, const LLVector2 &b); // Return vector a + b
+ friend LLVector2 operator-(const LLVector2 &a, const LLVector2 &b); // Return vector a minus b
+ friend F32 operator*(const LLVector2 &a, const LLVector2 &b); // Return a dot b
+ friend LLVector2 operator%(const LLVector2 &a, const LLVector2 &b); // Return a cross b
+ friend LLVector2 operator/(const LLVector2 &a, F32 k); // Return a divided by scaler k
+ friend LLVector2 operator*(const LLVector2 &a, F32 k); // Return a times scaler k
+ friend LLVector2 operator*(F32 k, const LLVector2 &a); // Return a times scaler k
+ friend bool operator==(const LLVector2 &a, const LLVector2 &b); // Return a == b
+ friend bool operator!=(const LLVector2 &a, const LLVector2 &b); // Return a != b
+
+ friend const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b); // Return vector a + b
+ friend const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b); // Return vector a minus b
+ friend const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b); // Return a cross b
+ friend const LLVector2& operator*=(LLVector2 &a, F32 k); // Return a times scaler k
+ friend const LLVector2& operator/=(LLVector2 &a, F32 k); // Return a divided by scaler k
+
+ friend LLVector2 operator-(const LLVector2 &a); // Return vector -a
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector2 &a); // Stream a
+};
+
+
+// Non-member functions
+
+F32 angle_between(const LLVector2 &a, const LLVector2 &b); // Returns angle (radians) between a and b
+BOOL are_parallel(const LLVector2 &a, const LLVector2 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns TRUE if a and b are very close to parallel
+F32 dist_vec(const LLVector2 &a, const LLVector2 &b); // Returns distance between a and b
+F32 dist_vec_squared(const LLVector2 &a, const LLVector2 &b);// Returns distance sqaured between a and b
+F32 dist_vec_squared2D(const LLVector2 &a, const LLVector2 &b);// Returns distance sqaured between a and b ignoring Z component
+LLVector2 lerp(const LLVector2 &a, const LLVector2 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+
+// Constructors
+
+inline LLVector2::LLVector2(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+}
+
+inline LLVector2::LLVector2(F32 x, F32 y)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+}
+
+inline LLVector2::LLVector2(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+}
+
+
+// Clear and Assignment Functions
+
+inline void LLVector2::clearVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+}
+
+inline void LLVector2::zeroVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+}
+
+inline void LLVector2::setVec(F32 x, F32 y)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+}
+
+inline void LLVector2::setVec(const LLVector2 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+}
+
+inline void LLVector2::setVec(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+}
+
+// LLVector2 Magnitude and Normalization Functions
+
+inline F32 LLVector2::magVec(void) const
+{
+ return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+}
+
+inline F32 LLVector2::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1];
+}
+
+inline F32 LLVector2::normVec(void)
+{
+ F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1]);
+ F32 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mV[0] *= oomag;
+ mV[1] *= oomag;
+ }
+ else
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+inline const LLVector2& LLVector2::scaleVec(const LLVector2& vec)
+{
+ mV[VX] *= vec.mV[VX];
+ mV[VY] *= vec.mV[VY];
+
+ return *this;
+}
+
+inline BOOL LLVector2::isNull()
+{
+ if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] )
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// LLVector2 Operators
+
+// For sorting. By convention, x is "more significant" than y.
+inline bool operator<(const LLVector2 &a, const LLVector2 &b)
+{
+ if( a.mV[VX] == b.mV[VX] )
+ {
+ return a.mV[VY] < b.mV[VY];
+ }
+ else
+ {
+ return a.mV[VX] < b.mV[VX];
+ }
+}
+
+
+inline LLVector2 operator+(const LLVector2 &a, const LLVector2 &b)
+{
+ LLVector2 c(a);
+ return c += b;
+}
+
+inline LLVector2 operator-(const LLVector2 &a, const LLVector2 &b)
+{
+ LLVector2 c(a);
+ return c -= b;
+}
+
+inline F32 operator*(const LLVector2 &a, const LLVector2 &b)
+{
+ return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1]);
+}
+
+inline LLVector2 operator%(const LLVector2 &a, const LLVector2 &b)
+{
+ return LLVector2(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]);
+}
+
+inline LLVector2 operator/(const LLVector2 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ return LLVector2( a.mV[0] * t, a.mV[1] * t );
+}
+
+inline LLVector2 operator*(const LLVector2 &a, F32 k)
+{
+ return LLVector2( a.mV[0] * k, a.mV[1] * k );
+}
+
+inline LLVector2 operator*(F32 k, const LLVector2 &a)
+{
+ return LLVector2( a.mV[0] * k, a.mV[1] * k );
+}
+
+inline bool operator==(const LLVector2 &a, const LLVector2 &b)
+{
+ return ( (a.mV[0] == b.mV[0])
+ &&(a.mV[1] == b.mV[1]));
+}
+
+inline bool operator!=(const LLVector2 &a, const LLVector2 &b)
+{
+ return ( (a.mV[0] != b.mV[0])
+ ||(a.mV[1] != b.mV[1]));
+}
+
+inline const LLVector2& operator+=(LLVector2 &a, const LLVector2 &b)
+{
+ a.mV[0] += b.mV[0];
+ a.mV[1] += b.mV[1];
+ return a;
+}
+
+inline const LLVector2& operator-=(LLVector2 &a, const LLVector2 &b)
+{
+ a.mV[0] -= b.mV[0];
+ a.mV[1] -= b.mV[1];
+ return a;
+}
+
+inline const LLVector2& operator%=(LLVector2 &a, const LLVector2 &b)
+{
+ LLVector2 ret(a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1], a.mV[1]*b.mV[0] - b.mV[1]*a.mV[0]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector2& operator*=(LLVector2 &a, F32 k)
+{
+ a.mV[0] *= k;
+ a.mV[1] *= k;
+ return a;
+}
+
+inline const LLVector2& operator/=(LLVector2 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ a.mV[0] *= t;
+ a.mV[1] *= t;
+ return a;
+}
+
+inline LLVector2 operator-(const LLVector2 &a)
+{
+ return LLVector2( -a.mV[0], -a.mV[1] );
+}
+
+inline std::ostream& operator<<(std::ostream& s, const LLVector2 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << " }";
+ return s;
+}
+
+#endif
diff --git a/indra/llmath/v3color.cpp b/indra/llmath/v3color.cpp
new file mode 100644
index 0000000000..2dbc368d98
--- /dev/null
+++ b/indra/llmath/v3color.cpp
@@ -0,0 +1,44 @@
+/**
+ * @file v3color.cpp
+ * @brief LLColor3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "v3color.h"
+#include "v4color.h"
+
+LLColor3 LLColor3::white(1.0f, 1.0f, 1.0f);
+LLColor3 LLColor3::black(0.0f, 0.0f, 0.0f);
+LLColor3 LLColor3::grey (0.5f, 0.5f, 0.5f);
+
+LLColor3::LLColor3(const LLColor4 &a)
+{
+ mV[0] = a.mV[0];
+ mV[1] = a.mV[1];
+ mV[2] = a.mV[2];
+}
+
+LLColor3::LLColor3(const LLSD &sd)
+{
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+}
+
+const LLColor3& LLColor3::operator=(const LLColor4 &a)
+{
+ mV[0] = a.mV[0];
+ mV[1] = a.mV[1];
+ mV[2] = a.mV[2];
+ return (*this);
+}
+
+std::ostream& operator<<(std::ostream& s, const LLColor3 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
+ return s;
+}
diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h
new file mode 100644
index 0000000000..3777c00054
--- /dev/null
+++ b/indra/llmath/v3color.h
@@ -0,0 +1,362 @@
+/**
+ * @file v3color.h
+ * @brief LLColor3 class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V3COLOR_H
+#define LL_V3COLOR_H
+
+class LLColor4;
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llsd.h"
+
+// LLColor3 = |r g b|
+
+static const U32 LENGTHOFCOLOR3 = 3;
+
+class LLColor3
+{
+public:
+ F32 mV[LENGTHOFCOLOR3];
+
+ static LLColor3 white;
+ static LLColor3 black;
+ static LLColor3 grey;
+
+public:
+ LLColor3(); // Initializes LLColor3 to (0, 0, 0)
+ LLColor3(F32 r, F32 g, F32 b); // Initializes LLColor3 to (r, g, b)
+ LLColor3(const F32 *vec); // Initializes LLColor3 to (vec[0]. vec[1], vec[2])
+ LLColor3(char *color_string); // html format color ie "#FFDDEE"
+ explicit LLColor3(const LLColor4& color4); // "explicit" to avoid automatic conversion
+ LLColor3(const LLSD& sd);
+
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mV[0];
+ ret[1] = mV[1];
+ ret[2] = mV[2];
+ return ret;
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mV[0] = (F32) sd[0].asReal();;
+ mV[1] = (F32) sd[1].asReal();;
+ mV[2] = (F32) sd[2].asReal();;
+ }
+
+ const LLColor3& setToBlack(); // Clears LLColor3 to (0, 0, 0)
+ const LLColor3& setToWhite(); // Zero LLColor3 to (0, 0, 0)
+ const LLColor3& setVec(F32 x, F32 y, F32 z); // Sets LLColor3 to (x, y, z)
+ const LLColor3& setVec(const LLColor3 &vec); // Sets LLColor3 to vec
+ const LLColor3& setVec(const F32 *vec); // Sets LLColor3 to vec
+
+ F32 magVec() const; // Returns magnitude of LLColor3
+ F32 magVecSquared() const; // Returns magnitude squared of LLColor3
+ F32 normVec(); // Normalizes and returns the magnitude of LLColor3
+
+ const LLColor3& operator=(const LLColor4 &a);
+
+ friend std::ostream& operator<<(std::ostream& s, const LLColor3 &a); // Print a
+ friend LLColor3 operator+(const LLColor3 &a, const LLColor3 &b); // Return vector a + b
+ friend LLColor3 operator-(const LLColor3 &a, const LLColor3 &b); // Return vector a minus b
+
+ friend const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b); // Return vector a + b
+ friend const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b); // Return vector a minus b
+ friend const LLColor3& operator*=(LLColor3 &a, const LLColor3 &b);
+
+ friend LLColor3 operator*(const LLColor3 &a, const LLColor3 &b); // Return a dot b
+ friend LLColor3 operator*(const LLColor3 &a, F32 k); // Return a times scaler k
+ friend LLColor3 operator*(F32 k, const LLColor3 &a); // Return a times scaler k
+
+ friend bool operator==(const LLColor3 &a, const LLColor3 &b); // Return a == b
+ friend bool operator!=(const LLColor3 &a, const LLColor3 &b); // Return a != b
+
+ friend const LLColor3& operator*=(LLColor3 &a, F32 k); // Return a times scaler k
+
+ friend LLColor3 operator-(const LLColor3 &a); // Return vector 1-rgb (inverse)
+
+ inline void clamp();
+ inline void exp(); // Do an exponential on the color
+};
+
+LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u);
+
+
+void LLColor3::clamp()
+{
+ // Clamp the color...
+ if (mV[0] < 0.f)
+ {
+ mV[0] = 0.f;
+ }
+ else if (mV[0] > 1.f)
+ {
+ mV[0] = 1.f;
+ }
+ if (mV[1] < 0.f)
+ {
+ mV[1] = 0.f;
+ }
+ else if (mV[1] > 1.f)
+ {
+ mV[1] = 1.f;
+ }
+ if (mV[2] < 0.f)
+ {
+ mV[2] = 0.f;
+ }
+ else if (mV[2] > 1.f)
+ {
+ mV[2] = 1.f;
+ }
+}
+
+// Non-member functions
+F32 distVec(const LLColor3 &a, const LLColor3 &b); // Returns distance between a and b
+F32 distVec_squared(const LLColor3 &a, const LLColor3 &b);// Returns distance sqaured between a and b
+
+inline LLColor3::LLColor3(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline LLColor3::LLColor3(F32 r, F32 g, F32 b)
+{
+ mV[VX] = r;
+ mV[VY] = g;
+ mV[VZ] = b;
+}
+
+inline LLColor3::LLColor3(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+}
+
+inline LLColor3::LLColor3(char* color_string) // takes a string of format "RRGGBB" where RR is hex 00..FF
+{
+ if (strlen(color_string) < 6)
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ return;
+ }
+
+ static char tempstr[7];
+ strncpy(tempstr,color_string,6);
+ tempstr[6] = '\0';
+ mV[VZ] = (F32)strtol(&tempstr[4],NULL,16)/255.f;
+ tempstr[4] = '\0';
+ mV[VY] = (F32)strtol(&tempstr[2],NULL,16)/255.f;
+ tempstr[2] = '\0';
+ mV[VX] = (F32)strtol(&tempstr[0],NULL,16)/255.f;
+}
+
+inline const LLColor3& LLColor3::setToBlack(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setToWhite(void)
+{
+ mV[0] = 1.f;
+ mV[1] = 1.f;
+ mV[2] = 1.f;
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setVec(F32 r, F32 g, F32 b)
+{
+ mV[0] = r;
+ mV[1] = g;
+ mV[2] = b;
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setVec(const LLColor3 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+ return (*this);
+}
+
+inline const LLColor3& LLColor3::setVec(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+ return (*this);
+}
+
+inline F32 LLColor3::magVec(void) const
+{
+ return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32 LLColor3::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
+inline F32 LLColor3::normVec(void)
+{
+ F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+ F32 oomag;
+
+ if (mag)
+ {
+ oomag = 1.f/mag;
+ mV[0] *= oomag;
+ mV[1] *= oomag;
+ mV[2] *= oomag;
+ }
+ return (mag);
+}
+
+inline void LLColor3::exp()
+{
+#if 0
+ mV[0] = ::exp(mV[0]);
+ mV[1] = ::exp(mV[1]);
+ mV[2] = ::exp(mV[2]);
+#else
+ mV[0] = (F32)LL_FAST_EXP(mV[0]);
+ mV[1] = (F32)LL_FAST_EXP(mV[1]);
+ mV[2] = (F32)LL_FAST_EXP(mV[2]);
+#endif
+}
+
+
+inline LLColor3 operator+(const LLColor3 &a, const LLColor3 &b)
+{
+ return LLColor3(
+ a.mV[0] + b.mV[0],
+ a.mV[1] + b.mV[1],
+ a.mV[2] + b.mV[2]);
+}
+
+inline LLColor3 operator-(const LLColor3 &a, const LLColor3 &b)
+{
+ return LLColor3(
+ a.mV[0] - b.mV[0],
+ a.mV[1] - b.mV[1],
+ a.mV[2] - b.mV[2]);
+}
+
+inline LLColor3 operator*(const LLColor3 &a, const LLColor3 &b)
+{
+ return LLColor3(
+ a.mV[0] * b.mV[0],
+ a.mV[1] * b.mV[1],
+ a.mV[2] * b.mV[2]);
+}
+
+inline LLColor3 operator*(const LLColor3 &a, F32 k)
+{
+ return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline LLColor3 operator*(F32 k, const LLColor3 &a)
+{
+ return LLColor3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline bool operator==(const LLColor3 &a, const LLColor3 &b)
+{
+ return ( (a.mV[0] == b.mV[0])
+ &&(a.mV[1] == b.mV[1])
+ &&(a.mV[2] == b.mV[2]));
+}
+
+inline bool operator!=(const LLColor3 &a, const LLColor3 &b)
+{
+ return ( (a.mV[0] != b.mV[0])
+ ||(a.mV[1] != b.mV[1])
+ ||(a.mV[2] != b.mV[2]));
+}
+
+inline const LLColor3 &operator*=(LLColor3 &a, const LLColor3 &b)
+{
+ a.mV[0] *= b.mV[0];
+ a.mV[1] *= b.mV[1];
+ a.mV[2] *= b.mV[2];
+ return a;
+}
+
+inline const LLColor3& operator+=(LLColor3 &a, const LLColor3 &b)
+{
+ a.mV[0] += b.mV[0];
+ a.mV[1] += b.mV[1];
+ a.mV[2] += b.mV[2];
+ return a;
+}
+
+inline const LLColor3& operator-=(LLColor3 &a, const LLColor3 &b)
+{
+ a.mV[0] -= b.mV[0];
+ a.mV[1] -= b.mV[1];
+ a.mV[2] -= b.mV[2];
+ return a;
+}
+
+inline const LLColor3& operator*=(LLColor3 &a, F32 k)
+{
+ a.mV[0] *= k;
+ a.mV[1] *= k;
+ a.mV[2] *= k;
+ return a;
+}
+
+inline LLColor3 operator-(const LLColor3 &a)
+{
+ return LLColor3(
+ 1.f - a.mV[0],
+ 1.f - a.mV[1],
+ 1.f - a.mV[2] );
+}
+
+// Non-member functions
+
+inline F32 distVec(const LLColor3 &a, const LLColor3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return fsqrtf( x*x + y*y + z*z );
+}
+
+inline F32 distVec_squared(const LLColor3 &a, const LLColor3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return x*x + y*y + z*z;
+}
+
+inline LLColor3 lerp(const LLColor3 &a, const LLColor3 &b, F32 u)
+{
+ return LLColor3(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
+ a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u);
+}
+
+
+#endif
diff --git a/indra/llmath/v3dmath.cpp b/indra/llmath/v3dmath.cpp
new file mode 100644
index 0000000000..0e12389acd
--- /dev/null
+++ b/indra/llmath/v3dmath.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file v3dmath.cpp
+ * @brief LLVector3d class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include <sstream> // gcc 2.95.2 doesn't support sstream
+
+#include "v3dmath.h"
+
+//#include "vmath.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+#include "llquantize.h"
+
+// LLVector3d
+// WARNING: Don't use these for global const definitions!
+// For example:
+// const LLQuaternion(0.5f * F_PI, LLVector3d::zero);
+// at the top of a *.cpp file might not give you what you think.
+const LLVector3d LLVector3d::zero(0,0,0);
+const LLVector3d LLVector3d::x_axis(1, 0, 0);
+const LLVector3d LLVector3d::y_axis(0, 1, 0);
+const LLVector3d LLVector3d::z_axis(0, 0, 1);
+const LLVector3d LLVector3d::x_axis_neg(-1, 0, 0);
+const LLVector3d LLVector3d::y_axis_neg(0, -1, 0);
+const LLVector3d LLVector3d::z_axis_neg(0, 0, -1);
+
+
+// Clamps each values to range (min,max).
+// Returns TRUE if data changed.
+BOOL LLVector3d::clamp(F64 min, F64 max)
+{
+ BOOL ret = FALSE;
+
+ if (mdV[0] < min) { mdV[0] = min; ret = TRUE; }
+ if (mdV[1] < min) { mdV[1] = min; ret = TRUE; }
+ if (mdV[2] < min) { mdV[2] = min; ret = TRUE; }
+
+ if (mdV[0] > max) { mdV[0] = max; ret = TRUE; }
+ if (mdV[1] > max) { mdV[1] = max; ret = TRUE; }
+ if (mdV[2] > max) { mdV[2] = max; ret = TRUE; }
+
+ return ret;
+}
+
+// Sets all values to absolute value of their original values
+// Returns TRUE if data changed
+BOOL LLVector3d::abs()
+{
+ BOOL ret = FALSE;
+
+ if (mdV[0] < 0.0) { mdV[0] = -mdV[0]; ret = TRUE; }
+ if (mdV[1] < 0.0) { mdV[1] = -mdV[1]; ret = TRUE; }
+ if (mdV[2] < 0.0) { mdV[2] = -mdV[2]; ret = TRUE; }
+
+ return ret;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLVector3d &a)
+{
+ s << "{ " << a.mdV[VX] << ", " << a.mdV[VY] << ", " << a.mdV[VZ] << " }";
+ return s;
+}
+
+const LLVector3d& LLVector3d::operator=(const LLVector4 &a)
+{
+ mdV[0] = a.mV[0];
+ mdV[1] = a.mV[1];
+ mdV[2] = a.mV[2];
+ return *this;
+}
+
+const LLVector3d& LLVector3d::rotVec(const LLMatrix3 &mat)
+{
+ *this = *this * mat;
+ return *this;
+}
+
+const LLVector3d& LLVector3d::rotVec(const LLQuaternion &q)
+{
+ *this = *this * q;
+ return *this;
+}
+
+const LLVector3d& LLVector3d::rotVec(F64 angle, const LLVector3d &vec)
+{
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix3((F32)angle, vec);
+ }
+ return *this;
+}
+
+const LLVector3d& LLVector3d::rotVec(F64 angle, F64 x, F64 y, F64 z)
+{
+ LLVector3d vec(x, y, z);
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix3((F32)angle, vec);
+ }
+ return *this;
+}
+
+
+BOOL LLVector3d::parseVector3d(const char* buf, LLVector3d* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLVector3d v;
+ S32 count = sscanf( buf, "%lf %lf %lf", v.mdV + 0, v.mdV + 1, v.mdV + 2 );
+ if( 3 == count )
+ {
+ value->setVec( v );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
diff --git a/indra/llmath/v3dmath.h b/indra/llmath/v3dmath.h
new file mode 100644
index 0000000000..d8feb10757
--- /dev/null
+++ b/indra/llmath/v3dmath.h
@@ -0,0 +1,409 @@
+/**
+ * @file v3dmath.h
+ * @brief High precision 3 dimensional vector.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V3DMATH_H
+#define LL_V3DMATH_H
+
+#include "llerror.h"
+#include "v3math.h"
+
+class LLVector3d
+{
+ public:
+ F64 mdV[3];
+
+ const static LLVector3d zero;
+ const static LLVector3d x_axis;
+ const static LLVector3d y_axis;
+ const static LLVector3d z_axis;
+ const static LLVector3d x_axis_neg;
+ const static LLVector3d y_axis_neg;
+ const static LLVector3d z_axis_neg;
+
+ inline LLVector3d(); // Initializes LLVector3d to (0, 0, 0)
+ inline LLVector3d(const F64 x, const F64 y, const F64 z); // Initializes LLVector3d to (x. y, z)
+ inline explicit LLVector3d(const F64 *vec); // Initializes LLVector3d to (vec[0]. vec[1], vec[2])
+ inline explicit LLVector3d(const LLVector3 &vec);
+ LLVector3d(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mdV[0] = sd[0].asReal();
+ mdV[1] = sd[1].asReal();
+ mdV[2] = sd[2].asReal();
+ }
+
+ const LLVector3d& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mdV[0];
+ ret[1] = mdV[1];
+ ret[2] = mdV[2];
+ return ret;
+ }
+
+ inline BOOL isFinite() const; // checks to see if all values of LLVector3d are finite
+ BOOL clamp(const F64 min, const F64 max); // Clamps all values to (min,max), returns TRUE if data changed
+ BOOL abs(); // sets all values to absolute value of original value (first octant), returns TRUE if changed
+
+ inline const LLVector3d& clearVec(); // Clears LLVector3d to (0, 0, 0, 1)
+ inline const LLVector3d& zeroVec(); // Zero LLVector3d to (0, 0, 0, 0)
+ inline const LLVector3d& setVec(const F64 x, const F64 y, const F64 z); // Sets LLVector3d to (x, y, z, 1)
+ inline const LLVector3d& setVec(const LLVector3d &vec); // Sets LLVector3d to vec
+ inline const LLVector3d& setVec(const F64 *vec); // Sets LLVector3d to vec
+ inline const LLVector3d& setVec(const LLVector3 &vec);
+
+ F64 magVec() const; // Returns magnitude of LLVector3d
+ F64 magVecSquared() const; // Returns magnitude squared of LLVector3d
+ inline F64 normVec(); // Normalizes and returns the magnitude of LLVector3d
+
+ const LLVector3d& rotVec(const F64 angle, const LLVector3d &vec); // Rotates about vec by angle radians
+ const LLVector3d& rotVec(const F64 angle, const F64 x, const F64 y, const F64 z); // Rotates about x,y,z by angle radians
+ const LLVector3d& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
+ const LLVector3d& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
+
+ BOOL isNull() const; // Returns TRUE if vector has a _very_small_ length
+ BOOL isExactlyZero() const { return !mdV[VX] && !mdV[VY] && !mdV[VZ]; }
+
+ const LLVector3d& operator=(const LLVector4 &a);
+
+ F64 operator[](int idx) const { return mdV[idx]; }
+ F64 &operator[](int idx) { return mdV[idx]; }
+
+ friend LLVector3d operator+(const LLVector3d &a, const LLVector3d &b); // Return vector a + b
+ friend LLVector3d operator-(const LLVector3d &a, const LLVector3d &b); // Return vector a minus b
+ friend F64 operator*(const LLVector3d &a, const LLVector3d &b); // Return a dot b
+ friend LLVector3d operator%(const LLVector3d &a, const LLVector3d &b); // Return a cross b
+ friend LLVector3d operator*(const LLVector3d &a, const F64 k); // Return a times scaler k
+ friend LLVector3d operator/(const LLVector3d &a, const F64 k); // Return a divided by scaler k
+ friend LLVector3d operator*(const F64 k, const LLVector3d &a); // Return a times scaler k
+ friend bool operator==(const LLVector3d &a, const LLVector3d &b); // Return a == b
+ friend bool operator!=(const LLVector3d &a, const LLVector3d &b); // Return a != b
+
+ friend const LLVector3d& operator+=(LLVector3d &a, const LLVector3d &b); // Return vector a + b
+ friend const LLVector3d& operator-=(LLVector3d &a, const LLVector3d &b); // Return vector a minus b
+ friend const LLVector3d& operator%=(LLVector3d &a, const LLVector3d &b); // Return a cross b
+ friend const LLVector3d& operator*=(LLVector3d &a, const F64 k); // Return a times scaler k
+ friend const LLVector3d& operator/=(LLVector3d &a, const F64 k); // Return a divided by scaler k
+
+ friend LLVector3d operator-(const LLVector3d &a); // Return vector -a
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector3d &a); // Stream a
+
+ static BOOL parseVector3d(const char* buf, LLVector3d* value);
+
+};
+
+typedef LLVector3d LLGlobalVec;
+
+const LLVector3d &LLVector3d::setVec(const LLVector3 &vec)
+{
+ mdV[0] = vec.mV[0];
+ mdV[1] = vec.mV[1];
+ mdV[2] = vec.mV[2];
+ return *this;
+}
+
+
+inline LLVector3d::LLVector3d(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+}
+
+inline LLVector3d::LLVector3d(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+}
+
+inline LLVector3d::LLVector3d(const F64 *vec)
+{
+ mdV[VX] = vec[VX];
+ mdV[VY] = vec[VY];
+ mdV[VZ] = vec[VZ];
+}
+
+inline LLVector3d::LLVector3d(const LLVector3 &vec)
+{
+ mdV[VX] = vec.mV[VX];
+ mdV[VY] = vec.mV[VY];
+ mdV[VZ] = vec.mV[VZ];
+}
+
+/*
+inline LLVector3d::LLVector3d(const LLVector3d &copy)
+{
+ mdV[VX] = copy.mdV[VX];
+ mdV[VY] = copy.mdV[VY];
+ mdV[VZ] = copy.mdV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline BOOL LLVector3d::isFinite() const
+{
+ return (llfinite(mdV[VX]) && llfinite(mdV[VY]) && llfinite(mdV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline const LLVector3d& LLVector3d::clearVec(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2]= 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::zeroVec(void)
+{
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const F64 x, const F64 y, const F64 z)
+{
+ mdV[VX] = x;
+ mdV[VY] = y;
+ mdV[VZ] = z;
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const LLVector3d &vec)
+{
+ mdV[0] = vec.mdV[0];
+ mdV[1] = vec.mdV[1];
+ mdV[2] = vec.mdV[2];
+ return (*this);
+}
+
+inline const LLVector3d& LLVector3d::setVec(const F64 *vec)
+{
+ mdV[0] = vec[0];
+ mdV[1] = vec[1];
+ mdV[2] = vec[2];
+ return (*this);
+}
+
+inline F64 LLVector3d::normVec(void)
+{
+ F64 mag = fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+ F64 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mdV[0] *= oomag;
+ mdV[1] *= oomag;
+ mdV[2] *= oomag;
+ }
+ else
+ {
+ mdV[0] = 0.f;
+ mdV[1] = 0.f;
+ mdV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+// LLVector3d Magnitude and Normalization Functions
+
+inline F64 LLVector3d::magVec(void) const
+{
+ return fsqrtf(mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2]);
+}
+
+inline F64 LLVector3d::magVecSquared(void) const
+{
+ return mdV[0]*mdV[0] + mdV[1]*mdV[1] + mdV[2]*mdV[2];
+}
+
+inline LLVector3d operator+(const LLVector3d &a, const LLVector3d &b)
+{
+ LLVector3d c(a);
+ return c += b;
+}
+
+inline LLVector3d operator-(const LLVector3d &a, const LLVector3d &b)
+{
+ LLVector3d c(a);
+ return c -= b;
+}
+
+inline F64 operator*(const LLVector3d &a, const LLVector3d &b)
+{
+ return (a.mdV[0]*b.mdV[0] + a.mdV[1]*b.mdV[1] + a.mdV[2]*b.mdV[2]);
+}
+
+inline LLVector3d operator%(const LLVector3d &a, const LLVector3d &b)
+{
+ return LLVector3d( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1] );
+}
+
+inline LLVector3d operator/(const LLVector3d &a, const F64 k)
+{
+ F64 t = 1.f / k;
+ return LLVector3d( a.mdV[0] * t, a.mdV[1] * t, a.mdV[2] * t );
+}
+
+inline LLVector3d operator*(const LLVector3d &a, const F64 k)
+{
+ return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
+}
+
+inline LLVector3d operator*(F64 k, const LLVector3d &a)
+{
+ return LLVector3d( a.mdV[0] * k, a.mdV[1] * k, a.mdV[2] * k );
+}
+
+inline bool operator==(const LLVector3d &a, const LLVector3d &b)
+{
+ return ( (a.mdV[0] == b.mdV[0])
+ &&(a.mdV[1] == b.mdV[1])
+ &&(a.mdV[2] == b.mdV[2]));
+}
+
+inline bool operator!=(const LLVector3d &a, const LLVector3d &b)
+{
+ return ( (a.mdV[0] != b.mdV[0])
+ ||(a.mdV[1] != b.mdV[1])
+ ||(a.mdV[2] != b.mdV[2]));
+}
+
+inline const LLVector3d& operator+=(LLVector3d &a, const LLVector3d &b)
+{
+ a.mdV[0] += b.mdV[0];
+ a.mdV[1] += b.mdV[1];
+ a.mdV[2] += b.mdV[2];
+ return a;
+}
+
+inline const LLVector3d& operator-=(LLVector3d &a, const LLVector3d &b)
+{
+ a.mdV[0] -= b.mdV[0];
+ a.mdV[1] -= b.mdV[1];
+ a.mdV[2] -= b.mdV[2];
+ return a;
+}
+
+inline const LLVector3d& operator%=(LLVector3d &a, const LLVector3d &b)
+{
+ LLVector3d ret( a.mdV[1]*b.mdV[2] - b.mdV[1]*a.mdV[2], a.mdV[2]*b.mdV[0] - b.mdV[2]*a.mdV[0], a.mdV[0]*b.mdV[1] - b.mdV[0]*a.mdV[1]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector3d& operator*=(LLVector3d &a, const F64 k)
+{
+ a.mdV[0] *= k;
+ a.mdV[1] *= k;
+ a.mdV[2] *= k;
+ return a;
+}
+
+inline const LLVector3d& operator/=(LLVector3d &a, const F64 k)
+{
+ F64 t = 1.f / k;
+ a.mdV[0] *= t;
+ a.mdV[1] *= t;
+ a.mdV[2] *= t;
+ return a;
+}
+
+inline LLVector3d operator-(const LLVector3d &a)
+{
+ return LLVector3d( -a.mdV[0], -a.mdV[1], -a.mdV[2] );
+}
+
+inline F64 dist_vec(const LLVector3d &a, const LLVector3d &b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ F64 z = a.mdV[2] - b.mdV[2];
+ return fsqrtf( x*x + y*y + z*z );
+}
+
+inline F64 dist_vec_squared(const LLVector3d &a, const LLVector3d &b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ F64 z = a.mdV[2] - b.mdV[2];
+ return x*x + y*y + z*z;
+}
+
+inline F64 dist_vec_squared2D(const LLVector3d &a, const LLVector3d &b)
+{
+ F64 x = a.mdV[0] - b.mdV[0];
+ F64 y = a.mdV[1] - b.mdV[1];
+ return x*x + y*y;
+}
+
+inline LLVector3d lerp(const LLVector3d &a, const LLVector3d &b, const F64 u)
+{
+ return LLVector3d(
+ a.mdV[VX] + (b.mdV[VX] - a.mdV[VX]) * u,
+ a.mdV[VY] + (b.mdV[VY] - a.mdV[VY]) * u,
+ a.mdV[VZ] + (b.mdV[VZ] - a.mdV[VZ]) * u);
+}
+
+
+inline BOOL LLVector3d::isNull() const
+{
+ if ( F_APPROXIMATELY_ZERO > mdV[VX]*mdV[VX] + mdV[VY]*mdV[VY] + mdV[VZ]*mdV[VZ] )
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+inline F64 angle_between(const LLVector3d& a, const LLVector3d& b)
+{
+ LLVector3d an = a;
+ LLVector3d bn = b;
+ an.normVec();
+ bn.normVec();
+ F64 cosine = an * bn;
+ F64 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ acos(cosine);
+ return angle;
+}
+
+inline BOOL are_parallel(const LLVector3d &a, const LLVector3d &b, const F64 epsilon)
+{
+ LLVector3d an = a;
+ LLVector3d bn = b;
+ an.normVec();
+ bn.normVec();
+ F64 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif // LL_V3DMATH_H
diff --git a/indra/llmath/v3math.cpp b/indra/llmath/v3math.cpp
new file mode 100644
index 0000000000..f254f4112e
--- /dev/null
+++ b/indra/llmath/v3math.cpp
@@ -0,0 +1,213 @@
+/**
+ * @file v3math.cpp
+ * @brief LLVector3 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "v3math.h"
+
+//#include "vmath.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+#include "llquantize.h"
+#include "v3dmath.h"
+
+// LLVector3
+// WARNING: Don't use these for global const definitions!
+// For example:
+// const LLQuaternion(0.5f * F_PI, LLVector3::zero);
+// at the top of a *.cpp file might not give you what you think.
+const LLVector3 LLVector3::zero(0,0,0);
+const LLVector3 LLVector3::x_axis(1.f, 0, 0);
+const LLVector3 LLVector3::y_axis(0, 1.f, 0);
+const LLVector3 LLVector3::z_axis(0, 0, 1.f);
+const LLVector3 LLVector3::x_axis_neg(-1.f, 0, 0);
+const LLVector3 LLVector3::y_axis_neg(0, -1.f, 0);
+const LLVector3 LLVector3::z_axis_neg(0, 0, -1.f);
+const LLVector3 LLVector3::all_one(1.f,1.f,1.f);
+
+
+// Clamps each values to range (min,max).
+// Returns TRUE if data changed.
+BOOL LLVector3::clamp(F32 min, F32 max)
+{
+ BOOL ret = FALSE;
+
+ if (mV[0] < min) { mV[0] = min; ret = TRUE; }
+ if (mV[1] < min) { mV[1] = min; ret = TRUE; }
+ if (mV[2] < min) { mV[2] = min; ret = TRUE; }
+
+ if (mV[0] > max) { mV[0] = max; ret = TRUE; }
+ if (mV[1] > max) { mV[1] = max; ret = TRUE; }
+ if (mV[2] > max) { mV[2] = max; ret = TRUE; }
+
+ return ret;
+}
+
+// Sets all values to absolute value of their original values
+// Returns TRUE if data changed
+BOOL LLVector3::abs()
+{
+ BOOL ret = FALSE;
+
+ if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = TRUE; }
+ if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = TRUE; }
+ if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = TRUE; }
+
+ return ret;
+}
+
+// Quatizations
+void LLVector3::quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz)
+{
+ F32 x = mV[VX];
+ F32 y = mV[VY];
+ F32 z = mV[VZ];
+
+ x = U16_to_F32(F32_to_U16(x, lowerxy, upperxy), lowerxy, upperxy);
+ y = U16_to_F32(F32_to_U16(y, lowerxy, upperxy), lowerxy, upperxy);
+ z = U16_to_F32(F32_to_U16(z, lowerz, upperz), lowerz, upperz);
+
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+void LLVector3::quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz)
+{
+ mV[VX] = U8_to_F32(F32_to_U8(mV[VX], lowerxy, upperxy), lowerxy, upperxy);;
+ mV[VY] = U8_to_F32(F32_to_U8(mV[VY], lowerxy, upperxy), lowerxy, upperxy);
+ mV[VZ] = U8_to_F32(F32_to_U8(mV[VZ], lowerz, upperz), lowerz, upperz);
+}
+
+
+void LLVector3::snap(S32 sig_digits)
+{
+ mV[VX] = snap_to_sig_figs(mV[VX], sig_digits);
+ mV[VY] = snap_to_sig_figs(mV[VY], sig_digits);
+ mV[VZ] = snap_to_sig_figs(mV[VZ], sig_digits);
+}
+
+
+std::ostream& operator<<(std::ostream& s, const LLVector3 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << " }";
+ return s;
+}
+
+
+const LLVector3& LLVector3::rotVec(const LLMatrix3 &mat)
+{
+ *this = *this * mat;
+ return *this;
+}
+
+const LLVector3& LLVector3::rotVec(const LLQuaternion &q)
+{
+ *this = *this * q;
+ return *this;
+}
+
+const LLVector3& LLVector3::rotVec(F32 angle, const LLVector3 &vec)
+{
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix3(angle, vec);
+ }
+ return *this;
+}
+
+const LLVector3& LLVector3::rotVec(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLVector3 vec(x, y, z);
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix3(angle, vec);
+ }
+ return *this;
+}
+
+const LLVector3& LLVector3::scaleVec(const LLVector3& vec)
+{
+ mV[VX] *= vec.mV[VX];
+ mV[VY] *= vec.mV[VY];
+ mV[VZ] *= vec.mV[VZ];
+
+ return *this;
+}
+
+LLVector3 LLVector3::scaledVec(const LLVector3& vec) const
+{
+ LLVector3 ret = LLVector3(*this);
+ ret.scaleVec(vec);
+ return ret;
+}
+
+const LLVector3& LLVector3::setVec(const LLVector3d &vec)
+{
+ mV[0] = (F32)vec.mdV[0];
+ mV[1] = (F32)vec.mdV[1];
+ mV[2] = (F32)vec.mdV[2];
+ return (*this);
+}
+
+const LLVector3& LLVector3::setVec(const LLVector4 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+ return (*this);
+}
+
+LLVector3::LLVector3(const LLVector3d &vec)
+{
+ mV[VX] = (F32)vec.mdV[VX];
+ mV[VY] = (F32)vec.mdV[VY];
+ mV[VZ] = (F32)vec.mdV[VZ];
+}
+
+LLVector3::LLVector3(const LLVector4 &vec)
+{
+ mV[VX] = (F32)vec.mV[VX];
+ mV[VY] = (F32)vec.mV[VY];
+ mV[VZ] = (F32)vec.mV[VZ];
+}
+
+const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &rot)
+{
+ const F32 rw = - rot.mQ[VX] * a.mV[VX] - rot.mQ[VY] * a.mV[VY] - rot.mQ[VZ] * a.mV[VZ];
+ const F32 rx = rot.mQ[VW] * a.mV[VX] + rot.mQ[VY] * a.mV[VZ] - rot.mQ[VZ] * a.mV[VY];
+ const F32 ry = rot.mQ[VW] * a.mV[VY] + rot.mQ[VZ] * a.mV[VX] - rot.mQ[VX] * a.mV[VZ];
+ const F32 rz = rot.mQ[VW] * a.mV[VZ] + rot.mQ[VX] * a.mV[VY] - rot.mQ[VY] * a.mV[VX];
+
+ a.mV[VX] = - rw * rot.mQ[VX] + rx * rot.mQ[VW] - ry * rot.mQ[VZ] + rz * rot.mQ[VY];
+ a.mV[VY] = - rw * rot.mQ[VY] + ry * rot.mQ[VW] - rz * rot.mQ[VX] + rx * rot.mQ[VZ];
+ a.mV[VZ] = - rw * rot.mQ[VZ] + rz * rot.mQ[VW] - rx * rot.mQ[VY] + ry * rot.mQ[VX];
+
+ return a;
+}
+
+// static
+BOOL LLVector3::parseVector3(const char* buf, LLVector3* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLVector3 v;
+ S32 count = sscanf( buf, "%f %f %f", v.mV + 0, v.mV + 1, v.mV + 2 );
+ if( 3 == count )
+ {
+ value->setVec( v );
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/indra/llmath/v3math.h b/indra/llmath/v3math.h
new file mode 100644
index 0000000000..09042b6dc3
--- /dev/null
+++ b/indra/llmath/v3math.h
@@ -0,0 +1,446 @@
+/**
+ * @file v3math.h
+ * @brief LLVector3 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V3MATH_H
+#define LL_V3MATH_H
+
+#include "llerror.h"
+#include "llmath.h"
+
+#include "llsd.h"
+class LLVector4;
+class LLMatrix3;
+class LLVector3d;
+class LLQuaternion;
+
+// Llvector3 = |x y z w|
+
+static const U32 LENGTHOFVECTOR3 = 3;
+
+class LLVector3
+{
+ public:
+ F32 mV[LENGTHOFVECTOR3];
+
+ static const LLVector3 zero;
+ static const LLVector3 x_axis;
+ static const LLVector3 y_axis;
+ static const LLVector3 z_axis;
+ static const LLVector3 x_axis_neg;
+ static const LLVector3 y_axis_neg;
+ static const LLVector3 z_axis_neg;
+ static const LLVector3 all_one;
+
+ inline LLVector3(); // Initializes LLVector3 to (0, 0, 0)
+ inline LLVector3(const F32 x, const F32 y, const F32 z); // Initializes LLVector3 to (x. y, z)
+ inline explicit LLVector3(const F32 *vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLVector3d &vec); // Initializes LLVector3 to (vec[0]. vec[1], vec[2])
+ explicit LLVector3(const LLVector4 &vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2])
+ LLVector3(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mV[0];
+ ret[1] = mV[1];
+ ret[2] = mV[2];
+ return ret;
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+ }
+
+ const LLVector3& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ inline BOOL isFinite() const; // checks to see if all values of LLVector3 are finite
+ BOOL clamp(F32 min, F32 max); // Clamps all values to (min,max), returns TRUE if data changed
+
+ void quantize16(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
+ void quantize8(F32 lowerxy, F32 upperxy, F32 lowerz, F32 upperz); // changes the vector to reflect quatization
+ void snap(S32 sig_digits); // snaps x,y,z to sig_digits decimal places
+
+ BOOL abs(); // sets all values to absolute value of original value (first octant), returns TRUE if changed
+
+ inline void clearVec(); // Clears LLVector3 to (0, 0, 0, 1)
+ inline void zeroVec(); // Zero LLVector3 to (0, 0, 0, 0)
+ inline void setVec(F32 x, F32 y, F32 z); // Sets LLVector3 to (x, y, z, 1)
+ inline void setVec(const LLVector3 &vec); // Sets LLVector3 to vec
+ inline void setVec(const F32 *vec); // Sets LLVector3 to vec
+
+ const LLVector3& setVec(const LLVector4 &vec);
+ const LLVector3& setVec(const LLVector3d &vec); // Sets LLVector3 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector3
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector3
+ inline F32 normVec(); // Normalizes and returns the magnitude of LLVector3
+
+ const LLVector3& rotVec(F32 angle, const LLVector3 &vec); // Rotates about vec by angle radians
+ const LLVector3& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
+ const LLVector3& rotVec(const LLMatrix3 &mat); // Rotates by LLMatrix4 mat
+ const LLVector3& rotVec(const LLQuaternion &q); // Rotates by LLQuaternion q
+
+ const LLVector3& scaleVec(const LLVector3& vec); // scales per component by vec
+ LLVector3 scaledVec(const LLVector3& vec) const; // get a copy of this vector scaled by vec
+
+ BOOL isNull() const; // Returns TRUE if vector has a _very_small_ length
+ BOOL isExactlyZero() const { return !mV[VX] && !mV[VY] && !mV[VZ]; }
+
+ F32 operator[](int idx) const { return mV[idx]; }
+ F32 &operator[](int idx) { return mV[idx]; }
+
+ friend LLVector3 operator+(const LLVector3 &a, const LLVector3 &b); // Return vector a + b
+ friend LLVector3 operator-(const LLVector3 &a, const LLVector3 &b); // Return vector a minus b
+ friend F32 operator*(const LLVector3 &a, const LLVector3 &b); // Return a dot b
+ friend LLVector3 operator%(const LLVector3 &a, const LLVector3 &b); // Return a cross b
+ friend LLVector3 operator*(const LLVector3 &a, F32 k); // Return a times scaler k
+ friend LLVector3 operator/(const LLVector3 &a, F32 k); // Return a divided by scaler k
+ friend LLVector3 operator*(F32 k, const LLVector3 &a); // Return a times scaler k
+ friend bool operator==(const LLVector3 &a, const LLVector3 &b); // Return a == b
+ friend bool operator!=(const LLVector3 &a, const LLVector3 &b); // Return a != b
+ // less than operator useful for using vectors as std::map keys
+ friend bool operator<(const LLVector3 &a, const LLVector3 &b); // Return a < b
+
+ friend const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b); // Return vector a + b
+ friend const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b); // Return vector a minus b
+ friend const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b); // Return a cross b
+ friend const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b); // Returns a * b;
+ friend const LLVector3& operator*=(LLVector3 &a, F32 k); // Return a times scaler k
+ friend const LLVector3& operator/=(LLVector3 &a, F32 k); // Return a divided by scaler k
+ friend const LLVector3& operator*=(LLVector3 &a, const LLQuaternion &b); // Returns a * b;
+
+ friend LLVector3 operator-(const LLVector3 &a); // Return vector -a
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector3 &a); // Stream a
+
+ static BOOL parseVector3(const char* buf, LLVector3* value);
+};
+
+typedef LLVector3 LLSimLocalVec;
+
+// Non-member functions
+
+F32 angle_between(const LLVector3 &a, const LLVector3 &b); // Returns angle (radians) between a and b
+BOOL are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns TRUE if a and b are very close to parallel
+F32 dist_vec(const LLVector3 &a, const LLVector3 &b); // Returns distance between a and b
+F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b);// Returns distance sqaured between a and b
+F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b);// Returns distance sqaured between a and b ignoring Z component
+LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b); // Returns vector a projected on vector b
+LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+
+inline LLVector3::LLVector3(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline LLVector3::LLVector3(const F32 x, const F32 y, const F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline LLVector3::LLVector3(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+}
+
+/*
+inline LLVector3::LLVector3(const LLVector3 &copy)
+{
+ mV[VX] = copy.mV[VX];
+ mV[VY] = copy.mV[VY];
+ mV[VZ] = copy.mV[VZ];
+}
+*/
+
+// Destructors
+
+// checker
+inline BOOL LLVector3::isFinite() const
+{
+ return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]));
+}
+
+
+// Clear and Assignment Functions
+
+inline void LLVector3::clearVec(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::zeroVec(void)
+{
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+}
+
+inline void LLVector3::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+}
+
+inline void LLVector3::setVec(const LLVector3 &vec)
+{
+ mV[0] = vec.mV[0];
+ mV[1] = vec.mV[1];
+ mV[2] = vec.mV[2];
+}
+
+inline void LLVector3::setVec(const F32 *vec)
+{
+ mV[0] = vec[0];
+ mV[1] = vec[1];
+ mV[2] = vec[2];
+}
+
+inline F32 LLVector3::normVec(void)
+{
+ F32 mag = fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+ F32 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mV[0] *= oomag;
+ mV[1] *= oomag;
+ mV[2] *= oomag;
+ }
+ else
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+// LLVector3 Magnitude and Normalization Functions
+
+inline F32 LLVector3::magVec(void) const
+{
+ return fsqrtf(mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2]);
+}
+
+inline F32 LLVector3::magVecSquared(void) const
+{
+ return mV[0]*mV[0] + mV[1]*mV[1] + mV[2]*mV[2];
+}
+
+inline LLVector3 operator+(const LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 c(a);
+ return c += b;
+}
+
+inline LLVector3 operator-(const LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 c(a);
+ return c -= b;
+}
+
+inline F32 operator*(const LLVector3 &a, const LLVector3 &b)
+{
+ return (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2]);
+}
+
+inline LLVector3 operator%(const LLVector3 &a, const LLVector3 &b)
+{
+ return LLVector3( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1] );
+}
+
+inline LLVector3 operator/(const LLVector3 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ return LLVector3( a.mV[0] * t, a.mV[1] * t, a.mV[2] * t );
+}
+
+inline LLVector3 operator*(const LLVector3 &a, F32 k)
+{
+ return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline LLVector3 operator*(F32 k, const LLVector3 &a)
+{
+ return LLVector3( a.mV[0] * k, a.mV[1] * k, a.mV[2] * k );
+}
+
+inline bool operator==(const LLVector3 &a, const LLVector3 &b)
+{
+ return ( (a.mV[0] == b.mV[0])
+ &&(a.mV[1] == b.mV[1])
+ &&(a.mV[2] == b.mV[2]));
+}
+
+inline bool operator!=(const LLVector3 &a, const LLVector3 &b)
+{
+ return ( (a.mV[0] != b.mV[0])
+ ||(a.mV[1] != b.mV[1])
+ ||(a.mV[2] != b.mV[2]));
+}
+
+inline bool operator<(const LLVector3 &a, const LLVector3 &b)
+{
+ return (a.mV[0] < b.mV[0]
+ || (a.mV[0] == b.mV[0]
+ && (a.mV[1] < b.mV[1]
+ || (a.mV[1] == b.mV[1])
+ && a.mV[2] < b.mV[2])));
+}
+
+inline const LLVector3& operator+=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] += b.mV[0];
+ a.mV[1] += b.mV[1];
+ a.mV[2] += b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator-=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] -= b.mV[0];
+ a.mV[1] -= b.mV[1];
+ a.mV[2] -= b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator%=(LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 ret( a.mV[1]*b.mV[2] - b.mV[1]*a.mV[2], a.mV[2]*b.mV[0] - b.mV[2]*a.mV[0], a.mV[0]*b.mV[1] - b.mV[0]*a.mV[1]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector3& operator*=(LLVector3 &a, F32 k)
+{
+ a.mV[0] *= k;
+ a.mV[1] *= k;
+ a.mV[2] *= k;
+ return a;
+}
+
+inline const LLVector3& operator*=(LLVector3 &a, const LLVector3 &b)
+{
+ a.mV[0] *= b.mV[0];
+ a.mV[1] *= b.mV[1];
+ a.mV[2] *= b.mV[2];
+ return a;
+}
+
+inline const LLVector3& operator/=(LLVector3 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ a.mV[0] *= t;
+ a.mV[1] *= t;
+ a.mV[2] *= t;
+ return a;
+}
+
+inline LLVector3 operator-(const LLVector3 &a)
+{
+ return LLVector3( -a.mV[0], -a.mV[1], -a.mV[2] );
+}
+
+inline F32 dist_vec(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return fsqrtf( x*x + y*y + z*z );
+}
+
+inline F32 dist_vec_squared(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ F32 z = a.mV[2] - b.mV[2];
+ return x*x + y*y + z*z;
+}
+
+inline F32 dist_vec_squared2D(const LLVector3 &a, const LLVector3 &b)
+{
+ F32 x = a.mV[0] - b.mV[0];
+ F32 y = a.mV[1] - b.mV[1];
+ return x*x + y*y;
+}
+
+inline LLVector3 projected_vec(const LLVector3 &a, const LLVector3 &b)
+{
+ LLVector3 project_axis = b;
+ project_axis.normVec();
+ return project_axis * (a * project_axis);
+}
+
+inline LLVector3 lerp(const LLVector3 &a, const LLVector3 &b, F32 u)
+{
+ return LLVector3(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
+ a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u);
+}
+
+
+inline BOOL LLVector3::isNull() const
+{
+ if ( F_APPROXIMATELY_ZERO > mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ] )
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+inline F32 angle_between(const LLVector3& a, const LLVector3& b)
+{
+ LLVector3 an = a;
+ LLVector3 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 cosine = an * bn;
+ F32 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ (F32)acos(cosine);
+ return angle;
+}
+
+inline BOOL are_parallel(const LLVector3 &a, const LLVector3 &b, F32 epsilon)
+{
+ LLVector3 an = a;
+ LLVector3 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#endif
diff --git a/indra/llmath/v4color.cpp b/indra/llmath/v4color.cpp
new file mode 100644
index 0000000000..83870941df
--- /dev/null
+++ b/indra/llmath/v4color.cpp
@@ -0,0 +1,561 @@
+/**
+ * @file v4color.cpp
+ * @brief LLColor4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llboost.h"
+
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3color.h"
+//#include "vmath.h"
+#include "llmath.h"
+
+// LLColor4
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLColor4 LLColor4::red( 1.f, 0.f, 0.f, 1.f);
+LLColor4 LLColor4::green( 0.f, 1.f, 0.f, 1.f);
+LLColor4 LLColor4::blue( 0.f, 0.f, 1.f, 1.f);
+LLColor4 LLColor4::black( 0.f, 0.f, 0.f, 1.f);
+LLColor4 LLColor4::yellow( 1.f, 1.f, 0.f, 1.f);
+LLColor4 LLColor4::magenta( 1.0f, 0.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::cyan( 0.0f, 1.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::white( 1.f, 1.f, 1.f, 1.f);
+LLColor4 LLColor4::smoke( 0.5f, 0.5f, 0.5f, 0.5f);
+LLColor4 LLColor4::grey( 0.5f, 0.5f, 0.5f, 1.0f);
+LLColor4 LLColor4::orange( 1.f, 0.5, 0.f, 1.f );
+LLColor4 LLColor4::purple( 0.6f, 0.2f, 0.8f, 1.0f);
+LLColor4 LLColor4::pink( 1.0f, 0.5f, 0.8f, 1.0f);
+LLColor4 LLColor4::transparent( 0.f, 0.f, 0.f, 0.f );
+
+//////////////////////////////////////////////////////////////////////////////
+
+LLColor4 LLColor4::grey1(0.8f, 0.8f, 0.8f, 1.0f);
+LLColor4 LLColor4::grey2(0.6f, 0.6f, 0.6f, 1.0f);
+LLColor4 LLColor4::grey3(0.4f, 0.4f, 0.4f, 1.0f);
+LLColor4 LLColor4::grey4(0.3f, 0.3f, 0.3f, 1.0f);
+
+LLColor4 LLColor4::red1(1.0f, 0.0f, 0.0f, 1.0f);
+LLColor4 LLColor4::red2(0.6f, 0.0f, 0.0f, 1.0f);
+LLColor4 LLColor4::red3(1.0f, 0.2f, 0.2f, 1.0f);
+LLColor4 LLColor4::red4(0.5f, 0.1f, 0.1f, 1.0f);
+LLColor4 LLColor4::red5(0.8f, 0.1f, 0.0f, 1.0f);
+
+LLColor4 LLColor4::green1(0.0f, 1.0f, 0.0f, 1.0f);
+LLColor4 LLColor4::green2(0.0f, 0.6f, 0.0f, 1.0f);
+LLColor4 LLColor4::green3(0.0f, 0.4f, 0.0f, 1.0f);
+LLColor4 LLColor4::green4(0.0f, 1.0f, 0.4f, 1.0f);
+LLColor4 LLColor4::green5(0.2f, 0.6f, 0.4f, 1.0f);
+LLColor4 LLColor4::green6(0.4f, 0.6f, 0.2f, 1.0f);
+
+LLColor4 LLColor4::blue1(0.0f, 0.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::blue2(0.0f, 0.4f, 1.0f, 1.0f);
+LLColor4 LLColor4::blue3(0.2f, 0.2f, 0.8f, 1.0f);
+LLColor4 LLColor4::blue4(0.0f, 0.0f, 0.6f, 1.0f);
+LLColor4 LLColor4::blue5(0.4f, 0.2f, 1.0f, 1.0f);
+LLColor4 LLColor4::blue6(0.4f, 0.5f, 1.0f, 1.0f);
+
+LLColor4 LLColor4::yellow1(1.0f, 1.0f, 0.0f, 1.0f);
+LLColor4 LLColor4::yellow2(0.6f, 0.6f, 0.0f, 1.0f);
+LLColor4 LLColor4::yellow3(0.8f, 1.0f, 0.2f, 1.0f);
+LLColor4 LLColor4::yellow4(1.0f, 1.0f, 0.4f, 1.0f);
+LLColor4 LLColor4::yellow5(0.6f, 0.4f, 0.2f, 1.0f);
+LLColor4 LLColor4::yellow6(1.0f, 0.8f, 0.4f, 1.0f);
+LLColor4 LLColor4::yellow7(0.8f, 0.8f, 0.0f, 1.0f);
+LLColor4 LLColor4::yellow8(0.8f, 0.8f, 0.2f, 1.0f);
+LLColor4 LLColor4::yellow9(0.8f, 0.8f, 0.4f, 1.0f);
+
+LLColor4 LLColor4::orange1(1.0f, 0.8f, 0.0f, 1.0f);
+LLColor4 LLColor4::orange2(1.0f, 0.6f, 0.0f, 1.0f);
+LLColor4 LLColor4::orange3(1.0f, 0.4f, 0.2f, 1.0f);
+LLColor4 LLColor4::orange4(0.8f, 0.4f, 0.0f, 1.0f);
+LLColor4 LLColor4::orange5(0.9f, 0.5f, 0.0f, 1.0f);
+LLColor4 LLColor4::orange6(1.0f, 0.8f, 0.2f, 1.0f);
+
+LLColor4 LLColor4::magenta1(1.0f, 0.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::magenta2(0.6f, 0.2f, 0.4f, 1.0f);
+LLColor4 LLColor4::magenta3(1.0f, 0.4f, 0.6f, 1.0f);
+LLColor4 LLColor4::magenta4(1.0f, 0.2f, 0.8f, 1.0f);
+
+LLColor4 LLColor4::purple1(0.6f, 0.2f, 0.8f, 1.0f);
+LLColor4 LLColor4::purple2(0.8f, 0.2f, 1.0f, 1.0f);
+LLColor4 LLColor4::purple3(0.6f, 0.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::purple4(0.4f, 0.0f, 0.8f, 1.0f);
+LLColor4 LLColor4::purple5(0.6f, 0.0f, 0.8f, 1.0f);
+LLColor4 LLColor4::purple6(0.8f, 0.0f, 0.6f, 1.0f);
+
+LLColor4 LLColor4::pink1(1.0f, 0.5f, 0.8f, 1.0f);
+LLColor4 LLColor4::pink2(1.0f, 0.8f, 0.9f, 1.0f);
+
+LLColor4 LLColor4::cyan1(0.0f, 1.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::cyan2(0.4f, 0.8f, 0.8f, 1.0f);
+LLColor4 LLColor4::cyan3(0.0f, 1.0f, 0.6f, 1.0f);
+LLColor4 LLColor4::cyan4(0.6f, 1.0f, 1.0f, 1.0f);
+LLColor4 LLColor4::cyan5(0.2f, 0.6f, 1.0f, 1.0f);
+LLColor4 LLColor4::cyan6(0.2f, 0.6f, 0.6f, 1.0f);
+
+//////////////////////////////////////////////////////////////////////////////
+
+// conversion
+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)));
+}
+
+LLColor4::LLColor4(const LLColor3 &vec, F32 a)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = a;
+}
+
+LLColor4::LLColor4(const LLColor4U& color4u)
+{
+ const F32 SCALE = 1.f/255.f;
+ mV[VX] = color4u.mV[VX] * SCALE;
+ mV[VY] = color4u.mV[VY] * SCALE;
+ mV[VZ] = color4u.mV[VZ] * SCALE;
+ mV[VW] = color4u.mV[VW] * SCALE;
+}
+
+const LLColor4& LLColor4::setVec(const LLColor4U& color4u)
+{
+ const F32 SCALE = 1.f/255.f;
+ mV[VX] = color4u.mV[VX] * SCALE;
+ mV[VY] = color4u.mV[VY] * SCALE;
+ mV[VZ] = color4u.mV[VZ] * SCALE;
+ mV[VW] = color4u.mV[VW] * SCALE;
+ return (*this);
+}
+
+const LLColor4& LLColor4::setVec(const LLColor3 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+
+// no change to alpha!
+// mV[VW] = 1.f;
+
+ return (*this);
+}
+
+const LLColor4& LLColor4::setVec(const LLColor3 &vec, F32 a)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = a;
+ return (*this);
+}
+
+const LLColor4& LLColor4::operator=(const LLColor3 &a)
+{
+ mV[VX] = a.mV[VX];
+ mV[VY] = a.mV[VY];
+ mV[VZ] = a.mV[VZ];
+
+// converting from an rgb sets a=1 (opaque)
+ mV[VW] = 1.f;
+ return (*this);
+}
+
+
+std::ostream& operator<<(std::ostream& s, const LLColor4 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }";
+ return s;
+}
+
+bool operator==(const LLColor4 &a, const LLColor3 &b)
+{
+ return ( (a.mV[VX] == b.mV[VX])
+ &&(a.mV[VY] == b.mV[VY])
+ &&(a.mV[VZ] == b.mV[VZ]));
+}
+
+bool operator!=(const LLColor4 &a, const LLColor3 &b)
+{
+ return ( (a.mV[VX] != b.mV[VX])
+ ||(a.mV[VY] != b.mV[VY])
+ ||(a.mV[VZ] != b.mV[VZ]));
+}
+
+LLColor3 vec4to3(const LLColor4 &vec)
+{
+ LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ return temp;
+}
+
+LLColor4 vec3to4(const LLColor3 &vec)
+{
+ LLColor3 temp(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+ return temp;
+}
+
+// static
+BOOL LLColor4::parseColor(const char* buf, LLColor4* color)
+{
+ if( buf == NULL || buf[0] == '\0' || color == NULL)
+ {
+ return FALSE;
+ }
+
+ LLString full_string(buf);
+
+ boost_tokenizer tokens(full_string, boost::char_separator<char>(", "));
+ boost_tokenizer::iterator token_iter = tokens.begin();
+ if (token_iter == tokens.end())
+ {
+ return FALSE;
+ }
+
+ // Grab the first token into a string, since we don't know
+ // if this is a float or a color name.
+ LLString color_name( (*token_iter) );
+ ++token_iter;
+
+ if (token_iter != tokens.end())
+ {
+ // There are more tokens to read. This must be a vector.
+ LLColor4 v;
+ LLString::convertToF32( color_name, v.mV[VX] );
+ LLString::convertToF32( *token_iter, v.mV[VY] );
+ v.mV[VZ] = 0.0f;
+ v.mV[VW] = 1.0f;
+
+ ++token_iter;
+ if (token_iter == tokens.end())
+ {
+ // This is a malformed vector.
+ llwarns << "LLColor4::parseColor() malformed color " << full_string << llendl;
+ }
+ else
+ {
+ // There is a z-component.
+ LLString::convertToF32( *token_iter, v.mV[VZ] );
+
+ ++token_iter;
+ if (token_iter != tokens.end())
+ {
+ // There is an alpha component.
+ LLString::convertToF32( *token_iter, v.mV[VW] );
+ }
+ }
+
+ // Make sure all values are between 0 and 1.
+ if (v.mV[VX] > 1.f || v.mV[VY] > 1.f || v.mV[VZ] > 1.f || v.mV[VW] > 1.f)
+ {
+ v = v * (1.f / 255.f);
+ }
+ color->setVec( v );
+ }
+ else // Single value. Read as a named color.
+ {
+ // We have a color name
+ if ( "red" == color_name )
+ {
+ color->setVec(LLColor4::red);
+ }
+ else if ( "red1" == color_name )
+ {
+ color->setVec(LLColor4::red1);
+ }
+ else if ( "red2" == color_name )
+ {
+ color->setVec(LLColor4::red2);
+ }
+ else if ( "red3" == color_name )
+ {
+ color->setVec(LLColor4::red3);
+ }
+ else if ( "red4" == color_name )
+ {
+ color->setVec(LLColor4::red4);
+ }
+ else if ( "red5" == color_name )
+ {
+ color->setVec(LLColor4::red5);
+ }
+ else if( "green" == color_name )
+ {
+ color->setVec(LLColor4::green);
+ }
+ else if( "green1" == color_name )
+ {
+ color->setVec(LLColor4::green1);
+ }
+ else if( "green2" == color_name )
+ {
+ color->setVec(LLColor4::green2);
+ }
+ else if( "green3" == color_name )
+ {
+ color->setVec(LLColor4::green3);
+ }
+ else if( "green4" == color_name )
+ {
+ color->setVec(LLColor4::green4);
+ }
+ else if( "green5" == color_name )
+ {
+ color->setVec(LLColor4::green5);
+ }
+ else if( "green6" == color_name )
+ {
+ color->setVec(LLColor4::green6);
+ }
+ else if( "blue" == color_name )
+ {
+ color->setVec(LLColor4::blue);
+ }
+ else if( "blue1" == color_name )
+ {
+ color->setVec(LLColor4::blue1);
+ }
+ else if( "blue2" == color_name )
+ {
+ color->setVec(LLColor4::blue2);
+ }
+ else if( "blue3" == color_name )
+ {
+ color->setVec(LLColor4::blue3);
+ }
+ else if( "blue4" == color_name )
+ {
+ color->setVec(LLColor4::blue4);
+ }
+ else if( "blue5" == color_name )
+ {
+ color->setVec(LLColor4::blue5);
+ }
+ else if( "blue6" == color_name )
+ {
+ color->setVec(LLColor4::blue6);
+ }
+ else if( "black" == color_name )
+ {
+ color->setVec(LLColor4::black);
+ }
+ else if( "white" == color_name )
+ {
+ color->setVec(LLColor4::white);
+ }
+ else if( "yellow" == color_name )
+ {
+ color->setVec(LLColor4::yellow);
+ }
+ else if( "yellow1" == color_name )
+ {
+ color->setVec(LLColor4::yellow1);
+ }
+ else if( "yellow2" == color_name )
+ {
+ color->setVec(LLColor4::yellow2);
+ }
+ else if( "yellow3" == color_name )
+ {
+ color->setVec(LLColor4::yellow3);
+ }
+ else if( "yellow4" == color_name )
+ {
+ color->setVec(LLColor4::yellow4);
+ }
+ else if( "yellow5" == color_name )
+ {
+ color->setVec(LLColor4::yellow5);
+ }
+ else if( "yellow6" == color_name )
+ {
+ color->setVec(LLColor4::yellow6);
+ }
+ else if( "magenta" == color_name )
+ {
+ color->setVec(LLColor4::magenta);
+ }
+ else if( "magenta1" == color_name )
+ {
+ color->setVec(LLColor4::magenta1);
+ }
+ else if( "magenta2" == color_name )
+ {
+ color->setVec(LLColor4::magenta2);
+ }
+ else if( "magenta3" == color_name )
+ {
+ color->setVec(LLColor4::magenta3);
+ }
+ else if( "magenta4" == color_name )
+ {
+ color->setVec(LLColor4::magenta4);
+ }
+ else if( "purple" == color_name )
+ {
+ color->setVec(LLColor4::purple);
+ }
+ else if( "purple1" == color_name )
+ {
+ color->setVec(LLColor4::purple1);
+ }
+ else if( "purple2" == color_name )
+ {
+ color->setVec(LLColor4::purple2);
+ }
+ else if( "purple3" == color_name )
+ {
+ color->setVec(LLColor4::purple3);
+ }
+ else if( "purple4" == color_name )
+ {
+ color->setVec(LLColor4::purple4);
+ }
+ else if( "purple5" == color_name )
+ {
+ color->setVec(LLColor4::purple5);
+ }
+ else if( "purple6" == color_name )
+ {
+ color->setVec(LLColor4::purple6);
+ }
+ else if( "pink" == color_name )
+ {
+ color->setVec(LLColor4::pink);
+ }
+ else if( "pink1" == color_name )
+ {
+ color->setVec(LLColor4::pink1);
+ }
+ else if( "pink2" == color_name )
+ {
+ color->setVec(LLColor4::pink2);
+ }
+ else if( "cyan" == color_name )
+ {
+ color->setVec(LLColor4::cyan);
+ }
+ else if( "cyan1" == color_name )
+ {
+ color->setVec(LLColor4::cyan1);
+ }
+ else if( "cyan2" == color_name )
+ {
+ color->setVec(LLColor4::cyan2);
+ }
+ else if( "cyan3" == color_name )
+ {
+ color->setVec(LLColor4::cyan3);
+ }
+ else if( "cyan4" == color_name )
+ {
+ color->setVec(LLColor4::cyan4);
+ }
+ else if( "cyan5" == color_name )
+ {
+ color->setVec(LLColor4::cyan5);
+ }
+ else if( "cyan6" == color_name )
+ {
+ color->setVec(LLColor4::cyan6);
+ }
+ else if( "smoke" == color_name )
+ {
+ color->setVec(LLColor4::smoke);
+ }
+ else if( "grey" == color_name )
+ {
+ color->setVec(LLColor4::grey);
+ }
+ else if( "grey1" == color_name )
+ {
+ color->setVec(LLColor4::grey1);
+ }
+ else if( "grey2" == color_name )
+ {
+ color->setVec(LLColor4::grey2);
+ }
+ else if( "grey3" == color_name )
+ {
+ color->setVec(LLColor4::grey3);
+ }
+ else if( "grey4" == color_name )
+ {
+ color->setVec(LLColor4::grey4);
+ }
+ else if( "orange" == color_name )
+ {
+ color->setVec(LLColor4::orange);
+ }
+ else if( "orange1" == color_name )
+ {
+ color->setVec(LLColor4::orange1);
+ }
+ else if( "orange2" == color_name )
+ {
+ color->setVec(LLColor4::orange2);
+ }
+ else if( "orange3" == color_name )
+ {
+ color->setVec(LLColor4::orange3);
+ }
+ else if( "orange4" == color_name )
+ {
+ color->setVec(LLColor4::orange4);
+ }
+ else if( "orange5" == color_name )
+ {
+ color->setVec(LLColor4::orange5);
+ }
+ else if( "orange6" == color_name )
+ {
+ color->setVec(LLColor4::orange6);
+ }
+ else if ( "clear" == color_name )
+ {
+ color->setVec(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ llwarns << "invalid color " << color_name << llendl;
+ }
+ }
+
+ return TRUE;
+}
+
+// static
+BOOL LLColor4::parseColor4(const char* buf, LLColor4* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ LLColor4 v;
+ S32 count = sscanf( buf, "%f, %f, %f, %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 );
+ if (1 == count )
+ {
+ // try this format
+ count = sscanf( buf, "%f %f %f %f", v.mV + 0, v.mV + 1, v.mV + 2, v.mV + 3 );
+ }
+ if( 4 == count )
+ {
+ value->setVec( v );
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// EOF
diff --git a/indra/llmath/v4color.h b/indra/llmath/v4color.h
new file mode 100644
index 0000000000..8fe5846e26
--- /dev/null
+++ b/indra/llmath/v4color.h
@@ -0,0 +1,539 @@
+/**
+ * @file v4color.h
+ * @brief LLColor4 class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V4COLOR_H
+#define LL_V4COLOR_H
+
+#include "llerror.h"
+//#include "vmath.h"
+#include "llmath.h"
+#include "llsd.h"
+
+class LLColor3;
+class LLColor4U;
+
+// LLColor4 = |x y z w|
+
+static const U32 LENGTHOFCOLOR4 = 4;
+
+static const U32 MAX_LENGTH_OF_COLOR_NAME = 15; //Give plenty of room for additional colors...
+
+class LLColor4
+{
+ public:
+ F32 mV[LENGTHOFCOLOR4];
+ LLColor4(); // Initializes LLColor4 to (0, 0, 0, 1)
+ LLColor4(F32 r, F32 g, F32 b); // Initializes LLColor4 to (r, g, b, 1)
+ LLColor4(F32 r, F32 g, F32 b, F32 a); // Initializes LLColor4 to (r. g, b, a)
+ LLColor4(U32 clr); // Initializes LLColor4 to (r=clr>>24, etc))
+ LLColor4(const F32 *vec); // Initializes LLColor4 to (vec[0]. vec[1], vec[2], 1)
+ LLColor4(const LLColor3 &vec, F32 a = 1.f); // Initializes LLColor4 to (vec, a)
+ LLColor4(const LLSD& sd);
+ explicit LLColor4(const LLColor4U& color4u); // "explicit" to avoid automatic conversion
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mV[0];
+ ret[1] = mV[1];
+ ret[2] = mV[2];
+ ret[3] = mV[3];
+ return ret;
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+ mV[3] = (F32) sd[3].asReal();
+ }
+
+ const LLColor4& setToBlack(); // zero LLColor4 to (0, 0, 0, 1)
+ const LLColor4& setToWhite(); // zero LLColor4 to (0, 0, 0, 1)
+
+ const LLColor4& setVec(F32 r, F32 g, F32 b, F32 a); // Sets LLColor4 to (r, g, b, a)
+ const LLColor4& setVec(F32 r, F32 g, F32 b); // Sets LLColor4 to (r, g, b) (no change in a)
+ const LLColor4& setVec(const LLColor4 &vec); // Sets LLColor4 to vec
+ const LLColor4& setVec(const LLColor3 &vec); // Sets LLColor4 to LLColor3 vec (no change in alpha)
+ const LLColor4& setVec(const LLColor3 &vec, F32 a); // Sets LLColor4 to LLColor3 vec, with alpha specified
+ const LLColor4& setVec(const F32 *vec); // Sets LLColor4 to vec
+ const LLColor4& setVec(const LLColor4U& color4u); // Sets LLColor4 to color4u, rescaled.
+
+
+ const LLColor4& setAlpha(F32 a);
+
+ F32 magVec() const; // Returns magnitude of LLColor4
+ F32 magVecSquared() const; // Returns magnitude squared of LLColor4
+ F32 normVec(); // Normalizes and returns the magnitude of LLColor4
+ const BOOL isOpaque() { return mV[VALPHA] == 1.f; }
+
+ F32 operator[](int idx) const { return mV[idx]; }
+ F32 &operator[](int idx) { return mV[idx]; }
+
+ const LLColor4& operator=(const LLColor3 &a); // Assigns vec3 to vec4 and returns vec4
+ const LLColor4& operator=(const LLSD& sd);
+
+ friend std::ostream& operator<<(std::ostream& s, const LLColor4 &a); // Print a
+ friend LLColor4 operator+(const LLColor4 &a, const LLColor4 &b); // Return vector a + b
+ friend LLColor4 operator-(const LLColor4 &a, const LLColor4 &b); // Return vector a minus b
+ friend LLColor4 operator*(const LLColor4 &a, const LLColor4 &b); // Return a * b
+ friend LLColor4 operator*(const LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change)
+ friend LLColor4 operator*(F32 k, const LLColor4 &a); // Return rgb times scaler k (no alpha change)
+ friend LLColor4 operator%(const LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change)
+ friend LLColor4 operator%(F32 k, const LLColor4 &a); // Return alpha times scaler k (no rgb change)
+ friend bool operator==(const LLColor4 &a, const LLColor4 &b); // Return a == b
+ friend bool operator!=(const LLColor4 &a, const LLColor4 &b); // Return a != b
+
+ friend bool operator==(const LLColor4 &a, const LLColor3 &b); // Return a == b
+ friend bool operator!=(const LLColor4 &a, const LLColor3 &b); // Return a != b
+
+ friend const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b); // Return vector a + b
+ friend const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b); // Return vector a minus b
+ friend const LLColor4& operator*=(LLColor4 &a, F32 k); // Return rgb times scaler k (no alpha change)
+ friend const LLColor4& operator%=(LLColor4 &a, F32 k); // Return alpha times scaler k (no rgb change)
+
+ friend const LLColor4& operator*=(LLColor4 &a, const LLColor4 &b); // Doesn't multiply alpha! (for lighting)
+
+ // conversion
+ operator const LLColor4U() const;
+
+ // Basic color values.
+ static LLColor4 red;
+ static LLColor4 green;
+ static LLColor4 blue;
+ static LLColor4 black;
+ static LLColor4 white;
+ static LLColor4 yellow;
+ static LLColor4 magenta;
+ static LLColor4 cyan;
+ static LLColor4 smoke;
+ static LLColor4 grey;
+ static LLColor4 orange;
+ static LLColor4 purple;
+ static LLColor4 pink;
+ static LLColor4 transparent;
+
+ // Extra color values.
+ static LLColor4 grey1;
+ static LLColor4 grey2;
+ static LLColor4 grey3;
+ static LLColor4 grey4;
+
+ static LLColor4 red1;
+ static LLColor4 red2;
+ static LLColor4 red3;
+ static LLColor4 red4;
+ static LLColor4 red5;
+
+ static LLColor4 green1;
+ static LLColor4 green2;
+ static LLColor4 green3;
+ static LLColor4 green4;
+ static LLColor4 green5;
+ static LLColor4 green6;
+
+ static LLColor4 blue1;
+ static LLColor4 blue2;
+ static LLColor4 blue3;
+ static LLColor4 blue4;
+ static LLColor4 blue5;
+ static LLColor4 blue6;
+
+ static LLColor4 yellow1;
+ static LLColor4 yellow2;
+ static LLColor4 yellow3;
+ static LLColor4 yellow4;
+ static LLColor4 yellow5;
+ static LLColor4 yellow6;
+ static LLColor4 yellow7;
+ static LLColor4 yellow8;
+ static LLColor4 yellow9;
+
+ static LLColor4 orange1;
+ static LLColor4 orange2;
+ static LLColor4 orange3;
+ static LLColor4 orange4;
+ static LLColor4 orange5;
+ static LLColor4 orange6;
+
+ static LLColor4 magenta1;
+ static LLColor4 magenta2;
+ static LLColor4 magenta3;
+ static LLColor4 magenta4;
+
+ static LLColor4 purple1;
+ static LLColor4 purple2;
+ static LLColor4 purple3;
+ static LLColor4 purple4;
+ static LLColor4 purple5;
+ static LLColor4 purple6;
+
+ static LLColor4 pink1;
+ static LLColor4 pink2;
+
+ static LLColor4 cyan1;
+ static LLColor4 cyan2;
+ static LLColor4 cyan3;
+ static LLColor4 cyan4;
+ static LLColor4 cyan5;
+ static LLColor4 cyan6;
+
+ static BOOL parseColor(const char* buf, LLColor4* color);
+ static BOOL parseColor4(const char* buf, LLColor4* color);
+
+ inline void clamp();
+};
+
+
+// Non-member functions
+F32 distVec(const LLColor4 &a, const LLColor4 &b); // Returns distance between a and b
+F32 distVec_squared(const LLColor4 &a, const LLColor4 &b); // Returns distance squared between a and b
+LLColor3 vec4to3(const LLColor4 &vec);
+LLColor4 vec3to4(const LLColor3 &vec);
+LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u);
+
+inline LLColor4::LLColor4(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 1.f;
+}
+
+inline LLColor4::LLColor4(const LLSD& sd)
+{
+ *this = sd;
+}
+
+inline LLColor4::LLColor4(F32 r, F32 g, F32 b)
+{
+ mV[VX] = r;
+ mV[VY] = g;
+ mV[VZ] = b;
+ mV[VW] = 1.f;
+}
+
+inline LLColor4::LLColor4(F32 r, F32 g, F32 b, F32 a)
+{
+ mV[VX] = r;
+ mV[VY] = g;
+ mV[VZ] = b;
+ mV[VW] = a;
+}
+
+inline LLColor4::LLColor4(U32 clr)
+{
+ mV[VX] = (clr&0xff) * (1.0f/255.0f);
+ mV[VY] = ((clr>>8)&0xff) * (1.0f/255.0f);
+ mV[VZ] = ((clr>>16)&0xff) * (1.0f/255.0f);
+ mV[VW] = (clr>>24) * (1.0f/255.0f);
+}
+
+inline LLColor4::LLColor4(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+}
+
+inline const LLColor4& LLColor4::setToBlack(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 1.f;
+ return (*this);
+}
+
+inline const LLColor4& LLColor4::setToWhite(void)
+{
+ mV[VX] = 1.f;
+ mV[VY] = 1.f;
+ mV[VZ] = 1.f;
+ mV[VW] = 1.f;
+ return (*this);
+}
+
+inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+
+// no change to alpha!
+// mV[VW] = 1.f;
+
+ return (*this);
+}
+
+inline const LLColor4& LLColor4::setVec(F32 x, F32 y, F32 z, F32 a)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = a;
+ return (*this);
+}
+
+inline const LLColor4& LLColor4::setVec(const LLColor4 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = vec.mV[VW];
+ return (*this);
+}
+
+
+inline const LLColor4& LLColor4::setVec(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+ return (*this);
+}
+
+inline const LLColor4& LLColor4::setAlpha(F32 a)
+{
+ mV[VW] = a;
+ return (*this);
+}
+
+// LLColor4 Magnitude and Normalization Functions
+
+inline F32 LLColor4::magVec(void) const
+{
+ return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+}
+
+inline F32 LLColor4::magVecSquared(void) const
+{
+ return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
+}
+
+inline F32 LLColor4::normVec(void)
+{
+ F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+ F32 oomag;
+
+ if (mag)
+ {
+ oomag = 1.f/mag;
+ mV[VX] *= oomag;
+ mV[VY] *= oomag;
+ mV[VZ] *= oomag;
+ }
+ return (mag);
+}
+
+// LLColor4 Operators
+
+
+inline LLColor4 operator+(const LLColor4 &a, const LLColor4 &b)
+{
+ return LLColor4(
+ a.mV[VX] + b.mV[VX],
+ a.mV[VY] + b.mV[VY],
+ a.mV[VZ] + b.mV[VZ],
+ a.mV[VW] + b.mV[VW]);
+}
+
+inline LLColor4 operator-(const LLColor4 &a, const LLColor4 &b)
+{
+ return LLColor4(
+ a.mV[VX] - b.mV[VX],
+ a.mV[VY] - b.mV[VY],
+ a.mV[VZ] - b.mV[VZ],
+ a.mV[VW] - b.mV[VW]);
+}
+
+inline LLColor4 operator*(const LLColor4 &a, const LLColor4 &b)
+{
+ return LLColor4(
+ a.mV[VX] * b.mV[VX],
+ a.mV[VY] * b.mV[VY],
+ a.mV[VZ] * b.mV[VZ],
+ a.mV[VW] * b.mV[VW]);
+}
+
+inline LLColor4 operator*(const LLColor4 &a, F32 k)
+{
+ // only affects rgb (not a!)
+ return LLColor4(
+ a.mV[VX] * k,
+ a.mV[VY] * k,
+ a.mV[VZ] * k,
+ a.mV[VW]);
+}
+
+inline LLColor4 operator*(F32 k, const LLColor4 &a)
+{
+ // only affects rgb (not a!)
+ return LLColor4(
+ a.mV[VX] * k,
+ a.mV[VY] * k,
+ a.mV[VZ] * k,
+ a.mV[VW]);
+}
+
+inline LLColor4 operator%(F32 k, const LLColor4 &a)
+{
+ // only affects alpha (not rgb!)
+ return LLColor4(
+ a.mV[VX],
+ a.mV[VY],
+ a.mV[VZ],
+ a.mV[VW] * k);
+}
+
+inline LLColor4 operator%(const LLColor4 &a, F32 k)
+{
+ // only affects alpha (not rgb!)
+ return LLColor4(
+ a.mV[VX],
+ a.mV[VY],
+ a.mV[VZ],
+ a.mV[VW] * k);
+}
+
+inline bool operator==(const LLColor4 &a, const LLColor4 &b)
+{
+ return ( (a.mV[VX] == b.mV[VX])
+ &&(a.mV[VY] == b.mV[VY])
+ &&(a.mV[VZ] == b.mV[VZ])
+ &&(a.mV[VW] == b.mV[VW]));
+}
+
+inline bool operator!=(const LLColor4 &a, const LLColor4 &b)
+{
+ return ( (a.mV[VX] != b.mV[VX])
+ ||(a.mV[VY] != b.mV[VY])
+ ||(a.mV[VZ] != b.mV[VZ])
+ ||(a.mV[VW] != b.mV[VW]));
+}
+
+inline const LLColor4& operator+=(LLColor4 &a, const LLColor4 &b)
+{
+ a.mV[VX] += b.mV[VX];
+ a.mV[VY] += b.mV[VY];
+ a.mV[VZ] += b.mV[VZ];
+ a.mV[VW] += b.mV[VW];
+ return a;
+}
+
+inline const LLColor4& operator-=(LLColor4 &a, const LLColor4 &b)
+{
+ a.mV[VX] -= b.mV[VX];
+ a.mV[VY] -= b.mV[VY];
+ a.mV[VZ] -= b.mV[VZ];
+ a.mV[VW] -= b.mV[VW];
+ return a;
+}
+
+inline const LLColor4& operator*=(LLColor4 &a, F32 k)
+{
+ // only affects rgb (not a!)
+ a.mV[VX] *= k;
+ a.mV[VY] *= k;
+ a.mV[VZ] *= k;
+ return a;
+}
+
+inline const LLColor4& operator *=(LLColor4 &a, const LLColor4 &b)
+{
+ a.mV[VX] *= b.mV[VX];
+ a.mV[VY] *= b.mV[VY];
+ a.mV[VZ] *= b.mV[VZ];
+// a.mV[VW] *= b.mV[VW];
+ return a;
+}
+
+inline const LLColor4& operator%=(LLColor4 &a, F32 k)
+{
+ // only affects alpha (not rgb!)
+ a.mV[VW] *= k;
+ return a;
+}
+
+
+// Non-member functions
+
+inline F32 distVec(const LLColor4 &a, const LLColor4 &b)
+{
+ LLColor4 vec = a - b;
+ return (vec.magVec());
+}
+
+inline F32 distVec_squared(const LLColor4 &a, const LLColor4 &b)
+{
+ LLColor4 vec = a - b;
+ return (vec.magVecSquared());
+}
+
+inline LLColor4 lerp(const LLColor4 &a, const LLColor4 &b, F32 u)
+{
+ return LLColor4(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
+ a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u,
+ a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
+}
+
+
+void LLColor4::clamp()
+{
+ // Clamp the color...
+ if (mV[0] < 0.f)
+ {
+ mV[0] = 0.f;
+ }
+ else if (mV[0] > 1.f)
+ {
+ mV[0] = 1.f;
+ }
+ if (mV[1] < 0.f)
+ {
+ mV[1] = 0.f;
+ }
+ else if (mV[1] > 1.f)
+ {
+ mV[1] = 1.f;
+ }
+ if (mV[2] < 0.f)
+ {
+ mV[2] = 0.f;
+ }
+ else if (mV[2] > 1.f)
+ {
+ mV[2] = 1.f;
+ }
+ if (mV[3] < 0.f)
+ {
+ mV[3] = 0.f;
+ }
+ else if (mV[3] > 1.f)
+ {
+ mV[3] = 1.f;
+ }
+}
+
+inline const LLColor4& LLColor4::operator=(const LLSD& sd)
+{
+ mV[0] = (F32) sd[0].asReal();
+ mV[1] = (F32) sd[1].asReal();
+ mV[2] = (F32) sd[2].asReal();
+ mV[3] = (F32) sd[3].asReal();
+
+ return *this;
+}
+
+#endif
+
diff --git a/indra/llmath/v4coloru.cpp b/indra/llmath/v4coloru.cpp
new file mode 100644
index 0000000000..e5a17e1a97
--- /dev/null
+++ b/indra/llmath/v4coloru.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file v4coloru.cpp
+ * @brief LLColor4U class implementation.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "v3coloru.h"
+#include "v4coloru.h"
+#include "v4color.h"
+//#include "vmath.h"
+#include "llmath.h"
+
+// LLColor4U
+LLColor4U LLColor4U::white(255, 255, 255, 255);
+LLColor4U LLColor4U::black( 0, 0, 0, 255);
+LLColor4U LLColor4U::red (255, 0, 0, 255);
+LLColor4U LLColor4U::green( 0, 255, 0, 255);
+LLColor4U LLColor4U::blue ( 0, 0, 255, 255);
+
+// conversion
+/* inlined to fix gcc compile link error
+LLColor4U::operator LLColor4()
+{
+ return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f));
+}
+*/
+
+// Constructors
+
+
+/*
+LLColor4U::LLColor4U(const LLColor3 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = 255;
+}
+*/
+
+
+// Clear and Assignment Functions
+
+
+
+// LLColor4U Operators
+
+/*
+LLColor4U LLColor4U::operator=(const LLColor3 &a)
+{
+ mV[VX] = a.mV[VX];
+ mV[VY] = a.mV[VY];
+ mV[VZ] = a.mV[VZ];
+
+// converting from an rgb sets a=1 (opaque)
+ mV[VW] = 255;
+ return (*this);
+}
+*/
+
+
+std::ostream& operator<<(std::ostream& s, const LLColor4U &a)
+{
+ s << "{ " << (S32)a.mV[VX] << ", " << (S32)a.mV[VY] << ", " << (S32)a.mV[VZ] << ", " << (S32)a.mV[VW] << " }";
+ return s;
+}
+
+// static
+BOOL LLColor4U::parseColor4U(const char* buf, LLColor4U* value)
+{
+ if( buf == NULL || buf[0] == '\0' || value == NULL)
+ {
+ return FALSE;
+ }
+
+ U32 v[4];
+ S32 count = sscanf( buf, "%u, %u, %u, %u", v + 0, v + 1, v + 2, v + 3 );
+ if (1 == count )
+ {
+ // try this format
+ count = sscanf( buf, "%u %u %u %u", v + 0, v + 1, v + 2, v + 3 );
+ }
+ if( 4 != count )
+ {
+ return FALSE;
+ }
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( v[i] > U8_MAX )
+ {
+ return FALSE;
+ }
+ }
+
+ value->setVec( U8(v[0]), U8(v[1]), U8(v[2]), U8(v[3]) );
+ return TRUE;
+}
diff --git a/indra/llmath/v4coloru.h b/indra/llmath/v4coloru.h
new file mode 100644
index 0000000000..e9cc9b2ab3
--- /dev/null
+++ b/indra/llmath/v4coloru.h
@@ -0,0 +1,501 @@
+/**
+ * @file v4coloru.h
+ * @brief The LLColor4U class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V4COLORU_H
+#define LL_V4COLORU_H
+
+#include "llerror.h"
+//#include "vmath.h"
+#include "llmath.h"
+//#include "v4color.h"
+
+#include "v3color.h"
+#include "v4color.h"
+
+//class LLColor3U;
+class LLColor4;
+
+// LLColor4U = | red green blue alpha |
+
+static const U32 LENGTHOFCOLOR4U = 4;
+
+
+class LLColor4U
+{
+public:
+
+ union
+ {
+ U8 mV[LENGTHOFCOLOR4U];
+ U32 mAll;
+ LLColor4* mSources;
+ LLColor4U* mSourcesU;
+ };
+
+
+ LLColor4U(); // Initializes LLColor4U to (0, 0, 0, 1)
+ LLColor4U(U8 r, U8 g, U8 b); // Initializes LLColor4U to (r, g, b, 1)
+ LLColor4U(U8 r, U8 g, U8 b, U8 a); // Initializes LLColor4U to (r. g, b, a)
+ LLColor4U(const U8 *vec); // Initializes LLColor4U to (vec[0]. vec[1], vec[2], 1)
+ LLColor4U(const LLSD& sd)
+ {
+ setValue(sd);
+ }
+
+ void setValue(const LLSD& sd)
+ {
+ mV[0] = sd[0].asInteger();
+ mV[1] = sd[1].asInteger();
+ mV[2] = sd[2].asInteger();
+ mV[3] = sd[3].asInteger();
+ }
+
+ const LLColor4U& operator=(const LLSD& sd)
+ {
+ setValue(sd);
+ return *this;
+ }
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mV[0];
+ ret[1] = mV[1];
+ ret[2] = mV[2];
+ ret[3] = mV[3];
+ return ret;
+ }
+
+ const LLColor4U& setToBlack(); // zero LLColor4U to (0, 0, 0, 1)
+ const LLColor4U& setToWhite(); // zero LLColor4U to (0, 0, 0, 1)
+
+ const LLColor4U& setVec(U8 r, U8 g, U8 b, U8 a); // Sets LLColor4U to (r, g, b, a)
+ const LLColor4U& setVec(U8 r, U8 g, U8 b); // Sets LLColor4U to (r, g, b) (no change in a)
+ const LLColor4U& setVec(const LLColor4U &vec); // Sets LLColor4U to vec
+ const LLColor4U& setVec(const U8 *vec); // Sets LLColor4U to vec
+
+ const LLColor4U& setAlpha(U8 a);
+
+ F32 magVec() const; // Returns magnitude of LLColor4U
+ F32 magVecSquared() const; // Returns magnitude squared of LLColor4U
+
+ friend std::ostream& operator<<(std::ostream& s, const LLColor4U &a); // Print a
+ friend LLColor4U operator+(const LLColor4U &a, const LLColor4U &b); // Return vector a + b
+ friend LLColor4U operator-(const LLColor4U &a, const LLColor4U &b); // Return vector a minus b
+ friend LLColor4U operator*(const LLColor4U &a, const LLColor4U &b); // Return a * b
+ friend bool operator==(const LLColor4U &a, const LLColor4U &b); // Return a == b
+ friend bool operator!=(const LLColor4U &a, const LLColor4U &b); // Return a != b
+
+ friend const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b); // Return vector a + b
+ friend const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b); // Return vector a minus b
+ friend const LLColor4U& operator*=(LLColor4U &a, U8 k); // Return rgb times scaler k (no alpha change)
+ friend const LLColor4U& operator%=(LLColor4U &a, U8 k); // Return alpha times scaler k (no rgb change)
+
+ LLColor4U addClampMax(const LLColor4U &color); // Add and clamp the max
+
+ LLColor4U multAll(const F32 k); // Multiply ALL channels by scalar k
+ const LLColor4U& combine();
+
+ inline void setVecScaleClamp(const LLColor3 &color);
+ inline void setVecScaleClamp(const LLColor4 &color);
+
+ static BOOL parseColor4U(const char* buf, LLColor4U* value);
+
+ static LLColor4U white;
+ static LLColor4U black;
+ static LLColor4U red;
+ static LLColor4U green;
+ static LLColor4U blue;
+};
+
+
+// Non-member functions
+F32 distVec(const LLColor4U &a, const LLColor4U &b); // Returns distance between a and b
+F32 distVec_squared(const LLColor4U &a, const LLColor4U &b); // Returns distance squared between a and b
+
+
+inline LLColor4U::LLColor4U()
+{
+ mV[VX] = 0;
+ mV[VY] = 0;
+ mV[VZ] = 0;
+ mV[VW] = 255;
+}
+
+inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b)
+{
+ mV[VX] = r;
+ mV[VY] = g;
+ mV[VZ] = b;
+ mV[VW] = 255;
+}
+
+inline LLColor4U::LLColor4U(U8 r, U8 g, U8 b, U8 a)
+{
+ mV[VX] = r;
+ mV[VY] = g;
+ mV[VZ] = b;
+ mV[VW] = a;
+}
+
+inline LLColor4U::LLColor4U(const U8 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+}
+
+/*
+inline LLColor4U::operator LLColor4()
+{
+ return(LLColor4((F32)mV[VRED]/255.f,(F32)mV[VGREEN]/255.f,(F32)mV[VBLUE]/255.f,(F32)mV[VALPHA]/255.f));
+}
+*/
+
+inline const LLColor4U& LLColor4U::setToBlack(void)
+{
+ mV[VX] = 0;
+ mV[VY] = 0;
+ mV[VZ] = 0;
+ mV[VW] = 255;
+ return (*this);
+}
+
+inline const LLColor4U& LLColor4U::setToWhite(void)
+{
+ mV[VX] = 255;
+ mV[VY] = 255;
+ mV[VZ] = 255;
+ mV[VW] = 255;
+ return (*this);
+}
+
+inline const LLColor4U& LLColor4U::setVec(const U8 x, const U8 y, const U8 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+
+// no change to alpha!
+// mV[VW] = 255;
+
+ return (*this);
+}
+
+inline const LLColor4U& LLColor4U::setVec(const U8 r, const U8 g, const U8 b, U8 a)
+{
+ mV[0] = r;
+ mV[1] = g;
+ mV[2] = b;
+ mV[3] = a;
+ return (*this);
+}
+
+inline const LLColor4U& LLColor4U::setVec(const LLColor4U &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = vec.mV[VW];
+ return (*this);
+}
+
+/*
+inline const LLColor4U& LLColor4U::setVec(const LLColor4 &vec)
+{
+ mV[VX] = (U8) (llmin(1.f, vec.mV[VX]) * 255.f);
+ mV[VY] = (U8) (llmin(1.f, vec.mV[VY]) * 255.f);
+ mV[VZ] = (U8) (llmin(1.f, vec.mV[VZ]) * 255.f);
+ mV[VW] = (U8) (llmin(1.f, vec.mV[VW]) * 255.f);
+ return (*this);
+}
+*/
+
+inline const LLColor4U& LLColor4U::setVec(const U8 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+ return (*this);
+}
+
+inline const LLColor4U& LLColor4U::setAlpha(U8 a)
+{
+ mV[VW] = a;
+ return (*this);
+}
+
+// LLColor4U Magnitude and Normalization Functions
+// bookmark
+
+inline F32 LLColor4U::magVec(void) const
+{
+ return fsqrtf( ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ] );
+}
+
+inline F32 LLColor4U::magVecSquared(void) const
+{
+ return ((F32)mV[VX]) * mV[VX] + ((F32)mV[VY]) * mV[VY] + ((F32)mV[VZ]) * mV[VZ];
+}
+
+inline LLColor4U operator+(const LLColor4U &a, const LLColor4U &b)
+{
+ return LLColor4U(
+ a.mV[VX] + b.mV[VX],
+ a.mV[VY] + b.mV[VY],
+ a.mV[VZ] + b.mV[VZ],
+ a.mV[VW] + b.mV[VW]);
+}
+
+inline LLColor4U operator-(const LLColor4U &a, const LLColor4U &b)
+{
+ return LLColor4U(
+ a.mV[VX] - b.mV[VX],
+ a.mV[VY] - b.mV[VY],
+ a.mV[VZ] - b.mV[VZ],
+ a.mV[VW] - b.mV[VW]);
+}
+
+inline LLColor4U operator*(const LLColor4U &a, const LLColor4U &b)
+{
+ return LLColor4U(
+ a.mV[VX] * b.mV[VX],
+ a.mV[VY] * b.mV[VY],
+ a.mV[VZ] * b.mV[VZ],
+ a.mV[VW] * b.mV[VW]);
+}
+
+inline LLColor4U LLColor4U::addClampMax(const LLColor4U &color)
+{
+ return LLColor4U(llmin((S32)mV[VX] + color.mV[VX], 255),
+ llmin((S32)mV[VY] + color.mV[VY], 255),
+ llmin((S32)mV[VZ] + color.mV[VZ], 255),
+ llmin((S32)mV[VW] + color.mV[VW], 255));
+}
+
+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));
+}
+/*
+inline LLColor4U operator*(const LLColor4U &a, U8 k)
+{
+ // only affects rgb (not a!)
+ return LLColor4U(
+ a.mV[VX] * k,
+ a.mV[VY] * k,
+ a.mV[VZ] * k,
+ a.mV[VW]);
+}
+
+inline LLColor4U operator*(U8 k, const LLColor4U &a)
+{
+ // only affects rgb (not a!)
+ return LLColor4U(
+ a.mV[VX] * k,
+ a.mV[VY] * k,
+ a.mV[VZ] * k,
+ a.mV[VW]);
+}
+
+inline LLColor4U operator%(U8 k, const LLColor4U &a)
+{
+ // only affects alpha (not rgb!)
+ return LLColor4U(
+ a.mV[VX],
+ a.mV[VY],
+ a.mV[VZ],
+ a.mV[VW] * k );
+}
+
+inline LLColor4U operator%(const LLColor4U &a, U8 k)
+{
+ // only affects alpha (not rgb!)
+ return LLColor4U(
+ a.mV[VX],
+ a.mV[VY],
+ a.mV[VZ],
+ a.mV[VW] * k );
+}
+*/
+
+inline bool operator==(const LLColor4U &a, const LLColor4U &b)
+{
+ return ( (a.mV[VX] == b.mV[VX])
+ &&(a.mV[VY] == b.mV[VY])
+ &&(a.mV[VZ] == b.mV[VZ])
+ &&(a.mV[VW] == b.mV[VW]));
+}
+
+inline bool operator!=(const LLColor4U &a, const LLColor4U &b)
+{
+ return ( (a.mV[VX] != b.mV[VX])
+ ||(a.mV[VY] != b.mV[VY])
+ ||(a.mV[VZ] != b.mV[VZ])
+ ||(a.mV[VW] != b.mV[VW]));
+}
+
+inline const LLColor4U& operator+=(LLColor4U &a, const LLColor4U &b)
+{
+ a.mV[VX] += b.mV[VX];
+ a.mV[VY] += b.mV[VY];
+ a.mV[VZ] += b.mV[VZ];
+ a.mV[VW] += b.mV[VW];
+ return a;
+}
+
+inline const LLColor4U& operator-=(LLColor4U &a, const LLColor4U &b)
+{
+ a.mV[VX] -= b.mV[VX];
+ a.mV[VY] -= b.mV[VY];
+ a.mV[VZ] -= b.mV[VZ];
+ a.mV[VW] -= b.mV[VW];
+ return a;
+}
+
+inline const LLColor4U& operator*=(LLColor4U &a, U8 k)
+{
+ // only affects rgb (not a!)
+ a.mV[VX] *= k;
+ a.mV[VY] *= k;
+ a.mV[VZ] *= k;
+ return a;
+}
+
+inline const LLColor4U& operator%=(LLColor4U &a, U8 k)
+{
+ // only affects alpha (not rgb!)
+ a.mV[VW] *= k;
+ return a;
+}
+
+inline F32 distVec(const LLColor4U &a, const LLColor4U &b)
+{
+ LLColor4U vec = a - b;
+ return (vec.magVec());
+}
+
+inline F32 distVec_squared(const LLColor4U &a, const LLColor4U &b)
+{
+ LLColor4U vec = a - b;
+ return (vec.magVecSquared());
+}
+
+void LLColor4U::setVecScaleClamp(const LLColor4& color)
+{
+ F32 color_scale_factor = 255.f;
+ F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
+ if (max_color > 1.f)
+ {
+ color_scale_factor /= max_color;
+ }
+ const S32 MAX_COLOR = 255;
+ S32 r = llround(color.mV[0] * color_scale_factor);
+ if (r > MAX_COLOR)
+ {
+ r = MAX_COLOR;
+ }
+ else if (r < 0)
+ {
+ r = 0;
+ }
+ mV[0] = r;
+
+ S32 g = llround(color.mV[1] * color_scale_factor);
+ if (g > MAX_COLOR)
+ {
+ g = MAX_COLOR;
+ }
+ else if (g < 0)
+ {
+ g = 0;
+ }
+ mV[1] = g;
+
+ S32 b = llround(color.mV[2] * color_scale_factor);
+ if (b > MAX_COLOR)
+ {
+ b = MAX_COLOR;
+ }
+ else if (b < 0)
+ {
+ b = 0;
+ }
+ mV[2] = b;
+
+ // Alpha shouldn't be scaled, just clamped...
+ S32 a = llround(color.mV[3] * MAX_COLOR);
+ if (a > MAX_COLOR)
+ {
+ a = MAX_COLOR;
+ }
+ else if (a < 0)
+ {
+ a = 0;
+ }
+ mV[3] = a;
+}
+
+void LLColor4U::setVecScaleClamp(const LLColor3& color)
+{
+ F32 color_scale_factor = 255.f;
+ F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
+ if (max_color > 1.f)
+ {
+ color_scale_factor /= max_color;
+ }
+
+ const S32 MAX_COLOR = 255;
+ S32 r = llround(color.mV[0] * color_scale_factor);
+ if (r > MAX_COLOR)
+ {
+ r = MAX_COLOR;
+ }
+ else
+ if (r < 0)
+ {
+ r = 0;
+ }
+ mV[0] = r;
+
+ S32 g = llround(color.mV[1] * color_scale_factor);
+ if (g > MAX_COLOR)
+ {
+ g = MAX_COLOR;
+ }
+ else
+ if (g < 0)
+ {
+ g = 0;
+ }
+ mV[1] = g;
+
+ S32 b = llround(color.mV[2] * color_scale_factor);
+ if (b > MAX_COLOR)
+ {
+ b = MAX_COLOR;
+ }
+ if (b < 0)
+ {
+ b = 0;
+ }
+ mV[2] = b;
+
+ mV[3] = 255;
+}
+
+
+#endif
+
diff --git a/indra/llmath/v4math.cpp b/indra/llmath/v4math.cpp
new file mode 100644
index 0000000000..16fbdd24a1
--- /dev/null
+++ b/indra/llmath/v4math.cpp
@@ -0,0 +1,124 @@
+/**
+ * @file v4math.cpp
+ * @brief LLVector4 class implementation.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "m3math.h"
+#include "llquaternion.h"
+
+// LLVector4
+
+// Axis-Angle rotations
+
+/*
+const LLVector4& LLVector4::rotVec(F32 angle, const LLVector4 &vec)
+{
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix4(angle, vec);
+ }
+ return *this;
+}
+
+const LLVector4& LLVector4::rotVec(F32 angle, F32 x, F32 y, F32 z)
+{
+ LLVector3 vec(x, y, z);
+ if ( !vec.isExactlyZero() && angle )
+ {
+ *this = *this * LLMatrix4(angle, vec);
+ }
+ return *this;
+}
+*/
+
+const LLVector4& LLVector4::rotVec(const LLMatrix4 &mat)
+{
+ *this = *this * mat;
+ return *this;
+}
+
+const LLVector4& LLVector4::rotVec(const LLQuaternion &q)
+{
+ *this = *this * q;
+ return *this;
+}
+
+const LLVector4& LLVector4::scaleVec(const LLVector4& vec)
+{
+ mV[VX] *= vec.mV[VX];
+ mV[VY] *= vec.mV[VY];
+ mV[VZ] *= vec.mV[VZ];
+ mV[VW] *= vec.mV[VW];
+
+ return *this;
+}
+
+// Sets all values to absolute value of their original values
+// Returns TRUE if data changed
+BOOL LLVector4::abs()
+{
+ BOOL ret = FALSE;
+
+ if (mV[0] < 0.f) { mV[0] = -mV[0]; ret = TRUE; }
+ if (mV[1] < 0.f) { mV[1] = -mV[1]; ret = TRUE; }
+ if (mV[2] < 0.f) { mV[2] = -mV[2]; ret = TRUE; }
+ if (mV[3] < 0.f) { mV[3] = -mV[3]; ret = TRUE; }
+
+ return ret;
+}
+
+
+std::ostream& operator<<(std::ostream& s, const LLVector4 &a)
+{
+ s << "{ " << a.mV[VX] << ", " << a.mV[VY] << ", " << a.mV[VZ] << ", " << a.mV[VW] << " }";
+ return s;
+}
+
+
+// Non-member functions
+
+F32 angle_between( const LLVector4& a, const LLVector4& b )
+{
+ LLVector4 an = a;
+ LLVector4 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 cosine = an * bn;
+ F32 angle = (cosine >= 1.0f) ? 0.0f :
+ (cosine <= -1.0f) ? F_PI :
+ acos(cosine);
+ return angle;
+}
+
+BOOL are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon)
+{
+ LLVector4 an = a;
+ LLVector4 bn = b;
+ an.normVec();
+ bn.normVec();
+ F32 dot = an * bn;
+ if ( (1.0f - fabs(dot)) < epsilon)
+ return TRUE;
+ return FALSE;
+}
+
+
+LLVector3 vec4to3(const LLVector4 &vec)
+{
+ return LLVector3( vec.mV[VX], vec.mV[VY], vec.mV[VZ] );
+}
+
+LLVector4 vec3to4(const LLVector3 &vec)
+{
+ return LLVector4(vec.mV[VX], vec.mV[VY], vec.mV[VZ]);
+}
+
diff --git a/indra/llmath/v4math.h b/indra/llmath/v4math.h
new file mode 100644
index 0000000000..abdc66e0b1
--- /dev/null
+++ b/indra/llmath/v4math.h
@@ -0,0 +1,385 @@
+/**
+ * @file v4math.h
+ * @brief LLVector4 class header file.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_V4MATH_H
+#define LL_V4MATH_H
+
+#include "llerror.h"
+#include "llmath.h"
+#include "v3math.h"
+
+class LLMatrix3;
+class LLMatrix4;
+class LLQuaternion;
+
+// LLVector4 = |x y z w|
+
+static const U32 LENGTHOFVECTOR4 = 4;
+
+#if LL_WINDOWS
+__declspec( align(16) )
+#endif
+
+class LLVector4
+{
+ public:
+ F32 mV[LENGTHOFVECTOR4];
+ LLVector4(); // Initializes LLVector4 to (0, 0, 0, 1)
+ explicit LLVector4(const F32 *vec); // Initializes LLVector4 to (vec[0]. vec[1], vec[2], 1)
+ explicit LLVector4(const LLVector3 &vec); // Initializes LLVector4 to (vec, 1)
+ explicit LLVector4(const LLVector3 &vec, F32 w); // Initializes LLVector4 to (vec, w)
+ LLVector4(F32 x, F32 y, F32 z); // Initializes LLVector4 to (x. y, z, 1)
+ LLVector4(F32 x, F32 y, F32 z, F32 w);
+
+ LLSD getValue() const
+ {
+ LLSD ret;
+ ret[0] = mV[0];
+ ret[1] = mV[1];
+ ret[2] = mV[2];
+ ret[3] = mV[3];
+ return ret;
+ }
+
+ inline BOOL isFinite() const; // checks to see if all values of LLVector3 are finite
+
+ inline void clearVec(); // Clears LLVector4 to (0, 0, 0, 1)
+ inline void zeroVec(); // zero LLVector4 to (0, 0, 0, 0)
+ inline void setVec(F32 x, F32 y, F32 z); // Sets LLVector4 to (x, y, z, 1)
+ inline void setVec(F32 x, F32 y, F32 z, F32 w); // Sets LLVector4 to (x, y, z, w)
+ inline void setVec(const LLVector4 &vec); // Sets LLVector4 to vec
+ inline void setVec(const LLVector3 &vec, F32 w = 1.f); // Sets LLVector4 to LLVector3 vec
+ inline void setVec(const F32 *vec); // Sets LLVector4 to vec
+
+ F32 magVec() const; // Returns magnitude of LLVector4
+ F32 magVecSquared() const; // Returns magnitude squared of LLVector4
+ F32 normVec(); // Normalizes and returns the magnitude of LLVector4
+
+ // Sets all values to absolute value of their original values
+ // Returns TRUE if data changed
+ BOOL abs();
+
+ BOOL isExactlyClear() const { return (mV[VW] == 1.0f) && !mV[VX] && !mV[VY] && !mV[VZ]; }
+ BOOL isExactlyZero() const { return !mV[VW] && !mV[VX] && !mV[VY] && !mV[VZ]; }
+
+ const LLVector4& rotVec(F32 angle, const LLVector4 &vec); // Rotates about vec by angle radians
+ const LLVector4& rotVec(F32 angle, F32 x, F32 y, F32 z); // Rotates about x,y,z by angle radians
+ const LLVector4& rotVec(const LLMatrix4 &mat); // Rotates by MAT4 mat
+ const LLVector4& rotVec(const LLQuaternion &q); // Rotates by QUAT q
+
+ const LLVector4& scaleVec(const LLVector4& vec); // Scales component-wise by vec
+
+ F32 operator[](int idx) const { return mV[idx]; }
+ F32 &operator[](int idx) { return mV[idx]; }
+
+ friend std::ostream& operator<<(std::ostream& s, const LLVector4 &a); // Print a
+ friend LLVector4 operator+(const LLVector4 &a, const LLVector4 &b); // Return vector a + b
+ friend LLVector4 operator-(const LLVector4 &a, const LLVector4 &b); // Return vector a minus b
+ friend F32 operator*(const LLVector4 &a, const LLVector4 &b); // Return a dot b
+ friend LLVector4 operator%(const LLVector4 &a, const LLVector4 &b); // Return a cross b
+ friend LLVector4 operator/(const LLVector4 &a, F32 k); // Return a divided by scaler k
+ friend LLVector4 operator*(const LLVector4 &a, F32 k); // Return a times scaler k
+ friend LLVector4 operator*(F32 k, const LLVector4 &a); // Return a times scaler k
+ friend bool operator==(const LLVector4 &a, const LLVector4 &b); // Return a == b
+ friend bool operator!=(const LLVector4 &a, const LLVector4 &b); // Return a != b
+
+ friend const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b); // Return vector a + b
+ friend const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b); // Return vector a minus b
+ friend const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b); // Return a cross b
+ friend const LLVector4& operator*=(LLVector4 &a, F32 k); // Return a times scaler k
+ friend const LLVector4& operator/=(LLVector4 &a, F32 k); // Return a divided by scaler k
+
+ friend LLVector4 operator-(const LLVector4 &a); // Return vector -a
+}
+#if LL_DARWIN
+__attribute__ ((aligned (16)))
+#endif
+;
+
+
+// Non-member functions
+F32 angle_between(const LLVector4 &a, const LLVector4 &b); // Returns angle (radians) between a and b
+BOOL are_parallel(const LLVector4 &a, const LLVector4 &b, F32 epsilon=F_APPROXIMATELY_ZERO); // Returns TRUE if a and b are very close to parallel
+F32 dist_vec(const LLVector4 &a, const LLVector4 &b); // Returns distance between a and b
+F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b); // Returns distance squared between a and b
+LLVector3 vec4to3(const LLVector4 &vec);
+LLVector4 vec3to4(const LLVector3 &vec);
+LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u); // Returns a vector that is a linear interpolation between a and b
+
+// Constructors
+
+inline LLVector4::LLVector4(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 1.f;
+}
+
+inline LLVector4::LLVector4(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = 1.f;
+}
+
+inline LLVector4::LLVector4(F32 x, F32 y, F32 z, F32 w)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = w;
+}
+
+inline LLVector4::LLVector4(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+}
+
+inline LLVector4::LLVector4(const LLVector3 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = 1.f;
+}
+
+inline LLVector4::LLVector4(const LLVector3 &vec, F32 w)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = w;
+}
+
+
+inline BOOL LLVector4::isFinite() const
+{
+ return (llfinite(mV[VX]) && llfinite(mV[VY]) && llfinite(mV[VZ]) && llfinite(mV[VW]));
+}
+
+// Clear and Assignment Functions
+
+inline void LLVector4::clearVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 1.f;
+}
+
+inline void LLVector4::zeroVec(void)
+{
+ mV[VX] = 0.f;
+ mV[VY] = 0.f;
+ mV[VZ] = 0.f;
+ mV[VW] = 0.f;
+}
+
+inline void LLVector4::setVec(F32 x, F32 y, F32 z)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = 1.f;
+}
+
+inline void LLVector4::setVec(F32 x, F32 y, F32 z, F32 w)
+{
+ mV[VX] = x;
+ mV[VY] = y;
+ mV[VZ] = z;
+ mV[VW] = w;
+}
+
+inline void LLVector4::setVec(const LLVector4 &vec)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = vec.mV[VW];
+}
+
+inline void LLVector4::setVec(const LLVector3 &vec, F32 w)
+{
+ mV[VX] = vec.mV[VX];
+ mV[VY] = vec.mV[VY];
+ mV[VZ] = vec.mV[VZ];
+ mV[VW] = w;
+}
+
+inline void LLVector4::setVec(const F32 *vec)
+{
+ mV[VX] = vec[VX];
+ mV[VY] = vec[VY];
+ mV[VZ] = vec[VZ];
+ mV[VW] = vec[VW];
+}
+
+// LLVector4 Magnitude and Normalization Functions
+
+inline F32 LLVector4::magVec(void) const
+{
+ return fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+}
+
+inline F32 LLVector4::magVecSquared(void) const
+{
+ return mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ];
+}
+
+// LLVector4 Operators
+
+inline LLVector4 operator+(const LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 c(a);
+ return c += b;
+}
+
+inline LLVector4 operator-(const LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 c(a);
+ return c -= b;
+}
+
+inline F32 operator*(const LLVector4 &a, const LLVector4 &b)
+{
+ return (a.mV[VX]*b.mV[VX] + a.mV[VY]*b.mV[VY] + a.mV[VZ]*b.mV[VZ]);
+}
+
+inline LLVector4 operator%(const LLVector4 &a, const LLVector4 &b)
+{
+ return LLVector4(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
+}
+
+inline LLVector4 operator/(const LLVector4 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ return LLVector4( a.mV[VX] * t, a.mV[VY] * t, a.mV[VZ] * t );
+}
+
+
+inline LLVector4 operator*(const LLVector4 &a, F32 k)
+{
+ return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k );
+}
+
+inline LLVector4 operator*(F32 k, const LLVector4 &a)
+{
+ return LLVector4( a.mV[VX] * k, a.mV[VY] * k, a.mV[VZ] * k );
+}
+
+inline bool operator==(const LLVector4 &a, const LLVector4 &b)
+{
+ return ( (a.mV[VX] == b.mV[VX])
+ &&(a.mV[VY] == b.mV[VY])
+ &&(a.mV[VZ] == b.mV[VZ]));
+}
+
+inline bool operator!=(const LLVector4 &a, const LLVector4 &b)
+{
+ return ( (a.mV[VX] != b.mV[VX])
+ ||(a.mV[VY] != b.mV[VY])
+ ||(a.mV[VZ] != b.mV[VZ]));
+}
+
+inline const LLVector4& operator+=(LLVector4 &a, const LLVector4 &b)
+{
+ a.mV[VX] += b.mV[VX];
+ a.mV[VY] += b.mV[VY];
+ a.mV[VZ] += b.mV[VZ];
+ return a;
+}
+
+inline const LLVector4& operator-=(LLVector4 &a, const LLVector4 &b)
+{
+ a.mV[VX] -= b.mV[VX];
+ a.mV[VY] -= b.mV[VY];
+ a.mV[VZ] -= b.mV[VZ];
+ return a;
+}
+
+inline const LLVector4& operator%=(LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 ret(a.mV[VY]*b.mV[VZ] - b.mV[VY]*a.mV[VZ], a.mV[VZ]*b.mV[VX] - b.mV[VZ]*a.mV[VX], a.mV[VX]*b.mV[VY] - b.mV[VX]*a.mV[VY]);
+ a = ret;
+ return a;
+}
+
+inline const LLVector4& operator*=(LLVector4 &a, F32 k)
+{
+ a.mV[VX] *= k;
+ a.mV[VY] *= k;
+ a.mV[VZ] *= k;
+ return a;
+}
+
+inline const LLVector4& operator/=(LLVector4 &a, F32 k)
+{
+ F32 t = 1.f / k;
+ a.mV[VX] *= t;
+ a.mV[VY] *= t;
+ a.mV[VZ] *= t;
+ return a;
+}
+
+inline LLVector4 operator-(const LLVector4 &a)
+{
+ return LLVector4( -a.mV[VX], -a.mV[VY], -a.mV[VZ] );
+}
+
+inline F32 dist_vec(const LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 vec = a - b;
+ return (vec.magVec());
+}
+
+inline F32 dist_vec_squared(const LLVector4 &a, const LLVector4 &b)
+{
+ LLVector4 vec = a - b;
+ return (vec.magVecSquared());
+}
+
+inline LLVector4 lerp(const LLVector4 &a, const LLVector4 &b, F32 u)
+{
+ return LLVector4(
+ a.mV[VX] + (b.mV[VX] - a.mV[VX]) * u,
+ a.mV[VY] + (b.mV[VY] - a.mV[VY]) * u,
+ a.mV[VZ] + (b.mV[VZ] - a.mV[VZ]) * u,
+ a.mV[VW] + (b.mV[VW] - a.mV[VW]) * u);
+}
+
+inline F32 LLVector4::normVec(void)
+{
+ F32 mag = fsqrtf(mV[VX]*mV[VX] + mV[VY]*mV[VY] + mV[VZ]*mV[VZ]);
+ F32 oomag;
+
+ if (mag > FP_MAG_THRESHOLD)
+ {
+ oomag = 1.f/mag;
+ mV[VX] *= oomag;
+ mV[VY] *= oomag;
+ mV[VZ] *= oomag;
+ }
+ else
+ {
+ mV[0] = 0.f;
+ mV[1] = 0.f;
+ mV[2] = 0.f;
+ mag = 0;
+ }
+ return (mag);
+}
+
+
+#endif
+
diff --git a/indra/llmath/xform.cpp b/indra/llmath/xform.cpp
new file mode 100644
index 0000000000..715b993b2b
--- /dev/null
+++ b/indra/llmath/xform.cpp
@@ -0,0 +1,96 @@
+/**
+ * @file xform.cpp
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "xform.h"
+
+LLXform::LLXform()
+{
+ init();
+}
+
+LLXform::~LLXform()
+{
+}
+
+
+LLXform* LLXform::getRoot() const
+{
+ const LLXform* root = this;
+ while(root->mParent)
+ {
+ root = root->mParent;
+ }
+ return (LLXform*)root;
+}
+
+BOOL LLXform::isRoot() const
+{
+ return (!mParent);
+}
+
+BOOL LLXform::isRootEdit() const
+{
+ return (!mParent);
+}
+
+LLXformMatrix::~LLXformMatrix()
+{
+}
+
+void LLXformMatrix::update()
+{
+ if (mParent)
+ {
+ mWorldPosition = mPosition;
+ if (mParent->getScaleChildOffset())
+ {
+ mWorldPosition.scaleVec(mParent->getScale());
+ }
+ mWorldPosition *= mParent->getWorldRotation();
+ mWorldPosition += mParent->getWorldPosition();
+ mWorldRotation = mRotation * mParent->getWorldRotation();
+ }
+ else
+ {
+ mWorldPosition = mPosition;
+ mWorldRotation = mRotation;
+ }
+}
+
+void LLXformMatrix::updateMatrix(BOOL update_bounds)
+{
+ update();
+
+ mWorldMatrix.initAll(mScale, mWorldRotation, mWorldPosition);
+
+ if (update_bounds && (mChanged & MOVED))
+ {
+ mMin.mV[0] = mMax.mV[0] = mWorldMatrix.mMatrix[3][0];
+ mMin.mV[1] = mMax.mV[1] = mWorldMatrix.mMatrix[3][1];
+ mMin.mV[2] = mMax.mV[2] = mWorldMatrix.mMatrix[3][2];
+
+ F32 f0 = (fabs(mWorldMatrix.mMatrix[0][0])+fabs(mWorldMatrix.mMatrix[1][0])+fabs(mWorldMatrix.mMatrix[2][0])) * 0.5f;
+ F32 f1 = (fabs(mWorldMatrix.mMatrix[0][1])+fabs(mWorldMatrix.mMatrix[1][1])+fabs(mWorldMatrix.mMatrix[2][1])) * 0.5f;
+ F32 f2 = (fabs(mWorldMatrix.mMatrix[0][2])+fabs(mWorldMatrix.mMatrix[1][2])+fabs(mWorldMatrix.mMatrix[2][2])) * 0.5f;
+
+ mMin.mV[0] -= f0;
+ mMin.mV[1] -= f1;
+ mMin.mV[2] -= f2;
+
+ mMax.mV[0] += f0;
+ mMax.mV[1] += f1;
+ mMax.mV[2] += f2;
+ }
+}
+
+void LLXformMatrix::getMinMax(LLVector3& min, LLVector3& max) const
+{
+ min = mMin;
+ max = mMax;
+}
diff --git a/indra/llmath/xform.h b/indra/llmath/xform.h
new file mode 100644
index 0000000000..bd4bc74c79
--- /dev/null
+++ b/indra/llmath/xform.h
@@ -0,0 +1,269 @@
+/**
+ * @file xform.h
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_XFORM_H
+#define LL_XFORM_H
+
+#include "v3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+
+#define CHECK_FOR_FINITE
+
+
+const F32 MAX_OBJECT_Z = 768.f;
+const F32 MIN_OBJECT_Z = -256.f;
+const F32 MIN_OBJECT_SCALE = 0.01f;
+const F32 MAX_OBJECT_SCALE = 10.f;
+
+class LLXform
+{
+protected:
+ LLVector3 mPosition;
+ LLQuaternion mRotation;
+ LLVector3 mScale;
+
+ //RN: TODO: move these world transform members to LLXformMatrix
+ // as they are *never* updated or accessed in the base class
+ LLVector3 mWorldPosition;
+ LLQuaternion mWorldRotation;
+
+ LLXform* mParent;
+ U32 mChanged;
+
+ BOOL mScaleChildOffset;
+
+public:
+ typedef enum e_changed_flags
+ {
+ UNCHANGED = 0x00,
+ TRANSLATED = 0x01,
+ ROTATED = 0x02,
+ SCALED = 0x04,
+ SHIFTED = 0x08,
+ GEOMETRY = 0x10,
+ TEXTURE = 0x20,
+ MOVED = TRANSLATED|ROTATED|SCALED,
+ SILHOUETTE = 0x40,
+ ALL_CHANGED = 0x7f
+ }EChangedFlags;
+
+ void init()
+ {
+ mParent = NULL;
+ mChanged = UNCHANGED;
+ mPosition.setVec(0,0,0);
+ mRotation.loadIdentity();
+ mScale. setVec(1,1,1);
+ mWorldPosition.clearVec();
+ mWorldRotation.loadIdentity();
+ mScaleChildOffset = FALSE;
+ }
+
+ LLXform();
+ virtual ~LLXform();
+
+ void getLocalMat4(LLMatrix4 &mat) const { mat.initAll(mScale, mRotation, mPosition); }
+
+ inline BOOL setParent(LLXform *parent);
+
+ inline void setPosition(const LLVector3& pos);
+ inline void setPosition(const F32 x, const F32 y, const F32 z);
+ inline void setPositionX(const F32 x);
+ inline void setPositionY(const F32 y);
+ inline void setPositionZ(const F32 z);
+ inline void addPosition(const LLVector3& pos);
+
+
+ inline void setScale(const LLVector3& scale);
+ inline void setScale(const F32 x, const F32 y, const F32 z);
+ inline void setRotation(const LLQuaternion& rot);
+ inline void setRotation(const F32 x, const F32 y, const F32 z);
+ inline void setRotation(const F32 x, const F32 y, const F32 z, const F32 s);
+
+ void setChanged(const U32 bits) { mChanged |= bits; }
+ BOOL isChanged() const { return mChanged; }
+ BOOL isChanged(const U32 bits) const { return mChanged & bits; }
+ void clearChanged() { mChanged = 0; }
+ void clearChanged(U32 bits) { mChanged &= ~bits; }
+
+ void setScaleChildOffset(BOOL scale) { mScaleChildOffset = scale; }
+ BOOL getScaleChildOffset() { return mScaleChildOffset; }
+
+ LLXform* getParent() const { return mParent; }
+ LLXform* getRoot() const;
+ virtual BOOL isRoot() const;
+ virtual BOOL isRootEdit() const;
+
+ const LLVector3& getPosition() const { return mPosition; }
+ const LLVector3& getScale() const { return mScale; }
+ const LLQuaternion& getRotation() const { return mRotation; }
+ const LLVector3& getPositionW() const { return mWorldPosition; }
+ const LLQuaternion& getWorldRotation() const { return mWorldRotation; }
+ const LLVector3& getWorldPosition() const { return mWorldPosition; }
+};
+
+class LLXformMatrix : public LLXform
+{
+public:
+ LLXformMatrix() : LLXform() {};
+ virtual ~LLXformMatrix();
+
+ const LLMatrix4& getWorldMatrix() const { return mWorldMatrix; }
+ void setWorldMatrix (const LLMatrix4& mat) { mWorldMatrix = mat; }
+
+ void init()
+ {
+ mWorldMatrix.identity();
+ mMin.clearVec();
+ mMax.clearVec();
+
+ LLXform::init();
+ }
+
+ void update();
+ void updateMatrix(BOOL update_bounds = TRUE);
+ void getMinMax(LLVector3& min,LLVector3& max) const;
+
+protected:
+ LLMatrix4 mWorldMatrix;
+ LLVector3 mMin;
+ LLVector3 mMax;
+
+};
+
+BOOL LLXform::setParent(LLXform* parent)
+{
+ // Validate and make sure we're not creating a loop
+ if (parent == mParent)
+ {
+ return TRUE;
+ }
+ if (parent)
+ {
+ LLXform *cur_par = parent->mParent;
+ while (cur_par)
+ {
+ if (cur_par == this)
+ {
+ llwarns << "LLXform::setParent Creating loop when setting parent!" << llendl;
+ return FALSE;
+ }
+ cur_par = cur_par->mParent;
+ }
+ }
+ mParent = parent;
+ return TRUE;
+}
+
+#ifdef CHECK_FOR_FINITE
+void LLXform::setPosition(const LLVector3& pos)
+{
+ setChanged(TRANSLATED);
+ if (pos.isFinite())
+ mPosition = pos;
+ else
+ llerror("Non Finite in LLXform::setPosition(LLVector3)", 0);
+}
+
+void LLXform::setPosition(const F32 x, const F32 y, const F32 z)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ mPosition.setVec(x,y,z);
+ else
+ llerror("Non Finite in LLXform::setPosition(F32,F32,F32)", 0);
+}
+
+void LLXform::setPositionX(const F32 x)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(x))
+ mPosition.mV[VX] = x;
+ else
+ llerror("Non Finite in LLXform::setPositionX", 0);
+}
+
+void LLXform::setPositionY(const F32 y)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(y))
+ mPosition.mV[VY] = y;
+ else
+ llerror("Non Finite in LLXform::setPositionY", 0);
+}
+
+void LLXform::setPositionZ(const F32 z)
+{
+ setChanged(TRANSLATED);
+ if (llfinite(z))
+ mPosition.mV[VZ] = z;
+ else
+ llerror("Non Finite in LLXform::setPositionZ", 0);
+}
+
+void LLXform::addPosition(const LLVector3& pos)
+{
+ setChanged(TRANSLATED);
+ if (pos.isFinite())
+ mPosition += pos;
+ else
+ llerror("Non Finite in LLXform::addPosition", 0);
+}
+
+void LLXform::setScale(const LLVector3& scale)
+{
+ setChanged(SCALED);
+ if (scale.isFinite())
+ mScale = scale;
+ else
+ llerror("Non Finite in LLXform::setScale", 0);
+}
+void LLXform::setScale(const F32 x, const F32 y, const F32 z)
+{
+ setChanged(SCALED);
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ mScale.setVec(x,y,z);
+ else
+ llerror("Non Finite in LLXform::setScale", 0);
+}
+void LLXform::setRotation(const LLQuaternion& rot)
+{
+ setChanged(ROTATED);
+ if (rot.isFinite())
+ mRotation = rot;
+ else
+ llerror("Non Finite in LLXform::setRotation", 0);
+}
+void LLXform::setRotation(const F32 x, const F32 y, const F32 z)
+{
+ setChanged(ROTATED);
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ {
+ mRotation.setQuat(x,y,z);
+ }
+ else
+ {
+ llerror("Non Finite in LLXform::setRotation", 0);
+ }
+}
+void LLXform::setRotation(const F32 x, const F32 y, const F32 z, const F32 s)
+{
+ setChanged(ROTATED);
+ if (llfinite(x) && llfinite(y) && llfinite(z) && llfinite(s))
+ {
+ mRotation.mQ[VX] = x; mRotation.mQ[VY] = y; mRotation.mQ[VZ] = z; mRotation.mQ[VS] = s;
+ }
+ else
+ {
+ llerror("Non Finite in LLXform::setRotation", 0);
+ }
+}
+
+#endif
+
+#endif
diff --git a/indra/llmessage/llassetstorage.cpp b/indra/llmessage/llassetstorage.cpp
new file mode 100644
index 0000000000..b23567288d
--- /dev/null
+++ b/indra/llmessage/llassetstorage.cpp
@@ -0,0 +1,1116 @@
+/**
+ * @file llassetstorage.cpp
+ * @brief Implementation of the base asset storage system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+// system library includes
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <algorithm>
+
+#include "llassetstorage.h"
+
+// linden library includes
+#include "llmath.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llsd.h"
+
+// this library includes
+#include "message.h"
+#include "llxfermanager.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "lldbstrings.h"
+
+#include "lltransfersourceasset.h"
+#include "lltransfertargetvfile.h" // For debugging
+
+LLAssetStorage *gAssetStorage = NULL;
+
+const LLUUID CATEGORIZE_LOST_AND_FOUND_ID("00000000-0000-0000-0000-000000000010");
+
+const F32 LL_ASSET_STORAGE_TIMEOUT = 300.0f; // anything that takes longer than this will abort
+
+
+
+///----------------------------------------------------------------------------
+/// LLAssetInfo
+///----------------------------------------------------------------------------
+
+LLAssetInfo::LLAssetInfo( void )
+: mDescription(),
+ mName(),
+ mUuid(),
+ mCreatorID(),
+ mType( LLAssetType::AT_NONE )
+{ }
+
+LLAssetInfo::LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
+ LLAssetType::EType type, const char* name,
+ const char* desc )
+: mUuid( object_id ),
+ mCreatorID( creator_id ),
+ mType( type )
+{
+ setName( name );
+ setDescription( desc );
+}
+
+LLAssetInfo::LLAssetInfo( const LLNameValue& nv )
+{
+ setFromNameValue( nv );
+}
+
+// make sure the name is short enough, and strip all pipes since they
+// are reserved characters in our inventory tracking system.
+void LLAssetInfo::setName( const std::string& name )
+{
+ if( !name.empty() )
+ {
+ mName.assign( name, 0, llmin((U32)name.size(), (U32)DB_INV_ITEM_NAME_STR_LEN) );
+ mName.erase( std::remove(mName.begin(), mName.end(), '|'),
+ mName.end() );
+ }
+}
+
+// make sure the name is short enough, and strip all pipes since they
+// are reserved characters in our inventory tracking system.
+void LLAssetInfo::setDescription( const std::string& desc )
+{
+ if( !desc.empty() )
+ {
+ mDescription.assign( desc, 0, llmin((U32)desc.size(),
+ (U32)DB_INV_ITEM_DESC_STR_LEN) );
+ mDescription.erase( std::remove(mDescription.begin(),
+ mDescription.end(), '|'),
+ mDescription.end() );
+ }
+}
+
+// Assets (aka potential inventory items) can be applied to an
+// object in the world. We'll store that as a string name value
+// pair where the name encodes part of asset info, and the value
+// the rest. LLAssetInfo objects will be responsible for parsing
+// the meaning out froman LLNameValue object. See the inventory
+// design docs for details. Briefly:
+// name=<inv_type>|<uuid>
+// value=<creatorid>|<name>|<description>|
+void LLAssetInfo::setFromNameValue( const LLNameValue& nv )
+{
+ std::string str;
+ std::string buf;
+ std::string::size_type pos1;
+ std::string::size_type pos2;
+
+ // convert the name to useful information
+ str.assign( nv.mName );
+ pos1 = str.find('|');
+ buf.assign( str, 0, pos1++ );
+ mType = LLAssetType::lookup( buf.c_str() );
+ buf.assign( str, pos1, std::string::npos );
+ mUuid.set( buf.c_str() );
+
+ // convert the value to useful information
+ str.assign( nv.getAsset() );
+ pos1 = str.find('|');
+ buf.assign( str, 0, pos1++ );
+ mCreatorID.set( buf.c_str() );
+ pos2 = str.find( '|', pos1 );
+ buf.assign( str, pos1, (pos2++) - pos1 );
+ setName( buf.c_str() );
+ buf.assign( str, pos2, std::string::npos );
+ setDescription( buf.c_str() );
+ llinfos << "uuid: " << mUuid << llendl;
+ llinfos << "creator: " << mCreatorID << llendl;
+}
+
+///----------------------------------------------------------------------------
+/// LLAssetRequest
+///----------------------------------------------------------------------------
+
+LLAssetRequest::LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType type)
+: mUUID(uuid),
+ mType(type),
+ mDownCallback( NULL ),
+ mUpCallback( NULL ),
+ mInfoCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsLocal(FALSE),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLAssetRequest::~LLAssetRequest()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// LLInvItemRequest
+///----------------------------------------------------------------------------
+
+LLInvItemRequest::LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType type)
+: mUUID(uuid),
+ mType(type),
+ mDownCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLInvItemRequest::~LLInvItemRequest()
+{
+}
+
+///----------------------------------------------------------------------------
+/// LLEstateAssetRequest
+///----------------------------------------------------------------------------
+
+LLEstateAssetRequest::LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType atype,
+ EstateAssetType etype)
+: mUUID(uuid),
+ mAType(atype),
+ mEstateAssetType(etype),
+ mDownCallback( NULL ),
+ mUserData( NULL ),
+ mHost(),
+ mIsTemp( FALSE ),
+ mIsPriority(FALSE),
+ mDataSentInFirstPacket(FALSE),
+ mDataIsInVFS( FALSE )
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ mTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+LLEstateAssetRequest::~LLEstateAssetRequest()
+{
+}
+
+
+///----------------------------------------------------------------------------
+/// LLAssetStorage
+///----------------------------------------------------------------------------
+
+// since many of these functions are called by the messaging and xfer systems,
+// they are declared as static and are passed a "this" handle
+// it's a C/C++ mish-mash!
+
+// TODO: permissions on modifications - maybe don't allow at all?
+// TODO: verify that failures get propogated down
+// TODO: rework tempfile handling?
+
+
+LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer, LLVFS *vfs, const LLHost &upstream_host)
+{
+ _init(msg, xfer, vfs, upstream_host);
+}
+
+
+LLAssetStorage::LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs)
+{
+ _init(msg, xfer, vfs, LLHost::invalid);
+}
+
+
+void LLAssetStorage::_init(LLMessageSystem *msg,
+ LLXferManager *xfer,
+ LLVFS *vfs,
+ const LLHost &upstream_host)
+{
+ mShutDown = FALSE;
+ mMessageSys = msg;
+ mXferManager = xfer;
+ mVFS = vfs;
+
+ setUpstream(upstream_host);
+ msg->setHandlerFuncFast(_PREHASH_AssetUploadComplete, processUploadComplete, (void **)this);
+}
+
+LLAssetStorage::~LLAssetStorage()
+{
+ mShutDown = TRUE;
+
+ _cleanupRequests(TRUE, LL_ERR_CIRCUIT_GONE);
+
+ if (gMessageSystem)
+ {
+ // Warning! This won't work if there's more than one asset storage.
+ // unregister our callbacks with the message system
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AssetUploadComplete, NULL, NULL);
+ }
+}
+
+void LLAssetStorage::setUpstream(const LLHost &upstream_host)
+{
+ llinfos << "AssetStorage: Setting upstream provider to " << upstream_host << llendl;
+
+ mUpstreamHost = upstream_host;
+}
+
+void LLAssetStorage::checkForTimeouts()
+{
+ _cleanupRequests(FALSE, LL_ERR_TCP_TIMEOUT);
+}
+
+void LLAssetStorage::_cleanupRequests(BOOL all, S32 error)
+{
+ const S32 NUM_QUEUES = 3;
+ F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
+
+ std::list<LLAssetRequest*>* requests[NUM_QUEUES];
+ requests[0] = &mPendingDownloads;
+ requests[1] = &mPendingUploads;
+ requests[2] = &mPendingLocalUploads;
+ static const char* REQUEST_TYPE[NUM_QUEUES] = { "download", "upload", "localuploads"};
+
+ std::list<LLAssetRequest*> timed_out;
+
+ for (S32 ii = 0; ii < NUM_QUEUES; ++ii)
+ {
+ for (std::list<LLAssetRequest*>::iterator iter = requests[ii]->begin();
+ iter != requests[ii]->end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ // if all is true, we want to clean up everything
+ // otherwise just check for timed out requests
+ // EXCEPT for upload timeouts
+ if (all
+ || ((0 == ii)
+ && LL_ASSET_STORAGE_TIMEOUT < (mt_secs - tmp->mTime)))
+ {
+ llwarns << "Asset " << REQUEST_TYPE[ii] << " request "
+ << (all ? "aborted" : "timed out") << " for "
+ << tmp->getUUID() << "."
+ << LLAssetType::lookup(tmp->getType()) << llendl;
+
+ timed_out.push_front(tmp);
+ iter = requests[ii]->erase(curiter);
+ }
+ }
+ }
+
+ LLAssetInfo info;
+ for (std::list<LLAssetRequest*>::iterator iter = timed_out.begin();
+ iter != timed_out.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if (tmp->mUpCallback)
+ {
+ tmp->mUpCallback(tmp->getUUID(), tmp->mUserData, error);
+ }
+ if (tmp->mDownCallback)
+ {
+ tmp->mDownCallback(mVFS, tmp->getUUID(), tmp->getType(), tmp->mUserData, error);
+ }
+ if (tmp->mInfoCallback)
+ {
+ tmp->mInfoCallback(&info, tmp->mUserData, error);
+ }
+ delete tmp;
+ }
+
+}
+
+BOOL LLAssetStorage::hasLocalAsset(const LLUUID &uuid, const LLAssetType::EType type)
+{
+ return mVFS->getExists(uuid, type);
+}
+
+///////////////////////////////////////////////////////////////////////////
+// GET routines
+///////////////////////////////////////////////////////////////////////////
+
+// IW - uuid is passed by value to avoid side effects, please don't re-add &
+void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *,S32), void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getAssetData() - " << uuid << "," << LLAssetType::lookup(type) << llendl;
+
+ if (mShutDown)
+ {
+ return; // don't get the asset or do any callbacks, we are shutting down
+ }
+
+ if (uuid.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, uuid, type, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(uuid, type);
+ LLVFile file(mVFS, uuid, type);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << uuid << ":" << type << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ BOOL duplicate = FALSE;
+
+ // check to see if there's a pending download of this uuid already
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
+ iter != mPendingDownloads.end(); ++iter )
+ {
+ LLAssetRequest *tmp = *iter;
+ if ((type == tmp->getType()) && (uuid == tmp->getUUID()))
+ {
+ if (callback == tmp->mDownCallback && user_data == tmp->mUserData)
+ {
+ // this is a duplicate from the same subsystem - throw it away
+ llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
+ return;
+ }
+ else
+ {
+ llinfos << "Adding additional non-duplicate request for UUID " << uuid << llendl;
+ }
+
+ // this is a duplicate request
+ // queue the request, but don't actually ask for it again
+ duplicate = TRUE;
+ break;
+ }
+ }
+
+ // This can be overridden by subclasses
+ _queueDataRequest(uuid, type, callback, user_data, duplicate, is_priority);
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, uuid, type, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+void LLAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType atype,
+ LLGetAssetCallback callback,
+ void *user_data, BOOL duplicate,
+ BOOL is_priority)
+{
+ if (mUpstreamHost.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLAssetRequest *req = new LLAssetRequest(uuid, atype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ mPendingDownloads.push_back(req);
+
+ if (!duplicate)
+ {
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsAsset spa;
+ spa.setAsset(uuid, atype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(uuid, atype);
+ tpvf.setCallback(downloadCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << uuid << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(mUpstreamHost, LLTCT_ASSET);
+ ttcp->requestTransfer(spa, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, uuid, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+}
+
+
+void LLAssetStorage::downloadCompleteCallback(S32 result, void *user_data)
+{
+ LLAssetRequest* req = (LLAssetRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset " << req->getUUID() << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ // find and callback ALL pending requests for this UUID
+ // SJB: We process the callbacks in reverse order, I do not know if this is important,
+ // but I didn't want to mess with it.
+ std::list<LLAssetRequest*> requests;
+ for (std::list<LLAssetRequest*>::iterator iter = gAssetStorage->mPendingDownloads.begin();
+ iter != gAssetStorage->mPendingDownloads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if ((tmp->getUUID() == req->getUUID()) && (tmp->getType()== req->getType()))
+ {
+ requests.push_front(tmp);
+ iter = gAssetStorage->mPendingDownloads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
+ iter != requests.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* tmp = *curiter;
+ if (tmp->mDownCallback)
+ {
+ tmp->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), tmp->mUserData, result);
+ }
+ delete tmp;
+ }
+}
+
+void LLAssetStorage::getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getEstateAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << ", estatetype " << etype << llendl;
+
+ //
+ // Probably will get rid of this early out?
+ //
+ if (asset_id.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(asset_id, atype);
+ LLVFile file(mVFS, asset_id, atype);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ // See whether we should talk to the object's originating sim, or the upstream provider.
+ LLHost source_host;
+ if (object_sim.isOk())
+ {
+ source_host = object_sim;
+ }
+ else
+ {
+ source_host = mUpstreamHost;
+ }
+ if (source_host.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLEstateAssetRequest *req = new LLEstateAssetRequest(asset_id, atype, etype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsEstate spe;
+ spe.setAgentSession(agent_id, session_id);
+ spe.setEstateAssetType(etype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(asset_id, atype);
+ tpvf.setCallback(downloadEstateAssetCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << asset_id << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
+ ttcp->requestTransfer(spe, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+void LLAssetStorage::downloadEstateAssetCompleteCallback(S32 result, void *user_data)
+{
+ LLEstateAssetRequest *req = (LLEstateAssetRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getAType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getAType(), req->mUserData, result);
+}
+
+void LLAssetStorage::getInvItemAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
+ const LLUUID &asset_id, LLAssetType::EType atype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority)
+{
+ lldebugs << "LLAssetStorage::getInvItemAsset() - " << asset_id << "," << LLAssetType::lookup(atype) << llendl;
+
+ //
+ // Probably will get rid of this early out?
+ //
+ if (asset_id.isNull())
+ {
+ // Special case early out for NULL uuid
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE);
+ }
+ return;
+ }
+
+ BOOL exists = mVFS->getExists(asset_id, atype);
+ LLVFile file(mVFS, asset_id, atype);
+ U32 size = exists ? file.getSize() : 0;
+
+ if (size < 1)
+ {
+ if (exists)
+ {
+ llwarns << "Asset vfile " << asset_id << ":" << atype << " found with bad size " << file.getSize() << ", removing" << llendl;
+ file.remove();
+ }
+
+ // See whether we should talk to the object's originating sim, or the upstream provider.
+ LLHost source_host;
+ if (object_sim.isOk())
+ {
+ source_host = object_sim;
+ }
+ else
+ {
+ source_host = mUpstreamHost;
+ }
+ if (source_host.isOk())
+ {
+ // stash the callback info so we can find it after we get the response message
+ LLInvItemRequest *req = new LLInvItemRequest(asset_id, atype);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // send request message to our upstream data provider
+ // Create a new asset transfer.
+ LLTransferSourceParamsInvItem spi;
+ spi.setAgentSession(agent_id, session_id);
+ spi.setInvItem(owner_id, task_id, item_id);
+ spi.setAsset(asset_id, atype);
+
+ // Set our destination file, and the completion callback.
+ LLTransferTargetParamsVFile tpvf;
+ tpvf.setAsset(asset_id, atype);
+ tpvf.setCallback(downloadInvItemCompleteCallback, req);
+
+ llinfos << "Starting transfer for " << asset_id << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(source_host, LLTCT_ASSET);
+ ttcp->requestTransfer(spi, tpvf, 100.f + (is_priority ? 1.f : 0.f));
+ }
+ else
+ {
+ // uh-oh, we shouldn't have gotten here
+ llwarns << "Attempt to move asset data request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+ }
+ else
+ {
+ // we've already got the file
+ // theoretically, partial files w/o a pending request shouldn't happen
+ // unless there's a weird error
+ if (callback)
+ {
+ callback(mVFS, asset_id, atype, user_data, LL_ERR_NOERR);
+ }
+ }
+}
+
+
+void LLAssetStorage::downloadInvItemCompleteCallback(S32 result, void *user_data)
+{
+ LLInvItemRequest *req = (LLInvItemRequest *)user_data;
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::downloadCompleteCallback called without any asset system, aborting!" << llendl;
+ return;
+ }
+
+ if (LL_ERR_NOERR == result)
+ {
+ // we might have gotten a zero-size file
+ LLVFile vfile(gAssetStorage->mVFS, req->getUUID(), req->getType());
+ if (vfile.getSize() <= 0)
+ {
+ llwarns << "downloadCompleteCallback has non-existent or zero-size asset!" << llendl;
+
+ result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ vfile.remove();
+ }
+ }
+
+ req->mDownCallback(gAssetStorage->mVFS, req->getUUID(), req->getType(), req->mUserData, result);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// Store routines
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// virtual
+void LLAssetStorage::cancelStoreAsset(
+ const LLUUID& uuid,
+ LLAssetType::EType atype)
+{
+ bool do_callback = true;
+ LLAssetRequest* req = NULL;
+
+ if(mPendingUploads.size() > 0)
+ {
+ req = mPendingUploads.front();
+ if((req->getUUID() == uuid) && (req->getType() == atype))
+ {
+ // This is probably because the request is in progress - do
+ // not attempt to cancel.
+ do_callback = false;
+ }
+ }
+
+ if (mPendingLocalUploads.size() > 0)
+ {
+ req = mPendingLocalUploads.front();
+ if((req->getUUID() == uuid) && (req->getType() == atype))
+ {
+ // This is probably because the request is in progress - do
+ // not attempt to cancel.
+ do_callback = false;
+ }
+ }
+
+ if (do_callback)
+ {
+ // clear it out of the upload queue if it is there.
+ _callUploadCallbacks(uuid, atype, FALSE);
+ }
+}
+
+// static
+void LLAssetStorage::uploadCompleteCallback(const LLUUID& uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "LLAssetStorage::uploadCompleteCallback has no gAssetStorage!" << llendl;
+ return;
+ }
+ LLAssetRequest *req = (LLAssetRequest *)user_data;
+ BOOL success = TRUE;
+
+ if (result)
+ {
+ llwarns << "LLAssetStorage::uploadCompleteCallback " << result << ":" << getErrorString(result) << " trying to upload file to upstream provider" << llendl;
+ success = FALSE;
+ }
+
+ // we're done grabbing the file, tell the client
+ gAssetStorage->mMessageSys->newMessageFast(_PREHASH_AssetUploadComplete);
+ gAssetStorage->mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
+ gAssetStorage->mMessageSys->addUUIDFast(_PREHASH_UUID, uuid);
+ gAssetStorage->mMessageSys->addS8Fast(_PREHASH_Type, req->getType());
+ gAssetStorage->mMessageSys->addBOOLFast(_PREHASH_Success, success);
+ gAssetStorage->mMessageSys->sendReliable(req->mHost);
+
+ delete req;
+}
+
+void LLAssetStorage::processUploadComplete(LLMessageSystem *msg, void **user_data)
+{
+ LLAssetStorage *this_ptr = (LLAssetStorage *)user_data;
+ LLUUID uuid;
+ S8 asset_type_s8;
+ LLAssetType::EType asset_type;
+ BOOL success = FALSE;
+
+ msg->getUUIDFast(_PREHASH_AssetBlock, _PREHASH_UUID, uuid);
+ msg->getS8Fast(_PREHASH_AssetBlock, _PREHASH_Type, asset_type_s8);
+ msg->getBOOLFast(_PREHASH_AssetBlock, _PREHASH_Success, success);
+
+ asset_type = (LLAssetType::EType)asset_type_s8;
+ this_ptr->_callUploadCallbacks(uuid, asset_type, success);
+}
+
+void LLAssetStorage::_callUploadCallbacks(const LLUUID &uuid, LLAssetType::EType asset_type, BOOL success)
+{
+ // SJB: We process the callbacks in reverse order, I do not know if this is important,
+ // but I didn't want to mess with it.
+ std::list<LLAssetRequest*> requests;
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingUploads.begin();
+ iter != mPendingUploads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if ((req->getUUID() == uuid) && (req->getType() == asset_type))
+ {
+ requests.push_front(req);
+ iter = mPendingUploads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingLocalUploads.begin();
+ iter != mPendingLocalUploads.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if ((req->getUUID() == uuid) && (req->getType() == asset_type))
+ {
+ requests.push_front(req);
+ iter = mPendingLocalUploads.erase(curiter);
+ }
+ }
+ for (std::list<LLAssetRequest*>::iterator iter = requests.begin();
+ iter != requests.end(); )
+ {
+ std::list<LLAssetRequest*>::iterator curiter = iter++;
+ LLAssetRequest* req = *curiter;
+ if (req->mUpCallback)
+ {
+ req->mUpCallback(uuid, req->mUserData, (success ? LL_ERR_NOERR : LL_ERR_ASSET_REQUEST_FAILED ));
+ }
+ delete req;
+ }
+}
+
+
+S32 LLAssetStorage::getNumPendingDownloads() const
+{
+ return mPendingDownloads.size();
+}
+
+S32 LLAssetStorage::getNumPendingUploads() const
+{
+ return mPendingUploads.size();
+}
+
+S32 LLAssetStorage::getNumPendingLocalUploads()
+{
+ return mPendingLocalUploads.size();
+}
+
+LLSD LLAssetStorage::getPendingTypes(const std::list<LLAssetRequest*>& requests) const
+{
+ LLSD type_counts;
+ std::list<LLAssetRequest*>::const_iterator it = requests.begin();
+ std::list<LLAssetRequest*>::const_iterator end = requests.end();
+ for ( ; it != end; ++it)
+ {
+ LLAssetRequest* req = *it;
+
+ const char* type_name = LLAssetType::lookupHumanReadable(req->getType());
+ type_counts[type_name] = type_counts[type_name].asInteger() + 1;
+ }
+ return type_counts;
+}
+
+LLSD LLAssetStorage::getPendingDownloadTypes() const
+{
+ return getPendingTypes(mPendingDownloads);
+}
+
+LLSD LLAssetStorage::getPendingUploadTypes() const
+{
+ return getPendingTypes(mPendingUploads);
+}
+
+// static
+const char* LLAssetStorage::getErrorString(S32 status)
+{
+ switch( status )
+ {
+ case LL_ERR_NOERR:
+ return "No error";
+
+ case LL_ERR_ASSET_REQUEST_FAILED:
+ return "Asset request: failed";
+
+ case LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE:
+ return "Asset request: non-existent file";
+
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ return "Asset request: asset not found in database";
+
+ case LL_ERR_EOF:
+ return "End of file";
+
+ case LL_ERR_CANNOT_OPEN_FILE:
+ return "Cannot open file";
+
+ case LL_ERR_FILE_NOT_FOUND:
+ return "File not found";
+
+ case LL_ERR_TCP_TIMEOUT:
+ return "File transfer timeout";
+
+ case LL_ERR_CIRCUIT_GONE:
+ return "Circuit gone";
+
+ default:
+ return "Unknown status";
+ }
+}
+
+
+
+void LLAssetStorage::getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32), void *user_data, BOOL is_priority)
+{
+ // check for duplicates here, since we're about to fool the normal duplicate checker
+ for (std::list<LLAssetRequest*>::iterator iter = mPendingDownloads.begin();
+ iter != mPendingDownloads.end(); )
+ {
+ LLAssetRequest* tmp = *iter++;
+ if (type == tmp->getType() &&
+ uuid == tmp->getUUID() &&
+ legacyGetDataCallback == tmp->mDownCallback &&
+ callback == ((LLLegacyAssetRequest *)tmp->mUserData)->mDownCallback &&
+ user_data == ((LLLegacyAssetRequest *)tmp->mUserData)->mUserData)
+ {
+ // this is a duplicate from the same subsystem - throw it away
+ llinfos << "Discarding duplicate request for UUID " << uuid << llendl;
+ return;
+ }
+ }
+
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ legacy->mDownCallback = callback;
+ legacy->mUserData = user_data;
+
+ getAssetData(uuid, type, legacyGetDataCallback, (void **)legacy,
+ is_priority);
+}
+
+// static
+void LLAssetStorage::legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 status)
+{
+ LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
+ char filename[LL_MAX_PATH] = ""; /* Flawfinder: ignore */
+
+ if (! status)
+ {
+ LLVFile file(vfs, uuid, type);
+
+ char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */
+
+ uuid.toString(uuid_str);
+ snprintf(filename,sizeof(filename),"%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type)); /* Flawfinder: ignore */
+
+ FILE *fp = LLFile::fopen(filename, "wb"); /* Flawfinder: ignore */
+ if (fp)
+ {
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while (file.read(copy_buf, buf_size))
+ {
+ if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
+ {
+ // return a bad file error if we can't write the whole thing
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ fclose(fp);
+ }
+ else
+ {
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ legacy->mDownCallback(filename, uuid, legacy->mUserData, status);
+ delete legacy;
+}
+
+// this is overridden on the viewer and the sim, so it doesn't really do anything
+// virtual
+void LLAssetStorage::storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file ,
+ bool is_priority,
+ bool store_local,
+ const LLUUID& requesting_agent_id)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// virtual
+// this does nothing, viewer and sim both override this.
+void LLAssetStorage::storeAssetData(
+ const char* filename,
+ const LLTransactionID &transactoin_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llwarns << "storeAssetData: wrong version called" << llendl;
+}
+
+// static
+void LLAssetStorage::legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status)
+{
+ LLLegacyAssetRequest *legacy = (LLLegacyAssetRequest *)user_data;
+ if (legacy && legacy->mUpCallback)
+ {
+ legacy->mUpCallback(uuid, legacy->mUserData, status);
+ }
+ delete legacy;
+}
+
+// virtual
+void LLAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
+{ }
+
+// virtual
+BOOL LLAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
+{ return FALSE; }
+
+// virtual
+std::string LLAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
+{ return std::string(); }
+
+// virtual
+LLUUID LLAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
+{ return LLUUID::null; }
+
+// virtual
+void LLAssetStorage::removeTempAssetData(const LLUUID& asset_id)
+{ }
+
+// virtual
+void LLAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
+{ }
+
+// virtual
+void LLAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
+{ }
+
+// virtual
+void LLAssetStorage::clearTempAssetData()
+{ }
diff --git a/indra/llmessage/llassetstorage.h b/indra/llmessage/llassetstorage.h
new file mode 100644
index 0000000000..4ccbb08abd
--- /dev/null
+++ b/indra/llmessage/llassetstorage.h
@@ -0,0 +1,328 @@
+/**
+ * @file llassetstorage.h
+ * @brief definition of LLAssetStorage class which allows simple
+ * up/downloads of uuid,type asets
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLASSETSTORAGE_H
+#define LL_LLASSETSTORAGE_H
+
+#include <string>
+
+#include "lluuid.h"
+#include "lltimer.h"
+#include "llnamevalue.h"
+#include "llhost.h"
+#include "stdenums.h" // for EDragAndDropType
+#include "lltransfermanager.h" // For LLTSCode enum
+#include "llassettype.h"
+#include "llstring.h"
+
+// Forward declarations
+class LLMessageSystem;
+class LLXferManager;
+class LLAssetStorage;
+class LLVFS;
+class LLSD;
+
+class LLAssetInfo
+{
+protected:
+ std::string mDescription;
+ std::string mName;
+
+public:
+ LLUUID mUuid;
+ LLTransactionID mTransactionID;
+ LLUUID mCreatorID;
+ LLAssetType::EType mType;
+
+ LLAssetInfo( void );
+ LLAssetInfo( const LLUUID& object_id, const LLUUID& creator_id,
+ LLAssetType::EType type, const char* name, const char* desc );
+ LLAssetInfo( const LLNameValue& nv );
+
+ const std::string& getName( void ) const { return mName; }
+ const std::string& getDescription( void ) const { return mDescription; }
+ void setName( const std::string& name );
+ void setDescription( const std::string& desc );
+
+ // Assets (aka potential inventory items) can be applied to an
+ // object in the world. We'll store that as a string name value
+ // pair where the name encodes part of asset info, and the value
+ // the rest. LLAssetInfo objects will be responsible for parsing
+ // the meaning out froman LLNameValue object. See the inventory
+ // design docs for details.
+ void setFromNameValue( const LLNameValue& nv );
+};
+
+
+class LLAssetRequest
+{
+public:
+ LLAssetRequest(const LLUUID &uuid, const LLAssetType::EType at);
+ virtual ~LLAssetRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getType() const { return mType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+ void (*mUpCallback)(const LLUUID&, void *, S32);
+ void (*mInfoCallback)(LLAssetInfo *, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ BOOL mIsLocal;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+ LLUUID mRequestingAgentID; // Only valid for uploads from an agent
+};
+
+
+class LLInvItemRequest
+{
+public:
+ LLInvItemRequest(const LLUUID &uuid, const LLAssetType::EType at);
+ virtual ~LLInvItemRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getType() const { return mType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+
+};
+
+class LLEstateAssetRequest
+{
+public:
+ LLEstateAssetRequest(const LLUUID &uuid, const LLAssetType::EType at, EstateAssetType et);
+ virtual ~LLEstateAssetRequest();
+
+ LLUUID getUUID() const { return mUUID; }
+ LLAssetType::EType getAType() const { return mAType; }
+protected:
+ LLUUID mUUID;
+ LLAssetType::EType mAType;
+ EstateAssetType mEstateAssetType;
+
+public:
+ void (*mDownCallback)(LLVFS*, const LLUUID&, LLAssetType::EType, void *, S32);
+
+ void *mUserData;
+ LLHost mHost;
+ BOOL mIsTemp;
+ F64 mTime; // Message system time
+ BOOL mIsPriority;
+ BOOL mDataSentInFirstPacket;
+ BOOL mDataIsInVFS;
+
+};
+
+
+
+
+typedef void (*LLGetAssetCallback)(LLVFS *vfs, const LLUUID &asset_id,
+ LLAssetType::EType asset_type, void *user_data, S32 status);
+
+class LLAssetStorage
+{
+public:
+ // VFS member is public because static child methods need it :(
+ LLVFS *mVFS;
+ typedef void (*LLStoreAssetCallback)(const LLUUID &asset_id, void *user_data, S32 status);
+
+protected:
+ BOOL mShutDown;
+ LLHost mUpstreamHost;
+
+ LLMessageSystem *mMessageSys;
+ LLXferManager *mXferManager;
+
+ std::list<LLAssetRequest*> mPendingDownloads;
+ std::list<LLAssetRequest*> mPendingUploads;
+ std::list<LLAssetRequest*> mPendingLocalUploads;
+
+public:
+ LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host);
+
+ LLAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs);
+ virtual ~LLAssetStorage();
+
+ void setUpstream(const LLHost &upstream_host);
+
+ virtual BOOL hasLocalAsset(const LLUUID &uuid, LLAssetType::EType type);
+
+ // public interface methods
+ // note that your callback may get called BEFORE the function returns
+
+ virtual void getAssetData(const LLUUID uuid, LLAssetType::EType atype, LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE);
+
+ /*
+ * TransactionID version
+ * Viewer needs the store_local
+ */
+ virtual void storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false);
+
+ /*
+ * AssetID version
+ * Sim needs both store_local and requesting_agent_id.
+ */
+ virtual void storeAssetData(
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false,
+ const LLUUID& requesting_agent_id = LLUUID::null);
+
+ // This call will attempt to clear a store asset. This will only
+ // attempt to cancel an upload that has not yet begun. The
+ // callback will be called with an error code.
+ virtual void cancelStoreAsset(
+ const LLUUID& uuid,
+ LLAssetType::EType oatype);
+
+ virtual void checkForTimeouts();
+
+ void getEstateAsset(const LLHost &object_sim, const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &asset_id, LLAssetType::EType atype, EstateAssetType etype,
+ LLGetAssetCallback callback, void *user_data, BOOL is_priority);
+
+ void getInvItemAsset(const LLHost &object_sim,
+ const LLUUID &agent_id, const LLUUID &session_id,
+ const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id,
+ const LLUUID &asset_id, LLAssetType::EType atype,
+ LLGetAssetCallback cb, void *user_data, BOOL is_priority = FALSE); // Get a particular inventory item.
+
+
+ S32 getNumPendingDownloads() const;
+ S32 getNumPendingUploads() const;
+ S32 getNumPendingLocalUploads();
+
+ // Returns a map from type to num pending, eg 'texture' => 5, 'object' => 10
+ LLSD getPendingDownloadTypes() const;
+ LLSD getPendingUploadTypes() const;
+
+ // download process callbacks
+ static void downloadCompleteCallback(const S32 result, void *user_data);
+ static void downloadEstateAssetCompleteCallback(const S32 result, void *user_data);
+ static void downloadInvItemCompleteCallback(const S32 result, void *user_data);
+
+ // upload process callbacks
+ static void uploadCompleteCallback(const LLUUID&, void *user_data, S32 result);
+ static void processUploadComplete(LLMessageSystem *msg, void **this_handle);
+
+ // debugging
+ static const char* getErrorString( S32 status );
+
+ // deprecated file-based methods
+ void getAssetData(const LLUUID uuid, LLAssetType::EType type, void (*callback)(const char*, const LLUUID&, void *, S32), void *user_data, BOOL is_priority = FALSE);
+
+ /*
+ * AssetID version.
+ */
+ virtual void storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+
+ /*
+ * TransactionID version
+ */
+ virtual void storeAssetData(
+ const char * filename,
+ const LLTransactionID &transaction_id,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void *user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+
+ static void legacyGetDataCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType, void *user_data, S32 status);
+ static void legacyStoreDataCallback(const LLUUID &uuid, void *user_data, S32 status);
+
+ // Temp assets are stored on sim nodes, they have agent ID and location data associated with them.
+ // This is a no-op for non-http asset systems
+ virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name);
+ virtual BOOL hasTempAssetData(const LLUUID& texture_id) const;
+ virtual std::string getTempAssetHostName(const LLUUID& texture_id) const;
+ virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const;
+ virtual void removeTempAssetData(const LLUUID& asset_id);
+ virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id);
+ // Pass LLUUID::null for all
+ virtual void dumpTempAssetData(const LLUUID& avatar_id) const;
+ virtual void clearTempAssetData();
+
+ // add extra methods to handle metadata
+
+protected:
+ void _cleanupRequests(BOOL all, S32 error);
+ void _callUploadCallbacks(const LLUUID &uuid, const LLAssetType::EType asset_type, BOOL success);
+
+ virtual void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate,
+ BOOL is_priority);
+
+private:
+ void _init(LLMessageSystem *msg,
+ LLXferManager *xfer,
+ LLVFS *vfs,
+ const LLHost &upstream_host);
+ LLSD getPendingTypes(const std::list<LLAssetRequest*>& requests) const;
+
+};
+
+////////////////////////////////////////////////////////////////////////
+// Wrappers to replicate deprecated API
+////////////////////////////////////////////////////////////////////////
+
+class LLLegacyAssetRequest
+{
+public:
+ void (*mDownCallback)(const char *, const LLUUID&, void *, S32);
+ LLAssetStorage::LLStoreAssetCallback mUpCallback;
+
+ void *mUserData;
+};
+
+extern LLAssetStorage *gAssetStorage;
+extern const LLUUID CATEGORIZE_LOST_AND_FOUND_ID;
+#endif
diff --git a/indra/llmessage/llbuffer.cpp b/indra/llmessage/llbuffer.cpp
new file mode 100644
index 0000000000..009387598b
--- /dev/null
+++ b/indra/llmessage/llbuffer.cpp
@@ -0,0 +1,746 @@
+/**
+ * @file llbuffer.cpp
+ * @author Phoenix
+ * @date 2005-09-20
+ * @brief Implementation of the segments, buffers, and buffer arrays.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llbuffer.h"
+
+#include "llmath.h"
+#include "llmemtype.h"
+#include "llstl.h"
+
+/**
+ * LLSegment
+ */
+LLSegment::LLSegment() :
+ mChannel(0),
+ mData(NULL),
+ mSize(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLSegment::LLSegment(S32 channel, U8* data, S32 data_len) :
+ mChannel(channel),
+ mData(data),
+ mSize(data_len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLSegment::~LLSegment()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+bool LLSegment::isOnChannel(S32 channel) const
+{
+ return (mChannel == channel);
+}
+
+S32 LLSegment::getChannel() const
+{
+ return mChannel;
+}
+
+void LLSegment::setChannel(S32 channel)
+{
+ mChannel = channel;
+}
+
+
+U8* LLSegment::data() const
+{
+ return mData;
+}
+
+S32 LLSegment::size() const
+{
+ return mSize;
+}
+
+
+/**
+ * LLHeapBuffer
+ */
+LLHeapBuffer::LLHeapBuffer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ const S32 DEFAULT_HEAP_BUFFER_SIZE = 16384;
+ allocate(DEFAULT_HEAP_BUFFER_SIZE);
+}
+
+LLHeapBuffer::LLHeapBuffer(S32 size)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ allocate(size);
+}
+
+LLHeapBuffer::LLHeapBuffer(const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if((len > 0) && src)
+ {
+ allocate(len);
+ if(mBuffer)
+ {
+ memcpy(mBuffer, src, len);
+ }
+ }
+ else
+ {
+ mBuffer = NULL;
+ mSize = 0;
+ mNextFree = NULL;
+ }
+}
+
+// virtual
+LLHeapBuffer::~LLHeapBuffer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ delete[] mBuffer;
+ mBuffer = NULL;
+ mSize = 0;
+ mNextFree = NULL;
+}
+
+// virtual
+//S32 LLHeapBuffer::bytesLeft() const
+//{
+// return (mSize - (mNextFree - mBuffer));
+//}
+
+// virtual
+bool LLHeapBuffer::createSegment(
+ S32 channel,
+ S32 size,
+ LLSegment& segment)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // get actual size of the segment.
+ S32 actual_size = llmin(size, (mSize - S32(mNextFree - mBuffer)));
+
+ // bail if we cannot build a valid segment
+ if(actual_size <= 0)
+ {
+ return false;
+ }
+
+ // Yay, we're done.
+ segment = LLSegment(channel, mNextFree, actual_size);
+ mNextFree += actual_size;
+ return true;
+}
+
+void LLHeapBuffer::allocate(S32 size)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ mBuffer = new U8[size];
+ if(mBuffer)
+ {
+ mSize = size;
+ mNextFree = mBuffer;
+ }
+}
+
+
+/**
+ * LLBufferArray
+ */
+LLBufferArray::LLBufferArray() :
+ mNextBaseChannel(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferArray::~LLBufferArray()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::for_each(mBuffers.begin(), mBuffers.end(), DeletePointer());
+}
+
+// static
+LLChannelDescriptors LLBufferArray::makeChannelConsumer(
+ const LLChannelDescriptors& channels)
+{
+ LLChannelDescriptors rv(channels.out());
+ return rv;
+}
+
+LLChannelDescriptors LLBufferArray::nextChannel()
+{
+ LLChannelDescriptors rv(mNextBaseChannel++);
+ return rv;
+}
+
+bool LLBufferArray::append(S32 channel, const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(mSegments.end(), segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+bool LLBufferArray::prepend(S32 channel, const U8* src, S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(mSegments.begin(), segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+bool LLBufferArray::insertAfter(
+ segment_iterator_t segment,
+ S32 channel,
+ const U8* src,
+ S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::vector<LLSegment> segments;
+ if(mSegments.end() != segment)
+ {
+ ++segment;
+ }
+ if(copyIntoBuffers(channel, src, len, segments))
+ {
+ mSegments.insert(segment, segments.begin(), segments.end());
+ return true;
+ }
+ return false;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::splitAfter(U8* address)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ segment_iterator_t end = mSegments.end();
+ segment_iterator_t it = getSegment(address);
+ if(it == end)
+ {
+ return end;
+ }
+
+ // We have the location and the segment.
+ U8* base = (*it).data();
+ S32 size = (*it).size();
+ if(address == (base + size))
+ {
+ // No need to split, since this is the last byte of the
+ // segment. We do not want to have zero length segments, since
+ // that will only incur processing overhead with no advantage.
+ return it;
+ }
+ S32 channel = (*it).getChannel();
+ LLSegment segment1(channel, base, (address - base) + 1);
+ *it = segment1;
+ segment_iterator_t rv = it;
+ ++it;
+ LLSegment segment2(channel, address + 1, size - (address - base) - 1);
+ mSegments.insert(it, segment2);
+ return rv;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::beginSegment()
+{
+ return mSegments.begin();
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::endSegment()
+{
+ return mSegments.end();
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::constructSegmentAfter(
+ U8* address,
+ LLSegment& segment)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ segment_iterator_t rv = mSegments.begin();
+ segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ if(rv != end)
+ {
+ segment = (*rv);
+ }
+ }
+ else
+ {
+ // we have an address - find the segment it is in.
+ for( ; rv != end; ++rv)
+ {
+ if((address >= (*rv).data())
+ && (address < ((*rv).data() + (*rv).size())))
+ {
+ if((++address) < ((*rv).data() + (*rv).size()))
+ {
+ // it's in this segment - construct an appropriate
+ // sub-segment.
+ segment = LLSegment(
+ (*rv).getChannel(),
+ address,
+ (*rv).size() - (address - (*rv).data()));
+ }
+ else
+ {
+ ++rv;
+ if(rv != end)
+ {
+ segment = (*rv);
+ }
+ }
+ break;
+ }
+ }
+ }
+ if(rv == end)
+ {
+ segment = LLSegment();
+ }
+ return rv;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::getSegment(U8* address)
+{
+ segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ return end;
+ }
+ segment_iterator_t it = mSegments.begin();
+ for( ; it != end; ++it)
+ {
+ if((address >= (*it).data())&&(address < (*it).data() + (*it).size()))
+ {
+ // found it.
+ return it;
+ }
+ }
+ return end;
+}
+
+LLBufferArray::const_segment_iterator_t LLBufferArray::getSegment(
+ U8* address) const
+{
+ const_segment_iterator_t end = mSegments.end();
+ if(!address)
+ {
+ return end;
+ }
+ const_segment_iterator_t it = mSegments.begin();
+ for( ; it != end; ++it)
+ {
+ if((address >= (*it).data())
+ && (address < (*it).data() + (*it).size()))
+ {
+ // found it.
+ return it;
+ }
+ }
+ return end;
+}
+
+/*
+U8* LLBufferArray::getAddressAfter(U8* address)
+{
+ U8* rv = NULL;
+ segment_iterator_t it = getSegment(address);
+ segment_iterator_t end = mSegments.end();
+ if(it != end)
+ {
+ if(++address < ((*it).data() + (*it).size()))
+ {
+ // it's in the same segment
+ rv = address;
+ }
+ else
+ {
+ // it's in the next segment
+ if(++it != end)
+ {
+ rv = (*it).data();
+ }
+ }
+ }
+ return rv;
+}
+*/
+
+S32 LLBufferArray::countAfter(S32 channel, U8* start) const
+{
+ S32 count = 0;
+ S32 offset = 0;
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ if(start)
+ {
+ it = getSegment(start);
+ if(it == end)
+ {
+ return count;
+ }
+ if(++start < ((*it).data() + (*it).size()))
+ {
+ // it's in the same segment
+ offset = start - (*it).data();
+ }
+ else if(++it == end)
+ {
+ // it's in the next segment
+ return count;
+ }
+ }
+ else
+ {
+ it = mSegments.begin();
+ }
+ while(it != end)
+ {
+ if((*it).isOnChannel(channel))
+ {
+ count += (*it).size() - offset;
+ }
+ offset = 0;
+ ++it;
+ }
+ return count;
+}
+
+U8* LLBufferArray::readAfter(
+ S32 channel,
+ U8* start,
+ U8* dest,
+ S32& len) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ U8* rv = start;
+ if(!dest || len <= 0)
+ {
+ return rv;
+ }
+ S32 bytes_left = len;
+ len = 0;
+ S32 bytes_to_copy = 0;
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ if(start)
+ {
+ it = getSegment(start);
+ if(it == end)
+ {
+ return rv;
+ }
+ if((++start < ((*it).data() + (*it).size()))
+ && (*it).isOnChannel(channel))
+ {
+ // copy the data out of this segment
+ S32 bytes_in_segment = (*it).size() - (start - (*it).data());
+ bytes_to_copy = llmin(bytes_left, bytes_in_segment);
+ memcpy(dest, start, bytes_to_copy); /*Flawfinder: ignore*/
+ len += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ rv = start + bytes_to_copy - 1;
+ ++it;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ else
+ {
+ it = mSegments.begin();
+ }
+ while(bytes_left && (it != end))
+ {
+ if(!((*it).isOnChannel(channel)))
+ {
+ ++it;
+ continue;
+ }
+ bytes_to_copy = llmin(bytes_left, (*it).size());
+ memcpy(dest + len, (*it).data(), bytes_to_copy); /*Flawfinder: ignore*/
+ len += bytes_to_copy;
+ bytes_left -= bytes_to_copy;
+ rv = (*it).data() + bytes_to_copy - 1;
+ ++it;
+ }
+ return rv;
+}
+
+U8* LLBufferArray::seek(
+ S32 channel,
+ U8* start,
+ S32 delta) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ const_segment_iterator_t it;
+ const_segment_iterator_t end = mSegments.end();
+ U8* rv = start;
+ if(0 == delta)
+ {
+ if((U8*)npos == start)
+ {
+ // someone is looking for end of data.
+ segment_list_t::const_reverse_iterator rit = mSegments.rbegin();
+ segment_list_t::const_reverse_iterator rend = mSegments.rend();
+ while(rit != rend)
+ {
+ if(!((*rit).isOnChannel(channel)))
+ {
+ ++rit;
+ continue;
+ }
+ rv = (*rit).data() + (*rit).size();
+ break;
+ }
+ }
+ else if(start)
+ {
+ // This is sort of a weird case - check if zero bytes away
+ // from current position is on channel and return start if
+ // that is true. Otherwise, return NULL.
+ it = getSegment(start);
+ if((it == end) || !(*it).isOnChannel(channel))
+ {
+ rv = NULL;
+ }
+ }
+ else
+ {
+ // Start is NULL, so return the very first byte on the
+ // channel, or NULL.
+ it = mSegments.begin();
+ while((it != end) && !(*it).isOnChannel(channel))
+ {
+ ++it;
+ }
+ if(it != end)
+ {
+ rv = (*it).data();
+ }
+ }
+ return rv;
+ }
+ if(start)
+ {
+ it = getSegment(start);
+ if((it != end) && (*it).isOnChannel(channel))
+ {
+ if(delta > 0)
+ {
+ S32 bytes_in_segment = (*it).size() - (start - (*it).data());
+ S32 local_delta = llmin(delta, bytes_in_segment);
+ rv += local_delta;
+ delta -= local_delta;
+ ++it;
+ }
+ else
+ {
+ S32 bytes_in_segment = start - (*it).data();
+ S32 local_delta = llmin(llabs(delta), bytes_in_segment);
+ rv -= local_delta;
+ delta += local_delta;
+ }
+ }
+ }
+ else if(delta < 0)
+ {
+ // start is NULL, and delta indicates seeking backwards -
+ // return NULL.
+ return NULL;
+ }
+ else
+ {
+ // start is NULL and delta > 0
+ it = mSegments.begin();
+ }
+ if(delta > 0)
+ {
+ // At this point, we have an iterator into the segments, and
+ // are seeking forward until delta is zero or we run out
+ while(delta && (it != end))
+ {
+ if(!((*it).isOnChannel(channel)))
+ {
+ ++it;
+ continue;
+ }
+ if(delta <= (*it).size())
+ {
+ // it's in this segment
+ rv = (*it).data() + delta;
+ }
+ delta -= (*it).size();
+ ++it;
+ }
+ if(delta && (it == end))
+ {
+ // Whoops - sought past end.
+ rv = NULL;
+ }
+ }
+ else //if(delta < 0)
+ {
+ // We are at the beginning of a segment, and need to search
+ // backwards.
+ segment_list_t::const_reverse_iterator rit(it);
+ segment_list_t::const_reverse_iterator rend = mSegments.rend();
+ while(delta && (rit != rend))
+ {
+ if(!((*rit).isOnChannel(channel)))
+ {
+ ++rit;
+ continue;
+ }
+ if(llabs(delta) <= (*rit).size())
+ {
+ // it's in this segment.
+ rv = (*rit).data() + (*rit).size() + delta;
+ delta = 0;
+ }
+ else
+ {
+ delta += (*rit).size();
+ }
+ ++rit;
+ }
+ if(delta && (rit == rend))
+ {
+ // sought past the beginning.
+ rv = NULL;
+ }
+ }
+ return rv;
+}
+
+bool LLBufferArray::takeContents(LLBufferArray& source)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ std::copy(
+ source.mBuffers.begin(),
+ source.mBuffers.end(),
+ std::back_insert_iterator<buffer_list_t>(mBuffers));
+ source.mBuffers.clear();
+ std::copy(
+ source.mSegments.begin(),
+ source.mSegments.end(),
+ std::back_insert_iterator<segment_list_t>(mSegments));
+ source.mSegments.clear();
+ source.mNextBaseChannel = 0;
+ return true;
+}
+
+LLBufferArray::segment_iterator_t LLBufferArray::makeSegment(
+ S32 channel,
+ S32 len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // start at the end of the buffers, because it is the most likely
+ // to have free space.
+ LLSegment segment;
+ buffer_list_t::reverse_iterator it = mBuffers.rbegin();
+ buffer_list_t::reverse_iterator end = mBuffers.rend();
+ bool made_segment = false;
+ for(; it != end; ++it)
+ {
+ if((*it)->createSegment(channel, len, segment))
+ {
+ made_segment = true;
+ break;
+ }
+ }
+ segment_iterator_t send = mSegments.end();
+ if(!made_segment)
+ {
+ LLBuffer* buf = new LLHeapBuffer;
+ mBuffers.push_back(buf);
+ if(!buf->createSegment(channel, len, segment))
+ {
+ // failed. this should never happen.
+ return send;
+ }
+ }
+
+ // store and return the newly made segment
+ mSegments.insert(send, segment);
+ std::list<LLSegment>::reverse_iterator rv = mSegments.rbegin();
+ ++rv;
+ send = rv.base();
+ return send;
+}
+
+bool LLBufferArray::eraseSegment(const segment_iterator_t& iter)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ // *FIX: in theory, we could reclaim the memory. We are leaking a
+ // bit of buffered memory into an unusable but still referenced
+ // location.
+ (void)mSegments.erase(iter);
+ return true;
+}
+
+
+bool LLBufferArray::copyIntoBuffers(
+ S32 channel,
+ const U8* src,
+ S32 len,
+ std::vector<LLSegment>& segments)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!src || !len) return false;
+ S32 copied = 0;
+ LLSegment segment;
+ buffer_iterator_t it = mBuffers.begin();
+ buffer_iterator_t end = mBuffers.end();
+ for(; it != end;)
+ {
+ if(!(*it)->createSegment(channel, len, segment))
+ {
+ ++it;
+ continue;
+ }
+ segments.push_back(segment);
+ S32 bytes = llmin(segment.size(), len);
+ memcpy(segment.data(), src + copied, bytes); /* Flawfinder Ignore */
+ copied += bytes;
+ len -= bytes;
+ if(0 == len)
+ {
+ break;
+ }
+ }
+ while(len)
+ {
+ LLBuffer* buf = new LLHeapBuffer;
+ mBuffers.push_back(buf);
+ if(!buf->createSegment(channel, len, segment))
+ {
+ // this totally failed - bail. This is the weird corner
+ // case were we 'leak' memory. No worries about an actual
+ // leak - we will still reclaim the memory later, but this
+ // particular buffer array is hosed for some reason.
+ // This should never happen.
+ return false;
+ }
+ segments.push_back(segment);
+ memcpy(segment.data(), src + copied, segment.size());
+ copied += segment.size();
+ len -= segment.size();
+ }
+ return true;
+}
diff --git a/indra/llmessage/llbuffer.h b/indra/llmessage/llbuffer.h
new file mode 100644
index 0000000000..3d7f209123
--- /dev/null
+++ b/indra/llmessage/llbuffer.h
@@ -0,0 +1,493 @@
+/**
+ * @file llbuffer.h
+ * @author Phoenix
+ * @date 2005-09-20
+ * @brief Declaration of buffer and buffer arrays primarily used in I/O.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBUFFER_H
+#define LL_LLBUFFER_H
+
+/**
+ * Declaration of classes used for minimizing calls to new[],
+ * memcpy(), and delete[]. Typically, you would create an LLHeapArray,
+ * feed it data, modify and add segments as you process it, and feed
+ * it to a sink.
+ */
+
+#include <list>
+
+/**
+ * @class LLChannelDescriptors
+ * @brief A way simple interface to accesss channels inside a buffer
+ */
+class LLChannelDescriptors
+{
+public:
+ // enumeration for segmenting the channel information
+ enum { E_CHANNEL_COUNT = 3 };
+ LLChannelDescriptors() : mBaseChannel(0) {}
+ explicit LLChannelDescriptors(S32 base) : mBaseChannel(base) {}
+ S32 in() const { return mBaseChannel; }
+ S32 out() const { return mBaseChannel + 1; }
+ //S32 err() const { return mBaseChannel + 2; }
+protected:
+ S32 mBaseChannel;
+};
+
+
+/**
+ * @class LLSegment
+ * @brief A segment is a single, contiguous chunk of memory in a buffer
+ *
+ * Each segment represents a contiguous addressable piece of memory
+ * which is located inside a buffer. The segment is not responsible
+ * for allocation or deallcoation of the data. Each segment is a light
+ * weight object, and simple enough to copy around, use, and generate
+ * as necessary.
+ * This is the preferred interface for working with memory blocks,
+ * since it is the only way to safely, inexpensively, and directly
+ * access linear blocks of memory.
+ */
+class LLSegment
+{
+public:
+ LLSegment();
+ LLSegment(S32 channel, U8* data, S32 data_len);
+ ~LLSegment();
+
+ /**
+ * @brief Check if this segment is on the given channel.
+ *
+ */
+ bool isOnChannel(S32 channel) const;
+
+ /**
+ * @brief Get the channel
+ */
+ S32 getChannel() const;
+
+ /**
+ * @brief Set the channel
+ */
+ void setChannel(S32 channel);
+
+ /**
+ * @brief Return a raw pointer to the current data set.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to size() bytes into memory.
+ * @return A potentially NULL pointer to the raw buffer data
+ */
+ U8* data() const;
+
+ /**
+ * @brief Return the size of the segment
+ */
+ S32 size() const;
+
+protected:
+ S32 mChannel;
+ U8* mData;
+ S32 mSize;
+};
+
+/**
+ * @class LLBuffer
+ * @brief Abstract base class for buffers
+ *
+ * This class declares the interface necessary for buffer arrays. A
+ * buffer is not necessarily a single contiguous memory chunk, so
+ * please do not circumvent the segment API.
+ */
+class LLBuffer
+{
+public:
+ /**
+ * @brief The buffer base class should have no responsibilities
+ * other than an interface.
+ */
+ virtual ~LLBuffer() {}
+
+ /**
+ * @brief Generate a segment for this buffer.
+ *
+ * The segment returned is always contiguous memory. This call can
+ * fail if no contiguous memory is available, eg, offset is past
+ * the end. The segment returned may be smaller than the requested
+ * size. The segment will never be larger than the requested size.
+ * @param channel The channel for the segment.
+ * @param offset The offset from zero in the buffer.
+ * @param size The requested size of the segment.
+ * @param segment[out] The out-value from the operation
+ * @return Returns true if a segment was found.
+ */
+ virtual bool createSegment(S32 channel, S32 size, LLSegment& segment) = 0;
+};
+
+/**
+ * @class LLHeapBuffer
+ * @brief A large contiguous buffer allocated on the heap with new[].
+ *
+ * This class is a simple buffer implementation which allocates chunks
+ * off the heap. Once a buffer is constructed, it's buffer has a fixed
+ * length.
+ */
+class LLHeapBuffer : public LLBuffer
+{
+public:
+ /**
+ * @brief Construct a heap buffer with a reasonable default size.
+ */
+ LLHeapBuffer();
+
+ /**
+ * @brief Construct a heap buffer with a specified size.
+ *
+ * @param size The minimum size of the buffer.
+ */
+ explicit LLHeapBuffer(S32 size);
+
+ /**
+ * @brief Construct a heap buffer of minimum size len, and copy from src.
+ *
+ * @param src The source of the data to be copied.
+ * @param len The minimum size of the buffer.
+ */
+ LLHeapBuffer(const U8* src, S32 len);
+
+ /**
+ * @brief Simple destruction.
+ */
+ virtual ~LLHeapBuffer();
+
+ /**
+ * @brief Get the number of bytes left in the buffer.
+ *
+ * @return Returns the number of bytes left.
+ */
+ //virtual S32 bytesLeft() const;
+
+ /**
+ * @brief Generate a segment for this buffer.
+ *
+ * The segment returned is always contiguous memory. This call can
+ * fail if no contiguous memory is available, eg, offset is past
+ * the end. The segment returned may be smaller than the requested
+ * size. It is up to the caller to delete the segment returned.
+ * @param channel The channel for the segment.
+ * @param offset The offset from zero in the buffer
+ * @param size The requested size of the segment
+ * @param segment[out] The out-value from the operation
+ * @return Returns true if a segment was found.
+ */
+ virtual bool createSegment(S32 channel, S32 size, LLSegment& segment);
+
+protected:
+ U8* mBuffer;
+ S32 mSize;
+ U8* mNextFree;
+
+private:
+ /**
+ * @brief Helper method to allocate a buffer and correctly set
+ * intertnal state of this buffer.
+ */
+ void allocate(S32 size);
+};
+
+/**
+ * @class LLBufferArray
+ * @brief Class to represent scattered memory buffers and in-order segments
+ * of that buffered data.
+ *
+ * NOTE: This class needs to have an iovec interface
+ */
+class LLBufferArray
+{
+public:
+ typedef std::vector<LLBuffer*> buffer_list_t;
+ typedef buffer_list_t::iterator buffer_iterator_t;
+ typedef std::list<LLSegment> segment_list_t;
+ typedef segment_list_t::const_iterator const_segment_iterator_t;
+ typedef segment_list_t::iterator segment_iterator_t;
+ enum { npos = 0xffffffff };
+
+ LLBufferArray();
+ ~LLBufferArray();
+
+ /* @name Channel methods
+ */
+ //@{
+ /**
+ * @brief Generate the a channel descriptor which consumes the
+ * output for the channel passed in.
+ */
+ static LLChannelDescriptors makeChannelConsumer(
+ const LLChannelDescriptors& channels);
+
+ /**
+ * @brief Generate the next channel descriptor for this buffer array.
+ *
+ * The channel descriptor interface is how the buffer array
+ * clients can know where to read and write data. Use this
+ * interface to get the 'next' channel set for usage. This is a
+ * bit of a simple hack until it's utility indicates it should be
+ * extended.
+ * @return Returns a valid channel descriptor set for input and output.
+ */
+ LLChannelDescriptors nextChannel();
+ //@}
+
+ /* @name Data methods
+ */
+ //@{
+
+ // These methods will be useful once there is any kind of buffer
+ // besides a heap buffer.
+ //bool append(EBufferChannel channel, LLBuffer* data);
+ //bool prepend(EBufferChannel channel, LLBuffer* data);
+ //bool insertAfter(
+ // segment_iterator_t segment,
+ // EBufferChannel channel,
+ // LLBuffer* data);
+
+ /**
+ * @brief Put data on a channel at the end of this buffer array.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put on the end of the array. This
+ * object will internally allocate new buffers if necessary.
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool append(S32 channel, const U8* src, S32 len);
+
+ /**
+ * @brief Put data on a channel at the front of this buffer array.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put in the front of the array. This
+ * object will internally allocate new buffers if necessary.
+ * @param channel The channel for this data
+
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool prepend(S32 channel, const U8* src, S32 len);
+
+ /**
+ * @brief Insert data into a buffer array after a particular segment.
+ *
+ * The data is copied from src into the buffer array. At least one
+ * new segment is created and put in the array. This object will
+ * internally allocate new buffers if necessary.
+ * @param segment The segment in front of the new segments location
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @return Returns true if the method worked.
+ */
+ bool insertAfter(
+ segment_iterator_t segment,
+ S32 channel,
+ const U8* src,
+ S32 len);
+
+ /**
+ * @brief Count bytes in the buffer array on the specified channel
+ *
+ * @param channel The channel to count.
+ * @param start The start address in the array for counting. You
+ * can specify NULL to start at the beginning.
+ * @return Returns the number of bytes in the channel after start
+ */
+ S32 countAfter(S32 channel, U8* start) const;
+
+ /**
+ * @brief Read bytes in the buffer array on the specified channel
+ *
+ * You should prefer iterating over segments is possible since
+ * this method requires you to allocate large buffers - precisely
+ * what this class is trying to prevent. This method will skip
+ * any segments which are not on the given channel, so this method
+ * would usually be used to read a channel and copy that to a log
+ * or a socket buffer or something.
+ * @param channel The channel to read.
+ * @param start The start address in the array for reading. You
+ * can specify NULL to start at the beginning.
+ * @param dest The destination of the data read. This must be at
+ * least len bytes long.
+ * @param len[in,out] <b>in</b> How many bytes to read. <b>out</b> How
+ * many bytes were read.
+ * @return Returns the address of the last read byte.
+ */
+ U8* readAfter(S32 channel, U8* start, U8* dest, S32& len) const;
+
+ /**
+ * @brief Find an address in a buffer array
+ *
+ * @param channel The channel to seek in.
+ * @param start The start address in the array for the seek
+ * operation. You can specify NULL to start the seek at the
+ * beginning, or pass in npos to start at the end.
+ * @param delta How many bytes to seek through the array.
+ * @return Returns the address of the last read byte.
+ */
+ U8* seek(S32 channel, U8* start, S32 delta) const;
+ //@}
+
+ /* @name Buffer interaction
+ */
+ //@{
+ /**
+ * @brief Take the contents of another buffer array
+ *
+ * This method simply strips the contents out of the source
+ * buffery array - segments, buffers, etc, and appends them to
+ * this instance. After this operation, the source is empty and
+ * ready for reuse.
+ * @param source The source buffer
+ * @return Returns true if the operation succeeded.
+ */
+ bool takeContents(LLBufferArray& source);
+ //@}
+
+ /* @name Segment methods
+ */
+ //@{
+ /**
+ * @brief Split a segments so that address is the last address of
+ * one segment, and the rest of the original segment becomes
+ * another segment on the same channel.
+ *
+ * After this method call,
+ * <code>getLastSegmentAddress(*getSegment(address)) ==
+ * address</code> should be true. This call will only create a new
+ * segment if the statement above is false before the call. Since
+ * you usually call splitAfter() to change a segment property, use
+ * getSegment() to perform those operations.
+ * @param address The address which will become the last address
+ * of the segment it is in.
+ * @return Returns an iterator to the segment which contains
+ * <code>address</code> which is <code>endSegment()</code> on
+ * failure.
+ */
+ segment_iterator_t splitAfter(U8* address);
+
+ /**
+ * @brief Get the first segment in the buffer array.
+ *
+ * @return Returns the segment if there is one.
+ */
+ segment_iterator_t beginSegment();
+
+ /**
+ * @brief Get the one-past-the-end segment in the buffer array
+ *
+ * @return Returns the iterator for an invalid segment location.
+ */
+ segment_iterator_t endSegment();
+
+ /**
+ * @brief Get the segment which holds the given address.
+ *
+ * As opposed to some methods, passing a NULL will result in
+ * returning the end segment.
+ * @param address An address in the middle of the sought segment.
+ * @return Returns the iterator for the segment or endSegment() on
+ * failure.
+ */
+ const_segment_iterator_t getSegment(U8* address) const;
+
+ /**
+ * @brief Get the segment which holds the given address.
+ *
+ * As opposed to some methods, passing a NULL will result in
+ * returning the end segment.
+ * @param address An address in the middle of the sought segment.
+ * @return Returns the iterator for the segment or endSegment() on
+ * failure.
+ */
+ segment_iterator_t getSegment(U8* address);
+
+ /**
+ * @brief Get a segment iterator after address, and a constructed
+ * segment to represent the next linear block of memory.
+ *
+ * This method is a helper by giving you the largest segment
+ * possible in the out-value param after the address provided. The
+ * iterator will be useful for iteration, while the segment can be
+ * used for direct access to memory after address if the return
+ * values isnot end. Passing in NULL will return beginSegment()
+ * which may be endSegment(). The segment returned will only be
+ * zero length if the return value equals end.
+ * This is really just a helper method, since all the information
+ * returned could be constructed through other methods.
+ * @param address An address in the middle of the sought segment.
+ * @param segment[out] segment to be used for reading or writing
+ * @return Returns an iterator which contains at least segment or
+ * endSegment() on failure.
+ */
+ segment_iterator_t constructSegmentAfter(U8* address, LLSegment& segment);
+
+ /**
+ * @brief Make a new segment at the end of buffer array
+ *
+ * This method will attempt to create a new and empty segment of
+ * the specified length. The segment created may be shorter than
+ * requested.
+ * @param channel[in] The channel for the newly created segment.
+ * @param length[in] The requested length of the segment.
+ * @return Returns an iterator which contains at least segment or
+ * endSegment() on failure.
+ */
+ segment_iterator_t makeSegment(S32 channel, S32 length);
+
+ /**
+ * @brief Erase the segment if it is in the buffer array.
+ *
+ * @param iter An iterator referring to the segment to erase.
+ * @return Returns true on success.
+ */
+ bool eraseSegment(const segment_iterator_t& iter);
+ //@}
+
+protected:
+ /**
+ * @brief Optimally put data in buffers, and reutrn segments.
+ *
+ * This is an internal function used to create buffers as
+ * necessary, and sequence the segments appropriately for the
+ * various ways to copy data from src into this.
+ * If this method fails, it may actually leak some space inside
+ * buffers, but I am not too worried about the slim possibility
+ * that we may have some 'dead' space which will be recovered when
+ * the buffer (which we will not lose) is deleted. Addressing this
+ * weakness will make the buffers almost as complex as a general
+ * memory management system.
+ * @param channel The channel for this data
+ * @param src The start of memory for the data to be copied
+ * @param len The number of bytes of data to copy
+ * @param segments Out-value for the segments created.
+ * @return Returns true if the method worked.
+ */
+ bool copyIntoBuffers(
+ S32 channel,
+ const U8* src,
+ S32 len,
+ std::vector<LLSegment>& segments);
+
+protected:
+ S32 mNextBaseChannel;
+ buffer_list_t mBuffers;
+ segment_list_t mSegments;
+};
+
+#endif // LL_LLBUFFER_H
diff --git a/indra/llmessage/llbufferstream.cpp b/indra/llmessage/llbufferstream.cpp
new file mode 100644
index 0000000000..684548b408
--- /dev/null
+++ b/indra/llmessage/llbufferstream.cpp
@@ -0,0 +1,265 @@
+/**
+ * @file llbufferstream.cpp
+ * @author Phoenix
+ * @date 2005-10-10
+ * @brief Implementation of the buffer iostream classes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llbufferstream.h"
+
+#include "llbuffer.h"
+#include "llmemtype.h"
+
+static const S32 DEFAULT_OUTPUT_SEGMENT_SIZE = 1024 * 4;
+
+/*
+ * LLBufferStreamBuf
+ */
+LLBufferStreamBuf::LLBufferStreamBuf(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer) :
+ mChannels(channels),
+ mBuffer(buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferStreamBuf::~LLBufferStreamBuf()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ sync();
+}
+
+// virtual
+int LLBufferStreamBuf::underflow()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ //lldebugs << "LLBufferStreamBuf::underflow()" << llendl;
+ if(!mBuffer)
+ {
+ return EOF;
+ }
+ LLSegment segment;
+ LLBufferArray::segment_iterator_t it;
+ U8* last_pos = (U8*)gptr();
+ if(last_pos) --last_pos;
+
+ LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
+
+ // Get iterator to full segment containing last_pos
+ // and construct sub-segment starting at last_pos.
+ // Note: segment may != *it at this point
+ it = mBuffer->constructSegmentAfter(last_pos, segment);
+ if(it == end)
+ {
+ return EOF;
+ }
+
+ // Iterate through segments to find a non-empty segment on input channel.
+ while((!segment.isOnChannel(mChannels.in()) || (segment.size() == 0)))
+ {
+ ++it;
+ if(it == end)
+ {
+ return EOF;
+ }
+
+ segment = *it;
+ }
+
+ char* start = (char*)segment.data();
+ setg(start, start, start + segment.size());
+ return *gptr();
+}
+
+// virtual
+int LLBufferStreamBuf::overflow(int c)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!mBuffer)
+ {
+ return EOF;
+ }
+ if(EOF == c)
+ {
+ // if someone puts an EOF, I suppose we should sync and return
+ // success.
+ if(0 == sync())
+ {
+ return 1;
+ }
+ else
+ {
+ return EOF;
+ }
+ }
+
+ // since we got here, we have a buffer, and we have a character to
+ // put on it.
+ LLBufferArray::segment_iterator_t it;
+ it = mBuffer->makeSegment(mChannels.out(), DEFAULT_OUTPUT_SEGMENT_SIZE);
+ if(it != mBuffer->endSegment())
+ {
+ char* start = (char*)(*it).data();
+ (*start) = (char)(c);
+ setp(start + 1, start + (*it).size());
+ return c;
+ }
+ else
+ {
+ return EOF;
+ }
+}
+
+// virtual
+int LLBufferStreamBuf::sync()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ int return_value = -1;
+ if(!mBuffer)
+ {
+ return return_value;
+ }
+
+ // set the put pointer so that we force an overflow on the next
+ // write.
+ U8* address = (U8*)pptr();
+ setp(NULL, NULL);
+
+ // *NOTE: I bet we could just --address. Need to think about that.
+ address = mBuffer->seek(mChannels.out(), address, -1);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t it;
+ it = mBuffer->splitAfter(address);
+ LLBufferArray::segment_iterator_t end = mBuffer->endSegment();
+ if(it != end)
+ {
+ ++it;
+ if(it != end)
+ {
+ mBuffer->eraseSegment(it);
+ }
+ return_value = 0;
+ }
+ }
+ else
+ {
+ // nothing was put on the buffer, so the sync() is a no-op.
+ return_value = 0;
+ }
+ return return_value;
+}
+
+// virtual
+#if( LL_WINDOWS || __GNUC__ > 2)
+LLBufferStreamBuf::pos_type LLBufferStreamBuf::seekoff(
+ LLBufferStreamBuf::off_type off,
+ std::ios::seekdir way,
+ std::ios::openmode which)
+#else
+streampos LLBufferStreamBuf::seekoff(
+ streamoff off,
+ std::ios::seekdir way,
+ std::ios::openmode which)
+#endif
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+ if(!mBuffer
+ || ((way == std::ios::beg) && (off < 0))
+ || ((way == std::ios::end) && (off > 0)))
+ {
+ return -1;
+ }
+ U8* address = NULL;
+ if(which & std::ios::in)
+ {
+ U8* base_addr = NULL;
+ switch(way)
+ {
+ case std::ios::end:
+ base_addr = (U8*)LLBufferArray::npos;
+ break;
+ case std::ios::cur:
+ // get the current get pointer and adjust it for buffer
+ // array semantics.
+ base_addr = (U8*)gptr();
+ break;
+ case std::ios::beg:
+ default:
+ // NULL is fine
+ break;
+ }
+ address = mBuffer->seek(mChannels.in(), base_addr, off);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t iter;
+ iter = mBuffer->getSegment(address);
+ char* start = (char*)(*iter).data();
+ setg(start, (char*)address, start + (*iter).size());
+ }
+ else
+ {
+ address = (U8*)(-1);
+ }
+ }
+ if(which & std::ios::out)
+ {
+ U8* base_addr = NULL;
+ switch(way)
+ {
+ case std::ios::end:
+ base_addr = (U8*)LLBufferArray::npos;
+ break;
+ case std::ios::cur:
+ // get the current put pointer and adjust it for buffer
+ // array semantics.
+ base_addr = (U8*)pptr();
+ break;
+ case std::ios::beg:
+ default:
+ // NULL is fine
+ break;
+ }
+ address = mBuffer->seek(mChannels.out(), base_addr, off);
+ if(address)
+ {
+ LLBufferArray::segment_iterator_t iter;
+ iter = mBuffer->getSegment(address);
+ setp((char*)address, (char*)(*iter).data() + (*iter).size());
+ }
+ else
+ {
+ address = (U8*)(-1);
+ }
+ }
+
+#if( LL_WINDOWS || __GNUC__ > 2 )
+ S32 rv = (S32)(intptr_t)address;
+ return (pos_type)rv;
+#else
+ return (streampos)address;
+#endif
+}
+
+
+/*
+ * LLBufferStream
+ */
+LLBufferStream::LLBufferStream(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer) :
+ std::iostream(&mStreamBuf),
+ mStreamBuf(channels, buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
+
+LLBufferStream::~LLBufferStream()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_BUFFER);
+}
diff --git a/indra/llmessage/llbufferstream.h b/indra/llmessage/llbufferstream.h
new file mode 100644
index 0000000000..8b972322ce
--- /dev/null
+++ b/indra/llmessage/llbufferstream.h
@@ -0,0 +1,134 @@
+/**
+ * @file llbufferstream.h
+ * @author Phoenix
+ * @date 2005-10-10
+ * @brief Classes to treat an LLBufferArray as a c++ iostream.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBUFFERSTREAM_H
+#define LL_LLBUFFERSTREAM_H
+
+#include <iosfwd>
+#include <iostream>
+#include "llbuffer.h"
+
+/**
+ * @class LLBufferStreamBuf
+ * @brief This implements the buffer wrapper for an istream
+ *
+ * The buffer array passed in is not owned by the stream buf object.
+ */
+class LLBufferStreamBuf : public std::streambuf
+{
+public:
+ LLBufferStreamBuf(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer);
+ virtual ~LLBufferStreamBuf();
+
+protected:
+#if( LL_WINDOWS || __GNUC__ > 2 )
+ typedef std::streambuf::pos_type pos_type;
+ typedef std::streambuf::off_type off_type;
+#endif
+
+ /* @name streambuf vrtual implementations
+ */
+ //@{
+ /*
+ * @brief called when we hit the end of input
+ *
+ * @return Returns the character at the current position or EOF.
+ */
+ virtual int underflow();
+
+ /*
+ * @brief called when we hit the end of output
+ *
+ * @param c The character to store at the current put position
+ * @return Returns EOF if the function failed. Any other value on success.
+ */
+ virtual int overflow(int c);
+
+ /*
+ * @brief synchronize the buffer
+ *
+ * @return Returns 0 on success or -1 on failure.
+ */
+ virtual int sync();
+
+ /*
+ * @brief Seek to an offset position in a stream.
+ *
+ * @param off Offset value relative to way paramter
+ * @param way The seek direction. One of ios::beg, ios::cur, and ios::end.
+ * @param which Which pointer to modify. One of ios::in, ios::out,
+ * or both masked together.
+ * @return Returns the new position or an invalid position on failure.
+ */
+#if( LL_WINDOWS || __GNUC__ > 2)
+ virtual pos_type seekoff(
+ off_type off,
+ std::ios::seekdir way,
+ std::ios::openmode which);
+#else
+ virtual streampos seekoff(
+ streamoff off,
+ std::ios::seekdir way,
+ std::ios::openmode which);
+#endif
+
+ /*
+ * @brief Get s sequence of characters from the input
+ *
+ * @param dst Pointer to a block of memory to accept the characters
+ * @param length Number of characters to be read
+ * @return Returns the number of characters read
+ */
+ //virtual streamsize xsgetn(char* dst, streamsize length);
+
+ /*
+ * @brief Write some characters to output
+ *
+ * @param src Pointer to a sequence of characters to be output
+ * @param length Number of characters to be put
+ * @return Returns the number of characters written
+ */
+ //virtual streamsize xsputn(char* src, streamsize length);
+ //@}
+
+protected:
+ // This channels we are working on.
+ LLChannelDescriptors mChannels;
+
+ // The buffer we work on
+ LLBufferArray* mBuffer;
+};
+
+
+/**
+ * @class LLBufferStream
+ * @brief This implements an istream based wrapper around an LLBufferArray.
+ *
+ * This class does not own the buffer array, and does not hold a
+ * shared pointer to it. Since the class itself is fairly ligthweight,
+ * just make one on the stack when needed and let it fall out of
+ * scope.
+ */
+class LLBufferStream : public std::iostream
+{
+public:
+ LLBufferStream(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* buffer);
+ ~LLBufferStream();
+
+protected:
+ LLBufferStreamBuf mStreamBuf;
+};
+
+
+#endif // LL_LLBUFFERSTREAM_H
diff --git a/indra/llmessage/llcachename.cpp b/indra/llmessage/llcachename.cpp
new file mode 100644
index 0000000000..075f4f01cf
--- /dev/null
+++ b/indra/llmessage/llcachename.cpp
@@ -0,0 +1,763 @@
+/**
+ * @file llcachename.cpp
+ * @brief A hierarchical cache of first and last names queried based on UUID.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcachename.h"
+
+// system includes
+#include <string.h> // strcpy
+#include <time.h>
+#include <algorithm>
+#include <functional>
+#include <map>
+
+// linden library includes
+#include "message.h"
+#include "llrand.h"
+#include "lldbstrings.h"
+#include "llframetimer.h"
+#include "llhost.h"
+#include "lluuid.h"
+
+// Constants
+const char* CN_WAITING = "(waiting)";
+const char* CN_NOBODY = "(nobody)";
+const char* CN_NONE = "(none)";
+const char* CN_HIPPOS = "(hippos)";
+const F32 HIPPO_PROBABILITY = 0.01f;
+
+// File version number
+const S32 CN_FILE_VERSION = 2;
+
+// Globals
+LLCacheName* gCacheName = NULL;
+
+/// ---------------------------------------------------------------------------
+/// class LLCacheNameEntry
+/// ---------------------------------------------------------------------------
+
+namespace {
+ class LLCacheNameEntry
+ {
+ public:
+ LLCacheNameEntry();
+
+ public:
+ bool mIsGroup;
+ U32 mCreateTime; // unix time_t
+ char mFirstName[DB_FIRST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ char mLastName[DB_LAST_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ char mGroupName[DB_GROUP_NAME_BUF_SIZE]; /*Flawfinder: ignore*/
+ };
+
+ LLCacheNameEntry::LLCacheNameEntry()
+ {
+ mFirstName[0] = '\0';
+ mLastName[0] = '\0';
+ mGroupName[0] = '\0';
+ }
+
+
+ class PendingReply
+ {
+ public:
+ LLUUID mID;
+ LLCacheNameCallback mCallback;
+ LLHost mHost;
+ void* mData;
+ PendingReply(const LLUUID& id, LLCacheNameCallback callback, void* data = NULL)
+ : mID(id), mCallback(callback), mData(data)
+ { }
+
+ PendingReply(const LLUUID& id, const LLHost& host)
+ : mID(id), mCallback(0), mHost(host)
+ { }
+
+ void done() { mID.setNull(); }
+ bool isDone() const { return mID.isNull() != FALSE; }
+ };
+
+ class ReplySender
+ {
+ public:
+ ReplySender(LLMessageSystem* msg);
+ ~ReplySender();
+
+ void send(const LLUUID& id,
+ const LLCacheNameEntry& entry, const LLHost& host);
+
+ private:
+ void flush();
+
+ LLMessageSystem* mMsg;
+ bool mPending;
+ bool mCurrIsGroup;
+ LLHost mCurrHost;
+ };
+
+ ReplySender::ReplySender(LLMessageSystem* msg)
+ : mMsg(msg), mPending(false)
+ { }
+
+ ReplySender::~ReplySender()
+ {
+ flush();
+ }
+
+ void ReplySender::send(const LLUUID& id,
+ const LLCacheNameEntry& entry, const LLHost& host)
+ {
+ if (mPending)
+ {
+ if (mCurrIsGroup != entry.mIsGroup
+ || mCurrHost != host)
+ {
+ flush();
+ }
+ }
+
+ if (!mPending)
+ {
+ mPending = true;
+ mCurrIsGroup = entry.mIsGroup;
+ mCurrHost = host;
+
+ if(mCurrIsGroup)
+ mMsg->newMessageFast(_PREHASH_UUIDGroupNameReply);
+ else
+ mMsg->newMessageFast(_PREHASH_UUIDNameReply);
+ }
+
+ mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
+ mMsg->addUUIDFast(_PREHASH_ID, id);
+ if(mCurrIsGroup)
+ {
+ mMsg->addStringFast(_PREHASH_GroupName, entry.mGroupName);
+ }
+ else
+ {
+ mMsg->addStringFast(_PREHASH_FirstName, entry.mFirstName);
+ mMsg->addStringFast(_PREHASH_LastName, entry.mLastName);
+ }
+
+ if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
+ {
+ flush();
+ }
+ }
+
+ void ReplySender::flush()
+ {
+ if (mPending)
+ {
+ mMsg->sendReliable(mCurrHost);
+ mPending = false;
+ }
+ }
+
+
+ typedef std::vector<LLUUID> AskQueue;
+ typedef std::vector<PendingReply> ReplyQueue;
+ typedef std::map<LLUUID, LLCacheNameEntry*> Cache;
+ typedef std::vector<LLCacheNameCallback> Observers;
+};
+
+class LLCacheName::Impl
+{
+public:
+ LLMessageSystem* mMsg;
+ LLHost mUpstreamHost;
+
+ Cache mCache;
+ // the map of UUIDs to names
+
+ AskQueue mAskNameQueue;
+ AskQueue mAskGroupQueue;
+ // UUIDs to ask our upstream host about
+
+ ReplyQueue mReplyQueue;
+ // requests awaiting replies from us
+
+ Observers mObservers;
+
+ LLFrameTimer mProcessTimer;
+
+ Impl(LLMessageSystem* msg);
+ ~Impl();
+
+ void processPendingAsks();
+ void processPendingReplies();
+ void sendRequest(const char* msg_name, const AskQueue& queue);
+
+ // Message system callbacks.
+ void processUUIDRequest(LLMessageSystem* msg, bool isGroup);
+ void processUUIDReply(LLMessageSystem* msg, bool isGroup);
+
+ static void handleUUIDNameRequest(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDNameReply(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userdata);
+ static void handleUUIDGroupNameReply(LLMessageSystem* msg, void** userdata);
+
+ void notifyObservers(const LLUUID& id, const char* first, const char* last, BOOL group);
+};
+
+
+/// --------------------------------------------------------------------------
+/// class LLCacheName
+/// ---------------------------------------------------------------------------
+
+LLCacheName::LLCacheName(LLMessageSystem* msg)
+ : impl(* new Impl(msg))
+ { }
+
+LLCacheName::LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host)
+ : impl(* new Impl(msg))
+{
+ setUpstream(upstream_host);
+}
+
+LLCacheName::~LLCacheName()
+{
+ delete &impl;
+}
+
+LLCacheName::Impl::Impl(LLMessageSystem* msg)
+ : mMsg(msg), mUpstreamHost(LLHost::invalid)
+{
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDNameRequest, handleUUIDNameRequest, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDNameReply, handleUUIDNameReply, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDGroupNameRequest, handleUUIDGroupNameRequest, (void**)this);
+ mMsg->setHandlerFuncFast(
+ _PREHASH_UUIDGroupNameReply, handleUUIDGroupNameReply, (void**)this);
+}
+
+
+LLCacheName::Impl::~Impl()
+{
+ for_each(mCache.begin(), mCache.end(), DeletePairedPointer());
+}
+
+
+void LLCacheName::setUpstream(const LLHost& upstream_host)
+{
+ impl.mUpstreamHost = upstream_host;
+}
+
+void LLCacheName::addObserver(LLCacheNameCallback callback)
+{
+ impl.mObservers.push_back(callback);
+}
+
+
+void LLCacheName::importFile(FILE* fp)
+{
+ S32 count = 0;
+
+ const S32 BUFFER_SIZE = 1024;
+ char buffer[BUFFER_SIZE]; /*Flawfinder: ignore*/
+
+ char id_string[MAX_STRING]; /*Flawfinder: ignore*/
+ char firstname[MAX_STRING]; /*Flawfinder: ignore*/
+ char lastname[MAX_STRING]; /*Flawfinder: ignore*/
+ U32 create_time;
+
+ // This is OK if the first line is actually a name. We just don't load it.
+ char* valid = fgets(buffer, BUFFER_SIZE, fp);
+ if (!valid) return;
+
+ char version_string[BUFFER_SIZE]; /*Flawfinder: ignore*/
+ S32 version = 0;
+ S32 match = sscanf(buffer, "%s %d", version_string, &version); // XXXTBD
+ if ( match != 2
+ || strcmp(version_string, "version")
+ || version != CN_FILE_VERSION)
+ {
+ llwarns << "Ignoring old cache name file format" << llendl;
+ return;
+ }
+
+ // We'll expire entries more than a week old
+ U32 now = (U32)time(NULL);
+ const U32 SECS_PER_DAY = 60 * 60 * 24;
+ U32 delete_before_time = now - (7 * SECS_PER_DAY);
+
+ while(!feof(fp))
+ {
+ valid = fgets(buffer, BUFFER_SIZE, fp);
+ if (!valid) break;
+
+ match = sscanf(buffer, "%s %u %s %s", // XXXTBD
+ id_string,
+ &create_time,
+ firstname,
+ lastname);
+ if (4 != match) continue;
+
+ LLUUID id(id_string);
+ if (id.isNull()) continue;
+
+ // undo trivial XOR
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ id.mData[i] ^= 0x33;
+ }
+
+ // Don't load entries that are more than a week old
+ if (create_time < delete_before_time) continue;
+
+ LLCacheNameEntry* entry = new LLCacheNameEntry();
+ entry->mIsGroup = false;
+ entry->mCreateTime = create_time;
+ LLString::copy(entry->mFirstName, firstname, DB_FIRST_NAME_BUF_SIZE);
+ LLString::copy(entry->mLastName, lastname, DB_LAST_NAME_BUF_SIZE);
+ impl.mCache[id] = entry;
+
+ count++;
+ }
+
+ llinfos << "LLCacheName loaded " << count << " names" << llendl;
+}
+
+
+void LLCacheName::exportFile(FILE* fp)
+{
+ fprintf(fp, "version\t%d\n", CN_FILE_VERSION);
+
+ for (Cache::iterator iter = impl.mCache.begin(),
+ end = impl.mCache.end();
+ iter != end; iter++)
+ {
+ LLCacheNameEntry* entry = iter->second;
+ // Only write entries for which we have valid data.
+ // HACK: Only write agent names. This makes the reader easier.
+ if ( entry->mFirstName[0]
+ && entry->mLastName[0])
+ {
+ LLUUID id = iter->first;
+
+ // Trivial XOR encoding
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ id.mData[i] ^= 0x33;
+ }
+
+ char id_string[UUID_STR_SIZE]; /*Flawfinder:ignore*/
+ id.toString(id_string);
+
+ // ...not a group name
+ fprintf(fp, "%s\t%u\t%s\t%s\n",
+ id_string,
+ entry->mCreateTime,
+ entry->mFirstName,
+ entry->mLastName);
+ }
+ }
+}
+
+
+BOOL LLCacheName::getName(const LLUUID& id, char* first, char* last)
+{
+ if(id.isNull())
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(first, CN_NOBODY);
+ last[0] = '\0';
+ return FALSE;
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
+ if (entry)
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(first, entry->mFirstName);
+ strcpy(last, entry->mLastName);
+ return TRUE;
+ }
+ else
+ {
+ //The function signature needs to change to pass in the
+ //length of first and last.
+ strcpy(first,(frand(1.0f) < HIPPO_PROBABILITY)
+ ? CN_HIPPOS
+ : CN_WAITING);
+ strcpy(last, "");
+
+ impl.mAskNameQueue.push_back(id);
+ return FALSE;
+ }
+
+}
+
+
+
+BOOL LLCacheName::getGroupName(const LLUUID& id, char* group)
+{
+ if(id.isNull())
+ {
+ // The function signature needs to change to pass in the
+ // length of first and last.
+ strcpy(group, CN_NONE);
+ return FALSE;
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache,id);
+ if (entry && !entry->mGroupName[0])
+ {
+ // COUNTER-HACK to combat James' HACK in exportFile()...
+ // this group name was loaded from a name cache that did not
+ // bother to save the group name ==> we must ask for it
+ lldebugs << "LLCacheName queuing HACK group request: " << id << llendl;
+ entry = NULL;
+ }
+
+ if (entry)
+ {
+ // The function signature needs to change to pass in the length
+ // of group.
+ strcpy(group, entry->mGroupName);
+ return TRUE;
+ }
+ else
+ {
+ // The function signature needs to change to pass in the length
+ // of first and last.
+ strcpy(group, CN_WAITING);
+
+ impl.mAskGroupQueue.push_back(id);
+ return FALSE;
+ }
+}
+
+// TODO: Make the cache name callback take a SINGLE std::string,
+// not a separate first and last name.
+void LLCacheName::get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data)
+{
+ if(id.isNull())
+ {
+ callback(id, CN_NOBODY, "", is_group, user_data);
+ }
+
+ LLCacheNameEntry* entry = get_ptr_in_map(impl.mCache, id );
+ if (entry)
+ {
+ if (!entry->mIsGroup)
+ {
+ callback(id, entry->mFirstName, entry->mLastName, entry->mIsGroup, user_data);
+ }
+ else
+ {
+ callback(id, entry->mGroupName, "", entry->mIsGroup, user_data);
+ }
+ }
+ else
+ {
+ if (!is_group)
+ {
+ impl.mAskNameQueue.push_back(id);
+ }
+ else
+ {
+ impl.mAskGroupQueue.push_back(id);
+ }
+ impl.mReplyQueue.push_back(PendingReply(id, callback, user_data));
+ }
+}
+
+void LLCacheName::processPending()
+{
+ const F32 SECS_BETWEEN_PROCESS = 0.1f;
+ if(!impl.mProcessTimer.checkExpirationAndReset(SECS_BETWEEN_PROCESS))
+ {
+ return;
+ }
+
+ if(!impl.mUpstreamHost.isOk())
+ {
+ lldebugs << "LLCacheName::processPending() - bad upstream host."
+ << llendl;
+ return;
+ }
+
+ impl.processPendingAsks();
+ impl.processPendingReplies();
+}
+
+void LLCacheName::deleteEntriesOlderThan(S32 secs)
+{
+ U32 now = (U32)time(NULL);
+ U32 expire_time = now - secs;
+ for(Cache::iterator iter = impl.mCache.begin(); iter != impl.mCache.end(); )
+ {
+ Cache::iterator curiter = iter++;
+ LLCacheNameEntry* entry = curiter->second;
+ if (entry->mCreateTime < expire_time)
+ {
+ delete entry;
+ impl.mCache.erase(curiter);
+ }
+ }
+}
+
+
+void LLCacheName::dump()
+{
+ for (Cache::iterator iter = impl.mCache.begin(),
+ end = impl.mCache.end();
+ iter != end; iter++)
+ {
+ LLCacheNameEntry* entry = iter->second;
+ if (entry->mIsGroup)
+ {
+ llinfos
+ << iter->first << " = (group) "
+ << entry->mGroupName
+ << " @ " << entry->mCreateTime
+ << llendl;
+ }
+ else
+ {
+ llinfos
+ << iter->first << " = "
+ << entry->mFirstName << " " << entry->mLastName
+ << " @ " << entry->mCreateTime
+ << llendl;
+ }
+ }
+}
+
+void LLCacheName::Impl::processPendingAsks()
+{
+ sendRequest(_PREHASH_UUIDNameRequest, mAskNameQueue);
+ sendRequest(_PREHASH_UUIDGroupNameRequest, mAskGroupQueue);
+ mAskNameQueue.clear();
+ mAskGroupQueue.clear();
+}
+
+void LLCacheName::Impl::processPendingReplies()
+{
+ ReplyQueue::iterator it = mReplyQueue.begin();
+ ReplyQueue::iterator end = mReplyQueue.end();
+
+ // First call all the callbacks, because they might send messages.
+ for(; it != end; ++it)
+ {
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
+ if(!entry) continue;
+
+ if (it->mCallback)
+ {
+ if (!entry->mIsGroup)
+ {
+ (it->mCallback)(it->mID,
+ entry->mFirstName, entry->mLastName,
+ FALSE, it->mData);
+ }
+ else {
+ (it->mCallback)(it->mID,
+ entry->mGroupName, "",
+ TRUE, it->mData);
+ }
+ }
+ }
+
+ // Forward on all replies, if needed.
+ ReplySender sender(mMsg);
+ for (it = mReplyQueue.begin(); it != end; ++it)
+ {
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, it->mID);
+ if(!entry) continue;
+
+ if (it->mHost.isOk())
+ {
+ sender.send(it->mID, *entry, it->mHost);
+ }
+
+ it->done();
+ }
+
+ mReplyQueue.erase(
+ remove_if(mReplyQueue.begin(), mReplyQueue.end(),
+ std::mem_fun_ref(&PendingReply::isDone)),
+ mReplyQueue.end());
+}
+
+
+void LLCacheName::Impl::sendRequest(
+ const char* msg_name,
+ const AskQueue& queue)
+{
+ if(queue.empty())
+ {
+ return;
+ }
+
+ bool start_new_message = true;
+ AskQueue::const_iterator it = queue.begin();
+ AskQueue::const_iterator end = queue.end();
+ for(; it != end; ++it)
+ {
+ if(start_new_message)
+ {
+ start_new_message = false;
+ mMsg->newMessageFast(msg_name);
+ }
+ mMsg->nextBlockFast(_PREHASH_UUIDNameBlock);
+ mMsg->addUUIDFast(_PREHASH_ID, (*it));
+
+ if(mMsg->isSendFullFast(_PREHASH_UUIDNameBlock))
+ {
+ start_new_message = true;
+ mMsg->sendReliable(mUpstreamHost);
+ }
+ }
+ if(!start_new_message)
+ {
+ mMsg->sendReliable(mUpstreamHost);
+ }
+}
+
+void LLCacheName::Impl::notifyObservers(const LLUUID& id,
+ const char* first, const char* last, BOOL is_group)
+{
+ for (Observers::const_iterator i = mObservers.begin(),
+ end = mObservers.end();
+ i != end;
+ ++i)
+ {
+ (**i)(id, first, last, is_group, NULL);
+ }
+}
+
+
+void LLCacheName::Impl::processUUIDRequest(LLMessageSystem* msg, bool isGroup)
+{
+ // You should only get this message if the cache is at the simulator
+ // level, hence having an upstream provider.
+ if (!mUpstreamHost.isOk())
+ {
+ llwarns << "LLCacheName - got UUID name/group request, but no upstream provider!" << llendl;
+ return;
+ }
+
+ LLHost fromHost = msg->getSender();
+ ReplySender sender(msg);
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
+ if(entry)
+ {
+ if (isGroup != entry->mIsGroup)
+ {
+ llwarns << "LLCacheName - Asked for "
+ << (isGroup ? "group" : "user") << " name, "
+ << "but found "
+ << (entry->mIsGroup ? "group" : "user")
+ << ": " << id << llendl;
+ }
+ else
+ {
+ // ...it's in the cache, so send it as the reply
+ sender.send(id, *entry, fromHost);
+ }
+ }
+ else
+ {
+ if (isGroup)
+ {
+ mAskGroupQueue.push_back(id);
+ }
+ else
+ {
+ mAskNameQueue.push_back(id);
+ }
+
+ mReplyQueue.push_back(PendingReply(id, fromHost));
+ }
+ }
+}
+
+
+
+void LLCacheName::Impl::processUUIDReply(LLMessageSystem* msg, bool isGroup)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_UUIDNameBlock);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_UUIDNameBlock, _PREHASH_ID, id, i);
+ LLCacheNameEntry* entry = get_ptr_in_map(mCache, id);
+ if (!entry)
+ {
+ entry = new LLCacheNameEntry;
+ mCache[id] = entry;
+ }
+
+ entry->mIsGroup = isGroup;
+ entry->mCreateTime = (U32)time(NULL);
+ if (!isGroup)
+ {
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_FirstName, DB_FIRST_NAME_BUF_SIZE, entry->mFirstName, i);
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_LastName, DB_LAST_NAME_BUF_SIZE, entry->mLastName, i);
+ }
+ else
+ {
+ msg->getStringFast(_PREHASH_UUIDNameBlock, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, entry->mGroupName, i);
+ }
+
+ if (!isGroup)
+ {
+ notifyObservers(id,
+ entry->mFirstName, entry->mLastName,
+ FALSE);
+ }
+ else
+ {
+ notifyObservers(id, entry->mGroupName, "", TRUE);
+ }
+ }
+}
+
+
+
+// static call back functions
+
+void LLCacheName::Impl::handleUUIDNameReply(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDReply(msg, false);
+}
+
+void LLCacheName::Impl::handleUUIDNameRequest(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, false);
+}
+
+void LLCacheName::Impl::handleUUIDGroupNameRequest(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDRequest(msg, true);
+}
+
+void LLCacheName::Impl::handleUUIDGroupNameReply(LLMessageSystem* msg, void** userData)
+{
+ ((LLCacheName::Impl*)userData)->processUUIDReply(msg, true);
+}
+
+
+
+
diff --git a/indra/llmessage/llcachename.h b/indra/llmessage/llcachename.h
new file mode 100644
index 0000000000..ec9c467d8b
--- /dev/null
+++ b/indra/llmessage/llcachename.h
@@ -0,0 +1,90 @@
+/**
+ * @file llcachename.h
+ * @brief A cache of names from UUIDs.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCACHENAME_H
+#define LL_LLCACHENAME_H
+
+// Forward declarations
+#include <stdio.h>
+
+class LLMessageSystem;
+class LLHost;
+class LLUUID;
+
+// agent_id/group_id, first_name, last_name, is_group, user_data
+typedef void (*LLCacheNameCallback)(const LLUUID&, const char*, const char*, BOOL, void*);
+
+// Here's the theory:
+// If you request a name that isn't in the cache, it returns "waiting"
+// and requests the data. After the data arrives, you get that on
+// subsequent calls.
+// If the data hasn't been updated in an hour, it requests it again,
+// but keeps giving you the old value until new data arrives.
+// If you haven't requested the data in an hour, it releases it.
+class LLCacheName
+{
+public:
+ LLCacheName(LLMessageSystem* msg);
+ LLCacheName(LLMessageSystem* msg, const LLHost& upstream_host);
+ ~LLCacheName();
+
+ // registers the upstream host
+ // for viewers, this is the currently connected simulator
+ // for simulators, this is the data server
+ void setUpstream(const LLHost& upstream_host);
+
+ void addObserver(LLCacheNameCallback callback);
+ void removeObserver(LLCacheNameCallback callback);
+
+ // storing cache on disk; for viewer, in name.cache
+ void importFile(FILE* fp);
+ void exportFile(FILE* fp);
+
+ // If available, copies the first and last name into the strings provided.
+ // first must be at least DB_FIRST_NAME_BUF_SIZE characters.
+ // last must be at least DB_LAST_NAME_BUF_SIZE characters.
+ // If not available, copies the string "waiting".
+ // Returns TRUE iff available.
+ BOOL getName(const LLUUID& id, char* first, char* last);
+
+ // If available, this method copies the group name into the string
+ // provided. The caller must allocate at least
+ // DB_GROUP_NAME_BUF_SIZE characters. If not available, this
+ // method copies the string "waiting". Returns TRUE iff available.
+ BOOL getGroupName(const LLUUID& id, char* group);
+
+ // Call the callback with the group or avatar name.
+ // If the data is currently available, may call the callback immediatly
+ // otherwise, will request the data, and will call the callback when
+ // available. There is no garuntee the callback will ever be called.
+ void get(const LLUUID& id, BOOL is_group, LLCacheNameCallback callback, void* user_data = NULL);
+
+ // LEGACY
+ void getName(const LLUUID& id, LLCacheNameCallback callback, void* user_data = NULL)
+ { get(id, FALSE, callback, user_data); }
+
+ // This method needs to be called from time to time to send out
+ // requests.
+ void processPending();
+
+ // Expire entries created more than "secs" seconds ago.
+ void deleteEntriesOlderThan(S32 secs);
+
+ // Debugging
+ void dump();
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+extern LLCacheName* gCacheName;
+
+#endif
diff --git a/indra/llmessage/llchainio.cpp b/indra/llmessage/llchainio.cpp
new file mode 100644
index 0000000000..c7795f1792
--- /dev/null
+++ b/indra/llmessage/llchainio.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file llchainio.cpp
+ * @author Phoenix
+ * @date 2005-08-04
+ * @brief Implementaiton of the chain factory.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llchainio.h"
+
+#include "lliopipe.h"
+#include "llioutil.h"
+
+
+/**
+ * LLDeferredChain
+ */
+// static
+bool LLDeferredChain::addToPump(
+ LLPumpIO* pump,
+ F32 in_seconds,
+ const LLPumpIO::chain_t& deferred_chain,
+ F32 chain_timeout)
+{
+ if(!pump) return false;
+ LLPumpIO::chain_t sleep_chain;
+ sleep_chain.push_back(LLIOPipe::ptr_t(new LLIOSleep(in_seconds)));
+ sleep_chain.push_back(
+ LLIOPipe::ptr_t(new LLIOAddChain(deferred_chain, chain_timeout)));
+
+ // give it a litle bit of padding.
+ pump->addChain(sleep_chain, in_seconds + 10.0f);
+ return true;
+}
+
+/**
+ * LLChainIOFactory
+ */
+LLChainIOFactory::LLChainIOFactory()
+{
+}
+
+// virtual
+LLChainIOFactory::~LLChainIOFactory()
+{
+}
+
+#if 0
+bool LLChainIOFactory::build(LLIOPipe* in, LLIOPipe* out) const
+{
+ if(!in || !out)
+ {
+ return false;
+ }
+ LLIOPipe* first = NULL;
+ LLIOPipe* last = NULL;
+ if(build_impl(first, last) && first && last)
+ {
+ in->connect(first);
+ last->connect(out);
+ return true;
+ }
+ LLIOPipe::ptr_t foo(first);
+ LLIOPipe::ptr_t bar(last);
+ return false;
+}
+#endif
diff --git a/indra/llmessage/llchainio.h b/indra/llmessage/llchainio.h
new file mode 100644
index 0000000000..f07432da05
--- /dev/null
+++ b/indra/llmessage/llchainio.h
@@ -0,0 +1,117 @@
+/**
+ * @file llchainio.h
+ * @author Phoenix
+ * @date 2005-08-04
+ * @brief This class declares the interface for constructing io chains.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHAINIO_H
+#define LL_LLCHAINIO_H
+
+#include "llpumpio.h"
+
+/**
+ * @class LLDeferredChain
+ * @brief This class allows easy addition of a chain which will sleep
+ * and then process another chain.
+ */
+class LLDeferredChain
+{
+public:
+ /**
+ * @brief Add a chain to a pump in a finite # of seconds
+ *
+ * @prarm pump The pump to work on.
+ * @prarm in_seconds The number of seconds from now when chain should start.
+ * @prarm chain The chain to add in in_seconds seconds.
+ * @prarm chain_timeout timeout for chain on the pump.
+ * @return Returns true if the operation was queued.
+ */
+ static bool addToPump(
+ LLPumpIO* pump,
+ F32 in_seconds,
+ const LLPumpIO::chain_t& chain,
+ F32 chain_timeout);
+};
+
+/**
+ * @class LLChainIOFactory
+ * @brief This class is an abstract base class for building io chains.
+ *
+ * This declares an abstract base class for a chain factory. The
+ * factory is used to connect an input pipe to the first pipe in the
+ * chain, and an output pipe to the last pipe in the chain. This will
+ * allow easy construction for buffer based io like services to for
+ * API centered IO while abstracting the input and output to simple
+ * data passing.
+ * To use this class, you should derive a class which implements the
+ * <code>build</code> method.
+ */
+class LLChainIOFactory
+{
+public:
+ // Constructor
+ LLChainIOFactory();
+
+ // Destructor
+ virtual ~LLChainIOFactory();
+
+ /**
+ * @brief Build the chian with in as the first and end as the last
+ *
+ * The caller of the LLChainIOFactory is responsible for managing
+ * the memory of the in pipe. All of the chains generated by the
+ * factory will be ref counted as usual, so the caller will also
+ * need to break the links in the chain.
+ * @param chain The chain which will have new pipes appended
+ * @param context A context for use by this factory if you choose
+ * @retrun Returns true if the call was successful.
+ */
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const = 0;
+
+protected:
+};
+
+/**
+ * @class LLSimpleIOFactory
+ * @brief Basic implementation for making a factory that returns a
+ * 'chain' of one object
+ */
+template<class Pipe>
+class LLSimpleIOFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe));
+ return true;
+ }
+};
+
+/**
+ * @class LLCloneIOFactory
+ * @brief Implementation for a facory which copies a particular pipe.
+ */
+template<class Pipe>
+class LLCloneIOFactory : public LLChainIOFactory
+{
+public:
+ LLCloneIOFactory(Pipe* original) :
+ mHandle(original),
+ mOriginal(original) {}
+
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe(*mOriginal)));
+ return true;
+ }
+
+protected:
+ LLIOPipe::ptr_t mHandle;
+ Pipe* mOriginal;
+};
+
+#endif // LL_LLCHAINIO_H
diff --git a/indra/llmessage/llcircuit.cpp b/indra/llmessage/llcircuit.cpp
new file mode 100644
index 0000000000..d3ef92e4a7
--- /dev/null
+++ b/indra/llmessage/llcircuit.cpp
@@ -0,0 +1,1382 @@
+/**
+ * @file llcircuit.cpp
+ * @brief Class to track UDP endpoints for the message system.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if LL_WINDOWS
+
+#include <process.h>
+
+#else
+
+#if LL_LINUX
+#include <dlfcn.h> // RTLD_LAZY
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#endif
+
+
+#if !defined(USE_CIRCUIT_LIST)
+#include <algorithm>
+#endif
+#include <sstream>
+#include <iterator>
+#include <stack>
+
+#include "llcircuit.h"
+
+#include "message.h"
+#include "llrand.h"
+#include "llstl.h"
+#include "lltransfermanager.h"
+
+const F32 PING_INTERVAL = 5.f; // seconds
+const S32 PING_START_BLOCK = 3; // How many pings behind we have to be to consider ourself blocked.
+const S32 PING_RELEASE_BLOCK = 2; // How many pings behind we have to be to consider ourself unblocked.
+
+const F32 TARGET_PERIOD_LENGTH = 5.f; // seconds
+const F32 LL_DUPLICATE_SUPPRESSION_TIMEOUT = 60.f; //seconds - this can be long, as time-based cleanup is
+ // only done when wrapping packetids, now...
+
+LLCircuitData::LLCircuitData(const LLHost &host, TPACKETID in_id)
+: mHost (host),
+ mWrapID(0),
+ mPacketsOutID(0),
+ mPacketsInID(in_id),
+ mHighestPacketID(in_id),
+ mTrusted(FALSE),
+ mbAllowTimeout(TRUE),
+ mbAlive(TRUE),
+ mBlocked(FALSE),
+ mPingTime(0.0),
+ mLastPingSendTime(0.0),
+ mLastPingReceivedTime(0.0),
+ mNextPingSendTime(0.0),
+ mPingsInTransit(0),
+ mLastPingID(0),
+ mPingDelay(INITIAL_PING_VALUE_MSEC),
+ mPingDelayAveraged((F32)INITIAL_PING_VALUE_MSEC),
+ mUnackedPacketCount(0),
+ mUnackedPacketBytes(0),
+ mLocalEndPointID(),
+ mPacketsOut(0),
+ mPacketsIn(0),
+ mPacketsLost(0),
+ mBytesIn(0),
+ mBytesOut(0),
+ mLastPeriodLength(-1.f),
+ mBytesInLastPeriod(0),
+ mBytesOutLastPeriod(0),
+ mBytesInThisPeriod(0),
+ mBytesOutThisPeriod(0),
+ mPeakBPSIn(0),
+ mPeakBPSOut(0),
+ mPeriodTime(0.0),
+ mExistenceTimer(),
+ mCurrentResendCount(0)
+{
+ // Need to guarantee that this time is up to date, we may be creating a circuit even though we haven't been
+ // running a message system loop.
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds(TRUE);
+ F32 distribution_offset = frand(1.0f);
+
+ mPingTime = mt_sec;
+ mLastPingSendTime = mt_sec + PING_INTERVAL * distribution_offset;
+ mLastPingReceivedTime = mt_sec;
+ mNextPingSendTime = mLastPingSendTime + 0.95*PING_INTERVAL + frand(0.1f*PING_INTERVAL);
+ mPeriodTime = mt_sec;
+
+ mTimeoutCallback = NULL;
+ mTimeoutUserData = NULL;
+
+ mLocalEndPointID.generate();
+}
+
+
+LLCircuitData::~LLCircuitData()
+{
+ LLReliablePacket *packetp = NULL;
+
+ // Clean up all pending transfers.
+ gTransferManager.cleanupConnection(mHost);
+
+ // remove all pending reliable messages on this circuit
+ std::vector<TPACKETID> doomed;
+ reliable_iter iter;
+ reliable_iter end = mUnackedPackets.end();
+ for(iter = mUnackedPackets.begin(); iter != end; ++iter)
+ {
+ packetp = iter->second;
+ gMessageSystem->mFailedResendPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ doomed.push_back(packetp->mPacketID);
+ }
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_CIRCUIT_GONE);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ delete packetp;
+ }
+
+ // remove all pending final retry reliable messages on this circuit
+ end = mFinalRetryPackets.end();
+ for(iter = mFinalRetryPackets.begin(); iter != end; ++iter)
+ {
+ packetp = iter->second;
+ gMessageSystem->mFailedResendPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ doomed.push_back(packetp->mPacketID);
+ }
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_CIRCUIT_GONE);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ delete packetp;
+ }
+
+ // log aborted reliable packets for this circuit.
+ if(gMessageSystem->mVerboseLog && !doomed.empty())
+ {
+ std::ostringstream str;
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ str << "MSG: -> " << mHost << "\tABORTING RELIABLE:\t";
+ std::copy(doomed.begin(), doomed.end(), append);
+ llinfos << str.str().c_str() << llendl;
+ }
+}
+
+
+void LLCircuitData::ackReliablePacket(TPACKETID packet_num)
+{
+ reliable_iter iter;
+ LLReliablePacket *packetp;
+
+ iter = mUnackedPackets.find(packet_num);
+ if (iter != mUnackedPackets.end())
+ {
+ packetp = iter->second;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+ if (packetp->mCallback)
+ {
+ if (packetp->mTimeout < 0.f) // negative timeout will always return timeout even for successful ack, for debugging
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+ else
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_NOERR);
+ }
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ // Cleanup
+ delete packetp;
+ mUnackedPackets.erase(iter);
+ return;
+ }
+
+ iter = mFinalRetryPackets.find(packet_num);
+ if (iter != mFinalRetryPackets.end())
+ {
+ packetp = iter->second;
+ // llinfos << "Packet " << packet_num << " removed from the pending list" << llendl;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << packetp->mHost << "\tRELIABLE ACKED:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+ if (packetp->mCallback)
+ {
+ if (packetp->mTimeout < 0.f) // negative timeout will always return timeout even for successful ack, for debugging
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+ else
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_NOERR);
+ }
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ // Cleanup
+ delete packetp;
+ mFinalRetryPackets.erase(iter);
+ }
+ else
+ {
+ // Couldn't find this packet on either of the unacked lists.
+ // maybe it's a duplicate ack?
+ }
+}
+
+
+
+S32 LLCircuitData::resendUnackedPackets(const F64 now)
+{
+ S32 resent_packets = 0;
+ LLReliablePacket *packetp;
+
+
+ //
+ // Theoretically we should search through the list for the packet with the oldest
+ // packet ID, as otherwise when we WRAP we will resend reliable packets out of order.
+ // Since resends are ALREADY out of order, and wrapping is highly rare (16+million packets),
+ // I'm not going to worry about this for now - djs
+ //
+
+ reliable_iter iter;
+ BOOL have_resend_overflow = FALSE;
+ for (iter = mUnackedPackets.begin(); iter != mUnackedPackets.end();)
+ {
+ packetp = iter->second;
+
+ // Only check overflow if we haven't had one yet.
+ if (!have_resend_overflow)
+ {
+ have_resend_overflow = mThrottles.checkOverflow(TC_RESEND, 0);
+ }
+
+ if (have_resend_overflow)
+ {
+ // We've exceeded our bandwidth for resends.
+ // Time to stop trying to send them.
+
+ // If we have too many unacked packets, we need to start dropping expired ones.
+ if (mUnackedPacketBytes > 512000)
+ {
+ if (now > packetp->mExpirationTime)
+ {
+ // This circuit has overflowed. Do not retry. Do not pass go.
+ packetp->mRetries = 0;
+ // Remove it from this list and add it to the final list.
+ mUnackedPackets.erase(iter++);
+ mFinalRetryPackets[packetp->mPacketID] = packetp;
+ }
+ else
+ {
+ ++iter;
+ }
+ // Move on to the next unacked packet.
+ continue;
+ }
+
+ if (mUnackedPacketBytes > 256000 && !(getPacketsOut() % 1024))
+ {
+ // Warn if we've got a lot of resends waiting.
+ llwarns << mHost << " has " << mUnackedPacketBytes
+ << " bytes of reliable messages waiting" << llendl;
+ }
+ // Stop resending. There are less than 512000 unacked packets.
+ break;
+ }
+
+ if (now > packetp->mExpirationTime)
+ {
+ packetp->mRetries--;
+
+ // retry
+ mCurrentResendCount++;
+
+ gMessageSystem->mResentPackets++;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << packetp->mHost
+ << "\tRESENDING RELIABLE:\t" << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ packetp->mBuffer[0] |= LL_RESENT_FLAG; // tag packet id as being a resend
+
+ gMessageSystem->mPacketRing.sendPacket(packetp->mSocket,
+ (char *)packetp->mBuffer, packetp->mBufferLength,
+ packetp->mHost);
+
+ mThrottles.throttleOverflow(TC_RESEND, packetp->mBufferLength * 8.f);
+
+ // The new method, retry time based on ping
+ if (packetp->mPingBasedRetry)
+ {
+ packetp->mExpirationTime = now + llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, (LL_RELIABLE_TIMEOUT_FACTOR * getPingDelayAveraged()));
+ }
+ else
+ {
+ // custom, constant retry time
+ packetp->mExpirationTime = now + packetp->mTimeout;
+ }
+
+ if (!packetp->mRetries)
+ {
+ // Last resend, remove it from this list and add it to the final list.
+ mUnackedPackets.erase(iter++);
+ mFinalRetryPackets[packetp->mPacketID] = packetp;
+ }
+ else
+ {
+ // Don't remove it yet, it still gets to try to resend at least once.
+ ++iter;
+ }
+ resent_packets++;
+ }
+ else
+ {
+ // Don't need to do anything with this packet, keep iterating.
+ ++iter;
+ }
+ }
+
+
+ for (iter = mFinalRetryPackets.begin(); iter != mFinalRetryPackets.end();)
+ {
+ packetp = iter->second;
+ if (now > packetp->mExpirationTime)
+ {
+ // fail (too many retries)
+ //llinfos << "Packet " << packetp->mPacketID << " removed from the pending list: exceeded retry limit" << llendl;
+ //if (packetp->mMessageName)
+ //{
+ // llinfos << "Packet name " << packetp->mMessageName << llendl;
+ //}
+ gMessageSystem->mFailedResendPackets++;
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << packetp->mHost << "\tABORTING RELIABLE:\t"
+ << packetp->mPacketID;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ if (packetp->mCallback)
+ {
+ packetp->mCallback(packetp->mCallbackData,LL_ERR_TCP_TIMEOUT);
+ }
+
+ // Update stats
+ mUnackedPacketCount--;
+ mUnackedPacketBytes -= packetp->mBufferLength;
+
+ mFinalRetryPackets.erase(iter++);
+ delete packetp;
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ return mUnackedPacketCount;
+}
+
+
+LLCircuit::LLCircuit() : mLastCircuit(NULL)
+{
+}
+
+LLCircuit::~LLCircuit()
+{
+ // delete pointers in the map.
+ std::for_each(mCircuitData.begin(),
+ mCircuitData.end(),
+ llcompose1(
+ DeletePointerFunctor<LLCircuitData>(),
+ llselect2nd<circuit_data_map::value_type>()));
+}
+
+LLCircuitData *LLCircuit::addCircuitData(const LLHost &host, TPACKETID in_id)
+{
+ // This should really validate if one already exists
+ llinfos << "LLCircuit::addCircuitData for " << host << llendl;
+ LLCircuitData *tempp = new LLCircuitData(host, in_id);
+ mCircuitData.insert(circuit_data_map::value_type(host, tempp));
+ mPingSet.insert(tempp);
+
+ mLastCircuit = tempp;
+ return tempp;
+}
+
+void LLCircuit::removeCircuitData(const LLHost &host)
+{
+ llinfos << "LLCircuit::removeCircuitData for " << host << llendl;
+ mLastCircuit = NULL;
+ circuit_data_map::iterator it = mCircuitData.find(host);
+ if(it != mCircuitData.end())
+ {
+ LLCircuitData *cdp = it->second;
+ mCircuitData.erase(it);
+
+ LLCircuit::ping_set_t::iterator psit = mPingSet.find(cdp);
+ if (psit != mPingSet.end())
+ {
+ mPingSet.erase(psit);
+ }
+ else
+ {
+ llwarns << "Couldn't find entry for next ping in ping set!" << llendl;
+ }
+
+ // Clean up from optimization maps
+ mUnackedCircuitMap.erase(host);
+ mSendAckMap.erase(host);
+ delete cdp;
+ }
+
+ // This also has to happen AFTER we nuke the circuit, because various
+ // callbacks for the circuit may result in messages being sent to
+ // this circuit, and the setting of mLastCircuit. We don't check
+ // if the host matches, but we don't really care because mLastCircuit
+ // is an optimization, and this happens VERY rarely.
+ mLastCircuit = NULL;
+}
+
+void LLCircuitData::setAlive(BOOL b_alive)
+{
+ if (mbAlive != b_alive)
+ {
+ mPacketsOutID = 0;
+ mPacketsInID = 0;
+ mbAlive = b_alive;
+ }
+ if (b_alive)
+ {
+ mLastPingReceivedTime = LLMessageSystem::getMessageTimeSeconds();
+ mPingsInTransit = 0;
+ mBlocked = FALSE;
+ }
+}
+
+
+void LLCircuitData::setAllowTimeout(BOOL allow)
+{
+ mbAllowTimeout = allow;
+
+ if (allow)
+ {
+ // resuming circuit
+ // make sure it's alive
+ setAlive(TRUE);
+ }
+}
+
+
+// Reset per-period counters if necessary.
+void LLCircuitData::checkPeriodTime()
+{
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ F64 period_length = mt_sec - mPeriodTime;
+ if ( period_length > TARGET_PERIOD_LENGTH)
+ {
+ F32 bps_in = (F32)(mBytesInThisPeriod * 8.f / period_length);
+ if (bps_in > mPeakBPSIn)
+ {
+ mPeakBPSIn = bps_in;
+ }
+
+ F32 bps_out = (F32)(mBytesOutThisPeriod * 8.f / period_length);
+ if (bps_out > mPeakBPSOut)
+ {
+ mPeakBPSOut = bps_out;
+ }
+
+ mBytesInLastPeriod = mBytesInThisPeriod;
+ mBytesOutLastPeriod = mBytesOutThisPeriod;
+ mBytesInThisPeriod = 0;
+ mBytesOutThisPeriod = 0;
+ mLastPeriodLength = (F32)period_length;
+
+ mPeriodTime = mt_sec;
+ }
+}
+
+
+void LLCircuitData::addBytesIn(S32 bytes)
+{
+ mBytesIn += bytes;
+ mBytesInThisPeriod += bytes;
+}
+
+
+void LLCircuitData::addBytesOut(S32 bytes)
+{
+ mBytesOut += bytes;
+ mBytesOutThisPeriod += bytes;
+}
+
+
+void LLCircuitData::addReliablePacket(S32 mSocket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params)
+{
+ LLReliablePacket *packet_info;
+
+ packet_info = new LLReliablePacket(mSocket, buf_ptr, buf_len, params);
+
+ mUnackedPacketCount++;
+ mUnackedPacketBytes += packet_info->mBufferLength;
+
+ if (params && params->mRetries)
+ {
+ mUnackedPackets[packet_info->mPacketID] = packet_info;
+ }
+ else
+ {
+ mFinalRetryPackets[packet_info->mPacketID] = packet_info;
+ }
+}
+
+
+void LLCircuit::resendUnackedPackets(S32& unacked_list_length, S32& unacked_list_size)
+{
+ F64 now = LLMessageSystem::getMessageTimeSeconds();
+ unacked_list_length = 0;
+ unacked_list_size = 0;
+
+ LLCircuitData* circ;
+ circuit_data_map::iterator end = mUnackedCircuitMap.end();
+ for(circuit_data_map::iterator it = mUnackedCircuitMap.begin(); it != end; ++it)
+ {
+ circ = (*it).second;
+ unacked_list_length += circ->resendUnackedPackets(now);
+ unacked_list_size += circ->getUnackedPacketBytes();
+ }
+}
+
+
+BOOL LLCircuitData::isDuplicateResend(TPACKETID packetnum)
+{
+ return (mRecentlyReceivedReliablePackets.find(packetnum) != mRecentlyReceivedReliablePackets.end());
+}
+
+
+void LLCircuit::dumpResends()
+{
+ circuit_data_map::iterator end = mCircuitData.end();
+ for(circuit_data_map::iterator it = mCircuitData.begin(); it != end; ++it)
+ {
+ (*it).second->dumpResendCountAndReset();
+ }
+}
+
+LLCircuitData* LLCircuit::findCircuit(const LLHost& host) const
+{
+ // An optimization on finding the previously found circuit.
+ if (mLastCircuit && (mLastCircuit->mHost == host))
+ {
+ return mLastCircuit;
+ }
+
+ circuit_data_map::const_iterator it = mCircuitData.find(host);
+ if(it == mCircuitData.end())
+ {
+ return NULL;
+ }
+ mLastCircuit = it->second;
+ return mLastCircuit;
+}
+
+
+BOOL LLCircuit::isCircuitAlive(const LLHost& host) const
+{
+ LLCircuitData *cdp = findCircuit(host);
+ if(cdp)
+ {
+ return cdp->mbAlive;
+ }
+
+ return FALSE;
+}
+
+void LLCircuitData::setTimeoutCallback(void (*callback_func)(const LLHost &host, void *user_data), void *user_data)
+{
+ mTimeoutCallback = callback_func;
+ mTimeoutUserData = user_data;
+}
+
+void LLCircuitData::checkPacketInID(TPACKETID id, BOOL receive_resent)
+{
+ // Done as floats so we don't have to worry about running out of room
+ // with U32 getting poked into an S32.
+ F32 delta = (F32)mHighestPacketID - (F32)id;
+ if (delta > (0.5f*LL_MAX_OUT_PACKET_ID))
+ {
+ // We've almost definitely wrapped, reset the mLastPacketID to be low again.
+ mHighestPacketID = id;
+ }
+ else if (delta < (-0.5f*LL_MAX_OUT_PACKET_ID))
+ {
+ // This is almost definitely an old packet coming in after a wrap, ignore it.
+ }
+ else
+ {
+ mHighestPacketID = llmax(mHighestPacketID, id);
+ }
+
+
+ // Have we received anything on this circuit yet?
+ if (0 == mPacketsIn)
+ {
+ // Must be first packet from unclosed circuit.
+ mPacketsIn++;
+ setPacketInID((id + 1) % LL_MAX_OUT_PACKET_ID);
+
+ return;
+ }
+
+ mPacketsIn++;
+
+
+ // now, check to see if we've got a gap
+ if ((mPacketsInID == id))
+ {
+ // nope! bump and wrap the counter, then return
+ mPacketsInID++;
+ mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID;
+ }
+ else if (id < mWrapID)
+ {
+ // id < mWrapID will happen if the first few packets are out of order. . .
+ // at that point we haven't marked anything "potentially lost" and
+ // the out-of-order packet will cause a full wrap marking all the IDs "potentially lost"
+
+ // do nothing
+ }
+ else
+ {
+ // we have a gap! if that id is in the map, remove it from the map, leave mCurrentCircuit->mPacketsInID
+ // alone
+ // otherwise, walk from mCurrentCircuit->mPacketsInID to id with wrapping, adding the values to the map
+ // and setting mPacketsInID to id + 1 % LL_MAX_OUT_PACKET_ID
+
+ if (mPotentialLostPackets.find(id) != mPotentialLostPackets.end())
+ {
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tRECOVERING LOST:\t" << id;
+ llinfos << str.str().c_str() << llendl;
+ }
+ // llinfos << "removing potential lost: " << id << llendl;
+ mPotentialLostPackets.erase(id);
+ }
+ else if (!receive_resent) // don't freak out over out-of-order reliable resends
+ {
+ U64 time = LLMessageSystem::getMessageTimeUsecs();
+ TPACKETID index = mPacketsInID;
+ S32 gap_count = 0;
+ if ((index < id) && ((id - index) < 16))
+ {
+ while (index != id)
+ {
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tPACKET GAP:\t"
+ << index;
+ llinfos << str.str().c_str() << llendl;
+ }
+
+// llinfos << "adding potential lost: " << index << llendl;
+ mPotentialLostPackets[index] = time;
+ index++;
+ index = index % LL_MAX_OUT_PACKET_ID;
+ gap_count++;
+ }
+ }
+ else
+ {
+ llinfos << "packet_out_of_order - got packet " << id << " expecting " << index << " from " << mHost << llendl;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tPACKET GAP:\t"
+ << id << " expected " << index;
+ llinfos << str.str().c_str() << llendl;
+ }
+ }
+
+ mPacketsInID = id + 1;
+ mPacketsInID = (mPacketsInID) % LL_MAX_OUT_PACKET_ID;
+
+ if (gap_count > 128)
+ {
+ llwarns << "Packet loss gap filler running amok!" << llendl;
+ }
+ else if (gap_count > 16)
+ {
+ llwarns << "Sustaining large amounts of packet loss!" << llendl;
+ }
+
+ }
+ }
+}
+
+
+void LLCircuit::updateWatchDogTimers(LLMessageSystem *msgsys)
+{
+ F64 cur_time = LLMessageSystem::getMessageTimeSeconds();
+ S32 count = mPingSet.size();
+ S32 cur = 0;
+
+ // Only process each circuit once at most, stop processing if no circuits
+ while((cur < count) && !mPingSet.empty())
+ {
+ cur++;
+
+ LLCircuit::ping_set_t::iterator psit = mPingSet.begin();
+ LLCircuitData *cdp = *psit;
+
+ if (!cdp->mbAlive)
+ {
+ // We suspect that this case should never happen, given how
+ // the alive status is set.
+ // Skip over dead circuits, just add the ping interval and push it to the back
+ // Always remember to remove it from the set before changing the sorting
+ // key (mNextPingSendTime)
+ mPingSet.erase(psit);
+ cdp->mNextPingSendTime = cur_time + PING_INTERVAL;
+ mPingSet.insert(cdp);
+ continue;
+ }
+ else
+ {
+ // Check to see if this needs a ping
+ if (cur_time < cdp->mNextPingSendTime)
+ {
+ // This circuit doesn't need a ping, break out because
+ // we have a sorted list, thus no more circuits need pings
+ break;
+ }
+
+ // Update watchdog timers
+ if (cdp->updateWatchDogTimers(msgsys))
+ {
+ // Randomize our pings a bit by doing some up to 5% early or late
+ F64 dt = 0.95f*PING_INTERVAL + frand(0.1f*PING_INTERVAL);
+
+ // Remove it, and reinsert it with the new next ping time.
+ // Always remove before changing the sorting key.
+ mPingSet.erase(psit);
+ cdp->mNextPingSendTime = cur_time + dt;
+ mPingSet.insert(cdp);
+
+ // Update our throttles
+ cdp->mThrottles.dynamicAdjust();
+
+ // Update some stats, this is not terribly important
+ cdp->checkPeriodTime();
+ }
+ else
+ {
+ // This mPingSet.erase isn't necessary, because removing the circuit will
+ // remove the ping set.
+ //mPingSet.erase(psit);
+ removeCircuitData(cdp->mHost);
+ }
+ }
+ }
+}
+
+
+BOOL LLCircuitData::updateWatchDogTimers(LLMessageSystem *msgsys)
+{
+ F64 cur_time = LLMessageSystem::getMessageTimeSeconds();
+ mLastPingSendTime = cur_time;
+
+ if (!checkCircuitTimeout())
+ {
+ // Pass this back to the calling LLCircuit, this circuit needs to be cleaned up.
+ return FALSE;
+ }
+
+ // WARNING!
+ // Duplicate suppression can FAIL if packets are delivered out of
+ // order, although it's EXTREMELY unlikely. It would require
+ // that the ping get delivered out of order enough that the ACK
+ // for the packet that it was out of order with was received BEFORE
+ // the ping was sent.
+
+ // Find the current oldest reliable packetID
+ // This is to handle the case if we actually manage to wrap our
+ // packet IDs - the oldest will actually have a higher packet ID
+ // than the current.
+ BOOL wrapped = FALSE;
+ reliable_iter iter;
+ iter = mUnackedPackets.upper_bound(getPacketOutID());
+ if (iter == mUnackedPackets.end())
+ {
+ // Nothing AFTER this one, so we want the lowest packet ID
+ // then.
+ iter = mUnackedPackets.begin();
+ wrapped = TRUE;
+ }
+
+ TPACKETID packet_id = 0;
+
+ // Check against the "final" packets
+ BOOL wrapped_final = FALSE;
+ reliable_iter iter_final;
+ iter_final = mFinalRetryPackets.upper_bound(getPacketOutID());
+ if (iter_final == mFinalRetryPackets.end())
+ {
+ iter_final = mFinalRetryPackets.begin();
+ wrapped_final = TRUE;
+ }
+
+ //llinfos << mHost << " - unacked count " << mUnackedPackets.size() << llendl;
+ //llinfos << mHost << " - final count " << mFinalRetryPackets.size() << llendl;
+ if (wrapped != wrapped_final)
+ {
+ // One of the "unacked" or "final" lists hasn't wrapped. Whichever one
+ // hasn't has the oldest packet.
+ if (!wrapped)
+ {
+ // Hasn't wrapped, so the one on the
+ // unacked packet list is older
+ packet_id = iter->first;
+ //llinfos << mHost << ": nowrapped unacked" << llendl;
+ }
+ else
+ {
+ packet_id = iter_final->first;
+ //llinfos << mHost << ": nowrapped final" << llendl;
+ }
+ }
+ else
+ {
+ // They both wrapped, we can just use the minimum of the two.
+ if ((iter == mUnackedPackets.end()) && (iter_final == mFinalRetryPackets.end()))
+ {
+ // Wow! No unacked packets at all!
+ // Send the ID of the last packet we sent out.
+ // This will flush all of the destination's
+ // unacked packets, theoretically.
+ //llinfos << mHost << ": No unacked!" << llendl;
+ packet_id = getPacketOutID();
+ }
+ else
+ {
+ BOOL had_unacked = FALSE;
+ if (iter != mUnackedPackets.end())
+ {
+ // Unacked list has the lowest so far
+ packet_id = iter->first;
+ had_unacked = TRUE;
+ //llinfos << mHost << ": Unacked" << llendl;
+ }
+
+ if (iter_final != mFinalRetryPackets.end())
+ {
+ // Use the lowest of the unacked list and the final list
+ if (had_unacked)
+ {
+ // Both had a packet, use the lowest.
+ packet_id = llmin(packet_id, iter_final->first);
+ //llinfos << mHost << ": Min of unacked/final" << llendl;
+ }
+ else
+ {
+ // Only the final had a packet, use it.
+ packet_id = iter_final->first;
+ //llinfos << mHost << ": Final!" << llendl;
+ }
+ }
+ }
+ }
+
+ // Send off the another ping.
+ pingTimerStart();
+ msgsys->newMessageFast(_PREHASH_StartPingCheck);
+ msgsys->nextBlock(_PREHASH_PingID);
+ msgsys->addU8Fast(_PREHASH_PingID, nextPingID());
+ msgsys->addU32Fast(_PREHASH_OldestUnacked, packet_id);
+ msgsys->sendMessage(mHost);
+
+ // Also do lost packet accounting.
+ // Check to see if anything on our lost list is old enough to
+ // be considered lost
+
+ LLCircuitData::packet_time_map::iterator it;
+ U64 timeout = (U64)(1000000.0*llmin(LL_MAX_LOST_TIMEOUT, getPingDelayAveraged() * LL_LOST_TIMEOUT_FACTOR));
+
+ U64 mt_usec = LLMessageSystem::getMessageTimeUsecs();
+ for (it = mPotentialLostPackets.begin(); it != mPotentialLostPackets.end(); )
+ {
+ U64 delta_t_usec = mt_usec - (*it).second;
+ if (delta_t_usec > timeout)
+ {
+ // let's call this one a loss!
+ mPacketsLost++;
+ gMessageSystem->mDroppedPackets++;
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << mHost << "\tLOST PACKET:\t"
+ << (*it).first;
+ llinfos << str.str().c_str() << llendl;
+ }
+ mPotentialLostPackets.erase((*(it++)).first);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ return TRUE;
+}
+
+
+void LLCircuitData::clearDuplicateList(TPACKETID oldest_id)
+{
+ // purge old data from the duplicate suppression queue
+
+ // we want to KEEP all x where oldest_id <= x <= last incoming packet, and delete everything else.
+
+ //llinfos << mHost << ": clearing before oldest " << oldest_id << llendl;
+ //llinfos << "Recent list before: " << mRecentlyReceivedReliablePackets.size() << llendl;
+ if (oldest_id < mHighestPacketID)
+ {
+ // Clean up everything with a packet ID less than oldest_id.
+ packet_time_map::iterator pit_start;
+ packet_time_map::iterator pit_end;
+ pit_start = mRecentlyReceivedReliablePackets.begin();
+ pit_end = mRecentlyReceivedReliablePackets.lower_bound(oldest_id);
+ mRecentlyReceivedReliablePackets.erase(pit_start, pit_end);
+ }
+
+ // Do timeout checks on everything with an ID > mHighestPacketID.
+ // This should be empty except for wrapping IDs. Thus, this should be
+ // highly rare.
+ U64 mt_usec = LLMessageSystem::getMessageTimeUsecs();
+
+ packet_time_map::iterator pit;
+ for(pit = mRecentlyReceivedReliablePackets.upper_bound(mHighestPacketID);
+ pit != mRecentlyReceivedReliablePackets.end(); )
+ {
+ // Validate that the packet ID seems far enough away
+ if ((pit->first - mHighestPacketID) < 100)
+ {
+ llwarns << "Probably incorrectly timing out non-wrapped packets!" << llendl;
+ }
+ U64 delta_t_usec = mt_usec - (*pit).second;
+ F64 delta_t_sec = delta_t_usec * SEC_PER_USEC;
+ if (delta_t_sec > LL_DUPLICATE_SUPPRESSION_TIMEOUT)
+ {
+ // enough time has elapsed we're not likely to get a duplicate on this one
+ llinfos << "Clearing " << pit->first << " from recent list" << llendl;
+ mRecentlyReceivedReliablePackets.erase(pit++);
+ }
+ else
+ {
+ ++pit;
+ }
+ }
+ //llinfos << "Recent list after: " << mRecentlyReceivedReliablePackets.size() << llendl;
+}
+
+BOOL LLCircuitData::checkCircuitTimeout()
+{
+ F64 time_since_last_ping = LLMessageSystem::getMessageTimeSeconds() - mLastPingReceivedTime;
+
+ // Nota Bene: This needs to be turned off if you are debugging multiple simulators
+ if (time_since_last_ping > PING_INTERVAL_MAX)
+ {
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " last ping " << time_since_last_ping << " seconds ago." <<llendl;
+ setAlive(FALSE);
+ if (mTimeoutCallback)
+ {
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " calling callback." << llendl;
+ mTimeoutCallback(mHost, mTimeoutUserData);
+ }
+ if (!isAlive())
+ {
+ // The callback didn't try and resurrect the circuit. We should kill it.
+ llwarns << "LLCircuitData::checkCircuitTimeout for " << mHost << " still dead, dropping." << llendl;
+ return FALSE;
+ }
+ }
+ else if (time_since_last_ping > PING_INTERVAL_ALARM)
+ {
+ //llwarns << "Unresponsive circuit: " << mHost << ": " << time_since_last_ping << " seconds since last ping."<< llendl;
+ }
+ return TRUE;
+}
+
+// Call this method when a reliable message comes in - this will
+// correctly place the packet in the correct list to be acked later.
+BOOL LLCircuitData::collectRAck(TPACKETID packet_num)
+{
+ if (mAcks.empty())
+ {
+ // First extra ack, we need to add ourselves to the list of circuits that need to send acks
+ gMessageSystem->mCircuitInfo.mSendAckMap[mHost] = this;
+ }
+
+ mAcks.push_back(packet_num);
+ return TRUE;
+}
+
+// this method is called during the message system processAcks() to
+// send out any acks that did not get sent already.
+void LLCircuit::sendAcks()
+{
+ LLCircuitData* cd;
+ circuit_data_map::iterator end = mSendAckMap.end();
+ for(circuit_data_map::iterator it = mSendAckMap.begin(); it != end; ++it)
+ {
+ cd = (*it).second;
+
+ S32 count = (S32)cd->mAcks.size();
+ if(count > 0)
+ {
+ // send the packet acks
+ S32 acks_this_packet = 0;
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(acks_this_packet == 0)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_PacketAck);
+ }
+ gMessageSystem->nextBlockFast(_PREHASH_Packets);
+ gMessageSystem->addU32Fast(_PREHASH_ID, cd->mAcks[i]);
+ ++acks_this_packet;
+ if(acks_this_packet > 250)
+ {
+ gMessageSystem->sendMessage(cd->mHost);
+ acks_this_packet = 0;
+ }
+ }
+ if(acks_this_packet > 0)
+ {
+ gMessageSystem->sendMessage(cd->mHost);
+ }
+
+ if(gMessageSystem->mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << cd->mHost << "\tPACKET ACKS:\t";
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ std::copy(cd->mAcks.begin(), cd->mAcks.end(), append);
+ llinfos << str.str().c_str() << llendl;
+ }
+
+ // empty out the acks list
+ cd->mAcks.clear();
+ }
+ }
+
+ // All acks have been sent, clear the map
+ mSendAckMap.clear();
+}
+
+
+std::ostream& operator<<(std::ostream& s, LLCircuitData& circuit)
+{
+ F32 age = circuit.mExistenceTimer.getElapsedTimeF32();
+
+ using namespace std;
+ s << "Circuit " << circuit.mHost << " ";
+ s << circuit.mRemoteID << " ";
+ s << (circuit.mbAlive ? "Alive" : "Not Alive") << " ";
+ s << (circuit.mbAllowTimeout ? "Timeout Allowed" : "Timeout Not Allowed");
+ s << endl;
+
+ s << " Packets Lost: " << circuit.mPacketsLost;
+ s << " Measured Ping: " << circuit.mPingDelay;
+ s << " Averaged Ping: " << circuit.mPingDelayAveraged;
+ s << endl;
+
+ s << "Global In/Out " << S32(age) << " sec";
+ s << " KBytes: " << circuit.mBytesIn / 1024 << "/" << circuit.mBytesOut / 1024;
+ s << " Kbps: ";
+ s << S32(circuit.mBytesIn * 8.f / circuit.mExistenceTimer.getElapsedTimeF32() / 1024.f);
+ s << "/";
+ s << S32(circuit.mBytesOut * 8.f / circuit.mExistenceTimer.getElapsedTimeF32() / 1024.f);
+ s << " Packets: " << circuit.mPacketsIn << "/" << circuit.mPacketsOut;
+ s << endl;
+
+ s << "Recent In/Out " << S32(circuit.mLastPeriodLength) << " sec";
+ s << " KBytes: ";
+ s << circuit.mBytesInLastPeriod / 1024;
+ s << "/";
+ s << circuit.mBytesOutLastPeriod / 1024;
+ s << " Kbps: ";
+ s << S32(circuit.mBytesInLastPeriod * 8.f / circuit.mLastPeriodLength / 1024.f);
+ s << "/";
+ s << S32(circuit.mBytesOutLastPeriod * 8.f / circuit.mLastPeriodLength / 1024.f);
+ s << " Peak Kbps: ";
+ s << S32(circuit.mPeakBPSIn / 1024.f);
+ s << "/";
+ s << S32(circuit.mPeakBPSOut / 1024.f);
+ s << endl;
+
+ return s;
+}
+
+const char* LLCircuitData::getInfoString() const
+{
+ std::ostringstream info;
+ info << "Circuit: " << mHost << std::endl
+ << (mbAlive ? "Alive" : "Not Alive") << std::endl
+ << "Age: " << mExistenceTimer.getElapsedTimeF32() << std::endl;
+ return info.str().c_str();
+}
+
+void LLCircuitData::dumpResendCountAndReset()
+{
+ if (mCurrentResendCount)
+ {
+ llinfos << "Circuit: " << mHost << " resent " << mCurrentResendCount << " packets" << llendl;
+ mCurrentResendCount = 0;
+ }
+}
+
+std::ostream& operator<<(std::ostream& s, LLCircuit &circuit)
+{
+ s << "Circuit Info:" << std::endl;
+ LLCircuit::circuit_data_map::iterator end = circuit.mCircuitData.end();
+ LLCircuit::circuit_data_map::iterator it;
+ for(it = circuit.mCircuitData.begin(); it != end; ++it)
+ {
+ s << *((*it).second) << std::endl;
+ }
+ return s;
+}
+
+const char* LLCircuit::getInfoString() const
+{
+ std::ostringstream info;
+ info << "Circuit Info:" << std::endl;
+ LLCircuit::circuit_data_map::const_iterator end = mCircuitData.end();
+ LLCircuit::circuit_data_map::const_iterator it;
+ for(it = mCircuitData.begin(); it != end; ++it)
+ {
+ info << (*it).second->getInfoString() << std::endl;
+ }
+ return info.str().c_str();
+}
+
+void LLCircuit::getCircuitRange(
+ const LLHost& key,
+ LLCircuit::circuit_data_map::iterator& first,
+ LLCircuit::circuit_data_map::iterator& end)
+{
+ end = mCircuitData.end();
+ first = mCircuitData.upper_bound(key);
+}
+
+TPACKETID LLCircuitData::nextPacketOutID()
+{
+ mPacketsOut++;
+
+ TPACKETID id;
+
+ id = (mPacketsOutID + 1) % LL_MAX_OUT_PACKET_ID;
+
+ if (id < mPacketsOutID)
+ {
+ // we just wrapped on a circuit, reset the wrap ID to zero
+ mWrapID = 0;
+ }
+ mPacketsOutID = id;
+ return id;
+}
+
+
+void LLCircuitData::setPacketInID(TPACKETID id)
+{
+ id = id % LL_MAX_OUT_PACKET_ID;
+ mPacketsInID = id;
+ mRecentlyReceivedReliablePackets.clear();
+
+ mWrapID = id;
+}
+
+
+void LLCircuitData::pingTimerStop(const U8 ping_id)
+{
+ F64 mt_secs = LLMessageSystem::getMessageTimeSeconds();
+
+ // Nota Bene: no averaging of ping times until we get a feel for how this works
+ F64 time = mt_secs - mPingTime;
+ if (time == 0.0)
+ {
+ // Ack, we got our ping response on the same frame! Sigh, let's get a real time otherwise
+ // all of our ping calculations will be skewed.
+ mt_secs = LLMessageSystem::getMessageTimeSeconds(TRUE);
+ }
+ mLastPingReceivedTime = mt_secs;
+
+ // If ping is longer than 1 second, we'll get sequence deltas in the ping.
+ // Approximate by assuming each ping counts for 1 second (slightly low, probably)
+ S32 delta_ping = (S32)mLastPingID - (S32) ping_id;
+ if (delta_ping < 0)
+ {
+ delta_ping += 256;
+ }
+
+ U32 msec = (U32) ((delta_ping*PING_INTERVAL + time) * 1000.f);
+ setPingDelay(msec);
+
+ mPingsInTransit = delta_ping;
+ if (mBlocked && (mPingsInTransit <= PING_RELEASE_BLOCK))
+ {
+ mBlocked = FALSE;
+ }
+}
+
+
+void LLCircuitData::pingTimerStart()
+{
+ mPingTime = LLMessageSystem::getMessageTimeSeconds();
+ mPingsInTransit++;
+
+ if (!mBlocked && (mPingsInTransit > PING_START_BLOCK))
+ {
+ mBlocked = TRUE;
+ }
+}
+
+
+U32 LLCircuitData::getPacketsIn() const
+{
+ return mPacketsIn;
+}
+
+
+S32 LLCircuitData::getBytesIn() const
+{
+ return mBytesIn;
+}
+
+
+S32 LLCircuitData::getBytesOut() const
+{
+ return mBytesOut;
+}
+
+
+U32 LLCircuitData::getPacketsOut() const
+{
+ return mPacketsOut;
+}
+
+
+TPACKETID LLCircuitData::getPacketOutID() const
+{
+ return mPacketsOutID;
+}
+
+
+U32 LLCircuitData::getPacketsLost() const
+{
+ return mPacketsLost;
+}
+
+
+BOOL LLCircuitData::isAlive() const
+{
+ return mbAlive;
+}
+
+
+BOOL LLCircuitData::isBlocked() const
+{
+ return mBlocked;
+}
+
+
+BOOL LLCircuitData::getAllowTimeout() const
+{
+ return mbAllowTimeout;
+}
+
+
+U32 LLCircuitData::getPingDelay() const
+{
+ return mPingDelay;
+}
+
+
+F32 LLCircuitData::getPingInTransitTime()
+{
+ // This may be inaccurate in the case of a circuit that was "dead" and then revived,
+ // but only until the first round trip ping is sent - djs
+ F32 time_since_ping_was_sent = 0;
+
+ if (mPingsInTransit)
+ {
+ time_since_ping_was_sent = (F32)((mPingsInTransit*PING_INTERVAL - 1) + (LLMessageSystem::getMessageTimeSeconds() - mPingTime))*1000.f;
+ }
+
+ return time_since_ping_was_sent;
+}
+
+
+void LLCircuitData::setPingDelay(U32 ping)
+{
+ mPingDelay = ping;
+ mPingDelayAveraged = llmax((F32)ping, getPingDelayAveraged());
+ mPingDelayAveraged = ((1.f - LL_AVERAGED_PING_ALPHA) * mPingDelayAveraged)
+ + (LL_AVERAGED_PING_ALPHA * (F32) ping);
+ mPingDelayAveraged = llclamp(mPingDelayAveraged,
+ LL_AVERAGED_PING_MIN,
+ LL_AVERAGED_PING_MAX);
+}
+
+
+F32 LLCircuitData::getPingDelayAveraged()
+{
+ return llmin(llmax(getPingInTransitTime(), mPingDelayAveraged), LL_AVERAGED_PING_MAX);
+}
+
+
+BOOL LLCircuitData::getTrusted() const
+{
+ return mTrusted;
+}
+
+
+void LLCircuitData::setTrusted(BOOL t)
+{
+ mTrusted = t;
+}
+
+F32 LLCircuitData::getAgeInSeconds() const
+{
+ return mExistenceTimer.getElapsedTimeF32();
+}
diff --git a/indra/llmessage/llcircuit.h b/indra/llmessage/llcircuit.h
new file mode 100644
index 0000000000..003734df29
--- /dev/null
+++ b/indra/llmessage/llcircuit.h
@@ -0,0 +1,315 @@
+/**
+ * @file llcircuit.h
+ * @brief Provides a method for tracking network circuit information
+ * for the UDP message system
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCIRCUIT_H
+#define LL_LLCIRCUIT_H
+
+#include <map>
+#include <vector>
+
+#include "llerror.h"
+#include "linked_lists.h"
+
+#include "lltimer.h"
+#include "timing.h"
+#include "net.h"
+#include "llhost.h"
+#include "llpacketack.h"
+#include "lluuid.h"
+#include "llthrottle.h"
+
+//
+// Constants
+//
+const F32 PING_INTERVAL_MAX = 100.f;
+const F32 PING_INTERVAL_ALARM = 50.f;
+
+
+const F32 LL_AVERAGED_PING_ALPHA = 0.2f; // relaxation constant on ping running average
+const F32 LL_AVERAGED_PING_MAX = 2000; // msec
+const F32 LL_AVERAGED_PING_MIN = 100; // msec // IW: increased to avoid retransmits when a process is slow
+
+const U32 INITIAL_PING_VALUE_MSEC = 1000; // initial value for the ping delay, or for ping delay for an unknown circuit
+
+const TPACKETID LL_MAX_OUT_PACKET_ID = 0x01000000;
+const U8 LL_PACKET_ID_SIZE = 4;
+
+const S32 LL_MAX_RESENT_PACKETS_PER_FRAME = 100;
+const S32 LL_MAX_ACKED_PACKETS_PER_FRAME = 200;
+
+//
+// Prototypes and Predefines
+//
+class LLMessageSystem;
+class LLEncodedDatagramService;
+
+//
+// Classes
+//
+
+
+class LLCircuitData
+{
+public:
+ LLCircuitData(const LLHost &host, TPACKETID in_id);
+ ~LLCircuitData();
+
+ S32 resendUnackedPackets(const F64 now);
+ void clearDuplicateList(TPACKETID oldest_id);
+
+
+ void dumpResendCountAndReset(); // Used for tracking how many resends are being done on a circuit.
+
+
+
+ // Public because stupid message system callbacks uses it.
+ void pingTimerStart();
+ void pingTimerStop(const U8 ping_id);
+ void ackReliablePacket(TPACKETID packet_num);
+
+ // remote computer information
+ const LLUUID& getRemoteID() const { return mRemoteID; }
+ const LLUUID& getRemoteSessionID() const { return mRemoteSessionID; }
+ void setRemoteID(const LLUUID& id) { mRemoteID = id; }
+ void setRemoteSessionID(const LLUUID& id) { mRemoteSessionID = id; }
+
+ void setTrusted(BOOL t);
+
+ // The local end point ID is used when establishing a trusted circuit.
+ // no matching set function for getLocalEndPointID()
+ // mLocalEndPointID should only ever be setup in the LLCircuitData constructor
+ const LLUUID& getLocalEndPointID() const { return mLocalEndPointID; }
+
+ U32 getPingDelay() const;
+ S32 getPingsInTransit() const { return mPingsInTransit; }
+
+ // ACCESSORS
+ BOOL isAlive() const;
+ BOOL isBlocked() const;
+ BOOL getAllowTimeout() const;
+ F32 getPingDelayAveraged();
+ F32 getPingInTransitTime();
+ U32 getPacketsIn() const;
+ S32 getBytesIn() const;
+ S32 getBytesOut() const;
+ U32 getPacketsOut() const;
+ U32 getPacketsLost() const;
+ TPACKETID getPacketOutID() const;
+ BOOL getTrusted() const;
+ F32 getAgeInSeconds() const;
+ S32 getUnackedPacketCount() const { return mUnackedPacketCount; }
+ S32 getUnackedPacketBytes() const { return mUnackedPacketBytes; }
+ F64 getNextPingSendTime() const { return mNextPingSendTime; }
+
+ LLThrottleGroup &getThrottleGroup() { return mThrottles; }
+
+ class less
+ {
+ public:
+ bool operator()(const LLCircuitData* lhs, const LLCircuitData* rhs) const
+ {
+ if (lhs->getNextPingSendTime() < rhs->getNextPingSendTime())
+ {
+ return true;
+ }
+ else if (lhs->getNextPingSendTime() > rhs->getNextPingSendTime())
+ {
+ return false;
+ }
+ else return lhs > rhs;
+ }
+ };
+
+ //
+ // Debugging stuff (not necessary for operation)
+ //
+ void checkPeriodTime(); // Reset per-period counters if necessary.
+ friend std::ostream& operator<<(std::ostream& s, LLCircuitData &circuit);
+ const char* getInfoString() const;
+
+ friend class LLCircuit;
+ friend class LLMessageSystem;
+ friend class LLEncodedDatagramService;
+ friend void crash_on_spaceserver_timeout (const LLHost &host, void *); // HACK, so it has access to setAlive() so it can send a final shutdown message.
+protected:
+ TPACKETID nextPacketOutID();
+ void setPacketInID(TPACKETID id);
+ void checkPacketInID(TPACKETID id, BOOL receive_resent);
+ void setPingDelay(U32 ping);
+ BOOL checkCircuitTimeout(); // Return FALSE if the circuit is dead and should be cleaned up
+
+ void addBytesIn(S32 bytes);
+ void addBytesOut(S32 bytes);
+
+ U8 nextPingID() { mLastPingID++; return mLastPingID; }
+
+ BOOL updateWatchDogTimers(LLMessageSystem *msgsys); // Return FALSE if the circuit is dead and should be cleaned up
+
+ void addReliablePacket(S32 mSocket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params);
+ BOOL isDuplicateResend(TPACKETID packetnum);
+ // Call this method when a reliable message comes in - this will
+ // correctly place the packet in the correct list to be acked
+ // later. RAack = requested ack
+ BOOL collectRAck(TPACKETID packet_num);
+
+
+ void setTimeoutCallback(void (*callback_func)(const LLHost &host, void *user_data), void *user_data);
+
+
+
+ void setAlive(BOOL b_alive);
+ void setAllowTimeout(BOOL allow);
+
+protected:
+ // Identification for this circuit.
+ LLHost mHost;
+ LLUUID mRemoteID;
+ LLUUID mRemoteSessionID;
+
+ LLThrottleGroup mThrottles;
+
+ TPACKETID mWrapID;
+
+ // Current packet IDs of incoming/outgoing packets
+ // Used for packet sequencing/packet loss detection.
+ TPACKETID mPacketsOutID;
+ TPACKETID mPacketsInID;
+ TPACKETID mHighestPacketID;
+
+
+ // Callback and data to run in the case of a circuit timeout.
+ // Used primarily to try and reconnect to servers if they crash/die.
+ void (*mTimeoutCallback)(const LLHost &host, void *user_data);
+ void *mTimeoutUserData;
+
+ BOOL mTrusted; // Is this circuit trusted?
+ BOOL mbAllowTimeout; // Machines can "pause" circuits, forcing them not to be dropped
+
+ BOOL mbAlive; // Indicates whether a circuit is "alive", i.e. responded to pings
+
+ BOOL mBlocked; // Blocked is true if the circuit is hosed, i.e. far behind on pings
+
+ // Not sure what the difference between this and mLastPingSendTime is
+ F64 mPingTime; // Time at which a ping was sent.
+
+ F64 mLastPingSendTime; // Time we last sent a ping
+ F64 mLastPingReceivedTime; // Time we last received a ping
+ F64 mNextPingSendTime; // Time to try and send the next ping
+ S32 mPingsInTransit; // Number of pings in transit
+ U8 mLastPingID; // ID of the last ping that we sent out
+
+
+ // Used for determining the resend time for reliable resends.
+ U32 mPingDelay; // raw ping delay
+ F32 mPingDelayAveraged; // averaged ping delay (fast attack/slow decay)
+
+ typedef std::map<TPACKETID, U64> packet_time_map;
+
+ packet_time_map mPotentialLostPackets;
+ packet_time_map mRecentlyReceivedReliablePackets;
+ std::vector<TPACKETID> mAcks;
+
+ typedef std::map<TPACKETID, LLReliablePacket *> reliable_map;
+ typedef reliable_map::iterator reliable_iter;
+
+ reliable_map mUnackedPackets;
+ reliable_map mFinalRetryPackets;
+
+ S32 mUnackedPacketCount;
+ S32 mUnackedPacketBytes;
+
+
+ LLUUID mLocalEndPointID;
+
+ //
+ // These variables are being used for statistical and debugging purpose ONLY,
+ // as far as I can tell.
+ //
+
+ U32 mPacketsOut;
+ U32 mPacketsIn;
+ S32 mPacketsLost;
+ S32 mBytesIn;
+ S32 mBytesOut;
+
+ F32 mLastPeriodLength; // seconds
+ S32 mBytesInLastPeriod;
+ S32 mBytesOutLastPeriod;
+ S32 mBytesInThisPeriod;
+ S32 mBytesOutThisPeriod;
+ F32 mPeakBPSIn; // bits per second, max of all period bps
+ F32 mPeakBPSOut; // bits per second, max of all period bps
+ F64 mPeriodTime;
+ LLTimer mExistenceTimer; // initialized when circuit created, used to track bandwidth numbers
+
+ S32 mCurrentResendCount; // Number of resent packets since last spam
+};
+
+
+// Actually a singleton class -- the global messagesystem
+// has a single LLCircuit member.
+class LLCircuit
+{
+public:
+ // CREATORS
+ LLCircuit();
+ ~LLCircuit();
+
+ // ACCESSORS
+ LLCircuitData* findCircuit(const LLHost& host) const;
+ BOOL isCircuitAlive(const LLHost& host) const;
+
+ // MANIPULATORS
+ LLCircuitData *addCircuitData(const LLHost &host, TPACKETID in_id);
+ void removeCircuitData(const LLHost &host);
+
+ void updateWatchDogTimers(LLMessageSystem *msgsys);
+ void resendUnackedPackets(S32& unacked_list_length, S32& unacked_list_size);
+
+ // this method is called during the message system processAcks()
+ // to send out any acks that did not get sent already.
+ void sendAcks();
+
+ friend std::ostream& operator<<(std::ostream& s, LLCircuit &circuit);
+ const char* getInfoString() const;
+
+ void dumpResends();
+
+ typedef std::map<LLHost, LLCircuitData*> circuit_data_map;
+
+ /**
+ * @brief This method gets an iterator range starting after key in
+ * the circuit data map.
+ *
+ * @param key The the host before first.
+ * @param first[out] The first matching value after key. This
+ * value will equal end if there are no entries.
+ * @param end[out] The end of the iteration sequence.
+ */
+ void getCircuitRange(
+ const LLHost& key,
+ circuit_data_map::iterator& first,
+ circuit_data_map::iterator& end);
+
+ // Lists that optimize how many circuits we need to traverse a frame
+ // HACK - this should become protected eventually, but stupid !@$@# message system/circuit classes are jumbling things up.
+ circuit_data_map mUnackedCircuitMap; // Map of circuits with unacked data
+ circuit_data_map mSendAckMap; // Map of circuits which need to send acks
+protected:
+ circuit_data_map mCircuitData;
+
+ typedef std::set<LLCircuitData *, LLCircuitData::less> ping_set_t; // Circuits sorted by next ping time
+ ping_set_t mPingSet;
+
+ // This variable points to the last circuit data we found to
+ // optimize the many, many times we call findCircuit. This may be
+ // set in otherwise const methods, so it is declared mutable.
+ mutable LLCircuitData* mLastCircuit;
+};
+#endif
diff --git a/indra/llmessage/llclassifiedflags.cpp b/indra/llmessage/llclassifiedflags.cpp
new file mode 100644
index 0000000000..d84071e2eb
--- /dev/null
+++ b/indra/llmessage/llclassifiedflags.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file llclassifiedflags.cpp
+ * @brief
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+// llclassifiedflags.cpp
+//
+// Some exported symbols and functions for dealing with classified flags.
+//
+// Copyright 2005, Linden Research, Inc
+//*****************************************************************************
+
+#include "linden_common.h"
+
+#include "llclassifiedflags.h"
+
+ClassifiedFlags pack_classified_flags(BOOL is_mature, BOOL auto_renew)
+{
+ U8 rv = 0;
+ if(is_mature) rv |= CLASSIFIED_FLAG_MATURE;
+ if(auto_renew) rv |= CLASSIFIED_FLAG_AUTO_RENEW;
+ return rv;
+}
+
+bool is_cf_mature(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_MATURE) != 0);
+}
+
+// Deprecated, but leaving commented out because someday we might
+// want to let users enable/disable classifieds. JC
+//bool is_cf_enabled(ClassifiedFlags flags)
+//{
+// return ((flags & CLASSIFIED_FLAG_ENABLED) == CLASSIFIED_FLAG_ENABLED);
+//}
+
+bool is_cf_update_time(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_UPDATE_TIME) != 0);
+}
+
+bool is_cf_auto_renew(ClassifiedFlags flags)
+{
+ return ((flags & CLASSIFIED_FLAG_AUTO_RENEW) != 0);
+}
diff --git a/indra/llmessage/llclassifiedflags.h b/indra/llmessage/llclassifiedflags.h
new file mode 100644
index 0000000000..1949eb54d0
--- /dev/null
+++ b/indra/llmessage/llclassifiedflags.h
@@ -0,0 +1,31 @@
+/**
+ * @file llclassifiedflags.h
+ * @brief Flags used in the classifieds.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLASSIFIEDFLAGS_H
+#define LL_LLCLASSIFIEDFLAGS_H
+
+typedef U8 ClassifiedFlags;
+
+const U8 CLASSIFIED_FLAG_NONE = 1 << 0;
+const U8 CLASSIFIED_FLAG_MATURE = 1 << 1;
+//const U8 CLASSIFIED_FLAG_ENABLED = 1 << 2; // see llclassifiedflags.cpp
+//const U8 CLASSIFIED_FLAG_HAS_PRICE= 1 << 3; // deprecated
+const U8 CLASSIFIED_FLAG_UPDATE_TIME= 1 << 4;
+const U8 CLASSIFIED_FLAG_AUTO_RENEW = 1 << 5;
+
+const U8 CLASSIFIED_QUERY_FILTER_MATURE = 1 << 1;
+const U8 CLASSIFIED_QUERY_FILTER_ENABLED = 1 << 2;
+const U8 CLASSIFIED_QUERY_FILTER_PRICE = 1 << 3;
+
+ClassifiedFlags pack_classified_flags(BOOL is_mature, BOOL auto_renew);
+bool is_cf_mature(ClassifiedFlags flags);
+//bool is_cf_enabled(ClassifiedFlags flags);
+bool is_cf_update_time(ClassifiedFlags flags);
+bool is_cf_auto_renew(ClassifiedFlags flags);
+
+#endif
diff --git a/indra/llmessage/lldatapacker.cpp b/indra/llmessage/lldatapacker.cpp
new file mode 100644
index 0000000000..bc9f4f3486
--- /dev/null
+++ b/indra/llmessage/lldatapacker.cpp
@@ -0,0 +1,1866 @@
+/**
+ * @file lldatapacker.cpp
+ * @brief Data packer implementation.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <string.h>
+
+#include "linden_common.h"
+
+#include "lldatapacker.h"
+#include "llerror.h"
+
+#include "message.h"
+
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "lluuid.h"
+
+// *NOTE: there are functions below which use sscanf and rely on this
+// particular value of DP_BUFSIZE. Search for '511' (DP_BUFSIZE - 1)
+// to find them if you change this number.
+const S32 DP_BUFSIZE = 512;
+
+static char DUMMY_BUFFER[128]; /*Flawfinder: ignore*/
+
+LLDataPacker::LLDataPacker() : mPassFlags(0), mWriteEnabled(FALSE)
+{
+}
+
+BOOL LLDataPacker::packFixed(const F32 value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits)
+{
+ BOOL success = TRUE;
+ S32 unsigned_bits = int_bits + frac_bits;
+ S32 total_bits = unsigned_bits;
+
+ if (is_signed)
+ {
+ total_bits++;
+ }
+
+ S32 min_val;
+ U32 max_val;
+ if (is_signed)
+ {
+ min_val = 1 << int_bits;
+ min_val *= -1;
+ }
+ else
+ {
+ min_val = 0;
+ }
+ max_val = 1 << int_bits;
+
+ // Clamp to be within range
+ F32 fixed_val = llclamp(value, (F32)min_val, (F32)max_val);
+ if (is_signed)
+ {
+ fixed_val += max_val;
+ }
+ fixed_val *= 1 << frac_bits;
+
+ if (total_bits <= 8)
+ {
+ packU8((U8)fixed_val, name);
+ }
+ else if (total_bits <= 16)
+ {
+ packU16((U16)fixed_val, name);
+ }
+ else if (total_bits <= 31)
+ {
+ packU32((U32)fixed_val, name);
+ }
+ else
+ {
+ llerrs << "Using fixed-point packing of " << total_bits << " bits, why?!" << llendl;
+ }
+ return success;
+}
+
+BOOL LLDataPacker::unpackFixed(F32 &value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits)
+{
+ //BOOL success = TRUE;
+ //llinfos << "unpackFixed:" << name << " int:" << int_bits << " frac:" << frac_bits << llendl;
+ BOOL ok = FALSE;
+ S32 unsigned_bits = int_bits + frac_bits;
+ S32 total_bits = unsigned_bits;
+
+ if (is_signed)
+ {
+ total_bits++;
+ }
+
+ S32 min_val;
+ U32 max_val;
+ if (is_signed)
+ {
+ min_val = 1 << int_bits;
+ min_val *= -1;
+ }
+ max_val = 1 << int_bits;
+
+ F32 fixed_val;
+ if (total_bits <= 8)
+ {
+ U8 fixed_8;
+ ok = unpackU8(fixed_8, name);
+ fixed_val = (F32)fixed_8;
+ }
+ else if (total_bits <= 16)
+ {
+ U16 fixed_16;
+ ok = unpackU16(fixed_16, name);
+ fixed_val = (F32)fixed_16;
+ }
+ else if (total_bits <= 31)
+ {
+ U32 fixed_32;
+ ok = unpackU32(fixed_32, name);
+ fixed_val = (F32)fixed_32;
+ }
+ else
+ {
+ fixed_val = 0;
+ llerrs << "Bad bit count: " << total_bits << llendl;
+ }
+
+ //llinfos << "Fixed_val:" << fixed_val << llendl;
+
+ fixed_val /= (F32)(1 << frac_bits);
+ if (is_signed)
+ {
+ fixed_val -= max_val;
+ }
+ value = fixed_val;
+ //llinfos << "Value: " << value << llendl;
+ return ok;
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerBinaryBuffer implementation
+//---------------------------------------------------------------------------
+
+BOOL LLDataPackerBinaryBuffer::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ S32 length = (S32)strlen(value) + 1; /*Flawfinder: ignore*/
+
+ success &= verifyLength(length, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, length);
+ }
+ mCurBufferp += length;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ S32 length = (S32)strlen((char *)mCurBufferp) + 1; /*Flawfinder: ignore*/
+
+ success &= verifyLength(length, name);
+
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, length);
+ mCurBufferp += length;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size + 4, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &size, MVT_S32, 4);
+ }
+ mCurBufferp += 4;
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size);
+ }
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+ htonmemcpy(&size, mCurBufferp, MVT_S32, 4);
+ mCurBufferp += 4;
+ success &= verifyLength(size, name);
+ if (success)
+ {
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size);
+ mCurBufferp += size;
+ }
+ else
+ {
+ llwarns << "LLDataPackerBinaryBuffer::unpackBinaryData would unpack invalid data, aborting!" << llendl;
+ success = FALSE;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value, MVT_VARIABLE, size);
+ }
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(size, name);
+ htonmemcpy(value, mCurBufferp, MVT_VARIABLE, size);
+ mCurBufferp += size;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U8), name);
+
+ if (mWriteEnabled)
+ {
+ *mCurBufferp = value;
+ }
+ mCurBufferp++;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U8), name);
+
+ value = *mCurBufferp;
+ mCurBufferp++;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U16), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_U16, 2);
+ }
+ mCurBufferp += 2;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U16), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_U16, 2);
+ mCurBufferp += 2;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_U32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(U32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_U32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(S32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_S32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(S32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_S32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(F32), name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value, MVT_F32, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(sizeof(F32), name);
+
+ htonmemcpy(&value, mCurBufferp, MVT_F32, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_VARIABLE, 4);
+ }
+ mCurBufferp += 4;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(4, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_VARIABLE, 4);
+ mCurBufferp += 4;
+ return success;
+}
+
+
+
+BOOL LLDataPackerBinaryBuffer::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(8, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, &value.mV[0], MVT_F32, 4);
+ htonmemcpy(mCurBufferp+4, &value.mV[1], MVT_F32, 4);
+ }
+ mCurBufferp += 8;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(8, name);
+
+ htonmemcpy(&value.mV[0], mCurBufferp, MVT_F32, 4);
+ htonmemcpy(&value.mV[1], mCurBufferp+4, MVT_F32, 4);
+ mCurBufferp += 8;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(12, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector3, 12);
+ }
+ mCurBufferp += 12;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(12, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector3, 12);
+ mCurBufferp += 12;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mV, MVT_LLVector4, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mV, mCurBufferp, MVT_LLVector4, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+BOOL LLDataPackerBinaryBuffer::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ if (mWriteEnabled)
+ {
+ htonmemcpy(mCurBufferp, value.mData, MVT_LLUUID, 16);
+ }
+ mCurBufferp += 16;
+ return success;
+}
+
+
+BOOL LLDataPackerBinaryBuffer::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ success &= verifyLength(16, name);
+
+ htonmemcpy(value.mData, mCurBufferp, MVT_LLUUID, 16);
+ mCurBufferp += 16;
+ return success;
+}
+
+const LLDataPackerBinaryBuffer& LLDataPackerBinaryBuffer::operator=(const LLDataPackerBinaryBuffer &a)
+{
+ if (a.getBufferSize() > getBufferSize())
+ {
+ // We've got problems, ack!
+ llerrs << "Trying to do an assignment with not enough room in the target." << llendl;
+ }
+ memcpy(mBufferp, a.mBufferp, a.getBufferSize());
+ return *this;
+}
+
+void LLDataPackerBinaryBuffer::dumpBufferToLog()
+{
+ llwarns << "Binary Buffer Dump, size: " << mBufferSize << llendl;
+ char line_buffer[256]; /*Flawfinder: ignore*/
+ S32 i;
+ S32 cur_line_pos = 0;
+
+ S32 cur_line = 0;
+ for (i = 0; i < mBufferSize; i++)
+ {
+ snprintf(line_buffer + cur_line_pos*3, sizeof(line_buffer) - cur_line_pos*3, "%02x ", mBufferp[i]); /*Flawfinder: ignore*/
+ cur_line_pos++;
+ if (cur_line_pos >= 16)
+ {
+ cur_line_pos = 0;
+ llwarns << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << llendl;
+ cur_line++;
+ }
+ }
+ if (cur_line_pos)
+ {
+ llwarns << "Offset:" << std::hex << cur_line*16 << std::dec << " Data:" << line_buffer << llendl;
+ }
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerAsciiBuffer implementation
+//---------------------------------------------------------------------------
+BOOL LLDataPackerAsciiBuffer::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ numCopied = (S32)strlen(value) + 1; /*Flawfinder: ignore*/
+ }
+
+ // snprintf returns number of bytes that would have been written
+ // had the output not being truncated. In that case, it will
+ // return >= passed in size value. so a check needs to be added
+ // to detect truncation, and if there is any, only account for the
+ // actual number of bytes written..and not what could have been
+ // written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ // *NOTE: I believe we need to mark a failure bit at this point.
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+ // XXXCHECK: Can result in buffer overrun. Need to pass in size for "value"
+ strcpy(value, valuestr); /*Flawfinder: ignore*/
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%010d ", size); /*Flawfinder: ignore*/
+
+ // snprintf returns number of bytes that would have been
+ // written had the output not being truncated. In that case,
+ // it will retuen >= passed in size value. so a check needs
+ // to be added to detect truncation, and if there is any, only
+ // account for the actual number of bytes written..and not
+ // what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+
+
+ S32 i;
+ BOOL bBufferFull = FALSE;
+ for (i = 0; i < size && !bBufferFull; i++)
+ {
+ numCopied = snprintf(mCurBufferp, getBufferSize()-getCurrentSize(), "%02x ", value[i]); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ bBufferFull = TRUE;
+ }
+ mCurBufferp += numCopied;
+ }
+
+ if (!bBufferFull)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+ }
+ else
+ {
+ // why +10 ?? XXXCHECK
+ numCopied = 10 + 1; // size plus newline
+ numCopied += size;
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+ sscanf(valuestr,"%010d", &size);
+ cur_pos += 11;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mWriteEnabled)
+ {
+ S32 i;
+ int numCopied = 0;
+ BOOL bBufferFull = FALSE;
+ for (i = 0; i < size && !bBufferFull; i++)
+ {
+ numCopied = snprintf(mCurBufferp, getBufferSize()-getCurrentSize(), "%02x ", value[i]); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ bBufferFull = TRUE;
+ }
+ mCurBufferp += numCopied;
+
+ }
+ if (!bBufferFull)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(), "\n"); /* Flawfinder: ignore */
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ }
+ }
+ else
+ {
+ int numCopied = 2 * size + 1; //hex bytes plus newline
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ mCurBufferp += numCopied;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+
+BOOL LLDataPackerAsciiBuffer::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ // just do the write to a temp buffer to get the length
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /*Flawfinder: ignore*/
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%u\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%u\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%u", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%d\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%d", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g\n", value); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER, sizeof(DUMMY_BUFFER), "%g\n", value); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 r, g, b, a;
+
+ sscanf(valuestr,"%d %d %d %d", &r, &g, &b, &a);
+ value.mV[0] = r;
+ value.mV[1] = g;
+ value.mV[2] = b;
+ value.mV[3] = a;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g\n", value.mV[0], value.mV[1]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g", &value.mV[0], &value.mV[1]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g", &value.mV[0], &value.mV[1], &value.mV[2]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiBuffer::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = snprintf(DUMMY_BUFFER,sizeof(DUMMY_BUFFER),"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]); /* Flawfinder: ignore */
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ char tmp_str[64]; /* Flawfinder: ignore */
+ value.toString(tmp_str);
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\n", tmp_str); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = 64 + 1; // UUID + newline
+ }
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ success = FALSE;
+ }
+ mCurBufferp += numCopied;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiBuffer::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char tmp_str[64]; /* Flawfinder: ignore */
+ sscanf(valuestr, "%63s", tmp_str);
+ value.set(tmp_str);
+
+ return success;
+}
+
+void LLDataPackerAsciiBuffer::dump()
+{
+ llinfos << "Buffer: " << mBufferp << llendl;
+}
+
+void LLDataPackerAsciiBuffer::writeIndentedName(const char *name)
+{
+ if (mIncludeNames)
+ {
+ int numCopied = 0;
+ if (mWriteEnabled)
+ {
+ numCopied = snprintf(mCurBufferp,getBufferSize()-getCurrentSize(),"%s\t", name); /* Flawfinder: ignore */
+ }
+ else
+ {
+ numCopied = (S32)strlen(name) + 1; //name + tab /* Flawfinder: ignore */
+ }
+
+ // snprintf returns number of bytes that would have been written had the
+ // output not being truncated. In that case, it will retuen >= passed in size value.
+ // so a check needs to be added to detect truncation, and if there is any,
+ // only account for the actual number of bytes written..and not what could have been written.
+ if (numCopied > getBufferSize()-getCurrentSize())
+ {
+ numCopied = getBufferSize()-getCurrentSize();
+ }
+ }
+}
+
+BOOL LLDataPackerAsciiBuffer::getValueStr(const char *name, char *out_value, S32 value_len)
+{
+ BOOL success = TRUE;
+ char buffer[DP_BUFSIZE]; /* Flawfinder: ignore */
+ char keyword[DP_BUFSIZE]; /* Flawfinder: ignore */
+ char value[DP_BUFSIZE]; /* Flawfinder: ignore */
+
+ buffer[0] = '\0';
+ keyword[0] = '\0';
+ value[0] = '\0';
+
+ if (mIncludeNames)
+ {
+ // Read both the name and the value, and validate the name.
+ sscanf(mCurBufferp, "%511[^\n]", buffer);
+ // Skip the \n
+ mCurBufferp += (S32)strlen(buffer) + 1;
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ // Just the value exists
+ sscanf(mCurBufferp, "%511[^\n]", value);
+ // Skip the \n
+ mCurBufferp += (S32)strlen(value) + 1; /* Flawfinder: ignore */
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /* Flawfinder: ignore */
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /* Flawfinder: ignore */
+ out_value[min_len-1] = 0;
+
+ return success;
+}
+
+//---------------------------------------------------------------------------
+// LLDataPackerAsciiFile implementation
+//---------------------------------------------------------------------------
+BOOL LLDataPackerAsciiFile::packString(const char *value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%s\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value << "\n";
+ }
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::unpackString(char *value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /* Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+ strncpy(value, valuestr,DP_BUFSIZE);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packBinaryData(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mFP)
+ {
+ fprintf(mFP, "%010d ", size);
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ fprintf(mFP, "%02x ", value[i]);
+ }
+ fprintf(mFP, "\n");
+ }
+ else if (mOutputStream)
+ {
+ char buffer[32]; /* Flawfinder: ignore */
+ snprintf(buffer,sizeof(buffer), "%010d ", size); /* Flawfinder: ignore */
+ *mOutputStream << buffer;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ snprintf(buffer, sizeof(buffer), "%02x ", value[i]); /* Flawfinder: ignore */
+ *mOutputStream << buffer;
+ }
+ *mOutputStream << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackBinaryData(U8 *value, S32 &size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+ sscanf(valuestr,"%010d", &size);
+ cur_pos += 11;
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packBinaryDataFixed(const U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+
+ if (mFP)
+ {
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ fprintf(mFP, "%02x ", value[i]);
+ }
+ fprintf(mFP, "\n");
+ }
+ else if (mOutputStream)
+ {
+ char buffer[32]; /*Flawfinder: ignore*/
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ snprintf(buffer, sizeof(buffer), "%02x ", value[i]); /*Flawfinder: ignore*/
+ *mOutputStream << buffer;
+ }
+ *mOutputStream << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackBinaryDataFixed(U8 *value, S32 size, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char *cur_pos = &valuestr[0];
+
+ S32 i;
+ for (i = 0; i < size; i++)
+ {
+ S32 val;
+ sscanf(cur_pos,"%02x", &val);
+ value[i] = val;
+ cur_pos += 3;
+ }
+ return success;
+}
+
+
+
+BOOL LLDataPackerAsciiFile::packU8(const U8 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ // We have to cast this to an integer because streams serialize
+ // bytes as bytes - not as text.
+ *mOutputStream << (S32)value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU8(U8 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packU16(const U16 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU16(U16 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 in_val;
+ sscanf(valuestr,"%d", &in_val);
+ value = in_val;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packU32(const U32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%u\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackU32(U32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%u", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packS32(const S32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackS32(S32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%d", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packF32(const F32 value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g\n", value);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << value << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackF32(F32 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g", &value);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packColor4(const LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << " " << value.mV[3] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackColor4(LLColor4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packColor4U(const LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%d %d %d %d\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << (S32)(value.mV[0]) << " " << (S32)(value.mV[1]) << " " << (S32)(value.mV[2]) << " " << (S32)(value.mV[3]) << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackColor4U(LLColor4U &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ S32 r, g, b, a;
+
+ sscanf(valuestr,"%d %d %d %d", &r, &g, &b, &a);
+ value.mV[0] = r;
+ value.mV[1] = g;
+ value.mV[2] = b;
+ value.mV[3] = a;
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packVector2(const LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g\n", value.mV[0], value.mV[1]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector2(LLVector2 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g", &value.mV[0], &value.mV[1]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packVector3(const LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g\n", value.mV[0], value.mV[1], value.mV[2]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector3(LLVector3 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g", &value.mV[0], &value.mV[1], &value.mV[2]);
+ return success;
+}
+
+BOOL LLDataPackerAsciiFile::packVector4(const LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ if (mFP)
+ {
+ fprintf(mFP,"%g %g %g %g\n", value.mV[0], value.mV[1], value.mV[2], value.mV[3]);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << value.mV[0] << " " << value.mV[1] << " " << value.mV[2] << " " << value.mV[3] << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackVector4(LLVector4 &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ sscanf(valuestr,"%g %g %g %g", &value.mV[0], &value.mV[1], &value.mV[2], &value.mV[3]);
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::packUUID(const LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ writeIndentedName(name);
+ char tmp_str[64]; /*Flawfinder: ignore */
+ value.toString(tmp_str);
+ if (mFP)
+ {
+ fprintf(mFP,"%s\n", tmp_str);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream <<"" << tmp_str << "\n";
+ }
+ return success;
+}
+
+
+BOOL LLDataPackerAsciiFile::unpackUUID(LLUUID &value, const char *name)
+{
+ BOOL success = TRUE;
+ char valuestr[DP_BUFSIZE]; /*Flawfinder: ignore */
+ if (!getValueStr(name, valuestr, DP_BUFSIZE))
+ {
+ return FALSE;
+ }
+
+ char tmp_str[64]; /*Flawfinder: ignore */
+ sscanf(valuestr,"%63s",tmp_str);
+ value.set(tmp_str);
+
+ return success;
+}
+
+
+void LLDataPackerAsciiFile::writeIndentedName(const char *name)
+{
+ char indent_buf[64]; /*Flawfinder: ignore*/
+
+ S32 i;
+ for(i = 0; i < mIndent; i++)
+ {
+ indent_buf[i] = '\t';
+ }
+ indent_buf[i] = 0;
+ if (mFP)
+ {
+ fprintf(mFP,"%s%s\t",indent_buf, name);
+ }
+ else if (mOutputStream)
+ {
+ *mOutputStream << indent_buf << name << "\t";
+ }
+}
+
+BOOL LLDataPackerAsciiFile::getValueStr(const char *name, char *out_value, S32 value_len)
+{
+ BOOL success = FALSE;
+ char buffer[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ char keyword[DP_BUFSIZE]; /*Flawfinder: ignore*/
+ char value[DP_BUFSIZE]; /*Flawfinder: ignore*/
+
+ buffer[0] = '\0';
+ keyword[0] = '\0';
+ value[0] = '\0';
+
+ if (mFP)
+ {
+ fpos_t last_pos;
+ fgetpos(mFP, &last_pos);
+ fgets(buffer, DP_BUFSIZE, mFP);
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+
+ if (!keyword[0])
+ {
+ llwarns << "Data packer could not get the keyword!" << llendl;
+ fsetpos(mFP, &last_pos);
+ return FALSE;
+ }
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ fsetpos(mFP, &last_pos);
+ return FALSE;
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /*Flawfinder: ignore*/
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /*Flawfinder: ignore*/
+ out_value[min_len-1] = 0;
+ success = TRUE;
+ }
+ else if (mInputStream)
+ {
+ mInputStream->getline(buffer, DP_BUFSIZE);
+
+ sscanf(buffer, "%511s %511[^\n]", keyword, value);
+ if (!keyword[0])
+ {
+ llwarns << "Data packer could not get the keyword!" << llendl;
+ return FALSE;
+ }
+ if (strcmp(keyword, name))
+ {
+ llwarns << "Data packer expecting keyword of type " << name << ", got " << keyword << " instead!" << llendl;
+ return FALSE;
+ }
+
+ S32 in_value_len = (S32)strlen(value)+1; /*Flawfinder: ignore*/
+ S32 min_len = llmin(in_value_len, value_len);
+ memcpy(out_value, value, min_len); /*Flawfinder: ignore*/
+ out_value[min_len-1] = 0;
+ success = TRUE;
+ }
+
+ return success;
+}
diff --git a/indra/llmessage/lldatapacker.h b/indra/llmessage/lldatapacker.h
new file mode 100644
index 0000000000..10ca35d2c7
--- /dev/null
+++ b/indra/llmessage/lldatapacker.h
@@ -0,0 +1,398 @@
+/**
+ * @file lldatapacker.h
+ * @brief Data packer declaration for tightly storing binary data.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDATAPACKER_H
+#define LL_LLDATAPACKER_H
+
+#include <stdio.h>
+#include <iostream>
+
+#include "llerror.h"
+
+class LLColor4;
+class LLColor4U;
+class LLVector2;
+class LLVector3;
+class LLVector4;
+class LLUUID;
+
+class LLDataPacker
+{
+public:
+ virtual ~LLDataPacker() {}
+
+ virtual void reset() { llerrs << "Using unimplemented datapacker reset!" << llendl; };
+ virtual void dumpBufferToLog() { llerrs << "dumpBufferToLog not implemented for this type!" << llendl; }
+
+ virtual BOOL hasNext() const = 0;
+
+ virtual BOOL packString(const char *value, const char *name) = 0;
+ virtual BOOL unpackString(char *value, const char *name) = 0;
+
+ virtual BOOL packBinaryData(const U8 *value, S32 size, const char *name) = 0;
+ virtual BOOL unpackBinaryData(U8 *value, S32 &size, const char *name) = 0;
+
+ // Constant size binary data packing
+ virtual BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name) = 0;
+ virtual BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name) = 0;
+
+ virtual BOOL packU8(const U8 value, const char *name) = 0;
+ virtual BOOL unpackU8(U8 &value, const char *name) = 0;
+
+ virtual BOOL packU16(const U16 value, const char *name) = 0;
+ virtual BOOL unpackU16(U16 &value, const char *name) = 0;
+
+ virtual BOOL packU32(const U32 value, const char *name) = 0;
+ virtual BOOL unpackU32(U32 &value, const char *name) = 0;
+
+ virtual BOOL packS32(const S32 value, const char *name) = 0;
+ virtual BOOL unpackS32(S32 &value, const char *name) = 0;
+
+ virtual BOOL packF32(const F32 value, const char *name) = 0;
+ virtual BOOL unpackF32(F32 &value, const char *name) = 0;
+
+ // Packs a float into an integer, using the given size
+ // and picks the right U* data type to pack into.
+ BOOL packFixed(const F32 value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits);
+ BOOL unpackFixed(F32 &value, const char *name,
+ const BOOL is_signed, const U32 int_bits, const U32 frac_bits);
+
+ virtual BOOL packColor4(const LLColor4 &value, const char *name) = 0;
+ virtual BOOL unpackColor4(LLColor4 &value, const char *name) = 0;
+
+ virtual BOOL packColor4U(const LLColor4U &value, const char *name) = 0;
+ virtual BOOL unpackColor4U(LLColor4U &value, const char *name) = 0;
+
+ virtual BOOL packVector2(const LLVector2 &value, const char *name) = 0;
+ virtual BOOL unpackVector2(LLVector2 &value, const char *name) = 0;
+
+ virtual BOOL packVector3(const LLVector3 &value, const char *name) = 0;
+ virtual BOOL unpackVector3(LLVector3 &value, const char *name) = 0;
+
+ virtual BOOL packVector4(const LLVector4 &value, const char *name) = 0;
+ virtual BOOL unpackVector4(LLVector4 &value, const char *name) = 0;
+
+ virtual BOOL packUUID(const LLUUID &value, const char *name) = 0;
+ virtual BOOL unpackUUID(LLUUID &value, const char *name) = 0;
+ U32 getPassFlags() const { return mPassFlags; }
+ void setPassFlags(U32 flags) { mPassFlags = flags; }
+protected:
+ LLDataPacker();
+protected:
+ U32 mPassFlags;
+ BOOL mWriteEnabled; // disable this to do things like determine filesize without actually copying data
+};
+
+class LLDataPackerBinaryBuffer : public LLDataPacker
+{
+public:
+ LLDataPackerBinaryBuffer(U8 *bufferp, S32 size)
+ : LLDataPacker(),
+ mBufferp(bufferp),
+ mCurBufferp(bufferp),
+ mBufferSize(size)
+ {
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerBinaryBuffer()
+ : LLDataPacker(),
+ mBufferp(NULL),
+ mCurBufferp(NULL),
+ mBufferSize(0)
+ {
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ // Constant size binary data packing
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+
+ S32 getCurrentSize() const { return (S32)(mCurBufferp - mBufferp); }
+ S32 getBufferSize() const { return mBufferSize; }
+ void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
+ void freeBuffer() { delete [] mBufferp; mBufferp = mCurBufferp = NULL; mBufferSize = 0; mWriteEnabled = FALSE; }
+ void assignBuffer(U8 *bufferp, S32 size)
+ {
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mWriteEnabled = TRUE;
+ }
+ const LLDataPackerBinaryBuffer& operator=(const LLDataPackerBinaryBuffer &a);
+
+ /*virtual*/ BOOL hasNext() const { return getCurrentSize() < getBufferSize(); }
+
+ /*virtual*/ void dumpBufferToLog();
+protected:
+ inline BOOL verifyLength(const S32 data_size, const char *name);
+
+ U8 *mBufferp;
+ U8 *mCurBufferp;
+ S32 mBufferSize;
+};
+
+inline BOOL LLDataPackerBinaryBuffer::verifyLength(const S32 data_size, const char *name)
+{
+ if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
+ {
+ llwarns << "Buffer overflow in BinaryBuffer length verify, field name " << name << "!" << llendl;
+ llwarns << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLDataPackerAsciiBuffer : public LLDataPacker
+{
+public:
+ LLDataPackerAsciiBuffer(char* bufferp, S32 size)
+ {
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mPassFlags = 0;
+ mIncludeNames = FALSE;
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerAsciiBuffer()
+ {
+ mBufferp = NULL;
+ mCurBufferp = NULL;
+ mBufferSize = 0;
+ mPassFlags = 0;
+ mIncludeNames = FALSE;
+ mWriteEnabled = FALSE;
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ // Constant size binary data packing
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+
+ void setIncludeNames(BOOL b) { mIncludeNames = b; }
+
+ // Include the trailing NULL so it's always a valid string
+ S32 getCurrentSize() const { return (S32)(mCurBufferp - mBufferp) + 1; }
+
+ S32 getBufferSize() const { return mBufferSize; }
+ /*virtual*/ void reset() { mCurBufferp = mBufferp; mWriteEnabled = (mCurBufferp != NULL); }
+
+ /*virtual*/ BOOL hasNext() const { return getCurrentSize() < getBufferSize(); }
+
+ inline void freeBuffer();
+ inline void assignBuffer(char* bufferp, S32 size);
+ void dump();
+
+protected:
+ void writeIndentedName(const char *name);
+ BOOL getValueStr(const char *name, char *out_value, const S32 value_len);
+
+protected:
+ inline BOOL verifyLength(const S32 data_size, const char *name);
+
+ char *mBufferp;
+ char *mCurBufferp;
+ S32 mBufferSize;
+ BOOL mIncludeNames; // useful for debugging, print the name of each field
+};
+
+inline void LLDataPackerAsciiBuffer::freeBuffer()
+{
+ delete [] mBufferp;
+ mBufferp = mCurBufferp = NULL;
+ mBufferSize = 0;
+ mWriteEnabled = FALSE;
+}
+
+inline void LLDataPackerAsciiBuffer::assignBuffer(char* bufferp, S32 size)
+{
+ mBufferp = bufferp;
+ mCurBufferp = bufferp;
+ mBufferSize = size;
+ mWriteEnabled = TRUE;
+}
+
+inline BOOL LLDataPackerAsciiBuffer::verifyLength(const S32 data_size, const char *name)
+{
+ if (mWriteEnabled && (mCurBufferp - mBufferp) > mBufferSize - data_size)
+ {
+ llwarns << "Buffer overflow in AsciiBuffer length verify, field name " << name << "!" << llendl;
+ llwarns << "Current pos: " << (int)(mCurBufferp - mBufferp) << " Buffer size: " << mBufferSize << " Data size: " << data_size << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLDataPackerAsciiFile : public LLDataPacker
+{
+public:
+ LLDataPackerAsciiFile(FILE *fp, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(fp),
+ mOutputStream(NULL),
+ mInputStream(NULL)
+ {
+ }
+
+ LLDataPackerAsciiFile(std::ostream& output_stream, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(NULL),
+ mOutputStream(&output_stream),
+ mInputStream(NULL)
+ {
+ mWriteEnabled = TRUE;
+ }
+
+ LLDataPackerAsciiFile(std::istream& input_stream, const S32 indent = 2)
+ : LLDataPacker(),
+ mIndent(indent),
+ mFP(NULL),
+ mOutputStream(NULL),
+ mInputStream(&input_stream)
+ {
+ }
+
+ /*virtual*/ BOOL packString(const char *value, const char *name);
+ /*virtual*/ BOOL unpackString(char *value, const char *name);
+
+ /*virtual*/ BOOL packBinaryData(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryData(U8 *value, S32 &size, const char *name);
+
+ /*virtual*/ BOOL packBinaryDataFixed(const U8 *value, S32 size, const char *name);
+ /*virtual*/ BOOL unpackBinaryDataFixed(U8 *value, S32 size, const char *name);
+
+ /*virtual*/ BOOL packU8(const U8 value, const char *name);
+ /*virtual*/ BOOL unpackU8(U8 &value, const char *name);
+
+ /*virtual*/ BOOL packU16(const U16 value, const char *name);
+ /*virtual*/ BOOL unpackU16(U16 &value, const char *name);
+
+ /*virtual*/ BOOL packU32(const U32 value, const char *name);
+ /*virtual*/ BOOL unpackU32(U32 &value, const char *name);
+
+ /*virtual*/ BOOL packS32(const S32 value, const char *name);
+ /*virtual*/ BOOL unpackS32(S32 &value, const char *name);
+
+ /*virtual*/ BOOL packF32(const F32 value, const char *name);
+ /*virtual*/ BOOL unpackF32(F32 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4(const LLColor4 &value, const char *name);
+ /*virtual*/ BOOL unpackColor4(LLColor4 &value, const char *name);
+
+ /*virtual*/ BOOL packColor4U(const LLColor4U &value, const char *name);
+ /*virtual*/ BOOL unpackColor4U(LLColor4U &value, const char *name);
+
+ /*virtual*/ BOOL packVector2(const LLVector2 &value, const char *name);
+ /*virtual*/ BOOL unpackVector2(LLVector2 &value, const char *name);
+
+ /*virtual*/ BOOL packVector3(const LLVector3 &value, const char *name);
+ /*virtual*/ BOOL unpackVector3(LLVector3 &value, const char *name);
+
+ /*virtual*/ BOOL packVector4(const LLVector4 &value, const char *name);
+ /*virtual*/ BOOL unpackVector4(LLVector4 &value, const char *name);
+
+ /*virtual*/ BOOL packUUID(const LLUUID &value, const char *name);
+ /*virtual*/ BOOL unpackUUID(LLUUID &value, const char *name);
+protected:
+ void writeIndentedName(const char *name);
+ BOOL getValueStr(const char *name, char *out_value, const S32 value_len);
+
+ /*virtual*/ BOOL hasNext() const { return true; }
+
+protected:
+ S32 mIndent;
+ FILE *mFP;
+ std::ostream* mOutputStream;
+ std::istream* mInputStream;
+};
+
+#endif // LL_LLDATAPACKER
+
diff --git a/indra/llmessage/lldbstrings.h b/indra/llmessage/lldbstrings.h
new file mode 100644
index 0000000000..73aca880ef
--- /dev/null
+++ b/indra/llmessage/lldbstrings.h
@@ -0,0 +1,208 @@
+/**
+ * @file lldbstrings.h
+ * @brief Database String Lengths.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDBSTRINGS_H
+#define LL_LLDBSTRINGS_H
+
+/**
+ * Defines the length of strings that are stored in the database (and
+ * the size of the buffer large enough to hold each one)
+ */
+
+// asset.name varchar(63)
+// -also-
+// user_inventory_item.name varchar(63)
+// -also-
+// user_inventory_folder.name varchar(63) was CAT_NAME_SIZE
+// Must be >= DB_FULL_NAME_STR_LEN so that calling cards work
+const S32 DB_INV_ITEM_NAME_STR_LEN = 63; // was MAX_ASSET_NAME_LENGTH
+const S32 DB_INV_ITEM_NAME_BUF_SIZE = 64; // was ITEM_NAME_SIZE
+
+// asset.description varchar(127)
+// -also-
+// user_inventory_item.description varchar(127)
+const S32 DB_INV_ITEM_DESC_STR_LEN = 127; // was MAX_ASSET_DESCRIPTION_LENGTH
+const S32 DB_INV_ITEM_DESC_BUF_SIZE = 128; // was ITEM_DESC_SIZE
+
+// groups.name varchar(35)
+const S32 DB_GROUP_NAME_STR_LEN = 35;
+const S32 DB_GROUP_NAME_BUF_SIZE = 36;
+const S32 DB_GROUP_NAME_MIN_LEN = 4;
+
+//group_roles.name
+const U32 DB_GROUP_ROLE_NAME_STR_LEN = 20;
+const U32 DB_GROUP_ROLE_NAME_BUF_SIZE = DB_GROUP_ROLE_NAME_STR_LEN + 1;
+
+//group_roles.title
+const U32 DB_GROUP_ROLE_TITLE_STR_LEN = 20;
+const U32 DB_GROUP_ROLE_TITLE_BUF_SIZE = DB_GROUP_ROLE_TITLE_STR_LEN + 1;
+
+
+// group.charter text
+const S32 DB_GROUP_CHARTER_STR_LEN = 511;
+const S32 DB_GROUP_CHARTER_BUF_SIZE = 512;
+
+// group.officer_title varchar(20)
+// -also-
+// group.member_title varchar(20)
+const S32 DB_GROUP_TITLE_STR_LEN = 20;
+const S32 DB_GROUP_TITLE_BUF_SIZE = 21;
+
+// Since chat and im both dump into the database text message log,
+// they derive their max size from the same constant.
+const S32 MAX_MSG_STR_LEN = 1023;
+const S32 MAX_MSG_BUF_SIZE = 1024;
+
+// instant_message.message text
+const S32 DB_IM_MSG_STR_LEN = MAX_MSG_STR_LEN;
+const S32 DB_IM_MSG_BUF_SIZE = MAX_MSG_BUF_SIZE;
+
+// groupnotices
+const S32 DB_GROUP_NOTICE_SUBJ_STR_LEN = 63;
+const S32 DB_GROUP_NOTICE_SUBJ_STR_SIZE = 64;
+const S32 DB_GROUP_NOTICE_MSG_STR_LEN = MAX_MSG_STR_LEN - DB_GROUP_NOTICE_SUBJ_STR_LEN;
+const S32 DB_GROUP_NOTICE_MSG_STR_SIZE = MAX_MSG_BUF_SIZE - DB_GROUP_NOTICE_SUBJ_STR_SIZE;
+
+// log_text_message.message text
+const S32 DB_CHAT_MSG_STR_LEN = MAX_MSG_STR_LEN;
+const S32 DB_CHAT_MSG_BUF_SIZE = MAX_MSG_BUF_SIZE;
+
+// money_stipend.description varchar(254)
+const S32 DB_STIPEND_DESC_STR_LEN = 254;
+const S32 DB_STIPEND_DESC_BUF_SIZE = 255;
+
+// script_email_message.from_email varchar(78)
+const S32 DB_EMAIL_FROM_STR_LEN = 78;
+const S32 DB_EMAIL_FROM_BUF_SIZE = 79;
+
+// script_email_message.subject varchar(72)
+const S32 DB_EMAIL_SUBJECT_STR_LEN = 72;
+const S32 DB_EMAIL_SUBJECT_BUF_SIZE = 73;
+
+// system_globals.motd varchar(254)
+const S32 DB_MOTD_STR_LEN = 254;
+const S32 DB_MOTD_BUF_SIZE = 255;
+
+// Must be <= user_inventory_item.name so that calling cards work
+// First name + " " + last name...or a system assigned "from" name
+// instant_message.from_agent_name varchar(63)
+// -also-
+// user_mute.mute_agent_name varchar(63)
+const S32 DB_FULL_NAME_STR_LEN = 63;
+const S32 DB_FULL_NAME_BUF_SIZE = 64; // was USER_NAME_SIZE
+
+// user.username varchar(31)
+const S32 DB_FIRST_NAME_STR_LEN = 31;
+const S32 DB_FIRST_NAME_BUF_SIZE = 32; // was MAX_FIRST_NAME
+
+// user_last_name.name varchar(31)
+const S32 DB_LAST_NAME_STR_LEN = 31;
+const S32 DB_LAST_NAME_BUF_SIZE = 32; // was MAX_LAST_NAME
+
+// user.password varchar(100)
+const S32 DB_USER_PASSWORD_STR_LEN = 100;
+const S32 DB_USER_PASSWORD_BUF_SIZE = 101; // was MAX_PASSWORD
+
+// user.email varchar(254)
+const S32 DB_USER_EMAIL_ADDR_STR_LEN = 254;
+const S32 DB_USER_EMAIL_ADDR_BUF_SIZE = 255;
+
+// user.about text
+const S32 DB_USER_ABOUT_STR_LEN = 511;
+const S32 DB_USER_ABOUT_BUF_SIZE = 512;
+
+// user.fl_about_text text
+// Must be 255 not 256 as gets packed into message Variable 1
+const S32 DB_USER_FL_ABOUT_STR_LEN = 254;
+const S32 DB_USER_FL_ABOUT_BUF_SIZE = 255;
+
+// user.profile_url text
+// Must be 255 not 256 as gets packed into message Variable 1
+const S32 DB_USER_PROFILE_URL_STR_LEN = 254;
+const S32 DB_USER_PROFILE_URL_BUF_SIZE = 255;
+
+// user.want_to varchar(254)
+const S32 DB_USER_WANT_TO_STR_LEN = 254;
+const S32 DB_USER_WANT_TO_BUF_SIZE = 255;
+
+// user.skills varchar(254)
+const S32 DB_USER_SKILLS_STR_LEN = 254;
+const S32 DB_USER_SKILLS_BUF_SIZE = 255;
+
+// user_nv.name varchar(128)
+const S32 DB_NV_NAME_STR_LEN = 128;
+const S32 DB_NV_NAME_BUF_SIZE = 129;
+
+// votes.vote_text varchar(254)
+const S32 DB_VOTE_TEXT_STR_LEN = 254;
+const S32 DB_VOTE_TEXT_BUF_SIZE = 255;
+
+// vpte type text varchar(9)
+const S32 DB_VOTE_TYPE_STR_LEN = 9;
+const S32 DB_VOTE_TYPE_BUF_SIZE = 10;
+
+// vote result text
+const S32 DB_VOTE_RESULT_BUF_LEN = 8;
+const S32 DB_VOTE_RESULT_BUF_SIZE = 9;
+
+// user_start_location.location_name varchar(254)
+const S32 DB_START_LOCATION_STR_LEN = 254;
+const S32 DB_START_LOCATION_BUF_SIZE = 255;
+
+// money_tax_assessment.sim varchar(100)
+//const S32 DB_SIM_NAME_STR_LEN = 100;
+//const S32 DB_SIM_NAME_BUF_SIZE = 101;
+
+// born on date date
+const S32 DB_BORN_STR_LEN = 15;
+const S32 DB_BORN_BUF_SIZE = 16;
+
+// place.name
+const S32 DB_PLACE_NAME_LEN = 63;
+const S32 DB_PLACE_NAME_SIZE = 64;
+const S32 DB_PARCEL_NAME_LEN = 63;
+const S32 DB_PARCEL_NAME_SIZE = 64;
+
+// place.desc
+const S32 DB_PLACE_DESC_LEN = 255;
+const S32 DB_PLACE_DESC_SIZE = 256;
+const S32 DB_PARCEL_DESC_LEN = 255;
+const S32 DB_PARCEL_DESC_SIZE = 256;
+const S32 DB_PARCEL_MUSIC_URL_LEN = 255;
+const S32 DB_PARCEL_MEDIA_URL_LEN = 255;
+const S32 DB_PARCEL_MUSIC_URL_SIZE = 256;
+
+// date time that is easily human readable
+const S32 DB_DATETIME_STR_LEN = 35;
+const S32 DB_DATETIME_BUF_SIZE = 36;
+
+// date time that isn't easily human readable
+const S32 DB_TERSE_DATETIME_STR_LEN = 15;
+const S32 DB_TERSE_DATETIME_BUF_SIZE = 16;
+
+// indra.simulator constants
+const S32 DB_SIM_NAME_STR_LEN = 35;
+const S32 DB_SIM_NAME_BUF_SIZE = 36;
+const S32 DB_HOST_NAME_STR_LEN = 100;
+const S32 DB_HOST_NAME_BUF_SIZE = 101;
+const S32 DB_ESTATE_NAME_STR_LEN = 63;
+const S32 DB_ESTATE_NAME_BUF_SIZE = DB_ESTATE_NAME_STR_LEN + 1;
+
+// user_note.note
+const S32 DB_USER_NOTE_LEN = 1023;
+const S32 DB_USER_NOTE_SIZE = 1024;
+
+// pick.name
+const S32 DB_PICK_NAME_LEN = 63;
+const S32 DB_PICK_NAME_SIZE = 64;
+
+// pick.desc
+const S32 DB_PICK_DESC_LEN = 1023;
+const S32 DB_PICK_DESC_SIZE = 1024;
+
+#endif // LL_LLDBSTRINGS_H
diff --git a/indra/llmessage/lldispatcher.cpp b/indra/llmessage/lldispatcher.cpp
new file mode 100644
index 0000000000..8ba051765e
--- /dev/null
+++ b/indra/llmessage/lldispatcher.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file lldispatcher.cpp
+ * @brief Implementation of the dispatcher object.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lldispatcher.h"
+
+#include <algorithm>
+#include "llstl.h"
+#include "message.h"
+
+///----------------------------------------------------------------------------
+/// Class lldispatcher
+///----------------------------------------------------------------------------
+
+
+LLDispatcher::LLDispatcher()
+{
+}
+
+LLDispatcher::~LLDispatcher()
+{
+}
+
+bool LLDispatcher::isHandlerPresent(const key_t& name) const
+{
+ if(mHandlers.find(name) != mHandlers.end())
+ {
+ return true;
+ }
+ return false;
+}
+
+void LLDispatcher::copyAllHandlerNames(keys_t& names) const
+{
+ // copy the names onto the vector we are given
+ std::transform(
+ mHandlers.begin(),
+ mHandlers.end(),
+ std::back_insert_iterator<keys_t>(names),
+ llselect1st<dispatch_map_t::value_type>());
+}
+
+bool LLDispatcher::dispatch(
+ const key_t& name,
+ const LLUUID& invoice,
+ const sparam_t& strings) const
+{
+ dispatch_map_t::const_iterator it = mHandlers.find(name);
+ if(it != mHandlers.end())
+ {
+ LLDispatchHandler* func = (*it).second;
+ return (*func)(this, name, invoice, strings);
+ }
+ return false;
+}
+
+LLDispatchHandler* LLDispatcher::addHandler(
+ const key_t& name, LLDispatchHandler* func)
+{
+ dispatch_map_t::iterator it = mHandlers.find(name);
+ LLDispatchHandler* old_handler = NULL;
+ if(it != mHandlers.end())
+ {
+ old_handler = (*it).second;
+ mHandlers.erase(it);
+ }
+ if(func)
+ {
+ // only non-null handlers so that we don't have to worry about
+ // it later.
+ mHandlers.insert(dispatch_map_t::value_type(name, func));
+ }
+ return old_handler;
+}
+
+// static
+bool LLDispatcher::unpackMessage(
+ LLMessageSystem* msg,
+ LLDispatcher::key_t& method,
+ LLUUID& invoice,
+ LLDispatcher::sparam_t& parameters)
+{
+ char buf[MAX_STRING]; /*Flawfinder: ignore*/
+ msg->getStringFast(_PREHASH_MethodData, _PREHASH_Method, MAX_STRING, buf);
+ method.assign(buf);
+ msg->getUUIDFast(_PREHASH_MethodData, _PREHASH_Invoice, invoice);
+ S32 size;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_ParamList);
+ for (S32 i = 0; i < count; ++i)
+ {
+ // we treat the SParam as binary data (since it might be an
+ // LLUUID in compressed form which may have embedded \0's,)
+ size = msg->getSizeFast(_PREHASH_ParamList, i, _PREHASH_Parameter);
+ msg->getBinaryDataFast(
+ _PREHASH_ParamList, _PREHASH_Parameter,
+ buf, size, i, MAX_STRING-1);
+
+ // If the last byte of the data is 0x0, this is either a normally
+ // packed string, or a binary packed UUID (which for these messages
+ // are packed with a 17th byte 0x0). Unpack into a std::string
+ // without the trailing \0, so "abc\0" becomes std::string("abc", 3)
+ // which matches const char* "abc".
+ if (size > 0
+ && buf[size-1] == 0x0)
+ {
+ // special char*/size constructor because UUIDs may have embedded
+ // 0x0 bytes.
+ std::string binary_data(buf, size-1);
+ parameters.push_back(binary_data);
+ }
+ else
+ {
+ // This is either a NULL string, or a string that was packed
+ // incorrectly as binary data, without the usual trailing '\0'.
+ std::string string_data(buf, size);
+ parameters.push_back(string_data);
+ }
+ }
+ return true;
+}
diff --git a/indra/llmessage/lldispatcher.h b/indra/llmessage/lldispatcher.h
new file mode 100644
index 0000000000..e0eb706be1
--- /dev/null
+++ b/indra/llmessage/lldispatcher.h
@@ -0,0 +1,95 @@
+/**
+ * @file lldispatcher.h
+ * @brief LLDispatcher class header file.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDISPATCHER_H
+#define LL_LLDISPATCHER_H
+
+#include <map>
+#include <vector>
+#include <string>
+
+class LLDispatcher;
+class LLMessageSystem;
+class LLUUID;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDispatchHandler
+//
+// Abstract base class for handling dispatches. Derive your own
+// classes, construct them, and add them to the dispatcher you want to
+// use.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLDispatchHandler
+{
+public:
+ typedef std::vector<std::string> sparam_t;
+ //typedef std::vector<S32> iparam_t;
+ LLDispatchHandler() {}
+ virtual ~LLDispatchHandler() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& string) = 0;
+ //const iparam_t& integers) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDispatcher
+//
+// Basic utility class that handles dispatching keyed operations to
+// function objects implemented as LLDispatchHandler derivations.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLDispatcher
+{
+public:
+ typedef std::string key_t;
+ typedef std::vector<std::string> keys_t;
+ typedef std::vector<std::string> sparam_t;
+ //typedef std::vector<S32> iparam_t;
+
+ // construct a dispatcher.
+ LLDispatcher();
+ virtual ~LLDispatcher();
+
+ // Returns if they keyed handler exists in this dispatcher.
+ bool isHandlerPresent(const key_t& name) const;
+
+ // copy all known keys onto keys_t structure
+ void copyAllHandlerNames(keys_t& names) const;
+
+ // Call this method with the name of the request that has come
+ // in. If the handler is present, it is called with the params and
+ // returns the return value from
+ bool dispatch(
+ const key_t& name,
+ const LLUUID& invoice,
+ const sparam_t& strings) const;
+ //const iparam_t& itegers) const;
+
+ // Add a handler. If one with the same key already exists, its
+ // pointer is returned, otherwise returns NULL. This object does
+ // not do memory management of the LLDispatchHandler, and relies
+ // on the caller to delete the object if necessary.
+ LLDispatchHandler* addHandler(const key_t& name, LLDispatchHandler* func);
+
+ // Helper method to unpack the dispatcher message bus
+ // format. Returns true on success.
+ static bool unpackMessage(
+ LLMessageSystem* msg,
+ key_t& method,
+ LLUUID& invoice,
+ sparam_t& parameters);
+
+protected:
+ typedef std::map<key_t, LLDispatchHandler*> dispatch_map_t;
+ dispatch_map_t mHandlers;
+};
+
+#endif // LL_LLDISPATCHER_H
diff --git a/indra/llmessage/lleventflags.h b/indra/llmessage/lleventflags.h
new file mode 100644
index 0000000000..90120b2648
--- /dev/null
+++ b/indra/llmessage/lleventflags.h
@@ -0,0 +1,17 @@
+/**
+ * @file lleventflags.h
+ * @brief Flags for events.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTFLAGS_H
+#define LL_LLEVENTFLAGS_H
+
+const U32 EVENT_FLAG_NONE = 0x0000;
+
+// set for mature events
+const U32 EVENT_FLAG_MATURE = 0x0001;
+
+#endif
diff --git a/indra/llmessage/llfiltersd2xmlrpc.cpp b/indra/llmessage/llfiltersd2xmlrpc.cpp
new file mode 100644
index 0000000000..6d5f92e983
--- /dev/null
+++ b/indra/llmessage/llfiltersd2xmlrpc.cpp
@@ -0,0 +1,728 @@
+/**
+ * @file llfiltersd2xmlrpc.cpp
+ * @author Phoenix
+ * @date 2005-04-26
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * xml rpc request:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodCall><methodName>examples.getStateName</methodName>
+ * <params><param><value><i4>41</i4></value></param></params>
+ * </methodCall>
+ * </code>
+ *
+ * xml rpc response:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodResponse>
+ * <params><param><value><string>South Dakota</string></value></param></params>
+ * </methodResponse>
+ * </code>
+ *
+ * xml rpc fault:
+ * </code>
+ * <?xml version="1.0"?>
+ * <methodResponse>
+ * <fault><value><struct>
+ * <member><name>faultCode</name><value><int>4</int></value></member>
+ * <member><name>faultString</name><value><string>...</string></value></member>
+ * </struct></value></fault>
+ * </methodResponse>
+ * </code>
+ *
+ * llsd rpc request:
+ * <code>
+ * { 'method':'...', 'parameter':...]}
+ * </code>
+ *
+ * llsd rpc response:
+ * <code>
+ * { 'response':... }
+ * </code>
+ *
+ * llsd rpc fault:
+ * <code>
+ * { 'fault': {'code':i..., 'description':'...'} }
+ * </code>
+ *
+ */
+
+#include "linden_common.h"
+#include "llfiltersd2xmlrpc.h"
+
+#include <sstream>
+#include <iterator>
+#include <xmlrpc-epi/xmlrpc.h>
+#include "apr-1/apr_base64.h"
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llmemorystream.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "lluuid.h"
+
+// spammy mode
+//#define LL_SPEW_STREAM_OUT_DEBUGGING 1
+
+/**
+ * String constants
+ */
+static const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
+static const char XMLRPC_REQUEST_HEADER_1[] = "<methodCall><methodName>";
+static const char XMLRPC_REQUEST_HEADER_2[] = "</methodName><params>";
+static const char XMLRPC_REQUEST_FOOTER[] = "</params></methodCall>";
+static const char XMLRPC_METHOD_RESPONSE_HEADER[] = "<methodResponse>";
+static const char XMLRPC_METHOD_RESPONSE_FOOTER[] = "</methodResponse>";
+static const char XMLRPC_RESPONSE_HEADER[] = "<params><param>";
+static const char XMLRPC_RESPONSE_FOOTER[] = "</param></params>";
+static const char XMLRPC_FAULT_1[] = "<fault><value><struct><member><name>faultCode</name><value><int>";
+static const char XMLRPC_FAULT_2[] = "</int></value></member><member><name>faultString</name><value><string>";
+static const char XMLRPC_FAULT_3[] = "</string></value></member></struct></value></fault>";
+static const char LLSDRPC_RESPONSE_HEADER[] = "{'response':";
+static const char LLSDRPC_RESPONSE_FOOTER[] = "}";
+const char LLSDRPC_REQUEST_HEADER_1[] = "{'method':'";
+const char LLSDRPC_REQUEST_HEADER_2[] = "', 'parameter': ";
+const char LLSDRPC_REQUEST_FOOTER[] = "}";
+static const char LLSDRPC_FAULT_HADER_1[] = "{ 'fault': {'code':i";
+static const char LLSDRPC_FAULT_HADER_2[] = ", 'description':";
+static const char LLSDRPC_FAULT_FOOTER[] = "} }";
+static const S32 DEFAULT_PRECISION = 20;
+
+/**
+ * LLFilterSD2XMLRPC
+ */
+LLFilterSD2XMLRPC::LLFilterSD2XMLRPC()
+{
+}
+
+LLFilterSD2XMLRPC::~LLFilterSD2XMLRPC()
+{
+}
+
+std::string xml_escape_string(const std::string& in)
+{
+ std::ostringstream out;
+ std::string::const_iterator it = in.begin();
+ std::string::const_iterator end = in.end();
+ for(; it != end; ++it)
+ {
+ switch((*it))
+ {
+ case '<':
+ out << "&lt;";
+ break;
+ case '>':
+ out << "&gt;";
+ break;
+ case '&':
+ out << "&amp;";
+ break;
+ case '\'':
+ out << "&apos;";
+ break;
+ case '"':
+ out << "&quot;";
+ break;
+ default:
+ out << (*it);
+ break;
+ }
+ }
+ return out.str();
+}
+
+void LLFilterSD2XMLRPC::streamOut(std::ostream& ostr, const LLSD& sd)
+{
+ ostr << "<value>";
+ switch(sd.type())
+ {
+ case LLSD::TypeMap:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(map) BEGIN" << llendl;
+#endif
+ ostr << "<struct>";
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing struct" << llendl;
+ }
+ LLSD::map_const_iterator it = sd.beginMap();
+ LLSD::map_const_iterator end = sd.endMap();
+ for(; it != end; ++it)
+ {
+ ostr << "<member><name>" << xml_escape_string((*it).first)
+ << "</name>";
+ streamOut(ostr, (*it).second);
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing '" << (*it).first
+ << "' with sd type " << (*it).second.type() << llendl;
+ }
+ ostr << "</member>";
+ }
+ ostr << "</struct>";
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(map) END" << llendl;
+#endif
+ break;
+ }
+ case LLSD::TypeArray:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(array) BEGIN" << llendl;
+#endif
+ ostr << "<array><data>";
+ LLSD::array_const_iterator it = sd.beginArray();
+ LLSD::array_const_iterator end = sd.endArray();
+ for(; it != end; ++it)
+ {
+ streamOut(ostr, *it);
+ if(ostr.fail())
+ {
+ llinfos << "STREAM FAILURE writing array element sd type "
+ << (*it).type() << llendl;
+ }
+ }
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(array) END" << llendl;
+#endif
+ ostr << "</data></array>";
+ break;
+ }
+ case LLSD::TypeUndefined:
+ // treat undefined as a bool with a false value.
+ case LLSD::TypeBoolean:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(bool)" << llendl;
+#endif
+ ostr << "<boolean>" << (sd.asBoolean() ? "1" : "0") << "</boolean>";
+ break;
+ case LLSD::TypeInteger:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(int)" << llendl;
+#endif
+ ostr << "<i4>" << sd.asInteger() << "</i4>";
+ break;
+ case LLSD::TypeReal:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(real)" << llendl;
+#endif
+ ostr << "<double>" << sd.asReal() << "</double>";
+ break;
+ case LLSD::TypeString:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(string)" << llendl;
+#endif
+ ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
+ break;
+ case LLSD::TypeUUID:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(uuid)" << llendl;
+#endif
+ // serialize it as a string
+ ostr << "<string>" << sd.asString() << "</string>";
+ break;
+ case LLSD::TypeURI:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(uri)" << llendl;
+#endif
+ // serialize it as a string
+ ostr << "<string>" << xml_escape_string(sd.asString()) << "</string>";
+ break;
+ }
+ case LLSD::TypeBinary:
+ {
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(binary)" << llendl;
+#endif
+ // this is pretty inefficient, but we'll deal with that
+ // problem when it becomes one.
+ ostr << "<base64>";
+ LLSD::Binary buffer = sd.asBinary();
+ if(!buffer.empty())
+ {
+ int b64_buffer_length = apr_base64_encode_len(buffer.size());
+ char* b64_buffer = new char[b64_buffer_length];
+ b64_buffer_length = apr_base64_encode_binary(
+ b64_buffer,
+ &buffer[0],
+ buffer.size());
+ ostr.write(b64_buffer, b64_buffer_length - 1);
+ delete[] b64_buffer;
+ }
+ ostr << "</base64>";
+ break;
+ }
+ case LLSD::TypeDate:
+#if LL_SPEW_STREAM_OUT_DEBUGGING
+ llinfos << "streamOut(date)" << llendl;
+#endif
+ // no need to escape this since it will be alpha-numeric.
+ ostr << "<dateTime.iso8601>" << sd.asString() << "</dateTime.iso8601>";
+ break;
+ default:
+ // unhandled type
+ llwarns << "Unhandled structured data type: " << sd.type()
+ << llendl;
+ break;
+ }
+ ostr << "</value>";
+}
+
+/**
+ * LLFilterSD2XMLRPCResponse
+ */
+
+LLFilterSD2XMLRPCResponse::LLFilterSD2XMLRPCResponse()
+{
+}
+
+LLFilterSD2XMLRPCResponse::~LLFilterSD2XMLRPCResponse()
+{
+}
+
+
+// virtual
+LLIOPipe::EStatus LLFilterSD2XMLRPCResponse::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ // This pipe does not work if it does not have everyting. This
+ // could be addressed by making a stream parser for llsd which
+ // handled partial information.
+ if(!eos)
+ {
+ return STATUS_BREAK;
+ }
+
+ PUMP_DEBUG;
+ // we have everyting in the buffer, so turn the structure data rpc
+ // response into an xml rpc response.
+ LLBufferStream stream(channels, buffer.get());
+ stream << XML_HEADER << XMLRPC_METHOD_RESPONSE_HEADER;
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, stream);
+
+ PUMP_DEBUG;
+ LLIOPipe::EStatus rv = STATUS_ERROR;
+ if(sd.has("response"))
+ {
+ PUMP_DEBUG;
+ // it is a normal response. pack it up and ship it out.
+ stream.precision(DEFAULT_PRECISION);
+ stream << XMLRPC_RESPONSE_HEADER;
+ streamOut(stream, sd["response"]);
+ stream << XMLRPC_RESPONSE_FOOTER << XMLRPC_METHOD_RESPONSE_FOOTER;
+ rv = STATUS_DONE;
+ }
+ else if(sd.has("fault"))
+ {
+ PUMP_DEBUG;
+ // it is a fault.
+ stream << XMLRPC_FAULT_1 << sd["fault"]["code"].asInteger()
+ << XMLRPC_FAULT_2
+ << xml_escape_string(sd["fault"]["description"].asString())
+ << XMLRPC_FAULT_3 << XMLRPC_METHOD_RESPONSE_FOOTER;
+ rv = STATUS_DONE;
+ }
+ else
+ {
+ llwarns << "Unable to determine the type of LLSD response." << llendl;
+ }
+ PUMP_DEBUG;
+ return rv;
+}
+
+/**
+ * LLFilterSD2XMLRPCRequest
+ */
+LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest()
+{
+}
+
+LLFilterSD2XMLRPCRequest::LLFilterSD2XMLRPCRequest(const char* method)
+{
+ if(method)
+ {
+ mMethod.assign(method);
+ }
+}
+
+LLFilterSD2XMLRPCRequest::~LLFilterSD2XMLRPCRequest()
+{
+}
+
+// virtual
+LLIOPipe::EStatus LLFilterSD2XMLRPCRequest::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ // This pipe does not work if it does not have everyting. This
+ // could be addressed by making a stream parser for llsd which
+ // handled partial information.
+ PUMP_DEBUG;
+ if(!eos)
+ {
+ llinfos << "!eos" << llendl;
+ return STATUS_BREAK;
+ }
+
+ // See if we can parse it
+ LLBufferStream stream(channels, buffer.get());
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, stream);
+ if(stream.fail())
+ {
+ llinfos << "STREAM FAILURE reading structure data." << llendl;
+ }
+
+ PUMP_DEBUG;
+ // We can get the method and parameters from either the member
+ // function or passed in via the buffer. We prefer the buffer if
+ // we found a parameter and a method, or fall back to using
+ // mMethod and putting everyting in the buffer into the parameter.
+ std::string method;
+ LLSD param_sd;
+ if(sd.has("method") && sd.has("parameter"))
+ {
+ method = sd["method"].asString();
+ param_sd = sd["parameter"];
+ }
+ else
+ {
+ method = mMethod;
+ param_sd = sd;
+ }
+ if(method.empty())
+ {
+ llwarns << "SD -> XML Request no method found." << llendl;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ // We have a method, and some kind of parameter, so package it up
+ // and send it out.
+ LLBufferStream ostream(channels, buffer.get());
+ ostream.precision(DEFAULT_PRECISION);
+ if(ostream.fail())
+ {
+ llinfos << "STREAM FAILURE setting precision" << llendl;
+ }
+ ostream << XML_HEADER << XMLRPC_REQUEST_HEADER_1
+ << xml_escape_string(method) << XMLRPC_REQUEST_HEADER_2;
+ if(ostream.fail())
+ {
+ llinfos << "STREAM FAILURE writing method headers" << llendl;
+ }
+ switch(param_sd.type())
+ {
+ case LLSD::TypeMap:
+ // If the params are a map, then we do not want to iterate
+ // through them since the iterators returned will be map
+ // ordered un-named values, which will lose the names, and
+ // only stream the values, turning it into an array.
+ ostream << "<param>";
+ streamOut(ostream, param_sd);
+ ostream << "</param>";
+ break;
+ case LLSD::TypeArray:
+ {
+
+ LLSD::array_iterator it = param_sd.beginArray();
+ LLSD::array_iterator end = param_sd.endArray();
+ for(; it != end; ++it)
+ {
+ ostream << "<param>";
+ streamOut(ostream, *it);
+ ostream << "</param>";
+ }
+ break;
+ }
+ default:
+ ostream << "<param>";
+ streamOut(ostream, param_sd);
+ ostream << "</param>";
+ break;
+ }
+
+ stream << XMLRPC_REQUEST_FOOTER;
+ return STATUS_DONE;
+}
+
+/**
+ * LLFilterXMLRPCResponse2LLSD
+ */
+// this is a c function here since it's really an implementation
+// detail that requires a header file just get the definition of the
+// parameters.
+LLIOPipe::EStatus stream_out(std::ostream& ostr, XMLRPC_VALUE value)
+{
+ XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(value);
+ LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
+ switch(type)
+ {
+ case xmlrpc_type_base64:
+ {
+ S32 len = XMLRPC_GetValueStringLen(value);
+ const char* buf = XMLRPC_GetValueBase64(value);
+ ostr << " b(";
+ if((len > 0) && buf)
+ {
+ ostr << len << ")\"";
+ ostr.write(buf, len);
+ ostr << "\"";
+ }
+ else
+ {
+ ostr << "0)\"\"";
+ }
+ break;
+ }
+ case xmlrpc_type_boolean:
+ //lldebugs << "stream_out() bool" << llendl;
+ ostr << " " << (XMLRPC_GetValueBoolean(value) ? "true" : "false");
+ break;
+ case xmlrpc_type_datetime:
+ ostr << " d\"" << XMLRPC_GetValueDateTime_ISO8601(value) << "\"";
+ break;
+ case xmlrpc_type_double:
+ ostr << " r" << XMLRPC_GetValueDouble(value);
+ //lldebugs << "stream_out() double" << XMLRPC_GetValueDouble(value)
+ // << llendl;
+ break;
+ case xmlrpc_type_int:
+ ostr << " i" << XMLRPC_GetValueInt(value);
+ //lldebugs << "stream_out() integer:" << XMLRPC_GetValueInt(value)
+ // << llendl;
+ break;
+ case xmlrpc_type_string:
+ //lldebugs << "stream_out() string: " << str << llendl;
+ ostr << " s(" << XMLRPC_GetValueStringLen(value) << ")'"
+ << XMLRPC_GetValueString(value) << "'";
+ break;
+ case xmlrpc_type_array: // vector
+ case xmlrpc_type_mixed: // vector
+ {
+ //lldebugs << "stream_out() array" << llendl;
+ ostr << " [";
+ U32 needs_comma = 0;
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
+ while(current && (LLIOPipe::STATUS_OK == status))
+ {
+ if(needs_comma++) ostr << ",";
+ status = stream_out(ostr, current);
+ current = XMLRPC_VectorNext(value);
+ }
+ ostr << "]";
+ break;
+ }
+ case xmlrpc_type_struct: // still vector
+ {
+ //lldebugs << "stream_out() struct" << llendl;
+ ostr << " {";
+ std::string name;
+ U32 needs_comma = 0;
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(value);
+ while(current && (LLIOPipe::STATUS_OK == status))
+ {
+ if(needs_comma++) ostr << ",";
+ name.assign(XMLRPC_GetValueID(current));
+ ostr << "'" << LLSDNotationFormatter::escapeString(name) << "':";
+ status = stream_out(ostr, current);
+ current = XMLRPC_VectorNext(value);
+ }
+ ostr << "}";
+ break;
+ }
+ case xmlrpc_type_empty:
+ case xmlrpc_type_none:
+ default:
+ status = LLIOPipe::STATUS_ERROR;
+ llwarns << "Found an empty xmlrpc type.." << llendl;
+ // not much we can do here...
+ break;
+ };
+ return status;
+}
+
+LLFilterXMLRPCResponse2LLSD::LLFilterXMLRPCResponse2LLSD()
+{
+}
+
+LLFilterXMLRPCResponse2LLSD::~LLFilterXMLRPCResponse2LLSD()
+{
+}
+
+LLIOPipe::EStatus LLFilterXMLRPCResponse2LLSD::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ if(!eos) return STATUS_BREAK;
+ if(!buffer) return STATUS_ERROR;
+
+ PUMP_DEBUG;
+ // *FIX: This technique for reading data is far from optimal. We
+ // need to have some kind of istream interface into the xml
+ // parser...
+ S32 bytes = buffer->countAfter(channels.in(), NULL);
+ if(!bytes) return STATUS_ERROR;
+ char* buf = new char[bytes + 1];
+ buf[bytes] = '\0';
+ buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
+
+ //lldebugs << "xmlrpc response: " << buf << llendl;
+
+ PUMP_DEBUG;
+ XMLRPC_REQUEST response = XMLRPC_REQUEST_FromXML(
+ buf,
+ bytes,
+ NULL);
+ if(!response)
+ {
+ llwarns << "XML -> SD Response unable to parse xml." << llendl;
+ delete[] buf;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ LLBufferStream stream(channels, buffer.get());
+ stream.precision(DEFAULT_PRECISION);
+ if(XMLRPC_ResponseIsFault(response))
+ {
+ PUMP_DEBUG;
+ stream << LLSDRPC_FAULT_HADER_1
+ << XMLRPC_GetResponseFaultCode(response)
+ << LLSDRPC_FAULT_HADER_2;
+ const char* fault_str = XMLRPC_GetResponseFaultString(response);
+ std::string fault_string;
+ if(fault_str)
+ {
+ fault_string.assign(fault_str);
+ }
+ stream << "'" << LLSDNotationFormatter::escapeString(fault_string)
+ << "'" <<LLSDRPC_FAULT_FOOTER;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ stream << LLSDRPC_RESPONSE_HEADER;
+ XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
+ if(param)
+ {
+ stream_out(stream, param);
+ }
+ stream << LLSDRPC_RESPONSE_FOOTER;
+ }
+ PUMP_DEBUG;
+ XMLRPC_RequestFree(response, 1);
+ delete[] buf;
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
+/**
+ * LLFilterXMLRPCRequest2LLSD
+ */
+LLFilterXMLRPCRequest2LLSD::LLFilterXMLRPCRequest2LLSD()
+{
+}
+
+LLFilterXMLRPCRequest2LLSD::~LLFilterXMLRPCRequest2LLSD()
+{
+}
+
+LLIOPipe::EStatus LLFilterXMLRPCRequest2LLSD::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ if(!eos) return STATUS_BREAK;
+ if(!buffer) return STATUS_ERROR;
+
+ PUMP_DEBUG;
+ // *FIX: This technique for reading data is far from optimal. We
+ // need to have some kind of istream interface into the xml
+ // parser...
+ S32 bytes = buffer->countAfter(channels.in(), NULL);
+ if(!bytes) return STATUS_ERROR;
+ char* buf = new char[bytes + 1];
+ buf[bytes] = '\0';
+ buffer->readAfter(channels.in(), NULL, (U8*)buf, bytes);
+
+ //lldebugs << "xmlrpc request: " << buf << llendl;
+
+ PUMP_DEBUG;
+ XMLRPC_REQUEST request = XMLRPC_REQUEST_FromXML(
+ buf,
+ bytes,
+ NULL);
+ if(!request)
+ {
+ llwarns << "XML -> SD Request process parse error." << llendl;
+ delete[] buf;
+ return STATUS_ERROR;
+ }
+
+ PUMP_DEBUG;
+ LLBufferStream stream(channels, buffer.get());
+ stream.precision(DEFAULT_PRECISION);
+ const char* name = XMLRPC_RequestGetMethodName(request);
+ stream << LLSDRPC_REQUEST_HEADER_1 << (name ? name : "")
+ << LLSDRPC_REQUEST_HEADER_2;
+ XMLRPC_VALUE param = XMLRPC_RequestGetData(request);
+ if(param)
+ {
+ PUMP_DEBUG;
+ S32 size = XMLRPC_VectorSize(param);
+ if(size > 1)
+ {
+ // if there are multiple parameters, stuff the values into
+ // an array so that the next step in the chain can read them.
+ stream << "[";
+ }
+ XMLRPC_VALUE current = XMLRPC_VectorRewind(param);
+ bool needs_comma = false;
+ while(current)
+ {
+ if(needs_comma)
+ {
+ stream << ",";
+ }
+ needs_comma = true;
+ stream_out(stream, current);
+ current = XMLRPC_VectorNext(param);
+ }
+ if(size > 1)
+ {
+ // close the array
+ stream << "]";
+ }
+ }
+ stream << LLSDRPC_REQUEST_FOOTER;
+ XMLRPC_RequestFree(request, 1);
+ delete[] buf;
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
diff --git a/indra/llmessage/llfiltersd2xmlrpc.h b/indra/llmessage/llfiltersd2xmlrpc.h
new file mode 100644
index 0000000000..efde349271
--- /dev/null
+++ b/indra/llmessage/llfiltersd2xmlrpc.h
@@ -0,0 +1,253 @@
+/**
+ * @file llfiltersd2xmlrpc.h
+ * @author Phoenix
+ * @date 2005-04-26
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFILTERSD2XMLRPC_H
+#define LL_LLFILTERSD2XMLRPC_H
+
+/**
+ * These classes implement the necessary pipes for translating between
+ * xmlrpc and llsd rpc. The llsd rpcs mechanism was developed as an
+ * extensible and easy to parse serialization grammer which maintains
+ * a time efficient in-memory representation.
+ */
+
+#include <iosfwd>
+#include "lliopipe.h"
+
+/**
+ * @class LLFilterSD2XMLRPC
+ * @brief Filter from serialized LLSD to an XMLRPC method call
+ *
+ * This clas provides common functionality for the LLFilterSD2XMLRPRC
+ * request and response classes.
+ */
+class LLFilterSD2XMLRPC : public LLIOPipe
+{
+public:
+ LLFilterSD2XMLRPC();
+ virtual ~LLFilterSD2XMLRPC();
+
+protected:
+ /**
+ * @brief helper method
+ */
+ void streamOut(std::ostream& ostr, const LLSD& sd);
+};
+
+/**
+ * @class LLFilterSD2XMLRPCResponse
+ * @brief Filter from serialized LLSD to an XMLRPC response
+ *
+ * This class filters a serialized LLSD object to an xmlrpc
+ * repsonse. Since resonses are limited to a single param, the xmlrprc
+ * response only serializes it as one object.
+ * This class correctly handles normal llsd responses as well as llsd
+ * rpc faults.
+ *
+ * For example, if given:
+ * <code>{'response':[ i200, r3.4, {"foo":"bar"} ]}</code>
+ * Would generate:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodResponse><params><param><array><data>
+ * <value><int>200</int></value>
+ * <value><double>3.4</double></value>
+ * <value><struct><member>
+ * <name>foo</name><value><string>bar</string></value></member>
+ * </struct></value>
+ * </data></array></param></params></methodResponse>
+ * </code>
+ */
+class LLFilterSD2XMLRPCResponse : public LLFilterSD2XMLRPC
+{
+public:
+ // constructor
+ LLFilterSD2XMLRPCResponse();
+
+ // destructor
+ virtual ~LLFilterSD2XMLRPCResponse();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+};
+
+/**
+ * @class LLFilterSD2XMLRPCRequest
+ * @brief Filter from serialized LLSD to an XMLRPC method call
+ *
+ * This class will accept any kind of serialized LLSD object, but you
+ * probably want to have an array on the outer boundary since this
+ * object will interpret each element in the top level LLSD as a
+ * parameter into the xmlrpc spec.
+ *
+ * For example, you would represent 3 params as:
+ * <code>
+ * {'method'='foo', 'parameter':[i200, r3.4, {"foo":"bar"}]}
+ * </code>
+ * To generate:
+ * <code>
+ * <?xml version="1.0"?>
+ * <methodCall><params>
+ * <param><value><int>200</int></value></param>
+ * <param><value><double>3.4</double></value></param>
+ * <param><value><struct><member>
+ * <name>foo</name><value><string>bar</string></value></member>
+ * </struct></value></param>
+ * </params></methodCall>
+ *
+ * This class will accept 2 different kinds of encodings. The first
+ * just an array of params as long as you specify the method in the
+ * constructor. It will also accept a structured data in the form:
+ * {'method':'$method_name', 'parameter':[...] } In the latter form, the
+ * encoded 'method' will be used regardless of the construction of the
+ * object, and the 'parameter' will be used as parameter to the call.
+ */
+class LLFilterSD2XMLRPCRequest : public LLFilterSD2XMLRPC
+{
+public:
+ // constructor
+ LLFilterSD2XMLRPCRequest();
+
+ // constructor
+ LLFilterSD2XMLRPCRequest(const char* method);
+
+ // destructor
+ virtual ~LLFilterSD2XMLRPCRequest();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ // The method name of this request.
+ std::string mMethod;
+};
+
+/**
+ * @class LLFilterXMLRPCResponse2LLSD
+ * @brief Filter from serialized XMLRPC method response to LLSD
+ *
+ * The xmlrpc spec states that responses can only have one element
+ * which can be of any supported type.
+ * This takes in xml of the form:
+ * <code>
+ * <?xml version=\"1.0\"?><methodResponse><params><param>
+ * <value><string>ok</string></value></param></params></methodResponse>
+ * </code>
+ * And processes it into:
+ * <code>'ok'</code>
+ *
+ */
+class LLFilterXMLRPCResponse2LLSD : public LLIOPipe
+{
+public:
+ // constructor
+ LLFilterXMLRPCResponse2LLSD();
+
+ // destructor
+ virtual ~LLFilterXMLRPCResponse2LLSD();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+};
+
+/**
+ * @class LLFilterXMLRPCRequest2LLSD
+ * @brief Filter from serialized XMLRPC method call to LLSD
+ *
+ * This takes in xml of the form:
+ * <code>
+ * <?xml version=\"1.0\"?><methodCall>
+ * <methodName>repeat</methodName>
+ * <params>
+ * <param><value><i4>4</i4></value></param>
+ * <param><value><string>ok</string></value></param>
+ * </params></methodCall>
+ * </code>
+ * And processes it into:
+ * <code>{ 'method':'repeat', 'params':[i4, 'ok'] }</code>
+ */
+class LLFilterXMLRPCRequest2LLSD : public LLIOPipe
+{
+public:
+ // constructor
+ LLFilterXMLRPCRequest2LLSD();
+
+ // destructor
+ virtual ~LLFilterXMLRPCRequest2LLSD();
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+};
+
+/**
+ * @brief This function takes string, and escapes it appropritately
+ * for inclusion as xml data.
+ */
+std::string xml_escape_string(const std::string& in);
+
+/**
+ * @brief Externally available constants
+ */
+extern const char LLSDRPC_REQUEST_HEADER_1[];
+extern const char LLSDRPC_REQUEST_HEADER_2[];
+extern const char LLSDRPC_REQUEST_FOOTER[];
+
+#endif // LL_LLFILTERSD2XMLRPC_H
diff --git a/indra/llmessage/llfollowcamparams.h b/indra/llmessage/llfollowcamparams.h
new file mode 100644
index 0000000000..1fa2a089ae
--- /dev/null
+++ b/indra/llmessage/llfollowcamparams.h
@@ -0,0 +1,43 @@
+/**
+ * @file llfollowcamparams.h
+ * @brief Follow camera parameters.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_FOLLOWCAM_PARAMS_H
+#define LL_FOLLOWCAM_PARAMS_H
+
+
+//Ventrella Follow Cam Script Stuff
+enum EFollowCamAttributes {
+ FOLLOWCAM_PITCH = 0,
+ FOLLOWCAM_FOCUS_OFFSET,
+ FOLLOWCAM_FOCUS_OFFSET_X, //this HAS to come after FOLLOWCAM_FOCUS_OFFSET in this list
+ FOLLOWCAM_FOCUS_OFFSET_Y,
+ FOLLOWCAM_FOCUS_OFFSET_Z,
+ FOLLOWCAM_POSITION_LAG,
+ FOLLOWCAM_FOCUS_LAG,
+ FOLLOWCAM_DISTANCE,
+ FOLLOWCAM_BEHINDNESS_ANGLE,
+ FOLLOWCAM_BEHINDNESS_LAG,
+ FOLLOWCAM_POSITION_THRESHOLD,
+ FOLLOWCAM_FOCUS_THRESHOLD,
+ FOLLOWCAM_ACTIVE,
+ FOLLOWCAM_POSITION,
+ FOLLOWCAM_POSITION_X, //this HAS to come after FOLLOWCAM_POSITION in this list
+ FOLLOWCAM_POSITION_Y,
+ FOLLOWCAM_POSITION_Z,
+ FOLLOWCAM_FOCUS,
+ FOLLOWCAM_FOCUS_X, //this HAS to come after FOLLOWCAM_FOCUS in this list
+ FOLLOWCAM_FOCUS_Y,
+ FOLLOWCAM_FOCUS_Z,
+ FOLLOWCAM_POSITION_LOCKED,
+ FOLLOWCAM_FOCUS_LOCKED,
+ NUM_FOLLOWCAM_ATTRIBUTES
+};
+
+//end Ventrella
+
+#endif //FOLLOWCAM_PARAMS_H
diff --git a/indra/llmessage/llhost.cpp b/indra/llmessage/llhost.cpp
new file mode 100644
index 0000000000..f4a1740663
--- /dev/null
+++ b/indra/llmessage/llhost.cpp
@@ -0,0 +1,216 @@
+/**
+ * @file llhost.cpp
+ * @brief Encapsulates an IP address and a port.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+
+#if !LL_WINDOWS
+#include <netdb.h>
+#include <netinet/in.h> // ntonl()
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#endif
+
+#include "llhost.h"
+
+#include "llerror.h"
+
+LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS);
+
+LLHost::LLHost(const std::string& ip_and_port)
+{
+ std::string::size_type colon_index = ip_and_port.find(":");
+ if (colon_index != std::string::npos)
+ {
+ mIP = ip_string_to_u32(ip_and_port.c_str());
+ mPort = 0;
+ }
+ else
+ {
+ std::string ip_str(ip_and_port, 0, colon_index);
+ std::string port_str(ip_and_port, colon_index+1);
+
+ mIP = ip_string_to_u32(ip_str.c_str());
+ mPort = atol(port_str.c_str());
+ }
+}
+
+void LLHost::getString(char* buffer, S32 length) const
+{
+ if (((U32) length) < MAXADDRSTR + 1 + 5)
+ {
+ llerrs << "LLHost::getString - string too short" << llendl;
+ return;
+ }
+
+ snprintf(buffer, length, "%s:%u", u32_to_ip_string(mIP), mPort); /*Flawfinder: ignore*/
+}
+
+void LLHost::getIPString(char* buffer, S32 length) const
+{
+ if ( ((U32) length) < MAXADDRSTR)
+ {
+ llerrs << "LLHost::getIPString - string too short" << llendl;
+ return;
+ }
+
+ snprintf(buffer, length, "%s", u32_to_ip_string(mIP)); /*Flawfinder: ignore*/
+}
+
+
+std::string LLHost::getIPandPort() const
+{
+ char buffer[MAXADDRSTR + 1 + 5];
+ getString(buffer, sizeof(buffer));
+ return buffer;
+}
+
+
+std::string LLHost::getIPString() const
+{
+ return std::string( u32_to_ip_string( mIP ) );
+}
+
+
+void LLHost::getHostName(char *buf, S32 len) const
+{
+ hostent *he;
+
+ if (INVALID_HOST_IP_ADDRESS == mIP)
+ {
+ llwarns << "LLHost::getHostName() : Invalid IP address" << llendl;
+ buf[0] = '\0';
+ return;
+ }
+ he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET);
+ if (!he)
+ {
+#if LL_WINDOWS
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << WSAGetLastError() << llendl;
+#else
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << h_errno << llendl;
+#endif
+ buf[0] = '\0';
+ }
+ else
+ {
+ strncpy(buf, he->h_name, len); /*Flawfinder: ignore*/
+ buf[len-1] = '\0';
+ }
+}
+
+LLString LLHost::getHostName() const
+{
+ hostent *he;
+
+ if (INVALID_HOST_IP_ADDRESS == mIP)
+ {
+ llwarns << "LLHost::getHostName() : Invalid IP address" << llendl;
+ return "";
+ }
+ he = gethostbyaddr((char *)&mIP, sizeof(mIP), AF_INET);
+ if (!he)
+ {
+#if LL_WINDOWS
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << WSAGetLastError() << llendl;
+#else
+ llwarns << "LLHost::getHostName() : Couldn't find host name for address " << mIP << ", Error: "
+ << h_errno << llendl;
+#endif
+ return "";
+ }
+ else
+ {
+ LLString hostname = he->h_name;
+ return hostname;
+ }
+}
+
+BOOL LLHost::setHostByName(const char *string)
+{
+ hostent *he;
+ char local_name[MAX_STRING]; /*Flawfinder: ignore*/
+
+ if (strlen(string)+1 > MAX_STRING) /*Flawfinder: ignore*/
+ {
+ llerrs << "LLHost::setHostByName() : Address string is too long: "
+ << string << llendl;
+ }
+
+ strncpy(local_name, string,MAX_STRING); /*Flawfinder: ignore*/
+ local_name[MAX_STRING-1] = '\0';
+#if LL_WINDOWS
+ // We may need an equivalent for Linux, but not sure - djs
+ _strupr(local_name);
+#endif
+
+ he = gethostbyname(local_name);
+ if(!he)
+ {
+ U32 ip_address = inet_addr(string);
+ he = gethostbyaddr((char *)&ip_address, sizeof(ip_address), AF_INET);
+ }
+
+ if (he)
+ {
+ mIP = *(U32 *)he->h_addr_list[0];
+ return TRUE;
+ }
+ else
+ {
+ setAddress(local_name);
+
+ // In windows, h_errno is a macro for WSAGetLastError(), so store value here
+ S32 error_number = h_errno;
+ switch(error_number)
+ {
+ case TRY_AGAIN: // XXX how to handle this case?
+ llwarns << "LLHost::setAddress(): try again" << llendl;
+ break;
+ case HOST_NOT_FOUND:
+ case NO_ADDRESS: // NO_DATA
+ llwarns << "LLHost::setAddress(): host not found" << llendl;
+ break;
+ case NO_RECOVERY:
+ llwarns << "LLHost::setAddress(): unrecoverable error" << llendl;
+ break;
+ default:
+ llwarns << "LLHost::setAddress(): unknown error - " << error_number << llendl;
+ break;
+ }
+ return FALSE;
+ }
+}
+
+LLHost& LLHost::operator=(const LLHost &rhs)
+{
+ if (this != &rhs)
+ {
+ set(rhs.getAddress(), rhs.getPort());
+ }
+ return *this;
+}
+
+
+std::ostream& operator<< (std::ostream& os, const LLHost &hh)
+{
+ os << u32_to_ip_string(hh.mIP) << ":" << hh.mPort ;
+ return os;
+}
+
+
+std::istream& operator>> (std::istream& is, LLHost &rh)
+{
+ is >> rh.mIP;
+ is >> rh.mPort;
+ return is;
+}
diff --git a/indra/llmessage/llhost.h b/indra/llmessage/llhost.h
new file mode 100644
index 0000000000..09dbae61b9
--- /dev/null
+++ b/indra/llmessage/llhost.h
@@ -0,0 +1,133 @@
+/**
+ * @file llhost.h
+ * @brief a LLHost uniquely defines a host (Simulator, Proxy or other)
+ * across the network
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHOST_H
+#define LL_LLHOST_H
+
+#include <iostream>
+#include <string>
+
+#include "net.h"
+
+#include "llstring.h"
+
+const U32 INVALID_PORT = 0;
+const U32 INVALID_HOST_IP_ADDRESS = 0x0;
+
+class LLHost {
+protected:
+ U32 mPort;
+ U32 mIP;
+public:
+
+ static LLHost invalid;
+
+ // CREATORS
+ LLHost()
+ : mPort(INVALID_PORT),
+ mIP(INVALID_HOST_IP_ADDRESS)
+ { } // STL's hash_map expect this T()
+
+ LLHost( U32 ipv4_addr, U32 port )
+ : mPort( port )
+ {
+ mIP = ipv4_addr;
+ }
+
+ LLHost( const char *ipv4_addr, U32 port )
+ : mPort( port )
+ {
+ mIP = ip_string_to_u32(ipv4_addr);
+ }
+
+ explicit LLHost(const U64 ip_port)
+ {
+ U32 ip = (U32)(ip_port >> 32);
+ U32 port = (U32)(ip_port & (U64)0xFFFFFFFF);
+ mIP = ip;
+ mPort = port;
+ }
+
+ explicit LLHost(const std::string& ip_and_port);
+
+ ~LLHost()
+ { }
+
+ // MANIPULATORS
+ void set( U32 ip, U32 port ) { mIP = ip; mPort = port; }
+ void set( const char* ipstr, U32 port ) { mIP = ip_string_to_u32(ipstr); mPort = port; }
+ void setAddress( const char* ipstr ) { mIP = ip_string_to_u32(ipstr); }
+ void setAddress( U32 ip ) { mIP = ip; }
+ void setPort( U32 port ) { mPort = port; }
+ BOOL setHostByName(const char *hname);
+
+ LLHost& operator=(const LLHost &rhs);
+ void invalidate() { mIP = INVALID_HOST_IP_ADDRESS; mPort = INVALID_PORT;};
+
+ // READERS
+ U32 getAddress() const { return mIP; }
+ U32 getPort() const { return mPort; }
+ BOOL isOk() const { return (mIP != INVALID_HOST_IP_ADDRESS) && (mPort != INVALID_PORT); }
+ size_t hash() const { return (mIP << 16) | (mPort & 0xffff); }
+ void getString(char* buffer, S32 length) const; // writes IP:port into buffer
+ void getIPString(char* buffer, S32 length) const; // writes IP into buffer
+ std::string getIPString() const;
+ void getHostName(char *buf, S32 len) const;
+ LLString getHostName() const;
+ std::string getIPandPort() const;
+
+ friend std::ostream& operator<< (std::ostream& os, const LLHost &hh);
+ friend std::istream& operator>> (std::istream& is, LLHost &hh);
+ friend bool operator==( const LLHost &lhs, const LLHost &rhs );
+ friend bool operator!=( const LLHost &lhs, const LLHost &rhs );
+ friend bool operator<(const LLHost &lhs, const LLHost &rhs);
+};
+
+
+// Function Object required for STL templates using LLHost as key
+class LLHostHash
+{
+public:
+ size_t operator() (const LLHost &hh) const { return hh.hash(); }
+};
+
+
+inline bool operator==( const LLHost &lhs, const LLHost &rhs )
+{
+ return (lhs.mIP == rhs.mIP) && (lhs.mPort == rhs.mPort);
+}
+
+inline bool operator!=( const LLHost &lhs, const LLHost &rhs )
+{
+ return (lhs.mIP != rhs.mIP) || (lhs.mPort != rhs.mPort);
+}
+
+inline bool operator<(const LLHost &lhs, const LLHost &rhs)
+{
+ if (lhs.mIP < rhs.mIP)
+ {
+ return true;
+ }
+ if (lhs.mIP > rhs.mIP)
+ {
+ return false;
+ }
+
+ if (lhs.mPort < rhs.mPort)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+#endif // LL_LLHOST_H
diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp
new file mode 100644
index 0000000000..12d9d610cc
--- /dev/null
+++ b/indra/llmessage/llhttpassetstorage.cpp
@@ -0,0 +1,996 @@
+/**
+ * @file llhttpassetstorage.cpp
+ * @brief Subclass capable of loading asset data to/from an external
+ * source. Currently, a web server accessed via curl
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llhttpassetstorage.h"
+
+#include "indra_constants.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#include "zlib/zlib.h"
+
+const F32 MAX_PROCESSING_TIME = 0.005f;
+const S32 CURL_XFER_BUFFER_SIZE = 65536;
+// Try for 30 minutes for now.
+const F32 GET_URL_TO_FILE_TIMEOUT = 1800.0f;
+
+const S32 COMPRESSED_INPUT_BUFFER_SIZE = 4096;
+
+const S32 HTTP_OK = 200;
+const S32 HTTP_PUT_OK = 201;
+const S32 HTTP_NO_CONTENT = 204;
+const S32 HTTP_MISSING = 404;
+const S32 HTTP_SERVER_BAD_GATEWAY = 502;
+const S32 HTTP_SERVER_TEMP_UNAVAILABLE = 503;
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLTempAssetData
+// An asset not stored on central asset store, but on a simulator node somewhere.
+/////////////////////////////////////////////////////////////////////////////////
+struct LLTempAssetData
+{
+ LLUUID mAssetID;
+ LLUUID mAgentID;
+ std::string mHostName;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLHTTPAssetRequest
+/////////////////////////////////////////////////////////////////////////////////
+
+class LLHTTPAssetRequest : public LLAssetRequest
+{
+public:
+ LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi);
+ virtual ~LLHTTPAssetRequest();
+
+ void setupCurlHandle();
+
+ void prepareCompressedUpload();
+ void finishCompressedUpload();
+ size_t readCompressedData(void* data, size_t size);
+
+ static size_t curlCompressedUploadCallback(
+ void *data, size_t size, size_t nmemb, void *user_data);
+
+public:
+ LLHTTPAssetStorage *mAssetStoragep;
+
+ CURL *mCurlHandle;
+ CURLM *mCurlMultiHandle;
+ char *mURLBuffer;
+ struct curl_slist *mHTTPHeaders;
+ LLVFile *mVFile;
+ LLUUID mTmpUUID;
+ BOOL mIsUpload;
+ BOOL mIsLocalUpload;
+ BOOL mIsDownload;
+
+ bool mZInitialized;
+ z_stream mZStream;
+ char* mZInputBuffer;
+ bool mZInputExhausted;
+
+ FILE *mFP;
+};
+
+
+LLHTTPAssetRequest::LLHTTPAssetRequest(LLHTTPAssetStorage *asp, const LLUUID &uuid, LLAssetType::EType type, const char *url, CURLM *curl_multi)
+ : LLAssetRequest(uuid, type),
+ mZInitialized(false)
+{
+ mAssetStoragep = asp;
+ mCurlHandle = NULL;
+ mCurlMultiHandle = curl_multi;
+ mVFile = NULL;
+ mIsUpload = FALSE;
+ mIsLocalUpload = FALSE;
+ mIsDownload = FALSE;
+ mHTTPHeaders = NULL;
+
+ mURLBuffer = new char[strlen(url) + 1]; /*Flawfinder: ignore*/
+ if (mURLBuffer)
+ {
+ strcpy(mURLBuffer, url);
+ }
+}
+
+LLHTTPAssetRequest::~LLHTTPAssetRequest()
+{
+ // Cleanup/cancel the request
+ if (mCurlHandle)
+ {
+ curl_multi_remove_handle(mCurlMultiHandle, mCurlHandle);
+ curl_easy_cleanup(mCurlHandle);
+ if (mAssetStoragep)
+ {
+ // Terminating a request. Thus upload or download is no longer pending.
+ if (mIsUpload)
+ {
+ mAssetStoragep->clearPendingUpload();
+ }
+ else if (mIsLocalUpload)
+ {
+ mAssetStoragep->clearPendingLocalUpload();
+ }
+ else if (mIsDownload)
+ {
+ mAssetStoragep->clearPendingDownload();
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - Destroyed request is not upload OR download, this is bad!" << llendl;
+ }
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::~LLHTTPAssetRequest - No asset storage associated with this request!" << llendl;
+ }
+ }
+ if (mHTTPHeaders)
+ {
+ curl_slist_free_all(mHTTPHeaders);
+ }
+ delete[] mURLBuffer;
+ delete mVFile;
+ finishCompressedUpload();
+}
+
+void LLHTTPAssetRequest::setupCurlHandle()
+{
+ mCurlHandle = curl_easy_init();
+ curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1);
+ curl_easy_setopt(mCurlHandle, CURLOPT_URL, mURLBuffer);
+ curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this);
+ if (mIsDownload)
+ {
+ curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, "");
+ // only do this on downloads, as uploads
+ // to some apache configs (like our test grids)
+ // mistakenly claim the response is gzip'd if the resource
+ // name ends in .gz, even though in a PUT, the response is
+ // just plain HTML saying "created"
+ }
+ /* Remove the Pragma: no-cache header that libcurl inserts by default;
+ we want the cached version, if possible. */
+ if (mZInitialized)
+ {
+ curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, "");
+ // disable use of proxy, which can't handle chunked transfers
+ }
+ mHTTPHeaders = curl_slist_append(mHTTPHeaders, "Pragma:");
+ // resist the temptation to explicitly add the Transfer-Encoding: chunked
+ // header here - invokes a libCURL bug
+ curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mHTTPHeaders);
+ if (mAssetStoragep)
+ {
+ // Set the appropriate pending upload or download flag
+ if (mIsUpload)
+ {
+ mAssetStoragep->setPendingUpload();
+ }
+ else if (mIsLocalUpload)
+ {
+ mAssetStoragep->setPendingLocalUpload();
+ }
+ else if (mIsDownload)
+ {
+ mAssetStoragep->setPendingDownload();
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::setupCurlHandle - Request is not upload OR download, this is bad!" << llendl;
+ }
+ }
+ else
+ {
+ llerrs << "LLHTTPAssetRequest::setupCurlHandle - No asset storage associated with this request!" << llendl;
+ }
+}
+
+void LLHTTPAssetRequest::prepareCompressedUpload()
+{
+ mZStream.next_in = Z_NULL;
+ mZStream.avail_in = 0;
+ mZStream.zalloc = Z_NULL;
+ mZStream.zfree = Z_NULL;
+ mZStream.opaque = Z_NULL;
+
+ int r = deflateInit2(&mZStream,
+ 1, // compression level
+ Z_DEFLATED, // the only method defined
+ 15 + 16, // the default windowBits + gzip header flag
+ 8, // the default memLevel
+ Z_DEFAULT_STRATEGY);
+
+ if (r != Z_OK)
+ {
+ llerrs << "LLHTTPAssetRequest::prepareCompressedUpload defalateInit2() failed" << llendl;
+ }
+
+ mZInitialized = true;
+ mZInputBuffer = new char[COMPRESSED_INPUT_BUFFER_SIZE];
+ mZInputExhausted = false;
+
+ mVFile = new LLVFile(gAssetStorage->mVFS,
+ getUUID(), getType(), LLVFile::READ);
+}
+
+void LLHTTPAssetRequest::finishCompressedUpload()
+{
+ if (mZInitialized)
+ {
+ llinfos << "LLHTTPAssetRequest::finishCompressedUpload: "
+ << "read " << mZStream.total_in << " byte asset file, "
+ << "uploaded " << mZStream.total_out << " byte compressed asset"
+ << llendl;
+
+ deflateEnd(&mZStream);
+ delete[] mZInputBuffer;
+ }
+}
+
+size_t LLHTTPAssetRequest::readCompressedData(void* data, size_t size)
+{
+ mZStream.next_out = (Bytef*)data;
+ mZStream.avail_out = size;
+
+ while (mZStream.avail_out > 0)
+ {
+ if (mZStream.avail_in == 0 && !mZInputExhausted)
+ {
+ S32 to_read = llmin(COMPRESSED_INPUT_BUFFER_SIZE,
+ (S32)(mVFile->getSize() - mVFile->tell()));
+
+ mVFile->read((U8*)mZInputBuffer, to_read); /*Flawfinder: ignore*/
+
+ mZStream.next_in = (Bytef*)mZInputBuffer;
+ mZStream.avail_in = mVFile->getLastBytesRead();
+
+ mZInputExhausted = mZStream.avail_in == 0;
+ }
+
+ int r = deflate(&mZStream,
+ mZInputExhausted ? Z_FINISH : Z_NO_FLUSH);
+
+ if (r == Z_STREAM_END)
+ {
+ break;
+ }
+ }
+
+ return size - mZStream.avail_out;
+}
+
+//static
+size_t LLHTTPAssetRequest::curlCompressedUploadCallback(
+ void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ return 0;
+ }
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ return req->readCompressedData(data, size * nmemb);
+}
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLHTTPAssetStorage
+/////////////////////////////////////////////////////////////////////////////////
+
+
+LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name)
+ : LLAssetStorage(msg, xfer, vfs, upstream_host)
+{
+ _init(web_host, local_web_host, host_name);
+}
+
+LLHTTPAssetStorage::LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name)
+ : LLAssetStorage(msg, xfer, vfs)
+{
+ _init(web_host, local_web_host, host_name);
+}
+
+void LLHTTPAssetStorage::_init(const char *web_host, const char *local_web_host, const char* host_name)
+{
+ mBaseURL = web_host;
+ mLocalBaseURL = local_web_host;
+ mHostName = host_name;
+
+ // Do not change this "unless you are familiar with and mean to control
+ // internal operations of libcurl"
+ // - http://curl.haxx.se/libcurl/c/curl_global_init.html
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ mCurlMultiHandle = curl_multi_init();
+
+ mPendingDownload = FALSE;
+ mPendingUpload = FALSE;
+ mPendingLocalUpload = FALSE;
+}
+
+LLHTTPAssetStorage::~LLHTTPAssetStorage()
+{
+ curl_multi_cleanup(mCurlMultiHandle);
+ mCurlMultiHandle = NULL;
+
+ curl_global_cleanup();
+}
+
+// storing data is simpler than getting it, so we just overload the whole method
+void LLHTTPAssetStorage::storeAssetData(
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local,
+ const LLUUID& requesting_agent_id)
+{
+ if (mVFS->getExists(uuid, type))
+ {
+ LLAssetRequest *req = new LLAssetRequest(uuid, type);
+ req->mUpCallback = callback;
+ req->mUserData = user_data;
+ req->mRequestingAgentID = requesting_agent_id;
+
+ // this will get picked up and transmitted in checkForTimeouts
+ if(store_local)
+ {
+ mPendingLocalUploads.push_back(req);
+ }
+ else if(is_priority)
+ {
+ mPendingUploads.push_front(req);
+ }
+ else
+ {
+ mPendingUploads.push_back(req);
+ }
+ }
+ else
+ {
+ llwarns << "AssetStorage: attempt to upload non-existent vfile " << uuid << ":" << LLAssetType::lookup(type) << llendl;
+ if (callback)
+ {
+ callback(uuid, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE );
+ }
+ }
+}
+
+// virtual
+void LLHTTPAssetStorage::storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ llinfos << "LLAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ legacy->mUpCallback = callback;
+ legacy->mUserData = user_data;
+
+ FILE *fp = LLFile::fopen(filename, "rb"); /*Flawfinder: ignore*/
+ if (fp)
+ {
+ LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+
+ // if this upload fails, the caller needs to setup a new tempfile for us
+ if (temp_file)
+ {
+ LLFile::remove(filename);
+ }
+
+ storeAssetData(
+ asset_id,
+ asset_type,
+ legacyStoreDataCallback,
+ (void**)legacy,
+ temp_file,
+ is_priority);
+ }
+ else
+ {
+ if (callback)
+ {
+ callback(LLUUID::null, user_data, LL_ERR_CANNOT_OPEN_FILE);
+ }
+ }
+}
+
+// internal requester, used by getAssetData in superclass
+void LLHTTPAssetStorage::_queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate,
+ BOOL is_priority)
+{
+ // stash the callback info so we can find it after we get the response message
+ LLAssetRequest *req = new LLAssetRequest(uuid, type);
+ req->mDownCallback = callback;
+ req->mUserData = user_data;
+ req->mIsPriority = is_priority;
+
+ // this will get picked up and downloaded in checkForTimeouts
+
+ //
+ // HAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACK! Asset requests were taking too long and timing out.
+ // Since texture requests are the LEAST sensitive (on the simulator) to being delayed, add
+ // non-texture requests to the front, and add texture requests to the back. The theory is
+ // that we always want them first, even if they're out of order.
+ //
+
+ if (req->getType() == LLAssetType::AT_TEXTURE)
+ {
+ mPendingDownloads.push_back(req);
+ }
+ else
+ {
+ mPendingDownloads.push_front(req);
+ }
+}
+
+// overloaded to additionally move data to/from the webserver
+void LLHTTPAssetStorage::checkForTimeouts()
+{
+ LLAssetRequest *req = NULL;
+ if (mPendingDownloads.size() > 0 && !mPendingDownload)
+ {
+ req = mPendingDownloads.front();
+ // Setup this curl download request
+ // We need to generate a new request here
+ // since the one in the list could go away
+ char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+ std::string base_url = getBaseURL(req->getUUID(), req->getType());
+ snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", base_url.c_str() , uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mTmpUUID.generate();
+ new_req->mIsDownload = TRUE;
+
+ // Sets pending download flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &curlDownCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "Requesting " << new_req->mURLBuffer << llendl;
+
+ }
+
+
+ if (mPendingUploads.size() > 0 && !mPendingUpload)
+ {
+ req = mPendingUploads.front();
+ // setup this curl upload request
+
+ bool do_compress = req->getType() == LLAssetType::AT_OBJECT;
+
+ char tmp_url[MAX_STRING];/*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH];/*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+ snprintf(tmp_url, sizeof(tmp_url), /*Flawfinder: ignore*/
+ do_compress ? "%s/%s.%s.gz" : "%s/%s.%s",
+ mBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType()));
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mIsUpload = TRUE;
+ if (do_compress)
+ {
+ new_req->prepareCompressedUpload();
+ }
+
+ // Sets pending upload flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
+
+ if (do_compress)
+ {
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
+ &LLHTTPAssetRequest::curlCompressedUploadCallback);
+ }
+ else
+ {
+ LLVFile file(mVFS, req->getUUID(), req->getType());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION,
+ &curlUpCallback);
+ }
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "Requesting PUT " << new_req->mURLBuffer << llendl;
+ // Pending upload will have been flagged by the request
+ }
+
+
+ if (mPendingLocalUploads.size() > 0 && !mPendingLocalUpload)
+ {
+ req = mPendingLocalUploads.front();
+ // setup this curl upload request
+ LLVFile file(mVFS, req->getUUID(), req->getType());
+
+ char tmp_url[MAX_STRING]; /*Flawfinder: ignore*/
+ char uuid_str[UUID_STR_LENGTH]; /*Flawfinder: ignore*/
+ req->getUUID().toString(uuid_str);
+
+ // KLW - All temporary uploads are saved locally "http://localhost:12041/asset"
+ snprintf(tmp_url, sizeof(tmp_url), "%s/%36s.%s", mLocalBaseURL.c_str(), uuid_str, LLAssetType::lookup(req->getType())); /*Flawfinder: ignore*/
+
+ LLHTTPAssetRequest *new_req = new LLHTTPAssetRequest(this, req->getUUID(), req->getType(), tmp_url, mCurlMultiHandle);
+ new_req->mIsLocalUpload = TRUE;
+ new_req->mRequestingAgentID = req->mRequestingAgentID;
+
+ // Sets pending upload flag internally
+ new_req->setupCurlHandle();
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_PUT, 1);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_INFILESIZE, file.getSize());
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_WRITEFUNCTION, &nullOutputCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READFUNCTION, &curlUpCallback);
+ curl_easy_setopt(new_req->mCurlHandle, CURLOPT_READDATA, new_req->mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, new_req->mCurlHandle);
+ llinfos << "TAT: LLHTTPAssetStorage::checkForTimeouts() : pending local!"
+ << " Requesting PUT " << new_req->mURLBuffer << llendl;
+ // Pending upload will have been flagged by the request
+ }
+ S32 count = 0;
+ CURLMcode mcode;
+ int queue_length;
+ do
+ {
+ mcode = curl_multi_perform(mCurlMultiHandle, &queue_length);
+ count++;
+ } while (mcode == CURLM_CALL_MULTI_PERFORM && (count < 5));
+
+ CURLMsg *curl_msg;
+ do
+ {
+ curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
+ if (curl_msg && curl_msg->msg == CURLMSG_DONE)
+ {
+ long curl_result = 0;
+ S32 xfer_result = 0;
+
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_PRIVATE, &req);
+
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
+ if (req->mIsUpload || req->mIsLocalUpload)
+ {
+ if (curl_msg->data.result == CURLE_OK && (curl_result == HTTP_OK || curl_result == HTTP_PUT_OK || curl_result == HTTP_NO_CONTENT))
+ {
+ llinfos << "Success uploading " << req->getUUID() << " to " << req->mURLBuffer << llendl;
+ if (req->mIsLocalUpload)
+ {
+ addTempAssetData(req->getUUID(), req->mRequestingAgentID, mHostName);
+ }
+ }
+ else if (curl_msg->data.result == CURLE_COULDNT_CONNECT ||
+ curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
+ curl_result == HTTP_SERVER_BAD_GATEWAY ||
+ curl_result == HTTP_SERVER_TEMP_UNAVAILABLE)
+ {
+ llwarns << "Re-requesting upload for " << req->getUUID() << ". Received upload error to " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+ }
+ else
+ {
+ llwarns << "Failure uploading " << req->getUUID() << " to " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ }
+
+ if (!(curl_msg->data.result == CURLE_COULDNT_CONNECT ||
+ curl_msg->data.result == CURLE_OPERATION_TIMEOUTED ||
+ curl_result == HTTP_SERVER_BAD_GATEWAY ||
+ curl_result == HTTP_SERVER_TEMP_UNAVAILABLE))
+ {
+ // shared upload finished callback
+ // in the base class, this is called from processUploadComplete
+ _callUploadCallbacks(req->getUUID(), req->getType(), (xfer_result == 0));
+ // Pending upload flag will get cleared when the request is deleted
+ }
+ }
+ else if (req->mIsDownload)
+ {
+ if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
+ {
+ if (req->mVFile && req->mVFile->getSize() > 0)
+ {
+ llinfos << "Success downloading " << req->mURLBuffer << ", size " << req->mVFile->getSize() << llendl;
+
+ req->mVFile->rename(req->getUUID(), req->getType());
+ }
+ else
+ {
+ // TODO: if this actually indicates a bad asset on the server
+ // (not certain at this point), then delete it
+ llwarns << "Found " << req->mURLBuffer << " to be zero size" << llendl;
+ xfer_result = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ }
+ }
+ else
+ {
+ // KLW - TAT See if an avatar owns this texture, and if so request re-upload.
+ llwarns << "Failure downloading " << req->mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+
+ xfer_result = (curl_result == HTTP_MISSING) ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
+
+ if (req->mVFile)
+ {
+ req->mVFile->remove();
+ }
+ }
+
+ // call the static callback for transfer completion
+ // this will cleanup all requests for this asset, including ours
+ downloadCompleteCallback(xfer_result, (void *)req);
+ // Pending download flag will get cleared when the request is deleted
+ }
+ else
+ {
+ // nothing, just axe this request
+ // currently this can only mean an asset delete
+ }
+
+ // Deleting clears the pending upload/download flag if it's set and the request is transferring
+ delete req;
+ req = NULL;
+ }
+
+ } while (curl_msg && queue_length > 0);
+
+
+ LLAssetStorage::checkForTimeouts();
+}
+
+// static
+size_t LLHTTPAssetStorage::curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
+ return 0;
+ }
+ S32 bytes = (S32)(size * nmemb);
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mVFile)
+ {
+ req->mVFile = new LLVFile(gAssetStorage->mVFS, req->mTmpUUID, LLAssetType::AT_NONE, LLVFile::APPEND);
+ }
+
+ double content_length = 0.0;
+ curl_easy_getinfo(curl_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &content_length);
+
+ // sanitize content_length, reconcile w/ actual data
+ S32 file_length = llmax(0, (S32)llmin(content_length, 20000000.0), bytes + req->mVFile->getSize());
+
+ req->mVFile->setMaxSize(file_length);
+ req->mVFile->write((U8*)data, bytes);
+
+ return nmemb;
+}
+
+// static
+size_t LLHTTPAssetStorage::curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Missing gAssetStorage, aborting curl download callback!" << llendl;
+ return 0;
+ }
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mVFile)
+ {
+ req->mVFile = new LLVFile(gAssetStorage->mVFS, req->getUUID(), req->getType(), LLVFile::READ);
+ }
+
+ S32 bytes = llmin((S32)(size * nmemb), (S32)(req->mVFile->getSize() - req->mVFile->tell()));
+
+ req->mVFile->read((U8*)data, bytes);/*Flawfinder: ignore*/
+
+ return req->mVFile->getLastBytesRead();
+}
+
+// static
+size_t LLHTTPAssetStorage::nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ // do nothing, this is here to soak up script output so it doesn't end up on stdout
+
+ return nmemb;
+}
+
+
+
+// blocking asset fetch which bypasses the VFS
+// this is a very limited function for use by the simstate loader and other one-offs
+S32 LLHTTPAssetStorage::getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata)
+{
+ // FIXME: There is no guarantee that the uuid and the asset_type match
+ // - not that it matters. - Doug
+ lldebugs << "LLHTTPAssetStorage::getURLToFile() - " << url << llendl;
+
+ FILE *fp = LLFile::fopen(filename, "wb"); /*Flawfinder: ignore*/
+ if (! fp)
+ {
+ llwarns << "Failed to open " << filename << " for writing" << llendl;
+ return LL_ERR_ASSET_REQUEST_FAILED;
+ }
+
+ // make sure we use the normal curl setup, even though we don't really need a request object
+ LLHTTPAssetRequest req(this, uuid, asset_type, url.c_str(), mCurlMultiHandle);
+ req.mFP = fp;
+ req.mIsDownload = TRUE;
+
+ req.setupCurlHandle();
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEFUNCTION, &curlFileDownCallback);
+ curl_easy_setopt(req.mCurlHandle, CURLOPT_WRITEDATA, req.mCurlHandle);
+
+ curl_multi_add_handle(mCurlMultiHandle, req.mCurlHandle);
+ llinfos << "Requesting as file " << req.mURLBuffer << llendl;
+
+ // braindead curl loop
+ int queue_length;
+ CURLMsg *curl_msg;
+ LLTimer timeout;
+ timeout.setTimerExpirySec(GET_URL_TO_FILE_TIMEOUT);
+ bool success = false;
+ S32 xfer_result = 0;
+ do
+ {
+ curl_multi_perform(mCurlMultiHandle, &queue_length);
+ curl_msg = curl_multi_info_read(mCurlMultiHandle, &queue_length);
+
+ if (callback)
+ {
+ callback(userdata);
+ }
+
+ if ( curl_msg && (CURLMSG_DONE == curl_msg->msg) )
+ {
+ success = true;
+ }
+ else if (timeout.hasExpired())
+ {
+ llwarns << "Request for " << url << " has timed out." << llendl;
+ success = false;
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ break;
+ }
+ } while (!success);
+
+ if (success)
+ {
+ long curl_result = 0;
+ curl_easy_getinfo(curl_msg->easy_handle, CURLINFO_HTTP_CODE, &curl_result);
+
+ if (curl_result == HTTP_OK && curl_msg->data.result == CURLE_OK)
+ {
+ S32 size = ftell(req.mFP);
+ if (size > 0)
+ {
+ // everything seems to be in order
+ llinfos << "Success downloading " << req.mURLBuffer << " to file, size " << size << llendl;
+ }
+ else
+ {
+ llwarns << "Found " << req.mURLBuffer << " to be zero size" << llendl;
+ xfer_result = LL_ERR_ASSET_REQUEST_FAILED;
+ }
+ }
+ else
+ {
+ xfer_result = curl_result == HTTP_MISSING ? LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE : LL_ERR_ASSET_REQUEST_FAILED;
+ llinfos << "Failure downloading " << req.mURLBuffer <<
+ " with result " << curl_easy_strerror(curl_msg->data.result) << ", http result " << curl_result << llendl;
+ }
+ }
+
+ fclose(fp);
+ if (xfer_result)
+ {
+ LLFile::remove(filename);
+ }
+ return xfer_result;
+}
+
+
+// static
+size_t LLHTTPAssetStorage::curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data)
+{
+ CURL *curl_handle = (CURL *)user_data;
+ LLHTTPAssetRequest *req = NULL;
+ curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &req);
+
+ if (! req->mFP)
+ {
+ llwarns << "Missing mFP, aborting curl file download callback!" << llendl;
+ return 0;
+ }
+
+ return fwrite(data, size, nmemb, req->mFP);
+}
+
+// virtual
+void LLHTTPAssetStorage::addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name)
+{
+ if (agent_id.isNull() || asset_id.isNull())
+ {
+ llwarns << "TAT: addTempAssetData bad id's asset_id: " << asset_id << " agent_id: " << agent_id << llendl;
+ return;
+ }
+
+ LLTempAssetData temp_asset_data;
+ temp_asset_data.mAssetID = asset_id;
+ temp_asset_data.mAgentID = agent_id;
+ temp_asset_data.mHostName = host_name;
+
+ mTempAssets[asset_id] = temp_asset_data;
+}
+
+// virtual
+BOOL LLHTTPAssetStorage::hasTempAssetData(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ BOOL found = (citer != mTempAssets.end());
+ return found;
+}
+
+// virtual
+std::string LLHTTPAssetStorage::getTempAssetHostName(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ if (citer != mTempAssets.end())
+ {
+ return citer->second.mHostName;
+ }
+ else
+ {
+ return std::string();
+ }
+}
+
+// virtual
+LLUUID LLHTTPAssetStorage::getTempAssetAgentID(const LLUUID& texture_id) const
+{
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(texture_id);
+ if (citer != mTempAssets.end())
+ {
+ return citer->second.mAgentID;
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+// virtual
+void LLHTTPAssetStorage::removeTempAssetData(const LLUUID& asset_id)
+{
+ mTempAssets.erase(asset_id);
+}
+
+// virtual
+void LLHTTPAssetStorage::removeTempAssetDataByAgentID(const LLUUID& agent_id)
+{
+ uuid_tempdata_map::iterator it = mTempAssets.begin();
+ uuid_tempdata_map::iterator end = mTempAssets.end();
+
+ while (it != end)
+ {
+ const LLTempAssetData& asset_data = it->second;
+ if (asset_data.mAgentID == agent_id)
+ {
+ mTempAssets.erase(it++);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+std::string LLHTTPAssetStorage::getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type)
+{
+ if (LLAssetType::AT_TEXTURE == asset_type)
+ {
+ uuid_tempdata_map::const_iterator citer = mTempAssets.find(asset_id);
+ if (citer != mTempAssets.end())
+ {
+ const std::string& host_name = citer->second.mHostName;
+ std::string url = llformat(LOCAL_ASSET_URL_FORMAT, host_name.c_str());
+ return url;
+ }
+ }
+
+ return mBaseURL;
+}
+
+void LLHTTPAssetStorage::dumpTempAssetData(const LLUUID& avatar_id) const
+{
+ uuid_tempdata_map::const_iterator it = mTempAssets.begin();
+ uuid_tempdata_map::const_iterator end = mTempAssets.end();
+ S32 count = 0;
+ for ( ; it != end; ++it)
+ {
+ const LLTempAssetData& temp_asset_data = it->second;
+ if (avatar_id.isNull()
+ || avatar_id == temp_asset_data.mAgentID)
+ {
+ llinfos << "TAT: dump agent " << temp_asset_data.mAgentID
+ << " texture " << temp_asset_data.mAssetID
+ << " host " << temp_asset_data.mHostName
+ << llendl;
+ count++;
+ }
+ }
+
+ if (avatar_id.isNull())
+ {
+ llinfos << "TAT: dumped " << count << " entries for all avatars" << llendl;
+ }
+ else
+ {
+ llinfos << "TAT: dumped " << count << " entries for avatar " << avatar_id << llendl;
+ }
+}
+
+void LLHTTPAssetStorage::clearTempAssetData()
+{
+ llinfos << "TAT: Clearing temp asset data map" << llendl;
+ mTempAssets.clear();
+}
+
diff --git a/indra/llmessage/llhttpassetstorage.h b/indra/llmessage/llhttpassetstorage.h
new file mode 100644
index 0000000000..a6ba5c795c
--- /dev/null
+++ b/indra/llmessage/llhttpassetstorage.h
@@ -0,0 +1,116 @@
+/**
+ * @file llhttpassetstorage.h
+ * @brief Class for loading asset data to/from an external source over http.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLHTTPASSETSTORAGE_H
+#define LLHTTPASSETSTORAGE_H
+
+#include "llassetstorage.h"
+#include "curl/curl.h"
+
+class LLVFile;
+typedef void (*progress_callback)(void* userdata);
+
+struct LLTempAssetData;
+
+typedef std::map<LLUUID,LLTempAssetData> uuid_tempdata_map;
+
+class LLHTTPAssetStorage : public LLAssetStorage
+{
+public:
+ LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name);
+
+ LLHTTPAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs,
+ const char *web_host,
+ const char *local_web_host,
+ const char *host_name);
+
+
+ virtual ~LLHTTPAssetStorage();
+
+ virtual void storeAssetData(
+ const LLUUID& uuid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false,
+ const LLUUID& requesting_agent_id = LLUUID::null);
+
+ virtual void storeAssetData(
+ const char* filename,
+ const LLUUID& asset_id,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority);
+
+ // Hack. One off curl download an URL to a file. Probably should be elsewhere.
+ // Only used by lldynamicstate. The API is broken, and should be replaced with
+ // a generic HTTP file fetch - Doug 9/25/06
+ S32 getURLToFile(const LLUUID& uuid, LLAssetType::EType asset_type, const LLString &url, const char *filename, progress_callback callback, void *userdata);
+
+ void checkForTimeouts();
+
+ static size_t curlDownCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t curlFileDownCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t curlUpCallback(void *data, size_t size, size_t nmemb, void *user_data);
+ static size_t nullOutputCallback(void *data, size_t size, size_t nmemb, void *user_data);
+
+ // Should only be used by the LLHTTPAssetRequest
+ void setPendingUpload() { mPendingUpload = TRUE; }
+ void setPendingLocalUpload() { mPendingLocalUpload = TRUE; }
+ void setPendingDownload() { mPendingDownload = TRUE; }
+ void clearPendingUpload() { mPendingUpload = FALSE; }
+ void clearPendingLocalUpload() { mPendingLocalUpload = FALSE; }
+ void clearPendingDownload() { mPendingDownload = FALSE; }
+
+ // Temp assets are stored on sim nodes, they have agent ID and location data associated with them.
+ virtual void addTempAssetData(const LLUUID& asset_id, const LLUUID& agent_id, const std::string& host_name);
+ virtual BOOL hasTempAssetData(const LLUUID& texture_id) const;
+ virtual std::string getTempAssetHostName(const LLUUID& texture_id) const;
+ virtual LLUUID getTempAssetAgentID(const LLUUID& texture_id) const;
+ virtual void removeTempAssetData(const LLUUID& asset_id);
+ virtual void removeTempAssetDataByAgentID(const LLUUID& agent_id);
+
+ // Pass LLUUID::null for all
+ virtual void dumpTempAssetData(const LLUUID& avatar_id) const;
+ virtual void clearTempAssetData();
+
+protected:
+ void _queueDataRequest(const LLUUID& uuid, LLAssetType::EType type,
+ void (*callback)(LLVFS *vfs, const LLUUID&, LLAssetType::EType, void *, S32),
+ void *user_data, BOOL duplicate, BOOL is_priority);
+
+private:
+ void _init(const char *web_host, const char *local_web_host, const char* host_name);
+
+ // This will return the correct base URI for any http asset request
+ std::string getBaseURL(const LLUUID& asset_id, LLAssetType::EType asset_type);
+
+protected:
+ std::string mBaseURL;
+ std::string mLocalBaseURL;
+ std::string mHostName;
+
+ CURLM *mCurlMultiHandle;
+
+ BOOL mPendingDownload;
+ BOOL mPendingUpload;
+ BOOL mPendingLocalUpload;
+
+ uuid_tempdata_map mTempAssets;
+};
+
+#endif
diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp
new file mode 100644
index 0000000000..38dee26723
--- /dev/null
+++ b/indra/llmessage/llhttpclient.cpp
@@ -0,0 +1,278 @@
+/**
+ * @file llhttpclient.cpp
+ * @brief Implementation of classes for making HTTP requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llassetstorage.h"
+#include "llhttpclient.h"
+#include "lliopipe.h"
+#include "llurlrequest.h"
+#include "llbufferstream.h"
+#include "llsdserialize.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+static const F32 HTTP_REQUEST_EXPIRY_SECS = 60.0f;
+
+static std::string gCABundle;
+
+
+
+
+LLHTTPClient::Responder::Responder()
+ : mReferenceCount(0)
+{
+}
+
+LLHTTPClient::Responder::~Responder()
+{
+}
+
+// virtual
+void LLHTTPClient::Responder::error(U32 status, const std::string& reason)
+{
+ llinfos << "LLHTTPClient::Responder::error "
+ << status << ": " << reason << llendl;
+}
+
+// virtual
+void LLHTTPClient::Responder::result(const LLSD& content)
+{
+}
+
+// virtual
+void LLHTTPClient::Responder::completed(U32 status, const std::string& reason, const LLSD& content)
+{
+ if (200 <= status && status < 300)
+ {
+ result(content);
+ }
+ else
+ {
+ error(status, reason);
+ }
+}
+
+
+namespace
+{
+ class LLHTTPClientURLAdaptor : public LLURLRequestComplete
+ {
+ public:
+ LLHTTPClientURLAdaptor(LLHTTPClient::ResponderPtr responder)
+ : mResponder(responder),
+ mStatus(499), mReason("LLURLRequest complete w/no status")
+ {
+ }
+
+ ~LLHTTPClientURLAdaptor()
+ {
+ }
+
+ virtual void httpStatus(U32 status, const std::string& reason)
+ {
+ mStatus = status;
+ mReason = reason;
+ }
+
+ virtual void complete(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+ {
+ LLBufferStream istr(channels, buffer.get());
+ LLSD content;
+
+ if (200 <= mStatus && mStatus < 300)
+ {
+ LLSDSerialize::fromXML(content, istr);
+ }
+
+ if (mResponder.get())
+ {
+ mResponder->completed(mStatus, mReason, content);
+ }
+ }
+
+ private:
+ LLHTTPClient::ResponderPtr mResponder;
+ U32 mStatus;
+ std::string mReason;
+ };
+
+ class Injector : public LLIOPipe
+ {
+ public:
+ virtual const char* contentType() = 0;
+ };
+
+ class LLSDInjector : public Injector
+ {
+ public:
+ LLSDInjector(const LLSD& sd) : mSD(sd) {}
+ virtual ~LLSDInjector() {}
+
+ const char* contentType() { return "application/xml"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+ LLSDSerialize::toXML(mSD, ostream);
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const LLSD mSD;
+ };
+
+ class FileInjector : public Injector
+ {
+ public:
+ FileInjector(const std::string& filename) : mFilename(filename) {}
+ virtual ~FileInjector() {}
+
+ const char* contentType() { return "application/octet-stream"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+
+ llifstream fstream(mFilename.c_str(), std::iostream::binary | std::iostream::out);
+ fstream.seekg(0, std::ios::end);
+ U32 fileSize = fstream.tellg();
+ fstream.seekg(0, std::ios::beg);
+ char* fileBuffer;
+ fileBuffer = new char [fileSize];
+ fstream.read(fileBuffer, fileSize);
+ ostream.write(fileBuffer, fileSize);
+ fstream.close();
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const std::string mFilename;
+ };
+
+ class VFileInjector : public Injector
+ {
+ public:
+ VFileInjector(const LLUUID& uuid, LLAssetType::EType asset_type) : mUUID(uuid), mAssetType(asset_type) {}
+ virtual ~VFileInjector() {}
+
+ const char* contentType() { return "application/octet-stream"; }
+
+ virtual EStatus process_impl(const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump)
+ {
+ LLBufferStream ostream(channels, buffer.get());
+
+ LLVFile vfile(gVFS, mUUID, mAssetType, LLVFile::READ);
+ S32 fileSize = vfile.getSize();
+ U8* fileBuffer;
+ fileBuffer = new U8 [fileSize];
+ vfile.read(fileBuffer, fileSize);
+ ostream.write((char*)fileBuffer, fileSize);
+ eos = true;
+ return STATUS_DONE;
+ }
+
+ const LLUUID mUUID;
+ LLAssetType::EType mAssetType;
+ };
+
+
+ LLPumpIO* theClientPump = NULL;
+}
+
+static void request(const std::string& url, LLURLRequest::ERequestAction method,
+ Injector* body_injector, LLHTTPClient::ResponderPtr responder)
+{
+ if (!LLHTTPClient::hasPump())
+ {
+ responder->completed(U32_MAX, "No pump", LLSD());
+ return;
+ }
+
+ LLPumpIO::chain_t chain;
+
+ LLURLRequest *req = new LLURLRequest(method, url);
+ req->requestEncoding("");
+ if (!gCABundle.empty())
+ {
+ req->checkRootCertificate(true, gCABundle.c_str());
+ }
+ req->setCallback(new LLHTTPClientURLAdaptor(responder));
+
+ if (method == LLURLRequest::HTTP_PUT || method == LLURLRequest::HTTP_POST)
+ {
+ req->addHeader(llformat("Content-Type: %s", body_injector->contentType()).c_str());
+ chain.push_back(LLIOPipe::ptr_t(body_injector));
+ }
+ chain.push_back(LLIOPipe::ptr_t(req));
+
+ theClientPump->addChain(chain, HTTP_REQUEST_EXPIRY_SECS);
+}
+
+void LLHTTPClient::get(const std::string& url, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_GET, NULL, responder);
+}
+
+void LLHTTPClient::put(const std::string& url, const LLSD& body, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_PUT, new LLSDInjector(body), responder);
+}
+
+void LLHTTPClient::post(const std::string& url, const LLSD& body, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new LLSDInjector(body), responder);
+}
+
+#if 1
+void LLHTTPClient::postFile(const std::string& url, const std::string& filename, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new FileInjector(filename), responder);
+}
+
+void LLHTTPClient::postFile(const std::string& url, const LLUUID& uuid,
+ LLAssetType::EType asset_type, ResponderPtr responder)
+{
+ request(url, LLURLRequest::HTTP_POST, new VFileInjector(uuid, asset_type), responder);
+}
+#endif
+
+void LLHTTPClient::setPump(LLPumpIO& pump)
+{
+ theClientPump = &pump;
+}
+
+bool LLHTTPClient::hasPump()
+{
+ return theClientPump != NULL;
+}
+
+void LLHTTPClient::setCABundle(const std::string& caBundle)
+{
+ gCABundle = caBundle;
+}
+
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLHTTPClient::Responder* p)
+ {
+ ++p->mReferenceCount;
+ }
+
+ void intrusive_ptr_release(LLHTTPClient::Responder* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+
diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h
new file mode 100644
index 0000000000..563450f07e
--- /dev/null
+++ b/indra/llmessage/llhttpclient.h
@@ -0,0 +1,83 @@
+/**
+ * @file llhttpclient.h
+ * @brief Declaration of classes for making HTTP client requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHTTPCLIENT_H
+#define LL_LLHTTPCLIENT_H
+
+/**
+ * These classes represent the HTTP client framework.
+ */
+
+#include <string>
+
+#include <boost/intrusive_ptr.hpp>
+#include "llassetstorage.h"
+
+class LLPumpIO;
+class LLSD;
+
+
+class LLHTTPClient
+{
+public:
+ class Responder
+ {
+ public:
+ Responder();
+ virtual ~Responder();
+
+ virtual void error(U32 status, const std::string& reason); // called with bad status codes
+
+ virtual void result(const LLSD& content);
+
+ virtual void completed(U32 status, const std::string& reason, const LLSD& content);
+ /**< The default implemetnation calls
+ either:
+ * result(), or
+ * error()
+ */
+
+ public: /* but not really -- don't touch this */
+ U32 mReferenceCount;
+ };
+
+ typedef boost::intrusive_ptr<Responder> ResponderPtr;
+
+ static void get(const std::string& url, ResponderPtr);
+ static void put(const std::string& url, const LLSD& body, ResponderPtr);
+ ///< non-blocking
+ static void post(const std::string& url, const LLSD& body, ResponderPtr);
+ static void postFile(const std::string& url, const std::string& filename, ResponderPtr);
+ static void postFile(const std::string& url, const LLUUID& uuid,
+ LLAssetType::EType asset_type, ResponderPtr responder);
+
+ static void del(const std::string& url, ResponderPtr);
+ ///< sends a DELETE method, but we can't call it delete in c++
+
+
+ static void setPump(LLPumpIO& pump);
+ ///< must be called before any of the above calls are made
+ static bool hasPump();
+ ///< for testing
+
+ static void setCABundle(const std::string& caBundle);
+ ///< use this root CA bundle when checking SSL connections
+ ///< defaults to the standard system root CA bundle
+ ///< @see LLURLRequest::checkRootCertificate()
+};
+
+
+
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLHTTPClient::Responder* p);
+ void intrusive_ptr_release(LLHTTPClient::Responder* p);
+};
+
+
+#endif // LL_LLHTTPCLIENT_H
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
new file mode 100644
index 0000000000..e7d441b22c
--- /dev/null
+++ b/indra/llmessage/llhttpnode.cpp
@@ -0,0 +1,449 @@
+/**
+ * @file llhttpnode.cpp
+ * @brief Implementation of classes for generic HTTP/LSL/REST handling.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llhttpnode.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llstl.h"
+
+static const std::string CONTEXT_REQUEST("request");
+static const std::string CONTEXT_WILDCARD("wildcard");
+
+/**
+ * LLHTTPNode
+ */
+
+class LLHTTPNode::Impl
+{
+public:
+ typedef std::map<std::string, LLHTTPNode*> ChildMap;
+
+ ChildMap mNamedChildren;
+ LLHTTPNode* mWildcardChild;
+ std::string mWildcardName;
+ std::string mWildcardKey;
+ LLHTTPNode* mParentNode;
+
+ Impl() : mWildcardChild(NULL), mParentNode(NULL) { }
+
+ LLHTTPNode* findNamedChild(const std::string& name) const;
+};
+
+
+LLHTTPNode* LLHTTPNode::Impl::findNamedChild(const std::string& name) const
+{
+ LLHTTPNode* child = get_ptr_in_map(mNamedChildren, name);
+
+ if (!child && ((name[0] == '*') || (name == mWildcardName)))
+ {
+ child = mWildcardChild;
+ }
+
+ return child;
+}
+
+
+LLHTTPNode::LLHTTPNode()
+ : impl(* new Impl)
+{
+}
+
+// virtual
+LLHTTPNode::~LLHTTPNode()
+{
+ std::for_each(impl.mNamedChildren.begin(), impl.mNamedChildren.end(),
+ DeletePairedPointer());
+
+ delete impl.mWildcardChild;
+
+ delete &impl;
+}
+
+
+namespace {
+ class NotImplemented
+ {
+ };
+}
+
+// virtual
+LLSD LLHTTPNode::get() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::put(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::post(const LLSD& input) const
+{
+ throw NotImplemented();
+}
+
+
+// virtual
+void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(get());
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(put(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
+{
+ try
+ {
+ response->result(post(input));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+}
+
+// virtual
+void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) const
+{
+ try
+ {
+ response->result(del(context));
+ }
+ catch (NotImplemented)
+ {
+ response->methodNotAllowed();
+ }
+
+}
+
+// virtual
+LLSD LLHTTPNode::del() const
+{
+ throw NotImplemented();
+}
+
+// virtual
+LLSD LLHTTPNode::del(const LLSD&) const
+{
+ return del();
+}
+
+
+// virtual
+LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const
+{
+ LLHTTPNode* namedChild = get_ptr_in_map(impl.mNamedChildren, name);
+ if (namedChild)
+ {
+ return namedChild;
+ }
+
+ if (impl.mWildcardChild
+ && impl.mWildcardChild->validate(name, context))
+ {
+ context[CONTEXT_REQUEST][CONTEXT_WILDCARD][impl.mWildcardKey] = name;
+ return impl.mWildcardChild;
+ }
+
+ return NULL;
+}
+
+
+// virtual
+bool LLHTTPNode::handles(const LLSD& remainder, LLSD& context) const
+{
+ return remainder.size() == 0;
+}
+
+// virtual
+bool LLHTTPNode::validate(const std::string& name, LLSD& context) const
+{
+ return false;
+}
+
+const LLHTTPNode* LLHTTPNode::traverse(
+ const std::string& path, LLSD& context) const
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ const LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->getChild(*iter, context);
+ if(!child)
+ {
+ lldebugs << "LLHTTPNode::traverse: Couldn't find '" << *iter << "'" << llendl;
+ break;
+ }
+ lldebugs << "LLHTTPNode::traverse: Found '" << *iter << "'" << llendl;
+
+ node = child;
+ }
+
+ LLSD& remainder = context[CONTEXT_REQUEST]["remainder"];
+ for(; iter != end; ++iter)
+ {
+ remainder.append(*iter);
+ }
+
+ return node->handles(remainder, context) ? node : NULL;
+}
+
+
+
+void LLHTTPNode::addNode(const std::string& path, LLHTTPNode* nodeToAdd)
+{
+ typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(path, sep);
+ tokenizer::iterator iter = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ LLHTTPNode* node = this;
+ for(; iter != end; ++iter)
+ {
+ LLHTTPNode* child = node->impl.findNamedChild(*iter);
+ if (!child) { break; }
+ node = child;
+ }
+
+ if (iter == end)
+ {
+ llwarns << "LLHTTPNode::addNode: already a node that handles "
+ << path << llendl;
+ return;
+ }
+
+ while (true)
+ {
+ std::string pathPart = *iter;
+
+ ++iter;
+ bool lastOne = iter == end;
+
+ LLHTTPNode* nextNode = lastOne ? nodeToAdd : new LLHTTPNode();
+
+ switch (pathPart[0])
+ {
+ case '<':
+ // *NOTE: This should really validate that it is of
+ // the proper form: <wildcardkey> so that the substr()
+ // generates the correct key name.
+ node->impl.mWildcardChild = nextNode;
+ node->impl.mWildcardName = pathPart;
+ if(node->impl.mWildcardKey.empty())
+ {
+ node->impl.mWildcardKey = pathPart.substr(
+ 1,
+ pathPart.size() - 2);
+ }
+ break;
+ case '*':
+ node->impl.mWildcardChild = nextNode;
+ if(node->impl.mWildcardName.empty())
+ {
+ node->impl.mWildcardName = pathPart;
+ }
+ break;
+
+ default:
+ node->impl.mNamedChildren[pathPart] = nextNode;
+ }
+ nextNode->impl.mParentNode = node;
+
+ if (lastOne) break;
+ node = nextNode;
+ }
+}
+
+static void append_node_paths(LLSD& result,
+ const std::string& name, const LLHTTPNode* node)
+{
+ result.append(name);
+
+ LLSD paths = node->allNodePaths();
+ LLSD::array_const_iterator i = paths.beginArray();
+ LLSD::array_const_iterator end = paths.endArray();
+
+ for (; i != end; ++i)
+ {
+ result.append(name + "/" + (*i).asString());
+ }
+}
+
+LLSD LLHTTPNode::allNodePaths() const
+{
+ LLSD result;
+
+ Impl::ChildMap::const_iterator i = impl.mNamedChildren.begin();
+ Impl::ChildMap::const_iterator end = impl.mNamedChildren.end();
+ for (; i != end; ++i)
+ {
+ append_node_paths(result, i->first, i->second);
+ }
+
+ if (impl.mWildcardChild)
+ {
+ append_node_paths(result, impl.mWildcardName, impl.mWildcardChild);
+ }
+
+ return result;
+}
+
+
+const LLHTTPNode* LLHTTPNode::rootNode() const
+{
+ const LLHTTPNode* node = this;
+
+ while (true)
+ {
+ const LLHTTPNode* next = node->impl.mParentNode;
+ if (!next)
+ {
+ return node;
+ }
+ node = next;
+ }
+}
+
+
+const LLHTTPNode* LLHTTPNode::findNode(const std::string& name) const
+{
+ return impl.findNamedChild(name);
+}
+
+LLHTTPNode::Response::~Response()
+{
+}
+
+void LLHTTPNode::Response::status(S32 code)
+{
+ status(code, "Unknown Error");
+}
+
+void LLHTTPNode::Response::notFound(const std::string& message)
+{
+ status(404, message);
+}
+
+void LLHTTPNode::Response::notFound()
+{
+ status(404, "Not Found");
+}
+
+void LLHTTPNode::Response::methodNotAllowed()
+{
+ status(405, "Method Not Allowed");
+}
+
+void LLHTTPNode::describe(Description& desc) const
+{
+ desc.shortInfo("unknown service (missing describe() method)");
+}
+
+
+const LLChainIOFactory* LLHTTPNode::getProtocolHandler() const
+{
+ return NULL;
+}
+
+
+
+namespace
+{
+ typedef std::map<std::string, LLHTTPRegistrar::NodeFactory*> FactoryMap;
+
+ FactoryMap& factoryMap()
+ {
+ static FactoryMap theMap;
+ return theMap;
+ }
+}
+
+LLHTTPRegistrar::NodeFactory::~NodeFactory() { }
+
+void LLHTTPRegistrar::registerFactory(
+ const std::string& path, NodeFactory& factory)
+{
+ factoryMap()[path] = &factory;
+}
+
+void LLHTTPRegistrar::buildAllServices(LLHTTPNode& root)
+{
+ const FactoryMap& map = factoryMap();
+
+ FactoryMap::const_iterator i = map.begin();
+ FactoryMap::const_iterator end = map.end();
+ for (; i != end; ++i)
+ {
+ llinfos << "LLHTTPRegistrar::buildAllServices adding node for path "
+ << i->first << llendl;
+
+ root.addNode(i->first, i->second->build());
+ }
+}
+
+LLPointer<LLSimpleResponse> LLSimpleResponse::create()
+{
+ return new LLSimpleResponse();
+}
+
+LLSimpleResponse::~LLSimpleResponse()
+{
+}
+
+void LLSimpleResponse::result(const LLSD& result)
+{
+ status(200, "OK");
+}
+
+void LLSimpleResponse::status(S32 code, const std::string& message)
+{
+ mCode = code;
+ mMessage = message;
+}
+
+void LLSimpleResponse::print(std::ostream& out) const
+{
+ out << mCode << " " << mMessage;
+}
+
+
+std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp)
+{
+ resp.print(out);
+ return out;
+}
diff --git a/indra/llmessage/llhttpnode.h b/indra/llmessage/llhttpnode.h
new file mode 100644
index 0000000000..1e799c18b9
--- /dev/null
+++ b/indra/llmessage/llhttpnode.h
@@ -0,0 +1,306 @@
+/**
+ * @file llhttpnode.h
+ * @brief Declaration of classes for generic HTTP/LSL/REST handling.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHTTPNODE_H
+#define LL_LLHTTPNODE_H
+
+#include "llmemory.h"
+#include "llsd.h"
+
+class LLChainIOFactory;
+
+
+/**
+ * These classes represent the HTTP framework: The URL tree, and the LLSD
+ * REST interface that such nodes implement.
+ *
+ * To implement a service, in most cases, subclass LLHTTPNode, implement
+ * get() or post(), and create a global instance of LLHTTPRegistration<>.
+ * This can all be done in a .cpp file, with no publically declared parts.
+ *
+ * To implement a server see lliohttpserver.h
+ * @see LLHTTPWireServer
+ */
+
+/**
+ * @class LLHTTPNode
+ * @brief Base class which handles url traversal, response routing
+ * and support for standard LLSD services
+ *
+ * Users of the HTTP responder will typically derive a class from this
+ * one, implement the get(), put() and/or post() methods, and then
+ * use LLHTTPRegistration to insert it into the URL tree.
+ *
+ * The default implementation handles servicing the request and creating
+ * the pipe fittings needed to read the headers, manage them, convert
+ * to and from LLSD, etc.
+ */
+class LLHTTPNode
+{
+public:
+ LLHTTPNode();
+ virtual ~LLHTTPNode();
+
+ /** @name Responses
+ Most subclasses override one or more of these methods to provide
+ the service. By default, the rest of the LLHTTPNode architecture
+ will handle requests, create the needed LLIOPump, parse the input
+ to LLSD, and format the LLSD result to the output.
+
+ The default implementation of each of these is to call
+ response->methodNotAllowed(); The "simple" versions can be
+ overridden instead in those cases where the service can return
+ an immediately computed response.
+ */
+ //@{
+public:
+ virtual LLSD get() const;
+ virtual LLSD put(const LLSD& input) const;
+ virtual LLSD post(const LLSD& input) const;
+
+ virtual LLSD del() const;
+ virtual LLSD del(const LLSD& context) const;
+
+ class Response : public LLRefCount
+ {
+ public:
+ virtual ~Response();
+
+ virtual void result(const LLSD&) = 0;
+ virtual void status(S32 code, const std::string& message) = 0;
+
+ void status(S32 code);
+ void notFound(const std::string& message);
+ void notFound();
+ void methodNotAllowed();
+ };
+
+ typedef LLPointer<Response> ResponsePtr;
+
+ virtual void get(ResponsePtr, const LLSD& context) const;
+ virtual void put(ResponsePtr, const LLSD& context, const LLSD& input) const;
+ virtual void post(ResponsePtr, const LLSD& context, const LLSD& input) const;
+ virtual void del(ResponsePtr, const LLSD& context) const;
+ //@}
+
+
+ /** @name URL traversal
+ The tree is traversed by calling getChild() with successive
+ path components, on successive results. When getChild() returns
+ null, or there are no more components, the last child responds to
+ the request.
+
+ The default behavior is generally correct, though wildcard nodes
+ will want to implement validate().
+ */
+ //@{
+public:
+ virtual LLHTTPNode* getChild(const std::string& name, LLSD& context) const;
+ /**< returns a child node, if any, at the given name
+ default looks at children and wildcard child (see below)
+ */
+
+ virtual bool handles(const LLSD& remainder, LLSD& context) const;
+ /**< return true if this node can service the remaining components;
+ default returns true if there are no remaining components
+ */
+
+ virtual bool validate(const std::string& name, LLSD& context) const;
+ /**< called only on wildcard nodes, to check if they will handle
+ the name; default is false; overrides will want to check
+ name, and return true if the name will construct to a valid url.
+ For convenience, the <code>getChild()</code> method above will
+ automatically insert the name in
+ context["request"]["wildcard"][key] if this method returns true.
+ For example, the node "agent/<agent_id>/detail" will set
+ context["request"]["wildcard"]["agent_id"] eqaul to the value
+ found during traversal.
+ */
+
+ const LLHTTPNode* traverse(const std::string& path, LLSD& context) const;
+ /**< find a node, if any, that can service this path
+ set up context["request"] information
+ */
+ //@}
+
+ /** @name Child Nodes
+ The standard node can have any number of child nodes under
+ fixed names, and optionally one "wildcard" node that can
+ handle all other names.
+
+ Usually, child nodes are add through LLHTTPRegistration, not
+ by calling this interface directly.
+
+ The added node will be now owned by the parent node.
+ */
+ //@{
+
+ virtual void addNode(const std::string& path, LLHTTPNode* nodeToAdd);
+
+ LLSD allNodePaths() const;
+ ///< Returns an arrary of node paths at and under this node
+
+ const LLHTTPNode* rootNode() const;
+ const LLHTTPNode* findNode(const std::string& name) const;
+
+ //@}
+
+ /* @name Description system
+ The Description object contains information about a service.
+ All subclasses of LLHTTPNode should override description() and use
+ the methods of the Description class to set the various properties.
+ */
+ //@{
+ class Description
+ {
+ public:
+ void shortInfo(const std::string& s){ mInfo["description"] = s; }
+ void longInfo(const std::string& s) { mInfo["details"] = s; }
+
+ // Call this method when the service supports the specified verb.
+ void getAPI() { mInfo["api"].append("GET"); }
+ void putAPI() { mInfo["api"].append("PUT"); }
+ void postAPI() { mInfo["api"].append("POST"); }
+ void delAPI() { mInfo["api"].append("DELETE"); }
+
+ void input(const std::string& s) { mInfo["input"] = s; }
+ void output(const std::string& s) { mInfo["output"] = s; }
+ void source(const char* f, int l) { mInfo["__file__"] = f;
+ mInfo["__line__"] = l; }
+
+ LLSD getInfo() const { return mInfo; }
+
+ private:
+ LLSD mInfo;
+ };
+
+ virtual void describe(Description&) const;
+
+ //@}
+
+
+ virtual const LLChainIOFactory* getProtocolHandler() const;
+ /**< Return a factory object for handling wire protocols.
+ * The base class returns NULL, as it doesn't know about
+ * wire protocols at all. This is okay for most nodes
+ * as LLIOHTTPServer is smart enough to use a default
+ * wire protocol for HTTP for such nodes. Specialized
+ * subclasses that handle things like XML-RPC will want
+ * to implement this. (See LLXMLSDRPCServerFactory.)
+ */
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+class LLSimpleResponse : public LLHTTPNode::Response
+{
+public:
+ static LLPointer<LLSimpleResponse> create();
+ ~LLSimpleResponse();
+
+ void result(const LLSD& result);
+ void status(S32 code, const std::string& message);
+
+ void print(std::ostream& out) const;
+
+ S32 mCode;
+ std::string mMessage;
+
+private:
+ LLSimpleResponse() {;} // Must be accessed through LLPointer.
+};
+
+std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp);
+
+
+
+/**
+ * @name Automatic LLHTTPNode registration
+ *
+ * To register a node type at a particular url path, construct a global instance
+ * of LLHTTPRegistration:
+ *
+ * LLHTTPRegistration<LLMyNodeType> gHTTPServiceAlphaBeta("/alpha/beta");
+ *
+ * (Note the naming convention carefully.) This object must be global and not
+ * static. However, it needn't be declared in your .h file. It can exist
+ * solely in the .cpp file. The same is true of your subclass of LLHTTPNode:
+ * it can be declared and defined wholly within the .cpp file.
+ *
+ * When constructing a web server, use LLHTTPRegistrar to add all the registered
+ * nodes to the url tree:
+ *
+ * LLHTTPRegistrar::buidlAllServices(mRootNode);
+ */
+//@{
+
+class LLHTTPRegistrar
+{
+public:
+ class NodeFactory
+ {
+ public:
+ virtual ~NodeFactory();
+ virtual LLHTTPNode* build() const = 0;
+ };
+
+ static void buildAllServices(LLHTTPNode& root);
+
+ static void registerFactory(const std::string& path, NodeFactory& factory);
+ ///< construct an LLHTTPRegistration below to call this
+};
+
+template < class NodeType >
+class LLHTTPRegistration
+{
+public:
+ LLHTTPRegistration(const std::string& path)
+ {
+ LLHTTPRegistrar::registerFactory(path, mFactory);
+ }
+
+private:
+ class ThisNodeFactory : public LLHTTPRegistrar::NodeFactory
+ {
+ public:
+ virtual LLHTTPNode* build() const { return new NodeType; }
+ };
+
+ ThisNodeFactory mFactory;
+};
+
+template < class NodeType>
+class LLHTTPParamRegistration
+{
+public:
+ LLHTTPParamRegistration(const std::string& path, LLSD params) :
+ mFactory(params)
+ {
+ LLHTTPRegistrar::registerFactory(path, mFactory);
+ }
+
+private:
+ class ThisNodeFactory : public LLHTTPRegistrar::NodeFactory
+ {
+ public:
+ ThisNodeFactory(LLSD params) : mParams(params) {}
+ virtual LLHTTPNode* build() const { return new NodeType(mParams); }
+ private:
+ LLSD mParams;
+ };
+
+ ThisNodeFactory mFactory;
+};
+
+//@}
+
+#endif // LL_LLHTTPNODE_H
diff --git a/indra/llmessage/llinstantmessage.cpp b/indra/llmessage/llinstantmessage.cpp
new file mode 100644
index 0000000000..2bfa82a0ce
--- /dev/null
+++ b/indra/llmessage/llinstantmessage.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llinstantmessage.cpp
+ * @author Phoenix
+ * @date 2005-08-29
+ * @brief Constants and functions used in IM.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lldbstrings.h"
+#include "llinstantmessage.h"
+#include "llhost.h"
+#include "lluuid.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llmemory.h"
+#include "message.h"
+
+#include "message.h"
+
+const U8 IM_ONLINE = 0;
+const U8 IM_OFFLINE = 1;
+
+const S32 VOTE_YES = 1;
+const S32 VOTE_NO = 0;
+const S32 VOTE_ABSTAIN = -1;
+
+const S32 VOTE_MAJORITY = 0;
+const S32 VOTE_SUPER_MAJORITY = 1;
+const S32 VOTE_UNANIMOUS = 2;
+
+const char EMPTY_BINARY_BUCKET[] = "";
+const S32 EMPTY_BINARY_BUCKET_SIZE = 1;
+const U32 NO_TIMESTAMP = 0;
+const char SYSTEM_FROM[] = "Second Life";
+const S32 IM_TTL = 1;
+
+
+/**
+ * LLIMInfo
+ */
+LLIMInfo::LLIMInfo() :
+ mParentEstateID(0),
+ mOffline(0),
+ mViewerThinksToIsOnline(false),
+ mTimeStamp(0),
+ mSource(IM_FROM_SIM),
+ mTTL(IM_TTL)
+{
+}
+
+LLIMInfo::LLIMInfo(
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& to_id,
+ EInstantMessage im_type,
+ const std::string& name,
+ const std::string& message,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ LLSD data,
+ U8 offline,
+ U32 timestamp,
+ EIMSource source,
+ S32 ttl) :
+ mFromID(from_id),
+ mFromGroup(from_group),
+ mToID(to_id),
+ mParentEstateID(0),
+ mRegionID(region_id),
+ mPosition(position),
+ mOffline(offline),
+ mViewerThinksToIsOnline(false),
+ mIMType(im_type),
+ mID(id),
+ mTimeStamp(timestamp),
+ mName(name),
+ mMessage(message),
+ mData(data),
+ mSource(source),
+ mTTL(ttl)
+{
+}
+
+LLIMInfo::LLIMInfo(LLMessageSystem* msg, EIMSource source, S32 ttl) :
+ mViewerThinksToIsOnline(false),
+ mSource(source),
+ mTTL(ttl)
+{
+ unpackMessageBlock(msg);
+}
+
+LLIMInfo::~LLIMInfo()
+{
+}
+
+void LLIMInfo::packInstantMessage(LLMessageSystem* msg) const
+{
+ lldebugs << "LLIMInfo::packInstantMessage()" << llendl;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ packMessageBlock(msg);
+}
+
+void LLIMInfo::packMessageBlock(LLMessageSystem* msg) const
+{
+ // Construct binary bucket
+ std::vector<U8> bucket;
+ if (mData.has("binary_bucket"))
+ {
+ bucket = mData["binary_bucket"].asBinary();
+ }
+ pack_instant_message_block(
+ msg,
+ mFromID,
+ mFromGroup,
+ LLUUID::null,
+ mToID,
+ mName.c_str(),
+ mMessage.c_str(),
+ mOffline,
+ mIMType,
+ mID,
+ mParentEstateID,
+ mRegionID,
+ mPosition,
+ mTimeStamp,
+ &bucket[0],
+ bucket.size());
+}
+
+void pack_instant_message(
+ LLMessageSystem* msg,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ lldebugs << "pack_instant_message()" << llendl;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ pack_instant_message_block(
+ msg,
+ from_id,
+ from_group,
+ session_id,
+ to_id,
+ name,
+ message,
+ offline,
+ dialog,
+ id,
+ parent_estate_id,
+ region_id,
+ position,
+ timestamp,
+ binary_bucket,
+ binary_bucket_size);
+}
+
+void pack_instant_message_block(
+ LLMessageSystem* msg,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, from_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, from_group);
+ msg->addUUIDFast(_PREHASH_ToAgentID, to_id);
+ msg->addU32Fast(_PREHASH_ParentEstateID, parent_estate_id);
+ msg->addUUIDFast(_PREHASH_RegionID, region_id);
+ msg->addVector3Fast(_PREHASH_Position, position);
+ msg->addU8Fast(_PREHASH_Offline, offline);
+ msg->addU8Fast(_PREHASH_Dialog, (U8) dialog);
+ msg->addUUIDFast(_PREHASH_ID, id);
+ msg->addU32Fast(_PREHASH_Timestamp, timestamp);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ S32 bytes_left = MTUBYTES;
+ if(message)
+ {
+ char buffer[MTUBYTES];
+ bytes_left -= snprintf(buffer, MTUBYTES, "%s", message);
+ bytes_left = llmax(0, bytes_left);
+ msg->addStringFast(_PREHASH_Message, buffer);
+ }
+ else
+ {
+ msg->addStringFast(_PREHASH_Message, NULL);
+ }
+ const U8* bb;
+ if(binary_bucket)
+ {
+ bb = binary_bucket;
+ binary_bucket_size = llmin(bytes_left, binary_bucket_size);
+ }
+ else
+ {
+ bb = (const U8*)EMPTY_BINARY_BUCKET;
+ binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
+ }
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, bb, binary_bucket_size);
+}
+
+void LLIMInfo::unpackMessageBlock(LLMessageSystem* msg)
+{
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, mFromID);
+ msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, mFromGroup);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, mToID);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, mParentEstateID);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, mRegionID);
+ msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, mPosition);
+ msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Offline, mOffline);
+ U8 dialog;
+ msg->getU8Fast(_PREHASH_MessageBlock, _PREHASH_Dialog, dialog);
+ mIMType = (EInstantMessage) dialog;
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, mID);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_Timestamp, mTimeStamp);
+ char name[DB_FULL_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, DB_FULL_NAME_BUF_SIZE, name);
+ mName.assign(name);
+
+ char message[DB_IM_MSG_BUF_SIZE];
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, DB_IM_MSG_BUF_SIZE, message);
+ mMessage.assign(message);
+
+ S32 binary_bucket_size = llmin(
+ MTUBYTES,
+ msg->getSizeFast(
+ _PREHASH_MessageBlock,
+ _PREHASH_BinaryBucket));
+ if(binary_bucket_size)
+ {
+ std::vector<U8> bucket;
+ bucket.resize(binary_bucket_size);
+
+ msg->getBinaryDataFast(
+ _PREHASH_MessageBlock,
+ _PREHASH_BinaryBucket,
+ &bucket[0],
+ 0,
+ 0,
+ binary_bucket_size);
+ mData["binary_bucket"] = bucket;
+ }
+ else
+ {
+ mData.clear();
+ }
+}
+
+LLPointer<LLIMInfo> LLIMInfo::clone()
+{
+ return new LLIMInfo(
+ mFromID,
+ mFromGroup,
+ mToID,
+ mIMType,
+ mName,
+ mMessage,
+ mID,
+ mParentEstateID,
+ mRegionID,
+ mPosition,
+ mData,
+ mOffline,
+ mTimeStamp,
+ mSource,
+ mTTL);
+}
+
diff --git a/indra/llmessage/llinstantmessage.h b/indra/llmessage/llinstantmessage.h
new file mode 100644
index 0000000000..c8138cf491
--- /dev/null
+++ b/indra/llmessage/llinstantmessage.h
@@ -0,0 +1,308 @@
+/**
+ * @file llinstantmessage.h
+ * @brief Constants and declarations used by instant messages.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINSTANTMESSAGE_H
+#define LL_LLINSTANTMESSAGE_H
+
+#include "llhost.h"
+#include "lluuid.h"
+#include "llsd.h"
+#include "llmemory.h"
+#include "v3math.h"
+
+class LLMessageSystem;
+
+// The ImprovedInstantMessage only supports 8 bits in the "Dialog"
+// field, so don't go past the byte boundary
+enum EInstantMessage
+{
+ // default. ID is meaningless, nothing in the binary bucket.
+ IM_NOTHING_SPECIAL = 0,
+
+ // pops a messagebox with a single OK button
+ IM_MESSAGEBOX = 1,
+
+ // pops a countdown messagebox with a single OK button
+ // IM_MESSAGEBOX_COUNTDOWN = 2,
+
+ // You've been invited to join a group.
+ // ID is the group id.
+
+ // The binary bucket contains a null terminated string
+ // representation of the officer/member status and join cost for
+ // the invitee. (bug # 7672) The format is 1 byte for
+ // officer/member (O for officer, M for member), and as many bytes
+ // as necessary for cost.
+ IM_GROUP_INVITATION = 3,
+
+ // Inventory offer.
+ // ID is the transaction id
+ // Binary bucket is a list of inventory uuid and type.
+ IM_INVENTORY_OFFERED = 4,
+ IM_INVENTORY_ACCEPTED = 5,
+ IM_INVENTORY_DECLINED = 6,
+
+ // Group vote
+ // Name is name of person who called vote.
+ // ID is vote ID used for internal tracking
+ IM_GROUP_VOTE = 7,
+
+ // Group message
+ // This means that the message is meant for everyone in the
+ // agent's group. This will result in a database query to find all
+ // participants and start an im session.
+ IM_GROUP_MESSAGE_DEPRECATED = 8,
+
+ // Task inventory offer.
+ // ID is the transaction id
+ // Binary bucket is a (mostly) complete packed inventory item
+ IM_TASK_INVENTORY_OFFERED = 9,
+ IM_TASK_INVENTORY_ACCEPTED = 10,
+ IM_TASK_INVENTORY_DECLINED = 11,
+
+ // Copied as pending, type LL_NOTHING_SPECIAL, for new users
+ // used by offline tools
+ IM_NEW_USER_DEFAULT = 12,
+
+ //
+ // session based messaging - the way that people usually actually
+ // communicate with each other.
+ //
+
+ // Start a session, or add users to a session.
+ IM_SESSION_ADD = 13,
+
+ // Start a session, but don't prune offline users
+ IM_SESSION_OFFLINE_ADD = 14,
+
+ // start a session with your gruop
+ IM_SESSION_GROUP_START = 15,
+
+ // start a session without a calling card (finder or objects)
+ IM_SESSION_CARDLESS_START = 16,
+
+ // send a message to a session.
+ IM_SESSION_SEND = 17,
+
+ // leave a session
+ IM_SESSION_DROP = 18,
+
+ // an instant message from an object - for differentiation on the
+ // viewer, since you can't IM an object yet.
+ IM_FROM_TASK = 19,
+
+ // sent an IM to a busy user, this is the auto response
+ IM_BUSY_AUTO_RESPONSE = 20,
+
+ // Shows the message in the console and chat history
+ IM_CONSOLE_AND_CHAT_HISTORY = 21,
+
+ // IM Types used for luring your friends
+ IM_LURE_USER = 22,
+ IM_LURE_ACCEPTED = 23,
+ IM_LURE_DECLINED = 24,
+ IM_GODLIKE_LURE_USER = 25,
+ IM_YET_TO_BE_USED = 26,
+
+ // IM that notifie of a new group election.
+ // Name is name of person who called vote.
+ // ID is election ID used for internal tracking
+ IM_GROUP_ELECTION_DEPRECATED = 27,
+
+ // IM to tell the user to go to an URL. Put a text message in the
+ // message field, and put the url with a trailing \0 in the binary
+ // bucket.
+ IM_GOTO_URL = 28,
+
+ // IM for help from the GAURDIAN_ANGELS
+ // Binary bucket contains the name of the session.
+ IM_SESSION_911_START = 29,
+
+ // IM sent automatically on call for help,
+ // sends a lure to each Helper reached
+ IM_LURE_911 = 30,
+
+ // a message generated by a script which we don't want to
+ // be sent through e-mail. Similar to IM_FROM_TASK, but
+ // it is shown as an alert on the viewer.
+ IM_FROM_TASK_AS_ALERT = 31,
+
+ // IM from group officer to all group members.
+ IM_GROUP_NOTICE = 32,
+ IM_GROUP_NOTICE_INVENTORY_ACCEPTED = 33,
+ IM_GROUP_NOTICE_INVENTORY_DECLINED = 34,
+
+ IM_GROUP_INVITATION_ACCEPT = 35,
+ IM_GROUP_INVITATION_DECLINE = 36,
+
+ IM_GROUP_NOTICE_REQUESTED = 37,
+
+ IM_FRIENDSHIP_OFFERED = 38,
+ IM_FRIENDSHIP_ACCEPTED = 39,
+ IM_FRIENDSHIP_DECLINED = 40,
+
+ IM_TYPING_START = 41,
+ IM_TYPING_STOP = 42,
+
+ IM_COUNT
+};
+
+
+// Hooks for quickly hacking in experimental admin debug messages
+// without needing to recompile the viewer
+// *NOTE: This functionality has been moved to be a string based
+// operation so that we don't even have to do a full recompile. This
+// enumeration will be phased out soon.
+enum EGodlikeRequest
+{
+ GOD_WANTS_NOTHING,
+
+ // for requesting physics information about an object
+ GOD_WANTS_HAVOK_INFO,
+
+ // two unused requests that can be appropriated for debug
+ // purposes (no viewer recompile necessary)
+ GOD_WANTS_FOO,
+ GOD_WANTS_BAR,
+
+ // to dump simulator terrain data to terrain.raw file
+ GOD_WANTS_TERRAIN_SAVE,
+ // to load simulator terrain data from terrain.raw file
+ GOD_WANTS_TERRAIN_LOAD,
+
+ GOD_WANTS_TOGGLE_AVATAR_GEOMETRY, // HACK for testing new avatar geom
+
+ // real-time telehub operations
+ GOD_WANTS_TELEHUB_INFO,
+ GOD_WANTS_CONNECT_TELEHUB,
+ GOD_WANTS_DELETE_TELEHUB,
+ GOD_WANTS_ADD_TELEHUB_SPAWNPOINT,
+ GOD_WANTS_REMOVE_TELEHUB_SPAWNPOINT,
+
+};
+
+enum EIMSource
+{
+ IM_FROM_VIEWER,
+ IM_FROM_DATASERVER,
+ IM_FROM_SIM
+};
+
+extern const U8 IM_ONLINE;
+extern const U8 IM_OFFLINE;
+
+extern const S32 VOTE_YES;
+extern const S32 VOTE_NO;
+extern const S32 VOTE_ABSTAIN;
+
+extern const S32 VOTE_MAJORITY;
+extern const S32 VOTE_SUPER_MAJORITY;
+extern const S32 VOTE_UNANIMOUS;
+
+extern const char EMPTY_BINARY_BUCKET[];
+extern const S32 EMPTY_BINARY_BUCKET_SIZE;
+
+extern const U32 NO_TIMESTAMP;
+extern const char SYSTEM_FROM[];
+
+// Number of retry attempts on sending the im.
+extern const S32 IM_TTL;
+
+
+class LLIMInfo : public LLRefCount
+{
+protected:
+ LLIMInfo();
+ ~LLIMInfo();
+
+public:
+ LLIMInfo(LLMessageSystem* msg,
+ EIMSource source = IM_FROM_SIM,
+ S32 ttl = IM_TTL);
+
+ LLIMInfo(
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& to_id,
+ EInstantMessage im_type,
+ const std::string& name,
+ const std::string& message,
+ const LLUUID& id,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position,
+ LLSD data,
+ U8 offline,
+ U32 timestamp,
+ EIMSource source,
+ S32 ttl = IM_TTL);
+
+ void packInstantMessage(LLMessageSystem* msg) const;
+ void packMessageBlock(LLMessageSystem* msg) const;
+ void unpackMessageBlock(LLMessageSystem* msg);
+ LLPointer<LLIMInfo> clone();
+public:
+ LLUUID mFromID;
+ BOOL mFromGroup;
+ LLUUID mToID;
+ U32 mParentEstateID;
+ LLUUID mRegionID;
+ LLVector3 mPosition;
+ U8 mOffline;
+ bool mViewerThinksToIsOnline;
+ EInstantMessage mIMType;
+ LLUUID mID;
+ U32 mTimeStamp;
+ std::string mName;
+ std::string mMessage;
+ LLSD mData;
+
+ EIMSource mSource;
+ S32 mTTL;
+};
+
+
+void pack_instant_message(
+ LLMessageSystem* msgsystem,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+void pack_instant_message_block(
+ LLMessageSystem* msgsystem,
+ const LLUUID& from_id,
+ BOOL from_group,
+ const LLUUID& session_id,
+ const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+
+#endif // LL_LLINSTANTMESSAGE_H
+
diff --git a/indra/llmessage/llinvite.h b/indra/llmessage/llinvite.h
new file mode 100644
index 0000000000..5c66abc1eb
--- /dev/null
+++ b/indra/llmessage/llinvite.h
@@ -0,0 +1,15 @@
+/**
+ * @file llinvite.h
+ * @brief Constants used for inviting users to join groups.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVITE_H
+#define LL_LLINVITE_H
+
+const S32 INVITE_LIST_STR_LEN = 324; // Would be larger, but we don't have much room in the CreateGroupRequest msg.
+const S32 INVITE_LIST_BUF_SIZE = 325;
+
+#endif // LL_LLINVITE_H
diff --git a/indra/llmessage/lliobuffer.cpp b/indra/llmessage/lliobuffer.cpp
new file mode 100644
index 0000000000..f5d1ebd595
--- /dev/null
+++ b/indra/llmessage/lliobuffer.cpp
@@ -0,0 +1,96 @@
+/**
+ * @file lliobuffer.cpp
+ * @author Phoenix
+ * @date 2005-05-04
+ * @brief Definition of buffer based implementations of IO Pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliobuffer.h"
+
+//
+// LLIOBuffer
+//
+LLIOBuffer::LLIOBuffer() :
+ mBuffer(NULL),
+ mBufferSize(0L),
+ mReadHead(NULL),
+ mWriteHead(NULL)
+{
+}
+
+LLIOBuffer::~LLIOBuffer()
+{
+ if(mBuffer)
+ {
+ delete[] mBuffer;
+ }
+}
+
+U8* LLIOBuffer::data() const
+{
+ return mBuffer;
+}
+
+S64 LLIOBuffer::size() const
+{
+ return mBufferSize;
+}
+
+U8* LLIOBuffer::current() const
+{
+ return mReadHead;
+}
+
+S64 LLIOBuffer::bytesLeft() const
+{
+ return mWriteHead - mReadHead;
+}
+
+void LLIOBuffer::clear()
+{
+ mReadHead = mBuffer;
+ mWriteHead = mBuffer;
+}
+
+LLIOPipe::EStatus LLIOBuffer::seek(LLIOBuffer::EHead head, S64 delta)
+{
+ LLIOPipe::EStatus status = STATUS_ERROR;
+ switch(head)
+ {
+ case READ:
+ if(((delta >= 0) && ((mReadHead + delta) <= mWriteHead))
+ || (delta < 0) && ((mReadHead + delta) >= mBuffer))
+ {
+ mReadHead += delta;
+ status = STATUS_OK;
+ }
+ break;
+ case WRITE:
+ if(((delta >= 0) && ((mWriteHead + delta) < (mBuffer + mBufferSize)))
+ || (delta < 0) && ((mWriteHead + delta) > mReadHead))
+ {
+ mWriteHead += delta;
+ status = STATUS_OK;
+ }
+ default:
+ break;
+ }
+ return status;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOBuffer::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ // no-op (I think)
+ llwarns << "You are using an LLIOBuffer which is deprecated." << llendl;
+ return STATUS_OK;
+}
diff --git a/indra/llmessage/lliobuffer.h b/indra/llmessage/lliobuffer.h
new file mode 100644
index 0000000000..770738f2dd
--- /dev/null
+++ b/indra/llmessage/lliobuffer.h
@@ -0,0 +1,117 @@
+/**
+ * @file lliobuffer.h
+ * @author Phoenix
+ * @date 2005-05-04
+ * @brief Declaration of buffers for use in IO Pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOBUFFER_H
+#define LL_LLIOBUFFER_H
+
+#include "lliopipe.h"
+
+/**
+ * @class LLIOBuffer
+ * @brief This class is an io class that represents an automtically
+ * resizing io buffer.
+ * @see LLIOPipe
+ *
+ * This class is currently impelemented quick and dirty, but should be
+ * correct. This class should be extended to have a more flexible
+ * (and capped) memory allocation and usage scheme. Eventually, I
+ * would like to have the ability to share this buffer between
+ * different objects.
+ */
+class LLIOBuffer : public LLIOPipe
+{
+public:
+ LLIOBuffer();
+ virtual ~LLIOBuffer();
+
+ /**
+ * @brief Return a raw pointer to the current data set.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to size() bytes into memory.
+ * @return A potentially NULL pointer to the raw buffer data
+ */
+ U8* data() const;
+
+ /**
+ * @brief Return the size of the buffer
+ */
+ S64 size() const;
+
+ /**
+ * @brief Return a raw pointer to the current read position in the data.
+ *
+ * The pointer returned can be used for reading or even adjustment
+ * if you are a bit crazy up to bytesLeft() bytes into memory.
+ * @return A potentially NULL pointer to the buffer data starting
+ * at the read point
+ */
+ U8* current() const;
+
+ /**
+ * @brief Return the number of unprocessed bytes in buffer.
+ */
+ S64 bytesLeft() const;
+
+ /**
+ * @brief Move the buffer offsets back to the beginning.
+ *
+ * This method effectively clears what has been stored here,
+ * without mucking around with memory allocation.
+ */
+ void clear();
+
+ /**
+ * @brief Enumeration passed into the seek function
+ *
+ * The READ head is used for where to start processing data for
+ * the next link in the chain, while the WRITE head specifies
+ * where new data processed from the previous link in the chain
+ * will be written.
+ */
+ enum EHead
+ {
+ READ,
+ WRITE
+ };
+
+ /**
+ * @brief Seek to a place in the buffer
+ *
+ * @param head The READ or WRITE head.
+ * @param delta The offset from the current position to seek.
+ * @return The status of the operation. status >= if head moved.
+ */
+ EStatus seek(EHead head, S64 delta);
+
+public:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ U8* mBuffer;
+ S64 mBufferSize;
+ U8* mReadHead;
+ U8* mWriteHead;
+};
+
+#endif // LL_LLIOBUFFER_H
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp
new file mode 100644
index 0000000000..c1a9bc442e
--- /dev/null
+++ b/indra/llmessage/lliohttpserver.cpp
@@ -0,0 +1,833 @@
+/**
+ * @file lliohttpserver.cpp
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Implementation of the http server classes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliohttpserver.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llapr.h"
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llhttpnode.h"
+#include "lliopipe.h"
+#include "lliosocket.h"
+#include "llioutil.h"
+#include "llmemtype.h"
+#include "llmemorystream.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdserialize_xml.h"
+#include "llstl.h"
+
+static const char HTTP_VERSION_STR[] = "HTTP/1.0";
+static const std::string CONTEXT_REQUEST("request");
+static const std::string HTTP_VERB_GET("GET");
+static const std::string HTTP_VERB_PUT("PUT");
+static const std::string HTTP_VERB_POST("POST");
+static const std::string HTTP_VERB_DELETE("DELETE");
+
+
+class LLHTTPPipe : public LLIOPipe
+{
+public:
+ LLHTTPPipe(const LLHTTPNode& node)
+ : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0)
+ { }
+ virtual ~LLHTTPPipe()
+ {
+ if (mResponse.notNull())
+ {
+ mResponse->nullPipe();
+ }
+ }
+
+private:
+ // LLIOPipe API implementation.
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ LLIOPipe::buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+ const LLHTTPNode& mNode;
+
+ class Response : public LLHTTPNode::Response
+ {
+ public:
+
+ static LLPointer<Response> create(LLHTTPPipe* pipe);
+ virtual ~Response();
+
+ // from LLHTTPNode::Response
+ virtual void result(const LLSD&);
+ virtual void status(S32 code, const std::string& message);
+
+ void nullPipe();
+
+ private:
+ Response() {;} // Must be accessed through LLPointer.
+ LLHTTPPipe* mPipe;
+ };
+ friend class Response;
+
+ LLPointer<Response> mResponse;
+
+ enum State
+ {
+ STATE_INVOKE,
+ STATE_DELAYED,
+ STATE_LOCKED,
+ STATE_GOOD_RESULT,
+ STATE_STATUS_RESULT
+ };
+ State mState;
+
+ S32 mChainLock;
+ LLPumpIO* mLockedPump;
+
+ void lockChain(LLPumpIO*);
+ void unlockChain();
+
+ LLSD mGoodResult;
+ S32 mStatusCode;
+ std::string mStatusMessage;
+};
+
+LLIOPipe::EStatus LLHTTPPipe::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ lldebugs << "LLSDHTTPServer::process_impl" << llendl;
+
+ // Once we have all the data, We need to read the sd on
+ // the the in channel, and respond on the out channel
+
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ PUMP_DEBUG;
+ if (mState == STATE_INVOKE)
+ {
+ PUMP_DEBUG;
+ mState = STATE_DELAYED;
+ // assume deferred unless mResponse does otherwise
+ mResponse = Response::create(this);
+
+ // TODO: Babbage: Parameterize parser?
+ LLBufferStream istr(channels, buffer.get());
+
+ std::string verb = context[CONTEXT_REQUEST]["verb"];
+ if(verb == HTTP_VERB_GET)
+ {
+ mNode.get(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else if(verb == HTTP_VERB_PUT)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_POST)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_DELETE)
+ {
+ mNode.del(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else
+ {
+ mResponse->methodNotAllowed();
+ }
+
+ // Log Internal Server Errors
+ if(mStatusCode == 500)
+ {
+ llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error"
+ << llendl;
+ }
+ }
+
+ PUMP_DEBUG;
+ switch (mState)
+ {
+ case STATE_DELAYED:
+ lockChain(pump);
+ mState = STATE_LOCKED;
+ return STATUS_BREAK;
+
+ case STATE_LOCKED:
+ // should never ever happen!
+ return STATUS_ERROR;
+
+ case STATE_GOOD_RESULT:
+ {
+ context["response"]["contentType"] = "application/xml";
+ LLBufferStream ostr(channels, buffer.get());
+ LLSDSerialize::toXML(mGoodResult, ostr);
+
+ return STATUS_DONE;
+ }
+
+ case STATE_STATUS_RESULT:
+ {
+ context["response"]["contentType"] = "text/plain";
+ context["response"]["statusCode"] = mStatusCode;
+ context["response"]["statusMessage"] = mStatusMessage;
+ LLBufferStream ostr(channels, buffer.get());
+ ostr << mStatusMessage << std::ends;
+
+ return STATUS_DONE;
+ }
+ default:
+ llwarns << "LLHTTPPipe::process_impl: unexpected state "
+ << mState << llendl;
+
+ return STATUS_BREAK;
+ }
+// PUMP_DEBUG; // unreachable
+}
+
+LLPointer<LLHTTPPipe::Response> LLHTTPPipe::Response::create(LLHTTPPipe* pipe)
+{
+ LLPointer<Response> result = new Response();
+ result->mPipe = pipe;
+ return result;
+}
+
+// virtual
+LLHTTPPipe::Response::~Response()
+{
+}
+
+void LLHTTPPipe::Response::nullPipe()
+{
+ mPipe = NULL;
+}
+
+// virtual
+void LLHTTPPipe::Response::result(const LLSD& r)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::result: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = 200;
+ mPipe->mStatusMessage = "OK";
+ mPipe->mGoodResult = r;
+ mPipe->mState = STATE_GOOD_RESULT;
+ mPipe->unlockChain();
+}
+
+// virtual
+void LLHTTPPipe::Response::status(S32 code, const std::string& message)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = code;
+ mPipe->mStatusMessage = message;
+ mPipe->mState = STATE_STATUS_RESULT;
+ mPipe->unlockChain();
+}
+
+void LLHTTPPipe::lockChain(LLPumpIO* pump)
+{
+ if (mChainLock != 0) { return; }
+
+ mLockedPump = pump;
+ mChainLock = pump->setLock();
+}
+
+void LLHTTPPipe::unlockChain()
+{
+ if (mChainLock == 0) { return; }
+
+ mLockedPump->clearLock(mChainLock);
+ mLockedPump = NULL;
+ mChainLock = 0;
+}
+
+
+
+/**
+ * @class LLHTTPResponseHeader
+ * @brief Class which correctly builds HTTP headers on a pipe
+ * @see LLIOPipe
+ *
+ * An instance of this class can be placed in a chain where it will
+ * wait for an end of stream. Once it gets that, it will count the
+ * bytes on CHANNEL_OUT (or the size of the buffer in io pipe versions
+ * prior to 2) prepend that data to the request in an HTTP format, and
+ * supply all normal HTTP response headers.
+ */
+class LLHTTPResponseHeader : public LLIOPipe
+{
+public:
+ LLHTTPResponseHeader() {}
+ virtual ~LLHTTPResponseHeader() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ S32 mCode;
+};
+
+
+/**
+ * LLHTTPResponseHeader
+ */
+// virtual
+LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ if(eos)
+ {
+ PUMP_DEBUG;
+ //mGotEOS = true;
+ std::ostringstream ostr;
+ std::string message = context["response"]["statusMessage"];
+
+ int code = context["response"]["statusCode"];
+ if (code < 200)
+ {
+ code = 200;
+ message = "OK";
+ }
+
+ ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n";
+
+ std::string type = context["response"]["contentType"].asString();
+ if (!type.empty())
+ {
+ ostr << "Content-Type: " << type << "\r\n";
+ }
+ S32 content_length = buffer->countAfter(channels.in(), NULL);
+ if(0 < content_length)
+ {
+ ostr << "Content-Length: " << content_length << "\r\n";
+ }
+ ostr << "\r\n";
+
+ LLChangeChannel change(channels.in(), channels.out());
+ std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
+ std::string header = ostr.str();
+ buffer->prepend(channels.out(), (U8*)header.c_str(), header.size());
+ PUMP_DEBUG;
+ return STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return STATUS_OK;
+}
+
+
+
+/**
+ * @class LLHTTPResponder
+ * @brief This class
+ * @see LLIOPipe
+ *
+ * <b>NOTE:</b> You should not need to create or use one of these, the
+ * details are handled by the HTTPResponseFactory.
+ */
+class LLHTTPResponder : public LLIOPipe
+{
+public:
+ LLHTTPResponder(const LLHTTPNode& tree);
+ ~LLHTTPResponder();
+
+protected:
+ /**
+ * @brief Read data off of CHANNEL_IN keeping track of last read position.
+ *
+ * This is a quick little hack to read headers. It is not IO
+ * optimal, but it makes it easier for me to implement the header
+ * parsing. Plus, there should never be more than a few headers.
+ * This method will tend to read more than necessary, find the
+ * newline, make the front part of dest look like a c string, and
+ * move the read head back to where the newline was found. Thus,
+ * the next read will pick up on the next line.
+ * @param channel The channel to read in the buffer
+ * @param buffer The heap array of processed data
+ * @param dest Destination for the data to be read
+ * @param[in,out] len <b>in</b> The size of the buffer. <b>out</b> how
+ * much was read. This value is not useful for determining where to
+ * seek orfor string assignment.
+ * @returns Returns true if a line was found.
+ */
+ bool readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len);
+
+ /**
+ * @brief Mark the request as bad, and handle appropriately
+ *
+ * @param channels The channels to use in the buffer.
+ * @param buffer The heap array of processed data.
+ */
+ void markBad(const LLChannelDescriptors& channels, buffer_ptr_t buffer);
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ enum EState
+ {
+ STATE_NOTHING,
+ STATE_READING_HEADERS,
+ STATE_LOOKING_FOR_EOS,
+ STATE_DONE,
+ STATE_SHORT_CIRCUIT
+ };
+
+ EState mState;
+ U8* mLastRead;
+ std::string mVerb;
+ std::string mAbsPathAndQuery;
+ std::string mPath;
+ std::string mQuery;
+ std::string mVersion;
+ S32 mContentLength;
+
+ // handle the urls
+ const LLHTTPNode& mRootNode;
+};
+
+LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree) :
+ mState(STATE_NOTHING),
+ mLastRead(NULL),
+ mContentLength(0),
+ mRootNode(tree)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+}
+
+// virtual
+LLHTTPResponder::~LLHTTPResponder()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ //lldebugs << "destroying LLHTTPResponder" << llendl;
+}
+
+bool LLHTTPResponder::readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ --len;
+ U8* last = buffer->readAfter(channels.in(), mLastRead, dest, len);
+ dest[len] = '\0';
+ U8* newline = (U8*)strchr((char*)dest, '\n');
+ if(!newline)
+ {
+ if(len)
+ {
+ lldebugs << "readLine failed - too long maybe?" << llendl;
+ markBad(channels, buffer);
+ }
+ return false;
+ }
+ S32 offset = -((len - 1) - (newline - dest));
+ ++newline;
+ *newline = '\0';
+ mLastRead = buffer->seek(channels.in(), last, offset);
+ return true;
+}
+
+void LLHTTPResponder::markBad(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ mState = STATE_SHORT_CIRCUIT;
+ LLBufferStream out(channels, buffer.get());
+ out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n"
+ << "<title>Bad Request</title>\n<body>\nBad Request.\n"
+ << "</body>\n</html>\n";
+}
+
+// virtual
+LLIOPipe::EStatus LLHTTPResponder::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ LLIOPipe::EStatus status = STATUS_OK;
+
+ // parsing headers
+ if((STATE_NOTHING == mState) || (STATE_READING_HEADERS == mState))
+ {
+ PUMP_DEBUG;
+ status = STATUS_BREAK;
+ mState = STATE_READING_HEADERS;
+ const S32 HEADER_BUFFER_SIZE = 1024;
+ char buf[HEADER_BUFFER_SIZE + 1]; /*Flawfinder: ignore*/
+ S32 len = HEADER_BUFFER_SIZE;
+
+#if 0
+ if(true)
+ {
+ LLBufferArray::segment_iterator_t seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+ }
+#endif
+
+ PUMP_DEBUG;
+ if(readLine(channels, buffer, (U8*)buf, len))
+ {
+ bool read_next_line = false;
+ bool parse_all = true;
+ if(mVerb.empty())
+ {
+ read_next_line = true;
+ LLMemoryStream header((U8*)buf, len);
+ header >> mVerb;
+
+ if((HTTP_VERB_GET == mVerb)
+ || (HTTP_VERB_POST == mVerb)
+ || (HTTP_VERB_PUT == mVerb)
+ || (HTTP_VERB_DELETE == mVerb))
+ {
+ header >> mAbsPathAndQuery;
+ header >> mVersion;
+
+ lldebugs << "http request: "
+ << mVerb
+ << " " << mAbsPathAndQuery
+ << " " << mVersion << llendl;
+
+ std::string::size_type delimiter
+ = mAbsPathAndQuery.find('?');
+ if (delimiter == std::string::npos)
+ {
+ mPath = mAbsPathAndQuery;
+ mQuery = "";
+ }
+ else
+ {
+ mPath = mAbsPathAndQuery.substr(0, delimiter);
+ mQuery = mAbsPathAndQuery.substr(delimiter+1);
+ }
+
+ if(!mAbsPathAndQuery.empty())
+ {
+ if(mVersion.empty())
+ {
+ // simple request.
+ parse_all = false;
+ mState = STATE_DONE;
+ mVersion.assign("HTTP/1.0");
+ }
+ }
+ }
+ else
+ {
+ read_next_line = false;
+ parse_all = false;
+ lldebugs << "unknown http verb: " << mVerb << llendl;
+ markBad(channels, buffer);
+ }
+ }
+ if(parse_all)
+ {
+ bool keep_parsing = true;
+ while(keep_parsing)
+ {
+ if(read_next_line)
+ {
+ len = HEADER_BUFFER_SIZE;
+ readLine(channels, buffer, (U8*)buf, len);
+ }
+ if(0 == len)
+ {
+ return status;
+ }
+ if(buf[0] == '\r' && buf[1] == '\n')
+ {
+ // end-o-headers
+ keep_parsing = false;
+ mState = STATE_LOOKING_FOR_EOS;
+ break;
+ }
+ char* pos_colon = strchr(buf, ':');
+ if(NULL == pos_colon)
+ {
+ keep_parsing = false;
+ lldebugs << "bad header: " << buf << llendl;
+ markBad(channels, buffer);
+ break;
+ }
+ // we've found a header
+ read_next_line = true;
+ std::string name(buf, pos_colon - buf);
+ std::string value(pos_colon + 2);
+ LLString::toLower(name);
+ if("content-length" == name)
+ {
+ lldebugs << "Content-Length: " << value << llendl;
+ mContentLength = atoi(value.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ PUMP_DEBUG;
+ // look for the end of stream based on
+ if(STATE_LOOKING_FOR_EOS == mState)
+ {
+ if(0 == mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ else if(buffer->countAfter(channels.in(), mLastRead) >= mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ // else more bytes should be coming.
+ }
+
+ PUMP_DEBUG;
+ if(STATE_DONE == mState)
+ {
+ // hey, hey, we should have everything now, so we pass it to
+ // a content handler.
+ context[CONTEXT_REQUEST]["verb"] = mVerb;
+ const LLHTTPNode* node = mRootNode.traverse(mPath, context);
+ if(node)
+ {
+ llinfos << "LLHTTPResponder::process_impl found node for "
+ << mAbsPathAndQuery << llendl;
+
+ // Copy everything after mLast read to the out.
+ LLBufferArray::segment_iterator_t seg_iter;
+ seg_iter = buffer->splitAfter(mLastRead);
+ if(seg_iter != buffer->endSegment())
+ {
+ LLChangeChannel change(channels.in(), channels.out());
+ ++seg_iter;
+ std::for_each(seg_iter, buffer->endSegment(), change);
+
+#if 0
+ seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+#endif
+ }
+
+ //
+ // *FIX: get rid of extra bytes off the end
+ //
+
+ // Set up a chain which will prepend a content length and
+ // HTTP headers.
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
+ context[CONTEXT_REQUEST]["path"] = mPath;
+ context[CONTEXT_REQUEST]["query-string"] = mQuery;
+
+ const LLChainIOFactory* protocolHandler
+ = node->getProtocolHandler();
+ if (protocolHandler)
+ {
+ protocolHandler->build(chain, context);
+ }
+ else
+ {
+ // this is a simple LLHTTPNode, so use LLHTTPPipe
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPPipe(*node)));
+ }
+
+ // Add the header - which needs to have the same
+ // channel information as the link before it since it
+ // is part of the response.
+ LLIOPipe* header = new LLHTTPResponseHeader;
+ chain.push_back(LLIOPipe::ptr_t(header));
+
+ // We need to copy all of the pipes _after_ this so
+ // that the response goes out correctly.
+ LLPumpIO::links_t current_links;
+ pump->copyCurrentLinkInfo(current_links);
+ LLPumpIO::links_t::iterator link_iter = current_links.begin();
+ LLPumpIO::links_t::iterator links_end = current_links.end();
+ bool after_this = false;
+ for(; link_iter < links_end; ++link_iter)
+ {
+ if(after_this)
+ {
+ chain.push_back((*link_iter).mPipe);
+ }
+ else if(this == (*link_iter).mPipe.get())
+ {
+ after_this = true;
+ }
+ }
+
+ // Do the final build of the chain, and send it on
+ // it's way.
+ LLChannelDescriptors chnl = channels;
+ LLPumpIO::LLLinkInfo link;
+ LLPumpIO::links_t links;
+ LLPumpIO::chain_t::iterator it = chain.begin();
+ LLPumpIO::chain_t::iterator end = chain.end();
+ while(it != end)
+ {
+ link.mPipe = *it;
+ link.mChannels = chnl;
+ links.push_back(link);
+ chnl = LLBufferArray::makeChannelConsumer(chnl);
+ ++it;
+ }
+ pump->addChain(
+ links,
+ buffer,
+ context,
+ DEFAULT_CHAIN_EXPIRY_SECS);
+
+ status = STATUS_STOP;
+ }
+ else
+ {
+ llinfos << "LLHTTPResponder::process_impl didn't find a node for "
+ << mAbsPathAndQuery << llendl;
+ LLBufferStream str(channels, buffer.get());
+ mState = STATE_SHORT_CIRCUIT;
+ str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n"
+ << "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery
+ << "' not found.\n</body>\n</html>\n";
+ }
+ }
+
+ if(STATE_SHORT_CIRCUIT == mState)
+ {
+ //status = mNext->process(buffer, true, pump, context);
+ status = STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return status;
+}
+
+
+
+void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root)
+{
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root)));
+}
+
+
+class LLHTTPResponseFactory : public LLChainIOFactory
+{
+public:
+ bool build(LLPumpIO::chain_t& chain, LLSD ctx) const
+ {
+ LLCreateHTTPPipe(chain, mTree);
+ return true;
+ }
+
+ LLHTTPNode& getRootNode() { return mTree; }
+
+private:
+ LLHTTPNode mTree;
+};
+
+
+LLHTTPNode& LLCreateHTTPServer(
+ apr_pool_t* pool, LLPumpIO& pump, U16 port)
+{
+ LLSocket::ptr_t socket = LLSocket::create(
+ pool,
+ LLSocket::STREAM_TCP,
+ port);
+ if(!socket)
+ {
+ llerrs << "Unable to initialize socket" << llendl;
+ }
+
+ LLHTTPResponseFactory* factory = new LLHTTPResponseFactory;
+ boost::shared_ptr<LLChainIOFactory> factory_ptr(factory);
+
+ LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr);
+
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(server));
+ pump.addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ return factory->getRootNode();
+}
+
diff --git a/indra/llmessage/lliohttpserver.h b/indra/llmessage/lliohttpserver.h
new file mode 100644
index 0000000000..05dfdc4bf7
--- /dev/null
+++ b/indra/llmessage/lliohttpserver.h
@@ -0,0 +1,95 @@
+/**
+ * @file lliohttpserver.h
+ * @brief Declaration of function for creating an HTTP wire server
+ * @see LLIOServerSocket, LLPumpIO
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOHTTPSERVER_H
+#define LL_LLIOHTTPSERVER_H
+
+#include "llapr.h"
+#include "llchainio.h"
+#include "llhttpnode.h"
+
+class LLPumpIO;
+
+LLHTTPNode& LLCreateHTTPServer(apr_pool_t* pool, LLPumpIO& pump, U16 port);
+ /**< Creates an HTTP wire server on the pump for the given TCP port.
+ *
+ * Returns the root node of the new server. Add LLHTTPNode instances
+ * to this root.
+ *
+ * Nodes that return NULL for getProtocolHandler(), will use the
+ * default handler that interprets HTTP on the wire and converts
+ * it into calls to get(), put(), post(), del() with appropriate
+ * LLSD arguments and results.
+ *
+ * To have nodes that implement some other wire protocol (XML-RPC
+ * for example), use the helper templates below.
+ */
+
+void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root);
+ /**< Create a pipe on the chain that handles HTTP requests.
+ * The requests are served by the node tree given at root.
+ *
+ * This is primarily useful for unit testing.
+ */
+
+/* @name Helper Templates
+ *
+ * These templates make it easy to create nodes that use thier own protocol
+ * handlers rather than the default. Typically, you subclass LLIOPipe to
+ * implement the protocol, and then add a node using the templates:
+ *
+ * rootNode->addNode("thing", new LLHTTPNodeForPipe<LLThingPipe>);
+ *
+ * The templates are:
+ *
+ * LLChainIOFactoryForPipe
+ * - a simple factory that builds instances of a pipe
+ *
+ * LLHTTPNodeForFacotry
+ * - a HTTP node that uses a factory as the protocol handler
+ *
+ * LLHTTPNodeForPipe
+ * - a HTTP node that uses a simple factory based on a pipe
+ */
+//@{
+
+template<class Pipe>
+class LLChainIOFactoryForPipe : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ chain.push_back(LLIOPipe::ptr_t(new Pipe));
+ return true;
+ }
+};
+
+template<class Factory>
+class LLHTTPNodeForFactory : public LLHTTPNode
+{
+public:
+ const LLChainIOFactory* getProtocolHandler() const
+ { return &mProtocolHandler; }
+
+private:
+ Factory mProtocolHandler;
+};
+
+//@}
+
+
+template<class Pipe>
+class LLHTTPNodeForPipe : public LLHTTPNodeForFactory<
+ LLChainIOFactoryForPipe<Pipe> >
+{
+};
+
+
+#endif // LL_LLIOHTTPSERVER_H
+
diff --git a/indra/llmessage/lliopipe.cpp b/indra/llmessage/lliopipe.cpp
new file mode 100644
index 0000000000..eac1a8b68a
--- /dev/null
+++ b/indra/llmessage/lliopipe.cpp
@@ -0,0 +1,93 @@
+/**
+ * @file lliopipe.cpp
+ * @author Phoenix
+ * @date 2004-11-19
+ * @brief Implementation of the LLIOPipe class
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliopipe.h"
+
+#include "llpumpio.h"
+
+static const std::string STATUS_SUCCESS_NAMES[LLIOPipe::STATUS_SUCCESS_COUNT] =
+{
+ std::string("STATUS_OK"),
+ std::string("STATUS_STOP"),
+ std::string("STATUS_DONE"),
+ std::string("STATUS_BREAK"),
+ std::string("STATUS_NEED_PROCESS"),
+};
+
+static const std::string STATUS_ERROR_NAMES[LLIOPipe::STATUS_ERROR_COUNT] =
+{
+ std::string("STATUS_ERROR"),
+ std::string("STATUS_NOT_IMPLEMENTED"),
+ std::string("STATUS_PRECONDITION_NOT_MET"),
+ std::string("STATUS_NO_CONNECTION"),
+ std::string("STATUS_EXPIRED"),
+};
+
+// Debugging schmutz for deadlock
+const char *gPumpFile = "";
+S32 gPumpLine = 0;
+
+void pump_debug(const char *file, S32 line)
+{
+ gPumpFile = file;
+ gPumpLine = line;
+}
+
+/**
+ * LLIOPipe
+ */
+LLIOPipe::LLIOPipe() :
+ mReferenceCount(0)
+{
+}
+
+LLIOPipe::~LLIOPipe()
+{
+ //lldebugs << "destroying LLIOPipe" << llendl;
+}
+
+// static
+std::string LLIOPipe::lookupStatusString(EStatus status)
+{
+ if((status >= 0) && (status < STATUS_SUCCESS_COUNT))
+ {
+ return STATUS_SUCCESS_NAMES[status];
+ }
+ else
+ {
+ S32 error_code = ((S32)status * -1) - 1;
+ if(error_code < STATUS_ERROR_COUNT)
+ {
+ return STATUS_ERROR_NAMES[error_code];
+ }
+ }
+ std::string rv;
+ return rv;
+}
+
+LLIOPipe::EStatus LLIOPipe::process(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return process_impl(channels, buffer, eos, context, pump);
+}
+
+// virtual
+LLIOPipe::EStatus LLIOPipe::handleError(
+ LLIOPipe::EStatus status,
+ LLPumpIO* pump)
+{
+ // by default, the error is not handled.
+ return status;
+}
diff --git a/indra/llmessage/lliopipe.h b/indra/llmessage/lliopipe.h
new file mode 100644
index 0000000000..5cbe3d8743
--- /dev/null
+++ b/indra/llmessage/lliopipe.h
@@ -0,0 +1,291 @@
+/**
+ * @file lliopipe.h
+ * @author Phoenix
+ * @date 2004-11-18
+ * @brief Declaration of base IO class
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOPIPE_H
+#define LL_LLIOPIPE_H
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+#include "apr-1/apr_poll.h"
+
+#include "llsd.h"
+
+class LLIOPipe;
+class LLPumpIO;
+class LLBufferArray;
+class LLChannelDescriptors;
+
+// Debugging schmutz for deadlocks
+#define LL_DEBUG_PUMPS
+#ifdef LL_DEBUG_PUMPS
+void pump_debug(const char *file, S32 line);
+#define PUMP_DEBUG pump_debug(__FILE__, __LINE__);
+#define END_PUMP_DEBUG pump_debug("none", 0);
+#endif
+
+
+/**
+ * intrusive pointer support
+ */
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLIOPipe* p);
+ void intrusive_ptr_release(LLIOPipe* p);
+};
+
+/**
+ * @class LLIOPipe
+ * @brief This class is an abstract base class for data processing units
+ * @see LLPumpIO
+ *
+ * The LLIOPipe is a base class for implementing the basic non-blocking
+ * processing of data subsystem in our system.
+ *
+ * Implementations of this class should behave like a stateful or
+ * stateless signal processor. Each call to <code>process()</code>
+ * hands the pipe implementation a buffer and a set of channels in the
+ * buffer to process, and the pipe returns the status of the
+ * operation. This is an abstract base class and developer created
+ * concrete implementations provide block or stream based processing
+ * of data to implement a particular protocol.
+ */
+class LLIOPipe
+{
+public:
+ /**
+ * @brief I have decided that IO objects should have a reference
+ * count. In general, you can pass bald LLIOPipe pointers around
+ * as you need, but if you need to maintain a reference to one,
+ * you need to hold a ptr_t.
+ */
+ typedef boost::intrusive_ptr<LLIOPipe> ptr_t;
+
+ /**
+ * @brief Scattered memory container.
+ */
+ typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
+
+ /**
+ * @brief Enumeration for IO return codes
+ *
+ * A status code a positive integer value is considered a success,
+ * but may indicate special handling for future calls, for
+ * example, issuing a STATUS_STOP to an LLIOSocketReader instance
+ * will tell the instance to stop reading the socket. A status
+ * code with a negative value means that a problem has been
+ * encountered which will require further action on the caller or
+ * a developer to correct. Some mechanisms, such as the LLPumpIO
+ * may depend on this definition of success and failure.
+ */
+ enum EStatus
+ {
+ // Processing occurred normally, future calls will be accepted.
+ STATUS_OK = 0,
+
+ // Processing occured normally, but stop unsolicited calls to
+ // process.
+ STATUS_STOP = 1,
+
+ // This pipe is done with the processing. Future calls to
+ // process will be accepted as long as new data is available.
+ STATUS_DONE = 2,
+
+ // This pipe is requesting that it become the head in a process.
+ STATUS_BREAK = 3,
+
+ // This pipe is requesting that it become the head in a process.
+ STATUS_NEED_PROCESS = 4,
+
+ // Keep track of the highest number of success codes here.
+ STATUS_SUCCESS_COUNT = 5,
+
+ // A generic error code.
+ STATUS_ERROR = -1,
+
+ // This method has not yet been implemented. This usually
+ // indicates the programmer working on the pipe is not yet
+ // done.
+ STATUS_NOT_IMPLEMENTED = -2,
+
+ // This indicates that a pipe precondition was not met. For
+ // example, many pipes require an element to appear after them
+ // in a chain (ie, mNext is not null) and will return this in
+ // response to method calls. To recover from this, it will
+ // require the caller to adjust the pipe state or may require
+ // a dev to adjust the code to satisfy the preconditions.
+ STATUS_PRECONDITION_NOT_MET = -3,
+
+ // This means we could not connect to a remote host.
+ STATUS_NO_CONNECTION = -4,
+
+ // This means we could not connect to a remote host.
+ STATUS_EXPIRED = -5,
+
+ // Keep track of the count of codes here.
+ STATUS_ERROR_COUNT = 5,
+ };
+
+ /**
+ * @brief Helper function to check status.
+ *
+ * When writing code to check status codes, if you do not
+ * specifically check a particular value, use this method for
+ * checking an error condition.
+ * @param status The status to check.
+ * @return Returns true if the code indicates an error occurred.
+ */
+ inline static bool isError(EStatus status)
+ {
+ return ((S32)status < 0);
+ }
+
+ /**
+ * @brief Helper function to check status.
+ *
+ * When writing code to check status codes, if you do not
+ * specifically check a particular value, use this method for
+ * checking an error condition.
+ * @param status The status to check.
+ * @return Returns true if the code indicates no error was generated.
+ */
+ inline static bool isSuccess(EStatus status)
+ {
+ return ((S32)status >= 0);
+ }
+
+ /**
+ * @brief Helper function to turn status into a string.
+ *
+ * @param status The status to check.
+ * @return Returns the name of the status code or empty string on failure.
+ */
+ static std::string lookupStatusString(EStatus status);
+
+ /**
+ * @brief Process the data in buffer.
+ *
+ * @param data The data processed
+ * @param eos True if this function call is the last because end of stream.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context Shared meta-data for the process.
+ * @return Returns a status code from the operation.
+ */
+ EStatus process(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+ /**
+ * @brief Give this pipe a chance to handle a generated error
+ *
+ * If this pipe is in a chain being processed by a pump, and one
+ * of the pipes generates an error, the pump will rewind through
+ * the chain to see if any of the links can handle the error. For
+ * example, if a connection is refused in a socket connection, the
+ * socket client can try to find a new destination host. Return an
+ * error code if this pipe does not handle the error passed in.
+ * @param status The status code for the error
+ * @param pump The pump which was calling process before the error
+ * was generated.
+ * @return Returns a status code from the operation. Returns an
+ * error code if the error passed in was not handled. Returns
+ * STATUS_OK to indicate the error has been handled.
+ */
+ virtual EStatus handleError(EStatus status, LLPumpIO* pump);
+
+ /**
+ * @brief Base Destructor - do not call <code>delete</code> directly.
+ */
+ virtual ~LLIOPipe();
+
+protected:
+ /**
+ * @brief Base Constructor.
+ */
+ LLIOPipe();
+
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump) = 0;
+
+private:
+ friend void boost::intrusive_ptr_add_ref(LLIOPipe* p);
+ friend void boost::intrusive_ptr_release(LLIOPipe* p);
+ U32 mReferenceCount;
+};
+
+namespace boost
+{
+ inline void intrusive_ptr_add_ref(LLIOPipe* p)
+ {
+ ++p->mReferenceCount;
+ }
+ inline void intrusive_ptr_release(LLIOPipe* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+
+
+#if 0
+/**
+ * @class LLIOBoiler
+ * @brief This class helps construct new LLIOPipe specializations
+ * @see LLIOPipe
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLIOBoiler : public LLIOPipe
+{
+public:
+ LLIOBoiler();
+ virtual ~LLIOBoiler();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+};
+
+// virtual
+LLIOPipe::EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+#endif // #if 0 - use this block as a boilerplate
+
+#endif // LL_LLIOPIPE_H
diff --git a/indra/llmessage/lliosocket.cpp b/indra/llmessage/lliosocket.cpp
new file mode 100644
index 0000000000..7649fef0cf
--- /dev/null
+++ b/indra/llmessage/lliosocket.cpp
@@ -0,0 +1,596 @@
+/**
+ * @file lliosocket.cpp
+ * @author Phoenix
+ * @date 2005-07-31
+ * @brief Sockets declarations for use with the io pipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliosocket.h"
+
+#include "llapr.h"
+
+#include "llbuffer.h"
+#include "llhost.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+
+//
+// constants
+//
+
+static const S32 LL_DEFAULT_LISTEN_BACKLOG = 10;
+static const S32 LL_SEND_BUFFER_SIZE = 40000;
+static const S32 LL_RECV_BUFFER_SIZE = 40000;
+//static const U16 LL_PORT_DISCOVERY_RANGE_MIN = 13000;
+//static const U16 LL_PORT_DISCOVERY_RANGE_MAX = 13050;
+
+//
+// local methods
+//
+
+bool is_addr_in_use(apr_status_t status)
+{
+#if LL_WINDOWS
+ return (WSAEADDRINUSE == APR_TO_OS_ERROR(status));
+#else
+ return (EADDRINUSE == APR_TO_OS_ERROR(status));
+#endif
+}
+
+///
+/// LLSocket
+///
+
+// static
+LLSocket::ptr_t LLSocket::create(apr_pool_t* pool, EType type, U16 port)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ LLSocket::ptr_t rv;
+ apr_socket_t* socket = NULL;
+ apr_pool_t* new_pool = NULL;
+ apr_status_t status = APR_EGENERAL;
+
+ // create a pool for the socket
+ status = apr_pool_create(&new_pool, pool);
+ if(ll_apr_warn_status(status))
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+
+ if(STREAM_TCP == type)
+ {
+ status = apr_socket_create(
+ &socket,
+ APR_INET,
+ SOCK_STREAM,
+ APR_PROTO_TCP,
+ new_pool);
+ }
+ else if(DATAGRAM_UDP == type)
+ {
+ status = apr_socket_create(
+ &socket,
+ APR_INET,
+ SOCK_DGRAM,
+ APR_PROTO_UDP,
+ new_pool);
+ }
+ else
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+ if(ll_apr_warn_status(status))
+ {
+ if(new_pool) apr_pool_destroy(new_pool);
+ return rv;
+ }
+ rv = ptr_t(new LLSocket(socket, new_pool));
+ if(port > 0)
+ {
+ apr_sockaddr_t* sa = NULL;
+ status = apr_sockaddr_info_get(
+ &sa,
+ APR_ANYADDR,
+ APR_UNSPEC,
+ port,
+ 0,
+ new_pool);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ // This allows us to reuse the address on quick down/up. This
+ // is unlikely to create problems.
+ ll_apr_warn_status(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1));
+ status = apr_socket_bind(socket, sa);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ lldebugs << "Bound " << ((DATAGRAM_UDP == type) ? "udp" : "tcp")
+ << " socket to port: " << sa->port << llendl;
+ if(STREAM_TCP == type)
+ {
+ // If it's a stream based socket, we need to tell the OS
+ // to keep a queue of incoming connections for ACCEPT.
+ lldebugs << "Setting listen state for socket." << llendl;
+ status = apr_socket_listen(
+ socket,
+ LL_DEFAULT_LISTEN_BACKLOG);
+ if(ll_apr_warn_status(status))
+ {
+ rv.reset();
+ return rv;
+ }
+ }
+ }
+ else
+ {
+ // we need to indicate that we have an ephemeral port if the
+ // previous calls were successful. It will
+ port = PORT_EPHEMERAL;
+ }
+ rv->mPort = port;
+ rv->setOptions();
+ return rv;
+}
+
+// static
+LLSocket::ptr_t LLSocket::create(apr_socket_t* socket, apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ LLSocket::ptr_t rv;
+ if(!socket)
+ {
+ return rv;
+ }
+ rv = ptr_t(new LLSocket(socket, pool));
+ rv->mPort = PORT_EPHEMERAL;
+ rv->setOptions();
+ return rv;
+}
+
+
+bool LLSocket::blockingConnect(const LLHost& host)
+{
+ if(!mSocket) return false;
+ apr_sockaddr_t* sa = NULL;
+ char ip_address[MAXADDRSTR]; /*Flawfinder: ignore*/
+ host.getIPString(ip_address, MAXADDRSTR);
+ if(ll_apr_warn_status(apr_sockaddr_info_get(
+ &sa,
+ ip_address,
+ APR_UNSPEC,
+ host.getPort(),
+ 0,
+ mPool)))
+ {
+ return false;
+ }
+ apr_socket_timeout_set(mSocket, 1000);
+ if(ll_apr_warn_status(apr_socket_connect(mSocket, sa))) return false;
+ setOptions();
+ return true;
+}
+
+LLSocket::LLSocket(apr_socket_t* socket, apr_pool_t* pool) :
+ mSocket(socket),
+ mPool(pool),
+ mPort(PORT_INVALID)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLSocket::~LLSocket()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ // *FIX: clean up memory we are holding.
+ //lldebugs << "Destroying LLSocket" << llendl;
+ if(mSocket)
+ {
+ apr_socket_close(mSocket);
+ }
+ if(mPool)
+ {
+ apr_pool_destroy(mPool);
+ }
+}
+
+void LLSocket::setOptions()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ // set up the socket options
+ ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
+
+}
+
+///
+/// LLIOSocketReader
+///
+
+LLIOSocketReader::LLIOSocketReader(LLSocket::ptr_t socket) :
+ mSource(socket),
+ mInitialized(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOSocketReader::~LLIOSocketReader()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOSocketReader" << llendl;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOSocketReader::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!mSource) return STATUS_PRECONDITION_NOT_MET;
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // Since the read will not block, it's ok to initialize and
+ // attempt to read off the descriptor immediately.
+ mInitialized = true;
+ if(pump)
+ {
+ PUMP_DEBUG;
+ lldebugs << "Initializing poll descriptor for LLIOSocketReader."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLIN;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mSource->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ }
+ }
+ //if(!buffer)
+ //{
+ // buffer = new LLBufferArray;
+ //}
+ PUMP_DEBUG;
+ const apr_size_t READ_BUFFER_SIZE = 1024;
+ char read_buf[READ_BUFFER_SIZE]; /*Flawfinder: ignore*/
+ apr_size_t len;
+ apr_status_t status = APR_SUCCESS;
+ do
+ {
+ PUMP_DEBUG;
+ len = READ_BUFFER_SIZE;
+ status = apr_socket_recv(mSource->getSocket(), read_buf, &len);
+ buffer->append(channels.out(), (U8*)read_buf, len);
+ } while((APR_SUCCESS == status) && (READ_BUFFER_SIZE == len));
+ lldebugs << "socket read status: " << status << llendl;
+ LLIOPipe::EStatus rv = STATUS_OK;
+
+ PUMP_DEBUG;
+ // *FIX: Also need to check for broken pipe
+ if(APR_STATUS_IS_EOF(status))
+ {
+ // *FIX: Should we shut down the socket read?
+ if(pump)
+ {
+ pump->setConditional(this, NULL);
+ }
+ rv = STATUS_DONE;
+ eos = true;
+ }
+ else if(APR_STATUS_IS_EAGAIN(status))
+ {
+ // everything is fine, but we can terminate this process pump.
+ rv = STATUS_BREAK;
+ }
+ else
+ {
+ if(ll_apr_warn_status(status))
+ {
+ rv = STATUS_ERROR;
+ }
+ }
+ PUMP_DEBUG;
+ return rv;
+}
+
+///
+/// LLIOSocketWriter
+///
+
+LLIOSocketWriter::LLIOSocketWriter(LLSocket::ptr_t socket) :
+ mDestination(socket),
+ mLastWritten(NULL),
+ mInitialized(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOSocketWriter::~LLIOSocketWriter()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOSocketWriter" << llendl;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOSocketWriter::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!mDestination) return STATUS_PRECONDITION_NOT_MET;
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // Since the write will not block, it's ok to initialize and
+ // attempt to write immediately.
+ mInitialized = true;
+ if(pump)
+ {
+ PUMP_DEBUG;
+ lldebugs << "Initializing poll descriptor for LLIOSocketWriter."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLOUT;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mDestination->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ }
+ }
+
+ PUMP_DEBUG;
+ // *FIX: Some sort of writev implementation would be much more
+ // efficient - not only because writev() is better, but also
+ // because we won't have to do as much work to find the start
+ // address.
+ LLBufferArray::segment_iterator_t it;
+ LLBufferArray::segment_iterator_t end = buffer->endSegment();
+ LLSegment segment;
+ it = buffer->constructSegmentAfter(mLastWritten, segment);
+ /*
+ if(NULL == mLastWritten)
+ {
+ it = buffer->beginSegment();
+ segment = (*it);
+ }
+ else
+ {
+ it = buffer->getSegment(mLastWritten);
+ segment = (*it);
+ S32 size = segment.size();
+ U8* data = segment.data();
+ if((data + size) == mLastWritten)
+ {
+ ++it;
+ segment = (*it);
+ }
+ else
+ {
+ // *FIX: check the math on this one
+ segment = LLSegment(
+ (*it).getChannelMask(),
+ mLastWritten + 1,
+ size - (mLastWritten - data));
+ }
+ }
+ */
+
+ PUMP_DEBUG;
+ apr_size_t len;
+ bool done = false;
+ while(it != end)
+ {
+ PUMP_DEBUG;
+ if((*it).isOnChannel(channels.in()))
+ {
+ PUMP_DEBUG;
+ // *FIX: check return code - sockets will fail (broken, etc.)
+ len = (apr_size_t)segment.size();
+ apr_socket_send(
+ mDestination->getSocket(),
+ (const char*)segment.data(),
+ &len);
+ mLastWritten = segment.data() + len - 1;
+ PUMP_DEBUG;
+ if((S32)len < segment.size())
+ {
+ break;
+ }
+ }
+ ++it;
+ if(it != end)
+ {
+ segment = (*it);
+ }
+ else
+ {
+ done = true;
+ }
+ }
+ PUMP_DEBUG;
+ if(done && eos)
+ {
+ return STATUS_DONE;
+ }
+ return STATUS_OK;
+}
+
+
+///
+/// LLIOServerSocket
+///
+
+LLIOServerSocket::LLIOServerSocket(
+ apr_pool_t* pool,
+ LLIOServerSocket::socket_t listener,
+ factory_t factory) :
+ mPool(pool),
+ mListenSocket(listener),
+ mReactor(factory),
+ mInitialized(false),
+ mResponseTimeout(DEFAULT_CHAIN_EXPIRY_SECS)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+}
+
+LLIOServerSocket::~LLIOServerSocket()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ //lldebugs << "Destroying LLIOServerSocket" << llendl;
+}
+
+void LLIOServerSocket::setResponseTimeout(F32 timeout_secs)
+{
+ mResponseTimeout = timeout_secs;
+}
+
+// virtual
+LLIOPipe::EStatus LLIOServerSocket::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_TCP);
+ if(!pump)
+ {
+ llwarns << "Need a pump for server socket." << llendl;
+ return STATUS_ERROR;
+ }
+ if(!mInitialized)
+ {
+ PUMP_DEBUG;
+ // This segment sets up the pump so that we do not call
+ // process again until we have an incoming read, aka connect()
+ // from a remote host.
+ lldebugs << "Initializing poll descriptor for LLIOServerSocket."
+ << llendl;
+ apr_pollfd_t poll_fd;
+ poll_fd.p = NULL;
+ poll_fd.desc_type = APR_POLL_SOCKET;
+ poll_fd.reqevents = APR_POLLIN;
+ poll_fd.rtnevents = 0x0;
+ poll_fd.desc.s = mListenSocket->getSocket();
+ poll_fd.client_data = NULL;
+ pump->setConditional(this, &poll_fd);
+ mInitialized = true;
+ return STATUS_OK;
+ }
+
+ // we are initialized, and told to process, so we must have a
+ // socket waiting for a connection.
+ lldebugs << "accepting socket" << llendl;
+
+ PUMP_DEBUG;
+ apr_pool_t* new_pool = NULL;
+ apr_status_t status = apr_pool_create(&new_pool, mPool);
+ apr_socket_t* socket = NULL;
+ status = apr_socket_accept(
+ &socket,
+ mListenSocket->getSocket(),
+ new_pool);
+ LLSocket::ptr_t llsocket(LLSocket::create(socket, new_pool));
+ //EStatus rv = STATUS_ERROR;
+ if(llsocket)
+ {
+ PUMP_DEBUG;
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketReader(llsocket)));
+ if(mReactor->build(chain, LLSD()))
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(llsocket)));
+ pump->addChain(chain, mResponseTimeout);
+ status = STATUS_OK;
+ }
+ else
+ {
+ llwarns << "Unable to build reactor to socket." << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Unable to create linden socket." << llendl;
+ }
+
+ PUMP_DEBUG;
+ // This needs to always return success, lest it get removed from
+ // the pump.
+ return STATUS_OK;
+}
+
+
+#if 0
+LLIODataSocket::LLIODataSocket(
+ U16 suggested_port,
+ U16 start_discovery_port,
+ apr_pool_t* pool) :
+ mSocket(NULL)
+{
+ if(!pool || (PORT_INVALID == suggested_port)) return;
+ if(ll_apr_warn_status(apr_socket_create(&mSocket, APR_INET, SOCK_DGRAM, APR_PROTO_UDP, pool))) return;
+ apr_sockaddr_t* sa = NULL;
+ if(ll_apr_warn_status(apr_sockaddr_info_get(&sa, APR_ANYADDR, APR_UNSPEC, suggested_port, 0, pool))) return;
+ apr_status_t status = apr_socket_bind(mSocket, sa);
+ if((start_discovery_port > 0) && is_addr_in_use(status))
+ {
+ const U16 MAX_ATTEMPT_PORTS = 50;
+ for(U16 attempt_port = start_discovery_port;
+ attempt_port < (start_discovery_port + MAX_ATTEMPT_PORTS);
+ ++attempt_port)
+ {
+ sa->port = attempt_port;
+ sa->sa.sin.sin_port = htons(attempt_port);
+ status = apr_socket_bind(mSocket, sa);
+ if(APR_SUCCESS == status) break;
+ if(is_addr_in_use(status)) continue;
+ (void)ll_apr_warn_status(status);
+ }
+ }
+ if(ll_apr_warn_status(status)) return;
+ if(sa->port)
+ {
+ lldebugs << "Bound datagram socket to port: " << sa->port << llendl;
+ mPort = sa->port;
+ }
+ else
+ {
+ mPort = LLIOSocket::PORT_EPHEMERAL;
+ }
+
+ // set up the socket options options
+ ll_apr_warn_status(apr_socket_timeout_set(mSocket, 0));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_SNDBUF, LL_SEND_BUFFER_SIZE));
+ ll_apr_warn_status(apr_socket_opt_set(mSocket, APR_SO_RCVBUF, LL_RECV_BUFFER_SIZE));
+}
+
+LLIODataSocket::~LLIODataSocket()
+{
+}
+
+
+#endif
diff --git a/indra/llmessage/lliosocket.h b/indra/llmessage/lliosocket.h
new file mode 100644
index 0000000000..fd15949b69
--- /dev/null
+++ b/indra/llmessage/lliosocket.h
@@ -0,0 +1,355 @@
+/**
+ * @file lliosocket.h
+ * @author Phoenix
+ * @date 2005-07-31
+ * @brief Declaration of files used for handling sockets and associated pipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOSOCKET_H
+#define LL_LLIOSOCKET_H
+
+/**
+ * The socket interface provided here is a simple wraper around apr
+ * sockets, with a pipe source and sink to read and write off of the
+ * socket. Every socket only performs non-blocking operations except
+ * the server socket which only performs blocking operations when an
+ * OS poll indicates it will not block.
+ */
+
+#include "lliopipe.h"
+#include "apr-1/apr_pools.h"
+#include "apr-1/apr_network_io.h"
+#include "llchainio.h"
+
+class LLHost;
+
+/**
+ * @class LLSocket
+ * @brief Implementation of a wrapper around a socket.
+ *
+ * An instance of this class represents a single socket over it's
+ * entire life - from uninitialized, to connected, to a listening
+ * socket depending on it's purpose. This class simplifies our access
+ * into the socket interface by only providing stream/tcp and
+ * datagram/udp sockets - the only types we are interested in, since
+ * those are the only properly supported by all of our platforms.
+ */
+class LLSocket
+{
+public:
+ /**
+ * @brief Reference counted shared pointers to sockets.
+ */
+ typedef boost::shared_ptr<LLSocket> ptr_t;
+
+ /**
+ * @brief Type of socket to create.
+ */
+ enum EType
+ {
+ STREAM_TCP,
+ DATAGRAM_UDP,
+ };
+
+ /**
+ * @brief Anonymous enumeration to help identify ports
+ */
+ enum
+ {
+ PORT_INVALID = (U16)-1,
+ PORT_EPHEMERAL = 0,
+ };
+
+ /**
+ * @brief Create a socket.
+ *
+ * This is the call you would use if you intend to create a listen
+ * socket. If you intend the socket to be known to external
+ * clients without prior port notification, do not use
+ * PORT_EPHEMERAL.
+ * @param pool The apr pool to use. A child pool will be created
+ * and associated with the socket.
+ * @param type The type of socket to create
+ * @param port The port for the socket
+ * @return A valid socket shared pointer if the call worked.
+ */
+ static ptr_t create(
+ apr_pool_t* pool,
+ EType type,
+ U16 port = PORT_EPHEMERAL);
+
+ /**
+ * @brief Create a LLSocket when you already have an apr socket.
+ *
+ * This method assumes an ephemeral port. This is typically used
+ * by calls which spawn a socket such as a call to
+ * <code>accept()</code> as in the server socket. This call should
+ * not fail if you have a valid apr socket.
+ * Because of the nature of how accept() works, you are expected
+ * to create a new pool for the socket, use that pool for the
+ * accept, and pass it in here where it will be bound with the
+ * socket and destroyed at the same time.
+ * @param socket The apr socket to use
+ * @param pool The pool used to create the socket. *NOTE: The pool
+ * passed in will be DESTROYED.
+ * @return A valid socket shared pointer if the call worked.
+ */
+ static ptr_t create(apr_socket_t* socket, apr_pool_t* pool);
+
+ /**
+ * @brief Perform a blocking connect to a host. Do not use in production.
+ *
+ * @param host The host to connect this socket to.
+ * @return Returns true if the connect was successful.
+ */
+ bool blockingConnect(const LLHost& host);
+
+ /**
+ * @brief Get the type of socket
+ */
+ //EType getType() const { return mType; }
+
+ /**
+ * @brief Get the port.
+ *
+ * This will return PORT_EPHEMERAL if bind was never called.
+ * @return Returns the port associated with this socket.
+ */
+ U16 getPort() const { return mPort; }
+
+ /**
+ * @brief Get the apr socket implementation.
+ *
+ * @return Returns the raw apr socket.
+ */
+ apr_socket_t* getSocket() const { return mSocket; }
+
+protected:
+ /**
+ * @brief Protected constructor since should only make sockets
+ * with one of the two <code>create()</code> calls.
+ */
+ LLSocket(apr_socket_t* socket, apr_pool_t* pool);
+
+ /**
+ * @brief Set default socket options.
+ */
+ void setOptions();
+
+public:
+ /**
+ * @brief Do not call this directly.
+ */
+ ~LLSocket();
+
+protected:
+ // The apr socket.
+ apr_socket_t* mSocket;
+
+ // our memory pool
+ apr_pool_t* mPool;
+
+ // The port if we know it.
+ U16 mPort;
+
+ //EType mType;
+};
+
+/**
+ * @class LLIOSocketReader
+ * @brief An LLIOPipe implementation which reads from a socket.
+ * @see LLIOPipe
+ *
+ * An instance of a socket reader wraps around an LLSocket and
+ * performs non-blocking reads and passes it to the next pipe in the
+ * chain.
+ */
+class LLIOSocketReader : public LLIOPipe
+{
+public:
+ LLIOSocketReader(LLSocket::ptr_t socket);
+ ~LLIOSocketReader();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data coming in the socket.
+ *
+ * Since the socket and next pipe must exist for process to make
+ * any sense, this method will return STATUS_PRECONDITION_NOT_MET
+ * unless if they are not known.
+ * If a STATUS_STOP returned by the next link in the chain, this
+ * reader will turn of the socket polling.
+ * @param buffer Pointer to a buffer which needs processing. Probably NULL.
+ * @param bytes Number of bytes to in buffer to process. Probably 0.
+ * @param eos True if this function is the last. Almost always false.
+ * @param read Number of bytes actually processed.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context A data structure to pass structured data
+ * @return STATUS_OK unless the preconditions are not met.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSocket::ptr_t mSource;
+ std::vector<U8> mBuffer;
+ bool mInitialized;
+};
+
+/**
+ * @class LLIOSocketWriter
+ * @brief An LLIOPipe implementation which writes to a socket
+ * @see LLIOPipe
+ *
+ * An instance of a socket writer wraps around an LLSocket and
+ * performs non-blocking writes of the data passed in.
+ */
+class LLIOSocketWriter : public LLIOPipe
+{
+public:
+ LLIOSocketWriter(LLSocket::ptr_t socket);
+ ~LLIOSocketWriter();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Write the data in buffer to the socket.
+ *
+ * Since the socket pipe must exist for process to make any sense,
+ * this method will return STATUS_PRECONDITION_NOT_MET if it is
+ * not known.
+ * @param buffer Pointer to a buffer which needs processing.
+ * @param bytes Number of bytes to in buffer to process.
+ * @param eos True if this function is the last.
+ * @param read Number of bytes actually processed.
+ * @param pump The pump which is calling process. May be NULL.
+ * @param context A data structure to pass structured data
+ * @return A return code for the write.
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSocket::ptr_t mDestination;
+ U8* mLastWritten;
+ bool mInitialized;
+};
+
+/**
+ * @class LLIOServerSocket
+ * @brief An IOPipe implementation which listens and spawns connected
+ * sockets.
+ * @see LLIOPipe, LLChainIOFactory
+ *
+ * Each server socket instance coordinates with a pump to ensure it
+ * only processes waiting connections. It uses the provided socket,
+ * and assumes it is correctly initialized. When the connection is
+ * established, the server will call the chain factory to build a
+ * chain, and attach a socket reader and the front and a socket writer
+ * at the end. It is up to the chain factory to create something which
+ * correctly handles the established connection using the reader as a
+ * source, and the writer as the final sink.
+ * The newly added chain timeout is in DEFAULT_CHAIN_EXPIRY_SECS unless
+ * adjusted with a call to setResponseTimeout().
+ */
+class LLIOServerSocket : public LLIOPipe
+{
+public:
+ typedef LLSocket::ptr_t socket_t;
+ typedef boost::shared_ptr<LLChainIOFactory> factory_t;
+ LLIOServerSocket(apr_pool_t* pool, socket_t listener, factory_t reactor);
+ virtual ~LLIOServerSocket();
+
+ /**
+ * @brief Set the timeout for the generated chains.
+ *
+ * This value is passed directly to the LLPumpIO::addChain()
+ * method. The default on construction is set to
+ * DEFAULT_CHAIN_EXPIRY_SECS which is a reasonable value for most
+ * applications based on this library. Avoid passing in
+ * NEVER_CHAIN_EXPIRY_SECS unless you have another method of
+ * harvesting chains.
+ * @param timeout_secs The seconds before timeout for the response chain.
+ */
+ void setResponseTimeout(F32 timeout_secs);
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ apr_pool_t* mPool;
+ socket_t mListenSocket;
+ factory_t mReactor;
+ bool mInitialized;
+ F32 mResponseTimeout;
+};
+
+#if 0
+/**
+ * @class LLIODataSocket
+ * @brief BRIEF_DESC
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLIODataSocket : public LLIOSocket
+{
+public:
+ /**
+ * @brief Construct a datagram socket.
+ *
+ * If you pass in LLIOSocket::PORT_EPHEMERAL as the suggested
+ * port, The socket will not be in a 'listen' mode, but can still
+ * read data sent back to it's port. When suggested_port is not
+ * ephemeral or invalid and bind fails, the port discovery
+ * algorithm will search through a limited set of ports to
+ * try to find an open port. If that process fails, getPort() will
+ * return LLIOSocket::PORT_INVALID
+ * @param suggested_port The port you would like to bind. Use
+ * LLIOSocket::PORT_EPHEMERAL for an unspecified port.
+ * @param start_discovery_port The start range for
+ * @param pool The pool to use for allocation.
+ */
+ LLIODataSocket(
+ U16 suggested_port,
+ U16 start_discovery_port,
+ apr_pool_t* pool);
+ virtual ~LLIODataSocket();
+
+protected:
+
+private:
+ apr_socket_t* mSocket;
+};
+#endif
+
+#endif // LL_LLIOSOCKET_H
diff --git a/indra/llmessage/llioutil.cpp b/indra/llmessage/llioutil.cpp
new file mode 100644
index 0000000000..b0369439e6
--- /dev/null
+++ b/indra/llmessage/llioutil.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llioutil.cpp
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Utility functionality for the io pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llioutil.h"
+
+/**
+ * LLIOFlush
+ */
+LLIOPipe::EStatus LLIOFlush::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ eos = true;
+ return STATUS_OK;
+}
+
+/**
+ * @class LLIOSleep
+ */
+LLIOPipe::EStatus LLIOSleep::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ if(mSeconds > 0.0)
+ {
+ if(pump) pump->sleepChain(mSeconds);
+ mSeconds = 0.0;
+ return STATUS_BREAK;
+ }
+ return STATUS_DONE;
+}
+
+/**
+ * @class LLIOAddChain
+ */
+LLIOPipe::EStatus LLIOAddChain::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ pump->addChain(mChain, mTimeout);
+ return STATUS_DONE;
+}
+
+/**
+ * LLChangeChannel
+ */
+LLChangeChannel::LLChangeChannel(S32 is, S32 becomes) :
+ mIs(is),
+ mBecomes(becomes)
+{
+}
+
+void LLChangeChannel::operator()(LLSegment& segment)
+{
+ if(segment.isOnChannel(mIs))
+ {
+ segment.setChannel(mBecomes);
+ }
+}
diff --git a/indra/llmessage/llioutil.h b/indra/llmessage/llioutil.h
new file mode 100644
index 0000000000..3b8452918e
--- /dev/null
+++ b/indra/llmessage/llioutil.h
@@ -0,0 +1,154 @@
+/**
+ * @file llioutil.h
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Helper classes for dealing with IOPipes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIOUTIL_H
+#define LL_LLIOUTIL_H
+
+#include "llbuffer.h"
+#include "lliopipe.h"
+#include "llpumpio.h"
+
+/**
+ * @class LLIOFlush
+ * @brief This class is used as a mini chain head which drains the buffer.
+ * @see LLIOPipe
+ *
+ * An instance of this class acts as a useful chain head when all data
+ * is known, and you simply want to get the chain moving.
+ */
+class LLIOFlush : public LLIOPipe
+{
+public:
+ LLIOFlush() {}
+ virtual ~LLIOFlush() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+protected:
+};
+
+/**
+ * @class LLIOSleep
+ * @brief This is a simple helper class which will hold a chain and
+ * process it later using pump mechanisms
+ * @see LLIOPipe
+ */
+class LLIOSleep : public LLIOPipe
+{
+public:
+ LLIOSleep(F64 sleep_seconds) : mSeconds(sleep_seconds) {}
+ virtual ~LLIOSleep() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+protected:
+ F64 mSeconds;
+};
+
+/**
+ * @class LLIOAddChain
+ * @brief Simple pipe that just adds a chain to a pump.
+ * @see LLIOPipe
+ */
+class LLIOAddChain : public LLIOPipe
+{
+public:
+ LLIOAddChain(const LLPumpIO::chain_t& chain, F32 timeout) :
+ mChain(chain),
+ mTimeout(timeout)
+ {}
+ virtual ~LLIOAddChain() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLPumpIO::chain_t mChain;
+ F32 mTimeout;
+};
+
+/**
+ * @class LLChangeChannel
+ * @brief This class changes the channel of segments in the buffer
+ * @see LLBufferArray
+ *
+ * This class is useful for iterating over the segments in a buffer
+ * array and changing each channel that matches to a different
+ * channel.
+ * Example:
+ * <code>
+ * set_in_to_out(LLChannelDescriptors channels, LLBufferArray* buf)
+ * {
+ * std::for_each(
+ * buf->beginSegment(),
+ * buf->endSegment(),
+ * LLChangeChannel(channels.in(), channels.out()));
+ * }
+ * </code>
+ */
+class LLChangeChannel //: public unary_function<T, void>
+{
+public:
+ /**
+ * @brief Constructor for iterating over a segment range to change channel.
+ *
+ * @param is The channel to match when looking at a segment.
+ * @param becomes The channel to set the segment when a match is found.
+ */
+ LLChangeChannel(S32 is, S32 becomes);
+
+ /**
+ * @brief Do the work of changing the channel
+ */
+ void operator()(LLSegment& segment);
+
+protected:
+ S32 mIs;
+ S32 mBecomes;
+};
+
+
+#endif // LL_LLIOUTIL_H
diff --git a/indra/llmessage/llloginflags.h b/indra/llmessage/llloginflags.h
new file mode 100644
index 0000000000..b21088a61d
--- /dev/null
+++ b/indra/llmessage/llloginflags.h
@@ -0,0 +1,37 @@
+/**
+ * @file llloginflags.h
+ * @brief Login flag constants.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLOGINFLAGS_H
+#define LL_LLLOGINFLAGS_H
+
+// Is this your first login to Second Life?
+const U32 LOGIN_FLAG_FIRST_LOGIN = (1 << 0);
+
+// Is this a trial account?
+const U32 LOGIN_FLAG_TRIAL = (1 << 1);
+
+// Stipend paid since last login?
+const U32 LOGIN_FLAG_STIPEND_SINCE_LOGIN = (1 << 2);
+
+// Is your account enabled?
+const U32 LOGIN_FLAG_ENABLED = (1 << 3);
+
+// Is the Pacific Time zone (aka the server time zone)
+// currently observing daylight savings time?
+const U32 LOGIN_FLAG_PACIFIC_DAYLIGHT_TIME = (1 << 4);
+
+// Does the avatar have wearables or not
+const U32 LOGIN_FLAG_GENDERED = (1 << 5);
+
+// Kick flags
+const U32 LOGIN_KICK_OK = 0x0;
+const U32 LOGIN_KICK_NO_AGENT = (1 << 0);
+const U32 LOGIN_KICK_SESSION_MISMATCH = (1 << 1);
+const U32 LOGIN_KICK_NO_SIMULATOR = (1 << 2);
+
+#endif
diff --git a/indra/llmessage/llmail.cpp b/indra/llmessage/llmail.cpp
new file mode 100644
index 0000000000..9fe8e89b20
--- /dev/null
+++ b/indra/llmessage/llmail.cpp
@@ -0,0 +1,285 @@
+/**
+ * @file llmail.cpp
+ * @brief smtp helper functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <string>
+#include <sstream>
+#include <boost/regex.hpp>
+
+#include "llmail.h"
+
+#include "apr-1/apr_pools.h"
+#include "apr-1/apr_network_io.h"
+
+#include "llapr.h"
+#include "llerror.h"
+#include "llhost.h"
+#include "net.h"
+
+//
+// constants
+//
+const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE = 4096;
+
+static bool gMailEnabled = true;
+static apr_pool_t* gMailPool;
+static apr_sockaddr_t* gSockAddr;
+static apr_socket_t* gMailSocket;
+
+// According to RFC2822
+static const boost::regex valid_subject_chars("[\\x1-\\x9\\xb\\xc\\xe-\\x7f]+");
+bool connect_smtp();
+void disconnect_smtp();
+
+//#if LL_WINDOWS
+//SOCKADDR_IN gMailDstAddr, gMailSrcAddr, gMailLclAddr;
+//#else
+//struct sockaddr_in gMailDstAddr, gMailSrcAddr, gMailLclAddr;
+//#endif
+
+// Define this for a super-spammy mail mode.
+//#define LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND 1
+
+bool connect_smtp()
+{
+ // Prepare an soket to talk smtp
+ apr_status_t status;
+ status = apr_socket_create(
+ &gMailSocket,
+ gSockAddr->sa.sin.sin_family,
+ SOCK_STREAM,
+ APR_PROTO_TCP,
+ gMailPool);
+ if(ll_apr_warn_status(status)) return false;
+ status = apr_socket_connect(gMailSocket, gSockAddr);
+ if(ll_apr_warn_status(status))
+ {
+ status = apr_socket_close(gMailSocket);
+ ll_apr_warn_status(status);
+ return false;
+ }
+ return true;
+}
+
+void disconnect_smtp()
+{
+ if(gMailSocket)
+ {
+ apr_status_t status = apr_socket_close(gMailSocket);
+ ll_apr_warn_status(status);
+ gMailSocket = NULL;
+ }
+}
+
+// Returns TRUE on success.
+// message should NOT be SMTP escaped.
+BOOL send_mail(const char* from_name, const char* from_address,
+ const char* to_name, const char* to_address,
+ const char* subject, const char* message)
+{
+ std::string header = build_smtp_transaction(
+ from_name,
+ from_address,
+ to_name,
+ to_address,
+ subject);
+ if(header.empty())
+ {
+ return FALSE;
+ }
+
+ std::string message_str;
+ if(message)
+ {
+ message_str = message;
+ }
+ bool rv = send_mail(header, message_str, to_address, from_address);
+ if(rv) return TRUE;
+ return FALSE;
+}
+
+void init_mail(const std::string& hostname, apr_pool_t* pool)
+{
+ gMailSocket = NULL;
+ if(hostname.empty() || !pool)
+ {
+ gMailPool = NULL;
+ gSockAddr = NULL;
+ }
+ else
+ {
+ gMailPool = pool;
+
+ // collect all the information into a socaddr sturcture. the
+ // documentation is a bit unclear, but I either have to
+ // specify APR_UNSPEC or not specify any flags. I am not sure
+ // which option is better.
+ apr_status_t status = apr_sockaddr_info_get(
+ &gSockAddr,
+ hostname.c_str(),
+ APR_UNSPEC,
+ 25,
+ APR_IPV4_ADDR_OK,
+ gMailPool);
+ ll_apr_warn_status(status);
+ }
+}
+
+void enable_mail(bool mail_enabled)
+{
+ gMailEnabled = mail_enabled;
+}
+
+std::string build_smtp_transaction(
+ const char* from_name,
+ const char* from_address,
+ const char* to_name,
+ const char* to_address,
+ const char* subject)
+{
+ if(!from_address || !to_address)
+ {
+ llinfos << "send_mail build_smtp_transaction reject: missing to and/or"
+ << " from address." << llendl;
+ return std::string();
+ }
+ if(! boost::regex_match(subject, valid_subject_chars))
+ {
+ llinfos << "send_mail build_smtp_transaction reject: bad subject header: "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << llendl;
+ return std::string();
+ }
+ std::ostringstream from_fmt;
+ if(from_name && from_name[0])
+ {
+ // "My Name" <myaddress@example.com>
+ from_fmt << "\"" << from_name << "\" <" << from_address << ">";
+ }
+ else
+ {
+ // <myaddress@example.com>
+ from_fmt << "<" << from_address << ">";
+ }
+ std::ostringstream to_fmt;
+ if(to_name && to_name[0])
+ {
+ to_fmt << "\"" << to_name << "\" <" << to_address << ">";
+ }
+ else
+ {
+ to_fmt << "<" << to_address << ">";
+ }
+ std::ostringstream header;
+ header
+ << "HELO lindenlab.com\r\n"
+ << "MAIL FROM:<" << from_address << ">\r\n"
+ << "RCPT TO:<" << to_address << ">\r\n"
+ << "DATA\r\n"
+ << "From: " << from_fmt.str() << "\r\n"
+ << "To: " << to_fmt.str() << "\r\n"
+ << "Subject: " << subject << "\r\n"
+ << "\r\n";
+ return header.str();
+}
+
+bool send_mail(
+ const std::string& header,
+ const std::string& message,
+ const char* from_address,
+ const char* to_address)
+{
+ if(!from_address || !to_address)
+ {
+ llinfos << "send_mail reject: missing to and/or from address."
+ << llendl;
+ return false;
+ }
+
+ // *FIX: this translation doesn't deal with a single period on a
+ // line by itself.
+ std::ostringstream rfc2822_msg;
+ for(U32 i = 0; i < message.size(); ++i)
+ {
+ switch(message[i])
+ {
+ case '\0':
+ break;
+ case '\n':
+ // *NOTE: this is kinda busted if we're fed \r\n
+ rfc2822_msg << "\r\n";
+ break;
+ default:
+ rfc2822_msg << message[i];
+ break;
+ }
+ }
+
+ if(!gMailEnabled)
+ {
+ llinfos << "send_mail reject: mail system is disabled: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ // Any future interface to SMTP should return this as an
+ // error. --mark
+ return true;
+ }
+ if(!gSockAddr)
+ {
+ llwarns << "send_mail reject: mail system not initialized: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ return false;
+ }
+
+ if(!connect_smtp())
+ {
+ llwarns << "send_mail reject: SMTP connect failure: to=<"
+ << to_address << ">, from=<" << from_address
+ << ">" << llendl;
+ return false;
+ }
+
+ std::ostringstream smtp_fmt;
+ smtp_fmt << header << rfc2822_msg.str() << "\r\n" << ".\r\n" << "QUIT\r\n";
+ std::string smtp_transaction = smtp_fmt.str();
+ size_t original_size = smtp_transaction.size();
+ apr_size_t send_size = original_size;
+ apr_status_t status = apr_socket_send(
+ gMailSocket,
+ smtp_transaction.c_str(),
+ (apr_size_t*)&send_size);
+ disconnect_smtp();
+ if(ll_apr_warn_status(status))
+ {
+ llwarns << "send_mail socket failure: unable to write "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << ", bytes=" << original_size
+ << ", sent=" << send_size << llendl;
+ return false;
+ }
+ if(send_size >= LL_MAX_KNOWN_GOOD_MAIL_SIZE)
+ {
+ llwarns << "send_mail message has been shown to fail in testing "
+ << "when sending messages larger than " << LL_MAX_KNOWN_GOOD_MAIL_SIZE
+ << " bytes. The next log about success is potentially a lie." << llendl;
+ }
+ llinfos << "send_mail success: "
+ << "to=<" << to_address
+ << ">, from=<" << from_address << ">"
+ << ", bytes=" << original_size
+ << ", sent=" << send_size << llendl;
+
+#if LL_LOG_ENTIRE_MAIL_MESSAGE_ON_SEND
+ llinfos << rfc2822_msg.str() << llendl;
+#endif
+ return true;
+}
diff --git a/indra/llmessage/llmail.h b/indra/llmessage/llmail.h
new file mode 100644
index 0000000000..e34b827f5f
--- /dev/null
+++ b/indra/llmessage/llmail.h
@@ -0,0 +1,66 @@
+/**
+ * @file llmail.h
+ * @brief smtp helper functions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMAIL_H
+#define LL_LLMAIL_H
+
+#include "apr-1/apr_pools.h"
+
+// if hostname is NULL, then the host is resolved as 'mail'
+void init_mail(const std::string& hostname, apr_pool_t* pool);
+
+// Allow all email transmission to be disabled/enabled.
+void enable_mail(bool mail_enabled);
+
+// returns TRUE if the call succeeds, FALSE otherwise.
+//
+// Results in:
+// From: "from_name" <from_address>
+// To: "to_name" <to_address>
+// Subject: subject
+// message
+BOOL send_mail(const char* from_name, const char* from_address,
+ const char* to_name, const char* to_address,
+ const char* subject, const char* message);
+
+/**
+ * @brief build the complete smtp transaction & header for use in an
+ * mail.
+ *
+ * @param from_name The name of the email sender
+ * @param from_address The email address for the sender
+ * @param to_name The name of the email recipient
+ * @param to_name The email recipient address
+ * @param subject The subject of the email
+ * @return Returns the complete SMTP transaction mail header.
+ */
+std::string build_smtp_transaction(
+ const char* from_name,
+ const char* from_address,
+ const char* to_name,
+ const char* to_address,
+ const char* subject);
+
+/**
+ * @brief send an email with header and body.
+ *
+ * @param header The email header. Use build_mail_header().
+ * @param message The unescaped email message.
+ * @param from_address Used for debugging
+ * @param to_address Used for debugging
+ * @return Returns true if the message could be sent.
+ */
+bool send_mail(
+ const std::string& header,
+ const std::string& message,
+ const char* from_address,
+ const char* to_address);
+
+extern const size_t LL_MAX_KNOWN_GOOD_MAIL_SIZE;
+
+#endif
diff --git a/indra/llmessage/llmessagethrottle.cpp b/indra/llmessage/llmessagethrottle.cpp
new file mode 100644
index 0000000000..0cfaac3276
--- /dev/null
+++ b/indra/llmessage/llmessagethrottle.cpp
@@ -0,0 +1,135 @@
+/**
+ * @file llmessagethrottle.cpp
+ * @brief LLMessageThrottle class used for throttling messages.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llhash.h"
+
+#include "llmessagethrottle.h"
+#include "llframetimer.h"
+
+// This is used for the stl search_n function.
+bool eq_message_throttle_entry(LLMessageThrottleEntry a, LLMessageThrottleEntry b)
+ { return a.getHash() == b.getHash(); }
+
+const U64 SEC_TO_USEC = 1000000;
+
+// How long (in microseconds) each type of message stays in its throttle list.
+const U64 MAX_MESSAGE_AGE[MTC_EOF] =
+{
+ 10 * SEC_TO_USEC, // MTC_VIEWER_ALERT
+ 10 * SEC_TO_USEC // MTC_AGENT_ALERT
+};
+
+LLMessageThrottle::LLMessageThrottle()
+{
+}
+
+LLMessageThrottle::~LLMessageThrottle()
+{
+}
+
+void LLMessageThrottle::pruneEntries()
+{
+ // Go through each message category, and prune entries older than max age.
+ S32 cat;
+ for (cat = 0; cat < MTC_EOF; cat++)
+ {
+ message_list_t* message_list = &(mMessageList[cat]);
+
+ // Use a reverse iterator, since entries on the back will be the oldest.
+ message_list_reverse_iterator_t r_iterator = message_list->rbegin();
+ message_list_reverse_iterator_t r_last = message_list->rend();
+
+ // Look for the first entry younger than the maximum age.
+ F32 max_age = (F32)MAX_MESSAGE_AGE[cat];
+ BOOL found = FALSE;
+ while (r_iterator != r_last && !found)
+ {
+ if ( LLFrameTimer::getTotalTime() - (*r_iterator).getEntryTime() < max_age )
+ {
+ // We found a young enough entry.
+ found = TRUE;
+
+ // Did we find at least one entry to remove?
+ if (r_iterator != message_list->rbegin())
+ {
+ // Yes, remove it.
+ message_list->erase(r_iterator.base(), message_list->end());
+ }
+ }
+ else
+ {
+ r_iterator++;
+ }
+ }
+
+ // If we didn't find any entries young enough to keep, remove them all.
+ if (!found)
+ {
+ message_list->clear();
+ }
+ }
+}
+
+BOOL LLMessageThrottle::addViewerAlert(const LLUUID& to, const char* mesg)
+{
+ message_list_t* message_list = &(mMessageList[MTC_VIEWER_ALERT]);
+
+ // Concatenate from,to,mesg into one string.
+ std::ostringstream full_mesg;
+ full_mesg << to << mesg;
+
+ // Create an entry for this message.
+ size_t hash = llhash<const char*> (full_mesg.str().c_str());
+ LLMessageThrottleEntry entry(hash, LLFrameTimer::getTotalTime());
+
+ // Check if this message is already in the list.
+ message_list_iterator_t found = std::search_n(message_list->begin(), message_list->end(),
+ 1, entry, eq_message_throttle_entry);
+
+ if (found == message_list->end())
+ {
+ // This message was not found. Add it to the list.
+ message_list->push_front(entry);
+ return TRUE;
+ }
+ else
+ {
+ // This message was already in the list.
+ return FALSE;
+ }
+}
+
+BOOL LLMessageThrottle::addAgentAlert(const LLUUID& agent, const LLUUID& task, const char* mesg)
+{
+ message_list_t* message_list = &(mMessageList[MTC_AGENT_ALERT]);
+
+ // Concatenate from,to,mesg into one string.
+ std::ostringstream full_mesg;
+ full_mesg << agent << task << mesg;
+
+ // Create an entry for this message.
+ size_t hash = llhash<const char*> (full_mesg.str().c_str());
+ LLMessageThrottleEntry entry(hash, LLFrameTimer::getTotalTime());
+
+ // Check if this message is already in the list.
+ message_list_iterator_t found = std::search_n(message_list->begin(), message_list->end(),
+ 1, entry, eq_message_throttle_entry);
+
+ if (found == message_list->end())
+ {
+ // This message was not found. Add it to the list.
+ message_list->push_front(entry);
+ return TRUE;
+ }
+ else
+ {
+ // This message was already in the list.
+ return FALSE;
+ }
+}
+
diff --git a/indra/llmessage/llmessagethrottle.h b/indra/llmessage/llmessagethrottle.h
new file mode 100644
index 0000000000..4c3c01bab9
--- /dev/null
+++ b/indra/llmessage/llmessagethrottle.h
@@ -0,0 +1,62 @@
+/**
+ * @file llmessagethrottle.h
+ * @brief LLMessageThrottle class used for throttling messages.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMESSAGETHROTTLE_H
+#define LL_LLMESSAGETHROTTLE_H
+
+#include <deque>
+
+#include "linden_common.h"
+#include "lluuid.h"
+
+typedef enum e_message_throttle_categories
+{
+ MTC_VIEWER_ALERT,
+ MTC_AGENT_ALERT,
+ MTC_EOF
+} EMessageThrottleCats;
+
+class LLMessageThrottleEntry
+{
+public:
+ LLMessageThrottleEntry(const size_t hash, const U64 entry_time)
+ : mHash(hash), mEntryTime(entry_time) {}
+
+ size_t getHash() { return mHash; }
+ U64 getEntryTime() { return mEntryTime; }
+protected:
+ size_t mHash;
+ U64 mEntryTime;
+};
+
+
+class LLMessageThrottle
+{
+public:
+ LLMessageThrottle();
+ ~LLMessageThrottle();
+
+ BOOL addViewerAlert (const LLUUID& to, const char* mesg);
+ BOOL addAgentAlert (const LLUUID& agent, const LLUUID& task, const char* mesg);
+
+ void pruneEntries();
+
+protected:
+ typedef std::deque<LLMessageThrottleEntry> message_list_t;
+ typedef std::deque<LLMessageThrottleEntry>::iterator message_list_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::reverse_iterator message_list_reverse_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::const_iterator message_list_const_iterator_t;
+ typedef std::deque<LLMessageThrottleEntry>::const_reverse_iterator message_list_const_reverse_iterator_t;
+ message_list_t mMessageList[MTC_EOF];
+};
+
+extern LLMessageThrottle gMessageThrottle;
+
+#endif
+
+
diff --git a/indra/llmessage/llmime.cpp b/indra/llmessage/llmime.cpp
new file mode 100644
index 0000000000..9df9cdf3a7
--- /dev/null
+++ b/indra/llmessage/llmime.cpp
@@ -0,0 +1,613 @@
+/**
+ * @file llmime.cpp
+ * @author Phoenix
+ * @date 2006-12-20
+ * @brief Implementation of mime tools.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmime.h"
+
+#include <vector>
+
+#include "llmemorystream.h"
+
+/**
+ * Useful constants.
+ */
+// Headers specified in rfc-2045 will be canonicalized below.
+static const std::string CONTENT_LENGTH("Content-Length");
+static const std::string CONTENT_TYPE("Content-Type");
+static const S32 KNOWN_HEADER_COUNT = 6;
+static const std::string KNOWN_HEADER[KNOWN_HEADER_COUNT] =
+{
+ CONTENT_LENGTH,
+ CONTENT_TYPE,
+ std::string("MIME-Version"),
+ std::string("Content-Transfer-Encoding"),
+ std::string("Content-ID"),
+ std::string("Content-Description"),
+};
+
+// parser helpers
+static const std::string MULTIPART("multipart");
+static const std::string BOUNDARY("boundary");
+static const std::string END_OF_CONTENT_PARAMETER("\r\n ;\t");
+static const std::string SEPARATOR_PREFIX("--");
+//static const std::string SEPARATOR_SUFFIX("\r\n");
+
+/*
+Content-Type: multipart/mixed; boundary="segment"
+Content-Length: 24832
+
+--segment
+Content-Type: image/j2c
+Content-Length: 23715
+
+<data>
+
+--segment
+Content-Type: text/xml; charset=UTF-8
+
+<meta data>
+EOF
+
+*/
+
+/**
+ * LLMimeIndex
+ */
+
+/**
+ * @class LLMimeIndex::Impl
+ * @brief Implementation details of the mime index class.
+ * @see LLMimeIndex
+ */
+class LLMimeIndex::Impl
+{
+public:
+ Impl() : mOffset(-1), mUseCount(1)
+ {}
+ Impl(LLSD headers, S32 offset) :
+ mHeaders(headers), mOffset(offset), mUseCount(1)
+ {}
+public:
+ LLSD mHeaders;
+ S32 mOffset;
+ S32 mUseCount;
+
+ typedef std::vector<LLMimeIndex> sub_part_t;
+ sub_part_t mAttachments;
+};
+
+LLSD LLMimeIndex::headers() const
+{
+ return mImpl->mHeaders;
+}
+
+S32 LLMimeIndex::offset() const
+{
+ return mImpl->mOffset;
+}
+
+S32 LLMimeIndex::contentLength() const
+{
+ // Find the content length in the headers.
+ S32 length = -1;
+ LLSD content_length = mImpl->mHeaders[CONTENT_LENGTH];
+ if(content_length.isDefined())
+ {
+ length = content_length.asInteger();
+ }
+ return length;
+}
+
+std::string LLMimeIndex::contentType() const
+{
+ std::string type;
+ LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
+ if(content_type.isDefined())
+ {
+ type = content_type.asString();
+ }
+ return type;
+}
+
+bool LLMimeIndex::isMultipart() const
+{
+ bool multipart = false;
+ LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
+ if(content_type.isDefined())
+ {
+ std::string type = content_type.asString();
+ int comp = type.compare(0, MULTIPART.size(), MULTIPART);
+ if(0 == comp)
+ {
+ multipart = true;
+ }
+ }
+ return multipart;
+}
+
+S32 LLMimeIndex::subPartCount() const
+{
+ return mImpl->mAttachments.size();
+}
+
+LLMimeIndex LLMimeIndex::subPart(S32 index) const
+{
+ LLMimeIndex part;
+ if((index >= 0) && (index < (S32)mImpl->mAttachments.size()))
+ {
+ part = mImpl->mAttachments[index];
+ }
+ return part;
+}
+
+LLMimeIndex::LLMimeIndex() : mImpl(new LLMimeIndex::Impl)
+{
+}
+
+LLMimeIndex::LLMimeIndex(LLSD headers, S32 content_offset) :
+ mImpl(new LLMimeIndex::Impl(headers, content_offset))
+{
+}
+
+LLMimeIndex::LLMimeIndex(const LLMimeIndex& mime) :
+ mImpl(mime.mImpl)
+{
+ ++mImpl->mUseCount;
+}
+
+LLMimeIndex::~LLMimeIndex()
+{
+ if(0 == --mImpl->mUseCount)
+ {
+ delete mImpl;
+ }
+}
+
+LLMimeIndex& LLMimeIndex::operator=(const LLMimeIndex& mime)
+{
+ // Increment use count first so that we handle self assignment
+ // automatically.
+ ++mime.mImpl->mUseCount;
+ if(0 == --mImpl->mUseCount)
+ {
+ delete mImpl;
+ }
+ mImpl = mime.mImpl;
+ return *this;
+}
+
+bool LLMimeIndex::attachSubPart(LLMimeIndex sub_part)
+{
+ // *FIX: Should we check for multi-part?
+ if(mImpl->mAttachments.size() < S32_MAX)
+ {
+ mImpl->mAttachments.push_back(sub_part);
+ return true;
+ }
+ return false;
+}
+
+/**
+ * LLMimeParser
+ */
+/**
+ * @class LLMimeParser::Impl
+ * @brief Implementation details of the mime parser class.
+ * @see LLMimeParser
+ */
+class LLMimeParser::Impl
+{
+public:
+ // @brief Constructor.
+ Impl();
+
+ // @brief Reset this for a new parse.
+ void reset();
+
+ /**
+ * @brief Parse a mime entity to find the index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read, an EOF, or limit bytes have been scanned. The
+ * istr will be modified by this parsing, so pass in a temporary
+ * stream or rewind/reset the stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param limit The maximum number of bytes to scan.
+ * @param separator The multipart separator if it is known.
+ * @param is_subpart Set true if parsing a multipart sub part.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator,
+ bool is_subpart,
+ LLMimeIndex& index);
+
+protected:
+ /**
+ * @brief parse the headers.
+ *
+ * At the end of a successful parse, mScanCount will be at the
+ * start of the content.
+ * @param istr The input stream.
+ * @param limit maximum number of bytes to process
+ * @param headers[out] A map of the headers found.
+ * @return Returns true if the parse was successful.
+ */
+ bool parseHeaders(std::istream& istr, S32 limit, LLSD& headers);
+
+ /**
+ * @brief Figure out the separator string from a content type header.
+ *
+ * @param multipart_content_type The content type value from the headers.
+ * @return Returns the separator string.
+ */
+ std::string findSeparator(std::string multipart_content_type);
+
+ /**
+ * @brief Scan through istr past the separator.
+ *
+ * @param istr The input stream.
+ * @param limit Maximum number of bytes to scan.
+ * @param separator The multipart separator.
+ */
+ void scanPastSeparator(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator);
+
+ /**
+ * @brief Scan through istr past the content of the current mime part.
+ *
+ * @param istr The input stream.
+ * @param limit Maximum number of bytes to scan.
+ * @param headers The headers for this mime part.
+ * @param separator The multipart separator if known.
+ */
+ void scanPastContent(
+ std::istream& istr,
+ S32 limit,
+ LLSD headers,
+ const std::string separator);
+
+ /**
+ * @brief Eat CRLF.
+ *
+ * This method has no concept of the limit, so ensure you have at
+ * least 2 characters left to eat before hitting the limit. This
+ * method will increment mScanCount as it goes.
+ * @param istr The input stream.
+ * @return Returns true if CRLF was found and consumed off of istr.
+ */
+ bool eatCRLF(std::istream& istr);
+
+ // @brief Returns true if parsing should continue.
+ bool continueParse() const { return (!mError && mContinue); }
+
+ // @brief anonymous enumeration for parse buffer size.
+ enum
+ {
+ LINE_BUFFER_LENGTH = 1024
+ };
+
+protected:
+ S32 mScanCount;
+ bool mContinue;
+ bool mError;
+ char mBuffer[LINE_BUFFER_LENGTH];
+};
+
+LLMimeParser::Impl::Impl()
+{
+ reset();
+}
+
+void LLMimeParser::Impl::reset()
+{
+ mScanCount = 0;
+ mContinue = true;
+ mError = false;
+ mBuffer[0] = '\0';
+}
+
+bool LLMimeParser::Impl::parseIndex(
+ std::istream& istr,
+ S32 limit,
+ const std::string& separator,
+ bool is_subpart,
+ LLMimeIndex& index)
+{
+ LLSD headers;
+ bool parsed_something = false;
+ if(parseHeaders(istr, limit, headers))
+ {
+ parsed_something = true;
+ LLMimeIndex mime(headers, mScanCount);
+ index = mime;
+ if(index.isMultipart())
+ {
+ // Figure out the separator, scan past it, and recurse.
+ std::string ct = headers[CONTENT_TYPE].asString();
+ std::string sep = findSeparator(ct);
+ scanPastSeparator(istr, limit, sep);
+ while(continueParse() && parseIndex(istr, limit, sep, true, mime))
+ {
+ index.attachSubPart(mime);
+ }
+ }
+ else
+ {
+ // Scan to the end of content.
+ scanPastContent(istr, limit, headers, separator);
+ if(is_subpart)
+ {
+ scanPastSeparator(istr, limit, separator);
+ }
+ }
+ }
+ if(mError) return false;
+ return parsed_something;
+}
+
+bool LLMimeParser::Impl::parseHeaders(
+ std::istream& istr,
+ S32 limit,
+ LLSD& headers)
+{
+ while(continueParse())
+ {
+ // Get the next line.
+ // We subtract 1 from the limit so that we make sure
+ // not to read past limit when we get() the newline.
+ S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
+ istr.getline(mBuffer, max_get, '\r');
+ mScanCount += istr.gcount();
+ int c = istr.get();
+ if(EOF == c)
+ {
+ mContinue = false;
+ return false;
+ }
+ ++mScanCount;
+ if(c != '\n')
+ {
+ mError = true;
+ return false;
+ }
+ if(mScanCount >= limit)
+ {
+ mContinue = false;
+ }
+
+ // Check if that's the end of headers.
+ if('\0' == mBuffer[0])
+ {
+ break;
+ }
+
+ // Split out the name and value.
+ // *NOTE: The use of strchr() here is safe since mBuffer is
+ // guaranteed to be NULL terminated from the call to getline()
+ // above.
+ char* colon = strchr(mBuffer, ':');
+ if(!colon)
+ {
+ mError = true;
+ return false;
+ }
+
+ // Cononicalize the name part, and store the name: value in
+ // the headers structure. We do this by iterating through
+ // 'known' headers and replacing the value found with the
+ // correct one.
+ // *NOTE: Not so efficient, but iterating through a small
+ // subset should not be too much of an issue.
+ std::string name(mBuffer, colon++ - mBuffer);
+ while(isspace(*colon)) ++colon;
+ std::string value(colon);
+ for(S32 ii = 0; ii < KNOWN_HEADER_COUNT; ++ii)
+ {
+ if(0 == LLString::compareInsensitive(
+ name.c_str(),
+ KNOWN_HEADER[ii].c_str()))
+ {
+ name = KNOWN_HEADER[ii];
+ break;
+ }
+ }
+ headers[name] = value;
+ }
+ if(headers.isUndefined()) return false;
+ return true;
+}
+
+std::string LLMimeParser::Impl::findSeparator(std::string header)
+{
+ // 01234567890
+ //Content-Type: multipart/mixed; boundary="segment"
+ std::string separator;
+ std::string::size_type pos = header.find(BOUNDARY);
+ if(std::string::npos == pos) return separator;
+ pos += BOUNDARY.size() + 1;
+ std::string::size_type end;
+ if(header[pos] == '"')
+ {
+ // the boundary is quoted, find the end from pos, and take the
+ // substring.
+ end = header.find('"', ++pos);
+ if(std::string::npos == end)
+ {
+ // poorly formed boundary.
+ mError = true;
+ }
+ }
+ else
+ {
+ // otherwise, it's every character until a whitespace, end of
+ // line, or another parameter begins.
+ end = header.find_first_of(END_OF_CONTENT_PARAMETER, pos);
+ if(std::string::npos == end)
+ {
+ // it goes to the end of the string.
+ end = header.size();
+ }
+ }
+ if(!mError) separator = header.substr(pos, end - pos);
+ return separator;
+}
+
+void LLMimeParser::Impl::scanPastSeparator(
+ std::istream& istr,
+ S32 limit,
+ const std::string& sep)
+{
+ std::ostringstream ostr;
+ ostr << SEPARATOR_PREFIX << sep;
+ std::string separator = ostr.str();
+ bool found_separator = false;
+ while(!found_separator && continueParse())
+ {
+ // Subtract 1 from the limit so that we make sure not to read
+ // past limit when we get() the newline.
+ S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
+ istr.getline(mBuffer, max_get, '\r');
+ mScanCount += istr.gcount();
+ if(istr.gcount() >= LINE_BUFFER_LENGTH - 1)
+ {
+ // that's way too long to be a separator, so ignore it.
+ continue;
+ }
+ int c = istr.get();
+ if(EOF == c)
+ {
+ mContinue = false;
+ return;
+ }
+ ++mScanCount;
+ if(c != '\n')
+ {
+ mError = true;
+ return;
+ }
+ if(mScanCount >= limit)
+ {
+ mContinue = false;
+ }
+ if(0 == LLString::compareStrings(mBuffer, separator.c_str()))
+ {
+ found_separator = true;
+ }
+ }
+}
+
+void LLMimeParser::Impl::scanPastContent(
+ std::istream& istr,
+ S32 limit,
+ LLSD headers,
+ const std::string separator)
+{
+ if(headers.has(CONTENT_LENGTH))
+ {
+ S32 content_length = headers[CONTENT_LENGTH].asInteger();
+ // Subtract 2 here for the \r\n after the content.
+ S32 max_skip = llmin(content_length, limit - mScanCount - 2);
+ istr.ignore(max_skip);
+ mScanCount += max_skip;
+
+ // *NOTE: Check for hitting the limit and eof here before
+ // checking for the trailing EOF, because our mime parser has
+ // to gracefully handle incomplete mime entites.
+ if((mScanCount >= limit) || istr.eof())
+ {
+ mContinue = false;
+ }
+ else if(!eatCRLF(istr))
+ {
+ mError = true;
+ return;
+ }
+ }
+}
+
+bool LLMimeParser::Impl::eatCRLF(std::istream& istr)
+{
+ int c = istr.get();
+ ++mScanCount;
+ if(c != '\r')
+ {
+ return false;
+ }
+ c = istr.get();
+ ++mScanCount;
+ if(c != '\n')
+ {
+ return false;
+ }
+ return true;
+}
+
+
+LLMimeParser::LLMimeParser() : mImpl(* new LLMimeParser::Impl)
+{
+}
+
+LLMimeParser::~LLMimeParser()
+{
+ delete & mImpl;
+}
+
+void LLMimeParser::reset()
+{
+ mImpl.reset();
+}
+
+bool LLMimeParser::parseIndex(std::istream& istr, LLMimeIndex& index)
+{
+ std::string separator;
+ return mImpl.parseIndex(istr, S32_MAX, separator, false, index);
+}
+
+bool LLMimeParser::parseIndex(
+ const std::vector<U8>& buffer,
+ LLMimeIndex& index)
+{
+ LLMemoryStream mstr(&buffer[0], buffer.size());
+ return parseIndex(mstr, buffer.size() + 1, index);
+}
+
+bool LLMimeParser::parseIndex(
+ std::istream& istr,
+ S32 limit,
+ LLMimeIndex& index)
+{
+ std::string separator;
+ return mImpl.parseIndex(istr, limit, separator, false, index);
+}
+
+bool LLMimeParser::parseIndex(const U8* buffer, S32 length, LLMimeIndex& index)
+{
+ LLMemoryStream mstr(buffer, length);
+ return parseIndex(mstr, length + 1, index);
+}
+
+/*
+bool LLMimeParser::verify(std::istream& isr, LLMimeIndex& index) const
+{
+ return false;
+}
+
+bool LLMimeParser::verify(U8* buffer, S32 length, LLMimeIndex& index) const
+{
+ LLMemoryStream mstr(buffer, length);
+ return verify(mstr, index);
+}
+*/
diff --git a/indra/llmessage/llmime.h b/indra/llmessage/llmime.h
new file mode 100644
index 0000000000..62e1204b88
--- /dev/null
+++ b/indra/llmessage/llmime.h
@@ -0,0 +1,274 @@
+/**
+ * @file llmime.h
+ * @author Phoenix
+ * @date 2006-12-20
+ * @brief Declaration of mime tools.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMIME_H
+#define LL_LLMIME_H
+
+#include <string>
+#include "llsd.h"
+
+/**
+ * This file declares various tools for parsing and creating MIME
+ * objects as described in RFCs 2045, 2046, 2047, 2048, and 2049.
+ */
+
+/**
+ * @class LLMimeIndex
+ * @brief Skeletal information useful for handling mime packages.
+ * @see LLMimeParser
+ *
+ * An instance of this class is the parsed output from a LLMimeParser
+ * which then allows for easy access into a data stream to find and
+ * get what you want out of it.
+ *
+ * This class meant as a tool to quickly find what you seek in a
+ * parsed mime entity. As such, it does not have useful support for
+ * modification of a mime entity and specializes the interface toward
+ * querying data from a fixed mime entity. Modifying an instance of
+ * LLMimeIndx does not alter a mime entity and changes to a mime
+ * entity itself are not propogated into an instance of a LLMimeIndex.
+ *
+ * Usage:<br>
+ * LLMimeIndex mime_index;<br>
+ * std::ifstream fstr("package.mime", ios::binary);<br>
+ * LLMimeParser parser;<br>
+ * if(parser.parseIndex(fstr, mime_index))<br>
+ * {<br>
+ * std::vector<U8> content;<br>
+ * content.resize(mime_index.contentLength());<br>
+ * fstr.seekg(mime_index.offset(), ios::beg);<br>
+ * // ...do work on fstr and content<br>
+ * }<br>
+ */
+class LLMimeIndex
+{
+public:
+ /* @name Client interface.
+ */
+ //@{
+ /**
+ * @brief Get the full parsed headers for this.
+ *
+ * If there are any headers, it will be a map of header name to
+ * the value found on the line. The name is everything before the
+ * colon, and the value is the string found after the colon to the
+ * end of the line after trimming leading whitespace. So, for
+ * example:
+ * Content-Type: text/plain
+ * would become an entry in the headers of:
+ * headers["Content-Type"] == "text/plain"
+ *
+ * If this instance of an index was generated by the
+ * LLMimeParser::parseIndex() call, all header names in rfc2045
+ * will be capitalized as in rfc, eg Content-Length and
+ * MIME-Version, not content-length and mime-version.
+ * @return Returns an LLSD map of header name to value. Returns
+ * undef if there are no headers.
+ */
+ LLSD headers() const;
+
+ /**
+ * @brief Get the content offset.
+ *
+ * @return Returns the number of bytes to the start of the data
+ * segment from the start of serialized mime entity. Returns -1 if
+ * offset is not known.
+ */
+ S32 offset() const;
+
+ /**
+ * @brief Get the length of the data segment for this mime part.
+ *
+ * @return Returns the content length in bytes. Returns -1 if
+ * length is not known.
+ */
+ S32 contentLength() const;
+
+ /**
+ * @brief Get the mime type associated with this node.
+ *
+ * @return Returns the mimetype.
+ */
+ std::string contentType() const;
+
+ /**
+ * @brief Helper method which simplifies parsing the return from type()
+ *
+ * @return Returns true if this is a multipart mime, and therefore
+ * getting subparts will succeed.
+ */
+ bool isMultipart() const;
+
+ /**
+ * @brief Get the number of atachments.
+ *
+ * @return Returns the number of sub-parts for this.
+ */
+ S32 subPartCount() const;
+
+ /**
+ * @brief Get the indicated attachment.
+ *
+ * @param index Value from 0 to (subPartCount() - 1).
+ * @return Returns the indicated sub-part, or an invalid mime
+ * index on failure.
+ */
+ LLMimeIndex subPart(S32 index) const;
+ //@}
+
+ /* @name Interface for building, testing, and helpers for typical use.
+ */
+ //@{
+ /**
+ * @brief Default constructor - creates a useless LLMimeIndex.
+ */
+ LLMimeIndex();
+
+ /**
+ * @brief Full constructor.
+ *
+ * @param headers The complete headers.
+ * @param content_offset The number of bytes to the start of the
+ * data segment of this mime entity from the start of the stream
+ * or buffer.
+ */
+ LLMimeIndex(LLSD headers, S32 content_offset);
+
+ /**
+ * @brief Copy constructor.
+ *
+ * @param mime The other mime object.
+ */
+ LLMimeIndex(const LLMimeIndex& mime);
+
+ // @brief Destructor.
+ ~LLMimeIndex();
+
+ /*
+ * @breif Assignment operator.
+ *
+ * @param mime The other mime object.
+ * @return Returns this after assignment.
+ */
+ LLMimeIndex& operator=(const LLMimeIndex& mime);
+
+ /**
+ * @brief Add attachment information as a sub-part to a multipart mime.
+ *
+ * @param sub_part the part to attach.
+ * @return Returns true on success, false on failure.
+ */
+ bool attachSubPart(LLMimeIndex sub_part);
+ //@}
+
+protected:
+ // Implementation.
+ class Impl;
+ Impl* mImpl;
+};
+
+
+/**
+ * @class LLMimeParser
+ * @brief This class implements a MIME parser and verifier.
+ *
+ * THOROUGH_DESCRIPTION
+ */
+class LLMimeParser
+{
+public:
+ // @brief Make a new mime parser.
+ LLMimeParser();
+
+ // @brief Mime parser Destructor.
+ ~LLMimeParser();
+
+ // @brief Reset internal state of this parser.
+ void reset();
+
+
+ /* @name Index generation interface.
+ */
+ //@{
+ /**
+ * @brief Parse a stream to find the mime index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read or EOF. The istr will be modified by this
+ * parsing, so pass in a temporary stream or rewind/reset the
+ * stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(std::istream& istr, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a vector to find the mime index information.
+ *
+ * @param buffer A vector with data to parse.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(const std::vector<U8>& buffer, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a stream to find the mime index information.
+ *
+ * This method will scan the istr until a single complete mime
+ * entity is read, an EOF, or limit bytes have been scanned. The
+ * istr will be modified by this parsing, so pass in a temporary
+ * stream or rewind/reset the stream after this call.
+ * @param istr An istream which contains a mime entity.
+ * @param limit The maximum number of bytes to scan.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(std::istream& istr, S32 limit, LLMimeIndex& index);
+
+ /**
+ * @brief Parse a memory bufffer to find the mime index information.
+ *
+ * @param buffer The start of the buffer to parse.
+ * @param buffer_length The length of the buffer.
+ * @param index[out] The parsed output.
+ * @return Returns true if an index was parsed and no errors occurred.
+ */
+ bool parseIndex(const U8* buffer, S32 buffer_length, LLMimeIndex& index);
+ //@}
+
+ /**
+ * @brief
+ *
+ * @return
+ */
+ //bool verify(std::istream& istr, LLMimeIndex& index) const;
+
+ /**
+ * @brief
+ *
+ * @return
+ */
+ //bool verify(U8* buffer, S32 buffer_length, LLMimeIndex& index) const;
+
+protected:
+ // Implementation.
+ class Impl;
+ Impl& mImpl;
+
+private:
+ // @brief Not implemneted to prevent copy consturction.
+ LLMimeParser(const LLMimeParser& parser);
+
+ // @brief Not implemneted to prevent assignment.
+ LLMimeParser& operator=(const LLMimeParser& mime);
+};
+
+#endif // LL_LLMIME_H
diff --git a/indra/llmessage/llnamevalue.cpp b/indra/llmessage/llnamevalue.cpp
new file mode 100644
index 0000000000..02ddec1bf5
--- /dev/null
+++ b/indra/llmessage/llnamevalue.cpp
@@ -0,0 +1,2141 @@
+/**
+ * @file llnamevalue.cpp
+ * @brief class for defining name value pairs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Examples:
+// AvatarCharacter STRING RW DSV male1
+
+#include "linden_common.h"
+
+#include <map>
+
+#include "llnamevalue.h"
+#include "u64.h"
+#include "llstring.h"
+#include "llcamera.h"
+
+// Anonymous enumeration to provide constants in this file.
+// *NOTE: These values may be used in sscanf statements below as their
+// value-1, so search for '2047' if you cange NV_BUFFER_LEN or '63' if
+// you change U64_BUFFER_LEN.
+enum
+{
+ NV_BUFFER_LEN = 2048,
+ U64_BUFFER_LEN = 64
+};
+
+struct user_callback_t
+{
+ user_callback_t() {};
+ user_callback_t(TNameValueCallback cb, void** data) : m_Callback(cb), m_Data(data) {}
+ TNameValueCallback m_Callback;
+ void ** m_Data;
+};
+typedef std::map<char *, user_callback_t> user_callback_map_t;
+user_callback_map_t gUserCallbackMap;
+
+LLStringTable gNVNameTable(16384);
+
+char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH] =
+{
+ "NULL",
+ "STRING",
+ "F32",
+ "S32",
+ "VEC3",
+ "U32",
+ "CAMERA", // Deprecated, but leaving in case removing completely would cause problems
+ "ASSET",
+ "U64"
+}; /*Flawfinder: Ignore*/
+
+char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH] =
+{
+ "NULL",
+ "R", // read only
+ "RW", // read write
+ "CB" // callback
+}; /*Flawfinder: Ignore*/
+
+char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH] =
+{
+ "NULL",
+ "S", // "Sim", formerly SIM
+ "DS", // "Data Sim" formerly SIM_SPACE
+ "SV", // "Sim Viewer" formerly SIM_VIEWER
+ "DSV" // "Data Sim Viewer", formerly SIM_SPACE_VIEWER
+}; /*Flawfinder: Ignore*/
+
+
+void add_use_callback(char *name, TNameValueCallback ucb, void **user_data)
+{
+ char *temp = gNVNameTable.addString(name);
+ gUserCallbackMap[temp] = user_callback_t(ucb,user_data);
+}
+
+
+//
+// Class
+//
+
+LLNameValue::LLNameValue()
+{
+ baseInit();
+}
+
+void LLNameValue::baseInit()
+{
+ mNVNameTable = &gNVNameTable;
+
+ mName = NULL;
+ mNameValueReference.string = NULL;
+
+ mType = NVT_NULL;
+ mStringType = NameValueTypeStrings[NVT_NULL];
+
+ mClass = NVC_NULL;
+ mStringClass = NameValueClassStrings[NVC_NULL];
+
+ mSendto = NVS_NULL;
+ mStringSendto = NameValueSendtoStrings[NVS_NULL];
+}
+
+void LLNameValue::init(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto, TNameValueCallback nvcb, void **user_data)
+{
+ mNVNameTable = &gNVNameTable;
+
+ mName = mNVNameTable->addString(name);
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringType = mNVNameTable->addString(type);
+ if (!strcmp(mStringType, "STRING"))
+ {
+ S32 string_length = (S32)strlen(data); /*Flawfinder: Ignore*/
+ mType = NVT_STRING;
+
+ delete[] mNameValueReference.string;
+
+ // two options here. . . data can either look like foo or "foo"
+ // WRONG! - this is a poorly implemented and incomplete escape
+ // mechanism. For example, using this scheme, there is no way
+ // to tell an intentional double quotes from a zero length
+ // string. This needs to excised. Phoenix
+ //if (strchr(data, '\"'))
+ //{
+ // string_length -= 2;
+ // mNameValueReference.string = new char[string_length + 1];;
+ // strncpy(mNameValueReference.string, data + 1, string_length);
+ //}
+ //else
+ //{
+ mNameValueReference.string = new char[string_length + 1];;
+ strncpy(mNameValueReference.string, data, string_length); /*Flawfinder: Ignore*/
+ //}
+ mNameValueReference.string[string_length] = 0;
+ }
+ else if (!strcmp(mStringType, "F32"))
+ {
+ mType = NVT_F32;
+ mNameValueReference.f32 = new F32((F32)atof(data));
+ }
+ else if (!strcmp(mStringType, "S32"))
+ {
+ mType = NVT_S32;
+ mNameValueReference.s32 = new S32(atoi(data));
+ }
+ else if (!strcmp(mStringType, "U64"))
+ {
+ mType = NVT_U64;
+ mNameValueReference.u64 = new U64(str_to_U64(data));
+ }
+ else if (!strcmp(mStringType, "VEC3"))
+ {
+ mType = NVT_VEC3;
+ F32 t1, t2, t3;
+
+ // two options here. . . data can either look like 0, 1, 2 or <0, 1, 2>
+
+ if (strchr(data, '<'))
+ {
+ sscanf(data, "<%f, %f, %f>", &t1, &t2, &t3);
+ }
+ else
+ {
+ sscanf(data, "%f, %f, %f", &t1, &t2, &t3);
+ }
+
+ // finite checks
+ if (!llfinite(t1) || !llfinite(t2) || !llfinite(t3))
+ {
+ t1 = 0.f;
+ t2 = 0.f;
+ t3 = 0.f;
+ }
+
+ mNameValueReference.vec3 = new LLVector3(t1, t2, t3);
+ }
+ else if (!strcmp(mStringType, "U32"))
+ {
+ mType = NVT_U32;
+ mNameValueReference.u32 = new U32(atoi(data));
+ }
+ else if(!strcmp(mStringType, (const char*)NameValueTypeStrings[NVT_ASSET]))
+ {
+ // assets are treated like strings, except that the name has
+ // meaning to an LLAssetInfo object
+ S32 string_length = (S32)strlen(data); /*Flawfinder: Ignore*/
+ mType = NVT_ASSET;
+
+ // two options here. . . data can either look like foo or "foo"
+ // WRONG! - this is a poorly implemented and incomplete escape
+ // mechanism. For example, using this scheme, there is no way
+ // to tell an intentional double quotes from a zero length
+ // string. This needs to excised. Phoenix
+ //if (strchr(data, '\"'))
+ //{
+ // string_length -= 2;
+ // mNameValueReference.string = new char[string_length + 1];;
+ // strncpy(mNameValueReference.string, data + 1, string_length);
+ //}
+ //else
+ //{
+ mNameValueReference.string = new char[string_length + 1];;
+ strncpy(mNameValueReference.string, data, string_length); /*Flawfinder: Ignore*/
+ //}
+ mNameValueReference.string[string_length] = 0;
+ }
+ else
+ {
+ llwarns << "Unknown name value type string " << mStringType << " for " << mName << llendl;
+ mType = NVT_NULL;
+ }
+
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ if (!strcmp(nvclass, "R") ||
+ !strcmp(nvclass, "READ_ONLY")) // legacy
+ {
+ mClass = NVC_READ_ONLY;
+ mStringClass = mNVNameTable->addString("R");
+ }
+ else if (!strcmp(nvclass, "RW") ||
+ !strcmp(nvclass, "READ_WRITE")) // legacy
+ {
+ mClass = NVC_READ_WRITE;
+ mStringClass = mNVNameTable->addString("RW");
+ }
+ else if (!strcmp(nvclass, "CB") ||
+ !strcmp(nvclass, "CALLBACK")) // legacy
+ {
+ mClass = NVC_CALLBACK;
+ mStringClass = mNVNameTable->addString("CB");
+ mNameValueCB = nvcb;
+ mUserData = user_data;
+ }
+ else
+ {
+ // assume it's bad
+ mClass = NVC_NULL;
+ mStringClass = mNVNameTable->addString(nvclass);
+ mNameValueCB = NULL;
+ mUserData = NULL;
+
+ // are we a user-defined call back?
+ for (user_callback_map_t::iterator iter = gUserCallbackMap.begin();
+ iter != gUserCallbackMap.end(); iter++)
+ {
+ char* tname = iter->first;
+ if (tname == mStringClass)
+ {
+ mClass = NVC_CALLBACK;
+ mNameValueCB = (iter->second).m_Callback;
+ mUserData = (iter->second).m_Data;
+ }
+ }
+
+ // Warn if we didn't find a callback
+ if (mClass == NVC_NULL)
+ {
+ llwarns << "Unknown user callback in name value init() for " << mName << llendl;
+ }
+ }
+
+ // Initialize the sendto variable
+ if (!strcmp(nvsendto, "S") ||
+ !strcmp(nvsendto, "SIM")) // legacy
+ {
+ mSendto = NVS_SIM;
+ mStringSendto = mNVNameTable->addString("S");
+ }
+ else if (!strcmp(nvsendto, "DS") ||
+ !strcmp(nvsendto, "SIM_SPACE")) // legacy
+ {
+ mSendto = NVS_DATA_SIM;
+ mStringSendto = mNVNameTable->addString("DS");
+ }
+ else if (!strcmp(nvsendto, "SV") ||
+ !strcmp(nvsendto, "SIM_VIEWER")) // legacy
+ {
+ mSendto = NVS_SIM_VIEWER;
+ mStringSendto = mNVNameTable->addString("SV");
+ }
+ else if (!strcmp(nvsendto, "DSV") ||
+ !strcmp(nvsendto, "SIM_SPACE_VIEWER")) // legacy
+ {
+ mSendto = NVS_DATA_SIM_VIEWER;
+ mStringSendto = mNVNameTable->addString("DSV");
+ }
+ else
+ {
+ llwarns << "LLNameValue::init() - unknown sendto field "
+ << nvsendto << " for NV " << mName << llendl;
+ mSendto = NVS_NULL;
+ mStringSendto = mNVNameTable->addString("S");
+ }
+
+}
+
+
+LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ // if not specified, send to simulator only
+ init(name, data, type, nvclass, "SIM", nvcb, user_data);
+}
+
+
+LLNameValue::LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ init(name, data, type, nvclass, nvsendto, nvcb, user_data);
+}
+
+
+
+// Initialize without any initial data.
+LLNameValue::LLNameValue(const char *name, const char *type, const char *nvclass, TNameValueCallback nvcb, void **user_data)
+{
+ baseInit();
+ mName = mNVNameTable->addString(name);
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringType = mNVNameTable->addString(type);
+ if (!strcmp(mStringType, "STRING"))
+ {
+ mType = NVT_STRING;
+ mNameValueReference.string = NULL;
+ }
+ else if (!strcmp(mStringType, "F32"))
+ {
+ mType = NVT_F32;
+ mNameValueReference.f32 = NULL;
+ }
+ else if (!strcmp(mStringType, "S32"))
+ {
+ mType = NVT_S32;
+ mNameValueReference.s32 = NULL;
+ }
+ else if (!strcmp(mStringType, "VEC3"))
+ {
+ mType = NVT_VEC3;
+ mNameValueReference.vec3 = NULL;
+ }
+ else if (!strcmp(mStringType, "U32"))
+ {
+ mType = NVT_U32;
+ mNameValueReference.u32 = NULL;
+ }
+ else if (!strcmp(mStringType, "U64"))
+ {
+ mType = NVT_U64;
+ mNameValueReference.u64 = NULL;
+ }
+ else if(!strcmp(mStringType, (const char*)NameValueTypeStrings[NVT_ASSET]))
+ {
+ mType = NVT_ASSET;
+ mNameValueReference.string = NULL;
+ }
+ else
+ {
+ mType = NVT_NULL;
+ llinfos << "Unknown name-value type " << mStringType << llendl;
+ }
+
+ // Nota Bene: Whatever global structure manages this should have these in the name table already!
+ mStringClass = mNVNameTable->addString(nvclass);
+ if (!strcmp(mStringClass, "READ_ONLY"))
+ {
+ mClass = NVC_READ_ONLY;
+ }
+ else if (!strcmp(mStringClass, "READ_WRITE"))
+ {
+ mClass = NVC_READ_WRITE;
+ }
+ else if (!strcmp(mStringClass, "CALLBACK"))
+ {
+ mClass = NVC_READ_WRITE;
+ mNameValueCB = nvcb;
+ mUserData = user_data;
+ }
+
+ // Initialize the sendto variable
+ mStringSendto = mNVNameTable->addString("SIM");
+ mSendto = NVS_SIM;
+}
+
+
+// data is in the format:
+// "NameValueName Type Class Data"
+LLNameValue::LLNameValue(const char *data)
+{
+ baseInit();
+ static char name[NV_BUFFER_LEN];
+ static char type[NV_BUFFER_LEN];
+ static char nvclass[NV_BUFFER_LEN];
+ static char nvsendto[NV_BUFFER_LEN];
+ static char nvdata[NV_BUFFER_LEN];
+
+ S32 i;
+
+ S32 character_count = 0;
+ S32 length = 0;
+
+ // go to first non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // read in the name
+ sscanf((data + character_count), "%2047s", name);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(name); /* Flawfinder: ignore */
+ name[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // read in the type
+ sscanf((data + character_count), "%2047s", type);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(type); /* Flawfinder: ignore */
+ type[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // do we have a type argument?
+ for (i = NVC_READ_ONLY; i < NVC_EOF; i++)
+ {
+ if (!strncmp(NameValueClassStrings[i], data + character_count, strlen(NameValueClassStrings[i]))) /* Flawfinder: ignore */
+ {
+ break;
+ }
+ }
+
+ if (i != NVC_EOF)
+ {
+ // yes we do!
+ // read in the class
+ sscanf((data + character_count), "%2047s", nvclass);
+
+ // bump past it and add null terminator
+ length = (S32)strlen(nvclass); /* Flawfinder: ignore */
+ nvclass[length] = 0;
+ character_count += length;
+
+ // go to the next non-whitespace character
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ // no type argument given, default to read-write
+ strncpy(nvclass, "READ_WRITE", sizeof(nvclass) -1); /* Flawfinder: ignore */
+ nvclass[sizeof(nvclass) -1] = '\0';
+ }
+
+ // Do we have a sendto argument?
+ for (i = NVS_SIM; i < NVS_EOF; i++)
+ {
+ if (!strncmp(NameValueSendtoStrings[i], data + character_count, strlen(NameValueSendtoStrings[i]))) /* Flawfinder: ignore */
+ {
+ break;
+ }
+ }
+
+ if (i != NVS_EOF)
+ {
+ // found a sendto argument
+ sscanf((data + character_count), "%2047s", nvsendto);
+
+ // add null terminator
+ length = (S32)strlen(nvsendto); /* Flawfinder: ignore */
+ nvsendto[length] = 0;
+ character_count += length;
+
+ // seek to next non-whitespace characer
+ while (1)
+ {
+ if ( (*(data + character_count) == ' ')
+ ||(*(data + character_count) == '\n')
+ ||(*(data + character_count) == '\t')
+ ||(*(data + character_count) == '\r'))
+ {
+ character_count++;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ // no sendto argument given, default to sim only
+ strncpy(nvsendto, "SIM", sizeof(nvsendto) -1); /* Flawfinder: ignore */
+ nvsendto[sizeof(nvsendto) -1] ='\0';
+ }
+
+
+ // copy the rest character by character into data
+ length = 0;
+
+ while ( (*(nvdata + length++) = *(data + character_count++)) )
+ ;
+
+ init(name, nvdata, type, nvclass, nvsendto);
+}
+
+
+LLNameValue::~LLNameValue()
+{
+ mNVNameTable->removeString(mName);
+ mName = NULL;
+
+ switch(mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ delete [] mNameValueReference.string;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_F32:
+ delete mNameValueReference.f32;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_S32:
+ delete mNameValueReference.s32;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_VEC3:
+ delete mNameValueReference.vec3;
+ mNameValueReference.string = NULL;
+ break;
+ case NVT_U32:
+ delete mNameValueReference.u32;
+ mNameValueReference.u32 = NULL;
+ break;
+ case NVT_U64:
+ delete mNameValueReference.u64;
+ mNameValueReference.u64 = NULL;
+ break;
+ default:
+ break;
+ }
+
+ delete[] mNameValueReference.string;
+ mNameValueReference.string = NULL;
+}
+
+char *LLNameValue::getString()
+{
+ if (mType == NVT_STRING)
+ {
+ return mNameValueReference.string;
+ }
+ else
+ {
+ llerrs << mName << " not a string!" << llendl;
+ return NULL;
+ }
+}
+
+const char *LLNameValue::getAsset() const
+{
+ if (mType == NVT_ASSET)
+ {
+ return mNameValueReference.string;
+ }
+ else
+ {
+ llerrs << mName << " not an asset!" << llendl;
+ return NULL;
+ }
+}
+
+F32 *LLNameValue::getF32()
+{
+ if (mType == NVT_F32)
+ {
+ return mNameValueReference.f32;
+ }
+ else
+ {
+ llerrs << mName << " not a F32!" << llendl;
+ return NULL;
+ }
+}
+
+S32 *LLNameValue::getS32()
+{
+ if (mType == NVT_S32)
+ {
+ return mNameValueReference.s32;
+ }
+ else
+ {
+ llerrs << mName << " not a S32!" << llendl;
+ return NULL;
+ }
+}
+
+U32 *LLNameValue::getU32()
+{
+ if (mType == NVT_U32)
+ {
+ return mNameValueReference.u32;
+ }
+ else
+ {
+ llerrs << mName << " not a U32!" << llendl;
+ return NULL;
+ }
+}
+
+U64 *LLNameValue::getU64()
+{
+ if (mType == NVT_U64)
+ {
+ return mNameValueReference.u64;
+ }
+ else
+ {
+ llerrs << mName << " not a U64!" << llendl;
+ return NULL;
+ }
+}
+
+void LLNameValue::getVec3(LLVector3 &vec)
+{
+ if (mType == NVT_VEC3)
+ {
+ vec = *mNameValueReference.vec3;
+ }
+ else
+ {
+ llerrs << mName << " not a Vec3!" << llendl;
+ }
+}
+
+LLVector3 *LLNameValue::getVec3()
+{
+ if (mType == NVT_VEC3)
+ {
+ return (mNameValueReference.vec3);
+ }
+ else
+ {
+ llerrs << mName << " not a Vec3!" << llendl;
+ return NULL;
+ }
+}
+
+
+F32 LLNameValue::magnitude()
+{
+ switch(mType)
+ {
+ case NVT_STRING:
+ return (F32)(strlen(mNameValueReference.string)); /* Flawfinder: ignore */
+ break;
+ case NVT_F32:
+ return (fabsf(*mNameValueReference.f32));
+ break;
+ case NVT_S32:
+ return (fabsf((F32)(*mNameValueReference.s32)));
+ break;
+ case NVT_VEC3:
+ return (mNameValueReference.vec3->magVec());
+ break;
+ case NVT_U32:
+ return (F32)(*mNameValueReference.u32);
+ break;
+ default:
+ llerrs << "No magnitude operation for NV type " << mStringType << llendl;
+ break;
+ }
+ return 0.f;
+}
+
+
+void LLNameValue::callCallback()
+{
+ if (mNameValueCB)
+ {
+ (*mNameValueCB)(this, mUserData);
+ }
+ else
+ {
+ llinfos << mName << " has no callback!" << llendl;
+ }
+}
+
+
+BOOL LLNameValue::sendToData() const
+{
+ return (mSendto == NVS_DATA_SIM || mSendto == NVS_DATA_SIM_VIEWER);
+}
+
+
+BOOL LLNameValue::sendToViewer() const
+{
+ return (mSendto == NVS_SIM_VIEWER || mSendto == NVS_DATA_SIM_VIEWER);
+}
+
+
+LLNameValue &LLNameValue::operator=(const LLNameValue &a)
+{
+ if (mType != a.mType)
+ {
+ return *this;
+ }
+ if (mClass == NVC_READ_ONLY)
+ return *this;
+
+ BOOL b_changed = FALSE;
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this != a))
+ {
+ b_changed = TRUE;
+ }
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [strlen(a.mNameValueReference.string) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a.mNameValueReference.string); /* Flawfinder: ignore */
+ }
+ break;
+ case NVT_F32:
+ *mNameValueReference.f32 = *a.mNameValueReference.f32;
+ break;
+ case NVT_S32:
+ *mNameValueReference.s32 = *a.mNameValueReference.s32;
+ break;
+ case NVT_VEC3:
+ *mNameValueReference.vec3 = *a.mNameValueReference.vec3;
+ break;
+ case NVT_U32:
+ *mNameValueReference.u32 = *a.mNameValueReference.u32;
+ break;
+ case NVT_U64:
+ *mNameValueReference.u64 = *a.mNameValueReference.u64;
+ break;
+ default:
+ llerrs << "Unknown Name value type " << (U32)a.mType << llendl;
+ break;
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return *this;
+}
+
+void LLNameValue::setString(const char *a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_STRING:
+ if (a)
+ {
+ if ( (mClass == NVC_CALLBACK)
+ &&(strcmp(this->mNameValueReference.string,a)))
+ {
+ b_changed = TRUE;
+ }
+
+ if (mNameValueReference.string)
+ {
+ delete [] mNameValueReference.string;
+ }
+
+ mNameValueReference.string = new char [strlen(a) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a); /* Flawfinder: ignore */
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+ }
+ else
+ {
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [1];
+ mNameValueReference.string[0] = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setAsset(const char *a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_ASSET:
+ if (a)
+ {
+ if ( (mClass == NVC_CALLBACK)
+ &&(strcmp(this->mNameValueReference.string,a)))
+ {
+ b_changed = TRUE;
+ }
+
+ if (mNameValueReference.string)
+ {
+ delete [] mNameValueReference.string;
+ }
+ mNameValueReference.string = new char [strlen(a) + 1]; /* Flawfinder: ignore */
+ if(mNameValueReference.string != NULL)
+ {
+ strcpy(mNameValueReference.string, a); /* Flawfinder: ignore */
+ }
+
+ if (b_changed)
+ {
+ callCallback();
+ }
+ }
+ else
+ {
+ if (mNameValueReference.string)
+ delete [] mNameValueReference.string;
+
+ mNameValueReference.string = new char [1];
+ mNameValueReference.string[0] = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+}
+
+
+void LLNameValue::setF32(const F32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setS32(const S32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_S32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.s32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.s32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_U32:
+ if ( (mClass == NVC_CALLBACK)
+ && ((S32) (*this->mNameValueReference.u32) != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.u32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = (F32)a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ break;
+ }
+ if (b_changed)
+ {
+ callCallback();
+ }
+
+ return;
+}
+
+
+void LLNameValue::setU32(const U32 a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_S32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.s32 != (S32) a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.s32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_U32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.u32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.u32 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ case NVT_F32:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.f32 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.f32 = (F32)a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ llerrs << "NameValue: Trying to set U32 into a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return;
+}
+
+
+void LLNameValue::setVec3(const LLVector3 &a)
+{
+ if (mClass == NVC_READ_ONLY)
+ return;
+ BOOL b_changed = FALSE;
+
+ switch(mType)
+ {
+ case NVT_VEC3:
+ if ( (mClass == NVC_CALLBACK)
+ &&(*this->mNameValueReference.vec3 != a))
+ {
+ b_changed = TRUE;
+ }
+ *mNameValueReference.vec3 = a;
+ if (b_changed)
+ {
+ callCallback();
+ }
+ break;
+ default:
+ llerrs << "NameValue: Trying to set LLVector3 into a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return;
+}
+
+
+BOOL LLNameValue::nonzero()
+{
+ switch(mType)
+ {
+ case NVT_STRING:
+ if (!mNameValueReference.string)
+ return 0;
+ return (mNameValueReference.string[0] != 0);
+ case NVT_F32:
+ return (*mNameValueReference.f32 != 0.f);
+ case NVT_S32:
+ return (*mNameValueReference.s32 != 0);
+ case NVT_U32:
+ return (*mNameValueReference.u32 != 0);
+ case NVT_VEC3:
+ return (mNameValueReference.vec3->magVecSquared() != 0.f);
+ default:
+ llerrs << "NameValue: Trying to call nonzero on a " << mStringType << ", unknown conversion" << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+std::string LLNameValue::printNameValue()
+{
+ std::string buffer;
+ buffer = llformat("%s %s %s %s ", mName, mStringType, mStringClass, mStringSendto);
+ buffer += printData();
+// llinfos << "Name Value Length: " << buffer.size() + 1 << llendl;
+ return buffer;
+}
+
+std::string LLNameValue::printData()
+{
+ std::string buffer;
+ switch(mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ buffer = mNameValueReference.string;
+ break;
+ case NVT_F32:
+ buffer = llformat("%f", *mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ buffer = llformat("%d", *mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ buffer = llformat("%u", *mNameValueReference.u32);
+ break;
+ case NVT_U64:
+ {
+ char u64_string[U64_BUFFER_LEN]; /* Flawfinder: ignore */
+ U64_to_str(*mNameValueReference.u64, u64_string, sizeof(u64_string));
+ buffer = u64_string;
+ }
+ break;
+ case NVT_VEC3:
+ buffer = llformat( "%f, %f, %f", mNameValueReference.vec3->mV[VX], mNameValueReference.vec3->mV[VY], mNameValueReference.vec3->mV[VZ]);
+ break;
+ default:
+ llerrs << "Trying to print unknown NameValue type " << mStringType << llendl;
+ break;
+ }
+ return buffer;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLNameValue &a)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ case NVT_ASSET:
+ s << a.mNameValueReference.string;
+ break;
+ case NVT_F32:
+ s << (*a.mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ s << *(a.mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ s << *(a.mNameValueReference.u32);
+ break;
+ case NVT_U64:
+ {
+ char u64_string[U64_BUFFER_LEN]; /* Flawfinder: ignore */
+ U64_to_str(*a.mNameValueReference.u64, u64_string, sizeof(u64_string));
+ s << u64_string;
+ }
+ case NVT_VEC3:
+ s << *(a.mNameValueReference.vec3);
+ break;
+ default:
+ llerrs << "Trying to print unknown NameValue type " << a.mStringType << llendl;
+ break;
+ }
+ return s;
+}
+
+
+// nota bene: return values aren't static for now to prevent memory leaks
+
+LLNameValue &operator+(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+
+ S32 length1 = (S32)strlen(a.mNameValueReference.string); /* Flawfinder: Ignore */
+ S32 length2 = (S32)strlen(b.mNameValueReference.string); /* Flawfinder: Ignore */
+ delete [] retval.mNameValueReference.string;
+ retval.mNameValueReference.string = new char[length1 + length2 + 1];
+ if(retval.mNameValueReference.string != NULL)
+ {
+ strcpy(retval.mNameValueReference.string, a.mNameValueReference.string); /* Flawfinder: Ignore */
+ strcat(retval.mNameValueReference.string, b.mNameValueReference.string); /* Flawfinder: Ignore */
+ }
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 + *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 + *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 + *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 + *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown add of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator-(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 - *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 - *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 - *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 - *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown subtract of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator*(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 * *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 * *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32((*a.mNameValueReference.vec3) * (*b.mNameValueReference.vec3));
+ }
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator/(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 / *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 / *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 / *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 / *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 / *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown divide of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+LLNameValue &operator%(const LLNameValue &a, const LLNameValue &b)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 % *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.s32 % *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_S32)
+ {
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(*a.mNameValueReference.u32 % *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ retval.mType = NVT_U32;
+ retval.mStringType = NameValueTypeStrings[NVT_U32];
+ delete retval.mNameValueReference.u32;
+ retval.mNameValueReference.u32 = new U32(*a.mNameValueReference.u32 % *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 % *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown % of NV type " << a.mStringType << " to " << b.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+// Multiplying anything times a float gives you some floats
+LLNameValue &operator*(const LLNameValue &a, F32 k)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * k);
+ break;
+ case NVT_S32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * k);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * k);
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 * k);
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " with F32" << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+LLNameValue &operator*(F32 k, const LLNameValue &a)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.f32 * k);
+ break;
+ case NVT_S32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.s32 * k);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_F32;
+ retval.mStringType = NameValueTypeStrings[NVT_F32];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(*a.mNameValueReference.u32 * k);
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(*a.mNameValueReference.vec3 * k);
+ break;
+ default:
+ llerrs << "Unknown multiply of NV type " << a.mStringType << " with F32" << llendl;
+ break;
+ }
+ return retval;
+}
+
+
+bool operator==(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ if (!a.mNameValueReference.string)
+ return FALSE;
+ if (!b.mNameValueReference.string)
+ return FALSE;
+ return (!strcmp(a.mNameValueReference.string, b.mNameValueReference.string));
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 == *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 == (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 == *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 == *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 == *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ return (*a.mNameValueReference.vec3 == *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown == NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+bool operator<=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if (b.mType == NVT_STRING)
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval <= 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 <= *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 <= (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 <= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 <= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 <= *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown <= NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator>=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval >= 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 >= *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 >= (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 >= *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 >= *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 >= *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown >= NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator<(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval < 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 < *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 < (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 < *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 < *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 < *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown < NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+bool operator>(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ S32 retval = strcmp(a.mNameValueReference.string, b.mNameValueReference.string);
+ return (retval > 0);
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 > *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 > (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 > *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 > *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 > *b.mNameValueReference.u32);
+ }
+ break;
+ default:
+ llerrs << "Unknown > NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+bool operator!=(const LLNameValue &a, const LLNameValue &b)
+{
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_STRING))
+ {
+ return (strcmp(a.mNameValueReference.string, b.mNameValueReference.string)) ? true : false;
+ }
+ break;
+ case NVT_F32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.f32 != *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_S32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.s32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return (*a.mNameValueReference.s32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.s32 != (S32) *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_U32:
+ if (b.mType == NVT_F32)
+ {
+ return (*a.mNameValueReference.u32 != *b.mNameValueReference.f32);
+ }
+ else if (b.mType == NVT_S32)
+ {
+ return ((S32) *a.mNameValueReference.u32 != *b.mNameValueReference.s32);
+ }
+ else if (b.mType == NVT_U32)
+ {
+ return (*a.mNameValueReference.u32 != *b.mNameValueReference.u32);
+ }
+ break;
+ case NVT_VEC3:
+ if ( (a.mType == b.mType)
+ &&(a.mType == NVT_VEC3))
+ {
+ return (*a.mNameValueReference.vec3 != *b.mNameValueReference.vec3);
+ }
+ break;
+ default:
+ llerrs << "Unknown != NV type " << a.mStringType << " with " << b.mStringType << llendl;
+ break;
+ }
+ return FALSE;
+}
+
+
+LLNameValue &operator-(const LLNameValue &a)
+{
+ static LLNameValue retval;
+
+ switch(a.mType)
+ {
+ case NVT_STRING:
+ break;
+ case NVT_F32:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.f32;
+ retval.mNameValueReference.f32 = new F32(-*a.mNameValueReference.f32);
+ break;
+ case NVT_S32:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.s32;
+ retval.mNameValueReference.s32 = new S32(-*a.mNameValueReference.s32);
+ break;
+ case NVT_U32:
+ retval.mType = NVT_S32;
+ retval.mStringType = NameValueTypeStrings[NVT_S32];
+ delete retval.mNameValueReference.s32;
+ // Can't do unary minus on U32, doesn't work.
+ retval.mNameValueReference.s32 = new S32(-S32(*a.mNameValueReference.u32));
+ break;
+ case NVT_VEC3:
+ retval.mType = a.mType;
+ retval.mStringType = NameValueTypeStrings[a.mType];
+ delete retval.mNameValueReference.vec3;
+ retval.mNameValueReference.vec3 = new LLVector3(-*a.mNameValueReference.vec3);
+ break;
+ default:
+ llerrs << "Unknown - NV type " << a.mStringType << llendl;
+ break;
+ }
+ return retval;
+}
diff --git a/indra/llmessage/llnamevalue.h b/indra/llmessage/llnamevalue.h
new file mode 100644
index 0000000000..27355277ca
--- /dev/null
+++ b/indra/llmessage/llnamevalue.h
@@ -0,0 +1,186 @@
+/**
+ * @file llnamevalue.h
+ * @brief class for defining name value pairs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEVALUE_H
+#define LL_LLNAMEVALUE_H
+
+#include <iostream>
+#include <string.h>
+
+#include "string_table.h"
+#include "llskipmap.h"
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "lldbstrings.h"
+
+class LLNameValue;
+typedef void (*TNameValueCallback)(LLNameValue *changed, void **user_data);
+
+void add_use_callback(char *name, TNameValueCallback ucb, void **user_data);
+
+typedef enum e_name_value_types
+{
+ NVT_NULL,
+ NVT_STRING,
+ NVT_F32,
+ NVT_S32,
+ NVT_VEC3,
+ NVT_U32,
+ NVT_CAMERA, // Deprecated, but leaving in case removing this will cause problems
+ NVT_ASSET,
+ NVT_U64,
+ NVT_EOF
+} ENameValueType;
+
+typedef enum e_name_value_class
+{
+ NVC_NULL,
+ NVC_READ_ONLY,
+ NVC_READ_WRITE,
+ NVC_CALLBACK,
+ NVC_EOF
+} ENameValueClass;
+
+typedef enum e_name_value_sento
+{
+ NVS_NULL,
+ NVS_SIM,
+ NVS_DATA_SIM,
+ NVS_SIM_VIEWER,
+ NVS_DATA_SIM_VIEWER,
+ NVS_EOF
+} ENameValueSendto;
+
+
+// NameValues can always be "printed" into a buffer of this length.
+const U32 NAME_VALUE_BUF_SIZE = 1024;
+
+
+const U32 NAME_VALUE_TYPE_STRING_LENGTH = 8;
+const U32 NAME_VALUE_CLASS_STRING_LENGTH = 16;
+const U32 NAME_VALUE_SENDTO_STRING_LENGTH = 18;
+const U32 NAME_VALUE_DATA_SIZE =
+ NAME_VALUE_BUF_SIZE -
+ ( DB_NV_NAME_BUF_SIZE +
+ NAME_VALUE_TYPE_STRING_LENGTH +
+ NAME_VALUE_CLASS_STRING_LENGTH +
+ NAME_VALUE_SENDTO_STRING_LENGTH );
+
+
+extern char NameValueTypeStrings[NVT_EOF][NAME_VALUE_TYPE_STRING_LENGTH]; /* Flawfinder: Ignore */
+extern char NameValueClassStrings[NVC_EOF][NAME_VALUE_CLASS_STRING_LENGTH]; /* Flawfinder: Ignore */
+extern char NameValueSendtoStrings[NVS_EOF][NAME_VALUE_SENDTO_STRING_LENGTH]; /* Flawfinder: Ignore */
+
+typedef union u_name_value_reference
+{
+ char *string;
+ F32 *f32;
+ S32 *s32;
+ LLVector3 *vec3;
+ U32 *u32;
+ U64 *u64;
+} UNameValueReference;
+
+
+class LLNameValue
+{
+public:
+ void baseInit();
+ void init(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+
+ LLNameValue();
+ LLNameValue(const char *data);
+ LLNameValue(const char *name, const char *type, const char *nvclass,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+ LLNameValue(const char *name, const char *data, const char *type, const char *nvclass,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+ LLNameValue(const char *name, const char *data, const char *type, const char *nvclass, const char *nvsendto,
+ TNameValueCallback nvcb = NULL, void **user_data = NULL);
+
+ ~LLNameValue();
+
+ char *getString();
+ const char *getAsset() const;
+ F32 *getF32();
+ S32 *getS32();
+ void getVec3(LLVector3 &vec);
+ LLVector3 *getVec3();
+ F32 magnitude();
+ U32 *getU32();
+ U64 *getU64();
+
+ const char *getType() const { return mStringType; }
+ const char *getClass() const { return mStringClass; }
+ const char *getSendto() const { return mStringSendto; }
+
+ BOOL sendToData() const;
+ BOOL sendToViewer() const;
+
+ void callCallback();
+ std::string printNameValue();
+ std::string printData();
+
+ ENameValueType getTypeEnum() const { return mType; }
+ ENameValueClass getClassEnum() const { return mClass; }
+ ENameValueSendto getSendtoEnum() const { return mSendto; }
+
+ LLNameValue &operator=(const LLNameValue &a);
+ void setString(const char *a);
+ void setAsset(const char *a);
+ void setF32(const F32 a);
+ void setS32(const S32 a);
+ void setVec3(const LLVector3 &a);
+ void setU32(const U32 a);
+
+ BOOL nonzero();
+
+ friend std::ostream& operator<<(std::ostream& s, const LLNameValue &a);
+
+ friend LLNameValue &operator+(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator-(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator*(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator/(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator%(const LLNameValue &a, const LLNameValue &b);
+ friend LLNameValue &operator*(const LLNameValue &a, F32 k);
+ friend LLNameValue &operator*(F32 k, const LLNameValue &a);
+
+ friend bool operator==(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator<=(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator>=(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator<(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator>(const LLNameValue &a, const LLNameValue &b);
+ friend bool operator!=(const LLNameValue &a, const LLNameValue &b);
+
+ friend LLNameValue &operator-(const LLNameValue &a);
+
+private:
+ void printNameValue(std::ostream& s);
+
+public:
+ char *mName;
+
+ char *mStringType;
+ ENameValueType mType;
+ char *mStringClass;
+ ENameValueClass mClass;
+ char *mStringSendto;
+ ENameValueSendto mSendto;
+
+ UNameValueReference mNameValueReference;
+ S32 mNumberEntries;
+ LLStringTable *mNVNameTable;
+ TNameValueCallback mNameValueCB;
+ void **mUserData;
+};
+
+extern LLStringTable gNVNameTable;
+
+
+#endif
diff --git a/indra/llmessage/llnullcipher.cpp b/indra/llmessage/llnullcipher.cpp
new file mode 100644
index 0000000000..53bb748415
--- /dev/null
+++ b/indra/llmessage/llnullcipher.cpp
@@ -0,0 +1,40 @@
+/**
+ * @file llnullcipher.cpp
+ * @brief Implementation of a cipher which does not encrypt.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcrypto.h"
+
+///----------------------------------------------------------------------------
+/// Class LLNullCipher
+///----------------------------------------------------------------------------
+
+BOOL LLNullCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if((src_len == dst_len) && src && dst)
+ {
+ memmove(dst, src, src_len);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLNullCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if((src_len == dst_len) && src && dst)
+ {
+ memmove(dst, src, src_len);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+U32 LLNullCipher::requiredEncryptionSpace(U32 len)
+{
+ return len;
+}
diff --git a/indra/llmessage/llpacketack.h b/indra/llmessage/llpacketack.h
new file mode 100644
index 0000000000..1b62dc9415
--- /dev/null
+++ b/indra/llmessage/llpacketack.h
@@ -0,0 +1,143 @@
+/**
+ * @file llpacketack.h
+ * @brief Reliable UDP helpers for the message system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETACK_H
+#define LL_LLPACKETACK_H
+
+#include <cstring>
+#include <stdio.h>
+
+#include "llerror.h"
+#include "lltimer.h"
+#include "llhost.h"
+
+//class LLPacketAck
+//{
+//public:
+// LLHost mHost;
+// TPACKETID mPacketID;
+//public:
+// LLPacketAck(const LLHost &host, TPACKETID packet_id)
+// {
+// mHost = host;
+// mPacketID = packet_id;
+// };
+// ~LLPacketAck(){};
+//};
+
+class LLReliablePacketParams
+{
+public:
+ LLHost mHost;
+ S32 mRetries;
+ BOOL mPingBasedRetry;
+ F32 mTimeout;
+ void (*mCallback)(void **,S32);
+ void **mCallbackData;
+ char *mMessageName;
+
+public:
+ LLReliablePacketParams()
+ {
+ mRetries = 0;
+ mPingBasedRetry = TRUE;
+ mTimeout = 0.f;
+ mCallback = NULL;
+ mCallbackData = NULL;
+ mMessageName = NULL;
+ };
+
+ ~LLReliablePacketParams() { };
+
+ void set ( const LLHost &host, S32 retries, BOOL ping_based_retry,
+ F32 timeout,
+ void (*callback)(void **,S32), void **callback_data, char *name )
+ {
+ mHost = host;
+ mRetries = retries;
+ mPingBasedRetry = ping_based_retry;
+ mTimeout = timeout;
+ mCallback = callback;
+ mCallbackData = callback_data;
+ mMessageName = name;
+ };
+};
+
+class LLReliablePacket
+{
+public:
+ LLReliablePacket(S32 socket, U8 *buf_ptr, S32 buf_len, LLReliablePacketParams *params) :
+ mBuffer(NULL),
+ mBufferLength(0)
+ {
+ if (params)
+ {
+ mHost = params->mHost;
+ mRetries = params->mRetries;
+ mPingBasedRetry = params->mPingBasedRetry;
+ mTimeout = params->mTimeout;
+ mCallback = params->mCallback;
+ mCallbackData = params->mCallbackData;
+ mMessageName = params->mMessageName;
+ }
+ else
+ {
+ mRetries = 0;
+ mPingBasedRetry = TRUE;
+ mTimeout = 0.f;
+ mCallback = NULL;
+ mCallbackData = NULL;
+ mMessageName = NULL;
+ }
+
+ mExpirationTime = (F64)((S64)totalTime())/1000000.0 + mTimeout;
+ mPacketID = buf_ptr[1] + ((buf_ptr[0] & 0x0f ) * 256);
+ if (sizeof(TPACKETID) == 4)
+ {
+ mPacketID *= 256;
+ mPacketID += buf_ptr[2];
+ mPacketID *= 256;
+ mPacketID += buf_ptr[3];
+ }
+
+ mSocket = socket;
+ if (mRetries)
+ {
+ mBuffer = new U8[buf_len];
+ if (mBuffer != NULL)
+ {
+ memcpy(mBuffer,buf_ptr,buf_len);
+ mBufferLength = buf_len;
+ }
+
+ }
+ };
+ ~LLReliablePacket(){ delete [] mBuffer; };
+
+ friend class LLCircuitData;
+protected:
+ S32 mSocket;
+ LLHost mHost;
+ S32 mRetries;
+ BOOL mPingBasedRetry;
+ F32 mTimeout;
+ void (*mCallback)(void **,S32);
+ void **mCallbackData;
+ char *mMessageName;
+
+ U8 *mBuffer;
+ S32 mBufferLength;
+
+ TPACKETID mPacketID;
+
+ F64 mExpirationTime;
+
+};
+
+#endif
+
diff --git a/indra/llmessage/llpacketbuffer.cpp b/indra/llmessage/llpacketbuffer.cpp
new file mode 100644
index 0000000000..95c2217a69
--- /dev/null
+++ b/indra/llmessage/llpacketbuffer.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file llpacketbuffer.cpp
+ * @brief implementation of LLPacketBuffer class for a packet.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpacketbuffer.h"
+
+#include "net.h"
+#include "timing.h"
+#include "llhost.h"
+
+///////////////////////////////////////////////////////////
+
+LLPacketBuffer::LLPacketBuffer(const LLHost &host, const char *datap, const S32 size) : mHost(host)
+{
+ if (size > NET_BUFFER_SIZE)
+ {
+ llerrs << "Sending packet > " << NET_BUFFER_SIZE << " of size " << size << llendl;
+ }
+
+ if (datap != NULL)
+ {
+ memcpy(mData, datap, size);
+ mSize = size;
+ }
+
+}
+
+LLPacketBuffer::LLPacketBuffer (S32 hSocket)
+{
+ init(hSocket);
+}
+
+///////////////////////////////////////////////////////////
+
+LLPacketBuffer::~LLPacketBuffer ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLPacketBuffer::init (S32 hSocket)
+{
+ mSize = receive_packet(hSocket, mData);
+ mHost = ::get_sender();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLPacketBuffer::free ()
+{
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llpacketbuffer.h b/indra/llmessage/llpacketbuffer.h
new file mode 100644
index 0000000000..13841aaa08
--- /dev/null
+++ b/indra/llmessage/llpacketbuffer.h
@@ -0,0 +1,37 @@
+/**
+ * @file llpacketbuffer.h
+ * @brief definition of LLPacketBuffer class for implementing a
+ * resend, drop, or delay in packet transmissions.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETBUFFER_H
+#define LL_LLPACKETBUFFER_H
+
+#include "net.h" // for NET_BUFFER_SIZE
+#include "llhost.h"
+
+class LLPacketBuffer
+{
+public:
+ LLPacketBuffer(const LLHost &host, const char *datap, const S32 size);
+ LLPacketBuffer(S32 hSocket); // receive a packet
+ ~LLPacketBuffer();
+
+ S32 getSize() const { return mSize; }
+ const char *getData() const { return mData; }
+ LLHost getHost() const { return mHost; }
+ void init(S32 hSocket);
+ void free();
+
+protected:
+ char mData[NET_BUFFER_SIZE]; // packet data /* Flawfinder : ignore */
+ S32 mSize; // size of buffer in bytes
+ LLHost mHost; // source/dest IP and port
+};
+
+#endif
+
+
diff --git a/indra/llmessage/llpacketring.cpp b/indra/llmessage/llpacketring.cpp
new file mode 100644
index 0000000000..4f17d1ae5a
--- /dev/null
+++ b/indra/llmessage/llpacketring.cpp
@@ -0,0 +1,293 @@
+/**
+ * @file llpacketring.cpp
+ * @brief implementation of LLPacketRing class for a packet.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpacketring.h"
+
+// linden library includes
+#include "llerror.h"
+#include "lltimer.h"
+#include "timing.h"
+#include "llrand.h"
+#include "u64.h"
+
+///////////////////////////////////////////////////////////
+LLPacketRing::LLPacketRing () :
+ mUseInThrottle(FALSE),
+ mUseOutThrottle(FALSE),
+ mInThrottle(256000.f),
+ mOutThrottle(64000.f),
+ mActualBitsIn(0),
+ mActualBitsOut(0),
+ mMaxBufferLength(64000),
+ mInBufferLength(0),
+ mOutBufferLength(0),
+ mDropPercentage(0.0f),
+ mPacketsToDrop(0x0)
+{
+}
+
+///////////////////////////////////////////////////////////
+LLPacketRing::~LLPacketRing ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::free ()
+{
+ LLPacketBuffer *packetp;
+
+ while (!mReceiveQueue.empty())
+ {
+ packetp = mReceiveQueue.front();
+ delete packetp;
+ mReceiveQueue.pop();
+ }
+
+ while (!mSendQueue.empty())
+ {
+ packetp = mSendQueue.front();
+ delete packetp;
+ mSendQueue.pop();
+ }
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::dropPackets (U32 num_to_drop)
+{
+ mPacketsToDrop += num_to_drop;
+}
+
+///////////////////////////////////////////////////////////
+void LLPacketRing::setDropPercentage (F32 percent_to_drop)
+{
+ mDropPercentage = percent_to_drop;
+}
+
+void LLPacketRing::setUseInThrottle(const BOOL use_throttle)
+{
+ mUseInThrottle = use_throttle;
+}
+
+void LLPacketRing::setUseOutThrottle(const BOOL use_throttle)
+{
+ mUseOutThrottle = use_throttle;
+}
+
+void LLPacketRing::setInBandwidth(const F32 bps)
+{
+ mInThrottle.setRate(bps);
+}
+
+void LLPacketRing::setOutBandwidth(const F32 bps)
+{
+ mOutThrottle.setRate(bps);
+}
+///////////////////////////////////////////////////////////
+S32 LLPacketRing::receiveFromRing (S32 socket, char *datap)
+{
+
+ if (mInThrottle.checkOverflow(0))
+ {
+ // We don't have enough bandwidth, don't give them a packet.
+ return 0;
+ }
+
+ LLPacketBuffer *packetp = NULL;
+ if (mReceiveQueue.empty())
+ {
+ // No packets on the queue, don't give them any.
+ return 0;
+ }
+
+ S32 packet_size = 0;
+ packetp = mReceiveQueue.front();
+ mReceiveQueue.pop();
+ packet_size = packetp->getSize();
+ if (packetp->getData() != NULL)
+ {
+ memcpy(datap, packetp->getData(), packet_size);
+ }
+ // need to set sender IP/port!!
+ mLastSender = packetp->getHost();
+ delete packetp;
+
+ this->mInBufferLength -= packet_size;
+
+ // Adjust the throttle
+ mInThrottle.throttleOverflow(packet_size * 8.f);
+ return packet_size;
+}
+
+///////////////////////////////////////////////////////////
+S32 LLPacketRing::receivePacket (S32 socket, char *datap)
+{
+ S32 packet_size = 0;
+
+ // If using the throttle, simulate a limited size input buffer.
+ if (mUseInThrottle)
+ {
+ BOOL done = FALSE;
+
+ // push any current net packet (if any) onto delay ring
+ while (!done)
+ {
+ LLPacketBuffer *packetp;
+ packetp = new LLPacketBuffer(socket);
+
+ if (packetp->getSize())
+ {
+ mActualBitsIn += packetp->getSize() * 8;
+
+ // Fake packet loss
+ if (mDropPercentage && (frand(100.f) < mDropPercentage))
+ {
+ mPacketsToDrop++;
+ }
+
+ if (mPacketsToDrop)
+ {
+ delete packetp;
+ packetp = NULL;
+ packet_size = 0;
+ mPacketsToDrop--;
+ }
+ }
+
+ // If we faked packet loss, then we don't have a packet
+ // to use for buffer overflow testing
+ if (packetp)
+ {
+ if (mInBufferLength + packetp->getSize() > mMaxBufferLength)
+ {
+ // Toss it.
+ llwarns << "Throwing away packet, overflowing buffer" << llendl;
+ delete packetp;
+ packetp = NULL;
+ }
+ else if (packetp->getSize())
+ {
+ mReceiveQueue.push(packetp);
+ mInBufferLength += packetp->getSize();
+ }
+ else
+ {
+ delete packetp;
+ packetp = NULL;
+ done = true;
+ }
+ }
+ else
+ {
+ // No packetp, keep going? - no packetp == faked packet loss
+ }
+ }
+
+ // Now, grab data off of the receive queue according to our
+ // throttled bandwidth settings.
+ packet_size = receiveFromRing(socket, datap);
+ }
+ else
+ {
+ // no delay, pull straight from net
+ packet_size = receive_packet(socket, datap);
+ mLastSender = ::get_sender();
+
+ if (packet_size) // did we actually get a packet?
+ {
+ if (mDropPercentage && (frand(100.f) < mDropPercentage))
+ {
+ mPacketsToDrop++;
+ }
+
+ if (mPacketsToDrop)
+ {
+ packet_size = 0;
+ mPacketsToDrop--;
+ }
+ }
+ }
+
+ return packet_size;
+}
+
+BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host)
+{
+ BOOL status = TRUE;
+ if (!mUseOutThrottle)
+ {
+ return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() );
+ }
+ else
+ {
+ mActualBitsOut += buf_size * 8;
+ LLPacketBuffer *packetp = NULL;
+ // See if we've got enough throttle to send a packet.
+ while (!mOutThrottle.checkOverflow(0.f))
+ {
+ // While we have enough bandwidth, send a packet from the queue or the current packet
+
+ S32 packet_size = 0;
+ if (!mSendQueue.empty())
+ {
+ // Send a packet off of the queue
+ LLPacketBuffer *packetp = mSendQueue.front();
+ mSendQueue.pop();
+
+ mOutBufferLength -= packetp->getSize();
+ packet_size = packetp->getSize();
+
+ status = send_packet(h_socket, packetp->getData(), packet_size, packetp->getHost().getAddress(), packetp->getHost().getPort());
+
+ delete packetp;
+ // Update the throttle
+ mOutThrottle.throttleOverflow(packet_size * 8.f);
+ }
+ else
+ {
+ // If the queue's empty, we can just send this packet right away.
+ status = send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() );
+ packet_size = buf_size;
+
+ // Update the throttle
+ mOutThrottle.throttleOverflow(packet_size * 8.f);
+
+ // This was the packet we're sending now, there are no other packets
+ // that we need to send
+ return status;
+ }
+
+ }
+
+ // We haven't sent the incoming packet, add it to the queue
+ if (mOutBufferLength + buf_size > mMaxBufferLength)
+ {
+ // Nuke this packet, we overflowed the buffer.
+ // Toss it.
+ llwarns << "Throwing away outbound packet, overflowing buffer" << llendl;
+ }
+ else
+ {
+ static LLTimer queue_timer;
+ if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f)
+ {
+ // Add it to the queue
+ llinfos << "Outbound packet queue " << mOutBufferLength << " bytes" << llendl;
+ queue_timer.reset();
+ }
+ packetp = new LLPacketBuffer(host, send_buffer, buf_size);
+
+ mOutBufferLength += packetp->getSize();
+ mSendQueue.push(packetp);
+ }
+ }
+
+ return status;
+}
diff --git a/indra/llmessage/llpacketring.h b/indra/llmessage/llpacketring.h
new file mode 100644
index 0000000000..0327586d78
--- /dev/null
+++ b/indra/llmessage/llpacketring.h
@@ -0,0 +1,74 @@
+/**
+ * @file llpacketring.h
+ * @brief definition of LLPacketRing class for implementing a resend,
+ * drop, or delay in packet transmissions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPACKETRING_H
+#define LL_LLPACKETRING_H
+
+#include <queue>
+
+#include "llpacketbuffer.h"
+#include "linked_lists.h"
+#include "llhost.h"
+#include "net.h"
+#include "llthrottle.h"
+
+
+class LLPacketRing
+{
+public:
+ LLPacketRing();
+ ~LLPacketRing();
+
+ void free();
+
+ void dropPackets(U32);
+ void setDropPercentage (F32 percent_to_drop);
+ void setUseInThrottle(const BOOL use_throttle);
+ void setUseOutThrottle(const BOOL use_throttle);
+ void setInBandwidth(const F32 bps);
+ void setOutBandwidth(const F32 bps);
+ S32 receivePacket (S32 socket, char *datap);
+ S32 receiveFromRing (S32 socket, char *datap);
+
+ BOOL sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host);
+
+ inline LLHost getLastSender();
+
+ S32 getAndResetActualInBits() { S32 bits = mActualBitsIn; mActualBitsIn = 0; return bits;}
+ S32 getAndResetActualOutBits() { S32 bits = mActualBitsOut; mActualBitsOut = 0; return bits;}
+protected:
+ BOOL mUseInThrottle;
+ BOOL mUseOutThrottle;
+
+ // For simulating a lower-bandwidth connection - BPS
+ LLThrottle mInThrottle;
+ LLThrottle mOutThrottle;
+
+ S32 mActualBitsIn;
+ S32 mActualBitsOut;
+ S32 mMaxBufferLength; // How much data can we queue up before dropping data.
+ S32 mInBufferLength; // Current incoming buffer length
+ S32 mOutBufferLength; // Current outgoing buffer length
+
+ F32 mDropPercentage; // % of packets to drop
+ U32 mPacketsToDrop; // drop next n packets
+
+ std::queue<LLPacketBuffer *> mReceiveQueue;
+ std::queue<LLPacketBuffer *> mSendQueue;
+
+ LLHost mLastSender;
+};
+
+
+inline LLHost LLPacketRing::getLastSender()
+{
+ return mLastSender;
+}
+
+#endif
diff --git a/indra/llmessage/llpartdata.cpp b/indra/llmessage/llpartdata.cpp
new file mode 100644
index 0000000000..4ce7bc1fcd
--- /dev/null
+++ b/indra/llmessage/llpartdata.cpp
@@ -0,0 +1,307 @@
+/**
+ * @file llpartdata.cpp
+ * @brief Particle system data packing
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llpartdata.h"
+#include "message.h"
+
+#include "lldatapacker.h"
+#include "v4coloru.h"
+
+#include "llsdutil.h"
+
+
+const S32 PS_PART_DATA_BLOCK_SIZE = 4 + 2 + 4 + 4 + 2 + 2; // 18
+const S32 PS_DATA_BLOCK_SIZE = 68 + PS_PART_DATA_BLOCK_SIZE; // 68 + 18 = 86
+
+
+const F32 MAX_PART_SCALE = 4.f;
+
+BOOL LLPartData::pack(LLDataPacker &dp)
+{
+ LLColor4U coloru;
+ dp.packU32(mFlags, "pdflags");
+ dp.packFixed(mMaxAge, "pdmaxage", FALSE, 8, 8);
+ coloru.setVec(mStartColor);
+ dp.packColor4U(coloru, "pdstartcolor");
+ coloru.setVec(mEndColor);
+ dp.packColor4U(coloru, "pdendcolor");
+ dp.packFixed(mStartScale.mV[0], "pdstartscalex", FALSE, 3, 5);
+ dp.packFixed(mStartScale.mV[1], "pdstartscaley", FALSE, 3, 5);
+ dp.packFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
+ dp.packFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
+ return TRUE;
+}
+
+LLSD LLPartData::asLLSD() const
+{
+ LLSD sd = LLSD();
+ sd["pdflags"] = ll_sd_from_U32(mFlags);
+ sd["pdmaxage"] = mMaxAge;
+ sd["pdstartcolor"] = ll_sd_from_color4(mStartColor);
+ sd["pdendcolor"] = ll_sd_from_color4(mEndColor);
+ sd["pdstartscale"] = ll_sd_from_vector2(mStartScale);
+ sd["pdendscale"] = ll_sd_from_vector2(mEndScale);
+ return sd;
+}
+
+bool LLPartData::fromLLSD(LLSD& sd)
+{
+ mFlags = ll_U32_from_sd(sd["pdflags"]);
+ mMaxAge = (F32)sd["pdmaxage"].asReal();
+ mStartColor = ll_color4_from_sd(sd["pdstartcolor"]);
+ mEndColor = ll_color4_from_sd(sd["pdendcolor"]);
+ mStartScale = ll_vector2_from_sd(sd["pdstartscale"]);
+ mEndScale = ll_vector2_from_sd(sd["pdendscale"]);
+ return true;
+}
+
+
+BOOL LLPartData::unpack(LLDataPacker &dp)
+{
+ LLColor4U coloru;
+
+ dp.unpackU32(mFlags, "pdflags");
+ dp.unpackFixed(mMaxAge, "pdmaxage", FALSE, 8, 8);
+
+ dp.unpackColor4U(coloru, "pdstartcolor");
+ mStartColor.setVec(coloru);
+ dp.unpackColor4U(coloru, "pdendcolor");
+ mEndColor.setVec(coloru);
+ dp.unpackFixed(mStartScale.mV[0], "pdstartscalex", FALSE, 3, 5);
+ dp.unpackFixed(mStartScale.mV[1], "pdstartscaley", FALSE, 3, 5);
+ dp.unpackFixed(mEndScale.mV[0], "pdendscalex", FALSE, 3, 5);
+ dp.unpackFixed(mEndScale.mV[1], "pdendscaley", FALSE, 3, 5);
+ return TRUE;
+}
+
+
+void LLPartData::setFlags(const U32 flags)
+{
+ mFlags = flags;
+}
+
+
+void LLPartData::setMaxAge(const F32 max_age)
+{
+ mMaxAge = llclamp(max_age, 0.f, 30.f);
+}
+
+
+void LLPartData::setStartScale(const F32 xs, const F32 ys)
+{
+ mStartScale.mV[VX] = llmin(xs, MAX_PART_SCALE);
+ mStartScale.mV[VY] = llmin(ys, MAX_PART_SCALE);
+}
+
+
+void LLPartData::setEndScale(const F32 xs, const F32 ys)
+{
+ mEndScale.mV[VX] = llmin(xs, MAX_PART_SCALE);
+ mEndScale.mV[VY] = llmin(ys, MAX_PART_SCALE);
+}
+
+
+void LLPartData::setStartColor(const LLVector3 &rgb)
+{
+ mStartColor.setVec(rgb.mV[0], rgb.mV[1], rgb.mV[2]);
+}
+
+
+void LLPartData::setEndColor(const LLVector3 &rgb)
+{
+ mEndColor.setVec(rgb.mV[0], rgb.mV[1], rgb.mV[2]);
+}
+
+void LLPartData::setStartAlpha(const F32 alpha)
+{
+ mStartColor.mV[3] = alpha;
+}
+void LLPartData::setEndAlpha(const F32 alpha)
+{
+ mEndColor.mV[3] = alpha;
+}
+
+
+LLPartSysData::LLPartSysData()
+{
+ mCRC = 0;
+ mPartData.mFlags = 0;
+ mPartData.mStartColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mPartData.mEndColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mPartData.mStartScale = LLVector2(1.f, 1.f);
+ mPartData.mEndScale = LLVector2(1.f, 1.f);
+ mPartData.mMaxAge = 10.0;
+
+ mMaxAge = 0.0;
+ mStartAge = 0.0;
+ mPattern = LL_PART_SRC_PATTERN_DROP; // Pattern for particle velocity
+ mInnerAngle = 0.0; // Inner angle of PATTERN_ANGLE_*
+ mOuterAngle = 0.0; // Outer angle of PATTERN_ANGLE_*
+ mBurstRate = 0.1f; // How often to do a burst of particles
+ mBurstPartCount = 1; // How many particles in a burst
+ mBurstSpeedMin = 1.f; // Minimum particle velocity
+ mBurstSpeedMax = 1.f; // Maximum particle velocity
+ mBurstRadius = 0.f;
+}
+
+
+BOOL LLPartSysData::pack(LLDataPacker &dp)
+{
+ dp.packU32(mCRC, "pscrc");
+ dp.packU32(mFlags, "psflags");
+ dp.packU8(mPattern, "pspattern");
+ dp.packFixed(mMaxAge, "psmaxage", FALSE, 8, 8);
+ dp.packFixed(mStartAge, "psstartage", FALSE, 8, 8);
+ dp.packFixed(mInnerAngle, "psinnerangle", FALSE, 3, 5);
+ dp.packFixed(mOuterAngle, "psouterangle", FALSE, 3, 5);
+ dp.packFixed(mBurstRate, "psburstrate", FALSE, 8, 8);
+ dp.packFixed(mBurstRadius, "psburstradius", FALSE, 8, 8);
+ dp.packFixed(mBurstSpeedMin, "psburstspeedmin", FALSE, 8, 8);
+ dp.packFixed(mBurstSpeedMax, "psburstspeedmax", FALSE, 8, 8);
+ dp.packU8(mBurstPartCount, "psburstpartcount");
+
+ dp.packFixed(mAngularVelocity.mV[0], "psangvelx", TRUE, 8, 7);
+ dp.packFixed(mAngularVelocity.mV[1], "psangvely", TRUE, 8, 7);
+ dp.packFixed(mAngularVelocity.mV[2], "psangvelz", TRUE, 8, 7);
+
+ dp.packFixed(mPartAccel.mV[0], "psaccelx", TRUE, 8, 7);
+ dp.packFixed(mPartAccel.mV[1], "psaccely", TRUE, 8, 7);
+ dp.packFixed(mPartAccel.mV[2], "psaccelz", TRUE, 8, 7);
+
+ dp.packUUID(mPartImageID, "psuuid");
+ dp.packUUID(mTargetUUID, "pstargetuuid");
+ mPartData.pack(dp);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::unpack(LLDataPacker &dp)
+{
+ dp.unpackU32(mCRC, "pscrc");
+ dp.unpackU32(mFlags, "psflags");
+ dp.unpackU8(mPattern, "pspattern");
+ dp.unpackFixed(mMaxAge, "psmaxage", FALSE, 8, 8);
+ dp.unpackFixed(mStartAge, "psstartage", FALSE, 8, 8);
+ dp.unpackFixed(mInnerAngle, "psinnerangle", FALSE, 3, 5);
+ dp.unpackFixed(mOuterAngle, "psouterangle", FALSE, 3, 5);
+ dp.unpackFixed(mBurstRate, "psburstrate", FALSE, 8, 8);
+ mBurstRate = llmax(0.01f, mBurstRate);
+ dp.unpackFixed(mBurstRadius, "psburstradius", FALSE, 8, 8);
+ dp.unpackFixed(mBurstSpeedMin, "psburstspeedmin", FALSE, 8, 8);
+ dp.unpackFixed(mBurstSpeedMax, "psburstspeedmax", FALSE, 8, 8);
+ dp.unpackU8(mBurstPartCount, "psburstpartcount");
+
+ dp.unpackFixed(mAngularVelocity.mV[0], "psangvelx", TRUE, 8, 7);
+ dp.unpackFixed(mAngularVelocity.mV[1], "psangvely", TRUE, 8, 7);
+ dp.unpackFixed(mAngularVelocity.mV[2], "psangvelz", TRUE, 8, 7);
+
+ dp.unpackFixed(mPartAccel.mV[0], "psaccelx", TRUE, 8, 7);
+ dp.unpackFixed(mPartAccel.mV[1], "psaccely", TRUE, 8, 7);
+ dp.unpackFixed(mPartAccel.mV[2], "psaccelz", TRUE, 8, 7);
+
+ dp.unpackUUID(mPartImageID, "psuuid");
+ dp.unpackUUID(mTargetUUID, "pstargetuuid");
+ mPartData.unpack(dp);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::isNullPS(const S32 block_num)
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+ U32 crc;
+
+ S32 size;
+ // Check size of block
+ size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
+
+ if (!size)
+ {
+ return TRUE;
+ }
+ else if (size != PS_DATA_BLOCK_SIZE)
+ {
+ llwarns << "PSBlock is wrong size for particle system data - got " << size << ", expecting " << PS_DATA_BLOCK_SIZE << llendl;
+ return TRUE;
+ }
+ gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE, block_num, PS_DATA_BLOCK_SIZE);
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ dp.unpackU32(crc, "crc");
+
+ if (crc == 0)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+//static
+BOOL LLPartSysData::packNull()
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+ gMessageSystem->addBinaryData("PSBlock", ps_data_block, 0);
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::packBlock()
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ pack(dp);
+
+ // Add to message
+ gMessageSystem->addBinaryData("PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE);
+
+ return TRUE;
+}
+
+
+BOOL LLPartSysData::unpackBlock(const S32 block_num)
+{
+ U8 ps_data_block[PS_DATA_BLOCK_SIZE];
+
+ // Check size of block
+ S32 size = gMessageSystem->getSize("ObjectData", block_num, "PSBlock");
+
+ if (size != PS_DATA_BLOCK_SIZE)
+ {
+ llwarns << "PSBlock is wrong size for particle system data - got " << size << ", expecting " << PS_DATA_BLOCK_SIZE << llendl;
+ return FALSE;
+ }
+
+ // Get from message
+ gMessageSystem->getBinaryData("ObjectData", "PSBlock", ps_data_block, PS_DATA_BLOCK_SIZE, block_num, PS_DATA_BLOCK_SIZE);
+
+ LLDataPackerBinaryBuffer dp(ps_data_block, PS_DATA_BLOCK_SIZE);
+ unpack(dp);
+
+ return TRUE;
+}
+
+void LLPartSysData::clampSourceParticleRate()
+{
+ F32 particle_rate = 0;
+ particle_rate = mBurstPartCount/mBurstRate;
+ if (particle_rate > 256.f)
+ {
+ mBurstPartCount = llfloor(((F32)mBurstPartCount)*(256.f/particle_rate));
+ }
+}
+
+void LLPartSysData::setPartAccel(const LLVector3 &accel)
+{
+ mPartAccel.mV[VX] = llclamp(accel.mV[VX], -100.f, 100.f);
+ mPartAccel.mV[VY] = llclamp(accel.mV[VY], -100.f, 100.f);
+ mPartAccel.mV[VZ] = llclamp(accel.mV[VZ], -100.f, 100.f);
+}
diff --git a/indra/llmessage/llpartdata.h b/indra/llmessage/llpartdata.h
new file mode 100644
index 0000000000..ba3bdaf9b6
--- /dev/null
+++ b/indra/llmessage/llpartdata.h
@@ -0,0 +1,217 @@
+/**
+ * @file llpartdata.h
+ * @brief Particle system data packing
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPARTDATA_H
+#define LL_LLPARTDATA_H
+
+#include <stdio.h>
+
+#include "lluuid.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v2math.h"
+#include "v4color.h"
+
+class LLMessageSystem;
+class LLDataPacker;
+
+const S32 PS_CUR_VERSION = 18;
+
+//
+// These constants are used by the script code, not by the particle system itself
+//
+
+enum LLPSScriptFlags
+{
+ // Flags for the different parameters of individual particles
+ LLPS_PART_FLAGS,
+ LLPS_PART_START_COLOR,
+ LLPS_PART_START_ALPHA,
+ LLPS_PART_END_COLOR,
+ LLPS_PART_END_ALPHA,
+ LLPS_PART_START_SCALE,
+ LLPS_PART_END_SCALE,
+ LLPS_PART_MAX_AGE,
+
+ // Flags for the different parameters of the particle source
+ LLPS_SRC_ACCEL,
+ LLPS_SRC_PATTERN,
+ LLPS_SRC_INNERANGLE,
+ LLPS_SRC_OUTERANGLE,
+ LLPS_SRC_TEXTURE,
+ LLPS_SRC_BURST_RATE,
+ LLPS_SRC_BURST_DURATION,
+ LLPS_SRC_BURST_PART_COUNT,
+ LLPS_SRC_BURST_RADIUS,
+ LLPS_SRC_BURST_SPEED_MIN,
+ LLPS_SRC_BURST_SPEED_MAX,
+ LLPS_SRC_MAX_AGE,
+ LLPS_SRC_TARGET_UUID,
+ LLPS_SRC_OMEGA,
+ LLPS_SRC_ANGLE_BEGIN,
+ LLPS_SRC_ANGLE_END
+};
+
+
+class LLPartData
+{
+public:
+ LLPartData() :
+ mFlags(0),
+ mMaxAge(0)
+ {
+ }
+ BOOL unpack(LLDataPacker &dp);
+ BOOL pack(LLDataPacker &dp);
+ LLSD asLLSD() const;
+ operator LLSD() const {return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ // Masks for the different particle flags
+ enum
+ {
+ LL_PART_INTERP_COLOR_MASK = 0x01,
+ LL_PART_INTERP_SCALE_MASK = 0x02,
+ LL_PART_BOUNCE_MASK = 0x04,
+ LL_PART_WIND_MASK = 0x08,
+ LL_PART_FOLLOW_SRC_MASK = 0x10, // Follows source, no rotation following (expensive!)
+ LL_PART_FOLLOW_VELOCITY_MASK = 0x20, // Particles orient themselves with velocity
+ LL_PART_TARGET_POS_MASK = 0x40,
+ LL_PART_TARGET_LINEAR_MASK = 0x80, // Particle uses a direct linear interpolation
+ LL_PART_EMISSIVE_MASK = 0x100, // Particle is "emissive", instead of being lit
+ LL_PART_BEAM_MASK = 0x200, // Particle is a "beam" connecting source and target
+
+ // Not implemented yet!
+ //LL_PART_RANDOM_ACCEL_MASK = 0x100, // Patricles have random accelearation
+ //LL_PART_RANDOM_VEL_MASK = 0x200, // Particles have random velocity shifts"
+ //LL_PART_TRAIL_MASK = 0x400, // Particles have historical "trails"
+
+ // Viewer side use only!
+ LL_PART_DEAD_MASK = 0x80000000,
+ };
+
+ void setFlags(const U32 flags);
+ void setMaxAge(const F32 max_age);
+ void setStartScale(const F32 xs, F32 ys);
+ void setEndScale(const F32 xs, F32 ys);
+ void setStartColor(const LLVector3 &rgb);
+ void setEndColor(const LLVector3 &rgb);
+ void setStartAlpha(const F32 alpha);
+ void setEndAlpha(const F32 alpha);
+
+
+ friend class LLPartSysData;
+ friend class LLViewerPartSourceScript;
+
+ // These are public because I'm really lazy...
+public:
+ U32 mFlags; // Particle state/interpolators in effect
+ F32 mMaxAge; // Maximum age of the particle
+ LLColor4 mStartColor; // Start color
+ LLColor4 mEndColor; // End color
+ LLVector2 mStartScale; // Start scale
+ LLVector2 mEndScale; // End scale
+
+ LLVector3 mPosOffset; // Offset from source if using FOLLOW_SOURCE
+ F32 mParameter; // A single floating point parameter
+};
+
+
+class LLPartSysData
+{
+public:
+ LLPartSysData();
+
+ BOOL unpack(LLDataPacker &dp);
+ BOOL pack(LLDataPacker &dp);
+
+
+ BOOL unpackBlock(const S32 block_num);
+ BOOL packBlock();
+
+ static BOOL packNull();
+ static BOOL isNullPS(const S32 block_num); // Returns FALSE if this is a "NULL" particle system (i.e. no system)
+
+ // Different masks for effects on the source
+ enum
+ {
+ LL_PART_SRC_OBJ_REL_MASK = 0x01, // Accel and velocity for particles relative object rotation
+ LL_PART_USE_NEW_ANGLE = 0x02, // Particles uses new 'correct' angle parameters.
+ };
+
+ // The different patterns for how particles are created
+ enum
+ {
+ LL_PART_SRC_PATTERN_DROP = 0x01,
+ LL_PART_SRC_PATTERN_EXPLODE = 0x02,
+ // Not implemented fully yet
+ LL_PART_SRC_PATTERN_ANGLE = 0x04,
+ LL_PART_SRC_PATTERN_ANGLE_CONE = 0x08,
+ LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY = 0x10,
+ };
+
+
+ void setBurstSpeedMin(const F32 spd) { mBurstSpeedMin = llclamp(spd, -100.f, 100.f); }
+ void setBurstSpeedMax(const F32 spd) { mBurstSpeedMax = llclamp(spd, -100.f, 100.f); }
+ void setBurstRadius(const F32 rad) { mBurstRadius = llclamp(rad, 0.f, 50.f); }
+ void setPartAccel(const LLVector3 &accel);
+ void setUseNewAngle() { mFlags |= LL_PART_USE_NEW_ANGLE; }
+ void unsetUseNewAngle() { mFlags &= ~LL_PART_USE_NEW_ANGLE; }
+
+ // Since the actual particle creation rate is
+ // a combination of multiple parameters, we
+ // need to clamp it using a separate method instead of an accessor.
+ void clampSourceParticleRate();
+public:
+ // Public because I'm lazy....
+
+ //
+ // There are two kinds of data for the particle system
+ // 1. Parameters which specify parameters of the source (mSource*)
+ // 2. Parameters which specify parameters of the particles generated by the source (mPart*)
+ //
+
+ U32 mCRC;
+ U32 mFlags;
+
+ U8 mPattern; // Pattern for particle velocity/output
+ F32 mInnerAngle; // Inner angle for PATTERN_ANGLE
+ F32 mOuterAngle; // Outer angle for PATTERN_ANGLE
+ LLVector3 mAngularVelocity; // Angular velocity for emission axis (for PATTERN_ANGLE)
+
+ F32 mBurstRate; // How often to do a burst of particles
+ U8 mBurstPartCount; // How many particles in a burst
+ F32 mBurstRadius;
+ F32 mBurstSpeedMin; // Minimum particle velocity
+ F32 mBurstSpeedMax; // Maximum particle velocity
+
+ F32 mMaxAge; // Maximum lifetime of this particle source
+
+ LLUUID mTargetUUID; // Target UUID for the particle system
+
+ F32 mStartAge; // Age at which to start the particle system (for an update after the
+ // particle system has started)
+
+
+ //
+ // These are actually particle properties, but can be mutated by the source,
+ // so are stored here instead
+ //
+ LLVector3 mPartAccel;
+ LLUUID mPartImageID;
+
+ //
+ // The "template" partdata where we actually store the non-mutable particle parameters
+ //
+ LLPartData mPartData;
+
+protected:
+ S32 mNumParticles; // Number of particles generated
+};
+
+#endif // LL_LLPARTDATA_H
diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp
new file mode 100644
index 0000000000..0c5bd1eaea
--- /dev/null
+++ b/indra/llmessage/llpumpio.cpp
@@ -0,0 +1,1006 @@
+/**
+ * @file llpumpio.cpp
+ * @author Phoenix
+ * @date 2004-11-21
+ * @brief Implementation of the i/o pump and related functions.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llpumpio.h"
+
+#include <set>
+#include "apr-1/apr_poll.h"
+
+#include "llapr.h"
+#include "llmemtype.h"
+#include "llstl.h"
+
+// This should not be in production, but it is intensely useful during
+// development.
+#if LL_LINUX
+#define LL_DEBUG_PIPE_TYPE_IN_PUMP 0
+#endif
+
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+#include <typeinfo>
+#endif
+
+// constants for poll timeout. if we are threading, we want to have a
+// longer poll timeout.
+#if LL_THREADS_APR
+static const S32 DEFAULT_POLL_TIMEOUT = 1000;
+#else
+static const S32 DEFAULT_POLL_TIMEOUT = 0;
+#endif
+
+// The default (and fallback) expiration time for chains
+const F32 DEFAULT_CHAIN_EXPIRY_SECS = 30.0f;
+extern const F32 SHORT_CHAIN_EXPIRY_SECS = 1.0f;
+extern const F32 NEVER_CHAIN_EXPIRY_SECS = 0.0f;
+
+// sorta spammy debug modes.
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_IN_ON_ERROR 1
+//#define LL_DEBUG_PROCESS_LINK 1
+//#define LL_DEBUG_PROCESS_RETURN_VALUE 1
+
+// Super spammy debug mode.
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_IN 1
+//#define LL_DEBUG_SPEW_BUFFER_CHANNEL_OUT 1
+
+/**
+ * @class
+ */
+class LLChainSleeper : public LLRunnable
+{
+public:
+ static LLRunner::run_ptr_t build(LLPumpIO* pump, S32 key)
+ {
+ return LLRunner::run_ptr_t(new LLChainSleeper(pump, key));
+ }
+
+ virtual void run(LLRunner* runner, S64 handle)
+ {
+ mPump->clearLock(mKey);
+ }
+
+protected:
+ LLChainSleeper(LLPumpIO* pump, S32 key) : mPump(pump), mKey(key) {}
+ LLPumpIO* mPump;
+ S32 mKey;
+};
+
+
+/**
+ * @struct ll_delete_apr_pollset_fd_client_data
+ * @brief This is a simple helper class to clean up our client data.
+ */
+struct ll_delete_apr_pollset_fd_client_data
+{
+ typedef std::pair<LLIOPipe::ptr_t, apr_pollfd_t> pipe_conditional_t;
+ void operator()(const pipe_conditional_t& conditional)
+ {
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ S32* client_id = (S32*)conditional.second.client_data;
+ delete client_id;
+ }
+};
+
+/**
+ * LLPumpIO
+ */
+LLPumpIO::LLPumpIO(apr_pool_t* pool) :
+ mState(LLPumpIO::NORMAL),
+ mRebuildPollset(false),
+ mPollset(NULL),
+ mPollsetClientID(0),
+ mNextLock(0),
+ mPool(NULL),
+ mCurrentPool(NULL),
+ mCurrentPoolReallocCount(0),
+ mChainsMutex(NULL),
+ mCallbackMutex(NULL)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ initialize(pool);
+}
+
+LLPumpIO::~LLPumpIO()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ cleanup();
+}
+
+bool LLPumpIO::prime(apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ cleanup();
+ initialize(pool);
+ return ((pool == NULL) ? false : true);
+}
+
+bool LLPumpIO::addChain(const chain_t& chain, F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(chain.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ LLChainInfo info;
+ info.setTimeoutSeconds(timeout);
+ info.mData = LLIOPipe::buffer_ptr_t(new LLBufferArray);
+ LLLinkInfo link;
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "LLPumpIO::addChain() " << chain[0] << " '"
+ << typeid(*(chain[0])).name() << "'" << llendl;
+#else
+ lldebugs << "LLPumpIO::addChain() " << chain[0] <<llendl;
+#endif
+ chain_t::const_iterator it = chain.begin();
+ chain_t::const_iterator end = chain.end();
+ for(; it != end; ++it)
+ {
+ link.mPipe = (*it);
+ link.mChannels = info.mData->nextChannel();
+ info.mChainLinks.push_back(link);
+ }
+ mPendingChains.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::addChain(
+ const LLPumpIO::links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context,
+ F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+
+ // remember that if the caller is providing a full link
+ // description, we need to have that description matched to a
+ // particular buffer.
+ if(!data) return false;
+ if(links.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << " '"
+ << typeid(*(links[0].mPipe)).name() << "'" << llendl;
+#else
+ lldebugs << "LLPumpIO::addChain() " << links[0].mPipe << llendl;
+#endif
+ LLChainInfo info;
+ info.setTimeoutSeconds(timeout);
+ info.mChainLinks = links;
+ info.mData = data;
+ info.mContext = context;
+ mPendingChains.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::setTimeoutSeconds(F32 timeout)
+{
+ // If no chain is running, return failure.
+ if(current_chain_t() == mCurrentChain)
+ {
+ return false;
+ }
+ (*mCurrentChain).setTimeoutSeconds(timeout);
+ return true;
+}
+
+bool LLPumpIO::setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //lldebugs << "LLPumpIO::setConditional" << llendl;
+ if(pipe)
+ {
+ // remove any matching poll file descriptors for this pipe.
+ LLIOPipe::ptr_t pipe_ptr(pipe);
+
+ LLChainInfo::conditionals_t::iterator it = (*mCurrentChain).mDescriptors.begin();
+ LLChainInfo::conditionals_t::iterator end = (*mCurrentChain).mDescriptors.end();
+ while (it != end)
+ {
+ LLChainInfo::pipe_conditional_t& value = (*it);
+ if ( pipe_ptr == value.first )
+ {
+ ll_delete_apr_pollset_fd_client_data()(value);
+ (*mCurrentChain).mDescriptors.erase(it++);
+ mRebuildPollset = true;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ if(poll)
+ {
+ LLChainInfo::pipe_conditional_t value;
+ value.first = pipe_ptr;
+ value.second = *poll;
+ if(!poll->p)
+ {
+ // each fd needs a pool to work with, so if one was
+ // not specified, use this pool.
+ // *FIX: Should it always be this pool?
+ value.second.p = mPool;
+ }
+ value.second.client_data = new S32(++mPollsetClientID);
+ (*mCurrentChain).mDescriptors.push_back(value);
+ mRebuildPollset = true;
+ }
+ return true;
+ }
+ return false;
+}
+
+S32 LLPumpIO::setLock()
+{
+ // *NOTE: I do not think it is necessary to acquire a mutex here
+ // since this should only be called during the pump(), and should
+ // only change the running chain. Any other use of this method is
+ // incorrect usage. If it becomes necessary to acquire a lock
+ // here, be sure to lock here and call a protected method to get
+ // the lock, and sleepChain() should probably acquire the same
+ // lock while and calling the same protected implementation to
+ // lock the runner at the same time.
+
+ // If no chain is running, return failure.
+ if(current_chain_t() == mCurrentChain)
+ {
+ return 0;
+ }
+
+ // deal with wrap.
+ if(++mNextLock <= 0)
+ {
+ mNextLock = 1;
+ }
+
+ // set the lock
+ (*mCurrentChain).mLock = mNextLock;
+ return mNextLock;
+}
+
+void LLPumpIO::clearLock(S32 key)
+{
+ // We need to lock it here since we do not want to be iterating
+ // over the chains twice. We can safely call process() while this
+ // is happening since we should not be erasing a locked pipe, and
+ // 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);
+#endif
+ mClearLocks.insert(key);
+}
+
+bool LLPumpIO::sleepChain(F64 seconds)
+{
+ // Much like the call to setLock(), this should only be called
+ // from one chain during processing, so there is no need to
+ // acquire a mutex.
+ if(seconds <= 0.0) return false;
+ S32 key = setLock();
+ if(!key) return false;
+ LLRunner::run_handle_t handle = mRunner.addRunnable(
+ LLChainSleeper::build(this, key),
+ LLRunner::RUN_IN,
+ seconds);
+ if(0 == handle) return false;
+ return true;
+}
+
+bool LLPumpIO::copyCurrentLinkInfo(links_t& links) const
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(current_chain_t() == mCurrentChain)
+ {
+ return false;
+ }
+ std::copy(
+ (*mCurrentChain).mChainLinks.begin(),
+ (*mCurrentChain).mChainLinks.end(),
+ std::back_insert_iterator<links_t>(links));
+ return true;
+}
+
+void LLPumpIO::pump()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //llinfos << "LLPumpIO::pump()" << llendl;
+
+ // Run any pending runners.
+ mRunner.run();
+
+ // We need to move all of the pending heads over to the running
+ // chains.
+ PUMP_DEBUG;
+ if(true)
+ {
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ // bail if this pump is paused.
+ if(PAUSING == mState)
+ {
+ mState = PAUSED;
+ }
+ if(PAUSED == mState)
+ {
+ return;
+ }
+
+ PUMP_DEBUG;
+ // Move the pending chains over to the running chaings
+ if(!mPendingChains.empty())
+ {
+ PUMP_DEBUG;
+ //lldebugs << "Pushing " << mPendingChains.size() << "." << llendl;
+ std::copy(
+ mPendingChains.begin(),
+ mPendingChains.end(),
+ std::back_insert_iterator<running_chains_t>(mRunningChains));
+ mPendingChains.clear();
+ PUMP_DEBUG;
+ }
+
+ // Clear any locks. This needs to be done here so that we do
+ // not clash during a call to clearLock().
+ if(!mClearLocks.empty())
+ {
+ PUMP_DEBUG;
+ running_chains_t::iterator it = mRunningChains.begin();
+ running_chains_t::iterator end = mRunningChains.end();
+ std::set<S32>::iterator not_cleared = mClearLocks.end();
+ for(; it != end; ++it)
+ {
+ if((*it).mLock && mClearLocks.find((*it).mLock) != not_cleared)
+ {
+ (*it).mLock = 0;
+ }
+ }
+ PUMP_DEBUG;
+ mClearLocks.clear();
+ }
+ }
+
+ PUMP_DEBUG;
+ // rebuild the pollset if necessary
+ if(mRebuildPollset)
+ {
+ PUMP_DEBUG;
+ rebuildPollset();
+ mRebuildPollset = false;
+ }
+
+ // Poll based on the last known pollset
+ // *FIX: may want to pass in a poll timeout so it works correctly
+ // in single and multi threaded processes.
+ PUMP_DEBUG;
+ typedef std::set<S32> signal_client_t;
+ signal_client_t signalled_client;
+ if(mPollset)
+ {
+ PUMP_DEBUG;
+ //llinfos << "polling" << llendl;
+ S32 count = 0;
+ S32 client_id = 0;
+ const apr_pollfd_t* poll_fd = NULL;
+ apr_pollset_poll(mPollset, DEFAULT_POLL_TIMEOUT, &count, &poll_fd);
+ PUMP_DEBUG;
+ for(S32 i = 0; i < count; ++i)
+ {
+ client_id = *((S32*)poll_fd[i].client_data);
+ signalled_client.insert(client_id);
+ }
+ PUMP_DEBUG;
+ }
+
+ PUMP_DEBUG;
+ // set up for a check to see if each one was signalled
+ signal_client_t::iterator not_signalled = signalled_client.end();
+
+ // Process everything as appropriate
+ //lldebugs << "Running chain count: " << mRunningChains.size() << llendl;
+ running_chains_t::iterator run_chain = mRunningChains.begin();
+ bool process_this_chain = false;
+ for(; run_chain != mRunningChains.end(); )
+ {
+ PUMP_DEBUG;
+ if((*run_chain).mInit
+ && (*run_chain).mTimer.getStarted()
+ && (*run_chain).mTimer.hasExpired())
+ {
+ PUMP_DEBUG;
+ if(handleChainError(*run_chain, LLIOPipe::STATUS_EXPIRED))
+ {
+ // the pipe probably handled the error. If the handler
+ // forgot to reset the expiration then we need to do
+ // that here.
+ if((*run_chain).mTimer.getStarted()
+ && (*run_chain).mTimer.hasExpired())
+ {
+ PUMP_DEBUG;
+ llinfos << "Error handler forgot to reset timeout. "
+ << "Resetting to " << DEFAULT_CHAIN_EXPIRY_SECS
+ << " seconds." << llendl;
+ (*run_chain).setTimeoutSeconds(DEFAULT_CHAIN_EXPIRY_SECS);
+ }
+ }
+ else
+ {
+ PUMP_DEBUG;
+ // it timed out and no one handled it, so we need to
+ // retire the chain
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Removing chain "
+ << (*run_chain).mChainLinks[0].mPipe
+ << " '"
+ << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
+ << "' because it timed out." << llendl;
+#else
+// lldebugs << "Removing chain "
+// << (*run_chain).mChainLinks[0].mPipe
+// << " because we reached the end." << llendl;
+#endif
+ mRunningChains.erase(run_chain++);
+ continue;
+ }
+ }
+ PUMP_DEBUG;
+ if((*run_chain).mLock)
+ {
+ ++run_chain;
+ continue;
+ }
+ PUMP_DEBUG;
+ mCurrentChain = run_chain;
+ if((*run_chain).mDescriptors.empty())
+ {
+ // if there are no conditionals, just process this chain.
+ process_this_chain = true;
+ //lldebugs << "no conditionals - processing" << llendl;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ //lldebugs << "checking conditionals" << llendl;
+ // Check if this run chain was signalled. If any file
+ // descriptor is ready for something, then go ahead and
+ // process this chian.
+ process_this_chain = false;
+ if(!signalled_client.empty())
+ {
+ PUMP_DEBUG;
+ LLChainInfo::conditionals_t::iterator it;
+ it = (*run_chain).mDescriptors.begin();
+ LLChainInfo::conditionals_t::iterator end;
+ end = (*run_chain).mDescriptors.end();
+ S32 client_id = 0;
+ for(; it != end; ++it)
+ {
+ PUMP_DEBUG;
+ client_id = *((S32*)((*it).second.client_data));
+ if(signalled_client.find(client_id) != not_signalled)
+ {
+ process_this_chain = true;
+ break;
+ }
+ //llinfos << "no fd ready for this one." << llendl;
+ }
+ }
+ }
+ if(process_this_chain)
+ {
+ PUMP_DEBUG;
+ if(!((*run_chain).mInit))
+ {
+ (*run_chain).mHead = (*run_chain).mChainLinks.begin();
+ (*run_chain).mInit = true;
+ }
+ PUMP_DEBUG;
+ processChain(*run_chain);
+ }
+
+ PUMP_DEBUG;
+ if((*run_chain).mHead == (*run_chain).mChainLinks.end())
+ {
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
+ << " '"
+ << typeid(*((*run_chain).mChainLinks[0].mPipe)).name()
+ << "' because we reached the end." << llendl;
+#else
+// lldebugs << "Removing chain " << (*run_chain).mChainLinks[0].mPipe
+// << " because we reached the end." << llendl;
+#endif
+
+ PUMP_DEBUG;
+ // This chain is done. Clean up any allocated memory and
+ // erase the chain info.
+ std::for_each(
+ (*run_chain).mDescriptors.begin(),
+ (*run_chain).mDescriptors.end(),
+ ll_delete_apr_pollset_fd_client_data());
+ mRunningChains.erase(run_chain++);
+
+ // *NOTE: may not always need to rebuild the pollset.
+ mRebuildPollset = true;
+ }
+ else
+ {
+ PUMP_DEBUG;
+ // this chain needs more processing - just go to the next
+ // chain.
+ ++run_chain;
+ }
+ }
+
+ PUMP_DEBUG;
+ // null out the chain
+ mCurrentChain = current_chain_t();
+ END_PUMP_DEBUG;
+}
+
+//bool LLPumpIO::respond(const chain_t& pipes)
+//{
+//#if LL_THREADS_APR
+// LLScopedLock lock(mCallbackMutex);
+//#endif
+// LLChainInfo info;
+// links_t links;
+//
+// mPendingCallbacks.push_back(info);
+// return true;
+//}
+
+bool LLPumpIO::respond(LLIOPipe* pipe)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(NULL == pipe) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+ LLChainInfo info;
+ LLLinkInfo link;
+ link.mPipe = pipe;
+ info.mChainLinks.push_back(link);
+ mPendingCallbacks.push_back(info);
+ return true;
+}
+
+bool LLPumpIO::respond(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ // if the caller is providing a full link description, we need to
+ // have that description matched to a particular buffer.
+ if(!data) return false;
+ if(links.empty()) return false;
+
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+
+ // Add the callback response
+ LLChainInfo info;
+ info.mChainLinks = links;
+ info.mData = data;
+ info.mContext = context;
+ mPendingCallbacks.push_back(info);
+ return true;
+}
+
+void LLPumpIO::callback()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ //llinfos << "LLPumpIO::callback()" << llendl;
+ if(true)
+ {
+#if LL_THREADS_APR
+ LLScopedLock lock(mCallbackMutex);
+#endif
+ std::copy(
+ mPendingCallbacks.begin(),
+ mPendingCallbacks.end(),
+ std::back_insert_iterator<callbacks_t>(mCallbacks));
+ mPendingCallbacks.clear();
+ }
+ if(!mCallbacks.empty())
+ {
+ callbacks_t::iterator it = mCallbacks.begin();
+ callbacks_t::iterator end = mCallbacks.end();
+ for(; it != end; ++it)
+ {
+ // it's always the first and last time for respone chains
+ (*it).mHead = (*it).mChainLinks.begin();
+ (*it).mInit = true;
+ (*it).mEOS = true;
+ processChain(*it);
+ }
+ mCallbacks.clear();
+ }
+}
+
+void LLPumpIO::control(LLPumpIO::EControl op)
+{
+#if LL_THREADS_APR
+ LLScopedLock lock(mChainsMutex);
+#endif
+ switch(op)
+ {
+ case PAUSE:
+ mState = PAUSING;
+ break;
+ case RESUME:
+ mState = NORMAL;
+ break;
+ default:
+ // no-op
+ break;
+ }
+}
+
+void LLPumpIO::initialize(apr_pool_t* pool)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(!pool) return;
+#if LL_THREADS_APR
+ apr_thread_mutex_create(&mChainsMutex, APR_THREAD_MUTEX_DEFAULT, pool);
+ apr_thread_mutex_create(&mCallbackMutex, APR_THREAD_MUTEX_DEFAULT, pool);
+#endif
+ mPool = pool;
+}
+
+void LLPumpIO::cleanup()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+#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;
+ apr_pollset_destroy(mPollset);
+ mPollset = NULL;
+ }
+ if(mCurrentPool)
+ {
+ apr_pool_destroy(mCurrentPool);
+ mCurrentPool = NULL;
+ }
+ mPool = NULL;
+}
+
+void LLPumpIO::rebuildPollset()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+// lldebugs << "LLPumpIO::rebuildPollset()" << llendl;
+ if(mPollset)
+ {
+ //lldebugs << "destroying pollset" << llendl;
+ apr_pollset_destroy(mPollset);
+ mPollset = NULL;
+ }
+ U32 size = 0;
+ running_chains_t::iterator run_it = mRunningChains.begin();
+ running_chains_t::iterator run_end = mRunningChains.end();
+ for(; run_it != run_end; ++run_it)
+ {
+ size += (*run_it).mDescriptors.size();
+ }
+ //lldebugs << "found " << size << " descriptors." << llendl;
+ if(size)
+ {
+ // Recycle the memory pool
+ const S32 POLLSET_POOL_RECYCLE_COUNT = 100;
+ if(mCurrentPool
+ && (0 == (++mCurrentPoolReallocCount % POLLSET_POOL_RECYCLE_COUNT)))
+ {
+ apr_pool_destroy(mCurrentPool);
+ mCurrentPool = NULL;
+ mCurrentPoolReallocCount = 0;
+ }
+ if(!mCurrentPool)
+ {
+ apr_status_t status = apr_pool_create(&mCurrentPool, mPool);
+ (void)ll_apr_warn_status(status);
+ }
+
+ // add all of the file descriptors
+ run_it = mRunningChains.begin();
+ LLChainInfo::conditionals_t::iterator fd_it;
+ LLChainInfo::conditionals_t::iterator fd_end;
+ apr_pollset_create(&mPollset, size, mCurrentPool, 0);
+ for(; run_it != run_end; ++run_it)
+ {
+ fd_it = (*run_it).mDescriptors.begin();
+ fd_end = (*run_it).mDescriptors.end();
+ for(; fd_it != fd_end; ++fd_it)
+ {
+ apr_pollset_add(mPollset, &((*fd_it).second));
+ }
+ }
+ }
+}
+
+void LLPumpIO::processChain(LLChainInfo& chain)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ LLIOPipe::EStatus status = LLIOPipe::STATUS_OK;
+ links_t::iterator it = chain.mHead;
+ links_t::iterator end = chain.mChainLinks.end();
+ bool need_process_signaled = false;
+ bool keep_going = true;
+ do
+ {
+#if LL_DEBUG_PROCESS_LINK
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ llinfos << "Processing " << typeid(*((*it).mPipe)).name() << "."
+ << llendl;
+#else
+ llinfos << "Processing link " << (*it).mPipe << "." << llendl;
+#endif
+#endif
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_IN
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter((*it).mChannels.in(), NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.in(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "CHANNEL IN(" << (*it).mChannels.in() << "): "
+ << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "CHANNEL IN(" << (*it).mChannels.in()<< "): (null)"
+ << llendl;
+ }
+ }
+#endif
+ PUMP_DEBUG;
+ status = (*it).mPipe->process(
+ (*it).mChannels,
+ chain.mData,
+ chain.mEOS,
+ chain.mContext,
+ this);
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_OUT
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter((*it).mChannels.out(), NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.out(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "CHANNEL OUT(" << (*it).mChannels.out()<< "): "
+ << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "CHANNEL OUT(" << (*it).mChannels.out()<< "): (null)"
+ << llendl;
+ }
+ }
+#endif
+
+#if LL_DEBUG_PROCESS_RETURN_VALUE
+ // Only bother with the success codes - error codes are logged
+ // below.
+ if(LLIOPipe::isSuccess(status))
+ {
+ llinfos << "Pipe returned: '"
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ << typeid(*((*it).mPipe)).name() << "':'"
+#endif
+ << LLIOPipe::lookupStatusString(status) << "'" << llendl;
+ }
+#endif
+
+ PUMP_DEBUG;
+ switch(status)
+ {
+ case LLIOPipe::STATUS_OK:
+ // no-op
+ break;
+ case LLIOPipe::STATUS_STOP:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ chain.mHead = end;
+ keep_going = false;
+ break;
+ case LLIOPipe::STATUS_DONE:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ chain.mHead = (it + 1);
+ chain.mEOS = true;
+ break;
+ case LLIOPipe::STATUS_BREAK:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ keep_going = false;
+ break;
+ case LLIOPipe::STATUS_NEED_PROCESS:
+ PUMP_DEBUG;
+ status = LLIOPipe::STATUS_OK;
+ if(!need_process_signaled)
+ {
+ need_process_signaled = true;
+ chain.mHead = it;
+ }
+ break;
+ default:
+ PUMP_DEBUG;
+ if(LLIOPipe::isError(status))
+ {
+ llinfos << "Pump generated pipe error: '"
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ << typeid(*((*it).mPipe)).name() << "':'"
+#endif
+ << LLIOPipe::lookupStatusString(status)
+ << "'" << llendl;
+#if LL_DEBUG_SPEW_BUFFER_CHANNEL_IN_ON_ERROR
+ if(chain.mData)
+ {
+ char* buf = NULL;
+ S32 bytes = chain.mData->countAfter(
+ (*it).mChannels.in(),
+ NULL);
+ if(bytes)
+ {
+ buf = new char[bytes + 1];
+ chain.mData->readAfter(
+ (*it).mChannels.in(),
+ NULL,
+ (U8*)buf,
+ bytes);
+ buf[bytes] = '\0';
+ llinfos << "Input After Error: " << buf << llendl;
+ delete[] buf;
+ buf = NULL;
+ }
+ else
+ {
+ llinfos << "Input After Error: (null)" << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Input After Error: (null)" << llendl;
+ }
+#endif
+ keep_going = false;
+ chain.mHead = it;
+ if(!handleChainError(chain, status))
+ {
+ chain.mHead = end;
+ }
+ }
+ else
+ {
+ llinfos << "Unhandled status code: " << status << ":"
+ << LLIOPipe::lookupStatusString(status) << llendl;
+ }
+ break;
+ }
+ PUMP_DEBUG;
+ } while(keep_going && (++it != end));
+ PUMP_DEBUG;
+}
+
+bool LLPumpIO::handleChainError(
+ LLChainInfo& chain,
+ LLIOPipe::EStatus error)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ links_t::reverse_iterator rit;
+ if(chain.mHead == chain.mChainLinks.end())
+ {
+ rit = links_t::reverse_iterator(chain.mHead);
+ }
+ else
+ {
+ rit = links_t::reverse_iterator(chain.mHead + 1);
+ }
+
+ links_t::reverse_iterator rend = chain.mChainLinks.rend();
+ bool handled = false;
+ bool keep_going = true;
+ do
+ {
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Passing error to " << typeid(*((*rit).mPipe)).name()
+ << "." << llendl;
+#endif
+ error = (*rit).mPipe->handleError(error, this);
+ switch(error)
+ {
+ case LLIOPipe::STATUS_OK:
+ handled = true;
+ chain.mHead = rit.base();
+ break;
+ case LLIOPipe::STATUS_STOP:
+ case LLIOPipe::STATUS_DONE:
+ case LLIOPipe::STATUS_BREAK:
+ case LLIOPipe::STATUS_NEED_PROCESS:
+#if LL_DEBUG_PIPE_TYPE_IN_PUMP
+ lldebugs << "Pipe " << typeid(*((*rit).mPipe)).name()
+ << " returned code to stop error handler." << llendl;
+#endif
+ keep_going = false;
+ break;
+ default:
+ if(LLIOPipe::isSuccess(error))
+ {
+ llinfos << "Unhandled status code: " << error << ":"
+ << LLIOPipe::lookupStatusString(error) << llendl;
+ error = LLIOPipe::STATUS_ERROR;
+ keep_going = false;
+ }
+ break;
+ }
+ } while(keep_going && !handled && (++rit != rend));
+ return handled;
+}
+
+/**
+ * LLPumpIO::LLChainInfo
+ */
+
+LLPumpIO::LLChainInfo::LLChainInfo() :
+ mInit(false),
+ mLock(0),
+ mEOS(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ mTimer.setTimerExpirySec(DEFAULT_CHAIN_EXPIRY_SECS);
+}
+
+void LLPumpIO::LLChainInfo::setTimeoutSeconds(F32 timeout)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_PUMP);
+ if(timeout > 0.0f)
+ {
+ mTimer.start();
+ mTimer.reset();
+ mTimer.setTimerExpirySec(timeout);
+ }
+ else
+ {
+ mTimer.stop();
+ }
+}
diff --git a/indra/llmessage/llpumpio.h b/indra/llmessage/llpumpio.h
new file mode 100644
index 0000000000..50f7411298
--- /dev/null
+++ b/indra/llmessage/llpumpio.h
@@ -0,0 +1,406 @@
+/**
+ * @file llpumpio.h
+ * @author Phoenix
+ * @date 2004-11-19
+ * @brief Declaration of pump class which manages io chains.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPUMPIO_H
+#define LL_LLPUMPIO_H
+
+#include <set>
+#if LL_LINUX // needed for PATH_MAX in APR.
+#include <sys/param.h>
+#endif
+
+#include "apr-1/apr_pools.h"
+#include "llbuffer.h"
+#include "llframetimer.h"
+#include "lliopipe.h"
+#include "llrun.h"
+
+// Define this to enable use with the APR thread library.
+//#define LL_THREADS_APR 1
+
+// some simple constants to help with timeouts
+extern const F32 DEFAULT_CHAIN_EXPIRY_SECS;
+extern const F32 SHORT_CHAIN_EXPIRY_SECS;
+extern const F32 NEVER_CHAIN_EXPIRY_SECS;
+
+/**
+ * @class LLPumpIO
+ * @brief Class to manage sets of io chains.
+ *
+ * The pump class provides a thread abstraction for doing IO based
+ * communication between two threads in a structured and optimized for
+ * processor time. The primary usage is to create a pump, and call
+ * <code>pump()</code> on a thread used for IO and call
+ * <code>respond()</code> on a thread that is expected to do higher
+ * level processing. You can call almost any other method from any
+ * thread - see notes for each method for details. In order for the
+ * threading abstraction to work, you need to call <code>prime()</code>
+ * with a valid apr pool.
+ * A pump instance manages much of the state for the pipe, including
+ * the list of pipes in the chain, the channel for each element in the
+ * chain, the buffer, and if any pipe has marked the stream or process
+ * as done. Pipes can also set file descriptor based conditional
+ * statements so that calls to process do not happen until data is
+ * ready to be read or written. Pipes control execution of calls to
+ * process by returning a status code such as STATUS_OK or
+ * STATUS_BREAK.
+ * One way to conceptualize the way IO will work is that a pump
+ * combines the unit processing of pipes to behave like file pipes on
+ * the unix command line.
+ */
+class LLPumpIO
+{
+public:
+ /**
+ * @brief Constructor.
+ */
+ LLPumpIO(apr_pool_t* pool);
+
+ /**
+ * @brief Destructor.
+ */
+ ~LLPumpIO();
+
+ /**
+ * @brief Prepare this pump for usage.
+ *
+ * If you fail to call this method prior to use, the pump will
+ * try to work, but will not come with any thread locking
+ * mechanisms.
+ * @param pool The apr pool to use.
+ * @return Returns true if the pump is primed.
+ */
+ bool prime(apr_pool_t* pool);
+
+ /**
+ * @brief Typedef for having a chain of pipes.
+ */
+ typedef std::vector<LLIOPipe::ptr_t> chain_t;
+
+ /**
+ * @brief Add a chain to this pump and process in the next cycle.
+ *
+ * This method will automatically generate a buffer and assign
+ * each link in the chain as if it were the consumer to the
+ * previous.
+ * @param chain The pipes for the chain
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool addChain(const chain_t& chain, F32 timeout);
+
+ /**
+ * @brief Struct to associate a pipe with it's buffer io indexes.
+ */
+ struct LLLinkInfo
+ {
+ LLIOPipe::ptr_t mPipe;
+ LLChannelDescriptors mChannels;
+ };
+
+ /**
+ * @brief Typedef for having a chain of <code>LLLinkInfo</code>
+ * instances.
+ */
+ typedef std::vector<LLLinkInfo> links_t;
+
+ /**
+ * @brief Add a chain to this pump and process in the next cycle.
+ *
+ * This method provides a slightly more sophisticated method for
+ * adding a chain where the caller can specify which link elements
+ * are on what channels. This method will fail if no buffer is
+ * provided since any calls to generate new channels for the
+ * buffers will cause unpredictable interleaving of data.
+ * @param links The pipes and io indexes for the chain
+ * @param data Shared pointer to data buffer
+ * @param context Potentially undefined context meta-data for chain.
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool addChain(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context,
+ F32 timeout);
+
+ /**
+ * @brief Set or clear a timeout for the running chain
+ *
+ * @param timeout The number of seconds in the future to
+ * expire. Pass in 0.0f to never expire.
+ * @return Returns true if the timer was set.
+ */
+ bool setTimeoutSeconds(F32 timeout);
+
+ /**
+ * @brief Set up file descriptors for for the running chain.
+ * @see rebuildPollset()
+ *
+ * There is currently a limit of one conditional per pipe.
+ * *NOTE: The internal mechanism for building a pollset based on
+ * pipe/pollfd/chain generates an epoll error on linux (and
+ * probably behaves similarly on other platforms) because the
+ * pollset rebuilder will add each apr_pollfd_t serially. This
+ * does not matter for pipes on the same chain, since any
+ * signalled pipe will eventually invoke a call to process(), but
+ * is a problem if the same apr_pollfd_t is on different
+ * chains. Once we have more than just network i/o on the pump,
+ * this might matter.
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @param pipe The pipe which is setting a conditional
+ * @param poll The entire socket and read/write condition - null to remove
+ * @return Returns true if the poll state was set.
+ */
+ bool setConditional(LLIOPipe* pipe, const apr_pollfd_t* poll);
+
+ /**
+ * @brief Lock the current chain.
+ * @see sleepChain() since it relies on the implementation of this method.
+ *
+ * This locks the currently running chain so that no more calls to
+ * <code>process()</code> until you call <code>clearLock()</code>
+ * with the lock identifier.
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @return Returns the lock identifer to be used in
+ * <code>clearLock()</code> or 0 on failure.
+ */
+ S32 setLock();
+
+ /**
+ * @brief Clears the identified lock.
+ *
+ * @param links A container for the links which will be appended
+ */
+ void clearLock(S32 key);
+
+ /**
+ * @brief Stop processing a chain for a while.
+ * @see setLock()
+ *
+ * This method will <em>not</em> update the timeout for this
+ * chain, so it is possible to sleep the chain until it is
+ * collected by the pump during a timeout cleanup.
+ * @param seconds The number of seconds in the future to
+ * resume processing.
+ * @return Returns true if the
+ */
+ bool sleepChain(F64 seconds);
+
+ /**
+ * @brief Copy the currently running chain link info
+ *
+ * *FIX: Given the structure of the pump and pipe relationship,
+ * this should probably go through a different mechanism than the
+ * pump. I think it would be best if the pipe had some kind of
+ * controller which was passed into <code>process()</code> rather
+ * than the pump which exposed this interface.
+ * @param links A container for the links which will be appended
+ * @return Returns true if the currently running chain was copied.
+ */
+ bool copyCurrentLinkInfo(links_t& links) const;
+
+ /**
+ * @brief Call this method to call process on all running chains.
+ *
+ * This method iterates through the running chains, and if all
+ * pipe on a chain are unconditionally ready or if any pipe has
+ * any conditional processiong condition then process will be
+ * called on every chain which has requested processing. that
+ * chain has a file descriptor ready, <code>process()</code> will
+ * be called for all pipes which have requested it.
+ */
+ void pump();
+
+ /**
+ * @brief Add a chain to a special queue which will be called
+ * during the next call to <code>callback()</code> and then
+ * dropped from the queue.
+ *
+ * @param chain The IO chain that will get one <code>process()</code>.
+ */
+ //void respond(const chain_t& pipes);
+
+ /**
+ * @brief Add pipe to a special queue which will be called
+ * during the next call to <code>callback()</code> and then dropped
+ * from the queue.
+ *
+ * This call will add a single pipe, with no buffer, context, or
+ * channel information to the callback queue. It will be called
+ * once, and then dropped.
+ * @param pipe A single io pipe which will be called
+ * @return Returns true if anything was added to the pump.
+ */
+ bool respond(LLIOPipe* pipe);
+
+ /**
+ * @brief Add a chain to a special queue which will be called
+ * during the next call to <code>callback()</code> and then
+ * dropped from the queue.
+ *
+ * It is important to remember that you should not add a data
+ * buffer or context which may still be in another chain - that
+ * will almost certainly lead to a problems. Ensure that you are
+ * done reading and writing to those parameters, have new
+ * generated, or empty pointers.
+ * @param links The pipes and io indexes for the chain
+ * @param data Shared pointer to data buffer
+ * @param context Potentially undefined context meta-data for chain.
+ * @return Returns true if anything was added to the pump.
+ */
+ bool respond(
+ const links_t& links,
+ LLIOPipe::buffer_ptr_t data,
+ LLSD context);
+
+ /**
+ * @brief Run through the callback queue and call <code>process()</code>.
+ *
+ * This call will process all prending responses and call process
+ * on each. This method will then drop all processed callback
+ * requests which may lead to deleting the referenced objects.
+ */
+ void callback();
+
+ /**
+ * @brief Enumeration to send commands to the pump.
+ */
+ enum EControl
+ {
+ PAUSE,
+ RESUME,
+ };
+
+ /**
+ * @brief Send a command to the pump.
+ *
+ * @param op What control to send to the pump.
+ */
+ void control(EControl op);
+
+protected:
+ /**
+ * @brief State of the pump
+ */
+ enum EState
+ {
+ NORMAL,
+ PAUSING,
+ PAUSED
+ };
+
+ // instance data
+ EState mState;
+ bool mRebuildPollset;
+ apr_pollset_t* mPollset;
+ S32 mPollsetClientID;
+ S32 mNextLock;
+ std::set<S32> mClearLocks;
+
+ // This is the pump's runnable scheduler used for handling
+ // expiring locks.
+ LLRunner mRunner;
+
+ // This structure is the stuff we track while running chains.
+ struct LLChainInfo
+ {
+ // methods
+ LLChainInfo();
+ void setTimeoutSeconds(F32 timeout);
+
+ // basic member data
+ bool mInit;
+ S32 mLock;
+ LLFrameTimer mTimer;
+ links_t::iterator mHead;
+ links_t mChainLinks;
+ LLIOPipe::buffer_ptr_t mData;
+ bool mEOS;
+ LLSD mContext;
+
+ // tracking inside the pump
+ typedef std::pair<LLIOPipe::ptr_t, apr_pollfd_t> pipe_conditional_t;
+ typedef std::vector<pipe_conditional_t> conditionals_t;
+ conditionals_t mDescriptors;
+ };
+
+ // All the running chains & info
+ typedef std::vector<LLChainInfo> pending_chains_t;
+ pending_chains_t mPendingChains;
+ typedef std::list<LLChainInfo> running_chains_t;
+ running_chains_t mRunningChains;
+
+ typedef running_chains_t::iterator current_chain_t;
+ current_chain_t mCurrentChain;
+
+ // structures necessary for doing callbacks
+ // since the callbacks only get one chance to run, we do not have
+ // to maintain a list.
+ typedef std::vector<LLChainInfo> callbacks_t;
+ callbacks_t mPendingCallbacks;
+ callbacks_t mCallbacks;
+
+ // memory allocator for pollsets & mutexes.
+ apr_pool_t* mPool;
+ apr_pool_t* mCurrentPool;
+ S32 mCurrentPoolReallocCount;
+
+#if LL_THREADS_APR
+ apr_thread_mutex_t* mChainsMutex;
+ apr_thread_mutex_t* mCallbackMutex;
+#else
+ int* mChainsMutex;
+ int* mCallbackMutex;
+#endif
+
+protected:
+ void initialize(apr_pool_t* pool);
+ void cleanup();
+
+ /**
+ * @brief Given the internal state of the chains, rebuild the pollset
+ * @see setConditional()
+ */
+ void rebuildPollset();
+
+ /**
+ * @brief Process the chain passed in.
+ *
+ * This method will potentially modify the internals of the
+ * chain. On end, the chain.mHead will equal
+ * chain.mChainLinks.end().
+ * @param chain The LLChainInfo object to work on.
+ */
+ void processChain(LLChainInfo& chain);
+
+ /**
+ * @brief Rewind through the chain to try to recover from an error.
+ *
+ * This method will potentially modify the internals of the
+ * chain.
+ * @param chain The LLChainInfo object to work on.
+ * @return Retuns true if someone handled the error
+ */
+ bool handleChainError(LLChainInfo& chain, LLIOPipe::EStatus error);
+};
+
+
+#endif // LL_LLPUMPIO_H
diff --git a/indra/llmessage/llqueryflags.h b/indra/llmessage/llqueryflags.h
new file mode 100644
index 0000000000..be6c9acf67
--- /dev/null
+++ b/indra/llmessage/llqueryflags.h
@@ -0,0 +1,32 @@
+/**
+ * @file llqueryflags.h
+ * @brief Flags for directory queries
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLQUERYFLAGS_H
+#define LL_LLQUERYFLAGS_H
+
+// Binary flags used for Find queries, shared between viewer and dataserver.
+
+// DirFindQuery flags
+const U32 DFQ_PEOPLE = 0x0001;
+const U32 DFQ_ONLINE = 0x0002;
+//const U32 DFQ_PLACES = 0x0004;
+const U32 DFQ_EVENTS = 0x0008;
+const U32 DFQ_GROUPS = 0x0010;
+const U32 DFQ_DATE_EVENTS = 0x0020;
+
+const U32 DFQ_AGENT_OWNED = 0x0040;
+const U32 DFQ_FOR_SALE = 0x0080;
+const U32 DFQ_GROUP_OWNED = 0x0100;
+//const U32 DFQ_AUCTION = 0x0200;
+const U32 DFQ_DWELL_SORT = 0x0400;
+const U32 DFQ_PG_SIMS_ONLY = 0x0800;
+const U32 DFQ_PICTURES_ONLY = 0x1000;
+const U32 DFQ_PG_EVENTS_ONLY = 0x2000;
+const U32 DFQ_MATURE_SIMS_ONLY = 0x4000;
+
+#endif
diff --git a/indra/llmessage/llregionflags.h b/indra/llmessage/llregionflags.h
new file mode 100644
index 0000000000..3f37c72cee
--- /dev/null
+++ b/indra/llmessage/llregionflags.h
@@ -0,0 +1,157 @@
+/**
+ * @file llregionflags.h
+ * @brief Flags that are sent in the statistics message region_flags field.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONFLAGS_H
+#define LL_LLREGIONFLAGS_H
+
+// Can you be hurt here? Should health be on?
+const U32 REGION_FLAGS_ALLOW_DAMAGE = (1 << 0);
+
+// Can you make landmarks here?
+const U32 REGION_FLAGS_ALLOW_LANDMARK = (1 << 1);
+
+// Do we reset the home position when someone teleports away from here?
+const U32 REGION_FLAGS_ALLOW_SET_HOME = (1 << 2);
+
+// Do we reset the home position when someone teleports away from here?
+const U32 REGION_FLAGS_RESET_HOME_ON_TELEPORT = (1 << 3);
+
+// Does the sun move?
+const U32 REGION_FLAGS_SUN_FIXED = (1 << 4);
+
+// Tax free zone (no taxes on objects, land, etc.)
+const U32 REGION_FLAGS_TAX_FREE = (1 << 5);
+
+// Can't change the terrain heightfield, even on owned parcels,
+// but can plant trees and grass.
+const U32 REGION_FLAGS_BLOCK_TERRAFORM = (1 << 6);
+
+// Can't release, sell, or buy land.
+const U32 REGION_FLAGS_BLOCK_LAND_RESELL = (1 << 7);
+
+// All content wiped once per night
+const U32 REGION_FLAGS_SANDBOX = (1 << 8);
+
+const U32 REGION_FLAGS_SKIP_AGENT_ACTION = (1 << 10);
+const U32 REGION_FLAGS_SKIP_UPDATE_INTEREST_LIST= (1 << 11);
+const U32 REGION_FLAGS_SKIP_COLLISIONS = (1 << 12); // Pin all non agent rigid bodies
+const U32 REGION_FLAGS_SKIP_SCRIPTS = (1 << 13);
+const U32 REGION_FLAGS_SKIP_PHYSICS = (1 << 14); // Skip all physics
+const U32 REGION_FLAGS_EXTERNALLY_VISIBLE = (1 << 15);
+const U32 REGION_FLAGS_MAINLAND_VISIBLE = (1 << 16);
+const U32 REGION_FLAGS_PUBLIC_ALLOWED = (1 << 17);
+const U32 REGION_FLAGS_BLOCK_DWELL = (1 << 18);
+
+// Is flight allowed?
+const U32 REGION_FLAGS_BLOCK_FLY = (1 << 19);
+
+// Is direct teleport (p2p) allowed?
+const U32 REGION_FLAGS_ALLOW_DIRECT_TELEPORT = (1 << 20);
+
+// Is there an administrative override on scripts in the region at the
+// moment. This is the similar skip scripts, except this flag is
+// presisted in the database on an estate level.
+const U32 REGION_FLAGS_ESTATE_SKIP_SCRIPTS = (1 << 21);
+
+const U32 REGION_FLAGS_RESTRICT_PUSHOBJECT = (1 << 22);
+
+const U32 REGION_FLAGS_DENY_ANONYMOUS = (1 << 23);
+const U32 REGION_FLAGS_DENY_IDENTIFIED = (1 << 24);
+const U32 REGION_FLAGS_DENY_TRANSACTED = (1 << 25);
+
+const U32 REGION_FLAGS_ALLOW_PARCEL_CHANGES = (1 << 26);
+
+const U32 REGION_FLAGS_NULL_LAYER = (1 << 9);
+
+const U32 REGION_FLAGS_DEFAULT = REGION_FLAGS_ALLOW_LANDMARK |
+ REGION_FLAGS_ALLOW_SET_HOME |
+ REGION_FLAGS_ALLOW_PARCEL_CHANGES;
+
+const U32 REGION_FLAGS_PRELUDE_SET = REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+const U32 REGION_FLAGS_PRELUDE_UNSET = REGION_FLAGS_ALLOW_LANDMARK
+ | REGION_FLAGS_ALLOW_SET_HOME;
+
+const U32 REGION_FLAGS_ESTATE_MASK = REGION_FLAGS_EXTERNALLY_VISIBLE
+ | REGION_FLAGS_MAINLAND_VISIBLE
+ | REGION_FLAGS_PUBLIC_ALLOWED
+ | REGION_FLAGS_SUN_FIXED
+ | REGION_FLAGS_DENY_ANONYMOUS
+ | REGION_FLAGS_DENY_IDENTIFIED
+ | REGION_FLAGS_DENY_TRANSACTED;
+
+inline BOOL is_prelude( U32 flags )
+{
+ // definition of prelude does not depend on fixed-sun
+ return 0 == (flags & REGION_FLAGS_PRELUDE_UNSET)
+ && 0 != (flags & REGION_FLAGS_PRELUDE_SET);
+}
+
+inline U32 set_prelude_flags(U32 flags)
+{
+ // also set the sun-fixed flag
+ return ((flags & ~REGION_FLAGS_PRELUDE_UNSET)
+ | (REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
+}
+
+inline U32 unset_prelude_flags(U32 flags)
+{
+ // also unset the fixed-sun flag
+ return ((flags | REGION_FLAGS_PRELUDE_UNSET)
+ & ~(REGION_FLAGS_PRELUDE_SET | REGION_FLAGS_SUN_FIXED));
+}
+
+// estate constants. Need to match first few etries in indra.estate table.
+const U32 ESTATE_ALL = 0; // will not match in db, reserved key for logic
+const U32 ESTATE_MAINLAND = 1;
+const U32 ESTATE_ORIENTATION = 2;
+const U32 ESTATE_INTERNAL = 3;
+const U32 ESTATE_SHOWCASE = 4;
+const U32 ESTATE_KIDGRID = 5;
+const U32 ESTATE_LAST_LINDEN = 5; // last linden owned/managed estate
+
+// for EstateOwnerRequest, setaccess message
+const U32 ESTATE_ACCESS_ALLOWED_AGENTS = 1 << 0;
+const U32 ESTATE_ACCESS_ALLOWED_GROUPS = 1 << 1;
+const U32 ESTATE_ACCESS_BANNED_AGENTS = 1 << 2;
+const U32 ESTATE_ACCESS_MANAGERS = 1 << 3;
+
+//maximum number of access list entries we can fit in one packet
+const S32 ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET = 63;
+
+// for reply to "getinfo", don't need to forward to all sims in estate
+const U32 ESTATE_ACCESS_SEND_TO_AGENT_ONLY = 1 << 4;
+
+const U32 ESTATE_ACCESS_ALL = ESTATE_ACCESS_ALLOWED_AGENTS
+ | ESTATE_ACCESS_ALLOWED_GROUPS
+ | ESTATE_ACCESS_BANNED_AGENTS
+ | ESTATE_ACCESS_MANAGERS;
+
+// for EstateOwnerRequest, estateaccessdelta message
+const U32 ESTATE_ACCESS_APPLY_TO_ALL_ESTATES = 1 << 0;
+const U32 ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES = 1 << 1;
+
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_ADD = 1 << 2;
+const U32 ESTATE_ACCESS_ALLOWED_AGENT_REMOVE = 1 << 3;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_ADD = 1 << 4;
+const U32 ESTATE_ACCESS_ALLOWED_GROUP_REMOVE = 1 << 5;
+const U32 ESTATE_ACCESS_BANNED_AGENT_ADD = 1 << 6;
+const U32 ESTATE_ACCESS_BANNED_AGENT_REMOVE = 1 << 7;
+const U32 ESTATE_ACCESS_MANAGER_ADD = 1 << 8;
+const U32 ESTATE_ACCESS_MANAGER_REMOVE = 1 << 9;
+
+const S32 ESTATE_MAX_MANAGERS = 10;
+const S32 ESTATE_MAX_ACCESS_IDS = 300; // max for access, banned
+const S32 ESTATE_MAX_GROUP_IDS = (S32) ESTATE_ACCESS_MAX_ENTRIES_PER_PACKET;
+
+// 'Sim Wide Delete' flags
+const U32 SWD_OTHERS_LAND_ONLY = (1 << 0);
+const U32 SWD_ALWAYS_RETURN_OBJECTS = (1 << 1);
+const U32 SWD_SCRIPTED_ONLY = (1 << 2);
+
+#endif
+
diff --git a/indra/llmessage/llregionhandle.h b/indra/llmessage/llregionhandle.h
new file mode 100644
index 0000000000..41d104231c
--- /dev/null
+++ b/indra/llmessage/llregionhandle.h
@@ -0,0 +1,110 @@
+/**
+ * @file llregionhandle.h
+ * @brief Routines for converting positions to/from region handles.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONHANDLE_H
+#define LL_LLREGIONHANDLE_H
+
+#include <math.h>
+
+#include "indra_constants.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+inline U64 to_region_handle(const U32 x_origin, const U32 y_origin)
+{
+ U64 region_handle;
+ region_handle = ((U64)x_origin) << 32;
+ region_handle |= (U64) y_origin;
+ return region_handle;
+}
+
+inline U64 to_region_handle(const LLVector3d& pos_global)
+{
+ U32 global_x = (U32)pos_global.mdV[VX];
+ global_x -= global_x % 256;
+
+ U32 global_y = (U32)pos_global.mdV[VY];
+ global_y -= global_y % 256;
+
+ return to_region_handle(global_x, global_y);
+}
+
+inline U64 to_region_handle_global(const F32 x_global, const F32 y_global)
+{
+ // Round down to the nearest origin
+ U32 x_origin = (U32)x_global;
+ x_origin -= x_origin % REGION_WIDTH_U32;
+ U32 y_origin = (U32)y_global;
+ y_origin -= y_origin % REGION_WIDTH_U32;
+ U64 region_handle;
+ region_handle = ((U64)x_origin) << 32;
+ region_handle |= (U64) y_origin;
+ return region_handle;
+}
+
+inline BOOL to_region_handle(const F32 x_pos, const F32 y_pos, U64 *region_handle)
+{
+ U32 x_int, y_int;
+ if (x_pos < 0.f)
+ {
+// llwarns << "to_region_handle:Clamping negative x position " << x_pos << " to zero!" << llendl;
+ return FALSE;
+ }
+ else
+ {
+ x_int = (U32)llround(x_pos);
+ }
+ if (y_pos < 0.f)
+ {
+// llwarns << "to_region_handle:Clamping negative y position " << y_pos << " to zero!" << llendl;
+ return FALSE;
+ }
+ else
+ {
+ y_int = (U32)llround(y_pos);
+ }
+ *region_handle = to_region_handle(x_int, y_int);
+ return TRUE;
+}
+
+// stuff the word-frame XY location of sim's SouthWest corner in x_pos, y_pos
+inline void from_region_handle(const U64 &region_handle, F32 *x_pos, F32 *y_pos)
+{
+ *x_pos = (F32)((U32)(region_handle >> 32));
+ *y_pos = (F32)((U32)(region_handle & 0xFFFFFFFF));
+}
+
+// stuff the word-frame XY location of sim's SouthWest corner in x_pos, y_pos
+inline void from_region_handle(const U64 &region_handle, U32 *x_pos, U32 *y_pos)
+{
+ *x_pos = ((U32)(region_handle >> 32));
+ *y_pos = ((U32)(region_handle & 0xFFFFFFFF));
+}
+
+// return the word-frame XY location of sim's SouthWest corner in LLVector3d
+inline LLVector3d from_region_handle(const U64 &region_handle)
+{
+ return LLVector3d(((U32)(region_handle >> 32)), (U32)(region_handle & 0xFFFFFFFF), 0.f);
+}
+
+// grid-based region handle encoding. pass in a grid position
+// (eg: 1000,1000) and this will return the region handle.
+inline U64 grid_to_region_handle(U32 grid_x, U32 grid_y)
+{
+ return to_region_handle(grid_x * REGION_WIDTH_UNITS,
+ grid_y * REGION_WIDTH_UNITS);
+}
+
+inline void grid_from_region_handle(const U64& region_handle, U32* grid_x, U32* grid_y)
+{
+ from_region_handle(region_handle, grid_x, grid_y);
+ *grid_x /= REGION_WIDTH_UNITS;
+ *grid_y /= REGION_WIDTH_UNITS;
+}
+
+#endif
diff --git a/indra/llmessage/llsdappservices.cpp b/indra/llmessage/llsdappservices.cpp
new file mode 100644
index 0000000000..c10e9bd113
--- /dev/null
+++ b/indra/llmessage/llsdappservices.cpp
@@ -0,0 +1,259 @@
+/**
+ * @file llsdappservices.cpp
+ * @author Phoenix
+ * @date 2006-09-12
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdappservices.h"
+
+#include "llapp.h"
+#include "llhttpnode.h"
+#include "llsdserialize.h"
+
+void LLSDAppServices::useServices()
+{
+ /*
+ Having this function body here, causes the classes and globals in this
+ file to be linked into any program that uses the llmessage library.
+ */
+}
+
+class LLHTTPConfigService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("GET an array of all the options in priority order.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ LLSD result;
+ LLApp* app = LLApp::instance();
+ for(int ii = 0; ii < LLApp::PRIORITY_COUNT; ++ii)
+ {
+ result.append(app->getOptionData((LLApp::OptionPriority)ii));
+ }
+ return result;
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigService>
+ gHTTPRegistratiAppConfig("/app/config");
+
+class LLHTTPConfigRuntimeService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Manipulate a map of runtime-override options.");
+ desc.getAPI();
+ desc.postAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ return LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ }
+
+ virtual void post(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ LLSD result = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ LLSD::map_const_iterator iter = input.beginMap();
+ LLSD::map_const_iterator end = input.endMap();
+ for(; iter != end; ++iter)
+ {
+ result[(*iter).first] = (*iter).second;
+ }
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ result);
+ response->result(result);
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigRuntimeService>
+ gHTTPRegistrationRuntimeConfig("/app/config/runtime-override");
+
+class LLHTTPConfigRuntimeSingleService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Manipulate a single runtime-override option.");
+ desc.getAPI();
+ desc.putAPI();
+ desc.delAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ //llinfos << "validate: " << name << ", "
+ // << LLSDOStreamer<LLSDNotationFormatter>(context) << llendl;
+ if((std::string("PUT") == context["request"]["verb"].asString()) && !name.empty())
+ {
+ return true;
+ }
+ else
+ {
+ // This is for GET and DELETE
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ if(options.has(name)) return true;
+ else return false;
+ }
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ response->result(options[name]);
+ }
+
+ virtual void put(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context,
+ const LLSD& input) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ options[name] = input;
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ options);
+ response->result(input);
+ }
+
+ virtual void del(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ LLSD options = LLApp::instance()->getOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE);
+ options.erase(name);
+ LLApp::instance()->setOptionData(
+ LLApp::PRIORITY_RUNTIME_OVERRIDE,
+ options);
+ response->result(LLSD());
+ }
+};
+
+LLHTTPRegistration<LLHTTPConfigRuntimeSingleService>
+ gHTTPRegistrationRuntimeSingleConfig(
+ "/app/config/runtime-override/<option-name>");
+
+template<int PRIORITY>
+class LLHTTPConfigPriorityService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get a map of the options at this priority.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ response->result(LLApp::instance()->getOptionData(
+ (LLApp::OptionPriority)PRIORITY));
+ }
+};
+
+LLHTTPRegistration< LLHTTPConfigPriorityService<LLApp::PRIORITY_COMMAND_LINE> >
+ gHTTPRegistrationCommandLineConfig("/app/config/command-line");
+LLHTTPRegistration<
+ LLHTTPConfigPriorityService<LLApp::PRIORITY_SPECIFIC_CONFIGURATION> >
+ gHTTPRegistrationSpecificConfig("/app/config/specific");
+LLHTTPRegistration<
+ LLHTTPConfigPriorityService<LLApp::PRIORITY_GENERAL_CONFIGURATION> >
+ gHTTPRegistrationGeneralConfig("/app/config/general");
+LLHTTPRegistration< LLHTTPConfigPriorityService<LLApp::PRIORITY_DEFAULT> >
+ gHTTPRegistrationDefaultConfig("/app/config/default");
+
+class LLHTTPLiveConfigService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get a map of the currently live options.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ LLSD result;
+ LLApp* app = LLApp::instance();
+ LLSD::map_const_iterator iter;
+ LLSD::map_const_iterator end;
+ for(int ii = LLApp::PRIORITY_COUNT - 1; ii >= 0; --ii)
+ {
+ LLSD options = app->getOptionData((LLApp::OptionPriority)ii);
+ iter = options.beginMap();
+ end = options.endMap();
+ for(; iter != end; ++iter)
+ {
+ result[(*iter).first] = (*iter).second;
+ }
+ }
+ response->result(result);
+ }
+};
+
+LLHTTPRegistration<LLHTTPLiveConfigService>
+ gHTTPRegistrationLiveConfig("/app/config/live");
+
+class LLHTTPLiveConfigSingleService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("Get the named live option.");
+ desc.getAPI();
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ llinfos << "LLHTTPLiveConfigSingleService::validate(" << name
+ << ")" << llendl;
+ LLSD option = LLApp::instance()->getOption(name);
+ if(option) return true;
+ else return false;
+ }
+
+ virtual void get(
+ LLHTTPNode::ResponsePtr response,
+ const LLSD& context) const
+ {
+ std::string name = context["request"]["wildcard"]["option-name"];
+ response->result(LLApp::instance()->getOption(name));
+ }
+};
+
+LLHTTPRegistration<LLHTTPLiveConfigSingleService>
+ gHTTPRegistrationLiveSingleConfig("/app/config/live/<option-name>");
diff --git a/indra/llmessage/llsdappservices.h b/indra/llmessage/llsdappservices.h
new file mode 100644
index 0000000000..c9bc9570df
--- /dev/null
+++ b/indra/llmessage/llsdappservices.h
@@ -0,0 +1,40 @@
+/**
+ * @file llsdappservices.h
+ * @author Phoenix
+ * @date 2006-09-12
+ * @brief Header file to declare the /app common web services.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDAPPSERVICES_H
+#define LL_LLSDAPPSERVICES_H
+
+/**
+ * @class LLSDAppServices
+ * @brief This class forces a link to llsdappservices if the static
+ * method is called which declares the /app web services.
+ */
+class LLSDAppServices
+{
+public:
+ /**
+ * @brief Call this method to declare the /app common web services.
+ *
+ * This will register:
+ * /app/config
+ * /app/config/runtime-override
+ * /app/config/runtime-override/<option-name>
+ * /app/config/command-line
+ * /app/config/specific
+ * /app/config/general
+ * /app/config/default
+ * /app/config/live
+ * /app/config/live/<option-name>
+ */
+ static void useServices();
+};
+
+
+#endif // LL_LLSDAPPSERVICES_H
diff --git a/indra/llmessage/llsdhttpserver.cpp b/indra/llmessage/llsdhttpserver.cpp
new file mode 100644
index 0000000000..0eda0e69cb
--- /dev/null
+++ b/indra/llmessage/llsdhttpserver.cpp
@@ -0,0 +1,132 @@
+/**
+ * @file llsdhttpserver.cpp
+ * @brief Standard LLSD services
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdhttpserver.h"
+
+#include "llhttpnode.h"
+
+/**
+ * This module implements common services that should be included
+ * in all server URL trees. These services facilitate debugging and
+ * introsepction.
+ */
+
+void LLHTTPStandardServices::useServices()
+{
+ /*
+ Having this function body here, causes the classes and globals in this
+ file to be linked into any program that uses the llmessage library.
+ */
+}
+
+
+
+class LLHTTPHelloService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("says hello");
+ desc.getAPI();
+ desc.output("\"hello\"");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD get() const
+ {
+ LLSD result = "hello";
+ return result;
+ }
+};
+
+LLHTTPRegistration<LLHTTPHelloService>
+ gHTTPRegistrationWebHello("/web/hello");
+
+
+
+class LLHTTPEchoService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("echo input");
+ desc.postAPI();
+ desc.input("<any>");
+ desc.output("<the input>");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual LLSD post(const LLSD& params) const
+ {
+ return params;
+ }
+};
+
+LLHTTPRegistration<LLHTTPEchoService>
+ gHTTPRegistrationWebEcho("/web/echo");
+
+
+
+class LLAPIService : public LLHTTPNode
+{
+public:
+ virtual void describe(Description& desc) const
+ {
+ desc.shortInfo("information about the URLs this server supports");
+ desc.getAPI();
+ desc.output("a list of URLs supported");
+ desc.source(__FILE__, __LINE__);
+ }
+
+ virtual bool handles(const LLSD& remainder, LLSD& context) const
+ {
+ return followRemainder(remainder) != NULL;
+ }
+
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ const LLSD& remainder = context["request"]["remainder"];
+
+ if (remainder.size() > 0)
+ {
+ const LLHTTPNode* node = followRemainder(remainder);
+ if (!node)
+ {
+ response->notFound();
+ return;
+ }
+
+ Description desc;
+ node->describe(desc);
+ response->result(desc.getInfo());
+ return;
+ }
+
+ response->result(rootNode()->allNodePaths());
+ }
+
+private:
+ const LLHTTPNode* followRemainder(const LLSD& remainder) const
+ {
+ const LLHTTPNode* node = rootNode();
+
+ LLSD::array_const_iterator i = remainder.beginArray();
+ LLSD::array_const_iterator end = remainder.endArray();
+ for (; node && i != end; ++i)
+ {
+ node = node->findNode(*i);
+ }
+
+ return node;
+ }
+};
+
+LLHTTPRegistration<LLAPIService>
+ gHTTPRegistrationWebServerApi("/web/server/api");
+
diff --git a/indra/llmessage/llsdhttpserver.h b/indra/llmessage/llsdhttpserver.h
new file mode 100644
index 0000000000..11ff38955b
--- /dev/null
+++ b/indra/llmessage/llsdhttpserver.h
@@ -0,0 +1,33 @@
+/**
+ * @file llsdhttpserver.h
+ * @brief Standard LLSD services
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDHTTPSERVER_H
+#define LL_LLSDHTTPSERVER_H
+
+/**
+ * This module implements and defines common services that should be included
+ * in all server URL trees. These services facilitate debugging and
+ * introsepction.
+ */
+
+class LLHTTPStandardServices
+{
+public:
+ static void useServices();
+ /**<
+ Having a call to this function causes the following services to be
+ registered:
+ /web/echo -- echo input
+ /web/hello -- return "hello"
+ /web/server/api -- return a list of url paths on the server
+ /web/server/api/<..path..>
+ -- return description of the path
+ */
+};
+
+#endif // LL_LLSDHTTPSERVER_H
diff --git a/indra/llmessage/llsdrpcclient.cpp b/indra/llmessage/llsdrpcclient.cpp
new file mode 100644
index 0000000000..4832ddaa58
--- /dev/null
+++ b/indra/llmessage/llsdrpcclient.cpp
@@ -0,0 +1,230 @@
+/**
+ * @file llsdrpcclient.cpp
+ * @author Phoenix
+ * @date 2005-11-05
+ * @brief Implementation of the llsd client classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdrpcclient.h"
+
+#include "llbufferstream.h"
+#include "llfiltersd2xmlrpc.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llurlrequest.h"
+
+/**
+ * String constants
+ */
+static std::string LLSDRPC_RESPONSE_NAME("response");
+static std::string LLSDRPC_FAULT_NAME("fault");
+
+/**
+ * LLSDRPCResponse
+ */
+LLSDRPCResponse::LLSDRPCResponse() :
+ mIsError(false),
+ mIsFault(false)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+// virtual
+LLSDRPCResponse::~LLSDRPCResponse()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+bool LLSDRPCResponse::extractResponse(const LLSD& sd)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ bool rv = true;
+ if(sd.has(LLSDRPC_RESPONSE_NAME))
+ {
+ mReturnValue = sd[LLSDRPC_RESPONSE_NAME];
+ mIsFault = false;
+ }
+ else if(sd.has(LLSDRPC_FAULT_NAME))
+ {
+ mReturnValue = sd[LLSDRPC_FAULT_NAME];
+ mIsFault = true;
+ }
+ else
+ {
+ mReturnValue.clear();
+ mIsError = true;
+ rv = false;
+ }
+ return rv;
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCResponse::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ if(mIsError)
+ {
+ error(pump);
+ }
+ else if(mIsFault)
+ {
+ fault(pump);
+ }
+ else
+ {
+ response(pump);
+ }
+ PUMP_DEBUG;
+ return STATUS_DONE;
+}
+
+/**
+ * LLSDRPCClient
+ */
+
+LLSDRPCClient::LLSDRPCClient() :
+ mState(STATE_NONE),
+ mQueue(EPBQ_PROCESS)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+// virtual
+LLSDRPCClient::~LLSDRPCClient()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+}
+
+bool LLSDRPCClient::call(
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ //llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")"
+ // << llendl;
+ if(method.empty() || !response)
+ {
+ return false;
+ }
+ mState = STATE_READY;
+ mURI.assign(uri);
+ std::stringstream req;
+ req << LLSDRPC_REQUEST_HEADER_1 << method
+ << LLSDRPC_REQUEST_HEADER_2;
+ LLSDSerialize::toNotation(parameter, req);
+ req << LLSDRPC_REQUEST_FOOTER;
+ mRequest = req.str();
+ mQueue = queue;
+ mResponse = response;
+ return true;
+}
+
+bool LLSDRPCClient::call(
+ const std::string& uri,
+ const std::string& method,
+ const std::string& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ //llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")"
+ // << llendl;
+ if(method.empty() || parameter.empty() || !response)
+ {
+ return false;
+ }
+ mState = STATE_READY;
+ mURI.assign(uri);
+ std::stringstream req;
+ req << LLSDRPC_REQUEST_HEADER_1 << method
+ << LLSDRPC_REQUEST_HEADER_2 << parameter
+ << LLSDRPC_REQUEST_FOOTER;
+ mRequest = req.str();
+ mQueue = queue;
+ mResponse = response;
+ return true;
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCClient::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
+ if((STATE_NONE == mState) || (!pump))
+ {
+ // You should have called the call() method already.
+ return STATUS_PRECONDITION_NOT_MET;
+ }
+ EStatus rv = STATUS_DONE;
+ switch(mState)
+ {
+ case STATE_READY:
+ {
+ PUMP_DEBUG;
+// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl;
+ buffer->append(
+ channels.out(),
+ (U8*)mRequest.c_str(),
+ mRequest.length());
+ context[CONTEXT_DEST_URI_SD_LABEL] = mURI;
+ mState = STATE_WAITING_FOR_RESPONSE;
+ break;
+ }
+ case STATE_WAITING_FOR_RESPONSE:
+ {
+ PUMP_DEBUG;
+ // The input channel has the sd response in it.
+ //lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE"
+ // << llendl;
+ LLBufferStream resp(channels, buffer.get());
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, resp);
+ LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get();
+ if (!response)
+ {
+ mState = STATE_DONE;
+ break;
+ }
+ response->extractResponse(sd);
+ if(EPBQ_PROCESS == mQueue)
+ {
+ LLPumpIO::chain_t chain;
+ chain.push_back(mResponse);
+ pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
+ }
+ else
+ {
+ pump->respond(mResponse.get());
+ }
+ mState = STATE_DONE;
+ break;
+ }
+ case STATE_DONE:
+ default:
+ PUMP_DEBUG;
+ llinfos << "invalid state to process" << llendl;
+ rv = STATUS_ERROR;
+ break;
+ }
+ return rv;
+}
diff --git a/indra/llmessage/llsdrpcclient.h b/indra/llmessage/llsdrpcclient.h
new file mode 100644
index 0000000000..173a0d1dbb
--- /dev/null
+++ b/indra/llmessage/llsdrpcclient.h
@@ -0,0 +1,291 @@
+/**
+ * @file llsdrpcclient.h
+ * @author Phoenix
+ * @date 2005-11-05
+ * @brief Implementation and helpers for structure data RPC clients.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDRPCCLIENT_H
+#define LL_LLSDRPCCLIENT_H
+
+/**
+ * This file declares classes to encapsulate a basic structured data
+ * remote procedure client.
+ */
+
+#include "llchainio.h"
+#include "llfiltersd2xmlrpc.h"
+#include "lliopipe.h"
+#include "llurlrequest.h"
+
+/**
+ * @class LLSDRPCClientResponse
+ * @brief Abstract base class to represent a response from an SD server.
+ *
+ * This is used as a base class for callbacks generated from an
+ * structured data remote procedure call. The
+ * <code>extractResponse</code> method will deal with the llsdrpc method
+ * call overhead, and keep track of what to call during the next call
+ * into <code>process</code>. If you use this as a base class, you
+ * need to implement <code>response</code>, <code>fault</code>, and
+ * <code>error</code> to do something useful. When in those methods,
+ * you can parse and utilize the mReturnValue member data.
+ */
+class LLSDRPCResponse : public LLIOPipe
+{
+public:
+ LLSDRPCResponse();
+ virtual ~LLSDRPCResponse();
+
+ /**
+ * @brief This method extracts the response out of the sd passed in
+ *
+ * Any appropriate data found in the sd passed in will be
+ * extracted and managed by this object - not copied or cloned. It
+ * will still be up to the caller to delete the pointer passed in.
+ * @param sd The raw structured data response from the remote server.
+ * @return Returns true if this was able to parse the structured data.
+ */
+ bool extractResponse(const LLSD& sd);
+
+protected:
+ /**
+ * @brief Method called when the response is ready.
+ */
+ virtual bool response(LLPumpIO* pump) = 0;
+
+ /**
+ * @brief Method called when a fault is generated by the remote server.
+ */
+ virtual bool fault(LLPumpIO* pump) = 0;
+
+ /**
+ * @brief Method called when there was an error
+ */
+ virtual bool error(LLPumpIO* pump) = 0;
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLSD mReturnValue;
+ bool mIsError;
+ bool mIsFault;
+};
+
+/**
+ * @class LLSDRPCClient
+ * @brief Client class for a structured data remote procedure call.
+ *
+ * This class helps deal with making structured data calls to a remote
+ * server. You can visualize the calls as:
+ * <code>
+ * response = uri.method(parameter)
+ * </code>
+ * where you pass in everything to <code>call</code> and this class
+ * takes care of the rest of the details.
+ * In typical usage, you will derive a class from this class and
+ * provide an API more useful for the specific application at
+ * hand. For example, if you were writing a service to send an instant
+ * message, you could create an API for it to send the messsage, and
+ * that class would do the work of translating it into the method and
+ * parameter, find the destination, and invoke <code>call</call> with
+ * a useful implementation of LLSDRPCResponse passed in to handle the
+ * response from the network.
+ */
+class LLSDRPCClient : public LLIOPipe
+{
+public:
+ LLSDRPCClient();
+ virtual ~LLSDRPCClient();
+
+ /**
+ * @brief Enumeration for tracking which queue to process the
+ * response.
+ */
+ enum EPassBackQueue
+ {
+ EPBQ_PROCESS,
+ EPBQ_CALLBACK,
+ };
+
+ /**
+ * @brief Call a method on a remote LLSDRPCServer
+ *
+ * @param uri The remote object to call, eg,
+ * http://localhost/usher. If you are using a factory with a fixed
+ * url, the uri passed in will probably be ignored.
+ * @param method The method to call on the remote object
+ * @param parameter The parameter to pass into the remote
+ * object. It is up to the caller to delete the value passed in.
+ * @param response The object which gets the response.
+ * @param queue Specifies to call the response on the process or
+ * callback queue.
+ * @return Returns true if this object will be able to make the RPC call.
+ */
+ bool call(
+ const std::string& uri,
+ const std::string& method,
+ const LLSD& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue);
+
+ /**
+ * @brief Call a method on a remote LLSDRPCServer
+ *
+ * @param uri The remote object to call, eg,
+ * http://localhost/usher. If you are using a factory with a fixed
+ * url, the uri passed in will probably be ignored.
+ * @param method The method to call on the remote object
+ * @param parameter The seriailized parameter to pass into the
+ * remote object.
+ * @param response The object which gets the response.
+ * @param queue Specifies to call the response on the process or
+ * callback queue.
+ * @return Returns true if this object will be able to make the RPC call.
+ */
+ bool call(
+ const std::string& uri,
+ const std::string& method,
+ const std::string& parameter,
+ LLSDRPCResponse* response,
+ EPassBackQueue queue);
+
+protected:
+ /**
+ * @brief Enumeration for tracking client state.
+ */
+ enum EState
+ {
+ STATE_NONE,
+ STATE_READY,
+ STATE_WAITING_FOR_RESPONSE,
+ STATE_DONE
+ };
+
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ EState mState;
+ std::string mURI;
+ std::string mRequest;
+ EPassBackQueue mQueue;
+ LLIOPipe::ptr_t mResponse;
+};
+
+/**
+ * @class LLSDRPCClientFactory
+ * @brief Basic implementation for making an SD RPC client factory
+ *
+ * This class eases construction of a basic sd rpc client. Here is an
+ * example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }
+ * LLService::registerCreator(
+ * "useful",
+ * LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>))
+ * </code>
+ */
+template<class Client>
+class LLSDRPCClientFactory : public LLChainIOFactory
+{
+public:
+ LLSDRPCClientFactory() {}
+ LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLSDRPCClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+ LLIOPipe::ptr_t http_pipe(http);
+ http->addHeader("Content-Type: text/llsd");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(http_pipe);
+ chain.push_back(service);
+ return true;
+ }
+protected:
+ std::string mURL;
+};
+
+/**
+ * @class LLXMLSDRPCClientFactory
+ * @brief Basic implementation for making an XMLRPC to SD RPC client factory
+ *
+ * This class eases construction of a basic sd rpc client which uses
+ * xmlrpc as a serialization grammar. Here is an example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }
+ * LLService::registerCreator(
+ * "useful",
+ * LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>))
+ * </code>
+ */
+template<class Client>
+class LLXMLSDRPCClientFactory : public LLChainIOFactory
+{
+public:
+ LLXMLSDRPCClientFactory() {}
+ LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(LLURLRequest::HTTP_POST));
+ LLIOPipe::ptr_t http_pipe(http);
+ http->addHeader("Content-Type: text/xml");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL)));
+ chain.push_back(http_pipe);
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
+ chain.push_back(service);
+ return true;
+ }
+protected:
+ std::string mURL;
+};
+
+#endif // LL_LLSDRPCCLIENT_H
diff --git a/indra/llmessage/llsdrpcserver.cpp b/indra/llmessage/llsdrpcserver.cpp
new file mode 100644
index 0000000000..0348147a71
--- /dev/null
+++ b/indra/llmessage/llsdrpcserver.cpp
@@ -0,0 +1,322 @@
+/**
+ * @file llsdrpcserver.cpp
+ * @author Phoenix
+ * @date 2005-10-11
+ * @brief Implementation of the LLSDRPCServer and related classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llsdrpcserver.h"
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsdserialize.h"
+#include "llstl.h"
+
+static const char FAULT_PART_1[] = "{'fault':{'code':i";
+static const char FAULT_PART_2[] = ", 'description':'";
+static const char FAULT_PART_3[] = "'}}";
+
+static const char RESPONSE_PART_1[] = "{'response':";
+static const char RESPONSE_PART_2[] = "}";
+
+static const S32 FAULT_GENERIC = 1000;
+static const S32 FAULT_METHOD_NOT_FOUND = 1001;
+
+static const std::string LLSDRPC_METHOD_SD_NAME("method");
+static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter");
+
+
+/**
+ * LLSDRPCServer
+ */
+LLSDRPCServer::LLSDRPCServer() :
+ mState(LLSDRPCServer::STATE_NONE),
+ mPump(NULL),
+ mLock(0)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+}
+
+LLSDRPCServer::~LLSDRPCServer()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ std::for_each(
+ mMethods.begin(),
+ mMethods.end(),
+ llcompose1(
+ DeletePointerFunctor<LLSDRPCMethodCallBase>(),
+ llselect2nd<method_map_t::value_type>()));
+ std::for_each(
+ mCallbackMethods.begin(),
+ mCallbackMethods.end(),
+ llcompose1(
+ DeletePointerFunctor<LLSDRPCMethodCallBase>(),
+ llselect2nd<method_map_t::value_type>()));
+}
+
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::deferredResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data) {
+ // subclass should provide a sane implementation
+ return ESDRPCS_DONE;
+}
+
+void LLSDRPCServer::clearLock()
+{
+ if(mLock && mPump)
+ {
+ mPump->clearLock(mLock);
+ mPump = NULL;
+ mLock = 0;
+ }
+}
+
+// virtual
+LLIOPipe::EStatus LLSDRPCServer::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+// lldebugs << "LLSDRPCServer::process_impl" << llendl;
+ // Once we have all the data, We need to read the sd on
+ // the the in channel, and respond on the out channel
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ std::string method_name;
+ LLIOPipe::EStatus status = STATUS_DONE;
+
+ switch(mState)
+ {
+ case STATE_DEFERRED:
+ PUMP_DEBUG;
+ if(ESDRPCS_DONE != deferredResponse(channels, buffer.get()))
+ {
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "deferred response failed.");
+ }
+ mState = STATE_DONE;
+ return STATUS_DONE;
+
+ case STATE_DONE:
+// lldebugs << "STATE_DONE" << llendl;
+ break;
+ case STATE_CALLBACK:
+// lldebugs << "STATE_CALLBACK" << llendl;
+ PUMP_DEBUG;
+ method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
+ if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
+ {
+ if(ESDRPCS_DONE != callbackMethod(
+ method_name,
+ mRequest[LLSDRPC_PARAMETER_SD_NAME],
+ channels,
+ buffer.get()))
+ {
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Callback method call failed.");
+ }
+ }
+ else
+ {
+ // this should never happen, since we should not be in
+ // this state unless we originally found a method and
+ // params during the first call to process.
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Invalid LLSDRPC sever state - callback without method.");
+ }
+ pump->clearLock(mLock);
+ mLock = 0;
+ mState = STATE_DONE;
+ break;
+ case STATE_NONE:
+// lldebugs << "STATE_NONE" << llendl;
+ default:
+ {
+ // First time we got here - process the SD request, and call
+ // the method.
+ PUMP_DEBUG;
+ LLBufferStream istr(channels, buffer.get());
+ mRequest.clear();
+ LLSDSerialize::fromNotation(mRequest, istr);
+
+ // { 'method':'...', 'parameter': ... }
+ method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
+ if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
+ {
+ ESDRPCSStatus rv = callMethod(
+ method_name,
+ mRequest[LLSDRPC_PARAMETER_SD_NAME],
+ channels,
+ buffer.get());
+ switch(rv)
+ {
+ case ESDRPCS_DEFERRED:
+ mPump = pump;
+ mLock = pump->setLock();
+ mState = STATE_DEFERRED;
+ status = STATUS_BREAK;
+ break;
+
+ case ESDRPCS_CALLBACK:
+ {
+ mState = STATE_CALLBACK;
+ LLPumpIO::LLLinkInfo link;
+ link.mPipe = LLIOPipe::ptr_t(this);
+ link.mChannels = channels;
+ LLPumpIO::links_t links;
+ links.push_back(link);
+ pump->respond(links, buffer, context);
+ mLock = pump->setLock();
+ status = STATUS_BREAK;
+ break;
+ }
+ case ESDRPCS_DONE:
+ mState = STATE_DONE;
+ break;
+ case ESDRPCS_ERROR:
+ default:
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Method call failed.");
+ break;
+ }
+ }
+ else
+ {
+ // send a fault
+ buildFault(
+ channels,
+ buffer.get(),
+ FAULT_GENERIC,
+ "Unable to find method and parameter in request.");
+ }
+ break;
+ }
+ }
+
+ PUMP_DEBUG;
+ return status;
+}
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::callMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ // Try to find the method in the method table.
+ ESDRPCSStatus rv = ESDRPCS_DONE;
+ method_map_t::iterator it = mMethods.find(method);
+ if(it != mMethods.end())
+ {
+ rv = (*it).second->call(params, channels, response);
+ }
+ else
+ {
+ it = mCallbackMethods.find(method);
+ if(it == mCallbackMethods.end())
+ {
+ // method not found.
+ std::ostringstream message;
+ message << "rpc server unable to find method: " << method;
+ buildFault(
+ channels,
+ response,
+ FAULT_METHOD_NOT_FOUND,
+ message.str());
+ }
+ else
+ {
+ // we found it in the callback methods - tell the process
+ // to coordinate calling on the pump callback.
+ return ESDRPCS_CALLBACK;
+ }
+ }
+ return rv;
+}
+
+// virtual
+ESDRPCSStatus LLSDRPCServer::callbackMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ // Try to find the method in the callback method table.
+ ESDRPCSStatus rv = ESDRPCS_DONE;
+ method_map_t::iterator it = mCallbackMethods.find(method);
+ if(it != mCallbackMethods.end())
+ {
+ rv = (*it).second->call(params, channels, response);
+ }
+ else
+ {
+ std::ostringstream message;
+ message << "pcserver unable to find callback method: " << method;
+ buildFault(
+ channels,
+ response,
+ FAULT_METHOD_NOT_FOUND,
+ message.str());
+ }
+ return rv;
+}
+
+// static
+void LLSDRPCServer::buildFault(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ S32 code,
+ const std::string& msg)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ LLBufferStream ostr(channels, data);
+ ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3;
+ llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl;
+}
+
+// static
+void LLSDRPCServer::buildResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ const LLSD& response)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
+ LLBufferStream ostr(channels, data);
+ ostr << RESPONSE_PART_1;
+ LLSDSerialize::toNotation(response, ostr);
+ ostr << RESPONSE_PART_2;
+#if LL_DEBUG
+ std::ostringstream debug_ostr;
+ debug_ostr << "LLSDRPCServer::buildResponse: ";
+ LLSDSerialize::toNotation(response, debug_ostr);
+ llinfos << debug_ostr.str() << llendl;
+#endif
+}
diff --git a/indra/llmessage/llsdrpcserver.h b/indra/llmessage/llsdrpcserver.h
new file mode 100644
index 0000000000..abb291d007
--- /dev/null
+++ b/indra/llmessage/llsdrpcserver.h
@@ -0,0 +1,342 @@
+/**
+ * @file llsdrpcserver.h
+ * @author Phoenix
+ * @date 2005-10-11
+ * @brief Declaration of the structured data remote procedure call server.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSDRPCSERVER_H
+#define LL_LLSDRPCSERVER_H
+
+/**
+ * I've set this up to be pretty easy to use when you want to make a
+ * structured data rpc server which responds to methods by
+ * name. Derive a class from the LLSDRPCServer, and during
+ * construction (or initialization if you have the luxury) map method
+ * names to pointers to member functions. This will look a lot like:
+ *
+ * <code>
+ * class LLMessageAgents : public LLSDRPCServer {<br>
+ * public:<br>
+ * typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
+ * LLMessageAgents() {<br>
+ * mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
+ * mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
+ * }<br>
+ * protected:<br>
+ * rpc_IM(const LLSD& params,
+ * const LLChannelDescriptors& channels,
+ * LLBufferArray* data)
+ * {...}<br>
+ * rpc_Alert(const LLSD& params,
+ * const LLChannelDescriptors& channels,
+ * LLBufferArray* data)
+ * {...}<br>
+ * };<br>
+ * </code>
+ *
+ * The params are an array where each element in the array is a single
+ * parameter in the call.
+ *
+ * It is up to you to pack a valid serialized llsd response into the
+ * data object passed into the method, but you can use the helper
+ * methods below to help.
+ */
+
+#include <map>
+#include "lliopipe.h"
+#include "lliohttpserver.h"
+#include "llfiltersd2xmlrpc.h"
+
+class LLSD;
+
+/**
+ * @brief Enumeration for specifying server method call status. This
+ * enumeration controls how the server class will manage the pump
+ * process/callback mechanism.
+ */
+enum ESDRPCSStatus
+{
+ // The call went ok, but the response is not yet ready. The
+ // method will arrange for the clearLock() call to be made at
+ // a later date, after which, once the chain is being pumped
+ // again, deferredResponse() will be called to gather the result
+ ESDRPCS_DEFERRED,
+
+ // The LLSDRPCServer would like to handle the method on the
+ // callback queue of the pump.
+ ESDRPCS_CALLBACK,
+
+ // The method call finished and generated output.
+ ESDRPCS_DONE,
+
+ // Method failed for some unspecified reason - you should avoid
+ // this. A generic fault will be sent to the output.
+ ESDRPCS_ERROR,
+
+ ESDRPCS_COUNT,
+};
+
+/**
+ * @class LLSDRPCMethodCallBase
+ * @brief Base class for calling a member function in an sd rpcserver
+ * implementation.
+ */
+class LLSDRPCMethodCallBase
+{
+public:
+ LLSDRPCMethodCallBase() {}
+ virtual ~LLSDRPCMethodCallBase() {}
+
+ virtual ESDRPCSStatus call(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* response) = 0;
+protected:
+};
+
+/**
+ * @class LLSDRPCMethodCall
+ * @brief Class which implements member function calls.
+ */
+template<class Server>
+class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
+{
+public:
+ typedef ESDRPCSStatus (Server::*mem_fn)(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+ LLSDRPCMethodCall(Server* s, mem_fn fn) :
+ mServer(s),
+ mMemFn(fn)
+ {
+ }
+ virtual ~LLSDRPCMethodCall() {}
+ virtual ESDRPCSStatus call(
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data)
+ {
+ return (*mServer.*mMemFn)(params, channels, data);
+ }
+
+protected:
+ Server* mServer;
+ mem_fn mMemFn;
+ //bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
+};
+
+
+/**
+ * @class LLSDRPCServer
+ * @brief Basic implementation of a structure data rpc server
+ *
+ * The rpc server is also designed to appropriately straddle the pump
+ * <code>process()</code> and <code>callback()</code> to specify which
+ * thread you want to work on when handling a method call. The
+ * <code>mMethods</code> methods are called from
+ * <code>process()</code>, while the <code>mCallbackMethods</code> are
+ * called when a pump is in a <code>callback()</code> cycle.
+ */
+class LLSDRPCServer : public LLIOPipe
+{
+public:
+ LLSDRPCServer();
+ virtual ~LLSDRPCServer();
+
+ /**
+ * enumeration for generic fault codes
+ */
+ enum
+ {
+ FAULT_BAD_REQUEST = 2000,
+ FAULT_NO_RESPONSE = 2001,
+ };
+
+ /**
+ * @brief Call this method to return an rpc fault.
+ *
+ * @param channel The channel for output on the data buffer
+ * @param data buffer which will recieve the final output
+ * @param code The fault code
+ * @param msg The fault message
+ */
+ static void buildFault(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ S32 code,
+ const std::string& msg);
+
+ /**
+ * @brief Call this method to build an rpc response.
+ *
+ * @param channel The channel for output on the data buffer
+ * @param data buffer which will recieve the final output
+ * @param response The return value from the method call
+ */
+ static void buildResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data,
+ const LLSD& response);
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+
+ /**
+ * @brief Enumeration to track the state of the rpc server instance
+ */
+ enum EState
+ {
+ STATE_NONE,
+ STATE_CALLBACK,
+ STATE_DEFERRED,
+ STATE_DONE
+ };
+
+ /**
+ * @brief This method is called when an http post comes in.
+ *
+ * The default behavior is to look at the method name, look up the
+ * method in the method table, and call it. If the method is not
+ * found, this function will build a fault response. You can
+ * implement your own version of this function if you want to hard
+ * wire some behavior or optimize things a bit.
+ * @param method The method name being called
+ * @param params The parameters
+ * @param channel The channel for output on the data buffer
+ * @param data The http data
+ * @return Returns the status of the method call, done/deferred/etc
+ */
+ virtual ESDRPCSStatus callMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ /**
+ * @brief This method is called when a pump callback is processed.
+ *
+ * The default behavior is to look at the method name, look up the
+ * method in the callback method table, and call it. If the method
+ * is not found, this function will build a fault response. You
+ * can implement your own version of this function if you want to
+ * hard wire some behavior or optimize things a bit.
+ * @param method The method name being called
+ * @param params The parameters
+ * @param channel The channel for output on the data buffer
+ * @param data The http data
+ * @return Returns the status of the method call, done/deferred/etc
+ */
+ virtual ESDRPCSStatus callbackMethod(
+ const std::string& method,
+ const LLSD& params,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ /**
+ * @brief Called after a deferred service is unlocked
+ *
+ * If a method returns ESDRPCS_DEFERRED, then the service chain
+ * will be locked and not processed until some other system calls
+ * clearLock() on the service instance again. At that point,
+ * once the pump starts processing the chain again, this method
+ * will be called so the service can output the final result
+ * into the buffers.
+ */
+ virtual ESDRPCSStatus deferredResponse(
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data);
+
+ // donovan put this public here 7/27/06
+public:
+ /**
+ * @brief unlock a service that as ESDRPCS_DEFERRED
+ */
+ void clearLock();
+
+protected:
+ EState mState;
+ LLSD mRequest;
+ LLPumpIO* mPump;
+ S32 mLock;
+ typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
+ method_map_t mMethods;
+ method_map_t mCallbackMethods;
+};
+
+/**
+ * @name Helper Templates for making LLHTTPNodes
+ *
+ * These templates help in creating nodes for handing a service from
+ * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
+ *
+ * To use it:
+ * \code
+ * class LLUsefulServer : public LLSDRPCServer { ... }
+ *
+ * LLHTTPNode& root = LLCreateHTTPWireServer(...);
+ * root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
+ * root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
+ * \endcode
+ */
+//@{
+
+template<class Server>
+class LLSDRPCServerFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
+ chain.push_back(LLIOPipe::ptr_t(new Server));
+ return true;
+ }
+};
+
+template<class Server>
+class LLSDRPCNode : public LLHTTPNodeForFactory<
+ LLSDRPCServerFactory<Server> >
+{
+};
+
+template<class Server>
+class LLXMLRPCServerFactory : public LLChainIOFactory
+{
+public:
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
+ chain.push_back(LLIOPipe::ptr_t(new Server));
+ chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
+ return true;
+ }
+};
+
+template<class Server>
+class LLXMLRPCNode : public LLHTTPNodeForFactory<
+ LLXMLRPCServerFactory<Server> >
+{
+};
+
+//@}
+
+#endif // LL_LLSDRPCSERVER_H
diff --git a/indra/llmessage/llservice.cpp b/indra/llmessage/llservice.cpp
new file mode 100644
index 0000000000..03b04054dc
--- /dev/null
+++ b/indra/llmessage/llservice.cpp
@@ -0,0 +1,93 @@
+/**
+ * @file llservice.cpp
+ * @author Phoenix
+ * @date 2005-04-20
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llservice.h"
+
+LLService::creators_t LLService::sCreatorFunctors;
+
+LLService::LLService()
+{
+}
+
+LLService::~LLService()
+{
+}
+
+// static
+bool LLService::registerCreator(const std::string& name, creator_t fn)
+{
+ llinfos << "LLService::registerCreator(" << name << ")" << llendl;
+ if(name.empty())
+ {
+ return false;
+ }
+
+ creators_t::value_type vt(name, fn);
+ std::pair<creators_t::iterator, bool> rv = sCreatorFunctors.insert(vt);
+ return rv.second;
+
+ // alternately...
+ //std::string name_str(name);
+ //sCreatorFunctors[name_str] = fn;
+}
+
+// static
+LLIOPipe* LLService::activate(
+ const std::string& name,
+ LLPumpIO::chain_t& chain,
+ LLSD context)
+{
+ if(name.empty())
+ {
+ llinfos << "LLService::activate - no service specified." << llendl;
+ return NULL;
+ }
+ creators_t::iterator it = sCreatorFunctors.find(name);
+ LLIOPipe* rv = NULL;
+ if(it != sCreatorFunctors.end())
+ {
+ if((*it).second->build(chain, context))
+ {
+ rv = chain[0].get();
+ }
+ else
+ {
+ // empty out the chain, because failed service creation
+ // should just discard this stuff.
+ llwarns << "LLService::activate - unable to build chain: " << name
+ << llendl;
+ chain.clear();
+ }
+ }
+ else
+ {
+ llwarns << "LLService::activate - unable find factory: " << name
+ << llendl;
+ }
+ return rv;
+}
+
+// static
+bool LLService::discard(const std::string& name)
+{
+ if(name.empty())
+ {
+ return false;
+ }
+ creators_t::iterator it = sCreatorFunctors.find(name);
+ if(it != sCreatorFunctors.end())
+ {
+ //(*it).second->discard();
+ sCreatorFunctors.erase(it);
+ return true;
+ }
+ return false;
+}
+
diff --git a/indra/llmessage/llservice.h b/indra/llmessage/llservice.h
new file mode 100644
index 0000000000..e243e710d6
--- /dev/null
+++ b/indra/llmessage/llservice.h
@@ -0,0 +1,167 @@
+/**
+ * @file llservice.h
+ * @author Phoenix
+ * @date 2004-11-21
+ * @brief Declaration file for LLService and related classes.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSERVICE_H
+#define LL_LLSERVICE_H
+
+#include <string>
+#include <map>
+//#include <boost/intrusive_ptr.hpp>
+//#include <boost/shared_ptr.hpp>
+
+//#include "llframetimer.h"
+#include "lliopipe.h"
+#include "llchainio.h"
+
+#if 0
+class LLServiceCreator;
+/**
+ * intrusive pointer support
+ */
+namespace boost
+{
+ void intrusive_ptr_add_ref(LLServiceCreator* p);
+ void intrusive_ptr_release(LLServiceCreator* p);
+};
+#endif
+
+/**
+ * @class LLServiceCreator
+ * @brief This class is an abstract base class for classes which create
+ * new <code>LLService</code> instances.
+ *
+ * Derive classes from this class which appropriately implement the
+ * <code>operator()</code> and destructor.
+ * @see LLService
+ */
+#if 0
+class LLServiceCreator
+{
+public:
+ typedef boost::intrusive_ptr<LLService> service_t;
+ virtual ~LLServiceCreator() {}
+ virtual service_t activate() = 0;
+ virtual void discard() = 0;
+
+protected:
+ LLServiceCreator() : mReferenceCount(0)
+ {
+ }
+
+private:
+ friend void boost::intrusive_ptr_add_ref(LLServiceCreator* p);
+ friend void boost::intrusive_ptr_release(LLServiceCreator* p);
+ U32 mReferenceCount;
+};
+#endif
+
+#if 0
+namespace boost
+{
+ inline void intrusive_ptr_add_ref(LLServiceCreator* p)
+ {
+ ++p->mReferenceCount;
+ }
+ inline void intrusive_ptr_release(LLServiceCreator* p)
+ {
+ if(0 == --p->mReferenceCount)
+ {
+ delete p;
+ }
+ }
+};
+#endif
+
+/**
+ * @class LLService
+ * @brief This class is the base class for the service classes.
+ * @see LLIOPipe
+ *
+ * The services map a string to a chain factory with a known interface
+ * at the front of the chain. So, to activate a service, call
+ * <code>activate()</code> with the name of the service needed which
+ * will call the associated factory, and return a pointer to the
+ * known interface.
+ * <b>NOTE:</b> If you are implementing a service factory, it is
+ * vitally important that the service pipe is at the front of the
+ * chain.
+ */
+class LLService : public LLIOPipe
+{
+public:
+ //typedef boost::intrusive_ptr<LLServiceCreator> creator_t;
+ //typedef boost::intrusive_ptr<LLService> service_t;
+ typedef boost::shared_ptr<LLChainIOFactory> creator_t;
+
+ /**
+ * @brief This method is used to register a protocol name with a
+ * a functor that creates the service.
+ *
+ * THOROUGH_DESCRIPTION
+ * @param aParameter A brief description of aParameter.
+ * @return Returns true if a service was successfully registered.
+ */
+ static bool registerCreator(const std::string& name, creator_t fn);
+
+ /**
+ * @brief This method connects to a service by name.
+ *
+ * @param name The name of the service to connect to.
+ * @param chain The constructed chain including the service instance.
+ * @param context Context for the activation.
+ * @return An instance of the service for use or NULL on failure.
+ */
+ static LLIOPipe* activate(
+ const std::string& name,
+ LLPumpIO::chain_t& chain,
+ LLSD context);
+
+ /**
+ * @brief
+ *
+ * @param name The name of the service to discard.
+ * @return true if service creator was found and discarded.
+ */
+ static bool discard(const std::string& name);
+
+protected:
+ // The creation factory static data.
+ typedef std::map<std::string, creator_t> creators_t;
+ static creators_t sCreatorFunctors;
+
+protected:
+ // construction & destruction. since this class is an abstract
+ // base class, it is up to Service implementations to actually
+ // deal with construction and not a public method. How that
+ // construction takes place will be handled by the service
+ // creators.
+ LLService();
+ virtual ~LLService();
+
+protected:
+ // This frame timer records how long this service has
+ // existed. Useful for derived services to give themselves a
+ // lifetime and expiration.
+ // *NOTE: Phoenix - This functionaity has been moved to the
+ // pump. 2005-12-13
+ //LLFrameTimer mTimer;
+
+ // Since services are designed in an 'ask now, respond later'
+ // idiom which probably crosses thread boundaries, almost all
+ // services will need a handle to a response pipe. It will usually
+ // be the job of the service author to derive a useful
+ // implementation of response, and up to the service subscriber to
+ // further derive that class to do something useful when the
+ // response comes in.
+ LLIOPipe::ptr_t mResponse;
+};
+
+
+#endif // LL_LLSERVICE_H
diff --git a/indra/llmessage/lltaskname.h b/indra/llmessage/lltaskname.h
new file mode 100644
index 0000000000..2a5a823e08
--- /dev/null
+++ b/indra/llmessage/lltaskname.h
@@ -0,0 +1,42 @@
+/**
+ * @file lltaskname.h
+ * @brief This contains the current list of valid tasks and is inluded
+ * into both simulator and viewer
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTASKNAME_H
+#define LL_LLTASKNAME_H
+
+// Current valid tasks
+// If you add a taskname here you will have to
+// 1) Add an initializer to init_object() in llscript.cpp
+// 1.1) Add to object_type_to_task_name() in llregion.cpp
+// 2) Add display code to LLStupidObject::render2(LLAgent* agentp) in llstupidobject.cpp
+// 3) Add any additional code to support new opcodes you create
+
+typedef enum e_lltask_name
+{
+ LLTASK_NULL = 0, // Not a valid task
+ LLTASK_AGENT = 1, // The player's agent in Linden World
+ LLTASK_CHILD_AGENT = 2, // Child agents sent to adjacent regions
+// LLTASK_BASIC_SHOT, // Simple shot that moves in a straight line
+// LLTASK_BIG_SHOT, // Big shot that uses gravity
+ LLTASK_TREE = 5, // A tree
+// LLTASK_BIRD, // a bird
+// LLTASK_ATOR, // a predator
+// LLTASK_SMOKE, // Smoke poof
+// LLTASK_SPARK, // Little spark
+// LLTASK_ROCK, // Rock
+ LLTASK_GRASS = 11, // Grass
+ LLTASK_PSYS = 12, // particle system test example
+// LLTASK_ORACLE,
+// LLTASK_DEMON, // Maxwell's demon
+// LLTASK_LSL_TEST, // Linden Scripting Language Test Object
+ LLTASK_PRIMITIVE = 16,
+// LLTASK_GHOST = 17, // a ghost (Boo!)
+ LLTASK_TREE_NEW = 18
+} ELLTaskName;
+#endif
diff --git a/indra/llmessage/llteleportflags.h b/indra/llmessage/llteleportflags.h
new file mode 100644
index 0000000000..001d05d109
--- /dev/null
+++ b/indra/llmessage/llteleportflags.h
@@ -0,0 +1,43 @@
+/**
+ * @file llteleportflags.h
+ * @brief Teleport flags
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTELEPORTFLAGS_H
+#define LL_LLTELEPORTFLAGS_H
+
+const U32 TELEPORT_FLAGS_DEFAULT = 0;
+const U32 TELEPORT_FLAGS_SET_HOME_TO_TARGET = 1 << 0; // newbie leaving prelude
+const U32 TELEPORT_FLAGS_SET_LAST_TO_TARGET = 1 << 1;
+const U32 TELEPORT_FLAGS_VIA_LURE = 1 << 2;
+const U32 TELEPORT_FLAGS_VIA_LANDMARK = 1 << 3;
+const U32 TELEPORT_FLAGS_VIA_LOCATION = 1 << 4;
+const U32 TELEPORT_FLAGS_VIA_HOME = 1 << 5;
+const U32 TELEPORT_FLAGS_VIA_TELEHUB = 1 << 6;
+const U32 TELEPORT_FLAGS_VIA_LOGIN = 1 << 7;
+const U32 TELEPORT_FLAGS_VIA_GODLIKE_LURE = 1 << 8;
+const U32 TELEPORT_FLAGS_GODLIKE = 1 << 9;
+const U32 TELEPORT_FLAGS_911 = 1 << 10;
+const U32 TELEPORT_FLAGS_DISABLE_CANCEL = 1 << 11; // Used for llTeleportAgentHome()
+const U32 TELEPORT_FLAGS_VIA_REGION_ID = 1 << 12;
+const U32 TELEPORT_FLAGS_IS_FLYING = 1 << 13;
+
+const U32 TELEPORT_FLAGS_MASK_VIA = TELEPORT_FLAGS_VIA_LURE
+ | TELEPORT_FLAGS_VIA_LANDMARK
+ | TELEPORT_FLAGS_VIA_LOCATION
+ | TELEPORT_FLAGS_VIA_HOME
+ | TELEPORT_FLAGS_VIA_TELEHUB
+ | TELEPORT_FLAGS_VIA_LOGIN
+ | TELEPORT_FLAGS_VIA_REGION_ID;
+
+
+
+
+const U32 LURE_FLAG_NORMAL_LURE = 1 << 0;
+const U32 LURE_FLAG_GODLIKE_LURE = 1 << 1;
+const U32 LURE_FLAG_GODLIKE_PURSUIT = 1 << 2;
+
+#endif
diff --git a/indra/llmessage/llthrottle.cpp b/indra/llmessage/llthrottle.cpp
new file mode 100644
index 0000000000..01e83ca5cd
--- /dev/null
+++ b/indra/llmessage/llthrottle.cpp
@@ -0,0 +1,542 @@
+/**
+ * @file llthrottle.cpp
+ * @brief LLThrottle class used for network bandwidth control.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llthrottle.h"
+#include "llmath.h"
+#include "lldatapacker.h"
+#include "message.h"
+
+
+LLThrottle::LLThrottle(const F32 rate)
+{
+ mRate = rate;
+ mAvailable = 0.f;
+ mLookaheadSecs = 0.25f;
+ mLastSendTime = LLMessageSystem::getMessageTimeSeconds(TRUE);
+}
+
+
+void LLThrottle::setRate(const F32 rate)
+{
+ // Need to accumulate available bits when adjusting the rate.
+ mAvailable = getAvailable();
+ mLastSendTime = LLMessageSystem::getMessageTimeSeconds();
+ mRate = rate;
+}
+
+F32 LLThrottle::getAvailable()
+{
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
+ return mAvailable + (mRate * elapsed_time);
+}
+
+BOOL LLThrottle::checkOverflow(const F32 amount)
+{
+ BOOL retval = TRUE;
+
+ F32 lookahead_amount = mRate * mLookaheadSecs;
+
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime);
+ F32 amount_available = mAvailable + (mRate * elapsed_time);
+
+ if ((amount_available >= lookahead_amount) || (amount_available > amount))
+ {
+ // ...enough space to send this message
+ // Also do if > lookahead so we can use if amount > capped amount.
+ retval = FALSE;
+ }
+
+ return retval;
+}
+
+BOOL LLThrottle::throttleOverflow(const F32 amount)
+{
+ F32 elapsed_time;
+ F32 lookahead_amount;
+ BOOL retval = TRUE;
+
+ lookahead_amount = mRate * mLookaheadSecs;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ elapsed_time = (F32)(mt_sec - mLastSendTime);
+ mLastSendTime = mt_sec;
+
+ mAvailable += mRate * elapsed_time;
+
+ if (mAvailable >= lookahead_amount)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mAvailable = lookahead_amount;
+ retval = FALSE;
+ }
+ else if (mAvailable > amount)
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ // We actually already sent the bits.
+ mAvailable -= amount;
+
+ // What if bitsavailable goes negative?
+ // That's OK, because it means someone is banging on the channel,
+ // so we need some time to recover.
+
+ return retval;
+}
+
+
+
+const F32 THROTTLE_LOOKAHEAD_TIME = 1.f; // seconds
+
+// Make sure that we don't set above these
+// values, even if the client asks to be set
+// higher
+// Note that these values are replicated on the
+// client side to set max bandwidth throttling there,
+// in llviewerthrottle.cpp. These values are the sum
+// of the top two tiers of bandwidth there.
+
+F32 gThrottleMaximumBPS[TC_EOF] =
+{
+ 150000.f, // TC_RESEND
+ 170000.f, // TC_LAND
+ 34000.f, // TC_WIND
+ 34000.f, // TC_CLOUD
+ 446000.f, // TC_TASK
+ 446000.f, // TC_TEXTURE
+ 220000.f, // TC_ASSET
+};
+
+// Start low until viewer informs us of capability
+// Asset and resend get high values, since they
+// aren't used JUST by the viewer necessarily.
+// This is a HACK and should be dealt with more properly on
+// circuit creation.
+
+F32 gThrottleDefaultBPS[TC_EOF] =
+{
+ 100000.f, // TC_RESEND
+ 4000.f, // TC_LAND
+ 4000.f, // TC_WIND
+ 4000.f, // TC_CLOUD
+ 4000.f, // TC_TASK
+ 4000.f, // TC_TEXTURE
+ 100000.f, // TC_ASSET
+};
+
+// Don't throttle down lower than this
+// This potentially wastes 50 kbps, but usually
+// wont.
+F32 gThrottleMinimumBPS[TC_EOF] =
+{
+ 10000.f, // TC_RESEND
+ 10000.f, // TC_LAND
+ 4000.f, // TC_WIND
+ 4000.f, // TC_CLOUD
+ 20000.f, // TC_TASK
+ 10000.f, // TC_TEXTURE
+ 10000.f, // TC_ASSET
+};
+
+const char* THROTTLE_NAMES[TC_EOF] =
+{
+ "Resend ",
+ "Land ",
+ "Wind ",
+ "Cloud ",
+ "Task ",
+ "Texture",
+ "Asset "
+};
+
+LLThrottleGroup::LLThrottleGroup()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottleTotal[i] = gThrottleDefaultBPS[i];
+ mNominalBPS[i] = gThrottleDefaultBPS[i];
+ }
+
+ resetDynamicAdjust();
+}
+
+void LLThrottleGroup::packThrottle(LLDataPacker &dp) const
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ dp.packF32(mThrottleTotal[i], "Throttle");
+ }
+}
+
+void LLThrottleGroup::unpackThrottle(LLDataPacker &dp)
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ F32 temp_throttle;
+ dp.unpackF32(temp_throttle, "Throttle");
+ temp_throttle = llclamp(temp_throttle, 0.f, 2250000.f);
+ mThrottleTotal[i] = temp_throttle;
+ if(mThrottleTotal[i] > gThrottleMaximumBPS[i])
+ {
+ mThrottleTotal[i] = gThrottleMaximumBPS[i];
+ }
+ }
+}
+
+// Call this whenever mNominalBPS changes. Need to reset
+// the measurement systems. In the future, we should look
+// into NOT resetting the system.
+void LLThrottleGroup::resetDynamicAdjust()
+{
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mCurrentBPS[i] = mNominalBPS[i];
+ mBitsAvailable[i] = mNominalBPS[i] * THROTTLE_LOOKAHEAD_TIME;
+ mLastSendTime[i] = mt_sec;
+ mBitsSentThisPeriod[i] = 0;
+ mBitsSentHistory[i] = 0;
+ }
+ mDynamicAdjustTime = mt_sec;
+}
+
+
+BOOL LLThrottleGroup::setNominalBPS(F32* throttle_vec)
+{
+ BOOL changed = FALSE;
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mNominalBPS[i] != throttle_vec[i])
+ {
+ changed = TRUE;
+ mNominalBPS[i] = throttle_vec[i];
+ }
+ }
+
+ // If we changed the nominal settings, reset the dynamic
+ // adjustment subsystem.
+ if (changed)
+ {
+ resetDynamicAdjust();
+ }
+
+ return changed;
+}
+
+
+BOOL LLThrottleGroup::checkOverflow(S32 throttle_cat, F32 bits)
+{
+ BOOL retval = TRUE;
+
+ F32 category_bps = mCurrentBPS[throttle_cat];
+ F32 lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
+
+ // use a temporary bits_available
+ // since we don't want to change mBitsAvailable every time
+ F32 elapsed_time = (F32)(LLMessageSystem::getMessageTimeSeconds() - mLastSendTime[throttle_cat]);
+ F32 bits_available = mBitsAvailable[throttle_cat] + (category_bps * elapsed_time);
+
+ if (bits_available >= lookahead_bits)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mBitsAvailable[throttle_cat] = lookahead_bits;
+ retval = FALSE;
+ }
+ else if ( bits_available > bits )
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ return retval;
+}
+
+BOOL LLThrottleGroup::throttleOverflow(S32 throttle_cat, F32 bits)
+{
+ F32 elapsed_time;
+ F32 category_bps;
+ F32 lookahead_bits;
+ BOOL retval = TRUE;
+
+ category_bps = mCurrentBPS[throttle_cat];
+ lookahead_bits = category_bps * THROTTLE_LOOKAHEAD_TIME;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+ elapsed_time = (F32)(mt_sec - mLastSendTime[throttle_cat]);
+ mLastSendTime[throttle_cat] = mt_sec;
+ mBitsAvailable[throttle_cat] += category_bps * elapsed_time;
+
+ if (mBitsAvailable[throttle_cat] >= lookahead_bits)
+ {
+ // ...channel completely open, so allow send regardless
+ // of size. This allows sends on very low BPS channels.
+ mBitsAvailable[throttle_cat] = lookahead_bits;
+ retval = FALSE;
+ }
+ else if ( mBitsAvailable[throttle_cat] > bits )
+ {
+ // ...enough space to send this message
+ retval = FALSE;
+ }
+
+ // We actually already sent the bits.
+ mBitsAvailable[throttle_cat] -= bits;
+
+ mBitsSentThisPeriod[throttle_cat] += bits;
+
+ // What if bitsavailable goes negative?
+ // That's OK, because it means someone is banging on the channel,
+ // so we need some time to recover.
+
+ return retval;
+}
+
+
+BOOL LLThrottleGroup::dynamicAdjust()
+{
+ const F32 DYNAMIC_ADJUST_TIME = 1.0f; // seconds
+ const F32 CURRENT_PERIOD_WEIGHT = .25f; // how much weight to give to last period while determining BPS utilization
+ const F32 BUSY_PERCENT = 0.75f; // if use more than this fraction of BPS, you are busy
+ const F32 IDLE_PERCENT = 0.70f; // if use less than this fraction, you are "idle"
+ const F32 TRANSFER_PERCENT = 0.90f; // how much unused bandwidth to take away each adjustment
+ const F32 RECOVER_PERCENT = 0.25f; // how much to give back during recovery phase
+
+ S32 i;
+
+ F64 mt_sec = LLMessageSystem::getMessageTimeSeconds();
+
+ // Only dynamically adjust every few seconds
+ if ((mt_sec - mDynamicAdjustTime) < DYNAMIC_ADJUST_TIME)
+ {
+ return FALSE;
+ }
+ mDynamicAdjustTime = mt_sec;
+
+ S32 total = 0;
+ // Update historical information
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mBitsSentHistory[i] == 0)
+ {
+ // first run, just copy current period
+ mBitsSentHistory[i] = mBitsSentThisPeriod[i];
+ }
+ else
+ {
+ // have some history, so weight accordingly
+ mBitsSentHistory[i] = (1.f - CURRENT_PERIOD_WEIGHT) * mBitsSentHistory[i]
+ + CURRENT_PERIOD_WEIGHT * mBitsSentThisPeriod[i];
+ }
+
+ mBitsSentThisPeriod[i] = 0;
+ total += llround(mBitsSentHistory[i]);
+ }
+
+ // Look for busy channels
+ // TODO: Fold into loop above.
+ BOOL channels_busy = FALSE;
+ F32 busy_nominal_sum = 0;
+ BOOL channel_busy[TC_EOF];
+ BOOL channel_idle[TC_EOF];
+ BOOL channel_over_nominal[TC_EOF];
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ // Is this a busy channel?
+ if (mBitsSentHistory[i] >= BUSY_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i])
+ {
+ // this channel is busy
+ channels_busy = TRUE;
+ busy_nominal_sum += mNominalBPS[i]; // use for allocation of pooled idle bandwidth
+ channel_busy[i] = TRUE;
+ }
+ else
+ {
+ channel_busy[i] = FALSE;
+ }
+
+ // Is this an idle channel?
+ if ((mBitsSentHistory[i] < IDLE_PERCENT * DYNAMIC_ADJUST_TIME * mCurrentBPS[i]) &&
+ (mBitsAvailable[i] > 0))
+ {
+ channel_idle[i] = TRUE;
+ }
+ else
+ {
+ channel_idle[i] = FALSE;
+ }
+
+ // Is this an overpumped channel?
+ if (mCurrentBPS[i] > mNominalBPS[i])
+ {
+ channel_over_nominal[i] = TRUE;
+ }
+ else
+ {
+ channel_over_nominal[i] = FALSE;
+ }
+
+ //if (total)
+ //{
+ // llinfos << i << ": B" << channel_busy[i] << " I" << channel_idle[i] << " N" << channel_over_nominal[i];
+ // llcont << " Nom: " << mNominalBPS[i] << " Cur: " << mCurrentBPS[i] << " BS: " << mBitsSentHistory[i] << llendl;
+ //}
+ }
+
+ if (channels_busy)
+ {
+ // Some channels are busy. Let's see if we can get them some bandwidth.
+ F32 used_bps;
+ F32 avail_bps;
+ F32 transfer_bps;
+
+ F32 pool_bps = 0;
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (channel_idle[i] || channel_over_nominal[i] )
+ {
+ // Either channel i is idle, or has been overpumped.
+ // Therefore it's a candidate to give up some bandwidth.
+ // Figure out how much bandwidth it has been using, and how
+ // much is available to steal.
+ used_bps = mBitsSentHistory[i] / DYNAMIC_ADJUST_TIME;
+
+ // CRO make sure to keep a minimum amount of throttle available
+ // CRO NB: channels set to < MINIMUM_BPS will never give up bps,
+ // which is correct I think
+ if (used_bps < gThrottleMinimumBPS[i])
+ {
+ used_bps = gThrottleMinimumBPS[i];
+ }
+
+ if (channel_over_nominal[i])
+ {
+ F32 unused_current = mCurrentBPS[i] - used_bps;
+ avail_bps = llmax(mCurrentBPS[i] - mNominalBPS[i], unused_current);
+ }
+ else
+ {
+ avail_bps = mCurrentBPS[i] - used_bps;
+ }
+
+ //llinfos << i << " avail " << avail_bps << llendl;
+
+ // Historically, a channel could have used more than its current share,
+ // even if it's idle right now.
+ // Make sure we don't steal too much.
+ if (avail_bps < 0)
+ {
+ continue;
+ }
+
+ // Transfer some bandwidth from this channel into the global pool.
+ transfer_bps = avail_bps * TRANSFER_PERCENT;
+ mCurrentBPS[i] -= transfer_bps;
+ pool_bps += transfer_bps;
+ }
+ }
+
+ //llinfos << "Pool BPS: " << pool_bps << llendl;
+ // Now redistribute the bandwidth to busy channels.
+ F32 unused_bps = 0.f;
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (channel_busy[i])
+ {
+ F32 add_amount = pool_bps * (mNominalBPS[i] / busy_nominal_sum);
+ //llinfos << "Busy " << i << " gets " << pool_bps << llendl;
+ mCurrentBPS[i] += add_amount;
+
+ // CRO: make sure this doesn't get too huge
+ // JC - Actually, need to let mCurrentBPS go less than nominal, otherwise
+ // you aren't allowing bandwidth to actually be moved from one channel
+ // to another.
+ // FIXME: If clamping high end, would be good to re-
+ // allocate to other channels in the above code.
+ const F32 MAX_BPS = 4 * mNominalBPS[i];
+ if (mCurrentBPS[i] > MAX_BPS)
+ {
+ F32 overage = mCurrentBPS[i] - MAX_BPS;
+ mCurrentBPS[i] -= overage;
+ unused_bps += overage;
+ }
+
+ // Paranoia
+ if (mCurrentBPS[i] < gThrottleMinimumBPS[i])
+ {
+ mCurrentBPS[i] = gThrottleMinimumBPS[i];
+ }
+ }
+ }
+
+ // For fun, add the overage back in to objects
+ if (unused_bps > 0.f)
+ {
+ mCurrentBPS[TC_TASK] += unused_bps;
+ }
+ }
+ else
+ {
+ // No one is busy.
+ // Make the channel allocations seek toward nominal.
+
+ // Look for overpumped channels
+ F32 starved_nominal_sum = 0;
+ F32 avail_bps = 0;
+ F32 transfer_bps = 0;
+ F32 pool_bps = 0;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] > mNominalBPS[i])
+ {
+ avail_bps = (mCurrentBPS[i] - mNominalBPS[i]);
+ transfer_bps = avail_bps * RECOVER_PERCENT;
+
+ mCurrentBPS[i] -= transfer_bps;
+ pool_bps += transfer_bps;
+ }
+ }
+
+ // Evenly distribute bandwidth to channels currently
+ // using less than nominal.
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] < mNominalBPS[i])
+ {
+ // We're going to weight allocations by nominal BPS.
+ starved_nominal_sum += mNominalBPS[i];
+ }
+ }
+
+ for (i = 0; i < TC_EOF; i++)
+ {
+ if (mCurrentBPS[i] < mNominalBPS[i])
+ {
+ // Distribute bandwidth according to nominal allocation ratios.
+ mCurrentBPS[i] += pool_bps * (mNominalBPS[i] / starved_nominal_sum);
+ }
+ }
+ }
+ return TRUE;
+}
diff --git a/indra/llmessage/llthrottle.h b/indra/llmessage/llthrottle.h
new file mode 100644
index 0000000000..1ee772e687
--- /dev/null
+++ b/indra/llmessage/llthrottle.h
@@ -0,0 +1,81 @@
+/**
+ * @file llthrottle.h
+ * @brief LLThrottle class used for network bandwidth control
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTHROTTLE_H
+#define LL_LLTHROTTLE_H
+
+#include "lltimer.h"
+
+const S32 MAX_THROTTLE_SIZE = 32;
+
+class LLDataPacker;
+
+// Single instance of a generic throttle
+class LLThrottle
+{
+public:
+ LLThrottle(const F32 throttle = 1.f);
+ ~LLThrottle() { }
+
+ void setRate(const F32 rate);
+ BOOL checkOverflow(const F32 amount); // I'm about to add an amount, TRUE if would overflow throttle
+ BOOL throttleOverflow(const F32 amount); // I just sent amount, TRUE if that overflowed the throttle
+
+ F32 getAvailable(); // Return the available bits
+ F32 getRate() const { return mRate; }
+private:
+ F32 mLookaheadSecs; // Seconds to look ahead, maximum
+ F32 mRate; // BPS available, dynamically adjusted
+ F32 mAvailable; // Bits available to send right now on each channel
+ F64 mLastSendTime; // Time since last send on this channel
+};
+
+typedef enum e_throttle_categories
+{
+ TC_RESEND,
+ TC_LAND,
+ TC_WIND,
+ TC_CLOUD,
+ TC_TASK,
+ TC_TEXTURE,
+ TC_ASSET,
+ TC_EOF
+} EThrottleCats;
+
+
+class LLThrottleGroup
+{
+public:
+ LLThrottleGroup();
+ ~LLThrottleGroup() { }
+
+ void resetDynamicAdjust();
+ BOOL checkOverflow(S32 throttle_cat, F32 bits); // I'm about to send bits, TRUE if would overflow channel
+ BOOL throttleOverflow(S32 throttle_cat, F32 bits); // I just sent bits, TRUE if that overflowed the channel
+ BOOL dynamicAdjust(); // Shift bandwidth from idle channels to busy channels, TRUE if adjustment occurred
+ BOOL setNominalBPS(F32* throttle_vec); // TRUE if any value was different, resets adjustment system if was different
+
+ void packThrottle(LLDataPacker &dp) const;
+ void unpackThrottle(LLDataPacker &dp);
+public:
+ F32 mThrottleTotal[TC_EOF]; // BPS available, sent by viewer, sum for all simulators
+
+protected:
+ F32 mNominalBPS[TC_EOF]; // BPS available, adjusted to be just this simulator
+ F32 mCurrentBPS[TC_EOF]; // BPS available, dynamically adjusted
+
+ F32 mBitsAvailable[TC_EOF]; // Bits available to send right now on each channel
+ F32 mBitsSentThisPeriod[TC_EOF]; // Sent in this dynamic allocation period
+ F32 mBitsSentHistory[TC_EOF]; // Sent before this dynamic allocation period, adjusted to one period length
+
+ F64 mLastSendTime[TC_EOF]; // Time since last send on this channel
+ F64 mDynamicAdjustTime; // Only dynamic adjust every 2 seconds or so.
+
+};
+
+#endif
diff --git a/indra/llmessage/lltransfermanager.cpp b/indra/llmessage/lltransfermanager.cpp
new file mode 100644
index 0000000000..46fc386d71
--- /dev/null
+++ b/indra/llmessage/lltransfermanager.cpp
@@ -0,0 +1,1270 @@
+/**
+ * @file lltransfermanager.cpp
+ * @brief Improved transfer mechanism for moving data through the
+ * message system.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfermanager.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+
+#include "lltransfersourcefile.h"
+#include "lltransfersourceasset.h"
+#include "lltransfertargetfile.h"
+#include "lltransfertargetvfile.h"
+
+const S32 MAX_PACKET_DATA_SIZE = 2048;
+const S32 MAX_PARAMS_SIZE = 1024;
+
+LLTransferManager gTransferManager;
+LLTransferSource::stype_scfunc_map LLTransferSource::sSourceCreateMap;
+
+//
+// LLTransferManager implementation
+//
+
+LLTransferManager::LLTransferManager() :
+ mValid(FALSE)
+{
+ S32 i;
+ for (i = 0; i < LLTTT_NUM_TYPES; i++)
+ {
+ mTransferBitsIn[i] = 0;
+ mTransferBitsOut[i] = 0;
+ }
+}
+
+
+LLTransferManager::~LLTransferManager()
+{
+ if (mValid)
+ {
+ llwarns << "LLTransferManager::~LLTransferManager - Should have been cleaned up by message system shutdown process" << llendl;
+ cleanup();
+ }
+}
+
+
+void LLTransferManager::init()
+{
+ if (mValid)
+ {
+ llerrs << "Double initializing LLTransferManager!" << llendl;
+ }
+ mValid = TRUE;
+
+ // Register message system handlers
+ gMessageSystem->setHandlerFunc("TransferRequest", processTransferRequest, NULL);
+ gMessageSystem->setHandlerFunc("TransferInfo", processTransferInfo, NULL);
+ gMessageSystem->setHandlerFunc("TransferPacket", processTransferPacket, NULL);
+ gMessageSystem->setHandlerFunc("TransferAbort", processTransferAbort, NULL);
+}
+
+
+void LLTransferManager::cleanup()
+{
+ mValid = FALSE;
+
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ delete iter->second;
+ }
+ mTransferConnections.clear();
+}
+
+
+void LLTransferManager::updateTransfers()
+{
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ iter->second->updateTransfers();
+ }
+}
+
+
+void LLTransferManager::cleanupConnection(const LLHost &host)
+{
+ host_tc_map::iterator iter;
+ iter = mTransferConnections.find(host);
+ if (iter == mTransferConnections.end())
+ {
+ // This can happen legitimately if we've never done a transfer, and we're
+ // cleaning up a circuit.
+ //llwarns << "Cleaning up nonexistent transfer connection to " << host << llendl;
+ return;
+ }
+ LLTransferConnection *connp = iter->second;
+ delete connp;
+ mTransferConnections.erase(iter);
+}
+
+
+LLTransferConnection *LLTransferManager::getTransferConnection(const LLHost &host)
+{
+ host_tc_map::iterator iter;
+ iter = mTransferConnections.find(host);
+ if (iter == mTransferConnections.end())
+ {
+ mTransferConnections[host] = new LLTransferConnection(host);
+ return mTransferConnections[host];
+ }
+
+ return iter->second;
+}
+
+
+LLTransferSourceChannel *LLTransferManager::getSourceChannel(const LLHost &host, const LLTransferChannelType type)
+{
+ LLTransferConnection *tcp = getTransferConnection(host);
+ if (!tcp)
+ {
+ return NULL;
+ }
+ return tcp->getSourceChannel(type);
+}
+
+
+
+LLTransferTargetChannel *LLTransferManager::getTargetChannel(const LLHost &host, const LLTransferChannelType type)
+{
+ LLTransferConnection *tcp = getTransferConnection(host);
+ if (!tcp)
+ {
+ return NULL;
+ }
+ return tcp->getTargetChannel(type);
+}
+
+// virtual
+LLTransferSourceParams::~LLTransferSourceParams()
+{ }
+
+
+LLTransferSource *LLTransferManager::findTransferSource(const LLUUID &transfer_id)
+{
+ // This linear traversal could screw us later if we do lots of
+ // searches for sources. However, this ONLY happens right now
+ // in asset transfer callbacks, so this should be relatively quick.
+ host_tc_map::iterator iter;
+ for (iter = mTransferConnections.begin(); iter != mTransferConnections.end(); iter++)
+ {
+ LLTransferConnection *tcp = iter->second;
+ LLTransferConnection::tsc_iter sc_iter;
+ for (sc_iter = tcp->mTransferSourceChannels.begin(); sc_iter != tcp->mTransferSourceChannels.end(); sc_iter++)
+ {
+ LLTransferSourceChannel *scp = *sc_iter;
+ LLTransferSource *sourcep = scp->findTransferSource(transfer_id);
+ if (sourcep)
+ {
+ return sourcep;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//
+// Message handlers
+//
+
+//static
+void LLTransferManager::processTransferRequest(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferRequest" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferSourceType source_type;
+ LLTransferChannelType channel_type;
+ F32 priority;
+
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "SourceType", (S32 &)source_type);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getF32("TransferInfo", "Priority", priority);
+
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+
+ if (!tscp)
+ {
+ llwarns << "Source channel not found" << llendl;
+ return;
+ }
+
+ if (tscp->findTransferSource(transfer_id))
+ {
+ llwarns << "Duplicate request for transfer " << transfer_id << ", aborting!" << llendl;
+ return;
+ }
+
+
+ //llinfos << transfer_id << ":" << source_type << ":" << channel_type << ":" << priority << llendl;
+ LLTransferSource *tsp = LLTransferSource::createSource(source_type, transfer_id, priority);
+ if (!tsp)
+ {
+ llwarns << "LLTransferManager::processTransferRequest couldn't create transfer source!" << llendl;
+ return;
+ }
+ tscp->addTransferSource(tsp);
+
+ U8 tmp[MAX_PARAMS_SIZE];
+ S32 size = msgp->getSize("TransferInfo", "Params");
+ gMessageSystem->getBinaryData("TransferInfo", "Params", tmp, size);
+
+ LLDataPackerBinaryBuffer dpb(tmp, MAX_PARAMS_SIZE);
+ BOOL unpack_ok = tsp->unpackParams(dpb);
+ if (!unpack_ok)
+ {
+ llwarns << "Got bad parameters for a transfer request!" << llendl;
+ }
+
+ tsp->initTransfer();
+ // Don't use the status code from initTransfer for anything right now, was used before but the logic
+ // changed.
+}
+
+
+//static
+void LLTransferManager::processTransferInfo(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferInfo" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferTargetType target_type;
+ LLTransferChannelType channel_type;
+ LLTSCode status;
+ S32 size;
+
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "TargetType", (S32 &)target_type);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getS32("TransferInfo", "Status", (S32 &)status);
+ msgp->getS32("TransferInfo", "Size", size);
+
+ //llinfos << transfer_id << ":" << target_type<< ":" << channel_type << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (!ttcp)
+ {
+ llwarns << "Target channel not found" << llendl;
+ // Should send a message to abort the transfer.
+ return;
+ }
+
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (!ttp)
+ {
+ llwarns << "TransferInfo for unknown transfer! Not able to handle this yet!" << llendl;
+ // This could happen if we're doing a push transfer, although to avoid confusion,
+ // maybe it should be a different message.
+ return;
+ }
+
+ if (status != LLTS_OK)
+ {
+ llwarns << transfer_id << ": Non-ok status, cleaning up" << llendl;
+ ttp->completionCallback(status);
+ // Clean up the transfer.
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+
+ llinfos << "Receiving " << transfer_id << ", size " << size << " bytes" << llendl;
+ ttp->setSize(size);
+ ttp->setGotInfo(TRUE);
+
+ // OK, at this point we to handle any delayed transfer packets (which could happen
+ // if this packet was lost)
+
+ // This is a lame cut and paste of code down below. If we change the logic down there,
+ // we HAVE to change the logic up here.
+
+ while (1)
+ {
+ S32 packet_id = 0;
+ U8 tmp_data[MAX_PACKET_DATA_SIZE];
+ // See if we've got any delayed packets
+ packet_id = ttp->getNextPacketID();
+ if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
+ {
+ // Perhaps this stuff should be inside a method in LLTransferPacket?
+ // I'm too lazy to do it now, though.
+ llinfos << "Playing back delayed packet " << packet_id << llendl;
+ LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
+
+ // This is somewhat inefficient, but avoids us having to duplicate
+ // code between the off-the-wire and delayed paths.
+ packet_id = packetp->mPacketID;
+ size = packetp->mSize;
+ if (size)
+ {
+ if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
+ {
+ memcpy(tmp_data, packetp->mDatap, size);
+ }
+ }
+ status = packetp->mStatus;
+ ttp->mDelayedPacketMap.erase(packet_id);
+ delete packetp;
+ }
+ else
+ {
+ // No matching delayed packet, we're done.
+ break;
+ }
+
+ LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
+ if (ret_code == LLTS_OK)
+ {
+ ttp->setLastPacketID(packet_id);
+ }
+
+ if (status != LLTS_OK)
+ {
+ if (status != LLTS_DONE)
+ {
+ llwarns << "LLTransferManager::processTransferInfo Error in playback!" << llendl;
+ }
+ else
+ {
+ llinfos << "LLTransferManager::processTransferInfo replay FINISHED for " << transfer_id << llendl;
+ }
+ // This transfer is done, either via error or not.
+ ttp->completionCallback(status);
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+ }
+}
+
+
+//static
+void LLTransferManager::processTransferPacket(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ S32 packet_id;
+ LLTSCode status;
+ S32 size;
+ msgp->getUUID("TransferData", "TransferID", transfer_id);
+ msgp->getS32("TransferData", "ChannelType", (S32 &)channel_type);
+ msgp->getS32("TransferData", "Packet", packet_id);
+ msgp->getS32("TransferData", "Status", (S32 &)status);
+
+ // Find the transfer associated with this packet.
+ //llinfos << transfer_id << ":" << channel_type << llendl;
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (!ttcp)
+ {
+ llwarns << "Target channel not found" << llendl;
+ return;
+ }
+
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (!ttp)
+ {
+ llwarns << "Didn't find matching transfer for " << transfer_id << ", aborting!" << llendl;
+ llwarns << "Packet ID: " << packet_id << llendl;
+ llwarns << "Should notify source of this!" << llendl;
+ return;
+ }
+
+ size = msgp->getSize("TransferData", "Data");
+
+ S32 msg_bytes = 0;
+ if (msgp->getReceiveCompressedSize())
+ {
+ msg_bytes = msgp->getReceiveCompressedSize();
+ }
+ else
+ {
+ msg_bytes = msgp->getReceiveSize();
+ }
+ gTransferManager.addTransferBitsIn(ttcp->mChannelType, msg_bytes*8);
+
+ if ((size < 0) || (size > MAX_PACKET_DATA_SIZE))
+ {
+ llwarns << "Invalid transfer packet size " << size << llendl;
+ return;
+ }
+
+ U8 tmp_data[MAX_PACKET_DATA_SIZE];
+ if (size > 0)
+ {
+ // Only pull the data out if the size is > 0
+ msgp->getBinaryData("TransferData", "Data", tmp_data, size);
+ }
+
+ if ((!ttp->gotInfo()) || (ttp->getNextPacketID() != packet_id))
+ {
+
+ llwarns << "Out of order packet in transfer " << transfer_id << ", got " << packet_id << " expecting " << ttp->getNextPacketID() << llendl;
+
+ // Put this on a list of packets to be delivered later.
+ ttp->addDelayedPacket(packet_id, status, tmp_data, size);
+ return;
+ }
+
+ // Loop through this until we're done with all delayed packets
+
+ //
+ // NOTE: THERE IS A CUT AND PASTE OF THIS CODE IN THE TRANSFERINFO HANDLER
+ // SO WE CAN PLAY BACK DELAYED PACKETS THERE!!!!!!!!!!!!!!!!!!!!!!!!!
+ //
+ BOOL done = FALSE;
+ while (!done)
+ {
+ LLTSCode ret_code = ttp->dataCallback(packet_id, tmp_data, size);
+ if (ret_code == LLTS_OK)
+ {
+ ttp->setLastPacketID(packet_id);
+ }
+
+ if (status != LLTS_OK)
+ {
+ if (status != LLTS_DONE)
+ {
+ llwarns << "LLTransferManager::processTransferPacket Error in transfer!" << llendl;
+ }
+ else
+ {
+// llinfos << "LLTransferManager::processTransferPacket done for " << transfer_id << llendl;
+ }
+ // This transfer is done, either via error or not.
+ ttp->completionCallback(status);
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+
+ // See if we've got any delayed packets
+ packet_id = ttp->getNextPacketID();
+ if (ttp->mDelayedPacketMap.find(packet_id) != ttp->mDelayedPacketMap.end())
+ {
+ // Perhaps this stuff should be inside a method in LLTransferPacket?
+ // I'm too lazy to do it now, though.
+ llinfos << "Playing back delayed packet " << packet_id << llendl;
+ LLTransferPacket *packetp = ttp->mDelayedPacketMap[packet_id];
+
+ // This is somewhat inefficient, but avoids us having to duplicate
+ // code between the off-the-wire and delayed paths.
+ packet_id = packetp->mPacketID;
+ size = packetp->mSize;
+ if (size)
+ {
+ if ((packetp->mDatap != NULL) && (size<(S32)sizeof(tmp_data)))
+ {
+ memcpy(tmp_data, packetp->mDatap, size);
+ }
+ }
+ status = packetp->mStatus;
+ ttp->mDelayedPacketMap.erase(packet_id);
+ delete packetp;
+ }
+ else
+ {
+ // No matching delayed packet, abort it.
+ done = TRUE;
+ }
+ }
+}
+
+
+//static
+void LLTransferManager::processTransferAbort(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+
+
+ // See if it's a target that we're trying to abort
+ // Find the transfer associated with this packet.
+ LLTransferTargetChannel *ttcp = gTransferManager.getTargetChannel(msgp->getSender(), channel_type);
+ if (ttcp)
+ {
+ LLTransferTarget *ttp = ttcp->findTransferTarget(transfer_id);
+ if (ttp)
+ {
+ ttp->abortTransfer();
+ ttcp->deleteTransfer(ttp);
+ return;
+ }
+ }
+
+ // Hmm, not a target. Maybe it's a source.
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+ if (tscp)
+ {
+ LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
+ if (tsp)
+ {
+ tsp->abortTransfer();
+ tscp->deleteTransfer(tsp);
+ return;
+ }
+ }
+
+ llwarns << "Couldn't find transfer " << transfer_id << " to abort!" << llendl;
+}
+
+
+//static
+void LLTransferManager::processTransferPriority(LLMessageSystem *msgp, void **)
+{
+ //llinfos << "LLTransferManager::processTransferPacket" << llendl;
+
+ LLUUID transfer_id;
+ LLTransferChannelType channel_type;
+ F32 priority = 0.f;
+ msgp->getUUID("TransferInfo", "TransferID", transfer_id);
+ msgp->getS32("TransferInfo", "ChannelType", (S32 &)channel_type);
+ msgp->getF32("TransferInfo", "Priority", priority);
+
+ // Hmm, not a target. Maybe it's a source.
+ LLTransferSourceChannel *tscp = gTransferManager.getSourceChannel(msgp->getSender(), channel_type);
+ if (tscp)
+ {
+ LLTransferSource *tsp = tscp->findTransferSource(transfer_id);
+ if (tsp)
+ {
+ tscp->updatePriority(tsp, priority);
+ return;
+ }
+ }
+
+ llwarns << "Couldn't find transfer " << transfer_id << " to set priority!" << llendl;
+}
+
+
+//static
+void LLTransferManager::reliablePacketCallback(void **user_data, S32 result)
+{
+ LLUUID *transfer_idp = (LLUUID *)user_data;
+ if (result)
+ {
+ llwarns << "Aborting reliable transfer " << *transfer_idp << " due to failed reliable resends!" << llendl;
+ LLTransferSource *tsp = gTransferManager.findTransferSource(*transfer_idp);
+ if (tsp)
+ {
+ LLTransferSourceChannel *tscp = tsp->mChannelp;
+ tsp->abortTransfer();
+ tscp->deleteTransfer(tsp);
+ }
+ }
+ delete transfer_idp;
+}
+
+//
+// LLTransferConnection implementation
+//
+
+LLTransferConnection::LLTransferConnection(const LLHost &host)
+{
+ mHost = host;
+}
+
+LLTransferConnection::~LLTransferConnection()
+{
+ tsc_iter itersc;
+ for (itersc = mTransferSourceChannels.begin(); itersc != mTransferSourceChannels.end(); itersc++)
+ {
+ delete *itersc;
+ }
+ mTransferSourceChannels.clear();
+
+ ttc_iter itertc;
+ for (itertc = mTransferTargetChannels.begin(); itertc != mTransferTargetChannels.end(); itertc++)
+ {
+ delete *itertc;
+ }
+ mTransferTargetChannels.clear();
+}
+
+
+void LLTransferConnection::updateTransfers()
+{
+ // Do stuff for source transfers (basically, send data out).
+ tsc_iter iter;
+ for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
+ {
+ (*iter)->updateTransfers();
+ }
+
+ // Do stuff for target transfers
+ // Primarily, we should be aborting transfers that are irredeemably broken
+ // (large packet gaps that don't appear to be getting filled in, most likely)
+ // Probably should NOT be doing timeouts for other things, as new priority scheme
+ // means that a high priority transfer COULD block a transfer for a long time.
+}
+
+
+LLTransferSourceChannel *LLTransferConnection::getSourceChannel(const LLTransferChannelType channel_type)
+{
+ tsc_iter iter;
+ for (iter = mTransferSourceChannels.begin(); iter != mTransferSourceChannels.end(); iter++)
+ {
+ if ((*iter)->getChannelType() == channel_type)
+ {
+ return *iter;
+ }
+ }
+
+ LLTransferSourceChannel *tscp = new LLTransferSourceChannel(channel_type, mHost);
+ mTransferSourceChannels.push_back(tscp);
+ return tscp;
+}
+
+
+LLTransferTargetChannel *LLTransferConnection::getTargetChannel(const LLTransferChannelType channel_type)
+{
+ ttc_iter iter;
+ for (iter = mTransferTargetChannels.begin(); iter != mTransferTargetChannels.end(); iter++)
+ {
+ if ((*iter)->getChannelType() == channel_type)
+ {
+ return *iter;
+ }
+ }
+
+ LLTransferTargetChannel *ttcp = new LLTransferTargetChannel(channel_type, mHost);
+ mTransferTargetChannels.push_back(ttcp);
+ return ttcp;
+}
+
+
+//
+// LLTransferSourceChannel implementation
+//
+
+const S32 DEFAULT_PACKET_SIZE = 1000;
+
+
+LLTransferSourceChannel::LLTransferSourceChannel(const LLTransferChannelType channel_type, const LLHost &host) :
+ mChannelType(channel_type),
+ mHost(host),
+ mTransferSources(LLTransferSource::sSetPriority, LLTransferSource::sGetPriority),
+ mThrottleID(TC_ASSET)
+{
+}
+
+
+LLTransferSourceChannel::~LLTransferSourceChannel()
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ // Just kill off all of the transfers
+ (*iter).second->abortTransfer();
+ delete iter->second;
+ }
+ mTransferSources.mMap.clear();
+}
+
+void LLTransferSourceChannel::updatePriority(LLTransferSource *tsp, const F32 priority)
+{
+ mTransferSources.reprioritize(priority, tsp);
+}
+
+void LLTransferSourceChannel::updateTransfers()
+{
+ // Actually, this should do the following:
+ // Decide if we can actually send data.
+ // If so, update priorities so we know who gets to send it.
+ // Send data from the sources, while updating until we've sent our throttle allocation.
+
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(getHost());
+ if (!cdp)
+ {
+ return;
+ }
+
+ if (cdp->isBlocked())
+ {
+ // FIXME We need to make sure that the throttle bits available gets reset.
+
+ // We DON'T want to send any packets if they're blocked, they'll just end up
+ // piling up on the other end.
+ //llwarns << "Blocking transfers due to blocked circuit for " << getHost() << llendl;
+ return;
+ }
+
+ const S32 throttle_id = mThrottleID;
+
+ LLThrottleGroup &tg = cdp->getThrottleGroup();
+
+ if (tg.checkOverflow(throttle_id, 0.f))
+ {
+ return;
+ }
+
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+
+
+ BOOL done = FALSE;
+ for (iter = mTransferSources.mMap.begin(); (iter != mTransferSources.mMap.end()) && !done;)
+ {
+ //llinfos << "LLTransferSourceChannel::updateTransfers()" << llendl;
+ // Do stuff.
+ LLTransferSource *tsp = iter->second;
+ U8 *datap = NULL;
+ S32 data_size = 0;
+ BOOL delete_data = FALSE;
+ S32 packet_id = 0;
+ S32 sent_bytes = 0;
+ LLTSCode status = LLTS_OK;
+
+ // Get the packetID for the next packet that we're transferring.
+ packet_id = tsp->getNextPacketID();
+ status = tsp->dataCallback(packet_id, DEFAULT_PACKET_SIZE, &datap, data_size, delete_data);
+
+ if (status == LLTS_SKIP)
+ {
+ // We don't have any data, but we're not done, just go on.
+ // This will presumably be used for streaming or async transfers that
+ // are stalled waiting for data from another source.
+ iter++;
+ continue;
+ }
+
+ LLUUID *cb_uuid = new LLUUID(tsp->getID());
+
+ // Send the data now, even if it's an error.
+ // The status code will tell the other end what to do.
+ gMessageSystem->newMessage("TransferPacket");
+ gMessageSystem->nextBlock("TransferData");
+ gMessageSystem->addUUID("TransferID", tsp->getID());
+ gMessageSystem->addS32("ChannelType", getChannelType());
+ gMessageSystem->addS32("Packet", packet_id); // HACK! Need to put in a REAL packet id
+ gMessageSystem->addS32("Status", status);
+ gMessageSystem->addBinaryData("Data", datap, data_size);
+ sent_bytes = gMessageSystem->getCurrentSendTotal();
+ gMessageSystem->sendReliable(getHost(), LL_DEFAULT_RELIABLE_RETRIES, TRUE, 0.f,
+ LLTransferManager::reliablePacketCallback, (void**)cb_uuid);
+
+ // Do bookkeeping for the throttle
+ done = tg.throttleOverflow(throttle_id, sent_bytes*8.f);
+ gTransferManager.addTransferBitsOut(mChannelType, sent_bytes*8);
+
+ // Clean up our temporary data.
+ if (delete_data)
+ {
+ delete[] datap;
+ datap = NULL;
+ }
+
+ // Update the packet counter
+ tsp->setLastPacketID(packet_id);
+
+ switch (status)
+ {
+ case LLTS_OK:
+ // We're OK, don't need to do anything. Keep sending data.
+ break;
+ case LLTS_ERROR:
+ llwarns << "Error in transfer dataCallback!" << llendl;
+ case LLTS_DONE:
+ // We need to clean up this transfer source.
+ //llinfos << "LLTransferSourceChannel::updateTransfers() " << tsp->getID() << " done" << llendl;
+ tsp->completionCallback(status);
+ delete tsp;
+
+ mTransferSources.mMap.erase(iter++);
+ break;
+ default:
+ llerrs << "Unknown transfer error code!" << llendl;
+ }
+
+ // At this point, we should do priority adjustment (since some transfers like
+ // streaming transfers will adjust priority based on how much they've sent and time,
+ // but I'm not going to bother yet. - djs.
+ }
+}
+
+
+void LLTransferSourceChannel::addTransferSource(LLTransferSource *sourcep)
+{
+ sourcep->mChannelp = this;
+ mTransferSources.push(sourcep->getPriority(), sourcep);
+}
+
+
+LLTransferSource *LLTransferSourceChannel::findTransferSource(const LLUUID &transfer_id)
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ LLTransferSource *tsp = iter->second;
+ if (tsp->getID() == transfer_id)
+ {
+ return tsp;
+ }
+ }
+ return NULL;
+}
+
+
+BOOL LLTransferSourceChannel::deleteTransfer(LLTransferSource *tsp)
+{
+ LLPriQueueMap<LLTransferSource *>::pqm_iter iter;
+ for (iter = mTransferSources.mMap.begin(); iter != mTransferSources.mMap.end(); iter++)
+ {
+ if (iter->second == tsp)
+ {
+ break;
+ }
+ }
+
+ if (iter == mTransferSources.mMap.end())
+ {
+ llerrs << "Unable to find transfer source to delete!" << llendl;
+ return FALSE;
+ }
+ mTransferSources.mMap.erase(iter);
+ delete tsp;
+ return TRUE;
+}
+
+
+//
+// LLTransferTargetChannel implementation
+//
+
+LLTransferTargetChannel::LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host) :
+ mChannelType(channel_type),
+ mHost(host)
+{
+}
+
+LLTransferTargetChannel::~LLTransferTargetChannel()
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ // Abort all of the current transfers
+ (*iter)->abortTransfer();
+ delete *iter;
+ }
+ mTransferTargets.clear();
+}
+
+
+void LLTransferTargetChannel::requestTransfer(const LLTransferSourceParams &source_params,
+ const LLTransferTargetParams &target_params,
+ const F32 priority)
+{
+ LLUUID id;
+ id.generate();
+ LLTransferTarget *ttp = LLTransferTarget::createTarget(target_params.getType(), id);
+ if (!ttp)
+ {
+ llwarns << "LLTransferManager::requestTransfer aborting due to target creation failure!" << llendl;
+ }
+
+ ttp->applyParams(target_params);
+ addTransferTarget(ttp);
+
+ sendTransferRequest(ttp, source_params, priority);
+}
+
+
+void LLTransferTargetChannel::sendTransferRequest(LLTransferTarget *targetp,
+ const LLTransferSourceParams &params,
+ const F32 priority)
+{
+ //
+ // Pack the message with data which explains how to get the source, and
+ // send it off to the source for this channel.
+ //
+ llassert(targetp);
+ llassert(targetp->getChannel() == this);
+
+ gMessageSystem->newMessage("TransferRequest");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", targetp->getID());
+ gMessageSystem->addS32("SourceType", params.getType());
+ gMessageSystem->addS32("ChannelType", getChannelType());
+ gMessageSystem->addF32("Priority", priority);
+
+ U8 tmp[MAX_PARAMS_SIZE];
+ LLDataPackerBinaryBuffer dp(tmp, MAX_PARAMS_SIZE);
+ params.packParams(dp);
+ S32 len = dp.getCurrentSize();
+ gMessageSystem->addBinaryData("Params", tmp, len);
+
+ gMessageSystem->sendReliable(mHost);
+}
+
+
+void LLTransferTargetChannel::addTransferTarget(LLTransferTarget *targetp)
+{
+ targetp->mChannelp = this;
+ mTransferTargets.push_back(targetp);
+}
+
+
+LLTransferTarget *LLTransferTargetChannel::findTransferTarget(const LLUUID &transfer_id)
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ LLTransferTarget *ttp = *iter;
+ if (ttp->getID() == transfer_id)
+ {
+ return ttp;
+ }
+ }
+ return NULL;
+}
+
+
+BOOL LLTransferTargetChannel::deleteTransfer(LLTransferTarget *ttp)
+{
+ tt_iter iter;
+ for (iter = mTransferTargets.begin(); iter != mTransferTargets.end(); iter++)
+ {
+ if (*iter == ttp)
+ {
+ break;
+ }
+ }
+
+ if (iter == mTransferTargets.end())
+ {
+ llerrs << "Unable to find transfer target to delete!" << llendl;
+ return FALSE;
+ }
+ mTransferTargets.erase(iter);
+ delete ttp;
+ return TRUE;
+}
+
+
+//
+// LLTransferSource implementation
+//
+
+LLTransferSource::LLTransferSource(const LLTransferSourceType type,
+ const LLUUID &transfer_id,
+ const F32 priority) :
+ mType(type),
+ mID(transfer_id),
+ mChannelp(NULL),
+ mPriority(priority),
+ mSize(0),
+ mLastPacketID(-1)
+{
+ setPriority(priority);
+}
+
+
+LLTransferSource::~LLTransferSource()
+{
+ // No actual cleanup of the transfer is done here, this is purely for
+ // memory cleanup. The completionCallback is guaranteed to get called
+ // before this happens.
+}
+
+
+void LLTransferSource::sendTransferStatus(LLTSCode status)
+{
+ gMessageSystem->newMessage("TransferInfo");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("TargetType", LLTTT_UNKNOWN);
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->addS32("Status", status);
+ gMessageSystem->addS32("Size", mSize);
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ // Abort if there was as asset system issue.
+ if (status != LLTS_OK)
+ {
+ completionCallback(status);
+ mChannelp->deleteTransfer(this);
+ }
+}
+
+
+// This should never be called directly, the transfer manager is responsible for
+// aborting the transfer from the channel. I might want to rethink this in the
+// future, though.
+void LLTransferSource::abortTransfer()
+{
+ // Send a message down, call the completion callback
+ llinfos << "Aborting transfer " << getID() << " to " << mChannelp->getHost() << llendl;
+ gMessageSystem->newMessage("TransferAbort");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ completionCallback(LLTS_ABORT);
+}
+
+
+//static
+void LLTransferSource::registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc func)
+{
+ if (sSourceCreateMap.count(stype))
+ {
+ // Disallow changing what class handles a source type
+ // Unclear when you would want to do this, and whether it would work.
+ llerrs << "Reregistering source type " << stype << llendl;
+ }
+ else
+ {
+ sSourceCreateMap[stype] = func;
+ }
+}
+
+//static
+LLTransferSource *LLTransferSource::createSource(const LLTransferSourceType stype,
+ const LLUUID &id,
+ const F32 priority)
+{
+ switch (stype)
+ {
+ // *NOTE: The source file transfer mechanism is highly insecure and could
+ // lead to easy exploitation of a server process.
+ // I have removed all uses of it from the codebase. Phoenix.
+ //
+ //case LLTST_FILE:
+ // return new LLTransferSourceFile(id, priority);
+ case LLTST_ASSET:
+ return new LLTransferSourceAsset(id, priority);
+ default:
+ {
+ if (!sSourceCreateMap.count(stype))
+ {
+ // Use the callback to create the source type if it's not there.
+ llwarns << "Unknown transfer source type: " << stype << llendl;
+ return NULL;
+ }
+ return (sSourceCreateMap[stype])(id, priority);
+ }
+ }
+}
+
+
+// static
+void LLTransferSource::sSetPriority(LLTransferSource *&tsp, const F32 priority)
+{
+ tsp->setPriority(priority);
+}
+
+
+// static
+F32 LLTransferSource::sGetPriority(LLTransferSource *&tsp)
+{
+ return tsp->getPriority();
+}
+
+
+//
+// LLTransferPacket implementation
+//
+
+LLTransferPacket::LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size) :
+ mPacketID(packet_id),
+ mStatus(status),
+ mDatap(NULL),
+ mSize(size)
+{
+ if (size == 0)
+ {
+ return;
+ }
+
+ mDatap = new U8[size];
+ if (mDatap != NULL)
+ {
+ memcpy(mDatap, datap, size);
+ }
+}
+
+LLTransferPacket::~LLTransferPacket()
+{
+ delete[] mDatap;
+}
+
+//
+// LLTransferTarget implementation
+//
+
+LLTransferTarget::LLTransferTarget(LLTransferTargetType type, const LLUUID &id) :
+ mType(type),
+ mID(id),
+ mGotInfo(FALSE),
+ mSize(0),
+ mLastPacketID(-1)
+{
+}
+
+LLTransferTarget::~LLTransferTarget()
+{
+ // No actual cleanup of the transfer is done here, this is purely for
+ // memory cleanup. The completionCallback is guaranteed to get called
+ // before this happens.
+ tpm_iter iter;
+ for (iter = mDelayedPacketMap.begin(); iter != mDelayedPacketMap.end(); iter++)
+ {
+ delete iter->second;
+ }
+ mDelayedPacketMap.clear();
+}
+
+// This should never be called directly, the transfer manager is responsible for
+// aborting the transfer from the channel. I might want to rethink this in the
+// future, though.
+void LLTransferTarget::abortTransfer()
+{
+ // Send a message up, call the completion callback
+ llinfos << "Aborting transfer " << getID() << " from " << mChannelp->getHost() << llendl;
+ gMessageSystem->newMessage("TransferAbort");
+ gMessageSystem->nextBlock("TransferInfo");
+ gMessageSystem->addUUID("TransferID", getID());
+ gMessageSystem->addS32("ChannelType", mChannelp->getChannelType());
+ gMessageSystem->sendReliable(mChannelp->getHost());
+
+ completionCallback(LLTS_ABORT);
+}
+
+void LLTransferTarget::addDelayedPacket(const S32 packet_id, const LLTSCode status, U8 *datap, const S32 size)
+{
+ LLTransferPacket *tpp = new LLTransferPacket(packet_id, status, datap, size);
+#ifdef _DEBUG
+ if (mDelayedPacketMap.find(packet_id) != mDelayedPacketMap.end())
+ {
+ llerrs << "Packet ALREADY in delayed packet map!" << llendl;
+ }
+#endif
+ mDelayedPacketMap[packet_id] = tpp;
+}
+
+
+LLTransferTarget *LLTransferTarget::createTarget(const LLTransferTargetType type, const LLUUID &id)
+{
+ switch (type)
+ {
+ case LLTTT_FILE:
+ return new LLTransferTargetFile(id);
+ case LLTTT_VFILE:
+ return new LLTransferTargetVFile(id);
+ default:
+ llwarns << "Unknown transfer target type: " << type << llendl;
+ return NULL;
+ }
+}
+
+
+LLTransferSourceParamsInvItem::LLTransferSourceParamsInvItem() : LLTransferSourceParams(LLTST_SIM_INV_ITEM), mAssetType(LLAssetType::AT_NONE)
+{
+}
+
+
+void LLTransferSourceParamsInvItem::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
+{
+ mAgentID = agent_id;
+ mSessionID = session_id;
+}
+
+
+void LLTransferSourceParamsInvItem::setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id)
+{
+ mOwnerID = owner_id;
+ mTaskID = task_id;
+ mItemID = item_id;
+}
+
+
+void LLTransferSourceParamsInvItem::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+
+void LLTransferSourceParamsInvItem::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAgentID, "AgentID");
+ dp.packUUID(mSessionID, "SessionID");
+ dp.packUUID(mOwnerID, "OwnerID");
+ dp.packUUID(mTaskID, "TaskID");
+ dp.packUUID(mItemID, "ItemID");
+ dp.packUUID(mAssetID, "AssetID");
+ dp.packS32(mAssetType, "AssetType");
+}
+
+
+BOOL LLTransferSourceParamsInvItem::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_at;
+
+ dp.unpackUUID(mAgentID, "AgentID");
+ dp.unpackUUID(mSessionID, "SessionID");
+ dp.unpackUUID(mOwnerID, "OwnerID");
+ dp.unpackUUID(mTaskID, "TaskID");
+ dp.unpackUUID(mItemID, "ItemID");
+ dp.unpackUUID(mAssetID, "AssetID");
+ dp.unpackS32(tmp_at, "AssetType");
+
+ mAssetType = (LLAssetType::EType)tmp_at;
+
+ return TRUE;
+}
+
+LLTransferSourceParamsEstate::LLTransferSourceParamsEstate() :
+ LLTransferSourceParams(LLTST_SIM_ESTATE), mEstateAssetType(ET_NONE)
+{
+}
+
+void LLTransferSourceParamsEstate::setAgentSession(const LLUUID &agent_id, const LLUUID &session_id)
+{
+ mAgentID = agent_id;
+ mSessionID = session_id;
+}
+
+void LLTransferSourceParamsEstate::setEstateAssetType(const EstateAssetType etype)
+{
+ mEstateAssetType = etype;
+}
+
+void LLTransferSourceParamsEstate::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferSourceParamsEstate::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAgentID, "AgentID");
+ dp.packUUID(mSessionID, "SessionID");
+ dp.packS32(mEstateAssetType, "EstateAssetType");
+}
+
+
+BOOL LLTransferSourceParamsEstate::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_et;
+
+ dp.unpackUUID(mAgentID, "AgentID");
+ dp.unpackUUID(mSessionID, "SessionID");
+ dp.unpackS32(tmp_et, "EstateAssetType");
+
+ mEstateAssetType = (EstateAssetType)tmp_et;
+
+ return TRUE;
+}
diff --git a/indra/llmessage/lltransfermanager.h b/indra/llmessage/lltransfermanager.h
new file mode 100644
index 0000000000..8c4c9f8ba7
--- /dev/null
+++ b/indra/llmessage/lltransfermanager.h
@@ -0,0 +1,466 @@
+/**
+ * @file lltransfermanager.h
+ * @brief Improved transfer mechanism for moving data through the
+ * message system.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERMANAGER_H
+#define LL_LLTRANSFERMANAGER_H
+
+#include <map>
+#include <list>
+
+#include "llhost.h"
+#include "lluuid.h"
+#include "llthrottle.h"
+#include "llpriqueuemap.h"
+#include "llassettype.h"
+
+//
+// Definition of the manager class for the new LLXfer replacement.
+// Provides prioritized, bandwidth-throttled transport of arbitrary
+// binary data between host/circuit combos
+//
+
+
+typedef enum e_transfer_channel_type
+{
+ LLTCT_UNKNOWN = 0,
+ LLTCT_MISC,
+ LLTCT_ASSET,
+ LLTCT_NUM_TYPES
+} LLTransferChannelType;
+
+
+typedef enum e_transfer_source_type
+{
+ LLTST_UNKNOWN = 0,
+ LLTST_FILE,
+ LLTST_ASSET,
+ LLTST_SIM_INV_ITEM, // Simulator specific, may not be handled
+ LLTST_SIM_ESTATE, // Simulator specific, may not be handled
+ LLTST_NUM_TYPES
+} LLTransferSourceType;
+
+
+typedef enum e_transfer_target_type
+{
+ LLTTT_UNKNOWN = 0,
+ LLTTT_FILE,
+ LLTTT_VFILE,
+ LLTTT_NUM_TYPES
+} LLTransferTargetType;
+
+
+// Errors are negative, expected values are positive.
+typedef enum e_status_codes
+{
+ LLTS_OK = 0,
+ LLTS_DONE = 1,
+ LLTS_SKIP = 2,
+ LLTS_ABORT = 3,
+ LLTS_ERROR = -1,
+ LLTS_UNKNOWN_SOURCE = -2, // Equivalent of a 404
+ LLTS_INSUFFICIENT_PERMISSIONS = -3 // Not enough permissions
+} LLTSCode;
+
+// Types of requests for estate wide information
+typedef enum e_estate_type
+{
+ ET_Covenant = 0,
+ ET_NONE = -1
+} EstateAssetType;
+
+class LLMessageSystem;
+class LLDataPacker;
+
+class LLTransferConnection;
+class LLTransferSourceChannel;
+class LLTransferTargetChannel;
+class LLTransferSourceParams;
+class LLTransferTargetParams;
+class LLTransferSource;
+class LLTransferTarget;
+
+class LLTransferManager
+{
+public:
+ LLTransferManager();
+ virtual ~LLTransferManager();
+
+ void init();
+ void cleanup();
+
+ void updateTransfers(); // Called per frame to push packets out on the various different channels.
+ void cleanupConnection(const LLHost &host);
+
+
+ LLTransferSourceChannel *getSourceChannel(const LLHost &host, const LLTransferChannelType stype);
+ LLTransferTargetChannel *getTargetChannel(const LLHost &host, const LLTransferChannelType stype);
+
+ LLTransferSource *findTransferSource(const LLUUID &transfer_id);
+
+ BOOL isValid() const { return mValid; }
+
+ static void processTransferRequest(LLMessageSystem *mesgsys, void **);
+ static void processTransferInfo(LLMessageSystem *mesgsys, void **);
+ static void processTransferPacket(LLMessageSystem *mesgsys, void **);
+ static void processTransferAbort(LLMessageSystem *mesgsys, void **);
+ static void processTransferPriority(LLMessageSystem *mesgsys, void **);
+
+ static void reliablePacketCallback(void **, S32 result);
+
+ S32 getTransferBitsIn(const LLTransferChannelType tctype) const { return mTransferBitsIn[tctype]; }
+ S32 getTransferBitsOut(const LLTransferChannelType tctype) const { return mTransferBitsOut[tctype]; }
+ void resetTransferBitsIn(const LLTransferChannelType tctype) { mTransferBitsIn[tctype] = 0; }
+ void resetTransferBitsOut(const LLTransferChannelType tctype) { mTransferBitsOut[tctype] = 0; }
+ void addTransferBitsIn(const LLTransferChannelType tctype, const S32 bits) { mTransferBitsIn[tctype] += bits; }
+ void addTransferBitsOut(const LLTransferChannelType tctype, const S32 bits) { mTransferBitsOut[tctype] += bits; }
+protected:
+ LLTransferConnection *getTransferConnection(const LLHost &host);
+ BOOL removeTransferConnection(const LLHost &host);
+
+protected:
+ // Convenient typedefs
+ typedef std::map<LLHost, LLTransferConnection *> host_tc_map;
+
+ BOOL mValid;
+ LLHost mHost;
+
+ S32 mTransferBitsIn[LLTTT_NUM_TYPES];
+ S32 mTransferBitsOut[LLTTT_NUM_TYPES];
+
+ // We keep a map between each host and LLTransferConnection.
+ host_tc_map mTransferConnections;
+};
+
+
+//
+// Keeps tracks of all channels to/from a particular host.
+//
+class LLTransferConnection
+{
+public:
+ LLTransferConnection(const LLHost &host);
+ virtual ~LLTransferConnection();
+
+ void updateTransfers();
+
+ LLTransferSourceChannel *getSourceChannel(const LLTransferChannelType type);
+ LLTransferTargetChannel *getTargetChannel(const LLTransferChannelType type);
+
+ // Convenient typedefs
+ typedef std::list<LLTransferSourceChannel *>::iterator tsc_iter;
+ typedef std::list<LLTransferTargetChannel *>::iterator ttc_iter;
+ friend class LLTransferManager;
+protected:
+
+ LLHost mHost;
+ std::list<LLTransferSourceChannel *> mTransferSourceChannels;
+ std::list<LLTransferTargetChannel *> mTransferTargetChannels;
+
+};
+
+
+//
+// A channel which is pushing data out.
+//
+
+class LLTransferSourceChannel
+{
+public:
+ LLTransferSourceChannel(const LLTransferChannelType channel_type,
+ const LLHost &host);
+ virtual ~LLTransferSourceChannel();
+
+ void updateTransfers();
+
+ void updatePriority(LLTransferSource *tsp, const F32 priority);
+
+ void addTransferSource(LLTransferSource *sourcep);
+ LLTransferSource *findTransferSource(const LLUUID &transfer_id);
+ BOOL deleteTransfer(LLTransferSource *tsp);
+
+ void setThrottleID(const S32 throttle_id) { mThrottleID = throttle_id; }
+
+ LLTransferChannelType getChannelType() const { return mChannelType; }
+ LLHost getHost() const { return mHost; }
+
+protected:
+ typedef std::list<LLTransferSource *>::iterator ts_iter;
+
+ LLTransferChannelType mChannelType;
+ LLHost mHost;
+ LLPriQueueMap<LLTransferSource*> mTransferSources;
+
+ // The throttle that this source channel should use
+ S32 mThrottleID;
+};
+
+
+//
+// A channel receiving data from a source.
+//
+class LLTransferTargetChannel
+{
+public:
+ LLTransferTargetChannel(const LLTransferChannelType channel_type, const LLHost &host);
+ virtual ~LLTransferTargetChannel();
+
+ void requestTransfer(const LLTransferSourceParams &source_params,
+ const LLTransferTargetParams &target_params,
+ const F32 priority);
+
+ LLTransferTarget *findTransferTarget(const LLUUID &transfer_id);
+ BOOL deleteTransfer(LLTransferTarget *ttp);
+
+
+ LLTransferChannelType getChannelType() const { return mChannelType; }
+ LLHost getHost() const { return mHost; }
+
+protected:
+ void sendTransferRequest(LLTransferTarget *targetp,
+ const LLTransferSourceParams &params,
+ const F32 priority);
+
+ void addTransferTarget(LLTransferTarget *targetp);
+
+ friend class LLTransferTarget;
+ friend class LLTransferManager;
+protected:
+ typedef std::list<LLTransferTarget *>::iterator tt_iter;
+
+ LLTransferChannelType mChannelType;
+ LLHost mHost;
+ std::list<LLTransferTarget *> mTransferTargets;
+};
+
+
+class LLTransferSourceParams
+{
+public:
+ LLTransferSourceParams(const LLTransferSourceType type) : mType(type) { }
+ virtual ~LLTransferSourceParams();
+
+ virtual void packParams(LLDataPacker &dp) const = 0;
+ virtual BOOL unpackParams(LLDataPacker &dp) = 0;
+
+ LLTransferSourceType getType() const { return mType; }
+
+protected:
+ LLTransferSourceType mType;
+};
+
+
+//
+// LLTransferSource is an interface, all transfer sources should be derived from it.
+//
+typedef LLTransferSource *(*LLTransferSourceCreateFunc)(const LLUUID &id, const F32 priority);
+
+class LLTransferSource
+{
+public:
+
+ LLUUID getID() { return mID; }
+
+ friend class LLTransferManager;
+ friend class LLTransferSourceChannel;
+
+protected:
+ LLTransferSource(const LLTransferSourceType source_type,
+ const LLUUID &request_id,
+ const F32 priority);
+ virtual ~LLTransferSource();
+
+ void sendTransferStatus(LLTSCode status); // When you've figured out your transfer status, do this
+
+ virtual void initTransfer() = 0;
+ virtual F32 updatePriority() = 0;
+ virtual LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned) = 0;
+
+ // The completionCallback is GUARANTEED to be called before the destructor.
+ virtual void completionCallback(const LLTSCode status) = 0;
+
+ virtual BOOL unpackParams(LLDataPacker &dp) = 0;
+
+ virtual S32 getNextPacketID() { return mLastPacketID + 1; }
+ virtual void setLastPacketID(const S32 packet_id) { mLastPacketID = packet_id; }
+
+
+ // For now, no self-induced priority changes
+ F32 getPriority() { return mPriority; }
+ void setPriority(const F32 pri) { mPriority = pri; }
+
+ virtual void abortTransfer(); // DON'T USE THIS ONE, used internally by LLTransferManager
+
+ static LLTransferSource *createSource(const LLTransferSourceType stype,
+ const LLUUID &request_id,
+ const F32 priority);
+ static void registerSourceType(const LLTransferSourceType stype, LLTransferSourceCreateFunc);
+
+ static void sSetPriority(LLTransferSource *&tsp, const F32 priority);
+ static F32 sGetPriority(LLTransferSource *&tsp);
+protected:
+ typedef std::map<LLTransferSourceType, LLTransferSourceCreateFunc> stype_scfunc_map;
+ static stype_scfunc_map sSourceCreateMap;
+
+ LLTransferSourceType mType;
+ LLUUID mID;
+ LLTransferSourceChannel *mChannelp;
+ F32 mPriority;
+ S32 mSize;
+ S32 mLastPacketID;
+};
+
+
+class LLTransferTargetParams
+{
+public:
+ LLTransferTargetParams(const LLTransferTargetType type) : mType(type) {}
+ LLTransferTargetType getType() const { return mType; }
+protected:
+ LLTransferTargetType mType;
+};
+
+
+class LLTransferPacket
+{
+ // Used for storing a packet that's being delivered later because it's out of order.
+ // ONLY should be accessed by the following two classes, for now.
+ friend class LLTransferTarget;
+ friend class LLTransferManager;
+
+protected:
+
+ LLTransferPacket(const S32 packet_id, const LLTSCode status, const U8 *datap, const S32 size);
+ virtual ~LLTransferPacket();
+
+protected:
+ S32 mPacketID;
+ LLTSCode mStatus;
+ U8 *mDatap;
+ S32 mSize;
+};
+
+
+class LLTransferTarget
+{
+public:
+ LLTransferTarget(LLTransferTargetType target_type, const LLUUID &transfer_id);
+ virtual ~LLTransferTarget();
+
+
+ // Accessors
+ LLUUID getID() const { return mID; }
+ LLTransferTargetType getType() const { return mType; }
+ LLTransferTargetChannel *getChannel() const { return mChannelp; }
+
+ friend class LLTransferManager;
+ friend class LLTransferTargetChannel;
+
+ static LLTransferTarget *createTarget(const LLTransferTargetType type,
+ const LLUUID &request_id);
+protected:
+ virtual void applyParams(const LLTransferTargetParams &params) = 0;
+ virtual LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size) = 0;
+
+ // The completionCallback is GUARANTEED to be called before the destructor, so all handling
+ // of errors/aborts should be done here.
+ virtual void completionCallback(const LLTSCode status) = 0;
+
+ void abortTransfer();
+
+ virtual S32 getNextPacketID() { return mLastPacketID + 1; }
+ virtual void setLastPacketID(const S32 packet_id) { mLastPacketID = packet_id; }
+ void setSize(const S32 size) { mSize = size; }
+ void setGotInfo(const BOOL got_info) { mGotInfo = got_info; }
+ BOOL gotInfo() const { return mGotInfo; }
+
+ void addDelayedPacket(const S32 packet_id, const LLTSCode status, U8 *datap, const S32 size);
+
+protected:
+ typedef std::map<S32, LLTransferPacket *> transfer_packet_map;
+ typedef std::map<S32, LLTransferPacket *>::iterator tpm_iter;
+
+ LLTransferTargetType mType;
+ LLUUID mID;
+ LLTransferTargetChannel *mChannelp;
+ BOOL mGotInfo;
+ S32 mSize;
+ S32 mLastPacketID;
+
+ transfer_packet_map mDelayedPacketMap; // Packets that are waiting because of missing/out of order issues
+};
+
+
+// Hack, here so it's publicly available even though LLTransferSourceInvItem is only available on the simulator
+class LLTransferSourceParamsInvItem: public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsInvItem();
+ virtual ~LLTransferSourceParamsInvItem() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
+ void setInvItem(const LLUUID &owner_id, const LLUUID &task_id, const LLUUID &item_id);
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);
+
+ LLUUID getAgentID() const { return mAgentID; }
+ LLUUID getSessionID() const { return mSessionID; }
+ LLUUID getOwnerID() const { return mOwnerID; }
+ LLUUID getTaskID() const { return mTaskID; }
+ LLUUID getItemID() const { return mItemID; }
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mSessionID;
+ LLUUID mOwnerID;
+ LLUUID mTaskID;
+ LLUUID mItemID;
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+
+// Hack, here so it's publicly available even though LLTransferSourceEstate is only available on the simulator
+class LLTransferSourceParamsEstate: public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsEstate();
+ virtual ~LLTransferSourceParamsEstate() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAgentSession(const LLUUID &agent_id, const LLUUID &session_id);
+ void setEstateAssetType(const EstateAssetType etype);
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType at);
+
+ LLUUID getAgentID() const { return mAgentID; }
+ LLUUID getSessionID() const { return mSessionID; }
+ EstateAssetType getEstateAssetType() const { return mEstateAssetType; }
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mSessionID;
+ EstateAssetType mEstateAssetType;
+ // these are set on the sim based on estateinfotype
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+
+extern LLTransferManager gTransferManager;
+
+#endif//LL_LLTRANSFERMANAGER_H
diff --git a/indra/llmessage/lltransfersourceasset.cpp b/indra/llmessage/lltransfersourceasset.cpp
new file mode 100644
index 0000000000..f7c6711bd0
--- /dev/null
+++ b/indra/llmessage/lltransfersourceasset.cpp
@@ -0,0 +1,235 @@
+/**
+ * @file lltransfersourceasset.cpp
+ * @brief Transfer system for sending an asset.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfersourceasset.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llvfile.h"
+
+LLTransferSourceAsset::LLTransferSourceAsset(const LLUUID &request_id, const F32 priority) :
+ LLTransferSource(LLTST_ASSET, request_id, priority),
+ mGotResponse(FALSE),
+ mCurPos(0)
+{
+}
+
+LLTransferSourceAsset::~LLTransferSourceAsset()
+{
+}
+
+
+void LLTransferSourceAsset::initTransfer()
+{
+ if (gAssetStorage)
+ {
+ // *HACK: asset transfers will only be coming from the viewer
+ // to the simulator. This is subset of assets we allow to be
+ // simply pulled straight from the asset system.
+ // *FIX: Make this list smaller.
+ LLUUID* tidp;
+ switch(mParams.getAssetType())
+ {
+ case LLAssetType::AT_SOUND:
+ case LLAssetType::AT_LANDMARK:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_GESTURE:
+ case LLAssetType::AT_ANIMATION:
+ tidp = new LLUUID(getID());
+ gAssetStorage->getAssetData(
+ mParams.getAssetID(),
+ mParams.getAssetType(),
+ LLTransferSourceAsset::responderCallback,
+ tidp,
+ FALSE);
+ break;
+ default:
+ llwarns << "Attempted to request blocked asset "
+ << mParams.getAssetID() << ":"
+ << LLAssetType::lookupHumanReadable(mParams.getAssetType())
+ << llendl;
+ sendTransferStatus(LLTS_ERROR);
+ break;
+ }
+ }
+ else
+ {
+ llwarns << "Attempted to request asset "
+ << mParams.getAssetID() << ":" << LLAssetType::lookupHumanReadable(mParams.getAssetType())
+ << " without an asset system!" << llendl;
+ sendTransferStatus(LLTS_ERROR);
+ }
+}
+
+F32 LLTransferSourceAsset::updatePriority()
+{
+ return 0.f;
+}
+
+LLTSCode LLTransferSourceAsset::dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **data_handle,
+ S32 &returned_bytes,
+ BOOL &delete_returned)
+{
+ //llinfos << "LLTransferSourceAsset::dataCallback" << llendl;
+ if (!mGotResponse)
+ {
+ return LLTS_SKIP;
+ }
+
+ LLVFile vf(gAssetStorage->mVFS, mParams.getAssetID(), mParams.getAssetType(), LLVFile::READ);
+
+ if (!vf.getSize())
+ {
+ // Something bad happened with the asset request!
+ return LLTS_ERROR;
+ }
+
+ if (packet_id != mLastPacketID + 1)
+ {
+ llerrs << "Can't handle out of order file transfer yet!" << llendl;
+ }
+
+ // grab a buffer from the right place in the file
+ if (!vf.seek(mCurPos, 0))
+ {
+ llwarns << "LLTransferSourceAsset Can't seek to " << mCurPos << " length " << vf.getSize() << llendl;
+ llwarns << "While sending " << mParams.getAssetID() << llendl;
+ return LLTS_ERROR;
+ }
+
+ delete_returned = TRUE;
+ U8 *tmpp = new U8[max_bytes];
+ *data_handle = tmpp;
+ if (!vf.read(tmpp, max_bytes)) /* Flawfinder: Ignore */
+ {
+ // Crap, read failure, need to deal with it.
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ return LLTS_ERROR;
+ }
+
+ returned_bytes = vf.getLastBytesRead();
+ mCurPos += returned_bytes;
+
+
+ if (vf.eof())
+ {
+ if (!returned_bytes)
+ {
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ }
+ return LLTS_DONE;
+ }
+
+ return LLTS_OK;
+}
+
+void LLTransferSourceAsset::completionCallback(const LLTSCode status)
+{
+ // No matter what happens, all we want to do is close the vfile if
+ // we've got it open.
+}
+
+BOOL LLTransferSourceAsset::unpackParams(LLDataPacker &dp)
+{
+ //llinfos << "LLTransferSourceAsset::unpackParams" << llendl;
+
+ return mParams.unpackParams(dp);
+}
+
+
+void LLTransferSourceAsset::responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type,
+ void *user_data, S32 result)
+{
+ LLUUID *tidp = ((LLUUID*) user_data);
+ LLUUID transfer_id = *(tidp);
+ delete tidp;
+ tidp = NULL;
+
+ LLTransferSourceAsset *tsap = (LLTransferSourceAsset *) gTransferManager.findTransferSource(transfer_id);
+
+ if (!tsap)
+ {
+ llinfos << "Aborting transfer " << transfer_id << " callback, transfer source went away" << llendl;
+ return;
+ }
+
+ if (result)
+ {
+ llinfos << "AssetStorage: Error " << gAssetStorage->getErrorString(result) << " downloading uuid " << uuid << llendl;
+ }
+
+ LLTSCode status;
+
+ tsap->mGotResponse = TRUE;
+ if (LL_ERR_NOERR == result)
+ {
+ // Everything's OK.
+ LLVFile vf(gAssetStorage->mVFS, uuid, type, LLVFile::READ);
+ tsap->mSize = vf.getSize();
+ status = LLTS_OK;
+ }
+ else
+ {
+ // Uh oh, something bad happened when we tried to get this asset!
+ switch (result)
+ {
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ status = LLTS_UNKNOWN_SOURCE;
+ break;
+ default:
+ status = LLTS_ERROR;
+ }
+ }
+
+ tsap->sendTransferStatus(status);
+}
+
+
+
+LLTransferSourceParamsAsset::LLTransferSourceParamsAsset() : LLTransferSourceParams(LLTST_ASSET)
+{
+}
+
+void LLTransferSourceParamsAsset::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferSourceParamsAsset::packParams(LLDataPacker &dp) const
+{
+ dp.packUUID(mAssetID, "AssetID");
+ dp.packS32(mAssetType, "AssetType");
+}
+
+
+BOOL LLTransferSourceParamsAsset::unpackParams(LLDataPacker &dp)
+{
+ S32 tmp_at;
+
+ dp.unpackUUID(mAssetID, "AssetID");
+ dp.unpackS32(tmp_at, "AssetType");
+
+ mAssetType = (LLAssetType::EType)tmp_at;
+
+ return TRUE;
+}
+
diff --git a/indra/llmessage/lltransfersourceasset.h b/indra/llmessage/lltransfersourceasset.h
new file mode 100644
index 0000000000..931fc461f9
--- /dev/null
+++ b/indra/llmessage/lltransfersourceasset.h
@@ -0,0 +1,62 @@
+/**
+ * @file lltransfersourceasset.h
+ * @brief Transfer system for sending an asset.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERSOURCEASSET_H
+#define LL_LLTRANSFERSOURCEASSET_H
+
+#include "lltransfermanager.h"
+#include "llassetstorage.h"
+
+class LLVFile;
+
+class LLTransferSourceParamsAsset : public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsAsset();
+ virtual ~LLTransferSourceParamsAsset() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type);
+
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+protected:
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+};
+
+class LLTransferSourceAsset : public LLTransferSource
+{
+public:
+ LLTransferSourceAsset(const LLUUID &request_id, const F32 priority);
+ virtual ~LLTransferSourceAsset();
+
+ static void responderCallback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type,
+ void *user_data, S32 result);
+protected:
+ /*virtual*/ void initTransfer();
+ /*virtual*/ F32 updatePriority();
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+protected:
+ LLTransferSourceParamsAsset mParams;
+ BOOL mGotResponse;
+
+ S32 mCurPos;
+};
+
+#endif // LL_LLTRANSFERSOURCEASSET_H
diff --git a/indra/llmessage/lltransfersourcefile.cpp b/indra/llmessage/lltransfersourcefile.cpp
new file mode 100644
index 0000000000..45b03d7653
--- /dev/null
+++ b/indra/llmessage/lltransfersourcefile.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltransfersourcefile.cpp
+ * @brief Transfer system for sending a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfersourcefile.h"
+
+#include "llerror.h"
+#include "message.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+
+LLTransferSourceFile::LLTransferSourceFile(const LLUUID &request_id, const F32 priority) :
+ LLTransferSource(LLTST_FILE, request_id, priority),
+ mFP(NULL)
+{
+}
+
+LLTransferSourceFile::~LLTransferSourceFile()
+{
+ if (mFP)
+ {
+ llerrs << "Destructor called without the completion callback being called!" << llendl;
+ }
+}
+
+void LLTransferSourceFile::initTransfer()
+{
+ std::string filename = mParams.getFilename();
+ std::string delimiter = gDirUtilp->getDirDelimiter();
+
+ if((filename == ".")
+ || (filename == "..")
+ || (filename.find(delimiter[0]) != std::string::npos))
+ {
+ llwarns << "Attempting to transfer file " << filename << " with path delimiter, aborting!" << llendl;
+
+ sendTransferStatus(LLTS_ERROR);
+ return;
+ }
+ // Look for the file.
+ mFP = LLFile::fopen(mParams.getFilename().c_str(), "rb"); /* Flawfinder: ignore */
+ if (!mFP)
+ {
+ sendTransferStatus(LLTS_ERROR);
+ return;
+ }
+
+ // Get the size of the file using the hack from
+ fseek(mFP,0,SEEK_END);
+ mSize = ftell(mFP);
+ fseek(mFP,0,SEEK_SET);
+
+ sendTransferStatus(LLTS_OK);
+}
+
+F32 LLTransferSourceFile::updatePriority()
+{
+ return 0.f;
+}
+
+LLTSCode LLTransferSourceFile::dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **data_handle,
+ S32 &returned_bytes,
+ BOOL &delete_returned)
+{
+ //llinfos << "LLTransferSourceFile::dataCallback" << llendl;
+
+ if (!mFP)
+ {
+ llerrs << "Data callback without file set!" << llendl;
+ return LLTS_ERROR;
+ }
+
+ if (packet_id != mLastPacketID + 1)
+ {
+ llerrs << "Can't handle out of order file transfer yet!" << llendl;
+ }
+
+ // Grab up until the max number of bytes from the file.
+ delete_returned = TRUE;
+ U8 *tmpp = new U8[max_bytes];
+ *data_handle = tmpp;
+ returned_bytes = (S32)fread(tmpp, 1, max_bytes, mFP);
+ if (!returned_bytes)
+ {
+ delete[] tmpp;
+ *data_handle = NULL;
+ returned_bytes = 0;
+ delete_returned = FALSE;
+ return LLTS_DONE;
+ }
+
+ return LLTS_OK;
+}
+
+void LLTransferSourceFile::completionCallback(const LLTSCode status)
+{
+ // No matter what happens, all we want to do is close the file pointer if
+ // we've got it open.
+ if (mFP)
+ {
+ fclose(mFP);
+ mFP = NULL;
+
+ }
+ // Delete the file iff the filename begins with "TEMP"
+ if (mParams.getDeleteOnCompletion() && memcmp(mParams.getFilename().c_str(), "TEMP", 4) == 0)
+ {
+ LLFile::remove(mParams.getFilename().c_str());
+ }
+}
+
+BOOL LLTransferSourceFile::unpackParams(LLDataPacker &dp)
+{
+ //llinfos << "LLTransferSourceFile::unpackParams" << llendl;
+
+ return mParams.unpackParams(dp);
+}
+
+
+LLTransferSourceParamsFile::LLTransferSourceParamsFile() : LLTransferSourceParams(LLTST_FILE)
+{
+}
+
+
+void LLTransferSourceParamsFile::packParams(LLDataPacker &dp) const
+{
+ dp.packString(mFilename.c_str(), "Filename");
+ dp.packU8((U8)mDeleteOnCompletion, "Delete");
+}
+
+
+BOOL LLTransferSourceParamsFile::unpackParams(LLDataPacker &dp)
+{
+ char tmp_str[512]; /* Flawfinder: ignore */
+ dp.unpackString(tmp_str, "Filename");
+ mFilename = tmp_str;
+ U8 delete_flag;
+ dp.unpackU8(delete_flag, "Delete");
+ mDeleteOnCompletion = delete_flag;
+
+ llinfos << "Unpacked filename: " << mFilename << llendl;
+ return TRUE;
+}
diff --git a/indra/llmessage/lltransfersourcefile.h b/indra/llmessage/lltransfersourcefile.h
new file mode 100644
index 0000000000..ecaa6c908a
--- /dev/null
+++ b/indra/llmessage/lltransfersourcefile.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltransfersourcefile.h
+ * @brief Transfer system for sending a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERSOURCEFILE_H
+#define LL_LLTRANSFERSOURCEFILE_H
+
+#include "lltransfermanager.h"
+
+#include <stdio.h>
+
+class LLTransferSourceParamsFile : public LLTransferSourceParams
+{
+public:
+ LLTransferSourceParamsFile();
+ virtual ~LLTransferSourceParamsFile() {}
+ /*virtual*/ void packParams(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+ void setFilename(const LLString &filename) { mFilename = filename; }
+ std::string getFilename() const { return mFilename; }
+
+ void setDeleteOnCompletion(BOOL enabled) { mDeleteOnCompletion = enabled; }
+ BOOL getDeleteOnCompletion() { return mDeleteOnCompletion; }
+protected:
+ std::string mFilename;
+ // ONLY DELETE THINGS OFF THE SIM IF THE FILENAME BEGINS IN 'TEMP'
+ BOOL mDeleteOnCompletion;
+};
+
+class LLTransferSourceFile : public LLTransferSource
+{
+public:
+ LLTransferSourceFile(const LLUUID &transfer_id, const F32 priority);
+ virtual ~LLTransferSourceFile();
+
+protected:
+ /*virtual*/ void initTransfer();
+ /*virtual*/ F32 updatePriority();
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id,
+ const S32 max_bytes,
+ U8 **datap,
+ S32 &returned_bytes,
+ BOOL &delete_returned);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ /*virtual*/ BOOL unpackParams(LLDataPacker &dp);
+
+protected:
+ LLTransferSourceParamsFile mParams;
+ FILE *mFP;
+};
+
+#endif // LL_LLTRANSFERSOURCEFILE_H
diff --git a/indra/llmessage/lltransfertargetfile.cpp b/indra/llmessage/lltransfertargetfile.cpp
new file mode 100644
index 0000000000..92776e081d
--- /dev/null
+++ b/indra/llmessage/lltransfertargetfile.cpp
@@ -0,0 +1,104 @@
+/**
+ * @file lltransfertargetfile.cpp
+ * @brief Transfer system for receiving a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfertargetfile.h"
+#include "llerror.h"
+
+
+
+
+LLTransferTargetFile::LLTransferTargetFile(const LLUUID &uuid) :
+ LLTransferTarget(LLTTT_FILE, uuid),
+ mFP(NULL)
+{
+}
+
+LLTransferTargetFile::~LLTransferTargetFile()
+{
+ if (mFP)
+ {
+ llerrs << "LLTransferTargetFile::~LLTransferTargetFile - Should have been cleaned up in completion callback" << llendl;
+ fclose(mFP);
+ mFP = NULL;
+ }
+}
+
+void LLTransferTargetFile::applyParams(const LLTransferTargetParams &params)
+{
+ if (params.getType() != mType)
+ {
+ llwarns << "Target parameter type doesn't match!" << llendl;
+ return;
+ }
+
+ mParams = (LLTransferTargetParamsFile &)params;
+}
+
+LLTSCode LLTransferTargetFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size)
+{
+ //llinfos << "LLTransferTargetFile::dataCallback" << llendl;
+ //llinfos << "Packet: " << packet_id << llendl;
+
+ if (!mFP)
+ {
+ mFP = LLFile::fopen(mParams.mFilename.c_str(), "wb"); /* Flawfinder: ignore */
+
+ if (!mFP)
+ {
+ llwarns << "Failure opening " << mParams.mFilename << " for write by LLTransferTargetFile" << llendl;
+ return LLTS_ERROR;
+ }
+ }
+ if (!in_size)
+ {
+ return LLTS_OK;
+ }
+
+ S32 count = (S32)fwrite(in_datap, 1, in_size, mFP);
+ if (count != in_size)
+ {
+ llwarns << "Failure in LLTransferTargetFile::dataCallback!" << llendl;
+ return LLTS_ERROR;
+ }
+ return LLTS_OK;
+}
+
+void LLTransferTargetFile::completionCallback(const LLTSCode status)
+{
+ llinfos << "LLTransferTargetFile::completionCallback" << llendl;
+ if (mFP)
+ {
+ fclose(mFP);
+ }
+
+ // Still need to gracefully handle error conditions.
+ switch (status)
+ {
+ case LLTS_DONE:
+ break;
+ case LLTS_ABORT:
+ case LLTS_ERROR:
+ // We're aborting this transfer, we don't want to keep this file.
+ llwarns << "Aborting file transfer for " << mParams.mFilename << llendl;
+ if (mFP)
+ {
+ // Only need to remove file if we successfully opened it.
+ LLFile::remove(mParams.mFilename.c_str());
+ }
+ default:
+ break;
+ }
+
+ mFP = NULL;
+ if (mParams.mCompleteCallback)
+ {
+ mParams.mCompleteCallback(status, mParams.mUserData);
+ }
+}
diff --git a/indra/llmessage/lltransfertargetfile.h b/indra/llmessage/lltransfertargetfile.h
new file mode 100644
index 0000000000..63cc21262b
--- /dev/null
+++ b/indra/llmessage/lltransfertargetfile.h
@@ -0,0 +1,53 @@
+/**
+ * @file lltransfertargetfile.h
+ * @brief Transfer system for receiving a file.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERTARGETFILE_H
+#define LL_LLTRANSFERTARGETFILE_H
+
+#include "lltransfermanager.h"
+
+#include <stdio.h>
+
+typedef void (*LLTTFCompleteCallback)(const LLTSCode status, void *user_data);
+
+class LLTransferTargetParamsFile : public LLTransferTargetParams
+{
+public:
+ LLTransferTargetParamsFile() : LLTransferTargetParams(LLTTT_FILE) {}
+ void setFilename(const char *filename) { mFilename = filename; }
+ void setCallback(LLTTFCompleteCallback cb, void *user_data) { mCompleteCallback = cb; mUserData = user_data; }
+
+ friend class LLTransferTargetFile;
+protected:
+ LLString mFilename;
+ LLTTFCompleteCallback mCompleteCallback;
+ void * mUserData;
+};
+
+
+class LLTransferTargetFile : public LLTransferTarget
+{
+public:
+ LLTransferTargetFile(const LLUUID &uuid);
+ virtual ~LLTransferTargetFile();
+
+ static void requestTransfer(LLTransferTargetChannel *channelp,
+ const char *local_filename,
+ const LLTransferSourceParams &source_params,
+ LLTTFCompleteCallback callback);
+protected:
+ /*virtual*/ void applyParams(const LLTransferTargetParams &params);
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ LLTransferTargetParamsFile mParams;
+
+ FILE *mFP;
+};
+
+#endif // LL_LLTRANSFERTARGETFILE_H
diff --git a/indra/llmessage/lltransfertargetvfile.cpp b/indra/llmessage/lltransfertargetvfile.cpp
new file mode 100644
index 0000000000..9e323537d7
--- /dev/null
+++ b/indra/llmessage/lltransfertargetvfile.cpp
@@ -0,0 +1,187 @@
+/**
+ * @file lltransfertargetvfile.cpp
+ * @brief Transfer system for receiving a vfile.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltransfertargetvfile.h"
+#include "llerror.h"
+
+#include "llvfile.h"
+
+//static
+std::list<LLTransferTargetParamsVFile*> LLTransferTargetVFile::sCallbackQueue;
+
+//static
+void LLTransferTargetVFile::updateQueue(bool shutdown)
+{
+ for(std::list<LLTransferTargetParamsVFile*>::iterator iter = sCallbackQueue.begin();
+ iter != sCallbackQueue.end(); )
+ {
+ std::list<LLTransferTargetParamsVFile*>::iterator curiter = iter++;
+ LLTransferTargetParamsVFile* params = *curiter;
+ LLVFSThread::status_t s = LLVFile::getVFSThread()->getRequestStatus(params->mHandle);
+ if (s == LLVFSThread::STATUS_COMPLETE || s == LLVFSThread::STATUS_EXPIRED)
+ {
+ params->mCompleteCallback(params->mErrCode, params->mUserDatap);
+ delete params;
+ iter = sCallbackQueue.erase(curiter);
+ }
+ else if (shutdown)
+ {
+ delete params;
+ iter = sCallbackQueue.erase(curiter);
+ }
+ }
+}
+
+
+LLTransferTargetParamsVFile::LLTransferTargetParamsVFile() :
+ LLTransferTargetParams(LLTTT_VFILE),
+ mAssetType(LLAssetType::AT_NONE),
+ mCompleteCallback(NULL),
+ mUserDatap(NULL),
+ mErrCode(0),
+ mHandle(LLVFSThread::nullHandle())
+{
+}
+
+void LLTransferTargetParamsVFile::setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type)
+{
+ mAssetID = asset_id;
+ mAssetType = asset_type;
+}
+
+void LLTransferTargetParamsVFile::setCallback(LLTTVFCompleteCallback cb, void *user_data)
+{
+ mCompleteCallback = cb;
+ mUserDatap = user_data;
+}
+
+
+LLTransferTargetVFile::LLTransferTargetVFile(const LLUUID &uuid) :
+ LLTransferTarget(LLTTT_VFILE, uuid),
+ mNeedsCreate(TRUE)
+{
+ mTempID.generate();
+}
+
+
+LLTransferTargetVFile::~LLTransferTargetVFile()
+{
+}
+
+
+void LLTransferTargetVFile::applyParams(const LLTransferTargetParams &params)
+{
+ if (params.getType() != mType)
+ {
+ llwarns << "Target parameter type doesn't match!" << llendl;
+ return;
+ }
+
+ mParams = (LLTransferTargetParamsVFile &)params;
+}
+
+
+LLTSCode LLTransferTargetVFile::dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size)
+{
+ //llinfos << "LLTransferTargetFile::dataCallback" << llendl;
+ //llinfos << "Packet: " << packet_id << llendl;
+
+ LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND);
+ if (mNeedsCreate)
+ {
+ vf.setMaxSize(mSize);
+ mNeedsCreate = FALSE;
+ }
+
+ if (!in_size)
+ {
+ return LLTS_OK;
+ }
+
+ if (!vf.write(in_datap, in_size))
+ {
+ llwarns << "Failure in LLTransferTargetVFile::dataCallback!" << llendl;
+ return LLTS_ERROR;
+ }
+ return LLTS_OK;
+}
+
+
+void LLTransferTargetVFile::completionCallback(const LLTSCode status)
+{
+ //llinfos << "LLTransferTargetVFile::completionCallback" << llendl;
+
+ if (!gAssetStorage)
+ {
+ llwarns << "Aborting vfile transfer after asset storage shut down!" << llendl;
+ return;
+ }
+ LLVFSThread::handle_t handle = LLVFSThread::nullHandle();
+
+ // Still need to gracefully handle error conditions.
+ S32 err_code = 0;
+ switch (status)
+ {
+ case LLTS_DONE:
+ if (!mNeedsCreate)
+ {
+ handle = LLVFile::getVFSThread()->rename(gAssetStorage->mVFS,
+ mTempID, mParams.getAssetType(),
+ mParams.getAssetID(), mParams.getAssetType(),
+ LLVFSThread::AUTO_DELETE);
+ }
+ err_code = LL_ERR_NOERR;
+ // llinfos << "Successful vfile transfer for " << mParams.getAssetID() << llendl;
+ break;
+ case LLTS_ERROR:
+ case LLTS_ABORT:
+ case LLTS_UNKNOWN_SOURCE:
+ default:
+ {
+ // We're aborting this transfer, we don't want to keep this file.
+ llwarns << "Aborting vfile transfer for " << mParams.getAssetID() << llendl;
+ LLVFile vf(gAssetStorage->mVFS, mTempID, mParams.getAssetType(), LLVFile::APPEND);
+ vf.remove();
+ }
+ break;
+ }
+
+ switch (status)
+ {
+ case LLTS_DONE:
+ err_code = LL_ERR_NOERR;
+ break;
+ case LLTS_UNKNOWN_SOURCE:
+ err_code = LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE;
+ break;
+ case LLTS_INSUFFICIENT_PERMISSIONS:
+ err_code = LL_ERR_INSUFFICIENT_PERMISSIONS;
+ break;
+ case LLTS_ERROR:
+ case LLTS_ABORT:
+ default:
+ err_code = LL_ERR_ASSET_REQUEST_FAILED;
+ break;
+ }
+ if (mParams.mCompleteCallback)
+ {
+ if (handle != LLVFSThread::nullHandle())
+ {
+ LLTransferTargetParamsVFile* params = new LLTransferTargetParamsVFile(mParams);
+ params->mErrCode = err_code;
+ params->mHandle = handle;
+ sCallbackQueue.push_back(params);
+ }
+ else
+ {
+ mParams.mCompleteCallback(err_code, mParams.mUserDatap);
+ }
+ }
+}
diff --git a/indra/llmessage/lltransfertargetvfile.h b/indra/llmessage/lltransfertargetvfile.h
new file mode 100644
index 0000000000..7614021179
--- /dev/null
+++ b/indra/llmessage/lltransfertargetvfile.h
@@ -0,0 +1,70 @@
+/**
+ * @file lltransfertargetvfile.h
+ * @brief Transfer system for receiving a vfile.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTRANSFERTARGETVFILE_H
+#define LL_LLTRANSFERTARGETVFILE_H
+
+#include "lltransfermanager.h"
+#include "llassetstorage.h"
+#include "llvfile.h"
+
+class LLVFile;
+
+// Lame, an S32 for now until I figure out the deal with how we want to do
+// error codes.
+typedef void (*LLTTVFCompleteCallback)(const S32 status, void *user_data);
+
+class LLTransferTargetParamsVFile : public LLTransferTargetParams
+{
+public:
+ LLTransferTargetParamsVFile();
+
+ void setAsset(const LLUUID &asset_id, const LLAssetType::EType asset_type);
+ void setCallback(LLTTVFCompleteCallback cb, void *user_data);
+
+ LLUUID getAssetID() const { return mAssetID; }
+ LLAssetType::EType getAssetType() const { return mAssetType; }
+
+ friend class LLTransferTargetVFile;
+protected:
+ LLUUID mAssetID;
+ LLAssetType::EType mAssetType;
+
+ LLTTVFCompleteCallback mCompleteCallback;
+ void * mUserDatap;
+ S32 mErrCode;
+ LLVFSThread::handle_t mHandle;
+};
+
+
+class LLTransferTargetVFile : public LLTransferTarget
+{
+public:
+ LLTransferTargetVFile(const LLUUID &uuid);
+ virtual ~LLTransferTargetVFile();
+
+ static void requestTransfer(LLTransferTargetChannel *channelp,
+ const char *local_filename,
+ const LLTransferSourceParams &source_params,
+ LLTTVFCompleteCallback callback);
+ static void updateQueue(bool shutdown = false);
+
+protected:
+ /*virtual*/ void applyParams(const LLTransferTargetParams &params);
+ /*virtual*/ LLTSCode dataCallback(const S32 packet_id, U8 *in_datap, const S32 in_size);
+ /*virtual*/ void completionCallback(const LLTSCode status);
+
+ LLTransferTargetParamsVFile mParams;
+
+ BOOL mNeedsCreate;
+ LLUUID mTempID;
+
+ static std::list<LLTransferTargetParamsVFile*> sCallbackQueue;
+};
+
+#endif // LL_LLTRANSFERTARGETFILE_H
diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp
new file mode 100644
index 0000000000..ea0b13f703
--- /dev/null
+++ b/indra/llmessage/llurlrequest.cpp
@@ -0,0 +1,650 @@
+/**
+ * @file llurlrequest.cpp
+ * @author Phoenix
+ * @date 2005-04-28
+ * @brief Implementation of the URLRequest class and related classes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llurlrequest.h"
+
+#include <curl/curl.h>
+#include <algorithm>
+
+#include "llioutil.h"
+#include "llmemtype.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llstring.h"
+
+static const U32 HTTP_STATUS_PIPE_ERROR = 499;
+
+/**
+ * String constants
+ */
+const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
+
+
+static
+size_t headerCallback(void* data, size_t size, size_t nmemb, void* user);
+
+/**
+ * class LLURLRequestDetail
+ */
+class LLURLRequestDetail
+{
+public:
+ LLURLRequestDetail();
+ ~LLURLRequestDetail();
+ CURLM* mCurlMulti;
+ CURL* mCurl;
+ struct curl_slist* mHeaders;
+ char* mURL;
+ char mCurlErrorBuf[CURL_ERROR_SIZE + 1]; /* Flawfinder: ignore */
+ bool mNeedToRemoveEasyHandle;
+ LLBufferArray* mResponseBuffer;
+ LLChannelDescriptors mChannels;
+ U8* mLastRead;
+ U32 mBodyLimit;
+ bool mIsBodyLimitSet;
+};
+
+LLURLRequestDetail::LLURLRequestDetail() :
+ mCurlMulti(NULL),
+ mCurl(NULL),
+ mHeaders(NULL),
+ mURL(NULL),
+ mNeedToRemoveEasyHandle(false),
+ mResponseBuffer(NULL),
+ mLastRead(NULL),
+ mBodyLimit(0),
+ mIsBodyLimitSet(false)
+
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mCurlErrorBuf[0] = '\0';
+}
+
+LLURLRequestDetail::~LLURLRequestDetail()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mCurl)
+ {
+ if(mNeedToRemoveEasyHandle && mCurlMulti)
+ {
+ curl_multi_remove_handle(mCurlMulti, mCurl);
+ mNeedToRemoveEasyHandle = false;
+ }
+ curl_easy_cleanup(mCurl);
+ mCurl = NULL;
+ }
+ if(mCurlMulti)
+ {
+ curl_multi_cleanup(mCurlMulti);
+ mCurlMulti = NULL;
+ }
+ if(mHeaders)
+ {
+ curl_slist_free_all(mHeaders);
+ mHeaders = NULL;
+ }
+ delete[] mURL;
+ mURL = NULL;
+ mResponseBuffer = NULL;
+ mLastRead = NULL;
+}
+
+
+/**
+ * class LLURLRequest
+ */
+
+static std::string sCAFile("");
+static std::string sCAPath("");
+
+LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :
+ mAction(action)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ initialize();
+}
+
+LLURLRequest::LLURLRequest(
+ LLURLRequest::ERequestAction action,
+ const std::string& url) :
+ mAction(action)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ initialize();
+ setURL(url);
+}
+
+LLURLRequest::~LLURLRequest()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ delete mDetail;
+}
+
+void LLURLRequest::setURL(const std::string& url)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mDetail->mURL)
+ {
+ // *NOTE: if any calls to set the url have been made to curl,
+ // this will probably lead to a crash.
+ delete[] mDetail->mURL;
+ mDetail->mURL = NULL;
+ }
+ if(!url.empty())
+ {
+ mDetail->mURL = new char[url.size() + 1];
+ url.copy(mDetail->mURL, url.size());
+ mDetail->mURL[url.size()] = '\0';
+ }
+}
+
+void LLURLRequest::addHeader(const char* header)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mDetail->mHeaders = curl_slist_append(mDetail->mHeaders, header);
+}
+
+void LLURLRequest::requestEncoding(const char* encoding)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_ENCODING, encoding);
+}
+
+void LLURLRequest::setBodyLimit(U32 size)
+{
+ mDetail->mBodyLimit = size;
+ mDetail->mIsBodyLimitSet = true;
+}
+
+void LLURLRequest::checkRootCertificate(bool check, const char* caBundle)
+{
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_SSL_VERIFYPEER, (check? TRUE : FALSE));
+ if (caBundle)
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, caBundle);
+ }
+}
+
+void LLURLRequest::setCallback(LLURLRequestComplete* callback)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mCompletionCallback = callback;
+
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_HEADERFUNCTION, &headerCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEHEADER, callback);
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequest::handleError(
+ LLIOPipe::EStatus status,
+ LLPumpIO* pump)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ if(mCompletionCallback && pump)
+ {
+ LLURLRequestComplete* complete = NULL;
+ complete = (LLURLRequestComplete*)mCompletionCallback.get();
+ complete->httpStatus(
+ HTTP_STATUS_PIPE_ERROR,
+ LLIOPipe::lookupStatusString(status));
+ complete->responseStatus(status);
+ pump->respond(complete);
+ mCompletionCallback = NULL;
+ }
+ return status;
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequest::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ //llinfos << "LLURLRequest::process_impl()" << llendl;
+ if(!buffer) return STATUS_ERROR;
+ switch(mState)
+ {
+ case STATE_INITIALIZED:
+ {
+ PUMP_DEBUG;
+ // We only need to wait for input if we are uploading
+ // something.
+ if(((HTTP_PUT == mAction) || (HTTP_POST == mAction)) && !eos)
+ {
+ // we're waiting to get all of the information
+ return STATUS_BREAK;
+ }
+
+ // *FIX: bit of a hack, but it should work. The configure and
+ // callback method expect this information to be ready.
+ mDetail->mResponseBuffer = buffer.get();
+ mDetail->mChannels = channels;
+ if(!configure())
+ {
+ return STATUS_ERROR;
+ }
+ mState = STATE_WAITING_FOR_RESPONSE;
+
+ // *FIX: Maybe we should just go to the next state now...
+ return STATUS_BREAK;
+ }
+ case STATE_WAITING_FOR_RESPONSE:
+ case STATE_PROCESSING_RESPONSE:
+ {
+ PUMP_DEBUG;
+ const S32 MAX_CALLS = 5;
+ S32 count = MAX_CALLS;
+ CURLMcode code;
+ LLIOPipe::EStatus status = STATUS_BREAK;
+ S32 queue;
+ do
+ {
+ code = curl_multi_perform(mDetail->mCurlMulti, &queue);
+ }while((CURLM_CALL_MULTI_PERFORM == code) && (queue > 0) && count--);
+ CURLMsg* curl_msg;
+ do
+ {
+ curl_msg = curl_multi_info_read(mDetail->mCurlMulti, &queue);
+ if(curl_msg && (curl_msg->msg == CURLMSG_DONE))
+ {
+ mState = STATE_HAVE_RESPONSE;
+
+ CURLcode result = curl_msg->data.result;
+ switch(result)
+ {
+ case CURLE_OK:
+ case CURLE_WRITE_ERROR:
+ // NB: The error indication means that we stopped the
+ // writing due the body limit being reached
+ if(mCompletionCallback && pump)
+ {
+ LLURLRequestComplete* complete = NULL;
+ complete = (LLURLRequestComplete*)
+ mCompletionCallback.get();
+ complete->responseStatus(
+ result == CURLE_OK
+ ? STATUS_OK : STATUS_STOP);
+ LLPumpIO::links_t chain;
+ LLPumpIO::LLLinkInfo link;
+ link.mPipe = mCompletionCallback;
+ link.mChannels = LLBufferArray::makeChannelConsumer(
+ channels);
+ chain.push_back(link);
+ pump->respond(chain, buffer, context);
+ mCompletionCallback = NULL;
+ }
+ break;
+ case CURLE_COULDNT_CONNECT:
+ status = STATUS_NO_CONNECTION;
+ break;
+ default:
+ llwarns << "URLRequest Error: " << curl_msg->data.result
+ << ", "
+#if LL_DARWIN
+ // curl_easy_strerror was added in libcurl 7.12.0. Unfortunately, the version in the Mac OS X 10.3.9 SDK is 7.10.2...
+ // There's a problem with the custom curl headers in our build that keeps me from #ifdefing this on the libcurl version number
+ // (the correct check would be #if LIBCURL_VERSION_NUM >= 0x070c00). We'll fix the header problem soon, but for now
+ // just punt and print the numeric error code on the Mac.
+ << curl_msg->data.result
+#else // LL_DARWIN
+ << curl_easy_strerror(curl_msg->data.result)
+#endif // LL_DARWIN
+ << ", "
+ << (mDetail->mURL ? mDetail->mURL : "<EMPTY URL>")
+ << llendl;
+ status = STATUS_ERROR;
+ break;
+ }
+ curl_multi_remove_handle(mDetail->mCurlMulti, mDetail->mCurl);
+ mDetail->mNeedToRemoveEasyHandle = false;
+ }
+ }while(curl_msg && (queue > 0));
+ return status;
+ }
+ case STATE_HAVE_RESPONSE:
+ PUMP_DEBUG;
+ // we already stuffed everything into channel in in the curl
+ // callback, so we are done.
+ eos = true;
+ return STATUS_DONE;
+
+ default:
+ PUMP_DEBUG;
+ return STATUS_ERROR;
+ }
+}
+
+void LLURLRequest::initialize()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mState = STATE_INITIALIZED;
+ mDetail = new LLURLRequestDetail;
+ mDetail->mCurl = curl_easy_init();
+ mDetail->mCurlMulti = curl_multi_init();
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEFUNCTION, &downCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_READFUNCTION, &upCallback);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_READDATA, this);
+ curl_easy_setopt(
+ mDetail->mCurl,
+ CURLOPT_ERRORBUFFER,
+ mDetail->mCurlErrorBuf);
+
+ if(sCAPath != std::string(""))
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAPATH, sCAPath.c_str());
+ }
+ if(sCAFile != std::string(""))
+ {
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CAINFO, sCAFile.c_str());
+ }
+}
+
+bool LLURLRequest::configure()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ bool rv = false;
+ S32 bytes = mDetail->mResponseBuffer->countAfter(
+ mDetail->mChannels.in(),
+ NULL);
+ switch(mAction)
+ {
+ case HTTP_GET:
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_HTTPGET, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_FOLLOWLOCATION, 1);
+ rv = true;
+ break;
+
+ case HTTP_PUT:
+ // Disable the expect http 1.1 extension. POST and PUT default
+ // to turning this on, and I am not too sure what it means.
+ addHeader("Expect:");
+
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_UPLOAD, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_INFILESIZE, bytes);
+ rv = true;
+ break;
+
+ case HTTP_POST:
+ // Disable the expect http 1.1 extension. POST and PUT default
+ // to turning this on, and I am not too sure what it means.
+ addHeader("Expect:");
+
+ // Disable the content type http header.
+ // *FIX: what should it be?
+ addHeader("Content-Type:");
+
+ // Set the handle for an http post
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POST, 1);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDS, NULL);
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_POSTFIELDSIZE, bytes);
+ rv = true;
+ break;
+
+ case HTTP_DELETE:
+ // Set the handle for an http post
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_CUSTOMREQUEST, "DELETE");
+ rv = true;
+ break;
+
+ default:
+ llwarns << "Unhandled URLRequest action: " << mAction << llendl;
+ break;
+ }
+ if(rv)
+ {
+ if(mDetail->mHeaders)
+ {
+ curl_easy_setopt(
+ mDetail->mCurl,
+ CURLOPT_HTTPHEADER,
+ mDetail->mHeaders);
+ }
+ curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL);
+ curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl);
+ mDetail->mNeedToRemoveEasyHandle = true;
+ }
+ return rv;
+}
+
+// static
+size_t LLURLRequest::downCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ LLURLRequest* req = (LLURLRequest*)user;
+ if(STATE_WAITING_FOR_RESPONSE == req->mState)
+ {
+ req->mState = STATE_PROCESSING_RESPONSE;
+ }
+ U32 bytes = size * nmemb;
+ if (req->mDetail->mIsBodyLimitSet)
+ {
+ if (bytes > req->mDetail->mBodyLimit)
+ {
+ bytes = req->mDetail->mBodyLimit;
+ req->mDetail->mBodyLimit = 0;
+ }
+ else
+ {
+ req->mDetail->mBodyLimit -= bytes;
+ }
+ }
+
+ req->mDetail->mResponseBuffer->append(
+ req->mDetail->mChannels.out(),
+ (U8*)data,
+ bytes);
+ return bytes;
+}
+
+// static
+size_t LLURLRequest::upCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ LLURLRequest* req = (LLURLRequest*)user;
+ S32 bytes = llmin(
+ (S32)(size * nmemb),
+ req->mDetail->mResponseBuffer->countAfter(
+ req->mDetail->mChannels.in(),
+ req->mDetail->mLastRead));
+ req->mDetail->mLastRead = req->mDetail->mResponseBuffer->readAfter(
+ req->mDetail->mChannels.in(),
+ req->mDetail->mLastRead,
+ (U8*)data,
+ bytes);
+ return bytes;
+}
+
+static
+size_t headerCallback(void* data, size_t size, size_t nmemb, void* user)
+{
+ const char* headerLine = (const char*)data;
+ size_t headerLen = size * nmemb;
+ LLURLRequestComplete* complete = (LLURLRequestComplete*)user;
+
+ // FIXME: This should be a utility in llstring.h: isascii()
+ for (size_t i = 0; i < headerLen; ++i)
+ {
+ if (headerLine[i] < 0)
+ {
+ return headerLen;
+ }
+ }
+
+ size_t sep;
+ for (sep = 0; sep < headerLen && headerLine[sep] != ':'; ++sep) { }
+
+ if (sep < headerLen && complete)
+ {
+ std::string key(headerLine, sep);
+ std::string value(headerLine + sep + 1, headerLen - sep - 1);
+
+ key = utf8str_tolower(utf8str_trim(key));
+ value = utf8str_trim(value);
+
+ complete->header(key, value);
+ }
+ else
+ {
+ std::string s(headerLine, headerLen);
+
+ std::string::iterator end = s.end();
+ std::string::iterator pos1 = std::find(s.begin(), end, ' ');
+ if (pos1 != end) ++pos1;
+ std::string::iterator pos2 = std::find(pos1, end, ' ');
+ if (pos2 != end) ++pos2;
+ std::string::iterator pos3 = std::find(pos2, end, '\r');
+
+ std::string version(s.begin(), pos1);
+ std::string status(pos1, pos2);
+ std::string reason(pos2, pos3);
+
+ int statusCode = atoi(status.c_str());
+ if (statusCode > 0)
+ {
+ complete->httpStatus((U32)statusCode, reason);
+ }
+ }
+
+ return headerLen;
+}
+
+//static
+void LLURLRequest::setCertificateAuthorityFile(const std::string& file_name)
+{
+ sCAFile = file_name;
+}
+
+//static
+void LLURLRequest::setCertificateAuthorityPath(const std::string& path)
+{
+ sCAPath = path;
+}
+
+/**
+ * LLContextURLExtractor
+ */
+// virtual
+LLIOPipe::EStatus LLContextURLExtractor::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ // The destination host is in the context.
+ if(context.isUndefined() || !mRequest)
+ {
+ return STATUS_PRECONDITION_NOT_MET;
+ }
+
+ // copy in to out, since this just extract the URL and does not
+ // actually change the data.
+ LLChangeChannel change(channels.in(), channels.out());
+ std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
+
+ // find the context url
+ if(context.has(CONTEXT_DEST_URI_SD_LABEL))
+ {
+ mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL]);
+ return STATUS_DONE;
+ }
+ return STATUS_ERROR;
+}
+
+
+/**
+ * LLURLRequestComplete
+ */
+LLURLRequestComplete::LLURLRequestComplete() :
+ mRequestStatus(LLIOPipe::STATUS_ERROR)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+}
+
+// virtual
+LLURLRequestComplete::~LLURLRequestComplete()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+}
+
+//virtual
+void LLURLRequestComplete::header(const std::string& header, const std::string& value)
+{
+}
+
+//virtual
+void LLURLRequestComplete::httpStatus(U32 status, const std::string& reason)
+{
+}
+
+//virtual
+void LLURLRequestComplete::complete(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+{
+ if(STATUS_OK == mRequestStatus)
+ {
+ response(channels, buffer);
+ }
+ else
+ {
+ noResponse();
+ }
+}
+
+//virtual
+void LLURLRequestComplete::response(const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer)
+{
+ llwarns << "LLURLRequestComplete::response default implementation called"
+ << llendl;
+}
+
+//virtual
+void LLURLRequestComplete::noResponse()
+{
+ llwarns << "LLURLRequestComplete::noResponse default implementation called"
+ << llendl;
+}
+
+void LLURLRequestComplete::responseStatus(LLIOPipe::EStatus status)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
+ mRequestStatus = status;
+}
+
+// virtual
+LLIOPipe::EStatus LLURLRequestComplete::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ complete(channels, buffer);
+ return STATUS_OK;
+}
diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h
new file mode 100644
index 0000000000..38c801cb10
--- /dev/null
+++ b/indra/llmessage/llurlrequest.h
@@ -0,0 +1,395 @@
+/**
+ * @file llurlrequest.h
+ * @author Phoenix
+ * @date 2005-04-21
+ * @brief Declaration of url based requests on pipes.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURLREQUEST_H
+#define LL_LLURLREQUEST_H
+
+/**
+ * This file holds the declaration of useful classes for dealing with
+ * url based client requests.
+ */
+
+#include <string>
+#include "lliopipe.h"
+#include "llchainio.h"
+
+class LLURLRequestDetail;
+
+class LLURLRequestComplete;
+
+/**
+ * @class LLURLRequest
+ * @brief Class to handle url based requests.
+ * @see LLIOPipe
+ *
+ * Currently, this class is implemented on top of curl. From the
+ * vantage of a programmer using this class, you do not care so much,
+ * but it's useful to know since in order to accomplish 'non-blocking'
+ * behavior, we have to use a more expensive curl interface which can
+ * still block if the server enters a half-accepted state. It would be
+ * worth the time and effort to eventually port this to a raw client
+ * socket.
+ */
+class LLURLRequest : public LLIOPipe
+{
+public:
+ /**
+ * @brief This enumeration is for specifying the type of request.
+ */
+ enum ERequestAction
+ {
+ INVALID,
+ HTTP_GET,
+ HTTP_PUT,
+ HTTP_POST,
+ HTTP_DELETE,
+ REQUEST_ACTION_COUNT
+ };
+
+ /**
+ * @brief Constructor.
+ *
+ * @param action One of the ERequestAction enumerations.
+ */
+ LLURLRequest(ERequestAction action);
+
+ /**
+ * @brief Constructor.
+ *
+ * @param action One of the ERequestAction enumerations.
+ * @param url The url of the request. It should already be encoded.
+ */
+ LLURLRequest(ERequestAction action, const std::string& url);
+
+ /**
+ * @brief Destructor.
+ */
+ virtual ~LLURLRequest();
+
+ /* @name Instance methods
+ */
+ //@{
+ /**
+ * @brief Set the url for the request
+ *
+ * This method assumes the url is encoded appropriately for the
+ * request.
+ * The url must be set somehow before the first call to process(),
+ * or the url will not be set correctly.
+ *
+ */
+ void setURL(const std::string& url);
+
+ /**
+ * @brief Add a header to the http post.
+ *
+ * The header must be correctly formatted for HTTP requests. This
+ * provides a raw interface if you know what kind of request you
+ * will be making during construction of this instance. All
+ * required headers will be automatically constructed, so this is
+ * usually useful for encoding parameters.
+ */
+ void addHeader(const char* header);
+
+ /**
+ * @brief Check remote server certificate signed by a known root CA.
+ *
+ * Set whether request will check that remote server
+ * certificates are signed by a known root CA when using HTTPS.
+ * Use the supplied root certificate bundle if supplied, else use
+ * the standard bundle as found by libcurl and openssl.
+ */
+ void checkRootCertificate(bool check, const char* caBundle = NULL);
+
+ /**
+ * @brief Request a particular response encoding if available.
+ *
+ * This call is a shortcut for requesting a particular encoding
+ * from the server, eg, 'gzip'.
+ */
+ void requestEncoding(const char* encoding);
+
+ /**
+ * @brief Return at most size bytes of body.
+ *
+ * If the body had more bytes than this limit, they will not be
+ * returned and the connection closed. In this case, STATUS_STOP
+ * will be passed to responseStatus();
+ */
+ void setBodyLimit(U32 size);
+
+ /**
+ * @brief Set a completion callback for this URLRequest.
+ *
+ * The callback is added to this URLRequet's pump when either the
+ * entire buffer is known or an error like timeout or connection
+ * refused has happened. In the case of a complete transfer, this
+ * object builds a response chain such that the callback and the
+ * next process consumer get to read the output.
+ *
+ * This setup is a little fragile since the url request consumer
+ * might not just read the data - it may do a channel change,
+ * which invalidates the input to the callback, but it works well
+ * in practice.
+ */
+ void setCallback(LLURLRequestComplete* callback);
+ //@}
+
+ /**
+ * @ brief Set certificate authority file used to verify HTTPS certs.
+ */
+ static void setCertificateAuthorityFile(const std::string& file_name);
+
+ /**
+ * @ brief Set certificate authority path used to verify HTTPS certs.
+ */
+ static void setCertificateAuthorityPath(const std::string& path);
+
+ /* @name LLIOPipe virtual implementations
+ */
+public:
+ /**
+ * @brief Give this pipe a chance to handle a generated error
+ */
+ virtual EStatus handleError(EStatus status, LLPumpIO* pump);
+
+protected:
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ enum EState
+ {
+ STATE_INITIALIZED,
+ STATE_WAITING_FOR_RESPONSE,
+ STATE_PROCESSING_RESPONSE,
+ STATE_HAVE_RESPONSE,
+ };
+ EState mState;
+ ERequestAction mAction;
+ LLURLRequestDetail* mDetail;
+ LLIOPipe::ptr_t mCompletionCallback;
+
+private:
+ /**
+ * @brief Initialize the object. Called during construction.
+ */
+ void initialize();
+
+ /**
+ * @brief Handle action specific url request configuration.
+ *
+ * @return Returns true if this is configured.
+ */
+ bool configure();
+
+ /**
+ * @brief Download callback method.
+ */
+ static size_t downCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user);
+
+ /**
+ * @brief Upload callback method.
+ */
+ static size_t upCallback(
+ void* data,
+ size_t size,
+ size_t nmemb,
+ void* user);
+
+ /**
+ * @brief Declaration of unimplemented method to prevent copy
+ * construction.
+ */
+ LLURLRequest(const LLURLRequest&);
+};
+
+
+/**
+ * @class LLContextURLExtractor
+ * @brief This class unpacks the url out of a agent usher service so
+ * it can be packed into a LLURLRequest object.
+ * @see LLIOPipe
+ *
+ * This class assumes that the context is a map that contains an entry
+ * named CONTEXT_DEST_URI_SD_LABEL.
+ */
+class LLContextURLExtractor : public LLIOPipe
+{
+public:
+ LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {}
+ ~LLContextURLExtractor() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ LLURLRequest* mRequest;
+};
+
+
+/**
+ * @class LLURLRequestComplete
+ * @brief Class which can optionally be used with an LLURLRequest to
+ * get notification when the url request is complete.
+ */
+class LLURLRequestComplete : public LLIOPipe
+{
+public:
+
+ virtual void header(const std::string& header, const std::string& value);
+ ///< Called once for each header received, prior to httpStatus
+
+ virtual void httpStatus(U32 status, const std::string& reason);
+ ///< Always called on request completion, prior to complete
+
+ virtual void complete(
+ const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer);
+
+ /**
+ * @brief This method is called when we got a valid response.
+ *
+ * It is up to class implementers to do something useful here.
+ */
+ virtual void response(
+ const LLChannelDescriptors& channels,
+ const buffer_ptr_t& buffer);
+
+ /**
+ * @brief This method is called if there was no response.
+ *
+ * It is up to class implementers to do something useful here.
+ */
+ virtual void noResponse();
+
+ /**
+ * @brief This method will be called by the LLURLRequest object.
+ *
+ * If this is set to STATUS_OK or STATUS_STOP, then the transfer
+ * is asssumed to have worked. This will lead to calling response()
+ * on the next call to process(). Otherwise, this object will call
+ * noResponse() on the next call to process.
+ * @param status The status of the URLRequest.
+ */
+ void responseStatus(EStatus status);
+
+ // constructor & destructor.
+ LLURLRequestComplete();
+ virtual ~LLURLRequestComplete();
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+ // value to note if we actually got the response. This value
+ // depends on correct useage from the LLURLRequest instance.
+ EStatus mRequestStatus;
+};
+
+
+/**
+ * @class LLURLRequestClientFactory
+ * @brief Template class to build url request based client chains
+ *
+ * This class eases construction of a basic sd rpc client. Here is an
+ * example of it's use:
+ * <code>
+ * class LLUsefulService : public LLService { ... }<br>
+ * LLService::registerCreator(<br>
+ * "useful",<br>
+ * LLService::creator_t(new LLURLRequestClientFactory<LLUsefulService>))<br>
+ * </code>
+ *
+ * This class should work, but I never got around to using/testing it.
+ *
+ */
+#if 0
+template<class Client>
+class LLURLRequestClientFactory : public LLChainIOFactory
+{
+public:
+ LLURLRequestClientFactory(LLURLRequest::ERequestAction action) {}
+ LLURLRequestClientFactory(
+ LLURLRequest::ERequestAction action,
+ const std::string& fixed_url) :
+ mAction(action),
+ mURL(fixed_url)
+ {
+ }
+ virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
+ {
+ lldebugs << "LLURLRequestClientFactory::build" << llendl;
+ LLIOPipe::ptr_t service(new Client);
+ chain.push_back(service);
+ LLURLRequest* http(new LLURLRequest(mAction));
+ LLIOPipe::ptr_t http_pipe(http);
+ // *FIX: how do we know the content type?
+ //http->addHeader("Content-Type: text/llsd");
+ if(mURL.empty())
+ {
+ chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
+ }
+ else
+ {
+ http->setURL(mURL);
+ }
+ chain.push_back(http_pipe);
+ chain.push_back(service);
+ return true;
+ }
+
+protected:
+ LLURLRequest::ERequestAction mAction;
+ std::string mURL;
+};
+#endif
+
+/**
+ * External constants
+ */
+extern const std::string CONTEXT_DEST_URI_SD_LABEL;
+
+#endif // LL_LLURLREQUEST_H
diff --git a/indra/llmessage/lluseroperation.cpp b/indra/llmessage/lluseroperation.cpp
new file mode 100644
index 0000000000..f7506c955c
--- /dev/null
+++ b/indra/llmessage/lluseroperation.cpp
@@ -0,0 +1,161 @@
+/**
+ * @file lluseroperation.cpp
+ * @brief LLUserOperation class definition.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lluseroperation.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLUserOperationMgr* gUserOperationMgr = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLUserOperation
+///----------------------------------------------------------------------------
+
+LLUserOperation::LLUserOperation(const LLUUID& agent_id)
+: mAgentID(agent_id),
+ mTimer()
+{
+ mTransactionID.generate();
+}
+
+LLUserOperation::LLUserOperation(const LLUUID& agent_id,
+ const LLUUID& transaction_id) :
+ mAgentID(agent_id),
+ mTransactionID(transaction_id),
+ mTimer()
+{
+}
+
+// protected constructor which is used by base classes that determine
+// transaction, agent, et. after construction.
+LLUserOperation::LLUserOperation() :
+ mTimer()
+{
+}
+
+LLUserOperation::~LLUserOperation()
+{
+}
+
+
+BOOL LLUserOperation::isExpired()
+{
+ const F32 EXPIRE_TIME_SECS = 10.f;
+ return mTimer.getElapsedTimeF32() > EXPIRE_TIME_SECS;
+}
+
+void LLUserOperation::expire()
+{
+ // by default, do do anything.
+}
+
+///----------------------------------------------------------------------------
+/// Class LLUserOperationMgr
+///----------------------------------------------------------------------------
+
+LLUserOperationMgr::LLUserOperationMgr()
+{
+}
+
+
+LLUserOperationMgr::~LLUserOperationMgr()
+{
+ if (mUserOperationList.size() > 0)
+ {
+ llwarns << "Exiting with user operations pending." << llendl;
+ }
+}
+
+
+void LLUserOperationMgr::addOperation(LLUserOperation* op)
+{
+ if(!op)
+ {
+ llwarns << "Tried to add null op" << llendl;
+ return;
+ }
+ LLUUID id = op->getTransactionID();
+ llassert(mUserOperationList.count(id) == 0);
+ mUserOperationList[id] = op;
+}
+
+
+LLUserOperation* LLUserOperationMgr::findOperation(const LLUUID& tid)
+{
+ user_operation_list_t::iterator iter = mUserOperationList.find(tid);
+ if (iter != mUserOperationList.end())
+ return iter->second;
+ else
+ return NULL;
+}
+
+
+BOOL LLUserOperationMgr::deleteOperation(LLUserOperation* op)
+{
+ size_t rv = 0;
+ if(op)
+ {
+ LLUUID id = op->getTransactionID();
+ rv = mUserOperationList.erase(id);
+ delete op;
+ op = NULL;
+ }
+ return rv ? TRUE : FALSE;
+}
+
+void LLUserOperationMgr::deleteExpiredOperations()
+{
+ const S32 MAX_OPS_CONSIDERED = 2000;
+ S32 ops_left = MAX_OPS_CONSIDERED;
+ LLUserOperation* op = NULL;
+ user_operation_list_t::iterator it;
+ if(mLastOperationConsidered.isNull())
+ {
+ it = mUserOperationList.begin();
+ }
+ else
+ {
+ it = mUserOperationList.lower_bound(mLastOperationConsidered);
+ }
+ while((ops_left--) && (it != mUserOperationList.end()))
+ {
+ op = (*it).second;
+ if(op && op->isExpired())
+ {
+ lldebugs << "expiring: " << (*it).first << llendl;
+ op->expire();
+ mUserOperationList.erase(it++);
+ delete op;
+ }
+ else if(op)
+ {
+ ++it;
+ }
+ else
+ {
+ mUserOperationList.erase(it++);
+ }
+ }
+ if(it != mUserOperationList.end())
+ {
+ mLastOperationConsidered = (*it).first;
+ }
+ else
+ {
+ mLastOperationConsidered.setNull();
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/llmessage/lluseroperation.h b/indra/llmessage/lluseroperation.h
new file mode 100644
index 0000000000..ac6590abf9
--- /dev/null
+++ b/indra/llmessage/lluseroperation.h
@@ -0,0 +1,75 @@
+/**
+ * @file lluseroperation.h
+ * @brief LLUserOperation class header file - used for message based
+ * transaction. For example, money transactions.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUSEROPERATION_H
+#define LL_LLUSEROPERATION_H
+
+#include "lluuid.h"
+#include "llframetimer.h"
+
+#include <map>
+
+class LLUserOperation
+{
+public:
+ LLUserOperation(const LLUUID& agent_id);
+ LLUserOperation(const LLUUID& agent_id, const LLUUID& transaction_id);
+ virtual ~LLUserOperation();
+
+ const LLUUID& getTransactionID() const { return mTransactionID; }
+ const LLUUID& getAgentID() const { return mAgentID; }
+
+ // Operation never got necessary data, so expired
+ virtual BOOL isExpired();
+
+ // Send request to the dataserver
+ virtual void sendRequest() = 0;
+
+ // Run the operation. This will only be called in the case of an
+ // actual success or failure of the operation.
+ virtual BOOL execute(BOOL transaction_success) = 0;
+
+ // This method is called when the user op has expired, and is
+ // about to be deleted by the manager. This gives the user op the
+ // ability to nack someone when the user op is never evaluated
+ virtual void expire();
+
+protected:
+ LLUserOperation();
+
+protected:
+ LLUUID mAgentID;
+ LLUUID mTransactionID;
+ LLFrameTimer mTimer;
+};
+
+
+class LLUserOperationMgr
+{
+public:
+ LLUserOperationMgr();
+ ~LLUserOperationMgr();
+
+ void addOperation(LLUserOperation* op);
+ LLUserOperation* findOperation(const LLUUID& transaction_id);
+ BOOL deleteOperation(LLUserOperation* op);
+
+ // Call this method every once in a while to clean up old
+ // transactions.
+ void deleteExpiredOperations();
+
+private:
+ typedef std::map<LLUUID, LLUserOperation*> user_operation_list_t;
+ user_operation_list_t mUserOperationList;
+ LLUUID mLastOperationConsidered;
+};
+
+extern LLUserOperationMgr* gUserOperationMgr;
+
+#endif // LL_LLUSEROPERATION_H
diff --git a/indra/llmessage/llvehicleparams.h b/indra/llmessage/llvehicleparams.h
new file mode 100644
index 0000000000..bd49d3f6ae
--- /dev/null
+++ b/indra/llmessage/llvehicleparams.h
@@ -0,0 +1,105 @@
+/**
+ * @file llvehicleparams.h
+ * @brief For parameter names that must be shared between the
+ * scripting language and the LLVehicleAction class on the simulator.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VEHICLE_PARAMS_H
+#define LL_VEHICLE_PARAMS_H
+
+/**
+ * The idea is that the various parameters that control vehicle
+ * behavior can be tweeked by name using general-purpose script calls.
+ */
+
+typedef enum e_vehicle_param
+{
+ VEHICLE_TYPE_NONE, // TYPE_0
+ VEHICLE_TYPE_SLED,
+ VEHICLE_TYPE_CAR,
+ VEHICLE_TYPE_BOAT,
+ VEHICLE_TYPE_AIRPLANE,
+ VEHICLE_TYPE_BALLOON, // TYPE_5
+ VEHICLE_TYPE_6,
+ VEHICLE_TYPE_7,
+ VEHICLE_TYPE_8,
+ VEHICLE_TYPE_9,
+ VEHICLE_TYPE_10,
+ VEHICLE_TYPE_11,
+ VEHICLE_TYPE_12,
+ VEHICLE_TYPE_13,
+ VEHICLE_TYPE_14,
+ VEHICLE_TYPE_15,
+
+ // vector parameters
+ VEHICLE_LINEAR_FRICTION_TIMESCALE,
+ VEHICLE_ANGULAR_FRICTION_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_DIRECTION,
+ VEHICLE_ANGULAR_MOTOR_DIRECTION,
+ VEHICLE_LINEAR_MOTOR_OFFSET,
+ VEHICLE_VECTOR_PARAM_5,
+ VEHICLE_VECTOR_PARAM_6,
+ VEHICLE_VECTOR_PARAM_7,
+
+ // floating point parameters
+ VEHICLE_HOVER_HEIGHT,
+ VEHICLE_HOVER_EFFICIENCY,
+ VEHICLE_HOVER_TIMESCALE,
+ VEHICLE_BUOYANCY,
+
+ VEHICLE_LINEAR_DEFLECTION_EFFICIENCY,
+ VEHICLE_LINEAR_DEFLECTION_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_TIMESCALE,
+ VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE,
+
+ VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY,
+ VEHICLE_ANGULAR_DEFLECTION_TIMESCALE,
+ VEHICLE_ANGULAR_MOTOR_TIMESCALE,
+ VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE,
+
+ VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY,
+ VEHICLE_VERTICAL_ATTRACTION_TIMESCALE,
+
+ VEHICLE_BANKING_EFFICIENCY,
+ VEHICLE_BANKING_MIX,
+ VEHICLE_BANKING_TIMESCALE,
+
+ VEHICLE_FLOAT_PARAM_17,
+ VEHICLE_FLOAT_PARAM_18,
+ VEHICLE_FLOAT_PARAM_19,
+
+ // rotation parameters
+ VEHICLE_REFERENCE_FRAME,
+ VEHICLE_ROTATION_PARAM_1,
+ VEHICLE_ROTATION_PARAM_2,
+ VEHICLE_ROTATION_PARAM_3,
+
+} EVehicleParam;
+
+
+// some flags that effect how the vehicle moves
+
+// zeros world-z component of linear deflection
+const U32 VEHICLE_FLAG_NO_DEFLECTION_UP = 1 << 0;
+
+// spring-loads roll only
+const U32 VEHICLE_FLAG_LIMIT_ROLL_ONLY = 1 << 1;
+
+// hover flags
+const U32 VEHICLE_FLAG_HOVER_WATER_ONLY = 1 << 2;
+const U32 VEHICLE_FLAG_HOVER_TERRAIN_ONLY = 1 << 3;
+const U32 VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT = 1 << 4;
+const U32 VEHICLE_FLAG_HOVER_UP_ONLY = 1 << 5;
+
+// caps world-z component of linear motor to prevent
+// climbing up into the sky
+const U32 VEHICLE_FLAG_LIMIT_MOTOR_UP = 1 << 6;
+
+const U32 VEHICLE_FLAG_MOUSELOOK_STEER = 1 << 7;
+const U32 VEHICLE_FLAG_MOUSELOOK_BANK = 1 << 8;
+const U32 VEHICLE_FLAG_CAMERA_DECOUPLED = 1 << 9;
+
+#endif
diff --git a/indra/llmessage/llxfer.cpp b/indra/llmessage/llxfer.cpp
new file mode 100644
index 0000000000..154696eb4e
--- /dev/null
+++ b/indra/llmessage/llxfer.cpp
@@ -0,0 +1,350 @@
+/**
+ * @file llxfer.cpp
+ * @brief implementation of LLXfer class for a single xfer.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "u64.h"
+
+//number of bytes sent in each message
+const U32 LL_XFER_CHUNK_SIZE = 1000;
+
+const U32 LLXfer::XFER_FILE = 1;
+const U32 LLXfer::XFER_VFILE = 2;
+const U32 LLXfer::XFER_MEM = 3;
+
+///////////////////////////////////////////////////////////
+
+LLXfer::LLXfer (S32 chunk_size)
+{
+ init(chunk_size);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer::~LLXfer ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::init (S32 chunk_size)
+{
+ mID = 0;
+
+ mPacketNum = -1; // there's a preincrement before sending the zeroth packet
+ mXferSize = 0;
+
+ mStatus = e_LL_XFER_UNINITIALIZED;
+ mNext = NULL;
+ mWaitingForACK = FALSE;
+
+ mCallback = NULL;
+ mCallbackDataHandle = NULL;
+
+ mBufferContainsEOF = FALSE;
+ mBuffer = NULL;
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ mRetries = 0;
+
+ if (chunk_size < 1)
+ {
+ chunk_size = LL_XFER_CHUNK_SIZE;
+ }
+ mChunkSize = chunk_size;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::free ()
+{
+ if (mBuffer)
+ {
+ delete[] mBuffer;
+ mBuffer = NULL;
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ llwarns << "undifferentiated LLXfer::startSend for " << getName() << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::setXferSize (S32 xfer_size)
+{
+ mXferSize = xfer_size;
+// cout << "starting transfer of size: " << xfer_size << endl;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::startDownload()
+{
+ llwarns << "undifferentiated LLXfer::startDownload for " << getName()
+ << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::receiveData (char *datap, S32 data_size)
+{
+ S32 retval = 0;
+
+ if (((S32) mBufferLength + data_size) > getMaxBufferSize())
+ {
+ retval = flush();
+ }
+
+ if (!retval)
+ {
+ if (datap != NULL)
+ {
+ memcpy(&mBuffer[mBufferLength],datap,data_size);
+ mBufferLength += data_size;
+ }
+ else
+ {
+ llerrs << "NULL data passed in receiveData" << llendl;
+ }
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::flush()
+{
+ // only files have somewhere to flush to
+ // if we get called with a flush it means we've blown past our
+ // allocated buffer size
+
+ return (-1);
+}
+
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::suck(S32 start_position)
+{
+ llwarns << "Attempted to send a packet outside the buffer bounds in LLXfer::suck()" << llendl;
+ return (-1);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::sendPacket(S32 packet_num)
+{
+ char fdata_buf[LL_XFER_LARGE_PAYLOAD+4]; /* Flawfinder: ignore */
+ S32 fdata_size = mChunkSize;
+ BOOL last_packet = FALSE;
+ S32 num_copy = 0;
+
+ // if the desired packet is not in our current buffered excerpt from the file. . .
+ if (((U32)packet_num*fdata_size < mBufferStartOffset)
+ || ((U32)llmin((U32)mXferSize,(U32)((U32)(packet_num+1)*fdata_size)) > mBufferStartOffset + mBufferLength))
+
+ {
+ if (suck(packet_num*fdata_size)) // returns non-zero on failure
+ {
+ abort(LL_ERR_EOF);
+ return;
+ }
+ }
+
+ S32 desired_read_position = 0;
+
+ desired_read_position = packet_num * fdata_size - mBufferStartOffset;
+
+ fdata_size = llmin((S32)mBufferLength-desired_read_position, mChunkSize);
+
+ if (fdata_size < 0)
+ {
+ llwarns << "negative data size in xfer send, aborting" << llendl;
+ abort(LL_ERR_EOF);
+ return;
+ }
+
+ if (((U32)(desired_read_position + fdata_size) >= (U32)mBufferLength) && (mBufferContainsEOF))
+ {
+ last_packet = TRUE;
+ }
+
+ if (packet_num)
+ {
+ num_copy = llmin(fdata_size, (S32)sizeof(fdata_buf));
+ num_copy = llmin(num_copy, (S32)(sizeof(mBuffer) - desired_read_position));
+ if (num_copy > 0)
+ {
+ memcpy(fdata_buf,&mBuffer[desired_read_position],num_copy);
+ }
+ }
+ else // if we're the first packet, encode size as an additional S32 at start of data
+ {
+ num_copy = llmin(fdata_size, (S32)(sizeof(fdata_buf)-sizeof(S32)));
+ num_copy = llmin(num_copy, (S32)(sizeof(mBuffer) - desired_read_position));
+ if (num_copy > 0)
+ {
+ memcpy(fdata_buf+sizeof(S32),&mBuffer[desired_read_position],fdata_size);
+ }
+ fdata_size += sizeof(S32);
+ htonmemcpy(fdata_buf,&mXferSize, MVT_S32, sizeof(S32));
+ }
+
+ S32 encoded_packetnum = encodePacketNum(packet_num,last_packet);
+
+ if (fdata_size)
+ {
+ // send the packet
+ gMessageSystem->newMessageFast(_PREHASH_SendXferPacket);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, encoded_packetnum);
+
+ gMessageSystem->nextBlockFast(_PREHASH_DataPacket);
+ gMessageSystem->addBinaryDataFast(_PREHASH_Data, &fdata_buf,fdata_size);
+
+ gMessageSystem->sendMessage(mRemoteHost);
+
+ ACKTimer.reset();
+ mWaitingForACK = TRUE;
+ }
+ if (last_packet)
+ {
+ mStatus = e_LL_XFER_COMPLETE;
+ }
+ else
+ {
+ mStatus = e_LL_XFER_IN_PROGRESS;
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::sendNextPacket()
+{
+ mRetries = 0;
+ sendPacket(++mPacketNum);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::resendLastPacket()
+{
+ mRetries++;
+ sendPacket(mPacketNum);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::processEOF()
+{
+ S32 retval = 0;
+
+ mStatus = e_LL_XFER_COMPLETE;
+
+ if (LL_ERR_NOERR == mCallbackResult)
+ {
+ llinfos << "xfer from " << mRemoteHost << " complete: " << getName()
+ << llendl;
+ }
+ else
+ {
+ llinfos << "xfer from " << mRemoteHost << " failed, code "
+ << mCallbackResult << ": " << getName() << llendl;
+ }
+
+ if (mCallback)
+ {
+ mCallback(mCallbackDataHandle,mCallbackResult);
+ }
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::encodePacketNum(S32 packet_num, BOOL is_EOF)
+{
+ if (is_EOF)
+ {
+ packet_num |= 0x80000000;
+ }
+ return packet_num;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer::abort (S32 result_code)
+{
+ mCallbackResult = result_code;
+
+ llinfos << "Aborting xfer from " << mRemoteHost << " named " << getName()
+ << " - error: " << result_code << llendl;
+
+ gMessageSystem->newMessageFast(_PREHASH_AbortXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addS32Fast(_PREHASH_Result, result_code);
+
+ gMessageSystem->sendMessage(mRemoteHost);
+
+ mStatus = e_LL_XFER_ABORTED;
+}
+
+
+///////////////////////////////////////////////////////////
+
+const char * LLXfer::getName()
+{
+ static char tmp_str[256]; /* Flawfinder: ignore */
+
+ return (U64_to_str(mID, tmp_str, sizeof(tmp_str)));
+}
+
+///////////////////////////////////////////////////////////
+
+U32 LLXfer::getXferTypeTag()
+{
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer::getMaxBufferSize ()
+{
+ return(mXferSize);
+}
+
+
+std::ostream& operator<< (std::ostream& os, LLXfer &hh)
+{
+ os << hh.getName() ;
+ return os;
+}
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfer.h b/indra/llmessage/llxfer.h
new file mode 100644
index 0000000000..3fd12df7a5
--- /dev/null
+++ b/indra/llmessage/llxfer.h
@@ -0,0 +1,98 @@
+/**
+ * @file llxfer.h
+ * @brief definition of LLXfer class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_H
+#define LL_LLXFER_H
+
+#include "message.h"
+#include "lltimer.h"
+
+const S32 LL_XFER_LARGE_PAYLOAD = 7680;
+
+typedef enum ELLXferStatus {
+ e_LL_XFER_UNINITIALIZED,
+ e_LL_XFER_REGISTERED, // a buffer which has been registered as available for a request
+ e_LL_XFER_PENDING, // a transfer which has been requested but is waiting for a free slot
+ e_LL_XFER_IN_PROGRESS,
+ e_LL_XFER_COMPLETE,
+ e_LL_XFER_ABORTED,
+ e_LL_XFER_NONE
+} ELLXferStatus;
+
+class LLXfer
+{
+ private:
+ protected:
+ S32 mChunkSize;
+
+ public:
+ LLXfer *mNext;
+ U64 mID;
+ S32 mPacketNum;
+
+ LLHost mRemoteHost;
+ S32 mXferSize;
+
+ char *mBuffer;
+ U32 mBufferLength;
+ U32 mBufferStartOffset;
+ BOOL mBufferContainsEOF;
+
+ ELLXferStatus mStatus;
+
+ BOOL mWaitingForACK;
+
+ void (*mCallback)(void **,S32);
+ void **mCallbackDataHandle;
+ S32 mCallbackResult;
+
+ LLTimer ACKTimer;
+ S32 mRetries;
+
+ static const U32 XFER_FILE;
+ static const U32 XFER_VFILE;
+ static const U32 XFER_MEM;
+
+ private:
+ protected:
+ public:
+ LLXfer (S32 chunk_size);
+ virtual ~LLXfer();
+
+ void init(S32 chunk_size);
+ virtual void free();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+ virtual void sendPacket(S32 packet_num);
+ virtual void sendNextPacket();
+ virtual void resendLastPacket();
+ virtual S32 processEOF();
+ virtual S32 startDownload();
+ virtual S32 receiveData (char *datap, S32 data_size);
+ virtual void abort(S32);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual S32 encodePacketNum(S32 packet_num, BOOL is_eof);
+ virtual void setXferSize (S32 data_size);
+ virtual S32 getMaxBufferSize();
+
+ virtual const char *getName();
+
+ virtual U32 getXferTypeTag();
+
+ friend std::ostream& operator<< (std::ostream& os, LLXfer &hh);
+
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_file.cpp b/indra/llmessage/llxfer_file.cpp
new file mode 100644
index 0000000000..da72467c76
--- /dev/null
+++ b/indra/llmessage/llxfer_file.cpp
@@ -0,0 +1,416 @@
+/**
+ * @file llxfer_file.cpp
+ * @brief implementation of LLXfer_File class for a single xfer (file)
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if !LL_WINDOWS
+#include <errno.h>
+#include <unistd.h>
+#endif
+
+#include "llxfer_file.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llstring.h"
+#include "lldir.h"
+
+// size of chunks read from/written to disk
+const U32 LL_MAX_XFER_FILE_BUFFER = 65536;
+
+// local function to copy a file
+S32 copy_file(const char* from, const char* to);
+
+///////////////////////////////////////////////////////////
+
+LLXfer_File::LLXfer_File (S32 chunk_size)
+: LLXfer(chunk_size)
+{
+ init(LLString::null, FALSE, chunk_size);
+}
+
+LLXfer_File::LLXfer_File (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
+: LLXfer(chunk_size)
+{
+ init(local_filename, delete_local_on_completion, chunk_size);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_File::~LLXfer_File ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_File::init (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size)
+{
+
+ mFp = NULL;
+ mLocalFilename[0] = 0;
+ mRemoteFilename[0] = 0;
+ mRemotePath = LL_PATH_NONE;
+ mTempFilename[0] = 0;
+ mDeleteLocalOnCompletion = FALSE;
+ mDeleteRemoteOnCompletion = FALSE;
+
+ if (!local_filename.empty())
+ {
+ strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+
+ // You can only automatically delete .tmp file as a safeguard against nasty messages.
+ mDeleteLocalOnCompletion = (delete_local_on_completion && (strstr(mLocalFilename,".tmp") == &mLocalFilename[strlen(mLocalFilename)-4])); /* Flawfinder : ignore */
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_File::free ()
+{
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+ }
+
+ LLFile::remove(mTempFilename);
+
+ if (mDeleteLocalOnCompletion)
+ {
+ lldebugs << "Removing file: " << mLocalFilename << llendl;
+ LLFile::remove(mLocalFilename);
+ }
+ else
+ {
+ lldebugs << "Keeping local file: " << mLocalFilename << llendl;
+ }
+
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::initializeRequest(U64 xfer_id,
+ const LLString& local_filename,
+ const LLString& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mID = xfer_id;
+ strncpy(mLocalFilename, local_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ strncpy(mRemoteFilename,remote_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ mRemotePath = remote_path;
+ mRemoteHost = remote_host;
+ mDeleteRemoteOnCompletion = delete_remote_on_completion;
+
+ snprintf(mTempFilename, sizeof(mTempFilename), "%s",gDirUtilp->getTempFilename().c_str()); /* Flawfinder : ignore */
+
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ llinfos << "Requesting xfer from " << remote_host << " for file: " << mLocalFilename << llendl;
+
+ if (mBuffer)
+ {
+ delete(mBuffer);
+ mBuffer = NULL;
+ }
+
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+ mBufferLength = 0;
+
+ mPacketNum = 0;
+
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::startDownload()
+{
+ S32 retval = 0; // presume success
+ mFp = LLFile::fopen(mTempFilename,"w+b"); /* Flawfinder : ignore */
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename);
+ gMessageSystem->addU8("FilePath", (U8) mRemotePath);
+ gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, -1);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+ }
+ else
+ {
+ llwarns << "Couldn't create file to be received!" << llendl;
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << mLocalFilename << endl;
+
+ delete [] mBuffer;
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ mFp = LLFile::fopen(mLocalFilename,"rb"); /* Flawfinder : ignore */
+ if (mFp)
+ {
+ fseek(mFp,0,SEEK_END);
+
+ S32 file_size = ftell(mFp);
+ if (file_size <= 0)
+ {
+ return LL_ERR_FILE_EMPTY;
+ }
+ setXferSize(file_size);
+
+ fseek(mFp,0,SEEK_SET);
+ }
+ else
+ {
+ llinfos << "Warning: " << mLocalFilename << " not found." << llendl;
+ return (LL_ERR_FILE_NOT_FOUND);
+ }
+
+ mStatus = e_LL_XFER_PENDING;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::getMaxBufferSize ()
+{
+ return(LL_MAX_XFER_FILE_BUFFER);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::suck(S32 start_position)
+{
+ S32 retval = 0;
+
+ if (mFp)
+ {
+ // grab a buffer from the right place in the file
+ fseek (mFp,start_position,SEEK_SET);
+
+ mBufferLength = (U32)fread(mBuffer,1,LL_MAX_XFER_FILE_BUFFER,mFp);
+ mBufferStartOffset = start_position;
+
+ if (feof(mFp))
+ {
+ mBufferContainsEOF = TRUE;
+ }
+ else
+ {
+ mBufferContainsEOF = FALSE;
+ }
+ }
+ else
+ {
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::flush()
+{
+ S32 retval = 0;
+ if (mBufferLength)
+ {
+ if (mFp)
+ {
+ llerrs << "Overwriting open file pointer!" << llendl;
+ }
+ mFp = LLFile::fopen(mTempFilename,"a+b"); /* Flawfinder : ignore */
+
+ if (mFp)
+ {
+ fwrite(mBuffer,1,mBufferLength,mFp);
+// llinfos << "******* wrote " << mBufferLength << " bytes of file xfer" << llendl;
+ fclose(mFp);
+ mFp = NULL;
+
+ mBufferLength = 0;
+ }
+ else
+ {
+ llwarns << "LLXfer_File::flush() unable to open " << mTempFilename << " for writing!" << llendl;
+ retval = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_File::processEOF()
+{
+ S32 retval = 0;
+ mStatus = e_LL_XFER_COMPLETE;
+
+ S32 flushval = flush();
+
+ // If we have no other errors, our error becomes the error generated by
+ // flush.
+ if (!mCallbackResult)
+ {
+ mCallbackResult = flushval;
+ }
+
+ LLFile::remove(mLocalFilename);
+
+ if (!mCallbackResult)
+ {
+ if (LLFile::rename(mTempFilename,mLocalFilename))
+ {
+#if !LL_WINDOWS
+ S32 error_number = errno;
+ llinfos << "Rename failure (" << error_number << ") - "
+ << mTempFilename << " to " << mLocalFilename << llendl;
+ if(EXDEV == error_number)
+ {
+ if(copy_file(mTempFilename, mLocalFilename) == 0)
+ {
+ llinfos << "Rename across mounts; copying+unlinking the file instead." << llendl;
+ unlink(mTempFilename);
+ }
+ else
+ {
+ llwarns << "Copy failure - " << mTempFilename << " to "
+ << mLocalFilename << llendl;
+ }
+ }
+ else
+ {
+ //FILE* fp = LLFile::fopen(mTempFilename, "r");
+ //llwarns << "File " << mTempFilename << " does "
+ // << (!fp ? "not" : "" ) << " exit." << llendl;
+ //if(fp) fclose(fp);
+ //fp = LLFile::fopen(mLocalFilename, "r");
+ //llwarns << "File " << mLocalFilename << " does "
+ // << (!fp ? "not" : "" ) << " exit." << llendl;
+ //if(fp) fclose(fp);
+ llwarns << "Rename fatally failed, can only handle EXDEV ("
+ << EXDEV << ")" << llendl;
+ }
+#else
+ llwarns << "Rename failure - " << mTempFilename << " to "
+ << mLocalFilename << llendl;
+#endif
+ }
+ }
+
+ if (mFp)
+ {
+ fclose(mFp);
+ mFp = NULL;
+ }
+
+ retval = LLXfer::processEOF();
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXfer_File::matchesLocalFilename(const LLString& filename)
+{
+ return (filename == mLocalFilename);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXfer_File::matchesRemoteFilename(const LLString& filename, ELLPath remote_path)
+{
+ return ((filename == mRemoteFilename) && (remote_path == mRemotePath));
+}
+
+
+///////////////////////////////////////////////////////////
+
+const char * LLXfer_File::getName()
+{
+ return (mLocalFilename);
+}
+
+///////////////////////////////////////////////////////////
+
+// hacky - doesn't matter what this is
+// as long as it's different from the other classes
+U32 LLXfer_File::getXferTypeTag()
+{
+ return LLXfer::XFER_FILE;
+}
+
+///////////////////////////////////////////////////////////
+
+#if !LL_WINDOWS
+
+// This is really close to, but not quite a general purpose copy
+// function. It does not really spam enough information, but is useful
+// for this cpp file, because this should never be called in a
+// production environment.
+S32 copy_file(const char* from, const char* to)
+{
+ S32 rv = 0;
+ FILE* in = LLFile::fopen(from, "rb");
+ FILE* out = LLFile::fopen(to, "wb");
+ if(in && out)
+ {
+ S32 read = 0;
+ const S32 COPY_BUFFER_SIZE = 16384;
+ U8 buffer[COPY_BUFFER_SIZE];
+ while(((read = fread(buffer, 1, sizeof(buffer), in)) > 0)
+ && (fwrite(buffer, 1, read, out) == (U32)read)); /* Flawfinder : ignore */
+ if(ferror(in) || ferror(out)) rv = -2;
+ }
+ else
+ {
+ rv = -1;
+ }
+ if(in) fclose(in);
+ if(out) fclose(out);
+ return rv;
+}
+#endif
diff --git a/indra/llmessage/llxfer_file.h b/indra/llmessage/llxfer_file.h
new file mode 100644
index 0000000000..b3d1ccbfbe
--- /dev/null
+++ b/indra/llmessage/llxfer_file.h
@@ -0,0 +1,68 @@
+/**
+ * @file llxfer_file.h
+ * @brief definition of LLXfer_File class for a single xfer_file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_FILE_H
+#define LL_LLXFER_FILE_H
+
+#include <stdio.h>
+
+#include "llxfer.h"
+#include "lldir.h"
+
+class LLXfer_File : public LLXfer
+{
+ protected:
+ FILE *mFp;
+ char mLocalFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ char mRemoteFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ ELLPath mRemotePath;
+ char mTempFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+
+ BOOL mDeleteLocalOnCompletion;
+ BOOL mDeleteRemoteOnCompletion;
+
+ public:
+ LLXfer_File (S32 chunk_size);
+ LLXfer_File (const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size);
+ virtual ~LLXfer_File();
+
+ virtual void init(const LLString& local_filename, BOOL delete_local_on_completion, S32 chunk_size);
+ virtual void free();
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ const LLString& local_filename,
+ const LLString& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual BOOL matchesLocalFilename(const LLString& filename);
+ virtual BOOL matchesRemoteFilename(const LLString& filename, ELLPath remote_path);
+
+ virtual S32 getMaxBufferSize();
+
+ virtual U32 getXferTypeTag();
+
+ virtual const char *getName();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_mem.cpp b/indra/llmessage/llxfer_mem.cpp
new file mode 100644
index 0000000000..8f48247e20
--- /dev/null
+++ b/indra/llmessage/llxfer_mem.cpp
@@ -0,0 +1,199 @@
+/**
+ * @file llxfer_mem.cpp
+ * @brief implementation of LLXfer_Mem class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer_mem.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+
+///////////////////////////////////////////////////////////
+
+LLXfer_Mem::LLXfer_Mem ()
+: LLXfer(-1)
+{
+ init();
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_Mem::~LLXfer_Mem ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::init ()
+{
+ mRemoteFilename[0] = '\0';
+ mRemotePath = LL_PATH_NONE;
+ mDeleteRemoteOnCompletion = FALSE;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::free ()
+{
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_Mem::setXferSize (S32 xfer_size)
+{
+ mXferSize = xfer_size;
+
+ delete[] mBuffer;
+ mBuffer = new char[xfer_size];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+ mBufferContainsEOF = TRUE;
+
+// cout << "starting transfer of size: " << xfer_size << endl;
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXfer_Mem::registerXfer(U64 xfer_id, const void *datap, const S32 length)
+{
+ mID = xfer_id;
+
+ if (datap)
+ {
+ setXferSize(length);
+ if (mBuffer)
+ {
+ memcpy(mBuffer,datap,length); /* Flawfinder : ignore */
+ mBufferLength = length;
+ }
+ else
+ {
+ xfer_id = 0;
+ }
+ }
+
+ mStatus = e_LL_XFER_REGISTERED;
+ return (xfer_id);
+}
+
+S32 LLXfer_Mem::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ if (mXferSize <= 0)
+ {
+ return LL_ERR_FILE_EMPTY;
+ }
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << getName() << endl;
+
+ mStatus = e_LL_XFER_PENDING;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::processEOF()
+{
+ S32 retval = 0;
+
+ mStatus = e_LL_XFER_COMPLETE;
+
+ llinfos << "xfer complete: " << getName() << llendl;
+
+ if (mCallback)
+ {
+ mCallback((void *)mBuffer,mBufferLength,mCallbackDataHandle,mCallbackResult);
+ }
+
+ return(retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::initializeRequest(U64 xfer_id,
+ const std::string& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mRemoteHost = remote_host;
+
+ // create a temp filename string using a GUID
+ mID = xfer_id;
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ strncpy(mRemoteFilename, remote_filename.c_str(), LL_MAX_PATH); /* Flawfinder : ignore */
+ mRemotePath = remote_path;
+ mDeleteRemoteOnCompletion = delete_remote_on_completion;
+
+ llinfos << "Requesting file: " << remote_filename << llendl;
+
+ delete [] mBuffer;
+ mBuffer = NULL;
+
+ mBufferLength = 0;
+ mPacketNum = 0;
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+//////////////////////////////////////////////////////////
+
+S32 LLXfer_Mem::startDownload()
+{
+ S32 retval = 0; // presume success
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, mRemoteFilename);
+ gMessageSystem->addU8("FilePath", (U8) mRemotePath);
+ gMessageSystem->addBOOL("DeleteOnCompletion", mDeleteRemoteOnCompletion);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, LLUUID::null);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, -1);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+
+ return (retval);
+}
+
+//////////////////////////////////////////////////////////
+
+U32 LLXfer_Mem::getXferTypeTag()
+{
+ return LLXfer::XFER_MEM;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfer_mem.h b/indra/llmessage/llxfer_mem.h
new file mode 100644
index 0000000000..d7cbdc4f85
--- /dev/null
+++ b/indra/llmessage/llxfer_mem.h
@@ -0,0 +1,61 @@
+/**
+ * @file llxfer_mem.h
+ * @brief definition of LLXfer_Mem class for a single xfer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_MEM_H
+#define LL_LLXFER_MEM_H
+
+#include <stdio.h>
+
+#include "message.h"
+#include "lltimer.h"
+#include "llxfer.h"
+#include "lldir.h"
+
+class LLXfer_Mem : public LLXfer
+{
+ private:
+ protected:
+ void (*mCallback)(void *, S32, void **,S32);
+ char mRemoteFilename[LL_MAX_PATH]; /* Flawfinder : ignore */
+ ELLPath mRemotePath;
+ BOOL mDeleteRemoteOnCompletion;
+
+ public:
+
+ private:
+ protected:
+ public:
+ LLXfer_Mem ();
+ virtual ~LLXfer_Mem();
+
+ virtual void init();
+ virtual void free();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+ virtual U64 registerXfer(U64 xfer_id, const void *datap, const S32 length);
+ virtual void setXferSize (S32 data_size);
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ const std::string& remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual U32 getXferTypeTag();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfer_vfile.cpp b/indra/llmessage/llxfer_vfile.cpp
new file mode 100644
index 0000000000..5030556eb0
--- /dev/null
+++ b/indra/llmessage/llxfer_vfile.cpp
@@ -0,0 +1,320 @@
+/**
+ * @file llxfer_vfile.cpp
+ * @brief implementation of LLXfer_VFile class for a single xfer (vfile).
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfer_vfile.h"
+#include "lluuid.h"
+#include "llerror.h"
+#include "llmath.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "lldir.h"
+
+// size of chunks read from/written to disk
+const U32 LL_MAX_XFER_FILE_BUFFER = 65536;
+
+///////////////////////////////////////////////////////////
+
+LLXfer_VFile::LLXfer_VFile ()
+: LLXfer(-1)
+{
+ init(NULL, LLUUID::null, LLAssetType::AT_NONE);
+}
+
+LLXfer_VFile::LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type)
+: LLXfer(-1)
+{
+ init(vfs, local_id, type);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer_VFile::~LLXfer_VFile ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_VFile::init (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type)
+{
+
+ mVFS = vfs;
+ mLocalID = local_id;
+ mType = type;
+
+ mVFile = NULL;
+
+ char id_string[UUID_STR_LENGTH]; /* Flawfinder : ignore */
+ mLocalID.toString(id_string);
+
+ snprintf(mName, sizeof(mName), "VFile %s:%s", id_string, LLAssetType::lookup(mType)); /* Flawfinder : ignore */
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXfer_VFile::free ()
+{
+ LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE);
+ file.remove();
+
+ delete mVFile;
+ mVFile = NULL;
+
+ LLXfer::free();
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::initializeRequest(U64 xfer_id,
+ LLVFS* vfs,
+ const LLUUID& local_id,
+ const LLUUID& remote_id,
+ LLAssetType::EType type,
+ const LLHost& remote_host,
+ void (*callback)(void**,S32),
+ void** user_data)
+{
+ S32 retval = 0; // presume success
+
+ mRemoteHost = remote_host;
+
+ mVFS = vfs;
+ mLocalID = local_id;
+ mRemoteID = remote_id;
+ mType = type;
+
+ mID = xfer_id;
+ mCallback = callback;
+ mCallbackDataHandle = user_data;
+ mCallbackResult = LL_ERR_NOERR;
+
+ char id_string[UUID_STR_LENGTH]; /* Flawfinder : ignore */
+ mLocalID.toString(id_string);
+
+ snprintf(mName, sizeof(mName), "VFile %s:%s", id_string, LLAssetType::lookup(mType)); /* Flawfinder : ignore */
+
+ llinfos << "Requesting " << mName << llendl;
+
+ if (mBuffer)
+ {
+ delete[] mBuffer;
+ mBuffer = NULL;
+ }
+
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mPacketNum = 0;
+ mTempID.generate();
+ mStatus = e_LL_XFER_PENDING;
+ return retval;
+}
+
+//////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::startDownload()
+{
+ S32 retval = 0; // presume success
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+
+ gMessageSystem->newMessageFast(_PREHASH_RequestXfer);
+ gMessageSystem->nextBlockFast(_PREHASH_XferID);
+ gMessageSystem->addU64Fast(_PREHASH_ID, mID);
+ gMessageSystem->addStringFast(_PREHASH_Filename, "");
+ gMessageSystem->addU8("FilePath", (U8) LL_PATH_NONE);
+ gMessageSystem->addBOOL("DeleteOnCompletion", FALSE);
+ gMessageSystem->addBOOL("UseBigPackets", BOOL(mChunkSize == LL_XFER_LARGE_PAYLOAD));
+ gMessageSystem->addUUIDFast(_PREHASH_VFileID, mRemoteID);
+ gMessageSystem->addS16Fast(_PREHASH_VFileType, (S16)mType);
+
+ gMessageSystem->sendReliable(mRemoteHost);
+ mStatus = e_LL_XFER_IN_PROGRESS;
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::startSend (U64 xfer_id, const LLHost &remote_host)
+{
+ S32 retval = LL_ERR_NOERR; // presume success
+
+ mRemoteHost = remote_host;
+ mID = xfer_id;
+ mPacketNum = -1;
+
+// cout << "Sending file: " << mLocalFilename << endl;
+
+ delete [] mBuffer;
+ mBuffer = new char[LL_MAX_XFER_FILE_BUFFER];
+
+ mBufferLength = 0;
+ mBufferStartOffset = 0;
+
+ delete mVFile;
+ mVFile = NULL;
+ if(mVFS->getExists(mLocalID, mType))
+ {
+ mVFile = new LLVFile(mVFS, mLocalID, mType, LLVFile::READ);
+
+ if (mVFile->getSize() <= 0)
+ {
+ delete mVFile;
+ mVFile = NULL;
+
+ return LL_ERR_FILE_EMPTY;
+ }
+ }
+
+ if(mVFile)
+ {
+ setXferSize(mVFile->getSize());
+ mStatus = e_LL_XFER_PENDING;
+ }
+ else
+ {
+ retval = LL_ERR_FILE_NOT_FOUND;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+void LLXfer_VFile::setXferSize (S32 xfer_size)
+{
+ LLXfer::setXferSize(xfer_size);
+
+ // Don't do this on the server side, where we have a persistent mVFile
+ // It would be nice if LLXFers could tell which end of the pipe they were
+ if (! mVFile)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+ file.setMaxSize(xfer_size);
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::getMaxBufferSize ()
+{
+ return(LL_MAX_XFER_FILE_BUFFER);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::suck(S32 start_position)
+{
+ S32 retval = 0;
+
+ if (mVFile)
+ {
+ // grab a buffer from the right place in the file
+ if (! mVFile->seek(start_position, 0))
+ {
+ llwarns << "VFile Xfer Can't seek to position " << start_position << ", file length " << mVFile->getSize() << llendl;
+ llwarns << "While sending file " << mLocalID << llendl;
+ return -1;
+ }
+
+ if (mVFile->read((U8*)mBuffer, LL_MAX_XFER_FILE_BUFFER)) /* Flawfinder : ignore */
+ {
+ mBufferLength = mVFile->getLastBytesRead();
+ mBufferStartOffset = start_position;
+
+ mBufferContainsEOF = mVFile->eof();
+ }
+ else
+ {
+ retval = -1;
+ }
+ }
+ else
+ {
+ retval = -1;
+ }
+
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::flush()
+{
+ S32 retval = 0;
+ if (mBufferLength)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::APPEND);
+
+ file.write((U8*)mBuffer, mBufferLength);
+
+ mBufferLength = 0;
+ }
+ return (retval);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXfer_VFile::processEOF()
+{
+ S32 retval = 0;
+ mStatus = e_LL_XFER_COMPLETE;
+
+ flush();
+
+ if (!mCallbackResult)
+ {
+ LLVFile file(mVFS, mTempID, mType, LLVFile::WRITE);
+ if (! file.rename(mLocalID, mType))
+ {
+ llinfos << "copy from temp file failed: unable to rename to " << mLocalID << llendl;
+ }
+
+ }
+
+ if (mVFile)
+ {
+ delete mVFile;
+ mVFile = NULL;
+ }
+
+ retval = LLXfer::processEOF();
+
+ return(retval);
+}
+
+////////////////////////////////////////////////////////////
+
+BOOL LLXfer_VFile::matchesLocalFile(const LLUUID &id, LLAssetType::EType type)
+{
+ return (id == mLocalID && type == mType);
+}
+
+//////////////////////////////////////////////////////////
+
+BOOL LLXfer_VFile::matchesRemoteFile(const LLUUID &id, LLAssetType::EType type)
+{
+ return (id == mRemoteID && type == mType);
+}
+
+//////////////////////////////////////////////////////////
+
+const char * LLXfer_VFile::getName()
+{
+ return mName;
+}
+
+//////////////////////////////////////////////////////////
+
+// hacky - doesn't matter what this is
+// as long as it's different from the other classes
+U32 LLXfer_VFile::getXferTypeTag()
+{
+ return LLXfer::XFER_VFILE;
+}
diff --git a/indra/llmessage/llxfer_vfile.h b/indra/llmessage/llxfer_vfile.h
new file mode 100644
index 0000000000..3d9d8de7a7
--- /dev/null
+++ b/indra/llmessage/llxfer_vfile.h
@@ -0,0 +1,74 @@
+/**
+ * @file llxfer_vfile.h
+ * @brief definition of LLXfer_VFile class for a single xfer_vfile.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFER_VFILE_H
+#define LL_LLXFER_VFILE_H
+
+#include <stdio.h>
+
+#include "llxfer.h"
+#include "llassetstorage.h"
+
+class LLVFS;
+class LLVFile;
+
+class LLXfer_VFile : public LLXfer
+{
+ protected:
+ LLUUID mLocalID;
+ LLUUID mRemoteID;
+ LLUUID mTempID;
+ LLAssetType::EType mType;
+
+ LLVFile *mVFile;
+
+ LLVFS *mVFS;
+
+ char mName[MAX_STRING]; /* Flawfinder : ignore */
+
+ public:
+ LLXfer_VFile ();
+ LLXfer_VFile (LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type);
+ virtual ~LLXfer_VFile();
+
+ virtual void init(LLVFS *vfs, const LLUUID &local_id, LLAssetType::EType type);
+ virtual void free();
+
+ virtual S32 initializeRequest(U64 xfer_id,
+ LLVFS *vfs,
+ const LLUUID &local_id,
+ const LLUUID &remote_id,
+ const LLAssetType::EType type,
+ const LLHost &remote_host,
+ void (*callback)(void **,S32),
+ void **user_data);
+ virtual S32 startDownload();
+
+ virtual S32 processEOF();
+
+ virtual S32 startSend (U64 xfer_id, const LLHost &remote_host);
+
+ virtual S32 suck(S32 start_position);
+ virtual S32 flush();
+
+ virtual BOOL matchesLocalFile(const LLUUID &id, LLAssetType::EType type);
+ virtual BOOL matchesRemoteFile(const LLUUID &id, LLAssetType::EType type);
+
+ virtual void setXferSize(S32 xfer_size);
+ virtual S32 getMaxBufferSize();
+
+ virtual U32 getXferTypeTag();
+
+ virtual const char *getName();
+};
+
+#endif
+
+
+
+
diff --git a/indra/llmessage/llxfermanager.cpp b/indra/llmessage/llxfermanager.cpp
new file mode 100644
index 0000000000..e2d8cd30b3
--- /dev/null
+++ b/indra/llmessage/llxfermanager.cpp
@@ -0,0 +1,1133 @@
+/**
+ * @file llxfermanager.cpp
+ * @brief implementation of LLXferManager class for a collection of xfers
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxfermanager.h"
+
+#include "llxfer.h"
+#include "llxfer_file.h"
+#include "llxfer_mem.h"
+#include "llxfer_vfile.h"
+
+#include "llerror.h"
+#include "lluuid.h"
+#include "u64.h"
+
+const F32 LL_XFER_REGISTRATION_TIMEOUT = 60.0f; // timeout if a registered transfer hasn't been requested in 60 seconds
+const F32 LL_PACKET_TIMEOUT = 3.0f; // packet timeout at 3 s
+const S32 LL_PACKET_RETRY_LIMIT = 10; // packet retransmission limit
+
+const S32 LL_DEFAULT_MAX_SIMULTANEOUS_XFERS = 10;
+const S32 LL_DEFAULT_MAX_REQUEST_FIFO_XFERS = 1000;
+
+#define LL_XFER_PROGRESS_MESSAGES 0
+#define LL_XFER_TEST_REXMIT 0
+
+
+///////////////////////////////////////////////////////////
+
+LLXferManager::LLXferManager (LLVFS *vfs)
+{
+ init(vfs);
+}
+
+///////////////////////////////////////////////////////////
+
+LLXferManager::~LLXferManager ()
+{
+ free();
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::init (LLVFS *vfs)
+{
+ mSendList = NULL;
+ mReceiveList = NULL;
+
+ setMaxOutgoingXfersPerCircuit(LL_DEFAULT_MAX_SIMULTANEOUS_XFERS);
+ setMaxIncomingXfers(LL_DEFAULT_MAX_REQUEST_FIFO_XFERS);
+
+ mVFS = vfs;
+
+ // Turn on or off ack throttling
+ mUseAckThrottling = FALSE;
+ setAckThrottleBPS(100000);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::free ()
+{
+ LLXfer *xferp;
+ LLXfer *delp;
+
+ mOutgoingHosts.deleteAllData();
+
+ delp = mSendList;
+ while (delp)
+ {
+ xferp = delp->mNext;
+ delete delp;
+ delp = xferp;
+ }
+ mSendList = NULL;
+
+ delp = mReceiveList;
+ while (delp)
+ {
+ xferp = delp->mNext;
+ delete delp;
+ delp = xferp;
+ }
+ mReceiveList = NULL;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::setMaxIncomingXfers(S32 max_num)
+{
+ mMaxIncomingXfers = max_num;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::setMaxOutgoingXfersPerCircuit(S32 max_num)
+{
+ mMaxOutgoingXfersPerCircuit = max_num;
+}
+
+void LLXferManager::setUseAckThrottling(const BOOL use)
+{
+ mUseAckThrottling = use;
+}
+
+void LLXferManager::setAckThrottleBPS(const F32 bps)
+{
+ // Let's figure out the min we can set based on the ack retry rate
+ // and number of simultaneous.
+
+ // Assuming we're running as slow as possible, this is the lowest ack
+ // rate we can use.
+ F32 min_bps = (1000.f * 8.f* mMaxIncomingXfers) / LL_PACKET_TIMEOUT;
+
+ // Set
+ F32 actual_rate = llmax(min_bps*1.1f, bps);
+ llinfos << "LLXferManager ack throttle min rate: " << min_bps << llendl;
+ llinfos << "LLXferManager ack throttle actual rate: " << actual_rate << llendl;
+ mAckThrottle.setRate(actual_rate);
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::updateHostStatus()
+{
+ LLXfer *xferp;
+ LLHostStatus *host_statusp = NULL;
+
+ mOutgoingHosts.deleteAllData();
+
+ for (xferp = mSendList; xferp; xferp = xferp->mNext)
+ {
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == xferp->mRemoteHost)
+ {
+ break;
+ }
+ }
+ if (!host_statusp)
+ {
+ host_statusp = new LLHostStatus();
+ if (host_statusp)
+ {
+ host_statusp->mHost = xferp->mRemoteHost;
+ mOutgoingHosts.addData(host_statusp);
+ }
+ }
+ if (host_statusp)
+ {
+ if (xferp->mStatus == e_LL_XFER_PENDING)
+ {
+ host_statusp->mNumPending++;
+ }
+ else if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ host_statusp->mNumActive++;
+ }
+ }
+
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::printHostStatus()
+{
+ LLHostStatus *host_statusp = NULL;
+ if (mOutgoingHosts.getFirstData())
+ {
+ llinfos << "Outgoing Xfers:" << llendl;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ llinfos << " " << host_statusp->mHost << " active: " << host_statusp->mNumActive << " pending: " << host_statusp->mNumPending << llendl;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+LLXfer *LLXferManager::findXfer (U64 id, LLXfer *list_head)
+{
+ LLXfer *xferp;
+ for (xferp = list_head; xferp; xferp = xferp->mNext)
+ {
+ if (xferp->mID == id)
+ {
+ return(xferp);
+ }
+ }
+ return(NULL);
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::removeXfer (LLXfer *delp, LLXfer **list_head)
+{
+ LLXfer *xferp;
+
+ if (delp)
+ {
+ if (*list_head == delp)
+ {
+ *list_head = delp->mNext;
+ delete (delp);
+ }
+ else
+ {
+ xferp = *list_head;
+ while (xferp->mNext)
+ {
+ if (xferp->mNext == delp)
+ {
+ xferp->mNext = delp->mNext;
+ delete (delp);
+ continue;
+ }
+ xferp = xferp->mNext;
+ }
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+U32 LLXferManager::numActiveListEntries(LLXfer *list_head)
+{
+ U32 num_entries = 0;
+
+ while (list_head)
+ {
+ if ((list_head->mStatus == e_LL_XFER_IN_PROGRESS))
+ {
+ num_entries++;
+ }
+ list_head = list_head->mNext;
+ }
+ return(num_entries);
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::numPendingXfers(const LLHost &host)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ return (host_statusp->mNumPending);
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::numActiveXfers(const LLHost &host)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ return (host_statusp->mNumActive);
+ }
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::changeNumActiveXfers(const LLHost &host, S32 delta)
+{
+ LLHostStatus *host_statusp = NULL;
+
+ for (host_statusp = mOutgoingHosts.getFirstData(); host_statusp; host_statusp = mOutgoingHosts.getNextData())
+ {
+ if (host_statusp->mHost == host)
+ {
+ host_statusp->mNumActive += delta;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::registerCallbacks(LLMessageSystem *msgsystem)
+{
+ msgsystem->setHandlerFuncFast(_PREHASH_ConfirmXferPacket, process_confirm_packet, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_RequestXfer, process_request_xfer, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_SendXferPacket, continue_file_receive, NULL);
+ msgsystem->setHandlerFuncFast(_PREHASH_AbortXfer, process_abort_xfer, NULL);
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXferManager::getNextID ()
+{
+ LLUUID a_guid;
+
+ a_guid.generate();
+
+
+ return(*((U64*)(a_guid.mData)));
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::encodePacketNum(S32 packet_num, BOOL is_EOF)
+{
+ if (is_EOF)
+ {
+ packet_num |= 0x80000000;
+ }
+ return packet_num;
+}
+
+///////////////////////////////////////////////////////////
+
+S32 LLXferManager::decodePacketNum(S32 packet_num)
+{
+ return(packet_num & 0x0FFFFFFF);
+}
+
+///////////////////////////////////////////////////////////
+
+BOOL LLXferManager::isLastPacket(S32 packet_num)
+{
+ return(packet_num & 0x80000000);
+}
+
+///////////////////////////////////////////////////////////
+
+U64 LLXferManager::registerXfer(const void *datap, const S32 length)
+{
+ LLXfer *xferp;
+ U64 xfer_id = getNextID();
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+
+ xfer_id = ((LLXfer_Mem *)xferp)->registerXfer(xfer_id, datap,length);
+
+ if (!xfer_id)
+ {
+ removeXfer(xferp,&mSendList);
+ }
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ xfer_id = 0;
+ }
+
+ return(xfer_id);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::requestFile(const char* local_filename,
+ const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32),
+ void** user_data,
+ BOOL is_priority,
+ BOOL use_big_packets)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_FILE
+ && (((LLXfer_File*)xferp)->matchesLocalFilename(local_filename))
+ && (((LLXfer_File*)xferp)->matchesRemoteFilename(remote_filename, remote_path))
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ S32 chunk_size = use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1;
+ xferp = (LLXfer *) new LLXfer_File(chunk_size);
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+
+ // Remove any file by the same name that happens to be lying
+ // around.
+ // Note: according to AaronB, this is here to deal with locks on files that were
+ // in transit during a crash,
+ if(delete_remote_on_completion &&
+ (strstr(remote_filename,".tmp") == &remote_filename[strlen(remote_filename)-4])) /* Flawfinder : ignore */
+ {
+ LLFile::remove(local_filename);
+ }
+ ((LLXfer_File *)xferp)->initializeRequest(
+ getNextID(),
+ local_filename,
+ remote_filename,
+ remote_path,
+ remote_host,
+ delete_remote_on_completion,
+ callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+}
+
+void LLXferManager::requestFile(const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*,S32,void**,S32),
+ void** user_data,
+ BOOL is_priority)
+{
+ LLXfer *xferp;
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+ ((LLXfer_Mem *)xferp)->initializeRequest(getNextID(),
+ remote_filename,
+ remote_path,
+ remote_host,
+ delete_remote_on_completion,
+ callback, user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+}
+
+void LLXferManager::requestVFile(const LLUUID& local_id,
+ const LLUUID& remote_id,
+ LLAssetType::EType type, LLVFS* vfs,
+ const LLHost& remote_host,
+ void (*callback)(void**, S32),
+ void** user_data,
+ BOOL is_priority)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_VFILE
+ && (((LLXfer_VFile*)xferp)->matchesLocalFile(local_id, type))
+ && (((LLXfer_VFile*)xferp)->matchesRemoteFile(remote_id, type))
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ xferp = (LLXfer *) new LLXfer_VFile();
+ if (xferp)
+ {
+ addToList(xferp, mReceiveList, is_priority);
+ ((LLXfer_VFile *)xferp)->initializeRequest(getNextID(),
+ vfs,
+ local_id,
+ remote_id,
+ type,
+ remote_host,
+ callback,
+ user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allocation error" << llendl;
+ }
+
+}
+
+/*
+void LLXferManager::requestXfer(
+ const char *local_filename,
+ BOOL delete_remote_on_completion,
+ U64 xfer_id,
+ const LLHost &remote_host,
+ void (*callback)(void **,S32),
+ void **user_data)
+{
+ LLXfer *xferp;
+
+ for (xferp = mReceiveList; xferp ; xferp = xferp->mNext)
+ {
+ if (xferp->getXferTypeTag() == LLXfer::XFER_FILE
+ && (((LLXfer_File*)xferp)->matchesLocalFilename(local_filename))
+ && (xfer_id == xferp->mID)
+ && (remote_host == xferp->mRemoteHost)
+ && (callback == xferp->mCallback)
+ && (user_data == xferp->mCallbackDataHandle))
+
+ {
+ // cout << "requested a xfer already in progress" << endl;
+ return;
+ }
+ }
+
+ xferp = (LLXfer *) new LLXfer_File();
+ if (xferp)
+ {
+ xferp->mNext = mReceiveList;
+ mReceiveList = xferp;
+
+ ((LLXfer_File *)xferp)->initializeRequest(xfer_id,local_filename,"",LL_PATH_NONE,remote_host,delete_remote_on_completion,callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+}
+
+void LLXferManager::requestXfer(U64 xfer_id, const LLHost &remote_host, BOOL delete_remote_on_completion, void (*callback)(void *,S32,void **,S32),void **user_data)
+{
+ LLXfer *xferp;
+
+ xferp = (LLXfer *) new LLXfer_Mem();
+ if (xferp)
+ {
+ xferp->mNext = mReceiveList;
+ mReceiveList = xferp;
+
+ ((LLXfer_Mem *)xferp)->initializeRequest(xfer_id,"",LL_PATH_NONE,remote_host,delete_remote_on_completion,callback,user_data);
+ startPendingDownloads();
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+}
+*/
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processReceiveData (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ // there's sometimes an extra 4 bytes added to an xfer payload
+ const S32 BUF_SIZE = LL_XFER_LARGE_PAYLOAD + 4;
+ char fdata_buf[LL_XFER_LARGE_PAYLOAD + 4]; /* Flawfinder : ignore */
+ S32 fdata_size;
+ U64 id;
+ S32 packetnum;
+ LLXfer * xferp;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Packet, packetnum);
+
+ fdata_size = mesgsys->getSizeFast(_PREHASH_DataPacket,_PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_DataPacket, _PREHASH_Data, fdata_buf, 0, 0, BUF_SIZE);
+
+ xferp = findXfer(id, mReceiveList);
+
+ if (!xferp)
+ {
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llwarns << "received xfer data from " << mesgsys->getSender()
+ << " for non-existent xfer id: "
+ << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << llendl;
+ return;
+ }
+
+ S32 xfer_size;
+
+ if (decodePacketNum(packetnum) != xferp->mPacketNum) // is the packet different from what we were expecting?
+ {
+ // confirm it if it was a resend of the last one, since the confirmation might have gotten dropped
+ if (decodePacketNum(packetnum) == (xferp->mPacketNum - 1))
+ {
+ llinfos << "Reconfirming xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet " << packetnum << llendl; sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender());
+ }
+ else
+ {
+ llinfos << "Ignoring xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " recv'd packet " << packetnum << "; expecting " << xferp->mPacketNum << llendl;
+ }
+ return;
+ }
+
+ S32 result = 0;
+
+ if (xferp->mPacketNum == 0) // first packet has size encoded as additional S32 at beginning of data
+ {
+ ntohmemcpy(&xfer_size,fdata_buf,MVT_S32,sizeof(S32));
+
+// do any necessary things on first packet ie. allocate memory
+ xferp->setXferSize(xfer_size);
+
+ // adjust buffer start and size
+ result = xferp->receiveData(&(fdata_buf[sizeof(S32)]),fdata_size-(sizeof(S32)));
+ }
+ else
+ {
+ result = xferp->receiveData(fdata_buf,fdata_size);
+ }
+
+ if (result == LL_ERR_CANNOT_OPEN_FILE)
+ {
+ xferp->abort(LL_ERR_CANNOT_OPEN_FILE);
+ removeXfer(xferp,&mReceiveList);
+ startPendingDownloads();
+ return;
+ }
+
+ xferp->mPacketNum++; // expect next packet
+
+ if (!mUseAckThrottling)
+ {
+ // No throttling, confirm right away
+ sendConfirmPacket(mesgsys, id, decodePacketNum(packetnum), mesgsys->getSender());
+ }
+ else
+ {
+ // Throttling, put on queue to be confirmed later.
+ LLXferAckInfo ack_info;
+ ack_info.mID = id;
+ ack_info.mPacketNum = decodePacketNum(packetnum);
+ ack_info.mRemoteHost = mesgsys->getSender();
+ mXferAckQueue.push(ack_info);
+ }
+
+ if (isLastPacket(packetnum))
+ {
+ xferp->processEOF();
+ removeXfer(xferp,&mReceiveList);
+ startPendingDownloads();
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host)
+{
+#if LL_XFER_PROGRESS_MESSAGES
+ if (!(packetnum % 50))
+ {
+ cout << "confirming xfer packet #" << packetnum << endl;
+ }
+#endif
+ mesgsys->newMessageFast(_PREHASH_ConfirmXferPacket);
+ mesgsys->nextBlockFast(_PREHASH_XferID);
+ mesgsys->addU64Fast(_PREHASH_ID, id);
+ mesgsys->addU32Fast(_PREHASH_Packet, packetnum);
+
+ mesgsys->sendMessage(remote_host);
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processFileRequest (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+
+ U64 id;
+ char local_filename[MAX_STRING]; /* Flawfinder : ignore */
+ ELLPath local_path = LL_PATH_NONE;
+ S32 result = LL_ERR_NOERR;
+ LLUUID uuid;
+ LLAssetType::EType type;
+ S16 type_s16;
+ BOOL b_use_big_packets;
+
+ mesgsys->getBOOL("XferID", "UseBigPackets", b_use_big_packets);
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llinfos << "xfer request id: " << U64_to_str(id, U64_BUF, sizeof(U64_BUF))
+ << " to " << mesgsys->getSender() << llendl;
+
+ mesgsys->getStringFast(_PREHASH_XferID, _PREHASH_Filename, MAX_STRING, local_filename);
+
+ U8 local_path_u8;
+ mesgsys->getU8("XferID", "FilePath", local_path_u8);
+ if( local_path_u8 < (U8)LL_PATH_COUNT )
+ {
+ local_path = (ELLPath)local_path_u8;
+ }
+ else
+ {
+ llwarns << "Invalid file path in LLXferManager::processFileRequest() " << (U32)local_path_u8 << llendl;
+ }
+
+ mesgsys->getUUIDFast(_PREHASH_XferID, _PREHASH_VFileID, uuid);
+ mesgsys->getS16Fast(_PREHASH_XferID, _PREHASH_VFileType, type_s16);
+ type = (LLAssetType::EType)type_s16;
+
+ LLXfer *xferp;
+
+ if (uuid != LLUUID::null)
+ {
+ if(NULL == LLAssetType::lookup(type))
+ {
+ llwarns << "Invalid type for xfer request: " << uuid << ":"
+ << type_s16 << " to " << mesgsys->getSender() << llendl;
+ return;
+ }
+
+ llinfos << "starting vfile transfer: " << uuid << "," << LLAssetType::lookup(type) << " to " << mesgsys->getSender() << llendl;
+
+ if (! mVFS)
+ {
+ llwarns << "Attempt to send VFile w/o available VFS" << llendl;
+ return;
+ }
+
+ xferp = (LLXfer *)new LLXfer_VFile(mVFS, uuid, type);
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+ }
+ else if (strlen(local_filename)) /* Flawfinder : ignore */
+ {
+ std::string expanded_filename = gDirUtilp->getExpandedFilename( local_path, local_filename );
+ llinfos << "starting file transfer: " << expanded_filename << " to " << mesgsys->getSender() << llendl;
+
+ BOOL delete_local_on_completion = FALSE;
+ mesgsys->getBOOL("XferID", "DeleteOnCompletion", delete_local_on_completion);
+
+ // -1 chunk_size causes it to use the default
+ xferp = (LLXfer *)new LLXfer_File(expanded_filename, delete_local_on_completion, b_use_big_packets ? LL_XFER_LARGE_PAYLOAD : -1);
+
+ if (xferp)
+ {
+ xferp->mNext = mSendList;
+ mSendList = xferp;
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llerrs << "Xfer allcoation error" << llendl;
+ }
+ }
+ else
+ {
+ char U64_BUF[MAX_STRING]; /* Flawfinder : ignore */
+ llinfos << "starting memory transfer: "
+ << U64_to_str(id, U64_BUF, sizeof(U64_BUF)) << " to "
+ << mesgsys->getSender() << llendl;
+
+ xferp = findXfer(id, mSendList);
+
+ if (xferp)
+ {
+ result = xferp->startSend(id,mesgsys->getSender());
+ }
+ else
+ {
+ llinfos << "Warning: " << U64_BUF << " not found." << llendl;
+ result = LL_ERR_FILE_NOT_FOUND;
+ }
+ }
+
+ if (result)
+ {
+ if (xferp)
+ {
+ xferp->abort(result);
+ removeXfer(xferp,&mSendList);
+ }
+ else // can happen with a memory transfer not found
+ {
+ llinfos << "Aborting xfer to " << mesgsys->getSender() << " with error: " << result << llendl;
+
+ mesgsys->newMessageFast(_PREHASH_AbortXfer);
+ mesgsys->nextBlockFast(_PREHASH_XferID);
+ mesgsys->addU64Fast(_PREHASH_ID, id);
+ mesgsys->addS32Fast(_PREHASH_Result, result);
+
+ mesgsys->sendMessage(mesgsys->getSender());
+ }
+ }
+ else if(xferp && (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit))
+ {
+ xferp->sendNextPacket();
+ changeNumActiveXfers(xferp->mRemoteHost,1);
+// llinfos << "***STARTING XFER IMMEDIATELY***" << llendl;
+ }
+ else
+ {
+ if(xferp)
+ {
+ llinfos << " queueing xfer request, " << numPendingXfers(xferp->mRemoteHost) << " ahead of this one" << llendl;
+ }
+ else
+ {
+ llwarns << "LLXferManager::processFileRequest() - no xfer found!"
+ << llendl;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processConfirmation (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ U64 id = 0;
+ S32 packetNum = 0;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Packet, packetNum);
+
+ LLXfer* xferp = findXfer(id, mSendList);
+ if (xferp)
+ {
+// cout << "confirmed packet #" << packetNum << " ping: "<< xferp->ACKTimer.getElapsedTimeF32() << endl;
+ xferp->mWaitingForACK = FALSE;
+ if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ xferp->sendNextPacket();
+ }
+ else
+ {
+ removeXfer(xferp, &mSendList);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::retransmitUnackedPackets ()
+{
+ LLXfer *xferp;
+ LLXfer *delp;
+ xferp = mReceiveList;
+ while(xferp)
+ {
+ if (xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ // if the circuit dies, abort
+ if (! gMessageSystem->mCircuitInfo.isCircuitAlive( xferp->mRemoteHost ))
+ {
+ llinfos << "Xfer found in progress on dead circuit, aborting" << llendl;
+ xferp->mCallbackResult = LL_ERR_CIRCUIT_GONE;
+ xferp->processEOF();
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mReceiveList);
+ continue;
+ }
+
+ }
+ xferp = xferp->mNext;
+ }
+
+ xferp = mSendList;
+ updateHostStatus();
+ F32 et;
+ while (xferp)
+ {
+ if (xferp->mWaitingForACK && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_PACKET_TIMEOUT))
+ {
+ if (xferp->mRetries > LL_PACKET_RETRY_LIMIT)
+ {
+ llinfos << "dropping xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet retransmit limit exceeded, xfer dropped" << llendl;
+ xferp->abort(LL_ERR_TCP_TIMEOUT);
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else
+ {
+ llinfos << "resending xfer " << xferp->mRemoteHost << ":" << xferp->getName() << " packet unconfirmed after: "<< et << " sec, packet " << xferp->mPacketNum << llendl;
+ xferp->resendLastPacket();
+ xferp = xferp->mNext;
+ }
+ }
+ else if ((xferp->mStatus == e_LL_XFER_REGISTERED) && ( (et = xferp->ACKTimer.getElapsedTimeF32()) > LL_XFER_REGISTRATION_TIMEOUT))
+ {
+ llinfos << "registered xfer never requested, xfer dropped" << llendl;
+ xferp->abort(LL_ERR_TCP_TIMEOUT);
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else if (xferp->mStatus == e_LL_XFER_ABORTED)
+ {
+ llwarns << "Removing aborted xfer " << xferp->mRemoteHost << ":" << xferp->getName() << llendl;
+ delp = xferp;
+ xferp = xferp->mNext;
+ removeXfer(delp,&mSendList);
+ }
+ else if (xferp->mStatus == e_LL_XFER_PENDING)
+ {
+// llinfos << "*** numActiveXfers = " << numActiveXfers(xferp->mRemoteHost) << " mMaxOutgoingXfersPerCircuit = " << mMaxOutgoingXfersPerCircuit << llendl;
+ if (numActiveXfers(xferp->mRemoteHost) < mMaxOutgoingXfersPerCircuit)
+ {
+// llinfos << "bumping pending xfer to active" << llendl;
+ xferp->sendNextPacket();
+ changeNumActiveXfers(xferp->mRemoteHost,1);
+ }
+ xferp = xferp->mNext;
+ }
+ else
+ {
+ xferp = xferp->mNext;
+ }
+ }
+
+ //
+ // HACK - if we're using xfer confirm throttling, throttle our xfer confirms here
+ // so we don't blow through bandwidth.
+ //
+
+ while (mXferAckQueue.getLength())
+ {
+ if (mAckThrottle.checkOverflow(1000.0f*8.0f))
+ {
+ break;
+ }
+ //llinfos << "Confirm packet queue length:" << mXferAckQueue.getLength() << llendl;
+ LLXferAckInfo ack_info;
+ mXferAckQueue.pop(ack_info);
+ //llinfos << "Sending confirm packet" << llendl;
+ sendConfirmPacket(gMessageSystem, ack_info.mID, ack_info.mPacketNum, ack_info.mRemoteHost);
+ mAckThrottle.throttleOverflow(1000.f*8.f); // Assume 1000 bytes/packet
+ }
+}
+
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::processAbort (LLMessageSystem *mesgsys, void ** /*user_data*/)
+{
+ U64 id = 0;
+ S32 result_code = 0;
+ LLXfer * xferp;
+
+ mesgsys->getU64Fast(_PREHASH_XferID, _PREHASH_ID, id);
+ mesgsys->getS32Fast(_PREHASH_XferID, _PREHASH_Result, result_code);
+
+ xferp = findXfer(id, mReceiveList);
+ if (xferp)
+ {
+ xferp->mCallbackResult = result_code;
+ xferp->processEOF();
+ removeXfer(xferp, &mReceiveList);
+ startPendingDownloads();
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::startPendingDownloads()
+{
+ // This method goes through the list, and starts pending
+ // operations until active downloads == mMaxIncomingXfers. I copy
+ // the pending xfers into a temporary data structure because the
+ // xfers are stored as an intrusive linked list where older
+ // requests get pushed toward the back. Thus, if we didn't do a
+ // stateful iteration, it would be possible for old requests to
+ // never start.
+ LLXfer* xferp = mReceiveList;
+ LLLinkedList<LLXfer> pending_downloads;
+ S32 download_count = 0;
+ S32 pending_count = 0;
+ while(xferp)
+ {
+ if(xferp->mStatus == e_LL_XFER_PENDING)
+ {
+ ++pending_count; // getLength() is O(N), so track it here.
+ pending_downloads.addData(xferp);
+ }
+ else if(xferp->mStatus == e_LL_XFER_IN_PROGRESS)
+ {
+ ++download_count;
+ }
+ xferp = xferp->mNext;
+ }
+
+ S32 start_count = mMaxIncomingXfers - download_count;
+
+ lldebugs << "LLXferManager::startPendingDownloads() - XFER_IN_PROGRESS: "
+ << download_count << " XFER_PENDING: " << pending_count
+ << " startring " << llmin(start_count, pending_count) << llendl;
+
+ if((start_count > 0) && (pending_count > 0))
+ {
+ S32 result;
+ xferp = pending_downloads.getFirstData();
+ while(start_count-- && xferp)
+ {
+ result = xferp->startDownload();
+ if(result)
+ {
+ xferp->abort(result);
+ ++start_count;
+ }
+ xferp = pending_downloads.getNextData();
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////
+
+void LLXferManager::addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority)
+{
+ if(is_priority)
+ {
+ xferp->mNext = NULL;
+ LLXfer* next = head;
+ if(next)
+ {
+ while(next->mNext)
+ {
+ next = next->mNext;
+ }
+ next->mNext = xferp;
+ }
+ else
+ {
+ head = xferp;
+ }
+ }
+ else
+ {
+ xferp->mNext = head;
+ head = xferp;
+ }
+}
+
+///////////////////////////////////////////////////////////
+// Globals and C routines
+///////////////////////////////////////////////////////////
+
+LLXferManager *gXferManager = NULL;
+
+
+void start_xfer_manager(LLVFS *vfs)
+{
+ gXferManager = new LLXferManager(vfs);
+}
+
+void cleanup_xfer_manager()
+{
+ if (gXferManager)
+ {
+ delete(gXferManager);
+ gXferManager = NULL;
+ }
+}
+
+void process_confirm_packet (LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processConfirmation(mesgsys,user_data);
+}
+
+void process_request_xfer(LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processFileRequest(mesgsys,user_data);
+}
+
+void continue_file_receive(LLMessageSystem *mesgsys, void **user_data)
+{
+#if LL_TEST_XFER_REXMIT
+ if (frand(1.f) > 0.05f)
+ {
+#endif
+ gXferManager->processReceiveData(mesgsys,user_data);
+#if LL_TEST_XFER_REXMIT
+ }
+ else
+ {
+ cout << "oops! dropped a xfer packet" << endl;
+ }
+#endif
+}
+
+void process_abort_xfer(LLMessageSystem *mesgsys, void **user_data)
+{
+ gXferManager->processAbort(mesgsys,user_data);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/llmessage/llxfermanager.h b/indra/llmessage/llxfermanager.h
new file mode 100644
index 0000000000..eca3684df5
--- /dev/null
+++ b/indra/llmessage/llxfermanager.h
@@ -0,0 +1,187 @@
+/**
+ * @file llxfermanager.h
+ * @brief definition of LLXferManager class for a keeping track of
+ * multiple xfers
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXFERMANAGER_H
+#define LL_LLXFERMANAGER_H
+
+/**
+ * this manager keeps both a send list and a receive list; anything with a
+ * LLXferManager can send and receive files via messages
+ */
+
+//Forward declaration to avoid circular dependencies
+class LLXfer;
+class LLVFS;
+
+#include "llxfer.h"
+#include "message.h"
+#include "llassetstorage.h"
+#include "linked_lists.h"
+#include "lldir.h"
+#include "lllinkedqueue.h"
+#include "llthrottle.h"
+
+class LLHostStatus
+{
+ public:
+ LLHost mHost;
+ S32 mNumActive;
+ S32 mNumPending;
+
+ LLHostStatus() {mNumActive = 0; mNumPending = 0;};
+ virtual ~LLHostStatus(){};
+};
+
+// Class stores ack information, to be put on list so we can throttle xfer rate.
+class LLXferAckInfo
+{
+public:
+ LLXferAckInfo(U32 dummy = 0)
+ {
+ mID = 0;
+ mPacketNum = -1;
+ }
+
+ U64 mID;
+ S32 mPacketNum;
+ LLHost mRemoteHost;
+};
+
+class LLXferManager
+{
+ private:
+ LLVFS *mVFS;
+
+ protected:
+ S32 mMaxOutgoingXfersPerCircuit;
+ S32 mMaxIncomingXfers;
+
+ BOOL mUseAckThrottling; // Use ack throttling to cap file xfer bandwidth
+ LLLinkedQueue<LLXferAckInfo> mXferAckQueue;
+ LLThrottle mAckThrottle;
+ public:
+
+ // This enumeration is useful in the requestFile() to specify if
+ // an xfer must happen asap.
+ enum
+ {
+ LOW_PRIORITY = FALSE,
+ HIGH_PRIORITY = TRUE,
+ };
+
+ LLXfer *mSendList;
+ LLXfer *mReceiveList;
+
+ LLLinkedList <LLHostStatus> mOutgoingHosts;
+
+ private:
+ protected:
+ // implementation methods
+ virtual void startPendingDownloads();
+ virtual void addToList(LLXfer* xferp, LLXfer*& head, BOOL is_priority);
+
+ public:
+ LLXferManager(LLVFS *vfs);
+ virtual ~LLXferManager();
+
+ virtual void init(LLVFS *vfs);
+ virtual void free();
+
+ void setUseAckThrottling(const BOOL use);
+ void setAckThrottleBPS(const F32 bps);
+
+// list management routines
+ virtual LLXfer *findXfer(U64 id, LLXfer *list_head);
+ virtual void removeXfer (LLXfer *delp, LLXfer **list_head);
+ virtual U32 numActiveListEntries(LLXfer *list_head);
+ virtual S32 numActiveXfers(const LLHost &host);
+ virtual S32 numPendingXfers(const LLHost &host);
+ virtual void changeNumActiveXfers(const LLHost &host, S32 delta);
+
+ virtual void setMaxOutgoingXfersPerCircuit (S32 max_num);
+ virtual void setMaxIncomingXfers(S32 max_num);
+ virtual void updateHostStatus();
+ virtual void printHostStatus();
+
+// general utility routines
+ virtual void registerCallbacks(LLMessageSystem *mesgsys);
+ virtual U64 getNextID ();
+ virtual S32 encodePacketNum(S32 packet_num, BOOL is_eof);
+ virtual S32 decodePacketNum(S32 packet_num);
+ virtual BOOL isLastPacket(S32 packet_num);
+
+ virtual U64 registerXfer(const void *datap, const S32 length);
+
+// file requesting routines
+// .. to file
+ virtual void requestFile(const char* local_filename,
+ const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost& remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void**,S32), void** user_data,
+ BOOL is_priority = FALSE,
+ BOOL use_big_packets = FALSE);
+
+// .. to memory
+ virtual void requestFile(const char* remote_filename,
+ ELLPath remote_path,
+ const LLHost &remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void*, S32, void**, S32),
+ void** user_data,
+ BOOL is_priority = FALSE);
+
+// vfile requesting
+// .. to vfile
+ virtual void requestVFile(const LLUUID &local_id, const LLUUID& remote_id,
+ LLAssetType::EType type, LLVFS* vfs,
+ const LLHost& remote_host,
+ void (*callback)(void**, S32), void** user_data,
+ BOOL is_priority = FALSE);
+
+/*
+// xfer request (may be memory or file)
+// .. to file
+ virtual void requestXfer(const char *local_filename, U64 xfer_id,
+ BOOL delete_remote_on_completion,
+ const LLHost &remote_host, void (*callback)(void **,S32),void **user_data);
+// .. to memory
+ virtual void requestXfer(U64 xfer_id,
+ const LLHost &remote_host,
+ BOOL delete_remote_on_completion,
+ void (*callback)(void *, S32, void **, S32),void **user_data);
+*/
+
+ virtual void processReceiveData (LLMessageSystem *mesgsys, void **user_data);
+ virtual void sendConfirmPacket (LLMessageSystem *mesgsys, U64 id, S32 packetnum, const LLHost &remote_host);
+
+// file sending routines
+ virtual void processFileRequest (LLMessageSystem *mesgsys, void **user_data);
+ virtual void processConfirmation (LLMessageSystem *mesgsys, void **user_data);
+ virtual void retransmitUnackedPackets ();
+
+// error handling
+ virtual void processAbort (LLMessageSystem *mesgsys, void **user_data);
+};
+
+extern LLXferManager* gXferManager;
+
+// initialization and garbage collection
+void start_xfer_manager(LLVFS *vfs);
+void cleanup_xfer_manager();
+
+// message system callbacks
+void process_confirm_packet (LLMessageSystem *mesgsys, void **user_data);
+void process_request_xfer (LLMessageSystem *mesgsys, void **user_data);
+void continue_file_receive(LLMessageSystem *mesgsys, void **user_data);
+void process_abort_xfer (LLMessageSystem *mesgsys, void **user_data);
+#endif
+
+
diff --git a/indra/llmessage/llxorcipher.cpp b/indra/llmessage/llxorcipher.cpp
new file mode 100644
index 0000000000..1fbbfec9e0
--- /dev/null
+++ b/indra/llmessage/llxorcipher.cpp
@@ -0,0 +1,108 @@
+/**
+ * @file llxorcipher.cpp
+ * @brief Implementation of LLXORCipher
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llcrypto.h"
+#include "llerror.h"
+
+///----------------------------------------------------------------------------
+/// Class LLXORCipher
+///----------------------------------------------------------------------------
+
+LLXORCipher::LLXORCipher(const U8* pad, U32 pad_len) :
+ mPad(NULL),
+ mHead(NULL),
+ mPadLen(0)
+{
+ init(pad, pad_len);
+}
+
+// Destroys the object
+LLXORCipher::~LLXORCipher()
+{
+ init(NULL, 0);
+}
+
+LLXORCipher::LLXORCipher(const LLXORCipher& cipher) :
+ mPad(NULL),
+ mHead(NULL),
+ mPadLen(0)
+{
+ init(cipher.mPad, cipher.mPadLen);
+}
+
+LLXORCipher& LLXORCipher::operator=(const LLXORCipher& cipher)
+{
+ if(this == &cipher) return *this;
+ init(cipher.mPad, cipher.mPadLen);
+ return *this;
+}
+
+BOOL LLXORCipher::encrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ if(!src || !src_len || !dst || !dst_len || !mPad) return FALSE;
+ U8* pad_end = mPad + mPadLen;
+ while(src_len--)
+ {
+ *dst++ = *src++ ^ *mHead++;
+ if(mHead >= pad_end) mHead = mPad;
+ }
+ return TRUE;
+}
+
+BOOL LLXORCipher::decrypt(const U8* src, U32 src_len, U8* dst, U32 dst_len)
+{
+ // xor is a symetric cipher, thus, just call the other function.
+ return encrypt(src, src_len, dst, dst_len);
+}
+
+U32 LLXORCipher::requiredEncryptionSpace(U32 len)
+{
+ return len;
+}
+
+void LLXORCipher::init(const U8* pad, U32 pad_len)
+{
+ if(mPad)
+ {
+ delete [] mPad;
+ mPad = NULL;
+ mPadLen = 0;
+ }
+ if(pad && pad_len)
+ {
+ mPadLen = pad_len;
+ mPad = new U8[mPadLen];
+ if (mPad != NULL)
+ {
+ memcpy(mPad, pad, mPadLen); /* Flawfinder : ignore */
+ }
+ }
+ mHead = mPad;
+}
+
+#ifdef _DEBUG
+// static
+BOOL LLXORCipher::testHarness()
+{
+ const U32 PAD_LEN = 3;
+ const U8 PAD[] = "abc";
+ const S32 MSG_LENGTH = 12;
+ const char MESSAGE[MSG_LENGTH+1] = "gesundheight"; /* Flawfinder : ignore */
+ U8 encrypted[MSG_LENGTH];
+ U8 decrypted[MSG_LENGTH];
+
+ LLXORCipher cipher(PAD, PAD_LEN);
+ cipher.encrypt((U8*)MESSAGE, MSG_LENGTH, encrypted, MSG_LENGTH);
+ cipher.decrypt(encrypted, MSG_LENGTH, decrypted, MSG_LENGTH);
+
+ if(0 != memcmp((void*)MESSAGE, decrypted, MSG_LENGTH)) return FALSE;
+ return TRUE;
+}
+#endif
diff --git a/indra/llmessage/machine.h b/indra/llmessage/machine.h
new file mode 100644
index 0000000000..b5efe717d8
--- /dev/null
+++ b/indra/llmessage/machine.h
@@ -0,0 +1,101 @@
+/**
+ * @file machine.h
+ * @brief LLMachine class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MACHINE_H
+#define LL_MACHINE_H
+
+#include "llerror.h"
+#include "net.h"
+#include "llhost.h"
+
+typedef enum e_machine_type
+{
+ MT_NULL,
+ MT_SIMULATOR,
+ MT_VIEWER,
+ MT_SPACE_SERVER,
+ MT_OBJECT_REPOSITORY,
+ MT_PROXY,
+ MT_EOF
+} EMachineType;
+
+const U32 ADDRESS_STRING_SIZE = 12;
+
+class LLMachine
+{
+public:
+ LLMachine()
+ : mMachineType(MT_NULL), mControlPort(0) {}
+
+ LLMachine(EMachineType machine_type, U32 ip, S32 port)
+ : mMachineType(machine_type), mControlPort(0), mHost(ip,port) {}
+
+ LLMachine(EMachineType machine_type, const LLHost &host)
+ : mMachineType(machine_type) {mHost = host; mControlPort = 0;}
+
+ ~LLMachine() {}
+
+ // get functions
+ EMachineType getMachineType() const { return mMachineType; }
+ const U32 getMachineIP() const { return mHost.getAddress(); }
+ const S32 getMachinePort() const { return mHost.getPort(); }
+ const LLHost &getMachineHost() const { return mHost; }
+ // The control port is the listen port of the parent process that
+ // launched this machine. 0 means none or not known.
+ const S32 &getControlPort() const { return mControlPort; }
+ BOOL isValid() const { return (mHost.getPort() != 0); } // TRUE if corresponds to functioning machine
+
+ // set functions
+ void setMachineType(EMachineType machine_type) { mMachineType = machine_type; }
+ void setMachineIP(U32 ip) { mHost.setAddress(ip); }
+ void setMachineHost(const LLHost &host) { mHost = host; }
+
+ void setMachinePort(S32 port)
+ {
+ if (port < 0)
+ {
+ llinfos << "Can't assign a negative number to LLMachine::mPort" << llendl;
+ mHost.setPort(0);
+ }
+ else
+ {
+ mHost.setPort(port);
+ }
+ }
+
+ void setControlPort( S32 port )
+ {
+ if (port < 0)
+ {
+ llinfos << "Can't assign a negative number to LLMachine::mControlPort" << llendl;
+ mControlPort = 0;
+ }
+ else
+ {
+ mControlPort = port;
+ }
+ }
+
+
+ // member variables
+
+// Someday these should be made private.
+// When they are, some of the code that breaks should
+// become member functions of LLMachine -- Leviathan
+//private:
+
+ // I fixed the others, somebody should fix these! - djs
+ EMachineType mMachineType;
+
+protected:
+
+ S32 mControlPort;
+ LLHost mHost;
+};
+
+#endif
diff --git a/indra/llmessage/mean_collision_data.h b/indra/llmessage/mean_collision_data.h
new file mode 100644
index 0000000000..7d3f90cde6
--- /dev/null
+++ b/indra/llmessage/mean_collision_data.h
@@ -0,0 +1,81 @@
+/**
+ * @file mean_collision_data.h
+ * @brief data type to log interactions between stuff and agents that
+ * might be community standards violations
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MEAN_COLLISIONS_DATA_H
+#define LL_MEAN_COLLISIONS_DATA_H
+
+#include <time.h>
+#include "lldbstrings.h"
+
+const F32 MEAN_COLLISION_TIMEOUT = 5.f;
+const S32 MAX_MEAN_COLLISIONS = 5;
+
+typedef enum e_mean_collision_types
+{
+ MEAN_INVALID,
+ MEAN_BUMP,
+ MEAN_LLPUSHOBJECT,
+ MEAN_SELECTED_OBJECT_COLLIDE,
+ MEAN_SCRIPTED_OBJECT_COLLIDE,
+ MEAN_PHYSICAL_OBJECT_COLLIDE,
+ MEAN_EOF
+} EMeanCollisionType;
+
+class LLMeanCollisionData
+{
+public:
+ LLMeanCollisionData(const LLUUID &victim, const LLUUID &perp, time_t time, EMeanCollisionType type, F32 mag)
+ : mVictim(victim), mPerp(perp), mTime(time), mType(type), mMag(mag)
+ { mFirstName[0] = 0; mLastName[0] = 0; }
+
+ LLMeanCollisionData(LLMeanCollisionData *mcd)
+ : mVictim(mcd->mVictim), mPerp(mcd->mPerp), mTime(mcd->mTime), mType(mcd->mType), mMag(mcd->mMag)
+ {
+ strncpy(mFirstName, mcd->mFirstName, sizeof(mFirstName) -1); /* Flawfinder: Ignore */
+ mFirstName[sizeof(mFirstName) -1] = '\0';
+ strncpy(mLastName, mcd->mLastName, sizeof(mLastName) -1); /* Flawfinder: Ignore */
+ mLastName[sizeof(mLastName) -1] = '\0';
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, const LLMeanCollisionData &a)
+ {
+ switch(a.mType)
+ {
+ case MEAN_BUMP:
+ s << "Mean Collision: " << a.mPerp << " bumped " << a.mVictim << " with a velocity of " << a.mMag << " at " << ctime(&a.mTime);
+ break;
+ case MEAN_LLPUSHOBJECT:
+ s << "Mean Collision: " << a.mPerp << " llPushObject-ed " << a.mVictim << " with a total force of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_SELECTED_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " dragged an object into " << a.mVictim << " with a velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_SCRIPTED_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " smacked " << a.mVictim << " with a scripted object with velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ case MEAN_PHYSICAL_OBJECT_COLLIDE:
+ s << "Mean Collision: " << a.mPerp << " smacked " << a.mVictim << " with a physical object with velocity of " << a.mMag << " at "<< ctime(&a.mTime);
+ break;
+ default:
+ break;
+ }
+ return s;
+ }
+
+ LLUUID mVictim;
+ LLUUID mPerp;
+ time_t mTime;
+ EMeanCollisionType mType;
+ F32 mMag;
+ char mFirstName[DB_FIRST_NAME_BUF_SIZE]; /* Flawfinder: Ignore */
+ char mLastName[DB_LAST_NAME_BUF_SIZE]; /* Flawfinder: Ignore */
+};
+
+
+#endif
diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp
new file mode 100644
index 0000000000..cdafafc8db
--- /dev/null
+++ b/indra/llmessage/message.cpp
@@ -0,0 +1,5876 @@
+/**
+ * @file message.cpp
+ * @brief LLMessageSystem class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "message.h"
+
+// system library includes
+#if !LL_WINDOWS
+// following header files required for inet_addr()
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <cstring>
+#include <time.h>
+#include <iomanip>
+#include <iterator>
+#include <sstream>
+
+#include "llapr.h"
+#include "apr-1/apr_portable.h"
+#include "apr-1/apr_network_io.h"
+#include "apr-1/apr_poll.h"
+
+// linden library headers
+#include "indra_constants.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "llfasttimer.h"
+#include "llmd5.h"
+#include "llsd.h"
+#include "lltransfermanager.h"
+#include "lluuid.h"
+#include "llxfermanager.h"
+#include "timing.h"
+#include "llquaternion.h"
+#include "u64.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "lltransfertargetvfile.h"
+
+// Constants
+//const char* MESSAGE_LOG_FILENAME = "message.log";
+static const F32 CIRCUIT_DUMP_TIMEOUT = 30.f;
+static const S32 TRUST_TIME_WINDOW = 3;
+
+class LLMsgVarData
+{
+public:
+ LLMsgVarData() : mName(NULL), mSize(-1), mDataSize(-1), mData(NULL), mType(MVT_U8)
+ {
+ }
+
+ LLMsgVarData(const char *name, EMsgVariableType type) : mSize(-1), mDataSize(-1), mData(NULL), mType(type)
+ {
+ mName = (char *)name;
+ }
+
+ ~LLMsgVarData()
+ {
+ // copy constructor just copies the mData pointer, so only delete mData explicitly
+ }
+
+ void deleteData()
+ {
+ delete[] mData;
+ mData = NULL;
+ }
+
+ void addData(const void *indata, S32 size, EMsgVariableType type, S32 data_size = -1);
+
+ char *getName() const { return mName; }
+ S32 getSize() const { return mSize; }
+ void *getData() { return (void*)mData; }
+ S32 getDataSize() const { return mDataSize; }
+ EMsgVariableType getType() const { return mType; }
+
+protected:
+ char *mName;
+ S32 mSize;
+ S32 mDataSize;
+
+ U8 *mData;
+ EMsgVariableType mType;
+};
+
+
+class LLMsgBlkData
+{
+public:
+ LLMsgBlkData(const char *name, S32 blocknum) : mOffset(-1), mBlockNumber(blocknum), mTotalSize(-1)
+ {
+ mName = (char *)name;
+ }
+
+ ~LLMsgBlkData()
+ {
+ for (msg_var_data_map_t::iterator iter = mMemberVarData.begin();
+ iter != mMemberVarData.end(); iter++)
+ {
+ iter->deleteData();
+ }
+ }
+
+ void addVariable(const char *name, EMsgVariableType type)
+ {
+ LLMsgVarData tmp(name,type);
+ mMemberVarData[name] = tmp;
+ }
+
+ void addData(char *name, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1)
+ {
+ LLMsgVarData* temp = &mMemberVarData[name]; // creates a new entry if one doesn't exist
+ temp->addData(data, size, type, data_size);
+ }
+
+ S32 mOffset;
+ S32 mBlockNumber;
+ typedef LLDynamicArrayIndexed<LLMsgVarData, const char *, 8> msg_var_data_map_t;
+ msg_var_data_map_t mMemberVarData;
+ char *mName;
+ S32 mTotalSize;
+};
+
+
+class LLMsgData
+{
+public:
+ LLMsgData(const char *name) : mTotalSize(-1)
+ {
+ mName = (char *)name;
+ }
+ ~LLMsgData()
+ {
+ for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer());
+ }
+
+ void addBlock(LLMsgBlkData *blockp)
+ {
+ mMemberBlocks[blockp->mName] = blockp;
+ }
+
+ void addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size = -1);
+
+public:
+ S32 mOffset;
+ typedef std::map<char*, LLMsgBlkData*> msg_blk_data_map_t;
+ msg_blk_data_map_t mMemberBlocks;
+ char *mName;
+ S32 mTotalSize;
+};
+
+inline void LLMsgVarData::addData(const void *data, S32 size, EMsgVariableType type, S32 data_size)
+{
+ mSize = size;
+ mDataSize = data_size;
+ if ( (type != MVT_VARIABLE) && (type != MVT_FIXED)
+ && (mType != MVT_VARIABLE) && (mType != MVT_FIXED))
+ {
+ if (mType != type)
+ {
+ llwarns << "Type mismatch in addData for " << mName
+ << " message: " << gMessageSystem->getCurrentSMessageName()
+ << " block: " << gMessageSystem->getCurrentSBlockName()
+ << llendl;
+ }
+ }
+ if(size)
+ {
+ delete mData; // Delete it if it already exists
+ mData = new U8[size];
+ htonmemcpy(mData, data, mType, size);
+ }
+}
+
+
+
+inline void LLMsgData::addDataFast(char *blockname, char *varname, const void *data, S32 size, EMsgVariableType type, S32 data_size)
+{
+ // remember that if the blocknumber is > 0 then the number is appended to the name
+ char *namep = (char *)blockname;
+ LLMsgBlkData* block_data = mMemberBlocks[namep];
+ if (block_data->mBlockNumber)
+ {
+ namep += block_data->mBlockNumber;
+ block_data->addData(varname, data, size, type, data_size);
+ }
+ else
+ {
+ block_data->addData(varname, data, size, type, data_size);
+ }
+}
+
+// LLMessage* classes store the template of messages
+
+
+class LLMessageVariable
+{
+public:
+ LLMessageVariable() : mName(NULL), mType(MVT_NULL), mSize(-1)
+ {
+ }
+
+ LLMessageVariable(char *name) : mType(MVT_NULL), mSize(-1)
+ {
+ mName = name;
+ }
+
+ LLMessageVariable(char *name, const EMsgVariableType type, const S32 size) : mType(type), mSize(size)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageVariable() {}
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg);
+
+ EMsgVariableType getType() const { return mType; }
+ S32 getSize() const { return mSize; }
+ char *getName() const { return mName; }
+protected:
+ char *mName;
+ EMsgVariableType mType;
+ S32 mSize;
+};
+
+
+typedef enum e_message_block_type
+{
+ MBT_NULL,
+ MBT_SINGLE,
+ MBT_MULTIPLE,
+ MBT_VARIABLE,
+ MBT_EOF
+} EMsgBlockType;
+
+class LLMessageBlock
+{
+public:
+ LLMessageBlock(char *name, EMsgBlockType type, S32 number = 1) : mType(type), mNumber(number), mTotalSize(0)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageBlock()
+ {
+ for_each(mMemberVariables.begin(), mMemberVariables.end(), DeletePairedPointer());
+ }
+
+ void addVariable(char *name, const EMsgVariableType type, const S32 size)
+ {
+ LLMessageVariable** varp = &mMemberVariables[name];
+ if (*varp != NULL)
+ {
+ llerrs << name << " has already been used as a variable name!" << llendl;
+ }
+ *varp = new LLMessageVariable(name, type, size);
+ if (((*varp)->getType() != MVT_VARIABLE)
+ &&(mTotalSize != -1))
+ {
+ mTotalSize += (*varp)->getSize();
+ }
+ else
+ {
+ mTotalSize = -1;
+ }
+ }
+
+ EMsgVariableType getVariableType(char *name)
+ {
+ return (mMemberVariables[name])->getType();
+ }
+
+ S32 getVariableSize(char *name)
+ {
+ return (mMemberVariables[name])->getSize();
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg);
+
+ typedef std::map<const char *, LLMessageVariable*> message_variable_map_t;
+ message_variable_map_t mMemberVariables;
+ char *mName;
+ EMsgBlockType mType;
+ S32 mNumber;
+ S32 mTotalSize;
+};
+
+
+enum EMsgFrequency
+{
+ MFT_NULL = 0, // value is size of message number in bytes
+ MFT_HIGH = 1,
+ MFT_MEDIUM = 2,
+ MFT_LOW = 4
+};
+
+typedef enum e_message_trust
+{
+ MT_TRUST,
+ MT_NOTRUST
+} EMsgTrust;
+
+enum EMsgEncoding
+{
+ ME_UNENCODED,
+ ME_ZEROCODED
+};
+
+class LLMessageTemplate
+{
+public:
+ LLMessageTemplate(const char *name, U32 message_number, EMsgFrequency freq)
+ :
+ //mMemberBlocks(),
+ mName(NULL),
+ mFrequency(freq),
+ mTrust(MT_NOTRUST),
+ mEncoding(ME_ZEROCODED),
+ mMessageNumber(message_number),
+ mTotalSize(0),
+ mReceiveCount(0),
+ mReceiveBytes(0),
+ mReceiveInvalid(0),
+ mDecodeTimeThisFrame(0.f),
+ mTotalDecoded(0),
+ mTotalDecodeTime(0.f),
+ mMaxDecodeTimePerMsg(0.f),
+ mBanFromTrusted(false),
+ mBanFromUntrusted(false),
+ mHandlerFunc(NULL),
+ mUserData(NULL)
+ {
+ mName = gMessageStringTable.getString(name);
+ }
+
+ ~LLMessageTemplate()
+ {
+ for_each(mMemberBlocks.begin(), mMemberBlocks.end(), DeletePairedPointer());
+ }
+
+ void addBlock(LLMessageBlock *blockp)
+ {
+ LLMessageBlock** member_blockp = &mMemberBlocks[blockp->mName];
+ if (*member_blockp != NULL)
+ {
+ llerrs << "Block " << blockp->mName
+ << "has already been used as a block name!" << llendl;
+ }
+ *member_blockp = blockp;
+ if ( (mTotalSize != -1)
+ &&(blockp->mTotalSize != -1)
+ &&( (blockp->mType == MBT_SINGLE)
+ ||(blockp->mType == MBT_MULTIPLE)))
+ {
+ mTotalSize += blockp->mNumber*blockp->mTotalSize;
+ }
+ else
+ {
+ mTotalSize = -1;
+ }
+ }
+
+ LLMessageBlock *getBlock(char *name)
+ {
+ return mMemberBlocks[name];
+ }
+
+ // Trusted messages can only be recieved on trusted circuits.
+ void setTrust(EMsgTrust t)
+ {
+ mTrust = t;
+ }
+
+ EMsgTrust getTrust(void)
+ {
+ return mTrust;
+ }
+
+ // controls for how the message should be encoded
+ void setEncoding(EMsgEncoding e)
+ {
+ mEncoding = e;
+ }
+ EMsgEncoding getEncoding()
+ {
+ return mEncoding;
+ }
+
+ void setHandlerFunc(void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
+ {
+ mHandlerFunc = handler_func;
+ mUserData = user_data;
+ }
+
+ BOOL callHandlerFunc(LLMessageSystem *msgsystem)
+ {
+ if (mHandlerFunc)
+ {
+ mHandlerFunc(msgsystem, mUserData);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ bool isBanned(bool trustedSource)
+ {
+ return trustedSource ? mBanFromTrusted : mBanFromUntrusted;
+ }
+
+ friend std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg);
+
+public:
+ typedef std::map<char*, LLMessageBlock*> message_block_map_t;
+ message_block_map_t mMemberBlocks;
+ char *mName;
+ EMsgFrequency mFrequency;
+ EMsgTrust mTrust;
+ EMsgEncoding mEncoding;
+ U32 mMessageNumber;
+ S32 mTotalSize;
+ U32 mReceiveCount; // how many of this template have been received since last reset
+ U32 mReceiveBytes; // How many bytes received
+ U32 mReceiveInvalid; // How many "invalid" packets
+ F32 mDecodeTimeThisFrame; // Total seconds spent decoding this frame
+ U32 mTotalDecoded; // Total messages successfully decoded
+ F32 mTotalDecodeTime; // Total time successfully decoding messages
+ F32 mMaxDecodeTimePerMsg;
+
+ bool mBanFromTrusted;
+ bool mBanFromUntrusted;
+
+private:
+ // message handler function (this is set by each application)
+ void (*mHandlerFunc)(LLMessageSystem *msgsystem, void **user_data);
+ void **mUserData;
+};
+
+
+
+// static
+BOOL LLMessageSystem::mTimeDecodes = FALSE;
+
+// static, 50ms per message decode
+F32 LLMessageSystem::mTimeDecodesSpamThreshold = 0.05f;
+
+// FIXME: This needs to be moved into a seperate file so that it never gets
+// included in the viewer. 30 Sep 2002 mark
+// NOTE: I don't think it's important that the messgage system tracks
+// this since it must get set externally. 2004.08.25 Phoenix.
+static std::string g_shared_secret;
+std::string get_shared_secret();
+
+class LLMessagePollInfo
+{
+public:
+ apr_socket_t *mAPRSocketp;
+ apr_pollfd_t mPollFD;
+};
+
+
+// LLMessageVariable functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageVariable &msg)
+{
+ s << "\t\t" << msg.mName << " (";
+ switch (msg.mType)
+ {
+ case MVT_FIXED:
+ s << "Fixed, " << msg.mSize << " bytes total)\n";
+ break;
+ case MVT_VARIABLE:
+ s << "Variable, " << msg.mSize << " bytes of size info)\n";
+ break;
+ default:
+ s << "Unknown\n";
+ break;
+ }
+ return s;
+}
+
+// LLMessageBlock functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageBlock &msg)
+{
+ s << "\t" << msg.mName << " (";
+ switch (msg.mType)
+ {
+ case MBT_SINGLE:
+ s << "Fixed";
+ break;
+ case MBT_MULTIPLE:
+ s << "Multiple - " << msg.mNumber << " copies";
+ break;
+ case MBT_VARIABLE:
+ s << "Variable";
+ break;
+ default:
+ s << "Unknown";
+ break;
+ }
+ if (msg.mTotalSize != -1)
+ {
+ s << ", " << msg.mTotalSize << " bytes each, " << msg.mNumber*msg.mTotalSize << " bytes total)\n";
+ }
+ else
+ {
+ s << ")\n";
+ }
+
+
+ for (LLMessageBlock::message_variable_map_t::iterator iter = msg.mMemberVariables.begin();
+ iter != msg.mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ s << ci;
+ }
+
+ return s;
+}
+
+// LLMessageTemplate functions and friends
+
+std::ostream& operator<<(std::ostream& s, LLMessageTemplate &msg)
+{
+ switch (msg.mFrequency)
+ {
+ case MFT_HIGH:
+ s << "========================================\n" << "Message #" << msg.mMessageNumber << "\n" << msg.mName << " (";
+ s << "High";
+ break;
+ case MFT_MEDIUM:
+ s << "========================================\n" << "Message #";
+ s << (msg.mMessageNumber & 0xFF) << "\n" << msg.mName << " (";
+ s << "Medium";
+ break;
+ case MFT_LOW:
+ s << "========================================\n" << "Message #";
+ s << (msg.mMessageNumber & 0xFFFF) << "\n" << msg.mName << " (";
+ s << "Low";
+ break;
+ default:
+ s << "Unknown";
+ break;
+ }
+
+ if (msg.mTotalSize != -1)
+ {
+ s << ", " << msg.mTotalSize << " bytes total)\n";
+ }
+ else
+ {
+ s << ")\n";
+ }
+
+ for (LLMessageTemplate::message_block_map_t::iterator iter = msg.mMemberBlocks.begin();
+ iter != msg.mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* ci = iter->second;
+ s << *ci;
+ }
+
+ return s;
+}
+
+// LLMessageList functions and friends
+
+// Lets support a small subset of regular expressions here
+// Syntax is a string made up of:
+// a - checks against alphanumeric ([A-Za-z0-9])
+// c - checks against character ([A-Za-z])
+// f - checks against first variable character ([A-Za-z_])
+// v - checks against variable ([A-Za-z0-9_])
+// s - checks against sign of integer ([-0-9])
+// d - checks against integer digit ([0-9])
+// * - repeat last check
+
+// checks 'a'
+BOOL b_return_alphanumeric_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&( (c < '0')
+ ||(c > '9')))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'c'
+BOOL b_return_character_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z')))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'f'
+BOOL b_return_first_variable_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&(c != '_'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'v'
+BOOL b_return_variable_ok(char c)
+{
+ if ( ( (c < 'A')
+ ||(c > 'Z'))
+ &&( (c < 'a')
+ ||(c > 'z'))
+ &&( (c < '0')
+ ||(c > '9'))
+ &&(c != '_'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 's'
+BOOL b_return_signed_integer_ok(char c)
+{
+ if ( ( (c < '0')
+ ||(c > '9'))
+ &&(c != '-'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// checks 'd'
+BOOL b_return_integer_ok(char c)
+{
+ if ( (c < '0')
+ ||(c > '9'))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL (*gParseCheckCharacters[])(char c) =
+{
+ b_return_alphanumeric_ok,
+ b_return_character_ok,
+ b_return_first_variable_ok,
+ b_return_variable_ok,
+ b_return_signed_integer_ok,
+ b_return_integer_ok
+};
+
+S32 get_checker_number(char checker)
+{
+ switch(checker)
+ {
+ case 'a':
+ return 0;
+ case 'c':
+ return 1;
+ case 'f':
+ return 2;
+ case 'v':
+ return 3;
+ case 's':
+ return 4;
+ case 'd':
+ return 5;
+ case '*':
+ return 9999;
+ default:
+ return -1;
+ }
+}
+
+// check token based on passed simplified regular expression
+BOOL b_check_token(char *token, char *regexp)
+{
+ S32 tptr, rptr = 0;
+ S32 current_checker, next_checker = 0;
+
+ current_checker = get_checker_number(regexp[rptr++]);
+
+ if (current_checker == -1)
+ {
+ llerrs << "Invalid regular expression value!" << llendl;
+ return FALSE;
+ }
+
+ if (current_checker == 9999)
+ {
+ llerrs << "Regular expression can't start with *!" << llendl;
+ return FALSE;
+ }
+
+ for (tptr = 0; token[tptr]; tptr++)
+ {
+ if (current_checker == -1)
+ {
+ llerrs << "Input exceeds regular expression!\nDid you forget a *?" << llendl;
+ return FALSE;
+ }
+
+ if (!gParseCheckCharacters[current_checker](token[tptr]))
+ {
+ return FALSE;
+ }
+ if (next_checker != 9999)
+ {
+ next_checker = get_checker_number(regexp[rptr++]);
+ if (next_checker != 9999)
+ {
+ current_checker = next_checker;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// C variable can be made up of upper or lower case letters, underscores, or numbers, but can't start with a number
+BOOL b_variable_ok(char *token)
+{
+ if (!b_check_token(token, "fv*"))
+ {
+ llerrs << "Token '" << token << "' isn't a variable!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// An integer is made up of the digits 0-9 and may be preceded by a '-'
+BOOL b_integer_ok(char *token)
+{
+ if (!b_check_token(token, "sd*"))
+ {
+ llerrs << "Token isn't an integer!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// An integer is made up of the digits 0-9
+BOOL b_positive_integer_ok(char *token)
+{
+ if (!b_check_token(token, "d*"))
+ {
+ llerrs << "Token isn't an integer!" << llendl;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void LLMessageSystem::init()
+{
+ // initialize member variables
+ mVerboseLog = FALSE;
+
+ mbError = FALSE;
+ mErrorCode = 0;
+ mIncomingCompressedSize = 0;
+ mSendReliable = FALSE;
+
+ mbSBuilt = FALSE;
+ mbSClear = TRUE;
+
+ mUnackedListDepth = 0;
+ mUnackedListSize = 0;
+ mDSMaxListDepth = 0;
+
+ mCurrentRMessageData = NULL;
+ mCurrentRMessageTemplate = NULL;
+
+ mCurrentSMessageData = NULL;
+ mCurrentSMessageTemplate = NULL;
+ mCurrentSMessageName = NULL;
+
+ mCurrentRecvPacketID = 0;
+
+ mNumberHighFreqMessages = 0;
+ mNumberMediumFreqMessages = 0;
+ mNumberLowFreqMessages = 0;
+ mPacketsIn = mPacketsOut = 0;
+ mBytesIn = mBytesOut = 0;
+ mCompressedPacketsIn = mCompressedPacketsOut = 0;
+ mReliablePacketsIn = mReliablePacketsOut = 0;
+
+ mCompressedBytesIn = 0;
+ mCompressedBytesOut = 0;
+ mUncompressedBytesIn = 0;
+ mUncompressedBytesOut = 0;
+ mTotalBytesIn = 0;
+ mTotalBytesOut = 0;
+
+ mDroppedPackets = 0; // total dropped packets in
+ mResentPackets = 0; // total resent packets out
+ mFailedResendPackets = 0; // total resend failure packets out
+ mOffCircuitPackets = 0; // total # of off-circuit packets rejected
+ mInvalidOnCircuitPackets = 0; // total # of on-circuit packets rejected
+
+ mOurCircuitCode = 0;
+
+ mMessageFileChecksum = 0;
+ mMessageFileVersionNumber = 0.f;
+}
+
+LLMessageSystem::LLMessageSystem()
+{
+ init();
+
+ mSystemVersionMajor = 0;
+ mSystemVersionMinor = 0;
+ mSystemVersionPatch = 0;
+ mSystemVersionServer = 0;
+ mVersionFlags = 0x0;
+
+ // default to not accepting packets from not alive circuits
+ mbProtected = TRUE;
+
+ mSendPacketFailureCount = 0;
+ mCircuitPrintFreq = 0.f; // seconds
+
+ // initialize various bits of net info
+ mSocket = 0;
+ mPort = 0;
+
+ mPollInfop = NULL;
+
+ mResendDumpTime = 0;
+ mMessageCountTime = 0;
+ mCircuitPrintTime = 0;
+ mCurrentMessageTimeSeconds = 0;
+
+ // Constants for dumping output based on message processing time/count
+ mNumMessageCounts = 0;
+ mMaxMessageCounts = 0; // >= 0 means dump warnings
+ mMaxMessageTime = 0.f;
+
+ mTrueReceiveSize = 0;
+
+ // Error if checking this state, subclass methods which aren't implemented are delegated
+ // to properly constructed message system.
+ mbError = TRUE;
+}
+
+// Read file and build message templates
+LLMessageSystem::LLMessageSystem(const char *filename, U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch)
+{
+ init();
+
+ mSystemVersionMajor = version_major;
+ mSystemVersionMinor = version_minor;
+ mSystemVersionPatch = version_patch;
+ mSystemVersionServer = 0;
+ mVersionFlags = 0x0;
+
+ // default to not accepting packets from not alive circuits
+ mbProtected = TRUE;
+
+ mSendPacketFailureCount = 0;
+
+ mCircuitPrintFreq = 60.f; // seconds
+
+ loadTemplateFile(filename);
+
+ // initialize various bits of net info
+ mSocket = 0;
+ mPort = port;
+
+ S32 error = start_net(mSocket, mPort);
+ if (error != 0)
+ {
+ mbError = TRUE;
+ mErrorCode = error;
+ }
+ //llinfos << << "*** port: " << mPort << llendl;
+
+ //
+ // Create the data structure that we can poll on
+ //
+ if (!gAPRPoolp)
+ {
+ llerrs << "No APR pool before message system initialization!" << llendl;
+ ll_init_apr();
+ }
+ apr_socket_t *aprSocketp = NULL;
+ apr_os_sock_put(&aprSocketp, (apr_os_sock_t*)&mSocket, gAPRPoolp);
+
+ mPollInfop = new LLMessagePollInfo;
+ mPollInfop->mAPRSocketp = aprSocketp;
+ mPollInfop->mPollFD.p = gAPRPoolp;
+ mPollInfop->mPollFD.desc_type = APR_POLL_SOCKET;
+ mPollInfop->mPollFD.reqevents = APR_POLLIN;
+ mPollInfop->mPollFD.rtnevents = 0;
+ mPollInfop->mPollFD.desc.s = aprSocketp;
+ mPollInfop->mPollFD.client_data = NULL;
+
+ F64 mt_sec = getMessageTimeSeconds();
+ mResendDumpTime = mt_sec;
+ mMessageCountTime = mt_sec;
+ mCircuitPrintTime = mt_sec;
+ mCurrentMessageTimeSeconds = mt_sec;
+
+ // Constants for dumping output based on message processing time/count
+ mNumMessageCounts = 0;
+ mMaxMessageCounts = 200; // >= 0 means dump warnings
+ mMaxMessageTime = 1.f;
+
+ mTrueReceiveSize = 0;
+}
+
+// Read file and build message templates
+void LLMessageSystem::loadTemplateFile(const char* filename)
+{
+ if(!filename)
+ {
+ llerrs << "No template filename specified" << llendl;
+ }
+
+ char token[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ // state variables
+ BOOL b_template_start = TRUE;
+ BOOL b_template_end = FALSE;
+ BOOL b_template = FALSE;
+ BOOL b_block_start = FALSE;
+ BOOL b_block_end = FALSE;
+ BOOL b_block = FALSE;
+ BOOL b_variable_start = FALSE;
+ BOOL b_variable_end = FALSE;
+ BOOL b_variable = FALSE;
+ //BOOL b_in_comment_block = FALSE; // not yet used
+
+ // working temp variables
+ LLMessageTemplate *templatep = NULL;
+ char template_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ LLMessageBlock *blockp = NULL;
+ char block_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+
+ LLMessageVariable var;
+ char var_name[MAX_MESSAGE_INTERNAL_NAME_SIZE]; /* Flawfinder: ignore */
+ char formatString[MAX_MESSAGE_INTERNAL_NAME_SIZE];
+
+ FILE* messagefilep = NULL;
+ mMessageFileChecksum = 0;
+ mMessageFileVersionNumber = 0.f;
+ S32 checksum_offset = 0;
+ char* checkp = NULL;
+
+ snprintf(formatString, sizeof(formatString), "%%%ds", MAX_MESSAGE_INTERNAL_NAME_SIZE);
+ messagefilep = LLFile::fopen(filename, "r");
+ if (messagefilep)
+ {
+// mName = gMessageStringTable.getString(filename);
+
+ fseek(messagefilep, 0L, SEEK_SET );
+ while(fscanf(messagefilep, formatString, token) != EOF)
+ {
+ // skip comments
+ if (token[0] == '/')
+ {
+ // skip to end of line
+ while (token[0] != 10)
+ fscanf(messagefilep, "%c", token);
+ continue;
+ }
+
+ checkp = token;
+
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // what are we looking for
+ if (!strcmp(token, "{"))
+ {
+ // is that a legit option?
+ if (b_template_start)
+ {
+ // yup!
+ b_template_start = FALSE;
+
+ // remember that it could be only a signal message, so name is all that it contains
+ b_template_end = TRUE;
+
+ // start working on it!
+ b_template = TRUE;
+ }
+ else if (b_block_start)
+ {
+ // yup!
+ b_block_start = FALSE;
+ b_template_end = FALSE;
+
+ // start working on it!
+ b_block = TRUE;
+ }
+ else if (b_variable_start)
+ {
+ // yup!
+ b_variable_start = FALSE;
+ b_block_end = FALSE;
+
+ // start working on it!
+ b_variable = TRUE;
+ }
+ else
+ {
+ llerrs << "Detcted unexpected token '" << token
+ << "' while parsing template." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ }
+
+ if (!strcmp(token, "}"))
+ {
+ // is that a legit option?
+ if (b_template_end)
+ {
+ // yup!
+ b_template_end = FALSE;
+ b_template = FALSE;
+ b_block_start = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it!
+ addTemplate(templatep);
+
+ //llinfos << "Read template: "templatep->mNametemp_str
+ // << llendl;
+
+ // look for next one!
+ b_template_start = TRUE;
+ }
+ else if (b_block_end)
+ {
+ // yup!
+ b_block_end = FALSE;
+ b_variable_start = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it to template
+
+ templatep->addBlock(blockp);
+
+ // start working on it!
+ b_template_end = TRUE;
+ b_block_start = TRUE;
+ }
+ else if (b_variable_end)
+ {
+ // yup!
+ b_variable_end = FALSE;
+
+ // add data!
+ // we've gotten a complete variable! hooray!
+ // add it to block
+ blockp->addVariable(var.getName(), var.getType(), var.getSize());
+
+ // start working on it!
+ b_variable_start = TRUE;
+ b_block_end = TRUE;
+ }
+ else
+ {
+ llerrs << "Detcted unexpected token '" << token
+ << "' while parsing template." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ }
+
+ // now, are we looking to start a template?
+ if (b_template)
+ {
+
+ b_template = FALSE;
+
+ // name first
+ if (fscanf(messagefilep, formatString, template_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template name, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // debugging to help figure out busted templates
+ //llinfos << template_name << llendl;
+
+ // is name a legit C variable name
+ if (!b_variable_ok(template_name))
+ {
+ // nope!
+ llerrs << "Not legal message template name: "
+ << template_name << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = template_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // ok, now get Frequency ("High", "Medium", or "Low")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template frequency, found EOF."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // which one is it?
+ if (!strcmp(token, "High"))
+ {
+ if (++mNumberHighFreqMessages == 255)
+ {
+ // oops, too many High Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 254 High frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is just mNumberHighFreqMessages
+ templatep = new LLMessageTemplate(template_name, mNumberHighFreqMessages, MFT_HIGH);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberHighFreqMessages
+ // << std::dec << " high"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Medium"))
+ {
+ if (++mNumberMediumFreqMessages == 255)
+ {
+ // oops, too many Medium Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 254 Medium frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is ((255 << 8) | mNumberMediumFreqMessages)
+ templatep = new LLMessageTemplate(template_name, (255 << 8) | mNumberMediumFreqMessages, MFT_MEDIUM);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberMediumFreqMessages
+ // << std::dec << " medium"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Low"))
+ {
+ if (++mNumberLowFreqMessages == 65535)
+ {
+ // oops, too many High Frequency messages!!
+ llerrs << "Message " << template_name
+ << " exceeded 65534 Low frequency messages!"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a template!
+ // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages)
+ templatep = new LLMessageTemplate(template_name, (255 << 24) | (255 << 16) | mNumberLowFreqMessages, MFT_LOW);
+ //lldebugs << "Template " << template_name << " # "
+ // << std::hex << mNumberLowFreqMessages
+ // << std::dec << " low"
+ // << llendl;
+ }
+ else if (!strcmp(token, "Fixed"))
+ {
+ U32 message_num = 0;
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected message template number (fixed),"
+ << " found EOF." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ message_num = strtoul(token,NULL,0);
+
+ // ok, we can create a template!
+ // message number is ((255 << 24) | (255 << 16) | mNumberLowFreqMessages)
+ templatep = new LLMessageTemplate(template_name, message_num, MFT_LOW);
+ }
+ else
+ {
+ // oops, bad frequency line
+ llerrs << "Bad frequency! " << token
+ << " isn't High, Medium, or Low" << llendl
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // Now get trust ("Trusted", "NotTrusted")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // File ended
+ llerrs << "Expected message template "
+ "trust, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32) *checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ if (strcmp(token, "Trusted") == 0)
+ {
+ templatep->setTrust(MT_TRUST);
+ }
+ else if (strcmp(token, "NotTrusted") == 0)
+ {
+ templatep->setTrust(MT_NOTRUST);
+ }
+ else
+ {
+ // bad trust token
+ llerrs << "bad trust: " << token
+ << " isn't Trusted or NotTrusted"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // get encoding
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // File ended
+ llerrs << "Expected message encoding, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ checkp = token;
+ while(*checkp)
+ {
+ mMessageFileChecksum += ((U32) *checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ if(0 == strcmp(token, "Unencoded"))
+ {
+ templatep->setEncoding(ME_UNENCODED);
+ }
+ else if(0 == strcmp(token, "Zerocoded"))
+ {
+ templatep->setEncoding(ME_ZEROCODED);
+ }
+ else
+ {
+ // bad trust token
+ llerrs << "bad encoding: " << token
+ << " isn't Unencoded or Zerocoded" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // ok, now we need to look for a block
+ b_block_start = TRUE;
+ continue;
+ }
+
+ // now, are we looking to start a template?
+ if (b_block)
+ {
+ b_block = FALSE;
+ // ok, need to pull header info
+
+ // name first
+ if (fscanf(messagefilep, formatString, block_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block name, but file ended" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = block_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is name a legit C variable name
+ if (!b_variable_ok(block_name))
+ {
+ // nope!
+ llerrs << block_name << "is not a legal block name"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // now, block type ("Single", "Multiple", or "Variable")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block type, but file ended." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // which one is it?
+ if (!strcmp(token, "Single"))
+ {
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_SINGLE);
+ }
+ else if (!strcmp(token, "Multiple"))
+ {
+ // need to get the number of repeats
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected block multiple count,"
+ " but file ended." << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << "is not a legal integer for"
+ " block multiple count" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_MULTIPLE, atoi(token));
+ }
+ else if (!strcmp(token, "Variable"))
+ {
+ // ok, we can create a block
+ blockp = new LLMessageBlock(block_name, MBT_VARIABLE);
+ }
+ else
+ {
+ // oops, bad block type
+ llerrs << "Bad block type! " << token
+ << " isn't Single, Multiple, or Variable" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, now we need to look for a variable
+ b_variable_start = TRUE;
+ continue;
+ }
+
+ // now, are we looking to start a template?
+ if (b_variable)
+ {
+ b_variable = FALSE;
+ // ok, need to pull header info
+
+ // name first
+ if (fscanf(messagefilep, formatString, var_name) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable name, but file ended."
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = var_name;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is name a legit C variable name
+ if (!b_variable_ok(var_name))
+ {
+ // nope!
+ llerrs << var_name << " is not a legal variable name"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // now, variable type ("Fixed" or "Variable")
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable type, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+
+ // which one is it?
+ if (!strcmp(token, "U8"))
+ {
+ var = LLMessageVariable(var_name, MVT_U8, 1);
+ }
+ else if (!strcmp(token, "U16"))
+ {
+ var = LLMessageVariable(var_name, MVT_U16, 2);
+ }
+ else if (!strcmp(token, "U32"))
+ {
+ var = LLMessageVariable(var_name, MVT_U32, 4);
+ }
+ else if (!strcmp(token, "U64"))
+ {
+ var = LLMessageVariable(var_name, MVT_U64, 8);
+ }
+ else if (!strcmp(token, "S8"))
+ {
+ var = LLMessageVariable(var_name, MVT_S8, 1);
+ }
+ else if (!strcmp(token, "S16"))
+ {
+ var = LLMessageVariable(var_name, MVT_S16, 2);
+ }
+ else if (!strcmp(token, "S32"))
+ {
+ var = LLMessageVariable(var_name, MVT_S32, 4);
+ }
+ else if (!strcmp(token, "S64"))
+ {
+ var = LLMessageVariable(var_name, MVT_S64, 8);
+ }
+ else if (!strcmp(token, "F32"))
+ {
+ var = LLMessageVariable(var_name, MVT_F32, 4);
+ }
+ else if (!strcmp(token, "F64"))
+ {
+ var = LLMessageVariable(var_name, MVT_F64, 8);
+ }
+ else if (!strcmp(token, "LLVector3"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector3, 12);
+ }
+ else if (!strcmp(token, "LLVector3d"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector3d, 24);
+ }
+ else if (!strcmp(token, "LLVector4"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLVector4, 16);
+ }
+ else if (!strcmp(token, "LLQuaternion"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLQuaternion, 12);
+ }
+ else if (!strcmp(token, "LLUUID"))
+ {
+ var = LLMessageVariable(var_name, MVT_LLUUID, 16);
+ }
+ else if (!strcmp(token, "BOOL"))
+ {
+ var = LLMessageVariable(var_name, MVT_BOOL, 1);
+ }
+ else if (!strcmp(token, "IPADDR"))
+ {
+ var = LLMessageVariable(var_name, MVT_IP_ADDR, 4);
+ }
+ else if (!strcmp(token, "IPPORT"))
+ {
+ var = LLMessageVariable(var_name, MVT_IP_PORT, 2);
+ }
+ else if (!strcmp(token, "Fixed"))
+ {
+ // need to get the variable size
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable size, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << " is not a legal integer for"
+ " variable size" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ var = LLMessageVariable(var_name, MVT_FIXED, atoi(token));
+ }
+ else if (!strcmp(token, "Variable"))
+ {
+ // need to get the variable size
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected variable size, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ // is it a legal integer
+ if (!b_positive_integer_ok(token))
+ {
+ // nope!
+ llerrs << token << "is not a legal integer"
+ " for variable size" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+ // ok, we can create a block
+ var = LLMessageVariable(var_name, MVT_VARIABLE, atoi(token));
+ }
+ else
+ {
+ // oops, bad variable type
+ llerrs << "Bad variable type! " << token
+ << " isn't Fixed or Variable" << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ // we got us a variable!
+ b_variable_end = TRUE;
+ continue;
+ }
+
+ // do we have a version number stuck in the file?
+ if (!strcmp(token, "version"))
+ {
+ // version number
+ if (fscanf(messagefilep, formatString, token) == EOF)
+ {
+ // oops, file ended
+ llerrs << "Expected version number, but file ended"
+ << llendl;
+ mbError = TRUE;
+ fclose(messagefilep);
+ return;
+ }
+
+ checkp = token;
+ while (*checkp)
+ {
+ mMessageFileChecksum += ((U32)*checkp++) << checksum_offset;
+ checksum_offset = (checksum_offset + 8) % 32;
+ }
+
+ mMessageFileVersionNumber = (F32)atof(token);
+
+// llinfos << "### Message template version " << mMessageFileVersionNumber << " ###" << llendl;
+ continue;
+ }
+ }
+
+ llinfos << "Message template checksum = " << std::hex << mMessageFileChecksum << std::dec << llendl;
+ }
+ else
+ {
+ llwarns << "Failed to open template: " << filename << llendl;
+ mbError = TRUE;
+ return;
+ }
+ fclose(messagefilep);
+}
+
+
+LLMessageSystem::~LLMessageSystem()
+{
+ mMessageTemplates.clear(); // don't delete templates.
+ for_each(mMessageNumbers.begin(), mMessageNumbers.end(), DeletePairedPointer());
+ mMessageNumbers.clear();
+
+ if (!mbError)
+ {
+ end_net();
+ }
+
+ delete mCurrentRMessageData;
+ mCurrentRMessageData = NULL;
+
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ delete mPollInfop;
+ mPollInfop = NULL;
+}
+
+void LLMessageSystem::clearReceiveState()
+{
+ mReceiveSize = -1;
+ mCurrentRecvPacketID = 0;
+ mCurrentRMessageTemplate = NULL;
+ delete mCurrentRMessageData;
+ mCurrentRMessageData = NULL;
+ mIncomingCompressedSize = 0;
+ mLastSender.invalidate();
+}
+
+
+BOOL LLMessageSystem::poll(F32 seconds)
+{
+ S32 num_socks;
+ apr_status_t status;
+ status = apr_poll(&(mPollInfop->mPollFD), 1, &num_socks,(U64)(seconds*1000000.f));
+ if (status != APR_TIMEUP)
+ {
+ ll_apr_warn_status(status);
+ }
+ if (num_socks)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// Returns TRUE if a valid, on-circuit message has been received.
+BOOL LLMessageSystem::checkMessages( S64 frame_count )
+{
+ BOOL valid_packet = FALSE;
+
+ LLTransferTargetVFile::updateQueue();
+
+ if (!mNumMessageCounts)
+ {
+ // This is the first message being handled after a resetReceiveCounts, we must be starting
+ // the message processing loop. Reset the timers.
+ mCurrentMessageTimeSeconds = totalTime() * SEC_PER_USEC;
+ mMessageCountTime = getMessageTimeSeconds();
+ }
+
+ // loop until either no packets or a valid packet
+ // i.e., burn through packets from unregistered circuits
+ do
+ {
+ clearReceiveState();
+
+ BOOL recv_reliable = FALSE;
+ BOOL recv_resent = FALSE;
+ S32 acks = 0;
+ S32 true_rcv_size = 0;
+
+ U8* buffer = mTrueReceiveBuffer;
+
+ mTrueReceiveSize = mPacketRing.receivePacket(mSocket, (char *)mTrueReceiveBuffer);
+ // If you want to dump all received packets into SecondLife.log, uncomment this
+ //dumpPacketToLog();
+
+ mReceiveSize = mTrueReceiveSize;
+ mLastSender = mPacketRing.getLastSender();
+
+ if (mReceiveSize < (S32) LL_MINIMUM_VALID_PACKET_SIZE)
+ {
+ // A receive size of zero is OK, that means that there are no more packets available.
+ // Ones that are non-zero but below the minimum packet size are worrisome.
+ if (mReceiveSize > 0)
+ {
+ llwarns << "Invalid (too short) packet discarded " << mReceiveSize << llendl;
+ callExceptionFunc(MX_PACKET_TOO_SHORT);
+ }
+ // no data in packet receive buffer
+ valid_packet = FALSE;
+ }
+ else
+ {
+ LLHost host;
+ LLCircuitData *cdp;
+
+ // note if packet acks are appended.
+ if(buffer[0] & LL_ACK_FLAG)
+ {
+ acks += buffer[--mReceiveSize];
+ true_rcv_size = mReceiveSize;
+ mReceiveSize -= acks * sizeof(TPACKETID);
+ }
+
+ // process the message as normal
+
+ mIncomingCompressedSize = zeroCodeExpand(&buffer,&mReceiveSize);
+ mCurrentRecvPacketID = buffer[1] + ((buffer[0] & 0x0f ) * 256);
+ if (sizeof(TPACKETID) == 4)
+ {
+ mCurrentRecvPacketID *= 256;
+ mCurrentRecvPacketID += buffer[2];
+ mCurrentRecvPacketID *= 256;
+ mCurrentRecvPacketID += buffer[3];
+ }
+
+ host = getSender();
+ //llinfos << host << ":" << mCurrentRecvPacketID << llendl;
+
+ // For testing the weird case we're having in the office where the first few packets
+ // on a connection get dropped
+ //if ((mCurrentRecvPacketID < 8) && !(buffer[0] & LL_RESENT_FLAG))
+ //{
+ // llinfos << "Evil! Dropping " << mCurrentRecvPacketID << " from " << host << " for fun!" << llendl;
+ // continue;
+ //}
+
+ cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ // This packet comes from a circuit we don't know about.
+
+ // Are we rejecting off-circuit packets?
+ if (mbProtected)
+ {
+ // cdp is already NULL, so we don't need to unset it.
+ }
+ else
+ {
+ // nope, open the new circuit
+ cdp = mCircuitInfo.addCircuitData(host, mCurrentRecvPacketID);
+
+ // I added this - I think it's correct - DJS
+ // reset packet in ID
+ cdp->setPacketInID(mCurrentRecvPacketID);
+
+ // And claim the packet is on the circuit we just added.
+ }
+ }
+ else
+ {
+ // this is an old circuit. . . is it still alive?
+ if (!cdp->isAlive())
+ {
+ // nope. don't accept if we're protected
+ if (mbProtected)
+ {
+ // don't accept packets from unexpected sources
+ cdp = NULL;
+ }
+ else
+ {
+ // wake up the circuit
+ cdp->setAlive(TRUE);
+
+ // reset packet in ID
+ cdp->setPacketInID(mCurrentRecvPacketID);
+ }
+ }
+ }
+
+ // At this point, cdp is now a pointer to the circuit that
+ // this message came in on if it's valid, and NULL if the
+ // circuit was bogus.
+
+ if(cdp && (acks > 0) && ((S32)(acks * sizeof(TPACKETID)) < (true_rcv_size)))
+ {
+ TPACKETID packet_id;
+ U32 mem_id=0;
+ for(S32 i = 0; i < acks; ++i)
+ {
+ true_rcv_size -= sizeof(TPACKETID);
+ memcpy(&mem_id, &mTrueReceiveBuffer[true_rcv_size], /* Flawfinder: ignore*/
+ sizeof(TPACKETID));
+ packet_id = ntohl(mem_id);
+ //llinfos << "got ack: " << packet_id << llendl;
+ cdp->ackReliablePacket(packet_id);
+ }
+ if (!cdp->getUnackedPacketCount())
+ {
+ // Remove this circuit from the list of circuits with unacked packets
+ mCircuitInfo.mUnackedCircuitMap.erase(cdp->mHost);
+ }
+ }
+
+ if (buffer[0] & LL_RELIABLE_FLAG)
+ {
+ recv_reliable = TRUE;
+ }
+ if (buffer[0] & LL_RESENT_FLAG)
+ {
+ recv_resent = TRUE;
+ if (cdp && cdp->isDuplicateResend(mCurrentRecvPacketID))
+ {
+ // We need to ACK here to suppress
+ // further resends of packets we've
+ // already seen.
+ if (recv_reliable)
+ {
+ //mAckList.addData(new LLPacketAck(host, mCurrentRecvPacketID));
+ // ***************************************
+ // TESTING CODE
+ //if(mCircuitInfo.mCurrentCircuit->mHost != host)
+ //{
+ // llwarns << "DISCARDED PACKET HOST MISMATCH! HOST: "
+ // << host << " CIRCUIT: "
+ // << mCircuitInfo.mCurrentCircuit->mHost
+ // << llendl;
+ //}
+ // ***************************************
+ //mCircuitInfo.mCurrentCircuit->mAcks.put(mCurrentRecvPacketID);
+ cdp->collectRAck(mCurrentRecvPacketID);
+ }
+
+ //llinfos << "Discarding duplicate resend from " << host << llendl;
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore*/
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID);/* Flawfinder: ignore*/
+ str << buffer << "(unknown)"
+ << (recv_reliable ? " reliable" : "")
+ << " resent "
+ << ((acks > 0) ? "acks" : "")
+ << " DISCARD DUPLICATE";
+ llinfos << str.str() << llendl;
+ }
+ mPacketsIn++;
+ valid_packet = FALSE;
+ continue;
+ }
+ }
+
+ // UseCircuitCode can be a valid, off-circuit packet.
+ // But we don't want to acknowledge UseCircuitCode until the circuit is
+ // available, which is why the acknowledgement test is done above. JC
+
+ valid_packet = decodeTemplate( buffer, mReceiveSize, &mCurrentRMessageTemplate );
+ if( valid_packet )
+ {
+ mCurrentRMessageTemplate->mReceiveCount++;
+ lldebugst(LLERR_MESSAGE) << "MessageRecvd:" << mCurrentRMessageTemplate->mName << " from " << host << llendl;
+ }
+
+ // UseCircuitCode is allowed in even from an invalid circuit, so that
+ // we can toss circuits around.
+ if (valid_packet && !cdp && (mCurrentRMessageTemplate->mName != _PREHASH_UseCircuitCode) )
+ {
+ logMsgFromInvalidCircuit( host, recv_reliable );
+ clearReceiveState();
+ valid_packet = FALSE;
+ }
+
+ if (valid_packet && cdp && !cdp->getTrusted() && (mCurrentRMessageTemplate->getTrust() == MT_TRUST) )
+ {
+ logTrustedMsgFromUntrustedCircuit( host );
+ clearReceiveState();
+
+ sendDenyTrustedCircuit(host);
+ valid_packet = FALSE;
+ }
+
+ if (valid_packet
+ && mCurrentRMessageTemplate->isBanned(cdp && cdp->getTrusted()))
+ {
+ llwarns << "LLMessageSystem::checkMessages "
+ << "received banned message "
+ << mCurrentRMessageTemplate->mName
+ << " from "
+ << ((cdp && cdp->getTrusted()) ? "trusted " : "untrusted ")
+ << host << llendl;
+ clearReceiveState();
+ valid_packet = FALSE;
+ }
+
+ if( valid_packet )
+ {
+ logValidMsg(cdp, host, recv_reliable, recv_resent, (BOOL)(acks>0) );
+
+ valid_packet = decodeData( buffer, host );
+ }
+
+ // It's possible that the circuit went away, because ANY message can disable the circuit
+ // (for example, UseCircuit, CloseCircuit, DisableSimulator). Find it again.
+ cdp = mCircuitInfo.findCircuit(host);
+
+ if (valid_packet)
+ {
+ /* Code for dumping the complete contents of a message. Keep for future use in optimizing messages.
+ if( 1 )
+ {
+ static char* object_update = gMessageStringTable.getString("ObjectUpdate");
+ if(object_update == mCurrentRMessageTemplate->mName )
+ {
+ llinfos << "ObjectUpdate:" << llendl;
+ U32 i;
+ llinfos << " Zero Encoded: " << zero_unexpanded_size << llendl;
+ for( i = 0; i<zero_unexpanded_size; i++ )
+ {
+ llinfos << " " << i << ": " << (U32) zero_unexpanded_buffer[i] << llendl;
+ }
+ llinfos << "" << llendl;
+
+ llinfos << " Zero Unencoded: " << mReceiveSize << llendl;
+ for( i = 0; i<mReceiveSize; i++ )
+ {
+ llinfos << " " << i << ": " << (U32) buffer[i] << llendl;
+ }
+ llinfos << "" << llendl;
+
+ llinfos << " Blocks and variables: " << llendl;
+ S32 byte_count = 0;
+ for (LLMessageTemplate::message_block_map_t::iterator
+ iter = mCurrentRMessageTemplate->mMemberBlocks.begin(),
+ end = mCurrentRMessageTemplate->mMemberBlocks.end();
+ iter != end; iter++)
+ {
+ LLMessageBlock* block = iter->second;
+ const char* block_name = block->mName;
+ for (LLMsgBlkData::msg_var_data_map_t::iterator
+ iter = block->mMemberVariables.begin(),
+ end = block->mMemberVariables.end();
+ iter != end; iter++)
+ {
+ const char* var_name = iter->first;
+
+ if( getNumberOfBlocksFast( block_name ) < 1 )
+ {
+ llinfos << var_name << " has no blocks" << llendl;
+ }
+ for( S32 blocknum = 0; blocknum < getNumberOfBlocksFast( block_name ); blocknum++ )
+ {
+ char *bnamep = (char *)block_name + blocknum; // this works because it's just a hash. The bnamep is never derefference
+ char *vnamep = (char *)var_name;
+
+ LLMsgBlkData *msg_block_data = mCurrentRMessageData->mMemberBlocks[bnamep];
+
+ char errmsg[1024];
+ if (!msg_block_data)
+ {
+ sprintf(errmsg, "Block %s #%d not in message %s", block_name, blocknum, mCurrentRMessageData->mName);
+ llerrs << errmsg << llendl;
+ }
+
+ LLMsgVarData vardata = msg_block_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ sprintf(errmsg, "Variable %s not in message %s block %s", vnamep, mCurrentRMessageData->mName, bnamep);
+ llerrs << errmsg << llendl;
+ }
+
+ const S32 vardata_size = vardata.getSize();
+ if( vardata_size )
+ {
+ for( i = 0; i < vardata_size; i++ )
+ {
+ byte_count++;
+ llinfos << block_name << " " << var_name << " [" << blocknum << "][" << i << "]= " << (U32)(((U8*)vardata.getData())[i]) << llendl;
+ }
+ }
+ else
+ {
+ llinfos << block_name << " " << var_name << " [" << blocknum << "] 0 bytes" << llendl;
+ }
+ }
+ }
+ }
+ llinfos << "Byte count =" << byte_count << llendl;
+ }
+ }
+ */
+
+ mPacketsIn++;
+ mBytesIn += mTrueReceiveSize;
+
+ // ACK here for valid packets that we've seen
+ // for the first time.
+ if (cdp && recv_reliable)
+ {
+ // Add to the recently received list for duplicate suppression
+ cdp->mRecentlyReceivedReliablePackets[mCurrentRecvPacketID] = getMessageTimeUsecs();
+
+ // Put it onto the list of packets to be acked
+ cdp->collectRAck(mCurrentRecvPacketID);
+ mReliablePacketsIn++;
+ }
+ }
+ else
+ {
+ if (mbProtected && (!cdp))
+ {
+ llwarns << "Packet "
+ << (mCurrentRMessageTemplate ? mCurrentRMessageTemplate->mName : "")
+ << " from invalid circuit " << host << llendl;
+ mOffCircuitPackets++;
+ }
+ else
+ {
+ mInvalidOnCircuitPackets++;
+ }
+ }
+
+ // Code for dumping the complete contents of a message
+ // delete [] zero_unexpanded_buffer;
+ }
+ } while (!valid_packet && mReceiveSize > 0);
+
+ F64 mt_sec = getMessageTimeSeconds();
+ // Check to see if we need to print debug info
+ if ((mt_sec - mCircuitPrintTime) > mCircuitPrintFreq)
+ {
+ dumpCircuitInfo();
+ mCircuitPrintTime = mt_sec;
+ }
+
+ if( !valid_packet )
+ {
+ clearReceiveState();
+ }
+
+ return valid_packet;
+}
+
+S32 LLMessageSystem::getReceiveBytes() const
+{
+ if (getReceiveCompressedSize())
+ {
+ return getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ return getReceiveSize() * 8;
+ }
+}
+
+
+void LLMessageSystem::processAcks()
+{
+ F64 mt_sec = getMessageTimeSeconds();
+ {
+ gTransferManager.updateTransfers();
+
+ if (gXferManager)
+ {
+ gXferManager->retransmitUnackedPackets();
+ }
+
+ if (gAssetStorage)
+ {
+ gAssetStorage->checkForTimeouts();
+ }
+ }
+
+ BOOL dump = FALSE;
+ {
+ // Check the status of circuits
+ mCircuitInfo.updateWatchDogTimers(this);
+
+ //resend any necessary packets
+ mCircuitInfo.resendUnackedPackets(mUnackedListDepth, mUnackedListSize);
+
+ //cycle through ack list for each host we need to send acks to
+ mCircuitInfo.sendAcks();
+
+ if (!mDenyTrustedCircuitSet.empty())
+ {
+ llinfos << "Sending queued DenyTrustedCircuit messages." << llendl;
+ for (host_set_t::iterator hostit = mDenyTrustedCircuitSet.begin(); hostit != mDenyTrustedCircuitSet.end(); ++hostit)
+ {
+ reallySendDenyTrustedCircuit(*hostit);
+ }
+ mDenyTrustedCircuitSet.clear();
+ }
+
+ if (mMaxMessageCounts >= 0)
+ {
+ if (mNumMessageCounts >= mMaxMessageCounts)
+ {
+ dump = TRUE;
+ }
+ }
+
+ if (mMaxMessageTime >= 0.f)
+ {
+ // This is one of the only places where we're required to get REAL message system time.
+ mReceiveTime = (F32)(getMessageTimeSeconds(TRUE) - mMessageCountTime);
+ if (mReceiveTime > mMaxMessageTime)
+ {
+ dump = TRUE;
+ }
+ }
+ }
+
+ if (dump)
+ {
+ dumpReceiveCounts();
+ }
+ resetReceiveCounts();
+
+ if ((mt_sec - mResendDumpTime) > CIRCUIT_DUMP_TIMEOUT)
+ {
+ mResendDumpTime = mt_sec;
+ mCircuitInfo.dumpResends();
+ }
+}
+
+
+void LLMessageSystem::newMessageFast(const char *name)
+{
+ mbSBuilt = FALSE;
+ mbSClear = FALSE;
+
+ mCurrentSendTotal = 0;
+ mSendReliable = FALSE;
+
+ char *namep = (char *)name;
+
+ if (mMessageTemplates.count(namep) > 0)
+ {
+ mCurrentSMessageTemplate = mMessageTemplates[namep];
+ if (mCurrentSMessageData)
+ {
+ delete mCurrentSMessageData;
+ }
+ mCurrentSMessageData = new LLMsgData(namep);
+ mCurrentSMessageName = namep;
+ mCurrentSDataBlock = NULL;
+ mCurrentSBlockName = NULL;
+
+ // add at one of each block
+ LLMessageTemplate* msg_template = mMessageTemplates[namep];
+ for (LLMessageTemplate::message_block_map_t::iterator iter = msg_template->mMemberBlocks.begin();
+ iter != msg_template->mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* ci = iter->second;
+ LLMsgBlkData *tblockp;
+ tblockp = new LLMsgBlkData(ci->mName, 0);
+ mCurrentSMessageData->addBlock(tblockp);
+ }
+ }
+ else
+ {
+ llerrs << "newMessage - Message " << name << " not registered" << llendl;
+ }
+}
+
+void LLMessageSystem::copyMessageRtoS()
+{
+ if (!mCurrentRMessageTemplate)
+ {
+ return;
+ }
+ newMessageFast(mCurrentRMessageTemplate->mName);
+
+ // copy the blocks
+ // counting variables used to encode multiple block info
+ S32 block_count = 0;
+ char *block_name = NULL;
+
+ // loop through msg blocks to loop through variables, totalling up size data and filling the new (send) message
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.begin();
+ LLMsgData::msg_blk_data_map_t::iterator end = mCurrentRMessageData->mMemberBlocks.end();
+ for(; iter != end; ++iter)
+ {
+ LLMsgBlkData* mbci = iter->second;
+ if(!mbci) continue;
+
+ // do we need to encode a block code?
+ if (block_count == 0)
+ {
+ block_count = mbci->mBlockNumber;
+ block_name = (char *)mbci->mName;
+ }
+
+ // counting down mutliple blocks
+ block_count--;
+
+ nextBlockFast(block_name);
+
+ // now loop through the variables
+ LLMsgBlkData::msg_var_data_map_t::iterator dit = mbci->mMemberVarData.begin();
+ LLMsgBlkData::msg_var_data_map_t::iterator dend = mbci->mMemberVarData.end();
+
+ for(; dit != dend; ++dit)
+ {
+ LLMsgVarData& mvci = *dit;
+ addDataFast(mvci.getName(), mvci.getData(), mvci.getType(), mvci.getSize());
+ }
+ }
+}
+
+void LLMessageSystem::clearMessage()
+{
+ mbSBuilt = FALSE;
+ mbSClear = TRUE;
+
+ mCurrentSendTotal = 0;
+ mSendReliable = FALSE;
+
+ mCurrentSMessageTemplate = NULL;
+
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ mCurrentSMessageName = NULL;
+ mCurrentSDataBlock = NULL;
+ mCurrentSBlockName = NULL;
+}
+
+
+// set block to add data to within current message
+void LLMessageSystem::nextBlockFast(const char *blockname)
+{
+ char *bnamep = (char *)blockname;
+
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to setBlock" << llendl;
+ return;
+ }
+
+ // now, does this block exist?
+ LLMessageTemplate::message_block_map_t::iterator temp_iter = mCurrentSMessageTemplate->mMemberBlocks.find(bnamep);
+ if (temp_iter == mCurrentSMessageTemplate->mMemberBlocks.end())
+ {
+ llerrs << "LLMessageSystem::nextBlockFast " << bnamep
+ << " not a block in " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ LLMessageBlock* template_data = temp_iter->second;
+
+ // ok, have we already set this block?
+ LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[bnamep];
+ if (block_data->mBlockNumber == 0)
+ {
+ // nope! set this as the current block
+ block_data->mBlockNumber = 1;
+ mCurrentSDataBlock = block_data;
+ mCurrentSBlockName = bnamep;
+
+ // add placeholders for each of the variables
+ for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin();
+ iter != template_data->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSDataBlock->addVariable(ci.getName(), ci.getType());
+ }
+ return;
+ }
+ else
+ {
+ // already have this block. . .
+ // are we supposed to have a new one?
+
+ // if the block is type MBT_SINGLE this is bad!
+ if (template_data->mType == MBT_SINGLE)
+ {
+ llerrs << "LLMessageSystem::nextBlockFast called multiple times"
+ << " for " << bnamep << " but is type MBT_SINGLE" << llendl;
+ return;
+ }
+
+
+ // if the block is type MBT_MULTIPLE then we need a known number, make sure that we're not exceeding it
+ if ( (template_data->mType == MBT_MULTIPLE)
+ &&(mCurrentSDataBlock->mBlockNumber == template_data->mNumber))
+ {
+ llerrs << "LLMessageSystem::nextBlockFast called "
+ << mCurrentSDataBlock->mBlockNumber << " times for " << bnamep
+ << " exceeding " << template_data->mNumber
+ << " specified in type MBT_MULTIPLE." << llendl;
+ return;
+ }
+
+ // ok, we can make a new one
+ // modify the name to avoid name collision by adding number to end
+ S32 count = block_data->mBlockNumber;
+
+ // incrememt base name's count
+ block_data->mBlockNumber++;
+
+ if (block_data->mBlockNumber > MAX_BLOCKS)
+ {
+ llerrs << "Trying to pack too many blocks into MBT_VARIABLE type (limited to " << MAX_BLOCKS << ")" << llendl;
+ }
+
+ // create new name
+ // Nota Bene: if things are working correctly, mCurrentMessageData->mMemberBlocks[blockname]->mBlockNumber == mCurrentDataBlock->mBlockNumber + 1
+
+ char *nbnamep = bnamep + count;
+
+ mCurrentSDataBlock = new LLMsgBlkData(bnamep, count);
+ mCurrentSDataBlock->mName = nbnamep;
+ mCurrentSMessageData->mMemberBlocks[nbnamep] = mCurrentSDataBlock;
+
+ // add placeholders for each of the variables
+ for (LLMessageBlock::message_variable_map_t::iterator
+ iter = template_data->mMemberVariables.begin(),
+ end = template_data->mMemberVariables.end();
+ iter != end; iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSDataBlock->addVariable(ci.getName(), ci.getType());
+ }
+ return;
+ }
+}
+
+// add data to variable in current block
+void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type, S32 size)
+{
+ char *vnamep = (char *)varname;
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to addData" << llendl;
+ return;
+ }
+
+ // do we have a current block?
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "setBlock not called prior to addData" << llendl;
+ return;
+ }
+
+ // kewl, add the data if it exists
+ LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep];
+ if (!var_data || !var_data->getName())
+ {
+ llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ // ok, it seems ok. . . are we the correct size?
+ if (var_data->getType() == MVT_VARIABLE)
+ {
+ // Variable 1 can only store 255 bytes, make sure our data is smaller
+ if ((var_data->getSize() == 1) &&
+ (size > 255))
+ {
+ llwarns << "Field " << varname << " is a Variable 1 but program "
+ << "attempted to stuff more than 255 bytes in "
+ << "(" << size << "). Clamping size and truncating data." << llendl;
+ size = 255;
+ char *truncate = (char *)data;
+ truncate[255] = 0;
+ }
+
+ // no correct size for MVT_VARIABLE, instead we need to tell how many bytes the size will be encoded as
+ mCurrentSDataBlock->addData(vnamep, data, size, type, var_data->getSize());
+ mCurrentSendTotal += size;
+ }
+ else
+ {
+ if (size != var_data->getSize())
+ {
+ llerrs << varname << " is type MVT_FIXED but request size " << size << " doesn't match template size "
+ << var_data->getSize() << llendl;
+ return;
+ }
+ // alright, smash it in
+ mCurrentSDataBlock->addData(vnamep, data, size, type);
+ mCurrentSendTotal += size;
+ }
+}
+
+// add data to variable in current block - fails if variable isn't MVT_FIXED
+void LLMessageSystem::addDataFast(const char *varname, const void *data, EMsgVariableType type)
+{
+ char *vnamep = (char *)varname;
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to addData" << llendl;
+ return;
+ }
+
+ // do we have a current block?
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "setBlock not called prior to addData" << llendl;
+ return;
+ }
+
+ // kewl, add the data if it exists
+ LLMessageVariable* var_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName]->mMemberVariables[vnamep];
+ if (!var_data->getName())
+ {
+ llerrs << vnamep << " not a variable in block " << mCurrentSBlockName << " of " << mCurrentSMessageTemplate->mName << llendl;
+ return;
+ }
+
+ // ok, it seems ok. . . are we MVT_VARIABLE?
+ if (var_data->getType() == MVT_VARIABLE)
+ {
+ // nope
+ llerrs << vnamep << " is type MVT_VARIABLE. Call using addData(name, data, size)" << llendl;
+ return;
+ }
+ else
+ {
+ mCurrentSDataBlock->addData(vnamep, data, var_data->getSize(), type);
+ mCurrentSendTotal += var_data->getSize();
+ }
+}
+
+BOOL LLMessageSystem::isSendFull(const char* blockname)
+{
+ if(!blockname)
+ {
+ return (mCurrentSendTotal > MTUBYTES);
+ }
+ return isSendFullFast(gMessageStringTable.getString(blockname));
+}
+
+BOOL LLMessageSystem::isSendFullFast(const char* blockname)
+{
+ if(mCurrentSendTotal > MTUBYTES)
+ {
+ return TRUE;
+ }
+ if(!blockname)
+ {
+ return FALSE;
+ }
+ char* bnamep = (char*)blockname;
+ S32 max;
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[bnamep];
+
+ switch(template_data->mType)
+ {
+ case MBT_SINGLE:
+ max = 1;
+ break;
+ case MBT_MULTIPLE:
+ max = template_data->mNumber;
+ break;
+ case MBT_VARIABLE:
+ default:
+ max = MAX_BLOCKS;
+ break;
+ }
+ if(mCurrentSMessageData->mMemberBlocks[bnamep]->mBlockNumber >= max)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// blow away the last block of a message, return FALSE if that leaves no blocks or there wasn't a block to remove
+BOOL LLMessageSystem::removeLastBlock()
+{
+ if (mCurrentSBlockName)
+ {
+ if ( (mCurrentSMessageData)
+ &&(mCurrentSMessageTemplate))
+ {
+ if (mCurrentSMessageData->mMemberBlocks[mCurrentSBlockName]->mBlockNumber >= 1)
+ {
+ // At least one block for the current block name.
+
+ // Store the current block name for future reference.
+ char *block_name = mCurrentSBlockName;
+
+ // Decrement the sent total by the size of the
+ // data in the message block that we're currently building.
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mCurrentSBlockName];
+
+ for (LLMessageBlock::message_variable_map_t::iterator iter = template_data->mMemberVariables.begin();
+ iter != template_data->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& ci = *(iter->second);
+ mCurrentSendTotal -= ci.getSize();
+ }
+
+
+ // Now we want to find the block that we're blowing away.
+
+ // Get the number of blocks.
+ LLMsgBlkData* block_data = mCurrentSMessageData->mMemberBlocks[block_name];
+ S32 num_blocks = block_data->mBlockNumber;
+
+ // Use the same (suspect?) algorithm that's used to generate
+ // the names in the nextBlock method to find it.
+ char *block_getting_whacked = block_name + num_blocks - 1;
+ LLMsgBlkData* whacked_data = mCurrentSMessageData->mMemberBlocks[block_getting_whacked];
+ delete whacked_data;
+ mCurrentSMessageData->mMemberBlocks.erase(block_getting_whacked);
+
+ if (num_blocks <= 1)
+ {
+ // we just blew away the last one, so return FALSE
+ return FALSE;
+ }
+ else
+ {
+ // Decrement the counter.
+ block_data->mBlockNumber--;
+ return TRUE;
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+// make sure that all the desired data is in place and then copy the data into mSendBuffer
+void LLMessageSystem::buildMessage()
+{
+ // basic algorithm is to loop through the various pieces, building
+ // size and offset info if we encounter a -1 for mSize at any
+ // point that variable wasn't given data
+
+ // do we have a current message?
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "newMessage not called prior to buildMessage" << llendl;
+ return;
+ }
+
+ // zero out some useful values
+
+ // leave room for circuit counter
+ mSendSize = LL_PACKET_ID_SIZE;
+
+ // encode message number and adjust total_offset
+ if (mCurrentSMessageTemplate->mFrequency == MFT_HIGH)
+ {
+// old, endian-dependant way
+// memcpy(&mSendBuffer[mSendSize], &mCurrentMessageTemplate->mMessageNumber, sizeof(U8));
+
+// new, independant way
+ mSendBuffer[mSendSize] = (U8)mCurrentSMessageTemplate->mMessageNumber;
+ mSendSize += sizeof(U8);
+ }
+ else if (mCurrentSMessageTemplate->mFrequency == MFT_MEDIUM)
+ {
+ U8 temp = 255;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+
+ // mask off unsightly bits
+ temp = mCurrentSMessageTemplate->mMessageNumber & 255;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+ }
+ else if (mCurrentSMessageTemplate->mFrequency == MFT_LOW)
+ {
+ U8 temp = 255;
+ U16 message_num;
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+ memcpy(&mSendBuffer[mSendSize], &temp, sizeof(U8)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U8);
+
+ // mask off unsightly bits
+ message_num = mCurrentSMessageTemplate->mMessageNumber & 0xFFFF;
+
+ // convert to network byte order
+ message_num = htons(message_num);
+ memcpy(&mSendBuffer[mSendSize], &message_num, sizeof(U16)); /*Flawfinder: ignore*/
+ mSendSize += sizeof(U16);
+ }
+ else
+ {
+ llerrs << "unexpected message frequency in buildMessage" << llendl;
+ return;
+ }
+
+ // counting variables used to encode multiple block info
+ S32 block_count = 0;
+ U8 temp_block_number;
+
+ // loop through msg blocks to loop through variables, totalling up size data and copying into mSendBuffer
+ for (LLMsgData::msg_blk_data_map_t::iterator
+ iter = mCurrentSMessageData->mMemberBlocks.begin(),
+ end = mCurrentSMessageData->mMemberBlocks.end();
+ iter != end; iter++)
+ {
+ LLMsgBlkData* mbci = iter->second;
+ // do we need to encode a block code?
+ if (block_count == 0)
+ {
+ block_count = mbci->mBlockNumber;
+
+ LLMessageBlock* template_data = mCurrentSMessageTemplate->mMemberBlocks[mbci->mName];
+
+ // ok, if this is the first block of a repeating pack, set block_count and, if it's type MBT_VARIABLE encode a byte for how many there are
+ if (template_data->mType == MBT_VARIABLE)
+ {
+ // remember that mBlockNumber is a S32
+ temp_block_number = (U8)mbci->mBlockNumber;
+ if ((S32)(mSendSize + sizeof(U8)) < MAX_BUFFER_SIZE)
+ {
+ memcpy(&mSendBuffer[mSendSize], &temp_block_number, sizeof(U8));
+ mSendSize += sizeof(U8);
+ }
+ else
+ {
+ // Just reporting error is likely not enough. Need
+ // to check how to abort or error out gracefully
+ // from this function. XXXTBD
+ llerrs << "buildMessage failed. Message excedding"
+ " sendBuffersize." << llendl;
+ }
+ }
+ else if (template_data->mType == MBT_MULTIPLE)
+ {
+ if (block_count != template_data->mNumber)
+ {
+ // nope! need to fill it in all the way!
+ llerrs << "Block " << mbci->mName
+ << " is type MBT_MULTIPLE but only has data for "
+ << block_count << " out of its "
+ << template_data->mNumber << " blocks" << llendl;
+ }
+ }
+ }
+
+ // counting down multiple blocks
+ block_count--;
+
+ // now loop through the variables
+ for (LLMsgBlkData::msg_var_data_map_t::iterator iter = mbci->mMemberVarData.begin();
+ iter != mbci->mMemberVarData.end(); iter++)
+ {
+ LLMsgVarData& mvci = *iter;
+ if (mvci.getSize() == -1)
+ {
+ // oops, this variable wasn't ever set!
+ llerrs << "The variable " << mvci.getName() << " in block "
+ << mbci->mName << " of message "
+ << mCurrentSMessageData->mName
+ << " wasn't set prior to buildMessage call" << llendl;
+ }
+ else
+ {
+ S32 data_size = mvci.getDataSize();
+ if(data_size > 0)
+ {
+ // The type is MVT_VARIABLE, which means that we
+ // need to encode a size argument. Otherwise,
+ // there is no need.
+ S32 size = mvci.getSize();
+ U8 sizeb;
+ U16 sizeh;
+ switch(data_size)
+ {
+ case 1:
+ sizeb = size;
+ htonmemcpy(&mSendBuffer[mSendSize], &sizeb, MVT_U8, 1);
+ break;
+ case 2:
+ sizeh = size;
+ htonmemcpy(&mSendBuffer[mSendSize], &sizeh, MVT_U16, 2);
+ break;
+ case 4:
+ htonmemcpy(&mSendBuffer[mSendSize], &size, MVT_S32, 4);
+ break;
+ default:
+ llerrs << "Attempting to build variable field with unknown size of " << size << llendl;
+ break;
+ }
+ mSendSize += mvci.getDataSize();
+ }
+
+ // if there is any data to pack, pack it
+ if((mvci.getData() != NULL) && mvci.getSize())
+ {
+ if(mSendSize + mvci.getSize() < (S32)sizeof(mSendBuffer))
+ {
+ memcpy(
+ &mSendBuffer[mSendSize],
+ mvci.getData(),
+ mvci.getSize());
+ mSendSize += mvci.getSize();
+ }
+ else
+ {
+ // Just reporting error is likely not
+ // enough. Need to check how to abort or error
+ // out gracefully from this function. XXXTBD
+ llerrs << "LLMessageSystem::buildMessage failed. "
+ << "Attempted to pack "
+ << mSendSize + mvci.getSize()
+ << " bytes into a buffer with size "
+ << mSendBuffer << "." << llendl
+ }
+ }
+ }
+ }
+ }
+ mbSBuilt = TRUE;
+}
+
+S32 LLMessageSystem::sendReliable(const LLHost &host)
+{
+ return sendReliable(host, LL_DEFAULT_RELIABLE_RETRIES, TRUE, LL_PING_BASED_TIMEOUT_DUMMY, NULL, NULL);
+}
+
+
+S32 LLMessageSystem::sendSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
+{
+ F32 timeout;
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
+ LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
+ }
+
+ return sendReliable(host, 0, FALSE, timeout, callback, callback_data);
+}
+
+// send the message via a UDP packet
+S32 LLMessageSystem::sendReliable( const LLHost &host,
+ S32 retries,
+ BOOL ping_based_timeout,
+ F32 timeout,
+ void (*callback)(void **,S32),
+ void ** callback_data)
+{
+ if (ping_based_timeout)
+ {
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = llmax(LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS, LL_RELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX);
+ }
+ }
+
+ mSendReliable = TRUE;
+ mReliablePacketParams.set(host, retries, ping_based_timeout, timeout,
+ callback, callback_data, mCurrentSMessageName);
+ return sendMessage(host);
+}
+
+void LLMessageSystem::forwardMessage(const LLHost &host)
+{
+ copyMessageRtoS();
+ sendMessage(host);
+}
+
+void LLMessageSystem::forwardReliable(const LLHost &host)
+{
+ copyMessageRtoS();
+ sendReliable(host);
+}
+
+void LLMessageSystem::forwardReliable(const U32 circuit_code)
+{
+ copyMessageRtoS();
+ sendReliable(findHost(circuit_code));
+}
+
+S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data)
+{
+ F32 timeout;
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ timeout = llmax(LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS,
+ LL_SEMIRELIABLE_TIMEOUT_FACTOR * cdp->getPingDelayAveraged());
+ }
+ else
+ {
+ timeout = LL_SEMIRELIABLE_TIMEOUT_FACTOR * LL_AVERAGED_PING_MAX;
+ }
+
+ S32 send_bytes = 0;
+ if (mCurrentSendTotal)
+ {
+ mSendReliable = TRUE;
+ // No need for ping-based retry as not going to retry
+ mReliablePacketParams.set(host, 0, FALSE, timeout, callback, callback_data, mCurrentSMessageName);
+ send_bytes = sendMessage(host);
+ clearMessage();
+ }
+ else
+ {
+ delete callback_data;
+ }
+ return send_bytes;
+}
+
+S32 LLMessageSystem::flushReliable(const LLHost &host)
+{
+ S32 send_bytes = 0;
+ if (mCurrentSendTotal)
+ {
+ send_bytes = sendReliable(host);
+ }
+ clearMessage();
+ return send_bytes;
+}
+
+
+// This can be called from signal handlers,
+// so should should not use llinfos.
+S32 LLMessageSystem::sendMessage(const LLHost &host)
+{
+ if (!mbSBuilt)
+ {
+ buildMessage();
+ }
+
+ mCurrentSendTotal = 0;
+
+ if (!(host.isOk())) // if port and ip are zero, don't bother trying to send the message
+ {
+ return 0;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ // this is a new circuit!
+ // are we protected?
+ if (mbProtected)
+ {
+ // yup! don't send packets to an unknown circuit
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tUNKNOWN CIRCUIT:\t"
+ << mCurrentSMessageName << llendl;
+ }
+ llwarns << "sendMessage - Trying to send "
+ << mCurrentSMessageName << " on unknown circuit "
+ << host << llendl;
+ return 0;
+ }
+ else
+ {
+ // nope, open the new circuit
+ cdp = mCircuitInfo.addCircuitData(host, 0);
+ }
+ }
+ else
+ {
+ // this is an old circuit. . . is it still alive?
+ if (!cdp->isAlive())
+ {
+ // nope. don't send to dead circuits
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tDEAD CIRCUIT\t\t"
+ << mCurrentSMessageName << llendl;
+ }
+ llwarns << "sendMessage - Trying to send message "
+ << mCurrentSMessageName << " to dead circuit "
+ << host << llendl;
+ return 0;
+ }
+ }
+
+ memset(mSendBuffer,0,LL_PACKET_ID_SIZE); // zero out the packet ID field
+
+ // add the send id to the front of the message
+ cdp->nextPacketOutID();
+
+ // Packet ID size is always 4
+ *((S32*)&mSendBuffer[0]) = htonl(cdp->getPacketOutID());
+
+ // Compress the message, which will usually reduce its size.
+ U8 * buf_ptr = (U8 *)mSendBuffer;
+ S32 buffer_length = mSendSize;
+ if(ME_ZEROCODED == mCurrentSMessageTemplate->getEncoding())
+ {
+ zeroCode(&buf_ptr, &buffer_length);
+ }
+
+ if (buffer_length > 1500)
+ {
+ if((mCurrentSMessageName != _PREHASH_ChildAgentUpdate)
+ && (mCurrentSMessageName != _PREHASH_SendXferPacket))
+ {
+ llwarns << "sendMessage - Trying to send "
+ << ((buffer_length > 4000) ? "EXTRA " : "")
+ << "BIG message " << mCurrentSMessageName << " - "
+ << buffer_length << llendl;
+ }
+ }
+ if (mSendReliable)
+ {
+ buf_ptr[0] |= LL_RELIABLE_FLAG;
+
+ if (!cdp->getUnackedPacketCount())
+ {
+ // We are adding the first packed onto the unacked packet list(s)
+ // Add this circuit to the list of circuits with unacked packets
+ mCircuitInfo.mUnackedCircuitMap[cdp->mHost] = cdp;
+ }
+
+ cdp->addReliablePacket(mSocket,buf_ptr,buffer_length, &mReliablePacketParams);
+ mReliablePacketsOut++;
+ }
+
+ // tack packet acks onto the end of this message
+ S32 space_left = (MTUBYTES - buffer_length) / sizeof(TPACKETID); // space left for packet ids
+ S32 ack_count = (S32)cdp->mAcks.size();
+ BOOL is_ack_appended = FALSE;
+ std::vector<TPACKETID> acks;
+ if((space_left > 0) && (ack_count > 0) &&
+ (mCurrentSMessageName != _PREHASH_PacketAck))
+ {
+ buf_ptr[0] |= LL_ACK_FLAG;
+ S32 append_ack_count = llmin(space_left, ack_count);
+ const S32 MAX_ACKS = 250;
+ append_ack_count = llmin(append_ack_count, MAX_ACKS);
+ std::vector<TPACKETID>::iterator iter = cdp->mAcks.begin();
+ std::vector<TPACKETID>::iterator last = cdp->mAcks.begin();
+ last += append_ack_count;
+ TPACKETID packet_id;
+ for( ; iter != last ; ++iter)
+ {
+ // grab the next packet id.
+ packet_id = (*iter);
+ if(mVerboseLog)
+ {
+ acks.push_back(packet_id);
+ }
+
+ // put it on the end of the buffer
+ packet_id = htonl(packet_id);
+
+ if((S32)(buffer_length + sizeof(TPACKETID)) < MAX_BUFFER_SIZE)
+ {
+ memcpy(&buf_ptr[buffer_length], &packet_id, sizeof(TPACKETID));
+ // Do the accounting
+ buffer_length += sizeof(TPACKETID);
+ }
+ else
+ {
+ // Just reporting error is likely not enough. Need to
+ // check how to abort or error out gracefully from
+ // this function. XXXTBD
+ // *NOTE: Actually hitting this error would indicate
+ // the calculation above for space_left, ack_count,
+ // append_acout_count is incorrect or that
+ // MAX_BUFFER_SIZE has fallen below MTU which is bad
+ // and probably programmer error.
+ llerrs << "Buffer packing failed due to size.." << llendl;
+ }
+ }
+
+ // clean up the source
+ cdp->mAcks.erase(cdp->mAcks.begin(), last);
+
+ // tack the count in the final byte
+ U8 count = (U8)append_ack_count;
+ buf_ptr[buffer_length++] = count;
+ is_ack_appended = TRUE;
+ }
+
+ BOOL success;
+ success = mPacketRing.sendPacket(mSocket, (char *)buf_ptr, buffer_length, host);
+
+ if (!success)
+ {
+ mSendPacketFailureCount++;
+ }
+ else
+ {
+ // mCircuitInfo already points to the correct circuit data
+ cdp->addBytesOut( buffer_length );
+ }
+
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: -> " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mSendSize, buffer_length, cdp->getPacketOutID()); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentSMessageTemplate->mName
+ << (mSendReliable ? " reliable " : "");
+ if(is_ack_appended)
+ {
+ str << "\tACKS:\t";
+ std::ostream_iterator<TPACKETID> append(str, " ");
+ std::copy(acks.begin(), acks.end(), append);
+ }
+ llinfos << str.str() << llendl;
+ }
+
+ lldebugst(LLERR_MESSAGE) << "MessageSent at: " << (S32)totalTime()
+ << ", " << mCurrentSMessageTemplate->mName
+ << " to " << host
+ << llendl;
+
+ // ok, clean up temp data
+ delete mCurrentSMessageData;
+ mCurrentSMessageData = NULL;
+
+ mPacketsOut++;
+ mBytesOut += buffer_length;
+
+ return buffer_length;
+}
+
+
+// Returns template for the message contained in buffer
+BOOL LLMessageSystem::decodeTemplate(
+ const U8* buffer, S32 buffer_size, // inputs
+ LLMessageTemplate** msg_template ) // outputs
+{
+ const U8* header = buffer + LL_PACKET_ID_SIZE;
+
+ // is there a message ready to go?
+ if (buffer_size <= 0)
+ {
+ llwarns << "No message waiting for decode!" << llendl;
+ return(FALSE);
+ }
+
+ U32 num = 0;
+
+ if (header[0] != 255)
+ {
+ // high frequency message
+ num = header[0];
+ }
+ else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 1)) && (header[1] != 255))
+ {
+ // medium frequency message
+ num = (255 << 8) | header[1];
+ }
+ else if ((buffer_size >= ((S32) LL_MINIMUM_VALID_PACKET_SIZE + 3)) && (header[1] == 255))
+ {
+ // low frequency message
+ U16 message_id_U16 = 0;
+ // I think this check busts the message system.
+ // it appears that if there is a NULL in the message #, it won't copy it....
+ // what was the goal?
+ //if(header[2])
+ memcpy(&message_id_U16, &header[2], 2);
+
+ // dependant on endian-ness:
+ // U32 temp = (255 << 24) | (255 << 16) | header[2];
+
+ // independant of endian-ness:
+ message_id_U16 = ntohs(message_id_U16);
+ num = 0xFFFF0000 | message_id_U16;
+ }
+ else // bogus packet received (too short)
+ {
+ llwarns << "Packet with unusable length received (too short): "
+ << buffer_size << llendl;
+ return(FALSE);
+ }
+
+ LLMessageTemplate* temp = get_ptr_in_map(mMessageNumbers,num);
+ if (temp)
+ {
+ *msg_template = temp;
+ }
+ else
+ {
+ llwarns << "Message #" << std::hex << num << std::dec
+ << " received but not registered!" << llendl;
+ callExceptionFunc(MX_UNREGISTERED_MESSAGE);
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+void LLMessageSystem::logMsgFromInvalidCircuit( const LLHost& host, BOOL recv_reliable )
+{
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize: mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentRMessageTemplate->mName
+ << (recv_reliable ? " reliable" : "")
+ << " REJECTED";
+ llinfos << str.str() << llendl;
+ }
+ // nope!
+ // cout << "Rejecting unexpected message " << mCurrentMessageTemplate->mName << " from " << hex << ip << " , " << dec << port << endl;
+
+ // Keep track of rejected messages as well
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = TRUE;
+ mNumMessageCounts++;
+ }
+}
+
+void LLMessageSystem::logTrustedMsgFromUntrustedCircuit( const LLHost& host )
+{
+ llwarns << "Recieved trusted message on untrusted circuit. "
+ << "Will reply with deny. "
+ << "Message: " << mCurrentRMessageTemplate->mName
+ << " Host: " << host << llendl;
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "got more than " << MAX_MESSAGE_COUNT_NUM
+ << " packets without clearing counts"
+ << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum
+ = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes
+ = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = TRUE;
+ mNumMessageCounts++;
+ }
+}
+
+void LLMessageSystem::logValidMsg(LLCircuitData *cdp, const LLHost& host, BOOL recv_reliable, BOOL recv_resent, BOOL recv_acks )
+{
+ if (mNumMessageCounts >= MAX_MESSAGE_COUNT_NUM)
+ {
+ llwarns << "Got more than " << MAX_MESSAGE_COUNT_NUM << " packets without clearing counts" << llendl;
+ }
+ else
+ {
+ mMessageCountList[mNumMessageCounts].mMessageNum = mCurrentRMessageTemplate->mMessageNumber;
+ mMessageCountList[mNumMessageCounts].mMessageBytes = mReceiveSize;
+ mMessageCountList[mNumMessageCounts].mInvalid = FALSE;
+ mNumMessageCounts++;
+ }
+
+ if (cdp)
+ {
+ // update circuit packet ID tracking (missing/out of order packets)
+ cdp->checkPacketInID( mCurrentRecvPacketID, recv_resent );
+ cdp->addBytesIn( mTrueReceiveSize );
+ }
+
+ if(mVerboseLog)
+ {
+ std::ostringstream str;
+ str << "MSG: <- " << host;
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ snprintf(buffer, MAX_STRING, "\t%6d\t%6d\t%6d ", mReceiveSize, (mIncomingCompressedSize ? mIncomingCompressedSize : mReceiveSize), mCurrentRecvPacketID); /* Flawfinder: ignore */
+ str << buffer
+ << mCurrentRMessageTemplate->mName
+ << (recv_reliable ? " reliable" : "")
+ << (recv_resent ? " resent" : "")
+ << (recv_acks ? " acks" : "");
+ llinfos << str.str() << llendl;
+ }
+}
+
+
+void LLMessageSystem::logRanOffEndOfPacket( const LLHost& host )
+{
+ // we've run off the end of the packet!
+ llwarns << "Ran off end of packet " << mCurrentRMessageTemplate->mName
+ << " with id " << mCurrentRecvPacketID << " from " << host
+ << llendl;
+ if(mVerboseLog)
+ {
+ llinfos << "MSG: -> " << host << "\tREAD PAST END:\t"
+ << mCurrentRecvPacketID << " "
+ << mCurrentSMessageTemplate->mName << llendl;
+ }
+ callExceptionFunc(MX_RAN_OFF_END_OF_PACKET);
+}
+
+
+// decode a given message
+BOOL LLMessageSystem::decodeData(const U8* buffer, const LLHost& sender )
+{
+ llassert( mReceiveSize >= 0 );
+ llassert( mCurrentRMessageTemplate);
+ llassert( !mCurrentRMessageData );
+ delete mCurrentRMessageData; // just to make sure
+
+ S32 decode_pos = LL_PACKET_ID_SIZE + (S32)(mCurrentRMessageTemplate->mFrequency);
+
+ // create base working data set
+ mCurrentRMessageData = new LLMsgData(mCurrentRMessageTemplate->mName);
+
+ // loop through the template building the data structure as we go
+ for (LLMessageTemplate::message_block_map_t::iterator iter = mCurrentRMessageTemplate->mMemberBlocks.begin();
+ iter != mCurrentRMessageTemplate->mMemberBlocks.end(); iter++)
+ {
+ LLMessageBlock* mbci = iter->second;
+ U8 repeat_number;
+ S32 i;
+
+ // how many of this block?
+
+ if (mbci->mType == MBT_SINGLE)
+ {
+ // just one
+ repeat_number = 1;
+ }
+ else if (mbci->mType == MBT_MULTIPLE)
+ {
+ // a known number
+ repeat_number = mbci->mNumber;
+ }
+ else if (mbci->mType == MBT_VARIABLE)
+ {
+ // need to read the number from the message
+ // repeat number is a single byte
+ if (decode_pos >= mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ repeat_number = buffer[decode_pos];
+ decode_pos++;
+ }
+ else
+ {
+ llerrs << "Unknown block type" << llendl;
+ return FALSE;
+ }
+
+ LLMsgBlkData* cur_data_block = NULL;
+
+ // now loop through the block
+ for (i = 0; i < repeat_number; i++)
+ {
+ if (i)
+ {
+ // build new name to prevent collisions
+ // TODO: This should really change to a vector
+ cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
+ cur_data_block->mName = mbci->mName + i;
+ }
+ else
+ {
+ cur_data_block = new LLMsgBlkData(mbci->mName, repeat_number);
+ }
+
+ // add the block to the message
+ mCurrentRMessageData->addBlock(cur_data_block);
+
+ // now read the variables
+ for (LLMessageBlock::message_variable_map_t::iterator iter = mbci->mMemberVariables.begin();
+ iter != mbci->mMemberVariables.end(); iter++)
+ {
+ LLMessageVariable& mvci = *(iter->second);
+ // ok, build out the variables
+ // add variable block
+ cur_data_block->addVariable(mvci.getName(), mvci.getType());
+
+ // what type of variable?
+ if (mvci.getType() == MVT_VARIABLE)
+ {
+ // variable, get the number of bytes to read from the template
+ S32 data_size = mvci.getSize();
+ U8 tsizeb = 0;
+ U16 tsizeh = 0;
+ U32 tsize = 0;
+
+ if ((decode_pos + data_size) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ switch(data_size)
+ {
+ case 1:
+ htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U8, 1);
+ tsize = tsizeb;
+ break;
+ case 2:
+ htonmemcpy(&tsizeh, &buffer[decode_pos], MVT_U16, 2);
+ tsize = tsizeh;
+ break;
+ case 4:
+ htonmemcpy(&tsizeb, &buffer[decode_pos], MVT_U32, 4);
+ break;
+ default:
+ llerrs << "Attempting to read variable field with unknown size of " << data_size << llendl;
+ break;
+
+ }
+ decode_pos += data_size;
+
+ if ((decode_pos + (S32)tsize) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+ cur_data_block->addData(mvci.getName(), &buffer[decode_pos], tsize, mvci.getType());
+ decode_pos += tsize;
+ }
+ else
+ {
+ // fixed!
+ // so, copy data pointer and set data size to fixed size
+
+ if ((decode_pos + mvci.getSize()) > mReceiveSize)
+ {
+ logRanOffEndOfPacket( sender );
+ return FALSE;
+ }
+
+ cur_data_block->addData(mvci.getName(), &buffer[decode_pos], mvci.getSize(), mvci.getType());
+ decode_pos += mvci.getSize();
+ }
+ }
+ }
+ }
+
+ if (mCurrentRMessageData->mMemberBlocks.empty()
+ && !mCurrentRMessageTemplate->mMemberBlocks.empty())
+ {
+ lldebugs << "Empty message '" << mCurrentRMessageTemplate->mName << "' (no blocks)" << llendl;
+ return FALSE;
+ }
+
+ {
+ static LLTimer decode_timer;
+
+ if( mTimeDecodes )
+ {
+ decode_timer.reset();
+ }
+
+ // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion )
+ // {
+ // VTResume(); // VTune
+ // }
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_MESSAGES);
+ if( !mCurrentRMessageTemplate->callHandlerFunc(this) )
+ {
+ llwarns << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << llendl;
+ }
+ }
+
+ // if( mCurrentRMessageTemplate->mName == _PREHASH_AgentToNewRegion )
+ // {
+ // VTPause(); // VTune
+ // }
+
+ if( mTimeDecodes )
+ {
+ F32 decode_time = decode_timer.getElapsedTimeF32();
+ mCurrentRMessageTemplate->mDecodeTimeThisFrame += decode_time;
+
+ mCurrentRMessageTemplate->mTotalDecoded++;
+ mCurrentRMessageTemplate->mTotalDecodeTime += decode_time;
+
+ if( mCurrentRMessageTemplate->mMaxDecodeTimePerMsg < decode_time )
+ {
+ mCurrentRMessageTemplate->mMaxDecodeTimePerMsg = decode_time;
+ }
+
+
+ if( decode_time > mTimeDecodesSpamThreshold )
+ {
+ lldebugs << "--------- Message " << mCurrentRMessageTemplate->mName << " decode took " << decode_time << " seconds. (" <<
+ mCurrentRMessageTemplate->mMaxDecodeTimePerMsg << " max, " <<
+ (mCurrentRMessageTemplate->mTotalDecodeTime / mCurrentRMessageTemplate->mTotalDecoded) << " avg)" << llendl;
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLMessageSystem::getDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum, S32 max_size)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 2!" << llendl;
+ return;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentMessageData in getData!" << llendl;
+ return;
+ }
+
+ char *bnamep = (char *)blockname + blocknum; // this works because it's just a hash. The bnamep is never derefference
+ char *vnamep = (char *)varname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << blockname << " #" << blocknum
+ << " not in message " << mCurrentRMessageData->mName << llendl;
+ return;
+ }
+
+ LLMsgBlkData *msg_block_data = iter->second;
+ LLMsgVarData& vardata = msg_block_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable "<< vnamep << " not in message "
+ << mCurrentRMessageData->mName<< " block " << bnamep << llendl;
+ return;
+ }
+
+ if (size && size != vardata.getSize())
+ {
+ llerrs << "Msg " << mCurrentRMessageData->mName
+ << " variable " << vnamep
+ << " is size " << vardata.getSize()
+ << " but copying into buffer of size " << size
+ << llendl;
+ return;
+ }
+
+
+ const S32 vardata_size = vardata.getSize();
+ if( max_size >= vardata_size )
+ {
+ switch( vardata_size )
+ {
+ case 1:
+ *((U8*)datap) = *((U8*)vardata.getData());
+ break;
+ case 2:
+ *((U16*)datap) = *((U16*)vardata.getData());
+ break;
+ case 4:
+ *((U32*)datap) = *((U32*)vardata.getData());
+ break;
+ case 8:
+ ((U32*)datap)[0] = ((U32*)vardata.getData())[0];
+ ((U32*)datap)[1] = ((U32*)vardata.getData())[1];
+ break;
+ default:
+ memcpy(datap, vardata.getData(), vardata_size);
+ break;
+ }
+ }
+ else
+ {
+ llwarns << "Msg " << mCurrentRMessageData->mName
+ << " variable " << vnamep
+ << " is size " << vardata.getSize()
+ << " but truncated to max size of " << max_size
+ << llendl;
+
+ memcpy(datap, vardata.getData(), max_size);
+ }
+}
+
+S32 LLMessageSystem::getNumberOfBlocksFast(const char *blockname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 3!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+// sprintf(errmsg, "Block %s not in message %s", bnamep, mCurrentRMessageData->mName);
+// llerrs << errmsg << llendl;
+// return -1;
+ return 0;
+ }
+
+ return (iter->second)->mBlockNumber;
+}
+
+S32 LLMessageSystem::getSizeFast(const char *blockname, const char *varname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 4!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << bnamep << " not in message "
+ << mCurrentRMessageData->mName << llendl;
+ return -1;
+ }
+
+ char *vnamep = (char *)varname;
+
+ LLMsgBlkData* msg_data = iter->second;
+ LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable " << varname << " not in message "
+ << mCurrentRMessageData->mName << " block " << bnamep << llendl;
+ return -1;
+ }
+
+ if (mCurrentRMessageTemplate->mMemberBlocks[bnamep]->mType != MBT_SINGLE)
+ {
+ llerrs << "Block " << bnamep << " isn't type MBT_SINGLE,"
+ " use getSize with blocknum argument!" << llendl;
+ return -1;
+ }
+
+ return vardata.getSize();
+}
+
+
+S32 LLMessageSystem::getSizeFast(const char *blockname, S32 blocknum, const char *varname)
+{
+ // is there a message ready to go?
+ if (mReceiveSize == -1)
+ {
+ llerrs << "No message waiting for decode 5!" << llendl;
+ return -1;
+ }
+
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "Invalid mCurrentRMessageData in getData!" << llendl;
+ return -1;
+ }
+
+ char *bnamep = (char *)blockname + blocknum;
+ char *vnamep = (char *)varname;
+
+ LLMsgData::msg_blk_data_map_t::iterator iter = mCurrentRMessageData->mMemberBlocks.find(bnamep);
+
+ if (iter == mCurrentRMessageData->mMemberBlocks.end())
+ {
+ llerrs << "Block " << bnamep << " not in message "
+ << mCurrentRMessageData->mName << llendl;
+ return -1;
+ }
+
+ LLMsgBlkData* msg_data = iter->second;
+ LLMsgVarData& vardata = msg_data->mMemberVarData[vnamep];
+
+ if (!vardata.getName())
+ {
+ llerrs << "Variable " << vnamep << " not in message "
+ << mCurrentRMessageData->mName << " block " << bnamep << llendl;
+ return -1;
+ }
+
+ return vardata.getSize();
+}
+
+
+void LLMessageSystem::sanityCheck()
+{
+ if (!mCurrentRMessageData)
+ {
+ llerrs << "mCurrentRMessageData is NULL" << llendl;
+ }
+
+ if (!mCurrentRMessageTemplate)
+ {
+ llerrs << "mCurrentRMessageTemplate is NULL" << llendl;
+ }
+
+// if (!mCurrentRTemplateBlock)
+// {
+// llerrs << "mCurrentRTemplateBlock is NULL" << llendl;
+// }
+
+// if (!mCurrentRDataBlock)
+// {
+// llerrs << "mCurrentRDataBlock is NULL" << llendl;
+// }
+
+ if (!mCurrentSMessageData)
+ {
+ llerrs << "mCurrentSMessageData is NULL" << llendl;
+ }
+
+ if (!mCurrentSMessageTemplate)
+ {
+ llerrs << "mCurrentSMessageTemplate is NULL" << llendl;
+ }
+
+// if (!mCurrentSTemplateBlock)
+// {
+// llerrs << "mCurrentSTemplateBlock is NULL" << llendl;
+// }
+
+ if (!mCurrentSDataBlock)
+ {
+ llerrs << "mCurrentSDataBlock is NULL" << llendl;
+ }
+}
+
+void LLMessageSystem::showCircuitInfo()
+{
+ llinfos << mCircuitInfo << llendl;
+}
+
+
+void LLMessageSystem::dumpCircuitInfo()
+{
+ lldebugst(LLERR_CIRCUIT_INFO) << mCircuitInfo << llendl;
+}
+
+/* virtual */
+U32 LLMessageSystem::getOurCircuitCode()
+{
+ return mOurCircuitCode;
+}
+
+LLString LLMessageSystem::getCircuitInfoString()
+{
+ LLString info_string;
+
+ info_string += mCircuitInfo.getInfoString();
+ return info_string;
+}
+
+// returns whether the given host is on a trusted circuit
+BOOL LLMessageSystem::getCircuitTrust(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->getTrusted();
+ }
+
+ return FALSE;
+}
+
+// Activate a circuit, and set its trust level (TRUE if trusted,
+// FALSE if not).
+void LLMessageSystem::enableCircuit(const LLHost &host, BOOL trusted)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ cdp = mCircuitInfo.addCircuitData(host, 0);
+ }
+ else
+ {
+ cdp->setAlive(TRUE);
+ }
+ cdp->setTrusted(trusted);
+}
+
+void LLMessageSystem::disableCircuit(const LLHost &host)
+{
+ llinfos << "LLMessageSystem::disableCircuit for " << host << llendl;
+ U32 code = gMessageSystem->findCircuitCode( host );
+
+ // Don't need to do this, as we're removing the circuit info anyway - djs 01/28/03
+
+ // don't clean up 0 circuit code entries
+ // because many hosts (neighbor sims, etc) can have the 0 circuit
+ if (code)
+ {
+ //if (mCircuitCodes.checkKey(code))
+ code_session_map_t::iterator it = mCircuitCodes.find(code);
+ if(it != mCircuitCodes.end())
+ {
+ llinfos << "Circuit " << code << " removed from list" << llendl;
+ //mCircuitCodes.removeData(code);
+ mCircuitCodes.erase(it);
+ }
+
+ U64 ip_port = 0;
+ std::map<U32, U64>::iterator iter = gMessageSystem->mCircuitCodeToIPPort.find(code);
+ if (iter != gMessageSystem->mCircuitCodeToIPPort.end())
+ {
+ ip_port = iter->second;
+
+ gMessageSystem->mCircuitCodeToIPPort.erase(iter);
+
+ U32 old_port = (U32)(ip_port & (U64)0xFFFFFFFF);
+ U32 old_ip = (U32)(ip_port >> 32);
+
+ llinfos << "Host " << LLHost(old_ip, old_port) << " circuit " << code << " removed from lookup table" << llendl;
+ gMessageSystem->mIPPortToCircuitCode.erase(ip_port);
+ }
+ }
+ else
+ {
+ // Sigh, since we can open circuits which don't have circuit
+ // codes, it's possible for this to happen...
+
+ //llwarns << "Couldn't find circuit code for " << host << llendl;
+ }
+
+ mCircuitInfo.removeCircuitData(host);
+}
+
+
+void LLMessageSystem::setCircuitAllowTimeout(const LLHost &host, BOOL allow)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ cdp->setAllowTimeout(allow);
+ }
+}
+
+void LLMessageSystem::setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost & host, void *user_data), void *user_data)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ cdp->setTimeoutCallback(callback_func, user_data);
+ }
+}
+
+
+BOOL LLMessageSystem::checkCircuitBlocked(const U32 circuit)
+{
+ LLHost host = findHost(circuit);
+
+ if (!host.isOk())
+ {
+ //llinfos << "checkCircuitBlocked: Unknown circuit " << circuit << llendl;
+ return TRUE;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isBlocked();
+ }
+ else
+ {
+ llinfos << "checkCircuitBlocked(circuit): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+BOOL LLMessageSystem::checkCircuitAlive(const U32 circuit)
+{
+ LLHost host = findHost(circuit);
+
+ if (!host.isOk())
+ {
+ //llinfos << "checkCircuitAlive: Unknown circuit " << circuit << llendl;
+ return FALSE;
+ }
+
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isAlive();
+ }
+ else
+ {
+ llinfos << "checkCircuitAlive(circuit): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+BOOL LLMessageSystem::checkCircuitAlive(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+ return cdp->isAlive();
+ }
+ else
+ {
+ //llinfos << "checkCircuitAlive(host): Unknown host - " << host << llendl;
+ return FALSE;
+ }
+}
+
+
+void LLMessageSystem::setCircuitProtection(BOOL b_protect)
+{
+ mbProtected = b_protect;
+}
+
+
+U32 LLMessageSystem::findCircuitCode(const LLHost &host)
+{
+ U64 ip64 = (U64) host.getAddress();
+ U64 port64 = (U64) host.getPort();
+ U64 ip_port = (ip64 << 32) | port64;
+
+ return get_if_there(mIPPortToCircuitCode, ip_port, U32(0));
+}
+
+LLHost LLMessageSystem::findHost(const U32 circuit_code)
+{
+ if (mCircuitCodeToIPPort.count(circuit_code) > 0)
+ {
+ return LLHost(mCircuitCodeToIPPort[circuit_code]);
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
+
+void LLMessageSystem::setMaxMessageTime(const F32 seconds)
+{
+ mMaxMessageTime = seconds;
+}
+
+void LLMessageSystem::setMaxMessageCounts(const S32 num)
+{
+ mMaxMessageCounts = num;
+}
+
+
+std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg)
+{
+ U32 i;
+ if (msg.mbError)
+ {
+ s << "Message system not correctly initialized";
+ }
+ else
+ {
+ s << "Message system open on port " << msg.mPort << " and socket " << msg.mSocket << "\n";
+// s << "Message template file " << msg.mName << " loaded\n";
+
+ s << "\nHigh frequency messages:\n";
+
+ for (i = 1; msg.mMessageNumbers[i] && (i < 255); i++)
+ {
+ s << *(msg.mMessageNumbers[i]);
+ }
+
+ s << "\nMedium frequency messages:\n";
+
+ for (i = (255 << 8) + 1; msg.mMessageNumbers[i] && (i < (255 << 8) + 255); i++)
+ {
+ s << *msg.mMessageNumbers[i];
+ }
+
+ s << "\nLow frequency messages:\n";
+
+ for (i = (0xFFFF0000) + 1; msg.mMessageNumbers[i] && (i < 0xFFFFFFFF); i++)
+ {
+ s << *msg.mMessageNumbers[i];
+ }
+ }
+ return s;
+}
+
+LLMessageSystem *gMessageSystem = NULL;
+
+// update appropriate ping info
+void process_complete_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U8 ping_id;
+ msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id);
+
+ LLCircuitData *cdp;
+ cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender());
+
+ // stop the appropriate timer
+ if (cdp)
+ {
+ cdp->pingTimerStop(ping_id);
+ }
+}
+
+void process_start_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U8 ping_id;
+ msgsystem->getU8Fast(_PREHASH_PingID, _PREHASH_PingID, ping_id);
+
+ LLCircuitData *cdp;
+ cdp = msgsystem->mCircuitInfo.findCircuit(msgsystem->getSender());
+ if (cdp)
+ {
+ // Grab the packet id of the oldest unacked packet
+ U32 packet_id;
+ msgsystem->getU32Fast(_PREHASH_PingID, _PREHASH_OldestUnacked, packet_id);
+ cdp->clearDuplicateList(packet_id);
+ }
+
+ // Send off the response
+ msgsystem->newMessageFast(_PREHASH_CompletePingCheck);
+ msgsystem->nextBlockFast(_PREHASH_PingID);
+ msgsystem->addU8(_PREHASH_PingID, ping_id);
+ msgsystem->sendMessage(msgsystem->getSender());
+}
+
+
+
+// Note: this is currently unused. --mark
+void open_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ U32 ip;
+ U16 port;
+
+ msgsystem->getIPAddrFast(_PREHASH_CircuitInfo, _PREHASH_IP, ip);
+ msgsystem->getIPPortFast(_PREHASH_CircuitInfo, _PREHASH_Port, port);
+
+ // By default, OpenCircuit's are untrusted
+ msgsystem->enableCircuit(LLHost(ip, port), FALSE);
+}
+
+void close_circuit(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ msgsystem->disableCircuit(msgsystem->getSender());
+}
+
+// static
+/*
+void LLMessageSystem::processAssignCircuitCode(LLMessageSystem* msg, void**)
+{
+ // if we already have a circuit code, we can bail
+ if(msg->mOurCircuitCode) return;
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ if(session_id != msg->getMySessionID())
+ {
+ llwarns << "AssignCircuitCode, bad session id. Expecting "
+ << msg->getMySessionID() << " but got " << session_id
+ << llendl;
+ return;
+ }
+ U32 code;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code);
+ if (!code)
+ {
+ llerrs << "Assigning circuit code of zero!" << llendl;
+ }
+
+ msg->mOurCircuitCode = code;
+ llinfos << "Circuit code " << code << " assigned." << llendl;
+}
+*/
+
+// static
+void LLMessageSystem::processAddCircuitCode(LLMessageSystem* msg, void**)
+{
+ U32 code;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, code);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ (void)msg->addCircuitCode(code, session_id);
+
+ // Send the ack back
+ //msg->newMessageFast(_PREHASH_AckAddCircuitCode);
+ //msg->nextBlockFast(_PREHASH_CircuitCode);
+ //msg->addU32Fast(_PREHASH_Code, code);
+ //msg->sendMessage(msg->getSender());
+}
+
+bool LLMessageSystem::addCircuitCode(U32 code, const LLUUID& session_id)
+{
+ if(!code)
+ {
+ llwarns << "addCircuitCode: zero circuit code" << llendl;
+ return false;
+ }
+ code_session_map_t::iterator it = mCircuitCodes.find(code);
+ if(it == mCircuitCodes.end())
+ {
+ llinfos << "New circuit code " << code << " added" << llendl;
+ //msg->mCircuitCodes[circuit_code] = circuit_code;
+
+ mCircuitCodes.insert(code_session_map_t::value_type(code, session_id));
+ }
+ else
+ {
+ llinfos << "Duplicate circuit code " << code << " added" << llendl;
+ }
+ return true;
+}
+
+//void ack_add_circuit_code(LLMessageSystem *msgsystem, void** /*user_data*/)
+//{
+ // By default, we do nothing. This particular message is only handled by the spaceserver
+//}
+
+// static
+void LLMessageSystem::processUseCircuitCode(LLMessageSystem* msg, void**)
+{
+ U32 circuit_code_in;
+ msg->getU32Fast(_PREHASH_CircuitCode, _PREHASH_Code, circuit_code_in);
+
+ U32 ip = msg->getSenderIP();
+ U32 port = msg->getSenderPort();
+
+ U64 ip64 = ip;
+ U64 port64 = port;
+ U64 ip_port_in = (ip64 << 32) | port64;
+
+ if (circuit_code_in)
+ {
+ //if (!msg->mCircuitCodes.checkKey(circuit_code_in))
+ code_session_map_t::iterator it;
+ it = msg->mCircuitCodes.find(circuit_code_in);
+ if(it == msg->mCircuitCodes.end())
+ {
+ // Whoah, abort! We don't know anything about this circuit code.
+ llwarns << "UseCircuitCode for " << circuit_code_in
+ << " received without AddCircuitCode message - aborting"
+ << llendl;
+ return;
+ }
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_ID, id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_CircuitCode, _PREHASH_SessionID, session_id);
+ if(session_id != (*it).second)
+ {
+ llwarns << "UseCircuitCode unmatched session id. Got "
+ << session_id << " but expected " << (*it).second
+ << llendl;
+ return;
+ }
+
+ // Clean up previous references to this ip/port or circuit
+ U64 ip_port_old = get_if_there(msg->mCircuitCodeToIPPort, circuit_code_in, U64(0));
+ U32 circuit_code_old = get_if_there(msg->mIPPortToCircuitCode, ip_port_in, U32(0));
+
+ if (ip_port_old)
+ {
+ if ((ip_port_old == ip_port_in) && (circuit_code_old == circuit_code_in))
+ {
+ // Current information is the same as incoming info, ignore
+ llinfos << "Got duplicate UseCircuitCode for circuit " << circuit_code_in << " to " << msg->getSender() << llendl;
+ return;
+ }
+
+ // Hmm, got a different IP and port for the same circuit code.
+ U32 circut_code_old_ip_port = get_if_there(msg->mIPPortToCircuitCode, ip_port_old, U32(0));
+ msg->mCircuitCodeToIPPort.erase(circut_code_old_ip_port);
+ msg->mIPPortToCircuitCode.erase(ip_port_old);
+ U32 old_port = (U32)(ip_port_old & (U64)0xFFFFFFFF);
+ U32 old_ip = (U32)(ip_port_old >> 32);
+ llinfos << "Removing derelict lookup entry for circuit " << circuit_code_old << " to " << LLHost(old_ip, old_port) << llendl;
+ }
+
+ if (circuit_code_old)
+ {
+ LLHost cur_host(ip, port);
+
+ llwarns << "Disabling existing circuit for " << cur_host << llendl;
+ msg->disableCircuit(cur_host);
+ if (circuit_code_old == circuit_code_in)
+ {
+ llwarns << "Asymmetrical circuit to ip/port lookup!" << llendl;
+ llwarns << "Multiple circuit codes for " << cur_host << " probably!" << llendl;
+ llwarns << "Permanently disabling circuit" << llendl;
+ return;
+ }
+ else
+ {
+ llwarns << "Circuit code changed for " << msg->getSender()
+ << " from " << circuit_code_old << " to "
+ << circuit_code_in << llendl;
+ }
+ }
+
+ // Since this comes from the viewer, it's untrusted, but it
+ // passed the circuit code and session id check, so we will go
+ // ahead and persist the ID associated.
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ BOOL had_circuit_already = cdp ? TRUE : FALSE;
+
+ msg->enableCircuit(msg->getSender(), FALSE);
+ cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if(cdp)
+ {
+ cdp->setRemoteID(id);
+ cdp->setRemoteSessionID(session_id);
+ }
+
+ if (!had_circuit_already)
+ {
+ //
+ // HACK HACK HACK HACK HACK!
+ //
+ // This would NORMALLY happen inside logValidMsg, but at the point that this happens
+ // inside logValidMsg, there's no circuit for this message yet. So the awful thing that
+ // we do here is do it inside this message handler immediately AFTER the message is
+ // handled.
+ //
+ // We COULD not do this, but then what happens is that some of the circuit bookkeeping
+ // gets broken, especially the packets in count. That causes some later packets to flush
+ // the RecentlyReceivedReliable list, resulting in an error in which UseCircuitCode
+ // doesn't get properly duplicate suppressed. Not a BIG deal, but it's somewhat confusing
+ // (and bad from a state point of view). DJS 9/23/04
+ //
+ cdp->checkPacketInID(gMessageSystem->mCurrentRecvPacketID, FALSE ); // Since this is the first message on the circuit, by definition it's not resent.
+ }
+
+ msg->mIPPortToCircuitCode[ip_port_in] = circuit_code_in;
+ msg->mCircuitCodeToIPPort[circuit_code_in] = ip_port_in;
+
+ llinfos << "Circuit code " << circuit_code_in << " from "
+ << msg->getSender() << " for agent " << id << " in session "
+ << session_id << llendl;
+ }
+ else
+ {
+ llwarns << "Got zero circuit code in use_circuit_code" << llendl;
+ }
+}
+
+
+
+static void check_for_unrecognized_messages(
+ const char* type,
+ const LLSD& map,
+ LLMessageSystem::message_template_name_map_t& templates)
+{
+ for (LLSD::map_const_iterator iter = map.beginMap(),
+ end = map.endMap();
+ iter != end; ++iter)
+ {
+ const char* name = gMessageStringTable.getString(iter->first.c_str());
+
+ if (templates.find(name) == templates.end())
+ {
+ llinfos << " " << type
+ << " ban list contains unrecognized message "
+ << name << llendl;
+ }
+ }
+}
+
+void LLMessageSystem::setMessageBans(
+ const LLSD& trusted, const LLSD& untrusted)
+{
+ llinfos << "LLMessageSystem::setMessageBans:" << llendl;
+ bool any_set = false;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; ++iter)
+ {
+ LLMessageTemplate* mt = iter->second;
+
+ std::string name(mt->mName);
+ bool ban_from_trusted
+ = trusted.has(name) && trusted.get(name).asBoolean();
+ bool ban_from_untrusted
+ = untrusted.has(name) && untrusted.get(name).asBoolean();
+
+ mt->mBanFromTrusted = ban_from_trusted;
+ mt->mBanFromUntrusted = ban_from_untrusted;
+
+ if (ban_from_trusted || ban_from_untrusted)
+ {
+ llinfos << " " << name << " banned from "
+ << (ban_from_trusted ? "TRUSTED " : " ")
+ << (ban_from_untrusted ? "UNTRUSTED " : " ")
+ << llendl;
+ any_set = true;
+ }
+ }
+
+ if (!any_set)
+ {
+ llinfos << " no messages banned" << llendl;
+ }
+
+ check_for_unrecognized_messages("trusted", trusted, mMessageTemplates);
+ check_for_unrecognized_messages("untrusted", untrusted, mMessageTemplates);
+}
+
+void process_packet_ack(LLMessageSystem *msgsystem, void** /*user_data*/)
+{
+ TPACKETID packet_id;
+
+ LLHost host = msgsystem->getSender();
+ LLCircuitData *cdp = msgsystem->mCircuitInfo.findCircuit(host);
+ if (cdp)
+ {
+
+ S32 ack_count = msgsystem->getNumberOfBlocksFast(_PREHASH_Packets);
+
+ for (S32 i = 0; i < ack_count; i++)
+ {
+ msgsystem->getU32Fast(_PREHASH_Packets, _PREHASH_ID, packet_id, i);
+// llinfos << "ack recvd' from " << host << " for packet " << (TPACKETID)packet_id << llendl;
+ cdp->ackReliablePacket(packet_id);
+ }
+ if (!cdp->getUnackedPacketCount())
+ {
+ // Remove this circuit from the list of circuits with unacked packets
+ gMessageSystem->mCircuitInfo.mUnackedCircuitMap.erase(host);
+ }
+ }
+}
+
+void send_template_reply(LLMessageSystem* msg, const LLUUID& token)
+{
+ msg->newMessageFast(_PREHASH_TemplateChecksumReply);
+ msg->nextBlockFast(_PREHASH_DataBlock);
+ msg->addU32Fast(_PREHASH_Checksum, msg->mMessageFileChecksum);
+ msg->addU8Fast(_PREHASH_MajorVersion, U8(msg->mSystemVersionMajor) );
+ msg->addU8Fast(_PREHASH_MinorVersion, U8(msg->mSystemVersionMinor) );
+ msg->addU8Fast(_PREHASH_PatchVersion, U8(msg->mSystemVersionPatch) );
+ msg->addU8Fast(_PREHASH_ServerVersion, U8(msg->mSystemVersionServer) );
+ msg->addU32Fast(_PREHASH_Flags, msg->mVersionFlags);
+ msg->nextBlockFast(_PREHASH_TokenBlock);
+ msg->addUUIDFast(_PREHASH_Token, token);
+ msg->sendMessage(msg->getSender());
+}
+
+void process_template_checksum_request(LLMessageSystem* msg, void**)
+{
+ llinfos << "Message template checksum request received from "
+ << msg->getSender() << llendl;
+ send_template_reply(msg, LLUUID::null);
+}
+
+void process_secured_template_checksum_request(LLMessageSystem* msg, void**)
+{
+ llinfos << "Secured message template checksum request received from "
+ << msg->getSender() << llendl;
+ LLUUID token;
+ msg->getUUIDFast(_PREHASH_TokenBlock, _PREHASH_Token, token);
+ send_template_reply(msg, token);
+}
+
+void process_log_control(LLMessageSystem* msg, void**)
+{
+ U8 level;
+ U32 mask;
+ BOOL time;
+ BOOL location;
+ BOOL remote_infos;
+
+ msg->getU8Fast(_PREHASH_Options, _PREHASH_Level, level);
+ msg->getU32Fast(_PREHASH_Options, _PREHASH_Mask, mask);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_Time, time);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_Location, location);
+ msg->getBOOLFast(_PREHASH_Options, _PREHASH_RemoteInfos, remote_infos);
+
+ gErrorStream.setLevel(LLErrorStream::ELevel(level));
+ gErrorStream.setDebugMask(mask);
+ gErrorStream.setTime(time);
+ gErrorStream.setPrintLocation(location);
+ gErrorStream.setElevatedRemote(remote_infos);
+
+ llinfos << "Logging set to level " << gErrorStream.getLevel()
+ << " mask " << std::hex << gErrorStream.getDebugMask() << std::dec
+ << " time " << gErrorStream.getTime()
+ << " loc " << gErrorStream.getPrintLocation()
+ << llendl;
+}
+
+void process_log_messages(LLMessageSystem* msg, void**)
+{
+ U8 log_message;
+
+ msg->getU8Fast(_PREHASH_Options, _PREHASH_Enable, log_message);
+
+ if (log_message)
+ {
+ llinfos << "Starting logging via message" << llendl;
+ msg->startLogging();
+ }
+ else
+ {
+ llinfos << "Stopping logging via message" << llendl;
+ msg->stopLogging();
+ }
+}
+
+// Make circuit trusted if the MD5 Digest matches, otherwise
+// notify remote end that they are not trusted.
+void process_create_trusted_circuit(LLMessageSystem *msg, void **)
+{
+ // don't try to create trust on machines with no shared secret
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+
+ LLUUID remote_id;
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id);
+
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if (!cdp)
+ {
+ llwarns << "Attempt to create trusted circuit without circuit data: "
+ << msg->getSender() << llendl;
+ return;
+ }
+
+ LLUUID local_id;
+ local_id = cdp->getLocalEndPointID();
+ if (remote_id == local_id)
+ {
+ // Don't respond to requests that use the same end point ID
+ return;
+ }
+
+ char their_digest[MD5HEX_STR_SIZE];
+ msg->getBinaryDataFast(_PREHASH_DataBlock, _PREHASH_Digest, their_digest, 32);
+ their_digest[MD5HEX_STR_SIZE - 1] = '\0';
+ if(msg->isMatchingDigestForWindowAndUUIDs(their_digest, TRUST_TIME_WINDOW, local_id, remote_id))
+ {
+ cdp->setTrusted(TRUE);
+ llinfos << "Trusted digest from " << msg->getSender() << llendl;
+ return;
+ }
+ else if (cdp->getTrusted())
+ {
+ // The digest is bad, but this circuit is already trusted.
+ // This means that this could just be the result of a stale deny sent from a while back, and
+ // the message system is being slow. Don't bother sending the deny, as it may continually
+ // ping-pong back and forth on a very hosed circuit.
+ llwarns << "Ignoring bad digest from known trusted circuit: " << their_digest
+ << " host: " << msg->getSender() << llendl;
+ return;
+ }
+ else
+ {
+ llwarns << "Bad digest from known circuit: " << their_digest
+ << " host: " << msg->getSender() << llendl;
+ msg->sendDenyTrustedCircuit(msg->getSender());
+ return;
+ }
+}
+
+void process_deny_trusted_circuit(LLMessageSystem *msg, void **)
+{
+ // don't try to create trust on machines with no shared secret
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+
+ LLUUID remote_id;
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_EndPointID, remote_id);
+
+ LLCircuitData *cdp = msg->mCircuitInfo.findCircuit(msg->getSender());
+ if (!cdp)
+ {
+ return;
+ }
+
+ LLUUID local_id;
+ local_id = cdp->getLocalEndPointID();
+ if (remote_id == local_id)
+ {
+ // Don't respond to requests that use the same end point ID
+ return;
+ }
+
+ // Assume that we require trust to proceed, so resend.
+ // This catches the case where a circuit that was trusted
+ // times out, and allows us to re-establish it, but does
+ // mean that if our shared_secret or clock is wrong, we'll
+ // spin.
+ // FIXME: probably should keep a count of number of resends
+ // per circuit, and stop resending after a while.
+ llinfos << "Got DenyTrustedCircuit. Sending CreateTrustedCircuit to "
+ << msg->getSender() << llendl;
+ msg->sendCreateTrustedCircuit(msg->getSender(), local_id, remote_id);
+}
+
+#define LL_ENCRYPT_BUF_LENGTH 16384
+
+void encrypt_template(const char *src_name, const char *dest_name)
+{
+ // encrypt and decrypt are symmetric
+ decrypt_template(src_name, dest_name);
+}
+
+BOOL decrypt_template(const char *src_name, const char *dest_name)
+{
+ S32 buf_length = LL_ENCRYPT_BUF_LENGTH;
+ char buf[LL_ENCRYPT_BUF_LENGTH];
+
+ FILE* infp = NULL;
+ FILE* outfp = NULL;
+ BOOL success = FALSE;
+ char* bufp = NULL;
+ U32 key = 0;
+ S32 more_data = 0;
+
+ if(src_name==NULL)
+ {
+ llwarns << "Input src_name is NULL!!" << llendl;
+ goto exit;
+ }
+
+ infp = LLFile::fopen(src_name,"rb");
+ if (!infp)
+ {
+ llwarns << "could not open " << src_name << " for reading" << llendl;
+ goto exit;
+ }
+
+ if(dest_name==NULL)
+ {
+ llwarns << "Output dest_name is NULL!!" << llendl;
+ goto exit;
+ }
+
+ outfp = LLFile::fopen(dest_name,"w+b");
+ if (!outfp)
+ {
+ llwarns << "could not open " << src_name << " for writing" << llendl;
+ goto exit;
+ }
+
+ while ((buf_length = (S32)fread(buf,1,LL_ENCRYPT_BUF_LENGTH,infp)))
+ {
+ // unscrozzle bits here
+ bufp = buf;
+ more_data = buf_length;
+ while (more_data--)
+ {
+ *bufp = *bufp ^ ((key * 43) % 256);
+ key++;
+ bufp++;
+ }
+
+ if(buf_length != (S32)fwrite(buf,1,buf_length,outfp))
+ {
+ goto exit;
+ }
+ }
+ success = TRUE;
+
+ exit:
+ if(infp) fclose(infp);
+ if(outfp) fclose(outfp);
+ return success;
+}
+
+void dump_prehash_files()
+{
+ U32 i;
+ FILE *fp = LLFile::fopen("../../indra/llmessage/message_prehash.h", "w");
+ if (fp)
+ {
+ fprintf(
+ fp,
+ "/**\n"
+ " * @file message_prehash.h\n"
+ " * @brief header file of externs of prehashed variables plus defines.\n"
+ " *\n"
+ " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n"
+ " * $License$\n"
+ " */\n\n"
+ "#ifndef LL_MESSAGE_PREHASH_H\n#define LL_MESSAGE_PREHASH_H\n\n");
+ fprintf(
+ fp,
+ "/**\n"
+ " * Generated from message template version number %.3f\n"
+ " */\n",
+ gMessageSystem->mMessageFileVersionNumber);
+ fprintf(fp, "\n\nextern F32 gPrehashVersionNumber;\n\n");
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "extern char * _PREHASH_%s;\n", gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "\n\nvoid init_prehash_data();\n\n");
+ fprintf(fp, "\n\n");
+ fprintf(fp, "\n\n#endif\n");
+ fclose(fp);
+ }
+ fp = LLFile::fopen("../../indra/llmessage/message_prehash.cpp", "w");
+ if (fp)
+ {
+ fprintf(
+ fp,
+ "/**\n"
+ " * @file message_prehash.cpp\n"
+ " * @brief file of prehashed variables\n"
+ " *\n"
+ " * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.\n"
+ " * $License$\n"
+ " */\n\n"
+ "/**\n"
+ " * Generated from message template version number %.3f\n"
+ " */\n",
+ gMessageSystem->mMessageFileVersionNumber);
+ fprintf(fp, "#include \"linden_common.h\"\n");
+ fprintf(fp, "#include \"message.h\"\n\n");
+ fprintf(fp, "\n\nF32 gPrehashVersionNumber = %.3ff;\n\n", gMessageSystem->mMessageFileVersionNumber);
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "char * _PREHASH_%s;\n", gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "\nvoid init_prehash_data()\n");
+ fprintf(fp, "{\n");
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ if (!gMessageStringTable.mEmpty[i] && gMessageStringTable.mString[i][0] != '.')
+ {
+ fprintf(fp, "\t_PREHASH_%s = gMessageStringTable.getString(\"%s\");\n", gMessageStringTable.mString[i], gMessageStringTable.mString[i]);
+ }
+ }
+ fprintf(fp, "}\n");
+ fclose(fp);
+ }
+}
+
+BOOL start_messaging_system(
+ const std::string& template_name,
+ U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch,
+ BOOL b_dump_prehash_file,
+ const std::string& secret)
+{
+ gMessageSystem = new LLMessageSystem(
+ template_name.c_str(),
+ port,
+ version_major,
+ version_minor,
+ version_patch);
+ g_shared_secret.assign(secret);
+
+ if (!gMessageSystem)
+ {
+ llerrs << "Messaging system initialization failed." << llendl;
+ return FALSE;
+ }
+
+ // bail if system encountered an error.
+ if(!gMessageSystem->isOK())
+ {
+ return FALSE;
+ }
+
+ if (b_dump_prehash_file)
+ {
+ dump_prehash_files();
+ exit(0);
+ }
+ else
+ {
+ init_prehash_data();
+ if (gMessageSystem->mMessageFileVersionNumber != gPrehashVersionNumber)
+ {
+ llinfos << "Message template version does not match prehash version number" << llendl;
+ llinfos << "Run simulator with -prehash command line option to rebuild prehash data" << llendl;
+ }
+ else
+ {
+ llinfos << "Message template version matches prehash version number" << llendl;
+ }
+ }
+
+ gMessageSystem->setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_OpenCircuit, open_circuit, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CloseCircuit, close_circuit, NULL);
+
+ //gMessageSystem->setHandlerFuncFast(_PREHASH_AssignCircuitCode, LLMessageSystem::processAssignCircuitCode);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AddCircuitCode, LLMessageSystem::processAddCircuitCode);
+ //gMessageSystem->setHandlerFuncFast(_PREHASH_AckAddCircuitCode, ack_add_circuit_code, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_UseCircuitCode, LLMessageSystem::processUseCircuitCode);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck, process_packet_ack, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_TemplateChecksumRequest, process_template_checksum_request, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_SecuredTemplateChecksumRequest, process_secured_template_checksum_request, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_LogControl, process_log_control, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_LogMessages, process_log_messages, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_CreateTrustedCircuit,
+ process_create_trusted_circuit,
+ NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_DenyTrustedCircuit,
+ process_deny_trusted_circuit,
+ NULL);
+
+ // We can hand this to the null_message_callback since it is a
+ // trusted message, so it will automatically be denied if it isn't
+ // trusted and ignored if it is -- exactly what we want.
+ gMessageSystem->setHandlerFunc(
+ "RequestTrustedCircuit",
+ null_message_callback,
+ NULL);
+
+ // Initialize the transfer manager
+ gTransferManager.init();
+
+ return TRUE;
+}
+
+void LLMessageSystem::startLogging()
+{
+ mVerboseLog = TRUE;
+ std::ostringstream str;
+ str << "START MESSAGE LOG" << std::endl;
+ str << "Legend:" << std::endl;
+ str << "\t<-\tincoming message" <<std::endl;
+ str << "\t->\toutgoing message" << std::endl;
+ str << " <> host size zero id name";
+ llinfos << str.str() << llendl;
+}
+
+void LLMessageSystem::stopLogging()
+{
+ if(mVerboseLog)
+ {
+ mVerboseLog = FALSE;
+ llinfos << "END MESSAGE LOG" << llendl;
+ }
+}
+
+void LLMessageSystem::summarizeLogs(std::ostream& str)
+{
+ char buffer[MAX_STRING]; /* Flawfinder: ignore */
+ char tmp_str[MAX_STRING]; /* Flawfinder: ignore */
+ F32 run_time = mMessageSystemTimer.getElapsedTimeF32();
+ str << "START MESSAGE LOG SUMMARY" << std::endl;
+ snprintf(buffer, MAX_STRING, "Run time: %12.3f seconds", run_time); /* Flawfinder: ignore */
+
+ // Incoming
+ str << buffer << std::endl << "Incoming:" << std::endl;
+ U64_to_str(mTotalBytesIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total bytes received: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesIn * 0.008f) / run_time); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mPacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total packets received: %20s (%5.2f packets per second)", tmp_str, ((F32) mPacketsIn / run_time)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesIn / (F32)mPacketsIn); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mReliablePacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mCompressedPacketsIn, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsIn)/((F32) mPacketsIn + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ S64 savings = mUncompressedBytesIn - mCompressedBytesIn;
+ U64_to_str(savings, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mCompressedPacketsIn +1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesIn)/((F32) mCompressedBytesIn+1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mPacketsIn+1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesIn + (F32) savings)/((F32) mTotalBytesIn + 1.f)); /* Flawfinder: ignore */
+
+ // Outgoing
+ str << buffer << std::endl << std::endl << "Outgoing:" << std::endl;
+ U64_to_str(mTotalBytesOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total bytes sent: %20s (%5.2f kbits per second)", tmp_str, ((F32)mTotalBytesOut * 0.008f) / run_time ); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mPacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total packets sent: %20s (%5.2f packets per second)", tmp_str, ((F32)mPacketsOut / run_time)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Average packet size: %20.0f bytes", (F32)mTotalBytesOut / (F32)mPacketsOut); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mReliablePacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total reliable packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mReliablePacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(mCompressedPacketsOut, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compressed packets: %20s (%5.2f%%)", tmp_str, 100.f * ((F32) mCompressedPacketsOut)/((F32) mPacketsOut + 1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ savings = mUncompressedBytesOut - mCompressedBytesOut;
+ U64_to_str(savings, tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Total compression savings: %20s bytes", tmp_str); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mCompressedPacketsOut +1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg comp packet savings: %20s (%5.2f : 1)", tmp_str, ((F32) mUncompressedBytesOut)/((F32) mCompressedBytesOut+1)); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ U64_to_str(savings/(mPacketsOut+1), tmp_str, sizeof(tmp_str));
+ snprintf(buffer, MAX_STRING, "Avg overall comp savings: %20s (%5.2f : 1)", tmp_str, ((F32) mTotalBytesOut + (F32) savings)/((F32) mTotalBytesOut + 1.f)); /* Flawfinder: ignore */
+ str << buffer << std::endl << std::endl;
+ snprintf(buffer, MAX_STRING, "SendPacket failures: %20d", mSendPacketFailureCount); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Dropped packets: %20d", mDroppedPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Resent packets: %20d", mResentPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Failed reliable resends: %20d", mFailedResendPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "Off-circuit rejected packets: %17d", mOffCircuitPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ snprintf(buffer, MAX_STRING, "On-circuit invalid packets: %17d", mInvalidOnCircuitPackets); /* Flawfinder: ignore */
+ str << buffer << std::endl << std::endl;
+
+ str << "Decoding: " << std::endl;
+ snprintf(buffer, MAX_STRING, "%35s%10s%10s%10s%10s", "Message", "Count", "Time", "Max", "Avg"); /* Flawfinder: ignore */
+ str << buffer << std:: endl;
+ F32 avg;
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ if(mt->mTotalDecoded > 0)
+ {
+ avg = mt->mTotalDecodeTime / (F32)mt->mTotalDecoded;
+ snprintf(buffer, MAX_STRING, "%35s%10u%10f%10f%10f", mt->mName, mt->mTotalDecoded, mt->mTotalDecodeTime, mt->mMaxDecodeTimePerMsg, avg); /* Flawfinder: ignore */
+ str << buffer << std::endl;
+ }
+ }
+ str << "END MESSAGE LOG SUMMARY" << std::endl;
+}
+
+void end_messaging_system()
+{
+ gTransferManager.cleanup();
+ LLTransferTargetVFile::updateQueue(true); // shutdown LLTransferTargetVFile
+ if (gMessageSystem)
+ {
+ gMessageSystem->stopLogging();
+
+ std::ostringstream str;
+ gMessageSystem->summarizeLogs(str);
+ llinfos << str.str().c_str() << llendl;
+
+ delete gMessageSystem;
+ gMessageSystem = NULL;
+ }
+}
+
+void LLMessageSystem::resetReceiveCounts()
+{
+ mNumMessageCounts = 0;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ mt->mDecodeTimeThisFrame = 0.f;
+ }
+}
+
+
+void LLMessageSystem::dumpReceiveCounts()
+{
+ LLMessageTemplate *mt;
+
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ mt->mReceiveCount = 0;
+ mt->mReceiveBytes = 0;
+ mt->mReceiveInvalid = 0;
+ }
+
+ S32 i;
+ for (i = 0; i < mNumMessageCounts; i++)
+ {
+ mt = get_ptr_in_map(mMessageNumbers,mMessageCountList[i].mMessageNum);
+ if (mt)
+ {
+ mt->mReceiveCount++;
+ mt->mReceiveBytes += mMessageCountList[i].mMessageBytes;
+ if (mMessageCountList[i].mInvalid)
+ {
+ mt->mReceiveInvalid++;
+ }
+ }
+ }
+
+ if(mNumMessageCounts > 0)
+ {
+ llinfos << "Dump: " << mNumMessageCounts << " messages processed in " << mReceiveTime << " seconds" << llendl;
+ for (message_template_name_map_t::iterator iter = mMessageTemplates.begin(),
+ end = mMessageTemplates.end();
+ iter != end; iter++)
+ {
+ LLMessageTemplate* mt = iter->second;
+ if (mt->mReceiveCount > 0)
+ {
+ llinfos << "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;
+ }
+ }
+ }
+}
+
+
+
+BOOL LLMessageSystem::isClear() const
+{
+ return mbSClear;
+}
+
+
+S32 LLMessageSystem::flush(const LLHost &host)
+{
+ if (mCurrentSendTotal)
+ {
+ S32 sentbytes = sendMessage(host);
+ clearMessage();
+ return sentbytes;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+U32 LLMessageSystem::getListenPort( void ) const
+{
+ return mPort;
+}
+
+
+S32 LLMessageSystem::zeroCode(U8 **data, S32 *data_size)
+{
+ S32 count = *data_size;
+
+ S32 net_gain = 0;
+ U8 num_zeroes = 0;
+
+ U8 *inptr = (U8 *)*data;
+ U8 *outptr = (U8 *)mEncodedSendBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ *outptr++ = *inptr++;
+ }
+
+// build encoded packet, keeping track of net size gain
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (!(*inptr)) // in a zero count
+ {
+ if (num_zeroes)
+ {
+ if (++num_zeroes > 254)
+ {
+ *outptr++ = num_zeroes;
+ num_zeroes = 0;
+ }
+ net_gain--; // subseqent zeroes save one
+ }
+ else
+ {
+ *outptr++ = 0;
+ net_gain++; // starting a zero count adds one
+ num_zeroes = 1;
+ }
+ inptr++;
+ }
+ else
+ {
+ if (num_zeroes)
+ {
+ *outptr++ = num_zeroes;
+ num_zeroes = 0;
+ }
+ *outptr++ = *inptr++;
+ }
+ }
+
+ if (num_zeroes)
+ {
+ *outptr++ = num_zeroes;
+ }
+
+ if (net_gain < 0)
+ {
+ mCompressedPacketsOut++;
+ mUncompressedBytesOut += *data_size;
+
+ *data = mEncodedSendBuffer;
+ *data_size += net_gain;
+ mEncodedSendBuffer[0] |= LL_ZERO_CODE_FLAG; // set the head bit to indicate zero coding
+
+ mCompressedBytesOut += *data_size;
+
+ }
+ mTotalBytesOut += *data_size;
+
+ return(net_gain);
+}
+
+S32 LLMessageSystem::zeroCodeAdjustCurrentSendTotal()
+{
+ if (!mbSBuilt)
+ {
+ buildMessage();
+ }
+ mbSBuilt = FALSE;
+
+ S32 count = mSendSize;
+
+ S32 net_gain = 0;
+ U8 num_zeroes = 0;
+
+ U8 *inptr = (U8 *)mSendBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ inptr++;
+ }
+
+// don't actually build, just test
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (!(*inptr)) // in a zero count
+ {
+ if (num_zeroes)
+ {
+ if (++num_zeroes > 254)
+ {
+ num_zeroes = 0;
+ }
+ net_gain--; // subseqent zeroes save one
+ }
+ else
+ {
+ net_gain++; // starting a zero count adds one
+ num_zeroes = 1;
+ }
+ inptr++;
+ }
+ else
+ {
+ if (num_zeroes)
+ {
+ num_zeroes = 0;
+ }
+ inptr++;
+ }
+ }
+ if (net_gain < 0)
+ {
+ return net_gain;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+
+S32 LLMessageSystem::zeroCodeExpand(U8 **data, S32 *data_size)
+{
+
+ if ((*data_size ) < LL_PACKET_ID_SIZE)
+ {
+ llwarns << "zeroCodeExpand() called with data_size of " << *data_size << llendl;
+ }
+
+ mTotalBytesIn += *data_size;
+
+ if (!(*data[0] & LL_ZERO_CODE_FLAG)) // if we're not zero-coded, just go 'way
+ {
+ return(0);
+ }
+
+ S32 in_size = *data_size;
+ mCompressedPacketsIn++;
+ mCompressedBytesIn += *data_size;
+
+ *data[0] &= (~LL_ZERO_CODE_FLAG);
+
+ S32 count = (*data_size);
+
+ U8 *inptr = (U8 *)*data;
+ U8 *outptr = (U8 *)mEncodedRecvBuffer;
+
+// skip the packet id field
+
+ for (U32 i=0;i<LL_PACKET_ID_SIZE;i++)
+ {
+ count--;
+ *outptr++ = *inptr++;
+ }
+
+// reconstruct encoded packet, keeping track of net size gain
+
+// sequential zero bytes are encoded as 0 [U8 count]
+// with 0 0 [count] representing wrap (>256 zeroes)
+
+ while (count--)
+ {
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-1]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 1" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ break;
+ }
+ if (!((*outptr++ = *inptr++)))
+ {
+ while (((count--)) && (!(*inptr)))
+ {
+ *outptr++ = *inptr++;
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-256]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 2" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ count = -1;
+ break;
+ }
+ memset(outptr,0,255);
+ outptr += 255;
+ }
+
+ if (count < 0)
+ {
+ break;
+ }
+
+ else
+ {
+ if (outptr > (&mEncodedRecvBuffer[MAX_BUFFER_SIZE-(*inptr)]))
+ {
+ llwarns << "attempt to write past reasonable encoded buffer size 3" << llendl;
+ callExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE);
+ outptr = mEncodedRecvBuffer;
+ }
+ memset(outptr,0,(*inptr) - 1);
+ outptr += ((*inptr) - 1);
+ inptr++;
+ }
+ }
+ }
+
+ *data = mEncodedRecvBuffer;
+ *data_size = (S32)(outptr - mEncodedRecvBuffer);
+ mUncompressedBytesIn += *data_size;
+
+ return(in_size);
+}
+
+
+void LLMessageSystem::addTemplate(LLMessageTemplate *templatep)
+{
+ if (mMessageTemplates.count(templatep->mName) > 0)
+ {
+ llerrs << templatep->mName << " already used as a template name!"
+ << llendl;
+ }
+ mMessageTemplates[templatep->mName] = templatep;
+ mMessageNumbers[templatep->mMessageNumber] = templatep;
+}
+
+
+void LLMessageSystem::setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data)
+{
+ LLMessageTemplate* msgtemplate = get_ptr_in_map(mMessageTemplates, name);
+ if (msgtemplate)
+ {
+ msgtemplate->setHandlerFunc(handler_func, user_data);
+ }
+ else
+ {
+ llerrs << name << " is not a known message name!" << llendl;
+ }
+}
+
+
+bool LLMessageSystem::callHandler(const char *name,
+ bool trustedSource, LLMessageSystem* msg)
+{
+ name = gMessageStringTable.getString(name);
+ LLMessageTemplate* msg_template = mMessageTemplates[(char*)name];
+ if (!msg_template)
+ {
+ llwarns << "LLMessageSystem::callHandler: unknown message "
+ << name << llendl;
+ return false;
+ }
+
+ if (msg_template->isBanned(trustedSource))
+ {
+ llwarns << "LLMessageSystem::callHandler: banned message "
+ << name
+ << " from "
+ << (trustedSource ? "trusted " : "untrusted ")
+ << "source" << llendl;
+ return false;
+ }
+
+ return msg_template->callHandlerFunc(msg);
+}
+
+
+void LLMessageSystem::setExceptionFunc(EMessageException e,
+ msg_exception_callback func,
+ void* data)
+{
+ callbacks_t::iterator it = mExceptionCallbacks.find(e);
+ if(it != mExceptionCallbacks.end())
+ {
+ mExceptionCallbacks.erase(it);
+ }
+ if(func)
+ {
+ mExceptionCallbacks.insert(callbacks_t::value_type(e, exception_t(func, data)));
+ }
+}
+
+BOOL LLMessageSystem::callExceptionFunc(EMessageException exception)
+{
+ callbacks_t::iterator it = mExceptionCallbacks.find(exception);
+ if(it != mExceptionCallbacks.end())
+ {
+ ((*it).second.first)(this, (*it).second.second,exception);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLMessageSystem::isCircuitCodeKnown(U32 code) const
+{
+ if(mCircuitCodes.find(code) == mCircuitCodes.end())
+ return FALSE;
+ return TRUE;
+}
+
+BOOL LLMessageSystem::isMessageFast(const char *msg)
+{
+ if (mCurrentRMessageTemplate)
+ {
+ return(msg == mCurrentRMessageTemplate->mName);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+char* LLMessageSystem::getMessageName()
+{
+ if (mCurrentRMessageTemplate)
+ {
+ return mCurrentRMessageTemplate->mName;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+const LLUUID& LLMessageSystem::getSenderID() const
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender);
+ if (cdp)
+ {
+ return (cdp->mRemoteID);
+ }
+
+ return LLUUID::null;
+}
+
+const LLUUID& LLMessageSystem::getSenderSessionID() const
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(mLastSender);
+ if (cdp)
+ {
+ return (cdp->mRemoteSessionID);
+ }
+ return LLUUID::null;
+}
+
+void LLMessageSystem::addVector3Fast(const char *varname, const LLVector3& vec)
+{
+ addDataFast(varname, vec.mV, MVT_LLVector3, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector3(const char *varname, const LLVector3& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector3, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector4Fast(const char *varname, const LLVector4& vec)
+{
+ addDataFast(varname, vec.mV, MVT_LLVector4, sizeof(vec.mV));
+}
+
+void LLMessageSystem::addVector4(const char *varname, const LLVector4& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mV, MVT_LLVector4, sizeof(vec.mV));
+}
+
+
+void LLMessageSystem::addVector3dFast(const char *varname, const LLVector3d& vec)
+{
+ addDataFast(varname, vec.mdV, MVT_LLVector3d, sizeof(vec.mdV));
+}
+
+void LLMessageSystem::addVector3d(const char *varname, const LLVector3d& vec)
+{
+ addDataFast(gMessageStringTable.getString(varname), vec.mdV, MVT_LLVector3d, sizeof(vec.mdV));
+}
+
+
+void LLMessageSystem::addQuatFast(const char *varname, const LLQuaternion& quat)
+{
+ addDataFast(varname, quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3));
+}
+
+void LLMessageSystem::addQuat(const char *varname, const LLQuaternion& quat)
+{
+ addDataFast(gMessageStringTable.getString(varname), quat.packToVector3().mV, MVT_LLQuaternion, sizeof(LLVector3));
+}
+
+
+void LLMessageSystem::addUUIDFast(const char *varname, const LLUUID& uuid)
+{
+ addDataFast(varname, uuid.mData, MVT_LLUUID, sizeof(uuid.mData));
+}
+
+void LLMessageSystem::addUUID(const char *varname, const LLUUID& uuid)
+{
+ addDataFast(gMessageStringTable.getString(varname), uuid.mData, MVT_LLUUID, sizeof(uuid.mData));
+}
+
+void LLMessageSystem::getF32Fast(const char *block, const char *var, F32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(F32), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF32Fast " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF32(const char *block, const char *var, F32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F32), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF32 " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF64Fast(const char *block, const char *var, F64 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(F64), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF64Fast " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+void LLMessageSystem::getF64(const char *block, const char *var, F64 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(F64), blocknum);
+
+ if( !llfinite( d ) )
+ {
+ llwarns << "non-finite in getF64 " << block << " " << var << llendl;
+ d = 0;
+ }
+}
+
+
+void LLMessageSystem::getVector3Fast(const char *block, const char *var, LLVector3 &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3Fast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector3(const char *block, const char *var, LLVector3 &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector4 " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector4Fast(const char *block, const char *var, LLVector4 &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector4Fast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector4(const char *block, const char *var, LLVector4 &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mV, sizeof(v.mV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3 " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getVector3dFast(const char *block, const char *var, LLVector3d &v, S32 blocknum )
+{
+ getDataFast(block, var, v.mdV, sizeof(v.mdV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3dFast " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+
+}
+
+void LLMessageSystem::getVector3d(const char *block, const char *var, LLVector3d &v, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), v.mdV, sizeof(v.mdV), blocknum);
+
+ if( !v.isFinite() )
+ {
+ llwarns << "non-finite in getVector3d " << block << " " << var << llendl;
+ v.zeroVec();
+ }
+}
+
+void LLMessageSystem::getQuatFast(const char *block, const char *var, LLQuaternion &q, S32 blocknum )
+{
+ LLVector3 vec;
+ getDataFast(block, var, vec.mV, sizeof(vec.mV), blocknum);
+ if( vec.isFinite() )
+ {
+ q.unpackFromVector3( vec );
+ }
+ else
+ {
+ llwarns << "non-finite in getQuatFast " << block << " " << var << llendl;
+ q.loadIdentity();
+ }
+}
+
+void LLMessageSystem::getQuat(const char *block, const char *var, LLQuaternion &q, S32 blocknum )
+{
+ LLVector3 vec;
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), vec.mV, sizeof(vec.mV), blocknum);
+ if( vec.isFinite() )
+ {
+ q.unpackFromVector3( vec );
+ }
+ else
+ {
+ llwarns << "non-finite in getQuat " << block << " " << var << llendl;
+ q.loadIdentity();
+ }
+}
+
+void LLMessageSystem::getUUIDFast(const char *block, const char *var, LLUUID &u, S32 blocknum )
+{
+ getDataFast(block, var, u.mData, sizeof(u.mData), blocknum);
+}
+
+void LLMessageSystem::getUUID(const char *block, const char *var, LLUUID &u, S32 blocknum )
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), u.mData, sizeof(u.mData), blocknum);
+}
+
+bool LLMessageSystem::generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const
+{
+ const char *colon = ":";
+ char tbuf[16]; /* Flawfinder: ignore */
+ LLMD5 d;
+ LLString id1string = id1.getString();
+ LLString id2string = id2.getString();
+ std::string shared_secret = get_shared_secret();
+ unsigned char * secret = (unsigned char*)shared_secret.c_str();
+ unsigned char * id1str = (unsigned char*)id1string.c_str();
+ unsigned char * id2str = (unsigned char*)id2string.c_str();
+
+ memset(digest, 0, MD5HEX_STR_SIZE);
+
+ if( secret != NULL)
+ {
+ d.update(secret, (U32)strlen((char *) secret));
+ }
+
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+
+ snprintf(tbuf, sizeof(tbuf),"%i", number); /* Flawfinder: ignore */
+ d.update((unsigned char *) tbuf, (U32)strlen(tbuf)); /* Flawfinder: ignore */
+
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+ if( (char*) id1str != NULL)
+ {
+ d.update(id1str, (U32)strlen((char *) id1str));
+ }
+ d.update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */
+
+ if( (char*) id2str != NULL)
+ {
+ d.update(id2str, (U32)strlen((char *) id2str));
+ }
+
+ d.finalize();
+ d.hex_digest(digest);
+ digest[MD5HEX_STR_SIZE - 1] = '\0';
+
+ return true;
+}
+
+bool LLMessageSystem::generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const
+{
+ if(0 == window) return false;
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to generate complex digest on a machine without a shared secret!" << llendl;
+ }
+
+ U32 now = time(NULL);
+
+ now /= window;
+
+ bool result = generateDigestForNumberAndUUIDs(digest, now, id1, id2);
+
+ return result;
+}
+
+bool LLMessageSystem::isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to compare complex digests on a machine without a shared secret!" << llendl;
+ }
+
+ char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ U32 now = time(NULL);
+
+ now /= window;
+
+ // Check 1 window ago, now, and one window from now to catch edge
+ // conditions. Process them as current window, one window ago, and
+ // one window in the future to catch the edges.
+ const S32 WINDOW_BIN_COUNT = 3;
+ U32 window_bin[WINDOW_BIN_COUNT];
+ window_bin[0] = now;
+ window_bin[1] = now - 1;
+ window_bin[2] = now + 1;
+ for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i)
+ {
+ generateDigestForNumberAndUUIDs(our_digest, window_bin[i], id2, id1);
+ if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLMessageSystem::generateDigestForNumber(char* digest, const U32 number) const
+{
+ memset(digest, 0, MD5HEX_STR_SIZE);
+
+ LLMD5 d;
+ std::string shared_secret = get_shared_secret();
+ d = LLMD5((const unsigned char *)shared_secret.c_str(), number);
+ d.hex_digest(digest);
+ digest[MD5HEX_STR_SIZE - 1] = '\0';
+
+ return true;
+}
+
+bool LLMessageSystem::generateDigestForWindow(char* digest, const S32 window) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to generate simple digest on a machine without a shared secret!" << llendl;
+ }
+
+ U32 now = time(NULL);
+
+ now /= window;
+
+ bool result = generateDigestForNumber(digest, now);
+
+ return result;
+}
+
+bool LLMessageSystem::isMatchingDigestForWindow(const char* digest, S32 const window) const
+{
+ if(0 == window) return false;
+
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to compare simple digests on a machine without a shared secret!" << llendl;
+ }
+
+ char our_digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ U32 now = (S32)time(NULL);
+
+ now /= window;
+
+ // Check 1 window ago, now, and one window from now to catch edge
+ // conditions. Process them as current window, one window ago, and
+ // one window in the future to catch the edges.
+ const S32 WINDOW_BIN_COUNT = 3;
+ U32 window_bin[WINDOW_BIN_COUNT];
+ window_bin[0] = now;
+ window_bin[1] = now - 1;
+ window_bin[2] = now + 1;
+ for(S32 i = 0; i < WINDOW_BIN_COUNT; ++i)
+ {
+ generateDigestForNumber(our_digest, window_bin[i]);
+ if(0 == strncmp(digest, our_digest, MD5HEX_STR_BYTES))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+void LLMessageSystem::sendCreateTrustedCircuit(const LLHost &host, const LLUUID & id1, const LLUUID & id2)
+{
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty()) return;
+ char digest[MD5HEX_STR_SIZE]; /* Flawfinder: ignore */
+ if (id1.isNull())
+ {
+ llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the local end point ID" << llendl;
+ return;
+ }
+ if (id2.isNull())
+ {
+ llwarns << "Can't send CreateTrustedCircuit to " << host << " because we don't have the remote end point ID" << llendl;
+ return;
+ }
+ generateDigestForWindowAndUUIDs(digest, TRUST_TIME_WINDOW, id1, id2);
+ newMessageFast(_PREHASH_CreateTrustedCircuit);
+ nextBlockFast(_PREHASH_DataBlock);
+ addUUIDFast(_PREHASH_EndPointID, id1);
+ addBinaryDataFast(_PREHASH_Digest, digest, MD5HEX_STR_BYTES);
+ llinfos << "xmitting digest: " << digest << " Host: " << host << llendl;
+ sendMessage(host);
+}
+
+void LLMessageSystem::sendDenyTrustedCircuit(const LLHost &host)
+{
+ mDenyTrustedCircuitSet.insert(host);
+}
+
+void LLMessageSystem::reallySendDenyTrustedCircuit(const LLHost &host)
+{
+ LLCircuitData *cdp = mCircuitInfo.findCircuit(host);
+ if (!cdp)
+ {
+ llwarns << "Not sending DenyTrustedCircuit to host without a circuit." << llendl;
+ return;
+ }
+ llinfos << "Sending DenyTrustedCircuit to " << host << llendl;
+ newMessageFast(_PREHASH_DenyTrustedCircuit);
+ nextBlockFast(_PREHASH_DataBlock);
+ addUUIDFast(_PREHASH_EndPointID, cdp->getLocalEndPointID());
+ sendMessage(host);
+}
+
+void null_message_callback(LLMessageSystem *msg, void **data)
+{
+ // Nothing should ever go here, but we use this to register messages
+ // that we are expecting to see (and spinning on) at startup.
+ return;
+}
+
+// Try to establish a bidirectional trust metric by pinging a host until it's
+// up, and then sending auth messages.
+void LLMessageSystem::establishBidirectionalTrust(const LLHost &host, S64 frame_count )
+{
+ std::string shared_secret = get_shared_secret();
+ if(shared_secret.empty())
+ {
+ llerrs << "Trying to establish bidirectional trust on a machine without a shared secret!" << llendl;
+ }
+ LLTimer timeout;
+
+ timeout.setTimerExpirySec(20.0);
+ setHandlerFuncFast(_PREHASH_StartPingCheck, null_message_callback, NULL);
+ setHandlerFuncFast(_PREHASH_CompletePingCheck, null_message_callback,
+ NULL);
+
+ while (! timeout.hasExpired())
+ {
+ newMessageFast(_PREHASH_StartPingCheck);
+ nextBlockFast(_PREHASH_PingID);
+ addU8Fast(_PREHASH_PingID, 0);
+ addU32Fast(_PREHASH_OldestUnacked, 0);
+ sendMessage(host);
+ if (checkMessages( frame_count ))
+ {
+ if (isMessageFast(_PREHASH_CompletePingCheck) &&
+ (getSender() == host))
+ {
+ break;
+ }
+ }
+ processAcks();
+ ms_sleep(1);
+ }
+
+ // Send a request, a deny, and give the host 2 seconds to complete
+ // the trust handshake.
+ newMessage("RequestTrustedCircuit");
+ sendMessage(host);
+ reallySendDenyTrustedCircuit(host);
+ setHandlerFuncFast(_PREHASH_StartPingCheck, process_start_ping_check, NULL);
+ setHandlerFuncFast(_PREHASH_CompletePingCheck, process_complete_ping_check, NULL);
+
+ timeout.setTimerExpirySec(2.0);
+ LLCircuitData* cdp = NULL;
+ while(!timeout.hasExpired())
+ {
+ cdp = mCircuitInfo.findCircuit(host);
+ if(!cdp) break; // no circuit anymore, no point continuing.
+ if(cdp->getTrusted()) break; // circuit is trusted.
+ checkMessages(frame_count);
+ processAcks();
+ ms_sleep(1);
+ }
+}
+
+
+void LLMessageSystem::dumpPacketToLog()
+{
+ llwarns << "Packet Dump from:" << mPacketRing.getLastSender() << llendl;
+ llwarns << "Packet Size:" << mTrueReceiveSize << llendl;
+ char line_buffer[256]; /* Flawfinder: ignore */
+ S32 i;
+ S32 cur_line_pos = 0;
+
+ S32 cur_line = 0;
+ for (i = 0; i < mTrueReceiveSize; i++)
+ {
+ snprintf(line_buffer + cur_line_pos*3, sizeof(line_buffer),"%02x ", mTrueReceiveBuffer[i]); /* Flawfinder: ignore */
+ cur_line_pos++;
+ if (cur_line_pos >= 16)
+ {
+ cur_line_pos = 0;
+ llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl;
+ cur_line++;
+ }
+ }
+ if (cur_line_pos)
+ {
+ llwarns << "PD:" << cur_line << "PD:" << line_buffer << llendl;
+ }
+}
+
+//static
+U64 LLMessageSystem::getMessageTimeUsecs(const BOOL update)
+{
+ if (gMessageSystem)
+ {
+ if (update)
+ {
+ gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC;
+ }
+ return (U64)(gMessageSystem->mCurrentMessageTimeSeconds * USEC_PER_SEC);
+ }
+ else
+ {
+ return totalTime();
+ }
+}
+
+//static
+F64 LLMessageSystem::getMessageTimeSeconds(const BOOL update)
+{
+ if (gMessageSystem)
+ {
+ if (update)
+ {
+ gMessageSystem->mCurrentMessageTimeSeconds = totalTime()*SEC_PER_USEC;
+ }
+ return gMessageSystem->mCurrentMessageTimeSeconds;
+ }
+ else
+ {
+ return totalTime()*SEC_PER_USEC;
+ }
+}
+
+std::string get_shared_secret()
+{
+ static const std::string SHARED_SECRET_KEY("shared_secret");
+ if(g_shared_secret.empty())
+ {
+ LLApp* app = LLApp::instance();
+ if(app) return app->getOption(SHARED_SECRET_KEY);
+ }
+ return g_shared_secret;
+}
+
diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h
new file mode 100644
index 0000000000..c33016669d
--- /dev/null
+++ b/indra/llmessage/message.h
@@ -0,0 +1,1253 @@
+/**
+ * @file message.h
+ * @brief LLMessageSystem class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MESSAGE_H
+#define LL_MESSAGE_H
+
+#include <cstring>
+#include <stdio.h>
+#include <map>
+#include <set>
+
+#if LL_LINUX
+#include <endian.h>
+#include <netinet/in.h>
+#endif
+
+#if LL_WINDOWS
+#include "winsock2.h" // htons etc.
+#endif
+
+#include "llerror.h"
+#include "net.h"
+#include "string_table.h"
+#include "llptrskipmap.h"
+#include "llcircuit.h"
+#include "lltimer.h"
+#include "llpacketring.h"
+#include "llhost.h"
+#include "llpacketack.h"
+#include "doublelinkedlist.h"
+#include "message_prehash.h"
+#include "llstl.h"
+#include "lldarray.h"
+
+const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
+const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
+
+const S32 MESSAGE_MAX_PER_FRAME = 400;
+
+// FIXME: This needs to be moved to a server-side only header.
+// 30 Sep 2002 mark
+//extern char *MESSAGE_SHARED_SECRET;
+
+class LLMessageStringTable
+{
+public:
+ LLMessageStringTable();
+ ~LLMessageStringTable();
+
+ char *getString(const char *str);
+
+ U32 mUsed;
+ BOOL mEmpty[MESSAGE_NUMBER_OF_HASH_BUCKETS];
+ char mString[MESSAGE_NUMBER_OF_HASH_BUCKETS][MESSAGE_MAX_STRINGS_LENGTH]; /* Flawfinder: ignore */
+};
+
+extern LLMessageStringTable gMessageStringTable;
+
+// Individual Messages are described with the following format
+// Note that to ease parsing, keywords are used
+//
+// // Comment (Comment like a C++ single line comment)
+// Comments can only be placed between Messages
+// {
+// MessageName (same naming restrictions as C variable)
+// Frequency ("High", "Medium", or "Low" - determines whether message ID is 8, 16, or 32-bits --
+// there can 254 messages in the first 2 groups, 32K in the last group)
+// (A message can be made up only of the Name if it is only a signal)
+// Trust ("Trusted", "NotTrusted" - determines if a message will be accepted
+// on a circuit. "Trusted" messages are not accepted from NotTrusted circuits
+// while NotTrusted messages are accepted on any circuit. An example of a
+// NotTrusted circuit is any circuit from the viewer.)
+// Encoding ("Zerocoded", "Unencoded" - zerocoded messages attempt to compress sequences of
+// zeros, but if there is no space win, it discards the compression and goes unencoded)
+// {
+// Block Name (same naming restrictions as C variable)
+// Block Type ("Single", "Multiple", or "Variable" - determines if the block is coded once,
+// a known number of times, or has a 8 bit argument encoded to tell the decoder
+// how many times the group is repeated)
+// Block Repeat Number (Optional - used only with the "Multiple" type - tells how many times the field is repeated
+// {
+// Variable 1 Name (same naming restrictions as C variable)
+// Variable Type ("Fixed" or "Variable" - determines if the variable is of fixed size or needs to
+// encode an argument describing the size in bytes)
+// Variable Size (In bytes, either of the "Fixed" variable itself or of the size argument)
+//
+// repeat variables
+//
+// }
+//
+// Repeat for number of variables in block
+// }
+//
+// Repeat for number of blocks in message
+// }
+// Repeat for number of messages in file
+//
+
+// Constants
+const S32 MAX_MESSAGE_INTERNAL_NAME_SIZE = 255;
+const S32 MAX_BUFFER_SIZE = NET_BUFFER_SIZE;
+const S32 MAX_BLOCKS = 255;
+
+const U8 LL_ZERO_CODE_FLAG = 0x80;
+const U8 LL_RELIABLE_FLAG = 0x40;
+const U8 LL_RESENT_FLAG = 0x20;
+const U8 LL_ACK_FLAG = 0x10;
+
+const S32 LL_MINIMUM_VALID_PACKET_SIZE = LL_PACKET_ID_SIZE + 1; // 4 bytes id + 1 byte message name (high)
+
+const S32 LL_DEFAULT_RELIABLE_RETRIES = 3;
+const F32 LL_MINIMUM_RELIABLE_TIMEOUT_SECONDS = 1.f;
+const F32 LL_MINIMUM_SEMIRELIABLE_TIMEOUT_SECONDS = 1.f;
+const F32 LL_PING_BASED_TIMEOUT_DUMMY = 0.0f;
+
+// FIXME: These factors shouldn't include the msec to sec conversion implicitly
+const F32 LL_SEMIRELIABLE_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_RELIABLE_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_FILE_XFER_TIMEOUT_FACTOR = 5.f / 1000.f; // factor * averaged ping
+const F32 LL_LOST_TIMEOUT_FACTOR = 16.f / 1000.f; // factor * averaged ping for marking packets "Lost"
+const F32 LL_MAX_LOST_TIMEOUT = 5.f; // Maximum amount of time before considering something "lost"
+
+const S32 MAX_MESSAGE_COUNT_NUM = 1024;
+
+// Forward declarations
+class LLCircuit;
+class LLVector3;
+class LLVector4;
+class LLVector3d;
+class LLQuaternion;
+class LLSD;
+class LLUUID;
+class LLMessageSystem;
+
+// message data pieces are used to collect the data called for by the message template
+
+// iterator typedefs precede each class as needed
+typedef enum e_message_variable_type
+{
+ MVT_NULL,
+ MVT_FIXED,
+ MVT_VARIABLE,
+ MVT_U8,
+ MVT_U16,
+ MVT_U32,
+ MVT_U64,
+ MVT_S8,
+ MVT_S16,
+ MVT_S32,
+ MVT_S64,
+ MVT_F32,
+ MVT_F64,
+ MVT_LLVector3,
+ MVT_LLVector3d,
+ MVT_LLVector4,
+ MVT_LLQuaternion,
+ MVT_LLUUID,
+ MVT_BOOL,
+ MVT_IP_ADDR,
+ MVT_IP_PORT,
+ MVT_U16Vec3,
+ MVT_U16Quat,
+ MVT_S16Array,
+ MVT_EOL
+} EMsgVariableType;
+
+// message system exceptional condition handlers.
+enum EMessageException
+{
+ MX_UNREGISTERED_MESSAGE, // message number not part of template
+ MX_PACKET_TOO_SHORT, // invalid packet, shorter than minimum packet size
+ MX_RAN_OFF_END_OF_PACKET, // ran off the end of the packet during decode
+ MX_WROTE_PAST_BUFFER_SIZE // wrote past buffer size in zero code expand
+};
+typedef void (*msg_exception_callback)(LLMessageSystem*,void*,EMessageException);
+
+
+
+class LLMsgData;
+class LLMsgBlkData;
+class LLMessageTemplate;
+
+class LLMessagePollInfo;
+
+class LLMessageSystem
+{
+public:
+ U8 mSendBuffer[MAX_BUFFER_SIZE];
+ // Encoded send buffer needs to be slightly larger since the zero
+ // coding can potentially increase the size of the send data.
+ U8 mEncodedSendBuffer[2 * MAX_BUFFER_SIZE];
+ S32 mSendSize;
+ S32 mCurrentSendTotal;
+
+ LLPacketRing mPacketRing;
+ LLReliablePacketParams mReliablePacketParams;
+
+ //LLLinkedList<LLPacketAck> mAckList;
+
+ // Set this flag to TRUE when you want *very* verbose logs.
+ BOOL mVerboseLog;
+
+ U32 mMessageFileChecksum;
+ F32 mMessageFileVersionNumber;
+
+ typedef std::map<const char *, LLMessageTemplate*> message_template_name_map_t;
+ typedef std::map<U32, LLMessageTemplate*> message_template_number_map_t;
+
+private:
+ message_template_name_map_t mMessageTemplates;
+ message_template_number_map_t mMessageNumbers;
+
+public:
+ S32 mSystemVersionMajor;
+ S32 mSystemVersionMinor;
+ S32 mSystemVersionPatch;
+ S32 mSystemVersionServer;
+ U32 mVersionFlags;
+
+
+ BOOL mbProtected;
+
+ U32 mNumberHighFreqMessages;
+ U32 mNumberMediumFreqMessages;
+ U32 mNumberLowFreqMessages;
+ S32 mPort;
+ S32 mSocket;
+
+ U32 mPacketsIn; // total packets in, including compressed and uncompressed
+ U32 mPacketsOut; // total packets out, including compressed and uncompressed
+
+ U64 mBytesIn; // total bytes in, including compressed and uncompressed
+ U64 mBytesOut; // total bytes out, including compressed and uncompressed
+
+ U32 mCompressedPacketsIn; // total compressed packets in
+ U32 mCompressedPacketsOut; // total compressed packets out
+
+ U32 mReliablePacketsIn; // total reliable packets in
+ U32 mReliablePacketsOut; // total reliable packets out
+
+ U32 mDroppedPackets; // total dropped packets in
+ U32 mResentPackets; // total resent packets out
+ U32 mFailedResendPackets; // total resend failure packets out
+ U32 mOffCircuitPackets; // total # of off-circuit packets rejected
+ U32 mInvalidOnCircuitPackets; // total # of on-circuit but invalid packets rejected
+
+ S64 mUncompressedBytesIn; // total uncompressed size of compressed packets in
+ S64 mUncompressedBytesOut; // total uncompressed size of compressed packets out
+ S64 mCompressedBytesIn; // total compressed size of compressed packets in
+ S64 mCompressedBytesOut; // total compressed size of compressed packets out
+ S64 mTotalBytesIn; // total size of all uncompressed packets in
+ S64 mTotalBytesOut; // total size of all uncompressed packets out
+
+ BOOL mSendReliable; // does the outgoing message require a pos ack?
+
+ LLCircuit mCircuitInfo;
+ F64 mCircuitPrintTime; // used to print circuit debug info every couple minutes
+ F32 mCircuitPrintFreq; // seconds
+
+ std::map<U64, U32> mIPPortToCircuitCode;
+ std::map<U32, U64> mCircuitCodeToIPPort;
+ U32 mOurCircuitCode;
+ S32 mSendPacketFailureCount;
+ S32 mUnackedListDepth;
+ S32 mUnackedListSize;
+ S32 mDSMaxListDepth;
+
+public:
+ // Read file and build message templates
+ LLMessageSystem(const char *filename, U32 port, S32 version_major,
+ S32 version_minor, S32 version_patch);
+
+public:
+ // Subclass use.
+ LLMessageSystem();
+
+public:
+ virtual ~LLMessageSystem();
+
+ BOOL isOK() const { return !mbError; }
+ S32 getErrorCode() const { return mErrorCode; }
+
+ // Read file and build message templates filename must point to a
+ // valid string which specifies the path of a valid linden
+ // template.
+ void loadTemplateFile(const char* filename);
+
+
+ // methods for building, sending, receiving, and handling messages
+ void setHandlerFuncFast(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL);
+ void setHandlerFunc(const char *name, void (*handler_func)(LLMessageSystem *msgsystem, void **user_data), void **user_data = NULL)
+ {
+ setHandlerFuncFast(gMessageStringTable.getString(name), handler_func, user_data);
+ }
+
+ bool callHandler(const char *name, bool trustedSource,
+ LLMessageSystem* msg);
+
+ // Set a callback function for a message system exception.
+ void setExceptionFunc(EMessageException exception, msg_exception_callback func, void* data = NULL);
+ // Call the specified exception func, and return TRUE if a
+ // function was found and called. Otherwise return FALSE.
+ BOOL callExceptionFunc(EMessageException exception);
+
+ // This method returns true if the code is in the circuit codes map.
+ BOOL isCircuitCodeKnown(U32 code) const;
+
+ // usually called in response to an AddCircuitCode message, but
+ // may also be called by the login process.
+ bool addCircuitCode(U32 code, const LLUUID& session_id);
+
+ BOOL poll(F32 seconds); // Number of seconds that we want to block waiting for data, returns if data was received
+ BOOL checkMessages( S64 frame_count = 0 );
+ void processAcks();
+
+ BOOL isMessageFast(const char *msg);
+ BOOL isMessage(const char *msg)
+ {
+ return isMessageFast(gMessageStringTable.getString(msg));
+ }
+
+ void dumpPacketToLog();
+
+ char *getMessageName();
+
+ const LLHost& getSender() const;
+ U32 getSenderIP() const; // getSender() is preferred
+ U32 getSenderPort() const; // getSender() is preferred
+
+ // This method returns the uuid associated with the sender. The
+ // UUID will be null if it is not yet known or is a server
+ // circuit.
+ const LLUUID& getSenderID() const;
+
+ // This method returns the session id associated with the last
+ // sender.
+ const LLUUID& getSenderSessionID() const;
+
+ // set & get the session id (useful for viewers for now.)
+ void setMySessionID(const LLUUID& session_id) { mSessionID = session_id; }
+ const LLUUID& getMySessionID() { return mSessionID; }
+
+ virtual void newMessageFast(const char *name);
+ void newMessage(const char *name)
+ {
+ newMessageFast(gMessageStringTable.getString(name));
+ }
+
+ void copyMessageRtoS();
+ void clearMessage();
+
+ virtual void nextBlockFast(const char *blockname);
+ void nextBlock(const char *blockname)
+ {
+ nextBlockFast(gMessageStringTable.getString(blockname));
+ }
+private:
+ void addDataFast(const char *varname, const void *data, EMsgVariableType type, S32 size); // Use only for types not in system already
+ void addData(const char *varname, const void *data, EMsgVariableType type, S32 size)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, type, size);
+ }
+
+
+ void addDataFast(const char *varname, const void *data, EMsgVariableType type); // DEPRECATED - not typed, doesn't check storage space
+ void addData(const char *varname, const void *data, EMsgVariableType type)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, type);
+ }
+public:
+ void addBinaryDataFast(const char *varname, const void *data, S32 size)
+ {
+ addDataFast(varname, data, MVT_FIXED, size);
+ }
+ void addBinaryData(const char *varname, const void *data, S32 size)
+ {
+ addDataFast(gMessageStringTable.getString(varname), data, MVT_FIXED, size);
+ }
+
+ void addBOOLFast( const char* varname, BOOL b); // typed, checks storage space
+ void addBOOL( const char* varname, BOOL b); // typed, checks storage space
+ void addS8Fast( const char *varname, S8 s); // typed, checks storage space
+ void addS8( const char *varname, S8 s); // typed, checks storage space
+ void addU8Fast( const char *varname, U8 u); // typed, checks storage space
+ void addU8( const char *varname, U8 u); // typed, checks storage space
+ void addS16Fast( const char *varname, S16 i); // typed, checks storage space
+ void addS16( const char *varname, S16 i); // typed, checks storage space
+ void addU16Fast( const char *varname, U16 i); // typed, checks storage space
+ void addU16( const char *varname, U16 i); // typed, checks storage space
+ void addF32Fast( const char *varname, F32 f); // typed, checks storage space
+ void addF32( const char *varname, F32 f); // typed, checks storage space
+ void addS32Fast( const char *varname, S32 s); // typed, checks storage space
+ void addS32( const char *varname, S32 s); // typed, checks storage space
+ virtual void addU32Fast( const char *varname, U32 u); // typed, checks storage space
+ void addU32( const char *varname, U32 u); // typed, checks storage space
+ void addU64Fast( const char *varname, U64 lu); // typed, checks storage space
+ void addU64( const char *varname, U64 lu); // typed, checks storage space
+ void addF64Fast( const char *varname, F64 d); // typed, checks storage space
+ void addF64( const char *varname, F64 d); // typed, checks storage space
+ void addVector3Fast( const char *varname, const LLVector3& vec); // typed, checks storage space
+ void addVector3( const char *varname, const LLVector3& vec); // typed, checks storage space
+ void addVector4Fast( const char *varname, const LLVector4& vec); // typed, checks storage space
+ void addVector4( const char *varname, const LLVector4& vec); // typed, checks storage space
+ void addVector3dFast( const char *varname, const LLVector3d& vec); // typed, checks storage space
+ void addVector3d( const char *varname, const LLVector3d& vec); // typed, checks storage space
+ void addQuatFast( const char *varname, const LLQuaternion& quat); // typed, checks storage space
+ void addQuat( const char *varname, const LLQuaternion& quat); // typed, checks storage space
+ virtual void addUUIDFast( const char *varname, const LLUUID& uuid); // typed, checks storage space
+ void addUUID( const char *varname, const LLUUID& uuid); // typed, checks storage space
+ void addIPAddrFast( const char *varname, const U32 ip); // typed, checks storage space
+ void addIPAddr( const char *varname, const U32 ip); // typed, checks storage space
+ void addIPPortFast( const char *varname, const U16 port); // typed, checks storage space
+ void addIPPort( const char *varname, const U16 port); // typed, checks storage space
+ void addStringFast( const char* varname, const char* s); // typed, checks storage space
+ void addString( const char* varname, const char* s); // typed, checks storage space
+ void addStringFast( const char* varname, const std::string& s); // typed, checks storage space
+ void addString( const char* varname, const std::string& s); // typed, checks storage space
+
+ S32 getCurrentSendTotal() const { return mCurrentSendTotal; }
+
+ // This method checks for current send total and returns true if
+ // you need to go to the next block type or need to start a new
+ // message. Specify the current blockname to check block counts,
+ // otherwise the method only checks against MTU.
+ BOOL isSendFull(const char* blockname = NULL);
+ BOOL isSendFullFast(const char* blockname = NULL);
+
+ BOOL removeLastBlock();
+
+ void buildMessage();
+
+ S32 zeroCode(U8 **data, S32 *data_size);
+ S32 zeroCodeExpand(U8 **data, S32 *data_size);
+ S32 zeroCodeAdjustCurrentSendTotal();
+
+ // Uses ping-based retry
+ virtual S32 sendReliable(const LLHost &host);
+
+ // Uses ping-based retry
+ S32 sendReliable(const U32 circuit) { return sendReliable(findHost(circuit)); }
+
+ // Use this one if you DON'T want automatic ping-based retry.
+ S32 sendReliable( const LLHost &host,
+ S32 retries,
+ BOOL ping_based_retries,
+ F32 timeout,
+ void (*callback)(void **,S32),
+ void ** callback_data);
+
+ S32 sendSemiReliable( const LLHost &host,
+ void (*callback)(void **,S32), void ** callback_data);
+
+ // flush sends a message only if data's been pushed on it.
+ S32 flushSemiReliable( const LLHost &host,
+ void (*callback)(void **,S32), void ** callback_data);
+
+ S32 flushReliable( const LLHost &host );
+
+ void forwardMessage(const LLHost &host);
+ void forwardReliable(const LLHost &host);
+ void forwardReliable(const U32 circuit_code);
+
+ S32 sendMessage(const LLHost &host);
+ S32 sendMessage(const U32 circuit);
+
+ BOOL decodeData(const U8 *buffer, const LLHost &host);
+
+ // TODO: Consolide these functions
+ // TODO: Make these private, force use of typed functions.
+ // If size is not 0, an error is generated if size doesn't exactly match the size of the data.
+ // At all times, the number if bytes written to *datap is <= max_size.
+private:
+ void getDataFast(const char *blockname, const char *varname, void *datap, S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX);
+ void getData(const char *blockname, const char *varname, void *datap, S32 size = 0, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname), datap, size, blocknum, max_size);
+ }
+public:
+ void getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(blockname, varname, datap, size, blocknum, max_size);
+ }
+ void getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX)
+ {
+ getDataFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname), datap, size, blocknum, max_size);
+ }
+
+ void getBOOLFast( const char *block, const char *var, BOOL &data, S32 blocknum = 0);
+ void getBOOL( const char *block, const char *var, BOOL &data, S32 blocknum = 0);
+ void getS8Fast( const char *block, const char *var, S8 &data, S32 blocknum = 0);
+ void getS8( const char *block, const char *var, S8 &data, S32 blocknum = 0);
+ void getU8Fast( const char *block, const char *var, U8 &data, S32 blocknum = 0);
+ void getU8( const char *block, const char *var, U8 &data, S32 blocknum = 0);
+ void getS16Fast( const char *block, const char *var, S16 &data, S32 blocknum = 0);
+ void getS16( const char *block, const char *var, S16 &data, S32 blocknum = 0);
+ void getU16Fast( const char *block, const char *var, U16 &data, S32 blocknum = 0);
+ void getU16( const char *block, const char *var, U16 &data, S32 blocknum = 0);
+ void getS32Fast( const char *block, const char *var, S32 &data, S32 blocknum = 0);
+ void getS32( const char *block, const char *var, S32 &data, S32 blocknum = 0);
+ void getF32Fast( const char *block, const char *var, F32 &data, S32 blocknum = 0);
+ void getF32( const char *block, const char *var, F32 &data, S32 blocknum = 0);
+ virtual void getU32Fast( const char *block, const char *var, U32 &data, S32 blocknum = 0);
+ void getU32( const char *block, const char *var, U32 &data, S32 blocknum = 0);
+ virtual void getU64Fast( const char *block, const char *var, U64 &data, S32 blocknum = 0);
+ void getU64( const char *block, const char *var, U64 &data, S32 blocknum = 0);
+ void getF64Fast( const char *block, const char *var, F64 &data, S32 blocknum = 0);
+ void getF64( const char *block, const char *var, F64 &data, S32 blocknum = 0);
+ void getVector3Fast( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0);
+ void getVector3( const char *block, const char *var, LLVector3 &vec, S32 blocknum = 0);
+ void getVector4Fast( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0);
+ void getVector4( const char *block, const char *var, LLVector4 &vec, S32 blocknum = 0);
+ void getVector3dFast(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0);
+ void getVector3d(const char *block, const char *var, LLVector3d &vec, S32 blocknum = 0);
+ void getQuatFast( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0);
+ void getQuat( const char *block, const char *var, LLQuaternion &q, S32 blocknum = 0);
+ virtual void getUUIDFast( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0);
+ void getUUID( const char *block, const char *var, LLUUID &uuid, S32 blocknum = 0);
+ virtual void getIPAddrFast( const char *block, const char *var, U32 &ip, S32 blocknum = 0);
+ void getIPAddr( const char *block, const char *var, U32 &ip, S32 blocknum = 0);
+ virtual void getIPPortFast( const char *block, const char *var, U16 &port, S32 blocknum = 0);
+ void getIPPort( const char *block, const char *var, U16 &port, S32 blocknum = 0);
+ virtual void getStringFast( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0);
+ void getString( const char *block, const char *var, S32 buffer_size, char *buffer, S32 blocknum = 0);
+
+
+ // Utility functions to generate a replay-resistant digest check
+ // against the shared secret. The window specifies how much of a
+ // time window is allowed - 1 second is good for tight
+ // connections, but multi-process windows might want to be upwards
+ // of 5 seconds. For generateDigest, you want to pass in a
+ // character array of at least MD5HEX_STR_SIZE so that the hex
+ // digest and null termination will fit.
+ bool generateDigestForNumberAndUUIDs(char* digest, const U32 number, const LLUUID &id1, const LLUUID &id2) const;
+ bool generateDigestForWindowAndUUIDs(char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const;
+ bool isMatchingDigestForWindowAndUUIDs(const char* digest, const S32 window, const LLUUID &id1, const LLUUID &id2) const;
+
+ bool generateDigestForNumber(char* digest, const U32 number) const;
+ bool generateDigestForWindow(char* digest, const S32 window) const;
+ bool isMatchingDigestForWindow(const char* digest, const S32 window) const;
+
+ void showCircuitInfo();
+ LLString getCircuitInfoString();
+
+ virtual U32 getOurCircuitCode();
+
+ void enableCircuit(const LLHost &host, BOOL trusted);
+ void disableCircuit(const LLHost &host);
+
+ // Use this to establish trust on startup and in response to
+ // DenyTrustedCircuit.
+ void sendCreateTrustedCircuit(const LLHost& host, const LLUUID & id1, const LLUUID & id2);
+
+ // Use this to inform a peer that they aren't currently trusted...
+ // This now enqueues the request so that we can ensure that we only send
+ // one deny per circuit per message loop so that this doesn't become a DoS.
+ // The actual sending is done by reallySendDenyTrustedCircuit()
+ void sendDenyTrustedCircuit(const LLHost &host);
+
+private:
+ // A list of the circuits that need to be sent DenyTrustedCircuit messages.
+ typedef std::set<LLHost> host_set_t;
+ host_set_t mDenyTrustedCircuitSet;
+
+ // Really sends the DenyTrustedCircuit message to a given host
+ // related to sendDenyTrustedCircuit()
+ void reallySendDenyTrustedCircuit(const LLHost &host);
+
+
+public:
+ // Use this to establish trust to and from a host. This blocks
+ // until trust has been established, and probably should only be
+ // used on startup.
+ void establishBidirectionalTrust(const LLHost &host, S64 frame_count = 0);
+
+ // returns whether the given host is on a trusted circuit
+ BOOL getCircuitTrust(const LLHost &host);
+
+ void setCircuitAllowTimeout(const LLHost &host, BOOL allow);
+ void setCircuitTimeoutCallback(const LLHost &host, void (*callback_func)(const LLHost &host, void *user_data), void *user_data);
+
+ BOOL checkCircuitBlocked(const U32 circuit);
+ BOOL checkCircuitAlive(const U32 circuit);
+ BOOL checkCircuitAlive(const LLHost &host);
+ void setCircuitProtection(BOOL b_protect);
+ U32 findCircuitCode(const LLHost &host);
+ LLHost findHost(const U32 circuit_code);
+ void sanityCheck();
+
+ S32 getNumberOfBlocksFast(const char *blockname);
+ S32 getNumberOfBlocks(const char *blockname)
+ {
+ return getNumberOfBlocksFast(gMessageStringTable.getString(blockname));
+ }
+ S32 getSizeFast(const char *blockname, const char *varname);
+ S32 getSize(const char *blockname, const char *varname)
+ {
+ return getSizeFast(gMessageStringTable.getString(blockname), gMessageStringTable.getString(varname));
+ }
+ S32 getSizeFast(const char *blockname, S32 blocknum, const char *varname); // size in bytes of variable length data
+ S32 getSize(const char *blockname, S32 blocknum, const char *varname)
+ {
+ return getSizeFast(gMessageStringTable.getString(blockname), blocknum, gMessageStringTable.getString(varname));
+ }
+
+ void resetReceiveCounts(); // resets receive counts for all message types to 0
+ void dumpReceiveCounts(); // dumps receive count for each message type to llinfos
+ void dumpCircuitInfo(); // Circuit information to llinfos
+
+ BOOL isClear() const; // returns mbSClear;
+ S32 flush(const LLHost &host);
+
+ U32 getListenPort( void ) const;
+
+ void startLogging(); // start verbose logging
+ void stopLogging(); // flush and close file
+ void summarizeLogs(std::ostream& str); // log statistics
+
+ S32 getReceiveSize() const { return mReceiveSize; }
+ S32 getReceiveCompressedSize() const { return mIncomingCompressedSize; }
+ S32 getReceiveBytes() const;
+
+ S32 getUnackedListSize() const { return mUnackedListSize; }
+
+ const char* getCurrentSMessageName() const { return mCurrentSMessageName; }
+ const char* getCurrentSBlockName() const { return mCurrentSBlockName; }
+
+ // friends
+ friend std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg);
+
+ void setMaxMessageTime(const F32 seconds); // Max time to process messages before warning and dumping (neg to disable)
+ void setMaxMessageCounts(const S32 num); // Max number of messages before dumping (neg to disable)
+
+ // statics
+public:
+ static U64 getMessageTimeUsecs(const BOOL update = FALSE); // Get the current message system time in microseconds
+ static F64 getMessageTimeSeconds(const BOOL update = FALSE); // Get the current message system time in seconds
+
+ static void setTimeDecodes( BOOL b )
+ { LLMessageSystem::mTimeDecodes = b; }
+
+ static void setTimeDecodesSpamThreshold( F32 seconds )
+ { LLMessageSystem::mTimeDecodesSpamThreshold = seconds; }
+
+ // message handlers internal to the message systesm
+ //static void processAssignCircuitCode(LLMessageSystem* msg, void**);
+ static void processAddCircuitCode(LLMessageSystem* msg, void**);
+ static void processUseCircuitCode(LLMessageSystem* msg, void**);
+
+ void setMessageBans(const LLSD& trusted, const LLSD& untrusted);
+
+private:
+ // data used in those internal handlers
+
+ // The mCircuitCodes is a map from circuit codes to session
+ // ids. This allows us to verify sessions on connect.
+ typedef std::map<U32, LLUUID> code_session_map_t;
+ code_session_map_t mCircuitCodes;
+
+ // Viewers need to track a process session in order to make sure
+ // that no one gives them a bad circuit code.
+ LLUUID mSessionID;
+
+private:
+ void addTemplate(LLMessageTemplate *templatep);
+ void clearReceiveState();
+ BOOL decodeTemplate( const U8* buffer, S32 buffer_size, LLMessageTemplate** msg_template );
+
+ void logMsgFromInvalidCircuit( const LLHost& sender, BOOL recv_reliable );
+ void logTrustedMsgFromUntrustedCircuit( const LLHost& sender );
+ void logValidMsg(LLCircuitData *cdp, const LLHost& sender, BOOL recv_reliable, BOOL recv_resent, BOOL recv_acks );
+ void logRanOffEndOfPacket( const LLHost& sender );
+
+private:
+ class LLMessageCountInfo
+ {
+ public:
+ U32 mMessageNum;
+ U32 mMessageBytes;
+ BOOL mInvalid;
+ };
+
+ LLMessagePollInfo *mPollInfop;
+
+ U8 mEncodedRecvBuffer[MAX_BUFFER_SIZE];
+ U8 mTrueReceiveBuffer[MAX_BUFFER_SIZE];
+ S32 mTrueReceiveSize;
+
+ // Must be valid during decode
+ S32 mReceiveSize;
+ TPACKETID mCurrentRecvPacketID; // packet ID of current receive packet (for reporting)
+ LLMessageTemplate *mCurrentRMessageTemplate;
+ LLMsgData *mCurrentRMessageData;
+ S32 mIncomingCompressedSize; // original size of compressed msg (0 if uncomp.)
+ LLHost mLastSender;
+
+ // send message storage
+ LLMsgData *mCurrentSMessageData;
+ LLMessageTemplate *mCurrentSMessageTemplate;
+ LLMsgBlkData *mCurrentSDataBlock;
+ char *mCurrentSMessageName;
+ char *mCurrentSBlockName;
+
+ BOOL mbError;
+ S32 mErrorCode;
+
+ BOOL mbSBuilt; // is send message built?
+ BOOL mbSClear; // is the send message clear?
+
+ F64 mResendDumpTime; // The last time we dumped resends
+
+ LLMessageCountInfo mMessageCountList[MAX_MESSAGE_COUNT_NUM];
+ S32 mNumMessageCounts;
+ F32 mReceiveTime;
+ F32 mMaxMessageTime; // Max number of seconds for processing messages
+ S32 mMaxMessageCounts; // Max number of messages to process before dumping.
+ F64 mMessageCountTime;
+
+ F64 mCurrentMessageTimeSeconds; // The current "message system time" (updated the first call to checkMessages after a resetReceiveCount
+
+ // message system exceptions
+ typedef std::pair<msg_exception_callback, void*> exception_t;
+ typedef std::map<EMessageException, exception_t> callbacks_t;
+ callbacks_t mExceptionCallbacks;
+
+ // stuff for logging
+ LLTimer mMessageSystemTimer;
+
+ static F32 mTimeDecodesSpamThreshold; // If mTimeDecodes is on, all this many seconds for each msg decode before spamming
+ static BOOL mTimeDecodes; // Measure time for all message decodes if TRUE;
+
+ void LLMessageSystem::init(); // ctor shared initialisation.
+};
+
+
+// external hook into messaging system
+extern LLMessageSystem *gMessageSystem;
+//extern const char* MESSAGE_LOG_FILENAME;
+
+void encrypt_template(const char *src_name, const char *dest_name);
+BOOL decrypt_template(const char *src_name, const char *dest_name);
+
+// Must specific overall system version, which is used to determine
+// if a patch is available in the message template checksum verification.
+// Return TRUE if able to initialize system.
+BOOL start_messaging_system(
+ const std::string& template_name,
+ U32 port,
+ S32 version_major,
+ S32 version_minor,
+ S32 version_patch,
+ BOOL b_dump_prehash_file,
+ const std::string& secret);
+
+void end_messaging_system();
+
+void null_message_callback(LLMessageSystem *msg, void **data);
+void process_log_control(LLMessageSystem* msg, void**);
+
+//
+// Inlines
+//
+
+static inline void *htonmemcpy(void *vs, const void *vct, EMsgVariableType type, size_t n)
+{
+ char *s = (char *)vs;
+ const char *ct = (const char *)vct;
+#ifdef LL_BIG_ENDIAN
+ S32 i, length;
+#endif
+ switch(type)
+ {
+ case MVT_FIXED:
+ case MVT_VARIABLE:
+ case MVT_U8:
+ case MVT_S8:
+ case MVT_BOOL:
+ case MVT_LLUUID:
+ case MVT_IP_ADDR: // these two are swizzled in the getters and setters
+ case MVT_IP_PORT: // these two are swizzled in the getters and setters
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+
+ case MVT_U16:
+ case MVT_S16:
+ if (n != 2)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 1) = *(ct);
+ *(s) = *(ct + 1);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U32:
+ case MVT_S32:
+ case MVT_F32:
+ if (n != 4)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 3) = *(ct);
+ *(s + 2) = *(ct + 1);
+ *(s + 1) = *(ct + 2);
+ *(s) = *(ct + 3);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U64:
+ case MVT_S64:
+ case MVT_F64:
+ if (n != 8)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ *(s + 7) = *(ct);
+ *(s + 6) = *(ct + 1);
+ *(s + 5) = *(ct + 2);
+ *(s + 4) = *(ct + 3);
+ *(s + 3) = *(ct + 4);
+ *(s + 2) = *(ct + 5);
+ *(s + 1) = *(ct + 6);
+ *(s) = *(ct + 7);
+ return(vs);
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector3:
+ case MVT_LLQuaternion: // We only send x, y, z and infer w (we set x, y, z to ensure that w >= 0)
+ if (n != 12)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 8, ct + 8, MVT_F32, 4);
+ htonmemcpy(s + 4, ct + 4, MVT_F32, 4);
+ return(htonmemcpy(s, ct, MVT_F32, 4));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector3d:
+ if (n != 24)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 16, ct + 16, MVT_F64, 8);
+ htonmemcpy(s + 8, ct + 8, MVT_F64, 8);
+ return(htonmemcpy(s, ct, MVT_F64, 8));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_LLVector4:
+ if (n != 16)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 12, ct + 12, MVT_F32, 4);
+ htonmemcpy(s + 8, ct + 8, MVT_F32, 4);
+ htonmemcpy(s + 4, ct + 4, MVT_F32, 4);
+ return(htonmemcpy(s, ct, MVT_F32, 4));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U16Vec3:
+ if (n != 6)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 4, ct + 4, MVT_U16, 2);
+ htonmemcpy(s + 2, ct + 2, MVT_U16, 2);
+ return(htonmemcpy(s, ct, MVT_U16, 2));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_U16Quat:
+ if (n != 8)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ htonmemcpy(s + 6, ct + 6, MVT_U16, 2);
+ htonmemcpy(s + 4, ct + 4, MVT_U16, 2);
+ htonmemcpy(s + 2, ct + 2, MVT_U16, 2);
+ return(htonmemcpy(s, ct, MVT_U16, 2));
+#else
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+#endif
+
+ case MVT_S16Array:
+ if (n % 2)
+ {
+ llerrs << "Size argument passed to htonmemcpy doesn't match swizzle type size" << llendl;
+ }
+#ifdef LL_BIG_ENDIAN
+ length = n % 2;
+ for (i = 1; i < length; i++)
+ {
+ htonmemcpy(s + i*2, ct + i*2, MVT_S16, 2);
+ }
+ return(htonmemcpy(s, ct, MVT_S16, 2));
+#else
+ return(memcpy(s,ct,n));
+#endif
+
+ default:
+ return(memcpy(s,ct,n)); /* Flawfinder: ignore */
+ }
+}
+
+inline void *ntohmemcpy(void *s, const void *ct, EMsgVariableType type, size_t n)
+{
+ return(htonmemcpy(s,ct,type, n));
+}
+
+
+inline const LLHost& LLMessageSystem::getSender() const
+{
+ return mLastSender;
+}
+
+inline U32 LLMessageSystem::getSenderIP() const
+{
+ return mLastSender.getAddress();
+}
+
+inline U32 LLMessageSystem::getSenderPort() const
+{
+ return mLastSender.getPort();
+}
+
+inline void LLMessageSystem::addS8Fast(const char *varname, S8 s)
+{
+ addDataFast(varname, &s, MVT_S8, sizeof(s));
+}
+
+inline void LLMessageSystem::addS8(const char *varname, S8 s)
+{
+ addDataFast(gMessageStringTable.getString(varname), &s, MVT_S8, sizeof(s));
+}
+
+inline void LLMessageSystem::addU8Fast(const char *varname, U8 u)
+{
+ addDataFast(varname, &u, MVT_U8, sizeof(u));
+}
+
+inline void LLMessageSystem::addU8(const char *varname, U8 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_U8, sizeof(u));
+}
+
+inline void LLMessageSystem::addS16Fast(const char *varname, S16 i)
+{
+ addDataFast(varname, &i, MVT_S16, sizeof(i));
+}
+
+inline void LLMessageSystem::addS16(const char *varname, S16 i)
+{
+ addDataFast(gMessageStringTable.getString(varname), &i, MVT_S16, sizeof(i));
+}
+
+inline void LLMessageSystem::addU16Fast(const char *varname, U16 i)
+{
+ addDataFast(varname, &i, MVT_U16, sizeof(i));
+}
+
+inline void LLMessageSystem::addU16(const char *varname, U16 i)
+{
+ addDataFast(gMessageStringTable.getString(varname), &i, MVT_U16, sizeof(i));
+}
+
+inline void LLMessageSystem::addF32Fast(const char *varname, F32 f)
+{
+ addDataFast(varname, &f, MVT_F32, sizeof(f));
+}
+
+inline void LLMessageSystem::addF32(const char *varname, F32 f)
+{
+ addDataFast(gMessageStringTable.getString(varname), &f, MVT_F32, sizeof(f));
+}
+
+inline void LLMessageSystem::addS32Fast(const char *varname, S32 s)
+{
+ addDataFast(varname, &s, MVT_S32, sizeof(s));
+}
+
+inline void LLMessageSystem::addS32(const char *varname, S32 s)
+{
+ addDataFast(gMessageStringTable.getString(varname), &s, MVT_S32, sizeof(s));
+}
+
+inline void LLMessageSystem::addU32Fast(const char *varname, U32 u)
+{
+ addDataFast(varname, &u, MVT_U32, sizeof(u));
+}
+
+inline void LLMessageSystem::addU32(const char *varname, U32 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_U32, sizeof(u));
+}
+
+inline void LLMessageSystem::addU64Fast(const char *varname, U64 lu)
+{
+ addDataFast(varname, &lu, MVT_U64, sizeof(lu));
+}
+
+inline void LLMessageSystem::addU64(const char *varname, U64 lu)
+{
+ addDataFast(gMessageStringTable.getString(varname), &lu, MVT_U64, sizeof(lu));
+}
+
+inline void LLMessageSystem::addF64Fast(const char *varname, F64 d)
+{
+ addDataFast(varname, &d, MVT_F64, sizeof(d));
+}
+
+inline void LLMessageSystem::addF64(const char *varname, F64 d)
+{
+ addDataFast(gMessageStringTable.getString(varname), &d, MVT_F64, sizeof(d));
+}
+
+inline void LLMessageSystem::addIPAddrFast(const char *varname, U32 u)
+{
+ addDataFast(varname, &u, MVT_IP_ADDR, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPAddr(const char *varname, U32 u)
+{
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_IP_ADDR, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPPortFast(const char *varname, U16 u)
+{
+ u = htons(u);
+ addDataFast(varname, &u, MVT_IP_PORT, sizeof(u));
+}
+
+inline void LLMessageSystem::addIPPort(const char *varname, U16 u)
+{
+ u = htons(u);
+ addDataFast(gMessageStringTable.getString(varname), &u, MVT_IP_PORT, sizeof(u));
+}
+
+inline void LLMessageSystem::addBOOLFast(const char* varname, BOOL b)
+{
+ // Can't just cast a BOOL (actually a U32) to a U8.
+ // In some cases the low order bits will be zero.
+ U8 temp = (b != 0);
+ addDataFast(varname, &temp, MVT_BOOL, sizeof(temp));
+}
+
+inline void LLMessageSystem::addBOOL(const char* varname, BOOL b)
+{
+ // Can't just cast a BOOL (actually a U32) to a U8.
+ // In some cases the low order bits will be zero.
+ U8 temp = (b != 0);
+ addDataFast(gMessageStringTable.getString(varname), &temp, MVT_BOOL, sizeof(temp));
+}
+
+inline void LLMessageSystem::addStringFast(const char* varname, const char* s)
+{
+ if (s)
+ addDataFast( varname, (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */
+ else
+ addDataFast( varname, NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addString(const char* varname, const char* s)
+{
+ if (s)
+ addDataFast( gMessageStringTable.getString(varname), (void *)s, MVT_VARIABLE, (S32)strlen(s) + 1); /* Flawfinder: ignore */
+ else
+ addDataFast( gMessageStringTable.getString(varname), NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addStringFast(const char* varname, const std::string& s)
+{
+ if (s.size())
+ addDataFast( varname, (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1);
+ else
+ addDataFast( varname, NULL, MVT_VARIABLE, 0);
+}
+
+inline void LLMessageSystem::addString(const char* varname, const std::string& s)
+{
+ if (s.size())
+ addDataFast( gMessageStringTable.getString(varname), (void *)s.c_str(), MVT_VARIABLE, (S32)(s.size()) + 1);
+ else
+ addDataFast( gMessageStringTable.getString(varname), NULL, MVT_VARIABLE, 0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Retrieval aliases
+//-----------------------------------------------------------------------------
+inline void LLMessageSystem::getS8Fast(const char *block, const char *var, S8 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(S8), blocknum);
+}
+
+inline void LLMessageSystem::getS8(const char *block, const char *var, S8 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(S8), blocknum);
+}
+
+inline void LLMessageSystem::getU8Fast(const char *block, const char *var, U8 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U8), blocknum);
+}
+
+inline void LLMessageSystem::getU8(const char *block, const char *var, U8 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U8), blocknum);
+}
+
+inline void LLMessageSystem::getBOOLFast(const char *block, const char *var, BOOL &b, S32 blocknum )
+{
+ U8 value;
+ getDataFast(block, var, &value, sizeof(U8), blocknum);
+ b = (BOOL) value;
+}
+
+inline void LLMessageSystem::getBOOL(const char *block, const char *var, BOOL &b, S32 blocknum )
+{
+ U8 value;
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &value, sizeof(U8), blocknum);
+ b = (BOOL) value;
+}
+
+inline void LLMessageSystem::getS16Fast(const char *block, const char *var, S16 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(S16), blocknum);
+}
+
+inline void LLMessageSystem::getS16(const char *block, const char *var, S16 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(S16), blocknum);
+}
+
+inline void LLMessageSystem::getU16Fast(const char *block, const char *var, U16 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U16), blocknum);
+}
+
+inline void LLMessageSystem::getU16(const char *block, const char *var, U16 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U16), blocknum);
+}
+
+inline void LLMessageSystem::getS32Fast(const char *block, const char *var, S32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(S32), blocknum);
+}
+
+inline void LLMessageSystem::getS32(const char *block, const char *var, S32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(S32), blocknum);
+}
+
+inline void LLMessageSystem::getU32Fast(const char *block, const char *var, U32 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getU32(const char *block, const char *var, U32 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getU64Fast(const char *block, const char *var, U64 &d, S32 blocknum)
+{
+ getDataFast(block, var, &d, sizeof(U64), blocknum);
+}
+
+inline void LLMessageSystem::getU64(const char *block, const char *var, U64 &d, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &d, sizeof(U64), blocknum);
+}
+
+
+inline void LLMessageSystem::getIPAddrFast(const char *block, const char *var, U32 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getIPAddr(const char *block, const char *var, U32 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U32), blocknum);
+}
+
+inline void LLMessageSystem::getIPPortFast(const char *block, const char *var, U16 &u, S32 blocknum)
+{
+ getDataFast(block, var, &u, sizeof(U16), blocknum);
+ u = ntohs(u);
+}
+
+inline void LLMessageSystem::getIPPort(const char *block, const char *var, U16 &u, S32 blocknum)
+{
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), &u, sizeof(U16), blocknum);
+ u = ntohs(u);
+}
+
+
+inline void LLMessageSystem::getStringFast(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum )
+{
+ s[0] = '\0';
+ getDataFast(block, var, s, 0, blocknum, buffer_size);
+ s[buffer_size - 1] = '\0';
+}
+
+inline void LLMessageSystem::getString(const char *block, const char *var, S32 buffer_size, char *s, S32 blocknum )
+{
+ s[0] = '\0';
+ getDataFast(gMessageStringTable.getString(block), gMessageStringTable.getString(var), s, 0, blocknum, buffer_size);
+ s[buffer_size - 1] = '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Transmission aliases
+//-----------------------------------------------------------------------------
+//inline S32 LLMessageSystem::sendMessage(U32 ip, U32 port, BOOL zero_code)
+//{
+// return sendMessage(LLHost(ip, port), zero_code);
+//}
+
+//inline S32 LLMessageSystem::sendMessage(const char *ip_str, U32 port, BOOL zero_code)
+//{
+// return sendMessage(LLHost(ip_str, port), zero_code);
+//}
+
+inline S32 LLMessageSystem::sendMessage(const U32 circuit)//, BOOL zero_code)
+{
+ return sendMessage(findHost(circuit));//, zero_code);
+}
+
+#endif
diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp
new file mode 100644
index 0000000000..18be31af58
--- /dev/null
+++ b/indra/llmessage/message_prehash.cpp
@@ -0,0 +1,2964 @@
+/**
+ * @file message_prehash.cpp
+ * @brief file of prehashed variables
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Generated from message template version number 1.053
+ */
+#include "linden_common.h"
+#include "message.h"
+
+
+
+F32 gPrehashVersionNumber = 1.053f;
+
+char * _PREHASH_X;
+char * _PREHASH_Y;
+char * _PREHASH_Z;
+char * _PREHASH_AddFlags;
+char * _PREHASH_ReservedNewbie;
+char * _PREHASH_FailureInfo;
+char * _PREHASH_MapData;
+char * _PREHASH_AddItem;
+char * _PREHASH_MeanCollision;
+char * _PREHASH_RezScript;
+char * _PREHASH_AvatarSitResponse;
+char * _PREHASH_InventoryAssetResponse;
+char * _PREHASH_KillObject;
+char * _PREHASH_ProposalID;
+char * _PREHASH_SerialNum;
+char * _PREHASH_Duration;
+char * _PREHASH_ScriptQuestion;
+char * _PREHASH_AddCircuitCode;
+char * _PREHASH_UseCircuitCode;
+char * _PREHASH_ViewerCircuitCode;
+char * _PREHASH_ScriptAnswerYes;
+char * _PREHASH_PartnerID;
+char * _PREHASH_DirLandQuery;
+char * _PREHASH_TeleportStart;
+char * _PREHASH_LogMessages;
+char * _PREHASH_AboutText;
+char * _PREHASH_VisualParam;
+char * _PREHASH_GroupPrims;
+char * _PREHASH_SelectedPrims;
+char * _PREHASH_ID;
+char * _PREHASH_UUIDNameRequest;
+char * _PREHASH_UUIDGroupNameRequest;
+char * _PREHASH_MoneyTransactionsRequest;
+char * _PREHASH_GroupAccountTransactionsRequest;
+char * _PREHASH_MapNameRequest;
+char * _PREHASH_MailTaskSimRequest;
+char * _PREHASH_UpdateSimulator;
+char * _PREHASH_BillableFactor;
+char * _PREHASH_ObjectBonusFactor;
+char * _PREHASH_EnableSimulator;
+char * _PREHASH_DisableSimulator;
+char * _PREHASH_ConfirmEnableSimulator;
+char * _PREHASH_LayerType;
+char * _PREHASH_OwnerRole;
+char * _PREHASH_ParcelOverlay;
+char * _PREHASH_AdjustBalance;
+char * _PREHASH_GroupOwned;
+char * _PREHASH_IP;
+char * _PREHASH_ChatFromViewer;
+char * _PREHASH_AvgAgentsInView;
+char * _PREHASH_AgentsInView;
+char * _PREHASH_GroupTitle;
+char * _PREHASH_MapLayerReply;
+char * _PREHASH_CompoundMsgID;
+char * _PREHASH_CameraConstraint;
+char * _PREHASH_DownloadTotals;
+char * _PREHASH_GenCounter;
+char * _PREHASH_FrozenData;
+char * _PREHASH_ChildAgentDying;
+char * _PREHASH_To;
+char * _PREHASH_CopyInventoryFromNotecard;
+char * _PREHASH_RezObjectFromNotecard;
+char * _PREHASH_ParcelDirFeeCurrent;
+char * _PREHASH_SeedCapability;
+char * _PREHASH_ObjectDuplicate;
+char * _PREHASH_InventoryData;
+char * _PREHASH_ReplyData;
+char * _PREHASH_ResetList;
+char * _PREHASH_MediaID;
+char * _PREHASH_RelatedRights;
+char * _PREHASH_RedirectGridX;
+char * _PREHASH_RedirectGridY;
+char * _PREHASH_TransferID;
+char * _PREHASH_Transacted;
+char * _PREHASH_TexturesChanged;
+char * _PREHASH_UserLookAt;
+char * _PREHASH_TestBlock1;
+char * _PREHASH_SensedData;
+char * _PREHASH_UpdateBlock;
+char * _PREHASH_ClassifiedGodDelete;
+char * _PREHASH_ObjectGrabUpdate;
+char * _PREHASH_TaxDate;
+char * _PREHASH_LocationPos;
+char * _PREHASH_StartDateTime;
+char * _PREHASH_ObjectUpdateCached;
+char * _PREHASH_Packets;
+char * _PREHASH_FailureType;
+char * _PREHASH_UpdateGroupInfo;
+char * _PREHASH_ObjectPermissions;
+char * _PREHASH_RevokePermissions;
+char * _PREHASH_UpdateFlags;
+char * _PREHASH_ObjectExportSelected;
+char * _PREHASH_RezSelected;
+char * _PREHASH_AutoPilot;
+char * _PREHASH_UpdateMuteListEntry;
+char * _PREHASH_RemoveMuteListEntry;
+char * _PREHASH_SetSimStatusInDatabase;
+char * _PREHASH_SetSimPresenceInDatabase;
+char * _PREHASH_CameraProperty;
+char * _PREHASH_BrushSize;
+char * _PREHASH_StartExpungeProcess;
+char * _PREHASH_SimulatorSetMap;
+char * _PREHASH_RegionPresenceRequestByRegionID;
+char * _PREHASH_ParcelObjectOwnersReply;
+char * _PREHASH_GroupMembersReply;
+char * _PREHASH_GroupRoleMembersReply;
+char * _PREHASH_RequestRegionInfo;
+char * _PREHASH_AABBMax;
+char * _PREHASH_RequestPayPrice;
+char * _PREHASH_SimulatorPresentAtLocation;
+char * _PREHASH_AgentRequestSit;
+char * _PREHASH_AABBMin;
+char * _PREHASH_ClassifiedFlags;
+char * _PREHASH_ControlFlags;
+char * _PREHASH_TeleportRequest;
+char * _PREHASH_SpaceLocationTeleportRequest;
+char * _PREHASH_LeaderBoardRequest;
+char * _PREHASH_ScriptTeleportRequest;
+char * _PREHASH_DateUTC;
+char * _PREHASH_TaskIDs;
+char * _PREHASH_EstateCovenantRequest;
+char * _PREHASH_RequestResult;
+char * _PREHASH_ReputationAgentAssign;
+char * _PREHASH_CanAcceptAgents;
+char * _PREHASH_ObjectSaleInfo;
+char * _PREHASH_KillChildAgents;
+char * _PREHASH_Balance;
+char * _PREHASH_DerezContainer;
+char * _PREHASH_ObjectData;
+char * _PREHASH_CameraAtAxis;
+char * _PREHASH_InfoBlock;
+char * _PREHASH_OwnershipCost;
+char * _PREHASH_AvatarNotesUpdate;
+char * _PREHASH_PID;
+char * _PREHASH_TimeString;
+char * _PREHASH_DirPopularReply;
+char * _PREHASH_TerrainHeightRange00;
+char * _PREHASH_SimData;
+char * _PREHASH_TerrainHeightRange01;
+char * _PREHASH_TerrainHeightRange10;
+char * _PREHASH_TerrainHeightRange11;
+char * _PREHASH_UpdateInventoryItem;
+char * _PREHASH_UpdateCreateInventoryItem;
+char * _PREHASH_MoveInventoryItem;
+char * _PREHASH_CopyInventoryItem;
+char * _PREHASH_RemoveInventoryItem;
+char * _PREHASH_CreateInventoryItem;
+char * _PREHASH_PathTwistBegin;
+char * _PREHASH_CRC;
+char * _PREHASH_AttachmentPoint;
+char * _PREHASH_TelehubBlock;
+char * _PREHASH_FOVBlock;
+char * _PREHASH_StartLocationData;
+char * _PREHASH_PositionData;
+char * _PREHASH_TimeSinceLast;
+char * _PREHASH_MapImage;
+char * _PREHASH_Objects;
+char * _PREHASH_URL;
+char * _PREHASH_CreationDate;
+char * _PREHASH_JointPivot;
+char * _PREHASH_RateeID;
+char * _PREHASH_FPS;
+char * _PREHASH_HasTelehub;
+char * _PREHASH_PathEnd;
+char * _PREHASH_ScriptDataReply;
+char * _PREHASH_MapBlockReply;
+char * _PREHASH_PropertiesData;
+char * _PREHASH_ViewerEffect;
+char * _PREHASH_FreezeUser;
+char * _PREHASH_OwnerPrims;
+char * _PREHASH_ObjectGrab;
+char * _PREHASH_ToAgentID;
+char * _PREHASH_SimulatorMapUpdate;
+char * _PREHASH_TransferPacket;
+char * _PREHASH_ObjectName;
+char * _PREHASH_GroupPowers;
+char * _PREHASH_OriginalName;
+char * _PREHASH_CompletePingCheck;
+char * _PREHASH_OnlineStatus;
+char * _PREHASH_ObjectDrop;
+char * _PREHASH_UseBigPackets;
+char * _PREHASH_GroupNoticesListReply;
+char * _PREHASH_ParcelAccessListReply;
+char * _PREHASH_RpcChannelReply;
+char * _PREHASH_RegionPresenceResponse;
+char * _PREHASH_AgentPresenceResponse;
+char * _PREHASH_CharterMember;
+char * _PREHASH_EdgeData;
+char * _PREHASH_NameData;
+char * _PREHASH_RegionPushOverride;
+char * _PREHASH_SimName;
+char * _PREHASH_UserReport;
+char * _PREHASH_DownloadPriority;
+char * _PREHASH_ToAgentId;
+char * _PREHASH_Mag;
+char * _PREHASH_DirPopularQuery;
+char * _PREHASH_ParcelPropertiesRequestByID;
+char * _PREHASH_ObjectLink;
+char * _PREHASH_RpcScriptReplyInbound;
+char * _PREHASH_BoardData;
+char * _PREHASH_RezData;
+char * _PREHASH_RemoveInventoryObjects;
+char * _PREHASH_GroupProposalBallot;
+char * _PREHASH_RPCServerIP;
+char * _PREHASH_Far;
+char * _PREHASH_GodSessionID;
+char * _PREHASH_ViewerDigest;
+char * _PREHASH_FLAboutText;
+char * _PREHASH_RegionHandshakeReply;
+char * _PREHASH_GroupActiveProposalItemReply;
+char * _PREHASH_MapItemReply;
+char * _PREHASH_Seconds;
+char * _PREHASH_UpdateUserInfo;
+char * _PREHASH_AggregatePermTexturesOwner;
+char * _PREHASH_Set;
+char * _PREHASH_NewName;
+char * _PREHASH_Key;
+char * _PREHASH_AgentID;
+char * _PREHASH_OnlineStatusRequest;
+char * _PREHASH_EventNotificationRemoveRequest;
+char * _PREHASH_NewFolderID;
+char * _PREHASH_Arc;
+char * _PREHASH_RegionX;
+char * _PREHASH_RegionY;
+char * _PREHASH_RequestData;
+char * _PREHASH_Msg;
+char * _PREHASH_Top;
+char * _PREHASH_MiscStats;
+char * _PREHASH_ImageID;
+char * _PREHASH_DataPacket;
+char * _PREHASH_ObjectDehinge;
+char * _PREHASH_You;
+char * _PREHASH_ScriptControlChange;
+char * _PREHASH_LoadURL;
+char * _PREHASH_SetCPURatio;
+char * _PREHASH_NameValueData;
+char * _PREHASH_AtomicPassObject;
+char * _PREHASH_ErrorMessage;
+char * _PREHASH_ViewerFrozenMessage;
+char * _PREHASH_HealthMessage;
+char * _PREHASH_LogTextMessage;
+char * _PREHASH_TimeDilation;
+char * _PREHASH_RemoveContribution;
+char * _PREHASH_Contribution;
+char * _PREHASH_SetGroupContribution;
+char * _PREHASH_Offline;
+char * _PREHASH_AgentIsNowWearing;
+char * _PREHASH_SecPerDay;
+char * _PREHASH_Members;
+char * _PREHASH_FailedResends;
+char * _PREHASH_CameraCenter;
+char * _PREHASH_CameraLeftAxis;
+char * _PREHASH_ExBlock;
+char * _PREHASH_Channel;
+char * _PREHASH_NetTest;
+char * _PREHASH_DiscardLevel;
+char * _PREHASH_LayerID;
+char * _PREHASH_RatorID;
+char * _PREHASH_GrabOffset;
+char * _PREHASH_SimPort;
+char * _PREHASH_PricePerMeter;
+char * _PREHASH_RegionFlags;
+char * _PREHASH_VoteResult;
+char * _PREHASH_ParcelDirFeeEstimate;
+char * _PREHASH_ModifyBlock;
+char * _PREHASH_InventoryBlock;
+char * _PREHASH_ReplyBlock;
+char * _PREHASH_ValidUntil;
+char * _PREHASH_VelocityInterpolateOn;
+char * _PREHASH_ClassifiedDelete;
+char * _PREHASH_RegionDenyAnonymous;
+char * _PREHASH_FLImageID;
+char * _PREHASH_AllowPublish;
+char * _PREHASH_SitName;
+char * _PREHASH_RegionsVisited;
+char * _PREHASH_DirClassifiedReply;
+char * _PREHASH_AvatarClassifiedReply;
+char * _PREHASH_ReputationIndividualReply;
+char * _PREHASH_MediaURL;
+char * _PREHASH_CompleteAgentMovement;
+char * _PREHASH_SpaceIP;
+char * _PREHASH_ClassifiedID;
+char * _PREHASH_LocalID;
+char * _PREHASH_RemoveItem;
+char * _PREHASH_LogFailedMoneyTransaction;
+char * _PREHASH_ViewerStartAuction;
+char * _PREHASH_StartAuction;
+char * _PREHASH_NameValueName;
+char * _PREHASH_AngVelX;
+char * _PREHASH_DuplicateFlags;
+char * _PREHASH_AngVelY;
+char * _PREHASH_AngVelZ;
+char * _PREHASH_TextColor;
+char * _PREHASH_SlaveID;
+char * _PREHASH_Charter;
+char * _PREHASH_AlertData;
+char * _PREHASH_TargetBlock;
+char * _PREHASH_CheckParcelAuctions;
+char * _PREHASH_ParcelAuctions;
+char * _PREHASH_OwnerIsGroup;
+char * _PREHASH_NameValuePair;
+char * _PREHASH_RemoveNameValuePair;
+char * _PREHASH_GetNameValuePair;
+char * _PREHASH_BulkUpdateInventory;
+char * _PREHASH_UpdateTaskInventory;
+char * _PREHASH_RemoveTaskInventory;
+char * _PREHASH_MoveTaskInventory;
+char * _PREHASH_RequestTaskInventory;
+char * _PREHASH_ReplyTaskInventory;
+char * _PREHASH_DeclineInventory;
+char * _PREHASH_AggregatePermInventory;
+char * _PREHASH_SimulatorInfo;
+char * _PREHASH_MoneyTransactionsReply;
+char * _PREHASH_GroupAccountTransactionsReply;
+char * _PREHASH_MailTaskSimReply;
+char * _PREHASH_WearableData;
+char * _PREHASH_StatisticsData;
+char * _PREHASH_Enabled;
+char * _PREHASH_Savings;
+char * _PREHASH_SimulatorLoad;
+char * _PREHASH_InternalRegionIP;
+char * _PREHASH_ExternalRegionIP;
+char * _PREHASH_TotalPairs;
+char * _PREHASH_CreateGroupRequest;
+char * _PREHASH_JoinGroupRequest;
+char * _PREHASH_LeaveGroupRequest;
+char * _PREHASH_InviteGroupRequest;
+char * _PREHASH_LiveHelpGroupRequest;
+char * _PREHASH_ServerVersion;
+char * _PREHASH_PriceParcelClaimFactor;
+char * _PREHASH_BillableArea;
+char * _PREHASH_ObjectID;
+char * _PREHASH_ObjectFlagUpdate;
+char * _PREHASH_GroupRoleUpdate;
+char * _PREHASH_RequestInventoryAsset;
+char * _PREHASH_RedoLand;
+char * _PREHASH_TravelAccess;
+char * _PREHASH_ChangedGrid;
+char * _PREHASH_AgentDropGroup;
+char * _PREHASH_Details;
+char * _PREHASH_LocationX;
+char * _PREHASH_SaleType;
+char * _PREHASH_LocationY;
+char * _PREHASH_LocationZ;
+char * _PREHASH_EconomyData;
+char * _PREHASH_HeadRotation;
+char * _PREHASH_DeleteOnCompletion;
+char * _PREHASH_PublicPort;
+char * _PREHASH_DirClassifiedQuery;
+char * _PREHASH_CallbackID;
+char * _PREHASH_RequestParcelTransfer;
+char * _PREHASH_RoleCount;
+char * _PREHASH_ObjectCapacity;
+char * _PREHASH_RequestID;
+char * _PREHASH_RequestXfer;
+char * _PREHASH_ObjectTaxCurrent;
+char * _PREHASH_LightTaxCurrent;
+char * _PREHASH_LandTaxCurrent;
+char * _PREHASH_GroupTaxCurrent;
+char * _PREHASH_FetchInventoryDescendents;
+char * _PREHASH_InventoryDescendents;
+char * _PREHASH_Descendents;
+char * _PREHASH_PurgeInventoryDescendents;
+char * _PREHASH_ShowDir;
+char * _PREHASH_IsOwner;
+char * _PREHASH_Timestamp;
+char * _PREHASH_GlobalPos;
+char * _PREHASH_GrabOffsetInitial;
+char * _PREHASH_IsTrial;
+char * _PREHASH_FinalizeLogout;
+char * _PREHASH_ObjectDuplicateOnRay;
+char * _PREHASH_GroupMembershipCount;
+char * _PREHASH_MethodData;
+char * _PREHASH_ActivateGestures;
+char * _PREHASH_DeactivateGestures;
+char * _PREHASH_ProposalData;
+char * _PREHASH_PosGlobal;
+char * _PREHASH_SearchID;
+char * _PREHASH_RezMultipleAttachmentsFromInv;
+char * _PREHASH_SearchName;
+char * _PREHASH_VersionString;
+char * _PREHASH_CreateGroupReply;
+char * _PREHASH_LeaveGroupReply;
+char * _PREHASH_ActualArea;
+char * _PREHASH_Message;
+char * _PREHASH_ClickAction;
+char * _PREHASH_AssetUploadComplete;
+char * _PREHASH_RequestType;
+char * _PREHASH_UUID;
+char * _PREHASH_BaseMask;
+char * _PREHASH_NetBlock;
+char * _PREHASH_GlobalX;
+char * _PREHASH_GlobalY;
+char * _PREHASH_CopyRotates;
+char * _PREHASH_KickUserAck;
+char * _PREHASH_TopPick;
+char * _PREHASH_SessionID;
+char * _PREHASH_GlobalZ;
+char * _PREHASH_DeclineFriendship;
+char * _PREHASH_FormFriendship;
+char * _PREHASH_TerminateFriendship;
+char * _PREHASH_TaskData;
+char * _PREHASH_SimWideMaxPrims;
+char * _PREHASH_TotalPrims;
+char * _PREHASH_SourceFilename;
+char * _PREHASH_ProfileBegin;
+char * _PREHASH_MoneyDetailsRequest;
+char * _PREHASH_Request;
+char * _PREHASH_GroupAccountDetailsRequest;
+char * _PREHASH_GroupActiveProposalsRequest;
+char * _PREHASH_StringValue;
+char * _PREHASH_ClosestSimulator;
+char * _PREHASH_Version;
+char * _PREHASH_OtherCount;
+char * _PREHASH_MemberCount;
+char * _PREHASH_ChatData;
+char * _PREHASH_IsGroupOwned;
+char * _PREHASH_EnergyEfficiency;
+char * _PREHASH_MaxPlace;
+char * _PREHASH_PickInfoUpdate;
+char * _PREHASH_PickDelete;
+char * _PREHASH_ScriptReset;
+char * _PREHASH_Requester;
+char * _PREHASH_ForSale;
+char * _PREHASH_NearestLandingRegionReply;
+char * _PREHASH_RecordAgentPresence;
+char * _PREHASH_EraseAgentPresence;
+char * _PREHASH_ParcelID;
+char * _PREHASH_Godlike;
+char * _PREHASH_TotalDebits;
+char * _PREHASH_Direction;
+char * _PREHASH_Appearance;
+char * _PREHASH_HealthData;
+char * _PREHASH_LeftAxis;
+char * _PREHASH_LocationBlock;
+char * _PREHASH_ObjectImage;
+char * _PREHASH_TerrainStartHeight00;
+char * _PREHASH_TerrainStartHeight01;
+char * _PREHASH_TerrainStartHeight10;
+char * _PREHASH_ObjectHinge;
+char * _PREHASH_TerrainStartHeight11;
+char * _PREHASH_MetersPerGrid;
+char * _PREHASH_WaterHeight;
+char * _PREHASH_FetchInventoryReply;
+char * _PREHASH_MoneySummaryReply;
+char * _PREHASH_GroupAccountSummaryReply;
+char * _PREHASH_AttachedSound;
+char * _PREHASH_ParamInUse;
+char * _PREHASH_GodKickUser;
+char * _PREHASH_PickName;
+char * _PREHASH_TaskName;
+char * _PREHASH_ParcelGodReserveForNewbie;
+char * _PREHASH_SubType;
+char * _PREHASH_ObjectCount;
+char * _PREHASH_RegionPresenceRequestByHandle;
+char * _PREHASH_RezSingleAttachmentFromInv;
+char * _PREHASH_ChildAgentUpdate;
+char * _PREHASH_ToID;
+char * _PREHASH_ViewerPort;
+char * _PREHASH_IsOwnerGroup;
+char * _PREHASH_AgentHeightWidth;
+char * _PREHASH_VerticalAngle;
+char * _PREHASH_WearableType;
+char * _PREHASH_AggregatePermNextOwner;
+char * _PREHASH_ShowInList;
+char * _PREHASH_PositionSuggestion;
+char * _PREHASH_UpdateParcel;
+char * _PREHASH_ClearAgentSessions;
+char * _PREHASH_SetAlwaysRun;
+char * _PREHASH_NVPair;
+char * _PREHASH_ObjectSpinStart;
+char * _PREHASH_UseEstateSun;
+char * _PREHASH_LogoutBlock;
+char * _PREHASH_RelayLogControl;
+char * _PREHASH_RegionID;
+char * _PREHASH_Creator;
+char * _PREHASH_ProposalText;
+char * _PREHASH_DirEventsReply;
+char * _PREHASH_EventInfoReply;
+char * _PREHASH_UserInfoReply;
+char * _PREHASH_PathRadiusOffset;
+char * _PREHASH_SessionInfo;
+char * _PREHASH_TextureData;
+char * _PREHASH_ChatPass;
+char * _PREHASH_TargetID;
+char * _PREHASH_DefaultPayPrice;
+char * _PREHASH_UserLocation;
+char * _PREHASH_MaxPrims;
+char * _PREHASH_RegionIP;
+char * _PREHASH_LandmarkID;
+char * _PREHASH_InitiateDownload;
+char * _PREHASH_Name;
+char * _PREHASH_OtherCleanTime;
+char * _PREHASH_ParcelSetOtherCleanTime;
+char * _PREHASH_TeleportPriceExponent;
+char * _PREHASH_Gain;
+char * _PREHASH_VelX;
+char * _PREHASH_PacketAck;
+char * _PREHASH_PathSkew;
+char * _PREHASH_Negative;
+char * _PREHASH_VelY;
+char * _PREHASH_SimulatorShutdownRequest;
+char * _PREHASH_NearestLandingRegionRequest;
+char * _PREHASH_VelZ;
+char * _PREHASH_OtherID;
+char * _PREHASH_MemberID;
+char * _PREHASH_MapLayerRequest;
+char * _PREHASH_PatchVersion;
+char * _PREHASH_ObjectScale;
+char * _PREHASH_TargetIP;
+char * _PREHASH_Redo;
+char * _PREHASH_MoneyBalance;
+char * _PREHASH_TrackAgent;
+char * _PREHASH_MaxX;
+char * _PREHASH_Data;
+char * _PREHASH_MaxY;
+char * _PREHASH_TextureAnim;
+char * _PREHASH_ReturnIDs;
+char * _PREHASH_Date;
+char * _PREHASH_GestureUpdate;
+char * _PREHASH_AgentWearablesUpdate;
+char * _PREHASH_AgentDataUpdate;
+char * _PREHASH_Hash;
+char * _PREHASH_GroupDataUpdate;
+char * _PREHASH_AgentGroupDataUpdate;
+char * _PREHASH_Left;
+char * _PREHASH_Mask;
+char * _PREHASH_ForceMouselook;
+char * _PREHASH_Success;
+char * _PREHASH_ObjectGroup;
+char * _PREHASH_SunHour;
+char * _PREHASH_MinX;
+char * _PREHASH_ScriptSensorReply;
+char * _PREHASH_MinY;
+char * _PREHASH_Command;
+char * _PREHASH_Desc;
+char * _PREHASH_AttachmentNeedsSave;
+char * _PREHASH_HistoryItemData;
+char * _PREHASH_AgentCachedTexture;
+char * _PREHASH_Subject;
+char * _PREHASH_East;
+char * _PREHASH_GodExpungeUser;
+char * _PREHASH_QueryReplies;
+char * _PREHASH_ObjectCategory;
+char * _PREHASH_Time;
+char * _PREHASH_CreateLandmarkForEvent;
+char * _PREHASH_ParentID;
+char * _PREHASH_Ping;
+char * _PREHASH_Perp;
+char * _PREHASH_Code;
+char * _PREHASH_InvType;
+char * _PREHASH_AgentFOV;
+char * _PREHASH_BulkMoneyTransfer;
+char * _PREHASH_Audible;
+char * _PREHASH_AuctionData;
+char * _PREHASH_IDBlock;
+char * _PREHASH_ReputationData;
+char * _PREHASH_West;
+char * _PREHASH_Undo;
+char * _PREHASH_TotalNumItems;
+char * _PREHASH_Info;
+char * _PREHASH_Area;
+char * _PREHASH_Behavior;
+char * _PREHASH_SimCrashed;
+char * _PREHASH_Text;
+char * _PREHASH_AgentToNewRegion;
+char * _PREHASH_PriceGroupCreate;
+char * _PREHASH_ObjectShape;
+char * _PREHASH_GroupRoleDataReply;
+char * _PREHASH_PosX;
+char * _PREHASH_PosY;
+char * _PREHASH_MuteCRC;
+char * _PREHASH_PosZ;
+char * _PREHASH_Size;
+char * _PREHASH_FromAddress;
+char * _PREHASH_Body;
+char * _PREHASH_FileData;
+char * _PREHASH_List;
+char * _PREHASH_KickUser;
+char * _PREHASH_OtherPrims;
+char * _PREHASH_RunTime;
+char * _PREHASH_GrantUserRights;
+char * _PREHASH_RpcScriptRequestInboundForward;
+char * _PREHASH_More;
+char * _PREHASH_Majority;
+char * _PREHASH_MetersTraveled;
+char * _PREHASH_Stat;
+char * _PREHASH_SoundID;
+char * _PREHASH_Item;
+char * _PREHASH_User;
+char * _PREHASH_RemoteInfos;
+char * _PREHASH_Prey;
+char * _PREHASH_UsecSinceStart;
+char * _PREHASH_RayStart;
+char * _PREHASH_ParcelData;
+char * _PREHASH_CameraUpAxis;
+char * _PREHASH_ScriptDialog;
+char * _PREHASH_MasterParcelData;
+char * _PREHASH_Invalid;
+char * _PREHASH_MinPlace;
+char * _PREHASH_ProfileCurve;
+char * _PREHASH_ParcelAccessListUpdate;
+char * _PREHASH_MuteListUpdate;
+char * _PREHASH_SendPacket;
+char * _PREHASH_SendXferPacket;
+char * _PREHASH_RegionDenyIdentified;
+char * _PREHASH_NotecardItemID;
+char * _PREHASH_LastName;
+char * _PREHASH_From;
+char * _PREHASH_RoleChange;
+char * _PREHASH_Port;
+char * _PREHASH_MemberTitle;
+char * _PREHASH_LogParcelChanges;
+char * _PREHASH_AgentCachedTextureResponse;
+char * _PREHASH_DeRezObject;
+char * _PREHASH_IsTemporary;
+char * _PREHASH_InsigniaID;
+char * _PREHASH_CheckFlags;
+char * _PREHASH_TransferPriority;
+char * _PREHASH_EventID;
+char * _PREHASH_Selected;
+char * _PREHASH_FromAgentId;
+char * _PREHASH_Type;
+char * _PREHASH_ChatType;
+char * _PREHASH_ReportData;
+char * _PREHASH_LeaderBoardData;
+char * _PREHASH_RequestBlock;
+char * _PREHASH_GrantData;
+char * _PREHASH_DetachAttachmentIntoInv;
+char * _PREHASH_ParcelDisableObjects;
+char * _PREHASH_Sections;
+char * _PREHASH_GodLevel;
+char * _PREHASH_PayPriceReply;
+char * _PREHASH_QueryID;
+char * _PREHASH_CameraEyeOffset;
+char * _PREHASH_AgentPosition;
+char * _PREHASH_GrabPosition;
+char * _PREHASH_OnlineNotification;
+char * _PREHASH_OfflineNotification;
+char * _PREHASH_SendPostcard;
+char * _PREHASH_RequestFlags;
+char * _PREHASH_MoneyHistoryRequest;
+char * _PREHASH_MoneySummaryRequest;
+char * _PREHASH_GroupAccountSummaryRequest;
+char * _PREHASH_GroupVoteHistoryRequest;
+char * _PREHASH_ParamValue;
+char * _PREHASH_Checksum;
+char * _PREHASH_MaxAgents;
+char * _PREHASH_CreateNewOutfitAttachments;
+char * _PREHASH_RegionHandle;
+char * _PREHASH_TeleportProgress;
+char * _PREHASH_AgentQuitCopy;
+char * _PREHASH_ToViewer;
+char * _PREHASH_AvatarInterestsUpdate;
+char * _PREHASH_GroupNoticeID;
+char * _PREHASH_ParcelName;
+char * _PREHASH_PriceObjectRent;
+char * _PREHASH_ConnectAgentToUserserver;
+char * _PREHASH_ConnectToUserserver;
+char * _PREHASH_OfferCallingCard;
+char * _PREHASH_AgentAccess;
+char * _PREHASH_AcceptCallingCard;
+char * _PREHASH_DeclineCallingCard;
+char * _PREHASH_DataHomeLocationReply;
+char * _PREHASH_EventLocationReply;
+char * _PREHASH_TerseDateID;
+char * _PREHASH_ObjectOwner;
+char * _PREHASH_AssetID;
+char * _PREHASH_AlertMessage;
+char * _PREHASH_AgentAlertMessage;
+char * _PREHASH_EstateOwnerMessage;
+char * _PREHASH_ParcelMediaCommandMessage;
+char * _PREHASH_Auction;
+char * _PREHASH_Category;
+char * _PREHASH_FilePath;
+char * _PREHASH_ItemFlags;
+char * _PREHASH_Invoice;
+char * _PREHASH_IntervalDays;
+char * _PREHASH_PathScaleX;
+char * _PREHASH_FromTaskID;
+char * _PREHASH_TimeInfo;
+char * _PREHASH_PathScaleY;
+char * _PREHASH_PublicCount;
+char * _PREHASH_ParcelJoin;
+char * _PREHASH_GroupRolesCount;
+char * _PREHASH_SimulatorBlock;
+char * _PREHASH_GroupID;
+char * _PREHASH_AgentVel;
+char * _PREHASH_RequestImage;
+char * _PREHASH_NetStats;
+char * _PREHASH_AgentPos;
+char * _PREHASH_AgentSit;
+char * _PREHASH_Material;
+char * _PREHASH_ObjectDeGrab;
+char * _PREHASH_VelocityInterpolateOff;
+char * _PREHASH_AuthorizedBuyerID;
+char * _PREHASH_AvatarPropertiesReply;
+char * _PREHASH_GroupProfileReply;
+char * _PREHASH_SimOwner;
+char * _PREHASH_SalePrice;
+char * _PREHASH_Animation;
+char * _PREHASH_OwnerID;
+char * _PREHASH_NearestLandingRegionUpdated;
+char * _PREHASH_PassToAgent;
+char * _PREHASH_PreyAgent;
+char * _PREHASH_SimStats;
+char * _PREHASH_Options;
+char * _PREHASH_LogoutReply;
+char * _PREHASH_FeatureDisabled;
+char * _PREHASH_ObjectLocalID;
+char * _PREHASH_Dropped;
+char * _PREHASH_WebProfilesDisabled;
+char * _PREHASH_Destination;
+char * _PREHASH_MasterID;
+char * _PREHASH_TransferData;
+char * _PREHASH_WantToMask;
+char * _PREHASH_AvatarData;
+char * _PREHASH_ParcelSelectObjects;
+char * _PREHASH_ExtraParams;
+char * _PREHASH_LogLogin;
+char * _PREHASH_CreatorID;
+char * _PREHASH_Summary;
+char * _PREHASH_BuyObjectInventory;
+char * _PREHASH_FetchInventory;
+char * _PREHASH_InventoryID;
+char * _PREHASH_PacketNumber;
+char * _PREHASH_SetFollowCamProperties;
+char * _PREHASH_ClearFollowCamProperties;
+char * _PREHASH_SequenceID;
+char * _PREHASH_DataServerLogout;
+char * _PREHASH_NameValue;
+char * _PREHASH_PathShearX;
+char * _PREHASH_PathShearY;
+char * _PREHASH_Velocity;
+char * _PREHASH_SecPerYear;
+char * _PREHASH_FirstName;
+char * _PREHASH_AttachedSoundGainChange;
+char * _PREHASH_LocationID;
+char * _PREHASH_Running;
+char * _PREHASH_AgentThrottle;
+char * _PREHASH_NeighborList;
+char * _PREHASH_PathTaperX;
+char * _PREHASH_PathTaperY;
+char * _PREHASH_AgentRelated;
+char * _PREHASH_GranterBlock;
+char * _PREHASH_UseCachedMuteList;
+char * _PREHASH_FailStats;
+char * _PREHASH_Tempfile;
+char * _PREHASH_BuyerID;
+char * _PREHASH_DirPeopleReply;
+char * _PREHASH_TransferInfo;
+char * _PREHASH_AvatarPickerRequestBackend;
+char * _PREHASH_AvatarPropertiesRequestBackend;
+char * _PREHASH_UpdateData;
+char * _PREHASH_SimFPS;
+char * _PREHASH_ReporterID;
+char * _PREHASH_ButtonLabel;
+char * _PREHASH_GranterID;
+char * _PREHASH_WantToText;
+char * _PREHASH_ReportType;
+char * _PREHASH_DataBlock;
+char * _PREHASH_SimulatorReady;
+char * _PREHASH_AnimationSourceList;
+char * _PREHASH_SubscribeLoad;
+char * _PREHASH_UnsubscribeLoad;
+char * _PREHASH_Packet;
+char * _PREHASH_UndoLand;
+char * _PREHASH_SimAccess;
+char * _PREHASH_MembershipFee;
+char * _PREHASH_InviteGroupResponse;
+char * _PREHASH_CreateInventoryFolder;
+char * _PREHASH_UpdateInventoryFolder;
+char * _PREHASH_MoveInventoryFolder;
+char * _PREHASH_RemoveInventoryFolder;
+char * _PREHASH_MoneyData;
+char * _PREHASH_ObjectDeselect;
+char * _PREHASH_NewAssetID;
+char * _PREHASH_ObjectAdd;
+char * _PREHASH_RayEndIsIntersection;
+char * _PREHASH_CompleteAuction;
+char * _PREHASH_CircuitCode;
+char * _PREHASH_AgentMovementComplete;
+char * _PREHASH_ViewerIP;
+char * _PREHASH_Header;
+char * _PREHASH_GestureFlags;
+char * _PREHASH_XferID;
+char * _PREHASH_StatValue;
+char * _PREHASH_PickID;
+char * _PREHASH_TaskID;
+char * _PREHASH_GridsPerEdge;
+char * _PREHASH_RayEnd;
+char * _PREHASH_Throttles;
+char * _PREHASH_RebakeAvatarTextures;
+char * _PREHASH_UpAxis;
+char * _PREHASH_AgentTextures;
+char * _PREHASH_NotecardData;
+char * _PREHASH_Radius;
+char * _PREHASH_OffCircuit;
+char * _PREHASH_Access;
+char * _PREHASH_TitleRoleID;
+char * _PREHASH_SquareMetersCredit;
+char * _PREHASH_Filename;
+char * _PREHASH_SecuredTemplateChecksumRequest;
+char * _PREHASH_TemplateChecksumRequest;
+char * _PREHASH_AgentPresenceRequest;
+char * _PREHASH_ClassifiedInfoRequest;
+char * _PREHASH_ParcelInfoRequest;
+char * _PREHASH_ParcelObjectOwnersRequest;
+char * _PREHASH_TeleportLandmarkRequest;
+char * _PREHASH_EventInfoRequest;
+char * _PREHASH_ChatFromSimulator;
+char * _PREHASH_PickInfoRequest;
+char * _PREHASH_MoneyBalanceRequest;
+char * _PREHASH_GroupMembersRequest;
+char * _PREHASH_GroupRoleMembersRequest;
+char * _PREHASH_OldFolderID;
+char * _PREHASH_UserInfoRequest;
+char * _PREHASH_TextureID;
+char * _PREHASH_ProfileURL;
+char * _PREHASH_Handle;
+char * _PREHASH_StartParcelRenameAck;
+char * _PREHASH_ButtonIndex;
+char * _PREHASH_GetScriptRunning;
+char * _PREHASH_SetScriptRunning;
+char * _PREHASH_Health;
+char * _PREHASH_FileID;
+char * _PREHASH_CircuitInfo;
+char * _PREHASH_ObjectBuy;
+char * _PREHASH_ProfileEnd;
+char * _PREHASH_Effect;
+char * _PREHASH_TestMessage;
+char * _PREHASH_ScriptMailRegistration;
+char * _PREHASH_AgentSetAppearance;
+char * _PREHASH_AvatarAppearance;
+char * _PREHASH_RegionData;
+char * _PREHASH_RequestingRegionData;
+char * _PREHASH_LandingRegionData;
+char * _PREHASH_SitTransform;
+char * _PREHASH_TerrainBase0;
+char * _PREHASH_SkillsMask;
+char * _PREHASH_AtAxis;
+char * _PREHASH_TerrainBase1;
+char * _PREHASH_Reason;
+char * _PREHASH_TerrainBase2;
+char * _PREHASH_TerrainBase3;
+char * _PREHASH_Params;
+char * _PREHASH_PingID;
+char * _PREHASH_Change;
+char * _PREHASH_Height;
+char * _PREHASH_Region;
+char * _PREHASH_MoneyHistoryReply;
+char * _PREHASH_TelehubInfo;
+char * _PREHASH_StateSave;
+char * _PREHASH_RoleData;
+char * _PREHASH_AgentAnimation;
+char * _PREHASH_AvatarAnimation;
+char * _PREHASH_LogDwellTime;
+char * _PREHASH_ParcelGodMarkAsContent;
+char * _PREHASH_UsePhysics;
+char * _PREHASH_RegionDenyTransacted;
+char * _PREHASH_JointType;
+char * _PREHASH_TaxEstimate;
+char * _PREHASH_ObjectTaxEstimate;
+char * _PREHASH_LightTaxEstimate;
+char * _PREHASH_TeleportLandingStatusChanged;
+char * _PREHASH_LandTaxEstimate;
+char * _PREHASH_GroupTaxEstimate;
+char * _PREHASH_AvgViewerFPS;
+char * _PREHASH_Buttons;
+char * _PREHASH_Sender;
+char * _PREHASH_Dialog;
+char * _PREHASH_TargetData;
+char * _PREHASH_DestID;
+char * _PREHASH_PricePublicObjectDelete;
+char * _PREHASH_ObjectDelete;
+char * _PREHASH_Delete;
+char * _PREHASH_EventGodDelete;
+char * _PREHASH_LastTaxDate;
+char * _PREHASH_MapImageID;
+char * _PREHASH_EndDateTime;
+char * _PREHASH_TerrainDetail0;
+char * _PREHASH_TerrainDetail1;
+char * _PREHASH_TerrainDetail2;
+char * _PREHASH_TerrainDetail3;
+char * _PREHASH_Offset;
+char * _PREHASH_ObjectDelink;
+char * _PREHASH_TargetObject;
+char * _PREHASH_IsEstateManager;
+char * _PREHASH_CancelAuction;
+char * _PREHASH_ObjectDetach;
+char * _PREHASH_Compressed;
+char * _PREHASH_PathBegin;
+char * _PREHASH_BypassRaycast;
+char * _PREHASH_WinnerID;
+char * _PREHASH_ChannelType;
+char * _PREHASH_NonExemptMembers;
+char * _PREHASH_Agents;
+char * _PREHASH_SimulatorStart;
+char * _PREHASH_Enable;
+char * _PREHASH_MemberData;
+char * _PREHASH_ToGroupID;
+char * _PREHASH_ImageNotInDatabase;
+char * _PREHASH_StartDate;
+char * _PREHASH_AnimID;
+char * _PREHASH_Serial;
+char * _PREHASH_ControlPort;
+char * _PREHASH_ModifyLand;
+char * _PREHASH_Digest;
+char * _PREHASH_Victim;
+char * _PREHASH_Script;
+char * _PREHASH_TemplateChecksumReply;
+char * _PREHASH_PickInfoReply;
+char * _PREHASH_MoneyBalanceReply;
+char * _PREHASH_RoutedMoneyBalanceReply;
+char * _PREHASH_RoleID;
+char * _PREHASH_RegionInfo;
+char * _PREHASH_Sequence;
+char * _PREHASH_GodUpdateRegionInfo;
+char * _PREHASH_LocalX;
+char * _PREHASH_LocalY;
+char * _PREHASH_StartAnim;
+char * _PREHASH_Location;
+char * _PREHASH_Action;
+char * _PREHASH_Rights;
+char * _PREHASH_SearchDir;
+char * _PREHASH_Active;
+char * _PREHASH_TransferRequest;
+char * _PREHASH_ScriptSensorRequest;
+char * _PREHASH_MoneyTransferRequest;
+char * _PREHASH_EjectGroupMemberRequest;
+char * _PREHASH_SkillsText;
+char * _PREHASH_Resent;
+char * _PREHASH_Center;
+char * _PREHASH_SharedData;
+char * _PREHASH_PSBlock;
+char * _PREHASH_UUIDNameBlock;
+char * _PREHASH_Viewer;
+char * _PREHASH_GroupNoticeDelete;
+char * _PREHASH_GroupTitleUpdate;
+char * _PREHASH_Method;
+char * _PREHASH_TouchName;
+char * _PREHASH_UpdateType;
+char * _PREHASH_KickedFromEstateID;
+char * _PREHASH_CandidateID;
+char * _PREHASH_ParamData;
+char * _PREHASH_GodlikeMessage;
+char * _PREHASH_SystemMessage;
+char * _PREHASH_BodyRotation;
+char * _PREHASH_SearchRegions;
+char * _PREHASH_Ignore;
+char * _PREHASH_AnimationData;
+char * _PREHASH_StatID;
+char * _PREHASH_ItemID;
+char * _PREHASH_AvatarStatisticsReply;
+char * _PREHASH_ScriptDialogReply;
+char * _PREHASH_RegionIDAndHandleReply;
+char * _PREHASH_CameraAtOffset;
+char * _PREHASH_VoteID;
+char * _PREHASH_ParcelGodForceOwner;
+char * _PREHASH_Filter;
+char * _PREHASH_InviteData;
+char * _PREHASH_PCode;
+char * _PREHASH_SearchPos;
+char * _PREHASH_PreyID;
+char * _PREHASH_TerrainLowerLimit;
+char * _PREHASH_EventFlags;
+char * _PREHASH_TallyVotes;
+char * _PREHASH_Result;
+char * _PREHASH_LookAt;
+char * _PREHASH_PayButton;
+char * _PREHASH_SelfCount;
+char * _PREHASH_PacketCount;
+char * _PREHASH_ParcelBuyPass;
+char * _PREHASH_Identified;
+char * _PREHASH_OldItemID;
+char * _PREHASH_RegionPort;
+char * _PREHASH_PriceEnergyUnit;
+char * _PREHASH_Bitmap;
+char * _PREHASH_TrackAgentSession;
+char * _PREHASH_CacheMissType;
+char * _PREHASH_VFileID;
+char * _PREHASH_GroupInsigniaID;
+char * _PREHASH_FromID;
+char * _PREHASH_Online;
+char * _PREHASH_KickFlags;
+char * _PREHASH_CovenantID;
+char * _PREHASH_SysCPU;
+char * _PREHASH_EMail;
+char * _PREHASH_AggregatePermTextures;
+char * _PREHASH_ChatChannel;
+char * _PREHASH_ReturnID;
+char * _PREHASH_ObjectAttach;
+char * _PREHASH_TargetPort;
+char * _PREHASH_ObjectSpinStop;
+char * _PREHASH_FullID;
+char * _PREHASH_ActivateGroup;
+char * _PREHASH_SysGPU;
+char * _PREHASH_AvatarInterestsReply;
+char * _PREHASH_StartLure;
+char * _PREHASH_SysRAM;
+char * _PREHASH_ObjectPosition;
+char * _PREHASH_SitPosition;
+char * _PREHASH_StartTime;
+char * _PREHASH_BornOn;
+char * _PREHASH_CameraCollidePlane;
+char * _PREHASH_EconomyDataRequest;
+char * _PREHASH_TeleportLureRequest;
+char * _PREHASH_FolderID;
+char * _PREHASH_RegionHandleRequest;
+char * _PREHASH_GestureRequest;
+char * _PREHASH_ScriptDataRequest;
+char * _PREHASH_GroupRoleDataRequest;
+char * _PREHASH_GroupTitlesRequest;
+char * _PREHASH_AgentWearablesRequest;
+char * _PREHASH_MapBlockRequest;
+char * _PREHASH_LureID;
+char * _PREHASH_CopyCenters;
+char * _PREHASH_ParamList;
+char * _PREHASH_InventorySerial;
+char * _PREHASH_EdgeDataPacket;
+char * _PREHASH_AvatarPickerReply;
+char * _PREHASH_ParcelDwellReply;
+char * _PREHASH_IsForSale;
+char * _PREHASH_MuteID;
+char * _PREHASH_MeanCollisionAlert;
+char * _PREHASH_CanAcceptTasks;
+char * _PREHASH_ItemData;
+char * _PREHASH_AnimationList;
+char * _PREHASH_PassObject;
+char * _PREHASH_Reputation;
+char * _PREHASH_IntValue;
+char * _PREHASH_TargetType;
+char * _PREHASH_Amount;
+char * _PREHASH_HasAttachment;
+char * _PREHASH_UpdateAttachment;
+char * _PREHASH_RemoveAttachment;
+char * _PREHASH_HeightWidthBlock;
+char * _PREHASH_RequestObjectPropertiesFamily;
+char * _PREHASH_ObjectPropertiesFamily;
+char * _PREHASH_UserData;
+char * _PREHASH_IsReadable;
+char * _PREHASH_PathCurve;
+char * _PREHASH_Status;
+char * _PREHASH_FromGroup;
+char * _PREHASH_AlreadyVoted;
+char * _PREHASH_PlacesReply;
+char * _PREHASH_DirPlacesReply;
+char * _PREHASH_ParcelBuy;
+char * _PREHASH_DirFindQueryBackend;
+char * _PREHASH_DirPlacesQueryBackend;
+char * _PREHASH_DirClassifiedQueryBackend;
+char * _PREHASH_DirPicksQueryBackend;
+char * _PREHASH_DirLandQueryBackend;
+char * _PREHASH_DirPopularQueryBackend;
+char * _PREHASH_LogoutDemand;
+char * _PREHASH_HistoryData;
+char * _PREHASH_SnapshotID;
+char * _PREHASH_Aspect;
+char * _PREHASH_ParamSize;
+char * _PREHASH_VoteCast;
+char * _PREHASH_CastsShadows;
+char * _PREHASH_EveryoneMask;
+char * _PREHASH_SetSunPhase;
+char * _PREHASH_ObjectSpinUpdate;
+char * _PREHASH_MaturePublish;
+char * _PREHASH_UseExistingAsset;
+char * _PREHASH_Powers;
+char * _PREHASH_ParcelLocalID;
+char * _PREHASH_TeleportCancel;
+char * _PREHASH_UnixTime;
+char * _PREHASH_QueryFlags;
+char * _PREHASH_LastExecFroze;
+char * _PREHASH_AlwaysRun;
+char * _PREHASH_Bottom;
+char * _PREHASH_ButtonData;
+char * _PREHASH_SoundData;
+char * _PREHASH_ViewerStats;
+char * _PREHASH_RegionHandshake;
+char * _PREHASH_ObjectDescription;
+char * _PREHASH_Description;
+char * _PREHASH_ParamType;
+char * _PREHASH_UUIDNameReply;
+char * _PREHASH_UUIDGroupNameReply;
+char * _PREHASH_SaveAssetIntoInventory;
+char * _PREHASH_UserInfo;
+char * _PREHASH_AnimSequenceID;
+char * _PREHASH_NVPairs;
+char * _PREHASH_GroupNoticesListRequest;
+char * _PREHASH_ParcelAccessListRequest;
+char * _PREHASH_MuteListRequest;
+char * _PREHASH_StartPeriod;
+char * _PREHASH_RpcChannelRequest;
+char * _PREHASH_LandStatRequest;
+char * _PREHASH_PlacesQuery;
+char * _PREHASH_DirPlacesQuery;
+char * _PREHASH_SortOrder;
+char * _PREHASH_Hunter;
+char * _PREHASH_SunAngVelocity;
+char * _PREHASH_BinaryBucket;
+char * _PREHASH_ImagePacket;
+char * _PREHASH_StartGroupProposal;
+char * _PREHASH_EnergyLevel;
+char * _PREHASH_PriceForListing;
+char * _PREHASH_Scale;
+char * _PREHASH_EstateCovenantReply;
+char * _PREHASH_ParentEstateID;
+char * _PREHASH_Extra2;
+char * _PREHASH_Throttle;
+char * _PREHASH_SimIP;
+char * _PREHASH_GodID;
+char * _PREHASH_TeleportMinPrice;
+char * _PREHASH_VoteItem;
+char * _PREHASH_ObjectRotation;
+char * _PREHASH_SitRotation;
+char * _PREHASH_SnapSelection;
+char * _PREHASH_SoundTrigger;
+char * _PREHASH_TerrainRaiseLimit;
+char * _PREHASH_Quorum;
+char * _PREHASH_TokenBlock;
+char * _PREHASH_AgentBlock;
+char * _PREHASH_CommandBlock;
+char * _PREHASH_PricePublicObjectDecay;
+char * _PREHASH_SpawnPointPos;
+char * _PREHASH_AttachedSoundCutoffRadius;
+char * _PREHASH_VolumeDetail;
+char * _PREHASH_FromAgentName;
+char * _PREHASH_Range;
+char * _PREHASH_DirectoryVisibility;
+char * _PREHASH_PublicIP;
+char * _PREHASH_TeleportFailed;
+char * _PREHASH_OnlineStatusReply;
+char * _PREHASH_RequestAvatarInfo;
+char * _PREHASH_PreloadSound;
+char * _PREHASH_ScreenshotID;
+char * _PREHASH_CovenantTimestamp;
+char * _PREHASH_OldestUnacked;
+char * _PREHASH_SimulatorIP;
+char * _PREHASH_ObjectImport;
+char * _PREHASH_Value;
+char * _PREHASH_JointAxisOrAnchor;
+char * _PREHASH_Test0;
+char * _PREHASH_Test1;
+char * _PREHASH_Test2;
+char * _PREHASH_SunPhase;
+char * _PREHASH_Place;
+char * _PREHASH_Phase;
+char * _PREHASH_ParcelDivide;
+char * _PREHASH_PriceObjectClaim;
+char * _PREHASH_Field;
+char * _PREHASH_Ratio;
+char * _PREHASH_JoinGroupReply;
+char * _PREHASH_LiveHelpGroupReply;
+char * _PREHASH_Score;
+char * _PREHASH_ExpungeData;
+char * _PREHASH_Image;
+char * _PREHASH_ObjectClickAction;
+char * _PREHASH_Delta;
+char * _PREHASH_InitiateUpload;
+char * _PREHASH_Parameter;
+char * _PREHASH_Flags;
+char * _PREHASH_Plane;
+char * _PREHASH_Width;
+char * _PREHASH_Right;
+char * _PREHASH_DirFindQuery;
+char * _PREHASH_Textures;
+char * _PREHASH_EventData;
+char * _PREHASH_Final;
+char * _PREHASH_TelehubPos;
+char * _PREHASH_ReportAutosaveCrash;
+char * _PREHASH_Reset;
+char * _PREHASH_CreateTrustedCircuit;
+char * _PREHASH_DenyTrustedCircuit;
+char * _PREHASH_RequestTrustedCircuit;
+char * _PREHASH_Codec;
+char * _PREHASH_Level;
+char * _PREHASH_Modal;
+char * _PREHASH_ChildAgentUnknown;
+char * _PREHASH_LandingType;
+char * _PREHASH_ScriptRunningReply;
+char * _PREHASH_MoneyDetailsReply;
+char * _PREHASH_Reply;
+char * _PREHASH_TelehubRot;
+char * _PREHASH_RequestFriendship;
+char * _PREHASH_AcceptFriendship;
+char * _PREHASH_GroupAccountDetailsReply;
+char * _PREHASH_DwellInfo;
+char * _PREHASH_AgentResume;
+char * _PREHASH_ItemType;
+char * _PREHASH_MailFilter;
+char * _PREHASH_Disconnect;
+char * _PREHASH_SimPosition;
+char * _PREHASH_SimWideTotalPrims;
+char * _PREHASH_Index;
+char * _PREHASH_BaseFilename;
+char * _PREHASH_SimFilename;
+char * _PREHASH_LastOwnerID;
+char * _PREHASH_GroupNoticeRequest;
+char * _PREHASH_EmailMessageRequest;
+char * _PREHASH_MapItemRequest;
+char * _PREHASH_AgentCount;
+char * _PREHASH_MessageBlock;
+char * _PREHASH_FuseBlock;
+char * _PREHASH_AgentGroupData;
+char * _PREHASH_ClassifiedInfoUpdate;
+char * _PREHASH_RegionPos;
+char * _PREHASH_ParcelMediaUpdate;
+char * _PREHASH_NoticeID;
+char * _PREHASH_GridX;
+char * _PREHASH_GridY;
+char * _PREHASH_Title;
+char * _PREHASH_AuctionID;
+char * _PREHASH_VoteType;
+char * _PREHASH_CategoryID;
+char * _PREHASH_Token;
+char * _PREHASH_AggregatePerms;
+char * _PREHASH_StartParcelRemoveAck;
+char * _PREHASH_ObjectSelect;
+char * _PREHASH_ForceObjectSelect;
+char * _PREHASH_Price;
+char * _PREHASH_SunDirection;
+char * _PREHASH_FromName;
+char * _PREHASH_ChangeInventoryItemFlags;
+char * _PREHASH_Force;
+char * _PREHASH_TransactionBlock;
+char * _PREHASH_PowersMask;
+char * _PREHASH_Stamp;
+char * _PREHASH_TotalCredits;
+char * _PREHASH_State;
+char * _PREHASH_TextureIndex;
+char * _PREHASH_InviteeID;
+char * _PREHASH_ParcelReclaim;
+char * _PREHASH_Money;
+char * _PREHASH_PathTwist;
+char * _PREHASH_AuthBuyerID;
+char * _PREHASH_Color;
+char * _PREHASH_SourceType;
+char * _PREHASH_World;
+char * _PREHASH_QueryData;
+char * _PREHASH_Users;
+char * _PREHASH_SysOS;
+char * _PREHASH_Notes;
+char * _PREHASH_AvatarID;
+char * _PREHASH_FounderID;
+char * _PREHASH_EndPointID;
+char * _PREHASH_StipendEstimate;
+char * _PREHASH_LocationLookAt;
+char * _PREHASH_Sound;
+char * _PREHASH_Cover;
+char * _PREHASH_TotalObjectCount;
+char * _PREHASH_TextureEntry;
+char * _PREHASH_SquareMetersCommitted;
+char * _PREHASH_ChannelID;
+char * _PREHASH_Dwell;
+char * _PREHASH_North;
+char * _PREHASH_AgentUpdate;
+char * _PREHASH_PickGodDelete;
+char * _PREHASH_HostName;
+char * _PREHASH_PriceParcelClaim;
+char * _PREHASH_ParcelClaim;
+char * _PREHASH_AgentPowers;
+char * _PREHASH_ProfileHollow;
+char * _PREHASH_GroupRoleChanges;
+char * _PREHASH_Count;
+char * _PREHASH_South;
+char * _PREHASH_Entry;
+char * _PREHASH_ObjectUpdateCompressed;
+char * _PREHASH_MuteFlags;
+char * _PREHASH_Group;
+char * _PREHASH_AgentPause;
+char * _PREHASH_LanguagesText;
+char * _PREHASH_InternalScriptMail;
+char * _PREHASH_FindAgent;
+char * _PREHASH_AgentData;
+char * _PREHASH_FolderData;
+char * _PREHASH_AssetBlock;
+char * _PREHASH_AcceptNotices;
+char * _PREHASH_SetGroupAcceptNotices;
+char * _PREHASH_CloseCircuit;
+char * _PREHASH_LogControl;
+char * _PREHASH_TeleportFinish;
+char * _PREHASH_PathRevolutions;
+char * _PREHASH_ClassifiedInfoReply;
+char * _PREHASH_ParcelInfoReply;
+char * _PREHASH_AutosaveData;
+char * _PREHASH_SetStartLocation;
+char * _PREHASH_PassHours;
+char * _PREHASH_AttachmentPt;
+char * _PREHASH_ParcelFlags;
+char * _PREHASH_NumVotes;
+char * _PREHASH_AvatarPickerRequest;
+char * _PREHASH_TeleportLocationRequest;
+char * _PREHASH_DataHomeLocationRequest;
+char * _PREHASH_EventNotificationAddRequest;
+char * _PREHASH_ParcelDwellRequest;
+char * _PREHASH_EventLocationRequest;
+char * _PREHASH_EndPeriod;
+char * _PREHASH_SetStartLocationRequest;
+char * _PREHASH_QueryStart;
+char * _PREHASH_EjectData;
+char * _PREHASH_AvatarTextureUpdate;
+char * _PREHASH_RPCServerPort;
+char * _PREHASH_Bytes;
+char * _PREHASH_Extra;
+char * _PREHASH_ForceScriptControlRelease;
+char * _PREHASH_ParcelRelease;
+char * _PREHASH_VFileType;
+char * _PREHASH_EjectGroupMemberReply;
+char * _PREHASH_ImageData;
+char * _PREHASH_SpaceServerSimulatorTimeMessage;
+char * _PREHASH_SimulatorViewerTimeMessage;
+char * _PREHASH_Rotation;
+char * _PREHASH_Selection;
+char * _PREHASH_TransactionData;
+char * _PREHASH_OperationData;
+char * _PREHASH_ExpirationDate;
+char * _PREHASH_ParcelDeedToGroup;
+char * _PREHASH_DirPicksReply;
+char * _PREHASH_AvatarPicksReply;
+char * _PREHASH_GroupTitlesReply;
+char * _PREHASH_AgentInfo;
+char * _PREHASH_MoneyTransferBackend;
+char * _PREHASH_NextOwnerMask;
+char * _PREHASH_MuteData;
+char * _PREHASH_PassPrice;
+char * _PREHASH_SourceID;
+char * _PREHASH_ChangeUserRights;
+char * _PREHASH_TeleportFlags;
+char * _PREHASH_AssetData;
+char * _PREHASH_SlaveParcelData;
+char * _PREHASH_MultipleObjectUpdate;
+char * _PREHASH_ObjectUpdate;
+char * _PREHASH_ImprovedTerseObjectUpdate;
+char * _PREHASH_ConfirmXferPacket;
+char * _PREHASH_StartPingCheck;
+char * _PREHASH_SimWideDeletes;
+char * _PREHASH_LandStatReply;
+char * _PREHASH_IsPhantom;
+char * _PREHASH_AgentList;
+char * _PREHASH_SimApproved;
+char * _PREHASH_RezObject;
+char * _PREHASH_TaskLocalID;
+char * _PREHASH_ClaimDate;
+char * _PREHASH_MergeParcel;
+char * _PREHASH_Priority;
+char * _PREHASH_Building;
+char * _PREHASH_QueryText;
+char * _PREHASH_GroupNoticeAdd;
+char * _PREHASH_ReturnType;
+char * _PREHASH_FetchFolders;
+char * _PREHASH_SimulatorPublicHostBlock;
+char * _PREHASH_HeaderData;
+char * _PREHASH_RequestMultipleObjects;
+char * _PREHASH_RetrieveInstantMessages;
+char * _PREHASH_DequeueInstantMessages;
+char * _PREHASH_OpenCircuit;
+char * _PREHASH_SecureSessionID;
+char * _PREHASH_CrossedRegion;
+char * _PREHASH_DirGroupsReply;
+char * _PREHASH_AvatarGroupsReply;
+char * _PREHASH_EmailMessageReply;
+char * _PREHASH_GroupVoteHistoryItemReply;
+char * _PREHASH_ViewerPosition;
+char * _PREHASH_Position;
+char * _PREHASH_ParentEstate;
+char * _PREHASH_EstateName;
+char * _PREHASH_MuteName;
+char * _PREHASH_StartParcelRename;
+char * _PREHASH_BulkParcelRename;
+char * _PREHASH_ParcelRename;
+char * _PREHASH_ViewerFilename;
+char * _PREHASH_Positive;
+char * _PREHASH_UserReportInternal;
+char * _PREHASH_AvatarPropertiesRequest;
+char * _PREHASH_ParcelPropertiesRequest;
+char * _PREHASH_GroupProfileRequest;
+char * _PREHASH_AgentDataUpdateRequest;
+char * _PREHASH_PriceObjectScaleFactor;
+char * _PREHASH_DirPicksQuery;
+char * _PREHASH_OpenEnrollment;
+char * _PREHASH_GroupData;
+char * _PREHASH_RequestGodlikePowers;
+char * _PREHASH_GrantGodlikePowers;
+char * _PREHASH_TransactionID;
+char * _PREHASH_DestinationID;
+char * _PREHASH_Controls;
+char * _PREHASH_FirstDetachAll;
+char * _PREHASH_EstateID;
+char * _PREHASH_ImprovedInstantMessage;
+char * _PREHASH_AgentQuit;
+char * _PREHASH_CheckParcelSales;
+char * _PREHASH_ParcelSales;
+char * _PREHASH_CurrentInterval;
+char * _PREHASH_PriceRentLight;
+char * _PREHASH_MediaAutoScale;
+char * _PREHASH_NeighborBlock;
+char * _PREHASH_LayerData;
+char * _PREHASH_NVPairData;
+char * _PREHASH_TeleportLocal;
+char * _PREHASH_EjecteeID;
+char * _PREHASH_VoteInitiator;
+char * _PREHASH_TypeData;
+char * _PREHASH_OwnerIDs;
+char * _PREHASH_SystemKickUser;
+char * _PREHASH_TransactionTime;
+char * _PREHASH_TimeToLive;
+char * _PREHASH_StartParcelRemove;
+char * _PREHASH_BulkParcelRemove;
+char * _PREHASH_OldAgentID;
+char * _PREHASH_BonusEstimate;
+char * _PREHASH_MusicURL;
+char * _PREHASH_CompleteLure;
+char * _PREHASH_ParcelPrimBonus;
+char * _PREHASH_EjectUser;
+char * _PREHASH_CoarseLocationUpdate;
+char * _PREHASH_ChildAgentPositionUpdate;
+char * _PREHASH_StoreLocal;
+char * _PREHASH_GroupName;
+char * _PREHASH_PriceParcelRent;
+char * _PREHASH_SimStatus;
+char * _PREHASH_TransactionSuccess;
+char * _PREHASH_LureType;
+char * _PREHASH_GroupMask;
+char * _PREHASH_SitObject;
+char * _PREHASH_Override;
+char * _PREHASH_LocomotionState;
+char * _PREHASH_PriceUpload;
+char * _PREHASH_RemoveParcel;
+char * _PREHASH_ConfirmAuctionStart;
+char * _PREHASH_RpcScriptRequestInbound;
+char * _PREHASH_ActiveGroupID;
+char * _PREHASH_ParcelReturnObjects;
+char * _PREHASH_TotalObjects;
+char * _PREHASH_ObjectExtraParams;
+char * _PREHASH_Questions;
+char * _PREHASH_TransferAbort;
+char * _PREHASH_TransferInventory;
+char * _PREHASH_RayTargetID;
+char * _PREHASH_ClaimPrice;
+char * _PREHASH_ObjectProperties;
+char * _PREHASH_ParcelProperties;
+char * _PREHASH_EstateOwnerID;
+char * _PREHASH_LogoutRequest;
+char * _PREHASH_AssetUploadRequest;
+char * _PREHASH_ReputationIndividualRequest;
+char * _PREHASH_MajorVersion;
+char * _PREHASH_MinorVersion;
+char * _PREHASH_SimulatorAssign;
+char * _PREHASH_TransactionType;
+char * _PREHASH_AvatarPropertiesUpdate;
+char * _PREHASH_ParcelPropertiesUpdate;
+char * _PREHASH_FetchItems;
+char * _PREHASH_AbortXfer;
+char * _PREHASH_DeRezAck;
+char * _PREHASH_TakeControls;
+char * _PREHASH_DirLandReply;
+char * _PREHASH_SpaceLocationTeleportReply;
+char * _PREHASH_MuteType;
+char * _PREHASH_IMViaEMail;
+char * _PREHASH_StartExpungeProcessAck;
+char * _PREHASH_RentPrice;
+char * _PREHASH_GenericMessage;
+char * _PREHASH_ChildAgentAlive;
+char * _PREHASH_AssetType;
+char * _PREHASH_SpawnPointBlock;
+char * _PREHASH_AttachmentBlock;
+char * _PREHASH_ObjectMaterial;
+char * _PREHASH_OwnerName;
+char * _PREHASH_AvatarNotesReply;
+char * _PREHASH_CacheID;
+char * _PREHASH_OwnerMask;
+char * _PREHASH_TransferInventoryAck;
+
+void init_prehash_data()
+{
+ _PREHASH_X = gMessageStringTable.getString("X");
+ _PREHASH_Y = gMessageStringTable.getString("Y");
+ _PREHASH_Z = gMessageStringTable.getString("Z");
+ _PREHASH_AddFlags = gMessageStringTable.getString("AddFlags");
+ _PREHASH_ReservedNewbie = gMessageStringTable.getString("ReservedNewbie");
+ _PREHASH_FailureInfo = gMessageStringTable.getString("FailureInfo");
+ _PREHASH_MapData = gMessageStringTable.getString("MapData");
+ _PREHASH_AddItem = gMessageStringTable.getString("AddItem");
+ _PREHASH_MeanCollision = gMessageStringTable.getString("MeanCollision");
+ _PREHASH_RezScript = gMessageStringTable.getString("RezScript");
+ _PREHASH_AvatarSitResponse = gMessageStringTable.getString("AvatarSitResponse");
+ _PREHASH_InventoryAssetResponse = gMessageStringTable.getString("InventoryAssetResponse");
+ _PREHASH_KillObject = gMessageStringTable.getString("KillObject");
+ _PREHASH_ProposalID = gMessageStringTable.getString("ProposalID");
+ _PREHASH_SerialNum = gMessageStringTable.getString("SerialNum");
+ _PREHASH_Duration = gMessageStringTable.getString("Duration");
+ _PREHASH_ScriptQuestion = gMessageStringTable.getString("ScriptQuestion");
+ _PREHASH_AddCircuitCode = gMessageStringTable.getString("AddCircuitCode");
+ _PREHASH_UseCircuitCode = gMessageStringTable.getString("UseCircuitCode");
+ _PREHASH_ViewerCircuitCode = gMessageStringTable.getString("ViewerCircuitCode");
+ _PREHASH_ScriptAnswerYes = gMessageStringTable.getString("ScriptAnswerYes");
+ _PREHASH_PartnerID = gMessageStringTable.getString("PartnerID");
+ _PREHASH_DirLandQuery = gMessageStringTable.getString("DirLandQuery");
+ _PREHASH_TeleportStart = gMessageStringTable.getString("TeleportStart");
+ _PREHASH_LogMessages = gMessageStringTable.getString("LogMessages");
+ _PREHASH_AboutText = gMessageStringTable.getString("AboutText");
+ _PREHASH_VisualParam = gMessageStringTable.getString("VisualParam");
+ _PREHASH_GroupPrims = gMessageStringTable.getString("GroupPrims");
+ _PREHASH_SelectedPrims = gMessageStringTable.getString("SelectedPrims");
+ _PREHASH_ID = gMessageStringTable.getString("ID");
+ _PREHASH_UUIDNameRequest = gMessageStringTable.getString("UUIDNameRequest");
+ _PREHASH_UUIDGroupNameRequest = gMessageStringTable.getString("UUIDGroupNameRequest");
+ _PREHASH_MoneyTransactionsRequest = gMessageStringTable.getString("MoneyTransactionsRequest");
+ _PREHASH_GroupAccountTransactionsRequest = gMessageStringTable.getString("GroupAccountTransactionsRequest");
+ _PREHASH_MapNameRequest = gMessageStringTable.getString("MapNameRequest");
+ _PREHASH_MailTaskSimRequest = gMessageStringTable.getString("MailTaskSimRequest");
+ _PREHASH_UpdateSimulator = gMessageStringTable.getString("UpdateSimulator");
+ _PREHASH_BillableFactor = gMessageStringTable.getString("BillableFactor");
+ _PREHASH_ObjectBonusFactor = gMessageStringTable.getString("ObjectBonusFactor");
+ _PREHASH_EnableSimulator = gMessageStringTable.getString("EnableSimulator");
+ _PREHASH_DisableSimulator = gMessageStringTable.getString("DisableSimulator");
+ _PREHASH_ConfirmEnableSimulator = gMessageStringTable.getString("ConfirmEnableSimulator");
+ _PREHASH_LayerType = gMessageStringTable.getString("LayerType");
+ _PREHASH_OwnerRole = gMessageStringTable.getString("OwnerRole");
+ _PREHASH_ParcelOverlay = gMessageStringTable.getString("ParcelOverlay");
+ _PREHASH_AdjustBalance = gMessageStringTable.getString("AdjustBalance");
+ _PREHASH_GroupOwned = gMessageStringTable.getString("GroupOwned");
+ _PREHASH_IP = gMessageStringTable.getString("IP");
+ _PREHASH_ChatFromViewer = gMessageStringTable.getString("ChatFromViewer");
+ _PREHASH_AvgAgentsInView = gMessageStringTable.getString("AvgAgentsInView");
+ _PREHASH_AgentsInView = gMessageStringTable.getString("AgentsInView");
+ _PREHASH_GroupTitle = gMessageStringTable.getString("GroupTitle");
+ _PREHASH_MapLayerReply = gMessageStringTable.getString("MapLayerReply");
+ _PREHASH_CompoundMsgID = gMessageStringTable.getString("CompoundMsgID");
+ _PREHASH_CameraConstraint = gMessageStringTable.getString("CameraConstraint");
+ _PREHASH_DownloadTotals = gMessageStringTable.getString("DownloadTotals");
+ _PREHASH_GenCounter = gMessageStringTable.getString("GenCounter");
+ _PREHASH_FrozenData = gMessageStringTable.getString("FrozenData");
+ _PREHASH_ChildAgentDying = gMessageStringTable.getString("ChildAgentDying");
+ _PREHASH_To = gMessageStringTable.getString("To");
+ _PREHASH_CopyInventoryFromNotecard = gMessageStringTable.getString("CopyInventoryFromNotecard");
+ _PREHASH_RezObjectFromNotecard = gMessageStringTable.getString("RezObjectFromNotecard");
+ _PREHASH_ParcelDirFeeCurrent = gMessageStringTable.getString("ParcelDirFeeCurrent");
+ _PREHASH_SeedCapability = gMessageStringTable.getString("SeedCapability");
+ _PREHASH_ObjectDuplicate = gMessageStringTable.getString("ObjectDuplicate");
+ _PREHASH_InventoryData = gMessageStringTable.getString("InventoryData");
+ _PREHASH_ReplyData = gMessageStringTable.getString("ReplyData");
+ _PREHASH_ResetList = gMessageStringTable.getString("ResetList");
+ _PREHASH_MediaID = gMessageStringTable.getString("MediaID");
+ _PREHASH_RelatedRights = gMessageStringTable.getString("RelatedRights");
+ _PREHASH_RedirectGridX = gMessageStringTable.getString("RedirectGridX");
+ _PREHASH_RedirectGridY = gMessageStringTable.getString("RedirectGridY");
+ _PREHASH_TransferID = gMessageStringTable.getString("TransferID");
+ _PREHASH_Transacted = gMessageStringTable.getString("Transacted");
+ _PREHASH_TexturesChanged = gMessageStringTable.getString("TexturesChanged");
+ _PREHASH_UserLookAt = gMessageStringTable.getString("UserLookAt");
+ _PREHASH_TestBlock1 = gMessageStringTable.getString("TestBlock1");
+ _PREHASH_SensedData = gMessageStringTable.getString("SensedData");
+ _PREHASH_UpdateBlock = gMessageStringTable.getString("UpdateBlock");
+ _PREHASH_ClassifiedGodDelete = gMessageStringTable.getString("ClassifiedGodDelete");
+ _PREHASH_ObjectGrabUpdate = gMessageStringTable.getString("ObjectGrabUpdate");
+ _PREHASH_TaxDate = gMessageStringTable.getString("TaxDate");
+ _PREHASH_LocationPos = gMessageStringTable.getString("LocationPos");
+ _PREHASH_StartDateTime = gMessageStringTable.getString("StartDateTime");
+ _PREHASH_ObjectUpdateCached = gMessageStringTable.getString("ObjectUpdateCached");
+ _PREHASH_Packets = gMessageStringTable.getString("Packets");
+ _PREHASH_FailureType = gMessageStringTable.getString("FailureType");
+ _PREHASH_UpdateGroupInfo = gMessageStringTable.getString("UpdateGroupInfo");
+ _PREHASH_ObjectPermissions = gMessageStringTable.getString("ObjectPermissions");
+ _PREHASH_RevokePermissions = gMessageStringTable.getString("RevokePermissions");
+ _PREHASH_UpdateFlags = gMessageStringTable.getString("UpdateFlags");
+ _PREHASH_ObjectExportSelected = gMessageStringTable.getString("ObjectExportSelected");
+ _PREHASH_RezSelected = gMessageStringTable.getString("RezSelected");
+ _PREHASH_AutoPilot = gMessageStringTable.getString("AutoPilot");
+ _PREHASH_UpdateMuteListEntry = gMessageStringTable.getString("UpdateMuteListEntry");
+ _PREHASH_RemoveMuteListEntry = gMessageStringTable.getString("RemoveMuteListEntry");
+ _PREHASH_SetSimStatusInDatabase = gMessageStringTable.getString("SetSimStatusInDatabase");
+ _PREHASH_SetSimPresenceInDatabase = gMessageStringTable.getString("SetSimPresenceInDatabase");
+ _PREHASH_CameraProperty = gMessageStringTable.getString("CameraProperty");
+ _PREHASH_BrushSize = gMessageStringTable.getString("BrushSize");
+ _PREHASH_StartExpungeProcess = gMessageStringTable.getString("StartExpungeProcess");
+ _PREHASH_SimulatorSetMap = gMessageStringTable.getString("SimulatorSetMap");
+ _PREHASH_RegionPresenceRequestByRegionID = gMessageStringTable.getString("RegionPresenceRequestByRegionID");
+ _PREHASH_ParcelObjectOwnersReply = gMessageStringTable.getString("ParcelObjectOwnersReply");
+ _PREHASH_GroupMembersReply = gMessageStringTable.getString("GroupMembersReply");
+ _PREHASH_GroupRoleMembersReply = gMessageStringTable.getString("GroupRoleMembersReply");
+ _PREHASH_RequestRegionInfo = gMessageStringTable.getString("RequestRegionInfo");
+ _PREHASH_AABBMax = gMessageStringTable.getString("AABBMax");
+ _PREHASH_RequestPayPrice = gMessageStringTable.getString("RequestPayPrice");
+ _PREHASH_SimulatorPresentAtLocation = gMessageStringTable.getString("SimulatorPresentAtLocation");
+ _PREHASH_AgentRequestSit = gMessageStringTable.getString("AgentRequestSit");
+ _PREHASH_AABBMin = gMessageStringTable.getString("AABBMin");
+ _PREHASH_ClassifiedFlags = gMessageStringTable.getString("ClassifiedFlags");
+ _PREHASH_ControlFlags = gMessageStringTable.getString("ControlFlags");
+ _PREHASH_TeleportRequest = gMessageStringTable.getString("TeleportRequest");
+ _PREHASH_SpaceLocationTeleportRequest = gMessageStringTable.getString("SpaceLocationTeleportRequest");
+ _PREHASH_LeaderBoardRequest = gMessageStringTable.getString("LeaderBoardRequest");
+ _PREHASH_ScriptTeleportRequest = gMessageStringTable.getString("ScriptTeleportRequest");
+ _PREHASH_DateUTC = gMessageStringTable.getString("DateUTC");
+ _PREHASH_TaskIDs = gMessageStringTable.getString("TaskIDs");
+ _PREHASH_EstateCovenantRequest = gMessageStringTable.getString("EstateCovenantRequest");
+ _PREHASH_RequestResult = gMessageStringTable.getString("RequestResult");
+ _PREHASH_ReputationAgentAssign = gMessageStringTable.getString("ReputationAgentAssign");
+ _PREHASH_CanAcceptAgents = gMessageStringTable.getString("CanAcceptAgents");
+ _PREHASH_ObjectSaleInfo = gMessageStringTable.getString("ObjectSaleInfo");
+ _PREHASH_KillChildAgents = gMessageStringTable.getString("KillChildAgents");
+ _PREHASH_Balance = gMessageStringTable.getString("Balance");
+ _PREHASH_DerezContainer = gMessageStringTable.getString("DerezContainer");
+ _PREHASH_ObjectData = gMessageStringTable.getString("ObjectData");
+ _PREHASH_CameraAtAxis = gMessageStringTable.getString("CameraAtAxis");
+ _PREHASH_InfoBlock = gMessageStringTable.getString("InfoBlock");
+ _PREHASH_OwnershipCost = gMessageStringTable.getString("OwnershipCost");
+ _PREHASH_AvatarNotesUpdate = gMessageStringTable.getString("AvatarNotesUpdate");
+ _PREHASH_PID = gMessageStringTable.getString("PID");
+ _PREHASH_TimeString = gMessageStringTable.getString("TimeString");
+ _PREHASH_DirPopularReply = gMessageStringTable.getString("DirPopularReply");
+ _PREHASH_TerrainHeightRange00 = gMessageStringTable.getString("TerrainHeightRange00");
+ _PREHASH_SimData = gMessageStringTable.getString("SimData");
+ _PREHASH_TerrainHeightRange01 = gMessageStringTable.getString("TerrainHeightRange01");
+ _PREHASH_TerrainHeightRange10 = gMessageStringTable.getString("TerrainHeightRange10");
+ _PREHASH_TerrainHeightRange11 = gMessageStringTable.getString("TerrainHeightRange11");
+ _PREHASH_UpdateInventoryItem = gMessageStringTable.getString("UpdateInventoryItem");
+ _PREHASH_UpdateCreateInventoryItem = gMessageStringTable.getString("UpdateCreateInventoryItem");
+ _PREHASH_MoveInventoryItem = gMessageStringTable.getString("MoveInventoryItem");
+ _PREHASH_CopyInventoryItem = gMessageStringTable.getString("CopyInventoryItem");
+ _PREHASH_RemoveInventoryItem = gMessageStringTable.getString("RemoveInventoryItem");
+ _PREHASH_CreateInventoryItem = gMessageStringTable.getString("CreateInventoryItem");
+ _PREHASH_PathTwistBegin = gMessageStringTable.getString("PathTwistBegin");
+ _PREHASH_CRC = gMessageStringTable.getString("CRC");
+ _PREHASH_AttachmentPoint = gMessageStringTable.getString("AttachmentPoint");
+ _PREHASH_TelehubBlock = gMessageStringTable.getString("TelehubBlock");
+ _PREHASH_FOVBlock = gMessageStringTable.getString("FOVBlock");
+ _PREHASH_StartLocationData = gMessageStringTable.getString("StartLocationData");
+ _PREHASH_PositionData = gMessageStringTable.getString("PositionData");
+ _PREHASH_TimeSinceLast = gMessageStringTable.getString("TimeSinceLast");
+ _PREHASH_MapImage = gMessageStringTable.getString("MapImage");
+ _PREHASH_Objects = gMessageStringTable.getString("Objects");
+ _PREHASH_URL = gMessageStringTable.getString("URL");
+ _PREHASH_CreationDate = gMessageStringTable.getString("CreationDate");
+ _PREHASH_JointPivot = gMessageStringTable.getString("JointPivot");
+ _PREHASH_RateeID = gMessageStringTable.getString("RateeID");
+ _PREHASH_FPS = gMessageStringTable.getString("FPS");
+ _PREHASH_HasTelehub = gMessageStringTable.getString("HasTelehub");
+ _PREHASH_PathEnd = gMessageStringTable.getString("PathEnd");
+ _PREHASH_ScriptDataReply = gMessageStringTable.getString("ScriptDataReply");
+ _PREHASH_MapBlockReply = gMessageStringTable.getString("MapBlockReply");
+ _PREHASH_PropertiesData = gMessageStringTable.getString("PropertiesData");
+ _PREHASH_ViewerEffect = gMessageStringTable.getString("ViewerEffect");
+ _PREHASH_FreezeUser = gMessageStringTable.getString("FreezeUser");
+ _PREHASH_OwnerPrims = gMessageStringTable.getString("OwnerPrims");
+ _PREHASH_ObjectGrab = gMessageStringTable.getString("ObjectGrab");
+ _PREHASH_ToAgentID = gMessageStringTable.getString("ToAgentID");
+ _PREHASH_SimulatorMapUpdate = gMessageStringTable.getString("SimulatorMapUpdate");
+ _PREHASH_TransferPacket = gMessageStringTable.getString("TransferPacket");
+ _PREHASH_ObjectName = gMessageStringTable.getString("ObjectName");
+ _PREHASH_GroupPowers = gMessageStringTable.getString("GroupPowers");
+ _PREHASH_OriginalName = gMessageStringTable.getString("OriginalName");
+ _PREHASH_CompletePingCheck = gMessageStringTable.getString("CompletePingCheck");
+ _PREHASH_OnlineStatus = gMessageStringTable.getString("OnlineStatus");
+ _PREHASH_ObjectDrop = gMessageStringTable.getString("ObjectDrop");
+ _PREHASH_UseBigPackets = gMessageStringTable.getString("UseBigPackets");
+ _PREHASH_GroupNoticesListReply = gMessageStringTable.getString("GroupNoticesListReply");
+ _PREHASH_ParcelAccessListReply = gMessageStringTable.getString("ParcelAccessListReply");
+ _PREHASH_RpcChannelReply = gMessageStringTable.getString("RpcChannelReply");
+ _PREHASH_RegionPresenceResponse = gMessageStringTable.getString("RegionPresenceResponse");
+ _PREHASH_AgentPresenceResponse = gMessageStringTable.getString("AgentPresenceResponse");
+ _PREHASH_CharterMember = gMessageStringTable.getString("CharterMember");
+ _PREHASH_EdgeData = gMessageStringTable.getString("EdgeData");
+ _PREHASH_NameData = gMessageStringTable.getString("NameData");
+ _PREHASH_RegionPushOverride = gMessageStringTable.getString("RegionPushOverride");
+ _PREHASH_SimName = gMessageStringTable.getString("SimName");
+ _PREHASH_UserReport = gMessageStringTable.getString("UserReport");
+ _PREHASH_DownloadPriority = gMessageStringTable.getString("DownloadPriority");
+ _PREHASH_ToAgentId = gMessageStringTable.getString("ToAgentId");
+ _PREHASH_Mag = gMessageStringTable.getString("Mag");
+ _PREHASH_DirPopularQuery = gMessageStringTable.getString("DirPopularQuery");
+ _PREHASH_ParcelPropertiesRequestByID = gMessageStringTable.getString("ParcelPropertiesRequestByID");
+ _PREHASH_ObjectLink = gMessageStringTable.getString("ObjectLink");
+ _PREHASH_RpcScriptReplyInbound = gMessageStringTable.getString("RpcScriptReplyInbound");
+ _PREHASH_BoardData = gMessageStringTable.getString("BoardData");
+ _PREHASH_RezData = gMessageStringTable.getString("RezData");
+ _PREHASH_RemoveInventoryObjects = gMessageStringTable.getString("RemoveInventoryObjects");
+ _PREHASH_GroupProposalBallot = gMessageStringTable.getString("GroupProposalBallot");
+ _PREHASH_RPCServerIP = gMessageStringTable.getString("RPCServerIP");
+ _PREHASH_Far = gMessageStringTable.getString("Far");
+ _PREHASH_GodSessionID = gMessageStringTable.getString("GodSessionID");
+ _PREHASH_ViewerDigest = gMessageStringTable.getString("ViewerDigest");
+ _PREHASH_FLAboutText = gMessageStringTable.getString("FLAboutText");
+ _PREHASH_RegionHandshakeReply = gMessageStringTable.getString("RegionHandshakeReply");
+ _PREHASH_GroupActiveProposalItemReply = gMessageStringTable.getString("GroupActiveProposalItemReply");
+ _PREHASH_MapItemReply = gMessageStringTable.getString("MapItemReply");
+ _PREHASH_Seconds = gMessageStringTable.getString("Seconds");
+ _PREHASH_UpdateUserInfo = gMessageStringTable.getString("UpdateUserInfo");
+ _PREHASH_AggregatePermTexturesOwner = gMessageStringTable.getString("AggregatePermTexturesOwner");
+ _PREHASH_Set = gMessageStringTable.getString("Set");
+ _PREHASH_NewName = gMessageStringTable.getString("NewName");
+ _PREHASH_Key = gMessageStringTable.getString("Key");
+ _PREHASH_AgentID = gMessageStringTable.getString("AgentID");
+ _PREHASH_OnlineStatusRequest = gMessageStringTable.getString("OnlineStatusRequest");
+ _PREHASH_EventNotificationRemoveRequest = gMessageStringTable.getString("EventNotificationRemoveRequest");
+ _PREHASH_NewFolderID = gMessageStringTable.getString("NewFolderID");
+ _PREHASH_Arc = gMessageStringTable.getString("Arc");
+ _PREHASH_RegionX = gMessageStringTable.getString("RegionX");
+ _PREHASH_RegionY = gMessageStringTable.getString("RegionY");
+ _PREHASH_RequestData = gMessageStringTable.getString("RequestData");
+ _PREHASH_Msg = gMessageStringTable.getString("Msg");
+ _PREHASH_Top = gMessageStringTable.getString("Top");
+ _PREHASH_MiscStats = gMessageStringTable.getString("MiscStats");
+ _PREHASH_ImageID = gMessageStringTable.getString("ImageID");
+ _PREHASH_DataPacket = gMessageStringTable.getString("DataPacket");
+ _PREHASH_ObjectDehinge = gMessageStringTable.getString("ObjectDehinge");
+ _PREHASH_You = gMessageStringTable.getString("You");
+ _PREHASH_ScriptControlChange = gMessageStringTable.getString("ScriptControlChange");
+ _PREHASH_LoadURL = gMessageStringTable.getString("LoadURL");
+ _PREHASH_SetCPURatio = gMessageStringTable.getString("SetCPURatio");
+ _PREHASH_NameValueData = gMessageStringTable.getString("NameValueData");
+ _PREHASH_AtomicPassObject = gMessageStringTable.getString("AtomicPassObject");
+ _PREHASH_ErrorMessage = gMessageStringTable.getString("ErrorMessage");
+ _PREHASH_ViewerFrozenMessage = gMessageStringTable.getString("ViewerFrozenMessage");
+ _PREHASH_HealthMessage = gMessageStringTable.getString("HealthMessage");
+ _PREHASH_LogTextMessage = gMessageStringTable.getString("LogTextMessage");
+ _PREHASH_TimeDilation = gMessageStringTable.getString("TimeDilation");
+ _PREHASH_RemoveContribution = gMessageStringTable.getString("RemoveContribution");
+ _PREHASH_Contribution = gMessageStringTable.getString("Contribution");
+ _PREHASH_SetGroupContribution = gMessageStringTable.getString("SetGroupContribution");
+ _PREHASH_Offline = gMessageStringTable.getString("Offline");
+ _PREHASH_AgentIsNowWearing = gMessageStringTable.getString("AgentIsNowWearing");
+ _PREHASH_SecPerDay = gMessageStringTable.getString("SecPerDay");
+ _PREHASH_Members = gMessageStringTable.getString("Members");
+ _PREHASH_FailedResends = gMessageStringTable.getString("FailedResends");
+ _PREHASH_CameraCenter = gMessageStringTable.getString("CameraCenter");
+ _PREHASH_CameraLeftAxis = gMessageStringTable.getString("CameraLeftAxis");
+ _PREHASH_ExBlock = gMessageStringTable.getString("ExBlock");
+ _PREHASH_Channel = gMessageStringTable.getString("Channel");
+ _PREHASH_NetTest = gMessageStringTable.getString("NetTest");
+ _PREHASH_DiscardLevel = gMessageStringTable.getString("DiscardLevel");
+ _PREHASH_LayerID = gMessageStringTable.getString("LayerID");
+ _PREHASH_RatorID = gMessageStringTable.getString("RatorID");
+ _PREHASH_GrabOffset = gMessageStringTable.getString("GrabOffset");
+ _PREHASH_SimPort = gMessageStringTable.getString("SimPort");
+ _PREHASH_PricePerMeter = gMessageStringTable.getString("PricePerMeter");
+ _PREHASH_RegionFlags = gMessageStringTable.getString("RegionFlags");
+ _PREHASH_VoteResult = gMessageStringTable.getString("VoteResult");
+ _PREHASH_ParcelDirFeeEstimate = gMessageStringTable.getString("ParcelDirFeeEstimate");
+ _PREHASH_ModifyBlock = gMessageStringTable.getString("ModifyBlock");
+ _PREHASH_InventoryBlock = gMessageStringTable.getString("InventoryBlock");
+ _PREHASH_ReplyBlock = gMessageStringTable.getString("ReplyBlock");
+ _PREHASH_ValidUntil = gMessageStringTable.getString("ValidUntil");
+ _PREHASH_VelocityInterpolateOn = gMessageStringTable.getString("VelocityInterpolateOn");
+ _PREHASH_ClassifiedDelete = gMessageStringTable.getString("ClassifiedDelete");
+ _PREHASH_RegionDenyAnonymous = gMessageStringTable.getString("RegionDenyAnonymous");
+ _PREHASH_FLImageID = gMessageStringTable.getString("FLImageID");
+ _PREHASH_AllowPublish = gMessageStringTable.getString("AllowPublish");
+ _PREHASH_SitName = gMessageStringTable.getString("SitName");
+ _PREHASH_RegionsVisited = gMessageStringTable.getString("RegionsVisited");
+ _PREHASH_DirClassifiedReply = gMessageStringTable.getString("DirClassifiedReply");
+ _PREHASH_AvatarClassifiedReply = gMessageStringTable.getString("AvatarClassifiedReply");
+ _PREHASH_ReputationIndividualReply = gMessageStringTable.getString("ReputationIndividualReply");
+ _PREHASH_MediaURL = gMessageStringTable.getString("MediaURL");
+ _PREHASH_CompleteAgentMovement = gMessageStringTable.getString("CompleteAgentMovement");
+ _PREHASH_SpaceIP = gMessageStringTable.getString("SpaceIP");
+ _PREHASH_ClassifiedID = gMessageStringTable.getString("ClassifiedID");
+ _PREHASH_LocalID = gMessageStringTable.getString("LocalID");
+ _PREHASH_RemoveItem = gMessageStringTable.getString("RemoveItem");
+ _PREHASH_LogFailedMoneyTransaction = gMessageStringTable.getString("LogFailedMoneyTransaction");
+ _PREHASH_ViewerStartAuction = gMessageStringTable.getString("ViewerStartAuction");
+ _PREHASH_StartAuction = gMessageStringTable.getString("StartAuction");
+ _PREHASH_NameValueName = gMessageStringTable.getString("NameValueName");
+ _PREHASH_AngVelX = gMessageStringTable.getString("AngVelX");
+ _PREHASH_DuplicateFlags = gMessageStringTable.getString("DuplicateFlags");
+ _PREHASH_AngVelY = gMessageStringTable.getString("AngVelY");
+ _PREHASH_AngVelZ = gMessageStringTable.getString("AngVelZ");
+ _PREHASH_TextColor = gMessageStringTable.getString("TextColor");
+ _PREHASH_SlaveID = gMessageStringTable.getString("SlaveID");
+ _PREHASH_Charter = gMessageStringTable.getString("Charter");
+ _PREHASH_AlertData = gMessageStringTable.getString("AlertData");
+ _PREHASH_TargetBlock = gMessageStringTable.getString("TargetBlock");
+ _PREHASH_CheckParcelAuctions = gMessageStringTable.getString("CheckParcelAuctions");
+ _PREHASH_ParcelAuctions = gMessageStringTable.getString("ParcelAuctions");
+ _PREHASH_OwnerIsGroup = gMessageStringTable.getString("OwnerIsGroup");
+ _PREHASH_NameValuePair = gMessageStringTable.getString("NameValuePair");
+ _PREHASH_RemoveNameValuePair = gMessageStringTable.getString("RemoveNameValuePair");
+ _PREHASH_GetNameValuePair = gMessageStringTable.getString("GetNameValuePair");
+ _PREHASH_BulkUpdateInventory = gMessageStringTable.getString("BulkUpdateInventory");
+ _PREHASH_UpdateTaskInventory = gMessageStringTable.getString("UpdateTaskInventory");
+ _PREHASH_RemoveTaskInventory = gMessageStringTable.getString("RemoveTaskInventory");
+ _PREHASH_MoveTaskInventory = gMessageStringTable.getString("MoveTaskInventory");
+ _PREHASH_RequestTaskInventory = gMessageStringTable.getString("RequestTaskInventory");
+ _PREHASH_ReplyTaskInventory = gMessageStringTable.getString("ReplyTaskInventory");
+ _PREHASH_DeclineInventory = gMessageStringTable.getString("DeclineInventory");
+ _PREHASH_AggregatePermInventory = gMessageStringTable.getString("AggregatePermInventory");
+ _PREHASH_SimulatorInfo = gMessageStringTable.getString("SimulatorInfo");
+ _PREHASH_MoneyTransactionsReply = gMessageStringTable.getString("MoneyTransactionsReply");
+ _PREHASH_GroupAccountTransactionsReply = gMessageStringTable.getString("GroupAccountTransactionsReply");
+ _PREHASH_MailTaskSimReply = gMessageStringTable.getString("MailTaskSimReply");
+ _PREHASH_WearableData = gMessageStringTable.getString("WearableData");
+ _PREHASH_StatisticsData = gMessageStringTable.getString("StatisticsData");
+ _PREHASH_Enabled = gMessageStringTable.getString("Enabled");
+ _PREHASH_Savings = gMessageStringTable.getString("Savings");
+ _PREHASH_SimulatorLoad = gMessageStringTable.getString("SimulatorLoad");
+ _PREHASH_InternalRegionIP = gMessageStringTable.getString("InternalRegionIP");
+ _PREHASH_ExternalRegionIP = gMessageStringTable.getString("ExternalRegionIP");
+ _PREHASH_TotalPairs = gMessageStringTable.getString("TotalPairs");
+ _PREHASH_CreateGroupRequest = gMessageStringTable.getString("CreateGroupRequest");
+ _PREHASH_JoinGroupRequest = gMessageStringTable.getString("JoinGroupRequest");
+ _PREHASH_LeaveGroupRequest = gMessageStringTable.getString("LeaveGroupRequest");
+ _PREHASH_InviteGroupRequest = gMessageStringTable.getString("InviteGroupRequest");
+ _PREHASH_LiveHelpGroupRequest = gMessageStringTable.getString("LiveHelpGroupRequest");
+ _PREHASH_ServerVersion = gMessageStringTable.getString("ServerVersion");
+ _PREHASH_PriceParcelClaimFactor = gMessageStringTable.getString("PriceParcelClaimFactor");
+ _PREHASH_BillableArea = gMessageStringTable.getString("BillableArea");
+ _PREHASH_ObjectID = gMessageStringTable.getString("ObjectID");
+ _PREHASH_ObjectFlagUpdate = gMessageStringTable.getString("ObjectFlagUpdate");
+ _PREHASH_GroupRoleUpdate = gMessageStringTable.getString("GroupRoleUpdate");
+ _PREHASH_RequestInventoryAsset = gMessageStringTable.getString("RequestInventoryAsset");
+ _PREHASH_RedoLand = gMessageStringTable.getString("RedoLand");
+ _PREHASH_TravelAccess = gMessageStringTable.getString("TravelAccess");
+ _PREHASH_ChangedGrid = gMessageStringTable.getString("ChangedGrid");
+ _PREHASH_AgentDropGroup = gMessageStringTable.getString("AgentDropGroup");
+ _PREHASH_Details = gMessageStringTable.getString("Details");
+ _PREHASH_LocationX = gMessageStringTable.getString("LocationX");
+ _PREHASH_SaleType = gMessageStringTable.getString("SaleType");
+ _PREHASH_LocationY = gMessageStringTable.getString("LocationY");
+ _PREHASH_LocationZ = gMessageStringTable.getString("LocationZ");
+ _PREHASH_EconomyData = gMessageStringTable.getString("EconomyData");
+ _PREHASH_HeadRotation = gMessageStringTable.getString("HeadRotation");
+ _PREHASH_DeleteOnCompletion = gMessageStringTable.getString("DeleteOnCompletion");
+ _PREHASH_PublicPort = gMessageStringTable.getString("PublicPort");
+ _PREHASH_DirClassifiedQuery = gMessageStringTable.getString("DirClassifiedQuery");
+ _PREHASH_CallbackID = gMessageStringTable.getString("CallbackID");
+ _PREHASH_RequestParcelTransfer = gMessageStringTable.getString("RequestParcelTransfer");
+ _PREHASH_RoleCount = gMessageStringTable.getString("RoleCount");
+ _PREHASH_ObjectCapacity = gMessageStringTable.getString("ObjectCapacity");
+ _PREHASH_RequestID = gMessageStringTable.getString("RequestID");
+ _PREHASH_RequestXfer = gMessageStringTable.getString("RequestXfer");
+ _PREHASH_ObjectTaxCurrent = gMessageStringTable.getString("ObjectTaxCurrent");
+ _PREHASH_LightTaxCurrent = gMessageStringTable.getString("LightTaxCurrent");
+ _PREHASH_LandTaxCurrent = gMessageStringTable.getString("LandTaxCurrent");
+ _PREHASH_GroupTaxCurrent = gMessageStringTable.getString("GroupTaxCurrent");
+ _PREHASH_FetchInventoryDescendents = gMessageStringTable.getString("FetchInventoryDescendents");
+ _PREHASH_InventoryDescendents = gMessageStringTable.getString("InventoryDescendents");
+ _PREHASH_Descendents = gMessageStringTable.getString("Descendents");
+ _PREHASH_PurgeInventoryDescendents = gMessageStringTable.getString("PurgeInventoryDescendents");
+ _PREHASH_ShowDir = gMessageStringTable.getString("ShowDir");
+ _PREHASH_IsOwner = gMessageStringTable.getString("IsOwner");
+ _PREHASH_Timestamp = gMessageStringTable.getString("Timestamp");
+ _PREHASH_GlobalPos = gMessageStringTable.getString("GlobalPos");
+ _PREHASH_GrabOffsetInitial = gMessageStringTable.getString("GrabOffsetInitial");
+ _PREHASH_IsTrial = gMessageStringTable.getString("IsTrial");
+ _PREHASH_FinalizeLogout = gMessageStringTable.getString("FinalizeLogout");
+ _PREHASH_ObjectDuplicateOnRay = gMessageStringTable.getString("ObjectDuplicateOnRay");
+ _PREHASH_GroupMembershipCount = gMessageStringTable.getString("GroupMembershipCount");
+ _PREHASH_MethodData = gMessageStringTable.getString("MethodData");
+ _PREHASH_ActivateGestures = gMessageStringTable.getString("ActivateGestures");
+ _PREHASH_DeactivateGestures = gMessageStringTable.getString("DeactivateGestures");
+ _PREHASH_ProposalData = gMessageStringTable.getString("ProposalData");
+ _PREHASH_PosGlobal = gMessageStringTable.getString("PosGlobal");
+ _PREHASH_SearchID = gMessageStringTable.getString("SearchID");
+ _PREHASH_RezMultipleAttachmentsFromInv = gMessageStringTable.getString("RezMultipleAttachmentsFromInv");
+ _PREHASH_SearchName = gMessageStringTable.getString("SearchName");
+ _PREHASH_VersionString = gMessageStringTable.getString("VersionString");
+ _PREHASH_CreateGroupReply = gMessageStringTable.getString("CreateGroupReply");
+ _PREHASH_LeaveGroupReply = gMessageStringTable.getString("LeaveGroupReply");
+ _PREHASH_ActualArea = gMessageStringTable.getString("ActualArea");
+ _PREHASH_Message = gMessageStringTable.getString("Message");
+ _PREHASH_ClickAction = gMessageStringTable.getString("ClickAction");
+ _PREHASH_AssetUploadComplete = gMessageStringTable.getString("AssetUploadComplete");
+ _PREHASH_RequestType = gMessageStringTable.getString("RequestType");
+ _PREHASH_UUID = gMessageStringTable.getString("UUID");
+ _PREHASH_BaseMask = gMessageStringTable.getString("BaseMask");
+ _PREHASH_NetBlock = gMessageStringTable.getString("NetBlock");
+ _PREHASH_GlobalX = gMessageStringTable.getString("GlobalX");
+ _PREHASH_GlobalY = gMessageStringTable.getString("GlobalY");
+ _PREHASH_CopyRotates = gMessageStringTable.getString("CopyRotates");
+ _PREHASH_KickUserAck = gMessageStringTable.getString("KickUserAck");
+ _PREHASH_TopPick = gMessageStringTable.getString("TopPick");
+ _PREHASH_SessionID = gMessageStringTable.getString("SessionID");
+ _PREHASH_GlobalZ = gMessageStringTable.getString("GlobalZ");
+ _PREHASH_DeclineFriendship = gMessageStringTable.getString("DeclineFriendship");
+ _PREHASH_FormFriendship = gMessageStringTable.getString("FormFriendship");
+ _PREHASH_TerminateFriendship = gMessageStringTable.getString("TerminateFriendship");
+ _PREHASH_TaskData = gMessageStringTable.getString("TaskData");
+ _PREHASH_SimWideMaxPrims = gMessageStringTable.getString("SimWideMaxPrims");
+ _PREHASH_TotalPrims = gMessageStringTable.getString("TotalPrims");
+ _PREHASH_SourceFilename = gMessageStringTable.getString("SourceFilename");
+ _PREHASH_ProfileBegin = gMessageStringTable.getString("ProfileBegin");
+ _PREHASH_MoneyDetailsRequest = gMessageStringTable.getString("MoneyDetailsRequest");
+ _PREHASH_Request = gMessageStringTable.getString("Request");
+ _PREHASH_GroupAccountDetailsRequest = gMessageStringTable.getString("GroupAccountDetailsRequest");
+ _PREHASH_GroupActiveProposalsRequest = gMessageStringTable.getString("GroupActiveProposalsRequest");
+ _PREHASH_StringValue = gMessageStringTable.getString("StringValue");
+ _PREHASH_ClosestSimulator = gMessageStringTable.getString("ClosestSimulator");
+ _PREHASH_Version = gMessageStringTable.getString("Version");
+ _PREHASH_OtherCount = gMessageStringTable.getString("OtherCount");
+ _PREHASH_MemberCount = gMessageStringTable.getString("MemberCount");
+ _PREHASH_ChatData = gMessageStringTable.getString("ChatData");
+ _PREHASH_IsGroupOwned = gMessageStringTable.getString("IsGroupOwned");
+ _PREHASH_EnergyEfficiency = gMessageStringTable.getString("EnergyEfficiency");
+ _PREHASH_MaxPlace = gMessageStringTable.getString("MaxPlace");
+ _PREHASH_PickInfoUpdate = gMessageStringTable.getString("PickInfoUpdate");
+ _PREHASH_PickDelete = gMessageStringTable.getString("PickDelete");
+ _PREHASH_ScriptReset = gMessageStringTable.getString("ScriptReset");
+ _PREHASH_Requester = gMessageStringTable.getString("Requester");
+ _PREHASH_ForSale = gMessageStringTable.getString("ForSale");
+ _PREHASH_NearestLandingRegionReply = gMessageStringTable.getString("NearestLandingRegionReply");
+ _PREHASH_RecordAgentPresence = gMessageStringTable.getString("RecordAgentPresence");
+ _PREHASH_EraseAgentPresence = gMessageStringTable.getString("EraseAgentPresence");
+ _PREHASH_ParcelID = gMessageStringTable.getString("ParcelID");
+ _PREHASH_Godlike = gMessageStringTable.getString("Godlike");
+ _PREHASH_TotalDebits = gMessageStringTable.getString("TotalDebits");
+ _PREHASH_Direction = gMessageStringTable.getString("Direction");
+ _PREHASH_Appearance = gMessageStringTable.getString("Appearance");
+ _PREHASH_HealthData = gMessageStringTable.getString("HealthData");
+ _PREHASH_LeftAxis = gMessageStringTable.getString("LeftAxis");
+ _PREHASH_LocationBlock = gMessageStringTable.getString("LocationBlock");
+ _PREHASH_ObjectImage = gMessageStringTable.getString("ObjectImage");
+ _PREHASH_TerrainStartHeight00 = gMessageStringTable.getString("TerrainStartHeight00");
+ _PREHASH_TerrainStartHeight01 = gMessageStringTable.getString("TerrainStartHeight01");
+ _PREHASH_TerrainStartHeight10 = gMessageStringTable.getString("TerrainStartHeight10");
+ _PREHASH_ObjectHinge = gMessageStringTable.getString("ObjectHinge");
+ _PREHASH_TerrainStartHeight11 = gMessageStringTable.getString("TerrainStartHeight11");
+ _PREHASH_MetersPerGrid = gMessageStringTable.getString("MetersPerGrid");
+ _PREHASH_WaterHeight = gMessageStringTable.getString("WaterHeight");
+ _PREHASH_FetchInventoryReply = gMessageStringTable.getString("FetchInventoryReply");
+ _PREHASH_MoneySummaryReply = gMessageStringTable.getString("MoneySummaryReply");
+ _PREHASH_GroupAccountSummaryReply = gMessageStringTable.getString("GroupAccountSummaryReply");
+ _PREHASH_AttachedSound = gMessageStringTable.getString("AttachedSound");
+ _PREHASH_ParamInUse = gMessageStringTable.getString("ParamInUse");
+ _PREHASH_GodKickUser = gMessageStringTable.getString("GodKickUser");
+ _PREHASH_PickName = gMessageStringTable.getString("PickName");
+ _PREHASH_TaskName = gMessageStringTable.getString("TaskName");
+ _PREHASH_ParcelGodReserveForNewbie = gMessageStringTable.getString("ParcelGodReserveForNewbie");
+ _PREHASH_SubType = gMessageStringTable.getString("SubType");
+ _PREHASH_ObjectCount = gMessageStringTable.getString("ObjectCount");
+ _PREHASH_RegionPresenceRequestByHandle = gMessageStringTable.getString("RegionPresenceRequestByHandle");
+ _PREHASH_RezSingleAttachmentFromInv = gMessageStringTable.getString("RezSingleAttachmentFromInv");
+ _PREHASH_ChildAgentUpdate = gMessageStringTable.getString("ChildAgentUpdate");
+ _PREHASH_ToID = gMessageStringTable.getString("ToID");
+ _PREHASH_ViewerPort = gMessageStringTable.getString("ViewerPort");
+ _PREHASH_IsOwnerGroup = gMessageStringTable.getString("IsOwnerGroup");
+ _PREHASH_AgentHeightWidth = gMessageStringTable.getString("AgentHeightWidth");
+ _PREHASH_VerticalAngle = gMessageStringTable.getString("VerticalAngle");
+ _PREHASH_WearableType = gMessageStringTable.getString("WearableType");
+ _PREHASH_AggregatePermNextOwner = gMessageStringTable.getString("AggregatePermNextOwner");
+ _PREHASH_ShowInList = gMessageStringTable.getString("ShowInList");
+ _PREHASH_PositionSuggestion = gMessageStringTable.getString("PositionSuggestion");
+ _PREHASH_UpdateParcel = gMessageStringTable.getString("UpdateParcel");
+ _PREHASH_ClearAgentSessions = gMessageStringTable.getString("ClearAgentSessions");
+ _PREHASH_SetAlwaysRun = gMessageStringTable.getString("SetAlwaysRun");
+ _PREHASH_NVPair = gMessageStringTable.getString("NVPair");
+ _PREHASH_ObjectSpinStart = gMessageStringTable.getString("ObjectSpinStart");
+ _PREHASH_UseEstateSun = gMessageStringTable.getString("UseEstateSun");
+ _PREHASH_LogoutBlock = gMessageStringTable.getString("LogoutBlock");
+ _PREHASH_RelayLogControl = gMessageStringTable.getString("RelayLogControl");
+ _PREHASH_RegionID = gMessageStringTable.getString("RegionID");
+ _PREHASH_Creator = gMessageStringTable.getString("Creator");
+ _PREHASH_ProposalText = gMessageStringTable.getString("ProposalText");
+ _PREHASH_DirEventsReply = gMessageStringTable.getString("DirEventsReply");
+ _PREHASH_EventInfoReply = gMessageStringTable.getString("EventInfoReply");
+ _PREHASH_UserInfoReply = gMessageStringTable.getString("UserInfoReply");
+ _PREHASH_PathRadiusOffset = gMessageStringTable.getString("PathRadiusOffset");
+ _PREHASH_SessionInfo = gMessageStringTable.getString("SessionInfo");
+ _PREHASH_TextureData = gMessageStringTable.getString("TextureData");
+ _PREHASH_ChatPass = gMessageStringTable.getString("ChatPass");
+ _PREHASH_TargetID = gMessageStringTable.getString("TargetID");
+ _PREHASH_DefaultPayPrice = gMessageStringTable.getString("DefaultPayPrice");
+ _PREHASH_UserLocation = gMessageStringTable.getString("UserLocation");
+ _PREHASH_MaxPrims = gMessageStringTable.getString("MaxPrims");
+ _PREHASH_RegionIP = gMessageStringTable.getString("RegionIP");
+ _PREHASH_LandmarkID = gMessageStringTable.getString("LandmarkID");
+ _PREHASH_InitiateDownload = gMessageStringTable.getString("InitiateDownload");
+ _PREHASH_Name = gMessageStringTable.getString("Name");
+ _PREHASH_OtherCleanTime = gMessageStringTable.getString("OtherCleanTime");
+ _PREHASH_ParcelSetOtherCleanTime = gMessageStringTable.getString("ParcelSetOtherCleanTime");
+ _PREHASH_TeleportPriceExponent = gMessageStringTable.getString("TeleportPriceExponent");
+ _PREHASH_Gain = gMessageStringTable.getString("Gain");
+ _PREHASH_VelX = gMessageStringTable.getString("VelX");
+ _PREHASH_PacketAck = gMessageStringTable.getString("PacketAck");
+ _PREHASH_PathSkew = gMessageStringTable.getString("PathSkew");
+ _PREHASH_Negative = gMessageStringTable.getString("Negative");
+ _PREHASH_VelY = gMessageStringTable.getString("VelY");
+ _PREHASH_SimulatorShutdownRequest = gMessageStringTable.getString("SimulatorShutdownRequest");
+ _PREHASH_NearestLandingRegionRequest = gMessageStringTable.getString("NearestLandingRegionRequest");
+ _PREHASH_VelZ = gMessageStringTable.getString("VelZ");
+ _PREHASH_OtherID = gMessageStringTable.getString("OtherID");
+ _PREHASH_MemberID = gMessageStringTable.getString("MemberID");
+ _PREHASH_MapLayerRequest = gMessageStringTable.getString("MapLayerRequest");
+ _PREHASH_PatchVersion = gMessageStringTable.getString("PatchVersion");
+ _PREHASH_ObjectScale = gMessageStringTable.getString("ObjectScale");
+ _PREHASH_TargetIP = gMessageStringTable.getString("TargetIP");
+ _PREHASH_Redo = gMessageStringTable.getString("Redo");
+ _PREHASH_MoneyBalance = gMessageStringTable.getString("MoneyBalance");
+ _PREHASH_TrackAgent = gMessageStringTable.getString("TrackAgent");
+ _PREHASH_MaxX = gMessageStringTable.getString("MaxX");
+ _PREHASH_Data = gMessageStringTable.getString("Data");
+ _PREHASH_MaxY = gMessageStringTable.getString("MaxY");
+ _PREHASH_TextureAnim = gMessageStringTable.getString("TextureAnim");
+ _PREHASH_ReturnIDs = gMessageStringTable.getString("ReturnIDs");
+ _PREHASH_Date = gMessageStringTable.getString("Date");
+ _PREHASH_GestureUpdate = gMessageStringTable.getString("GestureUpdate");
+ _PREHASH_AgentWearablesUpdate = gMessageStringTable.getString("AgentWearablesUpdate");
+ _PREHASH_AgentDataUpdate = gMessageStringTable.getString("AgentDataUpdate");
+ _PREHASH_Hash = gMessageStringTable.getString("Hash");
+ _PREHASH_GroupDataUpdate = gMessageStringTable.getString("GroupDataUpdate");
+ _PREHASH_AgentGroupDataUpdate = gMessageStringTable.getString("AgentGroupDataUpdate");
+ _PREHASH_Left = gMessageStringTable.getString("Left");
+ _PREHASH_Mask = gMessageStringTable.getString("Mask");
+ _PREHASH_ForceMouselook = gMessageStringTable.getString("ForceMouselook");
+ _PREHASH_Success = gMessageStringTable.getString("Success");
+ _PREHASH_ObjectGroup = gMessageStringTable.getString("ObjectGroup");
+ _PREHASH_SunHour = gMessageStringTable.getString("SunHour");
+ _PREHASH_MinX = gMessageStringTable.getString("MinX");
+ _PREHASH_ScriptSensorReply = gMessageStringTable.getString("ScriptSensorReply");
+ _PREHASH_MinY = gMessageStringTable.getString("MinY");
+ _PREHASH_Command = gMessageStringTable.getString("Command");
+ _PREHASH_Desc = gMessageStringTable.getString("Desc");
+ _PREHASH_AttachmentNeedsSave = gMessageStringTable.getString("AttachmentNeedsSave");
+ _PREHASH_HistoryItemData = gMessageStringTable.getString("HistoryItemData");
+ _PREHASH_AgentCachedTexture = gMessageStringTable.getString("AgentCachedTexture");
+ _PREHASH_Subject = gMessageStringTable.getString("Subject");
+ _PREHASH_East = gMessageStringTable.getString("East");
+ _PREHASH_GodExpungeUser = gMessageStringTable.getString("GodExpungeUser");
+ _PREHASH_QueryReplies = gMessageStringTable.getString("QueryReplies");
+ _PREHASH_ObjectCategory = gMessageStringTable.getString("ObjectCategory");
+ _PREHASH_Time = gMessageStringTable.getString("Time");
+ _PREHASH_CreateLandmarkForEvent = gMessageStringTable.getString("CreateLandmarkForEvent");
+ _PREHASH_ParentID = gMessageStringTable.getString("ParentID");
+ _PREHASH_Ping = gMessageStringTable.getString("Ping");
+ _PREHASH_Perp = gMessageStringTable.getString("Perp");
+ _PREHASH_Code = gMessageStringTable.getString("Code");
+ _PREHASH_InvType = gMessageStringTable.getString("InvType");
+ _PREHASH_AgentFOV = gMessageStringTable.getString("AgentFOV");
+ _PREHASH_BulkMoneyTransfer = gMessageStringTable.getString("BulkMoneyTransfer");
+ _PREHASH_Audible = gMessageStringTable.getString("Audible");
+ _PREHASH_AuctionData = gMessageStringTable.getString("AuctionData");
+ _PREHASH_IDBlock = gMessageStringTable.getString("IDBlock");
+ _PREHASH_ReputationData = gMessageStringTable.getString("ReputationData");
+ _PREHASH_West = gMessageStringTable.getString("West");
+ _PREHASH_Undo = gMessageStringTable.getString("Undo");
+ _PREHASH_TotalNumItems = gMessageStringTable.getString("TotalNumItems");
+ _PREHASH_Info = gMessageStringTable.getString("Info");
+ _PREHASH_Area = gMessageStringTable.getString("Area");
+ _PREHASH_Behavior = gMessageStringTable.getString("Behavior");
+ _PREHASH_SimCrashed = gMessageStringTable.getString("SimCrashed");
+ _PREHASH_Text = gMessageStringTable.getString("Text");
+ _PREHASH_AgentToNewRegion = gMessageStringTable.getString("AgentToNewRegion");
+ _PREHASH_PriceGroupCreate = gMessageStringTable.getString("PriceGroupCreate");
+ _PREHASH_ObjectShape = gMessageStringTable.getString("ObjectShape");
+ _PREHASH_GroupRoleDataReply = gMessageStringTable.getString("GroupRoleDataReply");
+ _PREHASH_PosX = gMessageStringTable.getString("PosX");
+ _PREHASH_PosY = gMessageStringTable.getString("PosY");
+ _PREHASH_MuteCRC = gMessageStringTable.getString("MuteCRC");
+ _PREHASH_PosZ = gMessageStringTable.getString("PosZ");
+ _PREHASH_Size = gMessageStringTable.getString("Size");
+ _PREHASH_FromAddress = gMessageStringTable.getString("FromAddress");
+ _PREHASH_Body = gMessageStringTable.getString("Body");
+ _PREHASH_FileData = gMessageStringTable.getString("FileData");
+ _PREHASH_List = gMessageStringTable.getString("List");
+ _PREHASH_KickUser = gMessageStringTable.getString("KickUser");
+ _PREHASH_OtherPrims = gMessageStringTable.getString("OtherPrims");
+ _PREHASH_RunTime = gMessageStringTable.getString("RunTime");
+ _PREHASH_GrantUserRights = gMessageStringTable.getString("GrantUserRights");
+ _PREHASH_RpcScriptRequestInboundForward = gMessageStringTable.getString("RpcScriptRequestInboundForward");
+ _PREHASH_More = gMessageStringTable.getString("More");
+ _PREHASH_Majority = gMessageStringTable.getString("Majority");
+ _PREHASH_MetersTraveled = gMessageStringTable.getString("MetersTraveled");
+ _PREHASH_Stat = gMessageStringTable.getString("Stat");
+ _PREHASH_SoundID = gMessageStringTable.getString("SoundID");
+ _PREHASH_Item = gMessageStringTable.getString("Item");
+ _PREHASH_User = gMessageStringTable.getString("User");
+ _PREHASH_RemoteInfos = gMessageStringTable.getString("RemoteInfos");
+ _PREHASH_Prey = gMessageStringTable.getString("Prey");
+ _PREHASH_UsecSinceStart = gMessageStringTable.getString("UsecSinceStart");
+ _PREHASH_RayStart = gMessageStringTable.getString("RayStart");
+ _PREHASH_ParcelData = gMessageStringTable.getString("ParcelData");
+ _PREHASH_CameraUpAxis = gMessageStringTable.getString("CameraUpAxis");
+ _PREHASH_ScriptDialog = gMessageStringTable.getString("ScriptDialog");
+ _PREHASH_MasterParcelData = gMessageStringTable.getString("MasterParcelData");
+ _PREHASH_Invalid = gMessageStringTable.getString("Invalid");
+ _PREHASH_MinPlace = gMessageStringTable.getString("MinPlace");
+ _PREHASH_ProfileCurve = gMessageStringTable.getString("ProfileCurve");
+ _PREHASH_ParcelAccessListUpdate = gMessageStringTable.getString("ParcelAccessListUpdate");
+ _PREHASH_MuteListUpdate = gMessageStringTable.getString("MuteListUpdate");
+ _PREHASH_SendPacket = gMessageStringTable.getString("SendPacket");
+ _PREHASH_SendXferPacket = gMessageStringTable.getString("SendXferPacket");
+ _PREHASH_RegionDenyIdentified = gMessageStringTable.getString("RegionDenyIdentified");
+ _PREHASH_NotecardItemID = gMessageStringTable.getString("NotecardItemID");
+ _PREHASH_LastName = gMessageStringTable.getString("LastName");
+ _PREHASH_From = gMessageStringTable.getString("From");
+ _PREHASH_RoleChange = gMessageStringTable.getString("RoleChange");
+ _PREHASH_Port = gMessageStringTable.getString("Port");
+ _PREHASH_MemberTitle = gMessageStringTable.getString("MemberTitle");
+ _PREHASH_LogParcelChanges = gMessageStringTable.getString("LogParcelChanges");
+ _PREHASH_AgentCachedTextureResponse = gMessageStringTable.getString("AgentCachedTextureResponse");
+ _PREHASH_DeRezObject = gMessageStringTable.getString("DeRezObject");
+ _PREHASH_IsTemporary = gMessageStringTable.getString("IsTemporary");
+ _PREHASH_InsigniaID = gMessageStringTable.getString("InsigniaID");
+ _PREHASH_CheckFlags = gMessageStringTable.getString("CheckFlags");
+ _PREHASH_TransferPriority = gMessageStringTable.getString("TransferPriority");
+ _PREHASH_EventID = gMessageStringTable.getString("EventID");
+ _PREHASH_Selected = gMessageStringTable.getString("Selected");
+ _PREHASH_FromAgentId = gMessageStringTable.getString("FromAgentId");
+ _PREHASH_Type = gMessageStringTable.getString("Type");
+ _PREHASH_ChatType = gMessageStringTable.getString("ChatType");
+ _PREHASH_ReportData = gMessageStringTable.getString("ReportData");
+ _PREHASH_LeaderBoardData = gMessageStringTable.getString("LeaderBoardData");
+ _PREHASH_RequestBlock = gMessageStringTable.getString("RequestBlock");
+ _PREHASH_GrantData = gMessageStringTable.getString("GrantData");
+ _PREHASH_DetachAttachmentIntoInv = gMessageStringTable.getString("DetachAttachmentIntoInv");
+ _PREHASH_ParcelDisableObjects = gMessageStringTable.getString("ParcelDisableObjects");
+ _PREHASH_Sections = gMessageStringTable.getString("Sections");
+ _PREHASH_GodLevel = gMessageStringTable.getString("GodLevel");
+ _PREHASH_PayPriceReply = gMessageStringTable.getString("PayPriceReply");
+ _PREHASH_QueryID = gMessageStringTable.getString("QueryID");
+ _PREHASH_CameraEyeOffset = gMessageStringTable.getString("CameraEyeOffset");
+ _PREHASH_AgentPosition = gMessageStringTable.getString("AgentPosition");
+ _PREHASH_GrabPosition = gMessageStringTable.getString("GrabPosition");
+ _PREHASH_OnlineNotification = gMessageStringTable.getString("OnlineNotification");
+ _PREHASH_OfflineNotification = gMessageStringTable.getString("OfflineNotification");
+ _PREHASH_SendPostcard = gMessageStringTable.getString("SendPostcard");
+ _PREHASH_RequestFlags = gMessageStringTable.getString("RequestFlags");
+ _PREHASH_MoneyHistoryRequest = gMessageStringTable.getString("MoneyHistoryRequest");
+ _PREHASH_MoneySummaryRequest = gMessageStringTable.getString("MoneySummaryRequest");
+ _PREHASH_GroupAccountSummaryRequest = gMessageStringTable.getString("GroupAccountSummaryRequest");
+ _PREHASH_GroupVoteHistoryRequest = gMessageStringTable.getString("GroupVoteHistoryRequest");
+ _PREHASH_ParamValue = gMessageStringTable.getString("ParamValue");
+ _PREHASH_Checksum = gMessageStringTable.getString("Checksum");
+ _PREHASH_MaxAgents = gMessageStringTable.getString("MaxAgents");
+ _PREHASH_CreateNewOutfitAttachments = gMessageStringTable.getString("CreateNewOutfitAttachments");
+ _PREHASH_RegionHandle = gMessageStringTable.getString("RegionHandle");
+ _PREHASH_TeleportProgress = gMessageStringTable.getString("TeleportProgress");
+ _PREHASH_AgentQuitCopy = gMessageStringTable.getString("AgentQuitCopy");
+ _PREHASH_ToViewer = gMessageStringTable.getString("ToViewer");
+ _PREHASH_AvatarInterestsUpdate = gMessageStringTable.getString("AvatarInterestsUpdate");
+ _PREHASH_GroupNoticeID = gMessageStringTable.getString("GroupNoticeID");
+ _PREHASH_ParcelName = gMessageStringTable.getString("ParcelName");
+ _PREHASH_PriceObjectRent = gMessageStringTable.getString("PriceObjectRent");
+ _PREHASH_ConnectAgentToUserserver = gMessageStringTable.getString("ConnectAgentToUserserver");
+ _PREHASH_ConnectToUserserver = gMessageStringTable.getString("ConnectToUserserver");
+ _PREHASH_OfferCallingCard = gMessageStringTable.getString("OfferCallingCard");
+ _PREHASH_AgentAccess = gMessageStringTable.getString("AgentAccess");
+ _PREHASH_AcceptCallingCard = gMessageStringTable.getString("AcceptCallingCard");
+ _PREHASH_DeclineCallingCard = gMessageStringTable.getString("DeclineCallingCard");
+ _PREHASH_DataHomeLocationReply = gMessageStringTable.getString("DataHomeLocationReply");
+ _PREHASH_EventLocationReply = gMessageStringTable.getString("EventLocationReply");
+ _PREHASH_TerseDateID = gMessageStringTable.getString("TerseDateID");
+ _PREHASH_ObjectOwner = gMessageStringTable.getString("ObjectOwner");
+ _PREHASH_AssetID = gMessageStringTable.getString("AssetID");
+ _PREHASH_AlertMessage = gMessageStringTable.getString("AlertMessage");
+ _PREHASH_AgentAlertMessage = gMessageStringTable.getString("AgentAlertMessage");
+ _PREHASH_EstateOwnerMessage = gMessageStringTable.getString("EstateOwnerMessage");
+ _PREHASH_ParcelMediaCommandMessage = gMessageStringTable.getString("ParcelMediaCommandMessage");
+ _PREHASH_Auction = gMessageStringTable.getString("Auction");
+ _PREHASH_Category = gMessageStringTable.getString("Category");
+ _PREHASH_FilePath = gMessageStringTable.getString("FilePath");
+ _PREHASH_ItemFlags = gMessageStringTable.getString("ItemFlags");
+ _PREHASH_Invoice = gMessageStringTable.getString("Invoice");
+ _PREHASH_IntervalDays = gMessageStringTable.getString("IntervalDays");
+ _PREHASH_PathScaleX = gMessageStringTable.getString("PathScaleX");
+ _PREHASH_FromTaskID = gMessageStringTable.getString("FromTaskID");
+ _PREHASH_TimeInfo = gMessageStringTable.getString("TimeInfo");
+ _PREHASH_PathScaleY = gMessageStringTable.getString("PathScaleY");
+ _PREHASH_PublicCount = gMessageStringTable.getString("PublicCount");
+ _PREHASH_ParcelJoin = gMessageStringTable.getString("ParcelJoin");
+ _PREHASH_GroupRolesCount = gMessageStringTable.getString("GroupRolesCount");
+ _PREHASH_SimulatorBlock = gMessageStringTable.getString("SimulatorBlock");
+ _PREHASH_GroupID = gMessageStringTable.getString("GroupID");
+ _PREHASH_AgentVel = gMessageStringTable.getString("AgentVel");
+ _PREHASH_RequestImage = gMessageStringTable.getString("RequestImage");
+ _PREHASH_NetStats = gMessageStringTable.getString("NetStats");
+ _PREHASH_AgentPos = gMessageStringTable.getString("AgentPos");
+ _PREHASH_AgentSit = gMessageStringTable.getString("AgentSit");
+ _PREHASH_Material = gMessageStringTable.getString("Material");
+ _PREHASH_ObjectDeGrab = gMessageStringTable.getString("ObjectDeGrab");
+ _PREHASH_VelocityInterpolateOff = gMessageStringTable.getString("VelocityInterpolateOff");
+ _PREHASH_AuthorizedBuyerID = gMessageStringTable.getString("AuthorizedBuyerID");
+ _PREHASH_AvatarPropertiesReply = gMessageStringTable.getString("AvatarPropertiesReply");
+ _PREHASH_GroupProfileReply = gMessageStringTable.getString("GroupProfileReply");
+ _PREHASH_SimOwner = gMessageStringTable.getString("SimOwner");
+ _PREHASH_SalePrice = gMessageStringTable.getString("SalePrice");
+ _PREHASH_Animation = gMessageStringTable.getString("Animation");
+ _PREHASH_OwnerID = gMessageStringTable.getString("OwnerID");
+ _PREHASH_NearestLandingRegionUpdated = gMessageStringTable.getString("NearestLandingRegionUpdated");
+ _PREHASH_PassToAgent = gMessageStringTable.getString("PassToAgent");
+ _PREHASH_PreyAgent = gMessageStringTable.getString("PreyAgent");
+ _PREHASH_SimStats = gMessageStringTable.getString("SimStats");
+ _PREHASH_Options = gMessageStringTable.getString("Options");
+ _PREHASH_LogoutReply = gMessageStringTable.getString("LogoutReply");
+ _PREHASH_FeatureDisabled = gMessageStringTable.getString("FeatureDisabled");
+ _PREHASH_ObjectLocalID = gMessageStringTable.getString("ObjectLocalID");
+ _PREHASH_Dropped = gMessageStringTable.getString("Dropped");
+ _PREHASH_WebProfilesDisabled = gMessageStringTable.getString("WebProfilesDisabled");
+ _PREHASH_Destination = gMessageStringTable.getString("Destination");
+ _PREHASH_MasterID = gMessageStringTable.getString("MasterID");
+ _PREHASH_TransferData = gMessageStringTable.getString("TransferData");
+ _PREHASH_WantToMask = gMessageStringTable.getString("WantToMask");
+ _PREHASH_AvatarData = gMessageStringTable.getString("AvatarData");
+ _PREHASH_ParcelSelectObjects = gMessageStringTable.getString("ParcelSelectObjects");
+ _PREHASH_ExtraParams = gMessageStringTable.getString("ExtraParams");
+ _PREHASH_LogLogin = gMessageStringTable.getString("LogLogin");
+ _PREHASH_CreatorID = gMessageStringTable.getString("CreatorID");
+ _PREHASH_Summary = gMessageStringTable.getString("Summary");
+ _PREHASH_BuyObjectInventory = gMessageStringTable.getString("BuyObjectInventory");
+ _PREHASH_FetchInventory = gMessageStringTable.getString("FetchInventory");
+ _PREHASH_InventoryID = gMessageStringTable.getString("InventoryID");
+ _PREHASH_PacketNumber = gMessageStringTable.getString("PacketNumber");
+ _PREHASH_SetFollowCamProperties = gMessageStringTable.getString("SetFollowCamProperties");
+ _PREHASH_ClearFollowCamProperties = gMessageStringTable.getString("ClearFollowCamProperties");
+ _PREHASH_SequenceID = gMessageStringTable.getString("SequenceID");
+ _PREHASH_DataServerLogout = gMessageStringTable.getString("DataServerLogout");
+ _PREHASH_NameValue = gMessageStringTable.getString("NameValue");
+ _PREHASH_PathShearX = gMessageStringTable.getString("PathShearX");
+ _PREHASH_PathShearY = gMessageStringTable.getString("PathShearY");
+ _PREHASH_Velocity = gMessageStringTable.getString("Velocity");
+ _PREHASH_SecPerYear = gMessageStringTable.getString("SecPerYear");
+ _PREHASH_FirstName = gMessageStringTable.getString("FirstName");
+ _PREHASH_AttachedSoundGainChange = gMessageStringTable.getString("AttachedSoundGainChange");
+ _PREHASH_LocationID = gMessageStringTable.getString("LocationID");
+ _PREHASH_Running = gMessageStringTable.getString("Running");
+ _PREHASH_AgentThrottle = gMessageStringTable.getString("AgentThrottle");
+ _PREHASH_NeighborList = gMessageStringTable.getString("NeighborList");
+ _PREHASH_PathTaperX = gMessageStringTable.getString("PathTaperX");
+ _PREHASH_PathTaperY = gMessageStringTable.getString("PathTaperY");
+ _PREHASH_AgentRelated = gMessageStringTable.getString("AgentRelated");
+ _PREHASH_GranterBlock = gMessageStringTable.getString("GranterBlock");
+ _PREHASH_UseCachedMuteList = gMessageStringTable.getString("UseCachedMuteList");
+ _PREHASH_FailStats = gMessageStringTable.getString("FailStats");
+ _PREHASH_Tempfile = gMessageStringTable.getString("Tempfile");
+ _PREHASH_BuyerID = gMessageStringTable.getString("BuyerID");
+ _PREHASH_DirPeopleReply = gMessageStringTable.getString("DirPeopleReply");
+ _PREHASH_TransferInfo = gMessageStringTable.getString("TransferInfo");
+ _PREHASH_AvatarPickerRequestBackend = gMessageStringTable.getString("AvatarPickerRequestBackend");
+ _PREHASH_AvatarPropertiesRequestBackend = gMessageStringTable.getString("AvatarPropertiesRequestBackend");
+ _PREHASH_UpdateData = gMessageStringTable.getString("UpdateData");
+ _PREHASH_SimFPS = gMessageStringTable.getString("SimFPS");
+ _PREHASH_ReporterID = gMessageStringTable.getString("ReporterID");
+ _PREHASH_ButtonLabel = gMessageStringTable.getString("ButtonLabel");
+ _PREHASH_GranterID = gMessageStringTable.getString("GranterID");
+ _PREHASH_WantToText = gMessageStringTable.getString("WantToText");
+ _PREHASH_ReportType = gMessageStringTable.getString("ReportType");
+ _PREHASH_DataBlock = gMessageStringTable.getString("DataBlock");
+ _PREHASH_SimulatorReady = gMessageStringTable.getString("SimulatorReady");
+ _PREHASH_AnimationSourceList = gMessageStringTable.getString("AnimationSourceList");
+ _PREHASH_SubscribeLoad = gMessageStringTable.getString("SubscribeLoad");
+ _PREHASH_UnsubscribeLoad = gMessageStringTable.getString("UnsubscribeLoad");
+ _PREHASH_Packet = gMessageStringTable.getString("Packet");
+ _PREHASH_UndoLand = gMessageStringTable.getString("UndoLand");
+ _PREHASH_SimAccess = gMessageStringTable.getString("SimAccess");
+ _PREHASH_MembershipFee = gMessageStringTable.getString("MembershipFee");
+ _PREHASH_InviteGroupResponse = gMessageStringTable.getString("InviteGroupResponse");
+ _PREHASH_CreateInventoryFolder = gMessageStringTable.getString("CreateInventoryFolder");
+ _PREHASH_UpdateInventoryFolder = gMessageStringTable.getString("UpdateInventoryFolder");
+ _PREHASH_MoveInventoryFolder = gMessageStringTable.getString("MoveInventoryFolder");
+ _PREHASH_RemoveInventoryFolder = gMessageStringTable.getString("RemoveInventoryFolder");
+ _PREHASH_MoneyData = gMessageStringTable.getString("MoneyData");
+ _PREHASH_ObjectDeselect = gMessageStringTable.getString("ObjectDeselect");
+ _PREHASH_NewAssetID = gMessageStringTable.getString("NewAssetID");
+ _PREHASH_ObjectAdd = gMessageStringTable.getString("ObjectAdd");
+ _PREHASH_RayEndIsIntersection = gMessageStringTable.getString("RayEndIsIntersection");
+ _PREHASH_CompleteAuction = gMessageStringTable.getString("CompleteAuction");
+ _PREHASH_CircuitCode = gMessageStringTable.getString("CircuitCode");
+ _PREHASH_AgentMovementComplete = gMessageStringTable.getString("AgentMovementComplete");
+ _PREHASH_ViewerIP = gMessageStringTable.getString("ViewerIP");
+ _PREHASH_Header = gMessageStringTable.getString("Header");
+ _PREHASH_GestureFlags = gMessageStringTable.getString("GestureFlags");
+ _PREHASH_XferID = gMessageStringTable.getString("XferID");
+ _PREHASH_StatValue = gMessageStringTable.getString("StatValue");
+ _PREHASH_PickID = gMessageStringTable.getString("PickID");
+ _PREHASH_TaskID = gMessageStringTable.getString("TaskID");
+ _PREHASH_GridsPerEdge = gMessageStringTable.getString("GridsPerEdge");
+ _PREHASH_RayEnd = gMessageStringTable.getString("RayEnd");
+ _PREHASH_Throttles = gMessageStringTable.getString("Throttles");
+ _PREHASH_RebakeAvatarTextures = gMessageStringTable.getString("RebakeAvatarTextures");
+ _PREHASH_UpAxis = gMessageStringTable.getString("UpAxis");
+ _PREHASH_AgentTextures = gMessageStringTable.getString("AgentTextures");
+ _PREHASH_NotecardData = gMessageStringTable.getString("NotecardData");
+ _PREHASH_Radius = gMessageStringTable.getString("Radius");
+ _PREHASH_OffCircuit = gMessageStringTable.getString("OffCircuit");
+ _PREHASH_Access = gMessageStringTable.getString("Access");
+ _PREHASH_TitleRoleID = gMessageStringTable.getString("TitleRoleID");
+ _PREHASH_SquareMetersCredit = gMessageStringTable.getString("SquareMetersCredit");
+ _PREHASH_Filename = gMessageStringTable.getString("Filename");
+ _PREHASH_SecuredTemplateChecksumRequest = gMessageStringTable.getString("SecuredTemplateChecksumRequest");
+ _PREHASH_TemplateChecksumRequest = gMessageStringTable.getString("TemplateChecksumRequest");
+ _PREHASH_AgentPresenceRequest = gMessageStringTable.getString("AgentPresenceRequest");
+ _PREHASH_ClassifiedInfoRequest = gMessageStringTable.getString("ClassifiedInfoRequest");
+ _PREHASH_ParcelInfoRequest = gMessageStringTable.getString("ParcelInfoRequest");
+ _PREHASH_ParcelObjectOwnersRequest = gMessageStringTable.getString("ParcelObjectOwnersRequest");
+ _PREHASH_TeleportLandmarkRequest = gMessageStringTable.getString("TeleportLandmarkRequest");
+ _PREHASH_EventInfoRequest = gMessageStringTable.getString("EventInfoRequest");
+ _PREHASH_ChatFromSimulator = gMessageStringTable.getString("ChatFromSimulator");
+ _PREHASH_PickInfoRequest = gMessageStringTable.getString("PickInfoRequest");
+ _PREHASH_MoneyBalanceRequest = gMessageStringTable.getString("MoneyBalanceRequest");
+ _PREHASH_GroupMembersRequest = gMessageStringTable.getString("GroupMembersRequest");
+ _PREHASH_GroupRoleMembersRequest = gMessageStringTable.getString("GroupRoleMembersRequest");
+ _PREHASH_OldFolderID = gMessageStringTable.getString("OldFolderID");
+ _PREHASH_UserInfoRequest = gMessageStringTable.getString("UserInfoRequest");
+ _PREHASH_TextureID = gMessageStringTable.getString("TextureID");
+ _PREHASH_ProfileURL = gMessageStringTable.getString("ProfileURL");
+ _PREHASH_Handle = gMessageStringTable.getString("Handle");
+ _PREHASH_StartParcelRenameAck = gMessageStringTable.getString("StartParcelRenameAck");
+ _PREHASH_ButtonIndex = gMessageStringTable.getString("ButtonIndex");
+ _PREHASH_GetScriptRunning = gMessageStringTable.getString("GetScriptRunning");
+ _PREHASH_SetScriptRunning = gMessageStringTable.getString("SetScriptRunning");
+ _PREHASH_Health = gMessageStringTable.getString("Health");
+ _PREHASH_FileID = gMessageStringTable.getString("FileID");
+ _PREHASH_CircuitInfo = gMessageStringTable.getString("CircuitInfo");
+ _PREHASH_ObjectBuy = gMessageStringTable.getString("ObjectBuy");
+ _PREHASH_ProfileEnd = gMessageStringTable.getString("ProfileEnd");
+ _PREHASH_Effect = gMessageStringTable.getString("Effect");
+ _PREHASH_TestMessage = gMessageStringTable.getString("TestMessage");
+ _PREHASH_ScriptMailRegistration = gMessageStringTable.getString("ScriptMailRegistration");
+ _PREHASH_AgentSetAppearance = gMessageStringTable.getString("AgentSetAppearance");
+ _PREHASH_AvatarAppearance = gMessageStringTable.getString("AvatarAppearance");
+ _PREHASH_RegionData = gMessageStringTable.getString("RegionData");
+ _PREHASH_RequestingRegionData = gMessageStringTable.getString("RequestingRegionData");
+ _PREHASH_LandingRegionData = gMessageStringTable.getString("LandingRegionData");
+ _PREHASH_SitTransform = gMessageStringTable.getString("SitTransform");
+ _PREHASH_TerrainBase0 = gMessageStringTable.getString("TerrainBase0");
+ _PREHASH_SkillsMask = gMessageStringTable.getString("SkillsMask");
+ _PREHASH_AtAxis = gMessageStringTable.getString("AtAxis");
+ _PREHASH_TerrainBase1 = gMessageStringTable.getString("TerrainBase1");
+ _PREHASH_Reason = gMessageStringTable.getString("Reason");
+ _PREHASH_TerrainBase2 = gMessageStringTable.getString("TerrainBase2");
+ _PREHASH_TerrainBase3 = gMessageStringTable.getString("TerrainBase3");
+ _PREHASH_Params = gMessageStringTable.getString("Params");
+ _PREHASH_PingID = gMessageStringTable.getString("PingID");
+ _PREHASH_Change = gMessageStringTable.getString("Change");
+ _PREHASH_Height = gMessageStringTable.getString("Height");
+ _PREHASH_Region = gMessageStringTable.getString("Region");
+ _PREHASH_MoneyHistoryReply = gMessageStringTable.getString("MoneyHistoryReply");
+ _PREHASH_TelehubInfo = gMessageStringTable.getString("TelehubInfo");
+ _PREHASH_StateSave = gMessageStringTable.getString("StateSave");
+ _PREHASH_RoleData = gMessageStringTable.getString("RoleData");
+ _PREHASH_AgentAnimation = gMessageStringTable.getString("AgentAnimation");
+ _PREHASH_AvatarAnimation = gMessageStringTable.getString("AvatarAnimation");
+ _PREHASH_LogDwellTime = gMessageStringTable.getString("LogDwellTime");
+ _PREHASH_ParcelGodMarkAsContent = gMessageStringTable.getString("ParcelGodMarkAsContent");
+ _PREHASH_UsePhysics = gMessageStringTable.getString("UsePhysics");
+ _PREHASH_RegionDenyTransacted = gMessageStringTable.getString("RegionDenyTransacted");
+ _PREHASH_JointType = gMessageStringTable.getString("JointType");
+ _PREHASH_TaxEstimate = gMessageStringTable.getString("TaxEstimate");
+ _PREHASH_ObjectTaxEstimate = gMessageStringTable.getString("ObjectTaxEstimate");
+ _PREHASH_LightTaxEstimate = gMessageStringTable.getString("LightTaxEstimate");
+ _PREHASH_TeleportLandingStatusChanged = gMessageStringTable.getString("TeleportLandingStatusChanged");
+ _PREHASH_LandTaxEstimate = gMessageStringTable.getString("LandTaxEstimate");
+ _PREHASH_GroupTaxEstimate = gMessageStringTable.getString("GroupTaxEstimate");
+ _PREHASH_AvgViewerFPS = gMessageStringTable.getString("AvgViewerFPS");
+ _PREHASH_Buttons = gMessageStringTable.getString("Buttons");
+ _PREHASH_Sender = gMessageStringTable.getString("Sender");
+ _PREHASH_Dialog = gMessageStringTable.getString("Dialog");
+ _PREHASH_TargetData = gMessageStringTable.getString("TargetData");
+ _PREHASH_DestID = gMessageStringTable.getString("DestID");
+ _PREHASH_PricePublicObjectDelete = gMessageStringTable.getString("PricePublicObjectDelete");
+ _PREHASH_ObjectDelete = gMessageStringTable.getString("ObjectDelete");
+ _PREHASH_Delete = gMessageStringTable.getString("Delete");
+ _PREHASH_EventGodDelete = gMessageStringTable.getString("EventGodDelete");
+ _PREHASH_LastTaxDate = gMessageStringTable.getString("LastTaxDate");
+ _PREHASH_MapImageID = gMessageStringTable.getString("MapImageID");
+ _PREHASH_EndDateTime = gMessageStringTable.getString("EndDateTime");
+ _PREHASH_TerrainDetail0 = gMessageStringTable.getString("TerrainDetail0");
+ _PREHASH_TerrainDetail1 = gMessageStringTable.getString("TerrainDetail1");
+ _PREHASH_TerrainDetail2 = gMessageStringTable.getString("TerrainDetail2");
+ _PREHASH_TerrainDetail3 = gMessageStringTable.getString("TerrainDetail3");
+ _PREHASH_Offset = gMessageStringTable.getString("Offset");
+ _PREHASH_ObjectDelink = gMessageStringTable.getString("ObjectDelink");
+ _PREHASH_TargetObject = gMessageStringTable.getString("TargetObject");
+ _PREHASH_IsEstateManager = gMessageStringTable.getString("IsEstateManager");
+ _PREHASH_CancelAuction = gMessageStringTable.getString("CancelAuction");
+ _PREHASH_ObjectDetach = gMessageStringTable.getString("ObjectDetach");
+ _PREHASH_Compressed = gMessageStringTable.getString("Compressed");
+ _PREHASH_PathBegin = gMessageStringTable.getString("PathBegin");
+ _PREHASH_BypassRaycast = gMessageStringTable.getString("BypassRaycast");
+ _PREHASH_WinnerID = gMessageStringTable.getString("WinnerID");
+ _PREHASH_ChannelType = gMessageStringTable.getString("ChannelType");
+ _PREHASH_NonExemptMembers = gMessageStringTable.getString("NonExemptMembers");
+ _PREHASH_Agents = gMessageStringTable.getString("Agents");
+ _PREHASH_SimulatorStart = gMessageStringTable.getString("SimulatorStart");
+ _PREHASH_Enable = gMessageStringTable.getString("Enable");
+ _PREHASH_MemberData = gMessageStringTable.getString("MemberData");
+ _PREHASH_ToGroupID = gMessageStringTable.getString("ToGroupID");
+ _PREHASH_ImageNotInDatabase = gMessageStringTable.getString("ImageNotInDatabase");
+ _PREHASH_StartDate = gMessageStringTable.getString("StartDate");
+ _PREHASH_AnimID = gMessageStringTable.getString("AnimID");
+ _PREHASH_Serial = gMessageStringTable.getString("Serial");
+ _PREHASH_ControlPort = gMessageStringTable.getString("ControlPort");
+ _PREHASH_ModifyLand = gMessageStringTable.getString("ModifyLand");
+ _PREHASH_Digest = gMessageStringTable.getString("Digest");
+ _PREHASH_Victim = gMessageStringTable.getString("Victim");
+ _PREHASH_Script = gMessageStringTable.getString("Script");
+ _PREHASH_TemplateChecksumReply = gMessageStringTable.getString("TemplateChecksumReply");
+ _PREHASH_PickInfoReply = gMessageStringTable.getString("PickInfoReply");
+ _PREHASH_MoneyBalanceReply = gMessageStringTable.getString("MoneyBalanceReply");
+ _PREHASH_RoutedMoneyBalanceReply = gMessageStringTable.getString("RoutedMoneyBalanceReply");
+ _PREHASH_RoleID = gMessageStringTable.getString("RoleID");
+ _PREHASH_RegionInfo = gMessageStringTable.getString("RegionInfo");
+ _PREHASH_Sequence = gMessageStringTable.getString("Sequence");
+ _PREHASH_GodUpdateRegionInfo = gMessageStringTable.getString("GodUpdateRegionInfo");
+ _PREHASH_LocalX = gMessageStringTable.getString("LocalX");
+ _PREHASH_LocalY = gMessageStringTable.getString("LocalY");
+ _PREHASH_StartAnim = gMessageStringTable.getString("StartAnim");
+ _PREHASH_Location = gMessageStringTable.getString("Location");
+ _PREHASH_Action = gMessageStringTable.getString("Action");
+ _PREHASH_Rights = gMessageStringTable.getString("Rights");
+ _PREHASH_SearchDir = gMessageStringTable.getString("SearchDir");
+ _PREHASH_Active = gMessageStringTable.getString("Active");
+ _PREHASH_TransferRequest = gMessageStringTable.getString("TransferRequest");
+ _PREHASH_ScriptSensorRequest = gMessageStringTable.getString("ScriptSensorRequest");
+ _PREHASH_MoneyTransferRequest = gMessageStringTable.getString("MoneyTransferRequest");
+ _PREHASH_EjectGroupMemberRequest = gMessageStringTable.getString("EjectGroupMemberRequest");
+ _PREHASH_SkillsText = gMessageStringTable.getString("SkillsText");
+ _PREHASH_Resent = gMessageStringTable.getString("Resent");
+ _PREHASH_Center = gMessageStringTable.getString("Center");
+ _PREHASH_SharedData = gMessageStringTable.getString("SharedData");
+ _PREHASH_PSBlock = gMessageStringTable.getString("PSBlock");
+ _PREHASH_UUIDNameBlock = gMessageStringTable.getString("UUIDNameBlock");
+ _PREHASH_Viewer = gMessageStringTable.getString("Viewer");
+ _PREHASH_GroupNoticeDelete = gMessageStringTable.getString("GroupNoticeDelete");
+ _PREHASH_GroupTitleUpdate = gMessageStringTable.getString("GroupTitleUpdate");
+ _PREHASH_Method = gMessageStringTable.getString("Method");
+ _PREHASH_TouchName = gMessageStringTable.getString("TouchName");
+ _PREHASH_UpdateType = gMessageStringTable.getString("UpdateType");
+ _PREHASH_KickedFromEstateID = gMessageStringTable.getString("KickedFromEstateID");
+ _PREHASH_CandidateID = gMessageStringTable.getString("CandidateID");
+ _PREHASH_ParamData = gMessageStringTable.getString("ParamData");
+ _PREHASH_GodlikeMessage = gMessageStringTable.getString("GodlikeMessage");
+ _PREHASH_SystemMessage = gMessageStringTable.getString("SystemMessage");
+ _PREHASH_BodyRotation = gMessageStringTable.getString("BodyRotation");
+ _PREHASH_SearchRegions = gMessageStringTable.getString("SearchRegions");
+ _PREHASH_Ignore = gMessageStringTable.getString("Ignore");
+ _PREHASH_AnimationData = gMessageStringTable.getString("AnimationData");
+ _PREHASH_StatID = gMessageStringTable.getString("StatID");
+ _PREHASH_ItemID = gMessageStringTable.getString("ItemID");
+ _PREHASH_AvatarStatisticsReply = gMessageStringTable.getString("AvatarStatisticsReply");
+ _PREHASH_ScriptDialogReply = gMessageStringTable.getString("ScriptDialogReply");
+ _PREHASH_RegionIDAndHandleReply = gMessageStringTable.getString("RegionIDAndHandleReply");
+ _PREHASH_CameraAtOffset = gMessageStringTable.getString("CameraAtOffset");
+ _PREHASH_VoteID = gMessageStringTable.getString("VoteID");
+ _PREHASH_ParcelGodForceOwner = gMessageStringTable.getString("ParcelGodForceOwner");
+ _PREHASH_Filter = gMessageStringTable.getString("Filter");
+ _PREHASH_InviteData = gMessageStringTable.getString("InviteData");
+ _PREHASH_PCode = gMessageStringTable.getString("PCode");
+ _PREHASH_SearchPos = gMessageStringTable.getString("SearchPos");
+ _PREHASH_PreyID = gMessageStringTable.getString("PreyID");
+ _PREHASH_TerrainLowerLimit = gMessageStringTable.getString("TerrainLowerLimit");
+ _PREHASH_EventFlags = gMessageStringTable.getString("EventFlags");
+ _PREHASH_TallyVotes = gMessageStringTable.getString("TallyVotes");
+ _PREHASH_Result = gMessageStringTable.getString("Result");
+ _PREHASH_LookAt = gMessageStringTable.getString("LookAt");
+ _PREHASH_PayButton = gMessageStringTable.getString("PayButton");
+ _PREHASH_SelfCount = gMessageStringTable.getString("SelfCount");
+ _PREHASH_PacketCount = gMessageStringTable.getString("PacketCount");
+ _PREHASH_ParcelBuyPass = gMessageStringTable.getString("ParcelBuyPass");
+ _PREHASH_Identified = gMessageStringTable.getString("Identified");
+ _PREHASH_OldItemID = gMessageStringTable.getString("OldItemID");
+ _PREHASH_RegionPort = gMessageStringTable.getString("RegionPort");
+ _PREHASH_PriceEnergyUnit = gMessageStringTable.getString("PriceEnergyUnit");
+ _PREHASH_Bitmap = gMessageStringTable.getString("Bitmap");
+ _PREHASH_TrackAgentSession = gMessageStringTable.getString("TrackAgentSession");
+ _PREHASH_CacheMissType = gMessageStringTable.getString("CacheMissType");
+ _PREHASH_VFileID = gMessageStringTable.getString("VFileID");
+ _PREHASH_GroupInsigniaID = gMessageStringTable.getString("GroupInsigniaID");
+ _PREHASH_FromID = gMessageStringTable.getString("FromID");
+ _PREHASH_Online = gMessageStringTable.getString("Online");
+ _PREHASH_KickFlags = gMessageStringTable.getString("KickFlags");
+ _PREHASH_CovenantID = gMessageStringTable.getString("CovenantID");
+ _PREHASH_SysCPU = gMessageStringTable.getString("SysCPU");
+ _PREHASH_EMail = gMessageStringTable.getString("EMail");
+ _PREHASH_AggregatePermTextures = gMessageStringTable.getString("AggregatePermTextures");
+ _PREHASH_ChatChannel = gMessageStringTable.getString("ChatChannel");
+ _PREHASH_ReturnID = gMessageStringTable.getString("ReturnID");
+ _PREHASH_ObjectAttach = gMessageStringTable.getString("ObjectAttach");
+ _PREHASH_TargetPort = gMessageStringTable.getString("TargetPort");
+ _PREHASH_ObjectSpinStop = gMessageStringTable.getString("ObjectSpinStop");
+ _PREHASH_FullID = gMessageStringTable.getString("FullID");
+ _PREHASH_ActivateGroup = gMessageStringTable.getString("ActivateGroup");
+ _PREHASH_SysGPU = gMessageStringTable.getString("SysGPU");
+ _PREHASH_AvatarInterestsReply = gMessageStringTable.getString("AvatarInterestsReply");
+ _PREHASH_StartLure = gMessageStringTable.getString("StartLure");
+ _PREHASH_SysRAM = gMessageStringTable.getString("SysRAM");
+ _PREHASH_ObjectPosition = gMessageStringTable.getString("ObjectPosition");
+ _PREHASH_SitPosition = gMessageStringTable.getString("SitPosition");
+ _PREHASH_StartTime = gMessageStringTable.getString("StartTime");
+ _PREHASH_BornOn = gMessageStringTable.getString("BornOn");
+ _PREHASH_CameraCollidePlane = gMessageStringTable.getString("CameraCollidePlane");
+ _PREHASH_EconomyDataRequest = gMessageStringTable.getString("EconomyDataRequest");
+ _PREHASH_TeleportLureRequest = gMessageStringTable.getString("TeleportLureRequest");
+ _PREHASH_FolderID = gMessageStringTable.getString("FolderID");
+ _PREHASH_RegionHandleRequest = gMessageStringTable.getString("RegionHandleRequest");
+ _PREHASH_GestureRequest = gMessageStringTable.getString("GestureRequest");
+ _PREHASH_ScriptDataRequest = gMessageStringTable.getString("ScriptDataRequest");
+ _PREHASH_GroupRoleDataRequest = gMessageStringTable.getString("GroupRoleDataRequest");
+ _PREHASH_GroupTitlesRequest = gMessageStringTable.getString("GroupTitlesRequest");
+ _PREHASH_AgentWearablesRequest = gMessageStringTable.getString("AgentWearablesRequest");
+ _PREHASH_MapBlockRequest = gMessageStringTable.getString("MapBlockRequest");
+ _PREHASH_LureID = gMessageStringTable.getString("LureID");
+ _PREHASH_CopyCenters = gMessageStringTable.getString("CopyCenters");
+ _PREHASH_ParamList = gMessageStringTable.getString("ParamList");
+ _PREHASH_InventorySerial = gMessageStringTable.getString("InventorySerial");
+ _PREHASH_EdgeDataPacket = gMessageStringTable.getString("EdgeDataPacket");
+ _PREHASH_AvatarPickerReply = gMessageStringTable.getString("AvatarPickerReply");
+ _PREHASH_ParcelDwellReply = gMessageStringTable.getString("ParcelDwellReply");
+ _PREHASH_IsForSale = gMessageStringTable.getString("IsForSale");
+ _PREHASH_MuteID = gMessageStringTable.getString("MuteID");
+ _PREHASH_MeanCollisionAlert = gMessageStringTable.getString("MeanCollisionAlert");
+ _PREHASH_CanAcceptTasks = gMessageStringTable.getString("CanAcceptTasks");
+ _PREHASH_ItemData = gMessageStringTable.getString("ItemData");
+ _PREHASH_AnimationList = gMessageStringTable.getString("AnimationList");
+ _PREHASH_PassObject = gMessageStringTable.getString("PassObject");
+ _PREHASH_Reputation = gMessageStringTable.getString("Reputation");
+ _PREHASH_IntValue = gMessageStringTable.getString("IntValue");
+ _PREHASH_TargetType = gMessageStringTable.getString("TargetType");
+ _PREHASH_Amount = gMessageStringTable.getString("Amount");
+ _PREHASH_HasAttachment = gMessageStringTable.getString("HasAttachment");
+ _PREHASH_UpdateAttachment = gMessageStringTable.getString("UpdateAttachment");
+ _PREHASH_RemoveAttachment = gMessageStringTable.getString("RemoveAttachment");
+ _PREHASH_HeightWidthBlock = gMessageStringTable.getString("HeightWidthBlock");
+ _PREHASH_RequestObjectPropertiesFamily = gMessageStringTable.getString("RequestObjectPropertiesFamily");
+ _PREHASH_ObjectPropertiesFamily = gMessageStringTable.getString("ObjectPropertiesFamily");
+ _PREHASH_UserData = gMessageStringTable.getString("UserData");
+ _PREHASH_IsReadable = gMessageStringTable.getString("IsReadable");
+ _PREHASH_PathCurve = gMessageStringTable.getString("PathCurve");
+ _PREHASH_Status = gMessageStringTable.getString("Status");
+ _PREHASH_FromGroup = gMessageStringTable.getString("FromGroup");
+ _PREHASH_AlreadyVoted = gMessageStringTable.getString("AlreadyVoted");
+ _PREHASH_PlacesReply = gMessageStringTable.getString("PlacesReply");
+ _PREHASH_DirPlacesReply = gMessageStringTable.getString("DirPlacesReply");
+ _PREHASH_ParcelBuy = gMessageStringTable.getString("ParcelBuy");
+ _PREHASH_DirFindQueryBackend = gMessageStringTable.getString("DirFindQueryBackend");
+ _PREHASH_DirPlacesQueryBackend = gMessageStringTable.getString("DirPlacesQueryBackend");
+ _PREHASH_DirClassifiedQueryBackend = gMessageStringTable.getString("DirClassifiedQueryBackend");
+ _PREHASH_DirPicksQueryBackend = gMessageStringTable.getString("DirPicksQueryBackend");
+ _PREHASH_DirLandQueryBackend = gMessageStringTable.getString("DirLandQueryBackend");
+ _PREHASH_DirPopularQueryBackend = gMessageStringTable.getString("DirPopularQueryBackend");
+ _PREHASH_LogoutDemand = gMessageStringTable.getString("LogoutDemand");
+ _PREHASH_HistoryData = gMessageStringTable.getString("HistoryData");
+ _PREHASH_SnapshotID = gMessageStringTable.getString("SnapshotID");
+ _PREHASH_Aspect = gMessageStringTable.getString("Aspect");
+ _PREHASH_ParamSize = gMessageStringTable.getString("ParamSize");
+ _PREHASH_VoteCast = gMessageStringTable.getString("VoteCast");
+ _PREHASH_CastsShadows = gMessageStringTable.getString("CastsShadows");
+ _PREHASH_EveryoneMask = gMessageStringTable.getString("EveryoneMask");
+ _PREHASH_SetSunPhase = gMessageStringTable.getString("SetSunPhase");
+ _PREHASH_ObjectSpinUpdate = gMessageStringTable.getString("ObjectSpinUpdate");
+ _PREHASH_MaturePublish = gMessageStringTable.getString("MaturePublish");
+ _PREHASH_UseExistingAsset = gMessageStringTable.getString("UseExistingAsset");
+ _PREHASH_Powers = gMessageStringTable.getString("Powers");
+ _PREHASH_ParcelLocalID = gMessageStringTable.getString("ParcelLocalID");
+ _PREHASH_TeleportCancel = gMessageStringTable.getString("TeleportCancel");
+ _PREHASH_UnixTime = gMessageStringTable.getString("UnixTime");
+ _PREHASH_QueryFlags = gMessageStringTable.getString("QueryFlags");
+ _PREHASH_LastExecFroze = gMessageStringTable.getString("LastExecFroze");
+ _PREHASH_AlwaysRun = gMessageStringTable.getString("AlwaysRun");
+ _PREHASH_Bottom = gMessageStringTable.getString("Bottom");
+ _PREHASH_ButtonData = gMessageStringTable.getString("ButtonData");
+ _PREHASH_SoundData = gMessageStringTable.getString("SoundData");
+ _PREHASH_ViewerStats = gMessageStringTable.getString("ViewerStats");
+ _PREHASH_RegionHandshake = gMessageStringTable.getString("RegionHandshake");
+ _PREHASH_ObjectDescription = gMessageStringTable.getString("ObjectDescription");
+ _PREHASH_Description = gMessageStringTable.getString("Description");
+ _PREHASH_ParamType = gMessageStringTable.getString("ParamType");
+ _PREHASH_UUIDNameReply = gMessageStringTable.getString("UUIDNameReply");
+ _PREHASH_UUIDGroupNameReply = gMessageStringTable.getString("UUIDGroupNameReply");
+ _PREHASH_SaveAssetIntoInventory = gMessageStringTable.getString("SaveAssetIntoInventory");
+ _PREHASH_UserInfo = gMessageStringTable.getString("UserInfo");
+ _PREHASH_AnimSequenceID = gMessageStringTable.getString("AnimSequenceID");
+ _PREHASH_NVPairs = gMessageStringTable.getString("NVPairs");
+ _PREHASH_GroupNoticesListRequest = gMessageStringTable.getString("GroupNoticesListRequest");
+ _PREHASH_ParcelAccessListRequest = gMessageStringTable.getString("ParcelAccessListRequest");
+ _PREHASH_MuteListRequest = gMessageStringTable.getString("MuteListRequest");
+ _PREHASH_StartPeriod = gMessageStringTable.getString("StartPeriod");
+ _PREHASH_RpcChannelRequest = gMessageStringTable.getString("RpcChannelRequest");
+ _PREHASH_LandStatRequest = gMessageStringTable.getString("LandStatRequest");
+ _PREHASH_PlacesQuery = gMessageStringTable.getString("PlacesQuery");
+ _PREHASH_DirPlacesQuery = gMessageStringTable.getString("DirPlacesQuery");
+ _PREHASH_SortOrder = gMessageStringTable.getString("SortOrder");
+ _PREHASH_Hunter = gMessageStringTable.getString("Hunter");
+ _PREHASH_SunAngVelocity = gMessageStringTable.getString("SunAngVelocity");
+ _PREHASH_BinaryBucket = gMessageStringTable.getString("BinaryBucket");
+ _PREHASH_ImagePacket = gMessageStringTable.getString("ImagePacket");
+ _PREHASH_StartGroupProposal = gMessageStringTable.getString("StartGroupProposal");
+ _PREHASH_EnergyLevel = gMessageStringTable.getString("EnergyLevel");
+ _PREHASH_PriceForListing = gMessageStringTable.getString("PriceForListing");
+ _PREHASH_Scale = gMessageStringTable.getString("Scale");
+ _PREHASH_EstateCovenantReply = gMessageStringTable.getString("EstateCovenantReply");
+ _PREHASH_ParentEstateID = gMessageStringTable.getString("ParentEstateID");
+ _PREHASH_Extra2 = gMessageStringTable.getString("Extra2");
+ _PREHASH_Throttle = gMessageStringTable.getString("Throttle");
+ _PREHASH_SimIP = gMessageStringTable.getString("SimIP");
+ _PREHASH_GodID = gMessageStringTable.getString("GodID");
+ _PREHASH_TeleportMinPrice = gMessageStringTable.getString("TeleportMinPrice");
+ _PREHASH_VoteItem = gMessageStringTable.getString("VoteItem");
+ _PREHASH_ObjectRotation = gMessageStringTable.getString("ObjectRotation");
+ _PREHASH_SitRotation = gMessageStringTable.getString("SitRotation");
+ _PREHASH_SnapSelection = gMessageStringTable.getString("SnapSelection");
+ _PREHASH_SoundTrigger = gMessageStringTable.getString("SoundTrigger");
+ _PREHASH_TerrainRaiseLimit = gMessageStringTable.getString("TerrainRaiseLimit");
+ _PREHASH_Quorum = gMessageStringTable.getString("Quorum");
+ _PREHASH_TokenBlock = gMessageStringTable.getString("TokenBlock");
+ _PREHASH_AgentBlock = gMessageStringTable.getString("AgentBlock");
+ _PREHASH_CommandBlock = gMessageStringTable.getString("CommandBlock");
+ _PREHASH_PricePublicObjectDecay = gMessageStringTable.getString("PricePublicObjectDecay");
+ _PREHASH_SpawnPointPos = gMessageStringTable.getString("SpawnPointPos");
+ _PREHASH_AttachedSoundCutoffRadius = gMessageStringTable.getString("AttachedSoundCutoffRadius");
+ _PREHASH_VolumeDetail = gMessageStringTable.getString("VolumeDetail");
+ _PREHASH_FromAgentName = gMessageStringTable.getString("FromAgentName");
+ _PREHASH_Range = gMessageStringTable.getString("Range");
+ _PREHASH_DirectoryVisibility = gMessageStringTable.getString("DirectoryVisibility");
+ _PREHASH_PublicIP = gMessageStringTable.getString("PublicIP");
+ _PREHASH_TeleportFailed = gMessageStringTable.getString("TeleportFailed");
+ _PREHASH_OnlineStatusReply = gMessageStringTable.getString("OnlineStatusReply");
+ _PREHASH_RequestAvatarInfo = gMessageStringTable.getString("RequestAvatarInfo");
+ _PREHASH_PreloadSound = gMessageStringTable.getString("PreloadSound");
+ _PREHASH_ScreenshotID = gMessageStringTable.getString("ScreenshotID");
+ _PREHASH_CovenantTimestamp = gMessageStringTable.getString("CovenantTimestamp");
+ _PREHASH_OldestUnacked = gMessageStringTable.getString("OldestUnacked");
+ _PREHASH_SimulatorIP = gMessageStringTable.getString("SimulatorIP");
+ _PREHASH_ObjectImport = gMessageStringTable.getString("ObjectImport");
+ _PREHASH_Value = gMessageStringTable.getString("Value");
+ _PREHASH_JointAxisOrAnchor = gMessageStringTable.getString("JointAxisOrAnchor");
+ _PREHASH_Test0 = gMessageStringTable.getString("Test0");
+ _PREHASH_Test1 = gMessageStringTable.getString("Test1");
+ _PREHASH_Test2 = gMessageStringTable.getString("Test2");
+ _PREHASH_SunPhase = gMessageStringTable.getString("SunPhase");
+ _PREHASH_Place = gMessageStringTable.getString("Place");
+ _PREHASH_Phase = gMessageStringTable.getString("Phase");
+ _PREHASH_ParcelDivide = gMessageStringTable.getString("ParcelDivide");
+ _PREHASH_PriceObjectClaim = gMessageStringTable.getString("PriceObjectClaim");
+ _PREHASH_Field = gMessageStringTable.getString("Field");
+ _PREHASH_Ratio = gMessageStringTable.getString("Ratio");
+ _PREHASH_JoinGroupReply = gMessageStringTable.getString("JoinGroupReply");
+ _PREHASH_LiveHelpGroupReply = gMessageStringTable.getString("LiveHelpGroupReply");
+ _PREHASH_Score = gMessageStringTable.getString("Score");
+ _PREHASH_ExpungeData = gMessageStringTable.getString("ExpungeData");
+ _PREHASH_Image = gMessageStringTable.getString("Image");
+ _PREHASH_ObjectClickAction = gMessageStringTable.getString("ObjectClickAction");
+ _PREHASH_Delta = gMessageStringTable.getString("Delta");
+ _PREHASH_InitiateUpload = gMessageStringTable.getString("InitiateUpload");
+ _PREHASH_Parameter = gMessageStringTable.getString("Parameter");
+ _PREHASH_Flags = gMessageStringTable.getString("Flags");
+ _PREHASH_Plane = gMessageStringTable.getString("Plane");
+ _PREHASH_Width = gMessageStringTable.getString("Width");
+ _PREHASH_Right = gMessageStringTable.getString("Right");
+ _PREHASH_DirFindQuery = gMessageStringTable.getString("DirFindQuery");
+ _PREHASH_Textures = gMessageStringTable.getString("Textures");
+ _PREHASH_EventData = gMessageStringTable.getString("EventData");
+ _PREHASH_Final = gMessageStringTable.getString("Final");
+ _PREHASH_TelehubPos = gMessageStringTable.getString("TelehubPos");
+ _PREHASH_ReportAutosaveCrash = gMessageStringTable.getString("ReportAutosaveCrash");
+ _PREHASH_Reset = gMessageStringTable.getString("Reset");
+ _PREHASH_CreateTrustedCircuit = gMessageStringTable.getString("CreateTrustedCircuit");
+ _PREHASH_DenyTrustedCircuit = gMessageStringTable.getString("DenyTrustedCircuit");
+ _PREHASH_RequestTrustedCircuit = gMessageStringTable.getString("RequestTrustedCircuit");
+ _PREHASH_Codec = gMessageStringTable.getString("Codec");
+ _PREHASH_Level = gMessageStringTable.getString("Level");
+ _PREHASH_Modal = gMessageStringTable.getString("Modal");
+ _PREHASH_ChildAgentUnknown = gMessageStringTable.getString("ChildAgentUnknown");
+ _PREHASH_LandingType = gMessageStringTable.getString("LandingType");
+ _PREHASH_ScriptRunningReply = gMessageStringTable.getString("ScriptRunningReply");
+ _PREHASH_MoneyDetailsReply = gMessageStringTable.getString("MoneyDetailsReply");
+ _PREHASH_Reply = gMessageStringTable.getString("Reply");
+ _PREHASH_TelehubRot = gMessageStringTable.getString("TelehubRot");
+ _PREHASH_RequestFriendship = gMessageStringTable.getString("RequestFriendship");
+ _PREHASH_AcceptFriendship = gMessageStringTable.getString("AcceptFriendship");
+ _PREHASH_GroupAccountDetailsReply = gMessageStringTable.getString("GroupAccountDetailsReply");
+ _PREHASH_DwellInfo = gMessageStringTable.getString("DwellInfo");
+ _PREHASH_AgentResume = gMessageStringTable.getString("AgentResume");
+ _PREHASH_ItemType = gMessageStringTable.getString("ItemType");
+ _PREHASH_MailFilter = gMessageStringTable.getString("MailFilter");
+ _PREHASH_Disconnect = gMessageStringTable.getString("Disconnect");
+ _PREHASH_SimPosition = gMessageStringTable.getString("SimPosition");
+ _PREHASH_SimWideTotalPrims = gMessageStringTable.getString("SimWideTotalPrims");
+ _PREHASH_Index = gMessageStringTable.getString("Index");
+ _PREHASH_BaseFilename = gMessageStringTable.getString("BaseFilename");
+ _PREHASH_SimFilename = gMessageStringTable.getString("SimFilename");
+ _PREHASH_LastOwnerID = gMessageStringTable.getString("LastOwnerID");
+ _PREHASH_GroupNoticeRequest = gMessageStringTable.getString("GroupNoticeRequest");
+ _PREHASH_EmailMessageRequest = gMessageStringTable.getString("EmailMessageRequest");
+ _PREHASH_MapItemRequest = gMessageStringTable.getString("MapItemRequest");
+ _PREHASH_AgentCount = gMessageStringTable.getString("AgentCount");
+ _PREHASH_MessageBlock = gMessageStringTable.getString("MessageBlock");
+ _PREHASH_FuseBlock = gMessageStringTable.getString("FuseBlock");
+ _PREHASH_AgentGroupData = gMessageStringTable.getString("AgentGroupData");
+ _PREHASH_ClassifiedInfoUpdate = gMessageStringTable.getString("ClassifiedInfoUpdate");
+ _PREHASH_RegionPos = gMessageStringTable.getString("RegionPos");
+ _PREHASH_ParcelMediaUpdate = gMessageStringTable.getString("ParcelMediaUpdate");
+ _PREHASH_NoticeID = gMessageStringTable.getString("NoticeID");
+ _PREHASH_GridX = gMessageStringTable.getString("GridX");
+ _PREHASH_GridY = gMessageStringTable.getString("GridY");
+ _PREHASH_Title = gMessageStringTable.getString("Title");
+ _PREHASH_AuctionID = gMessageStringTable.getString("AuctionID");
+ _PREHASH_VoteType = gMessageStringTable.getString("VoteType");
+ _PREHASH_CategoryID = gMessageStringTable.getString("CategoryID");
+ _PREHASH_Token = gMessageStringTable.getString("Token");
+ _PREHASH_AggregatePerms = gMessageStringTable.getString("AggregatePerms");
+ _PREHASH_StartParcelRemoveAck = gMessageStringTable.getString("StartParcelRemoveAck");
+ _PREHASH_ObjectSelect = gMessageStringTable.getString("ObjectSelect");
+ _PREHASH_ForceObjectSelect = gMessageStringTable.getString("ForceObjectSelect");
+ _PREHASH_Price = gMessageStringTable.getString("Price");
+ _PREHASH_SunDirection = gMessageStringTable.getString("SunDirection");
+ _PREHASH_FromName = gMessageStringTable.getString("FromName");
+ _PREHASH_ChangeInventoryItemFlags = gMessageStringTable.getString("ChangeInventoryItemFlags");
+ _PREHASH_Force = gMessageStringTable.getString("Force");
+ _PREHASH_TransactionBlock = gMessageStringTable.getString("TransactionBlock");
+ _PREHASH_PowersMask = gMessageStringTable.getString("PowersMask");
+ _PREHASH_Stamp = gMessageStringTable.getString("Stamp");
+ _PREHASH_TotalCredits = gMessageStringTable.getString("TotalCredits");
+ _PREHASH_State = gMessageStringTable.getString("State");
+ _PREHASH_TextureIndex = gMessageStringTable.getString("TextureIndex");
+ _PREHASH_InviteeID = gMessageStringTable.getString("InviteeID");
+ _PREHASH_ParcelReclaim = gMessageStringTable.getString("ParcelReclaim");
+ _PREHASH_Money = gMessageStringTable.getString("Money");
+ _PREHASH_PathTwist = gMessageStringTable.getString("PathTwist");
+ _PREHASH_AuthBuyerID = gMessageStringTable.getString("AuthBuyerID");
+ _PREHASH_Color = gMessageStringTable.getString("Color");
+ _PREHASH_SourceType = gMessageStringTable.getString("SourceType");
+ _PREHASH_World = gMessageStringTable.getString("World");
+ _PREHASH_QueryData = gMessageStringTable.getString("QueryData");
+ _PREHASH_Users = gMessageStringTable.getString("Users");
+ _PREHASH_SysOS = gMessageStringTable.getString("SysOS");
+ _PREHASH_Notes = gMessageStringTable.getString("Notes");
+ _PREHASH_AvatarID = gMessageStringTable.getString("AvatarID");
+ _PREHASH_FounderID = gMessageStringTable.getString("FounderID");
+ _PREHASH_EndPointID = gMessageStringTable.getString("EndPointID");
+ _PREHASH_StipendEstimate = gMessageStringTable.getString("StipendEstimate");
+ _PREHASH_LocationLookAt = gMessageStringTable.getString("LocationLookAt");
+ _PREHASH_Sound = gMessageStringTable.getString("Sound");
+ _PREHASH_Cover = gMessageStringTable.getString("Cover");
+ _PREHASH_TotalObjectCount = gMessageStringTable.getString("TotalObjectCount");
+ _PREHASH_TextureEntry = gMessageStringTable.getString("TextureEntry");
+ _PREHASH_SquareMetersCommitted = gMessageStringTable.getString("SquareMetersCommitted");
+ _PREHASH_ChannelID = gMessageStringTable.getString("ChannelID");
+ _PREHASH_Dwell = gMessageStringTable.getString("Dwell");
+ _PREHASH_North = gMessageStringTable.getString("North");
+ _PREHASH_AgentUpdate = gMessageStringTable.getString("AgentUpdate");
+ _PREHASH_PickGodDelete = gMessageStringTable.getString("PickGodDelete");
+ _PREHASH_HostName = gMessageStringTable.getString("HostName");
+ _PREHASH_PriceParcelClaim = gMessageStringTable.getString("PriceParcelClaim");
+ _PREHASH_ParcelClaim = gMessageStringTable.getString("ParcelClaim");
+ _PREHASH_AgentPowers = gMessageStringTable.getString("AgentPowers");
+ _PREHASH_ProfileHollow = gMessageStringTable.getString("ProfileHollow");
+ _PREHASH_GroupRoleChanges = gMessageStringTable.getString("GroupRoleChanges");
+ _PREHASH_Count = gMessageStringTable.getString("Count");
+ _PREHASH_South = gMessageStringTable.getString("South");
+ _PREHASH_Entry = gMessageStringTable.getString("Entry");
+ _PREHASH_ObjectUpdateCompressed = gMessageStringTable.getString("ObjectUpdateCompressed");
+ _PREHASH_MuteFlags = gMessageStringTable.getString("MuteFlags");
+ _PREHASH_Group = gMessageStringTable.getString("Group");
+ _PREHASH_AgentPause = gMessageStringTable.getString("AgentPause");
+ _PREHASH_LanguagesText = gMessageStringTable.getString("LanguagesText");
+ _PREHASH_InternalScriptMail = gMessageStringTable.getString("InternalScriptMail");
+ _PREHASH_FindAgent = gMessageStringTable.getString("FindAgent");
+ _PREHASH_AgentData = gMessageStringTable.getString("AgentData");
+ _PREHASH_FolderData = gMessageStringTable.getString("FolderData");
+ _PREHASH_AssetBlock = gMessageStringTable.getString("AssetBlock");
+ _PREHASH_AcceptNotices = gMessageStringTable.getString("AcceptNotices");
+ _PREHASH_SetGroupAcceptNotices = gMessageStringTable.getString("SetGroupAcceptNotices");
+ _PREHASH_CloseCircuit = gMessageStringTable.getString("CloseCircuit");
+ _PREHASH_LogControl = gMessageStringTable.getString("LogControl");
+ _PREHASH_TeleportFinish = gMessageStringTable.getString("TeleportFinish");
+ _PREHASH_PathRevolutions = gMessageStringTable.getString("PathRevolutions");
+ _PREHASH_ClassifiedInfoReply = gMessageStringTable.getString("ClassifiedInfoReply");
+ _PREHASH_ParcelInfoReply = gMessageStringTable.getString("ParcelInfoReply");
+ _PREHASH_AutosaveData = gMessageStringTable.getString("AutosaveData");
+ _PREHASH_SetStartLocation = gMessageStringTable.getString("SetStartLocation");
+ _PREHASH_PassHours = gMessageStringTable.getString("PassHours");
+ _PREHASH_AttachmentPt = gMessageStringTable.getString("AttachmentPt");
+ _PREHASH_ParcelFlags = gMessageStringTable.getString("ParcelFlags");
+ _PREHASH_NumVotes = gMessageStringTable.getString("NumVotes");
+ _PREHASH_AvatarPickerRequest = gMessageStringTable.getString("AvatarPickerRequest");
+ _PREHASH_TeleportLocationRequest = gMessageStringTable.getString("TeleportLocationRequest");
+ _PREHASH_DataHomeLocationRequest = gMessageStringTable.getString("DataHomeLocationRequest");
+ _PREHASH_EventNotificationAddRequest = gMessageStringTable.getString("EventNotificationAddRequest");
+ _PREHASH_ParcelDwellRequest = gMessageStringTable.getString("ParcelDwellRequest");
+ _PREHASH_EventLocationRequest = gMessageStringTable.getString("EventLocationRequest");
+ _PREHASH_EndPeriod = gMessageStringTable.getString("EndPeriod");
+ _PREHASH_SetStartLocationRequest = gMessageStringTable.getString("SetStartLocationRequest");
+ _PREHASH_QueryStart = gMessageStringTable.getString("QueryStart");
+ _PREHASH_EjectData = gMessageStringTable.getString("EjectData");
+ _PREHASH_AvatarTextureUpdate = gMessageStringTable.getString("AvatarTextureUpdate");
+ _PREHASH_RPCServerPort = gMessageStringTable.getString("RPCServerPort");
+ _PREHASH_Bytes = gMessageStringTable.getString("Bytes");
+ _PREHASH_Extra = gMessageStringTable.getString("Extra");
+ _PREHASH_ForceScriptControlRelease = gMessageStringTable.getString("ForceScriptControlRelease");
+ _PREHASH_ParcelRelease = gMessageStringTable.getString("ParcelRelease");
+ _PREHASH_VFileType = gMessageStringTable.getString("VFileType");
+ _PREHASH_EjectGroupMemberReply = gMessageStringTable.getString("EjectGroupMemberReply");
+ _PREHASH_ImageData = gMessageStringTable.getString("ImageData");
+ _PREHASH_SpaceServerSimulatorTimeMessage = gMessageStringTable.getString("SpaceServerSimulatorTimeMessage");
+ _PREHASH_SimulatorViewerTimeMessage = gMessageStringTable.getString("SimulatorViewerTimeMessage");
+ _PREHASH_Rotation = gMessageStringTable.getString("Rotation");
+ _PREHASH_Selection = gMessageStringTable.getString("Selection");
+ _PREHASH_TransactionData = gMessageStringTable.getString("TransactionData");
+ _PREHASH_OperationData = gMessageStringTable.getString("OperationData");
+ _PREHASH_ExpirationDate = gMessageStringTable.getString("ExpirationDate");
+ _PREHASH_ParcelDeedToGroup = gMessageStringTable.getString("ParcelDeedToGroup");
+ _PREHASH_DirPicksReply = gMessageStringTable.getString("DirPicksReply");
+ _PREHASH_AvatarPicksReply = gMessageStringTable.getString("AvatarPicksReply");
+ _PREHASH_GroupTitlesReply = gMessageStringTable.getString("GroupTitlesReply");
+ _PREHASH_AgentInfo = gMessageStringTable.getString("AgentInfo");
+ _PREHASH_MoneyTransferBackend = gMessageStringTable.getString("MoneyTransferBackend");
+ _PREHASH_NextOwnerMask = gMessageStringTable.getString("NextOwnerMask");
+ _PREHASH_MuteData = gMessageStringTable.getString("MuteData");
+ _PREHASH_PassPrice = gMessageStringTable.getString("PassPrice");
+ _PREHASH_SourceID = gMessageStringTable.getString("SourceID");
+ _PREHASH_ChangeUserRights = gMessageStringTable.getString("ChangeUserRights");
+ _PREHASH_TeleportFlags = gMessageStringTable.getString("TeleportFlags");
+ _PREHASH_AssetData = gMessageStringTable.getString("AssetData");
+ _PREHASH_SlaveParcelData = gMessageStringTable.getString("SlaveParcelData");
+ _PREHASH_MultipleObjectUpdate = gMessageStringTable.getString("MultipleObjectUpdate");
+ _PREHASH_ObjectUpdate = gMessageStringTable.getString("ObjectUpdate");
+ _PREHASH_ImprovedTerseObjectUpdate = gMessageStringTable.getString("ImprovedTerseObjectUpdate");
+ _PREHASH_ConfirmXferPacket = gMessageStringTable.getString("ConfirmXferPacket");
+ _PREHASH_StartPingCheck = gMessageStringTable.getString("StartPingCheck");
+ _PREHASH_SimWideDeletes = gMessageStringTable.getString("SimWideDeletes");
+ _PREHASH_LandStatReply = gMessageStringTable.getString("LandStatReply");
+ _PREHASH_IsPhantom = gMessageStringTable.getString("IsPhantom");
+ _PREHASH_AgentList = gMessageStringTable.getString("AgentList");
+ _PREHASH_SimApproved = gMessageStringTable.getString("SimApproved");
+ _PREHASH_RezObject = gMessageStringTable.getString("RezObject");
+ _PREHASH_TaskLocalID = gMessageStringTable.getString("TaskLocalID");
+ _PREHASH_ClaimDate = gMessageStringTable.getString("ClaimDate");
+ _PREHASH_MergeParcel = gMessageStringTable.getString("MergeParcel");
+ _PREHASH_Priority = gMessageStringTable.getString("Priority");
+ _PREHASH_Building = gMessageStringTable.getString("Building");
+ _PREHASH_QueryText = gMessageStringTable.getString("QueryText");
+ _PREHASH_GroupNoticeAdd = gMessageStringTable.getString("GroupNoticeAdd");
+ _PREHASH_ReturnType = gMessageStringTable.getString("ReturnType");
+ _PREHASH_FetchFolders = gMessageStringTable.getString("FetchFolders");
+ _PREHASH_SimulatorPublicHostBlock = gMessageStringTable.getString("SimulatorPublicHostBlock");
+ _PREHASH_HeaderData = gMessageStringTable.getString("HeaderData");
+ _PREHASH_RequestMultipleObjects = gMessageStringTable.getString("RequestMultipleObjects");
+ _PREHASH_RetrieveInstantMessages = gMessageStringTable.getString("RetrieveInstantMessages");
+ _PREHASH_DequeueInstantMessages = gMessageStringTable.getString("DequeueInstantMessages");
+ _PREHASH_OpenCircuit = gMessageStringTable.getString("OpenCircuit");
+ _PREHASH_SecureSessionID = gMessageStringTable.getString("SecureSessionID");
+ _PREHASH_CrossedRegion = gMessageStringTable.getString("CrossedRegion");
+ _PREHASH_DirGroupsReply = gMessageStringTable.getString("DirGroupsReply");
+ _PREHASH_AvatarGroupsReply = gMessageStringTable.getString("AvatarGroupsReply");
+ _PREHASH_EmailMessageReply = gMessageStringTable.getString("EmailMessageReply");
+ _PREHASH_GroupVoteHistoryItemReply = gMessageStringTable.getString("GroupVoteHistoryItemReply");
+ _PREHASH_ViewerPosition = gMessageStringTable.getString("ViewerPosition");
+ _PREHASH_Position = gMessageStringTable.getString("Position");
+ _PREHASH_ParentEstate = gMessageStringTable.getString("ParentEstate");
+ _PREHASH_EstateName = gMessageStringTable.getString("EstateName");
+ _PREHASH_MuteName = gMessageStringTable.getString("MuteName");
+ _PREHASH_StartParcelRename = gMessageStringTable.getString("StartParcelRename");
+ _PREHASH_BulkParcelRename = gMessageStringTable.getString("BulkParcelRename");
+ _PREHASH_ParcelRename = gMessageStringTable.getString("ParcelRename");
+ _PREHASH_ViewerFilename = gMessageStringTable.getString("ViewerFilename");
+ _PREHASH_Positive = gMessageStringTable.getString("Positive");
+ _PREHASH_UserReportInternal = gMessageStringTable.getString("UserReportInternal");
+ _PREHASH_AvatarPropertiesRequest = gMessageStringTable.getString("AvatarPropertiesRequest");
+ _PREHASH_ParcelPropertiesRequest = gMessageStringTable.getString("ParcelPropertiesRequest");
+ _PREHASH_GroupProfileRequest = gMessageStringTable.getString("GroupProfileRequest");
+ _PREHASH_AgentDataUpdateRequest = gMessageStringTable.getString("AgentDataUpdateRequest");
+ _PREHASH_PriceObjectScaleFactor = gMessageStringTable.getString("PriceObjectScaleFactor");
+ _PREHASH_DirPicksQuery = gMessageStringTable.getString("DirPicksQuery");
+ _PREHASH_OpenEnrollment = gMessageStringTable.getString("OpenEnrollment");
+ _PREHASH_GroupData = gMessageStringTable.getString("GroupData");
+ _PREHASH_RequestGodlikePowers = gMessageStringTable.getString("RequestGodlikePowers");
+ _PREHASH_GrantGodlikePowers = gMessageStringTable.getString("GrantGodlikePowers");
+ _PREHASH_TransactionID = gMessageStringTable.getString("TransactionID");
+ _PREHASH_DestinationID = gMessageStringTable.getString("DestinationID");
+ _PREHASH_Controls = gMessageStringTable.getString("Controls");
+ _PREHASH_FirstDetachAll = gMessageStringTable.getString("FirstDetachAll");
+ _PREHASH_EstateID = gMessageStringTable.getString("EstateID");
+ _PREHASH_ImprovedInstantMessage = gMessageStringTable.getString("ImprovedInstantMessage");
+ _PREHASH_AgentQuit = gMessageStringTable.getString("AgentQuit");
+ _PREHASH_CheckParcelSales = gMessageStringTable.getString("CheckParcelSales");
+ _PREHASH_ParcelSales = gMessageStringTable.getString("ParcelSales");
+ _PREHASH_CurrentInterval = gMessageStringTable.getString("CurrentInterval");
+ _PREHASH_PriceRentLight = gMessageStringTable.getString("PriceRentLight");
+ _PREHASH_MediaAutoScale = gMessageStringTable.getString("MediaAutoScale");
+ _PREHASH_NeighborBlock = gMessageStringTable.getString("NeighborBlock");
+ _PREHASH_LayerData = gMessageStringTable.getString("LayerData");
+ _PREHASH_NVPairData = gMessageStringTable.getString("NVPairData");
+ _PREHASH_TeleportLocal = gMessageStringTable.getString("TeleportLocal");
+ _PREHASH_EjecteeID = gMessageStringTable.getString("EjecteeID");
+ _PREHASH_VoteInitiator = gMessageStringTable.getString("VoteInitiator");
+ _PREHASH_TypeData = gMessageStringTable.getString("TypeData");
+ _PREHASH_OwnerIDs = gMessageStringTable.getString("OwnerIDs");
+ _PREHASH_SystemKickUser = gMessageStringTable.getString("SystemKickUser");
+ _PREHASH_TransactionTime = gMessageStringTable.getString("TransactionTime");
+ _PREHASH_TimeToLive = gMessageStringTable.getString("TimeToLive");
+ _PREHASH_StartParcelRemove = gMessageStringTable.getString("StartParcelRemove");
+ _PREHASH_BulkParcelRemove = gMessageStringTable.getString("BulkParcelRemove");
+ _PREHASH_OldAgentID = gMessageStringTable.getString("OldAgentID");
+ _PREHASH_BonusEstimate = gMessageStringTable.getString("BonusEstimate");
+ _PREHASH_MusicURL = gMessageStringTable.getString("MusicURL");
+ _PREHASH_CompleteLure = gMessageStringTable.getString("CompleteLure");
+ _PREHASH_ParcelPrimBonus = gMessageStringTable.getString("ParcelPrimBonus");
+ _PREHASH_EjectUser = gMessageStringTable.getString("EjectUser");
+ _PREHASH_CoarseLocationUpdate = gMessageStringTable.getString("CoarseLocationUpdate");
+ _PREHASH_ChildAgentPositionUpdate = gMessageStringTable.getString("ChildAgentPositionUpdate");
+ _PREHASH_StoreLocal = gMessageStringTable.getString("StoreLocal");
+ _PREHASH_GroupName = gMessageStringTable.getString("GroupName");
+ _PREHASH_PriceParcelRent = gMessageStringTable.getString("PriceParcelRent");
+ _PREHASH_SimStatus = gMessageStringTable.getString("SimStatus");
+ _PREHASH_TransactionSuccess = gMessageStringTable.getString("TransactionSuccess");
+ _PREHASH_LureType = gMessageStringTable.getString("LureType");
+ _PREHASH_GroupMask = gMessageStringTable.getString("GroupMask");
+ _PREHASH_SitObject = gMessageStringTable.getString("SitObject");
+ _PREHASH_Override = gMessageStringTable.getString("Override");
+ _PREHASH_LocomotionState = gMessageStringTable.getString("LocomotionState");
+ _PREHASH_PriceUpload = gMessageStringTable.getString("PriceUpload");
+ _PREHASH_RemoveParcel = gMessageStringTable.getString("RemoveParcel");
+ _PREHASH_ConfirmAuctionStart = gMessageStringTable.getString("ConfirmAuctionStart");
+ _PREHASH_RpcScriptRequestInbound = gMessageStringTable.getString("RpcScriptRequestInbound");
+ _PREHASH_ActiveGroupID = gMessageStringTable.getString("ActiveGroupID");
+ _PREHASH_ParcelReturnObjects = gMessageStringTable.getString("ParcelReturnObjects");
+ _PREHASH_TotalObjects = gMessageStringTable.getString("TotalObjects");
+ _PREHASH_ObjectExtraParams = gMessageStringTable.getString("ObjectExtraParams");
+ _PREHASH_Questions = gMessageStringTable.getString("Questions");
+ _PREHASH_TransferAbort = gMessageStringTable.getString("TransferAbort");
+ _PREHASH_TransferInventory = gMessageStringTable.getString("TransferInventory");
+ _PREHASH_RayTargetID = gMessageStringTable.getString("RayTargetID");
+ _PREHASH_ClaimPrice = gMessageStringTable.getString("ClaimPrice");
+ _PREHASH_ObjectProperties = gMessageStringTable.getString("ObjectProperties");
+ _PREHASH_ParcelProperties = gMessageStringTable.getString("ParcelProperties");
+ _PREHASH_EstateOwnerID = gMessageStringTable.getString("EstateOwnerID");
+ _PREHASH_LogoutRequest = gMessageStringTable.getString("LogoutRequest");
+ _PREHASH_AssetUploadRequest = gMessageStringTable.getString("AssetUploadRequest");
+ _PREHASH_ReputationIndividualRequest = gMessageStringTable.getString("ReputationIndividualRequest");
+ _PREHASH_MajorVersion = gMessageStringTable.getString("MajorVersion");
+ _PREHASH_MinorVersion = gMessageStringTable.getString("MinorVersion");
+ _PREHASH_SimulatorAssign = gMessageStringTable.getString("SimulatorAssign");
+ _PREHASH_TransactionType = gMessageStringTable.getString("TransactionType");
+ _PREHASH_AvatarPropertiesUpdate = gMessageStringTable.getString("AvatarPropertiesUpdate");
+ _PREHASH_ParcelPropertiesUpdate = gMessageStringTable.getString("ParcelPropertiesUpdate");
+ _PREHASH_FetchItems = gMessageStringTable.getString("FetchItems");
+ _PREHASH_AbortXfer = gMessageStringTable.getString("AbortXfer");
+ _PREHASH_DeRezAck = gMessageStringTable.getString("DeRezAck");
+ _PREHASH_TakeControls = gMessageStringTable.getString("TakeControls");
+ _PREHASH_DirLandReply = gMessageStringTable.getString("DirLandReply");
+ _PREHASH_SpaceLocationTeleportReply = gMessageStringTable.getString("SpaceLocationTeleportReply");
+ _PREHASH_MuteType = gMessageStringTable.getString("MuteType");
+ _PREHASH_IMViaEMail = gMessageStringTable.getString("IMViaEMail");
+ _PREHASH_StartExpungeProcessAck = gMessageStringTable.getString("StartExpungeProcessAck");
+ _PREHASH_RentPrice = gMessageStringTable.getString("RentPrice");
+ _PREHASH_GenericMessage = gMessageStringTable.getString("GenericMessage");
+ _PREHASH_ChildAgentAlive = gMessageStringTable.getString("ChildAgentAlive");
+ _PREHASH_AssetType = gMessageStringTable.getString("AssetType");
+ _PREHASH_SpawnPointBlock = gMessageStringTable.getString("SpawnPointBlock");
+ _PREHASH_AttachmentBlock = gMessageStringTable.getString("AttachmentBlock");
+ _PREHASH_ObjectMaterial = gMessageStringTable.getString("ObjectMaterial");
+ _PREHASH_OwnerName = gMessageStringTable.getString("OwnerName");
+ _PREHASH_AvatarNotesReply = gMessageStringTable.getString("AvatarNotesReply");
+ _PREHASH_CacheID = gMessageStringTable.getString("CacheID");
+ _PREHASH_OwnerMask = gMessageStringTable.getString("OwnerMask");
+ _PREHASH_TransferInventoryAck = gMessageStringTable.getString("TransferInventoryAck");
+}
diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h
new file mode 100644
index 0000000000..0da2e02f83
--- /dev/null
+++ b/indra/llmessage/message_prehash.h
@@ -0,0 +1,1498 @@
+/**
+ * @file message_prehash.h
+ * @brief header file of externs of prehashed variables plus defines.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MESSAGE_PREHASH_H
+#define LL_MESSAGE_PREHASH_H
+
+/**
+ * Generated from message template version number 1.053
+ */
+
+
+extern F32 gPrehashVersionNumber;
+
+extern char * _PREHASH_X;
+extern char * _PREHASH_Y;
+extern char * _PREHASH_Z;
+extern char * _PREHASH_AddFlags;
+extern char * _PREHASH_ReservedNewbie;
+extern char * _PREHASH_FailureInfo;
+extern char * _PREHASH_MapData;
+extern char * _PREHASH_AddItem;
+extern char * _PREHASH_MeanCollision;
+extern char * _PREHASH_RezScript;
+extern char * _PREHASH_AvatarSitResponse;
+extern char * _PREHASH_InventoryAssetResponse;
+extern char * _PREHASH_KillObject;
+extern char * _PREHASH_ProposalID;
+extern char * _PREHASH_SerialNum;
+extern char * _PREHASH_Duration;
+extern char * _PREHASH_ScriptQuestion;
+extern char * _PREHASH_AddCircuitCode;
+extern char * _PREHASH_UseCircuitCode;
+extern char * _PREHASH_ViewerCircuitCode;
+extern char * _PREHASH_ScriptAnswerYes;
+extern char * _PREHASH_PartnerID;
+extern char * _PREHASH_DirLandQuery;
+extern char * _PREHASH_TeleportStart;
+extern char * _PREHASH_LogMessages;
+extern char * _PREHASH_AboutText;
+extern char * _PREHASH_VisualParam;
+extern char * _PREHASH_GroupPrims;
+extern char * _PREHASH_SelectedPrims;
+extern char * _PREHASH_ID;
+extern char * _PREHASH_UUIDNameRequest;
+extern char * _PREHASH_UUIDGroupNameRequest;
+extern char * _PREHASH_MoneyTransactionsRequest;
+extern char * _PREHASH_GroupAccountTransactionsRequest;
+extern char * _PREHASH_MapNameRequest;
+extern char * _PREHASH_MailTaskSimRequest;
+extern char * _PREHASH_UpdateSimulator;
+extern char * _PREHASH_BillableFactor;
+extern char * _PREHASH_ObjectBonusFactor;
+extern char * _PREHASH_EnableSimulator;
+extern char * _PREHASH_DisableSimulator;
+extern char * _PREHASH_ConfirmEnableSimulator;
+extern char * _PREHASH_LayerType;
+extern char * _PREHASH_OwnerRole;
+extern char * _PREHASH_ParcelOverlay;
+extern char * _PREHASH_AdjustBalance;
+extern char * _PREHASH_GroupOwned;
+extern char * _PREHASH_IP;
+extern char * _PREHASH_ChatFromViewer;
+extern char * _PREHASH_AvgAgentsInView;
+extern char * _PREHASH_AgentsInView;
+extern char * _PREHASH_GroupTitle;
+extern char * _PREHASH_MapLayerReply;
+extern char * _PREHASH_CompoundMsgID;
+extern char * _PREHASH_CameraConstraint;
+extern char * _PREHASH_DownloadTotals;
+extern char * _PREHASH_GenCounter;
+extern char * _PREHASH_FrozenData;
+extern char * _PREHASH_ChildAgentDying;
+extern char * _PREHASH_To;
+extern char * _PREHASH_CopyInventoryFromNotecard;
+extern char * _PREHASH_RezObjectFromNotecard;
+extern char * _PREHASH_ParcelDirFeeCurrent;
+extern char * _PREHASH_SeedCapability;
+extern char * _PREHASH_ObjectDuplicate;
+extern char * _PREHASH_InventoryData;
+extern char * _PREHASH_ReplyData;
+extern char * _PREHASH_ResetList;
+extern char * _PREHASH_MediaID;
+extern char * _PREHASH_RelatedRights;
+extern char * _PREHASH_RedirectGridX;
+extern char * _PREHASH_RedirectGridY;
+extern char * _PREHASH_TransferID;
+extern char * _PREHASH_Transacted;
+extern char * _PREHASH_TexturesChanged;
+extern char * _PREHASH_UserLookAt;
+extern char * _PREHASH_TestBlock1;
+extern char * _PREHASH_SensedData;
+extern char * _PREHASH_UpdateBlock;
+extern char * _PREHASH_ClassifiedGodDelete;
+extern char * _PREHASH_ObjectGrabUpdate;
+extern char * _PREHASH_TaxDate;
+extern char * _PREHASH_LocationPos;
+extern char * _PREHASH_StartDateTime;
+extern char * _PREHASH_ObjectUpdateCached;
+extern char * _PREHASH_Packets;
+extern char * _PREHASH_FailureType;
+extern char * _PREHASH_UpdateGroupInfo;
+extern char * _PREHASH_ObjectPermissions;
+extern char * _PREHASH_RevokePermissions;
+extern char * _PREHASH_UpdateFlags;
+extern char * _PREHASH_ObjectExportSelected;
+extern char * _PREHASH_RezSelected;
+extern char * _PREHASH_AutoPilot;
+extern char * _PREHASH_UpdateMuteListEntry;
+extern char * _PREHASH_RemoveMuteListEntry;
+extern char * _PREHASH_SetSimStatusInDatabase;
+extern char * _PREHASH_SetSimPresenceInDatabase;
+extern char * _PREHASH_CameraProperty;
+extern char * _PREHASH_BrushSize;
+extern char * _PREHASH_StartExpungeProcess;
+extern char * _PREHASH_SimulatorSetMap;
+extern char * _PREHASH_RegionPresenceRequestByRegionID;
+extern char * _PREHASH_ParcelObjectOwnersReply;
+extern char * _PREHASH_GroupMembersReply;
+extern char * _PREHASH_GroupRoleMembersReply;
+extern char * _PREHASH_RequestRegionInfo;
+extern char * _PREHASH_AABBMax;
+extern char * _PREHASH_RequestPayPrice;
+extern char * _PREHASH_SimulatorPresentAtLocation;
+extern char * _PREHASH_AgentRequestSit;
+extern char * _PREHASH_AABBMin;
+extern char * _PREHASH_ClassifiedFlags;
+extern char * _PREHASH_ControlFlags;
+extern char * _PREHASH_TeleportRequest;
+extern char * _PREHASH_SpaceLocationTeleportRequest;
+extern char * _PREHASH_LeaderBoardRequest;
+extern char * _PREHASH_ScriptTeleportRequest;
+extern char * _PREHASH_DateUTC;
+extern char * _PREHASH_TaskIDs;
+extern char * _PREHASH_EstateCovenantRequest;
+extern char * _PREHASH_RequestResult;
+extern char * _PREHASH_ReputationAgentAssign;
+extern char * _PREHASH_CanAcceptAgents;
+extern char * _PREHASH_ObjectSaleInfo;
+extern char * _PREHASH_KillChildAgents;
+extern char * _PREHASH_Balance;
+extern char * _PREHASH_DerezContainer;
+extern char * _PREHASH_ObjectData;
+extern char * _PREHASH_CameraAtAxis;
+extern char * _PREHASH_InfoBlock;
+extern char * _PREHASH_OwnershipCost;
+extern char * _PREHASH_AvatarNotesUpdate;
+extern char * _PREHASH_PID;
+extern char * _PREHASH_TimeString;
+extern char * _PREHASH_DirPopularReply;
+extern char * _PREHASH_TerrainHeightRange00;
+extern char * _PREHASH_SimData;
+extern char * _PREHASH_TerrainHeightRange01;
+extern char * _PREHASH_TerrainHeightRange10;
+extern char * _PREHASH_TerrainHeightRange11;
+extern char * _PREHASH_UpdateInventoryItem;
+extern char * _PREHASH_UpdateCreateInventoryItem;
+extern char * _PREHASH_MoveInventoryItem;
+extern char * _PREHASH_CopyInventoryItem;
+extern char * _PREHASH_RemoveInventoryItem;
+extern char * _PREHASH_CreateInventoryItem;
+extern char * _PREHASH_PathTwistBegin;
+extern char * _PREHASH_CRC;
+extern char * _PREHASH_AttachmentPoint;
+extern char * _PREHASH_TelehubBlock;
+extern char * _PREHASH_FOVBlock;
+extern char * _PREHASH_StartLocationData;
+extern char * _PREHASH_PositionData;
+extern char * _PREHASH_TimeSinceLast;
+extern char * _PREHASH_MapImage;
+extern char * _PREHASH_Objects;
+extern char * _PREHASH_URL;
+extern char * _PREHASH_CreationDate;
+extern char * _PREHASH_JointPivot;
+extern char * _PREHASH_RateeID;
+extern char * _PREHASH_FPS;
+extern char * _PREHASH_HasTelehub;
+extern char * _PREHASH_PathEnd;
+extern char * _PREHASH_ScriptDataReply;
+extern char * _PREHASH_MapBlockReply;
+extern char * _PREHASH_PropertiesData;
+extern char * _PREHASH_ViewerEffect;
+extern char * _PREHASH_FreezeUser;
+extern char * _PREHASH_OwnerPrims;
+extern char * _PREHASH_ObjectGrab;
+extern char * _PREHASH_ToAgentID;
+extern char * _PREHASH_SimulatorMapUpdate;
+extern char * _PREHASH_TransferPacket;
+extern char * _PREHASH_ObjectName;
+extern char * _PREHASH_GroupPowers;
+extern char * _PREHASH_OriginalName;
+extern char * _PREHASH_CompletePingCheck;
+extern char * _PREHASH_OnlineStatus;
+extern char * _PREHASH_ObjectDrop;
+extern char * _PREHASH_UseBigPackets;
+extern char * _PREHASH_GroupNoticesListReply;
+extern char * _PREHASH_ParcelAccessListReply;
+extern char * _PREHASH_RpcChannelReply;
+extern char * _PREHASH_RegionPresenceResponse;
+extern char * _PREHASH_AgentPresenceResponse;
+extern char * _PREHASH_CharterMember;
+extern char * _PREHASH_EdgeData;
+extern char * _PREHASH_NameData;
+extern char * _PREHASH_RegionPushOverride;
+extern char * _PREHASH_SimName;
+extern char * _PREHASH_UserReport;
+extern char * _PREHASH_DownloadPriority;
+extern char * _PREHASH_ToAgentId;
+extern char * _PREHASH_Mag;
+extern char * _PREHASH_DirPopularQuery;
+extern char * _PREHASH_ParcelPropertiesRequestByID;
+extern char * _PREHASH_ObjectLink;
+extern char * _PREHASH_RpcScriptReplyInbound;
+extern char * _PREHASH_BoardData;
+extern char * _PREHASH_RezData;
+extern char * _PREHASH_RemoveInventoryObjects;
+extern char * _PREHASH_GroupProposalBallot;
+extern char * _PREHASH_RPCServerIP;
+extern char * _PREHASH_Far;
+extern char * _PREHASH_GodSessionID;
+extern char * _PREHASH_ViewerDigest;
+extern char * _PREHASH_FLAboutText;
+extern char * _PREHASH_RegionHandshakeReply;
+extern char * _PREHASH_GroupActiveProposalItemReply;
+extern char * _PREHASH_MapItemReply;
+extern char * _PREHASH_Seconds;
+extern char * _PREHASH_UpdateUserInfo;
+extern char * _PREHASH_AggregatePermTexturesOwner;
+extern char * _PREHASH_Set;
+extern char * _PREHASH_NewName;
+extern char * _PREHASH_Key;
+extern char * _PREHASH_AgentID;
+extern char * _PREHASH_OnlineStatusRequest;
+extern char * _PREHASH_EventNotificationRemoveRequest;
+extern char * _PREHASH_NewFolderID;
+extern char * _PREHASH_Arc;
+extern char * _PREHASH_RegionX;
+extern char * _PREHASH_RegionY;
+extern char * _PREHASH_RequestData;
+extern char * _PREHASH_Msg;
+extern char * _PREHASH_Top;
+extern char * _PREHASH_MiscStats;
+extern char * _PREHASH_ImageID;
+extern char * _PREHASH_DataPacket;
+extern char * _PREHASH_ObjectDehinge;
+extern char * _PREHASH_You;
+extern char * _PREHASH_ScriptControlChange;
+extern char * _PREHASH_LoadURL;
+extern char * _PREHASH_SetCPURatio;
+extern char * _PREHASH_NameValueData;
+extern char * _PREHASH_AtomicPassObject;
+extern char * _PREHASH_ErrorMessage;
+extern char * _PREHASH_ViewerFrozenMessage;
+extern char * _PREHASH_HealthMessage;
+extern char * _PREHASH_LogTextMessage;
+extern char * _PREHASH_TimeDilation;
+extern char * _PREHASH_RemoveContribution;
+extern char * _PREHASH_Contribution;
+extern char * _PREHASH_SetGroupContribution;
+extern char * _PREHASH_Offline;
+extern char * _PREHASH_AgentIsNowWearing;
+extern char * _PREHASH_SecPerDay;
+extern char * _PREHASH_Members;
+extern char * _PREHASH_FailedResends;
+extern char * _PREHASH_CameraCenter;
+extern char * _PREHASH_CameraLeftAxis;
+extern char * _PREHASH_ExBlock;
+extern char * _PREHASH_Channel;
+extern char * _PREHASH_NetTest;
+extern char * _PREHASH_DiscardLevel;
+extern char * _PREHASH_LayerID;
+extern char * _PREHASH_RatorID;
+extern char * _PREHASH_GrabOffset;
+extern char * _PREHASH_SimPort;
+extern char * _PREHASH_PricePerMeter;
+extern char * _PREHASH_RegionFlags;
+extern char * _PREHASH_VoteResult;
+extern char * _PREHASH_ParcelDirFeeEstimate;
+extern char * _PREHASH_ModifyBlock;
+extern char * _PREHASH_InventoryBlock;
+extern char * _PREHASH_ReplyBlock;
+extern char * _PREHASH_ValidUntil;
+extern char * _PREHASH_VelocityInterpolateOn;
+extern char * _PREHASH_ClassifiedDelete;
+extern char * _PREHASH_RegionDenyAnonymous;
+extern char * _PREHASH_FLImageID;
+extern char * _PREHASH_AllowPublish;
+extern char * _PREHASH_SitName;
+extern char * _PREHASH_RegionsVisited;
+extern char * _PREHASH_DirClassifiedReply;
+extern char * _PREHASH_AvatarClassifiedReply;
+extern char * _PREHASH_ReputationIndividualReply;
+extern char * _PREHASH_MediaURL;
+extern char * _PREHASH_CompleteAgentMovement;
+extern char * _PREHASH_SpaceIP;
+extern char * _PREHASH_ClassifiedID;
+extern char * _PREHASH_LocalID;
+extern char * _PREHASH_RemoveItem;
+extern char * _PREHASH_LogFailedMoneyTransaction;
+extern char * _PREHASH_ViewerStartAuction;
+extern char * _PREHASH_StartAuction;
+extern char * _PREHASH_NameValueName;
+extern char * _PREHASH_AngVelX;
+extern char * _PREHASH_DuplicateFlags;
+extern char * _PREHASH_AngVelY;
+extern char * _PREHASH_AngVelZ;
+extern char * _PREHASH_TextColor;
+extern char * _PREHASH_SlaveID;
+extern char * _PREHASH_Charter;
+extern char * _PREHASH_AlertData;
+extern char * _PREHASH_TargetBlock;
+extern char * _PREHASH_CheckParcelAuctions;
+extern char * _PREHASH_ParcelAuctions;
+extern char * _PREHASH_OwnerIsGroup;
+extern char * _PREHASH_NameValuePair;
+extern char * _PREHASH_RemoveNameValuePair;
+extern char * _PREHASH_GetNameValuePair;
+extern char * _PREHASH_BulkUpdateInventory;
+extern char * _PREHASH_UpdateTaskInventory;
+extern char * _PREHASH_RemoveTaskInventory;
+extern char * _PREHASH_MoveTaskInventory;
+extern char * _PREHASH_RequestTaskInventory;
+extern char * _PREHASH_ReplyTaskInventory;
+extern char * _PREHASH_DeclineInventory;
+extern char * _PREHASH_AggregatePermInventory;
+extern char * _PREHASH_SimulatorInfo;
+extern char * _PREHASH_MoneyTransactionsReply;
+extern char * _PREHASH_GroupAccountTransactionsReply;
+extern char * _PREHASH_MailTaskSimReply;
+extern char * _PREHASH_WearableData;
+extern char * _PREHASH_StatisticsData;
+extern char * _PREHASH_Enabled;
+extern char * _PREHASH_Savings;
+extern char * _PREHASH_SimulatorLoad;
+extern char * _PREHASH_InternalRegionIP;
+extern char * _PREHASH_ExternalRegionIP;
+extern char * _PREHASH_TotalPairs;
+extern char * _PREHASH_CreateGroupRequest;
+extern char * _PREHASH_JoinGroupRequest;
+extern char * _PREHASH_LeaveGroupRequest;
+extern char * _PREHASH_InviteGroupRequest;
+extern char * _PREHASH_LiveHelpGroupRequest;
+extern char * _PREHASH_ServerVersion;
+extern char * _PREHASH_PriceParcelClaimFactor;
+extern char * _PREHASH_BillableArea;
+extern char * _PREHASH_ObjectID;
+extern char * _PREHASH_ObjectFlagUpdate;
+extern char * _PREHASH_GroupRoleUpdate;
+extern char * _PREHASH_RequestInventoryAsset;
+extern char * _PREHASH_RedoLand;
+extern char * _PREHASH_TravelAccess;
+extern char * _PREHASH_ChangedGrid;
+extern char * _PREHASH_AgentDropGroup;
+extern char * _PREHASH_Details;
+extern char * _PREHASH_LocationX;
+extern char * _PREHASH_SaleType;
+extern char * _PREHASH_LocationY;
+extern char * _PREHASH_LocationZ;
+extern char * _PREHASH_EconomyData;
+extern char * _PREHASH_HeadRotation;
+extern char * _PREHASH_DeleteOnCompletion;
+extern char * _PREHASH_PublicPort;
+extern char * _PREHASH_DirClassifiedQuery;
+extern char * _PREHASH_CallbackID;
+extern char * _PREHASH_RequestParcelTransfer;
+extern char * _PREHASH_RoleCount;
+extern char * _PREHASH_ObjectCapacity;
+extern char * _PREHASH_RequestID;
+extern char * _PREHASH_RequestXfer;
+extern char * _PREHASH_ObjectTaxCurrent;
+extern char * _PREHASH_LightTaxCurrent;
+extern char * _PREHASH_LandTaxCurrent;
+extern char * _PREHASH_GroupTaxCurrent;
+extern char * _PREHASH_FetchInventoryDescendents;
+extern char * _PREHASH_InventoryDescendents;
+extern char * _PREHASH_Descendents;
+extern char * _PREHASH_PurgeInventoryDescendents;
+extern char * _PREHASH_ShowDir;
+extern char * _PREHASH_IsOwner;
+extern char * _PREHASH_Timestamp;
+extern char * _PREHASH_GlobalPos;
+extern char * _PREHASH_GrabOffsetInitial;
+extern char * _PREHASH_IsTrial;
+extern char * _PREHASH_FinalizeLogout;
+extern char * _PREHASH_ObjectDuplicateOnRay;
+extern char * _PREHASH_GroupMembershipCount;
+extern char * _PREHASH_MethodData;
+extern char * _PREHASH_ActivateGestures;
+extern char * _PREHASH_DeactivateGestures;
+extern char * _PREHASH_ProposalData;
+extern char * _PREHASH_PosGlobal;
+extern char * _PREHASH_SearchID;
+extern char * _PREHASH_RezMultipleAttachmentsFromInv;
+extern char * _PREHASH_SearchName;
+extern char * _PREHASH_VersionString;
+extern char * _PREHASH_CreateGroupReply;
+extern char * _PREHASH_LeaveGroupReply;
+extern char * _PREHASH_ActualArea;
+extern char * _PREHASH_Message;
+extern char * _PREHASH_ClickAction;
+extern char * _PREHASH_AssetUploadComplete;
+extern char * _PREHASH_RequestType;
+extern char * _PREHASH_UUID;
+extern char * _PREHASH_BaseMask;
+extern char * _PREHASH_NetBlock;
+extern char * _PREHASH_GlobalX;
+extern char * _PREHASH_GlobalY;
+extern char * _PREHASH_CopyRotates;
+extern char * _PREHASH_KickUserAck;
+extern char * _PREHASH_TopPick;
+extern char * _PREHASH_SessionID;
+extern char * _PREHASH_GlobalZ;
+extern char * _PREHASH_DeclineFriendship;
+extern char * _PREHASH_FormFriendship;
+extern char * _PREHASH_TerminateFriendship;
+extern char * _PREHASH_TaskData;
+extern char * _PREHASH_SimWideMaxPrims;
+extern char * _PREHASH_TotalPrims;
+extern char * _PREHASH_SourceFilename;
+extern char * _PREHASH_ProfileBegin;
+extern char * _PREHASH_MoneyDetailsRequest;
+extern char * _PREHASH_Request;
+extern char * _PREHASH_GroupAccountDetailsRequest;
+extern char * _PREHASH_GroupActiveProposalsRequest;
+extern char * _PREHASH_StringValue;
+extern char * _PREHASH_ClosestSimulator;
+extern char * _PREHASH_Version;
+extern char * _PREHASH_OtherCount;
+extern char * _PREHASH_MemberCount;
+extern char * _PREHASH_ChatData;
+extern char * _PREHASH_IsGroupOwned;
+extern char * _PREHASH_EnergyEfficiency;
+extern char * _PREHASH_MaxPlace;
+extern char * _PREHASH_PickInfoUpdate;
+extern char * _PREHASH_PickDelete;
+extern char * _PREHASH_ScriptReset;
+extern char * _PREHASH_Requester;
+extern char * _PREHASH_ForSale;
+extern char * _PREHASH_NearestLandingRegionReply;
+extern char * _PREHASH_RecordAgentPresence;
+extern char * _PREHASH_EraseAgentPresence;
+extern char * _PREHASH_ParcelID;
+extern char * _PREHASH_Godlike;
+extern char * _PREHASH_TotalDebits;
+extern char * _PREHASH_Direction;
+extern char * _PREHASH_Appearance;
+extern char * _PREHASH_HealthData;
+extern char * _PREHASH_LeftAxis;
+extern char * _PREHASH_LocationBlock;
+extern char * _PREHASH_ObjectImage;
+extern char * _PREHASH_TerrainStartHeight00;
+extern char * _PREHASH_TerrainStartHeight01;
+extern char * _PREHASH_TerrainStartHeight10;
+extern char * _PREHASH_ObjectHinge;
+extern char * _PREHASH_TerrainStartHeight11;
+extern char * _PREHASH_MetersPerGrid;
+extern char * _PREHASH_WaterHeight;
+extern char * _PREHASH_FetchInventoryReply;
+extern char * _PREHASH_MoneySummaryReply;
+extern char * _PREHASH_GroupAccountSummaryReply;
+extern char * _PREHASH_AttachedSound;
+extern char * _PREHASH_ParamInUse;
+extern char * _PREHASH_GodKickUser;
+extern char * _PREHASH_PickName;
+extern char * _PREHASH_TaskName;
+extern char * _PREHASH_ParcelGodReserveForNewbie;
+extern char * _PREHASH_SubType;
+extern char * _PREHASH_ObjectCount;
+extern char * _PREHASH_RegionPresenceRequestByHandle;
+extern char * _PREHASH_RezSingleAttachmentFromInv;
+extern char * _PREHASH_ChildAgentUpdate;
+extern char * _PREHASH_ToID;
+extern char * _PREHASH_ViewerPort;
+extern char * _PREHASH_IsOwnerGroup;
+extern char * _PREHASH_AgentHeightWidth;
+extern char * _PREHASH_VerticalAngle;
+extern char * _PREHASH_WearableType;
+extern char * _PREHASH_AggregatePermNextOwner;
+extern char * _PREHASH_ShowInList;
+extern char * _PREHASH_PositionSuggestion;
+extern char * _PREHASH_UpdateParcel;
+extern char * _PREHASH_ClearAgentSessions;
+extern char * _PREHASH_SetAlwaysRun;
+extern char * _PREHASH_NVPair;
+extern char * _PREHASH_ObjectSpinStart;
+extern char * _PREHASH_UseEstateSun;
+extern char * _PREHASH_LogoutBlock;
+extern char * _PREHASH_RelayLogControl;
+extern char * _PREHASH_RegionID;
+extern char * _PREHASH_Creator;
+extern char * _PREHASH_ProposalText;
+extern char * _PREHASH_DirEventsReply;
+extern char * _PREHASH_EventInfoReply;
+extern char * _PREHASH_UserInfoReply;
+extern char * _PREHASH_PathRadiusOffset;
+extern char * _PREHASH_SessionInfo;
+extern char * _PREHASH_TextureData;
+extern char * _PREHASH_ChatPass;
+extern char * _PREHASH_TargetID;
+extern char * _PREHASH_DefaultPayPrice;
+extern char * _PREHASH_UserLocation;
+extern char * _PREHASH_MaxPrims;
+extern char * _PREHASH_RegionIP;
+extern char * _PREHASH_LandmarkID;
+extern char * _PREHASH_InitiateDownload;
+extern char * _PREHASH_Name;
+extern char * _PREHASH_OtherCleanTime;
+extern char * _PREHASH_ParcelSetOtherCleanTime;
+extern char * _PREHASH_TeleportPriceExponent;
+extern char * _PREHASH_Gain;
+extern char * _PREHASH_VelX;
+extern char * _PREHASH_PacketAck;
+extern char * _PREHASH_PathSkew;
+extern char * _PREHASH_Negative;
+extern char * _PREHASH_VelY;
+extern char * _PREHASH_SimulatorShutdownRequest;
+extern char * _PREHASH_NearestLandingRegionRequest;
+extern char * _PREHASH_VelZ;
+extern char * _PREHASH_OtherID;
+extern char * _PREHASH_MemberID;
+extern char * _PREHASH_MapLayerRequest;
+extern char * _PREHASH_PatchVersion;
+extern char * _PREHASH_ObjectScale;
+extern char * _PREHASH_TargetIP;
+extern char * _PREHASH_Redo;
+extern char * _PREHASH_MoneyBalance;
+extern char * _PREHASH_TrackAgent;
+extern char * _PREHASH_MaxX;
+extern char * _PREHASH_Data;
+extern char * _PREHASH_MaxY;
+extern char * _PREHASH_TextureAnim;
+extern char * _PREHASH_ReturnIDs;
+extern char * _PREHASH_Date;
+extern char * _PREHASH_GestureUpdate;
+extern char * _PREHASH_AgentWearablesUpdate;
+extern char * _PREHASH_AgentDataUpdate;
+extern char * _PREHASH_Hash;
+extern char * _PREHASH_GroupDataUpdate;
+extern char * _PREHASH_AgentGroupDataUpdate;
+extern char * _PREHASH_Left;
+extern char * _PREHASH_Mask;
+extern char * _PREHASH_ForceMouselook;
+extern char * _PREHASH_Success;
+extern char * _PREHASH_ObjectGroup;
+extern char * _PREHASH_SunHour;
+extern char * _PREHASH_MinX;
+extern char * _PREHASH_ScriptSensorReply;
+extern char * _PREHASH_MinY;
+extern char * _PREHASH_Command;
+extern char * _PREHASH_Desc;
+extern char * _PREHASH_AttachmentNeedsSave;
+extern char * _PREHASH_HistoryItemData;
+extern char * _PREHASH_AgentCachedTexture;
+extern char * _PREHASH_Subject;
+extern char * _PREHASH_East;
+extern char * _PREHASH_GodExpungeUser;
+extern char * _PREHASH_QueryReplies;
+extern char * _PREHASH_ObjectCategory;
+extern char * _PREHASH_Time;
+extern char * _PREHASH_CreateLandmarkForEvent;
+extern char * _PREHASH_ParentID;
+extern char * _PREHASH_Ping;
+extern char * _PREHASH_Perp;
+extern char * _PREHASH_Code;
+extern char * _PREHASH_InvType;
+extern char * _PREHASH_AgentFOV;
+extern char * _PREHASH_BulkMoneyTransfer;
+extern char * _PREHASH_Audible;
+extern char * _PREHASH_AuctionData;
+extern char * _PREHASH_IDBlock;
+extern char * _PREHASH_ReputationData;
+extern char * _PREHASH_West;
+extern char * _PREHASH_Undo;
+extern char * _PREHASH_TotalNumItems;
+extern char * _PREHASH_Info;
+extern char * _PREHASH_Area;
+extern char * _PREHASH_Behavior;
+extern char * _PREHASH_SimCrashed;
+extern char * _PREHASH_Text;
+extern char * _PREHASH_AgentToNewRegion;
+extern char * _PREHASH_PriceGroupCreate;
+extern char * _PREHASH_ObjectShape;
+extern char * _PREHASH_GroupRoleDataReply;
+extern char * _PREHASH_PosX;
+extern char * _PREHASH_PosY;
+extern char * _PREHASH_MuteCRC;
+extern char * _PREHASH_PosZ;
+extern char * _PREHASH_Size;
+extern char * _PREHASH_FromAddress;
+extern char * _PREHASH_Body;
+extern char * _PREHASH_FileData;
+extern char * _PREHASH_List;
+extern char * _PREHASH_KickUser;
+extern char * _PREHASH_OtherPrims;
+extern char * _PREHASH_RunTime;
+extern char * _PREHASH_GrantUserRights;
+extern char * _PREHASH_RpcScriptRequestInboundForward;
+extern char * _PREHASH_More;
+extern char * _PREHASH_Majority;
+extern char * _PREHASH_MetersTraveled;
+extern char * _PREHASH_Stat;
+extern char * _PREHASH_SoundID;
+extern char * _PREHASH_Item;
+extern char * _PREHASH_User;
+extern char * _PREHASH_RemoteInfos;
+extern char * _PREHASH_Prey;
+extern char * _PREHASH_UsecSinceStart;
+extern char * _PREHASH_RayStart;
+extern char * _PREHASH_ParcelData;
+extern char * _PREHASH_CameraUpAxis;
+extern char * _PREHASH_ScriptDialog;
+extern char * _PREHASH_MasterParcelData;
+extern char * _PREHASH_Invalid;
+extern char * _PREHASH_MinPlace;
+extern char * _PREHASH_ProfileCurve;
+extern char * _PREHASH_ParcelAccessListUpdate;
+extern char * _PREHASH_MuteListUpdate;
+extern char * _PREHASH_SendPacket;
+extern char * _PREHASH_SendXferPacket;
+extern char * _PREHASH_RegionDenyIdentified;
+extern char * _PREHASH_NotecardItemID;
+extern char * _PREHASH_LastName;
+extern char * _PREHASH_From;
+extern char * _PREHASH_RoleChange;
+extern char * _PREHASH_Port;
+extern char * _PREHASH_MemberTitle;
+extern char * _PREHASH_LogParcelChanges;
+extern char * _PREHASH_AgentCachedTextureResponse;
+extern char * _PREHASH_DeRezObject;
+extern char * _PREHASH_IsTemporary;
+extern char * _PREHASH_InsigniaID;
+extern char * _PREHASH_CheckFlags;
+extern char * _PREHASH_TransferPriority;
+extern char * _PREHASH_EventID;
+extern char * _PREHASH_Selected;
+extern char * _PREHASH_FromAgentId;
+extern char * _PREHASH_Type;
+extern char * _PREHASH_ChatType;
+extern char * _PREHASH_ReportData;
+extern char * _PREHASH_LeaderBoardData;
+extern char * _PREHASH_RequestBlock;
+extern char * _PREHASH_GrantData;
+extern char * _PREHASH_DetachAttachmentIntoInv;
+extern char * _PREHASH_ParcelDisableObjects;
+extern char * _PREHASH_Sections;
+extern char * _PREHASH_GodLevel;
+extern char * _PREHASH_PayPriceReply;
+extern char * _PREHASH_QueryID;
+extern char * _PREHASH_CameraEyeOffset;
+extern char * _PREHASH_AgentPosition;
+extern char * _PREHASH_GrabPosition;
+extern char * _PREHASH_OnlineNotification;
+extern char * _PREHASH_OfflineNotification;
+extern char * _PREHASH_SendPostcard;
+extern char * _PREHASH_RequestFlags;
+extern char * _PREHASH_MoneyHistoryRequest;
+extern char * _PREHASH_MoneySummaryRequest;
+extern char * _PREHASH_GroupAccountSummaryRequest;
+extern char * _PREHASH_GroupVoteHistoryRequest;
+extern char * _PREHASH_ParamValue;
+extern char * _PREHASH_Checksum;
+extern char * _PREHASH_MaxAgents;
+extern char * _PREHASH_CreateNewOutfitAttachments;
+extern char * _PREHASH_RegionHandle;
+extern char * _PREHASH_TeleportProgress;
+extern char * _PREHASH_AgentQuitCopy;
+extern char * _PREHASH_ToViewer;
+extern char * _PREHASH_AvatarInterestsUpdate;
+extern char * _PREHASH_GroupNoticeID;
+extern char * _PREHASH_ParcelName;
+extern char * _PREHASH_PriceObjectRent;
+extern char * _PREHASH_ConnectAgentToUserserver;
+extern char * _PREHASH_ConnectToUserserver;
+extern char * _PREHASH_OfferCallingCard;
+extern char * _PREHASH_AgentAccess;
+extern char * _PREHASH_AcceptCallingCard;
+extern char * _PREHASH_DeclineCallingCard;
+extern char * _PREHASH_DataHomeLocationReply;
+extern char * _PREHASH_EventLocationReply;
+extern char * _PREHASH_TerseDateID;
+extern char * _PREHASH_ObjectOwner;
+extern char * _PREHASH_AssetID;
+extern char * _PREHASH_AlertMessage;
+extern char * _PREHASH_AgentAlertMessage;
+extern char * _PREHASH_EstateOwnerMessage;
+extern char * _PREHASH_ParcelMediaCommandMessage;
+extern char * _PREHASH_Auction;
+extern char * _PREHASH_Category;
+extern char * _PREHASH_FilePath;
+extern char * _PREHASH_ItemFlags;
+extern char * _PREHASH_Invoice;
+extern char * _PREHASH_IntervalDays;
+extern char * _PREHASH_PathScaleX;
+extern char * _PREHASH_FromTaskID;
+extern char * _PREHASH_TimeInfo;
+extern char * _PREHASH_PathScaleY;
+extern char * _PREHASH_PublicCount;
+extern char * _PREHASH_ParcelJoin;
+extern char * _PREHASH_GroupRolesCount;
+extern char * _PREHASH_SimulatorBlock;
+extern char * _PREHASH_GroupID;
+extern char * _PREHASH_AgentVel;
+extern char * _PREHASH_RequestImage;
+extern char * _PREHASH_NetStats;
+extern char * _PREHASH_AgentPos;
+extern char * _PREHASH_AgentSit;
+extern char * _PREHASH_Material;
+extern char * _PREHASH_ObjectDeGrab;
+extern char * _PREHASH_VelocityInterpolateOff;
+extern char * _PREHASH_AuthorizedBuyerID;
+extern char * _PREHASH_AvatarPropertiesReply;
+extern char * _PREHASH_GroupProfileReply;
+extern char * _PREHASH_SimOwner;
+extern char * _PREHASH_SalePrice;
+extern char * _PREHASH_Animation;
+extern char * _PREHASH_OwnerID;
+extern char * _PREHASH_NearestLandingRegionUpdated;
+extern char * _PREHASH_PassToAgent;
+extern char * _PREHASH_PreyAgent;
+extern char * _PREHASH_SimStats;
+extern char * _PREHASH_Options;
+extern char * _PREHASH_LogoutReply;
+extern char * _PREHASH_FeatureDisabled;
+extern char * _PREHASH_ObjectLocalID;
+extern char * _PREHASH_Dropped;
+extern char * _PREHASH_WebProfilesDisabled;
+extern char * _PREHASH_Destination;
+extern char * _PREHASH_MasterID;
+extern char * _PREHASH_TransferData;
+extern char * _PREHASH_WantToMask;
+extern char * _PREHASH_AvatarData;
+extern char * _PREHASH_ParcelSelectObjects;
+extern char * _PREHASH_ExtraParams;
+extern char * _PREHASH_LogLogin;
+extern char * _PREHASH_CreatorID;
+extern char * _PREHASH_Summary;
+extern char * _PREHASH_BuyObjectInventory;
+extern char * _PREHASH_FetchInventory;
+extern char * _PREHASH_InventoryID;
+extern char * _PREHASH_PacketNumber;
+extern char * _PREHASH_SetFollowCamProperties;
+extern char * _PREHASH_ClearFollowCamProperties;
+extern char * _PREHASH_SequenceID;
+extern char * _PREHASH_DataServerLogout;
+extern char * _PREHASH_NameValue;
+extern char * _PREHASH_PathShearX;
+extern char * _PREHASH_PathShearY;
+extern char * _PREHASH_Velocity;
+extern char * _PREHASH_SecPerYear;
+extern char * _PREHASH_FirstName;
+extern char * _PREHASH_AttachedSoundGainChange;
+extern char * _PREHASH_LocationID;
+extern char * _PREHASH_Running;
+extern char * _PREHASH_AgentThrottle;
+extern char * _PREHASH_NeighborList;
+extern char * _PREHASH_PathTaperX;
+extern char * _PREHASH_PathTaperY;
+extern char * _PREHASH_AgentRelated;
+extern char * _PREHASH_GranterBlock;
+extern char * _PREHASH_UseCachedMuteList;
+extern char * _PREHASH_FailStats;
+extern char * _PREHASH_Tempfile;
+extern char * _PREHASH_BuyerID;
+extern char * _PREHASH_DirPeopleReply;
+extern char * _PREHASH_TransferInfo;
+extern char * _PREHASH_AvatarPickerRequestBackend;
+extern char * _PREHASH_AvatarPropertiesRequestBackend;
+extern char * _PREHASH_UpdateData;
+extern char * _PREHASH_SimFPS;
+extern char * _PREHASH_ReporterID;
+extern char * _PREHASH_ButtonLabel;
+extern char * _PREHASH_GranterID;
+extern char * _PREHASH_WantToText;
+extern char * _PREHASH_ReportType;
+extern char * _PREHASH_DataBlock;
+extern char * _PREHASH_SimulatorReady;
+extern char * _PREHASH_AnimationSourceList;
+extern char * _PREHASH_SubscribeLoad;
+extern char * _PREHASH_UnsubscribeLoad;
+extern char * _PREHASH_Packet;
+extern char * _PREHASH_UndoLand;
+extern char * _PREHASH_SimAccess;
+extern char * _PREHASH_MembershipFee;
+extern char * _PREHASH_InviteGroupResponse;
+extern char * _PREHASH_CreateInventoryFolder;
+extern char * _PREHASH_UpdateInventoryFolder;
+extern char * _PREHASH_MoveInventoryFolder;
+extern char * _PREHASH_RemoveInventoryFolder;
+extern char * _PREHASH_MoneyData;
+extern char * _PREHASH_ObjectDeselect;
+extern char * _PREHASH_NewAssetID;
+extern char * _PREHASH_ObjectAdd;
+extern char * _PREHASH_RayEndIsIntersection;
+extern char * _PREHASH_CompleteAuction;
+extern char * _PREHASH_CircuitCode;
+extern char * _PREHASH_AgentMovementComplete;
+extern char * _PREHASH_ViewerIP;
+extern char * _PREHASH_Header;
+extern char * _PREHASH_GestureFlags;
+extern char * _PREHASH_XferID;
+extern char * _PREHASH_StatValue;
+extern char * _PREHASH_PickID;
+extern char * _PREHASH_TaskID;
+extern char * _PREHASH_GridsPerEdge;
+extern char * _PREHASH_RayEnd;
+extern char * _PREHASH_Throttles;
+extern char * _PREHASH_RebakeAvatarTextures;
+extern char * _PREHASH_UpAxis;
+extern char * _PREHASH_AgentTextures;
+extern char * _PREHASH_NotecardData;
+extern char * _PREHASH_Radius;
+extern char * _PREHASH_OffCircuit;
+extern char * _PREHASH_Access;
+extern char * _PREHASH_TitleRoleID;
+extern char * _PREHASH_SquareMetersCredit;
+extern char * _PREHASH_Filename;
+extern char * _PREHASH_SecuredTemplateChecksumRequest;
+extern char * _PREHASH_TemplateChecksumRequest;
+extern char * _PREHASH_AgentPresenceRequest;
+extern char * _PREHASH_ClassifiedInfoRequest;
+extern char * _PREHASH_ParcelInfoRequest;
+extern char * _PREHASH_ParcelObjectOwnersRequest;
+extern char * _PREHASH_TeleportLandmarkRequest;
+extern char * _PREHASH_EventInfoRequest;
+extern char * _PREHASH_ChatFromSimulator;
+extern char * _PREHASH_PickInfoRequest;
+extern char * _PREHASH_MoneyBalanceRequest;
+extern char * _PREHASH_GroupMembersRequest;
+extern char * _PREHASH_GroupRoleMembersRequest;
+extern char * _PREHASH_OldFolderID;
+extern char * _PREHASH_UserInfoRequest;
+extern char * _PREHASH_TextureID;
+extern char * _PREHASH_ProfileURL;
+extern char * _PREHASH_Handle;
+extern char * _PREHASH_StartParcelRenameAck;
+extern char * _PREHASH_ButtonIndex;
+extern char * _PREHASH_GetScriptRunning;
+extern char * _PREHASH_SetScriptRunning;
+extern char * _PREHASH_Health;
+extern char * _PREHASH_FileID;
+extern char * _PREHASH_CircuitInfo;
+extern char * _PREHASH_ObjectBuy;
+extern char * _PREHASH_ProfileEnd;
+extern char * _PREHASH_Effect;
+extern char * _PREHASH_TestMessage;
+extern char * _PREHASH_ScriptMailRegistration;
+extern char * _PREHASH_AgentSetAppearance;
+extern char * _PREHASH_AvatarAppearance;
+extern char * _PREHASH_RegionData;
+extern char * _PREHASH_RequestingRegionData;
+extern char * _PREHASH_LandingRegionData;
+extern char * _PREHASH_SitTransform;
+extern char * _PREHASH_TerrainBase0;
+extern char * _PREHASH_SkillsMask;
+extern char * _PREHASH_AtAxis;
+extern char * _PREHASH_TerrainBase1;
+extern char * _PREHASH_Reason;
+extern char * _PREHASH_TerrainBase2;
+extern char * _PREHASH_TerrainBase3;
+extern char * _PREHASH_Params;
+extern char * _PREHASH_PingID;
+extern char * _PREHASH_Change;
+extern char * _PREHASH_Height;
+extern char * _PREHASH_Region;
+extern char * _PREHASH_MoneyHistoryReply;
+extern char * _PREHASH_TelehubInfo;
+extern char * _PREHASH_StateSave;
+extern char * _PREHASH_RoleData;
+extern char * _PREHASH_AgentAnimation;
+extern char * _PREHASH_AvatarAnimation;
+extern char * _PREHASH_LogDwellTime;
+extern char * _PREHASH_ParcelGodMarkAsContent;
+extern char * _PREHASH_UsePhysics;
+extern char * _PREHASH_RegionDenyTransacted;
+extern char * _PREHASH_JointType;
+extern char * _PREHASH_TaxEstimate;
+extern char * _PREHASH_ObjectTaxEstimate;
+extern char * _PREHASH_LightTaxEstimate;
+extern char * _PREHASH_TeleportLandingStatusChanged;
+extern char * _PREHASH_LandTaxEstimate;
+extern char * _PREHASH_GroupTaxEstimate;
+extern char * _PREHASH_AvgViewerFPS;
+extern char * _PREHASH_Buttons;
+extern char * _PREHASH_Sender;
+extern char * _PREHASH_Dialog;
+extern char * _PREHASH_TargetData;
+extern char * _PREHASH_DestID;
+extern char * _PREHASH_PricePublicObjectDelete;
+extern char * _PREHASH_ObjectDelete;
+extern char * _PREHASH_Delete;
+extern char * _PREHASH_EventGodDelete;
+extern char * _PREHASH_LastTaxDate;
+extern char * _PREHASH_MapImageID;
+extern char * _PREHASH_EndDateTime;
+extern char * _PREHASH_TerrainDetail0;
+extern char * _PREHASH_TerrainDetail1;
+extern char * _PREHASH_TerrainDetail2;
+extern char * _PREHASH_TerrainDetail3;
+extern char * _PREHASH_Offset;
+extern char * _PREHASH_ObjectDelink;
+extern char * _PREHASH_TargetObject;
+extern char * _PREHASH_IsEstateManager;
+extern char * _PREHASH_CancelAuction;
+extern char * _PREHASH_ObjectDetach;
+extern char * _PREHASH_Compressed;
+extern char * _PREHASH_PathBegin;
+extern char * _PREHASH_BypassRaycast;
+extern char * _PREHASH_WinnerID;
+extern char * _PREHASH_ChannelType;
+extern char * _PREHASH_NonExemptMembers;
+extern char * _PREHASH_Agents;
+extern char * _PREHASH_SimulatorStart;
+extern char * _PREHASH_Enable;
+extern char * _PREHASH_MemberData;
+extern char * _PREHASH_ToGroupID;
+extern char * _PREHASH_ImageNotInDatabase;
+extern char * _PREHASH_StartDate;
+extern char * _PREHASH_AnimID;
+extern char * _PREHASH_Serial;
+extern char * _PREHASH_ControlPort;
+extern char * _PREHASH_ModifyLand;
+extern char * _PREHASH_Digest;
+extern char * _PREHASH_Victim;
+extern char * _PREHASH_Script;
+extern char * _PREHASH_TemplateChecksumReply;
+extern char * _PREHASH_PickInfoReply;
+extern char * _PREHASH_MoneyBalanceReply;
+extern char * _PREHASH_RoutedMoneyBalanceReply;
+extern char * _PREHASH_RoleID;
+extern char * _PREHASH_RegionInfo;
+extern char * _PREHASH_Sequence;
+extern char * _PREHASH_GodUpdateRegionInfo;
+extern char * _PREHASH_LocalX;
+extern char * _PREHASH_LocalY;
+extern char * _PREHASH_StartAnim;
+extern char * _PREHASH_Location;
+extern char * _PREHASH_Action;
+extern char * _PREHASH_Rights;
+extern char * _PREHASH_SearchDir;
+extern char * _PREHASH_Active;
+extern char * _PREHASH_TransferRequest;
+extern char * _PREHASH_ScriptSensorRequest;
+extern char * _PREHASH_MoneyTransferRequest;
+extern char * _PREHASH_EjectGroupMemberRequest;
+extern char * _PREHASH_SkillsText;
+extern char * _PREHASH_Resent;
+extern char * _PREHASH_Center;
+extern char * _PREHASH_SharedData;
+extern char * _PREHASH_PSBlock;
+extern char * _PREHASH_UUIDNameBlock;
+extern char * _PREHASH_Viewer;
+extern char * _PREHASH_GroupNoticeDelete;
+extern char * _PREHASH_GroupTitleUpdate;
+extern char * _PREHASH_Method;
+extern char * _PREHASH_TouchName;
+extern char * _PREHASH_UpdateType;
+extern char * _PREHASH_KickedFromEstateID;
+extern char * _PREHASH_CandidateID;
+extern char * _PREHASH_ParamData;
+extern char * _PREHASH_GodlikeMessage;
+extern char * _PREHASH_SystemMessage;
+extern char * _PREHASH_BodyRotation;
+extern char * _PREHASH_SearchRegions;
+extern char * _PREHASH_Ignore;
+extern char * _PREHASH_AnimationData;
+extern char * _PREHASH_StatID;
+extern char * _PREHASH_ItemID;
+extern char * _PREHASH_AvatarStatisticsReply;
+extern char * _PREHASH_ScriptDialogReply;
+extern char * _PREHASH_RegionIDAndHandleReply;
+extern char * _PREHASH_CameraAtOffset;
+extern char * _PREHASH_VoteID;
+extern char * _PREHASH_ParcelGodForceOwner;
+extern char * _PREHASH_Filter;
+extern char * _PREHASH_InviteData;
+extern char * _PREHASH_PCode;
+extern char * _PREHASH_SearchPos;
+extern char * _PREHASH_PreyID;
+extern char * _PREHASH_TerrainLowerLimit;
+extern char * _PREHASH_EventFlags;
+extern char * _PREHASH_TallyVotes;
+extern char * _PREHASH_Result;
+extern char * _PREHASH_LookAt;
+extern char * _PREHASH_PayButton;
+extern char * _PREHASH_SelfCount;
+extern char * _PREHASH_PacketCount;
+extern char * _PREHASH_ParcelBuyPass;
+extern char * _PREHASH_Identified;
+extern char * _PREHASH_OldItemID;
+extern char * _PREHASH_RegionPort;
+extern char * _PREHASH_PriceEnergyUnit;
+extern char * _PREHASH_Bitmap;
+extern char * _PREHASH_TrackAgentSession;
+extern char * _PREHASH_CacheMissType;
+extern char * _PREHASH_VFileID;
+extern char * _PREHASH_GroupInsigniaID;
+extern char * _PREHASH_FromID;
+extern char * _PREHASH_Online;
+extern char * _PREHASH_KickFlags;
+extern char * _PREHASH_CovenantID;
+extern char * _PREHASH_SysCPU;
+extern char * _PREHASH_EMail;
+extern char * _PREHASH_AggregatePermTextures;
+extern char * _PREHASH_ChatChannel;
+extern char * _PREHASH_ReturnID;
+extern char * _PREHASH_ObjectAttach;
+extern char * _PREHASH_TargetPort;
+extern char * _PREHASH_ObjectSpinStop;
+extern char * _PREHASH_FullID;
+extern char * _PREHASH_ActivateGroup;
+extern char * _PREHASH_SysGPU;
+extern char * _PREHASH_AvatarInterestsReply;
+extern char * _PREHASH_StartLure;
+extern char * _PREHASH_SysRAM;
+extern char * _PREHASH_ObjectPosition;
+extern char * _PREHASH_SitPosition;
+extern char * _PREHASH_StartTime;
+extern char * _PREHASH_BornOn;
+extern char * _PREHASH_CameraCollidePlane;
+extern char * _PREHASH_EconomyDataRequest;
+extern char * _PREHASH_TeleportLureRequest;
+extern char * _PREHASH_FolderID;
+extern char * _PREHASH_RegionHandleRequest;
+extern char * _PREHASH_GestureRequest;
+extern char * _PREHASH_ScriptDataRequest;
+extern char * _PREHASH_GroupRoleDataRequest;
+extern char * _PREHASH_GroupTitlesRequest;
+extern char * _PREHASH_AgentWearablesRequest;
+extern char * _PREHASH_MapBlockRequest;
+extern char * _PREHASH_LureID;
+extern char * _PREHASH_CopyCenters;
+extern char * _PREHASH_ParamList;
+extern char * _PREHASH_InventorySerial;
+extern char * _PREHASH_EdgeDataPacket;
+extern char * _PREHASH_AvatarPickerReply;
+extern char * _PREHASH_ParcelDwellReply;
+extern char * _PREHASH_IsForSale;
+extern char * _PREHASH_MuteID;
+extern char * _PREHASH_MeanCollisionAlert;
+extern char * _PREHASH_CanAcceptTasks;
+extern char * _PREHASH_ItemData;
+extern char * _PREHASH_AnimationList;
+extern char * _PREHASH_PassObject;
+extern char * _PREHASH_Reputation;
+extern char * _PREHASH_IntValue;
+extern char * _PREHASH_TargetType;
+extern char * _PREHASH_Amount;
+extern char * _PREHASH_HasAttachment;
+extern char * _PREHASH_UpdateAttachment;
+extern char * _PREHASH_RemoveAttachment;
+extern char * _PREHASH_HeightWidthBlock;
+extern char * _PREHASH_RequestObjectPropertiesFamily;
+extern char * _PREHASH_ObjectPropertiesFamily;
+extern char * _PREHASH_UserData;
+extern char * _PREHASH_IsReadable;
+extern char * _PREHASH_PathCurve;
+extern char * _PREHASH_Status;
+extern char * _PREHASH_FromGroup;
+extern char * _PREHASH_AlreadyVoted;
+extern char * _PREHASH_PlacesReply;
+extern char * _PREHASH_DirPlacesReply;
+extern char * _PREHASH_ParcelBuy;
+extern char * _PREHASH_DirFindQueryBackend;
+extern char * _PREHASH_DirPlacesQueryBackend;
+extern char * _PREHASH_DirClassifiedQueryBackend;
+extern char * _PREHASH_DirPicksQueryBackend;
+extern char * _PREHASH_DirLandQueryBackend;
+extern char * _PREHASH_DirPopularQueryBackend;
+extern char * _PREHASH_LogoutDemand;
+extern char * _PREHASH_HistoryData;
+extern char * _PREHASH_SnapshotID;
+extern char * _PREHASH_Aspect;
+extern char * _PREHASH_ParamSize;
+extern char * _PREHASH_VoteCast;
+extern char * _PREHASH_CastsShadows;
+extern char * _PREHASH_EveryoneMask;
+extern char * _PREHASH_SetSunPhase;
+extern char * _PREHASH_ObjectSpinUpdate;
+extern char * _PREHASH_MaturePublish;
+extern char * _PREHASH_UseExistingAsset;
+extern char * _PREHASH_Powers;
+extern char * _PREHASH_ParcelLocalID;
+extern char * _PREHASH_TeleportCancel;
+extern char * _PREHASH_UnixTime;
+extern char * _PREHASH_QueryFlags;
+extern char * _PREHASH_LastExecFroze;
+extern char * _PREHASH_AlwaysRun;
+extern char * _PREHASH_Bottom;
+extern char * _PREHASH_ButtonData;
+extern char * _PREHASH_SoundData;
+extern char * _PREHASH_ViewerStats;
+extern char * _PREHASH_RegionHandshake;
+extern char * _PREHASH_ObjectDescription;
+extern char * _PREHASH_Description;
+extern char * _PREHASH_ParamType;
+extern char * _PREHASH_UUIDNameReply;
+extern char * _PREHASH_UUIDGroupNameReply;
+extern char * _PREHASH_SaveAssetIntoInventory;
+extern char * _PREHASH_UserInfo;
+extern char * _PREHASH_AnimSequenceID;
+extern char * _PREHASH_NVPairs;
+extern char * _PREHASH_GroupNoticesListRequest;
+extern char * _PREHASH_ParcelAccessListRequest;
+extern char * _PREHASH_MuteListRequest;
+extern char * _PREHASH_StartPeriod;
+extern char * _PREHASH_RpcChannelRequest;
+extern char * _PREHASH_LandStatRequest;
+extern char * _PREHASH_PlacesQuery;
+extern char * _PREHASH_DirPlacesQuery;
+extern char * _PREHASH_SortOrder;
+extern char * _PREHASH_Hunter;
+extern char * _PREHASH_SunAngVelocity;
+extern char * _PREHASH_BinaryBucket;
+extern char * _PREHASH_ImagePacket;
+extern char * _PREHASH_StartGroupProposal;
+extern char * _PREHASH_EnergyLevel;
+extern char * _PREHASH_PriceForListing;
+extern char * _PREHASH_Scale;
+extern char * _PREHASH_EstateCovenantReply;
+extern char * _PREHASH_ParentEstateID;
+extern char * _PREHASH_Extra2;
+extern char * _PREHASH_Throttle;
+extern char * _PREHASH_SimIP;
+extern char * _PREHASH_GodID;
+extern char * _PREHASH_TeleportMinPrice;
+extern char * _PREHASH_VoteItem;
+extern char * _PREHASH_ObjectRotation;
+extern char * _PREHASH_SitRotation;
+extern char * _PREHASH_SnapSelection;
+extern char * _PREHASH_SoundTrigger;
+extern char * _PREHASH_TerrainRaiseLimit;
+extern char * _PREHASH_Quorum;
+extern char * _PREHASH_TokenBlock;
+extern char * _PREHASH_AgentBlock;
+extern char * _PREHASH_CommandBlock;
+extern char * _PREHASH_PricePublicObjectDecay;
+extern char * _PREHASH_SpawnPointPos;
+extern char * _PREHASH_AttachedSoundCutoffRadius;
+extern char * _PREHASH_VolumeDetail;
+extern char * _PREHASH_FromAgentName;
+extern char * _PREHASH_Range;
+extern char * _PREHASH_DirectoryVisibility;
+extern char * _PREHASH_PublicIP;
+extern char * _PREHASH_TeleportFailed;
+extern char * _PREHASH_OnlineStatusReply;
+extern char * _PREHASH_RequestAvatarInfo;
+extern char * _PREHASH_PreloadSound;
+extern char * _PREHASH_ScreenshotID;
+extern char * _PREHASH_CovenantTimestamp;
+extern char * _PREHASH_OldestUnacked;
+extern char * _PREHASH_SimulatorIP;
+extern char * _PREHASH_ObjectImport;
+extern char * _PREHASH_Value;
+extern char * _PREHASH_JointAxisOrAnchor;
+extern char * _PREHASH_Test0;
+extern char * _PREHASH_Test1;
+extern char * _PREHASH_Test2;
+extern char * _PREHASH_SunPhase;
+extern char * _PREHASH_Place;
+extern char * _PREHASH_Phase;
+extern char * _PREHASH_ParcelDivide;
+extern char * _PREHASH_PriceObjectClaim;
+extern char * _PREHASH_Field;
+extern char * _PREHASH_Ratio;
+extern char * _PREHASH_JoinGroupReply;
+extern char * _PREHASH_LiveHelpGroupReply;
+extern char * _PREHASH_Score;
+extern char * _PREHASH_ExpungeData;
+extern char * _PREHASH_Image;
+extern char * _PREHASH_ObjectClickAction;
+extern char * _PREHASH_Delta;
+extern char * _PREHASH_InitiateUpload;
+extern char * _PREHASH_Parameter;
+extern char * _PREHASH_Flags;
+extern char * _PREHASH_Plane;
+extern char * _PREHASH_Width;
+extern char * _PREHASH_Right;
+extern char * _PREHASH_DirFindQuery;
+extern char * _PREHASH_Textures;
+extern char * _PREHASH_EventData;
+extern char * _PREHASH_Final;
+extern char * _PREHASH_TelehubPos;
+extern char * _PREHASH_ReportAutosaveCrash;
+extern char * _PREHASH_Reset;
+extern char * _PREHASH_CreateTrustedCircuit;
+extern char * _PREHASH_DenyTrustedCircuit;
+extern char * _PREHASH_RequestTrustedCircuit;
+extern char * _PREHASH_Codec;
+extern char * _PREHASH_Level;
+extern char * _PREHASH_Modal;
+extern char * _PREHASH_ChildAgentUnknown;
+extern char * _PREHASH_LandingType;
+extern char * _PREHASH_ScriptRunningReply;
+extern char * _PREHASH_MoneyDetailsReply;
+extern char * _PREHASH_Reply;
+extern char * _PREHASH_TelehubRot;
+extern char * _PREHASH_RequestFriendship;
+extern char * _PREHASH_AcceptFriendship;
+extern char * _PREHASH_GroupAccountDetailsReply;
+extern char * _PREHASH_DwellInfo;
+extern char * _PREHASH_AgentResume;
+extern char * _PREHASH_ItemType;
+extern char * _PREHASH_MailFilter;
+extern char * _PREHASH_Disconnect;
+extern char * _PREHASH_SimPosition;
+extern char * _PREHASH_SimWideTotalPrims;
+extern char * _PREHASH_Index;
+extern char * _PREHASH_BaseFilename;
+extern char * _PREHASH_SimFilename;
+extern char * _PREHASH_LastOwnerID;
+extern char * _PREHASH_GroupNoticeRequest;
+extern char * _PREHASH_EmailMessageRequest;
+extern char * _PREHASH_MapItemRequest;
+extern char * _PREHASH_AgentCount;
+extern char * _PREHASH_MessageBlock;
+extern char * _PREHASH_FuseBlock;
+extern char * _PREHASH_AgentGroupData;
+extern char * _PREHASH_ClassifiedInfoUpdate;
+extern char * _PREHASH_RegionPos;
+extern char * _PREHASH_ParcelMediaUpdate;
+extern char * _PREHASH_NoticeID;
+extern char * _PREHASH_GridX;
+extern char * _PREHASH_GridY;
+extern char * _PREHASH_Title;
+extern char * _PREHASH_AuctionID;
+extern char * _PREHASH_VoteType;
+extern char * _PREHASH_CategoryID;
+extern char * _PREHASH_Token;
+extern char * _PREHASH_AggregatePerms;
+extern char * _PREHASH_StartParcelRemoveAck;
+extern char * _PREHASH_ObjectSelect;
+extern char * _PREHASH_ForceObjectSelect;
+extern char * _PREHASH_Price;
+extern char * _PREHASH_SunDirection;
+extern char * _PREHASH_FromName;
+extern char * _PREHASH_ChangeInventoryItemFlags;
+extern char * _PREHASH_Force;
+extern char * _PREHASH_TransactionBlock;
+extern char * _PREHASH_PowersMask;
+extern char * _PREHASH_Stamp;
+extern char * _PREHASH_TotalCredits;
+extern char * _PREHASH_State;
+extern char * _PREHASH_TextureIndex;
+extern char * _PREHASH_InviteeID;
+extern char * _PREHASH_ParcelReclaim;
+extern char * _PREHASH_Money;
+extern char * _PREHASH_PathTwist;
+extern char * _PREHASH_AuthBuyerID;
+extern char * _PREHASH_Color;
+extern char * _PREHASH_SourceType;
+extern char * _PREHASH_World;
+extern char * _PREHASH_QueryData;
+extern char * _PREHASH_Users;
+extern char * _PREHASH_SysOS;
+extern char * _PREHASH_Notes;
+extern char * _PREHASH_AvatarID;
+extern char * _PREHASH_FounderID;
+extern char * _PREHASH_EndPointID;
+extern char * _PREHASH_StipendEstimate;
+extern char * _PREHASH_LocationLookAt;
+extern char * _PREHASH_Sound;
+extern char * _PREHASH_Cover;
+extern char * _PREHASH_TotalObjectCount;
+extern char * _PREHASH_TextureEntry;
+extern char * _PREHASH_SquareMetersCommitted;
+extern char * _PREHASH_ChannelID;
+extern char * _PREHASH_Dwell;
+extern char * _PREHASH_North;
+extern char * _PREHASH_AgentUpdate;
+extern char * _PREHASH_PickGodDelete;
+extern char * _PREHASH_HostName;
+extern char * _PREHASH_PriceParcelClaim;
+extern char * _PREHASH_ParcelClaim;
+extern char * _PREHASH_AgentPowers;
+extern char * _PREHASH_ProfileHollow;
+extern char * _PREHASH_GroupRoleChanges;
+extern char * _PREHASH_Count;
+extern char * _PREHASH_South;
+extern char * _PREHASH_Entry;
+extern char * _PREHASH_ObjectUpdateCompressed;
+extern char * _PREHASH_MuteFlags;
+extern char * _PREHASH_Group;
+extern char * _PREHASH_AgentPause;
+extern char * _PREHASH_LanguagesText;
+extern char * _PREHASH_InternalScriptMail;
+extern char * _PREHASH_FindAgent;
+extern char * _PREHASH_AgentData;
+extern char * _PREHASH_FolderData;
+extern char * _PREHASH_AssetBlock;
+extern char * _PREHASH_AcceptNotices;
+extern char * _PREHASH_SetGroupAcceptNotices;
+extern char * _PREHASH_CloseCircuit;
+extern char * _PREHASH_LogControl;
+extern char * _PREHASH_TeleportFinish;
+extern char * _PREHASH_PathRevolutions;
+extern char * _PREHASH_ClassifiedInfoReply;
+extern char * _PREHASH_ParcelInfoReply;
+extern char * _PREHASH_AutosaveData;
+extern char * _PREHASH_SetStartLocation;
+extern char * _PREHASH_PassHours;
+extern char * _PREHASH_AttachmentPt;
+extern char * _PREHASH_ParcelFlags;
+extern char * _PREHASH_NumVotes;
+extern char * _PREHASH_AvatarPickerRequest;
+extern char * _PREHASH_TeleportLocationRequest;
+extern char * _PREHASH_DataHomeLocationRequest;
+extern char * _PREHASH_EventNotificationAddRequest;
+extern char * _PREHASH_ParcelDwellRequest;
+extern char * _PREHASH_EventLocationRequest;
+extern char * _PREHASH_EndPeriod;
+extern char * _PREHASH_SetStartLocationRequest;
+extern char * _PREHASH_QueryStart;
+extern char * _PREHASH_EjectData;
+extern char * _PREHASH_AvatarTextureUpdate;
+extern char * _PREHASH_RPCServerPort;
+extern char * _PREHASH_Bytes;
+extern char * _PREHASH_Extra;
+extern char * _PREHASH_ForceScriptControlRelease;
+extern char * _PREHASH_ParcelRelease;
+extern char * _PREHASH_VFileType;
+extern char * _PREHASH_EjectGroupMemberReply;
+extern char * _PREHASH_ImageData;
+extern char * _PREHASH_SpaceServerSimulatorTimeMessage;
+extern char * _PREHASH_SimulatorViewerTimeMessage;
+extern char * _PREHASH_Rotation;
+extern char * _PREHASH_Selection;
+extern char * _PREHASH_TransactionData;
+extern char * _PREHASH_OperationData;
+extern char * _PREHASH_ExpirationDate;
+extern char * _PREHASH_ParcelDeedToGroup;
+extern char * _PREHASH_DirPicksReply;
+extern char * _PREHASH_AvatarPicksReply;
+extern char * _PREHASH_GroupTitlesReply;
+extern char * _PREHASH_AgentInfo;
+extern char * _PREHASH_MoneyTransferBackend;
+extern char * _PREHASH_NextOwnerMask;
+extern char * _PREHASH_MuteData;
+extern char * _PREHASH_PassPrice;
+extern char * _PREHASH_SourceID;
+extern char * _PREHASH_ChangeUserRights;
+extern char * _PREHASH_TeleportFlags;
+extern char * _PREHASH_AssetData;
+extern char * _PREHASH_SlaveParcelData;
+extern char * _PREHASH_MultipleObjectUpdate;
+extern char * _PREHASH_ObjectUpdate;
+extern char * _PREHASH_ImprovedTerseObjectUpdate;
+extern char * _PREHASH_ConfirmXferPacket;
+extern char * _PREHASH_StartPingCheck;
+extern char * _PREHASH_SimWideDeletes;
+extern char * _PREHASH_LandStatReply;
+extern char * _PREHASH_IsPhantom;
+extern char * _PREHASH_AgentList;
+extern char * _PREHASH_SimApproved;
+extern char * _PREHASH_RezObject;
+extern char * _PREHASH_TaskLocalID;
+extern char * _PREHASH_ClaimDate;
+extern char * _PREHASH_MergeParcel;
+extern char * _PREHASH_Priority;
+extern char * _PREHASH_Building;
+extern char * _PREHASH_QueryText;
+extern char * _PREHASH_GroupNoticeAdd;
+extern char * _PREHASH_ReturnType;
+extern char * _PREHASH_FetchFolders;
+extern char * _PREHASH_SimulatorPublicHostBlock;
+extern char * _PREHASH_HeaderData;
+extern char * _PREHASH_RequestMultipleObjects;
+extern char * _PREHASH_RetrieveInstantMessages;
+extern char * _PREHASH_DequeueInstantMessages;
+extern char * _PREHASH_OpenCircuit;
+extern char * _PREHASH_SecureSessionID;
+extern char * _PREHASH_CrossedRegion;
+extern char * _PREHASH_DirGroupsReply;
+extern char * _PREHASH_AvatarGroupsReply;
+extern char * _PREHASH_EmailMessageReply;
+extern char * _PREHASH_GroupVoteHistoryItemReply;
+extern char * _PREHASH_ViewerPosition;
+extern char * _PREHASH_Position;
+extern char * _PREHASH_ParentEstate;
+extern char * _PREHASH_EstateName;
+extern char * _PREHASH_MuteName;
+extern char * _PREHASH_StartParcelRename;
+extern char * _PREHASH_BulkParcelRename;
+extern char * _PREHASH_ParcelRename;
+extern char * _PREHASH_ViewerFilename;
+extern char * _PREHASH_Positive;
+extern char * _PREHASH_UserReportInternal;
+extern char * _PREHASH_AvatarPropertiesRequest;
+extern char * _PREHASH_ParcelPropertiesRequest;
+extern char * _PREHASH_GroupProfileRequest;
+extern char * _PREHASH_AgentDataUpdateRequest;
+extern char * _PREHASH_PriceObjectScaleFactor;
+extern char * _PREHASH_DirPicksQuery;
+extern char * _PREHASH_OpenEnrollment;
+extern char * _PREHASH_GroupData;
+extern char * _PREHASH_RequestGodlikePowers;
+extern char * _PREHASH_GrantGodlikePowers;
+extern char * _PREHASH_TransactionID;
+extern char * _PREHASH_DestinationID;
+extern char * _PREHASH_Controls;
+extern char * _PREHASH_FirstDetachAll;
+extern char * _PREHASH_EstateID;
+extern char * _PREHASH_ImprovedInstantMessage;
+extern char * _PREHASH_AgentQuit;
+extern char * _PREHASH_CheckParcelSales;
+extern char * _PREHASH_ParcelSales;
+extern char * _PREHASH_CurrentInterval;
+extern char * _PREHASH_PriceRentLight;
+extern char * _PREHASH_MediaAutoScale;
+extern char * _PREHASH_NeighborBlock;
+extern char * _PREHASH_LayerData;
+extern char * _PREHASH_NVPairData;
+extern char * _PREHASH_TeleportLocal;
+extern char * _PREHASH_EjecteeID;
+extern char * _PREHASH_VoteInitiator;
+extern char * _PREHASH_TypeData;
+extern char * _PREHASH_OwnerIDs;
+extern char * _PREHASH_SystemKickUser;
+extern char * _PREHASH_TransactionTime;
+extern char * _PREHASH_TimeToLive;
+extern char * _PREHASH_StartParcelRemove;
+extern char * _PREHASH_BulkParcelRemove;
+extern char * _PREHASH_OldAgentID;
+extern char * _PREHASH_BonusEstimate;
+extern char * _PREHASH_MusicURL;
+extern char * _PREHASH_CompleteLure;
+extern char * _PREHASH_ParcelPrimBonus;
+extern char * _PREHASH_EjectUser;
+extern char * _PREHASH_CoarseLocationUpdate;
+extern char * _PREHASH_ChildAgentPositionUpdate;
+extern char * _PREHASH_StoreLocal;
+extern char * _PREHASH_GroupName;
+extern char * _PREHASH_PriceParcelRent;
+extern char * _PREHASH_SimStatus;
+extern char * _PREHASH_TransactionSuccess;
+extern char * _PREHASH_LureType;
+extern char * _PREHASH_GroupMask;
+extern char * _PREHASH_SitObject;
+extern char * _PREHASH_Override;
+extern char * _PREHASH_LocomotionState;
+extern char * _PREHASH_PriceUpload;
+extern char * _PREHASH_RemoveParcel;
+extern char * _PREHASH_ConfirmAuctionStart;
+extern char * _PREHASH_RpcScriptRequestInbound;
+extern char * _PREHASH_ActiveGroupID;
+extern char * _PREHASH_ParcelReturnObjects;
+extern char * _PREHASH_TotalObjects;
+extern char * _PREHASH_ObjectExtraParams;
+extern char * _PREHASH_Questions;
+extern char * _PREHASH_TransferAbort;
+extern char * _PREHASH_TransferInventory;
+extern char * _PREHASH_RayTargetID;
+extern char * _PREHASH_ClaimPrice;
+extern char * _PREHASH_ObjectProperties;
+extern char * _PREHASH_ParcelProperties;
+extern char * _PREHASH_EstateOwnerID;
+extern char * _PREHASH_LogoutRequest;
+extern char * _PREHASH_AssetUploadRequest;
+extern char * _PREHASH_ReputationIndividualRequest;
+extern char * _PREHASH_MajorVersion;
+extern char * _PREHASH_MinorVersion;
+extern char * _PREHASH_SimulatorAssign;
+extern char * _PREHASH_TransactionType;
+extern char * _PREHASH_AvatarPropertiesUpdate;
+extern char * _PREHASH_ParcelPropertiesUpdate;
+extern char * _PREHASH_FetchItems;
+extern char * _PREHASH_AbortXfer;
+extern char * _PREHASH_DeRezAck;
+extern char * _PREHASH_TakeControls;
+extern char * _PREHASH_DirLandReply;
+extern char * _PREHASH_SpaceLocationTeleportReply;
+extern char * _PREHASH_MuteType;
+extern char * _PREHASH_IMViaEMail;
+extern char * _PREHASH_StartExpungeProcessAck;
+extern char * _PREHASH_RentPrice;
+extern char * _PREHASH_GenericMessage;
+extern char * _PREHASH_ChildAgentAlive;
+extern char * _PREHASH_AssetType;
+extern char * _PREHASH_SpawnPointBlock;
+extern char * _PREHASH_AttachmentBlock;
+extern char * _PREHASH_ObjectMaterial;
+extern char * _PREHASH_OwnerName;
+extern char * _PREHASH_AvatarNotesReply;
+extern char * _PREHASH_CacheID;
+extern char * _PREHASH_OwnerMask;
+extern char * _PREHASH_TransferInventoryAck;
+
+
+void init_prehash_data();
+
+
+
+
+
+#endif
diff --git a/indra/llmessage/message_string_table.cpp b/indra/llmessage/message_string_table.cpp
new file mode 100644
index 0000000000..687b47a112
--- /dev/null
+++ b/indra/llmessage/message_string_table.cpp
@@ -0,0 +1,75 @@
+/**
+ * @file message_string_table.cpp
+ * @brief static string table for message template
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llerror.h"
+#include "message.h"
+
+inline U32 message_hash_my_string(const char *str)
+{
+ U32 retval = 0;
+ while (*str++)
+ {
+ retval += *str;
+ retval <<= 1;
+ }
+ return (retval % MESSAGE_NUMBER_OF_HASH_BUCKETS);
+}
+
+
+LLMessageStringTable gMessageStringTable;
+
+
+LLMessageStringTable::LLMessageStringTable()
+: mUsed(0)
+{
+ for (U32 i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ mEmpty[i] = TRUE;
+ mString[i][0] = 0;
+ }
+}
+
+
+LLMessageStringTable::~LLMessageStringTable()
+{ }
+
+
+char* LLMessageStringTable::getString(const char *str)
+{
+ U32 hash_value = message_hash_my_string(str);
+ while (!mEmpty[hash_value])
+ {
+ if (!strncmp(str, mString[hash_value], MESSAGE_MAX_STRINGS_LENGTH))
+ {
+ return mString[hash_value];
+ }
+ else
+ {
+ hash_value++;
+ hash_value %= MESSAGE_NUMBER_OF_HASH_BUCKETS;
+ }
+ }
+ // not found, so add!
+ strncpy(mString[hash_value], str, MESSAGE_MAX_STRINGS_LENGTH);
+ mString[hash_value][MESSAGE_MAX_STRINGS_LENGTH - 1] = 0;
+ mEmpty[hash_value] = FALSE;
+ mUsed++;
+ if (mUsed >= MESSAGE_NUMBER_OF_HASH_BUCKETS - 1)
+ {
+ U32 i;
+ llinfos << "Dumping string table before crashing on HashTable full!" << llendl;
+ for (i = 0; i < MESSAGE_NUMBER_OF_HASH_BUCKETS; i++)
+ {
+ llinfos << "Entry #" << i << ": " << mString[i] << llendl;
+ }
+ }
+ return mString[hash_value];
+}
+
diff --git a/indra/llmessage/net.cpp b/indra/llmessage/net.cpp
new file mode 100644
index 0000000000..2b712840d8
--- /dev/null
+++ b/indra/llmessage/net.cpp
@@ -0,0 +1,516 @@
+/**
+ * @file net.cpp
+ * @brief Cross-platform routines for sending and receiving packets.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "net.h"
+
+// system library includes
+#include <stdexcept>
+#include <stdio.h>
+
+#if !LL_WINDOWS // Windows Versions
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <errno.h>
+#endif
+
+// linden library includes
+#include "network.h"
+#include "llerror.h"
+#include "llhost.h"
+#include "lltimer.h"
+#include "indra_constants.h"
+
+
+// Globals
+#if LL_WINDOWS
+
+SOCKADDR_IN stDstAddr;
+SOCKADDR_IN stSrcAddr;
+SOCKADDR_IN stLclAddr;
+static WSADATA stWSAData;
+
+#else
+
+struct sockaddr_in stDstAddr;
+struct sockaddr_in stSrcAddr;
+struct sockaddr_in stLclAddr;
+
+#if LL_DARWIN
+#ifndef _SOCKLEN_T
+#define _SOCKLEN_T
+typedef int socklen_t;
+#endif
+#endif
+
+#endif
+
+
+const char* LOOPBACK_ADDRESS_STRING = "127.0.0.1";
+
+#if LL_DARWIN
+ // Mac OS X returns an error when trying to set these to 400000. Smaller values succeed.
+ const int SEND_BUFFER_SIZE = 200000;
+ const int RECEIVE_BUFFER_SIZE = 200000;
+#else // LL_DARWIN
+ const int SEND_BUFFER_SIZE = 400000;
+ const int RECEIVE_BUFFER_SIZE = 400000;
+#endif // LL_DARWIN
+
+// universal functions (cross-platform)
+
+LLHost get_sender()
+{
+ return LLHost(stSrcAddr.sin_addr.s_addr, ntohs(stSrcAddr.sin_port));
+}
+
+U32 get_sender_ip(void)
+{
+ return stSrcAddr.sin_addr.s_addr;
+}
+
+U32 get_sender_port()
+{
+ return ntohs(stSrcAddr.sin_port);
+}
+
+const char* u32_to_ip_string(U32 ip)
+{
+ static char buffer[MAXADDRSTR]; /* Flawfinder: ignore */
+
+ // Convert the IP address into a string
+ in_addr in;
+ in.s_addr = ip;
+ char* result = inet_ntoa(in);
+
+ // NULL indicates error in conversion
+ if (result != NULL)
+ {
+ strncpy( buffer, result, MAXADDRSTR ); /* Flawfinder: ignore */
+ buffer[MAXADDRSTR-1] = '\0';
+ return buffer;
+ }
+ else
+ {
+ return "(bad IP addr)";
+ }
+}
+
+
+// Returns ip_string if successful, NULL if not. Copies into ip_string
+char *u32_to_ip_string(U32 ip, char *ip_string)
+{
+ char *result;
+ in_addr in;
+
+ // Convert the IP address into a string
+ in.s_addr = ip;
+ result = inet_ntoa(in);
+
+ // NULL indicates error in conversion
+ if (result != NULL)
+ {
+ //the function signature needs to change to pass in the lengfth of first and last.
+ strcpy(ip_string, result);
+ return ip_string;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+// Wrapper for inet_addr()
+U32 ip_string_to_u32(const char* ip_string)
+{
+ return inet_addr(ip_string);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Windows Versions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#if LL_WINDOWS
+
+S32 start_net(S32& socket_out, int& nPort)
+{
+ // Create socket, make non-blocking
+ // Init WinSock
+ int nRet;
+ int hSocket;
+
+ int snd_size = SEND_BUFFER_SIZE;
+ int rec_size = RECEIVE_BUFFER_SIZE;
+ int buff_size = 4;
+
+ // Initialize windows specific stuff
+ if(WSAStartup(0x0202, &stWSAData))
+ {
+ S32 err = WSAGetLastError();
+ WSACleanup();
+ llwarns << "Windows Sockets initialization failed, err " << err << llendl;
+ return 1;
+ }
+
+ // Get a datagram socket
+ hSocket = (int)socket(AF_INET, SOCK_DGRAM, 0);
+ if (hSocket == INVALID_SOCKET)
+ {
+ S32 err = WSAGetLastError();
+ WSACleanup();
+ llwarns << "socket() failed, err " << err << llendl;
+ return 2;
+ }
+
+ // Name the socket (assign the local port number to receive on)
+ stLclAddr.sin_family = AF_INET;
+ stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ stLclAddr.sin_port = htons(nPort);
+
+ S32 attempt_port = nPort;
+ llinfos << "attempting to connect on port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+
+ if (nRet == SOCKET_ERROR)
+ {
+ // If we got an address in use error...
+ if (WSAGetLastError() == WSAEADDRINUSE)
+ {
+ // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX
+ for(attempt_port = PORT_DISCOVERY_RANGE_MIN;
+ attempt_port <= PORT_DISCOVERY_RANGE_MAX;
+ attempt_port++)
+ {
+ stLclAddr.sin_port = htons(attempt_port);
+ llinfos << "trying port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+
+ if (!(nRet == SOCKET_ERROR &&
+ WSAGetLastError() == WSAEADDRINUSE))
+ {
+ break;
+ }
+ }
+
+ if (nRet == SOCKET_ERROR)
+ {
+ llwarns << "startNet() : Couldn't find available network port." << llendl;
+ // Fail gracefully here in release
+ return 3;
+ }
+ }
+ else
+ // Some other socket error
+ {
+ llwarns << llformat("bind() port: %d failed, Err: %d\n", nPort, WSAGetLastError()) << llendl;
+ // Fail gracefully in release.
+ return 4;
+ }
+ }
+ llinfos << "connected on port " << attempt_port << llendl;
+ nPort = attempt_port;
+
+ // Set socket to be non-blocking
+ unsigned long argp = 1;
+ nRet = ioctlsocket (hSocket, FIONBIO, &argp);
+ if (nRet == SOCKET_ERROR)
+ {
+ printf("Failed to set socket non-blocking, Err: %d\n",
+ WSAGetLastError());
+ }
+
+ // set a large receive buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set receive buffer size!" << llendl;
+ }
+
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set send buffer size!" << llendl;
+ }
+
+ getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
+ getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
+
+ llinfos << "startNet - receive buffer size : " << rec_size << llendl;
+ llinfos << "startNet - send buffer size : " << snd_size << llendl;
+
+ // Setup a destination address
+ char achMCAddr[MAXADDRSTR] = " "; /* Flawfinder: ignore */
+ stDstAddr.sin_family = AF_INET;
+ stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr);
+ stDstAddr.sin_port = htons(nPort);
+
+ socket_out = hSocket;
+ return 0;
+}
+
+void end_net()
+{
+ WSACleanup();
+}
+
+S32 receive_packet(int hSocket, char * receiveBuffer)
+{
+ // Receives data asynchronously from the socket set by initNet().
+ // Returns the number of bytes received into dataReceived, or zero
+ // if there is no data received.
+ int nRet;
+ int addr_size = sizeof(struct sockaddr_in);
+
+ nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size);
+ if (nRet == SOCKET_ERROR )
+ {
+ if (WSAEWOULDBLOCK == WSAGetLastError())
+ return 0;
+ if (WSAECONNRESET == WSAGetLastError())
+ return 0;
+ llinfos << "receivePacket() failed, Error: " << WSAGetLastError() << llendl;
+ }
+
+ return nRet;
+}
+
+// Returns TRUE on success.
+BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, int nPort)
+{
+ // Sends a packet to the address set in initNet
+ //
+ int nRet = 0;
+ U32 last_error = 0;
+
+ stDstAddr.sin_addr.s_addr = recipient;
+ stDstAddr.sin_port = htons(nPort);
+ do
+ {
+ nRet = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr));
+
+ if (nRet == SOCKET_ERROR )
+ {
+ last_error = WSAGetLastError();
+ if (last_error != WSAEWOULDBLOCK)
+ {
+ // WSAECONNRESET - I think this is caused by an ICMP "connection refused"
+ // message being sent back from a Linux box... I'm not finding helpful
+ // documentation or web pages on this. The question is whether the packet
+ // actually got sent or not. Based on the structure of this code, I would
+ // assume it is. JNC 2002.01.18
+ if (WSAECONNRESET == WSAGetLastError())
+ {
+ return TRUE;
+ }
+ llinfos << "sendto() failed to " << u32_to_ip_string(recipient) << ":" << nPort
+ << ", Error " << last_error << llendl;
+ }
+ }
+ } while ( (nRet == SOCKET_ERROR)
+ &&(last_error == WSAEWOULDBLOCK));
+
+ return (nRet != SOCKET_ERROR);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// Linux Versions
+//////////////////////////////////////////////////////////////////////////////////////////
+
+#else
+
+// Create socket, make non-blocking
+S32 start_net(S32& socket_out, int& nPort)
+{
+ int hSocket, nRet;
+ int snd_size = SEND_BUFFER_SIZE;
+ int rec_size = RECEIVE_BUFFER_SIZE;
+
+ socklen_t buff_size = 4;
+
+ // Create socket
+ hSocket = socket(AF_INET, SOCK_DGRAM, 0);
+ if (hSocket < 0)
+ {
+ llwarns << "socket() failed" << llendl;
+ return 1;
+ }
+
+ // Don't bind() if we want the operating system to assign our ports for
+ // us.
+ if (NET_USE_OS_ASSIGNED_PORT == nPort)
+ {
+ // Do nothing; the operating system will do it for us.
+ }
+ else
+ {
+ // Name the socket (assign the local port number to receive on)
+ stLclAddr.sin_family = AF_INET;
+ stLclAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ stLclAddr.sin_port = htons(nPort);
+ U32 attempt_port = nPort;
+ llinfos << "attempting to connect on port " << attempt_port << llendl;
+
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+ if (nRet < 0)
+ {
+ // If we got an address in use error...
+ if (errno == EADDRINUSE)
+ {
+ // Try all ports from PORT_DISCOVERY_RANGE_MIN to PORT_DISCOVERY_RANGE_MAX
+ for(attempt_port = PORT_DISCOVERY_RANGE_MIN;
+ attempt_port <= PORT_DISCOVERY_RANGE_MAX;
+ attempt_port++)
+ {
+ stLclAddr.sin_port = htons(attempt_port);
+ llinfos << "trying port " << attempt_port << llendl;
+ nRet = bind(hSocket, (struct sockaddr*) &stLclAddr, sizeof(stLclAddr));
+ if (!((nRet < 0) && (errno == EADDRINUSE)))
+ {
+ break;
+ }
+ }
+ if (nRet < 0)
+ {
+ llwarns << "startNet() : Couldn't find available network port." << llendl;
+ // Fail gracefully in release.
+ return 3;
+ }
+ }
+ // Some other socket error
+ else
+ {
+ llwarns << llformat ("bind() port: %d failed, Err: %s\n", nPort, strerror(errno)) << llendl;
+ // Fail gracefully in release.
+ return 4;
+ }
+ }
+ llinfos << "connected on port " << attempt_port << llendl;
+ nPort = attempt_port;
+ }
+ // Set socket to be non-blocking
+ fcntl(hSocket, F_SETFL, O_NONBLOCK);
+ // set a large receive buffer
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set receive size!" << llendl;
+ }
+ nRet = setsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, buff_size);
+ if (nRet)
+ {
+ llinfos << "Can't set send size!" << llendl;
+ }
+ getsockopt(hSocket, SOL_SOCKET, SO_RCVBUF, (char *)&rec_size, &buff_size);
+ getsockopt(hSocket, SOL_SOCKET, SO_SNDBUF, (char *)&snd_size, &buff_size);
+
+ llinfos << "startNet - receive buffer size : " << rec_size << llendl;
+ llinfos << "startNet - send buffer size : " << snd_size << llendl;
+
+ // Setup a destination address
+ char achMCAddr[MAXADDRSTR] = "127.0.0.1"; /* Flawfinder: ignore */
+ stDstAddr.sin_family = AF_INET;
+ stDstAddr.sin_addr.s_addr = inet_addr(achMCAddr);
+ stDstAddr.sin_port = htons(nPort);
+
+ socket_out = hSocket;
+ return 0;
+}
+
+void end_net()
+{
+}
+
+int receive_packet(int hSocket, char * receiveBuffer)
+{
+ // Receives data asynchronously from the socket set by initNet().
+ // Returns the number of bytes received into dataReceived, or zero
+ // if there is no data received.
+ // or -1 if an error occured!
+ int nRet;
+ socklen_t addr_size = sizeof(struct sockaddr_in);
+
+ nRet = recvfrom(hSocket, receiveBuffer, NET_BUFFER_SIZE, 0, (struct sockaddr*)&stSrcAddr, &addr_size);
+
+ if (nRet == -1)
+ {
+ // To maintain consistency with the Windows implementation, return a zero for size on error.
+ return 0;
+ }
+
+ return nRet;
+}
+
+BOOL send_packet(int hSocket, const char * sendBuffer, int size, U32 recipient, int nPort)
+{
+ int ret;
+ BOOL success;
+ BOOL resend;
+ S32 send_attempts = 0;
+
+ stDstAddr.sin_addr.s_addr = recipient;
+ stDstAddr.sin_port = htons(nPort);
+
+ do
+ {
+ ret = sendto(hSocket, sendBuffer, size, 0, (struct sockaddr*)&stDstAddr, sizeof(stDstAddr));
+ send_attempts++;
+
+ if (ret >= 0)
+ {
+ // successful send
+ success = TRUE;
+ resend = FALSE;
+ }
+ else
+ {
+ // send failed, check to see if we should resend
+ success = FALSE;
+
+ if (errno == EAGAIN)
+ {
+ // say nothing, just repeat send
+ llinfos << "sendto() reported buffer full, resending (attempt " << send_attempts << ")" << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = TRUE;
+ }
+ else if (errno == ECONNREFUSED)
+ {
+ // response to ICMP connection refused message on earlier send
+ llinfos << "sendto() reported connection refused, resending (attempt " << send_attempts << ")" << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = TRUE;
+ }
+ else
+ {
+ // some other error
+ llinfos << "sendto() failed: " << errno << ", " << strerror(errno) << llendl;
+ llinfos << inet_ntoa(stDstAddr.sin_addr) << ":" << nPort << llendl;
+ resend = FALSE;
+ }
+ }
+ }
+ while ( resend && send_attempts < 3);
+
+ if (send_attempts >= 3)
+ {
+ llinfos << "sendPacket() bailed out of send!" << llendl;
+ return FALSE;
+ }
+
+ return success;
+}
+
+#endif
+
+//EOF
diff --git a/indra/llmessage/net.h b/indra/llmessage/net.h
new file mode 100644
index 0000000000..1044807ba6
--- /dev/null
+++ b/indra/llmessage/net.h
@@ -0,0 +1,50 @@
+/**
+ * @file net.h
+ * @brief Cross platform UDP network code.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_NET_H
+#define LL_NET_H
+
+class LLTimer;
+class LLHost;
+
+#define NET_BUFFER_SIZE (0x2000)
+
+// Request a free local port from the operating system
+#define NET_USE_OS_ASSIGNED_PORT 0
+
+// Returns 0 on success, non-zero on error.
+// Sets socket handler/descriptor, changes nPort if port requested is unavailable.
+S32 start_net(S32& socket_out, int& nPort);
+void end_net();
+
+// returns size of packet or -1 in case of error
+S32 receive_packet(int hSocket, char * receiveBuffer);
+
+BOOL send_packet(int hSocket, const char *sendBuffer, int size, U32 recipient, int nPort); // Returns TRUE on success.
+
+//void get_sender(char * tmp);
+LLHost get_sender();
+U32 get_sender_port();
+U32 get_sender_ip(void);
+
+const char* u32_to_ip_string(U32 ip); // Returns pointer to internal string buffer, "(bad IP addr)" on failure, cannot nest calls
+char* u32_to_ip_string(U32 ip, char *ip_string); // NULL on failure, ip_string on success, you must allocate at least MAXADDRSTR chars
+U32 ip_string_to_u32(const char* ip_string); // Wrapper for inet_addr()
+
+extern const char* LOOPBACK_ADDRESS_STRING;
+
+
+// useful MTU consts
+
+const S32 MTUBYTES = 1200; // 1500 = standard Ethernet MTU
+const S32 ETHERNET_MTU_BYTES = 1500;
+const S32 MTUBITS = MTUBYTES*8;
+const S32 MTUU32S = MTUBITS/32;
+
+
+#endif
diff --git a/indra/llmessage/partsyspacket.cpp b/indra/llmessage/partsyspacket.cpp
new file mode 100644
index 0000000000..4030cd815b
--- /dev/null
+++ b/indra/llmessage/partsyspacket.cpp
@@ -0,0 +1,1277 @@
+/**
+ * @file partsyspacket.cpp
+ * @brief Object for packing particle system initialization parameters
+ * before sending them over the network.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "partsyspacket.h"
+#include "imageids.h"
+
+// this function is global
+void gSetInitDataDefaults(LLPartInitData *setMe)
+{
+ U32 i;
+
+ //for(i = 0; i < 18; i++)
+ //{
+ // setMe->k[i] = 0.0f;
+ //}
+
+ //setMe->kill_p[0] = setMe->kill_p[1] = setMe->kill_p[2] = 0.0f;
+ //setMe->kill_p[3] = -0.2f; // time parameter, die when t= 5.0f
+ //setMe->kill_p[4] = 1.0f;
+ //setMe->kill_p[5] = -0.5f; // or radius == 2 (contracting)
+
+ //setMe->bounce_p[0] = setMe->bounce_p[1] =
+ // setMe->bounce_p[2] = setMe->bounce_p[3] = 0.0f;
+ //setMe->bounce_p[4] = 1.0f;
+
+ setMe->bounce_b = 1.0f;
+ // i just changed the meaning of bounce_b
+ // its now the attenuation from revlecting your velocity across the normal
+ // set by bounce_p
+
+ //setMe->pos_ranges[0] = setMe->pos_ranges[2] = setMe->pos_ranges[4] = -1.0f;
+ //setMe->pos_ranges[1] = setMe->pos_ranges[3] = setMe->pos_ranges[5] = 1.0f;
+
+ //setMe->vel_ranges[0] = setMe->vel_ranges[2] = setMe->vel_ranges[4] = -1.0f;
+ //setMe->vel_ranges[1] = setMe->vel_ranges[3] = setMe->vel_ranges[5] = 1.0f;
+
+ for(i = 0; i < 3; i++)
+ {
+ setMe->diffEqAlpha[i] = 0.0f;
+ setMe->diffEqScale[i] = 0.0f;
+ }
+
+ setMe->scale_range[0] = 1.00f;
+ setMe->scale_range[1] = 5.00f;
+ setMe->scale_range[2] = setMe->scale_range[3] = 0.0f;
+
+ setMe->alpha_range[0] = setMe->alpha_range[1] = 1.0f;
+ setMe->alpha_range[2] = setMe->alpha_range[3] = 0.0f;
+
+ setMe->vel_offset[0] = 0.0f;
+ setMe->vel_offset[1] = 0.0f;
+ setMe->vel_offset[2] = 0.0f;
+
+ // start dropping particles when I'm more then one sim away
+ setMe->mDistBeginFadeout = 256.0f;
+ setMe->mDistEndFadeout = 1.414f * 512.0f;
+ // stop displaying particles when I'm more then two sim diagonals away
+
+ setMe->mImageUuid = IMG_SHOT;
+
+ for(i = 0; i < 8; i++)
+ {
+ setMe->mFlags[i] = 0x00;
+ }
+
+ setMe->createMe = TRUE;
+
+ setMe->maxParticles = 25;
+ setMe->initialParticles = 25;
+
+ //These defaults are for an explosion - a short lived set of debris affected by gravity.
+ //Action flags default to PART_SYS_AFFECTED_BY_WIND + PART_SYS_AFFECTED_BY_GRAVITY + PART_SYS_DISTANCE_DEATH
+ setMe->mFlags[PART_SYS_ACTION_BYTE] = PART_SYS_AFFECTED_BY_WIND | PART_SYS_AFFECTED_BY_GRAVITY | PART_SYS_DISTANCE_DEATH;
+ setMe->mFlags[PART_SYS_KILL_BYTE] = PART_SYS_DISTANCE_DEATH + PART_SYS_TIME_DEATH;
+
+ setMe->killPlaneNormal[0] = 0.0f;setMe->killPlaneNormal[1] = 0.0f;setMe->killPlaneNormal[2] = 1.0f; //Straight up
+ setMe->killPlaneZ = 0.0f; //get local ground z as an approximation if turn on PART_SYS_KILL_PLANE
+ setMe->bouncePlaneNormal[0] = 0.0f;setMe->bouncePlaneNormal[1] = 0.0f;setMe->bouncePlaneNormal[2] = 1.0f; //Straight up
+ setMe->bouncePlaneZ = 0.0f; //get local ground z as an approximation if turn on PART_SYS_BOUNCE
+ setMe->spawnRange = 1.0f;
+ setMe->spawnFrequency = 0.0f; //Create the instant one dies
+ setMe->spawnFreqencyRange = 0.0f;
+ setMe->spawnDirection[0] = 0.0f;setMe->spawnDirection[1] = 0.0f;setMe->spawnDirection[2] = 1.0f; //Straight up
+ setMe->spawnDirectionRange = 1.0f; //global scattering
+ setMe->spawnVelocity = 0.75f;
+ setMe->spawnVelocityRange = 0.25f; //velocity +/- 0.25
+ setMe->speedLimit = 1.0f;
+
+ setMe->windWeight = 0.5f; //0.0f means looks like a heavy object (if gravity is on), 1.0f means light and fluffy
+ setMe->currentGravity[0] = 0.0f;setMe->currentGravity[1] = 0.0f;setMe->currentGravity[2] = -9.81f;
+ //This has to be constant to allow for compression
+
+ setMe->gravityWeight = 0.5f; //0.0f means boyed by air, 1.0f means it's a lead weight
+ setMe->globalLifetime = 0.0f; //Arbitrary, but default is no global die, so doesn't matter
+ setMe->individualLifetime = 5.0f;
+ setMe->individualLifetimeRange = 1.0f; //Particles last 5 secs +/- 1
+ setMe->alphaDecay = 1.0f; //normal alpha fadeout
+ setMe->scaleDecay = 0.0f; //no scale decay
+ setMe->distanceDeath = 10.0f; //die if hit unit radius
+ setMe->dampMotionFactor = 0.0f;
+
+ setMe->windDiffusionFactor[0] = 0.0f;
+ setMe->windDiffusionFactor[1] = 0.0f;
+ setMe->windDiffusionFactor[2] = 0.0f;
+}
+
+LLPartSysCompressedPacket::LLPartSysCompressedPacket()
+{
+ // default constructor for mDefaults called implicitly/automatically here
+ for(int i = 0; i < MAX_PART_SYS_PACKET_SIZE; i++)
+ {
+ mData[i] = '\0';
+ }
+
+ gSetInitDataDefaults(&mDefaults);
+}
+
+LLPartSysCompressedPacket::~LLPartSysCompressedPacket()
+{
+ // no dynamic data is stored by this class, do nothing.
+}
+
+void LLPartSysCompressedPacket::writeFlagByte(LLPartInitData *in)
+{
+ mData[0] = mData[1] = mData[2] = '\0';
+
+ U32 i;
+ //for(i = 1; i < 18; i++) {
+ // if(in->k[i] != mDefaults.k[i])
+ // {
+ // mData[0] |= PART_SYS_K_MASK;
+ // break;
+ // }
+ //}
+
+ if(in->killPlaneZ != mDefaults.killPlaneZ ||
+ in->killPlaneNormal[0] != mDefaults.killPlaneNormal[0] ||
+ in->killPlaneNormal[1] != mDefaults.killPlaneNormal[1] ||
+ in->killPlaneNormal[2] != mDefaults.killPlaneNormal[2] ||
+ in->distanceDeath != mDefaults.distanceDeath)
+ {
+ mData[0] |= PART_SYS_KILL_P_MASK;
+ }
+
+
+
+ if(in->bouncePlaneZ != mDefaults.bouncePlaneZ ||
+ in->bouncePlaneNormal[0] != mDefaults.bouncePlaneNormal[0] ||
+ in->bouncePlaneNormal[1] != mDefaults.bouncePlaneNormal[1] ||
+ in->bouncePlaneNormal[2] != mDefaults.bouncePlaneNormal[2])
+ {
+ mData[0] |= PART_SYS_BOUNCE_P_MASK;
+ }
+
+ if(in->bounce_b != mDefaults.bounce_b)
+ {
+ mData[0] |= PART_SYS_BOUNCE_B_MASK;
+ }
+
+
+ //if(in->pos_ranges[0] != mDefaults.pos_ranges[0] || in->pos_ranges[1] != mDefaults.pos_ranges[1] ||
+ // in->pos_ranges[2] != mDefaults.pos_ranges[2] || in->pos_ranges[3] != mDefaults.pos_ranges[3] ||
+ // in->pos_ranges[4] != mDefaults.pos_ranges[4] || in->pos_ranges[5] != mDefaults.pos_ranges[5])
+ //{
+ // mData[0] |= PART_SYS_POS_RANGES_MASK;
+ //}
+
+ //if(in->vel_ranges[0] != mDefaults.vel_ranges[0] || in->vel_ranges[1] != mDefaults.vel_ranges[1] ||
+ // in->vel_ranges[2] != mDefaults.vel_ranges[2] || in->vel_ranges[3] != mDefaults.vel_ranges[3] ||
+ // in->vel_ranges[4] != mDefaults.vel_ranges[4] || in->vel_ranges[5] != mDefaults.vel_ranges[5])
+ //{
+// mData[0] |= PART_SYS_VEL_RANGES_MASK;
+ //}
+
+
+ if(in->diffEqAlpha[0] != mDefaults.diffEqAlpha[0] ||
+ in->diffEqAlpha[1] != mDefaults.diffEqAlpha[1] ||
+ in->diffEqAlpha[2] != mDefaults.diffEqAlpha[2] ||
+ in->diffEqScale[0] != mDefaults.diffEqScale[0] ||
+ in->diffEqScale[1] != mDefaults.diffEqScale[1] ||
+ in->diffEqScale[2] != mDefaults.diffEqScale[2])
+ {
+ mData[0] |= PART_SYS_ALPHA_SCALE_DIFF_MASK;
+ }
+
+
+ if(in->scale_range[0] != mDefaults.scale_range[0] ||
+ in->scale_range[1] != mDefaults.scale_range[1] ||
+ in->scale_range[2] != mDefaults.scale_range[2] ||
+ in->scale_range[3] != mDefaults.scale_range[3])
+ {
+ mData[0] |= PART_SYS_SCALE_RANGE_MASK;
+ }
+
+
+ if(in->alpha_range[0] != mDefaults.alpha_range[0] ||
+ in->alpha_range[1] != mDefaults.alpha_range[1] ||
+ in->alpha_range[2] != mDefaults.alpha_range[2] ||
+ in->alpha_range[3] != mDefaults.alpha_range[3])
+ {
+ mData[2] |= PART_SYS_BYTE_3_ALPHA_MASK;
+ }
+
+ if(in->vel_offset[0] != mDefaults.vel_offset[0] ||
+ in->vel_offset[1] != mDefaults.vel_offset[1] ||
+ in->vel_offset[2] != mDefaults.vel_offset[2])
+ {
+ mData[0] |= PART_SYS_VEL_OFFSET_MASK;
+ }
+
+
+ if(in->mImageUuid != mDefaults.mImageUuid)
+ {
+ mData[0] |= PART_SYS_M_IMAGE_UUID_MASK;
+ }
+
+ for( i = 0; i < 8; i++)
+ {
+ if(in->mFlags[i])
+ {
+ mData[1] |= 1<<i;
+// llprintline("Flag \"%x\" gets byte \"%x\"\n", i<<i, in->mFlags[i]);
+ }
+ }
+
+
+ if(in->spawnRange != mDefaults.spawnRange ||
+ in->spawnFrequency != mDefaults.spawnFrequency ||
+ in->spawnFreqencyRange != mDefaults.spawnFreqencyRange ||
+ in->spawnDirection[0] != mDefaults.spawnDirection[0] ||
+ in->spawnDirection[1] != mDefaults.spawnDirection[1] ||
+ in->spawnDirection[2] != mDefaults.spawnDirection[2] ||
+ in->spawnDirectionRange != mDefaults.spawnDirectionRange ||
+ in->spawnVelocity != mDefaults.spawnVelocity ||
+ in->spawnVelocityRange != mDefaults.spawnVelocityRange)
+ {
+ mData[3] |= PART_SYS_BYTE_SPAWN_MASK;
+ }
+
+
+ if(in->windWeight != mDefaults.windWeight ||
+ in->currentGravity[0] != mDefaults.currentGravity[0] ||
+ in->currentGravity[1] != mDefaults.currentGravity[1] ||
+ in->currentGravity[2] != mDefaults.currentGravity[2] ||
+ in->gravityWeight != mDefaults.gravityWeight)
+ {
+ mData[3] |= PART_SYS_BYTE_ENVIRONMENT_MASK;
+ }
+
+
+ if(in->globalLifetime != mDefaults.globalLifetime ||
+ in->individualLifetime != mDefaults.individualLifetime ||
+ in->individualLifetimeRange != mDefaults.individualLifetimeRange)
+ {
+ mData[3] |= PART_SYS_BYTE_LIFESPAN_MASK;
+ }
+
+
+ if(in->speedLimit != mDefaults.speedLimit ||
+ in->alphaDecay != mDefaults.alphaDecay ||
+ in->scaleDecay != mDefaults.scaleDecay ||
+ in->dampMotionFactor != mDefaults.dampMotionFactor)
+ {
+ mData[3] |= PART_SYS_BYTE_DECAY_DAMP_MASK;
+ }
+
+ if(in->windDiffusionFactor[0] != mDefaults.windDiffusionFactor[0] ||
+ in->windDiffusionFactor[1] != mDefaults.windDiffusionFactor[1] ||
+ in->windDiffusionFactor[2] != mDefaults.windDiffusionFactor[2])
+ {
+ mData[3] |= PART_SYS_BYTE_WIND_DIFF_MASK;
+ }
+}
+
+F32 floatFromTwoBytes(S8 bMant, S8 bExp)
+{
+ F32 result = bMant;
+ while(bExp > 0)
+ {
+ result *= 2.0f;
+ bExp--;
+ }
+ while(bExp < 0)
+ {
+ result *= 0.5f;
+ bExp++;
+ }
+ return result;
+}
+
+void twoBytesFromFloat(F32 fIn, S8 &bMant, S8 &bExp)
+{
+ bExp = 0;
+ if(fIn > 127.0f)
+ {
+ fIn = 127.0f;
+ }
+ if(fIn < -127.0f)
+ {
+ fIn = -127.0f;
+ }
+ while(fIn < 64 && fIn > -64 && bExp > -127)
+ {
+ fIn *= 2.0f;
+ bExp--;
+ }
+ while((fIn > 128 || fIn < -128) && bExp < 127)
+ {
+ fIn *= 0.5f;
+ bExp++;
+ }
+ bMant = (S8)fIn;
+}
+
+
+
+/*
+U32 LLPartSysCompressedPacket::writeK(LLPartInitData *in, U32 startByte)
+{
+ U32 i, kFlag, i_mod_eight;
+ S8 bMant, bExp;
+
+ kFlag = startByte;
+
+ startByte += 3; // 3 bytes contain enough room for 18 flag bits
+ mData[kFlag] = 0x00;
+// llprintline("In the writeK\n");
+
+ i_mod_eight = 0;
+ for(i = 0; i < 18; i++)
+ {
+ if(in->k[i] != mDefaults.k[i])
+ {
+
+ mData[kFlag] |= 1<<i_mod_eight;
+ twoBytesFromFloat(in->k[i], bMant, bExp);
+
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ i_mod_eight++;
+ while(i_mod_eight >= 8)
+ {
+ kFlag++;
+ i_mod_eight -= 8;
+ }
+ }
+
+ return startByte;
+}*/
+
+U32 LLPartSysCompressedPacket::writeKill_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ twoBytesFromFloat(in->killPlaneNormal[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->killPlaneNormal[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->killPlaneNormal[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->killPlaneZ, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->distanceDeath, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeBounce_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ twoBytesFromFloat(in->bouncePlaneNormal[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->bouncePlaneNormal[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->bouncePlaneNormal[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+
+ twoBytesFromFloat(in->bouncePlaneZ, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeBounce_b(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ twoBytesFromFloat(in->bounce_b, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ return startByte;
+}
+
+//U32 LLPartSysCompressedPacket::writePos_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8) in->pos_ranges[i]; // float to int conversion (keep the sign)
+// mData[startByte++] = (U8)tmp; // signed to unsigned typecast
+// }
+// return startByte;
+//}
+
+//U32 LLPartSysCompressedPacket::writeVel_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8) in->vel_ranges[i]; // float to int conversion (keep the sign)
+// mData[startByte++] = (U8)tmp; // signed to unsigned typecast
+// }
+// return startByte;
+//}
+
+U32 LLPartSysCompressedPacket::writeAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->diffEqAlpha[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->diffEqScale[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeScale_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ twoBytesFromFloat(in->scale_range[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+
+U32 LLPartSysCompressedPacket::writeAlpha_range(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ twoBytesFromFloat(in->alpha_range[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeVelocityOffset(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->vel_offset[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeUUID(LLPartInitData *in, U32 startByte)
+{
+ U8 * bufPtr = mData + startByte;
+ if(in->mImageUuid == IMG_SHOT) {
+ mData[startByte++] = 0x01;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_SPARK) {
+ mData[startByte++] = 0x02;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_BIG_EXPLOSION_1) {
+ mData[startByte++] = 0x03;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_BIG_EXPLOSION_2) {
+ mData[startByte++] = 0x04;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_SMOKE_POOF) {
+ mData[startByte++] = 0x05;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_FIRE) {
+ mData[startByte++] = 0x06;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_EXPLOSION) {
+ mData[startByte++] = 0x07;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_EXPLOSION_2) {
+ mData[startByte++] = 0x08;
+ return startByte;
+ }
+
+
+ if(in->mImageUuid == IMG_EXPLOSION_3) {
+ mData[startByte++] = 0x09;
+ return startByte;
+ }
+
+ if(in->mImageUuid == IMG_EXPLOSION_4) {
+ mData[startByte++] = 0x0A;
+ return startByte;
+ }
+
+ mData[startByte++] = 0x00; // flag for "read whole UUID"
+
+ memcpy(bufPtr, in->mImageUuid.mData, 16); /* Flawfinder: ignore */
+ return (startByte+16);
+}
+
+U32 LLPartSysCompressedPacket::writeSpawn(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+
+ twoBytesFromFloat(in->spawnRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnFrequency, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnFreqencyRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+
+
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->spawnDirection[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+
+ twoBytesFromFloat(in->spawnDirectionRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnVelocity, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ twoBytesFromFloat(in->spawnVelocityRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeEnvironment(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+ int i;
+
+ twoBytesFromFloat(in->windWeight, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ for(i = 0; i < 3; i++)
+ {
+ twoBytesFromFloat(in->currentGravity[i], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ }
+
+ twoBytesFromFloat(in->gravityWeight, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeLifespan(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->globalLifetime, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->individualLifetime, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->individualLifetimeRange, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+
+U32 LLPartSysCompressedPacket::writeDecayDamp(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->speedLimit, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->alphaDecay, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->scaleDecay, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->dampMotionFactor, bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::writeWindDiffusionFactor(LLPartInitData *in, U32 startByte)
+{
+ S8 bExp, bMant;
+
+ twoBytesFromFloat(in->windDiffusionFactor[0], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->windDiffusionFactor[1], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ twoBytesFromFloat(in->windDiffusionFactor[2], bMant, bExp);
+ mData[startByte++] = bMant;
+ mData[startByte++] = bExp;
+
+ return startByte;
+}
+
+
+
+
+
+
+/*
+U32 LLPartSysCompressedPacket::readK(LLPartInitData *in, U32 startByte)
+{
+ U32 i, i_mod_eight, kFlag;
+ S8 bMant, bExp; // 1 bytes mantissa and exponent for a float
+ kFlag = startByte;
+ startByte += 3; // 3 bytes has enough room for 18 bits
+
+ i_mod_eight = 0;
+ for(i = 0; i < 18; i++)
+ {
+ if(mData[kFlag]&(1<<i_mod_eight))
+ {
+
+
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+
+
+ in->k[i] = floatFromTwoBytes(bMant, bExp); // much tighter platform-independent
+ // way to ship floats
+
+ }
+ i_mod_eight++;
+ if(i_mod_eight >= 8)
+ {
+ i_mod_eight -= 8;
+ kFlag++;
+ }
+ }
+
+ return startByte;
+}
+*/
+
+U32 LLPartSysCompressedPacket::readKill_p(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[0] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[1] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneNormal[2] = floatFromTwoBytes(bMant, bExp);
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->killPlaneZ = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->distanceDeath = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readBounce_p(LLPartInitData *in, U32 startByte)
+{
+
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[0] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[1] = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneNormal[2] = floatFromTwoBytes(bMant, bExp);
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bouncePlaneZ = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readBounce_b(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->bounce_b = floatFromTwoBytes(bMant, bExp);
+ return startByte;
+}
+
+
+//U32 LLPartSysCompressedPacket::readPos_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8)mData[startByte++];
+// in->pos_ranges[i] = tmp;
+// }
+// return startByte;
+//}
+
+//U32 LLPartSysCompressedPacket::readVel_ranges(LLPartInitData *in, U32 startByte)
+//{
+// S8 tmp;
+// int i;
+// for(i = 0; i < 6; i++)
+// {
+// tmp = (S8)mData[startByte++];
+// in->vel_ranges[i] = tmp;
+// }
+// return startByte;
+//}
+
+
+
+U32 LLPartSysCompressedPacket::readAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->diffEqAlpha[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->diffEqScale[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readAlpha_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 4; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->alpha_range[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readScale_range(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 4; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->scale_range[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readVelocityOffset(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->vel_offset[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readUUID(LLPartInitData *in, U32 startByte)
+{
+ U8 * bufPtr = mData + startByte;
+
+ if(mData[startByte] == 0x01)
+ {
+ in->mImageUuid = IMG_SHOT;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x02)
+ {
+ in->mImageUuid = IMG_SPARK;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x03)
+ {
+ in->mImageUuid = IMG_BIG_EXPLOSION_1;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x04)
+ {
+ in->mImageUuid = IMG_BIG_EXPLOSION_2;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x05)
+ {
+ in->mImageUuid = IMG_SMOKE_POOF;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x06)
+ {
+ in->mImageUuid = IMG_FIRE;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x07)
+ {
+ in->mImageUuid = IMG_EXPLOSION;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x08)
+ {
+ in->mImageUuid = IMG_EXPLOSION_2;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x09)
+ {
+ in->mImageUuid = IMG_EXPLOSION_3;
+ return startByte+1;
+ }
+ if(mData[startByte] == 0x0A)
+ {
+ in->mImageUuid = IMG_EXPLOSION_4;
+ return startByte+1;
+ }
+
+ startByte++; // cause we actually have to read the UUID now.
+ memcpy(in->mImageUuid.mData, bufPtr, 16); /* Flawfinder: ignore */
+ return (startByte+16);
+}
+
+
+
+
+U32 LLPartSysCompressedPacket::readSpawn(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ U32 i;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnRange = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnFrequency = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnFreqencyRange = floatFromTwoBytes(bMant, bExp);
+
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnDirection[i] = floatFromTwoBytes(bMant, bExp);
+ }
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnDirectionRange = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnVelocity = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->spawnVelocityRange = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readEnvironment(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+ U32 i;
+
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->windWeight = floatFromTwoBytes(bMant, bExp);
+
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->currentGravity[i] = floatFromTwoBytes(bMant, bExp);
+ }
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->gravityWeight = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readLifespan(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->globalLifetime = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->individualLifetime = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->individualLifetimeRange = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readDecayDamp(LLPartInitData *in, U32 startByte)
+{
+ S8 bMant, bExp;
+
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->speedLimit = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->alphaDecay = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->scaleDecay = floatFromTwoBytes(bMant, bExp);
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->dampMotionFactor = floatFromTwoBytes(bMant, bExp);
+
+ return startByte;
+}
+
+U32 LLPartSysCompressedPacket::readWindDiffusionFactor(LLPartInitData *in, U32 startByte)
+{
+ int i;
+ S8 bMant, bExp;
+ for(i = 0; i < 3; i++)
+ {
+ bMant = mData[startByte++];
+ bExp = mData[startByte++];
+ in->windDiffusionFactor[i] = floatFromTwoBytes(bMant, bExp);
+ }
+ return startByte;
+}
+
+BOOL LLPartSysCompressedPacket::fromLLPartInitData(LLPartInitData *in, U32 &bytesUsed)
+{
+
+ writeFlagByte(in);
+ U32 currByte = 4;
+
+// llprintline("calling \"fromLLPartInitData\"\n");
+
+ //if(mData[0] & PART_SYS_K_MASK)
+ //{
+ // currByte = writeK(in, 3); // first 3 bytes are reserved for header data
+ //}
+
+
+
+ if(mData[0] & PART_SYS_KILL_P_MASK)
+ {
+ currByte = writeKill_p(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_P_MASK)
+ {
+ currByte = writeBounce_p(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_B_MASK)
+ {
+ currByte = writeBounce_b(in, currByte);
+ }
+
+ //if(mData[0] & PART_SYS_POS_RANGES_MASK)
+ //{
+ // currByte = writePos_ranges(in, currByte);
+ //}
+
+ //if(mData[0] & PART_SYS_VEL_RANGES_MASK)
+ //{
+ // currByte = writeVel_ranges(in, currByte);
+ //}
+
+ if(mData[0] & PART_SYS_ALPHA_SCALE_DIFF_MASK)
+ {
+ currByte = writeAlphaScaleDiffEqn_range(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_SCALE_RANGE_MASK)
+ {
+ currByte = writeScale_range(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_VEL_OFFSET_MASK)
+ {
+ currByte = writeVelocityOffset(in, currByte);
+ }
+
+ if(mData[0] & PART_SYS_M_IMAGE_UUID_MASK)
+ {
+ currByte = writeUUID(in, currByte);
+ }
+
+
+ if(mData[3] & PART_SYS_BYTE_SPAWN_MASK)
+ {
+ currByte = writeSpawn(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_ENVIRONMENT_MASK)
+ {
+ currByte = writeEnvironment(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_LIFESPAN_MASK)
+ {
+ currByte = writeLifespan(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_DECAY_DAMP_MASK)
+ {
+ currByte = writeDecayDamp(in, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_WIND_DIFF_MASK)
+ {
+ currByte = writeWindDiffusionFactor(in, currByte);
+ }
+
+
+ if(mData[2] & PART_SYS_BYTE_3_ALPHA_MASK)
+ {
+ currByte = writeAlpha_range(in, currByte);
+ }
+
+ mData[currByte++] = (U8)in->maxParticles;
+ mData[currByte++] = (U8)in->initialParticles;
+
+
+ U32 flagFlag = 1; // flag indicating which flag bytes are non-zero
+ // yeah, I know, the name sounds funny
+ for(U32 i = 0; i < 8; i++)
+ {
+
+// llprintline("Flag \"%x\" gets byte \"%x\"\n", flagFlag, in->mFlags[i]);
+ if(mData[1] & flagFlag)
+ {
+ mData[currByte++] = in->mFlags[i];
+// llprintline("and is valid...\n");
+ }
+ flagFlag <<= 1;
+ }
+
+ bytesUsed = mNumBytes = currByte;
+
+
+
+// llprintline("returning from \"fromLLPartInitData\" with %d bytes\n", bytesUsed);
+
+ return TRUE;
+}
+
+BOOL LLPartSysCompressedPacket::toLLPartInitData(LLPartInitData *out, U32 *bytesUsed)
+{
+ U32 currByte = 4;
+
+ gSetInitDataDefaults(out);
+
+ if(mData[0] & PART_SYS_KILL_P_MASK)
+ {
+ currByte = readKill_p(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_P_MASK)
+ {
+ currByte = readBounce_p(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_BOUNCE_B_MASK)
+ {
+ currByte = readBounce_b(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_ALPHA_SCALE_DIFF_MASK)
+ {
+ currByte = readAlphaScaleDiffEqn_range(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_SCALE_RANGE_MASK)
+ {
+ currByte = readScale_range(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_VEL_OFFSET_MASK)
+ {
+ currByte = readVelocityOffset(out, currByte);
+ }
+
+ if(mData[0] & PART_SYS_M_IMAGE_UUID_MASK)
+ {
+ currByte = readUUID(out, currByte);
+ }
+
+
+ if(mData[3] & PART_SYS_BYTE_SPAWN_MASK)
+ {
+ currByte = readSpawn(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_ENVIRONMENT_MASK)
+ {
+ currByte = readEnvironment(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_LIFESPAN_MASK)
+ {
+ currByte = readLifespan(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_DECAY_DAMP_MASK)
+ {
+ currByte = readDecayDamp(out, currByte);
+ }
+
+ if(mData[3] & PART_SYS_BYTE_WIND_DIFF_MASK)
+ {
+ currByte = readWindDiffusionFactor(out, currByte);
+ }
+
+ if(mData[2] & PART_SYS_BYTE_3_ALPHA_MASK)
+ {
+ currByte = readAlpha_range(out, currByte);
+ }
+
+ out->maxParticles = mData[currByte++];
+ out->initialParticles = mData[currByte++];
+
+ U32 flagFlag = 1; // flag indicating which flag bytes are non-zero
+ // yeah, I know, the name sounds funny
+ for(U32 i = 0; i < 8; i++)
+ {
+ flagFlag = 1<<i;
+
+ if((mData[1] & flagFlag))
+ {
+ out->mFlags[i] = mData[currByte++];
+ }
+ }
+
+ *bytesUsed = currByte;
+ return TRUE;
+}
+
+BOOL LLPartSysCompressedPacket::fromUnsignedBytes(U8 *in, U32 bytesUsed)
+{
+ if ((in != NULL) && (bytesUsed <= sizeof(mData)))
+ {
+ memcpy(mData, in, bytesUsed);
+ mNumBytes = bytesUsed;
+ return TRUE;
+ }
+ else
+ {
+ llerrs << "NULL input data or number of bytes exceed mData size" << llendl;
+ return FALSE;
+ }
+}
+
+
+U32 LLPartSysCompressedPacket::bufferSize()
+{
+ return mNumBytes;
+}
+
+BOOL LLPartSysCompressedPacket::toUnsignedBytes(U8 *out)
+{
+ memcpy(out, mData, mNumBytes); /* Flawfinder: ignore */
+ return TRUE;
+}
+
+U8 * LLPartSysCompressedPacket::getBytePtr()
+{
+ return mData;
+}
+
+
diff --git a/indra/llmessage/partsyspacket.h b/indra/llmessage/partsyspacket.h
new file mode 100644
index 0000000000..c98eb42bfb
--- /dev/null
+++ b/indra/llmessage/partsyspacket.h
@@ -0,0 +1,243 @@
+/**
+ * @file partsyspacket.h
+ * @brief Object for packing particle system initialization parameters
+ * before sending them over the network
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PARTSYSPACKET_H
+#define LL_PARTSYSPACKET_H
+
+#include "lluuid.h"
+
+// Particle system stuff
+
+const U64 PART_SYS_MAX_TIME_IN_USEC = 1000000; // 1 second, die not quite near instantaneously
+
+
+// this struct is for particle system initialization parameters
+// I'm breaking some rules here, but I need a storage structure to hold initialization data
+// for these things. Sorry guys, they're not simple enough (yet) to avoid this cleanly
+struct LLPartInitData {
+ // please do not add functions to this class -- data only!
+ //F32 k[18]; // first 9 --> x,y,z last 9 --> scale, alpha, rot
+ //F32 kill_p[6]; // last one is for particles that die when they reach a spherical bounding radius
+ //F32 kill_plane[3];
+ //F32 bounce_p[5];
+ F32 bounce_b; // recently changed
+ // no need to store orientation and position here, as they're sent over seperately
+ //F32 pos_ranges[6];
+ //F32 vel_ranges[6];
+ F32 scale_range[4];
+ F32 alpha_range[4];
+ F32 vel_offset[3]; //new - more understandable!
+
+ F32 mDistBeginFadeout; // for fadeout LOD optimization
+ F32 mDistEndFadeout;
+
+ LLUUID mImageUuid;
+ //U8 n; // number of particles
+ U8 mFlags[8]; // for miscellaneous data --> its interpretation can change at my whim!
+ U8 createMe; // do I need to be created? or has the work allready been done?
+ //ActionFlag is now mFlags[PART_SYS_ACTION_BYTE]
+ //Spawn point is initially object creation center
+
+ F32 diffEqAlpha[3];
+ F32 diffEqScale[3];
+
+ U8 maxParticles;
+ //How many particles exist at any time within the system?
+ U8 initialParticles;
+ //How many particles exist when the system is created?
+ F32 killPlaneZ;
+ //For simplicity assume the XY plane, so this sets an altitude at which to die
+ F32 killPlaneNormal[3];
+ //Normal if not planar XY
+ F32 bouncePlaneZ;
+ //For simplicity assume the XY plane, so this sets an altitude at which to bounce
+ F32 bouncePlaneNormal[3];
+ //Normal if not planar XY
+ F32 spawnRange;
+ //Range of emission points about the mSpawnPoint
+ F32 spawnFrequency;
+ //Required if the system is to spawn new particles.
+ //This variable determines the time after a particle dies when it is respawned.
+ F32 spawnFreqencyRange;
+ //Determines the random range of time until a new particle is spawned.
+ F32 spawnDirection[3];
+ //Direction vector giving the mean direction in which particles are spawned
+ F32 spawnDirectionRange;
+ //Direction limiting the angular range of emissions about the mean direction. 1.0f means everywhere, 0.0f means uni-directional
+ F32 spawnVelocity;
+ //The mean speed at which particles are emitted
+ F32 spawnVelocityRange;
+ //The range of speeds about the mean at which particles are emitted.
+ F32 speedLimit;
+ //Used to constrain particle maximum velocity
+ F32 windWeight;
+ //How much of an effect does wind have
+ F32 currentGravity[3];
+ //Gravity direction used in update calculations
+ F32 gravityWeight;
+ //How much of an effect does gravity have
+ F32 globalLifetime;
+ //If particles re-spawn, a system can exist forever.
+ //If (ActionFlags & PART_SYS_GLOBAL_DIE) is TRUE this variable is used to determine how long the system lasts.
+ F32 individualLifetime;
+ //How long does each particle last if nothing else happens to it
+ F32 individualLifetimeRange;
+ //Range of variation in individual lifetimes
+ F32 alphaDecay;
+ //By what factor does alpha decrease as the lifetime of a particle is approached.
+ F32 scaleDecay;
+ //By what factor does scale decrease as the lifetime of a particle is approached.
+ F32 distanceDeath;
+ //With the increased functionality, particle systems can expand to indefinite size
+ //(e.g. wind can chaotically move particles into a wide spread).
+ //To avoid particles exceeding normal object size constraints,
+ //set the PART_SYS_DISTANCE_DEATH flag, and set a distance value here, representing a radius around the spawn point.
+ F32 dampMotionFactor;
+ //How much to damp motion
+ F32 windDiffusionFactor[3];
+ //Change the size and alpha of particles as wind speed increases (scale gets bigger, alpha smaller)
+};
+
+// constants for setting flag values
+// BYTES are in range 0-8, bits are in range 2^0 - 2^8 and can only be powers of two
+const int PART_SYS_NO_Z_BUFFER_BYTE = 0; // option to turn off z-buffer when rendering
+const int PART_SYS_NO_Z_BUFFER_BIT = 2; // particle systems --
+// I advise against using this, as it looks bad in every case I've tried
+
+const int PART_SYS_SLOW_ANIM_BYTE = 0; // slow animation down by a factor of 10
+const int PART_SYS_SLOW_ANIM_BIT = 1; // useful for tweaking anims during debugging
+
+const int PART_SYS_FOLLOW_VEL_BYTE = 0; // indicates whether to orient sprites towards
+const int PART_SYS_FOLLOW_VEL_BIT = 4; // their velocity vector -- default is FALSE
+
+const int PART_SYS_IS_LIGHT_BYTE = 0; // indicates whether a particular particle system
+const int PART_SYS_IS_LIGHT_BIT = 8; // is also a light object -- for andrew
+// should deprecate this once there is a general method for setting light properties of objects
+
+const int PART_SYS_SPAWN_COPY_BYTE = 0; // indicates whether to spawn baby particle systems on
+const int PART_SYS_SPAWN_COPY_BIT = 0x10; // particle death -- intended for smoke trails
+
+const int PART_SYS_COPY_VEL_BYTE = 0; // indicates whether baby particle systems inherit parents vel
+const int PART_SYS_COPY_VEL_BIT = 0x20; // (by default they don't)
+
+const int PART_SYS_INVISIBLE_BYTE = 0; // optional -- turn off display, just simulate
+const int PART_SYS_INVISIBLE_BIT = 0x40; // useful for smoke trails
+
+const int PART_SYS_ADAPT_TO_FRAMERATE_BYTE = 0; // drop sprites from render call proportionally
+const int PART_SYS_ADAPT_TO_FRAMERATE_BIT = 0x80; // to how far we are below 60 fps
+
+
+// 26 September 2001 - not even big enough to hold all changes, so should enlarge anyway
+//const U16 MAX_PART_SYS_PACKET_SIZE = 180;
+const U16 MAX_PART_SYS_PACKET_SIZE = 256;
+
+//const U8 PART_SYS_K_MASK = 0x01;
+const U8 PART_SYS_KILL_P_MASK = 0x02;
+const U8 PART_SYS_BOUNCE_P_MASK = 0x04;
+const U8 PART_SYS_BOUNCE_B_MASK = 0x08;
+//const U8 PART_SYS_POS_RANGES_MASK = 0x10;
+//const U8 PART_SYS_VEL_RANGES_MASK = 0x20;
+const U8 PART_SYS_VEL_OFFSET_MASK = 0x10; //re-use one of the original slots now commented out
+const U8 PART_SYS_ALPHA_SCALE_DIFF_MASK = 0x20; //re-use one of the original slots now commented out
+const U8 PART_SYS_SCALE_RANGE_MASK = 0x40;
+const U8 PART_SYS_M_IMAGE_UUID_MASK = 0x80;
+const U8 PART_SYS_BYTE_3_ALPHA_MASK = 0x01; // wrapped around, didn't we?
+
+const U8 PART_SYS_BYTE_SPAWN_MASK = 0x01;
+const U8 PART_SYS_BYTE_ENVIRONMENT_MASK = 0x02;
+const U8 PART_SYS_BYTE_LIFESPAN_MASK = 0x04;
+const U8 PART_SYS_BYTE_DECAY_DAMP_MASK = 0x08;
+const U8 PART_SYS_BYTE_WIND_DIFF_MASK = 0x10;
+
+
+// 26 September 2001 - new constants for mActionFlags
+const int PART_SYS_ACTION_BYTE = 1;
+const U8 PART_SYS_SPAWN = 0x01;
+const U8 PART_SYS_BOUNCE = 0x02;
+const U8 PART_SYS_AFFECTED_BY_WIND = 0x04;
+const U8 PART_SYS_AFFECTED_BY_GRAVITY = 0x08;
+const U8 PART_SYS_EVALUATE_WIND_PER_PARTICLE = 0x10;
+const U8 PART_SYS_DAMP_MOTION = 0x20;
+const U8 PART_SYS_WIND_DIFFUSION = 0x40;
+
+// 26 September 2001 - new constants for mKillFlags
+const int PART_SYS_KILL_BYTE = 2;
+const U8 PART_SYS_KILL_PLANE = 0x01;
+const U8 PART_SYS_GLOBAL_DIE = 0x02;
+const U8 PART_SYS_DISTANCE_DEATH = 0x04;
+const U8 PART_SYS_TIME_DEATH = 0x08;
+
+
+// global, because the sim-side also calls it in the LLPartInitDataFactory
+
+
+void gSetInitDataDefaults(LLPartInitData *setMe);
+
+class LLPartSysCompressedPacket
+{
+public:
+ LLPartSysCompressedPacket();
+ ~LLPartSysCompressedPacket();
+ BOOL fromLLPartInitData(LLPartInitData *in, U32 &bytesUsed);
+ BOOL toLLPartInitData(LLPartInitData *out, U32 *bytesUsed);
+ BOOL fromUnsignedBytes(U8 *in, U32 bytesUsed);
+ BOOL toUnsignedBytes(U8 *out);
+ U32 bufferSize();
+ U8 *getBytePtr();
+
+protected:
+ U8 mData[MAX_PART_SYS_PACKET_SIZE];
+ U32 mNumBytes;
+ LLPartInitData mDefaults; // this is intended to hold default LLPartInitData values
+ // please do not modify it
+ LLPartInitData mWorkingCopy; // uncompressed data I'm working with
+
+protected:
+ // private functions (used only to break up code)
+ void writeFlagByte(LLPartInitData *in);
+ //U32 writeK(LLPartInitData *in, U32 startByte);
+ U32 writeKill_p(LLPartInitData *in, U32 startByte);
+ U32 writeBounce_p(LLPartInitData *in, U32 startByte);
+ U32 writeBounce_b(LLPartInitData *in, U32 startByte);
+ //U32 writePos_ranges(LLPartInitData *in, U32 startByte);
+ //U32 writeVel_ranges(LLPartInitData *in, U32 startByte);
+ U32 writeAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte);
+ U32 writeScale_range(LLPartInitData *in, U32 startByte);
+ U32 writeAlpha_range(LLPartInitData *in, U32 startByte);
+ U32 writeUUID(LLPartInitData *in, U32 startByte);
+
+ U32 writeVelocityOffset(LLPartInitData *in, U32 startByte);
+ U32 writeSpawn(LLPartInitData *in, U32 startByte); //all spawn data
+ U32 writeEnvironment(LLPartInitData *in, U32 startByte); //wind and gravity
+ U32 writeLifespan(LLPartInitData *in, U32 startByte); //lifespan data - individual and global
+ U32 writeDecayDamp(LLPartInitData *in, U32 startByte); //alpha and scale, and motion damp
+ U32 writeWindDiffusionFactor(LLPartInitData *in, U32 startByte);
+
+
+ //U32 readK(LLPartInitData *in, U32 startByte);
+ U32 readKill_p(LLPartInitData *in, U32 startByte);
+ U32 readBounce_p(LLPartInitData *in, U32 startByte);
+ U32 readBounce_b(LLPartInitData *in, U32 startByte);
+ //U32 readPos_ranges(LLPartInitData *in, U32 startByte);
+ //U32 readVel_ranges(LLPartInitData *in, U32 startByte);
+ U32 readAlphaScaleDiffEqn_range(LLPartInitData *in, U32 startByte);
+ U32 readScale_range(LLPartInitData *in, U32 startByte);
+ U32 readAlpha_range(LLPartInitData *in, U32 startByte);
+ U32 readUUID(LLPartInitData *in, U32 startByte);
+
+ U32 readVelocityOffset(LLPartInitData *in, U32 startByte);
+ U32 readSpawn(LLPartInitData *in, U32 startByte); //all spawn data
+ U32 readEnvironment(LLPartInitData *in, U32 startByte); //wind and gravity
+ U32 readLifespan(LLPartInitData *in, U32 startByte); //lifespan data - individual and global
+ U32 readDecayDamp(LLPartInitData *in, U32 startByte); //alpha and scale, and motion damp
+ U32 readWindDiffusionFactor(LLPartInitData *in, U32 startByte);
+};
+
+#endif
+
diff --git a/indra/llmessage/patch_code.cpp b/indra/llmessage/patch_code.cpp
new file mode 100644
index 0000000000..c8ebac53e7
--- /dev/null
+++ b/indra/llmessage/patch_code.cpp
@@ -0,0 +1,390 @@
+/**
+ * @file patch_code.cpp
+ * @brief Encode patch DCT data into bitcode.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "bitpack.h"
+
+U32 gPatchSize, gWordBits;
+
+void init_patch_coding(LLBitPack &bitpack)
+{
+ bitpack.resetBitPacking();
+}
+
+void code_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp)
+{
+#ifdef LL_BIG_ENDIAN
+ U8 *stride = (U8 *)&gopp->stride;
+ bitpack.bitPack(&(stride[1]), 8);
+ bitpack.bitPack(&(stride[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&gopp->stride, 16);
+#endif
+ bitpack.bitPack((U8 *)&gopp->patch_size, 8);
+ bitpack.bitPack((U8 *)&gopp->layer_type, 8);
+
+ gPatchSize = gopp->patch_size;
+}
+
+void code_patch_header(LLBitPack &bitpack, LLPatchHeader *ph, S32 *patch)
+{
+ S32 i, j, temp, patch_size = gPatchSize, wbits = (ph->quant_wbits & 0xf) + 2;
+ U32 max_wbits = wbits + 5, min_wbits = wbits>>1;
+
+ wbits = min_wbits;
+
+ for (i = 0; i < (int) patch_size*patch_size; i++)
+ {
+ temp = patch[i];
+ if (temp)
+ {
+ if (temp < 0)
+ temp *= -1;
+ for (j = max_wbits; j > (int) min_wbits; j--)
+ {
+ if (temp & (1<<j))
+ {
+ if (j > wbits)
+ wbits = j;
+ break;
+ }
+ }
+ }
+ }
+
+ wbits += 1;
+
+ ph->quant_wbits &= 0xf0;
+
+ if ( (wbits > 17)
+ ||(wbits < 2))
+ {
+ llerrs << "Bits needed per word in code_patch_header out of legal range. Adjust compression quatization." << llendl;
+ }
+
+ ph->quant_wbits |= (wbits - 2);
+
+ bitpack.bitPack((U8 *)&ph->quant_wbits, 8);
+#ifdef LL_BIG_ENDIAN
+ U8 *offset = (U8 *)&ph->dc_offset;
+ bitpack.bitPack(&(offset[3]), 8);
+ bitpack.bitPack(&(offset[2]), 8);
+ bitpack.bitPack(&(offset[1]), 8);
+ bitpack.bitPack(&(offset[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&ph->dc_offset, 32);
+#endif
+#ifdef LL_BIG_ENDIAN
+ U8 *range = (U8 *)&ph->range;
+ bitpack.bitPack(&(range[1]), 8);
+ bitpack.bitPack(&(range[0]), 8);
+#else
+ bitpack.bitPack((U8 *)&ph->range, 16);
+#endif
+#ifdef LL_BIG_ENDIAN
+ U8 *ids = (U8 *)&ph->patchids;
+ bitpack.bitPack(&(ids[1]), 8);
+ bitpack.bitPack(&(ids[0]), 2);
+#else
+ bitpack.bitPack((U8 *)&ph->patchids, 10);
+#endif
+
+ gWordBits = wbits;
+}
+
+void code_end_of_data(LLBitPack &bitpack)
+{
+ bitpack.bitPack((U8 *)&END_OF_PATCHES, 8);
+}
+
+void code_patch(LLBitPack &bitpack, S32 *patch, S32 postquant)
+{
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ S32 temp;
+ BOOL b_eob;
+
+ if ( (postquant > patch_size*patch_size)
+ ||(postquant < 0))
+ {
+ llerrs << "Bad postquant in code_patch!" << llendl;
+ }
+
+ if (postquant)
+ patch[patch_size*patch_size - postquant] = 0;
+
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ b_eob = FALSE;
+ temp = patch[i];
+ if (!temp)
+ {
+ b_eob = TRUE;
+ for (j = i; j < patch_size*patch_size - postquant; j++)
+ {
+ if (patch[j])
+ {
+ b_eob = FALSE;
+ break;
+ }
+ }
+ if (b_eob)
+ {
+ bitpack.bitPack((U8 *)&ZERO_EOB, 2);
+ return;
+ }
+ else
+ {
+ bitpack.bitPack((U8 *)&ZERO_CODE, 1);
+ }
+ }
+ else
+ {
+ if (temp < 0)
+ {
+ temp *= -1;
+ if (temp > (1<<wbits))
+ {
+ temp = (1<<wbits);
+// printf("patch quatization exceeding allowable bits!");
+ }
+ bitpack.bitPack((U8 *)&NEGATIVE_VALUE, 3);
+ bitpack.bitPack((U8 *)&temp, wbits);
+ }
+ else
+ {
+ if (temp > (1<<wbits))
+ {
+ temp = (1<<wbits);
+// printf("patch quatization exceeding allowable bits!");
+ }
+ bitpack.bitPack((U8 *)&POSITIVE_VALUE, 3);
+ bitpack.bitPack((U8 *)&temp, wbits);
+ }
+ }
+ }
+}
+
+
+void end_patch_coding(LLBitPack &bitpack)
+{
+ bitpack.flushBitPack();
+}
+
+void init_patch_decoding(LLBitPack &bitpack)
+{
+ bitpack.resetBitPacking();
+}
+
+void decode_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp)
+{
+ U16 retvalu16;
+
+ retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ U8 *ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 16);
+#endif
+ gopp->stride = retvalu16;
+
+ U8 retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ gopp->patch_size = retvalu8;
+
+ retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ gopp->layer_type = retvalu8;
+
+ gPatchSize = gopp->patch_size;
+}
+
+void decode_patch_header(LLBitPack &bitpack, LLPatchHeader *ph)
+{
+ U8 retvalu8;
+
+ retvalu8 = 0;
+ bitpack.bitUnpack(&retvalu8, 8);
+ ph->quant_wbits = retvalu8;
+
+ if (END_OF_PATCHES == ph->quant_wbits)
+ {
+ // End of data, blitz the rest.
+ ph->dc_offset = 0;
+ ph->range = 0;
+ ph->patchids = 0;
+ return;
+ }
+
+ U32 retvalu32 = 0;
+#ifdef LL_BIG_ENDIAN
+ U8 *ret = (U8 *)&retvalu32;
+ bitpack.bitUnpack(&(ret[3]), 8);
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu32, 32);
+#endif
+ ph->dc_offset = *(F32 *)&retvalu32;
+
+ U16 retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 8);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 16);
+#endif
+ ph->range = retvalu16;
+
+ retvalu16 = 0;
+#ifdef LL_BIG_ENDIAN
+ ret = (U8 *)&retvalu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), 2);
+#else
+ bitpack.bitUnpack((U8 *)&retvalu16, 10);
+#endif
+ ph->patchids = retvalu16;
+
+ gWordBits = (ph->quant_wbits & 0xf) + 2;
+}
+
+void decode_patch(LLBitPack &bitpack, S32 *patches)
+{
+#ifdef LL_BIG_ENDIAN
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ U8 tempu8;
+ U16 tempu16;
+ U32 tempu32;
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // either 0 EOB or Value
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // value
+ bitpack.bitUnpack((U8 *)&tempu8, 1);
+ if (tempu8)
+ {
+ // negative
+ patches[i] = -1;
+ }
+ else
+ {
+ // positive
+ patches[i] = 1;
+ }
+ if (wbits <= 8)
+ {
+ bitpack.bitUnpack((U8 *)&tempu8, wbits);
+ patches[i] *= tempu8;
+ }
+ else if (wbits <= 16)
+ {
+ tempu16 = 0;
+ U8 *ret = (U8 *)&tempu16;
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 8);
+ patches[i] *= tempu16;
+ }
+ else if (wbits <= 24)
+ {
+ tempu32 = 0;
+ U8 *ret = (U8 *)&tempu32;
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 16);
+ patches[i] *= tempu32;
+ }
+ else if (wbits <= 32)
+ {
+ tempu32 = 0;
+ U8 *ret = (U8 *)&tempu32;
+ bitpack.bitUnpack(&(ret[3]), 8);
+ bitpack.bitUnpack(&(ret[2]), 8);
+ bitpack.bitUnpack(&(ret[1]), 8);
+ bitpack.bitUnpack(&(ret[0]), wbits - 24);
+ patches[i] *= tempu32;
+ }
+ }
+ else
+ {
+ for (j = i; j < patch_size*patch_size; j++)
+ {
+ patches[j] = 0;
+ }
+ return;
+ }
+ }
+ else
+ {
+ patches[i] = 0;
+ }
+ }
+#else
+ S32 i, j, patch_size = gPatchSize, wbits = gWordBits;
+ U32 temp;
+ for (i = 0; i < patch_size*patch_size; i++)
+ {
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // either 0 EOB or Value
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // value
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, 1);
+ if (temp)
+ {
+ // negative
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, wbits);
+ patches[i] = temp;
+ patches[i] *= -1;
+ }
+ else
+ {
+ // positive
+ temp = 0;
+ bitpack.bitUnpack((U8 *)&temp, wbits);
+ patches[i] = temp;
+ }
+ }
+ else
+ {
+ for (j = i; j < patch_size*patch_size; j++)
+ {
+ patches[j] = 0;
+ }
+ return;
+ }
+ }
+ else
+ {
+ patches[i] = 0;
+ }
+ }
+#endif
+}
+
diff --git a/indra/llmessage/patch_code.h b/indra/llmessage/patch_code.h
new file mode 100644
index 0000000000..a18736df11
--- /dev/null
+++ b/indra/llmessage/patch_code.h
@@ -0,0 +1,28 @@
+/**
+ * @file patch_code.h
+ * @brief Function declarations for encoding and decoding patches.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PATCH_CODE_H
+#define LL_PATCH_CODE_H
+
+class LLBitPack;
+class LLGroupHeader;
+class LLPatchHeader;
+
+void init_patch_coding(LLBitPack &bitpack);
+void code_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp);
+void code_patch_header(LLBitPack &bitpack, LLPatchHeader *ph, S32 *patch);
+void code_end_of_data(LLBitPack &bitpack);
+void code_patch(LLBitPack &bitpack, S32 *patch, S32 postquant);
+void end_patch_coding(LLBitPack &bitpack);
+
+void init_patch_decoding(LLBitPack &bitpack);
+void decode_patch_group_header(LLBitPack &bitpack, LLGroupHeader *gopp);
+void decode_patch_header(LLBitPack &bitpack, LLPatchHeader *ph);
+void decode_patch(LLBitPack &bitpack, S32 *patches);
+
+#endif
diff --git a/indra/llmessage/patch_dct.cpp b/indra/llmessage/patch_dct.cpp
new file mode 100644
index 0000000000..8d6969a2cd
--- /dev/null
+++ b/indra/llmessage/patch_dct.cpp
@@ -0,0 +1,751 @@
+/**
+ * @file patch_dct.cpp
+ * @brief DCT patch.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+
+typedef struct s_patch_compress_global_data
+{
+ S32 patch_size;
+ S32 patch_stride;
+ U32 charptr;
+ S32 layer_type;
+} PCGD;
+
+PCGD gPatchCompressGlobalData;
+
+void reset_patch_compressor(void)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+
+ pcp->charptr = 0;
+}
+
+S32 gCurrentSize = 0;
+
+F32 gPatchQuantizeTable[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_patch_quantize_table(S32 size)
+{
+ S32 i, j;
+ for (j = 0; j < size; j++)
+ {
+ for (i = 0; i < size; i++)
+ {
+ gPatchQuantizeTable[j*size + i] = 1.f/(1.f + 2.f*(i+j));
+ }
+ }
+}
+
+F32 gPatchCosines[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void setup_patch_cosines(S32 size)
+{
+ S32 n, u;
+ F32 oosob = F_PI*0.5f/size;
+
+ for (u = 0; u < size; u++)
+ {
+ for (n = 0; n < size; n++)
+ {
+ gPatchCosines[u*size+n] = cosf((2.f*n+1.f)*u*oosob);
+ }
+ }
+}
+
+S32 gCopyMatrix[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_copy_matrix(S32 size)
+{
+ S32 i, j, count;
+ BOOL b_diag = FALSE;
+ BOOL b_right = TRUE;
+
+ i = 0;
+ j = 0;
+ count = 0;
+
+ while ( (i < size)
+ &&(j < size))
+ {
+ gCopyMatrix[j*size + i] = count;
+
+ count++;
+
+ if (!b_diag)
+ {
+ if (b_right)
+ {
+ if (i < size - 1)
+ i++;
+ else
+ j++;
+ b_right = FALSE;
+ b_diag = TRUE;
+ }
+ else
+ {
+ if (j < size - 1)
+ j++;
+ else
+ i++;
+ b_right = TRUE;
+ b_diag = TRUE;
+ }
+ }
+ else
+ {
+ if (b_right)
+ {
+ i++;
+ j--;
+ if ( (i == size - 1)
+ ||(j == 0))
+ {
+ b_diag = FALSE;
+ }
+ }
+ else
+ {
+ i--;
+ j++;
+ if ( (i == 0)
+ ||(j == size - 1))
+ {
+ b_diag = FALSE;
+ }
+ }
+ }
+ }
+}
+
+
+void init_patch_compressor(S32 patch_size, S32 patch_stride, S32 layer_type)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+
+ pcp->charptr = 0;
+
+ pcp->patch_size = patch_size;
+ pcp->patch_stride = patch_stride;
+ pcp->layer_type = layer_type;
+
+ if (patch_size != gCurrentSize)
+ {
+ gCurrentSize = patch_size;
+ build_patch_quantize_table(patch_size);
+ setup_patch_cosines(patch_size);
+ build_copy_matrix(patch_size);
+ }
+}
+
+void prescan_patch(F32 *patch, LLPatchHeader *php, F32 &zmax, F32 &zmin)
+{
+ S32 i, j;
+ PCGD *pcp = &gPatchCompressGlobalData;
+ S32 stride = pcp->patch_stride;
+ S32 size = pcp->patch_size;
+ S32 jstride;
+
+ zmax = -99999999.f;
+ zmin = 99999999.f;
+
+ for (j = 0; j < size; j++)
+ {
+ jstride = j*stride;
+ for (i = 0; i < size; i++)
+ {
+ if (*(patch + jstride + i) > zmax)
+ {
+ zmax = *(patch + jstride + i);
+ }
+ if (*(patch + jstride + i) < zmin)
+ {
+ zmin = *(patch + jstride + i);
+ }
+ }
+ }
+
+ php->dc_offset = zmin;
+ php->range = (U16) ((zmax - zmin) + 1.f);
+}
+
+void dct_line(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 u;
+ F32 total;
+ F32 *pcp = gPatchCosines;
+ S32 line_size = line*NORMAL_PATCH_SIZE;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+
+ tlinein = linein + line_size;
+
+ total = *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein);
+
+ *(lineout + line_size) = OO_SQRT2*total;
+
+ for (u = 1; u < NORMAL_PATCH_SIZE; u++)
+ {
+ tlinein = linein + line_size;
+ tpcp = pcp + (u<<4);
+
+ total = *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein)*(*tpcp);
+
+ *(lineout + line_size + u) = total;
+ }
+#else
+ S32 n;
+ S32 size = gPatchCompressGlobalData.patch_size;
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[line_size + n];
+ }
+ lineout[line_size] = OO_SQRT2*total;
+
+ for (u = 1; u < size; u++)
+ {
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[line_size + n]*pcp[u*size+n];
+ }
+ lineout[line_size + u] = total;
+ }
+#endif
+}
+
+void dct_line_large(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 u;
+ F32 total;
+ F32 *pcp = gPatchCosines;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+
+ F32 *tlinein, *tpcp;
+
+ tlinein = linein + line_size;
+
+ total = *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein++);
+ total += *(tlinein);
+
+ *(lineout + line_size) = OO_SQRT2*total;
+
+ for (u = 1; u < LARGE_PATCH_SIZE; u++)
+ {
+ tlinein = linein + line_size;
+ tpcp = pcp + (u<<5);
+
+ total = *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein++)*(*(tpcp++));
+ total += *(tlinein)*(*tpcp);
+
+ *(lineout + line_size + u) = total;
+ }
+}
+
+inline void dct_column(F32 *linein, S32 *lineout, S32 column)
+{
+ S32 u;
+ F32 total;
+ F32 oosob = 2.f/16.f;
+ F32 *pcp = gPatchCosines;
+ S32 *copy_matrix = gCopyMatrix;
+ F32 *qt = gPatchQuantizeTable;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+ S32 sizeu;
+
+ tlinein = linein + column;
+
+ total = *(tlinein);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+ total += *(tlinein += NORMAL_PATCH_SIZE);
+
+ *(lineout + *(copy_matrix + column)) = (S32)(OO_SQRT2*total*oosob*(*(qt + column)));
+
+ for (u = 1; u < NORMAL_PATCH_SIZE; u++)
+ {
+ tlinein = linein + column;
+ tpcp = pcp + (u<<4);
+
+ total = *(tlinein)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp));
+
+ sizeu = NORMAL_PATCH_SIZE*u + column;
+
+ *(lineout + *(copy_matrix + sizeu)) = (S32)(total*oosob*(*(qt+sizeu)));
+ }
+#else
+ S32 size = gPatchCompressGlobalData.patch_size;
+ F32 oosob = 2.f/size;
+ S32 n;
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[size*n + column];
+ }
+ lineout[copy_matrix[column]] = OO_SQRT2*total*oosob*qt[column];
+
+ for (u = 1; u < size; u++)
+ {
+ total = 0.f;
+ for (n = 0; n < size; n++)
+ {
+ total += linein[size*n + column]*pcp[u*size+n];
+ }
+ lineout[copy_matrix[size*u + column]] = total*oosob*qt[size*u + column];
+ }
+#endif
+}
+
+inline void dct_column_large(F32 *linein, S32 *lineout, S32 column)
+{
+ S32 u;
+ F32 total;
+ F32 oosob = 2.f/32.f;
+ F32 *pcp = gPatchCosines;
+ S32 *copy_matrix = gCopyMatrix;
+ F32 *qt = gPatchQuantizeTable;
+
+ F32 *tlinein, *tpcp;
+ S32 sizeu;
+
+ tlinein = linein + column;
+
+ total = *(tlinein);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+ total += *(tlinein += LARGE_PATCH_SIZE);
+
+ *(lineout + *(copy_matrix + column)) = (S32)(OO_SQRT2*total*oosob*(*(qt + column)));
+
+ for (u = 1; u < LARGE_PATCH_SIZE; u++)
+ {
+ tlinein = linein + column;
+ tpcp = pcp + (u<<5);
+
+ total = *(tlinein)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp++));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp));
+
+ sizeu = LARGE_PATCH_SIZE*u + column;
+
+ *(lineout + *(copy_matrix + sizeu)) = (S32)(total*oosob*(*(qt+sizeu)));
+ }
+}
+
+inline void dct_patch(F32 *block, S32 *cpatch)
+{
+ F32 temp[NORMAL_PATCH_SIZE*NORMAL_PATCH_SIZE];
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ dct_line(block, temp, 0);
+ dct_line(block, temp, 1);
+ dct_line(block, temp, 2);
+ dct_line(block, temp, 3);
+
+ dct_line(block, temp, 4);
+ dct_line(block, temp, 5);
+ dct_line(block, temp, 6);
+ dct_line(block, temp, 7);
+
+ dct_line(block, temp, 8);
+ dct_line(block, temp, 9);
+ dct_line(block, temp, 10);
+ dct_line(block, temp, 11);
+
+ dct_line(block, temp, 12);
+ dct_line(block, temp, 13);
+ dct_line(block, temp, 14);
+ dct_line(block, temp, 15);
+
+ dct_column(temp, cpatch, 0);
+ dct_column(temp, cpatch, 1);
+ dct_column(temp, cpatch, 2);
+ dct_column(temp, cpatch, 3);
+
+ dct_column(temp, cpatch, 4);
+ dct_column(temp, cpatch, 5);
+ dct_column(temp, cpatch, 6);
+ dct_column(temp, cpatch, 7);
+
+ dct_column(temp, cpatch, 8);
+ dct_column(temp, cpatch, 9);
+ dct_column(temp, cpatch, 10);
+ dct_column(temp, cpatch, 11);
+
+ dct_column(temp, cpatch, 12);
+ dct_column(temp, cpatch, 13);
+ dct_column(temp, cpatch, 14);
+ dct_column(temp, cpatch, 15);
+#else
+ S32 i;
+ S32 size = gPatchCompressGlobalData.patch_size;
+ for (i = 0; i < size; i++)
+ {
+ dct_line(block, temp, i);
+ }
+ for (i = 0; i < size; i++)
+ {
+ dct_column(temp, cpatch, i);
+ }
+#endif
+}
+
+inline void dct_patch_large(F32 *block, S32 *cpatch)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+ dct_line_large(block, temp, 0);
+ dct_line_large(block, temp, 1);
+ dct_line_large(block, temp, 2);
+ dct_line_large(block, temp, 3);
+
+ dct_line_large(block, temp, 4);
+ dct_line_large(block, temp, 5);
+ dct_line_large(block, temp, 6);
+ dct_line_large(block, temp, 7);
+
+ dct_line_large(block, temp, 8);
+ dct_line_large(block, temp, 9);
+ dct_line_large(block, temp, 10);
+ dct_line_large(block, temp, 11);
+
+ dct_line_large(block, temp, 12);
+ dct_line_large(block, temp, 13);
+ dct_line_large(block, temp, 14);
+ dct_line_large(block, temp, 15);
+
+ dct_line_large(block, temp, 16);
+ dct_line_large(block, temp, 17);
+ dct_line_large(block, temp, 18);
+ dct_line_large(block, temp, 19);
+
+ dct_line_large(block, temp, 20);
+ dct_line_large(block, temp, 21);
+ dct_line_large(block, temp, 22);
+ dct_line_large(block, temp, 23);
+
+ dct_line_large(block, temp, 24);
+ dct_line_large(block, temp, 25);
+ dct_line_large(block, temp, 26);
+ dct_line_large(block, temp, 27);
+
+ dct_line_large(block, temp, 28);
+ dct_line_large(block, temp, 29);
+ dct_line_large(block, temp, 30);
+ dct_line_large(block, temp, 31);
+
+ dct_column_large(temp, cpatch, 0);
+ dct_column_large(temp, cpatch, 1);
+ dct_column_large(temp, cpatch, 2);
+ dct_column_large(temp, cpatch, 3);
+
+ dct_column_large(temp, cpatch, 4);
+ dct_column_large(temp, cpatch, 5);
+ dct_column_large(temp, cpatch, 6);
+ dct_column_large(temp, cpatch, 7);
+
+ dct_column_large(temp, cpatch, 8);
+ dct_column_large(temp, cpatch, 9);
+ dct_column_large(temp, cpatch, 10);
+ dct_column_large(temp, cpatch, 11);
+
+ dct_column_large(temp, cpatch, 12);
+ dct_column_large(temp, cpatch, 13);
+ dct_column_large(temp, cpatch, 14);
+ dct_column_large(temp, cpatch, 15);
+
+ dct_column_large(temp, cpatch, 16);
+ dct_column_large(temp, cpatch, 17);
+ dct_column_large(temp, cpatch, 18);
+ dct_column_large(temp, cpatch, 19);
+
+ dct_column_large(temp, cpatch, 20);
+ dct_column_large(temp, cpatch, 21);
+ dct_column_large(temp, cpatch, 22);
+ dct_column_large(temp, cpatch, 23);
+
+ dct_column_large(temp, cpatch, 24);
+ dct_column_large(temp, cpatch, 25);
+ dct_column_large(temp, cpatch, 26);
+ dct_column_large(temp, cpatch, 27);
+
+ dct_column_large(temp, cpatch, 28);
+ dct_column_large(temp, cpatch, 29);
+ dct_column_large(temp, cpatch, 30);
+ dct_column_large(temp, cpatch, 31);
+}
+
+void compress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *php, S32 prequant)
+{
+ S32 i, j;
+ PCGD *pcp = &gPatchCompressGlobalData;
+ S32 stride = pcp->patch_stride;
+ S32 size = pcp->patch_size;
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock;
+ F32 *tpatch;
+
+ S32 wordsize = prequant;
+ F32 oozrange = 1.f/php->range;
+
+ F32 dc = php->dc_offset;
+
+ S32 range = (1<<prequant);
+ F32 premult = oozrange*range;
+// F32 sub = (F32)(1<<(prequant - 1));
+ F32 sub = (F32)(1<<(prequant - 1)) + dc*premult;
+
+ php->quant_wbits = wordsize - 2;
+ php->quant_wbits |= (prequant - 2)<<4;
+
+ for (j = 0; j < size; j++)
+ {
+ tblock = block + j*size;
+ tpatch = patch + j*stride;
+ for (i = 0; i < size; i++)
+ {
+// block[j*size + i] = (patch[j*stride + i] - dc)*premult - sub;
+ *(tblock++) = *(tpatch++)*premult - sub;
+ }
+ }
+
+ if (size == 16)
+ dct_patch(block, cpatch);
+ else
+ dct_patch_large(block, cpatch);
+}
+
+void get_patch_group_header(LLGroupHeader *gopp)
+{
+ PCGD *pcp = &gPatchCompressGlobalData;
+ gopp->stride = pcp->patch_stride;
+ gopp->patch_size = pcp->patch_size;
+ gopp->layer_type = pcp->layer_type;
+}
diff --git a/indra/llmessage/patch_dct.h b/indra/llmessage/patch_dct.h
new file mode 100644
index 0000000000..d5c0d067fc
--- /dev/null
+++ b/indra/llmessage/patch_dct.h
@@ -0,0 +1,73 @@
+/**
+ * @file patch_dct.h
+ * @brief Function declarations for DCT and IDCT routines
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PATCH_DCT_H
+#define LL_PATCH_DCT_H
+
+class LLVector3;
+
+// Code Values
+const U8 ZERO_CODE = 0x0;
+const U8 ZERO_EOB = 0x2;
+const U8 POSITIVE_VALUE = 0x6;
+const U8 NEGATIVE_VALUE = 0x7;
+
+const S8 NORMAL_PATCH_SIZE = 16;
+const S8 LARGE_PATCH_SIZE = 32;
+
+const U8 END_OF_PATCHES = 97;
+
+#define _PATCH_SIZE_16_AND_32_ONLY
+
+// Top level header for group of headers
+//typedef struct LL_Group_Header
+//{
+// U16 stride; // 2 = 2
+// U8 patch_size; // 1 = 3
+// U8 layer_type; // 1 = 4
+//} LLGroupHeader;
+
+class LLGroupHeader
+{
+public:
+ U16 stride; // 2 = 2
+ U8 patch_size; // 1 = 3
+ U8 layer_type; // 1 = 4
+};
+
+// Individual patch header
+
+//typedef struct LL_Patch_Header
+//{
+// F32 dc_offset; // 4 bytes
+// U16 range; // 2 = 7 ((S16) FP range (breaks if we need > 32K meters in 1 patch)
+// U8 quant_wbits; // 1 = 8 (upper 4 bits is quant - 2, lower 4 bits is word bits - 2)
+// U16 patchids; // 2 = 10 (actually only uses 10 bits, 5 for each)
+//} LLPatchHeader;
+class LLPatchHeader
+{
+public:
+ F32 dc_offset; // 4 bytes
+ U16 range; // 2 = 7 ((S16) FP range (breaks if we need > 32K meters in 1 patch)
+ U8 quant_wbits; // 1 = 8 (upper 4 bits is quant - 2, lower 4 bits is word bits - 2)
+ U16 patchids; // 2 = 10 (actually only uses 10 bits, 5 for each)
+};
+
+// Compression routines
+void init_patch_compressor(S32 patch_size, S32 patch_stride, S32 layer_type);
+void prescan_patch(F32 *patch, LLPatchHeader *php, F32 &zmax, F32 &zmin);
+void compress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *php, S32 prequant);
+void get_patch_group_header(LLGroupHeader *gopp);
+
+// Decompression routines
+void set_group_of_patch_header(LLGroupHeader *gopp);
+void init_patch_decompressor(S32 size);
+void decompress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *ph);
+void decompress_patchv(LLVector3 *v, S32 *cpatch, LLPatchHeader *ph);
+
+#endif
diff --git a/indra/llmessage/patch_idct.cpp b/indra/llmessage/patch_idct.cpp
new file mode 100644
index 0000000000..cfc52c551d
--- /dev/null
+++ b/indra/llmessage/patch_idct.cpp
@@ -0,0 +1,666 @@
+/**
+ * @file patch_idct.cpp
+ * @brief IDCT patch.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "patch_dct.h"
+
+LLGroupHeader *gGOPP;
+
+void set_group_of_patch_header(LLGroupHeader *gopp)
+{
+ gGOPP = gopp;
+}
+
+F32 gPatchDequantizeTable[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+void build_patch_dequantize_table(S32 size)
+{
+ S32 i, j;
+ for (j = 0; j < size; j++)
+ {
+ for (i = 0; i < size; i++)
+ {
+ gPatchDequantizeTable[j*size + i] = (1.f + 2.f*(i+j));
+ }
+ }
+}
+
+S32 gCurrentDeSize = 0;
+
+F32 gPatchICosines[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void setup_patch_icosines(S32 size)
+{
+ S32 n, u;
+ F32 oosob = F_PI*0.5f/size;
+
+ for (u = 0; u < size; u++)
+ {
+ for (n = 0; n < size; n++)
+ {
+ gPatchICosines[u*size+n] = cosf((2.f*n+1.f)*u*oosob);
+ }
+ }
+}
+
+S32 gDeCopyMatrix[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+void build_decopy_matrix(S32 size)
+{
+ S32 i, j, count;
+ BOOL b_diag = FALSE;
+ BOOL b_right = TRUE;
+
+ i = 0;
+ j = 0;
+ count = 0;
+
+ while ( (i < size)
+ &&(j < size))
+ {
+ gDeCopyMatrix[j*size + i] = count;
+
+ count++;
+
+ if (!b_diag)
+ {
+ if (b_right)
+ {
+ if (i < size - 1)
+ i++;
+ else
+ j++;
+ b_right = FALSE;
+ b_diag = TRUE;
+ }
+ else
+ {
+ if (j < size - 1)
+ j++;
+ else
+ i++;
+ b_right = TRUE;
+ b_diag = TRUE;
+ }
+ }
+ else
+ {
+ if (b_right)
+ {
+ i++;
+ j--;
+ if ( (i == size - 1)
+ ||(j == 0))
+ {
+ b_diag = FALSE;
+ }
+ }
+ else
+ {
+ i--;
+ j++;
+ if ( (i == 0)
+ ||(j == size - 1))
+ {
+ b_diag = FALSE;
+ }
+ }
+ }
+ }
+}
+
+void init_patch_decompressor(S32 size)
+{
+ if (size != gCurrentDeSize)
+ {
+ gCurrentDeSize = size;
+ build_patch_dequantize_table(size);
+ setup_patch_icosines(size);
+ build_decopy_matrix(size);
+ }
+}
+
+inline void idct_line(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 oosob = 2.f/16.f;
+ S32 line_size = line*NORMAL_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+
+
+ for (n = 0; n < NORMAL_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + line_size;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ *(lineout + line_size + n) = total*oosob;
+ }
+#else
+ F32 oosob = 2.f/size;
+ S32 size = gGOPP->patch_size;
+ S32 line_size = line*size;
+ S32 u;
+ for (n = 0; n < size; n++)
+ {
+ total = OO_SQRT2*linein[line_size];
+ for (u = 1; u < size; u++)
+ {
+ total += linein[line_size + u]*pcp[u*size+n];
+ }
+ lineout[line_size + n] = total*oosob;
+ }
+#endif
+}
+
+inline void idct_line_large_slow(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 oosob = 2.f/32.f;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + line_size;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(lineout + line_size + n) = total*oosob;
+ }
+}
+
+// Nota Bene: assumes that coefficients beyond 128 are 0!
+
+void idct_line_large(F32 *linein, F32 *lineout, S32 line)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 oosob = 2.f/32.f;
+ S32 line_size = line*LARGE_PATCH_SIZE;
+ F32 *tlinein, *tpcp;
+ F32 *baselinein = linein + line_size;
+ F32 *baselineout = lineout + line_size;
+
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp++;
+ tlinein = baselinein;
+
+ total = OO_SQRT2*(*(tlinein++));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein++)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein)*(*(tpcp));
+
+ *baselineout++ = total*oosob;
+ }
+}
+
+inline void idct_column(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ F32 *tlinein, *tpcp;
+
+ for (n = 0; n < NORMAL_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + column;
+
+ total = OO_SQRT2*(*tlinein);
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+ total += *(tlinein += NORMAL_PATCH_SIZE)*(*(tpcp += NORMAL_PATCH_SIZE));
+
+ *(lineout + (n<<4) + column) = total;
+ }
+
+#else
+ S32 size = gGOPP->patch_size;
+ S32 u;
+ S32 u_size;
+
+ for (n = 0; n < size; n++)
+ {
+ total = OO_SQRT2*linein[column];
+ for (u = 1; u < size; u++)
+ {
+ u_size = u*size;
+ total += linein[u_size + column]*pcp[u_size+n];
+ }
+ lineout[size*n + column] = total;
+ }
+#endif
+}
+
+inline void idct_column_large_slow(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 *tlinein, *tpcp;
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp + n;
+ tlinein = linein + column;
+
+ total = OO_SQRT2*(*tlinein);
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(lineout + (n<<5) + column) = total;
+ }
+}
+
+// Nota Bene: assumes that coefficients beyond 128 are 0!
+
+void idct_column_large(F32 *linein, F32 *lineout, S32 column)
+{
+ S32 n, m;
+ F32 total;
+ F32 *pcp = gPatchICosines;
+
+ F32 *tlinein, *tpcp;
+ F32 *baselinein = linein + column;
+ F32 *baselineout = lineout + column;
+
+ for (n = 0; n < LARGE_PATCH_SIZE; n++)
+ {
+ tpcp = pcp++;
+ tlinein = baselinein;
+
+ total = OO_SQRT2*(*tlinein);
+ for (m = 1; m < NORMAL_PATCH_SIZE; m++)
+ total += *(tlinein += LARGE_PATCH_SIZE)*(*(tpcp += LARGE_PATCH_SIZE));
+
+ *(baselineout + (n<<5)) = total;
+ }
+}
+
+inline void idct_patch(F32 *block)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+#ifdef _PATCH_SIZE_16_AND_32_ONLY
+ idct_column(block, temp, 0);
+ idct_column(block, temp, 1);
+ idct_column(block, temp, 2);
+ idct_column(block, temp, 3);
+
+ idct_column(block, temp, 4);
+ idct_column(block, temp, 5);
+ idct_column(block, temp, 6);
+ idct_column(block, temp, 7);
+
+ idct_column(block, temp, 8);
+ idct_column(block, temp, 9);
+ idct_column(block, temp, 10);
+ idct_column(block, temp, 11);
+
+ idct_column(block, temp, 12);
+ idct_column(block, temp, 13);
+ idct_column(block, temp, 14);
+ idct_column(block, temp, 15);
+
+ idct_line(temp, block, 0);
+ idct_line(temp, block, 1);
+ idct_line(temp, block, 2);
+ idct_line(temp, block, 3);
+
+ idct_line(temp, block, 4);
+ idct_line(temp, block, 5);
+ idct_line(temp, block, 6);
+ idct_line(temp, block, 7);
+
+ idct_line(temp, block, 8);
+ idct_line(temp, block, 9);
+ idct_line(temp, block, 10);
+ idct_line(temp, block, 11);
+
+ idct_line(temp, block, 12);
+ idct_line(temp, block, 13);
+ idct_line(temp, block, 14);
+ idct_line(temp, block, 15);
+#else
+ S32 i;
+ S32 size = gGOPP->patch_size;
+ for (i = 0; i < size; i++)
+ {
+ idct_column(block, temp, i);
+ }
+ for (i = 0; i < size; i++)
+ {
+ idct_line(temp, block, i);
+ }
+#endif
+}
+
+inline void idct_patch_large(F32 *block)
+{
+ F32 temp[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+
+ idct_column_large_slow(block, temp, 0);
+ idct_column_large_slow(block, temp, 1);
+ idct_column_large_slow(block, temp, 2);
+ idct_column_large_slow(block, temp, 3);
+
+ idct_column_large_slow(block, temp, 4);
+ idct_column_large_slow(block, temp, 5);
+ idct_column_large_slow(block, temp, 6);
+ idct_column_large_slow(block, temp, 7);
+
+ idct_column_large_slow(block, temp, 8);
+ idct_column_large_slow(block, temp, 9);
+ idct_column_large_slow(block, temp, 10);
+ idct_column_large_slow(block, temp, 11);
+
+ idct_column_large_slow(block, temp, 12);
+ idct_column_large_slow(block, temp, 13);
+ idct_column_large_slow(block, temp, 14);
+ idct_column_large_slow(block, temp, 15);
+
+ idct_column_large_slow(block, temp, 16);
+ idct_column_large_slow(block, temp, 17);
+ idct_column_large_slow(block, temp, 18);
+ idct_column_large_slow(block, temp, 19);
+
+ idct_column_large_slow(block, temp, 20);
+ idct_column_large_slow(block, temp, 21);
+ idct_column_large_slow(block, temp, 22);
+ idct_column_large_slow(block, temp, 23);
+
+ idct_column_large_slow(block, temp, 24);
+ idct_column_large_slow(block, temp, 25);
+ idct_column_large_slow(block, temp, 26);
+ idct_column_large_slow(block, temp, 27);
+
+ idct_column_large_slow(block, temp, 28);
+ idct_column_large_slow(block, temp, 29);
+ idct_column_large_slow(block, temp, 30);
+ idct_column_large_slow(block, temp, 31);
+
+ idct_line_large_slow(temp, block, 0);
+ idct_line_large_slow(temp, block, 1);
+ idct_line_large_slow(temp, block, 2);
+ idct_line_large_slow(temp, block, 3);
+
+ idct_line_large_slow(temp, block, 4);
+ idct_line_large_slow(temp, block, 5);
+ idct_line_large_slow(temp, block, 6);
+ idct_line_large_slow(temp, block, 7);
+
+ idct_line_large_slow(temp, block, 8);
+ idct_line_large_slow(temp, block, 9);
+ idct_line_large_slow(temp, block, 10);
+ idct_line_large_slow(temp, block, 11);
+
+ idct_line_large_slow(temp, block, 12);
+ idct_line_large_slow(temp, block, 13);
+ idct_line_large_slow(temp, block, 14);
+ idct_line_large_slow(temp, block, 15);
+
+ idct_line_large_slow(temp, block, 16);
+ idct_line_large_slow(temp, block, 17);
+ idct_line_large_slow(temp, block, 18);
+ idct_line_large_slow(temp, block, 19);
+
+ idct_line_large_slow(temp, block, 20);
+ idct_line_large_slow(temp, block, 21);
+ idct_line_large_slow(temp, block, 22);
+ idct_line_large_slow(temp, block, 23);
+
+ idct_line_large_slow(temp, block, 24);
+ idct_line_large_slow(temp, block, 25);
+ idct_line_large_slow(temp, block, 26);
+ idct_line_large_slow(temp, block, 27);
+
+ idct_line_large_slow(temp, block, 28);
+ idct_line_large_slow(temp, block, 29);
+ idct_line_large_slow(temp, block, 30);
+ idct_line_large_slow(temp, block, 31);
+}
+
+S32 gDitherNoise = 128;
+
+void decompress_patch(F32 *patch, S32 *cpatch, LLPatchHeader *ph)
+{
+ S32 i, j;
+
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock = block;
+ F32 *tpatch;
+
+ LLGroupHeader *gopp = gGOPP;
+ S32 size = gopp->patch_size;
+ F32 range = ph->range;
+ S32 prequant = (ph->quant_wbits >> 4) + 2;
+ S32 quantize = 1<<prequant;
+ F32 hmin = ph->dc_offset;
+ S32 stride = gopp->stride;
+
+ F32 ooq = 1.f/(F32)quantize;
+ F32 *dq = gPatchDequantizeTable;
+ S32 *decopy_matrix = gDeCopyMatrix;
+
+ F32 mult = ooq*range;
+ F32 addval = mult*(F32)(1<<(prequant - 1))+hmin;
+
+ for (i = 0; i < size*size; i++)
+ {
+ *(tblock++) = *(cpatch + *(decopy_matrix++))*(*dq++);
+ }
+
+ if (size == 16)
+ {
+ idct_patch(block);
+ }
+ else
+ {
+ idct_patch_large(block);
+ }
+
+ for (j = 0; j < size; j++)
+ {
+ tpatch = patch + j*stride;
+ tblock = block + j*size;
+ for (i = 0; i < size; i++)
+ {
+ *(tpatch++) = *(tblock++)*mult+addval;
+ }
+ }
+}
+
+
+void decompress_patchv(LLVector3 *v, S32 *cpatch, LLPatchHeader *ph)
+{
+ S32 i, j;
+
+ F32 block[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE], *tblock = block;
+ LLVector3 *tvec;
+
+ LLGroupHeader *gopp = gGOPP;
+ S32 size = gopp->patch_size;
+ F32 range = ph->range;
+ S32 prequant = (ph->quant_wbits >> 4) + 2;
+ S32 quantize = 1<<prequant;
+ F32 hmin = ph->dc_offset;
+ S32 stride = gopp->stride;
+
+ F32 ooq = 1.f/(F32)quantize;
+ F32 *dq = gPatchDequantizeTable;
+ S32 *decopy_matrix = gDeCopyMatrix;
+
+ F32 mult = ooq*range;
+ F32 addval = mult*(F32)(1<<(prequant - 1))+hmin;
+
+// BOOL b_diag = FALSE;
+// BOOL b_right = TRUE;
+
+ for (i = 0; i < size*size; i++)
+ {
+ *(tblock++) = *(cpatch + *(decopy_matrix++))*(*dq++);
+ }
+
+ if (size == 16)
+ idct_patch(block);
+ else
+ idct_patch_large(block);
+
+ for (j = 0; j < size; j++)
+ {
+ tvec = v + j*stride;
+ tblock = block + j*size;
+ for (i = 0; i < size; i++)
+ {
+ (*tvec++).mV[VZ] = *(tblock++)*mult+addval;
+ }
+ }
+}
+
diff --git a/indra/llmessage/sound_ids.h b/indra/llmessage/sound_ids.h
new file mode 100644
index 0000000000..35c7f6e438
--- /dev/null
+++ b/indra/llmessage/sound_ids.h
@@ -0,0 +1,293 @@
+/**
+ * @file sound_ids.h
+ * @brief Temporary holder for sound IDs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SOUND_IDS_H
+#define LL_SOUND_IDS_H
+
+#include "lluuid.h"
+
+const LLUUID SND_NULL = LLUUID::null;
+const LLUUID SND_RIDE ("00000000-0000-0000-0000-000000000100");
+const LLUUID SND_SHOT ("00000000-0000-0000-0000-000000000101");
+const LLUUID SND_MORTAR ("00000000-0000-0000-0000-000000000102");
+const LLUUID SND_HIT ("00000000-0000-0000-0000-000000000103");
+const LLUUID SND_EXPLOSION ("00000000-0000-0000-0000-000000000104");
+const LLUUID SND_BOING ("00000000-0000-0000-0000-000000000105");
+const LLUUID SND_OBJECT_CREATE ("9f1bc096-3592-411e-9b0b-c447a9ff054c");
+
+//
+// Different bird sounds for different states
+//
+
+const LLUUID SND_CHIRP ("00000000-0000-0000-0000-000000000106"); // Flying random chirp
+const LLUUID SND_CHIRP2 ("828a9526-175b-455d-8af0-0e3c0fb602b2"); // Spooked by user
+const LLUUID SND_CHIRP3 ("f99772d6-1ce6-4a39-a28b-06d26c94c9e3"); // Spooked by object
+const LLUUID SND_CHIRP4 ("54472ca4-7fc9-42cb-b7d5-99ad5b12bd50"); // Chasing other bird
+const LLUUID SND_CHIRP5 ("2929964f-fac5-40d7-9179-2864a8fa9ace"); // Hopping random chirp
+const LLUUID SND_CHIRPDEAD ("9abff1d3-863a-4e04-bd83-3834fd7fcff4"); // Hit by grenade - dead!
+
+
+const LLUUID SND_MUNCH ("00000000-0000-0000-0000-000000000107");
+const LLUUID SND_PUNCH ("00000000-0000-0000-0000-000000000108");
+const LLUUID SND_SPLASH ("00000000-0000-0000-0000-000000000109");
+const LLUUID SND_CLICK ("00000000-0000-0000-0000-000000000110");
+const LLUUID SND_WHISTLE ("ab858f9a-1f44-4d39-9b33-351543d03ccb");
+const LLUUID SND_TYPING ("5e191c7b-8996-9ced-a177-b2ac32bfea06");
+
+const LLUUID SND_ARROW_SHOT ("00000000-0000-0000-0000-000000000111");
+const LLUUID SND_ARROW_THUD ("00000000-0000-0000-0000-000000000112");
+const LLUUID SND_LASER_SHOT ("00000000-0000-0000-0000-000000000113");
+const LLUUID SND_JET_THRUST ("67f5e4f0-0534-4d97-bc01-f297648d20e0");
+
+const LLUUID SND_SILENCE ("00000000-0000-0000-0000-000000000114");
+const LLUUID SND_BUBBLES ("00000000-0000-0000-0000-000000000115");
+const LLUUID SND_WELCOME ("00000000-0000-0000-0000-000000000116");
+const LLUUID SND_SQUISH ("00000000-0000-0000-0000-000000000117");
+const LLUUID SND_SUBPOD ("00000000-0000-0000-0000-000000000118");
+const LLUUID SND_FOOTSTEPS ("00000000-0000-0000-0000-000000000119");
+const LLUUID SND_STEP_LEFT ("00000000-0000-0000-0000-000000000124");
+const LLUUID SND_STEP_RIGHT ("00000000-0000-0000-0000-000000000125");
+
+const LLUUID SND_BALL_COLLISION ("00000000-0000-0000-0000-000000000120");
+
+const LLUUID SND_OOOH_SCARE_ME ("00000000-0000-0000-0000-000000000121");
+const LLUUID SND_PAYBACK_TIME ("00000000-0000-0000-0000-000000000122");
+const LLUUID SND_READY_FOR_BATTLE ("00000000-0000-0000-0000-000000000123");
+
+const LLUUID SND_FLESH_FLESH ("dce5fdd4-afe4-4ea1-822f-dd52cac46b08");
+const LLUUID SND_FLESH_PLASTIC ("51011582-fbca-4580-ae9e-1a5593f094ec");
+const LLUUID SND_FLESH_RUBBER ("68d62208-e257-4d0c-bbe2-20c9ea9760bb");
+const LLUUID SND_GLASS_FLESH ("75872e8c-bc39-451b-9b0b-042d7ba36cba");
+const LLUUID SND_GLASS_GLASS ("6a45ba0b-5775-4ea8-8513-26008a17f873");
+const LLUUID SND_GLASS_PLASTIC ("992a6d1b-8c77-40e0-9495-4098ce539694");
+const LLUUID SND_GLASS_RUBBER ("2de4da5a-faf8-46be-bac6-c4d74f1e5767");
+const LLUUID SND_GLASS_WOOD ("6e3fb0f7-6d9c-42ca-b86b-1122ff562d7d");
+const LLUUID SND_METAL_FLESH ("14209133-4961-4acc-9649-53fc38ee1667");
+const LLUUID SND_METAL_GLASS ("bc4a4348-cfcc-4e5e-908e-8a52a8915fe6");
+const LLUUID SND_METAL_METAL ("9e5c1297-6eed-40c0-825a-d9bcd86e3193");
+const LLUUID SND_METAL_PLASTIC ("e534761c-1894-4b61-b20c-658a6fb68157");
+const LLUUID SND_METAL_RUBBER ("8761f73f-6cf9-4186-8aaa-0948ed002db1");
+const LLUUID SND_METAL_WOOD ("874a26fd-142f-4173-8c5b-890cd846c74d");
+const LLUUID SND_PLASTIC_PLASTIC ("0e24a717-b97e-4b77-9c94-b59a5a88b2da");
+const LLUUID SND_RUBBER_PLASTIC ("75cf3ade-9a5b-4c4d-bb35-f9799bda7fb2");
+const LLUUID SND_RUBBER_RUBBER ("153c8bf7-fb89-4d89-b263-47e58b1b4774");
+const LLUUID SND_STONE_FLESH ("55c3e0ce-275a-46fa-82ff-e0465f5e8703");
+const LLUUID SND_STONE_GLASS ("24babf58-7156-4841-9a3f-761bdbb8e237");
+const LLUUID SND_STONE_METAL ("aca261d8-e145-4610-9e20-9eff990f2c12");
+const LLUUID SND_STONE_PLASTIC ("0642fba6-5dcf-4d62-8e7b-94dbb529d117");
+const LLUUID SND_STONE_RUBBER ("25a863e8-dc42-4e8a-a357-e76422ace9b5");
+const LLUUID SND_STONE_STONE ("9538f37c-456e-4047-81be-6435045608d4");
+const LLUUID SND_STONE_WOOD ("8c0f84c3-9afd-4396-b5f5-9bca2c911c20");
+const LLUUID SND_WOOD_FLESH ("be582e5d-b123-41a2-a150-454c39e961c8");
+const LLUUID SND_WOOD_PLASTIC ("c70141d4-ba06-41ea-bcbc-35ea81cb8335");
+const LLUUID SND_WOOD_RUBBER ("7d1826f4-24c4-4aac-8c2e-eff45df37783");
+const LLUUID SND_WOOD_WOOD ("063c97d3-033a-4e9b-98d8-05c8074922cb");
+
+
+const LLUUID SND_SLIDE_FLESH_FLESH ("614eec22-f73d-4fdc-8691-a37dc5c58333");
+const LLUUID SND_SLIDE_FLESH_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_FLESH_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_FLESH_FABRIC ("3678b9b9-2a0c-42b5-9c83-80b64ad6e898");
+const LLUUID SND_SLIDE_FLESH_GRAVEL ("02eaa42a-ce1a-4b6b-9c38-cd7ad0e8f4a6");
+const LLUUID SND_SLIDE_FLESH_GRAVEL_02 ("e7d3b501-79f8-4419-b842-ab6843e0f840");
+const LLUUID SND_SLIDE_FLESH_GRAVEL_03 ("4c3e8b52-6244-4e44-85a6-f4ab994418ed");
+const LLUUID SND_SLIDE_GLASS_GRAVEL ("ca491e77-5c47-4ea1-8021-b3ebbf636cab");
+const LLUUID SND_SLIDE_GLASS_GRAVEL_02 ("30794d49-91ce-48e3-a527-c06f67bd6cbe");
+const LLUUID SND_SLIDE_GLASS_GRAVEL_03 ("04c78e54-fd8d-46b6-8ab9-7678b5d6e5cb");
+const LLUUID SND_SLIDE_GLASS_FLESH (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_GLASS (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_GLASS_WOOD (SND_NULL);
+const LLUUID SND_SLIDE_METAL_FABRIC ("18b66e81-2958-42d4-a373-7a5054919adc");
+const LLUUID SND_SLIDE_METAL_FLESH ("dde65837-633c-4841-af2f-62ec471bf61e");
+const LLUUID SND_SLIDE_METAL_FLESH_02 ("f3cc2cbe-1a1a-4db7-a8d2-e9c8f8fa1f4f");
+const LLUUID SND_SLIDE_METAL_GLASS ("4188be39-7b1f-4495-bf2b-83ddd82eea05");
+const LLUUID SND_SLIDE_METAL_GLASS_02 ("336faa2b-9d96-4e14-93ad-b63b60074379");
+const LLUUID SND_SLIDE_METAL_GLASS_03 ("34d912aa-cf73-4462-b7d0-dcba2c66caba");
+const LLUUID SND_SLIDE_METAL_GLASS_04 ("97ffc063-e872-4469-8e95-1450ac6bad2b");
+const LLUUID SND_SLIDE_METAL_GRAVEL ("2bbff37d-009a-4cfc-9a0d-817652c08fbe");
+const LLUUID SND_SLIDE_METAL_GRAVEL_02 ("a906a228-783b-49e7-9f0a-e20a41d0e39f");
+const LLUUID SND_SLIDE_METAL_METAL ("09461277-c691-45de-b2c5-89dfd3712f79");
+const LLUUID SND_SLIDE_METAL_METAL_02 ("e00a5d97-8fdc-46c1-bd53-7e312727466c");
+const LLUUID SND_SLIDE_METAL_METAL_03 ("8ebfa780-c440-4b52-ab65-5edf3bc15bf1");
+const LLUUID SND_SLIDE_METAL_METAL_04 ("d6d03cb2-5b16-4e31-b7d4-2a81d2a0909b");
+const LLUUID SND_SLIDE_METAL_METAL_05 ("3a46f447-916e-47de-a1e5-95d1af46bd0f");
+const LLUUID SND_SLIDE_METAL_METAL_06 ("cd423231-e70d-4fd2-ad26-f1c6cf5f0610");
+const LLUUID SND_SLIDE_METAL_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_METAL_RUBBER ("12d97bc0-3c15-4744-b6bd-77d1316eb4f0");
+const LLUUID SND_SLIDE_METAL_WOOD ("4afb6926-a73f-4cb7-85d5-0f9a40107434");
+const LLUUID SND_SLIDE_METAL_WOOD_02 ("349970bf-187d-4bcb-b2cf-e7bb6581590f");
+const LLUUID SND_SLIDE_METAL_WOOD_03 ("64bf6e87-73d4-4cb4-84f7-55cecfd97cd3");
+const LLUUID SND_SLIDE_METAL_WOOD_04 ("0dc670a9-dbe8-41bc-b8ee-4d96d99219d5");
+const LLUUID SND_SLIDE_METAL_WOOD_05 ("6e3cc57b-c9aa-4829-86a1-8e82aeaccb47");
+const LLUUID SND_SLIDE_METAL_WOOD_06 ("c1237f4c-8c88-4da1-bfbc-2af26a8d9e5a");
+const LLUUID SND_SLIDE_METAL_WOOD_07 ("0e1ec243-063b-4dcb-a903-52b8dffed3d2");
+const LLUUID SND_SLIDE_METAL_WOOD_08 ("66736d0f-533d-4007-a8ee-0f27c2034126");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL ("35092c21-5c48-4b4d-a818-3cf240af2348");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_02("c37f5776-0020-47e8-89a0-c74cc6f5742d");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_03("d2fc8db6-2e66-464a-8ccb-f99b61ee4987");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_04("93cbdb10-6e82-4c0b-a547-7b3b79ac25f6");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_05("2f6d0542-fcd1-4264-a17b-f57bf5ebf402");
+const LLUUID SND_SLIDE_PLASTIC_GRAVEL_06("5b8887d4-3be2-45a0-b25d-85af3b1e6392");
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC_02 (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_PLASTIC_03 (SND_NULL);
+const LLUUID SND_SLIDE_PLASTIC_FABRIC ("7294d9ad-3e41-4373-992c-a9f21d5d66ad");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_02("58608ce1-f524-472f-b447-bbe6ce4a46e0");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_03("06ae285e-0b34-4ea6-84ab-9c6c31b414fc");
+const LLUUID SND_SLIDE_PLASTIC_FABRIC_04("211613db-0461-49bd-9554-5c14ad8b31f6");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC ("a98ffa5a-e48e-4f9d-9242-b9a3210ad84a");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC_02 ("d4136c40-eeaa-49c6-a982-8e5a16f5d93a");
+const LLUUID SND_SLIDE_RUBBER_PLASTIC_03 ("29ec0fb2-0b23-47b2-835b-c83cc7cf9fb0");
+const LLUUID SND_SLIDE_RUBBER_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_STONE_FLESH (SND_NULL);
+const LLUUID SND_SLIDE_STONE_GLASS (SND_NULL);
+const LLUUID SND_SLIDE_STONE_METAL (SND_NULL);
+const LLUUID SND_SLIDE_STONE_PLASTIC ("afd0bcc3-d41a-4572-9e7f-08a29eeb0b8a");
+const LLUUID SND_SLIDE_STONE_PLASTIC_02 ("881b720a-96cf-4128-bb98-5d87e03e93c7");
+const LLUUID SND_SLIDE_STONE_PLASTIC_03 ("293dac42-658a-4c5a-a7a2-6d4c5e5658b0");
+const LLUUID SND_SLIDE_STONE_RUBBER ("0724b946-6a3f-4eeb-bb50-0a3b33120974");
+const LLUUID SND_SLIDE_STONE_RUBBER_02 ("ada93d00-76e2-4bf1-9ad9-493727630717");
+const LLUUID SND_SLIDE_STONE_STONE ("ade766dc-2e75-4699-9b41-7c8e53d2b3f2");
+const LLUUID SND_SLIDE_STONE_STONE_02 ("66698375-6594-47b0-8046-c3973de1291d");
+const LLUUID SND_SLIDE_STONE_WOOD ("174ef324-ed50-4f65-9479-b4da580aeb3c");
+const LLUUID SND_SLIDE_STONE_WOOD_02 ("33d517fd-ff11-4d01-a7b5-0e3abf818dcf");
+const LLUUID SND_SLIDE_STONE_WOOD_03 ("1bac4b63-e6fd-4659-9761-991284cf4582");
+const LLUUID SND_SLIDE_STONE_WOOD_04 ("a7d28564-6821-4c01-a378-cde98fba7ba9");
+const LLUUID SND_SLIDE_WOOD_FABRIC ("22c58e74-22cd-4960-9ab7-5bf08ab824e5");
+const LLUUID SND_SLIDE_WOOD_FABRIC_02 ("0b0ed22e-4a0f-4617-a4cf-20d0f2b78ccc");
+const LLUUID SND_SLIDE_WOOD_FABRIC_03 ("42b80abb-9823-4b74-a210-326ccf23636a");
+const LLUUID SND_SLIDE_WOOD_FABRIC_04 ("8538298a-1e6b-4b69-a9ee-5e01e4a02b35");
+const LLUUID SND_SLIDE_WOOD_FLESH ("84b026f3-a11c-4366-aa7c-07edcd89b2bb");
+const LLUUID SND_SLIDE_WOOD_FLESH_02 ("2644191f-4848-47ba-8ba7-bddc0bfcb3da");
+const LLUUID SND_SLIDE_WOOD_FLESH_03 ("edb978e4-9be9-456f-b2fc-e8502bfe25be");
+const LLUUID SND_SLIDE_WOOD_FLESH_04 ("bf2b972e-f42a-46d7-b53e-5fca38f5bc61");
+const LLUUID SND_SLIDE_WOOD_GRAVEL ("d063bb4d-0eff-4403-a6cc-c6c6c073e624");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_02 ("511eb679-6d93-47fa-9141-c3ef9261c919");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_03 ("4ed1fd43-4707-4e5c-b7b7-21ec4e72c1ac");
+const LLUUID SND_SLIDE_WOOD_GRAVEL_04 ("99ea89b3-aa76-4b87-99c8-670365c6d8c3");
+const LLUUID SND_SLIDE_WOOD_PLASTIC ("505ca3c4-94a0-4e28-8fc1-ea72a428396b");
+const LLUUID SND_SLIDE_WOOD_PLASTIC_02 ("fc404011-df71-4ed0-8f22-b72bdd18f63c");
+const LLUUID SND_SLIDE_WOOD_PLASTIC_03 ("67dbe225-26df-4efa-8c8b-f1ef669fec45");
+const LLUUID SND_SLIDE_WOOD_RUBBER (SND_NULL);
+const LLUUID SND_SLIDE_WOOD_WOOD ("3079d569-b3e8-4df4-9e09-f0d4611213ef");
+const LLUUID SND_SLIDE_WOOD_WOOD_02 ("276b093d-dbcb-4279-a89e-a54b0b416af6");
+const LLUUID SND_SLIDE_WOOD_WOOD_03 ("c3f3ca5e-2768-4081-847f-247139310fdb");
+const LLUUID SND_SLIDE_WOOD_WOOD_04 ("f08d44b8-ff87-4a98-9561-c72f1f2fec81");
+const LLUUID SND_SLIDE_WOOD_WOOD_05 ("2d8a58cf-f139-4238-8503-27d334d05c85");
+const LLUUID SND_SLIDE_WOOD_WOOD_06 ("e157ebbd-b12d-4225-aa7c-d47b026a7687");
+const LLUUID SND_SLIDE_WOOD_WOOD_07 ("35e17956-e7b4-478c-b274-e37db8a166b2");
+const LLUUID SND_SLIDE_WOOD_WOOD_08 ("e606fc65-0643-4964-9979-ff964fa6a62c");
+
+
+const LLUUID SND_ROLL_FLESH_FLESH (SND_NULL);
+const LLUUID SND_ROLL_FLESH_PLASTIC ("89a0be4c-848d-4a6e-8886-298f56c2cff4");
+const LLUUID SND_ROLL_FLESH_PLASTIC_02 ("beb06343-1aa1-4af2-b320-5d2ec31c53b1");
+const LLUUID SND_ROLL_FLESH_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_GLASS_GRAVEL ("ba795c74-7e09-4572-b495-e09886a46b86");
+const LLUUID SND_ROLL_GLASS_GRAVEL_02 ("4c93c3b7-14cb-4d9b-a7df-628ad935f1f2");
+const LLUUID SND_ROLL_GLASS_FLESH (SND_NULL);
+const LLUUID SND_ROLL_GLASS_GLASS (SND_NULL);
+const LLUUID SND_ROLL_GLASS_PLASTIC (SND_NULL);
+const LLUUID SND_ROLL_GLASS_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_GLASS_WOOD ("d40b1f48-a061-4f6e-b18f-4326a3dd5c29");
+const LLUUID SND_ROLL_GLASS_WOOD_02 ("78cd407a-bb36-4163-ba09-20f2e6d9d44b");
+const LLUUID SND_ROLL_GRAVEL_GRAVEL ("c7354cc3-6df5-4738-8dbb-b28a6ac46a05");
+const LLUUID SND_ROLL_GRAVEL_GRAVEL_02 ("01d194c4-72a6-47df-81a5-8db430faff87");
+const LLUUID SND_ROLL_METAL_FABRIC ("ce6e6564-20fd-48e4-81e2-cd3f81c00a3e");
+const LLUUID SND_ROLL_METAL_FABRIC_02 ("fc4d0065-32f6-4bb0-9f3f-f4737eb27163");
+const LLUUID SND_ROLL_METAL_FLESH (SND_NULL);
+const LLUUID SND_ROLL_METAL_GLASS ("63d530bb-a41f-402b-aa1f-be6b11959809");
+const LLUUID SND_ROLL_METAL_GLASS_02 ("f62642c2-6db5-4faa-8b77-939067d837c3");
+const LLUUID SND_ROLL_METAL_GLASS_03 ("db5b5a15-2817-4cd7-9f0b-9ad49b5e52c8");
+const LLUUID SND_ROLL_METAL_GRAVEL ("447164e3-9646-4c1a-a16d-606892891466");
+const LLUUID SND_ROLL_METAL_METAL ("c3c22cf3-5d1f-4cc3-b4b5-708b9f65979c");
+const LLUUID SND_ROLL_METAL_METAL_02 ("d8386277-a1ea-460e-b6fd-bb285c323bf1");
+const LLUUID SND_ROLL_METAL_METAL_03 ("69ee1f02-f9cd-4c8b-aedd-39a2d6705680");
+const LLUUID SND_ROLL_METAL_METAL_04 ("5cc6b5fd-26ce-47ad-b21d-3a7c190dd375");
+const LLUUID SND_ROLL_METAL_PLASTIC ("c6a9bbf6-df15-4713-9f84-7237fce4051e");
+const LLUUID SND_ROLL_METAL_PLASTIC_01 ("0fedb59b-2dbb-4cec-b6cc-8559ec027749");
+const LLUUID SND_ROLL_METAL_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_METAL_WOOD ("1d76af57-01b1-4c73-9a1d-69523bfa50ea");
+const LLUUID SND_ROLL_METAL_WOOD_02 ("78aa4e71-8e7c-4b90-a561-3ebdc639f99b");
+const LLUUID SND_ROLL_METAL_WOOD_03 ("777d95bf-962f-48fa-93bf-8c1806557d72");
+const LLUUID SND_ROLL_METAL_WOOD_04 ("1833da76-45e2-4a8b-97da-d17413e056c9");
+const LLUUID SND_ROLL_METAL_WOOD_05 ("b13e1232-3d8d-42e9-92ec-b30f9f823962");
+const LLUUID SND_ROLL_PLASTIC_FABRIC ("616a1f03-209f-4c55-b264-83a000b6ef0a");
+const LLUUID SND_ROLL_PLASTIC_PLASTIC ("873f3d82-00b2-4082-9c69-7aef3461dba1");
+const LLUUID SND_ROLL_PLASTIC_PLASTIC_02 ("cc39879f-ebc8-4405-a4fc-8342f5bed31e");
+const LLUUID SND_ROLL_RUBBER_PLASTIC (SND_NULL);
+const LLUUID SND_ROLL_RUBBER_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_STONE_FLESH (SND_NULL);
+const LLUUID SND_ROLL_STONE_GLASS (SND_NULL);
+const LLUUID SND_ROLL_STONE_METAL (SND_NULL);
+const LLUUID SND_ROLL_STONE_PLASTIC ("155f65a8-cae7-476e-a58b-fd362be7fd0e");
+const LLUUID SND_ROLL_STONE_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_STONE_STONE ("67d56e3f-6ed5-4658-9418-14f020c38b11");
+const LLUUID SND_ROLL_STONE_STONE_02 ("43d99d10-d75b-4246-accf-4ceb2c909aa7");
+const LLUUID SND_ROLL_STONE_STONE_03 ("f04e83ff-eed7-4e99-8f45-eb97e4e1d3b7");
+const LLUUID SND_ROLL_STONE_STONE_04 ("10fcc5ad-fa89-48d6-b774-986b580c1efc");
+const LLUUID SND_ROLL_STONE_STONE_05 ("3d86f5a3-1a91-49d9-b99f-8521a7422497");
+const LLUUID SND_ROLL_STONE_WOOD ("53e46fb7-6c21-4fe1-bffe-0567475d48fa");
+const LLUUID SND_ROLL_STONE_WOOD_02 ("5eba8c9a-a014-4299-87f1-315c45ec795b");
+const LLUUID SND_ROLL_STONE_WOOD_03 ("ea6c05fc-6e9c-4526-8a20-bc47810bb549");
+const LLUUID SND_ROLL_STONE_WOOD_04 ("64618cbf-3f42-4728-8094-e77807545efb");
+const LLUUID SND_ROLL_WOOD_FLESH ("26ee185d-6fc3-49f8-89ba-51cab04cfc42");
+const LLUUID SND_ROLL_WOOD_FLESH_02 ("334faa25-1e80-4c99-b29f-4c9c2a3d079d");
+const LLUUID SND_ROLL_WOOD_FLESH_03 ("2f876626-4dce-4f71-a91e-a25302edfab7");
+const LLUUID SND_ROLL_WOOD_FLESH_04 ("d6877aac-07fc-4931-bcde-585f223802ad");
+const LLUUID SND_ROLL_WOOD_GRAVEL ("2a23ebb5-a4a2-4f1f-8d75-7384239354aa");
+const LLUUID SND_ROLL_WOOD_GRAVEL_02 ("208bf26d-f097-450c-95c4-9d26317c613c");
+const LLUUID SND_ROLL_WOOD_GRAVEL_03 ("a26ecaf4-92c6-4e32-9864-56b7c70cab8e");
+const LLUUID SND_ROLL_WOOD_PLASTIC ("71c1000a-9f16-4cc3-8ede-ec4aa3bf5723");
+const LLUUID SND_ROLL_WOOD_PLASTIC_02 ("7bc20ba6-1e6d-4eea-83ad-c5cc3ae0e409");
+const LLUUID SND_ROLL_WOOD_RUBBER (SND_NULL);
+const LLUUID SND_ROLL_WOOD_WOOD ("2cc8eec4-bb4a-4ba8-b783-71526ec708e8");
+const LLUUID SND_ROLL_WOOD_WOOD_02 ("0a1f8070-a11a-4b4c-b260-5ffb6acb0a5d");
+const LLUUID SND_ROLL_WOOD_WOOD_03 ("160bef64-da9c-4be8-b07b-a5060b501700");
+const LLUUID SND_ROLL_WOOD_WOOD_04 ("1c62ea16-cc60-48ed-829a-68b8f4cf0c1c");
+const LLUUID SND_ROLL_WOOD_WOOD_05 ("be9cc8fe-b920-4bf5-8924-453088cbc03f");
+const LLUUID SND_ROLL_WOOD_WOOD_06 ("a76cfe60-56b0-43b1-8f31-93e56947d78b");
+const LLUUID SND_ROLL_WOOD_WOOD_07 ("0c6aa481-b5bc-4573-ae83-8e16ff27e750");
+const LLUUID SND_ROLL_WOOD_WOOD_08 ("214ab2c7-871a-451b-b0db-4c5677199011");
+const LLUUID SND_ROLL_WOOD_WOOD_09 ("0086e4db-3ac6-4545-b414-6f359bedd9a5");
+
+const LLUUID SND_SLIDE_STONE_STONE_01 ("2a7dcbd1-d3e6-4767-8432-8322648e7b9d");
+
+const LLUUID SND_STONE_DIRT_01 ("97727335-392c-4338-ac4b-23a7883279c2");
+const LLUUID SND_STONE_DIRT_02 ("cbe75eb2-3375-41d8-9e3f-2ae46b4164ed");
+const LLUUID SND_STONE_DIRT_03 ("31e236ee-001b-4c8e-ad6c-c2074cb64357");
+const LLUUID SND_STONE_DIRT_04 ("c8091652-e04b-4a11-84ba-15dba06e7a1b");
+
+const LLUUID SND_STONE_STONE_02 ("ba4ef5ac-7435-4240-b826-c24ba8fa5a78");
+const LLUUID SND_STONE_STONE_04 ("ea296329-0f09-4993-af1b-e6784bab1dc9");
+
+
+
+// extra guids
+#if 0
+const LLUUID SND_ ("a839b8ac-b0af-4ba9-9fde-188754744e02");
+const LLUUID SND_ ("20165fa8-836f-4993-85dc-1529172dcd14");
+const LLUUID SND_ ("fba8e17b-a4b3-4693-9fce-c14800f8a349");
+const LLUUID SND_ ("2d48db8b-7260-4b02-ad2a-b2c6bee60e94");
+const LLUUID SND_ ("956d344b-1808-4d8b-88b1-cbc82b7a96a1");
+const LLUUID SND_ ("b8303cc6-f0b4-4c6f-a199-81f87aba342e");
+const LLUUID SND_ ("fbf7cd0c-bc8f-4cba-9c19-11f4dd03a06b");
+const LLUUID SND_ ("85047f7d-933a-4ce5-a7b5-34670243e1ab");
+const LLUUID SND_ ("0f81acf7-6a2e-4490-957f-c7b0eda00559");
+const LLUUID SND_ ("5631a6a1-79b4-4de8-bccf-1880b6882da1");
+const LLUUID SND_ ("43c87a6b-ffb2-437b-89a0-9deba890a4fc");
+const LLUUID SND_ ("58878d1d-3156-4d01-ac3c-0c4fb99f4d53");
+const LLUUID SND_ ("9a83f321-44bf-40f6-b006-46c085515345");
+const LLUUID SND_ ("ff144533-33ab-40f2-bac8-39c34699ecc4");
+const LLUUID SND_ ("09018e87-d52c-4cd5-9805-015f413319e7");
+const LLUUID SND_ ("17d4c057-7edd-401e-9589-d5b9fe981bf2");
+#endif
+
+#endif
diff --git a/indra/llprimitive/legacy_object_types.h b/indra/llprimitive/legacy_object_types.h
new file mode 100644
index 0000000000..57ace87e89
--- /dev/null
+++ b/indra/llprimitive/legacy_object_types.h
@@ -0,0 +1,59 @@
+/**
+ * @file legacy_object_types.h
+ * @brief Byte codes for basic object and primitive types
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LEGACY_OBJECT_TYPES_H
+#define LL_LEGACY_OBJECT_TYPES_H
+
+const S8 PLAYER = 'c';
+//const S8 BASIC_SHOT = 's';
+//const S8 BIG_SHOT = 'S';
+//const S8 TREE_SHOT = 'g';
+//const S8 PHYSICAL_BALL = 'b';
+
+const S8 TREE = 'T';
+const S8 TREE_NEW = 'R';
+//const S8 SPARK = 'p';
+//const S8 SMOKE = 'q';
+//const S8 BOX = 'x';
+//const S8 CYLINDER = 'y';
+//const S8 CONE = 'o';
+//const S8 SPHERE = 'h';
+//const S8 BIRD = 'r'; // ascii 114
+//const S8 ATOR = 'a';
+//const S8 ROCK = 'k';
+
+const S8 GRASS = 'd';
+const S8 PART_SYS = 'P';
+
+//const S8 ORACLE = 'O';
+//const S8 TEXTBUBBLE = 't'; // Text bubble to show communication
+//const S8 DEMON = 'M'; // Maxwell's demon for scarfing legacy_object_types.h
+//const S8 CUBE = 'f';
+//const S8 LSL_TEST = 'L';
+//const S8 PRISM = '1';
+//const S8 PYRAMID = '2';
+//const S8 TETRAHEDRON = '3';
+//const S8 HALF_CYLINDER = '4';
+//const S8 HALF_CONE = '5';
+//const S8 HALF_SPHERE = '6';
+
+const S8 PRIMITIVE_VOLUME = 'v';
+
+// Misc constants
+
+//const F32 AVATAR_RADIUS = 0.5f;
+//const F32 SHOT_RADIUS = 0.05f;
+//const F32 BIG_SHOT_RADIUS = 0.05f;
+//const F32 TREE_SIZE = 5.f;
+//const F32 BALL_SIZE = 4.f;
+
+//const F32 SHOT_VELOCITY = 100.f;
+//const F32 GRENADE_BLAST_RADIUS = 5.f;
+
+#endif
+
diff --git a/indra/llprimitive/llmaterialtable.cpp b/indra/llprimitive/llmaterialtable.cpp
new file mode 100644
index 0000000000..ebd6306284
--- /dev/null
+++ b/indra/llprimitive/llmaterialtable.cpp
@@ -0,0 +1,657 @@
+/**
+ * @file llmaterialtable.cpp
+ * @brief Table of material names and IDs for viewer
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmaterialtable.h"
+#include "material_codes.h"
+#include "sound_ids.h"
+#include "imageids.h"
+
+LLMaterialTable LLMaterialTable::basic(1);
+
+F32 const LLMaterialTable::DEFAULT_FRICTION = 0.5f;
+F32 const LLMaterialTable::DEFAULT_RESTITUTION = 0.4f;
+
+LLMaterialTable::LLMaterialTable()
+{
+}
+
+LLMaterialTable::LLMaterialTable(U8 isBasic)
+{
+ initBasicTable();
+}
+
+LLMaterialTable::~LLMaterialTable()
+{
+ if (mCollisionSoundMatrix)
+ {
+ delete [] mCollisionSoundMatrix;
+ mCollisionSoundMatrix = NULL;
+ }
+
+ if (mSlidingSoundMatrix)
+ {
+ delete [] mSlidingSoundMatrix;
+ mSlidingSoundMatrix = NULL;
+ }
+
+ if (mRollingSoundMatrix)
+ {
+ delete [] mRollingSoundMatrix;
+ mRollingSoundMatrix = NULL;
+ }
+
+ mMaterialInfoList.deleteAllData();
+}
+
+void LLMaterialTable::initBasicTable()
+{
+ add(LL_MCODE_STONE,"Stone", LL_DEFAULT_STONE_UUID);
+ add(LL_MCODE_METAL,"Metal", LL_DEFAULT_METAL_UUID);
+ add(LL_MCODE_GLASS,"Glass", LL_DEFAULT_GLASS_UUID);
+ add(LL_MCODE_WOOD,"Wood", LL_DEFAULT_WOOD_UUID);
+ add(LL_MCODE_FLESH,"Flesh", LL_DEFAULT_FLESH_UUID);
+ add(LL_MCODE_PLASTIC,"Plastic", LL_DEFAULT_PLASTIC_UUID);
+ add(LL_MCODE_RUBBER,"Rubber", LL_DEFAULT_RUBBER_UUID);
+ add(LL_MCODE_LIGHT,"Light", LL_DEFAULT_LIGHT_UUID);
+
+ // specify densities for these materials. . .
+ // these were taken from http://www.mcelwee.net/html/densities_of_various_materials.html
+
+ addDensity(LL_MCODE_STONE,30.f);
+ addDensity(LL_MCODE_METAL,50.f);
+ addDensity(LL_MCODE_GLASS,20.f);
+ addDensity(LL_MCODE_WOOD,10.f);
+ addDensity(LL_MCODE_FLESH,10.f);
+ addDensity(LL_MCODE_PLASTIC,5.f);
+ addDensity(LL_MCODE_RUBBER,0.5f); //
+ addDensity(LL_MCODE_LIGHT,20.f); //
+
+ // add damage and energy values
+ addDamageAndEnergy(LL_MCODE_STONE, 1.f, 1.f, 1.f); // concrete
+ addDamageAndEnergy(LL_MCODE_METAL, 1.f, 1.f, 1.f); // steel
+ addDamageAndEnergy(LL_MCODE_GLASS, 1.f, 1.f, 1.f); // borosilicate glass
+ addDamageAndEnergy(LL_MCODE_WOOD, 1.f, 1.f, 1.f); // southern pine
+ addDamageAndEnergy(LL_MCODE_FLESH, 1.f, 1.f, 1.f); // saltwater
+ addDamageAndEnergy(LL_MCODE_PLASTIC, 1.f, 1.f, 1.f); // HDPE
+ addDamageAndEnergy(LL_MCODE_RUBBER, 1.f, 1.f, 1.f); //
+ addDamageAndEnergy(LL_MCODE_LIGHT, 1.f, 1.f, 1.f); //
+
+ addFriction(LL_MCODE_STONE,0.8f); // concrete
+ addFriction(LL_MCODE_METAL,0.3f); // steel
+ addFriction(LL_MCODE_GLASS,0.2f); // borosilicate glass
+ addFriction(LL_MCODE_WOOD,0.6f); // southern pine
+ addFriction(LL_MCODE_FLESH,0.9f); // saltwater
+ addFriction(LL_MCODE_PLASTIC,0.4f); // HDPE
+ addFriction(LL_MCODE_RUBBER,0.9f); //
+ addFriction(LL_MCODE_LIGHT,0.2f); //
+
+ addRestitution(LL_MCODE_STONE,0.4f); // concrete
+ addRestitution(LL_MCODE_METAL,0.4f); // steel
+ addRestitution(LL_MCODE_GLASS,0.7f); // borosilicate glass
+ addRestitution(LL_MCODE_WOOD,0.5f); // southern pine
+ addRestitution(LL_MCODE_FLESH,0.3f); // saltwater
+ addRestitution(LL_MCODE_PLASTIC,0.7f); // HDPE
+ addRestitution(LL_MCODE_RUBBER,0.9f); //
+ addRestitution(LL_MCODE_LIGHT,0.7f); //
+
+ addShatterSound(LL_MCODE_STONE,LLUUID("ea296329-0f09-4993-af1b-e6784bab1dc9"));
+ addShatterSound(LL_MCODE_METAL,LLUUID("d1375446-1c4d-470b-9135-30132433b678"));
+ addShatterSound(LL_MCODE_GLASS,LLUUID("85cda060-b393-48e6-81c8-2cfdfb275351"));
+ addShatterSound(LL_MCODE_WOOD,LLUUID("6f00669f-15e0-4793-a63e-c03f62fee43a"));
+ addShatterSound(LL_MCODE_FLESH,LLUUID("2d8c6f51-149e-4e23-8413-93a379b42b67"));
+ addShatterSound(LL_MCODE_PLASTIC,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e"));
+ addShatterSound(LL_MCODE_RUBBER,LLUUID("212b6d1e-8d9c-4986-b3aa-f3c6df8d987d"));
+ addShatterSound(LL_MCODE_LIGHT,LLUUID("d55c7f3c-e1c3-4ddc-9eff-9ef805d9190e"));
+
+ // CollisionSounds
+ mCollisionSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
+ if (mCollisionSoundMatrix)
+ {
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_STONE_STONE);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_STONE_METAL);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_STONE_GLASS);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_STONE_WOOD);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_STONE_FLESH);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_STONE_PLASTIC);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_STONE_RUBBER);
+ addCollisionSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_STONE_PLASTIC);
+
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_METAL_METAL);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_METAL_GLASS);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_METAL_WOOD);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_METAL_FLESH);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_METAL_PLASTIC);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_METAL_PLASTIC);
+ addCollisionSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_METAL_RUBBER);
+
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_GLASS_GLASS);
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_GLASS_WOOD);
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_GLASS_FLESH);
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_GLASS_PLASTIC);
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_GLASS_RUBBER);
+ addCollisionSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_GLASS_PLASTIC);
+
+ addCollisionSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_WOOD_WOOD);
+ addCollisionSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_WOOD_FLESH);
+ addCollisionSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_WOOD_PLASTIC);
+ addCollisionSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_WOOD_RUBBER);
+ addCollisionSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_WOOD_PLASTIC);
+
+ addCollisionSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_FLESH_FLESH);
+ addCollisionSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_FLESH_PLASTIC);
+ addCollisionSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_FLESH_RUBBER);
+ addCollisionSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_FLESH_PLASTIC);
+
+ addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_RUBBER_RUBBER);
+ addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_RUBBER_PLASTIC);
+ addCollisionSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_RUBBER_PLASTIC);
+
+ addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_PLASTIC_PLASTIC);
+ addCollisionSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC);
+
+ addCollisionSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_PLASTIC_PLASTIC);
+ }
+
+ // Sliding Sounds
+ mSlidingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
+ if (mSlidingSoundMatrix)
+ {
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_SLIDE_STONE_STONE);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_SLIDE_STONE_WOOD);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_SLIDE_STONE_PLASTIC);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_RUBBER);
+ addSlidingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_SLIDE_STONE_PLASTIC);
+
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_METAL_METAL);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_SLIDE_METAL_GLASS);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_SLIDE_METAL_WOOD);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_METAL_FLESH);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_METAL_RUBBER);
+ addSlidingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addSlidingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_SLIDE_WOOD_WOOD);
+ addSlidingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_SLIDE_WOOD_FLESH);
+ addSlidingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_SLIDE_WOOD_PLASTIC);
+ addSlidingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_SLIDE_WOOD_PLASTIC);
+
+ addSlidingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_FLESH_FLESH);
+ addSlidingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_RUBBER_PLASTIC);
+ addSlidingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_RUBBER_PLASTIC);
+
+ addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addSlidingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addSlidingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+ }
+
+ // Rolling Sounds
+ mRollingSoundMatrix = new LLUUID[LL_MCODE_END*LL_MCODE_END];
+ if (mRollingSoundMatrix)
+ {
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_STONE, SND_ROLL_STONE_STONE);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_WOOD, SND_ROLL_STONE_WOOD);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_PLASTIC, SND_ROLL_STONE_PLASTIC);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_STONE, LL_MCODE_LIGHT, SND_ROLL_STONE_PLASTIC);
+
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_METAL, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_GLASS, SND_ROLL_METAL_GLASS);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_WOOD, SND_ROLL_METAL_WOOD);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_PLASTIC, SND_ROLL_METAL_WOOD);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_METAL, LL_MCODE_LIGHT, SND_ROLL_METAL_WOOD);
+
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_GLASS, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_WOOD, SND_ROLL_GLASS_WOOD);
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_GLASS, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addRollingSound(LL_MCODE_WOOD, LL_MCODE_WOOD, SND_ROLL_WOOD_WOOD);
+ addRollingSound(LL_MCODE_WOOD, LL_MCODE_FLESH, SND_ROLL_WOOD_FLESH);
+ addRollingSound(LL_MCODE_WOOD, LL_MCODE_PLASTIC, SND_ROLL_WOOD_PLASTIC);
+ addRollingSound(LL_MCODE_WOOD, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_WOOD, LL_MCODE_LIGHT, SND_ROLL_WOOD_PLASTIC);
+
+ addRollingSound(LL_MCODE_FLESH, LL_MCODE_FLESH, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_FLESH, LL_MCODE_PLASTIC, SND_ROLL_FLESH_PLASTIC);
+ addRollingSound(LL_MCODE_FLESH, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_FLESH, LL_MCODE_LIGHT, SND_ROLL_FLESH_PLASTIC);
+
+ addRollingSound(LL_MCODE_RUBBER, LL_MCODE_RUBBER, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_RUBBER, LL_MCODE_PLASTIC, SND_SLIDE_STONE_STONE_01);
+ addRollingSound(LL_MCODE_RUBBER, LL_MCODE_LIGHT, SND_SLIDE_STONE_STONE_01);
+
+ addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_PLASTIC, SND_ROLL_PLASTIC_PLASTIC);
+ addRollingSound(LL_MCODE_PLASTIC, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC);
+
+ addRollingSound(LL_MCODE_LIGHT, LL_MCODE_LIGHT, SND_ROLL_PLASTIC_PLASTIC);
+ }
+}
+
+BOOL LLMaterialTable::add(U8 mcode, char* name, const LLUUID &uuid)
+{
+ LLMaterialInfo *infop;
+
+ infop = new LLMaterialInfo(mcode,name,uuid);
+ if (!infop) return FALSE;
+
+ // Add at the end so the order in menus matches the order in this
+ // file. JNC 11.30.01
+ mMaterialInfoList.addDataAtEnd(infop);
+
+ return TRUE;
+}
+
+BOOL LLMaterialTable::addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
+{
+ if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
+ if (mcode != mcode2)
+ {
+ mCollisionSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLMaterialTable::addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
+{
+ if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
+ if (mcode != mcode2)
+ {
+ mSlidingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLMaterialTable::addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid)
+{
+ if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2] = uuid;
+ if (mcode != mcode2)
+ {
+ mRollingSoundMatrix[mcode2 * LL_MCODE_END + mcode] = uuid;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLMaterialTable::addShatterSound(U8 mcode, const LLUUID &uuid)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ infop->mShatterSoundID = uuid;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMaterialTable::addDensity(U8 mcode, const F32 &density)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ infop->mDensity = density;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMaterialTable::addRestitution(U8 mcode, const F32 &restitution)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ infop->mRestitution = restitution;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMaterialTable::addFriction(U8 mcode, const F32 &friction)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ infop->mFriction = friction;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMaterialTable::addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ infop->mHPModifier = hp_mod;
+ infop->mDamageModifier = damage_mod;
+ infop->mEPModifier = ep_mod;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+LLUUID LLMaterialTable::getDefaultTextureID(char* name)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (!strcmp(name, infop->mName))
+ {
+ return infop->mDefaultTextureID;
+ }
+ }
+
+ return LLUUID::null;
+}
+
+
+LLUUID LLMaterialTable::getDefaultTextureID(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mDefaultTextureID;
+ }
+ }
+
+ return LLUUID::null;
+}
+
+
+U8 LLMaterialTable::getMCode(const char* name)
+{
+ LLMaterialInfo *infop;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (!strcmp(name, infop->mName))
+ {
+ return infop->mMCode;
+ }
+ }
+
+ return 0;
+}
+
+
+char* LLMaterialTable::getName(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mName;
+ }
+ }
+
+ return NULL;
+}
+
+
+LLUUID LLMaterialTable::getCollisionSoundUUID(U8 mcode, U8 mcode2)
+{
+ mcode &= LL_MCODE_MASK;
+ mcode2 &= LL_MCODE_MASK;
+
+ //llinfos << "code 1: " << ((U32) mcode) << " code 2:" << ((U32) mcode2) << llendl;
+ if (mCollisionSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ return(mCollisionSoundMatrix[mcode * LL_MCODE_END + mcode2]);
+ }
+ else
+ {
+ //llinfos << "Null Sound" << llendl;
+ return(SND_NULL);
+ }
+}
+
+LLUUID LLMaterialTable::getSlidingSoundUUID(U8 mcode, U8 mcode2)
+{
+ mcode &= LL_MCODE_MASK;
+ mcode2 &= LL_MCODE_MASK;
+
+ if (mSlidingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ return(mSlidingSoundMatrix[mcode * LL_MCODE_END + mcode2]);
+ }
+ else
+ {
+ return(SND_NULL);
+ }
+}
+
+LLUUID LLMaterialTable::getRollingSoundUUID(U8 mcode, U8 mcode2)
+{
+ mcode &= LL_MCODE_MASK;
+ mcode2 &= LL_MCODE_MASK;
+
+ if (mRollingSoundMatrix && (mcode < LL_MCODE_END) && (mcode2 < LL_MCODE_END))
+ {
+ return(mRollingSoundMatrix[mcode * LL_MCODE_END + mcode2]);
+ }
+ else
+ {
+ return(SND_NULL);
+ }
+}
+
+LLUUID LLMaterialTable::getGroundCollisionSoundUUID(U8 mcode)
+{
+ // Create material appropriate sounds for collisions with the ground
+ // For now, simply return a single sound for all materials
+ return SND_STONE_DIRT_02;
+}
+
+LLUUID LLMaterialTable::getGroundSlidingSoundUUID(U8 mcode)
+{
+ // Create material-specific sound for sliding on ground
+ // For now, just return a single sound
+ return SND_SLIDE_STONE_STONE_01;
+}
+
+LLUUID LLMaterialTable::getGroundRollingSoundUUID(U8 mcode)
+{
+ // Create material-specific sound for rolling on ground
+ // For now, just return a single sound
+ return SND_SLIDE_STONE_STONE_01;
+}
+
+LLUUID LLMaterialTable::getCollisionParticleUUID(U8 mcode, U8 mcode2)
+{
+ // Returns an appropriate UUID to use as sprite at collision betweeen objects
+ // For now, just return a single image
+ return IMG_SHOT;
+}
+
+LLUUID LLMaterialTable::getGroundCollisionParticleUUID(U8 mcode)
+{
+ // Returns an appropriate
+ // For now, just return a single sound
+ return IMG_SMOKE_POOF;
+}
+
+
+F32 LLMaterialTable::getDensity(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mDensity;
+ }
+ }
+
+ return 0.f;
+}
+
+F32 LLMaterialTable::getRestitution(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mRestitution;
+ }
+ }
+
+ return LLMaterialTable::DEFAULT_RESTITUTION;
+}
+
+F32 LLMaterialTable::getFriction(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mFriction;
+ }
+ }
+
+ return LLMaterialTable::DEFAULT_FRICTION;
+}
+
+F32 LLMaterialTable::getHPMod(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mHPModifier;
+ }
+ }
+
+ return 1.f;
+}
+
+F32 LLMaterialTable::getDamageMod(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mDamageModifier;
+ }
+ }
+
+ return 1.f;
+}
+
+F32 LLMaterialTable::getEPMod(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mEPModifier;
+ }
+ }
+
+ return 1.f;
+}
+
+LLUUID LLMaterialTable::getShatterSoundUUID(U8 mcode)
+{
+ LLMaterialInfo *infop;
+
+ mcode &= LL_MCODE_MASK;
+ for (infop = mMaterialInfoList.getFirstData(); infop != NULL; infop = mMaterialInfoList.getNextData() )
+ {
+ if (mcode == infop->mMCode)
+ {
+ return infop->mShatterSoundID;
+ }
+ }
+
+ return SND_NULL;
+}
diff --git a/indra/llprimitive/llmaterialtable.h b/indra/llprimitive/llmaterialtable.h
new file mode 100644
index 0000000000..7146be54cf
--- /dev/null
+++ b/indra/llprimitive/llmaterialtable.h
@@ -0,0 +1,116 @@
+/**
+ * @file llmaterialtable.h
+ * @brief Table of material information for the viewer UI
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMATERIALTABLE_H
+#define LL_LLMATERIALTABLE_H
+
+#include "lluuid.h"
+#include "linked_lists.h"
+#include "llstring.h"
+
+const U32 LLMATERIAL_INFO_NAME_LENGTH = 256;
+
+class LLMaterialInfo
+{
+public:
+ U8 mMCode;
+ char mName[LLMATERIAL_INFO_NAME_LENGTH];
+ LLUUID mDefaultTextureID;
+ LLUUID mShatterSoundID;
+ F32 mDensity; // kg/m^3
+ F32 mFriction;
+ F32 mRestitution;
+
+ // damage and energy constants
+ F32 mHPModifier; // modifier on mass based HP total
+ F32 mDamageModifier; // modifier on KE based damage
+ F32 mEPModifier; // modifier on mass based EP total
+
+ LLMaterialInfo(U8 mcode, char* name, const LLUUID &uuid)
+ {
+ init(mcode,name,uuid);
+ };
+
+ void init(U8 mcode, char* name, const LLUUID &uuid)
+ {
+ mName[0] = 0;
+ mDensity = 1000.f; // default to 1000.0 (water)
+ mHPModifier = 1.f;
+ mDamageModifier = 1.f;
+ mEPModifier = 1.f;
+
+ mMCode = mcode;
+ if (name)
+ {
+ LLString::copy(mName,name,LLMATERIAL_INFO_NAME_LENGTH);
+ }
+ mDefaultTextureID = uuid;
+ };
+
+ ~LLMaterialInfo()
+ {
+ };
+
+};
+
+class LLMaterialTable
+{
+public:
+ LLLinkedList<LLMaterialInfo> mMaterialInfoList;
+ LLUUID *mCollisionSoundMatrix;
+ LLUUID *mSlidingSoundMatrix;
+ LLUUID *mRollingSoundMatrix;
+
+ static const F32 DEFAULT_FRICTION;
+ static const F32 DEFAULT_RESTITUTION;
+
+public:
+ LLMaterialTable();
+ LLMaterialTable(U8); // cheat with an overloaded constructor to build our basic table
+ ~LLMaterialTable();
+
+ void initBasicTable();
+
+ BOOL add(U8 mcode, char* name, const LLUUID &uuid);
+ BOOL addCollisionSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
+ BOOL addSlidingSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
+ BOOL addRollingSound(U8 mcode, U8 mcode2, const LLUUID &uuid);
+ BOOL addShatterSound(U8 mcode, const LLUUID &uuid);
+ BOOL addDensity(U8 mcode, const F32 &density);
+ BOOL addFriction(U8 mcode, const F32 &friction);
+ BOOL addRestitution(U8 mcode, const F32 &restitution);
+ BOOL addDamageAndEnergy(U8 mcode, const F32 &hp_mod, const F32 &damage_mod, const F32 &ep_mod);
+
+ LLUUID getDefaultTextureID(char* name); // LLUUID::null if not found
+ LLUUID getDefaultTextureID(U8 mcode); // LLUUID::null if not found
+ U8 getMCode(const char* name); // 0 if not found
+ char* getName(U8 mcode);
+
+ F32 getDensity(U8 mcode); // kg/m^3, 0 if not found
+ F32 getFriction(U8 mcode); // havok values
+ F32 getRestitution(U8 mcode); // havok values
+ F32 getHPMod(U8 mcode);
+ F32 getDamageMod(U8 mcode);
+ F32 getEPMod(U8 mcode);
+
+ LLUUID getCollisionSoundUUID(U8 mcode, U8 mcode2);
+ LLUUID getSlidingSoundUUID(U8 mcode, U8 mcode2);
+ LLUUID getRollingSoundUUID(U8 mcode, U8 mcode2);
+ LLUUID getShatterSoundUUID(U8 mcode); // LLUUID::null if not found
+
+ LLUUID getGroundCollisionSoundUUID(U8 mcode);
+ LLUUID getGroundSlidingSoundUUID(U8 mcode);
+ LLUUID getGroundRollingSoundUUID(U8 mcode);
+ LLUUID getCollisionParticleUUID(U8 mcode, U8 mcode2);
+ LLUUID getGroundCollisionParticleUUID(U8 mcode);
+
+ static LLMaterialTable basic;
+};
+
+#endif
+
diff --git a/indra/llprimitive/llprimitive.cpp b/indra/llprimitive/llprimitive.cpp
new file mode 100644
index 0000000000..fa8010eb6b
--- /dev/null
+++ b/indra/llprimitive/llprimitive.cpp
@@ -0,0 +1,1749 @@
+/**
+ * @file llprimitive.cpp
+ * @brief LLPrimitive base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "material_codes.h"
+#include "llerror.h"
+#include "message.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "legacy_object_types.h"
+#include "v4coloru.h"
+#include "llvolumemgr.h"
+#include "llstring.h"
+#include "lldatapacker.h"
+
+/**
+ * exported constants
+ */
+
+const F32 OBJECT_CUT_MIN = 0.f;
+const F32 OBJECT_CUT_MAX = 1.f;
+const F32 OBJECT_CUT_INC = 0.05f;
+const F32 OBJECT_MIN_CUT_INC = 0.02f;
+const F32 OBJECT_ROTATION_PRECISION = 0.05f;
+
+const F32 OBJECT_TWIST_MIN = -360.f;
+const F32 OBJECT_TWIST_MAX = 360.f;
+const F32 OBJECT_TWIST_INC = 18.f;
+
+// This is used for linear paths,
+// since twist is used in a slightly different manner.
+const F32 OBJECT_TWIST_LINEAR_MIN = -180.f;
+const F32 OBJECT_TWIST_LINEAR_MAX = 180.f;
+const F32 OBJECT_TWIST_LINEAR_INC = 9.f;
+
+const F32 OBJECT_MIN_HOLE_SIZE = 0.05f;
+const F32 OBJECT_MAX_HOLE_SIZE_X = 1.0f;
+const F32 OBJECT_MAX_HOLE_SIZE_Y = 0.5f;
+
+// Revolutions parameters.
+const F32 OBJECT_REV_MIN = 1.0f;
+const F32 OBJECT_REV_MAX = 4.0f;
+const F32 OBJECT_REV_INC = 0.1f;
+
+// lights
+const F32 LIGHT_MIN_RADIUS = 0.0f;
+const F32 LIGHT_DEFAULT_RADIUS = 5.0f;
+const F32 LIGHT_MAX_RADIUS = 20.0f;
+const F32 LIGHT_MIN_FALLOFF = 0.0f;
+const F32 LIGHT_DEFAULT_FALLOFF = 1.0f;
+const F32 LIGHT_MAX_FALLOFF = 2.0f;
+const F32 LIGHT_MIN_CUTOFF = 0.0f;
+const F32 LIGHT_DEFAULT_CUTOFF = 0.0f;
+const F32 LIGHT_MAX_CUTOFF = 180.f;
+
+// "Tension" => [0,10], increments of 0.1
+const F32 FLEXIBLE_OBJECT_MIN_TENSION = 0.0f;
+const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION = 1.0f;
+const F32 FLEXIBLE_OBJECT_MAX_TENSION = 10.0f;
+
+// "Drag" => [0,10], increments of 0.1
+const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION = 0.0f;
+const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION = 2.0f;
+const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION = 10.0f;
+
+// "Gravity" = [-10,10], increments of 0.1
+const F32 FLEXIBLE_OBJECT_MIN_GRAVITY = -10.0f;
+const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY = 0.3f;
+const F32 FLEXIBLE_OBJECT_MAX_GRAVITY = 10.0f;
+
+// "Wind" = [0,10], increments of 0.1
+const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY = 0.0f;
+const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY = 0.0f;
+const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY = 10.0f;
+
+// I'll explain later...
+const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE = 0.99f;
+
+const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH = 1.0f;
+const BOOL FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE = FALSE;
+const BOOL FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE = FALSE;
+
+
+//===============================================================
+LLPrimitive::LLPrimitive()
+{
+ mPrimitiveCode = 0;
+
+ mMaterial = LL_MCODE_STONE;
+ mVolumep = NULL;
+
+ mChanged = UNCHANGED;
+
+ mPosition.setVec(0.f,0.f,0.f);
+ mVelocity.setVec(0.f,0.f,0.f);
+ mAcceleration.setVec(0.f,0.f,0.f);
+
+ mRotation.loadIdentity();
+ mAngularVelocity.setVec(0.f,0.f,0.f);
+
+ mScale.setVec(1.f,1.f,1.f);
+
+ mNumTEs = 0;
+ mTextureList = NULL;
+}
+
+//===============================================================
+LLPrimitive::~LLPrimitive()
+{
+ if (mTextureList)
+ {
+ delete [] mTextureList;
+ mTextureList = NULL;
+ }
+
+ // Cleanup handled by volume manager
+ if (mVolumep)
+ {
+ gVolumeMgr->cleanupVolume(mVolumep);
+ }
+ mVolumep = NULL;
+}
+
+//===============================================================
+// static
+LLPrimitive *LLPrimitive::createPrimitive(LLPCode p_code)
+{
+ LLPrimitive *retval = new LLPrimitive();
+
+ if (retval)
+ {
+ retval->init(p_code);
+ }
+ else
+ {
+ llerrs << "primitive allocation failed" << llendl;
+ }
+
+ return retval;
+}
+
+//===============================================================
+void LLPrimitive::init(LLPCode p_code)
+{
+ if (mNumTEs)
+ {
+ if (mTextureList)
+ {
+ delete [] mTextureList;
+ }
+ mTextureList = new LLTextureEntry[mNumTEs];
+ }
+
+ mPrimitiveCode = p_code;
+}
+
+void LLPrimitive::setPCode(const U8 p_code)
+{
+ mPrimitiveCode = p_code;
+}
+
+//===============================================================
+const LLTextureEntry * LLPrimitive::getTE(const U8 te_num) const
+{
+ // if we're asking for a non-existent face, return null
+ if (mNumTEs && (te_num< mNumTEs))
+ {
+ return(&mTextureList[te_num]);
+ }
+ else
+ {
+ return(NULL);
+ }
+}
+
+//===============================================================
+void LLPrimitive::setNumTEs(const U8 num_tes)
+{
+ if (num_tes == mNumTEs)
+ {
+ return;
+ }
+
+ // Right now, we don't try and preserve entries when the number of faces
+ // changes.
+
+ if (num_tes)
+ {
+ LLTextureEntry *new_tes;
+ new_tes = new LLTextureEntry[num_tes];
+ U32 i;
+ for (i = 0; i < num_tes; i++)
+ {
+ if (i < mNumTEs)
+ {
+ new_tes[i] = mTextureList[i];
+ }
+ else if (mNumTEs)
+ {
+ new_tes[i] = mTextureList[mNumTEs - 1];
+ }
+ else
+ {
+ new_tes[i] = LLTextureEntry();
+ }
+ }
+ delete[] mTextureList;
+ mTextureList = new_tes;
+ }
+ else
+ {
+ delete[] mTextureList;
+ mTextureList = NULL;
+ }
+
+
+ mNumTEs = num_tes;
+}
+
+//===============================================================
+void LLPrimitive::setAllTETextures(const LLUUID &tex_id)
+{
+ U8 i;
+
+ for (i = 0; i < mNumTEs; i++)
+ {
+ mTextureList[i].setID(tex_id);
+ }
+}
+
+//===============================================================
+void LLPrimitive::setTE(const U8 index, const LLTextureEntry &te)
+{
+ mTextureList[index] = te;
+}
+
+S32 LLPrimitive::setTETexture(const U8 te, const LLUUID &tex_id)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setID(tex_id);
+}
+
+S32 LLPrimitive::setTEColor(const U8 te, const LLColor4 &color)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setColor(color);
+}
+
+S32 LLPrimitive::setTEColor(const U8 te, const LLColor3 &color)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setColor(color);
+}
+
+S32 LLPrimitive::setTEAlpha(const U8 te, const F32 alpha)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setAlpha(alpha);
+}
+
+//===============================================================
+S32 LLPrimitive::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ return mTextureList[te].setScale(s,t);
+}
+
+
+// BUG: slow - done this way because texture entries have some
+// voodoo related to texture coords
+S32 LLPrimitive::setTEScaleS(const U8 te, const F32 s)
+{
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ F32 ignore, t;
+ mTextureList[te].getScale(&ignore, &t);
+ return mTextureList[te].setScale(s,t);
+}
+
+
+// BUG: slow - done this way because texture entries have some
+// voodoo related to texture coords
+S32 LLPrimitive::setTEScaleT(const U8 te, const F32 t)
+{
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ F32 s, ignore;
+ mTextureList[te].getScale(&s, &ignore);
+ return mTextureList[te].setScale(s,t);
+}
+
+
+//===============================================================
+S32 LLPrimitive::setTEOffset(const U8 te, const F32 s, const F32 t)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ return mTextureList[te].setOffset(s,t);
+}
+
+
+// BUG: slow - done this way because texture entries have some
+// voodoo related to texture coords
+S32 LLPrimitive::setTEOffsetS(const U8 te, const F32 s)
+{
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ F32 ignore, t;
+ mTextureList[te].getOffset(&ignore, &t);
+ return mTextureList[te].setOffset(s,t);
+}
+
+
+// BUG: slow - done this way because texture entries have some
+// voodoo related to texture coords
+S32 LLPrimitive::setTEOffsetT(const U8 te, const F32 t)
+{
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ F32 s, ignore;
+ mTextureList[te].getOffset(&s, &ignore);
+ return mTextureList[te].setOffset(s,t);
+}
+
+
+//===============================================================
+S32 LLPrimitive::setTERotation(const U8 te, const F32 r)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "Setting nonexistent face" << llendl;
+ return 0;
+ }
+
+ return mTextureList[te].setRotation(r);
+}
+
+
+//===============================================================
+S32 LLPrimitive::setTEBumpShinyFullbright(const U8 te, const U8 bump)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setBumpShinyFullbright( bump );
+}
+
+S32 LLPrimitive::setTEMediaTexGen(const U8 te, const U8 media)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setMediaTexGen( media );
+}
+
+S32 LLPrimitive::setTEBumpmap(const U8 te, const U8 bump)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setBumpmap( bump );
+}
+
+S32 LLPrimitive::setTEBumpShiny(const U8 te, const U8 bump_shiny)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setBumpShiny( bump_shiny );
+}
+
+S32 LLPrimitive::setTETexGen(const U8 te, const U8 texgen)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setTexGen( texgen );
+}
+
+S32 LLPrimitive::setTEShiny(const U8 te, const U8 shiny)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setShiny( shiny );
+}
+
+S32 LLPrimitive::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setFullbright( fullbright );
+}
+
+S32 LLPrimitive::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ // if we're asking for a non-existent face, return null
+ if (te >= mNumTEs)
+ {
+ llwarns << "setting non-existent te " << te << llendl
+ return 0;
+ }
+
+ return mTextureList[te].setMediaFlags( media_flags );
+}
+
+
+LLPCode LLPrimitive::legacyToPCode(const U8 legacy)
+{
+ LLPCode pcode = 0;
+
+ switch (legacy)
+ {
+ /*
+ case BOX:
+ pcode = LL_PCODE_CUBE;
+ break;
+ case CYLINDER:
+ pcode = LL_PCODE_CYLINDER;
+ break;
+ case CONE:
+ pcode = LL_PCODE_CONE;
+ break;
+ case HALF_CONE:
+ pcode = LL_PCODE_CONE_HEMI;
+ break;
+ case HALF_CYLINDER:
+ pcode = LL_PCODE_CYLINDER_HEMI;
+ break;
+ case HALF_SPHERE:
+ pcode = LL_PCODE_SPHERE_HEMI;
+ break;
+ case PRISM:
+ pcode = LL_PCODE_PRISM;
+ break;
+ case PYRAMID:
+ pcode = LL_PCODE_PYRAMID;
+ break;
+ case SPHERE:
+ pcode = LL_PCODE_SPHERE;
+ break;
+ case TETRAHEDRON:
+ pcode = LL_PCODE_TETRAHEDRON;
+ break;
+ case DEMON:
+ pcode = LL_PCODE_LEGACY_DEMON;
+ break;
+ case LSL_TEST:
+ pcode = LL_PCODE_LEGACY_LSL_TEST;
+ break;
+ case ORACLE:
+ pcode = LL_PCODE_LEGACY_ORACLE;
+ break;
+ case TEXTBUBBLE:
+ pcode = LL_PCODE_LEGACY_TEXT_BUBBLE;
+ break;
+ case ATOR:
+ pcode = LL_PCODE_LEGACY_ATOR;
+ break;
+ case BASIC_SHOT:
+ pcode = LL_PCODE_LEGACY_SHOT;
+ break;
+ case BIG_SHOT:
+ pcode = LL_PCODE_LEGACY_SHOT_BIG;
+ break;
+ case BIRD:
+ pcode = LL_PCODE_LEGACY_BIRD;
+ break;
+ case ROCK:
+ pcode = LL_PCODE_LEGACY_ROCK;
+ break;
+ case SMOKE:
+ pcode = LL_PCODE_LEGACY_SMOKE;
+ break;
+ case SPARK:
+ pcode = LL_PCODE_LEGACY_SPARK;
+ break;
+ */
+ case PRIMITIVE_VOLUME:
+ pcode = LL_PCODE_VOLUME;
+ break;
+ case GRASS:
+ pcode = LL_PCODE_LEGACY_GRASS;
+ break;
+ case PART_SYS:
+ pcode = LL_PCODE_LEGACY_PART_SYS;
+ break;
+ case PLAYER:
+ pcode = LL_PCODE_LEGACY_AVATAR;
+ break;
+ case TREE:
+ pcode = LL_PCODE_LEGACY_TREE;
+ break;
+ case TREE_NEW:
+ pcode = LL_PCODE_TREE_NEW;
+ break;
+ default:
+ llwarns << "Unknown legacy code " << legacy << "!" << llendl;
+ }
+
+ return pcode;
+}
+
+U8 LLPrimitive::pCodeToLegacy(const LLPCode pcode)
+{
+ U8 legacy;
+ switch (pcode)
+ {
+/*
+ case LL_PCODE_CUBE:
+ legacy = BOX;
+ break;
+ case LL_PCODE_CYLINDER:
+ legacy = CYLINDER;
+ break;
+ case LL_PCODE_CONE:
+ legacy = CONE;
+ break;
+ case LL_PCODE_CONE_HEMI:
+ legacy = HALF_CONE;
+ break;
+ case LL_PCODE_CYLINDER_HEMI:
+ legacy = HALF_CYLINDER;
+ break;
+ case LL_PCODE_SPHERE_HEMI:
+ legacy = HALF_SPHERE;
+ break;
+ case LL_PCODE_PRISM:
+ legacy = PRISM;
+ break;
+ case LL_PCODE_PYRAMID:
+ legacy = PYRAMID;
+ break;
+ case LL_PCODE_SPHERE:
+ legacy = SPHERE;
+ break;
+ case LL_PCODE_TETRAHEDRON:
+ legacy = TETRAHEDRON;
+ break;
+ case LL_PCODE_LEGACY_ATOR:
+ legacy = ATOR;
+ break;
+ case LL_PCODE_LEGACY_SHOT:
+ legacy = BASIC_SHOT;
+ break;
+ case LL_PCODE_LEGACY_SHOT_BIG:
+ legacy = BIG_SHOT;
+ break;
+ case LL_PCODE_LEGACY_BIRD:
+ legacy = BIRD;
+ break;
+ case LL_PCODE_LEGACY_DEMON:
+ legacy = DEMON;
+ break;
+ case LL_PCODE_LEGACY_LSL_TEST:
+ legacy = LSL_TEST;
+ break;
+ case LL_PCODE_LEGACY_ORACLE:
+ legacy = ORACLE;
+ break;
+ case LL_PCODE_LEGACY_ROCK:
+ legacy = ROCK;
+ break;
+ case LL_PCODE_LEGACY_TEXT_BUBBLE:
+ legacy = TEXTBUBBLE;
+ break;
+ case LL_PCODE_LEGACY_SMOKE:
+ legacy = SMOKE;
+ break;
+ case LL_PCODE_LEGACY_SPARK:
+ legacy = SPARK;
+ break;
+*/
+ case LL_PCODE_VOLUME:
+ legacy = PRIMITIVE_VOLUME;
+ break;
+ case LL_PCODE_LEGACY_GRASS:
+ legacy = GRASS;
+ break;
+ case LL_PCODE_LEGACY_PART_SYS:
+ legacy = PART_SYS;
+ break;
+ case LL_PCODE_LEGACY_AVATAR:
+ legacy = PLAYER;
+ break;
+ case LL_PCODE_LEGACY_TREE:
+ legacy = TREE;
+ break;
+ case LL_PCODE_TREE_NEW:
+ legacy = TREE_NEW;
+ break;
+ default:
+ llwarns << "Unknown pcode " << (S32)pcode << ":" << pcode << "!" << llendl;
+ return 0;
+ }
+ return legacy;
+}
+
+
+// static
+// Don't crash or llerrs here! This function is used for debug strings.
+const char * LLPrimitive::pCodeToString(const LLPCode pcode)
+{
+ static char pcode_string[255];
+
+ U8 base_code = pcode & LL_PCODE_BASE_MASK;
+ pcode_string[0] = 0;
+ if (!pcode)
+ {
+ sprintf(pcode_string, "null");
+ }
+ else if ((base_code) == LL_PCODE_LEGACY)
+ {
+ // It's a legacy object
+ switch (pcode)
+ {
+ case LL_PCODE_LEGACY_GRASS:
+ sprintf(pcode_string, "grass");
+ break;
+ case LL_PCODE_LEGACY_PART_SYS:
+ sprintf(pcode_string, "particle system");
+ break;
+ case LL_PCODE_LEGACY_AVATAR:
+ sprintf(pcode_string, "avatar");
+ break;
+ case LL_PCODE_LEGACY_TEXT_BUBBLE:
+ sprintf(pcode_string, "text bubble");
+ break;
+ case LL_PCODE_LEGACY_TREE:
+ sprintf(pcode_string, "tree");
+ break;
+ case LL_PCODE_TREE_NEW:
+ sprintf(pcode_string, "tree_new");
+ break;
+ default:
+ sprintf(pcode_string, "unknown legacy pcode %i",(U32)pcode);
+ }
+ }
+ else
+ {
+ char shape[32];
+ char mask[32];
+ if (base_code == LL_PCODE_CUBE)
+ {
+ sprintf(shape, "cube");
+ }
+ else if (base_code == LL_PCODE_CYLINDER)
+ {
+ sprintf(shape, "cylinder");
+ }
+ else if (base_code == LL_PCODE_CONE)
+ {
+ sprintf(shape, "cone");
+ }
+ else if (base_code == LL_PCODE_PRISM)
+ {
+ sprintf(shape, "prism");
+ }
+ else if (base_code == LL_PCODE_PYRAMID)
+ {
+ sprintf(shape, "pyramid");
+ }
+ else if (base_code == LL_PCODE_SPHERE)
+ {
+ sprintf(shape, "sphere");
+ }
+ else if (base_code == LL_PCODE_TETRAHEDRON)
+ {
+ sprintf(shape, "tetrahedron");
+ }
+ else if (base_code == LL_PCODE_VOLUME)
+ {
+ sprintf(shape, "volume");
+ }
+ else if (base_code == LL_PCODE_APP)
+ {
+ sprintf(shape, "app");
+ }
+ else
+ {
+ llwarns << "Unknown base mask for pcode: " << base_code << llendl;
+ }
+
+ U8 mask_code = pcode & (~LL_PCODE_BASE_MASK);
+ if (base_code == LL_PCODE_APP)
+ {
+ sprintf(mask, "%x", mask_code);
+ }
+ else if (mask_code & LL_PCODE_HEMI_MASK)
+ {
+ sprintf(mask, "hemi");
+ }
+ else if (mask != 0)
+ {
+ sprintf(mask, "%x", mask_code);
+ }
+ else
+ {
+ mask[0] = 0;
+ }
+
+ if (mask[0])
+ {
+ sprintf(pcode_string, "%s-%s", shape, mask);
+ }
+ else
+ {
+ sprintf(pcode_string, "%s", shape);
+ }
+ }
+ return pcode_string;
+}
+
+
+void LLPrimitive::copyTEs(const LLPrimitive *primitivep)
+{
+ U32 i;
+ if (primitivep->getNumTEs() != getNumTEs())
+ {
+ llwarns << "Primitives don't have same number of TE's" << llendl;
+ }
+ U32 num_tes = llmin(primitivep->getNumTEs(), getNumTEs());
+ for (i = 0; i < num_tes; i++)
+ {
+ const LLTextureEntry *tep = primitivep->getTE(i);
+ F32 s, t;
+ setTETexture(i, tep->getID());
+ setTEColor(i, tep->getColor());
+ tep->getScale(&s, &t);
+ setTEScale(i, s, t);
+ tep->getOffset(&s, &t);
+ setTEOffset(i, s, t);
+ setTERotation(i, tep->getRotation());
+ setTEBumpShinyFullbright(i, tep->getBumpShinyFullbright());
+ setTEMediaTexGen(i, tep->getMediaTexGen());
+ }
+}
+
+S32 face_index_from_id(LLFaceID face_ID, const std::vector<LLProfile::Face>& faceArray)
+{
+ S32 i;
+ for (i = 0; i < (S32)faceArray.size(); i++)
+ {
+ if (faceArray[i].mFaceID == face_ID)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+BOOL LLPrimitive::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
+{
+ LLVolume *volumep;
+ if (unique_volume)
+ {
+ F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail);
+ if (mVolumep.notNull() && volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail()))
+ {
+ return FALSE;
+ }
+ volumep = new LLVolume(volume_params, volume_detail, FALSE, TRUE);
+ }
+ else
+ {
+ if (mVolumep.notNull())
+ {
+ F32 volume_detail = LLVolumeLODGroup::getVolumeScaleFromDetail(detail);
+ if (volume_params == mVolumep->getParams() && (volume_detail == mVolumep->getDetail()))
+ {
+ return FALSE;
+ }
+ }
+
+ volumep = gVolumeMgr->getVolume(volume_params, detail);
+ if (volumep == mVolumep)
+ {
+ gVolumeMgr->cleanupVolume( volumep ); // gVolumeMgr->getVolume() creates a reference, but we don't need a second one.
+ return TRUE;
+ }
+ }
+
+ setChanged(GEOMETRY);
+
+
+ if (!mVolumep)
+ {
+ mVolumep = volumep;
+ //mFaceMask = mVolumep->generateFaceMask();
+ setNumTEs(mVolumep->getNumFaces());
+ return TRUE;
+ }
+
+ U32 old_face_mask = mVolumep->mFaceMask;
+
+ S32 face_bit = 0;
+ S32 cur_mask = 0;
+
+ // grab copies of the old faces so we can determine the TE mappings...
+ std::vector<LLProfile::Face> old_faces; // list of old faces for remapping texture entries
+ LLTextureEntry old_tes[9];
+
+ for (S32 face = 0; face < mVolumep->getNumFaces(); face++)
+ {
+ old_faces.push_back(mVolumep->getProfile().mFaces[face]);
+ }
+
+ for (face_bit = 0; face_bit < 9; face_bit++)
+ {
+ cur_mask = 0x1 << face_bit;
+ if (old_face_mask & cur_mask)
+ {
+ S32 te_index = face_index_from_id(cur_mask, old_faces);
+ old_tes[face_bit] = *getTE(te_index);
+ //llinfos << face_bit << ":" << te_index << ":" << old_tes[face_bit].getID() << llendl;
+ }
+ }
+
+
+ // build the new object
+ gVolumeMgr->cleanupVolume(mVolumep);
+ mVolumep = volumep;
+
+ U32 new_face_mask = mVolumep->mFaceMask;
+ S32 i;
+
+ /*
+ LLString old_mask_string;
+ for (i = 0; i < 9; i++)
+ {
+ if (old_face_mask & (1 << i))
+ {
+ old_mask_string.append("1");
+ }
+ else
+ {
+ old_mask_string.append("0");
+ }
+ }
+ LLString new_mask_string;
+ for (i = 0; i < 9; i++)
+ {
+ if (new_face_mask & (1 << i))
+ {
+ new_mask_string.append("1");
+ }
+ else
+ {
+ new_mask_string.append("0");
+ }
+ }
+
+ llinfos << "old mask: " << old_mask_string << llendl;
+ llinfos << "new mask: " << new_mask_string << llendl;
+ */
+
+
+ if (old_face_mask == new_face_mask)
+ {
+ // nothing to do
+ return TRUE;
+ }
+
+ if (mVolumep->getNumFaces() == 0 && new_face_mask != 0)
+ {
+ llwarns << "Object with 0 faces found...INCORRECT!" << llendl;
+ setNumTEs(mVolumep->getNumFaces());
+ return TRUE;
+ }
+
+
+ S32 face_mapping[9];
+ for (face_bit = 0; face_bit < 9; face_bit++)
+ {
+ face_mapping[face_bit] = face_bit;
+ }
+
+ // Generate the face-type mappings
+ for (face_bit = 0; face_bit < 9; face_bit++)
+ {
+ cur_mask = 0x1 << face_bit;
+ if (!(new_face_mask & cur_mask))
+ {
+ // Face doesn't exist in new map.
+ face_mapping[face_bit] = -1;
+ continue;
+ }
+ else if (old_face_mask & cur_mask)
+ {
+ // Face exists in new and old map.
+ face_mapping[face_bit] = face_bit;
+ continue;
+ }
+
+ // OK, how we've got a mismatch, where we have to fill a new face with one from
+ // the old face.
+ if (cur_mask & (LL_FACE_PATH_BEGIN | LL_FACE_PATH_END | LL_FACE_INNER_SIDE))
+ {
+ // It's a top/bottom/hollow interior face.
+ if (old_face_mask & LL_FACE_PATH_END)
+ {
+ face_mapping[face_bit] = 1;
+ continue;
+ }
+ else
+ {
+ S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0;
+ for (i = 0; i < 4; i++)
+ {
+ if (old_face_mask & cur_outer_mask)
+ {
+ face_mapping[face_bit] = 5 + i;
+ break;
+ }
+ cur_outer_mask <<= 1;
+ }
+ if (i == 4)
+ {
+ llwarns << "No path end or outer face in volume!" << llendl;
+ }
+ continue;
+ }
+ }
+
+ if (cur_mask & (LL_FACE_PROFILE_BEGIN | LL_FACE_PROFILE_END))
+ {
+ // A cut slice. Use the hollow interior if we have it.
+ if (old_face_mask & LL_FACE_INNER_SIDE)
+ {
+ face_mapping[face_bit] = 2;
+ continue;
+ }
+
+ // No interior, use the bottom face.
+ // Could figure out which of the outer faces was nearest, but that would be harder.
+ if (old_face_mask & LL_FACE_PATH_END)
+ {
+ face_mapping[face_bit] = 1;
+ continue;
+ }
+ else
+ {
+ S32 cur_outer_mask = LL_FACE_OUTER_SIDE_0;
+ for (i = 0; i < 4; i++)
+ {
+ if (old_face_mask & cur_outer_mask)
+ {
+ face_mapping[face_bit] = 5 + i;
+ break;
+ }
+ cur_outer_mask <<= 1;
+ }
+ if (i == 4)
+ {
+ llwarns << "No path end or outer face in volume!" << llendl;
+ }
+ continue;
+ }
+ }
+
+ // OK, the face that's missing is an outer face...
+ // Pull from the nearest adjacent outer face (there's always guaranteed to be one...
+ S32 cur_outer = face_bit - 5;
+ S32 min_dist = 5;
+ S32 min_outer_bit = -1;
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ if (old_face_mask & (LL_FACE_OUTER_SIDE_0 << i))
+ {
+ S32 dist = abs(i - cur_outer);
+ if (dist < min_dist)
+ {
+ min_dist = dist;
+ min_outer_bit = i + 5;
+ }
+ }
+ }
+ if (-1 == min_outer_bit)
+ {
+ llinfos << (LLVolume *)mVolumep << llendl;
+ llwarns << "Bad! No outer faces, impossible!" << llendl;
+ }
+ face_mapping[face_bit] = min_outer_bit;
+ }
+
+
+ setNumTEs(mVolumep->getNumFaces());
+ for (face_bit = 0; face_bit < 9; face_bit++)
+ {
+ cur_mask = 0x1 << face_bit;
+ if (new_face_mask & cur_mask)
+ {
+ if (-1 == face_mapping[face_bit])
+ {
+ llwarns << "No mapping from old face to new face!" << llendl;
+ }
+
+ S32 te_num = face_index_from_id(cur_mask, mVolumep->getProfile().mFaces);
+ setTE(te_num, old_tes[face_mapping[face_bit]]);
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLPrimitive::setMaterial(U8 material)
+{
+ if (material != mMaterial)
+ {
+ mMaterial = material;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLPrimitive::setTEArrays(const U8 size,
+ const LLUUID* image_ids,
+ const F32* scale_s,
+ const F32* scale_t)
+{
+ S32 cur_size = size;
+ if (cur_size > getNumTEs())
+ {
+ llwarns << "Trying to set more TEs than exist!" << llendl;
+ cur_size = getNumTEs();
+ }
+
+ S32 i;
+ // Copy over image information
+ for (i = 0; i < cur_size; i++)
+ {
+ // This is very BAD!!!!!!
+ if (image_ids != NULL)
+ {
+ setTETexture(i,image_ids[i]);
+ }
+ if (scale_s && scale_t)
+ {
+ setTEScale(i, scale_s[i], scale_t[i]);
+ }
+ }
+
+ if (i < getNumTEs())
+ {
+ cur_size--;
+ for (i=i; i < getNumTEs(); i++) // the i=i removes a gcc warning
+ {
+ if (image_ids != NULL)
+ {
+ setTETexture(i, image_ids[cur_size]);
+ }
+ if (scale_s && scale_t)
+ {
+ setTEScale(i, scale_s[cur_size], scale_t[cur_size]);
+ }
+ }
+ }
+}
+
+const F32 LL_MAX_SCALE_S = 100.0f;
+const F32 LL_MAX_SCALE_T = 100.0f;
+S32 LLPrimitive::packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const
+{
+ S32 face_index;
+ S32 i;
+ U64 exception_faces;
+ U8 *start_loc = cur_ptr;
+
+ htonmemcpy(cur_ptr,data_ptr + (last_face_index * data_size), type, data_size);
+ cur_ptr += data_size;
+
+ for (face_index = last_face_index-1; face_index >= 0; face_index--)
+ {
+ BOOL already_sent = FALSE;
+ for (i = face_index+1; i <= last_face_index; i++)
+ {
+ if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size))
+ {
+ already_sent = TRUE;
+ break;
+ }
+ }
+
+ if (!already_sent)
+ {
+ exception_faces = 0;
+ for (i = face_index; i >= 0; i--)
+ {
+ if (!memcmp(data_ptr+(data_size *face_index), data_ptr+(data_size *i), data_size))
+ {
+ exception_faces |= (1 << i);
+ }
+ }
+
+ //assign exception faces to cur_ptr
+ if (exception_faces >= (0x1 << 7))
+ {
+ if (exception_faces >= (0x1 << 14))
+ {
+ if (exception_faces >= (0x1 << 21))
+ {
+ if (exception_faces >= (0x1 << 28))
+ {
+ *cur_ptr++ = (U8)(((exception_faces >> 28) & 0x7F) | 0x80);
+ }
+ *cur_ptr++ = (U8)(((exception_faces >> 21) & 0x7F) | 0x80);
+ }
+ *cur_ptr++ = (U8)(((exception_faces >> 14) & 0x7F) | 0x80);
+ }
+ *cur_ptr++ = (U8)(((exception_faces >> 7) & 0x7F) | 0x80);
+ }
+
+ *cur_ptr++ = (U8)(exception_faces & 0x7F);
+
+ htonmemcpy(cur_ptr,data_ptr + (face_index * data_size), type, data_size);
+ cur_ptr += data_size;
+ }
+ }
+ return (S32)(cur_ptr - start_loc);
+}
+
+S32 LLPrimitive::unpackTEField(U8 *cur_ptr, U8 *buffer_end, U8 *data_ptr, U8 data_size, U8 face_count, EMsgVariableType type)
+{
+ U8 *start_loc = cur_ptr;
+ U64 i;
+ htonmemcpy(data_ptr,cur_ptr, type,data_size);
+ cur_ptr += data_size;
+
+ for (i = 1; i < face_count; i++)
+ {
+ // Already unswizzled, don't need to unswizzle it again!
+ memcpy(data_ptr+(i*data_size),data_ptr,data_size);
+ }
+
+ while ((cur_ptr < buffer_end) && (*cur_ptr != 0))
+ {
+// llinfos << "TE exception" << llendl;
+ i = 0;
+ while (*cur_ptr & 0x80)
+ {
+ i |= ((*cur_ptr++) & 0x7F);
+ i = i << 7;
+ }
+
+ i |= *cur_ptr++;
+
+ for (S32 j = 0; j < face_count; j++)
+ {
+ if (i & 0x01)
+ {
+ htonmemcpy(data_ptr+(j*data_size),cur_ptr,type,data_size);
+// char foo[64];
+// sprintf(foo,"%x %x",*(data_ptr+(j*data_size)), *(data_ptr+(j*data_size)+1));
+// llinfos << "Assigning " << foo << " to face " << j << llendl;
+ }
+ i = i >> 1;
+ }
+ cur_ptr += data_size;
+ }
+ return (S32)(cur_ptr - start_loc);
+}
+
+
+// Pack information about all texture entries into container:
+// { TextureEntry Variable 2 }
+// Includes information about image ID, color, scale S,T, offset S,T and rotation
+BOOL LLPrimitive::packTEMessage(LLMessageSystem *mesgsys) const
+{
+ const U32 MAX_TES = 32;
+
+ U8 image_ids[MAX_TES*16];
+ U8 colors[MAX_TES*4];
+ S16 scale_s[MAX_TES];
+ S16 scale_t[MAX_TES];
+ S16 offset_s[MAX_TES];
+ S16 offset_t[MAX_TES];
+ S16 image_rot[MAX_TES];
+ U8 bump[MAX_TES];
+ U8 media_flags[MAX_TES];
+
+ const U32 MAX_TE_BUFFER = 4096;
+ U8 packed_buffer[MAX_TE_BUFFER];
+ U8 *cur_ptr = packed_buffer;
+
+ S32 last_face_index = getNumTEs() - 1;
+
+ if (last_face_index > -1)
+ {
+ // ...if we hit the front, send one image id
+ S8 face_index;
+ LLColor4U coloru;
+ for (face_index = 0; face_index <= last_face_index; face_index++)
+ {
+ // Directly sending image_ids is not safe!
+ memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16);
+
+ // Cast LLColor4 to LLColor4U
+ coloru.setVec( getTE(face_index)->getColor() );
+
+ // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
+ // as all zeros. However, the subtraction and addition must be done in unsigned
+ // byte space, not in float space, otherwise off-by-one errors occur. JC
+ colors[4*face_index] = 255 - coloru.mV[0];
+ colors[4*face_index + 1] = 255 - coloru.mV[1];
+ colors[4*face_index + 2] = 255 - coloru.mV[2];
+ colors[4*face_index + 3] = 255 - coloru.mV[3];
+
+ const LLTextureEntry* te = getTE(face_index);
+ scale_s[face_index] = (S16) llround(((llclamp(te->mScaleS,-LL_MAX_SCALE_S, LL_MAX_SCALE_S)-1.0f)/(LL_MAX_SCALE_S+1.f) * (F32)0x7FFF));
+ scale_t[face_index] = (S16) llround(((llclamp(te->mScaleT,-LL_MAX_SCALE_T, LL_MAX_SCALE_T)-1.0f)/(LL_MAX_SCALE_T+1.f) * (F32)0x7FFF));
+ 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) * (F32)0x7FFF));
+ bump[face_index] = te->getBumpShinyFullbright();
+ media_flags[face_index] = te->getMediaTexGen();
+// llinfos << "BUMP pack [" << (S32)face_index << "]=" << (S32) bump[face_index] << llendl;
+ }
+
+ cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8);
+ }
+ mesgsys->addBinaryDataFast(_PREHASH_TextureEntry, packed_buffer, (S32)(cur_ptr - packed_buffer));
+
+ return FALSE;
+}
+
+
+BOOL LLPrimitive::packTEMessage(LLDataPacker &dp) const
+{
+ const U32 MAX_TES = 32;
+
+ U8 image_ids[MAX_TES*16];
+ U8 colors[MAX_TES*4];
+ S16 scale_s[MAX_TES];
+ S16 scale_t[MAX_TES];
+ S16 offset_s[MAX_TES];
+ S16 offset_t[MAX_TES];
+ S16 image_rot[MAX_TES];
+ U8 bump[MAX_TES];
+ U8 media_flags[MAX_TES];
+
+ const U32 MAX_TE_BUFFER = 4096;
+ U8 packed_buffer[MAX_TE_BUFFER];
+ U8 *cur_ptr = packed_buffer;
+
+ S32 last_face_index = getNumTEs() - 1;
+
+ if (last_face_index > -1)
+ {
+ // ...if we hit the front, send one image id
+ S8 face_index;
+ LLColor4U coloru;
+ for (face_index = 0; face_index <= last_face_index; face_index++)
+ {
+ // Directly sending image_ids is not safe!
+ memcpy(&image_ids[face_index*16],getTE(face_index)->getID().mData,16);
+
+ // Cast LLColor4 to LLColor4U
+ coloru.setVec( getTE(face_index)->getColor() );
+
+ // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
+ // as all zeros. However, the subtraction and addition must be done in unsigned
+ // byte space, not in float space, otherwise off-by-one errors occur. JC
+ colors[4*face_index] = 255 - coloru.mV[0];
+ colors[4*face_index + 1] = 255 - coloru.mV[1];
+ colors[4*face_index + 2] = 255 - coloru.mV[2];
+ colors[4*face_index + 3] = 255 - coloru.mV[3];
+
+ const LLTextureEntry* te = getTE(face_index);
+ scale_s[face_index] = (S16) llround(((llclamp(te->mScaleS,-LL_MAX_SCALE_S, LL_MAX_SCALE_S)-1.0f)/(LL_MAX_SCALE_S+1.f) * (F32)0x7FFF));
+ scale_t[face_index] = (S16) llround(((llclamp(te->mScaleT,-LL_MAX_SCALE_T, LL_MAX_SCALE_T)-1.0f)/(LL_MAX_SCALE_T+1.f) * (F32)0x7FFF));
+ 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) * (F32)0x7FFF));
+ bump[face_index] = te->getBumpShinyFullbright();
+ media_flags[face_index] = te->getMediaTexGen();
+
+// llinfos << "BUMP pack (Datapacker) [" << (S32)face_index << "]=" << (S32) bump[face_index] << llendl;
+ }
+
+ cur_ptr += packTEField(cur_ptr, (U8 *)image_ids, sizeof(LLUUID),last_face_index, MVT_LLUUID);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)colors, 4 ,last_face_index, MVT_U8);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)scale_s, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)scale_t, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)offset_s, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)offset_t, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)image_rot, 2 ,last_face_index, MVT_S16Array);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)bump, 1 ,last_face_index, MVT_U8);
+ *cur_ptr++ = 0;
+ cur_ptr += packTEField(cur_ptr, (U8 *)media_flags, 1 ,last_face_index, MVT_U8);
+ }
+
+ dp.packBinaryData(packed_buffer, (S32)(cur_ptr - packed_buffer), "TextureEntry");
+ return FALSE;
+}
+
+S32 LLPrimitive::unpackTEMessage(LLMessageSystem *mesgsys, char *block_name)
+{
+ return(unpackTEMessage(mesgsys,block_name,-1));
+}
+
+S32 LLPrimitive::unpackTEMessage(LLMessageSystem *mesgsys, char *block_name, const S32 block_num)
+{
+ // use a negative block_num to indicate a single-block read (a non-variable block)
+ S32 retval = 0;
+ const U32 MAX_TES = 32;
+
+ // Avoid construction of 32 UUIDs per call. JC
+
+ U8 image_data[MAX_TES*16];
+ U8 colors[MAX_TES*4];
+ S16 scale_s[MAX_TES];
+ S16 scale_t[MAX_TES];
+ S16 offset_s[MAX_TES];
+ S16 offset_t[MAX_TES];
+ S16 image_rot[MAX_TES];
+ U8 bump[MAX_TES];
+ U8 media_flags[MAX_TES];
+
+ const U32 MAX_TE_BUFFER = 4096;
+ U8 packed_buffer[MAX_TE_BUFFER];
+ U8 *cur_ptr = packed_buffer;
+
+ U32 size;
+ U32 face_count = 0;
+
+ if (block_num < 0)
+ {
+ size = mesgsys->getSizeFast(block_name, _PREHASH_TextureEntry);
+ }
+ else
+ {
+ size = mesgsys->getSizeFast(block_name, block_num, _PREHASH_TextureEntry);
+ }
+
+ if (size == 0)
+ {
+ return retval;
+ }
+
+ if (block_num < 0)
+ {
+ mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, packed_buffer, 0, 0, MAX_TE_BUFFER);
+ }
+ else
+ {
+ mesgsys->getBinaryDataFast(block_name, _PREHASH_TextureEntry, packed_buffer, 0, block_num, MAX_TE_BUFFER);
+ }
+
+ face_count = getNumTEs();
+
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)image_data, 16, face_count, MVT_LLUUID);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)colors, 4, face_count, MVT_U8);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)scale_s, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)scale_t, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)offset_s, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)offset_t, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)image_rot, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)bump, 1, face_count, MVT_U8);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)media_flags, 1, face_count, MVT_U8);
+
+ LLColor4 color;
+ LLColor4U coloru;
+ for (U32 i = 0; i < face_count; i++)
+ {
+ retval |= setTETexture(i, ((LLUUID*)image_data)[i]);
+ retval |= setTEScale(i,
+ floor((1.0f + ((((F32)scale_s[i] / (F32)0x7FFF)) * (LL_MAX_SCALE_S+1.f))) * 100.f + 0.5f) / 100.f,
+ floor((1.0f + ((((F32)scale_t[i] / (F32)0x7FFF)) * (LL_MAX_SCALE_T+1.f))) * 100.f + 0.5f) / 100.f);
+ retval |= setTEOffset(i, (F32)offset_s[i] / (F32)0x7FFF, (F32) offset_t[i] / (F32) 0x7FFF);
+ retval |= setTERotation(i, ((F32)image_rot[i]/ (F32)0x7FFF) * F_TWO_PI);
+ retval |= setTEBumpShinyFullbright(i, bump[i]);
+ retval |= setTEMediaTexGen(i, media_flags[i]);
+ coloru = LLColor4U(colors + 4*i);
+
+ // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
+ // as all zeros. However, the subtraction and addition must be done in unsigned
+ // byte space, not in float space, otherwise off-by-one errors occur. JC
+ color.mV[VRED] = F32(255 - coloru.mV[VRED]) / 255.f;
+ color.mV[VGREEN] = F32(255 - coloru.mV[VGREEN]) / 255.f;
+ color.mV[VBLUE] = F32(255 - coloru.mV[VBLUE]) / 255.f;
+ color.mV[VALPHA] = F32(255 - coloru.mV[VALPHA]) / 255.f;
+
+ retval |= setTEColor(i, color);
+ }
+
+ return retval;
+}
+
+S32 LLPrimitive::unpackTEMessage(LLDataPacker &dp)
+{
+ // use a negative block_num to indicate a single-block read (a non-variable block)
+ S32 retval = 0;
+ const U32 MAX_TES = 32;
+
+ // Avoid construction of 32 UUIDs per call
+ static LLUUID image_ids[MAX_TES];
+
+ U8 image_data[MAX_TES*16];
+ U8 colors[MAX_TES*4];
+ S16 scale_s[MAX_TES];
+ S16 scale_t[MAX_TES];
+ S16 offset_s[MAX_TES];
+ S16 offset_t[MAX_TES];
+ S16 image_rot[MAX_TES];
+ U8 bump[MAX_TES];
+ U8 media_flags[MAX_TES];
+
+ const U32 MAX_TE_BUFFER = 4096;
+ U8 packed_buffer[MAX_TE_BUFFER];
+ U8 *cur_ptr = packed_buffer;
+
+ S32 size;
+ U32 face_count = 0;
+
+ if (!dp.unpackBinaryData(packed_buffer, size, "TextureEntry"))
+ {
+ retval = TEM_INVALID;
+ llwarns << "Bad texture entry block! Abort!" << llendl;
+ return retval;
+ }
+
+ if (size == 0)
+ {
+ return retval;
+ }
+
+ face_count = getNumTEs();
+ U32 i;
+
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)image_data, 16, face_count, MVT_LLUUID);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)colors, 4, face_count, MVT_U8);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)scale_s, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)scale_t, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)offset_s, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)offset_t, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)image_rot, 2, face_count, MVT_S16Array);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)bump, 1, face_count, MVT_U8);
+ cur_ptr++;
+ cur_ptr += unpackTEField(cur_ptr, packed_buffer+size, (U8 *)media_flags, 1, face_count, MVT_U8);
+
+ for (i = 0; i < face_count; i++)
+ {
+// llinfos << "BUMP unpack (Datapacker) [" << i << "]=" << S32(bump[i]) <<llendl;
+ memcpy(image_ids[i].mData,&image_data[i*16],16);
+ }
+
+ LLColor4 color;
+ LLColor4U coloru;
+ for (i = 0; i < face_count; i++)
+ {
+ retval |= setTETexture(i, image_ids[i]);
+ retval |= setTEScale(i,
+ floor((1.0f + ((((F32)scale_s[i] / (F32)0x7FFF)) * (LL_MAX_SCALE_S+1.f))) * 100.f + 0.5f) / 100.f,
+ floor((1.0f + ((((F32)scale_t[i] / (F32)0x7FFF)) * (LL_MAX_SCALE_T+1.f))) * 100.f + 0.5f) / 100.f);
+ retval |= setTEOffset(i, (F32)offset_s[i] / (F32)0x7FFF, (F32) offset_t[i] / (F32) 0x7FFF);
+ retval |= setTERotation(i, ((F32)image_rot[i]/ (F32)0x7FFF) * F_TWO_PI);
+ retval |= setTEBumpShinyFullbright(i, bump[i]);
+ retval |= setTEMediaTexGen(i, media_flags[i]);
+ coloru = LLColor4U(colors + 4*i);
+
+ // Note: This is an optimization to send common colors (1.f, 1.f, 1.f, 1.f)
+ // as all zeros. However, the subtraction and addition must be done in unsigned
+ // byte space, not in float space, otherwise off-by-one errors occur. JC
+ color.mV[VRED] = F32(255 - coloru.mV[VRED]) / 255.f;
+ color.mV[VGREEN] = F32(255 - coloru.mV[VGREEN]) / 255.f;
+ color.mV[VBLUE] = F32(255 - coloru.mV[VBLUE]) / 255.f;
+ color.mV[VALPHA] = F32(255 - coloru.mV[VALPHA]) / 255.f;
+
+ retval |= setTEColor(i, color);
+ }
+
+ return retval;
+}
+
+void LLPrimitive::setTextureList(LLTextureEntry *listp)
+{
+ LLTextureEntry* old_texture_list = mTextureList;
+ mTextureList = listp;
+ delete[] old_texture_list;
+}
+
+//============================================================================
+
+LLLightParams::LLLightParams()
+{
+ mColor.setToWhite();
+ mRadius = 10.f;
+ mCutoff = 0.0f;
+ mFalloff = 0.75f;
+
+ mType = PARAMS_LIGHT;
+}
+
+BOOL LLLightParams::pack(LLDataPacker &dp) const
+{
+ LLColor4U color4u(mColor);
+ dp.packColor4U(color4u, "color");
+ dp.packF32(mRadius, "radius");
+ dp.packF32(mCutoff, "cutoff");
+ dp.packF32(mFalloff, "falloff");
+ return TRUE;
+}
+
+BOOL LLLightParams::unpack(LLDataPacker &dp)
+{
+ LLColor4U color4u;
+ dp.unpackColor4U(color4u, "color");
+ mColor = LLColor4(color4u);
+ dp.unpackF32(mRadius, "radius");
+ dp.unpackF32(mCutoff, "cutoff");
+ dp.unpackF32(mFalloff, "falloff");
+ return TRUE;
+}
+
+bool LLLightParams::operator==(const LLNetworkData& data) const
+{
+ if (data.mType != PARAMS_LIGHT)
+ {
+ return false;
+ }
+ const LLLightParams *param = (const LLLightParams*)&data;
+ if (param->mColor != mColor ||
+ param->mRadius != mRadius ||
+ param->mCutoff != mCutoff ||
+ param->mFalloff != mFalloff)
+ {
+ return false;
+ }
+ return true;
+}
+
+void LLLightParams::copy(const LLNetworkData& data)
+{
+ const LLLightParams *param = (LLLightParams*)&data;
+ mType = param->mType;
+ mColor = param->mColor;
+ mRadius = param->mRadius;
+ mCutoff = param->mCutoff;
+ mFalloff = param->mFalloff;
+}
+
+//============================================================================
+
+LLFlexibleObjectData::LLFlexibleObjectData()
+{
+ mSimulateLOD = FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS;
+ mGravity = FLEXIBLE_OBJECT_DEFAULT_GRAVITY;
+ mAirFriction = FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION;
+ mWindSensitivity = FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY;
+ mTension = FLEXIBLE_OBJECT_DEFAULT_TENSION;
+ //mUsingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE;
+ //mRenderingCollisionSphere = FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE;
+ mUserForce = LLVector3(0.f, 0.f, 0.f);
+
+ mType = PARAMS_FLEXIBLE;
+}
+
+BOOL LLFlexibleObjectData::pack(LLDataPacker &dp) const
+{
+ // Custom, uber-svelte pack "softness" in upper bits of tension & drag
+ U8 bit1 = (mSimulateLOD & 2) << 6;
+ U8 bit2 = (mSimulateLOD & 1) << 7;
+ dp.packU8((U8)(mTension*10.01f) + bit1, "tension");
+ dp.packU8((U8)(mAirFriction*10.01f) + bit2, "drag");
+ dp.packU8((U8)((mGravity+10.f)*10.01f), "gravity");
+ dp.packU8((U8)(mWindSensitivity*10.01f), "wind");
+ dp.packVector3(mUserForce, "userforce");
+ return TRUE;
+}
+
+BOOL LLFlexibleObjectData::unpack(LLDataPacker &dp)
+{
+ U8 tension, friction, gravity, wind;
+ U8 bit1, bit2;
+ dp.unpackU8(tension, "tension"); bit1 = (tension >> 6) & 2;
+ mTension = ((F32)(tension&0x7f))/10.f;
+ dp.unpackU8(friction, "drag"); bit2 = (friction >> 7) & 1;
+ mAirFriction = ((F32)(friction&0x7f))/10.f;
+ mSimulateLOD = bit1 | bit2;
+ dp.unpackU8(gravity, "gravity"); mGravity = ((F32)gravity)/10.f - 10.f;
+ dp.unpackU8(wind, "wind"); mWindSensitivity = ((F32)wind)/10.f;
+ if (dp.hasNext())
+ {
+ dp.unpackVector3(mUserForce, "userforce");
+ }
+ else
+ {
+ mUserForce.setVec(0.f, 0.f, 0.f);
+ }
+ return TRUE;
+}
+
+bool LLFlexibleObjectData::operator==(const LLNetworkData& data) const
+{
+ if (data.mType != PARAMS_FLEXIBLE)
+ {
+ return false;
+ }
+ LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data;
+ return (mSimulateLOD == flex_data->mSimulateLOD &&
+ mGravity == flex_data->mGravity &&
+ mAirFriction == flex_data->mAirFriction &&
+ mWindSensitivity == flex_data->mWindSensitivity &&
+ mTension == flex_data->mTension &&
+ mUserForce == flex_data->mUserForce);
+ //mUsingCollisionSphere == flex_data->mUsingCollisionSphere &&
+ //mRenderingCollisionSphere == flex_data->mRenderingCollisionSphere
+}
+
+void LLFlexibleObjectData::copy(const LLNetworkData& data)
+{
+ const LLFlexibleObjectData *flex_data = (LLFlexibleObjectData*)&data;
+ mSimulateLOD = flex_data->mSimulateLOD;
+ mGravity = flex_data->mGravity;
+ mAirFriction = flex_data->mAirFriction;
+ mWindSensitivity = flex_data->mWindSensitivity;
+ mTension = flex_data->mTension;
+ mUserForce = flex_data->mUserForce;
+ //mUsingCollisionSphere = flex_data->mUsingCollisionSphere;
+ //mRenderingCollisionSphere = flex_data->mRenderingCollisionSphere;
+}
diff --git a/indra/llprimitive/llprimitive.h b/indra/llprimitive/llprimitive.h
new file mode 100644
index 0000000000..3ad96bf6e1
--- /dev/null
+++ b/indra/llprimitive/llprimitive.h
@@ -0,0 +1,510 @@
+/**
+ * @file llprimitive.h
+ * @brief LLPrimitive base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPRIMITIVE_H
+#define LL_LLPRIMITIVE_H
+
+#include "lluuid.h"
+#include "v3math.h"
+#include "xform.h"
+#include "message.h"
+#include "llmemory.h"
+#include "llvolume.h"
+#include "lltextureentry.h"
+
+// Moved to stdtypes.h --JC
+// typedef U8 LLPCode;
+class LLMessageSystem;
+class LLVolumeParams;
+class LLColor4;
+class LLColor3;
+class LLTextureEntry;
+class LLDataPacker;
+
+enum LLGeomType // NOTE: same vals as GL Ids
+{
+ LLInvalid = 0,
+ LLLineLoop = 2,
+ LLLineStrip = 3,
+ LLTriangles = 4,
+ LLTriStrip = 5,
+ LLTriFan = 6,
+ LLQuads = 7,
+ LLQuadStrip = 8
+};
+
+class LLVolume;
+
+/**
+ * exported constants
+ */
+extern const F32 OBJECT_CUT_MIN;
+extern const F32 OBJECT_CUT_MAX;
+extern const F32 OBJECT_CUT_INC;
+extern const F32 OBJECT_MIN_CUT_INC;
+extern const F32 OBJECT_ROTATION_PRECISION;
+
+extern const F32 OBJECT_TWIST_MIN;
+extern const F32 OBJECT_TWIST_MAX;
+extern const F32 OBJECT_TWIST_INC;
+
+// This is used for linear paths,
+// since twist is used in a slightly different manner.
+extern const F32 OBJECT_TWIST_LINEAR_MIN;
+extern const F32 OBJECT_TWIST_LINEAR_MAX;
+extern const F32 OBJECT_TWIST_LINEAR_INC;
+
+extern const F32 OBJECT_MIN_HOLE_SIZE;
+extern const F32 OBJECT_MAX_HOLE_SIZE_X;
+extern const F32 OBJECT_MAX_HOLE_SIZE_Y;
+
+// Revolutions parameters.
+extern const F32 OBJECT_REV_MIN;
+extern const F32 OBJECT_REV_MAX;
+extern const F32 OBJECT_REV_INC;
+
+
+//============================================================================
+
+// TomY: Base class for things that pack & unpack themselves
+class LLNetworkData
+{
+public:
+ // Extra parameter IDs
+ enum
+ {
+ PARAMS_FLEXIBLE = 0x10,
+ PARAMS_LIGHT = 0x20
+ };
+
+public:
+ U16 mType;
+ virtual ~LLNetworkData() {};
+ virtual BOOL pack(LLDataPacker &dp) const = 0;
+ virtual BOOL unpack(LLDataPacker &dp) = 0;
+ virtual bool operator==(const LLNetworkData& data) const = 0;
+ virtual void copy(const LLNetworkData& data) = 0;
+};
+
+extern const F32 LIGHT_MIN_RADIUS;
+extern const F32 LIGHT_DEFAULT_RADIUS;
+extern const F32 LIGHT_MAX_RADIUS;
+extern const F32 LIGHT_MIN_FALLOFF;
+extern const F32 LIGHT_DEFAULT_FALLOFF;
+extern const F32 LIGHT_MAX_FALLOFF;
+extern const F32 LIGHT_MIN_CUTOFF;
+extern const F32 LIGHT_DEFAULT_CUTOFF;
+extern const F32 LIGHT_MAX_CUTOFF;
+
+class LLLightParams : public LLNetworkData
+{
+protected:
+ LLColor4 mColor; // alpha = intensity
+ F32 mRadius;
+ F32 mFalloff;
+ F32 mCutoff;
+
+public:
+ LLLightParams();
+ /*virtual*/ BOOL pack(LLDataPacker &dp) const;
+ /*virtual*/ BOOL unpack(LLDataPacker &dp);
+ /*virtual*/ bool operator==(const LLNetworkData& data) const;
+ /*virtual*/ void copy(const LLNetworkData& data);
+
+ void setColor(const LLColor4& color) { mColor = color; mColor.clamp(); }
+ void setRadius(F32 radius) { mRadius = llclamp(radius, LIGHT_MIN_RADIUS, LIGHT_MAX_RADIUS); }
+ void setFalloff(F32 falloff) { mFalloff = llclamp(falloff, LIGHT_MIN_FALLOFF, LIGHT_MAX_FALLOFF); }
+ void setCutoff(F32 cutoff) { mCutoff = llclamp(cutoff, LIGHT_MIN_CUTOFF, LIGHT_MAX_CUTOFF); }
+
+ LLColor4 getColor() const { return mColor; }
+ F32 getRadius() const { return mRadius; }
+ F32 getFalloff() const { return mFalloff; }
+ F32 getCutoff() const { return mCutoff; }
+};
+
+//-------------------------------------------------
+// This structure is also used in the part of the
+// code that creates new flexible objects.
+//-------------------------------------------------
+
+// These were made into enums so that they could be used as fixed size
+// array bounds.
+enum EFlexibleObjectConst
+{
+ // "Softness" => [0,3], increments of 1
+ // Represents powers of 2: 0 -> 1, 3 -> 8
+ FLEXIBLE_OBJECT_MIN_SECTIONS = 0,
+ FLEXIBLE_OBJECT_DEFAULT_NUM_SECTIONS = 2,
+ FLEXIBLE_OBJECT_MAX_SECTIONS = 3
+};
+
+// "Tension" => [0,10], increments of 0.1
+extern const F32 FLEXIBLE_OBJECT_MIN_TENSION;
+extern const F32 FLEXIBLE_OBJECT_DEFAULT_TENSION;
+extern const F32 FLEXIBLE_OBJECT_MAX_TENSION;
+
+// "Drag" => [0,10], increments of 0.1
+extern const F32 FLEXIBLE_OBJECT_MIN_AIR_FRICTION;
+extern const F32 FLEXIBLE_OBJECT_DEFAULT_AIR_FRICTION;
+extern const F32 FLEXIBLE_OBJECT_MAX_AIR_FRICTION;
+
+// "Gravity" = [-10,10], increments of 0.1
+extern const F32 FLEXIBLE_OBJECT_MIN_GRAVITY;
+extern const F32 FLEXIBLE_OBJECT_DEFAULT_GRAVITY;
+extern const F32 FLEXIBLE_OBJECT_MAX_GRAVITY;
+
+// "Wind" = [0,10], increments of 0.1
+extern const F32 FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY;
+extern const F32 FLEXIBLE_OBJECT_DEFAULT_WIND_SENSITIVITY;
+extern const F32 FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY;
+
+extern const F32 FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE;
+
+extern const F32 FLEXIBLE_OBJECT_DEFAULT_LENGTH;
+extern const BOOL FLEXIBLE_OBJECT_DEFAULT_USING_COLLISION_SPHERE;
+extern const BOOL FLEXIBLE_OBJECT_DEFAULT_RENDERING_COLLISION_SPHERE;
+
+
+class LLFlexibleObjectData : public LLNetworkData
+{
+protected:
+ S32 mSimulateLOD; // 2^n = number of simulated sections
+ F32 mGravity;
+ F32 mAirFriction; // higher is more stable, but too much looks like it's underwater
+ F32 mWindSensitivity; // interacts with tension, air friction, and gravity
+ F32 mTension; //interacts in complex ways with other parameters
+ LLVector3 mUserForce; // custom user-defined force vector
+ //BOOL mUsingCollisionSphere;
+ //BOOL mRenderingCollisionSphere;
+
+public:
+ void setSimulateLOD(S32 lod) { mSimulateLOD = llclamp(lod, (S32)FLEXIBLE_OBJECT_MIN_SECTIONS, (S32)FLEXIBLE_OBJECT_MAX_SECTIONS); }
+ void setGravity(F32 gravity) { mGravity = llclamp(gravity, FLEXIBLE_OBJECT_MIN_GRAVITY, FLEXIBLE_OBJECT_MAX_GRAVITY); }
+ void setAirFriction(F32 friction) { mAirFriction = llclamp(friction, FLEXIBLE_OBJECT_MIN_AIR_FRICTION, FLEXIBLE_OBJECT_MAX_AIR_FRICTION); }
+ void setWindSensitivity(F32 wind) { mWindSensitivity = llclamp(wind, FLEXIBLE_OBJECT_MIN_WIND_SENSITIVITY, FLEXIBLE_OBJECT_MAX_WIND_SENSITIVITY); }
+ void setTension(F32 tension) { mTension = llclamp(tension, FLEXIBLE_OBJECT_MIN_TENSION, FLEXIBLE_OBJECT_MAX_TENSION); }
+ void setUserForce(LLVector3 &force) { mUserForce = force; }
+
+ S32 getSimulateLOD() const { return mSimulateLOD; }
+ F32 getGravity() const { return mGravity; }
+ F32 getAirFriction() const { return mAirFriction; }
+ F32 getWindSensitivity() const { return mWindSensitivity; }
+ F32 getTension() const { return mTension; }
+ LLVector3 getUserForce() const { return mUserForce; }
+
+ //------ the constructor for the structure ------------
+ LLFlexibleObjectData();
+ BOOL pack(LLDataPacker &dp) const;
+ BOOL unpack(LLDataPacker &dp);
+ bool operator==(const LLNetworkData& data) const;
+ void copy(const LLNetworkData& data);
+};// end of attributes structure
+
+class LLPrimitive : public LLXform
+{
+public:
+ LLPrimitive();
+ virtual ~LLPrimitive();
+
+ static LLPrimitive *createPrimitive(LLPCode p_code);
+ void init(LLPCode p_code);
+
+ void setPCode(const LLPCode pcode);
+ const LLVolume *getVolumeConst() const { return mVolumep; } // HACK for Windoze confusion about ostream operator in LLVolume
+ LLVolume *getVolume() const { return mVolumep; }
+ virtual BOOL setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
+
+ // Modify texture entry properties
+ inline BOOL validTE(const U8 te_num) const;
+ const LLTextureEntry *getTE(const U8 te_num) const;
+
+ virtual void setNumTEs(const U8 num_tes);
+ virtual void setAllTETextures(const LLUUID &tex_id);
+ virtual void setTE(const U8 index, const LLTextureEntry &te);
+ virtual S32 setTEColor(const U8 te, const LLColor4 &color);
+ virtual S32 setTEColor(const U8 te, const LLColor3 &color);
+ virtual S32 setTEAlpha(const U8 te, const F32 alpha);
+ virtual S32 setTETexture(const U8 te, const LLUUID &tex_id);
+ virtual S32 setTEScale (const U8 te, const F32 s, const F32 t);
+ virtual S32 setTEScaleS(const U8 te, const F32 s);
+ virtual S32 setTEScaleT(const U8 te, const F32 t);
+ virtual S32 setTEOffset (const U8 te, const F32 s, const F32 t);
+ virtual S32 setTEOffsetS(const U8 te, const F32 s);
+ virtual S32 setTEOffsetT(const U8 te, const F32 t);
+ virtual S32 setTERotation(const U8 te, const F32 r);
+ virtual S32 setTEBumpShinyFullbright(const U8 te, const U8 bump);
+ virtual S32 setTEBumpShiny(const U8 te, const U8 bump);
+ virtual S32 setTEMediaTexGen(const U8 te, const U8 media);
+ virtual S32 setTEBumpmap(const U8 te, const U8 bump);
+ virtual S32 setTETexGen(const U8 te, const U8 texgen);
+ virtual S32 setTEShiny(const U8 te, const U8 shiny);
+ virtual S32 setTEFullbright(const U8 te, const U8 fullbright);
+ virtual S32 setTEMediaFlags(const U8 te, const U8 flags);
+ virtual BOOL setMaterial(const U8 material); // returns TRUE if material changed
+
+ void setTEArrays(const U8 size,
+ const LLUUID* image_ids,
+ const F32* scale_s,
+ const F32* scale_t);
+ void copyTEs(const LLPrimitive *primitive);
+ S32 packTEField(U8 *cur_ptr, U8 *data_ptr, U8 data_size, U8 last_face_index, EMsgVariableType type) const;
+ S32 unpackTEField(U8 *cur_ptr, U8 *buffer_end, U8 *data_ptr, U8 data_size, U8 face_count, EMsgVariableType type);
+ BOOL packTEMessage(LLMessageSystem *mesgsys) const;
+ BOOL packTEMessage(LLDataPacker &dp) const;
+ S32 unpackTEMessage(LLMessageSystem *mesgsys, char *block_name);
+ S32 unpackTEMessage(LLMessageSystem *mesgsys, char *block_name, const S32 block_num); // Variable num of blocks
+ BOOL unpackTEMessage(LLDataPacker &dp);
+
+#ifdef CHECK_FOR_FINITE
+ inline void setPosition(const LLVector3& pos);
+ inline void setPosition(const F32 x, const F32 y, const F32 z);
+ inline void addPosition(const LLVector3& pos);
+
+ inline void setAngularVelocity(const LLVector3& avel);
+ inline void setAngularVelocity(const F32 x, const F32 y, const F32 z);
+ inline void setVelocity(const LLVector3& vel);
+ inline void setVelocity(const F32 x, const F32 y, const F32 z);
+ inline void setVelocityX(const F32 x);
+ inline void setVelocityY(const F32 y);
+ inline void setVelocityZ(const F32 z);
+ inline void addVelocity(const LLVector3& vel);
+ inline void setAcceleration(const LLVector3& accel);
+ inline void setAcceleration(const F32 x, const F32 y, const F32 z);
+#else
+ // Don't override the base LLXForm operators.
+ // Special case for setPosition. If not check-for-finite, fall through to LLXform method.
+ // void setPosition(F32 x, F32 y, F32 z)
+ // void setPosition(LLVector3)
+
+ void setAngularVelocity(const LLVector3& avel) { mAngularVelocity = avel; }
+ void setAngularVelocity(const F32 x, const F32 y, const F32 z) { mAngularVelocity.setVec(x,y,z); }
+ void setVelocity(const LLVector3& vel) { mVelocity = vel; }
+ void setVelocity(const F32 x, const F32 y, const F32 z) { mVelocity.setVec(x,y,z); }
+ void setVelocityX(const F32 x) { mVelocity.mV[VX] = x; }
+ void setVelocityY(const F32 y) { mVelocity.mV[VY] = y; }
+ void setVelocityZ(const F32 z) { mVelocity.mV[VZ] = z; }
+ void addVelocity(const LLVector3& vel) { mVelocity += vel; }
+ void setAcceleration(const LLVector3& accel) { mAcceleration = accel; }
+ void setAcceleration(const F32 x, const F32 y, const F32 z) { mAcceleration.setVec(x,y,z); }
+#endif
+
+ const LLPCode getPCode() const { return mPrimitiveCode; }
+ const char * getPCodeString() const { return pCodeToString(mPrimitiveCode); }
+ const LLVector3& getAngularVelocity() const { return mAngularVelocity; }
+ const LLVector3& getVelocity() const { return mVelocity; }
+ const LLVector3& getAcceleration() const { return mAcceleration; }
+ const U8 getNumTEs() const { return mNumTEs; }
+
+ const U8 getMaterial() const { return mMaterial; }
+
+ void setVolumeType(const U8 code);
+ U8 getVolumeType();
+
+ void setTextureList(LLTextureEntry *listp);
+
+ inline BOOL isAvatar() const;
+
+ static const char *pCodeToString(const LLPCode pcode);
+ static LLPCode legacyToPCode(const U8 legacy);
+ static U8 pCodeToLegacy(const LLPCode pcode);
+
+ inline static BOOL isPrimitive(const LLPCode pcode);
+ inline static BOOL isApp(const LLPCode pcode);
+
+protected:
+ LLPCode mPrimitiveCode; // Primitive code
+ LLVector3 mVelocity; // how fast are we moving?
+ LLVector3 mAcceleration; // are we under constant acceleration?
+ LLVector3 mAngularVelocity; // angular velocity
+ LLPointer<LLVolume> mVolumep;
+ LLTextureEntry *mTextureList; // list of texture GUIDs, scales, offsets
+ U8 mMaterial; // Material code
+ U8 mNumTEs; // # of faces on the primitve
+};
+
+inline BOOL LLPrimitive::isAvatar() const
+{
+ return mPrimitiveCode == LL_PCODE_LEGACY_AVATAR;
+}
+
+// static
+inline BOOL LLPrimitive::isPrimitive(const LLPCode pcode)
+{
+ LLPCode base_type = pcode & LL_PCODE_BASE_MASK;
+
+ if (base_type && (base_type < LL_PCODE_APP))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// static
+inline BOOL LLPrimitive::isApp(const LLPCode pcode)
+{
+ LLPCode base_type = pcode & LL_PCODE_BASE_MASK;
+
+ return (base_type == LL_PCODE_APP);
+}
+
+
+#ifdef CHECK_FOR_FINITE
+// Special case for setPosition. If not check-for-finite, fall through to LLXform method.
+void LLPrimitive::setPosition(const F32 x, const F32 y, const F32 z)
+{
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ {
+ LLXform::setPosition(x, y, z);
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setPosition(x,y,z) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+
+// Special case for setPosition. If not check-for-finite, fall through to LLXform method.
+void LLPrimitive::setPosition(const LLVector3& pos)
+{
+ if (pos.isFinite())
+ {
+ LLXform::setPosition(pos);
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setPosition(LLVector3) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+
+void LLPrimitive::setAngularVelocity(const LLVector3& avel)
+{
+ if (avel.isFinite())
+ {
+ mAngularVelocity = avel;
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::setAngularVelocity", 0);
+ }
+}
+
+void LLPrimitive::setAngularVelocity(const F32 x, const F32 y, const F32 z)
+{
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ {
+ mAngularVelocity.setVec(x,y,z);
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::setAngularVelocity", 0);
+ }
+}
+
+void LLPrimitive::setVelocity(const LLVector3& vel)
+{
+ if (vel.isFinite())
+ {
+ mVelocity = vel;
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setVelocity(LLVector3) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+
+void LLPrimitive::setVelocity(const F32 x, const F32 y, const F32 z)
+{
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ {
+ mVelocity.setVec(x,y,z);
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setVelocity(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+
+void LLPrimitive::setVelocityX(const F32 x)
+{
+ if (llfinite(x))
+ {
+ mVelocity.mV[VX] = x;
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::setVelocityX", 0);
+ }
+}
+
+void LLPrimitive::setVelocityY(const F32 y)
+{
+ if (llfinite(y))
+ {
+ mVelocity.mV[VY] = y;
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::setVelocityY", 0);
+ }
+}
+
+void LLPrimitive::setVelocityZ(const F32 z)
+{
+ if (llfinite(z))
+ {
+ mVelocity.mV[VZ] = z;
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::setVelocityZ", 0);
+ }
+}
+
+void LLPrimitive::addVelocity(const LLVector3& vel)
+{
+ if (vel.isFinite())
+ {
+ mVelocity += vel;
+ }
+ else
+ {
+ llerror("Non Finite in LLPrimitive::addVelocity", 0);
+ }
+}
+
+void LLPrimitive::setAcceleration(const LLVector3& accel)
+{
+ if (accel.isFinite())
+ {
+ mAcceleration = accel;
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setAcceleration(LLVector3) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+
+void LLPrimitive::setAcceleration(const F32 x, const F32 y, const F32 z)
+{
+ if (llfinite(x) && llfinite(y) && llfinite(z))
+ {
+ mAcceleration.setVec(x,y,z);
+ }
+ else
+ {
+ llerrs << "Non Finite in LLPrimitive::setAcceleration(F32,F32,F32) for " << pCodeToString(mPrimitiveCode) << llendl;
+ }
+}
+#endif // CHECK_FOR_FINITE
+
+inline BOOL LLPrimitive::validTE(const U8 te_num) const
+{
+ return (mNumTEs && te_num < mNumTEs);
+}
+
+#endif
+
diff --git a/indra/llprimitive/lltextureanim.cpp b/indra/llprimitive/lltextureanim.cpp
new file mode 100644
index 0000000000..3e2c80e782
--- /dev/null
+++ b/indra/llprimitive/lltextureanim.cpp
@@ -0,0 +1,221 @@
+/**
+ * @file lltextureanim.cpp
+ * @brief LLTextureAnim base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltextureanim.h"
+#include "message.h"
+#include "lldatapacker.h"
+
+const S32 TA_BLOCK_SIZE = 16;
+
+LLTextureAnim::LLTextureAnim()
+{
+ reset();
+}
+
+
+LLTextureAnim::~LLTextureAnim()
+{
+}
+
+
+void LLTextureAnim::reset()
+{
+ mMode = 0;
+ mFace = -1;
+ mSizeX = 4;
+ mSizeY = 4;
+ mStart = 0.f;
+ mLength = 0.f;
+ mRate = 1.f;
+}
+
+BOOL LLTextureAnim::equals(const LLTextureAnim &other) const
+{
+ if (mMode != other.mMode)
+ {
+ return FALSE;
+ }
+ if (mFace != other.mFace)
+ {
+ return FALSE;
+ }
+ if (mSizeX != other.mSizeX)
+ {
+ return FALSE;
+ }
+ if (mSizeY != other.mSizeY)
+ {
+ return FALSE;
+ }
+ if (mStart != other.mStart)
+ {
+ return FALSE;
+ }
+ if (mLength != other.mLength)
+ {
+ return FALSE;
+ }
+ if (mRate != other.mRate)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+void LLTextureAnim::packTAMessage(LLMessageSystem *mesgsys) const
+{
+ U8 data[TA_BLOCK_SIZE];
+ data[0] = mMode;
+ data[1] = mFace;
+ data[2] = mSizeX;
+ data[3] = mSizeY;
+ htonmemcpy(data + 4, &mStart, MVT_F32, sizeof(F32));
+ htonmemcpy(data + 8, &mLength, MVT_F32, sizeof(F32));
+ htonmemcpy(data + 12, &mRate, MVT_F32, sizeof(F32));
+
+ mesgsys->addBinaryDataFast(_PREHASH_TextureAnim, data, TA_BLOCK_SIZE);
+}
+
+
+void LLTextureAnim::packTAMessage(LLDataPacker &dp) const
+{
+ U8 data[TA_BLOCK_SIZE];
+ data[0] = mMode;
+ data[1] = mFace;
+ data[2] = mSizeX;
+ data[3] = mSizeY;
+ htonmemcpy(data + 4, &mStart, MVT_F32, sizeof(F32));
+ htonmemcpy(data + 8, &mLength, MVT_F32, sizeof(F32));
+ htonmemcpy(data + 12, &mRate, MVT_F32, sizeof(F32));
+
+ dp.packBinaryData(data, TA_BLOCK_SIZE, "TextureAnimation");
+}
+
+
+void LLTextureAnim::unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num)
+{
+ S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim);
+
+ if (size != TA_BLOCK_SIZE)
+ {
+ if (size)
+ {
+ llwarns << "Bad size " << size << " for TA block, ignoring." << llendl;
+ }
+ mMode = 0;
+ return;
+ }
+
+ U8 data[TA_BLOCK_SIZE];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureAnim, data, TA_BLOCK_SIZE, block_num);
+
+ mMode = data[0];
+ mFace = data[1];
+ if (mMode & LLTextureAnim::SMOOTH)
+ {
+ mSizeX = llmax((U8)0, data[2]);
+ mSizeY = llmax((U8)0, data[3]);
+ }
+ else
+ {
+ mSizeX = llmax((U8)1, data[2]);
+ mSizeY = llmax((U8)1, data[3]);
+ }
+ htonmemcpy(&mStart, data + 4, MVT_F32, sizeof(F32));
+ htonmemcpy(&mLength, data + 8, MVT_F32, sizeof(F32));
+ htonmemcpy(&mRate, data + 12, MVT_F32, sizeof(F32));
+}
+
+void LLTextureAnim::unpackTAMessage(LLDataPacker &dp)
+{
+ S32 size;
+ U8 data[TA_BLOCK_SIZE];
+ dp.unpackBinaryData(data, size, "TextureAnimation");
+ if (size != TA_BLOCK_SIZE)
+ {
+ if (size)
+ {
+ llwarns << "Bad size " << size << " for TA block, ignoring." << llendl;
+ }
+ mMode = 0;
+ return;
+ }
+
+ mMode = data[0];
+ mFace = data[1];
+ mSizeX = llmax((U8)1, data[2]);
+ mSizeY = llmax((U8)1, data[3]);
+ htonmemcpy(&mStart, data + 4, MVT_F32, sizeof(F32));
+ htonmemcpy(&mLength, data + 8, MVT_F32, sizeof(F32));
+ htonmemcpy(&mRate, data + 12, MVT_F32, sizeof(F32));
+}
+
+LLSD LLTextureAnim::asLLSD() const
+{
+ LLSD sd;
+ sd["mode"] = mMode;
+ sd["face"] = mFace;
+ sd["sizeX"] = mSizeX;
+ sd["sizeY"] = mSizeY;
+ sd["start"] = mStart;
+ sd["length"] = mLength;
+ sd["rate"] = mRate;
+ return sd;
+}
+
+bool LLTextureAnim::fromLLSD(LLSD& sd)
+{
+ const char *w;
+ w = "mode";
+ if (sd.has(w))
+ {
+ mMode = (U8)sd[w].asInteger();
+ } else goto fail;
+
+ w = "face";
+ if (sd.has(w))
+ {
+ mFace = (S8)sd[w].asInteger();
+ } else goto fail;
+
+ w = "sizeX";
+ if (sd.has(w))
+ {
+ mSizeX = (U8)sd[w].asInteger();
+ } else goto fail;
+
+ w = "sizeY";
+ if (sd.has(w))
+ {
+ mSizeY = (U8)sd[w].asInteger();
+ } else goto fail;
+
+ w = "start";
+ if (sd.has(w))
+ {
+ mStart = (F32)sd[w].asReal();
+ } else goto fail;
+
+ w = "length";
+ if (sd.has(w))
+ {
+ mLength = (F32)sd[w].asReal();
+ } else goto fail;
+
+ w = "rate";
+ if (sd.has(w))
+ {
+ mRate = (F32)sd[w].asReal();
+ } else goto fail;
+
+ return true;
+fail:
+ return false;
+}
diff --git a/indra/llprimitive/lltextureanim.h b/indra/llprimitive/lltextureanim.h
new file mode 100644
index 0000000000..db15642563
--- /dev/null
+++ b/indra/llprimitive/lltextureanim.h
@@ -0,0 +1,54 @@
+/**
+ * @file lltextureanim.h
+ * @brief LLTextureAnim base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREANIM_H
+#define LL_LLTEXTUREANIM_H
+
+#include "stdtypes.h"
+#include "llsd.h"
+
+class LLMessageSystem;
+class LLDataPacker;
+
+class LLTextureAnim
+{
+public:
+ LLTextureAnim();
+ virtual ~LLTextureAnim();
+
+ virtual void reset();
+ void packTAMessage(LLMessageSystem *mesgsys) const;
+ void packTAMessage(LLDataPacker &dp) const;
+ void unpackTAMessage(LLMessageSystem *mesgsys, const S32 block_num);
+ void unpackTAMessage(LLDataPacker &dp);
+ BOOL equals(const LLTextureAnim &other) const;
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ enum
+ {
+ ON = 0x01,
+ LOOP = 0x02,
+ REVERSE = 0x04,
+ PING_PONG = 0x08,
+ SMOOTH = 0x10,
+ ROTATE = 0x20,
+ SCALE = 0x40,
+ };
+
+public:
+ U8 mMode;
+ S8 mFace;
+ U8 mSizeX;
+ U8 mSizeY;
+ F32 mStart;
+ F32 mLength;
+ F32 mRate; // Rate in frames per second.
+};
+#endif
diff --git a/indra/llprimitive/lltextureentry.cpp b/indra/llprimitive/lltextureentry.cpp
new file mode 100644
index 0000000000..86952dfdb5
--- /dev/null
+++ b/indra/llprimitive/lltextureentry.cpp
@@ -0,0 +1,348 @@
+/**
+ * @file lltextureentry.cpp
+ * @brief LLTextureEntry base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltextureentry.h"
+#include "llsdutil.h"
+
+const U8 DEFAULT_BUMP_CODE = 0; // no bump or shininess
+
+const LLTextureEntry LLTextureEntry::null;
+
+//===============================================================
+LLTextureEntry::LLTextureEntry()
+{
+ init(LLUUID::null,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE);
+}
+
+LLTextureEntry::LLTextureEntry(const LLUUID& tex_id)
+{
+ init(tex_id,1.f,1.f,0.f,0.f,0.f,DEFAULT_BUMP_CODE);
+}
+
+LLTextureEntry::LLTextureEntry(const LLTextureEntry &rhs)
+{
+ mID = rhs.mID;
+ mScaleS = rhs.mScaleS;
+ mScaleT = rhs.mScaleT;
+ mOffsetS = rhs.mOffsetS;
+ mOffsetT = rhs.mOffsetT;
+ mRotation = rhs.mRotation;
+ mColor = rhs.mColor;
+ mBump = rhs.mBump;
+ mMediaFlags = rhs.mMediaFlags;
+}
+
+LLTextureEntry &LLTextureEntry::operator=(const LLTextureEntry &rhs)
+{
+ if (this != &rhs)
+ {
+ mID = rhs.mID;
+ mScaleS = rhs.mScaleS;
+ mScaleT = rhs.mScaleT;
+ mOffsetS = rhs.mOffsetS;
+ mOffsetT = rhs.mOffsetT;
+ mRotation = rhs.mRotation;
+ mColor = rhs.mColor;
+ mBump = rhs.mBump;
+ mMediaFlags = rhs.mMediaFlags;
+ }
+
+ return *this;
+}
+
+void LLTextureEntry::init(const LLUUID& tex_id, F32 scale_s, F32 scale_t, F32 offset_s, F32 offset_t, F32 rotation, U8 bump)
+{
+ setID(tex_id);
+
+ mScaleS = scale_s;
+ mScaleT = scale_t;
+ mOffsetS = offset_s;
+ mOffsetT = offset_t;
+ mRotation = rotation;
+ mBump = bump;
+ mMediaFlags = 0x0;
+
+ setColor(LLColor4(1.f, 1.f, 1.f, 1.f));
+}
+
+LLTextureEntry::~LLTextureEntry()
+{
+}
+
+bool LLTextureEntry::operator!=(const LLTextureEntry &rhs) const
+{
+ if (mID != rhs.mID) return(true);
+ if (mScaleS != rhs.mScaleS) return(true);
+ if (mScaleT != rhs.mScaleT) return(true);
+ if (mOffsetS != rhs.mOffsetS) return(true);
+ if (mOffsetT != rhs.mOffsetT) return(true);
+ if (mRotation != rhs.mRotation) return(true);
+ if (mColor != rhs.mColor) return (true);
+ if (mBump != rhs.mBump) return (true);
+ if (mMediaFlags != rhs.mMediaFlags) return true;
+ return(false);
+}
+
+bool LLTextureEntry::operator==(const LLTextureEntry &rhs) const
+{
+ if (mID != rhs.mID) return(false);
+ if (mScaleS != rhs.mScaleS) return(false);
+ if (mScaleT != rhs.mScaleT) return(false);
+ if (mOffsetS != rhs.mOffsetS) return(false);
+ if (mOffsetT != rhs.mOffsetT) return(false);
+ if (mRotation != rhs.mRotation) return(false);
+ if (mColor != rhs.mColor) return (false);
+ if (mBump != rhs.mBump) return (false);
+ if (mMediaFlags != rhs.mMediaFlags) return false;
+ return(true);
+}
+
+LLSD LLTextureEntry::asLLSD() const
+{
+ LLSD sd;
+
+ sd["imageid"] = getID();
+ sd["colors"] = ll_sd_from_color4(getColor());
+ sd["scales"] = mScaleS;
+ sd["scalet"] = mScaleT;
+ sd["offsets"] = mOffsetS;
+ sd["offsett"] = mOffsetT;
+ sd["imagerot"] = getRotation();
+ sd["bump"] = getBumpShiny();
+ sd["fullbright"] = getFullbright();
+ sd["media_flags"] = getMediaTexGen();
+
+ return sd;
+}
+
+bool LLTextureEntry::fromLLSD(LLSD& sd)
+{
+ const char *w, *x;
+ w = "imageid";
+ if (sd.has(w))
+ {
+ setID( sd[w] );
+ } else goto fail;
+ w = "colors";
+ if (sd.has(w))
+ {
+ setColor( ll_color4_from_sd(sd["colors"]) );
+ } else goto fail;
+ w = "scales";
+ x = "scalet";
+ if (sd.has(w) && sd.has(x))
+ {
+ setScale( (F32)sd[w].asReal(), (F32)sd[x].asReal() );
+ } else goto fail;
+ w = "offsets";
+ x = "offsett";
+ if (sd.has(w) && sd.has(x))
+ {
+ setOffset( (F32)sd[w].asReal(), (F32)sd[x].asReal() );
+ } else goto fail;
+ w = "imagerot";
+ if (sd.has(w))
+ {
+ setRotation( (F32)sd[w].asReal() );
+ } else goto fail;
+ w = "bump";
+ if (sd.has(w))
+ {
+ setBumpShiny( sd[w].asInteger() );
+ } else goto fail;
+ w = "fullbright";
+ if (sd.has(w))
+ {
+ setFullbright( sd[w].asInteger() );
+ } else goto fail;
+ w = "media_flags";
+ if (sd.has(w))
+ {
+ setMediaTexGen( sd[w].asInteger() );
+ } else goto fail;
+
+ return true;
+fail:
+ return false;
+}
+
+S32 LLTextureEntry::setID(const LLUUID &tex_id)
+{
+ if (mID != tex_id)
+ {
+ mID = tex_id;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setScale(F32 s, F32 t)
+{
+ S32 retval = 0;
+
+ if ( (mScaleS != s)
+ ||(mScaleT != t))
+ {
+ mScaleS = s;
+ mScaleT = t;
+
+ retval = TEM_CHANGE_TEXTURE;
+ }
+ return retval;
+}
+
+S32 LLTextureEntry::setColor(const LLColor4 &color)
+{
+ if (mColor != color)
+ {
+ mColor = color;
+ return TEM_CHANGE_COLOR;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setColor(const LLColor3 &color)
+{
+ if (mColor != color)
+ {
+ // This preserves alpha.
+ mColor.setVec(color);
+ return TEM_CHANGE_COLOR;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setAlpha(const F32 alpha)
+{
+ if (mColor.mV[VW] != alpha)
+ {
+ mColor.mV[VW] = alpha;
+ return TEM_CHANGE_COLOR;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setOffset(F32 s, F32 t)
+{
+ S32 retval = 0;
+
+ if ( (mOffsetS != s)
+ ||(mOffsetT != t))
+ {
+ mOffsetS = s;
+ mOffsetT = t;
+
+ retval = TEM_CHANGE_TEXTURE;
+ }
+ return retval;
+}
+
+S32 LLTextureEntry::setRotation(F32 theta)
+{
+ if (mRotation != theta)
+ {
+ mRotation = theta;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setBumpShinyFullbright(U8 bump)
+{
+ if (mBump != bump)
+ {
+ mBump = bump;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setMediaTexGen(U8 media)
+{
+ if (mMediaFlags != media)
+ {
+ mMediaFlags = media;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setBumpmap(U8 bump)
+{
+ bump &= TEM_BUMP_MASK;
+ if (getBumpmap() != bump)
+ {
+ mBump &= ~TEM_BUMP_MASK;
+ mBump |= bump;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setFullbright(U8 fullbright)
+{
+ fullbright &= TEM_FULLBRIGHT_MASK;
+ if (getFullbright() != fullbright)
+ {
+ mBump &= ~(TEM_FULLBRIGHT_MASK<<TEM_FULLBRIGHT_SHIFT);
+ mBump |= fullbright << TEM_FULLBRIGHT_SHIFT;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setShiny(U8 shiny)
+{
+ shiny &= TEM_SHINY_MASK;
+ if (getShiny() != shiny)
+ {
+ mBump &= ~(TEM_SHINY_MASK<<TEM_SHINY_SHIFT);
+ mBump |= shiny << TEM_SHINY_SHIFT;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setBumpShiny(U8 bump_shiny)
+{
+ bump_shiny &= TEM_BUMP_SHINY_MASK;
+ if (getBumpShiny() != bump_shiny)
+ {
+ mBump &= ~TEM_BUMP_SHINY_MASK;
+ mBump |= bump_shiny;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setMediaFlags(U8 media_flags)
+{
+ media_flags &= TEM_MEDIA_MASK;
+ if (getMediaFlags() != media_flags)
+ {
+ mMediaFlags &= ~TEM_MEDIA_MASK;
+ mMediaFlags |= media_flags;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
+S32 LLTextureEntry::setTexGen(U8 tex_gen)
+{
+ tex_gen &= TEM_TEX_GEN_MASK;
+ if (getTexGen() != tex_gen)
+ {
+ mMediaFlags &= ~TEM_TEX_GEN_MASK;
+ mMediaFlags |= tex_gen;
+ return TEM_CHANGE_TEXTURE;
+ }
+ return 0;
+}
+
diff --git a/indra/llprimitive/lltextureentry.h b/indra/llprimitive/lltextureentry.h
new file mode 100644
index 0000000000..b9558a159a
--- /dev/null
+++ b/indra/llprimitive/lltextureentry.h
@@ -0,0 +1,126 @@
+/**
+ * @file lltextureentry.h
+ * @brief LLTextureEntry base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREENTRY_H
+#define LL_LLTEXTUREENTRY_H
+
+#include "lluuid.h"
+#include "v4color.h"
+#include "llsd.h"
+
+const S32 TEM_CHANGE_COLOR = 0x1;
+const S32 TEM_CHANGE_TEXTURE = 0x2;
+const S32 TEM_INVALID = 0x4;
+
+const S32 TEM_BUMPMAP_COUNT = 32;
+
+// The Bump Shiny Fullbright values are bits in an eight bit field:
+// +----------+
+// | SSFBBBBB | S = Shiny, F = Fullbright, B = Bumpmap
+// | 76543210 |
+// +----------+
+const S32 TEM_BUMP_MASK = 0x1f; // 5 bits
+const S32 TEM_FULLBRIGHT_MASK = 0x01; // 1 bit
+const S32 TEM_SHINY_MASK = 0x03; // 2 bits
+const S32 TEM_BUMP_SHINY_MASK = (0xc0 | 0x1f);
+const S32 TEM_FULLBRIGHT_SHIFT = 5;
+const S32 TEM_SHINY_SHIFT = 6;
+
+// The Media Tex Gen values are bits in a bit field:
+// +----------+
+// | .....TTM | M = Media Flags (web page), T = LLTextureEntry::eTexGen, . = unused
+// | 76543210 |
+// +----------+
+const S32 TEM_MEDIA_MASK = 0x01;
+const S32 TEM_TEX_GEN_MASK = 0x06;
+const S32 TEM_TEX_GEN_SHIFT = 1;
+
+
+class LLTextureEntry
+{
+public:
+
+ typedef enum e_texgen
+ {
+ TEX_GEN_DEFAULT = 0x00,
+ TEX_GEN_PLANAR = 0x02,
+ TEX_GEN_SPHERICAL = 0x04,
+ TEX_GEN_CYLINDRICAL = 0x06
+ } eTexGen;
+
+ LLTextureEntry();
+ LLTextureEntry(const LLUUID& tex_id);
+ LLTextureEntry(const LLTextureEntry &rhs);
+
+ LLTextureEntry &operator=(const LLTextureEntry &rhs);
+ ~LLTextureEntry();
+
+ bool operator==(const LLTextureEntry &rhs) const;
+ bool operator!=(const LLTextureEntry &rhs) const;
+
+ LLSD asLLSD() const;
+ operator LLSD() const { return asLLSD(); }
+ bool fromLLSD(LLSD& sd);
+
+ void init(const LLUUID& tex_id, F32 scale_s, F32 scale_t, F32 offset_s, F32 offset_t, F32 rotation, U8 bump);
+
+ // These return a TEM_ flag from above to indicate if something changed.
+ S32 setID (const LLUUID &tex_id);
+ S32 setColor(const LLColor4 &color);
+ S32 setColor(const LLColor3 &color);
+ S32 setAlpha(const F32 alpha);
+ S32 setScale(F32 s, F32 t);
+ S32 setOffset(F32 s, F32 t);
+ S32 setRotation(F32 theta);
+
+ S32 setBumpmap(U8 bump);
+ S32 setFullbright(U8 bump);
+ S32 setShiny(U8 bump);
+ S32 setBumpShiny(U8 bump);
+ S32 setBumpShinyFullbright(U8 bump);
+
+ S32 setMediaFlags(U8 media_flags);
+ S32 setTexGen(U8 texGen);
+ S32 setMediaTexGen(U8 media);
+
+ const LLUUID &getID() const { return mID; }
+ const LLColor4 &getColor() const { return mColor; }
+ void getScale(F32 *s, F32 *t) const { *s = mScaleS; *t = mScaleT; }
+ void getOffset(F32 *s, F32 *t) const { *s = mOffsetS; *t = mOffsetT; }
+ F32 getRotation() const { return mRotation; }
+ void getRotation(F32 *theta) const { *theta = mRotation; }
+
+ U8 getBumpmap() const { return mBump & TEM_BUMP_MASK; }
+ U8 getFullbright() const { return (mBump>>TEM_FULLBRIGHT_SHIFT) & TEM_FULLBRIGHT_MASK; }
+ U8 getShiny() const { return (mBump>>TEM_SHINY_SHIFT) & TEM_SHINY_MASK; }
+ U8 getBumpShiny() const { return mBump & TEM_BUMP_SHINY_MASK; }
+ U8 getBumpShinyFullbright() const { return mBump; }
+
+ U8 getMediaFlags() const { return mMediaFlags & TEM_MEDIA_MASK; }
+ U8 getTexGen() const { return mMediaFlags & TEM_TEX_GEN_MASK; }
+ U8 getMediaTexGen() const { return mMediaFlags; }
+
+ // Media flags
+ enum { MF_NONE = 0x0, MF_WEB_PAGE = 0x1 };
+
+public:
+ F32 mScaleS; // S, T offset
+ F32 mScaleT; // S, T offset
+ F32 mOffsetS; // S, T offset
+ F32 mOffsetT; // S, T offset
+ F32 mRotation; // anti-clockwise rotation in rad about the bottom left corner
+
+ static const LLTextureEntry null;
+protected:
+ LLUUID mID; // Texture GUID
+ LLColor4 mColor;
+ U8 mBump; // Bump map, shiny, and fullbright
+ U8 mMediaFlags; // replace with web page, movie, etc.
+};
+
+#endif
diff --git a/indra/llprimitive/lltreeparams.cpp b/indra/llprimitive/lltreeparams.cpp
new file mode 100644
index 0000000000..ca3de36630
--- /dev/null
+++ b/indra/llprimitive/lltreeparams.cpp
@@ -0,0 +1,187 @@
+/**
+ * @file lltreeparams.cpp
+ * @brief implementation of the LLTreeParams class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//////////////////////////////////////////////////////////////////////
+
+#include "linden_common.h"
+
+#include "llmath.h"
+
+#include "lltreeparams.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+
+LLTreeParams::LLTreeParams()
+{
+
+// llinfos << "TREE PARAMS INITIALIZED" << llendl;
+ // init to basic something or other...
+ mShape = SR_TEND_FLAME;
+ mLevels = 1;
+ mScale = 15;
+ mScaleV = 0;
+
+ mBaseSize = 0.3f;
+
+ mRatio = 0.015f;
+ mRatioPower = 1.3f;
+
+ mLobes = 0;
+ mLobeDepth = .1f;
+
+ mFlare = 1.2f;
+ mFlarePercentage = 0.1f;
+ mFlareRes = 3;
+
+ //mAttractionUp = .5f;
+
+ mBaseSplits = 0;
+
+ mScale0 = 2.0;
+ mScaleV0 = 0.0;
+
+ // level 0
+
+ // scaling
+ mLength[0] = 1.0f;
+ mLengthV[0] = 0;
+ mTaper[0] = 1.0f;
+
+ // stem splits
+ mSegSplits[0] = 0.15f;
+ mSplitAngle[0] = 15.0f;
+ mSplitAngleV[0] = 10.0f;
+
+ mVertices[0] = 5;
+
+ // curvature
+ mCurveRes[0] = 4;
+ mCurve[0] = 0;
+ mCurveV[0] = 25;
+ mCurveBack[0] = 0;
+
+ // level 1
+
+ // scaling
+ mLength[1] = .3f;
+ mLengthV[1] = 0.05f;
+ mTaper[1] = 1.0f;
+
+ // angle params
+ mDownAngle[0] = 60.0f;
+ mDownAngleV[0] = 20.0f;
+ mRotate[0] = 140.0f;
+ mRotateV[0] = 0.0f;
+ mBranches[0] = 35;
+
+ mVertices[1] = 3;
+
+ // stem splits
+ mSplitAngle[1] = 0.0f;
+ mSplitAngleV[1] = 0.0f;
+ mSegSplits[1] = 0.0f;
+
+ // curvature
+ mCurveRes[1] = 4;
+ mCurve[1] = 0;
+ mCurveV[1] = 0;
+ mCurveBack[1] = 40;
+
+ // level 2
+ mLength[2] = .6f;
+ mLengthV[2] = .1f;
+ mTaper[2] = 1;
+
+ mDownAngle[1] = 30;
+ mDownAngleV[1] = 10;
+ mRotate[1] = 140;
+ mRotateV[1] = 0;
+
+ mBranches[1] = 20;
+ mVertices[2] = 3;
+
+ mSplitAngle[2] = 0;
+ mSplitAngleV[2] = 0;
+ mSegSplits[2] = 0;
+
+ mCurveRes[2] = 3;
+ mCurve[2] = 10;
+ mCurveV[2] = 150;
+ mCurveBack[2] = 0;
+
+ // level 3
+ mLength[3] = .4f;
+ mLengthV[3] = 0;
+ mTaper[3] = 1;
+
+ mDownAngle[2] = 45;
+ mDownAngleV[2] = 10;
+ mRotate[2] = 140;
+ mRotateV[2] = 0;
+
+ mBranches[2] = 5;
+ mVertices[3] = 3;
+
+
+ mSplitAngle[3] = 0;
+ mSplitAngleV[3] = 0;
+ mSegSplits[3] = 0;
+
+ mCurveRes[3] = 2;
+ mCurve[3] = 0;
+ mCurveV[3] = 0;
+ mCurveBack[3] = 0;
+
+ mLeaves = 0;
+ mLeafScaleX = 1.0f;
+ mLeafScaleY = 1.0f;
+
+ mLeafQuality = 1.25;
+}
+
+LLTreeParams::~LLTreeParams()
+{
+
+}
+
+F32 LLTreeParams::ShapeRatio(EShapeRatio shape, F32 ratio)
+{
+ switch (shape) {
+ case (SR_CONICAL):
+ return (.2f + .8f * ratio);
+ case (SR_SPHERICAL):
+ return (.2f + .8f * sinf(F_PI*ratio));
+ case (SR_HEMISPHERICAL):
+ return (.2f + .8f * sinf(.5*F_PI*ratio));
+ case (SR_CYLINDRICAL):
+ return (1);
+ case (SR_TAPERED_CYLINDRICAL):
+ return (.5f + .5f * ratio);
+ case (SR_FLAME):
+ if (ratio <= .7f) {
+ return ratio/.7f;
+ } else {
+ return ((1 - ratio)/.3f);
+ }
+ case (SR_INVERSE_CONICAL):
+ return (1 - .8f * ratio);
+ case (SR_TEND_FLAME):
+ if (ratio <= .7) {
+ return (.5f + .5f*(ratio/.7f));
+ } else {
+ return (.5f + .5f * (1 - ratio)/.3f);
+ }
+ case (SR_ENVELOPE):
+ return 1;
+ default:
+ return 1;
+ }
+}
diff --git a/indra/llprimitive/lltreeparams.h b/indra/llprimitive/lltreeparams.h
new file mode 100644
index 0000000000..fa55f584e3
--- /dev/null
+++ b/indra/llprimitive/lltreeparams.h
@@ -0,0 +1,183 @@
+/**
+ * @file lltreeparams.h
+ * @brief Implementation of the LLTreeParams class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTREEPARAMS_H
+#define LL_LLTREEPARAMS_H
+
+/* for information about formulas associated with each type
+ * check the Weber + Penn paper
+ */
+typedef enum EShapeRatio { SR_CONICAL, SR_SPHERICAL, SR_HEMISPHERICAL,
+ SR_CYLINDRICAL, SR_TAPERED_CYLINDRICAL, SR_FLAME,
+ SR_INVERSE_CONICAL, SR_TEND_FLAME, SR_ENVELOPE};
+
+const U32 TREE_BLOCK_SIZE = 16;
+
+const U8 MAX_NUM_LEVELS = 4;
+
+class LLTreeParams
+{
+public:
+ LLTreeParams();
+ virtual ~LLTreeParams();
+
+ static F32 ShapeRatio(EShapeRatio shape, F32 ratio);
+
+public:
+
+ // Variables with an asterick (*) cannot be modified without a re-instancing the
+ // trunk/branches
+
+ // Variables with an exclamation point (!) should probably not be modified outside and instead
+ // be tied directly to the species
+
+ // Variables with a tilde (~) should be tied to a range specified by the
+ // species type but still slightly controllable by the user
+
+ // GENERAL
+
+ //! determines length/radius of branches on tree -- ie: general 'shape'
+ EShapeRatio mShape;
+
+ //! number of recursive branch levels...limit to MAX_NUM_LEVELS
+ U8 mLevels;
+
+ //~ percentage of trunk at bottom without branches
+ F32 mBaseSize;
+
+ //~ the general scale + variance of tree
+ F32 mScale, mScaleV;
+
+ // general scale of tree
+ F32 mScale0, mScaleV0;
+
+
+
+ // LOBING
+
+ //*! number of peaks in the radial distance about the perimeter
+ U8 mLobes;
+ // even numbers = obvius symmetry ... use odd numbers
+
+ //*! magnitude of the variations as a fraction of the radius
+ F32 mLobeDepth;
+
+
+
+ // FLARE
+
+ //*! causes exponential expansion near base of trunk
+ F32 mFlare;
+ // scales radius base by min 1 to '1 + flare'
+
+ //*! percentage of the height of the trunk to flair -- likely less than baseSize
+ F32 mFlarePercentage;
+
+ //*! number of cross sections to make for the flair
+ U8 mFlareRes;
+
+
+
+ // LEAVES
+
+ //~ number of leaves to make
+ U8 mLeaves;
+
+ //! scale of the leaves
+ F32 mLeafScaleX, mLeafScaleY;
+
+ // quality/density of leaves
+ F32 mLeafQuality;
+
+ // several params don't have level 0 values
+
+ // BRANCHES
+
+ //~ angle away from parent
+ F32 mDownAngle[MAX_NUM_LEVELS - 1];
+ F32 mDownAngleV[MAX_NUM_LEVELS - 1];
+
+ //~ rotation around parent
+ F32 mRotate[MAX_NUM_LEVELS - 1];
+ F32 mRotateV[MAX_NUM_LEVELS - 1];
+
+ //~ num branches to spawn
+ U8 mBranches[MAX_NUM_LEVELS - 1];
+
+ //~ fractional length of branch. 1 = same length as parent branch
+ F32 mLength[MAX_NUM_LEVELS];
+ F32 mLengthV[MAX_NUM_LEVELS];
+
+ //!~ ratio and ratiopower determine radius/length
+ F32 mRatio, mRatioPower;
+
+ //*! taper of branches
+ F32 mTaper[MAX_NUM_LEVELS];
+ // 0 - non-tapering cylinder
+ // 1 - taper to a point
+ // 2 - taper to a spherical end
+ // 3 - periodic tapering (concatenated spheres)
+
+ //! SEG SPLITTING
+ U8 mBaseSplits; //! num segsplits at first curve cross section of trunk
+ F32 mSegSplits[MAX_NUM_LEVELS]; //~ splits per cross section. 1 = 1 split per section
+ F32 mSplitAngle[MAX_NUM_LEVELS]; //~ angle that splits go from parent (tempered by height)
+ F32 mSplitAngleV[MAX_NUM_LEVELS]; //~ variance of the splits
+
+ // CURVE
+ F32 mCurve[MAX_NUM_LEVELS]; //* general, 1-axis, overall curve of branch
+ F32 mCurveV[MAX_NUM_LEVELS]; //* curve variance at each cross section from general overall curve
+ U8 mCurveRes[MAX_NUM_LEVELS]; //* number of cross sections for curve
+ F32 mCurveBack[MAX_NUM_LEVELS]; //* curveback is amount branch curves back towards
+
+ // vertices per cross section
+ U8 mVertices[MAX_NUM_LEVELS];
+
+ // * no longer useful with pre-instanced branches
+ // specifies upward tendency of branches.
+ //F32 mAttractionUp;
+ // 1 = each branch will slightly go upwards by the end of the branch
+ // >1 = branches tend to go upwards earlier in their length
+ // pruning not implemented
+ // Prune parameters
+ //F32 mPruneRatio;
+ //F32 mPruneWidth, mPruneWidthPeak;
+ //F32 mPrunePowerLow, mPrunePowerHigh;
+
+
+ // NETWORK MESSAGE DATA
+ // Below is the outline for network messages regarding trees.
+ // The general idea is that a user would pick a general 'tree type' (the first variable)
+ // and then several 'open ended' variables like 'branchiness' and 'leafiness'.
+ // The effect that each of these general user variables would then affect the actual
+ // tree parameters (like # branches, # segsplits) in different ways depending on
+ // the tree type selected. Essentially, each tree type should have a formula
+ // that expands the 'leafiness' and 'branchiness' user variables into actual
+ // values for the tree parameters.
+
+ // These formulas aren't made yet and will certainly require some tuning. The
+ // estimates below for the # bits required seems like a good guesstimate.
+
+ // VARIABLE - # bits (range) - VARIABLES AFFECTED
+ // tree type - 5 bits (32) -
+ // branches - 6 bits (64) - numBranches
+ // splits - 6 bits (64) - segsplits
+ // leafiness - 3 bits (8) - numLeaves
+ // branch spread - 5 bits (32) - splitAngle(V), rotate(V)
+ // angle - 5 bits (32) - downAngle(V)
+ // branch length - 6 bits (64) - branchlength(V)
+ // randomness - 7 bits (128) - percentage for randomness of the (V)'s
+ // basesize - 5 bits (32) - basesize
+
+ // total - 48 bits
+
+ //U8 mNetSpecies;
+
+};
+
+#endif
diff --git a/indra/llprimitive/llvolumemessage.cpp b/indra/llprimitive/llvolumemessage.cpp
new file mode 100644
index 0000000000..d2f1e12526
--- /dev/null
+++ b/indra/llprimitive/llvolumemessage.cpp
@@ -0,0 +1,534 @@
+/**
+ * @file llvolumemessage.cpp
+ * @brief LLVolumeMessage base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "message.h"
+#include "llvolumemessage.h"
+#include "lldatapacker.h"
+
+//============================================================================
+
+// LLVolumeMessage is just a wrapper class; all members are static
+
+//============================================================================
+
+bool LLVolumeMessage::packProfileParams(
+ const LLProfileParams* params,
+ LLMessageSystem *mesgsys)
+{
+ // Default to cylinder
+ static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U8(0), U8(0), U8(0));
+
+ if (!params)
+ params = &defaultparams;
+
+ U8 tempU8;
+ tempU8 = params->getCurveType();
+ mesgsys->addU8Fast(_PREHASH_ProfileCurve, tempU8);
+
+ tempU8 = (U8) llround( params->getBegin() / CUT_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_ProfileBegin, tempU8);
+
+ tempU8 = 200 - (U8) llround(params->getEnd() / CUT_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_ProfileEnd, tempU8);
+
+ tempU8 = (S8) llround(params->getHollow() / SHEAR_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_ProfileHollow, tempU8);
+
+ return true;
+}
+
+bool LLVolumeMessage::packProfileParams(
+ const LLProfileParams* params,
+ LLDataPacker &dp)
+{
+ // Default to cylinder
+ static LLProfileParams defaultparams(LL_PCODE_PROFILE_CIRCLE, U8(0), U8(0), U8(0));
+
+ if (!params)
+ params = &defaultparams;
+
+ U8 tempU8;
+ tempU8 = params->getCurveType();
+ dp.packU8(tempU8, "Curve");
+
+ tempU8 = (U8) llround( params->getBegin() / CUT_QUANTA);
+ dp.packU8(tempU8, "Begin");
+
+ tempU8 = 200 - (U8) llround(params->getEnd() / CUT_QUANTA);
+ dp.packU8(tempU8, "End");
+
+ tempU8 = (S8) llround(params->getHollow() / SHEAR_QUANTA);
+ dp.packU8(tempU8, "Hollow");
+ return true;
+}
+
+bool LLVolumeMessage::unpackProfileParams(
+ LLProfileParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num)
+{
+ bool ok = true;
+ U8 temp_u8;
+ F32 temp_f32;
+
+ mesgsys->getU8Fast(block_name, _PREHASH_ProfileCurve, temp_u8, block_num);
+ params->setCurveType(temp_u8);
+
+ mesgsys->getU8Fast(block_name, _PREHASH_ProfileBegin, temp_u8, block_num);
+ temp_f32 = temp_u8 * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile begin out of range: " << temp_f32
+ << ". Clamping to 0.0." << llendl;
+ temp_f32 = 0.f;
+ ok = false;
+ }
+ params->setBegin(temp_f32);
+
+ mesgsys->getU8Fast(block_name, _PREHASH_ProfileEnd, temp_u8, block_num);
+ temp_f32 = temp_u8 * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile end out of range: " << 1.f - temp_f32
+ << ". Clamping to 1.0." << llendl;
+ temp_f32 = 1.f;
+ ok = false;
+ }
+ params->setEnd(1.f - temp_f32);
+
+ mesgsys->getU8Fast(block_name, _PREHASH_ProfileHollow, temp_u8, block_num);
+ temp_f32 = temp_u8 * SCALE_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile hollow out of range: " << temp_f32
+ << ". Clamping to 0.0." << llendl;
+ temp_f32 = 0.f;
+ ok = false;
+ }
+ params->setHollow(temp_f32);
+
+ /*
+ llinfos << "Unpacking Profile Block " << block_num << llendl;
+ llinfos << "Curve: " << (U32)getCurve() << llendl;
+ llinfos << "Begin: " << getBegin() << llendl;
+ llinfos << "End: " << getEnd() << llendl;
+ llinfos << "Hollow: " << getHollow() << llendl;
+ */
+ return ok;
+
+}
+
+bool LLVolumeMessage::unpackProfileParams(
+ LLProfileParams* params,
+ LLDataPacker &dp)
+{
+ bool ok = true;
+ U8 temp_u8;
+ F32 temp_f32;
+
+ dp.unpackU8(temp_u8, "Curve");
+ params->setCurveType(temp_u8);
+
+ dp.unpackU8(temp_u8, "Begin");
+ temp_f32 = temp_u8 * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile begin out of range: " << temp_f32 << llendl;
+ llwarns << "Clamping to 0.0" << llendl;
+ temp_f32 = 0.f;
+ ok = false;
+ }
+ params->setBegin(temp_f32);
+
+ dp.unpackU8(temp_u8, "End");
+ temp_f32 = temp_u8 * CUT_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile end out of range: " << 1.f - temp_f32 << llendl;
+ llwarns << "Clamping to 1.0" << llendl;
+ temp_f32 = 1.f;
+ ok = false;
+ }
+ params->setEnd(1.f - temp_f32);
+
+ dp.unpackU8(temp_u8, "Hollow");
+ temp_f32 = temp_u8 * SCALE_QUANTA;
+ if (temp_f32 > 1.f)
+ {
+ llwarns << "Profile hollow out of range: " << temp_f32 << llendl;
+ llwarns << "Clamping to 0.0" << llendl;
+ temp_f32 = 0.f;
+ ok = false;
+ }
+ params->setHollow(temp_f32);
+
+ return ok;
+}
+
+//============================================================================
+
+// Quantization:
+// For cut begin, range is 0 to 1, quanta is 0.005, 0 maps to 0
+// For cut end, range is 0 to 1, quanta is 0.005, 1 maps to 0
+// For scale, range is 0 to 1, quanta is 0.01, 0 maps to 0, 1 maps to 100
+// For shear, range is -0.5 to 0.5, quanta is 0.01, 0 maps to 0
+// For taper, range is -1 to 1, quanta is 0.01, 0 maps to 0
+bool LLVolumeMessage::packPathParams(
+ const LLPathParams* params,
+ LLMessageSystem *mesgsys)
+{
+ // Default to cylinder with no cut, top same size as bottom, no shear, no twist
+ static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0);
+ if (!params)
+ params = &defaultparams;
+
+ U8 curve = params->getCurveType();
+ mesgsys->addU8Fast(_PREHASH_PathCurve, curve);
+
+ U8 begin = (U8) llround(params->getBegin() / SCALE_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_PathBegin, begin);
+
+ U8 end = 100 - (U8) llround(params->getEnd() / SCALE_QUANTA);
+ mesgsys->addU8Fast(_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);
+ mesgsys->addU8Fast(_PREHASH_PathScaleX, pack_scale_x );
+
+ U8 pack_scale_y = 200 - (U8) llround(params->getScaleY() / SCALE_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_PathScaleY, pack_scale_y );
+
+ U8 pack_shear_x = (U8) llround(params->getShearX() / SHEAR_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_PathShearX, pack_shear_x );
+
+ U8 pack_shear_y = (U8) llround(params->getShearY() / SHEAR_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_PathShearY, pack_shear_y );
+
+ S8 twist = (S8) llround(params->getTwist() / SCALE_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathTwist, twist);
+
+ S8 twist_begin = (S8) llround(params->getTwistBegin() / SCALE_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathTwistBegin, twist_begin);
+
+ S8 radius_offset = (S8) llround(params->getRadiusOffset() / SCALE_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathRadiusOffset, radius_offset);
+
+ S8 taper_x = (S8) llround(params->getTaperX() / TAPER_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathTaperX, taper_x);
+
+ S8 taper_y = (S8) llround(params->getTaperY() / TAPER_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathTaperY, taper_y);
+
+ U8 revolutions = (U8) llround( (params->getRevolutions() - 1.0f) / REV_QUANTA);
+ mesgsys->addU8Fast(_PREHASH_PathRevolutions, revolutions);
+
+ S8 skew = (S8) llround(params->getSkew() / SCALE_QUANTA);
+ mesgsys->addS8Fast(_PREHASH_PathSkew, skew);
+
+ return true;
+}
+
+bool LLVolumeMessage::packPathParams(
+ const LLPathParams* params,
+ LLDataPacker &dp)
+{
+ // Default to cylinder with no cut, top same size as bottom, no shear, no twist
+ static LLPathParams defaultparams(LL_PCODE_PATH_LINE, U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), U8(0), 0);
+ if (!params)
+ params = &defaultparams;
+
+ U8 curve = params->getCurveType();
+ dp.packU8(curve, "Curve");
+
+ U8 begin = (U8) llround(params->getBegin() / SCALE_QUANTA);
+ dp.packU8(begin, "Begin");
+
+ U8 end = 100 - (U8) llround(params->getEnd() / SCALE_QUANTA);
+ dp.packU8(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);
+ dp.packU8(pack_scale_x, "ScaleX");
+
+ U8 pack_scale_y = 200 - (U8) llround(params->getScaleY() / SCALE_QUANTA);
+ dp.packU8(pack_scale_y, "ScaleY");
+
+ S8 pack_shear_x = (S8) llround(params->getShearX() / SHEAR_QUANTA);
+ dp.packU8(*(U8 *)&pack_shear_x, "ShearX");
+
+ S8 pack_shear_y = (S8) llround(params->getShearY() / SHEAR_QUANTA);
+ dp.packU8(*(U8 *)&pack_shear_y, "ShearY");
+
+ S8 twist = (S8) llround(params->getTwist() / SCALE_QUANTA);
+ dp.packU8(*(U8 *)&twist, "Twist");
+
+ S8 twist_begin = (S8) llround(params->getTwistBegin() / SCALE_QUANTA);
+ dp.packU8(*(U8 *)&twist_begin, "TwistBegin");
+
+ S8 radius_offset = (S8) llround(params->getRadiusOffset() / SCALE_QUANTA);
+ dp.packU8(*(U8 *)&radius_offset, "RadiusOffset");
+
+ S8 taper_x = (S8) llround(params->getTaperX() / TAPER_QUANTA);
+ dp.packU8(*(U8 *)&taper_x, "TaperX");
+
+ S8 taper_y = (S8) llround(params->getTaperY() / TAPER_QUANTA);
+ dp.packU8(*(U8 *)&taper_y, "TaperY");
+
+ U8 revolutions = (U8) llround( (params->getRevolutions() - 1.0f) / REV_QUANTA);
+ dp.packU8(*(U8 *)&revolutions, "Revolutions");
+
+ S8 skew = (S8) llround(params->getSkew() / SCALE_QUANTA);
+ dp.packU8(*(U8 *)&skew, "Skew");
+
+ return true;
+}
+
+bool LLVolumeMessage::unpackPathParams(
+ LLPathParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num)
+{
+ U8 curve;
+ mesgsys->getU8Fast(block_name, _PREHASH_PathCurve, curve, block_num);
+ params->setCurveType(curve);
+
+ U8 begin;
+ mesgsys->getU8Fast(block_name, _PREHASH_PathBegin, begin, block_num);
+ params->setBegin((F32)(begin * SCALE_QUANTA));
+
+ U8 end;
+ mesgsys->getU8Fast(block_name, _PREHASH_PathEnd, end, block_num);
+ params->setEnd((F32)((100 - end) * SCALE_QUANTA));
+
+ U8 pack_scale_x, pack_scale_y;
+ mesgsys->getU8Fast(block_name, _PREHASH_PathScaleX, pack_scale_x, block_num);
+ mesgsys->getU8Fast(block_name, _PREHASH_PathScaleY, pack_scale_y, block_num);
+ F32 x = (F32) (200 - pack_scale_x) * SCALE_QUANTA;
+ F32 y = (F32) (200 - pack_scale_y) * SCALE_QUANTA;
+ params->setScale( x, y );
+
+ S8 shear_x_quant, shear_y_quant;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathShearX, shear_x_quant, block_num);
+ mesgsys->getS8Fast(block_name, _PREHASH_PathShearY, shear_y_quant, block_num);
+ F32 shear_x = (F32) shear_x_quant * SHEAR_QUANTA;
+ F32 shear_y = (F32) shear_y_quant * SHEAR_QUANTA;
+ params->setShear( shear_x, shear_y );
+
+ S8 twist;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathTwist, twist, block_num );
+ params->setTwist((F32)(twist * SCALE_QUANTA));
+
+ S8 twist_begin;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathTwistBegin, twist_begin, block_num );
+ params->setTwistBegin((F32)(twist_begin * SCALE_QUANTA));
+
+ S8 radius_offset;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathRadiusOffset, radius_offset, block_num );
+ params->setRadiusOffset((F32)(radius_offset * SCALE_QUANTA));
+
+ S8 taper_x_quant, taper_y_quant;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathTaperX, taper_x_quant, block_num );
+ mesgsys->getS8Fast(block_name, _PREHASH_PathTaperY, taper_y_quant, block_num );
+ F32 taper_x = (F32)(taper_x_quant * TAPER_QUANTA);
+ F32 taper_y = (F32)(taper_y_quant * TAPER_QUANTA);
+ params->setTaper( taper_x, taper_y );
+
+ U8 revolutions;
+ mesgsys->getU8Fast(block_name, _PREHASH_PathRevolutions, revolutions, block_num );
+ params->setRevolutions((F32)(revolutions * REV_QUANTA + 1.0f));
+
+ S8 skew;
+ mesgsys->getS8Fast(block_name, _PREHASH_PathSkew, skew, block_num );
+ params->setSkew((F32)(skew * SCALE_QUANTA));
+
+/*
+ llinfos << "Unpacking Path Block " << block_num << llendl;
+ llinfos << "Curve: " << (U32)params->getCurve() << llendl;
+ llinfos << "Begin: " << params->getBegin() << llendl;
+ llinfos << "End: " << params->getEnd() << llendl;
+ llinfos << "Scale: " << params->getScale() << llendl;
+ llinfos << "Twist: " << params->getTwist() << llendl;
+*/
+
+ return true;
+
+}
+
+bool LLVolumeMessage::unpackPathParams(LLPathParams* params, LLDataPacker &dp)
+{
+ U8 value;
+ S8 svalue;
+ dp.unpackU8(value, "Curve");
+ params->setCurveType( value );
+
+ dp.unpackU8(value, "Begin");
+ params->setBegin((F32)(value * SCALE_QUANTA));
+
+ dp.unpackU8(value, "End");
+ params->setEnd((F32)((100 - value) * SCALE_QUANTA));
+
+ dp.unpackU8(value, "ScaleX");
+ F32 x = (F32) (200 - value) * SCALE_QUANTA;
+ dp.unpackU8(value, "ScaleY");
+ F32 y = (F32) (200 - value) * SCALE_QUANTA;
+ params->setScale( x, y );
+
+ dp.unpackU8(value, "ShearX");
+ svalue = *(S8 *)&value;
+ F32 shear_x = (F32) svalue * SHEAR_QUANTA;
+ dp.unpackU8(value, "ShearY");
+ svalue = *(S8 *)&value;
+ F32 shear_y = (F32) svalue * SHEAR_QUANTA;
+ params->setShear( shear_x, shear_y );
+
+ dp.unpackU8(value, "Twist");
+ svalue = *(S8 *)&value;
+ params->setTwist((F32)(svalue * SCALE_QUANTA));
+
+ dp.unpackU8(value, "TwistBegin");
+ svalue = *(S8 *)&value;
+ params->setTwistBegin((F32)(svalue * SCALE_QUANTA));
+
+ dp.unpackU8(value, "RadiusOffset");
+ svalue = *(S8 *)&value;
+ params->setRadiusOffset((F32)(svalue * SCALE_QUANTA));
+
+ dp.unpackU8(value, "TaperX");
+ svalue = *(S8 *)&value;
+ params->setTaperX((F32)(svalue * TAPER_QUANTA));
+
+ dp.unpackU8(value, "TaperY");
+ svalue = *(S8 *)&value;
+ params->setTaperY((F32)(svalue * TAPER_QUANTA));
+
+ dp.unpackU8(value, "Revolutions");
+ params->setRevolutions((F32)(value * REV_QUANTA + 1.0f));
+
+ dp.unpackU8(value, "Skew");
+ svalue = *(S8 *)&value;
+ params->setSkew((F32)(svalue * SCALE_QUANTA));
+
+ return true;
+}
+
+//============================================================================
+
+// static
+bool LLVolumeMessage::constrainVolumeParams(LLVolumeParams& params)
+{
+ bool ok = true;
+
+ // This is called immediately after an unpack. feed the raw data
+ // through the checked setters to constraint it to a valid set of
+ // volume params.
+ ok &= params.setType(
+ params.getProfileParams().getCurveType(),
+ params.getPathParams().getCurveType());
+ ok &= params.setBeginAndEndS(
+ params.getProfileParams().getBegin(),
+ params.getProfileParams().getEnd());
+ ok &= params.setBeginAndEndT(
+ params.getPathParams().getBegin(),
+ params.getPathParams().getEnd());
+ ok &= params.setHollow(params.getProfileParams().getHollow());
+ ok &= params.setTwistBegin(params.getPathParams().getTwistBegin());
+ ok &= params.setTwistEnd(params.getPathParams().getTwistEnd());
+ ok &= params.setRatio(
+ params.getPathParams().getScaleX(),
+ params.getPathParams().getScaleY());
+ ok &= params.setShear(
+ params.getPathParams().getShearX(),
+ params.getPathParams().getShearY());
+ ok &= params.setTaper(
+ params.getPathParams().getTaperX(),
+ params.getPathParams().getTaperY());
+ ok &= params.setRevolutions(params.getPathParams().getRevolutions());
+ ok &= params.setRadiusOffset(params.getPathParams().getRadiusOffset());
+ ok &= params.setSkew(params.getPathParams().getSkew());
+ if(!ok)
+ {
+ llwarns << "LLVolumeMessage::constrainVolumeParams() - "
+ << "forced to constrain incoming volume params." << llendl;
+ }
+ return ok;
+}
+
+bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLMessageSystem *mesgsys)
+{
+ // llinfos << "pack volume" << llendl;
+ if (params)
+ {
+ packPathParams(&params->getPathParams(), mesgsys);
+ packProfileParams(&params->getProfileParams(), mesgsys);
+ }
+ else
+ {
+ packPathParams(0, mesgsys);
+ packProfileParams(0, mesgsys);
+ }
+ return true;
+}
+
+bool LLVolumeMessage::packVolumeParams(const LLVolumeParams* params, LLDataPacker &dp)
+{
+ // llinfos << "pack volume" << llendl;
+ if (params)
+ {
+ packPathParams(&params->getPathParams(), dp);
+ packProfileParams(&params->getProfileParams(), dp);
+ }
+ else
+ {
+ packPathParams(0, dp);
+ packProfileParams(0, dp);
+ }
+ return true;
+}
+
+bool LLVolumeMessage::unpackVolumeParams(
+ LLVolumeParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num)
+{
+ bool ok = true;
+ ok &= unpackPathParams(
+ &params->getPathParams(),
+ mesgsys,
+ block_name,
+ block_num);
+ ok &= unpackProfileParams(
+ &params->getProfileParams(),
+ mesgsys,
+ block_name,
+ block_num);
+ ok &= constrainVolumeParams(*params);
+
+ return ok;
+}
+
+bool LLVolumeMessage::unpackVolumeParams(
+ LLVolumeParams* params,
+ LLDataPacker &dp)
+{
+ bool ok = true;
+ ok &= unpackPathParams(&params->getPathParams(), dp);
+ ok &= unpackProfileParams(&params->getProfileParams(), dp);
+ ok &= constrainVolumeParams(*params);
+ return ok;
+}
+
+//============================================================================
diff --git a/indra/llprimitive/llvolumemessage.h b/indra/llprimitive/llvolumemessage.h
new file mode 100644
index 0000000000..299d5813a0
--- /dev/null
+++ b/indra/llprimitive/llvolumemessage.h
@@ -0,0 +1,74 @@
+/**
+ * @file llvolumemessage.h
+ * @brief LLVolumeMessage base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUMEMESSAGE_H
+#define LL_LLVOLUMEMESSAGE_H
+
+#include "llvolume.h"
+
+class LLMessageSystem;
+class LLDataPacker;
+
+// wrapper class for some volume/message functions
+class LLVolumeMessage
+{
+protected:
+ // The profile and path params are protected since they do not do
+ // any kind of parameter validation or clamping. Use the public
+ // pack and unpack volume param methods below
+
+ static bool packProfileParams(
+ const LLProfileParams* params,
+ LLMessageSystem* mesgsys);
+ static bool packProfileParams(
+ const LLProfileParams* params,
+ LLDataPacker& dp);
+ static bool unpackProfileParams(
+ LLProfileParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num = 0);
+ static bool unpackProfileParams(LLProfileParams* params, LLDataPacker& dp);
+
+ static bool packPathParams(
+ const LLPathParams* params,
+ LLMessageSystem* mesgsys);
+ static bool packPathParams(const LLPathParams* params, LLDataPacker& dp);
+ static bool unpackPathParams(
+ LLPathParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num = 0);
+ static bool unpackPathParams(LLPathParams* params, LLDataPacker& dp);
+
+public:
+ /**
+ * @brief This method constrains any volume params to make them valid.
+ *
+ * @param[in,out] Possibly invalid params in, always valid out.
+ * @return Returns true if the in params were valid, and therefore
+ * unchanged.
+ */
+ static bool constrainVolumeParams(LLVolumeParams& params);
+
+ static bool packVolumeParams(
+ const LLVolumeParams* params,
+ LLMessageSystem* mesgsys);
+ static bool packVolumeParams(
+ const LLVolumeParams* params,
+ LLDataPacker& dp);
+ static bool unpackVolumeParams(
+ LLVolumeParams* params,
+ LLMessageSystem* mesgsys,
+ char* block_name,
+ S32 block_num = 0);
+ static bool unpackVolumeParams(LLVolumeParams* params, LLDataPacker &dp);
+};
+
+#endif // LL_LLVOLUMEMESSAGE_H
+
diff --git a/indra/llprimitive/llvolumexml.cpp b/indra/llprimitive/llvolumexml.cpp
new file mode 100644
index 0000000000..3c9d4d3b39
--- /dev/null
+++ b/indra/llprimitive/llvolumexml.cpp
@@ -0,0 +1,57 @@
+/**
+ * @file llvolumexml.cpp
+ * @brief LLVolumeXml base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llvolumexml.h"
+
+//============================================================================
+
+// LLVolumeXml is just a wrapper class; all members are static
+
+//============================================================================
+
+LLXMLNode *LLVolumeXml::exportProfileParams(const LLProfileParams* params)
+{
+ LLXMLNode *ret = new LLXMLNode("profile", FALSE);
+
+ ret->createChild("curve_type", TRUE)->setByteValue(1, &params->getCurveType());
+ ret->createChild("interval", FALSE)->setFloatValue(2, &params->getBegin());
+ ret->createChild("hollow", FALSE)->setFloatValue(1, &params->getHollow());
+
+ return ret;
+}
+
+
+LLXMLNode *LLVolumeXml::exportPathParams(const LLPathParams* params)
+{
+ LLXMLNode *ret = new LLXMLNode("path", FALSE);
+ ret->createChild("curve_type", TRUE)->setByteValue(1, &params->getCurveType());
+ ret->createChild("interval", FALSE)->setFloatValue(2, &params->getBegin());
+ ret->createChild("scale", FALSE)->setFloatValue(2, params->getScale().mV);
+ ret->createChild("shear", FALSE)->setFloatValue(2, params->getShear().mV);
+ ret->createChild("twist_interval", FALSE)->setFloatValue(2, &params->getTwistBegin());
+ ret->createChild("radius_offset", FALSE)->setFloatValue(1, &params->getRadiusOffset());
+ ret->createChild("taper", FALSE)->setFloatValue(2, params->getTaper().mV);
+ ret->createChild("revolutions", FALSE)->setFloatValue(1, &params->getRevolutions());
+ ret->createChild("skew", FALSE)->setFloatValue(1, &params->getSkew());
+
+ return ret;
+}
+
+
+LLXMLNode *LLVolumeXml::exportVolumeParams(const LLVolumeParams* params)
+{
+ LLXMLNode *ret = new LLXMLNode("shape", FALSE);
+
+ exportPathParams(&params->getPathParams())->setParent(ret);
+ exportProfileParams(&params->getProfileParams())->setParent(ret);
+
+ return ret;
+}
+
diff --git a/indra/llprimitive/llvolumexml.h b/indra/llprimitive/llvolumexml.h
new file mode 100644
index 0000000000..5d105f148a
--- /dev/null
+++ b/indra/llprimitive/llvolumexml.h
@@ -0,0 +1,27 @@
+/**
+ * @file llvolumexml.h
+ * @brief LLVolumeXml base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOLUMEXML_H
+#define LL_LLVOLUMEXML_H
+
+#include "llvolume.h"
+#include "llxmlnode.h"
+
+// wrapper class for some volume/message functions
+class LLVolumeXml
+{
+public:
+ static LLXMLNode* exportProfileParams(const LLProfileParams* params);
+
+ static LLXMLNode* exportPathParams(const LLPathParams* params);
+
+ static LLXMLNode* exportVolumeParams(const LLVolumeParams* params);
+};
+
+#endif // LL_LLVOLUMEXML_H
+
diff --git a/indra/llprimitive/material_codes.h b/indra/llprimitive/material_codes.h
new file mode 100644
index 0000000000..f9c05017c2
--- /dev/null
+++ b/indra/llprimitive/material_codes.h
@@ -0,0 +1,35 @@
+/**
+ * @file material_codes.h
+ * @brief Material_codes definitions
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MATERIAL_CODES_H
+#define LL_MATERIAL_CODES_H
+
+#include "lluuid.h"
+
+ // material types
+const U8 LL_MCODE_STONE = 0;
+const U8 LL_MCODE_METAL = 1;
+const U8 LL_MCODE_GLASS = 2;
+const U8 LL_MCODE_WOOD = 3;
+const U8 LL_MCODE_FLESH = 4;
+const U8 LL_MCODE_PLASTIC = 5;
+const U8 LL_MCODE_RUBBER = 6;
+const U8 LL_MCODE_LIGHT = 7;
+const U8 LL_MCODE_END = 8;
+const U8 LL_MCODE_MASK = 0x0F;
+
+const LLUUID LL_DEFAULT_STONE_UUID("87c5765b-aa26-43eb-b8c6-c09a1ca6208e");
+const LLUUID LL_DEFAULT_METAL_UUID("6f3c53e9-ba60-4010-8f3e-30f51a762476");
+const LLUUID LL_DEFAULT_GLASS_UUID("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d");
+const LLUUID LL_DEFAULT_WOOD_UUID("89556747-24cb-43ed-920b-47caed15465f");
+const LLUUID LL_DEFAULT_FLESH_UUID("80736669-e4b9-450e-8890-d5169f988a50");
+const LLUUID LL_DEFAULT_PLASTIC_UUID("304fcb4e-7d33-4339-ba80-76d3d22dc11a");
+const LLUUID LL_DEFAULT_RUBBER_UUID("9fae0bc5-666d-477e-9f70-84e8556ec867");
+const LLUUID LL_DEFAULT_LIGHT_UUID("00000000-0000-0000-0000-000000000000");
+
+#endif
diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp
new file mode 100644
index 0000000000..547a593447
--- /dev/null
+++ b/indra/llrender/llfontgl.cpp
@@ -0,0 +1,1434 @@
+/**
+ * @file llfontgl.cpp
+ * @brief Wrapper around FreeType
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llfont.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "v4color.h"
+
+const S32 BOLD_OFFSET = 1;
+
+// static class members
+F32 LLFontGL::sVertDPI = 96.f;
+F32 LLFontGL::sHorizDPI = 96.f;
+F32 LLFontGL::sScaleX = 1.f;
+F32 LLFontGL::sScaleY = 1.f;
+LLString LLFontGL::sAppDir;
+
+LLFontGL* LLFontGL::sMonospace = NULL;
+LLFontGL* LLFontGL::sSansSerifSmall = NULL;
+LLFontGL* LLFontGL::sSansSerif = NULL;
+LLFontGL* LLFontGL::sSansSerifBig = NULL;
+LLFontGL* LLFontGL::sSansSerifHuge = NULL;
+LLFontGL* LLFontGL::sSansSerifBold = NULL;
+LLFontList* LLFontGL::sSSFallback = NULL;
+LLFontList* LLFontGL::sSSSmallFallback = NULL;
+LLFontList* LLFontGL::sSSBigFallback = NULL;
+LLFontList* LLFontGL::sSSHugeFallback = NULL;
+LLFontList* LLFontGL::sSSBoldFallback = NULL;
+LLColor4 LLFontGL::sShadowColor(0.f, 0.f, 0.f, 1.f);
+
+LLCoordFont LLFontGL::sCurOrigin;
+std::vector<LLCoordFont> LLFontGL::sOriginStack;
+
+LLFontGL*& gExtCharFont = LLFontGL::sSansSerif;
+
+const F32 EXT_X_BEARING = 1.f;
+const F32 EXT_Y_BEARING = 0.f;
+const F32 EXT_KERNING = 1.f;
+const F32 PIXEL_BORDER_THRESHOLD = 0.0001f;
+const F32 PIXEL_CORRECTION_DISTANCE = 0.01f;
+
+const F32 PAD_AMT = 0.5f;
+
+F32 llfont_round_x(F32 x)
+{
+ //return llfloor((x-LLFontGL::sCurOrigin.mX)/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleX+LLFontGL::sCurOrigin.mX;
+ //return llfloor(x/LLFontGL::sScaleX+0.5f)*LLFontGL::sScaleY;
+ return x;
+}
+
+F32 llfont_round_y(F32 y)
+{
+ //return llfloor((y-LLFontGL::sCurOrigin.mY)/LLFontGL::sScaleY+0.5f)*LLFontGL::sScaleY+LLFontGL::sCurOrigin.mY;
+ //return llfloor(y+0.5f);
+ return y;
+}
+
+// static
+U8 LLFontGL::getStyleFromString(const LLString &style)
+{
+ S32 ret = 0;
+ if (style.find("NORMAL") != style.npos)
+ {
+ ret |= NORMAL;
+ }
+ if (style.find("BOLD") != style.npos)
+ {
+ ret |= BOLD;
+ }
+ if (style.find("ITALIC") != style.npos)
+ {
+ ret |= ITALIC;
+ }
+ if (style.find("UNDERLINE") != style.npos)
+ {
+ ret |= UNDERLINE;
+ }
+ return ret;
+}
+
+LLFontGL::LLFontGL()
+ : LLFont()
+{
+ init();
+ clearEmbeddedChars();
+}
+
+LLFontGL::LLFontGL(const LLFontGL &source)
+{
+ llerrs << "Not implemented!" << llendl;
+}
+
+LLFontGL::~LLFontGL()
+{
+ mImageGLp = NULL;
+ mRawImageGLp = NULL;
+ clearEmbeddedChars();
+}
+
+void LLFontGL::init()
+{
+ if (mImageGLp.isNull())
+ {
+ mImageGLp = new LLImageGL(FALSE);
+ //RN: use nearest mipmap filtering to obviate the need to do pixel-accurate positioning
+ mImageGLp->bind();
+ mImageGLp->setMipFilterNearest(TRUE,TRUE);
+ }
+ if (mRawImageGLp.isNull())
+ {
+ mRawImageGLp = new LLImageRaw; // Note LLFontGL owns the image, not LLFont.
+ }
+ setRawImage( mRawImageGLp );
+}
+
+void LLFontGL::reset()
+{
+ init();
+ resetBitmap();
+}
+
+// static
+LLString LLFontGL::getFontPathSystem()
+{
+ LLString system_path;
+
+ // Try to figure out where the system's font files are stored.
+ char *system_root = NULL;
+#if LL_WINDOWS
+ system_root = getenv("SystemRoot");
+ if (!system_root)
+ {
+ llwarns << "SystemRoot not found, attempting to load fonts from default path." << llendl;
+ }
+#endif
+
+ if (system_root)
+ {
+ system_path = llformat("%s/fonts/", system_root);
+ }
+ else
+ {
+#if LL_WINDOWS
+ // HACK for windows 98/Me
+ system_path = "/WINDOWS/FONTS/";
+#elif LL_DARWIN
+ // HACK for Mac OS X
+ system_path = "/System/Library/Fonts/";
+#endif
+ }
+ return system_path;
+}
+
+
+// static
+LLString LLFontGL::getFontPathLocal()
+{
+ LLString local_path;
+
+ // Backup files if we can't load from system fonts directory.
+ // We could store this in an end-user writable directory to allow
+ // end users to switch fonts.
+ if (LLFontGL::sAppDir.length())
+ {
+ // use specified application dir to look for fonts
+ local_path = LLFontGL::sAppDir + "/fonts/";
+ }
+ else
+ {
+ // assume working directory is executable directory
+ local_path = "./fonts/";
+ }
+ return local_path;
+}
+
+//static
+bool LLFontGL::loadFaceFallback(LLFontList *fontlistp, const LLString& fontname, const F32 point_size)
+{
+ LLString local_path = getFontPathLocal();
+ LLString sys_path = getFontPathSystem();
+
+ // The fontname string may contain multiple font file names separated by semicolons.
+ // Break it apart and try loading each one, in order.
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(";");
+ tokenizer tokens(fontname, sep);
+ tokenizer::iterator token_iter;
+
+ for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ LLFont *fontp = new LLFont();
+ LLString font_path = local_path + *token_iter;
+ if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE))
+ {
+ font_path = sys_path + *token_iter;
+ if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI, 2, TRUE))
+ {
+ llwarns << "Couldn't load font " << *token_iter << llendl;
+ delete fontp;
+ fontp = NULL;
+ }
+ }
+
+ if(fontp)
+ {
+ fontlistp->addAtEnd(fontp);
+ }
+ }
+
+ // We want to return true if at least one fallback font loaded correctly.
+ return (fontlistp->size() > 0);
+}
+
+//static
+bool LLFontGL::loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp)
+{
+ LLString local_path = getFontPathLocal();
+ LLString font_path = local_path + fontname;
+ if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI))
+ {
+ LLString sys_path = getFontPathSystem();
+ font_path = sys_path + fontname;
+ if (!fontp->loadFace(font_path.c_str(), point_size, sVertDPI, sHorizDPI))
+ {
+ llwarns << "Couldn't load font " << fontname << llendl;
+ return false;
+ }
+ }
+
+ fontp->setFallbackFont(fallback_fontp);
+ return true;
+}
+
+
+// static
+BOOL LLFontGL::initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale,
+ const LLString& monospace_file, F32 monospace_size,
+ const LLString& sansserif_file,
+ const LLString& sanserif_fallback_file, F32 ss_fallback_scale,
+ F32 small_size, F32 medium_size, F32 big_size, F32 huge_size,
+ const LLString& sansserif_bold_file, F32 bold_size,
+ const LLString& app_dir)
+{
+ BOOL failed = FALSE;
+ sVertDPI = (F32)llfloor(screen_dpi * y_scale);
+ sHorizDPI = (F32)llfloor(screen_dpi * x_scale);
+ sScaleX = x_scale;
+ sScaleY = y_scale;
+ sAppDir = app_dir;
+
+ //
+ // Monospace font
+ //
+
+ if (!sMonospace)
+ {
+ sMonospace = new LLFontGL();
+ }
+ else
+ {
+ sMonospace->reset();
+ }
+
+ failed |= !loadFace(sMonospace, monospace_file, monospace_size, NULL);
+
+ //
+ // Sans-serif fonts
+ //
+ if(!sSansSerifHuge)
+ {
+ sSansSerifHuge = new LLFontGL();
+ }
+ else
+ {
+ sSansSerifHuge->reset();
+ }
+
+ if (!sSSHugeFallback)
+ {
+ sSSHugeFallback = new LLFontList();
+ if (!loadFaceFallback(sSSHugeFallback, sanserif_fallback_file, huge_size*ss_fallback_scale))
+ {
+ delete sSSHugeFallback;
+ sSSHugeFallback = NULL;
+ }
+ }
+
+ failed |= !loadFace(sSansSerifHuge, sansserif_file, huge_size, sSSHugeFallback);
+
+
+ if(!sSansSerifBig)
+ {
+ sSansSerifBig = new LLFontGL();
+ }
+ else
+ {
+ sSansSerifBig->reset();
+ }
+
+ if (!sSSBigFallback)
+ {
+ sSSBigFallback = new LLFontList();
+ if (!loadFaceFallback(sSSBigFallback, sanserif_fallback_file, big_size*ss_fallback_scale))
+ {
+ delete sSSBigFallback;
+ sSSBigFallback = NULL;
+ }
+ }
+
+ failed |= !loadFace(sSansSerifBig, sansserif_file, big_size, sSSBigFallback);
+
+
+ if(!sSansSerif)
+ {
+ sSansSerif = new LLFontGL();
+ }
+ else
+ {
+ sSansSerif->reset();
+ }
+
+ if (!sSSFallback)
+ {
+ sSSFallback = new LLFontList();
+ if (!loadFaceFallback(sSSFallback, sanserif_fallback_file, medium_size*ss_fallback_scale))
+ {
+ delete sSSFallback;
+ sSSFallback = NULL;
+ }
+ }
+ failed |= !loadFace(sSansSerif, sansserif_file, medium_size, sSSFallback);
+
+
+ if(!sSansSerifSmall)
+ {
+ sSansSerifSmall = new LLFontGL();
+ }
+ else
+ {
+ sSansSerifSmall->reset();
+ }
+
+ if (!sSSSmallFallback)
+ {
+ sSSSmallFallback = new LLFontList();
+ if (!loadFaceFallback(sSSSmallFallback, sanserif_fallback_file, small_size*ss_fallback_scale))
+ {
+ delete sSSSmallFallback;
+ sSSSmallFallback = NULL;
+ }
+ }
+ failed |= !loadFace(sSansSerifSmall, sansserif_file, small_size, sSSSmallFallback);
+
+
+ //
+ // Sans-serif bold
+ //
+ if(!sSansSerifBold)
+ {
+ sSansSerifBold = new LLFontGL();
+ }
+ else
+ {
+ sSansSerifBold->reset();
+ }
+
+ if (!sSSBoldFallback)
+ {
+ sSSBoldFallback = new LLFontList();
+ if (!loadFaceFallback(sSSBoldFallback, sanserif_fallback_file, medium_size*ss_fallback_scale))
+ {
+ delete sSSBoldFallback;
+ sSSBoldFallback = NULL;
+ }
+ }
+ failed |= !loadFace(sSansSerifBold, sansserif_bold_file, medium_size, sSSBoldFallback);
+
+ return !failed;
+}
+
+
+
+// static
+void LLFontGL::destroyDefaultFonts()
+{
+ delete sMonospace;
+ sMonospace = NULL;
+
+ delete sSansSerifHuge;
+ sSansSerifHuge = NULL;
+
+ delete sSansSerifBig;
+ sSansSerifBig = NULL;
+
+ delete sSansSerif;
+ sSansSerif = NULL;
+
+ delete sSansSerifSmall;
+ sSansSerifSmall = NULL;
+
+ delete sSansSerifBold;
+ sSansSerifBold = NULL;
+
+ delete sSSHugeFallback;
+ sSSHugeFallback = NULL;
+
+ delete sSSBigFallback;
+ sSSBigFallback = NULL;
+
+ delete sSSFallback;
+ sSSFallback = NULL;
+
+ delete sSSSmallFallback;
+ sSSSmallFallback = NULL;
+
+ delete sSSBoldFallback;
+ sSSBoldFallback = NULL;
+}
+
+//static
+void LLFontGL::destroyGL()
+{
+ if (!sMonospace)
+ {
+ // Already all destroyed.
+ return;
+ }
+ sMonospace->mImageGLp->destroyGLTexture();
+ sSansSerifHuge->mImageGLp->destroyGLTexture();
+ sSansSerifSmall->mImageGLp->destroyGLTexture();
+ sSansSerif->mImageGLp->destroyGLTexture();
+ sSansSerifBig->mImageGLp->destroyGLTexture();
+ sSansSerifBold->mImageGLp->destroyGLTexture();
+}
+
+
+
+LLFontGL &LLFontGL::operator=(const LLFontGL &source)
+{
+ llerrs << "Not implemented" << llendl;
+ return *this;
+}
+
+BOOL LLFontGL::loadFace(const LLString& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi)
+{
+ if (!LLFont::loadFace(filename, point_size, vert_dpi, horz_dpi, 2, FALSE))
+ {
+ return FALSE;
+ }
+ mImageGLp->createGLTexture(0, mRawImageGLp);
+ mImageGLp->bind();
+ mImageGLp->setMipFilterNearest(TRUE, TRUE);
+ return TRUE;
+}
+
+BOOL LLFontGL::addChar(const llwchar wch)
+{
+ if (!LLFont::addChar(wch))
+ {
+ return FALSE;
+ }
+
+ stop_glerror();
+ mImageGLp->setSubImage(mRawImageGLp, 0, 0, mImageGLp->getWidth(), mImageGLp->getHeight());
+ mImageGLp->bind();
+ mImageGLp->setMipFilterNearest(TRUE, TRUE);
+ stop_glerror();
+ return TRUE;
+}
+
+
+S32 LLFontGL::renderUTF8(const LLString &text, const S32 offset,
+ const F32 x, const F32 y,
+ const LLColor4 &color,
+ const HAlign halign, const VAlign valign,
+ U8 style,
+ const S32 max_chars, const S32 max_pixels,
+ F32* right_x,
+ BOOL use_ellipses) const
+{
+ LLWString wstr = utf8str_to_wstring(text);
+ return render(wstr, offset, x, y, color, halign, valign, style, max_chars, max_pixels, right_x, use_ellipses);
+}
+
+S32 LLFontGL::render(const LLWString &wstr,
+ const S32 begin_offset,
+ const F32 x, const F32 y,
+ const LLColor4 &color,
+ const HAlign halign, const VAlign valign,
+ U8 style,
+ const S32 max_chars, S32 max_pixels,
+ F32* right_x,
+ BOOL use_embedded,
+ BOOL use_ellipses) const
+{
+ LLGLEnable texture_2d(GL_TEXTURE_2D);
+
+ if (wstr.empty())
+ {
+ return 0;
+ }
+
+ if (style & DROP_SHADOW)
+ {
+ LLColor4 shadow_color = sShadowColor;
+ shadow_color[3] = color[3];
+ render(wstr, begin_offset,
+ x + 1.f / sScaleX,
+ y - 1.f / sScaleY,
+ shadow_color,
+ halign,
+ valign,
+ style & (~DROP_SHADOW),
+ max_chars,
+ max_pixels,
+ right_x,
+ use_embedded,
+ use_ellipses);
+ }
+
+ S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX);
+
+ BOOL render_bold = FALSE;
+
+ // HACK for better bolding
+ if (style & BOLD)
+ {
+ if (this == LLFontGL::sSansSerif)
+ {
+ return LLFontGL::sSansSerifBold->render(
+ wstr, begin_offset,
+ x, y,
+ color,
+ halign, valign,
+ (style & ~BOLD),
+ max_chars, max_pixels,
+ right_x, use_embedded);
+ }
+ else
+ {
+ render_bold = TRUE;
+ }
+ }
+
+ glPushMatrix();
+ glLoadIdentity();
+ glTranslatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ);
+ //glScalef(sScaleX, sScaleY, 1.0f);
+
+ // avoid half pixels
+ // RN: if we're going to this trouble, might as well snap to nearest pixel all the time
+ // but the plan is to get rid of this so that fonts "just work"
+ //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f);
+ //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD)
+ //{
+ glTranslatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f);
+ //}
+
+ // this code would just snap to pixel grid, although it seems to introduce more jitter
+ //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX);
+ //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY);
+ //glTranslatef(-pixel_offset_x, -pixel_offset_y, 0.f);
+
+ // scale back to native pixel size
+ //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f);
+ //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f);
+ LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS);
+
+ glColor4fv( color.mV );
+
+ S32 chars_drawn = 0;
+ S32 i;
+ S32 length;
+
+ if (-1 == max_chars)
+ {
+ length = (S32)wstr.length() - begin_offset;
+ }
+ else
+ {
+ length = llmin((S32)wstr.length() - begin_offset, max_chars );
+ }
+
+ F32 cur_x, cur_y, cur_render_x, cur_render_y;
+ F32 slant_offset;
+
+ slant_offset = ((style & ITALIC) ? ( -mAscender * 0.25f) : 0.f);
+
+ // Bind the font texture
+
+ mImageGLp->bind(0);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Not guaranteed to be set correctly
+
+ cur_x = ((F32)x * sScaleX);
+ cur_y = ((F32)y * sScaleY);
+
+ // Offset y by vertical alignment.
+ switch (valign)
+ {
+ case TOP:
+ cur_y -= mAscender;
+ break;
+ case BOTTOM:
+ cur_y += mDescender;
+ break;
+ case VCENTER:
+ cur_y -= ((mAscender - mDescender)/2.f);
+ break;
+ case BASELINE:
+ // Baseline, do nothing.
+ break;
+ default:
+ break;
+ }
+
+ switch (halign)
+ {
+ case LEFT:
+ break;
+ case RIGHT:
+ cur_x -= (F32)getWidth(wstr.c_str(), 0, length) * sScaleX;
+ break;
+ case HCENTER:
+ cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2;
+ break;
+ default:
+ break;
+ }
+
+ // Round properly.
+ //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY;
+ //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX;
+
+ cur_render_y = cur_y;
+ cur_render_x = cur_x;
+
+ F32 start_x = cur_x;
+
+ F32 inv_width = 1.f / mImageGLp->getWidth();
+ F32 inv_height = 1.f / mImageGLp->getHeight();
+
+ const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
+
+
+ BOOL draw_ellipses = FALSE;
+ if (use_ellipses)
+ {
+ // check for too long of a string
+ if (getWidthF32(wstr.c_str(), 0, max_chars) > scaled_max_pixels)
+ {
+ const LLWString dots(utf8str_to_wstring(LLString("...")));
+ scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str())));
+ draw_ellipses = TRUE;
+ }
+ }
+
+
+ glBegin(GL_QUADS);
+ for (i = begin_offset; i < begin_offset + length; i++)
+ {
+ llwchar wch = wstr[i];
+
+ // Handle embedded characters first, if they're enabled.
+ // Embedded characters are a hack for notecards
+ const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
+ if (ext_data)
+ {
+ LLImageGL* ext_image = ext_data->mImage;
+ const LLWString& label = ext_data->mLabel;
+
+ F32 ext_height = (F32)ext_image->getHeight() * sScaleY;
+
+ F32 ext_width = (F32)ext_image->getWidth() * sScaleX;
+ F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width;
+
+ if (!label.empty())
+ {
+ ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX;
+ }
+
+ if (start_x + scaled_max_pixels < cur_x + ext_advance)
+ {
+ // Not enough room for this character.
+ break;
+ }
+
+ glEnd();
+
+ glColor3f(1.f, 1.f, 1.f);
+
+ ext_image->bind();
+ const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX);
+ const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight);
+
+ glBegin(GL_QUADS);
+ {
+ S32 num_passes = render_bold ? 2 : 1;
+ for (S32 pass = 0; pass < num_passes; pass++)
+ {
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)),
+ llfont_round_y(ext_y + ext_height));
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)),
+ llfont_round_y(ext_y + ext_height));
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(llfont_round_x(ext_x + (F32)(pass * BOLD_OFFSET)), llfont_round_y(ext_y));
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(llfont_round_x(ext_x + ext_width + (F32)(pass * BOLD_OFFSET)),
+ llfont_round_y(ext_y));
+ }
+ }
+ glEnd();
+
+ if (!label.empty())
+ {
+ glPushMatrix();
+ //glLoadIdentity();
+ //glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f);
+ //glScalef(sScaleX, sScaleY, 1.f);
+ gExtCharFont->render(label, 0,
+ /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX),
+ /*llfloor*/(cur_y / sScaleY),
+ color,
+ halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL,
+ TRUE );
+ glPopMatrix();
+ }
+
+ glColor4fv(color.mV);
+
+ chars_drawn++;
+ cur_x += ext_advance;
+ if (((i + 1) < length) && wstr[i+1])
+ {
+ cur_x += EXT_KERNING * sScaleX;
+ }
+ cur_render_x = cur_x;
+
+ // Bind the font texture
+ mImageGLp->bind();
+ glBegin(GL_QUADS);
+ }
+ else
+ {
+ if (!hasGlyph(wch))
+ {
+ glEnd();
+ (const_cast<LLFontGL*>(this))->addChar(wch);
+ glBegin(GL_QUADS);
+ }
+
+ const LLFontGlyphInfo* fgi= getGlyphInfo(wch);
+ if (!fgi)
+ {
+ llerrs << "Missing Glyph Info" << llendl;
+ }
+ if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth))
+ {
+ // Not enough room for this character.
+ break;
+ }
+
+ // Draw the text at the appropriate location
+ //Specify vertices and texture coordinates
+ S32 num_passes = render_bold ? 2 : 1;
+ for (S32 pass = 0; pass < num_passes; pass++)
+ {
+ glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
+ (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height);
+ glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)(pass * BOLD_OFFSET) - PAD_AMT),
+ llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT));
+ glTexCoord2f((fgi->mXBitmapOffset - PAD_AMT) * inv_width,
+ (fgi->mYBitmapOffset - PAD_AMT) * inv_height);
+ glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)(pass * BOLD_OFFSET) - PAD_AMT),
+ llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT));
+ glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
+ (fgi->mYBitmapOffset - PAD_AMT) * inv_height);
+ glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + slant_offset + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT),
+ llfont_round_y(cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT));
+ glTexCoord2f((fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width,
+ (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height);
+ glVertex2f(llfont_round_x(cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + (F32)(pass * BOLD_OFFSET) + PAD_AMT),
+ llfont_round_y(cur_render_y + (F32)fgi->mYBearing + PAD_AMT));
+ }
+
+ chars_drawn++;
+
+ cur_x += fgi->mXAdvance;
+ cur_y += fgi->mYAdvance;
+ llwchar next_char = wstr[i+1];
+ if (next_char && (next_char < LAST_CHARACTER))
+ {
+ // Kern this puppy.
+ if (!hasGlyph(next_char))
+ {
+ glEnd();
+ (const_cast<LLFontGL*>(this))->addChar(next_char);
+ glBegin(GL_QUADS);
+ }
+ cur_x += getXKerning(wch, next_char);
+ }
+
+ // Round after kerning.
+ // 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)llfloor(cur_x + 0.5f);
+ //cur_y = (F32)llfloor(cur_y + 0.5f);
+
+ cur_render_x = cur_x;
+ cur_render_y = cur_y;
+ }
+ }
+
+ glEnd();
+
+ if (right_x)
+ {
+ *right_x = cur_x / sScaleX;
+ }
+
+ if (style & UNDERLINE)
+ {
+ LLGLSNoTexture no_texture;
+ glBegin(GL_LINES);
+ glVertex2f(start_x, cur_y - (mDescender));
+ glVertex2f(cur_x, cur_y - (mDescender));
+ glEnd();
+ }
+
+ //FIXME: get this working in all alignment cases, etc.
+ if (draw_ellipses)
+ {
+ // recursively render ellipses at end of string
+ // we've already reserved enough room
+ glPushMatrix();
+ //glLoadIdentity();
+ //glTranslatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f);
+ //glScalef(sScaleX, sScaleY, 1.f);
+ renderUTF8("...",
+ 0,
+ cur_x / sScaleX, (F32)y,
+ color,
+ LEFT, valign,
+ style,
+ S32_MAX, max_pixels,
+ right_x,
+ FALSE);
+ glPopMatrix();
+ }
+
+ glPopMatrix();
+
+ return chars_drawn;
+}
+
+
+LLImageGL *LLFontGL::getImageGL() const
+{
+ return mImageGLp;
+}
+
+S32 LLFontGL::getWidth(const LLString& utf8text) const
+{
+ LLWString wtext = utf8str_to_wstring(utf8text);
+ return getWidth(wtext.c_str(), 0, S32_MAX);
+}
+
+S32 LLFontGL::getWidth(const llwchar* wchars) const
+{
+ return getWidth(wchars, 0, S32_MAX);
+}
+
+S32 LLFontGL::getWidth(const LLString& utf8text, const S32 begin_offset, const S32 max_chars) const
+{
+ LLWString wtext = utf8str_to_wstring(utf8text);
+ return getWidth(wtext.c_str(), begin_offset, max_chars);
+}
+
+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);
+}
+
+F32 LLFontGL::getWidthF32(const LLString& utf8text) const
+{
+ LLWString wtext = utf8str_to_wstring(utf8text);
+ return getWidthF32(wtext.c_str(), 0, S32_MAX);
+}
+
+F32 LLFontGL::getWidthF32(const llwchar* wchars) const
+{
+ return getWidthF32(wchars, 0, S32_MAX);
+}
+
+F32 LLFontGL::getWidthF32(const LLString& utf8text, const S32 begin_offset, const S32 max_chars ) const
+{
+ LLWString wtext = utf8str_to_wstring(utf8text);
+ return getWidthF32(wtext.c_str(), begin_offset, max_chars);
+}
+
+F32 LLFontGL::getWidthF32(const llwchar* wchars, const S32 begin_offset, const S32 max_chars, BOOL use_embedded) const
+{
+ const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL;
+
+ F32 cur_x = 0;
+ const S32 max_index = begin_offset + max_chars;
+ for (S32 i = begin_offset; i < max_index; i++)
+ {
+ const llwchar wch = wchars[i];
+ if (wch == 0)
+ {
+ break; // done
+ }
+ const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
+ if (ext_data)
+ {
+ // Handle crappy embedded hack
+ cur_x += getEmbeddedCharAdvance(ext_data);
+
+ if( ((i+1) < max_chars) && (i+1 < max_index))
+ {
+ cur_x += EXT_KERNING * sScaleX;
+ }
+ }
+ else
+ {
+ cur_x += getXAdvance(wch);
+ llwchar next_char = wchars[i+1];
+
+ if (((i + 1) < max_chars)
+ && next_char
+ && (next_char < LAST_CHARACTER))
+ {
+ // Kern this puppy.
+ cur_x += getXKerning(wch, next_char);
+ }
+ }
+ // Round after kerning.
+ cur_x = (F32)llfloor(cur_x + 0.5f);
+ }
+
+ return cur_x / sScaleX;
+}
+
+
+
+// Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels
+S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars,
+ BOOL end_on_word_boundary, const BOOL use_embedded,
+ F32* drawn_pixels) const
+{
+ if (!wchars || !wchars[0] || max_chars == 0)
+ {
+ return 0;
+ }
+
+ llassert(max_pixels >= 0.f);
+ llassert(max_chars >= 0);
+
+ BOOL clip = FALSE;
+ F32 cur_x = 0;
+ F32 drawn_x = 0;
+
+ S32 start_of_last_word = 0;
+ BOOL in_word = FALSE;
+
+ F32 scaled_max_pixels = (F32)llceil(max_pixels * sScaleX);
+
+ S32 i;
+ for (i=0; (i < max_chars); i++)
+ {
+ llwchar wch = wchars[i];
+
+ if(wch == 0)
+ {
+ // Null terminator. We're done.
+ break;
+ }
+
+ const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
+ if (ext_data)
+ {
+ if (in_word)
+ {
+ in_word = FALSE;
+ }
+ else
+ {
+ start_of_last_word = i;
+ }
+ cur_x += getEmbeddedCharAdvance(ext_data);
+
+ if (scaled_max_pixels < cur_x)
+ {
+ clip = TRUE;
+ break;
+ }
+
+ if (((i+1) < max_chars) && wchars[i+1])
+ {
+ cur_x += EXT_KERNING * sScaleX;
+ }
+
+ if( scaled_max_pixels < cur_x )
+ {
+ clip = TRUE;
+ break;
+ }
+ }
+ else
+ {
+ if (in_word)
+ {
+ if (iswspace(wch))
+ {
+ in_word = FALSE;
+ }
+ }
+ else
+ {
+ start_of_last_word = i;
+ if (!iswspace(wch))
+ {
+ in_word = TRUE;
+ }
+ }
+
+ cur_x += getXAdvance(wch);
+
+ if (scaled_max_pixels < cur_x)
+ {
+ clip = TRUE;
+ break;
+ }
+
+ if (((i+1) < max_chars) && wchars[i+1])
+ {
+ // Kern this puppy.
+ cur_x += getXKerning(wch, wchars[i+1]);
+ }
+ }
+ // Round after kerning.
+ cur_x = (F32)llfloor(cur_x + 0.5f);
+ drawn_x = cur_x;
+ }
+
+ if( clip && end_on_word_boundary && (start_of_last_word != 0) )
+ {
+ i = start_of_last_word;
+ }
+ if (drawn_pixels)
+ {
+ *drawn_pixels = drawn_x;
+ }
+ return i;
+}
+
+
+S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos, S32 max_chars) const
+{
+ if (!wchars || !wchars[0] || max_chars == 0)
+ {
+ return 0;
+ }
+
+ F32 total_width = 0.0;
+ S32 drawable_chars = 0;
+
+ F32 scaled_max_pixels = max_pixels * sScaleX;
+
+ S32 start = llmin(start_pos, text_len - 1);
+ for (S32 i = start; i >= 0; i--)
+ {
+ llwchar wch = wchars[i];
+
+ const embedded_data_t* ext_data = getEmbeddedCharData(wch);
+ if (ext_data)
+ {
+ F32 char_width = getEmbeddedCharAdvance(ext_data);
+
+ if( scaled_max_pixels < (total_width + char_width) )
+ {
+ break;
+ }
+
+ total_width += char_width;
+
+ drawable_chars++;
+ if( max_chars >= 0 && drawable_chars >= max_chars )
+ {
+ break;
+ }
+
+ if ( i > 0 )
+ {
+ total_width += EXT_KERNING * sScaleX;
+ }
+
+ // Round after kerning.
+ total_width = (F32)llfloor(total_width + 0.5f);
+ }
+ else
+ {
+ F32 char_width = getXAdvance(wch);
+ if( scaled_max_pixels < (total_width + char_width) )
+ {
+ break;
+ }
+
+ total_width += char_width;
+
+ drawable_chars++;
+ if( max_chars >= 0 && drawable_chars >= max_chars )
+ {
+ break;
+ }
+
+ if ( i > 0 )
+ {
+ // Kerning
+ total_width += getXKerning(wchars[i-1], wch);
+ }
+
+ // Round after kerning.
+ total_width = (F32)llfloor(total_width + 0.5f);
+ }
+ }
+
+ return text_len - drawable_chars;
+}
+
+
+S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, const S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const
+{
+ if (!wchars || !wchars[0] || max_chars == 0)
+ {
+ return 0;
+ }
+
+ F32 cur_x = 0;
+ S32 pos = 0;
+
+ target_x *= sScaleX;
+
+ // max_chars is S32_MAX by default, so make sure we don't get overflow
+ const S32 max_index = begin_offset + llmin(S32_MAX - begin_offset, max_chars);
+
+ F32 scaled_max_pixels = max_pixels * sScaleX;
+
+ for (S32 i = begin_offset; (i < max_index); i++)
+ {
+ llwchar wch = wchars[i];
+ if (!wch)
+ {
+ break; // done
+ }
+ const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL;
+ if (ext_data)
+ {
+ F32 ext_advance = getEmbeddedCharAdvance(ext_data);
+
+ if (round)
+ {
+ // Note: if the mouse is on the left half of the character, the pick is to the character's left
+ // If it's on the right half, the pick is to the right.
+ if (target_x < cur_x + ext_advance/2)
+ {
+ break;
+ }
+ }
+ else
+ {
+ if (target_x < cur_x + ext_advance)
+ {
+ break;
+ }
+ }
+
+ if (scaled_max_pixels < cur_x + ext_advance)
+ {
+ break;
+ }
+
+ pos++;
+ cur_x += ext_advance;
+
+ if (((i + 1) < max_index)
+ && (wchars[(i + 1)]))
+ {
+ cur_x += EXT_KERNING * sScaleX;
+ }
+ // Round after kerning.
+ cur_x = (F32)llfloor(cur_x + 0.5f);
+ }
+ else
+ {
+ F32 char_width = getXAdvance(wch);
+
+ if (round)
+ {
+ // Note: if the mouse is on the left half of the character, the pick is to the character's left
+ // If it's on the right half, the pick is to the right.
+ if (target_x < cur_x + char_width*0.5f)
+ {
+ break;
+ }
+ }
+ else if (target_x < cur_x + char_width)
+ {
+ break;
+ }
+
+ if (scaled_max_pixels < cur_x + char_width)
+ {
+ break;
+ }
+
+ pos++;
+ cur_x += char_width;
+
+ if (((i + 1) < max_index)
+ && (wchars[(i + 1)]))
+ {
+ llwchar next_char = wchars[i + 1];
+ // Kern this puppy.
+ cur_x += getXKerning(wch, next_char);
+ }
+
+ // Round after kerning.
+ cur_x = (F32)llfloor(cur_x + 0.5f);
+ }
+ }
+
+ return pos;
+}
+
+
+const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(const llwchar wch) const
+{
+ // Handle crappy embedded hack
+ embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch);
+ if (iter != mEmbeddedChars.end())
+ {
+ return iter->second;
+ }
+ return NULL;
+}
+
+
+F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const
+{
+ const LLWString& label = ext_data->mLabel;
+ LLImageGL* ext_image = ext_data->mImage;
+
+ F32 ext_width = (F32)ext_image->getWidth();
+ if( !label.empty() )
+ {
+ ext_width += (EXT_X_BEARING + gExtCharFont->getWidthF32(label.c_str())) * sScaleX;
+ }
+
+ return (EXT_X_BEARING * sScaleX) + ext_width;
+}
+
+
+void LLFontGL::clearEmbeddedChars()
+{
+ for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer());
+ mEmbeddedChars.clear();
+}
+
+void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label )
+{
+ LLWString wlabel = utf8str_to_wstring(label);
+ addEmbeddedChar(wc, image, wlabel);
+}
+
+void LLFontGL::addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& wlabel )
+{
+ embedded_data_t* ext_data = new embedded_data_t(image, wlabel);
+ mEmbeddedChars[wc] = ext_data;
+}
+
+void LLFontGL::removeEmbeddedChar( llwchar wc )
+{
+ embedded_map_t::iterator iter = mEmbeddedChars.find(wc);
+ if (iter != mEmbeddedChars.end())
+ {
+ delete iter->second;
+ mEmbeddedChars.erase(wc);
+ }
+}
+
+// static
+LLString LLFontGL::nameFromFont(const LLFontGL* fontp)
+{
+ if (fontp == sSansSerifHuge)
+ {
+ return LLString("SansSerifHude");
+ }
+ else if (fontp == sSansSerifSmall)
+ {
+ return LLString("SansSerifSmall");
+ }
+ else if (fontp == sSansSerif)
+ {
+ return LLString("SansSerif");
+ }
+ else if (fontp == sSansSerifBig)
+ {
+ return LLString("SansSerifBig");
+ }
+ else if (fontp == sSansSerifBold)
+ {
+ return LLString("SansSerifBold");
+ }
+ else if (fontp == sMonospace)
+ {
+ return LLString("Monospace");
+ }
+ else
+ {
+ return LLString();
+ }
+}
+
+// static
+LLFontGL* LLFontGL::fontFromName(const LLString& font_name)
+{
+ LLFontGL* gl_font = NULL;
+ if (font_name == "SansSerifHuge")
+ {
+ gl_font = LLFontGL::sSansSerifHuge;
+ }
+ else if (font_name == "SansSerifSmall")
+ {
+ gl_font = LLFontGL::sSansSerifSmall;
+ }
+ else if (font_name == "SansSerif")
+ {
+ gl_font = LLFontGL::sSansSerif;
+ }
+ else if (font_name == "SansSerifBig")
+ {
+ gl_font = LLFontGL::sSansSerifBig;
+ }
+ else if (font_name == "SansSerifBold")
+ {
+ gl_font = LLFontGL::sSansSerifBold;
+ }
+ else if (font_name == "Monospace")
+ {
+ gl_font = LLFontGL::sMonospace;
+ }
+ return gl_font;
+}
+
+// static
+LLString LLFontGL::nameFromHAlign(LLFontGL::HAlign align)
+{
+ if (align == LEFT) return LLString("left");
+ else if (align == RIGHT) return LLString("right");
+ else if (align == HCENTER) return LLString("center");
+ else return LLString();
+}
+
+// static
+LLFontGL::HAlign LLFontGL::hAlignFromName(const LLString& name)
+{
+ LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
+ if (name == "left")
+ {
+ gl_hfont_align = LLFontGL::LEFT;
+ }
+ else if (name == "right")
+ {
+ gl_hfont_align = LLFontGL::RIGHT;
+ }
+ else if (name == "center")
+ {
+ gl_hfont_align = LLFontGL::HCENTER;
+ }
+ //else leave left
+ return gl_hfont_align;
+}
+
+// static
+LLString LLFontGL::nameFromVAlign(LLFontGL::VAlign align)
+{
+ if (align == TOP) return LLString("top");
+ else if (align == VCENTER) return LLString("center");
+ else if (align == BASELINE) return LLString("baseline");
+ else if (align == BOTTOM) return LLString("bottom");
+ else return LLString();
+}
+
+// static
+LLFontGL::VAlign LLFontGL::vAlignFromName(const LLString& name)
+{
+ LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
+ if (name == "top")
+ {
+ gl_vfont_align = LLFontGL::TOP;
+ }
+ else if (name == "center")
+ {
+ gl_vfont_align = LLFontGL::VCENTER;
+ }
+ else if (name == "baseline")
+ {
+ gl_vfont_align = LLFontGL::BASELINE;
+ }
+ else if (name == "bottom")
+ {
+ gl_vfont_align = LLFontGL::BOTTOM;
+ }
+ //else leave baseline
+ return gl_vfont_align;
+}
diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h
new file mode 100644
index 0000000000..789879a5ca
--- /dev/null
+++ b/indra/llrender/llfontgl.h
@@ -0,0 +1,233 @@
+/**
+ * @file llfontgl.h
+ * @author Doug Soo
+ * @brief Wrapper around FreeType
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFONTGL_H
+#define LL_LLFONTGL_H
+
+#include "llfont.h"
+#include "llimagegl.h"
+#include "v2math.h"
+#include "llcoord.h"
+
+class LLColor4;
+
+class LLFontGL : public LLFont
+{
+public:
+ enum HAlign
+ {
+ // Horizontal location of x,y coord to render.
+ LEFT = 0, // Left align
+ RIGHT = 1, // Right align
+ HCENTER = 2, // Center
+ };
+
+ enum VAlign
+ {
+ // Vertical location of x,y coord to render.
+ TOP = 3, // Top align
+ VCENTER = 4, // Center
+ BASELINE = 5, // Baseline
+ BOTTOM = 6 // Bottom
+ };
+
+ enum StyleFlags
+ {
+ // text style to render. May be combined (these are bit flags)
+ NORMAL = 0,
+ BOLD = 1,
+ ITALIC = 2,
+ UNDERLINE = 4,
+ DROP_SHADOW = 8
+ };
+
+ // Takes a string with potentially several flags, i.e. "NORMAL|BOLD|ITALIC"
+ static U8 getStyleFromString(const LLString &style);
+
+ LLFontGL();
+ LLFontGL(const LLFontGL &source);
+ ~LLFontGL();
+
+ void init(); // Internal init, or reinitialization
+ void reset(); // Reset a font after GL cleanup. ONLY works on an already loaded font.
+
+ LLFontGL &operator=(const LLFontGL &source);
+
+ static BOOL initDefaultFonts(F32 screen_dpi, F32 x_scale, F32 y_scale,
+ const LLString& monospace_file, F32 monospace_size,
+ const LLString& sansserif_file,
+ const LLString& sansserif_fallback_file, F32 ss_fallback_scale,
+ F32 small_size, F32 medium_size, F32 large_size, F32 huge_size,
+ const LLString& sansserif_bold_file, F32 bold_size,
+ const LLString& app_dir = LLString::null);
+
+ static void destroyDefaultFonts();
+ static void destroyGL();
+
+ static bool loadFaceFallback(LLFontList *fontp, const LLString& fontname, const F32 point_size);
+ static bool loadFace(LLFontGL *fontp, const LLString& fontname, const F32 point_size, LLFontList *fallback_fontp);
+ BOOL loadFace(const LLString& filename, const F32 point_size, const F32 vert_dpi, const F32 horz_dpi);
+
+
+ S32 renderUTF8(const LLString &text, const S32 begin_offset,
+ S32 x, S32 y,
+ const LLColor4 &color) const
+ {
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color,
+ LEFT, BASELINE, NORMAL,
+ S32_MAX, S32_MAX, NULL, FALSE);
+ }
+
+ S32 renderUTF8(const LLString &text, const S32 begin_offset,
+ S32 x, S32 y,
+ const LLColor4 &color,
+ HAlign halign, VAlign valign, U8 style = NORMAL) const
+ {
+ return renderUTF8(text, begin_offset, (F32)x, (F32)y, color,
+ halign, valign, style,
+ S32_MAX, S32_MAX, NULL, FALSE);
+ }
+
+ // renderUTF8 does a conversion, so is slower!
+ S32 renderUTF8(const LLString &text,
+ S32 begin_offset,
+ F32 x, F32 y,
+ const LLColor4 &color,
+ HAlign halign,
+ VAlign valign,
+ U8 style,
+ S32 max_chars,
+ S32 max_pixels,
+ F32* right_x,
+ BOOL use_ellipses) const;
+
+ S32 render(const LLWString &text, const S32 begin_offset,
+ F32 x, F32 y,
+ const LLColor4 &color) const
+ {
+ return render(text, begin_offset, x, y, color,
+ LEFT, BASELINE, NORMAL,
+ S32_MAX, S32_MAX, NULL, FALSE, FALSE);
+ }
+
+
+ S32 render(const LLWString &text,
+ S32 begin_offset,
+ F32 x, F32 y,
+ const LLColor4 &color,
+ HAlign halign = LEFT,
+ VAlign valign = BASELINE,
+ U8 style = NORMAL,
+ S32 max_chars = S32_MAX,
+ S32 max_pixels = S32_MAX,
+ F32* right_x=NULL,
+ BOOL use_embedded = FALSE,
+ BOOL use_ellipses = FALSE) const;
+
+ // font metrics - override for LLFont that returns units of virtual pixels
+ /*virtual*/ F32 getLineHeight() const { return (F32)llround(mLineHeight / sScaleY); }
+ /*virtual*/ F32 getAscenderHeight() const { return (F32)llround(mAscender / sScaleY); }
+ /*virtual*/ F32 getDescenderHeight() const { return (F32)llround(mDescender / sScaleY); }
+
+ virtual S32 getWidth(const LLString& utf8text) const;
+ virtual S32 getWidth(const llwchar* wchars) const;
+ virtual S32 getWidth(const LLString& utf8text, const S32 offset, const S32 max_chars ) const;
+ virtual S32 getWidth(const llwchar* wchars, const S32 offset, const S32 max_chars, BOOL use_embedded = FALSE) const;
+
+ virtual F32 getWidthF32(const LLString& utf8text) const;
+ virtual F32 getWidthF32(const llwchar* wchars) const;
+ virtual F32 getWidthF32(const LLString& text, const S32 offset, const S32 max_chars ) const;
+ virtual F32 getWidthF32(const llwchar* wchars, const S32 offset, const S32 max_chars, BOOL use_embedded = FALSE ) const;
+
+ // The following are called often, frequently with large buffers, so do not use a string interface
+
+ // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels
+ virtual S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX,
+ BOOL end_on_word_boundary = FALSE, const BOOL use_embedded = FALSE,
+ F32* drawn_pixels = NULL) const;
+
+ // Returns the index of the first complete characters from text that can be drawn in max_pixels
+ // starting on the right side (at character start_pos).
+ virtual S32 firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos=S32_MAX, S32 max_chars = S32_MAX) const;
+
+ // Returns the index of the character closest to pixel position x (ignoring text to the right of max_pixels and max_chars)
+ virtual S32 charFromPixelOffset(const llwchar* wchars, const S32 char_offset,
+ F32 x, F32 max_pixels=F32_MAX, S32 max_chars = S32_MAX,
+ BOOL round = TRUE, BOOL use_embedded = FALSE) const;
+
+
+ LLImageGL *getImageGL() const;
+
+ void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLString& label);
+ void addEmbeddedChar( llwchar wc, LLImageGL* image, const LLWString& label);
+ void removeEmbeddedChar( llwchar wc );
+
+ static LLString nameFromFont(const LLFontGL* fontp);
+ static LLFontGL* fontFromName(const LLString& name);
+
+ static LLString nameFromHAlign(LLFontGL::HAlign align);
+ static LLFontGL::HAlign hAlignFromName(const LLString& name);
+
+ static LLString nameFromVAlign(LLFontGL::VAlign align);
+ static LLFontGL::VAlign vAlignFromName(const LLString& name);
+
+protected:
+ struct embedded_data_t
+ {
+ embedded_data_t(LLImageGL* image, const LLWString& label) : mImage(image), mLabel(label) {}
+ LLPointer<LLImageGL> mImage;
+ LLWString mLabel;
+ };
+ const embedded_data_t* getEmbeddedCharData(const llwchar wch) const;
+ F32 getEmbeddedCharAdvance(const embedded_data_t* ext_data) const;
+ void clearEmbeddedChars();
+
+public:
+ static F32 sVertDPI;
+ static F32 sHorizDPI;
+ static F32 sScaleX;
+ static F32 sScaleY;
+ static LLString sAppDir; // For loading fonts
+
+ static LLFontGL* sMonospace; // medium
+
+ static LLFontGL* sSansSerifSmall; // small
+ static LLFontList* sSSSmallFallback;
+ static LLFontGL* sSansSerif; // medium
+ static LLFontList* sSSFallback;
+ static LLFontGL* sSansSerifBig; // large
+ static LLFontList* sSSBigFallback;
+ static LLFontGL* sSansSerifHuge; // very large
+ static LLFontList* sSSHugeFallback;
+
+ static LLFontGL* sSansSerifBold; // medium, bolded
+ static LLFontList* sSSBoldFallback;
+
+ static LLColor4 sShadowColor;
+
+ friend class LLTextBillboard;
+ friend class LLHUDText;
+
+protected:
+ /*virtual*/ BOOL addChar(const llwchar wch);
+ static LLString getFontPathLocal();
+ static LLString getFontPathSystem();
+
+protected:
+ LLPointer<LLImageRaw> mRawImageGLp;
+ LLPointer<LLImageGL> mImageGLp;
+ typedef std::map<llwchar,embedded_data_t*> embedded_map_t;
+ embedded_map_t mEmbeddedChars;
+
+public:
+ static LLCoordFont sCurOrigin;
+ static std::vector<LLCoordFont> sOriginStack;
+};
+
+#endif
diff --git a/indra/llrender/llgldbg.cpp b/indra/llrender/llgldbg.cpp
new file mode 100644
index 0000000000..2c61ebb851
--- /dev/null
+++ b/indra/llrender/llgldbg.cpp
@@ -0,0 +1,204 @@
+/**
+ * @file llgldbg.cpp
+ * @brief Definitions for OpenGL debugging support
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// This file sets some global GL parameters, and implements some
+// useful functions for GL operations.
+
+#include "linden_common.h"
+
+#include "llglheaders.h"
+
+#include "llgl.h"
+
+
+//------------------------------------------------------------------------
+// cmstr()
+//------------------------------------------------------------------------
+char *cmstr(int i)
+{
+ switch( i )
+ {
+ case GL_EMISSION: return "GL_EMISSION";
+ case GL_AMBIENT: return "GL_AMBIENT";
+ case GL_DIFFUSE: return "GL_DIFFUSE";
+ case GL_SPECULAR: return "GL_SPECULAR";
+ case GL_AMBIENT_AND_DIFFUSE: return "GL_AMBIENT_AND_DIFFUSE";
+ }
+ return "UNKNOWN";
+}
+
+//------------------------------------------------------------------------
+// facestr()
+//------------------------------------------------------------------------
+char *facestr(int i)
+{
+ switch( i )
+ {
+ case GL_FRONT: return "GL_FRONT";
+ case GL_BACK: return "GL_BACK";
+ case GL_FRONT_AND_BACK: return "GL_FRONT_AND_BACK";
+ }
+ return "UNKNOWN";
+}
+
+//------------------------------------------------------------------------
+// boolstr()
+//------------------------------------------------------------------------
+const char *boolstr(int b)
+{
+ return b ? "GL_TRUE" : "GL_FALSE";
+}
+
+//------------------------------------------------------------------------
+// fv4()
+//------------------------------------------------------------------------
+char *fv4(F32 *f)
+{
+ static char str[128];
+ sprintf(str, "%8.3f %8.3f %8.3f %8.3f", f[0], f[1], f[2], f[3]);
+ return str;
+}
+
+//------------------------------------------------------------------------
+// fv3()
+//------------------------------------------------------------------------
+char *fv3(F32 *f)
+{
+ static char str[128];
+ sprintf(str, "%8.3f, %8.3f, %8.3f", f[0], f[1], f[2]);
+ return str;
+}
+
+//------------------------------------------------------------------------
+// fv1()
+//------------------------------------------------------------------------
+char *fv1(F32 *f)
+{
+ static char str[128];
+ sprintf(str, "%8.3f", f[0]);
+ return str;
+}
+
+//------------------------------------------------------------------------
+// llgl_dump()
+//------------------------------------------------------------------------
+void llgl_dump()
+{
+ int i;
+ F32 fv[16];
+ GLboolean b;
+
+ llinfos << "==========================" << llendl;
+ llinfos << "OpenGL State" << llendl;
+ llinfos << "==========================" << llendl;
+
+ llinfos << "-----------------------------------" << llendl;
+ llinfos << "Current Values" << llendl;
+ llinfos << "-----------------------------------" << llendl;
+
+ glGetFloatv(GL_CURRENT_COLOR, fv);
+ llinfos << "GL_CURRENT_COLOR : " << fv4(fv) << llendl;
+
+ glGetFloatv(GL_CURRENT_NORMAL, fv);
+ llinfos << "GL_CURRENT_NORMAL : " << fv3(fv) << llendl;
+
+ llinfos << "-----------------------------------" << llendl;
+ llinfos << "Lighting" << llendl;
+ llinfos << "-----------------------------------" << llendl;
+
+ llinfos << "GL_LIGHTING : " << boolstr(glIsEnabled(GL_LIGHTING)) << llendl;
+
+ llinfos << "GL_COLOR_MATERIAL : " << boolstr(glIsEnabled(GL_COLOR_MATERIAL)) << llendl;
+
+ glGetIntegerv(GL_COLOR_MATERIAL_PARAMETER, (GLint*)&i);
+ llinfos << "GL_COLOR_MATERIAL_PARAMETER: " << cmstr(i) << llendl;
+
+ glGetIntegerv(GL_COLOR_MATERIAL_FACE, (GLint*)&i);
+ llinfos << "GL_COLOR_MATERIAL_FACE : " << facestr(i) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetMaterialfv(GL_FRONT, GL_AMBIENT, fv);
+ llinfos << "GL_AMBIENT material : " << fv4(fv) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetMaterialfv(GL_FRONT, GL_DIFFUSE, fv);
+ llinfos << "GL_DIFFUSE material : " << fv4(fv) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetMaterialfv(GL_FRONT, GL_SPECULAR, fv);
+ llinfos << "GL_SPECULAR material : " << fv4(fv) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetMaterialfv(GL_FRONT, GL_EMISSION, fv);
+ llinfos << "GL_EMISSION material : " << fv4(fv) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetMaterialfv(GL_FRONT, GL_SHININESS, fv);
+ llinfos << "GL_SHININESS material : " << fv1(fv) << llendl;
+
+ fv[0] = fv[1] = fv[2] = fv[3] = 12345.6789f;
+ glGetFloatv(GL_LIGHT_MODEL_AMBIENT, fv);
+ llinfos << "GL_LIGHT_MODEL_AMBIENT : " << fv4(fv) << llendl;
+
+ glGetBooleanv(GL_LIGHT_MODEL_LOCAL_VIEWER, &b);
+ llinfos << "GL_LIGHT_MODEL_LOCAL_VIEWER: " << boolstr(b) << llendl;
+
+ glGetBooleanv(GL_LIGHT_MODEL_TWO_SIDE, &b);
+ llinfos << "GL_LIGHT_MODEL_TWO_SIDE : " << boolstr(b) << llendl;
+
+ for (int l=0; l<8; l++)
+ {
+ b = glIsEnabled(GL_LIGHT0+l);
+ llinfos << "GL_LIGHT" << l << " : " << boolstr(b) << llendl;
+
+ if (!b)
+ continue;
+
+ glGetLightfv(GL_LIGHT0+l, GL_AMBIENT, fv);
+ llinfos << " GL_AMBIENT light : " << fv4(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_DIFFUSE, fv);
+ llinfos << " GL_DIFFUSE light : " << fv4(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_SPECULAR, fv);
+ llinfos << " GL_SPECULAR light : " << fv4(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_POSITION, fv);
+ llinfos << " GL_POSITION light : " << fv4(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_CONSTANT_ATTENUATION, fv);
+ llinfos << " GL_CONSTANT_ATTENUATION : " << fv1(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_QUADRATIC_ATTENUATION, fv);
+ llinfos << " GL_QUADRATIC_ATTENUATION : " << fv1(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_SPOT_DIRECTION, fv);
+ llinfos << " GL_SPOT_DIRECTION : " << fv4(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_SPOT_EXPONENT, fv);
+ llinfos << " GL_SPOT_EXPONENT : " << fv1(fv) << llendl;
+
+ glGetLightfv(GL_LIGHT0+l, GL_SPOT_CUTOFF, fv);
+ llinfos << " GL_SPOT_CUTOFF : " << fv1(fv) << llendl;
+ }
+
+ llinfos << "-----------------------------------" << llendl;
+ llinfos << "Pixel Operations" << llendl;
+ llinfos << "-----------------------------------" << llendl;
+
+ llinfos << "GL_ALPHA_TEST : " << boolstr(glIsEnabled(GL_ALPHA_TEST)) << llendl;
+ llinfos << "GL_DEPTH_TEST : " << boolstr(glIsEnabled(GL_DEPTH_TEST)) << llendl;
+
+ glGetBooleanv(GL_DEPTH_WRITEMASK, &b);
+ llinfos << "GL_DEPTH_WRITEMASK : " << boolstr(b) << llendl;
+
+ llinfos << "GL_BLEND : " << boolstr(glIsEnabled(GL_BLEND)) << llendl;
+ llinfos << "GL_DITHER : " << boolstr(glIsEnabled(GL_DITHER)) << llendl;
+}
+
+// End
diff --git a/indra/llrender/llgldbg.h b/indra/llrender/llgldbg.h
new file mode 100644
index 0000000000..9cb15dc316
--- /dev/null
+++ b/indra/llrender/llgldbg.h
@@ -0,0 +1,16 @@
+/**
+ * @file llgldbg.h
+ * @brief Definitions for OpenGL debugging support
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGLDBG_H
+#define LL_LLGLDBG_H
+
+// Dumps the current OpenGL state to the console.
+void llgl_dump();
+
+
+#endif // LL_LLGLDBG_H
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
new file mode 100644
index 0000000000..f26223e32b
--- /dev/null
+++ b/indra/llrender/llimagegl.cpp
@@ -0,0 +1,1205 @@
+/**
+ * @file llimagegl.cpp
+ * @brief Generic GL image handler
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+// TODO: create 2 classes for images w/ and w/o discard levels?
+
+#include "linden_common.h"
+
+#include "llimagegl.h"
+
+#include "llerror.h"
+#include "llimage.h"
+
+#include "llmath.h"
+#include "llgl.h"
+
+//----------------------------------------------------------------------------
+
+const F32 MIN_TEXTURE_LIFETIME = 10.f;
+
+//statics
+LLGLuint LLImageGL::sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS] = { 0 };
+
+S32 LLImageGL::sGlobalTextureMemory = 0;
+S32 LLImageGL::sBoundTextureMemory = 0;
+S32 LLImageGL::sCurBoundTextureMemory = 0;
+S32 LLImageGL::sCount = 0;
+
+BOOL LLImageGL::sGlobalUseAnisotropic = FALSE;
+F32 LLImageGL::sLastFrameTime = 0.f;
+
+std::set<LLImageGL*> LLImageGL::sImageList;
+
+//----------------------------------------------------------------------------
+
+//static
+S32 LLImageGL::dataFormatBits(S32 dataformat)
+{
+ switch (dataformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 4;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 8;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 8;
+ case GL_LUMINANCE: return 8;
+ case GL_ALPHA: return 8;
+ case GL_COLOR_INDEX: return 8;
+ case GL_LUMINANCE_ALPHA: return 16;
+ case GL_RGB: return 24;
+ case GL_RGB8: return 24;
+ case GL_RGBA: return 32;
+ case GL_BGRA: return 32; // Used for QuickTime media textures on the Mac
+ default:
+ llerrs << "LLImageGL::Unknown format: " << dataformat << llendl;
+ return 0;
+ }
+}
+
+//static
+S32 LLImageGL::dataFormatBytes(S32 dataformat, S32 width, S32 height)
+{
+ if (dataformat >= GL_COMPRESSED_RGB_S3TC_DXT1_EXT &&
+ dataformat <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
+ {
+ if (width < 4) width = 4;
+ if (height < 4) height = 4;
+ }
+ S32 bytes ((width*height*dataFormatBits(dataformat)+7)>>3);
+ S32 aligned = (bytes+3)&~3;
+ return aligned;
+}
+
+//static
+S32 LLImageGL::dataFormatComponents(S32 dataformat)
+{
+ switch (dataformat)
+ {
+ case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: return 3;
+ case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT: return 4;
+ case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: return 4;
+ case GL_LUMINANCE: return 1;
+ case GL_ALPHA: return 1;
+ case GL_COLOR_INDEX: return 1;
+ case GL_LUMINANCE_ALPHA: return 2;
+ case GL_RGB: return 3;
+ case GL_RGBA: return 4;
+ case GL_BGRA: return 4; // Used for QuickTime media textures on the Mac
+ default:
+ llerrs << "LLImageGL::Unknown format: " << dataformat << llendl;
+ return 0;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// static
+void LLImageGL::bindExternalTexture(LLGLuint gl_name, S32 stage, LLGLenum bind_target )
+{
+ glActiveTextureARB(GL_TEXTURE0_ARB + stage);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB + stage);
+ glBindTexture(bind_target, gl_name);
+ sCurrentBoundTextures[stage] = gl_name;
+}
+
+// static
+void LLImageGL::unbindTexture(S32 stage, LLGLenum bind_target)
+{
+ glActiveTextureARB(GL_TEXTURE0_ARB + stage);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB + stage);
+ glBindTexture(bind_target, 0);
+ sCurrentBoundTextures[stage] = 0;
+}
+
+// static
+void LLImageGL::updateStats(F32 current_time)
+{
+ sLastFrameTime = current_time;
+ sBoundTextureMemory = sCurBoundTextureMemory;
+ sCurBoundTextureMemory = 0;
+}
+
+//static
+S32 LLImageGL::updateBoundTexMem(const S32 delta)
+{
+ LLImageGL::sCurBoundTextureMemory += delta;
+ return LLImageGL::sCurBoundTextureMemory;
+}
+
+//----------------------------------------------------------------------------
+
+//static
+void LLImageGL::destroyGL(BOOL save_state)
+{
+ for (S32 stage = 0; stage < gGLManager.mNumTextureUnits; stage++)
+ {
+ LLImageGL::unbindTexture(stage, GL_TEXTURE_2D);
+ }
+ for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
+ iter != sImageList.end(); iter++)
+ {
+ LLImageGL* glimage = *iter;
+ if (glimage->mTexName && glimage->mComponents)
+ {
+ if (save_state)
+ {
+ glimage->mSaveData = new LLImageRaw;
+ glimage->readBackRaw(glimage->mCurrentDiscardLevel, glimage->mSaveData);
+ }
+ glimage->destroyGLTexture();
+ stop_glerror();
+ }
+ }
+}
+
+//static
+void LLImageGL::restoreGL()
+{
+ for (std::set<LLImageGL*>::iterator iter = sImageList.begin();
+ iter != sImageList.end(); iter++)
+ {
+ LLImageGL* glimage = *iter;
+ if (glimage->mSaveData.notNull() && glimage->mSaveData->getComponents())
+ {
+ if (glimage->getComponents())
+ {
+ glimage->createGLTexture(glimage->mCurrentDiscardLevel, glimage->mSaveData);
+ stop_glerror();
+ }
+ glimage->mSaveData = NULL; // deletes data
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+//static
+BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, BOOL usemipmaps)
+{
+ dest = new LLImageGL(usemipmaps);
+ return TRUE;
+}
+
+BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, U32 width, U32 height, U8 components, BOOL usemipmaps)
+{
+ dest = new LLImageGL(width, height, components, usemipmaps);
+ return TRUE;
+}
+
+BOOL LLImageGL::create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps)
+{
+ dest = new LLImageGL(imageraw, usemipmaps);
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+
+LLImageGL::LLImageGL(BOOL usemipmaps)
+ : mSaveData(0)
+{
+ init(usemipmaps);
+ setSize(0, 0, 0);
+ sImageList.insert(this);
+ sCount++;
+}
+
+LLImageGL::LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps)
+ : mSaveData(0)
+{
+ llassert( components <= 4 );
+ init(usemipmaps);
+ setSize(width, height, components);
+ sImageList.insert(this);
+ sCount++;
+}
+
+LLImageGL::LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps)
+ : mSaveData(0)
+{
+ init(usemipmaps);
+ setSize(0, 0, 0);
+ sImageList.insert(this);
+ sCount++;
+ createGLTexture(0, imageraw);
+}
+
+LLImageGL::~LLImageGL()
+{
+ LLImageGL::cleanup();
+ sImageList.erase(this);
+ sCount--;
+}
+
+void LLImageGL::init(BOOL usemipmaps)
+{
+#ifdef DEBUG_MISS
+ mMissed = FALSE;
+#endif
+
+ mTextureMemory = 0;
+ mLastBindTime = 0.f;
+
+ mTarget = GL_TEXTURE_2D;
+ mBindTarget = GL_TEXTURE_2D;
+ mUseMipMaps = usemipmaps;
+ mHasMipMaps = FALSE;
+ mAutoGenMips = FALSE;
+ mTexName = 0;
+ mIsResident = 0;
+ mClampS = FALSE;
+ mClampT = FALSE;
+ mMipFilterNearest = FALSE;
+ mWidth = 0;
+ mHeight = 0;
+ mComponents = 0;
+
+ mMaxDiscardLevel = MAX_DISCARD_LEVEL;
+ mCurrentDiscardLevel = -1;
+ mDontDiscard = FALSE;
+
+ mFormatInternal = -1;
+ mFormatPrimary = (LLGLenum) 0;
+ mFormatType = GL_UNSIGNED_BYTE;
+ mFormatSwapBytes = FALSE;
+ mHasExplicitFormat = FALSE;
+}
+
+void LLImageGL::cleanup()
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ destroyGLTexture();
+ }
+ mSaveData = NULL; // deletes data
+}
+
+//----------------------------------------------------------------------------
+
+static bool check_power_of_two(S32 dim)
+{
+ while(dim > 1)
+ {
+ if (dim & 1)
+ {
+ return false;
+ }
+ dim >>= 1;
+ }
+ return true;
+}
+
+//static
+bool LLImageGL::checkSize(S32 width, S32 height)
+{
+ return check_power_of_two(width) && check_power_of_two(height);
+}
+
+void LLImageGL::setSize(S32 width, S32 height, S32 ncomponents)
+{
+ if (width != mWidth || height != mHeight || ncomponents != mComponents)
+ {
+ // Check if dimensions are a power of two!
+ if (!checkSize(width,height))
+ {
+ llerrs << llformat("Texture has non power of two dimention: %dx%d",width,height) << llendl;
+ }
+
+ if (mTexName)
+ {
+// llwarns << "Setting Size of LLImageGL with existing mTexName = " << mTexName << llendl;
+ destroyGLTexture();
+ }
+
+ mWidth = width;
+ mHeight = height;
+ mComponents = ncomponents;
+ if (ncomponents > 0)
+ {
+ mMaxDiscardLevel = 0;
+ while (width > 1 && height > 1 && mMaxDiscardLevel < MAX_DISCARD_LEVEL)
+ {
+ mMaxDiscardLevel++;
+ width >>= 1;
+ height >>= 1;
+ }
+ }
+ else
+ {
+ mMaxDiscardLevel = MAX_DISCARD_LEVEL;
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// virtual
+void LLImageGL::dump()
+{
+ llinfos << "mMaxDiscardLevel " << S32(mMaxDiscardLevel)
+ << " mLastBindTime " << mLastBindTime
+ << " mTarget " << S32(mTarget)
+ << " mBindTarget " << S32(mBindTarget)
+ << " mUseMipMaps " << S32(mUseMipMaps)
+ << " mHasMipMaps " << S32(mHasMipMaps)
+ << " mCurrentDiscardLevel " << S32(mCurrentDiscardLevel)
+ << " mFormatInternal " << S32(mFormatInternal)
+ << " mFormatPrimary " << S32(mFormatPrimary)
+ << " mFormatType " << S32(mFormatType)
+ << " mFormatSwapBytes " << S32(mFormatSwapBytes)
+ << " mHasExplicitFormat " << S32(mHasExplicitFormat)
+#if DEBUG_MISS
+ << " mMissed " << mMissed
+#endif
+ << llendl;
+
+ llinfos << " mTextureMemory " << mTextureMemory
+ << " mTexNames " << mTexName
+ << " mIsResident " << S32(mIsResident)
+ << llendl;
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLImageGL::bindTextureInternal(const S32 stage) const
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llwarns << "Trying to bind a texture while GL is disabled!" << llendl;
+ }
+
+ stop_glerror();
+
+ glActiveTextureARB(GL_TEXTURE0_ARB + stage);
+ //glClientActiveTextureARB(GL_TEXTURE0_ARB + stage);
+
+ stop_glerror();
+
+ if (sCurrentBoundTextures[stage] && sCurrentBoundTextures[stage] == mTexName)
+ {
+ // already set!
+ return TRUE;
+ }
+
+ if (mTexName != 0)
+ {
+#ifdef DEBUG_MISS
+ mMissed = ! getIsResident(TRUE);
+#endif
+
+ glBindTexture(mBindTarget, mTexName);
+ sCurrentBoundTextures[stage] = mTexName;
+ stop_glerror();
+
+ if (mLastBindTime != sLastFrameTime)
+ {
+ // we haven't accounted for this texture yet this frame
+ updateBoundTexMem(mTextureMemory);
+ mLastBindTime = sLastFrameTime;
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ glBindTexture(mBindTarget, 0);
+ sCurrentBoundTextures[stage] = 0;
+ return FALSE;
+ }
+}
+
+//virtual
+BOOL LLImageGL::bind(const S32 stage) const
+{
+ if (stage == -1)
+ {
+ return FALSE;
+ }
+ BOOL res = bindTextureInternal(stage);
+ //llassert(res);
+ return res;
+}
+
+void LLImageGL::setExplicitFormat( LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes )
+{
+ // Note: must be called before createTexture()
+ // Note: it's up to the caller to ensure that the format matches the number of components.
+ mHasExplicitFormat = TRUE;
+ mFormatInternal = internal_format;
+ mFormatPrimary = primary_format;
+ if(type_format == 0)
+ mFormatType = GL_UNSIGNED_BYTE;
+ else
+ mFormatType = type_format;
+ mFormatSwapBytes = swap_bytes;
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageGL::setImage(const LLImageRaw* imageraw)
+{
+ llassert((imageraw->getWidth() == getWidth(mCurrentDiscardLevel)) &&
+ (imageraw->getHeight() == getHeight(mCurrentDiscardLevel)) &&
+ (imageraw->getComponents() == getComponents()));
+ const U8* rawdata = imageraw->getData();
+ setImage(rawdata, FALSE);
+}
+
+void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips)
+{
+// LLFastTimer t1(LLFastTimer::FTM_TEMP1);
+
+ bool is_compressed = false;
+ if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
+ {
+ is_compressed = true;
+ }
+
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP2);
+ llverify(bindTextureInternal(0));
+ }
+
+ if (mUseMipMaps)
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP3);
+ if (data_hasmips)
+ {
+ // NOTE: data_in points to largest image; smaller images
+ // are stored BEFORE the largest image
+ for (S32 d=mCurrentDiscardLevel; d<=mMaxDiscardLevel; d++)
+ {
+ S32 w = getWidth(d);
+ S32 h = getHeight(d);
+ S32 gl_level = d-mCurrentDiscardLevel;
+ if (d > mCurrentDiscardLevel)
+ {
+ data_in -= dataFormatBytes(mFormatPrimary, w, h); // see above comment
+ }
+ if (is_compressed)
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
+ S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
+ glCompressedTexImage2DARB(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
+ stop_glerror();
+ }
+ else
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ glTexImage2D(mTarget, gl_level, mFormatInternal, w, h, 0, mFormatPrimary, GL_UNSIGNED_BYTE, (GLvoid*)data_in);
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ stop_glerror();
+ }
+ stop_glerror();
+ }
+ }
+ else if (!is_compressed)
+ {
+ if (mAutoGenMips)
+ {
+ glTexParameteri(mBindTarget, GL_GENERATE_MIPMAP_SGIS, TRUE);
+ stop_glerror();
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP4);
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ glTexImage2D(mTarget, 0, mFormatInternal,
+ getWidth(mCurrentDiscardLevel), getHeight(mCurrentDiscardLevel), 0,
+ mFormatPrimary, mFormatType,
+ data_in);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+ }
+ }
+ else
+ {
+ // Create mips by hand
+ // about 30% faster than autogen on ATI 9800, 50% slower on nVidia 4800
+ // ~4x faster than gluBuild2DMipmaps
+ S32 width = getWidth(mCurrentDiscardLevel);
+ S32 height = getHeight(mCurrentDiscardLevel);
+ S32 nummips = mMaxDiscardLevel - mCurrentDiscardLevel + 1;
+ S32 w = width, h = height;
+ const U8* prev_mip_data = 0;
+ const U8* cur_mip_data = 0;
+ for (int m=0; m<nummips; m++)
+ {
+ if (m==0)
+ {
+ cur_mip_data = data_in;
+ }
+ else
+ {
+ S32 bytes = w * h * mComponents;
+ U8* new_data = new U8[bytes];
+ LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
+ cur_mip_data = new_data;
+ }
+ llassert(w > 0 && h > 0 && cur_mip_data);
+ {
+// LLFastTimer t1(LLFastTimer::FTM_TEMP4);
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ glTexImage2D(mTarget, m, mFormatInternal, w, h, 0, mFormatPrimary, mFormatType, cur_mip_data);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+ }
+ if (prev_mip_data && prev_mip_data != data_in)
+ {
+ delete[] prev_mip_data;
+ }
+ prev_mip_data = cur_mip_data;
+ w >>= 1;
+ h >>= 1;
+ }
+ if (prev_mip_data && prev_mip_data != data_in)
+ {
+ delete[] prev_mip_data;
+ }
+ }
+ }
+ else
+ {
+ llerrs << "Compressed Image has mipmaps but data does not (can not auto generate compressed mips)" << llendl;
+ }
+ mHasMipMaps = TRUE;
+ }
+ else
+ {
+// LLFastTimer t2(LLFastTimer::FTM_TEMP5);
+ S32 w = getWidth();
+ S32 h = getHeight();
+ if (is_compressed)
+ {
+ S32 tex_size = dataFormatBytes(mFormatPrimary, w, h);
+ glCompressedTexImage2DARB(mTarget, 0, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in);
+ stop_glerror();
+ }
+ else
+ {
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ glTexImage2D(mTarget, 0, mFormatInternal, w, h, 0,
+ mFormatPrimary, mFormatType, (GLvoid *)data_in);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ }
+ mHasMipMaps = FALSE;
+ }
+ stop_glerror();
+}
+
+BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height)
+{
+ if (!width || !height)
+ {
+ return TRUE;
+ }
+ if (mTexName == 0)
+ {
+ llwarns << "Setting subimage on image without GL texture" << llendl;
+ return FALSE;
+ }
+
+ if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight())
+ {
+ setImage(datap, FALSE);
+ }
+ else
+ {
+ if (mUseMipMaps)
+ {
+ dump();
+ llerrs << "setSubImage called with mipmapped image (not supported)" << llendl;
+ }
+ llassert(mCurrentDiscardLevel == 0);
+ if (((x_pos + width) > getWidth()) ||
+ (y_pos + height) > getHeight())
+ {
+ dump();
+ llerrs << "Subimage not wholly in target image!"
+ << " x_pos " << x_pos
+ << " y_pos " << y_pos
+ << " width " << width
+ << " height " << height
+ << " getWidth() " << getWidth()
+ << " getHeight() " << getHeight()
+ << llendl;
+ }
+
+ if ((x_pos + width) > data_width ||
+ (y_pos + height) > data_height)
+ {
+ dump();
+ llerrs << "Subimage not wholly in source image!"
+ << " x_pos " << x_pos
+ << " y_pos " << y_pos
+ << " width " << width
+ << " height " << height
+ << " source_width " << data_width
+ << " source_height " << data_height
+ << llendl;
+ }
+
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, data_width);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
+ stop_glerror();
+ }
+
+ datap += (y_pos * data_width + x_pos) * getComponents();
+ // Update the GL texture
+ llverify(bindTextureInternal(0));
+ stop_glerror();
+
+ glTexSubImage2D(mTarget, 0, x_pos, y_pos,
+ width, height, mFormatPrimary, mFormatType, datap);
+ stop_glerror();
+
+ if(mFormatSwapBytes)
+ {
+ glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
+ stop_glerror();
+ }
+
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ stop_glerror();
+ }
+
+ return TRUE;
+}
+
+BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height)
+{
+ return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height);
+}
+
+// Copy sub image from frame buffer
+BOOL LLImageGL::setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height)
+{
+ if (bindTextureInternal(0))
+ {
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, fb_x, fb_y, x_pos, y_pos, width, height);
+ stop_glerror();
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLImageGL::createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename/*=0*/)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llwarns << "Trying to create a texture while GL is disabled!" << llendl;
+ return FALSE;
+ }
+ llassert(gGLManager.mInited || gNoRender);
+ stop_glerror();
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+ discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
+
+ // Actual image width/height = raw image width/height * 2^discard_level
+ S32 w = imageraw->getWidth() << discard_level;
+ S32 h = imageraw->getHeight() << discard_level;
+
+ // setSize may call destroyGLTexture if the size does not match
+ setSize(w, h, imageraw->getComponents());
+
+ if( !mHasExplicitFormat )
+ {
+ switch (mComponents)
+ {
+ case 1:
+ // Use luminance alpha (for fonts)
+ mFormatInternal = GL_LUMINANCE8;
+ mFormatPrimary = GL_LUMINANCE;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 2:
+ // Use luminance alpha (for fonts)
+ mFormatInternal = GL_LUMINANCE8_ALPHA8;
+ mFormatPrimary = GL_LUMINANCE_ALPHA;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 3:
+ mFormatInternal = GL_RGB8;
+ mFormatPrimary = GL_RGB;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ case 4:
+ mFormatInternal = GL_RGBA8;
+ mFormatPrimary = GL_RGBA;
+ mFormatType = GL_UNSIGNED_BYTE;
+ break;
+ default:
+ llerrs << "Bad number of components for texture: " << (U32)getComponents() << llendl;
+ }
+ }
+
+ const U8* rawdata = imageraw->getData();
+ return createGLTexture(discard_level, rawdata, FALSE, usename);
+}
+
+BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_hasmips, S32 usename)
+{
+ llassert(data_in);
+
+ if (discard_level < 0)
+ {
+ llassert(mCurrentDiscardLevel >= 0);
+ discard_level = mCurrentDiscardLevel;
+ }
+ discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
+
+ if (mTexName != 0 && discard_level == mCurrentDiscardLevel)
+ {
+ // This will only be true if the size has not changed
+ setImage(data_in, data_hasmips);
+ return TRUE;
+ }
+
+ GLuint old_name = mTexName;
+// S32 old_discard = mCurrentDiscardLevel;
+
+ if (usename != 0)
+ {
+ mTexName = usename;
+ }
+ else
+ {
+ glGenTextures(1, (GLuint*)&mTexName);
+ stop_glerror();
+ {
+// LLFastTimer t1(LLFastTimer::FTM_TEMP6);
+ llverify(bindTextureInternal(0));
+ glTexParameteri(mBindTarget, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(mBindTarget, GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level);
+ }
+ }
+ if (!mTexName)
+ {
+ llerrs << "LLImageGL::createGLTexture failed to make texture" << llendl;
+ }
+
+ if (mUseMipMaps)
+ {
+ mAutoGenMips = gGLManager.mHasMipMapGeneration;
+#if LL_DARWIN
+ // On the Mac GF2 and GF4MX drivers, auto mipmap generation doesn't work right with alpha-only textures.
+ if(gGLManager.mIsGF2or4MX && (mFormatInternal == GL_ALPHA8) && (mFormatPrimary == GL_ALPHA))
+ {
+ mAutoGenMips = FALSE;
+ }
+#endif
+ }
+
+ mCurrentDiscardLevel = discard_level;
+
+ setImage(data_in, data_hasmips);
+
+ setClamp(mClampS, mClampT);
+ setMipFilterNearest(mMipFilterNearest);
+
+ // things will break if we don't unbind after creation
+ unbindTexture(0, mBindTarget);
+ stop_glerror();
+
+ if (old_name != 0)
+ {
+ sGlobalTextureMemory -= mTextureMemory;
+ glDeleteTextures(1, &old_name);
+ stop_glerror();
+ }
+
+ mTextureMemory = getMipBytes(discard_level);
+ sGlobalTextureMemory += mTextureMemory;
+
+ // mark this as bound at this point, so we don't throw it out immediately
+ mLastBindTime = sLastFrameTime;
+
+ return TRUE;
+}
+
+BOOL LLImageGL::setDiscardLevel(S32 discard_level)
+{
+ llassert(discard_level >= 0);
+ llassert(mCurrentDiscardLevel >= 0);
+
+ discard_level = llclamp(discard_level, 0, (S32)mMaxDiscardLevel);
+
+ if (mDontDiscard)
+ {
+ // don't discard!
+ return FALSE;
+ }
+ else if (discard_level == mCurrentDiscardLevel)
+ {
+ // nothing to do
+ return FALSE;
+ }
+ else if (discard_level < mCurrentDiscardLevel)
+ {
+ // larger image
+ dump();
+ llerrs << "LLImageGL::setDiscardLevel() called with larger discard level; use createGLTexture()" << llendl;
+ return FALSE;
+ }
+ else if (mUseMipMaps)
+ {
+ LLPointer<LLImageRaw> imageraw = new LLImageRaw;
+ while(discard_level > mCurrentDiscardLevel)
+ {
+ if (readBackRaw(discard_level, imageraw))
+ {
+ break;
+ }
+ discard_level--;
+ }
+ if (discard_level == mCurrentDiscardLevel)
+ {
+ // unable to increase the discard level
+ return FALSE;
+ }
+ return createGLTexture(discard_level, imageraw);
+ }
+ else
+ {
+#ifndef LL_LINUX // FIXME: This should not be skipped for the linux client.
+ llerrs << "LLImageGL::setDiscardLevel() called on image without mipmaps" << llendl;
+#endif
+ return FALSE;
+ }
+}
+
+BOOL LLImageGL::readBackRaw(S32 discard_level, LLImageRaw* imageraw)
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+
+ if (mTexName == 0 || discard_level < mCurrentDiscardLevel)
+ {
+ return FALSE;
+ }
+
+ S32 gl_discard = discard_level - mCurrentDiscardLevel;
+
+ llverify(bindTextureInternal(0));
+
+ LLGLint glwidth = 0;
+ glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_WIDTH, (GLint*)&glwidth);
+ if (glwidth == 0)
+ {
+ // No mip data smaller than current discard level
+ return FALSE;
+ }
+
+ S32 width = getWidth(discard_level);
+ S32 height = getHeight(discard_level);
+ S32 ncomponents = getComponents();
+ if (ncomponents == 0)
+ {
+ return FALSE;
+ }
+
+ if (width <= 0 || width > 2048 || height <= 0 || height > 2048 || ncomponents < 1 || ncomponents > 4)
+ {
+ llerrs << llformat("LLImageGL::readBackRaw: bogus params: %d x %d x %d",width,height,ncomponents) << llendl;
+ }
+
+ LLGLint is_compressed = 0;
+ glGetTexLevelParameteriv(mTarget, is_compressed, GL_TEXTURE_COMPRESSED, (GLint*)&is_compressed);
+ if (is_compressed)
+ {
+ LLGLint glbytes;
+ glGetTexLevelParameteriv(mTarget, gl_discard, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint*)&glbytes);
+ imageraw->allocateDataSize(width, height, ncomponents, glbytes);
+ glGetCompressedTexImageARB(mTarget, gl_discard, (GLvoid*)(imageraw->getData()));
+ stop_glerror();
+ }
+ else
+ {
+ imageraw->allocateDataSize(width, height, ncomponents);
+ glGetTexImage(GL_TEXTURE_2D, gl_discard, mFormatPrimary, mFormatType, (GLvoid*)(imageraw->getData()));
+ stop_glerror();
+ }
+
+ return TRUE;
+}
+
+void LLImageGL::destroyGLTexture()
+{
+ stop_glerror();
+
+ if (mTexName != 0)
+ {
+ for (int i = 0; i < gGLManager.mNumTextureUnits; i++)
+ {
+ if (sCurrentBoundTextures[i] == mTexName)
+ {
+ unbindTexture(i, GL_TEXTURE_2D);
+ stop_glerror();
+ }
+ }
+
+ sGlobalTextureMemory -= mTextureMemory;
+ mTextureMemory = 0;
+
+ glDeleteTextures(1, (GLuint*)&mTexName);
+ mTexName = 0;
+
+ stop_glerror();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+void LLImageGL::setClamp(BOOL clamps, BOOL clampt)
+{
+ mClampS = clamps;
+ mClampT = clampt;
+ if (mTexName != 0)
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_WRAP_S, clamps ? GL_CLAMP_TO_EDGE : GL_REPEAT);
+ glTexParameteri(mBindTarget, GL_TEXTURE_WRAP_T, clampt ? GL_CLAMP_TO_EDGE : GL_REPEAT);
+ }
+ stop_glerror();
+}
+
+void LLImageGL::setMipFilterNearest(BOOL nearest, BOOL min_nearest)
+{
+ mMipFilterNearest = nearest;
+
+ if (mTexName != 0)
+ {
+ if (min_nearest)
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ }
+ else if (mHasMipMaps)
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ }
+ else
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ }
+ if (mMipFilterNearest)
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ }
+ else
+ {
+ glTexParameteri(mBindTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+ if (gGLManager.mHasAnisotropic)
+ {
+ if (sGlobalUseAnisotropic && !mMipFilterNearest)
+ {
+ F32 largest_anisotropy;
+ glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_anisotropy);
+ glTexParameterf(mBindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, largest_anisotropy);
+ }
+ else
+ {
+ glTexParameterf(mBindTarget, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.f);
+ }
+ }
+ }
+
+ stop_glerror();
+}
+
+BOOL LLImageGL::getIsResident(BOOL test_now)
+{
+ if (test_now)
+ {
+ if (mTexName != 0)
+ {
+ glAreTexturesResident(1, (GLuint*)&mTexName, &mIsResident);
+ }
+ else
+ {
+ mIsResident = FALSE;
+ }
+ }
+
+ return mIsResident;
+}
+
+S32 LLImageGL::getHeight(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 height = mHeight >> discard_level;
+ if (height < 1) height = 1;
+ return height;
+}
+
+S32 LLImageGL::getWidth(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 width = mWidth >> discard_level;
+ if (width < 1) width = 1;
+ return width;
+}
+
+S32 LLImageGL::getBytes(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 w = mWidth>>discard_level;
+ S32 h = mHeight>>discard_level;
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+ return dataFormatBytes(mFormatPrimary, w, h);
+}
+
+S32 LLImageGL::getMipBytes(S32 discard_level) const
+{
+ if (discard_level < 0)
+ {
+ discard_level = mCurrentDiscardLevel;
+ }
+ S32 w = mWidth>>discard_level;
+ S32 h = mHeight>>discard_level;
+ S32 res = dataFormatBytes(mFormatPrimary, w, h);
+ if (mUseMipMaps)
+ {
+ while (w > 1 && h > 1)
+ {
+ w >>= 1; if (w == 0) w = 1;
+ h >>= 1; if (h == 0) h = 1;
+ res += dataFormatBytes(mFormatPrimary, w, h);
+ }
+ }
+ return res;
+}
+
+BOOL LLImageGL::getBoundRecently() const
+{
+ return (BOOL)(sLastFrameTime - mLastBindTime < MIN_TEXTURE_LIFETIME);
+}
+
+void LLImageGL::setTarget(const LLGLenum target, const LLGLenum bind_target)
+{
+ mTarget = target;
+ mBindTarget = bind_target;
+}
+
+//----------------------------------------------------------------------------
+
+// Manual Mip Generation
+/*
+ S32 width = getWidth(discard_level);
+ S32 height = getHeight(discard_level);
+ S32 w = width, h = height;
+ S32 nummips = 1;
+ while (w > 4 && h > 4)
+ {
+ w >>= 1; h >>= 1;
+ nummips++;
+ }
+ stop_glerror();
+ w = width, h = height;
+ const U8* prev_mip_data = 0;
+ const U8* cur_mip_data = 0;
+ for (int m=0; m<nummips; m++)
+ {
+ if (m==0)
+ {
+ cur_mip_data = rawdata;
+ }
+ else
+ {
+ S32 bytes = w * h * mComponents;
+ U8* new_data = new U8[bytes];
+ LLImageBase::generateMip(prev_mip_data, new_data, w, h, mComponents);
+ cur_mip_data = new_data;
+ }
+ llassert(w > 0 && h > 0 && cur_mip_data);
+ U8 test = cur_mip_data[w*h*mComponents-1];
+ {
+ glTexImage2D(mTarget, m, mFormatInternal, w, h, 0, mFormatPrimary, mFormatType, cur_mip_data);
+ stop_glerror();
+ }
+ if (prev_mip_data && prev_mip_data != rawdata)
+ {
+ delete prev_mip_data;
+ }
+ prev_mip_data = cur_mip_data;
+ w >>= 1;
+ h >>= 1;
+ }
+ if (prev_mip_data && prev_mip_data != rawdata)
+ {
+ delete prev_mip_data;
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, nummips);
+*/
diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h
new file mode 100644
index 0000000000..f8c6a008eb
--- /dev/null
+++ b/indra/llrender/llimagegl.h
@@ -0,0 +1,185 @@
+/**
+ * @file llimagegl.h
+ * @brief Object for managing images and their textures
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLIMAGEGL_H
+#define LL_LLIMAGEGL_H
+
+#include "llimage.h"
+
+#include "llgltypes.h"
+#include "llmemory.h"
+
+//============================================================================
+
+class LLImageGL : public LLThreadSafeRefCount
+{
+public:
+ // Size calculation
+ static S32 dataFormatBits(S32 dataformat);
+ static S32 dataFormatBytes(S32 dataformat, S32 width, S32 height);
+ static S32 dataFormatComponents(S32 dataformat);
+
+ // Wrapper for glBindTexture that keeps LLImageGL in sync.
+ // Usually you want stage = 0 and bind_target = GL_TEXTURE_2D
+ static void bindExternalTexture( LLGLuint gl_name, S32 stage, LLGLenum bind_target);
+ static void unbindTexture(S32 stage, LLGLenum target);
+
+ // needs to be called every frame
+ static void updateStats(F32 current_time);
+
+ // Save off / restore GL textures
+ static void destroyGL(BOOL save_state = TRUE);
+ static void restoreGL();
+
+ // Sometimes called externally for textures not using LLImageGL (should go away...)
+ static S32 updateBoundTexMem(const S32 delta);
+
+ static bool checkSize(S32 width, S32 height);
+
+ // Not currently necessary for LLImageGL, but required in some derived classes,
+ // so include for compatability
+ static BOOL create(LLPointer<LLImageGL>& dest, BOOL usemipmaps = TRUE);
+ static BOOL create(LLPointer<LLImageGL>& dest, U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE);
+ static BOOL create(LLPointer<LLImageGL>& dest, const LLImageRaw* imageraw, BOOL usemipmaps = TRUE);
+
+public:
+ LLImageGL(BOOL usemipmaps = TRUE);
+ LLImageGL(U32 width, U32 height, U8 components, BOOL usemipmaps = TRUE);
+ LLImageGL(const LLImageRaw* imageraw, BOOL usemipmaps = TRUE);
+
+protected:
+ virtual ~LLImageGL();
+ BOOL bindTextureInternal(const S32 stage = 0) const;
+
+public:
+ virtual void dump(); // debugging info to llinfos
+ virtual BOOL bind(const S32 stage = 0) const;
+
+ void setSize(S32 width, S32 height, S32 ncomponents);
+
+ BOOL createGLTexture(S32 discard_level, const LLImageRaw* imageraw, S32 usename = 0);
+ BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0);
+ void setImage(const LLImageRaw* imageraw);
+ void setImage(const U8* data_in, BOOL data_hasmips = FALSE);
+ BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height);
+ BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height);
+ BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height);
+ BOOL setDiscardLevel(S32 discard_level);
+ BOOL readBackRaw(S32 discard_level, LLImageRaw* imageraw); // Read back a raw image for this discard level, if it exists
+ void destroyGLTexture();
+
+ void setClamp(BOOL clamps, BOOL clampt);
+ void setMipFilterNearest(BOOL nearest, BOOL min_nearest = FALSE);
+ void setExplicitFormat(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format = 0, BOOL swap_bytes = FALSE);
+ void dontDiscard() { mDontDiscard = 1; }
+
+ S32 getDiscardLevel() const { return mCurrentDiscardLevel; }
+ S32 getMaxDiscardLevel() const { return mMaxDiscardLevel; }
+
+ S32 getWidth(S32 discard_level = -1) const;
+ S32 getHeight(S32 discard_level = -1) const;
+ U8 getComponents() const { return mComponents; }
+ S32 getBytes(S32 discard_level = -1) const;
+ S32 getMipBytes(S32 discard_level = -1) const;
+ BOOL getBoundRecently() const;
+ LLGLenum getPrimaryFormat() const { return mFormatPrimary; }
+
+ BOOL getClampS() const { return mClampS; }
+ BOOL getClampT() const { return mClampT; }
+ BOOL getMipFilterNearest() const { return mMipFilterNearest; }
+
+ BOOL getHasGLTexture() const { return mTexName != 0; }
+ LLGLuint getTexName() const { return mTexName; }
+
+ BOOL getIsResident(BOOL test_now = FALSE); // not const
+
+ void setTarget(const LLGLenum target, const LLGLenum bind_target);
+
+ BOOL getUseMipMaps() const { return mUseMipMaps; }
+ void setUseMipMaps(BOOL usemips) { mUseMipMaps = usemips; }
+ BOOL getUseDiscard() const { return mUseMipMaps && !mDontDiscard; }
+ BOOL getDontDiscard() const { return mDontDiscard; }
+
+protected:
+ void init(BOOL usemipmaps);
+ virtual void cleanup(); // Clean up the LLImageGL so it can be reinitialized. Be careful when using this in derived class destructors
+
+public:
+ // Various GL/Rendering options
+ S32 mTextureMemory;
+ mutable F32 mLastBindTime; // last time this was bound, by discard level
+ mutable F32 mLastBindAttempt; // last time bindTexture was called on this texture
+
+private:
+ LLPointer<LLImageRaw> mSaveData; // used for destroyGL/restoreGL
+ S8 mUseMipMaps;
+ S8 mHasMipMaps;
+ S8 mHasExplicitFormat; // If false (default), GL format is f(mComponents)
+ S8 mAutoGenMips;
+
+protected:
+ LLGLenum mTarget; // Normally GL_TEXTURE2D, sometimes something else (ex. cube maps)
+ LLGLenum mBindTarget; // NOrmally GL_TEXTURE2D, sometimes something else (ex. cube maps)
+
+ LLGLuint mTexName;
+
+ LLGLboolean mIsResident;
+
+ U16 mWidth;
+ U16 mHeight;
+
+ S8 mComponents;
+ S8 mMaxDiscardLevel;
+ S8 mCurrentDiscardLevel;
+ S8 mDontDiscard; // Keep full res version of this image (for UI, etc)
+
+ S8 mClampS; // Need to save clamp state
+ S8 mClampT;
+ S8 mMipFilterNearest; // if TRUE, set magfilter to GL_NEAREST
+
+ LLGLint mFormatInternal; // = GL internalformat
+ LLGLenum mFormatPrimary; // = GL format (pixel data format)
+ LLGLenum mFormatType;
+ BOOL mFormatSwapBytes;// if true, use glPixelStorei(GL_UNPACK_SWAP_BYTES, 1)
+
+ // STATICS
+public:
+ static std::set<LLImageGL*> LLImageGL::sImageList;
+ static S32 sCount;
+
+ static F32 sLastFrameTime;
+
+ static LLGLuint sCurrentBoundTextures[MAX_GL_TEXTURE_UNITS]; // Currently bound texture ID
+
+ // Global memory statistics
+ static S32 sGlobalTextureMemory; // Tracks main memory texmem
+ static S32 sBoundTextureMemory; // Tracks bound texmem for last completed frame
+ static S32 sCurBoundTextureMemory; // Tracks bound texmem for current frame
+
+ static BOOL sGlobalUseAnisotropic;
+
+#if DEBUG_MISS
+ BOOL mMissed; // Missed on last bind?
+ BOOL getMissed() const { return mMissed; };
+#else
+ BOOL getMissed() const { return FALSE; };
+#endif
+};
+
+//RN: maybe this needs to moved elsewhere?
+class LLImageProviderInterface
+{
+public:
+ LLImageProviderInterface() {};
+ virtual ~LLImageProviderInterface() {};
+
+ virtual LLImageGL* getUIImageByID(const LLUUID& id, BOOL clamped = TRUE) = 0;
+};
+
+#endif // LL_LLIMAGEGL_H
diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp
new file mode 100644
index 0000000000..c02be6bb8d
--- /dev/null
+++ b/indra/llui/llbutton.cpp
@@ -0,0 +1,1012 @@
+/**
+ * @file llbutton.cpp
+ * @brief LLButton base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llbutton.h"
+
+// Linden library includes
+#include "v4color.h"
+#include "llstring.h"
+
+// Project includes
+#include "llkeyboard.h"
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+//#include "llcallbacklist.h"
+#include "llresmgr.h"
+#include "llcriticaldamp.h"
+#include "llglheaders.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+
+// globals loaded from settings.xml
+S32 LLBUTTON_ORIG_H_PAD = 6; // Pre-zoomable UI
+S32 LLBUTTON_H_PAD = 0;
+S32 LLBUTTON_V_PAD = 0;
+S32 BTN_HEIGHT_SMALL= 0;
+S32 BTN_HEIGHT = 0;
+
+S32 BTN_GRID = 12;
+S32 BORDER_SIZE = 1;
+
+// static
+LLFrameTimer LLButton::sFlashingTimer;
+
+LLButton::LLButton( const LLString& name, const LLRect& rect, const LLString& control_name, void (*click_callback)(void*), void *callback_data)
+: LLUICtrl(name, rect, TRUE, NULL, NULL),
+ mClickedCallback( click_callback ),
+ mMouseDownCallback( NULL ),
+ mMouseUpCallback( NULL ),
+ mHeldDownCallback( NULL ),
+ mGLFont( NULL ),
+ mHeldDownDelay( 0.5f ), // seconds until held-down callback is called
+ mImageUnselected( NULL ),
+ mImageSelected( NULL ),
+ mImageHoverSelected( NULL ),
+ mImageHoverUnselected( NULL ),
+ mImageDisabled( NULL ),
+ mImageDisabledSelected( NULL ),
+ mToggleState( FALSE ),
+ mScaleImage( TRUE ),
+ mDropShadowedText( TRUE ),
+ mBorderEnabled( FALSE ),
+ mFlashing( FALSE ),
+ mHAlign( LLFontGL::HCENTER ),
+ mLeftHPad( LLBUTTON_H_PAD ),
+ mRightHPad( LLBUTTON_H_PAD ),
+ mFixedWidth( 16 ),
+ mFixedHeight( 16 ),
+ mHoverGlowStrength(0.15f),
+ mCurGlowStrength(0.f),
+ mNeedsHighlight(FALSE),
+ mCommitOnReturn(TRUE),
+ mImagep( NULL )
+{
+ mUnselectedLabel = name;
+ mSelectedLabel = name;
+
+ setImageUnselected("button_enabled_32x128.tga");
+ setImageSelected("button_enabled_selected_32x128.tga");
+ setImageDisabled("button_disabled_32x128.tga");
+ setImageDisabledSelected("button_disabled_32x128.tga");
+
+ mImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" );
+ mDisabledImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" );
+
+ init(click_callback, callback_data, NULL, control_name);
+}
+
+
+LLButton::LLButton(const LLString& name, const LLRect& rect,
+ const LLString &unselected_image_name,
+ const LLString &selected_image_name,
+ const LLString& control_name,
+ void (*click_callback)(void*),
+ void *callback_data,
+ const LLFontGL *font,
+ const LLString& unselected_label,
+ const LLString& selected_label )
+: LLUICtrl(name, rect, TRUE, NULL, NULL),
+ mClickedCallback( click_callback ),
+ mMouseDownCallback( NULL ),
+ mMouseUpCallback( NULL ),
+ mHeldDownCallback( NULL ),
+ mGLFont( NULL ),
+ mHeldDownDelay( 0.5f ), // seconds until held-down callback is called
+ mImageUnselected( NULL ),
+ mImageSelected( NULL ),
+ mImageHoverSelected( NULL ),
+ mImageHoverUnselected( NULL ),
+ mImageDisabled( NULL ),
+ mImageDisabledSelected( NULL ),
+ mToggleState( FALSE ),
+ mScaleImage( TRUE ),
+ mDropShadowedText( TRUE ),
+ mBorderEnabled( FALSE ),
+ mFlashing( FALSE ),
+ mHAlign( LLFontGL::HCENTER ),
+ mLeftHPad( LLBUTTON_H_PAD ),
+ mRightHPad( LLBUTTON_H_PAD ),
+ mFixedWidth( 16 ),
+ mFixedHeight( 16 ),
+ mHoverGlowStrength(0.25f),
+ mCurGlowStrength(0.f),
+ mNeedsHighlight(FALSE),
+ mCommitOnReturn(TRUE),
+ mImagep( NULL )
+{
+ mUnselectedLabel = unselected_label;
+ mSelectedLabel = selected_label;
+
+ // by default, disabled color is same as enabled
+ mImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" );
+ mDisabledImageColor = LLUI::sColorsGroup->getColor( "ButtonImageColor" );
+
+ if( unselected_image_name != "" )
+ {
+ setImageUnselected(unselected_image_name);
+ setImageDisabled(unselected_image_name);
+
+ mDisabledImageColor.mV[VALPHA] = 0.5f;
+ mImageDisabled = mImageUnselected;
+ mDisabledImageColor.mV[VALPHA] = 0.5f;
+ // user-specified image - don't use fixed borders unless requested
+ mFixedWidth = 0;
+ mFixedHeight = 0;
+ mScaleImage = FALSE;
+ }
+ else
+ {
+ setImageUnselected("button_enabled_32x128.tga");
+ setImageDisabled("button_disabled_32x128.tga");
+ }
+
+ if( selected_image_name != "" )
+ {
+ setImageSelected(selected_image_name);
+ setImageDisabledSelected(selected_image_name);
+
+ mDisabledImageColor.mV[VALPHA] = 0.5f;
+ // user-specified image - don't use fixed borders unless requested
+ mFixedWidth = 0;
+ mFixedHeight = 0;
+ mScaleImage = FALSE;
+ }
+ else
+ {
+ setImageSelected("button_enabled_selected_32x128.tga");
+ setImageDisabledSelected("button_disabled_32x128.tga");
+ }
+
+ init(click_callback, callback_data, font, control_name);
+}
+
+void LLButton::init(void (*click_callback)(void*), void *callback_data, const LLFontGL* font, const LLString& control_name)
+{
+ mGLFont = ( font ? font : LLFontGL::sSansSerif);
+
+ // Hack to make sure there is space for at least one character
+ if (mRect.getWidth() - (mRightHPad + mLeftHPad) < mGLFont->getWidth(" "))
+ {
+ // Use old defaults
+ mLeftHPad = LLBUTTON_ORIG_H_PAD;
+ mRightHPad = LLBUTTON_ORIG_H_PAD;
+ }
+
+ mCallbackUserData = callback_data;
+ mMouseDownTimer.stop();
+
+ setControlName(control_name, NULL);
+
+ mUnselectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelColor" ) );
+ mSelectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelSelectedColor" ) );
+ mDisabledLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelDisabledColor" ) );
+ mDisabledSelectedLabelColor = ( LLUI::sColorsGroup->getColor( "ButtonLabelSelectedDisabledColor" ) );
+ mHighlightColor = ( LLUI::sColorsGroup->getColor( "ButtonUnselectedFgColor" ) );
+ mUnselectedBgColor = ( LLUI::sColorsGroup->getColor( "ButtonUnselectedBgColor" ) );
+ mSelectedBgColor = ( LLUI::sColorsGroup->getColor( "ButtonSelectedBgColor" ) );
+}
+
+LLButton::~LLButton()
+{
+ if( this == gFocusMgr.getMouseCapture() )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ }
+}
+
+// virtual
+EWidgetType LLButton::getWidgetType() const
+{
+ return WIDGET_TYPE_BUTTON;
+}
+
+// virtual
+LLString LLButton::getWidgetTag() const
+{
+ return LL_BUTTON_TAG;
+}
+
+// HACK: Committing a button is the same as instantly clicking it.
+// virtual
+void LLButton::onCommit()
+{
+ // WARNING: Sometimes clicking a button destroys the floater or
+ // panel containing it. Therefore we need to call mClickedCallback
+ // LAST, otherwise this becomes deleted memory.
+ LLUICtrl::onCommit();
+
+ if (mMouseDownCallback)
+ {
+ (*mMouseDownCallback)(mCallbackUserData);
+ }
+
+ if (mMouseUpCallback)
+ {
+ (*mMouseUpCallback)(mCallbackUserData);
+ }
+
+ if (mSoundFlags & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ if (mSoundFlags & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ if (mClickedCallback)
+ {
+ (*mClickedCallback)( mCallbackUserData );
+ }
+}
+
+BOOL LLButton::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char && !gKeyboard->getKeyRepeated(' '))
+ {
+ if (mClickedCallback)
+ {
+ (*mClickedCallback)( mCallbackUserData );
+ }
+ handled = TRUE;
+ }
+ return handled;
+}
+
+BOOL LLButton::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ if( mCommitOnReturn && KEY_RETURN == key && mask == MASK_NONE && !gKeyboard->getKeyRepeated(key))
+ {
+ if (mClickedCallback)
+ {
+ (*mClickedCallback)( mCallbackUserData );
+ }
+ handled = TRUE;
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLButton::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this, &LLButton::onMouseCaptureLost );
+
+ if (hasTabStop() && !getIsChrome())
+ {
+ setFocus(TRUE);
+ }
+
+ if (mMouseDownCallback)
+ {
+ (*mMouseDownCallback)(mCallbackUserData);
+ }
+
+ mMouseDownTimer.start();
+
+ if (mSoundFlags & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLButton::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // We only handle the click if the click both started and ended within us
+ if( this == gFocusMgr.getMouseCapture() )
+ {
+ // Regardless of where mouseup occurs, handle callback
+ if (mMouseUpCallback)
+ {
+ (*mMouseUpCallback)(mCallbackUserData);
+ }
+
+ mMouseDownTimer.stop();
+
+ // Always release the mouse
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
+ // If mouseup in the widget, it's been clicked
+ if (pointInView(x, y))
+ {
+ if (mSoundFlags & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ if (mClickedCallback)
+ {
+ (*mClickedCallback)( mCallbackUserData );
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLButton::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLMouseHandler* other_captor = gFocusMgr.getMouseCapture();
+ mNeedsHighlight = other_captor == NULL ||
+ other_captor == this ||
+ // this following bit is to support modal dialogs
+ (other_captor->isView() && hasAncestor((LLView*)other_captor));
+
+ if (mMouseDownTimer.getStarted() && NULL != mHeldDownCallback)
+ {
+ F32 elapsed = mMouseDownTimer.getElapsedTimeF32();
+ if( mHeldDownDelay < elapsed )
+ {
+ mHeldDownCallback( mCallbackUserData );
+ }
+ }
+
+ // We only handle the click if the click both started and ended within us
+ if( this == gFocusMgr.getMouseCapture() )
+ {
+ handled = TRUE;
+ }
+ else if( getVisible() )
+ {
+ // Opaque
+ handled = TRUE;
+ }
+
+ if( handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ }
+
+ return handled;
+}
+
+
+// virtual
+void LLButton::draw()
+{
+ if( getVisible() )
+ {
+ BOOL flash = FALSE;
+ if( mFlashing )
+ {
+ F32 elapsed = LLButton::sFlashingTimer.getElapsedTimeF32();
+ flash = S32(elapsed * 2) & 1;
+ }
+
+ BOOL pressed_by_keyboard = FALSE;
+ if (hasFocus())
+ {
+ pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN));
+ }
+
+ // Unselected image assignments
+ S32 local_mouse_x;
+ S32 local_mouse_y;
+ LLCoordWindow cursor_pos_window;
+ getWindow()->getCursorPosition(&cursor_pos_window);
+ LLCoordGL cursor_pos_gl;
+ getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
+ cursor_pos_gl.mX = llround((F32)cursor_pos_gl.mX / LLUI::sGLScaleFactor.mV[VX]);
+ cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]);
+ screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y);
+
+ BOOL pressed = pressed_by_keyboard || (this == gFocusMgr.getMouseCapture() && pointInView(local_mouse_x, local_mouse_y));
+
+ BOOL display_state = FALSE;
+ if( pressed )
+ {
+ mImagep = mImageSelected;
+ // show the resulting state after releasing the mouse button while it is down
+ display_state = mToggleState ? FALSE : TRUE;
+ }
+ else
+ {
+ display_state = mToggleState || flash;
+ }
+
+ BOOL use_glow_effect = FALSE;
+ if ( mNeedsHighlight )
+ {
+ if (display_state)
+ {
+ if (mImageHoverSelected)
+ {
+ mImagep = mImageHoverSelected;
+ }
+ else
+ {
+ mImagep = mImageSelected;
+ use_glow_effect = TRUE;
+ }
+ }
+ else
+ {
+ if (mImageHoverUnselected)
+ {
+ mImagep = mImageHoverUnselected;
+ }
+ else
+ {
+ mImagep = mImageUnselected;
+ use_glow_effect = TRUE;
+ }
+ }
+ }
+ else if ( display_state )
+ {
+ mImagep = mImageSelected;
+ }
+ else
+ {
+ mImagep = mImageUnselected;
+ }
+
+ // Override if more data is available
+ // HACK: Use gray checked state to mean either:
+ // enabled and tentative
+ // or
+ // disabled but checked
+ if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && display_state ) ) )
+ {
+ mImagep = mImageDisabledSelected;
+ }
+ else if (!mImageDisabled.isNull() && !mEnabled && !display_state)
+ {
+ mImagep = mImageDisabled;
+ }
+
+ if (mNeedsHighlight && !mImagep)
+ {
+ use_glow_effect = TRUE;
+ }
+
+ // Figure out appropriate color for the text
+ LLColor4 label_color;
+
+ if ( mEnabled )
+ {
+ if ( !display_state )
+ {
+ label_color = mUnselectedLabelColor;
+ }
+ else
+ {
+ label_color = mSelectedLabelColor;
+ }
+ }
+ else
+ {
+ if ( !display_state )
+ {
+ label_color = mDisabledLabelColor;
+ }
+ else
+ {
+ label_color = mDisabledSelectedLabelColor;
+ }
+ }
+
+ // Unselected label assignments
+ LLWString label;
+
+ if( display_state )
+ {
+ if( mEnabled || mDisabledSelectedLabel.empty() )
+ {
+ label = mSelectedLabel;
+ }
+ else
+ {
+ label = mDisabledSelectedLabel;
+ }
+ }
+ else
+ {
+ if( mEnabled || mDisabledLabel.empty() )
+ {
+ label = mUnselectedLabel;
+ }
+ else
+ {
+ label = mDisabledLabel;
+ }
+ }
+
+ // draw default button border
+ if (mEnabled && mBorderEnabled && gFocusMgr.getAppHasFocus()) // because we're the default button in a panel
+ {
+ drawBorder(LLUI::sColorsGroup->getColor( "ButtonBorderColor" ), BORDER_SIZE);
+ }
+
+ // overlay with keyboard focus border
+ if (hasFocus())
+ {
+ F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
+ drawBorder(gFocusMgr.getFocusColor(), llround(lerp(1.f, 3.f, lerp_amt)));
+ }
+
+ if (use_glow_effect)
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f));
+ }
+ else
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
+ }
+
+ // Draw button image, if available.
+ // Otherwise draw basic rectangular button.
+ if( mImagep.notNull() && !mScaleImage)
+ {
+ gl_draw_image( 0, 0, mImagep, mEnabled ? mImageColor : mDisabledImageColor );
+ if (mCurGlowStrength > 0.01f)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ gl_draw_scaled_image_with_border(0, 0, 0, 0, mImagep->getWidth(), mImagep->getHeight(), mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else
+ if ( mImagep.notNull() && mScaleImage)
+ {
+ gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(),
+ mImagep, mEnabled ? mImageColor : mDisabledImageColor );
+ if (mCurGlowStrength > 0.01f)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ gl_draw_scaled_image_with_border(0, 0, mFixedWidth, mFixedHeight, mRect.getWidth(), mRect.getHeight(),
+ mImagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else
+ {
+ // no image
+ llalerts << "No image for button " << mName << llendl;
+ // draw it in pink so we can find it
+ gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE);
+ }
+
+ // Draw label
+ if( !label.empty() )
+ {
+ S32 drawable_width = mRect.getWidth() - mLeftHPad - mRightHPad;
+
+ LLWString::trim(label);
+
+ S32 x;
+ switch( mHAlign )
+ {
+ case LLFontGL::RIGHT:
+ x = mRect.getWidth() - mRightHPad;
+ break;
+ case LLFontGL::HCENTER:
+ x = mRect.getWidth() / 2;
+ break;
+ case LLFontGL::LEFT:
+ default:
+ x = mLeftHPad;
+ break;
+ }
+
+ S32 y_offset = 2 + (mRect.getHeight() - 20)/2;
+
+ if (pressed || display_state)
+ {
+ y_offset--;
+ x++;
+ }
+
+ mGLFont->render(label, 0, (F32)x, (F32)(LLBUTTON_V_PAD + y_offset),
+ label_color,
+ mHAlign, LLFontGL::BOTTOM,
+ mDropShadowedText ? LLFontGL::DROP_SHADOW : LLFontGL::NORMAL,
+ U32_MAX, drawable_width,
+ NULL, FALSE, FALSE);
+ }
+
+ if (sDebugRects
+ || (LLView::sEditingUI && this == LLView::sEditingUIView))
+ {
+ drawDebugRect();
+ }
+ }
+ // reset hover status for next frame
+ mNeedsHighlight = FALSE;
+}
+
+void LLButton::drawBorder(const LLColor4& color, S32 size)
+{
+ S32 left = -size;
+ S32 top = mRect.getHeight() + size;
+ S32 right = mRect.getWidth() + size;
+ S32 bottom = -size;
+
+ if (mImagep.isNull())
+ {
+ gl_rect_2d(left, top, right, bottom, color, FALSE);
+ return;
+ }
+
+ if (mScaleImage)
+ {
+ gl_draw_scaled_image_with_border(left, bottom, mFixedWidth, mFixedHeight, right-left, top-bottom,
+ mImagep, color, TRUE );
+ }
+ else
+ {
+ gl_draw_scaled_image_with_border(left, bottom, 0, 0, mImagep->getWidth() + size * 2,
+ mImagep->getHeight() + size * 2, mImagep, color, TRUE );
+ }
+}
+
+void LLButton::setClickedCallback(void (*cb)(void*), void* userdata)
+{
+ mClickedCallback = cb;
+ if (userdata)
+ {
+ mCallbackUserData = userdata;
+ }
+}
+
+
+void LLButton::setToggleState(BOOL b)
+{
+ if( b != mToggleState )
+ {
+ mToggleState = b;
+ LLValueChangedEvent *evt = new LLValueChangedEvent(this, mToggleState);
+ fireEvent(evt, "");
+ }
+}
+
+void LLButton::setValue(const LLSD& value )
+{
+ mToggleState = value.asBoolean();
+}
+
+LLSD LLButton::getValue() const
+{
+ return mToggleState;
+}
+
+void LLButton::setLabel( const LLString& label )
+{
+ setLabelUnselected(label);
+ setLabelSelected(label);
+}
+
+//virtual
+BOOL LLButton::setLabelArg( const LLString& key, const LLString& text )
+{
+ mUnselectedLabel.setArg(key, text);
+ mSelectedLabel.setArg(key, text);
+ return TRUE;
+}
+
+void LLButton::setLabelUnselected( const LLString& label )
+{
+ mUnselectedLabel = label;
+}
+
+void LLButton::setLabelSelected( const LLString& label )
+{
+ mSelectedLabel = label;
+}
+
+void LLButton::setDisabledLabel( const LLString& label )
+{
+ mDisabledLabel = label;
+}
+
+void LLButton::setDisabledSelectedLabel( const LLString& label )
+{
+ mDisabledSelectedLabel = label;
+}
+
+void LLButton::setImageUnselectedID( const LLUUID &image_id )
+{
+ mImageUnselectedName = "";
+ mImageUnselected = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+void LLButton::setImages( const LLString &image_name, const LLString &selected_name )
+{
+ setImageUnselected(image_name);
+ setImageSelected(selected_name);
+
+}
+
+void LLButton::setImageSelectedID( const LLUUID &image_id )
+{
+ mImageSelectedName = "";
+ mImageSelected = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+void LLButton::setImageColor(const LLColor4& c)
+{
+ mImageColor = c;
+}
+
+
+void LLButton::setImageDisabledID( const LLUUID &image_id )
+{
+ mImageDisabledName = "";
+ mImageDisabled = LLUI::sImageProvider->getUIImageByID(image_id);
+ mDisabledImageColor = mImageColor;
+ mDisabledImageColor.mV[VALPHA] *= 0.5f;
+}
+
+void LLButton::setImageDisabledSelectedID( const LLUUID &image_id )
+{
+ mImageDisabledSelectedName = "";
+ mImageDisabledSelected = LLUI::sImageProvider->getUIImageByID(image_id);
+ mDisabledImageColor = mImageColor;
+ mDisabledImageColor.mV[VALPHA] *= 0.5f;
+}
+
+void LLButton::setDisabledImages( const LLString &image_name, const LLString &selected_name, const LLColor4& c )
+{
+ setImageDisabled(image_name);
+ setImageDisabledSelected(selected_name);
+ mDisabledImageColor = c;
+}
+
+
+void LLButton::setImageHoverSelectedID( const LLUUID& image_id )
+{
+ mImageHoverSelectedName = "";
+ mImageHoverSelected = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+void LLButton::setDisabledImages( const LLString &image_name, const LLString &selected_name)
+{
+ LLColor4 clr = mImageColor;
+ clr.mV[VALPHA] *= .5f;
+ setDisabledImages( image_name, selected_name, clr );
+}
+
+void LLButton::setImageHoverUnselectedID( const LLUUID& image_id )
+{
+ mImageHoverUnselectedName = "";
+ mImageHoverUnselected = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+void LLButton::setHoverImages( const LLString& image_name, const LLString& selected_name )
+{
+ setImageHoverUnselected(image_name);
+ setImageHoverSelected(selected_name);
+}
+
+// static
+void LLButton::onMouseCaptureLost( LLMouseHandler* old_captor )
+{
+ LLButton* self = (LLButton*) old_captor;
+ self->mMouseDownTimer.stop();
+}
+
+//-------------------------------------------------------------------------
+// LLSquareButton
+//-------------------------------------------------------------------------
+LLSquareButton::LLSquareButton(const LLString& name, const LLRect& rect,
+ const LLString& label,
+ const LLFontGL *font,
+ const LLString& control_name,
+ void (*click_callback)(void*),
+ void *callback_data,
+ const LLString& selected_label )
+: LLButton(name, rect, "","",
+ control_name,
+ click_callback, callback_data,
+ font,
+ label,
+ (selected_label.empty() ? label : selected_label) )
+{
+ setImageUnselected("square_btn_32x128.tga");
+ // mImageUnselected = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga")));
+ setImageSelected("square_btn_selected_32x128.tga");
+ // mImageSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga")));
+ setImageDisabled("square_btn_32x128.tga");
+ //mDisabledImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_32x128.tga")));
+ setImageDisabledSelected("square_btn_selected_32x128.tga");
+ //mDisabledSelectedImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("square_btn_selected_32x128.tga")));
+ mImageColor = LLUI::sColorsGroup->getColor("ButtonColor");
+}
+
+//-------------------------------------------------------------------------
+// Utilities
+//-------------------------------------------------------------------------
+S32 round_up(S32 grid, S32 value)
+{
+ S32 mod = value % grid;
+
+ if (mod > 0)
+ {
+ // not even multiple
+ return value + (grid - mod);
+ }
+ else
+ {
+ return value;
+ }
+}
+
+void LLButton::setImageUnselected(const LLString &image_name)
+{
+ setImageUnselectedID(LLUI::findAssetUUIDByName(image_name));
+ mImageUnselectedName = image_name;
+}
+
+void LLButton::setImageSelected(const LLString &image_name)
+{
+ setImageSelectedID(LLUI::findAssetUUIDByName(image_name));
+ mImageSelectedName = image_name;
+}
+
+void LLButton::setImageHoverSelected(const LLString &image_name)
+{
+ setImageHoverSelectedID(LLUI::findAssetUUIDByName(image_name));
+ mImageHoverSelectedName = image_name;
+}
+
+void LLButton::setImageHoverUnselected(const LLString &image_name)
+{
+ setImageHoverUnselectedID(LLUI::findAssetUUIDByName(image_name));
+ mImageHoverUnselectedName = image_name;
+}
+
+void LLButton::setImageDisabled(const LLString &image_name)
+{
+ setImageDisabledID(LLUI::findAssetUUIDByName(image_name));
+ mImageDisabledName = image_name;
+}
+
+void LLButton::setImageDisabledSelected(const LLString &image_name)
+{
+ setImageDisabledSelectedID(LLUI::findAssetUUIDByName(image_name));
+ mImageDisabledSelectedName = image_name;
+}
+
+void LLButton::addImageAttributeToXML(LLXMLNodePtr node,
+ const LLString& image_name,
+ const LLUUID& image_id,
+ const LLString& xml_tag_name) const
+{
+ if( !image_name.empty() )
+ {
+ node->createChild(xml_tag_name, TRUE)->setStringValue(image_name);
+ }
+ else if( image_id != LLUUID::null )
+ {
+ node->createChild(xml_tag_name + "_id", TRUE)->setUUIDValue(image_id);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLButton::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("label", TRUE)->setStringValue(getLabelUnselected());
+ node->createChild("label_selected", TRUE)->setStringValue(getLabelSelected());
+ node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont));
+ node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
+ node->createChild("border_width", TRUE)->setIntValue(mFixedWidth);
+ node->createChild("border_height", TRUE)->setIntValue(mFixedHeight);
+
+ addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,"image_unselected");
+ addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,"image_selected");
+ addImageAttributeToXML(node,mImageHoverSelectedName,mImageHoverSelectedID,"image_hover_selected");
+ addImageAttributeToXML(node,mImageHoverUnselectedName,mImageHoverUnselectedID,"image_hover_unselected");
+ addImageAttributeToXML(node,mImageDisabledName,mImageDisabledID,"image_disabled");
+ addImageAttributeToXML(node,mImageDisabledSelectedName,mImageDisabledSelectedID,"image_disabled_selected");
+
+ node->createChild("scale_image", TRUE)->setBoolValue(mScaleImage);
+
+ return node;
+}
+
+// static
+LLView* LLButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("button");
+ node->getAttributeString("name", name);
+
+ LLString label = name;
+ node->getAttributeString("label", label);
+
+ LLString label_selected = label;
+ node->getAttributeString("label_selected", label_selected);
+
+ LLFontGL* font = selectFont(node);
+
+ LLString image_unselected;
+ if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
+
+ LLString image_selected;
+ if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
+
+ LLString image_hover_selected;
+ if (node->hasAttribute("image_hover_selected")) node->getAttributeString("image_hover_selected",image_hover_selected);
+
+ LLString image_hover_unselected;
+ if (node->hasAttribute("image_hover_unselected")) node->getAttributeString("image_hover_unselected",image_hover_unselected);
+
+ LLString image_disabled_selected;
+ if (node->hasAttribute("image_disabled_selected")) node->getAttributeString("image_disabled_selected",image_disabled_selected);
+
+ LLString image_disabled;
+ if (node->hasAttribute("image_disabled")) node->getAttributeString("image_disabled",image_disabled);
+
+ LLButton *button = new LLButton(name,
+ LLRect(),
+ image_unselected,
+ image_selected,
+ "",
+ NULL,
+ parent,
+ font,
+ label,
+ label_selected);
+
+ node->getAttributeS32("border_width", button->mFixedWidth);
+ node->getAttributeS32("border_height", button->mFixedHeight);
+
+ if(image_hover_selected != LLString::null) button->setImageHoverSelected(image_hover_selected);
+
+ if(image_hover_unselected != LLString::null) button->setImageHoverUnselected(image_hover_unselected);
+
+ if(image_disabled_selected != LLString::null) button->setImageDisabledSelected(image_disabled_selected );
+
+ if(image_disabled != LLString::null) button->setImageDisabled(image_disabled);
+
+
+ if (node->hasAttribute("halign"))
+ {
+ LLFontGL::HAlign halign = selectFontHAlign(node);
+ button->setHAlign(halign);
+ }
+
+ if (node->hasAttribute("scale_image"))
+ {
+ BOOL needsScale = FALSE;
+ node->getAttributeBOOL("scale_image",needsScale);
+ button->setScaleImage( needsScale );
+ }
+
+ if(label.empty())
+ {
+ button->setLabelUnselected(node->getTextContents());
+ }
+ if (label_selected.empty())
+ {
+ button->setLabelSelected(node->getTextContents());
+ }
+
+ button->initFromXML(node, parent);
+
+ return button;
+}
+
diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h
new file mode 100644
index 0000000000..0a4e41b017
--- /dev/null
+++ b/indra/llui/llbutton.h
@@ -0,0 +1,261 @@
+/**
+ * @file llbutton.h
+ * @brief Header for buttons
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBUTTON_H
+#define LL_LLBUTTON_H
+
+#include "lluuid.h"
+#include "llcontrol.h"
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llframetimer.h"
+#include "llfontgl.h"
+#include "llimage.h"
+#include "lluistring.h"
+
+//
+// Constants
+//
+
+// PLEASE please use these "constants" when building your own buttons.
+// They are loaded from settings.xml at run time.
+extern S32 LLBUTTON_H_PAD;
+extern S32 LLBUTTON_V_PAD;
+extern S32 BTN_HEIGHT_SMALL;
+extern S32 BTN_HEIGHT;
+
+
+// All button widths should be rounded up to this size
+extern S32 BTN_GRID;
+
+//
+// Classes
+//
+
+class LLButton
+: public LLUICtrl
+{
+public:
+ // simple button with text label
+ LLButton(const LLString& name, const LLRect &rect, const LLString& control_name = "",
+ void (*on_click)(void*) = NULL, void *data = NULL);
+
+ LLButton(const LLString& name, const LLRect& rect,
+ const LLString &unselected_image,
+ const LLString &selected_image,
+ const LLString& control_name,
+ void (*click_callback)(void*),
+ void *callback_data = NULL,
+ const LLFontGL* mGLFont = NULL,
+ const LLString& unselected_label = LLString::null,
+ const LLString& selected_label = LLString::null );
+
+ virtual ~LLButton();
+ void init(void (*click_callback)(void*), void *callback_data, const LLFontGL* font, const LLString& control_name);
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void addImageAttributeToXML(LLXMLNodePtr node, const LLString& imageName,
+ const LLUUID& imageID,const LLString& xmlTagName) const;
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void draw();
+
+ // HACK: "committing" a button is the same as clicking on it.
+ virtual void onCommit();
+
+ void setUnselectedLabelColor( const LLColor4& c ) { mUnselectedLabelColor = c; }
+ void setSelectedLabelColor( const LLColor4& c ) { mSelectedLabelColor = c; }
+
+ void setClickedCallback( void (*cb)(void *data), void* data = NULL ); // mouse down and up within button
+ void setMouseDownCallback( void (*cb)(void *data) ) { mMouseDownCallback = cb; } // mouse down within button
+ void setMouseUpCallback( void (*cb)(void *data) ) { mMouseUpCallback = cb; } // mouse up, EVEN IF NOT IN BUTTON
+ void setHeldDownCallback( void (*cb)(void *data) ) { mHeldDownCallback = cb; } // Mouse button held down and in button
+ void setHeldDownDelay( F32 seconds) { mHeldDownDelay = seconds; }
+
+ F32 getHeldDownTime() const { return mMouseDownTimer.getElapsedTimeF32(); }
+
+ BOOL toggleState() { setToggleState( !mToggleState ); return mToggleState; }
+ BOOL getToggleState() const { return mToggleState; }
+ void setToggleState(BOOL b);
+
+ void setFlashing( BOOL b ) { mFlashing = b; }
+ BOOL getFlashing() const { return mFlashing; }
+
+ void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
+ void setLeftHPad( S32 pad ) { mLeftHPad = pad; }
+ void setRightHPad( S32 pad ) { mRightHPad = pad; }
+
+ const LLString getLabelUnselected() const { return wstring_to_utf8str(mUnselectedLabel); }
+ const LLString getLabelSelected() const { return wstring_to_utf8str(mSelectedLabel); }
+
+
+ // HACK to allow images to be freed when the caller knows he's done with it.
+ LLImageGL* getImageUnselected() const { return mImageUnselected; }
+
+ void setImageColor(const LLString& color_control);
+ void setImages(const LLString &image_name, const LLString &selected_name);
+ void setImageColor(const LLColor4& c);
+
+ void setDisabledImages(const LLString &image_name, const LLString &selected_name);
+ void setDisabledImages(const LLString &image_name, const LLString &selected_name, const LLColor4& c);
+
+ void setHoverImages(const LLString &image_name, const LLString &selected_name);
+
+ void setDisabledImageColor(const LLColor4& c) { mDisabledImageColor = c; }
+
+ void setDisabledSelectedLabelColor( const LLColor4& c ) { mDisabledSelectedLabelColor = c; }
+
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ void setLabel( const LLString& label);
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+ void setLabelUnselected(const LLString& label);
+ void setLabelSelected(const LLString& label);
+ void setDisabledLabel(const LLString& disabled_label);
+ void setDisabledSelectedLabel(const LLString& disabled_label);
+ void setDisabledLabelColor( const LLColor4& c ) { mDisabledLabelColor = c; }
+
+ void setFont(const LLFontGL *font)
+ { mGLFont = ( font ? font : LLFontGL::sSansSerif); }
+ void setScaleImage(BOOL scale) { mScaleImage = scale; }
+
+ void setDropShadowedText(BOOL b) { mDropShadowedText = b; }
+
+ void setBorderEnabled(BOOL b) { mBorderEnabled = b; }
+
+ static void onHeldDown(void *userdata); // to be called by gIdleCallbacks
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+
+ void setFixedBorder(S32 width, S32 height) { mFixedWidth = width; mFixedHeight = height; }
+ void setHoverGlowStrength(F32 strength) { mHoverGlowStrength = strength; }
+
+private:
+ void setImageUnselectedID(const LLUUID &image_id);
+ void setImageSelectedID(const LLUUID &image_id);
+ void setImageHoverSelectedID(const LLUUID &image_id);
+ void setImageHoverUnselectedID(const LLUUID &image_id);
+ void setImageDisabledID(const LLUUID &image_id);
+ void setImageDisabledSelectedID(const LLUUID &image_id);
+public:
+ void setImageUnselected(const LLString &image_name);
+ void setImageSelected(const LLString &image_name);
+ void setImageHoverSelected(const LLString &image_name);
+ void setImageHoverUnselected(const LLString &image_name);
+ void setImageDisabled(const LLString &image_name);
+ void setImageDisabledSelected(const LLString &image_name);
+ void setCommitOnReturn(BOOL commit) { mCommitOnReturn = commit; }
+ BOOL getCommitOnReturn() { return mCommitOnReturn; }
+
+protected:
+ virtual void drawBorder(const LLColor4& color, S32 size);
+
+protected:
+
+ void (*mClickedCallback)(void* data );
+ void (*mMouseDownCallback)(void *data);
+ void (*mMouseUpCallback)(void *data);
+ void (*mHeldDownCallback)(void *data);
+
+ const LLFontGL *mGLFont;
+
+ LLFrameTimer mMouseDownTimer;
+ F32 mHeldDownDelay; // seconds, after which held-down callbacks get called
+
+ LLPointer<LLImageGL> mImageUnselected;
+ LLUIString mUnselectedLabel;
+ LLColor4 mUnselectedLabelColor;
+
+ LLPointer<LLImageGL> mImageSelected;
+ LLUIString mSelectedLabel;
+ LLColor4 mSelectedLabelColor;
+
+ LLPointer<LLImageGL> mImageHoverSelected;
+
+ LLPointer<LLImageGL> mImageHoverUnselected;
+
+ LLPointer<LLImageGL> mImageDisabled;
+ LLUIString mDisabledLabel;
+ LLColor4 mDisabledLabelColor;
+
+ LLPointer<LLImageGL> mImageDisabledSelected;
+ LLUIString mDisabledSelectedLabel;
+ LLColor4 mDisabledSelectedLabelColor;
+
+
+ LLUUID mImageUnselectedID;
+ LLUUID mImageSelectedID;
+ LLUUID mImageHoverSelectedID;
+ LLUUID mImageHoverUnselectedID;
+ LLUUID mImageDisabledID;
+ LLUUID mImageDisabledSelectedID;
+ LLString mImageUnselectedName;
+ LLString mImageSelectedName;
+ LLString mImageHoverSelectedName;
+ LLString mImageHoverUnselectedName;
+ LLString mImageDisabledName;
+ LLString mImageDisabledSelectedName;
+
+ LLColor4 mHighlightColor;
+ LLColor4 mUnselectedBgColor;
+ LLColor4 mSelectedBgColor;
+
+ LLColor4 mImageColor;
+ LLColor4 mDisabledImageColor;
+
+ BOOL mToggleState;
+ BOOL mScaleImage;
+
+ BOOL mDropShadowedText;
+
+ BOOL mBorderEnabled;
+
+ BOOL mFlashing;
+
+ LLFontGL::HAlign mHAlign;
+ S32 mLeftHPad;
+ S32 mRightHPad;
+
+ S32 mFixedWidth;
+ S32 mFixedHeight;
+
+ F32 mHoverGlowStrength;
+ F32 mCurGlowStrength;
+
+ BOOL mNeedsHighlight;
+ BOOL mCommitOnReturn;
+
+ LLPointer<LLImageGL> mImagep;
+
+ static LLFrameTimer sFlashingTimer;
+};
+
+class LLSquareButton
+: public LLButton
+{
+public:
+ LLSquareButton(const LLString& name, const LLRect& rect,
+ const LLString& label,
+ const LLFontGL *font = NULL,
+ const LLString& control_name = "",
+ void (*click_callback)(void*) = NULL,
+ void *callback_data = NULL,
+ const LLString& selected_label = LLString::null );
+};
+
+// Helpful functions
+S32 round_up(S32 grid, S32 value);
+
+#endif // LL_LLBUTTON_H
diff --git a/indra/llui/llcallbackmap.h b/indra/llui/llcallbackmap.h
new file mode 100644
index 0000000000..dfc965aa08
--- /dev/null
+++ b/indra/llui/llcallbackmap.h
@@ -0,0 +1,36 @@
+/**
+ * @file llcallbackmap.h
+ * @brief LLCallbackMap base class
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// llcallbackmap.h
+//
+// Copyright 2006, Linden Research, Inc.
+
+#ifndef LL_CALLBACK_MAP_H
+#define LL_CALLBACK_MAP_H
+
+#include <map>
+#include "llstring.h"
+
+class LLCallbackMap
+{
+public:
+ // callback definition.
+ typedef void* (*callback_t)(void* data);
+
+ typedef std::map<LLString, LLCallbackMap> map_t;
+ typedef map_t::iterator map_iter_t;
+ typedef map_t::const_iterator map_const_iter_t;
+
+ LLCallbackMap() : mCallback(NULL), mData(NULL) { }
+ LLCallbackMap(callback_t callback, void* data) : mCallback(callback), mData(data) { }
+
+ callback_t mCallback;
+ void* mData;
+};
+
+#endif // LL_CALLBACK_MAP_H
diff --git a/indra/llui/llcheckboxctrl.cpp b/indra/llui/llcheckboxctrl.cpp
new file mode 100644
index 0000000000..3b054d2fec
--- /dev/null
+++ b/indra/llui/llcheckboxctrl.cpp
@@ -0,0 +1,315 @@
+/**
+ * @file llcheckboxctrl.cpp
+ * @brief LLCheckBoxCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// The mutants are coming!
+
+#include "linden_common.h"
+
+#include "llcheckboxctrl.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "lluictrlfactory.h"
+#include "llcontrol.h"
+
+#include "llstring.h"
+#include "llfontgl.h"
+#include "lltextbox.h"
+#include "llkeyboard.h"
+#include "llviewborder.h"
+
+const U32 MAX_STRING_LENGTH = 10;
+
+
+LLCheckBoxCtrl::LLCheckBoxCtrl(const LLString& name, const LLRect& rect,
+ const LLString& label,
+ const LLFontGL* font,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_user_data,
+ BOOL initial_value,
+ BOOL use_radio_style,
+ const LLString& control_which)
+: LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
+ mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
+ mRadioStyle( use_radio_style ),
+ mInitialValue( initial_value )
+{
+ if (font)
+ {
+ mFont = font;
+ }
+ else
+ {
+ mFont = LLFontGL::sSansSerifSmall;
+ }
+
+ // must be big enough to hold all children
+ setSpanChildren(TRUE);
+
+ mKeyboardFocusOnClick = TRUE;
+
+ // 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());
+ LLRect label_rect;
+ label_rect.setOriginAndSize(
+ LLCHECKBOXCTRL_HPAD + LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING,
+ LLCHECKBOXCTRL_VPAD + 1, // padding to get better alignment
+ text_width + LLCHECKBOXCTRL_HPAD,
+ text_height );
+ mLabel = new LLTextBox( "CheckboxCtrl Label", label_rect, label.c_str(), mFont );
+ mLabel->setFollowsLeft();
+ mLabel->setFollowsBottom();
+ addChild(mLabel);
+
+ // Button
+ // Note: button cover the label by extending all the way to the right.
+ LLRect btn_rect;
+ btn_rect.setOriginAndSize(
+ LLCHECKBOXCTRL_HPAD,
+ LLCHECKBOXCTRL_VPAD,
+ LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING + text_width + LLCHECKBOXCTRL_HPAD,
+ llmax( text_height, LLCHECKBOXCTRL_BTN_SIZE ) + LLCHECKBOXCTRL_VPAD);
+ LLString active_true_id, active_false_id;
+ LLString inactive_true_id, inactive_false_id;
+ if (mRadioStyle)
+ {
+ active_true_id = "UIImgRadioActiveSelectedUUID";
+ active_false_id = "UIImgRadioActiveUUID";
+ inactive_true_id = "UIImgRadioInactiveSelectedUUID";
+ inactive_false_id = "UIImgRadioInactiveUUID";
+ mButton = new LLButton(
+ "Radio control button", btn_rect,
+ active_false_id, active_true_id, control_which,
+ &LLCheckBoxCtrl::onButtonPress, this, LLFontGL::sSansSerif );
+ mButton->setDisabledImages( inactive_false_id, inactive_true_id );
+ mButton->setHoverGlowStrength(0.35f);
+ }
+ else
+ {
+ active_false_id = "UIImgCheckboxActiveUUID";
+ active_true_id = "UIImgCheckboxActiveSelectedUUID";
+ inactive_true_id = "UIImgCheckboxInactiveSelectedUUID";
+ inactive_false_id = "UIImgCheckboxInactiveUUID";
+ mButton = new LLButton(
+ "Checkbox control button", btn_rect,
+ active_false_id, active_true_id, control_which,
+ &LLCheckBoxCtrl::onButtonPress, this, LLFontGL::sSansSerif );
+ mButton->setDisabledImages( inactive_false_id, inactive_true_id );
+ mButton->setHoverGlowStrength(0.35f);
+ }
+ mButton->setToggleState( initial_value );
+ mButton->setFollowsLeft();
+ mButton->setFollowsBottom();
+ mButton->setCommitOnReturn(FALSE);
+ addChild(mButton);
+}
+
+LLCheckBoxCtrl::~LLCheckBoxCtrl()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+// static
+void LLCheckBoxCtrl::onButtonPress( void *userdata )
+{
+ LLCheckBoxCtrl* self = (LLCheckBoxCtrl*) userdata;
+
+ if (self->mRadioStyle)
+ {
+ if (!self->getValue())
+ {
+ self->setValue(TRUE);
+ }
+ }
+ else
+ {
+ self->toggle();
+ }
+ self->setControlValue(self->getValue());
+ self->onCommit();
+
+ if (self->mKeyboardFocusOnClick)
+ {
+ self->setFocus( TRUE );
+ self->onFocusReceived();
+ }
+}
+
+void LLCheckBoxCtrl::onCommit()
+{
+ if( getEnabled() )
+ {
+ setTentative(FALSE);
+ LLUICtrl::onCommit();
+ }
+}
+
+void LLCheckBoxCtrl::setEnabled(BOOL b)
+{
+ LLUICtrl::setEnabled(b);
+ mButton->setEnabled(b);
+}
+
+void LLCheckBoxCtrl::clear()
+{
+ setValue( FALSE );
+}
+
+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());
+ LLRect label_rect;
+ label_rect.setOriginAndSize(
+ LLCHECKBOXCTRL_HPAD + LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING,
+ LLCHECKBOXCTRL_VPAD,
+ text_width,
+ text_height );
+ mLabel->setRect(label_rect);
+
+ LLRect btn_rect;
+ btn_rect.setOriginAndSize(
+ LLCHECKBOXCTRL_HPAD,
+ LLCHECKBOXCTRL_VPAD,
+ LLCHECKBOXCTRL_BTN_SIZE + LLCHECKBOXCTRL_SPACING + text_width,
+ llmax( text_height, LLCHECKBOXCTRL_BTN_SIZE ) );
+ mButton->setRect( btn_rect );
+
+ LLUICtrl::reshape(width, height, called_from_parent);
+}
+
+void LLCheckBoxCtrl::draw()
+{
+ if (mEnabled)
+ {
+ mLabel->setColor( mTextEnabledColor );
+ }
+ else
+ {
+ mLabel->setColor( mTextDisabledColor );
+ }
+
+ // Draw children
+ LLUICtrl::draw();
+}
+
+//virtual
+void LLCheckBoxCtrl::setValue(const LLSD& value )
+{
+ mButton->setToggleState( value.asBoolean() );
+}
+
+//virtual
+LLSD LLCheckBoxCtrl::getValue() const
+{
+ return mButton->getToggleState();
+}
+
+void LLCheckBoxCtrl::setLabel( const LLString& label )
+{
+ mLabel->setText( label );
+ reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
+}
+
+LLString LLCheckBoxCtrl::getLabel() const
+{
+ return mLabel->getText();
+}
+
+BOOL LLCheckBoxCtrl::setLabelArg( const LLString& key, const LLString& text )
+{
+ BOOL res = mLabel->setTextArg(key, text);
+ reshape(getRect().getWidth(), getRect().getHeight(), FALSE);
+ return res;
+}
+
+//virtual
+LLString LLCheckBoxCtrl::getControlName() const
+{
+ return mButton->getControlName();
+}
+
+// virtual
+void LLCheckBoxCtrl::setControlName(const LLString& control_name, LLView* context)
+{
+ mButton->setControlName(control_name, context);
+}
+
+// virtual
+LLXMLNodePtr LLCheckBoxCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("label", TRUE)->setStringValue(mLabel->getText());
+
+ LLString control_name = mButton->getControlName();
+
+ node->createChild("initial_value", TRUE)->setBoolValue(mInitialValue);
+
+ node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mFont));
+
+ node->createChild("radio_style", TRUE)->setBoolValue(mRadioStyle);
+
+ return node;
+}
+
+// static
+LLView* LLCheckBoxCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("checkbox");
+ node->getAttributeString("name", name);
+
+ LLString label("");
+ node->getAttributeString("label", label);
+
+ BOOL initial_value = FALSE;
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ BOOL radio_style = FALSE;
+ node->getAttributeBOOL("radio_style", radio_style);
+
+ LLUICtrlCallback callback = NULL;
+
+ if (label.empty())
+ {
+ label.assign(node->getTextContents());
+ }
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLCheckBoxCtrl* checkbox = new LLCheckboxCtrl(name,
+ rect,
+ label,
+ font,
+ callback,
+ NULL,
+ initial_value,
+ radio_style); // if true, draw radio button style icons
+
+ LLColor4 color;
+ color = LLUI::sColorsGroup->getColor( "LabelTextColor" );
+ LLUICtrlFactory::getAttributeColor(node,"text_enabled_color", color);
+ checkbox->setEnabledColor(color);
+
+ color = LLUI::sColorsGroup->getColor( "LabelDisabledColor" );
+ LLUICtrlFactory::getAttributeColor(node,"text_disabled_color", color);
+ checkbox->setDisabledColor(color);
+
+ checkbox->initFromXML(node, parent);
+
+ return checkbox;
+}
diff --git a/indra/llui/llcheckboxctrl.h b/indra/llui/llcheckboxctrl.h
new file mode 100644
index 0000000000..b2f9c95974
--- /dev/null
+++ b/indra/llui/llcheckboxctrl.h
@@ -0,0 +1,112 @@
+/**
+ * @file llcheckboxctrl.h
+ * @brief LLCheckBoxCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHECKBOXCTRL_H
+#define LL_LLCHECKBOXCTRL_H
+
+
+#include "stdtypes.h"
+#include "lluictrl.h"
+#include "llbutton.h"
+#include "v4color.h"
+#include "llrect.h"
+
+//
+// Constants
+//
+const S32 LLCHECKBOXCTRL_BTN_SIZE = 13;
+const S32 LLCHECKBOXCTRL_VPAD = 2;
+const S32 LLCHECKBOXCTRL_HPAD = 2;
+const S32 LLCHECKBOXCTRL_SPACING = 5;
+const S32 LLCHECKBOXCTRL_HEIGHT = 16;
+
+// Deprecated, don't use.
+#define CHECKBOXCTRL_HEIGHT LLCHECKBOXCTRL_HEIGHT
+
+const BOOL RADIO_STYLE = TRUE;
+const BOOL CHECK_STYLE = FALSE;
+
+//
+// Classes
+//
+class LLFontGL;
+class LLTextBox;
+class LLViewBorder;
+
+class LLCheckBoxCtrl
+: public LLUICtrl
+{
+public:
+ LLCheckBoxCtrl(const LLString& name, const LLRect& rect, const LLString& label,
+ const LLFontGL* font = NULL,
+ void (*commit_callback)(LLUICtrl*, void*) = NULL,
+ void* callback_userdata = NULL,
+ BOOL initial_value = FALSE,
+ BOOL use_radio_style = FALSE, // if true, draw radio button style icons
+ const LLString& control_which = LLString::null);
+ virtual ~LLCheckBoxCtrl();
+
+ // LLView interface
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_CHECKBOX; }
+ virtual LLString getWidgetTag() const { return LL_CHECK_BOX_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void setEnabled( BOOL b );
+
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ // LLUICtrl interface
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ BOOL get() { return (BOOL)getValue().asBoolean(); }
+ void set(BOOL value) { setValue(value); }
+
+ virtual void setTentative(BOOL b) { mButton->setTentative(b); }
+ virtual BOOL getTentative() const { return mButton->getTentative(); }
+
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ virtual void clear();
+ virtual void onCommit();
+
+ // LLCheckBoxCtrl interface
+ virtual BOOL toggle() { return mButton->toggleState(); } // returns new state
+
+ void setEnabledColor( const LLColor4 &color ) { mTextEnabledColor = color; }
+ void setDisabledColor( const LLColor4 &color ) { mTextDisabledColor = color; }
+
+ void setLabel( const LLString& label );
+ LLString getLabel() const;
+
+ virtual void setControlName(const LLString& control_name, LLView* context);
+ virtual LLString getControlName() const;
+
+ static void onButtonPress(void *userdata);
+
+protected:
+ // note: value is stored in toggle state of button
+ LLButton* mButton;
+ LLTextBox* mLabel;
+ const LLFontGL* mFont;
+ LLColor4 mTextEnabledColor;
+ LLColor4 mTextDisabledColor;
+ BOOL mRadioStyle;
+ BOOL mInitialValue;
+ BOOL mKeyboardFocusOnClick;
+ LLViewBorder* mBorder;
+};
+
+
+// HACK: fix old capitalization problem
+//typedef LLCheckBoxCtrl LLCheckboxCtrl;
+#define LLCheckboxCtrl LLCheckBoxCtrl
+
+
+#endif // LL_LLCHECKBOXCTRL_H
diff --git a/indra/llui/llclipboard.cpp b/indra/llui/llclipboard.cpp
new file mode 100644
index 0000000000..f2b546ec28
--- /dev/null
+++ b/indra/llui/llclipboard.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file llclipboard.cpp
+ * @brief LLClipboard base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llclipboard.h"
+
+#include "llerror.h"
+#include "llmath.h"
+#include "llstring.h"
+#include "llview.h"
+#include "llwindow.h"
+
+// Global singleton
+LLClipboard gClipboard;
+
+
+LLClipboard::LLClipboard()
+{
+}
+
+
+LLClipboard::~LLClipboard()
+{
+}
+
+
+void LLClipboard::copyFromSubstring(const LLWString &src, S32 pos, S32 len, const LLUUID& source_id )
+{
+ mSourceID = source_id;
+ mString = src.substr(pos, len);
+ LLView::getWindow()->copyTextToClipboard( mString );
+}
+
+
+LLWString LLClipboard::getPasteWString( LLUUID* source_id )
+{
+ if( mSourceID.notNull() )
+ {
+ LLWString temp_string;
+ LLView::getWindow()->pasteTextFromClipboard(temp_string);
+
+ if( temp_string != mString )
+ {
+ mSourceID.setNull();
+ mString = temp_string;
+ }
+ }
+ else
+ {
+ LLView::getWindow()->pasteTextFromClipboard(mString);
+ }
+
+ if( source_id )
+ {
+ *source_id = mSourceID;
+ }
+
+ return mString;
+}
+
+
+BOOL LLClipboard::canPasteString()
+{
+ return LLView::getWindow()->isClipboardTextAvailable();
+}
diff --git a/indra/llui/llclipboard.h b/indra/llui/llclipboard.h
new file mode 100644
index 0000000000..a5bb4fc790
--- /dev/null
+++ b/indra/llui/llclipboard.h
@@ -0,0 +1,41 @@
+/**
+ * @file llclipboard.h
+ * @brief LLClipboard base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLIPBOARD_H
+#define LL_LLCLIPBOARD_H
+
+
+#include "llstring.h"
+#include "lluuid.h"
+
+//
+// Classes
+//
+class LLClipboard
+{
+protected:
+ LLUUID mSourceID;
+ LLWString mString;
+
+public:
+ LLClipboard();
+ ~LLClipboard();
+
+ void copyFromSubstring(const LLWString &copy_from, S32 pos, S32 len, const LLUUID& source_id = LLUUID::null );
+
+
+ BOOL canPasteString();
+ LLWString getPasteWString(LLUUID* source_id = NULL);
+};
+
+
+// Global singleton
+extern LLClipboard gClipboard;
+
+
+#endif // LL_LLCLIPBOARD_H
diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp
new file mode 100644
index 0000000000..84c5d354be
--- /dev/null
+++ b/indra/llui/llcombobox.cpp
@@ -0,0 +1,1133 @@
+/**
+ * @file llcombobox.cpp
+ * @brief LLComboBox base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A control that displays the name of the chosen item, which when
+// clicked shows a scrolling box of options.
+
+#include "linden_common.h"
+
+// file includes
+#include "llcombobox.h"
+
+// common includes
+#include "llstring.h"
+
+// newview includes
+#include "llbutton.h"
+#include "llkeyboard.h"
+#include "llscrolllistctrl.h"
+#include "llwindow.h"
+#include "llfloater.h"
+#include "llscrollbar.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "lllineeditor.h"
+#include "v2math.h"
+
+// Globals
+S32 LLCOMBOBOX_HEIGHT = 0;
+S32 LLCOMBOBOX_WIDTH = 0;
+
+LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label,
+ void (*commit_callback)(LLUICtrl*,void*),
+ void *callback_userdata,
+ S32 list_width
+ )
+: LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata,
+ FOLLOWS_LEFT | FOLLOWS_TOP),
+ mDrawButton(TRUE),
+ mTextEntry(NULL),
+ mArrowImage(NULL),
+ mAllowTextEntry(FALSE),
+ mMaxChars(20),
+ mTextEntryTentative(TRUE),
+ mPrearrangeCallback( NULL ),
+ mTextEntryCallback( NULL ),
+ mListWidth(list_width)
+{
+ // For now, all comboboxes don't take keyboard focus when clicked.
+ // This might change if it is part of a modal dialog.
+ // mKeyboardFocusOnClick = FALSE;
+
+ // Revert to standard behavior. When this control's parent is hidden, it needs to
+ // hide this ctrl--which won't just happen automatically since when LLComboBox is
+ // showing its list, it's also set to TopView. When keyboard focus is cleared all
+ // controls (including this one) know that they are no longer editing.
+ mKeyboardFocusOnClick = TRUE;
+
+ LLRect r;
+ r.setOriginAndSize(0, 0, rect.getWidth(), rect.getHeight());
+
+ // Always use text box
+ // Text label button
+ mButton = new LLSquareButton("comboxbox button",
+ r, label, NULL, LLString::null,
+ &LLComboBox::onButtonClick, this);
+ mButton->setFont(LLFontGL::sSansSerifSmall);
+ mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
+ mButton->setHAlign( LLFontGL::LEFT );
+
+ const S32 ARROW_WIDTH = 16;
+ mButton->setRightHPad( ARROW_WIDTH );
+ addChild(mButton);
+
+ // Default size, will be set by arrange() call in button callback.
+ if (list_width == 0)
+ {
+ list_width = mRect.getWidth() + SCROLLBAR_SIZE;
+ }
+ r.setOriginAndSize(0, 16, list_width, 220);
+
+ // disallow multiple selection
+ mList = new LLScrollListCtrl(
+ "ComboBox", r,
+ &LLComboBox::onItemSelected, this, FALSE);
+ mList->setVisible(FALSE);
+ mList->setBgWriteableColor( LLColor4(1,1,1,1) );
+ mList->setCommitOnKeyboardMovement(FALSE);
+ addChild(mList);
+
+ LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ mBorder = new LLViewBorder( "combo border", border_rect );
+ addChild( mBorder );
+ mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM);
+
+ LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") );
+ mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id);
+}
+
+
+LLComboBox::~LLComboBox()
+{
+ // children automatically deleted, including mMenu, mButton
+}
+
+// virtual
+LLXMLNodePtr LLComboBox::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ // Attributes
+
+ node->createChild("allow_text_entry", TRUE)->setBoolValue(mAllowTextEntry);
+
+ node->createChild("max_chars", TRUE)->setIntValue(mMaxChars);
+
+ // Contents
+
+ std::vector<LLScrollListItem*> data_list = mList->getAllData();
+ std::vector<LLScrollListItem*>::iterator data_itor;
+ for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
+ {
+ LLScrollListItem* item = *data_itor;
+ LLScrollListCell* cell = item->getColumn(0);
+ if (cell)
+ {
+ LLXMLNodePtr item_node = node->createChild("combo_item", FALSE);
+ LLSD value = item->getValue();
+ item_node->createChild("value", TRUE)->setStringValue(value.asString());
+ item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled());
+ item_node->setStringValue(cell->getText());
+ }
+ }
+
+ return node;
+}
+
+// static
+LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("combo_box");
+ node->getAttributeString("name", name);
+
+ LLString label("");
+ node->getAttributeString("label", label);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL allow_text_entry = FALSE;
+ node->getAttributeBOOL("allow_text_entry", allow_text_entry);
+
+ S32 max_chars = 20;
+ node->getAttributeS32("max_chars", max_chars);
+
+ LLUICtrlCallback callback = NULL;
+
+ LLComboBox* combo_box = new LLComboBox(name,
+ rect,
+ label,
+ callback,
+ NULL);
+ combo_box->setAllowTextEntry(allow_text_entry, max_chars);
+
+ combo_box->initFromXML(node, parent);
+
+ const LLString& contents = node->getValue();
+
+ if (contents.find_first_not_of(" \n\t") != contents.npos)
+ {
+ llerrs << "Legacy combo box item format used! Please convert to <combo_item> tags!" << llendl;
+ }
+ else
+ {
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("combo_item"))
+ {
+ LLString label = child->getTextContents();
+
+ LLString value = label;
+ child->getAttributeString("value", value);
+
+ combo_box->add(label, LLSD(value) );
+ }
+ }
+ }
+
+ combo_box->selectFirstItem();
+
+ return combo_box;
+}
+
+void LLComboBox::setEnabled(BOOL enabled)
+{
+ LLUICtrl::setEnabled(enabled);
+ mButton->setEnabled(enabled);
+}
+
+//FIXME: these are all hacks to support the fact that the combobox has mouse capture
+// so we can hide the list when we don't handle the mouse up event
+BOOL LLComboBox::handleHover(S32 x, S32 y, MASK mask)
+{
+ if (mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ return mList->handleHover(local_x, local_y, mask);
+ }
+ }
+ return LLUICtrl::handleHover(x, y, mask);
+}
+
+BOOL LLComboBox::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ return mList->handleMouseDown(local_x, local_y, mask);
+ }
+ }
+ BOOL has_focus_now = hasFocus();
+ BOOL handled = LLUICtrl::handleMouseDown(x, y, mask);
+ if (handled && !has_focus_now)
+ {
+ onFocusReceived();
+ }
+
+ return handled;
+}
+
+BOOL LLComboBox::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ return mList->handleRightMouseDown(local_x, local_y, mask);
+ }
+ }
+ return LLUICtrl::handleRightMouseDown(x, y, mask);
+}
+
+BOOL LLComboBox::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ return mList->handleRightMouseUp(local_x, local_y, mask);
+ }
+ }
+ return LLUICtrl::handleRightMouseUp(x, y, mask);
+}
+
+BOOL LLComboBox::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ return mList->handleDoubleClick(local_x, local_y, mask);
+ }
+ }
+ return LLUICtrl::handleDoubleClick(x, y, mask);
+}
+
+BOOL LLComboBox::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleMouseUp(x, y, mask) != NULL;
+
+ if (!handled && mList->getVisible())
+ {
+ S32 local_x, local_y;
+ LLView::localPointToOtherView(x, y, &local_x, &local_y, mList);
+ if (mList->pointInView(local_x, local_y))
+ {
+ handled = mList->handleMouseUp(local_x, local_y, mask);
+ }
+ }
+
+ if( !handled && gFocusMgr.getMouseCapture() == this )
+ {
+ // Mouse events that we didn't handle cause the list to be hidden.
+ // Eat mouse event, regardless of where on the screen it happens.
+ hideList();
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+void LLComboBox::clear()
+{
+ if (mTextEntry)
+ {
+ mTextEntry->setText("");
+ }
+ mButton->setLabelSelected("");
+ mButton->setLabelUnselected("");
+ mButton->setDisabledLabel("");
+ mButton->setDisabledSelectedLabel("");
+ mList->deselectAllItems();
+}
+
+void LLComboBox::onCommit()
+{
+ if (mAllowTextEntry && getCurrentIndex() != -1)
+ {
+ // we have selected an existing item, blitz the manual text entry with
+ // the properly capitalized item
+ mTextEntry->setValue(getSimple());
+ mTextEntry->setTentative(FALSE);
+ }
+ LLUICtrl::onCommit();
+}
+
+// add item "name" to menu
+void LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled)
+{
+ mList->addSimpleItem(name, pos, enabled);
+ mList->selectFirstItem();
+}
+
+// add item "name" with a unique id to menu
+void LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled )
+{
+ mList->addSimpleItem(name, LLSD(id), pos, enabled);
+ mList->selectFirstItem();
+}
+
+// add item "name" with attached userdata
+void LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled )
+{
+ LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled);
+ item->setUserdata( userdata );
+ mList->selectFirstItem();
+}
+
+// add item "name" with attached generic data
+void LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled )
+{
+ mList->addSimpleItem(name, value, pos, enabled);
+ mList->selectFirstItem();
+}
+
+
+void LLComboBox::sortByName()
+{
+ mList->sortByColumn(0, TRUE);
+}
+
+
+// Choose an item with a given name in the menu.
+// Returns TRUE if the item was found.
+BOOL LLComboBox::setSimple(const LLString& name)
+{
+ BOOL found = mList->selectSimpleItem(name, FALSE);
+
+ if (found)
+ {
+ setLabel(name);
+ }
+
+ return found;
+}
+
+// virtual
+void LLComboBox::setValue(const LLSD& value)
+{
+ BOOL found = mList->selectByValue(value);
+ if (found)
+ {
+ LLScrollListItem* item = mList->getFirstSelected();
+ if (item)
+ {
+ setLabel( mList->getSimpleSelectedItem() );
+ }
+ }
+}
+
+const LLString& LLComboBox::getSimple() const
+{
+ const LLString& res = mList->getSimpleSelectedItem();
+ if (res.empty() && mAllowTextEntry)
+ {
+ return mTextEntry->getText();
+ }
+ else
+ {
+ return res;
+ }
+}
+
+const LLString& LLComboBox::getSimpleSelectedItem(S32 column) const
+{
+ return mList->getSimpleSelectedItem(column);
+}
+
+// virtual
+LLSD LLComboBox::getValue() const
+{
+ LLScrollListItem* item = mList->getFirstSelected();
+ if( item )
+ {
+ return item->getValue();
+ }
+ else if (mAllowTextEntry)
+ {
+ return mTextEntry->getValue();
+ }
+ else
+ {
+ return LLSD();
+ }
+}
+
+void LLComboBox::setLabel(const LLString& name)
+{
+ if ( mAllowTextEntry )
+ {
+ mTextEntry->setText(name);
+ if (mList->selectSimpleItem(name, FALSE))
+ {
+ mTextEntry->setTentative(FALSE);
+ }
+ else
+ {
+ mTextEntry->setTentative(mTextEntryTentative);
+ }
+ }
+ else
+ {
+ mButton->setLabelUnselected(name);
+ mButton->setLabelSelected(name);
+ mButton->setDisabledLabel(name);
+ mButton->setDisabledSelectedLabel(name);
+ }
+}
+
+
+BOOL LLComboBox::remove(const LLString& name)
+{
+ BOOL found = mList->selectSimpleItem(name);
+
+ if (found)
+ {
+ LLScrollListItem* item = mList->getFirstSelected();
+ if (item)
+ {
+ mList->deleteSingleItem(mList->getItemIndex(item));
+ }
+ }
+
+ return found;
+}
+
+BOOL LLComboBox::remove(S32 index)
+{
+ if (index < mList->getItemCount())
+ {
+ mList->deleteSingleItem(index);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Keyboard focus lost.
+void LLComboBox::onFocusLost()
+{
+ hideList();
+ // if valid selection
+ if (mAllowTextEntry && getCurrentIndex() != -1)
+ {
+ mTextEntry->selectAll();
+ }
+}
+
+void LLComboBox::setButtonVisible(BOOL visible)
+{
+ mButton->setVisible(visible);
+ mDrawButton = visible;
+ if (mTextEntry)
+ {
+ LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ if (visible)
+ {
+ text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
+ }
+ //mTextEntry->setRect(text_entry_rect);
+ mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
+ }
+}
+
+void LLComboBox::draw()
+{
+ if( getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+
+ mButton->setEnabled(mEnabled /*&& !mList->isEmpty()*/);
+
+ // Draw children
+ LLUICtrl::draw();
+
+ if (mDrawButton)
+ {
+ // Paste the graphic on the right edge
+ if (!mArrowImage.isNull())
+ {
+ S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton");
+
+ gl_draw_image( left, 0, mArrowImage,
+ LLColor4::white);
+ }
+ }
+ }
+}
+
+BOOL LLComboBox::setCurrentByIndex( S32 index )
+{
+ BOOL found = mList->selectNthItem( index );
+ if (found)
+ {
+ setLabel(mList->getSimpleSelectedItem());
+ }
+ return found;
+}
+
+S32 LLComboBox::getCurrentIndex() const
+{
+ LLScrollListItem* item = mList->getFirstSelected();
+ if( item )
+ {
+ return mList->getItemIndex( item );
+ }
+ return -1;
+}
+
+
+void* LLComboBox::getCurrentUserdata()
+{
+ LLScrollListItem* item = mList->getFirstSelected();
+ if( item )
+ {
+ return item->getUserdata();
+ }
+ return NULL;
+}
+
+
+void LLComboBox::showList()
+{
+ // Make sure we don't go off top of screen.
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+ //HACK: shouldn't have to know about scale here
+ mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 );
+
+ // Move rect so it hangs off the bottom of this view
+ LLRect rect = mList->getRect();
+
+ rect.setLeftTopAndSize(0, 0, rect.getWidth(), rect.getHeight() );
+ mList->setRect(rect);
+
+ // Make sure that we can see the whole list
+ LLRect floater_area_screen;
+ LLRect floater_area_local;
+ gFloaterView->getParent()->localRectToScreen( gFloaterView->getRect(), &floater_area_screen );
+ screenRectToLocal( floater_area_screen, &floater_area_local );
+ mList->translateIntoRect( floater_area_local, FALSE );
+
+ // Make sure we didn't go off bottom of screen
+ S32 x, y;
+ mList->localPointToScreen(0, 0, &x, &y);
+
+ if (y < 0)
+ {
+ mList->translate(0, -y);
+ }
+
+ gFocusMgr.setMouseCapture( this, LLComboBox::onMouseCaptureLost );
+ // NB: this call will trigger the focuslost callback which will hide the list, so do it first
+ // before finally showing the list
+
+ if (!mList->getFirstSelected())
+ {
+ // if nothing is selected, select the first item
+ // so that the callback is not immediately triggered on setFocus()
+ mList->selectFirstItem();
+ }
+ gFocusMgr.setKeyboardFocus(mList, onListFocusLost);
+
+ // Show the list and push the button down
+ mButton->setToggleState(TRUE);
+ mList->setVisible(TRUE);
+
+ gFocusMgr.setTopView(mList, LLComboBox::onTopViewLost );
+
+}
+
+void LLComboBox::hideList()
+{
+ mButton->setToggleState(FALSE);
+ mList->setVisible(FALSE);
+ mList->highlightNthItem(-1);
+
+ if( gFocusMgr.getTopView() == mList )
+ {
+ gFocusMgr.setTopView(NULL, NULL);
+ }
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ }
+
+ if( gFocusMgr.getKeyboardFocus() == mList )
+ {
+ if (mAllowTextEntry)
+ {
+ mTextEntry->setFocus(TRUE);
+ }
+ else
+ {
+ setFocus(TRUE);
+ }
+ }
+}
+
+
+
+//------------------------------------------------------------------
+// static functions
+//------------------------------------------------------------------
+
+// static
+void LLComboBox::onButtonClick(void *userdata)
+{
+ LLComboBox *self = (LLComboBox *)userdata;
+
+ if (!self->mList->getVisible())
+ {
+ LLScrollListItem* last_selected_item = self->mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ // highlight the original selection before potentially selecting a new item
+ self->mList->highlightNthItem(self->mList->getItemIndex(last_selected_item));
+ }
+
+ if( self->mPrearrangeCallback )
+ {
+ self->mPrearrangeCallback( self, self->mCallbackUserData );
+ }
+
+ if (self->mList->getItemCount() != 0)
+ {
+ self->showList();
+ }
+
+ if (self->mKeyboardFocusOnClick && !self->hasFocus())
+ {
+ self->setFocus( TRUE );
+ }
+ }
+ else
+ {
+ // hide and release keyboard focus
+ self->hideList();
+
+ self->onCommit();
+ }
+}
+
+
+
+// static
+void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata)
+{
+ // Note: item is the LLScrollListCtrl
+ LLComboBox *self = (LLComboBox *) userdata;
+
+ const LLString& name = self->mList->getSimpleSelectedItem();
+
+ self->hideList();
+
+ S32 cur_id = self->getCurrentIndex();
+ if (cur_id != -1)
+ {
+ self->setLabel(self->mList->getSimpleSelectedItem());
+
+ if (self->mAllowTextEntry)
+ {
+ self->mTextEntry->setText(name);
+ self->mTextEntry->setTentative(FALSE);
+ gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL);
+ self->mTextEntry->selectAll();
+ }
+ else
+ {
+ self->mButton->setLabelUnselected( name );
+ self->mButton->setLabelSelected( name );
+ self->mButton->setDisabledLabel( name );
+ self->mButton->setDisabledSelectedLabel( name );
+ }
+ }
+ self->onCommit();
+}
+
+// static
+void LLComboBox::onTopViewLost(LLView* old_focus)
+{
+ LLComboBox *self = (LLComboBox *) old_focus->getParent();
+ self->hideList();
+}
+
+
+// static
+void LLComboBox::onMouseCaptureLost(LLMouseHandler*)
+{
+ // Can't hide the list here. If the list scrolls off the screen,
+ // and you click in the arrow buttons of the scroll bar, they must capture
+ // the mouse to handle scrolling-while-mouse-down.
+}
+
+BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+
+ LLString tool_tip;
+
+ if (LLUI::sShowXUINames)
+ {
+ tool_tip = mName;
+ }
+ else
+ {
+ tool_tip = mToolTipMsg;
+ }
+
+ if( getVisible() && pointInView( x, y ) )
+ {
+ if( !tool_tip.empty() )
+ {
+ msg = tool_tip;
+
+ // Convert rect local to screen coordinates
+ localPointToScreen(
+ 0, 0,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ localPointToScreen(
+ mRect.getWidth(), mRect.getHeight(),
+ &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLComboBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL result = FALSE;
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ //give list a chance to pop up and handle key
+ LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ // highlight the original selection before potentially selecting a new item
+ mList->highlightNthItem(mList->getItemIndex(last_selected_item));
+ }
+ result = mList->handleKeyHere(key, mask, FALSE);
+ // if selection has changed, pop open list
+ if (mList->getLastSelectedItem() != last_selected_item)
+ {
+ showList();
+ }
+ }
+ return result;
+}
+
+BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ BOOL result = FALSE;
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // space bar just shows the list
+ if (' ' != uni_char )
+ {
+ LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
+ if (last_selected_item)
+ {
+ // highlight the original selection before potentially selecting a new item
+ mList->highlightNthItem(mList->getItemIndex(last_selected_item));
+ }
+ result = mList->handleUnicodeCharHere(uni_char, called_from_parent);
+ if (mList->getLastSelectedItem() != last_selected_item)
+ {
+ showList();
+ }
+ }
+ }
+ return result;
+}
+
+void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative)
+{
+ LLRect rect( 0, mRect.getHeight(), mRect.getWidth(), 0);
+ if (allow && !mAllowTextEntry)
+ {
+ S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton");
+ mButton->setRect(LLRect( mRect.getWidth() - mArrowImage->getWidth() - 2 * shadow_size,
+ rect.mTop, rect.mRight, rect.mBottom));
+ mButton->setTabStop(FALSE);
+
+ // clear label on button
+ LLString cur_label = mButton->getLabelSelected();
+ setLabel("");
+ if (!mTextEntry)
+ {
+ LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ text_entry_rect.mRight -= mArrowImage->getWidth() + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
+ mTextEntry = new LLLineEditor("combo_text_entry",
+ text_entry_rect,
+ "",
+ LLFontGL::sSansSerifSmall,
+ max_chars,
+ onTextCommit,
+ onTextEntry,
+ NULL,
+ this,
+ NULL, // prevalidate func
+ LLViewBorder::BEVEL_NONE,
+ LLViewBorder::STYLE_LINE,
+ 0); // no border
+ mTextEntry->setSelectAllonFocusReceived(TRUE);
+ mTextEntry->setHandleEditKeysDirectly(TRUE);
+ mTextEntry->setCommitOnFocusLost(FALSE);
+ mTextEntry->setText(cur_label);
+ mTextEntry->setIgnoreTab(TRUE);
+ addChild(mTextEntry);
+ mMaxChars = max_chars;
+ }
+ else
+ {
+ mTextEntry->setVisible(TRUE);
+ }
+ }
+ else if (!allow && mAllowTextEntry)
+ {
+ mButton->setRect(rect);
+ mButton->setTabStop(TRUE);
+
+ if (mTextEntry)
+ {
+ mTextEntry->setVisible(FALSE);
+ }
+ }
+ mAllowTextEntry = allow;
+ mTextEntryTentative = set_tentative;
+}
+
+void LLComboBox::setTextEntry(const LLString& text)
+{
+ if (mTextEntry)
+ {
+ mTextEntry->setText(text);
+ updateSelection();
+ }
+}
+
+//static
+void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data)
+{
+ LLComboBox* self = (LLComboBox*)user_data;
+
+ if (self->mTextEntryCallback)
+ {
+ (*self->mTextEntryCallback)(line_editor, self->mCallbackUserData);
+ }
+
+ KEY key = gKeyboard->currentKey();
+ if (key == KEY_BACKSPACE ||
+ key == KEY_DELETE)
+ {
+ if (self->mList->selectSimpleItem(line_editor->getText(), FALSE))
+ {
+ line_editor->setTentative(FALSE);
+ }
+ else
+ {
+ line_editor->setTentative(self->mTextEntryTentative);
+ }
+ return;
+ }
+
+ if (key == KEY_LEFT ||
+ key == KEY_RIGHT)
+ {
+ return;
+ }
+
+ if (key == KEY_DOWN)
+ {
+ self->setCurrentByIndex(llmin(self->getItemCount() - 1, self->getCurrentIndex() + 1));
+ if (!self->mList->getVisible())
+ {
+ if( self->mPrearrangeCallback )
+ {
+ self->mPrearrangeCallback( self, self->mCallbackUserData );
+ }
+
+ if (self->mList->getItemCount() != 0)
+ {
+ self->showList();
+ }
+ }
+ line_editor->selectAll();
+ line_editor->setTentative(FALSE);
+ }
+ else if (key == KEY_UP)
+ {
+ self->setCurrentByIndex(llmax(0, self->getCurrentIndex() - 1));
+ if (!self->mList->getVisible())
+ {
+ if( self->mPrearrangeCallback )
+ {
+ self->mPrearrangeCallback( self, self->mCallbackUserData );
+ }
+
+ if (self->mList->getItemCount() != 0)
+ {
+ self->showList();
+ }
+ }
+ line_editor->selectAll();
+ line_editor->setTentative(FALSE);
+ }
+ else
+ {
+ // RN: presumably text entry
+ self->updateSelection();
+ }
+}
+
+void LLComboBox::updateSelection()
+{
+ LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
+ // user-entered portion of string, based on assumption that any selected
+ // text was a result of auto-completion
+ LLWString user_wstring = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText();
+ LLString full_string = mTextEntry->getText();
+
+ // go ahead and arrange drop down list on first typed character, even
+ // though we aren't showing it... some code relies on prearrange
+ // callback to populate content
+ if( mTextEntry->getWText().size() == 1 )
+ {
+ if (mPrearrangeCallback)
+ {
+ mPrearrangeCallback( this, mCallbackUserData );
+ }
+ }
+
+ if (mList->selectSimpleItem(full_string, FALSE))
+ {
+ mTextEntry->setTentative(FALSE);
+ }
+ else if (!mList->selectSimpleItemByPrefix(left_wstring, FALSE))
+ {
+ mList->deselectAllItems();
+ mTextEntry->setText(wstring_to_utf8str(user_wstring));
+ mTextEntry->setTentative(mTextEntryTentative);
+ }
+ else
+ {
+ LLWString selected_item = utf8str_to_wstring(mList->getSimpleSelectedItem());
+ LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
+ mTextEntry->setText(wstring_to_utf8str(wtext));
+ mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
+ mTextEntry->endSelection();
+ mTextEntry->setTentative(FALSE);
+ }
+}
+
+//static
+void LLComboBox::onTextCommit(LLUICtrl* caller, void* user_data)
+{
+ LLComboBox* self = (LLComboBox*)user_data;
+ LLString text = self->mTextEntry->getText();
+ self->setSimple(text);
+ self->onCommit();
+ self->mTextEntry->selectAll();
+}
+
+void LLComboBox::setFocus(BOOL b)
+{
+ LLUICtrl::setFocus(b);
+
+ if (b)
+ {
+ mList->clearSearchString();
+ }
+}
+
+//============================================================================
+// LLCtrlListInterface functions
+
+S32 LLComboBox::getItemCount() const
+{
+ return mList->getItemCount();
+}
+
+void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
+{
+ mList->clearColumns();
+ mList->addColumn(column, pos);
+}
+
+void LLComboBox::clearColumns()
+{
+ mList->clearColumns();
+}
+
+void LLComboBox::setColumnLabel(const LLString& column, const LLString& label)
+{
+ mList->setColumnLabel(column, label);
+}
+
+LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
+{
+ return mList->addElement(value, pos, userdata);
+}
+
+LLScrollListItem* LLComboBox::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
+{
+ return mList->addSimpleElement(value, pos, id);
+}
+
+void LLComboBox::clearRows()
+{
+ mList->clearRows();
+}
+
+void LLComboBox::sortByColumn(LLString name, BOOL ascending)
+{
+}
+
+//============================================================================
+//LLCtrlSelectionInterface functions
+
+BOOL LLComboBox::setCurrentByID(const LLUUID& id)
+{
+ BOOL found = mList->selectByID( id );
+
+ if (found)
+ {
+ setLabel(mList->getSimpleSelectedItem());
+ }
+
+ return found;
+}
+
+LLUUID LLComboBox::getCurrentID()
+{
+ return mList->getStringUUIDSelectedItem();
+}
+BOOL LLComboBox::setSelectedByValue(LLSD value, BOOL selected)
+{
+ BOOL found = mList->setSelectedByValue(value, selected);
+ if (found)
+ {
+ setLabel(mList->getSimpleSelectedItem());
+ }
+ return found;
+}
+
+LLSD LLComboBox::getSimpleSelectedValue()
+{
+ return mList->getSimpleSelectedValue();
+}
+
+BOOL LLComboBox::isSelected(LLSD value)
+{
+ return mList->isSelected(value);
+}
+
+BOOL LLComboBox::operateOnSelection(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ mList->deleteSelectedItems();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLComboBox::operateOnAll(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ clearRows();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//static
+void LLComboBox::onListFocusLost(LLUICtrl* old_focus)
+{
+ // if focus is going to nothing (user hit ESC), take it back
+ LLComboBox* combo = (LLComboBox*)old_focus->getParent();
+ combo->hideList();
+ if (gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ combo->focusFirstItem();
+ }
+}
diff --git a/indra/llui/llcombobox.h b/indra/llui/llcombobox.h
new file mode 100644
index 0000000000..1ec31ec1c0
--- /dev/null
+++ b/indra/llui/llcombobox.h
@@ -0,0 +1,178 @@
+/**
+ * @file llcombobox.h
+ * @brief LLComboBox base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A control that displays the name of the chosen item, which when clicked
+// shows a scrolling box of choices.
+
+#ifndef LL_LLCOMBOBOX_H
+#define LL_LLCOMBOBOX_H
+
+#include "llbutton.h"
+#include "lluictrl.h"
+#include "llctrlselectioninterface.h"
+#include "llimagegl.h"
+#include "llrect.h"
+
+// Classes
+
+class LLFontGL;
+class LLButton;
+class LLSquareButton;
+class LLScrollListCtrl;
+class LLLineEditor;
+class LLViewBorder;
+
+extern S32 LLCOMBOBOX_HEIGHT;
+extern S32 LLCOMBOBOX_WIDTH;
+
+class LLComboBox
+: public LLUICtrl, public LLCtrlListInterface
+{
+public:
+ LLComboBox(
+ const LLString& name,
+ const LLRect &rect,
+ const LLString& label,
+ void (*commit_callback)(LLUICtrl*, void*) = NULL,
+ void *callback_userdata = NULL,
+ S32 list_width = 0
+ );
+ virtual ~LLComboBox();
+
+ // LLView interface
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_COMBO_BOX; }
+ virtual LLString getWidgetTag() const { return LL_COMBO_BOX_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void draw();
+ virtual void onFocusLost();
+
+ virtual void setEnabled(BOOL enabled);
+
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ // LLUICtrl interface
+ virtual void clear(); // select nothing
+ virtual void onCommit();
+ virtual BOOL acceptsTextInput() const { return mAllowTextEntry; }
+
+ virtual void setFocus(BOOL b);
+
+ // Selects item by underlying LLSD value, using LLSD::asString() matching.
+ // For simple items, this is just the name of the label.
+ virtual void setValue(const LLSD& value );
+
+ // Gets underlying LLSD value for currently selected items. For simple
+ // items, this is just the label.
+ virtual LLSD getValue() const;
+
+ void setAllowTextEntry(BOOL allow, S32 max_chars = 50, BOOL make_tentative = TRUE);
+ void setTextEntry(const LLString& text);
+
+ void add(const LLString& name, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE); // add item "name" to menu
+ void add(const LLString& name, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE);
+ void add(const LLString& name, void* userdata, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE);
+ void add(const LLString& name, LLSD value, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE);
+ BOOL remove( S32 index ); // remove item by index, return TRUE if found and removed
+ void removeall() { clearRows(); }
+
+ void sortByName(); // Sort the entries in the combobox by name
+
+ // Select current item by name using selectSimpleItem. Returns FALSE if not found.
+ BOOL setSimple(const LLString& name);
+ // Get name of current item. Returns an empty string if not found.
+ const LLString& getSimple() const;
+ // Get contents of column x of selected row
+ const LLString& getSimpleSelectedItem(S32 column = 0) const;
+
+ // Sets the label, which doesn't have to exist in the label.
+ // This is probably a UI abuse.
+ void setLabel(const LLString& name);
+
+ BOOL remove(const LLString& name); // remove item "name", return TRUE if found and removed
+
+ BOOL setCurrentByIndex( S32 index );
+ S32 getCurrentIndex() const;
+
+ //========================================================================
+ LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; };
+ LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; };
+
+ // LLCtrlListInterface functions
+ // See llscrolllistctrl.h
+ virtual S32 getItemCount() const;
+ // Overwrites the default column (See LLScrollListCtrl for format)
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
+ virtual void clearColumns();
+ virtual void setColumnLabel(const LLString& column, const LLString& label);
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+ virtual LLScrollListItem* addSimpleElement(const LLString& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
+ virtual void clearRows();
+ virtual void sortByColumn(LLString name, BOOL ascending);
+
+ // LLCtrlSelectionInterface functions
+ virtual BOOL getCanSelect() const { return TRUE; }
+ virtual BOOL selectFirstItem() { return setCurrentByIndex(0); }
+ virtual BOOL selectNthItem( S32 index ) { return setCurrentByIndex(index); }
+ virtual S32 getFirstSelectedIndex() { return getCurrentIndex(); }
+ virtual BOOL setCurrentByID( const LLUUID& id );
+ virtual LLUUID getCurrentID(); // LLUUID::null if no items in menu
+ virtual BOOL setSelectedByValue(LLSD value, BOOL selected);
+ virtual LLSD getSimpleSelectedValue();
+ virtual BOOL isSelected(LLSD value);
+ virtual BOOL operateOnSelection(EOperation op);
+ virtual BOOL operateOnAll(EOperation op);
+
+ //========================================================================
+
+ void* getCurrentUserdata();
+
+ void setPrearrangeCallback( void (*cb)(LLUICtrl*,void*) ) { mPrearrangeCallback = cb; }
+ void setTextEntryCallback( void (*cb)(LLLineEditor*, void*) ) { mTextEntryCallback = cb; }
+
+ void setButtonVisible(BOOL visible);
+
+ static void onButtonClick(void *userdata);
+ static void onItemSelected(LLUICtrl* item, void *userdata);
+ static void onTopViewLost(LLView* old_focus);
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+ static void onTextEntry(LLLineEditor* line_editor, void* user_data);
+ static void onTextCommit(LLUICtrl* caller, void* user_data);
+
+ void updateSelection();
+ void showList();
+ void hideList();
+
+ static void onListFocusLost(LLUICtrl* old_focus);
+
+protected:
+ LLButton* mButton;
+ LLScrollListCtrl* mList;
+ LLViewBorder* mBorder;
+ BOOL mKeyboardFocusOnClick;
+ BOOL mDrawButton;
+ LLLineEditor* mTextEntry;
+ LLPointer<LLImageGL> mArrowImage;
+ BOOL mAllowTextEntry;
+ S32 mMaxChars;
+ BOOL mTextEntryTentative;
+ void (*mPrearrangeCallback)(LLUICtrl*,void*);
+ void (*mTextEntryCallback)(LLLineEditor*, void*);
+ S32 mListWidth; // width of pop-up list, 0 = use combobox width
+};
+
+#endif
diff --git a/indra/llui/llctrlselectioninterface.cpp b/indra/llui/llctrlselectioninterface.cpp
new file mode 100644
index 0000000000..a58fb88e75
--- /dev/null
+++ b/indra/llui/llctrlselectioninterface.cpp
@@ -0,0 +1,44 @@
+/**
+ * @file llctrlselectioninterface.cpp
+ * @brief Programmatic selection of items in a list.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llctrlselectioninterface.h"
+
+#include "llsd.h"
+
+// virtual
+LLCtrlSelectionInterface::~LLCtrlSelectionInterface()
+{ }
+
+BOOL LLCtrlSelectionInterface::selectByValue(LLSD value)
+{
+ return setSelectedByValue(value, TRUE);
+}
+
+BOOL LLCtrlSelectionInterface::deselectByValue(LLSD value)
+{
+ return setSelectedByValue(value, FALSE);
+}
+
+
+// virtual
+LLCtrlListInterface::~LLCtrlListInterface()
+{ }
+
+LLScrollListItem* LLCtrlListInterface::addSimpleElement(const LLString& value)
+{
+ return addSimpleElement(value, ADD_BOTTOM, LLSD());
+}
+
+LLScrollListItem* LLCtrlListInterface::addSimpleElement(const LLString& value, EAddPosition pos)
+{
+ return addSimpleElement(value, pos, LLSD());
+}
+
+// virtual
+LLCtrlScrollInterface::~LLCtrlScrollInterface()
+{ }
diff --git a/indra/llui/llctrlselectioninterface.h b/indra/llui/llctrlselectioninterface.h
new file mode 100644
index 0000000000..4e2807e9a1
--- /dev/null
+++ b/indra/llui/llctrlselectioninterface.h
@@ -0,0 +1,84 @@
+/**
+ * @file llctrlselectioninterface.h
+ * @brief Programmatic selection of items in a list.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLCTRLSELECTIONINTERFACE_H
+#define LLCTRLSELECTIONINTERFACE_H
+
+#include "stdtypes.h"
+#include "stdenums.h"
+#include "llstring.h"
+
+class LLSD;
+class LLUUID;
+class LLScrollListItem;
+
+class LLCtrlSelectionInterface
+{
+public:
+ virtual ~LLCtrlSelectionInterface();
+
+ enum EOperation
+ {
+ OP_DELETE = 1,
+ OP_SELECT,
+ OP_DESELECT,
+ };
+
+ virtual BOOL getCanSelect() const = 0;
+
+ virtual BOOL selectFirstItem() = 0;
+ virtual BOOL selectNthItem( S32 index ) = 0;
+
+ virtual S32 getFirstSelectedIndex() = 0;
+
+ // TomY TODO: Simply cast the UUIDs to LLSDs, using the selectByValue function
+ virtual BOOL setCurrentByID( const LLUUID& id ) = 0;
+ virtual LLUUID getCurrentID() = 0;
+
+ BOOL selectByValue(LLSD value);
+ BOOL deselectByValue(LLSD value);
+ virtual BOOL setSelectedByValue(LLSD value, BOOL selected) = 0;
+ virtual LLSD getSimpleSelectedValue() = 0;
+
+ virtual BOOL isSelected(LLSD value) = 0;
+
+ virtual BOOL operateOnSelection(EOperation op) = 0;
+ virtual BOOL operateOnAll(EOperation op) = 0;
+};
+
+class LLCtrlListInterface : public LLCtrlSelectionInterface
+{
+public:
+ virtual ~LLCtrlListInterface();
+
+ virtual S32 getItemCount() const = 0;
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM) = 0;
+ virtual void clearColumns() = 0;
+ virtual void setColumnLabel(const LLString& column, const LLString& label) = 0;
+ // TomY TODO: Document this
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL) = 0;
+
+ LLScrollListItem* addSimpleElement(const LLString& value); // defaults to bottom
+ LLScrollListItem* addSimpleElement(const LLString& value, EAddPosition pos); // defaults to no LLSD() id
+ virtual LLScrollListItem* addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id) = 0;
+
+ virtual void clearRows() = 0;
+ virtual void sortByColumn(LLString name, BOOL ascending) = 0;
+};
+
+class LLCtrlScrollInterface
+{
+public:
+ virtual ~LLCtrlScrollInterface();
+
+ virtual S32 getScrollPos() = 0;
+ virtual void setScrollPos( S32 pos ) = 0;
+ virtual void scrollToShowSelected() = 0;
+};
+
+#endif
diff --git a/indra/llui/lldraghandle.cpp b/indra/llui/lldraghandle.cpp
new file mode 100644
index 0000000000..a88fbb7744
--- /dev/null
+++ b/indra/llui/lldraghandle.cpp
@@ -0,0 +1,353 @@
+/**
+ * @file lldraghandle.cpp
+ * @brief LLDragHandle base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A widget for dragging a view around the screen using the mouse.
+
+#include "linden_common.h"
+
+#include "lldraghandle.h"
+
+#include "llmath.h"
+
+//#include "llviewerwindow.h"
+#include "llui.h"
+#include "llmenugl.h"
+#include "lltextbox.h"
+#include "llcontrol.h"
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llwindow.h"
+#include "llfocusmgr.h"
+
+const S32 LEADING_PAD = 5;
+const S32 TITLE_PAD = 8;
+const S32 BORDER_PAD = 1;
+const S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD;
+const S32 RIGHT_PAD = BORDER_PAD + 32; // HACK: space for close btn and minimize btn
+
+S32 LLDragHandle::sSnapMargin = 5;
+
+LLDragHandle::LLDragHandle( const LLString& name, const LLRect& rect, const LLString& title )
+: LLView( name, rect, TRUE ),
+ mDragLastScreenX( 0 ),
+ mDragLastScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mDragHighlightColor( LLUI::sColorsGroup->getColor( "DefaultHighlightLight" ) ),
+ mDragShadowColor( LLUI::sColorsGroup->getColor( "DefaultShadowDark" ) ),
+ mTitleBox( NULL ),
+ mMaxTitleWidth( 0 ),
+ mForeground( TRUE )
+{
+ sSnapMargin = LLUI::sConfigGroup->getS32("SnapMargin");
+
+ setSaveToXML(false);
+}
+
+void LLDragHandle::setTitleVisible(BOOL visible)
+{
+ mTitleBox->setVisible(visible);
+}
+
+LLDragHandleTop::LLDragHandleTop(const LLString& name, const LLRect &rect, const LLString& title)
+: LLDragHandle(name, rect, title)
+{
+ setFollowsAll();
+ setTitle( title );
+}
+
+EWidgetType LLDragHandleTop::getWidgetType() const
+{
+ return WIDGET_TYPE_DRAG_HANDLE_TOP;
+}
+
+LLString LLDragHandleTop::getWidgetTag() const
+{
+ return LL_DRAG_HANDLE_TOP_TAG;
+}
+
+LLDragHandleLeft::LLDragHandleLeft(const LLString& name, const LLRect &rect, const LLString& title)
+: LLDragHandle(name, rect, title)
+{
+ setFollowsAll();
+ setTitle( title );
+}
+
+EWidgetType LLDragHandleLeft::getWidgetType() const
+{
+ return WIDGET_TYPE_DRAG_HANDLE_LEFT;
+}
+
+LLString LLDragHandleLeft::getWidgetTag() const
+{
+ return LL_DRAG_HANDLE_LEFT_TAG;
+}
+
+void LLDragHandleTop::setTitle(const LLString& title)
+{
+ if( mTitleBox )
+ {
+ removeChild(mTitleBox);
+ delete mTitleBox;
+ }
+
+ LLString trimmed_title = title;
+ LLString::trim(trimmed_title);
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ mTitleBox = new LLTextBox( "Drag Handle Title", mRect, trimmed_title, font );
+ mTitleBox->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT);
+ reshapeTitleBox();
+
+ // allow empty titles, as default behavior replaces them with title box name
+ if (trimmed_title.empty())
+ {
+ mTitleBox->setText(LLString::null);
+ }
+ addChild( mTitleBox );
+}
+
+
+const LLString& LLDragHandleTop::getTitle() const
+{
+ return mTitleBox->getText();
+}
+
+
+void LLDragHandleLeft::setTitle(const LLString& )
+{
+ if( mTitleBox )
+ {
+ removeChild(mTitleBox);
+ delete mTitleBox;
+ }
+
+ mTitleBox = NULL;
+
+ /* no title on left edge */
+}
+
+
+const LLString& LLDragHandleLeft::getTitle() const
+{
+ return LLString::null;
+}
+
+
+void LLDragHandleTop::draw()
+{
+ /* Disable lines. Can drag anywhere in most windows. JC
+ if( getVisible() && mEnabled && mForeground)
+ {
+ const S32 BORDER_PAD = 2;
+ const S32 HPAD = 2;
+ const S32 VPAD = 2;
+ S32 left = BORDER_PAD + HPAD;
+ S32 top = mRect.getHeight() - 2 * VPAD;
+ S32 right = mRect.getWidth() - HPAD;
+// S32 bottom = VPAD;
+
+ // draw lines for drag areas
+
+ const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4;
+ S32 line = top - LINE_SPACING;
+
+ LLRect title_rect = mTitleBox->getRect();
+ S32 title_right = title_rect.mLeft + mTitleWidth;
+ BOOL show_right_side = title_right < mRect.getWidth();
+
+ for( S32 i=0; i<4; i++ )
+ {
+ gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor);
+ if( show_right_side )
+ {
+ gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor);
+ }
+
+ gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor);
+ if( show_right_side )
+ {
+ gl_line_2d(title_right, line, right, line, mDragShadowColor);
+ }
+ line -= LINE_SPACING;
+ }
+ }
+ */
+
+ // Colorize the text to match the frontmost state
+ if (mTitleBox)
+ {
+ mTitleBox->setEnabled(mForeground);
+ }
+
+ LLView::draw();
+}
+
+
+// assumes GL state is set for 2D
+void LLDragHandleLeft::draw()
+{
+ /* Disable lines. Can drag anywhere in most windows. JC
+ if( getVisible() && mEnabled && mForeground )
+ {
+ const S32 BORDER_PAD = 2;
+// const S32 HPAD = 2;
+ const S32 VPAD = 2;
+ const S32 LINE_SPACING = 3;
+
+ S32 left = BORDER_PAD + LINE_SPACING;
+ S32 top = mRect.getHeight() - 2 * VPAD;
+// S32 right = mRect.getWidth() - HPAD;
+ S32 bottom = VPAD;
+
+ // draw lines for drag areas
+
+ // no titles yet
+ //LLRect title_rect = mTitleBox->getRect();
+ //S32 title_right = title_rect.mLeft + mTitleWidth;
+ //BOOL show_right_side = title_right < mRect.getWidth();
+
+ S32 line = left;
+ for( S32 i=0; i<4; i++ )
+ {
+ gl_line_2d(line, top, line, bottom, mDragHighlightColor);
+
+ gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor);
+
+ line += LINE_SPACING;
+ }
+ }
+ */
+
+ // Colorize the text to match the frontmost state
+ if (mTitleBox)
+ {
+ mTitleBox->setEnabled(mForeground);
+ }
+
+ LLView::draw();
+}
+
+void LLDragHandleTop::reshapeTitleBox()
+{
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ S32 title_width = font->getWidth( mTitleBox->getText() ) + TITLE_PAD;
+ if (mMaxTitleWidth > 0)
+ title_width = llmin(title_width, mMaxTitleWidth);
+ S32 title_height = llround(font->getLineHeight());
+ LLRect title_rect;
+ title_rect.setLeftTopAndSize(
+ LEFT_PAD,
+ mRect.getHeight() - BORDER_PAD,
+ mRect.getWidth() - LEFT_PAD - RIGHT_PAD,
+ title_height);
+
+ mTitleBox->setRect( title_rect );
+}
+
+void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+ reshapeTitleBox();
+}
+
+void LLDragHandleLeft::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+}
+
+//-------------------------------------------------------------
+// UI event handling
+//-------------------------------------------------------------
+
+BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ // No handler needed for focus lost since this clas has no state that depends on it.
+ gFocusMgr.setMouseCapture(this, NULL );
+
+ localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY);
+ mLastMouseScreenX = mDragLastScreenX;
+ mLastMouseScreenY = mDragLastScreenY;
+
+ // Note: don't pass on to children
+ return TRUE;
+}
+
+
+BOOL LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ }
+
+ // Note: don't pass on to children
+ return TRUE;
+}
+
+
+BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // We only handle the click if the click both started and ended within us
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+
+ // Resize the parent
+ S32 delta_x = screen_x - mDragLastScreenX;
+ S32 delta_y = screen_y - mDragLastScreenY;
+ getParent()->translate(delta_x, delta_y);
+ S32 pre_snap_x = getParent()->getRect().mLeft;
+ S32 pre_snap_y = getParent()->getRect().mBottom;
+ mDragLastScreenX = screen_x;
+ mDragLastScreenY = screen_y;
+
+ LLRect new_rect;
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
+ mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
+ mLastMouseDir = mouse_dir;
+ mLastMouseScreenX = screen_x;
+ mLastMouseScreenY = screen_y;
+
+ LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin);
+
+ getParent()->snappedTo(snap_view);
+ delta_x = new_rect.mLeft - pre_snap_x;
+ delta_y = new_rect.mBottom - pre_snap_y;
+ getParent()->translate(delta_x, delta_y);
+ mDragLastScreenX += delta_x;
+ mDragLastScreenY += delta_y;
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" <<llendl;
+ handled = TRUE;
+ }
+ else if( getVisible() )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ handled = TRUE;
+ }
+
+ // Note: don't pass on to children
+
+ return handled;
+}
+
+void LLDragHandle::setValue(const LLSD& value)
+{
+ setTitle(value.asString());
+}
diff --git a/indra/llui/lldraghandle.h b/indra/llui/lldraghandle.h
new file mode 100644
index 0000000000..557c01cec6
--- /dev/null
+++ b/indra/llui/lldraghandle.h
@@ -0,0 +1,98 @@
+/**
+ * @file lldraghandle.h
+ * @brief LLDragHandle base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A widget for dragging a view around the screen using the mouse.
+
+#ifndef LL_DRAGHANDLE_H
+#define LL_DRAGHANDLE_H
+
+#include "llview.h"
+#include "v4color.h"
+#include "llrect.h"
+#include "llcoord.h"
+
+class LLTextBox;
+
+class LLDragHandle : public LLView
+{
+public:
+ LLDragHandle(const LLString& name, const LLRect& rect, const LLString& title );
+
+ virtual void setValue(const LLSD& value);
+
+ void setForeground(BOOL b) { mForeground = b; }
+ void setMaxTitleWidth(S32 max_width) {mMaxTitleWidth = llmin(max_width, mMaxTitleWidth); }
+ void setTitleVisible(BOOL visible);
+
+ virtual void setTitle( const LLString& title ) = 0;
+ virtual const LLString& getTitle() const = 0;
+ virtual void draw() = 0;
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE) = 0;
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+protected:
+ S32 mDragLastScreenX;
+ S32 mDragLastScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ LLColor4 mDragHighlightColor;
+ LLColor4 mDragShadowColor;
+ LLTextBox* mTitleBox;
+ S32 mMaxTitleWidth;
+ BOOL mForeground;
+
+ // Pixels near the edge to snap floaters.
+ static S32 sSnapMargin;
+};
+
+
+// Use this one for traditional top-of-window draggers
+class LLDragHandleTop
+: public LLDragHandle
+{
+public:
+ LLDragHandleTop(const LLString& name, const LLRect& rect, const LLString& title );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void setTitle( const LLString& title );
+ virtual const LLString& getTitle() const;
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+private:
+ void reshapeTitleBox();
+};
+
+
+// Use this for left-side, vertical text draggers
+class LLDragHandleLeft
+: public LLDragHandle
+{
+public:
+ LLDragHandleLeft(const LLString& name, const LLRect& rect, const LLString& title );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void setTitle( const LLString& title );
+ virtual const LLString& getTitle() const;
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+};
+
+const S32 DRAG_HANDLE_HEIGHT = 16;
+const S32 DRAG_HANDLE_WIDTH = 16;
+
+#endif // LL_DRAGHANDLE_H
diff --git a/indra/llui/lleditmenuhandler.cpp b/indra/llui/lleditmenuhandler.cpp
new file mode 100644
index 0000000000..656eaff563
--- /dev/null
+++ b/indra/llui/lleditmenuhandler.cpp
@@ -0,0 +1,107 @@
+/**
+* @file lleditmenuhandler.cpp
+* @authors Aaron Yonas, James Cook
+*
+* Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+* $License$
+*/
+
+#include "stdtypes.h"
+
+#include "lleditmenuhandler.h"
+
+LLEditMenuHandler* gEditMenuHandler = NULL;
+
+// virtual
+LLEditMenuHandler::~LLEditMenuHandler()
+{ }
+
+// virtual
+void LLEditMenuHandler::undo()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canUndo()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::redo()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canRedo()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::cut()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canCut()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::copy()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canCopy()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::paste()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canPaste()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::doDelete()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canDoDelete()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::selectAll()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canSelectAll()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::deselect()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canDeselect()
+{
+ return FALSE;
+}
+
+// virtual
+void LLEditMenuHandler::duplicate()
+{ }
+
+// virtual
+BOOL LLEditMenuHandler::canDuplicate()
+{
+ return FALSE;
+}
diff --git a/indra/llui/lleditmenuhandler.h b/indra/llui/lleditmenuhandler.h
new file mode 100644
index 0000000000..3f49f2c6e8
--- /dev/null
+++ b/indra/llui/lleditmenuhandler.h
@@ -0,0 +1,50 @@
+/**
+* @file lleditmenuhandler.h
+* @authors Aaron Yonas, James Cook
+*
+* Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+* $License$
+*/
+
+#ifndef LLEDITMENUHANDLER_H
+#define LLEDITMENUHANDLER_H
+
+// Interface used by menu system for plug-in hotkey/menu handling
+class LLEditMenuHandler
+{
+public:
+ // this is needed even though this is just an interface class.
+ virtual ~LLEditMenuHandler();
+
+ virtual void undo();
+ virtual BOOL canUndo();
+
+ virtual void redo();
+ virtual BOOL canRedo();
+
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void paste();
+ virtual BOOL canPaste();
+
+ // "delete" is a keyword
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ virtual void selectAll();
+ virtual BOOL canSelectAll();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ virtual void duplicate();
+ virtual BOOL canDuplicate();
+};
+
+extern LLEditMenuHandler* gEditMenuHandler;
+
+#endif
diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp
new file mode 100644
index 0000000000..3f9139fe86
--- /dev/null
+++ b/indra/llui/llfloater.cpp
@@ -0,0 +1,2933 @@
+/**
+ * @file llfloater.cpp
+ * @brief LLFloater base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+#include "linden_common.h"
+
+#include "llfloater.h"
+
+#include "llfocusmgr.h"
+
+#include "lluictrlfactory.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llmenugl.h" // MENU_BAR_HEIGHT
+#include "lltextbox.h"
+#include "llresmgr.h"
+#include "llui.h"
+#include "llviewborder.h"
+#include "llwindow.h"
+#include "llstl.h"
+#include "llcontrol.h"
+#include "lltabcontainer.h"
+#include "v2math.h"
+
+extern BOOL gNoRender;
+
+const S32 MINIMIZED_WIDTH = 160;
+const S32 CLOSE_BOX_FROM_TOP = 1;
+
+LLString LLFloater::sButtonActiveImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnCloseActiveUUID", //BUTTON_CLOSE
+ "UIImgBtnRestoreActiveUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizeActiveUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffActiveUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnCloseActiveUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonInactiveImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnCloseInactiveUUID", //BUTTON_CLOSE
+ "UIImgBtnRestoreInactiveUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizeInactiveUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffInactiveUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnCloseInactiveUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonPressedImageNames[BUTTON_COUNT] =
+{
+ "UIImgBtnClosePressedUUID", //BUTTON_CLOSE
+ "UIImgBtnRestorePressedUUID", //BUTTON_RESTORE
+ "UIImgBtnMinimizePressedUUID", //BUTTON_MINIMIZE
+ "UIImgBtnTearOffPressedUUID", //BUTTON_TEAR_OFF
+ "UIImgBtnClosePressedUUID", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonNames[BUTTON_COUNT] =
+{
+ "llfloater_close_btn", //BUTTON_CLOSE
+ "llfloater_restore_btn", //BUTTON_RESTORE
+ "llfloater_minimize_btn", //BUTTON_MINIMIZE
+ "llfloater_tear_off_btn", //BUTTON_TEAR_OFF
+ "llfloater_edit_btn", //BUTTON_EDIT
+};
+
+LLString LLFloater::sButtonToolTips[BUTTON_COUNT] =
+{
+#ifdef LL_DARWIN
+ "Close (Cmd-W)", //BUTTON_CLOSE
+#else
+ "Close (Ctrl-W)", //BUTTON_CLOSE
+#endif
+ "Restore", //BUTTON_RESTORE
+ "Minimize", //BUTTON_MINIMIZE
+ "Tear Off", //BUTTON_TEAR_OFF
+ "Edit", //BUTTON_EDIT
+};
+
+LLFloater::click_callback LLFloater::sButtonCallbacks[BUTTON_COUNT] =
+{
+ LLFloater::onClickClose, //BUTTON_CLOSE
+ LLFloater::onClickMinimize, //BUTTON_RESTORE
+ LLFloater::onClickMinimize, //BUTTON_MINIMIZE
+ LLFloater::onClickTearOff, //BUTTON_TEAR_OFF
+ LLFloater::onClickEdit, //BUTTON_EDIT
+};
+
+LLMultiFloater* LLFloater::sHostp = NULL;
+BOOL LLFloater::sEditModeEnabled;
+LLFloater::handle_map_t LLFloater::sFloaterMap;
+
+LLFloaterView* gFloaterView = NULL;
+
+LLFloater::LLFloater()
+{
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ mDragHandle = NULL;
+}
+
+LLFloater::LLFloater(const LLString& name)
+: LLPanel(name)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+
+ LLString title; // null string
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init(title, FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, TRUE, TRUE); // defaults
+}
+
+
+LLFloater::LLFloater(const LLString& name, const LLRect& rect, const LLString& title,
+ BOOL resizable,
+ S32 min_width,
+ S32 min_height,
+ BOOL drag_on_left,
+ BOOL minimizable,
+ BOOL close_btn,
+ BOOL bordered)
+: LLPanel(name, rect, bordered)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+}
+
+LLFloater::LLFloater(const LLString& name, const LLString& rect_control, const LLString& title,
+ BOOL resizable,
+ S32 min_width,
+ S32 min_height,
+ BOOL drag_on_left,
+ BOOL minimizable,
+ BOOL close_btn,
+ BOOL bordered)
+: LLPanel(name, rect_control, bordered)
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ mButtons[i] = NULL;
+ }
+ // automatically take focus when opened
+ mAutoFocus = TRUE;
+ init( title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+}
+
+
+// Note: Floaters constructed from XML call init() twice!
+void LLFloater::init(const LLString& title,
+ BOOL resizable, S32 min_width, S32 min_height,
+ BOOL drag_on_left, BOOL minimizable, BOOL close_btn)
+{
+ // Init function can be called more than once, so clear out old data.
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ mButtonsEnabled[i] = FALSE;
+ if (mButtons[i] != NULL)
+ {
+ removeChild(mButtons[i]);
+ delete mButtons[i];
+ mButtons[i] = NULL;
+ }
+ }
+ mButtonScale = 1.f;
+
+ LLPanel::deleteAllChildren();
+ //sjb: HACK! we had a border which was just deleted, so re-create it
+ if (mBorder != NULL)
+ {
+ addBorder();
+ }
+
+ // chrome floaters don't take focus at all
+ mIsFocusRoot = !getIsChrome();
+
+ // Reset cached pointers
+ mDragHandle = NULL;
+ for (S32 i = 0; i < 4; i++)
+ {
+ mResizeBar[i] = NULL;
+ mResizeHandle[i] = NULL;
+ }
+ mCanTearOff = TRUE;
+ mEditing = FALSE;
+
+ // Clicks stop here.
+ setMouseOpaque(TRUE);
+
+ mFirstLook = TRUE;
+ mForeground = FALSE;
+ mDragOnLeft = drag_on_left == TRUE;
+
+ // Floaters always draw their background, unlike every other panel.
+ setBackgroundVisible(TRUE);
+
+ // Floaters start not minimized. When minimized, they save their
+ // prior rectangle to be used on restore.
+ mMinimized = FALSE;
+ mPreviousRect.set(0,0,0,0);
+
+ S32 close_pad; // space to the right of close box
+ S32 close_box_size; // For layout purposes, how big is the close box?
+ if (close_btn)
+ {
+ close_box_size = LLFLOATER_CLOSE_BOX_SIZE;
+ close_pad = 0;
+ }
+ else
+ {
+ close_box_size = 0;
+ close_pad = 0;
+ }
+
+ S32 minimize_box_size;
+ S32 minimize_pad;
+ if (minimizable && !drag_on_left)
+ {
+ minimize_box_size = LLFLOATER_CLOSE_BOX_SIZE;
+ minimize_pad = 0;
+ }
+ else
+ {
+ minimize_box_size = 0;
+ minimize_pad = 0;
+ }
+
+ // Drag Handle
+ // Add first so it's in the background.
+// const S32 drag_pad = 2;
+ LLRect drag_handle_rect;
+ if (!drag_on_left)
+ {
+ drag_handle_rect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+
+ /*
+ drag_handle_rect.setLeftTopAndSize(
+ 0, mRect.getHeight(),
+ mRect.getWidth()
+ - LLPANEL_BORDER_WIDTH
+ - drag_pad
+ - minimize_box_size - minimize_pad
+ - close_box_size - close_pad,
+ DRAG_HANDLE_HEIGHT);
+ */
+ mDragHandle = new LLDragHandleTop( "Drag Handle", drag_handle_rect, title );
+ }
+ else
+ {
+ drag_handle_rect.setOriginAndSize(
+ 0, 0,
+ DRAG_HANDLE_WIDTH,
+ mRect.getHeight() - LLPANEL_BORDER_WIDTH - close_box_size);
+ mDragHandle = new LLDragHandleLeft("drag", drag_handle_rect, title );
+ }
+ mDragHandle->setSaveToXML(false);
+ addChild(mDragHandle);
+
+ // Resize Handle
+ mResizable = resizable;
+ mMinWidth = min_width;
+ mMinHeight = min_height;
+
+ if( mResizable )
+ {
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ mResizeBar[0] = new LLResizeBar(
+ "resizebar_left",
+ LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0),
+ min_width, min_height, LLResizeBar::LEFT );
+ mResizeBar[0]->setSaveToXML(false);
+ addChild( mResizeBar[0] );
+
+ mResizeBar[1] = new LLResizeBar(
+ "resizebar_top",
+ LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS),
+ min_width, min_height, LLResizeBar::TOP );
+ mResizeBar[1]->setSaveToXML(false);
+ addChild( mResizeBar[1] );
+
+ mResizeBar[2] = new LLResizeBar(
+ "resizebar_right",
+ LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0),
+ min_width, min_height, LLResizeBar::RIGHT );
+ mResizeBar[2]->setSaveToXML(false);
+ addChild( mResizeBar[2] );
+
+ mResizeBar[3] = new LLResizeBar(
+ "resizebar_bottom",
+ LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0),
+ min_width, min_height, LLResizeBar::BOTTOM );
+ mResizeBar[3]->setSaveToXML(false);
+ addChild( mResizeBar[3] );
+
+
+ // Resize handles (corners)
+ mResizeHandle[0] = new LLResizeHandle(
+ "Resize Handle",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, mRect.getWidth(), 0),
+ min_width,
+ min_height,
+ LLResizeHandle::RIGHT_BOTTOM);
+ mResizeHandle[0]->setSaveToXML(false);
+ addChild(mResizeHandle[0]);
+
+ mResizeHandle[1] = new LLResizeHandle( "resize",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_HANDLE_HEIGHT),
+ min_width,
+ min_height,
+ LLResizeHandle::RIGHT_TOP );
+ mResizeHandle[1]->setSaveToXML(false);
+ addChild(mResizeHandle[1]);
+
+ mResizeHandle[2] = new LLResizeHandle( "resize",
+ LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
+ min_width,
+ min_height,
+ LLResizeHandle::LEFT_BOTTOM );
+ mResizeHandle[2]->setSaveToXML(false);
+ addChild(mResizeHandle[2]);
+
+ mResizeHandle[3] = new LLResizeHandle( "resize",
+ LLRect( 0, mRect.getHeight(), RESIZE_HANDLE_WIDTH, mRect.getHeight() - RESIZE_HANDLE_HEIGHT ),
+ min_width,
+ min_height,
+ LLResizeHandle::LEFT_TOP );
+ mResizeHandle[3]->setSaveToXML(false);
+ addChild(mResizeHandle[3]);
+ }
+ else
+ {
+ mResizeBar[0] = NULL;
+ mResizeBar[1] = NULL;
+ mResizeBar[2] = NULL;
+ mResizeBar[3] = NULL;
+
+ mResizeHandle[0] = NULL;
+ mResizeHandle[1] = NULL;
+ mResizeHandle[2] = NULL;
+ mResizeHandle[3] = NULL;
+ }
+
+ // Close button.
+ if (close_btn)
+ {
+ mButtonsEnabled[BUTTON_CLOSE] = TRUE;
+ }
+
+ // Minimize button only for top draggers
+ if ( !drag_on_left && minimizable )
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ }
+
+ buildButtons();
+
+ // JC - Don't do this here, because many floaters first construct themselves,
+ // then show themselves. Put it in setVisibleAndFrontmost.
+ // make_ui_sound("UISndWindowOpen");
+
+ // RN: floaters are created in the invisible state
+ setVisible(FALSE);
+
+ // add self to handle->floater map
+ sFloaterMap[mViewHandle] = this;
+
+ if (!getParent())
+ {
+ gFloaterView->addChild(this);
+ }
+}
+
+// virtual
+LLFloater::~LLFloater()
+{
+ control_map_t::iterator itor;
+ for (itor = mFloaterControls.begin(); itor != mFloaterControls.end(); ++itor)
+ {
+ delete itor->second;
+ }
+ mFloaterControls.clear();
+
+ //// am I not hosted by another floater?
+ //if (mHostHandle.isDead())
+ //{
+ // LLFloaterView* parent = (LLFloaterView*) getParent();
+
+ // if( parent )
+ // {
+ // parent->removeChild( this );
+ // }
+ //}
+
+ // Just in case we might still have focus here, release it.
+ releaseFocus();
+
+ // This is important so that floaters with persistent rects (i.e., those
+ // created with rect control rather than an LLRect) are restored in their
+ // correct, non-minimized positions.
+ setMinimized( FALSE );
+
+ sFloaterMap.erase(mViewHandle);
+
+ delete mDragHandle;
+ for (S32 i = 0; i < 4; i++)
+ {
+ delete mResizeBar[i];
+ delete mResizeHandle[i];
+ }
+}
+
+// virtual
+EWidgetType LLFloater::getWidgetType() const
+{
+ return WIDGET_TYPE_FLOATER;
+}
+
+// virtual
+LLString LLFloater::getWidgetTag() const
+{
+ return LL_FLOATER_TAG;
+}
+
+void LLFloater::destroy()
+{
+ die();
+}
+
+void LLFloater::setVisible( BOOL visible )
+{
+ LLPanel::setVisible(visible);
+ if( visible && mFirstLook )
+ {
+ mFirstLook = FALSE;
+ }
+
+ if( !visible )
+ {
+ if( gFocusMgr.childIsTopView( this ) )
+ {
+ gFocusMgr.setTopView(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+ }
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+
+ if (floaterp)
+ {
+ floaterp->setVisible(visible);
+ }
+ ++dependent_it;
+ }
+}
+
+LLView* LLFloater::getRootMostFastFrameView()
+{
+ // trying to render a background floater in a fast frame, abort!!!
+ //if (!isFrontmost())
+ //{
+ // gViewerWindow->finishFastFrame();
+ //}
+
+ return LLView::getRootMostFastFrameView();
+}
+
+void LLFloater::open()
+{
+ //RN: for now, we don't allow rehosting from one multifloater to another
+ // just need to fix the bugs
+ LLMultiFloater* hostp = getHost();
+ if (sHostp != NULL && hostp == NULL)
+ {
+ // needs a host
+ sHostp->addFloater(this, TRUE);
+ }
+ else if (hostp != NULL)
+ {
+ // already hosted
+ hostp->showFloater(this);
+ }
+ else
+ {
+ setMinimized(FALSE);
+ setVisibleAndFrontmost(mAutoFocus);
+ }
+
+ if (mSoundFlags != SILENT)
+ {
+ if (!getVisible() || isMinimized())
+ {
+ make_ui_sound("UISndWindowOpen");
+ }
+ }
+}
+
+void LLFloater::close(bool app_quitting)
+{
+ // Always unminimize before trying to close.
+ // Most of the time the user will never see this state.
+ setMinimized(FALSE);
+
+ if (canClose())
+ {
+ if (getHost())
+ {
+ ((LLMultiFloater*)getHost())->removeFloater(this);
+ }
+
+ if (mSoundFlags != SILENT
+ && getVisible()
+ && !app_quitting)
+ {
+ make_ui_sound("UISndWindowClose");
+ }
+
+ // now close dependent floater
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ ++dependent_it;
+ floaterp->close();
+ }
+ else
+ {
+ mDependents.erase(dependent_it++);
+ }
+ }
+
+ cleanupHandles();
+ gFocusMgr.clearLastFocusForGroup(this);
+
+ // Do this early, so UI controls will commit before the
+ // window is taken down.
+ releaseFocus();
+
+ // give focus to dependee floater if it exists, and we had focus first
+ if (isDependent())
+ {
+ LLFloater* dependee = LLFloater::getFloaterByHandle(mDependeeHandle);
+ if (dependee && !dependee->isDead())
+ {
+ dependee->setFocus(TRUE);
+ }
+ }
+
+ // Let floater do cleanup.
+ onClose(app_quitting);
+ }
+}
+
+
+void LLFloater::releaseFocus()
+{
+ if( gFocusMgr.childIsTopView( this ) )
+ {
+ gFocusMgr.setTopView(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasKeyboardFocus( this ) )
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ if( gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+}
+
+
+void LLFloater::setResizeLimits( S32 min_width, S32 min_height )
+{
+ mMinWidth = min_width;
+ mMinHeight = min_height;
+
+ for( S32 i = 0; i < 4; i++ )
+ {
+ if( mResizeBar[i] )
+ {
+ mResizeBar[i]->setResizeLimits( min_width, min_height );
+ }
+ if( mResizeHandle[i] )
+ {
+ mResizeHandle[i]->setResizeLimits( min_width, min_height );
+ }
+ }
+}
+
+
+void LLFloater::center()
+{
+ if(getHost())
+ {
+ // hosted floaters can't move
+ return;
+ }
+ const LLRect &window = gFloaterView->getRect();
+
+ S32 left = window.mLeft + (window.getWidth() - mRect.getWidth()) / 2;
+ S32 bottom = window.mBottom + (window.getHeight() - mRect.getHeight()) / 2;
+
+ translate( left - mRect.mLeft, bottom - mRect.mBottom );
+}
+
+void LLFloater::applyRectControl()
+{
+ if (!mRectControl.empty())
+ {
+ const LLRect& rect = LLUI::sConfigGroup->getRect(mRectControl);
+ translate( rect.mLeft - mRect.mLeft, rect.mBottom - mRect.mBottom);
+ if (mResizable)
+ {
+ reshape(llmax(mMinWidth, rect.getWidth()), llmax(mMinHeight, rect.getHeight()));
+ }
+ }
+}
+
+void LLFloater::setTitle( const LLString& title )
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ mDragHandle->setTitle( title );
+}
+
+const LLString& LLFloater::getTitle() const
+{
+ return mDragHandle ? mDragHandle->getTitle() : LLString::null;
+}
+
+void LLFloater::translate(S32 x, S32 y)
+{
+ LLView::translate(x, y);
+
+ if (x != 0 || y != 0)
+ {
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); ++dependent_it)
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ // is a dependent snapped to us?
+ if (floaterp && floaterp->getSnapTarget() == mViewHandle)
+ {
+ floaterp->translate(x, y);
+ }
+ }
+ }
+}
+
+BOOL LLFloater::canSnapTo(LLView* other_view)
+{
+ if (other_view && other_view != getParent())
+ {
+ LLFloater* other_floaterp = (LLFloater*)other_view;
+
+ if (other_floaterp->getSnapTarget() == mViewHandle && mDependents.find(other_floaterp->getHandle()) != mDependents.end())
+ {
+ // this is a dependent that is already snapped to us, so don't snap back to it
+ return FALSE;
+ }
+ }
+
+ return LLView::canSnapTo(other_view);
+}
+
+void LLFloater::snappedTo(LLView* snap_view)
+{
+ if (!snap_view || snap_view == getParent())
+ {
+ clearSnapTarget();
+ }
+ else
+ {
+ //RN: assume it's a floater as it must be a sibling to our parent floater
+ LLFloater* floaterp = (LLFloater*)snap_view;
+
+ setSnapTarget(floaterp->getHandle());
+ }
+}
+
+void LLFloater::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ S32 old_width = mRect.getWidth();
+ S32 old_height = mRect.getHeight();
+
+ LLView::reshape(width, height, called_from_parent);
+
+ if (width != old_width || height != old_height)
+ {
+ // gather all snapped dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); ++dependent_it)
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ // is a dependent snapped to us?
+ if (floaterp && floaterp->getSnapTarget() == mViewHandle)
+ {
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ // check to see if it snapped to right or top
+ LLRect floater_rect = floaterp->getRect();
+ if (floater_rect.mLeft - mRect.mLeft >= old_width ||
+ floater_rect.mRight == mRect.mLeft + old_width)
+ {
+ // was snapped directly onto right side or aligned with it
+ delta_x += width - old_width;
+ }
+ if (floater_rect.mBottom - mRect.mBottom >= old_height ||
+ floater_rect.mTop == mRect.mBottom + old_height)
+ {
+ // was snapped directly onto top side or aligned with it
+ delta_y += height - old_height;
+ }
+
+ floaterp->translate(delta_x, delta_y);
+ }
+ }
+ }
+}
+
+void LLFloater::setMinimized(BOOL minimize)
+{
+ if (minimize == mMinimized) return;
+
+ if (minimize)
+ {
+ mMinimized = TRUE;
+
+ mPreviousRect = mRect;
+
+ reshape( MINIMIZED_WIDTH, LLFLOATER_HEADER_SIZE, TRUE);
+
+ S32 left, bottom;
+ gFloaterView->getMinimizePosition(&left, &bottom);
+ setOrigin( left, bottom );
+
+ if (mButtonsEnabled[BUTTON_MINIMIZE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = TRUE;
+ }
+
+ mMinimizedHiddenChildren.clear();
+ // hide all children
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (!viewp->getVisible())
+ {
+ mMinimizedHiddenChildren.push_back(viewp);
+ }
+ viewp->setVisible(FALSE);
+ }
+
+ // except the special controls
+ if (mDragHandle)
+ {
+ mDragHandle->setVisible(TRUE);
+ }
+
+ setBorderVisible(TRUE);
+
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ floaterp->setVisible(FALSE);
+ }
+ ++dependent_it;
+ }
+
+ // Lose keyboard focus when minimized
+ releaseFocus();
+ }
+ else
+ {
+ reshape( mPreviousRect.getWidth(), mPreviousRect.getHeight(), TRUE );
+ setOrigin( mPreviousRect.mLeft, mPreviousRect.mBottom );
+
+ mMinimized = FALSE;
+
+ if (mButtonsEnabled[BUTTON_RESTORE])
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+
+ // show all children
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ viewp->setVisible(TRUE);
+ }
+
+ std::vector<LLView*>::iterator itor = mMinimizedHiddenChildren.begin();
+ while (itor != mMinimizedHiddenChildren.end())
+ {
+ (*itor)->setVisible(FALSE);
+ ++itor;
+ }
+ mMinimizedHiddenChildren.clear();
+
+ // show dependent floater
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (floaterp)
+ {
+ floaterp->setVisible(TRUE);
+ }
+ ++dependent_it;
+ }
+ }
+ make_ui_sound("UISndWindowClose");
+ updateButtons();
+}
+
+void LLFloater::setFocus( BOOL b )
+{
+ if (b && getIsChrome())
+ {
+ return;
+ }
+ LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this);
+ // a descendent already has focus
+ BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this);
+
+ // give focus to first valid descendent
+ LLPanel::setFocus(b);
+
+ if (b)
+ {
+ // only push focused floaters to front of stack if not in midst of ctrl-tab cycle
+ if (!getHost() && !((LLFloaterView*)getParent())->getCycleMode())
+ {
+ if (!isFrontmost())
+ {
+ setFrontmost();
+ }
+ }
+
+ // when getting focus, delegate to last descendent which had focus
+ if (last_focus && !child_had_focus &&
+ last_focus->isInEnabledChain() &&
+ last_focus->isInVisibleChain())
+ {
+ // FIXME: should handle case where focus doesn't stick
+ last_focus->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloater::setIsChrome(BOOL is_chrome)
+{
+ // chrome floaters don't take focus at all
+ if (is_chrome)
+ {
+ // remove focus if we're changing to chrome
+ setFocus(FALSE);
+ // can't Ctrl-Tab to "chrome" floaters
+ mIsFocusRoot = FALSE;
+ }
+
+ // no titles displayed on "chrome" floaters
+ mDragHandle->setTitleVisible(!is_chrome);
+
+ LLPanel::setIsChrome(is_chrome);
+}
+
+// Change the draw style to account for the foreground state.
+void LLFloater::setForeground(BOOL front)
+{
+ if (front != mForeground)
+ {
+ mForeground = front;
+ mDragHandle->setForeground( front );
+
+ if (!front)
+ {
+ releaseFocus();
+ }
+
+ setBackgroundOpaque( front );
+ }
+}
+
+void LLFloater::cleanupHandles()
+{
+ // remove handles to non-existent dependents
+ for(handle_set_iter_t dependent_it = mDependents.begin();
+ dependent_it != mDependents.end(); )
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(*dependent_it);
+ if (!floaterp)
+ {
+ mDependents.erase(dependent_it++);
+ }
+ else
+ {
+ ++dependent_it;
+ }
+ }
+}
+
+void LLFloater::setHost(LLMultiFloater* host)
+{
+ if (mHostHandle.isDead() && host)
+ {
+ // make buttons smaller for hosted windows to differentiate from parent
+ mButtonScale = 0.9f;
+
+ // add tear off button
+ if (mCanTearOff)
+ {
+ mButtonsEnabled[BUTTON_TEAR_OFF] = TRUE;
+ }
+
+ mIsFocusRoot = FALSE;
+ }
+ else if (!mHostHandle.isDead() && !host)
+ {
+ mButtonScale = 1.f;
+ mIsFocusRoot = TRUE;
+ //mButtonsEnabled[BUTTON_TEAR_OFF] = FALSE;
+ }
+ updateButtons();
+ if (host)
+ {
+ mHostHandle = host->getHandle();
+ mLastHostHandle = host->getHandle();
+ }
+ else
+ {
+ mHostHandle.markDead();
+ }
+}
+
+void LLFloater::moveResizeHandleToFront()
+{
+ // 0 is the bottom right
+ if( mResizeHandle[0] )
+ {
+ sendChildToFront(mResizeHandle[0]);
+ }
+}
+
+BOOL LLFloater::isFrontmost()
+{
+ return gFloaterView && gFloaterView->getFrontmost() == this && getVisible();
+}
+
+void LLFloater::addDependentFloater(LLFloater* floaterp, BOOL reposition)
+{
+ mDependents.insert(floaterp->getHandle());
+ floaterp->mDependeeHandle = getHandle();
+
+ if (reposition)
+ {
+ floaterp->setRect(gFloaterView->findNeighboringPosition(this, floaterp));
+ floaterp->setSnapTarget(mViewHandle);
+ }
+ gFloaterView->adjustToFitScreen(floaterp, FALSE);
+ if (floaterp->isFrontmost())
+ {
+ // make sure to bring self and sibling floaters to front
+ gFloaterView->bringToFront(floaterp);
+ }
+}
+
+void LLFloater::addDependentFloater(LLViewHandle dependent, BOOL reposition)
+{
+ LLFloater* dependent_floaterp = LLFloater::getFloaterByHandle(dependent);
+ if(dependent_floaterp)
+ {
+ addDependentFloater(dependent_floaterp, reposition);
+ }
+}
+
+void LLFloater::removeDependentFloater(LLFloater* floaterp)
+{
+ mDependents.erase(floaterp->getHandle());
+ floaterp->mDependeeHandle = LLViewHandle::sDeadHandle;
+}
+
+// virtual
+BOOL LLFloater::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if( mMinimized )
+ {
+ // Offer the click to the close button.
+ // Any other click = restore
+ if( mButtonsEnabled[BUTTON_CLOSE] )
+ {
+ S32 local_x = x - mButtons[BUTTON_CLOSE]->getRect().mLeft;
+ S32 local_y = y - mButtons[BUTTON_CLOSE]->getRect().mBottom;
+
+ if (mButtons[BUTTON_CLOSE]->pointInView(local_x, local_y)
+ && mButtons[BUTTON_CLOSE]->handleMouseDown(local_x, local_y, mask))
+ {
+ // close button handled it, return
+ return TRUE;
+ }
+ }
+
+ // restore
+ bringToFront( x, y );
+ return TRUE;
+ }
+ else
+ {
+ bringToFront( x, y );
+ return LLPanel::handleMouseDown( x, y, mask );
+ }
+}
+
+// virtual
+BOOL LLFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL was_minimized = mMinimized;
+ bringToFront( x, y );
+ return was_minimized || LLPanel::handleRightMouseDown( x, y, mask );
+}
+
+
+// virtual
+BOOL LLFloater::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL was_minimized = mMinimized;
+ setMinimized(FALSE);
+ return was_minimized || LLPanel::handleDoubleClick(x, y, mask);
+}
+
+void LLFloater::bringToFront( S32 x, S32 y )
+{
+ if (getVisible() && pointInView(x, y))
+ {
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ hostp->showFloater(this);
+ }
+ else
+ {
+ LLFloaterView* parent = (LLFloaterView*) getParent();
+ if (parent)
+ {
+ parent->bringToFront( this );
+ }
+ }
+ }
+}
+
+
+// virtual
+void LLFloater::setVisibleAndFrontmost(BOOL take_focus)
+{
+ setVisible(TRUE);
+ setFrontmost(take_focus);
+}
+
+void LLFloater::setFrontmost(BOOL take_focus)
+{
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ // this will bring the host floater to the front and select
+ // the appropriate panel
+ hostp->showFloater(this);
+ }
+ else
+ {
+ // there are more than one floater view
+ // so we need to query our parent directly
+ ((LLFloaterView*)getParent())->bringToFront(this, take_focus);
+ }
+}
+
+// static
+LLFloater* LLFloater::getFloaterByHandle(LLViewHandle handle)
+{
+ LLFloater* floater = NULL;
+ if (sFloaterMap.count(handle))
+ {
+ floater = sFloaterMap[handle];
+ }
+ if (floater && !floater->isDead())
+ {
+ return floater;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//static
+void LLFloater::setEditModeEnabled(BOOL enable)
+{
+ if (enable != sEditModeEnabled)
+ {
+ S32 count = 0;
+ std::map<LLViewHandle, LLFloater*>::iterator iter;
+ for(iter = sFloaterMap.begin(); iter != sFloaterMap.end(); ++iter)
+ {
+ LLFloater* floater = iter->second;
+ if (!floater->isDead())
+ {
+ iter->second->mButtonsEnabled[BUTTON_EDIT] = enable;
+ iter->second->updateButtons();
+ }
+ count++;
+ }
+ }
+
+ sEditModeEnabled = enable;
+}
+
+//static
+BOOL LLFloater::getEditModeEnabled()
+{
+ return sEditModeEnabled;
+}
+
+// static
+void LLFloater::onClickMinimize(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->setMinimized( !self->isMinimized() );
+}
+
+void LLFloater::onClickTearOff(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ LLMultiFloater* host_floater = self->getHost();
+ if (host_floater) //Tear off
+ {
+ LLRect new_rect;
+ host_floater->removeFloater(self);
+ // reparent to floater view
+ gFloaterView->addChild(self);
+
+ new_rect.setLeftTopAndSize(host_floater->getRect().mLeft + 5, host_floater->getRect().mTop - LLFLOATER_HEADER_SIZE - 5, self->mRect.getWidth(), self->mRect.getHeight());
+
+ self->open();
+ self->setRect(new_rect);
+ gFloaterView->adjustToFitScreen(self, FALSE);
+ self->setCanDrag(TRUE);
+ self->setCanResize(TRUE);
+ self->setCanMinimize(TRUE);
+ }
+ else //Attach to parent.
+ {
+ LLMultiFloater* new_host = (LLMultiFloater*)LLFloater::getFloaterByHandle(self->mLastHostHandle);
+ if (new_host)
+ {
+ new_host->showFloater(self);
+ }
+ }
+}
+
+// static
+void LLFloater::onClickEdit(void *userdata)
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->mEditing = self->mEditing ? FALSE : TRUE;
+}
+
+// static
+void LLFloater::closeByMenu( void* userdata )
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self || self->getHost()) return;
+
+ LLFloaterView* parent = (LLFloaterView*) self->getParent();
+
+ // grab focus status before close just in case floater is deleted
+ BOOL has_focus = gFocusMgr.childHasKeyboardFocus(self);
+ self->close();
+
+ // if this floater used to have focus and now nothing took focus
+ // give it to next floater (to allow closing multiple windows via keyboard in rapid succession)
+ if (has_focus && gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ parent->focusFrontFloater();
+ }
+
+}
+
+
+// static
+void LLFloater::onClickClose( void* userdata )
+{
+ LLFloater* self = (LLFloater*) userdata;
+ if (!self) return;
+
+ self->close();
+}
+
+
+// virtual
+void LLFloater::draw()
+{
+ if( getVisible() )
+ {
+ // draw background
+ if( mBgVisible )
+ {
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = mRect.getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = mRect.getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow");
+ F32 shadow_offset = (F32)LLUI::sConfigGroup->getS32("DropShadowFloater");
+ if (!mBgOpaque)
+ {
+ shadow_offset *= 0.2f;
+ shadow_color.mV[VALPHA] *= 0.5f;
+ }
+ gl_drop_shadow(left, top, right, bottom,
+ shadow_color,
+ llround(shadow_offset));
+
+ // No transparent windows in simple UI
+ if (mBgOpaque)
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
+ }
+ else
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
+ }
+
+ if(gFocusMgr.childHasKeyboardFocus(this) && !getIsChrome() && !getTitle().empty())
+ {
+ // draw highlight on title bar to indicate focus. RDW
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ LLRect r = getRect();
+ gl_rect_2d_offset_local(0, r.getHeight(), r.getWidth(), r.getHeight() - (S32)font->getLineHeight() - 1,
+ LLUI::sColorsGroup->getColor("TitleBarFocusColor"), 0, TRUE);
+ }
+ }
+
+ if( mDefaultBtn)
+ {
+ if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ // is this button a direct descendent and not a nested widget (e.g. checkbox)?
+ BOOL focus_is_child_button = focus_ctrl->getWidgetType() == WIDGET_TYPE_BUTTON && focus_ctrl->getParent() == this;
+ // only enable default button when current focus is not a button
+ mDefaultBtn->setBorderEnabled(!focus_is_child_button);
+ }
+ else
+ {
+ mDefaultBtn->setBorderEnabled(FALSE);
+ }
+ }
+
+ // draw children
+ LLView* focused_child = gFocusMgr.getKeyboardFocus();
+ BOOL focused_child_visible = FALSE;
+ if (focused_child && focused_child->getParent() == this)
+ {
+ focused_child_visible = focused_child->getVisible();
+ focused_child->setVisible(FALSE);
+ }
+
+ LLView::draw();
+
+ if( mBgVisible )
+ {
+ // add in a border to improve spacialized visual aclarity ;)
+ // use lines instead of gl_rect_2d so we can round the edges as per james' recommendation
+ LLUI::setLineWidth(1.5f);
+ LLColor4 outlineColor = gFocusMgr.childHasKeyboardFocus(this) ? LLUI::sColorsGroup->getColor("FloaterFocusBorderColor") : LLUI::sColorsGroup->getColor("FloaterUnfocusBorderColor");
+ gl_rect_2d_offset_local(0, mRect.getHeight() + 1, mRect.getWidth() + 1, 0, outlineColor, -LLPANEL_BORDER_WIDTH, FALSE);
+ LLUI::setLineWidth(1.f);
+ }
+
+ if (focused_child_visible)
+ {
+ focused_child->setVisible(TRUE);
+ }
+ drawChild(focused_child);
+ }
+}
+
+// virtual
+void LLFloater::onClose(bool app_quitting)
+{
+ destroy();
+}
+
+// virtual
+BOOL LLFloater::canClose()
+{
+ return TRUE;
+}
+
+// virtual
+BOOL LLFloater::canSaveAs()
+{
+ return FALSE;
+}
+
+// virtual
+void LLFloater::saveAs()
+{
+}
+
+void LLFloater::setCanMinimize(BOOL can_minimize)
+{
+ // removing minimize/restore button programmatically,
+ // go ahead and uniminimize floater
+ if (!can_minimize)
+ {
+ setMinimized(FALSE);
+ }
+
+ if (can_minimize)
+ {
+ if (isMinimized())
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = TRUE;
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = TRUE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+ }
+ else
+ {
+ mButtonsEnabled[BUTTON_MINIMIZE] = FALSE;
+ mButtonsEnabled[BUTTON_RESTORE] = FALSE;
+ }
+
+ updateButtons();
+}
+
+void LLFloater::setCanClose(BOOL can_close)
+{
+ mButtonsEnabled[BUTTON_CLOSE] = can_close;
+
+ updateButtons();
+}
+
+void LLFloater::setCanTearOff(BOOL can_tear_off)
+{
+ mCanTearOff = can_tear_off;
+ mButtonsEnabled[BUTTON_TEAR_OFF] = mCanTearOff && !mHostHandle.isDead();
+
+ updateButtons();
+}
+
+
+void LLFloater::setCanResize(BOOL can_resize)
+{
+ if (mResizable && !can_resize)
+ {
+ removeChild(mResizeBar[0]);
+ removeChild(mResizeBar[1]);
+ removeChild(mResizeBar[2]);
+ removeChild(mResizeBar[3]);
+ removeChild(mResizeHandle[0]);
+ removeChild(mResizeHandle[1]);
+ removeChild(mResizeHandle[2]);
+ removeChild(mResizeHandle[3]);
+ delete mResizeBar[0];
+ delete mResizeBar[1];
+ delete mResizeBar[2];
+ delete mResizeBar[3];
+ delete mResizeHandle[0];
+ delete mResizeHandle[1];
+ delete mResizeHandle[2];
+ mResizeHandle[3] = NULL;
+ mResizeBar[0] = NULL;
+ mResizeBar[1] = NULL;
+ mResizeBar[2] = NULL;
+ mResizeBar[3] = NULL;
+ mResizeHandle[0] = NULL;
+ mResizeHandle[1] = NULL;
+ mResizeHandle[2] = NULL;
+ mResizeHandle[3] = NULL;
+ }
+ else if (!mResizable && can_resize)
+ {
+ // Resize bars (sides)
+ const S32 RESIZE_BAR_THICKNESS = 3;
+ mResizeBar[0] = new LLResizeBar(
+ "resizebar_left",
+ LLRect( 0, mRect.getHeight(), RESIZE_BAR_THICKNESS, 0),
+ mMinWidth, mMinHeight, LLResizeBar::LEFT );
+ mResizeBar[0]->setSaveToXML(false);
+ addChild( mResizeBar[0] );
+
+ mResizeBar[1] = new LLResizeBar(
+ "resizebar_top",
+ LLRect( 0, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_BAR_THICKNESS),
+ mMinWidth, mMinHeight, LLResizeBar::TOP );
+ mResizeBar[1]->setSaveToXML(false);
+ addChild( mResizeBar[1] );
+
+ mResizeBar[2] = new LLResizeBar(
+ "resizebar_right",
+ LLRect( mRect.getWidth() - RESIZE_BAR_THICKNESS, mRect.getHeight(), mRect.getWidth(), 0),
+ mMinWidth, mMinHeight, LLResizeBar::RIGHT );
+ mResizeBar[2]->setSaveToXML(false);
+ addChild( mResizeBar[2] );
+
+ mResizeBar[3] = new LLResizeBar(
+ "resizebar_bottom",
+ LLRect( 0, RESIZE_BAR_THICKNESS, mRect.getWidth(), 0),
+ mMinWidth, mMinHeight, LLResizeBar::BOTTOM );
+ mResizeBar[3]->setSaveToXML(false);
+ addChild( mResizeBar[3] );
+
+
+ // Resize handles (corners)
+ mResizeHandle[0] = new LLResizeHandle(
+ "Resize Handle",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT, mRect.getWidth(), 0),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::RIGHT_BOTTOM);
+ mResizeHandle[0]->setSaveToXML(false);
+ addChild(mResizeHandle[0]);
+
+ mResizeHandle[1] = new LLResizeHandle( "resize",
+ LLRect( mRect.getWidth() - RESIZE_HANDLE_WIDTH, mRect.getHeight(), mRect.getWidth(), mRect.getHeight() - RESIZE_HANDLE_HEIGHT),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::RIGHT_TOP );
+ mResizeHandle[1]->setSaveToXML(false);
+ addChild(mResizeHandle[1]);
+
+ mResizeHandle[2] = new LLResizeHandle( "resize",
+ LLRect( 0, RESIZE_HANDLE_HEIGHT, RESIZE_HANDLE_WIDTH, 0 ),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::LEFT_BOTTOM );
+ mResizeHandle[2]->setSaveToXML(false);
+ addChild(mResizeHandle[2]);
+
+ mResizeHandle[3] = new LLResizeHandle( "resize",
+ LLRect( 0, mRect.getHeight(), RESIZE_HANDLE_WIDTH, mRect.getHeight() - RESIZE_HANDLE_HEIGHT ),
+ mMinWidth,
+ mMinHeight,
+ LLResizeHandle::LEFT_TOP );
+ mResizeHandle[3]->setSaveToXML(false);
+ addChild(mResizeHandle[3]);
+ }
+ mResizable = can_resize;
+}
+
+void LLFloater::setCanDrag(BOOL can_drag)
+{
+ // if we delete drag handle, we no longer have access to the floater's title
+ // so just enable/disable it
+ if (!can_drag && mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(FALSE);
+ }
+ else if (can_drag && !mDragHandle->getEnabled())
+ {
+ mDragHandle->setEnabled(TRUE);
+ }
+}
+
+void LLFloater::updateButtons()
+{
+ S32 button_count = 0;
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ if (mButtonsEnabled[i])
+ {
+ button_count++;
+
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ mRect.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));
+ }
+ else
+ {
+ btn_rect.setLeftTopAndSize(
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * button_count,
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP,
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround((F32)LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+
+ mButtons[i]->setRect(btn_rect);
+ mButtons[i]->setVisible(TRUE);
+ mButtons[i]->setEnabled(TRUE);
+ // the restore button should have a tab stop so that it takes action when you Ctrl-Tab to a minimized floater
+ mButtons[i]->setTabStop(i == BUTTON_RESTORE);
+ }
+ else if (mButtons[i])
+ {
+ mButtons[i]->setVisible(FALSE);
+ mButtons[i]->setEnabled(FALSE);
+ }
+ }
+
+ mDragHandle->setMaxTitleWidth(mRect.getWidth() - (button_count * (LLFLOATER_CLOSE_BOX_SIZE + 1)));
+}
+
+void LLFloater::buildButtons()
+{
+ for (S32 i = 0; i < BUTTON_COUNT; i++)
+ {
+ LLRect btn_rect;
+ if (mDragOnLeft)
+ {
+ btn_rect.setLeftTopAndSize(
+ LLPANEL_BORDER_WIDTH,
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+ else
+ {
+ btn_rect.setLeftTopAndSize(
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH - (LLFLOATER_CLOSE_BOX_SIZE + 1) * (i + 1),
+ mRect.getHeight() - CLOSE_BOX_FROM_TOP,
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale),
+ llround(LLFLOATER_CLOSE_BOX_SIZE * mButtonScale));
+ }
+
+ LLButton* buttonp = new LLButton(
+ sButtonNames[i],
+ btn_rect,
+ sButtonActiveImageNames[i],
+ sButtonPressedImageNames[i],
+ "",
+ sButtonCallbacks[i],
+ this,
+ LLFontGL::sSansSerif);
+
+ buttonp->setTabStop(FALSE);
+ buttonp->setFollowsTop();
+ buttonp->setFollowsRight();
+ buttonp->setToolTip( sButtonToolTips[i] );
+ buttonp->setImageColor(LLUI::sColorsGroup->getColor("FloaterButtonImageColor"));
+ buttonp->setHoverImages(sButtonPressedImageNames[i],
+ sButtonPressedImageNames[i]);
+ buttonp->setScaleImage(TRUE);
+ buttonp->setSaveToXML(false);
+ addChild(buttonp);
+ mButtons[i] = buttonp;
+ }
+
+ updateButtons();
+}
+
+/////////////////////////////////////////////////////
+// LLFloaterView
+
+LLFloaterView::LLFloaterView( const LLString& name, const LLRect& rect )
+: LLUICtrl( name, rect, FALSE, NULL, NULL, FOLLOWS_ALL ),
+ mFocusCycleMode(FALSE),
+ mSnapOffsetBottom(0)
+{
+ setTabStop(FALSE);
+ resetStartingFloaterPosition();
+}
+
+EWidgetType LLFloaterView::getWidgetType() const
+{
+ return WIDGET_TYPE_FLOATER_VIEW;
+}
+
+LLString LLFloaterView::getWidgetTag() const
+{
+ return LL_FLOATER_VIEW_TAG;
+}
+
+// By default, adjust vertical.
+void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ reshape(width, height, called_from_parent, ADJUST_VERTICAL_YES);
+}
+
+// When reshaping this view, make the floaters follow their closest edge.
+void LLFloaterView::reshape(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical)
+{
+ S32 old_width = mRect.getWidth();
+ S32 old_height = mRect.getHeight();
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater* floaterp = (LLFloater*)viewp;
+ if (floaterp->isDependent())
+ {
+ // dependents use same follow flags as their "dependee"
+ continue;
+ }
+ LLRect r = floaterp->getRect();
+
+ // Compute absolute distance from each edge of screen
+ S32 left_offset = llabs(r.mLeft - 0);
+ S32 right_offset = llabs(old_width - r.mRight);
+
+ S32 top_offset = llabs(old_height - r.mTop);
+ S32 bottom_offset = llabs(r.mBottom - 0);
+
+ // Make if follow the edge it is closest to
+ U32 follow_flags = 0x0;
+
+ if (left_offset < right_offset)
+ {
+ follow_flags |= FOLLOWS_LEFT;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_RIGHT;
+ }
+
+ // "No vertical adjustment" usually means that the bottom of the view
+ // has been pushed up or down. Hence we want the floaters to follow
+ // the top.
+ if (!adjust_vertical)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else if (top_offset < bottom_offset)
+ {
+ follow_flags |= FOLLOWS_TOP;
+ }
+ else
+ {
+ follow_flags |= FOLLOWS_BOTTOM;
+ }
+
+ floaterp->setFollows(follow_flags);
+
+ //RN: all dependent floaters copy follow behavior of "parent"
+ for(LLFloater::handle_set_iter_t dependent_it = floaterp->mDependents.begin();
+ dependent_it != floaterp->mDependents.end(); ++dependent_it)
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp)
+ {
+ dependent_floaterp->setFollows(follow_flags);
+ }
+ }
+ }
+
+ LLView::reshape(width, height, called_from_parent);
+}
+
+
+void LLFloaterView::restoreAll()
+{
+ // make sure all subwindows aren't minimized
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ floaterp->setMinimized(FALSE);
+ }
+
+ //FIXME: make sure dependents are restored
+
+ // children then deleted by default view constructor
+}
+
+
+void LLFloaterView::getNewFloaterPosition(S32* left,S32* top)
+{
+ // Workaround: mRect may change between when this object is created and the first time it is used.
+ static BOOL first = TRUE;
+ if( first )
+ {
+ resetStartingFloaterPosition();
+ first = FALSE;
+ }
+
+ const S32 FLOATER_PAD = 16;
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+ LLRect full_window(0, window_size.mY, window_size.mX, 0);
+ LLRect floater_creation_rect(
+ 160,
+ full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
+ full_window.getWidth() * 2 / 3,
+ 130 );
+ floater_creation_rect.stretch( -FLOATER_PAD );
+
+ *left = mNextLeft;
+ *top = mNextTop;
+
+ const S32 STEP = 25;
+ S32 bottom = floater_creation_rect.mBottom + 2 * STEP;
+ S32 right = floater_creation_rect.mRight - 4 * STEP;
+
+ mNextTop -= STEP;
+ mNextLeft += STEP;
+
+ if( (mNextTop < bottom ) || (mNextLeft > right) )
+ {
+ mColumn++;
+ mNextTop = floater_creation_rect.mTop;
+ mNextLeft = STEP * mColumn;
+
+ if( (mNextTop < bottom) || (mNextLeft > right) )
+ {
+ // Advancing the column didn't work, so start back at the beginning
+ resetStartingFloaterPosition();
+ }
+ }
+}
+
+void LLFloaterView::resetStartingFloaterPosition()
+{
+ const S32 FLOATER_PAD = 16;
+ LLCoordWindow window_size;
+ getWindow()->getSize(&window_size);
+ LLRect full_window(0, window_size.mY, window_size.mX, 0);
+ LLRect floater_creation_rect(
+ 160,
+ full_window.getHeight() - 2 * MENU_BAR_HEIGHT,
+ full_window.getWidth() * 2 / 3,
+ 130 );
+ floater_creation_rect.stretch( -FLOATER_PAD );
+
+ mNextLeft = floater_creation_rect.mLeft;
+ mNextTop = floater_creation_rect.mTop;
+ mColumn = 0;
+}
+
+LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor )
+{
+ LLRect base_rect = reference_floater->getRect();
+ S32 width = neighbor->getRect().getWidth();
+ S32 height = neighbor->getRect().getHeight();
+ LLRect new_rect = neighbor->getRect();
+
+ LLRect expanded_base_rect = base_rect;
+ expanded_base_rect.stretch(10);
+ for(LLFloater::handle_set_iter_t dependent_it = reference_floater->mDependents.begin();
+ dependent_it != reference_floater->mDependents.end(); ++dependent_it)
+ {
+ LLFloater* sibling = LLFloater::getFloaterByHandle(*dependent_it);
+ // check for dependents within 10 pixels of base floater
+ if (sibling &&
+ sibling != neighbor &&
+ sibling->getVisible() &&
+ expanded_base_rect.rectInRect(&sibling->getRect()))
+ {
+ base_rect |= sibling->getRect();
+ }
+ }
+
+ S32 left_margin = llmax(0, base_rect.mLeft);
+ S32 right_margin = llmax(0, mRect.getWidth() - base_rect.mRight);
+ S32 top_margin = llmax(0, mRect.getHeight() - base_rect.mTop);
+ S32 bottom_margin = llmax(0, base_rect.mBottom);
+
+ // find position for floater in following order
+ // right->left->bottom->top
+ for (S32 i = 0; i < 5; i++)
+ {
+ if (right_margin > width)
+ {
+ new_rect.translate(base_rect.mRight - neighbor->mRect.mLeft, base_rect.mTop - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (left_margin > width)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mRight, base_rect.mTop - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (bottom_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mLeft, base_rect.mBottom - neighbor->mRect.mTop);
+ return new_rect;
+ }
+ else if (top_margin > height)
+ {
+ new_rect.translate(base_rect.mLeft - neighbor->mRect.mLeft, base_rect.mTop - neighbor->mRect.mBottom);
+ return new_rect;
+ }
+
+ // keep growing margins to find "best" fit
+ left_margin += 20;
+ right_margin += 20;
+ top_margin += 20;
+ bottom_margin += 20;
+ }
+
+ // didn't find anything, return initial rect
+ return new_rect;
+}
+
+void LLFloaterView::setCycleMode(BOOL mode)
+{
+ mFocusCycleMode = mode;
+}
+
+BOOL LLFloaterView::getCycleMode()
+{
+ return mFocusCycleMode;
+}
+
+void LLFloaterView::bringToFront(LLFloater* child, BOOL give_focus)
+{
+ //FIXME: make this respect floater's mAutoFocus value, instead of using parameter
+ if (child->getHost())
+ {
+ // this floater is hosted elsewhere and hence not one of our children, abort
+ return;
+ }
+ std::vector<LLView*> floaters_to_move;
+ // Look at all floaters...tab
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater *floater = (LLFloater *)viewp;
+
+ // ...but if I'm a dependent floater...
+ if (child->isDependent())
+ {
+ // ...look for floaters that have me as a dependent...
+ LLFloater::handle_set_iter_t found_dependent = floater->mDependents.find(child->getHandle());
+
+ if (found_dependent != floater->mDependents.end())
+ {
+ // ...and make sure all children of that floater (including me) are brought to front...
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); )
+ {
+ LLFloater* sibling = LLFloater::getFloaterByHandle(*dependent_it);
+ if (sibling)
+ {
+ floaters_to_move.push_back(sibling);
+ }
+ ++dependent_it;
+ }
+ //...before bringing my parent to the front...
+ floaters_to_move.push_back(floater);
+ }
+ }
+ }
+
+ std::vector<LLView*>::iterator view_it;
+ for(view_it = floaters_to_move.begin(); view_it != floaters_to_move.end(); ++view_it)
+ {
+ LLFloater* floaterp = (LLFloater*)(*view_it);
+ sendChildToFront(floaterp);
+
+ floaterp->setMinimized(FALSE);
+ }
+ floaters_to_move.clear();
+
+ // ...then bringing my own dependents to the front...
+ for(LLFloater::handle_set_iter_t dependent_it = child->mDependents.begin();
+ dependent_it != child->mDependents.end(); )
+ {
+ LLFloater* dependent = getFloaterByHandle(*dependent_it);
+ if (dependent)
+ {
+ sendChildToFront(dependent);
+ dependent->setMinimized(FALSE);
+ }
+ ++dependent_it;
+ }
+
+ // ...and finally bringing myself to front
+ // (do this last, so that I'm left in front at end of this call)
+ if( *getChildList()->begin() != child )
+ {
+ sendChildToFront(child);
+ }
+ child->setMinimized(FALSE);
+ if (give_focus && !gFocusMgr.childHasKeyboardFocus(child))
+ {
+ child->setFocus(TRUE);
+ }
+}
+
+void LLFloaterView::highlightFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ // skip dependent floaters, as we'll handle them in a batch along with their dependee(?)
+ if (floater->isDependent())
+ {
+ continue;
+ }
+
+ BOOL floater_or_dependent_has_focus = gFocusMgr.childHasKeyboardFocus(floater);
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end();
+ ++dependent_it)
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp && gFocusMgr.childHasKeyboardFocus(dependent_floaterp))
+ {
+ floater_or_dependent_has_focus = TRUE;
+ }
+ }
+
+ // now set this floater and all its dependents
+ floater->setForeground(floater_or_dependent_has_focus);
+
+ for(LLFloater::handle_set_iter_t dependent_it = floater->mDependents.begin();
+ dependent_it != floater->mDependents.end(); )
+ {
+ LLFloater* dependent_floaterp = getFloaterByHandle(*dependent_it);
+ if (dependent_floaterp)
+ {
+ dependent_floaterp->setForeground(floater_or_dependent_has_focus);
+ }
+ ++dependent_it;
+ }
+
+ floater->cleanupHandles();
+ }
+}
+
+void LLFloaterView::unhighlightFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater *floater = (LLFloater *)(*child_it);
+
+ floater->setForeground(FALSE);
+ }
+}
+
+void LLFloaterView::focusFrontFloater()
+{
+ LLFloater* floaterp = getFrontmost();
+ if (floaterp)
+ {
+ floaterp->setFocus(TRUE);
+ }
+}
+
+void LLFloaterView::getMinimizePosition(S32 *left, S32 *bottom)
+{
+ // count the number of minimized children
+ S32 count = 0;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLFloater *floater = (LLFloater *)viewp;
+ if (floater->isMinimized())
+ {
+ count++;
+ }
+ }
+
+ // space over for that many and up if necessary
+ S32 tiles_per_row = mRect.getWidth() / MINIMIZED_WIDTH;
+
+ *left = (count % tiles_per_row) * MINIMIZED_WIDTH;
+ *bottom = (count / tiles_per_row) * LLFLOATER_HEADER_SIZE;
+}
+
+
+void LLFloaterView::destroyAllChildren()
+{
+ LLView::deleteAllChildren();
+}
+
+void LLFloaterView::closeAllChildren(bool app_quitting)
+{
+ // iterate over a copy of the list, because closing windows will destroy
+ // some windows on the list.
+ child_list_t child_list = *(getChildList());
+
+ for (child_list_const_iter_t it = child_list.begin(); it != child_list.end(); ++it)
+ {
+ LLView* viewp = *it;
+ child_list_const_iter_t exists = std::find(getChildList()->begin(), getChildList()->end(), viewp);
+ if (exists == getChildList()->end())
+ {
+ // this floater has already been removed
+ continue;
+ }
+
+ LLFloater* floaterp = (LLFloater*)viewp;
+
+ // Attempt to close floater. This will cause the "do you want to save"
+ // dialogs to appear.
+ if (floaterp->canClose())
+ {
+ floaterp->close(app_quitting);
+ }
+ }
+}
+
+
+BOOL LLFloaterView::allChildrenClosed()
+{
+ // see if there are any visible floaters (some floaters "close"
+ // by setting themselves invisible)
+ S32 visible_count = 0;
+ for (child_list_const_iter_t it = getChildList()->begin(); it != getChildList()->end(); ++it)
+ {
+ LLView* viewp = *it;
+ LLFloater* floaterp = (LLFloater*)viewp;
+
+ if (floaterp->getVisible() && floaterp->canClose())
+ {
+ visible_count++;
+ }
+ }
+
+ return (visible_count == 0);
+}
+
+
+void LLFloaterView::refresh()
+{
+ // Constrain children to be entirely on the screen
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ if( floaterp->getVisible() )
+ {
+ adjustToFitScreen(floaterp, TRUE);
+ }
+ }
+}
+
+void LLFloaterView::adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside)
+{
+ if (floater->getParent() != this)
+ {
+ // floater is hosted elsewhere, so ignore
+ return;
+ }
+ S32 screen_width = getSnapRect().getWidth();
+ S32 screen_height = getSnapRect().getHeight();
+ // convert to local coordinate frame
+ LLRect snap_rect_local = getSnapRect();
+ snap_rect_local.translate(-mRect.mLeft, -mRect.mBottom);
+
+ if( floater->isResizable() )
+ {
+ LLRect view_rect = floater->getRect();
+ S32 view_width = view_rect.getWidth();
+ S32 view_height = view_rect.getHeight();
+ S32 min_width;
+ S32 min_height;
+ floater->getResizeLimits( &min_width, &min_height );
+
+ S32 new_width = llmax( min_width, view_width );
+ S32 new_height = llmax( min_height, view_height );
+
+ if( (new_width > screen_width) || (new_height > screen_height) )
+ {
+ new_width = llmin(new_width, screen_width);
+ new_height = llmin(new_height, screen_height);
+
+ floater->reshape( new_width, new_height, TRUE );
+
+ // Make sure the damn thing is actually onscreen.
+ if (floater->translateIntoRect(snap_rect_local, FALSE))
+ {
+ floater->clearSnapTarget();
+ }
+ }
+ else if (!floater->isMinimized())
+ {
+ floater->reshape(new_width, new_height, TRUE);
+ }
+ }
+
+ if (floater->translateIntoRect( snap_rect_local, allow_partial_outside ))
+ {
+ floater->clearSnapTarget();
+ }
+}
+
+void LLFloaterView::draw()
+{
+ if( getVisible() )
+ {
+ refresh();
+
+ // hide focused floater if in cycle mode, so that it can be drawn on top
+ LLFloater* focused_floater = getFocusedFloater();
+ BOOL floater_visible = FALSE;
+ if (mFocusCycleMode && focused_floater)
+ {
+ floater_visible = focused_floater->getVisible();
+ focused_floater->setVisible(FALSE);
+ }
+
+ // And actually do the draw
+ LLView::draw();
+
+ // manually draw focused floater on top when in cycle mode
+ if (mFocusCycleMode && focused_floater)
+ {
+ // draw focused item on top for better feedback
+ focused_floater->setVisible(floater_visible);
+ if (floater_visible)
+ {
+ drawChild(focused_floater);
+ }
+ }
+ }
+}
+
+const LLRect LLFloaterView::getSnapRect() const
+{
+ LLRect snap_rect = mRect;
+ snap_rect.mBottom += mSnapOffsetBottom;
+
+ return snap_rect;
+}
+
+LLFloater *LLFloaterView::getFocusedFloater()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLUICtrl* ctrlp = (*child_it)->isCtrl() ? static_cast<LLUICtrl*>(*child_it) : NULL;
+ if ( ctrlp && ctrlp->hasFocus() )
+ {
+ return static_cast<LLFloater *>(ctrlp);
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getFrontmost()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() )
+ {
+ return (LLFloater *)viewp;
+ }
+ }
+ return NULL;
+}
+
+LLFloater *LLFloaterView::getBackmost()
+{
+ LLFloater* back_most = NULL;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if ( viewp->getVisible() )
+ {
+ back_most = (LLFloater *)viewp;
+ }
+ }
+ return back_most;
+}
+
+void LLFloaterView::syncFloaterTabOrder()
+{
+ // bring focused floater to front
+ for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ if (gFocusMgr.childHasKeyboardFocus(floaterp))
+ {
+ bringToFront(floaterp, FALSE);
+ break;
+ }
+ }
+
+ // then sync draw order to tab order
+ for ( child_list_const_reverse_iter_t child_it = getChildList()->rbegin(); child_it != getChildList()->rend(); ++child_it)
+ {
+ LLFloater* floaterp = (LLFloater*)*child_it;
+ moveChildToFrontOfTabGroup(floaterp);
+ }
+}
+
+LLFloater* LLFloaterView::getFloaterByHandle(LLViewHandle handle)
+{
+ if (handle == LLViewHandle::sDeadHandle)
+ {
+ return NULL;
+ }
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (((LLFloater*)viewp)->getHandle() == handle)
+ {
+ return (LLFloater*)viewp;
+ }
+ }
+ return NULL;
+}
+
+LLFloater* LLFloaterView::getParentFloater(LLView* viewp)
+{
+ LLView* parentp = viewp->getParent();
+
+ while(parentp && parentp != this)
+ {
+ viewp = parentp;
+ parentp = parentp->getParent();
+ }
+
+ if (parentp == this)
+ {
+ return (LLFloater*)viewp;
+ }
+
+ return NULL;
+}
+
+S32 LLFloaterView::getZOrder(LLFloater* child)
+{
+ S32 rv = 0;
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if(viewp == child)
+ {
+ break;
+ }
+ ++rv;
+ }
+ return rv;
+}
+
+void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->pushVisible(visible);
+ }
+ }
+}
+
+void LLFloaterView::popVisibleAll(const skip_list_t& skip_list)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if (skip_list.find(view) == skip_list.end())
+ {
+ view->popVisible();
+ }
+ }
+}
+
+//
+// LLMultiFloater
+//
+
+LLMultiFloater::LLMultiFloater() :
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(FALSE)
+{
+
+}
+
+LLMultiFloater::LLMultiFloater(LLTabContainerCommon::TabPosition tab_pos) :
+ mTabContainer(NULL),
+ mTabPos(tab_pos),
+ mAutoResize(FALSE)
+{
+
+}
+
+LLMultiFloater::LLMultiFloater(const LLString &name) :
+ LLFloater(name),
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(FALSE)
+{
+}
+
+LLMultiFloater::LLMultiFloater(
+ const LLString& name,
+ const LLRect& rect,
+ LLTabContainer::TabPosition tab_pos,
+ BOOL auto_resize) :
+ LLFloater(name, rect, name),
+ mTabContainer(NULL),
+ mTabPos(LLTabContainerCommon::TOP),
+ mAutoResize(auto_resize)
+{
+ mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);
+}
+
+LLMultiFloater::LLMultiFloater(
+ const LLString& name,
+ const LLString& rect_control,
+ LLTabContainer::TabPosition tab_pos,
+ BOOL auto_resize) :
+ LLFloater(name, rect_control, name),
+ mTabContainer(NULL),
+ mTabPos(tab_pos),
+ mAutoResize(auto_resize)
+{
+ mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable && mTabPos == LLTabContainerCommon::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);
+
+}
+
+LLMultiFloater::~LLMultiFloater()
+{
+}
+
+// virtual
+EWidgetType LLMultiFloater::getWidgetType() const
+{
+ return WIDGET_TYPE_MULTI_FLOATER;
+}
+
+// virtual
+LLString LLMultiFloater::getWidgetTag() const
+{
+ return LL_MULTI_FLOATER_TAG;
+}
+
+void LLMultiFloater::init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn)
+{
+ LLFloater::init(title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+
+ /*mTabContainer = new LLTabContainer("Preview Tabs",
+ LLRect(LLPANEL_BORDER_WIDTH, mRect.getHeight() - LLFLOATER_HEADER_SIZE, mRect.getWidth() - LLPANEL_BORDER_WIDTH, 0),
+ mTabPos,
+ NULL,
+ NULL);
+ mTabContainer->setFollowsAll();
+ if (mResizable && mTabPos == LLTabContainerCommon::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+
+ addChild(mTabContainer);*/
+}
+
+void LLMultiFloater::open()
+{
+ if (mTabContainer->getTabCount() > 0)
+ {
+ LLFloater::open();
+ }
+ else
+ {
+ // for now, don't allow multifloaters
+ // without any child floaters
+ close();
+ }
+}
+
+void LLMultiFloater::onClose(bool app_quitting)
+{
+ if(closeAllFloaters() == TRUE)
+ {
+ LLFloater::onClose(app_quitting ? true : false);
+ }//else not all tabs could be closed...
+}
+
+void LLMultiFloater::draw()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ //RN: could this potentially crash in draw hierarchy?
+ close();
+ }
+ else
+ {
+ for (S32 i = 0; i < mTabContainer->getTabCount(); i++)
+ {
+ LLFloater* floaterp = (LLFloater*)mTabContainer->getPanelByIndex(i);
+ if (floaterp->getTitle() != mTabContainer->getPanelTitle(i))
+ {
+ mTabContainer->setPanelTitle(i, floaterp->getTitle());
+ }
+ }
+ LLFloater::draw();
+ }
+}
+
+BOOL LLMultiFloater::closeAllFloaters()
+{
+ S32 tabToClose = 0;
+ S32 lastTabCount = mTabContainer->getTabCount();
+ while (tabToClose < mTabContainer->getTabCount())
+ {
+ LLFloater* first_floater = (LLFloater*)mTabContainer->getPanelByIndex(tabToClose);
+ first_floater->close();
+ if(lastTabCount == mTabContainer->getTabCount())
+ {
+ //Tab did not actually close, possibly due to a pending Save Confirmation dialog..
+ //so try and close the next one in the list...
+ tabToClose++;
+ }else
+ {
+ //Tab closed ok.
+ lastTabCount = mTabContainer->getTabCount();
+ }
+ }
+ if( mTabContainer->getTabCount() != 0 )
+ return FALSE; // Couldn't close all the tabs (pending save dialog?) so return FALSE.
+ return TRUE; //else all tabs were successfully closed...
+}
+
+void LLMultiFloater::growToFit(LLFloater* floaterp, S32 width, S32 height)
+{
+ floater_data_map_t::iterator found_data_it;
+ found_data_it = mFloaterDataMap.find(floaterp->getHandle());
+ if (found_data_it != mFloaterDataMap.end())
+ {
+ // store new width and height with this floater so that it will keep its size when detached
+ found_data_it->second.mWidth = width;
+ found_data_it->second.mHeight = height;
+
+ S32 cur_height = mRect.getHeight();
+ reshape(llmax(mRect.getWidth(), width + LLPANEL_BORDER_WIDTH * 2), llmax(mRect.getHeight(), height + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT + (LLPANEL_BORDER_WIDTH * 2)));
+
+ // make sure upper left corner doesn't move
+ translate(0, mRect.getHeight() - cur_height);
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+}
+
+/**
+ void addFloater(LLFloater* floaterp, BOOL select_added_floater)
+
+ Adds the LLFloater pointed to by floaterp to this.
+ If floaterp is already hosted by this, then it is re-added to get
+ new titles, etc.
+ If select_added_floater is true, the LLFloater pointed to by floaterp will
+ become the selected tab in this
+
+ Affects: mTabContainer, floaterp
+**/
+void LLMultiFloater::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
+{
+ if (!floaterp)
+ {
+ return;
+ }
+
+ if (!mTabContainer)
+ {
+ llerrs << "Tab Container used without having been initialized." << llendl;
+ }
+
+ if (floaterp->getHost() == this)
+ {
+ // already hosted by me, remove
+ // do this so we get updated title, etc.
+ mFloaterDataMap.erase(floaterp->getHandle());
+ mTabContainer->removeTabPanel(floaterp);
+ }
+ else if (floaterp->getHost())
+ {
+ // floaterp is hosted by somebody else and
+ // this is adding it, so remove it from it's old host
+ floaterp->getHost()->removeFloater(floaterp);
+ }
+ else if (floaterp->getParent() == gFloaterView)
+ {
+ // rehost preview floater as child panel
+ gFloaterView->removeChild(floaterp);
+ }
+
+ // store original configuration
+ LLFloaterData floater_data;
+ floater_data.mWidth = floaterp->getRect().getWidth();
+ floater_data.mHeight = floaterp->getRect().getHeight();
+ floater_data.mCanMinimize = floaterp->isMinimizeable();
+ floater_data.mCanResize = floaterp->isResizable();
+
+ // remove minimize and close buttons
+ floaterp->setCanMinimize(FALSE);
+ floaterp->setCanResize(FALSE);
+ floaterp->setCanDrag(FALSE);
+
+ S32 new_width = llmax(mRect.getWidth(), floaterp->getRect().getWidth());
+ S32 new_height = llmax(mRect.getHeight(), floaterp->getRect().getHeight() + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
+
+ reshape(new_width, new_height);
+
+ //add the panel, add it to proper maps
+ mTabContainer->addTabPanel(floaterp, floaterp->getTitle(), FALSE, onTabSelected, this, 0, FALSE, insertion_point);
+ mFloaterDataMap[floaterp->getHandle()] = floater_data;
+
+ if ( select_added_floater )
+ {
+ mTabContainer->selectLastTab();
+ // explicitly call tabopen to load preview assets, etc.
+ tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), true);
+ }
+
+ floaterp->setHost(this);
+ if (mMinimized)
+ {
+ floaterp->setVisible(FALSE);
+ }
+}
+
+/**
+ BOOL selectFloater(LLFloater* floaterp)
+
+ If the LLFloater pointed to by floaterp is hosted by this,
+ then its tab is selected and returns true. Otherwise returns false.
+
+ Affects: mTabContainer
+**/
+BOOL LLMultiFloater::selectFloater(LLFloater* floaterp)
+{
+ return mTabContainer->selectTabPanel(floaterp);
+}
+
+// virtual
+void LLMultiFloater::selectNextFloater()
+{
+ mTabContainer->selectNextTab();
+}
+
+// virtual
+void LLMultiFloater::selectPrevFloater()
+{
+ mTabContainer->selectPrevTab();
+}
+
+void LLMultiFloater::showFloater(LLFloater* floaterp)
+{
+ // we won't select a panel that already is selected
+ // it is hard to do this internally to tab container
+ // as tab selection is handled via index and the tab at a given
+ // index might have changed
+ if (floaterp != mTabContainer->getCurrentPanel() &&
+ !mTabContainer->selectTabPanel(floaterp))
+ {
+ addFloater(floaterp, TRUE);
+ }
+ setVisibleAndFrontmost();
+}
+
+void LLMultiFloater::removeFloater(LLFloater* floaterp)
+{
+ if ( floaterp->getHost() != this )
+ return;
+
+ floater_data_map_t::iterator found_data_it = mFloaterDataMap.find(floaterp->getHandle());
+ if (found_data_it != mFloaterDataMap.end())
+ {
+ LLFloaterData& floater_data = found_data_it->second;
+ floaterp->setCanMinimize(floater_data.mCanMinimize);
+ if (!floater_data.mCanResize)
+ {
+ // restore original size
+ floaterp->reshape(floater_data.mWidth, floater_data.mHeight);
+ }
+ floaterp->setCanResize(floater_data.mCanResize);
+ mFloaterDataMap.erase(found_data_it);
+ }
+ mTabContainer->removeTabPanel(floaterp);
+ floaterp->setBackgroundVisible(TRUE);
+ floaterp->setHost(NULL);
+
+ if (mAutoResize)
+ {
+ floater_data_map_t::iterator floater_it;
+ S32 new_width = 0;
+ S32 new_height = 0;
+ for (floater_it = mFloaterDataMap.begin(); floater_it != mFloaterDataMap.end(); ++floater_it)
+ {
+ new_width = llmax(new_width, floater_it->second.mWidth + LLPANEL_BORDER_WIDTH * 2);
+ new_height = llmax(new_height, floater_it->second.mHeight + LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT);
+ }
+
+ S32 cur_height = mRect.getHeight();
+
+ reshape(new_width, new_height);
+
+ // make sure upper left corner doesn't move
+ translate(0, cur_height - new_height);
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+
+ tabOpen((LLFloater*)mTabContainer->getCurrentPanel(), false);
+}
+
+void LLMultiFloater::tabOpen(LLFloater* opened_floater, bool from_click)
+{
+ // default implementation does nothing
+}
+
+void LLMultiFloater::tabClose()
+{
+ if (mTabContainer->getTabCount() == 0)
+ {
+ // no more children, close myself
+ close();
+ }
+}
+
+void LLMultiFloater::setVisible(BOOL visible)
+{
+ //FIXME: shouldn't have to do this, fix adding to minimized multifloater
+ LLFloater::setVisible(visible);
+
+ if (mTabContainer)
+ {
+ LLPanel* cur_floaterp = mTabContainer->getCurrentPanel();
+
+ if (cur_floaterp)
+ {
+ cur_floaterp->setVisible(visible);
+ }
+ }
+}
+
+BOOL LLMultiFloater::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getEnabled()
+ && mask == (MASK_CONTROL|MASK_SHIFT))
+ {
+ if (key == 'W')
+ {
+ LLFloater* floater = getActiveFloater();
+ if (floater && floater->canClose())
+ {
+ floater->close();
+ }
+ return TRUE;
+ }
+ }
+
+ return LLFloater::handleKeyHere(key, mask, called_from_parent);
+}
+
+LLFloater* LLMultiFloater::getActiveFloater()
+{
+ return (LLFloater*)mTabContainer->getCurrentPanel();
+}
+
+S32 LLMultiFloater::getFloaterCount()
+{
+ return mTabContainer->getTabCount();
+}
+
+/**
+ BOOL isFloaterFlashing(LLFloater* floaterp)
+
+ Returns true if the LLFloater pointed to by floaterp
+ is currently in a flashing state and is hosted by this.
+ False otherwise.
+
+ Requires: floaterp != NULL
+**/
+BOOL LLMultiFloater::isFloaterFlashing(LLFloater* floaterp)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ return mTabContainer->getTabPanelFlashing(floaterp);
+
+ return FALSE;
+}
+
+/**
+ BOOL setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
+
+ Sets the current flashing state of the LLFloater pointed
+ to by floaterp to be the BOOL flashing if the LLFloater pointed
+ to by floaterp is hosted by this.
+
+ Requires: floaterp != NULL
+**/
+void LLMultiFloater::setFloaterFlashing(LLFloater* floaterp, BOOL flashing)
+{
+ if ( floaterp && floaterp->getHost() == this )
+ mTabContainer->setTabPanelFlashing(floaterp, flashing);
+}
+
+//static
+void LLMultiFloater::onTabSelected(void* userdata, bool from_click)
+{
+ LLMultiFloater* floaterp = (LLMultiFloater*)userdata;
+
+ floaterp->tabOpen((LLFloater*)floaterp->mTabContainer->getCurrentPanel(), from_click);
+}
+
+void LLMultiFloater::setCanResize(BOOL can_resize)
+{
+ LLFloater::setCanResize(can_resize);
+ if (mResizable && mTabContainer->getTabPosition() == LLTabContainer::BOTTOM)
+ {
+ mTabContainer->setRightTabBtnOffset(RESIZE_HANDLE_WIDTH);
+ }
+ else
+ {
+ mTabContainer->setRightTabBtnOffset(0);
+ }
+}
+
+BOOL LLMultiFloater::postBuild()
+{
+ if (mTabContainer)
+ {
+ return TRUE;
+ }
+
+ requires("Preview Tabs", WIDGET_TYPE_TAB_CONTAINER);
+ if (checkRequirements())
+ {
+ mTabContainer = LLUICtrlFactory::getTabContainerByName(this, "Preview Tabs");
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// virtual
+LLXMLNodePtr LLFloater::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLPanel::getXML();
+
+ node->createChild("title", TRUE)->setStringValue(getTitle());
+
+ node->createChild("can_resize", TRUE)->setBoolValue(isResizable());
+
+ node->createChild("can_minimize", TRUE)->setBoolValue(isMinimizeable());
+
+ node->createChild("can_close", TRUE)->setBoolValue(isCloseable());
+
+ node->createChild("can_drag_on_left", TRUE)->setBoolValue(isDragOnLeft());
+
+ node->createChild("min_width", TRUE)->setIntValue(getMinWidth());
+
+ node->createChild("min_height", TRUE)->setIntValue(getMinHeight());
+
+ node->createChild("can_tear_off", TRUE)->setBoolValue(mCanTearOff);
+
+ return node;
+}
+
+// static
+LLView* LLFloater::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("floater");
+ node->getAttributeString("name", name);
+
+ LLFloater *floaterp = new LLFloater(name);
+
+ LLString filename;
+ node->getAttributeString("filename", filename);
+
+ if (filename.empty())
+ {
+ // Load from node
+ floaterp->initFloaterXML(node, parent, factory);
+ }
+ else
+ {
+ // Load from file
+ factory->buildFloater(floaterp, filename);
+ }
+
+ return floaterp;
+}
+
+void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory, BOOL open)
+{
+ LLString name(getName());
+ LLString title(getTitle());
+ LLString rect_control("");
+ BOOL resizable = isResizable();
+ S32 min_width = getMinWidth();
+ S32 min_height = getMinHeight();
+ BOOL drag_on_left = isDragOnLeft();
+ BOOL minimizable = isMinimizeable();
+ BOOL close_btn = isCloseable();
+ LLRect rect;
+
+ node->getAttributeString("name", name);
+ node->getAttributeString("title", title);
+ node->getAttributeString("rect_control", rect_control);
+ node->getAttributeBOOL("can_resize", resizable);
+ node->getAttributeBOOL("can_minimize", minimizable);
+ node->getAttributeBOOL("can_close", close_btn);
+ node->getAttributeBOOL("can_drag_on_left", drag_on_left);
+ node->getAttributeS32("min_width", min_width);
+ node->getAttributeS32("min_height", min_height);
+
+ if (! rect_control.empty())
+ {
+ setRectControl(rect_control);
+ }
+
+ createRect(node, rect, parent, LLRect());
+
+ setRect(rect);
+ setName(name);
+
+ init(title,
+ resizable,
+ min_width,
+ min_height,
+ drag_on_left,
+ minimizable,
+ close_btn);
+
+ BOOL can_tear_off;
+ if (node->getAttributeBOOL("can_tear_off", can_tear_off))
+ {
+ setCanTearOff(can_tear_off);
+ }
+
+ initFromXML(node, parent);
+
+ LLMultiFloater* last_host = LLFloater::getFloaterHost();
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost((LLMultiFloater*) this);
+ }
+
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ factory->createWidget(this, child);
+ }
+ if (node->hasName("multi_floater"))
+ {
+ LLFloater::setFloaterHost(last_host);
+ }
+
+
+ BOOL result = postBuild();
+
+ if (!result)
+ {
+ llerrs << "Failed to construct floater " << name << llendl;
+ }
+
+ applyRectControl();
+ if (open)
+ {
+ this->open();
+ }
+}
diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h
new file mode 100644
index 0000000000..d682c7a36a
--- /dev/null
+++ b/indra/llui/llfloater.h
@@ -0,0 +1,399 @@
+/**
+ * @file llfloater.h
+ * @brief LLFloater base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Floating "windows" within the GL display, like the inventory floater,
+// mini-map floater, etc.
+
+
+#ifndef LL_FLOATER_H
+#define LL_FLOATER_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+#include "lltabcontainer.h"
+#include <set>
+
+class LLDragHandle;
+class LLResizeHandle;
+class LLResizeBar;
+class LLButton;
+class LLMultiFloater;
+
+const S32 LLFLOATER_VPAD = 6;
+const S32 LLFLOATER_HPAD = 6;
+const S32 LLFLOATER_CLOSE_BOX_SIZE = 16;
+const S32 LLFLOATER_HEADER_SIZE = 16;
+
+const BOOL RESIZE_YES = TRUE;
+const BOOL RESIZE_NO = FALSE;
+
+const S32 DEFAULT_MIN_WIDTH = 100;
+const S32 DEFAULT_MIN_HEIGHT = 100;
+
+const BOOL DRAG_ON_TOP = FALSE;
+const BOOL DRAG_ON_LEFT = TRUE;
+
+const BOOL MINIMIZE_YES = TRUE;
+const BOOL MINIMIZE_NO = FALSE;
+
+const BOOL CLOSE_YES = TRUE;
+const BOOL CLOSE_NO = FALSE;
+
+const BOOL ADJUST_VERTICAL_YES = TRUE;
+const BOOL ADJUST_VERTICAL_NO = FALSE;
+
+
+class LLFloater : public LLPanel
+{
+friend class LLFloaterView;
+public:
+ enum EFloaterButtons
+ {
+ BUTTON_CLOSE,
+ BUTTON_RESTORE,
+ BUTTON_MINIMIZE,
+ BUTTON_TEAR_OFF,
+ BUTTON_EDIT,
+ BUTTON_COUNT
+ };
+
+ LLFloater();
+ LLFloater(const LLString& name); //simple constructor for data-driven initialization
+ LLFloater( const LLString& name, const LLRect& rect, const LLString& title,
+ BOOL resizable = FALSE,
+ S32 min_width = DEFAULT_MIN_WIDTH,
+ S32 min_height = DEFAULT_MIN_HEIGHT,
+ BOOL drag_on_left = FALSE,
+ BOOL minimizable = TRUE,
+ BOOL close_btn = TRUE,
+ BOOL bordered = BORDER_NO);
+
+ LLFloater( const LLString& name, const LLString& rect_control, const LLString& title,
+ BOOL resizable = FALSE,
+ S32 min_width = DEFAULT_MIN_WIDTH,
+ S32 min_height = DEFAULT_MIN_HEIGHT,
+ BOOL drag_on_left = FALSE,
+ BOOL minimizable = TRUE,
+ BOOL close_btn = TRUE,
+ BOOL bordered = BORDER_NO);
+
+ virtual ~LLFloater();
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void initFloaterXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory, BOOL open = TRUE);
+
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = 1);
+ /*virtual*/ void translate(S32 x, S32 y);
+ /*virtual*/ BOOL canSnapTo(LLView* other_view);
+ /*virtual*/ void snappedTo(LLView* snap_view);
+ /*virtual*/ void setFocus( BOOL b );
+ /*virtual*/ void setIsChrome(BOOL is_chrome);
+
+ // Can be called multiple times to reset floater parameters.
+ // Deletes all children of the floater.
+ virtual void init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn);
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void open();
+
+ // If allowed, close the floater cleanly, releasing focus.
+ // app_quitting is passed to onClose() below.
+ virtual void close(bool app_quitting = false);
+
+ void setAutoFocus(BOOL focus) { mAutoFocus = focus; setFocus(focus); }
+
+ // Release keyboard and mouse focus
+ void releaseFocus();
+
+ // moves to center of gFloaterView
+ void center();
+ // applies rectangle stored in mRectControl, if any
+ void applyRectControl();
+
+
+ LLMultiFloater* getHost() { return (LLMultiFloater*)LLFloater::getFloaterByHandle(mHostHandle); }
+
+ void setTitle( const LLString& title );
+ const LLString& getTitle() const;
+ virtual void setMinimized(BOOL b);
+ void moveResizeHandleToFront();
+ void addDependentFloater(LLFloater* dependent, BOOL reposition = TRUE);
+ void addDependentFloater(LLViewHandle dependent_handle, BOOL reposition = TRUE);
+ LLFloater* getDependee() { return (LLFloater*)LLFloater::getFloaterByHandle(mDependeeHandle); }
+ void removeDependentFloater(LLFloater* dependent);
+ BOOL isMinimized() { return mMinimized; }
+ BOOL isFrontmost();
+ BOOL isDependent() { return !mDependeeHandle.isDead(); }
+ void setCanMinimize(BOOL can_minimize);
+ void setCanClose(BOOL can_close);
+ void setCanTearOff(BOOL can_tear_off);
+ virtual void setCanResize(BOOL can_resize);
+ void setCanDrag(BOOL can_drag);
+ void setHost(LLMultiFloater* host);
+ BOOL isResizable() const { return mResizable; }
+ void setResizeLimits( S32 min_width, S32 min_height );
+ void getResizeLimits( S32* min_width, S32* min_height ) { *min_width = mMinWidth; *min_height = mMinHeight; }
+
+
+ bool isMinimizeable() const{ return mButtonsEnabled[BUTTON_MINIMIZE]; }
+ // Does this window have a close button, NOT can we close it right now.
+ bool isCloseable() const{ return (mButtonsEnabled[BUTTON_CLOSE] ? true : false); }
+ bool isDragOnLeft() const{ return mDragOnLeft; }
+ S32 getMinWidth() const{ return mMinWidth; }
+ S32 getMinHeight() const{ return mMinHeight; }
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ virtual void draw();
+
+ // Call destroy() to free memory, or setVisible(FALSE) to keep it
+ // If app_quitting, you might not want to save your visibility.
+ // Defaults to destroy().
+ virtual void onClose(bool app_quitting);
+
+ // Defaults to true.
+ virtual BOOL canClose();
+
+ virtual void setVisible(BOOL visible);
+ void setFrontmost(BOOL take_focus = TRUE);
+
+ // Defaults to false.
+ virtual BOOL canSaveAs();
+
+ // Defaults to no-op.
+ virtual void saveAs();
+
+ void setSnapTarget(LLViewHandle handle) { mSnappedTo = handle; }
+ void clearSnapTarget() { mSnappedTo.markDead(); }
+ LLViewHandle getSnapTarget() { return mSnappedTo; }
+
+ /*virtual*/ LLView* getRootMostFastFrameView();
+
+ static void closeByMenu(void *userdata);
+ static void onClickClose(void *userdata);
+ static void onClickMinimize(void *userdata);
+ static void onClickTearOff(void *userdata);
+ static void onClickEdit(void *userdata);
+
+ static void setFloaterHost(LLMultiFloater* hostp) {sHostp = hostp; }
+ static void setEditModeEnabled(BOOL enable);
+ static BOOL getEditModeEnabled();
+ static LLMultiFloater* getFloaterHost() {return sHostp; }
+
+ static LLFloater* getFloaterByHandle(LLViewHandle handle);
+
+protected:
+ // Don't call this directly. You probably want to call close(). JC
+ void destroy();
+ virtual void bringToFront(S32 x, S32 y);
+ virtual void setVisibleAndFrontmost(BOOL take_focus=TRUE);
+ void setForeground(BOOL b); // called only by floaterview
+ void cleanupHandles(); // remove handles to dead floaters
+ void createMinimizeButton();
+ void updateButtons();
+ void buildButtons();
+
+protected:
+// static LLViewerImage* sBackgroundImage;
+// static LLViewerImage* sShadowImage;
+
+ LLDragHandle* mDragHandle;
+ LLResizeBar* mResizeBar[4];
+ LLResizeHandle* mResizeHandle[4];
+ LLButton *mMinimizeButton;
+ BOOL mCanTearOff;
+ BOOL mMinimized;
+ LLRect mPreviousRect;
+ BOOL mForeground;
+ LLViewHandle mDependeeHandle;
+
+ BOOL mFirstLook; // TRUE if the _next_ time this floater is visible will be the first time in the session that it is visible.
+
+ BOOL mResizable;
+ S32 mMinWidth;
+ S32 mMinHeight;
+
+ BOOL mAutoFocus;
+ BOOL mEditing;
+
+ typedef std::set<LLViewHandle> handle_set_t;
+ typedef std::set<LLViewHandle>::iterator handle_set_iter_t;
+ handle_set_t mDependents;
+ bool mDragOnLeft;
+
+ BOOL mButtonsEnabled[BUTTON_COUNT];
+ LLButton* mButtons[BUTTON_COUNT];
+ F32 mButtonScale;
+
+ LLViewHandle mSnappedTo;
+
+ LLViewHandle mHostHandle;
+ LLViewHandle mLastHostHandle;
+
+ static BOOL sEditModeEnabled;
+ static LLMultiFloater* sHostp;
+ static LLString sButtonActiveImageNames[BUTTON_COUNT];
+ static LLString sButtonInactiveImageNames[BUTTON_COUNT];
+ static LLString sButtonPressedImageNames[BUTTON_COUNT];
+ static LLString sButtonNames[BUTTON_COUNT];
+ static LLString sButtonToolTips[BUTTON_COUNT];
+ typedef void (*click_callback)(void *);
+ static click_callback sButtonCallbacks[BUTTON_COUNT];
+
+ typedef std::map<LLViewHandle, LLFloater*> handle_map_t;
+ typedef std::map<LLViewHandle, LLFloater*>::iterator handle_map_iter_t;
+ static handle_map_t sFloaterMap;
+
+ std::vector<LLView*> mMinimizedHiddenChildren;
+};
+
+
+/////////////////////////////////////////////////////////////
+// LLFloaterView
+// Parent of all floating panels
+
+class LLFloaterView : public LLUICtrl
+{
+public:
+ LLFloaterView( const LLString& name, const LLRect& rect );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+ void reshape(S32 width, S32 height, BOOL called_from_parent, BOOL adjust_vertical);
+
+ /*virtual*/ void draw();
+ /*virtual*/ const LLRect getSnapRect() const;
+ void refresh();
+
+ void getNewFloaterPosition( S32* left, S32* top );
+ void resetStartingFloaterPosition();
+ LLRect findNeighboringPosition( LLFloater* reference_floater, LLFloater* neighbor );
+
+ // Given a child of gFloaterView, make sure this view can fit entirely onscreen.
+ void adjustToFitScreen(LLFloater* floater, BOOL allow_partial_outside);
+
+ void getMinimizePosition( S32 *left, S32 *bottom);
+ void restoreAll(); // un-minimize all floaters
+ typedef std::set<LLView*> skip_list_t;
+ void LLFloaterView::pushVisibleAll(BOOL visible, const skip_list_t& skip_list = skip_list_t());
+ void LLFloaterView::popVisibleAll(const skip_list_t& skip_list = skip_list_t());
+
+ void setCycleMode(BOOL mode);
+ BOOL getCycleMode();
+ void bringToFront( LLFloater* child, BOOL give_focus = TRUE );
+ void highlightFocusedFloater();
+ void unhighlightFocusedFloater();
+ void focusFrontFloater();
+ void destroyAllChildren();
+ // attempt to close all floaters
+ void closeAllChildren(bool app_quitting);
+ BOOL allChildrenClosed();
+
+ LLFloater* getFrontmost();
+ LLFloater* getBackmost();
+ LLFloater* getParentFloater(LLView* viewp);
+ LLFloater* getFocusedFloater();
+ void syncFloaterTabOrder();
+
+ // Get a floater based the handle. If this returns NULL, it is up
+ // to the caller to discard the handle.
+ LLFloater* getFloaterByHandle(LLViewHandle handle);
+
+ // Returns z order of child provided. 0 is closest, larger numbers
+ // are deeper in the screen. If there is no such child, the return
+ // value is not defined.
+ S32 getZOrder(LLFloater* child);
+
+ void setSnapOffsetBottom(S32 offset) { mSnapOffsetBottom = offset; }
+
+private:
+ S32 mColumn;
+ S32 mNextLeft;
+ S32 mNextTop;
+ BOOL mFocusCycleMode;
+ S32 mSnapOffsetBottom;
+};
+
+class LLMultiFloater : public LLFloater
+{
+public:
+ LLMultiFloater();
+ LLMultiFloater(LLTabContainerCommon::TabPosition tab_pos);
+ LLMultiFloater(const LLString& name);
+ LLMultiFloater(const LLString& name, const LLRect& rect, LLTabContainer::TabPosition tab_pos = LLTabContainer::TOP, BOOL auto_resize = FALSE);
+ LLMultiFloater(const LLString& name, const LLString& rect_control, LLTabContainer::TabPosition tab_pos = LLTabContainer::TOP, BOOL auto_resize = FALSE);
+ virtual ~LLMultiFloater();
+
+ virtual void init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn);
+
+ virtual BOOL postBuild();
+ /*virtual*/ void open();
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ void draw();
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ /*virtual*/ EWidgetType getWidgetType() const;
+ /*virtual*/ LLString getWidgetTag() const;
+
+ virtual void setCanResize(BOOL can_resize);
+ virtual void growToFit(LLFloater* floaterp, S32 width, S32 height);
+ virtual void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainerCommon::eInsertionPoint insertion_point = LLTabContainerCommon::END);
+
+ virtual void showFloater(LLFloater* floaterp);
+ virtual void removeFloater(LLFloater* floaterp);
+
+ virtual void tabOpen(LLFloater* opened_floater, bool from_click);
+ virtual void tabClose();
+
+ virtual BOOL selectFloater(LLFloater* floaterp);
+ virtual void selectNextFloater();
+ virtual void selectPrevFloater();
+
+ virtual LLFloater* getActiveFloater();
+ virtual BOOL isFloaterFlashing(LLFloater* floaterp);
+ virtual S32 getFloaterCount();
+
+ virtual void setFloaterFlashing(LLFloater* floaterp, BOOL flashing);
+ virtual BOOL closeAllFloaters(); //Returns FALSE if the floater could not be closed due to pending confirmation dialogs
+ void setTabContainer(LLTabContainerCommon* tab_container) { if (!mTabContainer) mTabContainer = tab_container; }
+ static void onTabSelected(void* userdata, bool);
+
+protected:
+ struct LLFloaterData
+ {
+ S32 mWidth;
+ S32 mHeight;
+ BOOL mCanMinimize;
+ BOOL mCanResize;
+ };
+
+ LLTabContainerCommon* mTabContainer;
+
+ typedef std::map<LLViewHandle, LLFloaterData> floater_data_map_t;
+ floater_data_map_t mFloaterDataMap;
+
+ LLTabContainerCommon::TabPosition mTabPos;
+ BOOL mAutoResize;
+};
+
+
+extern LLFloaterView* gFloaterView;
+
+#endif // LL_FLOATER_H
+
diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp
new file mode 100644
index 0000000000..030fbf0653
--- /dev/null
+++ b/indra/llui/llfocusmgr.cpp
@@ -0,0 +1,369 @@
+/**
+ * @file llfocusmgr.cpp
+ * @brief LLFocusMgr base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llfocusmgr.h"
+#include "lluictrl.h"
+#include "v4color.h"
+
+const F32 FOCUS_FADE_TIME = 0.3f;
+
+LLFocusMgr gFocusMgr;
+
+LLFocusMgr::LLFocusMgr()
+ :
+ mLockedView( NULL ),
+ mKeyboardLockedFocusLostCallback( NULL ),
+ mMouseCaptor( NULL ),
+ mMouseCaptureLostCallback( NULL ),
+ mKeyboardFocus( NULL ),
+ mDefaultKeyboardFocus( NULL ),
+ mKeyboardFocusLostCallback( NULL ),
+ mTopView( NULL ),
+ mTopViewLostCallback( NULL ),
+ mFocusWeight(0.f),
+ mAppHasFocus(TRUE) // Macs don't seem to notify us that we've gotten focus, so default to true
+ #ifdef _DEBUG
+ , mMouseCaptorName("none")
+ , mKeyboardFocusName("none")
+ , mTopViewName("none")
+ #endif
+{
+}
+
+LLFocusMgr::~LLFocusMgr()
+{
+ mFocusHistory.clear();
+}
+
+void LLFocusMgr::releaseFocusIfNeeded( LLView* view )
+{
+ if( childHasMouseCapture( view ) )
+ {
+ setMouseCapture( NULL, NULL );
+ }
+
+ if( childHasKeyboardFocus( view ))
+ {
+ if (view == mLockedView)
+ {
+ mLockedView = NULL;
+ mKeyboardLockedFocusLostCallback = NULL;
+ setKeyboardFocus( NULL, NULL );
+ }
+ else
+ {
+ setKeyboardFocus( mLockedView, mKeyboardLockedFocusLostCallback );
+ }
+ }
+
+ if( childIsTopView( view ) )
+ {
+ setTopView( NULL, NULL );
+ }
+}
+
+
+void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock)
+{
+ if (mLockedView &&
+ (new_focus == NULL ||
+ (new_focus != mLockedView && !new_focus->hasAncestor(mLockedView))))
+ {
+ // don't allow focus to go to anything that is not the locked focus
+ // or one of its descendants
+ return;
+ }
+ FocusLostCallback old_callback = mKeyboardFocusLostCallback;
+ mKeyboardFocusLostCallback = on_focus_lost;
+
+ //llinfos << "Keyboard focus handled by " << (new_focus ? new_focus->getName() : "nothing") << llendl;
+
+ if( new_focus != mKeyboardFocus )
+ {
+ LLUICtrl* old_focus = mKeyboardFocus;
+ mKeyboardFocus = new_focus;
+
+ // clear out any existing flash
+ if (new_focus)
+ {
+ mFocusWeight = 0.f;
+ }
+ mFocusTimer.reset();
+
+ if( old_callback )
+ {
+ old_callback( old_focus );
+ }
+
+ #ifdef _DEBUG
+ mKeyboardFocusName = new_focus ? new_focus->getName() : "none";
+ #endif
+
+ // If we've got a default keyboard focus, and the caller is
+ // releasing keyboard focus, move to the default.
+ if (mDefaultKeyboardFocus != NULL && new_focus == NULL)
+ {
+ mDefaultKeyboardFocus->setFocus(TRUE);
+ }
+
+ LLView* focus_subtree = new_focus;
+ LLView* viewp = new_focus;
+ // find root-most focus root
+ while(viewp)
+ {
+ if (viewp->isFocusRoot())
+ {
+ focus_subtree = viewp;
+ }
+ viewp = viewp->getParent();
+ }
+
+
+ if (focus_subtree)
+ {
+ mFocusHistory[focus_subtree->mViewHandle] = new_focus ? new_focus->mViewHandle : LLViewHandle::sDeadHandle;
+ }
+ }
+
+ if (lock)
+ {
+ mLockedView = new_focus;
+ mKeyboardLockedFocusLostCallback = on_focus_lost;
+ }
+}
+
+void LLFocusMgr::setDefaultKeyboardFocus(LLUICtrl* default_focus)
+{
+ mDefaultKeyboardFocus = default_focus;
+}
+
+// Returns TRUE is parent or any descedent of parent has keyboard focus.
+BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const
+{
+ LLView* focus_view = mKeyboardFocus;
+ while( focus_view )
+ {
+ if( focus_view == parent )
+ {
+ return TRUE;
+ }
+ focus_view = focus_view->getParent();
+ }
+ return FALSE;
+}
+
+// Returns TRUE is parent or any descedent of parent is the mouse captor.
+BOOL LLFocusMgr::childHasMouseCapture( LLView* parent )
+{
+ if( mMouseCaptor && mMouseCaptor->isView() )
+ {
+ LLView* captor_view = (LLView*)mMouseCaptor;
+ while( captor_view )
+ {
+ if( captor_view == parent )
+ {
+ return TRUE;
+ }
+ captor_view = captor_view->getParent();
+ }
+ }
+ return FALSE;
+}
+
+void LLFocusMgr::removeKeyboardFocusWithoutCallback( LLView* focus )
+{
+ // should be ok to unlock here, as you have to know the locked view
+ // in order to unlock it
+ if (focus == mLockedView)
+ {
+ mLockedView = NULL;
+ mKeyboardLockedFocusLostCallback = NULL;
+ }
+
+ if( mKeyboardFocus == focus )
+ {
+ mKeyboardFocus = NULL;
+ mKeyboardFocusLostCallback = NULL;
+ #ifdef _DEBUG
+ mKeyboardFocusName = "none";
+ #endif
+ }
+}
+
+
+void LLFocusMgr::setMouseCapture( LLMouseHandler* new_captor, void (*on_capture_lost)(LLMouseHandler* old_captor) )
+{
+ //if (mFocusLocked)
+ //{
+ // return;
+ //}
+
+ void (*old_callback)(LLMouseHandler*) = mMouseCaptureLostCallback;
+ mMouseCaptureLostCallback = on_capture_lost;
+
+ if( new_captor != mMouseCaptor )
+ {
+ LLMouseHandler* old_captor = mMouseCaptor;
+ mMouseCaptor = new_captor;
+ /*
+ if (new_captor)
+ {
+ if ( new_captor->getName() == "Stickto")
+ {
+ llinfos << "New mouse captor: " << new_captor->getName() << llendl;
+ }
+ else
+ {
+ llinfos << "New mouse captor: " << new_captor->getName() << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "New mouse captor: NULL" << llendl;
+ }
+ */
+
+ if( old_callback )
+ {
+ old_callback( old_captor );
+ }
+
+ #ifdef _DEBUG
+ mMouseCaptorName = new_captor ? new_captor->getName() : "none";
+ #endif
+ }
+}
+
+void LLFocusMgr::removeMouseCaptureWithoutCallback( LLMouseHandler* captor )
+{
+ //if (mFocusLocked)
+ //{
+ // return;
+ //}
+ if( mMouseCaptor == captor )
+ {
+ mMouseCaptor = NULL;
+ mMouseCaptureLostCallback = NULL;
+ #ifdef _DEBUG
+ mMouseCaptorName = "none";
+ #endif
+ }
+}
+
+
+BOOL LLFocusMgr::childIsTopView( LLView* parent )
+{
+ LLView* top_view = mTopView;
+ while( top_view )
+ {
+ if( top_view == parent )
+ {
+ return TRUE;
+ }
+ top_view = top_view->getParent();
+ }
+ return FALSE;
+}
+
+
+
+// set new_top = NULL to release top_view.
+void LLFocusMgr::setTopView( LLView* new_top, void (*on_top_lost)(LLView* old_top) )
+{
+ void (*old_callback)(LLView*) = mTopViewLostCallback;
+ mTopViewLostCallback = on_top_lost;
+
+ if( new_top != mTopView )
+ {
+ LLView* old_top = mTopView;
+ mTopView = new_top;
+ if( old_callback )
+ {
+ old_callback( old_top );
+ }
+
+ mTopView = new_top;
+
+ #ifdef _DEBUG
+ mTopViewName = new_top ? new_top->getName() : "none";
+ #endif
+ }
+}
+
+void LLFocusMgr::removeTopViewWithoutCallback( LLView* top_view )
+{
+ if( mTopView == top_view )
+ {
+ mTopView = NULL;
+ mTopViewLostCallback = NULL;
+ #ifdef _DEBUG
+ mTopViewName = "none";
+ #endif
+ }
+}
+
+void LLFocusMgr::unlockFocus()
+{
+ mLockedView = NULL;
+ mKeyboardLockedFocusLostCallback = NULL;
+}
+
+F32 LLFocusMgr::getFocusFlashAmt()
+{
+ return clamp_rescale(getFocusTime(), 0.f, FOCUS_FADE_TIME, mFocusWeight, 0.f);
+}
+
+LLColor4 LLFocusMgr::getFocusColor()
+{
+ LLColor4 focus_color = lerp(LLUI::sColorsGroup->getColor( "FocusColor" ), LLColor4::white, getFocusFlashAmt());
+ // de-emphasize keyboard focus when app has lost focus (to avoid typing into wrong window problem)
+ if (!mAppHasFocus)
+ {
+ focus_color.mV[VALPHA] *= 0.4f;
+ }
+ return focus_color;
+}
+
+void LLFocusMgr::triggerFocusFlash()
+{
+ mFocusTimer.reset();
+ mFocusWeight = 1.f;
+}
+
+void LLFocusMgr::setAppHasFocus(BOOL focus)
+{
+ if (!mAppHasFocus && focus)
+ {
+ triggerFocusFlash();
+ }
+ mAppHasFocus = focus;
+}
+
+LLUICtrl* LLFocusMgr::getLastFocusForGroup(LLView* subtree_root)
+{
+ if (subtree_root)
+ {
+ focus_history_map_t::iterator found_it = mFocusHistory.find(subtree_root->mViewHandle);
+ if (found_it != mFocusHistory.end())
+ {
+ // found last focus for this subtree
+ return static_cast<LLUICtrl*>(LLView::getViewByHandle(found_it->second));
+ }
+ }
+ return NULL;
+}
+
+void LLFocusMgr::clearLastFocusForGroup(LLView* subtree_root)
+{
+ if (subtree_root)
+ {
+ mFocusHistory.erase(subtree_root->mViewHandle);
+ }
+}
diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h
new file mode 100644
index 0000000000..cb555fca91
--- /dev/null
+++ b/indra/llui/llfocusmgr.h
@@ -0,0 +1,102 @@
+/**
+ * @file llfocusmgr.h
+ * @brief LLFocusMgr base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Singleton that manages keyboard and mouse focus
+
+#ifndef LL_LLFOCUSMGR_H
+#define LL_LLFOCUSMGR_H
+
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llview.h"
+
+class LLUICtrl;
+class LLMouseHandler;
+
+class LLFocusMgr
+{
+public:
+ typedef void (*FocusLostCallback)(LLUICtrl*);
+
+ LLFocusMgr();
+ ~LLFocusMgr();
+
+ // Mouse Captor
+ void setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor)); // new_captor = NULL to release the mouse.
+ LLMouseHandler* getMouseCapture() { return mMouseCaptor; }
+ void removeMouseCaptureWithoutCallback( LLMouseHandler* captor );
+ BOOL childHasMouseCapture( LLView* parent );
+
+ // Keyboard Focus
+ void setKeyboardFocus(LLUICtrl* new_focus, FocusLostCallback on_focus_lost, BOOL lock = FALSE); // new_focus = NULL to release the focus.
+ LLUICtrl* getKeyboardFocus() const { return mKeyboardFocus; }
+ BOOL childHasKeyboardFocus( const LLView* parent ) const;
+ void removeKeyboardFocusWithoutCallback( LLView* focus );
+ FocusLostCallback getFocusCallback() { return mKeyboardFocusLostCallback; }
+ F32 getFocusTime() const { return mFocusTimer.getElapsedTimeF32(); }
+ F32 getFocusFlashAmt();
+ LLColor4 getFocusColor();
+ void triggerFocusFlash();
+ BOOL getAppHasFocus() { return mAppHasFocus; }
+ void setAppHasFocus(BOOL focus);
+ LLUICtrl* getLastFocusForGroup(LLView* subtree_root);
+ void clearLastFocusForGroup(LLView* subtree_root);
+
+ // If setKeyboardFocus(NULL) is called, and there is a non-NULL default
+ // keyboard focus view, focus goes there. JC
+ void setDefaultKeyboardFocus(LLUICtrl* default_focus);
+ LLUICtrl* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; }
+
+
+ // Top View
+ void setTopView(LLView* new_top, void (*on_top_lost)(LLView* old_top));
+ LLView* getTopView() const { return mTopView; }
+ void removeTopViewWithoutCallback( LLView* top_view );
+ BOOL childIsTopView( LLView* parent );
+
+ // All Three
+ void releaseFocusIfNeeded( LLView* top_view );
+ void unlockFocus();
+ BOOL focusLocked() { return mLockedView != NULL; }
+
+protected:
+ LLUICtrl* mLockedView;
+ FocusLostCallback mKeyboardLockedFocusLostCallback;
+
+ // Mouse Captor
+ LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object
+ void (*mMouseCaptureLostCallback)(LLMouseHandler*); // The object to which mouse events are routed is called before another object takes its place
+
+ // Keyboard Focus
+ LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object
+ LLUICtrl* mDefaultKeyboardFocus;
+ FocusLostCallback mKeyboardFocusLostCallback; // The object to which keyboard events are routed is called before another object takes its place
+
+ // Top View
+ LLView* mTopView;
+ void (*mTopViewLostCallback)(LLView*);
+
+ LLFrameTimer mFocusTimer;
+ F32 mFocusWeight;
+
+ BOOL mAppHasFocus;
+
+ typedef std::map<LLViewHandle, LLViewHandle> focus_history_map_t;
+ focus_history_map_t mFocusHistory;
+
+ #ifdef _DEBUG
+ LLString mMouseCaptorName;
+ LLString mKeyboardFocusName;
+ LLString mTopViewName;
+ #endif
+};
+
+extern LLFocusMgr gFocusMgr;
+
+#endif // LL_LLFOCUSMGR_H
+
diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp
new file mode 100644
index 0000000000..006334fa4e
--- /dev/null
+++ b/indra/llui/lliconctrl.cpp
@@ -0,0 +1,139 @@
+/**
+ * @file lliconctrl.cpp
+ * @brief LLIconCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lliconctrl.h"
+
+// Linden library includes
+
+// Project includes
+#include "llcontrol.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+
+const F32 RESOLUTION_BUMP = 1.f;
+
+LLIconCtrl::LLIconCtrl(const LLString& name, const LLRect &rect, const LLUUID &image_id)
+: LLUICtrl(name,
+ rect,
+ FALSE, // mouse opaque
+ NULL, NULL,
+ FOLLOWS_LEFT | FOLLOWS_TOP),
+ mColor( LLColor4::white ),
+ mImageName("")
+{
+ setImage( image_id );
+ setTabStop(FALSE);
+}
+
+LLIconCtrl::LLIconCtrl(const LLString& name, const LLRect &rect, const LLString &image_name)
+: LLUICtrl(name,
+ rect,
+ FALSE, // mouse opaque
+ NULL, NULL,
+ FOLLOWS_LEFT | FOLLOWS_TOP),
+ mColor( LLColor4::white ),
+ mImageName(image_name)
+{
+ LLUUID image_id;
+ image_id.set(LLUI::sAssetsGroup->getString( image_name ));
+ setImage( image_id );
+ setTabStop(FALSE);
+}
+
+
+LLIconCtrl::~LLIconCtrl()
+{
+ mImagep = NULL;
+}
+
+
+void LLIconCtrl::setImage(const LLUUID &image_id)
+{
+ mImageID = image_id;
+ mImagep = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+
+void LLIconCtrl::draw()
+{
+ if( getVisible() )
+ {
+ // Border
+ BOOL has_image = !mImageID.isNull();
+
+ if( has_image )
+ {
+ if( mImagep.notNull() )
+ {
+ gl_draw_scaled_image(0, 0,
+ mRect.getWidth(), mRect.getHeight(),
+ mImagep,
+ mColor );
+ }
+ }
+
+ LLUICtrl::draw();
+ }
+}
+
+// virtual
+void LLIconCtrl::setValue(const LLSD& value )
+{
+ setImage(value.asUUID());
+}
+
+// virtual
+LLSD LLIconCtrl::getValue() const
+{
+ LLSD ret = getImage();
+ return ret;
+}
+
+// virtual
+LLXMLNodePtr LLIconCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ if (mImageName != "")
+ {
+ node->createChild("image_name", TRUE)->setStringValue(mImageName);
+ }
+
+ node->createChild("color", TRUE)->setFloatValue(4, mColor.mV);
+
+ return node;
+}
+
+LLView* LLIconCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("icon");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLUUID image_id;
+ if (node->hasAttribute("image_name"))
+ {
+ LLString image_name;
+ node->getAttributeString("image_name", image_name);
+ image_id.set(LLUI::sAssetsGroup->getString( image_name ));
+ }
+
+ LLColor4 color(LLColor4::white);
+ LLUICtrlFactory::getAttributeColor(node,"color", color);
+
+ LLIconCtrl* icon = new LLIconCtrl(name, rect, image_id);
+ icon->setColor(color);
+
+ icon->initFromXML(node, parent);
+
+ return icon;
+}
diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h
new file mode 100644
index 0000000000..ea762982a2
--- /dev/null
+++ b/indra/llui/lliconctrl.h
@@ -0,0 +1,55 @@
+/**
+ * @file lliconctrl.h
+ * @brief LLIconCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLICONCTRL_H
+#define LL_LLICONCTRL_H
+
+#include "lluuid.h"
+#include "v4color.h"
+#include "lluictrl.h"
+#include "stdenums.h"
+#include "llimagegl.h"
+
+class LLTextBox;
+
+//
+// Classes
+//
+class LLIconCtrl
+: public LLUICtrl
+{
+public:
+ LLIconCtrl(const LLString& name, const LLRect &rect, const LLUUID &image_id);
+ LLIconCtrl(const LLString& name, const LLRect &rect, const LLString &image_name);
+ virtual ~LLIconCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_ICON; }
+ virtual LLString getWidgetTag() const { return LL_ICON_CTRL_TAG; }
+
+ // llview overrides
+ virtual void draw();
+
+ void setImage(const LLUUID &image_id);
+ const LLUUID &getImage() const { return mImageID; }
+
+ // Takes a UUID, wraps get/setImage
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ void setColor(const LLColor4& color) { mColor = color; }
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+protected:
+ LLColor4 mColor;
+ LLString mImageName;
+ LLUUID mImageID;
+ LLPointer<LLImageGL> mImagep;
+};
+
+#endif
diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp
new file mode 100644
index 0000000000..e8628c9374
--- /dev/null
+++ b/indra/llui/llkeywords.cpp
@@ -0,0 +1,502 @@
+/**
+ * @file llkeywords.cpp
+ * @brief Keyword list for LSL
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <fstream>
+
+#include "llkeywords.h"
+#include "lltexteditor.h"
+#include "llstl.h"
+#include <boost/tokenizer.hpp>
+
+const U32 KEYWORD_FILE_CURRENT_VERSION = 2;
+
+inline BOOL LLKeywordToken::isHead(const llwchar* s)
+{
+ // strncmp is much faster than string compare
+ BOOL res = TRUE;
+ const llwchar* t = mToken.c_str();
+ S32 len = mToken.size();
+ for (S32 i=0; i<len; i++)
+ {
+ if (s[i] != t[i])
+ {
+ res = FALSE;
+ break;
+ }
+ }
+ return res;
+}
+
+LLKeywords::LLKeywords() : mLoaded(FALSE)
+{
+}
+
+LLKeywords::~LLKeywords()
+{
+ std::for_each(mWordTokenMap.begin(), mWordTokenMap.end(), DeletePairedPointer());
+ std::for_each(mLineTokenList.begin(), mLineTokenList.end(), DeletePointer());
+ std::for_each(mDelimiterTokenList.begin(), mDelimiterTokenList.end(), DeletePointer());
+}
+
+BOOL LLKeywords::loadFromFile( const LLString& filename )
+{
+ mLoaded = FALSE;
+
+ ////////////////////////////////////////////////////////////
+ // File header
+
+ const S32 BUFFER_SIZE = 1024;
+ char buffer[BUFFER_SIZE];
+
+ llifstream file;
+ file.open(filename.c_str());
+ if( file.fail() )
+ {
+ llinfos << "LLKeywords::loadFromFile() Unable to open file: " << filename << llendl;
+ return mLoaded;
+ }
+
+ // Identifying string
+ file >> buffer;
+ if( strcmp( buffer, "llkeywords" ) )
+ {
+ llinfos << filename << " does not appear to be a keyword file" << llendl;
+ return mLoaded;
+ }
+
+ // Check file version
+ file >> buffer;
+ U32 version_num;
+ file >> version_num;
+ if( strcmp(buffer, "version") || version_num != (U32)KEYWORD_FILE_CURRENT_VERSION )
+ {
+ llinfos << filename << " does not appear to be a version " << KEYWORD_FILE_CURRENT_VERSION << " keyword file" << llendl;
+ return mLoaded;
+ }
+
+ // start of line (SOL)
+ const char SOL_COMMENT[] = "#";
+ const char SOL_WORD[] = "[word ";
+ const char SOL_LINE[] = "[line ";
+ const char SOL_ONE_SIDED_DELIMITER[] = "[one_sided_delimiter ";
+ const char SOL_TWO_SIDED_DELIMITER[] = "[two_sided_delimiter ";
+
+ LLColor3 cur_color( 1, 0, 0 );
+ LLKeywordToken::TOKEN_TYPE cur_type = LLKeywordToken::WORD;
+
+ while (!file.eof())
+ {
+ file.getline( buffer, BUFFER_SIZE );
+ if( !strncmp( buffer, SOL_COMMENT, strlen(SOL_COMMENT) ) )
+ {
+ continue;
+ }
+ else
+ if( !strncmp( buffer, SOL_WORD, strlen(SOL_WORD) ) )
+ {
+ cur_color = readColor( buffer + strlen(SOL_WORD) );
+ cur_type = LLKeywordToken::WORD;
+ continue;
+ }
+ else
+ if( !strncmp( buffer, SOL_LINE, strlen(SOL_LINE) ) )
+ {
+ cur_color = readColor( buffer + strlen(SOL_LINE) );
+ cur_type = LLKeywordToken::LINE;
+ continue;
+ }
+ else
+ if( !strncmp( buffer, SOL_TWO_SIDED_DELIMITER, strlen(SOL_TWO_SIDED_DELIMITER) ) )
+ {
+ cur_color = readColor( buffer + strlen(SOL_TWO_SIDED_DELIMITER) );
+ cur_type = LLKeywordToken::TWO_SIDED_DELIMITER;
+ continue;
+ }
+ if( !strncmp( buffer, SOL_ONE_SIDED_DELIMITER, strlen(SOL_ONE_SIDED_DELIMITER) ) )
+ {
+ cur_color = readColor( buffer + strlen(SOL_ONE_SIDED_DELIMITER) );
+ cur_type = LLKeywordToken::ONE_SIDED_DELIMITER;
+ continue;
+ }
+
+ LLString token_buffer( buffer );
+ LLString::trim(token_buffer);
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep_word("", " \t");
+ tokenizer word_tokens(token_buffer, sep_word);
+ tokenizer::iterator token_word_iter = word_tokens.begin();
+
+ if( !token_buffer.empty() && token_word_iter != word_tokens.end() )
+ {
+ // first word is keyword
+ LLString keyword = (*token_word_iter);
+ LLString::trim(keyword);
+
+ // following words are tooltip
+ LLString tool_tip;
+ while (++token_word_iter != word_tokens.end())
+ {
+ tool_tip += (*token_word_iter);
+ }
+ LLString::trim(tool_tip);
+
+ if( !tool_tip.empty() )
+ {
+ // Replace : with \n for multi-line tool tips.
+ LLString::replaceChar( tool_tip, ':', '\n' );
+ addToken(cur_type, keyword, cur_color, tool_tip );
+ }
+ else
+ {
+ addToken(cur_type, keyword, cur_color, NULL );
+ }
+ }
+ }
+
+ file.close();
+
+ mLoaded = TRUE;
+ return mLoaded;
+}
+
+// Add the token as described
+void LLKeywords::addToken(LLKeywordToken::TOKEN_TYPE type,
+ const LLString& key_in,
+ const LLColor3& color,
+ const LLString& tool_tip_in )
+{
+ LLWString key = utf8str_to_wstring(key_in);
+ LLWString tool_tip = utf8str_to_wstring(tool_tip_in);
+ switch(type)
+ {
+ case LLKeywordToken::WORD:
+ mWordTokenMap[key] = new LLKeywordToken(type, color, key, tool_tip);
+ break;
+
+ case LLKeywordToken::LINE:
+ mLineTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
+ break;
+
+ case LLKeywordToken::TWO_SIDED_DELIMITER:
+ case LLKeywordToken::ONE_SIDED_DELIMITER:
+ mDelimiterTokenList.push_front(new LLKeywordToken(type, color, key, tool_tip));
+ break;
+
+ default:
+ llassert(0);
+ }
+}
+
+LLColor3 LLKeywords::readColor( const LLString& s )
+{
+ F32 r, g, b;
+ r = g = b = 0.0f;
+ S32 read = sscanf(s.c_str(), "%f, %f, %f]", &r, &g, &b );
+ if( read != 3 )
+ {
+ llinfos << " poorly formed color in keyword file" << llendl;
+ }
+ return LLColor3( r, g, b );
+}
+
+// Walk through a string, applying the rules specified by the keyword token list and
+// create a list of color segments.
+void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWString& wtext)
+{
+ std::for_each(seg_list->begin(), seg_list->end(), DeletePointer());
+ seg_list->clear();
+
+ if( wtext.empty() )
+ {
+ return;
+ }
+
+ S32 text_len = wtext.size();
+
+ seg_list->push_back( new LLTextSegment( LLColor3(0,0,0), 0, text_len ) );
+
+ const llwchar* base = wtext.c_str();
+ const llwchar* cur = base;
+ const llwchar* line = NULL;
+
+ while( *cur )
+ {
+ if( *cur == '\n' || cur == base )
+ {
+ if( *cur == '\n' )
+ {
+ cur++;
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+ }
+
+ // Start of a new line
+ line = cur;
+
+ // Skip white space
+ while( *cur && isspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+ if( !*cur || *cur == '\n' )
+ {
+ continue;
+ }
+
+ // cur is now at the first non-whitespace character of a new line
+
+ // Line start tokens
+ {
+ BOOL line_done = FALSE;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* cur_token = *iter;
+ if( cur_token->isHead( cur ) )
+ {
+ S32 seg_start = cur - base;
+ while( *cur && *cur != '\n' )
+ {
+ // skip the rest of the line
+ cur++;
+ }
+ S32 seg_end = cur - base;
+
+ //llinfos << "Seg: [" << (char*)LLString( base, seg_start, seg_end-seg_start) << "]" << llendl;
+ LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len);
+ line_done = TRUE; // to break out of second loop.
+ break;
+ }
+ }
+
+ if( line_done )
+ {
+ continue;
+ }
+ }
+ }
+
+ // Skip white space
+ while( *cur && isspace(*cur) && (*cur != '\n') )
+ {
+ cur++;
+ }
+
+ while( *cur && *cur != '\n' )
+ {
+ // Check against delimiters
+ {
+ S32 seg_start = 0;
+ LLKeywordToken* cur_delimiter = NULL;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter = *iter;
+ if( delimiter->isHead( cur ) )
+ {
+ cur_delimiter = delimiter;
+ break;
+ }
+ }
+
+ if( cur_delimiter )
+ {
+ S32 between_delimiters = 0;
+ S32 seg_end = 0;
+
+ seg_start = cur - base;
+ cur += cur_delimiter->getLength();
+
+ if( cur_delimiter->getType() == LLKeywordToken::TWO_SIDED_DELIMITER )
+ {
+ while( *cur && !cur_delimiter->isHead(cur))
+ {
+ // Check for an escape sequence.
+ if (*cur == '\\')
+ {
+ // Count the number of backslashes.
+ S32 num_backslashes = 0;
+ while (*cur == '\\')
+ {
+ num_backslashes++;
+ between_delimiters++;
+ cur++;
+ }
+ // Is the next character the end delimiter?
+ if (cur_delimiter->isHead(cur))
+ {
+ // Is there was an odd number of backslashes, then this delimiter
+ // does not end the sequence.
+ if (num_backslashes % 2 == 1)
+ {
+ between_delimiters++;
+ cur++;
+ }
+ else
+ {
+ // This is an end delimiter.
+ break;
+ }
+ }
+ }
+ else
+ {
+ between_delimiters++;
+ cur++;
+ }
+ }
+
+ if( *cur )
+ {
+ cur += cur_delimiter->getLength();
+ seg_end = seg_start + between_delimiters + 2 * cur_delimiter->getLength();
+ }
+ else
+ {
+ // eof
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
+ }
+ }
+ else
+ {
+ llassert( cur_delimiter->getType() == LLKeywordToken::ONE_SIDED_DELIMITER );
+ // Left side is the delimiter. Right side is eol or eof.
+ while( *cur && ('\n' != *cur) )
+ {
+ between_delimiters++;
+ cur++;
+ }
+ seg_end = seg_start + between_delimiters + cur_delimiter->getLength();
+ }
+
+
+ //llinfos << "Seg: [" << (char*)LLString( base, seg_start, seg_end-seg_start ) << "]" << llendl;
+ LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end );
+ text_segment->setToken( cur_delimiter );
+ insertSegment( seg_list, text_segment, text_len);
+
+ // Note: we don't increment cur, since the end of one delimited seg may be immediately
+ // followed by the start of another one.
+ continue;
+ }
+ }
+
+ // check against words
+ llwchar prev = cur > base ? *(cur-1) : 0;
+ if( !isalnum( prev ) && (prev != '_') )
+ {
+ const llwchar* p = cur;
+ while( isalnum( *p ) || (*p == '_') )
+ {
+ p++;
+ }
+ S32 seg_len = p - cur;
+ if( seg_len > 0 )
+ {
+ LLWString word( cur, 0, seg_len );
+ word_token_map_t::iterator map_iter = mWordTokenMap.find(word);
+ if( map_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* cur_token = map_iter->second;
+ S32 seg_start = cur - base;
+ S32 seg_end = seg_start + seg_len;
+
+ // llinfos << "Seg: [" << word.c_str() << "]" << llendl;
+
+
+ LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end );
+ text_segment->setToken( cur_token );
+ insertSegment( seg_list, text_segment, text_len);
+ }
+ cur += seg_len;
+ continue;
+ }
+ }
+
+ if( *cur && *cur != '\n' )
+ {
+ cur++;
+ }
+ }
+ }
+}
+
+void LLKeywords::insertSegment(std::vector<LLTextSegment*>* seg_list, LLTextSegment* new_segment, S32 text_len )
+{
+ LLTextSegment* last = seg_list->back();
+ S32 new_seg_end = new_segment->getEnd();
+
+ if( new_segment->getStart() == last->getStart() )
+ {
+ *last = *new_segment;
+ delete new_segment;
+ }
+ else
+ {
+ last->setEnd( new_segment->getStart() );
+ seg_list->push_back( new_segment );
+ }
+
+ if( new_seg_end < text_len )
+ {
+ seg_list->push_back( new LLTextSegment( LLColor3(0,0,0), new_seg_end, text_len ) );
+ }
+}
+
+#ifdef _DEBUG
+void LLKeywords::dump()
+{
+ llinfos << "LLKeywords" << llendl;
+
+
+ llinfos << "LLKeywords::sWordTokenMap" << llendl;
+ word_token_map_t::iterator word_token_iter = mWordTokenMap.begin();
+ while( word_token_iter != mWordTokenMap.end() )
+ {
+ LLKeywordToken* word_token = word_token_iter->second;
+ word_token->dump();
+ ++word_token_iter;
+ }
+
+ llinfos << "LLKeywords::sLineTokenList" << llendl;
+ for (token_list_t::iterator iter = mLineTokenList.begin();
+ iter != mLineTokenList.end(); ++iter)
+ {
+ LLKeywordToken* line_token = *iter;
+ line_token->dump();
+ }
+
+
+ llinfos << "LLKeywords::sDelimiterTokenList" << llendl;
+ for (token_list_t::iterator iter = mDelimiterTokenList.begin();
+ iter != mDelimiterTokenList.end(); ++iter)
+ {
+ LLKeywordToken* delimiter_token = *iter;
+ delimiter_token->dump();
+ }
+}
+
+void LLKeywordToken::dump()
+{
+ llinfos << "[" <<
+ mColor.mV[VX] << ", " <<
+ mColor.mV[VY] << ", " <<
+ mColor.mV[VZ] << "] [" <<
+ mToken.c_str() << "]" <<
+ llendl;
+}
+
+#endif // DEBUG
diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h
new file mode 100644
index 0000000000..fcf70b77b1
--- /dev/null
+++ b/indra/llui/llkeywords.h
@@ -0,0 +1,91 @@
+/**
+ * @file llkeywords.h
+ * @brief Keyword list for LSL
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYWORDS_H
+#define LL_LLKEYWORDS_H
+
+
+#include "llstring.h"
+#include "v3color.h"
+#include <map>
+#include <list>
+#include <deque>
+
+class LLTextSegment;
+
+
+class LLKeywordToken
+{
+public:
+ enum TOKEN_TYPE { WORD, LINE, TWO_SIDED_DELIMITER, ONE_SIDED_DELIMITER };
+
+ LLKeywordToken( TOKEN_TYPE type, const LLColor3& color, const LLWString& token, const LLWString& tool_tip )
+ :
+ mType( type ),
+ mToken( token ),
+ mColor( color ),
+ mToolTip( tool_tip )
+ {
+ }
+
+ S32 getLength() { return mToken.size(); }
+ BOOL isHead(const llwchar* s);
+ const LLColor3& getColor() { return mColor; }
+ TOKEN_TYPE getType() { return mType; }
+ const LLWString& getToolTip() { return mToolTip; }
+
+#ifdef _DEBUG
+ void dump();
+#endif
+
+private:
+ TOKEN_TYPE mType;
+public:
+ LLWString mToken;
+ LLColor3 mColor;
+private:
+ LLWString mToolTip;
+};
+
+class LLKeywords
+{
+public:
+ LLKeywords();
+ ~LLKeywords();
+
+ BOOL loadFromFile(const LLString& filename);
+ BOOL isLoaded() { return mLoaded; }
+
+ void findSegments(std::vector<LLTextSegment *> *seg_list, const LLWString& text );
+
+#ifdef _DEBUG
+ void dump();
+#endif
+
+ // Add the token as described
+ void addToken(LLKeywordToken::TOKEN_TYPE type,
+ const LLString& key,
+ const LLColor3& color,
+ const LLString& tool_tip = LLString::null);
+
+private:
+ LLColor3 readColor(const LLString& s);
+ void insertSegment(std::vector<LLTextSegment *> *seg_list, LLTextSegment* new_segment, S32 text_len);
+
+private:
+ BOOL mLoaded;
+public:
+ typedef std::map<LLWString, LLKeywordToken*> word_token_map_t;
+ word_token_map_t mWordTokenMap;
+private:
+ typedef std::deque<LLKeywordToken*> token_list_t;
+ token_list_t mLineTokenList;
+ token_list_t mDelimiterTokenList;
+};
+
+#endif // LL_LLKEYWORDS_H
diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp
new file mode 100644
index 0000000000..41049fdf1f
--- /dev/null
+++ b/indra/llui/lllineeditor.cpp
@@ -0,0 +1,2301 @@
+/**
+ * @file lllineeditor.cpp
+ * @brief LLLineEditor base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Text editor widget to let users enter a single line.
+
+#include "linden_common.h"
+
+#include "lllineeditor.h"
+
+#include "audioengine.h"
+#include "llmath.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+
+//#include "llclipboard.h"
+#include "llcontrol.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h"
+#include "llrect.h"
+#include "llresmgr.h"
+#include "llstring.h"
+#include "llwindow.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llclipboard.h"
+
+//
+// Imported globals
+//
+
+//
+// Constants
+//
+
+const S32 UI_LINEEDITOR_CURSOR_THICKNESS = 2;
+const S32 UI_LINEEDITOR_H_PAD = 2;
+const S32 UI_LINEEDITOR_V_PAD = 1;
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+const S32 SCROLL_INCREMENT_ADD = 0; // make space for typing
+const S32 SCROLL_INCREMENT_DEL = 4; // make space for baskspacing
+const F32 AUTO_SCROLL_TIME = 0.05f;
+const F32 LABEL_HPAD = 5.f;
+
+// This is a friend class of and is only used by LLLineEditor
+class LLLineEditorRollback
+{
+public:
+ LLLineEditorRollback( LLLineEditor* ed )
+ :
+ mCursorPos( ed->mCursorPos ),
+ mScrollHPos( ed->mScrollHPos ),
+ mIsSelecting( ed->mIsSelecting ),
+ mSelectionStart( ed->mSelectionStart ),
+ mSelectionEnd( ed->mSelectionEnd )
+ {
+ mText = ed->getText();
+ }
+
+ void doRollback( LLLineEditor* ed )
+ {
+ ed->mCursorPos = mCursorPos;
+ ed->mScrollHPos = mScrollHPos;
+ ed->mIsSelecting = mIsSelecting;
+ ed->mSelectionStart = mSelectionStart;
+ ed->mSelectionEnd = mSelectionEnd;
+ ed->mText = mText;
+ }
+
+ LLString getText() { return mText; }
+
+private:
+ LLString mText;
+ S32 mCursorPos;
+ S32 mScrollHPos;
+ BOOL mIsSelecting;
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+};
+
+
+//
+// Member functions
+//
+
+LLLineEditor::LLLineEditor(const LLString& name, const LLRect& rect,
+ const LLString& default_text, const LLFontGL* font,
+ S32 max_length_bytes,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data ),
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data ),
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data ),
+ void* userdata,
+ LLLinePrevalidateFunc prevalidate_func,
+ LLViewBorder::EBevel border_bevel,
+ LLViewBorder::EStyle border_style,
+ S32 border_thickness)
+ :
+ LLUICtrl( name, rect, TRUE, commit_callback, userdata, FOLLOWS_TOP | FOLLOWS_LEFT ),
+ mMaxLengthChars(max_length_bytes),
+ mMaxLengthBytes(max_length_bytes),
+ mCursorPos( 0 ),
+ mScrollHPos( 0 ),
+ mBorderLeft(0),
+ mBorderRight(0),
+ mCommitOnFocusLost( TRUE ),
+ mKeystrokeCallback( keystroke_callback ),
+ mFocusLostCallback( focus_lost_callback ),
+ mIsSelecting( FALSE ),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mLastSelectionX(-1),
+ mLastSelectionY(-1),
+ mPrevalidateFunc( prevalidate_func ),
+ mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ),
+ mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ),
+ mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ),
+ mTentativeFgColor( LLUI::sColorsGroup->getColor( "TextFgTentativeColor" ) ),
+ mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ),
+ mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ),
+ mFocusBgColor( LLUI::sColorsGroup->getColor( "TextBgFocusColor" ) ),
+ mBorderThickness( border_thickness ),
+ mIgnoreArrowKeys( FALSE ),
+ mIgnoreTab( TRUE ),
+ mDrawAsterixes( FALSE ),
+ mHandleEditKeysDirectly( FALSE ),
+ mSelectAllonFocusReceived( FALSE ),
+ mPassDelete(FALSE),
+ mReadOnly(FALSE)
+{
+ llassert( max_length_bytes > 0 );
+
+ if (font)
+ {
+ mGLFont = font;
+ }
+ else
+ {
+ mGLFont = LLFontGL::sSansSerifSmall;
+ }
+
+ mMinHPixels = mBorderThickness + UI_LINEEDITOR_H_PAD + mBorderLeft;
+ mMaxHPixels = mRect.getWidth() - mMinHPixels - mBorderThickness - mBorderRight;
+
+ mScrollTimer.reset();
+
+ setText(default_text);
+
+ setCursor(mText.length());
+
+ // Scalable UI somehow made these rectangles off-by-one.
+ // I don't know why. JC
+ LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
+ mBorder = new LLViewBorder( "line ed border", border_rect, border_bevel, border_style, mBorderThickness );
+ addChild( mBorder );
+ mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM);
+}
+
+
+LLLineEditor::~LLLineEditor()
+{
+ mFocusLostCallback = NULL;
+ mCommitOnFocusLost = FALSE;
+
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+//virtual
+EWidgetType LLLineEditor::getWidgetType() const
+{
+ return WIDGET_TYPE_LINE_EDITOR;
+}
+
+//virtual
+LLString LLLineEditor::getWidgetTag() const
+{
+ return LL_LINE_EDITOR_TAG;
+}
+
+void LLLineEditor::onFocusLost()
+{
+ if( mFocusLostCallback )
+ {
+ mFocusLostCallback( this, mCallbackUserData );
+ }
+
+ if( mCommitOnFocusLost )
+ {
+ onCommit();
+ }
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ getWindow()->showCursorFromMouseMove();
+}
+
+void LLLineEditor::onCommit()
+{
+ LLUICtrl::onCommit();
+ selectAll();
+}
+
+void LLLineEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLUICtrl::reshape(width, height, called_from_parent );
+
+ mMaxHPixels = mRect.getWidth() - 2 * (mBorderThickness + UI_LINEEDITOR_H_PAD) + 1 - mBorderRight;
+}
+
+
+void LLLineEditor::setEnabled(BOOL enabled)
+{
+ mReadOnly = !enabled;
+ setTabStop(!mReadOnly);
+}
+
+
+void LLLineEditor::setMaxTextLength(S32 max_text_length)
+{
+ S32 max_len = llmax(0, max_text_length);
+ mMaxLengthBytes = max_len;
+ mMaxLengthChars = max_len;
+}
+
+void LLLineEditor::setBorderWidth(S32 left, S32 right)
+{
+ mBorderLeft = llclamp(left, 0, mRect.getWidth());
+ mBorderRight = llclamp(right, 0, mRect.getWidth());
+ mMinHPixels = mBorderThickness + UI_LINEEDITOR_H_PAD + mBorderLeft;
+ mMaxHPixels = mRect.getWidth() - mMinHPixels - mBorderThickness - mBorderRight;
+}
+
+void LLLineEditor::setLabel(const LLString &new_label)
+{
+ mLabel = new_label;
+}
+
+void LLLineEditor::setText(const LLString &new_text)
+{
+ // If new text is identical, don't copy and don't move insertion point
+ if (mText.getString() == new_text)
+ {
+ return;
+ }
+
+ // Check to see if entire field is selected.
+ S32 len = mText.length();
+ BOOL allSelected = (len > 0) && (( mSelectionStart == 0 && mSelectionEnd == len )
+ || ( mSelectionStart == len && mSelectionEnd == 0 ));
+
+ LLString truncated_utf8 = new_text;
+ if (truncated_utf8.size() > (U32)mMaxLengthBytes)
+ {
+ utf8str_truncate(truncated_utf8, mMaxLengthBytes);
+ }
+ mText.assign(truncated_utf8);
+ mText.truncate(mMaxLengthChars);
+
+ if (allSelected)
+ {
+ // ...keep whole thing selected
+ selectAll();
+ }
+ else
+ {
+ // try to preserve insertion point, but deselect text
+ deselect();
+ }
+ setCursor(llmin((S32)mText.length(), getCursor()));
+}
+
+
+// Picks a new cursor position based on the actual screen size of text being drawn.
+void LLLineEditor::setCursorAtLocalPos( S32 local_mouse_x )
+{
+ const llwchar* wtext = mText.getWString().c_str();
+ LLWString asterix_text;
+ if (mDrawAsterixes)
+ {
+ for (S32 i = 0; i < mText.length(); i++)
+ {
+ asterix_text += '*';
+ }
+ wtext = asterix_text.c_str();
+ }
+
+ S32 cursor_pos =
+ mScrollHPos +
+ mGLFont->charFromPixelOffset(
+ wtext, mScrollHPos,
+ (F32)(local_mouse_x - mMinHPixels),
+ (F32)(mMaxHPixels - mMinHPixels + 1)); // min-max range is inclusive
+ setCursor(cursor_pos);
+}
+
+void LLLineEditor::setCursor( S32 pos )
+{
+ S32 old_cursor_pos = getCursor();
+ mCursorPos = llclamp( pos, 0, mText.length());
+
+ S32 pixels_after_scroll = findPixelNearestPos();
+ if( pixels_after_scroll > mMaxHPixels )
+ {
+ S32 width_chars_to_left = mGLFont->getWidth(mText.getWString().c_str(), 0, mScrollHPos);
+ S32 last_visible_char = mGLFont->maxDrawableChars(mText.getWString().c_str(), llmax(0.f, (F32)(mMaxHPixels - mMinHPixels + width_chars_to_left)));
+ S32 min_scroll = mGLFont->firstDrawableChar(mText.getWString().c_str(), (F32)(mMaxHPixels - mMinHPixels), mText.length(), getCursor());
+ if (old_cursor_pos == last_visible_char)
+ {
+ mScrollHPos = llmin(mText.length(), llmax(min_scroll, mScrollHPos + SCROLL_INCREMENT_ADD));
+ }
+ else
+ {
+ mScrollHPos = min_scroll;
+ }
+ }
+ else if (getCursor() < mScrollHPos)
+ {
+ if (old_cursor_pos == mScrollHPos)
+ {
+ mScrollHPos = llmax(0, llmin(getCursor(), mScrollHPos - SCROLL_INCREMENT_DEL));
+ }
+ else
+ {
+ mScrollHPos = getCursor();
+ }
+ }
+}
+
+
+void LLLineEditor::setCursorToEnd()
+{
+ setCursor(mText.length());
+ deselect();
+}
+
+BOOL LLLineEditor::canDeselect()
+{
+ return hasSelection();
+}
+
+
+void LLLineEditor::deselect()
+{
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mIsSelecting = FALSE;
+}
+
+
+void LLLineEditor::startSelection()
+{
+ mIsSelecting = TRUE;
+ mSelectionStart = getCursor();
+ mSelectionEnd = getCursor();
+}
+
+void LLLineEditor::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = FALSE;
+ mSelectionEnd = getCursor();
+ }
+}
+
+BOOL LLLineEditor::canSelectAll()
+{
+ return TRUE;
+}
+
+void LLLineEditor::selectAll()
+{
+ mSelectionStart = mText.length();
+ mSelectionEnd = 0;
+ setCursor(mSelectionEnd);
+ //mScrollHPos = 0;
+ mIsSelecting = TRUE;
+}
+
+
+BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ setFocus( TRUE );
+
+ if (mSelectionEnd == 0 && mSelectionStart == mText.length())
+ {
+ // if everything is selected, handle this as a normal click to change insertion point
+ handleMouseDown(x, y, mask);
+ }
+ else
+ {
+ // otherwise select everything
+ selectAll();
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection
+ // here.
+ mIsSelecting = FALSE;
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ return TRUE;
+}
+
+BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (x < mBorderLeft || x > (mRect.getWidth() - mBorderRight))
+ {
+ return LLUICtrl::handleMouseDown(x, y, mask);
+ }
+ if (mSelectAllonFocusReceived
+ && gFocusMgr.getKeyboardFocus() != this)
+ {
+ setFocus( TRUE );
+ }
+ else
+ {
+ setFocus( TRUE );
+
+ if (mask & MASK_SHIFT)
+ {
+ // Handle selection extension
+ S32 old_cursor_pos = getCursor();
+ setCursorAtLocalPos(x);
+
+ if (hasSelection())
+ {
+ /* Mac-like behavior - extend selection towards the cursor
+ if (getCursor() < mSelectionStart
+ && getCursor() < mSelectionEnd)
+ {
+ // ...left of selection
+ mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = getCursor();
+ }
+ else if (getCursor() > mSelectionStart
+ && getCursor() > mSelectionEnd)
+ {
+ // ...right of selection
+ mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = getCursor();
+ }
+ else
+ {
+ mSelectionEnd = getCursor();
+ }
+ */
+ // Windows behavior
+ mSelectionEnd = getCursor();
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = getCursor();
+ }
+ // assume we're starting a drag select
+ mIsSelecting = TRUE;
+ }
+ else
+ {
+ // Move cursor and deselect for regular click
+ setCursorAtLocalPos( x );
+ deselect();
+ startSelection();
+ }
+
+ gFocusMgr.setMouseCapture( this, &LLLineEditor::onMouseCaptureLost );
+ }
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ return TRUE;
+}
+
+
+BOOL LLLineEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if (gFocusMgr.getMouseCapture() != this && (x < mBorderLeft || x > (mRect.getWidth() - mBorderRight)))
+ {
+ return LLUICtrl::handleHover(x, y, mask);
+ }
+
+ if( getVisible() )
+ {
+ if( (gFocusMgr.getMouseCapture() == this) && mIsSelecting )
+ {
+ if (x != mLastSelectionX || y != mLastSelectionY)
+ {
+ mLastSelectionX = x;
+ mLastSelectionY = y;
+ }
+ // Scroll if mouse cursor outside of bounds
+ if (mScrollTimer.hasExpired())
+ {
+ S32 increment = llround(mScrollTimer.getElapsedTimeF32() / AUTO_SCROLL_TIME);
+ mScrollTimer.reset();
+ mScrollTimer.setTimerExpirySec(AUTO_SCROLL_TIME);
+ if( (x < mMinHPixels) && (mScrollHPos > 0 ) )
+ {
+ // Scroll to the left
+ mScrollHPos = llclamp(mScrollHPos - increment, 0, mText.length());
+ }
+ else
+ if( (x > mMaxHPixels) && (mCursorPos < (S32)mText.length()) )
+ {
+ // If scrolling one pixel would make a difference...
+ S32 pixels_after_scrolling_one_char = findPixelNearestPos(1);
+ if( pixels_after_scrolling_one_char >= mMaxHPixels )
+ {
+ // ...scroll to the right
+ mScrollHPos = llclamp(mScrollHPos + increment, 0, mText.length());
+ }
+ }
+ }
+
+ setCursorAtLocalPos( x );
+ mSelectionEnd = getCursor();
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ handled = TRUE;
+ }
+
+ if( !handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLLineEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+
+ if (!handled && (x < mBorderLeft || x > (mRect.getWidth() - mBorderRight)))
+ {
+ return LLUICtrl::handleMouseUp(x, y, mask);
+ }
+
+ if( mIsSelecting )
+ {
+ setCursorAtLocalPos( x );
+ mSelectionEnd = getCursor();
+
+ handled = TRUE;
+ }
+
+ if( handled )
+ {
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+ }
+
+ return handled;
+}
+
+
+// Remove a single character from the text
+void LLLineEditor::removeChar()
+{
+ if( getCursor() > 0 )
+ {
+ mText.erase(getCursor() - 1, 1);
+
+ setCursor(getCursor() - 1);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+}
+
+
+void LLLineEditor::addChar(const llwchar uni_char)
+{
+ llwchar new_c = uni_char;
+ if (hasSelection())
+ {
+ deleteSelection();
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ mText.erase(getCursor(), 1);
+ }
+
+ S32 length_chars = mText.length();
+ S32 cur_bytes = mText.getString().size();;
+ S32 new_bytes = wchar_utf8_length(new_c);
+
+ BOOL allow_char = TRUE;
+
+ // Inserting character
+ if (length_chars == mMaxLengthChars)
+ {
+ allow_char = FALSE;
+ }
+ if ((new_bytes + cur_bytes) > mMaxLengthBytes)
+ {
+ allow_char = FALSE;
+ }
+
+ if (allow_char)
+ {
+ // Will we need to scroll?
+ LLWString w_buf;
+ w_buf.assign(1, new_c);
+
+ mText.insert(getCursor(), w_buf);
+ setCursor(getCursor() + 1);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+
+ getWindow()->hideCursorUntilMouseMove();
+}
+
+// Extends the selection box to the new cursor position
+void LLLineEditor::extendSelection( S32 new_cursor_pos )
+{
+ if( !mIsSelecting )
+ {
+ startSelection();
+ }
+
+ setCursor(new_cursor_pos);
+ mSelectionEnd = getCursor();
+}
+
+
+void LLLineEditor::setSelection(S32 start, S32 end)
+{
+ S32 len = mText.length();
+
+ mIsSelecting = TRUE;
+
+ // JC, yes, this seems odd, but I think you have to presume a
+ // selection dragged from the end towards the start.
+ mSelectionStart = llclamp(end, 0, len);
+ mSelectionEnd = llclamp(start, 0, len);
+ setCursor(start);
+}
+
+S32 LLLineEditor::prevWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mText.getWString();
+ while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
+ {
+ cursorPos--;
+ }
+ while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) )
+ {
+ cursorPos--;
+ }
+ return cursorPos;
+}
+
+S32 LLLineEditor::nextWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mText.getWString();
+ while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos+1] ) )
+ {
+ cursorPos++;
+ }
+ while( (cursorPos < getLength()) && (wtext[cursorPos+1] == ' ') )
+ {
+ cursorPos++;
+ }
+ return cursorPos;
+}
+
+
+BOOL LLLineEditor::handleSelectionKey(KEY key, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( mask & MASK_SHIFT )
+ {
+ handled = TRUE;
+
+ switch( key )
+ {
+ case KEY_LEFT:
+ if (mIgnoreArrowKeys)
+ {
+ handled = FALSE;
+ break;
+ }
+ if( 0 < getCursor() )
+ {
+ S32 cursorPos = getCursor() - 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = prevWordPos(cursorPos);
+ }
+ extendSelection( cursorPos );
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (mIgnoreArrowKeys)
+ {
+ handled = FALSE;
+ break;
+ }
+ if( getCursor() < mText.length())
+ {
+ S32 cursorPos = getCursor() + 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = nextWordPos(cursorPos);
+ }
+ extendSelection( cursorPos );
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ break;
+
+ case KEY_PAGE_UP:
+ case KEY_HOME:
+ if (mIgnoreArrowKeys)
+ {
+ handled = FALSE;
+ break;
+ }
+ extendSelection( 0 );
+ break;
+
+ case KEY_PAGE_DOWN:
+ case KEY_END:
+ {
+ if (mIgnoreArrowKeys)
+ {
+ handled = FALSE;
+ break;
+ }
+ S32 len = mText.length();
+ if( len )
+ {
+ extendSelection( len );
+ }
+ break;
+ }
+
+ default:
+ handled = FALSE;
+ break;
+ }
+ }
+
+ if (!handled && mHandleEditKeysDirectly)
+ {
+ if( (MASK_CONTROL & mask) && ('A' == key) )
+ {
+ if( canSelectAll() )
+ {
+ selectAll();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ }
+
+
+ return handled;
+}
+
+void LLLineEditor::deleteSelection()
+{
+ if( !mReadOnly && hasSelection() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 selection_length = abs( mSelectionStart - mSelectionEnd );
+
+ mText.erase(left_pos, selection_length);
+ deselect();
+ setCursor(left_pos);
+ }
+}
+
+BOOL LLLineEditor::canCut()
+{
+ return !mReadOnly && !mDrawAsterixes && hasSelection();
+}
+
+// cut selection to clipboard
+void LLLineEditor::cut()
+{
+ if( canCut() )
+ {
+ // Prepare for possible rollback
+ LLLineEditorRollback rollback( this );
+
+
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = abs( mSelectionStart - mSelectionEnd );
+ gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+ deleteSelection();
+
+ // Validate new string and rollback the if needed.
+ BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
+ if( need_to_rollback )
+ {
+ rollback.doRollback( this );
+ reportBadKeystroke();
+ }
+ else
+ if( mKeystrokeCallback )
+ {
+ mKeystrokeCallback( this, mCallbackUserData );
+ }
+ }
+}
+
+BOOL LLLineEditor::canCopy()
+{
+ return !mDrawAsterixes && hasSelection();
+}
+
+
+// copy selection to clipboard
+void LLLineEditor::copy()
+{
+ if( canCopy() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = abs( mSelectionStart - mSelectionEnd );
+ gClipboard.copyFromSubstring( mText.getWString(), left_pos, length );
+ }
+}
+
+BOOL LLLineEditor::canPaste()
+{
+ return !mReadOnly && gClipboard.canPasteString();
+}
+
+
+// paste from clipboard
+void LLLineEditor::paste()
+{
+ if (canPaste())
+ {
+ LLWString paste = gClipboard.getPasteWString();
+ if (!paste.empty())
+ {
+ // Prepare for possible rollback
+ LLLineEditorRollback rollback(this);
+
+ // Delete any selected characters
+ if (hasSelection())
+ {
+ deleteSelection();
+ }
+
+ // Clean up string (replace tabs and returns and remove characters that our fonts don't support.)
+ LLWString clean_string(paste);
+ LLWString::replaceTabsWithSpaces(clean_string, 1);
+ //clean_string = wstring_detabify(paste, 1);
+ LLWString::replaceChar(clean_string, '\n', ' ');
+
+ // Insert the string
+
+ //check to see that the size isn't going to be larger than the
+ //max number of characters or bytes
+ U32 available_bytes = mMaxLengthBytes - wstring_utf8_length(mText);
+ size_t available_chars = mMaxLengthChars - mText.length();
+
+ if ( available_bytes < (U32) wstring_utf8_length(clean_string) )
+ {
+ llwchar current_symbol = clean_string[0];
+ U32 wchars_that_fit = 0;
+ U32 total_bytes = wchar_utf8_length(current_symbol);
+
+ //loop over the "wide" characters (symbols)
+ //and check to see how large (in bytes) each symbol is.
+ while ( total_bytes <= available_bytes )
+ {
+ //while we still have available bytes
+ //"accept" the current symbol and check the size
+ //of the next one
+ current_symbol = clean_string[++wchars_that_fit];
+ total_bytes += wchar_utf8_length(current_symbol);
+ }
+
+ clean_string = clean_string.substr(0, wchars_that_fit);
+ reportBadKeystroke();
+ }
+ else if (available_chars < clean_string.length())
+ {
+ // We can't insert all the characters. Insert as many as possible
+ // but make a noise to alert the user. JC
+ clean_string = clean_string.substr(0, available_chars);
+ reportBadKeystroke();
+ }
+
+ mText.insert(getCursor(), clean_string);
+ setCursor(llmin(mMaxLengthChars, getCursor() + (S32)clean_string.length()));
+ deselect();
+
+ // Validate new string and rollback the if needed.
+ BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
+ if( need_to_rollback )
+ {
+ rollback.doRollback( this );
+ reportBadKeystroke();
+ }
+ else
+ if( mKeystrokeCallback )
+ {
+ mKeystrokeCallback( this, mCallbackUserData );
+ }
+ }
+ }
+}
+
+
+BOOL LLLineEditor::handleSpecialKey(KEY key, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ switch( key )
+ {
+ case KEY_INSERT:
+ if (mask == MASK_NONE)
+ {
+ gKeyboard->toggleInsertMode();
+ }
+
+ handled = TRUE;
+ break;
+
+ case KEY_BACKSPACE:
+ if (!mReadOnly)
+ {
+ //llinfos << "Handling backspace" << llendl;
+ if( hasSelection() )
+ {
+ deleteSelection();
+ }
+ else
+ if( 0 < getCursor() )
+ {
+ removeChar();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ }
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_UP:
+ case KEY_HOME:
+ if (!mIgnoreArrowKeys)
+ {
+ setCursor(0);
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_PAGE_DOWN:
+ case KEY_END:
+ if (!mIgnoreArrowKeys)
+ {
+ S32 len = mText.length();
+ if( len )
+ {
+ setCursor(len);
+ }
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_LEFT:
+ if (!mIgnoreArrowKeys)
+ {
+ if( hasSelection() )
+ {
+ setCursor(llmin( getCursor() - 1, mSelectionStart, mSelectionEnd ));
+ }
+ else
+ if( 0 < getCursor() )
+ {
+ S32 cursorPos = getCursor() - 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = prevWordPos(cursorPos);
+ }
+ setCursor(cursorPos);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (!mIgnoreArrowKeys)
+ {
+ if (hasSelection())
+ {
+ setCursor(llmax(getCursor() + 1, mSelectionStart, mSelectionEnd));
+ }
+ else
+ if (getCursor() < mText.length())
+ {
+ S32 cursorPos = getCursor() + 1;
+ if( mask & MASK_CONTROL )
+ {
+ cursorPos = nextWordPos(cursorPos);
+ }
+ setCursor(cursorPos);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if( !handled && mHandleEditKeysDirectly )
+ {
+ // Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system.
+ if( KEY_DELETE == key )
+ {
+ if( canDoDelete() )
+ {
+ doDelete();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( MASK_CONTROL & mask )
+ {
+ if( 'C' == key )
+ {
+ if( canCopy() )
+ {
+ copy();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( 'V' == key )
+ {
+ if( canPaste() )
+ {
+ paste();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( 'X' == key )
+ {
+ if( canCut() )
+ {
+ cut();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLLineEditor::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+ BOOL selection_modified = FALSE;
+
+ if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible())
+ {
+ LLLineEditorRollback rollback( this );
+
+ if( !handled )
+ {
+ handled = handleSelectionKey( key, mask );
+ selection_modified = handled;
+ }
+
+ // Handle most keys only if the text editor is writeable.
+ if ( !mReadOnly )
+ {
+ if( !handled )
+ {
+ handled = handleSpecialKey( key, mask );
+ }
+ }
+
+ if( handled )
+ {
+ mKeystrokeTimer.reset();
+
+ // Most keystrokes will make the selection box go away, but not all will.
+ if( !selection_modified &&
+ KEY_SHIFT != key &&
+ KEY_CONTROL != key &&
+ KEY_ALT != key &&
+ KEY_CAPSLOCK )
+ {
+ deselect();
+ }
+
+ BOOL need_to_rollback = FALSE;
+
+ // If read-only, don't allow changes
+ need_to_rollback |= (mReadOnly && (mText.getString() == rollback.getText()));
+
+ // Validate new string and rollback the keystroke if needed.
+ need_to_rollback |= (mPrevalidateFunc && !mPrevalidateFunc(mText.getWString()));
+
+ if (need_to_rollback)
+ {
+ rollback.doRollback(this);
+
+ reportBadKeystroke();
+ }
+
+ // Notify owner if requested
+ if (!need_to_rollback && handled)
+ {
+ if (mKeystrokeCallback)
+ {
+ mKeystrokeCallback(this, mCallbackUserData);
+ }
+ }
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLLineEditor::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+
+ if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible() && !mReadOnly)
+ {
+ handled = TRUE;
+
+ LLLineEditorRollback rollback( this );
+
+ addChar(uni_char);
+
+ mKeystrokeTimer.reset();
+
+ deselect();
+
+ BOOL need_to_rollback = FALSE;
+
+ // Validate new string and rollback the keystroke if needed.
+ need_to_rollback |= ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
+
+ if( need_to_rollback )
+ {
+ rollback.doRollback( this );
+
+ reportBadKeystroke();
+ }
+
+ // Notify owner if requested
+ if( !need_to_rollback && handled )
+ {
+ if( mKeystrokeCallback )
+ {
+ // HACK! The only usage of this callback doesn't do anything with the character.
+ // We'll have to do something about this if something ever changes! - Doug
+ mKeystrokeCallback( this, mCallbackUserData );
+ }
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLLineEditor::canDoDelete()
+{
+ return ( !mReadOnly && (!mPassDelete || (hasSelection() || (getCursor() < mText.length()))) );
+}
+
+void LLLineEditor::doDelete()
+{
+ if (canDoDelete())
+ {
+ // Prepare for possible rollback
+ LLLineEditorRollback rollback( this );
+
+ if (hasSelection())
+ {
+ deleteSelection();
+ }
+ else if ( getCursor() < mText.length())
+ {
+ setCursor(getCursor() + 1);
+ removeChar();
+ }
+
+ // Validate new string and rollback the if needed.
+ BOOL need_to_rollback = ( mPrevalidateFunc && !mPrevalidateFunc( mText.getWString() ) );
+ if( need_to_rollback )
+ {
+ rollback.doRollback( this );
+ reportBadKeystroke();
+ }
+ else
+ {
+ if( mKeystrokeCallback )
+ {
+ mKeystrokeCallback( this, mCallbackUserData );
+ }
+ }
+ }
+}
+
+
+void LLLineEditor::draw()
+{
+ if( !getVisible() )
+ {
+ return;
+ }
+
+ S32 text_len = mText.length();
+
+ LLString saved_text;
+ if (mDrawAsterixes)
+ {
+ saved_text = mText.getString();
+ LLString text;
+ for (S32 i = 0; i < mText.length(); i++)
+ {
+ text += '*';
+ }
+ mText = text;
+ }
+
+ // draw rectangle for the background
+ LLRect background( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ background.stretch( -mBorderThickness );
+
+ LLColor4 bg_color = mReadOnlyBgColor;
+
+ // drawing solids requires texturing be disabled
+ {
+ LLGLSNoTexture no_texture;
+ // draw background for text
+ if( !mReadOnly )
+ {
+ if( gFocusMgr.getKeyboardFocus() == this )
+ {
+ bg_color = mFocusBgColor;
+ }
+ else
+ {
+ bg_color = mWriteableBgColor;
+ }
+ }
+ gl_rect_2d(background, bg_color);
+ }
+
+ // draw text
+
+ S32 cursor_bottom = background.mBottom + 1;
+ S32 cursor_top = background.mTop - 1;
+
+ LLColor4 text_color;
+ if (!mReadOnly)
+ {
+ if (!mTentative)
+ {
+ text_color = mFgColor;
+ }
+ else
+ {
+ text_color = mTentativeFgColor;
+ }
+ }
+ else
+ {
+ text_color = mReadOnlyFgColor;
+ }
+ LLColor4 label_color = mTentativeFgColor;
+
+ S32 rendered_text = 0;
+ F32 rendered_pixels_right = (F32)mMinHPixels;
+ F32 text_bottom = (F32)background.mBottom + (F32)UI_LINEEDITOR_V_PAD;
+
+ if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
+ {
+ S32 select_left;
+ S32 select_right;
+ if( mSelectionStart < getCursor() )
+ {
+ select_left = mSelectionStart;
+ select_right = getCursor();
+ }
+ else
+ {
+ select_left = getCursor();
+ select_right = mSelectionStart;
+ }
+
+ if( select_left > mScrollHPos )
+ {
+ // unselected, left side
+ rendered_text = mGLFont->render(
+ mText, mScrollHPos,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ select_left - mScrollHPos,
+ mMaxHPixels - llround(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+
+ if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
+ {
+ LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f);
+ // 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);
+
+ rendered_text += mGLFont->render(
+ mText, mScrollHPos + rendered_text,
+ rendered_pixels_right, text_bottom,
+ LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ select_right - mScrollHPos - rendered_text,
+ mMaxHPixels - llround(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+
+ if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
+ {
+ // unselected, right side
+ mGLFont->render(
+ mText, mScrollHPos + rendered_text,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ S32_MAX,
+ mMaxHPixels - llround(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+ }
+ else
+ {
+ mGLFont->render(
+ mText, mScrollHPos,
+ rendered_pixels_right, text_bottom,
+ text_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ S32_MAX,
+ mMaxHPixels - llround(rendered_pixels_right),
+ &rendered_pixels_right);
+ }
+
+ // If we're editing...
+ if( gFocusMgr.getKeyboardFocus() == this)
+ {
+ // (Flash the cursor every half second)
+ if (gShowTextEditCursor && !mReadOnly)
+ {
+ F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
+ if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
+ {
+ S32 cursor_left = findPixelNearestPos();
+ cursor_left -= UI_LINEEDITOR_CURSOR_THICKNESS / 2;
+ S32 cursor_right = cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS;
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ const LLWString space(utf8str_to_wstring(LLString(" ")));
+ S32 wswidth = mGLFont->getWidth(space.c_str());
+ S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
+ cursor_right = cursor_left + llmax(wswidth, width);
+ }
+ // Use same color as text for the Cursor
+ gl_rect_2d(cursor_left, cursor_top,
+ cursor_right, cursor_bottom, text_color);
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ mGLFont->render(mText, getCursor(), (F32)(cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS / 2), text_bottom,
+ LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ 1);
+ }
+ }
+ }
+
+ // Draw children (border)
+ //mBorder->setVisible(TRUE);
+ mBorder->setKeyboardFocusHighlight( TRUE );
+ LLView::draw();
+ mBorder->setKeyboardFocusHighlight( FALSE );
+ //mBorder->setVisible(FALSE);
+ }
+ else // does not have keyboard input
+ {
+ // draw label if no text provided
+ if (0 == mText.length())
+ {
+ mGLFont->render(mLabel.getWString(), 0,
+ LABEL_HPAD, (F32)text_bottom,
+ label_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::NORMAL,
+ S32_MAX,
+ mMaxHPixels - llround(rendered_pixels_right),
+ &rendered_pixels_right, FALSE);
+ }
+ // Draw children (border)
+ LLView::draw();
+ }
+
+ if (mDrawAsterixes)
+ {
+ mText = saved_text;
+ }
+}
+
+
+// Returns the local screen space X coordinate associated with the text cursor position.
+S32 LLLineEditor::findPixelNearestPos(const S32 cursor_offset)
+{
+ S32 dpos = getCursor() - mScrollHPos + cursor_offset;
+ S32 result = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos, dpos) + mMinHPixels;
+ return result;
+}
+
+void LLLineEditor::reportBadKeystroke()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+//virtual
+void LLLineEditor::clear()
+{
+ mText.clear();
+ setCursor(0);
+}
+
+//virtual
+void LLLineEditor::onTabInto()
+{
+ selectAll();
+}
+
+//virtual
+BOOL LLLineEditor::acceptsTextInput() const
+{
+ return TRUE;
+}
+
+// Start or stop the editor from accepting text-editing keystrokes
+void LLLineEditor::setFocus( BOOL new_state )
+{
+ BOOL old_state = hasFocus();
+
+ // getting focus when we didn't have it before, and we want to select all
+ if (!old_state && new_state && mSelectAllonFocusReceived)
+ {
+ selectAll();
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection
+ // here.
+ mIsSelecting = FALSE;
+ }
+
+ if( new_state )
+ {
+ gEditMenuHandler = this;
+
+ // Don't start the cursor flashing right away
+ mKeystrokeTimer.reset();
+ }
+ else
+ {
+ // Not really needed, since loss of keyboard focus should take care of this,
+ // but limited paranoia is ok.
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ endSelection();
+ }
+
+ LLUICtrl::setFocus( new_state );
+}
+
+//virtual
+void LLLineEditor::setRect(const LLRect& rect)
+{
+ LLUICtrl::setRect(rect);
+ if (mBorder)
+ {
+ LLRect border_rect = mBorder->getRect();
+ // Scalable UI somehow made these rectangles off-by-one.
+ // I don't know why. JC
+ border_rect.setOriginAndSize(border_rect.mLeft, border_rect.mBottom,
+ rect.getWidth()-1, rect.getHeight()-1);
+ mBorder->setRect(border_rect);
+ }
+}
+
+// Limits what characters can be used to [1234567890.-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+//
+// static
+BOOL LLLineEditor::prevalidateFloat(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ BOOL success = TRUE;
+ LLWString trimmed = str;
+ LLWString::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ // May be a comma or period, depending on the locale
+ char decimal_point = gResMgr->getDecimalPoint();
+
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ for( ; i < len; i++ )
+ {
+ if( (decimal_point != trimmed[i] ) && !isdigit( trimmed[i] ) )
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ return success;
+}
+
+//static
+BOOL LLLineEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); }
+
+// static
+BOOL LLLineEditor::postvalidateFloat(const LLString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ BOOL success = TRUE;
+ BOOL has_decimal = FALSE;
+ BOOL has_digit = FALSE;
+
+ LLWString trimmed = utf8str_to_wstring(str);
+ LLWString::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ // May be a comma or period, depending on the locale
+ char decimal_point = gResMgr->getDecimalPoint();
+
+ for( ; i < len; i++ )
+ {
+ if( decimal_point == trimmed[i] )
+ {
+ if( has_decimal )
+ {
+ // can't have two
+ success = FALSE;
+ break;
+ }
+ else
+ {
+ has_decimal = TRUE;
+ }
+ }
+ else
+ if( isdigit( trimmed[i] ) )
+ {
+ has_digit = TRUE;
+ }
+ else
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ // Gotta have at least one
+ success = has_digit;
+
+ return success;
+}
+
+// Limits what characters can be used to [1234567890-] with [-] only valid in the first position.
+// Does NOT ensure that the string is a well-formed number--that's the job of post-validation--for
+// the simple reasons that intermediate states may be invalid even if the final result is valid.
+//
+// static
+BOOL LLLineEditor::prevalidateInt(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ BOOL success = TRUE;
+ LLWString trimmed = str;
+ LLWString::trim(trimmed);
+ S32 len = trimmed.length();
+ if( 0 < len )
+ {
+ S32 i = 0;
+
+ // First character can be a negative sign
+ if( '-' == trimmed[0] )
+ {
+ i++;
+ }
+
+ for( ; i < len; i++ )
+ {
+ if( !isdigit( trimmed[i] ) )
+ {
+ success = FALSE;
+ break;
+ }
+ }
+ }
+
+ return success;
+}
+
+// static
+BOOL LLLineEditor::prevalidatePositiveS32(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLWString trimmed = str;
+ LLWString::trim(trimmed);
+ S32 len = trimmed.length();
+ BOOL success = TRUE;
+ if(0 < len)
+ {
+ if(('-' == trimmed[0]) || ('0' == trimmed[0]))
+ {
+ success = FALSE;
+ }
+ S32 i = 0;
+ while(success && (i < len))
+ {
+ if(!isdigit(trimmed[i++]))
+ {
+ success = FALSE;
+ }
+ }
+ }
+ if (success)
+ {
+ S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
+ if (val <= 0)
+ {
+ success = FALSE;
+ }
+ }
+ return success;
+}
+
+BOOL LLLineEditor::prevalidateNonNegativeS32(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLWString trimmed = str;
+ LLWString::trim(trimmed);
+ S32 len = trimmed.length();
+ BOOL success = TRUE;
+ if(0 < len)
+ {
+ if('-' == trimmed[0])
+ {
+ success = FALSE;
+ }
+ S32 i = 0;
+ while(success && (i < len))
+ {
+ if(!isdigit(trimmed[i++]))
+ {
+ success = FALSE;
+ }
+ }
+ }
+ if (success)
+ {
+ S32 val = strtol(wstring_to_utf8str(trimmed).c_str(), NULL, 10);
+ if (val < 0)
+ {
+ success = FALSE;
+ }
+ }
+ return success;
+}
+
+BOOL LLLineEditor::prevalidateAlphaNum(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ BOOL rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if( !isalnum(str[len]) )
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+// static
+BOOL LLLineEditor::prevalidateAlphaNumSpace(const LLWString &str)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ BOOL rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if(!(isalnum(str[len]) || (' ' == str[len])))
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+// static
+BOOL LLLineEditor::prevalidatePrintableNotPipe(const LLWString &str)
+{
+ BOOL rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if('|' == str[len])
+ {
+ rv = FALSE;
+ break;
+ }
+ if(!((' ' == str[len]) || isalnum(str[len]) || ispunct(str[len])))
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+
+// static
+BOOL LLLineEditor::prevalidatePrintableNoSpace(const LLWString &str)
+{
+ BOOL rv = TRUE;
+ S32 len = str.length();
+ if(len == 0) return rv;
+ while(len--)
+ {
+ if(iswspace(str[len]))
+ {
+ rv = FALSE;
+ break;
+ }
+ if( !(isalnum(str[len]) || ispunct(str[len]) ) )
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+// static
+BOOL LLLineEditor::prevalidateASCII(const LLWString &str)
+{
+ BOOL rv = TRUE;
+ S32 len = str.length();
+ while(len--)
+ {
+ if (str[len] < 0x20 || str[len] > 0x7f)
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+void LLLineEditor::onMouseCaptureLost( LLMouseHandler* old_captor )
+{
+ LLLineEditor* self = (LLLineEditor*) old_captor;
+ self->endSelection();
+}
+
+
+void LLLineEditor::setSelectAllonFocusReceived(BOOL b)
+{
+ mSelectAllonFocusReceived = b;
+}
+
+
+void LLLineEditor::setKeystrokeCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data))
+{
+ mKeystrokeCallback = keystroke_callback;
+}
+
+void LLLineEditor::setFocusLostCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data))
+{
+ mFocusLostCallback = keystroke_callback;
+}
+
+// virtual
+LLXMLNodePtr LLLineEditor::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("max_length", TRUE)->setIntValue(mMaxLengthBytes);
+
+ node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont));
+
+ if (mBorder)
+ {
+ LLString bevel;
+ switch(mBorder->getBevel())
+ {
+ default:
+ case LLViewBorder::BEVEL_NONE: bevel = "none"; break;
+ case LLViewBorder::BEVEL_IN: bevel = "in"; break;
+ case LLViewBorder::BEVEL_OUT: bevel = "out"; break;
+ case LLViewBorder::BEVEL_BRIGHT:bevel = "bright"; break;
+ }
+ node->createChild("bevel_style", TRUE)->setStringValue(bevel);
+
+ LLString style;
+ switch(mBorder->getStyle())
+ {
+ default:
+ case LLViewBorder::STYLE_LINE: style = "line"; break;
+ case LLViewBorder::STYLE_TEXTURE: style = "texture"; break;
+ }
+ node->createChild("border_style", TRUE)->setStringValue(style);
+
+ node->createChild("border_thickness", TRUE)->setIntValue(mBorder->getBorderWidth());
+ }
+
+ if (!mLabel.empty())
+ {
+ node->createChild("label", TRUE)->setStringValue(mLabel.getString());
+ }
+
+ node->createChild("select_all_on_focus_received", TRUE)->setBoolValue(mSelectAllonFocusReceived);
+
+ node->createChild("handle_edit_keys_directly", TRUE)->setBoolValue(mHandleEditKeysDirectly );
+
+ addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor");
+ addColorXML(node, mFgColor, "text_color", "TextFgColor");
+ addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor");
+ addColorXML(node, mTentativeFgColor, "text_tentative_color", "TextFgTentativeColor");
+ addColorXML(node, mReadOnlyBgColor, "bg_readonly_color", "TextBgReadOnlyColor");
+ addColorXML(node, mWriteableBgColor, "bg_writeable_color", "TextBgWriteableColor");
+ addColorXML(node, mFocusBgColor, "bg_focus_color", "TextBgFocusColor");
+
+ node->createChild("select_on_focus", TRUE)->setBoolValue(mSelectAllonFocusReceived );
+
+ return node;
+}
+
+// static
+LLView* LLLineEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("line_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ S32 max_text_length = 128;
+ node->getAttributeS32("max_length", max_text_length);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLString text = node->getTextContents().substr(0, max_text_length - 1);
+
+ LLViewBorder::EBevel bevel_style = LLViewBorder::BEVEL_IN;
+ LLViewBorder::getBevelFromAttribute(node, bevel_style);
+
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE;
+ LLString border_string;
+ node->getAttributeString("border_style", border_string);
+ LLString::toLower(border_string);
+
+ if (border_string == "texture")
+ {
+ border_style = LLViewBorder::STYLE_TEXTURE;
+ }
+
+ S32 border_thickness = 1;
+ node->getAttributeS32("border_thickness", border_thickness);
+
+ LLUICtrlCallback commit_callback = NULL;
+
+ LLLineEditor* line_editor = new LLLineEditor(name,
+ rect,
+ text,
+ font,
+ max_text_length,
+ commit_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ bevel_style,
+ border_style,
+ border_thickness);
+
+ LLString label;
+ if(node->getAttributeString("label", label))
+ {
+ line_editor->setLabel(label);
+ }
+ BOOL select_all_on_focus_received = FALSE;
+ if (node->getAttributeBOOL("select_all_on_focus_received", select_all_on_focus_received))
+ {
+ line_editor->setSelectAllonFocusReceived(select_all_on_focus_received);
+ }
+ BOOL handle_edit_keys_directly = FALSE;
+ if (node->getAttributeBOOL("handle_edit_keys_directly", handle_edit_keys_directly))
+ {
+ line_editor->setHandleEditKeysDirectly(handle_edit_keys_directly);
+ }
+
+ line_editor->setColorParameters(node);
+
+ if(node->hasAttribute("select_on_focus"))
+ {
+ BOOL selectall = FALSE;
+ node->getAttributeBOOL("select_on_focus", selectall);
+ line_editor->setSelectAllonFocusReceived(selectall);
+ }
+
+ LLString prevalidate;
+ if(node->getAttributeString("prevalidate", prevalidate))
+ {
+ LLString::toLower(prevalidate);
+
+ if ("ascii" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateASCII );
+ }
+ else if ("float" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateFloat );
+ }
+ else if ("int" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateInt );
+ }
+ else if ("positive_s32" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidatePositiveS32 );
+ }
+ else if ("non_negative_s32" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateNonNegativeS32 );
+ }
+ else if ("alpha_num" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateAlphaNum );
+ }
+ else if ("alpha_num_space" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidateAlphaNumSpace );
+ }
+ else if ("printable_not_pipe" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidatePrintableNotPipe );
+ }
+ else if ("printable_no_space" == prevalidate)
+ {
+ line_editor->setPrevalidate( LLLineEditor::prevalidatePrintableNoSpace );
+ }
+ }
+
+ line_editor->initFromXML(node, parent);
+
+ return line_editor;
+}
+
+void LLLineEditor::setColorParameters(LLXMLNodePtr node)
+{
+ LLColor4 color;
+ if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color))
+ {
+ setCursorColor(color);
+ }
+ if(node->hasAttribute("text_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"text_color", color);
+ setFgColor(color);
+ }
+ if(node->hasAttribute("text_readonly_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"text_readonly_color", color);
+ setReadOnlyFgColor(color);
+ }
+ if (LLUICtrlFactory::getAttributeColor(node,"text_tentative_color", color))
+ {
+ setTentativeFgColor(color);
+ }
+ if(node->hasAttribute("bg_readonly_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"bg_readonly_color", color);
+ setReadOnlyBgColor(color);
+ }
+ if(node->hasAttribute("bg_writeable_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
+ setWriteableBgColor(color);
+ }
+}
+
+void LLLineEditor::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+LLSD LLLineEditor::getValue() const
+{
+ LLString str = getText();
+ LLSD ret(str);
+ return ret;
+}
+
+BOOL LLLineEditor::setTextArg( const LLString& key, const LLString& text )
+{
+ mText.setArg(key, text);
+ return TRUE;
+}
+
+BOOL LLLineEditor::setLabelArg( const LLString& key, const LLString& text )
+{
+ mLabel.setArg(key, text);
+ return TRUE;
+}
+
+LLSearchEditor::LLSearchEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length_bytes,
+ void (*search_callback)(const LLString& search_string, void* user_data),
+ void* userdata)
+ :
+ LLUICtrl(name, rect, TRUE, NULL, userdata),
+ mSearchCallback(search_callback)
+{
+ LLRect search_edit_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ mSearchEdit = new LLLineEditor("search edit",
+ search_edit_rect,
+ LLString::null,
+ NULL,
+ max_length_bytes,
+ NULL,
+ onSearchEdit,
+ NULL,
+ this);
+ // TODO: this should be translatable
+ mSearchEdit->setLabel("Type here to search");
+ mSearchEdit->setFollowsAll();
+ mSearchEdit->setSelectAllonFocusReceived(TRUE);
+
+ addChild(mSearchEdit);
+
+ S32 btn_width = rect.getHeight(); // button is square, and as tall as search editor
+ LLRect clear_btn_rect(rect.getWidth() - btn_width, rect.getHeight(), rect.getWidth(), 0);
+ mClearSearchButton = new LLButton("clear search",
+ clear_btn_rect,
+ "closebox.tga",
+ "UIImgBtnCloseInactiveUUID",
+ LLString::null,
+ onClearSearch,
+ this,
+ NULL,
+ LLString::null);
+ mClearSearchButton->setFollowsRight();
+ mClearSearchButton->setFollowsTop();
+ mClearSearchButton->setImageColor(LLUI::sColorsGroup->getColor("TextFgTentativeColor"));
+ mClearSearchButton->setTabStop(FALSE);
+ mSearchEdit->addChild(mClearSearchButton);
+
+ mSearchEdit->setBorderWidth(0, btn_width);
+}
+
+LLSearchEditor::~LLSearchEditor()
+{
+}
+
+//virtual
+EWidgetType LLSearchEditor::getWidgetType() const
+{
+ return WIDGET_TYPE_SEARCH_EDITOR;
+}
+
+//virtual
+LLString LLSearchEditor::getWidgetTag() const
+{
+ return LL_SEARCH_EDITOR_TAG;
+}
+
+//virtual
+void LLSearchEditor::setValue(const LLSD& value )
+{
+ mSearchEdit->setValue(value);
+}
+
+//virtual
+LLSD LLSearchEditor::getValue() const
+{
+ return mSearchEdit->getValue();
+}
+
+//virtual
+BOOL LLSearchEditor::setTextArg( const LLString& key, const LLString& text )
+{
+ return mSearchEdit->setTextArg(key, text);
+}
+
+//virtual
+BOOL LLSearchEditor::setLabelArg( const LLString& key, const LLString& text )
+{
+ return mSearchEdit->setLabelArg(key, text);
+}
+
+//virtual
+void LLSearchEditor::clear()
+{
+ if (mSearchEdit)
+ {
+ mSearchEdit->clear();
+ }
+}
+
+
+void LLSearchEditor::draw()
+{
+ mClearSearchButton->setVisible(!mSearchEdit->getWText().empty());
+
+ LLUICtrl::draw();
+}
+
+void LLSearchEditor::setText(const LLString &new_text)
+{
+ mSearchEdit->setText(new_text);
+}
+
+//static
+void LLSearchEditor::onSearchEdit(LLLineEditor* caller, void* user_data )
+{
+ LLSearchEditor* search_editor = (LLSearchEditor*)user_data;
+ if (search_editor->mSearchCallback)
+ {
+ search_editor->mSearchCallback(caller->getText(), search_editor->mCallbackUserData);
+ }
+}
+
+//static
+void LLSearchEditor::onClearSearch(void* user_data)
+{
+ LLSearchEditor* search_editor = (LLSearchEditor*)user_data;
+
+ search_editor->setText(LLString::null);
+ if (search_editor->mSearchCallback)
+ {
+ search_editor->mSearchCallback(LLString::null, search_editor->mCallbackUserData);
+ }
+}
+
+// static
+LLView* LLSearchEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("search_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ S32 max_text_length = 128;
+ node->getAttributeS32("max_length", max_text_length);
+
+ LLString text = node->getValue().substr(0, max_text_length - 1);
+
+ LLSearchEditor* search_editor = new LLSearchEditor(name,
+ rect,
+ max_text_length,
+ NULL, NULL);
+
+ search_editor->setText(text);
+
+ search_editor->initFromXML(node, parent);
+
+ return search_editor;
+}
diff --git a/indra/llui/lllineeditor.h b/indra/llui/lllineeditor.h
new file mode 100644
index 0000000000..1df5dd88f7
--- /dev/null
+++ b/indra/llui/lllineeditor.h
@@ -0,0 +1,298 @@
+/**
+ * @file lllineeditor.h
+ * @brief LLLineEditor base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Text editor widget to let users enter/edit a single line.
+//
+//
+// Features:
+// Text entry of a single line (text, delete, left and right arrow, insert, return).
+// Callbacks either on every keystroke or just on the return key.
+// Focus (allow multiple text entry widgets)
+// Clipboard (cut, copy, and paste)
+// Horizontal scrolling to allow strings longer than widget size allows
+// Pre-validation (limit which keys can be used)
+
+
+#ifndef LL_LLLINEEDITOR_H
+#define LL_LLLINEEDITOR_H
+
+#include "v4color.h"
+#include "llframetimer.h"
+
+#include "lleditmenuhandler.h"
+#include "lluictrl.h"
+#include "lluistring.h"
+#include "llviewborder.h"
+
+class LLFontGL;
+class LLLineEditorRollback;
+class LLButton;
+
+typedef BOOL (*LLLinePrevalidateFunc)(const LLWString &wstr);
+
+//
+// Classes
+//
+class LLLineEditor
+: public LLUICtrl, public LLEditMenuHandler
+{
+ friend class LLLineEditorRollback;
+
+public:
+ LLLineEditor(const LLString& name,
+ const LLRect& rect,
+ const LLString& default_text = LLString::null,
+ const LLFontGL* glfont = NULL,
+ S32 max_length_bytes = 254,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL,
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void* userdata = NULL,
+ LLLinePrevalidateFunc prevalidate_func = NULL,
+ LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN,
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE,
+ S32 border_thickness = 1);
+
+ virtual ~LLLineEditor();
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ void setColorParameters(LLXMLNodePtr node);
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ // mousehandler overrides
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x,S32 y,MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+
+ // LLEditMenuHandler overrides
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void paste();
+ virtual BOOL canPaste();
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ virtual void selectAll();
+ virtual BOOL canSelectAll();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ // view overrides
+ virtual void draw();
+ virtual void reshape(S32 width,S32 height,BOOL called_from_parent=TRUE);
+ virtual void onFocusLost();
+ virtual void setEnabled(BOOL enabled);
+
+ // UI control overrides
+ virtual void clear();
+ virtual void onTabInto();
+ virtual void setFocus( BOOL b );
+ virtual void setRect(const LLRect& rect);
+ virtual BOOL acceptsTextInput() const;
+ virtual void onCommit();
+
+ // assumes UTF8 text
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ virtual BOOL setTextArg( const LLString& key, const LLString& text );
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ void setLabel(const LLString &new_label);
+ void setText(const LLString &new_text);
+
+ const LLString& getText() const { return mText.getString(); }
+ const LLWString& getWText() const { return mText.getWString(); }
+ S32 getLength() const { return mText.length(); }
+
+ S32 getCursor() { return mCursorPos; }
+ void setCursor( S32 pos );
+ void setCursorToEnd();
+
+ // Selects characters 'start' to 'end'.
+ void setSelection(S32 start, S32 end);
+
+ void setCommitOnFocusLost( BOOL b ) { mCommitOnFocusLost = b; }
+
+ void setCursorColor(const LLColor4& c) { mCursorColor = c; }
+ const LLColor4& getCursorColor() const { return mCursorColor; }
+
+ void setFgColor( const LLColor4& c ) { mFgColor = c; }
+ void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
+ void setTentativeFgColor(const LLColor4& c) { mTentativeFgColor = c; }
+ void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
+ void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
+ void setFocusBgColor(const LLColor4& c) { mFocusBgColor = c; }
+
+ const LLColor4& getFgColor() const { return mFgColor; }
+ const LLColor4& getReadOnlyFgColor() const { return mReadOnlyFgColor; }
+ const LLColor4& getTentativeFgColor() const { return mTentativeFgColor; }
+ const LLColor4& getWriteableBgColor() const { return mWriteableBgColor; }
+ const LLColor4& getReadOnlyBgColor() const { return mReadOnlyBgColor; }
+ const LLColor4& getFocusBgColor() const { return mFocusBgColor; }
+
+ void setIgnoreArrowKeys(BOOL b) { mIgnoreArrowKeys = b; }
+ void setIgnoreTab(BOOL b) { mIgnoreTab = b; }
+ void setPassDelete(BOOL b) { mPassDelete = b; }
+
+ void setDrawAsterixes(BOOL b) { mDrawAsterixes = b; }
+
+ // get the cursor position of the beginning/end of the prev/next word in the text
+ S32 prevWordPos(S32 cursorPos) const;
+ S32 nextWordPos(S32 cursorPos) const;
+
+ BOOL hasSelection() { return (mSelectionStart != mSelectionEnd); }
+ void startSelection();
+ void endSelection();
+ void extendSelection(S32 new_cursor_pos);
+ void deleteSelection();
+
+ void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
+ void setSelectAllonFocusReceived(BOOL b);
+
+ void setKeystrokeCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data));
+ void setFocusLostCallback(void (*keystroke_callback)(LLLineEditor* caller, void* user_data));
+
+ void setMaxTextLength(S32 max_text_length);
+ void setBorderWidth(S32 left, S32 right);
+
+ static BOOL isPartOfWord(llwchar c);
+ // Prevalidation controls which keystrokes can affect the editor
+ void setPrevalidate( BOOL (*func)(const LLWString &) ) { mPrevalidateFunc = func; }
+ static BOOL prevalidateFloat(const LLWString &str );
+ static BOOL prevalidateInt(const LLWString &str );
+ static BOOL prevalidatePositiveS32(const LLWString &str);
+ static BOOL prevalidateNonNegativeS32(const LLWString &str);
+ static BOOL prevalidateAlphaNum(const LLWString &str );
+ static BOOL prevalidateAlphaNumSpace(const LLWString &str );
+ static BOOL prevalidatePrintableNotPipe(const LLWString &str);
+ static BOOL prevalidatePrintableNoSpace(const LLWString &str);
+ static BOOL prevalidateASCII(const LLWString &str);
+
+ static BOOL postvalidateFloat(const LLString &str);
+
+ static void onMouseCaptureLost( LLMouseHandler* old_captor );
+
+protected:
+ void removeChar();
+ void addChar(const llwchar c);
+ void setCursorAtLocalPos(S32 local_mouse_x);
+
+ S32 findPixelNearestPos(S32 cursor_offset = 0);
+ void reportBadKeystroke();
+
+ BOOL handleSpecialKey(KEY key, MASK mask);
+ BOOL handleSelectionKey(KEY key, MASK mask);
+ BOOL handleControlKey(KEY key, MASK mask);
+ S32 handleCommitKey(KEY key, MASK mask);
+
+protected:
+ LLUIString mText; // The string being edited.
+ LLUIString mLabel; // text label that is visible when no user text provided
+
+ LLViewBorder* mBorder;
+ const LLFontGL* mGLFont;
+ S32 mMaxLengthChars; // Max number of characters
+ S32 mMaxLengthBytes; // Max length of the UTF8 string.
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ S32 mScrollHPos; // Horizontal offset from the start of mText. Used for scrolling.
+ LLFrameTimer mScrollTimer;
+ S32 mMinHPixels;
+ S32 mMaxHPixels;
+ S32 mBorderLeft;
+ S32 mBorderRight;
+
+ BOOL mCommitOnFocusLost;
+
+ void (*mKeystrokeCallback)( LLLineEditor* caller, void* userdata );
+ void (*mFocusLostCallback)( LLLineEditor* caller, void* userdata );
+
+ BOOL mIsSelecting; // Selection for clipboard operations
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+ S32 mLastSelectionX;
+ S32 mLastSelectionY;
+
+ S32 (*mPrevalidateFunc)(const LLWString &str);
+
+ LLFrameTimer mKeystrokeTimer;
+
+ LLColor4 mCursorColor;
+
+ LLColor4 mFgColor;
+ LLColor4 mReadOnlyFgColor;
+ LLColor4 mTentativeFgColor;
+ LLColor4 mWriteableBgColor;
+ LLColor4 mReadOnlyBgColor;
+ LLColor4 mFocusBgColor;
+
+ S32 mBorderThickness;
+
+ BOOL mIgnoreArrowKeys;
+ BOOL mIgnoreTab;
+ BOOL mDrawAsterixes;
+
+ BOOL mHandleEditKeysDirectly; // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system
+ BOOL mSelectAllonFocusReceived;
+ BOOL mPassDelete;
+
+ BOOL mReadOnly;
+};
+
+
+class LLSearchEditor : public LLUICtrl
+{
+friend class LLLineEditorRollback;
+
+public:
+ LLSearchEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length_bytes,
+ void (*search_callback)(const LLString& search_string, void* user_data),
+ void* userdata);
+
+ virtual ~LLSearchEditor();
+
+ /*virtual*/ void draw();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ void setText(const LLString &new_text);
+
+ void setSearchCallback(void (*search_callback)(const LLString& search_string, void* user_data), void* data) { mSearchCallback = search_callback; mCallbackUserData = data; }
+
+ // LLUICtrl interface
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ virtual BOOL setTextArg( const LLString& key, const LLString& text );
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+ virtual void clear();
+
+
+protected:
+ LLLineEditor* mSearchEdit;
+ LLButton* mClearSearchButton;
+
+ void (*mSearchCallback)(const LLString& search_string, void* user_data);
+
+ static void onSearchEdit(LLLineEditor* caller, void* user_data );
+ static void onClearSearch(void* user_data);
+};
+
+#endif // LL_LINEEDITOR_
diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
new file mode 100644
index 0000000000..de06c34c44
--- /dev/null
+++ b/indra/llui/llmenugl.cpp
@@ -0,0 +1,4341 @@
+/**
+ * @file llmenugl.cpp
+ * @brief LLMenuItemGL base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// This file contains the opengl based menu implementation.
+//
+// NOTES: A menu label is split into 4 columns. The left column, the
+// label colum, the accelerator column, and the right column. The left
+// column is used for displaying boolean values for toggle and check
+// controls. The right column is used for submenus.
+//
+//*****************************************************************************
+
+//#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "llmenugl.h"
+
+#include "llmath.h"
+#include "llgl.h"
+#include "llfocusmgr.h"
+#include "llfont.h"
+#include "llcoord.h"
+#include "llwindow.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+
+#include "llfontgl.h"
+#include "llresmgr.h"
+#include "llui.h"
+
+#include "llglheaders.h"
+#include "llstl.h"
+
+#include "v2math.h"
+#include <set>
+#include <boost/tokenizer.hpp>
+
+// static
+LLView *LLMenuGL::sDefaultMenuContainer = NULL;
+
+S32 MENU_BAR_HEIGHT = 0;
+S32 MENU_BAR_WIDTH = 0;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const LLString SEPARATOR_NAME("separator");
+const LLString TEAROFF_SEPARATOR_LABEL( "~~~~~~~~~~~" );
+const LLString SEPARATOR_LABEL( "-----------" );
+const LLString VERTICAL_SEPARATOR_LABEL( "|" );
+
+const S32 LABEL_BOTTOM_PAD_PIXELS = 2;
+
+const U32 LEFT_PAD_PIXELS = 3;
+const U32 LEFT_WIDTH_PIXELS = 15;
+const U32 LEFT_PLAIN_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS;
+
+const U32 RIGHT_PAD_PIXELS = 2;
+const U32 RIGHT_WIDTH_PIXELS = 15;
+const U32 RIGHT_PLAIN_PIXELS = RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 ACCEL_PAD_PIXELS = 10;
+const U32 PLAIN_PAD_PIXELS = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS + RIGHT_WIDTH_PIXELS;
+
+const U32 BRIEF_PAD_PIXELS = 2;
+
+const U32 SEPARATOR_HEIGHT_PIXELS = 8;
+const S32 TEAROFF_SEPARATOR_HEIGHT_PIXELS = 10;
+const S32 MENU_ITEM_PADDING = 4;
+
+const LLString BOOLEAN_TRUE_PREFIX( "X" );
+const LLString BRANCH_SUFFIX( ">" );
+const LLString ARROW_UP ("^^^^^^^");
+const LLString ARROW_DOWN("vvvvvvv");
+
+const F32 MAX_MOUSE_SLOPE_SUB_MENU = 0.9f;
+
+const S32 PIE_GESTURE_ACTIVATE_DISTANCE = 10;
+
+LLColor4 LLMenuItemGL::sEnabledColor( 0.0f, 0.0f, 0.0f, 1.0f );
+LLColor4 LLMenuItemGL::sDisabledColor( 0.5f, 0.5f, 0.5f, 1.0f );
+LLColor4 LLMenuItemGL::sHighlightBackground( 0.0f, 0.0f, 0.7f, 1.0f );
+LLColor4 LLMenuItemGL::sHighlightForeground( 1.0f, 1.0f, 1.0f, 1.0f );
+BOOL LLMenuItemGL::sDropShadowText = TRUE;
+LLColor4 LLMenuGL::sDefaultBackgroundColor( 0.25f, 0.25f, 0.25f, 0.75f );
+
+LLViewHandle LLMenuHolderGL::sItemLastSelectedHandle;
+LLFrameTimer LLMenuHolderGL::sItemActivationTimer;
+//LLColor4 LLMenuGL::sBackgroundColor( 0.8f, 0.8f, 0.0f, 1.0f );
+
+const S32 PIE_CENTER_SIZE = 20; // pixels, radius of center hole
+const F32 PIE_SCALE_FACTOR = 1.7f; // scale factor for pie menu when mouse is initially down
+const F32 PIE_SHRINK_TIME = 0.2f; // time of transition between unbounded and bounded display of pie menu
+
+const F32 ACTIVATE_HIGHLIGHT_TIME = 0.3f;
+
+///============================================================================
+/// Class LLMenuItemGL
+///============================================================================
+
+// Default constructor
+LLMenuItemGL::LLMenuItemGL( const LLString& name, const LLString& label, KEY key, MASK mask ) :
+ LLView( name, TRUE ),
+ mJumpKey(KEY_NONE),
+ mAcceleratorKey( key ),
+ mAcceleratorMask( mask ),
+ mAllowKeyRepeat(FALSE),
+ mHighlight( FALSE ),
+ mGotHover( FALSE ),
+ mBriefItem( FALSE ),
+ mFont( LLFontGL::sSansSerif ),
+ mStyle(LLFontGL::NORMAL),
+ mDrawTextDisabled( FALSE )
+{
+ setLabel( label );
+}
+
+// virtual
+LLXMLNodePtr LLMenuItemGL::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLView::getXML();
+
+ node->createChild("type", TRUE)->setStringValue(getType());
+
+ node->createChild("label", TRUE)->setStringValue(mLabel);
+
+ if (mAcceleratorKey != KEY_NONE)
+ {
+ std::stringstream out;
+ if (mAcceleratorMask & MASK_CONTROL)
+ {
+ out << "control|";
+ }
+ if (mAcceleratorMask & MASK_ALT)
+ {
+ out << "alt|";
+ }
+ if (mAcceleratorMask & MASK_SHIFT)
+ {
+ out << "shift|";
+ }
+ out << LLKeyboard::stringFromKey(mAcceleratorKey);
+
+ node->createChild("shortcut", TRUE)->setStringValue(out.str());
+ }
+
+ return node;
+}
+
+BOOL LLMenuItemGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ // modified from LLView::handleKey
+ // ignore visibility, as keyboard accelerators should still trigger menu items
+ // even when they are not visible
+ // also, ignore enabled flag for self, as that can change based on menu callbacks
+ BOOL handled = FALSE;
+
+ if( called_from_parent )
+ {
+ // Downward traversal
+ if (mEnabled)
+ {
+ handled = childrenHandleKey( key, mask ) != NULL;
+ }
+ }
+
+ if( !handled )
+ {
+ handled = handleKeyHere( key, mask, called_from_parent );
+ }
+
+ return handled;
+}
+
+BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ if( mEnabled && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == mAcceleratorMask) )
+ {
+ doIt();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ mGotHover = TRUE;
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+void LLMenuItemGL::setBriefItem(BOOL b)
+{
+ mBriefItem = b;
+}
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp)
+{
+ LLKeyBinding *accelerator = NULL;
+
+ if (mAcceleratorKey != KEY_NONE)
+ {
+ std::list<LLKeyBinding*>::iterator list_it;
+ for (list_it = listp->begin(); list_it != listp->end(); ++list_it)
+ {
+ accelerator = *list_it;
+ if ((accelerator->mKey == mAcceleratorKey) && (accelerator->mMask == mAcceleratorMask))
+ {
+ //FIXME: get calling code to throw up warning or route warning messages back to app-provided output
+ // LLString warning;
+ // warning.append("Duplicate key binding <");
+ // appendAcceleratorString( warning );
+ // warning.append("> for menu items:\n ");
+ // warning.append(accelerator->mName);
+ // warning.append("\n ");
+ // warning.append(mLabel);
+
+ // llwarns << warning << llendl;
+ // LLAlertDialog::modalAlert(warning);
+ return FALSE;
+ }
+ }
+ if (!accelerator)
+ {
+ accelerator = new LLKeyBinding;
+ if (accelerator)
+ {
+ accelerator->mKey = mAcceleratorKey;
+ accelerator->mMask = mAcceleratorMask;
+// accelerator->mName = mLabel;
+ }
+ listp->push_back(accelerator);//addData(accelerator);
+ }
+ }
+ return TRUE;
+}
+
+// This function appends the character string representation of
+// the current accelerator key and mask to the provided string.
+void LLMenuItemGL::appendAcceleratorString( LLString& st )
+{
+ // break early if this is a silly thing to do.
+ if( KEY_NONE == mAcceleratorKey )
+ {
+ return;
+ }
+
+ // Append any masks
+#ifdef LL_DARWIN
+ // Standard Mac names for modifier keys in menu equivalents
+ // We could use the symbol characters, but they only exist in certain fonts.
+ if( mAcceleratorMask & MASK_CONTROL )
+ st.append( "Cmd-" ); // Symbol would be "\xE2\x8C\x98"
+ if( mAcceleratorMask & MASK_ALT )
+ st.append( "Opt-" ); // Symbol would be "\xE2\x8C\xA5"
+ if( mAcceleratorMask & MASK_SHIFT )
+ st.append( "Shift-" ); // Symbol would be "\xE2\x8C\xA7"
+#else
+ if( mAcceleratorMask & MASK_CONTROL )
+ st.append( "Ctrl-" );
+ if( mAcceleratorMask & MASK_ALT )
+ st.append( "Alt-" );
+ if( mAcceleratorMask & MASK_SHIFT )
+ st.append( "Shift-" );
+#endif
+
+ LLString keystr = LLKeyboard::stringFromKey( mAcceleratorKey );
+ if ((mAcceleratorMask & (MASK_CONTROL|MASK_ALT|MASK_SHIFT)) &&
+ (keystr[0] == '-' || keystr[0] == '='))
+ {
+ st.append( " " );
+ }
+ st.append( keystr );
+}
+
+void LLMenuItemGL::setJumpKey(KEY key)
+{
+ mJumpKey = LLStringOps::toUpper((char)key);
+}
+
+KEY LLMenuItemGL::getJumpKey()
+{
+ return mJumpKey;
+}
+
+
+// set the font used by all of the menu objects
+void LLMenuItemGL::setFont(LLFontGL* font)
+{
+ mFont = font;
+}
+
+// returns the height in pixels for the current font.
+U32 LLMenuItemGL::getNominalHeight( void )
+{
+ return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING;
+}
+
+// functions to control the color scheme
+void LLMenuItemGL::setEnabledColor( const LLColor4& color )
+{
+ sEnabledColor = color;
+}
+
+void LLMenuItemGL::setDisabledColor( const LLColor4& color )
+{
+ sDisabledColor = color;
+}
+
+void LLMenuItemGL::setHighlightBGColor( const LLColor4& color )
+{
+ sHighlightBackground = color;
+}
+
+void LLMenuItemGL::setHighlightFGColor( const LLColor4& color )
+{
+ sHighlightForeground = color;
+}
+
+
+// change the label
+void LLMenuItemGL::setLabel( const LLString& label )
+{
+ mLabel = label;
+}
+
+// Get the parent menu for this item
+LLMenuGL* LLMenuItemGL::getMenu()
+{
+ return (LLMenuGL*) getParent();
+}
+
+
+// getNominalWidth() - returns the normal width of this control in
+// pixels - this is used for calculating the widest item, as well as
+// for horizontal arrangement.
+U32 LLMenuItemGL::getNominalWidth( void )
+{
+ U32 width;
+
+ if (mBriefItem)
+ {
+ width = BRIEF_PAD_PIXELS;
+ }
+ else
+ {
+ width = PLAIN_PAD_PIXELS;
+ }
+
+ if( KEY_NONE != mAcceleratorKey )
+ {
+ width += ACCEL_PAD_PIXELS;
+ LLString temp;
+ appendAcceleratorString( temp );
+ width += mFont->getWidth( temp );
+ }
+ width += mFont->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ LLString st = mDrawAccelLabel.getString();
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+// set the hover status (called by it's menu)
+ void LLMenuItemGL::setHighlight( BOOL highlight )
+{
+ mHighlight = highlight;
+}
+
+// determine if this object is active
+BOOL LLMenuItemGL::isActive( void ) const
+{
+ return FALSE;
+}
+
+BOOL LLMenuItemGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ if (mHighlight &&
+ getMenu()->getVisible() &&
+ (!getMenu()->getTornOff() || ((LLFloater*)getMenu()->getParent())->hasFocus()))
+ {
+ if (key == KEY_UP)
+ {
+ getMenu()->highlightPrevItem(this);
+ return TRUE;
+ }
+ else if (key == KEY_DOWN)
+ {
+ getMenu()->highlightNextItem(this);
+ return TRUE;
+ }
+ else if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ doIt();
+ if (!getMenu()->getTornOff())
+ {
+ ((LLMenuHolderGL*)getMenu()->getParent())->hideMenus();
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMenuItemGL::handleMouseUp( S32 x, S32 y, MASK )
+{
+ //llinfos << mLabel.c_str() << " handleMouseUp " << x << "," << y
+ // << llendl;
+ if (mEnabled)
+ {
+ doIt();
+ mHighlight = FALSE;
+ make_ui_sound("UISndClickRelease");
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLMenuItemGL::draw( void )
+{
+ // *FIX: This can be optimized by using switches. Want to avoid
+ // that until the functionality is finalized.
+
+ // HACK: Brief items don't highlight. Pie menu takes care of it. JC
+ if( mHighlight && !mBriefItem)
+ {
+ glColor4fv( sHighlightBackground.mV );
+ gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ }
+
+ LLColor4 color;
+
+ U8 font_style = mStyle;
+ if (LLMenuItemGL::sDropShadowText && getEnabled() && !mDrawTextDisabled )
+ {
+ font_style |= LLFontGL::DROP_SHADOW;
+ }
+
+ if ( mHighlight )
+ {
+ color = sHighlightForeground;
+ }
+ else if( getEnabled() && !mDrawTextDisabled )
+ {
+ color = sEnabledColor;
+ }
+ else
+ {
+ color = sDisabledColor;
+ }
+
+ // Draw the text on top.
+ if (mBriefItem)
+ {
+ mFont->render( mLabel, 0, BRIEF_PAD_PIXELS / 2, 0, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, font_style );
+ }
+ else
+ {
+ if( !mDrawBoolLabel.empty() )
+ {
+ mFont->render( mDrawBoolLabel.getWString(), 0, (F32)LEFT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ mFont->render( mLabel.getWString(), 0, (F32)LEFT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
+ if( !mDrawAccelLabel.empty() )
+ {
+ mFont->render( mDrawAccelLabel.getWString(), 0, (F32)mRect.mRight - (F32)RIGHT_PLAIN_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ if( !mDrawBranchLabel.empty() )
+ {
+ mFont->render( mDrawBranchLabel.getWString(), 0, (F32)mRect.mRight - (F32)RIGHT_PAD_PIXELS, ((F32)MENU_ITEM_PADDING / 2.f) + 1.f, color,
+ LLFontGL::RIGHT, LLFontGL::BOTTOM, font_style, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ }
+
+ // underline navigation key
+ BOOL draw_jump_key = gKeyboard->currentMask(FALSE) == MASK_ALT &&
+ (!getMenu()->getHighlightedItem() || !getMenu()->getHighlightedItem()->isActive()) &&
+ (!getMenu()->getTornOff());
+ if (draw_jump_key)
+ {
+ LLString upper_case_label = mLabel.getString();
+ LLString::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(mJumpKey);
+ if (offset != std::string::npos)
+ {
+ S32 x_begin = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset);
+ S32 x_end = LEFT_PLAIN_PIXELS + mFont->getWidth(mLabel, 0, offset + 1);
+ gl_line_2d(x_begin, (MENU_ITEM_PADDING / 2) + 1, x_end, (MENU_ITEM_PADDING / 2) + 1);
+ }
+ }
+
+ // clear got hover every frame
+ mGotHover = FALSE;
+}
+
+BOOL LLMenuItemGL::setLabelArg( const LLString& key, const LLString& text )
+{
+ mLabel.setArg(key, text);
+ return TRUE;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemSeparatorGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemSeparatorGL : public LLMenuItemGL
+{
+public:
+ LLMenuItemSeparatorGL( const LLString &name = SEPARATOR_NAME );
+
+ virtual LLString getType() const { return "separator"; }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_SEPARATOR; }
+ virtual LLString getWidgetTag() const { return LL_MENU_ITEM_SEPARATOR_GL_TAG; }
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void ) {}
+
+ virtual void draw( void );
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual U32 getNominalHeight( void ) { return SEPARATOR_HEIGHT_PIXELS; }
+};
+
+LLMenuItemSeparatorGL::LLMenuItemSeparatorGL( const LLString &name ) :
+ LLMenuItemGL( SEPARATOR_NAME, SEPARATOR_LABEL )
+{
+}
+
+void LLMenuItemSeparatorGL::draw( void )
+{
+ glColor4fv( sDisabledColor.mV );
+ const S32 y = mRect.getHeight() / 2;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, mRect.getWidth() - PAD, y );
+}
+
+BOOL LLMenuItemSeparatorGL::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > mRect.getHeight() / 2)
+ {
+ return parent_menu->handleMouseDown(x + mRect.mLeft, mRect.mTop + 1, mask);
+ }
+ else
+ {
+ return parent_menu->handleMouseDown(x + mRect.mLeft, mRect.mBottom - 1, mask);
+ }
+}
+
+BOOL LLMenuItemSeparatorGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > mRect.getHeight() / 2)
+ {
+ return parent_menu->handleMouseUp(x + mRect.mLeft, mRect.mTop + 1, mask);
+ }
+ else
+ {
+ return parent_menu->handleMouseUp(x + mRect.mLeft, mRect.mBottom - 1, mask);
+ }
+}
+
+BOOL LLMenuItemSeparatorGL::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* parent_menu = getMenu();
+ if (y > mRect.getHeight() / 2)
+ {
+ parent_menu->highlightPrevItem(this, FALSE);
+ return FALSE;
+ }
+ else
+ {
+ parent_menu->highlightNextItem(this, FALSE);
+ return FALSE;
+ }
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemVerticalSeparatorGL
+//
+// This class represents a vertical separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemVerticalSeparatorGL
+: public LLMenuItemSeparatorGL
+{
+public:
+ LLMenuItemVerticalSeparatorGL( void );
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_SEPARATOR_VERTICAL; }
+ virtual LLString getWidgetTag() const { return LL_MENU_ITEM_VERTICAL_SEPARATOR_GL_TAG; }
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }
+};
+
+LLMenuItemVerticalSeparatorGL::LLMenuItemVerticalSeparatorGL( void )
+{
+ setLabel( VERTICAL_SEPARATOR_LABEL );
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemTearOffGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LLMenuItemTearOffGL::LLMenuItemTearOffGL(LLViewHandle parent_floater_handle) :
+ LLMenuItemGL("tear off", TEAROFF_SEPARATOR_LABEL),
+ mParentHandle(parent_floater_handle)
+{
+}
+
+EWidgetType LLMenuItemTearOffGL::getWidgetType() const
+{
+ return WIDGET_TYPE_TEAROFF_MENU;
+}
+
+LLString LLMenuItemTearOffGL::getWidgetTag() const
+{
+ return LL_MENU_ITEM_TEAR_OFF_GL_TAG;
+}
+
+void LLMenuItemTearOffGL::doIt()
+{
+ if (getMenu()->getTornOff())
+ {
+ LLTearOffMenu* torn_off_menu = (LLTearOffMenu*)(getMenu()->getParent());
+ torn_off_menu->close();
+ }
+ else
+ {
+ // transfer keyboard focus and highlight to first real item in list
+ if (getHighlight())
+ {
+ getMenu()->highlightNextItem(this);
+ }
+
+ // grab menu holder before this menu is parented to a floater
+ LLMenuHolderGL* menu_holder = ((LLMenuHolderGL*)getMenu()->getParent());
+ getMenu()->arrange();
+
+ LLFloater* parent_floater = LLFloater::getFloaterByHandle(mParentHandle);
+ LLFloater* tear_off_menu = LLTearOffMenu::create(getMenu());
+ if (parent_floater && tear_off_menu)
+ {
+ parent_floater->addDependentFloater(tear_off_menu, FALSE);
+ }
+
+ // hide menus
+ // only do it if the menu is open, not being triggered via accelerator
+ if (getMenu()->getVisible())
+ {
+ menu_holder->hideMenus();
+ }
+
+ // give focus to torn off menu because it will have been taken away
+ // when parent menu closes
+ tear_off_menu->setFocus(TRUE);
+ }
+}
+
+void LLMenuItemTearOffGL::draw()
+{
+ if( mHighlight && !mBriefItem)
+ {
+ glColor4fv( sHighlightBackground.mV );
+ gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ }
+
+ if (mEnabled)
+ {
+ glColor4fv( sEnabledColor.mV );
+ }
+ else
+ {
+ glColor4fv( sDisabledColor.mV );
+ }
+ const S32 y = mRect.getHeight() / 3;
+ const S32 PAD = 6;
+ gl_line_2d( PAD, y, mRect.getWidth() - PAD, y );
+ gl_line_2d( PAD, y * 2, mRect.getWidth() - PAD, y * 2 );
+}
+
+U32 LLMenuItemTearOffGL::getNominalHeight( void ) { return TEAROFF_SEPARATOR_HEIGHT_PIXELS; }
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBlankGL
+//
+// This class represents a blank, non-functioning item.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBlankGL : public LLMenuItemGL
+{
+public:
+ LLMenuItemBlankGL( void );
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_BLANK; }
+ virtual LLString getWidgetTag() const { return LL_MENU_ITEM_BLANK_GL_TAG; }
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void ) {}
+
+ virtual void draw( void ) {}
+};
+
+LLMenuItemBlankGL::LLMenuItemBlankGL( void )
+: LLMenuItemGL( "", "" )
+{
+ mEnabled = FALSE;
+}
+
+///============================================================================
+/// Class LLMenuItemCallGL
+///============================================================================
+
+LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ void* user_data,
+ KEY key, MASK mask,
+ BOOL enabled,
+ on_disabled_callback on_disabled_cb) :
+ LLMenuItemGL( name, label, key, mask ),
+ mCallback( clicked_cb ),
+ mEnabledCallback( enabled_cb ),
+ mLabelCallback(NULL),
+ mUserData( user_data ),
+ mOnDisabledCallback(on_disabled_cb)
+{
+ if(!enabled) setEnabled(FALSE);
+}
+
+LLMenuItemCallGL::LLMenuItemCallGL( const LLString& name,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ void* user_data,
+ KEY key, MASK mask,
+ BOOL enabled,
+ on_disabled_callback on_disabled_cb) :
+ LLMenuItemGL( name, name, key, mask ),
+ mCallback( clicked_cb ),
+ mEnabledCallback( enabled_cb ),
+ mLabelCallback(NULL),
+ mUserData( user_data ),
+ mOnDisabledCallback(on_disabled_cb)
+{
+ if(!enabled) setEnabled(FALSE);
+}
+
+LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ label_callback label_cb,
+ void* user_data,
+ KEY key, MASK mask,
+ BOOL enabled,
+ on_disabled_callback on_disabled_cb) :
+ LLMenuItemGL(name, label, key, mask),
+ mCallback(clicked_cb),
+ mEnabledCallback(enabled_cb),
+ mLabelCallback(label_cb),
+ mUserData(user_data),
+ mOnDisabledCallback(on_disabled_cb)
+{
+ if(!enabled) setEnabled(FALSE);
+}
+
+LLMenuItemCallGL::LLMenuItemCallGL(const LLString& name,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ label_callback label_cb,
+ void* user_data,
+ KEY key, MASK mask,
+ BOOL enabled,
+ on_disabled_callback on_disabled_cb) :
+ LLMenuItemGL(name, name, key, mask),
+ mCallback(clicked_cb),
+ mEnabledCallback(enabled_cb),
+ mLabelCallback(label_cb),
+ mUserData(user_data),
+ mOnDisabledCallback(on_disabled_cb)
+{
+ if(!enabled) setEnabled(FALSE);
+}
+
+void LLMenuItemCallGL::setEnabledControl(LLString enabled_control, LLView *context)
+{
+ // Register new listener
+ if (!enabled_control.empty())
+ {
+ LLControlBase *control = context->findControl(enabled_control);
+ if (control)
+ {
+ LLSD state = control->registerListener(this, "ENABLED");
+ setEnabled(state);
+ }
+ else
+ {
+ context->addBoolControl(enabled_control, mEnabled);
+ control = context->findControl(enabled_control);
+ control->registerListener(this, "ENABLED");
+ }
+ }
+}
+
+void LLMenuItemCallGL::setVisibleControl(LLString enabled_control, LLView *context)
+{
+ // Register new listener
+ if (!enabled_control.empty())
+ {
+ LLControlBase *control = context->findControl(enabled_control);
+ if (control)
+ {
+ LLSD state = control->registerListener(this, "VISIBLE");
+ setVisible(state);
+ }
+ else
+ {
+ context->addBoolControl(enabled_control, mEnabled);
+ control = context->findControl(enabled_control);
+ control->registerListener(this, "VISIBLE");
+ }
+ }
+}
+
+// virtual
+bool LLMenuItemCallGL::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (userdata.asString() == "ENABLED" && event->desc() == "value_changed")
+ {
+ LLSD state = event->getValue();
+ setEnabled(state);
+ return TRUE;
+ }
+ if (userdata.asString() == "VISIBLE" && event->desc() == "value_changed")
+ {
+ LLSD state = event->getValue();
+ setVisible(state);
+ return TRUE;
+ }
+ return LLMenuItemGL::handleEvent(event, userdata);
+}
+
+// virtual
+LLXMLNodePtr LLMenuItemCallGL::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLMenuItemGL::getXML();
+
+ // Contents
+
+ std::vector<LLListenerEntry> listeners = mDispatcher->getListeners();
+ std::vector<LLListenerEntry>::iterator itor;
+ for (itor = listeners.begin(); itor != listeners.end(); ++itor)
+ {
+ LLString listener_name = findEventListener((LLSimpleListener*)itor->listener);
+ if (!listener_name.empty())
+ {
+ LLXMLNodePtr child_node = node->createChild("on_click", FALSE);
+ child_node->createChild("function", TRUE)->setStringValue(listener_name);
+ child_node->createChild("filter", TRUE)->setStringValue(itor->filter.asString());
+ child_node->createChild("userdata", TRUE)->setStringValue(itor->userdata.asString());
+ }
+ }
+
+ return node;
+}
+
+// doIt() - Call the callback provided
+void LLMenuItemCallGL::doIt( void )
+{
+ // RN: menu item can be deleted in callback, so beware
+ getMenu()->setItemLastSelected( this );
+
+ if( mCallback )
+ {
+ mCallback( mUserData );
+ }
+ LLPointer<LLEvent> fired_event = new LLEvent(this);
+ fireEvent(fired_event, "on_click");
+}
+
+EWidgetType LLMenuItemCallGL::getWidgetType() const
+{
+ return WIDGET_TYPE_MENU_ITEM_CALL;
+}
+
+LLString LLMenuItemCallGL::getWidgetTag() const
+{
+ return LL_MENU_ITEM_CALL_GL_TAG;
+}
+
+void LLMenuItemCallGL::buildDrawLabel( void )
+{
+ LLPointer<LLEvent> fired_event = new LLEvent(this);
+ fireEvent(fired_event, "on_build");
+ if( mEnabledCallback )
+ {
+ setEnabled( mEnabledCallback( mUserData ) );
+ }
+ if(mLabelCallback)
+ {
+ LLString label;
+ mLabelCallback(label, mUserData);
+ mLabel = label;
+ }
+ LLMenuItemGL::buildDrawLabel();
+}
+
+BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask )
+{
+ if( (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == mAcceleratorMask) )
+ {
+ LLPointer<LLEvent> fired_event = new LLEvent(this);
+ fireEvent(fired_event, "on_build");
+ if( mEnabledCallback )
+ {
+ setEnabled( mEnabledCallback( mUserData ) );
+ }
+ if( !mEnabled )
+ {
+ if( mOnDisabledCallback )
+ {
+ mOnDisabledCallback( mUserData );
+ }
+ }
+ }
+ return LLMenuItemGL::handleAcceleratorKey(key, mask);
+}
+
+///============================================================================
+/// Class LLMenuItemCheckGL
+///============================================================================
+
+LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ check_callback check_cb,
+ void* user_data,
+ KEY key, MASK mask ) :
+ LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ),
+ mCheckCallback( check_cb ),
+ mChecked(FALSE)
+{
+}
+
+LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ check_callback check_cb,
+ void* user_data,
+ KEY key, MASK mask ) :
+ LLMenuItemCallGL( name, name, clicked_cb, enabled_cb, user_data, key, mask ),
+ mCheckCallback( check_cb ),
+ mChecked(FALSE)
+{
+}
+
+LLMenuItemCheckGL::LLMenuItemCheckGL ( const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ LLString control_name,
+ LLView *context,
+ void* user_data,
+ KEY key, MASK mask ) :
+ LLMenuItemCallGL( name, label, clicked_cb, enabled_cb, user_data, key, mask ),
+ mCheckCallback( NULL )
+{
+ setControlName(control_name, context);
+}
+
+void LLMenuItemCheckGL::setCheckedControl(LLString checked_control, LLView *context)
+{
+ // Register new listener
+ if (!checked_control.empty())
+ {
+ LLControlBase *control = context->findControl(checked_control);
+ if (control)
+ {
+ LLSD state = control->registerListener(this, "CHECKED");
+ mChecked = state;
+ }
+ else
+ {
+ context->addBoolControl(checked_control, mChecked);
+ control = context->findControl(checked_control);
+ control->registerListener(this, "CHECKED");
+ }
+ }
+}
+
+// virtual
+bool LLMenuItemCheckGL::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (userdata.asString() == "CHECKED" && event->desc() == "value_changed")
+ {
+ LLSD state = event->getValue();
+ mChecked = state;
+ if(mChecked)
+ {
+ mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+ return TRUE;
+ }
+ return LLMenuItemCallGL::handleEvent(event, userdata);
+}
+
+// virtual
+LLXMLNodePtr LLMenuItemCheckGL::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLMenuItemCallGL::getXML();
+ return node;
+}
+
+EWidgetType LLMenuItemCheckGL::getWidgetType() const
+{
+ return WIDGET_TYPE_MENU_ITEM_CHECK;
+}
+
+LLString LLMenuItemCheckGL::getWidgetTag() const
+{
+ return LL_MENU_ITEM_CHECK_GL_TAG;
+}
+
+// called to rebuild the draw label
+void LLMenuItemCheckGL::buildDrawLabel( void )
+{
+ if(mChecked || (mCheckCallback && mCheckCallback( mUserData ) ) )
+ {
+ mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+ LLMenuItemCallGL::buildDrawLabel();
+}
+
+
+///============================================================================
+/// Class LLMenuItemToggleGL
+///============================================================================
+
+LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, const LLString& label, BOOL* toggle,
+ KEY key, MASK mask ) :
+ LLMenuItemGL( name, label, key, mask ),
+ mToggle( toggle )
+{
+}
+
+LLMenuItemToggleGL::LLMenuItemToggleGL( const LLString& name, BOOL* toggle,
+ KEY key, MASK mask ) :
+ LLMenuItemGL( name, name, key, mask ),
+ mToggle( toggle )
+{
+}
+
+
+// called to rebuild the draw label
+void LLMenuItemToggleGL::buildDrawLabel( void )
+{
+ if( *mToggle )
+ {
+ mDrawBoolLabel = BOOLEAN_TRUE_PREFIX;
+ }
+ else
+ {
+ mDrawBoolLabel.clear();
+ }
+ mDrawAccelLabel.clear();
+ LLString st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+// doIt() - do the primary funcationality of the menu item.
+void LLMenuItemToggleGL::doIt( void )
+{
+ getMenu()->setItemLastSelected( this );
+ //llinfos << "LLMenuItemToggleGL::doIt " << mLabel.c_str() << llendl;
+ *mToggle = !(*mToggle);
+ buildDrawLabel();
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBranchGL
+//
+// The LLMenuItemBranchGL represents a menu item that has a
+// sub-menu. This is used to make cascading menus.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBranchGL : public LLMenuItemGL
+{
+protected:
+ LLMenuGL* mBranch;
+
+public:
+ LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ virtual LLView* getChildByName(const LLString& name, BOOL recurse) const;
+
+ virtual LLString getType() const { return "menu"; }
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
+
+ // check if we've used these accelerators already
+ virtual BOOL addToAcceleratorList(std::list <LLKeyBinding*> *listp);
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void );
+
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+
+ // set the hover status (called by it's menu) and if the object is
+ // active. This is used for behavior transfer.
+ virtual void setHighlight( BOOL highlight );
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ virtual BOOL isActive() const { return !mBranch->getTornOff() && mBranch->getVisible(); }
+
+ LLMenuGL *getBranch() const { return mBranch; }
+
+ virtual void updateBranchParent( LLView* parentp );
+
+ // LLView Functionality
+ virtual void onVisibilityChange( BOOL curVisibilityIn );
+
+ virtual void draw();
+
+ virtual void setEnabledSubMenus(BOOL enabled);
+};
+
+LLMenuItemBranchGL::LLMenuItemBranchGL( const LLString& name, const LLString& label, LLMenuGL* branch,
+ KEY key, MASK mask ) :
+ LLMenuItemGL( name, label, key, mask ),
+ mBranch( branch )
+{
+ mBranch->setVisible( FALSE );
+ mBranch->setParentMenuItem(this);
+}
+
+// virtual
+LLView* LLMenuItemBranchGL::getChildByName(const LLString& name, BOOL recurse) const
+{
+ if (mBranch->getName() == name)
+ {
+ return mBranch;
+ }
+ // Always recurse on branches
+ return mBranch->getChildByName(name, recurse);
+}
+
+EWidgetType LLMenuItemBranchGL::getWidgetType() const
+{
+ return WIDGET_TYPE_MENU_ITEM_BRANCH;
+}
+
+LLString LLMenuItemBranchGL::getWidgetTag() const
+{
+ return LL_MENU_ITEM_BRANCH_GL_TAG;
+}
+
+// virtual
+BOOL LLMenuItemBranchGL::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (mEnabled)
+ {
+ doIt();
+ make_ui_sound("UISndClickRelease");
+ }
+ return FALSE;
+}
+
+BOOL LLMenuItemBranchGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ return mBranch->handleAcceleratorKey(key, mask);
+}
+
+// virtual
+LLXMLNodePtr LLMenuItemBranchGL::getXML(bool save_children) const
+{
+ if (mBranch)
+ {
+ return mBranch->getXML();
+ }
+
+ return LLMenuItemGL::getXML();
+}
+
+
+// This function checks to see if the accelerator key is already in use;
+// if not, it will be added to the list
+BOOL LLMenuItemBranchGL::addToAcceleratorList(std::list<LLKeyBinding*> *listp)
+{
+ U32 item_count = mBranch->getItemCount();
+ LLMenuItemGL *item;
+
+ while (item_count--)
+ {
+ if ((item = mBranch->getItem(item_count)))
+ {
+ return item->addToAcceleratorList(listp);
+ }
+ }
+ return FALSE;
+}
+
+
+// called to rebuild the draw label
+void LLMenuItemBranchGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ LLString st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+ mDrawBranchLabel = BRANCH_SUFFIX;
+}
+
+// doIt() - do the primary functionality of the menu item.
+void LLMenuItemBranchGL::doIt( void )
+{
+ if (mBranch->getTornOff())
+ {
+ gFloaterView->bringToFront((LLFloater*)mBranch->getParent());
+ // this might not be necessary, as torn off branches don't get focus and hence no highligth
+ mBranch->highlightNextItem(NULL);
+ }
+ else if( !mBranch->getVisible() )
+ {
+ mBranch->arrange();
+
+ LLRect rect = mBranch->getRect();
+ // calculate root-view relative position for branch menu
+ S32 left = mRect.mRight;
+ S32 top = mRect.mTop - mRect.mBottom;
+
+ localPointToOtherView(left, top, &left, &top, mBranch->getParent());
+
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+
+ if (mBranch->getCanTearOff())
+ {
+ rect.translate(0, TEAROFF_SEPARATOR_HEIGHT_PIXELS);
+ }
+ mBranch->setRect( rect );
+ S32 x = 0;
+ S32 y = 0;
+ mBranch->localPointToOtherView( 0, 0, &x, &y, mBranch->getParent() );
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ if( y < 0 )
+ {
+ delta_y = -y;
+ }
+
+ S32 window_width = mBranch->getParent()->getRect().getWidth();
+ if( x > window_width - rect.getWidth() )
+ {
+ // move sub-menu over to left side
+ delta_x = llmax(-x, (-1 * (rect.getWidth() + mRect.getWidth())));
+ }
+ mBranch->translate( delta_x, delta_y );
+ mBranch->setVisible( TRUE );
+ }
+}
+
+BOOL LLMenuItemBranchGL::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+ if (called_from_parent)
+ {
+ handled = mBranch->handleKey(key, mask, called_from_parent);
+ }
+
+ if (!handled)
+ {
+ handled = LLMenuItemGL::handleKey(key, mask, called_from_parent);
+ }
+
+ return handled;
+}
+
+// set the hover status (called by it's menu)
+void LLMenuItemBranchGL::setHighlight( BOOL highlight )
+{
+ BOOL auto_open = mEnabled && (!mBranch->getVisible() || mBranch->getTornOff());
+ // torn off menus don't open sub menus on hover unless they have focus
+ if (getMenu()->getTornOff() && !((LLFloater*)getMenu()->getParent())->hasFocus())
+ {
+ auto_open = FALSE;
+ }
+ // don't auto open torn off sub-menus (need to explicitly active menu item to give them focus)
+ if (mBranch->getTornOff())
+ {
+ auto_open = FALSE;
+ }
+
+ mHighlight = highlight;
+ if( highlight )
+ {
+ if(auto_open)
+ {
+ doIt();
+ }
+ }
+ else
+ {
+ if (mBranch->getTornOff())
+ {
+ ((LLFloater*)mBranch->getParent())->setFocus(FALSE);
+ mBranch->clearHoverItem();
+ }
+ else
+ {
+ mBranch->setVisible( FALSE );
+ }
+ }
+}
+
+void LLMenuItemBranchGL::setEnabledSubMenus(BOOL enabled)
+{
+ mBranch->setEnabledSubMenus(enabled);
+}
+
+void LLMenuItemBranchGL::draw()
+{
+ LLMenuItemGL::draw();
+ if (mBranch->getVisible() && !mBranch->getTornOff())
+ {
+ mHighlight = TRUE;
+ }
+}
+
+void LLMenuItemBranchGL::updateBranchParent(LLView* parentp)
+{
+ if (mBranch->getParent() == NULL)
+ {
+ // make the branch menu a sibling of my parent menu
+ mBranch->updateParent(parentp);
+ }
+}
+
+void LLMenuItemBranchGL::onVisibilityChange( BOOL curVisibilityIn )
+{
+ if (curVisibilityIn == FALSE && mBranch->getVisible() && !mBranch->getTornOff())
+ {
+ mBranch->setVisible(FALSE);
+ }
+}
+
+BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ if (getMenu()->getVisible() && mBranch->getVisible() && key == KEY_LEFT)
+ {
+ BOOL handled = mBranch->clearHoverItem();
+ if (handled && getMenu()->getTornOff())
+ {
+ ((LLFloater*)getMenu()->getParent())->setFocus(TRUE);
+ }
+ return handled;
+ }
+
+ if (mHighlight &&
+ getMenu()->getVisible() &&
+ // ignore keystrokes on background torn-off menus
+ (!getMenu()->getTornOff() || ((LLFloater*)getMenu()->getParent())->hasFocus()) &&
+ key == KEY_RIGHT && !mBranch->getHighlightedItem())
+ {
+ LLMenuItemGL* itemp = mBranch->highlightNextItem(NULL);
+ if (itemp)
+ {
+ return TRUE;
+ }
+ }
+
+ return LLMenuItemGL::handleKeyHere(key, mask, called_from_parent);
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemBranchDownGL
+//
+// The LLMenuItemBranchDownGL represents a menu item that has a
+// sub-menu. This is used to make menu bar menus.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemBranchDownGL : public LLMenuItemBranchGL
+{
+protected:
+
+public:
+ LLMenuItemBranchDownGL( const LLString& name, const LLString& label, LLMenuGL* branch,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM_BRANCH_DOWN; }
+ virtual LLString getWidgetTag() const { return LL_MENU_ITEM_BRANCH_DOWN_GL_TAG; }
+
+ virtual LLString getType() const { return "menu"; }
+
+ // returns the normal width of this control in pixels - this is
+ // used for calculating the widest item, as well as for horizontal
+ // arrangement.
+ virtual U32 getNominalWidth( void );
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void );
+
+ // set the hover status (called by it's menu) and if the object is
+ // active. This is used for behavior transfer.
+ virtual void setHighlight( BOOL highlight );
+
+ // determine if this object is active
+ virtual BOOL isActive( void ) const;
+
+ // LLView functionality
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ) {return FALSE; }
+ virtual void draw( void );
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
+};
+
+LLMenuItemBranchDownGL::LLMenuItemBranchDownGL( const LLString& name,
+ const LLString& label,
+ LLMenuGL* branch,
+ KEY key, MASK mask ) :
+ LLMenuItemBranchGL( name, label, branch, key, mask )
+{
+}
+
+// returns the normal width of this control in pixels - this is used
+// for calculating the widest item, as well as for horizontal
+// arrangement.
+U32 LLMenuItemBranchDownGL::getNominalWidth( void )
+{
+ U32 width = LEFT_PAD_PIXELS + LEFT_WIDTH_PIXELS + RIGHT_PAD_PIXELS;
+ width += mFont->getWidth( mLabel.getWString().c_str() );
+ return width;
+}
+
+// called to rebuild the draw label
+void LLMenuItemBranchDownGL::buildDrawLabel( void )
+{
+ mDrawAccelLabel.clear();
+ LLString st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+}
+
+// doIt() - do the primary funcationality of the menu item.
+void LLMenuItemBranchDownGL::doIt( void )
+{
+ if( mBranch->getVisible() && !mBranch->getTornOff() )
+ {
+ mBranch->setVisible( FALSE );
+ }
+ else
+ {
+ if (mBranch->getTornOff())
+ {
+ gFloaterView->bringToFront((LLFloater*)mBranch->getParent());
+ }
+ else
+ {
+ // We're showing the drop-down menu, so patch up its labels/rects
+ mBranch->arrange();
+
+ LLRect rect = mBranch->getRect();
+ S32 left = 0;
+ S32 top = mRect.mBottom;
+ localPointToOtherView(left, top, &left, &top, mBranch->getParent());
+
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+ mBranch->setRect( rect );
+ S32 x = 0;
+ S32 y = 0;
+ mBranch->localPointToScreen( 0, 0, &x, &y );
+ S32 delta_x = 0;
+
+ LLCoordScreen window_size;
+ LLWindow* windowp = getWindow();
+ windowp->getSize(&window_size);
+
+ S32 window_width = window_size.mX;
+ if( x > window_width - rect.getWidth() )
+ {
+ delta_x = (window_width - rect.getWidth()) - x;
+ }
+ mBranch->translate( delta_x, 0 );
+
+ //FIXME: get menuholder lookup working more generically
+ // hide existing menus
+ if (!mBranch->getTornOff())
+ {
+ ((LLMenuHolderGL*)mBranch->getParent())->hideMenus();
+ }
+
+ mBranch->setVisible( TRUE );
+ }
+ }
+}
+
+// set the hover status (called by it's menu)
+void LLMenuItemBranchDownGL::setHighlight( BOOL highlight )
+{
+ mHighlight = highlight;
+ if( !highlight)
+ {
+ if (mBranch->getTornOff())
+ {
+ ((LLFloater*)mBranch->getParent())->setFocus(FALSE);
+ mBranch->clearHoverItem();
+ }
+ else
+ {
+ mBranch->setVisible( FALSE );
+ }
+ }
+}
+
+// determine if this object is active
+// which, for branching menus, means the branch is open and has "focus"
+BOOL LLMenuItemBranchDownGL::isActive( void ) const
+{
+ if (mBranch->getTornOff())
+ {
+ return ((LLFloater*)mBranch->getParent())->hasFocus();
+ }
+ else
+ {
+ return mBranch->getVisible();
+ }
+}
+
+BOOL LLMenuItemBranchDownGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ doIt();
+ make_ui_sound("UISndClick");
+ return TRUE;
+}
+
+
+BOOL LLMenuItemBranchDownGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ BOOL branch_visible = mBranch->getVisible();
+ BOOL handled = mBranch->handleAcceleratorKey(key, mask);
+ if (handled && !branch_visible)
+ {
+ // flash this menu entry because we triggered an invisible menu item
+ LLMenuHolderGL::setActivatedItem(this);
+ }
+
+ return handled;
+}
+
+BOOL LLMenuItemBranchDownGL::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (mHighlight && getMenu()->getVisible() && mBranch->getVisible())
+ {
+ if (key == KEY_LEFT)
+ {
+ LLMenuItemGL* itemp = getMenu()->highlightPrevItem(this);
+ if (itemp)
+ {
+ itemp->doIt();
+ }
+
+ return TRUE;
+ }
+ else if (key == KEY_RIGHT)
+ {
+ LLMenuItemGL* itemp = getMenu()->highlightNextItem(this);
+ if (itemp)
+ {
+ itemp->doIt();
+ }
+
+ return TRUE;
+ }
+ else if (key == KEY_DOWN)
+ {
+ if (!mBranch->getTornOff())
+ {
+ mBranch->setVisible(TRUE);
+ }
+ mBranch->highlightNextItem(NULL);
+ return TRUE;
+ }
+ else if (key == KEY_UP)
+ {
+ if (!mBranch->getTornOff())
+ {
+ mBranch->setVisible(TRUE);
+ }
+ mBranch->highlightPrevItem(NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+void LLMenuItemBranchDownGL::draw( void )
+{
+ if( mHighlight )
+ {
+ glColor4fv( sHighlightBackground.mV );
+ gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ }
+
+ U8 font_style = mStyle;
+ if (LLMenuItemGL::sDropShadowText && getEnabled() && !mDrawTextDisabled )
+ {
+ font_style |= LLFontGL::DROP_SHADOW;
+ }
+
+ LLColor4 color;
+ if (mHighlight)
+ {
+ color = sHighlightForeground;
+ }
+ else if( mEnabled )
+ {
+ color = sEnabledColor;
+ }
+ else
+ {
+ color = sDisabledColor;
+ }
+ mFont->render( mLabel.getWString(), 0, (F32)mRect.getWidth() / 2.f, (F32)LABEL_BOTTOM_PAD_PIXELS, color,
+ LLFontGL::HCENTER, LLFontGL::BOTTOM, font_style );
+ // if branching menu is closed clear out highlight
+ if (mHighlight && ((!mBranch->getVisible() /*|| mBranch->getTornOff()*/) && !mGotHover))
+ {
+ setHighlight(FALSE);
+ }
+
+ // underline navigation key
+ BOOL draw_jump_key = gKeyboard->currentMask(FALSE) == MASK_ALT &&
+ (!getMenu()->getHighlightedItem() || !getMenu()->getHighlightedItem()->isActive()) &&
+ (!getMenu()->getTornOff()); // torn off menus don't use jump keys, too complicated
+
+ if (draw_jump_key)
+ {
+ LLString upper_case_label = mLabel.getString();
+ LLString::toUpper(upper_case_label);
+ std::string::size_type offset = upper_case_label.find(mJumpKey);
+ if (offset != std::string::npos)
+ {
+ S32 x_offset = llround((F32)mRect.getWidth() / 2.f - mFont->getWidthF32(mLabel.getString(), 0, S32_MAX) / 2.f);
+ S32 x_begin = x_offset + mFont->getWidth(mLabel, 0, offset);
+ S32 x_end = x_offset + mFont->getWidth(mLabel, 0, offset + 1);
+ gl_line_2d(x_begin, LABEL_BOTTOM_PAD_PIXELS, x_end, LABEL_BOTTOM_PAD_PIXELS);
+ }
+ }
+
+ // reset every frame so that we only show highlight
+ // when we get hover events on that frame
+ mGotHover = FALSE;
+}
+
+///============================================================================
+/// Class LLMenuGL
+///============================================================================
+
+// Default constructor
+LLMenuGL::LLMenuGL( const LLString& name, const LLString& label, LLViewHandle parent_floater_handle )
+: LLUICtrl( name, LLRect(), FALSE, NULL, NULL ),
+ mBackgroundColor( sDefaultBackgroundColor ),
+ mBgVisible( TRUE ),
+ mParentMenuItem( NULL ),
+ mLabel( label ),
+ mDropShadowed( TRUE ),
+ mHorizontalLayout( FALSE ),
+ mKeepFixedSize( FALSE ),
+ mLastMouseX(0),
+ mLastMouseY(0),
+ mMouseVelX(0),
+ mMouseVelY(0),
+ mTornOff(FALSE),
+ mTearOffItem(NULL),
+ mSpilloverBranch(NULL),
+ mSpilloverMenu(NULL),
+ mParentFloaterHandle(parent_floater_handle),
+ mJumpKey(KEY_NONE)
+{
+ mFadeTimer.stop();
+ setCanTearOff(TRUE, parent_floater_handle);
+ setTabStop(FALSE);
+}
+
+LLMenuGL::LLMenuGL( const LLString& label, LLViewHandle parent_floater_handle )
+: LLUICtrl( label, LLRect(), FALSE, NULL, NULL ),
+ mBackgroundColor( sDefaultBackgroundColor ),
+ mBgVisible( TRUE ),
+ mParentMenuItem( NULL ),
+ mLabel( label ),
+ mDropShadowed( TRUE ),
+ mHorizontalLayout( FALSE ),
+ mKeepFixedSize( FALSE ),
+ mLastMouseX(0),
+ mLastMouseY(0),
+ mMouseVelX(0),
+ mMouseVelY(0),
+ mTornOff(FALSE),
+ mTearOffItem(NULL),
+ mSpilloverBranch(NULL),
+ mSpilloverMenu(NULL),
+ mParentFloaterHandle(parent_floater_handle),
+ mJumpKey(KEY_NONE)
+{
+ mFadeTimer.stop();
+ setCanTearOff(TRUE, parent_floater_handle);
+ setTabStop(FALSE);
+}
+
+// Destroys the object
+LLMenuGL::~LLMenuGL( void )
+{
+ // delete the branch, as it might not be in view hierarchy
+ // leave the menu, because it is always in view hierarchy
+ delete mSpilloverBranch;
+ mJumpKeys.clear();
+}
+
+void LLMenuGL::setCanTearOff(BOOL tear_off, LLViewHandle parent_floater_handle )
+{
+ if (tear_off && mTearOffItem == NULL)
+ {
+ mTearOffItem = new LLMenuItemTearOffGL(parent_floater_handle);
+ mItems.insert(mItems.begin(), mTearOffItem);
+ addChildAtEnd(mTearOffItem);
+ arrange();
+ }
+ else if (!tear_off && mTearOffItem != NULL)
+ {
+ mItems.remove(mTearOffItem);
+ removeChild(mTearOffItem);
+ delete mTearOffItem;
+ mTearOffItem = NULL;
+ arrange();
+ }
+}
+
+// virtual
+LLXMLNodePtr LLMenuGL::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLView::getXML();
+
+ // Attributes
+
+ node->createChild("opaque", TRUE)->setBoolValue(mBgVisible);
+
+ node->createChild("drop_shadow", TRUE)->setBoolValue(mDropShadowed);
+
+ node->createChild("tear_off", TRUE)->setBoolValue((mTearOffItem != NULL));
+
+ if (mBgVisible)
+ {
+ // TomY TODO: this should save out the color control name
+ node->createChild("color", TRUE)->setFloatValue(4, mBackgroundColor.mV);
+ }
+
+ // Contents
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLView* child = (*item_iter);
+ LLMenuItemGL* item = (LLMenuItemGL*)child;
+
+ LLXMLNodePtr child_node = item->getXML();
+
+ node->addChild(child_node);
+ }
+
+ return node;
+}
+
+void LLMenuGL::parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory)
+{
+ if (child->hasName(LL_MENU_GL_TAG))
+ {
+ // SUBMENU
+ LLMenuGL *submenu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory);
+ appendMenu(submenu);
+ if (LLMenuGL::sDefaultMenuContainer != NULL)
+ {
+ submenu->updateParent(LLMenuGL::sDefaultMenuContainer);
+ }
+ else
+ {
+ submenu->updateParent(parent);
+ }
+ }
+ else if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) ||
+ child->hasName(LL_MENU_ITEM_CHECK_GL_TAG) ||
+ child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
+ {
+ LLMenuItemGL *item = NULL;
+
+ LLString type;
+ LLString item_name;
+ LLString source_label;
+ LLString item_label;
+ KEY jump_key = KEY_NONE;
+
+ child->getAttributeString("type", type);
+ child->getAttributeString("name", item_name);
+ child->getAttributeString("label", source_label);
+
+ // parse jump key out of label
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("_");
+ tokenizer tokens(source_label, sep);
+ tokenizer::iterator token_iter;
+ S32 token_count = 0;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ item_label += (*token_iter);
+ if (token_count > 0)
+ {
+ jump_key = (*token_iter).c_str()[0];
+ }
+ ++token_count;
+ }
+
+
+ if (child->hasName(LL_MENU_ITEM_SEPARATOR_GL_TAG))
+ {
+ appendSeparator(item_name);
+ }
+ else
+ {
+ // ITEM
+ if (child->hasName(LL_MENU_ITEM_CALL_GL_TAG) ||
+ child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
+ {
+ MASK mask = 0;
+ LLString shortcut;
+ child->getAttributeString("shortcut", shortcut);
+ if (shortcut.find("control") != shortcut.npos)
+ {
+ mask |= MASK_CONTROL;
+ }
+ if (shortcut.find("alt") != shortcut.npos)
+ {
+ mask |= MASK_ALT;
+ }
+ if (shortcut.find("shift") != shortcut.npos)
+ {
+ mask |= MASK_SHIFT;
+ }
+ S32 pipe_pos = shortcut.rfind("|");
+ LLString key_str = shortcut.substr(pipe_pos+1);
+
+ KEY key = KEY_NONE;
+ LLKeyboard::keyFromString(key_str, &key);
+
+ LLMenuItemCallGL *new_item;
+ LLXMLNodePtr call_child;
+
+ if (child->hasName(LL_MENU_ITEM_CHECK_GL_TAG))
+ {
+ LLString control_name;
+ child->getAttributeString("control_name", control_name);
+
+ new_item = new LLMenuItemCheckGL(item_name, item_label, 0, 0, control_name, parent, 0, key, mask);
+
+ for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling())
+ {
+ if (call_child->hasName("on_check"))
+ {
+ LLString callback_name;
+ LLString control_name = "";
+ if (call_child->hasAttribute("function"))
+ {
+ call_child->getAttributeString("function", callback_name);
+
+ control_name = callback_name;
+
+ LLString callback_data = item_name;
+ if (call_child->hasAttribute("userdata"))
+ {
+ call_child->getAttributeString("userdata", callback_data);
+ if (!callback_data.empty())
+ {
+ control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
+ }
+ }
+
+ LLSD userdata;
+ userdata["control"] = control_name;
+ userdata["data"] = callback_data;
+
+ LLSimpleListener* callback = parent->getListenerByName(callback_name);
+
+ if (!callback) continue;
+
+ new_item->addListener(callback, "on_build", userdata);
+ }
+ else if (call_child->hasAttribute("control"))
+ {
+ call_child->getAttributeString("control", control_name);
+ }
+ else
+ {
+ continue;
+ }
+ LLControlBase *control = parent->findControl(control_name);
+ if (!control)
+ {
+ parent->addBoolControl(control_name, FALSE);
+ }
+ ((LLMenuItemCheckGL*)new_item)->setCheckedControl(control_name, parent);
+ }
+ }
+ }
+ else
+ {
+ new_item = new LLMenuItemCallGL(item_name, item_label, 0, 0, 0, 0, key, mask);
+ }
+
+ for (call_child = child->getFirstChild(); call_child.notNull(); call_child = call_child->getNextSibling())
+ {
+ if (call_child->hasName("on_click"))
+ {
+ LLString callback_name;
+ call_child->getAttributeString("function", callback_name);
+
+ LLString callback_data = item_name;
+ if (call_child->hasAttribute("userdata"))
+ {
+ call_child->getAttributeString("userdata", callback_data);
+ }
+
+ LLSimpleListener* callback = parent->getListenerByName(callback_name);
+
+ if (!callback) continue;
+
+ new_item->addListener(callback, "on_click", callback_data);
+ }
+ if (call_child->hasName("on_enable"))
+ {
+ LLString callback_name;
+ LLString control_name = "";
+ if (call_child->hasAttribute("function"))
+ {
+ call_child->getAttributeString("function", callback_name);
+
+ control_name = callback_name;
+
+ LLString callback_data = "";
+ if (call_child->hasAttribute("userdata"))
+ {
+ call_child->getAttributeString("userdata", callback_data);
+ if (!callback_data.empty())
+ {
+ control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
+ }
+ }
+
+ LLSD userdata;
+ userdata["control"] = control_name;
+ userdata["data"] = callback_data;
+
+ LLSimpleListener* callback = parent->getListenerByName(callback_name);
+
+ if (!callback) continue;
+
+ new_item->addListener(callback, "on_build", userdata);
+ }
+ else if (call_child->hasAttribute("control"))
+ {
+ call_child->getAttributeString("control", control_name);
+ }
+ else
+ {
+ continue;
+ }
+ new_item->setEnabledControl(control_name, parent);
+ }
+ if (call_child->hasName("on_visible"))
+ {
+ LLString callback_name;
+ LLString control_name = "";
+ if (call_child->hasAttribute("function"))
+ {
+ call_child->getAttributeString("function", callback_name);
+
+ control_name = callback_name;
+
+ LLString callback_data = "";
+ if (call_child->hasAttribute("userdata"))
+ {
+ call_child->getAttributeString("userdata", callback_data);
+ if (!callback_data.empty())
+ {
+ control_name = llformat("%s(%s)", callback_name.c_str(), callback_data.c_str());
+ }
+ }
+
+ LLSD userdata;
+ userdata["control"] = control_name;
+ userdata["data"] = callback_data;
+
+ LLSimpleListener* callback = parent->getListenerByName(callback_name);
+
+ if (!callback) continue;
+
+ new_item->addListener(callback, "on_build", userdata);
+ }
+ else if (call_child->hasAttribute("control"))
+ {
+ call_child->getAttributeString("control", control_name);
+ }
+ else
+ {
+ continue;
+ }
+ new_item->setVisibleControl(control_name, parent);
+ }
+ }
+ item = new_item;
+ item->setLabel(item_label);
+ item->setJumpKey(jump_key);
+ }
+
+ if (item != NULL)
+ {
+ append(item);
+ }
+ }
+ }
+}
+
+// static
+LLView* LLMenuGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("menu");
+ node->getAttributeString("name", name);
+
+ LLString label = name;
+ node->getAttributeString("label", label);
+
+ // parse jump key out of label
+ LLString new_menu_label;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("_");
+ tokenizer tokens(label, sep);
+ tokenizer::iterator token_iter;
+
+ KEY jump_key = KEY_NONE;
+ S32 token_count = 0;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ new_menu_label += (*token_iter);
+ if (token_count > 0)
+ {
+ jump_key = (*token_iter).c_str()[0];
+ }
+ ++token_count;
+ }
+
+ BOOL opaque = FALSE;
+ node->getAttributeBOOL("opaque", opaque);
+
+ LLMenuGL *menu = new LLMenuGL(name, new_menu_label);
+
+ menu->setJumpKey(jump_key);
+
+ BOOL tear_off = FALSE;
+ node->getAttributeBOOL("tear_off", tear_off);
+ menu->setCanTearOff(tear_off);
+
+ if (node->hasAttribute("drop_shadow"))
+ {
+ BOOL drop_shadow = FALSE;
+ node->getAttributeBOOL("drop_shadow", drop_shadow);
+ menu->setDropShadowed(drop_shadow);
+ }
+
+ menu->setBackgroundVisible(opaque);
+ LLColor4 color(0,0,0,1);
+ if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color))
+ {
+ menu->setBackgroundColor(color);
+ }
+
+ BOOL create_jump_keys = FALSE;
+ node->getAttributeBOOL("create_jump_keys", create_jump_keys);
+
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ menu->parseChildXML(child, parent, factory);
+ }
+
+ if (create_jump_keys)
+ {
+ menu->createJumpKeys();
+ }
+ return menu;
+}
+
+// control the color scheme
+void LLMenuGL::setDefaultBackgroundColor( const LLColor4& color )
+{
+ sDefaultBackgroundColor = color;
+}
+
+void LLMenuGL::setBackgroundColor( const LLColor4& color )
+{
+ mBackgroundColor = color;
+}
+
+// rearrange the child rects so they fit the shape of the menu.
+void LLMenuGL::arrange( void )
+{
+ // calculate the height & width, and set our rect based on that
+ // information.
+ LLRect initial_rect = mRect;
+
+ U32 width = 0, height = MENU_ITEM_PADDING;
+
+ cleanupSpilloverBranch();
+
+ if( mItems.size() )
+ {
+ U32 max_width = (getParent() != NULL) ? getParent()->getRect().getWidth() : U32_MAX;
+ U32 max_height = (getParent() != NULL) ? getParent()->getRect().getHeight() : U32_MAX;
+ //FIXME: create the item first and then ask for its dimensions?
+ S32 spillover_item_width = PLAIN_PAD_PIXELS + LLFontGL::sSansSerif->getWidth( "More" );
+ S32 spillover_item_height = llround(LLFontGL::sSansSerif->getLineHeight()) + MENU_ITEM_PADDING;
+
+ if (mHorizontalLayout)
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff() && width + (*item_iter)->getNominalWidth() > max_width - spillover_item_width)
+ {
+ // no room for any more items
+ createSpilloverBranch();
+
+ item_list_t::iterator spillover_iter;
+ for (spillover_iter = item_iter; spillover_iter != mItems.end(); ++spillover_iter)
+ {
+ LLMenuItemGL* itemp = (*spillover_iter);
+ removeChild(itemp);
+ mSpilloverMenu->append(itemp);
+ }
+ mItems.erase(item_iter, mItems.end());
+
+ mItems.push_back(mSpilloverBranch);
+ addChild(mSpilloverBranch);
+ height = llmax(height, mSpilloverBranch->getNominalHeight());
+ width += mSpilloverBranch->getNominalWidth();
+
+ break;
+ }
+ else
+ {
+ // track our rect
+ height = llmax(height, (*item_iter)->getNominalHeight());
+ width += (*item_iter)->getNominalWidth();
+ }
+ }
+ }
+ }
+ else
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ if (!getTornOff() && height + (*item_iter)->getNominalHeight() > max_height - spillover_item_height)
+ {
+ // no room for any more items
+ createSpilloverBranch();
+
+ item_list_t::iterator spillover_iter;
+ for (spillover_iter= item_iter; spillover_iter != mItems.end(); ++spillover_iter)
+ {
+ LLMenuItemGL* itemp = (*spillover_iter);
+ removeChild(itemp);
+ mSpilloverMenu->append(itemp);
+ }
+ mItems.erase(item_iter, mItems.end());
+ mItems.push_back(mSpilloverBranch);
+ addChild(mSpilloverBranch);
+ height += mSpilloverBranch->getNominalHeight();
+ width = llmax( width, mSpilloverBranch->getNominalWidth() );
+
+ break;
+ }
+ else
+ {
+ // track our rect
+ height += (*item_iter)->getNominalHeight();
+ width = llmax( width, (*item_iter)->getNominalWidth() );
+ }
+ }
+ }
+ }
+
+ mRect.mRight = mRect.mLeft + width;
+ mRect.mTop = mRect.mBottom + height;
+
+ S32 cur_height = (S32)llmin(max_height, height);
+ S32 cur_width = 0;
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ // setup item rect to hold label
+ LLRect rect;
+ if (mHorizontalLayout)
+ {
+ rect.setLeftTopAndSize( cur_width, height, (*item_iter)->getNominalWidth(), height);
+ cur_width += (*item_iter)->getNominalWidth();
+ }
+ else
+ {
+ rect.setLeftTopAndSize( 0, cur_height, width, (*item_iter)->getNominalHeight());
+ cur_height -= (*item_iter)->getNominalHeight();
+ }
+ (*item_iter)->setRect( rect );
+ (*item_iter)->buildDrawLabel();
+ }
+ }
+ }
+ if (mKeepFixedSize)
+ {
+ reshape(initial_rect.getWidth(), initial_rect.getHeight());
+ }
+}
+
+void LLMenuGL::createSpilloverBranch()
+{
+ if (!mSpilloverBranch)
+ {
+ // should be NULL but delete anyway
+ delete mSpilloverMenu;
+ // technically, you can't tear off spillover menus, but we're passing the handle
+ // along just to be safe
+ mSpilloverMenu = new LLMenuGL("More", "More", mParentFloaterHandle);
+ mSpilloverMenu->updateParent(getParent());
+ // Inherit colors
+ mSpilloverMenu->setBackgroundColor( mBackgroundColor );
+ mSpilloverMenu->setCanTearOff(FALSE);
+
+ mSpilloverBranch = new LLMenuItemBranchGL("More", "More", mSpilloverMenu);
+ mSpilloverBranch->setFontStyle(LLFontGL::ITALIC);
+ }
+}
+
+void LLMenuGL::cleanupSpilloverBranch()
+{
+ if (mSpilloverBranch && mSpilloverBranch->getParent() == this)
+ {
+ // head-recursion to propagate items back up to root menu
+ mSpilloverMenu->cleanupSpilloverBranch();
+
+ removeChild(mSpilloverBranch);
+
+ item_list_t::iterator found_iter = std::find(mItems.begin(), mItems.end(), mSpilloverBranch);
+ if (found_iter != mItems.end())
+ {
+ mItems.erase(found_iter);
+ }
+
+ // pop off spillover items
+ while (mSpilloverMenu->getItemCount())
+ {
+ LLMenuItemGL* itemp = mSpilloverMenu->getItem(0);
+ mSpilloverMenu->removeChild(itemp);
+ mSpilloverMenu->mItems.erase(mSpilloverMenu->mItems.begin());
+ // put them at the end of our own list
+ mItems.push_back(itemp);
+ addChild(itemp);
+ }
+ }
+}
+
+void LLMenuGL::createJumpKeys()
+{
+ mJumpKeys.clear();
+
+ std::set<LLString> unique_words;
+ std::set<LLString> shared_words;
+
+ item_list_t::iterator item_it;
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ LLString uppercase_label = (*item_it)->getLabel();
+ LLString::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ if (unique_words.find(*token_iter) != unique_words.end())
+ {
+ // this word exists in more than one menu instance
+ shared_words.insert(*token_iter);
+ }
+ else
+ {
+ // we have a new word, keep track of it
+ unique_words.insert(*token_iter);
+ }
+ }
+ }
+
+ // pre-assign specified jump keys
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ KEY jump_key = (*item_it)->getJumpKey();
+ if(jump_key != KEY_NONE)
+ {
+ if (mJumpKeys.find(jump_key) == mJumpKeys.end())
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ }
+ else
+ {
+ // this key is already spoken for,
+ // so we need to reassign it below
+ (*item_it)->setJumpKey(KEY_NONE);
+ }
+ }
+ }
+
+ for(item_it = mItems.begin(); item_it != mItems.end(); ++item_it)
+ {
+ // skip over items that already have assigned jump keys
+ if ((*item_it)->getJumpKey() != KEY_NONE)
+ {
+ continue;
+ }
+ LLString uppercase_label = (*item_it)->getLabel();
+ LLString::toUpper(uppercase_label);
+
+ tokenizer tokens(uppercase_label, sep);
+ tokenizer::iterator token_iter;
+
+ BOOL found_key = FALSE;
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ LLString uppercase_word = *token_iter;
+
+ // this word is not shared with other menu entries...
+ if (shared_words.find(*token_iter) == shared_words.end())
+ {
+ S32 i;
+ for(i = 0; i < (S32)uppercase_word.size(); i++)
+ {
+ char jump_key = uppercase_word[i];
+
+ if (LLStringOps::isDigit(jump_key) || LLStringOps::isUpper(jump_key) &&
+ mJumpKeys.find(jump_key) == mJumpKeys.end())
+ {
+ mJumpKeys.insert(std::pair<KEY, LLMenuItemGL*>(jump_key, (*item_it)));
+ (*item_it)->setJumpKey(jump_key);
+ found_key = TRUE;
+ break;
+ }
+ }
+ }
+ if (found_key)
+ {
+ break;
+ }
+ }
+ }
+}
+
+// remove all items on the menu
+void LLMenuGL::empty( void )
+{
+ mItems.clear();
+
+ deleteAllChildren();
+
+}
+
+// Adjust rectangle of the menu
+void LLMenuGL::setLeftAndBottom(S32 left, S32 bottom)
+{
+ mRect.mLeft = left;
+ mRect.mBottom = bottom;
+ arrange();
+}
+
+void LLMenuGL::handleJumpKey(KEY key)
+{
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ clearHoverItem();
+ // force highlight to close old menus and open and sub-menus
+ found_it->second->setHighlight(TRUE);
+ found_it->second->doIt();
+ if (!found_it->second->isActive() && !getTornOff())
+ {
+ // parent is a menu holder, because this is not a menu bar
+ ((LLMenuHolderGL*)getParent())->hideMenus();
+ }
+ }
+}
+
+
+// Add the menu item to this menu.
+BOOL LLMenuGL::append( LLMenuItemGL* item )
+{
+ mItems.push_back( item );
+ addChild( item );
+ arrange();
+ return TRUE;
+}
+
+// add a separator to this menu
+BOOL LLMenuGL::appendSeparator( const LLString &separator_name )
+{
+ LLMenuItemGL* separator = new LLMenuItemSeparatorGL(separator_name);
+ return append( separator );
+}
+
+// add a menu - this will create a cascading menu
+BOOL LLMenuGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ llerrs << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << llendl;
+ }
+ BOOL success = TRUE;
+
+ LLMenuItemBranchGL* branch = NULL;
+ branch = new LLMenuItemBranchGL( menu->getName(), menu->getLabel(), menu );
+ branch->setJumpKey(menu->getJumpKey());
+ success &= append( branch );
+
+ // Inherit colors
+ menu->setBackgroundColor( mBackgroundColor );
+
+ return success;
+}
+
+void LLMenuGL::setEnabledSubMenus(BOOL enable)
+{
+ setEnabled(enable);
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->setEnabledSubMenus( enable );
+ }
+}
+
+// setItemEnabled() - pass the label and the enable flag for a menu
+// item. TRUE will make sure it's enabled, FALSE will disable it.
+void LLMenuGL::setItemEnabled( const LLString& name, BOOL enable )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setEnabled( enable );
+ (*item_iter)->setEnabledSubMenus( enable );
+ break;
+ }
+ }
+}
+
+void LLMenuGL::setItemVisible( const LLString& name, BOOL visible )
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if( (*item_iter)->getName() == name )
+ {
+ (*item_iter)->setVisible( visible );
+ break;
+ }
+ }
+}
+
+void LLMenuGL::setItemLastSelected(LLMenuItemGL* item)
+{
+ if (getVisible())
+ {
+ LLMenuHolderGL::setActivatedItem(item);
+ }
+
+ // fix the checkmarks
+ item->buildDrawLabel();
+}
+
+// Set whether drop shadowed
+void LLMenuGL::setDropShadowed( const BOOL shadowed )
+{
+ mDropShadowed = shadowed;
+}
+
+void LLMenuGL::setTornOff(BOOL torn_off)
+{
+ mTornOff = torn_off;
+}
+
+U32 LLMenuGL::getItemCount()
+{
+ return mItems.size();
+}
+
+LLMenuItemGL* LLMenuGL::getItem(S32 number)
+{
+ if (number >= 0 && number < (S32)mItems.size())
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if (number == 0)
+ {
+ return (*item_iter);
+ }
+ number--;
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::getHighlightedItem()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getHighlight())
+ {
+ return (*item_iter);
+ }
+ }
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
+{
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ ((LLFloater*)getParent())->setFocus(TRUE);
+ }
+
+ item_list_t::iterator cur_item_iter;
+ for (cur_item_iter = mItems.begin(); cur_item_iter != mItems.end(); ++cur_item_iter)
+ {
+ if( (*cur_item_iter) == cur_item)
+ {
+ break;
+ }
+ }
+
+ item_list_t::iterator next_item_iter;
+ if (cur_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ else
+ {
+ next_item_iter = cur_item_iter;
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ // when first highlighting a menu, skip over tear off menu item
+ if (mTearOffItem && !cur_item)
+ {
+ // we know the first item is the tear off menu item
+ cur_item_iter = mItems.begin();
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled items
+ if ((*next_item_iter)->getEnabled() && (*next_item_iter)->getName() != SEPARATOR_NAME)
+ {
+ if (cur_item)
+ {
+ cur_item->setHighlight(FALSE);
+ }
+ (*next_item_iter)->setHighlight(TRUE);
+ return (*next_item_iter);
+ }
+
+
+ if (!skip_disabled || next_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ next_item_iter++;
+ if (next_item_iter == mItems.end())
+ {
+ if (cur_item_iter == mItems.end())
+ {
+ break;
+ }
+ next_item_iter = mItems.begin();
+ }
+ }
+
+ return NULL;
+}
+
+LLMenuItemGL* LLMenuGL::highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled)
+{
+ // highlighting first item on a torn off menu is the
+ // same as giving focus to it
+ if (!cur_item && getTornOff())
+ {
+ ((LLFloater*)getParent())->setFocus(TRUE);
+ }
+
+ item_list_t::reverse_iterator cur_item_iter;
+ for (cur_item_iter = mItems.rbegin(); cur_item_iter != mItems.rend(); ++cur_item_iter)
+ {
+ if( (*cur_item_iter) == cur_item)
+ {
+ break;
+ }
+ }
+
+ item_list_t::reverse_iterator prev_item_iter;
+ if (cur_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+ }
+ else
+ {
+ prev_item_iter = cur_item_iter;
+ prev_item_iter++;
+ if (prev_item_iter == mItems.rend())
+ {
+ prev_item_iter = mItems.rbegin();
+ }
+ }
+
+ while(1)
+ {
+ // skip separators and disabled items
+ if ((*prev_item_iter)->getEnabled() && (*prev_item_iter)->getName() != SEPARATOR_NAME)
+ {
+ if (cur_item)
+ {
+ cur_item->setHighlight(FALSE);
+ }
+ (*prev_item_iter)->setHighlight(TRUE);
+ return (*prev_item_iter);
+ }
+
+ if (!skip_disabled || prev_item_iter == cur_item_iter)
+ {
+ break;
+ }
+
+ prev_item_iter++;
+ if (prev_item_iter == mItems.rend())
+ {
+ if (cur_item_iter == mItems.rend())
+ {
+ break;
+ }
+
+ prev_item_iter = mItems.rbegin();
+ }
+ }
+
+ return NULL;
+}
+
+void LLMenuGL::buildDrawLabels()
+{
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->buildDrawLabel();
+ }
+}
+
+void LLMenuGL::updateParent(LLView* parentp)
+{
+ if (getParent())
+ {
+ getParent()->removeChild(this);
+ }
+ parentp->addChild(this);
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ (*item_iter)->updateBranchParent(parentp);
+ }
+}
+
+// LLView functionality
+BOOL LLMenuGL::handleKey( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ // Pass down even if not visible
+ if( mEnabled && called_from_parent )
+ {
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->handleKey(key, mask, TRUE))
+ {
+ handled = TRUE;
+ break;
+ }
+ }
+ }
+
+ if( !handled )
+ {
+ handled = handleKeyHere( key, mask, called_from_parent );
+ if (handled && LLView::sDebugKeys)
+ {
+ llinfos << "Key handled by " << getName() << llendl;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask)
+{
+ // Pass down even if not visible
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* itemp = *item_iter;
+ if (itemp->handleAcceleratorKey(key, mask))
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLMenuGL::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ if (key < KEY_SPECIAL && getVisible() && getEnabled() && mask == MASK_ALT)
+ {
+ if (getTornOff())
+ {
+ // torn off menus do not handle jump keys (for now, the interaction is complex)
+ return FALSE;
+ }
+ handleJumpKey(key);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLMenuGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ // leave submenu in place if slope of mouse < MAX_MOUSE_SLOPE_SUB_MENU
+ S32 mouse_delta_x = x - mLastMouseX;
+ S32 mouse_delta_y = y - mLastMouseY;
+ LLVector2 mouse_dir((F32)mouse_delta_x, (F32)mouse_delta_y);
+ mouse_dir.normVec();
+ 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));
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ // don't change menu focus unless mouse is moving or alt key is not held down
+ if ((gKeyboard->currentMask(FALSE) != MASK_ALT ||
+ llabs(mMouseVelX) > 0 ||
+ llabs(mMouseVelY) > 0) &&
+ (!mHasSelection ||
+ //(mouse_delta_x == 0 && mouse_delta_y == 0) ||
+ (mMouseVelX < 0) ||
+ llabs((F32)mMouseVelY) / llabs((F32)mMouseVelX) > MAX_MOUSE_SLOPE_SUB_MENU))
+ {
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
+ }
+ }
+ }
+
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ //RN: always call handleHover to track mGotHover status
+ // but only set highlight when mouse is moving
+ if( viewp->getVisible() &&
+ viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ // moving mouse always highlights new item
+ if (mouse_delta_x != 0 || mouse_delta_y != 0)
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
+ }
+ mHasSelection = TRUE;
+ }
+ }
+ }
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+BOOL LLMenuGL::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if( LLView::childrenHandleMouseUp( x, y, mask ) )
+ {
+ if (!getTornOff())
+ {
+ ((LLMenuHolderGL*)getParent())->hideMenus();
+ }
+ }
+
+ return TRUE;
+}
+
+void LLMenuGL::draw( void )
+{
+ if (mDropShadowed && !mTornOff)
+ {
+ gl_drop_shadow(0, mRect.getHeight(), mRect.getWidth(), 0,
+ LLUI::sColorsGroup->getColor("ColorDropShadow"),
+ LLUI::sConfigGroup->getS32("DropShadowFloater") );
+ }
+
+ LLColor4 bg_color = mBackgroundColor;
+
+ if( mBgVisible )
+ {
+ gl_rect_2d( 0, mRect.getHeight(), mRect.getWidth(), 0, mBackgroundColor );
+ }
+ LLView::draw();
+}
+
+void LLMenuGL::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
+{
+ glColor4fv( color.mV );
+ LLRect item_rect = itemp->getRect();
+ gl_rect_2d( 0, item_rect.getHeight(), item_rect.getWidth(), 0);
+}
+
+void LLMenuGL::setVisible(BOOL visible)
+{
+ if (visible != getVisible())
+ {
+ if (!visible)
+ {
+ mFadeTimer.start();
+ clearHoverItem();
+ }
+ else
+ {
+ mHasSelection = FALSE;
+ mFadeTimer.stop();
+ }
+
+ //gViewerWindow->finishFastFrame();
+ LLView::setVisible(visible);
+ }
+}
+
+LLMenuGL* LLMenuGL::getChildMenuByName(const LLString& name, BOOL recurse) const
+{
+ LLView* view = getChildByName(name, recurse);
+ if (view)
+ {
+ if (view->getWidgetType() == WIDGET_TYPE_MENU_ITEM_BRANCH)
+ {
+ LLMenuItemBranchGL *branch = (LLMenuItemBranchGL *)view;
+ return branch->getBranch();
+ }
+ if (view->getWidgetType() == WIDGET_TYPE_MENU || view->getWidgetType() == WIDGET_TYPE_PIE_MENU)
+ {
+ return (LLMenuGL*)view;
+ }
+ }
+ llwarns << "Child Menu " << name << " not found in menu " << mName << llendl;
+ return NULL;
+}
+
+BOOL LLMenuGL::clearHoverItem(BOOL include_active)
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLMenuItemGL* itemp = (LLMenuItemGL*)*child_it;
+ if (itemp->getHighlight() && (include_active || !itemp->isActive()))
+ {
+ itemp->setHighlight(FALSE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void hide_top_view( LLView* view )
+{
+ if( view ) view->setVisible( FALSE );
+}
+
+
+// static
+void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y)
+{
+ const S32 HPAD = 2;
+ LLRect rect = menu->getRect();
+ //LLView* cur_view = spawning_view;
+ S32 left = x + HPAD;
+ S32 top = y;
+ spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent());
+ rect.setLeftTopAndSize( left, top,
+ rect.getWidth(), rect.getHeight() );
+
+
+ //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight());
+ menu->setRect( rect );
+
+ S32 bottom;
+ left = rect.mLeft;
+ bottom = rect.mBottom;
+ //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom,
+ // &left, &bottom );
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+ if( bottom < 0 )
+ {
+ delta_y = -bottom;
+ }
+
+ S32 parent_width = menu->getParent()->getRect().getWidth();
+ if( left > parent_width - rect.getWidth() )
+ {
+ // At this point, we need to move the context menu to the
+ // other side of the mouse.
+ //delta_x = (window_width - rect.getWidth()) - x;
+ delta_x = -(rect.getWidth() + 2 * HPAD);
+ }
+ menu->translate( delta_x, delta_y );
+ menu->setVisible( TRUE );
+}
+
+//-----------------------------------------------------------------------------
+// class LLPieMenuBranch
+// A branch to another pie menu
+//-----------------------------------------------------------------------------
+class LLPieMenuBranch : public LLMenuItemGL
+{
+public:
+ LLPieMenuBranch(const LLString& name, const LLString& label, LLPieMenu* branch,
+ enabled_callback ecb, void* user_data);
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_PIE_MENU_BRANCH; }
+ virtual LLString getWidgetTag() const { return LL_PIE_MENU_BRANCH_TAG; }
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void );
+
+ LLPieMenu* getBranch() { return mBranch; }
+
+protected:
+ LLPieMenu* mBranch;
+ enabled_callback mEnabledCallback;
+ void* mUserData;
+};
+
+LLPieMenuBranch::LLPieMenuBranch(const LLString& name,
+ const LLString& label,
+ LLPieMenu* branch,
+ enabled_callback ecb,
+ void* user_data)
+: LLMenuItemGL( name, label, KEY_NONE, MASK_NONE ),
+ mBranch( branch ),
+ mEnabledCallback( ecb ),
+ mUserData(user_data)
+{
+ mBranch->hide(FALSE);
+ mBranch->setParentMenuItem(this);
+}
+
+// called to rebuild the draw label
+void LLPieMenuBranch::buildDrawLabel( void )
+{
+ if(mEnabledCallback)
+ {
+ setEnabled(mEnabledCallback(mUserData));
+ mDrawTextDisabled = FALSE;
+ }
+ else
+ {
+ // default enablement is this -- if any of the subitems are
+ // enabled, this item is enabled. JC
+ U32 sub_count = mBranch->getItemCount();
+ U32 i;
+ BOOL any_enabled = FALSE;
+ for (i = 0; i < sub_count; i++)
+ {
+ LLMenuItemGL* item = mBranch->getItem(i);
+ item->buildDrawLabel();
+ if (item->getEnabled() && !item->getDrawTextDisabled() )
+ {
+ any_enabled = TRUE;
+ break;
+ }
+ }
+ mDrawTextDisabled = !any_enabled;
+ setEnabled(TRUE);
+ }
+
+ mDrawAccelLabel.clear();
+ LLString st = mDrawAccelLabel;
+ appendAcceleratorString( st );
+ mDrawAccelLabel = st;
+
+ // No special branch suffix
+ mDrawBranchLabel.clear();
+}
+
+// doIt() - do the primary funcationality of the menu item.
+void LLPieMenuBranch::doIt( void )
+{
+ LLPieMenu *parent = (LLPieMenu *)getParent();
+
+ LLRect rect = parent->getRect();
+ S32 center_x;
+ S32 center_y;
+ parent->localPointToScreen(rect.getWidth() / 2, rect.getHeight() / 2, &center_x, &center_y);
+
+ parent->hide(TRUE);
+ mBranch->show( center_x, center_y, FALSE );
+}
+
+//-----------------------------------------------------------------------------
+// class LLPieMenu
+// A circular menu of items, icons, etc.
+//-----------------------------------------------------------------------------
+LLPieMenu::LLPieMenu(const LLString& name, const LLString& label)
+: LLMenuGL(name, label),
+ mFirstMouseDown(FALSE),
+ mUseInfiniteRadius(FALSE),
+ mHoverItem(NULL),
+ mHoverThisFrame(FALSE),
+ mOuterRingAlpha(1.f),
+ mCurRadius(0.f),
+ mRightMouseDown(FALSE)
+{
+ LLMenuGL::setVisible(FALSE);
+ setCanTearOff(FALSE);
+}
+
+LLPieMenu::LLPieMenu(const LLString& name)
+: LLMenuGL(name, name),
+ mFirstMouseDown(FALSE),
+ mUseInfiniteRadius(FALSE),
+ mHoverItem(NULL),
+ mHoverThisFrame(FALSE),
+ mOuterRingAlpha(1.f),
+ mCurRadius(0.f),
+ mRightMouseDown(FALSE)
+{
+ LLMenuGL::setVisible(FALSE);
+ setCanTearOff(FALSE);
+}
+
+// virtual
+LLPieMenu::~LLPieMenu()
+{ }
+
+
+EWidgetType LLPieMenu::getWidgetType() const
+{
+ return WIDGET_TYPE_PIE_MENU;
+}
+
+LLString LLPieMenu::getWidgetTag() const
+{
+ return LL_PIE_MENU_TAG;
+}
+
+void LLPieMenu::initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *factory)
+{
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName(LL_PIE_MENU_TAG))
+ {
+ // SUBMENU
+ LLString name("menu");
+ child->getAttributeString("name", name);
+ LLString label(name);
+ child->getAttributeString("label", label);
+
+ LLPieMenu *submenu = new LLPieMenu(name, label);
+ appendMenu(submenu);
+ submenu->initXML(child, context, factory);
+ }
+ else
+ {
+ parseChildXML(child, context, factory);
+ }
+ }
+}
+
+// virtual
+void LLPieMenu::setVisible(BOOL visible)
+{
+ if (!visible)
+ {
+ hide(FALSE);
+ }
+}
+
+BOOL LLPieMenu::handleHover( S32 x, S32 y, MASK mask )
+{
+ // This is mostly copied from the llview class, but it continues
+ // the hover handle code after a hover handler has been found.
+ BOOL handled = FALSE;
+
+ // If we got a hover event, we've already moved the cursor
+ // for any menu shifts, so subsequent mouseup messages will be in the
+ // correct position. No need to correct them.
+ //mShiftHoriz = 0;
+ //mShiftVert = 0;
+
+ // release mouse capture after short period of visibility if we're using a finite boundary
+ // so that right click outside of boundary will trigger new pie menu
+ if (gFocusMgr.getMouseCapture() == this &&
+ !mRightMouseDown &&
+ mShrinkBorderTimer.getStarted() &&
+ mShrinkBorderTimer.getElapsedTimeF32() >= PIE_SHRINK_TIME)
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ mUseInfiniteRadius = FALSE;
+ }
+
+ LLMenuItemGL *item = pieItemFromXY( x, y );
+
+ if (item && item->getEnabled())
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ handled = TRUE;
+
+ if (item != mHoverItem)
+ {
+ BOOL active = FALSE;
+ if (mHoverItem)
+ {
+ active = mHoverItem->isActive();
+ mHoverItem->setHighlight( FALSE );
+ }
+ mHoverItem = item;
+ mHoverItem->setHighlight( TRUE );
+
+ switch(pieItemIndexFromXY(x, y))
+ {
+ case 0:
+ make_ui_sound("UISndPieMenuSliceHighlight0");
+ break;
+ case 1:
+ make_ui_sound("UISndPieMenuSliceHighlight1");
+ break;
+ case 2:
+ make_ui_sound("UISndPieMenuSliceHighlight2");
+ break;
+ case 3:
+ make_ui_sound("UISndPieMenuSliceHighlight3");
+ break;
+ case 4:
+ make_ui_sound("UISndPieMenuSliceHighlight4");
+ break;
+ case 5:
+ make_ui_sound("UISndPieMenuSliceHighlight5");
+ break;
+ case 6:
+ make_ui_sound("UISndPieMenuSliceHighlight6");
+ break;
+ case 7:
+ make_ui_sound("UISndPieMenuSliceHighlight7");
+ break;
+ default:
+ make_ui_sound("UISndPieMenuSliceHighlight0");
+ break;
+ }
+ }
+ }
+ else
+ {
+ // clear out our selection
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight(FALSE);
+ mHoverItem = NULL;
+ }
+ }
+
+ if( !handled && pointInView( x, y ) )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ handled = TRUE;
+ }
+
+ mHoverThisFrame = TRUE;
+
+ return handled;
+}
+
+BOOL LLPieMenu::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ // The click was somewhere within our rectangle
+ LLMenuItemGL *item = pieItemFromXY( x, y );
+
+ if (item)
+ {
+ // lie to the item about where the click happened
+ // to make sure it's within the item's rectangle
+ handled = item->handleMouseDown( 0, 0, mask );
+ }
+
+ // always handle mouse down as mouse up will close open menus
+ return handled;
+}
+
+BOOL LLPieMenu::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ mRightMouseDown = TRUE;
+
+ // The click was somewhere within our rectangle
+ LLMenuItemGL *item = pieItemFromXY( x, y );
+ S32 delta_x = x /*+ mShiftHoriz*/ - getLocalRect().getCenterX();
+ S32 delta_y = y /*+ mShiftVert*/ - getLocalRect().getCenterY();
+ BOOL clicked_in_pie = ((delta_x * delta_x) + (delta_y * delta_y) < mCurRadius*mCurRadius) || mUseInfiniteRadius;
+
+ // grab mouse if right clicking anywhere within pie (even deadzone in middle), to detect drag outside of pie
+ if (clicked_in_pie)
+ {
+ // capture mouse cursor as if on initial menu show
+ gFocusMgr.setMouseCapture(this, NULL);
+ mShrinkBorderTimer.stop();
+ mUseInfiniteRadius = TRUE;
+ handled = TRUE;
+ }
+
+ if (item)
+ {
+ // lie to the item about where the click happened
+ // to make sure it's within the item's rectangle
+ if (item->handleMouseDown( 0, 0, mask ))
+ {
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLPieMenu::handleRightMouseUp( S32 x, S32 y, MASK mask )
+{
+ // release mouse capture when right mouse button released, and we're past the shrink time
+ if (mShrinkBorderTimer.getStarted() &&
+ mShrinkBorderTimer.getElapsedTimeF32() > PIE_SHRINK_TIME)
+ {
+ mUseInfiniteRadius = FALSE;
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+
+ BOOL result = handleMouseUp( x, y, mask );
+ mRightMouseDown = FALSE;
+
+ return result;
+}
+
+BOOL LLPieMenu::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+
+ // The click was somewhere within our rectangle
+ LLMenuItemGL *item = pieItemFromXY( x, y );
+
+ if (item)
+ {
+ // lie to the item about where the click happened
+ // to make sure it's within the item's rectangle
+ if (item->getEnabled())
+ {
+ handled = item->handleMouseUp( 0, 0, mask );
+ hide(TRUE);
+ }
+ }
+
+ if (handled)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ if (!handled && !mUseInfiniteRadius)
+ {
+ // call hidemenus to make sure transient selections get cleared
+ ((LLMenuHolderGL*)getParent())->hideMenus();
+ }
+
+ if (mFirstMouseDown)
+ {
+ make_ui_sound("UISndPieMenuAppear");
+ mFirstMouseDown = FALSE;
+ }
+
+ //FIXME: is this necessary?
+ if (!mShrinkBorderTimer.getStarted())
+ {
+ mShrinkBorderTimer.start();
+ }
+
+ return handled;
+}
+
+
+// virtual
+void LLPieMenu::draw()
+{
+ // clear hover if mouse moved away
+ if (!mHoverThisFrame && mHoverItem)
+ {
+ mHoverItem->setHighlight(FALSE);
+ mHoverItem = NULL;
+ }
+
+ F32 width = (F32) mRect.getWidth();
+ F32 height = (F32) mRect.getHeight();
+ mCurRadius = PIE_SCALE_FACTOR * llmax( width/2, height/2 );
+
+ mOuterRingAlpha = mUseInfiniteRadius ? 0.f : 1.f;
+ if (mShrinkBorderTimer.getStarted())
+ {
+ mOuterRingAlpha = clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 0.f, 1.f);
+ mCurRadius *= clamp_rescale(mShrinkBorderTimer.getElapsedTimeF32(), 0.f, PIE_SHRINK_TIME, 1.f, 1.f / PIE_SCALE_FACTOR);
+ }
+
+ // correct for non-square pixels
+ F32 center_x = width/2;
+ F32 center_y = height/2;
+ S32 steps = 100;
+
+ glPushMatrix();
+ {
+ glTranslatef(center_x, center_y, 0.f);
+
+ F32 line_width = LLUI::sConfigGroup->getF32("PieMenuLineWidth");
+ LLColor4 line_color = LLUI::sColorsGroup->getColor("PieMenuLineColor");
+ LLColor4 bg_color = LLUI::sColorsGroup->getColor("PieMenuBgColor");
+ LLColor4 selected_color = LLUI::sColorsGroup->getColor("PieMenuSelectedColor");
+
+ // main body
+ LLColor4 outer_color = bg_color;
+ outer_color.mV[VALPHA] *= mOuterRingAlpha;
+ gl_washer_2d( mCurRadius, (F32) PIE_CENTER_SIZE, steps, bg_color, outer_color );
+
+ // selected wedge
+ item_list_t::iterator item_iter;
+ S32 i = 0;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter)->getHighlight())
+ {
+ F32 arc_size = F_PI * 0.25f;
+
+ F32 start_radians = (i * arc_size) - (arc_size * 0.5f);
+ F32 end_radians = start_radians + arc_size;
+
+ LLColor4 outer_color = selected_color;
+ outer_color.mV[VALPHA] *= mOuterRingAlpha;
+ gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, selected_color, outer_color );
+ }
+ i++;
+ }
+
+ LLUI::setLineWidth( line_width );
+
+ // inner lines
+ outer_color = line_color;
+ outer_color.mV[VALPHA] *= mOuterRingAlpha;
+ gl_washer_spokes_2d( mCurRadius, (F32)PIE_CENTER_SIZE, 8, line_color, outer_color );
+
+ // inner circle
+ glColor4fv( line_color.mV );
+ gl_circle_2d( 0, 0, (F32)PIE_CENTER_SIZE, steps, FALSE );
+
+ // outer circle
+ glColor4fv( outer_color.mV );
+ gl_circle_2d( 0, 0, mCurRadius, steps, FALSE );
+
+ LLUI::setLineWidth(1.0f);
+ }
+ glPopMatrix();
+
+ mHoverThisFrame = FALSE;
+
+ LLView::draw();
+}
+
+void LLPieMenu::drawBackground(LLMenuItemGL* itemp, LLColor4& color)
+{
+ F32 width = (F32) mRect.getWidth();
+ F32 height = (F32) mRect.getHeight();
+ F32 center_x = width/2;
+ F32 center_y = height/2;
+ S32 steps = 100;
+
+ glColor4fv( color.mV );
+ glPushMatrix();
+ {
+ glTranslatef(center_x - itemp->getRect().mLeft, center_y - itemp->getRect().mBottom, 0.f);
+
+ item_list_t::iterator item_iter;
+ S32 i = 0;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if ((*item_iter) == itemp)
+ {
+ F32 arc_size = F_PI * 0.25f;
+
+ F32 start_radians = (i * arc_size) - (arc_size * 0.5f);
+ F32 end_radians = start_radians + arc_size;
+
+ LLColor4 outer_color = color;
+ outer_color.mV[VALPHA] *= mOuterRingAlpha;
+ gl_washer_segment_2d( mCurRadius, (F32)PIE_CENTER_SIZE, start_radians, end_radians, steps / 8, color, outer_color );
+ }
+ i++;
+ }
+ }
+ glPopMatrix();
+}
+
+// virtual
+BOOL LLPieMenu::append(LLMenuItemGL *item)
+{
+ item->setBriefItem(TRUE);
+ item->setFont( LLFontGL::sSansSerifSmall );
+ return LLMenuGL::append(item);
+}
+
+// virtual
+BOOL LLPieMenu::appendSeparator(const LLString &separator_name)
+{
+ LLMenuItemGL* separator = new LLMenuItemBlankGL();
+ separator->setFont( LLFontGL::sSansSerifSmall );
+ return append( separator );
+}
+
+
+// virtual
+BOOL LLPieMenu::appendMenu(LLPieMenu *menu,
+ enabled_callback enabled_cb,
+ void* user_data)
+{
+ if (menu == this)
+ {
+ llerrs << "Can't attach a pie menu to itself" << llendl;
+ }
+ LLPieMenuBranch *item;
+ item = new LLPieMenuBranch(menu->getName(), menu->getLabel(), menu, enabled_cb, user_data);
+ getParent()->addChild(item->getBranch());
+ item->setFont( LLFontGL::sSansSerifSmall );
+ return append( item );
+}
+
+// virtual
+void LLPieMenu::arrange()
+{
+ const S32 rect_height = 180;
+ const S32 rect_width = 180;
+
+ // all divide by 6
+ const S32 CARD_X = 60;
+ const S32 DIAG_X = 48;
+ const S32 CARD_Y = 76;
+ const S32 DIAG_Y = 42;
+
+ const S32 ITEM_CENTER_X[] = { CARD_X, DIAG_X, 0, -DIAG_X, -CARD_X, -DIAG_X, 0, DIAG_X };
+ const S32 ITEM_CENTER_Y[] = { 0, DIAG_Y, CARD_Y, DIAG_Y, 0, -DIAG_Y, -CARD_Y, -DIAG_Y };
+
+ LLRect rect;
+
+ S32 font_height = 0;
+ if( mItems.size() )
+ {
+ font_height = (*mItems.begin())->getNominalHeight();
+ }
+ S32 item_width = 0;
+
+// F32 sin_delta = OO_SQRT2; // sin(45 deg)
+// F32 cos_delta = OO_SQRT2; // cos(45 deg)
+
+ // TODO: Compute actual bounding rect for menu
+
+ mRect.setOriginAndSize(mRect.mLeft, mRect.mBottom, rect_width, rect_height );
+
+ // place items around a circle, with item 0 at positive X,
+ // rotating counter-clockwise
+ item_list_t::iterator item_iter;
+ S32 i = 0;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL *item = *item_iter;
+
+ item_width = item->getNominalWidth();
+
+ // Put in the right place around a circle centered at 0,0
+ rect.setCenterAndSize(ITEM_CENTER_X[i],
+ ITEM_CENTER_Y[i],
+ item_width, font_height );
+
+ // Correct for the actual rectangle size
+ rect.translate( rect_width/2, rect_height/2 );
+
+ item->setRect( rect );
+
+ // Make sure enablement is correct
+ item->buildDrawLabel();
+ i++;
+ }
+}
+
+LLMenuItemGL *LLPieMenu::pieItemFromXY(S32 x, S32 y)
+{
+ // We might have shifted this menu on draw. If so, we need
+ // to shift over mouseup events until we get a hover event.
+ //x += mShiftHoriz;
+ //y += mShiftVert;
+
+ // An arc of the pie menu is 45 degrees
+ const F32 ARC_DEG = 45.f;
+ S32 delta_x = x - mRect.getWidth() / 2;
+ S32 delta_y = y - mRect.getHeight() / 2;
+
+ // circle safe zone in the center
+ S32 dist_squared = delta_x*delta_x + delta_y*delta_y;
+ if (dist_squared < PIE_CENTER_SIZE*PIE_CENTER_SIZE)
+ {
+ return NULL;
+ }
+
+ // infinite radius is only used with right clicks
+ S32 radius = llmax( mRect.getWidth()/2, mRect.getHeight()/2 );
+ if (!(mUseInfiniteRadius && mRightMouseDown) && dist_squared > radius * radius)
+ {
+ return NULL;
+ }
+
+ F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x);
+
+ // rotate marks CCW so that east = [0, ARC_DEG) instead of
+ // [-ARC_DEG/2, ARC_DEG/2)
+ angle += ARC_DEG / 2.f;
+
+ // make sure we're only using positive angles
+ if (angle < 0.f) angle += 360.f;
+
+ S32 which = S32( angle / ARC_DEG );
+
+ if (0 <= which && which < (S32)mItems.size() )
+ {
+ item_list_t::iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ if (which == 0)
+ {
+ return (*item_iter);
+ }
+ which--;
+ }
+ }
+
+ return NULL;
+}
+
+S32 LLPieMenu::pieItemIndexFromXY(S32 x, S32 y)
+{
+ // An arc of the pie menu is 45 degrees
+ const F32 ARC_DEG = 45.f;
+ // correct for non-square pixels
+ S32 delta_x = x - mRect.getWidth() / 2;
+ S32 delta_y = y - mRect.getHeight() / 2;
+
+ // circle safe zone in the center
+ if (delta_x*delta_x + delta_y*delta_y < PIE_CENTER_SIZE*PIE_CENTER_SIZE)
+ {
+ return -1;
+ }
+
+ F32 angle = RAD_TO_DEG * (F32) atan2((F32)delta_y, (F32)delta_x);
+
+ // rotate marks CCW so that east = [0, ARC_DEG) instead of
+ // [-ARC_DEG/2, ARC_DEG/2)
+ angle += ARC_DEG / 2.f;
+
+ // make sure we're only using positive angles
+ if (angle < 0.f) angle += 360.f;
+
+ S32 which = S32( angle / ARC_DEG );
+ return which;
+}
+
+void LLPieMenu::show(S32 x, S32 y, BOOL mouse_down)
+{
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ LLView* parent_view = getParent();
+ S32 menu_region_width = parent_view->getRect().getWidth();
+ S32 menu_region_height = parent_view->getRect().getHeight();
+
+ BOOL moved = FALSE;
+
+ S32 local_x, local_y;
+ parent_view->screenPointToLocal(x, y, &local_x, &local_y);
+
+ mRect.setCenterAndSize(local_x, local_y, width, height);
+ arrange();
+
+ // Adjust the pie rectangle to keep it on screen
+ if (mRect.mLeft < 0)
+ {
+ //mShiftHoriz = 0 - mRect.mLeft;
+ //mRect.translate( mShiftHoriz, 0 );
+ mRect.translate( 0 - mRect.mLeft, 0 );
+ moved = TRUE;
+ }
+
+ if (mRect.mRight > menu_region_width)
+ {
+ //mShiftHoriz = menu_region_width - mRect.mRight;
+ //mRect.translate( mShiftHoriz, 0);
+ mRect.translate( menu_region_width - mRect.mRight, 0 );
+ moved = TRUE;
+ }
+
+ if (mRect.mBottom < 0)
+ {
+ //mShiftVert = -mRect.mBottom;
+ //mRect.translate( 0, mShiftVert );
+ mRect.translate( 0, 0 - mRect.mBottom );
+ moved = TRUE;
+ }
+
+
+ if (mRect.mTop > menu_region_height)
+ {
+ //mShiftVert = menu_region_height - mRect.mTop;
+ //mRect.translate( 0, mShiftVert );
+ mRect.translate( 0, menu_region_height - mRect.mTop );
+ moved = TRUE;
+ }
+
+ // If we had to relocate the pie menu, put the cursor in the
+ // center of its rectangle
+ if (moved)
+ {
+ LLCoordGL center;
+ center.mX = (mRect.mLeft + mRect.mRight) / 2;
+ center.mY = (mRect.mTop + mRect.mBottom) / 2;
+
+ LLUI::setCursorPositionLocal(getParent(), center.mX, center.mY);
+ }
+
+ // FIXME: what happens when mouse buttons reversed?
+ mRightMouseDown = mouse_down;
+ mFirstMouseDown = mouse_down;
+ mUseInfiniteRadius = TRUE;
+ if (!mFirstMouseDown)
+ {
+ make_ui_sound("UISndPieMenuAppear");
+ }
+
+ LLView::setVisible(TRUE);
+
+ // we want all mouse events in case user does quick right click again off of pie menu
+ // rectangle, to support gestural menu traversal
+ gFocusMgr.setMouseCapture(this, NULL);
+
+ if (mouse_down)
+ {
+ mShrinkBorderTimer.stop();
+ }
+ else
+ {
+ mShrinkBorderTimer.start();
+ }
+}
+
+void LLPieMenu::hide(BOOL item_selected)
+{
+ if (!getVisible()) return;
+
+ if (mHoverItem)
+ {
+ mHoverItem->setHighlight( FALSE );
+ mHoverItem = NULL;
+ }
+
+ make_ui_sound("UISndPieMenuHide");
+
+ mFirstMouseDown = FALSE;
+ mRightMouseDown = FALSE;
+ mUseInfiniteRadius = FALSE;
+
+ LLView::setVisible(FALSE);
+
+ gFocusMgr.setMouseCapture(NULL, NULL);
+}
+
+///============================================================================
+/// Class LLMenuBarGL
+///============================================================================
+
+// Default constructor
+LLMenuBarGL::LLMenuBarGL( const LLString& name ) : LLMenuGL ( name, name )
+{
+ mHorizontalLayout = TRUE;
+ setCanTearOff(FALSE);
+ mKeepFixedSize = TRUE;
+}
+
+// Default destructor
+LLMenuBarGL::~LLMenuBarGL()
+{
+ std::for_each(mAccelerators.begin(), mAccelerators.end(), DeletePointer());
+ mAccelerators.clear();
+}
+
+// virtual
+LLXMLNodePtr LLMenuBarGL::getXML(bool save_children) const
+{
+ // Sorty of hacky: reparent items to this and then back at the end of the export
+ LLView *orig_parent = NULL;
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* child = *item_iter;
+ LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
+ LLMenuGL *menu = branch->getBranch();
+ orig_parent = menu->getParent();
+ menu->updateParent((LLView *)this);
+ }
+
+ LLXMLNodePtr node = LLMenuGL::getXML();
+
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* child = *item_iter;
+ LLMenuItemBranchGL* branch = (LLMenuItemBranchGL*)child;
+ LLMenuGL *menu = branch->getBranch();
+ menu->updateParent(orig_parent);
+ }
+
+ return node;
+}
+
+LLView* LLMenuBarGL::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("menu");
+ node->getAttributeString("name", name);
+
+ BOOL opaque = FALSE;
+ node->getAttributeBOOL("opaque", opaque);
+
+ LLMenuBarGL *menubar = new LLMenuBarGL(name);
+
+ LLViewHandle parent_handle = LLViewHandle::sDeadHandle;
+ if (parent->getWidgetType() == WIDGET_TYPE_FLOATER)
+ {
+ parent_handle = ((LLFloater*)parent)->getHandle();
+ }
+
+ // We need to have the rect early so that it's around when building
+ // the menu items
+ LLRect view_rect;
+ createRect(node, view_rect, parent, menubar->getRequiredRect());
+ menubar->setRect(view_rect);
+
+ if (node->hasAttribute("drop_shadow"))
+ {
+ BOOL drop_shadow = FALSE;
+ node->getAttributeBOOL("drop_shadow", drop_shadow);
+ menubar->setDropShadowed(drop_shadow);
+ }
+
+ menubar->setBackgroundVisible(opaque);
+ LLColor4 color(0,0,0,0);
+ if (opaque && LLUICtrlFactory::getAttributeColor(node,"color", color))
+ {
+ menubar->setBackgroundColor(color);
+ }
+
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("menu"))
+ {
+ LLMenuGL *menu = (LLMenuGL*)LLMenuGL::fromXML(child, parent, factory);
+ // because of lazy initialization, have to disable tear off functionality
+ // and then re-enable with proper parent handle
+ if (menu->getCanTearOff())
+ {
+ menu->setCanTearOff(FALSE);
+ menu->setCanTearOff(TRUE, parent_handle);
+ }
+ menubar->appendMenu(menu);
+ if (LLMenuGL::sDefaultMenuContainer != NULL)
+ {
+ menu->updateParent(LLMenuGL::sDefaultMenuContainer);
+ }
+ else
+ {
+ menu->updateParent(parent);
+ }
+ }
+ }
+
+ menubar->initFromXML(node, parent);
+
+ BOOL create_jump_keys = FALSE;
+ node->getAttributeBOOL("create_jump_keys", create_jump_keys);
+ if (create_jump_keys)
+ {
+ menubar->createJumpKeys();
+ }
+
+ return menubar;
+}
+
+void LLMenuBarGL::handleJumpKey(KEY key)
+{
+ navigation_key_map_t::iterator found_it = mJumpKeys.find(key);
+ if(found_it != mJumpKeys.end() && found_it->second->getEnabled())
+ {
+ clearHoverItem();
+ found_it->second->setHighlight(TRUE);
+ found_it->second->doIt();
+ }
+}
+
+// rearrange the child rects so they fit the shape of the menu bar.
+void LLMenuBarGL::arrange( void )
+{
+ U32 pos = 0;
+ LLRect rect( 0, mRect.getHeight(), 0, 0 );
+ item_list_t::const_iterator item_iter;
+ for (item_iter = mItems.begin(); item_iter != mItems.end(); ++item_iter)
+ {
+ LLMenuItemGL* item = *item_iter;
+ rect.mLeft = pos;
+ pos += item->getNominalWidth();
+ rect.mRight = pos;
+ item->setRect( rect );
+ item->buildDrawLabel();
+ }
+}
+
+
+S32 LLMenuBarGL::getRightmostMenuEdge()
+{
+ // Find the last visible menu
+ item_list_t::reverse_iterator item_iter;
+ for (item_iter = mItems.rbegin(); item_iter != mItems.rend(); ++item_iter)
+ {
+ if ((*item_iter)->getVisible())
+ {
+ break;
+ }
+ }
+
+ if (item_iter == mItems.rend())
+ {
+ return 0;
+ }
+ return (*item_iter)->getRect().mRight;
+}
+
+// add a vertical separator to this menu
+BOOL LLMenuBarGL::appendSeparator( const LLString &separator_name )
+{
+ LLMenuItemGL* separator = new LLMenuItemVerticalSeparatorGL();
+ return append( separator );
+}
+
+// add a menu - this will create a drop down menu.
+BOOL LLMenuBarGL::appendMenu( LLMenuGL* menu )
+{
+ if( menu == this )
+ {
+ llerrs << "** Attempt to attach menu to itself. This is certainly "
+ << "a logic error." << llendl;
+ }
+
+ BOOL success = TRUE;
+
+ LLMenuItemBranchGL* branch = NULL;
+ branch = new LLMenuItemBranchDownGL( menu->getName(), menu->getLabel(), menu );
+ success &= branch->addToAcceleratorList(&mAccelerators);
+ success &= append( branch );
+ branch->setJumpKey(branch->getJumpKey());
+ return success;
+}
+
+BOOL LLMenuBarGL::handleHover( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ LLView* active_menu = NULL;
+
+ S32 mouse_delta_x = x - mLastMouseX;
+ S32 mouse_delta_y = y - mLastMouseY;
+ mMouseVelX = (mMouseVelX / 2) + (mouse_delta_x / 2);
+ mMouseVelY = (mMouseVelY / 2) + (mouse_delta_y / 2);
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ // if nothing currently selected or mouse has moved since last call, pick menu item via mouse
+ // otherwise let keyboard control it
+ if (!getHighlightedItem() || llabs(mMouseVelX) > 0 || llabs(mMouseVelY) > 0)
+ {
+ // find current active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (((LLMenuItemGL*)viewp)->isActive())
+ {
+ active_menu = viewp;
+ }
+ }
+
+ // check for new active menu
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->getVisible() &&
+ viewp->getEnabled() &&
+ viewp->pointInView(local_x, local_y) &&
+ viewp->handleHover(local_x, local_y, mask))
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(TRUE);
+ handled = TRUE;
+ if (active_menu && active_menu != viewp)
+ {
+ ((LLMenuItemGL*)viewp)->doIt();
+ }
+ }
+ }
+
+ if (handled)
+ {
+ // set hover false on inactive menus
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if (!viewp->pointInView(local_x, local_y) && ((LLMenuItemGL*)viewp)->getHighlight())
+ {
+ ((LLMenuItemGL*)viewp)->setHighlight(FALSE);
+ }
+ }
+ }
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+
+ return TRUE;
+}
+
+///============================================================================
+/// Class LLMenuHolderGL
+///============================================================================
+LLMenuHolderGL::LLMenuHolderGL()
+: LLPanel("Menu Holder")
+{
+ setMouseOpaque(FALSE);
+ sItemActivationTimer.stop();
+ mCanHide = TRUE;
+}
+
+LLMenuHolderGL::LLMenuHolderGL(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows)
+: LLPanel(name, rect, FALSE)
+{
+ setMouseOpaque(mouse_opaque);
+ sItemActivationTimer.stop();
+ mCanHide = TRUE;
+}
+
+LLMenuHolderGL::~LLMenuHolderGL()
+{
+}
+
+EWidgetType LLMenuHolderGL::getWidgetType() const
+{
+ return WIDGET_TYPE_MENU_HOLDER;
+}
+
+LLString LLMenuHolderGL::getWidgetTag() const
+{
+ return LL_MENU_HOLDER_GL_TAG;
+}
+
+void LLMenuHolderGL::draw()
+{
+ LLView::draw();
+ // now draw last selected item as overlay
+ LLMenuItemGL* selecteditem = (LLMenuItemGL*)LLView::getViewByHandle(sItemLastSelectedHandle);
+ if (selecteditem && sItemActivationTimer.getStarted() && sItemActivationTimer.getElapsedTimeF32() < ACTIVATE_HIGHLIGHT_TIME)
+ {
+ // make sure toggle items, for example, show the proper state when fading out
+ selecteditem->buildDrawLabel();
+
+ LLRect item_rect;
+ selecteditem->localRectToOtherView(selecteditem->getLocalRect(), &item_rect, this);
+
+ F32 interpolant = sItemActivationTimer.getElapsedTimeF32() / ACTIVATE_HIGHLIGHT_TIME;
+ F32 alpha = lerp(LLMenuItemGL::sHighlightBackground.mV[VALPHA], 0.f, interpolant);
+ LLColor4 bg_color(LLMenuItemGL::sHighlightBackground.mV[VRED],
+ LLMenuItemGL::sHighlightBackground.mV[VGREEN],
+ LLMenuItemGL::sHighlightBackground.mV[VBLUE],
+ alpha);
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)item_rect.mLeft, (F32)item_rect.mBottom, 0.f);
+ selecteditem->getMenu()->drawBackground(selecteditem, bg_color);
+ selecteditem->draw();
+ }
+ LLUI::popMatrix();
+ }
+}
+
+BOOL LLMenuHolderGL::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+BOOL LLMenuHolderGL::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = LLView::childrenHandleRightMouseDown(x, y, mask) != NULL;
+ if (!handled)
+ {
+ // clicked off of menu, hide them all
+ hideMenus();
+ }
+ return handled;
+}
+
+void LLMenuHolderGL::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ if (width != mRect.getWidth() || height != mRect.getHeight())
+ {
+ hideMenus();
+ }
+ LLView::reshape(width, height, called_from_parent);
+}
+
+BOOL LLMenuHolderGL::hasVisibleMenu()
+{
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->getVisible())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLMenuHolderGL::hideMenus()
+{
+ if (!mCanHide)
+ {
+ return FALSE;
+ }
+ BOOL menu_visible = hasVisibleMenu();
+ if (menu_visible)
+ {
+ // clicked off of menu, hide them all
+ for ( child_list_const_iter_t child_it = getChildList()->begin(); child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->getVisible())
+ {
+ viewp->setVisible(FALSE);
+ }
+ }
+ }
+ //if (gFocusMgr.childHasKeyboardFocus(this))
+ //{
+ // gFocusMgr.setKeyboardFocus(NULL, NULL);
+ //}
+
+ return menu_visible;
+}
+
+void LLMenuHolderGL::setActivatedItem(LLMenuItemGL* item)
+{
+ sItemLastSelectedHandle = item->mViewHandle;
+ sItemActivationTimer.start();
+}
+
+///============================================================================
+/// Class LLTearOffMenu
+///============================================================================
+LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) :
+ LLFloater(menup->getName(), LLRect(0, 100, 100, 0), menup->getLabel(), FALSE, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT, FALSE, FALSE)
+{
+ LLRect rect;
+ menup->localRectToOtherView(LLRect(-1, menup->getRect().getHeight(), menup->getRect().getWidth() + 3, 0), &rect, gFloaterView);
+ mTargetHeight = (F32)(rect.getHeight() + LLFLOATER_HEADER_SIZE + 5);
+ reshape(rect.getWidth(), rect.getHeight());
+ setRect(rect);
+ mOldParent = menup->getParent();
+ mOldParent->removeChild(menup);
+
+ menup->setFollowsAll();
+ addChild(menup);
+ menup->setVisible(TRUE);
+ menup->translate(-menup->getRect().mLeft + 1, -menup->getRect().mBottom + 1);
+
+ menup->setTornOff(TRUE);
+ menup->setDropShadowed(FALSE);
+
+ mMenu = menup;
+
+ // highlight first item (tear off item will be disabled)
+ mMenu->highlightNextItem(NULL);
+}
+
+LLTearOffMenu::~LLTearOffMenu()
+{
+}
+
+void LLTearOffMenu::draw()
+{
+ if (hasFocus())
+ {
+ LLMenuItemGL* parent_menu_item = mMenu->getParentMenuItem();
+ while(parent_menu_item)
+ {
+ if (parent_menu_item->getMenu()->getVisible())
+ {
+ parent_menu_item->setHighlight(TRUE);
+ parent_menu_item = parent_menu_item->getMenu()->getParentMenuItem();
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ mMenu->setBackgroundVisible(mBgOpaque);
+ mMenu->arrange();
+
+ if (mRect.getHeight() != mTargetHeight)
+ {
+ // animate towards target height
+ reshape(mRect.getWidth(), llceil(lerp((F32)mRect.getHeight(), mTargetHeight, LLCriticalDamp::getInterpolant(0.05f))));
+ }
+ else
+ {
+ // when in stasis, remain big enough to hold menu contents
+ mTargetHeight = (F32)(mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 4);
+ reshape(mMenu->getRect().getWidth() + 3, mMenu->getRect().getHeight() + LLFLOATER_HEADER_SIZE + 5);
+ }
+ LLFloater::draw();
+}
+
+void LLTearOffMenu::onFocusReceived()
+{
+ // if nothing is highlighted, just highlight first item
+ if (!mMenu->getHighlightedItem())
+ {
+ mMenu->highlightNextItem(NULL);
+ }
+}
+
+void LLTearOffMenu::onFocusLost()
+{
+ // remove highlight from parent item and our own menu
+ mMenu->clearHoverItem();
+}
+
+//static
+LLTearOffMenu* LLTearOffMenu::create(LLMenuGL* menup)
+{
+ LLTearOffMenu* tearoffp = new LLTearOffMenu(menup);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(tearoffp, FALSE);
+ tearoffp->open();
+ return tearoffp;
+}
+
+void LLTearOffMenu::onClose(bool app_quitting)
+{
+ removeChild(mMenu);
+ mOldParent->addChild(mMenu);
+ mMenu->clearHoverItem();
+ mMenu->setFollowsNone();
+ mMenu->setBackgroundVisible(TRUE);
+ mMenu->setVisible(FALSE);
+ mMenu->setTornOff(FALSE);
+ mMenu->setDropShadowed(TRUE);
+ destroy();
+}
+
+///============================================================================
+/// Class LLEditMenuHandlerMgr
+///============================================================================
+LLEditMenuHandlerMgr& LLEditMenuHandlerMgr::getInstance()
+{
+ static LLEditMenuHandlerMgr instance;
+ return instance;
+}
+
+LLEditMenuHandlerMgr::LLEditMenuHandlerMgr()
+{
+}
+
+LLEditMenuHandlerMgr::~LLEditMenuHandlerMgr()
+{
+}
+
+///============================================================================
+/// Local function definitions
+///============================================================================
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
new file mode 100644
index 0000000000..84cbf13b69
--- /dev/null
+++ b/indra/llui/llmenugl.h
@@ -0,0 +1,728 @@
+/**
+ * @file llmenugl.h
+ * @brief Declaration of the opengl based menu system.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMENUGL_H
+#define LL_LLMENUGL_H
+
+#include <list>
+
+#include "llstring.h"
+#include "v4color.h"
+#include "llframetimer.h"
+#include "llevent.h"
+
+#include "llkeyboard.h"
+#include "llfloater.h"
+#include "lluistring.h"
+#include "llview.h"
+
+class LLMenuItemGL;
+
+extern S32 MENU_BAR_HEIGHT;
+extern S32 MENU_BAR_WIDTH;
+
+// These callbacks are used by the LLMenuItemCallGL and LLMenuItemCheckGL
+// classes during their work.
+typedef void (*menu_callback)(void*);
+
+// These callbacks are used by the LLMenuItemCallGL
+// classes during their work.
+typedef void (*on_disabled_callback)(void*);
+
+// This callback is used by the LLMenuItemCallGL and LLMenuItemCheckGL
+// to determine if the current menu is enabled.
+typedef BOOL (*enabled_callback)(void*);
+
+// This callback is used by LLMenuItemCheckGL to determine it's
+// 'checked' state.
+typedef BOOL (*check_callback)(void*);
+
+// This callback is potentially used by LLMenuItemCallGL. If provided,
+// this function is called whenever it's time to determine the label's
+// contents. Put the contents of the label in the provided parameter.
+typedef void (*label_callback)(LLString&,void*);
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemGL
+//
+// The LLMenuItemGL represents a single menu item in a menu.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFontGL;
+class LLMenuGL;
+
+
+class LLMenuItemGL : public LLView
+{
+public:
+ LLMenuItemGL( const LLString& name, const LLString& label, KEY key = KEY_NONE, MASK = MASK_NONE );
+
+ virtual void setValue(const LLSD& value) { setLabel(value.asString()); }
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_ITEM; }
+ virtual LLString getWidgetTag() const { return LL_MENU_ITEM_TAG; }
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ virtual LLString getType() const { return "item"; }
+
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
+
+ BOOL getHighlight() const { return mHighlight; }
+
+ void setJumpKey(KEY key);
+ KEY getJumpKey();
+
+ // set the font used by this item.
+ void setFont(LLFontGL* font);
+ void setFontStyle(U8 style) { mStyle = style; }
+
+ // returns the height in pixels for the current font.
+ virtual U32 getNominalHeight( void );
+
+ // functions to control the color scheme
+ static void setEnabledColor( const LLColor4& color );
+ static void setDisabledColor( const LLColor4& color );
+ static void setHighlightBGColor( const LLColor4& color );
+ static void setHighlightFGColor( const LLColor4& color );
+
+ // Marks item as not needing space for check marks or accelerator keys
+ virtual void setBriefItem(BOOL brief);
+
+ virtual BOOL addToAcceleratorList(std::list<LLKeyBinding*> *listp);
+ void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; }
+
+ // return the name label
+ LLString getLabel( void ) const { return mLabel.getString(); }
+
+ // change the label
+ void setLabel( const LLString& label );
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ // Get the parent menu for this item
+ virtual LLMenuGL* getMenu();
+
+ // returns the normal width of this control in pixels - this is
+ // used for calculating the widest item, as well as for horizontal
+ // arrangement.
+ virtual U32 getNominalWidth( void );
+
+ // buildDrawLabel() - constructs the string used during the draw()
+ // function. This reduces the overall string manipulation, but can
+ // lead to visual errors if the state of the object changes
+ // without the knowledge of the menu item. For example, if a
+ // boolean being watched is changed outside of the menu item's
+ // doIt() function, the draw buffer will not be updated and will
+ // reflect the wrong value. If this ever becomes an issue, there
+ // are ways to fix this.
+ // Returns the enabled state of the item.
+ virtual void buildDrawLabel( void );
+
+ // for branching menu items, bring sub menus up to root level of menu hierarchy
+ virtual void updateBranchParent( LLView* parentp ){};
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void ) = 0;
+
+ // set the hover status (called by it's menu)
+ virtual void setHighlight( BOOL highlight );
+
+ // determine if this object is active
+ virtual BOOL isActive( void ) const;
+
+ virtual void setEnabledSubMenus(BOOL enable){};
+
+ // LLView Functionality
+ virtual BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual void draw( void );
+
+ BOOL getDrawTextDisabled() const { return mDrawTextDisabled; }
+
+protected:
+ // This function appends the character string representation of
+ // the current accelerator key and mask to the provided string.
+ void appendAcceleratorString( LLString& st );
+
+public:
+ static LLColor4 sEnabledColor;
+ static LLColor4 sDisabledColor;
+ static LLColor4 sHighlightBackground;
+ static LLColor4 sHighlightForeground;
+
+protected:
+ static BOOL sDropShadowText;
+
+ // mLabel contains the actual label specified by the user.
+ LLUIString mLabel;
+
+ // The draw labels contain some of the labels that we draw during
+ // the draw() routine. This optimizes away some of the string
+ // manipulation.
+ LLUIString mDrawBoolLabel;
+ LLUIString mDrawAccelLabel;
+ LLUIString mDrawBranchLabel;
+
+ // Keyboard and mouse variables
+ KEY mJumpKey;
+ KEY mAcceleratorKey;
+ MASK mAcceleratorMask;
+ BOOL mAllowKeyRepeat;
+ BOOL mHighlight;
+ BOOL mGotHover;
+
+ // If true, suppress normal space for check marks on the left and accelerator
+ // keys on the right.
+ BOOL mBriefItem;
+
+ // Font for this item
+ LLFontGL* mFont;
+
+ U8 mStyle;
+ BOOL mDrawTextDisabled;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemCallGL
+//
+// The LLMenuItemCallerGL represents a single menu item in a menu that
+// calls a user defined callback.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemCallGL : public LLMenuItemGL
+{
+protected:
+ menu_callback mCallback;
+ // mEnabledCallback should return TRUE if the item should be enabled
+ enabled_callback mEnabledCallback;
+ label_callback mLabelCallback;
+ void* mUserData;
+ on_disabled_callback mOnDisabledCallback;
+
+public:
+
+
+ void setMenuCallback(menu_callback callback, void* data) { mCallback = callback; mUserData = data; };
+ void setEnabledCallback(enabled_callback callback) { mEnabledCallback = callback; };
+
+ // normal constructor
+ LLMenuItemCallGL( const LLString& name,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb = NULL,
+ void* user_data = NULL,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE,
+ BOOL enabled = TRUE,
+ on_disabled_callback on_disabled_cb = NULL);
+ LLMenuItemCallGL( const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb = NULL,
+ void* user_data = NULL,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE,
+ BOOL enabled = TRUE,
+ on_disabled_callback on_disabled_cb = NULL);
+
+ // constructor for when you want to trap the arrange method.
+ LLMenuItemCallGL( const LLString& name,
+ const LLString& label,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ label_callback label_cb,
+ void* user_data,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE,
+ BOOL enabled = TRUE,
+ on_disabled_callback on_disabled_c = NULL);
+ LLMenuItemCallGL( const LLString& name,
+ menu_callback clicked_cb,
+ enabled_callback enabled_cb,
+ label_callback label_cb,
+ void* user_data,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE,
+ BOOL enabled = TRUE,
+ on_disabled_callback on_disabled_c = NULL);
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ virtual LLString getType() const { return "call"; }
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void setEnabledControl(LLString enabled_control, LLView *context);
+ void setVisibleControl(LLString enabled_control, LLView *context);
+
+ virtual void setUserData(void *userdata) { mUserData = userdata; }
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void );
+
+ virtual BOOL handleAcceleratorKey(KEY key, MASK mask);
+
+ //virtual void draw();
+
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemCheckGL
+//
+// The LLMenuItemCheckGL is an extension of the LLMenuItemCallGL
+// class, by allowing another method to be specified which determines
+// if the menu item should consider itself checked as true or not. Be
+// careful that the check callback provided - it needs to be VERY
+// FUCKING EFFICIENT, because it may need to be checked a lot.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemCheckGL
+: public LLMenuItemCallGL
+{
+protected:
+ check_callback mCheckCallback;
+ BOOL mChecked;
+
+public:
+ LLMenuItemCheckGL( const LLString& name,
+ const LLString& label,
+ menu_callback callback,
+ enabled_callback enabled_cb,
+ check_callback check,
+ void* user_data,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+ LLMenuItemCheckGL( const LLString& name,
+ menu_callback callback,
+ enabled_callback enabled_cb,
+ check_callback check,
+ void* user_data,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+ LLMenuItemCheckGL( const LLString& name,
+ const LLString& label,
+ menu_callback callback,
+ enabled_callback enabled_cb,
+ LLString control_name,
+ LLView *context,
+ void* user_data,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ void setCheckedControl(LLString checked_control, LLView *context);
+
+ virtual void setValue(const LLSD& value) { mChecked = value.asBoolean(); }
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual LLString getType() const { return "check"; }
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+
+ // LLView Functionality
+ //virtual void draw( void );
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemToggleGL
+//
+// The LLMenuItemToggleGL is a menu item that wraps around a user
+// specified and controlled boolean.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemToggleGL : public LLMenuItemGL
+{
+protected:
+ BOOL* mToggle;
+
+public:
+ LLMenuItemToggleGL( const LLString& name, const LLString& label,
+ BOOL* toggle,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+
+ LLMenuItemToggleGL( const LLString& name,
+ BOOL* toggle,
+ KEY key = KEY_NONE, MASK mask = MASK_NONE );
+
+ virtual LLString getType() const { return "toggle"; }
+
+ // called to rebuild the draw label
+ virtual void buildDrawLabel( void );
+
+ // doIt() - do the primary funcationality of the menu item.
+ virtual void doIt( void );
+
+ // LLView Functionality
+ //virtual void draw( void );
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuGL
+//
+// The Menu class represents a normal rectangular menu somewhere on
+// screen. A Menu can have menu items (described above) or sub-menus
+// attached to it. Sub-menus are implemented via a specialized
+// menu-item type known as a branch. Since it's easy to do wrong, I've
+// taken the branch functionality out of public view, and encapsulate
+// it in the appendMenu() method.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuArrowGL;
+class LLMenuItemBranchGL;
+class LLMenuItemTearOffGL;
+
+class LLMenuGL
+: public LLUICtrl
+{
+public:
+ LLMenuGL( const LLString& name, const LLString& label, LLViewHandle parent_floater = LLViewHandle::sDeadHandle );
+ LLMenuGL( const LLString& label, LLViewHandle parent_floater = LLViewHandle::sDeadHandle );
+ virtual ~LLMenuGL( void );
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ void parseChildXML(LLXMLNodePtr child, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU; }
+ virtual LLString getWidgetTag() const { return LL_MENU_GL_TAG; }
+
+ // LLView Functionality
+ virtual BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual void draw( void );
+ virtual void drawBackground(LLMenuItemGL* itemp, LLColor4& color);
+ virtual void setVisible(BOOL visible);
+
+ virtual BOOL LLMenuGL::handleAcceleratorKey(KEY key, MASK mask);
+
+ LLMenuGL* getChildMenuByName(const LLString& name, BOOL recurse) const;
+
+ BOOL clearHoverItem(BOOL include_active = TRUE);
+
+ // return the name label
+ const LLString& getLabel( void ) const { return mLabel.getString(); }
+ void setLabel(const LLString& label) { mLabel = label; }
+
+ static void setDefaultBackgroundColor( const LLColor4& color );
+ void setBackgroundColor( const LLColor4& color );
+ void setBackgroundVisible( BOOL b ) { mBgVisible = b; }
+ void setCanTearOff(BOOL tear_off, LLViewHandle parent_floater_handle = LLViewHandle::sDeadHandle);
+
+ // Add the menu item to this menu.
+ virtual BOOL append( LLMenuItemGL* item );
+
+ // add a separator to this menu
+ virtual BOOL appendSeparator( const LLString &separator_name = "separator" );
+
+ // add a menu - this will create a cascading menu
+ virtual BOOL appendMenu( LLMenuGL* menu );
+
+ // for branching menu items, bring sub menus up to root level of menu hierarchy
+ virtual void updateParent( LLView* parentp );
+
+ // setItemEnabled() - pass the name and the enable flag for a
+ // menu item. TRUE will make sure it's enabled, FALSE will disable
+ // it.
+ void setItemEnabled( const LLString& name, BOOL enable );
+
+ // propagate message to submenus
+ void setEnabledSubMenus(BOOL enable);
+
+ void setItemVisible( const LLString& name, BOOL visible);
+
+ // sets the left,bottom corner of menu, useful for popups
+ void setLeftAndBottom(S32 left, S32 bottom);
+
+ virtual void handleJumpKey(KEY key);
+
+ // Shape this menu to fit the current state of the children, and
+ // adjust the child rects to fit. This is called automatically
+ // when you add items. *FIX: We may need to deal with visibility
+ // arrangement.
+ virtual void arrange( void );
+
+ // remove all items on the menu
+ void empty( void );
+
+ // Rearrange the components, and do the right thing if the menu doesn't
+ // fit in the bounds.
+ // virtual void arrangeWithBounds(LLRect bounds);
+
+ void setItemLastSelected(LLMenuItemGL* item); // must be in menu
+ U32 getItemCount(); // number of menu items
+ LLMenuItemGL* getItem(S32 number); // 0 = first item
+ LLMenuItemGL* getHighlightedItem();
+
+ LLMenuItemGL* highlightNextItem(LLMenuItemGL* cur_item, BOOL skip_disabled = TRUE);
+ LLMenuItemGL* highlightPrevItem(LLMenuItemGL* cur_item, BOOL skip_disabled = TRUE);
+
+ void buildDrawLabels();
+ void createJumpKeys();
+
+ // Show popup in global screen space based on last mouse location.
+ static void showPopup(LLMenuGL* menu);
+
+ // Show popup at a specific location.
+ static void showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y);
+
+ // Whether to drop shadow menu bar
+ void setDropShadowed( const BOOL shadowed );
+
+ void setParentMenuItem( LLMenuItemGL* parent_menu_item ) { mParentMenuItem = parent_menu_item; }
+ LLMenuItemGL* getParentMenuItem() { return mParentMenuItem; }
+
+ void setTornOff(BOOL torn_off);
+ BOOL getTornOff() { return mTornOff; }
+
+ BOOL getCanTearOff() { return mTearOffItem != NULL; }
+
+ KEY getJumpKey() { return mJumpKey; }
+ void setJumpKey(KEY key) { mJumpKey = key; }
+
+ static void onFocusLost(LLView* old_focus);
+
+ static LLView *sDefaultMenuContainer;
+
+protected:
+ void createSpilloverBranch();
+ void cleanupSpilloverBranch();
+
+protected:
+ static LLColor4 sDefaultBackgroundColor;
+
+ LLColor4 mBackgroundColor;
+ BOOL mBgVisible;
+ typedef std::list< LLMenuItemGL* > item_list_t;
+ item_list_t mItems;
+ typedef std::map<KEY, LLMenuItemGL*> navigation_key_map_t;
+ navigation_key_map_t mJumpKeys;
+ LLMenuItemGL* mParentMenuItem;
+ LLUIString mLabel;
+ BOOL mDropShadowed; // Whether to drop shadow
+ BOOL mHorizontalLayout;
+ BOOL mKeepFixedSize;
+ BOOL mHasSelection;
+ LLFrameTimer mFadeTimer;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ S32 mMouseVelX;
+ S32 mMouseVelY;
+ BOOL mTornOff;
+ LLMenuItemTearOffGL* mTearOffItem;
+ LLMenuItemBranchGL* mSpilloverBranch;
+ LLMenuGL* mSpilloverMenu;
+ LLViewHandle mParentFloaterHandle;
+ KEY mJumpKey;
+};
+
+//-----------------------------------------------------------------------------
+// class LLPieMenu
+// A circular menu of items, icons, etc.
+//-----------------------------------------------------------------------------
+
+class LLPieMenu
+: public LLMenuGL
+{
+public:
+ LLPieMenu(const LLString& name, const LLString& label);
+ LLPieMenu(const LLString& name);
+ virtual ~LLPieMenu();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void initXML(LLXMLNodePtr node, LLView *context, LLUICtrlFactory *factory);
+
+ // LLView Functionality
+ // can't set visibility directly, must call show or hide
+ virtual void setVisible(BOOL visible);
+
+ //virtual BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual void draw();
+ virtual void drawBackground(LLMenuItemGL* itemp, LLColor4& color);
+
+ virtual BOOL append(LLMenuItemGL* item);
+ virtual BOOL appendSeparator( const LLString &separator_name = "separator" );
+
+ // the enabled callback is meant for the submenu. The api works
+ // this way because the menu branch item responsible for the pie
+ // submenu is constructed here.
+ virtual BOOL appendMenu(LLPieMenu *menu,
+ enabled_callback enabled_cb = NULL,
+ void* user_data = NULL );
+ virtual void arrange( void );
+
+ // Display the menu centered on this point on the screen.
+ void show(S32 x, S32 y, BOOL mouse_down);
+ void hide(BOOL item_selected);
+
+protected:
+ LLMenuItemGL *pieItemFromXY(S32 x, S32 y);
+ S32 pieItemIndexFromXY(S32 x, S32 y);
+
+private:
+ // These cause menu items to be spuriously selected by right-clicks
+ // near the window edge at low frame rates. I don't think they are
+ // needed unless you shift the menu position in the draw() function. JC
+ //S32 mShiftHoriz; // non-zero if menu had to shift this frame
+ //S32 mShiftVert; // non-zero if menu had to shift this frame
+ BOOL mFirstMouseDown; // true from show until mouse up
+ BOOL mUseInfiniteRadius; // allow picking pie menu items anywhere outside of center circle
+ LLMenuItemGL* mHoverItem;
+ BOOL mHoverThisFrame;
+ LLFrameTimer mShrinkBorderTimer;
+ F32 mOuterRingAlpha; // for rendering pie menus as both bounded and unbounded
+ F32 mCurRadius;
+ BOOL mRightMouseDown;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuBarGL
+//
+// A menu bar displays menus horizontally.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuBarGL : public LLMenuGL
+{
+protected:
+ std::list <LLKeyBinding*> mAccelerators;
+
+public:
+ LLMenuBarGL( const LLString& name );
+ virtual ~LLMenuBarGL();
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_MENU_BAR; }
+ virtual LLString getWidgetTag() const { return LL_MENU_BAR_GL_TAG; }
+
+ // rearrange the child rects so they fit the shape of the menu
+ // bar.
+ virtual void handleJumpKey(KEY key);
+ virtual void arrange( void );
+
+ // add a vertical separator to this menu
+ virtual BOOL appendSeparator( const LLString &separator_name = "separator" );
+
+ // add a menu - this will create a drop down menu.
+ virtual BOOL appendMenu( LLMenuGL* menu );
+
+ // LLView Functionality
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+
+ // Returns x position of rightmost child, usually Help menu
+ S32 getRightmostMenuEdge();
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuHolderGL
+//
+// High level view that serves as parent for all menus
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLMenuHolderGL : public LLPanel
+{
+public:
+ LLMenuHolderGL();
+ LLMenuHolderGL(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows = FOLLOWS_NONE);
+ virtual ~LLMenuHolderGL();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL hideMenus();
+ void reshape(S32 width, S32 height, BOOL called_from_parent);
+ void setCanHide(BOOL can_hide) { mCanHide = can_hide; }
+
+ // LLView functionality
+ virtual void draw();
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+
+ static void setActivatedItem(LLMenuItemGL* item);
+protected:
+ BOOL hasVisibleMenu();
+
+ static LLViewHandle sItemLastSelectedHandle;
+ static LLFrameTimer sItemActivationTimer;
+
+ BOOL mCanHide;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLTearOffMenu
+//
+// Floater that hosts a menu
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLTearOffMenu : public LLFloater
+{
+public:
+ static LLTearOffMenu* create(LLMenuGL* menup);
+ virtual ~LLTearOffMenu();
+ virtual void onClose(bool app_quitting);
+ virtual void draw(void);
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+
+protected:
+ LLTearOffMenu(LLMenuGL* menup);
+
+protected:
+ LLView* mOldParent;
+ LLMenuGL* mMenu;
+ F32 mTargetHeight;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLMenuItemTearOffGL
+//
+// This class represents a separator.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLMenuItemTearOffGL : public LLMenuItemGL
+{
+public:
+ LLMenuItemTearOffGL( LLViewHandle parent_floater_handle = (LLViewHandle)LLViewHandle::sDeadHandle );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual LLString getType() const { return "tearoff_menu"; }
+
+ virtual void doIt(void);
+ virtual void draw(void);
+ virtual U32 getNominalHeight();
+
+protected:
+ LLViewHandle mParentHandle;
+};
+
+
+//FIXME: this is currently working, so finish implementation
+class LLEditMenuHandlerMgr
+{
+public:
+ LLEditMenuHandlerMgr& getInstance();
+ virtual ~LLEditMenuHandlerMgr();
+protected:
+ LLEditMenuHandlerMgr();
+
+};
+
+#endif // LL_LLMENUGL_H
diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp
new file mode 100644
index 0000000000..4eaf6b7559
--- /dev/null
+++ b/indra/llui/llmodaldialog.cpp
@@ -0,0 +1,288 @@
+/**
+ * @file llmodaldialog.cpp
+ * @brief LLModalDialog base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llmodaldialog.h"
+
+#include "llfocusmgr.h"
+#include "v4color.h"
+#include "v2math.h"
+#include "llui.h"
+#include "llwindow.h"
+#include "llkeyboard.h"
+
+// static
+std::list<LLModalDialog*> LLModalDialog::sModalStack;
+
+LLModalDialog::LLModalDialog( const LLString& title, S32 width, S32 height, BOOL modal )
+ : LLFloater( "modal container",
+ LLRect( 0, height, width, 0 ),
+ title,
+ FALSE, // resizable
+ DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT,
+ FALSE, // drag_on_left
+ modal ? FALSE : TRUE, // minimizable
+ modal ? FALSE : TRUE, // close button
+ TRUE), // bordered
+ mModal( modal )
+{
+ setVisible( FALSE );
+ setBackgroundVisible(TRUE);
+ setBackgroundOpaque(TRUE);
+ centerOnScreen(); // default position
+}
+
+LLModalDialog::~LLModalDialog()
+{
+ // don't unlock focus unless we have it
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.unlockFocus();
+ }
+}
+
+void LLModalDialog::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLFloater::reshape(width, height, called_from_parent);
+ centerOnScreen();
+}
+
+void LLModalDialog::startModal()
+{
+ if (mModal)
+ {
+ // If Modal, Hide the active modal dialog
+ if (!sModalStack.empty())
+ {
+ LLModalDialog* front = sModalStack.front();
+ front->setVisible(FALSE);
+ }
+
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( this, NULL );
+ gFocusMgr.setTopView( this, NULL );
+ setFocus(TRUE);
+
+ sModalStack.push_front( this );
+ }
+
+ setVisible( TRUE );
+}
+
+void LLModalDialog::stopModal()
+{
+ gFocusMgr.unlockFocus();
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if (mModal)
+ {
+ std::list<LLModalDialog*>::iterator iter = std::find(sModalStack.begin(), sModalStack.end(), this);
+ if (iter != sModalStack.end())
+ {
+ sModalStack.erase(iter);
+ }
+ else
+ {
+ llwarns << "LLModalDialog::stopModal not in list!" << llendl;
+ }
+ }
+ if (!sModalStack.empty())
+ {
+ LLModalDialog* front = sModalStack.front();
+ front->setVisible(TRUE);
+ }
+}
+
+
+void LLModalDialog::setVisible( BOOL visible )
+{
+ if (mModal)
+ {
+ if( visible )
+ {
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ // The dialog view is a root view
+ gFocusMgr.setTopView( this, NULL );
+ setFocus( TRUE );
+ }
+ else
+ {
+ gFocusMgr.releaseFocusIfNeeded( this );
+ }
+ }
+
+ LLFloater::setVisible( visible );
+}
+
+BOOL LLModalDialog::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (!LLFloater::handleMouseDown(x, y, mask))
+ {
+ if (mModal)
+ {
+ // Click was outside the panel
+ make_ui_sound("UISndInvalidOp");
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLModalDialog::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( childrenHandleHover(x, y, mask) == NULL )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ }
+ return TRUE;
+}
+
+BOOL LLModalDialog::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ childrenHandleMouseUp(x, y, mask);
+ return TRUE;
+}
+
+BOOL LLModalDialog::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ childrenHandleScrollWheel(x, y, clicks);
+ return TRUE;
+}
+
+BOOL LLModalDialog::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!LLFloater::handleDoubleClick(x, y, mask))
+ {
+ // Click outside the panel
+ make_ui_sound("UISndInvalidOp");
+ }
+ return TRUE;
+}
+
+BOOL LLModalDialog::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ childrenHandleRightMouseDown(x, y, mask);
+ return TRUE;
+}
+
+
+BOOL LLModalDialog::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent )
+{
+ childrenHandleKey(key, mask);
+
+ LLFloater::handleKeyHere(key, mask, called_from_parent );
+
+ if (mModal)
+ {
+ // Suck up all keystokes except CTRL-Q.
+ BOOL is_quit = ('Q' == key) && (MASK_CONTROL == mask);
+ return !is_quit;
+ }
+ else
+ {
+ // don't process escape key until message box has been on screen a minimal amount of time
+ // to avoid accidentally destroying the message box when user is hitting escape at the time it appears
+ BOOL enough_time_elapsed = mVisibleTime.getElapsedTimeF32() > 1.0f;
+ if (enough_time_elapsed && key == KEY_ESCAPE)
+ {
+ close();
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+void LLModalDialog::onClose(bool app_quitting)
+{
+ stopModal();
+ LLFloater::onClose(app_quitting);
+}
+
+// virtual
+void LLModalDialog::draw()
+{
+ if (getVisible())
+ {
+ LLColor4 shadow_color = LLUI::sColorsGroup->getColor("ColorDropShadow");
+ S32 shadow_lines = LLUI::sConfigGroup->getS32("DropShadowFloater");
+
+ gl_drop_shadow( 0, mRect.getHeight(), mRect.getWidth(), 0,
+ shadow_color, shadow_lines);
+
+ LLFloater::draw();
+
+ if (mModal)
+ {
+ // If we've lost focus to a non-child, get it back ASAP.
+ if( gFocusMgr.getTopView() != this )
+ {
+ gFocusMgr.setTopView( this, NULL);
+ }
+
+ if( !gFocusMgr.childHasKeyboardFocus( this ) )
+ {
+ setFocus(TRUE);
+ }
+
+ if( !gFocusMgr.childHasMouseCapture( this ) )
+ {
+ gFocusMgr.setMouseCapture( this, NULL );
+ }
+ }
+ }
+}
+
+void LLModalDialog::centerOnScreen()
+{
+ LLVector2 window_size = LLUI::getWindowSize();
+
+ S32 dialog_left = (llround(window_size.mV[VX]) - mRect.getWidth()) / 2;
+ S32 dialog_bottom = (llround(window_size.mV[VY]) - mRect.getHeight()) / 2;
+
+ translate( dialog_left - mRect.mLeft, dialog_bottom - mRect.mBottom );
+}
+
+
+// static
+void LLModalDialog::onAppFocusLost()
+{
+ if( !sModalStack.empty() )
+ {
+ LLModalDialog* instance = LLModalDialog::sModalStack.front();
+ if( gFocusMgr.childHasMouseCapture( instance ) )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ }
+
+ if( gFocusMgr.childHasKeyboardFocus( instance ) )
+ {
+ gFocusMgr.setKeyboardFocus( NULL, NULL );
+ }
+ }
+}
+
+// static
+void LLModalDialog::onAppFocusGained()
+{
+ if( !sModalStack.empty() )
+ {
+ LLModalDialog* instance = LLModalDialog::sModalStack.front();
+
+ // This is a modal dialog. It sucks up all mouse and keyboard operations.
+ gFocusMgr.setMouseCapture( instance, NULL );
+ instance->setFocus(TRUE);
+ gFocusMgr.setTopView( instance, NULL );
+
+ instance->centerOnScreen();
+ }
+}
+
+
diff --git a/indra/llui/llmodaldialog.h b/indra/llui/llmodaldialog.h
new file mode 100644
index 0000000000..b97e95d12c
--- /dev/null
+++ b/indra/llui/llmodaldialog.h
@@ -0,0 +1,60 @@
+/**
+ * @file llmodaldialog.h
+ * @brief LLModalDialog base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMODALDIALOG_H
+#define LL_LLMODALDIALOG_H
+
+#include "llfloater.h"
+#include "llframetimer.h"
+
+class LLModalDialog;
+
+// By default, a ModalDialog is modal, i.e. no other window can have focus
+// However, for the sake of code reuse and simplicity, if mModal == false,
+// the dialog behaves like a normal floater
+
+class LLModalDialog : public LLFloater
+{
+public:
+ LLModalDialog( const LLString& title, S32 width, S32 height, BOOL modal = true );
+ /*virtual*/ ~LLModalDialog();
+
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = 1);
+
+ /*virtual*/ void startModal();
+ /*virtual*/ void stopModal();
+
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent );
+
+ /*virtual*/ void onClose(bool app_quitting);
+
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void draw();
+
+ static void onAppFocusLost();
+ static void onAppFocusGained();
+
+ static S32 activeCount() { return sModalStack.size(); }
+
+protected:
+ void centerOnScreen();
+
+protected:
+ LLFrameTimer mVisibleTime;
+ BOOL mModal; // do not change this after creation!
+
+ static std::list<LLModalDialog*> sModalStack; // Top of stack is currently being displayed
+};
+
+#endif // LL_LLMODALDIALOG_H
diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp
new file mode 100644
index 0000000000..91bf6befe7
--- /dev/null
+++ b/indra/llui/llpanel.cpp
@@ -0,0 +1,1030 @@
+/**
+ * @file llpanel.cpp
+ * @brief LLPanel base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Opaque view with a background and a border. Can contain LLUICtrls.
+
+#include "linden_common.h"
+
+#include "llpanel.h"
+
+#include "llalertdialog.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "lltimer.h"
+
+#include "llmenugl.h"
+//#include "llstatusbar.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llcontrol.h"
+#include "lltextbox.h"
+#include "lluictrl.h"
+#include "lluictrlfactory.h"
+#include "llviewborder.h"
+#include "llbutton.h"
+
+LLPanel::panel_map_t LLPanel::sPanelMap;
+LLPanel::alert_queue_t LLPanel::sAlertQueue;
+
+void LLPanel::init()
+{
+ // mRectControl
+ mBgColorAlpha = LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" );
+ mBgColorOpaque = LLUI::sColorsGroup->getColor( "FocusBackgroundColor" );
+ mDefaultBtnHighlight = LLUI::sColorsGroup->getColor( "DefaultHighlightLight" );
+ mBgVisible = FALSE;
+ mBgOpaque = FALSE;
+ mBorder = NULL;
+ mDefaultBtn = NULL;
+ setIsChrome(FALSE); //is this a decorator to a live window or a form?
+ mLastTabGroup = 0;
+
+ // add self to handle->panel map
+ sPanelMap[mViewHandle] = this;
+ setTabStop(FALSE);
+}
+
+LLPanel::LLPanel()
+: mRectControl()
+{
+ init();
+}
+
+LLPanel::LLPanel(const LLString& name)
+: LLUICtrl(name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL),
+ mRectControl()
+{
+ init();
+}
+
+
+LLPanel::LLPanel(const LLString& name, const LLRect& rect, BOOL bordered)
+: LLUICtrl(name, rect, TRUE, NULL, NULL),
+ mRectControl()
+{
+ init();
+ if (bordered)
+ {
+ addBorder();
+ }
+}
+
+
+LLPanel::LLPanel(const LLString& name, const LLString& rect_control, BOOL bordered)
+: LLUICtrl(name, LLUI::sConfigGroup->getRect(rect_control), TRUE, NULL, NULL),
+ mRectControl( rect_control )
+{
+ init();
+ if (bordered)
+ {
+ addBorder();
+ }
+}
+
+void LLPanel::addBorder(LLViewBorder::EBevel border_bevel,
+ LLViewBorder::EStyle border_style, S32 border_thickness)
+{
+ mBorder = new LLViewBorder( "panel border",
+ LLRect(0, mRect.getHeight(), mRect.getWidth(), 0),
+ border_bevel, border_style, border_thickness );
+ mBorder->setSaveToXML(false);
+ addChild( mBorder );
+}
+
+
+LLPanel::~LLPanel()
+{
+ if( !mRectControl.empty() )
+ {
+ LLUI::sConfigGroup->setRect( mRectControl, mRect );
+ }
+ sPanelMap.erase(mViewHandle);
+}
+
+
+// virtual
+EWidgetType LLPanel::getWidgetType() const
+{
+ return WIDGET_TYPE_PANEL;
+}
+
+// virtual
+LLString LLPanel::getWidgetTag() const
+{
+ return LL_PANEL_TAG;
+}
+
+// virtual
+BOOL LLPanel::isPanel()
+{
+ return TRUE;
+}
+
+// virtual
+BOOL LLPanel::postBuild()
+{
+ return TRUE;
+}
+
+// virtual
+void LLPanel::clearCtrls()
+{
+ LLView::ctrl_list_t ctrls = getCtrlList();
+ for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
+ {
+ LLUICtrl* ctrl = *ctrl_it;
+ ctrl->setFocus( FALSE );
+ ctrl->setEnabled( FALSE );
+ ctrl->clear();
+ }
+}
+
+void LLPanel::setCtrlsEnabled( BOOL b )
+{
+ LLView::ctrl_list_t ctrls = getCtrlList();
+ for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
+ {
+ LLUICtrl* ctrl = *ctrl_it;
+ ctrl->setEnabled( b );
+ }
+}
+
+void LLPanel::draw()
+{
+ if( getVisible() )
+ {
+ // draw background
+ if( mBgVisible )
+ {
+ S32 left = LLPANEL_BORDER_WIDTH;
+ S32 top = mRect.getHeight() - LLPANEL_BORDER_WIDTH;
+ S32 right = mRect.getWidth() - LLPANEL_BORDER_WIDTH;
+ S32 bottom = LLPANEL_BORDER_WIDTH;
+
+ if (mBgOpaque )
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorOpaque );
+ }
+ else
+ {
+ gl_rect_2d( left, top, right, bottom, mBgColorAlpha );
+ }
+ }
+
+ if( mDefaultBtn)
+ {
+ if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled())
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ BOOL focus_is_child_button = focus_ctrl->getWidgetType() == WIDGET_TYPE_BUTTON && static_cast<LLButton *>(focus_ctrl)->getCommitOnReturn();
+ // only enable default button when current focus is not a return-capturing button
+ mDefaultBtn->setBorderEnabled(!focus_is_child_button);
+ }
+ else
+ {
+ mDefaultBtn->setBorderEnabled(FALSE);
+ }
+ }
+
+ LLView::draw();
+ }
+}
+
+void LLPanel::refresh()
+{
+ // do nothing by default
+ // but is automatically called in setFocus(TRUE)
+}
+
+void LLPanel::setDefaultBtn(LLButton* btn)
+{
+ if (mDefaultBtn && mDefaultBtn->getEnabled())
+ {
+ mDefaultBtn->setBorderEnabled(FALSE);
+ }
+ mDefaultBtn = btn;
+ if (mDefaultBtn)
+ {
+ mDefaultBtn->setBorderEnabled(TRUE);
+ }
+}
+
+void LLPanel::setDefaultBtn(const LLString& id)
+{
+ LLButton *button = LLUICtrlFactory::getButtonByName(this, id);
+ if (button)
+ {
+ setDefaultBtn(button);
+ }
+ else
+ {
+ setDefaultBtn(NULL);
+ }
+}
+
+BOOL LLPanel::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+ if (getVisible() && getEnabled())
+ {
+ if( (mask == MASK_SHIFT) && (KEY_TAB == key))
+ {
+ //SHIFT-TAB
+ LLView* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ LLView* focus_root = cur_focus;
+ while(cur_focus->getParent())
+ {
+ cur_focus = cur_focus->getParent();
+ if (cur_focus->isFocusRoot())
+ {
+ // this is the root-most focus root found so far
+ focus_root = cur_focus;
+ }
+ }
+ handled = focus_root->focusPrevItem(FALSE);
+ }
+ else if (!cur_focus && mIsFocusRoot)
+ {
+ handled = focusLastItem();
+ if (!handled)
+ {
+ setFocus(TRUE);
+ handled = TRUE;
+ }
+ }
+ }
+ else
+ if( (mask == MASK_NONE ) && (KEY_TAB == key))
+ {
+ //TAB
+ LLView* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ LLView* focus_root = cur_focus;
+ while(cur_focus->getParent())
+ {
+ cur_focus = cur_focus->getParent();
+ if (cur_focus->isFocusRoot())
+ {
+ focus_root = cur_focus;
+ }
+ }
+ handled = focus_root->focusNextItem(FALSE);
+ }
+ else if (!cur_focus && mIsFocusRoot)
+ {
+ handled = focusFirstItem();
+ if (!handled)
+ {
+ setFocus(TRUE);
+ handled = TRUE;
+ }
+ }
+ }
+ }
+
+ if (!handled)
+ {
+ handled = LLView::handleKey(key, mask, called_from_parent);
+ }
+
+ return handled;
+}
+
+void LLPanel::addCtrl( LLUICtrl* ctrl, S32 tab_group)
+{
+ mLastTabGroup = tab_group;
+
+ LLView::addCtrl(ctrl, tab_group);
+ // propagate chrome to children only if they have not been flagged as chrome
+ if (!ctrl->getIsChrome())
+ {
+ ctrl->setIsChrome(getIsChrome());
+ }
+}
+
+void LLPanel::addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group)
+{
+ mLastTabGroup = tab_group;
+
+ LLView::addCtrlAtEnd(ctrl, tab_group);
+ if (!ctrl->getIsChrome())
+ {
+ ctrl->setIsChrome(getIsChrome());
+ }
+}
+
+BOOL LLPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ if( getVisible() && getEnabled() && gFocusMgr.childHasKeyboardFocus(this) && KEY_ESCAPE == key )
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+
+ if( getVisible() && getEnabled() &&
+ gFocusMgr.childHasKeyboardFocus(this) && !called_from_parent )
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ // set keyboard focus to self to trigger commitOnFocusLost behavior on current ctrl
+ if (cur_focus && cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ handled = TRUE;
+ }
+ }
+
+ // If we have a default button, click it when
+ // return is pressed, unless current focus is a return-capturing button
+ // in which case *that* button will handle the return key
+ if (!(cur_focus->getWidgetType() == WIDGET_TYPE_BUTTON && static_cast<LLButton *>(cur_focus)->getCommitOnReturn()))
+ {
+ // RETURN key means hit default button in this case
+ if (key == KEY_RETURN && mask == MASK_NONE
+ && mDefaultBtn != NULL
+ && mDefaultBtn->getVisible()
+ && mDefaultBtn->getEnabled())
+ {
+ mDefaultBtn->onCommit();
+ handled = TRUE;
+ }
+ }
+ }
+
+ return handled;
+}
+
+void LLPanel::requires(LLString name, EWidgetType type)
+{
+ mRequirements[name] = type;
+}
+
+BOOL LLPanel::checkRequirements()
+{
+ BOOL retval = TRUE;
+ LLString message;
+
+ for (requirements_map_t::iterator i = mRequirements.begin(); i != mRequirements.end(); ++i)
+ {
+ if (!this->getCtrlByNameAndType(i->first, i->second))
+ {
+ retval = FALSE;
+ message += i->first + " " + LLUICtrlFactory::getWidgetType(i->second) + "\n";
+ }
+ }
+
+ if (!retval)
+ {
+ LLString::format_map_t args;
+ args["[COMPONENTS]"] = message;
+ args["[FLOATER]"] = getName();
+
+ llwarns << getName() << " failed requirements check on: \n"
+ << message << llendl;
+
+ alertXml("FailedRequirementsCheck", args);
+ }
+
+ return retval;
+}
+
+//static
+void LLPanel::alertXml(LLString label, LLString::format_map_t args)
+{
+ sAlertQueue.push(LLAlertInfo(label,args));
+}
+
+//static
+BOOL LLPanel::nextAlert(LLAlertInfo &alert)
+{
+ if (!sAlertQueue.empty())
+ {
+ alert = sAlertQueue.front();
+ sAlertQueue.pop();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLPanel::setFocus(BOOL b)
+{
+ if( b )
+ {
+ if (!gFocusMgr.childHasKeyboardFocus(this))
+ {
+ //refresh();
+ if (!focusFirstItem())
+ {
+ LLUICtrl::setFocus(TRUE);
+ }
+ onFocusReceived();
+ }
+ }
+ else
+ {
+ if( this == gFocusMgr.getKeyboardFocus() )
+ {
+ gFocusMgr.setKeyboardFocus( NULL, NULL );
+ }
+ else
+ {
+ //RN: why is this here?
+ LLView::ctrl_list_t ctrls = getCtrlList();
+ for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it)
+ {
+ LLUICtrl* ctrl = *ctrl_it;
+ ctrl->setFocus( FALSE );
+ }
+ }
+ }
+}
+
+void LLPanel::setBackgroundColor(const LLColor4& color)
+{
+ mBgColorOpaque = color;
+}
+
+void LLPanel::setTransparentColor(const LLColor4& color)
+{
+ mBgColorAlpha = color;
+}
+
+void LLPanel::setBorderVisible(BOOL b)
+{
+ if (mBorder)
+ {
+ mBorder->setVisible( b );
+ }
+}
+
+LLView* LLPanel::getCtrlByNameAndType(const LLString& name, EWidgetType type)
+{
+ LLView* view = getChildByName(name, TRUE);
+ if (view)
+ {
+ if (type == WIDGET_TYPE_DONTCARE || view->getWidgetType() == type)
+ {
+ return view;
+ }
+ else
+ {
+ llwarns << "Widget " << name << " has improper type in panel " << mName << "\n"
+ << "Is: \t\t" << view->getWidgetType() << "\n"
+ << "Should be: \t" << type
+ << llendl;
+ }
+ }
+ else
+ {
+ childNotFound(name);
+ }
+ return NULL;
+}
+
+// static
+LLPanel* LLPanel::getPanelByHandle(LLViewHandle handle)
+{
+ if (!sPanelMap.count(handle))
+ {
+ return NULL;
+ }
+
+ return sPanelMap[handle];
+}
+
+// virtual
+LLXMLNodePtr LLPanel::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLView::getXML();
+
+ if (mBorder && mBorder->getVisible())
+ {
+ node->createChild("border", TRUE)->setBoolValue(TRUE);
+ }
+
+ if (!mRectControl.empty())
+ {
+ node->createChild("rect_control", TRUE)->setStringValue(mRectControl);
+ }
+
+ if (!mLabel.empty())
+ {
+ node->createChild("label", TRUE)->setStringValue(mLabel);
+ }
+
+ if (save_children)
+ {
+ LLView::child_list_const_reverse_iter_t rit;
+ for (rit = getChildList()->rbegin(); rit != getChildList()->rend(); ++rit)
+ {
+ LLView* childp = *rit;
+
+ if (childp->getSaveToXML())
+ {
+ LLXMLNodePtr xml_node = childp->getXML();
+
+ node->addChild(xml_node);
+ }
+ }
+ }
+
+ return node;
+}
+
+LLView* LLPanel::fromXML(LLXMLNodePtr node, LLView* parentp, LLUICtrlFactory *factory)
+{
+ LLString name("panel");
+ node->getAttributeString("name", name);
+
+ LLPanel* panelp = factory->createFactoryPanel(name);
+ // Fall back on a default panel, if there was no special factory.
+ if (!panelp)
+ {
+ panelp = new LLPanel("tab panel");
+ }
+
+ panelp->initPanelXML(node, parentp, factory);
+
+ return panelp;
+}
+
+void LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("panel");
+ node->getAttributeString("name", name);
+ setName(name);
+
+ setPanelParameters(node, parent);
+
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ factory->createWidget(this, child);
+ }
+
+ LLString xml_filename;
+ node->getAttributeString("filename", xml_filename);
+ if (!xml_filename.empty())
+ {
+ factory->buildPanel(this, xml_filename, NULL);
+ }
+
+ postBuild();
+}
+
+void LLPanel::setPanelParameters(LLXMLNodePtr node, LLView* parentp)
+{
+ /////// Rect, follows, tool_tip, enabled, visible attributes ///////
+ initFromXML(node, parentp);
+
+ /////// Border attributes ///////
+ BOOL border = FALSE;
+ node->getAttributeBOOL("border", border);
+ if (border)
+ {
+ LLViewBorder::EBevel bevel_style = LLViewBorder::BEVEL_OUT;
+ LLViewBorder::getBevelFromAttribute(node, bevel_style);
+
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE;
+ LLString border_string;
+ node->getAttributeString("border_style", border_string);
+ LLString::toLower(border_string);
+
+ if (border_string == "texture")
+ {
+ border_style = LLViewBorder::STYLE_TEXTURE;
+ }
+
+ S32 border_thickness = LLPANEL_BORDER_WIDTH;
+ node->getAttributeS32("border_thickness", border_thickness);
+
+ addBorder(bevel_style, border_style, border_thickness);
+ }
+
+ /////// Background attributes ///////
+ BOOL background_visible = FALSE;
+ node->getAttributeBOOL("background_visible", background_visible);
+ setBackgroundVisible(background_visible);
+
+ BOOL background_opaque = FALSE;
+ node->getAttributeBOOL("background_opaque", background_opaque);
+ setBackgroundOpaque(background_opaque);
+
+ LLColor4 color;
+ color = LLUI::sColorsGroup->getColor( "FocusBackgroundColor" );
+ LLUICtrlFactory::getAttributeColor(node,"bg_opaque_color", color);
+ setBackgroundColor(color);
+
+ color = LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" );
+ LLUICtrlFactory::getAttributeColor(node,"bg_alpha_color", color);
+ setTransparentColor(color);
+
+ LLString label;
+ node->getAttributeString("label", label);
+ setLabel(label);
+}
+
+void LLPanel::childSetVisible(const LLString& id, bool visible)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ child->setVisible(visible);
+ }
+}
+
+bool LLPanel::childIsVisible(const LLString& id) const
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ return (bool)child->getVisible();
+ }
+ return false;
+}
+
+void LLPanel::childSetEnabled(const LLString& id, bool enabled)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ child->setEnabled(enabled);
+ }
+}
+
+void LLPanel::childSetTentative(const LLString& id, bool tentative)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ child->setTentative(tentative);
+ }
+}
+
+bool LLPanel::childIsEnabled(const LLString& id) const
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ return (bool)child->getEnabled();
+ }
+ return false;
+}
+
+
+void LLPanel::childSetToolTip(const LLString& id, const LLString& msg)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ child->setToolTip(msg);
+ }
+}
+
+void LLPanel::childSetRect(const LLString& id, const LLRect& rect)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ child->setRect(rect);
+ }
+}
+
+bool LLPanel::childGetRect(const LLString& id, LLRect& rect) const
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ rect = child->getRect();
+ return true;
+ }
+ return false;
+}
+
+void LLPanel::childSetFocus(const LLString& id, BOOL focus)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setFocus(focus);
+ }
+}
+
+BOOL LLPanel::childHasFocus(const LLString& id)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->hasFocus();
+ }
+ else
+ {
+ childNotFound(id);
+ return FALSE;
+ }
+}
+
+
+void LLPanel::childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*))
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setFocusChangedCallback(cb);
+ }
+}
+
+void LLPanel::childSetCommitCallback(const LLString& id, void (*cb)(LLUICtrl*, void*), void *userdata )
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setCommitCallback(cb);
+ child->setCallbackUserData(userdata);
+ }
+}
+
+void LLPanel::childSetDoubleClickCallback(const LLString& id, void (*cb)(void*), void *userdata )
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setDoubleClickCallback(cb);
+ if (userdata)
+ {
+ child->setCallbackUserData(userdata);
+ }
+ }
+}
+
+void LLPanel::childSetValidate(const LLString& id, BOOL (*cb)(LLUICtrl*, void*))
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setValidateBeforeCommit(cb);
+ }
+}
+
+void LLPanel::childSetUserData(const LLString& id, void* userdata)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setCallbackUserData(userdata);
+ }
+}
+
+void LLPanel::childSetColor(const LLString& id, const LLColor4& color)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setColor(color);
+ }
+}
+
+LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const LLString& id)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->getSelectionInterface();
+ }
+ return NULL;
+}
+
+LLCtrlListInterface* LLPanel::childGetListInterface(const LLString& id)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->getListInterface();
+ }
+ return NULL;
+}
+
+LLCtrlScrollInterface* LLPanel::childGetScrollInterface(const LLString& id)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->getScrollInterface();
+ }
+ return NULL;
+}
+
+void LLPanel::childSetValue(const LLString& id, LLSD value)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setValue(value);
+ }
+}
+
+LLSD LLPanel::childGetValue(const LLString& id) const
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->getValue();
+ }
+ // Not found => return undefined
+ return LLSD();
+}
+
+BOOL LLPanel::childSetTextArg(const LLString& id, const LLString& key, const LLString& text)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ return child->setTextArg(key, text);
+ }
+ return FALSE;
+}
+
+BOOL LLPanel::childSetLabelArg(const LLString& id, const LLString& key, const LLString& text)
+{
+ LLView* child = getChildByName(id, true);
+ if (child)
+ {
+ return child->setLabelArg(key, text);
+ }
+ return FALSE;
+}
+
+void LLPanel::childSetMinValue(const LLString& id, LLSD min_value)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setMinValue(min_value);
+ }
+}
+
+void LLPanel::childSetMaxValue(const LLString& id, LLSD max_value)
+{
+ LLUICtrl* child = (LLUICtrl*)getChildByName(id, true);
+ if (child)
+ {
+ child->setMaxValue(max_value);
+ }
+}
+
+void LLPanel::childShowTab(const LLString& id, const LLString& tabname, bool visible)
+{
+ LLTabContainerCommon* child = LLUICtrlFactory::getTabContainerByName(this, id);
+ if (child)
+ {
+ child->selectTabByName(tabname);
+ }
+}
+
+LLPanel *LLPanel::childGetVisibleTab(const LLString& id)
+{
+ LLTabContainerCommon* child = LLUICtrlFactory::getTabContainerByName(this, id);
+ if (child)
+ {
+ return child->getCurrentPanel();
+ }
+ return NULL;
+}
+
+void LLPanel::childSetTabChangeCallback(const LLString& id, const LLString& tabname, void (*on_tab_clicked)(void*, bool), void *userdata)
+{
+ LLTabContainerCommon* child = LLUICtrlFactory::getTabContainerByName(this, id);
+ if (child)
+ {
+ LLPanel *panel = child->getPanelByName(tabname);
+ if (panel)
+ {
+ child->setTabChangeCallback(panel, on_tab_clicked);
+ child->setTabUserData(panel, userdata);
+ }
+ }
+}
+
+void LLPanel::childSetText(const LLString& id, const LLString& text)
+{
+ childSetValue(id, LLSD(text));
+}
+
+void LLPanel::childSetKeystrokeCallback(const LLString& id, void (*keystroke_callback)(LLLineEditor* caller, void* user_data), void *user_data)
+{
+ LLLineEditor* child = LLUICtrlFactory::getLineEditorByName(this, id);
+ if (child)
+ {
+ child->setKeystrokeCallback(keystroke_callback);
+ if (user_data)
+ {
+ child->setCallbackUserData(user_data);
+ }
+ }
+}
+
+void LLPanel::childSetPrevalidate(const LLString& id, BOOL (*func)(const LLWString &) )
+{
+ LLLineEditor* child = LLUICtrlFactory::getLineEditorByName(this, id);
+ if (child)
+ {
+ child->setPrevalidate(func);
+ }
+}
+
+LLString LLPanel::childGetText(const LLString& id)
+{
+ return childGetValue(id).asString();
+}
+
+void LLPanel::childSetWrappedText(const LLString& id, const LLString& text, bool visible)
+{
+ LLTextBox* child = (LLTextBox*)getCtrlByNameAndType(id, WIDGET_TYPE_TEXT_BOX);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setWrappedText(text);
+ }
+}
+
+void LLPanel::childSetAction(const LLString& id, void(*function)(void*), void* value)
+{
+ LLButton* button = (LLButton*)getCtrlByNameAndType(id, WIDGET_TYPE_BUTTON);
+ if (button)
+ {
+ button->setClickedCallback(function, value);
+ }
+}
+
+void LLPanel::childSetActionTextbox(const LLString& id, void(*function)(void*))
+{
+ LLTextBox* textbox = (LLTextBox*)getCtrlByNameAndType(id, WIDGET_TYPE_TEXT_BOX);
+ if (textbox)
+ {
+ textbox->setClickedCallback(function);
+ }
+}
+
+void LLPanel::childSetControlName(const LLString& id, const LLString& control_name)
+{
+ LLView* view = getChildByName(id, TRUE);
+ if (view)
+ {
+ view->setControlName(control_name, NULL);
+ }
+}
+
+//virtual
+LLView* LLPanel::getChildByName(const LLString& name, BOOL recurse) const
+{
+ LLView* view = LLUICtrl::getChildByName(name, recurse);
+ if (!view)
+ {
+ childNotFound(name);
+ }
+ return view;
+}
+
+void LLPanel::childNotFound(const LLString& id) const
+{
+ if (mExpectedMembers.find(id) == mExpectedMembers.end())
+ {
+ mNewExpectedMembers.insert(id);
+ }
+}
+
+void LLPanel::childDisplayNotFound()
+{
+ if (mNewExpectedMembers.empty())
+ {
+ return;
+ }
+ LLString msg;
+ expected_members_list_t::iterator itor;
+ for (itor=mNewExpectedMembers.begin(); itor!=mNewExpectedMembers.end(); ++itor)
+ {
+ msg.append(*itor);
+ msg.append("\n");
+ mExpectedMembers.insert(*itor);
+ }
+ mNewExpectedMembers.clear();
+ LLString::format_map_t args;
+ args["[CONTROLS]"] = msg;
+ LLAlertDialog::showXml("FloaterNotFound", args);
+}
+
diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h
new file mode 100644
index 0000000000..9da27b6f38
--- /dev/null
+++ b/indra/llui/llpanel.h
@@ -0,0 +1,229 @@
+/**
+ * @file llpanel.h
+ * @author James Cook, Tom Yedwab
+ * @brief LLPanel base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANEL_H
+#define LL_LLPANEL_H
+
+// Opaque view with a background and a border. Can contain LLUICtrls.
+
+#include "llcallbackmap.h"
+#include "lluictrl.h"
+#include "llviewborder.h"
+#include "v4color.h"
+#include <list>
+
+const S32 LLPANEL_BORDER_WIDTH = 1;
+const BOOL BORDER_YES = TRUE;
+const BOOL BORDER_NO = FALSE;
+
+class LLViewerImage;
+class LLUUID;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLIconCtrl;
+class LLLineEditor;
+class LLRadioGroup;
+class LLScrollListCtrl;
+class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTextBox;
+class LLTextEditor;
+
+class LLAlertInfo
+{
+public:
+ LLString mLabel;
+ LLString::format_map_t mArgs;
+
+ LLAlertInfo(LLString label, LLString::format_map_t args)
+ : mLabel(label), mArgs(args) { }
+
+ LLAlertInfo() { }
+};
+
+class LLPanel : public LLUICtrl
+{
+public:
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // defaults to TRUE
+ virtual BOOL isPanel();
+
+ // minimal constructor for data-driven initialization
+ LLPanel();
+ LLPanel(const LLString& name);
+
+ // Position and size not saved
+ LLPanel(const LLString& name, const LLRect& rect, BOOL bordered = TRUE);
+
+ // Position and size are saved to rect_control
+ LLPanel(const LLString& name, const LLString& rect_control, BOOL bordered = TRUE);
+
+ void addBorder( LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_OUT,
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE,
+ S32 border_thickness = LLPANEL_BORDER_WIDTH );
+
+ virtual ~LLPanel();
+ virtual void draw();
+ virtual void refresh(); // called in setFocus()
+ virtual void setFocus( BOOL b );
+ void setFocusRoot(BOOL b) { mIsFocusRoot = b; }
+ virtual BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL postBuild();
+
+ void requires(LLString name, EWidgetType type = WIDGET_TYPE_DONTCARE);
+ BOOL checkRequirements();
+
+ static void alertXml(LLString label, LLString::format_map_t args = LLString::format_map_t());
+ static BOOL nextAlert(LLAlertInfo &alert);
+
+ void setBackgroundColor( const LLColor4& color );
+ void setTransparentColor(const LLColor4& color);
+ void setBackgroundVisible( BOOL b ) { mBgVisible = b; }
+ void setBackgroundOpaque(BOOL b) { mBgOpaque = b; }
+ void setDefaultBtn(LLButton* btn = NULL);
+ void setDefaultBtn(const LLString& id);
+ void setLabel(LLString label) { mLabel = label; }
+ LLString getLabel() const { return mLabel; }
+
+ void setRectControl(const LLString& rect_control) { mRectControl.assign(rect_control); }
+
+ void setBorderVisible( BOOL b );
+
+ void setCtrlsEnabled(BOOL b);
+ virtual void clearCtrls();
+
+ LLViewHandle getHandle() { return mViewHandle; }
+
+ S32 getLastTabGroup() { return mLastTabGroup; }
+
+ LLView* getCtrlByNameAndType(const LLString& name, EWidgetType type);
+
+ static LLPanel* getPanelByHandle(LLViewHandle handle);
+
+ virtual const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; }
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void initPanelXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void setPanelParameters(LLXMLNodePtr node, LLView *parentp);
+
+ // ** Wrappers for setting child properties by name ** -TomY
+
+ // Override to set not found list
+ virtual LLView* getChildByName(const LLString& name, BOOL recurse = FALSE) const;
+
+ // LLView
+ void childSetVisible(const LLString& name, bool visible);
+ void childShow(const LLString& name) { childSetVisible(name, true); }
+ void childHide(const LLString& name) { childSetVisible(name, false); }
+ bool childIsVisible(const LLString& id) const;
+ void childSetTentative(const LLString& name, bool tentative);
+
+ void childSetEnabled(const LLString& name, bool enabled);
+ void childEnable(const LLString& name) { childSetEnabled(name, true); }
+ void childDisable(const LLString& name) { childSetEnabled(name, false); };
+ bool childIsEnabled(const LLString& id) const;
+
+ void childSetToolTip(const LLString& id, const LLString& msg);
+ void childSetRect(const LLString& id, const LLRect &rect);
+ bool childGetRect(const LLString& id, LLRect& rect) const;
+
+ // LLUICtrl
+ void childSetFocus(const LLString& id, BOOL focus = TRUE);
+ BOOL childHasFocus(const LLString& id);
+ void childSetFocusChangedCallback(const LLString& id, void (*cb)(LLUICtrl*, void*));
+
+ void childSetCommitCallback(const LLString& id, void (*cb)(LLUICtrl*, void*), void* userdata = NULL );
+ void childSetDoubleClickCallback(const LLString& id, void (*cb)(void*), void* userdata = NULL );
+ void childSetValidate(const LLString& id, BOOL (*cb)(LLUICtrl*, void*) );
+ void childSetUserData(const LLString& id, void* userdata);
+
+ void childSetColor(const LLString& id, const LLColor4& color);
+
+ LLCtrlSelectionInterface* childGetSelectionInterface(const LLString& id);
+ LLCtrlListInterface* childGetListInterface(const LLString& id);
+ LLCtrlScrollInterface* childGetScrollInterface(const LLString& id);
+
+ // This is the magic bullet for data-driven UI
+ void childSetValue(const LLString& id, LLSD value);
+ LLSD childGetValue(const LLString& id) const;
+
+ // For setting text / label replacement params, e.g. "Hello [NAME]"
+ // Not implemented for all types, defaults to noop, returns FALSE if not applicaple
+ BOOL childSetTextArg(const LLString& id, const LLString& key, const LLString& text);
+ BOOL childSetLabelArg(const LLString& id, const LLString& key, const LLString& text);
+
+ // LLSlider / LLSpinCtrl
+ void childSetMinValue(const LLString& id, LLSD min_value);
+ void childSetMaxValue(const LLString& id, LLSD max_value);
+
+ // LLTabContainer
+ void childShowTab(const LLString& id, const LLString& tabname, bool visible = true);
+ LLPanel *childGetVisibleTab(const LLString& id);
+ void childSetTabChangeCallback(const LLString& id, const LLString& tabname, void (*on_tab_clicked)(void*, bool), void *userdata);
+
+ // LLTextBox
+ void childSetWrappedText(const LLString& id, const LLString& text, bool visible = true);
+
+ // LLTextBox/LLTextEditor/LLLineEditor
+ void childSetText(const LLString& id, const LLString& text);
+ LLString childGetText(const LLString& id);
+
+ // LLLineEditor
+ void childSetKeystrokeCallback(const LLString& id, void (*keystroke_callback)(LLLineEditor* caller, void* user_data), void *user_data);
+ void childSetPrevalidate(const LLString& id, BOOL (*func)(const LLWString &) );
+
+ // LLButton
+ void childSetAction(const LLString& id, void(*function)(void*), void* value);
+ void childSetActionTextbox(const LLString& id, void(*function)(void*));
+ void childSetControlName(const LLString& id, const LLString& control_name);
+
+ // Error reporting
+ void childNotFound(const LLString& id) const;
+ void childDisplayNotFound();
+
+ typedef std::queue<LLAlertInfo> alert_queue_t;
+ static alert_queue_t sAlertQueue;
+
+private:
+ // common constructor
+ void init();
+
+protected:
+ virtual void addCtrl( LLUICtrl* ctrl, S32 tab_group );
+ virtual void addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group);
+
+ // Unified error reporting for the child* functions
+ typedef std::set<LLString> expected_members_list_t;
+ mutable expected_members_list_t mExpectedMembers;
+ mutable expected_members_list_t mNewExpectedMembers;
+
+ LLString mRectControl;
+ LLColor4 mBgColorAlpha;
+ LLColor4 mBgColorOpaque;
+ LLColor4 mDefaultBtnHighlight;
+ BOOL mBgVisible;
+ BOOL mBgOpaque;
+ LLViewBorder* mBorder;
+ LLButton* mDefaultBtn;
+ LLCallbackMap::map_t mFactoryMap;
+ LLString mLabel;
+ S32 mLastTabGroup;
+
+ typedef std::map<LLString, EWidgetType> requirements_map_t;
+ requirements_map_t mRequirements;
+
+ typedef std::map<LLViewHandle, LLPanel*> panel_map_t;
+ static panel_map_t sPanelMap;
+};
+
+#endif
diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp
new file mode 100644
index 0000000000..69c0da6933
--- /dev/null
+++ b/indra/llui/llradiogroup.cpp
@@ -0,0 +1,440 @@
+/**
+ * @file llradiogroup.cpp
+ * @brief LLRadioGroup base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// An invisible view containing multiple mutually exclusive toggling
+// buttons (usually radio buttons). Automatically handles the mutex
+// condition by highlighting only one button at a time.
+
+#include "linden_common.h"
+
+#include "llboost.h"
+
+#include "llradiogroup.h"
+#include "indra_constants.h"
+
+#include "llviewborder.h"
+#include "llcontrol.h"
+#include "llui.h"
+#include "llfocusmgr.h"
+
+LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
+ const LLString& control_name,
+ LLUICtrlCallback callback,
+ void* userdata,
+ BOOL border)
+: LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mSelectedIndex(0)
+{
+ setControlName(control_name, NULL);
+ init(border);
+}
+
+LLRadioGroup::LLRadioGroup(const LLString& name, const LLRect& rect,
+ S32 initial_index,
+ LLUICtrlCallback callback,
+ void* userdata,
+ BOOL border) :
+ LLUICtrl(name, rect, TRUE, callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mSelectedIndex(initial_index)
+{
+ init(border);
+}
+
+void LLRadioGroup::init(BOOL border)
+{
+ if (border)
+ {
+ addChild( new LLViewBorder( "radio group border",
+ LLRect(0, mRect.getHeight(), mRect.getWidth(), 0),
+ LLViewBorder::BEVEL_NONE,
+ LLViewBorder::STYLE_LINE,
+ 1 ) );
+ }
+ mHasBorder = border;
+}
+
+
+
+
+LLRadioGroup::~LLRadioGroup()
+{
+}
+
+
+// virtual
+void LLRadioGroup::setEnabled(BOOL enabled)
+{
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *child = *child_iter;
+ child->setEnabled(enabled);
+ }
+ LLView::setEnabled(enabled);
+}
+
+void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled)
+{
+ S32 count = 0;
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* child = *iter;
+ if (count == index)
+ {
+ child->setEnabled(enabled);
+ if (index == mSelectedIndex && enabled == FALSE)
+ {
+ setSelectedIndex(-1);
+ }
+ break;
+ }
+ count++;
+ }
+ count = 0;
+ if (mSelectedIndex < 0)
+ {
+ // Set to highest enabled value < index,
+ // or lowest value above index if none lower are enabled
+ // or 0 if none are enabled
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* child = *iter;
+ if (count >= index && mSelectedIndex >= 0)
+ {
+ break;
+ }
+ if (child->getEnabled())
+ {
+ setSelectedIndex(count);
+ }
+ count++;
+ }
+ if (mSelectedIndex < 0)
+ {
+ setSelectedIndex(0);
+ }
+ }
+}
+
+S32 LLRadioGroup::getSelectedIndex() const
+{
+ return mSelectedIndex;
+}
+
+BOOL LLRadioGroup::setSelectedIndex(S32 index, BOOL from_event)
+{
+ if (index < 0 || index >= (S32)mRadioButtons.size())
+ {
+ return FALSE;
+ }
+
+ mSelectedIndex = index;
+
+ if (!from_event)
+ {
+ setControlValue(getSelectedIndex());
+ }
+
+ return TRUE;
+}
+
+BOOL LLRadioGroup::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+ // do any of the tab buttons have keyboard focus?
+ if (getEnabled() && !called_from_parent)
+ {
+ switch(key)
+ {
+ case KEY_DOWN:
+ if (!setSelectedIndex((getSelectedIndex() + 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ break;
+ case KEY_UP:
+ if (!setSelectedIndex((getSelectedIndex() - 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ break;
+ case KEY_LEFT:
+ if (!setSelectedIndex((getSelectedIndex() - 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ break;
+ case KEY_RIGHT:
+ if (!setSelectedIndex((getSelectedIndex() + 1)))
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+ else
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ return handled;
+}
+
+void LLRadioGroup::draw()
+{
+ S32 current_button = 0;
+
+ BOOL take_focus = FALSE;
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ take_focus = TRUE;
+ }
+
+ for (button_list_t::iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+ BOOL selected = (current_button == mSelectedIndex);
+ radio->setValue( selected );
+ if (take_focus && selected && !gFocusMgr.childHasKeyboardFocus(radio))
+ {
+ radio->focusFirstItem();
+ }
+ current_button++;
+ }
+
+ LLView::draw();
+}
+
+
+// When adding a button, we need to ensure that the radio
+// group gets a message when the button is clicked.
+LLRadioCtrl* LLRadioGroup::addRadioButton(const LLString& name, const LLString& label, const LLRect& rect, const LLFontGL* font )
+{
+ // Highlight will get fixed in draw method above
+ LLRadioCtrl* radio = new LLRadioCtrl(name, rect, label, font,
+ onClickButton, this);
+ addChild(radio);
+ mRadioButtons.push_back(radio);
+ return radio;
+}
+
+// Handle one button being clicked. All child buttons must have this
+// function as their callback function.
+
+// static
+void LLRadioGroup::onClickButton(LLUICtrl* ui_ctrl, void* userdata)
+{
+ // llinfos << "LLRadioGroup::onClickButton" << llendl;
+
+ LLRadioCtrl* clickedRadio = (LLRadioCtrl*) ui_ctrl;
+ LLRadioGroup* self = (LLRadioGroup*) userdata;
+
+ S32 counter = 0;
+ for (button_list_t::iterator iter = self->mRadioButtons.begin();
+ iter != self->mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+ if (radio == clickedRadio)
+ {
+ // llinfos << "clicked button " << counter << llendl;
+ self->setSelectedIndex(counter);
+ self->setControlValue(counter);
+
+ // BUG: Calls click callback even if button didn't actually change
+ self->onCommit();
+
+ return;
+ }
+
+ counter++;
+ }
+
+ llwarns << "LLRadioGroup::onClickButton - clicked button that isn't a child" << llendl;
+}
+
+void LLRadioGroup::setValue( const LLSD& value )
+{
+ LLString value_name = value.asString();
+ int idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+ if (radio->getName() == value_name)
+ {
+ setSelectedIndex(idx);
+ idx = -1;
+ break;
+ }
+ ++idx;
+ }
+ if (idx != -1)
+ {
+ // string not found, try integer
+ if (value.isInteger())
+ {
+ setSelectedIndex((S32) value.asInteger(), TRUE);
+ }
+ else
+ {
+ llwarns << "LLRadioGroup::setValue: value not found: " << value_name << llendl;
+ }
+ }
+}
+
+LLSD LLRadioGroup::getValue() const
+{
+ int index = getSelectedIndex();
+ int idx = 0;
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ if (idx == index) return LLSD((*iter)->getName());
+ ++idx;
+ }
+ return LLSD();
+}
+
+// virtual
+LLXMLNodePtr LLRadioGroup::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ // Attributes
+
+ node->createChild("draw_border", TRUE)->setBoolValue(mHasBorder);
+
+ // Contents
+
+ for (button_list_t::const_iterator iter = mRadioButtons.begin();
+ iter != mRadioButtons.end(); ++iter)
+ {
+ LLRadioCtrl* radio = *iter;
+
+ LLXMLNodePtr child_node = radio->LLView::getXML();
+ child_node->setStringValue(radio->getLabel());
+ child_node->setName("radio_item");
+
+ node->addChild(child_node);
+ }
+
+ return node;
+}
+
+// static
+LLView* LLRadioGroup::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("radio_group");
+ node->getAttributeString("name", name);
+
+ U32 initial_value = 0;
+ node->getAttributeU32("initial_value", initial_value);
+
+ BOOL draw_border = TRUE;
+ node->getAttributeBOOL("draw_border", draw_border);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLRadioGroup* radio_group = new LLRadioGroup(name,
+ rect,
+ initial_value,
+ NULL,
+ NULL,
+ draw_border);
+
+ const LLString& contents = node->getValue();
+
+ LLRect group_rect = radio_group->getRect();
+
+ LLFontGL *font = LLView::selectFont(node);
+
+ if (contents.find_first_not_of(" \n\t") != contents.npos)
+ {
+ // ...old school default vertical layout
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\t\n");
+ tokenizer tokens(contents, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ const S32 HPAD = 4, VPAD = 4;
+ S32 cur_y = group_rect.getHeight() - VPAD;
+
+ while(token_iter != tokens.end())
+ {
+ const char* line = token_iter->c_str();
+ LLRect rect(HPAD, cur_y, group_rect.getWidth() - (2 * HPAD), cur_y - 15);
+ cur_y -= VPAD + 15;
+ radio_group->addRadioButton("radio", line, rect, font);
+ ++token_iter;
+ }
+ llwarns << "Legacy radio group format used! Please convert to use <radio_item> tags!" << llendl;
+ }
+ else
+ {
+ // ...per pixel layout
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("radio_item"))
+ {
+ LLRect item_rect;
+ createRect(child, item_rect, radio_group, rect);
+
+ LLString radioname("radio");
+ child->getAttributeString("name", radioname);
+ LLString item_label = child->getTextContents();
+ LLRadioCtrl* radio = radio_group->addRadioButton(radioname, item_label.c_str(), item_rect, font);
+
+ radio->initFromXML(child, radio_group);
+ }
+ }
+ }
+
+ radio_group->initFromXML(node, parent);
+
+ return radio_group;
+}
+
+
+LLRadioCtrl::LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label,
+ const LLFontGL* font, void (*commit_callback)(LLUICtrl*, void*), void* callback_userdata) :
+ LLCheckBoxCtrl(name, rect, label, font, commit_callback, callback_userdata, FALSE, RADIO_STYLE)
+{
+ setTabStop(FALSE);
+}
+
+LLRadioCtrl::~LLRadioCtrl()
+{
+}
+
+void LLRadioCtrl::setValue(const LLSD& value)
+{
+ LLCheckBoxCtrl::setValue(value);
+ mButton->setTabStop(value.asBoolean());
+}
diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h
new file mode 100644
index 0000000000..01b4a61b82
--- /dev/null
+++ b/indra/llui/llradiogroup.h
@@ -0,0 +1,102 @@
+/**
+ * @file llradiogroup.h
+ * @brief LLRadioGroup base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// An invisible view containing multiple mutually exclusive toggling
+// buttons (usually radio buttons). Automatically handles the mutex
+// condition by highlighting only one button at a time.
+
+#ifndef LL_LLRADIOGROUP_H
+#define LL_LLRADIOGROUP_H
+
+#include "lluictrl.h"
+#include "llcheckboxctrl.h"
+
+class LLFontGL;
+
+// Radio controls are checkbox controls with use_radio_style true
+class LLRadioCtrl : public LLCheckBoxCtrl
+{
+public:
+ LLRadioCtrl(const LLString& name, const LLRect& rect, const LLString& label,
+ const LLFontGL* font = NULL,
+ void (*commit_callback)(LLUICtrl*, void*) = NULL,
+ void* callback_userdata = NULL);
+ /*virtual*/ ~LLRadioCtrl();
+
+ /*virtual*/ void setValue(const LLSD& value);
+};
+
+class LLRadioGroup
+: public LLUICtrl
+{
+public:
+ // Build a radio group. The number (0...n-1) of the currently selected
+ // element will be stored in the named control. After the control is
+ // changed the callback will be called.
+ LLRadioGroup(const LLString& name, const LLRect& rect,
+ const LLString& control_name,
+ LLUICtrlCallback callback = NULL,
+ void* userdata = NULL,
+ BOOL border = TRUE);
+
+ // Another radio group constructor, but this one doesn't rely on
+ // needing a control
+ LLRadioGroup(const LLString& name, const LLRect& rect,
+ S32 initial_index,
+ LLUICtrlCallback callback = NULL,
+ void* userdata = NULL,
+ BOOL border = TRUE);
+
+ virtual ~LLRadioGroup();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_RADIO_GROUP; }
+ virtual LLString getWidgetTag() const { return LL_RADIO_GROUP_TAG; }
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ virtual void setEnabled(BOOL enabled);
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void setIndexEnabled(S32 index, BOOL enabled);
+
+ S32 getItemCount() { return mRadioButtons.size(); }
+ // return the index value of the selected item
+ S32 getSelectedIndex() const;
+
+ // set the index value programatically
+ BOOL setSelectedIndex(S32 index, BOOL from_event = FALSE);
+
+ // Accept and retrieve strings of the radio group control names
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ // Draw the group, but also fix the highlighting based on the
+ // control.
+ void draw();
+
+ // You must use this method to add buttons to a radio group.
+ // Don't use addChild -- it won't set the callback function
+ // correctly.
+ LLRadioCtrl* addRadioButton(const LLString& name, const LLString& label, const LLRect& rect, const LLFontGL* font);
+ LLRadioCtrl* getRadioButton(const S32& index) { return mRadioButtons[index]; }
+ // Update the control as needed. Userdata must be a pointer to the
+ // button.
+ static void onClickButton(LLUICtrl* radio, void* userdata);
+
+protected:
+ // protected function shared by the two constructors.
+ void init(BOOL border);
+
+ S32 mSelectedIndex;
+ typedef std::vector<LLRadioCtrl*> button_list_t;
+ button_list_t mRadioButtons;
+
+ BOOL mHasBorder;
+};
+
+
+#endif
diff --git a/indra/llui/llresizebar.cpp b/indra/llui/llresizebar.cpp
new file mode 100644
index 0000000000..0183c58c93
--- /dev/null
+++ b/indra/llui/llresizebar.cpp
@@ -0,0 +1,257 @@
+/**
+ * @file llresizebar.cpp
+ * @brief LLResizeBar base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llresizebar.h"
+
+//#include "llviewermenu.h"
+//#include "llviewerimagelist.h"
+#include "llmath.h"
+#include "llui.h"
+#include "llmenugl.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+
+LLResizeBar::LLResizeBar( const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, Side side )
+ :
+ LLView( name, rect, TRUE ),
+ mDragStartScreenX( 0 ),
+ mDragStartScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mMinWidth( min_width ),
+ mMinHeight( min_height ),
+ mSide( side )
+{
+ // set up some generically good follow code.
+ switch( side )
+ {
+ case LEFT:
+ setFollowsLeft();
+ setFollowsTop();
+ setFollowsBottom();
+ break;
+ case TOP:
+ setFollowsTop();
+ setFollowsLeft();
+ setFollowsRight();
+ break;
+ case RIGHT:
+ setFollowsRight();
+ setFollowsTop();
+ setFollowsBottom();
+ break;
+ case BOTTOM:
+ setFollowsBottom();
+ setFollowsLeft();
+ setFollowsRight();
+ break;
+ default:
+ break;
+ }
+}
+
+
+BOOL LLResizeBar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if( mEnabled )
+ {
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ // No handler needed for focus lost since this clas has no state that depends on it.
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ //localPointToScreen(x, y, &mDragStartScreenX, &mDragStartScreenX);
+ localPointToOtherView(x, y, &mDragStartScreenX, &mDragStartScreenY, getParent()->getParent());
+ mLastMouseScreenX = mDragStartScreenX;
+ mLastMouseScreenY = mDragStartScreenY;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLResizeBar::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+ else
+ {
+ handled = TRUE;
+ }
+ return handled;
+}
+
+EWidgetType LLResizeBar::getWidgetType() const
+{
+ return WIDGET_TYPE_RESIZE_BAR;
+}
+
+LLString LLResizeBar::getWidgetTag() const
+{
+ return LL_RESIZE_BAR_TAG;
+}
+
+BOOL LLResizeBar::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // We only handle the click if the click both started and ended within us
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ //FIXME: this, of course, is fragile
+ LLView* floater_view = getParent()->getParent();
+ S32 floater_view_x;
+ S32 floater_view_y;
+ localPointToOtherView(x, y, &floater_view_x, &floater_view_y, floater_view);
+
+ S32 delta_x = floater_view_x - mDragStartScreenX;
+ S32 delta_y = floater_view_y - mDragStartScreenY;
+
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (floater_view_x == mLastMouseScreenX) ? mLastMouseDir.mX : floater_view_x - mLastMouseScreenX;
+ mouse_dir.mY = (floater_view_y == mLastMouseScreenY) ? mLastMouseDir.mY : floater_view_y - mLastMouseScreenY;
+ mLastMouseDir = mouse_dir;
+ mLastMouseScreenX = floater_view_x;
+ mLastMouseScreenY = floater_view_y;
+
+ // Make sure the mouse in still over the application. We don't want to make the parent
+ // so big that we can't see the resize handle any more.
+ LLRect valid_rect = floater_view->getRect();
+ LLView* parentView = getParent();
+ if( valid_rect.localPointInRect( floater_view_x, floater_view_y ) && parentView )
+ {
+ // Resize the parent
+ LLRect parent_rect = parentView->getRect();
+ LLRect scaled_rect = parent_rect;
+
+ S32 new_width = parent_rect.getWidth();
+ S32 new_height = parent_rect.getHeight();
+
+ switch( mSide )
+ {
+ case LEFT:
+ new_width = parent_rect.getWidth() - delta_x;
+ if( new_width < mMinWidth )
+ {
+ new_width = mMinWidth;
+ delta_x = parent_rect.getWidth() - mMinWidth;
+ }
+ scaled_rect.translate(delta_x, 0);
+ break;
+
+ case TOP:
+ new_height = parent_rect.getHeight() + delta_y;
+ if( new_height < mMinHeight )
+ {
+ new_height = mMinHeight;
+ delta_y = mMinHeight - parent_rect.getHeight();
+ }
+ break;
+
+ case RIGHT:
+ new_width = parent_rect.getWidth() + delta_x;
+ if( new_width < mMinWidth )
+ {
+ new_width = mMinWidth;
+ delta_x = mMinWidth - parent_rect.getWidth();
+ }
+ break;
+
+ case BOTTOM:
+ new_height = parent_rect.getHeight() - delta_y;
+ if( new_height < mMinHeight )
+ {
+ new_height = mMinHeight;
+ delta_y = parent_rect.getHeight() - mMinHeight;
+ }
+ scaled_rect.translate(0, delta_y);
+ break;
+ }
+
+ scaled_rect.mTop = scaled_rect.mBottom + new_height;
+ scaled_rect.mRight = scaled_rect.mLeft + new_width;
+ parentView->setRect(scaled_rect);
+
+ S32 snap_delta_x = 0;
+ S32 snap_delta_y = 0;
+
+ LLView* snap_view = NULL;
+
+ switch( mSide )
+ {
+ case LEFT:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mLeft;
+ scaled_rect.mLeft += snap_delta_x;
+ break;
+ case TOP:
+ snap_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mTop;
+ scaled_rect.mTop += snap_delta_y;
+ break;
+ case RIGHT:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mRight;
+ scaled_rect.mRight += snap_delta_x;
+ break;
+ case BOTTOM:
+ snap_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mBottom;
+ scaled_rect.mBottom += snap_delta_y;
+ break;
+ }
+
+ parentView->snappedTo(snap_view);
+
+ parentView->setRect(parent_rect);
+
+ parentView->reshape(scaled_rect.getWidth(), scaled_rect.getHeight(), FALSE);
+ parentView->translate(scaled_rect.mLeft - parentView->getRect().mLeft, scaled_rect.mBottom - parentView->getRect().mBottom);
+
+ floater_view_x = mDragStartScreenX + delta_x;
+ floater_view_y = mDragStartScreenY + delta_y;
+ mDragStartScreenX = floater_view_x + snap_delta_x;
+ mDragStartScreenY = floater_view_y + snap_delta_y;
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ handled = TRUE;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ handled = TRUE;
+ }
+
+ if( handled )
+ {
+ switch( mSide )
+ {
+ case LEFT:
+ case RIGHT:
+ getWindow()->setCursor(UI_CURSOR_SIZEWE);
+ break;
+
+ case TOP:
+ case BOTTOM:
+ getWindow()->setCursor(UI_CURSOR_SIZENS);
+ break;
+ }
+ }
+
+ return handled;
+}
+
diff --git a/indra/llui/llresizebar.h b/indra/llui/llresizebar.h
new file mode 100644
index 0000000000..c2a07fd3e3
--- /dev/null
+++ b/indra/llui/llresizebar.h
@@ -0,0 +1,47 @@
+/**
+ * @file llresizebar.h
+ * @brief LLResizeBar base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_RESIZEBAR_H
+#define LL_RESIZEBAR_H
+
+#include "llview.h"
+#include "llcoord.h"
+
+class LLResizeBar : public LLView
+{
+public:
+ enum Side { LEFT, TOP, RIGHT, BOTTOM };
+
+ LLResizeBar(const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, Side side );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+// virtual void draw(); No appearance
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
+
+protected:
+ S32 mDragStartScreenX;
+ S32 mDragStartScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ S32 mMinWidth;
+ S32 mMinHeight;
+ Side mSide;
+};
+
+const S32 RESIZE_BAR_HEIGHT = 3;
+
+#endif // LL_RESIZEBAR_H
+
+
diff --git a/indra/llui/llresizehandle.cpp b/indra/llui/llresizehandle.cpp
new file mode 100644
index 0000000000..77101fa296
--- /dev/null
+++ b/indra/llui/llresizehandle.cpp
@@ -0,0 +1,321 @@
+/**
+ * @file llresizehandle.cpp
+ * @brief LLResizeHandle base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llresizehandle.h"
+
+#include "llfocusmgr.h"
+#include "llmath.h"
+#include "llui.h"
+#include "llmenugl.h"
+#include "llcontrol.h"
+#include "llfloater.h"
+#include "llwindow.h"
+
+const S32 RESIZE_BORDER_WIDTH = 3;
+
+LLResizeHandle::LLResizeHandle( const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner )
+ :
+ LLView( name, rect, TRUE ),
+ mDragStartScreenX( 0 ),
+ mDragStartScreenY( 0 ),
+ mLastMouseScreenX( 0 ),
+ mLastMouseScreenY( 0 ),
+ mImage( NULL ),
+ mMinWidth( min_width ),
+ mMinHeight( min_height ),
+ mCorner( corner )
+{
+ setSaveToXML(false);
+
+ if( RIGHT_BOTTOM == mCorner)
+ {
+ LLUUID image_id(LLUI::sConfigGroup->getString("UIImgResizeBottomRightUUID"));
+ mImage = LLUI::sImageProvider->getUIImageByID(image_id);
+ }
+
+ switch( mCorner )
+ {
+ case LEFT_TOP: setFollows( FOLLOWS_LEFT | FOLLOWS_TOP ); break;
+ case LEFT_BOTTOM: setFollows( FOLLOWS_LEFT | FOLLOWS_BOTTOM ); break;
+ case RIGHT_TOP: setFollows( FOLLOWS_RIGHT | FOLLOWS_TOP ); break;
+ case RIGHT_BOTTOM: setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM ); break;
+ }
+}
+
+EWidgetType LLResizeHandle::getWidgetType() const
+{
+ return WIDGET_TYPE_RESIZE_HANDLE;
+}
+
+LLString LLResizeHandle::getWidgetTag() const
+{
+ return LL_RESIZE_HANDLE_TAG;
+}
+
+BOOL LLResizeHandle::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if( getVisible() && pointInHandle(x, y) )
+ {
+ handled = TRUE;
+ if( mEnabled )
+ {
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ // No handler needed for focus lost since this clas has no state that depends on it.
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ localPointToScreen(x, y, &mDragStartScreenX, &mDragStartScreenY);
+ mLastMouseScreenX = mDragStartScreenX;
+ mLastMouseScreenY = mDragStartScreenY;
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLResizeHandle::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+ else
+ if( getVisible() && pointInHandle(x, y) )
+ {
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+
+BOOL LLResizeHandle::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // We only handle the click if the click both started and ended within us
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ // Make sure the mouse in still over the application. We don't want to make the parent
+ // so big that we can't see the resize handle any more.
+
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ const LLRect& valid_rect = gFloaterView->getRect(); // Assumes that the parent is a floater.
+ screen_x = llclamp( screen_x, valid_rect.mLeft, valid_rect.mRight );
+ screen_y = llclamp( screen_y, valid_rect.mBottom, valid_rect.mTop );
+
+ LLView* parentView = getParent();
+ if( parentView )
+ {
+ // Resize the parent
+ LLRect parent_rect = parentView->getRect();
+ LLRect scaled_rect = parent_rect;
+ S32 delta_x = screen_x - mDragStartScreenX;
+ S32 delta_y = screen_y - mDragStartScreenY;
+ LLCoordGL mouse_dir;
+ // use hysteresis on mouse motion to preserve user intent when mouse stops moving
+ mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX;
+ mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY;
+ mLastMouseScreenX = screen_x;
+ mLastMouseScreenY = screen_y;
+ mLastMouseDir = mouse_dir;
+
+ S32 x_multiple = 1;
+ S32 y_multiple = 1;
+ switch( mCorner )
+ {
+ case LEFT_TOP:
+ x_multiple = -1;
+ y_multiple = 1;
+ break;
+ case LEFT_BOTTOM:
+ x_multiple = -1;
+ y_multiple = -1;
+ break;
+ case RIGHT_TOP:
+ x_multiple = 1;
+ y_multiple = 1;
+ break;
+ case RIGHT_BOTTOM:
+ x_multiple = 1;
+ y_multiple = -1;
+ break;
+ }
+
+ S32 new_width = parent_rect.getWidth() + x_multiple * delta_x;
+ if( new_width < mMinWidth )
+ {
+ new_width = mMinWidth;
+ delta_x = x_multiple * (mMinWidth - parent_rect.getWidth());
+ }
+
+ S32 new_height = parent_rect.getHeight() + y_multiple * delta_y;
+ if( new_height < mMinHeight )
+ {
+ new_height = mMinHeight;
+ delta_y = y_multiple * (mMinHeight - parent_rect.getHeight());
+ }
+
+ switch( mCorner )
+ {
+ case LEFT_TOP:
+ scaled_rect.translate(delta_x, 0);
+ break;
+ case LEFT_BOTTOM:
+ scaled_rect.translate(delta_x, delta_y);
+ break;
+ case RIGHT_TOP:
+ break;
+ case RIGHT_BOTTOM:
+ scaled_rect.translate(0, delta_y);
+ break;
+ }
+
+ // temporarily set new parent rect
+ scaled_rect.mRight = scaled_rect.mLeft + new_width;
+ scaled_rect.mTop = scaled_rect.mBottom + new_height;
+ parentView->setRect(scaled_rect);
+
+ S32 snap_delta_x = 0;
+ S32 snap_delta_y = 0;
+
+ LLView* snap_view = NULL;
+ LLView* test_view = NULL;
+
+ // now do snapping
+ switch(mCorner)
+ {
+ case LEFT_TOP:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mLeft;
+ test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mTop;
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ scaled_rect.mLeft += snap_delta_x;
+ scaled_rect.mTop += snap_delta_y;
+ break;
+ case LEFT_BOTTOM:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_LEFT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mLeft;
+ test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mBottom;
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ scaled_rect.mLeft += snap_delta_x;
+ scaled_rect.mBottom += snap_delta_y;
+ break;
+ case RIGHT_TOP:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mRight;
+ test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_TOP, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mTop;
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ scaled_rect.mRight += snap_delta_x;
+ scaled_rect.mTop += snap_delta_y;
+ break;
+ case RIGHT_BOTTOM:
+ snap_view = parentView->findSnapEdge(snap_delta_x, mouse_dir, SNAP_RIGHT, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_x -= scaled_rect.mRight;
+ test_view = parentView->findSnapEdge(snap_delta_y, mouse_dir, SNAP_BOTTOM, SNAP_PARENT_AND_SIBLINGS, LLUI::sConfigGroup->getS32("SnapMargin"));
+ snap_delta_y -= scaled_rect.mBottom;
+ if (!snap_view)
+ {
+ snap_view = test_view;
+ }
+ scaled_rect.mRight += snap_delta_x;
+ scaled_rect.mBottom += snap_delta_y;
+ break;
+ }
+
+ parentView->snappedTo(snap_view);
+
+ // reset parent rect
+ parentView->setRect(parent_rect);
+
+ // translate and scale to new shape
+ parentView->reshape(scaled_rect.getWidth(), scaled_rect.getHeight(), FALSE);
+ parentView->translate(scaled_rect.mLeft - parentView->getRect().mLeft, scaled_rect.mBottom - parentView->getRect().mBottom);
+
+ screen_x = mDragStartScreenX + delta_x + snap_delta_x;
+ screen_y = mDragStartScreenY + delta_y + snap_delta_y;
+ mDragStartScreenX = screen_x;
+ mDragStartScreenY = screen_y;
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active) " << llendl;
+ handled = TRUE;
+ }
+ else
+ if( getVisible() && pointInHandle( x, y ) )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive) " << llendl;
+ handled = TRUE;
+ }
+
+ if( handled )
+ {
+ switch( mCorner )
+ {
+ case RIGHT_BOTTOM:
+ case LEFT_TOP:
+ getWindow()->setCursor(UI_CURSOR_SIZENWSE);
+ break;
+ case LEFT_BOTTOM:
+ case RIGHT_TOP:
+ getWindow()->setCursor(UI_CURSOR_SIZENESW);
+ break;
+ }
+ }
+
+ return handled;
+}
+
+// assumes GL state is set for 2D
+void LLResizeHandle::draw()
+{
+ if( mImage.notNull() && getVisible() && (RIGHT_BOTTOM == mCorner) )
+ {
+ gl_draw_image( 0, 0, mImage );
+ }
+}
+
+
+BOOL LLResizeHandle::pointInHandle( S32 x, S32 y )
+{
+ if( pointInView(x, y) )
+ {
+ const S32 TOP_BORDER = (mRect.getHeight() - RESIZE_BORDER_WIDTH);
+ const S32 RIGHT_BORDER = (mRect.getWidth() - RESIZE_BORDER_WIDTH);
+
+ switch( mCorner )
+ {
+ case LEFT_TOP: return (x <= RESIZE_BORDER_WIDTH) || (y >= TOP_BORDER);
+ case LEFT_BOTTOM: return (x <= RESIZE_BORDER_WIDTH) || (y <= RESIZE_BORDER_WIDTH);
+ case RIGHT_TOP: return (x >= RIGHT_BORDER) || (y >= TOP_BORDER);
+ case RIGHT_BOTTOM: return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h
new file mode 100644
index 0000000000..1350d1af20
--- /dev/null
+++ b/indra/llui/llresizehandle.h
@@ -0,0 +1,56 @@
+/**
+ * @file llresizehandle.h
+ * @brief LLResizeHandle base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_RESIZEHANDLE_H
+#define LL_RESIZEHANDLE_H
+
+#include "stdtypes.h"
+#include "llview.h"
+#include "llimagegl.h"
+#include "llcoord.h"
+
+
+class LLResizeHandle : public LLView
+{
+public:
+ enum ECorner { LEFT_TOP, LEFT_BOTTOM, RIGHT_TOP, RIGHT_BOTTOM };
+
+
+ LLResizeHandle(const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner = RIGHT_BOTTOM );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void draw();
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ void setResizeLimits( S32 min_width, S32 min_height ) { mMinWidth = min_width; mMinHeight = min_height; }
+
+protected:
+ BOOL pointInHandle( S32 x, S32 y );
+
+protected:
+ S32 mDragStartScreenX;
+ S32 mDragStartScreenY;
+ S32 mLastMouseScreenX;
+ S32 mLastMouseScreenY;
+ LLCoordGL mLastMouseDir;
+ LLPointer<LLImageGL> mImage;
+ S32 mMinWidth;
+ S32 mMinHeight;
+ ECorner mCorner;
+};
+
+const S32 RESIZE_HANDLE_HEIGHT = 16;
+const S32 RESIZE_HANDLE_WIDTH = 16;
+
+#endif // LL_RESIZEHANDLE_H
+
+
diff --git a/indra/llui/llresmgr.cpp b/indra/llui/llresmgr.cpp
new file mode 100644
index 0000000000..67137d8bbb
--- /dev/null
+++ b/indra/llui/llresmgr.cpp
@@ -0,0 +1,447 @@
+/**
+ * @file llresmgr.cpp
+ * @brief Localized resource manager
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will
+// (when the time is right) become dynamic and probably use external files.
+
+#include "linden_common.h"
+
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llerror.h"
+#include "llstring.h"
+
+// Global singleton
+LLResMgr* gResMgr = NULL;
+
+LLResMgr::LLResMgr()
+{
+ U32 i;
+
+ // Init values for each locale.
+ // Note: This is only the most bare-bones version. In the future, load these dynamically, on demand.
+
+ //////////////////////////////////////////////////////////////////////////////
+ // USA
+ // USA Fonts
+ for( i=0; i<LLFONT_COUNT; i++ )
+ {
+ mUSAFonts[i] = NULL;
+ }
+ mUSAFonts[ LLFONT_OCRA ] = LLFontGL::sMonospace;
+ mUSAFonts[ LLFONT_SANSSERIF ] = LLFontGL::sSansSerif;
+ mUSAFonts[ LLFONT_SANSSERIF_SMALL ] = LLFontGL::sSansSerifSmall;
+ mUSAFonts[ LLFONT_SANSSERIF_BIG ] = LLFontGL::sSansSerifBig;
+ mUSAFonts[ LLFONT_SMALL ] = LLFontGL::sMonospace;
+/*
+ // USA Strings
+ for( i=0; i<LLSTR_COUNT; i++ )
+ {
+ mUSAStrings[i] = "";
+ }
+ mUSAStrings[ LLSTR_HELLO ] = "hello";
+ mUSAStrings[ LLSTR_GOODBYE ] = "goodbye";
+ mUSAStrings[ LLSTR_CHAT_LABEL ] = "Chat";
+ mUSAStrings[ LLSTR_STATUS_LABEL ] = "Properties";
+ mUSAStrings[ LLSTR_X ] = "X";
+ mUSAStrings[ LLSTR_Y ] = "Y";
+ mUSAStrings[ LLSTR_Z ] = "Z";
+ mUSAStrings[ LLSTR_POSITION ] = "Position (meters)";
+ mUSAStrings[ LLSTR_SCALE ] = "Size (meters)";
+ mUSAStrings[ LLSTR_ROTATION ] = "Rotation (degrees)";
+ mUSAStrings[ LLSTR_HAS_PHYSICS ] = "Has Physics";
+ mUSAStrings[ LLSTR_SCRIPT ] = "Script";
+ mUSAStrings[ LLSTR_HELP ] = "Help";
+ mUSAStrings[ LLSTR_REMOVE ] = "Remove";
+ mUSAStrings[ LLSTR_CLEAR ] = "Clear";
+ mUSAStrings[ LLSTR_APPLY ] = "Apply";
+ mUSAStrings[ LLSTR_CANCEL ] = "Cancel";
+ mUSAStrings[ LLSTR_MATERIAL ] = "Material";
+ mUSAStrings[ LLSTR_FACE ] = "Face";
+ mUSAStrings[ LLSTR_TEXTURE ] = "Texture";
+ mUSAStrings[ LLSTR_TEXTURE_SIZE ] = "Repeats per Face";
+ mUSAStrings[ LLSTR_TEXTURE_OFFSET ] = "Offset";
+ mUSAStrings[ LLSTR_TEXTURE_ROTATION ] = "Rotation (degrees)";
+ mUSAStrings[ LLSTR_U ] = "U";
+ mUSAStrings[ LLSTR_V ] = "V";
+ mUSAStrings[ LLSTR_OWNERSHIP ] = "Ownership";
+ mUSAStrings[ LLSTR_PUBLIC ] = "Public";
+ mUSAStrings[ LLSTR_PRIVATE ] = "Private";
+ mUSAStrings[ LLSTR_REVERT ] = "Revert";
+ mUSAStrings[ LLSTR_INSERT_SAMPLE ] = "Insert Sample";
+ mUSAStrings[ LLSTR_SET_TEXTURE ] = "Set Texture";
+ mUSAStrings[ LLSTR_EDIT_SCRIPT ] = "Edit Script...";
+ mUSAStrings[ LLSTR_MOUSELOOK_INSTRUCTIONS ] = "Press ESC to leave Mouselook.";
+ mUSAStrings[ LLSTR_EDIT_FACE_INSTRUCTIONS ] = "Click on face to select part. Click and hold on a picture to look more like that. Press ESC to leave Face Edit Mode.";
+ mUSAStrings[ LLSTR_CLOSE ] = "Close";
+ mUSAStrings[ LLSTR_MOVE ] = "Move";
+ mUSAStrings[ LLSTR_ROTATE ] = "Rotate";
+ mUSAStrings[ LLSTR_RESIZE ] = "Resize";
+ mUSAStrings[ LLSTR_PLACE_BOX ] = "Place Box";
+ mUSAStrings[ LLSTR_PLACE_PRISM ] = "Place Prism";
+ mUSAStrings[ LLSTR_PLACE_PYRAMID ] = "Place Pyramid";
+ mUSAStrings[ LLSTR_PLACE_TETRAHEDRON ] = "Place Tetrahedron";
+ mUSAStrings[ LLSTR_PLACE_CYLINDER ] = "Place Cylinder";
+ mUSAStrings[ LLSTR_PLACE_HALF_CYLINDER ] = "Place Half-Cylinder";
+ mUSAStrings[ LLSTR_PLACE_CONE ] = "Place Cone";
+ mUSAStrings[ LLSTR_PLACE_HALF_CONE ] = "Place Half-Cone";
+ mUSAStrings[ LLSTR_PLACE_SPHERE ] = "Place Sphere";
+ mUSAStrings[ LLSTR_PLACE_HALF_SPHERE ] = "Place Half-Sphere";
+ mUSAStrings[ LLSTR_PLACE_BIRD ] = "Place Bird";
+ mUSAStrings[ LLSTR_PLACE_SNAKE ] = "Place Silly Snake";
+ mUSAStrings[ LLSTR_PLACE_ROCK ] = "Place Rock";
+ mUSAStrings[ LLSTR_PLACE_TREE ] = "Place Tree";
+ mUSAStrings[ LLSTR_PLACE_GRASS ] = "Place Grass";
+ mUSAStrings[ LLSTR_MODIFY_LAND ] = "Modify Land";
+*/
+ //////////////////////////////////////////////////////////////////////////////
+ // UK
+ // The Brits are a lot like us Americans, so initially assume we're the same and only code the exceptions.
+
+ // UK Fonts
+ for( i=0; i<LLFONT_COUNT; i++ )
+ {
+ mUKFonts[i] = mUSAFonts[i];
+ }
+/*
+ // UK Strings
+ for( i=0; i<LLSTR_COUNT; i++ )
+ {
+ mUKStrings[i] = mUSAStrings[i];
+ }
+ mUKStrings[ LLSTR_HELLO ] = "hullo";
+ mUKStrings[ LLSTR_GOODBYE ] = "cheerio";
+*/
+ //////////////////////////////////////////////////////////////////////////////
+ // Set default
+ setLocale( LLLOCALE_USA );
+
+}
+
+
+void LLResMgr::setLocale( LLLOCALE_ID locale_id )
+{
+ mLocale = locale_id;
+
+ //RN: for now, use normal 'C' locale for everything but specific UI input/output routines
+ switch( locale_id )
+ {
+ case LLLOCALE_USA:
+//#if LL_WINDOWS
+// // Windows doesn't use ISO country codes.
+// llinfos << "Setting locale to " << setlocale( LC_ALL, "english-usa" ) << llendl;
+//#else
+// // posix version should work everywhere else.
+// llinfos << "Setting locale to " << setlocale( LC_ALL, "en_US" ) << llendl;
+//#endif
+
+// mStrings = mUSAStrings;
+ mFonts = mUSAFonts;
+ break;
+ case LLLOCALE_UK:
+//#if LL_WINDOWS
+// // Windows doesn't use ISO country codes.
+// llinfos << "Setting locale to " << setlocale( LC_ALL, "english-uk" ) << llendl;
+//#else
+// // posix version should work everywhere else.
+// llinfos << "Setting locale to " << setlocale( LC_ALL, "en_GB" ) << llendl;
+//#endif
+
+// mStrings = mUKStrings;
+ mFonts = mUKFonts;
+ break;
+ default:
+ llassert(0);
+ setLocale(LLLOCALE_USA);
+ break;
+ }
+}
+
+char LLResMgr::getDecimalPoint() const
+{
+ char decimal = localeconv()->decimal_point[0];
+
+#if LL_DARWIN
+ // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped.
+ if(decimal == 0)
+ {
+ decimal = '.';
+ }
+#endif
+
+ return decimal;
+}
+
+char LLResMgr::getThousandsSeparator() const
+{
+ char separator = localeconv()->thousands_sep[0];
+
+#if LL_DARWIN
+ // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped.
+ if(separator == 0)
+ {
+ separator = ',';
+ }
+#endif
+
+ return separator;
+}
+
+char LLResMgr::getMonetaryDecimalPoint() const
+{
+ char decimal = localeconv()->mon_decimal_point[0];
+
+#if LL_DARWIN
+ // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped.
+ if(decimal == 0)
+ {
+ decimal = '.';
+ }
+#endif
+
+ return decimal;
+}
+
+char LLResMgr::getMonetaryThousandsSeparator() const
+{
+ char separator = localeconv()->mon_thousands_sep[0];
+
+#if LL_DARWIN
+ // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped.
+ if(separator == 0)
+ {
+ separator = ',';
+ }
+#endif
+
+ return separator;
+}
+
+
+// Sets output to a string of integers with monetary separators inserted according to the locale.
+void LLResMgr::getMonetaryString( LLString& output, S32 input ) const
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+ struct lconv *conv = localeconv();
+
+#if LL_DARWIN
+ // On the Mac, locale support is broken before 10.4, which causes things to go all pear-shaped.
+ // Fake up a conv structure with some reasonable values for the fields this function uses.
+ struct lconv fakeconv;
+ if(conv->negative_sign[0] == 0) // Real locales all seem to have something here...
+ {
+ fakeconv = *conv; // start with what's there.
+ switch(mLocale)
+ {
+ default: // Unknown -- use the US defaults.
+ case LLLOCALE_USA:
+ case LLLOCALE_UK: // UK ends up being the same as US for the items used here.
+ fakeconv.negative_sign = "-";
+ fakeconv.mon_grouping = "\x03\x03\x00"; // commas every 3 digits
+ fakeconv.n_sign_posn = 1; // negative sign before the string
+ break;
+ }
+ conv = &fakeconv;
+ }
+#endif
+
+ char* negative_sign = conv->negative_sign;
+ char separator = getMonetaryThousandsSeparator();
+ char* grouping = conv->mon_grouping;
+
+ // Note on mon_grouping:
+ // Specifies a string that defines the size of each group of digits in formatted monetary quantities.
+ // The operand for the mon_grouping keyword consists of a sequence of semicolon-separated integers.
+ // Each integer specifies the number of digits in a group. The initial integer defines the size of
+ // the group immediately to the left of the decimal delimiter. The following integers define succeeding
+ // groups to the left of the previous group. If the last integer is not -1, the size of the previous
+ // group (if any) is repeatedly used for the remainder of the digits. If the last integer is -1, no
+ // further grouping is performed.
+
+
+ // Note: we assume here that the currency symbol goes on the left. (Hey, it's Lindens! We can just decide.)
+ BOOL negative = (input < 0 );
+ BOOL negative_before = negative && (conv->n_sign_posn != 2);
+ BOOL negative_after = negative && (conv->n_sign_posn == 2);
+
+ LLString digits = llformat("%u", abs(input));
+ if( !grouping || !grouping[0] )
+ {
+ output.assign("L$");
+ if( negative_before )
+ {
+ output.append( negative_sign );
+ }
+ output.append( digits );
+ if( negative_after )
+ {
+ output.append( negative_sign );
+ }
+ return;
+ }
+
+ S32 groupings[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ S32 cur_group;
+ for( cur_group = 0; grouping[ cur_group ]; cur_group++ )
+ {
+ if( grouping[ cur_group ] != ';' )
+ {
+ groupings[cur_group] = grouping[ cur_group ];
+ }
+ cur_group++;
+
+ if( groupings[cur_group] < 0 )
+ {
+ break;
+ }
+ }
+ S32 group_count = cur_group;
+
+ char reversed_output[20] = "";
+ char forward_output[20] = "";
+ S32 output_pos = 0;
+
+ cur_group = 0;
+ S32 pos = digits.size()-1;
+ S32 count_within_group = 0;
+ while( (pos >= 0) && (groupings[cur_group] >= 0) )
+ {
+ count_within_group++;
+ if( count_within_group > groupings[cur_group] )
+ {
+ count_within_group = 1;
+ reversed_output[ output_pos++ ] = separator;
+
+ if( (cur_group + 1) >= group_count )
+ {
+ break;
+ }
+ else
+ if( groupings[cur_group + 1] > 0 )
+ {
+ cur_group++;
+ }
+ }
+ reversed_output[ output_pos++ ] = digits[pos--];
+ }
+
+ while( pos >= 0 )
+ {
+ reversed_output[ output_pos++ ] = digits[pos--];
+ }
+
+
+ reversed_output[ output_pos ] = '\0';
+ forward_output[ output_pos ] = '\0';
+
+ for( S32 i = 0; i < output_pos; i++ )
+ {
+ forward_output[ output_pos - 1 - i ] = reversed_output[ i ];
+ }
+
+ output.assign("L$");
+ if( negative_before )
+ {
+ output.append( negative_sign );
+ }
+ output.append( forward_output );
+ if( negative_after )
+ {
+ output.append( negative_sign );
+ }
+}
+
+void LLResMgr::getIntegerString( LLString& output, S32 input ) const
+{
+ S32 fraction = 0;
+ LLString fraction_string;
+ S32 remaining_count = input;
+ while(remaining_count > 0)
+ {
+ fraction = (remaining_count) % 1000;
+
+ if (!output.empty())
+ {
+ if (fraction == remaining_count)
+ {
+ fraction_string = llformat("%d%c", fraction, getThousandsSeparator());
+ }
+ else
+ {
+ fraction_string = llformat("%3.3d%c", fraction, getThousandsSeparator());
+ }
+ output = fraction_string + output;
+ }
+ else
+ {
+ if (fraction == remaining_count)
+ {
+ fraction_string = llformat("%d", fraction);
+ }
+ else
+ {
+ fraction_string = llformat("%3.3d", fraction);
+ }
+ output = fraction_string;
+ }
+ remaining_count /= 1000;
+ }
+}
+
+const LLString LLFONT_ID_NAMES[] =
+{
+ LLString("OCRA"),
+ LLString("SANSSERIF"),
+ LLString("SANSSERIF_SMALL"),
+ LLString("SANSSERIF_BIG"),
+ LLString("SMALL"),
+};
+
+const LLFontGL* LLResMgr::getRes( LLString font_id ) const
+{
+ for (S32 i=0; i<LLFONT_COUNT; ++i)
+ {
+ if (LLFONT_ID_NAMES[i] == font_id)
+ {
+ return getRes((LLFONT_ID)i);
+ }
+ }
+ return NULL;
+}
+
+#if LL_WINDOWS
+const LLString LLLocale::USER_LOCALE("English_United States.1252");// = LLString::null;
+const LLString LLLocale::SYSTEM_LOCALE("English_United States.1252");
+#elif LL_DARWIN
+const LLString LLLocale::USER_LOCALE("en_US.iso8859-1");// = LLString::null;
+const LLString LLLocale::SYSTEM_LOCALE("en_US.iso8859-1");
+#else // LL_LINUX likes this
+const LLString LLLocale::USER_LOCALE("en_US.utf8");// = LLString::null;
+const LLString LLLocale::SYSTEM_LOCALE("en_US.utf8");
+#endif
+
+
+LLLocale::LLLocale(const LLString& locale_string)
+{
+ mPrevLocaleString = setlocale( LC_ALL, NULL );
+ char* new_locale_string = setlocale( LC_ALL, locale_string.c_str());
+ if ( new_locale_string == NULL)
+ {
+ llwarns << "Failed to set locale " << locale_string << llendl;
+ setlocale(LC_ALL, SYSTEM_LOCALE.c_str());
+ }
+ //else
+ //{
+ // llinfos << "Set locale to " << new_locale_string << llendl;
+ //}
+}
+
+LLLocale::~LLLocale()
+{
+ setlocale( LC_ALL, mPrevLocaleString.c_str() );
+}
diff --git a/indra/llui/llresmgr.h b/indra/llui/llresmgr.h
new file mode 100644
index 0000000000..b79fa2021f
--- /dev/null
+++ b/indra/llui/llresmgr.h
@@ -0,0 +1,147 @@
+/**
+ * @file llresmgr.h
+ * @brief Localized resource manager
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// NOTE: this is a MINIMAL implementation. The interface will remain, but the implementation will
+// (when the time is right) become dynamic and probably use external files.
+
+#ifndef LL_LLRESMGR_H
+#define LL_LLRESMGR_H
+
+#include "locale.h"
+#include "stdtypes.h"
+#include "llstring.h"
+
+enum LLLOCALE_ID
+{
+ LLLOCALE_USA,
+ LLLOCALE_UK,
+ LLLOCALE_COUNT // Number of values in this enum. Keep at end.
+};
+
+/*
+enum LLSTR_ID
+{
+ LLSTR_HELLO,
+ LLSTR_GOODBYE,
+ LLSTR_CHAT_LABEL,
+ LLSTR_STATUS_LABEL,
+ LLSTR_X,
+ LLSTR_Y,
+ LLSTR_Z,
+ LLSTR_POSITION,
+ LLSTR_SCALE,
+ LLSTR_ROTATION,
+ LLSTR_HAS_PHYSICS,
+ LLSTR_SCRIPT,
+ LLSTR_HELP,
+ LLSTR_REMOVE,
+ LLSTR_CLEAR,
+ LLSTR_APPLY,
+ LLSTR_CANCEL,
+ LLSTR_MATERIAL,
+ LLSTR_FACE,
+ LLSTR_TEXTURE,
+ LLSTR_TEXTURE_SIZE,
+ LLSTR_TEXTURE_OFFSET,
+ LLSTR_TEXTURE_ROTATION,
+ LLSTR_U,
+ LLSTR_V,
+ LLSTR_OWNERSHIP,
+ LLSTR_PUBLIC,
+ LLSTR_PRIVATE,
+ LLSTR_REVERT,
+ LLSTR_INSERT_SAMPLE,
+ LLSTR_SET_TEXTURE,
+ LLSTR_EDIT_SCRIPT,
+ LLSTR_MOUSELOOK_INSTRUCTIONS,
+ LLSTR_EDIT_FACE_INSTRUCTIONS,
+ LLSTR_CLOSE,
+ LLSTR_MOVE,
+ LLSTR_ROTATE,
+ LLSTR_RESIZE,
+ LLSTR_PLACE_BOX,
+ LLSTR_PLACE_PRISM,
+ LLSTR_PLACE_PYRAMID,
+ LLSTR_PLACE_TETRAHEDRON,
+ LLSTR_PLACE_CYLINDER,
+ LLSTR_PLACE_HALF_CYLINDER,
+ LLSTR_PLACE_CONE,
+ LLSTR_PLACE_HALF_CONE,
+ LLSTR_PLACE_SPHERE,
+ LLSTR_PLACE_HALF_SPHERE,
+ LLSTR_PLACE_BIRD,
+ LLSTR_PLACE_SNAKE,
+ LLSTR_PLACE_ROCK,
+ LLSTR_PLACE_TREE,
+ LLSTR_PLACE_GRASS,
+ LLSTR_MODIFY_LAND,
+ LLSTR_COUNT // Number of values in this enum. Keep at end.
+};
+*/
+
+enum LLFONT_ID
+{
+ LLFONT_OCRA,
+ LLFONT_SANSSERIF,
+ LLFONT_SANSSERIF_SMALL,
+ LLFONT_SANSSERIF_BIG,
+ LLFONT_SMALL,
+ LLFONT_COUNT // Number of values in this enum. Keep at end.
+};
+
+class LLFontGL;
+
+class LLResMgr
+{
+public:
+ LLResMgr();
+
+ void setLocale( LLLOCALE_ID locale_id );
+ LLLOCALE_ID getLocale() const { return mLocale; }
+
+ char getDecimalPoint() const;
+ char getThousandsSeparator() const;
+
+ char getMonetaryDecimalPoint() const;
+ char getMonetaryThousandsSeparator() const;
+ void getMonetaryString( LLString& output, S32 input ) const;
+ void getIntegerString( LLString& output, S32 input ) const;
+
+// const char* getRes( LLSTR_ID string_id ) const { return mStrings[ string_id ]; }
+ const LLFontGL* getRes( LLFONT_ID font_id ) const { return mFonts[ font_id ]; }
+ const LLFontGL* getRes( LLString font_id ) const;
+
+private:
+ LLLOCALE_ID mLocale;
+// const char** mStrings;
+ const LLFontGL** mFonts;
+
+// const char* mUSAStrings[LLSTR_COUNT];
+ const LLFontGL* mUSAFonts[LLFONT_COUNT];
+
+// const char* mUKStrings[LLSTR_COUNT];
+ const LLFontGL* mUKFonts[LLFONT_COUNT];
+};
+
+class LLLocale
+{
+public:
+ LLLocale(const LLString& locale_string);
+ virtual ~LLLocale();
+
+public:
+ static const LLString USER_LOCALE;
+ static const LLString SYSTEM_LOCALE;
+
+protected:
+ LLString mPrevLocaleString;
+};
+
+extern LLResMgr* gResMgr;
+
+#endif // LL_RESMGR_
diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp
new file mode 100644
index 0000000000..4a5ae1dadf
--- /dev/null
+++ b/indra/llui/llscrollbar.cpp
@@ -0,0 +1,618 @@
+/**
+ * @file llscrollbar.cpp
+ * @brief Scrollbar UI widget
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llscrollbar.h"
+
+#include "llmath.h"
+#include "lltimer.h"
+#include "v3color.h"
+
+#include "llbutton.h"
+#include "llcriticaldamp.h"
+#include "llkeyboard.h"
+#include "llui.h"
+//#include "llviewerimagelist.h"
+#include "llfocusmgr.h"
+#include "llwindow.h"
+#include "llglheaders.h"
+#include "llcontrol.h"
+
+LLScrollbar::LLScrollbar(
+ const LLString& name, LLRect rect,
+ LLScrollbar::ORIENTATION orientation,
+ S32 doc_size, S32 doc_pos, S32 page_size,
+ void (*change_callback)( S32 new_pos, LLScrollbar* self, void* userdata ),
+ void* callback_user_data,
+ S32 step_size)
+: LLUICtrl( name, rect, TRUE, NULL, NULL ),
+
+ mChangeCallback( change_callback ),
+ mCallbackUserData( callback_user_data ),
+ mOrientation( orientation ),
+ mDocSize( doc_size ),
+ mDocPos( doc_pos ),
+ mPageSize( page_size ),
+ mStepSize( step_size ),
+ mDocChanged(FALSE),
+ mDragStartX( 0 ),
+ mDragStartY( 0 ),
+ mHoverGlowStrength(0.15f),
+ mCurGlowStrength(0.f),
+ mTrackColor( LLUI::sColorsGroup->getColor("ScrollbarTrackColor") ),
+ mThumbColor ( LLUI::sColorsGroup->getColor("ScrollbarThumbColor") ),
+ mHighlightColor ( LLUI::sColorsGroup->getColor("DefaultHighlightLight") ),
+ mShadowColor ( LLUI::sColorsGroup->getColor("DefaultShadowLight") ),
+ mOnScrollEndCallback( NULL ),
+ mOnScrollEndData( NULL )
+{
+ //llassert( 0 <= mDocSize );
+ //llassert( 0 <= mDocPos && mDocPos <= mDocSize );
+
+ setTabStop(FALSE);
+ updateThumbRect();
+
+ // Page up and page down buttons
+ LLRect line_up_rect;
+ LLString line_up_img;
+ LLString line_up_selected_img;
+ LLString line_down_img;
+ LLString line_down_selected_img;
+
+ LLRect line_down_rect;
+
+ if( LLScrollbar::VERTICAL == mOrientation )
+ {
+ line_up_rect.setLeftTopAndSize( 0, mRect.getHeight(), SCROLLBAR_SIZE, SCROLLBAR_SIZE );
+ line_up_img="UIImgBtnScrollUpOutUUID";
+ line_up_selected_img="UIImgBtnScrollUpInUUID";
+
+ line_down_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
+ line_down_img="UIImgBtnScrollDownOutUUID";
+ line_down_selected_img="UIImgBtnScrollDownInUUID";
+ }
+ else
+ {
+ // Horizontal
+ line_up_rect.setOriginAndSize( 0, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
+ line_up_img="UIImgBtnScrollLeftOutUUID";
+ line_up_selected_img="UIImgBtnScrollLeftInUUID";
+
+ line_down_rect.setOriginAndSize( mRect.getWidth() - SCROLLBAR_SIZE, 0, SCROLLBAR_SIZE, SCROLLBAR_SIZE );
+ line_down_img="UIImgBtnScrollRightOutUUID";
+ line_down_selected_img="UIImgBtnScrollRightInUUID";
+ }
+
+ LLButton* line_up_btn = new LLButton(
+ "Line Up", line_up_rect,
+ line_up_img, line_up_selected_img, "",
+ &LLScrollbar::onLineUpBtnPressed, this, LLFontGL::sSansSerif );
+ if( LLScrollbar::VERTICAL == mOrientation )
+ {
+ line_up_btn->setFollowsRight();
+ line_up_btn->setFollowsTop();
+ }
+ else
+ {
+ // horizontal
+ line_up_btn->setFollowsLeft();
+ line_up_btn->setFollowsBottom();
+ }
+ line_up_btn->setHeldDownCallback( &LLScrollbar::onLineUpBtnPressed );
+ line_up_btn->setTabStop(FALSE);
+ addChild(line_up_btn);
+
+ LLButton* line_down_btn = new LLButton(
+ "Line Down", line_down_rect,
+ line_down_img, line_down_selected_img, "",
+ &LLScrollbar::onLineDownBtnPressed, this, LLFontGL::sSansSerif );
+ line_down_btn->setFollowsRight();
+ line_down_btn->setFollowsBottom();
+ line_down_btn->setHeldDownCallback( &LLScrollbar::onLineDownBtnPressed );
+ line_down_btn->setTabStop(FALSE);
+ addChild(line_down_btn);
+}
+
+
+LLScrollbar::~LLScrollbar()
+{
+ // Children buttons killed by parent class
+}
+
+void LLScrollbar::setDocParams( S32 size, S32 pos )
+{
+ mDocSize = size;
+ mDocPos = llclamp( pos, 0, getDocPosMax() );
+ mDocChanged = TRUE;
+
+ updateThumbRect();
+}
+
+void LLScrollbar::setDocPos(S32 pos)
+{
+ mDocPos = llclamp( pos, 0, getDocPosMax() );
+ mDocChanged = TRUE;
+
+ updateThumbRect();
+}
+
+void LLScrollbar::setDocSize(S32 size)
+{
+ mDocSize = size;
+ mDocPos = llclamp( mDocPos, 0, getDocPosMax() );
+ mDocChanged = TRUE;
+
+ updateThumbRect();
+}
+
+void LLScrollbar::setPageSize( S32 page_size )
+{
+ mPageSize = page_size;
+ mDocPos = llclamp( mDocPos, 0, getDocPosMax() );
+ mDocChanged = TRUE;
+
+ updateThumbRect();
+}
+
+void LLScrollbar::updateThumbRect()
+{
+// llassert( 0 <= mDocSize );
+// llassert( 0 <= mDocPos && mDocPos <= getDocPosMax() );
+
+ const S32 THUMB_MIN_LENGTH = 16;
+
+ S32 window_length = (mOrientation == LLScrollbar::HORIZONTAL) ? mRect.getWidth() : mRect.getHeight();
+ S32 thumb_bg_length = window_length - 2 * SCROLLBAR_SIZE;
+ S32 visible_lines = llmin( mDocSize, mPageSize );
+ S32 thumb_length = mDocSize ? llmax( visible_lines * thumb_bg_length / mDocSize, THUMB_MIN_LENGTH ) : thumb_bg_length;
+
+ S32 variable_lines = mDocSize - visible_lines;
+
+ if( mOrientation == LLScrollbar::VERTICAL )
+ {
+ S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE;
+ S32 thumb_start_min = SCROLLBAR_SIZE + THUMB_MIN_LENGTH;
+ S32 thumb_start = variable_lines ? llclamp( thumb_start_max - (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min, thumb_start_max ) : thumb_start_max;
+
+ mThumbRect.mLeft = 0;
+ mThumbRect.mTop = thumb_start;
+ mThumbRect.mRight = SCROLLBAR_SIZE;
+ mThumbRect.mBottom = thumb_start - thumb_length;
+ }
+ else
+ {
+ // Horizontal
+ S32 thumb_start_max = thumb_bg_length + SCROLLBAR_SIZE - thumb_length;
+ S32 thumb_start_min = SCROLLBAR_SIZE;
+ S32 thumb_start = variable_lines ? llclamp( thumb_start_min + (mDocPos * (thumb_bg_length - thumb_length)) / variable_lines, thumb_start_min, thumb_start_max ) : thumb_start_min;
+
+ mThumbRect.mLeft = thumb_start;
+ mThumbRect.mTop = SCROLLBAR_SIZE;
+ mThumbRect.mRight = thumb_start + thumb_length;
+ mThumbRect.mBottom = 0;
+ }
+
+ if (mOnScrollEndCallback && mOnScrollEndData && (mDocPos == getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+}
+
+BOOL LLScrollbar::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Check children first
+ BOOL handled_by_child = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+ if( !handled_by_child )
+ {
+ if( mThumbRect.pointInRect(x,y) )
+ {
+ // Start dragging the thumb
+ // No handler needed for focus lost since this clas has no state that depends on it.
+ gFocusMgr.setMouseCapture( this, NULL );
+ mDragStartX = x;
+ mDragStartY = y;
+ mOrigRect.mTop = mThumbRect.mTop;
+ mOrigRect.mBottom = mThumbRect.mBottom;
+ mOrigRect.mLeft = mThumbRect.mLeft;
+ mOrigRect.mRight = mThumbRect.mRight;
+ mLastDelta = 0;
+ }
+ else
+ {
+ if(
+ ( (LLScrollbar::VERTICAL == mOrientation) && (mThumbRect.mTop < y) ) ||
+ ( (LLScrollbar::HORIZONTAL == mOrientation) && (x < mThumbRect.mLeft) )
+ )
+ {
+ // Page up
+ pageUp(0);
+ }
+ else
+ if(
+ ( (LLScrollbar::VERTICAL == mOrientation) && (y < mThumbRect.mBottom) ) ||
+ ( (LLScrollbar::HORIZONTAL == mOrientation) && (mThumbRect.mRight < x) )
+ )
+ {
+ // Page down
+ pageDown(0);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask)
+{
+ // Note: we don't bother sending the event to the children (the arrow buttons)
+ // because they'll capture the mouse whenever they need hover events.
+
+ BOOL handled = FALSE;
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ S32 height = mRect.getHeight();
+ S32 width = mRect.getWidth();
+
+ if( VERTICAL == mOrientation )
+ {
+// S32 old_pos = mThumbRect.mTop;
+
+ S32 delta_pixels = y - mDragStartY;
+ if( mOrigRect.mBottom + delta_pixels < SCROLLBAR_SIZE )
+ {
+ delta_pixels = SCROLLBAR_SIZE - mOrigRect.mBottom - 1;
+ }
+ else
+ if( mOrigRect.mTop + delta_pixels > height - SCROLLBAR_SIZE )
+ {
+ delta_pixels = height - SCROLLBAR_SIZE - mOrigRect.mTop + 1;
+ }
+
+ mThumbRect.mTop = mOrigRect.mTop + delta_pixels;
+ mThumbRect.mBottom = mOrigRect.mBottom + delta_pixels;
+
+ S32 thumb_length = mThumbRect.getHeight();
+ S32 thumb_track_length = height - 2 * SCROLLBAR_SIZE;
+
+
+ if( delta_pixels != mLastDelta || mDocChanged)
+ {
+ // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
+ S32 usable_track_length = thumb_track_length - thumb_length;
+ if( 0 < usable_track_length )
+ {
+ S32 variable_lines = getDocPosMax();
+ S32 pos = mThumbRect.mTop;
+ F32 ratio = F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length;
+
+ S32 new_pos = llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines );
+ // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
+ // out of sync (less than a line's worth) to make the thumb feel responsive.
+ changeLine( new_pos - mDocPos, FALSE );
+ }
+ }
+
+ mLastDelta = delta_pixels;
+
+ }
+ else
+ {
+ // Horizontal
+// S32 old_pos = mThumbRect.mLeft;
+
+ S32 delta_pixels = x - mDragStartX;
+
+ if( mOrigRect.mLeft + delta_pixels < SCROLLBAR_SIZE )
+ {
+ delta_pixels = SCROLLBAR_SIZE - mOrigRect.mLeft - 1;
+ }
+ else
+ if( mOrigRect.mRight + delta_pixels > width - SCROLLBAR_SIZE )
+ {
+ delta_pixels = width - SCROLLBAR_SIZE - mOrigRect.mRight + 1;
+ }
+
+ mThumbRect.mLeft = mOrigRect.mLeft + delta_pixels;
+ mThumbRect.mRight = mOrigRect.mRight + delta_pixels;
+
+ S32 thumb_length = mThumbRect.getWidth();
+ S32 thumb_track_length = width - 2 * SCROLLBAR_SIZE;
+
+ if( delta_pixels != mLastDelta || mDocChanged)
+ {
+ // Note: delta_pixels increases as you go up. mDocPos increases down (line 0 is at the top of the page).
+ S32 usable_track_length = thumb_track_length - thumb_length;
+ if( 0 < usable_track_length )
+ {
+ S32 variable_lines = getDocPosMax();
+ S32 pos = mThumbRect.mLeft;
+ F32 ratio = F32(pos - SCROLLBAR_SIZE) / usable_track_length;
+
+ S32 new_pos = llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines);
+
+ // Note: we do not call updateThumbRect() here. Instead we let the thumb and the document go slightly
+ // out of sync (less than a line's worth) to make the thumb feel responsive.
+ changeLine( new_pos - mDocPos, FALSE );
+ }
+ }
+
+ mLastDelta = delta_pixels;
+ }
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ handled = TRUE;
+ }
+ else
+ {
+ handled = childrenHandleMouseUp( x, y, mask ) != NULL;
+ }
+
+ // Opaque
+ if( !handled )
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ handled = TRUE;
+ }
+
+ mDocChanged = FALSE;
+ return handled;
+}
+
+
+BOOL LLScrollbar::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mRect.localPointInRect( x, y ) )
+ {
+ if( getEnabled() )
+ {
+ changeLine( clicks * mStepSize, TRUE );
+ }
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLScrollbar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type, void *carge_data, EAcceptance *accept, LLString &tooltip_msg)
+{
+ if (!drop)
+ {
+ //TODO: refactor this
+ S32 variable_lines = getDocPosMax();
+ S32 pos = (VERTICAL == mOrientation) ? y : x;
+ S32 thumb_length = (VERTICAL == mOrientation) ? mThumbRect.getHeight() : mThumbRect.getWidth();
+ S32 thumb_track_length = (VERTICAL == mOrientation) ? (mRect.getHeight() - 2 * SCROLLBAR_SIZE) : (mRect.getWidth() - 2 * SCROLLBAR_SIZE);
+ S32 usable_track_length = thumb_track_length - thumb_length;
+ F32 ratio = (VERTICAL == mOrientation) ? F32(pos - SCROLLBAR_SIZE - thumb_length) / usable_track_length
+ : F32(pos - SCROLLBAR_SIZE) / usable_track_length;
+ S32 new_pos = (VERTICAL == mOrientation) ? llclamp( S32(variable_lines - ratio * variable_lines + 0.5f), 0, variable_lines )
+ : llclamp( S32(ratio * variable_lines + 0.5f), 0, variable_lines );
+ changeLine( new_pos - mDocPos, TRUE );
+ }
+ return TRUE;
+}
+
+BOOL LLScrollbar::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+ else
+ {
+ // Opaque, so don't just check children
+ handled = LLView::handleMouseUp( x, y, mask );
+ }
+
+ return handled;
+}
+
+void LLScrollbar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape( width, height, called_from_parent );
+ updateThumbRect();
+}
+
+
+void LLScrollbar::draw()
+{
+ if( getVisible() )
+ {
+ S32 local_mouse_x;
+ S32 local_mouse_y;
+ LLCoordWindow cursor_pos_window;
+ getWindow()->getCursorPosition(&cursor_pos_window);
+ LLCoordGL cursor_pos_gl;
+ getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl);
+
+ screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y);
+ BOOL other_captor = gFocusMgr.getMouseCapture() && gFocusMgr.getMouseCapture() != this;
+ BOOL hovered = mEnabled && !other_captor && (gFocusMgr.getMouseCapture() == this || mThumbRect.pointInRect(local_mouse_x, local_mouse_y));
+ if (hovered)
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f));
+ }
+ else
+ {
+ mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f));
+ }
+
+
+ // Draw background and thumb.
+ LLUUID rounded_rect_image_id;
+ rounded_rect_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga"));
+ LLImageGL* rounded_rect_imagep = LLUI::sImageProvider->getUIImageByID(rounded_rect_image_id);
+
+ if (!rounded_rect_imagep)
+ {
+ gl_rect_2d(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0,
+ mOrientation == VERTICAL ? mRect.getHeight() - 2 * SCROLLBAR_SIZE : mRect.getHeight(),
+ mOrientation == HORIZONTAL ? mRect.getWidth() - 2 * SCROLLBAR_SIZE : mRect.getWidth(),
+ mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0, mTrackColor, TRUE);
+
+ gl_rect_2d(mThumbRect, mThumbColor, TRUE);
+
+ }
+ else
+ {
+ // Background
+ gl_draw_scaled_image_with_border(mOrientation == HORIZONTAL ? SCROLLBAR_SIZE : 0,
+ mOrientation == VERTICAL ? SCROLLBAR_SIZE : 0,
+ 16,
+ 16,
+ mOrientation == HORIZONTAL ? mRect.getWidth() - 2 * SCROLLBAR_SIZE : mRect.getWidth(),
+ mOrientation == VERTICAL ? mRect.getHeight() - 2 * SCROLLBAR_SIZE : mRect.getHeight(),
+ rounded_rect_imagep,
+ mTrackColor,
+ TRUE);
+
+ // Thumb
+ LLRect outline_rect = mThumbRect;
+ outline_rect.stretch(2);
+
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ gl_draw_scaled_image_with_border(outline_rect.mLeft, outline_rect.mBottom, 16, 16, outline_rect.getWidth(), outline_rect.getHeight(),
+ rounded_rect_imagep, gFocusMgr.getFocusColor() );
+ }
+
+ gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(),
+ rounded_rect_imagep, mThumbColor );
+ if (mCurGlowStrength > 0.01f)
+ {
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(),
+ rounded_rect_imagep, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength), TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+
+ BOOL was_scrolled_to_bottom = (getDocPos() == getDocPosMax());
+ if (mOnScrollEndCallback && was_scrolled_to_bottom)
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+ // Draw children
+ LLView::draw();
+ }
+}
+
+void LLScrollbar::changeLine( S32 delta, BOOL update_thumb )
+{
+ S32 new_pos = llclamp( mDocPos + delta, 0, getDocPosMax() );
+ if( new_pos != mDocPos )
+ {
+ mDocPos = new_pos;
+ }
+
+ if( mChangeCallback )
+ {
+ mChangeCallback( mDocPos, this, mCallbackUserData );
+ }
+
+ if( update_thumb )
+ {
+ updateThumbRect();
+ }
+}
+
+void LLScrollbar::setValue(const LLSD& value)
+{
+ setDocPos((S32) value.asInteger());
+}
+
+EWidgetType LLScrollbar::getWidgetType() const
+{
+ return WIDGET_TYPE_SCROLLBAR;
+}
+
+LLString LLScrollbar::getWidgetTag() const
+{
+ return LL_SCROLLBAR_TAG;
+}
+
+BOOL LLScrollbar::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ switch( key )
+ {
+ case KEY_HOME:
+ changeLine( -mDocPos, TRUE );
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ changeLine( getDocPosMax() - mDocPos, TRUE );
+ handled = TRUE;
+ break;
+
+ case KEY_DOWN:
+ changeLine( mStepSize, TRUE );
+ handled = TRUE;
+ break;
+
+ case KEY_UP:
+ changeLine( - mStepSize, TRUE );
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ pageDown(1);
+ break;
+
+ case KEY_PAGE_UP:
+ pageUp(1);
+ break;
+ }
+ }
+
+ return handled;
+}
+
+void LLScrollbar::pageUp(S32 overlap)
+{
+ if (mDocSize > mPageSize)
+ {
+ changeLine( -(mPageSize - overlap), TRUE );
+ }
+}
+
+void LLScrollbar::pageDown(S32 overlap)
+{
+ if (mDocSize > mPageSize)
+ {
+ changeLine( mPageSize - overlap, TRUE );
+ }
+}
+
+// static
+void LLScrollbar::onLineUpBtnPressed( void* userdata )
+{
+ LLScrollbar* self = (LLScrollbar*) userdata;
+
+ self->changeLine( - self->mStepSize, TRUE );
+}
+
+// static
+void LLScrollbar::onLineDownBtnPressed( void* userdata )
+{
+ LLScrollbar* self = (LLScrollbar*) userdata;
+ self->changeLine( self->mStepSize, TRUE );
+}
+
diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h
new file mode 100644
index 0000000000..f479707499
--- /dev/null
+++ b/indra/llui/llscrollbar.h
@@ -0,0 +1,123 @@
+/**
+ * @file llscrollbar.h
+ * @brief Scrollbar UI widget
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SCROLLBAR_H
+#define LL_SCROLLBAR_H
+
+#include "stdtypes.h"
+#include "lluictrl.h"
+#include "v4color.h"
+
+//
+// Constants
+//
+const S32 SCROLLBAR_SIZE = 16;
+
+
+//
+// Classes
+//
+class LLScrollbar
+: public LLUICtrl
+{
+public:
+ enum ORIENTATION { HORIZONTAL, VERTICAL };
+
+ LLScrollbar(const LLString& name, LLRect rect,
+ ORIENTATION orientation,
+ S32 doc_size, S32 doc_pos, S32 page_size,
+ void(*change_callback)( S32 new_pos, LLScrollbar* self, void* userdata ),
+ void* callback_user_data = NULL,
+ S32 step_size = 1);
+
+ virtual ~LLScrollbar();
+
+ virtual void setValue(const LLSD& value);
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // Overrides from LLView
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type, void *carge_data, EAcceptance *accept, LLString &tooltip_msg);
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual void draw();
+
+ void setDocParams( S32 size, S32 pos );
+
+ // How long the "document" is.
+ void setDocSize( S32 size );
+ S32 getDocSize() { return mDocSize; }
+
+ // How many "lines" the "document" has scrolled.
+ // 0 <= DocPos <= DocSize - DocVisibile
+ void setDocPos( S32 pos );
+ S32 getDocPos() { return mDocPos; }
+
+ // How many "lines" of the "document" is can appear on a page.
+ void setPageSize( S32 page_size );
+ S32 getPageSize() { return mPageSize; }
+
+ // The farthest the document can be scrolled (top of the last page).
+ S32 getDocPosMax() { return llmax( 0, mDocSize - mPageSize); }
+
+ void pageUp(S32 overlap);
+ void pageDown(S32 overlap);
+
+ static void onLineUpBtnPressed(void* userdata);
+ static void onLineDownBtnPressed(void* userdata);
+
+ void setTrackColor( const LLColor4& color ) { mTrackColor = color; }
+ void setThumbColor( const LLColor4& color ) { mThumbColor = color; }
+ void setHighlightColor( const LLColor4& color ) { mHighlightColor = color; }
+ void setShadowColor( const LLColor4& color ) { mShadowColor = color; }
+
+ void setOnScrollEndCallback(void (*callback)(void*), void* userdata) { mOnScrollEndCallback = callback; mOnScrollEndData = userdata;}
+protected:
+ void updateThumbRect();
+ void changeLine(S32 delta, BOOL update_thumb );
+
+protected:
+ void (*mChangeCallback)( S32 new_pos, LLScrollbar* self, void* userdata );
+ void* mCallbackUserData;
+
+ ORIENTATION mOrientation;
+ S32 mDocSize; // Size of the document that the scrollbar is modeling. Units depend on the user. 0 <= mDocSize.
+ S32 mDocPos; // Position within the doc that the scrollbar is modeling, in "lines" (user size)
+ S32 mPageSize; // Maximum number of lines that can be seen at one time.
+ S32 mStepSize;
+ BOOL mDocChanged;
+
+ LLRect mThumbRect;
+ S32 mDragStartX;
+ S32 mDragStartY;
+ F32 mHoverGlowStrength;
+ F32 mCurGlowStrength;
+
+ LLRect mOrigRect;
+ S32 mLastDelta;
+
+ LLColor4 mTrackColor;
+ LLColor4 mThumbColor;
+ LLColor4 mFocusColor;
+ LLColor4 mHighlightColor;
+ LLColor4 mShadowColor;
+
+ void (*mOnScrollEndCallback)(void*);
+ void *mOnScrollEndData;
+};
+
+
+
+#endif // LL_SCROLLBAR_H
diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp
new file mode 100644
index 0000000000..15bb8e3f24
--- /dev/null
+++ b/indra/llui/llscrollcontainer.cpp
@@ -0,0 +1,772 @@
+/**
+ * @file llscrollcontainer.cpp
+ * @brief LLScrollableContainerView base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// A view meant to encapsulate a clipped region which is
+// scrollable. It automatically takes care of pixel perfect scrolling
+// and cliipping, as well as turning the scrollbars on or off based on
+// the width and height of the view you're scrolling.
+//
+//*****************************************************************************
+
+#include "linden_common.h"
+
+#include "llgl.h"
+
+#include "llscrollcontainer.h"
+#include "llscrollbar.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "llviewborder.h"
+#include "llfocusmgr.h"
+#include "llframetimer.h"
+#include "lluictrlfactory.h"
+#include "llfontgl.h"
+
+#include "llglheaders.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+static const S32 HORIZONTAL_MULTIPLE = 8;
+static const S32 VERTICAL_MULTIPLE = 16;
+static const F32 MIN_AUTO_SCROLL_RATE = 120.f;
+static const F32 MAX_AUTO_SCROLL_RATE = 500.f;
+static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f;
+
+///----------------------------------------------------------------------------
+/// Class LLScrollableContainerView
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLScrollableContainerView::LLScrollableContainerView( const LLString& name,
+ const LLRect& rect,
+ LLView* scrolled_view,
+ BOOL is_opaque,
+ const LLColor4& bg_color ) :
+ LLUICtrl( name, rect, FALSE, NULL, NULL ),
+ mScrolledView( scrolled_view ),
+ mIsOpaque( is_opaque ),
+ mBackgroundColor( bg_color ),
+ mReserveScrollCorner( FALSE ),
+ mAutoScrolling( FALSE ),
+ mAutoScrollRate( 0.f )
+{
+ if( mScrolledView )
+ {
+ addChild( mScrolledView );
+ }
+
+ init();
+}
+
+// LLUICtrl constructor
+LLScrollableContainerView::LLScrollableContainerView( const LLString& name, const LLRect& rect,
+ LLUICtrl* scrolled_ctrl, BOOL is_opaque,
+ const LLColor4& bg_color) :
+ LLUICtrl( name, rect, FALSE, NULL, NULL ),
+ mScrolledView( scrolled_ctrl ),
+ mIsOpaque( is_opaque ),
+ mBackgroundColor( bg_color ),
+ mReserveScrollCorner( FALSE ),
+ mAutoScrolling( FALSE ),
+ mAutoScrollRate( 0.f )
+{
+ if( scrolled_ctrl )
+ {
+ addChild( scrolled_ctrl );
+ }
+
+ init();
+}
+
+void LLScrollableContainerView::init()
+{
+ LLRect border_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ mBorder = new LLViewBorder( "scroll border", border_rect, LLViewBorder::BEVEL_IN );
+ addChild( mBorder );
+
+ mInnerRect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ mInnerRect.stretch( -mBorder->getBorderWidth() );
+
+ LLRect vertical_scroll_rect = mInnerRect;
+ vertical_scroll_rect.mLeft = vertical_scroll_rect.mRight - SCROLLBAR_SIZE;
+ mScrollbar[VERTICAL] = new LLScrollbar( "scrollable vertical",
+ vertical_scroll_rect,
+ LLScrollbar::VERTICAL,
+ mInnerRect.getHeight(),
+ 0,
+ mInnerRect.getHeight(),
+ NULL, this,
+ VERTICAL_MULTIPLE);
+ addChild( mScrollbar[VERTICAL] );
+ mScrollbar[VERTICAL]->setVisible( FALSE );
+ mScrollbar[VERTICAL]->setFollowsRight();
+ mScrollbar[VERTICAL]->setFollowsTop();
+ mScrollbar[VERTICAL]->setFollowsBottom();
+
+ LLRect horizontal_scroll_rect = mInnerRect;
+ horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + SCROLLBAR_SIZE;
+ mScrollbar[HORIZONTAL] = new LLScrollbar( "scrollable horizontal",
+ horizontal_scroll_rect,
+ LLScrollbar::HORIZONTAL,
+ mInnerRect.getWidth(),
+ 0,
+ mInnerRect.getWidth(),
+ NULL, this,
+ HORIZONTAL_MULTIPLE);
+ addChild( mScrollbar[HORIZONTAL] );
+ mScrollbar[HORIZONTAL]->setVisible( FALSE );
+ mScrollbar[HORIZONTAL]->setFollowsLeft();
+ mScrollbar[HORIZONTAL]->setFollowsRight();
+
+ setTabStop(FALSE);
+}
+
+// Destroys the object
+LLScrollableContainerView::~LLScrollableContainerView( void )
+{
+ // mScrolledView and mScrollbar are child views, so the LLView
+ // destructor takes care of memory deallocation.
+ for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ {
+ mScrollbar[i] = NULL;
+ }
+ mScrolledView = NULL;
+}
+
+/*
+// scrollbar handlers
+void LLScrollableContainerView::horizontalChange( S32 new_pos,
+ LLScrollbar* sb,
+ void* user_data )
+{
+ LLScrollableContainerView* cont = reinterpret_cast<LLScrollableContainerView*>(user_data);
+// cont->scrollHorizontal( new_pos );
+}
+
+
+void LLScrollableContainerView::verticalChange( S32 new_pos, LLScrollbar* sb,
+ void* user_data )
+{
+ LLScrollableContainerView* cont = reinterpret_cast<LLScrollableContainerView*>(user_data);
+// cont->scrollVertical( new_pos );
+}
+*/
+
+// internal scrollbar handlers
+// virtual
+void LLScrollableContainerView::scrollHorizontal( S32 new_pos )
+{
+ //llinfos << "LLScrollableContainerView::scrollHorizontal()" << llendl;
+ if( mScrolledView )
+ {
+ LLRect doc_rect = mScrolledView->getRect();
+ S32 old_pos = -(doc_rect.mLeft - mInnerRect.mLeft);
+ mScrolledView->translate( -(new_pos - old_pos), 0 );
+ }
+}
+
+// virtual
+void LLScrollableContainerView::scrollVertical( S32 new_pos )
+{
+ // llinfos << "LLScrollableContainerView::scrollVertical() " << new_pos << llendl;
+ if( mScrolledView )
+ {
+ LLRect doc_rect = mScrolledView->getRect();
+ S32 old_pos = doc_rect.mTop - mInnerRect.mTop;
+ mScrolledView->translate( 0, new_pos - old_pos );
+ }
+}
+
+// LLView functionality
+void LLScrollableContainerView::reshape(S32 width, S32 height,
+ BOOL called_from_parent)
+{
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ mInnerRect.set( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ mInnerRect.stretch( -mBorder->getBorderWidth() );
+
+ if (mScrolledView)
+ {
+ const LLRect& scrolled_rect = mScrolledView->getRect();
+
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ BOOL show_v_scrollbar = FALSE;
+ BOOL show_h_scrollbar = FALSE;
+ calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+
+ mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
+ mScrollbar[VERTICAL]->setPageSize( visible_height );
+
+ mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+ }
+}
+
+BOOL LLScrollableContainerView::handleKey( KEY key, MASK mask, BOOL called_from_parent )
+{
+ if( getVisible() && mEnabled )
+ {
+ if( called_from_parent )
+ {
+ // Downward traversal
+
+ // Don't pass keys to scrollbars on downward.
+
+ // Handle 'child' view.
+ if( mScrolledView && mScrolledView->handleKey(key, mask, TRUE) )
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ // Upward traversal
+
+ for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ {
+ // Note: the scrollbar _is_ actually being called from it's parent. Here
+ // we're delgating LLScrollableContainerView's upward traversal to the scrollbars
+ if( mScrollbar[i]->handleKey(key, mask, TRUE) )
+ {
+ return TRUE;
+ }
+ }
+
+ if (getParent())
+ {
+ return getParent()->handleKey( key, mask, FALSE );
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLScrollableContainerView::handleScrollWheel( S32 x, S32 y, S32 clicks )
+{
+ if( mEnabled )
+ {
+ for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ {
+ // Note: tries vertical and then horizontal
+
+ // Pretend the mouse is over the scrollbar
+ if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ // Opaque
+ return TRUE;
+}
+
+BOOL LLScrollableContainerView::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ // Scroll folder view if needed. Never accepts a drag or drop.
+ *accept = ACCEPT_NO;
+ BOOL handled = FALSE;
+ if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() )
+ {
+ const S32 AUTOSCROLL_SIZE = 10;
+ S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32());
+
+ LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 );
+ if( mScrollbar[HORIZONTAL]->getVisible() )
+ {
+ inner_rect_local.mBottom += SCROLLBAR_SIZE;
+ }
+ if( mScrollbar[VERTICAL]->getVisible() )
+ {
+ inner_rect_local.mRight -= SCROLLBAR_SIZE;
+ }
+
+ if( mScrollbar[HORIZONTAL]->getVisible() )
+ {
+ LLRect left_scroll_rect = inner_rect_local;
+ left_scroll_rect.mRight = AUTOSCROLL_SIZE;
+ if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) )
+ {
+ mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed );
+ mAutoScrolling = TRUE;
+ handled = TRUE;
+ }
+
+ LLRect right_scroll_rect = inner_rect_local;
+ right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE;
+ if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) )
+ {
+ mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed );
+ mAutoScrolling = TRUE;
+ handled = TRUE;
+ }
+ }
+ if( mScrollbar[VERTICAL]->getVisible() )
+ {
+ LLRect bottom_scroll_rect = inner_rect_local;
+ bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom;
+ if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) )
+ {
+ mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed );
+ mAutoScrolling = TRUE;
+ handled = TRUE;
+ }
+
+ LLRect top_scroll_rect = inner_rect_local;
+ top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE;
+ if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) )
+ {
+ mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed );
+ mAutoScrolling = TRUE;
+ handled = TRUE;
+ }
+ }
+ }
+
+ if( !handled )
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLScrollableContainerView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect)
+{
+ if( getVisible() && pointInView(x,y) )
+ {
+ S32 local_x, local_y;
+ for( S32 i = 0; i < SCROLLBAR_COUNT; i++ )
+ {
+ local_x = x - mScrollbar[i]->getRect().mLeft;
+ local_y = y - mScrollbar[i]->getRect().mBottom;
+ if( mScrollbar[i]->handleToolTip(local_x, local_y, msg, sticky_rect) )
+ {
+ return TRUE;
+ }
+ }
+ // Handle 'child' view.
+ if( mScrolledView )
+ {
+ local_x = x - mScrolledView->getRect().mLeft;
+ local_y = y - mScrolledView->getRect().mBottom;
+ if( mScrolledView->handleToolTip(local_x, local_y, msg, sticky_rect) )
+ {
+ return TRUE;
+ }
+ }
+
+ // Opaque
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLScrollableContainerView::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar )
+{
+ const LLRect& rect = mScrolledView->getRect();
+ calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar);
+}
+
+void LLScrollableContainerView::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar )
+{
+ S32 doc_width = doc_rect.getWidth();
+ S32 doc_height = doc_rect.getHeight();
+
+ *visible_width = mRect.getWidth() - 2 * mBorder->getBorderWidth();
+ *visible_height = mRect.getHeight() - 2 * mBorder->getBorderWidth();
+
+ *show_v_scrollbar = FALSE;
+ if( *visible_height < doc_height )
+ {
+ *show_v_scrollbar = TRUE;
+ *visible_width -= SCROLLBAR_SIZE;
+ }
+
+ *show_h_scrollbar = FALSE;
+ if( *visible_width < doc_width )
+ {
+ *show_h_scrollbar = TRUE;
+ *visible_height -= SCROLLBAR_SIZE;
+
+ // Must retest now that visible_height has changed
+ if( !*show_v_scrollbar && (*visible_height < doc_height) )
+ {
+ *show_v_scrollbar = TRUE;
+ *visible_width -= SCROLLBAR_SIZE;
+ }
+ }
+}
+
+void LLScrollableContainerView::draw()
+{
+ if (mAutoScrolling)
+ {
+ // add acceleration to autoscroll
+ mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE);
+ }
+ else
+ {
+ // reset to minimum
+ mAutoScrollRate = MIN_AUTO_SCROLL_RATE;
+ }
+ // clear this flag to be set on next call to handleDragAndDrop
+ mAutoScrolling = FALSE;
+
+ if( getVisible() )
+ {
+ // auto-focus when scrollbar active
+ // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc)
+ if (!gFocusMgr.childHasKeyboardFocus(this) &&
+ (gFocusMgr.getMouseCapture() == mScrollbar[VERTICAL] || gFocusMgr.getMouseCapture() == mScrollbar[HORIZONTAL]))
+ {
+ focusFirstItem();
+ }
+
+ // Draw background
+ if( mIsOpaque )
+ {
+ LLGLSNoTexture no_texture;
+ glColor4fv( mBackgroundColor.mV );
+ gl_rect_2d( mInnerRect );
+ }
+
+ // Draw mScrolledViews and update scroll bars.
+ // get a scissor region ready, and draw the scrolling view. The
+ // scissor region ensures that we don't draw outside of the bounds
+ // of the rectangle.
+ if( mScrolledView )
+ {
+ updateScroll();
+
+ // Draw the scrolled area.
+ {
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ BOOL show_v_scrollbar = FALSE;
+ BOOL show_h_scrollbar = FALSE;
+ calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(mInnerRect.mLeft,
+ mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0) + visible_height,
+ visible_width,
+ mInnerRect.mBottom + (show_h_scrollbar ? SCROLLBAR_SIZE : 0)
+ ));
+ drawChild(mScrolledView);
+ }
+ }
+
+ // Highlight border if a child of this container has keyboard focus
+ if( mBorder->getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus(this) );
+ }
+
+ // Draw all children except mScrolledView
+ // Note: scrollbars have been adjusted by above drawing code
+ for (child_list_const_reverse_iter_t child_iter = getChildList()->rbegin();
+ child_iter != getChildList()->rend(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ if( sDebugRects )
+ {
+ sDepth++;
+ }
+ if( (viewp != mScrolledView) && viewp->getVisible() )
+ {
+ drawChild(viewp);
+ }
+ if( sDebugRects )
+ {
+ sDepth--;
+ }
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+void LLScrollableContainerView::updateScroll()
+{
+ LLRect doc_rect = mScrolledView->getRect();
+ S32 doc_width = doc_rect.getWidth();
+ S32 doc_height = doc_rect.getHeight();
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ BOOL show_v_scrollbar = FALSE;
+ BOOL show_h_scrollbar = FALSE;
+ calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+
+ S32 border_width = mBorder->getBorderWidth();
+ if( show_v_scrollbar )
+ {
+ if( doc_rect.mTop < mRect.getHeight() - border_width )
+ {
+ mScrolledView->translate( 0, mRect.getHeight() - border_width - doc_rect.mTop );
+ }
+
+ scrollVertical( mScrollbar[VERTICAL]->getDocPos() );
+ mScrollbar[VERTICAL]->setVisible( TRUE );
+
+ S32 v_scrollbar_height = visible_height;
+ if( !show_h_scrollbar && mReserveScrollCorner )
+ {
+ v_scrollbar_height -= SCROLLBAR_SIZE;
+ }
+ mScrollbar[VERTICAL]->reshape( SCROLLBAR_SIZE, v_scrollbar_height, TRUE );
+
+ // Make room for the horizontal scrollbar (or not)
+ S32 v_scrollbar_offset = 0;
+ if( show_h_scrollbar || mReserveScrollCorner )
+ {
+ v_scrollbar_offset = SCROLLBAR_SIZE;
+ }
+ LLRect r = mScrollbar[VERTICAL]->getRect();
+ r.translate( 0, mInnerRect.mBottom - r.mBottom + v_scrollbar_offset );
+ mScrollbar[VERTICAL]->setRect( r );
+ }
+ else
+ {
+ mScrolledView->translate( 0, mRect.getHeight() - border_width - doc_rect.mTop );
+
+ mScrollbar[VERTICAL]->setVisible( FALSE );
+ mScrollbar[VERTICAL]->setDocPos( 0 );
+ }
+
+ if( show_h_scrollbar )
+ {
+ if( doc_rect.mLeft > border_width )
+ {
+ mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
+ mScrollbar[HORIZONTAL]->setDocPos( 0 );
+ }
+ else
+ {
+ scrollHorizontal( mScrollbar[HORIZONTAL]->getDocPos() );
+ }
+
+ mScrollbar[HORIZONTAL]->setVisible( TRUE );
+ S32 h_scrollbar_width = visible_width;
+ if( !show_v_scrollbar && mReserveScrollCorner )
+ {
+ h_scrollbar_width -= SCROLLBAR_SIZE;
+ }
+ mScrollbar[HORIZONTAL]->reshape( h_scrollbar_width, SCROLLBAR_SIZE, TRUE );
+ }
+ else
+ {
+ mScrolledView->translate( border_width - doc_rect.mLeft, 0 );
+
+ mScrollbar[HORIZONTAL]->setVisible( FALSE );
+ mScrollbar[HORIZONTAL]->setDocPos( 0 );
+ }
+
+ mScrollbar[HORIZONTAL]->setDocSize( doc_width );
+ mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+
+ mScrollbar[VERTICAL]->setDocSize( doc_height );
+ mScrollbar[VERTICAL]->setPageSize( visible_height );
+}
+
+void LLScrollableContainerView::setBorderVisible(BOOL b)
+{
+ mBorder->setVisible( b );
+}
+
+// Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled)
+void LLScrollableContainerView::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset)
+{
+ if (!mScrolledView)
+ {
+ llwarns << "LLScrollableContainerView::scrollToShowRect with no view!" << llendl;
+ return;
+ }
+
+ S32 visible_width = 0;
+ S32 visible_height = 0;
+ BOOL show_v_scrollbar = FALSE;
+ BOOL show_h_scrollbar = FALSE;
+ const LLRect& scrolled_rect = mScrolledView->getRect();
+ calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar );
+
+ // can't be so far left that right side of rect goes off screen, or so far right that left side does
+ S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0);
+ // can't be so high that bottom of rect goes off screen, or so low that top does
+ S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight()));
+
+ // Vertical
+ // 1. First make sure the top is visible
+ // 2. Then, if possible without hiding the top, make the bottom visible.
+ S32 vert_pos = mScrollbar[VERTICAL]->getDocPos();
+
+ // find scrollbar position to get top of rect on screen (scrolling up)
+ S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset;
+ // find scrollbar position to get bottom of rect on screen (scrolling down)
+ S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset;
+ // scroll up far enough to see top or scroll down just enough if item is bigger than visual area
+ if( vert_pos >= top_offset || visible_height < rect.getHeight())
+ {
+ vert_pos = top_offset;
+ }
+ // else scroll down far enough to see bottom
+ else
+ if( vert_pos <= bottom_offset )
+ {
+ vert_pos = bottom_offset;
+ }
+
+ mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() );
+ mScrollbar[VERTICAL]->setPageSize( visible_height );
+ mScrollbar[VERTICAL]->setDocPos( vert_pos );
+
+ // Horizontal
+ // 1. First make sure left side is visible
+ // 2. Then, if possible without hiding the left side, make the right side visible.
+ S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos();
+ S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset;
+ S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset;
+
+ if( horiz_pos >= left_offset || visible_width < rect.getWidth() )
+ {
+ horiz_pos = left_offset;
+ }
+ else if( horiz_pos <= right_offset )
+ {
+ horiz_pos = right_offset;
+ }
+
+ mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() );
+ mScrollbar[HORIZONTAL]->setPageSize( visible_width );
+ mScrollbar[HORIZONTAL]->setDocPos( horiz_pos );
+
+ // propagate scroll to document
+ updateScroll();
+}
+
+void LLScrollableContainerView::pageUp(S32 overlap)
+{
+ mScrollbar[VERTICAL]->pageUp(overlap);
+}
+
+void LLScrollableContainerView::pageDown(S32 overlap)
+{
+ mScrollbar[VERTICAL]->pageDown(overlap);
+}
+
+void LLScrollableContainerView::goToTop()
+{
+ mScrollbar[VERTICAL]->setDocPos(0);
+}
+
+void LLScrollableContainerView::goToBottom()
+{
+ mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize());
+}
+
+S32 LLScrollableContainerView::getBorderWidth()
+{
+ if (mBorder)
+ {
+ return mBorder->getBorderWidth();
+ }
+
+ return 0;
+}
+
+// virtual
+LLXMLNodePtr LLScrollableContainerView::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLView::getXML();
+
+ // Attributes
+
+ node->createChild("opaque", TRUE)->setBoolValue(mIsOpaque);
+
+ if (mIsOpaque)
+ {
+ node->createChild("color", TRUE)->setFloatValue(4, mBackgroundColor.mV);
+ }
+
+ // Contents
+
+ LLXMLNodePtr child_node = mScrolledView->getXML();
+
+ node->addChild(child_node);
+
+ return node;
+}
+
+LLView* LLScrollableContainerView::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("scroll_container");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL opaque = FALSE;
+ node->getAttributeBOOL("opaque", opaque);
+
+ LLColor4 color(0,0,0,0);
+ LLUICtrlFactory::getAttributeColor(node,"color", color);
+
+ // Create the scroll view
+ LLScrollableContainerView *ret = new LLScrollableContainerView(name, rect, (LLPanel*)NULL, opaque, color);
+
+ LLPanel* panelp = NULL;
+
+ // Find a child panel to add
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ LLView *control = factory->createCtrlWidget(panelp, child);
+ if (control && control->isPanel())
+ {
+ if (panelp)
+ {
+ llinfos << "Warning! Attempting to put multiple panels into a scrollable container view!" << llendl;
+ delete control;
+ }
+ else
+ {
+ panelp = (LLPanel*)control;
+ }
+ }
+ }
+
+ if (panelp == NULL)
+ {
+ panelp = new LLPanel("dummy", LLRect::null, FALSE);
+ }
+
+ ret->mScrolledView = panelp;
+
+ return ret;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h
new file mode 100644
index 0000000000..5f23be4628
--- /dev/null
+++ b/indra/llui/llscrollcontainer.h
@@ -0,0 +1,109 @@
+/**
+ * @file llscrollcontainer.h
+ * @brief LLScrollableContainerView class header file.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSCROLLCONTAINER_H
+#define LL_LLSCROLLCONTAINER_H
+
+#include "lluictrl.h"
+#ifndef LL_V4COLOR_H
+#include "v4color.h"
+#endif
+#include "stdenums.h"
+#include "llcoord.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLScrollableContainerView
+//
+// A view meant to encapsulate a clipped region which is
+// scrollable. It automatically takes care of pixel perfect scrolling
+// and cliipping, as well as turning the scrollbars on or off based on
+// the width and height of the view you're scrolling.
+//
+// This class is a decorator class.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLScrollbar;
+class LLViewBorder;
+
+
+class LLScrollableContainerView : public LLUICtrl
+{
+public:
+ LLScrollableContainerView( const LLString& name, const LLRect& rect,
+ LLView* scrolled_view, BOOL is_opaque = FALSE,
+ const LLColor4& bg_color = LLColor4(0,0,0,0) );
+ LLScrollableContainerView( const LLString& name, const LLRect& rect,
+ LLUICtrl* scrolled_ctrl, BOOL is_opaque = FALSE,
+ const LLColor4& bg_color = LLColor4(0,0,0,0) );
+ virtual ~LLScrollableContainerView( void );
+
+ void init();
+
+ void setScrolledView(LLView* view) { mScrolledView = view; }
+
+ virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); }
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_SCROLL_CONTAINER; }
+ virtual LLString getWidgetTag() const { return LL_SCROLLABLE_CONTAINER_VIEW_TAG; }
+
+ // scrollbar handlers
+ static void horizontalChange( S32 new_pos, LLScrollbar* sb, void* user_data );
+ static void verticalChange( S32 new_pos, LLScrollbar* sb, void* user_data );
+
+ void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar );
+ void calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar );
+ void setBorderVisible( BOOL b );
+
+ void scrollToShowRect( const LLRect& rect, const LLCoordGL& desired_offset );
+ void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; }
+ const LLRect& getScrolledViewRect() { return mScrolledView->getRect(); }
+ void pageUp(S32 overlap = 0);
+ void pageDown(S32 overlap = 0);
+ void goToTop();
+ void goToBottom();
+ S32 getBorderWidth();
+
+ // LLView functionality
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks );
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual void draw();
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+protected:
+ // internal scrollbar handlers
+ virtual void scrollHorizontal( S32 new_pos );
+ virtual void scrollVertical( S32 new_pos );
+ void updateScroll();
+
+ // Note: vertical comes before horizontal because vertical
+ // scrollbars have priority for mouse and keyboard events.
+ enum { VERTICAL, HORIZONTAL, SCROLLBAR_COUNT };
+
+ LLScrollbar* mScrollbar[SCROLLBAR_COUNT];
+ LLView* mScrolledView;
+ S32 mSize;
+ BOOL mIsOpaque;
+ LLColor4 mBackgroundColor;
+ LLRect mInnerRect;
+ LLViewBorder* mBorder;
+ BOOL mReserveScrollCorner;
+ BOOL mAutoScrolling;
+ F32 mAutoScrollRate;
+};
+
+
+#endif // LL_LLSCROLLCONTAINER_H
diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp
new file mode 100644
index 0000000000..a4d20edfe9
--- /dev/null
+++ b/indra/llui/llscrollingpanellist.cpp
@@ -0,0 +1,150 @@
+/**
+ * @file llscrollingpanellist.cpp
+ * @brief
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llstl.h"
+
+#include "llscrollingpanellist.h"
+
+/////////////////////////////////////////////////////////////////////
+// LLScrollingPanelList
+
+// This could probably be integrated with LLScrollContainer -SJB
+
+void LLScrollingPanelList::clearPanels()
+{
+ deleteAllChildren();
+ mPanelList.clear();
+ reshape( 1, 1, FALSE );
+}
+
+void LLScrollingPanelList::addPanel( LLScrollingPanel* panel )
+{
+ addChildAtEnd( panel );
+ mPanelList.push_front( panel );
+
+ const S32 GAP_BETWEEN_PANELS = 6;
+
+ // Resize this view
+ S32 total_height = 0;
+ S32 max_width = 0;
+ S32 cur_gap = 0;
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ total_height += childp->getRect().getHeight() + cur_gap;
+ max_width = llmax( max_width, childp->getRect().getWidth() );
+ cur_gap = GAP_BETWEEN_PANELS;
+ }
+ reshape( max_width, total_height, FALSE );
+
+ // Reposition each of the child views
+ S32 cur_y = total_height;
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ cur_y -= childp->getRect().getHeight();
+ childp->translate( -childp->getRect().mLeft, cur_y - childp->getRect().mBottom);
+ cur_y -= GAP_BETWEEN_PANELS;
+ }
+}
+
+void LLScrollingPanelList::updatePanels(BOOL allow_modify)
+{
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ childp->updatePanel(allow_modify);
+ }
+}
+
+void LLScrollingPanelList::updatePanelVisiblilty()
+{
+ // Determine visibility of children.
+ S32 BORDER_WIDTH = 2; // HACK
+
+ LLRect parent_local_rect = getParent()->getRect();
+ parent_local_rect.stretch( -BORDER_WIDTH );
+
+ LLRect parent_screen_rect;
+ getParent()->localPointToScreen(
+ BORDER_WIDTH, 0,
+ &parent_screen_rect.mLeft, &parent_screen_rect.mBottom );
+ getParent()->localPointToScreen(
+ parent_local_rect.getWidth() - BORDER_WIDTH, parent_local_rect.getHeight() - BORDER_WIDTH,
+ &parent_screen_rect.mRight, &parent_screen_rect.mTop );
+
+ for (std::deque<LLScrollingPanel*>::iterator iter = mPanelList.begin();
+ iter != mPanelList.end(); ++iter)
+ {
+ LLScrollingPanel *childp = *iter;
+ const LLRect& local_rect = childp->getRect();
+ LLRect screen_rect;
+ childp->localPointToScreen(
+ 0, 0,
+ &screen_rect.mLeft, &screen_rect.mBottom );
+ childp->localPointToScreen(
+ local_rect.getWidth(), local_rect.getHeight(),
+ &screen_rect.mRight, &screen_rect.mTop );
+
+ BOOL intersects =
+ ( (screen_rect.mRight > parent_screen_rect.mLeft) && (screen_rect.mLeft < parent_screen_rect.mRight) ) &&
+ ( (screen_rect.mTop > parent_screen_rect.mBottom) && (screen_rect.mBottom < parent_screen_rect.mTop) );
+
+ childp->setVisible( intersects );
+ }
+}
+
+void LLScrollingPanelList::setValue(const LLSD& value)
+{
+
+}
+
+EWidgetType LLScrollingPanelList::getWidgetType() const
+{
+ return WIDGET_TYPE_SCROLLING_PANEL_LIST;
+}
+
+LLString LLScrollingPanelList::getWidgetTag() const
+{
+ return LL_SCROLLING_PANEL_LIST_TAG;
+}
+
+void LLScrollingPanelList::draw()
+{
+ if( getVisible() )
+ {
+ updatePanelVisiblilty();
+ }
+ LLUICtrl::draw();
+}
+
+
+// static
+LLView* LLScrollingPanelList::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("scrolling_panel_list");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLScrollingPanelList* scrolling_panel_list = new LLScrollingPanelList(name, rect);
+ scrolling_panel_list->initFromXML(node, parent);
+ return scrolling_panel_list;
+}
+
+// virtual
+LLXMLNodePtr LLScrollingPanelList::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+ return node;
+}
diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h
new file mode 100644
index 0000000000..b5f20ce172
--- /dev/null
+++ b/indra/llui/llscrollingpanellist.h
@@ -0,0 +1,53 @@
+/**
+ * @file llscrollingpanellist.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <vector>
+
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llview.h"
+#include "llpanel.h"
+
+// virtual class for scrolling panels
+class LLScrollingPanel : public LLPanel
+{
+public:
+ LLScrollingPanel(const LLString& name, const LLRect& rect)
+ : LLPanel(name, rect)
+ {
+ }
+ virtual void updatePanel(BOOL allow_modify) = 0;
+
+};
+
+// A set of panels that are displayed in a vertical sequence inside a scroll container.
+class LLScrollingPanelList : public LLUICtrl
+{
+public:
+ LLScrollingPanelList(const LLString& name, const LLRect& rect)
+ : LLUICtrl(name, rect, TRUE, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_BOTTOM ) {}
+
+ virtual void setValue(const LLSD& value);
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual LLXMLNodePtr getXML(bool save_children) const;
+
+ virtual void draw();
+
+ void clearPanels();
+ void addPanel( LLScrollingPanel* panel );
+ void updatePanels(BOOL allow_modify);
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+protected:
+ void updatePanelVisiblilty();
+
+protected:
+ std::deque<LLScrollingPanel*> mPanelList;
+};
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
new file mode 100644
index 0000000000..5d11973b88
--- /dev/null
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -0,0 +1,2673 @@
+/**
+ * @file llscrolllistctrl.cpp
+ * @brief LLScrollListCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <algorithm>
+
+#include "linden_common.h"
+#include "llstl.h"
+#include "llboost.h"
+
+#include "llscrolllistctrl.h"
+
+#include "indra_constants.h"
+
+#include "llcheckboxctrl.h"
+#include "llclipboard.h"
+#include "llfocusmgr.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llstring.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llwindow.h"
+#include "llcontrol.h"
+#include "llkeyboard.h"
+
+const S32 LIST_BORDER_PAD = 2; // white space inside the border and to the left of the scrollbar
+
+U32 LLScrollListCtrl::sSortColumn = 1;
+BOOL LLScrollListCtrl::sSortAscending = TRUE;
+
+// local structures & classes.
+struct SortScrollListItem
+{
+ SortScrollListItem(const S32 sort_col, BOOL sort_ascending)
+ {
+ mSortCol = sort_col;
+ sSortAscending = sort_ascending;
+ }
+
+ bool operator()(const LLScrollListItem* i1, const LLScrollListItem* i2)
+ {
+ const LLScrollListCell *cell1;
+ const LLScrollListCell *cell2;
+
+ cell1 = i1->getColumn(mSortCol);
+ cell2 = i2->getColumn(mSortCol);
+
+ S32 order = 1;
+ if (!sSortAscending)
+ {
+ order = -1;
+ }
+
+ BOOL retval = FALSE;
+
+ if (cell1 && cell2)
+ {
+ retval = ((order * LLString::compareDict(cell1->getText(), cell2->getText())) < 0);
+ }
+
+ return (retval ? TRUE : FALSE);
+ }
+
+protected:
+ S32 mSortCol;
+ S32 sSortAscending;
+};
+
+
+
+//
+// LLScrollListIcon
+//
+LLScrollListIcon::LLScrollListIcon(LLImageGL* icon, S32 width, LLUUID image_id) :
+mIcon(icon), mImageUUID(image_id.getString())
+{
+ if (width)
+ {
+ mWidth = width;
+ }
+ else
+ {
+ mWidth = icon->getWidth();
+ }
+}
+
+LLScrollListIcon::~LLScrollListIcon()
+{
+}
+
+//
+// LLScrollListCheck
+//
+LLScrollListCheck::LLScrollListCheck(LLCheckBoxCtrl* check_box, S32 width)
+{
+ mCheckBox = check_box;
+ LLRect rect(mCheckBox->getRect());
+ if (width)
+ {
+
+ rect.mRight = rect.mLeft + width;
+ mCheckBox->setRect(rect);
+ mWidth = width;
+ }
+ else
+ {
+ mWidth = rect.getWidth(); //check_box->getWidth();
+ }
+}
+
+LLScrollListCheck::~LLScrollListCheck()
+{
+ delete mCheckBox;
+}
+
+void LLScrollListCheck::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const
+{
+ mCheckBox->draw();
+
+}
+
+BOOL LLScrollListCheck::handleClick()
+{
+ if ( mCheckBox->getEnabled() )
+ {
+ LLCheckBoxCtrl::onButtonPress(mCheckBox);
+ }
+ return TRUE;
+}
+
+//
+// LLScrollListText
+//
+U32 LLScrollListText::sCount = 0;
+
+LLScrollListText::LLScrollListText( const LLString& text, const LLFontGL* font, S32 width, U8 font_style, LLColor4& color, BOOL use_color, BOOL visible)
+: mText( text ),
+ mFont( font ),
+ mFontStyle( font_style ),
+ mWidth( width ),
+ mVisible( visible ),
+ mHighlightChars( 0 )
+{
+ if (use_color)
+ {
+ mColor = new LLColor4();
+ mColor->setVec(color);
+ }
+ else
+ {
+ mColor = NULL;
+ }
+
+ sCount++;
+
+ // initialize rounded rect image
+ if (!mRoundedRectImage)
+ {
+ mRoundedRectImage = LLUI::sImageProvider->getUIImageByID(LLUUID(LLUI::sAssetsGroup->getString("rounded_square.tga")));
+ }
+
+ // Yes, that's four dots, because we want it to have a little
+ // padding, in proportion to the font size.
+ mEllipsisWidth = (S32)mFont->getWidth("....");
+}
+
+LLScrollListText::~LLScrollListText()
+{
+ sCount--;
+ delete mColor;
+}
+
+void LLScrollListText::setText(const LLString& text)
+{
+ mText = text;
+}
+
+void LLScrollListText::drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const
+{
+ // If the user has specified a small minimum width, use that.
+ if (mWidth > 0 && mWidth < width)
+ {
+ width = mWidth;
+ }
+
+ const LLColor4* display_color;
+ if (mColor)
+ {
+ display_color = mColor;
+ }
+ else
+ {
+ display_color = &color;
+ }
+
+ if (mHighlightChars > 0)
+ {
+ mRoundedRectImage->bind();
+ glColor4fv(highlight_color.mV);
+ gl_segmented_rect_2d_tex(-2,
+ llround(mFont->getLineHeight()) + 1,
+ mFont->getWidth(mText.getString(), 0, mHighlightChars) + 1,
+ 1,
+ mRoundedRectImage->getWidth(),
+ mRoundedRectImage->getHeight(),
+ 16);
+ }
+
+ // Try to draw the entire string
+ F32 right_x;
+ U32 string_chars = mText.length();
+ U32 drawn_chars = mFont->render(mText.getWString(), 0, 0, 2,
+ *display_color,
+ LLFontGL::LEFT,
+ LLFontGL::BOTTOM,
+ mFontStyle,
+ string_chars,
+ width - mEllipsisWidth,
+ &right_x, FALSE);
+
+ // If we didn't get the whole string, abbreviate
+ if (drawn_chars < string_chars && drawn_chars)
+ {
+ mFont->renderUTF8("...", 0, right_x, 0.f, color, LLFontGL::LEFT, LLFontGL::BOTTOM, mFontStyle,
+ S32_MAX, S32_MAX, NULL, FALSE);
+ }
+}
+
+
+LLScrollListItem::~LLScrollListItem()
+{
+ std::for_each(mColumns.begin(), mColumns.end(), DeletePointer());
+}
+
+BOOL LLScrollListItem::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ S32 left = 0;
+ S32 right = 0;
+ S32 width = 0;
+
+ std::vector<LLScrollListCell *>::iterator iter = mColumns.begin();
+ std::vector<LLScrollListCell *>::iterator end = mColumns.end();
+ for ( ; iter != end; ++iter)
+ {
+ width = (*iter)->getWidth();
+ right += width;
+ if (left <= x && x < right )
+ {
+ handled = (*iter)->handleClick();
+ break;
+ }
+
+ left += width;
+ }
+ return handled;
+}
+
+void LLScrollListItem::setNumColumns(S32 columns)
+{
+ S32 prev_columns = mColumns.size();
+ if (columns < prev_columns)
+ {
+ std::for_each(mColumns.begin()+columns, mColumns.end(), DeletePointer());
+ }
+
+ mColumns.resize(columns);
+
+ for (S32 col = prev_columns; col < columns; ++col)
+ {
+ mColumns[col] = NULL;
+ }
+}
+
+void LLScrollListItem::setColumn( S32 column, LLScrollListCell *cell )
+{
+ if (column < (S32)mColumns.size())
+ {
+ delete mColumns[column];
+ mColumns[column] = cell;
+ }
+ else
+ {
+ llerrs << "LLScrollListItem::setColumn: bad column: " << column << llendl;
+ }
+}
+
+LLString LLScrollListItem::getContentsCSV()
+{
+ LLString ret;
+
+ S32 count = getNumColumns();
+ for (S32 i=0; i<count; ++i)
+ {
+ ret += getColumn(i)->getText();
+ if (i < count-1)
+ {
+ ret += ", ";
+ }
+ }
+
+ return ret;
+}
+
+void LLScrollListItem::setEnabled(BOOL b)
+{
+ if (b != mEnabled)
+ {
+ std::vector<LLScrollListCell *>::iterator iter = mColumns.begin();
+ std::vector<LLScrollListCell *>::iterator end = mColumns.end();
+ for ( ; iter != end; ++iter)
+ {
+ (*iter)->setEnabled(b);
+ }
+ mEnabled = b;
+ }
+}
+
+//---------------------------------------------------------------------------
+// LLScrollListCtrl
+//---------------------------------------------------------------------------
+
+LLScrollListCtrl::LLScrollListCtrl(const LLString& name, const LLRect& rect,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_user_data,
+ BOOL allow_multiple_selection,
+ BOOL show_border
+ )
+ : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data),
+ mLineHeight(0),
+ mScrollLines(0),
+ mPageLines(0),
+ mHeadingHeight(20),
+ mMaxSelectable(0),
+ mHeadingFont(NULL),
+ mAllowMultipleSelection( allow_multiple_selection ),
+ mAllowKeyboardMovement(TRUE),
+ mCommitOnKeyboardMovement(TRUE),
+ mCommitOnSelectionChange(FALSE),
+ mSelectionChanged(FALSE),
+ mCanSelect(TRUE),
+ mDisplayColumnButtons(FALSE),
+ mCollapseEmptyColumns(FALSE),
+ mIsPopup(FALSE),
+ mMaxItemCount(INT_MAX),
+ //mItemCount(0),
+ mBackgroundVisible( TRUE ),
+ mDrawStripes(TRUE),
+ mBgWriteableColor( LLUI::sColorsGroup->getColor( "ScrollBgWriteableColor" ) ),
+ mBgReadOnlyColor( LLUI::sColorsGroup->getColor( "ScrollBgReadOnlyColor" ) ),
+ mBgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedBGColor") ),
+ mBgStripeColor( LLUI::sColorsGroup->getColor("ScrollBGStripeColor") ),
+ mFgSelectedColor( LLUI::sColorsGroup->getColor("ScrollSelectedFGColor") ),
+ mFgUnselectedColor( LLUI::sColorsGroup->getColor("ScrollUnselectedColor") ),
+ mFgDisabledColor( LLUI::sColorsGroup->getColor("ScrollDisabledColor") ),
+ mHighlightedColor( LLUI::sColorsGroup->getColor("ScrollHighlightedColor") ),
+ mBorderThickness( 2 ),
+ mOnDoubleClickCallback( NULL ),
+ mOnMaximumSelectCallback( NULL ),
+ mHighlightedItem(-1),
+ mBorder(NULL),
+ mDefaultColumn("SIMPLE"),
+ mSearchColumn(0),
+ mNumDynamicWidthColumns(0),
+ mTotalStaticColumnWidth(0),
+ mDrewSelected(FALSE)
+{
+ mItemListRect.setOriginAndSize(
+ mBorderThickness + LIST_BORDER_PAD,
+ mBorderThickness + LIST_BORDER_PAD,
+ mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE,
+ mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) );
+
+ updateLineHeight();
+
+ mPageLines = mLineHeight? (mItemListRect.getHeight()) / mLineHeight : 0;
+
+ // Init the scrollbar
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ mRect.getWidth() - mBorderThickness - SCROLLBAR_SIZE,
+ mItemListRect.mBottom,
+ SCROLLBAR_SIZE,
+ mItemListRect.getHeight());
+ mScrollbar = new LLScrollbar( "Scrollbar", scroll_rect,
+ LLScrollbar::VERTICAL,
+ getItemCount(),
+ mScrollLines,
+ mPageLines,
+ &LLScrollListCtrl::onScrollChange, this );
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+ mScrollbar->setEnabled( TRUE );
+ mScrollbar->setVisible( TRUE );
+ addChild(mScrollbar);
+
+ // Border
+ if (show_border)
+ {
+ LLRect border_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ mBorder = new LLViewBorder( "dlg border", border_rect, LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, 1 );
+ addChild(mBorder);
+ }
+
+ mColumnPadding = 5;
+
+ mLastSelected = NULL;
+}
+
+LLScrollListCtrl::~LLScrollListCtrl()
+{
+ std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+
+BOOL LLScrollListCtrl::setMaxItemCount(S32 max_count)
+{
+ if (max_count >= getItemCount())
+ {
+ mMaxItemCount = max_count;
+ }
+ return (max_count == mMaxItemCount);
+}
+
+S32 LLScrollListCtrl::isEmpty() const
+{
+ return mItemList.empty();
+}
+
+S32 LLScrollListCtrl::getItemCount() const
+{
+ return mItemList.size();
+}
+
+// virtual LLScrolListInterface function (was deleteAllItems)
+void LLScrollListCtrl::clearRows()
+{
+ std::for_each(mItemList.begin(), mItemList.end(), DeletePointer());
+ mItemList.clear();
+ //mItemCount = 0;
+
+ // Scroll the bar back up to the top.
+ mScrollbar->setDocParams(0, 0);
+
+ mScrollLines = 0;
+ mLastSelected = NULL;
+}
+
+
+LLScrollListItem* LLScrollListCtrl::getFirstSelected() const
+{
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const
+{
+ std::vector<LLScrollListItem*> ret;
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ ret.push_back(item);
+ }
+ }
+ return ret;
+}
+
+S32 LLScrollListCtrl::getFirstSelectedIndex()
+{
+ S32 CurSelectedIndex = 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ return CurSelectedIndex;
+ }
+ CurSelectedIndex++;
+ }
+
+ return -1;
+}
+
+
+LLScrollListItem* LLScrollListCtrl::getFirstData() const
+{
+ if (mItemList.size() == 0)
+ {
+ return NULL;
+ }
+ return mItemList[0];
+}
+
+std::vector<LLScrollListItem*> LLScrollListCtrl::getAllData() const
+{
+ std::vector<LLScrollListItem*> ret;
+ item_list::const_iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ ret.push_back(item);
+ }
+ return ret;
+}
+
+
+void LLScrollListCtrl::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+ LLUICtrl::reshape( width, height, called_from_parent );
+
+ S32 heading_size = (mDisplayColumnButtons ? mHeadingHeight : 0);
+
+ mItemListRect.setOriginAndSize(
+ mBorderThickness + LIST_BORDER_PAD,
+ mBorderThickness + LIST_BORDER_PAD,
+ mRect.getWidth() - 2*( mBorderThickness + LIST_BORDER_PAD ) - SCROLLBAR_SIZE,
+ mRect.getHeight() - 2*( mBorderThickness + LIST_BORDER_PAD ) - heading_size );
+
+ mPageLines = mLineHeight? mItemListRect.getHeight() / mLineHeight : 0;
+ mScrollbar->setVisible(mPageLines < getItemCount());
+ mScrollbar->setPageSize( mPageLines );
+
+ updateColumns();
+ updateColumnButtons();
+}
+
+
+// Attempt to size the control to show all items.
+// Do not make larger than width or height.
+void LLScrollListCtrl::arrange(S32 max_width, S32 max_height)
+{
+ S32 height = mLineHeight * (getItemCount() + 1);
+ height = llmin( height, max_height );
+
+ S32 width = mRect.getWidth();
+
+ reshape( width, height );
+}
+
+
+LLRect LLScrollListCtrl::getRequiredRect()
+{
+ S32 height = mLineHeight * (getItemCount() + 1);
+ S32 width = mRect.getWidth();
+
+ return LLRect(0, height, width, 0);
+}
+
+
+BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos )
+{
+ BOOL not_too_big = getItemCount() < mMaxItemCount;
+ if (not_too_big)
+ {
+ switch( pos )
+ {
+ case ADD_TOP:
+ mItemList.push_front(item);
+ break;
+
+ case ADD_SORTED:
+ LLScrollListCtrl::sSortColumn = 0;
+ LLScrollListCtrl::sSortAscending = TRUE;
+ mItemList.push_back(item);
+ std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(sSortColumn, sSortAscending));
+ break;
+
+ case ADD_BOTTOM:
+ mItemList.push_back(item);
+ break;
+
+ default:
+ llassert(0);
+ mItemList.push_back(item);
+ break;
+ }
+
+ updateLineHeight();
+ mPageLines = mLineHeight ? mItemListRect.getHeight() / mLineHeight : 0;
+ mScrollbar->setVisible(mPageLines < getItemCount());
+ mScrollbar->setPageSize( mPageLines );
+
+ mScrollbar->setDocSize( getItemCount() );
+ }
+ return not_too_big;
+}
+
+
+// Line height is the max height of all the cells in all the items.
+void LLScrollListCtrl::updateLineHeight()
+{
+ const S32 ROW_PAD = 2;
+
+ mLineHeight = 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ S32 num_cols = itemp->getNumColumns();
+ S32 i = 0;
+ for (const LLScrollListCell* cell = itemp->getColumn(i); i < num_cols; cell = itemp->getColumn(++i))
+ {
+ mLineHeight = llmax( mLineHeight, cell->getHeight() + ROW_PAD );
+ }
+ }
+}
+
+void LLScrollListCtrl::updateColumns()
+{
+ mColumnsIndexed.resize(mColumns.size());
+
+ std::map<LLString, LLScrollListColumn>::iterator column_itor;
+ for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
+ {
+ LLScrollListColumn *column = &column_itor->second;
+ if (column->mRelWidth >= 0)
+ {
+ column->mWidth = (S32)llround(column->mRelWidth*mItemListRect.getWidth());
+ }
+ else if (column->mDynamicWidth)
+ {
+ column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
+
+ }
+ mColumnsIndexed[column_itor->second.mIndex] = column;
+ }
+}
+
+void LLScrollListCtrl::updateColumnButtons()
+{
+ std::map<LLString, LLScrollListColumn>::iterator column_itor;
+ for (column_itor = mColumns.begin(); column_itor != mColumns.end(); ++column_itor)
+ {
+ LLScrollListColumn* column = &column_itor->second;
+ LLButton *button = column->mButton;
+
+ if (button)
+ {
+ mColumnsIndexed[column->mIndex] = column;
+
+ S32 top = mItemListRect.mTop;
+ S32 left = mItemListRect.mLeft;
+ {
+ std::map<LLString, LLScrollListColumn>::iterator itor;
+ for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
+ {
+ if (itor->second.mIndex < column->mIndex &&
+ itor->second.mWidth > 0)
+ {
+ left += itor->second.mWidth + mColumnPadding;
+ }
+ }
+ }
+ S32 right = left+column->mWidth;
+ if (column->mIndex != (S32)mColumns.size()-1)
+ {
+ right += mColumnPadding;
+ }
+ LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
+ button->setRect(temp_rect);
+ button->setFont(mHeadingFont);
+ button->setVisible(mDisplayColumnButtons);
+ }
+ }
+}
+
+void LLScrollListCtrl::setDisplayHeading(BOOL display)
+{
+ mDisplayColumnButtons = display;
+
+ updateColumns();
+
+ setHeadingHeight(mHeadingHeight);
+}
+
+void LLScrollListCtrl::setHeadingHeight(S32 heading_height)
+{
+ mHeadingHeight = heading_height;
+
+ reshape(mRect.getWidth(), mRect.getHeight());
+
+ // Resize
+ mScrollbar->reshape(SCROLLBAR_SIZE, mItemListRect.getHeight());
+
+ updateColumnButtons();
+}
+
+void LLScrollListCtrl::setHeadingFont(const LLFontGL* heading_font)
+{
+ mHeadingFont = heading_font;
+ updateColumnButtons();
+}
+
+void LLScrollListCtrl::setCollapseEmptyColumns(BOOL collapse)
+{
+ mCollapseEmptyColumns = collapse;
+}
+
+BOOL LLScrollListCtrl::selectFirstItem()
+{
+ BOOL success = FALSE;
+
+ // our $%&@#$()^%#$()*^ iterators don't let us check against the first item inside out iteration
+ BOOL first_item = TRUE;
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if( first_item && itemp->getEnabled() )
+ {
+ if (!itemp->getSelected())
+ {
+ selectItem(itemp);
+ }
+ success = TRUE;
+ }
+ else
+ {
+ deselectItem(itemp);
+ }
+ first_item = FALSE;
+ }
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+ return success;
+}
+
+
+BOOL LLScrollListCtrl::selectNthItem( S32 target_index )
+{
+ // Deselects all other items
+ BOOL success = FALSE;
+ S32 index = 0;
+
+ target_index = llclamp(target_index, 0, (S32)mItemList.size() - 1);
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if( target_index == index )
+ {
+ if( itemp->getEnabled() )
+ {
+ selectItem(itemp);
+ success = TRUE;
+ }
+ }
+ else
+ {
+ deselectItem(itemp);
+ }
+ index++;
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ mSearchString.clear();
+
+ return success;
+}
+
+
+void LLScrollListCtrl::swapWithNext(S32 index)
+{
+ if (index >= ((S32)mItemList.size() - 1))
+ {
+ // At end of list, doesn't do anything
+ return;
+ }
+ LLScrollListItem *cur_itemp = mItemList[index];
+ mItemList[index] = mItemList[index + 1];
+ mItemList[index + 1] = cur_itemp;
+}
+
+
+void LLScrollListCtrl::swapWithPrevious(S32 index)
+{
+ if (index <= 0)
+ {
+ // At beginning of list, don't do anything
+ }
+
+ LLScrollListItem *cur_itemp = mItemList[index];
+ mItemList[index] = mItemList[index - 1];
+ mItemList[index - 1] = cur_itemp;
+}
+
+
+void LLScrollListCtrl::deleteSingleItem(S32 target_index)
+{
+ if (target_index >= (S32)mItemList.size())
+ {
+ return;
+ }
+
+ LLScrollListItem *itemp;
+ itemp = mItemList[target_index];
+ if (itemp == mLastSelected)
+ {
+ mLastSelected = NULL;
+ }
+ delete itemp;
+ mItemList.erase(mItemList.begin() + target_index);
+}
+
+void LLScrollListCtrl::deleteSelectedItems()
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter < mItemList.end(); )
+ {
+ LLScrollListItem* itemp = *iter;
+ if (itemp->getSelected())
+ {
+ delete itemp;
+ iter = mItemList.erase(iter);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+ mLastSelected = NULL;
+}
+
+void LLScrollListCtrl::highlightNthItem(S32 target_index)
+{
+ if (mHighlightedItem != target_index)
+ {
+ mHighlightedItem = target_index;
+ }
+}
+
+S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item )
+{
+ S32 index = 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if (target_item == itemp)
+ {
+ return index;
+ }
+ index++;
+ }
+ return -1;
+}
+
+S32 LLScrollListCtrl::getItemIndex( LLUUID& target_id )
+{
+ S32 index = 0;
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if (target_id == itemp->getUUID())
+ {
+ return index;
+ }
+ index++;
+ }
+ return -1;
+}
+
+void LLScrollListCtrl::selectPrevItem( BOOL extend_selection)
+{
+ LLScrollListItem* prev_item = NULL;
+
+ if (!getFirstSelected())
+ {
+ selectFirstItem();
+ }
+ else
+ {
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* cur_item = *iter;
+
+ if (cur_item->getSelected())
+ {
+ if (prev_item)
+ {
+ selectItem(prev_item, !extend_selection);
+ }
+ else
+ {
+ reportInvalidInput();
+ }
+ break;
+ }
+
+ prev_item = cur_item;
+ }
+ }
+
+ if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
+ {
+ commitIfChanged();
+ }
+
+ mSearchString.clear();
+}
+
+
+void LLScrollListCtrl::selectNextItem( BOOL extend_selection)
+{
+ if (!getFirstSelected())
+ {
+ selectFirstItem();
+ }
+ else
+ {
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getSelected())
+ {
+ if (++iter != mItemList.end())
+ {
+ LLScrollListItem *next_item = *iter;
+ if (next_item)
+ {
+ selectItem(next_item, !extend_selection);
+ }
+ else
+ {
+ reportInvalidInput();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if ((mCommitOnSelectionChange || mCommitOnKeyboardMovement))
+ {
+ onCommit();
+ }
+
+ mSearchString.clear();
+}
+
+
+
+void LLScrollListCtrl::deselectAllItems(BOOL no_commit_on_change)
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ deselectItem(item);
+ }
+
+ if (mCommitOnSelectionChange && !no_commit_on_change)
+ {
+ commitIfChanged();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// "Simple" interface: use this when you're creating a list that contains only unique strings, only
+// one of which can be selected at a time.
+
+LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, EAddPosition pos, BOOL enabled)
+{
+ LLScrollListItem* item = NULL;
+ if (getItemCount() < mMaxItemCount)
+ {
+ // simple items have their LLSD data set to their label
+ item = new LLScrollListItem( LLSD(item_text) );
+ item->setEnabled(enabled);
+ item->addColumn( item_text, gResMgr->getRes( LLFONT_SANSSERIF_SMALL ) );
+ addItem( item, pos );
+ }
+ return item;
+}
+
+
+// Selects first enabled item of the given name.
+// Returns false if item not found.
+BOOL LLScrollListCtrl::selectSimpleItem(const LLString& label, BOOL case_sensitive)
+{
+ //RN: assume no empty items
+ if (label.empty())
+ {
+ return FALSE;
+ }
+
+ LLString target_text = label;
+ if (!case_sensitive)
+ {
+ LLString::toLower(target_text);
+ }
+
+ BOOL found = FALSE;
+
+ item_list::iterator iter;
+ S32 index = 0;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ // Only select enabled items with matching names
+ LLString item_text = item->getColumn(0)->getText();
+ if (!case_sensitive)
+ {
+ LLString::toLower(item_text);
+ }
+ BOOL select = !found && item->getEnabled() && item_text == target_text;
+ if (select)
+ {
+ selectItem(item);
+ }
+ found = found || select;
+ index++;
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+
+BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive)
+{
+ return selectSimpleItemByPrefix(utf8str_to_wstring(target), case_sensitive);
+}
+
+// Selects first enabled item that has a name where the name's first part matched the target string.
+// Returns false if item not found.
+BOOL LLScrollListCtrl::selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive)
+{
+ BOOL found = FALSE;
+
+ LLWString target_trimmed( target );
+ S32 target_len = target_trimmed.size();
+
+ if( 0 == target_len )
+ {
+ // Is "" a valid choice?
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ // Only select enabled items with matching names
+ LLScrollListCell* cellp = item->getColumn(mSearchColumn);
+ BOOL select = cellp ? item->getEnabled() && ('\0' == cellp->getText()[0]) : FALSE;
+ if (select)
+ {
+ selectItem(item);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (!case_sensitive)
+ {
+ // do comparisons in lower case
+ LLWString::toLower(target_trimmed);
+ }
+
+ for (item_list::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+
+ // Only select enabled items with matching names
+ LLScrollListCell* cellp = item->getColumn(mSearchColumn);
+ if (!cellp)
+ {
+ continue;
+ }
+ LLWString item_label = utf8str_to_wstring(cellp->getText());
+ if (!case_sensitive)
+ {
+ LLWString::toLower(item_label);
+ }
+
+ BOOL select = item->getEnabled() && !item_label.compare(0, target_len, target_trimmed);
+
+ if (select)
+ {
+ selectItem(item);
+ found = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+const LLString& LLScrollListCtrl::getSimpleSelectedItem(S32 column) const
+{
+ LLScrollListItem* item;
+
+ item = getFirstSelected();
+ if (item)
+ {
+ return item->getColumn(column)->getText();
+ }
+
+ return LLString::null;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+// "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
+// has an associated, unique UUID, and only one of which can be selected at a time.
+
+LLScrollListItem* LLScrollListCtrl::addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos, BOOL enabled, S32 column_width)
+{
+ LLScrollListItem* item = NULL;
+ if (getItemCount() < mMaxItemCount)
+ {
+ item = new LLScrollListItem( enabled, NULL, id );
+ item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width);
+ addItem( item, pos );
+ }
+ return item;
+}
+
+LLScrollListItem* LLScrollListCtrl::addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos, BOOL enabled, S32 column_width)
+{
+ LLScrollListItem* item = NULL;
+ if (getItemCount() < mMaxItemCount)
+ {
+ item = new LLScrollListItem( sd );
+ item->setEnabled(enabled);
+ item->addColumn(item_text, gResMgr->getRes(LLFONT_SANSSERIF_SMALL), column_width);
+ addItem( item, pos );
+ }
+ return item;
+}
+
+
+// Select the line or lines that match this UUID
+BOOL LLScrollListCtrl::selectByID( const LLUUID& id )
+{
+ return selectByValue( LLSD(id) );
+}
+
+BOOL LLScrollListCtrl::setSelectedByValue(LLSD value, BOOL selected)
+{
+ BOOL found = FALSE;
+
+ if (selected && !mAllowMultipleSelection) deselectAllItems(TRUE);
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getEnabled() && (item->getValue().asString() == value.asString()))
+ {
+ if (selected)
+ {
+ selectItem(item);
+ }
+ else
+ {
+ deselectItem(item);
+ }
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+
+ return found;
+}
+
+BOOL LLScrollListCtrl::isSelected(LLSD value)
+{
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getValue().asString() == value.asString())
+ {
+ return item->getSelected();
+ }
+ }
+ return FALSE;
+}
+
+LLUUID LLScrollListCtrl::getStringUUIDSelectedItem()
+{
+ LLScrollListItem* item = getFirstSelected();
+
+ if (item)
+ {
+ return item->getUUID();
+ }
+
+ return LLUUID::null;
+}
+
+LLSD LLScrollListCtrl::getSimpleSelectedValue()
+{
+ LLScrollListItem* item = getFirstSelected();
+
+ if (item)
+ {
+ return item->getValue();
+ }
+ else
+ {
+ return LLSD();
+ }
+}
+
+void LLScrollListCtrl::drawItems()
+{
+ S32 x = mItemListRect.mLeft;
+ S32 y = mItemListRect.mTop - mLineHeight;
+
+ S32 num_page_lines = mPageLines;
+
+ LLRect item_rect;
+
+ LLGLSUIDefault gls_ui;
+
+ {
+
+ S32 cur_x = x;
+ S32 cur_y = y;
+
+ mDrewSelected = FALSE;
+
+ S32 line = 0;
+ LLColor4 color;
+ S32 max_columns = 0;
+
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+
+ item_rect.setOriginAndSize(
+ cur_x,
+ cur_y,
+ mScrollbar->getVisible() ? mItemListRect.getWidth() : mItemListRect.getWidth() + mScrollbar->getRect().getWidth(),
+ mLineHeight );
+
+ lldebugs << mItemListRect.getWidth() << llendl;
+
+ if (item->getSelected())
+ {
+ mDrewSelected = TRUE;
+ }
+
+ max_columns = llmax(max_columns, item->getNumColumns());
+
+ LLRect bg_rect = item_rect;
+ // pad background rectangle to separate it from contents
+ bg_rect.stretch(LIST_BORDER_PAD, 0);
+
+ if( mScrollLines <= line && line < mScrollLines + num_page_lines )
+ {
+ if( item->getSelected() && mCanSelect)
+ {
+ // Draw background of selected item
+ LLGLSNoTexture no_texture;
+ glColor4fv(mBgSelectedColor.mV);
+ gl_rect_2d( bg_rect );
+
+ color = mFgSelectedColor;
+ }
+ else if (mHighlightedItem == line && mCanSelect)
+ {
+ LLGLSNoTexture no_texture;
+ glColor4fv(mHighlightedColor.mV);
+ gl_rect_2d( bg_rect );
+ color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor);
+ }
+ else
+ {
+ color = (item->getEnabled() ? mFgUnselectedColor : mFgDisabledColor);
+ if (mDrawStripes && (line%2 == 0) && (max_columns > 1))
+ {
+ LLGLSNoTexture no_texture;
+ glColor4fv(mBgStripeColor.mV);
+ gl_rect_2d( bg_rect );
+ }
+ }
+
+ S32 line_x = cur_x;
+ {
+ S32 num_cols = item->getNumColumns();
+ S32 cur_col = 0;
+ S32 dynamic_width = 0;
+ S32 dynamic_remainder = 0;
+ if(mNumDynamicWidthColumns > 0)
+ {
+ dynamic_width = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
+ dynamic_remainder = (mItemListRect.getWidth() - mTotalStaticColumnWidth) % mNumDynamicWidthColumns;
+ }
+ for (LLScrollListCell* cell = item->getColumn(0); cur_col < num_cols; cell = item->getColumn(++cur_col))
+ {
+ S32 cell_width = cell->getWidth();
+ if(mColumnsIndexed.size() > (U32)cur_col && mColumnsIndexed[cur_col] && mColumnsIndexed[cur_col]->mDynamicWidth)
+ {
+ cell_width = dynamic_width + (--dynamic_remainder ? 1 : 0);
+ cell->setWidth(cell_width);
+ }
+ // Two ways a cell could be hidden
+ if (cell_width < 0
+ || !cell->getVisible()) continue;
+ LLUI::pushMatrix();
+ LLUI::translate((F32) cur_x, (F32) cur_y, 0.0f);
+ S32 space_left = mItemListRect.mRight - cur_x;
+ LLColor4 highlight_color = LLColor4::white;
+ F32 type_ahead_timeout = LLUI::sConfigGroup->getF32("TypeAheadTimeout");
+
+ highlight_color.mV[VALPHA] = clamp_rescale(mSearchTimer.getElapsedTimeF32(), type_ahead_timeout * 0.7f, type_ahead_timeout, 0.4f, 0.f);
+ cell->drawToWidth( space_left, color, highlight_color );
+ LLUI::popMatrix();
+
+ cur_x += cell_width + mColumnPadding;
+
+ }
+ }
+ cur_x = line_x;
+ cur_y -= mLineHeight;
+ }
+ line++;
+ }
+ }
+}
+
+
+void LLScrollListCtrl::draw()
+{
+ if( getVisible() )
+ {
+ LLRect background(0, mRect.getHeight(), mRect.getWidth(), 0);
+ // Draw background
+ if (mBackgroundVisible)
+ {
+ LLGLSNoTexture no_texture;
+ glColor4fv( getEnabled() ? mBgWriteableColor.mV : mBgReadOnlyColor.mV );
+ gl_rect_2d(background);
+ }
+
+ drawItems();
+
+ if (mBorder)
+ {
+ mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this);
+ }
+
+ LLUICtrl::draw();
+ }
+}
+
+void LLScrollListCtrl::setEnabled(BOOL enabled)
+{
+ mCanSelect = enabled;
+ setTabStop(enabled);
+ mScrollbar->setTabStop(!enabled && mScrollbar->getPageSize() < mScrollbar->getDocSize());
+}
+
+BOOL LLScrollListCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ BOOL handled = FALSE;
+ // Pretend the mouse is over the scrollbar
+ handled = mScrollbar->handleScrollWheel( 0, 0, clicks );
+ return handled;
+}
+
+
+BOOL LLScrollListCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+
+ if( !handled && mCanSelect)
+ {
+ LLScrollListItem* hit_item = hitItem(x, y);
+ if( hit_item )
+ {
+ if( mAllowMultipleSelection )
+ {
+ if (mask & MASK_SHIFT)
+ {
+ if (mLastSelected == NULL)
+ {
+ selectItem(hit_item);
+ }
+ else
+ {
+ // Select everthing between mLastSelected and hit_item
+ bool selecting = false;
+ item_list::iterator itor;
+ // If we multiselect backwards, we'll stomp on mLastSelected,
+ // meaning that we never stop selecting until hitting max or
+ // the end of the list.
+ LLScrollListItem* lastSelected = mLastSelected;
+ for (itor = mItemList.begin(); itor != mItemList.end(); ++itor)
+ {
+ if(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable)
+ {
+ if(mOnMaximumSelectCallback)
+ {
+ mOnMaximumSelectCallback(mCallbackUserData);
+ }
+ break;
+ }
+ LLScrollListItem *item = *itor;
+ if (item == hit_item || item == lastSelected)
+ {
+ selectItem(item, FALSE);
+ selecting = !selecting;
+ }
+ if (selecting)
+ {
+ selectItem(item, FALSE);
+ }
+ }
+ }
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ if (hit_item->getSelected())
+ {
+ deselectItem(hit_item);
+ }
+ else
+ {
+ if(!(mMaxSelectable > 0 && getAllSelected().size() >= mMaxSelectable))
+ {
+ selectItem(hit_item, FALSE);
+ }
+ else
+ {
+ if(mOnMaximumSelectCallback)
+ {
+ mOnMaximumSelectCallback(mCallbackUserData);
+ }
+ }
+ }
+ }
+ else
+ {
+ deselectAllItems(TRUE);
+ selectItem(hit_item);
+ }
+ }
+ else
+ {
+ selectItem(hit_item);
+ }
+
+ hit_item->handleMouseDown(x - mBorderThickness - LIST_BORDER_PAD,
+ 1, mask);
+ // always commit on mousedown
+ onCommit();
+ mSelectionChanged = FALSE;
+
+ // clear search string on mouse operations
+ mSearchString.clear();
+ }
+ else
+ {
+ mLastSelected = NULL;
+ }
+ }
+
+ gFocusMgr.setKeyboardFocus(this, NULL);
+
+ return TRUE;
+}
+
+BOOL LLScrollListCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ //BOOL handled = FALSE;
+ if(getVisible())
+ {
+ // Offer the click to the children, even if we aren't enabled
+ // so the scroll bars will work.
+ if (NULL == LLView::childrenHandleDoubleClick(x, y, mask))
+ {
+ if( mCanSelect && mOnDoubleClickCallback )
+ {
+ mOnDoubleClickCallback( mCallbackUserData );
+ }
+ }
+ }
+ return TRUE;
+}
+
+LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y )
+{
+ // Excludes disabled items.
+ LLScrollListItem* hit_item = NULL;
+
+ LLRect item_rect;
+ item_rect.setLeftTopAndSize(
+ mItemListRect.mLeft,
+ mItemListRect.mTop,
+ mItemListRect.getWidth(),
+ mLineHeight );
+
+ int num_page_lines = mPageLines;
+
+ S32 line = 0;
+ item_list::iterator iter;
+ for(iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if( mScrollLines <= line && line < mScrollLines + num_page_lines )
+ {
+ if( item->getEnabled() && item_rect.pointInRect( x, y ) )
+ {
+ hit_item = item;
+ break;
+ }
+
+ item_rect.translate(0, -mLineHeight);
+ }
+ line++;
+ }
+
+ return hit_item;
+}
+
+
+BOOL LLScrollListCtrl::handleHover(S32 x,S32 y,MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if(getVisible())
+ {
+ if (mCanSelect)
+ {
+ LLScrollListItem* item = hitItem(x, y);
+ if (item)
+ {
+ highlightNthItem(getItemIndex(item));
+ }
+ else
+ {
+ highlightNthItem(-1);
+ }
+ }
+
+ handled = LLView::handleHover( x, y, mask );
+
+ if( !handled )
+ {
+ // Opaque
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ handled = TRUE;
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLScrollListCtrl::handleKeyHere(KEY key,MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ // not called from parent means we have keyboard focus or a child does
+ if (mCanSelect && !called_from_parent)
+ {
+ // Ignore capslock
+ mask = mask;
+
+ if (mask == MASK_NONE)
+ {
+ switch(key)
+ {
+ case KEY_UP:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // commit implicit in call
+ selectPrevItem(FALSE);
+ scrollToShowSelected();
+ handled = TRUE;
+ }
+ break;
+ case KEY_DOWN:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ // commit implicit in call
+ selectNextItem(FALSE);
+ scrollToShowSelected();
+ handled = TRUE;
+ }
+ break;
+ case KEY_PAGE_UP:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getFirstSelectedIndex() - (mScrollbar->getPageSize() - 1));
+ scrollToShowSelected();
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ }
+ break;
+ case KEY_PAGE_DOWN:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getFirstSelectedIndex() + (mScrollbar->getPageSize() - 1));
+ scrollToShowSelected();
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ }
+ break;
+ case KEY_HOME:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectFirstItem();
+ scrollToShowSelected();
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ }
+ break;
+ case KEY_END:
+ if (mAllowKeyboardMovement || hasFocus())
+ {
+ selectNthItem(getItemCount() - 1);
+ scrollToShowSelected();
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ handled = TRUE;
+ }
+ break;
+ case KEY_RETURN:
+ // JC - Special case: Only claim to have handled it
+ // if we're the special non-commit-on-move
+ // type. AND we are visible
+ if (!mCommitOnKeyboardMovement && mask == MASK_NONE && getVisible())
+ {
+ onCommit();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+ case KEY_BACKSPACE:
+ mSearchTimer.reset();
+ if (mSearchString.size())
+ {
+ mSearchString.erase(mSearchString.size() - 1, 1);
+ }
+ if (mSearchString.empty())
+ {
+ if (getFirstSelected())
+ {
+ LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ cellp->highlightText(0);
+ }
+ }
+ }
+ else if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString), FALSE))
+ {
+ // update search string only on successful match
+ mSearchTimer.reset();
+
+ // highlight current search on matching item
+ LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ cellp->highlightText(mSearchString.size());
+ }
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ // TODO: multiple: shift-up, shift-down, shift-home, shift-end, select all
+ }
+
+ return handled;
+}
+
+BOOL LLScrollListCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ // perform incremental search based on keyboard input
+ if (mSearchTimer.getElapsedTimeF32() > LLUI::sConfigGroup->getF32("TypeAheadTimeout"))
+ {
+ mSearchString.clear();
+ }
+
+ // type ahead search is case insensitive
+ uni_char = LLStringOps::toLower((llwchar)uni_char);
+
+ if (selectSimpleItemByPrefix(wstring_to_utf8str(mSearchString + (llwchar)uni_char), FALSE))
+ {
+ // update search string only on successful match
+ mSearchString += uni_char;
+ mSearchTimer.reset();
+
+ // highlight current search on matching item
+ LLScrollListCell* cellp = getFirstSelected()->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ cellp->highlightText(mSearchString.size());
+ }
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+ }
+ // handle iterating over same starting character
+ else if (isRepeatedChars(mSearchString + (llwchar)uni_char) && !mItemList.empty())
+ {
+ // start from last selected item, in case we previously had a successful match against
+ // duplicated characters ('AA' matches 'Aaron')
+ item_list::iterator start_iter = mItemList.begin();
+ S32 first_selected = getFirstSelectedIndex();
+
+ // if we have a selection (> -1) then point iterator at the selected item
+ if (first_selected > 0)
+ {
+ // point iterator to first selected item
+ start_iter += first_selected;
+ }
+
+ // start search at first item after current selection
+ item_list::iterator iter = start_iter;
+ ++iter;
+ if (iter == mItemList.end())
+ {
+ iter = mItemList.begin();
+ }
+
+ // loop around once, back to previous selection
+ while(iter != start_iter)
+ {
+ LLScrollListItem* item = *iter;
+
+ LLScrollListCell* cellp = item->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ // Only select enabled items with matching first characters
+ LLWString item_label = utf8str_to_wstring(cellp->getText());
+ if (item->getEnabled() && LLStringOps::toLower(item_label[0]) == uni_char)
+ {
+ selectItem(item);
+ cellp->highlightText(1);
+ mSearchTimer.reset();
+
+ if (mCommitOnKeyboardMovement
+ && !mCommitOnSelectionChange)
+ {
+ onCommit();
+ }
+
+ break;
+ }
+ }
+
+ ++iter;
+ if (iter == mItemList.end())
+ {
+ iter = mItemList.begin();
+ }
+ }
+ }
+
+ // make sure selected item is on screen
+ scrollToShowSelected();
+ return TRUE;
+}
+
+
+void LLScrollListCtrl::reportInvalidInput()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+BOOL LLScrollListCtrl::isRepeatedChars(const LLWString& string) const
+{
+ if (string.empty())
+ {
+ return FALSE;
+ }
+
+ llwchar first_char = string[0];
+
+ for (U32 i = 0; i < string.size(); i++)
+ {
+ if (string[i] != first_char)
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLScrollListCtrl::selectItem(LLScrollListItem* itemp, BOOL select_single_item)
+{
+ if (!itemp) return;
+
+ if (!itemp->getSelected())
+ {
+ if (mLastSelected)
+ {
+ LLScrollListCell* cellp = mLastSelected->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ cellp->highlightText(0);
+ }
+ }
+ if (select_single_item)
+ {
+ deselectAllItems(TRUE);
+ }
+ itemp->setSelected(TRUE);
+ mLastSelected = itemp;
+ mSelectionChanged = TRUE;
+ }
+}
+
+void LLScrollListCtrl::deselectItem(LLScrollListItem* itemp)
+{
+ if (!itemp) return;
+
+ if (itemp->getSelected())
+ {
+ if (mLastSelected == itemp)
+ {
+ mLastSelected = NULL;
+ }
+
+ itemp->setSelected(FALSE);
+ LLScrollListCell* cellp = itemp->getColumn(mSearchColumn);
+ if (cellp)
+ {
+ cellp->highlightText(0);
+ }
+ mSelectionChanged = TRUE;
+ }
+}
+
+void LLScrollListCtrl::commitIfChanged()
+{
+ if (mSelectionChanged)
+ {
+ mSelectionChanged = FALSE;
+ onCommit();
+ }
+}
+
+// Called by scrollbar
+//static
+void LLScrollListCtrl::onScrollChange( S32 new_pos, LLScrollbar* scrollbar, void* userdata )
+{
+ LLScrollListCtrl* self = (LLScrollListCtrl*) userdata;
+ self->mScrollLines = new_pos;
+}
+
+
+// First column is column 0
+void LLScrollListCtrl::sortByColumn(U32 column, BOOL ascending)
+{
+ LLScrollListCtrl::sSortColumn = column;
+ LLScrollListCtrl::sSortAscending = ascending;
+ std::sort(mItemList.begin(), mItemList.end(), SortScrollListItem(sSortColumn, sSortAscending));
+}
+
+void LLScrollListCtrl::sortByColumn(LLString name, BOOL ascending)
+{
+ std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(name);
+ if (itor != mColumns.end())
+ {
+ sortByColumn((*itor).second.mIndex, ascending);
+ }
+}
+
+S32 LLScrollListCtrl::getScrollPos()
+{
+ return mScrollbar->getDocPos();
+}
+
+
+void LLScrollListCtrl::setScrollPos( S32 pos )
+{
+ mScrollbar->setDocPos( pos );
+
+ onScrollChange(mScrollbar->getDocPos(), mScrollbar, this);
+}
+
+
+void LLScrollListCtrl::scrollToShowSelected()
+{
+ S32 index = getFirstSelectedIndex();
+ if (index < 0)
+ {
+ return;
+ }
+
+ LLScrollListItem* item = mItemList[index];
+ if (!item)
+ {
+ // I don't THINK this should ever happen.
+ return;
+ }
+
+ S32 lowest = mScrollLines;
+ S32 highest = mScrollLines + mPageLines;
+
+ if (index < lowest)
+ {
+ // need to scroll to show item
+ setScrollPos(index);
+ }
+ else if (highest <= index)
+ {
+ setScrollPos(index - mPageLines + 1);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLScrollListCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ // Attributes
+
+ node->createChild("multi_select", TRUE)->setBoolValue(mAllowMultipleSelection);
+
+ node->createChild("draw_border", TRUE)->setBoolValue((mBorder != NULL));
+
+ node->createChild("draw_heading", TRUE)->setBoolValue(mDisplayColumnButtons);
+
+ node->createChild("background_visible", TRUE)->setBoolValue(mBackgroundVisible);
+
+ node->createChild("draw_stripes", TRUE)->setBoolValue(mDrawStripes);
+
+ node->createChild("column_padding", TRUE)->setIntValue(mColumnPadding);
+
+ addColorXML(node, mBgWriteableColor, "bg_writeable_color", "ScrollBgWriteableColor");
+ addColorXML(node, mBgReadOnlyColor, "bg_read_only_color", "ScrollBgReadOnlyColor");
+ addColorXML(node, mBgSelectedColor, "bg_selected_color", "ScrollSelectedBGColor");
+ addColorXML(node, mBgStripeColor, "bg_stripe_color", "ScrollBGStripeColor");
+ addColorXML(node, mFgSelectedColor, "fg_selected_color", "ScrollSelectedFGColor");
+ addColorXML(node, mFgUnselectedColor, "fg_unselected_color", "ScrollUnselectedColor");
+ addColorXML(node, mFgDisabledColor, "fg_disable_color", "ScrollDisabledColor");
+ addColorXML(node, mHighlightedColor, "highlighted_color", "ScrollHighlightedColor");
+
+ // Contents
+
+ std::map<LLString, LLScrollListColumn>::const_iterator itor;
+ std::vector<const LLScrollListColumn*> sorted_list;
+ sorted_list.resize(mColumns.size());
+ for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
+ {
+ sorted_list[itor->second.mIndex] = &itor->second;
+ }
+
+ std::vector<const LLScrollListColumn*>::iterator itor2;
+ for (itor2 = sorted_list.begin(); itor2 != sorted_list.end(); ++itor2)
+ {
+ LLXMLNodePtr child_node = node->createChild("column", FALSE);
+ const LLScrollListColumn *column = *itor2;
+
+ child_node->createChild("name", TRUE)->setStringValue(column->mName);
+ child_node->createChild("label", TRUE)->setStringValue(column->mLabel);
+ child_node->createChild("width", TRUE)->setIntValue(column->mWidth);
+ }
+
+ return node;
+}
+
+void LLScrollListCtrl::setScrollListParameters(LLXMLNodePtr node)
+{
+ // James: This is not a good way to do colors. We need a central "UI style"
+ // manager that sets the colors for ALL scroll lists, buttons, etc.
+
+ LLColor4 color;
+ if(node->hasAttribute("fg_unselected_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"fg_unselected_color", color);
+ setFgUnselectedColor(color);
+ }
+ if(node->hasAttribute("fg_selected_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"fg_selected_color", color);
+ setFgSelectedColor(color);
+ }
+ if(node->hasAttribute("bg_selected_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"bg_selected_color", color);
+ setBgSelectedColor(color);
+ }
+ if(node->hasAttribute("fg_disable_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"fg_disable_color", color);
+ setFgDisableColor(color);
+ }
+ if(node->hasAttribute("bg_writeable_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color);
+ setBgWriteableColor(color);
+ }
+ if(node->hasAttribute("bg_read_only_color"))
+ {
+ LLUICtrlFactory::getAttributeColor(node,"bg_read_only_color", color);
+ setReadOnlyBgColor(color);
+ }
+ if (LLUICtrlFactory::getAttributeColor(node,"bg_stripe_color", color))
+ {
+ setBgStripeColor(color);
+ }
+ if (LLUICtrlFactory::getAttributeColor(node,"highlighted_color", color))
+ {
+ setHighlightedColor(color);
+ }
+
+ if(node->hasAttribute("background_visible"))
+ {
+ BOOL background_visible;
+ node->getAttributeBOOL("background_visible", background_visible);
+ setBackgroundVisible(background_visible);
+ }
+
+ if(node->hasAttribute("draw_stripes"))
+ {
+ BOOL draw_stripes;
+ node->getAttributeBOOL("draw_stripes", draw_stripes);
+ setDrawStripes(draw_stripes);
+ }
+
+ if(node->hasAttribute("column_padding"))
+ {
+ S32 column_padding;
+ node->getAttributeS32("column_padding", column_padding);
+ setColumnPadding(column_padding);
+ }
+}
+
+// static
+LLView* LLScrollListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("scroll_list");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL multi_select = FALSE;
+ node->getAttributeBOOL("multi_select", multi_select);
+
+ BOOL draw_border = TRUE;
+ node->getAttributeBOOL("draw_border", draw_border);
+
+ BOOL draw_heading = FALSE;
+ node->getAttributeBOOL("draw_heading", draw_heading);
+
+ BOOL collapse_empty_columns = FALSE;
+ node->getAttributeBOOL("collapse_empty_columns", collapse_empty_columns);
+
+ S32 search_column = 0;
+ node->getAttributeS32("search_column", search_column);
+
+ LLUICtrlCallback callback = NULL;
+
+ LLScrollListCtrl* scroll_list = new LLScrollListCtrl(
+ name,
+ rect,
+ callback,
+ NULL,
+ multi_select,
+ draw_border);
+
+ scroll_list->setDisplayHeading(draw_heading);
+ if (node->hasAttribute("heading_height"))
+ {
+ S32 heading_height;
+ node->getAttributeS32("heading_height", heading_height);
+ scroll_list->setHeadingHeight(heading_height);
+ }
+ if (node->hasAttribute("heading_font"))
+ {
+ LLString heading_font("");
+ node->getAttributeString("heading_font", heading_font);
+ LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str());
+ scroll_list->setHeadingFont(gl_font);
+ }
+ scroll_list->setCollapseEmptyColumns(collapse_empty_columns);
+
+ scroll_list->setScrollListParameters(node);
+
+ scroll_list->initFromXML(node, parent);
+
+ scroll_list->setSearchColumn(search_column);
+
+ LLSD columns;
+ S32 index = 0;
+ LLXMLNodePtr child;
+ S32 total_static = 0, num_dynamic = 0;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("column"))
+ {
+ LLString labelname("");
+ child->getAttributeString("label", labelname);
+
+ LLString columnname(labelname);
+ child->getAttributeString("name", columnname);
+
+ LLString sortname(columnname);
+ child->getAttributeString("sort", sortname);
+
+ LLString imagename;
+ child->getAttributeString("image", imagename);
+
+ BOOL columndynamicwidth = FALSE;
+ child->getAttributeBOOL("dynamicwidth", columndynamicwidth);
+
+ S32 columnwidth = -1;
+ child->getAttributeS32("width", columnwidth);
+
+ if(!columndynamicwidth) total_static += columnwidth;
+ else ++num_dynamic;
+
+ F32 columnrelwidth = 0.f;
+ child->getAttributeF32("relwidth", columnrelwidth);
+
+
+ columns[index]["name"] = columnname;
+ columns[index]["sort"] = sortname;
+ columns[index]["image"] = imagename;
+ columns[index]["label"] = labelname;
+ columns[index]["width"] = columnwidth;
+ columns[index]["relwidth"] = columnrelwidth;
+ columns[index]["dynamicwidth"] = columndynamicwidth;
+ index++;
+ }
+ }
+ scroll_list->setNumDynamicColumns(num_dynamic);
+ scroll_list->setTotalStaticColumnWidth(total_static);
+ scroll_list->setColumnHeadings(columns);
+
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("row"))
+ {
+ LLUUID id;
+ child->getAttributeUUID("id", id);
+
+ LLSD row;
+
+ row["id"] = id;
+
+ S32 column_idx = 0;
+ LLXMLNodePtr row_child;
+ for (row_child = child->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling())
+ {
+ if (row_child->hasName("column"))
+ {
+ LLString value = row_child->getTextContents();
+
+ LLString columnname("");
+ row_child->getAttributeString("name", columnname);
+
+ LLString font("");
+ row_child->getAttributeString("font", font);
+
+ LLString font_style("");
+ row_child->getAttributeString("font-style", font_style);
+
+ row["columns"][column_idx]["column"] = columnname;
+ row["columns"][column_idx]["value"] = value;
+ row["columns"][column_idx]["font"] = font;
+ row["columns"][column_idx]["font-style"] = font_style;
+ column_idx++;
+ }
+ }
+ scroll_list->addElement(row);
+ }
+ }
+
+ LLString contents = node->getTextContents();
+ if (!contents.empty())
+ {
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\t\n");
+ tokenizer tokens(contents, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ while(token_iter != tokens.end())
+ {
+ const char* line = token_iter->c_str();
+ scroll_list->addSimpleItem(line);
+ ++token_iter;
+ }
+ }
+
+ return scroll_list;
+}
+
+// LLEditMenuHandler functions
+
+// virtual
+void LLScrollListCtrl::copy()
+{
+ LLString buffer;
+
+ std::vector<LLScrollListItem*> items = getAllSelected();
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = items.begin(); itor != items.end(); ++itor)
+ {
+ buffer += (*itor)->getContentsCSV() + "\n";
+ }
+ gClipboard.copyFromSubstring(utf8str_to_wstring(buffer), 0, buffer.length());
+}
+
+// virtual
+BOOL LLScrollListCtrl::canCopy()
+{
+ return (getFirstSelected() != NULL);
+}
+
+// virtual
+void LLScrollListCtrl::cut()
+{
+ copy();
+ doDelete();
+}
+
+// virtual
+BOOL LLScrollListCtrl::canCut()
+{
+ return canCopy() && canDoDelete();
+}
+
+// virtual
+void LLScrollListCtrl::doDelete()
+{
+ // Not yet implemented
+}
+
+// virtual
+BOOL LLScrollListCtrl::canDoDelete()
+{
+ // Not yet implemented
+ return FALSE;
+}
+
+// virtual
+void LLScrollListCtrl::selectAll()
+{
+ // Deselects all other items
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem *itemp = *iter;
+ if( itemp->getEnabled() )
+ {
+ selectItem(itemp, FALSE);
+ }
+ }
+
+ if (mCommitOnSelectionChange)
+ {
+ commitIfChanged();
+ }
+}
+
+// virtual
+BOOL LLScrollListCtrl::canSelectAll()
+{
+ return getCanSelect() && mAllowMultipleSelection && !(mMaxSelectable > 0 && mItemList.size() > mMaxSelectable);
+}
+
+// virtual
+void LLScrollListCtrl::deselect()
+{
+ deselectAllItems();
+}
+
+// virtual
+BOOL LLScrollListCtrl::canDeselect()
+{
+ return getCanSelect();
+}
+
+void LLScrollListCtrl::addColumn(const LLSD& column, EAddPosition pos)
+{
+ LLString name = column["name"].asString();
+ if (mColumns.size() == 0)
+ {
+ mDefaultColumn = 0;
+ }
+ if (mColumns.find(name) == mColumns.end())
+ {
+ // Add column
+ mColumns[name] = LLScrollListColumn(column);
+ LLScrollListColumn* new_column = &mColumns[name];
+ new_column->mParentCtrl = this;
+ new_column->mIndex = mColumns.size()-1;
+
+ // Add button
+ if (new_column->mWidth > 0 || new_column->mRelWidth > 0 || new_column->mDynamicWidth)
+ {
+ if (new_column->mRelWidth >= 0)
+ {
+ new_column->mWidth = (S32)llround(new_column->mRelWidth*mItemListRect.getWidth());
+ }
+ else if(new_column->mDynamicWidth)
+ {
+ new_column->mWidth = (mItemListRect.getWidth() - mTotalStaticColumnWidth) / mNumDynamicWidthColumns;
+ }
+ S32 top = mItemListRect.mTop;
+ S32 left = mItemListRect.mLeft;
+ {
+ std::map<LLString, LLScrollListColumn>::iterator itor;
+ for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
+ {
+ if (itor->second.mIndex < new_column->mIndex &&
+ itor->second.mWidth > 0)
+ {
+ left += itor->second.mWidth + mColumnPadding;
+ }
+ }
+ }
+ LLString button_name = "btn_" + name;
+ S32 right = left+new_column->mWidth;
+ if (new_column->mIndex != (S32)mColumns.size()-1)
+ {
+ right += mColumnPadding;
+ }
+ LLRect temp_rect = LLRect(left,top+mHeadingHeight,right,top);
+ new_column->mButton = new LLSquareButton(button_name, temp_rect, "", mHeadingFont, "", onClickColumn, NULL);
+ if(column["image"].asString() != "")
+ {
+ //new_column->mButton->setScaleImage(false);
+ new_column->mButton->setImageSelected(column["image"].asString());
+ new_column->mButton->setImageUnselected(column["image"].asString());
+ }
+ else
+ {
+ new_column->mButton->setLabelSelected(new_column->mLabel);
+ new_column->mButton->setLabelUnselected(new_column->mLabel);
+ }
+ //RN: although it might be useful to change sort order with the keyboard,
+ // mixing tab stops on child items along with the parent item is not supported yet
+ new_column->mButton->setTabStop(FALSE);
+ addChild(new_column->mButton);
+ new_column->mButton->setVisible(mDisplayColumnButtons);
+
+
+ // Move scroll to front
+ removeChild(mScrollbar);
+ addChild(mScrollbar);
+
+ new_column->mButton->setCallbackUserData(new_column);
+ }
+ }
+ updateColumns();
+}
+
+// static
+void LLScrollListCtrl::onClickColumn(void *userdata)
+{
+ LLScrollListColumn *info = (LLScrollListColumn*)userdata;
+ if (!info) return;
+
+ U32 column_index = info->mIndex;
+
+ LLScrollListColumn* column = info->mParentCtrl->mColumnsIndexed[info->mIndex];
+ if (column->mSortingColumn != column->mName)
+ {
+ if (info->mParentCtrl->mColumns.find(column->mSortingColumn) != info->mParentCtrl->mColumns.end())
+ {
+ LLScrollListColumn& info_redir = info->mParentCtrl->mColumns[column->mSortingColumn];
+ column_index = info_redir.mIndex;
+ }
+ }
+
+ // TomY TODO: shouldn't these be non-static members?
+ bool ascending = true;
+ if (column_index == LLScrollListCtrl::sSortColumn)
+ {
+ ascending = !LLScrollListCtrl::sSortAscending;
+ }
+
+ info->mParentCtrl->sortByColumn(column_index, ascending);
+}
+
+void LLScrollListCtrl::clearColumns()
+{
+ std::map<LLString, LLScrollListColumn>::iterator itor;
+ for (itor = mColumns.begin(); itor != mColumns.end(); ++itor)
+ {
+ LLButton *button = itor->second.mButton;
+ if (button)
+ {
+ removeChild(button);
+ delete button;
+ }
+ }
+ mColumns.clear();
+}
+
+void LLScrollListCtrl::setColumnLabel(const LLString& column, const LLString& label)
+{
+ std::map<LLString, LLScrollListColumn>::iterator itor = mColumns.find(column);
+ if (itor != mColumns.end())
+ {
+ itor->second.mLabel = label;
+ if (itor->second.mButton)
+ {
+ itor->second.mButton->setLabelSelected(label);
+ itor->second.mButton->setLabelUnselected(label);
+ }
+ }
+}
+
+void LLScrollListCtrl::setColumnHeadings(LLSD headings)
+{
+ mColumns.clear();
+ LLSD::array_const_iterator itor;
+ for (itor = headings.beginArray(); itor != headings.endArray(); ++itor)
+ {
+ addColumn(*itor);
+ }
+}
+
+LLScrollListItem* LLScrollListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata)
+{
+ // ID
+ LLSD id = value["id"];
+
+ LLScrollListItem *new_item = new LLScrollListItem(id, userdata);
+ if (value.has("enabled"))
+ {
+ new_item->setEnabled( value["enabled"].asBoolean() );
+ }
+
+ new_item->setNumColumns(mColumns.size());
+
+ // Add any columns we don't already have
+ LLSD columns = value["columns"];
+ LLSD::array_const_iterator itor;
+ for (itor = columns.beginArray(); itor != columns.endArray(); ++itor)
+ {
+ LLString column = (*itor)["column"].asString();
+
+ if (mColumns.size() == 0)
+ {
+ mDefaultColumn = 0;
+ }
+ std::map<LLString, LLScrollListColumn>::iterator column_itor = mColumns.find(column);
+ if (column_itor == mColumns.end())
+ {
+ LLSD new_column;
+ new_column["name"] = column;
+ new_column["label"] = column;
+ new_column["width"] = 0;
+ addColumn(new_column);
+ column_itor = mColumns.find(column);
+ new_item->setNumColumns(mColumns.size());
+ }
+
+ S32 index = column_itor->second.mIndex;
+ S32 width = column_itor->second.mWidth;
+
+ LLSD value = (*itor)["value"];
+ LLString fontname = (*itor)["font"].asString();
+ LLString fontstyle = (*itor)["font-style"].asString();
+ LLString type = (*itor)["type"].asString();
+
+ const LLFontGL *font = gResMgr->getRes(fontname);
+ if (!font)
+ {
+ font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ }
+ U8 font_style = LLFontGL::getStyleFromString(fontstyle);
+
+ if (type == "icon")
+ {
+ LLUUID image_id = value.asUUID();
+ LLImageGL* icon = LLUI::sImageProvider->getUIImageByID(image_id);
+ new_item->setColumn(index, new LLScrollListIcon(icon, width, image_id));
+ }
+ else if (type == "checkbox")
+ {
+ LLCheckBoxCtrl* ctrl = new LLCheckBoxCtrl(value.asString(),
+ LLRect(0, 0, width, width), "label");
+ new_item->setColumn(index, new LLScrollListCheck(ctrl,width));
+ }
+ else
+ {
+ new_item->setColumn(index, new LLScrollListText(value.asString(), font, width, font_style));
+ }
+ }
+
+ S32 num_columns = mColumns.size();
+ for (S32 column = 0; column < num_columns; ++column)
+ {
+ if (new_item->getColumn(column) == NULL)
+ {
+ LLScrollListColumn* column_ptr = mColumnsIndexed[column];
+ new_item->setColumn(column, new LLScrollListText("", gResMgr->getRes( LLFONT_SANSSERIF_SMALL ), column_ptr->mWidth, LLFontGL::NORMAL));
+ }
+ }
+
+ addItem(new_item, pos);
+
+ return new_item;
+}
+
+LLScrollListItem* LLScrollListCtrl::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
+{
+ LLSD entry_id = id;
+
+ if (id.isUndefined())
+ {
+ entry_id = value;
+ }
+
+ LLScrollListItem *new_item = new LLScrollListItem(entry_id);
+
+ const LLFontGL *font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+ new_item->addColumn(value, font, getRect().getWidth());
+
+ addItem(new_item, pos);
+ return new_item;
+}
+
+void LLScrollListCtrl::setValue(const LLSD& value )
+{
+ LLSD::array_const_iterator itor;
+ for (itor = value.beginArray(); itor != value.endArray(); ++itor)
+ {
+ addElement(*itor);
+ }
+}
+
+LLSD LLScrollListCtrl::getValue() const
+{
+ LLScrollListItem *item = getFirstSelected();
+ if (!item) return LLSD();
+ return item->getValue();
+}
+
+BOOL LLScrollListCtrl::operateOnSelection(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ deleteSelectedItems();
+ return TRUE;
+ }
+ else if (op == OP_DESELECT)
+ {
+ deselectAllItems();
+ }
+ return FALSE;
+}
+
+BOOL LLScrollListCtrl::operateOnAll(EOperation op)
+{
+ if (op == OP_DELETE)
+ {
+ clearRows();
+ return TRUE;
+ }
+ else if (op == OP_DESELECT)
+ {
+ deselectAllItems();
+ }
+ else if (op == OP_SELECT)
+ {
+ selectAll();
+ }
+ return FALSE;
+}
+//virtual
+void LLScrollListCtrl::setFocus(BOOL b)
+{
+ mSearchString.clear();
+ // for tabbing into pristine scroll lists (Finder)
+ if (!getFirstSelected())
+ {
+ selectFirstItem();
+ onCommit();
+ }
+ LLUICtrl::setFocus(b);
+}
+//virtual
+void LLScrollListCtrl::onFocusLost()
+{
+ if (mIsPopup)
+ {
+ if (getParent())
+ {
+ getParent()->onFocusLost();
+ }
+ }
+}
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
new file mode 100644
index 0000000000..426e817215
--- /dev/null
+++ b/indra/llui/llscrolllistctrl.h
@@ -0,0 +1,527 @@
+/**
+ * @file llscrolllistctrl.h
+ * @brief LLScrollListCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_SCROLLLISTCTRL_H
+#define LL_SCROLLLISTCTRL_H
+
+#include <vector>
+#include <deque>
+
+#include "lluictrl.h"
+#include "llctrlselectioninterface.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llstring.h"
+#include "llimagegl.h"
+#include "lleditmenuhandler.h"
+#include "llviewborder.h"
+#include "llframetimer.h"
+#include "llcheckboxctrl.h"
+
+class LLScrollbar;
+class LLScrollListCtrl;
+
+class LLScrollListCell
+{
+public:
+ virtual ~LLScrollListCell() {};
+ virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const = 0; // truncate to given width, if possible
+ virtual S32 getWidth() const = 0;
+ virtual S32 getHeight() const = 0;
+ virtual const LLString& getText() const { return LLString::null; }
+ virtual const LLString& getTextLower() const { return LLString::null; }
+ virtual const BOOL getVisible() const { return TRUE; }
+ virtual void setWidth(S32 width) = 0;
+ virtual void highlightText(S32 num_chars) {}
+
+ virtual BOOL handleClick() { return FALSE; }
+ virtual void setEnabled(BOOL enable) { }
+};
+
+class LLScrollListText : public LLScrollListCell
+{
+ static U32 sCount;
+public:
+ LLScrollListText( const LLString& text, const LLFontGL* font, S32 width = 0, U8 font_style = LLFontGL::NORMAL, LLColor4& color = LLColor4::black, BOOL use_color = FALSE, BOOL visible = TRUE);
+ /*virtual*/ ~LLScrollListText();
+
+ virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const;
+ virtual S32 getWidth() const { return mWidth; }
+ virtual void setWidth(S32 width) { mWidth = width; }
+ virtual S32 getHeight() const { return llround(mFont->getLineHeight()); }
+ virtual const LLString& getText() const { return mText.getString(); }
+ virtual const BOOL getVisible() const { return mVisible; }
+ virtual void highlightText(S32 num_chars) {mHighlightChars = num_chars;}
+ void setText(const LLString& text);
+
+private:
+ LLUIString mText;
+ const LLFontGL* mFont;
+ LLColor4* mColor;
+ const U8 mFontStyle;
+ S32 mWidth;
+ S32 mEllipsisWidth; // in pixels, of "..."
+ BOOL mVisible;
+ S32 mHighlightChars;
+
+ LLPointer<LLImageGL> mRoundedRectImage;
+};
+
+class LLScrollListIcon : public LLScrollListCell
+{
+public:
+ LLScrollListIcon( LLImageGL* icon, S32 width = 0, LLUUID image_id = LLUUID::null);
+ /*virtual*/ ~LLScrollListIcon();
+ virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const { gl_draw_image(0, 0, mIcon); }
+ virtual S32 getWidth() const { return mWidth; }
+ virtual S32 getHeight() const { return mIcon->getHeight(); }
+ virtual const LLString& getText() const { return mImageUUID; }
+ virtual const LLString& getTextLower() const { return mImageUUID; }
+ virtual void setWidth(S32 width) { mWidth = width; }
+
+private:
+ LLPointer<LLImageGL> mIcon;
+ LLString mImageUUID;
+ S32 mWidth;
+};
+
+class LLScrollListCheck : public LLScrollListCell
+{
+public:
+ LLScrollListCheck( LLCheckBoxCtrl* check_box, S32 width = 0);
+ /*virtual*/ ~LLScrollListCheck();
+ virtual void drawToWidth(S32 width, const LLColor4& color, const LLColor4& highlight_color) const;
+ virtual S32 getWidth() const { return mWidth; }
+ virtual S32 getHeight() const { return 0; }
+ virtual void setWidth(S32 width) { mWidth = width; }
+
+ virtual BOOL handleClick();
+ virtual void setEnabled(BOOL enable) { if (mCheckBox) mCheckBox->setEnabled(enable); }
+
+ LLCheckBoxCtrl* getCheckBox() { return mCheckBox; }
+
+private:
+ LLCheckBoxCtrl* mCheckBox;
+ S32 mWidth;
+};
+
+class LLScrollListColumn
+{
+public:
+ // Default constructor
+ LLScrollListColumn() : mName(""), mSortingColumn(""), mLabel(""), mWidth(-1), mRelWidth(-1.0), mDynamicWidth(FALSE), mIndex(-1), mParentCtrl(NULL), mButton(NULL) { }
+
+ LLScrollListColumn(LLString name, LLString label, S32 width, F32 relwidth)
+ : mName(name), mSortingColumn(name), mLabel(label), mWidth(width), mRelWidth(relwidth), mDynamicWidth(FALSE), mIndex(-1), mParentCtrl(NULL), mButton(NULL) { }
+
+ LLScrollListColumn(const LLSD &sd)
+ {
+ mName = sd.get("name").asString();
+ mSortingColumn = mName;
+ if (sd.has("sort"))
+ {
+ mSortingColumn = sd.get("sort").asString();
+ }
+ mLabel = sd.get("label").asString();
+ if (sd.has("relwidth") && (F32)sd.get("relwidth").asReal() > 0)
+ {
+ mRelWidth = (F32)sd.get("relwidth").asReal();
+ if (mRelWidth < 0) mRelWidth = 0;
+ if (mRelWidth > 1) mRelWidth = 1;
+ mDynamicWidth = FALSE;
+ mWidth = 0;
+ }
+ else if(sd.has("dynamicwidth") && (BOOL)sd.get("dynamicwidth").asBoolean() == TRUE)
+ {
+ mDynamicWidth = TRUE;
+ mRelWidth = -1;
+ mWidth = 0;
+ }
+ else
+ {
+ mWidth = sd.get("width").asInteger();
+ mDynamicWidth = FALSE;
+ mRelWidth = -1;
+ }
+ mIndex = -1;
+ mParentCtrl = NULL;
+ mButton = NULL;
+ }
+
+ LLString mName;
+ LLString mSortingColumn;
+ LLString mLabel;
+ S32 mWidth;
+ F32 mRelWidth;
+ BOOL mDynamicWidth;
+ S32 mIndex;
+ LLScrollListCtrl *mParentCtrl;
+ LLButton *mButton;
+};
+
+class LLScrollListItem
+{
+public:
+ LLScrollListItem( BOOL enabled = TRUE, void* userdata = NULL, const LLUUID& uuid = LLUUID::null )
+ : mSelected(FALSE), mEnabled( enabled ), mUserdata( userdata ), mItemValue( uuid ), mColumns() {}
+ LLScrollListItem( LLSD item_value, void* userdata = NULL )
+ : mSelected(FALSE), mEnabled( TRUE ), mUserdata( userdata ), mItemValue( item_value ), mColumns() {}
+
+ virtual ~LLScrollListItem();
+
+ void setSelected( BOOL b ) { mSelected = b; }
+ BOOL getSelected() const { return mSelected; }
+
+ void setEnabled( BOOL b );
+ BOOL getEnabled() const { return mEnabled; }
+
+ void setUserdata( void* userdata ) { mUserdata = userdata; }
+ void* getUserdata() const { return mUserdata; }
+
+ LLUUID getUUID() const { return mItemValue.asUUID(); }
+ LLSD getValue() const { return mItemValue; }
+
+ // If width = 0, just use the width of the text. Otherwise override with
+ // specified width in pixels.
+ void addColumn( const LLString& text, const LLFontGL* font, S32 width = 0 , U8 font_style = LLFontGL::NORMAL, BOOL visible = TRUE)
+ { mColumns.push_back( new LLScrollListText(text, font, width, font_style, LLColor4::black, FALSE, visible) ); }
+
+ void addColumn( LLImageGL* icon, S32 width = 0 )
+ { mColumns.push_back( new LLScrollListIcon(icon, width) ); }
+
+ void addColumn( LLCheckBoxCtrl* check, S32 width = 0 )
+ { mColumns.push_back( new LLScrollListCheck(check,width) ); }
+
+ void setNumColumns(S32 columns);
+
+ void setColumn( S32 column, LLScrollListCell *cell );
+
+ S32 getNumColumns() const { return mColumns.size(); }
+
+ LLScrollListCell *getColumn(const S32 i) const { if (i < (S32)mColumns.size()) { return mColumns[i]; } return NULL; }
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+
+ LLString getContentsCSV();
+
+private:
+ BOOL mSelected;
+ BOOL mEnabled;
+ void* mUserdata;
+ LLSD mItemValue;
+ std::vector<LLScrollListCell *> mColumns;
+};
+
+
+class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
+ public LLCtrlListInterface, public LLCtrlScrollInterface
+{
+public:
+ LLScrollListCtrl(
+ const LLString& name,
+ const LLRect& rect,
+ void (*commit_callback)(LLUICtrl*, void*),
+ void* callback_userdata,
+ BOOL allow_multiple_selection,
+ BOOL draw_border = TRUE);
+
+ virtual ~LLScrollListCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_SCROLL_LIST; }
+ virtual LLString getWidgetTag() const { return LL_SCROLL_LIST_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ void setScrollListParameters(LLXMLNodePtr node);
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ S32 isEmpty() const;
+
+ void deleteAllItems() { clearRows(); }
+
+ // Sets an array of column descriptors
+ void setColumnHeadings(LLSD headings);
+ // Numerical based sort by column function (used by LLComboBox)
+ void sortByColumn(U32 column, BOOL ascending);
+
+ // LLCtrlListInterface functions
+ virtual S32 getItemCount() const;
+ // Adds a single column descriptor: ["name" : string, "label" : string, "width" : integer, "relwidth" : integer ]
+ virtual void addColumn(const LLSD& column, EAddPosition pos = ADD_BOTTOM);
+ virtual void clearColumns();
+ virtual void setColumnLabel(const LLString& column, const LLString& label);
+ // Adds a single element, from an array of:
+ // "columns" => [ "column" => column name, "value" => value, "type" => type, "font" => font, "font-style" => style ], "id" => uuid
+ // Creates missing columns automatically.
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+ // Simple add element. Takes a single array of:
+ // [ "value" => value, "font" => font, "font-style" => style ]
+ virtual LLScrollListItem* addSimpleElement(const LLString& value, EAddPosition pos = ADD_BOTTOM, const LLSD& id = LLSD());
+ virtual void clearRows(); // clears all elements
+ virtual void sortByColumn(LLString name, BOOL ascending);
+
+ // These functions take and return an array of arrays of elements, as above
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+
+ LLCtrlSelectionInterface* getSelectionInterface() { return (LLCtrlSelectionInterface*)this; }
+ LLCtrlListInterface* getListInterface() { return (LLCtrlListInterface*)this; }
+ LLCtrlScrollInterface* getScrollInterface() { return (LLCtrlScrollInterface*)this; }
+
+ // DEPRECATED: Use setSelectedByValue() below.
+ BOOL setCurrentByID( const LLUUID& id ) { return selectByID(id); }
+ virtual LLUUID getCurrentID() { return getStringUUIDSelectedItem(); }
+
+ BOOL operateOnSelection(EOperation op);
+ BOOL operateOnAll(EOperation op);
+
+ // returns FALSE if unable to set the max count so low
+ BOOL setMaxItemCount(S32 max_count);
+
+ BOOL selectByID( const LLUUID& id ); // FALSE if item not found
+
+ // Match item by value.asString(), which should work for string, integer, uuid.
+ // Returns FALSE if not found.
+ BOOL setSelectedByValue(LLSD value, BOOL selected);
+
+ virtual BOOL isSelected(LLSD value);
+
+ BOOL selectFirstItem();
+ BOOL selectNthItem( S32 index );
+
+ void deleteSingleItem( S32 index ) ;
+ void deleteSelectedItems();
+ void deselectAllItems(BOOL no_commit_on_change = FALSE); // by default, go ahead and commit on selection change
+
+ void highlightNthItem( S32 index );
+ void setDoubleClickCallback( void (*cb)(void*) ) { mOnDoubleClickCallback = cb; }
+ void setMaxiumumSelectCallback( void (*cb)(void*) ) { mOnMaximumSelectCallback = cb; }
+
+ void swapWithNext(S32 index);
+ void swapWithPrevious(S32 index);
+
+ void setCanSelect(BOOL can_select) { mCanSelect = can_select; }
+ virtual BOOL getCanSelect() const { return mCanSelect; }
+
+ S32 getItemIndex( LLScrollListItem* item );
+ S32 getItemIndex( LLUUID& item_id );
+
+ // "Simple" interface: use this when you're creating a list that contains only unique strings, only
+ // one of which can be selected at a time.
+ LLScrollListItem* addSimpleItem( const LLString& item_text, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE );
+ // Add an item with an associated LLSD
+ LLScrollListItem* addSimpleItem(const LLString& item_text, LLSD sd, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0 );
+
+ BOOL selectSimpleItem( const LLString& item, BOOL case_sensitive = TRUE ); // FALSE if item not found
+ BOOL selectSimpleItemByPrefix(const LLString& target, BOOL case_sensitive);
+ BOOL selectSimpleItemByPrefix(const LLWString& target, BOOL case_sensitive);
+ const LLString& getSimpleSelectedItem(S32 column = 0) const;
+ LLSD getSimpleSelectedValue();
+
+ // DEPRECATED: Use LLSD versions of addSimpleItem() and getSimpleSelectedValue().
+ // "StringUUID" interface: use this when you're creating a list that contains non-unique strings each of which
+ // has an associated, unique UUID, and only one of which can be selected at a time.
+ LLScrollListItem* addStringUUIDItem(const LLString& item_text, const LLUUID& id, EAddPosition pos = ADD_BOTTOM, BOOL enabled = TRUE, S32 column_width = 0);
+ LLUUID getStringUUIDSelectedItem();
+
+ // "Full" interface: use this when you're creating a list that has one or more of the following:
+ // * contains icons
+ // * contains multiple columns
+ // * allows multiple selection
+ // * has items that are not guarenteed to have unique names
+ // * has additional per-item data (e.g. a UUID or void* userdata)
+ //
+ // To add items using this approach, create new LLScrollListItems and LLScrollListCells. Add the
+ // cells (column entries) to each item, and add the item to the LLScrollListCtrl.
+ //
+ // The LLScrollListCtrl owns its items and is responsible for deleting them
+ // (except in the case that the addItem() call fails, in which case it is up
+ // to the caller to delete the item)
+
+ // returns FALSE if item faile to be added to list, does NOT delete 'item'
+ // TomY TODO - Deprecate this API and remove it
+ BOOL addItem( LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM );
+ LLScrollListItem* getFirstSelected() const;
+ virtual S32 getFirstSelectedIndex();
+ std::vector<LLScrollListItem*> getAllSelected() const;
+
+ LLScrollListItem* getLastSelectedItem() const { return mLastSelected; }
+
+ // iterate over all items
+ LLScrollListItem* getFirstData() const;
+ std::vector<LLScrollListItem*> getAllData() const;
+
+ void setAllowMultipleSelection(BOOL mult ) { mAllowMultipleSelection = mult; }
+
+ void setBgWriteableColor(const LLColor4 &c) { mBgWriteableColor = c; }
+ void setReadOnlyBgColor(const LLColor4 &c) { mBgReadOnlyColor = c; }
+ void setBgSelectedColor(const LLColor4 &c) { mBgSelectedColor = c; }
+ void setBgStripeColor(const LLColor4& c) { mBgStripeColor = c; }
+ void setFgSelectedColor(const LLColor4 &c) { mFgSelectedColor = c; }
+ void setFgUnselectedColor(const LLColor4 &c){ mFgUnselectedColor = c; }
+ void setHighlightedColor(const LLColor4 &c) { mHighlightedColor = c; }
+ void setFgDisableColor(const LLColor4 &c) { mFgDisabledColor = c; }
+
+ void setBackgroundVisible(BOOL b) { mBackgroundVisible = b; }
+ void setDrawStripes(BOOL b) { mDrawStripes = b; }
+ void setColumnPadding(const S32 c) { mColumnPadding = c; }
+ void setCommitOnKeyboardMovement(BOOL b) { mCommitOnKeyboardMovement = b; }
+ void setCommitOnSelectionChange(BOOL b) { mCommitOnSelectionChange = b; }
+ void setAllowKeyboardMovement(BOOL b) { mAllowKeyboardMovement = b; }
+
+ void setMaxSelectable(U32 max_selected) { mMaxSelectable = max_selected; }
+ S32 getMaxSelectable() { return mMaxSelectable; }
+
+
+ virtual S32 getScrollPos();
+ virtual void setScrollPos( S32 pos );
+
+ S32 getSearchColumn() { return mSearchColumn; }
+ void setSearchColumn(S32 column) { mSearchColumn = column; }
+
+ void clearSearchString() { mSearchString.clear(); }
+
+ // Overridden from LLView
+ virtual void draw();
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void setEnabled(BOOL enabled);
+ virtual void setFocus( BOOL b );
+ virtual void onFocusLost();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ virtual void arrange(S32 max_width, S32 max_height);
+ virtual LLRect getRequiredRect();
+ static BOOL rowPreceeds(LLScrollListItem *new_row, LLScrollListItem *test_row);
+
+ // Used "internally" by the scroll bar.
+ static void onScrollChange( S32 new_pos, LLScrollbar* src, void* userdata );
+
+ static void onClickColumn(void *userdata);
+
+ void updateColumns();
+ void updateColumnButtons();
+
+ void setDisplayHeading(BOOL display);
+ void setHeadingHeight(S32 heading_height);
+ void setHeadingFont(const LLFontGL* heading_font);
+ void setCollapseEmptyColumns(BOOL collapse);
+ void setIsPopup(BOOL is_popup) { mIsPopup = is_popup; }
+
+ LLScrollListItem* hitItem(S32 x,S32 y);
+ virtual void scrollToShowSelected();
+
+ // LLEditMenuHandler functions
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ virtual void selectAll();
+ virtual BOOL canSelectAll();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ void setNumDynamicColumns(int num) { mNumDynamicWidthColumns = num; }
+ void setTotalStaticColumnWidth(int width) { mTotalStaticColumnWidth = width; }
+
+protected:
+ void selectPrevItem(BOOL extend_selection);
+ void selectNextItem(BOOL extend_selection);
+ void drawItems();
+ void updateLineHeight();
+ void reportInvalidInput();
+ BOOL isRepeatedChars(const LLWString& string) const;
+ void selectItem(LLScrollListItem* itemp, BOOL single_select = TRUE);
+ void deselectItem(LLScrollListItem* itemp);
+ void commitIfChanged();
+
+protected:
+ S32 mCurIndex; // For get[First/Next]Data
+ S32 mCurSelectedIndex; // For get[First/Next]Selected
+
+ S32 mLineHeight; // the max height of a single line
+ S32 mScrollLines; // how many lines we've scrolled down
+ S32 mPageLines; // max number of lines is it possible to see on the screen given mRect and mLineHeight
+ S32 mHeadingHeight; // the height of the column header buttons, if visible
+ U32 mMaxSelectable;
+ const LLFontGL* mHeadingFont; // the font to use for column head buttons, if visible
+ LLScrollbar* mScrollbar;
+ BOOL mAllowMultipleSelection;
+ BOOL mAllowKeyboardMovement;
+ BOOL mCommitOnKeyboardMovement;
+ BOOL mCommitOnSelectionChange;
+ BOOL mSelectionChanged;
+ BOOL mCanSelect;
+ BOOL mDisplayColumnButtons;
+ BOOL mCollapseEmptyColumns;
+ BOOL mIsPopup;
+
+ typedef std::deque<LLScrollListItem *> item_list;
+ item_list mItemList;
+
+ LLScrollListItem *mLastSelected;
+
+ S32 mMaxItemCount;
+
+ LLRect mItemListRect;
+
+ S32 mColumnPadding;
+
+ BOOL mBackgroundVisible;
+ BOOL mDrawStripes;
+
+ LLColor4 mBgWriteableColor;
+ LLColor4 mBgReadOnlyColor;
+ LLColor4 mBgSelectedColor;
+ LLColor4 mBgStripeColor;
+ LLColor4 mFgSelectedColor;
+ LLColor4 mFgUnselectedColor;
+ LLColor4 mFgDisabledColor;
+ LLColor4 mHighlightedColor;
+
+ S32 mBorderThickness;
+ void (*mOnDoubleClickCallback)(void* userdata);
+ void (*mOnMaximumSelectCallback)(void* userdata );
+
+ S32 mHighlightedItem;
+ LLViewBorder* mBorder;
+
+ LLWString mSearchString;
+ LLFrameTimer mSearchTimer;
+
+ LLString mDefaultColumn;
+
+ S32 mSearchColumn;
+ S32 mNumDynamicWidthColumns;
+ S32 mTotalStaticColumnWidth;
+
+ static U32 sSortColumn;
+ static BOOL sSortAscending;
+
+ std::map<LLString, LLScrollListColumn> mColumns;
+ std::vector<LLScrollListColumn*> mColumnsIndexed;
+
+public:
+ // HACK: Did we draw one selected item this frame?
+ BOOL mDrewSelected;
+};
+
+const BOOL MULTIPLE_SELECT_YES = TRUE;
+const BOOL MULTIPLE_SELECT_NO = FALSE;
+
+const BOOL SHOW_BORDER_YES = TRUE;
+const BOOL SHOW_BORDER_NO = FALSE;
+
+#endif // LL_SCROLLLISTCTRL_H
diff --git a/indra/llui/llslider.cpp b/indra/llui/llslider.cpp
new file mode 100644
index 0000000000..2a1c2f7845
--- /dev/null
+++ b/indra/llui/llslider.cpp
@@ -0,0 +1,352 @@
+/**
+ * @file llslider.cpp
+ * @brief LLSlider base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llslider.h"
+#include "llui.h"
+
+#include "llgl.h"
+#include "llwindow.h"
+#include "llfocusmgr.h"
+#include "llkeyboard.h" // for the MASK constants
+#include "llcontrol.h"
+#include "llimagegl.h"
+
+const S32 THUMB_WIDTH = 8;
+const S32 TRACK_HEIGHT = 6;
+
+LLSlider::LLSlider(
+ const LLString& name,
+ const LLRect& rect,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata,
+ F32 initial_value,
+ F32 min_value,
+ F32 max_value,
+ F32 increment,
+ const LLString& control_name)
+ :
+ LLUICtrl( name, rect, TRUE, on_commit_callback, callback_userdata,
+ FOLLOWS_LEFT | FOLLOWS_TOP),
+ mValue( initial_value ),
+ mInitialValue( initial_value ),
+ mMinValue( min_value ),
+ mMaxValue( max_value ),
+ mIncrement( increment ),
+ mMouseOffset( 0 ),
+ mDragStartThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ),
+ mThumbRect( 0, mRect.getHeight(), THUMB_WIDTH, 0 ),
+ mTrackColor( LLUI::sColorsGroup->getColor( "SliderTrackColor" ) ),
+ mThumbOutlineColor( LLUI::sColorsGroup->getColor( "SliderThumbOutlineColor" ) ),
+ mThumbCenterColor( LLUI::sColorsGroup->getColor( "SliderThumbCenterColor" ) ),
+ mDisabledThumbColor(LLUI::sColorsGroup->getColor( "SliderDisabledThumbColor" ) ),
+ mMouseDownCallback( NULL ),
+ mMouseUpCallback( NULL )
+{
+ // prperly handle setting the starting thumb rect
+ // do it this way to handle both the operating-on-settings
+ // and standalone ways of using this
+ setControlName(control_name, NULL);
+ setValue(getValueF32());
+}
+
+EWidgetType LLSlider::getWidgetType() const
+{
+ return WIDGET_TYPE_SLIDER_BAR;
+}
+
+LLString LLSlider::getWidgetTag() const
+{
+ return LL_SLIDER_TAG;
+}
+
+void LLSlider::setValue(F32 value, BOOL from_event)
+{
+ value = llclamp( value, mMinValue, mMaxValue );
+
+ // Round to nearest increment (bias towards rounding down)
+ value -= mMinValue;
+ value += mIncrement/2.0001f;
+ value -= fmod(value, mIncrement);
+ mValue = mMinValue + value;
+
+ if (!from_event)
+ {
+ setControlValue(mValue);
+ }
+
+ F32 t = (mValue - mMinValue) / (mMaxValue - mMinValue);
+
+ S32 left_edge = THUMB_WIDTH/2;
+ S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2);
+
+ S32 x = left_edge + S32( t * (right_edge - left_edge) );
+ mThumbRect.mLeft = x - (THUMB_WIDTH/2);
+ mThumbRect.mRight = x + (THUMB_WIDTH/2);
+}
+
+F32 LLSlider::getValueF32() const
+{
+ return mValue;
+}
+
+BOOL LLSlider::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ S32 left_edge = THUMB_WIDTH/2;
+ S32 right_edge = mRect.getWidth() - (THUMB_WIDTH/2);
+
+ x += mMouseOffset;
+ x = llclamp( x, left_edge, right_edge );
+
+ F32 t = F32(x - left_edge) / (right_edge - left_edge);
+ setValue(t * (mMaxValue - mMinValue) + mMinValue );
+ onCommit();
+
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ }
+ return TRUE;
+}
+
+BOOL LLSlider::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ if( mMouseUpCallback )
+ {
+ mMouseUpCallback( this, mCallbackUserData );
+ }
+ handled = TRUE;
+ make_ui_sound("UISndClickRelease");
+ }
+ else
+ {
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLSlider::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // only do sticky-focus on non-chrome widgets
+ if (!getIsChrome())
+ {
+ setFocus(TRUE);
+ }
+ if( mMouseDownCallback )
+ {
+ mMouseDownCallback( this, mCallbackUserData );
+ }
+
+ if (MASK_CONTROL & mask) // if CTRL is modifying
+ {
+ setValue(mInitialValue);
+ onCommit();
+ }
+ else
+ {
+ // Find the offset of the actual mouse location from the center of the thumb.
+ if (mThumbRect.pointInRect(x,y))
+ {
+ mMouseOffset = (mThumbRect.mLeft + THUMB_WIDTH/2) - x;
+ }
+ else
+ {
+ mMouseOffset = 0;
+ }
+
+ // Start dragging the thumb
+ // No handler needed for focus lost since this class has no state that depends on it.
+ gFocusMgr.setMouseCapture( this, NULL );
+ mDragStartThumbRect = mThumbRect;
+ }
+ make_ui_sound("UISndClick");
+
+ return TRUE;
+}
+
+BOOL LLSlider::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ switch(key)
+ {
+ case KEY_UP:
+ case KEY_DOWN:
+ // eat up and down keys to be consistent
+ handled = TRUE;
+ break;
+ case KEY_LEFT:
+ setValue(getValueF32() - getIncrement());
+ onCommit();
+ handled = TRUE;
+ break;
+ case KEY_RIGHT:
+ setValue(getValueF32() + getIncrement());
+ onCommit();
+ handled = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ return handled;
+}
+
+void LLSlider::draw()
+{
+ if( getVisible() )
+ {
+ // Draw background and thumb.
+
+ // drawing solids requires texturing be disabled
+ LLGLSNoTexture no_texture;
+
+ LLRect rect(mDragStartThumbRect);
+
+ F32 opacity = mEnabled ? 1.f : 0.3f;
+
+ // Track
+
+ LLUUID thumb_image_id;
+ thumb_image_id.set(LLUI::sAssetsGroup->getString("rounded_square.tga"));
+ LLImageGL* thumb_imagep = LLUI::sImageProvider->getUIImageByID(thumb_image_id);
+
+ S32 height_offset = (mRect.getHeight() - TRACK_HEIGHT) / 2;
+ LLRect track_rect(0, mRect.getHeight() - height_offset, mRect.getWidth(), height_offset );
+
+ track_rect.stretch(-1);
+ gl_draw_scaled_image_with_border(track_rect.mLeft, track_rect.mBottom, 16, 16, track_rect.getWidth(), track_rect.getHeight(),
+ thumb_imagep, mTrackColor % opacity);
+ //gl_rect_2d( track_rect, mThumbOutlineColor % opacity );
+
+ if (!thumb_imagep)
+ {
+ gl_rect_2d(mThumbRect, mThumbCenterColor, TRUE);
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ gl_rect_2d(mDragStartThumbRect, mThumbCenterColor % opacity, FALSE);
+ }
+ }
+ else if( gFocusMgr.getMouseCapture() == this )
+ {
+ gl_draw_scaled_image_with_border(mDragStartThumbRect.mLeft, mDragStartThumbRect.mBottom, 16, 16, mDragStartThumbRect.getWidth(), mDragStartThumbRect.getHeight(),
+ thumb_imagep, mThumbCenterColor % 0.3f, TRUE);
+
+ if (hasFocus())
+ {
+ F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
+ LLRect highlight_rect = mThumbRect;
+ highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt)));
+ gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(),
+ thumb_imagep, gFocusMgr.getFocusColor());
+ }
+
+
+ gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(),
+ thumb_imagep, mThumbOutlineColor, TRUE);
+
+ //// Start Thumb
+ //gl_rect_2d( mDragStartThumbRect, mThumbOutlineColor % 0.3f );
+ //rect.stretch(-1);
+ //gl_rect_2d( rect, mThumbCenterColor % 0.3f );
+
+ //// Thumb
+ //gl_rect_2d( mThumbRect, mThumbOutlineColor );
+ }
+ else
+ {
+ if (hasFocus())
+ {
+ F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
+ LLRect highlight_rect = mThumbRect;
+ highlight_rect.stretch(llround(lerp(1.f, 3.f, lerp_amt)));
+ gl_draw_scaled_image_with_border(highlight_rect.mLeft, highlight_rect.mBottom, 16, 16, highlight_rect.getWidth(), highlight_rect.getHeight(),
+ thumb_imagep, gFocusMgr.getFocusColor());
+ }
+
+ gl_draw_scaled_image_with_border(mThumbRect.mLeft, mThumbRect.mBottom, 16, 16, mThumbRect.getWidth(), mThumbRect.getHeight(),
+ thumb_imagep, mThumbCenterColor % opacity, TRUE);
+ //rect = mThumbRect;
+
+ //gl_rect_2d( mThumbRect, mThumbOutlineColor % opacity );
+ //
+ //rect.stretch(-1);
+
+ //// Thumb
+ //gl_rect_2d( rect, mThumbCenterColor % opacity );
+
+ }
+
+ LLUICtrl::draw();
+ }
+}
+
+// virtual
+LLXMLNodePtr LLSlider::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("initial_val", TRUE)->setFloatValue(getInitialValue());
+ node->createChild("min_val", TRUE)->setFloatValue(getMinValue());
+ node->createChild("max_val", TRUE)->setFloatValue(getMaxValue());
+ node->createChild("increment", TRUE)->setFloatValue(getIncrement());
+
+ return node;
+}
+
+
+//static
+LLView* LLSlider::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("slider_bar");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ F32 initial_value = 0.f;
+ node->getAttributeF32("initial_val", initial_value);
+
+ F32 min_value = 0.f;
+ node->getAttributeF32("min_val", min_value);
+
+ F32 max_value = 1.f;
+ node->getAttributeF32("max_val", max_value);
+
+ F32 increment = 0.1f;
+ node->getAttributeF32("increment", increment);
+
+
+ LLSlider* slider = new LLSlider(name,
+ rect,
+ NULL,
+ NULL,
+ initial_value,
+ min_value,
+ max_value,
+ increment);
+
+ slider->initFromXML(node, parent);
+
+ return slider;
+}
diff --git a/indra/llui/llslider.h b/indra/llui/llslider.h
new file mode 100644
index 0000000000..a437cf2886
--- /dev/null
+++ b/indra/llui/llslider.h
@@ -0,0 +1,79 @@
+/**
+ * @file llslider.h
+ * @brief A simple slider with no label.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSLIDER_H
+#define LL_LLSLIDER_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+
+class LLSlider : public LLUICtrl
+{
+public:
+ LLSlider(
+ const LLString& name,
+ const LLRect& rect,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata,
+ F32 initial_value,
+ F32 min_value,
+ F32 max_value,
+ F32 increment,
+ const LLString& control_name = LLString::null );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ void setValue( F32 value, BOOL from_event = FALSE );
+ F32 getValueF32() const;
+
+ virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), TRUE); }
+ virtual LLSD getValue() const { return LLSD(getValueF32()); }
+
+ virtual void setMinValue(LLSD min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(LLSD max_value) { setMaxValue((F32)max_value.asReal()); }
+
+ F32 getInitialValue() const { return mInitialValue; }
+ F32 getMinValue() const { return mMinValue; }
+ F32 getMaxValue() const { return mMaxValue; }
+ F32 getIncrement() const { return mIncrement; }
+ void setMinValue(F32 min_value) {mMinValue = min_value;}
+ void setMaxValue(F32 max_value) {mMaxValue = max_value;}
+ void setIncrement(F32 increment) {mIncrement = increment;}
+ void setMouseDownCallback( void (*cb)(LLUICtrl* ctrl, void* userdata) ) { mMouseDownCallback = cb; }
+ void setMouseUpCallback( void (*cb)(LLUICtrl* ctrl, void* userdata) ) { mMouseUpCallback = cb; }
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void draw();
+
+protected:
+ F32 mValue;
+ F32 mInitialValue;
+ F32 mMinValue;
+ F32 mMaxValue;
+ F32 mIncrement;
+
+ S32 mMouseOffset;
+ LLRect mDragStartThumbRect;
+
+ LLRect mThumbRect;
+ LLColor4 mTrackColor;
+ LLColor4 mThumbOutlineColor;
+ LLColor4 mThumbCenterColor;
+ LLColor4 mDisabledThumbColor;
+
+ void (*mMouseDownCallback)(LLUICtrl* ctrl, void* userdata);
+ void (*mMouseUpCallback)(LLUICtrl* ctrl, void* userdata);
+};
+
+#endif // LL_LLSLIDER_H
diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp
new file mode 100644
index 0000000000..6c740aa39e
--- /dev/null
+++ b/indra/llui/llsliderctrl.cpp
@@ -0,0 +1,538 @@
+/**
+ * @file llsliderctrl.cpp
+ * @brief LLSliderCtrl base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llsliderctrl.h"
+
+#include "audioengine.h"
+#include "sound_ids.h"
+
+#include "llmath.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llslider.h"
+#include "llstring.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llresmgr.h"
+
+const U32 MAX_STRING_LENGTH = 10;
+
+
+LLSliderCtrl::LLSliderCtrl(const LLString& name, const LLRect& rect,
+ const LLString& label,
+ const LLFontGL* font,
+ S32 label_width,
+ S32 text_left,
+ BOOL show_text,
+ BOOL can_edit_text,
+ void (*commit_callback)(LLUICtrl*, void*),
+ void* callback_user_data,
+ F32 initial_value, F32 min_value, F32 max_value, F32 increment,
+ const LLString& control_which)
+ : LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data ),
+ mFont(font),
+ mShowText( show_text ),
+ mCanEditText( can_edit_text ),
+ mPrecision( 3 ),
+ mLabelBox( NULL ),
+ mLabelWidth( label_width ),
+ mValue( initial_value ),
+ mEditor( NULL ),
+ mTextBox( NULL ),
+ mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
+ mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
+ mSliderMouseUpCallback( NULL ),
+ mSliderMouseDownCallback( NULL )
+{
+ S32 top = mRect.getHeight();
+ S32 bottom = 0;
+ S32 left = 0;
+
+ // Label
+ if( !label.empty() )
+ {
+ if (label_width == 0)
+ {
+ label_width = font->getWidth(label);
+ }
+ LLRect label_rect( left, top, label_width, bottom );
+ mLabelBox = new LLTextBox( "SliderCtrl Label", label_rect, label.c_str(), font );
+ addChild(mLabelBox);
+ }
+
+ S32 slider_right = mRect.getWidth();
+ if( show_text )
+ {
+ slider_right = text_left - SLIDERCTRL_SPACING;
+ }
+
+ S32 slider_left = label_width ? label_width + SLIDERCTRL_SPACING : 0;
+ LLRect slider_rect( slider_left, top, slider_right, bottom );
+ mSlider = new LLSlider(
+ "slider",
+ slider_rect,
+ LLSliderCtrl::onSliderCommit, this,
+ initial_value, min_value, max_value, increment,
+ control_which );
+ addChild( mSlider );
+
+ if( show_text )
+ {
+ LLRect text_rect( text_left, top, mRect.getWidth(), bottom );
+ if( can_edit_text )
+ {
+ mEditor = new LLLineEditor( "SliderCtrl Editor", text_rect,
+ "", font,
+ MAX_STRING_LENGTH,
+ &LLSliderCtrl::onEditorCommit, NULL, NULL, this,
+ &LLLineEditor::prevalidateFloat );
+ mEditor->setFollowsLeft();
+ mEditor->setFollowsBottom();
+ mEditor->setFocusReceivedCallback( &LLSliderCtrl::onEditorGainFocus );
+ mEditor->setIgnoreTab(TRUE);
+ // don't do this, as selecting the entire text is single clicking in some cases
+ // and double clicking in others
+ //mEditor->setSelectAllonFocusReceived(TRUE);
+ addChild(mEditor);
+ }
+ else
+ {
+ mTextBox = new LLTextBox( "SliderCtrl Text", text_rect, "", font);
+ mTextBox->setFollowsLeft();
+ mTextBox->setFollowsBottom();
+ addChild(mTextBox);
+ }
+ }
+
+ updateText();
+}
+
+LLSliderCtrl::~LLSliderCtrl()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+// static
+void LLSliderCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata )
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusReceived();
+}
+
+F32 LLSliderCtrl::getValueF32() const
+{
+ return mSlider->getValueF32();
+}
+
+void LLSliderCtrl::setValue(F32 v, BOOL from_event)
+{
+ mSlider->setValue( v, from_event );
+ mValue = mSlider->getValueF32();
+ updateText();
+}
+
+BOOL LLSliderCtrl::setLabelArg( const LLString& key, const LLString& text )
+{
+ BOOL res = FALSE;
+ if (mLabelBox)
+ {
+ res = mLabelBox->setTextArg(key, text);
+ if (res && mLabelWidth == 0)
+ {
+ S32 label_width = mFont->getWidth(mLabelBox->getText());
+ LLRect rect = mLabelBox->getRect();
+ S32 prev_right = rect.mRight;
+ rect.mRight = rect.mLeft + label_width;
+ mLabelBox->setRect(rect);
+
+ S32 delta = rect.mRight - prev_right;
+ rect = mSlider->getRect();
+ S32 left = rect.mLeft + delta;
+ left = llclamp(left, 0, rect.mRight-SLIDERCTRL_SPACING);
+ rect.mLeft = left;
+ mSlider->setRect(rect);
+ }
+ }
+ return res;
+}
+
+void LLSliderCtrl::clear()
+{
+ setValue(0.0f);
+ if( mEditor )
+ {
+ mEditor->setText( "" );
+ }
+ if( mTextBox )
+ {
+ mTextBox->setText( "" );
+ }
+
+}
+
+BOOL LLSliderCtrl::isMouseHeldDown()
+{
+ return gFocusMgr.getMouseCapture() == mSlider;
+}
+
+void LLSliderCtrl::updateText()
+{
+ if( mEditor || mTextBox )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ // Don't display very small negative values as -0.000
+ F32 displayed_value = (F32)(floor(getValueF32() * pow(10, mPrecision) + 0.5) / pow(10, mPrecision));
+
+ LLString format = llformat("%%.%df", mPrecision);
+ LLString text = llformat(format.c_str(), displayed_value);
+ if( mEditor )
+ {
+ mEditor->setText( text );
+ }
+ else
+ {
+ mTextBox->setText( text );
+ }
+ }
+}
+
+// static
+void LLSliderCtrl::onEditorCommit( LLUICtrl* caller, void *userdata )
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ BOOL success = FALSE;
+ F32 val = self->mValue;
+ F32 saved_val = self->mValue;
+
+ LLString text = self->mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ val = (F32) atof( text.c_str() );
+ if( self->mSlider->getMinValue() <= val && val <= self->mSlider->getMaxValue() )
+ {
+ if( self->mValidateCallback )
+ {
+ self->setValue( val ); // set the value temporarily so that the callback can retrieve it.
+ if( self->mValidateCallback( self, self->mCallbackUserData ) )
+ {
+ success = TRUE;
+ }
+ }
+ else
+ {
+ self->setValue( val );
+ success = TRUE;
+ }
+ }
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ }
+ else
+ {
+ if( self->getValueF32() != saved_val )
+ {
+ self->setValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+// static
+void LLSliderCtrl::onSliderCommit( LLUICtrl* caller, void *userdata )
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ llassert( caller == self->mSlider );
+
+ BOOL success = FALSE;
+ F32 saved_val = self->mValue;
+ F32 new_val = self->mSlider->getValueF32();
+
+ if( self->mValidateCallback )
+ {
+ self->mValue = new_val; // set the value temporarily so that the callback can retrieve it.
+ if( self->mValidateCallback( self, self->mCallbackUserData ) )
+ {
+ success = TRUE;
+ }
+ }
+ else
+ {
+ self->mValue = new_val;
+ success = TRUE;
+ }
+
+ if( success )
+ {
+ self->onCommit();
+ }
+ else
+ {
+ if( self->mValue != saved_val )
+ {
+ self->setValue( saved_val );
+ }
+ self->reportInvalidData();
+ }
+ self->updateText();
+}
+
+void LLSliderCtrl::setEnabled(BOOL b)
+{
+ LLUICtrl::setEnabled( b );
+
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( b ? mTextEnabledColor : mTextDisabledColor );
+ }
+
+ mSlider->setEnabled( b );
+
+ if( mEditor )
+ {
+ mEditor->setEnabled( b );
+ }
+
+ if( mTextBox )
+ {
+ mTextBox->setColor( b ? mTextEnabledColor : mTextDisabledColor );
+ }
+}
+
+
+void LLSliderCtrl::setTentative(BOOL b)
+{
+ if( mEditor )
+ {
+ mEditor->setTentative(b);
+ }
+ LLUICtrl::setTentative(b);
+}
+
+
+void LLSliderCtrl::onCommit()
+{
+ setTentative(FALSE);
+
+ if( mEditor )
+ {
+ mEditor->setTentative(FALSE);
+ }
+
+ LLUICtrl::onCommit();
+}
+
+
+void LLSliderCtrl::setPrecision(S32 precision)
+{
+ if (precision < 0 || precision > 10)
+ {
+ llerrs << "LLSliderCtrl::setPrecision - precision out of range" << llendl;
+ return;
+ }
+
+ mPrecision = precision;
+ updateText();
+}
+
+void LLSliderCtrl::setSliderMouseDownCallback( void (*slider_mousedown_callback)(LLUICtrl* caller, void* userdata) )
+{
+ mSliderMouseDownCallback = slider_mousedown_callback;
+ mSlider->setMouseDownCallback( LLSliderCtrl::onSliderMouseDown );
+}
+
+// static
+void LLSliderCtrl::onSliderMouseDown(LLUICtrl* caller, void* userdata)
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ if( self->mSliderMouseDownCallback )
+ {
+ self->mSliderMouseDownCallback( self, self->mCallbackUserData );
+ }
+}
+
+
+void LLSliderCtrl::setSliderMouseUpCallback( void (*slider_mouseup_callback)(LLUICtrl* caller, void* userdata) )
+{
+ mSliderMouseUpCallback = slider_mouseup_callback;
+ mSlider->setMouseUpCallback( LLSliderCtrl::onSliderMouseUp );
+}
+
+// static
+void LLSliderCtrl::onSliderMouseUp(LLUICtrl* caller, void* userdata)
+{
+ LLSliderCtrl* self = (LLSliderCtrl*) userdata;
+ if( self->mSliderMouseUpCallback )
+ {
+ self->mSliderMouseUpCallback( self, self->mCallbackUserData );
+ }
+}
+
+void LLSliderCtrl::onTabInto()
+{
+ if( mEditor )
+ {
+ mEditor->onTabInto();
+ }
+}
+
+void LLSliderCtrl::reportInvalidData()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+//virtual
+LLString LLSliderCtrl::getControlName() const
+{
+ return mSlider->getControlName();
+}
+
+// virtual
+void LLSliderCtrl::setControlName(const LLString& control_name, LLView* context)
+{
+ mSlider->setControlName(control_name, context);
+}
+
+// virtual
+LLXMLNodePtr LLSliderCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("show_text", TRUE)->setBoolValue(mShowText);
+
+ node->createChild("can_edit_text", TRUE)->setBoolValue(mCanEditText);
+
+ node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision);
+
+ if (mLabelBox)
+ {
+ node->createChild("label", TRUE)->setStringValue(mLabelBox->getText());
+ }
+
+ // TomY TODO: Do we really want to export the transient state of the slider?
+ node->createChild("value", TRUE)->setFloatValue(mValue);
+
+ if (mSlider)
+ {
+ node->createChild("initial_val", TRUE)->setFloatValue(mSlider->getInitialValue());
+ node->createChild("min_val", TRUE)->setFloatValue(mSlider->getMinValue());
+ node->createChild("max_val", TRUE)->setFloatValue(mSlider->getMaxValue());
+ node->createChild("increment", TRUE)->setFloatValue(mSlider->getIncrement());
+ }
+ addColorXML(node, mTextEnabledColor, "text_enabled_color", "LabelTextColor");
+ addColorXML(node, mTextDisabledColor, "text_disabled_color", "LabelDisabledColor");
+
+ return node;
+}
+
+LLView* LLSliderCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("slider");
+ node->getAttributeString("name", name);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ // HACK: Font might not be specified.
+ if (!font)
+ {
+ font = LLFontGL::sSansSerifSmall;
+ }
+
+ S32 label_width = 0;
+ node->getAttributeS32("label_width", label_width);
+
+ BOOL show_text = TRUE;
+ node->getAttributeBOOL("show_text", show_text);
+
+ BOOL can_edit_text = FALSE;
+ node->getAttributeBOOL("can_edit_text", can_edit_text);
+
+ F32 initial_value = 0.f;
+ node->getAttributeF32("initial_val", initial_value);
+
+ F32 min_value = 0.f;
+ node->getAttributeF32("min_val", min_value);
+
+ F32 max_value = 1.f;
+ node->getAttributeF32("max_val", max_value);
+
+ F32 increment = 0.1f;
+ node->getAttributeF32("increment", increment);
+
+ U32 precision = 3;
+ node->getAttributeU32("decimal_digits", precision);
+
+ S32 text_left = 0;
+ if (show_text)
+ {
+ // calculate the size of the text box (log max_value is number of digits - 1 so plus 1)
+ if ( max_value )
+ text_left = font->getWidth("0") * ( static_cast < S32 > ( log10 ( max_value ) ) + precision + 1 );
+
+ if ( increment < 1.0f )
+ text_left += font->getWidth("."); // (mostly) take account of decimal point in value
+
+ if ( min_value < 0.0f || max_value < 0.0f )
+ text_left += font->getWidth("-"); // (mostly) take account of minus sign
+
+ // padding to make things look nicer
+ text_left += 8;
+ }
+
+ LLUICtrlCallback callback = NULL;
+
+ if (label.empty())
+ {
+ label.assign(node->getTextContents());
+ }
+
+ LLSliderCtrl* slider = new LLSliderCtrl(name,
+ rect,
+ label,
+ font,
+ label_width,
+ rect.getWidth() - text_left,
+ show_text,
+ can_edit_text,
+ callback,
+ NULL,
+ initial_value,
+ min_value,
+ max_value,
+ increment);
+
+ slider->setPrecision(precision);
+
+ slider->initFromXML(node, parent);
+
+ slider->updateText();
+
+ return slider;
+}
diff --git a/indra/llui/llsliderctrl.h b/indra/llui/llsliderctrl.h
new file mode 100644
index 0000000000..2185e42eb1
--- /dev/null
+++ b/indra/llui/llsliderctrl.h
@@ -0,0 +1,124 @@
+/**
+ * @file llsliderctrl.h
+ * @brief LLSliderCtrl base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSLIDERCTRL_H
+#define LL_LLSLIDERCTRL_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llslider.h"
+#include "lltextbox.h"
+#include "llrect.h"
+
+//
+// Constants
+//
+const S32 SLIDERCTRL_SPACING = 4; // space between label, slider, and text
+const S32 SLIDERCTRL_HEIGHT = 16;
+
+//
+// Classes
+//
+class LLFontGL;
+class LLLineEditor;
+class LLSlider;
+
+
+class LLSliderCtrl : public LLUICtrl
+{
+public:
+ LLSliderCtrl(const LLString& name,
+ const LLRect& rect,
+ const LLString& label,
+ const LLFontGL* font,
+ S32 slider_left,
+ S32 text_left,
+ BOOL show_text,
+ BOOL can_edit_text,
+ void (*commit_callback)(LLUICtrl*, void*),
+ void* callback_userdata,
+ F32 initial_value, F32 min_value, F32 max_value, F32 increment,
+ const LLString& control_which = LLString::null );
+
+ virtual ~LLSliderCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_SLIDER; }
+ virtual LLString getWidgetTag() const { return LL_SLIDER_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ F32 getValueF32() const;
+ void setValue(F32 v, BOOL from_event = FALSE);
+
+ virtual void setValue(const LLSD& value ) { setValue((F32)value.asReal(), TRUE); }
+ virtual LLSD getValue() const { return LLSD(getValueF32()); }
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ virtual void setMinValue(LLSD min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(LLSD max_value) { setMaxValue((F32)max_value.asReal()); }
+
+ BOOL isMouseHeldDown();
+
+ virtual void setEnabled( BOOL b );
+ virtual void clear();
+ virtual void setPrecision(S32 precision);
+ void setMinValue(F32 min_value) {mSlider->setMinValue(min_value);}
+ void setMaxValue(F32 max_value) {mSlider->setMaxValue(max_value);}
+ void setIncrement(F32 increment) {mSlider->setIncrement(increment);}
+
+ F32 getMinValue() { return mSlider->getMinValue(); }
+ F32 getMaxValue() { return mSlider->getMaxValue(); }
+
+ void setLabel(const LLString& label) { if (mLabelBox) mLabelBox->setText(label); }
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
+
+ void setSliderMouseDownCallback( void (*slider_mousedown_callback)(LLUICtrl* caller, void* userdata) );
+ void setSliderMouseUpCallback( void (*slider_mouseup_callback)(LLUICtrl* caller, void* userdata) );
+
+ virtual void onTabInto();
+
+ virtual void setTentative(BOOL b); // marks value as tentative
+ virtual void onCommit(); // mark not tentative, then commit
+
+ virtual void setControlName(const LLString& control_name, LLView* context);
+ virtual LLString getControlName() const;
+
+ static void onSliderCommit(LLUICtrl* caller, void* userdata);
+ static void onSliderMouseDown(LLUICtrl* caller,void* userdata);
+ static void onSliderMouseUp(LLUICtrl* caller,void* userdata);
+
+ static void onEditorCommit(LLUICtrl* caller, void* userdata);
+ static void onEditorGainFocus(LLUICtrl* caller, void *userdata);
+ static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
+
+private:
+ void updateText();
+ void reportInvalidData();
+
+private:
+ const LLFontGL* mFont;
+ BOOL mShowText;
+ BOOL mCanEditText;
+
+ S32 mPrecision;
+ LLTextBox* mLabelBox;
+ S32 mLabelWidth;
+
+ F32 mValue;
+ LLSlider* mSlider;
+ LLLineEditor* mEditor;
+ LLTextBox* mTextBox;
+
+ LLColor4 mTextEnabledColor;
+ LLColor4 mTextDisabledColor;
+
+ void (*mSliderMouseUpCallback)( LLUICtrl* ctrl, void* userdata );
+ void (*mSliderMouseDownCallback)( LLUICtrl* ctrl, void* userdata );
+};
+
+#endif // LL_LLSLIDERCTRL_H
diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp
new file mode 100644
index 0000000000..332372011e
--- /dev/null
+++ b/indra/llui/llspinctrl.cpp
@@ -0,0 +1,509 @@
+/**
+ * @file llspinctrl.cpp
+ * @brief LLSpinCtrl base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llspinctrl.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+
+#include "llstring.h"
+#include "llfontgl.h"
+#include "lllineeditor.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "llkeyboard.h"
+#include "llmath.h"
+#include "sound_ids.h"
+#include "audioengine.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llresmgr.h"
+
+const U32 MAX_STRING_LENGTH = 32;
+
+
+LLSpinCtrl::LLSpinCtrl( const LLString& name, const LLRect& rect, const LLString& label, const LLFontGL* font,
+ void (*commit_callback)(LLUICtrl*, void*),
+ void* callback_user_data,
+ F32 initial_value, F32 min_value, F32 max_value, F32 increment,
+ const LLString& control_name,
+ S32 label_width)
+ :
+ LLUICtrl(name, rect, TRUE, commit_callback, callback_user_data, FOLLOWS_LEFT | FOLLOWS_TOP ),
+ mValue( initial_value ),
+ mInitialValue( initial_value ),
+ mMaxValue( max_value ),
+ mMinValue( min_value ),
+ mIncrement( increment ),
+ mPrecision( 3 ),
+ mLabelBox( NULL ),
+ mTextEnabledColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
+ mTextDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
+ mbHasBeenSet( FALSE )
+{
+ S32 top = mRect.getHeight();
+ S32 bottom = top - 2 * SPINCTRL_BTN_HEIGHT;
+ S32 centered_top = top;
+ S32 centered_bottom = bottom;
+ S32 btn_left = 0;
+
+ // Label
+ if( !label.empty() )
+ {
+ LLRect label_rect( 0, centered_top, label_width, centered_bottom );
+ mLabelBox = new LLTextBox( "SpinCtrl Label", label_rect, label.c_str(), font );
+ addChild(mLabelBox);
+
+ btn_left += label_rect.mRight + SPINCTRL_SPACING;
+ }
+
+ S32 btn_right = btn_left + SPINCTRL_BTN_WIDTH;
+
+ // Spin buttons
+ LLRect up_rect( btn_left, top, btn_right, top - SPINCTRL_BTN_HEIGHT );
+ LLString out_id = "UIImgBtnSpinUpOutUUID";
+ LLString in_id = "UIImgBtnSpinUpInUUID";
+ mUpBtn = new LLButton(
+ "SpinCtrl Up", up_rect,
+ out_id,
+ in_id,
+ "",
+ &LLSpinCtrl::onUpBtn, this, LLFontGL::sSansSerif );
+ mUpBtn->setFollowsLeft();
+ mUpBtn->setFollowsBottom();
+ mUpBtn->setHeldDownCallback( &LLSpinCtrl::onUpBtn );
+ mUpBtn->setTabStop(FALSE);
+ addChild(mUpBtn);
+
+ LLRect down_rect( btn_left, top - SPINCTRL_BTN_HEIGHT, btn_right, bottom );
+ out_id = "UIImgBtnSpinDownOutUUID";
+ in_id = "UIImgBtnSpinDownInUUID";
+ mDownBtn = new LLButton(
+ "SpinCtrl Down", down_rect,
+ out_id,
+ in_id,
+ "",
+ &LLSpinCtrl::onDownBtn, this, LLFontGL::sSansSerif );
+ mDownBtn->setFollowsLeft();
+ mDownBtn->setFollowsBottom();
+ mDownBtn->setHeldDownCallback( &LLSpinCtrl::onDownBtn );
+ mDownBtn->setTabStop(FALSE);
+ addChild(mDownBtn);
+
+ LLRect editor_rect( btn_right + 1, centered_top, mRect.getWidth(), centered_bottom );
+ mEditor = new LLLineEditor( "SpinCtrl Editor", editor_rect, "", font,
+ MAX_STRING_LENGTH,
+ &LLSpinCtrl::onEditorCommit, NULL, NULL, this,
+ &LLLineEditor::prevalidateFloat );
+ mEditor->setFollowsLeft();
+ mEditor->setFollowsBottom();
+ mEditor->setFocusReceivedCallback( &LLSpinCtrl::onEditorGainFocus );
+ //RN: this seems to be a BAD IDEA, as it makes the editor behavior different when it has focus
+ // than when it doesn't. Instead, if you always have to double click to select all the text,
+ // it's easier to understand
+ //mEditor->setSelectAllonFocusReceived(TRUE);
+ mEditor->setIgnoreTab(TRUE);
+ addChild(mEditor);
+
+ updateEditor();
+ setSpanChildren( TRUE );
+}
+
+LLSpinCtrl::~LLSpinCtrl()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+// static
+void LLSpinCtrl::onUpBtn( void *userdata )
+{
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+ if( self->getEnabled() )
+ {
+ // use getValue()/setValue() to force reload from/to control
+ F32 val = (F32)self->getValue().asReal() + self->mIncrement;
+ val = llmin( val, self->mMaxValue );
+
+ if( self->mValidateCallback )
+ {
+ F32 saved_val = (F32)self->getValue().asReal();
+ self->setValue(val);
+ if( !self->mValidateCallback( self, self->mCallbackUserData ) )
+ {
+ self->setValue( saved_val );
+ self->reportInvalidData();
+ self->updateEditor();
+ return;
+ }
+ }
+ else
+ {
+ self->setValue(val);
+ }
+
+ self->updateEditor();
+ self->onCommit();
+ }
+}
+
+// static
+void LLSpinCtrl::onDownBtn( void *userdata )
+{
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+
+ if( self->getEnabled() )
+ {
+ F32 val = (F32)self->getValue().asReal() - self->mIncrement;
+ val = llmax( val, self->mMinValue );
+
+ if( self->mValidateCallback )
+ {
+ F32 saved_val = (F32)self->getValue().asReal();
+ self->setValue(val);
+ if( !self->mValidateCallback( self, self->mCallbackUserData ) )
+ {
+ self->setValue( saved_val );
+ self->reportInvalidData();
+ self->updateEditor();
+ return;
+ }
+ }
+ else
+ {
+ self->setValue(val);
+ }
+
+ self->updateEditor();
+ self->onCommit();
+ }
+}
+
+// static
+void LLSpinCtrl::onEditorGainFocus( LLUICtrl* caller, void *userdata )
+{
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ self->onFocusReceived();
+}
+
+void LLSpinCtrl::setValue(const LLSD& value )
+{
+ F32 v = (F32)value.asReal();
+ if (mValue != v || !mbHasBeenSet)
+ {
+ mbHasBeenSet = TRUE;
+ mValue = v;
+
+ if (!mEditor->hasFocus())
+ {
+ updateEditor();
+ }
+ }
+}
+
+LLSD LLSpinCtrl::getValue() const
+{
+ return mValue;
+}
+
+void LLSpinCtrl::clear()
+{
+ setValue(mMinValue);
+ mEditor->clear();
+ mbHasBeenSet = FALSE;
+}
+
+
+void LLSpinCtrl::updateEditor()
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ // Don't display very small negative values as -0.000
+ F32 displayed_value = (F32)floor(getValue().asReal() * pow(10, mPrecision) + 0.5) / (F32)pow(10, mPrecision);
+
+// if( S32( displayed_value * pow( 10, mPrecision ) ) == 0 )
+// {
+// displayed_value = 0.f;
+// }
+
+ LLString format = llformat("%%.%df", mPrecision);
+ LLString text = llformat(format.c_str(), displayed_value);
+ mEditor->setText( text );
+}
+
+void LLSpinCtrl::onEditorCommit( LLUICtrl* caller, void *userdata )
+{
+ BOOL success = FALSE;
+
+ LLSpinCtrl* self = (LLSpinCtrl*) userdata;
+ llassert( caller == self->mEditor );
+
+ LLString text = self->mEditor->getText();
+ if( LLLineEditor::postvalidateFloat( text ) )
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ F32 val = (F32) atof(text.c_str());
+
+ if (val < self->mMinValue) val = self->mMinValue;
+ if (val > self->mMaxValue) val = self->mMaxValue;
+
+ if( self->mValidateCallback )
+ {
+ F32 saved_val = self->mValue;
+ self->mValue = val;
+ if( self->mValidateCallback( self, self->mCallbackUserData ) )
+ {
+ success = TRUE;
+ self->onCommit();
+ }
+ else
+ {
+ self->mValue = saved_val;
+ }
+ }
+ else
+ {
+ self->mValue = val;
+ self->onCommit();
+ success = TRUE;
+ }
+ }
+ self->updateEditor();
+
+ if( !success )
+ {
+ self->reportInvalidData();
+ }
+}
+
+
+void LLSpinCtrl::forceEditorCommit()
+{
+ onEditorCommit(mEditor, this);
+}
+
+
+void LLSpinCtrl::setFocus(BOOL b)
+{
+ LLUICtrl::setFocus( b );
+ mEditor->setFocus( b );
+}
+
+void LLSpinCtrl::setEnabled(BOOL b)
+{
+ LLUICtrl::setEnabled( b );
+ mEditor->setEnabled( b );
+}
+
+
+void LLSpinCtrl::setTentative(BOOL b)
+{
+ mEditor->setTentative(b);
+ LLUICtrl::setTentative(b);
+}
+
+
+BOOL LLSpinCtrl::isMouseHeldDown()
+{
+ return
+ gFocusMgr.getMouseCapture() == mDownBtn ||
+ gFocusMgr.getMouseCapture() == mUpBtn;
+}
+
+void LLSpinCtrl::onCommit()
+{
+ setTentative(FALSE);
+
+ setControlValue(mValue);
+
+ LLUICtrl::onCommit();
+}
+
+
+void LLSpinCtrl::setPrecision(S32 precision)
+{
+ if (precision < 0 || precision > 10)
+ {
+ llerrs << "LLSpinCtrl::setPrecision - precision out of range" << llendl;
+ return;
+ }
+
+ mPrecision = precision;
+ updateEditor();
+}
+
+void LLSpinCtrl::setLabel(const LLString& label)
+{
+ if (mLabelBox)
+ {
+ mLabelBox->setText(label);
+ }
+ else
+ {
+ llwarns << "Attempting to set label on LLSpinCtrl constructed without one " << getName() << llendl;
+ }
+}
+
+void LLSpinCtrl::onTabInto()
+{
+ mEditor->onTabInto();
+}
+
+
+void LLSpinCtrl::reportInvalidData()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+void LLSpinCtrl::draw()
+{
+ if( mLabelBox )
+ {
+ mLabelBox->setColor( mEnabled ? mTextEnabledColor : mTextDisabledColor );
+ }
+ LLUICtrl::draw();
+}
+
+
+BOOL LLSpinCtrl::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if( mEnabled )
+ {
+ if( clicks > 0 )
+ {
+ while( clicks-- )
+ {
+ LLSpinCtrl::onDownBtn(this);
+ }
+ }
+ else
+ while( clicks++ )
+ {
+ LLSpinCtrl::onUpBtn(this);
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLSpinCtrl::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (mEditor->hasFocus())
+ {
+ if(key == KEY_ESCAPE)
+ {
+ // text editors don't support revert normally (due to user confusion)
+ // but not allowing revert on a spinner seems dangerous
+ updateEditor();
+ mEditor->setFocus(FALSE);
+ return TRUE;
+ }
+ if(key == KEY_UP)
+ {
+ LLSpinCtrl::onUpBtn(this);
+ return TRUE;
+ }
+ if(key == KEY_DOWN)
+ {
+ LLSpinCtrl::onDownBtn(this);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// virtual
+LLXMLNodePtr LLSpinCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("decimal_digits", TRUE)->setIntValue(mPrecision);
+
+ if (mLabelBox)
+ {
+ node->createChild("label", TRUE)->setStringValue(mLabelBox->getText());
+
+ node->createChild("label_width", TRUE)->setIntValue(mLabelBox->getRect().getWidth());
+ }
+
+ node->createChild("initial_val", TRUE)->setFloatValue(mInitialValue);
+
+ node->createChild("min_val", TRUE)->setFloatValue(mMinValue);
+
+ node->createChild("max_val", TRUE)->setFloatValue(mMaxValue);
+
+ node->createChild("increment", TRUE)->setFloatValue(mIncrement);
+
+ addColorXML(node, mTextEnabledColor, "text_enabled_color", "LabelTextColor");
+ addColorXML(node, mTextDisabledColor, "text_disabled_color", "LabelDisabledColor");
+
+ return node;
+}
+
+LLView* LLSpinCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("spinner");
+ node->getAttributeString("name", name);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ F32 initial_value = 0.f;
+ node->getAttributeF32("initial_val", initial_value);
+
+ F32 min_value = 0.f;
+ node->getAttributeF32("min_val", min_value);
+
+ F32 max_value = 1.f;
+ node->getAttributeF32("max_val", max_value);
+
+ F32 increment = 0.1f;
+ node->getAttributeF32("increment", increment);
+
+ U32 precision = 3;
+ node->getAttributeU32("decimal_digits", precision);
+
+ S32 label_width = llmin(40, rect.getWidth() - 40);
+ node->getAttributeS32("label_width", label_width);
+
+ LLUICtrlCallback callback = NULL;
+
+ if(label.empty())
+ {
+ label.assign( node->getValue() );
+ }
+
+ LLSpinCtrl* spinner = new LLSpinCtrl(name,
+ rect,
+ label,
+ font,
+ callback,
+ NULL,
+ initial_value,
+ min_value,
+ max_value,
+ increment,
+ "",
+ label_width);
+
+ spinner->setPrecision(precision);
+
+ spinner->initFromXML(node, parent);
+
+ return spinner;
+}
diff --git a/indra/llui/llspinctrl.h b/indra/llui/llspinctrl.h
new file mode 100644
index 0000000000..d6ccd4d6bf
--- /dev/null
+++ b/indra/llui/llspinctrl.h
@@ -0,0 +1,121 @@
+/**
+ * @file llspinctrl.h
+ * @brief LLSpinCtrl base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSPINCTRL_H
+#define LL_LLSPINCTRL_H
+
+
+#include "stdtypes.h"
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llrect.h"
+
+//
+// Constants
+//
+const S32 SPINCTRL_BTN_HEIGHT = 8;
+const S32 SPINCTRL_BTN_WIDTH = 16;
+const S32 SPINCTRL_SPACING = 2; // space between label right and button left
+const S32 SPINCTRL_HEIGHT = 2 * SPINCTRL_BTN_HEIGHT;
+const S32 SPINCTRL_DEFAULT_LABEL_WIDTH = 10;
+
+//
+// Classes
+//
+class LLFontGL;
+class LLButton;
+class LLTextBox;
+class LLLineEditor;
+
+
+class LLSpinCtrl
+: public LLUICtrl
+{
+public:
+ LLSpinCtrl(const LLString& name, const LLRect& rect,
+ const LLString& label,
+ const LLFontGL* font,
+ void (*commit_callback)(LLUICtrl*, void*),
+ void* callback_userdata,
+ F32 initial_value, F32 min_value, F32 max_value, F32 increment,
+ const LLString& control_name = LLString(),
+ S32 label_width = SPINCTRL_DEFAULT_LABEL_WIDTH );
+
+ virtual ~LLSpinCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_SPINNER; }
+ virtual LLString getWidgetTag() const { return LL_SPIN_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ F32 get() { return (F32)getValue().asReal(); }
+ void set(F32 value) { setValue(value); }
+
+ virtual void setMinValue(LLSD min_value) { setMinValue((F32)min_value.asReal()); }
+ virtual void setMaxValue(LLSD max_value) { setMaxValue((F32)max_value.asReal()); }
+
+ BOOL isMouseHeldDown();
+
+ virtual void setEnabled( BOOL b );
+ virtual void setFocus( BOOL b );
+ virtual void clear();
+ virtual void setPrecision(S32 precision);
+ virtual void setMinValue(F32 min) { mMinValue = min; }
+ virtual void setMaxValue(F32 max) { mMaxValue = max; }
+ virtual void setIncrement(F32 inc) { mIncrement = inc; }
+
+ void setLabel(const LLString& label);
+ void setLabelColor(const LLColor4& c) { mTextEnabledColor = c; }
+ void setDisabledLabelColor(const LLColor4& c) { mTextDisabledColor = c; }
+
+ virtual void onTabInto();
+
+ virtual void setTentative(BOOL b); // marks value as tentative
+ virtual void onCommit(); // mark not tentative, then commit
+
+ void forceEditorCommit(); // for commit on external button
+
+ virtual BOOL handleScrollWheel(S32 x,S32 y,S32 clicks);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ virtual void draw();
+
+ static void onEditorCommit(LLUICtrl* caller, void* userdata);
+ static void onEditorGainFocus(LLUICtrl* caller, void *userdata);
+ static void onEditorChangeFocus(LLUICtrl* caller, S32 direction, void *userdata);
+
+ static void onUpBtn(void *userdata);
+ static void onDownBtn(void *userdata);
+
+protected:
+ void updateEditor();
+ void reportInvalidData();
+
+protected:
+
+ F32 mValue;
+ F32 mInitialValue;
+ F32 mMaxValue;
+ F32 mMinValue;
+ F32 mIncrement;
+
+ S32 mPrecision;
+ LLTextBox* mLabelBox;
+
+ LLLineEditor* mEditor;
+ LLColor4 mTextEnabledColor;
+ LLColor4 mTextDisabledColor;
+
+ LLButton* mUpBtn;
+ LLButton* mDownBtn;
+
+ BOOL mbHasBeenSet;
+};
+
+#endif // LL_LLSPINCTRL_H
diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp
new file mode 100644
index 0000000000..e03dd987d7
--- /dev/null
+++ b/indra/llui/llstyle.cpp
@@ -0,0 +1,223 @@
+/**
+ * @file llstyle.cpp
+ * @brief Text style class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llstyle.h"
+#include "llstring.h"
+#include "llui.h"
+
+//#include "llviewerimagelist.h"
+
+LLStyle::LLStyle()
+{
+ init(TRUE, LLColor4(0,0,0,1),"");
+}
+
+LLStyle::LLStyle(const LLStyle &style)
+{
+ if (this != &style)
+ {
+ init(style.isVisible(),style.getColor(),style.getFontString());
+ if (style.isLink())
+ {
+ setLinkHREF(style.getLinkHREF());
+ }
+ mItalic = style.mItalic;
+ mBold = style.mBold;
+ mUnderline = style.mUnderline;
+ mDropShadow = style.mDropShadow;
+ mImageHeight = style.mImageHeight;
+ mImageWidth = style.mImageWidth;
+ mImagep = style.mImagep;
+ mIsEmbeddedItem = style.mIsEmbeddedItem;
+ }
+ else
+ {
+ init(TRUE, LLColor4(0,0,0,1),"");
+ }
+}
+
+LLStyle::LLStyle(BOOL is_visible, const LLColor4 &color, const LLString& font_name)
+{
+ init(is_visible, color, font_name);
+}
+
+LLStyle::~LLStyle()
+{
+ free();
+}
+
+void LLStyle::init(BOOL is_visible, const LLColor4 &color, const LLString& font_name)
+{
+ mVisible = is_visible;
+ mColor = color;
+ setFontName(font_name);
+ setLinkHREF("");
+ mItalic = FALSE;
+ mBold = FALSE;
+ mUnderline = FALSE;
+ mDropShadow = FALSE;
+ mImageHeight = 0;
+ mImageWidth = 0;
+ mIsEmbeddedItem = FALSE;
+}
+
+void LLStyle::free()
+{
+}
+
+LLFONT_ID LLStyle::getFontID() const
+{
+ return mFontID;
+}
+
+// Copy assignment
+LLStyle &LLStyle::operator=(const LLStyle &rhs)
+{
+ if (this != &rhs)
+ {
+ setVisible(rhs.isVisible());
+ setColor(rhs.getColor());
+ this->mFontName = rhs.getFontString();
+ this->mLink = rhs.getLinkHREF();
+ mImagep = rhs.mImagep;
+ mImageHeight = rhs.mImageHeight;
+ mImageWidth = rhs.mImageWidth;
+ mItalic = rhs.mItalic;
+ mBold = rhs.mBold;
+ mUnderline = rhs.mUnderline;
+ mDropShadow = rhs.mUnderline;
+ mIsEmbeddedItem = rhs.mIsEmbeddedItem;
+ }
+
+ return *this;
+}
+
+// Compare
+bool LLStyle::operator==(const LLStyle &rhs) const
+{
+ if ((mVisible != rhs.isVisible())
+ || (mColor != rhs.getColor())
+ || (mFontName != rhs.getFontString())
+ || (mLink != rhs.getLinkHREF())
+ || (mImagep != rhs.mImagep)
+ || (mImageHeight != rhs.mImageHeight)
+ || (mImageWidth != rhs.mImageWidth)
+ || (mItalic != rhs.mItalic)
+ || (mBold != rhs.mBold)
+ || (mUnderline != rhs.mUnderline)
+ || (mDropShadow != rhs.mDropShadow)
+ || (mIsEmbeddedItem != rhs.mIsEmbeddedItem)
+ )
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool LLStyle::operator!=(const LLStyle& rhs) const
+{
+ return !(*this == rhs);
+}
+
+
+const LLColor4& LLStyle::getColor() const
+{
+ return(mColor);
+}
+
+void LLStyle::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+const LLString& LLStyle::getFontString() const
+{
+ return mFontName;
+}
+
+void LLStyle::setFontName(const LLString& fontname)
+{
+ mFontName = fontname;
+
+ LLString fontname_lc = fontname;
+ LLString::toLower(fontname_lc);
+
+ mFontID = LLFONT_OCRA; // default
+
+ if ((fontname_lc == "sansserif") || (fontname_lc == "sans-serif"))
+ {
+ mFontID = LLFONT_SANSSERIF;
+ }
+ else if ((fontname_lc == "serif"))
+ {
+ mFontID = LLFONT_SMALL;
+ }
+ else if ((fontname_lc == "sansserifbig"))
+ {
+ mFontID = LLFONT_SANSSERIF_BIG;
+ }
+ else if (fontname_lc == "small")
+ {
+ mFontID = LLFONT_SANSSERIF_SMALL;
+ }
+}
+
+const LLString& LLStyle::getLinkHREF() const
+{
+ return mLink;
+}
+
+void LLStyle::setLinkHREF(const LLString& href)
+{
+ mLink = href;
+}
+
+BOOL LLStyle::isLink() const
+{
+ return mLink.size();
+}
+
+BOOL LLStyle::isVisible() const
+{
+ return mVisible;
+}
+
+void LLStyle::setVisible(BOOL is_visible)
+{
+ mVisible = is_visible;
+}
+
+LLImageGL *LLStyle::getImage() const
+{
+ return mImagep;
+}
+
+void LLStyle::setImage(const LLString& src)
+{
+ if (src.size() < UUID_STR_LENGTH - 1)
+ {
+ return;
+ }
+ else
+ {
+ mImagep = LLUI::sImageProvider->getUIImageByID(LLUUID(src));
+ }
+}
+
+BOOL LLStyle::isImage() const
+{
+ return ((mImageWidth != 0) && (mImageHeight != 0));
+}
+
+void LLStyle::setImageSize(S32 width, S32 height)
+{
+ mImageWidth = width;
+ mImageHeight = height;
+}
diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h
new file mode 100644
index 0000000000..6a689dab98
--- /dev/null
+++ b/indra/llui/llstyle.h
@@ -0,0 +1,75 @@
+/**
+ * @file llstyle.h
+ * @brief Text style class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTYLE_H
+#define LL_LLSTYLE_H
+
+#include "v4color.h"
+#include "llresmgr.h"
+#include "llfont.h"
+#include "llimagegl.h"
+
+class LLStyle
+{
+public:
+ LLStyle();
+ LLStyle(const LLStyle &style);
+ LLStyle(BOOL is_visible, const LLColor4 &color, const LLString& font_name);
+
+ LLStyle &operator=(const LLStyle &rhs);
+
+ virtual ~LLStyle();
+
+ virtual void init (BOOL is_visible, const LLColor4 &color, const LLString& font_name);
+ virtual void free ();
+
+ bool operator==(const LLStyle &rhs) const;
+ bool operator!=(const LLStyle &rhs) const;
+
+ virtual const LLColor4& getColor() const;
+ virtual void setColor(const LLColor4 &color);
+
+ virtual BOOL isVisible() const;
+ virtual void setVisible(BOOL is_visible);
+
+ virtual const LLString& getFontString() const;
+ virtual void setFontName(const LLString& fontname);
+ virtual LLFONT_ID getFontID() const;
+
+ virtual const LLString& getLinkHREF() const;
+ virtual void setLinkHREF(const LLString& fontname);
+ virtual BOOL isLink() const;
+
+ virtual LLImageGL *getImage() const;
+ virtual void setImage(const LLString& src);
+ virtual BOOL isImage() const;
+ virtual void setImageSize(S32 width, S32 height);
+
+ BOOL getIsEmbeddedItem() const { return mIsEmbeddedItem; }
+ void setIsEmbeddedItem( BOOL b ) { mIsEmbeddedItem = b; }
+
+public:
+ BOOL mItalic;
+ BOOL mBold;
+ BOOL mUnderline;
+ BOOL mDropShadow;
+ S32 mImageWidth;
+ S32 mImageHeight;
+
+protected:
+ BOOL mVisible;
+ LLColor4 mColor;
+ LLString mFontName;
+ LLFONT_ID mFontID;
+ LLString mLink;
+ LLPointer<LLImageGL> mImagep;
+
+ BOOL mIsEmbeddedItem;
+};
+
+#endif // LL_LLSTYLE_H
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
new file mode 100644
index 0000000000..fd85dbb2f4
--- /dev/null
+++ b/indra/llui/lltabcontainer.cpp
@@ -0,0 +1,1469 @@
+/**
+ * @file lltabcontainer.cpp
+ * @brief LLTabContainerCommon base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltabcontainer.h"
+
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+
+#include "llbutton.h"
+#include "llrect.h"
+#include "llpanel.h"
+#include "llresmgr.h"
+#include "llkeyboard.h"
+#include "llresizehandle.h"
+#include "llui.h"
+#include "lltextbox.h"
+#include "llcontrol.h"
+#include "llcriticaldamp.h"
+#include "lluictrlfactory.h"
+
+#include "lltabcontainervertical.h"
+
+#include "llglheaders.h"
+
+const F32 SCROLL_STEP_TIME = 0.4f;
+const S32 TAB_PADDING = 15;
+const S32 TABCNTR_TAB_MIN_WIDTH = 60;
+const S32 TABCNTR_TAB_MAX_WIDTH = 150;
+const S32 TABCNTR_TAB_PARTIAL_WIDTH = 12; // When tabs are parially obscured, how much can you still see.
+const S32 TABCNTR_TAB_HEIGHT = 16;
+const S32 TABCNTR_ARROW_BTN_SIZE = 16;
+const S32 TABCNTR_BUTTON_PANEL_OVERLAP = 1; // how many pixels the tab buttons and tab panels overlap.
+const S32 TABCNTR_TAB_H_PAD = 4;
+
+
+LLTabContainerCommon::LLTabContainerCommon(
+ const LLString& name, const LLRect& rect,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered )
+ :
+ LLPanel(name, rect, bordered),
+ mCurrentTabIdx(-1),
+ mScrolled(FALSE),
+ mScrollPos(0),
+ mScrollPosPixels(0),
+ mMaxScrollPos(0),
+ mCloseCallback( close_callback ),
+ mCallbackUserdata( callback_userdata ),
+ mTitleBox(NULL),
+ mTopBorderHeight(LLPANEL_BORDER_WIDTH),
+ mTabPosition(pos)
+{
+ setMouseOpaque(FALSE);
+}
+
+
+LLTabContainerCommon::LLTabContainerCommon(
+ const LLString& name,
+ const LLString& rect_control,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered )
+ :
+ LLPanel(name, rect_control, bordered),
+ mCurrentTabIdx(-1),
+ mScrolled(FALSE),
+ mScrollPos(0),
+ mScrollPosPixels(0),
+ mMaxScrollPos(0),
+ mCloseCallback( close_callback ),
+ mCallbackUserdata( callback_userdata ),
+ mTitleBox(NULL),
+ mTopBorderHeight(LLPANEL_BORDER_WIDTH),
+ mTabPosition(pos)
+{
+ setMouseOpaque(FALSE);
+}
+
+
+LLTabContainerCommon::~LLTabContainerCommon()
+{
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+}
+
+LLView* LLTabContainerCommon::getChildByName(const LLString& name, BOOL recurse) const
+{
+ tuple_list_t::const_iterator itor;
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ if (panel->getName() == name)
+ {
+ return panel;
+ }
+ }
+ if (recurse)
+ {
+ for (itor = mTabList.begin(); itor != mTabList.end(); ++itor)
+ {
+ LLPanel *panel = (*itor)->mTabPanel;
+ LLView *child = panel->getChildByName(name, recurse);
+ if (child)
+ {
+ return child;
+ }
+ }
+ }
+ return LLView::getChildByName(name, recurse);
+}
+
+void LLTabContainerCommon::addPlaceholder(LLPanel* child, const LLString& label)
+{
+ addTabPanel(child, label, FALSE, NULL, NULL, 0, TRUE);
+}
+
+void LLTabContainerCommon::removeTabPanel(LLPanel* child)
+{
+ BOOL has_focus = gFocusMgr.childHasKeyboardFocus(this);
+
+ // If the tab being deleted is the selected one, select a different tab.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ removeChild( tuple->mButton );
+ delete tuple->mButton;
+
+ removeChild( tuple->mTabPanel );
+// delete tuple->mTabPanel;
+
+ mTabList.erase( iter );
+ delete tuple;
+
+ break;
+ }
+ }
+ if (mCurrentTabIdx >= (S32)mTabList.size())
+ {
+ mCurrentTabIdx = mTabList.size()-1;
+ }
+ selectTab(mCurrentTabIdx);
+ if (has_focus)
+ {
+ LLPanel* panelp = getPanelByIndex(mCurrentTabIdx);
+ if (panelp)
+ {
+ panelp->setFocus(TRUE);
+ }
+ }
+
+ updateMaxScrollPos();
+}
+
+void LLTabContainerCommon::deleteAllTabs()
+{
+ // Remove all the tab buttons and delete them. Also, unlink all the child panels.
+ for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+
+ removeChild( tuple->mButton );
+ delete tuple->mButton;
+
+ removeChild( tuple->mTabPanel );
+// delete tuple->mTabPanel;
+ }
+
+ // Actually delete the tuples themselves
+ std::for_each(mTabList.begin(), mTabList.end(), DeletePointer());
+ mTabList.clear();
+
+ // And there isn't a current tab any more
+ mCurrentTabIdx = -1;
+}
+
+
+LLPanel* LLTabContainerCommon::getCurrentPanel()
+{
+ if (mCurrentTabIdx < 0 || mCurrentTabIdx >= (S32) mTabList.size()) return NULL;
+
+ return mTabList[mCurrentTabIdx]->mTabPanel;
+}
+
+LLTabContainerCommon::LLTabTuple* LLTabContainerCommon::getTabByPanel(LLPanel* child)
+{
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return tuple;
+ }
+ }
+ return NULL;
+}
+
+void LLTabContainerCommon::setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*, bool))
+{
+ LLTabTuple* tuplep = getTabByPanel(tab);
+ if (tuplep)
+ {
+ tuplep->mOnChangeCallback = on_tab_clicked;
+ }
+}
+
+void LLTabContainerCommon::setTabUserData(LLPanel* tab, void* userdata)
+{
+ LLTabTuple* tuplep = getTabByPanel(tab);
+ if (tuplep)
+ {
+ tuplep->mUserData = userdata;
+ }
+}
+
+S32 LLTabContainerCommon::getCurrentPanelIndex()
+{
+ return mCurrentTabIdx;
+}
+
+S32 LLTabContainerCommon::getTabCount()
+{
+ return mTabList.size();
+}
+
+LLPanel* LLTabContainerCommon::getPanelByIndex(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ return mTabList[index]->mTabPanel;
+ }
+ return NULL;
+}
+
+S32 LLTabContainerCommon::getIndexForPanel(LLPanel* panel)
+{
+ for (S32 index = 0; index < (S32)mTabList.size(); index++)
+ {
+ if (mTabList[index]->mTabPanel == panel)
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+S32 LLTabContainerCommon::getPanelIndexByTitle(const LLString& title)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ if (title == mTabList[index]->mButton->getLabelSelected())
+ {
+ return index;
+ }
+ }
+ return -1;
+}
+
+LLPanel *LLTabContainerCommon::getPanelByName(const LLString& name)
+{
+ for (S32 index = 0 ; index < (S32)mTabList.size(); index++)
+ {
+ LLPanel *panel = mTabList[index]->mTabPanel;
+ if (name == panel->getName())
+ {
+ return panel;
+ }
+ }
+ return NULL;
+}
+
+
+void LLTabContainerCommon::scrollNext()
+{
+ // No wrap
+ if( mScrollPos < mMaxScrollPos )
+ {
+ mScrollPos++;
+ }
+}
+
+void LLTabContainerCommon::scrollPrev()
+{
+ // No wrap
+ if( mScrollPos > 0 )
+ {
+ mScrollPos--;
+ }
+}
+
+void LLTabContainerCommon::enableTabButton(S32 which, BOOL enable)
+{
+ if (which >= 0 && which < (S32)mTabList.size())
+ {
+ mTabList[which]->mButton->setEnabled(enable);
+ }
+}
+
+BOOL LLTabContainerCommon::selectTabPanel(LLPanel* child)
+{
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ return selectTab( idx );
+ }
+ idx++;
+ }
+ return FALSE;
+}
+
+BOOL LLTabContainerCommon::selectTabByName(const LLString& name)
+{
+ LLPanel* panel = getPanelByName(name);
+ if (!panel)
+ {
+ llwarns << "LLTabContainerCommon::selectTabByName("
+ << name << ") failed" << llendl;
+ return FALSE;
+ }
+
+ BOOL result = selectTabPanel(panel);
+ return result;
+}
+
+
+void LLTabContainerCommon::selectFirstTab()
+{
+ selectTab( 0 );
+}
+
+
+void LLTabContainerCommon::selectLastTab()
+{
+ selectTab( mTabList.size()-1 );
+}
+
+
+void LLTabContainerCommon::selectNextTab()
+{
+ BOOL tab_has_focus = FALSE;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = TRUE;
+ }
+ S32 idx = mCurrentTabIdx+1;
+ if (idx >= (S32)mTabList.size())
+ idx = 0;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = (idx + 1 ) % (S32)mTabList.size();
+ }
+
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(TRUE);
+ }
+}
+
+void LLTabContainerCommon::selectPrevTab()
+{
+ BOOL tab_has_focus = FALSE;
+ if (mCurrentTabIdx >= 0 && mTabList[mCurrentTabIdx]->mButton->hasFocus())
+ {
+ tab_has_focus = TRUE;
+ }
+ S32 idx = mCurrentTabIdx-1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ while (!selectTab(idx) && idx != mCurrentTabIdx)
+ {
+ idx = idx - 1;
+ if (idx < 0)
+ idx = mTabList.size()-1;
+ }
+ if (tab_has_focus)
+ {
+ mTabList[idx]->mButton->setFocus(TRUE);
+ }
+}
+
+
+void LLTabContainerCommon::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape( width, height, called_from_parent );
+ updateMaxScrollPos();
+}
+
+// static
+void LLTabContainerCommon::onTabBtn( void* userdata )
+{
+ LLTabTuple* tuple = (LLTabTuple*) userdata;
+ LLTabContainerCommon* self = tuple->mTabContainer;
+ self->selectTabPanel( tuple->mTabPanel );
+
+ if( tuple->mOnChangeCallback )
+ {
+ tuple->mOnChangeCallback( tuple->mUserData, true );
+ }
+
+ tuple->mTabPanel->setFocus(TRUE);
+}
+
+// static
+void LLTabContainerCommon::onCloseBtn( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if( self->mCloseCallback )
+ {
+ self->mCloseCallback( self->mCallbackUserdata );
+ }
+}
+
+// static
+void LLTabContainerCommon::onNextBtn( void* userdata )
+{
+ // Scroll tabs to the left
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (!self->mScrolled)
+ {
+ self->scrollNext();
+ }
+ self->mScrolled = FALSE;
+}
+
+// static
+void LLTabContainerCommon::onNextBtnHeld( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ self->mScrollTimer.reset();
+ self->scrollNext();
+ self->mScrolled = TRUE;
+ }
+}
+
+// static
+void LLTabContainerCommon::onPrevBtn( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (!self->mScrolled)
+ {
+ self->scrollPrev();
+ }
+ self->mScrolled = FALSE;
+}
+
+// static
+void LLTabContainerCommon::onPrevBtnHeld( void* userdata )
+{
+ LLTabContainer* self = (LLTabContainer*) userdata;
+ if (self->mScrollTimer.getElapsedTimeF32() > SCROLL_STEP_TIME)
+ {
+ self->mScrollTimer.reset();
+ self->scrollPrev();
+ self->mScrolled = TRUE;
+ }
+}
+
+BOOL LLTabContainerCommon::getTabPanelFlashing(LLPanel *child)
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ return tuple->mButton->getFlashing();
+ }
+ return FALSE;
+}
+
+void LLTabContainerCommon::setTabPanelFlashing(LLPanel* child, BOOL state )
+{
+ LLTabTuple* tuple = getTabByPanel(child);
+ if( tuple )
+ {
+ tuple->mButton->setFlashing( state );
+ }
+}
+
+void LLTabContainerCommon::setTitle(const LLString& title)
+{
+ if (mTitleBox)
+ {
+ mTitleBox->setText( title );
+ }
+}
+
+const LLString LLTabContainerCommon::getPanelTitle(S32 index)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ LLButton* tab_button = mTabList[index]->mButton;
+ return tab_button->getLabelSelected();
+ }
+ return LLString::null;
+}
+
+void LLTabContainerCommon::setTopBorderHeight(S32 height)
+{
+ mTopBorderHeight = height;
+}
+
+// Change the name of the button for the current tab.
+void LLTabContainerCommon::setCurrentTabName(const LLString& name)
+{
+ // Might not have a tab selected
+ if (mCurrentTabIdx < 0) return;
+
+ mTabList[mCurrentTabIdx]->mButton->setLabelSelected(name);
+ mTabList[mCurrentTabIdx]->mButton->setLabelUnselected(name);
+}
+
+LLView* LLTabContainerCommon::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("tab_container");
+ node->getAttributeString("name", name);
+
+ // Figure out if we are creating a vertical or horizontal tab container.
+ bool is_vertical = false;
+ LLTabContainer::TabPosition tab_position = LLTabContainer::TOP;
+ if (node->hasAttribute("tab_position"))
+ {
+ LLString tab_position_string;
+ node->getAttributeString("tab_position", tab_position_string);
+ LLString::toLower(tab_position_string);
+
+ if ("top" == tab_position_string)
+ {
+ tab_position = LLTabContainer::TOP;
+ is_vertical = false;
+ }
+ else if ("bottom" == tab_position_string)
+ {
+ tab_position = LLTabContainer::BOTTOM;
+ is_vertical = false;
+ }
+ else if ("left" == tab_position_string)
+ {
+ is_vertical = true;
+ }
+ }
+ BOOL border = FALSE;
+ node->getAttributeBOOL("border", border);
+
+ // Create the correct container type.
+ LLTabContainerCommon* tab_container = NULL;
+
+ if (is_vertical)
+ {
+ // Vertical tabs can specify tab width
+ U32 tab_width = TABCNTRV_TAB_WIDTH;
+ if (node->hasAttribute("tab_width"))
+ {
+ node->getAttributeU32("tab_width", tab_width);
+ }
+
+ tab_container = new LLTabContainerVertical(name,
+ LLRect::null,
+ NULL,
+ NULL,
+ tab_width,
+ border);
+
+ }
+ else // horizontal tab container
+ {
+ // Horizontal tabs can have a title (?)
+ LLString title(LLString::null);
+ if (node->hasAttribute("title"))
+ {
+ node->getAttributeString("title", title);
+ }
+
+ tab_container = new LLTabContainer(name,
+ LLRect::null,
+ tab_position,
+ NULL,
+ NULL,
+ title,
+ border);
+
+ if(node->hasAttribute("tab_min_width"))
+ {
+ S32 minTabWidth=0;
+ node->getAttributeS32("tab_min_width",minTabWidth);
+ ((LLTabContainer*)tab_container)->setMinTabWidth(minTabWidth);
+ }
+ if(node->hasAttribute("tab_max_width"))
+ {
+ S32 maxTabWidth=0;
+ node->getAttributeS32("tab_max_width",maxTabWidth);
+ ((LLTabContainer*)tab_container)->setMaxTabWidth(maxTabWidth);
+ }
+ }
+
+ tab_container->setPanelParameters(node, parent);
+
+ if (LLFloater::getFloaterHost())
+ {
+ LLFloater::getFloaterHost()->setTabContainer(tab_container);
+ }
+
+ //parent->addChild(tab_container);
+
+ // Add all tab panels.
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ LLView *control = factory->createCtrlWidget(tab_container, child);
+ if (control && control->isPanel())
+ {
+ LLPanel* panelp = (LLPanel*)control;
+ LLString label;
+ child->getAttributeString("label", label);
+ if (label.empty())
+ {
+ label = panelp->getLabel();
+ }
+ BOOL placeholder = FALSE;
+ child->getAttributeBOOL("placeholder", placeholder);
+ tab_container->addTabPanel(panelp, label.c_str(), false,
+ NULL, NULL, 0, placeholder);
+ }
+ }
+
+ tab_container->selectFirstTab();
+
+ tab_container->postBuild();
+
+ tab_container->initButtons(); // now that we have the correct rect
+
+ return tab_container;
+}
+
+void LLTabContainerCommon::insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point)
+{
+ switch(insertion_point)
+ {
+ case START:
+ // insert the new tab in the front of the list
+ mTabList.insert(mTabList.begin(), tuple);
+ break;
+ case RIGHT_OF_CURRENT:
+ // insert the new tab after the current tab
+ {
+ tuple_list_t::iterator current_iter = mTabList.begin() + mCurrentTabIdx + 1;
+ mTabList.insert(current_iter, tuple);
+ }
+ break;
+ case END:
+ default:
+ mTabList.push_back( tuple );
+ }
+}
+
+
+LLTabContainer::LLTabContainer(
+ const LLString& name, const LLRect& rect, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title, BOOL bordered )
+ :
+ LLTabContainerCommon(name, rect, pos, close_callback, callback_userdata, bordered),
+ mLeftArrowBtn(NULL),
+ mRightArrowBtn(NULL),
+ mRightTabBtnOffset(0),
+ mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
+ mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
+ mTotalTabWidth(0)
+{
+ initButtons( );
+}
+
+LLTabContainer::LLTabContainer(
+ const LLString& name, const LLString& rect_control, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title, BOOL bordered )
+ :
+ LLTabContainerCommon(name, rect_control, pos, close_callback, callback_userdata, bordered),
+ mLeftArrowBtn(NULL),
+ mRightArrowBtn(NULL),
+ mRightTabBtnOffset(0),
+ mMinTabWidth(TABCNTR_TAB_MIN_WIDTH),
+ mMaxTabWidth(TABCNTR_TAB_MAX_WIDTH),
+ mTotalTabWidth(0)
+{
+ initButtons( );
+}
+
+void LLTabContainer::initButtons()
+{
+ // Hack:
+ if (mRect.getHeight() == 0 || mLeftArrowBtn)
+ {
+ return; // Don't have a rect yet or already got called
+ }
+
+ LLString out_id;
+ LLString in_id;
+
+ S32 arrow_fudge = 1; // match new art better
+
+ // tabs on bottom reserve room for resize handle (just in case)
+ if (mTabPosition == BOTTOM)
+ {
+ mRightTabBtnOffset = RESIZE_HANDLE_WIDTH;
+ }
+
+ // Left and right scroll arrows (for when there are too many tabs to show all at once).
+ S32 btn_top = (mTabPosition == TOP ) ? mRect.getHeight() - mTopBorderHeight : TABCNTR_ARROW_BTN_SIZE + 1;
+
+ LLRect left_arrow_btn_rect;
+ left_arrow_btn_rect.setLeftTopAndSize( LLPANEL_BORDER_WIDTH+1, btn_top + arrow_fudge, TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
+
+ S32 right_pad = TABCNTR_ARROW_BTN_SIZE + LLPANEL_BORDER_WIDTH + 1;
+ LLRect right_arrow_btn_rect;
+ right_arrow_btn_rect.setLeftTopAndSize( mRect.getWidth() - mRightTabBtnOffset - right_pad,
+ btn_top + arrow_fudge,
+ TABCNTR_ARROW_BTN_SIZE, TABCNTR_ARROW_BTN_SIZE );
+
+ out_id = "UIImgBtnScrollLeftOutUUID";
+ in_id = "UIImgBtnScrollLeftInUUID";
+ mLeftArrowBtn = new LLButton(
+ "Left Arrow", left_arrow_btn_rect,
+ out_id, in_id, "",
+ &LLTabContainer::onPrevBtn, this, LLFontGL::sSansSerif );
+ mLeftArrowBtn->setHeldDownCallback(onPrevBtnHeld);
+ mLeftArrowBtn->setFollowsLeft();
+ mLeftArrowBtn->setSaveToXML(false);
+ mLeftArrowBtn->setTabStop(FALSE);
+ addChild(mLeftArrowBtn);
+
+ out_id = "UIImgBtnScrollRightOutUUID";
+ in_id = "UIImgBtnScrollRightInUUID";
+ mRightArrowBtn = new LLButton(
+ "Right Arrow", right_arrow_btn_rect,
+ out_id, in_id, "",
+ &LLTabContainer::onNextBtn, this,
+ LLFontGL::sSansSerif);
+ mRightArrowBtn->setFollowsRight();
+ mRightArrowBtn->setHeldDownCallback(onNextBtnHeld);
+ mRightArrowBtn->setSaveToXML(false);
+ mRightArrowBtn->setTabStop(FALSE);
+ addChild(mRightArrowBtn);
+
+ if( mTabPosition == TOP )
+ {
+ mRightArrowBtn->setFollowsTop();
+ mLeftArrowBtn->setFollowsTop();
+ }
+ else
+ {
+ mRightArrowBtn->setFollowsBottom();
+ mLeftArrowBtn->setFollowsBottom();
+ }
+
+ // set default tab group to be panel contents
+ mDefaultTabGroup = 1;
+}
+
+LLTabContainer::~LLTabContainer()
+{
+}
+
+void LLTabContainer::addTabPanel(LLPanel* child,
+ const LLString& label,
+ BOOL select,
+ void (*on_tab_clicked)(void*, bool),
+ void* userdata,
+ S32 indent,
+ BOOL placeholder,
+ eInsertionPoint insertion_point)
+{
+ if (child->getParent() == this)
+ {
+ // already a child of mine
+ return;
+ }
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+ // Store the original label for possible xml export.
+ child->setLabel(label);
+ LLString trimmed_label = label;
+ LLString::trim(trimmed_label);
+
+ S32 button_width = llclamp(font->getWidth(trimmed_label) + TAB_PADDING, mMinTabWidth, mMaxTabWidth);
+
+ // Tab panel
+ S32 tab_panel_top;
+ S32 tab_panel_bottom;
+ if( LLTabContainer::TOP == mTabPosition )
+ {
+ tab_panel_top = mRect.getHeight() - mTopBorderHeight - (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP);
+ tab_panel_bottom = LLPANEL_BORDER_WIDTH;
+ }
+ else
+ {
+ tab_panel_top = mRect.getHeight() - mTopBorderHeight;
+ tab_panel_bottom = (TABCNTR_TAB_HEIGHT - TABCNTR_BUTTON_PANEL_OVERLAP); // Run to the edge, covering up the border
+ }
+
+ LLRect tab_panel_rect(
+ LLPANEL_BORDER_WIDTH,
+ tab_panel_top,
+ mRect.getWidth()-LLPANEL_BORDER_WIDTH,
+ tab_panel_bottom );
+
+ child->setFollowsAll();
+ child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
+ child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
+ child->setBackgroundVisible( FALSE ); // No need to overdraw
+ // add this child later
+
+ child->setVisible( FALSE ); // Will be made visible when selected
+
+ mTotalTabWidth += button_width;
+
+ // Tab button
+ LLRect btn_rect; // Note: btn_rect.mLeft is just a dummy. Will be updated in draw().
+ LLString tab_img;
+ LLString tab_selected_img;
+ S32 tab_fudge = 1; // To make new tab art look better, nudge buttons up 1 pel
+
+ if( LLTabContainer::TOP == mTabPosition )
+ {
+ btn_rect.setLeftTopAndSize( 0, mRect.getHeight() - mTopBorderHeight + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
+ tab_img = "UIImgBtnTabTopOutUUID";
+ tab_selected_img = "UIImgBtnTabTopInUUID";
+ }
+ else
+ {
+ btn_rect.setOriginAndSize( 0, 0 + tab_fudge, button_width, TABCNTR_TAB_HEIGHT );
+ tab_img = "UIImgBtnTabBottomOutUUID";
+ tab_selected_img = "UIImgBtnTabBottomInUUID";
+ }
+
+ if (placeholder)
+ {
+ //FIXME: wont work for horizontal tabs
+ btn_rect.translate(0, -LLBUTTON_V_PAD-2);
+ LLString box_label = trimmed_label;
+ LLTextBox* text = new LLTextBox(box_label, btn_rect, box_label, font);
+ addChild( text, 0 );
+
+ LLButton* btn = new LLButton("", LLRect(0,0,0,0));
+ LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, text );
+ addChild( btn, 0 );
+ addChild( child, 1 );
+ insertTuple(tuple, insertion_point);
+ }
+ else
+ {
+ LLString tooltip = trimmed_label;
+ tooltip += "\nCtrl-[ for previous tab";
+ tooltip += "\nCtrl-] for next tab";
+
+ LLButton* btn = new LLButton(
+ LLString(child->getName()) + " tab",
+ btn_rect,
+ tab_img, tab_selected_img, "",
+ &LLTabContainer::onTabBtn, NULL, // set userdata below
+ font,
+ trimmed_label, trimmed_label );
+ btn->setSaveToXML(false);
+ btn->setVisible( FALSE );
+ btn->setToolTip( tooltip );
+ btn->setScaleImage(TRUE);
+ btn->setFixedBorder(14, 14);
+
+ // Try to squeeze in a bit more text
+ btn->setLeftHPad( 4 );
+ btn->setRightHPad( 2 );
+ btn->setHAlign(LLFontGL::LEFT);
+ btn->setTabStop(FALSE);
+ if (indent)
+ {
+ btn->setLeftHPad(indent);
+ }
+
+ if( mTabPosition == TOP )
+ {
+ btn->setFollowsTop();
+ }
+ else
+ {
+ btn->setFollowsBottom();
+ }
+
+ LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata );
+ btn->setCallbackUserData( tuple );
+ addChild( btn, 0 );
+ addChild( child, 1 );
+ insertTuple(tuple, insertion_point);
+ }
+
+ updateMaxScrollPos();
+
+ if( select )
+ {
+ selectLastTab();
+ }
+}
+
+void LLTabContainer::removeTabPanel(LLPanel* child)
+{
+ // Adjust the total tab width.
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ if( tuple->mTabPanel == child )
+ {
+ mTotalTabWidth -= tuple->mButton->getRect().getWidth();
+ break;
+ }
+ }
+
+ LLTabContainerCommon::removeTabPanel(child);
+}
+
+void LLTabContainer::setPanelTitle(S32 index, const LLString& title)
+{
+ if (index >= 0 && index < (S32)mTabList.size())
+ {
+ LLButton* tab_button = mTabList[index]->mButton;
+ const LLFontGL* fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ mTotalTabWidth -= tab_button->getRect().getWidth();
+ tab_button->reshape(llclamp(fontp->getWidth(title) + TAB_PADDING, mMinTabWidth, mMaxTabWidth), tab_button->getRect().getHeight());
+ mTotalTabWidth += tab_button->getRect().getWidth();
+ tab_button->setLabelSelected(title);
+ tab_button->setLabelUnselected(title);
+ }
+ updateMaxScrollPos();
+}
+
+
+void LLTabContainer::updateMaxScrollPos()
+{
+ S32 tab_space = 0;
+ S32 available_space = 0;
+ tab_space = mTotalTabWidth;
+ available_space = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_TAB_H_PAD);
+
+ if( tab_space > available_space )
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ // subtract off reserved portion on left
+ available_width_with_arrows -= TABCNTR_TAB_PARTIAL_WIDTH;
+
+ S32 running_tab_width = 0;
+ mMaxScrollPos = mTabList.size();
+ for(tuple_list_t::reverse_iterator tab_it = mTabList.rbegin(); tab_it != mTabList.rend(); ++tab_it)
+ {
+ running_tab_width += (*tab_it)->mButton->getRect().getWidth();
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ mMaxScrollPos--;
+ }
+ // in case last tab doesn't actually fit on screen, make it the last scrolling position
+ mMaxScrollPos = llmin(mMaxScrollPos, (S32)mTabList.size() - 1);
+ }
+ else
+ {
+ mMaxScrollPos = 0;
+ mScrollPos = 0;
+ }
+ if (mScrollPos > mMaxScrollPos)
+ {
+ mScrollPos = mMaxScrollPos;
+ }
+}
+
+void LLTabContainer::commitHoveredButton(S32 x, S32 y)
+{
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
+ {
+ tuple->mButton->onCommit();
+ }
+ }
+ }
+}
+
+void LLTabContainer::setMinTabWidth(S32 width)
+{
+ mMinTabWidth = width;
+}
+
+void LLTabContainer::setMaxTabWidth(S32 width)
+{
+ mMaxTabWidth = width;
+}
+
+S32 LLTabContainer::getMinTabWidth() const
+{
+ return mMinTabWidth;
+}
+
+S32 LLTabContainer::getMaxTabWidth() const
+{
+ return mMaxTabWidth;
+}
+
+BOOL LLTabContainer::selectTab(S32 which)
+{
+ if (which >= (S32)mTabList.size()) return FALSE;
+ if (which < 0) return FALSE;
+
+ //if( gFocusMgr.childHasKeyboardFocus( this ) )
+ //{
+ // gFocusMgr.setKeyboardFocus( NULL, NULL );
+ //}
+
+ LLTabTuple* selected_tuple = mTabList[which];
+ if (!selected_tuple)
+ {
+ return FALSE;
+ }
+
+ if (mTabList[which]->mButton->getEnabled())
+ {
+ mCurrentTabIdx = which;
+
+ S32 i = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ BOOL is_selected = ( tuple == selected_tuple );
+ tuple->mTabPanel->setVisible( is_selected );
+// tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
+ tuple->mButton->setToggleState( is_selected );
+ // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
+ tuple->mButton->setTabStop( is_selected && mTabList.size() > 1 );
+
+ if( is_selected && mMaxScrollPos > 0)
+ {
+ // Make sure selected tab is within scroll region
+ if( i < mScrollPos )
+ {
+ mScrollPos = i;
+ }
+ else
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ S32 running_tab_width = tuple->mButton->getRect().getWidth();
+ S32 j = i - 1;
+ S32 min_scroll_pos = i;
+ if (running_tab_width < available_width_with_arrows)
+ {
+ while (j >= 0)
+ {
+ LLTabTuple* other_tuple = mTabList[j];
+ running_tab_width += other_tuple->mButton->getRect().getWidth();
+ if (running_tab_width > available_width_with_arrows)
+ {
+ break;
+ }
+ j--;
+ }
+ min_scroll_pos = j + 1;
+ }
+ mScrollPos = llclamp(mScrollPos, min_scroll_pos, i);
+ mScrollPos = llmin(mScrollPos, mMaxScrollPos);
+ }
+ }
+ i++;
+ }
+ if( selected_tuple->mOnChangeCallback )
+ {
+ selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLTabContainer::draw()
+{
+ S32 target_pixel_scroll = 0;
+ S32 cur_scroll_pos = mScrollPos;
+ if (cur_scroll_pos > 0)
+ {
+ S32 available_width_with_arrows = mRect.getWidth() - mRightTabBtnOffset - 2 * (LLPANEL_BORDER_WIDTH + TABCNTR_ARROW_BTN_SIZE + 1);
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ if (cur_scroll_pos == 0)
+ {
+ break;
+ }
+ target_pixel_scroll += (*iter)->mButton->getRect().getWidth();
+ cur_scroll_pos--;
+ }
+
+ // Show part of the tab to the left of what is fully visible
+ target_pixel_scroll -= TABCNTR_TAB_PARTIAL_WIDTH;
+ // clamp so that rightmost tab never leaves right side of screen
+ target_pixel_scroll = llmin(mTotalTabWidth - available_width_with_arrows, target_pixel_scroll);
+ }
+
+ mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f));
+ if( getVisible() )
+ {
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
+ mLeftArrowBtn->setVisible( has_scroll_arrows );
+ mRightArrowBtn->setVisible( has_scroll_arrows );
+
+ // Set the leftmost position of the tab buttons.
+ S32 left = LLPANEL_BORDER_WIDTH + (has_scroll_arrows ? TABCNTR_ARROW_BTN_SIZE : TABCNTR_TAB_H_PAD);
+ left -= mScrollPosPixels;
+
+ // Hide all the buttons
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( FALSE );
+ }
+
+ LLPanel::draw();
+
+ // Show all the buttons
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ }
+
+ // Draw some of the buttons...
+
+ LLGLEnable scissor_test(has_scroll_arrows ? GL_SCISSOR_TEST : GL_FALSE);
+ if( has_scroll_arrows )
+ {
+ // ...but clip them.
+ S32 x1 = mLeftArrowBtn->getRect().mRight;
+ S32 y1 = 0;
+ S32 x2 = mRightArrowBtn->getRect().mLeft;
+ S32 y2 = 1;
+ if (mTabList.size() > 0)
+ {
+ y2 = mTabList[0]->mButton->getRect().mTop;
+ }
+ LLUI::setScissorRegionLocal(LLRect(x1, y2, x2, y1));
+ }
+
+ S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
+ S32 idx = 0;
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->translate( left - tuple->mButton->getRect().mLeft, 0 );
+ left += tuple->mButton->getRect().getWidth();
+
+ if( idx < mScrollPos )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mLeftArrowBtn->setFlashing( TRUE );
+ }
+ }
+ else
+ if( max_scroll_visible < idx )
+ {
+ if( tuple->mButton->getFlashing() )
+ {
+ mRightArrowBtn->setFlashing( TRUE );
+ }
+ }
+
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
+ tuple->mButton->draw();
+ }
+ LLUI::popMatrix();
+
+ idx++;
+ }
+
+ mLeftArrowBtn->setFlashing(FALSE);
+ mRightArrowBtn->setFlashing(FALSE);
+ }
+}
+
+
+void LLTabContainer::setRightTabBtnOffset(S32 offset)
+{
+ mRightArrowBtn->translate( -offset - mRightTabBtnOffset, 0 );
+ mRightTabBtnOffset = offset;
+ updateMaxScrollPos();
+}
+
+BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleMouseDown(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleMouseDown(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseDown( x, y, mask );
+ }
+
+ if (mTabList.size() > 0)
+ {
+ LLTabTuple* firsttuple = mTabList[0];
+ LLRect tab_rect(has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
+ firsttuple->mButton->getRect().mTop,
+ has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
+ firsttuple->mButton->getRect().mBottom );
+ if( tab_rect.pointInRect( x, y ) )
+ {
+ LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton;
+ gFocusMgr.setMouseCapture(this, NULL);
+ gFocusMgr.setKeyboardFocus(tab_button, NULL);
+ }
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleHover( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleHover(x, y, mask);
+ }
+
+ commitHoveredButton(x, y);
+ return handled;
+}
+
+BOOL LLTabContainer::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ handled = mLeftArrowBtn->handleMouseUp(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ handled = mRightArrowBtn->handleMouseUp(local_x, local_y, mask);
+ }
+ }
+ if (!handled)
+ {
+ handled = LLPanel::handleMouseUp( x, y, mask );
+ }
+
+ commitHoveredButton(x, y);
+ LLPanel* cur_panel = getCurrentPanel();
+ if (gFocusMgr.getMouseCapture() == this)
+ {
+ if (cur_panel)
+ {
+ if (!cur_panel->focusFirstItem(FALSE))
+ {
+ // if nothing in the panel gets focus, make sure the new tab does
+ // otherwise the last tab might keep focus
+ mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE);
+ }
+ }
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect )
+{
+ BOOL handled = LLPanel::handleToolTip( x, y, msg, sticky_rect );
+ if (!handled && mTabList.size() > 0 && getVisible() && pointInView( x, y ) )
+ {
+ LLTabTuple* firsttuple = mTabList[0];
+
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+ LLRect clip(
+ has_scroll_arrows ? mLeftArrowBtn->getRect().mRight : mLeftArrowBtn->getRect().mLeft,
+ firsttuple->mButton->getRect().mTop,
+ has_scroll_arrows ? mRightArrowBtn->getRect().mLeft : mRightArrowBtn->getRect().mRight,
+ 0 );
+ if( clip.pointInRect( x, y ) )
+ {
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ handled = tuple->mButton->handleToolTip( local_x, local_y, msg, sticky_rect );
+ if( handled )
+ {
+ break;
+ }
+ }
+ }
+
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( FALSE );
+ }
+ }
+ return handled;
+}
+
+BOOL LLTabContainer::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (!getEnabled()) return FALSE;
+
+ if (!gFocusMgr.childHasKeyboardFocus(this)) return FALSE;
+
+ BOOL handled = FALSE;
+ if (key == '[' && mask == MASK_CONTROL)
+ {
+ selectPrevTab();
+ handled = TRUE;
+ }
+ else if (key == ']' && mask == MASK_CONTROL)
+ {
+ selectNextTab();
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
+ if (getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ }
+
+ if (!gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
+ {
+ // if child has focus, but not the current panel, focus
+ // is on a button
+ switch(key)
+ {
+ case KEY_UP:
+ if (getTabPosition() == BOTTOM && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ handled = TRUE;
+ break;
+ case KEY_DOWN:
+ if (getTabPosition() == TOP && getCurrentPanel())
+ {
+ getCurrentPanel()->setFocus(TRUE);
+ }
+ handled = TRUE;
+ break;
+ case KEY_LEFT:
+ selectPrevTab();
+ handled = TRUE;
+ break;
+ case KEY_RIGHT:
+ selectNextTab();
+ handled = TRUE;
+ break;
+ default:
+ break;
+ }
+ }
+ return handled;
+}
+
+// virtual
+LLXMLNodePtr LLTabContainer::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLTabContainerCommon::getXML();
+
+ node->createChild("tab_position", TRUE)->setStringValue((mTabPosition == TOP ? "top" : "bottom"));
+
+ return node;
+}
+
+BOOL LLTabContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance *accept, LLString &tooltip)
+{
+ BOOL has_scroll_arrows = (mMaxScrollPos > 0);
+
+ if (has_scroll_arrows)
+ {
+ if (mLeftArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mLeftArrowBtn->getRect().mLeft;
+ S32 local_y = y - mLeftArrowBtn->getRect().mBottom;
+ mLeftArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ else if (mRightArrowBtn->getRect().pointInRect(x, y))
+ {
+ S32 local_x = x - mRightArrowBtn->getRect().mLeft;
+ S32 local_y = y - mRightArrowBtn->getRect().mBottom;
+ mRightArrowBtn->handleHover(local_x, local_y, mask);
+ }
+ }
+
+ for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
+ {
+ LLTabTuple* tuple = *iter;
+ tuple->mButton->setVisible( TRUE );
+ S32 local_x = x - tuple->mButton->getRect().mLeft;
+ S32 local_y = y - tuple->mButton->getRect().mBottom;
+ if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
+ {
+ tuple->mButton->onCommit();
+ }
+ }
+
+ return LLView::handleDragAndDrop(x, y, mask, drop, type, cargo_data, accept, tooltip);
+}
+
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
new file mode 100644
index 0000000000..41e602eaea
--- /dev/null
+++ b/indra/llui/lltabcontainer.h
@@ -0,0 +1,242 @@
+/**
+ * @file lltabcontainer.h
+ * @brief LLTabContainerCommon base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Fear my script-fu!
+
+#ifndef LL_TABCONTAINER_H
+#define LL_TABCONTAINER_H
+
+#include "llpanel.h"
+#include "llframetimer.h"
+
+class LLButton;
+class LLTextBox;
+
+
+class LLTabContainerCommon : public LLPanel
+{
+public:
+ enum TabPosition
+ {
+ TOP,
+ BOTTOM,
+ LEFT
+ };
+ typedef enum e_insertion_point
+ {
+ START,
+ END,
+ RIGHT_OF_CURRENT
+ } eInsertionPoint;
+
+ LLTabContainerCommon( const LLString& name,
+ const LLRect& rect,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered = TRUE);
+
+ LLTabContainerCommon( const LLString& name,
+ const LLString& rect_control,
+ TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ BOOL bordered = TRUE);
+
+ virtual ~LLTabContainerCommon();
+
+ virtual void initButtons() = 0;
+
+ virtual void setValue(const LLSD& value) { selectTab((S32) value.asInteger()); }
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TAB_CONTAINER; }
+ virtual LLString getWidgetTag() const { return LL_TAB_CONTAINER_COMMON_TAG; }
+
+ virtual LLView* getChildByName(const LLString& name, BOOL recurse = FALSE) const;
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void addTabPanel(LLPanel* child,
+ const LLString& label,
+ BOOL select = FALSE,
+ void (*on_tab_clicked)(void*, bool) = NULL,
+ void* userdata = NULL,
+ S32 indent = 0,
+ BOOL placeholder = FALSE,
+ eInsertionPoint insertion_point = END) = 0;
+ virtual void addPlaceholder(LLPanel* child, const LLString& label);
+
+ virtual void enableTabButton(S32 which, BOOL enable);
+
+ virtual void removeTabPanel( LLPanel* child );
+ virtual void deleteAllTabs();
+ virtual LLPanel* getCurrentPanel();
+ virtual S32 getCurrentPanelIndex();
+ virtual S32 getTabCount();
+ virtual S32 getPanelIndexByTitle(const LLString& title);
+ virtual LLPanel* getPanelByIndex(S32 index);
+ virtual LLPanel* getPanelByName(const LLString& name);
+ virtual S32 getIndexForPanel(LLPanel* panel);
+
+ virtual void setCurrentTabName(const LLString& name);
+
+
+ virtual void selectFirstTab();
+ virtual void selectLastTab();
+ virtual BOOL selectTabPanel( LLPanel* child );
+ virtual BOOL selectTab(S32 which) = 0;
+ virtual BOOL selectTabByName(const LLString& title);
+ virtual void selectNextTab();
+ virtual void selectPrevTab();
+
+ BOOL getTabPanelFlashing(LLPanel* child);
+ void setTabPanelFlashing(LLPanel* child, BOOL state);
+ void setTitle( const LLString& title );
+ const LLString getPanelTitle(S32 index);
+
+ virtual void setTopBorderHeight(S32 height);
+
+ virtual void setTabChangeCallback(LLPanel* tab, void (*on_tab_clicked)(void*,bool));
+ virtual void setTabUserData(LLPanel* tab, void* userdata);
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ static void onCloseBtn(void* userdata);
+ static void onTabBtn(void* userdata);
+ static void onNextBtn(void* userdata);
+ static void onNextBtnHeld(void* userdata);
+ static void onPrevBtn(void* userdata);
+ static void onPrevBtnHeld(void* userdata);
+
+ virtual void setRightTabBtnOffset( S32 offset ) { }
+ virtual void setPanelTitle(S32 index, const LLString& title) { }
+
+ virtual TabPosition getTabPosition() { return mTabPosition; }
+
+
+protected:
+ // Structure used to map tab buttons to and from tab panels
+ struct LLTabTuple
+ {
+ LLTabTuple( LLTabContainerCommon* c, LLPanel* p, LLButton* b,
+ void (*cb)(void*,bool), void* userdata, LLTextBox* placeholder = NULL )
+ :
+ mTabContainer(c),
+ mTabPanel(p),
+ mButton(b),
+ mOnChangeCallback( cb ),
+ mUserData( userdata ),
+ mOldState(FALSE),
+ mPlaceholderText(placeholder)
+ {}
+
+ LLTabContainerCommon* mTabContainer;
+ LLPanel* mTabPanel;
+ LLButton* mButton;
+ void (*mOnChangeCallback)(void*, bool);
+ void* mUserData;
+ BOOL mOldState;
+ LLTextBox* mPlaceholderText;
+ };
+
+ typedef std::vector<LLTabTuple*> tuple_list_t;
+ tuple_list_t mTabList;
+ S32 mCurrentTabIdx;
+
+ BOOL mScrolled;
+ LLFrameTimer mScrollTimer;
+ S32 mScrollPos;
+ S32 mScrollPosPixels;
+ S32 mMaxScrollPos;
+
+ void (*mCloseCallback)(void*);
+ void* mCallbackUserdata;
+
+ LLTextBox* mTitleBox;
+
+ S32 mTopBorderHeight;
+ TabPosition mTabPosition;
+
+protected:
+ void scrollPrev();
+ void scrollNext();
+
+ virtual void updateMaxScrollPos() = 0;
+ virtual void commitHoveredButton(S32 x, S32 y) = 0;
+ LLTabTuple* getTabByPanel(LLPanel* child);
+ void insertTuple(LLTabTuple * tuple, eInsertionPoint insertion_point);
+};
+
+class LLTabContainer : public LLTabContainerCommon
+{
+public:
+ LLTabContainer( const LLString& name, const LLRect& rect, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title=LLString::null, BOOL bordered = TRUE );
+
+ LLTabContainer( const LLString& name, const LLString& rect_control, TabPosition pos,
+ void(*close_callback)(void*), void* callback_userdata,
+ const LLString& title=LLString::null, BOOL bordered = TRUE );
+
+ ~LLTabContainer();
+
+ /*virtual*/ void initButtons();
+
+ /*virtual*/ void draw();
+
+ /*virtual*/ void addTabPanel(LLPanel* child,
+ const LLString& label,
+ BOOL select = FALSE,
+ void (*on_tab_clicked)(void*, bool) = NULL,
+ void* userdata = NULL,
+ S32 indent = 0,
+ BOOL placeholder = FALSE,
+ eInsertionPoint insertion_point = END);
+
+ /*virtual*/ BOOL selectTab(S32 which);
+ /*virtual*/ void removeTabPanel( LLPanel* child );
+
+ /*virtual*/ void setPanelTitle(S32 index, const LLString& title);
+
+ /*virtual*/ void setRightTabBtnOffset( S32 offset );
+
+ /*virtual*/ void setMinTabWidth(S32 width);
+ /*virtual*/ void setMaxTabWidth(S32 width);
+
+ /*virtual*/ S32 getMinTabWidth() const;
+ /*virtual*/ S32 getMaxTabWidth() const;
+
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect );
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType type, void* cargo_data,
+ EAcceptance* accept, LLString& tooltip);
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+
+protected:
+
+ LLButton* mLeftArrowBtn;
+ LLButton* mRightArrowBtn;
+
+ S32 mRightTabBtnOffset; // Extra room to the right of the tab buttons.
+
+protected:
+ virtual void updateMaxScrollPos();
+ virtual void commitHoveredButton(S32 x, S32 y);
+
+ S32 mMinTabWidth;
+ S32 mMaxTabWidth;
+ S32 mTotalTabWidth;
+};
+
+const S32 TABCNTR_CLOSE_BTN_SIZE = 16;
+const S32 TABCNTR_HEADER_HEIGHT = LLPANEL_BORDER_WIDTH + TABCNTR_CLOSE_BTN_SIZE;
+
+#endif // LL_TABCONTAINER_H
diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp
new file mode 100644
index 0000000000..0cd2e98514
--- /dev/null
+++ b/indra/llui/lltextbox.cpp
@@ -0,0 +1,438 @@
+/**
+ * @file lltextbox.cpp
+ * @brief A text display widget
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lltextbox.h"
+
+#include "llerror.h"
+#include "llgl.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llcontrol.h"
+#include "llfocusmgr.h"
+#include "llstl.h"
+#include <boost/tokenizer.hpp>
+
+LLTextBox::LLTextBox(const LLString& name, const LLRect& rect, const LLString& text,
+ const LLFontGL* font, BOOL mouse_opaque)
+: LLUICtrl(name, rect, mouse_opaque, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP ),
+ mTextColor( LLUI::sColorsGroup->getColor( "LabelTextColor" ) ),
+ mDisabledColor( LLUI::sColorsGroup->getColor( "LabelDisabledColor" ) ),
+ mBackgroundColor( LLUI::sColorsGroup->getColor( "DefaultBackgroundColor" ) ),
+ mBorderColor( LLUI::sColorsGroup->getColor( "DefaultHighlightLight" ) ),
+ mBackgroundVisible( FALSE ),
+ mBorderVisible( FALSE ),
+ mDropshadowVisible( TRUE ),
+ mBorderDropShadowVisible( FALSE ),
+ mHPad(0),
+ mVPad(0),
+ mHAlign( LLFontGL::LEFT ),
+ mVAlign( LLFontGL::TOP ),
+ mClickedCallback(NULL),
+ mCallbackUserData(NULL)
+{
+ // TomY TODO Nuke this eventually
+ setText( !text.empty() ? text : name );
+ mFontGL = font ? font : LLFontGL::sSansSerifSmall;
+ setTabStop(FALSE);
+}
+
+LLTextBox::LLTextBox(const LLString& name, const LLString& text, F32 max_width,
+ const LLFontGL* font, BOOL mouse_opaque) :
+ LLUICtrl(name, LLRect(0, 0, 1, 1), mouse_opaque, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mFontGL(font ? font : LLFontGL::sSansSerifSmall),
+ mTextColor(LLUI::sColorsGroup->getColor("LabelTextColor")),
+ mDisabledColor(LLUI::sColorsGroup->getColor("LabelDisabledColor")),
+ mBackgroundColor(LLUI::sColorsGroup->getColor("DefaultBackgroundColor")),
+ mBorderColor(LLUI::sColorsGroup->getColor("DefaultHighlightLight")),
+ mBackgroundVisible(FALSE),
+ mBorderVisible(FALSE),
+ mDropshadowVisible(TRUE),
+ mBorderDropShadowVisible(FALSE),
+ mHPad(0),
+ mVPad(0),
+ mHAlign(LLFontGL::LEFT),
+ mVAlign( LLFontGL::TOP ),
+ mClickedCallback(NULL),
+ mCallbackUserData(NULL)
+{
+ setWrappedText(!text.empty() ? text : name, max_width);
+ reshapeToFitText();
+ setTabStop(FALSE);
+}
+
+LLTextBox::~LLTextBox()
+{
+}
+
+// virtual
+EWidgetType LLTextBox::getWidgetType() const
+{
+ return WIDGET_TYPE_TEXT_BOX;
+}
+
+// virtual
+LLString LLTextBox::getWidgetTag() const
+{
+ return LL_TEXT_BOX_TAG;
+}
+
+BOOL LLTextBox::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // HACK: Only do this if there actually is a click callback, so that
+ // overly large text boxes in the older UI won't start eating clicks.
+ if (mClickedCallback)
+ {
+ handled = TRUE;
+
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ if (mSoundFlags & MOUSE_DOWN)
+ {
+ make_ui_sound("UISndClick");
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLTextBox::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // We only handle the click if the click both started and ended within us
+
+ // HACK: Only do this if there actually is a click callback, so that
+ // overly large text boxes in the older UI won't start eating clicks.
+ if (mClickedCallback
+ && this == gFocusMgr.getMouseCapture())
+ {
+ handled = TRUE;
+
+ // Release the mouse
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ if (mSoundFlags & MOUSE_UP)
+ {
+ make_ui_sound("UISndClickRelease");
+ }
+
+ // DO THIS AT THE VERY END to allow the button to be destroyed as a result of being clicked.
+ // If mouseup in the widget, it's been clicked
+ if (mClickedCallback)
+ {
+ (*mClickedCallback)( mCallbackUserData );
+ }
+ }
+
+ return handled;
+}
+
+void LLTextBox::setText(const LLString& text)
+{
+ mText.assign(text);
+ setLineLengths();
+}
+
+void LLTextBox::setLineLengths()
+{
+ mLineLengthList.clear();
+
+ LLString::size_type cur = 0;
+ LLString::size_type len = mText.getWString().size();
+
+ while (cur < len)
+ {
+ LLString::size_type end = mText.getWString().find('\n', cur);
+ LLString::size_type runLen;
+
+ if (end == LLString::npos)
+ {
+ runLen = len - cur;
+ cur = len;
+ }
+ else
+ {
+ runLen = end - cur;
+ cur = end + 1; // skip the new line character
+ }
+
+ mLineLengthList.push_back( (S32)runLen );
+ }
+}
+
+void LLTextBox::setWrappedText(const LLString& in_text, F32 max_width)
+{
+ if (max_width < 0.0)
+ {
+ max_width = (F32)getRect().getWidth();
+ }
+
+ LLWString wtext = utf8str_to_wstring(in_text);
+ LLWString final_wtext;
+
+ LLWString::size_type cur = 0;;
+ LLWString::size_type len = wtext.size();
+
+ while (cur < len)
+ {
+ LLWString::size_type end = wtext.find('\n', cur);
+ if (end == LLWString::npos)
+ {
+ end = len;
+ }
+
+ LLWString::size_type runLen = end - cur;
+ if (runLen > 0)
+ {
+ LLWString run(wtext, cur, runLen);
+ LLWString::size_type useLen =
+ mFontGL->maxDrawableChars(run.c_str(), max_width, runLen, TRUE);
+
+ final_wtext.append(wtext, cur, useLen);
+ cur += useLen;
+ }
+
+ if (cur < len)
+ {
+ if (wtext[cur] == '\n')
+ {
+ cur += 1;
+ }
+ final_wtext += '\n';
+ }
+ }
+
+ LLString final_text = wstring_to_utf8str(final_wtext);
+ setText(final_text);
+}
+
+S32 LLTextBox::getTextPixelWidth()
+{
+ S32 max_line_width = 0;
+ if( mLineLengthList.size() > 0 )
+ {
+ S32 cur_pos = 0;
+ for (std::vector<S32>::iterator iter = mLineLengthList.begin();
+ iter != mLineLengthList.end(); ++iter)
+ {
+ S32 line_length = *iter;
+ S32 line_width = mFontGL->getWidth( mText.getWString().c_str(), cur_pos, line_length );
+ if( line_width > max_line_width )
+ {
+ max_line_width = line_width;
+ }
+ cur_pos += line_length+1;
+ }
+ }
+ else
+ {
+ max_line_width = mFontGL->getWidth(mText.getWString().c_str());
+ }
+ return max_line_width;
+}
+
+S32 LLTextBox::getTextPixelHeight()
+{
+ S32 num_lines = mLineLengthList.size();
+ if( num_lines < 1 )
+ {
+ num_lines = 1;
+ }
+ return (S32)(num_lines * mFontGL->getLineHeight());
+}
+
+
+void LLTextBox::setValue(const LLSD& value )
+{
+ setText(value.asString());
+}
+
+LLSD LLTextBox::getValue() const
+{
+ return LLSD(getText());
+}
+
+BOOL LLTextBox::setTextArg( const LLString& key, const LLString& text )
+{
+ mText.setArg(key, text);
+ setLineLengths();
+ return TRUE;
+}
+
+void LLTextBox::draw()
+{
+ if( getVisible() )
+ {
+ if (mBorderVisible)
+ {
+ gl_rect_2d_offset_local(getLocalRect(), 2, FALSE);
+ }
+
+ if( mBorderDropShadowVisible )
+ {
+ static LLColor4 color_drop_shadow = LLUI::sColorsGroup->getColor("ColorDropShadow");
+ static S32 drop_shadow_tooltip = LLUI::sConfigGroup->getS32("DropShadowTooltip");
+ gl_drop_shadow(0, mRect.getHeight(), mRect.getWidth(), 0,
+ color_drop_shadow, drop_shadow_tooltip);
+ }
+
+ if (mBackgroundVisible)
+ {
+ LLRect r( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ gl_rect_2d( r, mBackgroundColor );
+ }
+
+ S32 text_x = 0;
+ switch( mHAlign )
+ {
+ case LLFontGL::LEFT:
+ text_x = mHPad;
+ break;
+ case LLFontGL::HCENTER:
+ text_x = mRect.getWidth() / 2;
+ break;
+ case LLFontGL::RIGHT:
+ text_x = mRect.getWidth() - mHPad;
+ break;
+ }
+
+ S32 text_y = mRect.getHeight() - mVPad;
+
+ if ( getEnabled() )
+ {
+ drawText( text_x, text_y, mTextColor );
+ }
+ else
+ {
+ drawText( text_x, text_y, mDisabledColor );
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+void LLTextBox::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ // reparse line lengths
+ setText(mText);
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color )
+{
+ if( !mLineLengthList.empty() )
+ {
+ S32 cur_pos = 0;
+ for (std::vector<S32>::iterator iter = mLineLengthList.begin();
+ iter != mLineLengthList.end(); ++iter)
+ {
+ S32 line_length = *iter;
+ mFontGL->render(mText.getWString(), cur_pos, (F32)x, (F32)y, color,
+ mHAlign, mVAlign,
+ mDropshadowVisible ? LLFontGL::DROP_SHADOW : LLFontGL::NORMAL,
+ line_length, mRect.getWidth(), NULL, TRUE );
+ cur_pos += line_length + 1;
+ y -= llfloor(mFontGL->getLineHeight());
+ }
+ }
+ else
+ {
+ mFontGL->render(mText.getWString(), 0, (F32)x, (F32)y, color,
+ mHAlign, mVAlign,
+ mDropshadowVisible ? LLFontGL::DROP_SHADOW : LLFontGL::NORMAL,
+ S32_MAX, mRect.getWidth(), NULL, TRUE);
+ }
+}
+
+
+void LLTextBox::reshapeToFitText()
+{
+ S32 width = getTextPixelWidth();
+ S32 height = getTextPixelHeight();
+ reshape( width + 2 * mHPad, height + 2 * mVPad );
+}
+
+// virtual
+LLXMLNodePtr LLTextBox::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ // Attributes
+
+ node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mFontGL));
+
+ node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
+
+ addColorXML(node, mTextColor, "text_color", "LabelTextColor");
+ addColorXML(node, mDisabledColor, "disabled_color", "LabelDisabledColor");
+ addColorXML(node, mBackgroundColor, "bg_color", "DefaultBackgroundColor");
+ addColorXML(node, mBorderColor, "border_color", "DefaultHighlightLight");
+
+ node->createChild("bg_visible", TRUE)->setBoolValue(mBackgroundVisible);
+
+ node->createChild("border_visible", TRUE)->setBoolValue(mBorderVisible);
+
+ node->createChild("drop_shadow_visible", TRUE)->setBoolValue(mDropshadowVisible);
+
+ node->createChild("border_drop_shadow_visible", TRUE)->setBoolValue(mBorderDropShadowVisible);
+
+ node->createChild("h_pad", TRUE)->setIntValue(mHPad);
+
+ node->createChild("v_pad", TRUE)->setIntValue(mVPad);
+
+ // Contents
+
+ node->setStringValue(mText);
+
+ return node;
+}
+
+// static
+LLView* LLTextBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("text_box");
+ node->getAttributeString("name", name);
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLString text = node->getTextContents();
+
+ // TomY Yes I know this is a hack, but insert a space to make a blank text field
+ if (text == "")
+ {
+ text = " ";
+ }
+
+ LLTextBox* text_box = new LLTextBox(name,
+ LLRect(),
+ text,
+ font,
+ FALSE);
+
+ LLFontGL::HAlign halign = LLView::selectFontHAlign(node);
+ text_box->setHAlign(halign);
+
+ text_box->initFromXML(node, parent);
+
+ if(node->hasAttribute("text_color"))
+ {
+ LLColor4 color;
+ LLUICtrlFactory::getAttributeColor(node, "text_color", color);
+ text_box->setColor(color);
+ }
+
+ return text_box;
+}
diff --git a/indra/llui/lltextbox.h b/indra/llui/lltextbox.h
new file mode 100644
index 0000000000..0c09ae26b4
--- /dev/null
+++ b/indra/llui/lltextbox.h
@@ -0,0 +1,106 @@
+/**
+ * @file lltextbox.h
+ * @brief A single text item display
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTBOX_H
+#define LL_LLTEXTBOX_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llstring.h"
+#include "llfontgl.h"
+#include "lluistring.h"
+
+
+class LLTextBox
+: public LLUICtrl
+{
+public:
+ // By default, follows top and left and is mouse-opaque.
+ // If no text, text = name.
+ // If no font, uses default system font.
+ LLTextBox(const LLString& name, const LLRect& rect, const LLString& text = LLString::null,
+ const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE );
+
+ // Construct a textbox which handles word wrapping for us.
+ LLTextBox(const LLString& name, const LLString& text, F32 max_width = 200,
+ const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE );
+
+ virtual ~LLTextBox();
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ void setColor( const LLColor4& c ) { mTextColor = c; }
+ void setDisabledColor( const LLColor4& c) { mDisabledColor = c; }
+ void setBackgroundColor( const LLColor4& c) { mBackgroundColor = c; }
+ void setBorderColor( const LLColor4& c) { mBorderColor = c; }
+ void setText( const LLString& text );
+ void setWrappedText(const LLString& text, F32 max_width = -1.0);
+ // default width means use existing control width
+
+ void setBackgroundVisible(BOOL visible) { mBackgroundVisible = visible; }
+ void setBorderVisible(BOOL visible) { mBorderVisible = visible; }
+ void setDropshadowVisible(BOOL visible) { mDropshadowVisible = visible; }
+ void setBorderDropshadowVisible(BOOL visible){ mBorderDropShadowVisible = visible; }
+ void setHPad(S32 pixels) { mHPad = pixels; }
+ void setVPad(S32 pixels) { mVPad = pixels; }
+ void setRightAlign() { mHAlign = LLFontGL::RIGHT; }
+ void setHAlign( LLFontGL::HAlign align ) { mHAlign = align; }
+ void setClickedCallback( void (*cb)(void *data) ){ mClickedCallback = cb; } // mouse down and up within button
+ void setCallbackUserData( void* data ) { mCallbackUserData = data; }
+
+ const LLFontGL* getFont() const { return mFontGL; }
+
+ void reshapeToFitText();
+
+ const LLString& getText() const { return mText.getString(); }
+ S32 getTextPixelWidth();
+ S32 getTextPixelHeight();
+
+
+ virtual void setValue(const LLSD& value );
+ virtual LLSD getValue() const;
+ virtual BOOL setTextArg( const LLString& key, const LLString& text );
+
+protected:
+ void setLineLengths();
+ void drawText(S32 x, S32 y, const LLColor4& color );
+
+protected:
+ LLUIString mText;
+ const LLFontGL* mFontGL;
+ LLColor4 mTextColor;
+ LLColor4 mDisabledColor;
+
+ LLColor4 mBackgroundColor;
+ LLColor4 mBorderColor;
+
+ BOOL mBackgroundVisible;
+ BOOL mBorderVisible;
+
+ BOOL mDropshadowVisible; // Draws black dropshadow below and to the right of the text.
+ BOOL mBorderDropShadowVisible;
+
+ S32 mHPad;
+ S32 mVPad;
+ LLFontGL::HAlign mHAlign;
+ LLFontGL::VAlign mVAlign;
+
+ std::vector<S32> mLineLengthList;
+ void (*mClickedCallback)(void* data );
+ void* mCallbackUserData;
+};
+
+#endif
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
new file mode 100644
index 0000000000..a4747aef67
--- /dev/null
+++ b/indra/llui/lltexteditor.cpp
@@ -0,0 +1,4144 @@
+/**
+ * @file lltexteditor.cpp
+ * @brief LLTextEditor base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Text editor widget to let users enter a a multi-line ASCII document.
+
+#include "linden_common.h"
+
+#include "lltexteditor.h"
+
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llui.h"
+#include "lluictrlfactory.h"
+#include "llrect.h"
+#include "llfocusmgr.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+#include "llmath.h"
+
+#include "audioengine.h"
+#include "llclipboard.h"
+#include "llscrollbar.h"
+#include "llstl.h"
+#include "llkeyboard.h"
+#include "llkeywords.h"
+#include "llundo.h"
+#include "llviewborder.h"
+#include "llcontrol.h"
+#include "llimagegl.h"
+#include "llwindow.h"
+#include "llglheaders.h"
+#include <queue>
+
+//
+// Globals
+//
+
+BOOL gDebugTextEditorTips = FALSE;
+
+//
+// Constants
+//
+
+const S32 UI_TEXTEDITOR_BUFFER_BLOCK_SIZE = 512;
+
+const S32 UI_TEXTEDITOR_BORDER = 1;
+const S32 UI_TEXTEDITOR_H_PAD = 4;
+const S32 UI_TEXTEDITOR_V_PAD_TOP = 4;
+const F32 CURSOR_FLASH_DELAY = 1.0f; // in seconds
+const S32 CURSOR_THICKNESS = 2;
+const S32 SPACES_PER_TAB = 4;
+
+LLColor4 LLTextEditor::mLinkColor = LLColor4::blue;
+void (* LLTextEditor::mURLcallback)(const char*) = NULL;
+BOOL (* LLTextEditor::mSecondlifeURLcallback)(LLString) = NULL;
+
+///////////////////////////////////////////////////////////////////
+//virtuals
+BOOL LLTextCmd::canExtend(S32 pos)
+{
+ return FALSE;
+}
+
+void LLTextCmd::blockExtensions()
+{
+}
+
+BOOL LLTextCmd::extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta )
+{
+ llassert(0);
+ return 0;
+}
+
+BOOL LLTextCmd::hasExtCharValue( llwchar value )
+{
+ return FALSE;
+}
+
+// Utility funcs
+S32 LLTextCmd::insert(LLTextEditor* editor, S32 pos, const LLWString &utf8str)
+{
+ return editor->insertStringNoUndo( pos, utf8str );
+}
+S32 LLTextCmd::remove(LLTextEditor* editor, S32 pos, S32 length)
+{
+ return editor->removeStringNoUndo( pos, length );
+}
+S32 LLTextCmd::overwrite(LLTextEditor* editor, S32 pos, llwchar wc)
+{
+ return editor->overwriteCharNoUndo(pos, wc);
+}
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdInsert : public LLTextCmd
+{
+public:
+ LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws)
+ : LLTextCmd(pos, group_with_next), mString(ws)
+ {
+ }
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ *delta = insert(editor, mPos, mString );
+ LLWString::truncate(mString, *delta);
+ //mString = wstring_truncate(mString, *delta);
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ remove(editor, mPos, mString.length() );
+ return mPos;
+ }
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ insert(editor, mPos, mString );
+ return mPos + mString.length();
+ }
+
+private:
+ LLWString mString;
+};
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdAddChar : public LLTextCmd
+{
+public:
+ LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc)
+ : LLTextCmd(pos, group_with_next), mString(1, wc), mBlockExtensions(FALSE)
+ {
+ }
+ virtual void blockExtensions()
+ {
+ mBlockExtensions = TRUE;
+ }
+ virtual BOOL canExtend(S32 pos)
+ {
+ return !mBlockExtensions && (pos == mPos + (S32)mString.length());
+ }
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ *delta = insert(editor, mPos, mString);
+ LLWString::truncate(mString, *delta);
+ //mString = wstring_truncate(mString, *delta);
+ return (*delta != 0);
+ }
+ virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar wc, S32* delta )
+ {
+ LLWString ws;
+ ws += wc;
+
+ *delta = insert(editor, pos, ws);
+ if( *delta > 0 )
+ {
+ mString += wc;
+ }
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ remove(editor, mPos, mString.length() );
+ return mPos;
+ }
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ insert(editor, mPos, mString );
+ return mPos + mString.length();
+ }
+
+private:
+ LLWString mString;
+ BOOL mBlockExtensions;
+
+};
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdOverwriteChar : public LLTextCmd
+{
+public:
+ LLTextCmdOverwriteChar( S32 pos, BOOL group_with_next, llwchar wc)
+ : LLTextCmd(pos, group_with_next), mChar(wc), mOldChar(0) {}
+
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ mOldChar = editor->getWChar(mPos);
+ overwrite(editor, mPos, mChar);
+ *delta = 0;
+ return TRUE;
+ }
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ overwrite(editor, mPos, mOldChar);
+ return mPos;
+ }
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ overwrite(editor, mPos, mChar);
+ return mPos+1;
+ }
+
+private:
+ llwchar mChar;
+ llwchar mOldChar;
+};
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdRemove : public LLTextCmd
+{
+public:
+ LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len ) :
+ LLTextCmd(pos, group_with_next), mLen(len)
+ {
+ }
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ mString = editor->getWSubString(mPos, mLen);
+ *delta = remove(editor, mPos, mLen );
+ return (*delta != 0);
+ }
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ insert(editor, mPos, mString );
+ return mPos + mString.length();
+ }
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ remove(editor, mPos, mLen );
+ return mPos;
+ }
+private:
+ LLWString mString;
+ S32 mLen;
+};
+
+///////////////////////////////////////////////////////////////////
+
+//
+// Member functions
+//
+
+LLTextEditor::LLTextEditor(
+ const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString &default_text,
+ const LLFontGL* font,
+ BOOL allow_embedded_items)
+ :
+ LLUICtrl( name, rect, TRUE, NULL, NULL, FOLLOWS_TOP | FOLLOWS_LEFT ),
+ mTextIsUpToDate(TRUE),
+ mMaxTextLength( max_length ),
+ mBaseDocIsPristine(TRUE),
+ mPristineCmd( NULL ),
+ mLastCmd( NULL ),
+ mCursorPos( 0 ),
+ mIsSelecting( FALSE ),
+ mSelectionStart( 0 ),
+ mSelectionEnd( 0 ),
+ mOnScrollEndCallback( NULL ),
+ mOnScrollEndData( NULL ),
+ mCursorColor( LLUI::sColorsGroup->getColor( "TextCursorColor" ) ),
+ mFgColor( LLUI::sColorsGroup->getColor( "TextFgColor" ) ),
+ mReadOnlyFgColor( LLUI::sColorsGroup->getColor( "TextFgReadOnlyColor" ) ),
+ mWriteableBgColor( LLUI::sColorsGroup->getColor( "TextBgWriteableColor" ) ),
+ mReadOnlyBgColor( LLUI::sColorsGroup->getColor( "TextBgReadOnlyColor" ) ),
+ mFocusBgColor( LLUI::sColorsGroup->getColor( "TextBgFocusColor" ) ),
+ mReadOnly(FALSE),
+ mWordWrap( FALSE ),
+ mTabToNextField( TRUE ),
+ mCommitOnFocusLost( FALSE ),
+ mTakesFocus( TRUE ),
+ mHideScrollbarForShortDocs( FALSE ),
+ mTakesNonScrollClicks( TRUE ),
+ mAllowEmbeddedItems( allow_embedded_items ),
+ mAcceptCallingCardNames(FALSE),
+ mHandleEditKeysDirectly( FALSE ),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mLastSelectionX(-1),
+ mLastSelectionY(-1)
+{
+ mSourceID.generate();
+
+ if (font)
+ {
+ mGLFont = font;
+ }
+ else
+ {
+ mGLFont = LLFontGL::sSansSerif;
+ }
+
+ updateTextRect();
+
+ S32 line_height = llround( mGLFont->getLineHeight() );
+ S32 page_size = mTextRect.getHeight() / line_height;
+
+ // Init the scrollbar
+ LLRect scroll_rect;
+ scroll_rect.setOriginAndSize(
+ mRect.getWidth() - UI_TEXTEDITOR_BORDER - SCROLLBAR_SIZE,
+ UI_TEXTEDITOR_BORDER,
+ SCROLLBAR_SIZE,
+ mRect.getHeight() - 2 * UI_TEXTEDITOR_BORDER );
+ S32 lines_in_doc = getLineCount();
+ mScrollbar = new LLScrollbar( "Scrollbar", scroll_rect,
+ LLScrollbar::VERTICAL,
+ lines_in_doc,
+ 0,
+ page_size,
+ NULL, this );
+ mScrollbar->setFollowsRight();
+ mScrollbar->setFollowsTop();
+ mScrollbar->setFollowsBottom();
+ mScrollbar->setEnabled( TRUE );
+ mScrollbar->setVisible( TRUE );
+ mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData);
+ addChild(mScrollbar);
+
+ mBorder = new LLViewBorder( "text ed border", LLRect(0, mRect.getHeight(), mRect.getWidth(), 0), LLViewBorder::BEVEL_IN, LLViewBorder::STYLE_LINE, UI_TEXTEDITOR_BORDER );
+ addChild( mBorder );
+
+ setText(default_text);
+
+ mParseHTML=FALSE;
+ mHTML="";
+}
+
+
+LLTextEditor::~LLTextEditor()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ // Scrollbar is deleted by LLView
+ mHoverSegment = NULL;
+ std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
+
+ std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
+}
+
+//virtual
+LLString LLTextEditor::getWidgetTag() const
+{
+ return LL_TEXT_EDITOR_TAG;
+}
+
+void LLTextEditor::setTrackColor( const LLColor4& color )
+{
+ mScrollbar->setTrackColor(color);
+}
+
+void LLTextEditor::setThumbColor( const LLColor4& color )
+{
+ mScrollbar->setThumbColor(color);
+}
+
+void LLTextEditor::setHighlightColor( const LLColor4& color )
+{
+ mScrollbar->setHighlightColor(color);
+}
+
+void LLTextEditor::setShadowColor( const LLColor4& color )
+{
+ mScrollbar->setShadowColor(color);
+}
+
+void LLTextEditor::updateLineStartList(S32 startpos)
+{
+ updateSegments();
+
+ bindEmbeddedChars( mGLFont );
+
+ S32 seg_num = mSegments.size();
+ S32 seg_idx = 0;
+ S32 seg_offset = 0;
+
+ if (!mLineStartList.empty())
+ {
+ getSegmentAndOffset(startpos, &seg_idx, &seg_offset);
+ line_info t(seg_idx, seg_offset);
+ line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), t, line_info_compare());
+ if (iter != mLineStartList.begin()) --iter;
+ seg_idx = iter->mSegment;
+ seg_offset = iter->mOffset;
+ mLineStartList.erase(iter, mLineStartList.end());
+ }
+
+ while( seg_idx < seg_num )
+ {
+ mLineStartList.push_back(line_info(seg_idx,seg_offset));
+ BOOL line_ended = FALSE;
+ S32 line_width = 0;
+ while(!line_ended && seg_idx < seg_num)
+ {
+ LLTextSegment* segment = mSegments[seg_idx];
+ S32 start_idx = segment->getStart() + seg_offset;
+ S32 end_idx = start_idx;
+ while (end_idx < segment->getEnd() && mWText[end_idx] != '\n')
+ {
+ end_idx++;
+ }
+ if (start_idx == end_idx)
+ {
+ if (end_idx >= segment->getEnd())
+ {
+ // empty segment
+ seg_idx++;
+ seg_offset = 0;
+ }
+ else
+ {
+ // empty line
+ line_ended = TRUE;
+ seg_offset++;
+ }
+ }
+ else
+ {
+ const llwchar* str = mWText.c_str() + start_idx;
+ S32 drawn = mGLFont->maxDrawableChars(str, (F32)mTextRect.getWidth() - line_width,
+ end_idx - start_idx, mWordWrap, mAllowEmbeddedItems );
+ if( 0 == drawn && line_width == 0)
+ {
+ // If at the beginning of a line, draw at least one character, even if it doesn't all fit.
+ drawn = 1;
+ }
+ seg_offset += drawn;
+ line_width += mGLFont->getWidth(str, 0, drawn, mAllowEmbeddedItems);
+ end_idx = segment->getStart() + seg_offset;
+ if (end_idx < segment->getEnd())
+ {
+ line_ended = TRUE;
+ if (mWText[end_idx] == '\n')
+ {
+ seg_offset++; // skip newline
+ }
+ }
+ else
+ {
+ // finished with segment
+ seg_idx++;
+ seg_offset = 0;
+ }
+ }
+ }
+ }
+
+ unbindEmbeddedChars(mGLFont);
+
+ mScrollbar->setDocSize( getLineCount() );
+
+ if (mHideScrollbarForShortDocs)
+ {
+ BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
+ mScrollbar->setVisible(!short_doc);
+ }
+
+}
+
+////////////////////////////////////////////////////////////
+// LLTextEditor
+// Public methods
+
+//static
+BOOL LLTextEditor::isPartOfWord(llwchar c) { return (c == '_') || isalnum(c); }
+
+
+
+void LLTextEditor::truncate()
+{
+ if (mWText.size() > (size_t)mMaxTextLength)
+ {
+ LLWString::truncate(mWText, mMaxTextLength);
+ mTextIsUpToDate = FALSE;
+ }
+}
+
+void LLTextEditor::setText(const LLString &utf8str)
+{
+ mUTF8Text = utf8str;
+ mWText = utf8str_to_wstring(utf8str);
+ mTextIsUpToDate = TRUE;
+
+ truncate();
+ blockUndo();
+
+ setCursorPos(0);
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+}
+
+void LLTextEditor::setWText(const LLWString &wtext)
+{
+ mWText = wtext;
+ mUTF8Text.clear();
+ mTextIsUpToDate = FALSE;
+
+ truncate();
+ blockUndo();
+
+ setCursorPos(0);
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+}
+
+void LLTextEditor::setValue(const LLSD& value)
+{
+ setText(value.asString());
+}
+
+const LLString& LLTextEditor::getText() const
+{
+ if (!mTextIsUpToDate)
+ {
+ if (mAllowEmbeddedItems)
+ {
+ llwarns << "getText() called on text with embedded items (not supported)" << llendl;
+ }
+ mUTF8Text = wstring_to_utf8str(mWText);
+ mTextIsUpToDate = TRUE;
+ }
+ return mUTF8Text;
+}
+
+LLSD LLTextEditor::getValue() const
+{
+ return LLSD(getText());
+}
+
+void LLTextEditor::setWordWrap(BOOL b)
+{
+ mWordWrap = b;
+
+ setCursorPos(0);
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+}
+
+
+void LLTextEditor::setBorderVisible(BOOL b)
+{
+ mBorder->setVisible(b);
+}
+
+
+
+void LLTextEditor::setHideScrollbarForShortDocs(BOOL b)
+{
+ mHideScrollbarForShortDocs = b;
+
+ if (mHideScrollbarForShortDocs)
+ {
+ BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize());
+ mScrollbar->setVisible(!short_doc);
+ }
+}
+
+void LLTextEditor::selectNext(const LLString& search_text_in, BOOL case_insensitive, BOOL wrap)
+{
+ if (search_text_in.empty())
+ {
+ return;
+ }
+
+ LLWString text = getWText();
+ LLWString search_text = utf8str_to_wstring(search_text_in);
+ if (case_insensitive)
+ {
+ LLWString::toLower(text);
+ LLWString::toLower(search_text);
+ }
+
+ if (mIsSelecting)
+ {
+ LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
+
+ if (selected_text == search_text)
+ {
+ // We already have this word selected, we are searching for the next.
+ mCursorPos += search_text.size();
+ }
+ }
+
+ S32 loc = text.find(search_text,mCursorPos);
+
+ // If Maybe we wrapped, search again
+ if (wrap && (-1 == loc))
+ {
+ loc = text.find(search_text);
+ }
+
+ // If still -1, then search_text just isn't found.
+ if (-1 == loc)
+ {
+ mIsSelecting = FALSE;
+ mSelectionEnd = 0;
+ mSelectionStart = 0;
+ return;
+ }
+
+ setCursorPos(loc);
+
+ mIsSelecting = TRUE;
+ mSelectionEnd = mCursorPos;
+ mSelectionStart = llmin((S32)getLength(), (S32)(mCursorPos + search_text.size()));
+}
+
+BOOL LLTextEditor::replaceText(const LLString& search_text_in, const LLString& replace_text,
+ BOOL case_insensitive, BOOL wrap)
+{
+ BOOL replaced = FALSE;
+
+ if (search_text_in.empty())
+ {
+ return replaced;
+ }
+
+ LLWString search_text = utf8str_to_wstring(search_text_in);
+ if (mIsSelecting)
+ {
+ LLWString text = getWText();
+ LLWString selected_text = text.substr(mSelectionEnd, mSelectionStart - mSelectionEnd);
+
+ if (case_insensitive)
+ {
+ LLWString::toLower(selected_text);
+ LLWString::toLower(search_text);
+ }
+
+ if (selected_text == search_text)
+ {
+ insertText(replace_text);
+ replaced = TRUE;
+ }
+ }
+
+ selectNext(search_text_in, case_insensitive, wrap);
+ return replaced;
+}
+
+void LLTextEditor::replaceTextAll(const LLString& search_text, const LLString& replace_text, BOOL case_insensitive)
+{
+ S32 cur_pos = mScrollbar->getDocPos();
+
+ setCursorPos(0);
+ selectNext(search_text, case_insensitive, FALSE);
+
+ BOOL replaced = TRUE;
+ while ( replaced )
+ {
+ replaced = replaceText(search_text,replace_text, case_insensitive, FALSE);
+ }
+
+ mScrollbar->setDocPos(cur_pos);
+}
+
+void LLTextEditor::setTakesNonScrollClicks(BOOL b)
+{
+ mTakesNonScrollClicks = b;
+}
+
+
+// Picks a new cursor position based on the screen size of text being drawn.
+void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, BOOL round )
+{
+ setCursorPos(getCursorPosFromLocalCoord(local_x, local_y, round));
+}
+
+S32 LLTextEditor::prevWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mWText;
+ while( (cursorPos > 0) && (wtext[cursorPos-1] == ' ') )
+ {
+ cursorPos--;
+ }
+ while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) )
+ {
+ cursorPos--;
+ }
+ return cursorPos;
+}
+
+S32 LLTextEditor::nextWordPos(S32 cursorPos) const
+{
+ const LLWString& wtext = mWText;
+ while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos+1] ) )
+ {
+ cursorPos++;
+ }
+ while( (cursorPos < getLength()) && (wtext[cursorPos+1] == ' ') )
+ {
+ cursorPos++;
+ }
+ return cursorPos;
+}
+
+S32 LLTextEditor::getLineCount()
+{
+ return mLineStartList.size();
+}
+
+S32 LLTextEditor::getLineStart( S32 line )
+{
+ S32 num_lines = getLineCount();
+ if (num_lines == 0)
+ {
+ return 0;
+ }
+ line = llclamp(line, 0, num_lines-1);
+ S32 segidx = mLineStartList[line].mSegment;
+ S32 segoffset = mLineStartList[line].mOffset;
+ LLTextSegment* seg = mSegments[segidx];
+ S32 res = seg->getStart() + segoffset;
+ if (res > seg->getEnd()) llerrs << "wtf" << llendl;
+ return res;
+}
+
+// Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line.
+void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp )
+{
+ if (mLineStartList.empty())
+ {
+ *linep = 0;
+ *offsetp = startpos;
+ }
+ else
+ {
+ S32 seg_idx, seg_offset;
+ getSegmentAndOffset( startpos, &seg_idx, &seg_offset );
+
+ line_info tline(seg_idx, seg_offset);
+ line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), tline, line_info_compare());
+ if (iter != mLineStartList.begin()) --iter;
+ *linep = iter - mLineStartList.begin();
+ S32 line_start = mSegments[iter->mSegment]->getStart() + iter->mOffset;
+ *offsetp = startpos - line_start;
+ }
+}
+
+void LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp )
+{
+ if (mSegments.empty())
+ {
+ *segidxp = -1;
+ *offsetp = startpos;
+ }
+
+ LLTextSegment tseg(startpos);
+ segment_list_t::iterator seg_iter;
+ seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &tseg, LLTextSegment::compare());
+ if (seg_iter != mSegments.begin()) --seg_iter;
+ *segidxp = seg_iter - mSegments.begin();
+ *offsetp = startpos - (*seg_iter)->getStart();
+}
+
+const LLWString& LLTextEditor::getWText() const
+{
+ return mWText;
+}
+
+S32 LLTextEditor::getLength() const
+{
+ return mWText.length();
+}
+
+llwchar LLTextEditor::getWChar(S32 pos)
+{
+ return mWText[pos];
+}
+
+LLWString LLTextEditor::getWSubString(S32 pos, S32 len)
+{
+ return mWText.substr(pos, len);
+}
+
+S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round )
+{
+ // If round is true, if the position is on the right half of a character, the cursor
+ // will be put to its right. If round is false, the cursor will always be put to the
+ // character's left.
+
+ // Figure out which line we're nearest to.
+ S32 total_lines = getLineCount();
+ S32 line_height = 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
+
+ //S32 line = S32( 0.5f + ((mTextRect.mTop - local_y) / mGLFont->getLineHeight()) );
+ S32 line = (mTextRect.mTop - 1 - local_y) / line_height;
+ if (line >= total_lines)
+ {
+ return getLength(); // past the end
+ }
+
+ line = llclamp( line, 0, visible_lines ) + scroll_lines;
+
+ S32 line_start = getLineStart(line);
+ S32 next_start = getLineStart(line+1);
+ S32 line_end = (next_start != line_start) ? next_start - 1 : getLength();
+
+ if(line_start == -1)
+ {
+ return 0;
+ }
+ else
+ {
+ S32 line_len = line_end - line_start;
+ S32 pos;
+
+ if (mAllowEmbeddedItems)
+ {
+ // Figure out which character we're nearest to.
+ bindEmbeddedChars(mGLFont);
+ pos = mGLFont->charFromPixelOffset(mWText.c_str(), line_start,
+ (F32)(local_x - mTextRect.mLeft),
+ (F32)(mTextRect.getWidth()),
+ line_len,
+ round, TRUE);
+ unbindEmbeddedChars(mGLFont);
+ }
+ else
+ {
+ pos = mGLFont->charFromPixelOffset(mWText.c_str(), line_start,
+ (F32)(local_x - mTextRect.mLeft),
+ (F32)mTextRect.getWidth(),
+ line_len,
+ round);
+ }
+
+ return line_start + pos;
+ }
+}
+
+void LLTextEditor::setCursor(S32 row, S32 column)
+{
+ const llwchar* doc = mWText.c_str();
+ const char CR = 10;
+ while(row--)
+ {
+ while (CR != *doc++);
+ }
+ doc += column;
+ setCursorPos(doc - mWText.c_str());
+ updateScrollFromCursor();
+}
+
+void LLTextEditor::setCursorPos(S32 offset)
+{
+ mCursorPos = llclamp(offset, 0, (S32)getLength());
+ updateScrollFromCursor();
+}
+
+
+BOOL LLTextEditor::canDeselect()
+{
+ return hasSelection();
+}
+
+
+void LLTextEditor::deselect()
+{
+ mSelectionStart = 0;
+ mSelectionEnd = 0;
+ mIsSelecting = FALSE;
+}
+
+
+void LLTextEditor::startSelection()
+{
+ if( !mIsSelecting )
+ {
+ mIsSelecting = TRUE;
+ mSelectionStart = mCursorPos;
+ mSelectionEnd = mCursorPos;
+ }
+}
+
+void LLTextEditor::endSelection()
+{
+ if( mIsSelecting )
+ {
+ mIsSelecting = FALSE;
+ mSelectionEnd = mCursorPos;
+ }
+ if (mParseHTML && mHTML.length() > 0)
+ {
+ //Special handling for slurls
+ if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) )
+ {
+ if (mURLcallback!=NULL) (*mURLcallback)(mHTML.c_str());
+
+ //load_url(url.c_str());
+ }
+ mHTML="";
+ }
+}
+
+BOOL LLTextEditor::selectionContainsLineBreaks()
+{
+ if (hasSelection())
+ {
+ S32 left = llmin(mSelectionStart, mSelectionEnd);
+ S32 right = left + abs(mSelectionStart - mSelectionEnd);
+
+ const LLWString &wtext = mWText;
+ for( S32 i = left; i < right; i++ )
+ {
+ if (wtext[i] == '\n')
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+S32 LLTextEditor::indentLine( S32 pos, S32 spaces )
+{
+ // Assumes that pos is at the start of the line
+ // spaces may be positive (indent) or negative (unindent).
+ // Returns the actual number of characters added or removed.
+
+ llassert(pos >= 0);
+ llassert(pos <= getLength() );
+
+ S32 delta_spaces = 0;
+
+ if (spaces >= 0)
+ {
+ // Indent
+ for(S32 i=0; i < spaces; i++)
+ {
+ delta_spaces += addChar(pos, ' ');
+ }
+ }
+ else
+ {
+ // Unindent
+ for(S32 i=0; i < -spaces; i++)
+ {
+ const LLWString &wtext = mWText;
+ if (wtext[pos] == ' ')
+ {
+ delta_spaces += remove( pos, 1, FALSE );
+ }
+ }
+ }
+
+ return delta_spaces;
+}
+
+void LLTextEditor::indentSelectedLines( S32 spaces )
+{
+ if( hasSelection() )
+ {
+ const LLWString &text = mWText;
+ S32 left = llmin( mSelectionStart, mSelectionEnd );
+ S32 right = left + abs( mSelectionStart - mSelectionEnd );
+ BOOL cursor_on_right = (mSelectionEnd > mSelectionStart);
+ S32 cur = left;
+
+ // Expand left to start of line
+ while( (cur > 0) && (text[cur] != '\n') )
+ {
+ cur--;
+ }
+ left = cur;
+ if( cur > 0 )
+ {
+ left++;
+ }
+
+ // Expand right to end of line
+ if( text[right - 1] == '\n' )
+ {
+ right--;
+ }
+ else
+ {
+ while( (text[right] != '\n') && (right <= getLength() ) )
+ {
+ right++;
+ }
+ }
+
+ // Find each start-of-line and indent it
+ do
+ {
+ if( text[cur] == '\n' )
+ {
+ cur++;
+ }
+
+ S32 delta_spaces = indentLine( cur, spaces );
+ if( delta_spaces > 0 )
+ {
+ cur += delta_spaces;
+ }
+ right += delta_spaces;
+
+ //text = mWText;
+
+ // Find the next new line
+ while( (cur < right) && (text[cur] != '\n') )
+ {
+ cur++;
+ }
+ }
+ while( cur < right );
+
+ if( (right < getLength()) && (text[right] == '\n') )
+ {
+ right++;
+ }
+
+ // Set the selection and cursor
+ if( cursor_on_right )
+ {
+ mSelectionStart = left;
+ mSelectionEnd = right;
+ }
+ else
+ {
+ mSelectionStart = right;
+ mSelectionEnd = left;
+ }
+ mCursorPos = mSelectionEnd;
+ }
+}
+
+
+BOOL LLTextEditor::canSelectAll()
+{
+ return TRUE;
+}
+
+void LLTextEditor::selectAll()
+{
+ mSelectionStart = getLength();
+ mSelectionEnd = 0;
+ mCursorPos = mSelectionEnd;
+}
+
+
+BOOL LLTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ if (pointInView(x, y) && getVisible())
+ {
+ for ( child_list_const_iter_t child_it = getChildList()->begin();
+ child_it != getChildList()->end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
+ {
+ return TRUE;
+ }
+ }
+
+ if( mSegments.empty() )
+ {
+ return TRUE;
+ }
+
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ BOOL has_tool_tip = FALSE;
+ has_tool_tip = cur_segment->getToolTip( msg );
+
+ if( has_tool_tip )
+ {
+ // Just use a slop area around the cursor
+ // Convert rect local to screen coordinates
+ S32 SLOP = 8;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // Pretend the mouse is over the scrollbar
+ if (getVisible())
+ {
+ return mScrollbar->handleScrollWheel( 0, 0, clicks );
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if (!(mask & MASK_SHIFT))
+ {
+ deselect();
+ }
+
+ BOOL start_select = TRUE;
+ if( start_select )
+ {
+ // If we're not scrolling (handled by child), then we're selecting
+ if (mask & MASK_SHIFT)
+ {
+ S32 old_cursor_pos = mCursorPos;
+ setCursorAtLocalPos( x, y, TRUE );
+
+ if (hasSelection())
+ {
+ /* Mac-like behavior - extend selection towards the cursor
+ if (mCursorPos < mSelectionStart
+ && mCursorPos < mSelectionEnd)
+ {
+ // ...left of selection
+ mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else if (mCursorPos > mSelectionStart
+ && mCursorPos > mSelectionEnd)
+ {
+ // ...right of selection
+ mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ */
+ // Windows behavior
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = mCursorPos;
+ }
+ // assume we're starting a drag select
+ mIsSelecting = TRUE;
+ }
+ else
+ {
+ setCursorAtLocalPos( x, y, TRUE );
+ startSelection();
+ }
+ gFocusMgr.setMouseCapture( this, &LLTextEditor::onMouseCaptureLost );
+ }
+
+ handled = TRUE;
+ }
+
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ return handled;
+}
+
+
+BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ mHoverSegment = NULL;
+ if( getVisible() )
+ {
+ if(gFocusMgr.getMouseCapture() == this )
+ {
+ if( mIsSelecting )
+ {
+ if (x != mLastSelectionX || y != mLastSelectionY)
+ {
+ mLastSelectionX = x;
+ mLastSelectionY = y;
+ }
+
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ mSelectionEnd = mCursorPos;
+
+ updateScrollFromCursor();
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ handled = TRUE;
+ }
+
+ if( !handled )
+ {
+ // Pass to children
+ handled = LLView::childrenHandleHover(x, y, mask) != NULL;
+ }
+
+ if( handled )
+ {
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+ }
+
+ // Opaque
+ if( !handled && mTakesNonScrollClicks)
+ {
+ // Check to see if we're over an HTML-style link
+ if( !mSegments.empty() )
+ {
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ if(cur_segment->getStyle().isLink())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ handled = TRUE;
+ }
+ else
+ if(cur_segment->getStyle().getIsEmbeddedItem())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ //getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = TRUE;
+ }
+ mHoverSegment = cur_segment;
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ if (!mScrollbar->getVisible() || x < mRect.getWidth() - SCROLLBAR_SIZE)
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+ return handled;
+}
+
+
+BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if( mIsSelecting )
+ {
+ // Finish selection
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ endSelection();
+
+ updateScrollFromCursor();
+ }
+
+ if( !hasSelection() )
+ {
+ handleMouseUpOverSegment( x, y, mask );
+ }
+
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+
+BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ }
+
+ setCursorAtLocalPos( x, y, FALSE );
+ deselect();
+
+ const LLWString &text = mWText;
+
+ if( isPartOfWord( text[mCursorPos] ) )
+ {
+ // Select word the cursor is over
+ while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
+ {
+ mCursorPos--;
+ }
+ startSelection();
+
+ while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
+ {
+ mCursorPos++;
+ }
+
+ mSelectionEnd = mCursorPos;
+ }
+ else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
+ {
+ // Select the character the cursor is over
+ startSelection();
+ mCursorPos++;
+ mSelectionEnd = mCursorPos;
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection here.
+ mIsSelecting = FALSE;
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ handled = TRUE;
+ }
+ return handled;
+}
+
+
+// Allow calling cards to be dropped onto text fields. Append the name and
+// a carriage return.
+// virtual
+BOOL LLTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ *accept = ACCEPT_NO;
+
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+// Returns change in number of characters in mText
+
+S32 LLTextEditor::execute( LLTextCmd* cmd )
+{
+ S32 delta = 0;
+ if( cmd->execute(this, &delta) )
+ {
+ // Delete top of undo stack
+ undo_stack_t::iterator enditer = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ if (enditer != mUndoStack.begin())
+ {
+ --enditer;
+ std::for_each(mUndoStack.begin(), enditer, DeletePointer());
+ mUndoStack.erase(mUndoStack.begin(), enditer);
+ }
+ // Push the new command is now on the top (front) of the undo stack.
+ mUndoStack.push_front(cmd);
+ mLastCmd = cmd;
+ }
+ else
+ {
+ // Operation failed, so don't put it on the undo stack.
+ delete cmd;
+ }
+
+ return delta;
+}
+
+S32 LLTextEditor::insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op)
+{
+ return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr ) );
+}
+
+S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_next_op)
+{
+ return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) );
+}
+
+S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op)
+{
+ return insert(mWText.length(), wstr, group_with_next_op);
+}
+
+S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc)
+{
+ if ((S32)mWText.length() == pos)
+ {
+ return addChar(pos, wc);
+ }
+ else
+ {
+ return execute(new LLTextCmdOverwriteChar(pos, FALSE, wc));
+ }
+}
+
+// Remove a single character from the text. Tries to remove
+// a pseudo-tab (up to for spaces in a row)
+void LLTextEditor::removeCharOrTab()
+{
+ if( getEnabled() )
+ {
+ if( mCursorPos > 0 )
+ {
+ S32 chars_to_remove = 1;
+
+ const LLWString &text = mWText;
+ if (text[mCursorPos - 1] == ' ')
+ {
+ // Try to remove a "tab"
+ S32 line, offset;
+ getLineAndOffset(mCursorPos, &line, &offset);
+ if (offset > 0)
+ {
+ chars_to_remove = offset % SPACES_PER_TAB;
+ if( chars_to_remove == 0 )
+ {
+ chars_to_remove = SPACES_PER_TAB;
+ }
+
+ for( S32 i = 0; i < chars_to_remove; i++ )
+ {
+ if (text[ mCursorPos - i - 1] != ' ')
+ {
+ // Fewer than a full tab's worth of spaces, so
+ // just delete a single character.
+ chars_to_remove = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ for (S32 i = 0; i < chars_to_remove; i++)
+ {
+ setCursorPos(mCursorPos - 1);
+ remove( mCursorPos, 1, FALSE );
+ }
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ }
+}
+
+// Remove a single character from the text
+S32 LLTextEditor::removeChar(S32 pos)
+{
+ return remove( pos, 1, FALSE );
+}
+
+void LLTextEditor::removeChar()
+{
+ if (getEnabled())
+ {
+ if (mCursorPos > 0)
+ {
+ setCursorPos(mCursorPos - 1);
+ removeChar(mCursorPos);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ }
+}
+
+// Add a single character to the text
+S32 LLTextEditor::addChar(S32 pos, llwchar wc)
+{
+ if ((S32)mWText.length() == mMaxTextLength)
+ {
+ make_ui_sound("UISndBadKeystroke");
+ return 0;
+ }
+
+ if (mLastCmd && mLastCmd->canExtend(pos))
+ {
+ S32 delta = 0;
+ mLastCmd->extendAndExecute(this, pos, wc, &delta);
+ return delta;
+ }
+ else
+ {
+ return execute(new LLTextCmdAddChar(pos, FALSE, wc));
+ }
+}
+
+void LLTextEditor::addChar(llwchar wc)
+{
+ if( getEnabled() )
+ {
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+ else if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode())
+ {
+ removeChar(mCursorPos);
+ }
+
+ setCursorPos(mCursorPos + addChar( mCursorPos, wc ));
+ }
+}
+
+
+BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( mask & MASK_SHIFT )
+ {
+ handled = TRUE;
+
+ switch( key )
+ {
+ case KEY_LEFT:
+ if( 0 < mCursorPos )
+ {
+ startSelection();
+ mCursorPos--;
+ if( mask & MASK_CONTROL )
+ {
+ mCursorPos = prevWordPos(mCursorPos);
+ }
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if( mCursorPos < getLength() )
+ {
+ startSelection();
+ mCursorPos++;
+ if( mask & MASK_CONTROL )
+ {
+ mCursorPos = nextWordPos(mCursorPos);
+ }
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+
+ case KEY_UP:
+ startSelection();
+ changeLine( -1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_PAGE_UP:
+ startSelection();
+ changePage( -1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_HOME:
+ startSelection();
+ if( mask & MASK_CONTROL )
+ {
+ mCursorPos = 0;
+ }
+ else
+ {
+ startOfLine();
+ }
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_DOWN:
+ startSelection();
+ changeLine( 1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_PAGE_DOWN:
+ startSelection();
+ changePage( 1 );
+ mSelectionEnd = mCursorPos;
+ break;
+
+ case KEY_END:
+ startSelection();
+ if( mask & MASK_CONTROL )
+ {
+ mCursorPos = getLength();
+ }
+ else
+ {
+ endOfLine();
+ }
+ mSelectionEnd = mCursorPos;
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+ }
+
+
+ if( !handled && mHandleEditKeysDirectly )
+ {
+ if( (MASK_CONTROL & mask) && ('A' == key) )
+ {
+ if( canSelectAll() )
+ {
+ selectAll();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Ignore capslock key
+ if( MASK_NONE == mask )
+ {
+ handled = TRUE;
+ switch( key )
+ {
+ case KEY_UP:
+ if (mReadOnly)
+ {
+ mScrollbar->setDocPos(mScrollbar->getDocPos() - 1);
+ }
+ else
+ {
+ changeLine( -1 );
+ }
+ break;
+
+ case KEY_PAGE_UP:
+ changePage( -1 );
+ break;
+
+ case KEY_HOME:
+ if (mReadOnly)
+ {
+ mScrollbar->setDocPos(0);
+ }
+ else
+ {
+ startOfLine();
+ }
+ break;
+
+ case KEY_DOWN:
+ if (mReadOnly)
+ {
+ mScrollbar->setDocPos(mScrollbar->getDocPos() + 1);
+ }
+ else
+ {
+ changeLine( 1 );
+ }
+ break;
+
+ case KEY_PAGE_DOWN:
+ changePage( 1 );
+ break;
+
+ case KEY_END:
+ if (mReadOnly)
+ {
+ mScrollbar->setDocPos(mScrollbar->getDocPosMax());
+ }
+ else
+ {
+ endOfLine();
+ }
+ break;
+
+ case KEY_LEFT:
+ if (mReadOnly)
+ {
+ break;
+ }
+ if( hasSelection() )
+ {
+ setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd ));
+ }
+ else
+ {
+ if( 0 < mCursorPos )
+ {
+ setCursorPos(mCursorPos - 1);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ }
+ break;
+
+ case KEY_RIGHT:
+ if (mReadOnly)
+ {
+ break;
+ }
+ if( hasSelection() )
+ {
+ setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd ));
+ }
+ else
+ {
+ if( mCursorPos < getLength() )
+ {
+ setCursorPos(mCursorPos + 1);
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ }
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+ }
+
+ if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+ return handled;
+}
+
+void LLTextEditor::deleteSelection(BOOL group_with_next_op )
+{
+ if( getEnabled() && hasSelection() )
+ {
+ S32 pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = abs( mSelectionStart - mSelectionEnd );
+
+ remove( pos, length, group_with_next_op );
+
+ deselect();
+ setCursorPos(pos);
+ }
+}
+
+BOOL LLTextEditor::canCut()
+{
+ return !mReadOnly && hasSelection();
+}
+
+// cut selection to clipboard
+void LLTextEditor::cut()
+{
+ if( canCut() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = abs( mSelectionStart - mSelectionEnd );
+ gClipboard.copyFromSubstring( mWText, left_pos, length, mSourceID );
+ deleteSelection( FALSE );
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+}
+
+BOOL LLTextEditor::canCopy()
+{
+ return hasSelection();
+}
+
+
+// copy selection to clipboard
+void LLTextEditor::copy()
+{
+ if( canCopy() )
+ {
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+ S32 length = abs( mSelectionStart - mSelectionEnd );
+ gClipboard.copyFromSubstring(mWText, left_pos, length, mSourceID);
+ }
+}
+
+BOOL LLTextEditor::canPaste()
+{
+ return !mReadOnly && gClipboard.canPasteString();
+}
+
+
+// paste from clipboard
+void LLTextEditor::paste()
+{
+ if (canPaste())
+ {
+ LLUUID source_id;
+ LLWString paste = gClipboard.getPasteWString(&source_id);
+ if (!paste.empty())
+ {
+ // Delete any selected characters (the paste replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+
+ // Clean up string (replace tabs and remove characters that our fonts don't support).
+ LLWString clean_string(paste);
+ LLWString::replaceTabsWithSpaces(clean_string, SPACES_PER_TAB);
+ if( mAllowEmbeddedItems )
+ {
+ const llwchar LF = 10;
+ S32 len = clean_string.length();
+ for( S32 i = 0; i < len; i++ )
+ {
+ llwchar wc = clean_string[i];
+ if( (wc < LLFont::FIRST_CHAR) && (wc != LF) )
+ {
+ clean_string[i] = LL_UNKNOWN_CHAR;
+ }
+ else if (wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR)
+ {
+ clean_string[i] = pasteEmbeddedItem(wc);
+ }
+ }
+ }
+
+ // Insert the new text into the existing text.
+ setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE));
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+ }
+}
+
+
+BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if( mask & MASK_CONTROL )
+ {
+ handled = TRUE;
+
+ switch( key )
+ {
+ case KEY_HOME:
+ if( mask & MASK_SHIFT )
+ {
+ startSelection();
+ mCursorPos = 0;
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+ setCursorPos(0);
+ }
+ break;
+
+ case KEY_END:
+ {
+ if( mask & MASK_SHIFT )
+ {
+ startSelection();
+ }
+ else
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+ }
+ endOfDoc();
+ if( mask & MASK_SHIFT )
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ break;
+ }
+
+ case KEY_RIGHT:
+ if( mCursorPos < getLength() )
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+
+ setCursorPos(nextWordPos(mCursorPos + 1));
+ }
+ break;
+
+
+ case KEY_LEFT:
+ if( mCursorPos > 0 )
+ {
+ // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down
+ // all move the cursor as if clicking, so should deselect.
+ deselect();
+
+ setCursorPos(prevWordPos(mCursorPos - 1));
+ }
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLTextEditor::handleEditKey(const KEY key, const MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system.
+ if( KEY_DELETE == key )
+ {
+ if( canDoDelete() )
+ {
+ doDelete();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( MASK_CONTROL & mask )
+ {
+ if( 'C' == key )
+ {
+ if( canCopy() )
+ {
+ copy();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( 'V' == key )
+ {
+ if( canPaste() )
+ {
+ paste();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ else
+ if( 'X' == key )
+ {
+ if( canCut() )
+ {
+ cut();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLTextEditor::handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit)
+{
+ *return_key_hit = FALSE;
+ BOOL handled = TRUE;
+
+ switch( key )
+ {
+ case KEY_INSERT:
+ if (mask == MASK_NONE)
+ {
+ gKeyboard->toggleInsertMode();
+ }
+ break;
+
+ case KEY_BACKSPACE:
+ if( hasSelection() )
+ {
+ deleteSelection(FALSE);
+ }
+ else
+ if( 0 < mCursorPos )
+ {
+ removeCharOrTab();
+ }
+ else
+ {
+ reportBadKeystroke();
+ }
+ break;
+
+
+ case KEY_RETURN:
+ if (mask == MASK_NONE)
+ {
+ if( hasSelection() )
+ {
+ deleteSelection(FALSE);
+ }
+ autoIndent(); // TODO: make this optional
+ }
+ else
+ {
+ handled = FALSE;
+ break;
+ }
+ break;
+
+ case KEY_TAB:
+ if (mask & MASK_CONTROL)
+ {
+ handled = FALSE;
+ break;
+ }
+ if( hasSelection() && selectionContainsLineBreaks() )
+ {
+ indentSelectedLines( (mask & MASK_SHIFT) ? -SPACES_PER_TAB : SPACES_PER_TAB );
+ }
+ else
+ {
+ if( hasSelection() )
+ {
+ deleteSelection(FALSE);
+ }
+
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+
+ S32 spaces_needed = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
+ for( S32 i=0; i < spaces_needed; i++ )
+ {
+ addChar( ' ' );
+ }
+ }
+ break;
+
+ default:
+ handled = FALSE;
+ break;
+ }
+
+ return handled;
+}
+
+
+void LLTextEditor::unindentLineBeforeCloseBrace()
+{
+ if( mCursorPos >= 1 )
+ {
+ const LLWString &text = mWText;
+ if( ' ' == text[ mCursorPos - 1 ] )
+ {
+ removeCharOrTab();
+ }
+ }
+}
+
+
+BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+ BOOL selection_modified = FALSE;
+ BOOL return_key_hit = FALSE;
+ BOOL text_may_have_changed = TRUE;
+
+ if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible())
+ {
+ // Special case for TAB. If want to move to next field, report
+ // not handled and let the parent take care of field movement.
+ if (KEY_TAB == key && mTabToNextField)
+ {
+ return FALSE;
+ }
+
+ handled = handleNavigationKey( key, mask );
+ if( handled )
+ {
+ text_may_have_changed = FALSE;
+ }
+
+ if( !handled )
+ {
+ handled = handleSelectionKey( key, mask );
+ if( handled )
+ {
+ selection_modified = TRUE;
+ }
+ }
+
+ if( !handled )
+ {
+ handled = handleControlKey( key, mask );
+ if( handled )
+ {
+ selection_modified = TRUE;
+ }
+ }
+
+ if( !handled && mHandleEditKeysDirectly )
+ {
+ handled = handleEditKey( key, mask );
+ if( handled )
+ {
+ selection_modified = TRUE;
+ text_may_have_changed = TRUE;
+ }
+ }
+
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
+ {
+ if( !handled )
+ {
+ handled = handleSpecialKey( key, mask, &return_key_hit );
+ if( handled )
+ {
+ selection_modified = TRUE;
+ text_may_have_changed = TRUE;
+ }
+ }
+
+ }
+
+ if( handled )
+ {
+ mKeystrokeTimer.reset();
+
+ // Most keystrokes will make the selection box go away, but not all will.
+ if( !selection_modified &&
+ KEY_SHIFT != key &&
+ KEY_CONTROL != key &&
+ KEY_ALT != key &&
+ KEY_CAPSLOCK )
+ {
+ deselect();
+ }
+
+ if(text_may_have_changed)
+ {
+ updateLineStartList();
+ }
+ updateScrollFromCursor();
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+
+ if ( (gFocusMgr.getKeyboardFocus() == this) && getVisible())
+ {
+ // Handle most keys only if the text editor is writeable.
+ if( !mReadOnly )
+ {
+ if( '}' == uni_char )
+ {
+ unindentLineBeforeCloseBrace();
+ }
+
+ // TODO: KLW Add auto show of tool tip on (
+ addChar( uni_char );
+
+ // Keys that add characters temporarily hide the cursor
+ getWindow()->hideCursorUntilMouseMove();
+
+ handled = TRUE;
+ }
+
+ if( handled )
+ {
+ mKeystrokeTimer.reset();
+
+ // Most keystrokes will make the selection box go away, but not all will.
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+ }
+
+ return handled;
+}
+
+
+
+BOOL LLTextEditor::canDoDelete()
+{
+ return !mReadOnly && ( hasSelection() || (mCursorPos < getLength()) );
+}
+
+void LLTextEditor::doDelete()
+{
+ if( canDoDelete() )
+ {
+ if( hasSelection() )
+ {
+ deleteSelection(FALSE);
+ }
+ else
+ if( mCursorPos < getLength() )
+ {
+ S32 i;
+ S32 chars_to_remove = 1;
+ const LLWString &text = mWText;
+ if( (text[ mCursorPos ] == ' ') && (mCursorPos + SPACES_PER_TAB < getLength()) )
+ {
+ // Try to remove a full tab's worth of spaces
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+ chars_to_remove = SPACES_PER_TAB - (offset % SPACES_PER_TAB);
+ if( chars_to_remove == 0 )
+ {
+ chars_to_remove = SPACES_PER_TAB;
+ }
+
+ for( i = 0; i < chars_to_remove; i++ )
+ {
+ if( text[mCursorPos + i] != ' ' )
+ {
+ chars_to_remove = 1;
+ break;
+ }
+ }
+ }
+
+
+ for( i = 0; i < chars_to_remove; i++ )
+ {
+ setCursorPos(mCursorPos + 1);
+ removeChar();
+ }
+ }
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+
+void LLTextEditor::blockUndo()
+{
+ mBaseDocIsPristine = FALSE;
+ mLastCmd = NULL;
+ std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer());
+ mUndoStack.clear();
+}
+
+
+BOOL LLTextEditor::canUndo()
+{
+ return !mReadOnly && mLastCmd != NULL;
+}
+
+void LLTextEditor::undo()
+{
+ if( canUndo() )
+ {
+ deselect();
+
+ S32 pos = 0;
+ do
+ {
+ pos = mLastCmd->undo(this);
+ undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ if (iter != mUndoStack.end())
+ ++iter;
+ if (iter != mUndoStack.end())
+ mLastCmd = *iter;
+ else
+ mLastCmd = NULL;
+
+ } while( mLastCmd && mLastCmd->groupWithNext() );
+
+ setCursorPos(pos);
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+}
+
+BOOL LLTextEditor::canRedo()
+{
+ return !mReadOnly && (mUndoStack.size() > 0) && (mLastCmd != mUndoStack.front());
+}
+
+void LLTextEditor::redo()
+{
+ if( canRedo() )
+ {
+ deselect();
+
+ S32 pos = 0;
+ do
+ {
+ if( !mLastCmd )
+ {
+ mLastCmd = mUndoStack.back();
+ }
+ else
+ {
+ undo_stack_t::iterator iter = std::find(mUndoStack.begin(), mUndoStack.end(), mLastCmd);
+ if (iter != mUndoStack.begin())
+ mLastCmd = *(--iter);
+ else
+ mLastCmd = NULL;
+ }
+
+ if( mLastCmd )
+ {
+ pos = mLastCmd->redo(this);
+ }
+ } while(
+ mLastCmd &&
+ mLastCmd->groupWithNext() &&
+ (mLastCmd != mUndoStack.front()) );
+
+ setCursorPos(pos);
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+}
+
+
+// virtual, from LLView
+void LLTextEditor::onFocusLost()
+{
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ if (mCommitOnFocusLost)
+ {
+ onCommit();
+ }
+
+ // Make sure cursor is shown again
+ getWindow()->showCursorFromMouseMove();
+}
+
+void LLTextEditor::setEnabled(BOOL enabled)
+{
+ // just treat enabled as read-only flag
+ BOOL read_only = !enabled;
+ if (read_only != mReadOnly)
+ {
+ mReadOnly = read_only;
+ updateSegments();
+ }
+}
+
+void LLTextEditor::drawBackground()
+{
+ S32 left = 0;
+ S32 top = mRect.getHeight();
+ S32 right = mRect.getWidth();
+ S32 bottom = 0;
+
+ LLColor4 bg_color = mReadOnlyBgColor;
+
+ if( !mReadOnly )
+ {
+ if (gFocusMgr.getKeyboardFocus() == this)
+ {
+ bg_color = mFocusBgColor;
+ }
+ else
+ {
+ bg_color = mWriteableBgColor;
+ }
+ }
+ gl_rect_2d(left, top, right, bottom, bg_color);
+
+ LLView::draw();
+}
+
+// Draws the black box behind the selected text
+void LLTextEditor::drawSelectionBackground()
+{
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection() )
+ {
+ const LLWString &text = mWText;
+ const S32 text_len = getLength();
+ std::queue<S32> line_endings;
+
+ S32 line_height = llround( mGLFont->getLineHeight() );
+
+ S32 selection_left = llmin( mSelectionStart, mSelectionEnd );
+ S32 selection_right = llmax( mSelectionStart, mSelectionEnd );
+ S32 selection_left_x = mTextRect.mLeft;
+ S32 selection_left_y = mTextRect.mTop - line_height;
+ S32 selection_right_x = mTextRect.mRight;
+ S32 selection_right_y = mTextRect.mBottom;
+
+ BOOL selection_left_visible = FALSE;
+ BOOL selection_right_visible = FALSE;
+
+ // Skip through the lines we aren't drawing.
+ S32 cur_line = mScrollbar->getDocPos();
+
+ S32 left_line_num = cur_line;
+ S32 num_lines = getLineCount();
+ S32 right_line_num = num_lines - 1;
+
+ S32 line_start = -1;
+ if (cur_line >= num_lines)
+ {
+ return;
+ }
+
+ line_start = getLineStart(cur_line);
+
+ S32 left_visible_pos = line_start;
+ S32 right_visible_pos = line_start;
+
+ S32 text_y = mTextRect.mTop - line_height;
+
+ // Find the coordinates of the selected area
+ while((cur_line < num_lines))
+ {
+ S32 next_line = -1;
+ S32 line_end = text_len;
+
+ if ((cur_line + 1) < num_lines)
+ {
+ next_line = getLineStart(cur_line + 1);
+ line_end = next_line;
+
+ line_end = ( (line_end - line_start)==0 || text[next_line-1] == '\n' || text[next_line-1] == '\0' || text[next_line-1] == ' ' || text[next_line-1] == '\t' ) ? next_line-1 : next_line;
+ }
+
+ const llwchar* line = text.c_str() + line_start;
+
+ if( line_start <= selection_left && selection_left <= line_end )
+ {
+ left_line_num = cur_line;
+ selection_left_visible = TRUE;
+ selection_left_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_left - line_start, mAllowEmbeddedItems);
+ selection_left_y = text_y;
+ }
+ if( line_start <= selection_right && selection_right <= line_end )
+ {
+ right_line_num = cur_line;
+ selection_right_visible = TRUE;
+ selection_right_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_right - line_start, mAllowEmbeddedItems);
+ if (selection_right == line_end)
+ {
+ // add empty space for "newline"
+ //selection_right_x += mGLFont->getWidth("n");
+ }
+ selection_right_y = text_y;
+ }
+
+ // if selection spans end of current line...
+ if (selection_left <= line_end && line_end < selection_right && selection_left != selection_right)
+ {
+ // extend selection slightly beyond end of line
+ // to indicate selection of newline character (use "n" character to determine width)
+ const LLWString nstr(utf8str_to_wstring(LLString("n")));
+ line_endings.push(mTextRect.mLeft + mGLFont->getWidth(line, 0, line_end - line_start, mAllowEmbeddedItems) + mGLFont->getWidth(nstr.c_str()));
+ }
+
+ // move down one line
+ text_y -= line_height;
+
+ right_visible_pos = line_end;
+ line_start = next_line;
+ cur_line++;
+
+ if (selection_right_visible)
+ {
+ break;
+ }
+ }
+
+ // Draw the selection box (we're using a box instead of reversing the colors on the selected text).
+ BOOL selection_visible = (left_visible_pos <= selection_right) && (selection_left <= right_visible_pos);
+ if( selection_visible )
+ {
+ LLGLSNoTexture no_texture;
+ const LLColor4& color = mReadOnly ? mReadOnlyBgColor : mWriteableBgColor;
+ glColor3f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2] );
+
+ if( selection_left_y == selection_right_y )
+ {
+ // Draw from selection start to selection end
+ gl_rect_2d( selection_left_x, selection_left_y + line_height + 1,
+ selection_right_x, selection_right_y);
+ }
+ else
+ {
+ // Draw from selection start to the end of the first line
+ if( mTextRect.mRight == selection_left_x )
+ {
+ selection_left_x -= CURSOR_THICKNESS;
+ }
+
+ S32 line_end = line_endings.front();
+ line_endings.pop();
+ gl_rect_2d( selection_left_x, selection_left_y + line_height + 1,
+ line_end, selection_left_y );
+
+ S32 line_num = left_line_num + 1;
+ while(line_endings.size())
+ {
+ S32 vert_offset = -(line_num - left_line_num) * line_height;
+ // Draw the block between the two lines
+ gl_rect_2d( mTextRect.mLeft, selection_left_y + vert_offset + line_height + 1,
+ line_endings.front(), selection_left_y + vert_offset);
+ line_endings.pop();
+ line_num++;
+ }
+
+ // Draw from the start of the last line to selection end
+ if( mTextRect.mLeft == selection_right_x )
+ {
+ selection_right_x += CURSOR_THICKNESS;
+ }
+ gl_rect_2d( mTextRect.mLeft, selection_right_y + line_height + 1,
+ selection_right_x, selection_right_y );
+ }
+ }
+ }
+}
+
+void LLTextEditor::drawCursor()
+{
+ if( gFocusMgr.getKeyboardFocus() == this
+ && gShowTextEditCursor && !mReadOnly)
+ {
+ const LLWString &text = mWText;
+ const S32 text_len = getLength();
+
+ // Skip through the lines we aren't drawing.
+ S32 cur_pos = mScrollbar->getDocPos();
+
+ S32 num_lines = getLineCount();
+ if (cur_pos >= num_lines)
+ {
+ return;
+ }
+ S32 line_start = getLineStart(cur_pos);
+
+ F32 line_height = mGLFont->getLineHeight();
+ F32 text_y = (F32)(mTextRect.mTop) - line_height;
+
+ F32 cursor_left = 0.f;
+ F32 next_char_left = 0.f;
+ F32 cursor_bottom = 0.f;
+ BOOL cursor_visible = FALSE;
+
+ 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))
+ {
+ line_end = text_len + 1;
+ S32 next_line = -1;
+
+ if ((cur_pos + 1) < num_lines)
+ {
+ next_line = getLineStart(cur_pos + 1);
+ line_end = next_line - 1;
+ }
+
+ const llwchar* line = text.c_str() + line_start;
+
+ // Find the cursor and selection bounds
+ if( line_start <= mCursorPos && mCursorPos <= line_end )
+ {
+ cursor_visible = TRUE;
+ next_char_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, mCursorPos - line_start, mAllowEmbeddedItems );
+ cursor_left = next_char_left - 1.f;
+ cursor_bottom = text_y;
+ break;
+ }
+
+ // move down one line
+ text_y -= line_height;
+ line_start = next_line;
+ cur_pos++;
+ }
+
+ // Draw the cursor
+ if( cursor_visible )
+ {
+ // (Flash the cursor every half second starting a fixed time after the last keystroke)
+ F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
+ if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
+ {
+ F32 cursor_top = cursor_bottom + line_height + 1.f;
+ F32 cursor_right = cursor_left + (F32)CURSOR_THICKNESS;
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
+ {
+ cursor_left += CURSOR_THICKNESS;
+ const LLWString space(utf8str_to_wstring(LLString(" ")));
+ F32 spacew = mGLFont->getWidthF32(space.c_str());
+ if (mCursorPos == line_end)
+ {
+ cursor_right = cursor_left + spacew;
+ }
+ else
+ {
+ F32 width = mGLFont->getWidthF32(text.c_str(), mCursorPos, 1, mAllowEmbeddedItems);
+ cursor_right = cursor_left + llmax(spacew, width);
+ }
+ }
+
+ LLGLSNoTexture no_texture;
+
+ glColor4fv( mCursorColor.mV );
+
+ gl_rect_2d(llfloor(cursor_left), llfloor(cursor_top),
+ llfloor(cursor_right), llfloor(cursor_bottom));
+
+ if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n')
+ {
+ LLTextSegment* segmentp = getSegmentAtOffset(mCursorPos);
+ LLColor4 text_color;
+ if (segmentp)
+ {
+ text_color = segmentp->getColor();
+ }
+ else if (mReadOnly)
+ {
+ text_color = mReadOnlyFgColor;
+ }
+ else
+ {
+ text_color = mFgColor;
+ }
+ LLGLSTexture texture;
+ mGLFont->render(text, mCursorPos, next_char_left, cursor_bottom + line_height,
+ LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f),
+ LLFontGL::LEFT, LLFontGL::TOP,
+ LLFontGL::NORMAL,
+ 1);
+ }
+
+
+ }
+ }
+ }
+}
+
+
+void LLTextEditor::drawText()
+{
+ const LLWString &text = mWText;
+ const S32 text_len = getLength();
+
+ if( text_len > 0 )
+ {
+ S32 selection_left = -1;
+ S32 selection_right = -1;
+ // Draw selection even if we don't have keyboard focus for search/replace
+ if( hasSelection())
+ {
+ selection_left = llmin( mSelectionStart, mSelectionEnd );
+ selection_right = llmax( mSelectionStart, mSelectionEnd );
+ }
+
+ LLGLSUIDefault gls_ui;
+
+ S32 cur_line = mScrollbar->getDocPos();
+ S32 num_lines = getLineCount();
+ if (cur_line >= num_lines)
+ {
+ return;
+ }
+
+ S32 line_start = getLineStart(cur_line);
+ LLTextSegment t(line_start);
+ segment_list_t::iterator seg_iter;
+ seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &t, LLTextSegment::compare());
+ if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter;
+ LLTextSegment* cur_segment = *seg_iter;
+
+ S32 line_height = llround( mGLFont->getLineHeight() );
+ F32 text_y = (F32)(mTextRect.mTop - line_height);
+ while((mTextRect.mBottom <= text_y) && (cur_line < num_lines))
+ {
+ S32 next_start = -1;
+ S32 line_end = text_len;
+
+ if ((cur_line + 1) < num_lines)
+ {
+ next_start = getLineStart(cur_line + 1);
+ line_end = next_start;
+ }
+ if ( text[line_end-1] == '\n' )
+ {
+ --line_end;
+ }
+
+ F32 text_x = (F32)mTextRect.mLeft;
+
+ S32 seg_start = line_start;
+ while( seg_start < line_end )
+ {
+ while( cur_segment->getEnd() <= seg_start )
+ {
+ seg_iter++;
+ if (seg_iter == mSegments.end())
+ {
+ llwarns << "Ran off the segmentation end!" << llendl;
+ return;
+ }
+ cur_segment = *seg_iter;
+ }
+
+ // Draw a segment within the line
+ S32 clipped_end = llmin( line_end, cur_segment->getEnd() );
+ S32 clipped_len = clipped_end - seg_start;
+ if( clipped_len > 0 )
+ {
+ LLStyle style = cur_segment->getStyle();
+ if ( style.isImage() && (cur_segment->getStart() >= seg_start) && (cur_segment->getStart() <= clipped_end))
+ {
+ LLImageGL *image = style.getImage();
+
+ gl_draw_scaled_image( llround(text_x), llround(text_y)+line_height-style.mImageHeight, style.mImageWidth, style.mImageHeight, image, LLColor4::white );
+
+ }
+
+ if (cur_segment == mHoverSegment && style.getIsEmbeddedItem())
+ {
+ style.mUnderline = TRUE;
+ }
+
+ S32 left_pos = llmin( mSelectionStart, mSelectionEnd );
+
+ if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) )
+ {
+ mHTML = style.getLinkHREF();
+ }
+
+ drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x );
+
+ // Note: text_x is incremented by drawClippedSegment()
+ seg_start += clipped_len;
+ }
+ }
+
+ // move down one line
+ text_y -= (F32)line_height;
+
+ line_start = next_start;
+ cur_line++;
+ }
+ }
+}
+
+// Draws a single text segment, reversing the color for selection if needed.
+void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyle& style, F32* right_x )
+{
+ const LLFontGL* font = mGLFont;
+
+ LLColor4 color;
+
+ if (!style.isVisible())
+ {
+ return;
+ }
+
+ color = style.getColor();
+
+ if ( style.getFontString()[0] )
+ {
+ font = gResMgr->getRes(style.getFontID());
+ }
+
+ U8 font_flags = LLFontGL::NORMAL;
+
+ if (style.mBold)
+ {
+ font_flags |= LLFontGL::BOLD;
+ }
+ if (style.mItalic)
+ {
+ font_flags |= LLFontGL::ITALIC;
+ }
+ if (style.mUnderline)
+ {
+ font_flags |= LLFontGL::UNDERLINE;
+ }
+
+ if (style.getIsEmbeddedItem())
+ {
+ if (mReadOnly)
+ {
+ color = LLUI::sColorsGroup->getColor("TextEmbeddedItemReadOnlyColor");
+ }
+ else
+ {
+ color = LLUI::sColorsGroup->getColor("TextEmbeddedItemColor");
+ }
+ }
+
+ F32 y_top = y + (F32)llround(font->getLineHeight());
+
+ if( selection_left > seg_start )
+ {
+ // Draw normally
+ S32 start = seg_start;
+ S32 end = llmin( selection_left, seg_end );
+ S32 length = end - start;
+ font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, font_flags, length, S32_MAX, right_x, mAllowEmbeddedItems);
+ }
+ x = *right_x;
+
+ if( (selection_left < seg_end) && (selection_right > seg_start) )
+ {
+ // Draw reversed
+ S32 start = llmax( selection_left, seg_start );
+ S32 end = llmin( selection_right, seg_end );
+ S32 length = end - start;
+
+ font->render(text, start, x, y_top,
+ LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP, font_flags, length, S32_MAX, right_x, mAllowEmbeddedItems);
+ }
+ x = *right_x;
+ if( selection_right < seg_end )
+ {
+ // Draw normally
+ S32 start = llmax( selection_right, seg_start );
+ S32 end = seg_end;
+ S32 length = end - start;
+ font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, font_flags, length, S32_MAX, right_x, mAllowEmbeddedItems);
+ }
+ }
+
+
+void LLTextEditor::draw()
+{
+ if( getVisible() )
+ {
+ {
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth() - (mScrollbar->getVisible() ? SCROLLBAR_SIZE : 0), 0));
+
+ bindEmbeddedChars( mGLFont );
+
+ drawBackground();
+ drawSelectionBackground();
+ drawText();
+ drawCursor();
+
+ unbindEmbeddedChars( mGLFont );
+
+ //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret
+ // when in readonly mode
+ mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly);
+ }
+ LLView::draw(); // Draw children (scrollbar and border)
+ }
+}
+
+void LLTextEditor::reportBadKeystroke()
+{
+ make_ui_sound("UISndBadKeystroke");
+}
+
+
+void LLTextEditor::onTabInto()
+{
+ // selecting all on tabInto causes users to hit tab twice and replace their text with a tab character
+ // theoretically, one could selectAll if mTabToNextField is true, but we couldn't think of a use case
+ // where you'd want to select all anyway
+ // preserve insertion point when returning to the editor
+ //selectAll();
+}
+
+void LLTextEditor::clear()
+{
+ setText("");
+}
+
+// Start or stop the editor from accepting text-editing keystrokes
+// see also LLLineEditor
+void LLTextEditor::setFocus( BOOL new_state )
+{
+ BOOL old_state = hasFocus();
+
+ // Don't change anything if the focus state didn't change
+ if (new_state == old_state) return;
+
+ LLUICtrl::setFocus( new_state );
+
+ if( new_state )
+ {
+ // Route menu to this class
+ gEditMenuHandler = this;
+
+ // Don't start the cursor flashing right away
+ mKeystrokeTimer.reset();
+ }
+ else
+ {
+ // Route menu back to the default
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ endSelection();
+ }
+}
+
+BOOL LLTextEditor::acceptsTextInput() const
+{
+ return !mReadOnly;
+}
+
+// Given a line (from the start of the doc) and an offset into the line, find the offset (pos) into text.
+S32 LLTextEditor::getPos( S32 line, S32 offset )
+{
+ S32 line_start = getLineStart(line);
+ S32 next_start = getLineStart(line+1);
+ if (next_start == line_start)
+ {
+ next_start = getLength() + 1;
+ }
+ S32 line_length = next_start - line_start - 1;
+ line_length = llmax(line_length, 0);
+ return line_start + llmin( offset, line_length );
+}
+
+
+void LLTextEditor::changePage( S32 delta )
+{
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+
+ // allow one line overlap
+ S32 page_size = mScrollbar->getPageSize() - 1;
+ if( delta == -1 )
+ {
+ line = llmax( line - page_size, 0);
+ setCursorPos(getPos( line, offset ));
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - page_size );
+ }
+ else
+ if( delta == 1 )
+ {
+ setCursorPos(getPos( line + page_size, offset ));
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + page_size );
+ }
+ if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+}
+
+void LLTextEditor::changeLine( S32 delta )
+{
+ bindEmbeddedChars( mGLFont );
+
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+
+ S32 line_start = getLineStart(line);
+
+ S32 desired_x_pixel;
+
+ desired_x_pixel = mGLFont->getWidth(mWText.c_str(), line_start, offset, mAllowEmbeddedItems );
+
+ S32 new_line = 0;
+ if( (delta < 0) && (line > 0 ) )
+ {
+ new_line = line - 1;
+ }
+ else
+ if( (delta > 0) && (line < (getLineCount() - 1)) )
+ {
+ new_line = line + 1;
+ }
+ else
+ {
+ unbindEmbeddedChars( mGLFont );
+ return;
+ }
+
+ S32 num_lines = getLineCount();
+ S32 new_line_start = getLineStart(new_line);
+ S32 new_line_end = getLength();
+ if (new_line + 1 < num_lines)
+ {
+ new_line_end = getLineStart(new_line + 1) - 1;
+ }
+
+ S32 new_line_len = new_line_end - new_line_start;
+
+ S32 new_offset;
+ new_offset = mGLFont->charFromPixelOffset(mWText.c_str(), new_line_start,
+ (F32)desired_x_pixel,
+ (F32)mTextRect.getWidth(),
+ new_line_len,
+ mAllowEmbeddedItems);
+
+ setCursorPos (getPos( new_line, new_offset ));
+ unbindEmbeddedChars( mGLFont );
+}
+
+void LLTextEditor::startOfLine()
+{
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+ setCursorPos(mCursorPos - offset);
+}
+
+
+// public
+void LLTextEditor::setCursorAndScrollToEnd()
+{
+ deselect();
+ endOfDoc();
+ updateScrollFromCursor();
+}
+
+
+void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap )
+{
+ if( include_wordwrap )
+ {
+ getLineAndOffset( mCursorPos, line, col );
+ }
+ else
+ {
+ const LLWString &text = mWText;
+ S32 line_count = 0;
+ S32 line_start = 0;
+ S32 i;
+ for( i = 0; text[i] && (i < mCursorPos); i++ )
+ {
+ if( '\n' == text[i] )
+ {
+ line_start = i + 1;
+ line_count++;
+ }
+ }
+ *line = line_count;
+ *col = i - line_start;
+ }
+}
+
+
+void LLTextEditor::endOfLine()
+{
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+ S32 num_lines = getLineCount();
+ if (line + 1 >= num_lines)
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos( getLineStart(line + 1) - 1 );
+ }
+}
+
+void LLTextEditor::endOfDoc()
+{
+ mScrollbar->setDocPos( mScrollbar->getDocPosMax() );
+ S32 len = getLength();
+ if( len )
+ {
+ setCursorPos(len);
+ }
+ if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+}
+
+// Sets the scrollbar from the cursor position
+void LLTextEditor::updateScrollFromCursor()
+{
+ mScrollbar->setDocSize( getLineCount() );
+
+ if (mReadOnly)
+ {
+ // no cursor in read only mode
+ return;
+ }
+
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+
+ S32 page_size = mScrollbar->getPageSize();
+
+ if( line < mScrollbar->getDocPos() )
+ {
+ // scroll so that the cursor is at the top of the page
+ mScrollbar->setDocPos( line );
+ }
+ else if( line >= mScrollbar->getDocPos() + page_size - 1 )
+ {
+ S32 new_pos = 0;
+ if( line < mScrollbar->getDocSize() - 1 )
+ {
+ // scroll so that the cursor is one line above the bottom of the page,
+ new_pos = line - page_size + 1;
+ }
+ else
+ {
+ // if there is less than a page of text remaining, scroll so that the cursor is at the bottom
+ new_pos = mScrollbar->getDocPosMax();
+ }
+ mScrollbar->setDocPos( new_pos );
+ }
+
+ // Check if we've scrolled to bottom for callback if asked for callback
+ if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()))
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+}
+
+void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape( width, height, called_from_parent );
+
+ updateTextRect();
+
+ S32 line_height = llround( mGLFont->getLineHeight() );
+ S32 page_lines = mTextRect.getHeight() / line_height;
+ mScrollbar->setPageSize( page_lines );
+
+ updateLineStartList();
+}
+
+void LLTextEditor::autoIndent()
+{
+ // Count the number of spaces in the current line
+ S32 line, offset;
+ getLineAndOffset( mCursorPos, &line, &offset );
+ S32 line_start = getLineStart(line);
+ S32 space_count = 0;
+ S32 i;
+
+ const LLWString &text = mWText;
+ while( ' ' == text[line_start] )
+ {
+ space_count++;
+ line_start++;
+ }
+
+ // If we're starting a braced section, indent one level.
+ if( (mCursorPos > 0) && (text[mCursorPos -1] == '{') )
+ {
+ space_count += SPACES_PER_TAB;
+ }
+
+ // Insert that number of spaces on the new line
+ addChar( '\n' );
+ for( i = 0; i < space_count; i++ )
+ {
+ addChar( ' ' );
+ }
+}
+
+// Inserts new text at the cursor position
+void LLTextEditor::insertText(const LLString &new_text)
+{
+ BOOL enabled = getEnabled();
+ setEnabled( TRUE );
+
+ // Delete any selected characters (the insertion replaces them)
+ if( hasSelection() )
+ {
+ deleteSelection(TRUE);
+ }
+
+ setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE ));
+
+ updateLineStartList();
+ updateScrollFromCursor();
+
+ setEnabled( enabled );
+}
+
+
+void LLTextEditor::appendColoredText(const LLString &new_text,
+ bool allow_undo,
+ bool prepend_newline,
+ const LLColor4 &color,
+ const LLString& font_name)
+{
+ LLStyle style;
+ style.setVisible(true);
+ style.setColor(color);
+ style.setFontName(font_name);
+ if(mParseHTML)
+ {
+
+ S32 start=0,end=0;
+ LLString text = new_text;
+ while ( findHTML(text, &start, &end) )
+ {
+ LLStyle html;
+ html.setVisible(true);
+ html.setColor(mLinkColor);
+ html.setFontName(font_name);
+ html.mUnderline = TRUE;
+
+ if (start > 0) appendText(text.substr(0,start),allow_undo, prepend_newline, &style);
+ html.setLinkHREF(text.substr(start,end-start));
+ appendText(text.substr(start, end-start),allow_undo, prepend_newline, &html);
+ if (end < (S32)text.length())
+ {
+ text = text.substr(end,text.length() - end);
+ end=0;
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (end < (S32)text.length()) appendText(text,allow_undo, prepend_newline, &style);
+ }
+ else
+ {
+ appendText(new_text, allow_undo, prepend_newline, &style);
+ }
+}
+
+void LLTextEditor::appendStyledText(const LLString &new_text,
+ bool allow_undo,
+ bool prepend_newline,
+ const LLStyle &style)
+{
+ appendText(new_text, allow_undo, prepend_newline, &style);
+}
+
+// Appends new text to end of document
+void LLTextEditor::appendText(const LLString &new_text, bool allow_undo, bool prepend_newline,
+ const LLStyle* segment_style)
+{
+ // Save old state
+ BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
+ S32 selection_start = mSelectionStart;
+ S32 selection_end = mSelectionEnd;
+ S32 cursor_pos = mCursorPos;
+ S32 old_length = getLength();
+ BOOL cursor_was_at_end = (mCursorPos == old_length);
+
+ deselect();
+
+ setCursorPos(old_length);
+
+ // Add carriage return if not first line
+ if (getLength() != 0
+ && prepend_newline)
+ {
+ LLString final_text = "\n";
+ final_text += new_text;
+ append(utf8str_to_wstring(final_text), TRUE);
+ }
+ else
+ {
+ append(utf8str_to_wstring(new_text), TRUE );
+ }
+
+ if (segment_style)
+ {
+ S32 segment_start = old_length;
+ S32 segment_end = getLength();
+ LLTextSegment* segment = new LLTextSegment(*segment_style, segment_start, segment_end );
+ mSegments.push_back(segment);
+ }
+
+ updateLineStartList(old_length);
+
+ // Set the cursor and scroll position
+ // Maintain the scroll position unless the scroll was at the end of the doc
+ // (in which case, move it to the new end of the doc)
+ if( was_scrolled_to_bottom )
+ {
+ endOfDoc();
+ }
+ else if( selection_start != selection_end )
+ {
+ mSelectionStart = selection_start;
+
+ mSelectionEnd = selection_end;
+ setCursorPos(cursor_pos);
+ }
+ else if( cursor_was_at_end )
+ {
+ setCursorPos(getLength());
+ }
+ else
+ {
+ setCursorPos(cursor_pos);
+ }
+
+ if( !allow_undo )
+ {
+ blockUndo();
+ }
+}
+
+void LLTextEditor::removeTextFromEnd(S32 num_chars)
+{
+ if (num_chars <= 0) return;
+
+ remove(getLength() - num_chars, num_chars, FALSE);
+
+ S32 len = getLength();
+ mCursorPos = llclamp(mCursorPos, 0, len);
+ mSelectionStart = llclamp(mSelectionStart, 0, len);
+ mSelectionEnd = llclamp(mSelectionEnd, 0, len);
+
+ pruneSegments();
+ updateLineStartList();
+}
+
+///////////////////////////////////////////////////////////////////
+// Returns change in number of characters in mWText
+
+S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr)
+{
+ S32 len = mWText.length();
+ S32 s_len = wstr.length();
+ S32 new_len = len + s_len;
+ if( new_len > mMaxTextLength )
+ {
+ new_len = mMaxTextLength;
+
+ // The user's not getting everything he's hoping for
+ make_ui_sound("UISndBadKeystroke");
+ }
+
+ mWText.insert(pos, wstr);
+ mTextIsUpToDate = FALSE;
+ truncate();
+
+ return new_len - len;
+}
+
+S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length)
+{
+ mWText.erase(pos, length);
+ mTextIsUpToDate = FALSE;
+ return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length
+}
+
+S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc)
+{
+ if (pos > (S32)mWText.length())
+ {
+ return 0;
+ }
+ mWText[pos] = wc;
+ mTextIsUpToDate = FALSE;
+ return 1;
+}
+
+//----------------------------------------------------------------------------
+
+void LLTextEditor::makePristine()
+{
+ mPristineCmd = mLastCmd;
+ mBaseDocIsPristine = !mLastCmd;
+
+ // Create a clean partition in the undo stack. We don't want a single command to extend from
+ // the "pre-pristine" state to the "post-pristine" state.
+ if( mLastCmd )
+ {
+ mLastCmd->blockExtensions();
+ }
+}
+
+BOOL LLTextEditor::isPristine() const
+{
+ if( mPristineCmd )
+ {
+ return (mPristineCmd == mLastCmd);
+ }
+ else
+ {
+ // No undo stack, so check if the version before and commands were done was the original version
+ return !mLastCmd && mBaseDocIsPristine;
+ }
+}
+
+BOOL LLTextEditor::tryToRevertToPristineState()
+{
+ if( !isPristine() )
+ {
+ deselect();
+ S32 i = 0;
+ while( !isPristine() && canUndo() )
+ {
+ undo();
+ i--;
+ }
+
+ while( !isPristine() && canRedo() )
+ {
+ redo();
+ i++;
+ }
+
+ if( !isPristine() )
+ {
+ // failed, so go back to where we started
+ while( i > 0 )
+ {
+ undo();
+ i--;
+ }
+ }
+
+ updateLineStartList();
+ updateScrollFromCursor();
+ }
+
+ return isPristine(); // TRUE => success
+}
+
+
+
+void LLTextEditor::updateTextRect()
+{
+ mTextRect.setOriginAndSize(
+ UI_TEXTEDITOR_BORDER + UI_TEXTEDITOR_H_PAD,
+ UI_TEXTEDITOR_BORDER,
+ mRect.getWidth() - SCROLLBAR_SIZE - 2 * (UI_TEXTEDITOR_BORDER + UI_TEXTEDITOR_H_PAD),
+ mRect.getHeight() - 2 * UI_TEXTEDITOR_BORDER - UI_TEXTEDITOR_V_PAD_TOP );
+}
+
+void LLTextEditor::loadKeywords(const LLString& filename,
+ const LLDynamicArray<const char*>& funcs,
+ const LLDynamicArray<const char*>& tooltips,
+ const LLColor3& color)
+{
+ if(mKeywords.loadFromFile(filename))
+ {
+ S32 count = funcs.count();
+ LLString name;
+ for(S32 i = 0; i < count; i++)
+ {
+ name = funcs.get(i);
+ name = utf8str_trim(name);
+ mKeywords.addToken(LLKeywordToken::WORD, name.c_str(), color, tooltips.get(i) );
+ }
+
+ mKeywords.findSegments( &mSegments, mWText );
+
+ llassert( mSegments.front()->getStart() == 0 );
+ llassert( mSegments.back()->getEnd() == getLength() );
+ }
+}
+
+void LLTextEditor::updateSegments()
+{
+ if (mKeywords.isLoaded())
+ {
+ // HACK: No non-ascii keywords for now
+ mKeywords.findSegments(&mSegments, mWText);
+ }
+ else if (mAllowEmbeddedItems)
+ {
+ findEmbeddedItemSegments();
+ }
+ // Make sure we have at least one segment
+ if (mSegments.size() == 1 && mSegments[0]->getIsDefault())
+ {
+ delete mSegments[0];
+ mSegments.clear(); // create default segment
+ }
+ if (mSegments.empty())
+ {
+ LLColor4& text_color = ( mReadOnly ? mReadOnlyFgColor : mFgColor );
+ LLTextSegment* default_segment = new LLTextSegment( text_color, 0, mWText.length() );
+ default_segment->setIsDefault(TRUE);
+ mSegments.push_back(default_segment);
+ }
+}
+
+// Only effective if text was removed from the end of the editor
+void LLTextEditor::pruneSegments()
+{
+ S32 len = mWText.length();
+ // Find and update the first valid segment
+ segment_list_t::iterator iter = mSegments.end();
+ while(iter != mSegments.begin())
+ {
+ --iter;
+ LLTextSegment* seg = *iter;
+ if (seg->getStart() < len)
+ {
+ // valid segment
+ if (seg->getEnd() > len)
+ {
+ seg->setEnd(len);
+ }
+ break; // done
+ }
+ }
+ // erase invalid segments
+ ++iter;
+ std::for_each(iter, mSegments.end(), DeletePointer());
+ mSegments.erase(iter, mSegments.end());
+}
+
+void LLTextEditor::findEmbeddedItemSegments()
+{
+ mHoverSegment = NULL;
+ std::for_each(mSegments.begin(), mSegments.end(), DeletePointer());
+ mSegments.clear();
+
+ BOOL found_embedded_items = FALSE;
+ const LLWString &text = mWText;
+ S32 idx = 0;
+ while( text[idx] )
+ {
+ if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
+ {
+ found_embedded_items = TRUE;
+ break;
+ }
+ ++idx;
+ }
+
+ if( !found_embedded_items )
+ {
+ return;
+ }
+
+ S32 text_len = text.length();
+
+ BOOL in_text = FALSE;
+
+ LLColor4& text_color = ( mReadOnly ? mReadOnlyFgColor : mFgColor );
+
+ if( idx > 0 )
+ {
+ mSegments.push_back( new LLTextSegment( text_color, 0, text_len ) ); // text
+ in_text = TRUE;
+ }
+
+ LLStyle embedded_style;
+ embedded_style.setIsEmbeddedItem( TRUE );
+
+ // Start with i just after the first embedded item
+ while ( text[idx] )
+ {
+ if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR )
+ {
+ if( in_text )
+ {
+ mSegments.back()->setEnd( idx );
+ }
+ mSegments.push_back( new LLTextSegment( embedded_style, idx, idx + 1 ) ); // item
+ in_text = FALSE;
+ }
+ else
+ if( !in_text )
+ {
+ mSegments.push_back( new LLTextSegment( text_color, idx, text_len ) ); // text
+ in_text = TRUE;
+ }
+ ++idx;
+ }
+}
+
+BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+llwchar LLTextEditor::pasteEmbeddedItem(llwchar ext_char)
+{
+ return ext_char;
+}
+
+void LLTextEditor::bindEmbeddedChars(const LLFontGL* font)
+{
+}
+
+void LLTextEditor::unbindEmbeddedChars(const LLFontGL* font)
+{
+}
+
+// Finds the text segment (if any) at the give local screen position
+LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y )
+{
+ // Find the cursor position at the requested local screen position
+ S32 offset = getCursorPosFromLocalCoord( x, y, FALSE );
+ S32 idx = getSegmentIdxAtOffset(offset);
+ return idx >= 0 ? mSegments[idx] : NULL;
+}
+
+LLTextSegment* LLTextEditor::getSegmentAtOffset(S32 offset)
+{
+ S32 idx = getSegmentIdxAtOffset(offset);
+ return idx >= 0 ? mSegments[idx] : NULL;
+}
+
+S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset)
+{
+ if (mSegments.empty() || offset < 0 || offset >= getLength())
+ {
+ return -1;
+ }
+ else
+ {
+ S32 segidx, segoff;
+ getSegmentAndOffset(offset, &segidx, &segoff);
+ return segidx;
+ }
+}
+
+//static
+void LLTextEditor::onMouseCaptureLost( LLMouseHandler* old_captor )
+{
+ LLTextEditor* self = (LLTextEditor*) old_captor;
+ self->endSelection();
+}
+
+void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata)
+{
+ mOnScrollEndCallback = callback;
+ mOnScrollEndData = userdata;
+ mScrollbar->setOnScrollEndCallback(callback, userdata);
+}
+
+///////////////////////////////////////////////////////////////////
+// Hack for Notecards
+
+BOOL LLTextEditor::importBuffer(const LLString& buffer )
+{
+ std::istringstream instream(buffer);
+
+ // Version 1 format:
+ // Linden text version 1\n
+ // {\n
+ // <EmbeddedItemList chunk>
+ // Text length <bytes without \0>\n
+ // <text without \0> (text may contain ext_char_values)
+ // }\n
+
+ char tbuf[MAX_STRING];
+
+ S32 version = 0;
+ instream.getline(tbuf, MAX_STRING);
+ if( 1 != sscanf(tbuf, "Linden text version %d", &version) )
+ {
+ llwarns << "Invalid Linden text file header " << llendl;
+ return FALSE;
+ }
+
+ if( 1 != version )
+ {
+ llwarns << "Invalid Linden text file version: " << version << llendl;
+ return FALSE;
+ }
+
+ instream.getline(tbuf, MAX_STRING);
+ if( 0 != sscanf(tbuf, "{") )
+ {
+ llwarns << "Invalid Linden text file format" << llendl;
+ return FALSE;
+ }
+
+ S32 text_len = 0;
+ instream.getline(tbuf, MAX_STRING);
+ if( 1 != sscanf(tbuf, "Text length %d", &text_len) )
+ {
+ llwarns << "Invalid Linden text length field" << llendl;
+ return FALSE;
+ }
+
+ if( text_len > mMaxTextLength )
+ {
+ llwarns << "Invalid Linden text length: " << text_len << llendl;
+ return FALSE;
+ }
+
+ BOOL success = TRUE;
+
+ char* text = new char[ text_len + 1];
+ instream.get(text, text_len + 1, '\0');
+ text[text_len] = '\0';
+ if( text_len != (S32)strlen(text) )
+ {
+ llwarns << llformat("Invalid text length: %d != %d ",strlen(text),text_len) << llendl;
+ success = FALSE;
+ }
+
+ instream.getline(tbuf, MAX_STRING);
+ if( success && (0 != sscanf(tbuf, "}")) )
+ {
+ llwarns << "Invalid Linden text file format: missing terminal }" << llendl;
+ success = FALSE;
+ }
+
+ if( success )
+ {
+ // Actually set the text
+ setText( text );
+ }
+
+ delete[] text;
+
+ setCursorPos(0);
+ deselect();
+
+ updateLineStartList();
+ updateScrollFromCursor();
+
+ return success;
+}
+
+BOOL LLTextEditor::exportBuffer(LLString &buffer )
+{
+ std::ostringstream outstream(buffer);
+
+ outstream << "Linden text version 1\n";
+ outstream << "{\n";
+
+ outstream << llformat("Text length %d\n", mWText.length() );
+ outstream << getText();
+ outstream << "}\n";
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// LLTextSegment
+
+LLTextSegment::LLTextSegment(S32 start) : mStart(start)
+{
+}
+LLTextSegment::LLTextSegment( const LLStyle& style, S32 start, S32 end ) :
+ mStyle( style ),
+ mStart( start),
+ mEnd( end ),
+ mToken(NULL),
+ mIsDefault(FALSE)
+{
+}
+LLTextSegment::LLTextSegment(
+ const LLColor4& color, S32 start, S32 end, BOOL is_visible) :
+ mStyle( is_visible, color,"" ),
+ mStart( start),
+ mEnd( end ),
+ mToken(NULL),
+ mIsDefault(FALSE)
+{
+}
+LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end ) :
+ mStyle( TRUE, color,"" ),
+ mStart( start),
+ mEnd( end ),
+ mToken(NULL),
+ mIsDefault(FALSE)
+{
+}
+LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) :
+ mStyle( TRUE, color,"" ),
+ mStart( start),
+ mEnd( end ),
+ mToken(NULL),
+ mIsDefault(FALSE)
+{
+}
+
+BOOL LLTextSegment::getToolTip(LLString& msg)
+{
+ if (mToken && !mToken->getToolTip().empty())
+ {
+ const LLWString& wmsg = mToken->getToolTip();
+ msg = wstring_to_utf8str(wmsg);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+void LLTextSegment::dump()
+{
+ llinfos << "Segment [" <<
+// mColor.mV[VX] << ", " <<
+// mColor.mV[VY] << ", " <<
+// mColor.mV[VZ] << "]\t[" <<
+ mStart << ", " <<
+ getEnd() << "]" <<
+ llendl;
+
+}
+
+// virtual
+LLXMLNodePtr LLTextEditor::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ // Attributes
+
+ node->createChild("max_length", TRUE)->setIntValue(getMaxLength());
+
+ node->createChild("embedded_items", TRUE)->setBoolValue(mAllowEmbeddedItems);
+
+ node->createChild("font", TRUE)->setStringValue(LLFontGL::nameFromFont(mGLFont));
+
+ node->createChild("word_wrap", TRUE)->setBoolValue(mWordWrap);
+
+ addColorXML(node, mCursorColor, "cursor_color", "TextCursorColor");
+ addColorXML(node, mFgColor, "text_color", "TextFgColor");
+ addColorXML(node, mReadOnlyFgColor, "text_readonly_color", "TextFgReadOnlyColor");
+ addColorXML(node, mReadOnlyBgColor, "bg_readonly_color", "TextBgReadOnlyColor");
+ addColorXML(node, mWriteableBgColor, "bg_writeable_color", "TextBgWriteableColor");
+ addColorXML(node, mFocusBgColor, "bg_focus_color", "TextBgFocusColor");
+
+ // Contents
+ node->setStringValue(getText());
+
+ return node;
+}
+
+// static
+LLView* LLTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("text_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ U32 max_text_length = 255;
+ node->getAttributeU32("max_length", max_text_length);
+
+ BOOL allow_embedded_items;
+ node->getAttributeBOOL("embedded_items", allow_embedded_items);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLString text = node->getTextContents().substr(0, max_text_length - 1);
+
+ LLTextEditor* text_editor = new LLTextEditor(name,
+ rect,
+ max_text_length,
+ text,
+ font,
+ allow_embedded_items);
+
+ text_editor->setTextEditorParameters(node);
+
+ BOOL hide_scrollbar = FALSE;
+ node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
+ text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
+
+ text_editor->initFromXML(node, parent);
+
+ return text_editor;
+}
+
+void LLTextEditor::setTextEditorParameters(LLXMLNodePtr node)
+{
+ BOOL word_wrap = FALSE;
+ node->getAttributeBOOL("word_wrap", word_wrap);
+ setWordWrap(word_wrap);
+
+ LLColor4 color;
+ if (LLUICtrlFactory::getAttributeColor(node,"cursor_color", color))
+ {
+ setCursorColor(color);
+ }
+ if(LLUICtrlFactory::getAttributeColor(node,"text_color", color))
+ {
+ setFgColor(color);
+ }
+ if(LLUICtrlFactory::getAttributeColor(node,"text_readonly_color", color))
+ {
+ setReadOnlyFgColor(color);
+ }
+ if(LLUICtrlFactory::getAttributeColor(node,"bg_readonly_color", color))
+ {
+ setReadOnlyBgColor(color);
+ }
+ if(LLUICtrlFactory::getAttributeColor(node,"bg_writeable_color", color))
+ {
+ setWriteableBgColor(color);
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+S32 LLTextEditor::findHTMLToken(const LLString &line, S32 pos, BOOL reverse)
+{
+ LLString openers=" \t('\"[{<>";
+ LLString closers=" \t)'\"]}><;";
+
+ S32 m2;
+ S32 retval;
+
+ if (reverse)
+ {
+
+ for (retval=pos; retval>0; retval--)
+ {
+ m2 = openers.find(line.substr(retval,1));
+ if (m2 >= 0)
+ {
+ retval++;
+ break;
+ }
+ }
+ }
+ else
+ {
+
+ for (retval=pos; retval<(S32)line.length(); retval++)
+ {
+ m2 = closers.find(line.substr(retval,1));
+ if (m2 >= 0)
+ {
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+BOOL LLTextEditor::findHTML(const LLString &line, S32 *begin, S32 *end)
+{
+
+ S32 m1,m2,m3;
+ BOOL matched = FALSE;
+
+ m1=line.find("://",*end);
+
+ if (m1 >= 0) //Easy match.
+ {
+ *begin = findHTMLToken(line, m1, TRUE);
+ *end = findHTMLToken(line, m1, FALSE);
+
+ //Load_url only handles http and https so don't hilite ftp, smb, etc.
+ m2 = line.substr(*begin,(m1 - *begin)).find("http");
+ m3 = line.substr(*begin,(m1 - *begin)).find("secondlife");
+
+ LLString badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`\t\r\n\\";
+
+ if (m2 >= 0 || m3>=0)
+ {
+ S32 bn = badneighbors.find(line.substr(m1+3,1));
+
+ if (bn < 0)
+ {
+ matched = TRUE;
+ }
+ }
+ }
+/* matches things like secondlife.com (no http://) needs a whitelist to really be effective.
+ else //Harder match.
+ {
+ m1 = line.find(".",*end);
+
+ if (m1 >= 0)
+ {
+ *end = findHTMLToken(line, m1, FALSE);
+ *begin = findHTMLToken(line, m1, TRUE);
+
+ m1 = line.rfind(".",*end);
+
+ if ( ( *end - m1 ) > 2 && m1 > *begin)
+ {
+ LLString badneighbors=".,<>/?';\"][}{=-+_)(*&^%$#@!~`";
+ m2 = badneighbors.find(line.substr(m1+1,1));
+ m3 = badneighbors.find(line.substr(m1-1,1));
+ if (m3<0 && m2<0)
+ {
+ matched = TRUE;
+ }
+ }
+ }
+ }
+ */
+
+ if (matched)
+ {
+ S32 strpos, strpos2;
+
+ LLString url = line.substr(*begin,*end - *begin);
+ LLString slurlID = "slurl.com/secondlife/";
+ strpos = url.find(slurlID);
+
+ if (strpos < 0)
+ {
+ slurlID="secondlife://";
+ strpos = url.find(slurlID);
+ }
+
+ if (strpos >= 0)
+ {
+ strpos+=slurlID.length();
+
+ while ( ( strpos2=url.find("/",strpos) ) == -1 )
+ {
+ if ((*end+2) >= (S32)line.length() || line.substr(*end,1) != " " )
+ {
+ matched=FALSE;
+ break;
+ }
+
+ strpos = (*end + 1) - *begin;
+
+ *end = findHTMLToken(line,(*begin + strpos),FALSE);
+ url = line.substr(*begin,*end - *begin);
+ }
+ }
+
+ }
+
+ if (!matched)
+ {
+ *begin=*end=0;
+ }
+ return matched;
+}
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
new file mode 100644
index 0000000000..bebf2b31f2
--- /dev/null
+++ b/indra/llui/lltexteditor.h
@@ -0,0 +1,483 @@
+/**
+ * @file lltexteditor.h
+ * @brief LLTextEditor base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Text editor widget to let users enter a a multi-line ASCII document//
+
+#ifndef LL_LLTEXTEDITOR_H
+#define LL_LLTEXTEDITOR_H
+
+#include "llrect.h"
+#include "llkeywords.h"
+#include "lluictrl.h"
+#include "llframetimer.h"
+#include "lldarray.h"
+#include "llstyle.h"
+#include "lleditmenuhandler.h"
+#include "lldarray.h"
+
+class LLFontGL;
+class LLScrollbar;
+class LLViewBorder;
+class LLKeywordToken;
+class LLTextCmd;
+
+//
+// Constants
+//
+
+const llwchar FIRST_EMBEDDED_CHAR = 0x100000;
+const llwchar LAST_EMBEDDED_CHAR = 0x10ffff;
+const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1;
+
+//
+// Classes
+//
+class LLTextSegment;
+class LLTextCmd;
+
+class LLTextEditor : public LLUICtrl, LLEditMenuHandler
+{
+ friend class LLTextCmd;
+public:
+ LLTextEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString &default_text,
+ const LLFontGL* glfont = NULL,
+ BOOL allow_embedded_items = FALSE);
+
+ virtual ~LLTextEditor();
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEXT_EDITOR; }
+ virtual LLString getWidgetTag() const;
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void setTextEditorParameters(LLXMLNodePtr node);
+ void setParseHTML(BOOL parsing) {mParseHTML=parsing;}
+
+ // mousehandler overrides
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent );
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept, LLString& tooltip_msg);
+
+ // view overrides
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+ virtual void draw();
+ virtual void onFocusLost();
+ virtual void setEnabled(BOOL enabled);
+
+ // uictrl overrides
+ virtual void onTabInto();
+ virtual void clear();
+ virtual void setFocus( BOOL b );
+ virtual BOOL acceptsTextInput() const;
+
+ // LLEditMenuHandler interface
+ virtual void undo();
+ virtual BOOL canUndo();
+
+ virtual void redo();
+ virtual BOOL canRedo();
+
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void paste();
+ virtual BOOL canPaste();
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ virtual void selectAll();
+ virtual BOOL canSelectAll();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ void selectNext(const LLString& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE);
+ BOOL replaceText(const LLString& search_text, const LLString& replace_text, BOOL case_insensitive, BOOL wrap = TRUE);
+ void replaceTextAll(const LLString& search_text, const LLString& replace_text, BOOL case_insensitive);
+
+ // Undo/redo stack
+ void blockUndo();
+
+ // Text editing
+ virtual void makePristine();
+ BOOL isPristine() const;
+
+ // inserts text at cursor
+ void insertText(const LLString &text);
+ // appends text at end
+ void appendText(const LLString &wtext, bool allow_undo, bool prepend_newline,
+ const LLStyle* segment_style = NULL);
+
+ void appendColoredText(const LLString &wtext, bool allow_undo,
+ bool prepend_newline,
+ const LLColor4 &color,
+ const LLString& font_name = LLString::null);
+ // if styled text starts a line, you need to prepend a newline.
+ void appendStyledText(const LLString &new_text, bool allow_undo,
+ bool prepend_newline,
+ const LLStyle &style);
+
+ // Removes text from the end of document
+ // Does not change highlight or cursor position.
+ void removeTextFromEnd(S32 num_chars);
+
+ BOOL tryToRevertToPristineState();
+
+ void setCursor(S32 row, S32 column);
+ void setCursorPos(S32 offset);
+ void setCursorAndScrollToEnd();
+
+ void getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap );
+
+ void loadKeywords(const LLString& filename,
+ const LLDynamicArray<const char*>& funcs,
+ const LLDynamicArray<const char*>& tooltips,
+ const LLColor3& func_color);
+
+ void setCursorColor(const LLColor4& c) { mCursorColor = c; }
+ void setFgColor( const LLColor4& c ) { mFgColor = c; }
+ void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; }
+ void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; }
+ void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; }
+
+ void setTrackColor( const LLColor4& color );
+ void setThumbColor( const LLColor4& color );
+ void setHighlightColor( const LLColor4& color );
+ void setShadowColor( const LLColor4& color );
+
+ // Hacky methods to make it into a word-wrapping, potentially scrolling,
+ // read-only text box.
+ void setBorderVisible(BOOL b);
+ void setTakesNonScrollClicks(BOOL b);
+ void setHideScrollbarForShortDocs(BOOL b);
+
+ void setWordWrap( BOOL b );
+ void setTabToNextField(BOOL b) { mTabToNextField = b; }
+ void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; }
+
+ // If takes focus, will take keyboard focus on click.
+ void setTakesFocus(BOOL b) { mTakesFocus = b; }
+
+ // Hack to handle Notecards
+ virtual BOOL importBuffer(const LLString& buffer );
+ virtual BOOL exportBuffer(LLString& buffer );
+
+ void setSourceID(const LLUUID& id) { mSourceID = id; }
+ void setAcceptCallingCardNames(BOOL enable) { mAcceptCallingCardNames = enable; }
+
+ void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; }
+
+ // Callbacks
+ static void onMouseCaptureLost( LLMouseHandler* old_captor );
+ static void setLinkColor(LLColor4 color) { mLinkColor = color; }
+ static void setURLCallbacks( void (*callback1) (const char* url),
+ BOOL (*callback2) (LLString url) )
+ { mURLcallback = callback1; mSecondlifeURLcallback = callback2;}
+
+ void setOnScrollEndCallback(void (*callback)(void*), void* userdata);
+
+ // new methods
+ void setValue(const LLSD& value);
+ LLSD getValue() const;
+
+ const LLString& getText() const;
+
+ // Non-undoable
+ void setText(const LLString &utf8str);
+ void setWText(const LLWString &wtext);
+
+ S32 getMaxLength() const { return mMaxTextLength; }
+
+ // Change cursor
+ void startOfLine();
+ void endOfLine();
+ void endOfDoc();
+
+ // Getters
+ const LLWString& getWText() const;
+ llwchar getWChar(S32 pos);
+ LLWString getWSubString(S32 pos, S32 len);
+
+protected:
+ S32 getLength() const;
+ void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp );
+
+ void drawBackground();
+ void drawSelectionBackground();
+ void drawCursor();
+ void drawText();
+ void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyle& color, F32* right_x);
+
+ void updateLineStartList(S32 startpos = 0);
+ void updateScrollFromCursor();
+ void updateTextRect();
+ void updateSegments();
+ void pruneSegments();
+
+ void assignEmbedded(const LLString &s);
+ void truncate();
+
+ static BOOL isPartOfWord(llwchar c);
+
+ void removeCharOrTab();
+ void setCursorAtLocalPos(S32 x, S32 y, BOOL round);
+ S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round );
+
+ void indentSelectedLines( S32 spaces );
+ S32 indentLine( S32 pos, S32 spaces );
+ void unindentLineBeforeCloseBrace();
+
+ S32 getSegmentIdxAtOffset(S32 offset);
+ LLTextSegment* getSegmentAtLocalPos(S32 x, S32 y);
+ LLTextSegment* getSegmentAtOffset(S32 offset);
+
+ void reportBadKeystroke();
+
+ BOOL handleNavigationKey(const KEY key, const MASK mask);
+ BOOL handleSpecialKey(const KEY key, const MASK mask, BOOL* return_key_hit);
+ BOOL handleSelectionKey(const KEY key, const MASK mask);
+ BOOL handleControlKey(const KEY key, const MASK mask);
+ BOOL handleEditKey(const KEY key, const MASK mask);
+
+ BOOL hasSelection() { return (mSelectionStart !=mSelectionEnd); }
+ BOOL selectionContainsLineBreaks();
+ void startSelection();
+ void endSelection();
+ void deleteSelection(BOOL transient_operation);
+
+ S32 prevWordPos(S32 cursorPos) const;
+ S32 nextWordPos(S32 cursorPos) const;
+
+ S32 getLineCount();
+ S32 getLineStart( S32 line );
+ void getLineAndOffset(S32 pos, S32* linep, S32* offsetp);
+ S32 getPos(S32 line, S32 offset);
+
+ void changePage(S32 delta);
+ void changeLine(S32 delta);
+
+ void autoIndent();
+
+ S32 execute(LLTextCmd* cmd);
+
+ void findEmbeddedItemSegments();
+
+ virtual BOOL handleMouseUpOverSegment(S32 x, S32 y, MASK mask);
+ virtual llwchar pasteEmbeddedItem(llwchar ext_char);
+ virtual void bindEmbeddedChars(const LLFontGL* font);
+ virtual void unbindEmbeddedChars(const LLFontGL* font);
+
+ S32 findHTMLToken(const LLString &line, S32 pos, BOOL reverse);
+ BOOL findHTML(const LLString &line, S32 *begin, S32 *end);
+
+protected:
+ // Undoable operations
+ void addChar(llwchar c); // at mCursorPos
+ S32 addChar(S32 pos, llwchar wc);
+ S32 overwriteChar(S32 pos, llwchar wc);
+ void removeChar();
+ S32 removeChar(S32 pos);
+ S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op);
+ S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op);
+ S32 append(const LLWString &wstr, const BOOL group_with_next_op);
+
+ // direct operations
+ S32 insertStringNoUndo(S32 pos, const LLWString &utf8str); // returns num of chars actually inserted
+ S32 removeStringNoUndo(S32 pos, S32 length);
+ S32 overwriteCharNoUndo(S32 pos, llwchar wc);
+
+public:
+ LLKeywords mKeywords;
+ static LLColor4 mLinkColor;
+ static void (*mURLcallback) (const char* url);
+ static BOOL (*mSecondlifeURLcallback) (LLString url);
+protected:
+ LLWString mWText;
+ mutable LLString mUTF8Text;
+ mutable BOOL mTextIsUpToDate;
+
+ S32 mMaxTextLength; // Maximum length mText is allowed to be
+
+ const LLFontGL* mGLFont;
+
+ LLScrollbar* mScrollbar;
+ LLViewBorder* mBorder;
+
+ BOOL mBaseDocIsPristine;
+ LLTextCmd* mPristineCmd;
+
+ LLTextCmd* mLastCmd;
+
+ typedef std::deque<LLTextCmd*> undo_stack_t;
+ undo_stack_t mUndoStack;
+
+ S32 mCursorPos; // I-beam is just after the mCursorPos-th character.
+ LLRect mTextRect; // The rect in which text is drawn. Excludes borders.
+ // List of offsets and segment index of the start of each line. Always has at least one node (0).
+ struct line_info
+ {
+ line_info(S32 segment, S32 offset) : mSegment(segment), mOffset(offset) {}
+ S32 mSegment;
+ S32 mOffset;
+ };
+ struct line_info_compare
+ {
+ bool operator()(const line_info& a, const line_info& b) const
+ {
+ if (a.mSegment < b.mSegment)
+ return true;
+ else if (a.mSegment > b.mSegment)
+ return false;
+ else
+ return a.mOffset < b.mOffset;
+ }
+ };
+ typedef std::vector<line_info> line_list_t;
+ line_list_t mLineStartList;
+
+ // Are we in the middle of a drag-select? To figure out if there is a current
+ // selection, call hasSelection().
+ BOOL mIsSelecting;
+
+ S32 mSelectionStart;
+ S32 mSelectionEnd;
+
+ void (*mOnScrollEndCallback)(void*);
+ void *mOnScrollEndData;
+
+ typedef std::vector<LLTextSegment *> segment_list_t;
+ segment_list_t mSegments;
+ LLTextSegment* mHoverSegment;
+ LLFrameTimer mKeystrokeTimer;
+
+ LLColor4 mCursorColor;
+
+ LLColor4 mFgColor;
+ LLColor4 mReadOnlyFgColor;
+ LLColor4 mWriteableBgColor;
+ LLColor4 mReadOnlyBgColor;
+ LLColor4 mFocusBgColor;
+
+ BOOL mReadOnly;
+ BOOL mWordWrap;
+
+ BOOL mTabToNextField; // if true, tab moves focus to next field, else inserts spaces
+ BOOL mCommitOnFocusLost;
+ BOOL mTakesFocus;
+ BOOL mHideScrollbarForShortDocs;
+ BOOL mTakesNonScrollClicks;
+
+ BOOL mAllowEmbeddedItems;
+
+ BOOL mAcceptCallingCardNames;
+
+ LLUUID mSourceID;
+
+ BOOL mHandleEditKeysDirectly; // If true, the standard edit keys (Ctrl-X, Delete, etc,) are handled here instead of routed by the menu system
+
+ // Use these to determine if a click on an embedded item is a drag
+ // or not.
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+
+ S32 mLastSelectionX;
+ S32 mLastSelectionY;
+
+ BOOL mParseHTML;
+ LLString mHTML;
+};
+
+class LLTextSegment
+{
+public:
+ // for creating a compare value
+ LLTextSegment(S32 start);
+ LLTextSegment( const LLStyle& style, S32 start, S32 end );
+ LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible);
+ LLTextSegment( const LLColor4& color, S32 start, S32 end );
+ LLTextSegment( const LLColor3& color, S32 start, S32 end );
+
+ S32 getStart() { return mStart; }
+ S32 getEnd() { return mEnd; }
+ void setEnd( S32 end ) { mEnd = end; }
+ const LLColor4& getColor() { return mStyle.getColor(); }
+ void setColor(const LLColor4 &color) { mStyle.setColor(color); }
+ const LLStyle& getStyle() { return mStyle; }
+ void setStyle(const LLStyle &style) { mStyle = style; }
+ void setIsDefault(BOOL b) { mIsDefault = b; }
+ BOOL getIsDefault() { return mIsDefault; }
+
+ void setToken( LLKeywordToken* token ) { mToken = token; }
+ LLKeywordToken* getToken() { return mToken; }
+ BOOL getToolTip( LLString& msg );
+
+ void dump();
+
+ struct compare
+ {
+ bool operator()(const LLTextSegment* a, const LLTextSegment* b) const
+ {
+ return a->mStart < b->mStart;
+ }
+ };
+
+private:
+ LLStyle mStyle;
+ S32 mStart;
+ S32 mEnd;
+ LLKeywordToken* mToken;
+ BOOL mIsDefault;
+};
+
+class LLTextCmd
+{
+public:
+ LLTextCmd( S32 pos, BOOL group_with_next )
+ : mPos(pos),
+ mGroupWithNext(group_with_next)
+ {
+ }
+ virtual ~LLTextCmd() {}
+ virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0;
+ virtual S32 undo(LLTextEditor* editor) = 0;
+ virtual S32 redo(LLTextEditor* editor) = 0;
+ virtual BOOL canExtend(S32 pos);
+ virtual void blockExtensions();
+ virtual BOOL extendAndExecute( LLTextEditor* editor, S32 pos, llwchar c, S32* delta );
+ virtual BOOL hasExtCharValue( llwchar value );
+
+ // Define these here so they can access LLTextEditor through the friend relationship
+ S32 insert(LLTextEditor* editor, S32 pos, const LLWString &utf8str);
+ S32 remove(LLTextEditor* editor, S32 pos, S32 length);
+ S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc);
+
+ BOOL groupWithNext() { return mGroupWithNext; }
+
+protected:
+ S32 mPos;
+ BOOL mGroupWithNext;
+};
+
+
+#endif // LL_TEXTEDITOR_
diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp
new file mode 100644
index 0000000000..d951cb70f6
--- /dev/null
+++ b/indra/llui/llui.cpp
@@ -0,0 +1,1764 @@
+/**
+ * @file llui.cpp
+ * @brief UI implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Utilities functions the user interface needs
+
+//#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include <string>
+#include <map>
+
+// Linden library includes
+#include "audioengine.h"
+#include "v2math.h"
+#include "v4color.h"
+#include "llgl.h"
+#include "llrect.h"
+#include "llimagegl.h"
+//#include "llviewerimage.h"
+#include "lldir.h"
+#include "llfontgl.h"
+
+// Project includes
+//#include "audioengine.h"
+#include "llcontrol.h"
+//#include "llstartup.h"
+#include "llui.h"
+#include "llview.h"
+#include "llwindow.h"
+
+#include "llglheaders.h"
+
+//
+// Globals
+//
+const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f);
+
+// Used to hide the flashing text cursor when window doesn't have focus.
+BOOL gShowTextEditCursor = TRUE;
+
+// Language for UI construction
+LLString gLanguage = "english-usa";
+std::map<LLString, LLString> gTranslation;
+std::list<LLString> gUntranslated;
+
+LLControlGroup* LLUI::sConfigGroup = NULL;
+LLControlGroup* LLUI::sColorsGroup = NULL;
+LLControlGroup* LLUI::sAssetsGroup = NULL;
+LLImageProviderInterface* LLUI::sImageProvider = NULL;
+LLUIAudioCallback LLUI::sAudioCallback = NULL;
+LLVector2 LLUI::sGLScaleFactor(1.f, 1.f);
+LLWindow* LLUI::sWindow = NULL;
+BOOL LLUI::sShowXUINames = FALSE;
+//
+// Functions
+//
+void make_ui_sound(const LLString& name)
+{
+ if (!LLUI::sConfigGroup->controlExists(name))
+ {
+ llwarns << "tried to make ui sound for unknown sound name: " << name << llendl;
+ }
+ else
+ {
+ LLUUID uuid(LLUI::sConfigGroup->getString(name));
+ if (uuid.isNull())
+ {
+ if ("00000000-0000-0000-0000-000000000000" == LLUI::sConfigGroup->getString(name))
+ {
+ if (LLUI::sConfigGroup->getBOOL("UISndDebugSpamToggle"))
+ {
+ llinfos << "ui sound name: " << name << " triggered but silent (null uuid)" << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "ui sound named: " << name << " does not translate to a valid uuid" << llendl;
+ }
+
+ }
+ else if (LLUI::sAudioCallback != NULL)
+ {
+ if (LLUI::sConfigGroup->getBOOL("UISndDebugSpamToggle"))
+ {
+ llinfos << "ui sound name: " << name << llendl;
+ }
+ LLUI::sAudioCallback(uuid, LLUI::sConfigGroup->getF32("AudioLevelUI"));
+ }
+ }
+}
+
+BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom)
+{
+ if (x < left || right < x) return FALSE;
+ if (y < bottom || top < y) return FALSE;
+ return TRUE;
+}
+
+
+// Puts GL into 2D drawing mode by turning off lighting, setting to an
+// orthographic projection, etc.
+void gl_state_for_2d(S32 width, S32 height)
+{
+ stop_glerror();
+ F32 window_width = (F32) width;//gViewerWindow->getWindowWidth();
+ F32 window_height = (F32) height;//gViewerWindow->getWindowHeight();
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0.0f, window_width, 0.0f, window_height, -1.0f, 1.0f);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ stop_glerror();
+}
+
+
+void gl_draw_x(const LLRect& rect, const LLColor4& color)
+{
+ LLGLSNoTexture no_texture;
+
+ glColor4fv( color.mV );
+
+ glBegin( GL_LINES );
+ glVertex2i( rect.mLeft, rect.mTop );
+ glVertex2i( rect.mRight, rect.mBottom );
+ glVertex2i( rect.mLeft, rect.mBottom );
+ glVertex2i( rect.mRight, rect.mTop );
+ glEnd();
+}
+
+
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset, BOOL filled)
+{
+ glColor4fv(color.mV);
+ gl_rect_2d_offset_local(left, top, right, bottom, pixel_offset, filled);
+}
+
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset, BOOL filled)
+{
+ glPushMatrix();
+ left += LLFontGL::sCurOrigin.mX;
+ right += LLFontGL::sCurOrigin.mX;
+ bottom += LLFontGL::sCurOrigin.mY;
+ top += LLFontGL::sCurOrigin.mY;
+
+ glLoadIdentity();
+ gl_rect_2d(llfloor((F32)left * LLUI::sGLScaleFactor.mV[VX]) - pixel_offset,
+ llfloor((F32)top * LLUI::sGLScaleFactor.mV[VY]) + pixel_offset,
+ llfloor((F32)right * LLUI::sGLScaleFactor.mV[VX]) + pixel_offset,
+ llfloor((F32)bottom * LLUI::sGLScaleFactor.mV[VY]) - pixel_offset,
+ filled);
+ glPopMatrix();
+}
+
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled )
+{
+ stop_glerror();
+ LLGLSNoTexture no_texture;
+
+ // Counterclockwise quad will face the viewer
+ if( filled )
+ {
+ glBegin( GL_QUADS );
+ glVertex2i(left, top);
+ glVertex2i(left, bottom);
+ glVertex2i(right, bottom);
+ glVertex2i(right, top);
+ glEnd();
+ }
+ else
+ {
+ if( gGLManager.mATIOffsetVerticalLines )
+ {
+ // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
+ glBegin( GL_LINES );
+
+ // Verticals
+ glVertex2i(left + 1, top);
+ glVertex2i(left + 1, bottom);
+
+ glVertex2i(right, bottom);
+ glVertex2i(right, top);
+
+ // Horizontals
+ top--;
+ right--;
+ glVertex2i(left, bottom);
+ glVertex2i(right, bottom);
+
+ glVertex2i(left, top);
+ glVertex2i(right, top);
+ glEnd();
+ }
+ else
+ {
+ top--;
+ right--;
+ glBegin( GL_LINE_STRIP );
+ glVertex2i(left, top);
+ glVertex2i(left, bottom);
+ glVertex2i(right, bottom);
+ glVertex2i(right, top);
+ glVertex2i(left, top);
+ glEnd();
+ }
+ }
+ stop_glerror();
+}
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, BOOL filled )
+{
+ glColor4fv( color.mV );
+ gl_rect_2d( left, top, right, bottom, filled );
+}
+
+
+void gl_rect_2d( const LLRect& rect, const LLColor4& color, BOOL filled )
+{
+ glColor4fv( color.mV );
+ gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
+}
+
+// Given a rectangle on the screen, draws a drop shadow _outside_
+// the right and bottom edges of it. Along the right it has width "lines"
+// and along the bottom it has height "lines".
+void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines)
+{
+ stop_glerror();
+ LLGLSNoTexture no_texture;
+
+ // HACK: Overlap with the rectangle by a single pixel.
+ right--;
+ bottom++;
+ lines++;
+
+ LLColor4 end_color = start_color;
+ end_color.mV[VALPHA] = 0.f;
+
+ glBegin(GL_QUADS);
+
+ // Right edge, CCW faces screen
+ glColor4fv(start_color.mV);
+ glVertex2i(right, top-lines);
+ glVertex2i(right, bottom);
+ glColor4fv(end_color.mV);
+ glVertex2i(right+lines, bottom);
+ glVertex2i(right+lines, top-lines);
+
+ // Bottom edge, CCW faces screen
+ glColor4fv(start_color.mV);
+ glVertex2i(right, bottom);
+ glVertex2i(left+lines, bottom);
+ glColor4fv(end_color.mV);
+ glVertex2i(left+lines, bottom-lines);
+ glVertex2i(right, bottom-lines);
+
+ // bottom left Corner
+ glColor4fv(start_color.mV);
+ glVertex2i(left+lines, bottom);
+ glColor4fv(end_color.mV);
+ glVertex2i(left, bottom);
+ // make the bottom left corner not sharp
+ glVertex2i(left+1, bottom-lines+1);
+ glVertex2i(left+lines, bottom-lines);
+
+ // bottom right corner
+ glColor4fv(start_color.mV);
+ glVertex2i(right, bottom);
+ glColor4fv(end_color.mV);
+ glVertex2i(right, bottom-lines);
+ // make the rightmost corner not sharp
+ glVertex2i(right+lines-1, bottom-lines+1);
+ glVertex2i(right+lines, bottom);
+
+ // top right corner
+ glColor4fv(start_color.mV);
+ glVertex2i( right, top-lines );
+ glColor4fv(end_color.mV);
+ glVertex2i( right+lines, top-lines );
+ // make the corner not sharp
+ glVertex2i( right+lines-1, top-1 );
+ glVertex2i( right, top );
+
+ glEnd();
+ stop_glerror();
+}
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2 )
+{
+ // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
+ if( gGLManager.mATIOffsetVerticalLines && (x1 == x2) )
+ {
+ x1++;
+ x2++;
+ y1++;
+ y2++;
+ }
+
+ LLGLSNoTexture no_texture;
+
+ glBegin(GL_LINES);
+ glVertex2i(x1, y1);
+ glVertex2i(x2, y2);
+ glEnd();
+}
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color )
+{
+ // Work around bug in ATI driver: vertical lines are offset by (-1,-1)
+ if( gGLManager.mATIOffsetVerticalLines && (x1 == x2) )
+ {
+ x1++;
+ x2++;
+ y1++;
+ y2++;
+ }
+
+ LLGLSNoTexture no_texture;
+
+ glColor4fv( color.mV );
+
+ glBegin(GL_LINES);
+ glVertex2i(x1, y1);
+ glVertex2i(x2, y2);
+ glEnd();
+}
+
+void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, BOOL filled)
+{
+ LLGLSNoTexture no_texture;
+
+ glColor4fv(color.mV);
+
+ if (filled)
+ {
+ glBegin(GL_TRIANGLES);
+ }
+ else
+ {
+ glBegin(GL_LINE_LOOP);
+ }
+ glVertex2i(x1, y1);
+ glVertex2i(x2, y2);
+ glVertex2i(x3, y3);
+ glEnd();
+}
+
+void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac)
+{
+ LLGLSNoTexture no_texture;
+
+ length = llmin((S32)(max_frac*(right - left)), length);
+ length = llmin((S32)(max_frac*(top - bottom)), length);
+ glBegin(GL_LINES);
+ glVertex2i(left, top);
+ glVertex2i(left + length, top);
+
+ glVertex2i(left, top);
+ glVertex2i(left, top - length);
+
+ glVertex2i(left, bottom);
+ glVertex2i(left + length, bottom);
+
+ glVertex2i(left, bottom);
+ glVertex2i(left, bottom + length);
+
+ glVertex2i(right, top);
+ glVertex2i(right - length, top);
+
+ glVertex2i(right, top);
+ glVertex2i(right, top - length);
+
+ glVertex2i(right, bottom);
+ glVertex2i(right - length, bottom);
+
+ glVertex2i(right, bottom);
+ glVertex2i(right, bottom + length);
+ glEnd();
+}
+
+
+void gl_draw_image( S32 x, S32 y, LLImageGL* image, const LLColor4& color )
+{
+ gl_draw_scaled_rotated_image( x, y, image->getWidth(), image->getHeight(), 0.f, image, color );
+}
+
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color)
+{
+ gl_draw_scaled_rotated_image( x, y, width, height, 0.f, image, color );
+}
+
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4& color, BOOL solid_color)
+{
+ stop_glerror();
+ F32 border_scale = 1.f;
+
+ if (border_height * 2 > height)
+ {
+ border_scale = (F32)height / ((F32)border_height * 2.f);
+ }
+ if (border_width * 2 > width)
+ {
+ border_scale = llmin(border_scale, (F32)width / ((F32)border_width * 2.f));
+ }
+
+ // scale screen size of borders down
+ S32 scaled_border_width = llfloor(border_scale * (F32)border_width);
+ S32 scaled_border_height = llfloor(border_scale * (F32)border_height);
+
+ LLGLSUIDefault gls_ui;
+
+ if (solid_color)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+ }
+
+ glPushMatrix();
+ {
+ glTranslatef((F32)x, (F32)y, 0.f);
+
+ image->bind();
+
+ glColor4fv(color.mV);
+
+ F32 border_width_fraction = (F32)border_width / (F32)image->getWidth();
+ F32 border_height_fraction = (F32)border_height / (F32)image->getHeight();
+
+ glBegin(GL_QUADS);
+ {
+ // draw bottom left
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(border_width_fraction, 0.f);
+ glVertex2i(scaled_border_width, 0);
+
+ glTexCoord2f(border_width_fraction, border_height_fraction);
+ glVertex2i(scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(0.f, border_height_fraction);
+ glVertex2i(0, scaled_border_height);
+
+ // draw bottom middle
+ glTexCoord2f(border_width_fraction, 0.f);
+ glVertex2i(scaled_border_width, 0);
+
+ glTexCoord2f(1.f - border_width_fraction, 0.f);
+ glVertex2i(width - scaled_border_width, 0);
+
+ glTexCoord2f(1.f - border_width_fraction, border_height_fraction);
+ glVertex2i(width - scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, border_height_fraction);
+ glVertex2i(scaled_border_width, scaled_border_height);
+
+ // draw bottom right
+ glTexCoord2f(1.f - border_width_fraction, 0.f);
+ glVertex2i(width - scaled_border_width, 0);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, 0);
+
+ glTexCoord2f(1.f, border_height_fraction);
+ glVertex2i(width, scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, border_height_fraction);
+ glVertex2i(width - scaled_border_width, scaled_border_height);
+
+ // draw left
+ glTexCoord2f(0.f, border_height_fraction);
+ glVertex2i(0, scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, border_height_fraction);
+ glVertex2i(scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(0.f, 1.f - border_height_fraction);
+ glVertex2i(0, height - scaled_border_height);
+
+ // draw middle
+ glTexCoord2f(border_width_fraction, border_height_fraction);
+ glVertex2i(scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, border_height_fraction);
+ glVertex2i(width - scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(width - scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(scaled_border_width, height - scaled_border_height);
+
+ // draw right
+ glTexCoord2f(1.f - border_width_fraction, border_height_fraction);
+ glVertex2i(width - scaled_border_width, scaled_border_height);
+
+ glTexCoord2f(1.f, border_height_fraction);
+ glVertex2i(width, scaled_border_height);
+
+ glTexCoord2f(1.f, 1.f - border_height_fraction);
+ glVertex2i(width, height - scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(width - scaled_border_width, height - scaled_border_height);
+
+ // draw top left
+ glTexCoord2f(0.f, 1.f - border_height_fraction);
+ glVertex2i(0, height - scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(border_width_fraction, 1.f);
+ glVertex2i(scaled_border_width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(0, height);
+
+ // draw top middle
+ glTexCoord2f(border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(width - scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(1.f - border_width_fraction, 1.f);
+ glVertex2i(width - scaled_border_width, height);
+
+ glTexCoord2f(border_width_fraction, 1.f);
+ glVertex2i(scaled_border_width, height);
+
+ // draw top right
+ glTexCoord2f(1.f - border_width_fraction, 1.f - border_height_fraction);
+ glVertex2i(width - scaled_border_width, height - scaled_border_height);
+
+ glTexCoord2f(1.f, 1.f - border_height_fraction);
+ glVertex2i(width, height - scaled_border_height);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(1.f - border_width_fraction, 1.f);
+ glVertex2i(width - scaled_border_width, height);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+
+ if (solid_color)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+}
+
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color)
+{
+ gl_draw_scaled_rotated_image( x, y, image->getWidth(), image->getHeight(), degrees, image, color );
+}
+
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees, LLImageGL* image, const LLColor4& color)
+{
+ LLGLSUIDefault gls_ui;
+
+ glPushMatrix();
+ {
+ glTranslatef((F32)x, (F32)y, 0.f);
+ if( degrees )
+ {
+ F32 offset_x = F32(width/2);
+ F32 offset_y = F32(height/2);
+ glTranslatef( offset_x, offset_y, 0.f);
+ glRotatef( degrees, 0.f, 0.f, 1.f );
+ glTranslatef( -offset_x, -offset_y, 0.f );
+ }
+
+ image->bind();
+
+ glColor4fv(color.mV);
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height );
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(0, height );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+}
+
+
+void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color)
+{
+ LLGLSUIDefault gls_ui;
+
+ glPushMatrix();
+ {
+ glTranslatef((F32)x, (F32)y, 0.f);
+
+ image->bind();
+
+ glColor4fv(color.mV);
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, height );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, height );
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+}
+
+
+void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase )
+{
+ phase = fmod(phase, 1.f);
+
+ S32 shift = S32(phase * 4.f) % 4;
+
+ // Stippled line
+ LLGLEnable stipple(GL_LINE_STIPPLE);
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], color.mV[VALPHA]);
+ glLineWidth(2.5f);
+ glLineStipple(2, 0x3333 << shift);
+
+ glBegin(GL_LINES);
+ {
+ glVertex3fv( start.mV );
+ glVertex3fv( end.mV );
+ }
+ glEnd();
+
+ LLUI::setLineWidth(1.f);
+}
+
+
+void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom)
+{
+ glColor4fv( LLColor4::white.mV );
+ glLogicOp( GL_XOR );
+ stop_glerror();
+
+ glBegin(GL_QUADS);
+ glVertex2i(left, top);
+ glVertex2i(left, bottom);
+ glVertex2i(right, bottom);
+ glVertex2i(right, top);
+ glEnd();
+
+ glLogicOp( GL_COPY );
+ stop_glerror();
+}
+
+
+void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F32 start_angle, F32 end_angle)
+{
+ if (end_angle < start_angle)
+ {
+ end_angle += F_TWO_PI;
+ }
+
+ glPushMatrix();
+ {
+ glTranslatef(center_x, center_y, 0.f);
+
+ // Inexact, but reasonably fast.
+ F32 delta = (end_angle - start_angle) / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = cosf(start_angle) * radius;
+ F32 y = sinf(start_angle) * radius;
+
+ if (filled)
+ {
+ glBegin(GL_TRIANGLE_FAN);
+ glVertex2f(0.f, 0.f);
+ // make sure circle is complete
+ steps += 1;
+ }
+ else
+ {
+ glBegin(GL_LINE_STRIP);
+ }
+
+ while( steps-- )
+ {
+ // Successive rotations
+ glVertex2f( x, y );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ glEnd();
+ }
+ glPopMatrix();
+}
+
+void gl_circle_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled)
+{
+ glPushMatrix();
+ {
+ LLGLSNoTexture gls_no_texture;
+ glTranslatef(center_x, center_y, 0.f);
+
+ // Inexact, but reasonably fast.
+ F32 delta = F_TWO_PI / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = radius;
+ F32 y = 0.f;
+
+ if (filled)
+ {
+ glBegin(GL_TRIANGLE_FAN);
+ glVertex2f(0.f, 0.f);
+ // make sure circle is complete
+ steps += 1;
+ }
+ else
+ {
+ glBegin(GL_LINE_LOOP);
+ }
+
+ while( steps-- )
+ {
+ // Successive rotations
+ glVertex2f( x, y );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ glEnd();
+ }
+ glPopMatrix();
+}
+
+// Renders a ring with sides (tube shape)
+void gl_deep_circle( F32 radius, F32 depth, S32 steps )
+{
+ F32 x = radius;
+ F32 y = 0.f;
+ F32 angle_delta = F_TWO_PI / (F32)steps;
+ glBegin( GL_TRIANGLE_STRIP );
+ {
+ S32 step = steps + 1; // An extra step to close the circle.
+ while( step-- )
+ {
+ glVertex3f( x, y, depth );
+ glVertex3f( x, y, 0.f );
+
+ F32 x_new = x * cosf(angle_delta) - y * sinf(angle_delta);
+ y = x * sinf(angle_delta) + y * cosf(angle_delta);
+ x = x_new;
+ }
+ }
+ glEnd();
+}
+
+void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center )
+{
+ glPushMatrix();
+ {
+ glTranslatef(0.f, 0.f, -width / 2);
+ if( render_center )
+ {
+ glColor4fv(center_color.mV);
+ gl_deep_circle( radius, width, steps );
+ }
+ else
+ {
+ gl_washer_2d(radius, radius - width, steps, side_color, side_color);
+ glTranslatef(0.f, 0.f, width);
+ gl_washer_2d(radius - width, radius, steps, side_color, side_color);
+ }
+ }
+ glPopMatrix();
+}
+
+// Draw gray and white checkerboard with black border
+void gl_rect_2d_checkerboard(const LLRect& rect)
+{
+ // Initialize the first time this is called.
+ const S32 PIXELS = 32;
+ static GLubyte checkerboard[PIXELS * PIXELS];
+ static BOOL first = TRUE;
+ if( first )
+ {
+ for( S32 i = 0; i < PIXELS; i++ )
+ {
+ for( S32 j = 0; j < PIXELS; j++ )
+ {
+ checkerboard[i * PIXELS + j] = ((i & 1) ^ (j & 1)) * 0xFF;
+ }
+ }
+ first = FALSE;
+ }
+
+ LLGLSNoTexture gls_no_texture;
+
+ // ...white squares
+ glColor3f( 1.f, 1.f, 1.f );
+ gl_rect_2d(rect);
+
+ // ...gray squares
+ glColor3f( .7f, .7f, .7f );
+ glPolygonStipple( checkerboard );
+
+ LLGLEnable polygon_stipple(GL_POLYGON_STIPPLE);
+ gl_rect_2d(rect);
+}
+
+
+// Draws the area between two concentric circles, like
+// a doughnut or washer.
+void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
+{
+ const F32 DELTA = F_TWO_PI / steps;
+ const F32 SIN_DELTA = sin( DELTA );
+ const F32 COS_DELTA = cos( DELTA );
+
+ F32 x1 = outer_radius;
+ F32 y1 = 0.f;
+ F32 x2 = inner_radius;
+ F32 y2 = 0.f;
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin( GL_TRIANGLE_STRIP );
+ {
+ steps += 1; // An extra step to close the circle.
+ while( steps-- )
+ {
+ glColor4fv(outer_color.mV);
+ glVertex2f( x1, y1 );
+ glColor4fv(inner_color.mV);
+ glVertex2f( x2, y2 );
+
+ F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
+ y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
+ x1 = x1_new;
+
+ F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
+ y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
+ x2 = x2_new;
+ }
+ }
+ glEnd();
+}
+
+// Draws the area between two concentric circles, like
+// a doughnut or washer.
+void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color)
+{
+ const F32 DELTA = (end_radians - start_radians) / steps;
+ const F32 SIN_DELTA = sin( DELTA );
+ const F32 COS_DELTA = cos( DELTA );
+
+ F32 x1 = outer_radius * cos( start_radians );
+ F32 y1 = outer_radius * sin( start_radians );
+ F32 x2 = inner_radius * cos( start_radians );
+ F32 y2 = inner_radius * sin( start_radians );
+
+ LLGLSNoTexture gls_no_texture;
+ glBegin( GL_TRIANGLE_STRIP );
+ {
+ steps += 1; // An extra step to close the circle.
+ while( steps-- )
+ {
+ glColor4fv(outer_color.mV);
+ glVertex2f( x1, y1 );
+ glColor4fv(inner_color.mV);
+ glVertex2f( x2, y2 );
+
+ F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
+ y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
+ x1 = x1_new;
+
+ F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
+ y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
+ x2 = x2_new;
+ }
+ }
+ glEnd();
+}
+
+// Draws spokes around a circle.
+void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color)
+{
+ const F32 DELTA = F_TWO_PI / count;
+ const F32 HALF_DELTA = DELTA * 0.5f;
+ const F32 SIN_DELTA = sin( DELTA );
+ const F32 COS_DELTA = cos( DELTA );
+
+ F32 x1 = outer_radius * cos( HALF_DELTA );
+ F32 y1 = outer_radius * sin( HALF_DELTA );
+ F32 x2 = inner_radius * cos( HALF_DELTA );
+ F32 y2 = inner_radius * sin( HALF_DELTA );
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin( GL_LINES );
+ {
+ while( count-- )
+ {
+ glColor4fv(outer_color.mV);
+ glVertex2f( x1, y1 );
+ glColor4fv(inner_color.mV);
+ glVertex2f( x2, y2 );
+
+ F32 x1_new = x1 * COS_DELTA - y1 * SIN_DELTA;
+ y1 = x1 * SIN_DELTA + y1 * COS_DELTA;
+ x1 = x1_new;
+
+ F32 x2_new = x2 * COS_DELTA - y2 * SIN_DELTA;
+ y2 = x2 * SIN_DELTA + y2 * COS_DELTA;
+ x2 = x2_new;
+ }
+ }
+ glEnd();
+}
+
+void gl_rect_2d_simple_tex( S32 width, S32 height )
+{
+ glBegin( GL_QUADS );
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(0, height);
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, 0);
+
+ glEnd();
+}
+
+void gl_rect_2d_simple( S32 width, S32 height )
+{
+ glBegin( GL_QUADS );
+ glVertex2i(width, height);
+ glVertex2i(0, height);
+ glVertex2i(0, 0);
+ glVertex2i(width, 0);
+ glEnd();
+}
+
+void gl_segmented_rect_2d_tex(const S32 left,
+ const S32 top,
+ const S32 right,
+ const S32 bottom,
+ const S32 texture_width,
+ const S32 texture_height,
+ const S32 border_size,
+ const U32 edges)
+{
+ S32 width = llabs(right - left);
+ S32 height = llabs(top - bottom);
+
+ glPushMatrix();
+
+ glTranslatef((F32)left, (F32)bottom, 0.f);
+ LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
+
+ if (border_uv_scale.mV[VX] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
+ }
+ if (border_uv_scale.mV[VY] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
+ }
+
+ F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
+ LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 width_vec((F32)width, 0.f);
+ LLVector2 height_vec(0.f, (F32)height);
+
+ glBegin(GL_QUADS);
+ {
+ // draw bottom left
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(0.f, 0.f);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 0.f);
+ glVertex2fv(border_width_left.mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + border_height_bottom).mV);
+
+ glTexCoord2f(0.f, border_uv_scale.mV[VY]);
+ glVertex2fv(border_height_bottom.mV);
+
+ // draw bottom middle
+ glTexCoord2f(border_uv_scale.mV[VX], 0.f);
+ glVertex2fv(border_width_left.mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ glVertex2fv((width_vec - border_width_right).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + border_height_bottom).mV);
+
+ // draw bottom right
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ glVertex2fv((width_vec - border_width_right).mV);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2fv(width_vec.mV);
+
+ glTexCoord2f(1.f, border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec + border_height_bottom).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ // draw left
+ glTexCoord2f(0.f, border_uv_scale.mV[VY]);
+ glVertex2fv(border_height_bottom.mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + border_height_bottom).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ glTexCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((height_vec - border_height_top).mV);
+
+ // draw middle
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + border_height_bottom).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ // draw right
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + border_height_bottom).mV);
+
+ glTexCoord2f(1.f, border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec + border_height_bottom).mV);
+
+ glTexCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ // draw top left
+ glTexCoord2f(0.f, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((height_vec - border_height_top).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((border_width_left + height_vec).mV);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2fv((height_vec).mV);
+
+ // draw top middle
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((border_width_left + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((width_vec - border_width_right + height_vec).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((border_width_left + height_vec).mV);
+
+ // draw top right
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec - border_width_right + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((width_vec + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2fv((width_vec + height_vec).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((width_vec - border_width_right + height_vec).mV);
+ }
+ glEnd();
+
+ glPopMatrix();
+}
+
+void gl_segmented_rect_2d_fragment_tex(const S32 left,
+ const S32 top,
+ const S32 right,
+ const S32 bottom,
+ const S32 texture_width,
+ const S32 texture_height,
+ const S32 border_size,
+ const F32 start_fragment,
+ const F32 end_fragment,
+ const U32 edges)
+{
+ S32 width = llabs(right - left);
+ S32 height = llabs(top - bottom);
+
+ glPushMatrix();
+
+ glTranslatef((F32)left, (F32)bottom, 0.f);
+ LLVector2 border_uv_scale((F32)border_size / (F32)texture_width, (F32)border_size / (F32)texture_height);
+
+ if (border_uv_scale.mV[VX] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VX];
+ }
+ if (border_uv_scale.mV[VY] > 0.5f)
+ {
+ border_uv_scale *= 0.5f / border_uv_scale.mV[VY];
+ }
+
+ F32 border_scale = llmin((F32)border_size, (F32)width * 0.5f, (F32)height * 0.5f);
+ LLVector2 border_width_left = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_width_right = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? LLVector2(border_scale, 0.f) : LLVector2::zero;
+ LLVector2 border_height_bottom = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 border_height_top = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? LLVector2(0.f, border_scale) : LLVector2::zero;
+ LLVector2 width_vec((F32)width, 0.f);
+ LLVector2 height_vec(0.f, (F32)height);
+
+ F32 middle_start = border_scale / (F32)width;
+ F32 middle_end = 1.f - middle_start;
+
+ F32 u_min;
+ F32 u_max;
+ LLVector2 x_min;
+ LLVector2 x_max;
+
+ glBegin(GL_QUADS);
+ {
+ if (start_fragment < middle_start)
+ {
+ u_min = (start_fragment / middle_start) * border_uv_scale.mV[VX];
+ u_max = llmin(end_fragment / middle_start, 1.f) * border_uv_scale.mV[VX];
+ x_min = (start_fragment / middle_start) * border_width_left;
+ x_max = llmin(end_fragment / middle_start, 1.f) * border_width_left;
+
+ // draw bottom left
+ glTexCoord2f(u_min, 0.f);
+ glVertex2fv(x_min.mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 0.f);
+ glVertex2fv(x_max.mV);
+
+ glTexCoord2f(u_max, border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(u_min, border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ // draw left
+ glTexCoord2f(u_min, border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ glTexCoord2f(u_max, border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top left
+ glTexCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_max, 1.f);
+ glVertex2fv((x_max + height_vec).mV);
+
+ glTexCoord2f(u_min, 1.f);
+ glVertex2fv((x_min + height_vec).mV);
+ }
+
+ if (end_fragment > middle_start || start_fragment < middle_end)
+ {
+ x_min = border_width_left + ((llclamp(start_fragment, middle_start, middle_end) - middle_start)) * width_vec;
+ x_max = border_width_left + ((llclamp(end_fragment, middle_start, middle_end) - middle_start)) * width_vec;
+
+ // draw bottom middle
+ glTexCoord2f(border_uv_scale.mV[VX], 0.f);
+ glVertex2fv(x_min.mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 0.f);
+ glVertex2fv((x_max).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ // draw middle
+ glTexCoord2f(border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top middle
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(1.f - border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((x_max + height_vec).mV);
+
+ glTexCoord2f(border_uv_scale.mV[VX], 1.f);
+ glVertex2fv((x_min + height_vec).mV);
+ }
+
+ if (end_fragment > middle_end)
+ {
+ u_min = (1.f - llmax(0.f, ((start_fragment - middle_end) / middle_start))) * border_uv_scale.mV[VX];
+ u_max = (1.f - ((end_fragment - middle_end) / middle_start)) * border_uv_scale.mV[VX];
+ x_min = width_vec - ((1.f - llmax(0.f, ((start_fragment - middle_end) / middle_start))) * border_width_right);
+ x_max = width_vec - ((1.f - ((end_fragment - middle_end) / middle_start)) * border_width_right);
+
+ // draw bottom right
+ glTexCoord2f(u_min, 0.f);
+ glVertex2fv((x_min).mV);
+
+ glTexCoord2f(u_max, 0.f);
+ glVertex2fv(x_max.mV);
+
+ glTexCoord2f(u_max, border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(u_min, border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ // draw right
+ glTexCoord2f(u_min, border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + border_height_bottom).mV);
+
+ glTexCoord2f(u_max, border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + border_height_bottom).mV);
+
+ glTexCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ // draw top right
+ glTexCoord2f(u_min, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_min + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_max, 1.f - border_uv_scale.mV[VY]);
+ glVertex2fv((x_max + height_vec - border_height_top).mV);
+
+ glTexCoord2f(u_max, 1.f);
+ glVertex2fv((x_max + height_vec).mV);
+
+ glTexCoord2f(u_min, 1.f);
+ glVertex2fv((x_min + height_vec).mV);
+ }
+ }
+ glEnd();
+
+ glPopMatrix();
+}
+
+void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width,
+ const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec,
+ const U32 edges)
+{
+ LLVector3 left_border_width = ((edges & (~(U32)ROUNDED_RECT_RIGHT)) != 0) ? border_width : LLVector3::zero;
+ LLVector3 right_border_width = ((edges & (~(U32)ROUNDED_RECT_LEFT)) != 0) ? border_width : LLVector3::zero;
+
+ LLVector3 top_border_height = ((edges & (~(U32)ROUNDED_RECT_BOTTOM)) != 0) ? border_height : LLVector3::zero;
+ LLVector3 bottom_border_height = ((edges & (~(U32)ROUNDED_RECT_TOP)) != 0) ? border_height : LLVector3::zero;
+
+ glBegin(GL_QUADS);
+ {
+ // draw bottom left
+ glTexCoord2f(0.f, 0.f);
+ glVertex3f(0.f, 0.f, 0.f);
+
+ glTexCoord2f(border_scale.mV[VX], 0.f);
+ glVertex3fv(left_border_width.mV);
+
+ glTexCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((left_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(0.f, border_scale.mV[VY]);
+ glVertex3fv(bottom_border_height.mV);
+
+ // draw bottom middle
+ glTexCoord2f(border_scale.mV[VX], 0.f);
+ glVertex3fv(left_border_width.mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 0.f);
+ glVertex3fv((width_vec - right_border_width).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((left_border_width + bottom_border_height).mV);
+
+ // draw bottom right
+ glTexCoord2f(1.f - border_scale.mV[VX], 0.f);
+ glVertex3fv((width_vec - right_border_width).mV);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex3fv(width_vec.mV);
+
+ glTexCoord2f(1.f, border_scale.mV[VY]);
+ glVertex3fv((width_vec + bottom_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+
+ // draw left
+ glTexCoord2f(0.f, border_scale.mV[VY]);
+ glVertex3fv(bottom_border_height.mV);
+
+ glTexCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((left_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((left_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(0.f, 1.f - border_scale.mV[VY]);
+ glVertex3fv((height_vec - top_border_height).mV);
+
+ // draw middle
+ glTexCoord2f(border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((left_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((left_border_width + height_vec - top_border_height).mV);
+
+ // draw right
+ glTexCoord2f(1.f - border_scale.mV[VX], border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + bottom_border_height).mV);
+
+ glTexCoord2f(1.f, border_scale.mV[VY]);
+ glVertex3fv((width_vec + bottom_border_height).mV);
+
+ glTexCoord2f(1.f, 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec + height_vec - top_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+
+ // draw top left
+ glTexCoord2f(0.f, 1.f - border_scale.mV[VY]);
+ glVertex3fv((height_vec - top_border_height).mV);
+
+ glTexCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((left_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(border_scale.mV[VX], 1.f);
+ glVertex3fv((left_border_width + height_vec).mV);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex3fv((height_vec).mV);
+
+ // draw top middle
+ glTexCoord2f(border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((left_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f);
+ glVertex3fv((width_vec - right_border_width + height_vec).mV);
+
+ glTexCoord2f(border_scale.mV[VX], 1.f);
+ glVertex3fv((left_border_width + height_vec).mV);
+
+ // draw top right
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec - right_border_width + height_vec - top_border_height).mV);
+
+ glTexCoord2f(1.f, 1.f - border_scale.mV[VY]);
+ glVertex3fv((width_vec + height_vec - top_border_height).mV);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex3fv((width_vec + height_vec).mV);
+
+ glTexCoord2f(1.f - border_scale.mV[VX], 1.f);
+ glVertex3fv((width_vec - right_border_width + height_vec).mV);
+ }
+ glEnd();
+}
+
+void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec)
+{
+ gl_segmented_rect_3d_tex(border_scale, border_width, border_height, width_vec, height_vec, ROUNDED_RECT_TOP);
+}
+
+#if 0 // No longer used
+void load_tr(const LLString& lang)
+{
+ LLString inname = "words." + lang + ".txt";
+ LLString filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, inname.c_str());
+
+ llifstream file;
+ file.open(filename.c_str(), std::ios_base::binary);
+ if (!file)
+ {
+ llinfos << "No translation dictionary for: " << filename << llendl;
+ return;
+ }
+
+ llinfos << "Reading language translation dictionary: " << filename << llendl;
+
+ gTranslation.clear();
+ gUntranslated.clear();
+
+ const S32 MAX_LINE_LEN = 1024;
+ char buffer[MAX_LINE_LEN];
+ while (!file.eof())
+ {
+ file.getline(buffer, MAX_LINE_LEN);
+ LLString line(buffer);
+ S32 commentpos = line.find("//");
+ if (commentpos != LLString::npos)
+ {
+ line = line.substr(0, commentpos);
+ }
+ S32 offset = line.find('\t');
+ if (offset != LLString::npos)
+ {
+ LLString english = line.substr(0,offset);
+ LLString translation = line.substr(offset+1);
+ //llinfos << "TR: " << english << " = " << translation << llendl;
+ gTranslation[english] = translation;
+ }
+ }
+
+ file.close();
+}
+
+void init_tr(const LLString& language)
+{
+ if (!language.empty())
+ {
+ gLanguage = language;
+ }
+ load_tr(gLanguage);
+}
+
+void cleanup_tr()
+{
+ // Dump untranslated phrases to help with translation
+ if (gUntranslated.size() > 0)
+ {
+ LLString outname = "untranslated_" + gLanguage + ".txt";
+ LLString outfilename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, outname.c_str());
+ llofstream outfile;
+ outfile.open(outfilename.c_str());
+ if (!outfile)
+ {
+ return;
+ }
+ llinfos << "Writing untranslated words to: " << outfilename << llendl;
+ LLString outtext;
+ for (std::list<LLString>::iterator iter = gUntranslated.begin();
+ iter != gUntranslated.end(); ++iter)
+ {
+ // output: english_phrase english_phrase
+ outtext += *iter;
+ outtext += "\t";
+ outtext += *iter;
+ outtext += "\n";
+ }
+ outfile << outtext.c_str();
+ outfile.close();
+ }
+}
+
+LLString tr(const LLString& english_string)
+{
+ std::map<LLString, LLString>::iterator it = gTranslation.find(english_string);
+ if (it != gTranslation.end())
+ {
+ return it->second;
+ }
+ else
+ {
+ gUntranslated.push_back(english_string);
+ return english_string;
+ }
+}
+
+#endif
+
+
+class LLShowXUINamesListener: public LLSimpleListener
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUI::sShowXUINames = (BOOL) event->getValue().asBoolean();
+ return true;
+ }
+};
+static LLShowXUINamesListener show_xui_names_listener;
+
+
+void LLUI::initClass(LLControlGroup* config,
+ LLControlGroup* colors,
+ LLControlGroup* assets,
+ LLImageProviderInterface* image_provider,
+ LLUIAudioCallback audio_callback,
+ const LLVector2* scale_factor,
+ const LLString& language)
+{
+ sConfigGroup = config;
+ sColorsGroup = colors;
+ sAssetsGroup = assets;
+ sImageProvider = image_provider;
+ sAudioCallback = audio_callback;
+ sGLScaleFactor = (scale_factor == NULL) ? LLVector2(1.f, 1.f) : *scale_factor;
+ sWindow = NULL; // set later in startup
+ LLFontGL::sShadowColor = colors->getColor("ColorDropShadow");
+
+ LLUI::sShowXUINames = LLUI::sConfigGroup->getBOOL("ShowXUINames");
+ LLUI::sConfigGroup->getControl("ShowXUINames")->addListener(&show_xui_names_listener);
+// init_tr(language);
+}
+
+void LLUI::cleanupClass()
+{
+// cleanup_tr();
+}
+
+
+//static
+void LLUI::translate(F32 x, F32 y, F32 z)
+{
+ glTranslatef(x,y,z);
+ LLFontGL::sCurOrigin.mX += (S32) x;
+ LLFontGL::sCurOrigin.mY += (S32) y;
+ LLFontGL::sCurOrigin.mZ += z;
+}
+
+//static
+void LLUI::pushMatrix()
+{
+ glPushMatrix();
+ LLFontGL::sOriginStack.push_back(LLFontGL::sCurOrigin);
+}
+
+//static
+void LLUI::popMatrix()
+{
+ glPopMatrix();
+ LLFontGL::sCurOrigin = *LLFontGL::sOriginStack.rbegin();
+ LLFontGL::sOriginStack.pop_back();
+}
+
+//static
+void LLUI::loadIdentity()
+{
+ glLoadIdentity();
+ LLFontGL::sCurOrigin.mX = 0;
+ LLFontGL::sCurOrigin.mY = 0;
+ LLFontGL::sCurOrigin.mZ = 0;
+}
+
+//static
+void LLUI::setScissorRegionScreen(const LLRect& rect)
+{
+ stop_glerror();
+ S32 x,y,w,h;
+ x = llround(rect.mLeft * LLUI::sGLScaleFactor.mV[VX]);
+ y = llround(rect.mBottom * LLUI::sGLScaleFactor.mV[VY]);
+ w = llround(rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]);
+ h = llround(rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]);
+ glScissor( x,y,w,h );
+ stop_glerror();
+}
+
+//static
+void LLUI::setScissorRegionLocal(const LLRect& rect)
+{
+ stop_glerror();
+ S32 screen_left = LLFontGL::sCurOrigin.mX + rect.mLeft;
+ S32 screen_bottom = LLFontGL::sCurOrigin.mY + rect.mBottom;
+
+ S32 x,y,w,h;
+
+ x = llround((F32)screen_left * LLUI::sGLScaleFactor.mV[VX]);
+ y = llround((F32)screen_bottom * LLUI::sGLScaleFactor.mV[VY]);
+ w = llround((F32)rect.getWidth() * LLUI::sGLScaleFactor.mV[VX]);
+ h = llround((F32)rect.getHeight() * LLUI::sGLScaleFactor.mV[VY]);
+
+ w = llmax(0,w);
+ h = llmax(0,h);
+
+ glScissor(x,y,w,h);
+ stop_glerror();
+}
+
+//static
+void LLUI::setScaleFactor(const LLVector2 &scale_factor)
+{
+ sGLScaleFactor = scale_factor;
+}
+
+//static
+void LLUI::setLineWidth(F32 width)
+{
+ glLineWidth(width * lerp(sGLScaleFactor.mV[VX], sGLScaleFactor.mV[VY], 0.5f));
+}
+
+//static
+void LLUI::setCursorPositionScreen(S32 x, S32 y)
+{
+ S32 screen_x, screen_y;
+ screen_x = llround((F32)x * sGLScaleFactor.mV[VX]);
+ screen_y = llround((F32)y * sGLScaleFactor.mV[VY]);
+
+ LLCoordWindow window_point;
+ LLView::getWindow()->convertCoords(LLCoordGL(screen_x, screen_y), &window_point);
+
+ LLView::getWindow()->setCursorPosition(window_point);
+}
+
+//static
+void LLUI::setCursorPositionLocal(LLView* viewp, S32 x, S32 y)
+{
+ S32 screen_x, screen_y;
+ viewp->localPointToScreen(x, y, &screen_x, &screen_y);
+
+ setCursorPositionScreen(screen_x, screen_y);
+}
+
+//static
+LLString LLUI::locateSkin(const LLString& filename)
+{
+ LLString slash = gDirUtilp->getDirDelimiter();
+ LLString found_file = filename;
+ if (!gDirUtilp->fileExists(found_file))
+ {
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS?
+ }
+ if (sConfigGroup && sConfigGroup->controlExists("Language"))
+ {
+ if (!gDirUtilp->fileExists(found_file))
+ {
+ LLString localization(sConfigGroup->getString("Language"));
+ LLString local_skin = "xui" + slash + localization + slash + filename;
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, local_skin);
+ }
+ }
+ if (!gDirUtilp->fileExists(found_file))
+ {
+ LLString local_skin = "xui" + slash + "en-us" + slash + filename;
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, local_skin);
+ }
+ if (!gDirUtilp->fileExists(found_file))
+ {
+ found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
+ }
+ return found_file;
+}
+
+//static
+LLVector2 LLUI::getWindowSize()
+{
+ LLCoordWindow window_rect;
+ sWindow->getSize(&window_rect);
+
+ return LLVector2(window_rect.mX / sGLScaleFactor.mV[VX], window_rect.mY / sGLScaleFactor.mV[VY]);
+}
+
+//static
+LLUUID LLUI::findAssetUUIDByName(const LLString &asset_name)
+{
+ if(asset_name == LLString::null) return LLUUID::null;
+ LLString foundValue = LLUI::sConfigGroup->findString(asset_name);
+ if(foundValue==LLString::null)
+ {
+ foundValue = LLUI::sAssetsGroup->findString(asset_name);
+ }
+ if(foundValue == LLString::null){
+ return LLUUID::null;
+ }
+ return LLUUID( foundValue );
+}
diff --git a/indra/llui/llui.h b/indra/llui/llui.h
new file mode 100644
index 0000000000..282e41a113
--- /dev/null
+++ b/indra/llui/llui.h
@@ -0,0 +1,255 @@
+/**
+ * @file llui.h
+ * @brief UI implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// All immediate-mode gl drawing should happen here.
+
+#ifndef LL_LLUI_H
+#define LL_LLUI_H
+
+#include "llrect.h"
+#include "llcontrol.h"
+#include "llrect.h"
+#include "llcoord.h"
+
+class LLColor4;
+class LLVector3;
+class LLVector2;
+class LLImageGL;
+class LLUUID;
+class LLWindow;
+class LLView;
+
+// UI colors
+extern const LLColor4 UI_VERTEX_COLOR;
+void make_ui_sound(const LLString& name);
+
+BOOL ui_point_in_rect(S32 x, S32 y, S32 left, S32 top, S32 right, S32 bottom);
+void gl_state_for_2d(S32 width, S32 height);
+
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2);
+void gl_line_2d(S32 x1, S32 y1, S32 x2, S32 y2, const LLColor4 &color );
+void gl_triangle_2d(S32 x1, S32 y1, S32 x2, S32 y2, S32 x3, S32 y3, const LLColor4& color, BOOL filled);
+void gl_rect_2d_simple( S32 width, S32 height );
+
+void gl_draw_x(const LLRect& rect, const LLColor4& color);
+
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, BOOL filled = TRUE );
+void gl_rect_2d(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, BOOL filled = TRUE );
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &color, S32 pixel_offset = 0, BOOL filled = TRUE );
+void gl_rect_2d_offset_local( S32 left, S32 top, S32 right, S32 bottom, S32 pixel_offset = 0, BOOL filled = TRUE );
+void gl_rect_2d(const LLRect& rect, BOOL filled = TRUE );
+void gl_rect_2d(const LLRect& rect, const LLColor4& color, BOOL filled = TRUE );
+void gl_rect_2d_checkerboard(const LLRect& rect);
+
+void gl_drop_shadow(S32 left, S32 top, S32 right, S32 bottom, const LLColor4 &start_color, S32 lines);
+
+void gl_circle_2d(F32 x, F32 y, F32 radius, S32 steps, BOOL filled);
+void gl_arc_2d(F32 center_x, F32 center_y, F32 radius, S32 steps, BOOL filled, F32 start_angle, F32 end_angle);
+void gl_deep_circle( F32 radius, F32 depth );
+void gl_ring( F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color, S32 steps, BOOL render_center );
+void gl_corners_2d(S32 left, S32 top, S32 right, S32 bottom, S32 length, F32 max_frac);
+void gl_washer_2d(F32 outer_radius, F32 inner_radius, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
+void gl_washer_segment_2d(F32 outer_radius, F32 inner_radius, F32 start_radians, F32 end_radians, S32 steps, const LLColor4& inner_color, const LLColor4& outer_color);
+void gl_washer_spokes_2d(F32 outer_radius, F32 inner_radius, S32 count, const LLColor4& inner_color, const LLColor4& outer_color);
+
+void gl_draw_image(S32 x, S32 y, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR);
+void gl_draw_scaled_image(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR);
+void gl_draw_rotated_image(S32 x, S32 y, F32 degrees, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR);
+void gl_draw_scaled_rotated_image(S32 x, S32 y, S32 width, S32 height, F32 degrees,LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR);
+void gl_draw_scaled_image_with_border(S32 x, S32 y, S32 border_width, S32 border_height, S32 width, S32 height, LLImageGL* image, const LLColor4 &color, BOOL solid_color = FALSE);
+// Flip vertical, used for LLFloaterHTML
+void gl_draw_scaled_image_inverted(S32 x, S32 y, S32 width, S32 height, LLImageGL* image, const LLColor4& color = UI_VERTEX_COLOR);
+
+void gl_rect_2d_xor(S32 left, S32 top, S32 right, S32 bottom);
+void gl_stippled_line_3d( const LLVector3& start, const LLVector3& end, const LLColor4& color, F32 phase = 0.f );
+
+void gl_rect_2d_simple_tex( S32 width, S32 height );
+
+// segmented rectangles
+
+/*
+ TL |______TOP_________| TR
+ /| |\
+ _/_|__________________|_\_
+ L| | MIDDLE | |R
+ _|_|__________________|_|_
+ \ | BOTTOM | /
+ BL\|__________________|/ BR
+ | |
+*/
+
+typedef enum e_rounded_edge
+{
+ ROUNDED_RECT_LEFT = 0x1,
+ ROUNDED_RECT_TOP = 0x2,
+ ROUNDED_RECT_RIGHT = 0x4,
+ ROUNDED_RECT_BOTTOM = 0x8,
+ ROUNDED_RECT_ALL = 0xf
+}ERoundedEdge;
+
+
+void gl_segmented_rect_2d_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const U32 edges = ROUNDED_RECT_ALL);
+void gl_segmented_rect_2d_fragment_tex(const S32 left, const S32 top, const S32 right, const S32 bottom, const S32 texture_width, const S32 texture_height, const S32 border_size, const F32 start_fragment, const F32 end_fragment, const U32 edges = ROUNDED_RECT_ALL);
+void gl_segmented_rect_3d_tex(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec, U32 edges = ROUNDED_RECT_ALL);
+void gl_segmented_rect_3d_tex_top(const LLVector2& border_scale, const LLVector3& border_width, const LLVector3& border_height, const LLVector3& width_vec, const LLVector3& height_vec);
+
+inline void gl_rect_2d( const LLRect& rect, BOOL filled )
+{
+ gl_rect_2d( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, filled );
+}
+
+inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, BOOL filled)
+{
+ gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled );
+}
+
+// No longer used
+// Initializes translation table
+// void init_tr();
+
+// Returns a string from the string table in the correct language
+// LLString tr(const LLString& english_chars);
+
+// Used to hide the flashing text cursor when window doesn't have focus.
+extern BOOL gShowTextEditCursor;
+
+// Language
+extern LLString gLanguage;
+
+class LLImageProviderInterface;
+typedef void (*LLUIAudioCallback)(const LLUUID& uuid, F32 volume);
+
+class LLUI
+{
+public:
+ static void initClass(LLControlGroup* config,
+ LLControlGroup* colors,
+ LLControlGroup* assets,
+ LLImageProviderInterface* image_provider,
+ LLUIAudioCallback audio_callback = NULL,
+ const LLVector2 *scale_factor = NULL,
+ const LLString& language = LLString::null);
+ static void cleanupClass();
+
+ static void pushMatrix();
+ static void popMatrix();
+ static void loadIdentity();
+ static void translate(F32 x, F32 y, F32 z = 0.0f);
+
+ //helper functions (should probably move free standing rendering helper functions here)
+ static LLString locateSkin(const LLString& filename);
+ static void setScissorRegionScreen(const LLRect& rect);
+ static void setScissorRegionLocal(const LLRect& rect); // works assuming LLUI::translate has been called
+ static void setCursorPositionScreen(S32 x, S32 y);
+ static void setCursorPositionLocal(LLView* viewp, S32 x, S32 y);
+ static void setScaleFactor(const LLVector2& scale_factor);
+ static void setLineWidth(F32 width);
+ static LLUUID findAssetUUIDByName(const LLString& name);
+ static LLVector2 getWindowSize();
+public:
+ static LLControlGroup* sConfigGroup;
+ static LLControlGroup* sColorsGroup;
+ static LLControlGroup* sAssetsGroup;
+ static LLImageProviderInterface* sImageProvider;
+ static LLUIAudioCallback sAudioCallback;
+ static LLVector2 sGLScaleFactor;
+ static LLWindow* sWindow;
+ static BOOL sShowXUINames;
+};
+
+// UI widgets
+// This MUST match UICtrlNames in lluictrlfactory.cpp
+typedef enum e_widget_type
+{
+ WIDGET_TYPE_VIEW = 0,
+ WIDGET_TYPE_ROOT_VIEW,
+ WIDGET_TYPE_FLOATER_VIEW,
+ WIDGET_TYPE_BUTTON,
+ WIDGET_TYPE_JOYSTICK_TURN,
+ WIDGET_TYPE_JOYSTICK_SLIDE,
+ WIDGET_TYPE_CHECKBOX,
+ WIDGET_TYPE_COLOR_SWATCH,
+ WIDGET_TYPE_COMBO_BOX,
+ WIDGET_TYPE_LINE_EDITOR,
+ WIDGET_TYPE_SEARCH_EDITOR,
+ WIDGET_TYPE_SCROLL_LIST,
+ WIDGET_TYPE_NAME_LIST,
+ WIDGET_TYPE_WEBBROWSER,
+ WIDGET_TYPE_SLIDER, // actually LLSliderCtrl
+ WIDGET_TYPE_SLIDER_BAR, // actually LLSlider
+ WIDGET_TYPE_VOLUME_SLIDER,//actually LLVolumeSliderCtrl
+ WIDGET_TYPE_SPINNER,
+ WIDGET_TYPE_TEXT_EDITOR,
+ WIDGET_TYPE_TEXTURE_PICKER,
+ WIDGET_TYPE_TEXT_BOX,
+ WIDGET_TYPE_PAD, // used in XML for positioning, not a real widget
+ WIDGET_TYPE_RADIO_GROUP,
+ WIDGET_TYPE_ICON,
+ WIDGET_TYPE_LOCATE, // used in XML for positioning, not a real widget
+ WIDGET_TYPE_VIEW_BORDER, // decorative border
+ WIDGET_TYPE_PANEL,
+ WIDGET_TYPE_MENU,
+ WIDGET_TYPE_PIE_MENU,
+ WIDGET_TYPE_PIE_MENU_BRANCH,
+ WIDGET_TYPE_MENU_ITEM,
+ WIDGET_TYPE_MENU_ITEM_SEPARATOR,
+ WIDGET_TYPE_MENU_SEPARATOR_VERTICAL,
+ WIDGET_TYPE_MENU_ITEM_CALL,
+ WIDGET_TYPE_MENU_ITEM_CHECK,
+ WIDGET_TYPE_MENU_ITEM_BRANCH,
+ WIDGET_TYPE_MENU_ITEM_BRANCH_DOWN,
+ WIDGET_TYPE_MENU_ITEM_BLANK,
+ WIDGET_TYPE_TEAROFF_MENU,
+ WIDGET_TYPE_MENU_BAR,
+ WIDGET_TYPE_TAB_CONTAINER,
+ WIDGET_TYPE_SCROLL_CONTAINER, // LLScrollableContainerView
+ WIDGET_TYPE_SCROLLBAR,
+ WIDGET_TYPE_INVENTORY_PANEL, // LLInventoryPanel
+ WIDGET_TYPE_FLOATER,
+ WIDGET_TYPE_DRAG_HANDLE_TOP,
+ WIDGET_TYPE_DRAG_HANDLE_LEFT,
+ WIDGET_TYPE_RESIZE_HANDLE,
+ WIDGET_TYPE_RESIZE_BAR,
+ WIDGET_TYPE_NAME_EDITOR,
+ WIDGET_TYPE_MULTI_FLOATER,
+ WIDGET_TYPE_MEDIA_REMOTE,
+ WIDGET_TYPE_FOLDER_VIEW,
+ WIDGET_TYPE_FOLDER_ITEM,
+ WIDGET_TYPE_FOLDER,
+ WIDGET_TYPE_STAT_GRAPH,
+ WIDGET_TYPE_STAT_VIEW,
+ WIDGET_TYPE_STAT_BAR,
+ WIDGET_TYPE_DROP_TARGET,
+ WIDGET_TYPE_TEXTURE_BAR,
+ WIDGET_TYPE_TEX_MEM_BAR,
+ WIDGET_TYPE_SNAPSHOT_LIVE_PREVIEW,
+ WIDGET_TYPE_STATUS_BAR,
+ WIDGET_TYPE_PROGRESS_VIEW,
+ WIDGET_TYPE_TALK_VIEW,
+ WIDGET_TYPE_OVERLAY_BAR,
+ WIDGET_TYPE_HUD_VIEW,
+ WIDGET_TYPE_HOVER_VIEW,
+ WIDGET_TYPE_MORPH_VIEW,
+ WIDGET_TYPE_NET_MAP,
+ WIDGET_TYPE_PERMISSIONS_VIEW,
+ WIDGET_TYPE_MENU_HOLDER,
+ WIDGET_TYPE_DEBUG_VIEW,
+ WIDGET_TYPE_SCROLLING_PANEL_LIST,
+ WIDGET_TYPE_AUDIO_STATUS,
+ WIDGET_TYPE_CONTAINER_VIEW,
+ WIDGET_TYPE_CONSOLE,
+ WIDGET_TYPE_FAST_TIMER_VIEW,
+ WIDGET_TYPE_VELOCITY_BAR,
+ WIDGET_TYPE_TEXTURE_VIEW,
+ WIDGET_TYPE_MEMORY_VIEW,
+ WIDGET_TYPE_FRAME_STAT_VIEW,
+ WIDGET_TYPE_DONTCARE,
+ WIDGET_TYPE_COUNT
+} EWidgetType;
+
+#endif
diff --git a/indra/llui/lluiconstants.h b/indra/llui/lluiconstants.h
new file mode 100644
index 0000000000..01663ed233
--- /dev/null
+++ b/indra/llui/lluiconstants.h
@@ -0,0 +1,32 @@
+/**
+ * @file lluiconstants.h
+ * @brief Compile-time configuration for UI
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUICONSTANTS_H
+#define LL_LLUICONSTANTS_H
+
+// spacing for small font lines of text, like LLTextBoxes
+const S32 LINE = 16;
+
+// spacing for larger lines of text
+const S32 LINE_BIG = 24;
+
+// default vertical padding
+const S32 VPAD = 4;
+
+// default horizontal padding
+const S32 HPAD = 4;
+
+// Account History, how far to look into past
+const S32 SUMMARY_INTERVAL = 7; // one week
+const S32 SUMMARY_MAX = 8; //
+const S32 DETAILS_INTERVAL = 1; // one day
+const S32 DETAILS_MAX = 30; // one month
+const S32 TRANSACTIONS_INTERVAL = 1;// one day
+const S32 TRANSACTIONS_MAX = 30; // one month
+
+#endif
diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp
new file mode 100644
index 0000000000..0d9791c660
--- /dev/null
+++ b/indra/llui/lluictrl.cpp
@@ -0,0 +1,341 @@
+/**
+ * @file lluictrl.cpp
+ * @author James Cook, Richard Nelson, Tom Yedwab
+ * @brief Abstract base class for UI controls
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//#include "llviewerprecompiledheaders.h"
+#include "linden_common.h"
+
+#include "lluictrl.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llfocusmgr.h"
+#include "v3color.h"
+
+#include "llstring.h"
+#include "llfontgl.h"
+#include "llkeyboard.h"
+
+const U32 MAX_STRING_LENGTH = 10;
+
+LLUICtrl::LLUICtrl() :
+ mCommitCallback(NULL),
+ mFocusReceivedCallback(NULL),
+ mFocusChangedCallback(NULL),
+ mValidateCallback(NULL),
+ mCallbackUserData(NULL),
+ mTentative(FALSE),
+ mTabStop(TRUE),
+ mIsChrome(FALSE)
+{
+}
+
+LLUICtrl::LLUICtrl(const LLString& name, const LLRect& rect, BOOL mouse_opaque,
+ void (*on_commit_callback)(LLUICtrl*, void*),
+ void* callback_userdata,
+ U32 reshape)
+: // can't make this automatically follow top and left, breaks lots
+ // of buttons in the UI. JC 7/20/2002
+ LLView( name, rect, mouse_opaque, reshape ),
+ mCommitCallback( on_commit_callback) ,
+ mFocusReceivedCallback( NULL ),
+ mFocusChangedCallback( NULL ),
+ mValidateCallback( NULL ),
+ mCallbackUserData( callback_userdata ),
+ mTentative( FALSE ),
+ mTabStop( TRUE ),
+ mIsChrome(FALSE)
+{
+}
+
+LLUICtrl::~LLUICtrl()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+}
+
+void LLUICtrl::onCommit()
+{
+ if( mCommitCallback )
+ {
+ mCommitCallback( this, mCallbackUserData );
+ }
+}
+
+// virtual
+BOOL LLUICtrl::setTextArg( const LLString& key, const LLString& text )
+{
+ return FALSE;
+}
+
+// virtual
+BOOL LLUICtrl::setLabelArg( const LLString& key, const LLString& text )
+{
+ return FALSE;
+}
+
+// virtual
+LLCtrlSelectionInterface* LLUICtrl::getSelectionInterface()
+{
+ return NULL;
+}
+
+// virtual
+LLCtrlListInterface* LLUICtrl::getListInterface()
+{
+ return NULL;
+}
+
+// virtual
+LLCtrlScrollInterface* LLUICtrl::getScrollInterface()
+{
+ return NULL;
+}
+
+// virtual
+void LLUICtrl::setTabStop( BOOL b )
+{
+ mTabStop = b;
+}
+
+// virtual
+BOOL LLUICtrl::hasTabStop() const
+{
+ return mTabStop;
+}
+
+// virtual
+BOOL LLUICtrl::acceptsTextInput() const
+{
+ return FALSE;
+}
+
+// virtual
+void LLUICtrl::onTabInto()
+{
+}
+
+// virtual
+void LLUICtrl::clear()
+{
+}
+
+// virtual
+void LLUICtrl::setIsChrome(BOOL is_chrome)
+{
+ mIsChrome = is_chrome;
+}
+
+// virtual
+BOOL LLUICtrl::getIsChrome() const
+{
+ return mIsChrome;
+}
+
+void LLUICtrl::onFocusReceived()
+{
+ if( mFocusReceivedCallback )
+ {
+ mFocusReceivedCallback( this, mCallbackUserData );
+ }
+ if( mFocusChangedCallback )
+ {
+ mFocusChangedCallback( this, mCallbackUserData );
+ }
+}
+
+void LLUICtrl::onFocusLost()
+{
+ if( mFocusChangedCallback )
+ {
+ mFocusChangedCallback( this, mCallbackUserData );
+ }
+}
+
+BOOL LLUICtrl::hasFocus() const
+{
+ return (gFocusMgr.childHasKeyboardFocus(this));
+}
+
+void LLUICtrl::setFocus(BOOL b)
+{
+ // focus NEVER goes to ui ctrls that are disabled!
+ if (!mEnabled)
+ {
+ return;
+ }
+ if( b )
+ {
+ if (!hasFocus())
+ {
+ gFocusMgr.setKeyboardFocus( this, &LLUICtrl::onFocusLostCallback );
+ onFocusReceived();
+ }
+ }
+ else
+ {
+ if( gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus( NULL, NULL );
+ onFocusLost();
+ }
+ }
+}
+
+// static
+void LLUICtrl::onFocusLostCallback( LLUICtrl* old_focus )
+{
+ old_focus->onFocusLost();
+}
+
+// this comparator uses the crazy disambiguating logic of LLCompareByTabOrder,
+// but to switch up the order so that children that have the default tab group come first
+// and those that are prior to the default tab group come last
+class CompareByDefaultTabGroup: public LLCompareByTabOrder
+{
+public:
+ CompareByDefaultTabGroup(LLView::child_tab_order_t order, S32 default_tab_group):
+ LLCompareByTabOrder(order),
+ mDefaultTabGroup(default_tab_group) {}
+protected:
+ /*virtual*/ bool compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const
+ {
+ S32 ag = a.first; // tab group for a
+ S32 bg = b.first; // tab group for b
+ // these two ifs have the effect of moving elements prior to the default tab group to the end of the list
+ // (still sorted relative to each other, though)
+ if(ag < mDefaultTabGroup && bg >= mDefaultTabGroup) return false;
+ if(bg < mDefaultTabGroup && ag >= mDefaultTabGroup) return true;
+ return a < b; // sort correctly if they're both on the same side of the default tab group
+ }
+ S32 mDefaultTabGroup;
+};
+
+// sorter for plugging into the query
+class DefaultTabGroupFirstSorter : public LLQuerySorter, public LLSingleton<DefaultTabGroupFirstSorter>
+{
+public:
+ /*virtual*/ void operator() (LLView * parent, viewList_t &children) const
+ {
+ children.sort(CompareByDefaultTabGroup(parent->getCtrlOrder(), parent->getDefaultTabGroup()));
+ }
+};
+
+BOOL LLUICtrl::focusFirstItem(BOOL prefer_text_fields)
+{
+ // try to select default tab group child
+ LLCtrlQuery query = LLView::getTabOrderQuery();
+ // sort things such that the default tab group is at the front
+ query.setSorter(DefaultTabGroupFirstSorter::getInstance());
+ LLView::child_list_t result = query(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ // fall back on default behavior if we didn't find anything
+ return LLView::focusFirstItem(prefer_text_fields);
+}
+
+/*
+// Don't let the children handle the tool tip. Handle it here instead.
+BOOL LLUICtrl::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ BOOL handled = FALSE;
+ if (getVisible() && pointInView( x, y ) )
+ {
+ if( !mToolTipMsg.empty() )
+ {
+ msg = mToolTipMsg;
+
+ // Convert rect local to screen coordinates
+ localPointToScreen(
+ 0, 0,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ localPointToScreen(
+ mRect.getWidth(), mRect.getHeight(),
+ &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+
+ handled = TRUE;
+ }
+ }
+
+ if (!handled)
+ {
+ return LLView::handleToolTip(x, y, msg, sticky_rect_screen);
+ }
+
+ return handled;
+}*/
+
+void LLUICtrl::initFromXML(LLXMLNodePtr node, LLView* parent)
+{
+ BOOL has_tab_stop = hasTabStop();
+ node->getAttributeBOOL("tab_stop", has_tab_stop);
+
+ setTabStop(has_tab_stop);
+
+ LLView::initFromXML(node, parent);
+}
+
+LLXMLNodePtr LLUICtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLView::getXML(save_children);
+ node->createChild("tab_stop", TRUE)->setBoolValue(hasTabStop());
+
+ return node;
+}
+
+// *NOTE: If other classes derive from LLPanel, they will need to be
+// added to this function.
+LLPanel* LLUICtrl::getParentPanel() const
+{
+ LLView* parent = getParent();
+ while (parent
+ && parent->getWidgetType() != WIDGET_TYPE_PANEL
+ && parent->getWidgetType() != WIDGET_TYPE_FLOATER)
+ {
+ parent = parent->getParent();
+ }
+ return reinterpret_cast<LLPanel*>(parent);
+}
+
+// virtual
+void LLUICtrl::setTentative(BOOL b)
+{
+ mTentative = b;
+}
+
+// virtual
+BOOL LLUICtrl::getTentative() const
+{
+ return mTentative;
+}
+
+// virtual
+void LLUICtrl::setDoubleClickCallback( void (*cb)(void*) )
+{
+}
+
+// virtual
+void LLUICtrl::setColor(const LLColor4& color)
+{ }
+
+// virtual
+void LLUICtrl::setMinValue(LLSD min_value)
+{ }
+
+// virtual
+void LLUICtrl::setMaxValue(LLSD max_value)
+{ }
diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h
new file mode 100644
index 0000000000..f58b7d6e16
--- /dev/null
+++ b/indra/llui/lluictrl.h
@@ -0,0 +1,153 @@
+/**
+ * @file lluictrl.h
+ * @author James Cook, Richard Nelson, Tom Yedwab
+ * @brief Abstract base class for UI controls
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUICTRL_H
+#define LL_LLUICTRL_H
+
+#include "llview.h"
+#include "llrect.h"
+#include "llsd.h"
+
+//
+// Classes
+//
+class LLViewerImage;
+class LLFontGL;
+class LLButton;
+class LLTextBox;
+class LLLineEditor;
+class LLUICtrl;
+class LLPanel;
+class LLCtrlSelectionInterface;
+class LLCtrlListInterface;
+class LLCtrlScrollInterface;
+
+typedef void (*LLUICtrlCallback)(LLUICtrl* ctrl, void* userdata);
+typedef BOOL (*LLUICtrlValidate)(LLUICtrl* ctrl, void* userdata);
+
+class LLUICtrl
+: public LLView
+{
+public:
+ LLUICtrl();
+ LLUICtrl( const LLString& name, const LLRect& rect, BOOL mouse_opaque,
+ LLUICtrlCallback callback,
+ void* callback_userdata,
+ U32 reshape=FOLLOWS_NONE);
+ virtual ~LLUICtrl();
+
+ // LLView interface
+ //virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual void initFromXML(LLXMLNodePtr node, LLView* parent);
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ virtual LLSD getValue() const { return LLSD(); }
+
+ // Defaults to no-op
+ virtual BOOL setTextArg( const LLString& key, const LLString& text );
+
+ // Defaults to no-op
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ // Defaults to return NULL
+ virtual LLCtrlSelectionInterface* getSelectionInterface();
+ virtual LLCtrlListInterface* getListInterface();
+ virtual LLCtrlScrollInterface* getScrollInterface();
+
+ virtual void setFocus( BOOL b );
+ virtual BOOL hasFocus() const;
+
+ virtual void setTabStop( BOOL b );
+ virtual BOOL hasTabStop() const;
+
+ // Defaults to false
+ virtual BOOL acceptsTextInput() const;
+
+ // Default to no-op
+ virtual void onTabInto();
+ virtual void clear();
+
+ virtual void setIsChrome(BOOL is_chrome);
+ virtual BOOL getIsChrome() const;
+
+ virtual void onCommit();
+
+ virtual BOOL isCtrl() const { return TRUE; }
+ // "Tentative" controls have a proposed value, but haven't committed
+ // it yet. This is used when multiple objects are selected and we
+ // want to display a parameter that differs between the objects.
+ virtual void setTentative(BOOL b);
+ virtual BOOL getTentative() const;
+
+ // Returns containing panel/floater or NULL if none found.
+ LLPanel* getParentPanel() const;
+
+ void* getCallbackUserData() const { return mCallbackUserData; }
+ void setCallbackUserData( void* data ) { mCallbackUserData = data; }
+
+ void setCommitCallback( void (*cb)(LLUICtrl*, void*) ) { mCommitCallback = cb; }
+ void setValidateBeforeCommit( BOOL(*cb)(LLUICtrl*, void*) ) { mValidateCallback = cb; }
+
+ // Defaults to no-op!
+ virtual void setDoubleClickCallback( void (*cb)(void*) );
+
+ // Defaults to no-op
+ virtual void setColor(const LLColor4& color);
+
+ // Defaults to no-op
+ virtual void setMinValue(LLSD min_value);
+ virtual void setMaxValue(LLSD max_value);
+
+ // In general, only LLPanel uses these.
+ void setFocusReceivedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusReceivedCallback = cb; }
+ void setFocusChangedCallback( void (*cb)(LLUICtrl*, void*) ) { mFocusChangedCallback = cb; }
+
+ static void onFocusLostCallback(LLUICtrl* old_focus);
+
+ /*virtual*/ BOOL focusFirstItem(BOOL prefer_text_fields = FALSE );
+
+ class LLTabStopPostFilter : public LLQueryFilter, public LLSingleton<LLTabStopPostFilter>
+ {
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
+ {
+ return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl * const>(view)->hasTabStop() && children.size() == 0, TRUE);
+ }
+ };
+
+ class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
+ {
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
+ {
+ return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl * const>(view)->acceptsTextInput(), TRUE);
+ }
+ };
+
+protected:
+ virtual void onFocusReceived();
+ virtual void onFocusLost();
+ void onChangeFocus( S32 direction );
+
+protected:
+
+ void (*mCommitCallback)( LLUICtrl* ctrl, void* userdata );
+ void (*mFocusReceivedCallback)( LLUICtrl* ctrl, void* userdata );
+ void (*mFocusChangedCallback)( LLUICtrl* ctrl, void* userdata );
+ BOOL (*mValidateCallback)( LLUICtrl* ctrl, void* userdata );
+
+ void* mCallbackUserData;
+ BOOL mTentative;
+ BOOL mTabStop;
+
+private:
+ BOOL mIsChrome;
+
+
+};
+
+#endif // LL_LLUICTRL_H
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
new file mode 100644
index 0000000000..7e286f0bee
--- /dev/null
+++ b/indra/llui/lluictrlfactory.cpp
@@ -0,0 +1,722 @@
+/**
+ * @file lluictrlfactory.cpp
+ * @brief Factory class for creating UI controls
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lluictrlfactory.h"
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+
+// other library includes
+#include "llcontrol.h"
+#include "lldir.h"
+#include "v4color.h"
+
+// this library includes
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+//#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "llcontrol.h"
+#include "lldir.h"
+#include "llevent.h"
+#include "llfloater.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llradiogroup.h"
+#include "llscrollcontainer.h"
+#include "llscrollingpanellist.h"
+#include "llscrolllistctrl.h"
+#include "llslider.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltabcontainervertical.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "llui.h"
+#include "llviewborder.h"
+
+
+const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n";
+
+// *NOTE: If you add a new class derived from LLPanel, add a check for its
+// widget type to LLUICtrl::getParentPanel().
+// *NOTE: This MUST match EWidgetType in llui.h
+//static
+const LLString LLUICtrlFactory::sUICtrlNames[WIDGET_TYPE_COUNT] =
+{
+ LLString("view"), //WIDGET_TYPE_VIEW
+ LLString("root_view"), //WIDGET_TYPE_ROOT_VIEW
+ LLString("floater_view"), //WIDGET_TYPE_FLOATER_VIEW
+ LLString("button"), //WIDGET_TYPE_BUTTON
+ LLString("joystick_turn"), //WIDGET_TYPE_JOYSTICK_TURN
+ LLString("joystick_slide"), //WIDGET_TYPE_JOYSTICK_SLIDE
+ LLString("check_box"), //WIDGET_TYPE_CHECKBOX
+ LLString("color_swatch"), //WIDGET_TYPE_COLOR_SWATCH
+ LLString("combo_box"), //WIDGET_TYPE_COMBO_BOX
+ LLString("line_editor"), //WIDGET_TYPE_LINE_EDITOR
+ LLString("search_editor"), //WIDGET_TYPE_SEARCH_EDITOR
+ LLString("scroll_list"), //WIDGET_TYPE_SCROLL_LIST
+ LLString("name_list"), //WIDGET_TYPE_NAME_LIST
+ LLString("web_browser"), //WIDGET_TYPE_WEBBROWSER
+ LLString("slider"), //WIDGET_TYPE_SLIDER, actually LLSliderCtrl
+ LLString("slider_bar"), //WIDGET_TYPE_SLIDER_BAR, actually LLSlider
+ LLString("volume_slider"), //WIDGET_TYPE_VOLUME_SLIDER, actually LLVolumeSliderCtrl
+ LLString("spinner"), //WIDGET_TYPE_SPINNER, actually LLSpinCtrl
+ LLString("text_editor"), //WIDGET_TYPE_TEXT_EDITOR
+ LLString("texture_picker"),//WIDGET_TYPE_TEXTURE_PICKER
+ LLString("text"), //WIDGET_TYPE_TEXT_BOX
+ LLString("pad"), //WIDGET_TYPE_PAD
+ LLString("radio_group"), //WIDGET_TYPE_RADIO_GROUP
+ LLString("icon"), //WIDGET_TYPE_ICON
+ LLString("locate"), //WIDGET_TYPE_LOCATE
+ LLString("view_border"), //WIDGET_TYPE_VIEW_BORDER
+ LLString("panel"), //WIDGET_TYPE_PANEL
+ LLString("menu"), //WIDGET_TYPE_MENU
+ LLString("pie_menu"), //WIDGET_TYPE_PIE_MENU
+ LLString("pie_menu_branch"), //WIDGET_TYPE_PIE_MENU_BRANCH
+ LLString("menu_item"), //WIDGET_TYPE_MENU_ITEM
+ LLString("menu_item_separator"), //WIDGET_TYPE_MENU_ITEM_SEPARATOR
+ LLString("menu_separator_vertical"), // WIDGET_TYPE_MENU_SEPARATOR_VERTICAL
+ LLString("menu_item_call"), // WIDGET_TYPE_MENU_ITEM_CALL
+ LLString("menu_item_check"),// WIDGET_TYPE_MENU_ITEM_CHECK
+ LLString("menu_item_branch"), // WIDGET_TYPE_MENU_ITEM_BRANCH
+ LLString("menu_item_branch_down"), //WIDGET_TYPE_MENU_ITEM_BRANCH_DOWN,
+ LLString("menu_item_blank"), //WIDGET_TYPE_MENU_ITEM_BLANK,
+ LLString("tearoff_menu"), //WIDGET_TYPE_TEAROFF_MENU
+ LLString("menu_bar"), //WIDGET_TYPE_MENU_BAR
+ LLString("tab_container"),//WIDGET_TYPE_TAB_CONTAINER
+ LLString("scroll_container"),//WIDGET_TYPE_SCROLL_CONTAINER
+ LLString("scrollbar"), //WIDGET_TYPE_SCROLLBAR
+ LLString("inventory_panel"), //WIDGET_TYPE_INVENTORY_PANEL
+ LLString("floater"), //WIDGET_TYPE_FLOATER
+ LLString("drag_handle_top"), //WIDGET_TYPE_DRAG_HANDLE_TOP
+ LLString("drag_handle_left"), //WIDGET_TYPE_DRAG_HANDLE_LEFT
+ LLString("resize_handle"), //WIDGET_TYPE_RESIZE_HANDLE
+ LLString("resize_bar"), //WIDGET_TYPE_RESIZE_BAR
+ LLString("name_editor"), //WIDGET_TYPE_NAME_EDITOR
+ LLString("multi_floater"), //WIDGET_TYPE_MULTI_FLOATER
+ LLString("media_remote"), //WIDGET_TYPE_MEDIA_REMOTE
+ LLString("folder_view"), //WIDGET_TYPE_FOLDER_VIEW
+ LLString("folder_item"), //WIDGET_TYPE_FOLDER_ITEM
+ LLString("folder"), //WIDGET_TYPE_FOLDER
+ LLString("stat_graph"), //WIDGET_TYPE_STAT_GRAPH
+ LLString("stat_view"), //WIDGET_TYPE_STAT_VIEW
+ LLString("stat_bar"), //WIDGET_TYPE_STAT_BAR
+ LLString("drop_target"), //WIDGET_TYPE_DROP_TARGET
+ LLString("texture_bar"), //WIDGET_TYPE_TEXTURE_BAR
+ LLString("tex_mem_bar"), //WIDGET_TYPE_TEX_MEM_BAR
+ LLString("snapshot_live_preview"), //WIDGET_TYPE_SNAPSHOT_LIVE_PREVIEW
+ LLString("status_bar"), //WIDGET_TYPE_STATUS_BAR
+ LLString("progress_view"), //WIDGET_TYPE_PROGRESS_VIEW
+ LLString("talk_view"), //WIDGET_TYPE_TALK_VIEW
+ LLString("overlay_bar"), //WIDGET_TYPE_OVERLAY_BAR
+ LLString("hud_view"), //WIDGET_TYPE_HUD_VIEW
+ LLString("hover_view"), //WIDGET_TYPE_HOVER_VIEW
+ LLString("morph_view"), //WIDGET_TYPE_MORPH_VIEW
+ LLString("net_map"), //WIDGET_TYPE_NET_MAP
+ LLString("permissions_view"), //WIDGET_TYPE_PERMISSIONS_VIEW
+ LLString("menu_holder"), //WIDGET_TYPE_MENU_HOLDER
+ LLString("debug_view"), //WIDGET_TYPE_DEBUG_VIEW
+ LLString("scrolling_panel_list"), //WIDGET_TYPE_SCROLLING_PANEL_LIST
+ LLString("audio_status"), //WIDGET_TYPE_AUDIO_STATUS
+ LLString("container_view"), //WIDGET_TYPE_CONTAINER_VIEW
+ LLString("console"), //WIDGET_TYPE_CONSOLE
+ LLString("fast_timer_view"), //WIDGET_TYPE_FAST_TIMER_VIEW
+ LLString("velocity_bar"), //WIDGET_TYPE_VELOCITY_BAR
+ LLString("texture_view"), //WIDGET_TYPE_TEXTURE_VIEW
+ LLString("memory_view"), //WIDGET_TYPE_MEMORY_VIEW
+ LLString("frame_stat_view"), //WIDGET_TYPE_FRAME_STAT_VIEW
+ LLString("DONT_CARE"), //WIDGET_TYPE_DONTCARE
+};
+
+const S32 HPAD = 4;
+const S32 VPAD = 4;
+const S32 FLOATER_H_MARGIN = 15;
+const S32 MIN_WIDGET_HEIGHT = 10;
+
+std::vector<LLString> LLUICtrlFactory::mXUIPaths;
+
+// UI Ctrl class for padding
+class LLUICtrlLocate : public LLUICtrl
+{
+public:
+ LLUICtrlLocate() : LLUICtrl("locate", LLRect(0,0,0,0), FALSE, NULL, NULL) {}
+ virtual void draw() { }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_LOCATE; }
+ virtual LLString getWidgetTag() const { return LL_UI_CTRL_LOCATE_TAG; }
+
+ static LLView *fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+ {
+ LLUICtrlLocate *new_ctrl = new LLUICtrlLocate();
+ new_ctrl->initFromXML(node, parent);
+ return new_ctrl;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// LLUICtrlFactory()
+//-----------------------------------------------------------------------------
+LLUICtrlFactory::LLUICtrlFactory()
+{
+ // Register controls
+ LLUICtrlCreator<LLButton>::registerCreator(LL_BUTTON_TAG, this);
+ LLUICtrlCreator<LLCheckBoxCtrl>::registerCreator(LL_CHECK_BOX_CTRL_TAG, this);
+ LLUICtrlCreator<LLComboBox>::registerCreator(LL_COMBO_BOX_TAG, this);
+ LLUICtrlCreator<LLLineEditor>::registerCreator(LL_LINE_EDITOR_TAG, this);
+ LLUICtrlCreator<LLSearchEditor>::registerCreator(LL_SEARCH_EDITOR_TAG, this);
+ LLUICtrlCreator<LLScrollListCtrl>::registerCreator(LL_SCROLL_LIST_CTRL_TAG, this);
+ LLUICtrlCreator<LLSliderCtrl>::registerCreator(LL_SLIDER_CTRL_TAG, this);
+ LLUICtrlCreator<LLSlider>::registerCreator(LL_SLIDER_TAG, this);
+ LLUICtrlCreator<LLSpinCtrl>::registerCreator(LL_SPIN_CTRL_TAG, this);
+ LLUICtrlCreator<LLTextBox>::registerCreator(LL_TEXT_BOX_TAG, this);
+ LLUICtrlCreator<LLRadioGroup>::registerCreator(LL_RADIO_GROUP_TAG, this);
+ LLUICtrlCreator<LLIconCtrl>::registerCreator(LL_ICON_CTRL_TAG, this);
+ LLUICtrlCreator<LLUICtrlLocate>::registerCreator(LL_UI_CTRL_LOCATE_TAG, this);
+ LLUICtrlCreator<LLUICtrlLocate>::registerCreator(LL_PAD_TAG, this);
+ LLUICtrlCreator<LLViewBorder>::registerCreator(LL_VIEW_BORDER_TAG, this);
+ LLUICtrlCreator<LLTabContainerCommon>::registerCreator(LL_TAB_CONTAINER_COMMON_TAG, this);
+ LLUICtrlCreator<LLScrollableContainerView>::registerCreator(LL_SCROLLABLE_CONTAINER_VIEW_TAG, this);
+ LLUICtrlCreator<LLPanel>::registerCreator(LL_PANEL_TAG, this);
+ LLUICtrlCreator<LLMenuGL>::registerCreator(LL_MENU_GL_TAG, this);
+ LLUICtrlCreator<LLMenuBarGL>::registerCreator(LL_MENU_BAR_GL_TAG, this);
+ LLUICtrlCreator<LLScrollingPanelList>::registerCreator(LL_SCROLLING_PANEL_LIST_TAG, this);
+
+
+ LLString filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml");
+
+ LLXMLNodePtr root;
+ BOOL success = LLXMLNode::parseFile(filename, root, NULL);
+
+ if (!success)
+ {
+ LLString slash = gDirUtilp->getDirDelimiter();
+ LLString dir = gDirUtilp->getAppRODataDir() + slash + "skins" + slash + "xui" + slash + "en-us" + slash;
+ llwarns << "XUI::config file unable to open." << llendl;
+ mXUIPaths.push_back(dir);
+ }
+ else
+ {
+ LLXMLNodePtr path;
+ LLString app_dir = gDirUtilp->getAppRODataDir();
+
+ for (path = root->getFirstChild(); path.notNull(); path = path->getNextSibling())
+ {
+ LLUIString path_val_ui(path->getValue());
+ LLString language = "en-us";
+ if (LLUI::sConfigGroup)
+ {
+ language = LLUI::sConfigGroup->getString("Language");
+ }
+ path_val_ui.setArg("[Language]", language);
+ LLString fullpath = app_dir + path_val_ui.getString();
+
+ if (mXUIPaths.empty() || (find(mXUIPaths.begin(), mXUIPaths.end(), fullpath) == mXUIPaths.end()) )
+ {
+ mXUIPaths.push_back(app_dir + path_val_ui.getString());
+ }
+ }
+ }
+
+
+}
+
+//-----------------------------------------------------------------------------
+// ~LLUICtrlFactory()
+//-----------------------------------------------------------------------------
+LLUICtrlFactory::~LLUICtrlFactory()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// getLayeredXMLNode()
+//-----------------------------------------------------------------------------
+bool LLUICtrlFactory::getLayeredXMLNode(const LLString &filename, LLXMLNodePtr& root)
+{
+
+ if (!LLXMLNode::parseFile(mXUIPaths.front() + filename, root, NULL))
+ {
+ llwarns << "Problem reading UI description file: " << mXUIPaths.front() + filename << llendl;
+ return FALSE;
+ }
+
+ LLXMLNodePtr updateRoot;
+
+ std::vector<LLString>::const_iterator itor;
+
+ for (itor = mXUIPaths.begin(), ++itor; itor != mXUIPaths.end(); ++itor)
+ {
+ LLString nodeName;
+ LLString updateName;
+
+ LLXMLNode::parseFile((*itor) + filename, updateRoot, NULL);
+
+ updateRoot->getAttributeString("name", updateName);
+ root->getAttributeString("name", nodeName);
+
+ if (updateName == nodeName)
+ {
+ LLXMLNode::updateNode(root, updateRoot);
+ }
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// buildFloater()
+//-----------------------------------------------------------------------------
+void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const LLString &filename,
+ const LLCallbackMap::map_t* factory_map, BOOL open)
+{
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ return;
+ }
+
+ // root must be called floater
+ if( !(root->hasName("floater") || root->hasName("multi_floater") ) )
+ {
+ llwarns << "Root node should be named floater in: " << filename << llendl;
+ return;
+ }
+
+ if (factory_map)
+ {
+ mFactoryStack.push_front(factory_map);
+ }
+
+ floaterp->initFloaterXML(root, NULL, this, open);
+
+ if (LLUI::sShowXUINames)
+ {
+ floaterp->mToolTipMsg = filename;
+ }
+
+ if (factory_map)
+ {
+ mFactoryStack.pop_front();
+ }
+
+ LLViewHandle handle = floaterp->getHandle();
+ mBuiltFloaters[handle] = filename;
+}
+
+//-----------------------------------------------------------------------------
+// saveToXML()
+//-----------------------------------------------------------------------------
+S32 LLUICtrlFactory::saveToXML(LLView* viewp, const LLString& filename)
+{
+ llofstream out(filename.c_str());
+ if (!out.good())
+ {
+ llwarns << "Unable to open " << filename << " for output." << llendl;
+ return 1;
+ }
+
+ out << XML_HEADER;
+
+ LLXMLNodePtr xml_node = viewp->getXML();
+
+ xml_node->writeToOstream(out);
+
+ out.close();
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// buildPanel()
+//-----------------------------------------------------------------------------
+void LLUICtrlFactory::buildPanel(LLPanel* panelp, const LLString &filename,
+ const LLCallbackMap::map_t* factory_map)
+{
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ return;
+ }
+
+ // root must be called panel
+ if( !root->hasName("panel" ) )
+ {
+ llwarns << "Root node should be named panel in : " << filename << llendl;
+ return;
+ }
+
+ if (factory_map)
+ {
+ mFactoryStack.push_front(factory_map);
+ }
+
+ panelp->initPanelXML(root, NULL, this);
+
+ if (LLUI::sShowXUINames)
+ {
+ panelp->mToolTipMsg = filename;
+ }
+
+ LLViewHandle handle = panelp->getHandle();
+ mBuiltPanels[handle] = filename;
+
+ if (factory_map)
+ {
+ mFactoryStack.pop_front();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// buildMenu()
+//-----------------------------------------------------------------------------
+LLMenuGL *LLUICtrlFactory::buildMenu(const LLString &filename, LLView* parentp)
+{
+ // TomY TODO: Break this function into buildMenu and buildMenuBar
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ return NULL;
+ }
+
+ // root must be called panel
+ if( !root->hasName( "menu_bar" ) && !root->hasName( "menu" ))
+ {
+ llwarns << "Root node should be named menu bar or menu in : " << filename << llendl;
+ return NULL;
+ }
+
+ if (root->hasName("menu"))
+ {
+ return (LLMenuGL*)LLMenuGL::fromXML(root, parentp, this);
+ }
+
+ return (LLMenuGL*)LLMenuBarGL::fromXML(root, parentp, this);
+}
+
+//-----------------------------------------------------------------------------
+// buildMenu()
+//-----------------------------------------------------------------------------
+LLPieMenu *LLUICtrlFactory::buildPieMenu(const LLString &filename, LLView* parentp)
+{
+
+ LLXMLNodePtr root;
+
+ if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))
+ {
+ return NULL;
+ }
+
+ // root must be called panel
+ if( !root->hasName( LL_PIE_MENU_TAG ))
+ {
+ llwarns << "Root node should be named " LL_PIE_MENU_TAG " in : " << filename << llendl;
+ return NULL;
+ }
+
+ LLString name("menu");
+ root->getAttributeString("name", name);
+
+ LLPieMenu *menu = new LLPieMenu(name);
+ parentp->addChild(menu);
+ menu->initXML(root, parentp, this);
+ return menu;
+}
+
+//-----------------------------------------------------------------------------
+// removePanel()
+//-----------------------------------------------------------------------------
+void LLUICtrlFactory::removePanel(LLPanel* panelp)
+{
+ mBuiltPanels.erase(panelp->getHandle());
+}
+
+//-----------------------------------------------------------------------------
+// removeFloater()
+//-----------------------------------------------------------------------------
+void LLUICtrlFactory::removeFloater(LLFloater* floaterp)
+{
+ mBuiltFloaters.erase(floaterp->getHandle());
+}
+
+//-----------------------------------------------------------------------------
+// rebuild()
+//-----------------------------------------------------------------------------
+void LLUICtrlFactory::rebuild()
+{
+ built_panel_t::iterator built_panel_it;
+ for (built_panel_it = mBuiltPanels.begin();
+ built_panel_it != mBuiltPanels.end();
+ ++built_panel_it)
+ {
+ LLString filename = built_panel_it->second;
+ LLPanel* panelp = LLPanel::getPanelByHandle(built_panel_it->first);
+ if (!panelp)
+ {
+ continue;
+ }
+ llinfos << "Rebuilding UI panel " << panelp->getName()
+ << " from " << filename
+ << llendl;
+ BOOL visible = panelp->getVisible();
+ panelp->setVisible(FALSE);
+ panelp->setFocus(FALSE);
+ panelp->deleteAllChildren();
+
+ buildPanel(panelp, filename.c_str(), &panelp->getFactoryMap());
+ panelp->setVisible(visible);
+ }
+
+ built_floater_t::iterator built_floater_it;
+ for (built_floater_it = mBuiltFloaters.begin();
+ built_floater_it != mBuiltFloaters.end();
+ ++built_floater_it)
+ {
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(built_floater_it->first);
+ if (!floaterp)
+ {
+ continue;
+ }
+ LLString filename = built_floater_it->second;
+ llinfos << "Rebuilding UI floater " << floaterp->getName()
+ << " from " << filename
+ << llendl;
+ BOOL visible = floaterp->getVisible();
+ floaterp->setVisible(FALSE);
+ floaterp->setFocus(FALSE);
+ floaterp->deleteAllChildren();
+
+ gFloaterView->removeChild(floaterp);
+ buildFloater(floaterp, filename, &floaterp->getFactoryMap());
+ floaterp->setVisible(visible);
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+// static
+EWidgetType LLUICtrlFactory::getWidgetType(const LLString& ctrl_type)
+{
+ U32 ctrl_id;
+ for (ctrl_id = 0; ctrl_id < WIDGET_TYPE_COUNT; ctrl_id++)
+ {
+ if (sUICtrlNames[ctrl_id] == ctrl_type)
+ {
+ break;
+ }
+ }
+ return (EWidgetType) ctrl_id;
+}
+
+LLString LLUICtrlFactory::getWidgetType(EWidgetType ctrl_type)
+{
+ return sUICtrlNames[ctrl_type];
+}
+
+LLView *LLUICtrlFactory::createCtrlWidget(LLPanel *parent, LLXMLNodePtr node)
+{
+ LLString ctrl_type = node->getName()->mString;
+ LLString::toLower(ctrl_type);
+
+ creator_list_t::const_iterator it = mCreatorFunctions.find(ctrl_type);
+ if (it == mCreatorFunctions.end())
+ {
+ llwarns << "Unknown control type " << ctrl_type << llendl;
+ return NULL;
+ }
+
+ LLView *ctrl = (*it->second)(node, parent, this);
+
+ return ctrl;
+}
+
+void LLUICtrlFactory::createWidget(LLPanel *parent, LLXMLNodePtr node)
+{
+ LLView* view = createCtrlWidget(parent, node);
+
+ S32 tab_group = parent->getLastTabGroup();
+ node->getAttributeS32("tab_group", tab_group);
+
+ if (view)
+ {
+ parent->addChild(view, tab_group);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// createFactoryPanel()
+//-----------------------------------------------------------------------------
+LLPanel* LLUICtrlFactory::createFactoryPanel(LLString name)
+{
+ std::deque<const LLCallbackMap::map_t*>::iterator itor;
+ for (itor = mFactoryStack.begin(); itor != mFactoryStack.end(); ++itor)
+ {
+ const LLCallbackMap::map_t* factory_map = *itor;
+
+ // Look up this panel's name in the map.
+ LLCallbackMap::map_const_iter_t iter = factory_map->find( name );
+ if (iter != factory_map->end())
+ {
+ // Use the factory to create the panel, instead of using a default LLPanel.
+ LLPanel *ret = (LLPanel*) iter->second.mCallback( iter->second.mData );
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+
+//static
+BOOL LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node, const LLString& name, LLColor4& color)
+{
+ LLString colorstring;
+ BOOL res = node->getAttributeString(name, colorstring);
+ if (res && LLUI::sColorsGroup)
+ {
+ if (LLUI::sColorsGroup->controlExists(colorstring))
+ {
+ color.setVec(LLUI::sColorsGroup->getColor(colorstring));
+ }
+ else
+ {
+ res = FALSE;
+ }
+ }
+ if (!res)
+ {
+ res = LLColor4::parseColor(colorstring.c_str(), &color);
+ }
+ if (!res)
+ {
+ res = node->getAttributeColor(name, color);
+ }
+ return res;
+}
+
+//============================================================================
+
+LLButton* LLUICtrlFactory::getButtonByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLButton*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_BUTTON);
+}
+
+LLCheckBoxCtrl* LLUICtrlFactory::getCheckBoxByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLCheckBoxCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_CHECKBOX);
+}
+
+LLComboBox* LLUICtrlFactory::getComboBoxByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLComboBox*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_COMBO_BOX);
+}
+
+LLIconCtrl* LLUICtrlFactory::getIconByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLIconCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_ICON);
+}
+
+LLLineEditor* LLUICtrlFactory::getLineEditorByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLLineEditor*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_LINE_EDITOR);
+}
+
+LLNameListCtrl* LLUICtrlFactory::getNameListByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLNameListCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_NAME_LIST);
+}
+
+LLRadioGroup* LLUICtrlFactory::getRadioGroupByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLRadioGroup*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_RADIO_GROUP);
+}
+
+LLScrollListCtrl* LLUICtrlFactory::getScrollListByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLScrollListCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SCROLL_LIST);
+}
+
+LLSliderCtrl* LLUICtrlFactory::getSliderByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLSliderCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SLIDER);
+}
+
+LLSlider* LLUICtrlFactory::getSliderBarByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLSlider*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SLIDER_BAR);
+}
+
+LLSpinCtrl* LLUICtrlFactory::getSpinnerByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLSpinCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SPINNER);
+}
+
+LLTextBox* LLUICtrlFactory::getTextBoxByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLTextBox*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_TEXT_BOX);
+}
+
+LLTextEditor* LLUICtrlFactory::getTextEditorByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLTextEditor*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_TEXT_EDITOR);
+}
+
+LLTabContainerCommon* LLUICtrlFactory::getTabContainerByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLTabContainerCommon*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_TAB_CONTAINER);
+}
+
+LLScrollableContainerView* LLUICtrlFactory::getScrollableContainerByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLScrollableContainerView*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SCROLL_CONTAINER);
+}
+
+LLTextureCtrl* LLUICtrlFactory::getTexturePickerByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLTextureCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_TEXTURE_PICKER);
+}
+
+LLPanel* LLUICtrlFactory::getPanelByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLPanel*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_PANEL);
+}
+
+LLColorSwatchCtrl* LLUICtrlFactory::getColorSwatchByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLColorSwatchCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_COLOR_SWATCH);
+}
+
+LLWebBrowserCtrl* LLUICtrlFactory::getWebBrowserCtrlByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLWebBrowserCtrl*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_WEBBROWSER);
+}
+
+LLMenuItemCallGL* LLUICtrlFactory::getMenuItemCallByName(LLPanel* panelp, const LLString& name)
+{
+ return (LLMenuItemCallGL*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_MENU_ITEM_CALL);
+}
+
+LLScrollingPanelList* LLUICtrlFactory::getScrollingPanelList(LLPanel* panelp, const LLString& name)
+{
+ return (LLScrollingPanelList*)panelp->getCtrlByNameAndType(name, WIDGET_TYPE_SCROLLING_PANEL_LIST);
+}
+
+void LLUICtrlFactory::registerCreator(LLString ctrlname, creator_function_t function)
+{
+ LLString::toLower(ctrlname);
+ mCreatorFunctions[ctrlname] = function;
+}
+
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
new file mode 100644
index 0000000000..b3bd5c9020
--- /dev/null
+++ b/indra/llui/lluictrlfactory.h
@@ -0,0 +1,142 @@
+/**
+ * @file lluictrlfactory.h
+ * @brief Factory class for creating UI controls
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLUICTRLFACTORY_H
+#define LLUICTRLFACTORY_H
+
+#include <iosfwd>
+#include <stack>
+
+#include "llcallbackmap.h"
+#include "llfloater.h"
+
+class LLControlGroup;
+class LLView;
+class LLFontGL;
+
+class LLFloater;
+class LLPanel;
+class LLButton;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLIconCtrl;
+class LLLineEditor;
+class LLMenuGL;
+class LLMenuBarGL;
+class LLMenuItemCallGL;
+class LLNameListCtrl;
+class LLPieMenu;
+class LLRadioGroup;
+class LLSearchEditor;
+class LLScrollableContainerView;
+class LLScrollListCtrl;
+class LLSlider;
+class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLWebBrowserCtrl;
+class LLViewBorder;
+class LLColorSwatchCtrl;
+class LLScrollingPanelList;
+
+// Widget
+
+class LLUICtrlFactory
+{
+public:
+ LLUICtrlFactory();
+ // do not call! needs to be public so run-time can clean up the singleton
+ virtual ~LLUICtrlFactory();
+
+ void buildFloater(LLFloater* floaterp, const LLString &filename,
+ const LLCallbackMap::map_t* factory_map = NULL, BOOL open = TRUE);
+
+ void buildPanel(LLPanel* panelp, const LLString &filename,
+ const LLCallbackMap::map_t* factory_map = NULL);
+
+ LLMenuGL *buildMenu(const LLString &filename, LLView* parentp);
+
+ LLPieMenu *buildPieMenu(const LLString &filename, LLView* parentp);
+
+ // Does what you want for LLFloaters and LLPanels
+ // Returns 0 on success
+ S32 saveToXML(LLView* viewp, const LLString& filename);
+
+ void removePanel(LLPanel* panelp);
+ void removeFloater(LLFloater* floaterp);
+
+ void rebuild();
+
+ static EWidgetType getWidgetType(const LLString& ctrl_type);
+ static LLString getWidgetType(EWidgetType ctrl_type);
+ static BOOL getAttributeColor(LLXMLNodePtr node, const LLString& name, LLColor4& color);
+
+ // specific typed getters
+ static LLButton* getButtonByName( LLPanel* panelp, const LLString& name);
+ static LLCheckBoxCtrl* getCheckBoxByName( LLPanel* panelp, const LLString& name);
+ static LLComboBox* getComboBoxByName( LLPanel* panelp, const LLString& name);
+ static LLIconCtrl* getIconByName( LLPanel* panelp, const LLString& name);
+ static LLLineEditor* getLineEditorByName( LLPanel* panelp, const LLString& name);
+ static LLNameListCtrl* getNameListByName( LLPanel* panelp, const LLString& name);
+ static LLRadioGroup* getRadioGroupByName( LLPanel* panelp, const LLString& name);
+ static LLScrollListCtrl* getScrollListByName( LLPanel* panelp, const LLString& name);
+ static LLSliderCtrl* getSliderByName( LLPanel* panelp, const LLString& name);
+ static LLSlider* getSliderBarByName( LLPanel* panelp, const LLString& name);
+ static LLSpinCtrl* getSpinnerByName( LLPanel* panelp, const LLString& name);
+ static LLTextBox* getTextBoxByName( LLPanel* panelp, const LLString& name);
+ static LLTextEditor* getTextEditorByName( LLPanel* panelp, const LLString& name);
+ static LLTabContainerCommon* getTabContainerByName( LLPanel* panelp, const LLString& name);
+ static LLScrollableContainerView* getScrollableContainerByName(LLPanel* panelp, const LLString& name);
+ static LLTextureCtrl* getTexturePickerByName( LLPanel* panelp, const LLString& name);
+ static LLPanel* getPanelByName(LLPanel* panelp, const LLString& name);
+ static LLColorSwatchCtrl* getColorSwatchByName(LLPanel* panelp, const LLString& name);
+ static LLWebBrowserCtrl* getWebBrowserCtrlByName(LLPanel* panelp, const LLString& name);
+ static LLMenuItemCallGL* getMenuItemCallByName(LLPanel* panelp, const LLString& name);
+ static LLScrollingPanelList* getScrollingPanelList(LLPanel* panelp, const LLString& name);
+
+ LLPanel* createFactoryPanel(LLString name);
+
+ virtual LLView* createCtrlWidget(LLPanel *parent, LLXMLNodePtr node);
+ virtual void createWidget(LLPanel *parent, LLXMLNodePtr node);
+
+ // Creator library
+ typedef LLView* (*creator_function_t)(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ void registerCreator(LLString ctrlname, creator_function_t function);
+
+ static bool getLayeredXMLNode(const LLString &filename, LLXMLNodePtr& root);
+
+protected:
+
+
+ typedef std::map<LLViewHandle, LLString> built_panel_t;
+ built_panel_t mBuiltPanels;
+ typedef std::map<LLViewHandle, LLString> built_floater_t;
+ built_floater_t mBuiltFloaters;
+
+ std::deque<const LLCallbackMap::map_t*> mFactoryStack;
+
+ static const LLString sUICtrlNames[];
+
+ typedef std::map<LLString, creator_function_t> creator_list_t;
+ creator_list_t mCreatorFunctions;
+ static std::vector<LLString> mXUIPaths;
+};
+
+template<class T>
+class LLUICtrlCreator
+{
+public:
+ static void registerCreator(LLString name, LLUICtrlFactory *factory)
+ {
+ factory->registerCreator(name, T::fromXML);
+ }
+};
+
+#endif //LL_LLWIDGETFACTORY_H
diff --git a/indra/llui/lluistring.cpp b/indra/llui/lluistring.cpp
new file mode 100755
index 0000000000..8c5b587158
--- /dev/null
+++ b/indra/llui/lluistring.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file lluistring.cpp
+ * @brief LLUIString base class
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lluistring.h"
+
+// public
+
+LLUIString::LLUIString(const LLString& instring, const LLString::format_map_t& args)
+ : mOrig(instring),
+ mArgs(args)
+{
+ format();
+}
+
+void LLUIString::assign(const LLString& s)
+{
+ mOrig = s;
+ format();
+}
+
+void LLUIString::setArgList(const LLString::format_map_t& args)
+{
+ mArgs = args;
+ format();
+}
+
+void LLUIString::setArg(const LLString& key, const LLString& replacement)
+{
+ mArgs[key] = replacement;
+ format();
+}
+
+void LLUIString::truncate(S32 maxchars)
+{
+ if (mWResult.size() > (size_t)maxchars)
+ {
+ LLWString::truncate(mWResult, maxchars);
+ mResult = wstring_to_utf8str(mWResult);
+ }
+}
+
+void LLUIString::erase(S32 charidx, S32 len)
+{
+ mWResult.erase(charidx, len);
+ mResult = wstring_to_utf8str(mWResult);
+}
+
+void LLUIString::insert(S32 charidx, const LLWString& wchars)
+{
+ mWResult.insert(charidx, wchars);
+ mResult = wstring_to_utf8str(mWResult);
+}
+
+void LLUIString::replace(S32 charidx, llwchar wc)
+{
+ mWResult[charidx] = wc;
+ mResult = wstring_to_utf8str(mWResult);
+}
+
+void LLUIString::clear()
+{
+ // Keep Args
+ mOrig.clear();
+ mResult.clear();
+ mWResult.clear();
+}
+
+void LLUIString::clearArgs()
+{
+ mArgs.clear();
+}
+
+// private
+
+void LLUIString::format()
+{
+ mResult = mOrig;
+ LLString::format(mResult, mArgs);
+ mWResult = utf8str_to_wstring(mResult);
+}
diff --git a/indra/llui/lluistring.h b/indra/llui/lluistring.h
new file mode 100755
index 0000000000..8c2e3c481c
--- /dev/null
+++ b/indra/llui/lluistring.h
@@ -0,0 +1,87 @@
+/**
+ * @file lluistring.h
+ * @author: Steve Bennetts
+ * @brief LLUIString base class
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUISTRING_H
+#define LL_LLUISTRING_H
+
+// lluistring.h
+//
+// Copyright 2006, Linden Research, Inc.
+// Original aurthor: Steve
+
+#include "stdtypes.h"
+#include "llstring.h"
+#include <string>
+
+// Use this class to store translated text that may have arguments
+// e.g. "Welcome [USERNAME] to [SECONDLIFE]!"
+
+// Adding or changing an argument will update the result string, preserving the origianl
+// Thus, subsequent changes to arguments or even the original string will produce
+// the correct result
+
+// Example Usage:
+// LLUIString mMessage("Welcome [USERNAME] to [SECONDLIFE]!");
+// mMessage.setArg("[USERNAME]", "Steve");
+// mMessage.setArg("[SECONDLIFE]", "Second Life");
+// llinfos << mMessage.getString().c_str() << llendl; // outputs "Welcome Steve to Second Life"
+// mMessage.setArg("[USERNAME]", "Joe");
+// llinfos << mMessage.getString().c_str() << llendl; // outputs "Welcome Joe to Second Life"
+// mMessage = "Recepción a la [SECONDLIFE] [USERNAME]"
+// mMessage.setArg("[SECONDLIFE]", "Segunda Vida");
+// llinfos << mMessage.getString().c_str() << llendl; // outputs "Recepción a la Segunda Vida Joe"
+
+// Implementation Notes:
+// Attempting to have operator[](const LLString& s) return mArgs[s] fails because we have
+// to call format() after the assignment happens.
+
+class LLUIString
+{
+public:
+ // These methods all perform appropriate argument substitution
+ // and modify mOrig where appropriate
+ LLUIString() {}
+ LLUIString(const LLString& instring, const LLString::format_map_t& args);
+ LLUIString(const LLString& instring) { assign(instring); }
+
+ void assign(const LLString& instring);
+ LLUIString& operator=(const LLString& s) { assign(s); return *this; }
+
+ void setArgList(const LLString::format_map_t& args);
+ void setArg(const LLString& key, const LLString& replacement);
+
+ const LLString& getString() const { return mResult; }
+ operator LLString() const { return mResult; }
+
+ const LLWString& getWString() const { return mWResult; }
+ operator LLWString() const { return mWResult; }
+
+ bool empty() const { return mWResult.empty(); }
+ S32 length() const { return mWResult.size(); }
+
+ void clear();
+ void clearArgs();
+
+ // These utuilty functions are included for text editing.
+ // They do not affect mOrig and do not perform argument substitution
+ void truncate(S32 maxchars);
+ void erase(S32 charidx, S32 len);
+ void insert(S32 charidx, const LLWString& wchars);
+ void replace(S32 charidx, llwchar wc);
+
+private:
+ void format();
+
+ LLString mOrig;
+ LLString mResult;
+ LLWString mWResult; // for displaying
+ LLString::format_map_t mArgs;
+};
+
+#endif // LL_LLUISTRING_H
diff --git a/indra/llui/llundo.cpp b/indra/llui/llundo.cpp
new file mode 100644
index 0000000000..61e17c1879
--- /dev/null
+++ b/indra/llui/llundo.cpp
@@ -0,0 +1,161 @@
+/**
+ * @file llundo.cpp
+ * @brief LLUndo class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Generic interface for undo/redo circular buffer
+
+#include "linden_common.h"
+
+#include "llundo.h"
+#include "llerror.h"
+
+
+// TODO:
+// implement doubly linked circular list for ring buffer
+// this will allow us to easily change the size of an undo buffer on the fly
+
+//-----------------------------------------------------------------------------
+// LLUndoBuffer()
+//-----------------------------------------------------------------------------
+LLUndoBuffer::LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count )
+{
+ mNextAction = 0;
+ mLastAction = 0;
+ mFirstAction = 0;
+ mOperationID = 0;
+
+ mNumActions = initial_count;
+
+ mActions = new LLUndoAction *[initial_count];
+
+ //initialize buffer with actions
+ for (S32 i = 0; i < initial_count; i++)
+ {
+ mActions[i] = create_func();
+ if (!mActions[i])
+ {
+ llerrs << "Unable to create action for undo buffer" << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// ~LLUndoBuffer()
+//-----------------------------------------------------------------------------
+LLUndoBuffer::~LLUndoBuffer()
+{
+ for (S32 i = 0; i < mNumActions; i++)
+ {
+ delete mActions[i];
+ }
+
+ delete [] mActions;
+}
+
+//-----------------------------------------------------------------------------
+// getNextAction()
+//-----------------------------------------------------------------------------
+LLUndoAction *LLUndoBuffer::getNextAction(BOOL setClusterBegin)
+{
+ LLUndoAction *nextAction = mActions[mNextAction];
+
+ if (setClusterBegin)
+ {
+ mOperationID++;
+ }
+ mActions[mNextAction]->mClusterID = mOperationID;
+
+ mNextAction = (mNextAction + 1) % mNumActions;
+ mLastAction = mNextAction;
+
+ if (mNextAction == mFirstAction)
+ {
+ mActions[mFirstAction]->cleanup();
+ mFirstAction = (mFirstAction + 1) % mNumActions;
+ }
+
+ return nextAction;
+}
+
+//-----------------------------------------------------------------------------
+// undoAction()
+//-----------------------------------------------------------------------------
+BOOL LLUndoBuffer::undoAction()
+{
+ if (!canUndo())
+ {
+ return FALSE;
+ }
+
+ S32 prevAction = (mNextAction + mNumActions - 1) % mNumActions;
+
+ while(mActions[prevAction]->mClusterID == mOperationID)
+ {
+ // go ahead and decrement action index
+ mNextAction = prevAction;
+
+ // undo this action
+ mActions[mNextAction]->undo();
+
+ // we're at the first action, so we don't know if we've actually undid everything
+ if (mNextAction == mFirstAction)
+ {
+ mOperationID--;
+ return FALSE;
+ }
+
+ // do wrap-around of index, but avoid negative numbers for modulo operator
+ prevAction = (mNextAction + mNumActions - 1) % mNumActions;
+ }
+
+ mOperationID--;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// redoAction()
+//-----------------------------------------------------------------------------
+BOOL LLUndoBuffer::redoAction()
+{
+ if (!canRedo())
+ {
+ return FALSE;
+ }
+
+ mOperationID++;
+
+ while(mActions[mNextAction]->mClusterID == mOperationID)
+ {
+ if (mNextAction == mLastAction)
+ {
+ return FALSE;
+ }
+
+ mActions[mNextAction]->redo();
+
+ // do wrap-around of index
+ mNextAction = (mNextAction + 1) % mNumActions;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// flushActions()
+//-----------------------------------------------------------------------------
+void LLUndoBuffer::flushActions()
+{
+ for (S32 i = 0; i < mNumActions; i++)
+ {
+ mActions[i]->cleanup();
+ }
+ mNextAction = 0;
+ mLastAction = 0;
+ mFirstAction = 0;
+ mOperationID = 0;
+}
diff --git a/indra/llui/llundo.h b/indra/llui/llundo.h
new file mode 100644
index 0000000000..8d0428ad3e
--- /dev/null
+++ b/indra/llui/llundo.h
@@ -0,0 +1,52 @@
+/**
+ * @file llundo.h
+ * @brief LLUndo class header file
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUNDO_H
+#define LL_LLUNDO_H
+
+class LLUndoAction
+{
+ friend class LLUndoBuffer;
+protected:
+ S32 mClusterID;
+protected:
+ LLUndoAction(): mClusterID(0) {};
+ virtual ~LLUndoAction(){};
+
+public:
+ static LLUndoAction *create() { return NULL; }
+
+ virtual void undo() = 0;
+ virtual void redo() = 0;
+ virtual void cleanup() {};
+};
+
+class LLUndoBuffer
+{
+protected:
+ LLUndoAction **mActions; // array of pointers to undoactions
+ S32 mNumActions; // total number of actions in ring buffer
+ S32 mNextAction; // next action to perform undo/redo on
+ S32 mLastAction; // last action actually added to undo buffer
+ S32 mFirstAction; // beginning of ring buffer (don't undo any further)
+ S32 mOperationID; // current operation id, for undoing and redoing in clusters
+
+public:
+ LLUndoBuffer( LLUndoAction (*create_func()), S32 initial_count );
+ virtual ~LLUndoBuffer();
+
+ LLUndoAction *getNextAction(BOOL setClusterBegin = TRUE);
+ BOOL undoAction();
+ BOOL redoAction();
+ BOOL canUndo() { return (mNextAction != mFirstAction); }
+ BOOL canRedo() { return (mNextAction != mLastAction); }
+
+ void flushActions();
+};
+
+#endif //LL_LLUNDO_H
diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp
new file mode 100644
index 0000000000..f8d1504f3c
--- /dev/null
+++ b/indra/llui/llview.cpp
@@ -0,0 +1,2924 @@
+/**
+ * @file llview.cpp
+ * @author James Cook
+ * @brief Container for other views, anything that draws.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llview.h"
+
+#include "llstring.h"
+#include "llrect.h"
+#include "llgl.h"
+#include "llevent.h"
+#include "llfontgl.h"
+#include "llfocusmgr.h"
+#include "llglheaders.h"
+#include "llwindow.h"
+#include "llstl.h"
+#include "lluictrl.h"
+#include "llui.h" // colors saved settings
+#include "v3color.h"
+#include "llstl.h"
+
+#include <boost/tokenizer.hpp>
+
+#include <assert.h>
+
+BOOL LLView::sDebugRects = FALSE;
+BOOL LLView::sDebugKeys = FALSE;
+S32 LLView::sDepth = 0;
+LLView* LLView::sFastFrameView = NULL;
+BOOL LLView::sDebugMouseHandling = FALSE;
+LLString LLView::sMouseHandlerMessage;
+S32 LLView::sSelectID = GL_NAME_UI_RESERVED;
+BOOL LLView::sEditingUI = FALSE;
+BOOL LLView::sForceReshape = FALSE;
+LLView* LLView::sEditingUIView = NULL;
+S32 LLView::sLastLeftXML = S32_MIN;
+S32 LLView::sLastBottomXML = S32_MIN;
+std::map<LLViewHandle,LLView*> LLView::sViewHandleMap;
+
+S32 LLViewHandle::sNextID = 0;
+LLViewHandle LLViewHandle::sDeadHandle;
+
+#if LL_DEBUG
+BOOL LLView::sIsDrawing = FALSE;
+#endif
+
+//static
+LLView* LLView::getViewByHandle(LLViewHandle handle)
+{
+ if (handle == LLViewHandle::sDeadHandle)
+ {
+ return NULL;
+ }
+ std::map<LLViewHandle,LLView*>::iterator iter = sViewHandleMap.find(handle);
+ if (iter != sViewHandleMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//static
+BOOL LLView::deleteViewByHandle(LLViewHandle handle)
+{
+ std::map<LLViewHandle,LLView*>::iterator iter = sViewHandleMap.find(handle);
+ if (iter != sViewHandleMap.end())
+ {
+ delete iter->second; // will remove from map
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+LLView::LLView() :
+ mParentView(NULL),
+ mReshapeFlags(FOLLOWS_NONE),
+ mDefaultTabGroup(0),
+ mEnabled(TRUE),
+ mMouseOpaque(TRUE),
+ mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
+ mSaveToXML(TRUE),
+ mIsFocusRoot(FALSE),
+ mLastVisible(TRUE),
+ mRenderInFastFrame(TRUE),
+ mSpanChildren(FALSE),
+ mVisible(TRUE),
+ mHidden(FALSE),
+ mNextInsertionOrdinal(0)
+{
+ mViewHandle.init();
+ sViewHandleMap[mViewHandle] = this;
+}
+
+LLView::LLView(const LLString& name, BOOL mouse_opaque) :
+ mParentView(NULL),
+ mName(name),
+ mReshapeFlags(FOLLOWS_NONE),
+ mDefaultTabGroup(0),
+ mEnabled(TRUE),
+ mMouseOpaque(mouse_opaque),
+ mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
+ mSaveToXML(TRUE),
+ mIsFocusRoot(FALSE),
+ mLastVisible(TRUE),
+ mRenderInFastFrame(TRUE),
+ mSpanChildren(FALSE),
+ mVisible(TRUE),
+ mHidden(FALSE),
+ mNextInsertionOrdinal(0)
+{
+ mViewHandle.init();
+ sViewHandleMap[mViewHandle] = this;
+}
+
+
+LLView::LLView(
+ const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 reshape) :
+ mParentView(NULL),
+ mName(name),
+ mRect(rect),
+ mReshapeFlags(reshape),
+ mDefaultTabGroup(0),
+ mEnabled(TRUE),
+ mMouseOpaque(mouse_opaque),
+ mSoundFlags(MOUSE_UP), // default to only make sound on mouse up
+ mSaveToXML(TRUE),
+ mIsFocusRoot(FALSE),
+ mLastVisible(TRUE),
+ mRenderInFastFrame(TRUE),
+ mSpanChildren(FALSE),
+ mVisible(TRUE),
+ mHidden(FALSE),
+ mNextInsertionOrdinal(0)
+{
+ mViewHandle.init();
+ sViewHandleMap[mViewHandle] = this;
+}
+
+
+LLView::~LLView()
+{
+ //llinfos << "Deleting view " << mName << ":" << (void*) this << llendl;
+// llassert(LLView::sIsDrawing == FALSE);
+ if( gFocusMgr.getKeyboardFocus() == this )
+ {
+ llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl;
+ gFocusMgr.removeKeyboardFocusWithoutCallback( this );
+ }
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl;
+ gFocusMgr.removeMouseCaptureWithoutCallback( this );
+ }
+
+ if( gFocusMgr.getTopView() == this )
+ {
+ llwarns << "View holding top view deleted: " << getName() << ". Top view removed." << llendl;
+ gFocusMgr.removeTopViewWithoutCallback( this );
+ }
+
+ sViewHandleMap.erase(mViewHandle);
+
+ deleteAllChildren();
+
+ if (mParentView != NULL)
+ {
+ mParentView->removeChild(this);
+ }
+
+ if(LLView::sFastFrameView == this)
+ {
+ LLView::sFastFrameView = NULL;
+ }
+
+ dispatch_list_t::iterator itor;
+ for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor)
+ {
+ (*itor).second->clearDispatchers();
+ delete (*itor).second;
+ }
+}
+
+// virtual
+BOOL LLView::isView()
+{
+ return TRUE;
+}
+
+// virtual
+BOOL LLView::isCtrl() const
+{
+ return FALSE;
+}
+
+// virtual
+BOOL LLView::isPanel()
+{
+ return FALSE;
+}
+
+void LLView::setMouseOpaque(BOOL b)
+{
+ mMouseOpaque = b;
+}
+
+void LLView::setToolTip(const LLString& msg)
+{
+ mToolTipMsg = msg;
+}
+
+// virtual
+void LLView::setRect(const LLRect& rect)
+{
+ mRect = rect;
+}
+
+
+void LLView::setFollows(U32 flags)
+{
+ mReshapeFlags = flags;
+}
+
+void LLView::setFollowsNone()
+{
+ mReshapeFlags = FOLLOWS_NONE;
+}
+
+void LLView::setFollowsLeft()
+{
+ mReshapeFlags |= FOLLOWS_LEFT;
+}
+
+void LLView::setFollowsTop()
+{
+ mReshapeFlags |= FOLLOWS_TOP;
+}
+
+void LLView::setFollowsRight()
+{
+ mReshapeFlags |= FOLLOWS_RIGHT;
+}
+
+void LLView::setFollowsBottom()
+{
+ mReshapeFlags |= FOLLOWS_BOTTOM;
+}
+
+void LLView::setFollowsAll()
+{
+ mReshapeFlags |= FOLLOWS_ALL;
+}
+
+void LLView::setSoundFlags(U8 flags)
+{
+ mSoundFlags = flags;
+}
+
+void LLView::setName(LLString name)
+{
+ mName = name;
+}
+
+void LLView::setSpanChildren( BOOL span_children )
+{
+ mSpanChildren = span_children; updateRect();
+}
+
+const LLString& LLView::getToolTip()
+{
+ return mToolTipMsg;
+}
+
+// virtual
+const LLString& LLView::getName() const
+{
+ static const LLString unnamed("(no name)");
+ return mName.empty() ? unnamed : mName;
+}
+
+void LLView::sendChildToFront(LLView* child)
+{
+ if (child->mParentView == this)
+ {
+ mChildList.remove( child );
+ mChildList.push_front(child);
+ }
+}
+
+void LLView::sendChildToBack(LLView* child)
+{
+ if (child->mParentView == this)
+ {
+ mChildList.remove( child );
+ mChildList.push_back(child);
+ }
+}
+
+void LLView::moveChildToFrontOfTabGroup(LLUICtrl* child)
+{
+ if(mCtrlOrder.find(child) != mCtrlOrder.end())
+ {
+ mCtrlOrder[child].second = -1 * mNextInsertionOrdinal++;
+ }
+}
+
+void LLView::addChild(LLView* child, S32 tab_group)
+{
+ // remove from current parent
+ if (child->mParentView)
+ {
+ child->mParentView->removeChild(child);
+ }
+
+ // add to front of child list, as normal
+ mChildList.push_front(child);
+
+ // add to ctrl list if is LLUICtrl
+ if (child->isCtrl())
+ {
+ // controls are stored in reverse order from render order
+ addCtrlAtEnd((LLUICtrl*) child, tab_group);
+ }
+
+ child->mParentView = this;
+ updateRect();
+}
+
+
+void LLView::addChildAtEnd(LLView* child, S32 tab_group)
+{
+ // remove from current parent
+ if (child->mParentView)
+ {
+ child->mParentView->removeChild(child);
+ }
+
+ // add to back of child list
+ mChildList.push_back(child);
+
+ // add to ctrl list if is LLUICtrl
+ if (child->isCtrl())
+ {
+ // controls are stored in reverse order from render order
+ addCtrl((LLUICtrl*) child, tab_group);
+ }
+
+ child->mParentView = this;
+ updateRect();
+}
+
+// remove the specified child from the view, and set it's parent to NULL.
+void LLView::removeChild( LLView* child )
+{
+ if (child->mParentView == this)
+ {
+ mChildList.remove( child );
+ child->mParentView = NULL;
+ }
+ else
+ {
+ llerrs << "LLView::removeChild called with non-child" << llendl;
+ }
+
+ if (child->isCtrl())
+ {
+ removeCtrl((LLUICtrl*)child);
+ }
+}
+
+void LLView::addCtrlAtEnd(LLUICtrl* ctrl, S32 tab_group)
+{
+ mCtrlOrder.insert(tab_order_pair_t(ctrl,
+ tab_order_t(tab_group, mNextInsertionOrdinal++)));
+}
+
+void LLView::addCtrl( LLUICtrl* ctrl, S32 tab_group)
+{
+ // add to front of list by using negative ordinal, which monotonically increases
+ mCtrlOrder.insert(tab_order_pair_t(ctrl,
+ tab_order_t(tab_group, -1 * mNextInsertionOrdinal++)));
+}
+
+void LLView::removeCtrl(LLUICtrl* ctrl)
+{
+ child_tab_order_t::iterator found = mCtrlOrder.find(ctrl);
+ if(found != mCtrlOrder.end())
+ {
+ mCtrlOrder.erase(found);
+ }
+}
+
+S32 LLView::getDefaultTabGroup() const { return mDefaultTabGroup; }
+
+LLView::ctrl_list_t LLView::getCtrlList() const
+{
+ ctrl_list_t controls;
+ for(child_list_const_iter_t iter = mChildList.begin();
+ iter != mChildList.end();
+ iter++)
+ {
+ if((*iter)->isCtrl())
+ {
+ controls.push_back(static_cast<LLUICtrl*>(*iter));
+ }
+ }
+ return controls;
+}
+
+LLView::ctrl_list_t LLView::getCtrlListSorted() const
+{
+ ctrl_list_t controls = getCtrlList();
+ std::sort(controls.begin(), controls.end(), LLCompareByTabOrder(mCtrlOrder));
+ return controls;
+}
+
+LLCompareByTabOrder::LLCompareByTabOrder(LLView::child_tab_order_t order): mTabOrder(order) {}
+
+bool LLCompareByTabOrder::compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const
+{
+ return a < b;
+}
+
+// This method compares two LLViews by the tab order specified in the comparator object. The
+// code for this is a little convoluted because each argument can have four states:
+// 1) not a control, 2) a control but not in the tab order, 3) a control in the tab order, 4) null
+bool LLCompareByTabOrder::operator() (const LLView* const a, const LLView* const b) const
+{
+ S32 a_score = 0, b_score = 0;
+ if(a) a_score--;
+ if(b) b_score--;
+ if(a && a->isCtrl()) a_score--;
+ if(b && b->isCtrl()) b_score--;
+ if(a_score == -2 && b_score == -2)
+ {
+ const LLUICtrl * const a_ctrl = static_cast<const LLUICtrl* const>(a);
+ const LLUICtrl * const b_ctrl = static_cast<const LLUICtrl* const>(b);
+ LLView::child_tab_order_const_iter_t a_found = mTabOrder.find(a_ctrl), b_found = mTabOrder.find(b_ctrl);
+ if(a_found != mTabOrder.end()) a_score--;
+ if(b_found != mTabOrder.end()) b_score--;
+ if(a_score == -3 && b_score == -3)
+ {
+ // whew! Once we're in here, they're both in the tab order, and we can compare based on that
+ return compareTabOrders(a_found->second, b_found->second);
+ }
+ }
+ return (a_score == b_score) ? a < b : a_score < b_score;
+}
+
+BOOL LLView::isInVisibleChain() const
+{
+ const LLView* cur_view = this;
+ while(cur_view)
+ {
+ if (!cur_view->getVisible())
+ {
+ return FALSE;
+ }
+ cur_view = cur_view->getParent();
+ }
+ return TRUE;
+}
+
+BOOL LLView::isInEnabledChain() const
+{
+ const LLView* cur_view = this;
+ while(cur_view)
+ {
+ if (!cur_view->getEnabled())
+ {
+ return FALSE;
+ }
+ cur_view = cur_view->getParent();
+ }
+ return TRUE;
+}
+
+BOOL LLView::isFocusRoot() const
+{
+ return mIsFocusRoot;
+}
+
+LLView* LLView::findRootMostFocusRoot()
+{
+ LLView* focus_root = NULL;
+ LLView* next_view = this;
+ while(next_view)
+ {
+ if (next_view->isFocusRoot())
+ {
+ focus_root = next_view;
+ }
+ next_view = next_view->getParent();
+ }
+ return focus_root;
+}
+
+BOOL LLView::canFocusChildren() const
+{
+ return TRUE;
+}
+
+BOOL LLView::focusNextRoot()
+{
+ LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
+ return LLView::focusNext(result);
+}
+
+BOOL LLView::focusPrevRoot()
+{
+ LLView::child_list_t result = LLView::getFocusRootsQuery().run(this);
+ return LLView::focusPrev(result);
+}
+
+BOOL LLView::focusNextItem(BOOL text_fields_only)
+{
+ // this assumes that this method is called on the focus root.
+ LLCtrlQuery query = LLView::getTabOrderQuery();
+ if(text_fields_only || LLUI::sConfigGroup->getBOOL("TabToTextFieldsOnly"))
+ {
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ }
+ LLView::child_list_t result = query(this);
+ return LLView::focusNext(result);
+}
+
+BOOL LLView::focusPrevItem(BOOL text_fields_only)
+{
+ // this assumes that this method is called on the focus root.
+ LLCtrlQuery query = LLView::getTabOrderQuery();
+ if(text_fields_only || LLUI::sConfigGroup->getBOOL("TabToTextFieldsOnly"))
+ {
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ }
+ LLView::child_list_t result = query(this);
+ return LLView::focusPrev(result);
+}
+
+
+// static
+BOOL LLView::focusNext(LLView::child_list_t & result)
+{
+ LLView::child_list_iter_t focused = result.end();
+ for(LLView::child_list_iter_t iter = result.begin();
+ iter != result.end();
+ ++iter)
+ {
+ if(gFocusMgr.childHasKeyboardFocus(*iter))
+ {
+ focused = iter;
+ break;
+ }
+ }
+ LLView::child_list_iter_t next = focused;
+ next = (next == result.end()) ? result.begin() : ++next;
+ while(next != focused)
+ {
+ // wrap around to beginning if necessary
+ if(next == result.end())
+ {
+ next = result.begin();
+ }
+ if((*next)->isCtrl())
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ return TRUE;
+ }
+ ++next;
+ }
+ return FALSE;
+}
+
+// static
+BOOL LLView::focusPrev(LLView::child_list_t & result)
+{
+ LLView::child_list_reverse_iter_t focused = result.rend();
+ for(LLView::child_list_reverse_iter_t iter = result.rbegin();
+ iter != result.rend();
+ ++iter)
+ {
+ if(gFocusMgr.childHasKeyboardFocus(*iter))
+ {
+ focused = iter;
+ break;
+ }
+ }
+ LLView::child_list_reverse_iter_t next = focused;
+ next = (next == result.rend()) ? result.rbegin() : ++next;
+ while(next != focused)
+ {
+ // wrap around to beginning if necessary
+ if(next == result.rend())
+ {
+ next = result.rbegin();
+ }
+ if((*next)->isCtrl())
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(*next);
+ if (!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ ++next;
+ }
+ return FALSE;
+}
+
+BOOL LLView::focusFirstItem(BOOL prefer_text_fields)
+{
+ // search for text field first
+ if(prefer_text_fields)
+ {
+ LLCtrlQuery query = LLView::getTabOrderQuery();
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ LLView::child_list_t result = query(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ }
+ // no text field found, or we don't care about text fields
+ LLView::child_list_t result = LLView::getTabOrderQuery().run(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.front());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLView::focusLastItem(BOOL prefer_text_fields)
+{
+ // search for text field first
+ if(prefer_text_fields)
+ {
+ LLCtrlQuery query = LLView::getTabOrderQuery();
+ query.addPreFilter(LLUICtrl::LLTextInputFilter::getInstance());
+ LLView::child_list_t result = query(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ }
+ // no text field found, or we don't care about text fields
+ LLView::child_list_t result = LLView::getTabOrderQuery().run(this);
+ if(result.size() > 0)
+ {
+ LLUICtrl * ctrl = static_cast<LLUICtrl*>(result.back());
+ if(!ctrl->hasFocus())
+ {
+ ctrl->setFocus(TRUE);
+ ctrl->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+
+// delete all children. Override this function if you need to
+// perform any extra clean up such as cached pointers to selected
+// children, etc.
+void LLView::deleteAllChildren()
+{
+ // clear out the control ordering
+ mCtrlOrder.clear();
+
+ while (!mChildList.empty())
+ {
+ LLView* viewp = mChildList.front();
+ delete viewp; // will remove the child from mChildList
+ }
+}
+
+void LLView::setAllChildrenEnabled(BOOL b)
+{
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ viewp->setEnabled(b);
+ }
+}
+
+// virtual
+void LLView::setTentative(BOOL b)
+{
+}
+
+// virtual
+BOOL LLView::getTentative() const
+{
+ return FALSE;
+}
+
+// virtual
+void LLView::setEnabled(BOOL enabled)
+{
+ mEnabled = enabled;
+}
+
+// virtual
+void LLView::setVisible(BOOL visible)
+{
+ if( !visible && (gFocusMgr.getTopView() == this) )
+ {
+ gFocusMgr.setTopView( NULL, NULL );
+ }
+
+ if ( mVisible != visible )
+ {
+ // tell all children of this view that the visibility may have changed
+ onVisibilityChange ( visible );
+ }
+
+ mVisible = visible;
+}
+
+// virtual
+void LLView::setHidden(BOOL hidden)
+{
+ mHidden = hidden;
+}
+
+// virtual
+BOOL LLView::setLabelArg(const LLString& key, const LLString& text)
+{
+ return FALSE;
+}
+
+void LLView::onVisibilityChange ( BOOL curVisibilityIn )
+{
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ // only views that are themselves visible will have their overall visibility affected by their ancestors
+ if (viewp->getVisible())
+ {
+ viewp->onVisibilityChange ( curVisibilityIn );
+ }
+ }
+}
+
+// virtual
+void LLView::translate(S32 x, S32 y)
+{
+ mRect.translate(x, y);
+}
+
+// virtual
+BOOL LLView::canSnapTo(LLView* other_view)
+{
+ return other_view->getVisible();
+}
+
+// virtual
+void LLView::snappedTo(LLView* snap_view)
+{
+}
+
+BOOL LLView::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleHover( x, y, mask ) != NULL;
+ if( !handled && mMouseOpaque && pointInView( x, y ) )
+ {
+ LLUI::sWindow->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << llendl;
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLView::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ BOOL handled = FALSE;
+
+ LLString tool_tip;
+
+ if ( getVisible() && getEnabled())
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
+ {
+ handled = TRUE;
+ break;
+ }
+ }
+
+ if (LLUI::sShowXUINames && (mToolTipMsg.find(".xml", 0) == LLString::npos) &&
+ (mName.find("Drag", 0) == LLString::npos))
+ {
+ tool_tip = mName;
+ }
+ else
+ {
+ tool_tip = mToolTipMsg;
+ }
+
+
+
+ BOOL showNamesTextBox = LLUI::sShowXUINames && (getWidgetType() == WIDGET_TYPE_TEXT_BOX);
+
+ if( !handled && (mMouseOpaque || showNamesTextBox) && pointInView( x, y ) && !tool_tip.empty())
+ {
+
+ msg = tool_tip;
+
+ // Convert rect local to screen coordinates
+ localPointToScreen(
+ 0, 0,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ localPointToScreen(
+ mRect.getWidth(), mRect.getHeight(),
+ &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
+
+ handled = TRUE;
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+
+ if( called_from_parent )
+ {
+ // Downward traversal
+ if (getVisible() && mEnabled)
+ {
+ handled = childrenHandleKey( key, mask ) != NULL;
+ }
+ }
+
+ if( !handled )
+ {
+ // JC: Must pass to disabled views, since they could have
+ // keyboard focus, which requires the escape key to exit.
+ if (getVisible())
+ {
+ handled = handleKeyHere( key, mask, called_from_parent );
+ if (handled && LLView::sDebugKeys)
+ {
+ llinfos << "Key handled by " << getName() << llendl;
+ }
+ }
+ }
+
+ if( !handled && !called_from_parent)
+ {
+ if (mIsFocusRoot)
+ {
+ // stop processing at focus root
+ handled = FALSE;
+ }
+ else if (mParentView)
+ {
+ // Upward traversal
+ handled = mParentView->handleKey( key, mask, FALSE );
+ }
+ }
+ return handled;
+}
+
+// Called from handleKey()
+// Handles key in this object. Checking parents and children happens in handleKey()
+BOOL LLView::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ return FALSE;
+}
+
+
+BOOL LLView::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent)
+{
+ BOOL handled = FALSE;
+
+ /*
+ if( called_from_parent )
+ {
+ // Downward traversal
+ if (getVisible() && mEnabled)
+ {
+ handled = childrenHandleKey( key, mask ) != NULL;
+ }
+ }
+ */
+
+ // JC: Must pass to disabled views, since they could have
+ // keyboard focus, which requires the escape key to exit.
+ if (getVisible())
+ {
+ handled = handleUnicodeCharHere(uni_char, called_from_parent);
+ if (handled && LLView::sDebugKeys)
+ {
+ llinfos << "Unicode key handled by " << getName() << llendl;
+ }
+ }
+
+
+ if (!handled && !called_from_parent)
+ {
+ if (mIsFocusRoot)
+ {
+ // stop processing at focus root
+ handled = FALSE;
+ }
+ else if(mParentView)
+ {
+ // Upward traversal
+ handled = mParentView->handleUnicodeChar(uni_char, FALSE);
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent )
+{
+ return FALSE;
+}
+
+
+BOOL LLView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type, void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
+ BOOL handled = childrenHandleDragAndDrop( x, y, mask, drop,
+ cargo_type,
+ cargo_data,
+ accept,
+ tooltip_msg) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ *accept = ACCEPT_NO;
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLView " << getName() << llendl;
+ }
+
+ return handled;
+}
+
+LLView* LLView::childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLView* handled_view = FALSE;
+ // CRO this is an experiment to allow drag and drop into object inventory based on the DragAndDrop tool's permissions rather than the parent
+ if( getVisible() )
+// if( getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if( viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->mEnabled &&
+ viewp->handleDragAndDrop(local_x, local_y, mask, drop,
+ cargo_type,
+ cargo_data,
+ accept,
+ tooltip_msg))
+ {
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+
+
+BOOL LLView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleMouseUp( x, y, mask ) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ handled = TRUE;
+ }
+ return handled;
+}
+
+BOOL LLView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = childrenHandleMouseDown( x, y, mask );
+ BOOL handled = (handled_view != NULL);
+ if( !handled && mMouseOpaque )
+ {
+ handled = TRUE;
+ handled_view = this;
+ }
+
+ // HACK If we're editing UI, select the leaf view that ate the click.
+ if (sEditingUI && handled_view)
+ {
+ // need to find leaf views, big hack
+ EWidgetType type = handled_view->getWidgetType();
+ if (type == WIDGET_TYPE_BUTTON
+ || type == WIDGET_TYPE_LINE_EDITOR
+ || type == WIDGET_TYPE_TEXT_EDITOR
+ || type == WIDGET_TYPE_TEXT_BOX)
+ {
+ sEditingUIView = handled_view;
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ handleMouseDown(x, y, mask);
+ handled = TRUE;
+ }
+ return handled;
+}
+
+BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled )
+ {
+ handled = childrenHandleScrollWheel( x, y, clicks ) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ handled = TRUE;
+ }
+ }
+ return handled;
+}
+
+BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ handled = TRUE;
+ }
+ return handled;
+}
+
+BOOL LLView::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = childrenHandleRightMouseUp( x, y, mask ) != NULL;
+ if( !handled && mMouseOpaque )
+ {
+ handled = TRUE;
+ }
+ return handled;
+}
+
+LLView* LLView::childrenHandleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ LLView* handled_view = NULL;
+ if (getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if (viewp->pointInView(local_x, local_y) &&
+ viewp->handleScrollWheel( local_x, local_y, clicks ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+LLView* LLView::childrenHandleHover(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+ if (getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if(viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->getEnabled() &&
+ viewp->handleHover(local_x, local_y, mask) )
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+// Called during downward traversal
+LLView* LLView::childrenHandleKey(KEY key, MASK mask)
+{
+ LLView* handled_view = NULL;
+
+ if ( getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->handleKey(key, mask, TRUE))
+ {
+ if (LLView::sDebugKeys)
+ {
+ llinfos << "Key handled by " << viewp->getName() << llendl;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+
+ return handled_view;
+}
+
+
+LLView* LLView::childrenHandleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+
+ if (viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->mEnabled &&
+ viewp->handleMouseDown( local_x, local_y, mask ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ return handled_view;
+}
+
+LLView* LLView::childrenHandleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+
+ if (getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if (viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->mEnabled &&
+ viewp->handleRightMouseDown( local_x, local_y, mask ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+LLView* LLView::childrenHandleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+
+ if (getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if (viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->mEnabled &&
+ viewp->handleDoubleClick( local_x, local_y, mask ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+LLView* LLView::childrenHandleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+ if( getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if (!viewp->pointInView(local_x, local_y))
+ continue;
+ if (!viewp->getVisible())
+ continue;
+ if (!viewp->mEnabled)
+ continue;
+ if (viewp->handleMouseUp( local_x, local_y, mask ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+LLView* LLView::childrenHandleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLView* handled_view = NULL;
+ if( getVisible() && mEnabled )
+ {
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ S32 local_x = x - viewp->mRect.mLeft;
+ S32 local_y = y - viewp->mRect.mBottom;
+ if (viewp->pointInView(local_x, local_y) &&
+ viewp->getVisible() &&
+ viewp->mEnabled &&
+ viewp->handleRightMouseUp( local_x, local_y, mask ))
+ {
+ if (sDebugMouseHandling)
+ {
+ sMouseHandlerMessage = LLString("->") + viewp->mName.c_str() + sMouseHandlerMessage;
+ }
+ handled_view = viewp;
+ break;
+ }
+ }
+ }
+ return handled_view;
+}
+
+
+void LLView::draw()
+{
+ if (getVisible())
+ {
+ if (sDebugRects)
+ {
+ drawDebugRect();
+
+ // Check for bogus rectangle
+ if (mRect.mRight <= mRect.mLeft
+ || mRect.mTop <= mRect.mBottom)
+ {
+ llwarns << "Bogus rectangle for " << getName() << " with " << mRect << llendl;
+ }
+ }
+
+ LLRect rootRect = getRootView()->getRect();
+ LLRect screenRect;
+
+ // draw focused control on top of everything else
+ LLView* focus_view = gFocusMgr.getKeyboardFocus();
+ if (focus_view && focus_view->getParent() != this)
+ {
+ focus_view = NULL;
+ }
+
+ for (child_list_reverse_iter_t child_iter = mChildList.rbegin(); child_iter != mChildList.rend(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ ++sDepth;
+
+ if (viewp->getVisible() && viewp != focus_view)
+ {
+ // Only draw views that are within the root view
+ localRectToScreen(viewp->getRect(),&screenRect);
+ if ( rootRect.rectInRect(&screenRect) )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)viewp->getRect().mLeft, (F32)viewp->getRect().mBottom, 0.f);
+ viewp->draw();
+ }
+ LLUI::popMatrix();
+ }
+ }
+
+ --sDepth;
+ }
+
+ if (focus_view && focus_view->getVisible())
+ {
+ drawChild(focus_view);
+ }
+
+ // HACK
+ if (sEditingUI && this == sEditingUIView)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+//Draw a box for debugging.
+void LLView::drawDebugRect()
+{
+ // drawing solids requires texturing be disabled
+ LLGLSNoTexture no_texture;
+
+ // draw red rectangle for the border
+ LLColor4 border_color(0.f, 0.f, 0.f, 1.f);
+ if (sEditingUI)
+ {
+ border_color.mV[0] = 1.f;
+ }
+ else
+ {
+ border_color.mV[sDepth%3] = 1.f;
+ }
+
+ glColor4fv( border_color.mV );
+
+ glBegin(GL_LINES);
+ glVertex2i(0, mRect.getHeight() - 1);
+ glVertex2i(0, 0);
+
+ glVertex2i(0, 0);
+ glVertex2i(mRect.getWidth() - 1, 0);
+
+ glVertex2i(mRect.getWidth() - 1, 0);
+ glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1);
+
+ glVertex2i(mRect.getWidth() - 1, mRect.getHeight() - 1);
+ glVertex2i(0, mRect.getHeight() - 1);
+ glEnd();
+
+ // Draw the name if it's not a leaf node
+ if (mChildList.size() && !sEditingUI)
+ {
+ //char temp[256];
+ S32 x, y;
+ glColor4fv( border_color.mV );
+ x = mRect.getWidth()/2;
+ y = mRect.getHeight()/2;
+ LLString debug_text = llformat("%s (%d x %d)", getName().c_str(),
+ mRect.getWidth(), mRect.getHeight());
+ LLFontGL::sSansSerifSmall->renderUTF8(debug_text, 0, (F32)x, (F32)y, border_color,
+ LLFontGL::HCENTER, LLFontGL::BASELINE, LLFontGL::NORMAL,
+ S32_MAX, S32_MAX, NULL, FALSE);
+ }
+}
+
+void LLView::drawChild(LLView* childp, S32 x_offset, S32 y_offset)
+{
+ if (childp && childp->getParent() == this)
+ {
+ ++sDepth;
+
+ if (childp->getVisible())
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ LLUI::translate((F32)childp->getRect().mLeft + x_offset, (F32)childp->getRect().mBottom + y_offset, 0.f);
+ childp->draw();
+ }
+ LLUI::popMatrix();
+ }
+
+ --sDepth;
+ }
+}
+
+
+void LLView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ // make sure this view contains all its children
+ updateRect();
+
+ // compute how much things changed and apply reshape logic to children
+ S32 delta_width = width - mRect.getWidth();
+ S32 delta_height = height - mRect.getHeight();
+
+ if (delta_width || delta_height || sForceReshape)
+ {
+ // adjust our rectangle
+ mRect.mRight = mRect.mLeft + width;
+ mRect.mTop = mRect.mBottom + height;
+
+ // move child views according to reshape flags
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ LLRect child_rect( viewp->mRect );
+
+ if (viewp->followsRight() && viewp->followsLeft())
+ {
+ child_rect.mRight += delta_width;
+ }
+ else if (viewp->followsRight())
+ {
+ child_rect.mLeft += delta_width;
+ child_rect.mRight += delta_width;
+ }
+ else if (viewp->followsLeft())
+ {
+ // left is 0, don't need to adjust coords
+ }
+ else
+ {
+ // BUG what to do when we don't follow anyone?
+ // for now, same as followsLeft
+ }
+
+ if (viewp->followsTop() && viewp->followsBottom())
+ {
+ child_rect.mTop += delta_height;
+ }
+ else if (viewp->followsTop())
+ {
+ child_rect.mTop += delta_height;
+ child_rect.mBottom += delta_height;
+ }
+ else if (viewp->followsBottom())
+ {
+ // bottom is 0, so don't need to adjust coords
+ }
+ else
+ {
+ // BUG what to do when we don't follow?
+ // for now, same as bottom
+ }
+
+ S32 delta_x = child_rect.mLeft - viewp->mRect.mLeft;
+ S32 delta_y = child_rect.mBottom - viewp->mRect.mBottom;
+ viewp->translate( delta_x, delta_y );
+ viewp->reshape(child_rect.getWidth(), child_rect.getHeight());
+ }
+ }
+
+ if (!called_from_parent)
+ {
+ if (mParentView)
+ {
+ mParentView->reshape(mParentView->getRect().getWidth(), mParentView->getRect().getHeight(), FALSE);
+ }
+ }
+}
+
+LLRect LLView::getRequiredRect()
+{
+ return mRect;
+}
+
+const LLRect LLView::getScreenRect() const
+{
+ //FIXME: check for one-off error
+ LLRect screen_rect;
+ localPointToScreen(0, 0, &screen_rect.mLeft, &screen_rect.mBottom);
+ localPointToScreen(mRect.getWidth(), mRect.getHeight(), &screen_rect.mRight, &screen_rect.mTop);
+ return screen_rect;
+}
+
+const LLRect LLView::getLocalRect() const
+{
+ LLRect local_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ return local_rect;
+}
+
+void LLView::updateRect()
+{
+ if (mSpanChildren && mChildList.size())
+ {
+ LLView* first_child = (*mChildList.begin());
+ LLRect child_spanning_rect = first_child->mRect;
+
+ for ( child_list_iter_t child_it = ++mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ if (viewp->getVisible())
+ {
+ child_spanning_rect |= viewp->mRect;
+ }
+ }
+
+ S32 translate_x = llmin(0, child_spanning_rect.mLeft);
+ S32 translate_y = llmin(0, child_spanning_rect.mBottom);
+ S32 new_width = llmax(mRect.getWidth() + translate_x, child_spanning_rect.getWidth());
+ S32 new_height = llmax(mRect.getHeight() + translate_y, child_spanning_rect.getHeight());
+
+ mRect.setOriginAndSize(mRect.mLeft + translate_x, mRect.mBottom + translate_y, new_width, new_height);
+
+ for ( child_list_iter_t child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* viewp = *child_it;
+ viewp->mRect.translate(-translate_x, -translate_y);
+ }
+ }
+}
+
+BOOL LLView::hasAncestor(LLView* parentp)
+{
+ if (!parentp)
+ {
+ return FALSE;
+ }
+
+ LLView* viewp = getParent();
+ while(viewp)
+ {
+ if (viewp == parentp)
+ {
+ return TRUE;
+ }
+ viewp = viewp->getParent();
+ }
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+
+BOOL LLView::childHasKeyboardFocus( const LLString& childname ) const
+{
+ LLView *child = getChildByName(childname);
+ if (child)
+ {
+ return gFocusMgr.childHasKeyboardFocus(child);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+BOOL LLView::hasChild(const LLString& childname, BOOL recurse) const
+{
+ return getChildByName(childname, recurse) ? TRUE : FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getChildByName()
+//-----------------------------------------------------------------------------
+LLView* LLView::getChildByName(const LLString& name, BOOL recurse) const
+{
+ if(name.empty()) return NULL;
+ child_list_const_iter_t child_it;
+ // Look for direct children *first*
+ for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* childp = *child_it;
+ if (childp->getName() == name)
+ {
+ return childp;
+ }
+ }
+ if (recurse)
+ {
+ // Look inside the child as well.
+ for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it)
+ {
+ LLView* childp = *child_it;
+ LLView* viewp = childp->getChildByName(name, recurse);
+ if ( viewp )
+ {
+ return viewp;
+ }
+ }
+ }
+ return NULL;
+}
+
+// virtual
+void LLView::onFocusLost()
+{
+}
+
+// virtual
+void LLView::onFocusReceived()
+{
+}
+
+// virtual
+void LLView::screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+{
+ *local_x = screen_x - mRect.mLeft;
+ *local_y = screen_y - mRect.mBottom;
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ *local_x -= cur->mRect.mLeft;
+ *local_y -= cur->mRect.mBottom;
+ }
+}
+
+void LLView::localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+{
+ *screen_x = local_x + mRect.mLeft;
+ *screen_y = local_y + mRect.mBottom;
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ *screen_x += cur->mRect.mLeft;
+ *screen_y += cur->mRect.mBottom;
+ }
+}
+
+void LLView::screenRectToLocal(const LLRect& screen, LLRect* local) const
+{
+ *local = screen;
+ local->translate( -mRect.mLeft, -mRect.mBottom );
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ local->translate( -cur->mRect.mLeft, -cur->mRect.mBottom );
+ }
+}
+
+void LLView::localRectToScreen(const LLRect& local, LLRect* screen) const
+{
+ *screen = local;
+ screen->translate( mRect.mLeft, mRect.mBottom );
+
+ const LLView* cur = this;
+ while( cur->mParentView )
+ {
+ cur = cur->mParentView;
+ screen->translate( cur->mRect.mLeft, cur->mRect.mBottom );
+ }
+}
+
+LLView* LLView::getRootMostFastFrameView()
+{
+ if (gFocusMgr.getTopView() == this)
+ {
+ return this;
+ }
+
+ if (getParent())
+ {
+ LLView* rootmost_view = getParent()->getRootMostFastFrameView();
+ if (rootmost_view)
+ {
+ return rootmost_view;
+ }
+ }
+
+ return mRenderInFastFrame ? this : NULL;
+}
+
+
+LLView* LLView::getRootView()
+{
+ LLView* view = this;
+ while( view->mParentView )
+ {
+ view = view->mParentView;
+ }
+ return view;
+}
+
+//static
+LLWindow* LLView::getWindow(void)
+{
+ return LLUI::sWindow;
+}
+
+// Moves the view so that it is entirely inside of constraint.
+// If the view will not fit because it's too big, aligns with the top and left.
+// (Why top and left? That's where the drag bars are for floaters.)
+BOOL LLView::translateIntoRect(const LLRect& constraint, BOOL allow_partial_outside )
+{
+ S32 delta_x = 0;
+ S32 delta_y = 0;
+
+ if (allow_partial_outside)
+ {
+ const S32 KEEP_ONSCREEN_PIXELS = 16;
+
+ if( mRect.mRight - KEEP_ONSCREEN_PIXELS < constraint.mLeft )
+ {
+ delta_x = constraint.mLeft - (mRect.mRight - KEEP_ONSCREEN_PIXELS);
+ }
+ else
+ if( mRect.mLeft + KEEP_ONSCREEN_PIXELS > constraint.mRight )
+ {
+ delta_x = constraint.mRight - (mRect.mLeft + KEEP_ONSCREEN_PIXELS);
+ delta_x += llmax( 0, mRect.getWidth() - constraint.getWidth() );
+ }
+
+ if( mRect.mTop > constraint.mTop )
+ {
+ delta_y = constraint.mTop - mRect.mTop;
+ }
+ else
+ if( mRect.mTop - KEEP_ONSCREEN_PIXELS < constraint.mBottom )
+ {
+ delta_y = constraint.mBottom - (mRect.mTop - KEEP_ONSCREEN_PIXELS);
+ delta_y -= llmax( 0, mRect.getHeight() - constraint.getHeight() );
+ }
+ }
+ else
+ {
+ if( mRect.mLeft < constraint.mLeft )
+ {
+ delta_x = constraint.mLeft - mRect.mLeft;
+ }
+ else
+ if( mRect.mRight > constraint.mRight )
+ {
+ delta_x = constraint.mRight - mRect.mRight;
+ delta_x += llmax( 0, mRect.getWidth() - constraint.getWidth() );
+ }
+
+ if( mRect.mTop > constraint.mTop )
+ {
+ delta_y = constraint.mTop - mRect.mTop;
+ }
+ else
+ if( mRect.mBottom < constraint.mBottom )
+ {
+ delta_y = constraint.mBottom - mRect.mBottom;
+ delta_y -= llmax( 0, mRect.getHeight() - constraint.getHeight() );
+ }
+ }
+
+ if (delta_x != 0 || delta_y != 0)
+ {
+ translate(delta_x, delta_y);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLView::localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view)
+{
+ LLView* cur_view = this;
+ LLView* root_view = NULL;
+
+ while (cur_view)
+ {
+ if (cur_view == other_view)
+ {
+ *other_x = x;
+ *other_y = y;
+ return TRUE;
+ }
+
+ x += cur_view->getRect().mLeft;
+ y += cur_view->getRect().mBottom;
+
+ cur_view = cur_view->getParent();
+ root_view = cur_view;
+ }
+
+ // assuming common root between two views, chase other_view's parents up to root
+ cur_view = other_view;
+ while (cur_view)
+ {
+ x -= cur_view->getRect().mLeft;
+ y -= cur_view->getRect().mBottom;
+
+ cur_view = cur_view->getParent();
+
+ if (cur_view == root_view)
+ {
+ *other_x = x;
+ *other_y = y;
+ return TRUE;
+ }
+ }
+
+ *other_x = x;
+ *other_y = y;
+ return FALSE;
+}
+
+BOOL LLView::localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const
+{
+ LLRect cur_rect = local;
+ const LLView* cur_view = this;
+ const LLView* root_view = NULL;
+
+ while (cur_view)
+ {
+ if (cur_view == other_view)
+ {
+ *other = cur_rect;
+ return TRUE;
+ }
+
+ cur_rect.translate(cur_view->getRect().mLeft, cur_view->getRect().mBottom);
+
+ cur_view = cur_view->getParent();
+ root_view = cur_view;
+ }
+
+ // assuming common root between two views, chase other_view's parents up to root
+ cur_view = other_view;
+ while (cur_view)
+ {
+ cur_rect.translate(-cur_view->getRect().mLeft, -cur_view->getRect().mBottom);
+
+ cur_view = cur_view->getParent();
+
+ if (cur_view == root_view)
+ {
+ *other = cur_rect;
+ return TRUE;
+ }
+ }
+
+ *other = cur_rect;
+ return FALSE;
+}
+
+// virtual
+LLXMLNodePtr LLView::getXML(bool save_children) const
+{
+ const LLString& type_name = getWidgetTag();
+
+ LLXMLNodePtr node = new LLXMLNode(type_name, FALSE);
+
+ node->createChild("name", TRUE)->setStringValue(getName());
+ node->createChild("width", TRUE)->setIntValue(getRect().getWidth());
+ node->createChild("height", TRUE)->setIntValue(getRect().getHeight());
+
+ LLView* parent = getParent();
+ S32 left = mRect.mLeft;
+ S32 bottom = mRect.mBottom;
+ if (parent) bottom -= parent->getRect().getHeight();
+
+ node->createChild("left", TRUE)->setIntValue(left);
+ node->createChild("bottom", TRUE)->setIntValue(bottom);
+
+ U32 follows_flags = getFollows();
+ if (follows_flags)
+ {
+ std::stringstream buffer;
+ bool pipe = false;
+ if (followsLeft())
+ {
+ buffer << "left";
+ pipe = true;
+ }
+ if (followsTop())
+ {
+ if (pipe) buffer << "|";
+ buffer << "top";
+ pipe = true;
+ }
+ if (followsRight())
+ {
+ if (pipe) buffer << "|";
+ buffer << "right";
+ pipe = true;
+ }
+ if (followsBottom())
+ {
+ if (pipe) buffer << "|";
+ buffer << "bottom";
+ }
+ node->createChild("follows", TRUE)->setStringValue(buffer.str());
+ }
+ // Export all widgets as enabled and visible - code must disable.
+ node->createChild("hidden", TRUE)->setBoolValue(mHidden);
+ node->createChild("mouse_opaque", TRUE)->setBoolValue(mMouseOpaque );
+ if (!mToolTipMsg.empty())
+ {
+ node->createChild("tool_tip", TRUE)->setStringValue(mToolTipMsg);
+ }
+ if (mSoundFlags != MOUSE_UP)
+ {
+ node->createChild("sound_flags", TRUE)->setIntValue((S32)mSoundFlags);
+ }
+
+ node->createChild("enabled", TRUE)->setBoolValue(mEnabled);
+
+ if (!mControlName.empty())
+ {
+ node->createChild("control_name", TRUE)->setStringValue(mControlName);
+ }
+ return node;
+}
+
+// static
+void LLView::addColorXML(LLXMLNodePtr node, const LLColor4& color,
+ const LLString& xml_name, const LLString& control_name)
+{
+ if (color != LLUI::sColorsGroup->getColor(control_name))
+ {
+ node->createChild(xml_name, TRUE)->setFloatValue(4, color.mV);
+ }
+}
+
+// static
+void LLView::saveColorToXML(std::ostream& out, const LLColor4& color,
+ const LLString& xml_name, const LLString& control_name,
+ const LLString& indent)
+{
+ if (color != LLUI::sColorsGroup->getColor(control_name))
+ {
+ out << indent << xml_name << "=\""
+ << color.mV[VRED] << ", "
+ << color.mV[VGREEN] << ", "
+ << color.mV[VBLUE] << ", "
+ << color.mV[VALPHA] << "\"\n";
+ }
+}
+
+//static
+LLString LLView::escapeXML(const LLString& xml, LLString& indent)
+{
+ LLString ret = indent + "\"" + LLXMLNode::escapeXML(xml);
+
+ //replace every newline with a close quote, new line, indent, open quote
+ size_t index = ret.size()-1;
+ size_t fnd;
+
+ while ((fnd = ret.rfind("\n", index)) != std::string::npos)
+ {
+ ret.replace(fnd, 1, "\"\n" + indent + "\"");
+ index = fnd-1;
+ }
+
+ //append close quote
+ ret.append("\"");
+
+ return ret;
+}
+
+// static
+LLWString LLView::escapeXML(const LLWString& xml)
+{
+ LLWString out;
+ for (LLWString::size_type i = 0; i < xml.size(); ++i)
+ {
+ llwchar c = xml[i];
+ switch(c)
+ {
+ case '"': out.append(utf8string_to_wstring("&quot;")); break;
+ case '\'': out.append(utf8string_to_wstring("&apos;")); break;
+ case '&': out.append(utf8string_to_wstring("&amp;")); break;
+ case '<': out.append(utf8string_to_wstring("&lt;")); break;
+ case '>': out.append(utf8string_to_wstring("&gt;")); break;
+ default: out.push_back(c); break;
+ }
+ }
+ return out;
+}
+
+// static
+const LLCtrlQuery & LLView::getTabOrderQuery()
+{
+ static LLCtrlQuery query;
+ if(query.getPreFilters().size() == 0) {
+ query.addPreFilter(LLVisibleFilter::getInstance());
+ query.addPreFilter(LLEnabledFilter::getInstance());
+ query.addPreFilter(LLTabStopFilter::getInstance());
+ query.addPostFilter(LLUICtrl::LLTabStopPostFilter::getInstance());
+ }
+ return query;
+}
+
+// static
+const LLCtrlQuery & LLView::getFocusRootsQuery()
+{
+ static LLCtrlQuery query;
+ if(query.getPreFilters().size() == 0) {
+ query.addPreFilter(LLVisibleFilter::getInstance());
+ query.addPreFilter(LLEnabledFilter::getInstance());
+ query.addPreFilter(LLView::LLFocusRootsFilter::getInstance());
+ }
+ return query;
+}
+
+
+LLView* LLView::findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir,
+ LLView::ESnapType snap_type, S32 threshold, S32 padding)
+{
+ LLView* snap_view = NULL;
+
+ if (!mParentView)
+ {
+ new_rect = mRect;
+ return snap_view;
+ }
+
+ // If the view is near the edge of its parent, snap it to
+ // the edge.
+ LLRect test_rect = getSnapRect();
+ LLRect view_rect = getSnapRect();
+ test_rect.stretch(padding);
+ view_rect.stretch(padding);
+
+ BOOL snapped_x = FALSE;
+ BOOL snapped_y = FALSE;
+
+ LLRect parent_local_snap_rect = mParentView->getSnapRect();
+ parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom);
+
+ if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= threshold && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
+ {
+ view_rect.translate(parent_local_snap_rect.mRight - view_rect.mRight, 0);
+ snap_view = mParentView;
+ snapped_x = TRUE;
+ }
+
+ if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= threshold && test_rect.mLeft * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(parent_local_snap_rect.mLeft - view_rect.mLeft, 0);
+ snap_view = mParentView;
+ snapped_x = TRUE;
+ }
+
+ if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= threshold && test_rect.mBottom * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, parent_local_snap_rect.mBottom - view_rect.mBottom);
+ snap_view = mParentView;
+ snapped_y = TRUE;
+ }
+
+ if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
+ {
+ view_rect.translate(0, parent_local_snap_rect.mTop - view_rect.mTop);
+ snap_view = mParentView;
+ snapped_y = TRUE;
+ }
+ }
+ if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
+ child_it != mParentView->getChildList()->end(); ++child_it)
+ {
+ LLView* siblingp = *child_it;
+ // skip self
+ if (siblingp == this || !siblingp->getVisible() || !canSnapTo(siblingp))
+ {
+ continue;
+ }
+
+ LLRect sibling_rect = siblingp->getSnapRect();
+
+ if (!snapped_x && llabs(test_rect.mRight - sibling_rect.mLeft) <= threshold && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mLeft - view_rect.mRight, 0);
+ if (!snapped_y)
+ {
+ if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mTop - test_rect.mTop);
+ snapped_y = TRUE;
+ }
+ else if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mBottom - test_rect.mBottom);
+ snapped_y = TRUE;
+ }
+ }
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+
+ if (!snapped_x && llabs(test_rect.mLeft - sibling_rect.mRight) <= threshold && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mRight - view_rect.mLeft, 0);
+ if (!snapped_y)
+ {
+ if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mTop - test_rect.mTop);
+ snapped_y = TRUE;
+ }
+ else if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mBottom - test_rect.mBottom);
+ snapped_y = TRUE;
+ }
+ }
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+
+ if (!snapped_y && llabs(test_rect.mBottom - sibling_rect.mTop) <= threshold && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mTop - view_rect.mBottom);
+ if (!snapped_x)
+ {
+ if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mLeft - test_rect.mLeft, 0);
+ snapped_x = TRUE;
+ }
+ else if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mRight - test_rect.mRight, 0);
+ snapped_x = TRUE;
+ }
+ }
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+
+ if (!snapped_y && llabs(test_rect.mTop - sibling_rect.mBottom) <= threshold && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ view_rect.translate(0, sibling_rect.mBottom - view_rect.mTop);
+ if (!snapped_x)
+ {
+ if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mLeft - test_rect.mLeft, 0);
+ snapped_x = TRUE;
+ }
+ else if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ view_rect.translate(sibling_rect.mRight - test_rect.mRight, 0);
+ snapped_x = TRUE;
+ }
+ }
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+
+ if (snapped_x && snapped_y)
+ {
+ break;
+ }
+ }
+ }
+
+ // shrink actual view rect back down
+ view_rect.stretch(-padding);
+ new_rect = view_rect;
+ return snap_view;
+}
+
+LLView* LLView::findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding)
+{
+ LLRect snap_rect = getSnapRect();
+ S32 snap_pos = 0;
+ switch(snap_edge)
+ {
+ case SNAP_LEFT:
+ snap_pos = snap_rect.mLeft;
+ break;
+ case SNAP_RIGHT:
+ snap_pos = snap_rect.mRight;
+ break;
+ case SNAP_TOP:
+ snap_pos = snap_rect.mTop;
+ break;
+ case SNAP_BOTTOM:
+ snap_pos = snap_rect.mBottom;
+ break;
+ }
+
+ if (!mParentView)
+ {
+ new_edge_val = snap_pos;
+ return NULL;
+ }
+
+ LLView* snap_view = NULL;
+
+ // If the view is near the edge of its parent, snap it to
+ // the edge.
+ LLRect test_rect = snap_rect;
+ test_rect.stretch(padding);
+
+ BOOL snapped_x = FALSE;
+ BOOL snapped_y = FALSE;
+
+ LLRect parent_local_snap_rect = mParentView->getSnapRect();
+ parent_local_snap_rect.translate(-mParentView->getRect().mLeft, -mParentView->getRect().mBottom);
+
+ if (snap_type == SNAP_PARENT || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ switch(snap_edge)
+ {
+ case SNAP_RIGHT:
+ if (llabs(parent_local_snap_rect.mRight - test_rect.mRight) <= threshold && (parent_local_snap_rect.mRight - test_rect.mRight) * mouse_dir.mX >= 0)
+ {
+ snap_pos = parent_local_snap_rect.mRight - padding;
+ snap_view = mParentView;
+ snapped_x = TRUE;
+ }
+ break;
+ case SNAP_LEFT:
+ if (llabs(test_rect.mLeft - parent_local_snap_rect.mLeft) <= threshold && test_rect.mLeft * mouse_dir.mX <= 0)
+ {
+ snap_pos = parent_local_snap_rect.mLeft + padding;
+ snap_view = mParentView;
+ snapped_x = TRUE;
+ }
+ break;
+ case SNAP_BOTTOM:
+ if (llabs(test_rect.mBottom - parent_local_snap_rect.mBottom) <= threshold && test_rect.mBottom * mouse_dir.mY <= 0)
+ {
+ snap_pos = parent_local_snap_rect.mBottom + padding;
+ snap_view = mParentView;
+ snapped_y = TRUE;
+ }
+ break;
+ case SNAP_TOP:
+ if (llabs(parent_local_snap_rect.mTop - test_rect.mTop) <= threshold && (parent_local_snap_rect.mTop - test_rect.mTop) * mouse_dir.mY >= 0)
+ {
+ snap_pos = parent_local_snap_rect.mTop - padding;
+ snap_view = mParentView;
+ snapped_y = TRUE;
+ }
+ break;
+ default:
+ llerrs << "Invalid snap edge" << llendl;
+ }
+ }
+
+ if (snap_type == SNAP_SIBLINGS || snap_type == SNAP_PARENT_AND_SIBLINGS)
+ {
+ for ( child_list_const_iter_t child_it = mParentView->getChildList()->begin();
+ child_it != mParentView->getChildList()->end(); ++child_it)
+ {
+ LLView* siblingp = *child_it;
+ // skip self
+ if (siblingp == this || !siblingp->getVisible() || !canSnapTo(siblingp))
+ {
+ continue;
+ }
+
+ LLRect sibling_rect = siblingp->getSnapRect();
+
+ switch(snap_edge)
+ {
+ case SNAP_RIGHT:
+ if (!snapped_x)
+ {
+ if (llabs(test_rect.mRight - sibling_rect.mLeft) <= threshold && (test_rect.mRight - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mLeft - padding;
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= threshold ||
+ llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= threshold)
+ {
+ if (llabs(test_rect.mRight - sibling_rect.mRight) <= threshold && (test_rect.mRight - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mRight;
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+ }
+ }
+ break;
+ case SNAP_LEFT:
+ if (!snapped_x)
+ {
+ if (llabs(test_rect.mLeft - sibling_rect.mRight) <= threshold && (test_rect.mLeft - sibling_rect.mRight) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mRight + padding;
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mTop - (test_rect.mBottom - padding)) <= threshold ||
+ llabs(sibling_rect.mBottom - (test_rect.mTop + padding)) <= threshold)
+ {
+ if (llabs(test_rect.mLeft - sibling_rect.mLeft) <= threshold && (test_rect.mLeft - sibling_rect.mLeft) * mouse_dir.mX <= 0)
+ {
+ snap_pos = sibling_rect.mLeft;
+ snap_view = siblingp;
+ snapped_x = TRUE;
+ }
+ }
+ }
+ break;
+ case SNAP_BOTTOM:
+ if (!snapped_y)
+ {
+ if (llabs(test_rect.mBottom - sibling_rect.mTop) <= threshold && (test_rect.mBottom - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mTop + padding;
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= threshold ||
+ llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= threshold)
+ {
+ if (llabs(test_rect.mBottom - sibling_rect.mBottom) <= threshold && (test_rect.mBottom - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mBottom;
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+ }
+ }
+ break;
+ case SNAP_TOP:
+ if (!snapped_y)
+ {
+ if (llabs(test_rect.mTop - sibling_rect.mBottom) <= threshold && (test_rect.mTop - sibling_rect.mBottom) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mBottom - padding;
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+ // if snapped with sibling along other axis, check for shared edge
+ else if (llabs(sibling_rect.mRight - (test_rect.mLeft - padding)) <= threshold ||
+ llabs(sibling_rect.mLeft - (test_rect.mRight + padding)) <= threshold)
+ {
+ if (llabs(test_rect.mTop - sibling_rect.mTop) <= threshold && (test_rect.mTop - sibling_rect.mTop) * mouse_dir.mY <= 0)
+ {
+ snap_pos = sibling_rect.mTop;
+ snap_view = siblingp;
+ snapped_y = TRUE;
+ }
+ }
+ }
+ break;
+ default:
+ llerrs << "Invalid snap edge" << llendl;
+ }
+ if (snapped_x && snapped_y)
+ {
+ break;
+ }
+ }
+ }
+
+ new_edge_val = snap_pos;
+ return snap_view;
+}
+
+bool operator==(const LLViewHandle& lhs, const LLViewHandle& rhs)
+{
+ return lhs.mID == rhs.mID;
+}
+
+bool operator!=(const LLViewHandle& lhs, const LLViewHandle& rhs)
+{
+ return lhs.mID != rhs.mID;
+}
+
+bool operator<(const LLViewHandle &lhs, const LLViewHandle &rhs)
+{
+ return lhs.mID < rhs.mID;
+}
+
+//-----------------------------------------------------------------------------
+// Listener dispatch functions
+//-----------------------------------------------------------------------------
+
+void LLView::registerEventListener(LLString name, LLSimpleListener* function)
+{
+ mDispatchList.insert(std::pair<LLString, LLSimpleListener*>(name, function));
+}
+
+void LLView::deregisterEventListener(LLString name)
+{
+ dispatch_list_t::iterator itor = mDispatchList.find(name);
+ if (itor != mDispatchList.end())
+ {
+ delete itor->second;
+ mDispatchList.erase(itor);
+ }
+}
+
+LLString LLView::findEventListener(LLSimpleListener *listener) const
+{
+ dispatch_list_t::const_iterator itor;
+ for (itor = mDispatchList.begin(); itor != mDispatchList.end(); ++itor)
+ {
+ if (itor->second == listener)
+ {
+ return itor->first;
+ }
+ }
+ if (mParentView)
+ {
+ return mParentView->findEventListener(listener);
+ }
+ return LLString::null;
+}
+
+LLSimpleListener* LLView::getListenerByName(const LLString& callback_name)
+{
+ LLSimpleListener* callback = NULL;
+ dispatch_list_t::iterator itor = mDispatchList.find(callback_name);
+ if (itor != mDispatchList.end())
+ {
+ callback = itor->second;
+ }
+ else if (mParentView)
+ {
+ callback = mParentView->getListenerByName(callback_name);
+ }
+ return callback;
+}
+
+void LLView::addListenerToControl(LLEventDispatcher *dispatcher, const LLString& name, LLSD filter, LLSD userdata)
+{
+ LLSimpleListener* listener = getListenerByName(name);
+ if (listener)
+ {
+ dispatcher->addListener(listener, filter, userdata);
+ }
+}
+
+LLControlBase *LLView::findControl(LLString name)
+{
+ control_map_t::iterator itor = mFloaterControls.find(name);
+ if (itor != mFloaterControls.end())
+ {
+ return itor->second;
+ }
+ if (mParentView)
+ {
+ return mParentView->findControl(name);
+ }
+ return LLUI::sConfigGroup->getControl(name);
+}
+
+const S32 FLOATER_H_MARGIN = 15;
+const S32 MIN_WIDGET_HEIGHT = 10;
+const S32 VPAD = 4;
+
+// static
+U32 LLView::createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect)
+{
+ U32 follows = 0;
+ S32 x = FLOATER_H_MARGIN;
+ S32 y = 0;
+ S32 w = 0;
+ S32 h = 0;
+
+ U32 last_x = 0;
+ U32 last_y = 0;
+ if (parent_view)
+ {
+ last_y = parent_view->getRect().getHeight();
+ child_list_t::const_iterator itor = parent_view->getChildList()->begin();
+ if (itor != parent_view->getChildList()->end())
+ {
+ LLView *last_view = (*itor);
+ if (last_view->getSaveToXML())
+ {
+ last_x = last_view->getRect().mLeft;
+ last_y = last_view->getRect().mBottom;
+ }
+ }
+ }
+
+ LLString rect_control;
+ node->getAttributeString("rect_control", rect_control);
+ if (! rect_control.empty())
+ {
+ LLRect rect = LLUI::sConfigGroup->getRect(rect_control);
+ x = rect.mLeft;
+ y = rect.mBottom;
+ w = rect.getWidth();
+ h = rect.getHeight();
+ }
+
+ if (node->hasAttribute("left"))
+ {
+ node->getAttributeS32("left", x);
+ }
+ if (node->hasAttribute("bottom"))
+ {
+ node->getAttributeS32("bottom", y);
+ }
+
+ // Make your width the width of the containing
+ // view if you don't specify a width.
+ if (parent_view)
+ {
+ w = llmax(required_rect.getWidth(), parent_view->getRect().getWidth() - (FLOATER_H_MARGIN) - x);
+ h = llmax(MIN_WIDGET_HEIGHT, required_rect.getHeight());
+ }
+
+ if (node->hasAttribute("width"))
+ {
+ node->getAttributeS32("width", w);
+ }
+ if (node->hasAttribute("height"))
+ {
+ node->getAttributeS32("height", h);
+ }
+
+ if (parent_view)
+ {
+ if (node->hasAttribute("left_delta"))
+ {
+ S32 left_delta = 0;
+ node->getAttributeS32("left_delta", left_delta);
+ x = last_x + left_delta;
+ }
+ else if (node->hasAttribute("left") && node->hasAttribute("right"))
+ {
+ // compute width based on left and right
+ S32 right = 0;
+ node->getAttributeS32("right", right);
+ if (right < 0)
+ {
+ right = parent_view->getRect().getWidth() + right;
+ }
+ w = right - x;
+ }
+ else if (node->hasAttribute("left"))
+ {
+ if (x < 0)
+ {
+ x = parent_view->getRect().getWidth() + x;
+ follows |= FOLLOWS_RIGHT;
+ }
+ else
+ {
+ follows |= FOLLOWS_LEFT;
+ }
+ }
+ else if (node->hasAttribute("width") && node->hasAttribute("right"))
+ {
+ S32 right = 0;
+ node->getAttributeS32("right", right);
+ if (right < 0)
+ {
+ right = parent_view->getRect().getWidth() + right;
+ }
+ x = right - w;
+ }
+ else
+ {
+ // left not specified, same as last
+ x = last_x;
+ }
+
+ if (node->hasAttribute("bottom_delta"))
+ {
+ S32 bottom_delta = 0;
+ node->getAttributeS32("bottom_delta", bottom_delta);
+ y = last_y + bottom_delta;
+ }
+ else if (node->hasAttribute("top"))
+ {
+ // compute height based on top
+ S32 top = 0;
+ node->getAttributeS32("top", top);
+ if (top < 0)
+ {
+ top = parent_view->getRect().getHeight() + top;
+ }
+ h = top - y;
+ }
+ else if (node->hasAttribute("bottom"))
+ {
+ if (y < 0)
+ {
+ y = parent_view->getRect().getHeight() + y;
+ follows |= FOLLOWS_TOP;
+ }
+ else
+ {
+ follows |= FOLLOWS_BOTTOM;
+ }
+ }
+ else
+ {
+ // if bottom not specified, generate automatically
+ if (last_y == 0)
+ {
+ // treat first child as "bottom"
+ y = parent_view->getRect().getHeight() - (h + VPAD);
+ follows |= FOLLOWS_TOP;
+ }
+ else
+ {
+ // treat subsequent children as "bottom_delta"
+ y = last_y - (h + VPAD);
+ }
+ }
+ }
+ else
+ {
+ x = llmax(x, 0);
+ y = llmax(y, 0);
+ follows = FOLLOWS_LEFT | FOLLOWS_TOP;
+ }
+ rect.setOriginAndSize(x, y, w, h);
+
+ return follows;
+}
+
+void LLView::initFromXML(LLXMLNodePtr node, LLView* parent)
+{
+ // create rect first, as this will supply initial follows flags
+ LLRect view_rect;
+ U32 follows_flags = createRect(node, view_rect, parent, getRequiredRect());
+ // call reshape in case there are any child elements that need to be layed out
+ reshape(view_rect.getWidth(), view_rect.getHeight());
+ setRect(view_rect);
+ setFollows(follows_flags);
+
+ if (node->hasAttribute("follows"))
+ {
+ setFollowsNone();
+
+ LLString follows;
+ node->getAttributeString("follows", follows);
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|");
+ tokenizer tokens(follows, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ while(token_iter != tokens.end())
+ {
+ const std::string& token_str = *token_iter;
+ if (token_str == "left")
+ {
+ setFollowsLeft();
+ }
+ else if (token_str == "right")
+ {
+ setFollowsRight();
+ }
+ else if (token_str == "top")
+ {
+ setFollowsTop();
+ }
+ else if (token_str == "bottom")
+ {
+ setFollowsBottom();
+ }
+ else if (token_str == "all")
+ {
+ setFollowsAll();
+ }
+ ++token_iter;
+ }
+ }
+
+ if (node->hasAttribute("control_name"))
+ {
+ LLString control_name;
+ node->getAttributeString("control_name", control_name);
+ setControlName(control_name, NULL);
+ }
+
+ if (node->hasAttribute("tool_tip"))
+ {
+ LLString tool_tip_msg("");
+ node->getAttributeString("tool_tip", tool_tip_msg);
+ setToolTip(tool_tip_msg);
+ }
+
+ if (node->hasAttribute("enabled"))
+ {
+ BOOL enabled;
+ node->getAttributeBOOL("enabled", enabled);
+ setEnabled(enabled);
+ }
+
+ if (node->hasAttribute("visible"))
+ {
+ BOOL visible;
+ node->getAttributeBOOL("visible", visible);
+ setVisible(visible);
+ }
+
+ if (node->hasAttribute("hidden"))
+ {
+ BOOL hidden;
+ node->getAttributeBOOL("hidden", hidden);
+ setHidden(hidden);
+ }
+
+ node->getAttributeS32("default_tab_group", mDefaultTabGroup);
+
+ reshape(view_rect.getWidth(), view_rect.getHeight());
+}
+
+// static
+LLFontGL* LLView::selectFont(LLXMLNodePtr node)
+{
+ LLFontGL* gl_font = NULL;
+
+ if (node->hasAttribute("font"))
+ {
+ LLString font_name;
+ node->getAttributeString("font", font_name);
+
+ gl_font = LLFontGL::fontFromName(font_name.c_str());
+ }
+ return gl_font;
+}
+
+// static
+LLFontGL::HAlign LLView::selectFontHAlign(LLXMLNodePtr node)
+{
+ LLFontGL::HAlign gl_hfont_align = LLFontGL::LEFT;
+
+ if (node->hasAttribute("halign"))
+ {
+ LLString horizontal_align_name;
+ node->getAttributeString("halign", horizontal_align_name);
+ gl_hfont_align = LLFontGL::hAlignFromName(horizontal_align_name);
+ }
+ return gl_hfont_align;
+}
+
+// static
+LLFontGL::VAlign LLView::selectFontVAlign(LLXMLNodePtr node)
+{
+ LLFontGL::VAlign gl_vfont_align = LLFontGL::BASELINE;
+
+ if (node->hasAttribute("valign"))
+ {
+ LLString vert_align_name;
+ node->getAttributeString("valign", vert_align_name);
+ gl_vfont_align = LLFontGL::vAlignFromName(vert_align_name);
+ }
+ return gl_vfont_align;
+}
+
+// static
+LLFontGL::StyleFlags LLView::selectFontStyle(LLXMLNodePtr node)
+{
+ LLFontGL::StyleFlags gl_font_style = LLFontGL::NORMAL;
+
+ if (node->hasAttribute("style"))
+ {
+ LLString style_flags_name;
+ node->getAttributeString("style", style_flags_name);
+
+ if (style_flags_name == "normal")
+ {
+ gl_font_style = LLFontGL::NORMAL;
+ }
+ else if (style_flags_name == "bold")
+ {
+ gl_font_style = LLFontGL::BOLD;
+ }
+ else if (style_flags_name == "italic")
+ {
+ gl_font_style = LLFontGL::ITALIC;
+ }
+ else if (style_flags_name == "underline")
+ {
+ gl_font_style = LLFontGL::UNDERLINE;
+ }
+ //else leave left
+ }
+ return gl_font_style;
+}
+
+void LLView::setControlValue(const LLSD& value)
+{
+ LLUI::sConfigGroup->setValue(getControlName(), value);
+}
+
+//virtual
+LLString LLView::getControlName() const
+{
+ return mControlName;
+}
+
+//virtual
+void LLView::setControlName(const LLString& control_name, LLView *context)
+{
+ if (context == NULL)
+ {
+ context = this;
+ }
+
+ // Unregister from existing listeners
+ if (!mControlName.empty())
+ {
+ clearDispatchers();
+ }
+
+ // Register new listener
+ if (!control_name.empty())
+ {
+ LLControlBase *control = context->findControl(control_name);
+ if (control)
+ {
+ mControlName = control_name;
+ LLSD state = control->registerListener(this, "DEFAULT");
+ setValue(state);
+ }
+ }
+}
+
+// virtual
+bool LLView::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (userdata.asString() == "DEFAULT" && event->desc() == "value_changed")
+ {
+ LLSD state = event->getValue();
+ setValue(state);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLView::setValue(const LLSD& value)
+{
+}
+
+
+void LLView::addBoolControl(LLString name, bool initial_value)
+{
+ mFloaterControls[name] = new LLControl(name, TYPE_BOOLEAN, initial_value, "Internal floater control");
+}
+
+LLControlBase *LLView::getControl(LLString name)
+{
+ control_map_t::iterator itor = mFloaterControls.find(name);
+ if (itor != mFloaterControls.end())
+ {
+ return itor->second;
+ }
+ return NULL;
+}
diff --git a/indra/llui/llview.h b/indra/llui/llview.h
new file mode 100644
index 0000000000..63d85fbcdc
--- /dev/null
+++ b/indra/llui/llview.h
@@ -0,0 +1,489 @@
+/**
+ * @file llview.h
+ * @brief Container for other views, anything that draws.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEW_H
+#define LL_LLVIEW_H
+
+// A view is an area in a window that can draw. It might represent
+// the HUD or a dialog box or a button. It can also contain sub-views
+// and child widgets
+
+#include <iosfwd>
+#include <list>
+
+#include "lluixmltags.h"
+#include "llrect.h"
+#include "llmousehandler.h"
+#include "stdenums.h"
+#include "llsd.h"
+#include "llstring.h"
+#include "llnametable.h"
+#include "llcoord.h"
+#include "llmortician.h"
+#include "llxmlnode.h"
+#include "llfontgl.h"
+#include "llviewquery.h"
+
+#include "llui.h"
+
+class LLColor4;
+class LLWindow;
+class LLUICtrl;
+class LLScrollListItem;
+
+const U32 FOLLOWS_NONE = 0x00;
+const U32 FOLLOWS_LEFT = 0x01;
+const U32 FOLLOWS_RIGHT = 0x02;
+const U32 FOLLOWS_TOP = 0x10;
+const U32 FOLLOWS_BOTTOM = 0x20;
+const U32 FOLLOWS_ALL = 0x33;
+
+const BOOL MOUSE_OPAQUE = TRUE;
+const BOOL NOT_MOUSE_OPAQUE = FALSE;
+
+const U32 GL_NAME_UI_RESERVED = 2;
+
+class LLSimpleListener;
+class LLEventDispatcher;
+
+class LLViewHandle
+{
+public:
+ LLViewHandle() { mID = 0; }
+
+ void init() { mID = ++sNextID; }
+ void markDead() { mID = 0; }
+ BOOL isDead() { return (mID == 0); }
+ friend bool operator==(const LLViewHandle& lhs, const LLViewHandle& rhs);
+ friend bool operator!=(const LLViewHandle& lhs, const LLViewHandle& rhs);
+ friend bool operator<(const LLViewHandle &a, const LLViewHandle &b);
+
+public:
+ static LLViewHandle sDeadHandle;
+
+protected:
+ S32 mID;
+
+ static S32 sNextID;
+};
+
+class LLView : public LLMouseHandler, public LLMortician, public LLSimpleListenerObservable
+{
+
+public:
+#if LL_DEBUG
+ static BOOL sIsDrawing;
+#endif
+ enum ESoundFlags
+ {
+ SILENT = 0,
+ MOUSE_DOWN = 1,
+ MOUSE_UP = 2
+ };
+
+ enum ESnapType
+ {
+ SNAP_PARENT,
+ SNAP_SIBLINGS,
+ SNAP_PARENT_AND_SIBLINGS
+ };
+
+ enum ESnapEdge
+ {
+ SNAP_LEFT,
+ SNAP_TOP,
+ SNAP_RIGHT,
+ SNAP_BOTTOM
+ };
+
+ typedef std::list<LLView*> child_list_t;
+ typedef child_list_t::iterator child_list_iter_t;
+ typedef child_list_t::const_iterator child_list_const_iter_t;
+ typedef child_list_t::reverse_iterator child_list_reverse_iter_t;
+ typedef child_list_t::const_reverse_iterator child_list_const_reverse_iter_t;
+
+ typedef std::vector<LLUICtrl *> ctrl_list_t;
+
+ typedef std::pair<S32, S32> tab_order_t;
+ typedef std::pair<LLUICtrl *, tab_order_t> tab_order_pair_t;
+ // this structure primarily sorts by the tab group, secondarily by the insertion ordinal (lastly by the value of the pointer)
+ typedef std::map<const LLUICtrl*, tab_order_t> child_tab_order_t;
+ typedef child_tab_order_t::iterator child_tab_order_iter_t;
+ typedef child_tab_order_t::const_iterator child_tab_order_const_iter_t;
+ typedef child_tab_order_t::reverse_iterator child_tab_order_reverse_iter_t;
+ typedef child_tab_order_t::const_reverse_iterator child_tab_order_const_reverse_iter_t;
+
+private:
+ LLView* mParentView;
+ child_list_t mChildList;
+
+protected:
+ LLString mName;
+ // location in pixels, relative to surrounding structure, bottom,left=0,0
+ LLRect mRect;
+
+ U32 mReshapeFlags;
+
+ child_tab_order_t mCtrlOrder;
+ S32 mDefaultTabGroup;
+
+ BOOL mEnabled; // Enabled means "accepts input that has an effect on the state of the application."
+ // A disabled view, for example, may still have a scrollbar that responds to mouse events.
+ BOOL mMouseOpaque; // Opaque views handle all mouse events that are over their rect.
+ LLString mToolTipMsg; // isNull() is true if none.
+
+ U8 mSoundFlags;
+ BOOL mSaveToXML;
+
+ BOOL mIsFocusRoot;
+
+public:
+ LLViewHandle mViewHandle;
+ BOOL mLastVisible;
+ BOOL mRenderInFastFrame;
+ BOOL mSpanChildren;
+
+private:
+ BOOL mVisible;
+ BOOL mHidden; // Never show (generally for replacement text only)
+
+ S32 mNextInsertionOrdinal;
+
+protected:
+ static LLWindow* sWindow; // All root views must know about their window.
+
+public:
+ static BOOL sDebugRects; // Draw debug rects behind everything.
+ static BOOL sDebugKeys;
+ static S32 sDepth;
+ static LLView* sFastFrameView;
+ static BOOL sDebugMouseHandling;
+ static LLString sMouseHandlerMessage;
+ static S32 sSelectID;
+ static BOOL sEditingUI;
+ static LLView* sEditingUIView;
+ static S32 sLastLeftXML;
+ static S32 sLastBottomXML;
+ static std::map<LLViewHandle,LLView*> sViewHandleMap;
+ static BOOL sForceReshape;
+
+public:
+ static LLView* getViewByHandle(LLViewHandle handle);
+ static BOOL deleteViewByHandle(LLViewHandle handle);
+
+public:
+ LLView();
+ LLView(const LLString& name, BOOL mouse_opaque);
+ LLView(const LLString& name, const LLRect& rect, BOOL mouse_opaque, U32 follows=FOLLOWS_NONE);
+
+ virtual ~LLView();
+
+ // Hack to support LLFocusMgr
+ virtual BOOL isView();
+
+ // Some UI widgets need to be added as controls. Others need to
+ // be added as regular view children. isCtrl should return TRUE
+ // if a widget needs to be added as a ctrl
+ virtual BOOL isCtrl() const;
+
+ virtual BOOL isPanel();
+
+ //
+ // MANIPULATORS
+ //
+ void setMouseOpaque( BOOL b );
+ void setToolTip( const LLString& msg );
+
+ virtual void setRect(const LLRect &rect);
+ void setFollows(U32 flags);
+
+ // deprecated, use setFollows() with FOLLOWS_LEFT | FOLLOWS_TOP, etc.
+ void setFollowsNone();
+ void setFollowsLeft();
+ void setFollowsTop();
+ void setFollowsRight();
+ void setFollowsBottom();
+ void setFollowsAll();
+
+ void setSoundFlags(U8 flags);
+ void setName(LLString name);
+ void setSpanChildren( BOOL span_children );
+
+ const LLString& getToolTip();
+
+ void sendChildToFront(LLView* child);
+ void sendChildToBack(LLView* child);
+ void moveChildToFrontOfTabGroup(LLUICtrl* child);
+
+ void addChild(LLView* view, S32 tab_group = 0);
+ void addChildAtEnd(LLView* view, S32 tab_group = 0);
+ // remove the specified child from the view, and set it's parent to NULL.
+ void removeChild( LLView* view );
+
+ virtual void addCtrl( LLUICtrl* ctrl, S32 tab_group);
+ virtual void addCtrlAtEnd( LLUICtrl* ctrl, S32 tab_group);
+ virtual void removeCtrl( LLUICtrl* ctrl);
+
+ child_tab_order_t getCtrlOrder() const { return mCtrlOrder; }
+ ctrl_list_t getCtrlList() const;
+ ctrl_list_t getCtrlListSorted() const;
+ S32 getDefaultTabGroup() const;
+
+ BOOL isInVisibleChain() const;
+ BOOL isInEnabledChain() const;
+
+ BOOL isFocusRoot() const;
+ LLView* findRootMostFocusRoot();
+ virtual BOOL canFocusChildren() const;
+
+ class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
+ {
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
+ {
+ return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
+ }
+ };
+
+ virtual BOOL focusNextRoot();
+ virtual BOOL focusPrevRoot();
+
+ virtual BOOL focusNextItem(BOOL text_entry_only);
+ virtual BOOL focusPrevItem(BOOL text_entry_only);
+ virtual BOOL focusFirstItem(BOOL prefer_text_fields = FALSE );
+ virtual BOOL focusLastItem(BOOL prefer_text_fields = FALSE);
+
+ // delete all children. Override this function if you need to
+ // perform any extra clean up such as cached pointers to selected
+ // children, etc.
+ virtual void deleteAllChildren();
+
+ // by default, does nothing
+ virtual void setTentative(BOOL b);
+ // by default, returns false
+ virtual BOOL getTentative() const;
+ virtual void setAllChildrenEnabled(BOOL b);
+
+ virtual void setEnabled(BOOL enabled);
+ virtual void setVisible(BOOL visible);
+ virtual void setHidden(BOOL hidden); // Never show (replacement text)
+
+ // by default, does nothing and returns false
+ virtual BOOL setLabelArg( const LLString& key, const LLString& text );
+
+ virtual void onVisibilityChange ( BOOL curVisibilityIn );
+
+ void pushVisible(BOOL visible) { mLastVisible = mVisible; setVisible(visible); }
+ void popVisible() { setVisible(mLastVisible); mLastVisible = TRUE; }
+
+ //
+ // ACCESSORS
+ //
+ BOOL getMouseOpaque() const { return mMouseOpaque; }
+
+ U32 getFollows() const { return mReshapeFlags; }
+ BOOL followsLeft() const { return mReshapeFlags & FOLLOWS_LEFT; }
+ BOOL followsRight() const { return mReshapeFlags & FOLLOWS_RIGHT; }
+ BOOL followsTop() const { return mReshapeFlags & FOLLOWS_TOP; }
+ BOOL followsBottom() const { return mReshapeFlags & FOLLOWS_BOTTOM; }
+ BOOL followsAll() const { return mReshapeFlags & FOLLOWS_ALL; }
+
+ const LLRect& getRect() const { return mRect; }
+ const LLRect getScreenRect() const;
+ const LLRect getLocalRect() const;
+ virtual const LLRect getSnapRect() const { return mRect; }
+
+ virtual LLRect getRequiredRect(); // Get required size for this object. 0 for width/height means don't care.
+ virtual void updateRect(); // apply procedural updates to own rectangle
+
+ LLView* getRootView();
+ LLView* getParent() const { return mParentView; }
+ LLView* getFirstChild() { return (mChildList.empty()) ? NULL : *(mChildList.begin()); }
+ S32 getChildCount() const { return (S32)mChildList.size(); }
+ template<class _Pr3> void sortChildren(_Pr3 _Pred) { mChildList.sort(_Pred); }
+ BOOL hasAncestor(LLView* parentp);
+
+ BOOL hasChild(const LLString& childname, BOOL recurse = FALSE) const;
+
+ BOOL childHasKeyboardFocus( const LLString& childname ) const;
+
+ //
+ // UTILITIES
+ //
+
+ // Default behavior is to use reshape flags to resize child views
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual void translate( S32 x, S32 y );
+ BOOL translateIntoRect( const LLRect& constraint, BOOL allow_partial_outside );
+ void setOrigin( S32 x, S32 y ) { mRect.translate( x - mRect.mLeft, y - mRect.mBottom ); }
+ LLView* findSnapRect(LLRect& new_rect, const LLCoordGL& mouse_dir, LLView::ESnapType snap_type, S32 threshold, S32 padding = 0);
+ LLView* findSnapEdge(S32& new_edge_val, const LLCoordGL& mouse_dir, ESnapEdge snap_edge, ESnapType snap_type, S32 threshold, S32 padding = 0);
+
+ // Defaults to other_view->getVisible()
+ virtual BOOL canSnapTo(LLView* other_view);
+
+ virtual void snappedTo(LLView* snap_view);
+
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ // LLMouseHandler functions
+ // Default behavior is to pass events to children
+
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+
+ // Default behavior is to pass the tooltip event to children,
+ // then display mToolTipMsg if no child handled it.
+ /*virtual*/ BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+
+ virtual void draw();
+
+ void drawDebugRect();
+ void drawChild(LLView* childp, S32 x_offset = 0, S32 y_offset = 0);
+
+ virtual const LLString& getName() const;
+
+ virtual EWidgetType getWidgetType() const = 0;
+ virtual LLString getWidgetTag() const = 0;
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+
+ static U32 createRect(LLXMLNodePtr node, LLRect &rect, LLView* parent_view, const LLRect &required_rect = LLRect());
+ virtual void initFromXML(LLXMLNodePtr node, LLView* parent);
+
+ static LLFontGL* selectFont(LLXMLNodePtr node);
+ static LLFontGL::HAlign selectFontHAlign(LLXMLNodePtr node);
+ static LLFontGL::VAlign selectFontVAlign(LLXMLNodePtr node);
+ static LLFontGL::StyleFlags selectFontStyle(LLXMLNodePtr node);
+
+ // Some widgets, like close box buttons, don't need to be saved
+ BOOL getSaveToXML() const { return mSaveToXML; }
+ void setSaveToXML(BOOL b) { mSaveToXML = b; }
+
+ // Only saves color if different from default setting.
+ static void addColorXML(LLXMLNodePtr node, const LLColor4& color,
+ const LLString& xml_name, const LLString& control_name);
+ static void saveColorToXML(std::ostream& out, const LLColor4& color,
+ const LLString& xml_name, const LLString& control_name,
+ const LLString& indent); // DEPRECATED
+ // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt)
+ //static LLString escapeXML(const LLString& xml);
+ static LLWString escapeXML(const LLWString& xml);
+
+ //same as above, but wraps multiple lines in quotes and prepends
+ //indent as leading white space on each line
+ static LLString escapeXML(const LLString& xml, LLString& indent);
+
+ // focuses the item in the list after the currently-focused item, wrapping if necessary
+ static BOOL focusNext(LLView::child_list_t & result);
+ // focuses the item in the list before the currently-focused item, wrapping if necessary
+ static BOOL focusPrev(LLView::child_list_t & result);
+
+ // returns query for iterating over controls in tab order
+ static const LLCtrlQuery & getTabOrderQuery();
+ // return query for iterating over focus roots in tab order
+ static const LLCtrlQuery & getFocusRootsQuery();
+
+ BOOL getEnabled() const { return mEnabled; }
+ BOOL getVisible() const { return mVisible && !mHidden; }
+ U8 getSoundFlags() const { return mSoundFlags; }
+
+ // Default to no action
+ virtual void onFocusLost();
+ virtual void onFocusReceived();
+
+ BOOL parentPointInView(S32 x, S32 y) const { return mRect.pointInRect( x, y ); }
+ BOOL pointInView(S32 x, S32 y) const { return mRect.localPointInRect( x, y ); }
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const;
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const;
+ virtual BOOL localPointToOtherView( S32 x, S32 y, S32 *other_x, S32 *other_y, LLView* other_view);
+ virtual void screenRectToLocal( const LLRect& screen, LLRect* local ) const;
+ virtual void localRectToScreen( const LLRect& local, LLRect* screen ) const;
+ virtual BOOL localRectToOtherView( const LLRect& local, LLRect* other, LLView* other_view ) const;
+
+ void setRenderInFastFrame(BOOL render) { mRenderInFastFrame = render; }
+ virtual LLView* getRootMostFastFrameView();
+
+ static LLWindow* getWindow(void);
+
+ // Listener dispatching functions (Dispatcher deletes pointers to listeners on deregistration or destruction)
+ LLSimpleListener* getListenerByName(const LLString &callback_name);
+ void registerEventListener(LLString name, LLSimpleListener* function);
+ void deregisterEventListener(LLString name);
+ LLString findEventListener(LLSimpleListener *listener) const;
+ void addListenerToControl(LLEventDispatcher *observer, const LLString& name, LLSD filter, LLSD userdata);
+
+ virtual LLView* getChildByName(const LLString& name, BOOL recurse = FALSE) const;
+
+ void addBoolControl(LLString name, bool initial_value);
+ LLControlBase *getControl(LLString name);
+ virtual LLControlBase *findControl(LLString name);
+
+ void setControlValue(const LLSD& value);
+ virtual void setControlName(const LLString& control, LLView *context);
+ virtual LLString getControlName() const;
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+ virtual void setValue(const LLSD& value);
+ const child_list_t* getChildList() const { return &mChildList; }
+
+protected:
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+
+ LLView* childrenHandleKey(KEY key, MASK mask);
+ LLView* childrenHandleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType type,
+ void* data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ LLView* childrenHandleHover(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMouseUp(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleMouseDown(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleDoubleClick(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleScrollWheel(S32 x, S32 y, S32 clicks);
+ LLView* childrenHandleRightMouseDown(S32 x, S32 y, MASK mask);
+ LLView* childrenHandleRightMouseUp(S32 x, S32 y, MASK mask);
+
+ typedef std::map<LLString, LLSimpleListener*> dispatch_list_t;
+ dispatch_list_t mDispatchList;
+
+protected:
+ typedef std::map<LLString, LLControlBase*> control_map_t;
+ control_map_t mFloaterControls;
+
+ LLString mControlName;
+ friend class LLUICtrlFactory;
+};
+
+
+
+
+class LLCompareByTabOrder
+{
+public:
+ LLCompareByTabOrder(LLView::child_tab_order_t order);
+ virtual ~LLCompareByTabOrder() {}
+ bool operator() (const LLView* const a, const LLView* const b) const;
+protected:
+ virtual bool compareTabOrders(const LLView::tab_order_t & a, const LLView::tab_order_t & b) const;
+ LLView::child_tab_order_t mTabOrder;
+};
+
+#endif
diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp
new file mode 100644
index 0000000000..24cc57e709
--- /dev/null
+++ b/indra/llui/llviewborder.cpp
@@ -0,0 +1,337 @@
+/**
+ * @file llviewborder.cpp
+ * @brief LLViewBorder base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A customizable decorative border. Does not interact with mouse events.
+
+#include "linden_common.h"
+
+#include "llviewborder.h"
+
+#include "llgl.h"
+#include "llui.h"
+#include "llimagegl.h"
+//#include "llviewerimagelist.h"
+#include "llcontrol.h"
+#include "llglheaders.h"
+#include "v2math.h"
+#include "llfocusmgr.h"
+
+LLViewBorder::LLViewBorder( const LLString& name, const LLRect& rect, EBevel bevel, EStyle style, S32 width )
+ :
+ LLView( name, rect, FALSE ),
+ mBevel( bevel ),
+ mStyle( style ),
+ mHighlightLight( LLUI::sColorsGroup->getColor( "DefaultHighlightLight" ) ),
+ mHighlightDark( LLUI::sColorsGroup->getColor( "DefaultHighlightDark" ) ),
+ mShadowLight( LLUI::sColorsGroup->getColor( "DefaultShadowLight" ) ),
+ mShadowDark( LLUI::sColorsGroup->getColor( "DefaultShadowDark" ) ),
+// mKeyboardFocusColor(LLUI::sColorsGroup->getColor( "FocusColor" ) ),
+ mBorderWidth( width ),
+ mTexture( NULL ),
+ mHasKeyboardFocus( FALSE )
+{
+ setFollowsAll();
+}
+
+// virtual
+BOOL LLViewBorder::isCtrl()
+{
+ return FALSE;
+}
+
+void LLViewBorder::setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light )
+{
+ mShadowDark = shadow_dark;
+ mHighlightLight = highlight_light;
+}
+
+void LLViewBorder::setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
+ const LLColor4& highlight_light, const LLColor4& highlight_dark )
+{
+ mShadowDark = shadow_dark;
+ mShadowLight = shadow_light;
+ mHighlightLight = highlight_light;
+ mHighlightDark = highlight_dark;
+}
+
+void LLViewBorder::setTexture( const LLUUID &image_id )
+{
+ mTexture = LLUI::sImageProvider->getUIImageByID(image_id);
+}
+
+
+void LLViewBorder::draw()
+{
+ if( getVisible() )
+ {
+ if( STYLE_LINE == mStyle )
+ {
+ if( 0 == mBorderWidth )
+ {
+ // no visible border
+ }
+ else
+ if( 1 == mBorderWidth )
+ {
+ drawOnePixelLines();
+ }
+ else
+ if( 2 == mBorderWidth )
+ {
+ drawTwoPixelLines();
+ }
+ else
+ {
+ llassert( FALSE ); // not implemented
+ }
+ }
+ else
+ if( STYLE_TEXTURE == mStyle )
+ {
+ if( mTexture )
+ {
+ drawTextures();
+ }
+ }
+
+ // draw the children
+ LLView::draw();
+ }
+}
+
+void LLViewBorder::drawOnePixelLines()
+{
+ LLGLSNoTexture uiNoTexture;
+
+ LLColor4 top_color = mHighlightLight;
+ LLColor4 bottom_color = mHighlightLight;
+ switch( mBevel )
+ {
+ case BEVEL_OUT:
+ top_color = mHighlightLight;
+ bottom_color = mShadowDark;
+ break;
+ case BEVEL_IN:
+ top_color = mShadowDark;
+ bottom_color = mHighlightLight;
+ break;
+ case BEVEL_NONE:
+ // use defaults
+ break;
+ default:
+ llassert(0);
+ }
+
+ if( mHasKeyboardFocus )
+ {
+ F32 lerp_amt = gFocusMgr.getFocusFlashAmt();
+ top_color = gFocusMgr.getFocusColor();
+ bottom_color = top_color;
+
+ LLUI::setLineWidth(lerp(1.f, 3.f, lerp_amt));
+ }
+
+ S32 left = 0;
+ S32 top = mRect.getHeight();
+ S32 right = mRect.getWidth();
+ S32 bottom = 0;
+
+ glColor4fv( top_color.mV );
+ gl_line_2d(left, bottom, left, top);
+ gl_line_2d(left, top, right, top);
+
+ glColor4fv( bottom_color.mV );
+ gl_line_2d(right, top, right, bottom);
+ gl_line_2d(left, bottom, right, bottom);
+
+ LLUI::setLineWidth(1.f);
+}
+
+void LLViewBorder::drawTwoPixelLines()
+{
+ LLGLSNoTexture no_texture;
+
+ LLColor4 focus_color = gFocusMgr.getFocusColor();
+
+ F32* top_in_color = mShadowDark.mV;
+ F32* top_out_color = mShadowDark.mV;
+ F32* bottom_in_color = mShadowDark.mV;
+ F32* bottom_out_color = mShadowDark.mV;
+ switch( mBevel )
+ {
+ case BEVEL_OUT:
+ top_in_color = mHighlightLight.mV;
+ top_out_color = mHighlightDark.mV;
+ bottom_in_color = mShadowLight.mV;
+ bottom_out_color = mShadowDark.mV;
+ break;
+ case BEVEL_IN:
+ top_in_color = mShadowDark.mV;
+ top_out_color = mShadowLight.mV;
+ bottom_in_color = mHighlightDark.mV;
+ bottom_out_color = mHighlightLight.mV;
+ break;
+ case BEVEL_BRIGHT:
+ top_in_color = mHighlightLight.mV;
+ top_out_color = mHighlightLight.mV;
+ bottom_in_color = mHighlightLight.mV;
+ bottom_out_color = mHighlightLight.mV;
+ break;
+ case BEVEL_NONE:
+ // use defaults
+ break;
+ default:
+ llassert(0);
+ }
+
+ if( mHasKeyboardFocus )
+ {
+ top_out_color = focus_color.mV;
+ bottom_out_color = focus_color.mV;
+ }
+
+ S32 left = 0;
+ S32 top = mRect.getHeight();
+ S32 right = mRect.getWidth();
+ S32 bottom = 0;
+
+ // draw borders
+ glColor3fv( top_out_color );
+ gl_line_2d(left, bottom, left, top-1);
+ gl_line_2d(left, top-1, right, top-1);
+
+ glColor3fv( top_in_color );
+ gl_line_2d(left+1, bottom+1, left+1, top-2);
+ gl_line_2d(left+1, top-2, right-1, top-2);
+
+ glColor3fv( bottom_out_color );
+ gl_line_2d(right-1, top-1, right-1, bottom);
+ gl_line_2d(left, bottom, right, bottom);
+
+ glColor3fv( bottom_in_color );
+ gl_line_2d(right-2, top-2, right-2, bottom+1);
+ gl_line_2d(left+1, bottom+1, right-1, bottom+1);
+}
+
+void LLViewBorder::drawTextures()
+{
+ LLGLSUIDefault gls_ui;
+
+ llassert( FALSE ); // TODO: finish implementing
+
+ glColor4fv(UI_VERTEX_COLOR.mV);
+
+ mTexture->bind();
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
+
+ drawTextureTrapezoid( 0.f, mBorderWidth, mRect.getWidth(), 0, 0 );
+ drawTextureTrapezoid( 90.f, mBorderWidth, mRect.getHeight(), (F32)mRect.getWidth(),0 );
+ drawTextureTrapezoid( 180.f, mBorderWidth, mRect.getWidth(), (F32)mRect.getWidth(),(F32)mRect.getHeight() );
+ drawTextureTrapezoid( 270.f, mBorderWidth, mRect.getHeight(), 0, (F32)mRect.getHeight() );
+}
+
+
+void LLViewBorder::drawTextureTrapezoid( F32 degrees, S32 width, S32 length, F32 start_x, F32 start_y )
+{
+ glPushMatrix();
+ {
+ glTranslatef(start_x, start_y, 0.f);
+ glRotatef( degrees, 0, 0, 1 );
+
+ glBegin(GL_QUADS);
+ {
+ // width, width /---------\ length-width, width //
+ // / \ //
+ // / \ //
+ // /---------------\ //
+ // 0,0 length, 0 //
+
+ glTexCoord2f( 0, 0 );
+ glVertex2i( 0, 0 );
+
+ glTexCoord2f( (GLfloat)length, 0 );
+ glVertex2i( length, 0 );
+
+ glTexCoord2f( (GLfloat)(length - width), (GLfloat)width );
+ glVertex2i( length - width, width );
+
+ glTexCoord2f( (GLfloat)width, (GLfloat)width );
+ glVertex2i( width, width );
+ }
+ glEnd();
+ }
+ glPopMatrix();
+}
+
+bool LLViewBorder::getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style)
+{
+ if (node->hasAttribute("bevel_style"))
+ {
+ LLString bevel_string;
+ node->getAttributeString("bevel_style", bevel_string);
+ LLString::toLower(bevel_string);
+
+ if (bevel_string == "none")
+ {
+ bevel_style = LLViewBorder::BEVEL_NONE;
+ }
+ else if (bevel_string == "in")
+ {
+ bevel_style = LLViewBorder::BEVEL_IN;
+ }
+ else if (bevel_string == "out")
+ {
+ bevel_style = LLViewBorder::BEVEL_OUT;
+ }
+ else if (bevel_string == "bright")
+ {
+ bevel_style = LLViewBorder::BEVEL_BRIGHT;
+ }
+ return true;
+ }
+ return false;
+}
+
+void LLViewBorder::setValue(const LLSD& val)
+{
+ setRect(LLRect(val));
+}
+
+EWidgetType LLViewBorder::getWidgetType() const
+{
+ return WIDGET_TYPE_VIEW_BORDER;
+}
+
+LLString LLViewBorder::getWidgetTag() const
+{
+ return LL_VIEW_BORDER_TAG;
+}
+
+// static
+LLView* LLViewBorder::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("view_border");
+ node->getAttributeString("name", name);
+
+ LLViewBorder::EBevel bevel_style = LLViewBorder::BEVEL_IN;
+ getBevelFromAttribute(node, bevel_style);
+
+ S32 border_thickness = 1;
+ node->getAttributeS32("border_thickness", border_thickness);
+
+ LLViewBorder* border = new LLViewBorder(name,
+ LLRect(),
+ bevel_style,
+ LLViewBorder::STYLE_LINE,
+ border_thickness);
+
+ border->initFromXML(node, parent);
+
+ return border;
+}
diff --git a/indra/llui/llviewborder.h b/indra/llui/llviewborder.h
new file mode 100644
index 0000000000..984eee6e60
--- /dev/null
+++ b/indra/llui/llviewborder.h
@@ -0,0 +1,77 @@
+/**
+ * @file llviewborder.h
+ * @brief LLViewBorder base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A customizable decorative border. Does not interact with mouse events.
+
+#ifndef LL_LLVIEWBORDER_H
+#define LL_LLVIEWBORDER_H
+
+#include "llview.h"
+#include "v4color.h"
+#include "lluuid.h"
+#include "llimagegl.h"
+#include "llxmlnode.h"
+
+class LLUUID;
+
+
+class LLViewBorder : public LLView
+{
+public:
+ enum EBevel { BEVEL_IN, BEVEL_OUT, BEVEL_BRIGHT, BEVEL_NONE };
+
+ enum EStyle { STYLE_LINE, STYLE_TEXTURE };
+
+ LLViewBorder( const LLString& name, const LLRect& rect, EBevel bevel = BEVEL_OUT, EStyle style = STYLE_LINE, S32 width = 1 );
+
+ virtual void setValue(const LLSD& val);
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL isCtrl();
+
+ // llview functionality
+ virtual void draw();
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+ static bool getBevelFromAttribute(LLXMLNodePtr node, LLViewBorder::EBevel& bevel_style);
+
+ void setBorderWidth(S32 width) { mBorderWidth = width; }
+ void setBevel(EBevel bevel) { mBevel = bevel; }
+ void setColors( const LLColor4& shadow_dark, const LLColor4& highlight_light );
+ void setColorsExtended( const LLColor4& shadow_light, const LLColor4& shadow_dark,
+ const LLColor4& highlight_light, const LLColor4& highlight_dark );
+ void setTexture( const LLUUID &image_id );
+
+ EBevel getBevel() const { return mBevel; }
+ EStyle getStyle() const { return mStyle; }
+ S32 getBorderWidth() const { return mBorderWidth; }
+
+ void setKeyboardFocusHighlight( BOOL b ) { mHasKeyboardFocus = b; }
+
+protected:
+ void drawOnePixelLines();
+ void drawTwoPixelLines();
+ void drawTextures();
+ void drawTextureTrapezoid( F32 degrees, S32 width, S32 length, F32 start_x, F32 start_y );
+
+protected:
+ EBevel mBevel;
+ EStyle mStyle;
+ LLColor4 mHighlightLight;
+ LLColor4 mHighlightDark;
+ LLColor4 mShadowLight;
+ LLColor4 mShadowDark;
+ LLColor4 mBackgroundColor;
+ S32 mBorderWidth;
+ LLPointer<LLImageGL> mTexture;
+ BOOL mHasKeyboardFocus;
+};
+
+#endif // LL_LLVIEWBORDER_H
+
diff --git a/indra/llui/llviewquery.cpp b/indra/llui/llviewquery.cpp
new file mode 100644
index 0000000000..416ca623bc
--- /dev/null
+++ b/indra/llui/llviewquery.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file llviewquery.cpp
+ * @brief Implementation of view query class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llview.h"
+#include "lluictrl.h"
+#include "llviewquery.h"
+
+void LLQuerySorter::operator() (LLView * parent, viewList_t &children) const {}
+
+filterResult_t LLNoLeavesFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(!(view->getChildList()->size() == 0), TRUE);
+}
+
+filterResult_t LLVisibleFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->getVisible(), view->getVisible());
+}
+filterResult_t LLEnabledFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->getEnabled(), view->getEnabled());
+}
+filterResult_t LLTabStopFilter::operator() (const LLView* const view, const viewList_t & children) const
+{
+ return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl* const>(view)->hasTabStop(),
+ view->canFocusChildren());
+}
+
+// LLViewQuery
+
+LLViewQuery::LLViewQuery(): mPreFilters(), mPostFilters(), mSorterp()
+{
+}
+
+void LLViewQuery::addPreFilter(const LLQueryFilter* prefilter) { mPreFilters.push_back(prefilter); }
+
+void LLViewQuery::addPostFilter(const LLQueryFilter* postfilter) { mPostFilters.push_back(postfilter); }
+
+const LLViewQuery::filterList_t & LLViewQuery::getPreFilters() const { return mPreFilters; }
+
+const LLViewQuery::filterList_t & LLViewQuery::getPostFilters() const { return mPostFilters; }
+
+void LLViewQuery::setSorter(const LLQuerySorter* sorterp) { mSorterp = sorterp; }
+const LLQuerySorter* LLViewQuery::getSorter() const { return mSorterp; }
+
+viewList_t LLViewQuery::run(LLView * view) const
+{
+ viewList_t result;
+
+ filterResult_t pre = runFilters(view, viewList_t(), mPreFilters);
+ if(!pre.first && !pre.second)
+ {
+ // skip post filters completely if we're not including ourselves or the children
+ return result;
+ }
+ if(pre.second)
+ {
+ // run filters on children
+ viewList_t filtered_children;
+ filterChildren(view, filtered_children);
+ filterResult_t post = runFilters(view, filtered_children, mPostFilters);
+ if(pre.first && post.first)
+ {
+ result.push_back(view);
+ }
+ if(post.second)
+ {
+ result.insert(result.end(), filtered_children.begin(), filtered_children.end());
+ }
+ }
+ else
+ {
+ if(pre.first)
+ {
+ result.push_back(view);
+ }
+ }
+ return result;
+}
+
+void LLViewQuery::filterChildren(LLView * view, viewList_t & filtered_children) const
+{
+ LLView::child_list_t views(*(view->getChildList()));
+ (*mSorterp)(view, views); // sort the children per the sorter
+ for(LLView::child_list_iter_t iter = views.begin();
+ iter != views.end();
+ iter++)
+ {
+ viewList_t indiv_children = this->run(*iter);
+ filtered_children.insert(filtered_children.end(), indiv_children.begin(), indiv_children.end());
+ }
+}
+
+filterResult_t LLViewQuery::runFilters(LLView * view, const viewList_t children, const filterList_t filters) const
+{
+ filterResult_t result = filterResult_t(TRUE, TRUE);
+ for(filterList_const_iter_t iter = filters.begin();
+ iter != filters.end();
+ iter++)
+ {
+ filterResult_t filtered = (**iter)(view, children);
+ result.first = result.first && filtered.first;
+ result.second = result.second && filtered.second;
+ }
+ return result;
+}
+
+class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>
+{
+ /*virtual*/ void operator() (LLView * parent, LLView::child_list_t &children) const
+ {
+ children.sort(LLCompareByTabOrder(parent->getCtrlOrder()));
+ }
+};
+
+LLCtrlQuery::LLCtrlQuery() :
+ LLViewQuery()
+{
+ setSorter(SortByTabOrder::getInstance());
+}
+
diff --git a/indra/llui/llviewquery.h b/indra/llui/llviewquery.h
new file mode 100644
index 0000000000..ba59965c59
--- /dev/null
+++ b/indra/llui/llviewquery.h
@@ -0,0 +1,89 @@
+/**
+ * @file llviewquery.h
+ * @brief Query algorithm for flattening and filtering the view hierarchy.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWQUERY_H
+#define LL_LLVIEWQUERY_H
+
+#include <list>
+
+#include "llmemory.h"
+
+class LLView;
+
+typedef std::list<LLView *> viewList_t;
+typedef std::pair<BOOL, BOOL> filterResult_t;
+
+// Abstract base class for all filters.
+class LLQueryFilter : public LLRefCount
+{
+public:
+ virtual filterResult_t operator() (const LLView* const view, const viewList_t & children) const =0;
+};
+
+class LLQuerySorter : public LLRefCount
+{
+public:
+ virtual void operator() (LLView * parent, viewList_t &children) const;
+};
+
+class LLNoLeavesFilter : public LLQueryFilter, public LLSingleton<LLNoLeavesFilter>
+{
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+};
+class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>
+{
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+};
+class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>
+{
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+};
+class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>
+{
+ /*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
+};
+
+// Algorithm for flattening
+class LLViewQuery
+{
+public:
+ typedef std::list<const LLQueryFilter*> filterList_t;
+ typedef filterList_t::iterator filterList_iter_t;
+ typedef filterList_t::const_iterator filterList_const_iter_t;
+
+ LLViewQuery();
+ virtual ~LLViewQuery() {}
+
+ void addPreFilter(const LLQueryFilter* prefilter);
+ void addPostFilter(const LLQueryFilter* postfilter);
+ const filterList_t & getPreFilters() const;
+ const filterList_t & getPostFilters() const;
+
+ void setSorter(const LLQuerySorter* sorter);
+ const LLQuerySorter* getSorter() const;
+
+ viewList_t run(LLView * view) const;
+ // syntactic sugar
+ viewList_t operator () (LLView * view) const { return run(view); }
+protected:
+ // override this method to provide iteration over other types of children
+ virtual void filterChildren(LLView * view, viewList_t & filtered_children) const;
+ filterResult_t runFilters(LLView * view, const viewList_t children, const filterList_t filters) const;
+protected:
+ filterList_t mPreFilters;
+ filterList_t mPostFilters;
+ const LLQuerySorter* mSorterp;
+};
+
+class LLCtrlQuery : public LLViewQuery
+{
+public:
+ LLCtrlQuery();
+};
+
+#endif
diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp
new file mode 100644
index 0000000000..3c82b28c74
--- /dev/null
+++ b/indra/llvfs/lldir.cpp
@@ -0,0 +1,461 @@
+/**
+ * @file lldir.cpp
+ * @brief implementation of directory utilities base class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if !LL_WINDOWS
+#include <sys/stat.h>
+#include <sys/types.h>
+#else
+#include <direct.h>
+#endif
+
+#include "lldir.h"
+#include "llerror.h"
+#include "lluuid.h"
+
+#if LL_WINDOWS
+#include "lldir_win32.h"
+LLDir_Win32 gDirUtil;
+#elif LL_DARWIN
+#include "lldir_mac.h"
+LLDir_Mac gDirUtil;
+#else
+#include "lldir_linux.h"
+LLDir_Linux gDirUtil;
+#endif
+
+LLDir *gDirUtilp = (LLDir *)&gDirUtil;
+
+LLDir::LLDir()
+: mAppName(""),
+ mExecutablePathAndName(""),
+ mExecutableFilename(""),
+ mExecutableDir(""),
+ mAppRODataDir(""),
+ mOSUserDir(""),
+ mOSUserAppDir(""),
+ mLindenUserDir(""),
+ mCAFile(""),
+ mTempDir(""),
+ mDirDelimiter("")
+{
+}
+
+LLDir::~LLDir()
+{
+}
+
+
+S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)
+{
+ S32 count = 0;
+ std::string filename;
+ std::string fullpath;
+ S32 result;
+ while (getNextFileInDir(dirname, mask, filename, FALSE))
+ {
+ if ((filename == ".") || (filename == ".."))
+ {
+ // skipping directory traversal filenames
+ count++;
+ continue;
+ }
+ fullpath = dirname;
+ fullpath += getDirDelimiter();
+ fullpath += filename;
+
+ S32 retry_count = 0;
+ while (retry_count < 5)
+ {
+ if (0 != LLFile::remove(fullpath.c_str()))
+ {
+ result = errno;
+ llwarns << "Problem removing " << fullpath << " - errorcode: "
+ << result << " attempt " << retry_count << llendl;
+ ms_sleep(1000);
+ }
+ else
+ {
+ if (retry_count)
+ {
+ llwarns << "Successfully removed " << fullpath << llendl;
+ }
+ break;
+ }
+ retry_count++;
+ }
+ count++;
+ }
+ return count;
+}
+
+const std::string LLDir::findFile(const std::string &filename,
+ const std::string searchPath1,
+ const std::string searchPath2,
+ const std::string searchPath3)
+{
+ std::vector<std::string> search_paths;
+ search_paths.push_back(searchPath1);
+ search_paths.push_back(searchPath2);
+ search_paths.push_back(searchPath3);
+
+ std::vector<std::string>::iterator search_path_iter;
+ for (search_path_iter = search_paths.begin();
+ search_path_iter != search_paths.end();
+ ++search_path_iter)
+ {
+ if (!search_path_iter->empty())
+ {
+ std::string filename_and_path = (*search_path_iter) + getDirDelimiter() + filename;
+ if (fileExists(filename_and_path))
+ {
+ return filename_and_path;
+ }
+ }
+ }
+ return "";
+}
+
+
+const std::string &LLDir::getExecutablePathAndName() const
+{
+ return mExecutablePathAndName;
+}
+
+const std::string &LLDir::getExecutableFilename() const
+{
+ return mExecutableFilename;
+}
+
+const std::string &LLDir::getExecutableDir() const
+{
+ return mExecutableDir;
+}
+
+const std::string &LLDir::getWorkingDir() const
+{
+ return mWorkingDir;
+}
+
+const std::string &LLDir::getAppName() const
+{
+ return mAppName;
+}
+
+const std::string &LLDir::getAppRODataDir() const
+{
+ return mAppRODataDir;
+}
+
+const std::string &LLDir::getOSUserDir() const
+{
+ return mOSUserDir;
+}
+
+const std::string &LLDir::getOSUserAppDir() const
+{
+ return mOSUserAppDir;
+}
+
+const std::string &LLDir::getLindenUserDir() const
+{
+ return mLindenUserDir;
+}
+
+const std::string &LLDir::getChatLogsDir() const
+{
+ return mChatLogsDir;
+}
+
+const std::string &LLDir::getPerAccountChatLogsDir() const
+{
+ return mPerAccountChatLogsDir;
+}
+
+const std::string &LLDir::getTempDir() const
+{
+ return mTempDir;
+}
+
+const std::string &LLDir::getCAFile() const
+{
+ return mCAFile;
+}
+
+const std::string &LLDir::getDirDelimiter() const
+{
+ return mDirDelimiter;
+}
+
+const std::string &LLDir::getSkinDir() const
+{
+ return mSkinDir;
+}
+
+std::string LLDir::getExpandedFilename(ELLPath location, const std::string &filename) const
+{
+ std::string prefix;
+ switch (location)
+ {
+ case LL_PATH_NONE:
+ // Do nothing
+ break;
+
+ case LL_PATH_APP_SETTINGS:
+ prefix = getAppRODataDir();
+ prefix += mDirDelimiter;
+ prefix += "app_settings";
+ break;
+
+ case LL_PATH_CHARACTER:
+ prefix = getAppRODataDir();
+ prefix += mDirDelimiter;
+ prefix += "character";
+ break;
+
+ case LL_PATH_MOTIONS:
+ prefix = getAppRODataDir();
+ prefix += mDirDelimiter;
+ prefix += "motions";
+ break;
+
+ case LL_PATH_HELP:
+ prefix = "help";
+ break;
+
+ case LL_PATH_CACHE:
+ if (getOSUserAppDir().empty())
+ {
+ prefix = "data";
+ }
+ else
+ {
+ prefix = getOSUserAppDir();
+ prefix += mDirDelimiter;
+ prefix += "cache";
+ }
+ break;
+
+ case LL_PATH_USER_SETTINGS:
+ prefix = getOSUserAppDir();
+ prefix += mDirDelimiter;
+ prefix += "user_settings";
+ break;
+
+ case LL_PATH_PER_SL_ACCOUNT:
+ prefix = getLindenUserDir();
+ break;
+
+ case LL_PATH_CHAT_LOGS:
+ prefix = getChatLogsDir();
+ break;
+
+ case LL_PATH_PER_ACCOUNT_CHAT_LOGS:
+ prefix = getPerAccountChatLogsDir();
+ break;
+
+ case LL_PATH_LOGS:
+ prefix = getOSUserAppDir();
+ prefix += mDirDelimiter;
+ prefix += "logs";
+ break;
+
+ case LL_PATH_TEMP:
+ prefix = getTempDir();
+ break;
+
+ case LL_PATH_TOP_SKIN:
+ prefix = getSkinDir();
+ break;
+
+ case LL_PATH_SKINS:
+ prefix = getAppRODataDir();
+ prefix += mDirDelimiter;
+ prefix += "skins";
+ break;
+
+ case LL_PATH_MOZILLA_PROFILE:
+ prefix = getOSUserAppDir();
+ prefix += mDirDelimiter;
+ prefix += "browser_profile";
+ break;
+
+ default:
+ llassert(0);
+ }
+
+ std::string expanded_filename;
+ if (!filename.empty())
+ {
+ if (!prefix.empty())
+ {
+ expanded_filename += prefix;
+ expanded_filename += mDirDelimiter;
+ expanded_filename += filename;
+ }
+ else
+ {
+ expanded_filename = filename;
+ }
+ }
+ else
+ if (!prefix.empty())
+ {
+ // Directory only, no file name.
+ expanded_filename = prefix;
+ }
+ else
+ {
+ expanded_filename.assign("");
+ }
+
+ //llinfos << "*** EXPANDED FILENAME: <" << mExpandedFilename << ">" << llendl;
+
+ return expanded_filename;
+}
+
+std::string LLDir::getTempFilename() const
+{
+ LLUUID random_uuid;
+ char uuid_str[64];
+
+ random_uuid.generate();
+ random_uuid.toString(uuid_str);
+
+ std::string temp_filename = getTempDir();
+ temp_filename += mDirDelimiter;
+ temp_filename += uuid_str;
+ temp_filename += ".tmp";
+
+ return temp_filename;
+}
+
+void LLDir::setLindenUserDir(const std::string &first, const std::string &last)
+{
+ // if both first and last aren't set, assume we're grabbing the cached dir
+ if (!first.empty() && !last.empty())
+ {
+ // some platforms have case-sensitive filesystems, so be
+ // utterly consistent with our firstname/lastname case.
+ LLString firstlower(first);
+ LLString::toLower(firstlower);
+ LLString lastlower(last);
+ LLString::toLower(lastlower);
+ mLindenUserDir = getOSUserAppDir();
+ mLindenUserDir += mDirDelimiter;
+ mLindenUserDir += firstlower.c_str();
+ mLindenUserDir += "_";
+ mLindenUserDir += lastlower.c_str();
+ }
+ else
+ {
+ llerrs << "Invalid name for LLDir::setLindenUserDir" << llendl;
+ }
+
+ dumpCurrentDirectories();
+}
+
+void LLDir::setChatLogsDir(const std::string &path)
+{
+ if (!path.empty() )
+ {
+ mChatLogsDir = path;
+ }
+ else
+ {
+ llwarns << "Invalid name for LLDir::setChatLogsDir" << llendl;
+ }
+}
+
+void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last)
+{
+ // if both first and last aren't set, assume we're grabbing the cached dir
+ if (!first.empty() && !last.empty())
+ {
+ // some platforms have case-sensitive filesystems, so be
+ // utterly consistent with our firstname/lastname case.
+ LLString firstlower(first);
+ LLString::toLower(firstlower);
+ LLString lastlower(last);
+ LLString::toLower(lastlower);
+ mPerAccountChatLogsDir = getChatLogsDir();
+ mPerAccountChatLogsDir += mDirDelimiter;
+ mPerAccountChatLogsDir += firstlower.c_str();
+ mPerAccountChatLogsDir += "_";
+ mPerAccountChatLogsDir += lastlower.c_str();
+ }
+ else
+ {
+ llwarns << "Invalid name for LLDir::setPerAccountChatLogsDir" << llendl;
+ }
+}
+
+void LLDir::setSkinFolder(const std::string &skin_folder)
+{
+ mSkinDir = getAppRODataDir();
+ mSkinDir += mDirDelimiter;
+ mSkinDir += "skins";
+ mSkinDir += mDirDelimiter;
+ mSkinDir += skin_folder;
+}
+
+void LLDir::dumpCurrentDirectories()
+{
+ llinfos << "Current Directories:" << llendl;
+
+ llinfos << " CurPath: " << getCurPath() << llendl;
+ llinfos << " AppName: " << getAppName() << llendl;
+ llinfos << " ExecutableFilename: " << getExecutableFilename() << llendl;
+ llinfos << " ExecutableDir: " << getExecutableDir() << llendl;
+ llinfos << " ExecutablePathAndName: " << getExecutablePathAndName() << llendl;
+ llinfos << " WorkingDir: " << getWorkingDir() << llendl;
+ llinfos << " AppRODataDir: " << getAppRODataDir() << llendl;
+ llinfos << " OSUserDir: " << getOSUserDir() << llendl;
+ llinfos << " OSUserAppDir: " << getOSUserAppDir() << llendl;
+ llinfos << " LindenUserDir: " << getLindenUserDir() << llendl;
+ llinfos << " TempDir: " << getTempDir() << llendl;
+ llinfos << " CAFile: " << getCAFile() << llendl;
+ llinfos << " SkinDir: " << getSkinDir() << llendl;
+}
+
+
+void dir_exists_or_crash(const std::string &dir_name)
+{
+#if LL_WINDOWS
+ // *FIX: lame - it doesn't do the same thing on windows. not so
+ // important since we don't deploy simulator to windows boxes.
+ LLFile::mkdir(dir_name.c_str(), 0700);
+#else
+ struct stat dir_stat;
+ if(0 != LLFile::stat(dir_name.c_str(), &dir_stat))
+ {
+ S32 stat_rv = errno;
+ if(ENOENT == stat_rv)
+ {
+ if(0 != LLFile::mkdir(dir_name.c_str(), 0700)) // octal
+ {
+ llerrs << "Unable to create directory: " << dir_name << llendl;
+ }
+ }
+ else
+ {
+ llerrs << "Unable to stat: " << dir_name << " errno = " << stat_rv
+ << llendl;
+ }
+ }
+ else
+ {
+ // data_dir exists, make sure it's a directory.
+ if(!S_ISDIR(dir_stat.st_mode))
+ {
+ llerrs << "Data directory collision: " << dir_name << llendl;
+ }
+ }
+#endif
+}
diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h
new file mode 100644
index 0000000000..710dcd1ae3
--- /dev/null
+++ b/indra/llvfs/lldir.h
@@ -0,0 +1,102 @@
+/**
+ * @file lldir.h
+ * @brief Definition of directory utilities class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDIR_H
+#define LL_LLDIR_H
+
+typedef enum ELLPath
+{
+ LL_PATH_NONE = 0,
+ LL_PATH_USER_SETTINGS = 1,
+ LL_PATH_APP_SETTINGS = 2,
+ LL_PATH_PER_SL_ACCOUNT = 3,
+ LL_PATH_CACHE = 4,
+ LL_PATH_CHARACTER = 5,
+ LL_PATH_MOTIONS = 6,
+ LL_PATH_HELP = 7,
+ LL_PATH_LOGS = 8,
+ LL_PATH_TEMP = 9,
+ LL_PATH_SKINS = 10,
+ LL_PATH_TOP_SKIN = 11,
+ LL_PATH_CHAT_LOGS = 12,
+ LL_PATH_PER_ACCOUNT_CHAT_LOGS = 13,
+ LL_PATH_MOZILLA_PROFILE = 14,
+ LL_PATH_COUNT = 15
+} ELLPath;
+
+
+class LLDir
+{
+ public:
+ LLDir();
+ virtual ~LLDir();
+
+ virtual void initAppDirs(const std::string &app_name) = 0;
+ public:
+ virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
+
+// pure virtual functions
+ virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask) = 0;
+ virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap) = 0;
+ virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname) = 0;
+ virtual std::string getCurPath() = 0;
+ virtual BOOL fileExists(const std::string &filename) = 0;
+
+ const std::string findFile(const std::string &filename, const std::string searchPath1 = "", const std::string searchPath2 = "", const std::string searchPath3 = "");
+ const std::string &getExecutablePathAndName() const; // Full pathname of the executable
+ const std::string &getAppName() const; // install directory under progams/ ie "SecondLife"
+ const std::string &getExecutableDir() const; // Directory where the executable is located
+ const std::string &getExecutableFilename() const;// Filename of .exe
+ const std::string &getWorkingDir() const; // Current working directory
+ const std::string &getAppRODataDir() const; // Location of read-only data files
+ const std::string &getOSUserDir() const; // Location of the os-specific user dir
+ const std::string &getOSUserAppDir() const; // Location of the os-specific user app dir
+ const std::string &getLindenUserDir() const; // Location of the Linden user dir.
+ const std::string &getChatLogsDir() const; // Location of the chat logs dir.
+ const std::string &getPerAccountChatLogsDir() const; // Location of the per account chat logs dir.
+ const std::string &getTempDir() const; // Common temporary directory
+ const std::string &getCAFile() const; // File containing TLS certificate authorities
+ const std::string &getDirDelimiter() const; // directory separator for platform (ie. '\' or '/' or ':')
+ const std::string &getSkinDir() const; // User-specified skin folder.
+
+ // Expanded filename
+ std::string getExpandedFilename(ELLPath location, const std::string &filename) const;
+
+ // random filename in common temporary directory
+ std::string getTempFilename() const;
+
+ virtual void setChatLogsDir(const std::string &path); // Set the chat logs dir to this user's dir
+ virtual void setPerAccountChatLogsDir(const std::string &first, const std::string &last); // Set the per user chat log directory.
+ virtual void setLindenUserDir(const std::string &first, const std::string &last); // Set the linden user dir to this user's dir
+ virtual void setSkinFolder(const std::string &skin_folder);
+
+ virtual void dumpCurrentDirectories();
+
+protected:
+ std::string mAppName; // install directory under progams/ ie "SecondLife"
+ std::string mExecutablePathAndName; // full path + Filename of .exe
+ std::string mExecutableFilename; // Filename of .exe
+ std::string mExecutableDir; // Location of executable
+ std::string mWorkingDir; // Current working directory
+ std::string mAppRODataDir; // Location for static app data
+ std::string mOSUserDir; // OS Specific user directory
+ std::string mOSUserAppDir; // OS Specific user app directory
+ std::string mLindenUserDir; // Location for Linden user-specific data
+ std::string mPerAccountChatLogsDir; // Location for chat logs.
+ std::string mChatLogsDir; // Location for chat logs.
+ std::string mCAFile; // Location of the TLS certificate authority PEM file.
+ std::string mTempDir;
+ std::string mDirDelimiter;
+ std::string mSkinDir; // Location for u ser-specified skin info.
+};
+
+void dir_exists_or_crash(const std::string &dir_name);
+
+extern LLDir *gDirUtilp;
+
+#endif // LL_LLDIR_H
diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp
new file mode 100644
index 0000000000..6e50f9f239
--- /dev/null
+++ b/indra/llvfs/lldir_linux.cpp
@@ -0,0 +1,329 @@
+/**
+ * @file lldir_linux.cpp
+ * @brief Implementation of directory utilities for linux
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lldir_linux.h"
+#include "llerror.h"
+#include "llrand.h" // for gLindenLabRandomNumber
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glob.h>
+#include <pwd.h>
+
+
+static std::string getCurrentUserHome(char* fallback)
+{
+ const uid_t uid = getuid();
+ struct passwd *pw;
+ char *result_cstr = fallback;
+
+ pw = getpwuid(uid);
+ if ((pw != NULL) && (pw->pw_dir != NULL))
+ {
+ result_cstr = (char*) pw->pw_dir;
+ }
+ else
+ {
+ llinfos << "Couldn't detect home directory from passwd - trying $HOME" << llendl;
+ const char *const home_env = getenv("HOME");
+ if (home_env)
+ {
+ result_cstr = (char*) home_env;
+ }
+ else
+ {
+ llwarns << "Couldn't detect home directory! Falling back to " << fallback << llendl;
+ }
+ }
+
+ return std::string(result_cstr);
+}
+
+
+LLDir_Linux::LLDir_Linux()
+{
+ mDirDelimiter = "/";
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = -1;
+ mDirp = NULL;
+
+ char tmp_str[LL_MAX_PATH];
+ getcwd(tmp_str, LL_MAX_PATH);
+
+ mExecutableFilename = "";
+ mExecutablePathAndName = "";
+ mExecutableDir = tmp_str;
+ mWorkingDir = tmp_str;
+ mAppRODataDir = tmp_str;
+ mOSUserDir = getCurrentUserHome(tmp_str);
+ mOSUserAppDir = "";
+ mLindenUserDir = tmp_str;
+
+ char path [32];
+
+ // !!! FIXME: /proc/%d/exe doesn't work on FreeBSD.
+ sprintf (path, "/proc/%d/exe", (int) getpid ());
+ int rc = readlink (path, tmp_str, sizeof (tmp_str)-1);
+ if ( (rc != -1) && (rc <= ((int) sizeof (tmp_str)-1)) )
+ {
+ tmp_str[rc] = '\0'; //readlink() doesn't 0-terminate the buffer
+ mExecutablePathAndName = tmp_str;
+ char *path_end;
+ if ((path_end = strrchr(tmp_str,'/')))
+ {
+ *path_end = '\0';
+ mExecutableDir = tmp_str;
+ mWorkingDir = tmp_str;
+ mExecutableFilename = path_end+1;
+ }
+ else
+ {
+ mExecutableFilename = tmp_str;
+ }
+ }
+
+ // !!! FIXME: don't use /tmp, use $HOME/.secondlife/tmp or something.
+ mTempDir = "/tmp";
+}
+
+LLDir_Linux::~LLDir_Linux()
+{
+}
+
+// Implementation
+
+
+void LLDir_Linux::initAppDirs(const std::string &app_name)
+{
+ mAppName = app_name;
+
+ LLString upper_app_name(app_name);
+ LLString::toUpper(upper_app_name);
+
+ char* app_home_env = getenv((upper_app_name + "_USER_DIR").c_str());
+ if (app_home_env)
+ {
+ // user has specified own userappdir i.e. $SECONDLIFE_USER_DIR
+ mOSUserAppDir = app_home_env;
+ }
+ else
+ {
+ // traditionally on unixoids, MyApp gets ~/.myapp dir for data
+ mOSUserAppDir = mOSUserDir;
+ mOSUserAppDir += "/";
+ mOSUserAppDir += ".";
+ LLString lower_app_name(app_name);
+ LLString::toLower(lower_app_name);
+ mOSUserAppDir += lower_app_name;
+ }
+
+ // create any directories we expect to write to.
+
+ int res = LLFile::mkdir(mOSUserAppDir.c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl;
+ llwarns << "Default to base dir" << mOSUserDir << llendl;
+ mOSUserAppDir = mOSUserDir;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_MOZILLA_PROFILE dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl;
+ }
+ }
+
+ mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
+}
+
+U32 LLDir_Linux::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+ U32 file_count = 0;
+ glob_t g;
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ file_count = g.gl_pathc;
+
+ globfree(&g);
+ }
+
+ return (file_count);
+}
+
+// get the next file in the directory
+// automatically wrap if we've hit the end
+BOOL LLDir_Linux::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
+{
+ glob_t g;
+ BOOL result = FALSE;
+ fname = "";
+
+ if(!(dirname == mCurrentDir))
+ {
+ // different dir specified, close old search
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = -1;
+ mCurrentDir = dirname;
+ }
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ if(g.gl_pathc > 0)
+ {
+ if((int)g.gl_pathc != mCurrentDirCount)
+ {
+ // Number of matches has changed since the last search, meaning a file has been added or deleted.
+ // Reset the index.
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = g.gl_pathc;
+ }
+
+ mCurrentDirIndex++;
+
+ if((mCurrentDirIndex >= (int)g.gl_pathc) && wrap)
+ {
+ mCurrentDirIndex = 0;
+ }
+
+ if(mCurrentDirIndex < (int)g.gl_pathc)
+ {
+// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl;
+
+ // The API wants just the filename, not the full path.
+ //fname = g.gl_pathv[mCurrentDirIndex];
+
+ char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/');
+
+ if(s == NULL)
+ s = g.gl_pathv[mCurrentDirIndex];
+ else if(s[0] == '/')
+ s++;
+
+ fname = s;
+
+ result = TRUE;
+ }
+ }
+
+ globfree(&g);
+ }
+
+ return(result);
+}
+
+
+// get a random file in the directory
+// automatically wrap if we've hit the end
+void LLDir_Linux::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
+{
+ U32 num_files;
+ U32 which_file;
+ DIR *dirp;
+ dirent *entryp = NULL;
+
+ fname = "";
+
+ num_files = countFilesInDir(dirname,mask);
+ if (!num_files)
+ {
+ return;
+ }
+
+ which_file = gLindenLabRandomNumber.llrand() % num_files;
+
+// llinfos << "Random select file #" << which_file << llendl;
+
+ // which_file now indicates the (zero-based) index to which file to play
+
+ if (!((dirp = opendir(dirname.c_str()))))
+ {
+ while (which_file--)
+ {
+ if (!((entryp = readdir(dirp))))
+ {
+ return;
+ }
+ }
+
+ if ((!which_file) && entryp)
+ {
+ fname = entryp->d_name;
+ }
+
+ closedir(dirp);
+ }
+}
+
+std::string LLDir_Linux::getCurPath()
+{
+ char tmp_str[LL_MAX_PATH];
+ getcwd(tmp_str, LL_MAX_PATH);
+ return tmp_str;
+}
+
+
+BOOL LLDir_Linux::fileExists(const std::string &filename)
+{
+ struct stat stat_data;
+ // Check the age of the file
+ // Now, we see if the files we've gathered are recent...
+ int res = stat(filename.c_str(), &stat_data);
+ if (!res)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h
new file mode 100644
index 0000000000..3e63e72303
--- /dev/null
+++ b/indra/llvfs/lldir_linux.h
@@ -0,0 +1,41 @@
+/**
+ * @file lldir_linux.h
+ * @brief Definition of directory utilities class for linux
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDIR_LINUX_H
+#define LL_LLDIR_LINUX_H
+
+#include "lldir.h"
+
+#include <stdio.h>
+#include <dirent.h>
+#include <errno.h>
+
+class LLDir_Linux : public LLDir
+{
+public:
+ LLDir_Linux();
+ virtual ~LLDir_Linux();
+
+ virtual void initAppDirs(const std::string &app_name);
+public:
+ virtual std::string getCurPath();
+ virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+ virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
+ virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname);
+ /*virtual*/ BOOL fileExists(const std::string &filename);
+
+private:
+ DIR *mDirp;
+ int mCurrentDirIndex;
+ int mCurrentDirCount;
+ std::string mCurrentDir;
+};
+
+#endif // LL_LLDIR_LINUX_H
+
+
diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp
new file mode 100644
index 0000000000..591241478d
--- /dev/null
+++ b/indra/llvfs/lldir_mac.cpp
@@ -0,0 +1,362 @@
+/**
+ * @file lldir_mac.cpp
+ * @brief Implementation of directory utilities for linux
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+
+#include "lldir_mac.h"
+#include "llerror.h"
+#include "llrand.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <glob.h>
+
+#include <Carbon/Carbon.h>
+
+// --------------------------------------------------------------------------------
+
+static OSStatus CFCreateDirectory(FSRef *parentRef, CFStringRef name, FSRef *newRef)
+{
+ OSStatus result = noErr;
+ HFSUniStr255 uniStr;
+
+ uniStr.length = CFStringGetLength(name);
+ CFStringGetCharacters(name, CFRangeMake(0, uniStr.length), uniStr.unicode);
+ result = FSMakeFSRefUnicode(parentRef, uniStr.length, uniStr.unicode, kTextEncodingMacRoman, newRef);
+ if (result != noErr)
+ {
+ result = FSCreateDirectoryUnicode(parentRef, uniStr.length, uniStr.unicode, 0, NULL, newRef, NULL, NULL);
+ }
+
+ return result;
+}
+
+// --------------------------------------------------------------------------------
+
+static void CFStringRefToLLString(CFStringRef stringRef, std::string &llString, bool releaseWhenDone)
+{
+ if (stringRef)
+ {
+ long bufferSize = CFStringGetLength(stringRef) + 1;
+ char* buffer = new char[bufferSize];
+ memset(buffer, 0, bufferSize);
+ if (CFStringGetCString(stringRef, buffer, bufferSize, kCFStringEncodingUTF8))
+ llString = buffer;
+ delete[] buffer;
+ if (releaseWhenDone)
+ CFRelease(stringRef);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+static void CFURLRefToLLString(CFURLRef urlRef, std::string &llString, bool releaseWhenDone)
+{
+ if (urlRef)
+ {
+ CFURLRef absoluteURLRef = CFURLCopyAbsoluteURL(urlRef);
+ if (absoluteURLRef)
+ {
+ CFStringRef stringRef = CFURLCopyFileSystemPath(absoluteURLRef, kCFURLPOSIXPathStyle);
+ CFStringRefToLLString(stringRef, llString, true);
+ CFRelease(absoluteURLRef);
+ }
+ if (releaseWhenDone)
+ CFRelease(urlRef);
+ }
+}
+
+// --------------------------------------------------------------------------------
+
+static void FSRefToLLString(FSRef *fsRef, std::string &llString)
+{
+ OSStatus error = noErr;
+ char path[MAX_PATH];
+
+ error = FSRefMakePath(fsRef, (UInt8*) path, sizeof(path));
+ if (error == noErr)
+ llString = path;
+}
+
+// --------------------------------------------------------------------------------
+
+LLDir_Mac::LLDir_Mac()
+{
+ mDirDelimiter = "/";
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = -1;
+
+ CFBundleRef mainBundleRef = NULL;
+ CFURLRef executableURLRef = NULL;
+ CFStringRef stringRef = NULL;
+ OSStatus error = noErr;
+ FSRef fileRef;
+ CFStringRef secondLifeString = CFSTR("SecondLife");
+
+ mainBundleRef = CFBundleGetMainBundle();
+
+ executableURLRef = CFBundleCopyExecutableURL(mainBundleRef);
+
+ if (executableURLRef != NULL)
+ {
+ // mExecutablePathAndName
+ CFURLRefToLLString(executableURLRef, mExecutablePathAndName, false);
+
+ // mExecutableFilename
+ stringRef = CFURLCopyLastPathComponent(executableURLRef);
+ CFStringRefToLLString(stringRef, mExecutableFilename, true);
+
+ // mExecutableDir
+ CFURLRef executableParentURLRef = CFURLCreateCopyDeletingLastPathComponent(NULL, executableURLRef);
+ CFURLRefToLLString(executableParentURLRef, mExecutableDir, true);
+
+ // mAppRODataDir
+ CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef);
+ CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true);
+
+ // mOSUserDir
+ error = FSFindFolder(kUserDomain, kApplicationSupportFolderType, true, &fileRef);
+ if (error == noErr)
+ {
+ FSRef newFileRef;
+
+ // Create the directory
+ error = CFCreateDirectory(&fileRef, secondLifeString, &newFileRef);
+ if (error == noErr)
+ {
+ // Save the full path to the folder
+ FSRefToLLString(&newFileRef, mOSUserDir);
+
+ // Create our sub-dirs
+ (void) CFCreateDirectory(&newFileRef, CFSTR("data"), NULL);
+ (void) CFCreateDirectory(&newFileRef, CFSTR("cache"), NULL);
+ (void) CFCreateDirectory(&newFileRef, CFSTR("logs"), NULL);
+ (void) CFCreateDirectory(&newFileRef, CFSTR("user_settings"), NULL);
+ (void) CFCreateDirectory(&newFileRef, CFSTR("browser_profile"), NULL);
+ }
+ }
+
+ // mOSUserAppDir
+ mOSUserAppDir = mOSUserDir;
+
+ // mTempDir
+ error = FSFindFolder(kOnAppropriateDisk, kTemporaryFolderType, true, &fileRef);
+ if (error == noErr)
+ {
+ FSRef tempRef;
+ error = CFCreateDirectory(&fileRef, secondLifeString, &tempRef);
+ if (error == noErr)
+ FSRefToLLString(&tempRef, mTempDir);
+ }
+
+ // Set the working dir to <bundle>/Contents/Resources
+ (void) chdir(mAppRODataDir.c_str());
+
+ // Canonically, since we set it here...
+ mWorkingDir = mAppRODataDir;
+
+ CFRelease(executableURLRef);
+ executableURLRef = NULL;
+ }
+}
+
+LLDir_Mac::~LLDir_Mac()
+{
+}
+
+// Implementation
+
+
+void LLDir_Mac::initAppDirs(const std::string &app_name)
+{
+ mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
+
+ //dumpCurrentDirectories();
+}
+
+U32 LLDir_Mac::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+ U32 file_count = 0;
+ glob_t g;
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ file_count = g.gl_pathc;
+
+ globfree(&g);
+ }
+
+ return (file_count);
+}
+
+// get the next file in the directory
+// automatically wrap if we've hit the end
+BOOL LLDir_Mac::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
+{
+ glob_t g;
+ BOOL result = FALSE;
+ fname = "";
+
+ if(!(dirname == mCurrentDir))
+ {
+ // different dir specified, close old search
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = -1;
+ mCurrentDir = dirname;
+ }
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ if(g.gl_pathc > 0)
+ {
+ if(g.gl_pathc != mCurrentDirCount)
+ {
+ // Number of matches has changed since the last search, meaning a file has been added or deleted.
+ // Reset the index.
+ mCurrentDirIndex = -1;
+ mCurrentDirCount = g.gl_pathc;
+ }
+
+ mCurrentDirIndex++;
+
+ if((mCurrentDirIndex >= g.gl_pathc) && wrap)
+ {
+ mCurrentDirIndex = 0;
+ }
+
+ if(mCurrentDirIndex < g.gl_pathc)
+ {
+// llinfos << "getNextFileInDir: returning number " << mCurrentDirIndex << ", path is " << g.gl_pathv[mCurrentDirIndex] << llendl;
+
+ // The API wants just the filename, not the full path.
+ //fname = g.gl_pathv[mCurrentDirIndex];
+
+ char *s = strrchr(g.gl_pathv[mCurrentDirIndex], '/');
+
+ if(s == NULL)
+ s = g.gl_pathv[mCurrentDirIndex];
+ else if(s[0] == '/')
+ s++;
+
+ fname = s;
+
+ result = TRUE;
+ }
+ }
+
+ globfree(&g);
+ }
+
+ return(result);
+}
+
+// get a random file in the directory
+void LLDir_Mac::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
+{
+ U32 which_file;
+ glob_t g;
+ fname = "";
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ if(g.gl_pathc > 0)
+ {
+
+ which_file = gLindenLabRandomNumber.llrand() % g.gl_pathc;
+
+// llinfos << "getRandomFileInDir: returning number " << which_file << ", path is " << g.gl_pathv[which_file] << llendl;
+ // The API wants just the filename, not the full path.
+ //fname = g.gl_pathv[which_file];
+
+ char *s = strrchr(g.gl_pathv[which_file], '/');
+
+ if(s == NULL)
+ s = g.gl_pathv[which_file];
+ else if(s[0] == '/')
+ s++;
+
+ fname = s;
+ }
+
+ globfree(&g);
+ }
+}
+
+S32 LLDir_Mac::deleteFilesInDir(const std::string &dirname, const std::string &mask)
+{
+ glob_t g;
+ S32 result = 0;
+
+ std::string tmp_str;
+ tmp_str = dirname;
+ tmp_str += mask;
+
+ if(glob(tmp_str.c_str(), GLOB_NOSORT, NULL, &g) == 0)
+ {
+ int i;
+
+ for(i = 0; i < g.gl_pathc; i++)
+ {
+// llinfos << "deleteFilesInDir: deleting number " << i << ", path is " << g.gl_pathv[i] << llendl;
+
+ if(unlink(g.gl_pathv[i]) != 0)
+ {
+ result = errno;
+
+ llwarns << "Problem removing " << g.gl_pathv[i] << " - errorcode: "
+ << result << llendl;
+ }
+ }
+
+ globfree(&g);
+ }
+
+ return(result);
+}
+
+std::string LLDir_Mac::getCurPath()
+{
+ char tmp_str[LL_MAX_PATH];
+ getcwd(tmp_str, LL_MAX_PATH);
+ return tmp_str;
+}
+
+
+
+BOOL LLDir_Mac::fileExists(const std::string &filename)
+{
+ struct stat stat_data;
+ // Check the age of the file
+ // Now, we see if the files we've gathered are recent...
+ int res = stat(filename.c_str(), &stat_data);
+ if (!res)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+#endif // LL_DARWIN
diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h
new file mode 100644
index 0000000000..24fb1ac583
--- /dev/null
+++ b/indra/llvfs/lldir_mac.h
@@ -0,0 +1,40 @@
+/**
+ * @file lldir_mac.h
+ * @brief Definition of directory utilities class for Mac OS X
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDIR_MAC_H
+#define LL_LLDIR_MAC_H
+
+#include "lldir.h"
+
+#include <stdio.h>
+#include <dirent.h>
+
+class LLDir_Mac : public LLDir
+{
+public:
+ LLDir_Mac();
+ virtual ~LLDir_Mac();
+
+ virtual void initAppDirs(const std::string &app_name);
+public:
+ virtual S32 deleteFilesInDir(const std::string &dirname, const std::string &mask);
+ virtual std::string getCurPath();
+ virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+ virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
+ virtual void getRandomFileInDir(const std::string &dirname, const std::string &ask, std::string &fname);
+ virtual BOOL fileExists(const std::string &filename);
+
+private:
+ int mCurrentDirIndex;
+ int mCurrentDirCount;
+ std::string mCurrentDir;
+};
+
+#endif // LL_LLDIR_MAC_H
+
+
diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp
new file mode 100644
index 0000000000..0c5b0ecf19
--- /dev/null
+++ b/indra/llvfs/lldir_win32.cpp
@@ -0,0 +1,382 @@
+/**
+ * @file lldir_win32.cpp
+ * @brief Implementation of directory utilities for windows
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_WINDOWS
+
+#include "linden_common.h"
+
+#include "lldir_win32.h"
+#include "llerror.h"
+#include "llrand.h" // for gLindenLabRandomNumber
+#include "shlobj.h"
+
+#include <direct.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// Utility stuff to get versions of the sh
+#define PACKVERSION(major,minor) MAKELONG(minor,major)
+DWORD GetDllVersion(LPCTSTR lpszDllName);
+
+LLDir_Win32::LLDir_Win32()
+{
+ mDirDelimiter = "\\";
+
+ WCHAR w_str[MAX_PATH];
+
+ // Application Data is where user settings go
+ SHGetSpecialFolderPath(NULL, w_str, CSIDL_APPDATA, TRUE);
+
+ mOSUserDir = utf16str_to_utf8str(llutf16string(w_str));
+
+ // Local Settings\Application Data is where cache files should
+ // go, they don't get copied to the server if the user moves his
+ // profile around on the network. JC
+ //
+ // TODO: patch the installer to remove old cache files on update, then
+ // enable this code.
+ //SHGetSpecialFolderPath(NULL, w_str, CSIDL_LOCAL_APPDATA, TRUE);
+ //mOSUserCacheDir = utf16str_to_utf8str(llutf16string(w_str));
+
+ if (GetTempPath(MAX_PATH, w_str))
+ {
+ if (wcslen(w_str))
+ {
+ w_str[wcslen(w_str)-1] = '\0'; // remove trailing slash
+ }
+ mTempDir = utf16str_to_utf8str(llutf16string(w_str));
+ }
+ else
+ {
+ mTempDir = mOSUserDir;
+ }
+
+// fprintf(stderr, "mTempDir = <%s>",mTempDir);
+
+#if 1
+ // Don't use the real app path for now, as we'll have to add parsing to detect if
+ // we're in a developer tree, which has a different structure from the installed product.
+
+ S32 size = GetModuleFileName(NULL, w_str, MAX_PATH);
+ if (size)
+ {
+ w_str[size] = '\0';
+ mExecutablePathAndName = utf16str_to_utf8str(llutf16string(w_str));
+ S32 path_end = mExecutablePathAndName.find_last_of('\\');
+ if (path_end != std::string::npos)
+ {
+ mExecutableDir = mExecutablePathAndName.substr(0, path_end);
+ mExecutableFilename = mExecutablePathAndName.substr(path_end+1, std::string::npos);
+ }
+ else
+ {
+ mExecutableFilename = mExecutablePathAndName;
+ }
+ GetCurrentDirectory(MAX_PATH, w_str);
+ mWorkingDir = utf16str_to_utf8str(llutf16string(w_str));
+
+ }
+ else
+ {
+ fprintf(stderr, "Couldn't get APP path, assuming current directory!");
+ GetCurrentDirectory(MAX_PATH, w_str);
+ mExecutableDir = utf16str_to_utf8str(llutf16string(w_str));
+ // Assume it's the current directory
+ }
+#else
+ GetCurrentDirectory(MAX_PATH, w_str);
+ mExecutableDir = utf16str_to_utf8str(llutf16string(w_str));
+#endif
+ if (strstr(mExecutableDir.c_str(), "indra\\newview"))
+ mAppRODataDir = getCurPath();
+ else
+ mAppRODataDir = mExecutableDir;
+}
+
+LLDir_Win32::~LLDir_Win32()
+{
+}
+
+// Implementation
+
+void LLDir_Win32::initAppDirs(const std::string &app_name)
+{
+ mAppName = app_name;
+ mOSUserAppDir = mOSUserDir;
+ mOSUserAppDir += "\\";
+ mOSUserAppDir += app_name;
+
+ int res = LLFile::mkdir(mOSUserAppDir.c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create app user dir " << mOSUserAppDir << llendl;
+ llwarns << "Default to base dir" << mOSUserDir << llendl;
+ mOSUserAppDir = mOSUserDir;
+ }
+ }
+ //dumpCurrentDirectories();
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_LOGS,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_LOGS dir " << getExpandedFilename(LL_PATH_LOGS,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_USER_SETTINGS,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_USER_SETTINGS dir " << getExpandedFilename(LL_PATH_USER_SETTINGS,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_CACHE,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_CACHE dir " << getExpandedFilename(LL_PATH_CACHE,"") << llendl;
+ }
+ }
+
+ res = LLFile::mkdir(getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"").c_str());
+ if (res == -1)
+ {
+ if (errno != EEXIST)
+ {
+ llwarns << "Couldn't create LL_PATH_MOZILLA_PROFILE dir " << getExpandedFilename(LL_PATH_MOZILLA_PROFILE,"") << llendl;
+ }
+ }
+
+ mCAFile = getExpandedFilename(LL_PATH_APP_SETTINGS, "CA.pem");
+}
+
+U32 LLDir_Win32::countFilesInDir(const std::string &dirname, const std::string &mask)
+{
+ HANDLE count_search_h;
+ U32 file_count;
+
+ file_count = 0;
+
+ WIN32_FIND_DATA FileData;
+
+ llutf16string pathname = utf8str_to_utf16str(dirname);
+ pathname += utf8str_to_utf16str(mask);
+
+ if ((count_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)
+ {
+ file_count++;
+
+ while (FindNextFile(count_search_h, &FileData))
+ {
+ file_count++;
+ }
+
+ FindClose(count_search_h);
+ }
+
+ return (file_count);
+}
+
+
+// get the next file in the directory
+// automatically wrap if we've hit the end
+BOOL LLDir_Win32::getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
+{
+ llutf16string dirnamew = utf8str_to_utf16str(dirname);
+ return getNextFileInDir(dirnamew, mask, fname, wrap);
+
+}
+
+BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap)
+{
+ WIN32_FIND_DATAW FileData;
+
+ fname = "";
+ llutf16string pathname = dirname;
+ pathname += utf8str_to_utf16str(mask);
+
+ if (pathname != mCurrentDir)
+ {
+ // different dir specified, close old search
+ if (mCurrentDir[0])
+ {
+ FindClose(mDirSearch_h);
+ }
+ mCurrentDir = pathname;
+
+ // and open new one
+ // Check error opening Directory structure
+ if ((mDirSearch_h = FindFirstFile(pathname.c_str(), &FileData)) == INVALID_HANDLE_VALUE)
+ {
+// llinfos << "Unable to locate first file" << llendl;
+ return(FALSE);
+ }
+ }
+ else // get next file in list
+ {
+ // Find next entry
+ if (!FindNextFile(mDirSearch_h, &FileData))
+ {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ {
+ // No more files, so reset to beginning of directory
+ FindClose(mDirSearch_h);
+ mCurrentDir[0] = NULL;
+
+ if (wrap)
+ {
+ return(getNextFileInDir(pathname,"",fname,TRUE));
+ }
+ else
+ {
+ fname[0] = 0;
+ return(FALSE);
+ }
+ }
+ else
+ {
+ // Error
+// llinfos << "Unable to locate next file" << llendl;
+ return(FALSE);
+ }
+ }
+ }
+
+ // convert from TCHAR to char
+ fname = utf16str_to_utf8str(FileData.cFileName);
+
+ // fname now first name in list
+ return(TRUE);
+}
+
+
+// get a random file in the directory
+// automatically wrap if we've hit the end
+void LLDir_Win32::getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname)
+{
+ U32 num_files;
+ U32 which_file;
+ HANDLE random_search_h;
+
+ fname = "";
+
+ llutf16string pathname = utf8str_to_utf16str(dirname);
+ pathname += utf8str_to_utf16str(mask);
+
+ WIN32_FIND_DATA FileData;
+ fname[0] = NULL;
+
+ num_files = countFilesInDir(dirname,mask);
+ if (!num_files)
+ {
+ return;
+ }
+
+ which_file = gLindenLabRandomNumber.llrand() % num_files;
+
+// llinfos << "Random select mp3 #" << which_file << llendl;
+
+ // which_file now indicates the (zero-based) index to which file to play
+
+ if ((random_search_h = FindFirstFile(pathname.c_str(), &FileData)) != INVALID_HANDLE_VALUE)
+ {
+ while (which_file--)
+ {
+ if (!FindNextFile(random_search_h, &FileData))
+ {
+ return;
+ }
+ }
+ FindClose(random_search_h);
+
+ fname = utf16str_to_utf8str(llutf16string(FileData.cFileName));
+ }
+}
+
+std::string LLDir_Win32::getCurPath()
+{
+ WCHAR w_str[MAX_PATH];
+ GetCurrentDirectory(MAX_PATH, w_str);
+
+ return utf16str_to_utf8str(llutf16string(w_str));
+}
+
+
+BOOL LLDir_Win32::fileExists(const std::string &filename)
+{
+ llstat stat_data;
+ // Check the age of the file
+ // Now, we see if the files we've gathered are recent...
+ int res = LLFile::stat(filename.c_str(), &stat_data);
+ if (!res)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+#if 0
+// Utility function to get version number of a DLL
+
+#define PACKVERSION(major,minor) MAKELONG(minor,major)
+
+DWORD GetDllVersion(LPCTSTR lpszDllName)
+{
+
+ HINSTANCE hinstDll;
+ DWORD dwVersion = 0;
+
+ hinstDll = LoadLibrary(lpszDllName);
+
+ if(hinstDll)
+ {
+ DLLGETVERSIONPROC pDllGetVersion;
+
+ pDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hinstDll, "DllGetVersion");
+
+/*Because some DLLs might not implement this function, you
+ must test for it explicitly. Depending on the particular
+ DLL, the lack of a DllGetVersion function can be a useful
+ indicator of the version.
+*/
+ if(pDllGetVersion)
+ {
+ DLLVERSIONINFO dvi;
+ HRESULT hr;
+
+ ZeroMemory(&dvi, sizeof(dvi));
+ dvi.cbSize = sizeof(dvi);
+
+ hr = (*pDllGetVersion)(&dvi);
+
+ if(SUCCEEDED(hr))
+ {
+ dwVersion = PACKVERSION(dvi.dwMajorVersion, dvi.dwMinorVersion);
+ }
+ }
+
+ FreeLibrary(hinstDll);
+ }
+ return dwVersion;
+}
+#endif
+
+#endif
+
+
diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h
new file mode 100644
index 0000000000..fbeeef2732
--- /dev/null
+++ b/indra/llvfs/lldir_win32.h
@@ -0,0 +1,37 @@
+/**
+ * @file lldir_win32.h
+ * @brief Definition of directory utilities class for windows
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDIR_WIN32_H
+#define LL_LLDIR_WIN32_H
+
+#include "lldir.h"
+
+class LLDir_Win32 : public LLDir
+{
+public:
+ LLDir_Win32();
+ virtual ~LLDir_Win32();
+
+ virtual void initAppDirs(const std::string &app_name);
+public:
+ virtual std::string getCurPath();
+ virtual U32 countFilesInDir(const std::string &dirname, const std::string &mask);
+ virtual BOOL getNextFileInDir(const std::string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
+ virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname);
+ /*virtual*/ BOOL fileExists(const std::string &filename);
+
+private:
+ BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap);
+
+ HANDLE mDirSearch_h;
+ llutf16string mCurrentDir;
+};
+
+#endif // LL_LLDIR_WIN32_H
+
+
diff --git a/indra/llvfs/lllfsthread.cpp b/indra/llvfs/lllfsthread.cpp
new file mode 100644
index 0000000000..57b4bc6d47
--- /dev/null
+++ b/indra/llvfs/lllfsthread.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file lllfsthread.cpp
+ * @brief LLLFSThread base class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+#include "lllfsthread.h"
+#include "llstl.h"
+#include "llapr.h"
+
+//============================================================================
+
+/*static*/ LLLFSThread* LLLFSThread::sLocal = NULL;
+
+//============================================================================
+// Run on MAIN thread
+//static
+void LLLFSThread::initClass(bool local_is_threaded, bool local_run_always)
+{
+ llassert(sLocal == NULL);
+ sLocal = new LLLFSThread(local_is_threaded, local_run_always);
+}
+
+//static
+S32 LLLFSThread::updateClass(U32 ms_elapsed)
+{
+ sLocal->update(ms_elapsed);
+ return sLocal->getPending();
+}
+
+//static
+void LLLFSThread::cleanupClass()
+{
+ sLocal->setQuitting();
+ while (sLocal->getPending())
+ {
+ sLocal->update(0);
+ }
+ delete sLocal;
+ sLocal = 0;
+}
+
+//----------------------------------------------------------------------------
+
+LLLFSThread::LLLFSThread(bool threaded, bool runalways) :
+ LLQueuedThread("LFS", threaded, runalways)
+{
+}
+
+LLLFSThread::~LLLFSThread()
+{
+ // ~LLQueuedThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+LLLFSThread::handle_t LLLFSThread::read(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW
+ Request* req = new Request(handle, priority, flags,
+ FILE_READ, filename,
+ buffer, offset, numbytes);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+S32 LLLFSThread::readImmediate(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
+ FILE_READ, filename,
+ buffer, offset, numbytes);
+
+ S32 res = addRequest(req) ? 1 : 0;
+ if (res == 0)
+ {
+ llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ }
+ else
+ {
+ llverify(waitForResult(handle, false) == true);
+ res = req->getBytesRead();
+ completeRequest(handle);
+ }
+ return res;
+}
+
+LLLFSThread::handle_t LLLFSThread::write(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, 0, flags,
+ FILE_WRITE, filename,
+ buffer, offset, numbytes);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLLFSThread::read called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+S32 LLLFSThread::writeImmediate(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0,
+ FILE_WRITE, filename,
+ buffer, offset, numbytes);
+
+ S32 res = addRequest(req) ? 1 : 0;
+ if (res == 0)
+ {
+ llerrs << "LLLFSThread::write called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ }
+ else
+ {
+ llverify(waitForResult(handle, false) == true);
+ res = req->getBytesRead();
+ completeRequest(handle);
+ }
+ return res;
+}
+
+
+LLLFSThread::handle_t LLLFSThread::rename(const LLString& filename, const LLString& newname, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ LLString* new_name_str = new LLString(newname); // deleted with Request
+ Request* req = new Request(handle, 0, flags,
+ FILE_RENAME, filename,
+ (U8*)new_name_str, 0, 0);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLLFSThread::rename called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+LLLFSThread::handle_t LLLFSThread::remove(const LLString& filename, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, 0, flags,
+ FILE_RENAME, filename,
+ NULL, 0, 0);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLLFSThread::remove called after LLLFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+bool LLLFSThread::processRequest(QueuedRequest* qreq)
+{
+ Request *req = (Request*)qreq;
+
+ bool complete = req->processIO();
+
+ return complete;
+}
+
+//============================================================================
+
+LLLFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
+ operation_t op, const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes) :
+ QueuedRequest(handle, priority, flags),
+ mOperation(op),
+ mFileName(filename),
+ mBuffer(buffer),
+ mOffset(offset),
+ mBytes(numbytes),
+ mBytesRead(0)
+{
+ llassert(mBuffer);
+
+ if (numbytes <= 0 && mOperation != FILE_RENAME && mOperation != FILE_REMOVE)
+ {
+ llwarns << "LLLFSThread: Request with numbytes = " << numbytes << llendl;
+ }
+}
+
+void LLLFSThread::Request::finishRequest()
+{
+}
+
+void LLLFSThread::Request::deleteRequest()
+{
+ if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
+ {
+ llerrs << "Attempt to delete a queued LLLFSThread::Request!" << llendl;
+ }
+ if (mOperation == FILE_WRITE)
+ {
+ if (mFlags & AUTO_DELETE)
+ {
+ delete mBuffer;
+ }
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ LLString* new_name = (LLString*)mBuffer;
+ delete new_name;
+ }
+ LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+bool LLLFSThread::Request::processIO()
+{
+ bool complete = false;
+ if (mOperation == FILE_READ)
+ {
+ llassert(mOffset >= 0);
+ apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_RB);
+ if (!filep)
+ {
+ llwarns << "LLLFS: Unable to read file: " << mFileName << llendl;
+ mBytesRead = 0; // fail
+ return true;
+ }
+ if (mOffset < 0)
+ ll_apr_file_seek(filep, APR_END, 0);
+ else
+ ll_apr_file_seek(filep, APR_SET, mOffset);
+ mBytesRead = ll_apr_file_read(filep, mBuffer, mBytes );
+ apr_file_close(filep);
+ complete = true;
+ //llinfos << llformat("LLLFSThread::READ '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
+ }
+ else if (mOperation == FILE_WRITE)
+ {
+ apr_file_t* filep = ll_apr_file_open(mFileName, LL_APR_WB);
+ if (!filep)
+ {
+ llwarns << "LLLFS: Unable to write file: " << mFileName << llendl;
+ mBytesRead = 0; // fail
+ return true;
+ }
+ if (mOffset < 0)
+ ll_apr_file_seek(filep, APR_END, 0);
+ else
+ ll_apr_file_seek(filep, APR_SET, mOffset);
+ mBytesRead = ll_apr_file_write(filep, mBuffer, mBytes );
+ complete = true;
+ apr_file_close(filep);
+ //llinfos << llformat("LLLFSThread::WRITE '%s': %d bytes",mFileName.c_str(),mBytesRead) << llendl;
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ LLString* new_name = (LLString*)mBuffer;
+ ll_apr_file_rename(mFileName, *new_name);
+ complete = true;
+ //llinfos << llformat("LLLFSThread::RENAME '%s': '%s'",mFileName.c_str(),new_name->c_str()) << llendl;
+ }
+ else if (mOperation == FILE_REMOVE)
+ {
+ ll_apr_file_remove(mFileName);
+ complete = true;
+ //llinfos << llformat("LLLFSThread::REMOVE '%s'",mFileName.c_str()) << llendl;
+ }
+ else
+ {
+ llerrs << llformat("LLLFSThread::unknown operation: %d", mOperation) << llendl;
+ }
+ return complete;
+}
+
+//============================================================================
diff --git a/indra/llvfs/lllfsthread.h b/indra/llvfs/lllfsthread.h
new file mode 100644
index 0000000000..a55a2668b3
--- /dev/null
+++ b/indra/llvfs/lllfsthread.h
@@ -0,0 +1,119 @@
+/**
+ * @file lllfsthread.h
+ * @brief LLLFSThread base class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLFSTHREAD_H
+#define LL_LLLFSTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llapr.h"
+
+#include "llqueuedthread.h"
+
+//============================================================================
+// Threaded Local File System
+//============================================================================
+
+class LLLFSThread : public LLQueuedThread
+{
+ //------------------------------------------------------------------------
+public:
+ enum operation_t {
+ FILE_READ,
+ FILE_WRITE,
+ FILE_RENAME,
+ FILE_REMOVE
+ };
+
+ //------------------------------------------------------------------------
+public:
+
+ class Request : public QueuedRequest
+ {
+ protected:
+ ~Request() {}; // use deleteRequest()
+
+ public:
+ Request(handle_t handle, U32 priority, U32 flags,
+ operation_t op, const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes);
+
+ S32 getBytes()
+ {
+ return mBytes;
+ }
+ S32 getBytesRead()
+ {
+ return mBytesRead;
+ }
+ S32 getOperation()
+ {
+ return mOperation;
+ }
+ U8* getBuffer()
+ {
+ return mBuffer;
+ }
+ const LLString& getFilename()
+ {
+ return mFileName;
+ }
+
+ /*virtual*/ void finishRequest();
+ /*virtual*/ void deleteRequest();
+
+ bool processIO();
+
+ private:
+ operation_t mOperation;
+
+ LLString mFileName;
+
+ U8* mBuffer; // dest for reads, source for writes, new UUID for rename
+ S32 mOffset; // offset into file, -1 = append (WRITE only)
+ S32 mBytes; // bytes to read from file, -1 = all
+ S32 mBytesRead; // bytes read from file
+ };
+
+ //------------------------------------------------------------------------
+public:
+ LLLFSThread(bool threaded = TRUE, bool runalways = TRUE);
+ ~LLLFSThread();
+
+ // Return a Request handle
+ handle_t read(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
+ handle_t write(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes, U32 flags = 0);
+ handle_t rename(const LLString& filename, const LLString& newname, U32 flags = 0);
+ handle_t remove(const LLString& filename, U32 flags = 0);
+
+ // Return number of bytes read
+ S32 readImmediate(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes);
+ S32 writeImmediate(const LLString& filename,
+ U8* buffer, S32 offset, S32 numbytes);
+
+ static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
+ static S32 updateClass(U32 ms_elapsed);
+ static void cleanupClass(); // Delete sLocal
+
+protected:
+ /*virtual*/ bool processRequest(QueuedRequest* req);
+
+public:
+ static LLLFSThread* sLocal; // Default local file thread
+};
+
+//============================================================================
+
+
+#endif // LL_LLLFSTHREAD_H
diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp
new file mode 100644
index 0000000000..36ac569d02
--- /dev/null
+++ b/indra/llvfs/llvfile.cpp
@@ -0,0 +1,415 @@
+/**
+ * @file llvfile.cpp
+ * @brief Implementation of virtual file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llvfile.h"
+
+#include "llerror.h"
+#include "llthread.h"
+#include "llvfs.h"
+
+const S32 LLVFile::READ = 0x00000001;
+const S32 LLVFile::WRITE = 0x00000002;
+const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE
+const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE
+
+//----------------------------------------------------------------------------
+LLVFSThread* LLVFile::sVFSThread = NULL;
+BOOL LLVFile::sAllocdVFSThread = FALSE;
+BOOL LLVFile::ALLOW_ASYNC = TRUE;
+//----------------------------------------------------------------------------
+
+//============================================================================
+
+LLVFile::LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode)
+{
+ mFileType = file_type;
+
+ mFileID = file_id;
+ mPosition = 0;
+ mMode = mode;
+ mVFS = vfs;
+
+ mBytesRead = 0;
+ mHandle = LLVFSThread::nullHandle();
+ mPriority = 128.f;
+
+ mVFS->incLock(mFileID, mFileType, VFSLOCK_OPEN);
+}
+
+LLVFile::~LLVFile()
+{
+ if (!isReadComplete())
+ {
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ if (!(mMode & LLVFile::WRITE))
+ {
+ // llwarns << "Destroying LLVFile with pending async read/write, aborting..." << llendl;
+ sVFSThread->abortRequest(mHandle, LLVFSThread::AUTO_COMPLETE);
+ }
+ else // WRITE
+ {
+ sVFSThread->setFlags(mHandle, LLVFSThread::AUTO_COMPLETE);
+ }
+ }
+ }
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
+}
+
+BOOL LLVFile::read(U8 *buffer, S32 bytes, BOOL async, F32 priority)
+{
+ if (! (mMode & READ))
+ {
+ llwarns << "Attempt to read from file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
+ return FALSE;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ llwarns << "Attempt to read from vfile object " << mFileID << " with pending async operation" << llendl;
+ return FALSE;
+ }
+ mPriority = priority;
+
+ BOOL success = TRUE;
+
+ // We can't do a read while there are pending async writes
+ waitForLock(VFSLOCK_APPEND);
+
+ // FIXME
+ if (async)
+ {
+ mHandle = sVFSThread->read(mVFS, mFileID, mFileType, buffer, mPosition, bytes, threadPri());
+ }
+ else
+ {
+ // We can't do a read while there are pending async writes on this file
+ mBytesRead = sVFSThread->readImmediate(mVFS, mFileID, mFileType, buffer, mPosition, bytes);
+ mPosition += mBytesRead;
+ if (! mBytesRead)
+ {
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
+//static
+U8* LLVFile::readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read)
+{
+ U8 *data;
+ LLVFile file(vfs, uuid, type, LLVFile::READ);
+ S32 file_size = file.getSize();
+ if (file_size == 0)
+ {
+ // File is empty.
+ data = NULL;
+ }
+ else
+ {
+ data = new U8[file_size];
+ file.read(data, file_size);
+
+ if (file.getLastBytesRead() != (S32)file_size)
+ {
+ delete[] data;
+ data = NULL;
+ file_size = 0;
+ }
+ }
+ if (bytes_read)
+ {
+ *bytes_read = file_size;
+ }
+ return data;
+}
+
+void LLVFile::setReadPriority(const F32 priority)
+{
+ mPriority = priority;
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ sVFSThread->setPriority(mHandle, threadPri());
+ }
+}
+
+BOOL LLVFile::isReadComplete()
+{
+ BOOL res = TRUE;
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
+ LLVFSThread::status_t status = req->getStatus();
+ if (status == LLVFSThread::STATUS_COMPLETE)
+ {
+ mBytesRead = req->getBytesRead();
+ mPosition += mBytesRead;
+ sVFSThread->completeRequest(mHandle);
+ mHandle = LLVFSThread::nullHandle();
+ }
+ else
+ {
+ res = FALSE;
+ }
+ }
+ return res;
+}
+
+S32 LLVFile::getLastBytesRead()
+{
+ return mBytesRead;
+}
+
+BOOL LLVFile::eof()
+{
+ return mPosition >= getSize();
+}
+
+BOOL LLVFile::write(const U8 *buffer, S32 bytes)
+{
+ if (! (mMode & WRITE))
+ {
+ llwarns << "Attempt to write to file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
+ }
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ llerrs << "Attempt to write to vfile object " << mFileID << " with pending async operation" << llendl;
+ return FALSE;
+ }
+ BOOL success = TRUE;
+
+ // FIXME: allow async writes? potential problem wit mPosition...
+ if (mMode == APPEND) // all appends are async (but WRITEs are not)
+ {
+ U8* writebuf = new U8[bytes];
+ memcpy(writebuf, buffer, bytes);
+ S32 offset = -1;
+ mHandle = sVFSThread->write(mVFS, mFileID, mFileType,
+ writebuf, offset, bytes,
+ LLVFSThread::AUTO_COMPLETE | LLVFSThread::AUTO_DELETE);
+ mHandle = LLVFSThread::nullHandle(); // AUTO_COMPLETE means we don't track this
+ }
+ else
+ {
+ // We can't do a write while there are pending reads or writes on this file
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+
+ S32 pos = (mMode & APPEND) == APPEND ? -1 : mPosition;
+
+ S32 wrote = sVFSThread->writeImmediate(mVFS, mFileID, mFileType, (U8*)buffer, pos, bytes);
+
+ mPosition += wrote;
+
+ if (wrote < bytes)
+ {
+ llwarns << "Tried to write " << bytes << " bytes, actually wrote " << wrote << llendl;
+
+ success = FALSE;
+ }
+ }
+ return success;
+}
+
+//static
+BOOL LLVFile::writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+{
+ LLVFile file(vfs, uuid, type, LLVFile::WRITE);
+ file.setMaxSize(bytes);
+ return file.write(buffer, bytes);
+}
+
+BOOL LLVFile::seek(S32 offset, S32 origin)
+{
+ if (mMode == APPEND)
+ {
+ llwarns << "Attempt to seek on append-only file" << llendl;
+ return FALSE;
+ }
+
+ if (-1 == origin)
+ {
+ origin = mPosition;
+ }
+
+ S32 new_pos = origin + offset;
+
+ S32 size = getSize(); // Calls waitForLock(VFSLOCK_APPEND)
+
+ if (new_pos > size)
+ {
+ llwarns << "Attempt to seek past end of file" << llendl;
+
+ mPosition = size;
+ return FALSE;
+ }
+ else if (new_pos < 0)
+ {
+ llwarns << "Attempt to seek past beginning of file" << llendl;
+
+ mPosition = 0;
+ return FALSE;
+ }
+
+ mPosition = new_pos;
+ return TRUE;
+}
+
+S32 LLVFile::tell() const
+{
+ return mPosition;
+}
+
+S32 LLVFile::getSize()
+{
+ waitForLock(VFSLOCK_APPEND);
+ S32 size = mVFS->getSize(mFileID, mFileType);
+
+ return size;
+}
+
+S32 LLVFile::getMaxSize()
+{
+ S32 size = mVFS->getMaxSize(mFileID, mFileType);
+
+ return size;
+}
+
+BOOL LLVFile::setMaxSize(S32 size)
+{
+ if (! (mMode & WRITE))
+ {
+ llwarns << "Attempt to change size of file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
+
+ return FALSE;
+ }
+
+ if (!mVFS->checkAvailable(size))
+ {
+ LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
+ S32 count = 0;
+ while (sVFSThread->getPending() > 1000)
+ {
+ if (count % 100 == 0)
+ {
+ llinfos << "VFS catching up... Pending: " << sVFSThread->getPending() << llendl;
+ }
+ if (sVFSThread->isPaused())
+ {
+ sVFSThread->updateQueue(0);
+ }
+ ms_sleep(10);
+ }
+ }
+ return mVFS->setMaxSize(mFileID, mFileType, size);
+}
+
+BOOL LLVFile::rename(const LLUUID &new_id, const LLAssetType::EType new_type)
+{
+ if (! (mMode & WRITE))
+ {
+ llwarns << "Attempt to rename file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
+
+ return FALSE;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ llwarns << "Renaming file with pending async read" << llendl;
+ }
+
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+
+ // we need to release / replace our own lock
+ // since the renamed file will inherit locks from the new name
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_OPEN);
+ mVFS->renameFile(mFileID, mFileType, new_id, new_type);
+ mVFS->incLock(new_id, new_type, VFSLOCK_OPEN);
+
+ mFileID = new_id;
+ mFileType = new_type;
+
+ return TRUE;
+}
+
+BOOL LLVFile::remove()
+{
+// llinfos << "Removing file " << mFileID << llendl;
+
+ if (! (mMode & WRITE))
+ {
+ // Leaving paranoia warning just because this should be a very infrequent
+ // operation.
+ llwarns << "Remove file " << mFileID << " opened with mode " << std::hex << mMode << std::dec << llendl;
+ }
+
+ if (mHandle != LLVFSThread::nullHandle())
+ {
+ llwarns << "Removing file with pending async read" << llendl;
+ }
+
+ // why not seek back to the beginning of the file too?
+ mPosition = 0;
+
+ waitForLock(VFSLOCK_READ);
+ waitForLock(VFSLOCK_APPEND);
+ mVFS->removeFile(mFileID, mFileType);
+
+ return TRUE;
+}
+
+// static
+void LLVFile::initClass(LLVFSThread* vfsthread)
+{
+ if (!vfsthread)
+ {
+ if (LLVFSThread::sLocal != NULL)
+ {
+ vfsthread = LLVFSThread::sLocal;
+ }
+ else
+ {
+ vfsthread = new LLVFSThread();
+ sAllocdVFSThread = TRUE;
+ }
+ }
+ sVFSThread = vfsthread;
+}
+
+// static
+void LLVFile::cleanupClass()
+{
+ if (sAllocdVFSThread)
+ {
+ delete sVFSThread;
+ }
+ sVFSThread = NULL;
+}
+
+bool LLVFile::isLocked(EVFSLock lock)
+{
+ return mVFS->isLocked(mFileID, mFileType, lock) ? true : false;
+}
+
+void LLVFile::waitForLock(EVFSLock lock)
+{
+ LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT);
+ // spin until the lock clears
+ while (isLocked(lock))
+ {
+ if (sVFSThread->isPaused())
+ {
+ sVFSThread->updateQueue(0);
+ }
+ ms_sleep(1);
+ }
+}
diff --git a/indra/llvfs/llvfile.h b/indra/llvfs/llvfile.h
new file mode 100644
index 0000000000..c00e843cad
--- /dev/null
+++ b/indra/llvfs/llvfile.h
@@ -0,0 +1,75 @@
+/**
+ * @file llvfile.h
+ * @brief Definition of virtual file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVFILE_H
+#define LL_LLVFILE_H
+
+#include "lluuid.h"
+#include "llassettype.h"
+#include "llvfs.h"
+#include "llvfsthread.h"
+
+class LLVFile
+{
+public:
+ LLVFile(LLVFS *vfs, const LLUUID &file_id, const LLAssetType::EType file_type, S32 mode = LLVFile::READ);
+ ~LLVFile();
+
+ BOOL read(U8 *buffer, S32 bytes, BOOL async = FALSE, F32 priority = 128.f);
+ static U8* readFile(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, S32* bytes_read = 0);
+ void setReadPriority(const F32 priority);
+ BOOL isReadComplete();
+ S32 getLastBytesRead();
+ BOOL eof();
+
+ BOOL write(const U8 *buffer, S32 bytes);
+ static BOOL writeFile(const U8 *buffer, S32 bytes, LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type);
+ BOOL seek(S32 offset, S32 origin = -1);
+ S32 tell() const;
+
+ S32 getSize();
+ S32 getMaxSize();
+ BOOL setMaxSize(S32 size);
+ BOOL rename(const LLUUID &new_id, const LLAssetType::EType new_type);
+ BOOL remove();
+
+ bool isLocked(EVFSLock lock);
+ void waitForLock(EVFSLock lock);
+
+ static void initClass(LLVFSThread* vfsthread = NULL);
+ static void cleanupClass();
+ static LLVFSThread* getVFSThread() { return sVFSThread; }
+
+protected:
+ static LLVFSThread* sVFSThread;
+ static BOOL sAllocdVFSThread;
+ U32 threadPri() { return LLVFSThread::PRIORITY_NORMAL + llmin((U32)mPriority,(U32)0xfff); }
+
+public:
+ static const S32 READ;
+ static const S32 WRITE;
+ static const S32 READ_WRITE;
+ static const S32 APPEND;
+
+ static BOOL ALLOW_ASYNC;
+
+protected:
+ LLAssetType::EType mFileType;
+
+ LLUUID mFileID;
+ S32 mPosition;
+ S32 mMode;
+ LLVFS *mVFS;
+ F32 mPriority;
+ BOOL mOnReadQueue;
+
+ S32 mBytesRead;
+ LLVFSThread::handle_t mHandle;
+};
+
+#endif
diff --git a/indra/llvfs/llvfs.cpp b/indra/llvfs/llvfs.cpp
new file mode 100644
index 0000000000..39b12035c9
--- /dev/null
+++ b/indra/llvfs/llvfs.cpp
@@ -0,0 +1,2048 @@
+/**
+ * @file llvfs.cpp
+ * @brief Implementation of virtual file system
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <set>
+#include <map>
+#if LL_WINDOWS
+#include <share.h>
+#else
+#include <sys/file.h>
+#endif
+
+#include "llvfs.h"
+#include "llstl.h"
+
+const S32 FILE_BLOCK_MASK = 0x000003FF; // 1024-byte blocks
+const S32 VFS_CLEANUP_SIZE = 5242880; // how much space we free up in a single stroke
+const S32 BLOCK_LENGTH_INVALID = -1; // mLength for invalid LLVFSFileBlocks
+
+LLVFS *gVFS = NULL;
+
+// internal class definitions
+class LLVFSBlock
+{
+public:
+ LLVFSBlock()
+ {
+ mLocation = 0;
+ mLength = 0;
+ }
+
+ LLVFSBlock(U32 loc, S32 size)
+ {
+ mLocation = loc;
+ mLength = size;
+ }
+
+ static BOOL insertFirstLL(LLVFSBlock *first, LLVFSBlock *second)
+ {
+ return first->mLocation != second->mLocation
+ ? first->mLocation < second->mLocation
+ : first->mLength < second->mLength;
+
+ }
+
+public:
+ U32 mLocation;
+ S32 mLength; // allocated block size
+};
+
+LLVFSFileSpecifier::LLVFSFileSpecifier()
+: mFileID(),
+ mFileType( LLAssetType::AT_NONE )
+{
+}
+
+LLVFSFileSpecifier::LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+ mFileID = file_id;
+ mFileType = file_type;
+}
+
+bool LLVFSFileSpecifier::operator<(const LLVFSFileSpecifier &rhs) const
+{
+ return (mFileID == rhs.mFileID)
+ ? mFileType < rhs.mFileType
+ : mFileID < rhs.mFileID;
+}
+
+bool LLVFSFileSpecifier::operator==(const LLVFSFileSpecifier &rhs) const
+{
+ return (mFileID == rhs.mFileID &&
+ mFileType == rhs.mFileType);
+}
+
+
+class LLVFSFileBlock : public LLVFSBlock, public LLVFSFileSpecifier
+{
+public:
+ LLVFSFileBlock() : LLVFSBlock(), LLVFSFileSpecifier()
+ {
+ init();
+ }
+
+ LLVFSFileBlock(const LLUUID &file_id, LLAssetType::EType file_type, U32 loc = 0, S32 size = 0)
+ : LLVFSBlock(loc, size), LLVFSFileSpecifier( file_id, file_type )
+ {
+ init();
+ }
+
+ void init()
+ {
+ mSize = 0;
+ mIndexLocation = -1;
+ mAccessTime = (U32)time(NULL);
+
+ for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
+ {
+ mLocks[(EVFSLock)i] = 0;
+ }
+ }
+
+ #ifdef LL_LITTLE_ENDIAN
+ inline void swizzleCopy(void *dst, void *src, int size) { memcpy(dst, src, size); }
+
+ #else
+
+ inline U32 swizzle32(U32 x)
+ {
+ return(((x >> 24) & 0x000000FF) | ((x >> 8) & 0x0000FF00) | ((x << 8) & 0x00FF0000) |((x << 24) & 0xFF000000));
+ }
+
+ inline U16 swizzle16(U16 x)
+ {
+ return( ((x >> 8) & 0x000000FF) | ((x << 8) & 0x0000FF00) );
+ }
+
+ inline void swizzleCopy(void *dst, void *src, int size)
+ {
+ if(size == 4)
+ {
+ ((U32*)dst)[0] = swizzle32(((U32*)src)[0]);
+ }
+ else if(size == 2)
+ {
+ ((U16*)dst)[0] = swizzle16(((U16*)src)[0]);
+ }
+ else
+ {
+ // Perhaps this should assert...
+ memcpy(dst, src, size);
+ }
+ }
+
+ #endif
+
+ void serialize(U8 *buffer)
+ {
+ swizzleCopy(buffer, &mLocation, 4);
+ buffer += 4;
+ swizzleCopy(buffer, &mLength, 4);
+ buffer +=4;
+ swizzleCopy(buffer, &mAccessTime, 4);
+ buffer +=4;
+ memcpy(buffer, &mFileID.mData, 16);
+ buffer += 16;
+ S16 temp_type = mFileType;
+ swizzleCopy(buffer, &temp_type, 2);
+ buffer += 2;
+ swizzleCopy(buffer, &mSize, 4);
+ }
+
+ void deserialize(U8 *buffer, const S32 index_loc)
+ {
+ mIndexLocation = index_loc;
+
+ swizzleCopy(&mLocation, buffer, 4);
+ buffer += 4;
+ swizzleCopy(&mLength, buffer, 4);
+ buffer += 4;
+ swizzleCopy(&mAccessTime, buffer, 4);
+ buffer += 4;
+ memcpy(&mFileID.mData, buffer, 16);
+ buffer += 16;
+ S16 temp_type;
+ swizzleCopy(&temp_type, buffer, 2);
+ mFileType = (LLAssetType::EType)temp_type;
+ buffer += 2;
+ swizzleCopy(&mSize, buffer, 4);
+ }
+
+ static BOOL insertLRU(LLVFSFileBlock* const& first,
+ LLVFSFileBlock* const& second)
+ {
+ return (first->mAccessTime == second->mAccessTime)
+ ? *first < *second
+ : first->mAccessTime < second->mAccessTime;
+ }
+
+public:
+ S32 mSize;
+ S32 mIndexLocation; // location of index entry
+ U32 mAccessTime;
+ BOOL mLocks[VFSLOCK_COUNT]; // number of outstanding locks of each type
+
+ static const S32 SERIAL_SIZE;
+};
+
+// Helper structure for doing lru w/ stl... is there a simpler way?
+struct LLVFSFileBlock_less
+{
+ bool operator()(LLVFSFileBlock* const& lhs, LLVFSFileBlock* const& rhs) const
+ {
+ return (LLVFSFileBlock::insertLRU(lhs, rhs)) ? true : false;
+ }
+};
+
+
+const S32 LLVFSFileBlock::SERIAL_SIZE = 34;
+
+
+LLVFS::LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash)
+: mRemoveAfterCrash(remove_after_crash)
+{
+ mDataMutex = new LLMutex(0);
+
+ S32 i;
+ for (i = 0; i < VFSLOCK_COUNT; i++)
+ {
+ mLockCounts[i] = 0;
+ }
+ mValid = VFSVALID_OK;
+ mReadOnly = read_only;
+ mIndexFilename = new char[strlen(index_filename) + 1];
+ mDataFilename = new char[strlen(data_filename) + 1];
+ strcpy(mIndexFilename, index_filename);
+ strcpy(mDataFilename, data_filename);
+
+ const char *file_mode = mReadOnly ? "rb" : "r+b";
+
+ if (! (mDataFP = openAndLock(mDataFilename, file_mode, mReadOnly)))
+ {
+
+ if (mReadOnly)
+ {
+ llwarns << "Can't find " << mDataFilename << " to open read-only VFS" << llendl;
+ mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
+ return;
+ }
+
+ if((mDataFP = openAndLock(mDataFilename, "w+b", FALSE)))
+ {
+ // Since we're creating this data file, assume any index file is bogus
+ // remove the index, since this vfs is now blank
+ LLFile::remove(mIndexFilename);
+ }
+ else
+ {
+ llwarns << "Can't open VFS data file " << mDataFilename << " attempting to use alternate" << llendl;
+
+ char *temp_index = new char[strlen(mIndexFilename) + 10];
+ char *temp_data = new char[strlen(mDataFilename) + 10];
+
+ for (U32 count = 0; count < 256; count++)
+ {
+ sprintf(temp_index, "%s.%u", mIndexFilename, count);
+ sprintf(temp_data, "%s.%u", mDataFilename, count);
+
+ // try just opening, then creating, each alternate
+ if ((mDataFP = openAndLock(temp_data, "r+b", FALSE)))
+ {
+ break;
+ }
+
+ if ((mDataFP = openAndLock(temp_data, "w+b", FALSE)))
+ {
+ // we're creating the datafile, so nuke the indexfile
+ LLFile::remove(temp_index);
+ break;
+ }
+ }
+
+ if (! mDataFP)
+ {
+ llwarns << "Couldn't open vfs data file after trying many alternates" << llendl;
+ mValid = VFSVALID_BAD_CANNOT_CREATE;
+ return;
+ }
+
+ delete[] mIndexFilename;
+ delete[] mDataFilename;
+
+ mIndexFilename = temp_index;
+ mDataFilename = temp_data;
+ }
+
+ if (presize)
+ {
+ presizeDataFile(presize);
+ }
+ }
+
+ // Did we leave this file open for writing last time?
+ // If so, close it and start over.
+ if (!mReadOnly && mRemoveAfterCrash)
+ {
+ llstat marker_info;
+ char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
+ sprintf(marker, "%s.open", mDataFilename);
+ if (!LLFile::stat(marker, &marker_info))
+ {
+ // marker exists, kill the lock and the VFS files
+ unlockAndClose(mDataFP);
+ mDataFP = NULL;
+
+ llwarns << "VFS: File left open on last run, removing old VFS file " << mDataFilename << llendl;
+ LLFile::remove(mIndexFilename);
+ LLFile::remove(mDataFilename);
+ LLFile::remove(marker);
+
+ mDataFP = openAndLock(mDataFilename, "w+b", FALSE);
+ if (!mDataFP)
+ {
+ llwarns << "Can't open VFS data file in crash recovery" << llendl;
+ mValid = VFSVALID_BAD_CANNOT_CREATE;
+ return;
+ }
+
+ if (presize)
+ {
+ presizeDataFile(presize);
+ }
+ }
+ delete [] marker;
+ marker = NULL;
+ }
+
+ // determine the real file size
+ fseek(mDataFP, 0, SEEK_END);
+ U32 data_size = ftell(mDataFP);
+
+ // read the index file
+ // make sure there's at least one file in it too
+ // if not, we'll treat this as a new vfs
+ llstat fbuf;
+ if (! LLFile::stat(mIndexFilename, &fbuf) &&
+ fbuf.st_size >= LLVFSFileBlock::SERIAL_SIZE &&
+ (mIndexFP = openAndLock(mIndexFilename, file_mode, mReadOnly))
+ )
+ {
+ U8 *buffer = new U8[fbuf.st_size];
+ fread(buffer, fbuf.st_size, 1, mIndexFP);
+
+ U8 *tmp_ptr = buffer;
+
+ LLLinkedList<LLVFSBlock> files_by_loc;
+ files_by_loc.setInsertBefore(LLVFSBlock::insertFirstLL);
+
+ while (tmp_ptr < buffer + fbuf.st_size)
+ {
+ LLVFSFileBlock *block = new LLVFSFileBlock();
+
+ block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
+
+ // Do sanity check on this block.
+ // Note that this skips zero size blocks, which helps VFS
+ // to heal after some errors. JC
+ if (block->mLength > 0 &&
+ (U32)block->mLength <= data_size &&
+ block->mLocation >= 0 &&
+ block->mLocation < data_size &&
+ block->mSize > 0 &&
+ block->mSize <= block->mLength &&
+ block->mFileType >= LLAssetType::AT_NONE &&
+ block->mFileType < LLAssetType::AT_COUNT)
+ {
+ mFileBlocks.insert(fileblock_map::value_type(*block, block));
+ files_by_loc.addDataSorted(block);
+ }
+ else
+ if (block->mLength && block->mSize > 0)
+ {
+ // this is corrupt, not empty
+ llwarns << "VFS corruption: " << block->mFileID << " (" << block->mFileType << ") at index " << block->mIndexLocation << " DS: " << data_size << llendl;
+ llwarns << "Length: " << block->mLength << "\tLocation: " << block->mLocation << "\tSize: " << block->mSize << llendl;
+ llwarns << "File has bad data - VFS removed" << llendl;
+
+ delete[] buffer;
+ delete block;
+
+ unlockAndClose( mIndexFP );
+ mIndexFP = NULL;
+ LLFile::remove( mIndexFilename );
+
+ unlockAndClose( mDataFP );
+ mDataFP = NULL;
+ LLFile::remove( mDataFilename );
+
+ mValid = VFSVALID_BAD_CORRUPT;
+ return;
+ }
+ else
+ {
+ // this is a null or bad entry, skip it
+ S32 index_loc = (S32)(tmp_ptr - buffer);
+ mIndexHoles.push_back(index_loc);
+
+ delete block;
+ }
+
+ tmp_ptr += block->SERIAL_SIZE;
+ }
+ delete[] buffer;
+
+ // discover all the free blocks
+ LLVFSFileBlock *last_file_block = (LLVFSFileBlock*)files_by_loc.getFirstData();
+
+ if (last_file_block)
+ {
+ // check for empty space at the beginning
+ if (last_file_block->mLocation > 0)
+ {
+ LLVFSBlock *block = new LLVFSBlock(0, last_file_block->mLocation);
+ addFreeBlock(block);
+ }
+
+ LLVFSFileBlock *cur_file_block;
+ while ((cur_file_block = (LLVFSFileBlock*)files_by_loc.getNextData()))
+ {
+ if (cur_file_block->mLocation == last_file_block->mLocation
+ && cur_file_block->mLength == last_file_block->mLength)
+ {
+ llwarns << "VFS: removing duplicate entry"
+ << " at " << cur_file_block->mLocation
+ << " length " << cur_file_block->mLength
+ << " size " << cur_file_block->mSize
+ << " ID " << cur_file_block->mFileID
+ << " type " << cur_file_block->mFileType
+ << llendl;
+
+ // Duplicate entries. Nuke them both for safety.
+ mFileBlocks.erase(*cur_file_block); // remove ID/type entry
+ if (cur_file_block->mLength > 0)
+ {
+ // convert to hole
+ LLVFSBlock* block = new LLVFSBlock(cur_file_block->mLocation,
+ cur_file_block->mLength);
+ addFreeBlock(block);
+ }
+ lockData(); // needed for sync()
+ sync(cur_file_block, TRUE); // remove first on disk
+ sync(last_file_block, TRUE); // remove last on disk
+ unlockData(); // needed for sync()
+ last_file_block = cur_file_block;
+ continue;
+ }
+
+ U32 loc = last_file_block->mLocation + last_file_block->mLength;
+ S32 length = cur_file_block->mLocation - loc;
+
+ if (length < 0 || loc < 0 || loc > data_size)
+ {
+ // Invalid VFS
+ unlockAndClose( mIndexFP );
+ mIndexFP = NULL;
+ LLFile::remove( mIndexFilename );
+
+ unlockAndClose( mDataFP );
+ mDataFP = NULL;
+ LLFile::remove( mDataFilename );
+
+ llwarns << "VFS: overlapping entries"
+ << " at " << cur_file_block->mLocation
+ << " length " << cur_file_block->mLength
+ << " ID " << cur_file_block->mFileID
+ << " type " << cur_file_block->mFileType
+ << llendl;
+ mValid = VFSVALID_BAD_CORRUPT;
+ return;
+ }
+
+ if (length > 0)
+ {
+ LLVFSBlock *block = new LLVFSBlock(loc, length);
+ addFreeBlock(block);
+ }
+
+ last_file_block = cur_file_block;
+ }
+
+ // also note any empty space at the end
+ U32 loc = last_file_block->mLocation + last_file_block->mLength;
+ if (loc < data_size)
+ {
+ LLVFSBlock *block = new LLVFSBlock(loc, data_size - loc);
+ addFreeBlock(block);
+ }
+ }
+ else
+ {
+ LLVFSBlock *first_block = new LLVFSBlock(0, data_size);
+ addFreeBlock(first_block);
+ }
+ }
+ else
+ {
+ if (mReadOnly)
+ {
+ llwarns << "Can't find " << mIndexFilename << " to open read-only VFS" << llendl;
+ mValid = VFSVALID_BAD_CANNOT_OPEN_READONLY;
+ return;
+ }
+
+
+ mIndexFP = openAndLock(mIndexFilename, "w+b", FALSE);
+ if (!mIndexFP)
+ {
+ llwarns << "Couldn't open an index file for the VFS, probably a sharing violation!" << llendl;
+
+ unlockAndClose( mDataFP );
+ mDataFP = NULL;
+ LLFile::remove( mDataFilename );
+
+ mValid = VFSVALID_BAD_CANNOT_CREATE;
+ return;
+ }
+
+ // no index file, start from scratch w/ 1GB allocation
+ LLVFSBlock *first_block = new LLVFSBlock(0, data_size ? data_size : 0x40000000);
+ addFreeBlock(first_block);
+ }
+
+ // Open marker file to look for bad shutdowns
+ if (!mReadOnly && mRemoveAfterCrash)
+ {
+ char* marker = new char[strlen(mDataFilename) + strlen(".open") + 1];
+ sprintf(marker, "%s.open", mDataFilename);
+ FILE* marker_fp = LLFile::fopen(marker, "w");
+ if (marker_fp)
+ {
+ fclose(marker_fp);
+ marker_fp = NULL;
+ }
+ delete [] marker;
+ marker = NULL;
+ }
+
+ llinfos << "VFS: Using index file " << mIndexFilename << " and data file " << mDataFilename << llendl;
+
+ mValid = VFSVALID_OK;
+}
+
+LLVFS::~LLVFS()
+{
+ if (mDataMutex->isLocked())
+ {
+ llerrs << "LLVFS destroyed with mutex locked" << llendl;
+ }
+
+ unlockAndClose(mIndexFP);
+ mIndexFP = NULL;
+
+ fileblock_map::const_iterator it;
+ for (it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ delete (*it).second;
+ }
+ mFileBlocks.clear();
+
+ mFreeBlocksByLength.clear();
+
+ for_each(mFreeBlocksByLocation.begin(), mFreeBlocksByLocation.end(), DeletePairedPointer());
+
+ unlockAndClose(mDataFP);
+ mDataFP = NULL;
+
+ // Remove marker file
+ if (!mReadOnly && mRemoveAfterCrash)
+ {
+ char* marker_file = new char[strlen(mDataFilename) + strlen(".open") + 1];
+ sprintf(marker_file, "%s.open", mDataFilename);
+ LLFile::remove(marker_file);
+ delete [] marker_file;
+ marker_file = NULL;
+ }
+
+ delete[] mIndexFilename;
+ mIndexFilename = NULL;
+ delete[] mDataFilename;
+ mDataFilename = NULL;
+
+ delete mDataMutex;
+}
+
+void LLVFS::presizeDataFile(const U32 size)
+{
+ if (!mDataFP)
+ {
+ llerrs << "LLVFS::presizeDataFile() with no data file open" << llendl;
+ }
+
+ // we're creating this file for the first time, size it
+ fseek(mDataFP, size-1, SEEK_SET);
+ S32 tmp = 0;
+ tmp = (S32)fwrite(&tmp, 1, 1, mDataFP);
+ // fflush(mDataFP);
+
+ // also remove any index, since this vfs is now blank
+ LLFile::remove(mIndexFilename);
+
+ if (tmp)
+ {
+ llinfos << "Pre-sized VFS data file to " << ftell(mDataFP) << " bytes" << llendl;
+ }
+ else
+ {
+ llwarns << "Failed to pre-size VFS data file" << llendl;
+ }
+}
+
+BOOL LLVFS::getExists(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+ LLVFSFileBlock *block = NULL;
+
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ block = (*it).second;
+ block->mAccessTime = (U32)time(NULL);
+ }
+
+ BOOL res = (block && block->mLength > 0) ? TRUE : FALSE;
+
+ unlockData();
+
+ return res;
+}
+
+S32 LLVFS::getSize(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+ S32 size = 0;
+
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+
+ block->mAccessTime = (U32)time(NULL);
+ size = block->mSize;
+ }
+
+ unlockData();
+
+ return size;
+}
+
+S32 LLVFS::getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+ S32 size = 0;
+
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+
+ block->mAccessTime = (U32)time(NULL);
+ size = block->mLength;
+ }
+
+ unlockData();
+
+ return size;
+}
+
+BOOL LLVFS::checkAvailable(S32 max_size)
+{
+ blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(max_size); // first entry >= size
+ return (iter == mFreeBlocksByLength.end()) ? FALSE : TRUE;
+}
+
+BOOL LLVFS::setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ if (mReadOnly)
+ {
+ llerrs << "Attempt to write to read-only VFS" << llendl;
+ }
+ if (max_size <= 0)
+ {
+ llwarns << "VFS: Attempt to assign size " << max_size << " to vfile " << file_id << llendl;
+ return FALSE;
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ LLVFSFileBlock *block = NULL;
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ block = (*it).second;
+ }
+
+ // round all sizes upward to KB increments
+ if (max_size & FILE_BLOCK_MASK)
+ {
+ max_size += FILE_BLOCK_MASK;
+ max_size &= ~FILE_BLOCK_MASK;
+ }
+
+ if (block && block->mLength > 0)
+ {
+ block->mAccessTime = (U32)time(NULL);
+
+ if (max_size == block->mLength)
+ {
+ unlockData();
+ return TRUE;
+ }
+ else if (max_size < block->mLength)
+ {
+ // this file is shrinking
+ LLVFSBlock *free_block = new LLVFSBlock(block->mLocation + max_size, block->mLength - max_size);
+
+ addFreeBlock(free_block);
+
+ block->mLength = max_size;
+
+ if (block->mLength < block->mSize)
+ {
+ // JC: Was a warning, but Ian says it's bad.
+ llerrs << "Truncating virtual file " << file_id << " to " << block->mLength << " bytes" << llendl;
+ block->mSize = block->mLength;
+ }
+
+ sync(block);
+ //mergeFreeBlocks();
+
+ unlockData();
+ return TRUE;
+ }
+ else if (max_size > block->mLength)
+ {
+ // this file is growing
+ // first check for an adjacent free block to grow into
+ S32 size_increase = max_size - block->mLength;
+
+ // Find the first free block with and addres > block->mLocation
+ LLVFSBlock *free_block;
+ blocks_location_map_t::iterator iter = mFreeBlocksByLocation.upper_bound(block->mLocation);
+ if (iter != mFreeBlocksByLocation.end())
+ {
+ free_block = iter->second;
+
+ if (free_block->mLocation == block->mLocation + block->mLength &&
+ free_block->mLength >= size_increase)
+ {
+ // this free block is at the end of the file and is large enough
+
+ // Must call useFreeSpace before sync(), as sync()
+ // unlocks data structures.
+ useFreeSpace(free_block, size_increase);
+ block->mLength += size_increase;
+ sync(block);
+
+ unlockData();
+ return TRUE;
+ }
+ }
+
+ // no adjecent free block, find one in the list
+ free_block = findFreeBlock(max_size, block);
+
+ if (free_block)
+ {
+ if (block->mLength > 0)
+ {
+ // create a new free block where this file used to be
+ LLVFSBlock *new_free_block = new LLVFSBlock(block->mLocation, block->mLength);
+
+ addFreeBlock(new_free_block);
+
+ if (block->mSize > 0)
+ {
+ // move the file into the new block
+ U8 *buffer = new U8[block->mSize];
+ fseek(mDataFP, block->mLocation, SEEK_SET);
+ fread(buffer, block->mSize, 1, mDataFP);
+ fseek(mDataFP, free_block->mLocation, SEEK_SET);
+ fwrite(buffer, block->mSize, 1, mDataFP);
+ // fflush(mDataFP);
+
+ delete[] buffer;
+ }
+ }
+
+ block->mLocation = free_block->mLocation;
+
+ block->mLength = max_size;
+
+ // Must call useFreeSpace before sync(), as sync()
+ // unlocks data structures.
+ useFreeSpace(free_block, max_size);
+
+ sync(block);
+
+ unlockData();
+ return TRUE;
+ }
+ else
+ {
+ llwarns << "VFS: No space (" << max_size << ") to resize existing vfile " << file_id << llendl;
+ //dumpMap();
+ unlockData();
+ dumpStatistics();
+ return FALSE;
+ }
+ }
+ }
+ else
+ {
+ // find a free block in the list
+ LLVFSBlock *free_block = findFreeBlock(max_size);
+
+ if (free_block)
+ {
+ if (block)
+ {
+ block->mLocation = free_block->mLocation;
+ block->mLength = max_size;
+ }
+ else
+ {
+ // this file doesn't exist, create it
+ block = new LLVFSFileBlock(file_id, file_type, free_block->mLocation, max_size);
+ mFileBlocks.insert(fileblock_map::value_type(spec, block));
+ }
+
+ // Must call useFreeSpace before sync(), as sync()
+ // unlocks data structures.
+ useFreeSpace(free_block, max_size);
+ block->mAccessTime = (U32)time(NULL);
+
+ sync(block);
+ }
+ else
+ {
+ llwarns << "VFS: No space (" << max_size << ") for new virtual file " << file_id << llendl;
+ //dumpMap();
+ unlockData();
+ dumpStatistics();
+ return FALSE;
+ }
+ }
+ unlockData();
+ return TRUE;
+}
+
+
+// WARNING: HERE BE DRAGONS!
+// rename is the weirdest VFS op, because the file moves but the locks don't!
+void LLVFS::renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
+ const LLUUID &new_id, const LLAssetType::EType &new_type)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ if (mReadOnly)
+ {
+ llerrs << "Attempt to write to read-only VFS" << llendl;
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier new_spec(new_id, new_type);
+ LLVFSFileSpecifier old_spec(file_id, file_type);
+
+ fileblock_map::iterator it = mFileBlocks.find(old_spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *src_block = (*it).second;
+
+ // this will purge the data but leave the file block in place, w/ locks, if any
+ // WAS: removeFile(new_id, new_type); NOW uses removeFileBlock() to avoid mutex lock recursion
+ fileblock_map::iterator new_it = mFileBlocks.find(new_spec);
+ if (new_it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *new_block = (*new_it).second;
+ removeFileBlock(new_block);
+ }
+
+ // if there's something in the target location, remove it but inherit its locks
+ it = mFileBlocks.find(new_spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *dest_block = (*it).second;
+
+ for (S32 i = 0; i < (S32)VFSLOCK_COUNT; i++)
+ {
+ src_block->mLocks[(EVFSLock)i] = dest_block->mLocks[(EVFSLock)i];
+ }
+
+ mFileBlocks.erase(new_spec);
+ delete dest_block;
+ }
+
+ src_block->mFileID = new_id;
+ src_block->mFileType = new_type;
+ src_block->mAccessTime = (U32)time(NULL);
+
+ mFileBlocks.erase(old_spec);
+ mFileBlocks.insert(fileblock_map::value_type(new_spec, src_block));
+
+ sync(src_block);
+ }
+ else
+ {
+ llwarns << "VFS: Attempt to rename nonexistent vfile " << file_id << ":" << file_type << llendl;
+ }
+ unlockData();
+}
+
+// mDataMutex must be LOCKED before calling this
+void LLVFS::removeFileBlock(LLVFSFileBlock *fileblock)
+{
+ // convert this into an unsaved, dummy fileblock to preserve locks
+ // a more rubust solution would store the locks in a seperate data structure
+ sync(fileblock, TRUE);
+
+ if (fileblock->mLength > 0)
+ {
+ // turn this file into an empty block
+ LLVFSBlock *free_block = new LLVFSBlock(fileblock->mLocation, fileblock->mLength);
+
+ addFreeBlock(free_block);
+ }
+
+ fileblock->mLocation = 0;
+ fileblock->mSize = 0;
+ fileblock->mLength = BLOCK_LENGTH_INVALID;
+ fileblock->mIndexLocation = -1;
+
+ //mergeFreeBlocks();
+}
+
+void LLVFS::removeFile(const LLUUID &file_id, const LLAssetType::EType file_type)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ if (mReadOnly)
+ {
+ llerrs << "Attempt to write to read-only VFS" << llendl;
+ }
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+ removeFileBlock(block);
+ }
+ else
+ {
+ llwarns << "VFS: attempting to remove nonexistent file " << file_id << " type " << file_type << llendl;
+ }
+
+ unlockData();
+}
+
+
+S32 LLVFS::getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length)
+{
+ S32 bytesread = 0;
+
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ llassert(location >= 0);
+ llassert(length >= 0);
+
+ BOOL do_read = FALSE;
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+
+ block->mAccessTime = (U32)time(NULL);
+
+ if (location > block->mSize)
+ {
+ llwarns << "VFS: Attempt to read location " << location << " in file " << file_id << " of length " << block->mSize << llendl;
+ }
+ else
+ {
+ if (length > block->mSize - location)
+ {
+ length = block->mSize - location;
+ }
+ location += block->mLocation;
+ do_read = TRUE;
+ }
+ }
+
+ unlockData();
+
+ if (do_read)
+ {
+ fseek(mDataFP, location, SEEK_SET);
+ bytesread = (S32)fread(buffer, 1, length, mDataFP);
+ }
+
+ return bytesread;
+}
+
+S32 LLVFS::storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ if (mReadOnly)
+ {
+ llerrs << "Attempt to write to read-only VFS" << llendl;
+ }
+
+ llassert(length > 0);
+
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+
+ S32 in_loc = location;
+ if (location == -1)
+ {
+ location = block->mSize;
+ }
+ llassert(location >= 0);
+
+ block->mAccessTime = (U32)time(NULL);
+
+ if (block->mLength == BLOCK_LENGTH_INVALID)
+ {
+ // Block was removed, ignore write
+ llwarns << "VFS: Attempt to write to invalid block"
+ << " in file " << file_id
+ << " location: " << in_loc
+ << " bytes: " << length
+ << llendl;
+ unlockData();
+ return length;
+ }
+ else if (location > block->mLength)
+ {
+ llwarns << "VFS: Attempt to write to location " << location
+ << " in file " << file_id
+ << " type " << S32(file_type)
+ << " of size " << block->mSize
+ << " block length " << block->mLength
+ << llendl;
+ unlockData();
+ return length;
+ }
+ else
+ {
+ if (length > block->mLength - location )
+ {
+ llwarns << "VFS: Truncating write to virtual file " << file_id << " type " << S32(file_type) << llendl;
+ length = block->mLength - location;
+ }
+ U32 file_location = location + block->mLocation;
+
+ unlockData();
+
+ fseek(mDataFP, file_location, SEEK_SET);
+ S32 write_len = (S32)fwrite(buffer, 1, length, mDataFP);
+ if (write_len != length)
+ {
+ llwarns << llformat("VFS Write Error: %d != %d",write_len,length) << llendl;
+ }
+ // fflush(mDataFP);
+
+ lockData();
+ if (location + length > block->mSize)
+ {
+ block->mSize = location + write_len;
+ sync(block);
+ }
+ unlockData();
+
+ return write_len;
+ }
+ }
+ else
+ {
+ unlockData();
+ return 0;
+ }
+}
+
+void LLVFS::incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
+{
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ LLVFSFileBlock *block;
+
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ block = (*it).second;
+ }
+ else
+ {
+ // Create a dummy block which isn't saved
+ block = new LLVFSFileBlock(file_id, file_type, 0, BLOCK_LENGTH_INVALID);
+ block->mAccessTime = (U32)time(NULL);
+ mFileBlocks.insert(fileblock_map::value_type(spec, block));
+ }
+
+ block->mLocks[lock]++;
+ mLockCounts[lock]++;
+
+ unlockData();
+}
+
+void LLVFS::decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
+{
+ lockData();
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+
+ if (block->mLocks[lock] > 0)
+ {
+ block->mLocks[lock]--;
+ }
+ else
+ {
+ llwarns << "VFS: Decrementing zero-value lock " << lock << llendl;
+ }
+ mLockCounts[lock]--;
+ }
+
+ unlockData();
+}
+
+BOOL LLVFS::isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock)
+{
+ lockData();
+
+ BOOL res = FALSE;
+
+ LLVFSFileSpecifier spec(file_id, file_type);
+ fileblock_map::iterator it = mFileBlocks.find(spec);
+ if (it != mFileBlocks.end())
+ {
+ LLVFSFileBlock *block = (*it).second;
+ res = (block->mLocks[lock] > 0);
+ }
+
+ unlockData();
+
+ return res;
+}
+
+//============================================================================
+// protected
+//============================================================================
+
+void LLVFS::eraseBlockLength(LLVFSBlock *block)
+{
+ // find the corresponding map entry in the length map and erase it
+ S32 length = block->mLength;
+ blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(length);
+ blocks_length_map_t::iterator end = mFreeBlocksByLength.end();
+ while(iter != end)
+ {
+ LLVFSBlock *tblock = iter->second;
+ llassert(tblock->mLength == length); // there had -better- be an entry with our length!
+ if (tblock == block)
+ {
+ mFreeBlocksByLength.erase(iter);
+ break;
+ }
+ ++iter;
+ }
+ if (iter == end)
+ {
+ llerrs << "eraseBlock could not find block" << llendl;
+ }
+}
+
+
+// Remove block from both free lists (by location and by length).
+void LLVFS::eraseBlock(LLVFSBlock *block)
+{
+ eraseBlockLength(block);
+ // find the corresponding map entry in the location map and erase it
+ U32 location = block->mLocation;
+ llverify(mFreeBlocksByLocation.erase(location) == 1); // we should only have one entry per location.
+}
+
+
+// Add the region specified by block location and length to the free lists.
+// Also incrementally defragment by merging with previous and next free blocks.
+void LLVFS::addFreeBlock(LLVFSBlock *block)
+{
+#if LL_DEBUG
+ size_t dbgcount = mFreeBlocksByLocation.count(block->mLocation);
+ if(dbgcount > 0)
+ {
+ llerrs << "addFreeBlock called with block already in list" << llendl;
+ }
+#endif
+
+ // Get a pointer to the next free block (by location).
+ blocks_location_map_t::iterator next_free_it = mFreeBlocksByLocation.lower_bound(block->mLocation);
+
+ // We can merge with previous if it ends at our requested location.
+ LLVFSBlock* prev_block = NULL;
+ bool merge_prev = false;
+ if (next_free_it != mFreeBlocksByLocation.begin())
+ {
+ blocks_location_map_t::iterator prev_free_it = next_free_it;
+ --prev_free_it;
+ prev_block = prev_free_it->second;
+ merge_prev = (prev_block->mLocation + prev_block->mLength == block->mLocation);
+ }
+
+ // We can merge with next if our block ends at the next block's location.
+ LLVFSBlock* next_block = NULL;
+ bool merge_next = false;
+ if (next_free_it != mFreeBlocksByLocation.end())
+ {
+ next_block = next_free_it->second;
+ merge_next = (block->mLocation + block->mLength == next_block->mLocation);
+ }
+
+ if (merge_prev && merge_next)
+ {
+ // llinfos << "VFS merge BOTH" << llendl;
+ // Previous block is changing length (a lot), so only need to update length map.
+ // Next block is going away completely. JC
+ eraseBlockLength(prev_block);
+ eraseBlock(next_block);
+ prev_block->mLength += block->mLength + next_block->mLength;
+ mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block));
+ delete block;
+ block = NULL;
+ delete next_block;
+ next_block = NULL;
+ }
+ else if (merge_prev)
+ {
+ // llinfos << "VFS merge previous" << llendl;
+ // Previous block is maintaining location, only changing length,
+ // therefore only need to update the length map. JC
+ eraseBlockLength(prev_block);
+ prev_block->mLength += block->mLength;
+ mFreeBlocksByLength.insert(blocks_length_map_t::value_type(prev_block->mLength, prev_block)); // multimap insert
+ delete block;
+ block = NULL;
+ }
+ else if (merge_next)
+ {
+ // llinfos << "VFS merge next" << llendl;
+ // Next block is changing both location and length,
+ // so both free lists must update. JC
+ eraseBlock(next_block);
+ next_block->mLocation = block->mLocation;
+ next_block->mLength += block->mLength;
+ // Don't hint here, next_free_it iterator may be invalid.
+ mFreeBlocksByLocation.insert(blocks_location_map_t::value_type(next_block->mLocation, next_block)); // multimap insert
+ mFreeBlocksByLength.insert(blocks_length_map_t::value_type(next_block->mLength, next_block)); // multimap insert
+ delete block;
+ block = NULL;
+ }
+ else
+ {
+ // Can't merge with other free blocks.
+ // Hint that insert should go near next_free_it.
+ mFreeBlocksByLocation.insert(next_free_it, blocks_location_map_t::value_type(block->mLocation, block)); // multimap insert
+ mFreeBlocksByLength.insert(blocks_length_map_t::value_type(block->mLength, block)); // multimap insert
+ }
+}
+
+// Superceeded by new addFreeBlock which does incremental free space merging.
+// Incremental is faster overall.
+//void LLVFS::mergeFreeBlocks()
+//{
+// if (!isValid())
+// {
+// llerrs << "Attempting to use invalid VFS!" << llendl;
+// }
+// // TODO: could we optimize this with hints from the calling code?
+// blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
+// blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
+// LLVFSBlock *first_block = iter->second;
+// while(iter != end)
+// {
+// blocks_location_map_t::iterator first_iter = iter; // save for if we do a merge
+// if (++iter == end)
+// break;
+// LLVFSBlock *second_block = iter->second;
+// if (first_block->mLocation + first_block->mLength == second_block->mLocation)
+// {
+// // remove the first block from the length map
+// eraseBlockLength(first_block);
+// // merge first_block with second_block, since they're adjacent
+// first_block->mLength += second_block->mLength;
+// // add the first block to the length map (with the new size)
+// mFreeBlocksByLength.insert(blocks_length_map_t::value_type(first_block->mLength, first_block)); // multimap insert
+//
+// // erase and delete the second block
+// eraseBlock(second_block);
+// delete second_block;
+//
+// // reset iterator
+// iter = first_iter; // haven't changed first_block, so corresponding iterator is still valid
+// end = mFreeBlocksByLocation.end();
+// }
+// first_block = second_block;
+// }
+//}
+
+
+void LLVFS::useFreeSpace(LLVFSBlock *free_block, S32 length)
+{
+ if (free_block->mLength == length)
+ {
+ eraseBlock(free_block);
+ delete free_block;
+ }
+ else
+ {
+ eraseBlock(free_block);
+
+ free_block->mLocation += length;
+ free_block->mLength -= length;
+
+ addFreeBlock(free_block);
+ }
+}
+
+// NOTE! mDataMutex must be LOCKED before calling this
+// sync this index entry out to the index file
+// we need to do this constantly to avoid corruption on viewer crash
+void LLVFS::sync(LLVFSFileBlock *block, BOOL remove)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ if (mReadOnly)
+ {
+ llwarns << "Attempt to sync read-only VFS" << llendl;
+ return;
+ }
+ if (block->mLength == BLOCK_LENGTH_INVALID)
+ {
+ // This is a dummy file, don't save
+ return;
+ }
+ if (block->mLength == 0)
+ {
+ llerrs << "VFS syncing zero-length block" << llendl;
+ }
+
+ BOOL set_index_to_end = FALSE;
+ S32 seek_pos = block->mIndexLocation;
+
+ if (-1 == seek_pos)
+ {
+ if (!mIndexHoles.empty())
+ {
+ seek_pos = mIndexHoles.front();
+ mIndexHoles.pop_front();
+ }
+ else
+ {
+ set_index_to_end = TRUE;
+ }
+ }
+
+ if (set_index_to_end)
+ {
+ // Need fseek/ftell to update the seek_pos and hence data
+ // structures, so can't unlockData() before this.
+ fseek(mIndexFP, 0, SEEK_END);
+ seek_pos = ftell(mIndexFP);
+ }
+
+ block->mIndexLocation = seek_pos;
+ if (remove)
+ {
+ mIndexHoles.push_back(seek_pos);
+ }
+
+ U8 buffer[LLVFSFileBlock::SERIAL_SIZE];
+ if (remove)
+ {
+ memset(buffer, 0, LLVFSFileBlock::SERIAL_SIZE);
+ }
+ else
+ {
+ block->serialize(buffer);
+ }
+
+ unlockData();
+
+ // If set_index_to_end, file pointer is already at seek_pos
+ // and we don't need to do anything. Only seek if not at end.
+ if (!set_index_to_end)
+ {
+ fseek(mIndexFP, seek_pos, SEEK_SET);
+ }
+
+ fwrite(buffer, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP);
+ // fflush(mIndexFP);
+
+ lockData();
+
+ return;
+}
+
+// mDataMutex must be LOCKED before calling this
+// Can initiate LRU-based file removal to make space.
+// The immune file block will not be removed.
+LLVFSBlock *LLVFS::findFreeBlock(S32 size, LLVFSFileBlock *immune)
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+
+ LLVFSBlock *block = NULL;
+ BOOL have_lru_list = FALSE;
+
+ typedef std::set<LLVFSFileBlock*, LLVFSFileBlock_less> lru_set;
+ lru_set lru_list;
+
+ LLTimer timer;
+
+ while (! block)
+ {
+ // look for a suitable free block
+ blocks_length_map_t::iterator iter = mFreeBlocksByLength.lower_bound(size); // first entry >= size
+ if (iter != mFreeBlocksByLength.end())
+ block = iter->second;
+
+ // no large enough free blocks, time to clean out some junk
+ if (! block)
+ {
+ // create a list of files sorted by usage time
+ // this is far faster than sorting a linked list
+ if (! have_lru_list)
+ {
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileBlock *tmp = (*it).second;
+
+ if (tmp != immune &&
+ tmp->mLength > 0 &&
+ ! tmp->mLocks[VFSLOCK_READ] &&
+ ! tmp->mLocks[VFSLOCK_APPEND] &&
+ ! tmp->mLocks[VFSLOCK_OPEN])
+ {
+ lru_list.insert(tmp);
+ }
+ }
+
+ have_lru_list = TRUE;
+ }
+
+ if (lru_list.size() == 0)
+ {
+ // No more files to delete, and still not enough room!
+ llwarns << "VFS: Can't make " << size << " bytes of free space in VFS, giving up" << llendl;
+ break;
+ }
+
+ // is the oldest file big enough? (Should be about half the time)
+ lru_set::iterator it = lru_list.begin();
+ LLVFSFileBlock *file_block = *it;
+ if (file_block->mLength >= size && file_block != immune)
+ {
+ // ditch this file and look again for a free block - should find it
+ // TODO: it'll be faster just to assign the free block and break
+ llinfos << "LRU: Removing " << file_block->mFileID << ":" << file_block->mFileType << llendl;
+ lru_list.erase(it);
+ removeFileBlock(file_block);
+ file_block = NULL;
+ continue;
+ }
+
+
+ llinfos << "VFS: LRU: Aggressive: " << (S32)lru_list.size() << " files remain" << llendl;
+ dumpLockCounts();
+
+ // Now it's time to aggressively make more space
+ // Delete the oldest 5MB of the vfs or enough to hold the file, which ever is larger
+ // This may yield too much free space, but we'll use it up soon enough
+ U32 cleanup_target = (size > VFS_CLEANUP_SIZE) ? size : VFS_CLEANUP_SIZE;
+ U32 cleaned_up = 0;
+ for (it = lru_list.begin();
+ it != lru_list.end() && cleaned_up < cleanup_target;
+ )
+ {
+ file_block = *it;
+
+ // TODO: it would be great to be able to batch all these sync() calls
+ // llinfos << "LRU2: Removing " << file_block->mFileID << ":" << file_block->mFileType << " last accessed" << file_block->mAccessTime << llendl;
+
+ cleaned_up += file_block->mLength;
+ lru_list.erase(it++);
+ removeFileBlock(file_block);
+ file_block = NULL;
+ }
+ //mergeFreeBlocks();
+ }
+ }
+
+ F32 time = timer.getElapsedTimeF32();
+ if (time > 0.5f)
+ {
+ llwarns << "VFS: Spent " << time << " seconds in findFreeBlock!" << llendl;
+ }
+
+ return block;
+}
+
+//============================================================================
+// public
+//============================================================================
+
+void LLVFS::pokeFiles()
+{
+ if (!isValid())
+ {
+ llerrs << "Attempting to use invalid VFS!" << llendl;
+ }
+ U32 word;
+
+ // only write data if we actually read 4 bytes
+ // otherwise we're writing garbage and screwing up the file
+ fseek(mDataFP, 0, SEEK_SET);
+ if (fread(&word, 1, 4, mDataFP) == 4)
+ {
+ fseek(mDataFP, 0, SEEK_SET);
+ fwrite(&word, 1, 4, mDataFP);
+ fflush(mDataFP);
+ }
+
+ fseek(mIndexFP, 0, SEEK_SET);
+ if (fread(&word, 1, 4, mIndexFP) == 4)
+ {
+ fseek(mIndexFP, 0, SEEK_SET);
+ fwrite(&word, 1, 4, mIndexFP);
+ fflush(mIndexFP);
+ }
+}
+
+
+void LLVFS::dumpMap()
+{
+ llinfos << "Files:" << llendl;
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileBlock *file_block = (*it).second;
+ llinfos << "Location: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
+ }
+
+ llinfos << "Free Blocks:" << llendl;
+ for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
+ end = mFreeBlocksByLocation.end();
+ iter != end; iter++)
+ {
+ LLVFSBlock *free_block = iter->second;
+ llinfos << "Location: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
+ }
+}
+
+// verify that the index file contents match the in-memory file structure
+// Very slow, do not call routinely. JC
+void LLVFS::audit()
+{
+ // Lock the mutex through this whole function.
+ LLMutexLock lock_data(mDataMutex);
+
+ fflush(mIndexFP);
+
+ fseek(mIndexFP, 0, SEEK_END);
+ S32 index_size = ftell(mIndexFP);
+ fseek(mIndexFP, 0, SEEK_SET);
+
+ U8 *buffer = new U8[index_size];
+ fread(buffer, index_size, 1, mIndexFP);
+
+ U8 *tmp_ptr = buffer;
+
+ std::map<LLVFSFileSpecifier, LLVFSFileBlock*> found_files;
+ U32 cur_time = (U32)time(NULL);
+
+ BOOL vfs_corrupt = FALSE;
+
+ std::vector<LLVFSFileBlock*> audit_blocks;
+ while (tmp_ptr < buffer + index_size)
+ {
+ LLVFSFileBlock *block = new LLVFSFileBlock();
+ audit_blocks.push_back(block);
+
+ block->deserialize(tmp_ptr, (S32)(tmp_ptr - buffer));
+ tmp_ptr += block->SERIAL_SIZE;
+
+ // do sanity check on this block
+ if (block->mLength >= 0 &&
+ block->mLocation >= 0 &&
+ block->mSize >= 0 &&
+ block->mSize <= block->mLength &&
+ block->mFileType >= LLAssetType::AT_NONE &&
+ block->mFileType < LLAssetType::AT_COUNT &&
+ block->mAccessTime <= cur_time &&
+ block->mFileID != LLUUID::null)
+ {
+ if (mFileBlocks.find(*block) == mFileBlocks.end())
+ {
+ llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " on disk, not in memory, loc " << block->mIndexLocation << llendl;
+ }
+ else if (found_files.find(*block) != found_files.end())
+ {
+ std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator it;
+ it = found_files.find(*block);
+ LLVFSFileBlock* dupe = it->second;
+ // try to keep data from being lost
+ unlockAndClose(mIndexFP);
+ mIndexFP = NULL;
+ unlockAndClose(mDataFP);
+ mDataFP = NULL;
+ llwarns << "VFS: Original block index " << block->mIndexLocation
+ << " location " << block->mLocation
+ << " length " << block->mLength
+ << " size " << block->mSize
+ << " id " << block->mFileID
+ << " type " << block->mFileType
+ << llendl;
+ llwarns << "VFS: Duplicate block index " << dupe->mIndexLocation
+ << " location " << dupe->mLocation
+ << " length " << dupe->mLength
+ << " size " << dupe->mSize
+ << " id " << dupe->mFileID
+ << " type " << dupe->mFileType
+ << llendl;
+ llwarns << "VFS: Index size " << index_size << llendl;
+ llwarns << "VFS: INDEX CORRUPT" << llendl;
+ vfs_corrupt = TRUE;
+ break;
+ }
+ else
+ {
+ found_files[*block] = block;
+ }
+ }
+ else
+ {
+ if (block->mLength)
+ {
+ llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " corrupt on disk" << llendl;
+ }
+ // else this is just a hole
+ }
+ }
+
+ delete[] buffer;
+
+ if (vfs_corrupt)
+ {
+ for (std::vector<LLVFSFileBlock*>::iterator iter = audit_blocks.begin();
+ iter != audit_blocks.end(); ++iter)
+ {
+ delete *iter;
+ }
+ audit_blocks.clear();
+ return;
+ }
+
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileBlock* block = (*it).second;
+
+ if (block->mSize > 0)
+ {
+ if (! found_files.count(*block))
+ {
+ llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " in memory, not on disk, loc " << block->mIndexLocation<< llendl;
+ fseek(mIndexFP, block->mIndexLocation, SEEK_SET);
+ U8 buf[LLVFSFileBlock::SERIAL_SIZE];
+ fread(buf, LLVFSFileBlock::SERIAL_SIZE, 1, mIndexFP);
+
+ LLVFSFileBlock disk_block;
+ disk_block.deserialize(buf, block->mIndexLocation);
+
+ llwarns << "Instead found " << disk_block.mFileID << ":" << block->mFileType << llendl;
+ }
+ else
+ {
+ block = found_files.find(*block)->second;
+ found_files.erase(*block);
+ delete block;
+ }
+ }
+ }
+
+ for (std::map<LLVFSFileSpecifier, LLVFSFileBlock*>::iterator iter = found_files.begin();
+ iter != found_files.end(); iter++)
+ {
+ LLVFSFileBlock* block = iter->second;
+ llwarns << "VFile " << block->mFileID << ":" << block->mFileType << " szie:" << block->mSize << " leftover" << llendl;
+ }
+
+ llinfos << "VFS: audit OK" << llendl;
+ // mutex released by LLMutexLock() destructor.
+}
+
+
+// quick check for uninitialized blocks
+// Slow, do not call in release.
+void LLVFS::checkMem()
+{
+ lockData();
+
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileBlock *block = (*it).second;
+ llassert(block->mFileType >= LLAssetType::AT_NONE &&
+ block->mFileType < LLAssetType::AT_COUNT &&
+ block->mFileID != LLUUID::null);
+
+ for (std::deque<S32>::iterator iter = mIndexHoles.begin();
+ iter != mIndexHoles.end(); ++iter)
+ {
+ S32 index_loc = *iter;
+ if (index_loc == block->mIndexLocation)
+ {
+ llwarns << "VFile block " << block->mFileID << ":" << block->mFileType << " is marked as a hole" << llendl;
+ }
+ }
+ }
+
+ llinfos << "VFS: mem check OK" << llendl;
+
+ unlockData();
+}
+
+void LLVFS::dumpLockCounts()
+{
+ S32 i;
+ for (i = 0; i < VFSLOCK_COUNT; i++)
+ {
+ llinfos << "LockType: " << i << ": " << mLockCounts[i] << llendl;
+ }
+}
+
+void LLVFS::dumpStatistics()
+{
+ lockData();
+
+ // Investigate file blocks.
+ std::map<S32, S32> size_counts;
+ std::map<U32, S32> location_counts;
+ std::map<LLAssetType::EType, std::pair<S32,S32> > filetype_counts;
+
+ S32 max_file_size = 0;
+ S32 total_file_size = 0;
+ S32 invalid_file_count = 0;
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileBlock *file_block = (*it).second;
+ if (file_block->mLength == BLOCK_LENGTH_INVALID)
+ {
+ invalid_file_count++;
+ }
+ else if (file_block->mLength <= 0)
+ {
+ llinfos << "Bad file block at: " << file_block->mLocation << "\tLength: " << file_block->mLength << "\t" << file_block->mFileID << "\t" << file_block->mFileType << llendl;
+ size_counts[file_block->mLength]++;
+ location_counts[file_block->mLocation]++;
+ }
+ else
+ {
+ total_file_size += file_block->mLength;
+ }
+
+ if (file_block->mLength > max_file_size)
+ {
+ max_file_size = file_block->mLength;
+ }
+
+ filetype_counts[file_block->mFileType].first++;
+ filetype_counts[file_block->mFileType].second += file_block->mLength;
+ }
+
+ for (std::map<S32,S32>::iterator it = size_counts.begin(); it != size_counts.end(); ++it)
+ {
+ S32 size = it->first;
+ S32 size_count = it->second;
+ llinfos << "Bad files size " << size << " count " << size_count << llendl;
+ }
+ for (std::map<U32,S32>::iterator it = location_counts.begin(); it != location_counts.end(); ++it)
+ {
+ U32 location = it->first;
+ S32 location_count = it->second;
+ llinfos << "Bad files location " << location << " count " << location_count << llendl;
+ }
+
+ // Investigate free list.
+ S32 max_free_size = 0;
+ S32 total_free_size = 0;
+ std::map<S32, S32> free_length_counts;
+ for (blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin(),
+ end = mFreeBlocksByLocation.end();
+ iter != end; iter++)
+ {
+ LLVFSBlock *free_block = iter->second;
+ if (free_block->mLength <= 0)
+ {
+ llinfos << "Bad free block at: " << free_block->mLocation << "\tLength: " << free_block->mLength << llendl;
+ }
+ else
+ {
+ llinfos << "Block: " << free_block->mLocation
+ << "\tLength: " << free_block->mLength
+ << "\tEnd: " << free_block->mLocation + free_block->mLength
+ << llendl;
+ total_free_size += free_block->mLength;
+ }
+
+ if (free_block->mLength > max_free_size)
+ {
+ max_free_size = free_block->mLength;
+ }
+
+ free_length_counts[free_block->mLength]++;
+ }
+
+ // Dump histogram of free block sizes
+ for (std::map<S32,S32>::iterator it = free_length_counts.begin(); it != free_length_counts.end(); ++it)
+ {
+ llinfos << "Free length " << it->first << " count " << it->second << llendl;
+ }
+
+ llinfos << "Invalid blocks: " << invalid_file_count << llendl;
+ llinfos << "File blocks: " << mFileBlocks.size() << llendl;
+
+ S32 length_list_count = (S32)mFreeBlocksByLength.size();
+ S32 location_list_count = (S32)mFreeBlocksByLocation.size();
+ if (length_list_count == location_list_count)
+ {
+ llinfos << "Free list lengths match, free blocks: " << location_list_count << llendl;
+ }
+ else
+ {
+ llwarns << "Free list lengths do not match!" << llendl;
+ llwarns << "By length: " << length_list_count << llendl;
+ llwarns << "By location: " << location_list_count << llendl;
+ }
+ llinfos << "Max file: " << max_file_size/1024 << "K" << llendl;
+ llinfos << "Max free: " << max_free_size/1024 << "K" << llendl;
+ llinfos << "Total file size: " << total_file_size/1024 << "K" << llendl;
+ llinfos << "Total free size: " << total_free_size/1024 << "K" << llendl;
+ llinfos << "Sum: " << (total_file_size + total_free_size) << " bytes" << llendl;
+ llinfos << llformat("%.0f%% full",((F32)(total_file_size)/(F32)(total_file_size+total_free_size))*100.f) << llendl;
+
+ llinfos << " " << llendl;
+ for (std::map<LLAssetType::EType, std::pair<S32,S32> >::iterator iter = filetype_counts.begin();
+ iter != filetype_counts.end(); ++iter)
+ {
+ llinfos << "Type: " << LLAssetType::getDesc(iter->first)
+ << " Count: " << iter->second.first
+ << " Bytes: " << (iter->second.second>>20) << " MB" << llendl;
+ }
+
+ // Look for potential merges
+ {
+ blocks_location_map_t::iterator iter = mFreeBlocksByLocation.begin();
+ blocks_location_map_t::iterator end = mFreeBlocksByLocation.end();
+ LLVFSBlock *first_block = iter->second;
+ while(iter != end)
+ {
+ if (++iter == end)
+ break;
+ LLVFSBlock *second_block = iter->second;
+ if (first_block->mLocation + first_block->mLength == second_block->mLocation)
+ {
+ llinfos << "Potential merge at " << first_block->mLocation << llendl;
+ }
+ first_block = second_block;
+ }
+ }
+ unlockData();
+}
+
+// Debug Only!
+#include "llapr.h"
+void LLVFS::dumpFiles()
+{
+ lockData();
+
+ for (fileblock_map::iterator it = mFileBlocks.begin(); it != mFileBlocks.end(); ++it)
+ {
+ LLVFSFileSpecifier file_spec = it->first;
+ LLVFSFileBlock *file_block = it->second;
+ S32 length = file_block->mLength;
+ S32 size = file_block->mSize;
+ if (length != BLOCK_LENGTH_INVALID && size > 0)
+ {
+ LLUUID id = file_spec.mFileID;
+ LLAssetType::EType type = file_spec.mFileType;
+ U8* buffer = new U8[size];
+
+ unlockData();
+ getData(id, type, buffer, 0, size);
+ lockData();
+
+ LLString extention = ".data";
+ switch(type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ extention = ".jp2"; // ".j2c"; // IrfanView recognizes .jp2 -sjb
+ break;
+ default:
+ break;
+ }
+ LLString filename = id.getString() + extention;
+ llinfos << " Writing " << filename << llendl;
+ apr_file_t* file = ll_apr_file_open(filename, LL_APR_WB);
+ ll_apr_file_write(file, buffer, size);
+ apr_file_close(file);
+ delete[] buffer;
+ }
+ }
+
+ unlockData();
+}
+
+//============================================================================
+// protected
+//============================================================================
+
+// static
+FILE *LLVFS::openAndLock(const char *filename, const char *mode, BOOL read_lock)
+{
+#if LL_WINDOWS
+
+ return LLFile::_fsopen(filename, mode, (read_lock ? _SH_DENYWR : _SH_DENYRW));
+
+#else
+
+ FILE *fp;
+ int fd;
+
+ // first test the lock in a non-destructive way
+ if (strstr(mode, "w"))
+ {
+ fp = LLFile::fopen(filename, "rb");
+ if (fp)
+ {
+ fd = fileno(fp);
+ if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
+ {
+ fclose(fp);
+ return NULL;
+ }
+
+ fclose(fp);
+ }
+ }
+
+ // now actually open the file for use
+ fp = LLFile::fopen(filename, mode);
+ if (fp)
+ {
+ fd = fileno(fp);
+ if (flock(fd, (read_lock ? LOCK_SH : LOCK_EX) | LOCK_NB) == -1)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ }
+
+ return fp;
+
+#endif
+}
+
+// static
+void LLVFS::unlockAndClose(FILE *fp)
+{
+ if (fp)
+ {
+ // IW: we don't actually want to unlock on linux
+ // this is because a forked process can kill the parent's lock
+ // with an explicit unlock
+ // however, fclose() will implicitly remove the lock
+ // but only once both parent and child have closed the file
+ /*
+ #if !LL_WINDOWS
+ int fd = fileno(fp);
+ flock(fd, LOCK_UN);
+ #endif
+ */
+
+ fclose(fp);
+ }
+}
diff --git a/indra/llvfs/llvfs.h b/indra/llvfs/llvfs.h
new file mode 100644
index 0000000000..c3fb0cdf22
--- /dev/null
+++ b/indra/llvfs/llvfs.h
@@ -0,0 +1,151 @@
+/**
+ * @file llvfs.h
+ * @brief Definition of virtual file system
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVFS_H
+#define LL_LLVFS_H
+
+#include <stdio.h>
+#include <map>
+#include <deque>
+#include "lluuid.h"
+#include "linked_lists.h"
+#include "llassettype.h"
+#include "llthread.h"
+
+enum EVFSValid
+{
+ VFSVALID_UNKNOWN = 0,
+ VFSVALID_OK = 1,
+ VFSVALID_BAD_CORRUPT = 2,
+ VFSVALID_BAD_CANNOT_OPEN_READONLY = 3,
+ VFSVALID_BAD_CANNOT_CREATE = 4
+};
+
+// Lock types for open vfiles, pending async reads, and pending async appends
+// (There are no async normal writes, currently)
+enum EVFSLock
+{
+ VFSLOCK_OPEN = 0,
+ VFSLOCK_READ = 1,
+ VFSLOCK_APPEND = 2,
+
+ VFSLOCK_COUNT = 3
+};
+
+// internal classes
+class LLVFSBlock;
+class LLVFSFileBlock;
+class LLVFSFileSpecifier
+{
+public:
+ LLVFSFileSpecifier();
+ LLVFSFileSpecifier(const LLUUID &file_id, const LLAssetType::EType file_type);
+ bool operator<(const LLVFSFileSpecifier &rhs) const;
+ bool operator==(const LLVFSFileSpecifier &rhs) const;
+
+public:
+ LLUUID mFileID;
+ LLAssetType::EType mFileType;
+};
+
+class LLVFS
+{
+public:
+ // Pass 0 to not presize
+ LLVFS(const char *index_filename, const char *data_filename, const BOOL read_only, const U32 presize, const BOOL remove_after_crash);
+ ~LLVFS();
+
+ BOOL isValid() const { return (VFSVALID_OK == mValid); }
+ EVFSValid getValidState() const { return mValid; }
+
+ // ---------- The following fucntions lock/unlock mDataMutex ----------
+ BOOL getExists(const LLUUID &file_id, const LLAssetType::EType file_type);
+ S32 getSize(const LLUUID &file_id, const LLAssetType::EType file_type);
+
+ BOOL checkAvailable(S32 max_size);
+
+ S32 getMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type);
+ BOOL setMaxSize(const LLUUID &file_id, const LLAssetType::EType file_type, S32 max_size);
+
+ void renameFile(const LLUUID &file_id, const LLAssetType::EType file_type,
+ const LLUUID &new_id, const LLAssetType::EType &new_type);
+ void removeFile(const LLUUID &file_id, const LLAssetType::EType file_type);
+
+ S32 getData(const LLUUID &file_id, const LLAssetType::EType file_type, U8 *buffer, S32 location, S32 length);
+ S32 storeData(const LLUUID &file_id, const LLAssetType::EType file_type, const U8 *buffer, S32 location, S32 length);
+
+ void incLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
+ void decLock(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
+ BOOL isLocked(const LLUUID &file_id, const LLAssetType::EType file_type, EVFSLock lock);
+ // ----------------------------------------------------------------
+
+ // Used to trigger evil WinXP behavior of "preloading" entire file into memory.
+ void pokeFiles();
+
+ // Verify that the index file contents match the in-memory file structure
+ // Very slow, do not call routinely. JC
+ void audit();
+ // Check for uninitialized blocks. Slow, do not call in release. JC
+ void checkMem();
+ // for debugging, prints a map of the vfs
+ void dumpMap();
+ void dumpLockCounts();
+ void dumpStatistics();
+ void dumpFiles();
+
+protected:
+ void removeFileBlock(LLVFSFileBlock *fileblock);
+
+ void eraseBlockLength(LLVFSBlock *block);
+ void eraseBlock(LLVFSBlock *block);
+ void addFreeBlock(LLVFSBlock *block);
+ //void mergeFreeBlocks();
+ void useFreeSpace(LLVFSBlock *free_block, S32 length);
+ void sync(LLVFSFileBlock *block, BOOL remove = FALSE);
+ void presizeDataFile(const U32 size);
+
+ static FILE *openAndLock(const char *filename, const char *mode, BOOL read_lock);
+ static void unlockAndClose(FILE *fp);
+
+ // Can initiate LRU-based file removal to make space.
+ // The immune file block will not be removed.
+ LLVFSBlock *findFreeBlock(S32 size, LLVFSFileBlock *immune = NULL);
+
+ // lock/unlock data mutex (mDataMutex)
+ void lockData() { mDataMutex->lock(); }
+ void unlockData() { mDataMutex->unlock(); }
+
+protected:
+ LLMutex* mDataMutex;
+
+ typedef std::map<LLVFSFileSpecifier, LLVFSFileBlock*> fileblock_map;
+ fileblock_map mFileBlocks;
+
+ typedef std::multimap<S32, LLVFSBlock*> blocks_length_map_t;
+ blocks_length_map_t mFreeBlocksByLength;
+ typedef std::multimap<U32, LLVFSBlock*> blocks_location_map_t;
+ blocks_location_map_t mFreeBlocksByLocation;
+
+ FILE *mDataFP;
+ FILE *mIndexFP;
+
+ std::deque<S32> mIndexHoles;
+
+ char *mIndexFilename;
+ char *mDataFilename;
+ BOOL mReadOnly;
+
+ EVFSValid mValid;
+
+ S32 mLockCounts[VFSLOCK_COUNT];
+ BOOL mRemoveAfterCrash;
+};
+
+extern LLVFS *gVFS;
+
+#endif
diff --git a/indra/llvfs/llvfsthread.cpp b/indra/llvfs/llvfsthread.cpp
new file mode 100644
index 0000000000..8ea98ab462
--- /dev/null
+++ b/indra/llvfs/llvfsthread.cpp
@@ -0,0 +1,294 @@
+/**
+ * @file llvfsthread.cpp
+ * @brief LLVFSThread implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llmath.h"
+#include "llvfsthread.h"
+#include "llstl.h"
+
+//============================================================================
+
+/*static*/ std::string LLVFSThread::sDataPath = "";
+
+/*static*/ LLVFSThread* LLVFSThread::sLocal = NULL;
+
+//============================================================================
+// Run on MAIN thread
+//static
+void LLVFSThread::initClass(bool local_is_threaded, bool local_run_always)
+{
+ llassert(sLocal == NULL);
+ sLocal = new LLVFSThread(local_is_threaded, local_run_always);
+}
+
+//static
+S32 LLVFSThread::updateClass(U32 ms_elapsed)
+{
+ sLocal->update(ms_elapsed);
+ return sLocal->getPending();
+}
+
+//static
+void LLVFSThread::cleanupClass()
+{
+ sLocal->setQuitting();
+ while (sLocal->getPending())
+ {
+ sLocal->update(0);
+ }
+ delete sLocal;
+ sLocal = 0;
+}
+
+//----------------------------------------------------------------------------
+
+LLVFSThread::LLVFSThread(bool threaded, bool runalways) :
+ LLQueuedThread("VFS", threaded, runalways)
+{
+}
+
+LLVFSThread::~LLVFSThread()
+{
+ // ~LLQueuedThread() will be called here
+}
+
+//----------------------------------------------------------------------------
+
+LLVFSThread::handle_t LLVFSThread::read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes, U32 priority, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ priority = llmax(priority, (U32)PRIORITY_LOW); // All reads are at least PRIORITY_LOW
+ Request* req = new Request(handle, priority, flags, FILE_READ, vfs, file_id, file_type,
+ buffer, offset, numbytes);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+S32 LLVFSThread::readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_READ, vfs, file_id, file_type,
+ buffer, offset, numbytes);
+
+ S32 res = addRequest(req) ? 1 : 0;
+ if (res == 0)
+ {
+ llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ }
+ else
+ {
+ llverify(waitForResult(handle, false) == true);
+ res = req->getBytesRead();
+ completeRequest(handle);
+ }
+ return res;
+}
+
+LLVFSThread::handle_t LLVFSThread::write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, 0, flags, FILE_WRITE, vfs, file_id, file_type,
+ buffer, offset, numbytes);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+S32 LLVFSThread::writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes)
+{
+ handle_t handle = generateHandle();
+
+ Request* req = new Request(handle, PRIORITY_IMMEDIATE, 0, FILE_WRITE, vfs, file_id, file_type,
+ buffer, offset, numbytes);
+
+ S32 res = addRequest(req) ? 1 : 0;
+ if (res == 0)
+ {
+ llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ }
+ else
+ {
+ llverify(waitForResult(handle, false) == true);
+ res = req->getBytesRead();
+ completeRequest(handle);
+ }
+ return res;
+}
+
+
+LLVFSThread::handle_t LLVFSThread::rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags)
+{
+ handle_t handle = generateHandle();
+
+ LLUUID* new_idp = new LLUUID(new_id); // deleted with Request
+ // new_type is passed as "numbytes"
+ Request* req = new Request(handle, 0, flags, FILE_RENAME, vfs, file_id, file_type,
+ (U8*)new_idp, 0, (S32)new_type);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ llerrs << "LLVFSThread::read called after LLVFSThread::cleanupClass()" << llendl;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+bool LLVFSThread::processRequest(QueuedRequest* qreq)
+{
+ Request *req = (Request*)qreq;
+
+ bool complete = req->processIO();
+
+ return complete;
+}
+
+//============================================================================
+
+LLVFSThread::Request::Request(handle_t handle, U32 priority, U32 flags,
+ operation_t op, LLVFS* vfs,
+ const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes) :
+ QueuedRequest(handle, priority, flags),
+ mOperation(op),
+ mVFS(vfs),
+ mFileID(file_id),
+ mFileType(file_type),
+ mBuffer(buffer),
+ mOffset(offset),
+ mBytes(numbytes),
+ mBytesRead(0)
+{
+ llassert(mBuffer);
+
+ if (numbytes <= 0 && mOperation != FILE_RENAME)
+ {
+ llwarns << "LLVFSThread: Request with numbytes = " << numbytes
+ << " operation = " << op
+ << " offset " << offset
+ << " file_type " << file_type << llendl;
+ }
+ if (mOperation == FILE_WRITE)
+ {
+ S32 blocksize = mVFS->getMaxSize(mFileID, mFileType);
+ if (blocksize < 0)
+ {
+ llwarns << "VFS write to temporary block (shouldn't happen)" << llendl;
+ }
+ mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND);
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ mVFS->incLock(mFileID, mFileType, VFSLOCK_APPEND);
+ }
+ else // if (mOperation == FILE_READ)
+ {
+ mVFS->incLock(mFileID, mFileType, VFSLOCK_READ);
+ }
+}
+
+// dec locks as soon as a request finishes
+void LLVFSThread::Request::finishRequest()
+{
+ if (mOperation == FILE_WRITE)
+ {
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND);
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_APPEND);
+ }
+ else // if (mOperation == FILE_READ)
+ {
+ mVFS->decLock(mFileID, mFileType, VFSLOCK_READ);
+ }
+}
+
+void LLVFSThread::Request::deleteRequest()
+{
+ if (getStatus() == STATUS_QUEUED || getStatus() == STATUS_ABORT)
+ {
+ llerrs << "Attempt to delete a queued LLVFSThread::Request!" << llendl;
+ }
+ if (mOperation == FILE_WRITE)
+ {
+ if (mFlags & AUTO_DELETE)
+ {
+ delete [] mBuffer;
+ }
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ LLUUID* new_idp = (LLUUID*)mBuffer;
+ delete new_idp;
+ }
+ LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+bool LLVFSThread::Request::processIO()
+{
+ bool complete = false;
+ if (mOperation == FILE_READ)
+ {
+ llassert(mOffset >= 0);
+ mBytesRead = mVFS->getData(mFileID, mFileType, mBuffer, mOffset, mBytes);
+ complete = true;
+ //llinfos << llformat("LLVFSThread::READ '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
+ }
+ else if (mOperation == FILE_WRITE)
+ {
+ mBytesRead = mVFS->storeData(mFileID, mFileType, mBuffer, mOffset, mBytes);
+ complete = true;
+ //llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
+ }
+ else if (mOperation == FILE_RENAME)
+ {
+ LLUUID* new_idp = (LLUUID*)mBuffer;
+ LLAssetType::EType new_type = (LLAssetType::EType)mBytes;
+ mVFS->renameFile(mFileID, mFileType, *new_idp, new_type);
+ complete = true;
+ //llinfos << llformat("LLVFSThread::WRITE '%s': %d bytes arg:%d",getFilename(),mBytesRead) << llendl;
+ }
+ else
+ {
+ llerrs << llformat("LLVFSThread::unknown operation: %d", mOperation) << llendl;
+ }
+ return complete;
+}
+
+//============================================================================
diff --git a/indra/llvfs/llvfsthread.h b/indra/llvfs/llvfsthread.h
new file mode 100644
index 0000000000..14a2fe0ba7
--- /dev/null
+++ b/indra/llvfs/llvfsthread.h
@@ -0,0 +1,125 @@
+/**
+ * @file llvfsthread.h
+ * @brief LLVFSThread definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVFSTHREAD_H
+#define LL_LLVFSTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llapr.h"
+
+#include "llqueuedthread.h"
+
+#include "llvfs.h"
+
+//============================================================================
+
+class LLVFSThread : public LLQueuedThread
+{
+ //------------------------------------------------------------------------
+public:
+ enum operation_t {
+ FILE_READ,
+ FILE_WRITE,
+ FILE_RENAME
+ };
+
+ //------------------------------------------------------------------------
+public:
+
+ class Request : public QueuedRequest
+ {
+ protected:
+ ~Request() {}; // use deleteRequest()
+
+ public:
+ Request(handle_t handle, U32 priority, U32 flags,
+ operation_t op, LLVFS* vfs,
+ const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes);
+
+ S32 getBytesRead()
+ {
+ return mBytesRead;
+ }
+ S32 getOperation()
+ {
+ return mOperation;
+ }
+ U8* getBuffer()
+ {
+ return mBuffer;
+ }
+ LLVFS* getVFS()
+ {
+ return mVFS;
+ }
+ std::string getFilename()
+ {
+ char tbuf[40];
+ mFileID.toString(tbuf);
+ return std::string(tbuf);
+ }
+
+ /*virtual*/ void finishRequest();
+ /*virtual*/ void deleteRequest();
+
+ bool processIO();
+
+ private:
+ operation_t mOperation;
+
+ LLVFS* mVFS;
+ LLUUID mFileID;
+ LLAssetType::EType mFileType;
+
+ U8* mBuffer; // dest for reads, source for writes, new UUID for rename
+ S32 mOffset; // offset into file, -1 = append (WRITE only)
+ S32 mBytes; // bytes to read from file, -1 = all (new mFileType for rename)
+ S32 mBytesRead; // bytes read from file
+ };
+
+ //------------------------------------------------------------------------
+public:
+ static std::string sDataPath;
+ static void setDataPath(const std::string& path) { sDataPath = path; }
+
+public:
+ LLVFSThread(bool threaded = TRUE, bool runalways = TRUE);
+ ~LLVFSThread();
+
+ // Return a Request handle
+ handle_t read(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes, U32 pri=PRIORITY_NORMAL, U32 flags = 0);
+ handle_t write(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes, U32 flags);
+ handle_t rename(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ const LLUUID &new_id, const LLAssetType::EType new_type, U32 flags);
+ // Return number of bytes read
+ S32 readImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes);
+ S32 writeImmediate(LLVFS* vfs, const LLUUID &file_id, const LLAssetType::EType file_type,
+ U8* buffer, S32 offset, S32 numbytes);
+
+ /*virtual*/ bool processRequest(QueuedRequest* req);
+
+ static void initClass(bool local_is_threaded = TRUE, bool run_always = TRUE); // Setup sLocal
+ static S32 updateClass(U32 ms_elapsed);
+ static void cleanupClass(); // Delete sLocal
+
+public:
+ static LLVFSThread* sLocal; // Default worker thread
+};
+
+//============================================================================
+
+
+#endif // LL_LLVFSTHREAD_H
diff --git a/indra/llwindow/lldxhardware.cpp b/indra/llwindow/lldxhardware.cpp
new file mode 100644
index 0000000000..a972a29aa4
--- /dev/null
+++ b/indra/llwindow/lldxhardware.cpp
@@ -0,0 +1,508 @@
+/**
+ * @file lldxhardware.cpp
+ * @brief LLDXHardware implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifdef LL_WINDOWS
+
+// Culled from some Microsoft sample code
+
+#include "linden_common.h"
+
+#include <assert.h>
+#include <dxdiag.h>
+
+#include <boost/tokenizer.hpp>
+
+#include "lldxhardware.h"
+#include "llerror.h"
+
+#include "llstring.h"
+#include "llstl.h"
+
+void (*gWriteDebug)(const char* msg) = NULL;
+LLDXHardware gDXHardware;
+
+//-----------------------------------------------------------------------------
+// Defines, and constants
+//-----------------------------------------------------------------------------
+#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
+#define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } }
+#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
+
+std::string get_string(IDxDiagContainer *containerp, WCHAR *wszPropName)
+{
+ HRESULT hr;
+ VARIANT var;
+ WCHAR wszPropValue[256];
+
+ VariantInit( &var );
+ hr = containerp->GetProp(wszPropName, &var );
+ if( SUCCEEDED(hr) )
+ {
+ // Switch off the type. There's 4 different types:
+ switch( var.vt )
+ {
+ case VT_UI4:
+ swprintf( wszPropValue, L"%d", var.ulVal );
+ break;
+ case VT_I4:
+ swprintf( wszPropValue, L"%d", var.lVal );
+ break;
+ case VT_BOOL:
+ wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" );
+ break;
+ case VT_BSTR:
+ wcsncpy( wszPropValue, var.bstrVal, 255 );
+ wszPropValue[255] = 0;
+ break;
+ }
+ }
+ // Clear the variant (this is needed to free BSTR memory)
+ VariantClear( &var );
+
+ return utf16str_to_utf8str(wszPropValue);
+}
+
+
+LLVersion::LLVersion()
+{
+ mValid = FALSE;
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+}
+
+BOOL LLVersion::set(const std::string &version_string)
+{
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+ // Split the version string.
+ std::string str(version_string);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ S32 count = 0;
+ for (;(iter != tokens.end()) && (count < 4);++iter)
+ {
+ mFields[count] = atoi(iter->c_str());
+ count++;
+ }
+ if (count < 4)
+ {
+ //llwarns << "Potentially bogus version string!" << version_string << llendl;
+ for (i = 0; i < 4; i++)
+ {
+ mFields[i] = 0;
+ }
+ mValid = FALSE;
+ }
+ else
+ {
+ mValid = TRUE;
+ }
+ return mValid;
+}
+
+S32 LLVersion::getField(const S32 field_num)
+{
+ if (!mValid)
+ {
+ return -1;
+ }
+ else
+ {
+ return mFields[field_num];
+ }
+}
+
+LLString LLDXDriverFile::dump()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("Filename:");
+ gWriteDebug(mName.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("Ver:");
+ gWriteDebug(mVersionString.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("Date:");
+ gWriteDebug(mDateString.c_str());
+ gWriteDebug("\n");
+ }
+ llinfos << mFilepath << llendl;
+ llinfos << mName << llendl;
+ llinfos << mVersionString << llendl;
+ llinfos << mDateString << llendl;
+
+ return "";
+}
+
+LLDXDevice::~LLDXDevice()
+{
+ for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer());
+}
+
+std::string LLDXDevice::dump()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("StartDevice\n");
+ gWriteDebug("DeviceName:");
+ gWriteDebug(mName.c_str());
+ gWriteDebug("\n");
+ gWriteDebug("PCIString:");
+ gWriteDebug(mPCIString.c_str());
+ gWriteDebug("\n");
+ }
+ llinfos << llendl;
+ llinfos << "DeviceName:" << mName << llendl;
+ llinfos << "PCIString:" << mPCIString << llendl;
+ llinfos << "Drivers" << llendl;
+ llinfos << "-------" << llendl;
+ for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
+ end = mDriverFiles.end();
+ iter != end; iter++)
+ {
+ LLDXDriverFile *filep = iter->second;
+ filep->dump();
+ }
+ if (gWriteDebug)
+ {
+ gWriteDebug("EndDevice\n");
+ }
+
+ return "";
+}
+
+LLDXDriverFile *LLDXDevice::findDriver(const std::string &driver)
+{
+ for (driver_file_map_t::iterator iter = mDriverFiles.begin(),
+ end = mDriverFiles.end();
+ iter != end; iter++)
+ {
+ LLDXDriverFile *filep = iter->second;
+ if (!utf8str_compare_insensitive(filep->mName,driver))
+ {
+ return filep;
+ }
+ }
+
+ return NULL;
+}
+
+LLDXHardware::LLDXHardware()
+{
+ mVRAM = 0;
+ gWriteDebug = NULL;
+}
+
+void LLDXHardware::cleanup()
+{
+ for_each(mDevices.begin(), mDevices.end(), DeletePairedPointer());
+}
+
+LLString LLDXHardware::dumpDevices()
+{
+ if (gWriteDebug)
+ {
+ gWriteDebug("\n");
+ gWriteDebug("StartAllDevices\n");
+ }
+ for (device_map_t::iterator iter = mDevices.begin(),
+ end = mDevices.end();
+ iter != end; iter++)
+ {
+ LLDXDevice *devicep = iter->second;
+ devicep->dump();
+ }
+ if (gWriteDebug)
+ {
+ gWriteDebug("EndAllDevices\n\n");
+ }
+ return "";
+}
+
+LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices)
+{
+ // Iterate through different devices tokenized in devices string
+ std::string str(devices);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ for (;iter != tokens.end();++iter)
+ {
+ std::string dev_str = *iter;
+ for (device_map_t::iterator iter = mDevices.begin(),
+ end = mDevices.end();
+ iter != end; iter++)
+ {
+ LLDXDevice *devicep = iter->second;
+ if ((devicep->mVendorID == vendor)
+ && (devicep->mDeviceID == dev_str))
+ {
+ return devicep;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+BOOL LLDXHardware::getInfo(BOOL vram_only)
+{
+ LLTimer hw_timer;
+ BOOL ok = FALSE;
+ HRESULT hr;
+
+ CoInitialize(NULL);
+
+ IDxDiagProvider *dx_diag_providerp = NULL;
+ IDxDiagContainer *dx_diag_rootp = NULL;
+ IDxDiagContainer *devices_containerp = NULL;
+ IDxDiagContainer *system_device_containerp= NULL;
+ IDxDiagContainer *device_containerp = NULL;
+ IDxDiagContainer *file_containerp = NULL;
+ IDxDiagContainer *driver_containerp = NULL;
+
+ // CoCreate a IDxDiagProvider*
+ llinfos << "CoCreateInstance IID_IDxDiagProvider" << llendl;
+ hr = CoCreateInstance(CLSID_DxDiagProvider,
+ NULL,
+ CLSCTX_INPROC_SERVER,
+ IID_IDxDiagProvider,
+ (LPVOID*) &dx_diag_providerp);
+
+ if (FAILED(hr))
+ {
+ llwarns << "No DXDiag provider found! DirectX 9 not installed!" << llendl;
+ gWriteDebug("No DXDiag provider found! DirectX 9 not installed!\n");
+ goto LCleanup;
+ }
+ if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed
+ {
+ // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
+ // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are
+ // digital signed as logo'd by WHQL which may connect via internet to update
+ // WHQL certificates.
+ DXDIAG_INIT_PARAMS dx_diag_init_params;
+ ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS));
+
+ dx_diag_init_params.dwSize = sizeof(DXDIAG_INIT_PARAMS);
+ dx_diag_init_params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
+ dx_diag_init_params.bAllowWHQLChecks = TRUE;
+ dx_diag_init_params.pReserved = NULL;
+
+ llinfos << "dx_diag_providerp->Initialize" << llendl;
+ hr = dx_diag_providerp->Initialize(&dx_diag_init_params);
+ if(FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ llinfos << "dx_diag_providerp->GetRootContainer" << llendl;
+ hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp );
+ if(FAILED(hr) || !dx_diag_rootp)
+ {
+ goto LCleanup;
+ }
+
+ HRESULT hr;
+
+ // Get display driver information
+ llinfos << "dx_diag_rootp->GetChildContainer" << llendl;
+ hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp);
+ if(FAILED(hr) || !devices_containerp)
+ {
+ goto LCleanup;
+ }
+
+ // Get device 0
+ llinfos << "devices_containerp->GetChildContainer" << llendl;
+ hr = devices_containerp->GetChildContainer(L"0", &device_containerp);
+ if(FAILED(hr) || !device_containerp)
+ {
+ goto LCleanup;
+ }
+
+ // Get the English VRAM string
+ std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish");
+
+ // We don't need the device any more
+ SAFE_RELEASE(device_containerp);
+
+ // Dump the string as an int into the structure
+ char *stopstring;
+ mVRAM = strtol(ram_str.c_str(), &stopstring, 10);
+ llinfos << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << llendl;
+
+ if (vram_only)
+ {
+ ok = TRUE;
+ goto LCleanup;
+ }
+
+ // Now let's get device and driver information
+ // Get the IDxDiagContainer object called "DxDiag_SystemDevices".
+ // This call may take some time while dxdiag gathers the info.
+ DWORD num_devices = 0;
+ WCHAR wszContainer[256];
+ llinfos << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << llendl;
+ hr = dx_diag_rootp->GetChildContainer(L"DxDiag_SystemDevices", &system_device_containerp);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = system_device_containerp->GetNumberOfChildContainers(&num_devices);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ llinfos << "DX9 iterating over devices" << llendl;
+ S32 device_num = 0;
+ for (device_num = 0; device_num < (S32)num_devices; device_num++)
+ {
+ hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp);
+ if (FAILED(hr) || device_containerp == NULL)
+ {
+ goto LCleanup;
+ }
+
+ std::string device_name = get_string(device_containerp, L"szDescription");
+ std::string device_id = get_string(device_containerp, L"szDeviceID");
+
+ LLDXDevice *dxdevicep = new LLDXDevice;
+ dxdevicep->mName = device_name;
+ dxdevicep->mPCIString = device_id;
+ mDevices[dxdevicep->mPCIString] = dxdevicep;
+
+ // Split the PCI string based on vendor, device, subsys, rev.
+ std::string str(device_id);
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("&\\", "", boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+
+ tokenizer::iterator iter = tokens.begin();
+ S32 count = 0;
+ BOOL valid = TRUE;
+ for (;(iter != tokens.end()) && (count < 3);++iter)
+ {
+ switch (count)
+ {
+ case 0:
+ if (strcmp(iter->c_str(), "PCI"))
+ {
+ valid = FALSE;
+ }
+ break;
+ case 1:
+ dxdevicep->mVendorID = iter->c_str();
+ break;
+ case 2:
+ dxdevicep->mDeviceID = iter->c_str();
+ break;
+ default:
+ // Ignore it
+ break;
+ }
+ count++;
+ }
+
+
+ // Now, iterate through the related drivers
+ hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp);
+ if (FAILED(hr) || !driver_containerp)
+ {
+ goto LCleanup;
+ }
+
+ DWORD num_files = 0;
+ hr = driver_containerp->GetNumberOfChildContainers(&num_files);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ S32 file_num = 0;
+ for (file_num = 0; file_num < (S32)num_files; file_num++ )
+ {
+ hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256);
+ if (FAILED(hr))
+ {
+ goto LCleanup;
+ }
+
+ hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp);
+ if (FAILED(hr) || file_containerp == NULL)
+ {
+ goto LCleanup;
+ }
+
+ std::string driver_path = get_string(file_containerp, L"szPath");
+ std::string driver_name = get_string(file_containerp, L"szName");
+ std::string driver_version = get_string(file_containerp, L"szVersion");
+ std::string driver_date = get_string(file_containerp, L"szDatestampEnglish");
+
+ LLDXDriverFile *dxdriverfilep = new LLDXDriverFile;
+ dxdriverfilep->mName = driver_name;
+ dxdriverfilep->mFilepath= driver_path;
+ dxdriverfilep->mVersionString = driver_version;
+ dxdriverfilep->mVersion.set(driver_version);
+ dxdriverfilep->mDateString = driver_date;
+
+ dxdevicep->mDriverFiles[driver_name] = dxdriverfilep;
+
+ SAFE_RELEASE(file_containerp);
+ }
+ SAFE_RELEASE(device_containerp);
+ }
+ }
+
+ dumpDevices();
+ ok = TRUE;
+
+LCleanup:
+ if (!ok)
+ {
+ llwarns << "DX9 probe failed" << llendl;
+ gWriteDebug("DX9 probe failed\n");
+ }
+
+ SAFE_RELEASE(file_containerp);
+ SAFE_RELEASE(driver_containerp);
+ SAFE_RELEASE(device_containerp);
+ SAFE_RELEASE(devices_containerp);
+ SAFE_RELEASE(dx_diag_rootp);
+ SAFE_RELEASE(dx_diag_providerp);
+
+ CoUninitialize();
+
+ return ok;
+}
+
+void LLDXHardware::setWriteDebugFunc(void (*func)(const char*))
+{
+ gWriteDebug = func;
+}
+
+#endif
diff --git a/indra/llwindow/lldxhardware.h b/indra/llwindow/lldxhardware.h
new file mode 100644
index 0000000000..00ae525372
--- /dev/null
+++ b/indra/llwindow/lldxhardware.h
@@ -0,0 +1,90 @@
+/**
+ * @file lldxhardware.h
+ * @brief LLDXHardware definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDXHARDWARE_H
+#define LL_LLDXHARDWARE_H
+
+#include <map>
+
+#include "stdtypes.h"
+#include "llstring.h"
+
+class LLVersion
+{
+public:
+ LLVersion();
+ BOOL set(const std::string &version_string);
+ S32 getField(const S32 field_num);
+protected:
+ std::string mVersionString;
+ S32 mFields[4];
+ BOOL mValid;
+};
+
+class LLDXDriverFile
+{
+public:
+ LLString dump();
+
+public:
+ std::string mFilepath;
+ std::string mName;
+ std::string mVersionString;
+ LLVersion mVersion;
+ std::string mDateString;
+};
+
+class LLDXDevice
+{
+public:
+ ~LLDXDevice();
+ std::string dump();
+
+ LLDXDriverFile *findDriver(const std::string &driver);
+public:
+ std::string mName;
+ std::string mPCIString;
+ std::string mVendorID;
+ std::string mDeviceID;
+
+ typedef std::map<std::string, LLDXDriverFile *> driver_file_map_t;
+ driver_file_map_t mDriverFiles;
+};
+
+
+class LLDXHardware
+{
+public:
+ LLDXHardware();
+ void setWriteDebugFunc(void (*func)(const char*));
+ void cleanup();
+
+ // Returns TRUE on success.
+ // vram_only TRUE does a "light" probe.
+ BOOL getInfo(BOOL vram_only);
+
+ S32 getVRAM() const { return mVRAM; }
+
+ // Find a particular device that matches the following specs.
+ // Empty strings indicate that you don't care.
+ // You can separate multiple devices with '|' chars to indicate you want
+ // ANY of them to match and return.
+ LLDXDevice *findDevice(const std::string &vendor, const std::string &devices);
+
+ LLString dumpDevices();
+public:
+ typedef std::map<std::string, LLDXDevice *> device_map_t;
+ device_map_t mDevices;
+protected:
+ S32 mVRAM;
+};
+
+extern void (*gWriteDebug)(const char* msg);
+extern LLDXHardware gDXHardware;
+
+#endif // LL_LLDXHARDWARE_H
diff --git a/indra/llwindow/llkeyboard.cpp b/indra/llwindow/llkeyboard.cpp
new file mode 100644
index 0000000000..ee42f53571
--- /dev/null
+++ b/indra/llwindow/llkeyboard.cpp
@@ -0,0 +1,387 @@
+/**
+ * @file llkeyboard.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "indra_constants.h"
+#include "llkeyboard.h"
+
+#include "llwindow.h"
+
+
+//
+// Globals
+//
+
+LLKeyboard *gKeyboard = NULL;
+
+//static
+std::map<KEY,LLString> LLKeyboard::sKeysToNames;
+std::map<LLString,KEY> LLKeyboard::sNamesToKeys;
+
+//
+// Class Implementation
+//
+
+LLKeyboard::LLKeyboard() : mCallbacks(NULL), mNumpadDistinct(ND_NUMLOCK_OFF)
+{
+ S32 i;
+
+ // Constructor for LLTimer inits each timer. We want them to
+ // be constructed without being initialized, so we shut them down here.
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyLevelFrameCount[i] = 0;
+ mKeyLevel[i] = FALSE;
+ mKeyUp[i] = FALSE;
+ mKeyDown[i] = FALSE;
+ mKeyRepeated[i] = FALSE;
+ }
+
+ mInsertMode = LL_KIM_INSERT;
+ mCurTranslatedKey = KEY_NONE;
+
+ addKeyName(' ', "Space" );
+ addKeyName(KEY_RETURN, "Enter" );
+ addKeyName(KEY_LEFT, "Left" );
+ addKeyName(KEY_RIGHT, "Right" );
+ addKeyName(KEY_UP, "Up" );
+ addKeyName(KEY_DOWN, "Down" );
+ addKeyName(KEY_ESCAPE, "Esc" );
+ addKeyName(KEY_HOME, "Home" );
+ addKeyName(KEY_END, "End" );
+ addKeyName(KEY_PAGE_UP, "PgUp" );
+ addKeyName(KEY_PAGE_DOWN, "PgDn" );
+ addKeyName(KEY_F1, "F1" );
+ addKeyName(KEY_F2, "F2" );
+ addKeyName(KEY_F3, "F3" );
+ addKeyName(KEY_F4, "F4" );
+ addKeyName(KEY_F5, "F5" );
+ addKeyName(KEY_F6, "F6" );
+ addKeyName(KEY_F7, "F7" );
+ addKeyName(KEY_F8, "F8" );
+ addKeyName(KEY_F9, "F9" );
+ addKeyName(KEY_F10, "F10" );
+ addKeyName(KEY_F11, "F11" );
+ addKeyName(KEY_F12, "F12" );
+ addKeyName(KEY_TAB, "Tab" );
+ addKeyName(KEY_ADD, "Add" );
+ addKeyName(KEY_SUBTRACT, "Subtract" );
+ addKeyName(KEY_MULTIPLY, "Multiply" );
+ addKeyName(KEY_DIVIDE, "Divide" );
+ addKeyName(KEY_PAD_LEFT, "PAD_LEFT" );
+ addKeyName(KEY_PAD_RIGHT, "PAD_RIGHT" );
+ addKeyName(KEY_PAD_DOWN, "PAD_DOWN" );
+ addKeyName(KEY_PAD_UP, "PAD_UP" );
+ addKeyName(KEY_PAD_HOME, "PAD_HOME" );
+ addKeyName(KEY_PAD_END, "PAD_END" );
+ addKeyName(KEY_PAD_PGUP, "PAD_PGUP" );
+ addKeyName(KEY_PAD_PGDN, "PAD_PGDN" );
+ addKeyName(KEY_PAD_CENTER, "PAD_CENTER" );
+ addKeyName(KEY_PAD_INS, "PAD_INS" );
+ addKeyName(KEY_PAD_DEL, "PAD_DEL" );
+ addKeyName(KEY_PAD_RETURN, "PAD_Enter" );
+ addKeyName(KEY_BUTTON0, "PAD_BUTTON0" );
+ addKeyName(KEY_BUTTON1, "PAD_BUTTON1" );
+ addKeyName(KEY_BUTTON2, "PAD_BUTTON2" );
+ addKeyName(KEY_BUTTON3, "PAD_BUTTON3" );
+ addKeyName(KEY_BUTTON4, "PAD_BUTTON4" );
+ addKeyName(KEY_BUTTON5, "PAD_BUTTON5" );
+ addKeyName(KEY_BUTTON6, "PAD_BUTTON6" );
+ addKeyName(KEY_BUTTON7, "PAD_BUTTON7" );
+ addKeyName(KEY_BUTTON8, "PAD_BUTTON8" );
+ addKeyName(KEY_BUTTON9, "PAD_BUTTON9" );
+ addKeyName(KEY_BUTTON10, "PAD_BUTTON10" );
+ addKeyName(KEY_BUTTON11, "PAD_BUTTON11" );
+ addKeyName(KEY_BUTTON12, "PAD_BUTTON12" );
+ addKeyName(KEY_BUTTON13, "PAD_BUTTON13" );
+ addKeyName(KEY_BUTTON14, "PAD_BUTTON14" );
+ addKeyName(KEY_BUTTON15, "PAD_BUTTON15" );
+
+ addKeyName(KEY_BACKSPACE, "Backsp" );
+ addKeyName(KEY_DELETE, "Del" );
+ addKeyName(KEY_SHIFT, "Shift" );
+ addKeyName(KEY_CONTROL, "Ctrl" );
+ addKeyName(KEY_ALT, "Alt" );
+ addKeyName(KEY_HYPHEN, "-" );
+ addKeyName(KEY_EQUALS, "=" );
+ addKeyName(KEY_INSERT, "Ins" );
+ addKeyName(KEY_CAPSLOCK, "CapsLock" );
+}
+
+
+LLKeyboard::~LLKeyboard()
+{
+ // nothing
+}
+
+void LLKeyboard::addKeyName(KEY key, const LLString& name)
+{
+ sKeysToNames[key] = name;
+ LLString nameuc = name;
+ LLString::toUpper(nameuc);
+ sNamesToKeys[nameuc] = key;
+}
+
+// BUG this has to be called when an OS dialog is shown, otherwise modifier key state
+// is wrong because the keyup event is never received by the main window. JC
+void LLKeyboard::resetKeys()
+{
+ S32 i;
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ if( mKeyLevel[i] )
+ {
+ mKeyLevel[i] = FALSE;
+ mKeyLevelFrameCount[i] = 0;
+ }
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyUp[i] = FALSE;
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyDown[i] = FALSE;
+ }
+
+ for (i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyRepeated[i] = FALSE;
+ }
+}
+
+
+BOOL LLKeyboard::translateKey(const U16 os_key, KEY *out_key)
+{
+ std::map<U16, KEY>::iterator iter;
+
+ // Only translate keys in the map, ignore all other keys for now
+ iter = mTranslateKeyMap.find(os_key);
+ if (iter == mTranslateKeyMap.end())
+ {
+ //llwarns << "Unknown virtual key " << os_key << llendl;
+ *out_key = 0;
+ return FALSE;
+ }
+ else
+ {
+ *out_key = iter->second;
+ return TRUE;
+ }
+}
+
+
+U16 LLKeyboard::inverseTranslateKey(const KEY translated_key)
+{
+ std::map<KEY, U16>::iterator iter;
+ iter = mInvTranslateKeyMap.find(translated_key);
+ if (iter == mInvTranslateKeyMap.end())
+ {
+ return 0;
+ }
+ else
+ {
+ return iter->second;
+ }
+}
+
+
+BOOL LLKeyboard::handleTranslatedKeyDown(KEY translated_key, U32 translated_mask)
+{
+ BOOL handled = FALSE;
+ BOOL repeated = FALSE;
+
+ // is this the first time the key went down?
+ // if so, generate "character" message
+ if( !mKeyLevel[translated_key] )
+ {
+ mKeyLevel[translated_key] = TRUE;
+ mKeyLevelTimer[translated_key].reset();
+ }
+ else
+ {
+ // Level is already down, assume it's repeated.
+ repeated = TRUE;
+ mKeyRepeated[translated_key] = TRUE;
+ }
+
+ mKeyDown[translated_key] = TRUE;
+ mCurTranslatedKey = (KEY)translated_key;
+ handled = mCallbacks->handleTranslatedKeyDown(translated_key, translated_mask, repeated);
+ return handled;
+}
+
+
+BOOL LLKeyboard::handleTranslatedKeyUp(KEY translated_key, U32 translated_mask)
+{
+ BOOL handled = FALSE;
+ if( mKeyLevel[translated_key] )
+ {
+ mKeyLevel[translated_key] = FALSE;
+ mKeyLevelFrameCount[translated_key] = 0;
+
+ // Only generate key up events if the key is thought to
+ // be down. This allows you to call resetKeys() in the
+ // middle of a frame and ignore subsequent KEY_UP
+ // messages in the same frame. This was causing the
+ // sequence W<return> in chat to move agents forward. JC
+ mKeyUp[translated_key] = TRUE;
+ mKeyRepeated[translated_key] = FALSE;
+ handled = mCallbacks->handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "keyup -" << translated_key << "-" << llendl;
+
+ return handled;
+}
+
+
+void LLKeyboard::toggleInsertMode()
+{
+ if (LL_KIM_INSERT == mInsertMode)
+ {
+ mInsertMode = LL_KIM_OVERWRITE;
+ }
+ else
+ {
+ mInsertMode = LL_KIM_INSERT;
+ }
+}
+
+
+// Returns time in seconds since key was pressed.
+F32 LLKeyboard::getKeyElapsedTime(KEY key)
+{
+ if( mKeyLevel[key] )
+ {
+ return mKeyLevelTimer[key].getElapsedTimeF32();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// Returns time in frames since key was pressed.
+S32 LLKeyboard::getKeyElapsedFrameCount(KEY key)
+{
+ if( mKeyLevel[key] )
+ {
+ return mKeyLevelFrameCount[key];
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// static
+BOOL LLKeyboard::keyFromString(const LLString& str, KEY *key)
+{
+ LLString instring(str);
+ size_t length = instring.size();
+
+ if (length < 1)
+ {
+ return FALSE;
+ }
+ if (length == 1)
+ {
+ char ch = toupper(instring[0]);
+ if (('0' <= ch && ch <= '9') ||
+ ('A' <= ch && ch <= 'Z') ||
+ ('!' <= ch && ch <= '/') || // !"#$%&'()*+,-./
+ (':' <= ch && ch <= '@') || // :;<=>?@
+ ('[' <= ch && ch <= '`') || // [\]^_`
+ ('{' <= ch && ch <= '~')) // {|}~
+ {
+ *key = ch;
+ return TRUE;
+ }
+ }
+
+ LLString::toUpper(instring);
+ KEY res = get_if_there(sNamesToKeys, instring, (KEY)0);
+ if (res != 0)
+ {
+ *key = res;
+ return TRUE;
+ }
+ llwarns << "keyFromString failed: " << str << llendl;
+ return FALSE;
+}
+
+
+// static
+LLString LLKeyboard::stringFromKey(KEY key)
+{
+ LLString res = get_if_there(sKeysToNames, key, LLString::null);
+ if (res.empty())
+ {
+ char buffer[2];
+ buffer[0] = key;
+ buffer[1] = '\0';
+ res = LLString(buffer);
+ }
+ return res;
+}
+
+
+
+//static
+BOOL LLKeyboard::maskFromString(const LLString& str, MASK *mask)
+{
+ LLString instring(str);
+ if (instring == "NONE")
+ {
+ *mask = MASK_NONE;
+ return TRUE;
+ }
+ else if (instring == "SHIFT")
+ {
+ *mask = MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "CTL")
+ {
+ *mask = MASK_CONTROL;
+ return TRUE;
+ }
+ else if (instring == "ALT")
+ {
+ *mask = MASK_ALT;
+ return TRUE;
+ }
+ else if (instring == "CTL_SHIFT")
+ {
+ *mask = MASK_CONTROL | MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "ALT_SHIFT")
+ {
+ *mask = MASK_ALT | MASK_SHIFT;
+ return TRUE;
+ }
+ else if (instring == "CTL_ALT")
+ {
+ *mask = MASK_CONTROL | MASK_ALT;
+ return TRUE;
+ }
+ else if (instring == "CTL_ALT_SHIFT")
+ {
+ *mask = MASK_CONTROL | MASK_ALT | MASK_SHIFT;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
diff --git a/indra/llwindow/llkeyboard.h b/indra/llwindow/llkeyboard.h
new file mode 100644
index 0000000000..0c47d117d0
--- /dev/null
+++ b/indra/llwindow/llkeyboard.h
@@ -0,0 +1,124 @@
+/**
+ * @file llkeyboard.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARD_H
+#define LL_LLKEYBOARD_H
+
+#include <map>
+
+#include "string_table.h"
+#include "lltimer.h"
+#include "indra_constants.h"
+
+enum EKeystate
+{
+ KEYSTATE_DOWN,
+ KEYSTATE_LEVEL,
+ KEYSTATE_UP
+};
+
+typedef void (*LLKeyFunc)(EKeystate keystate);
+
+enum EKeyboardInsertMode
+{
+ LL_KIM_INSERT,
+ LL_KIM_OVERWRITE
+};
+
+class LLKeyBinding
+{
+public:
+ KEY mKey;
+ MASK mMask;
+// const char *mName; // unused
+ LLKeyFunc mFunction;
+};
+
+class LLWindowCallbacks;
+
+class LLKeyboard
+{
+public:
+ typedef enum e_numpad_distinct
+ {
+ ND_NEVER,
+ ND_NUMLOCK_OFF,
+ ND_NUMLOCK_ON
+ } ENumpadDistinct;
+
+public:
+ LLKeyboard();
+ virtual ~LLKeyboard();
+
+ void resetKeys();
+
+
+ F32 getCurKeyElapsedTime() { return getKeyElapsedTime( mCurScanKey ); }
+ F32 getCurKeyElapsedFrameCount() { return (F32)getKeyElapsedFrameCount( mCurScanKey ); }
+ BOOL getKeyDown(const KEY key) { return mKeyLevel[key]; }
+ BOOL getKeyRepeated(const KEY key) { return mKeyRepeated[key]; }
+
+ BOOL translateKey(const U16 os_key, KEY *translated_key);
+ U16 inverseTranslateKey(const KEY translated_key);
+ BOOL handleTranslatedKeyUp(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes
+ BOOL handleTranslatedKeyDown(KEY translated_key, U32 translated_mask); // Translated into "Linden" keycodes
+
+
+ virtual BOOL handleKeyUp(const U16 key, MASK mask) = 0;
+ virtual BOOL handleKeyDown(const U16 key, MASK mask) = 0;
+
+ // Asynchronously poll the control, alt, and shift keys and set the
+ // appropriate internal key masks.
+ virtual void resetMaskKeys() = 0;
+ virtual void scanKeyboard() = 0; // scans keyboard, calls functions as necessary
+ // Mac must differentiate between Command = Control for keyboard events
+ // and Command != Control for mouse events.
+ virtual MASK currentMask(BOOL for_mouse_event) = 0;
+ virtual KEY currentKey() { return mCurTranslatedKey; }
+
+ EKeyboardInsertMode getInsertMode() { return mInsertMode; }
+ void toggleInsertMode();
+
+ static BOOL maskFromString(const LLString& str, MASK *mask); // False on failure
+ static BOOL keyFromString(const LLString& str, KEY *key); // False on failure
+ static LLString stringFromKey(KEY key);
+
+ e_numpad_distinct getNumpadDistinct() { return mNumpadDistinct; }
+ void setNumpadDistinct(e_numpad_distinct val) { mNumpadDistinct = val; }
+
+ void setCallbacks(LLWindowCallbacks *cbs) { mCallbacks = cbs; }
+protected:
+ F32 getKeyElapsedTime( KEY key ); // Returns time in seconds since key was pressed.
+ S32 getKeyElapsedFrameCount( KEY key ); // Returns time in frames since key was pressed.
+ void addKeyName(KEY key, const LLString& name);
+
+protected:
+ std::map<U16, KEY> mTranslateKeyMap; // Map of translations from OS keys to Linden KEYs
+ std::map<KEY, U16> mInvTranslateKeyMap; // Map of translations from Linden KEYs to OS keys
+ LLWindowCallbacks *mCallbacks;
+
+ LLTimer mKeyLevelTimer[KEY_COUNT]; // Time since level was set
+ S32 mKeyLevelFrameCount[KEY_COUNT]; // Frames since level was set
+ BOOL mKeyLevel[KEY_COUNT]; // Levels
+ BOOL mKeyRepeated[KEY_COUNT]; // Key was repeated
+ BOOL mKeyUp[KEY_COUNT]; // Up edge
+ BOOL mKeyDown[KEY_COUNT]; // Down edge
+ KEY mCurTranslatedKey;
+ KEY mCurScanKey; // Used during the scanKeyboard()
+
+ e_numpad_distinct mNumpadDistinct;
+
+ EKeyboardInsertMode mInsertMode;
+
+ static std::map<KEY,LLString> sKeysToNames;
+ static std::map<LLString,KEY> sNamesToKeys;
+};
+
+extern LLKeyboard *gKeyboard;
+
+#endif
diff --git a/indra/llwindow/llkeyboardmacosx.cpp b/indra/llwindow/llkeyboardmacosx.cpp
new file mode 100644
index 0000000000..8ce3b1fb2d
--- /dev/null
+++ b/indra/llwindow/llkeyboardmacosx.cpp
@@ -0,0 +1,309 @@
+/**
+ * @file llkeyboardmacosx.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+#include "llkeyboardmacosx.h"
+#include "llwindow.h"
+
+#include <Carbon/Carbon.h>
+
+LLKeyboardMacOSX::LLKeyboardMacOSX()
+{
+ // Virtual keycode mapping table. Yes, this was as annoying to generate as it looks.
+ mTranslateKeyMap[0x00] = 'A';
+ mTranslateKeyMap[0x01] = 'S';
+ mTranslateKeyMap[0x02] = 'D';
+ mTranslateKeyMap[0x03] = 'F';
+ mTranslateKeyMap[0x04] = 'H';
+ mTranslateKeyMap[0x05] = 'G';
+ mTranslateKeyMap[0x06] = 'Z';
+ mTranslateKeyMap[0x07] = 'X';
+ mTranslateKeyMap[0x08] = 'C';
+ mTranslateKeyMap[0x09] = 'V';
+ mTranslateKeyMap[0x0b] = 'B';
+ mTranslateKeyMap[0x0c] = 'Q';
+ mTranslateKeyMap[0x0d] = 'W';
+ mTranslateKeyMap[0x0e] = 'E';
+ mTranslateKeyMap[0x0f] = 'R';
+ mTranslateKeyMap[0x10] = 'Y';
+ mTranslateKeyMap[0x11] = 'T';
+ mTranslateKeyMap[0x12] = '1';
+ mTranslateKeyMap[0x13] = '2';
+ mTranslateKeyMap[0x14] = '3';
+ mTranslateKeyMap[0x15] = '4';
+ mTranslateKeyMap[0x16] = '6';
+ mTranslateKeyMap[0x17] = '5';
+ mTranslateKeyMap[0x18] = '='; // KEY_EQUALS
+ mTranslateKeyMap[0x19] = '9';
+ mTranslateKeyMap[0x1a] = '7';
+ mTranslateKeyMap[0x1b] = '-'; // KEY_HYPHEN
+ mTranslateKeyMap[0x1c] = '8';
+ mTranslateKeyMap[0x1d] = '0';
+ mTranslateKeyMap[0x1e] = ']';
+ mTranslateKeyMap[0x1f] = 'O';
+ mTranslateKeyMap[0x20] = 'U';
+ mTranslateKeyMap[0x21] = '[';
+ mTranslateKeyMap[0x22] = 'I';
+ mTranslateKeyMap[0x23] = 'P';
+ mTranslateKeyMap[0x24] = KEY_RETURN,
+ mTranslateKeyMap[0x25] = 'L';
+ mTranslateKeyMap[0x26] = 'J';
+ mTranslateKeyMap[0x27] = '\'';
+ mTranslateKeyMap[0x28] = 'K';
+ mTranslateKeyMap[0x29] = ';';
+ mTranslateKeyMap[0x2a] = '\\';
+ mTranslateKeyMap[0x2b] = ',';
+ mTranslateKeyMap[0x2c] = '/';
+ mTranslateKeyMap[0x2d] = 'N';
+ mTranslateKeyMap[0x2e] = 'M';
+ mTranslateKeyMap[0x2f] = '.';
+ mTranslateKeyMap[0x30] = KEY_TAB;
+ mTranslateKeyMap[0x31] = ' '; // space!
+ mTranslateKeyMap[0x32] = '`';
+ mTranslateKeyMap[0x33] = KEY_BACKSPACE;
+ mTranslateKeyMap[0x35] = KEY_ESCAPE;
+ //mTranslateKeyMap[0x37] = 0; // Command key. (not used yet)
+ mTranslateKeyMap[0x38] = KEY_SHIFT;
+ mTranslateKeyMap[0x39] = KEY_CAPSLOCK;
+ mTranslateKeyMap[0x3a] = KEY_ALT;
+ mTranslateKeyMap[0x3b] = KEY_CONTROL;
+ mTranslateKeyMap[0x41] = '.'; // keypad
+ mTranslateKeyMap[0x43] = '*'; // keypad
+ mTranslateKeyMap[0x45] = '+'; // keypad
+ mTranslateKeyMap[0x4b] = '/'; // keypad
+ mTranslateKeyMap[0x4c] = KEY_RETURN; // keypad enter
+ mTranslateKeyMap[0x4e] = '-'; // keypad
+ mTranslateKeyMap[0x51] = '='; // keypad
+ mTranslateKeyMap[0x52] = '0'; // keypad
+ mTranslateKeyMap[0x53] = '1'; // keypad
+ mTranslateKeyMap[0x54] = '2'; // keypad
+ mTranslateKeyMap[0x55] = '3'; // keypad
+ mTranslateKeyMap[0x56] = '4'; // keypad
+ mTranslateKeyMap[0x57] = '5'; // keypad
+ mTranslateKeyMap[0x58] = '6'; // keypad
+ mTranslateKeyMap[0x59] = '7'; // keypad
+ mTranslateKeyMap[0x5b] = '8'; // keypad
+ mTranslateKeyMap[0x5c] = '9'; // keypad
+ mTranslateKeyMap[0x60] = KEY_F5;
+ mTranslateKeyMap[0x61] = KEY_F6;
+ mTranslateKeyMap[0x62] = KEY_F7;
+ mTranslateKeyMap[0x63] = KEY_F3;
+ mTranslateKeyMap[0x64] = KEY_F8;
+ mTranslateKeyMap[0x65] = KEY_F9;
+ mTranslateKeyMap[0x67] = KEY_F11;
+ mTranslateKeyMap[0x6d] = KEY_F10;
+ mTranslateKeyMap[0x6f] = KEY_F12;
+ mTranslateKeyMap[0x72] = KEY_INSERT;
+ mTranslateKeyMap[0x73] = KEY_HOME;
+ mTranslateKeyMap[0x74] = KEY_PAGE_UP;
+ mTranslateKeyMap[0x75] = KEY_DELETE;
+ mTranslateKeyMap[0x76] = KEY_F4;
+ mTranslateKeyMap[0x77] = KEY_END;
+ mTranslateKeyMap[0x78] = KEY_F2;
+ mTranslateKeyMap[0x79] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[0x7a] = KEY_F1;
+ mTranslateKeyMap[0x7b] = KEY_LEFT;
+ mTranslateKeyMap[0x7c] = KEY_RIGHT;
+ mTranslateKeyMap[0x7d] = KEY_DOWN;
+ mTranslateKeyMap[0x7e] = KEY_UP;
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // build numpad maps
+ mTranslateNumpadMap[0x52] = KEY_PAD_INS; // keypad 0
+ mTranslateNumpadMap[0x53] = KEY_PAD_END; // keypad 1
+ mTranslateNumpadMap[0x54] = KEY_PAD_DOWN; // keypad 2
+ mTranslateNumpadMap[0x55] = KEY_PAD_PGDN; // keypad 3
+ mTranslateNumpadMap[0x56] = KEY_PAD_LEFT; // keypad 4
+ mTranslateNumpadMap[0x57] = KEY_PAD_CENTER; // keypad 5
+ mTranslateNumpadMap[0x58] = KEY_PAD_RIGHT; // keypad 6
+ mTranslateNumpadMap[0x59] = KEY_PAD_HOME; // keypad 7
+ mTranslateNumpadMap[0x5b] = KEY_PAD_UP; // keypad 8
+ mTranslateNumpadMap[0x5c] = KEY_PAD_PGUP; // keypad 9
+ mTranslateNumpadMap[0x41] = KEY_PAD_DEL; // keypad .
+ mTranslateNumpadMap[0x4c] = KEY_PAD_RETURN; // keypad enter
+
+ // Build inverse numpad map
+ for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+void LLKeyboardMacOSX::resetMaskKeys()
+{
+ U32 mask = GetCurrentEventKeyModifiers();
+
+ // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
+ // It looks a bit suspicious, as it won't correct for keys that have been released.
+ // Is this the way it's supposed to work?
+
+ if(mask & shiftKey)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if(mask & (controlKey))
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if(mask & optionKey)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+/*
+static BOOL translateKeyMac(const U16 key, const U32 mask, KEY &outKey, U32 &outMask)
+{
+ // Translate the virtual keycode into the keycodes the keyboard system expects.
+ U16 virtualKey = (mask >> 24) & 0x0000007F;
+ outKey = macKeyTransArray[virtualKey];
+
+
+ return(outKey != 0);
+}
+*/
+
+MASK LLKeyboardMacOSX::updateModifiers(const U32 mask)
+{
+ // translate the mask
+ MASK out_mask = 0;
+
+ if(mask & shiftKey)
+ {
+ out_mask |= MASK_SHIFT;
+ }
+
+ if(mask & (controlKey | cmdKey))
+ {
+ out_mask |= MASK_CONTROL;
+ }
+
+ if(mask & optionKey)
+ {
+ out_mask |= MASK_ALT;
+ }
+
+ return out_mask;
+}
+
+BOOL LLKeyboardMacOSX::handleKeyDown(const U16 key, const U32 mask)
+{
+ KEY translated_key = 0;
+ U32 translated_mask = 0;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+BOOL LLKeyboardMacOSX::handleKeyUp(const U16 key, const U32 mask)
+{
+ KEY translated_key = 0;
+ U32 translated_mask = 0;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(key, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+MASK LLKeyboardMacOSX::currentMask(BOOL for_mouse_event)
+{
+ MASK result = MASK_NONE;
+ U32 mask = GetCurrentEventKeyModifiers();
+
+ if (mask & shiftKey) result |= MASK_SHIFT;
+ if (mask & controlKey) result |= MASK_CONTROL;
+ if (mask & optionKey) result |= MASK_ALT;
+
+ // For keyboard events, consider Command equivalent to Control
+ if (!for_mouse_event)
+ {
+ if (mask & cmdKey) result |= MASK_CONTROL;
+ }
+
+ return result;
+}
+
+void LLKeyboardMacOSX::scanKeyboard()
+{
+ S32 key;
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+BOOL LLKeyboardMacOSX::translateNumpadKey( const U16 os_key, KEY *translated_key )
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
+ if(iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+ return translateKey(os_key, translated_key);
+}
+
+U16 LLKeyboardMacOSX::inverseTranslateNumpadKey(const KEY translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
+ if(iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+ return inverseTranslateKey(translated_key);
+}
+
+#endif // LL_DARWIN
diff --git a/indra/llwindow/llkeyboardmacosx.h b/indra/llwindow/llkeyboardmacosx.h
new file mode 100644
index 0000000000..d9b7de4049
--- /dev/null
+++ b/indra/llwindow/llkeyboardmacosx.h
@@ -0,0 +1,36 @@
+/**
+ * @file llkeyboardmacosx.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDMACOSX_H
+#define LL_LLKEYBOARDMACOSX_H
+
+#include "llkeyboard.h"
+
+class LLKeyboardMacOSX : public LLKeyboard
+{
+public:
+ LLKeyboardMacOSX();
+ /*virtual*/ ~LLKeyboardMacOSX() {};
+
+ /*virtual*/ BOOL handleKeyUp(const U16 key, MASK mask);
+ /*virtual*/ BOOL handleKeyDown(const U16 key, MASK mask);
+ /*virtual*/ void resetMaskKeys();
+ /*virtual*/ MASK currentMask(BOOL for_mouse_event);
+ /*virtual*/ void scanKeyboard();
+
+protected:
+ MASK updateModifiers(const U32 mask);
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+ BOOL translateNumpadKey( const U16 os_key, KEY *translated_key );
+ U16 inverseTranslateNumpadKey(const KEY translated_key);
+private:
+ std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
+ std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
+};
+
+#endif
diff --git a/indra/llwindow/llkeyboardsdl.cpp b/indra/llwindow/llkeyboardsdl.cpp
new file mode 100644
index 0000000000..436a31b5cd
--- /dev/null
+++ b/indra/llwindow/llkeyboardsdl.cpp
@@ -0,0 +1,324 @@
+/**
+ * @file llkeyboardsdl.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_SDL
+
+#include "linden_common.h"
+#include "llkeyboardsdl.h"
+#include "llwindow.h"
+#include "SDL/SDL.h"
+
+LLKeyboardSDL::LLKeyboardSDL()
+{
+ // Set up key mapping for SDL - eventually can read this from a file?
+ // Anything not in the key map gets dropped
+ // Add default A-Z
+
+ // Virtual key mappings from SDL_keysym.h ...
+
+ // SDL maps the letter keys to the ASCII you'd expect, but it's lowercase...
+ U16 cur_char;
+ for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = cur_char;
+ }
+ for (cur_char = 'a'; cur_char <= 'z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (cur_char - 'a') + 'A';
+ }
+
+ for (cur_char = '0'; cur_char <= '9'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = cur_char;
+ }
+
+ // These ones are translated manually upon keydown/keyup because
+ // SDL doesn't handle their numlock transition.
+ //mTranslateKeyMap[SDLK_KP4] = KEY_PAD_LEFT;
+ //mTranslateKeyMap[SDLK_KP6] = KEY_PAD_RIGHT;
+ //mTranslateKeyMap[SDLK_KP8] = KEY_PAD_UP;
+ //mTranslateKeyMap[SDLK_KP2] = KEY_PAD_DOWN;
+ //mTranslateKeyMap[SDLK_KP_PERIOD] = KEY_DELETE;
+ //mTranslateKeyMap[SDLK_KP7] = KEY_HOME;
+ //mTranslateKeyMap[SDLK_KP1] = KEY_END;
+ //mTranslateKeyMap[SDLK_KP9] = KEY_PAGE_UP;
+ //mTranslateKeyMap[SDLK_KP3] = KEY_PAGE_DOWN;
+ //mTranslateKeyMap[SDLK_KP0] = KEY_INSERT;
+
+ mTranslateKeyMap[SDLK_SPACE] = ' ';
+ mTranslateKeyMap[SDLK_RETURN] = KEY_RETURN;
+ mTranslateKeyMap[SDLK_LEFT] = KEY_LEFT;
+ mTranslateKeyMap[SDLK_RIGHT] = KEY_RIGHT;
+ mTranslateKeyMap[SDLK_UP] = KEY_UP;
+ mTranslateKeyMap[SDLK_DOWN] = KEY_DOWN;
+ mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[SDLK_KP_ENTER] = KEY_RETURN;
+ mTranslateKeyMap[SDLK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[SDLK_BACKSPACE] = KEY_BACKSPACE;
+ mTranslateKeyMap[SDLK_DELETE] = KEY_DELETE;
+ mTranslateKeyMap[SDLK_LSHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[SDLK_RSHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[SDLK_LCTRL] = KEY_CONTROL;
+ mTranslateKeyMap[SDLK_RCTRL] = KEY_CONTROL;
+ mTranslateKeyMap[SDLK_LALT] = KEY_ALT;
+ mTranslateKeyMap[SDLK_RALT] = KEY_ALT;
+ mTranslateKeyMap[SDLK_HOME] = KEY_HOME;
+ mTranslateKeyMap[SDLK_END] = KEY_END;
+ mTranslateKeyMap[SDLK_PAGEUP] = KEY_PAGE_UP;
+ mTranslateKeyMap[SDLK_PAGEDOWN] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[SDLK_MINUS] = KEY_HYPHEN;
+ mTranslateKeyMap[SDLK_EQUALS] = KEY_EQUALS;
+ mTranslateKeyMap[SDLK_KP_EQUALS] = KEY_EQUALS;
+ mTranslateKeyMap[SDLK_INSERT] = KEY_INSERT;
+ mTranslateKeyMap[SDLK_CAPSLOCK] = KEY_CAPSLOCK;
+ mTranslateKeyMap[SDLK_TAB] = KEY_TAB;
+ mTranslateKeyMap[SDLK_KP_PLUS] = KEY_ADD;
+ mTranslateKeyMap[SDLK_KP_MINUS] = KEY_SUBTRACT;
+ mTranslateKeyMap[SDLK_KP_MULTIPLY] = KEY_MULTIPLY;
+ mTranslateKeyMap[SDLK_KP_DIVIDE] = KEY_DIVIDE;
+ mTranslateKeyMap[SDLK_F1] = KEY_F1;
+ mTranslateKeyMap[SDLK_F2] = KEY_F2;
+ mTranslateKeyMap[SDLK_F3] = KEY_F3;
+ mTranslateKeyMap[SDLK_F4] = KEY_F4;
+ mTranslateKeyMap[SDLK_F5] = KEY_F5;
+ mTranslateKeyMap[SDLK_F6] = KEY_F6;
+ mTranslateKeyMap[SDLK_F7] = KEY_F7;
+ mTranslateKeyMap[SDLK_F8] = KEY_F8;
+ mTranslateKeyMap[SDLK_F9] = KEY_F9;
+ mTranslateKeyMap[SDLK_F10] = KEY_F10;
+ mTranslateKeyMap[SDLK_F11] = KEY_F11;
+ mTranslateKeyMap[SDLK_F12] = KEY_F12;
+ mTranslateKeyMap[SDLK_PLUS] = '=';
+ mTranslateKeyMap[SDLK_COMMA] = ',';
+ mTranslateKeyMap[SDLK_MINUS] = '-';
+ mTranslateKeyMap[SDLK_PERIOD] = '.';
+ mTranslateKeyMap[SDLK_BACKQUOTE] = '`';
+ mTranslateKeyMap[SDLK_SLASH] = '/';
+ mTranslateKeyMap[SDLK_SEMICOLON] = ';';
+ mTranslateKeyMap[SDLK_LEFTBRACKET] = '[';
+ mTranslateKeyMap[SDLK_BACKSLASH] = '\\';
+ mTranslateKeyMap[SDLK_RIGHTBRACKET] = ']';
+ mTranslateKeyMap[SDLK_QUOTE] = '\'';
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // numpad map
+ mTranslateNumpadMap[SDLK_KP0] = KEY_PAD_INS;
+ mTranslateNumpadMap[SDLK_KP1] = KEY_PAD_END;
+ mTranslateNumpadMap[SDLK_KP2] = KEY_PAD_DOWN;
+ mTranslateNumpadMap[SDLK_KP3] = KEY_PAD_PGDN;
+ mTranslateNumpadMap[SDLK_KP4] = KEY_PAD_LEFT;
+ mTranslateNumpadMap[SDLK_KP5] = KEY_PAD_CENTER;
+ mTranslateNumpadMap[SDLK_KP6] = KEY_PAD_RIGHT;
+ mTranslateNumpadMap[SDLK_KP7] = KEY_PAD_HOME;
+ mTranslateNumpadMap[SDLK_KP8] = KEY_PAD_UP;
+ mTranslateNumpadMap[SDLK_KP9] = KEY_PAD_PGUP;
+ mTranslateNumpadMap[SDLK_KP_PERIOD] = KEY_PAD_DEL;
+
+ // build inverse numpad map
+ for (iter = mTranslateNumpadMap.begin();
+ iter != mTranslateNumpadMap.end();
+ iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+void LLKeyboardSDL::resetMaskKeys()
+{
+ SDLMod mask = SDL_GetModState();
+
+ // MBW -- XXX -- This mirrors the operation of the Windows version of resetMaskKeys().
+ // It looks a bit suspicious, as it won't correct for keys that have been released.
+ // Is this the way it's supposed to work?
+
+ if(mask & KMOD_SHIFT)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if(mask & KMOD_CTRL)
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if(mask & KMOD_ALT)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+
+MASK LLKeyboardSDL::updateModifiers(const U32 mask)
+{
+ // translate the mask
+ MASK out_mask = MASK_NONE;
+
+ if(mask & KMOD_SHIFT)
+ {
+ out_mask |= MASK_SHIFT;
+ }
+
+ if(mask & KMOD_CTRL)
+ {
+ out_mask |= MASK_CONTROL;
+ }
+
+ if(mask & KMOD_ALT)
+ {
+ out_mask |= MASK_ALT;
+ }
+
+ return out_mask;
+}
+
+
+static U16 adjustNativekeyFromUnhandledMask(const U16 key, const U32 mask)
+{
+ // SDL doesn't automatically adjust the keysym according to
+ // whether NUMLOCK is engaged, so we massage the keysym manually.
+ U16 rtn = key;
+ if (!(mask & KMOD_NUM))
+ {
+ switch (key)
+ {
+ case SDLK_KP_PERIOD: rtn = SDLK_DELETE; break;
+ case SDLK_KP0: rtn = SDLK_INSERT; break;
+ case SDLK_KP1: rtn = SDLK_END; break;
+ case SDLK_KP2: rtn = SDLK_DOWN; break;
+ case SDLK_KP3: rtn = SDLK_PAGEDOWN; break;
+ case SDLK_KP4: rtn = SDLK_LEFT; break;
+ case SDLK_KP6: rtn = SDLK_RIGHT; break;
+ case SDLK_KP7: rtn = SDLK_HOME; break;
+ case SDLK_KP8: rtn = SDLK_UP; break;
+ case SDLK_KP9: rtn = SDLK_PAGEUP; break;
+ }
+ }
+ return rtn;
+}
+
+
+BOOL LLKeyboardSDL::handleKeyDown(const U16 key, const U32 mask)
+{
+ U16 adjusted_nativekey;
+ KEY translated_key = 0;
+ U32 translated_mask = MASK_NONE;
+ BOOL handled = FALSE;
+
+ adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(adjusted_nativekey, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+BOOL LLKeyboardSDL::handleKeyUp(const U16 key, const U32 mask)
+{
+ U16 adjusted_nativekey;
+ KEY translated_key = 0;
+ U32 translated_mask = MASK_NONE;
+ BOOL handled = FALSE;
+
+ adjusted_nativekey = adjustNativekeyFromUnhandledMask(key, mask);
+
+ translated_mask = updateModifiers(mask);
+
+ if(translateNumpadKey(adjusted_nativekey, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+MASK LLKeyboardSDL::currentMask(BOOL for_mouse_event)
+{
+ MASK result = MASK_NONE;
+ SDLMod mask = SDL_GetModState();
+
+ if (mask & KMOD_SHIFT) result |= MASK_SHIFT;
+ if (mask & KMOD_CTRL) result |= MASK_CONTROL;
+ if (mask & KMOD_ALT) result |= MASK_ALT;
+
+ // For keyboard events, consider Meta keys equivalent to Control
+ if (!for_mouse_event)
+ {
+ if (mask & KMOD_META) result |= MASK_CONTROL;
+ }
+
+ return result;
+}
+
+void LLKeyboardSDL::scanKeyboard()
+{
+ for (S32 key = 0; key < KEY_COUNT; key++)
+ {
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (S32 key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+
+BOOL LLKeyboardSDL::translateNumpadKey( const U16 os_key, KEY *translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter= mTranslateNumpadMap.find(os_key);
+ if(iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+ BOOL success = translateKey(os_key, translated_key);
+ return success;
+}
+
+U16 LLKeyboardSDL::inverseTranslateNumpadKey(const KEY translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<KEY, U16>::iterator iter= mInvTranslateNumpadMap.find(translated_key);
+ if(iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+ return inverseTranslateKey(translated_key);
+}
+
+#endif
+
diff --git a/indra/llwindow/llkeyboardsdl.h b/indra/llwindow/llkeyboardsdl.h
new file mode 100644
index 0000000000..83701d37bf
--- /dev/null
+++ b/indra/llwindow/llkeyboardsdl.h
@@ -0,0 +1,37 @@
+/**
+ * @file llkeyboardsdl.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDSDL_H
+#define LL_LLKEYBOARDSDL_H
+
+#include "llkeyboard.h"
+#include "SDL/SDL.h"
+
+class LLKeyboardSDL : public LLKeyboard
+{
+public:
+ LLKeyboardSDL();
+ /*virtual*/ ~LLKeyboardSDL() {};
+
+ /*virtual*/ BOOL handleKeyUp(const U16 key, MASK mask);
+ /*virtual*/ BOOL handleKeyDown(const U16 key, MASK mask);
+ /*virtual*/ void resetMaskKeys();
+ /*virtual*/ MASK currentMask(BOOL for_mouse_event);
+ /*virtual*/ void scanKeyboard();
+
+protected:
+ MASK updateModifiers(const U32 mask);
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+ BOOL translateNumpadKey( const U16 os_key, KEY *translated_key );
+ U16 inverseTranslateNumpadKey(const KEY translated_key);
+private:
+ std::map<U16, KEY> mTranslateNumpadMap; // special map for translating OS keys to numpad keys
+ std::map<KEY, U16> mInvTranslateNumpadMap; // inverse of the above
+};
+
+#endif
diff --git a/indra/llwindow/llkeyboardwin32.cpp b/indra/llwindow/llkeyboardwin32.cpp
new file mode 100644
index 0000000000..37eb967e27
--- /dev/null
+++ b/indra/llwindow/llkeyboardwin32.cpp
@@ -0,0 +1,372 @@
+/**
+ * @file llkeyboardwin32.cpp
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_WINDOWS
+
+#include "linden_common.h"
+#include "llkeyboardwin32.h"
+#include "llwindow.h"
+
+LLKeyboardWin32::LLKeyboardWin32()
+{
+ // Set up key mapping for windows - eventually can read this from a file?
+ // Anything not in the key map gets dropped
+ // Add default A-Z
+
+ // Virtual key mappings from WinUser.h
+
+ KEY cur_char;
+ for (cur_char = 'A'; cur_char <= 'Z'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)cur_char;
+ }
+
+ for (cur_char = '0'; cur_char <= '9'; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)cur_char;
+ }
+ // numpad number keys
+ for (cur_char = 0x60; cur_char <= 0x69; cur_char++)
+ {
+ mTranslateKeyMap[cur_char] = (KEY)('0' + (0x60 - cur_char));
+ }
+
+
+ mTranslateKeyMap[VK_SPACE] = ' ';
+ mTranslateKeyMap[VK_OEM_1] = ';';
+ // When the user hits, for example, Ctrl-= as a keyboard shortcut,
+ // Windows generates VK_OEM_PLUS. This is true on both QWERTY and DVORAK
+ // keyboards in the US. Numeric keypad '+' generates VK_ADD below.
+ // Thus we translate it as '='.
+ // Potential bug: This may not be true on international keyboards. JC
+ mTranslateKeyMap[VK_OEM_PLUS] = '=';
+ mTranslateKeyMap[VK_OEM_COMMA] = ',';
+ mTranslateKeyMap[VK_OEM_MINUS] = '-';
+ mTranslateKeyMap[VK_OEM_PERIOD] = '.';
+ mTranslateKeyMap[VK_OEM_2] = '/';
+ mTranslateKeyMap[VK_OEM_3] = '`';
+ mTranslateKeyMap[VK_OEM_4] = '[';
+ mTranslateKeyMap[VK_OEM_5] = '\\';
+ mTranslateKeyMap[VK_OEM_6] = ']';
+ mTranslateKeyMap[VK_OEM_7] = '\'';
+ mTranslateKeyMap[VK_ESCAPE] = KEY_ESCAPE;
+ mTranslateKeyMap[VK_RETURN] = KEY_RETURN;
+ mTranslateKeyMap[VK_LEFT] = KEY_LEFT;
+ mTranslateKeyMap[VK_RIGHT] = KEY_RIGHT;
+ mTranslateKeyMap[VK_UP] = KEY_UP;
+ mTranslateKeyMap[VK_DOWN] = KEY_DOWN;
+ mTranslateKeyMap[VK_BACK] = KEY_BACKSPACE;
+ mTranslateKeyMap[VK_INSERT] = KEY_INSERT;
+ mTranslateKeyMap[VK_DELETE] = KEY_DELETE;
+ mTranslateKeyMap[VK_SHIFT] = KEY_SHIFT;
+ mTranslateKeyMap[VK_CONTROL] = KEY_CONTROL;
+ mTranslateKeyMap[VK_MENU] = KEY_ALT;
+ mTranslateKeyMap[VK_CAPITAL] = KEY_CAPSLOCK;
+ mTranslateKeyMap[VK_HOME] = KEY_HOME;
+ mTranslateKeyMap[VK_END] = KEY_END;
+ mTranslateKeyMap[VK_PRIOR] = KEY_PAGE_UP;
+ mTranslateKeyMap[VK_NEXT] = KEY_PAGE_DOWN;
+ mTranslateKeyMap[VK_TAB] = KEY_TAB;
+ mTranslateKeyMap[VK_ADD] = KEY_ADD;
+ mTranslateKeyMap[VK_SUBTRACT] = KEY_SUBTRACT;
+ mTranslateKeyMap[VK_MULTIPLY] = KEY_MULTIPLY;
+ mTranslateKeyMap[VK_DIVIDE] = KEY_DIVIDE;
+ mTranslateKeyMap[VK_F1] = KEY_F1;
+ mTranslateKeyMap[VK_F2] = KEY_F2;
+ mTranslateKeyMap[VK_F3] = KEY_F3;
+ mTranslateKeyMap[VK_F4] = KEY_F4;
+ mTranslateKeyMap[VK_F5] = KEY_F5;
+ mTranslateKeyMap[VK_F6] = KEY_F6;
+ mTranslateKeyMap[VK_F7] = KEY_F7;
+ mTranslateKeyMap[VK_F8] = KEY_F8;
+ mTranslateKeyMap[VK_F9] = KEY_F9;
+ mTranslateKeyMap[VK_F10] = KEY_F10;
+ mTranslateKeyMap[VK_F11] = KEY_F11;
+ mTranslateKeyMap[VK_F12] = KEY_F12;
+ mTranslateKeyMap[VK_CLEAR] = KEY_PAD_CENTER;
+
+ // Build inverse map
+ std::map<U16, KEY>::iterator iter;
+ for (iter = mTranslateKeyMap.begin(); iter != mTranslateKeyMap.end(); iter++)
+ {
+ mInvTranslateKeyMap[iter->second] = iter->first;
+ }
+
+ // numpad map
+ mTranslateNumpadMap[0x60] = KEY_PAD_INS; // keypad 0
+ mTranslateNumpadMap[0x61] = KEY_PAD_END; // keypad 1
+ mTranslateNumpadMap[0x62] = KEY_PAD_DOWN; // keypad 2
+ mTranslateNumpadMap[0x63] = KEY_PAD_PGDN; // keypad 3
+ mTranslateNumpadMap[0x64] = KEY_PAD_LEFT; // keypad 4
+ mTranslateNumpadMap[0x65] = KEY_PAD_CENTER; // keypad 5
+ mTranslateNumpadMap[0x66] = KEY_PAD_RIGHT; // keypad 6
+ mTranslateNumpadMap[0x67] = KEY_PAD_HOME; // keypad 7
+ mTranslateNumpadMap[0x68] = KEY_PAD_UP; // keypad 8
+ mTranslateNumpadMap[0x69] = KEY_PAD_PGUP; // keypad 9
+ mTranslateNumpadMap[0x6E] = KEY_PAD_DEL; // keypad .
+
+ for (iter = mTranslateNumpadMap.begin(); iter != mTranslateNumpadMap.end(); iter++)
+ {
+ mInvTranslateNumpadMap[iter->second] = iter->first;
+ }
+}
+
+// Asynchronously poll the control, alt and shift keys and set the
+// appropriate states.
+// Note: this does not generate edges.
+void LLKeyboardWin32::resetMaskKeys()
+{
+ // GetAsyncKeyState returns a short and uses the most significant
+ // bit to indicate that the key is down.
+ if (GetAsyncKeyState(VK_SHIFT) & 0x8000)
+ {
+ mKeyLevel[KEY_SHIFT] = TRUE;
+ }
+
+ if (GetAsyncKeyState(VK_CONTROL) & 0x8000)
+ {
+ mKeyLevel[KEY_CONTROL] = TRUE;
+ }
+
+ if (GetAsyncKeyState(VK_MENU) & 0x8000)
+ {
+ mKeyLevel[KEY_ALT] = TRUE;
+ }
+}
+
+
+void LLKeyboardWin32::setModifierKeyLevel( KEY key, BOOL new_state )
+{
+ if( mKeyLevel[key] != new_state )
+ {
+ mKeyLevelFrameCount[key] = 0;
+
+ if( new_state )
+ {
+ mKeyLevelTimer[key].reset();
+ }
+ mKeyLevel[key] = new_state;
+ }
+}
+
+
+MASK LLKeyboardWin32::updateModifiers()
+{
+ // Scan the modifier keys as of the last Windows key message
+ // (keydown encoded in high order bit of short)
+ setModifierKeyLevel( KEY_SHIFT, GetKeyState(VK_SHIFT) & 0x8000 );
+ setModifierKeyLevel( KEY_CONTROL, GetKeyState(VK_CONTROL) & 0x8000 );
+ setModifierKeyLevel( KEY_ALT, GetKeyState(VK_MENU) & 0x8000 );
+ setModifierKeyLevel( KEY_CAPSLOCK, GetKeyState(VK_CAPITAL) & 0x0001); // Low order bit carries the toggle state.
+ // Get mask for keyboard events
+ MASK mask = currentMask(FALSE);
+ return mask;
+}
+
+
+// mask is ignored, except for extended flag -- we poll the modifier keys for the other flags
+BOOL LLKeyboardWin32::handleKeyDown(const U16 key, MASK mask)
+{
+ KEY translated_key;
+ U32 translated_mask;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers();
+
+ if (translateExtendedKey(key, mask, &translated_key))
+ {
+ handled = handleTranslatedKeyDown(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+// mask is ignored, except for extended flag -- we poll the modifier keys for the other flags
+BOOL LLKeyboardWin32::handleKeyUp(const U16 key, MASK mask)
+{
+ KEY translated_key;
+ U32 translated_mask;
+ BOOL handled = FALSE;
+
+ translated_mask = updateModifiers();
+
+ if (translateExtendedKey(key, mask, &translated_key))
+ {
+ handled = handleTranslatedKeyUp(translated_key, translated_mask);
+ }
+
+ return handled;
+}
+
+
+MASK LLKeyboardWin32::currentMask(BOOL)
+{
+ MASK mask = MASK_NONE;
+
+ if (mKeyLevel[KEY_SHIFT]) mask |= MASK_SHIFT;
+ if (mKeyLevel[KEY_CONTROL]) mask |= MASK_CONTROL;
+ if (mKeyLevel[KEY_ALT]) mask |= MASK_ALT;
+
+ return mask;
+}
+
+
+void LLKeyboardWin32::scanKeyboard()
+{
+ S32 key;
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ // On Windows, verify key down state. JC
+ if (mKeyLevel[key])
+ {
+ // FIXME: I KNOW there must be a better way of interrogating the key state than this, using async
+ // key state can cause ALL kinds of bugs - Doug
+ if (key < KEY_BUTTON0)
+ {
+ // ...under windows make sure the key actually still is down.
+ // ...translate back to windows key
+ U16 virtual_key = inverseTranslateExtendedKey(key);
+ // keydown in highest bit
+ if (!(GetAsyncKeyState(virtual_key) & 0x8000))
+ {
+ //llinfos << "Key up event missed, resetting" << llendl;
+ mKeyLevel[key] = FALSE;
+ mKeyLevelFrameCount[key] = 0;
+ }
+ }
+ }
+
+ // Generate callback if any event has occurred on this key this frame.
+ // Can't just test mKeyLevel, because this could be a slow frame and
+ // key might have gone down then up. JC
+ if (mKeyLevel[key] || mKeyDown[key] || mKeyUp[key])
+ {
+ mCurScanKey = key;
+ mCallbacks->handleScanKey(key, mKeyDown[key], mKeyUp[key], mKeyLevel[key]);
+ }
+ }
+
+ // Reset edges for next frame
+ for (key = 0; key < KEY_COUNT; key++)
+ {
+ mKeyUp[key] = FALSE;
+ mKeyDown[key] = FALSE;
+ if (mKeyLevel[key])
+ {
+ mKeyLevelFrameCount[key]++;
+ }
+ }
+}
+
+BOOL LLKeyboardWin32::translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key)
+{
+ if(mNumpadDistinct == ND_NUMLOCK_ON)
+ {
+ std::map<U16, KEY>::iterator iter = mTranslateNumpadMap.find(os_key);
+ if (iter != mTranslateNumpadMap.end())
+ {
+ *translated_key = iter->second;
+ return TRUE;
+ }
+ }
+
+ BOOL success = translateKey(os_key, translated_key);
+ if(mNumpadDistinct != ND_NEVER) {
+ if(!success) return success;
+ if(mask & MASK_EXTENDED)
+ {
+ // this is where we'd create new keycodes for extended keys
+ // the set of extended keys includes the 'normal' arrow keys and
+ // the pgup/dn/insert/home/end/delete cluster above the arrow keys
+ // see http://windowssdk.msdn.microsoft.com/en-us/library/ms646280.aspx
+
+ // only process the return key if numlock is off
+ if(((mNumpadDistinct == ND_NUMLOCK_OFF &&
+ !(GetKeyState(VK_NUMLOCK) & 1))
+ || mNumpadDistinct == ND_NUMLOCK_ON) &&
+ *translated_key == KEY_RETURN) {
+ *translated_key = KEY_PAD_RETURN;
+ }
+ }
+ else
+ {
+ // the non-extended keys, those are in the numpad
+ switch (*translated_key)
+ {
+ case KEY_LEFT:
+ *translated_key = KEY_PAD_LEFT; break;
+ case KEY_RIGHT:
+ *translated_key = KEY_PAD_RIGHT; break;
+ case KEY_UP:
+ *translated_key = KEY_PAD_UP; break;
+ case KEY_DOWN:
+ *translated_key = KEY_PAD_DOWN; break;
+ case KEY_HOME:
+ *translated_key = KEY_PAD_HOME; break;
+ case KEY_END:
+ *translated_key = KEY_PAD_END; break;
+ case KEY_PAGE_UP:
+ *translated_key = KEY_PAD_PGUP; break;
+ case KEY_PAGE_DOWN:
+ *translated_key = KEY_PAD_PGDN; break;
+ case KEY_INSERT:
+ *translated_key = KEY_PAD_INS; break;
+ case KEY_DELETE:
+ *translated_key = KEY_PAD_DEL; break;
+ }
+ }
+ }
+ return success;
+}
+
+U16 LLKeyboardWin32::inverseTranslateExtendedKey(const KEY translated_key)
+{
+ // if numlock is on, then we need to translate KEY_PAD_FOO to the corresponding number pad number
+ if((mNumpadDistinct == ND_NUMLOCK_ON) && (GetKeyState(VK_NUMLOCK) & 1))
+ {
+ std::map<KEY, U16>::iterator iter = mInvTranslateNumpadMap.find(translated_key);
+ if (iter != mInvTranslateNumpadMap.end())
+ {
+ return iter->second;
+ }
+ }
+
+ // if numlock is off or we're not converting numbers to arrows, we map our keypad arrows
+ // to regular arrows since Windows doesn't distinguish between them
+ KEY converted_key = translated_key;
+ switch (converted_key)
+ {
+ case KEY_PAD_LEFT:
+ converted_key = KEY_LEFT; break;
+ case KEY_PAD_RIGHT:
+ converted_key = KEY_RIGHT; break;
+ case KEY_PAD_UP:
+ converted_key = KEY_UP; break;
+ case KEY_PAD_DOWN:
+ converted_key = KEY_DOWN; break;
+ case KEY_PAD_HOME:
+ converted_key = KEY_HOME; break;
+ case KEY_PAD_END:
+ converted_key = KEY_END; break;
+ case KEY_PAD_PGUP:
+ converted_key = KEY_PAGE_UP; break;
+ case KEY_PAD_PGDN:
+ converted_key = KEY_PAGE_DOWN; break;
+ case KEY_PAD_INS:
+ converted_key = KEY_INSERT; break;
+ case KEY_PAD_DEL:
+ converted_key = KEY_DELETE; break;
+ case KEY_PAD_RETURN:
+ converted_key = KEY_RETURN; break;
+ }
+ // convert our virtual keys to OS keys
+ return inverseTranslateKey(converted_key);
+}
+
+#endif
diff --git a/indra/llwindow/llkeyboardwin32.h b/indra/llwindow/llkeyboardwin32.h
new file mode 100644
index 0000000000..e7eb4b9c1e
--- /dev/null
+++ b/indra/llwindow/llkeyboardwin32.h
@@ -0,0 +1,40 @@
+/**
+ * @file llkeyboardwin32.h
+ * @brief Handler for assignable key bindings
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLKEYBOARDWIN32_H
+#define LL_LLKEYBOARDWIN32_H
+
+#include "llkeyboard.h"
+
+// this mask distinguishes extended keys, which include non-numpad arrow keys
+// (and, curiously, the num lock and numpad '/')
+const MASK MASK_EXTENDED = 0x0100;
+
+class LLKeyboardWin32 : public LLKeyboard
+{
+public:
+ LLKeyboardWin32();
+ /*virtual*/ ~LLKeyboardWin32() {};
+
+ /*virtual*/ BOOL handleKeyUp(const U16 key, MASK mask);
+ /*virtual*/ BOOL handleKeyDown(const U16 key, MASK mask);
+ /*virtual*/ void resetMaskKeys();
+ /*virtual*/ MASK currentMask(BOOL for_mouse_event);
+ /*virtual*/ void scanKeyboard();
+ BOOL translateExtendedKey(const U16 os_key, const MASK mask, KEY *translated_key);
+ U16 inverseTranslateExtendedKey(const KEY translated_key);
+
+protected:
+ MASK updateModifiers();
+ void setModifierKeyLevel( KEY key, BOOL new_state );
+private:
+ std::map<U16, KEY> mTranslateNumpadMap;
+ std::map<KEY, U16> mInvTranslateNumpadMap;
+};
+
+#endif
diff --git a/indra/llwindow/llmousehandler.h b/indra/llwindow/llmousehandler.h
new file mode 100644
index 0000000000..bd163535a0
--- /dev/null
+++ b/indra/llwindow/llmousehandler.h
@@ -0,0 +1,39 @@
+/**
+ * @file llmousehandler.h
+ * @brief LLMouseHandler class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MOUSEHANDLER_H
+#define LL_MOUSEHANDLER_H
+
+// Abstract interface.
+// Intended for use via multiple inheritance.
+// A class may have as many interfaces as it likes, but never needs to inherit one more than once.
+
+class LLMouseHandler
+{
+public:
+ LLMouseHandler() {}
+ virtual ~LLMouseHandler() {}
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) = 0;
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) = 0;
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen) = 0;
+ virtual const LLString& getName() const = 0;
+
+ // Hack to support LLFocusMgr
+ virtual BOOL isView() = 0;
+
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const = 0;
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const = 0;
+};
+
+#endif
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
new file mode 100644
index 0000000000..435ac9d0cc
--- /dev/null
+++ b/indra/llwindow/llwindow.cpp
@@ -0,0 +1,399 @@
+/**
+ * @file llwindow.cpp
+ * @brief Basic graphical window class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "llwindowheadless.h"
+
+#if LL_MESA_HEADLESS
+#include "llwindowmesaheadless.h"
+#elif LL_SDL
+#include "llwindowsdl.h"
+#elif LL_WINDOWS
+#include "llwindowwin32.h"
+#elif LL_DARWIN
+#include "llwindowmacosx.h"
+#elif LL_LINUX
+#include "llwindowlinux.h" // currently just a dummy wrapper
+#endif
+
+#include "llerror.h"
+#include "llkeyboard.h"
+
+//static instance for default callbacks
+LLWindowCallbacks LLWindow::sDefaultCallbacks;
+
+//
+// LLWindowCallbacks
+//
+
+LLSplashScreen *gSplashScreenp = NULL;
+BOOL gDebugClicks = FALSE;
+BOOL gDebugWindowProc = FALSE;
+
+const S32 gURLProtocolWhitelistCount = 3;
+const char* gURLProtocolWhitelist[] = { "file", "http", "https" };
+
+// CP: added a handler list - this is what's used to open the protocol and is based on registry entry
+// only meaningful difference currently is that file: protocols are opened using http:
+// since no protocol handler exists in registry for file:
+// Important - these lists should match - protocol to handler
+const char* gURLProtocolWhitelistHandler[] = { "http", "http", "https" };
+
+BOOL LLWindowCallbacks::handleTranslatedKeyDown(const KEY key, const MASK mask, BOOL repeated)
+{
+ return FALSE;
+}
+
+
+BOOL LLWindowCallbacks::handleTranslatedKeyUp(const KEY key, const MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+}
+
+BOOL LLWindowCallbacks::handleUnicodeChar(llwchar uni_char, MASK mask)
+{
+ return FALSE;
+}
+
+
+BOOL LLWindowCallbacks::handleMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleMouseLeave(LLWindow *window)
+{
+ return;
+}
+
+BOOL LLWindowCallbacks::handleCloseRequest(LLWindow *window)
+{
+ //allow the window to close
+ return TRUE;
+}
+
+void LLWindowCallbacks::handleQuit(LLWindow *window)
+{
+ if(LLWindowManager::destroyWindow(window) == FALSE)
+ {
+ llerrs << "LLWindowCallbacks::handleQuit() : Couldn't destroy window" << llendl;
+ }
+}
+
+BOOL LLWindowCallbacks::handleRightMouseDown(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleRightMouseUp(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleActivate(LLWindow *window, BOOL activated)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleMouseMove(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+}
+
+void LLWindowCallbacks::handleScrollWheel(LLWindow *window, S32 clicks)
+{
+}
+
+void LLWindowCallbacks::handleResize(LLWindow *window, const S32 width, const S32 height)
+{
+}
+
+void LLWindowCallbacks::handleFocus(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleFocusLost(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleMenuSelect(LLWindow *window, const S32 menu_item)
+{
+}
+
+BOOL LLWindowCallbacks::handlePaint(LLWindow *window, const S32 x, const S32 y,
+ const S32 width, const S32 height)
+{
+ return FALSE;
+}
+
+BOOL LLWindowCallbacks::handleDoubleClick(LLWindow *window, const LLCoordGL pos, MASK mask)
+{
+ return FALSE;
+}
+
+void LLWindowCallbacks::handleWindowBlock(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleWindowUnblock(LLWindow *window)
+{
+}
+
+void LLWindowCallbacks::handleDataCopy(LLWindow *window, S32 data_type, void *data)
+{
+}
+
+
+S32 OSMessageBox(const char* text, const char* caption, U32 type)
+{
+ // Properly hide the splash screen when displaying the message box
+ BOOL was_visible = FALSE;
+ if (LLSplashScreen::isVisible())
+ {
+ was_visible = TRUE;
+ LLSplashScreen::hide();
+ }
+
+ S32 result = 0;
+#if LL_MESA_HEADLESS // !!! FIXME
+ llwarns << "OSMessageBox: " << text << llendl;
+ return OSBTN_OK;
+#elif LL_WINDOWS
+ result = OSMessageBoxWin32(text, caption, type);
+#elif LL_DARWIN
+ result = OSMessageBoxMacOSX(text, caption, type);
+#elif LL_SDL
+ result = OSMessageBoxSDL(text, caption, type);
+#else
+#error("OSMessageBox not implemented for this platform!")
+#endif
+
+ if (was_visible)
+ {
+ LLSplashScreen::show();
+ }
+
+ return result;
+}
+
+
+//
+// LLWindow
+//
+
+LLWindow::LLWindow(BOOL fullscreen, U32 flags)
+ : mCallbacks(&sDefaultCallbacks),
+ mPostQuit(TRUE),
+ mFullscreen(fullscreen),
+ mFullscreenWidth(0),
+ mFullscreenHeight(0),
+ mFullscreenBits(0),
+ mFullscreenRefresh(0),
+ mSupportedResolutions(NULL),
+ mNumSupportedResolutions(0),
+ mCurrentCursor(UI_CURSOR_ARROW),
+ mCursorHidden(FALSE),
+ mBusyCount(0),
+ mIsMouseClipping(FALSE),
+ mSwapMethod(SWAP_METHOD_UNDEFINED),
+ mHideCursorPermanent(FALSE),
+ mFlags(flags)
+{
+}
+
+// virtual
+void LLWindow::incBusyCount()
+{
+ ++mBusyCount;
+}
+
+// virtual
+void LLWindow::decBusyCount()
+{
+ if (mBusyCount > 0)
+ {
+ --mBusyCount;
+ }
+}
+
+void LLWindow::setCallbacks(LLWindowCallbacks *callbacks)
+{
+ mCallbacks = callbacks;
+ if (gKeyboard)
+ {
+ gKeyboard->setCallbacks(callbacks);
+ }
+}
+
+//
+// LLSplashScreen
+//
+
+// static
+bool LLSplashScreen::isVisible()
+{
+ return gSplashScreenp ? true: false;
+}
+
+// static
+LLSplashScreen *LLSplashScreen::create()
+{
+#if LL_MESA_HEADLESS || LL_SDL // !!! FIXME
+ return 0;
+#elif LL_WINDOWS
+ return new LLSplashScreenWin32;
+#elif LL_DARWIN
+ return new LLSplashScreenMacOSX;
+#else
+#error("LLSplashScreen not implemented on this platform!")
+#endif
+}
+
+
+//static
+void LLSplashScreen::show()
+{
+ if (!gSplashScreenp)
+ {
+#if LL_WINDOWS && !LL_MESA_HEADLESS
+ gSplashScreenp = new LLSplashScreenWin32;
+#elif LL_DARWIN
+ gSplashScreenp = new LLSplashScreenMacOSX;
+#endif
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->showImpl();
+ }
+ }
+}
+
+//static
+void LLSplashScreen::update(const char* str)
+{
+ LLSplashScreen::show();
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->updateImpl(str);
+ }
+}
+
+//static
+void LLSplashScreen::hide()
+{
+ if (gSplashScreenp)
+ {
+ gSplashScreenp->hideImpl();
+ }
+ delete gSplashScreenp;
+ gSplashScreenp = NULL;
+}
+
+//
+// LLWindowManager
+//
+
+LLLinkedList<LLWindow> LLWindowManager::sWindowList;
+
+LLWindow* LLWindowManager::createWindow(
+ char *title,
+ char *name,
+ LLCoordScreen upper_left,
+ LLCoordScreen size,
+ U32 flags,
+ BOOL fullscreen,
+ BOOL clearBg,
+ BOOL disable_vsync,
+ BOOL use_gl,
+ BOOL ignore_pixel_depth)
+{
+ return createWindow(
+ title, name, upper_left.mX, upper_left.mY, size.mX, size.mY, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+}
+
+LLWindow* LLWindowManager::createWindow(
+ char *title, char *name, S32 x, S32 y, S32 width, S32 height, U32 flags,
+ BOOL fullscreen,
+ BOOL clearBg,
+ BOOL disable_vsync,
+ BOOL use_gl,
+ BOOL ignore_pixel_depth)
+{
+ LLWindow* new_window;
+
+ if (use_gl)
+ {
+#if LL_MESA_HEADLESS
+ new_window = new LLWindowMesaHeadless(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_SDL
+ new_window = new LLWindowSDL(
+ title, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_WINDOWS
+ new_window = new LLWindowWin32(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_DARWIN
+ new_window = new LLWindowMacOSX(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#elif LL_LINUX
+ new_window = new LLWindowLinux(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+#endif
+ }
+ else
+ {
+ new_window = new LLWindowHeadless(
+ title, name, x, y, width, height, flags,
+ fullscreen, clearBg, disable_vsync, use_gl, ignore_pixel_depth);
+ }
+
+ if (FALSE == new_window->isValid())
+ {
+ delete new_window;
+ llwarns << "LLWindowManager::create() : Error creating window." << llendl;
+ return NULL;
+ }
+ sWindowList.addDataAtEnd(new_window);
+ return new_window;
+}
+
+BOOL LLWindowManager::destroyWindow(LLWindow* window)
+{
+ if (!sWindowList.checkData(window))
+ {
+ llerrs << "LLWindowManager::destroyWindow() : Window pointer not valid, this window doesn't exist!"
+ << llendl;
+ return FALSE;
+ }
+
+ window->close();
+
+ sWindowList.removeData(window);
+
+ delete window;
+
+ return TRUE;
+}
+
+BOOL LLWindowManager::isWindowValid(LLWindow *window)
+{
+ return sWindowList.checkData(window);
+}
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
new file mode 100644
index 0000000000..703eee32d0
--- /dev/null
+++ b/indra/llwindow/llwindow.h
@@ -0,0 +1,328 @@
+/**
+ * @file llwindow.h
+ * @brief Basic graphical window class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOW_H
+#define LL_LLWINDOW_H
+
+#include <sys/stat.h>
+
+#include "llrect.h"
+#include "linked_lists.h"
+#include "llcoord.h"
+#include "llstring.h"
+
+
+enum ECursorType {
+ UI_CURSOR_ARROW,
+ UI_CURSOR_WAIT,
+ UI_CURSOR_HAND,
+ UI_CURSOR_IBEAM,
+ UI_CURSOR_CROSS,
+ UI_CURSOR_SIZENWSE,
+ UI_CURSOR_SIZENESW,
+ UI_CURSOR_SIZEWE,
+ UI_CURSOR_SIZENS,
+ UI_CURSOR_NO,
+ UI_CURSOR_WORKING,
+ UI_CURSOR_TOOLGRAB,
+ UI_CURSOR_TOOLLAND,
+ UI_CURSOR_TOOLFOCUS,
+ UI_CURSOR_TOOLCREATE,
+ UI_CURSOR_ARROWDRAG,
+ UI_CURSOR_ARROWCOPY, // drag with copy
+ UI_CURSOR_ARROWDRAGMULTI,
+ UI_CURSOR_ARROWCOPYMULTI, // drag with copy
+ UI_CURSOR_NOLOCKED,
+ UI_CURSOR_ARROWLOCKED,
+ UI_CURSOR_GRABLOCKED,
+ UI_CURSOR_TOOLTRANSLATE,
+ UI_CURSOR_TOOLROTATE,
+ UI_CURSOR_TOOLSCALE,
+ UI_CURSOR_TOOLCAMERA,
+ UI_CURSOR_TOOLPAN,
+ UI_CURSOR_TOOLZOOMIN,
+ UI_CURSOR_TOOLPICKOBJECT3,
+ UI_CURSOR_TOOLSIT,
+ UI_CURSOR_TOOLBUY,
+ UI_CURSOR_TOOLPAY,
+ UI_CURSOR_TOOLOPEN,
+ UI_CURSOR_PIPETTE,
+ UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor)
+};
+
+class LLSplashScreen;
+
+class LLWindow;
+
+class LLWindowCallbacks
+{
+public:
+ virtual ~LLWindowCallbacks() {}
+ virtual BOOL handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated);
+ virtual BOOL handleTranslatedKeyUp(KEY key, MASK mask);
+ virtual void handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+ virtual BOOL handleUnicodeChar(llwchar uni_char, MASK mask);
+
+ virtual BOOL handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual void handleMouseLeave(LLWindow *window);
+ // return TRUE to allow window to close, which will then cause handleQuit to be called
+ virtual BOOL handleCloseRequest(LLWindow *window);
+ // window is about to be destroyed, clean up your business
+ virtual void handleQuit(LLWindow *window);
+ virtual BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual BOOL handleActivate(LLWindow *window, BOOL activated);
+ virtual void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
+ virtual void handleScrollWheel(LLWindow *window, S32 clicks);
+ virtual void handleResize(LLWindow *window, S32 width, S32 height);
+ virtual void handleFocus(LLWindow *window);
+ virtual void handleFocusLost(LLWindow *window);
+ virtual void handleMenuSelect(LLWindow *window, S32 menu_item);
+ virtual BOOL handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height);
+ virtual BOOL handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask); // double-click of left mouse button
+ virtual void handleWindowBlock(LLWindow *window); // window is taking over CPU for a while
+ virtual void handleWindowUnblock(LLWindow *window); // window coming back after taking over CPU for a while
+ virtual void handleDataCopy(LLWindow *window, S32 data_type, void *data);
+};
+
+// Refer to llwindow_test in test/common/llwindow for usage example
+
+class LLWindow
+{
+public:
+ struct LLWindowResolution
+ {
+ S32 mWidth;
+ S32 mHeight;
+ };
+ enum ESwapMethod
+ {
+ SWAP_METHOD_UNDEFINED,
+ SWAP_METHOD_EXCHANGE,
+ SWAP_METHOD_COPY
+ };
+ enum EFlags
+ {
+ // currently unused
+ };
+public:
+ virtual void show() = 0;
+ virtual void hide() = 0;
+ virtual void close() = 0;
+ virtual BOOL getVisible() = 0;
+ virtual BOOL getMinimized() = 0;
+ virtual BOOL getMaximized() = 0;
+ virtual BOOL maximize() = 0;
+ BOOL getFullscreen() { return mFullscreen; };
+ virtual BOOL getPosition(LLCoordScreen *position) = 0;
+ virtual BOOL getSize(LLCoordScreen *size) = 0;
+ virtual BOOL getSize(LLCoordWindow *size) = 0;
+ virtual BOOL setPosition(LLCoordScreen position) = 0;
+ virtual BOOL setSize(LLCoordScreen size) = 0;
+ virtual BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) = 0;
+ virtual BOOL setCursorPosition(LLCoordWindow position) = 0;
+ virtual BOOL getCursorPosition(LLCoordWindow *position) = 0;
+ virtual void showCursor() = 0;
+ virtual void hideCursor() = 0;
+ virtual BOOL isCursorHidden() = 0;
+ virtual void showCursorFromMouseMove() = 0;
+ virtual void hideCursorUntilMouseMove() = 0;
+
+ // These two functions create a way to make a busy cursor instead
+ // of an arrow when someone's busy doing something. Draw an
+ // arrow/hour if busycount > 0.
+ virtual void incBusyCount();
+ virtual void decBusyCount();
+ virtual void resetBusyCount() { mBusyCount = 0; }
+ virtual S32 getBusyCount() const { return mBusyCount; }
+
+ // Sets cursor, may set to arrow+hourglass
+ virtual void setCursor(ECursorType cursor) = 0;
+ virtual ECursorType getCursor() const { return mCurrentCursor; }
+
+ virtual void captureMouse() = 0;
+ virtual void releaseMouse() = 0;
+ virtual void setMouseClipping( BOOL b ) = 0;
+ virtual BOOL isClipboardTextAvailable() = 0;
+ virtual BOOL pasteTextFromClipboard(LLWString &dst) = 0;
+ virtual BOOL copyTextToClipboard(const LLWString &src) = 0;
+ virtual void flashIcon(F32 seconds) = 0;
+ virtual F32 getGamma() = 0;
+ virtual BOOL setGamma(const F32 gamma) = 0; // Set the gamma
+ virtual BOOL restoreGamma() = 0; // Restore original gamma table (before updating gamma)
+ virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
+ virtual void gatherInput() = 0;
+ virtual void delayInputProcessing() = 0;
+ virtual void swapBuffers() = 0;
+ virtual void bringToFront() = 0;
+ virtual void focusClient() { }; // this may not have meaning or be required on other platforms, therefore, it's not abstract
+
+ virtual S32 stat( const char* file_name, struct stat* stat_info ) = 0;
+ virtual BOOL sendEmail(const char* address,const char* subject,const char* body_text, const char* attachment=NULL, const char* attachment_displayed_name=NULL ) = 0;
+
+
+ // handy coordinate space conversion routines
+ // NB: screen to window and vice verse won't work on width/height coordinate pairs,
+ // as the conversion must take into account left AND right border widths, etc.
+ virtual BOOL convertCoords( LLCoordScreen from, LLCoordWindow *to) = 0;
+ virtual BOOL convertCoords( LLCoordWindow from, LLCoordScreen *to) = 0;
+ virtual BOOL convertCoords( LLCoordWindow from, LLCoordGL *to) = 0;
+ virtual BOOL convertCoords( LLCoordGL from, LLCoordWindow *to) = 0;
+ virtual BOOL convertCoords( LLCoordScreen from, LLCoordGL *to) = 0;
+ virtual BOOL convertCoords( LLCoordGL from, LLCoordScreen *to) = 0;
+
+ // query supported resolutions
+ virtual LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) = 0;
+ virtual F32 getNativeAspectRatio() = 0;
+ virtual F32 getPixelAspectRatio() = 0;
+ virtual void setNativeAspectRatio(F32 aspect) = 0;
+
+ void setCallbacks(LLWindowCallbacks *callbacks);
+
+ virtual void beforeDialog() {}; // prepare to put up an OS dialog (if special measures are required, such as in fullscreen mode)
+ virtual void afterDialog() {}; // undo whatever was done in beforeDialog()
+
+// opens system default color picker
+ virtual BOOL dialog_color_picker (F32 *r, F32 *g, F32 *b) { return FALSE; };
+
+// return a platform-specific window reference (HWND on Windows, WindowRef on the Mac)
+ virtual void *getPlatformWindow() = 0;
+
+protected:
+ LLWindow(BOOL fullscreen, U32 flags);
+ virtual ~LLWindow() {}
+ virtual BOOL isValid() {return TRUE;}
+ virtual BOOL canDelete() {return TRUE;}
+protected:
+ static LLWindowCallbacks sDefaultCallbacks;
+
+protected:
+ LLWindowCallbacks* mCallbacks;
+
+ BOOL mPostQuit; // should this window post a quit message when destroyed?
+ BOOL mFullscreen;
+ S32 mFullscreenWidth;
+ S32 mFullscreenHeight;
+ S32 mFullscreenBits;
+ S32 mFullscreenRefresh;
+ LLWindowResolution* mSupportedResolutions;
+ S32 mNumSupportedResolutions;
+ ECursorType mCurrentCursor;
+ BOOL mCursorHidden;
+ S32 mBusyCount; // how deep is the "cursor busy" stack?
+ BOOL mIsMouseClipping; // Is this window currently clipping the mouse
+ ESwapMethod mSwapMethod;
+ BOOL mHideCursorPermanent;
+ U32 mFlags;
+
+ friend class LLWindowManager;
+};
+
+
+// LLSplashScreen
+// A simple, OS-specific splash screen that we can display
+// while initializing the application and before creating a GL
+// window
+
+
+class LLSplashScreen
+{
+public:
+ LLSplashScreen() { };
+ virtual ~LLSplashScreen() { };
+
+
+ // Call to display the window.
+ static LLSplashScreen * create();
+ static void show();
+ static void hide();
+ static void update(const char* string);
+
+ static bool isVisible();
+protected:
+ // These are overridden by the platform implementation
+ virtual void showImpl() = 0;
+ virtual void updateImpl(const char* string) = 0;
+ virtual void hideImpl() = 0;
+
+ static BOOL sVisible;
+
+};
+
+// Platform-neutral for accessing the platform specific message box
+S32 OSMessageBox(const char* text, const char* caption, U32 type);
+const U32 OSMB_OK = 0;
+const U32 OSMB_OKCANCEL = 1;
+const U32 OSMB_YESNO = 2;
+
+const S32 OSBTN_YES = 0;
+const S32 OSBTN_NO = 1;
+const S32 OSBTN_OK = 2;
+const S32 OSBTN_CANCEL = 3;
+
+//
+// LLWindowManager
+// Manages window creation and error checking
+
+class LLWindowManager
+{
+private:
+ static LLLinkedList<LLWindow> sWindowList;
+
+public:
+ static LLWindow* createWindow(
+ char *title,
+ char *name,
+ LLCoordScreen upper_left = LLCoordScreen(10, 10),
+ LLCoordScreen size = LLCoordScreen(320, 240),
+ U32 flags = 0,
+ BOOL fullscreen = FALSE,
+ BOOL clearBg = FALSE,
+ BOOL disable_vsync = TRUE,
+ BOOL use_gl = TRUE,
+ BOOL ignore_pixel_depth = FALSE);
+ static LLWindow *createWindow(
+ char* title, char* name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags = 0,
+ BOOL fullscreen = FALSE,
+ BOOL clearBg = FALSE,
+ BOOL disable_vsync = TRUE,
+ BOOL use_gl = TRUE,
+ BOOL ignore_pixel_depth = FALSE);
+ static BOOL destroyWindow(LLWindow* window);
+ static BOOL isWindowValid(LLWindow *window);
+};
+
+//
+// helper funcs
+//
+
+// Protocols, like "http" and "https" we support in URLs
+extern const S32 gURLProtocolWhitelistCount;
+extern const char* gURLProtocolWhitelist[];
+extern const char* gURLProtocolWhitelistHandler[];
+
+// Loads a URL with the user's default browser
+void spawn_web_browser(const char* escaped_url);
+
+// Opens a file with ShellExecute. Security risk!
+void shell_open(const char* file_path);
+
+void simpleEscapeString ( std::string& stringIn );
+
+
+#if LL_WINDOWS
+ // return Win32 specific window handle
+ HWND llwindow_get_hwnd(LLWindow *window);
+
+ // backdoor for special case handling of Win32 messages
+ void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc);
+#endif
+
+#endif // _LL_window_h_
diff --git a/indra/llwindow/llwindowheadless.cpp b/indra/llwindow/llwindowheadless.cpp
new file mode 100644
index 0000000000..1251ed1bb7
--- /dev/null
+++ b/indra/llwindow/llwindowheadless.cpp
@@ -0,0 +1,32 @@
+/**
+ * @file llwindowheadless.cpp
+ * @brief Headless implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "indra_constants.h"
+
+#include "llwindowheadless.h"
+
+//
+// LLWindowHeadless
+//
+LLWindowHeadless::LLWindowHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+}
+
+
+LLWindowHeadless::~LLWindowHeadless()
+{
+}
+
+void LLWindowHeadless::swapBuffers()
+{
+}
+
diff --git a/indra/llwindow/llwindowheadless.h b/indra/llwindow/llwindowheadless.h
new file mode 100644
index 0000000000..7c9b2e2d77
--- /dev/null
+++ b/indra/llwindow/llwindowheadless.h
@@ -0,0 +1,98 @@
+/**
+ * @file llwindowheadless.h
+ * @brief Headless definition of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWHEADLESS_H
+#define LL_LLWINDOWHEADLESS_H
+
+#include "llwindow.h"
+
+class LLWindowHeadless : public LLWindow
+{
+public:
+ /*virtual*/ void show() {};
+ /*virtual*/ void hide() {};
+ /*virtual*/ void close() {};
+ /*virtual*/ BOOL getVisible() {return FALSE;};
+ /*virtual*/ BOOL getMinimized() {return FALSE;};
+ /*virtual*/ BOOL getMaximized() {return FALSE;};
+ /*virtual*/ BOOL maximize() {return FALSE;};
+ /*virtual*/ BOOL getFullscreen() {return FALSE;};
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position) {return FALSE;};
+ /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;};
+ /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;};
+ /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;};
+ /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;};
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) {return FALSE;};
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
+ /*virtual*/ void showCursor() {};
+ /*virtual*/ void hideCursor() {};
+ /*virtual*/ void showCursorFromMouseMove() {};
+ /*virtual*/ void hideCursorUntilMouseMove() {};
+ /*virtual*/ BOOL isCursorHidden() {return FALSE;};
+ /*virtual*/ void setCursor(ECursorType cursor) {};
+ //virtual ECursorType getCursor() { return mCurrentCursor; };
+ /*virtual*/ void captureMouse() {};
+ /*virtual*/ void releaseMouse() {};
+ /*virtual*/ void setMouseClipping( BOOL b ) {};
+ /*virtual*/ BOOL isClipboardTextAvailable() {return FALSE; };
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst) {return FALSE; };
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString &src) {return FALSE; };
+ /*virtual*/ void flashIcon(F32 seconds) {};
+ /*virtual*/ F32 getGamma() {return 1.0f; };
+ /*virtual*/ BOOL setGamma(const F32 gamma) {return FALSE; }; // Set the gamma
+ /*virtual*/ BOOL restoreGamma() {return FALSE; }; // Restore original gamma table (before updating gamma)
+ //virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput() {};
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName() {return LLString(""); };
+ /*virtual*/ void deleteFile( const char* file_name ) {};
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info ) {return 0; };
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL) { return FALSE; };
+
+
+ // handy coordinate space conversion routines
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) { return FALSE; };
+
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; };
+ /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; };
+ /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; };
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) {}
+
+ /*virtual*/ void *getPlatformWindow() { return 0; };
+ /*virtual*/ void bringToFront() {};
+
+ LLWindowHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth);
+ virtual ~LLWindowHeadless();
+
+private:
+};
+
+class LLSplashScreenHeadless : public LLSplashScreen
+{
+public:
+ LLSplashScreenHeadless() {};
+ virtual ~LLSplashScreenHeadless() {};
+
+ /*virtual*/ void showImpl() {};
+ /*virtual*/ void updateImpl(const char* mesg) {};
+ /*virtual*/ void hideImpl() {};
+
+};
+
+#endif //LL_LLWINDOWHEADLESS_H
+
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
new file mode 100644
index 0000000000..86ac74ff1f
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -0,0 +1,19 @@
+/**
+ * @file llwindowmacosx-objc.h
+ * @brief Prototypes for functions shared between llwindowmacosx.cpp
+ * and llwindowmacosx-objc.mm.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+// This will actually hold an NSCursor*, but that type is only available in objective C.
+typedef void *CursorRef;
+
+/* Defined in llwindowmacosx-objc.mm: */
+void setupCocoa();
+CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY);
+OSErr releaseImageCursor(CursorRef ref);
+OSErr setImageCursor(CursorRef ref);
+
diff --git a/indra/llwindow/llwindowmacosx-objc.mm b/indra/llwindow/llwindowmacosx-objc.mm
new file mode 100644
index 0000000000..82d2babab5
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx-objc.mm
@@ -0,0 +1,87 @@
+/**
+ * @file llwindowmacosx-objc.mm
+ * @brief Definition of functions shared between llwindowmacosx.cpp
+ * and llwindowmacosx-objc.mm.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <AppKit/AppKit.h>
+
+/*
+ * These functions are broken out into a separate file because the
+ * objective-C typedef for 'BOOL' conflicts with the one in
+ * llcommon/stdtypes.h. This makes it impossible to use the standard
+ * linden headers with any objective-C++ source.
+ */
+
+#include "llwindowmacosx-objc.h"
+
+void setupCocoa()
+{
+ static bool inited = false;
+
+ if(!inited)
+ {
+ // This is a bit of voodoo taken from the Apple sample code "CarbonCocoa_PictureCursor":
+ // http://developer.apple.com/samplecode/CarbonCocoa_PictureCursor/index.html
+
+ // Needed for Carbon based applications which call into Cocoa
+ NSApplicationLoad();
+
+ // Must first call [[[NSWindow alloc] init] release] to get the NSWindow machinery set up so that NSCursor can use a window to cache the cursor image
+ [[[NSWindow alloc] init] release];
+ }
+}
+
+CursorRef createImageCursor(const char *fullpath, int hotspotX, int hotspotY)
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+ // extra retain on the NSCursor since we want it to live for the lifetime of the app.
+ NSCursor *cursor =
+ [[[NSCursor alloc]
+ initWithImage:
+ [[[NSImage alloc] initWithContentsOfFile:
+ [NSString stringWithFormat:@"%s", fullpath]
+ ]autorelease]
+ hotSpot:NSMakePoint(hotspotX, hotspotY)
+ ]retain];
+
+ [pool release];
+
+ return (CursorRef)cursor;
+}
+
+// This is currently unused, since we want all our cursors to persist for the life of the app, but I've included it for completeness.
+OSErr releaseImageCursor(CursorRef ref)
+{
+ if( ref != NULL )
+ {
+ NSCursor *cursor = (NSCursor*)ref;
+ [cursor release];
+ }
+ else
+ {
+ return paramErr;
+ }
+
+ return noErr;
+}
+
+OSErr setImageCursor(CursorRef ref)
+{
+ if( ref != NULL )
+ {
+ NSCursor *cursor = (NSCursor*)ref;
+ [cursor set];
+ }
+ else
+ {
+ return paramErr;
+ }
+
+ return noErr;
+}
+
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
new file mode 100644
index 0000000000..4d75a30a8e
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -0,0 +1,2904 @@
+/**
+ * @file llwindowmacosx.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_DARWIN
+
+#include "linden_common.h"
+
+#include <Carbon/Carbon.h>
+
+#include "llwindowmacosx.h"
+#include "llkeyboardmacosx.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+#include "llwindowmacosx-objc.h"
+
+extern BOOL gDebugWindowProc;
+
+// culled from winuser.h
+//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+// On the Mac, the scroll wheel reports a delta of 1 for each detent.
+// There's also acceleration for faster scrolling, based on a slider in the system preferences.
+const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+
+
+//
+// LLWindowMacOSX
+//
+
+// Cross-platform bits:
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+// Switch to determine whether we capture all displays, or just the main one.
+// We may want to base this on the setting of _DEBUG...
+
+#define CAPTURE_ALL_DISPLAYS 0
+static double getDictDouble (CFDictionaryRef refDict, CFStringRef key);
+static long getDictLong (CFDictionaryRef refDict, CFStringRef key);
+
+
+
+
+// CarbonEvents we're interested in.
+static EventTypeSpec WindowHandlerEventList[] =
+{
+ // Window-related events
+ // { kEventClassWindow, kEventWindowCollapsing },
+ // { kEventClassWindow, kEventWindowCollapsed },
+ // { kEventClassWindow, kEventWindowShown },
+ { kEventClassWindow, kEventWindowActivated },
+ { kEventClassWindow, kEventWindowDeactivated },
+ { kEventClassWindow, kEventWindowShown },
+ { kEventClassWindow, kEventWindowHidden },
+ { kEventClassWindow, kEventWindowCollapsed },
+ { kEventClassWindow, kEventWindowExpanded },
+ { kEventClassWindow, kEventWindowGetClickActivation },
+ { kEventClassWindow, kEventWindowClose },
+ { kEventClassWindow, kEventWindowBoundsChanging },
+ { kEventClassWindow, kEventWindowBoundsChanged },
+ // { kEventClassWindow, kEventWindowZoomed },
+ // { kEventClassWindow, kEventWindowDrawContent },
+
+ // Mouse events
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseMoved },
+
+ // Keyboard events
+ // No longer handle raw key down events directly.
+ // When text input events come in, extract the raw key events from them and process at that point.
+ // This allows input methods to eat keystrokes the way they're supposed to.
+// { kEventClassKeyboard, kEventRawKeyDown },
+// { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+
+ // Text input events
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+
+};
+
+static EventTypeSpec GlobalHandlerEventList[] =
+{
+ // Mouse events
+ { kEventClassMouse, kEventMouseDown },
+ { kEventClassMouse, kEventMouseUp },
+ { kEventClassMouse, kEventMouseDragged },
+ { kEventClassMouse, kEventMouseWheelMoved },
+ { kEventClassMouse, kEventMouseMoved },
+
+ // Keyboard events
+ // No longer handle raw key down events directly.
+ // When text input events come in, extract the raw key events from them and process at that point.
+ // This allows input methods to eat keystrokes the way they're supposed to.
+// { kEventClassKeyboard, kEventRawKeyDown },
+// { kEventClassKeyboard, kEventRawKeyRepeat },
+ { kEventClassKeyboard, kEventRawKeyUp },
+ { kEventClassKeyboard, kEventRawKeyModifiersChanged },
+
+ // Text input events
+ { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent }
+};
+
+static EventTypeSpec CommandHandlerEventList[] =
+{
+ { kEventClassCommand, kEventCommandProcess }
+};
+
+// MBW -- HACK ALERT
+// On the Mac, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
+// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
+// require a pointer to the LLWindowMacOSX object. Stash it here and maintain in the constructor and destructor.
+// This assumes that there will be only one object of this class at any time. Hopefully this is true.
+static LLWindowMacOSX *gWindowImplementation = NULL;
+
+
+
+LLWindowMacOSX::LLWindowMacOSX(char *title, char *name, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ // Voodoo for calling cocoa from carbon (see llwindowmacosx-objc.mm).
+ setupCocoa();
+
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardMacOSX();
+
+ // Ignore use_gl for now, only used for drones on PC
+ mWindow = NULL;
+ mContext = NULL;
+ mPixelFormat = NULL;
+ mDisplay = CGMainDisplayID();
+ mOldDisplayMode = NULL;
+ mTimer = NULL;
+ mSimulatedRightClick = FALSE;
+ mLastModifiers = 0;
+ mHandsOffEvents = FALSE;
+ mCursorDecoupled = FALSE;
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ mNeedsResize = FALSE;
+ mOverrideAspectRatio = 0.f;
+ mMinimized = FALSE;
+
+ // For reasons that aren't clear to me, LLTimers seem to be created in the "started" state.
+ // Since the started state of this one is used to track whether the NMRec has been installed, it wants to start out in the "stopped" state.
+ mBounceTimer.stop();
+
+ // Get the original aspect ratio of the main device.
+ mOriginalAspectRatio = (double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
+
+ // Stash the window title
+ strcpy((char*)mWindowTitle + 1, title);
+ mWindowTitle[0] = strlen(title);
+
+ mEventHandlerUPP = NewEventHandlerUPP(staticEventHandler);
+ mGlobalHandlerRef = NULL;
+ mWindowHandlerRef = NULL;
+
+ // We're not clipping yet
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+
+ // Set up global event handlers (the fullscreen case needs this)
+ InstallStandardEventHandler(GetApplicationEventTarget());
+
+ // Stash an object pointer for OSMessageBox()
+ gWindowImplementation = this;
+
+ // Create the GL context and set it up for windowed or fullscreen, as appropriate.
+ if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+ {
+ if(mWindow != NULL)
+ {
+ // MBW -- XXX -- I think we can now do this here?
+ // Constrain the window to the screen it's mostly on, resizing if necessary.
+ ConstrainWindowToScreen(
+ mWindow,
+ kWindowStructureRgn,
+ kWindowConstrainMayResize |
+ // kWindowConstrainStandardOptions |
+ 0,
+ NULL,
+ NULL);
+
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+ }
+
+ if (!gGLManager.initGL())
+ {
+ setupFailure(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+
+ stop_glerror();
+}
+
+BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+{
+ OSStatus err;
+ BOOL glNeedsInit = FALSE;
+
+ if(mGlobalHandlerRef == NULL)
+ {
+ InstallApplicationEventHandler(mEventHandlerUPP, GetEventTypeCount (CommandHandlerEventList), CommandHandlerEventList, (void*)this, &mGlobalHandlerRef);
+ }
+
+ mFullscreen = fullscreen;
+
+ if (mFullscreen && (mOldDisplayMode == NULL))
+ {
+ llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
+
+ // NOTE: The refresh rate will be REPORTED AS 0 for many DVI and notebook displays. Plan accordingly.
+ double refresh = getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate);
+
+ // If the requested width or height is 0, find the best default for the monitor.
+ if((width == 0) || (height == 0))
+ {
+ // Scan through the list of modes, looking for one which has:
+ // height between 700 and 800
+ // aspect ratio closest to the user's original mode
+ S32 resolutionCount = 0;
+ LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
+
+ if(resolutionList != NULL)
+ {
+ F32 closestAspect = 0;
+ U32 closestHeight = 0;
+ U32 closestWidth = 0;
+ int i;
+
+ llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
+
+ for(i=0; i < resolutionCount; i++)
+ {
+ F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
+
+ llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
+
+ if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
+ (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
+ {
+ llinfos << " (new closest mode) " << llendl;
+
+ // This is the closest mode we've seen yet.
+ closestWidth = resolutionList[i].mWidth;
+ closestHeight = resolutionList[i].mHeight;
+ closestAspect = aspect;
+ }
+ }
+
+ width = closestWidth;
+ height = closestHeight;
+ }
+ }
+
+ if((width == 0) || (height == 0))
+ {
+ // Mode search failed for some reason. Use the old-school default.
+ width = 1024;
+ height = 768;
+ }
+
+ if (true)
+ {
+ // Fullscreen support
+ CFDictionaryRef refDisplayMode = 0;
+ boolean_t exactMatch = false;
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ CGCaptureAllDisplays ();
+#else
+ // Capture only the main display (useful for debugging)
+ CGDisplayCapture (mDisplay);
+#endif
+
+ // Switch the display to the desired resolution and refresh
+ refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
+ mDisplay,
+ BITS_PER_PIXEL,
+ width,
+ height,
+ refresh,
+ &exactMatch);
+
+ if (refDisplayMode)
+ {
+ llinfos << "createContext: switching display resolution" << llendl;
+ mOldDisplayMode = CGDisplayCurrentMode (mDisplay);
+ CGDisplaySwitchToMode (mDisplay, refDisplayMode);
+ // CFRelease(refDisplayMode);
+
+ AddEventTypesToHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
+ }
+
+
+ mFullscreen = TRUE;
+ mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
+ mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
+ mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
+ mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
+
+ llinfos << "Running at " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+ }
+ else
+ {
+ // No fullscreen support
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ if(!mFullscreen && (mWindow == NULL))
+ {
+ Rect window_rect;
+ //int displayWidth = CGDisplayPixelsWide(mDisplay);
+ //int displayHeight = CGDisplayPixelsHigh(mDisplay);
+ //const int menuBarPlusTitleBar = 44; // Ugly magic number.
+
+ llinfos << "createContext: creating window" << llendl;
+
+ window_rect.left = (long) x;
+ window_rect.right = (long) x + width;
+ window_rect.top = (long) y;
+ window_rect.bottom = (long) y + height;
+
+ //-----------------------------------------------------------------------
+ // Create the window
+ //-----------------------------------------------------------------------
+ mWindow = NewCWindow(
+ NULL,
+ &window_rect,
+ mWindowTitle,
+ false, // Create the window invisible. Whoever calls createContext() should show it after any moving/resizing.
+ // noGrowDocProc, // Window with no grow box and no zoom box
+ zoomDocProc, // Window with a grow box and a zoom box
+ // zoomNoGrow, // Window with a zoom box but no grow box
+ kFirstWindowOfClass,
+ true,
+ (long)this);
+
+
+ if (!mWindow)
+ {
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ // Turn on live resize.
+ // For this to work correctly, we need to be able to call LLViewerWindow::draw from
+ // the event handler for kEventWindowBoundsChanged. It's not clear that we have access from here.
+ // err = ChangeWindowAttributes(mWindow, kWindowLiveResizeAttribute, 0);
+
+ // Set up window event handlers (some window-related events ONLY go to window handlers.)
+ InstallStandardEventHandler(GetWindowEventTarget(mWindow));
+ InstallWindowEventHandler (mWindow, mEventHandlerUPP, GetEventTypeCount (WindowHandlerEventList), WindowHandlerEventList, (void*)this, &mWindowHandlerRef); // add event handler
+
+ }
+
+ if(mContext == NULL)
+ {
+ AGLRendererInfo rendererInfo = NULL;
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+
+ if(mPixelFormat == NULL)
+ {
+ if(mFullscreen)
+ {
+ GLint fullscreenAttrib[] =
+ {
+ AGL_RGBA,
+ AGL_FULLSCREEN,
+ // AGL_NO_RECOVERY, // MBW -- XXX -- Not sure if we want this attribute
+ AGL_DOUBLEBUFFER,
+ AGL_CLOSEST_POLICY,
+ AGL_ACCELERATED,
+ AGL_RED_SIZE, 8,
+ AGL_GREEN_SIZE, 8,
+ AGL_BLUE_SIZE, 8,
+ AGL_ALPHA_SIZE, 8,
+ AGL_DEPTH_SIZE, 24,
+ AGL_STENCIL_SIZE, 8,
+ AGL_NONE
+ };
+
+ llinfos << "createContext: creating fullscreen pixelformat" << llendl;
+
+ GDHandle gdhDisplay = NULL;
+ err = DMGetGDeviceByDisplayID ((DisplayIDType)mDisplay, &gdhDisplay, false);
+
+ mPixelFormat = aglChoosePixelFormat(&gdhDisplay, 1, fullscreenAttrib);
+ rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
+ }
+ else
+ {
+ GLint windowedAttrib[] =
+ {
+ AGL_RGBA,
+ AGL_DOUBLEBUFFER,
+ AGL_CLOSEST_POLICY,
+ AGL_ACCELERATED,
+ AGL_RED_SIZE, 8,
+ AGL_GREEN_SIZE, 8,
+ AGL_BLUE_SIZE, 8,
+ AGL_ALPHA_SIZE, 8,
+ AGL_DEPTH_SIZE, 24,
+ AGL_STENCIL_SIZE, 8,
+ AGL_NONE
+ };
+
+ llinfos << "createContext: creating windowed pixelformat" << llendl;
+
+ mPixelFormat = aglChoosePixelFormat(NULL, 0, windowedAttrib);
+
+ GDHandle gdhDisplay = GetMainDevice();
+ rendererInfo = aglQueryRendererInfo(&gdhDisplay, 1);
+ }
+
+ // May want to get the real error text like this:
+ // (char *) aglErrorString(aglGetError());
+
+ if(aglGetError() != AGL_NO_ERROR)
+ {
+ setupFailure("Can't find suitable pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+
+ if(mPixelFormat)
+ {
+ llinfos << "createContext: creating GL context" << llendl;
+ mContext = aglCreateContext(mPixelFormat, NULL);
+ }
+
+ if(mContext == NULL)
+ {
+ setupFailure("Can't make GL context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ gGLManager.mVRAM = 0;
+
+ if(rendererInfo != NULL)
+ {
+ GLint result;
+
+ if(aglDescribeRenderer(rendererInfo, AGL_VIDEO_MEMORY, &result))
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) returned " << result << llendl;
+ gGLManager.mVRAM = result / (1024 * 1024);
+ }
+ else
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_VIDEO_MEMORY) failed." << llendl;
+ }
+
+ // This could be useful at some point, if it takes into account the memory already used by screen buffers, etc...
+ if(aglDescribeRenderer(rendererInfo, AGL_TEXTURE_MEMORY, &result))
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) returned " << result << llendl;
+ }
+ else
+ {
+ // llinfos << "createContext: aglDescribeRenderer(AGL_TEXTURE_MEMORY) failed." << llendl;
+ }
+
+ aglDestroyRendererInfo(rendererInfo);
+ }
+
+ // Since we just created the context, it needs to be set up.
+ glNeedsInit = TRUE;
+ }
+
+ // Hook up the context to a drawable
+ if (mFullscreen && (mOldDisplayMode != NULL))
+ {
+ // We successfully captured the display. Use a fullscreen drawable
+
+ llinfos << "createContext: attaching fullscreen drawable" << llendl;
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ aglDisable (mContext, AGL_FS_CAPTURE_SINGLE);
+#else
+ // Capture only the main display (useful for debugging)
+ aglEnable (mContext, AGL_FS_CAPTURE_SINGLE);
+#endif
+
+ if (!aglSetFullScreen (mContext, 0, 0, 0, 0))
+ {
+ setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+ else if(!mFullscreen && (mWindow != NULL))
+ {
+ llinfos << "createContext: attaching windowed drawable" << llendl;
+
+ // We created a window. Use it as the drawable.
+ if(!aglSetDrawable(mContext, GetWindowPort (mWindow)))
+ {
+ setupFailure("Can't set GL drawable", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+ else
+ {
+ setupFailure("Can't get fullscreen or windowed drawable.", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if(mContext != NULL)
+ {
+ llinfos << "createContext: setting current context" << llendl;
+
+ if (!aglSetCurrentContext(mContext))
+ {
+ setupFailure("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+ }
+
+ if(glNeedsInit)
+ {
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return FALSE;
+ }
+ }
+ }
+
+ GLint colorBits, alphaBits, depthBits, stencilBits;
+
+ if( !aglDescribePixelFormat(mPixelFormat, AGL_BUFFER_SIZE, &colorBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_ALPHA_SIZE, &alphaBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_DEPTH_SIZE, &depthBits) ||
+ !aglDescribePixelFormat(mPixelFormat, AGL_STENCIL_SIZE, &stencilBits))
+ {
+ close();
+ setupFailure("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ llinfos << "GL buffer: Color Bits " << S32(colorBits)
+ << " Alpha Bits " << S32(alphaBits)
+ << " Depth Bits " << S32(depthBits)
+ << " Stencil Bits" << S32(stencilBits)
+ << llendl;
+
+ if (colorBits < 32)
+ {
+ close();
+ setupFailure(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (alphaBits < 8)
+ {
+ close();
+ setupFailure(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ // Disable vertical sync for swap
+ GLint frames_per_swap = 0;
+ if (disable_vsync)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ frames_per_swap = 0;
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ frames_per_swap = 1;
+ }
+ aglSetInteger(mContext, AGL_SWAP_INTERVAL, &frames_per_swap);
+
+ // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
+ return TRUE;
+}
+
+
+// changing fullscreen resolution, or switching between windowed and fullscreen mode.
+BOOL LLWindowMacOSX::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ BOOL needsRebuild = FALSE;
+ BOOL result = true;
+
+ if(fullscreen)
+ {
+ if(mFullscreen)
+ {
+ // Switching resolutions in fullscreen mode. Don't need to rebuild for this.
+ // Fullscreen support
+ CFDictionaryRef refDisplayMode = 0;
+ boolean_t exactMatch = false;
+
+ // Switch the display to the desired resolution and refresh
+ refDisplayMode = CGDisplayBestModeForParametersAndRefreshRate(
+ mDisplay,
+ BITS_PER_PIXEL,
+ size.mX,
+ size.mY,
+ getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate),
+ &exactMatch);
+
+ if (refDisplayMode)
+ {
+ CGDisplaySwitchToMode (mDisplay, refDisplayMode);
+ // CFRelease(refDisplayMode);
+ }
+
+ mFullscreenWidth = CGDisplayPixelsWide(mDisplay);
+ mFullscreenHeight = CGDisplayPixelsHigh(mDisplay);
+ mFullscreenBits = CGDisplayBitsPerPixel(mDisplay);
+ mFullscreenRefresh = llround(getDictDouble (CGDisplayCurrentMode (mDisplay), kCGDisplayRefreshRate));
+
+ llinfos << "Switched resolution to " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+
+ // Update the GL context to the new screen size
+ if (!aglUpdateContext(mContext))
+ {
+ setupFailure("Can't set GL fullscreen", "Error", OSMB_OK);
+ result = FALSE;
+ }
+ }
+ else
+ {
+ // Switching from windowed to fullscreen
+ needsRebuild = TRUE;
+ }
+ }
+ else
+ {
+ if(mFullscreen)
+ {
+ // Switching from fullscreen to windowed
+ needsRebuild = TRUE;
+ }
+ else
+ {
+ // Windowed to windowed -- not sure why we would be called like this. Just change the window size.
+ // The bounds changed event handler will do the rest.
+ if(mWindow != NULL)
+ {
+ ::SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+ }
+ }
+
+ stop_glerror();
+ if(needsRebuild)
+ {
+ destroyContext();
+ result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
+ if (result)
+ {
+ if(mWindow != NULL)
+ {
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+ }
+
+ llverify(gGLManager.initGL());
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+ }
+
+ stop_glerror();
+
+ return result;
+}
+
+void LLWindowMacOSX::destroyContext()
+{
+ if (!mContext)
+ {
+ // We don't have a context
+ return;
+ }
+ // Unhook the GL context from any drawable it may have
+ if(mContext != NULL)
+ {
+ llinfos << "destroyContext: unhooking drawable " << llendl;
+
+ aglSetCurrentContext (NULL);
+ aglSetDrawable(mContext, NULL);
+ }
+
+ // Make sure the display resolution gets restored
+ if(mOldDisplayMode != NULL)
+ {
+ llinfos << "destroyContext: restoring display resolution " << llendl;
+
+ CGDisplaySwitchToMode (mDisplay, mOldDisplayMode);
+
+#if CAPTURE_ALL_DISPLAYS
+ // Uncapture all displays (may want to do this for final build)
+ CGReleaseAllDisplays ();
+#else
+ // Uncapture only the main display (useful for debugging)
+ CGDisplayRelease (mDisplay);
+#endif
+
+ // CFRelease(mOldDisplayMode);
+
+ mOldDisplayMode = NULL;
+
+ // Remove the global event handlers the fullscreen case needed
+ RemoveEventTypesFromHandler(mGlobalHandlerRef, GetEventTypeCount (GlobalHandlerEventList), GlobalHandlerEventList);
+ }
+
+ // Clean up remaining GL state before blowing away window
+ gGLManager.shutdownGL();
+
+ // Clean up the pixel format
+ if(mPixelFormat != NULL)
+ {
+ llinfos << "destroyContext: destroying pixel format " << llendl;
+ aglDestroyPixelFormat(mPixelFormat);
+ mPixelFormat = NULL;
+ }
+
+ // Remove any Carbon Event handlers we installed
+ if(mGlobalHandlerRef != NULL)
+ {
+ llinfos << "destroyContext: removing global event handler" << llendl;
+ RemoveEventHandler(mGlobalHandlerRef);
+ mGlobalHandlerRef = NULL;
+ }
+
+ if(mWindowHandlerRef != NULL)
+ {
+ llinfos << "destroyContext: removing window event handler" << llendl;
+ RemoveEventHandler(mWindowHandlerRef);
+ mWindowHandlerRef = NULL;
+ }
+
+ // Close the window
+ if(mWindow != NULL)
+ {
+ llinfos << "destroyContext: disposing window" << llendl;
+ DisposeWindow(mWindow);
+ mWindow = NULL;
+ }
+
+ // Clean up the GL context
+ if(mContext != NULL)
+ {
+ llinfos << "destroyContext: destroying GL context" << llendl;
+ aglDestroyContext(mContext);
+ mContext = NULL;
+ }
+
+}
+
+LLWindowMacOSX::~LLWindowMacOSX()
+{
+ destroyContext();
+
+ if(mSupportedResolutions != NULL)
+ {
+ delete []mSupportedResolutions;
+ }
+
+ gWindowImplementation = NULL;
+
+}
+
+
+void LLWindowMacOSX::show()
+{
+ if(IsWindowCollapsed(mWindow))
+ CollapseWindow(mWindow, false);
+
+ MacShowWindow(mWindow);
+ BringToFront(mWindow);
+}
+
+void LLWindowMacOSX::hide()
+{
+ setMouseClipping(FALSE);
+ HideWindow(mWindow);
+}
+
+void LLWindowMacOSX::minimize()
+{
+ setMouseClipping(FALSE);
+ showCursor();
+ CollapseWindow(mWindow, true);
+}
+
+void LLWindowMacOSX::restore()
+{
+ show();
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowMacOSX::close()
+{
+ // Is window is already closed?
+ // if (!mWindow)
+ // {
+ // return;
+ // }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ destroyContext();
+}
+
+BOOL LLWindowMacOSX::isValid()
+{
+ if(mFullscreen)
+ {
+ return(TRUE);
+ }
+
+ return (mWindow != NULL);
+}
+
+BOOL LLWindowMacOSX::getVisible()
+{
+ BOOL result = FALSE;
+
+ if(mFullscreen)
+ {
+ result = TRUE;
+ }if (mWindow)
+ {
+ if(MacIsWindowVisible(mWindow))
+ result = TRUE;
+ }
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::getMinimized()
+{
+ BOOL result = FALSE;
+
+ // Since the set of states where we want to act "minimized" is non-trivial, it's easier to
+ // track things locally than to try and retrieve the state from the window manager.
+ result = mMinimized;
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::getMaximized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow)
+ {
+ // TODO
+ }
+
+ return(result);
+}
+
+BOOL LLWindowMacOSX::maximize()
+{
+ // TODO
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::getFullscreen()
+{
+ return mFullscreen;
+}
+
+void LLWindowMacOSX::gatherInput()
+{
+ // stop bouncing icon after fixed period of time
+ if (mBounceTimer.getStarted() && mBounceTimer.getElapsedTimeF32() > mBounceTime)
+ {
+ stopDockTileBounce();
+ }
+
+ // Use the old-school version so we get AppleEvent handler dispatch and menuselect handling.
+ // Anything that has an event handler will get processed inside WaitNextEvent, so we only need to handle
+ // the odd stuff here.
+ EventRecord evt;
+ while(WaitNextEvent(everyEvent, &evt, 0, NULL))
+ {
+ // printf("WaitNextEvent returned true, event is %d.\n", evt.what);
+ switch(evt.what)
+ {
+ case mouseDown:
+ {
+ short part;
+ WindowRef window;
+ long selectResult;
+ part = FindWindow(evt.where, &window);
+ switch ( part )
+ {
+ case inMenuBar:
+ selectResult = MenuSelect(evt.where);
+
+ HiliteMenu(0);
+ break;
+ }
+ }
+ break;
+
+ case kHighLevelEvent:
+ AEProcessAppleEvent (&evt);
+ break;
+
+ case updateEvt:
+ // We shouldn't be getting these regularly (since our window will be buffered), but we need to handle them correctly...
+ BeginUpdate((WindowRef)evt.message);
+ EndUpdate((WindowRef)evt.message);
+ break;
+
+ }
+ }
+}
+
+BOOL LLWindowMacOSX::getPosition(LLCoordScreen *position)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ position->mX = 0;
+ position->mY = 0;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ position->mX = window_rect.left;
+ position->mY = window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::getSize(LLCoordScreen *size)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ size->mX = mFullscreenWidth;
+ size->mY = mFullscreenHeight;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::getSize(LLCoordWindow *size)
+{
+ Rect window_rect;
+ OSStatus err = -1;
+
+ if(mFullscreen)
+ {
+ size->mX = mFullscreenWidth;
+ size->mY = mFullscreenHeight;
+ err = noErr;
+ }
+ else if(mWindow)
+ {
+ err = GetWindowBounds(mWindow, kWindowContentRgn, &window_rect);
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ }
+ else
+ {
+ llerrs << "LLWindowMacOSX::getPosition(): no window and not fullscreen!" << llendl;
+ }
+
+ return (err == noErr);
+}
+
+BOOL LLWindowMacOSX::setPosition(const LLCoordScreen position)
+{
+ if(mWindow)
+ {
+ MacMoveWindow(mWindow, position.mX, position.mY, false);
+ }
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::setSize(const LLCoordScreen size)
+{
+ if(mWindow)
+ {
+ SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+
+ return TRUE;
+}
+
+void LLWindowMacOSX::swapBuffers()
+{
+ aglSwapBuffers(mContext);
+}
+
+F32 LLWindowMacOSX::getGamma()
+{
+ F32 result = 1.8; // Default to something sane
+
+ CGGammaValue redMin;
+ CGGammaValue redMax;
+ CGGammaValue redGamma;
+ CGGammaValue greenMin;
+ CGGammaValue greenMax;
+ CGGammaValue greenGamma;
+ CGGammaValue blueMin;
+ CGGammaValue blueMax;
+ CGGammaValue blueGamma;
+
+ if(CGGetDisplayTransferByFormula(
+ mDisplay,
+ &redMin,
+ &redMax,
+ &redGamma,
+ &greenMin,
+ &greenMax,
+ &greenGamma,
+ &blueMin,
+ &blueMax,
+ &blueGamma) == noErr)
+ {
+ // So many choices...
+ // Let's just return the green channel gamma for now.
+ result = greenGamma;
+ }
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::restoreGamma()
+{
+ CGDisplayRestoreColorSyncSettings();
+ return true;
+}
+
+BOOL LLWindowMacOSX::setGamma(const F32 gamma)
+{
+ CGGammaValue redMin;
+ CGGammaValue redMax;
+ CGGammaValue redGamma;
+ CGGammaValue greenMin;
+ CGGammaValue greenMax;
+ CGGammaValue greenGamma;
+ CGGammaValue blueMin;
+ CGGammaValue blueMax;
+ CGGammaValue blueGamma;
+
+ // MBW -- XXX -- Should we allow this in windowed mode?
+
+ if(CGGetDisplayTransferByFormula(
+ mDisplay,
+ &redMin,
+ &redMax,
+ &redGamma,
+ &greenMin,
+ &greenMax,
+ &greenGamma,
+ &blueMin,
+ &blueMax,
+ &blueGamma) != noErr)
+ {
+ return false;
+ }
+
+ if(CGSetDisplayTransferByFormula(
+ mDisplay,
+ redMin,
+ redMax,
+ gamma,
+ greenMin,
+ greenMax,
+ gamma,
+ blueMin,
+ blueMax,
+ gamma) != noErr)
+ {
+ return false;
+ }
+
+
+ return true;
+}
+
+BOOL LLWindowMacOSX::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+
+// Constrains the mouse to the window.
+void LLWindowMacOSX::setMouseClipping( BOOL b )
+{
+ // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
+ mIsMouseClipping = b;
+
+ if(b)
+ {
+ // llinfos << "setMouseClipping(TRUE)" << llendl
+ }
+ else
+ {
+ // llinfos << "setMouseClipping(FALSE)" << llendl
+ }
+
+ adjustCursorDecouple();
+}
+
+BOOL LLWindowMacOSX::setCursorPosition(const LLCoordWindow position)
+{
+ BOOL result = FALSE;
+ LLCoordScreen screen_pos;
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ CGPoint newPosition;
+
+ // llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl
+
+ newPosition.x = screen_pos.mX;
+ newPosition.y = screen_pos.mY;
+
+ CGSetLocalEventsSuppressionInterval(0.0);
+ if(CGWarpMouseCursorPosition(newPosition) == noErr)
+ {
+ result = TRUE;
+ }
+
+ // Under certain circumstances, this will trigger us to decouple the cursor.
+ adjustCursorDecouple(true);
+
+ return result;
+}
+
+static void fixOrigin(void)
+{
+ GrafPtr port;
+ Rect portrect;
+
+ ::GetPort(&port);
+ ::GetPortBounds(port, &portrect);
+ if((portrect.left != 0) || (portrect.top != 0))
+ {
+ // Mozilla sometimes changes our port origin. Fuckers.
+ ::SetOrigin(0,0);
+ }
+}
+
+BOOL LLWindowMacOSX::getCursorPosition(LLCoordWindow *position)
+{
+ Point cursor_point;
+ LLCoordScreen screen_pos;
+ GrafPtr save;
+
+ if(mWindow == NULL)
+ return FALSE;
+
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ // gets the mouse location in local coordinates
+ ::GetMouse(&cursor_point);
+
+// lldebugs << "getCursorPosition(): cursor is at " << cursor_point.h << ", " << cursor_point.v << " port origin: " << portrect.left << ", " << portrect.top << llendl;
+
+ ::SetPort(save);
+
+ if(mCursorDecoupled)
+ {
+ // CGMouseDelta x, y;
+
+ // If the cursor's decoupled, we need to read the latest movement delta as well.
+ // CGGetLastMouseDelta( &x, &y );
+ // cursor_point.h += x;
+ // cursor_point.v += y;
+
+ // CGGetLastMouseDelta may behave strangely when the cursor's first captured.
+ // Stash in the event handler instead.
+ cursor_point.h += mCursorLastEventDeltaX;
+ cursor_point.v += mCursorLastEventDeltaY;
+ }
+
+ position->mX = cursor_point.h;
+ position->mY = cursor_point.v;
+
+ return TRUE;
+}
+
+void LLWindowMacOSX::adjustCursorDecouple(bool warpingMouse)
+{
+ if(mIsMouseClipping && mCursorHidden)
+ {
+ if(warpingMouse)
+ {
+ // The cursor should be decoupled. Make sure it is.
+ if(!mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
+ CGAssociateMouseAndMouseCursorPosition(false);
+ mCursorDecoupled = true;
+ mCursorIgnoreNextDelta = TRUE;
+ }
+ }
+ }
+ else
+ {
+ // The cursor should not be decoupled. Make sure it isn't.
+ if(mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
+ CGAssociateMouseAndMouseCursorPosition(true);
+ mCursorDecoupled = false;
+ }
+ }
+}
+
+F32 LLWindowMacOSX::getNativeAspectRatio()
+{
+ if (mFullscreen)
+ {
+ return (F32)mFullscreenWidth / (F32)mFullscreenHeight;
+ }
+ else
+ {
+ // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
+ // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
+
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+
+ return mOriginalAspectRatio;
+ }
+}
+
+F32 LLWindowMacOSX::getPixelAspectRatio()
+{
+ //OS X always enforces a 1:1 pixel aspect ratio, regardless of video mode
+ return 1.f;
+}
+
+//static SInt32 oldWindowLevel;
+
+// MBW -- XXX -- There's got to be a better way than this. Find it, please...
+
+void LLWindowMacOSX::beforeDialog()
+{
+ if(mFullscreen)
+ {
+
+#if CAPTURE_ALL_DISPLAYS
+ // Uncapture all displays (may want to do this for final build)
+ CGReleaseAllDisplays ();
+#else
+ // Uncapture only the main display (useful for debugging)
+ CGDisplayRelease (mDisplay);
+#endif
+ // kDocumentWindowClass
+ // kMovableModalWindowClass
+ // kAllWindowClasses
+
+ // GLint order = 0;
+ // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
+ aglSetDrawable(mContext, NULL);
+ // GetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), &oldWindowLevel);
+ // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), CGShieldingWindowLevel());
+
+ mHandsOffEvents = TRUE;
+
+ }
+}
+
+void LLWindowMacOSX::afterDialog()
+{
+ if(mFullscreen)
+ {
+ mHandsOffEvents = FALSE;
+
+ // SetWindowGroupLevel(GetWindowGroupOfClass(kAllWindowClasses), oldWindowLevel);
+ aglSetFullScreen(mContext, 0, 0, 0, 0);
+ // GLint order = 1;
+ // aglSetInteger(mContext, AGL_ORDER_CONTEXT_TO_FRONT, &order);
+
+#if CAPTURE_ALL_DISPLAYS
+ // Capture all displays (may want to do this for final build)
+ CGCaptureAllDisplays ();
+#else
+ // Capture only the main display (useful for debugging)
+ CGDisplayCapture (mDisplay);
+#endif
+ }
+}
+
+
+S32 LLWindowMacOSX::stat(const char* file_name, struct stat* stat_info)
+{
+ return ::stat( file_name, stat_info );
+}
+
+void LLWindowMacOSX::flashIcon(F32 seconds)
+{
+ // Don't do this if we're already started, since this would try to install the NMRec twice.
+ if(!mBounceTimer.getStarted())
+ {
+ OSErr err;
+
+ mBounceTime = seconds;
+ memset(&mBounceRec, sizeof(mBounceRec), 0);
+ mBounceRec.qType = nmType;
+ mBounceRec.nmMark = 1;
+ err = NMInstall(&mBounceRec);
+ if(err == noErr)
+ {
+ mBounceTimer.start();
+ }
+ else
+ {
+ // This is very not-fatal (only problem is the icon will not bounce), but we'd like to find out about it somehow...
+ llinfos << "NMInstall failed with error code " << err << llendl;
+ }
+ }
+}
+
+BOOL LLWindowMacOSX::isClipboardTextAvailable()
+{
+ OSStatus err;
+ ScrapRef scrap;
+ ScrapFlavorFlags flags;
+ BOOL result = false;
+
+ err = GetCurrentScrap(&scrap);
+
+ if(err == noErr)
+ {
+ err = GetScrapFlavorFlags(scrap, kScrapFlavorTypeUnicode, &flags);
+ }
+
+ if(err == noErr)
+ result = true;
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::pasteTextFromClipboard(LLWString &dst)
+{
+ OSStatus err;
+ ScrapRef scrap;
+ Size len;
+ BOOL result = false;
+
+ err = GetCurrentScrap(&scrap);
+
+ if(err == noErr)
+ {
+ err = GetScrapFlavorSize(scrap, kScrapFlavorTypeUnicode, &len);
+ }
+
+ if((err == noErr) && (len > 0))
+ {
+ int u16len = len / sizeof(U16);
+ U16 *temp = new U16[u16len + 1];
+ if (temp)
+ {
+ memset(temp, 0, (u16len + 1) * sizeof(temp[0]));
+ err = GetScrapFlavorData(scrap, kScrapFlavorTypeUnicode, &len, temp);
+ if (err == noErr)
+ {
+ // convert \r\n to \n and \r to \n in the incoming text.
+ U16 *s, *d;
+ for(s = d = temp; s[0] != '\0'; s++, d++)
+ {
+ if(s[0] == '\r')
+ {
+ if(s[1] == '\n')
+ {
+ // CRLF, a.k.a. DOS newline. Collapse to a single '\n'.
+ s++;
+ }
+
+ d[0] = '\n';
+ }
+ else
+ {
+ d[0] = s[0];
+ }
+ }
+
+ d[0] = '\0';
+
+ dst = utf16str_to_wstring(temp);
+
+ result = true;
+ }
+ delete[] temp;
+ }
+ }
+
+ return result;
+}
+
+BOOL LLWindowMacOSX::copyTextToClipboard(const LLWString &s)
+{
+ OSStatus err;
+ ScrapRef scrap;
+ //Size len;
+ //char *temp;
+ BOOL result = false;
+
+ if (!s.empty())
+ {
+ err = GetCurrentScrap(&scrap);
+ if (err == noErr)
+ err = ClearScrap(&scrap);
+
+ if (err == noErr)
+ {
+ llutf16string utf16str = wstring_to_utf16str(s);
+ size_t u16len = utf16str.length() * sizeof(U16);
+ err = PutScrapFlavor(scrap, kScrapFlavorTypeUnicode, kScrapFlavorMaskNone, u16len, utf16str.data());
+ if (err == noErr)
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+
+BOOL LLWindowMacOSX::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // MBW -- XXX -- Um... yeah. I'll get to this later.
+
+ return false;
+}
+
+
+// protected
+BOOL LLWindowMacOSX::resetDisplayResolution()
+{
+ // This is only called from elsewhere in this class, and it's not used by the Mac implementation.
+ return true;
+}
+
+
+LLWindow::LLWindowResolution* LLWindowMacOSX::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ CFArrayRef modes = CGDisplayAvailableModes(mDisplay);
+
+ if(modes != NULL)
+ {
+ CFIndex index, cnt;
+
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ mNumSupportedResolutions = 0;
+
+ // Examine each mode
+ cnt = CFArrayGetCount( modes );
+
+ for ( index = 0; (index < cnt) && (mNumSupportedResolutions < MAX_NUM_RESOLUTIONS); index++ )
+ {
+ // Pull the mode dictionary out of the CFArray
+ CFDictionaryRef mode = (CFDictionaryRef)CFArrayGetValueAtIndex( modes, index );
+ long width = getDictLong(mode, kCGDisplayWidth);
+ long height = getDictLong(mode, kCGDisplayHeight);
+ long bits = getDictLong(mode, kCGDisplayBitsPerPixel);
+
+ if(bits == BITS_PER_PIXEL && width >= 800 && height >= 600)
+ {
+ BOOL resolution_exists = FALSE;
+ for(S32 i = 0; i < mNumSupportedResolutions; i++)
+ {
+ if (mSupportedResolutions[i].mWidth == width &&
+ mSupportedResolutions[i].mHeight == height)
+ {
+ resolution_exists = TRUE;
+ }
+ }
+ if (!resolution_exists)
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = width;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = height;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ S32 client_height;
+ Rect client_rect;
+
+ if(mFullscreen)
+ {
+ // In the fullscreen case, the "window" is the entire screen.
+ client_rect.left = 0;
+ client_rect.top = 0;
+ client_rect.right = mFullscreenWidth;
+ client_rect.bottom = mFullscreenHeight;
+ }
+ else if (!mWindow ||
+ (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ S32 client_height;
+ Rect client_rect;
+
+ if(mFullscreen)
+ {
+ // In the fullscreen case, the "window" is the entire screen.
+ client_rect.left = 0;
+ client_rect.top = 0;
+ client_rect.right = mFullscreenWidth;
+ client_rect.bottom = mFullscreenHeight;
+ }
+ else if (!mWindow ||
+ (GetWindowBounds(mWindow, kWindowContentRgn, &client_rect) != noErr) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ if(mFullscreen)
+ {
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return TRUE;
+ }
+ else if(mWindow)
+ {
+ GrafPtr save;
+ Point mouse_point;
+
+ mouse_point.h = from.mX;
+ mouse_point.v = from.mY;
+
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ ::GlobalToLocal(&mouse_point);
+
+ to->mX = mouse_point.h;
+ to->mY = mouse_point.v;
+
+ ::SetPort(save);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ if(mFullscreen)
+ {
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return TRUE;
+ }
+ else if(mWindow)
+ {
+ GrafPtr save;
+ Point mouse_point;
+
+ mouse_point.h = from.mX;
+ mouse_point.v = from.mY;
+ ::GetPort(&save);
+ ::SetPort(GetWindowPort(mWindow));
+ fixOrigin();
+
+ LocalToGlobal(&mouse_point);
+
+ to->mX = mouse_point.h;
+ to->mY = mouse_point.v;
+
+ ::SetPort(save);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+BOOL LLWindowMacOSX::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+
+
+
+void LLWindowMacOSX::setupFailure(const char* text, const char* caption, U32 type)
+{
+ destroyContext();
+
+ OSMessageBox(text, caption, type);
+}
+
+pascal OSStatus LLWindowMacOSX::staticEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
+{
+ LLWindowMacOSX *self = (LLWindowMacOSX*)userData;
+
+ return(self->eventHandler(myHandler, event));
+}
+
+OSStatus LLWindowMacOSX::eventHandler (EventHandlerCallRef myHandler, EventRef event)
+{
+ OSStatus result = eventNotHandledErr;
+ UInt32 evtClass = GetEventClass (event);
+ UInt32 evtKind = GetEventKind (event);
+
+ // Always handle command events, even in hands-off mode.
+ if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
+ {
+ HICommand command;
+ GetEventParameter (event, kEventParamDirectObject, typeHICommand, NULL, sizeof(command), NULL, &command);
+
+ switch(command.commandID)
+ {
+ case kHICommandQuit:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ result = noErr;
+ break;
+
+ default:
+ // MBW -- XXX -- Should we handle other events here?
+ break;
+ }
+ }
+
+ if(mHandsOffEvents)
+ {
+ return(result);
+ }
+
+ switch (evtClass)
+ {
+ case kEventClassTextInput:
+ {
+ switch (evtKind)
+ {
+ case kEventTextInputUnicodeForKeyEvent:
+ {
+ UInt32 modifiers = 0;
+
+ // First, process the raw event.
+ {
+ EventRef rawEvent;
+
+ // Get the original event and extract the modifier keys, so we can ignore command-key events.
+ if (GetEventParameter(event, kEventParamTextInputSendKeyboardEvent, typeEventRef, NULL, sizeof(rawEvent), NULL, &rawEvent) == noErr)
+ {
+ // Grab the modifiers for later use in this function...
+ GetEventParameter (rawEvent, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
+
+ // and call this function recursively to handle the raw key event.
+ eventHandler (myHandler, rawEvent);
+ }
+ }
+
+ OSStatus err = noErr;
+ EventParamType actualType = typeUnicodeText;
+ UInt32 actualSize = 0;
+ size_t actualCount = 0;
+ U16 *buffer = NULL;
+
+ // Get the size of the unicode data
+ err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, 0, &actualSize, NULL);
+ if(err == noErr)
+ {
+ // allocate a buffer and get the actual data.
+ actualCount = actualSize / sizeof(U16);
+ buffer = new U16[actualCount];
+ err = GetEventParameter (event, kEventParamTextInputSendText, typeUnicodeText, &actualType, actualSize, &actualSize, buffer);
+ }
+
+ if(err == noErr)
+ {
+ if(modifiers & (cmdKey | controlKey))
+ {
+ // This was a menu key equivalent. Ignore it.
+ }
+ else
+ {
+ MASK mask = 0;
+ if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
+ if(modifiers & (cmdKey | controlKey)) { mask |= MASK_CONTROL; }
+ if(modifiers & optionKey) { mask |= MASK_ALT; }
+
+ llassert( actualType == typeUnicodeText );
+
+ // The result is a UTF16 buffer. Pass the characters in turn to handleUnicodeChar.
+
+ // Convert to UTF32 and go character-by-character.
+ llutf16string utf16(buffer, actualCount);
+ LLWString utf32 = utf16str_to_wstring(utf16);
+ LLWString::iterator iter;
+
+ for(iter = utf32.begin(); iter != utf32.end(); iter++)
+ {
+ mCallbacks->handleUnicodeChar(*iter, mask);
+ }
+ }
+ }
+
+ if(buffer != NULL)
+ {
+ delete[] buffer;
+ }
+
+ result = err;
+ }
+ break;
+ }
+ }
+ break;
+
+ case kEventClassKeyboard:
+ {
+ UInt32 keyCode = 0;
+ char charCode = 0;
+ UInt32 modifiers = 0;
+
+ // Some of these may fail for some event types. That's fine.
+ GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL, sizeof(UInt32), NULL, &keyCode);
+ GetEventParameter (event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(UInt32), NULL, &modifiers);
+
+ // printf("key event, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n", keyCode, charCode, (char)charCode, modifiers);
+ // fflush(stdout);
+
+ switch (evtKind)
+ {
+ case kEventRawKeyDown:
+ case kEventRawKeyRepeat:
+ if (gDebugWindowProc)
+ {
+ printf("key down, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n",
+ (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
+ fflush(stdout);
+ }
+ gKeyboard->handleKeyDown(keyCode, modifiers);
+ result = eventNotHandledErr;
+ break;
+
+ case kEventRawKeyUp:
+ if (gDebugWindowProc)
+ {
+ printf("key up, key code = 0x%08x, char code = 0x%02x (%c), modifiers = 0x%08x\n",
+ (unsigned int)keyCode, charCode, (char)charCode, (unsigned int)modifiers);
+ fflush(stdout);
+ }
+ gKeyboard->handleKeyUp(keyCode, modifiers);
+ result = eventNotHandledErr;
+ break;
+
+ case kEventRawKeyModifiersChanged:
+ // The keyboard input system wants key up/down events for modifier keys.
+ // Mac OS doesn't supply these directly, but can supply events when the collective modifier state changes.
+ // Use these events to generate up/down events for the modifiers.
+
+ if((modifiers & shiftKey) && !(mLastModifiers & shiftKey))
+ {
+ if (gDebugWindowProc) printf("Shift key down event\n");
+ gKeyboard->handleKeyDown(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & shiftKey) && (mLastModifiers & shiftKey))
+ {
+ if (gDebugWindowProc) printf("Shift key up event\n");
+ gKeyboard->handleKeyUp(0x38, (modifiers & 0x00FFFFFF) | ((0x38 << 24) & 0xFF000000));
+ }
+
+ if((modifiers & alphaLock) && !(mLastModifiers & alphaLock))
+ {
+ if (gDebugWindowProc) printf("Caps lock down event\n");
+ gKeyboard->handleKeyDown(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & alphaLock) && (mLastModifiers & alphaLock))
+ {
+ if (gDebugWindowProc) printf("Caps lock up event\n");
+ gKeyboard->handleKeyUp(0x39, (modifiers & 0x00FFFFFF) | ((0x39 << 24) & 0xFF000000));
+ }
+
+ if((modifiers & controlKey) && !(mLastModifiers & controlKey))
+ {
+ if (gDebugWindowProc) printf("Control key down event\n");
+ gKeyboard->handleKeyDown(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & controlKey) && (mLastModifiers & controlKey))
+ {
+ if (gDebugWindowProc) printf("Control key up event\n");
+ gKeyboard->handleKeyUp(0x3b, (modifiers & 0x00FFFFFF) | ((0x3b << 24) & 0xFF000000));
+ }
+
+ if((modifiers & optionKey) && !(mLastModifiers & optionKey))
+ {
+ if (gDebugWindowProc) printf("Option key down event\n");
+ gKeyboard->handleKeyDown(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
+ }
+ else if(!(modifiers & optionKey) && (mLastModifiers & optionKey))
+ {
+ if (gDebugWindowProc) printf("Option key up event\n");
+ gKeyboard->handleKeyUp(0x3a, (modifiers & 0x00FFFFFF) | ((0x3a << 24) & 0xFF000000));
+ }
+
+ // When the state of the 'Fn' key (the one that changes some of the mappings on a powerbook/macbook keyboard
+ // to an embedded keypad) changes, it may subsequently cause a key up event to be lost, which may lead to
+ // a movement key getting "stuck" down. This is bad.
+ // This is an OS bug -- even the GetKeys() API doesn't tell you the key has been released.
+ // This workaround causes all held-down keys to be reset whenever the state of the Fn key changes. This isn't
+ // exactly what we want, but it does avoid the case where you get stuck running forward.
+ if((modifiers & kEventKeyModifierFnMask) != (mLastModifiers & kEventKeyModifierFnMask))
+ {
+ if (gDebugWindowProc) printf("Fn key state change event\n");
+ gKeyboard->resetKeys();
+ }
+
+ if (gDebugWindowProc) fflush(stdout);
+
+ mLastModifiers = modifiers;
+ result = eventNotHandledErr;
+ break;
+ }
+ }
+ break;
+
+ case kEventClassMouse:
+ {
+ result = CallNextEventHandler(myHandler, event);
+ if (eventNotHandledErr == result)
+ { // only handle events not already handled (prevents wierd resize interaction)
+ EventMouseButton button = kEventMouseButtonPrimary;
+ HIPoint location = {0.0f, 0.0f};
+ UInt32 modifiers = 0;
+ UInt32 clickCount = 1;
+ long wheelDelta = 0;
+ LLCoordScreen inCoords;
+ LLCoordGL outCoords;
+ MASK mask = 0;
+
+ GetEventParameter(event, kEventParamMouseButton, typeMouseButton, NULL, sizeof(button), NULL, &button);
+ GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, NULL, sizeof(location), NULL, &location);
+ GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(modifiers), NULL, &modifiers);
+ GetEventParameter(event, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(wheelDelta), NULL, &wheelDelta);
+ GetEventParameter(event, kEventParamClickCount, typeUInt32, NULL, sizeof(clickCount), NULL, &clickCount);
+
+ inCoords.mX = llround(location.x);
+ inCoords.mY = llround(location.y);
+
+ if(modifiers & shiftKey) { mask |= MASK_SHIFT; }
+ if(modifiers & controlKey) { mask |= MASK_CONTROL; }
+ if(modifiers & optionKey) { mask |= MASK_ALT; }
+
+ if(mCursorDecoupled)
+ {
+ CGMouseDelta x, y;
+
+ // If the cursor's decoupled, we need to read the latest movement delta as well.
+ CGGetLastMouseDelta( &x, &y );
+ mCursorLastEventDeltaX = x;
+ mCursorLastEventDeltaY = y;
+
+ if(mCursorIgnoreNextDelta)
+ {
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ }
+ }
+ else
+ {
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ }
+
+ inCoords.mX += mCursorLastEventDeltaX;
+ inCoords.mY += mCursorLastEventDeltaY;
+
+ convertCoords(inCoords, &outCoords);
+
+ // printf("coords in: %d, %d; coords out: %d, %d\n", inCoords.mX, inCoords.mY, outCoords.mX, outCoords.mY);
+ // fflush(stdout);
+
+
+ switch (evtKind)
+ {
+ case kEventMouseDown:
+ switch(button)
+ {
+ case kEventMouseButtonPrimary:
+ if(modifiers & cmdKey)
+ {
+ // Simulate a right click
+ mSimulatedRightClick = true;
+ mCallbacks->handleRightMouseDown(this, outCoords, mask);
+ }
+ else if(clickCount == 2)
+ {
+ // Windows double-click events replace the second mousedown event in a double-click.
+ mCallbacks->handleDoubleClick(this, outCoords, mask);
+ }
+ else
+ {
+ mCallbacks->handleMouseDown(this, outCoords, mask);
+ }
+ break;
+ case kEventMouseButtonSecondary:
+ mCallbacks->handleRightMouseDown(this, outCoords, mask);
+ break;
+ }
+ result = noErr;
+ break;
+ case kEventMouseUp:
+
+ switch(button)
+ {
+ case kEventMouseButtonPrimary:
+ if(mSimulatedRightClick)
+ {
+ // End of simulated right click
+ mSimulatedRightClick = false;
+ mCallbacks->handleRightMouseUp(this, outCoords, mask);
+ }
+ else
+ {
+ mCallbacks->handleMouseUp(this, outCoords, mask);
+ }
+ break;
+ case kEventMouseButtonSecondary:
+ mCallbacks->handleRightMouseUp(this, outCoords, mask);
+ break;
+ }
+ result = noErr;
+ break;
+
+ case kEventMouseWheelMoved:
+ {
+ static S32 z_delta = 0;
+
+ z_delta += wheelDelta;
+
+ if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
+ {
+ mCallbacks->handleScrollWheel(this, -z_delta / WHEEL_DELTA);
+ z_delta = 0;
+ }
+ }
+ result = noErr;
+ break;
+
+ case kEventMouseDragged:
+ case kEventMouseMoved:
+ mCallbacks->handleMouseMove(this, outCoords, mask);
+ result = noErr;
+ break;
+
+ }
+ }
+ }
+ break;
+
+ case kEventClassWindow:
+ switch(evtKind)
+ {
+ case kEventWindowBoundsChanging:
+ {
+ Rect currentBounds;
+ Rect previousBounds;
+
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &currentBounds);
+ GetEventParameter(event, kEventParamPreviousBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &previousBounds);
+
+ // This is where we would constrain move/resize to a particular screen
+ if(0)
+ {
+ SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &currentBounds);
+ }
+ }
+ break;
+
+ case kEventWindowBoundsChanged:
+ {
+ Rect newBounds;
+
+ GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(Rect), NULL, &newBounds);
+ aglUpdateContext(mContext);
+ mCallbacks->handleResize(this, newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
+
+
+ }
+ break;
+
+ case kEventWindowClose:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ result = noErr;
+ break;
+
+ case kEventWindowHidden:
+ // llinfos << "LLWindowMacOSX: Deactivating on hide" << llendl;
+ mMinimized = TRUE;
+ mCallbacks->handleActivate(this, false);
+ // result = noErr;
+ break;
+
+ case kEventWindowShown:
+ // llinfos << "LLWindowMacOSX: Activating on show" << llendl;
+ mMinimized = FALSE;
+ mCallbacks->handleActivate(this, true);
+ // result = noErr;
+ break;
+
+ case kEventWindowCollapsed:
+ // llinfos << "LLWindowMacOSX: Deactivating on collapse" << llendl;
+ mMinimized = TRUE;
+ mCallbacks->handleActivate(this, false);
+ // result = noErr;
+ break;
+
+ case kEventWindowExpanded:
+ // llinfos << "LLWindowMacOSX: Activating on expand" << llendl;
+ mMinimized = FALSE;
+ mCallbacks->handleActivate(this, true);
+ // result = noErr;
+ break;
+
+ case kEventWindowGetClickActivation:
+ // BringToFront(mWindow);
+ // result = noErr;
+ break;
+ }
+ break;
+ }
+ return result;
+}
+
+const char* cursorIDToName(int id)
+{
+ switch (id)
+ {
+ case UI_CURSOR_ARROW: return "UI_CURSOR_ARROW";
+ case UI_CURSOR_WAIT: return "UI_CURSOR_WAIT";
+ case UI_CURSOR_HAND: return "UI_CURSOR_HAND";
+ case UI_CURSOR_IBEAM: return "UI_CURSOR_IBEAM";
+ case UI_CURSOR_CROSS: return "UI_CURSOR_CROSS";
+ case UI_CURSOR_SIZENWSE: return "UI_CURSOR_SIZENWSE";
+ case UI_CURSOR_SIZENESW: return "UI_CURSOR_SIZENESW";
+ case UI_CURSOR_SIZEWE: return "UI_CURSOR_SIZEWE";
+ case UI_CURSOR_SIZENS: return "UI_CURSOR_SIZENS";
+ case UI_CURSOR_NO: return "UI_CURSOR_NO";
+ case UI_CURSOR_WORKING: return "UI_CURSOR_WORKING";
+ case UI_CURSOR_TOOLGRAB: return "UI_CURSOR_TOOLGRAB";
+ case UI_CURSOR_TOOLLAND: return "UI_CURSOR_TOOLLAND";
+ case UI_CURSOR_TOOLFOCUS: return "UI_CURSOR_TOOLFOCUS";
+ case UI_CURSOR_TOOLCREATE: return "UI_CURSOR_TOOLCREATE";
+ case UI_CURSOR_ARROWDRAG: return "UI_CURSOR_ARROWDRAG";
+ case UI_CURSOR_ARROWCOPY: return "UI_CURSOR_ARROWCOPY";
+ case UI_CURSOR_ARROWDRAGMULTI: return "UI_CURSOR_ARROWDRAGMULTI";
+ case UI_CURSOR_ARROWCOPYMULTI: return "UI_CURSOR_ARROWCOPYMULTI";
+ case UI_CURSOR_NOLOCKED: return "UI_CURSOR_NOLOCKED";
+ case UI_CURSOR_ARROWLOCKED: return "UI_CURSOR_ARROWLOCKED";
+ case UI_CURSOR_GRABLOCKED: return "UI_CURSOR_GRABLOCKED";
+ case UI_CURSOR_TOOLTRANSLATE: return "UI_CURSOR_TOOLTRANSLATE";
+ case UI_CURSOR_TOOLROTATE: return "UI_CURSOR_TOOLROTATE";
+ case UI_CURSOR_TOOLSCALE: return "UI_CURSOR_TOOLSCALE";
+ case UI_CURSOR_TOOLCAMERA: return "UI_CURSOR_TOOLCAMERA";
+ case UI_CURSOR_TOOLPAN: return "UI_CURSOR_TOOLPAN";
+ case UI_CURSOR_TOOLZOOMIN: return "UI_CURSOR_TOOLZOOMIN";
+ case UI_CURSOR_TOOLPICKOBJECT3: return "UI_CURSOR_TOOLPICKOBJECT3";
+ case UI_CURSOR_TOOLSIT: return "UI_CURSOR_TOOLSIT";
+ case UI_CURSOR_TOOLBUY: return "UI_CURSOR_TOOLBUY";
+ case UI_CURSOR_TOOLPAY: return "UI_CURSOR_TOOLPAY";
+ case UI_CURSOR_TOOLOPEN: return "UI_CURSOR_TOOLOPEN";
+ case UI_CURSOR_PIPETTE: return "UI_CURSOR_PIPETTE";
+ }
+
+ llerrs << "cursorIDToName: unknown cursor id" << id << llendl;
+
+ return "UI_CURSOR_ARROW";
+}
+
+static CursorRef gCursors[UI_CURSOR_COUNT];
+
+
+static void initPixmapCursor(int cursorid, int hotspotX, int hotspotY)
+{
+ // cursors are in <Application Bundle>/Contents/Resources/cursors_mac/UI_CURSOR_FOO.tif
+ std::string fullpath = gDirUtilp->getAppRODataDir();
+ fullpath += gDirUtilp->getDirDelimiter();
+ fullpath += "cursors_mac";
+ fullpath += gDirUtilp->getDirDelimiter();
+ fullpath += cursorIDToName(cursorid);
+ fullpath += ".tif";
+
+ gCursors[cursorid] = createImageCursor(fullpath.c_str(), hotspotX, hotspotY);
+}
+
+void LLWindowMacOSX::setCursor(ECursorType cursor)
+{
+ OSStatus result = noErr;
+
+ if (cursor == UI_CURSOR_ARROW
+ && mBusyCount > 0)
+ {
+ cursor = UI_CURSOR_WORKING;
+ }
+
+ if(mCurrentCursor == cursor)
+ return;
+
+ // RN: replace multi-drag cursors with single versions
+ if (cursor == UI_CURSOR_ARROWDRAGMULTI)
+ {
+ cursor = UI_CURSOR_ARROWDRAG;
+ }
+ else if (cursor == UI_CURSOR_ARROWCOPYMULTI)
+ {
+ cursor = UI_CURSOR_ARROWCOPY;
+ }
+
+ switch(cursor)
+ {
+ default:
+ case UI_CURSOR_ARROW:
+ InitCursor();
+ if(mCursorHidden)
+ {
+ // Since InitCursor resets the hide level, correct for it here.
+ ::HideCursor();
+ }
+ break;
+
+ // MBW -- XXX -- Some of the standard Windows cursors have no standard Mac equivalents.
+ // Find out what they look like and replicate them.
+
+ // These are essentially correct
+ case UI_CURSOR_WAIT: SetThemeCursor(kThemeWatchCursor); break;
+ case UI_CURSOR_IBEAM: SetThemeCursor(kThemeIBeamCursor); break;
+ case UI_CURSOR_CROSS: SetThemeCursor(kThemeCrossCursor); break;
+ case UI_CURSOR_HAND: SetThemeCursor(kThemePointingHandCursor); break;
+ // case UI_CURSOR_NO: SetThemeCursor(kThemeNotAllowedCursor); break;
+ case UI_CURSOR_ARROWCOPY: SetThemeCursor(kThemeCopyArrowCursor); break;
+
+ // Double-check these
+ case UI_CURSOR_NO:
+ case UI_CURSOR_SIZEWE:
+ case UI_CURSOR_SIZENS:
+ case UI_CURSOR_SIZENWSE:
+ case UI_CURSOR_SIZENESW:
+ case UI_CURSOR_WORKING:
+ case UI_CURSOR_TOOLGRAB:
+ case UI_CURSOR_TOOLLAND:
+ case UI_CURSOR_TOOLFOCUS:
+ case UI_CURSOR_TOOLCREATE:
+ case UI_CURSOR_ARROWDRAG:
+ case UI_CURSOR_NOLOCKED:
+ case UI_CURSOR_ARROWLOCKED:
+ case UI_CURSOR_GRABLOCKED:
+ case UI_CURSOR_TOOLTRANSLATE:
+ case UI_CURSOR_TOOLROTATE:
+ case UI_CURSOR_TOOLSCALE:
+ case UI_CURSOR_TOOLCAMERA:
+ case UI_CURSOR_TOOLPAN:
+ case UI_CURSOR_TOOLZOOMIN:
+ case UI_CURSOR_TOOLPICKOBJECT3:
+ case UI_CURSOR_TOOLSIT:
+ case UI_CURSOR_TOOLBUY:
+ case UI_CURSOR_TOOLPAY:
+ case UI_CURSOR_TOOLOPEN:
+ result = setImageCursor(gCursors[cursor]);
+ break;
+
+ }
+
+ if(result != noErr)
+ {
+ InitCursor();
+ }
+
+ mCurrentCursor = cursor;
+}
+
+ECursorType LLWindowMacOSX::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowMacOSX::initCursors()
+{
+ initPixmapCursor(UI_CURSOR_NO, 8, 8);
+ initPixmapCursor(UI_CURSOR_WORKING, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLGRAB, 2, 14);
+ initPixmapCursor(UI_CURSOR_TOOLLAND, 13, 8);
+ initPixmapCursor(UI_CURSOR_TOOLFOCUS, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLCREATE, 7, 7);
+ initPixmapCursor(UI_CURSOR_ARROWDRAG, 1, 1);
+ initPixmapCursor(UI_CURSOR_ARROWCOPY, 1, 1);
+ initPixmapCursor(UI_CURSOR_NOLOCKED, 8, 8);
+ initPixmapCursor(UI_CURSOR_ARROWLOCKED, 1, 1);
+ initPixmapCursor(UI_CURSOR_GRABLOCKED, 2, 14);
+ initPixmapCursor(UI_CURSOR_TOOLTRANSLATE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLROTATE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLSCALE, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLCAMERA, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLPAN, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLZOOMIN, 7, 6);
+ initPixmapCursor(UI_CURSOR_TOOLPICKOBJECT3, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLSIT, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLBUY, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLPAY, 1, 1);
+ initPixmapCursor(UI_CURSOR_TOOLOPEN, 1, 1);
+
+ initPixmapCursor(UI_CURSOR_SIZENWSE, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZENESW, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZEWE, 10, 10);
+ initPixmapCursor(UI_CURSOR_SIZENS, 10, 10);
+
+}
+
+void LLWindowMacOSX::captureMouse()
+{
+ // By registering a global CarbonEvent handler for mouse move events, we ensure that
+ // mouse events are always processed. Thus, capture and release are unnecessary.
+}
+
+void LLWindowMacOSX::releaseMouse()
+{
+ // By registering a global CarbonEvent handler for mouse move events, we ensure that
+ // mouse events are always processed. Thus, capture and release are unnecessary.
+}
+
+void LLWindowMacOSX::hideCursor()
+{
+ if(!mCursorHidden)
+ {
+ // llinfos << "hideCursor: hiding" << llendl;
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+ ::HideCursor();
+ }
+ else
+ {
+ // llinfos << "hideCursor: already hidden" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowMacOSX::showCursor()
+{
+ if(mCursorHidden)
+ {
+ // llinfos << "showCursor: showing" << llendl;
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+ ::ShowCursor();
+ }
+ else
+ {
+ // llinfos << "showCursor: already visible" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowMacOSX::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowMacOSX::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+
+
+//
+// LLSplashScreenMacOSX
+//
+LLSplashScreenMacOSX::LLSplashScreenMacOSX()
+{
+ mWindow = NULL;
+}
+
+LLSplashScreenMacOSX::~LLSplashScreenMacOSX()
+{
+}
+
+void LLSplashScreenMacOSX::showImpl()
+{
+ // This code _could_ be used to display a spash screen...
+#if 0
+ IBNibRef nib = NULL;
+ OSStatus err;
+
+ err = CreateNibReference(CFSTR("SecondLife"), &nib);
+
+ if(err == noErr)
+ {
+ CreateWindowFromNib(nib, CFSTR("Splash Screen"), &mWindow);
+
+ DisposeNibReference(nib);
+ }
+
+ if(mWindow != NULL)
+ {
+ ShowWindow(mWindow);
+ }
+#endif
+}
+
+void LLSplashScreenMacOSX::updateImpl(const char* mesg)
+{
+ if(mWindow != NULL)
+ {
+ CFStringRef string = NULL;
+
+ if(mesg != NULL)
+ {
+ string = CFStringCreateWithCString(NULL, mesg, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ string = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ if(string != NULL)
+ {
+ ControlRef progressText = NULL;
+ ControlID id;
+ OSStatus err;
+
+ id.signature = 'what';
+ id.id = 0;
+
+ err = GetControlByID(mWindow, &id, &progressText);
+ if(err == noErr)
+ {
+ err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&string);
+ Draw1Control(progressText);
+ }
+
+ CFRelease(string);
+ }
+ }
+}
+
+
+void LLSplashScreenMacOSX::hideImpl()
+{
+ if(mWindow != NULL)
+ {
+ DisposeWindow(mWindow);
+ mWindow = NULL;
+ }
+}
+
+
+
+S32 OSMessageBoxMacOSX(const char* text, const char* caption, U32 type)
+{
+ S32 result = OSBTN_CANCEL;
+ SInt16 retval_mac = 1;
+ AlertStdCFStringAlertParamRec params;
+ CFStringRef errorString = NULL;
+ CFStringRef explanationString = NULL;
+ DialogRef alert = NULL;
+ AlertType alertType = kAlertCautionAlert;
+ OSStatus err;
+
+ if(text != NULL)
+ {
+ explanationString = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ explanationString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ if(caption != NULL)
+ {
+ errorString = CFStringCreateWithCString(NULL, caption, kCFStringEncodingUTF8);
+ }
+ else
+ {
+ errorString = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
+ }
+
+ params.version = kStdCFStringAlertVersionOne;
+ params.movable = false;
+ params.helpButton = false;
+ params.defaultText = (CFStringRef)kAlertDefaultOKText;
+ params.cancelText = 0;
+ params.otherText = 0;
+ params.defaultButton = 1;
+ params.cancelButton = 0;
+ params.position = kWindowDefaultPosition;
+ params.flags = 0;
+
+ switch(type)
+ {
+ case OSMB_OK:
+ default:
+ break;
+ case OSMB_OKCANCEL:
+ params.cancelText = (CFStringRef)kAlertDefaultCancelText;
+ params.cancelButton = 2;
+ break;
+ case OSMB_YESNO:
+ alertType = kAlertNoteAlert;
+ params.defaultText = CFSTR("Yes");
+ params.cancelText = CFSTR("No");
+ params.cancelButton = 2;
+ break;
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ err = CreateStandardAlert(
+ alertType,
+ errorString,
+ explanationString,
+ &params,
+ &alert);
+
+ if(err == noErr)
+ {
+ err = RunStandardAlert(
+ alert,
+ NULL,
+ &retval_mac);
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ switch(type)
+ {
+ case OSMB_OK:
+ case OSMB_OKCANCEL:
+ default:
+ if(retval_mac == 1)
+ result = OSBTN_OK;
+ else
+ result = OSBTN_CANCEL;
+ break;
+ case OSMB_YESNO:
+ if(retval_mac == 1)
+ result = OSBTN_YES;
+ else
+ result = OSBTN_NO;
+ break;
+ }
+
+ if(errorString != NULL)
+ {
+ CFRelease(errorString);
+ }
+
+ if(explanationString != NULL)
+ {
+ CFRelease(explanationString);
+ }
+
+ return result;
+}
+
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
+{
+ bool found = false;
+ S32 i;
+ for (i = 0; i < gURLProtocolWhitelistCount; i++)
+ {
+ S32 len = strlen(gURLProtocolWhitelist[i]);
+ if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len)
+ && escaped_url[len] == ':')
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl;
+ return;
+ }
+
+ OSStatus result = noErr;
+ CFURLRef urlRef = NULL;
+
+ llinfos << "Opening URL " << escaped_url << llendl;
+
+ CFStringRef stringRef = CFStringCreateWithCString(NULL, escaped_url, kCFStringEncodingUTF8);
+ if (stringRef)
+ {
+ // This will succeed if the string is a full URL, including the http://
+ // Note that URLs specified this way need to be properly percent-escaped.
+ urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
+
+ // Don't use CRURLCreateWithFileSystemPath -- only want valid URLs
+
+ CFRelease(stringRef);
+ }
+
+ if (urlRef)
+ {
+ result = LSOpenCFURLRef(urlRef, NULL);
+
+ if (result != noErr)
+ {
+ llinfos << "Error " << result << " on open." << llendl;
+ }
+
+ CFRelease(urlRef);
+ }
+ else
+ {
+ llinfos << "Error: couldn't create URL." << llendl;
+ }
+}
+
+void shell_open( const char* file_path )
+{
+ OSStatus result = noErr;
+
+ llinfos << "Opening " << file_path << llendl;
+ CFURLRef urlRef = NULL;
+
+ CFStringRef stringRef = CFStringCreateWithCString(NULL, file_path, kCFStringEncodingUTF8);
+ if (stringRef)
+ {
+ // This will succeed if the string is a full URL, including the http://
+ // Note that URLs specified this way need to be properly percent-escaped.
+ urlRef = CFURLCreateWithString(NULL, stringRef, NULL);
+
+ if(urlRef == NULL)
+ {
+ // This will succeed if the string is a full or partial posix path.
+ // This will work even if the path contains characters that would need to be percent-escaped
+ // in the URL (such as spaces).
+ urlRef = CFURLCreateWithFileSystemPath(NULL, stringRef, kCFURLPOSIXPathStyle, false);
+ }
+
+ CFRelease(stringRef);
+ }
+
+ if (urlRef)
+ {
+ result = LSOpenCFURLRef(urlRef, NULL);
+
+ if (result != noErr)
+ {
+ llinfos << "Error " << result << " on open." << llendl;
+ }
+ CFRelease(urlRef);
+ }
+ else
+ {
+ llinfos << "Error: couldn't create URL." << llendl;
+ }
+}
+
+BOOL LLWindowMacOSX::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ BOOL retval = FALSE;
+ OSErr error = noErr;
+ NColorPickerInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.theColor.color.rgb.red = (UInt16)(*r * 65535.f);
+ info.theColor.color.rgb.green = (UInt16)(*g * 65535.f);
+ info.theColor.color.rgb.blue = (UInt16)(*b * 65535.f);
+ info.placeWhere = kCenterOnMainScreen;
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ error = NPickColor(&info);
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ if (error == noErr)
+ {
+ retval = info.newColorChosen;
+ if (info.newColorChosen)
+ {
+ *r = ((float) info.theColor.color.rgb.red) / 65535.0;
+ *g = ((float) info.theColor.color.rgb.green) / 65535.0;
+ *b = ((float) info.theColor.color.rgb.blue) / 65535.0;
+ }
+ }
+ return (retval);
+}
+
+static WindowRef dummywindowref = NULL;
+
+void *LLWindowMacOSX::getPlatformWindow()
+{
+ if(mWindow != NULL)
+ return (void*)mWindow;
+
+ // If we're in fullscreen mode, there's no window pointer available.
+ // Since Mozilla needs one to function, create a dummy window here.
+ // Note that we will never destroy it, but since only one will be created per run of the application, that's okay.
+
+ if(dummywindowref == NULL)
+ {
+ Rect window_rect = {100, 100, 200, 200};
+
+ dummywindowref = NewCWindow(
+ NULL,
+ &window_rect,
+ "\p",
+ false, // Create the window invisible.
+ zoomDocProc, // Window with a grow box and a zoom box
+ kLastWindowOfClass, // create it behind other windows
+ false, // no close box
+ 0);
+ }
+
+ return (void*)dummywindowref;
+}
+
+void LLWindowMacOSX::stopDockTileBounce()
+{
+ NMRemove(&mBounceRec);
+ mBounceTimer.stop();
+}
+
+// get a double value from a dictionary
+static double getDictDouble (CFDictionaryRef refDict, CFStringRef key)
+{
+ double double_value;
+ CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
+ if (!number_value) // if can't get a number for the dictionary
+ return -1; // fail
+ if (!CFNumberGetValue(number_value, kCFNumberDoubleType, &double_value)) // or if cant convert it
+ return -1; // fail
+ return double_value; // otherwise return the long value
+}
+
+// get a long value from a dictionary
+static long getDictLong (CFDictionaryRef refDict, CFStringRef key)
+{
+ long int_value;
+ CFNumberRef number_value = (CFNumberRef) CFDictionaryGetValue(refDict, key);
+ if (!number_value) // if can't get a number for the dictionary
+ return -1; // fail
+ if (!CFNumberGetValue(number_value, kCFNumberLongType, &int_value)) // or if cant convert it
+ return -1; // fail
+ return int_value; // otherwise return the long value
+}
+
+#endif // LL_DARWIN
diff --git a/indra/llwindow/llwindowmacosx.h b/indra/llwindow/llwindowmacosx.h
new file mode 100644
index 0000000000..1927f9bf31
--- /dev/null
+++ b/indra/llwindow/llwindowmacosx.h
@@ -0,0 +1,189 @@
+/**
+ * @file llwindowmacosx.h
+ * @brief Mac implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWMACOSX_H
+#define LL_LLWINDOWMACOSX_H
+
+#include "llwindow.h"
+
+#include <Carbon/Carbon.h>
+#include <AGL/agl.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+
+class LLWindowMacOSX : public LLWindow
+{
+public:
+ /*virtual*/ void show();
+ /*virtual*/ void hide();
+ /*virtual*/ void close();
+ /*virtual*/ BOOL getVisible();
+ /*virtual*/ BOOL getMinimized();
+ /*virtual*/ BOOL getMaximized();
+ /*virtual*/ BOOL maximize();
+ /*virtual*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*virtual*/ void captureMouse();
+ /*virtual*/ void releaseMouse();
+ /*virtual*/ void setMouseClipping( BOOL b );
+ /*virtual*/ BOOL isClipboardTextAvailable();
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst);
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString & src);
+ /*virtual*/ void flashIcon(F32 seconds);
+ /*virtual*/ F32 getGamma();
+ /*virtual*/ BOOL setGamma(const F32 gamma); // Set the gamma
+ /*virtual*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+
+ // handy coordinate space conversion routines
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to);
+
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
+ /*virtual*/ F32 getNativeAspectRatio();
+ /*virtual*/ F32 getPixelAspectRatio();
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
+
+ /*virtual*/ void beforeDialog();
+ /*virtual*/ void afterDialog();
+
+ /*virtual*/ BOOL dialog_color_picker(F32 *r, F32 *g, F32 *b);
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront() {};
+
+protected:
+ LLWindowMacOSX(
+ char *title, char *name, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowMacOSX();
+
+ void initCursors();
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ // Restore the display resolution to its value before we ran the app.
+ BOOL resetDisplayResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ // create or re-create the GL context/window. Called from the constructor and switchContext().
+ BOOL createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync);
+ void destroyContext();
+ void setupFailure(const char* text, const char* caption, U32 type);
+ static pascal OSStatus staticEventHandler (EventHandlerCallRef myHandler, EventRef event, void* userData);
+ OSStatus eventHandler (EventHandlerCallRef myHandler, EventRef event);
+ void adjustCursorDecouple(bool warpingMouse = false);
+ void fixWindowSize(void);
+ void stopDockTileBounce();
+
+
+ //
+ // Platform specific variables
+ //
+ WindowRef mWindow;
+ AGLContext mContext;
+ AGLPixelFormat mPixelFormat;
+ CGDirectDisplayID mDisplay;
+ CFDictionaryRef mOldDisplayMode;
+ EventLoopTimerRef mTimer;
+ EventHandlerUPP mEventHandlerUPP;
+ EventHandlerRef mGlobalHandlerRef;
+ EventHandlerRef mWindowHandlerRef;
+ Rect mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
+ Str255 mWindowTitle;
+ double mOriginalAspectRatio;
+ BOOL mSimulatedRightClick;
+ UInt32 mLastModifiers;
+ BOOL mHandsOffEvents; // When true, temporarially disable CarbonEvent processing.
+ // Used to allow event processing when putting up dialogs in fullscreen mode.
+ BOOL mCursorDecoupled;
+ S32 mCursorLastEventDeltaX;
+ S32 mCursorLastEventDeltaY;
+ BOOL mCursorIgnoreNextDelta;
+ BOOL mNeedsResize; // Constructor figured out the window is too big, it needs a resize.
+ LLCoordScreen mNeedsResizeSize;
+ F32 mOverrideAspectRatio;
+ BOOL mMinimized;
+
+ F32 mBounceTime;
+ NMRec mBounceRec;
+ LLTimer mBounceTimer;
+
+ friend class LLWindowManager;
+};
+
+
+class LLSplashScreenMacOSX : public LLSplashScreen
+{
+public:
+ LLSplashScreenMacOSX();
+ virtual ~LLSplashScreenMacOSX();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+
+private:
+ WindowRef mWindow;
+};
+
+S32 OSMessageBoxMacOSX(const char* text, const char* caption, U32 type);
+
+void load_url_external(const char* url);
+void shell_open( const char* file_path );
+
+#endif //LL_LLWINDOWMACOSX_H
diff --git a/indra/llwindow/llwindowmesaheadless.cpp b/indra/llwindow/llwindowmesaheadless.cpp
new file mode 100644
index 0000000000..c924bb1efa
--- /dev/null
+++ b/indra/llwindow/llwindowmesaheadless.cpp
@@ -0,0 +1,64 @@
+/**
+ * @file llwindowmesaheadless.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_MESA_HEADLESS
+
+#include "linden_common.h"
+#include "indra_constants.h"
+
+#include "llwindowmesaheadless.h"
+#include "llgl.h"
+#include "llglheaders.h"
+
+#define MESA_CHANNEL_TYPE GL_UNSIGNED_SHORT
+#define MESA_CHANNEL_SIZE 2
+
+U16 *gMesaBuffer = NULL;
+
+//
+// LLWindowMesaHeadless
+//
+LLWindowMesaHeadless::LLWindowMesaHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ if (use_gl)
+ {
+ llinfos << "MESA Init" << llendl;
+ mMesaContext = OSMesaCreateContextExt( GL_RGBA, 32, 0, 0, NULL );
+
+ /* Allocate the image buffer */
+ mMesaBuffer = new unsigned char [width * height * 4 * MESA_CHANNEL_SIZE];
+ llassert(mMesaBuffer);
+
+ gMesaBuffer = (U16*)mMesaBuffer;
+
+ /* Bind the buffer to the context and make it current */
+ if (!OSMesaMakeCurrent( mMesaContext, mMesaBuffer, MESA_CHANNEL_TYPE, width, height ))
+ {
+ llerrs << "MESA: OSMesaMakeCurrent failed!" << llendl;
+ }
+
+ llverify(gGLManager.initGL());
+ }
+}
+
+
+LLWindowMesaHeadless::~LLWindowMesaHeadless()
+{
+ delete mMesaBuffer;
+ OSMesaDestroyContext( mMesaContext );
+}
+
+void LLWindowMesaHeadless::swapBuffers()
+{
+ glFinish();
+}
+
+#endif
diff --git a/indra/llwindow/llwindowmesaheadless.h b/indra/llwindow/llwindowmesaheadless.h
new file mode 100644
index 0000000000..550a61d37a
--- /dev/null
+++ b/indra/llwindow/llwindowmesaheadless.h
@@ -0,0 +1,104 @@
+/**
+ * @file llwindowmesaheadless.h
+ * @brief Windows implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWMESAHEADLESS_H
+#define LL_LLWINDOWMESAHEADLESS_H
+
+#if LL_MESA_HEADLESS
+
+#include "llwindow.h"
+#include "GL/osmesa.h"
+
+class LLWindowMesaHeadless : public LLWindow
+{
+public:
+ /*virtual*/ void show() {};
+ /*virtual*/ void hide() {};
+ /*virtual*/ void close() {};
+ /*virtual*/ BOOL getVisible() {return FALSE;};
+ /*virtual*/ BOOL getMinimized() {return FALSE;};
+ /*virtual*/ BOOL getMaximized() {return FALSE;};
+ /*virtual*/ BOOL maximize() {return FALSE;};
+ /*virtual*/ BOOL getFullscreen() {return FALSE;};
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position) {return FALSE;};
+ /*virtual*/ BOOL getSize(LLCoordScreen *size) {return FALSE;};
+ /*virtual*/ BOOL getSize(LLCoordWindow *size) {return FALSE;};
+ /*virtual*/ BOOL setPosition(LLCoordScreen position) {return FALSE;};
+ /*virtual*/ BOOL setSize(LLCoordScreen size) {return FALSE;};
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync) {return FALSE;};
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position) {return FALSE;};
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position) {return FALSE;};
+ /*virtual*/ void showCursor() {};
+ /*virtual*/ void hideCursor() {};
+ /*virtual*/ void showCursorFromMouseMove() {};
+ /*virtual*/ void hideCursorUntilMouseMove() {};
+ /*virtual*/ BOOL isCursorHidden() {return FALSE;};
+ /*virtual*/ void setCursor(ECursorType cursor) {};
+ //virtual ECursorType getCursor() { return mCurrentCursor; };
+ /*virtual*/ void captureMouse() {};
+ /*virtual*/ void releaseMouse() {};
+ /*virtual*/ void setMouseClipping( BOOL b ) {};
+ /*virtual*/ BOOL isClipboardTextAvailable() {return FALSE; };
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst) {return FALSE; };
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString &src) {return FALSE; };
+ /*virtual*/ void flashIcon(F32 seconds) {};
+ /*virtual*/ F32 getGamma() {return 1.0f; };
+ /*virtual*/ BOOL setGamma(const F32 gamma) {return FALSE; }; // Set the gamma
+ /*virtual*/ BOOL restoreGamma() {return FALSE; }; // Restore original gamma table (before updating gamma)
+ //virtual ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput() {};
+ /*virtual*/ void delayInputProcessing() {};
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName() {return LLString(""); };
+ /*virtual*/ void deleteFile( const char* file_name ) {};
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info ) {return 0; };
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL) { return FALSE; };
+
+
+ // handy coordinate space conversion routines
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to) { return FALSE; };
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to) { return FALSE; };
+
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions) { return NULL; };
+ /*virtual*/ F32 getNativeAspectRatio() { return 1.0f; };
+ /*virtual*/ F32 getPixelAspectRatio() { return 1.0f; };
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) {}
+
+ /*virtual*/ void *getPlatformWindow() { return 0; };
+ /*virtual*/ void bringToFront() {};
+
+ LLWindowMesaHeadless(char *title, char *name, S32 x, S32 y, S32 width, S32 height,
+ U32 flags, BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl, BOOL ignore_pixel_depth);
+ ~LLWindowMesaHeadless();
+
+private:
+ OSMesaContext mMesaContext;
+ unsigned char * mMesaBuffer;
+};
+
+class LLSplashScreenMesaHeadless : public LLSplashScreen
+{
+public:
+ LLSplashScreenMesaHeadless() {};
+ virtual ~LLSplashScreenMesaHeadless() {};
+
+ /*virtual*/ void showImpl() {};
+ /*virtual*/ void updateImpl(const char* mesg) {};
+ /*virtual*/ void hideImpl() {};
+
+};
+
+#endif
+
+#endif //LL_LLWINDOWMESAHEADLESS_H
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
new file mode 100644
index 0000000000..75793eb739
--- /dev/null
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -0,0 +1,2487 @@
+/**
+ * @file llwindowsdl.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if LL_SDL
+
+#include "linden_common.h"
+
+#include "llwindowsdl.h"
+#include "llkeyboardsdl.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+#if LL_LINUX
+// not necessarily available on random SDL platforms, so #if LL_LINUX
+// for execv(), waitpid(), fork()
+# include <unistd.h>
+# include <sys/types.h>
+# include <sys/wait.h>
+#endif // LL_LINUX
+
+extern BOOL gDebugWindowProc;
+
+// culled from winuser.h
+//const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+// On the Mac, the scroll wheel reports a delta of 1 for each detent.
+// There's also acceleration for faster scrolling, based on a slider in the system preferences.
+const S32 WHEEL_DELTA = 1; /* Value for rolling one detent */
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+
+//
+// LLWindowSDL
+//
+
+#if LL_X11
+# include <X11/Xutil.h>
+// A global! Well, SDL isn't really designed for communicating
+// with multiple physical X11 displays. Heck, it's not really
+// designed for multiple X11 windows.
+// So, we need this for the SDL/X11 event filter callback (which
+// doesnt have a userdata parameter) and more.
+static Display *SDL_Display = NULL;
+static Window SDL_XWindowID = None;
+#endif //LL_X11
+
+// TOFU HACK -- (*exactly* the same hack as LLWindowMacOSX for the same reasons)
+// For SDL, to put up an OS dialog in full screen mode, we must first switch OUT of full screen mode.
+// The proper way to do this is to bracket the dialog with calls to beforeDialog() and afterDialog(), but these
+// require a pointer to the LLWindowMacSDL object. Stash it here and maintain in the constructor and destructor.
+// This assumes that there will be only one object of this class at any time. Hopefully this is true.
+static LLWindowSDL *gWindowImplementation = NULL;
+
+static BOOL was_fullscreen = FALSE;
+
+// Cross-platform bits:
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+
+#if LL_GTK
+// Check the runtime GTK version for goodness.
+static BOOL maybe_do_gtk_diagnostics(void)
+{
+ static BOOL done_gtk_diag = FALSE;
+ static BOOL is_good = TRUE;
+ gtk_disable_setlocale();
+ if ((!done_gtk_diag) && gtk_init_check(NULL, NULL))
+ {
+ llinfos << "GTK Initialized." << llendl;
+ llinfos << "- Compiled against GTK version "
+ << GTK_MAJOR_VERSION << "."
+ << GTK_MINOR_VERSION << "."
+ << GTK_MICRO_VERSION << llendl;
+ llinfos << "- Running against GTK version "
+ << gtk_major_version << "."
+ << gtk_minor_version << "."
+ << gtk_micro_version << llendl;
+ gchar *gtk_warning;
+ gtk_warning = gtk_check_version(GTK_MAJOR_VERSION,
+ GTK_MINOR_VERSION,
+ GTK_MICRO_VERSION);
+ if (gtk_warning)
+ {
+ llwarns << "- GTK COMPATIBILITY WARNING: " <<
+ gtk_warning << llendl;
+ is_good = FALSE;
+ }
+
+ done_gtk_diag = TRUE;
+ }
+ return is_good;
+}
+#endif // LL_GTK
+
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strncasecmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+
+
+LLWindowSDL::LLWindowSDL(char *title, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags), mGamma(1.0f)
+{
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardSDL();
+ // Note that we can't set up key-repeat until after SDL has init'd video
+
+ // Ignore use_gl for now, only used for drones on PC
+ mWindow = NULL;
+ mCursorDecoupled = FALSE;
+ mCursorLastEventDeltaX = 0;
+ mCursorLastEventDeltaY = 0;
+ mCursorIgnoreNextDelta = FALSE;
+ mNeedsResize = FALSE;
+ mOverrideAspectRatio = 0.f;
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+ mHaveInputFocus = -1;
+ mIsMinimized = -1;
+
+ // Get the original aspect ratio of the main device.
+ mOriginalAspectRatio = 1024.0 / 768.0; // !!! FIXME //(double)CGDisplayPixelsWide(mDisplay) / (double)CGDisplayPixelsHigh(mDisplay);
+
+ if (!title)
+ title = "SDL Window"; // !!! FIXME
+
+ // Stash the window title
+ mWindowTitle = new char[strlen(title) + 1];
+ strcpy(mWindowTitle, title);
+
+ // Create the GL context and set it up for windowed or fullscreen, as appropriate.
+ if(createContext(x, y, width, height, 32, fullscreen, disable_vsync))
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+
+ stop_glerror();
+
+ // Stash an object pointer for OSMessageBox()
+ gWindowImplementation = this;
+
+#if LL_X11
+ mFlashing = FALSE;
+#endif // LL_X11
+}
+
+static SDL_Surface *Load_BMP_Resource(const char *basename)
+{
+ const int PATH_BUFFER_SIZE=1000;
+ char path_buffer[PATH_BUFFER_SIZE];
+
+ // Figure out where our BMP is living on the disk
+ snprintf(path_buffer, PATH_BUFFER_SIZE-1, "%s%sres-sdl%s%s",
+ gDirUtilp->getAppRODataDir().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ basename);
+ path_buffer[PATH_BUFFER_SIZE-1] = '\0';
+
+ return SDL_LoadBMP(path_buffer);
+}
+
+BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync)
+{
+ //bool glneedsinit = false;
+// const char *gllibname = null; // !!! fixme
+
+ llinfos << "createContext, fullscreen=" << fullscreen <<
+ " size=" << width << "x" << height << llendl;
+
+ // captures don't survive contexts
+ mGrabbyKeyFlags = 0;
+ mReallyCapturedCount = 0;
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ // !!! fixme: stderr?
+ llinfos << "sdl_init() failed! " << SDL_GetError() << llendl;
+ setupFailure("window creation error", "error", OSMB_OK);
+ return false;
+ }
+
+ SDL_version c_sdl_version;
+ SDL_VERSION(&c_sdl_version);
+ llinfos << "Compiled against SDL "
+ << int(c_sdl_version.major) << "."
+ << int(c_sdl_version.minor) << "."
+ << int(c_sdl_version.patch) << llendl;
+ const SDL_version *r_sdl_version;
+ r_sdl_version = SDL_Linked_Version();
+ llinfos << " Running against SDL "
+ << int(r_sdl_version->major) << "."
+ << int(r_sdl_version->minor) << "."
+ << int(r_sdl_version->patch) << llendl;
+
+ const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo( );
+ if (!videoInfo)
+ {
+ llinfos << "SDL_GetVideoInfo() failed! " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ SDL_EnableUNICODE(1);
+ SDL_WM_SetCaption(mWindowTitle, mWindowTitle);
+
+ // Set the application icon.
+ SDL_Surface *bmpsurface;
+ bmpsurface = Load_BMP_Resource("ll_icon.BMP");
+ if (bmpsurface)
+ {
+ // This attempts to give a black-keyed mask to the icon.
+ SDL_SetColorKey(bmpsurface,
+ SDL_SRCCOLORKEY,
+ SDL_MapRGB(bmpsurface->format, 0,0,0) );
+ SDL_WM_SetIcon(bmpsurface, NULL);
+ // The SDL examples cheerfully avoid freeing the icon
+ // surface, but I'm betting that's leaky.
+ SDL_FreeSurface(bmpsurface);
+ bmpsurface = NULL;
+ }
+
+ // note: these SetAttributes make Tom's 9600-on-AMD64 fail to
+ // get a visual, but it's broken anyway when it does, and without
+ // these SetAttributes we might easily get an avoidable substandard
+ // visual to work with on most other machines.
+ SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE,8);
+ SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
+ SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, (bits <= 16) ? 16 : 24);
+ SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, (bits <= 16) ? 1 : 8);
+
+ // !!! FIXME: try to toggle vsync here?
+
+ mFullscreen = fullscreen;
+ was_fullscreen = fullscreen;
+
+ int sdlflags = SDL_OPENGL | SDL_RESIZABLE | SDL_ANYFORMAT;
+
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
+
+ mSDLFlags = sdlflags;
+
+ if (mFullscreen)
+ {
+ llinfos << "createContext: setting up fullscreen " << width << "x" << height << llendl;
+
+ // If the requested width or height is 0, find the best default for the monitor.
+ if((width == 0) || (height == 0))
+ {
+ // Scan through the list of modes, looking for one which has:
+ // height between 700 and 800
+ // aspect ratio closest to the user's original mode
+ S32 resolutionCount = 0;
+ LLWindowResolution *resolutionList = getSupportedResolutions(resolutionCount);
+
+ if(resolutionList != NULL)
+ {
+ F32 closestAspect = 0;
+ U32 closestHeight = 0;
+ U32 closestWidth = 0;
+ int i;
+
+ llinfos << "createContext: searching for a display mode, original aspect is " << mOriginalAspectRatio << llendl;
+
+ for(i=0; i < resolutionCount; i++)
+ {
+ F32 aspect = (F32)resolutionList[i].mWidth / (F32)resolutionList[i].mHeight;
+
+ llinfos << "createContext: width " << resolutionList[i].mWidth << " height " << resolutionList[i].mHeight << " aspect " << aspect << llendl;
+
+ if( (resolutionList[i].mHeight >= 700) && (resolutionList[i].mHeight <= 800) &&
+ (fabs(aspect - mOriginalAspectRatio) < fabs(closestAspect - mOriginalAspectRatio)))
+ {
+ llinfos << " (new closest mode) " << llendl;
+
+ // This is the closest mode we've seen yet.
+ closestWidth = resolutionList[i].mWidth;
+ closestHeight = resolutionList[i].mHeight;
+ closestAspect = aspect;
+ }
+ }
+
+ width = closestWidth;
+ height = closestHeight;
+ }
+ }
+
+ if((width == 0) || (height == 0))
+ {
+ // Mode search failed for some reason. Use the old-school default.
+ width = 1024;
+ height = 768;
+ }
+
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags | SDL_FULLSCREEN);
+
+ if (mWindow)
+ {
+ mFullscreen = TRUE;
+ was_fullscreen = TRUE;
+ mFullscreenWidth = mWindow->w;
+ mFullscreenHeight = mWindow->h;
+ mFullscreenBits = mWindow->format->BitsPerPixel;
+ mFullscreenRefresh = -1;
+
+ llinfos << "Running at " << mFullscreenWidth
+ << "x" << mFullscreenHeight
+ << "x" << mFullscreenBits
+ << " @ " << mFullscreenRefresh
+ << llendl;
+ }
+ else
+ {
+ llwarns << "createContext: fullscreen creation failure. SDL: " << SDL_GetError() << llendl;
+ // No fullscreen support
+ mFullscreen = FALSE;
+ was_fullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ if(!mFullscreen && (mWindow == NULL))
+ {
+ if (width == 0)
+ width = 1024;
+ if (height == 0)
+ width = 768;
+
+ llinfos << "createContext: creating window " << width << "x" << height << "x" << bits << llendl;
+ mWindow = SDL_SetVideoMode(width, height, bits, sdlflags);
+
+ if (!mWindow)
+ {
+ llwarns << "createContext: window creation failure. SDL: " << SDL_GetError() << llendl;
+ setupFailure("Window creation error", "Error", OSMB_OK);
+ return FALSE;
+ }
+ } else if (!mFullscreen && (mWindow != NULL))
+ {
+ llinfos << "createContext: SKIPPING - !fullscreen, but +mWindow " << width << "x" << height << "x" << bits << llendl;
+ }
+
+ /*if (!load_all_glsyms(gllibname))
+ {
+ SDL_QuitSubSystem(SDL_INIT_VIDEO);
+ return FALSE;
+ }*/
+
+ gGLManager.mVRAM = videoInfo->video_mem / 1024;
+ if (gGLManager.mVRAM != 0)
+ {
+ llinfos << "Detected " << gGLManager.mVRAM << "MB VRAM." << llendl;
+ }
+ // If VRAM is not detected, that is handled later
+
+#if 0 // !!! FIXME: all video cards suck under Linux. :)
+ // Since we just created the context, it needs to be set up.
+ glNeedsInit = TRUE;
+ if(glNeedsInit)
+ {
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return FALSE;
+ }
+ }
+ }
+#endif
+
+ GLint depthBits, stencilBits, redBits, greenBits, blueBits, alphaBits;
+
+ glGetIntegerv(GL_RED_BITS, &redBits);
+ glGetIntegerv(GL_GREEN_BITS, &greenBits);
+ glGetIntegerv(GL_BLUE_BITS, &blueBits);
+ glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
+ glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+ glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
+
+ llinfos << "GL buffer:" << llendl
+ llinfos << " Red Bits " << S32(redBits) << llendl
+ llinfos << " Green Bits " << S32(greenBits) << llendl
+ llinfos << " Blue Bits " << S32(blueBits) << llendl
+ llinfos << " Alpha Bits " << S32(alphaBits) << llendl
+ llinfos << " Depth Bits " << S32(depthBits) << llendl
+ llinfos << " Stencil Bits " << S32(stencilBits) << llendl;
+
+ GLint colorBits = redBits + greenBits + blueBits + alphaBits;
+ // fixme: actually, it's REALLY important for picking that we get at
+ // least 8 bits each of red,green,blue. Alpha we can be a bit more
+ // relaxed about if we have to.
+ if (colorBits < 32)
+ {
+ close();
+ setupFailure(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+#if 0 // !!! FIXME: we're going to brave it for now...
+ if (alphaBits < 8)
+ {
+ close();
+ setupFailure(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+#endif
+
+#if LL_X11
+ init_x11clipboard();
+#endif // LL_X11
+
+ // We need to do this here, once video is init'd
+ if (-1 == SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY,
+ SDL_DEFAULT_REPEAT_INTERVAL))
+ llwarns << "Couldn't enable key-repeat: " << SDL_GetError() <<llendl;
+
+ // Don't need to get the current gamma, since there's a call that restores it to the system defaults.
+ return TRUE;
+}
+
+
+// changing fullscreen resolution, or switching between windowed and fullscreen mode.
+BOOL LLWindowSDL::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ const BOOL needsRebuild = TRUE; // Just nuke the context and start over.
+ BOOL result = true;
+
+ llinfos << "switchContext, fullscreen=" << fullscreen << llendl;
+ stop_glerror();
+ if(needsRebuild)
+ {
+ destroyContext();
+ result = createContext(0, 0, size.mX, size.mY, 0, fullscreen, disable_vsync);
+ if (result)
+ {
+ gGLManager.initGL();
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+ }
+ }
+
+ stop_glerror();
+
+ return result;
+}
+
+void LLWindowSDL::destroyContext()
+{
+ llinfos << "destroyContext begins" << llendl;
+#if LL_X11
+ quit_x11clipboard();
+#endif // LL_X11
+
+ // Clean up remaining GL state before blowing away window
+ llinfos << "shutdownGL begins" << llendl;
+ gGLManager.shutdownGL();
+ llinfos << "SDL_QuitSS/VID begins" << llendl;
+ SDL_QuitSubSystem(SDL_INIT_VIDEO); // !!! !!! FIXME: this might be risky...
+ //unload_all_glsyms();
+
+ mWindow = NULL;
+}
+
+LLWindowSDL::~LLWindowSDL()
+{
+ quitCursors();
+ destroyContext();
+
+ if(mSupportedResolutions != NULL)
+ {
+ delete []mSupportedResolutions;
+ }
+
+ delete[] mWindowTitle;
+
+ gWindowImplementation = NULL;
+}
+
+
+void LLWindowSDL::show()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::hide()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::minimize()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+void LLWindowSDL::restore()
+{
+ // !!! FIXME: What to do with SDL?
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowSDL::close()
+{
+ // Is window is already closed?
+ // if (!mWindow)
+ // {
+ // return;
+ // }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ destroyContext();
+}
+
+BOOL LLWindowSDL::isValid()
+{
+ return (mWindow != NULL);
+}
+
+BOOL LLWindowSDL::getVisible()
+{
+ BOOL result = FALSE;
+
+ // !!! FIXME: This isn't really right...
+ if (mWindow)
+ {
+ result = TRUE;
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::getMinimized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow && (1 == mIsMinimized))
+ {
+ result = TRUE;
+ }
+ return(result);
+}
+
+BOOL LLWindowSDL::getMaximized()
+{
+ BOOL result = FALSE;
+
+ if (mWindow)
+ {
+ // TODO
+ }
+
+ return(result);
+}
+
+BOOL LLWindowSDL::maximize()
+{
+ // TODO
+ return FALSE;
+}
+
+BOOL LLWindowSDL::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowSDL::getPosition(LLCoordScreen *position)
+{
+ // !!! FIXME: can anything be done with this?
+ position->mX = 0;
+ position->mY = 0;
+ return TRUE;
+}
+
+BOOL LLWindowSDL::getSize(LLCoordScreen *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::getSize(LLCoordWindow *size)
+{
+ if (mWindow)
+ {
+ size->mX = mWindow->w;
+ size->mY = mWindow->h;
+ return (TRUE);
+ }
+
+ llerrs << "LLWindowSDL::getPosition(): no window and not fullscreen!" << llendl;
+ return (FALSE);
+}
+
+BOOL LLWindowSDL::setPosition(const LLCoordScreen position)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //MacMoveWindow(mWindow, position.mX, position.mY, false);
+ }
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::setSize(const LLCoordScreen size)
+{
+ if(mWindow)
+ {
+ // !!! FIXME...
+ //SizeWindow(mWindow, size.mX, size.mY, true);
+ }
+
+ return TRUE;
+}
+
+void LLWindowSDL::swapBuffers()
+{
+ if (mWindow)
+ SDL_GL_SwapBuffers();
+}
+
+F32 LLWindowSDL::getGamma()
+{
+ return 1/mGamma;
+}
+
+BOOL LLWindowSDL::restoreGamma()
+{
+ //CGDisplayRestoreColorSyncSettings();
+ SDL_SetGamma(1.0f, 1.0f, 1.0f);
+ return true;
+}
+
+BOOL LLWindowSDL::setGamma(const F32 gamma)
+{
+ mGamma = gamma;
+ if (mGamma == 0) mGamma = 0.1f;
+ mGamma = 1/mGamma;
+ SDL_SetGamma(mGamma, mGamma, mGamma);
+ return true;
+}
+
+BOOL LLWindowSDL::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+
+// Constrains the mouse to the window.
+void LLWindowSDL::setMouseClipping( BOOL b )
+{
+ //llinfos << "LLWindowSDL::setMouseClipping " << b << llendl;
+ // Just stash the requested state. We'll simulate this when the cursor is hidden by decoupling.
+ mIsMouseClipping = b;
+ //SDL_WM_GrabInput(b ? SDL_GRAB_ON : SDL_GRAB_OFF);
+ adjustCursorDecouple();
+}
+
+BOOL LLWindowSDL::setCursorPosition(const LLCoordWindow position)
+{
+ BOOL result = TRUE;
+ LLCoordScreen screen_pos;
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ //llinfos << "setCursorPosition(" << screen_pos.mX << ", " << screen_pos.mY << ")" << llendl;
+
+ SDL_WarpMouse(screen_pos.mX, screen_pos.mY);
+
+ // Under certain circumstances, this will trigger us to decouple the cursor.
+ adjustCursorDecouple(true);
+
+ return result;
+}
+
+BOOL LLWindowSDL::getCursorPosition(LLCoordWindow *position)
+{
+ //Point cursor_point;
+ LLCoordScreen screen_pos;
+
+ //GetMouse(&cursor_point);
+ int x, y;
+ SDL_GetMouseState(&x, &y);
+
+ screen_pos.mX = x;
+ screen_pos.mY = y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowSDL::adjustCursorDecouple(bool warpingMouse)
+{
+ if(mIsMouseClipping && mCursorHidden)
+ {
+ if(warpingMouse)
+ {
+ // The cursor should be decoupled. Make sure it is.
+ if(!mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: decoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(false);
+ mCursorDecoupled = true;
+ mCursorIgnoreNextDelta = TRUE;
+ }
+ }
+ }
+ else
+ {
+ // The cursor should not be decoupled. Make sure it isn't.
+ if(mCursorDecoupled)
+ {
+ // llinfos << "adjustCursorDecouple: recoupling cursor" << llendl;
+ //CGAssociateMouseAndMouseCursorPosition(true);
+ mCursorDecoupled = false;
+ }
+ }
+}
+
+F32 LLWindowSDL::getNativeAspectRatio()
+{
+#if 0
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+ //rn: AC
+#endif
+
+ // MBW -- there are a couple of bad assumptions here. One is that the display list won't include
+ // ridiculous resolutions nobody would ever use. The other is that the list is in order.
+
+ // New assumptions:
+ // - pixels are square (the only reasonable choice, really)
+ // - The user runs their display at a native resolution, so the resolution of the display
+ // when the app is launched has an aspect ratio that matches the monitor.
+
+ //RN: actually, the assumption that there are no ridiculous resolutions (above the display's native capabilities) has
+ // been born out in my experience.
+ // Pixels are often not square (just ask the people who run their LCDs at 1024x768 or 800x600 when running fullscreen, like me)
+ // The ordering of display list is a blind assumption though, so we should check for max values
+ // Things might be different on the Mac though, so I'll defer to MBW
+
+ // The constructor for this class grabs the aspect ratio of the monitor before doing any resolution
+ // switching, and stashes it in mOriginalAspectRatio. Here, we just return it.
+
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+
+ return mOriginalAspectRatio;
+}
+
+F32 LLWindowSDL::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+
+// some of this stuff is to support 'temporarily windowed' mode so that
+// dialogs are still usable in fullscreen. HOWEVER! - it's not enabled/working
+// yet.
+static LLCoordScreen old_size;
+static BOOL old_fullscreen;
+void LLWindowSDL::beforeDialog()
+{
+ llinfos << "LLWindowSDL::beforeDialog()" << llendl;
+
+ if (SDLReallyCaptureInput(FALSE) // must ungrab input so popup works!
+ && getSize(&old_size))
+ {
+ old_fullscreen = was_fullscreen;
+
+ if (old_fullscreen)
+ {
+ // NOT YET WORKING
+ //switchContext(FALSE, old_size, TRUE);
+ }
+ }
+
+#if LL_X11
+ if (SDL_Display)
+ {
+ // Everything that we/SDL asked for should happen before we
+ // potentially hand control over to GTK.
+ XSync(SDL_Display, False);
+ }
+#endif // LL_X11
+
+#if LL_GTK
+ // this is a good time to grab some GTK version information for
+ // diagnostics
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+}
+
+void LLWindowSDL::afterDialog()
+{
+ llinfos << "LLWindowSDL::afterDialog()" << llendl;
+ if (old_fullscreen && !was_fullscreen)
+ {
+ // NOT YET WORKING (see below)
+ //switchContext(TRUE, old_size, TRUE);
+ }
+ // !!! FIXME - we need to restore the GL context using
+ // LLViewerWindow::restoreGL() - but how??
+}
+
+
+S32 LLWindowSDL::stat(const char* file_name, struct stat* stat_info)
+{
+ return ::stat( file_name, stat_info );
+}
+
+#if LL_X11
+// set/reset the XWMHints flag for 'urgency' that usually makes the icon flash
+void LLWindowSDL::x11_set_urgent(BOOL urgent)
+{
+ if (SDL_Display && !mFullscreen)
+ {
+ XWMHints *wm_hints;
+
+ llinfos << "X11 hint for urgency, " << urgent << llendl;
+
+ wm_hints = XGetWMHints(SDL_Display, mSDL_XWindowID);
+ if (!wm_hints)
+ wm_hints = XAllocWMHints();
+
+ if (urgent)
+ wm_hints->flags |= XUrgencyHint;
+ else
+ wm_hints->flags &= ~XUrgencyHint;
+
+ XSetWMHints(SDL_Display, mSDL_XWindowID, wm_hints);
+ XFree(wm_hints);
+ XSync(SDL_Display, False);
+ }
+}
+#endif // LL_X11
+
+void LLWindowSDL::flashIcon(F32 seconds)
+{
+#if !LL_X11
+ llinfos << "Stub LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+#else
+ llinfos << "X11 LLWindowSDL::flashIcon(" << seconds << ")" << llendl;
+
+ F32 remaining_time = mFlashTimer.getRemainingTimeF32();
+ if (remaining_time < seconds)
+ remaining_time = seconds;
+ mFlashTimer.reset();
+ mFlashTimer.setTimerExpirySec(remaining_time);
+
+ x11_set_urgent(TRUE);
+ mFlashing = TRUE;
+#endif // LL_X11
+}
+
+#if LL_X11
+/* Lots of low-level X11 stuff to handle X11 copy-and-paste */
+
+/* Our X11 clipboard support is a bit bizarre in various
+ organically-grown ways. Ideally it should be fixed to do
+ real string-type negotiation (this would make pasting to
+ xterm faster and pasting to UTF-8 emacs work properly), but
+ right now it has the rare and desirable trait of being
+ generally stable and working. */
+
+/* PRIMARY and CLIPBOARD are the two main kinds of
+ X11 clipboard. A third are the CUT_BUFFERs which an
+ obsolete holdover from X10 days and use a quite orthogonal
+ mechanism. CLIPBOARD is the type whose design most
+ closely matches SL's own win32-alike explicit copy-and-paste
+ paradigm.
+
+ Pragmatically we support all three to varying degrees. When
+ we paste into SL, it is strictly from CLIPBOARD. When we copy,
+ we support (to as full an extent as the clipboard content type
+ allows) CLIPBOARD, PRIMARY, and CUT_BUFFER0.
+ */
+#define SL_READWRITE_XCLIPBOARD_TYPE XInternAtom(SDL_Display, "CLIPBOARD", False)
+#define SL_WRITE_XCLIPBOARD_TYPE XA_PRIMARY
+
+/* This is where our own private cutbuffer goes - we don't use
+ a regular cutbuffer (XA_CUT_BUFFER0 etc) for intermediate
+ storage because their use isn't really defined for holding UTF8. */
+#define SL_CUTBUFFER_TYPE XInternAtom(SDL_Display, "SECONDLIFE_CUTBUFFER", False)
+
+/* These defines, and convert_data/convert_x11clipboard,
+ mostly exist to support non-text or unusually-encoded
+ clipboard data, which we don't really have a need for at
+ the moment. */
+#define SDLCLIPTYPE(A, B, C, D) (int)((A<<24)|(B<<16)|(C<<8)|(D<<0))
+#define FORMAT_PREFIX "SECONDLIFE_x11clipboard_0x"
+
+typedef Atom x11clipboard_type;
+
+static
+x11clipboard_type convert_format(int type)
+{
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ // old-style X11 clipboard, strictly only ISO 8859-1 encoding
+ return XA_STRING;
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ // newer de-facto UTF8 clipboard atom
+ return XInternAtom(SDL_Display, "UTF8_STRING", False);
+ default:
+ {
+ /* completely arbitrary clipboard types... we don't actually use
+ these right now, and support is skeletal. */
+ char format[sizeof(FORMAT_PREFIX)+8+1];
+
+ sprintf(format, "%s%08lx", FORMAT_PREFIX, (unsigned long)type);
+ return XInternAtom(SDL_Display, format, False);
+ }
+ }
+}
+
+/* convert platform string to x11 clipboard format. for our
+ purposes this is pretty trivial right now. */
+static int
+convert_data(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_data: Unknown medium type" << llendl;
+ break;
+ }
+ return(dstlen);
+}
+
+/* Convert x11clipboard data to platform string. This too is
+ pretty trivial for our needs right now, and just about identical
+ to above. */
+static int
+convert_x11clipboard(int type, char *dst, const char *src, int srclen)
+{
+ int dstlen;
+
+ dstlen = 0;
+ switch (type)
+ {
+ case SDLCLIPTYPE('U', 'T', 'F', '8'):
+ case SDLCLIPTYPE('T', 'E', 'X', 'T'):
+ if ( srclen == 0 )
+ srclen = strlen(src);
+
+ dstlen = srclen + 1;
+
+ if ( dst ) // assume caller made it big enough by asking us
+ {
+ memcpy(dst, src, srclen);
+ dst[srclen] = '\0';
+ }
+ break;
+
+ default:
+ llwarns << "convert_x11clipboard: Unknown medium type" << llendl;
+ break;
+ }
+ return dstlen;
+}
+
+int
+LLWindowSDL::is_empty_x11clipboard(void)
+{
+ int retval;
+
+ Lock_Display();
+ retval = ( XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE) == None );
+ Unlock_Display();
+
+ return(retval);
+}
+
+void
+LLWindowSDL::put_x11clipboard(int type, int srclen, const char *src)
+{
+ x11clipboard_type format;
+ int dstlen;
+ char *dst;
+
+ format = convert_format(type);
+ dstlen = convert_data(type, NULL, src, srclen);
+
+ dst = (char *)malloc(dstlen);
+ if ( dst != NULL )
+ {
+ Window root = DefaultRootWindow(SDL_Display);
+ Lock_Display();
+ convert_data(type, dst, src, srclen);
+ // Cutbuffers are only allowed to have STRING atom types,
+ // but Emacs puts UTF8 inside them anyway. We cautiously
+ // don't.
+ if (type == SDLCLIPTYPE('T','E','X','T'))
+ {
+ // dstlen-1 so we don't include the trailing \0
+ llinfos << "X11: Populating cutbuffer." <<llendl;
+ XChangeProperty(SDL_Display, root,
+ XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ } else {
+ // Should we clear the cutbuffer if we can't put the selection in
+ // it because it's a UTF8 selection? Eh, no great reason I think.
+ //XDeleteProperty(SDL_Display, root, XA_CUT_BUFFER0);
+ }
+ // Private cutbuffer of an appropriate type.
+ XChangeProperty(SDL_Display, root,
+ SL_CUTBUFFER_TYPE, format, 8, PropModeReplace,
+ (unsigned char*)dst, dstlen-1);
+ free(dst);
+
+ /* Claim ownership of both PRIMARY and CLIPBOARD */
+ XSetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+ XSetSelectionOwner(SDL_Display, SL_WRITE_XCLIPBOARD_TYPE,
+ mSDL_XWindowID, CurrentTime);
+
+ Unlock_Display();
+ }
+}
+
+void
+LLWindowSDL::get_x11clipboard(int type, int *dstlen, char **dst)
+{
+ x11clipboard_type format;
+
+ *dstlen = 0;
+ format = convert_format(type);
+
+ Window owner;
+ Atom selection;
+ Atom seln_type;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ char *src;
+
+ Lock_Display();
+ owner = XGetSelectionOwner(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE);
+ Unlock_Display();
+ if (owner == None)
+ {
+ // Fall right back to ancient X10 cut-buffers
+ owner = DefaultRootWindow(SDL_Display);
+ selection = XA_CUT_BUFFER0;
+ } else if (owner == mSDL_XWindowID)
+ {
+ // Use our own uncooked opaque string property
+ owner = DefaultRootWindow(SDL_Display);
+ selection = SL_CUTBUFFER_TYPE;
+ }
+ else
+ {
+ // Use full-on X11-style clipboard negotiation with the owning app
+ int selection_response = 0;
+ SDL_Event event;
+
+ owner = mSDL_XWindowID;
+ Lock_Display();
+ selection = XInternAtom(SDL_Display, "SDL_SELECTION", False);
+ XConvertSelection(SDL_Display, SL_READWRITE_XCLIPBOARD_TYPE, format,
+ selection, owner, CurrentTime);
+ Unlock_Display();
+ llinfos << "X11: Waiting for clipboard to arrive." <<llendl;
+ while ( ! selection_response )
+ {
+ // Only look for SYSWMEVENTs, or we may lose keypresses
+ // etc.
+ SDL_PumpEvents();
+ if (1 == SDL_PeepEvents(&event, 1, SDL_GETEVENT,
+ SDL_SYSWMEVENTMASK) )
+ {
+ if ( event.type == SDL_SYSWMEVENT )
+ {
+ XEvent xevent =
+ event.syswm.msg->event.xevent;
+
+ if ( (xevent.type == SelectionNotify)&&
+ (xevent.xselection.requestor == owner) )
+ selection_response = 1;
+ }
+ } else {
+ llinfos << "X11: Waiting for SYSWM event..." << llendl;
+ }
+ }
+ llinfos << "X11: Clipboard arrived." <<llendl;
+ }
+
+ Lock_Display();
+ if ( XGetWindowProperty(SDL_Display, owner, selection, 0, INT_MAX/4,
+ False, format, &seln_type, &seln_format,
+ &nbytes, &overflow, (unsigned char **)&src) == Success )
+ {
+ if ( seln_type == format )
+ {
+ *dstlen = convert_x11clipboard(type, NULL, src, nbytes);
+ *dst = (char *)realloc(*dst, *dstlen);
+ if ( *dst == NULL )
+ *dstlen = 0;
+ else
+ convert_x11clipboard(type, *dst, src, nbytes);
+ }
+ XFree(src);
+ }
+
+ Unlock_Display();
+}
+
+int clipboard_filter_callback(const SDL_Event *event)
+{
+ /* Post all non-window manager specific events */
+ if ( event->type != SDL_SYSWMEVENT )
+ {
+ return(1);
+ }
+
+ /* Handle window-manager specific clipboard events */
+ switch (event->syswm.msg->event.xevent.type) {
+ /* Copy the selection from SL_CUTBUFFER_TYPE to the requested property */
+ case SelectionRequest: {
+ XSelectionRequestEvent *req;
+ XEvent sevent;
+ int seln_format;
+ unsigned long nbytes;
+ unsigned long overflow;
+ unsigned char *seln_data;
+
+ req = &event->syswm.msg->event.xevent.xselectionrequest;
+ sevent.xselection.type = SelectionNotify;
+ sevent.xselection.display = req->display;
+ sevent.xselection.selection = req->selection;
+ sevent.xselection.target = None;
+ sevent.xselection.property = None;
+ sevent.xselection.requestor = req->requestor;
+ sevent.xselection.time = req->time;
+ if ( XGetWindowProperty(SDL_Display, DefaultRootWindow(SDL_Display),
+ SL_CUTBUFFER_TYPE, 0, INT_MAX/4, False, req->target,
+ &sevent.xselection.target, &seln_format,
+ &nbytes, &overflow, &seln_data) == Success )
+ {
+ if ( sevent.xselection.target == req->target)
+ {
+ if ( sevent.xselection.target == XA_STRING ||
+ sevent.xselection.target ==
+ convert_format(SDLCLIPTYPE('U','T','F','8')) )
+ {
+ if ( seln_data[nbytes-1] == '\0' )
+ --nbytes;
+ }
+ XChangeProperty(SDL_Display, req->requestor, req->property,
+ req->target, seln_format, PropModeReplace,
+ seln_data, nbytes);
+ sevent.xselection.property = req->property;
+#define XA_TARGETS XInternAtom(SDL_Display, "TARGETS", False)
+ } else if (XA_TARGETS == req->target) {
+ /* only advertise what we currently support */
+ const int num_supported = 3;
+ Atom supported[num_supported] = {
+ XA_STRING, // will be over-written below
+ XInternAtom(SDL_Display, "TEXT",False),
+ XA_TARGETS
+ };
+ supported[0] = sevent.xselection.target;
+ XChangeProperty(SDL_Display, req->requestor,
+ req->property, XA_ATOM, 32, PropModeReplace,
+ (unsigned char*)supported,
+ num_supported);
+ sevent.xselection.property = req->property;
+ llinfos << "Clipboard: An app asked us what selections format we offer." << llendl;
+ } else {
+ llinfos << "Clipboard: An app requested an unsupported selection format " << req->target << ", we have " << sevent.xselection.target << llendl;
+ sevent.xselection.target = None;
+ }
+ XFree(seln_data);
+ }
+ int sendret =
+ XSendEvent(SDL_Display,req->requestor,False,0,&sevent);
+ if ((sendret==BadValue) || (sendret==BadWindow))
+ llwarns << "Clipboard SendEvent failed" << llendl;
+ XSync(SDL_Display, False);
+ }
+ break;
+ }
+
+ /* Post the event for X11 clipboard reading above */
+ return(1);
+}
+
+int
+LLWindowSDL::init_x11clipboard(void)
+{
+ SDL_SysWMinfo info;
+ int retval;
+
+ /* Grab the window manager specific information */
+ retval = -1;
+ SDL_SetError("SDL is not running on known window manager");
+
+ SDL_VERSION(&info.version);
+ if ( SDL_GetWMInfo(&info) )
+ {
+ /* Save the information for later use */
+ if ( info.subsystem == SDL_SYSWM_X11 )
+ {
+ SDL_Display = info.info.x11.display;
+ SDL_XWindowID = info.info.x11.wmwindow;
+ mSDL_XWindowID = info.info.x11.wmwindow;
+ Lock_Display = info.info.x11.lock_func;
+ Unlock_Display = info.info.x11.unlock_func;
+
+ /* Enable the special window hook events */
+ SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE);
+ SDL_SetEventFilter(clipboard_filter_callback);
+
+ retval = 0;
+ }
+ else
+ {
+ SDL_SetError("SDL is not running on X11");
+ }
+ }
+ return(retval);
+}
+
+void
+LLWindowSDL::quit_x11clipboard(void)
+{
+ SDL_Display = NULL;
+ SDL_XWindowID = None;
+ mSDL_XWindowID = None;
+ Lock_Display = NULL;
+ Unlock_Display = NULL;
+
+ SDL_SetEventFilter(NULL); // Stop custom event filtering
+}
+
+/************************************************/
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return !is_empty_x11clipboard();
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ int cliplen; // seems 1 or 2 bytes longer than expected
+ char *cliptext = NULL;
+ get_x11clipboard(SDLCLIPTYPE('U','T','F','8'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got UTF8 clipboard text." << llendl;
+ // at some future time we can use cliplen instead of relying on \0,
+ // if we ever grok non-ascii, non-utf8 encodings on the clipboard.
+ std::string clip_str(cliptext);
+ // we can't necessarily trust the incoming text to be valid UTF-8,
+ // but utf8str_to_wstring() seems to do an appropriate level of
+ // validation for avoiding over-reads.
+ dst = utf8str_to_wstring(clip_str);
+ /*llinfos << "X11 pasteTextFromClipboard: cliplen=" << cliplen <<
+ " strlen(cliptext)=" << strlen(cliptext) <<
+ " clip_str.length()=" << clip_str.length() <<
+ " dst.length()=" << dst.length() <<
+ llendl;*/
+ free(cliptext);
+ return TRUE; // success
+ }
+ get_x11clipboard(SDLCLIPTYPE('T','E','X','T'), &cliplen, &cliptext);
+ if (cliptext)
+ {
+ llinfos << "X11: Got ISO 8859-1 clipboard text." << llendl;
+ std::string clip_str(cliptext);
+ std::string utf8_str = rawstr_to_utf8(clip_str);
+ dst = utf8str_to_wstring(utf8_str);
+ free(cliptext);
+ }
+ return FALSE; // failure
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ std::string utf8text = wstring_to_utf8str(s);
+ const char* cstr = utf8text.c_str();
+ int cstrlen = strlen(cstr);
+ int i;
+ for (i=0; i<cstrlen; ++i)
+ {
+ if (0x80 & (unsigned char)cstr[i])
+ {
+ // Found an 8-bit character; use new-style UTF8 clipboard
+ llinfos << "X11: UTF8 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('U','T','F','8'), cstrlen, cstr);
+ return TRUE;
+ }
+ }
+ // Didn't find any 8-bit characters; use old-style ISO 8859-1 clipboard
+ llinfos << "X11: ISO 8859-1 copyTextToClipboard" << llendl;
+ put_x11clipboard(SDLCLIPTYPE('T','E','X','T'), cstrlen, cstr);
+ return TRUE;
+}
+#else
+
+BOOL LLWindowSDL::isClipboardTextAvailable()
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::pasteTextFromClipboard(LLWString &dst)
+{
+ return FALSE; // unsupported
+}
+
+BOOL LLWindowSDL::copyTextToClipboard(const LLWString &s)
+{
+ return FALSE; // unsupported
+}
+#endif // LL_X11
+
+BOOL LLWindowSDL::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // MBW -- XXX -- Um... yeah. I'll get to this later.
+
+ return FALSE;
+}
+
+
+LLWindow::LLWindowResolution* LLWindowSDL::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ mNumSupportedResolutions = 0;
+
+ SDL_Rect **modes = SDL_ListModes(NULL, SDL_OPENGL | SDL_FULLSCREEN);
+ if ( (modes != NULL) && (modes != ((SDL_Rect **) -1)) )
+ {
+ int count = 0;
+ while (*modes) // they're sorted biggest to smallest, so find end...
+ {
+ modes++;
+ count++;
+ }
+
+ while (count--)
+ {
+ modes--;
+ SDL_Rect *r = *modes;
+ int w = r->w;
+ int h = r->h;
+ if ((w >= 800) && (h >= 600))
+ {
+ // make sure we don't add the same resolution multiple times!
+ if ( (mNumSupportedResolutions == 0) ||
+ ((mSupportedResolutions[mNumSupportedResolutions-1].mWidth != w) &&
+ (mSupportedResolutions[mNumSupportedResolutions-1].mHeight != h)) )
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = w;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = h;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ if (!to)
+ return FALSE;
+
+ to->mX = from.mX;
+ to->mY = mWindow->h - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ if (!to)
+ return FALSE;
+
+ // In the fullscreen case, window and screen coordinates are the same.
+ to->mX = from.mX;
+ to->mY = from.mY;
+ return (TRUE);
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+BOOL LLWindowSDL::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ return(convertCoords(from, &window_coord) && convertCoords(window_coord, to));
+}
+
+
+
+
+void LLWindowSDL::setupFailure(const char* text, const char* caption, U32 type)
+{
+ destroyContext();
+
+ OSMessageBox(text, caption, type);
+}
+
+BOOL LLWindowSDL::SDLReallyCaptureInput(BOOL capture)
+{
+ // note: this used to be safe to call nestedly, but in the
+ // end that's not really a wise usage pattern, so don't.
+
+ if (capture)
+ mReallyCapturedCount = 1;
+ else
+ mReallyCapturedCount = 0;
+
+ SDL_GrabMode wantmode, newmode;
+ if (mReallyCapturedCount <= 0) // uncapture
+ {
+ wantmode = SDL_GRAB_OFF;
+ } else // capture
+ {
+ wantmode = SDL_GRAB_ON;
+ }
+
+ if (mReallyCapturedCount < 0) // yuck, imbalance.
+ {
+ mReallyCapturedCount = 0;
+ llwarns << "ReallyCapture count was < 0" << llendl;
+ }
+
+ if (!mFullscreen) /* only bother if we're windowed anyway */
+ {
+#if LL_X11
+ if (SDL_Display)
+ {
+ /* we dirtily mix raw X11 with SDL so that our pointer
+ isn't (as often) constrained to the limits of the
+ window while grabbed, which feels nicer and
+ hopefully eliminates some reported 'sticky pointer'
+ problems. We use raw X11 instead of
+ SDL_WM_GrabInput() because the latter constrains
+ the pointer to the window and also steals all
+ *keyboard* input from the window manager, which was
+ frustrating users. */
+ int result;
+ if (wantmode == SDL_GRAB_ON)
+ {
+ //llinfos << "X11 POINTER GRABBY" << llendl;
+ //newmode = SDL_WM_GrabInput(wantmode);
+ result = XGrabPointer(SDL_Display, mSDL_XWindowID,
+ True, 0, GrabModeAsync,
+ GrabModeAsync,
+ None, None, CurrentTime);
+ if (GrabSuccess == result)
+ newmode = SDL_GRAB_ON;
+ else
+ newmode = SDL_GRAB_OFF;
+ } else if (wantmode == SDL_GRAB_OFF)
+ {
+ //llinfos << "X11 POINTER UNGRABBY" << llendl;
+ newmode = SDL_GRAB_OFF;
+ //newmode = SDL_WM_GrabInput(SDL_GRAB_OFF);
+
+ XUngrabPointer(SDL_Display, CurrentTime);
+ // Make sure the ungrab happens RIGHT NOW.
+ XSync(SDL_Display, False);
+ } else
+ {
+ newmode = SDL_GRAB_QUERY; // neutral
+ }
+ } else // not actually running on X11, for some reason
+ newmode = wantmode;
+#endif // LL_X11
+ } else {
+ // pretend we got what we wanted, when really we don't care.
+ newmode = wantmode;
+ }
+
+ // return boolean success for whether we ended up in the desired state
+ return (capture && SDL_GRAB_ON==newmode) ||
+ (!capture && SDL_GRAB_OFF==newmode);
+}
+
+U32 LLWindowSDL::SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain)
+{
+ /* part of the fix for SL-13243: Some popular window managers like
+ to totally eat alt-drag for the purposes of moving windows. We
+ spoil their day by acquiring the exclusive X11 mouse lock for as
+ long as LALT is held down, so the window manager can't easily
+ see what's happening. Tested successfully with Metacity.
+ And... do the same with CTRL, for other darn WMs. We don't
+ care about other metakeys as SL doesn't use them with dragging
+ (for now). */
+
+ /* We maintain a bitmap of critical keys which are up and down
+ instead of simply key-counting, because SDL sometimes reports
+ misbalanced keyup/keydown event pairs to us for whatever reason. */
+
+ U32 mask = 0;
+ switch (keysym)
+ {
+ case SDLK_LALT:
+ mask = 1U << 0; break;
+ case SDLK_LCTRL:
+ mask = 1U << 1; break;
+ case SDLK_RCTRL:
+ mask = 1U << 2; break;
+ default:
+ break;
+ }
+
+ if (gain)
+ mGrabbyKeyFlags |= mask;
+ else
+ mGrabbyKeyFlags &= ~mask;
+
+ //llinfos << "mGrabbyKeyFlags=" << mGrabbyKeyFlags << llendl;
+
+ /* 0 means we don't need to mousegrab, otherwise grab. */
+ return mGrabbyKeyFlags;
+}
+
+void LLWindowSDL::gatherInput()
+{
+ const Uint32 CLICK_THRESHOLD = 300; // milliseconds
+ static int leftClick = 0;
+ static int rightClick = 0;
+ static Uint32 lastLeftDown = 0;
+ static Uint32 lastRightDown = 0;
+ SDL_Event event;
+
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_MOUSEMOTION:
+ {
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ mCallbacks->handleMouseMove(this, openGlCoord, mask);
+ break;
+ }
+
+ case SDL_KEYDOWN:
+ gKeyboard->handleKeyDown(event.key.keysym.sym, event.key.keysym.mod);
+ // part of the fix for SL-13243
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, TRUE) != 0)
+ SDLReallyCaptureInput(TRUE);
+
+ if (event.key.keysym.unicode)
+ mCallbacks->handleUnicodeChar(event.key.keysym.unicode, gKeyboard->currentMask(FALSE));
+ break;
+
+ case SDL_KEYUP:
+ if (SDLCheckGrabbyKeys(event.key.keysym.sym, FALSE) == 0)
+ SDLReallyCaptureInput(FALSE); // part of the fix for SL-13243
+
+ // This is a testing hack to pop up a dialog when 4 is pressed
+ //if (event.key.keysym.sym == SDLK_4)
+ //OSMessageBox("a whole bunch of text goes right here, whee! test test test.", "this is the title!", OSMB_YESNO);
+
+ gKeyboard->handleKeyUp(event.key.keysym.sym, event.key.keysym.mod);
+ break;
+
+ case SDL_MOUSEBUTTONDOWN:
+ {
+ bool isDoubleClick = false;
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ if (event.button.button == SDL_BUTTON_LEFT) // SDL doesn't manage double clicking...
+ {
+ Uint32 now = SDL_GetTicks();
+ if ((now - lastLeftDown) > CLICK_THRESHOLD)
+ leftClick = 1;
+ else
+ {
+ if (++leftClick >= 2)
+ {
+ leftClick = 0;
+ isDoubleClick = true;
+ }
+ }
+ lastLeftDown = now;
+ }
+ else if (event.button.button == SDL_BUTTON_RIGHT)
+ {
+ Uint32 now = SDL_GetTicks();
+ if ((now - lastRightDown) > CLICK_THRESHOLD)
+ rightClick = 1;
+ else
+ {
+ if (++rightClick >= 2)
+ {
+ rightClick = 0;
+ isDoubleClick = true;
+ }
+ }
+ lastRightDown = now;
+ }
+
+ if (event.button.button == SDL_BUTTON_LEFT) // left
+ {
+ if (isDoubleClick)
+ mCallbacks->handleDoubleClick(this, openGlCoord, mask);
+ else
+ mCallbacks->handleMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ {
+ // right double click isn't handled right now in Second Life ... if (isDoubleClick)
+ mCallbacks->handleRightMouseDown(this, openGlCoord, mask);
+ }
+
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // Middle mouse isn't handled right now in Second Life ... mCallbacks->handleMiddleMouseDown(this, openGlCoord, mask);
+ else if (event.button.button == 4) // mousewheel up...thanks to X11 for making SDL consider these "buttons".
+ mCallbacks->handleScrollWheel(this, -1);
+ else if (event.button.button == 5) // mousewheel down...thanks to X11 for making SDL consider these "buttons".
+ mCallbacks->handleScrollWheel(this, 1);
+
+ break;
+ }
+
+ case SDL_MOUSEBUTTONUP:
+ {
+ LLCoordWindow winCoord(event.button.x, event.button.y);
+ LLCoordGL openGlCoord;
+ convertCoords(winCoord, &openGlCoord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ if (event.button.button == SDL_BUTTON_LEFT) // left
+ mCallbacks->handleMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_RIGHT) // right ... yes, it's 3, not 2, in SDL...
+ mCallbacks->handleRightMouseUp(this, openGlCoord, mask);
+ else if (event.button.button == SDL_BUTTON_MIDDLE) // middle
+ ; // UNUSED IN SECOND LIFE RIGHT NOW mCallbacks->handleMiddleMouseUp(this, openGlCoord, mask);
+
+ // don't handle mousewheel here...
+
+ break;
+ }
+
+ case SDL_VIDEOEXPOSE: // VIDEOEXPOSE doesn't specify the damage, but hey, it's OpenGL...repaint the whole thing!
+ mCallbacks->handlePaint(this, 0, 0, mWindow->w, mWindow->h);
+ break;
+
+ case SDL_VIDEORESIZE: // !!! FIXME: handle this?
+ llinfos << "Handling a resize event: " << event.resize.w <<
+ "x" << event.resize.h << llendl;
+
+ // !!! FIXME: I'm not sure this is necessary!
+ mWindow = SDL_SetVideoMode(event.resize.w, event.resize.h, 32, mSDLFlags);
+ if (!mWindow)
+ {
+ // FIXME: More informative dialog?
+ llinfos << "Could not recreate context after resize! Quitting..." << llendl;
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ }
+
+ mCallbacks->handleResize(this, event.resize.w, event.resize.h );
+ break;
+
+ case SDL_ACTIVEEVENT:
+ if (event.active.state & SDL_APPINPUTFOCUS)
+ {
+ // Note that for SDL (particularly on X11), keyboard
+ // and mouse focus are independent things. Here we are
+ // tracking keyboard focus state changes.
+
+ // We have to do our own state massaging because SDL
+ // can send us two unfocus events in a row for example,
+ // which confuses the focus code [SL-24071].
+ if (event.active.gain != mHaveInputFocus)
+ {
+ if (event.active.gain)
+ mCallbacks->handleFocus(this);
+ else
+ mCallbacks->handleFocusLost(this);
+
+ mHaveInputFocus = !!event.active.gain;
+ }
+ }
+ if (event.active.state & SDL_APPACTIVE)
+ {
+ // Change in iconification/minimization state.
+ if ((!event.active.gain) != mIsMinimized)
+ {
+ mCallbacks->handleActivate(this, !!event.active.gain);
+ llinfos << "SDL deiconification state switched to " << BOOL(event.active.gain) << llendl;
+
+ mIsMinimized = (!event.active.gain);
+ }
+ else
+ {
+ llinfos << "Ignored bogus redundant SDL deiconification state switch to " << BOOL(event.active.gain) << llendl;
+ }
+ }
+ break;
+
+ case SDL_QUIT:
+ if(mCallbacks->handleCloseRequest(this))
+ {
+ // Get the app to initiate cleanup.
+ mCallbacks->handleQuit(this);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ break;
+ default:
+ //llinfos << "Unhandled SDL event type " << event.type << llendl;
+ break;
+ }
+ }
+
+#if LL_X11
+ // This is a good time to stop flashing the icon if our mFlashTimer has
+ // expired.
+ if (mFlashing && mFlashTimer.hasExpired())
+ {
+ x11_set_urgent(FALSE);
+ mFlashing = FALSE;
+ }
+#endif // LL_X11
+}
+
+static SDL_Cursor *makeSDLCursorFromBMP(const char *filename, int hotx, int hoty)
+{
+ SDL_Cursor *sdlcursor = NULL;
+ SDL_Surface *bmpsurface;
+
+ // Load cursor pixel data from BMP file
+ bmpsurface = Load_BMP_Resource(filename);
+ if (bmpsurface && bmpsurface->w%8==0)
+ {
+ SDL_Surface *cursurface;
+ llinfos << "Loaded cursor file " << filename << " "
+ << bmpsurface->w << "x" << bmpsurface->h << llendl;
+ cursurface = SDL_CreateRGBSurface (SDL_SWSURFACE,
+ bmpsurface->w,
+ bmpsurface->h,
+ 32,
+ 0xFFU,
+ 0xFF00U,
+ 0xFF0000U,
+ 0xFF000000U);
+ SDL_FillRect(cursurface, NULL, 0x00000000U);
+
+ // Blit the cursor pixel data onto a 32-bit RGBA surface so we
+ // only have to cope with processing one type of pixel format.
+ if (0 == SDL_BlitSurface(bmpsurface, NULL,
+ cursurface, NULL))
+ {
+ // n.b. we already checked that width is a multiple of 8.
+ const int bitmap_bytes = (cursurface->w * cursurface->h) / 8;
+ unsigned char *cursor_data = new unsigned char[bitmap_bytes];
+ unsigned char *cursor_mask = new unsigned char[bitmap_bytes];
+ memset(cursor_data, 0, bitmap_bytes);
+ memset(cursor_mask, 0, bitmap_bytes);
+ int i,j;
+ // Walk the RGBA cursor pixel data, extracting both data and
+ // mask to build SDL-friendly cursor bitmaps from. The mask
+ // is inferred by color-keying against 200,200,200
+ for (i=0; i<cursurface->h; ++i) {
+ for (j=0; j<cursurface->w; ++j) {
+ unsigned char *pixelp =
+ ((unsigned char *)cursurface->pixels)
+ + cursurface->pitch * i
+ + j*cursurface->format->BytesPerPixel;
+ unsigned char srcred = pixelp[0];
+ unsigned char srcgreen = pixelp[1];
+ unsigned char srcblue = pixelp[2];
+ BOOL mask_bit = (srcred != 200)
+ || (srcgreen != 200)
+ || (srcblue != 200);
+ BOOL data_bit = mask_bit && (srcgreen <= 80);//not 0x80
+ unsigned char bit_offset = (cursurface->w/8) * i
+ + j/8;
+ cursor_data[bit_offset] |= (data_bit) << (7 - (j&7));
+ cursor_mask[bit_offset] |= (mask_bit) << (7 - (j&7));
+ }
+ }
+ sdlcursor = SDL_CreateCursor((Uint8*)cursor_data,
+ (Uint8*)cursor_mask,
+ cursurface->w, cursurface->h,
+ hotx, hoty);
+ delete[] cursor_data;
+ delete[] cursor_mask;
+ } else {
+ llwarns << "CURSOR BLIT FAILURE, cursurface: " << cursurface << llendl;
+ }
+ SDL_FreeSurface(cursurface);
+ SDL_FreeSurface(bmpsurface);
+ } else {
+ llwarns << "CURSOR LOAD FAILURE " << filename << llendl;
+ }
+
+ return sdlcursor;
+}
+
+void LLWindowSDL::setCursor(ECursorType cursor)
+{
+ if (mCurrentCursor != cursor)
+ {
+ if (cursor < UI_CURSOR_COUNT)
+ {
+ SDL_Cursor *sdlcursor = mSDLCursors[cursor];
+ // Try to default to the arrow for any cursors that
+ // did not load correctly.
+ if (!sdlcursor && mSDLCursors[UI_CURSOR_ARROW])
+ sdlcursor = mSDLCursors[UI_CURSOR_ARROW];
+ if (sdlcursor)
+ SDL_SetCursor(sdlcursor);
+ } else {
+ llwarns << "Tried to set invalid cursor number " << cursor << llendl;
+ }
+ mCurrentCursor = cursor;
+ }
+}
+
+ECursorType LLWindowSDL::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowSDL::initCursors()
+{
+ int i;
+ // Blank the cursor pointer array for those we may miss.
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ mSDLCursors[i] = NULL;
+ }
+ // Pre-make an SDL cursor for each of the known cursor types.
+ // We hardcode the hotspots - to avoid that we'd have to write
+ // a .cur file loader.
+ // NOTE: SDL doesn't load RLE-compressed BMP files.
+ mSDLCursors[UI_CURSOR_ARROW] = makeSDLCursorFromBMP("llarrow.BMP",0,0);
+ mSDLCursors[UI_CURSOR_WAIT] = makeSDLCursorFromBMP("wait.BMP",12,15);
+ mSDLCursors[UI_CURSOR_HAND] = makeSDLCursorFromBMP("hand.BMP",7,10);
+ mSDLCursors[UI_CURSOR_IBEAM] = makeSDLCursorFromBMP("ibeam.BMP",15,16);
+ mSDLCursors[UI_CURSOR_CROSS] = makeSDLCursorFromBMP("cross.BMP",16,14);
+ mSDLCursors[UI_CURSOR_SIZENWSE] = makeSDLCursorFromBMP("sizenwse.BMP",14,17);
+ mSDLCursors[UI_CURSOR_SIZENESW] = makeSDLCursorFromBMP("sizenesw.BMP",17,17);
+ mSDLCursors[UI_CURSOR_SIZEWE] = makeSDLCursorFromBMP("sizewe.BMP",16,14);
+ mSDLCursors[UI_CURSOR_SIZENS] = makeSDLCursorFromBMP("sizens.BMP",17,16);
+ mSDLCursors[UI_CURSOR_NO] = makeSDLCursorFromBMP("llno.BMP",8,8);
+ mSDLCursors[UI_CURSOR_WORKING] = makeSDLCursorFromBMP("working.BMP",12,15);
+ mSDLCursors[UI_CURSOR_TOOLGRAB] = makeSDLCursorFromBMP("lltoolgrab.BMP",2,13);
+ mSDLCursors[UI_CURSOR_TOOLLAND] = makeSDLCursorFromBMP("lltoolland.BMP",1,6);
+ mSDLCursors[UI_CURSOR_TOOLFOCUS] = makeSDLCursorFromBMP("lltoolfocus.BMP",8,5);
+ mSDLCursors[UI_CURSOR_TOOLCREATE] = makeSDLCursorFromBMP("lltoolcreate.BMP",7,7);
+ mSDLCursors[UI_CURSOR_ARROWDRAG] = makeSDLCursorFromBMP("arrowdrag.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPY] = makeSDLCursorFromBMP("arrowcop.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWDRAGMULTI] = makeSDLCursorFromBMP("llarrowdragmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_ARROWCOPYMULTI] = makeSDLCursorFromBMP("arrowcopmulti.BMP",0,0);
+ mSDLCursors[UI_CURSOR_NOLOCKED] = makeSDLCursorFromBMP("llnolocked.BMP",8,8);
+ mSDLCursors[UI_CURSOR_ARROWLOCKED] = makeSDLCursorFromBMP("llarrowlocked.BMP",0,0);
+ mSDLCursors[UI_CURSOR_GRABLOCKED] = makeSDLCursorFromBMP("llgrablocked.BMP",2,13);
+ mSDLCursors[UI_CURSOR_TOOLTRANSLATE] = makeSDLCursorFromBMP("lltooltranslate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLROTATE] = makeSDLCursorFromBMP("lltoolrotate.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSCALE] = makeSDLCursorFromBMP("lltoolscale.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLCAMERA] = makeSDLCursorFromBMP("lltoolcamera.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPAN] = makeSDLCursorFromBMP("lltoolpan.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLZOOMIN] = makeSDLCursorFromBMP("lltoolzoomin.BMP",7,5);
+ mSDLCursors[UI_CURSOR_TOOLPICKOBJECT3] = makeSDLCursorFromBMP("toolpickobject3.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLSIT] = makeSDLCursorFromBMP("toolsit.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLBUY] = makeSDLCursorFromBMP("toolbuy.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLPAY] = makeSDLCursorFromBMP("toolpay.BMP",0,0);
+ mSDLCursors[UI_CURSOR_TOOLOPEN] = makeSDLCursorFromBMP("toolopen.BMP",0,0);
+ mSDLCursors[UI_CURSOR_PIPETTE] = makeSDLCursorFromBMP("lltoolpipette.BMP",2,28);
+}
+
+void LLWindowSDL::quitCursors()
+{
+ int i;
+ if (mWindow)
+ {
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ {
+ if (mSDLCursors[i])
+ {
+ SDL_FreeCursor(mSDLCursors[i]);
+ mSDLCursors[i] = NULL;
+ }
+ }
+ } else {
+ // SDL doesn't refcount cursors, so if the window has
+ // already been destroyed then the cursors have gone with it.
+ llinfos << "Skipping quitCursors: mWindow already gone." << llendl;
+ for (i=0; i<UI_CURSOR_COUNT; ++i)
+ mSDLCursors[i] = NULL;
+ }
+}
+
+void LLWindowSDL::captureMouse()
+{
+ // SDL already enforces the semantics that captureMouse is
+ // used for, i.e. that we continue to get mouse events as long
+ // as a button is down regardless of whether we left the
+ // window, and in a less obnoxious way than SDL_WM_GrabInput
+ // which would confine the cursor to the window too.
+
+ //llinfos << "LLWindowSDL::captureMouse" << llendl;
+}
+
+void LLWindowSDL::releaseMouse()
+{
+ // see LWindowSDL::captureMouse()
+
+ //llinfos << "LLWindowSDL::releaseMouse" << llendl;
+}
+
+void LLWindowSDL::hideCursor()
+{
+ if(!mCursorHidden)
+ {
+ // llinfos << "hideCursor: hiding" << llendl;
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+ SDL_ShowCursor(0);
+ }
+ else
+ {
+ // llinfos << "hideCursor: already hidden" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursor()
+{
+ if(mCursorHidden)
+ {
+ // llinfos << "showCursor: showing" << llendl;
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+ SDL_ShowCursor(1);
+ }
+ else
+ {
+ // llinfos << "showCursor: already visible" << llendl;
+ }
+
+ adjustCursorDecouple();
+}
+
+void LLWindowSDL::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowSDL::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+
+
+//
+// LLSplashScreenSDL
+//
+LLSplashScreenSDL::LLSplashScreenSDL()
+{
+}
+
+LLSplashScreenSDL::~LLSplashScreenSDL()
+{
+}
+
+void LLSplashScreenSDL::showImpl()
+{
+}
+
+void LLSplashScreenSDL::updateImpl(const char* mesg)
+{
+}
+
+
+void LLSplashScreenSDL::hideImpl()
+{
+}
+
+
+
+#if LL_GTK
+static void response_callback (GtkDialog *dialog,
+ gint arg1,
+ gpointer user_data)
+{
+ gint *response = (gint*)user_data;
+ *response = arg1;
+ gtk_widget_destroy(GTK_WIDGET(dialog));
+ gtk_main_quit();
+}
+
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ S32 rtn = OSBTN_CANCEL;
+
+#if LL_GTK
+ maybe_do_gtk_diagnostics();
+#endif // LL_GTK
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && ((NULL==gWindowImplementation) || (!was_fullscreen))
+ )
+ {
+ GtkWidget *win = NULL;
+
+ llinfos << "Creating a dialog because we're in windowed mode and GTK is happy." << llendl;
+
+ GtkDialogFlags flags = GTK_DIALOG_MODAL;
+ GtkMessageType messagetype;
+ GtkButtonsType buttons;
+ switch (type)
+ {
+ default:
+ case OSMB_OK:
+ messagetype = GTK_MESSAGE_WARNING;
+ buttons = GTK_BUTTONS_OK;
+ break;
+ case OSMB_OKCANCEL:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_OK_CANCEL;
+ break;
+ case OSMB_YESNO:
+ messagetype = GTK_MESSAGE_QUESTION;
+ buttons = GTK_BUTTONS_YES_NO;
+ break;
+ }
+ win = gtk_message_dialog_new(NULL,
+ flags, messagetype, buttons,
+ text);
+
+# if LL_X11
+ // Make GTK tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ gtk_window_set_position(GTK_WINDOW(win),
+ GTK_WIN_POS_CENTER_ON_PARENT);
+
+ gtk_window_set_type_hint(GTK_WINDOW(win),
+ GDK_WINDOW_TYPE_HINT_DIALOG);
+
+ if (caption)
+ gtk_window_set_title(GTK_WINDOW(win), caption);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ // we should be able to us a gtk_dialog_run(), but it's
+ // apparently not written to exist in a world without a higher
+ // gtk_main(), so we manage its signal/destruction outselves.
+ gtk_widget_show_all (win);
+ gtk_main();
+
+ //llinfos << "response: " << response << llendl;
+ switch (response)
+ {
+ case GTK_RESPONSE_OK: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_YES: rtn = OSBTN_YES; break;
+ case GTK_RESPONSE_NO: rtn = OSBTN_NO; break;
+ case GTK_RESPONSE_APPLY: rtn = OSBTN_OK; break;
+ case GTK_RESPONSE_NONE:
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_CLOSE:
+ case GTK_RESPONSE_DELETE_EVENT:
+ default: rtn = OSBTN_CANCEL;
+ }
+ }
+ else
+ {
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ llinfos << "Skipping dialog because we're in fullscreen mode or GTK is not happy." << llendl;
+ rtn = OSBTN_OK;
+ }
+
+ if(gWindowImplementation != NULL)
+ gWindowImplementation->afterDialog();
+
+ return rtn;
+}
+
+static void color_changed_callback(GtkWidget *widget,
+ gpointer user_data)
+{
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION(widget);
+ GdkColor *colorp = (GdkColor*)user_data;
+
+ gtk_color_selection_get_current_color(colorsel, colorp);
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ BOOL rtn = FALSE;
+
+ beforeDialog();
+
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL)
+ // We can NOT expect to combine GTK and SDL's aggressive fullscreen
+ && !was_fullscreen
+ )
+ {
+ GtkWidget *win = NULL;
+
+ win = gtk_color_selection_dialog_new(NULL);
+
+# if LL_X11
+ // Get GTK to tell the window manager to associate this
+ // dialog with our non-GTK SDL window, which should try
+ // to keep it on top etc.
+ if (SDL_XWindowID != None)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(SDL_XWindowID);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+# endif //LL_X11
+
+ GtkColorSelection *colorsel = GTK_COLOR_SELECTION (GTK_COLOR_SELECTION_DIALOG(win)->colorsel);
+
+ GdkColor color, orig_color;
+ orig_color.red = guint16(65535 * *r);
+ orig_color.green= guint16(65535 * *g);
+ orig_color.blue = guint16(65535 * *b);
+ color = orig_color;
+
+ gtk_color_selection_set_previous_color (colorsel, &color);
+ gtk_color_selection_set_current_color (colorsel, &color);
+ gtk_color_selection_set_has_palette (colorsel, TRUE);
+ gtk_color_selection_set_has_opacity_control(colorsel, FALSE);
+
+ gint response = GTK_RESPONSE_NONE;
+ g_signal_connect (win,
+ "response",
+ G_CALLBACK (response_callback),
+ &response);
+
+ g_signal_connect (G_OBJECT (colorsel), "color_changed",
+ G_CALLBACK (color_changed_callback),
+ &color);
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+ gtk_widget_show_all(win);
+ // hide the help button - we don't service it.
+ gtk_widget_hide(GTK_COLOR_SELECTION_DIALOG(win)->help_button);
+ gtk_main();
+
+ if (response == GTK_RESPONSE_OK &&
+ (orig_color.red != color.red
+ || orig_color.green != color.green
+ || orig_color.blue != color.blue) )
+ {
+ *r = color.red / 65535.0f;
+ *g = color.green / 65535.0f;
+ *b = color.blue / 65535.0f;
+ rtn = TRUE;
+ }
+ }
+
+ afterDialog();
+
+ return rtn;
+}
+#else
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type)
+{
+ fprintf(stderr, "MSGBOX: %s: %s\n", caption, text);
+ return 0;
+}
+
+BOOL LLWindowSDL::dialog_color_picker ( F32 *r, F32 *g, F32 *b)
+{
+ return (FALSE);
+}
+#endif // LL_GTK
+
+// Open a URL with the user's default web browser.
+// Must begin with protocol identifier.
+void spawn_web_browser(const char* escaped_url)
+{
+ llinfos << "spawn_web_browser: " << escaped_url << llendl;
+
+#if LL_LINUX
+# if LL_X11
+ if (SDL_Display) // Just in case - before forking.
+ XSync(SDL_Display, False);
+# endif // LL_X11
+
+ std::string cmd;
+ cmd = gDirUtilp->getAppRODataDir().c_str();
+ cmd += gDirUtilp->getDirDelimiter().c_str();
+ cmd += "launch_url.sh";
+ char* const argv[] = {(char*)cmd.c_str(), (char*)escaped_url, NULL};
+
+ pid_t pid = fork();
+ if (pid == 0)
+ { // child
+ // disconnect from stdin/stdout/stderr, or child will
+ // keep our output pipe undesirably alive if it outlives us.
+ close(0);
+ close(1);
+ close(2);
+ // end ourself by running the command
+ execv(cmd.c_str(), argv);
+ // if execv returns at all, there was a problem.
+ llwarns << "execv failure when trying to start " << cmd << llendl;
+ _exit(1); // _exit because we don't want atexit() clean-up!
+ } else {
+ if (pid > 0)
+ {
+ // parent - wait for child to die
+ int childExitStatus;
+ waitpid(pid, &childExitStatus, 0);
+ } else {
+ llwarns << "fork failure." << llendl;
+ }
+ }
+#endif // LL_LINUX
+
+ llinfos << "spawn_web_browser returning." << llendl;
+}
+
+void shell_open( const char* file_path )
+{
+ // !!! FIXME:
+ fprintf(stderr, "shell_open: %s\n", file_path);
+}
+
+void *LLWindowSDL::getPlatformWindow()
+{
+#if LL_X11
+ // pointer to our static raw X window
+ return (void*)&SDL_XWindowID;
+#else
+ // doubt we really want to return a high-level SDL structure here.
+ return NULL;
+#endif
+}
+
+void LLWindowSDL::bringToFront()
+{
+ // !!! FIXME:
+ fprintf(stderr, "bringToFront\n");
+}
+
+#endif // LL_SDL
diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h
new file mode 100644
index 0000000000..704262061a
--- /dev/null
+++ b/indra/llwindow/llwindowsdl.h
@@ -0,0 +1,198 @@
+/**
+ * @file llwindowsdl.h
+ * @brief SDL implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWSDL_H
+#define LL_LLWINDOWSDL_H
+
+// Simple Directmedia Layer (http://libsdl.org/) implementation of LLWindow class
+
+#include "llwindow.h"
+
+#include "SDL/SDL.h"
+
+#if LL_X11
+// get X11-specific headers for use in low-level stuff like copy-and-paste support
+#include "SDL/SDL_syswm.h"
+#endif
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+
+class LLWindowSDL : public LLWindow
+{
+public:
+ /*virtual*/ void show();
+ /*virtual*/ void hide();
+ /*virtual*/ void close();
+ /*virtual*/ BOOL getVisible();
+ /*virtual*/ BOOL getMinimized();
+ /*virtual*/ BOOL getMaximized();
+ /*virtual*/ BOOL maximize();
+ /*virtual*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*virtual*/ void captureMouse();
+ /*virtual*/ void releaseMouse();
+ /*virtual*/ void setMouseClipping( BOOL b );
+ /*virtual*/ BOOL isClipboardTextAvailable();
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst);
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString & src);
+ /*virtual*/ void flashIcon(F32 seconds);
+ /*virtual*/ F32 getGamma();
+ /*virtual*/ BOOL setGamma(const F32 gamma); // Set the gamma
+ /*virtual*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+ /*virtual*/ void delayInputProcessing() { };
+
+ // handy coordinate space conversion routines
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to);
+
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
+ /*virtual*/ F32 getNativeAspectRatio();
+ /*virtual*/ F32 getPixelAspectRatio();
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
+
+ /*virtual*/ void beforeDialog();
+ /*virtual*/ void afterDialog();
+
+ /*virtual*/ BOOL dialog_color_picker(F32 *r, F32 *g, F32 *b);
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront();
+
+protected:
+ LLWindowSDL(
+ char *title, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowSDL();
+
+ void initCursors();
+ void quitCursors();
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ // create or re-create the GL context/window. Called from the constructor and switchContext().
+ BOOL createContext(int x, int y, int width, int height, int bits, BOOL fullscreen, BOOL disable_vsync);
+ void destroyContext();
+ void setupFailure(const char* text, const char* caption, U32 type);
+ void adjustCursorDecouple(bool warpingMouse = false);
+ void fixWindowSize(void);
+ U32 SDLCheckGrabbyKeys(SDLKey keysym, BOOL gain);
+ BOOL SDLReallyCaptureInput(BOOL capture);
+
+ //
+ // Platform specific variables
+ //
+ U32 mGrabbyKeyFlags;
+ int mReallyCapturedCount;
+ SDL_Surface * mWindow;
+ char * mWindowTitle;
+ double mOriginalAspectRatio;
+ BOOL mCursorDecoupled;
+ S32 mCursorLastEventDeltaX;
+ S32 mCursorLastEventDeltaY;
+ BOOL mCursorIgnoreNextDelta;
+ BOOL mNeedsResize; // Constructor figured out the window is too big, it needs a resize.
+ LLCoordScreen mNeedsResizeSize;
+ F32 mOverrideAspectRatio;
+ F32 mGamma;
+
+ int mSDLFlags;
+
+ SDL_Cursor* mSDLCursors[UI_CURSOR_COUNT];
+ int mHaveInputFocus; /* 0=no, 1=yes, else unknown */
+ int mIsMinimized; /* 0=no, 1=yes, else unknown */
+
+ friend class LLWindowManager;
+
+#if LL_X11
+private:
+ // These are set up by the X11 clipboard initialization code
+ Window mSDL_XWindowID;
+ void (*Lock_Display)(void);
+ void (*Unlock_Display)(void);
+ // more X11 clipboard stuff
+ int init_x11clipboard(void);
+ void quit_x11clipboard(void);
+ int is_empty_x11clipboard(void);
+ void put_x11clipboard(int type, int srclen, const char *src);
+ void get_x11clipboard(int type, int *dstlen, char **dst);
+ void x11_set_urgent(BOOL urgent);
+ BOOL mFlashing;
+ LLTimer mFlashTimer;
+#endif //LL_X11
+
+
+};
+
+
+class LLSplashScreenSDL : public LLSplashScreen
+{
+public:
+ LLSplashScreenSDL();
+ virtual ~LLSplashScreenSDL();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+};
+
+S32 OSMessageBoxSDL(const char* text, const char* caption, U32 type);
+
+void load_url_external(const char* url);
+void shell_open( const char* file_path );
+
+#endif //LL_LLWINDOWSDL_H
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
new file mode 100644
index 0000000000..ad56b97577
--- /dev/null
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -0,0 +1,3247 @@
+/**
+ * @file llwindowwin32.cpp
+ * @brief Platform-dependent implementation of llwindow
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#if LL_WINDOWS && !LL_MESA_HEADLESS
+
+#include <commdlg.h>
+#include <WinUser.h>
+#include <mapi.h>
+#include <process.h> // for _spawn
+#include <shellapi.h>
+
+// Require DirectInput version 8
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+#include "llwindowwin32.h"
+#include "llkeyboardwin32.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llstring.h"
+#include "lldir.h"
+
+#include "llglheaders.h"
+
+#include "indra_constants.h"
+
+// culled from winuser.h
+const S32 WM_MOUSEWHEEL = 0x020A;
+const S32 WHEEL_DELTA = 120; /* Value for rolling one detent */
+const S32 MAX_MESSAGE_PER_UPDATE = 20;
+const S32 BITS_PER_PIXEL = 32;
+const S32 MAX_NUM_RESOLUTIONS = 32;
+const F32 ICON_FLASH_TIME = 0.5f;
+
+extern BOOL gDebugWindowProc;
+
+LPWSTR gIconResource = IDI_APPLICATION;
+
+LLW32MsgCallback gAsyncMsgCallback = NULL;
+
+//
+// LLWindowWin32
+//
+
+void show_window_creation_error(const char* title)
+{
+ llwarns << title << llendl;
+ shell_open( "help/window_creation_error.html");
+ /*
+ OSMessageBox(
+ "Second Life is unable to run because it can't set up your display.\n"
+ "We need to be able to make a 32-bit color window at 1024x768, with\n"
+ "an 8 bit alpha channel.\n"
+ "\n"
+ "First, be sure your monitor is set to True Color (32-bit) in\n"
+ "Start -> Control Panels -> Display -> Settings.\n"
+ "\n"
+ "Otherwise, this may be due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "ATI drivers are available at http://www.ati.com/\n"
+ "nVidia drivers are available at http://www.nvidia.com/\n"
+ "\n"
+ "If you continue to receive this message, contact customer service.",
+ title,
+ OSMB_OK);
+ */
+}
+
+BOOL check_for_card(const char* RENDERER, const char* bad_card)
+{
+ if (!strnicmp(RENDERER, bad_card, strlen(bad_card)))
+ {
+ char buffer[1024];
+ sprintf(buffer,
+ "Your video card appears to be a %s, which Second Life does not support.\n"
+ "\n"
+ "Second Life requires a video card with 32 Mb of memory or more, as well as\n"
+ "multitexture support. We explicitly support nVidia GeForce 2 or better, \n"
+ "and ATI Radeon 8500 or better.\n"
+ "\n"
+ "If you own a supported card and continue to receive this message, try \n"
+ "updating to the latest video card drivers. Otherwise look in the\n"
+ "secondlife.com support section or e-mail technical support\n"
+ "\n"
+ "You can try to run Second Life, but it will probably crash or run\n"
+ "very slowly. Try anyway?",
+ bad_card);
+ S32 button = OSMessageBox(buffer, "Unsupported video card", OSMB_YESNO);
+ if (OSBTN_YES == button)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+//static
+BOOL LLWindowWin32::sIsClassRegistered = FALSE;
+
+
+
+LPDIRECTINPUT8 g_pDI = NULL;
+LPDIRECTINPUTDEVICE8 g_pJoystick = NULL;
+BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
+ VOID* pContext );
+BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
+ VOID* pContext );
+
+
+LLWindowWin32::LLWindowWin32(char *title, char *name, S32 x, S32 y, S32 width,
+ S32 height, U32 flags,
+ BOOL fullscreen, BOOL clearBg,
+ BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth)
+ : LLWindow(fullscreen, flags)
+{
+ mIconResource = gIconResource;
+ mOverrideAspectRatio = 0.f;
+ mNativeAspectRatio = 0.f;
+ mMousePositionModified = FALSE;
+ mInputProcessingPaused = FALSE;
+
+ // Initialize the keyboard
+ gKeyboard = new LLKeyboardWin32();
+
+ GLuint pixel_format;
+ WNDCLASS wc;
+ DWORD dw_ex_style;
+ DWORD dw_style;
+ RECT window_rect;
+
+ // Set the window title
+ if (!title)
+ {
+ mWindowTitle = new WCHAR[50];
+ wsprintf(mWindowTitle, L"OpenGL Window");
+ }
+ else
+ {
+ mWindowTitle = new WCHAR[256]; // Assume title length < 255 chars.
+ mbstowcs(mWindowTitle, title, 255);
+ mWindowTitle[255] = 0;
+ }
+
+ // Set the window class name
+ if (!name)
+ {
+ mWindowClassName = new WCHAR[50];
+ wsprintf(mWindowClassName, L"OpenGL Window");
+ }
+ else
+ {
+ mWindowClassName = new WCHAR[256]; // Assume title length < 255 chars.
+ mbstowcs(mWindowClassName, name, 255);
+ mWindowClassName[255] = 0;
+ }
+
+
+ // We're not clipping yet
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+
+ // Make an instance of our window then define the window class
+ mhInstance = GetModuleHandle(NULL);
+ mWndProc = NULL;
+
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+
+ // No WPARAM yet.
+ mLastSizeWParam = 0;
+
+ // Windows GDI rects don't include rightmost pixel
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width;
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+
+ // Grab screen size to sanitize the window
+ S32 window_border_y = GetSystemMetrics(SM_CYBORDER);
+ S32 virtual_screen_x = GetSystemMetrics(SM_XVIRTUALSCREEN);
+ S32 virtual_screen_y = GetSystemMetrics(SM_YVIRTUALSCREEN);
+ S32 virtual_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
+ S32 virtual_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
+
+ if (x < virtual_screen_x) x = virtual_screen_x;
+ if (y < virtual_screen_y - window_border_y) y = virtual_screen_y - window_border_y;
+
+ if (x + width > virtual_screen_x + virtual_screen_width) x = virtual_screen_x + virtual_screen_width - width;
+ if (y + height > virtual_screen_y + virtual_screen_height) y = virtual_screen_y + virtual_screen_height - height;
+
+ if (!sIsClassRegistered)
+ {
+ // Force redraw when resized and create a private device context
+
+ // Makes double click messages.
+ wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
+
+ // Set message handler function
+ wc.lpfnWndProc = (WNDPROC) mainWindowProc;
+
+ // unused
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+
+ wc.hInstance = mhInstance;
+ wc.hIcon = LoadIcon(mhInstance, mIconResource);
+
+ // We will set the cursor ourselves
+ wc.hCursor = NULL;
+
+ // background color is not used
+ if (clearBg)
+ {
+ wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ }
+ else
+ {
+ wc.hbrBackground = (HBRUSH) NULL;
+ }
+
+ // we don't use windows menus
+ wc.lpszMenuName = NULL;
+
+ wc.lpszClassName = mWindowClassName;
+
+ if (!RegisterClass(&wc))
+ {
+ OSMessageBox("RegisterClass failed", "Error", OSMB_OK);
+ return;
+ }
+ sIsClassRegistered = TRUE;
+ }
+
+ //-----------------------------------------------------------------------
+ // Get the current refresh rate
+ //-----------------------------------------------------------------------
+
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ current_refresh = dev_mode.dmDisplayFrequency;
+ mNativeAspectRatio = ((F32)dev_mode.dmPelsWidth) / ((F32)dev_mode.dmPelsHeight);
+ }
+ else
+ {
+ current_refresh = 60;
+ }
+
+ //-----------------------------------------------------------------------
+ // Drop resolution and go fullscreen
+ // use a display mode with our desired size and depth, with a refresh
+ // rate as close at possible to the users' default
+ //-----------------------------------------------------------------------
+ if (mFullscreen)
+ {
+ BOOL success = FALSE;
+ DWORD closest_refresh = 0;
+
+ for (S32 mode_num = 0;; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ {
+ success = TRUE;
+ if ((dev_mode.dmDisplayFrequency - current_refresh)
+ < (closest_refresh - current_refresh))
+ {
+ closest_refresh = dev_mode.dmDisplayFrequency;
+ }
+ }
+ }
+
+ if (closest_refresh == 0)
+ {
+ llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl;
+ success = FALSE;
+ }
+
+ // If we found a good resolution, use it.
+ if (success)
+ {
+ success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ }
+
+ // Keep a copy of the actual current device mode in case we minimize
+ // and change the screen resolution. JC
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ // If it failed, we don't want to run fullscreen
+ if (success)
+ {
+ mFullscreen = TRUE;
+ mFullscreenWidth = dev_mode.dmPelsWidth;
+ mFullscreenHeight = dev_mode.dmPelsHeight;
+ mFullscreenBits = dev_mode.dmBitsPerPel;
+ mFullscreenRefresh = dev_mode.dmDisplayFrequency;
+
+ llinfos << "Running at " << dev_mode.dmPelsWidth
+ << "x" << dev_mode.dmPelsHeight
+ << "x" << dev_mode.dmBitsPerPel
+ << " @ " << dev_mode.dmDisplayFrequency
+ << llendl;
+ }
+ else
+ {
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ char error[256];
+ sprintf(error, "Unable to run fullscreen at %d x %d.\nRunning in window.", width, height);
+ OSMessageBox(error, "Error", OSMB_OK);
+ }
+ }
+
+ //-----------------------------------------------------------------------
+ // Resize window to account for borders
+ //-----------------------------------------------------------------------
+ if (mFullscreen)
+ {
+ dw_ex_style = WS_EX_APPWINDOW;
+ dw_style = WS_POPUP;
+
+ // Move window borders out not to cover window contents
+ AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
+ }
+ else
+ {
+ // Window with an edge
+ dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ dw_style = WS_OVERLAPPEDWINDOW;
+ }
+
+ //-----------------------------------------------------------------------
+ // Create the window
+ // Microsoft help indicates that GL windows must be created with
+ // WS_CLIPSIBLINGS and WS_CLIPCHILDREN, but not CS_PARENTDC
+ //-----------------------------------------------------------------------
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ x, // x pos
+ y, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!mWindowHandle)
+ {
+ DestroyWindow(mWindowHandle);
+ OSMessageBox("Window creation error", "Error", OSMB_OK);
+ return;
+ }
+
+ // TODO: add this after resolving _WIN32_WINNT issue
+ // if (!fullscreen)
+ // {
+ // TRACKMOUSEEVENT track_mouse_event;
+ // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
+ // track_mouse_event.dwFlags = TME_LEAVE;
+ // track_mouse_event.hwndTrack = mWindowHandle;
+ // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
+ // TrackMouseEvent( &track_mouse_event );
+ // }
+
+
+
+ S32 pfdflags = PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
+ if (use_gl)
+ {
+ pfdflags |= PFD_SUPPORT_OPENGL;
+ }
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+ PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ pfdflags,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
+ 8, // alpha bits
+ 0, // alpha shift
+ 0, // accum bits
+ 0, 0, 0, 0, // accum RGBA
+ 24, // depth bits
+ 8, // stencil bits, avi added for stencil test
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
+ {
+ close();
+ OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return;
+ }
+
+ // sanity check pfd returned by Windows
+ if (!ignore_pixel_depth && (pfd.cColorBits < 32))
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!ignore_pixel_depth && (pfd.cAlphaBits < 8))
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ if (use_gl)
+ {
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ // Check for some explicitly unsupported cards.
+ const char* RENDERER = (const char*) glGetString(GL_RENDERER);
+
+ const char* CARD_LIST[] =
+ { "RAGE 128",
+ "RIVA TNT2",
+ "Intel 810",
+ "3Dfx/Voodoo3",
+ "Radeon 7000",
+ "Radeon 7200",
+ "Radeon 7500",
+ "Radeon DDR",
+ "Radeon VE",
+ "GDI Generic" };
+ const S32 CARD_COUNT = sizeof(CARD_LIST)/sizeof(char*);
+
+ // Future candidates:
+ // ProSavage/Twister
+ // SuperSavage
+
+ S32 i;
+ for (i = 0; i < CARD_COUNT; i++)
+ {
+ if (check_for_card(RENDERER, CARD_LIST[i]))
+ {
+ close();
+ shell_open( "help/unsupported_card.html" );
+ return;
+ }
+ }
+
+ gGLManager.initWGL();
+
+ if (gGLManager.mHasWGLARBPixelFormat && (wglChoosePixelFormatARB != NULL))
+ {
+ // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
+ // can get exactly what we want.
+ GLint attrib_list[256];
+ S32 cur_attrib = 0;
+
+ attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
+ attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
+
+ attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_RED_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ // End the list
+ attrib_list[cur_attrib++] = 0;
+
+ GLint pixel_formats[256];
+ U32 num_formats = 0;
+
+ // First we try and get a 32 bit depth pixel format
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit");
+ return;
+ }
+
+ if (!num_formats)
+ {
+ llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl;
+ // Try 24-bit format
+ attrib_list[1] = 24;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit");
+ return;
+ }
+
+ if (!num_formats)
+ {
+ llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl;
+ attrib_list[1] = 16;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result || !num_formats)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit");
+ return;
+ }
+ }
+
+ llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl;
+
+ pixel_format = pixel_formats[0];
+ }
+
+ DestroyWindow(mWindowHandle);
+
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ x, // x pos
+ y, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return;
+ }
+
+ int swap_method = 0;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
+
+ if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
+ {
+ switch (swap_method)
+ {
+ case WGL_SWAP_EXCHANGE_ARB:
+ mSwapMethod = SWAP_METHOD_EXCHANGE;
+ llinfos << "Swap Method: Exchange" << llendl;
+ break;
+ case WGL_SWAP_COPY_ARB:
+ mSwapMethod = SWAP_METHOD_COPY;
+ llinfos << "Swap Method: Copy" << llendl;
+ break;
+ case WGL_SWAP_UNDEFINED_ARB:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Undefined" << llendl;
+ break;
+ default:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Unknown" << llendl;
+ break;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return;
+ }
+ llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits)
+ << " Alpha Bits " << S32(pfd.cAlphaBits)
+ << " Depth Bits " << S32(pfd.cDepthBits)
+ << llendl;
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return;
+ }
+
+ if (!gGLManager.initGL())
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return;
+ }
+
+ // Disable vertical sync for swap
+ if (disable_vsync && wglSwapIntervalEXT)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ wglSwapIntervalEXT(0);
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ }
+
+
+ // OK, let's get the current gamma information and store it off.
+ mCurrentGamma = 0.f; // Not set, default;
+ if (!GetDeviceGammaRamp(mhDC, mPrevGammaRamp))
+ {
+ llwarns << "Unable to get device gamma ramp" << llendl;
+ }
+
+ // Calculate what the current gamma is. From a posting by Garrett T. Bass, Get/SetDeviceGammaRamp Demystified
+ // http://apollo.iwt.uni-bielefeld.de/~ml_robot/OpenGL-04-2000/0058.html
+
+ // We're going to assume that gamma's the same for all 3 channels, because I don't feel like doing it otherwise.
+ // Using the red channel.
+
+ F32 Csum = 0.0;
+ S32 Ccount = 0;
+ for (i = 0; i < 256; i++)
+ {
+ if (i != 0 && mPrevGammaRamp[i] != 0 && mPrevGammaRamp[i] != 65536)
+ {
+ F64 B = (i % 256) / 256.0;
+ F64 A = mPrevGammaRamp[i] / 65536.0;
+ F32 C = (F32) ( log(A) / log(B) );
+ Csum += C;
+ Ccount++;
+ }
+ }
+ mCurrentGamma = Csum / Ccount;
+
+ llinfos << "Previous gamma: " << mCurrentGamma << llendl;
+ }
+
+
+ //store this pointer for wndProc callback
+ SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this);
+
+ //start with arrow cursor
+ initCursors();
+ setCursor( UI_CURSOR_ARROW );
+
+ // Direct Input
+ HRESULT hr;
+
+ if( FAILED( hr = DirectInput8Create( GetModuleHandle(NULL), DIRECTINPUT_VERSION,
+ IID_IDirectInput8, (VOID**)&g_pDI, NULL ) ) )
+ {
+ llwarns << "Direct8InputCreate failed!" << llendl;
+ }
+ else
+ {
+ while(1)
+ {
+ // Look for a simple joystick we can use for this sample program.
+ if (FAILED( hr = g_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL,
+ EnumJoysticksCallback,
+ NULL, DIEDFL_ATTACHEDONLY ) ) )
+ break;
+ if (!g_pJoystick)
+ break;
+ if( FAILED( hr = g_pJoystick->SetDataFormat( &c_dfDIJoystick ) ) )
+ break;
+ if( FAILED( hr = g_pJoystick->EnumObjects( EnumObjectsCallback,
+ (VOID*)mWindowHandle, DIDFT_ALL ) ) )
+ break;
+ g_pJoystick->Acquire();
+ break;
+ }
+ }
+
+ SetTimer( mWindowHandle, 0, 1000 / 30, NULL ); // 30 fps timer
+ mJoyStickState = 0;
+ mJoyButtonState = 0;
+}
+
+
+LLWindowWin32::~LLWindowWin32()
+{
+ delete [] mWindowTitle;
+ mWindowTitle = NULL;
+
+ delete [] mSupportedResolutions;
+ mSupportedResolutions = NULL;
+
+ delete mWindowClassName;
+ mWindowClassName = NULL;
+}
+
+void LLWindowWin32::show()
+{
+ ShowWindow(mWindowHandle, SW_SHOW);
+ SetForegroundWindow(mWindowHandle);
+ SetFocus(mWindowHandle);
+}
+
+void LLWindowWin32::hide()
+{
+ setMouseClipping(FALSE);
+ ShowWindow(mWindowHandle, SW_HIDE);
+}
+
+void LLWindowWin32::minimize()
+{
+ setMouseClipping(FALSE);
+ showCursor();
+ ShowWindow(mWindowHandle, SW_MINIMIZE);
+}
+
+
+void LLWindowWin32::restore()
+{
+ ShowWindow(mWindowHandle, SW_RESTORE);
+ SetForegroundWindow(mWindowHandle);
+ SetFocus(mWindowHandle);
+}
+
+
+// close() destroys all OS-specific code associated with a window.
+// Usually called from LLWindowManager::destroyWindow()
+void LLWindowWin32::close()
+{
+ llinfos << "Closing LLWindowWin32" << llendl;
+ // Is window is already closed?
+ if (!mWindowHandle)
+ {
+ return;
+ }
+
+ // Make sure cursor is visible and we haven't mangled the clipping state.
+ setMouseClipping(FALSE);
+ showCursor();
+
+ // Go back to screen mode written in the registry.
+ if (mFullscreen)
+ {
+ resetDisplayResolution();
+ }
+
+ // Clean up remaining GL state
+ llinfos << "Shutting down GL" << llendl;
+ gGLManager.shutdownGL();
+
+ llinfos << "Releasing Context" << llendl;
+ if (mhRC)
+ {
+ if (!wglMakeCurrent(NULL, NULL))
+ {
+ llwarns << "Release of DC and RC failed" << llendl;
+ }
+
+ if (!wglDeleteContext(mhRC))
+ {
+ llwarns << "Release of rendering context failed" << llendl;
+ }
+
+ mhRC = NULL;
+ }
+
+ // Restore gamma to the system values.
+ restoreGamma();
+
+ if (mhDC && !ReleaseDC(mWindowHandle, mhDC))
+ {
+ llwarns << "Release of ghDC failed" << llendl;
+ mhDC = NULL;
+ }
+
+ llinfos << "Destroying Window" << llendl;
+
+ // Don't process events in our mainWindowProc any longer.
+ SetWindowLong(mWindowHandle, GWL_USERDATA, NULL);
+
+ // Make sure we don't leave a blank toolbar button.
+ ShowWindow(mWindowHandle, SW_HIDE);
+
+ // This causes WM_DESTROY to be sent *immediately*
+ if (!DestroyWindow(mWindowHandle))
+ {
+ OSMessageBox("DestroyWindow(mWindowHandle) failed", "Shutdown Error", OSMB_OK);
+ }
+
+ mWindowHandle = NULL;
+}
+
+BOOL LLWindowWin32::isValid()
+{
+ return (mWindowHandle != NULL);
+}
+
+BOOL LLWindowWin32::getVisible()
+{
+ return (mWindowHandle && IsWindowVisible(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMinimized()
+{
+ return (mWindowHandle && IsIconic(mWindowHandle));
+}
+
+BOOL LLWindowWin32::getMaximized()
+{
+ return (mWindowHandle && IsZoomed(mWindowHandle));
+}
+
+BOOL LLWindowWin32::maximize()
+{
+ BOOL success = FALSE;
+ if (!mWindowHandle) return success;
+
+ WINDOWPLACEMENT placement;
+ placement.length = sizeof(WINDOWPLACEMENT);
+
+ success = GetWindowPlacement(mWindowHandle, &placement);
+ if (!success) return success;
+
+ placement.showCmd = SW_MAXIMIZE;
+
+ success = SetWindowPlacement(mWindowHandle, &placement);
+ return success;
+}
+
+BOOL LLWindowWin32::getFullscreen()
+{
+ return mFullscreen;
+}
+
+BOOL LLWindowWin32::getPosition(LLCoordScreen *position)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == position)
+ {
+ return FALSE;
+ }
+
+ position->mX = window_rect.left;
+ position->mY = window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordScreen *size)
+{
+ RECT window_rect;
+
+ if (!mWindowHandle ||
+ !GetWindowRect(mWindowHandle, &window_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = window_rect.right - window_rect.left;
+ size->mY = window_rect.bottom - window_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::getSize(LLCoordWindow *size)
+{
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == size)
+ {
+ return FALSE;
+ }
+
+ size->mX = client_rect.right - client_rect.left;
+ size->mY = client_rect.bottom - client_rect.top;
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setPosition(const LLCoordScreen position)
+{
+ LLCoordScreen size;
+
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+ getSize(&size);
+ moveWindow(position, size);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::setSize(const LLCoordScreen size)
+{
+ LLCoordScreen position;
+
+ getPosition(&position);
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ moveWindow(position, size);
+ return TRUE;
+}
+
+// changing fullscreen resolution
+BOOL LLWindowWin32::switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync)
+{
+ GLuint pixel_format;
+ DEVMODE dev_mode;
+ DWORD current_refresh;
+ DWORD dw_ex_style;
+ DWORD dw_style;
+ RECT window_rect;
+ S32 width = size.mX;
+ S32 height = size.mY;
+
+ resetDisplayResolution();
+
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ current_refresh = dev_mode.dmDisplayFrequency;
+ }
+ else
+ {
+ current_refresh = 60;
+ }
+
+ gGLManager.shutdownGL();
+ //destroy gl context
+ if (mhRC)
+ {
+ if (!wglMakeCurrent(NULL, NULL))
+ {
+ llwarns << "Release of DC and RC failed" << llendl;
+ }
+
+ if (!wglDeleteContext(mhRC))
+ {
+ llwarns << "Release of rendering context failed" << llendl;
+ }
+
+ mhRC = NULL;
+ }
+
+ if (fullscreen)
+ {
+ mFullscreen = TRUE;
+ BOOL success = FALSE;
+ DWORD closest_refresh = 0;
+
+ for (S32 mode_num = 0;; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == BITS_PER_PIXEL)
+ {
+ success = TRUE;
+ if ((dev_mode.dmDisplayFrequency - current_refresh)
+ < (closest_refresh - current_refresh))
+ {
+ closest_refresh = dev_mode.dmDisplayFrequency;
+ }
+ }
+ }
+
+ if (closest_refresh == 0)
+ {
+ llwarns << "Couldn't find display mode " << width << " by " << height << " at " << BITS_PER_PIXEL << " bits per pixel" << llendl;
+ return FALSE;
+ }
+
+ // If we found a good resolution, use it.
+ if (success)
+ {
+ success = setDisplayResolution(width, height, BITS_PER_PIXEL, closest_refresh);
+ }
+
+ // Keep a copy of the actual current device mode in case we minimize
+ // and change the screen resolution. JC
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ if (success)
+ {
+ mFullscreen = TRUE;
+ mFullscreenWidth = dev_mode.dmPelsWidth;
+ mFullscreenHeight = dev_mode.dmPelsHeight;
+ mFullscreenBits = dev_mode.dmBitsPerPel;
+ mFullscreenRefresh = dev_mode.dmDisplayFrequency;
+
+ llinfos << "Running at " << dev_mode.dmPelsWidth
+ << "x" << dev_mode.dmPelsHeight
+ << "x" << dev_mode.dmBitsPerPel
+ << " @ " << dev_mode.dmDisplayFrequency
+ << llendl;
+
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+ dw_ex_style = WS_EX_APPWINDOW;
+ dw_style = WS_POPUP;
+
+ // Move window borders out not to cover window contents
+ AdjustWindowRectEx(&window_rect, dw_style, FALSE, dw_ex_style);
+ }
+ // If it failed, we don't want to run fullscreen
+ else
+ {
+ mFullscreen = FALSE;
+ mFullscreenWidth = -1;
+ mFullscreenHeight = -1;
+ mFullscreenBits = -1;
+ mFullscreenRefresh = -1;
+
+ llinfos << "Unable to run fullscreen at " << width << "x" << height << llendl;
+ llinfos << "Running in window." << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ mFullscreen = FALSE;
+ window_rect.left = (long) 0;
+ window_rect.right = (long) width; // Windows GDI rects don't include rightmost pixel
+ window_rect.top = (long) 0;
+ window_rect.bottom = (long) height;
+ // Window with an edge
+ dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
+ dw_style = WS_OVERLAPPEDWINDOW;
+ }
+
+ // don't post quit messages when destroying old windows
+ mPostQuit = FALSE;
+
+ // create window
+ DestroyWindow(mWindowHandle);
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ //-----------------------------------------------------------------------
+ // Create GL drawing context
+ //-----------------------------------------------------------------------
+ static PIXELFORMATDESCRIPTOR pfd =
+ {
+ sizeof(PIXELFORMATDESCRIPTOR),
+ 1,
+ PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+ PFD_TYPE_RGBA,
+ BITS_PER_PIXEL,
+ 0, 0, 0, 0, 0, 0, // RGB bits and shift, unused
+ 8, // alpha bits
+ 0, // alpha shift
+ 0, // accum bits
+ 0, 0, 0, 0, // accum RGBA
+ 24, // depth bits
+ 8, // stencil bits, avi added for stencil test
+ 0,
+ PFD_MAIN_PLANE,
+ 0,
+ 0, 0, 0
+ };
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(pixel_format = ChoosePixelFormat(mhDC, &pfd)))
+ {
+ close();
+ OSMessageBox("Can't find suitable pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ gGLManager.initWGL();
+
+ if (wglChoosePixelFormatARB)
+ {
+ // OK, at this point, use the ARB wglChoosePixelFormatsARB function to see if we
+ // can get exactly what we want.
+ GLint attrib_list[256];
+ S32 cur_attrib = 0;
+
+ attrib_list[cur_attrib++] = WGL_DEPTH_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_STENCIL_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_DRAW_TO_WINDOW_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_ACCELERATION_ARB;
+ attrib_list[cur_attrib++] = WGL_FULL_ACCELERATION_ARB;
+
+ attrib_list[cur_attrib++] = WGL_SUPPORT_OPENGL_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_DOUBLE_BUFFER_ARB;
+ attrib_list[cur_attrib++] = GL_TRUE;
+
+ attrib_list[cur_attrib++] = WGL_COLOR_BITS_ARB;
+ attrib_list[cur_attrib++] = 24;
+
+ attrib_list[cur_attrib++] = WGL_RED_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_GREEN_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_BLUE_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ attrib_list[cur_attrib++] = WGL_ALPHA_BITS_ARB;
+ attrib_list[cur_attrib++] = 8;
+
+ // End the list
+ attrib_list[cur_attrib++] = 0;
+
+ GLint pixel_formats[256];
+ U32 num_formats = 0;
+
+ // First we try and get a 32 bit depth pixel format
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 32-bit");
+ return FALSE;
+ }
+
+ if (!num_formats)
+ {
+ llinfos << "No 32 bit z-buffer, trying 24 bits instead" << llendl;
+ // Try 24-bit format
+ attrib_list[1] = 24;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 24-bit");
+ return FALSE;
+ }
+
+ if (!num_formats)
+ {
+ llwarns << "Couldn't get 24 bit z-buffer,trying 16 bits instead!" << llendl;
+ attrib_list[1] = 16;
+ BOOL result = wglChoosePixelFormatARB(mhDC, attrib_list, NULL, 256, pixel_formats, &num_formats);
+ if (!result || !num_formats)
+ {
+ close();
+ show_window_creation_error("Error after wglChoosePixelFormatARB 16-bit");
+ return FALSE;
+ }
+ }
+
+ llinfos << "Choosing pixel formats: " << num_formats << " pixel formats returned" << llendl;
+
+ pixel_format = pixel_formats[0];
+ }
+
+ DestroyWindow(mWindowHandle);
+ mWindowHandle = CreateWindowEx(dw_ex_style,
+ mWindowClassName,
+ mWindowTitle,
+ WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dw_style,
+ window_rect.left, // x pos
+ window_rect.top, // y pos
+ window_rect.right - window_rect.left, // width
+ window_rect.bottom - window_rect.top, // height
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ if (!(mhDC = GetDC(mWindowHandle)))
+ {
+ close();
+ OSMessageBox("Can't make GL device context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!SetPixelFormat(mhDC, pixel_format, &pfd))
+ {
+ close();
+ OSMessageBox("Can't set pixel format", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ int swap_method = 0;
+ GLint swap_query = WGL_SWAP_METHOD_ARB;
+
+ if (wglGetPixelFormatAttribivARB(mhDC, pixel_format, 0, 1, &swap_query, &swap_method))
+ {
+ switch (swap_method)
+ {
+ case WGL_SWAP_EXCHANGE_ARB:
+ mSwapMethod = SWAP_METHOD_EXCHANGE;
+ llinfos << "Swap Method: Exchange" << llendl;
+ break;
+ case WGL_SWAP_COPY_ARB:
+ mSwapMethod = SWAP_METHOD_COPY;
+ llinfos << "Swap Method: Copy" << llendl;
+ break;
+ case WGL_SWAP_UNDEFINED_ARB:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Undefined" << llendl;
+ break;
+ default:
+ mSwapMethod = SWAP_METHOD_UNDEFINED;
+ llinfos << "Swap Method: Unknown" << llendl;
+ break;
+ }
+ }
+ }
+ else
+ {
+ llwarns << "No wgl_ARB_pixel_format extension, using default ChoosePixelFormat!" << llendl;
+ }
+
+ // Verify what pixel format we actually received.
+ if (!DescribePixelFormat(mhDC, pixel_format, sizeof(PIXELFORMATDESCRIPTOR),
+ &pfd))
+ {
+ close();
+ OSMessageBox("Can't get pixel format description", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ llinfos << "GL buffer: Color Bits " << S32(pfd.cColorBits)
+ << " Alpha Bits " << S32(pfd.cAlphaBits)
+ << " Depth Bits " << S32(pfd.cDepthBits)
+ << llendl;
+
+ if (pfd.cColorBits < 32)
+ {
+ close();
+ OSMessageBox(
+ "Second Life requires True Color (32-bit) to run in a window.\n"
+ "Please go to Control Panels -> Display -> Settings and\n"
+ "set the screen to 32-bit color.\n"
+ "Alternately, if you choose to run fullscreen, Second Life\n"
+ "will automatically adjust the screen each time it runs.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (pfd.cAlphaBits < 8)
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because it can't get an 8 bit alpha\n"
+ "channel. Usually this is due to video card driver issues.\n"
+ "Please make sure you have the latest video card drivers installed.\n"
+ "Also be sure your monitor is set to True Color (32-bit) in\n"
+ "Control Panels -> Display -> Settings.\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ if (!(mhRC = wglCreateContext(mhDC)))
+ {
+ close();
+ OSMessageBox("Can't create GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!wglMakeCurrent(mhDC, mhRC))
+ {
+ close();
+ OSMessageBox("Can't activate GL rendering context", "Error", OSMB_OK);
+ return FALSE;
+ }
+
+ if (!gGLManager.initGL())
+ {
+ close();
+ OSMessageBox(
+ "Second Life is unable to run because your video card drivers\n"
+ "are out of date or unsupported. Please make sure you have\n"
+ "the latest video card drivers installed.\n\n"
+ "If you continue to receive this message, contact customer service.",
+ "Error",
+ OSMB_OK);
+ return FALSE;
+ }
+
+ // Disable vertical sync for swap
+ if (disable_vsync && wglSwapIntervalEXT)
+ {
+ llinfos << "Disabling vertical sync" << llendl;
+ wglSwapIntervalEXT(0);
+ }
+ else
+ {
+ llinfos << "Keeping vertical sync" << llendl;
+ }
+
+ SetWindowLong(mWindowHandle, GWL_USERDATA, (U32)this);
+ show();
+
+ // ok to post quit messages now
+ mPostQuit = TRUE;
+ return TRUE;
+}
+
+void LLWindowWin32::moveWindow( const LLCoordScreen& position, const LLCoordScreen& size )
+{
+ if( mIsMouseClipping )
+ {
+ RECT client_rect_in_screen_space;
+ if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
+ {
+ ClipCursor( &client_rect_in_screen_space );
+ }
+ }
+
+ MoveWindow(mWindowHandle, position.mX, position.mY, size.mX, size.mY, TRUE);
+}
+
+BOOL LLWindowWin32::setCursorPosition(const LLCoordWindow position)
+{
+ LLCoordScreen screen_pos;
+
+ mMousePositionModified = TRUE;
+ if (!mWindowHandle)
+ {
+ return FALSE;
+ }
+
+ if (!convertCoords(position, &screen_pos))
+ {
+ return FALSE;
+ }
+
+ return SetCursorPos(screen_pos.mX, screen_pos.mY);
+}
+
+BOOL LLWindowWin32::getCursorPosition(LLCoordWindow *position)
+{
+ POINT cursor_point;
+ LLCoordScreen screen_pos;
+
+ if (!mWindowHandle ||
+ !GetCursorPos(&cursor_point))
+ {
+ return FALSE;
+ }
+
+ screen_pos.mX = cursor_point.x;
+ screen_pos.mY = cursor_point.y;
+
+ return convertCoords(screen_pos, position);
+}
+
+void LLWindowWin32::hideCursor()
+{
+ while (ShowCursor(FALSE) >= 0)
+ {
+ // nothing, wait for cursor to push down
+ }
+ mCursorHidden = TRUE;
+ mHideCursorPermanent = TRUE;
+}
+
+void LLWindowWin32::showCursor()
+{
+ // makes sure the cursor shows up
+ while (ShowCursor(TRUE) < 0)
+ {
+ // do nothing, wait for cursor to pop out
+ }
+ mCursorHidden = FALSE;
+ mHideCursorPermanent = FALSE;
+}
+
+void LLWindowWin32::showCursorFromMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ showCursor();
+ }
+}
+
+void LLWindowWin32::hideCursorUntilMouseMove()
+{
+ if (!mHideCursorPermanent)
+ {
+ hideCursor();
+ mHideCursorPermanent = FALSE;
+ }
+}
+
+BOOL LLWindowWin32::isCursorHidden()
+{
+ return mCursorHidden;
+}
+
+
+HCURSOR LLWindowWin32::loadColorCursor(LPCTSTR name)
+{
+ return (HCURSOR)LoadImage(mhInstance,
+ name,
+ IMAGE_CURSOR,
+ 0, // default width
+ 0, // default height
+ LR_DEFAULTCOLOR);
+}
+
+
+void LLWindowWin32::initCursors()
+{
+ mCursor[ UI_CURSOR_ARROW ] = LoadCursor(NULL, IDC_ARROW);
+ mCursor[ UI_CURSOR_WAIT ] = LoadCursor(NULL, IDC_WAIT);
+ mCursor[ UI_CURSOR_HAND ] = LoadCursor(NULL, IDC_HAND);
+ mCursor[ UI_CURSOR_IBEAM ] = LoadCursor(NULL, IDC_IBEAM);
+ mCursor[ UI_CURSOR_CROSS ] = LoadCursor(NULL, IDC_CROSS);
+ mCursor[ UI_CURSOR_SIZENWSE ] = LoadCursor(NULL, IDC_SIZENWSE);
+ mCursor[ UI_CURSOR_SIZENESW ] = LoadCursor(NULL, IDC_SIZENESW);
+ mCursor[ UI_CURSOR_SIZEWE ] = LoadCursor(NULL, IDC_SIZEWE);
+ mCursor[ UI_CURSOR_SIZENS ] = LoadCursor(NULL, IDC_SIZENS);
+ mCursor[ UI_CURSOR_NO ] = LoadCursor(NULL, IDC_NO);
+ mCursor[ UI_CURSOR_WORKING ] = LoadCursor(NULL, IDC_APPSTARTING);
+
+ HMODULE module = GetModuleHandle(NULL);
+ mCursor[ UI_CURSOR_TOOLGRAB ] = LoadCursor(module, TEXT("TOOLGRAB"));
+ mCursor[ UI_CURSOR_TOOLLAND ] = LoadCursor(module, TEXT("TOOLLAND"));
+ mCursor[ UI_CURSOR_TOOLFOCUS ] = LoadCursor(module, TEXT("TOOLFOCUS"));
+ mCursor[ UI_CURSOR_TOOLCREATE ] = LoadCursor(module, TEXT("TOOLCREATE"));
+ mCursor[ UI_CURSOR_ARROWDRAG ] = LoadCursor(module, TEXT("ARROWDRAG"));
+ mCursor[ UI_CURSOR_ARROWCOPY ] = LoadCursor(module, TEXT("ARROWCOPY"));
+ mCursor[ UI_CURSOR_ARROWDRAGMULTI ] = LoadCursor(module, TEXT("ARROWDRAGMULTI"));
+ mCursor[ UI_CURSOR_ARROWCOPYMULTI ] = LoadCursor(module, TEXT("ARROWCOPYMULTI"));
+ mCursor[ UI_CURSOR_NOLOCKED ] = LoadCursor(module, TEXT("NOLOCKED"));
+ mCursor[ UI_CURSOR_ARROWLOCKED ]= LoadCursor(module, TEXT("ARROWLOCKED"));
+ mCursor[ UI_CURSOR_GRABLOCKED ] = LoadCursor(module, TEXT("GRABLOCKED"));
+ mCursor[ UI_CURSOR_TOOLTRANSLATE ] = LoadCursor(module, TEXT("TOOLTRANSLATE"));
+ mCursor[ UI_CURSOR_TOOLROTATE ] = LoadCursor(module, TEXT("TOOLROTATE"));
+ mCursor[ UI_CURSOR_TOOLSCALE ] = LoadCursor(module, TEXT("TOOLSCALE"));
+ mCursor[ UI_CURSOR_TOOLCAMERA ] = LoadCursor(module, TEXT("TOOLCAMERA"));
+ mCursor[ UI_CURSOR_TOOLPAN ] = LoadCursor(module, TEXT("TOOLPAN"));
+ mCursor[ UI_CURSOR_TOOLZOOMIN ] = LoadCursor(module, TEXT("TOOLZOOMIN"));
+ mCursor[ UI_CURSOR_TOOLPICKOBJECT3 ] = LoadCursor(module, TEXT("TOOLPICKOBJECT3"));
+ mCursor[ UI_CURSOR_PIPETTE ] = LoadCursor(module, TEXT("TOOLPIPETTE"));
+
+ // Color cursors
+ mCursor[UI_CURSOR_TOOLSIT] = loadColorCursor(TEXT("TOOLSIT"));
+ mCursor[UI_CURSOR_TOOLBUY] = loadColorCursor(TEXT("TOOLBUY"));
+ mCursor[UI_CURSOR_TOOLPAY] = loadColorCursor(TEXT("TOOLPAY"));
+ mCursor[UI_CURSOR_TOOLOPEN] = loadColorCursor(TEXT("TOOLOPEN"));
+
+ // Note: custom cursors that are not found make LoadCursor() return NULL.
+ for( S32 i = 0; i < UI_CURSOR_COUNT; i++ )
+ {
+ if( !mCursor[i] )
+ {
+ mCursor[i] = LoadCursor(NULL, IDC_ARROW);
+ }
+ }
+}
+
+
+
+void LLWindowWin32::setCursor(ECursorType cursor)
+{
+ if (cursor == UI_CURSOR_ARROW
+ && mBusyCount > 0)
+ {
+ cursor = UI_CURSOR_WORKING;
+ }
+
+ if( mCurrentCursor != cursor )
+ {
+ mCurrentCursor = cursor;
+ SetCursor( mCursor[cursor] );
+ }
+}
+
+ECursorType LLWindowWin32::getCursor()
+{
+ return mCurrentCursor;
+}
+
+void LLWindowWin32::captureMouse()
+{
+ SetCapture(mWindowHandle);
+}
+
+void LLWindowWin32::releaseMouse()
+{
+ ReleaseCapture();
+}
+
+
+void LLWindowWin32::delayInputProcessing()
+{
+ mInputProcessingPaused = TRUE;
+}
+
+void LLWindowWin32::gatherInput()
+{
+ MSG msg;
+ int msg_count = 0;
+
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) && msg_count < MAX_MESSAGE_PER_UPDATE)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+
+ if ( mInputProcessingPaused )
+ {
+ break;
+ }
+ /* Attempted workaround for problem where typing fast and hitting
+ return would result in only part of the text being sent. JC
+
+ BOOL key_posted = TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+
+ // If a key was translated, a WM_CHAR might have been posted to the end
+ // of the event queue. We need it immediately.
+ if (key_posted && msg.message == WM_KEYDOWN)
+ {
+ if (PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ msg_count++;
+ }
+ }
+ */
+
+ // For async host by name support. Really hacky.
+ if (gAsyncMsgCallback && (LL_WM_HOST_RESOLVED == msg.message))
+ {
+ gAsyncMsgCallback(msg);
+ }
+ }
+
+ mInputProcessingPaused = FALSE;
+
+ // clear this once we've processed all mouse messages that might have occurred after
+ // we slammed the mouse position
+ mMousePositionModified = FALSE;
+}
+
+LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param)
+{
+ LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA);
+
+ if (NULL != window_imp)
+ {
+ // Has user provided their own window callback?
+ if (NULL != window_imp->mWndProc)
+ {
+ if (!window_imp->mWndProc(h_wnd, u_msg, w_param, l_param))
+ {
+ // user has handled window message
+ return 0;
+ }
+ }
+
+ // Juggle to make sure we can get negative positions for when
+ // mouse is outside window.
+ LLCoordWindow window_coord((S32)(S16)LOWORD(l_param), (S32)(S16)HIWORD(l_param));
+
+ // This doesn't work, as LOWORD returns unsigned short.
+ //LLCoordWindow window_coord(LOWORD(l_param), HIWORD(l_param));
+ LLCoordGL gl_coord;
+
+ // pass along extended flag in mask
+ MASK mask = (l_param>>16 & KF_EXTENDED) ? MASK_EXTENDED : 0x0;
+ BOOL eat_keystroke = TRUE;
+
+ switch(u_msg)
+ {
+ RECT update_rect;
+ S32 update_width;
+ S32 update_height;
+
+ case WM_TIMER:
+ window_imp->updateJoystick( );
+ break;
+
+ case WM_PAINT:
+ GetUpdateRect(window_imp->mWindowHandle, &update_rect, FALSE);
+ update_width = update_rect.right - update_rect.left + 1;
+ update_height = update_rect.bottom - update_rect.top + 1;
+ window_imp->mCallbacks->handlePaint(window_imp, update_rect.left, update_rect.top,
+ update_width, update_height);
+ break;
+ case WM_PARENTNOTIFY:
+ u_msg = u_msg;
+ break;
+
+ case WM_SETCURSOR:
+ // This message is sent whenever the cursor is moved in a window.
+ // You need to set the appropriate cursor appearance.
+
+ // Only take control of cursor over client region of window
+ // This allows Windows(tm) to handle resize cursors, etc.
+ if (LOWORD(l_param) == HTCLIENT)
+ {
+ SetCursor(window_imp->mCursor[ window_imp->mCurrentCursor] );
+ return 0;
+ }
+ break;
+
+ case WM_ENTERMENULOOP:
+ window_imp->mCallbacks->handleWindowBlock(window_imp);
+ break;
+
+ case WM_EXITMENULOOP:
+ window_imp->mCallbacks->handleWindowUnblock(window_imp);
+ break;
+
+ case WM_ACTIVATEAPP:
+ {
+ // This message should be sent whenever the app gains or loses focus.
+ BOOL activating = (BOOL) w_param;
+ BOOL minimized = window_imp->getMinimized();
+
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC ActivateApp "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << " fullscreen " << S32(window_imp->mFullscreen)
+ << llendl;
+ }
+
+ if (window_imp->mFullscreen)
+ {
+ // When we run fullscreen, restoring or minimizing the app needs
+ // to switch the screen resolution
+ if (activating)
+ {
+ window_imp->setFullscreenResolution();
+ window_imp->restore();
+ }
+ else
+ {
+ window_imp->minimize();
+ window_imp->resetDisplayResolution();
+ }
+ }
+ break;
+ }
+
+ case WM_ACTIVATE:
+ {
+ // Can be one of WA_ACTIVE, WA_CLICKACTIVE, or WA_INACTIVE
+ BOOL activating = (LOWORD(w_param) != WA_INACTIVE);
+
+ BOOL minimized = BOOL(HIWORD(w_param));
+
+ // JC - I'm not sure why, but if we don't report that we handled the
+ // WM_ACTIVATE message, the WM_ACTIVATEAPP messages don't work
+ // properly when we run fullscreen.
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC Activate "
+ << " activating " << S32(activating)
+ << " minimized " << S32(minimized)
+ << llendl;
+ }
+
+ // Don't handle this.
+ break;
+ }
+
+ case WM_QUERYOPEN:
+ // TODO: use this to return a nice icon
+ break;
+
+ case WM_SYSCOMMAND:
+ switch(w_param)
+ {
+ case SC_KEYMENU:
+ // Disallow the ALT key from triggering the default system menu.
+ return 0;
+
+ case SC_SCREENSAVE:
+ case SC_MONITORPOWER:
+ // eat screen save messages and prevent them!
+ return 0;
+ }
+ break;
+
+ case WM_CLOSE:
+ // Will the app allow the window to close?
+ if (window_imp->mCallbacks->handleCloseRequest(window_imp))
+ {
+ // Get the app to initiate cleanup.
+ window_imp->mCallbacks->handleQuit(window_imp);
+ // The app is responsible for calling destroyWindow when done with GL
+ }
+ return 0;
+
+ case WM_DESTROY:
+ if (window_imp->shouldPostQuit())
+ {
+ PostQuitMessage(0); // Posts WM_QUIT with an exit code of 0
+ }
+ return 0;
+
+ case WM_COMMAND:
+ if (!HIWORD(w_param)) // this message is from a menu
+ {
+ window_imp->mCallbacks->handleMenuSelect(window_imp, LOWORD(w_param));
+ }
+ break;
+
+ case WM_SYSKEYDOWN:
+ // allow system keys, such as ALT-F4 to be processed by Windows
+ eat_keystroke = FALSE;
+ case WM_KEYDOWN:
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_KEYDOWN "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (gKeyboard->handleKeyDown(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+ // pass on to windows if we didn't handle it
+ break;
+
+ case WM_SYSKEYUP:
+ eat_keystroke = FALSE;
+ case WM_KEYUP:
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_KEYUP "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (gKeyboard->handleKeyUp(w_param, mask) && eat_keystroke)
+ {
+ return 0;
+ }
+
+ // pass on to windows
+ break;
+
+
+ case WM_CHAR:
+ // Should really use WM_UNICHAR eventually, but it requires a specific Windows version and I need
+ // to figure out how that works. - Doug
+ // llinfos << "WM_CHAR: " << w_param << llendl;
+ if (gDebugWindowProc)
+ {
+ llinfos << "Debug WindowProc WM_CHAR "
+ << " key " << S32(w_param)
+ << llendl;
+ }
+ if (window_imp->mCallbacks->handleUnicodeChar(w_param, gKeyboard->currentMask(FALSE)))
+ {
+ return 0;
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ //RN: ignore right button double clicks for now
+ //case WM_RBUTTONDBLCLK:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleDoubleClick(window_imp, gl_coord, mask) )
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_LBUTTONUP:
+ {
+ //if (gDebugClicks)
+ //{
+ // llinfos << "WndProc left button up" << llendl;
+ //}
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONDBLCLK:
+ case WM_RBUTTONDOWN:
+ {
+ // Because we move the cursor position in tllviewerhe app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleRightMouseDown(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_RBUTTONUP:
+ {
+ // Because we move the cursor position in the app, we need to query
+ // to find out where the cursor at the time the event is handled.
+ // If we don't do this, many clicks could get buffered up, and if the
+ // first click changes the cursor position, all subsequent clicks
+ // will occur at the wrong location. JC
+ LLCoordWindow cursor_coord_window;
+ if (window_imp->mMousePositionModified)
+ {
+ window_imp->getCursorPosition(&cursor_coord_window);
+ window_imp->convertCoords(cursor_coord_window, &gl_coord);
+ }
+ else
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ }
+ MASK mask = gKeyboard->currentMask(TRUE);
+ if (window_imp->mCallbacks->handleRightMouseUp(window_imp, gl_coord, mask))
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_MBUTTONDOWN:
+ // Handle middle button click
+ break;
+
+ case WM_MOUSEWHEEL:
+ {
+ static short z_delta = 0;
+
+ z_delta += HIWORD(w_param);
+ // cout << "z_delta " << z_delta << endl;
+
+ // current mouse wheels report changes in increments of zDelta (+120, -120)
+ // Future, higher resolution mouse wheels may report smaller deltas.
+ // So we sum the deltas and only act when we've exceeded WHEEL_DELTA
+ //
+ // If the user rapidly spins the wheel, we can get messages with
+ // large deltas, like 480 or so. Thus we need to scroll more quickly.
+ if (z_delta <= -WHEEL_DELTA || WHEEL_DELTA <= z_delta)
+ {
+ window_imp->mCallbacks->handleScrollWheel(window_imp, -z_delta / WHEEL_DELTA);
+ z_delta = 0;
+ }
+ return 0;
+ }
+ /*
+ // TODO: add this after resolving _WIN32_WINNT issue
+ case WM_MOUSELEAVE:
+ {
+ window_imp->mCallbacks->handleMouseLeave(window_imp);
+
+ // TRACKMOUSEEVENT track_mouse_event;
+ // track_mouse_event.cbSize = sizeof( TRACKMOUSEEVENT );
+ // track_mouse_event.dwFlags = TME_LEAVE;
+ // track_mouse_event.hwndTrack = h_wnd;
+ // track_mouse_event.dwHoverTime = HOVER_DEFAULT;
+ // TrackMouseEvent( &track_mouse_event );
+ return 0;
+ }
+ */
+ // Handle mouse movement within the window
+ case WM_MOUSEMOVE:
+ {
+ window_imp->convertCoords(window_coord, &gl_coord);
+ MASK mask = gKeyboard->currentMask(TRUE);
+ window_imp->mCallbacks->handleMouseMove(window_imp, gl_coord, mask);
+ return 0;
+ }
+
+ case WM_SIZE:
+ {
+ S32 width = S32( LOWORD(l_param) );
+ S32 height = S32( HIWORD(l_param) );
+
+ if (gDebugWindowProc)
+ {
+ BOOL maximized = ( w_param == SIZE_MAXIMIZED );
+ BOOL restored = ( w_param == SIZE_RESTORED );
+ BOOL minimized = ( w_param == SIZE_MINIMIZED );
+
+ llinfos << "WINDOWPROC Size "
+ << width << "x" << height
+ << " max " << S32(maximized)
+ << " min " << S32(minimized)
+ << " rest " << S32(restored)
+ << llendl;
+ }
+
+ // If we are now restored, but we weren't before, this
+ // means that the window was un-minimized.
+ if (w_param == SIZE_RESTORED && window_imp->mLastSizeWParam != SIZE_RESTORED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // handle case of window being maximized from fully minimized state
+ if (w_param == SIZE_MAXIMIZED && window_imp->mLastSizeWParam != SIZE_MAXIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, TRUE);
+ }
+
+ // Also handle the minimization case
+ if (w_param == SIZE_MINIMIZED && window_imp->mLastSizeWParam != SIZE_MINIMIZED)
+ {
+ window_imp->mCallbacks->handleActivate(window_imp, FALSE);
+ }
+
+ // Actually resize all of our views
+ if (w_param != SIZE_MINIMIZED)
+ {
+ // Ignore updates for minimizing and minimized "windows"
+ window_imp->mCallbacks->handleResize( window_imp,
+ LOWORD(l_param),
+ HIWORD(l_param) );
+ }
+
+ window_imp->mLastSizeWParam = w_param;
+
+ return 0;
+ }
+
+ case WM_SETFOCUS:
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC SetFocus" << llendl;
+ }
+ window_imp->mCallbacks->handleFocus(window_imp);
+ return 0;
+
+ case WM_KILLFOCUS:
+ if (gDebugWindowProc)
+ {
+ llinfos << "WINDOWPROC KillFocus" << llendl;
+ }
+ window_imp->mCallbacks->handleFocusLost(window_imp);
+ return 0;
+
+ case WM_COPYDATA:
+ // received a URL
+ PCOPYDATASTRUCT myCDS = (PCOPYDATASTRUCT) l_param;
+ window_imp->mCallbacks->handleDataCopy(window_imp, myCDS->dwData, myCDS->lpData);
+ return 0;
+ }
+ }
+
+ // pass unhandled messages down to Windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordWindow *to)
+{
+ S32 client_height;
+ RECT client_rect;
+ LLCoordWindow window_position;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordGL* to)
+{
+ S32 client_height;
+ RECT client_rect;
+
+ if (!mWindowHandle ||
+ !GetClientRect(mWindowHandle, &client_rect) ||
+ NULL == to)
+ {
+ return FALSE;
+ }
+
+ to->mX = from.mX;
+ client_height = client_rect.bottom - client_rect.top;
+ to->mY = client_height - from.mY - 1;
+
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordWindow* to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ScreenToClient(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordWindow from, LLCoordScreen *to)
+{
+ POINT mouse_point;
+
+ mouse_point.x = from.mX;
+ mouse_point.y = from.mY;
+ BOOL result = ClientToScreen(mWindowHandle, &mouse_point);
+
+ if (result)
+ {
+ to->mX = mouse_point.x;
+ to->mY = mouse_point.y;
+ }
+
+ return result;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordScreen from, LLCoordGL *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+BOOL LLWindowWin32::convertCoords(LLCoordGL from, LLCoordScreen *to)
+{
+ LLCoordWindow window_coord;
+
+ if (!mWindowHandle || (NULL == to))
+ {
+ return FALSE;
+ }
+
+ convertCoords(from, &window_coord);
+ convertCoords(window_coord, to);
+ return TRUE;
+}
+
+
+BOOL LLWindowWin32::isClipboardTextAvailable()
+{
+ return IsClipboardFormatAvailable(CF_UNICODETEXT) || IsClipboardFormatAvailable( CF_TEXT );
+}
+
+
+BOOL LLWindowWin32::pasteTextFromClipboard(LLWString &dst)
+{
+ BOOL success = FALSE;
+
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT))
+ {
+ if (OpenClipboard(mWindowHandle))
+ {
+ HGLOBAL h_data = GetClipboardData(CF_UNICODETEXT);
+ if (h_data)
+ {
+ WCHAR *utf16str = (WCHAR*) GlobalLock(h_data);
+ if (utf16str)
+ {
+ dst = utf16str_to_wstring(utf16str);
+ LLWString::removeCRLF(dst);
+ GlobalUnlock(h_data);
+ success = TRUE;
+ }
+ }
+ CloseClipboard();
+ }
+ }
+ else if (IsClipboardFormatAvailable(CF_TEXT))
+ {
+ // This must be an OLD OS. We don't do non-ASCII for old OSes
+ if (OpenClipboard(mWindowHandle))
+ {
+ HGLOBAL h_data = GetClipboardData(CF_TEXT);
+ if (h_data)
+ {
+ char* str = (char*) GlobalLock(h_data);
+ if (str)
+ {
+ // Strip non-ASCII characters
+ dst = utf8str_to_wstring(mbcsstring_makeASCII(str));
+ LLWString::removeCRLF(dst);
+ GlobalUnlock(h_data);
+ success = TRUE;
+ }
+ }
+ CloseClipboard();
+ }
+ }
+
+ return success;
+}
+
+
+BOOL LLWindowWin32::copyTextToClipboard(const LLWString& wstr)
+{
+ BOOL success = FALSE;
+
+ if (OpenClipboard(mWindowHandle))
+ {
+ EmptyClipboard();
+
+ // Provide a copy of the data in Unicode format.
+ LLWString sanitized_string(wstr);
+ LLWString::addCRLF(sanitized_string);
+ llutf16string out_utf16 = wstring_to_utf16str(sanitized_string);
+ const size_t size_utf16 = (out_utf16.length() + 1) * sizeof(WCHAR);
+
+ // Memory is allocated and then ownership of it is transfered to the system.
+ HGLOBAL hglobal_copy_utf16 = GlobalAlloc(GMEM_MOVEABLE, size_utf16);
+ if (hglobal_copy_utf16)
+ {
+ WCHAR* copy_utf16 = (WCHAR*) GlobalLock(hglobal_copy_utf16);
+ if (copy_utf16)
+ {
+ memcpy(copy_utf16, out_utf16.c_str(), size_utf16);
+ GlobalUnlock(hglobal_copy_utf16);
+
+ if (SetClipboardData(CF_UNICODETEXT, hglobal_copy_utf16))
+ {
+ success = TRUE;
+ }
+ }
+ }
+
+ // Also provide a copy as raw ASCII text.
+ LLWString ascii_string(wstr);
+ LLWString::_makeASCII(ascii_string);
+ LLWString::addCRLF(ascii_string);
+ std::string out_s = wstring_to_utf8str(ascii_string);
+ const size_t size = (out_s.length() + 1) * sizeof(char);
+
+ // Memory is allocated and then ownership of it is transfered to the system.
+ HGLOBAL hglobal_copy = GlobalAlloc(GMEM_MOVEABLE, size);
+ if (hglobal_copy)
+ {
+ char* copy = (char*) GlobalLock(hglobal_copy);
+ if( copy )
+ {
+ memcpy(copy, out_s.c_str(), size);
+ GlobalUnlock(hglobal_copy);
+
+ if (SetClipboardData(CF_TEXT, hglobal_copy))
+ {
+ success = TRUE;
+ }
+ }
+ }
+
+ CloseClipboard();
+ }
+
+ return success;
+}
+
+// Constrains the mouse to the window.
+void LLWindowWin32::setMouseClipping( BOOL b )
+{
+ if( b != mIsMouseClipping )
+ {
+ BOOL success = FALSE;
+
+ if( b )
+ {
+ GetClipCursor( &mOldMouseClip );
+
+ RECT client_rect_in_screen_space;
+ if( getClientRectInScreenSpace( &client_rect_in_screen_space ) )
+ {
+ success = ClipCursor( &client_rect_in_screen_space );
+ }
+ }
+ else
+ {
+ // Must restore the old mouse clip, which may be set by another window.
+ success = ClipCursor( &mOldMouseClip );
+ SetRect( &mOldMouseClip, 0, 0, 0, 0 );
+ }
+
+ if( success )
+ {
+ mIsMouseClipping = b;
+ }
+ }
+}
+
+BOOL LLWindowWin32::getClientRectInScreenSpace( RECT* rectp )
+{
+ BOOL success = FALSE;
+
+ RECT client_rect;
+ if( mWindowHandle && GetClientRect(mWindowHandle, &client_rect) )
+ {
+ POINT top_left;
+ top_left.x = client_rect.left;
+ top_left.y = client_rect.top;
+ ClientToScreen(mWindowHandle, &top_left);
+
+ POINT bottom_right;
+ bottom_right.x = client_rect.right;
+ bottom_right.y = client_rect.bottom;
+ ClientToScreen(mWindowHandle, &bottom_right);
+
+ SetRect( rectp,
+ top_left.x,
+ top_left.y,
+ bottom_right.x,
+ bottom_right.y );
+
+ success = TRUE;
+ }
+
+ return success;
+}
+
+
+BOOL LLWindowWin32::sendEmail(const char* address, const char* subject, const char* body_text,
+ const char* attachment, const char* attachment_displayed_name )
+{
+ // Based on "A SendMail() DLL" by Greg Turner, Windows Developer Magazine, Nov. 1997.
+ // See article for use of GetProcAddress
+ // No restrictions on use.
+
+ enum SendResult
+ {
+ LL_EMAIL_SUCCESS,
+ LL_EMAIL_MAPI_NOT_INSTALLED, // No MAPI Server (eg Microsoft Exchange) installed
+ LL_EMAIL_MAPILOAD_FAILED, // Load of MAPI32.DLL failed
+ LL_EMAIL_SEND_FAILED // The message send itself failed
+ };
+
+ SendResult result = LL_EMAIL_SUCCESS;
+
+ U32 mapi_installed = GetProfileInt(L"Mail", L"MAPI", 0);
+ if( !mapi_installed)
+ {
+ result = LL_EMAIL_MAPI_NOT_INSTALLED;
+ }
+ else
+ {
+ HINSTANCE hMAPIInst = LoadLibrary(L"MAPI32.DLL");
+ if(!hMAPIInst)
+ {
+ result = LL_EMAIL_MAPILOAD_FAILED;
+ }
+ else
+ {
+ LPMAPISENDMAIL pMAPISendMail = (LPMAPISENDMAIL) GetProcAddress(hMAPIInst, "MAPISendMail");
+
+ // Send the message
+ MapiRecipDesc recipients[1];
+ recipients[0].ulReserved = 0;
+ recipients[0].ulRecipClass = MAPI_TO;
+ recipients[0].lpszName = (char*)address;
+ recipients[0].lpszAddress = (char*)address;
+ recipients[0].ulEIDSize = 0;
+ recipients[0].lpEntryID = 0;
+
+ MapiFileDesc files[1];
+ files[0].ulReserved = 0;
+ files[0].flFlags = 0; // non-OLE file
+ files[0].nPosition = -1; // Leave file location in email unspecified.
+ files[0].lpszPathName = (char*)attachment; // Must be fully qualified name, including drive letter.
+ files[0].lpszFileName = (char*)attachment_displayed_name; // If NULL, uses attachment as displayed name.
+ files[0].lpFileType = NULL; // Recipient will have to figure out what kind of file this is.
+
+ MapiMessage msg;
+ memset(&msg, 0, sizeof(msg));
+ msg.lpszSubject = (char*)subject; // may be NULL
+ msg.lpszNoteText = (char*)body_text;
+ msg.nRecipCount = address ? 1 : 0;
+ msg.lpRecips = address ? recipients : NULL;
+ msg.nFileCount = attachment ? 1 : 0;
+ msg.lpFiles = attachment ? files : NULL;
+
+ U32 success = pMAPISendMail(0, (U32) mWindowHandle, &msg, MAPI_DIALOG|MAPI_LOGON_UI|MAPI_NEW_SESSION, 0);
+ if(success != SUCCESS_SUCCESS)
+ {
+ result = LL_EMAIL_SEND_FAILED;
+ }
+
+ FreeLibrary(hMAPIInst);
+ }
+ }
+
+ return result == LL_EMAIL_SUCCESS;
+}
+
+
+S32 LLWindowWin32::stat(const char* file_name, struct stat* stat_info)
+{
+ llassert( sizeof(struct stat) == sizeof(struct _stat) ); // They are defined identically in sys/stat.h, but I'm paranoid.
+ return LLFile::stat( file_name, (struct _stat*) stat_info );
+}
+
+void LLWindowWin32::flashIcon(F32 seconds)
+{
+ FLASHWINFO flash_info;
+
+ flash_info.cbSize = sizeof(FLASHWINFO);
+ flash_info.hwnd = mWindowHandle;
+ flash_info.dwFlags = FLASHW_TRAY;
+ flash_info.uCount = UINT(seconds / ICON_FLASH_TIME);
+ flash_info.dwTimeout = DWORD(1000.f * ICON_FLASH_TIME); // milliseconds
+ FlashWindowEx(&flash_info);
+}
+
+F32 LLWindowWin32::getGamma()
+{
+ return mCurrentGamma;
+}
+
+BOOL LLWindowWin32::restoreGamma()
+{
+ return SetDeviceGammaRamp(mhDC, mPrevGammaRamp);
+}
+
+BOOL LLWindowWin32::setGamma(const F32 gamma)
+{
+ mCurrentGamma = gamma;
+
+ llinfos << "Setting gamma to " << gamma << llendl;
+
+ for ( int i = 0; i < 256; ++i )
+ {
+ int mult = 256 - ( int ) ( ( gamma - 1.0f ) * 128.0f );
+
+ int value = mult * i;
+
+ if ( value > 0xffff )
+ value = 0xffff;
+
+ mCurrentGammaRamp [ 0 * 256 + i ] =
+ mCurrentGammaRamp [ 1 * 256 + i ] =
+ mCurrentGammaRamp [ 2 * 256 + i ] = ( WORD )value;
+ };
+
+ return SetDeviceGammaRamp ( mhDC, mCurrentGammaRamp );
+}
+
+LLWindow::LLWindowResolution* LLWindowWin32::getSupportedResolutions(S32 &num_resolutions)
+{
+ if (!mSupportedResolutions)
+ {
+ mSupportedResolutions = new LLWindowResolution[MAX_NUM_RESOLUTIONS];
+ DEVMODE dev_mode;
+
+ mNumSupportedResolutions = 0;
+ for (S32 mode_num = 0; mNumSupportedResolutions < MAX_NUM_RESOLUTIONS; mode_num++)
+ {
+ if (!EnumDisplaySettings(NULL, mode_num, &dev_mode))
+ {
+ break;
+ }
+
+ if (dev_mode.dmBitsPerPel == BITS_PER_PIXEL &&
+ dev_mode.dmPelsWidth >= 800 &&
+ dev_mode.dmPelsHeight >= 600)
+ {
+ BOOL resolution_exists = FALSE;
+ for(S32 i = 0; i < mNumSupportedResolutions; i++)
+ {
+ if (mSupportedResolutions[i].mWidth == dev_mode.dmPelsWidth &&
+ mSupportedResolutions[i].mHeight == dev_mode.dmPelsHeight)
+ {
+ resolution_exists = TRUE;
+ }
+ }
+ if (!resolution_exists)
+ {
+ mSupportedResolutions[mNumSupportedResolutions].mWidth = dev_mode.dmPelsWidth;
+ mSupportedResolutions[mNumSupportedResolutions].mHeight = dev_mode.dmPelsHeight;
+ mNumSupportedResolutions++;
+ }
+ }
+ }
+ }
+
+ num_resolutions = mNumSupportedResolutions;
+ return mSupportedResolutions;
+}
+
+
+F32 LLWindowWin32::getNativeAspectRatio()
+{
+ if (mOverrideAspectRatio > 0.f)
+ {
+ return mOverrideAspectRatio;
+ }
+ else if (mNativeAspectRatio > 0.f)
+ {
+ // we grabbed this value at startup, based on the user's desktop settings
+ return mNativeAspectRatio;
+ }
+ // RN: this hack presumes that the largest supported resolution is monitor-limited
+ // and that pixels in that mode are square, therefore defining the native aspect ratio
+ // of the monitor...this seems to work to a close approximation for most CRTs/LCDs
+ S32 num_resolutions;
+ LLWindowResolution* resolutions = getSupportedResolutions(num_resolutions);
+
+ return ((F32)resolutions[num_resolutions - 1].mWidth / (F32)resolutions[num_resolutions - 1].mHeight);
+}
+
+F32 LLWindowWin32::getPixelAspectRatio()
+{
+ F32 pixel_aspect = 1.f;
+ if (getFullscreen())
+ {
+ LLCoordScreen screen_size;
+ getSize(&screen_size);
+ pixel_aspect = getNativeAspectRatio() * (F32)screen_size.mY / (F32)screen_size.mX;
+ }
+
+ return pixel_aspect;
+}
+
+// Change display resolution. Returns true if successful.
+// protected
+BOOL LLWindowWin32::setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh)
+{
+ DEVMODE dev_mode;
+ dev_mode.dmSize = sizeof(dev_mode);
+ BOOL success = FALSE;
+
+ // Don't change anything if we don't have to
+ if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode))
+ {
+ if (dev_mode.dmPelsWidth == width &&
+ dev_mode.dmPelsHeight == height &&
+ dev_mode.dmBitsPerPel == bits &&
+ dev_mode.dmDisplayFrequency == refresh )
+ {
+ // ...display mode identical, do nothing
+ return TRUE;
+ }
+ }
+
+ memset(&dev_mode, 0, sizeof(dev_mode));
+ dev_mode.dmSize = sizeof(dev_mode);
+ dev_mode.dmPelsWidth = width;
+ dev_mode.dmPelsHeight = height;
+ dev_mode.dmBitsPerPel = bits;
+ dev_mode.dmDisplayFrequency = refresh;
+ dev_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
+
+ // CDS_FULLSCREEN indicates that this is a temporary change to the device mode.
+ LONG cds_result = ChangeDisplaySettings(&dev_mode, CDS_FULLSCREEN);
+
+ success = (DISP_CHANGE_SUCCESSFUL == cds_result);
+
+ if (!success)
+ {
+ llwarns << "setDisplayResolution failed, "
+ << width << "x" << height << "x" << bits << " @ " << refresh << llendl;
+ }
+
+ return success;
+}
+
+// protected
+BOOL LLWindowWin32::setFullscreenResolution()
+{
+ if (mFullscreen)
+ {
+ return setDisplayResolution( mFullscreenWidth, mFullscreenHeight, mFullscreenBits, mFullscreenRefresh);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// protected
+BOOL LLWindowWin32::resetDisplayResolution()
+{
+ llinfos << "resetDisplayResolution START" << llendl;
+
+ LONG cds_result = ChangeDisplaySettings(NULL, 0);
+
+ BOOL success = (DISP_CHANGE_SUCCESSFUL == cds_result);
+
+ if (!success)
+ {
+ llwarns << "resetDisplayResolution failed" << llendl;
+ }
+
+ llinfos << "resetDisplayResolution END" << llendl;
+
+ return success;
+}
+
+void LLWindowWin32::swapBuffers()
+{
+ SwapBuffers(mhDC);
+}
+
+
+BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance,
+ VOID* pContext )
+{
+ HRESULT hr;
+
+ // Obtain an interface to the enumerated joystick.
+ hr = g_pDI->CreateDevice( pdidInstance->guidInstance, &g_pJoystick, NULL );
+
+ // If it failed, then we can't use this joystick. (Maybe the user unplugged
+ // it while we were in the middle of enumerating it.)
+ if( FAILED(hr) )
+ return DIENUM_CONTINUE;
+
+ // Stop enumeration. Note: we're just taking the first joystick we get. You
+ // could store all the enumerated joysticks and let the user pick.
+ return DIENUM_STOP;
+}
+
+BOOL CALLBACK EnumObjectsCallback( const DIDEVICEOBJECTINSTANCE* pdidoi,
+ VOID* pContext )
+{
+ if( pdidoi->dwType & DIDFT_AXIS )
+ {
+ DIPROPRANGE diprg;
+ diprg.diph.dwSize = sizeof(DIPROPRANGE);
+ diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+ diprg.diph.dwHow = DIPH_BYID;
+ diprg.diph.dwObj = pdidoi->dwType; // Specify the enumerated axis
+ diprg.lMin = -1000;
+ diprg.lMax = +1000;
+
+ // Set the range for the axis
+ if( FAILED( g_pJoystick->SetProperty( DIPROP_RANGE, &diprg.diph ) ) )
+ return DIENUM_STOP;
+
+ }
+ return DIENUM_CONTINUE;
+}
+
+void LLWindowWin32::updateJoystick( )
+{
+ HRESULT hr;
+ DIJOYSTATE js; // DInput joystick state
+
+ if (!g_pJoystick)
+ return;
+ hr = g_pJoystick->Poll();
+ if ( hr == DIERR_INPUTLOST )
+ {
+ hr = g_pJoystick->Acquire();
+ return;
+ }
+ else if ( FAILED(hr) )
+ return;
+
+ // Get the input's device state
+ if( FAILED( hr = g_pJoystick->GetDeviceState( sizeof(DIJOYSTATE), &js ) ) )
+ return; // The device should have been acquired during the Poll()
+
+ if (js.lX <= -500)
+ {
+ if (!(mJoyStickState & 0x1))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_LEFT, 0);
+ mJoyStickState |= 0x1;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x1)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_LEFT, 0);
+ mJoyStickState &= ~0x1;
+ }
+ }
+ if (js.lX >= 500)
+ {
+ if (!(mJoyStickState & 0x2))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_RIGHT, 0);
+ mJoyStickState |= 0x2;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x2)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_RIGHT, 0);
+ mJoyStickState &= ~0x2;
+ }
+ }
+ if (js.lY <= -500)
+ {
+ if (!(mJoyStickState & 0x4))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_UP, 0);
+ mJoyStickState |= 0x4;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x4)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_UP, 0);
+ mJoyStickState &= ~0x4;
+ }
+ }
+ if (js.lY >= 500)
+ {
+ if (!(mJoyStickState & 0x8))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_PAD_DOWN, 0);
+ mJoyStickState |= 0x8;
+ }
+ }
+ else
+ {
+ if (mJoyStickState & 0x8)
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_PAD_DOWN, 0);
+ mJoyStickState &= ~0x8;
+ }
+ }
+
+ for( int i = 0; i < 15; i++ )
+ {
+ if ( js.rgbButtons[i] & 0x80 )
+ {
+ if (!(mJoyButtonState & (1<<i)))
+ {
+ gKeyboard->handleTranslatedKeyDown(KEY_BUTTON1+i, 0);
+ mJoyButtonState |= (1<<i);
+ }
+ }
+ else
+ {
+ if (mJoyButtonState & (1<<i))
+ {
+ gKeyboard->handleTranslatedKeyUp(KEY_BUTTON1+i, 0);
+ mJoyButtonState &= ~(1<<i);
+ }
+ }
+ }
+}
+
+
+//
+// LLSplashScreenImp
+//
+LLSplashScreenWin32::LLSplashScreenWin32()
+: mWindow(NULL)
+{
+}
+
+LLSplashScreenWin32::~LLSplashScreenWin32()
+{
+}
+
+void LLSplashScreenWin32::showImpl()
+{
+ // This appears to work. ???
+ HINSTANCE hinst = GetModuleHandle(NULL);
+
+ mWindow = CreateDialog(hinst,
+ TEXT("SPLASHSCREEN"),
+ NULL, // no parent
+ (DLGPROC) LLSplashScreenWin32::windowProc);
+ ShowWindow(mWindow, SW_SHOW);
+}
+
+
+void LLSplashScreenWin32::updateImpl(const char *mesg)
+{
+ if (!mWindow) return;
+
+ WCHAR w_mesg[1024];
+ mbstowcs(w_mesg, mesg, 1024);
+
+ SendDlgItemMessage(mWindow,
+ 666, // HACK: text id
+ WM_SETTEXT,
+ FALSE,
+ (LPARAM)w_mesg);
+}
+
+
+void LLSplashScreenWin32::hideImpl()
+{
+ if (mWindow)
+ {
+ DestroyWindow(mWindow);
+ mWindow = NULL;
+ }
+}
+
+
+// static
+LRESULT CALLBACK LLSplashScreenWin32::windowProc(HWND h_wnd, UINT u_msg,
+ WPARAM w_param, LPARAM l_param)
+{
+ // Just give it to windows
+ return DefWindowProc(h_wnd, u_msg, w_param, l_param);
+}
+
+//
+// Helper Funcs
+//
+
+HWND llwindow_get_hwnd(LLWindow *window)
+{
+ //assumes we are dealing with a Win32 window
+ return ((LLWindowWin32*)window)->mWindowHandle;
+}
+
+
+void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc)
+{
+ //assumes we are dealing with a Win32 window
+ ((LLWindowWin32*)window)->mWndProc = wnd_proc;
+}
+
+S32 OSMessageBoxWin32(const char* text, const char* caption, U32 type)
+{
+ UINT uType;
+
+ switch(type)
+ {
+ case OSMB_OK:
+ uType = MB_OK;
+ break;
+ case OSMB_OKCANCEL:
+ uType = MB_OKCANCEL;
+ break;
+ case OSMB_YESNO:
+ uType = MB_YESNO;
+ break;
+ default:
+ uType = MB_OK;
+ break;
+ }
+
+ // HACK! Doesn't properly handle wide strings!
+ int retval_win = MessageBoxA(NULL, text, caption, uType);
+ S32 retval;
+
+ switch(retval_win)
+ {
+ case IDYES:
+ retval = OSBTN_YES;
+ break;
+ case IDNO:
+ retval = OSBTN_NO;
+ break;
+ case IDOK:
+ retval = OSBTN_OK;
+ break;
+ case IDCANCEL:
+ retval = OSBTN_CANCEL;
+ break;
+ default:
+ retval = OSBTN_CANCEL;
+ break;
+ }
+
+ return retval;
+}
+
+
+void spawn_web_browser(const char* escaped_url )
+{
+ bool found = false;
+ S32 i;
+ for (i = 0; i < gURLProtocolWhitelistCount; i++)
+ {
+ S32 len = strlen(gURLProtocolWhitelist[i]);
+ if (!strncmp(escaped_url, gURLProtocolWhitelist[i], len)
+ && escaped_url[len] == ':')
+ {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ llwarns << "spawn_web_browser() called for url with protocol not on whitelist: " << escaped_url << llendl;
+ return;
+ }
+
+ llinfos << "Opening URL " << escaped_url << llendl;
+
+ // Figure out the user's default web browser
+ // HKEY_CLASSES_ROOT\http\shell\open\command
+ char reg_path_str[256];
+ sprintf(reg_path_str, "%s\\shell\\open\\command", gURLProtocolWhitelistHandler[i]);
+ WCHAR reg_path_wstr[256];
+ mbstowcs(reg_path_wstr, reg_path_str, 1024);
+
+ HKEY key;
+ WCHAR browser_open_wstr[1024];
+ DWORD buffer_length = 1024;
+ RegOpenKeyEx(HKEY_CLASSES_ROOT, reg_path_wstr, 0, KEY_QUERY_VALUE, &key);
+ RegQueryValueEx(key, NULL, NULL, NULL, (LPBYTE)browser_open_wstr, &buffer_length);
+ RegCloseKey(key);
+
+ // Convert to STL string
+ LLWString browser_open_wstring = utf16str_to_wstring(browser_open_wstr);
+
+ if (browser_open_wstring.length() < 2)
+ {
+ llwarns << "Invalid browser executable in registry " << browser_open_wstring << llendl;
+ return;
+ }
+
+ // Extract the process that's supposed to be launched
+ LLWString browser_executable;
+ if (browser_open_wstring[0] == '"')
+ {
+ // executable is quoted, find the matching quote
+ size_t quote_pos = browser_open_wstring.find('"', 1);
+ // copy out the string including both quotes
+ browser_executable = browser_open_wstring.substr(0, quote_pos+1);
+ }
+ else
+ {
+ // executable not quoted, find a space
+ size_t space_pos = browser_open_wstring.find(' ', 1);
+ browser_executable = browser_open_wstring.substr(0, space_pos);
+ }
+
+ llinfos << "Browser reg key: " << wstring_to_utf8str(browser_open_wstring) << llendl;
+ llinfos << "Browser executable: " << wstring_to_utf8str(browser_executable) << llendl;
+
+ // Convert URL to wide string for Windows API
+ // Assume URL is UTF8, as can come from scripts
+ LLWString url_wstring = utf8str_to_wstring(escaped_url);
+ llutf16string url_utf16 = wstring_to_utf16str(url_wstring);
+
+ // Convert executable and path to wide string for Windows API
+ llutf16string browser_exec_utf16 = wstring_to_utf16str(browser_executable);
+
+ // ShellExecute returns HINSTANCE for backwards compatiblity.
+ // MS docs say to cast to int and compare to 32.
+ HWND our_window = NULL;
+ LPCWSTR directory_wstr = NULL;
+ int retval = (int) ShellExecute(our_window,
+ L"open",
+ browser_exec_utf16.c_str(),
+ url_utf16.c_str(),
+ directory_wstr,
+ SW_SHOWNORMAL);
+ if (retval > 32)
+ {
+ llinfos << "load_url success with " << retval << llendl;
+ }
+ else
+ {
+ llinfos << "load_url failure with " << retval << llendl;
+ }
+}
+
+void shell_open( const char* file_path )
+{
+ llinfos << "Opening " << file_path << llendl;
+
+ WCHAR wstr[1024];
+ mbstowcs(wstr, file_path, 1024);
+
+ HWND our_window = NULL;
+ int retval = (int) ShellExecute(our_window, L"open", wstr, NULL, NULL, SW_SHOWNORMAL);
+ if (retval > 32)
+ {
+ llinfos << "ShellExecute success with " << retval << llendl;
+ }
+ else
+ {
+ llinfos << "ShellExecute failure with " << retval << llendl;
+ }
+}
+
+BOOL LLWindowWin32::dialog_color_picker ( F32 *r, F32 *g, F32 *b )
+{
+ BOOL retval = FALSE;
+
+ static CHOOSECOLOR cc;
+ static COLORREF crCustColors[16];
+ cc.lStructSize = sizeof(CHOOSECOLOR);
+ cc.hwndOwner = mWindowHandle;
+ cc.hInstance = NULL;
+ cc.rgbResult = RGB ((*r * 255.f),(*g *255.f),(*b * 255.f));
+ //cc.rgbResult = RGB (0x80,0x80,0x80);
+ cc.lpCustColors = crCustColors;
+ cc.Flags = CC_RGBINIT | CC_FULLOPEN;
+ cc.lCustData = 0;
+ cc.lpfnHook = NULL;
+ cc.lpTemplateName = NULL;
+
+ // This call is modal, so pause agent
+ //send_agent_pause(); // this is in newview and we don't want to set up a dependency
+ {
+ retval = ChooseColor(&cc);
+ }
+ //send_agent_resume(); // this is in newview and we don't want to set up a dependency
+
+ *b = ((F32)((cc.rgbResult >> 16) & 0xff)) / 255.f;
+
+ *g = ((F32)((cc.rgbResult >> 8) & 0xff)) / 255.f;
+
+ *r = ((F32)(cc.rgbResult & 0xff)) / 255.f;
+
+ return (retval);
+}
+
+void *LLWindowWin32::getPlatformWindow()
+{
+ return (void*)mWindowHandle;
+}
+
+void LLWindowWin32::bringToFront()
+{
+ BringWindowToTop(mWindowHandle);
+}
+
+// set (OS) window focus back to the client
+void LLWindowWin32::focusClient()
+{
+ SetFocus ( mWindowHandle );
+};
+
+#endif // LL_WINDOWS
diff --git a/indra/llwindow/llwindowwin32.h b/indra/llwindow/llwindowwin32.h
new file mode 100644
index 0000000000..6803ad6f2a
--- /dev/null
+++ b/indra/llwindow/llwindowwin32.h
@@ -0,0 +1,187 @@
+/**
+ * @file llwindowwin32.h
+ * @brief Windows implementation of LLWindow class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDOWWIN32_H
+#define LL_LLWINDOWWIN32_H
+
+#include "llwindow.h"
+
+// Hack for async host by name
+#define LL_WM_HOST_RESOLVED (WM_APP + 1)
+typedef void (*LLW32MsgCallback)(const MSG &msg);
+
+class LLWindowWin32 : public LLWindow
+{
+public:
+ /*virtual*/ void show();
+ /*virtual*/ void hide();
+ /*virtual*/ void close();
+ /*virtual*/ BOOL getVisible();
+ /*virtual*/ BOOL getMinimized();
+ /*virtual*/ BOOL getMaximized();
+ /*virtual*/ BOOL maximize();
+ /*virtual*/ BOOL getFullscreen();
+ /*virtual*/ BOOL getPosition(LLCoordScreen *position);
+ /*virtual*/ BOOL getSize(LLCoordScreen *size);
+ /*virtual*/ BOOL getSize(LLCoordWindow *size);
+ /*virtual*/ BOOL setPosition(LLCoordScreen position);
+ /*virtual*/ BOOL setSize(LLCoordScreen size);
+ /*virtual*/ BOOL switchContext(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync);
+ /*virtual*/ BOOL setCursorPosition(LLCoordWindow position);
+ /*virtual*/ BOOL getCursorPosition(LLCoordWindow *position);
+ /*virtual*/ void showCursor();
+ /*virtual*/ void hideCursor();
+ /*virtual*/ void showCursorFromMouseMove();
+ /*virtual*/ void hideCursorUntilMouseMove();
+ /*virtual*/ BOOL isCursorHidden();
+ /*virtual*/ void setCursor(ECursorType cursor);
+ /*virtual*/ ECursorType getCursor();
+ /*virtual*/ void captureMouse();
+ /*virtual*/ void releaseMouse();
+ /*virtual*/ void setMouseClipping( BOOL b );
+ /*virtual*/ BOOL isClipboardTextAvailable();
+ /*virtual*/ BOOL pasteTextFromClipboard(LLWString &dst);
+ /*virtual*/ BOOL copyTextToClipboard(const LLWString &src);
+ /*virtual*/ void flashIcon(F32 seconds);
+ /*virtual*/ F32 getGamma();
+ /*virtual*/ BOOL setGamma(const F32 gamma); // Set the gamma
+ /*virtual*/ BOOL restoreGamma(); // Restore original gamma table (before updating gamma)
+ /*virtual*/ ESwapMethod getSwapMethod() { return mSwapMethod; }
+ /*virtual*/ void gatherInput();
+ /*virtual*/ void delayInputProcessing();
+ /*virtual*/ void swapBuffers();
+
+ /*virtual*/ LLString getTempFileName();
+ /*virtual*/ void deleteFile( const char* file_name );
+ /*virtual*/ S32 stat( const char* file_name, struct stat* stat_info );
+ /*virtual*/ BOOL sendEmail(const char* address,const char* subject,const char* body_text,const char* attachment=NULL, const char* attachment_displayed_name=NULL);
+
+
+ // handy coordinate space conversion routines
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordScreen *to);
+ /*virtual*/ BOOL convertCoords(LLCoordWindow from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordWindow *to);
+ /*virtual*/ BOOL convertCoords(LLCoordScreen from, LLCoordGL *to);
+ /*virtual*/ BOOL convertCoords(LLCoordGL from, LLCoordScreen *to);
+
+ /*virtual*/ LLWindowResolution* getSupportedResolutions(S32 &num_resolutions);
+ /*virtual*/ F32 getNativeAspectRatio();
+ /*virtual*/ F32 getPixelAspectRatio();
+ /*virtual*/ void setNativeAspectRatio(F32 ratio) { mOverrideAspectRatio = ratio; }
+
+ /*virtual*/ BOOL dialog_color_picker (F32 *r, F32 *g, F32 *b );
+
+ /*virtual*/ void *getPlatformWindow();
+ /*virtual*/ void bringToFront();
+ /*virtual*/ void focusClient();
+
+protected:
+ LLWindowWin32(
+ char *title, char *name, int x, int y, int width, int height, U32 flags,
+ BOOL fullscreen, BOOL clearBg, BOOL disable_vsync, BOOL use_gl,
+ BOOL ignore_pixel_depth);
+ ~LLWindowWin32();
+
+ void initCursors();
+ HCURSOR loadColorCursor(LPCTSTR name);
+ BOOL isValid();
+ void moveWindow(const LLCoordScreen& position,const LLCoordScreen& size);
+
+
+ // Changes display resolution. Returns true if successful
+ BOOL setDisplayResolution(S32 width, S32 height, S32 bits, S32 refresh);
+
+ // Go back to last fullscreen display resolution.
+ BOOL setFullscreenResolution();
+
+ // Restore the display resolution to its value before we ran the app.
+ BOOL resetDisplayResolution();
+
+ void minimize();
+ void restore();
+
+ BOOL shouldPostQuit() { return mPostQuit; }
+
+
+protected:
+ //
+ // Platform specific methods
+ //
+
+ BOOL getClientRectInScreenSpace(RECT* rectp);
+ void updateJoystick( );
+
+ static LRESULT CALLBACK mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param);
+ static BOOL CALLBACK enumChildWindows(HWND h_wnd, LPARAM l_param);
+
+
+ //
+ // Platform specific variables
+ //
+ WCHAR *mWindowTitle;
+ WCHAR *mWindowClassName;
+
+ HWND mWindowHandle; // window handle
+ HGLRC mhRC; // OpenGL rendering context
+ HDC mhDC; // Windows Device context handle
+ HINSTANCE mhInstance; // handle to application instance
+ WNDPROC mWndProc; // user-installable window proc
+ RECT mOldMouseClip; // Screen rect to which the mouse cursor was globally constrained before we changed it in clipMouse()
+ WPARAM mLastSizeWParam;
+ F32 mOverrideAspectRatio;
+ F32 mNativeAspectRatio;
+
+ HCURSOR mCursor[ UI_CURSOR_COUNT ]; // Array of all mouse cursors
+
+ static BOOL sIsClassRegistered; // has the window class been registered?
+
+ F32 mCurrentGamma;
+ WORD mPrevGammaRamp[256*3];
+ WORD mCurrentGammaRamp[256*3];
+
+ U32 mJoyStickState;
+ U32 mJoyButtonState;
+
+ LPWSTR mIconResource;
+ BOOL mMousePositionModified;
+ BOOL mInputProcessingPaused;
+
+ friend HWND llwindow_get_hwnd(LLWindow *window);
+ friend void llwindow_install_wndproc(LLWindow *window, WNDPROC wnd_proc);
+ friend class LLWindowManager;
+};
+
+class LLSplashScreenWin32 : public LLSplashScreen
+{
+public:
+ LLSplashScreenWin32();
+ virtual ~LLSplashScreenWin32();
+
+ /*virtual*/ void showImpl();
+ /*virtual*/ void updateImpl(const char* mesg);
+ /*virtual*/ void hideImpl();
+
+#if LL_WINDOWS
+ static LRESULT CALLBACK windowProc(HWND h_wnd, UINT u_msg,
+ WPARAM w_param, LPARAM l_param);
+#endif
+
+private:
+#if LL_WINDOWS
+ HWND mWindow;
+#endif
+};
+
+extern LLW32MsgCallback gAsyncMsgCallback;
+
+static void handleMessage( const MSG& msg );
+
+S32 OSMessageBoxWin32(const char* text, const char* caption, U32 type);
+
+#endif //LL_LLWINDOWWIN32_H
diff --git a/indra/llxml/llcontrol.cpp b/indra/llxml/llcontrol.cpp
new file mode 100644
index 0000000000..a9651fafc7
--- /dev/null
+++ b/indra/llxml/llcontrol.cpp
@@ -0,0 +1,1401 @@
+/**
+ * @file llcontrol.cpp
+ * @brief Holds global state for viewer.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <fstream>
+#include <algorithm>
+
+#include "llcontrol.h"
+
+#include "llstl.h"
+
+#include "linked_lists.h"
+#include "llstring.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4coloru.h"
+#include "v4color.h"
+#include "v3color.h"
+#include "llrect.h"
+#include "llxmltree.h"
+#include "llsdserialize.h"
+
+#if LL_RELEASE_FOR_DOWNLOAD
+#define CONTROL_ERRS llwarns
+#else
+#define CONTROL_ERRS llerrs
+#endif
+
+//this defines the current version of the settings file
+U32 LLControlBase::sMaxControlNameLength = 0;
+
+//These lists are used to store the ID's of registered event listeners.
+std::list<S32> LLControlBase::mFreeIDs;
+std::list<S32> LLControlBase::mUsedIDs;
+
+S32 LLControlBase::mTopID;
+
+std::set<LLControlBase*> LLControlBase::mChangedControls;
+
+const S32 CURRENT_VERSION = 101;
+
+BOOL control_insert_before( LLControlBase* first, LLControlBase* second );
+
+BOOL LLControl::llsd_compare(const LLSD& a, const LLSD & b)
+{
+ switch (mType)
+ {
+ case TYPE_U32:
+ case TYPE_S32:
+ return a.asInteger() == b.asInteger();
+ case TYPE_BOOLEAN:
+ return a.asBoolean() == b.asBoolean();
+ case TYPE_F32:
+ return a.asReal() == b.asReal();
+ case TYPE_VEC3:
+ case TYPE_VEC3D:
+ return LLVector3d(a) == LLVector3d(b);
+ case TYPE_RECT:
+ return LLRect(a) == LLRect(b);
+ case TYPE_COL4:
+ return LLColor4(a) == LLColor4(b);
+ case TYPE_COL3:
+ return LLColor3(a) == LLColor3(b);
+ case TYPE_COL4U:
+ return LLColor4U(a) == LLColor4U(b);
+ case TYPE_STRING:
+ return a.asString() == b.asString();
+ default:
+ // no-op
+ break;
+ }
+
+ return FALSE;
+}
+
+LLControlBase::~LLControlBase()
+{
+}
+
+// virtual
+void LLControlBase::resetToDefault()
+{
+}
+
+LLControlGroup::LLControlGroup(): mNameTable()
+{
+ //mFreeStringOffset = 0;
+}
+
+LLControlGroup::~LLControlGroup()
+{
+}
+
+LLSD LLControlBase::registerListener(LLSimpleListenerObservable *listener, LLSD userdata)
+{
+ // Symmetric listener relationship
+ addListener(listener, "", userdata);
+ listener->addListener(this, "", userdata);
+ return getValue();
+}
+
+void LLControlGroup::cleanup()
+{
+ mNameTable.clear();
+}
+
+LLControlBase* LLControlGroup::getControl(const LLString& name)
+{
+ return mNameTable[name];
+}
+
+BOOL LLControlGroup::declareControl(const LLString& name, eControlType type, const LLSD initial_val, const LLString& comment, BOOL persist)
+{
+ if(!mNameTable[name])
+ {
+ // if not, create the control and add it to the name table
+ LLControl* control = new LLControl(name, type, initial_val, comment, persist);
+ mNameTable[name] = control;
+ return TRUE;
+ } else
+ {
+ llwarns << "LLControlGroup::declareControl: Control named " << name << " already exists." << llendl;
+ return FALSE;
+ }
+}
+
+BOOL LLControlGroup::declareU32(const LLString& name, const U32 initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_U32, (LLSD::Integer) initial_val, comment, persist);
+}
+
+BOOL LLControlGroup::declareS32(const LLString& name, const S32 initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_S32, initial_val, comment, persist);
+}
+
+BOOL LLControlGroup::declareF32(const LLString& name, const F32 initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_F32, initial_val, comment, persist);
+}
+
+BOOL LLControlGroup::declareBOOL(const LLString& name, const BOOL initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_BOOLEAN, initial_val, comment, persist);
+}
+
+BOOL LLControlGroup::declareString(const LLString& name, const LLString& initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_STRING, initial_val, comment, persist);
+}
+
+BOOL LLControlGroup::declareVec3(const LLString& name, const LLVector3 &initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_VEC3, initial_val.getValue(), comment, persist);
+}
+
+BOOL LLControlGroup::declareVec3d(const LLString& name, const LLVector3d &initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_VEC3D, initial_val.getValue(), comment, persist);
+}
+
+BOOL LLControlGroup::declareRect(const LLString& name, const LLRect &initial_val, const LLString& comment, BOOL persist)
+{
+ return declareControl(name, TYPE_RECT, initial_val.getValue(), comment, persist);
+}
+
+BOOL LLControlGroup::declareColor4U(const LLString& name, const LLColor4U &initial_val, const LLString& comment, BOOL persist )
+{
+ return declareControl(name, TYPE_COL4U, initial_val.getValue(), comment, persist);
+}
+
+BOOL LLControlGroup::declareColor4(const LLString& name, const LLColor4 &initial_val, const LLString& comment, BOOL persist )
+{
+ return declareControl(name, TYPE_COL4, initial_val.getValue(), comment, persist);
+}
+
+BOOL LLControlGroup::declareColor3(const LLString& name, const LLColor3 &initial_val, const LLString& comment, BOOL persist )
+{
+ return declareControl(name, TYPE_COL3, initial_val.getValue(), comment, persist);
+}
+
+LLSD LLControlGroup::registerListener(const LLString& name, LLSimpleListenerObservable *listener)
+{
+ LLControlBase *control = mNameTable[name];
+ if (control)
+ {
+ return control->registerListener(listener);
+ }
+ return LLSD();
+}
+
+BOOL LLControlGroup::getBOOL(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_BOOLEAN))
+ return control->get().asBoolean();
+ else
+ {
+ CONTROL_ERRS << "Invalid BOOL control " << name << llendl;
+ return FALSE;
+ }
+}
+
+S32 LLControlGroup::getS32(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_S32))
+ return control->get().asInteger();
+ else
+ {
+ CONTROL_ERRS << "Invalid S32 control " << name << llendl;
+ return 0;
+ }
+}
+
+U32 LLControlGroup::getU32(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_U32))
+ return control->get().asInteger();
+ else
+ {
+ CONTROL_ERRS << "Invalid U32 control " << name << llendl;
+ return 0;
+ }
+}
+
+F32 LLControlGroup::getF32(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_F32))
+ return (F32) control->get().asReal();
+ else
+ {
+ CONTROL_ERRS << "Invalid F32 control " << name << llendl;
+ return 0.0f;
+ }
+}
+
+LLString LLControlGroup::findString(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_STRING))
+ return control->get().asString();
+ return LLString::null;
+}
+
+LLString LLControlGroup::getString(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_STRING))
+ return control->get().asString();
+ else
+ {
+ CONTROL_ERRS << "Invalid string control " << name << llendl;
+ return LLString::null;
+ }
+}
+
+LLWString LLControlGroup::getWString(const LLString& name)
+{
+ return utf8str_to_wstring(getString(name));
+}
+
+LLString LLControlGroup::getText(const LLString& name)
+{
+ LLString utf8_string = getString(name);
+ LLString::replaceChar(utf8_string, '^', '\n');
+ LLString::replaceChar(utf8_string, '%', ' ');
+ return (utf8_string);
+}
+
+LLVector3 LLControlGroup::getVector3(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_VEC3))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid LLVector3 control " << name << llendl;
+ return LLVector3::zero;
+ }
+}
+
+LLVector3d LLControlGroup::getVector3d(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_VEC3D))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid LLVector3d control " << name << llendl;
+ return LLVector3d::zero;
+ }
+}
+
+LLRect LLControlGroup::getRect(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_RECT))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid rect control " << name << llendl;
+ return LLRect::null;
+ }
+}
+
+
+LLColor4 LLControlGroup::getColor(const LLString& name)
+{
+ ctrl_name_table_t::const_iterator i = mNameTable.find(name);
+
+ if (i != mNameTable.end())
+ {
+ LLControlBase* control = i->second;
+
+ switch(control->mType)
+ {
+ case TYPE_COL4:
+ {
+ return LLColor4(control->get());
+ }
+ case TYPE_COL4U:
+ {
+ return LLColor4(LLColor4U(control->get()));
+ }
+ default:
+ {
+ CONTROL_ERRS << "Control " << name << " not a color" << llendl;
+ return LLColor4::white;
+ }
+ }
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid getColor control " << name << llendl;
+ return LLColor4::white;
+ }
+}
+
+LLColor4U LLControlGroup::getColor4U(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_COL4U))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl;
+ return LLColor4U::white;
+ }
+}
+
+LLColor4 LLControlGroup::getColor4(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_COL4))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl;
+ return LLColor4::white;
+ }
+}
+
+LLColor3 LLControlGroup::getColor3(const LLString& name)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_COL3))
+ return control->get();
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor3 control " << name << llendl;
+ return LLColor3::white;
+ }
+}
+
+BOOL LLControlGroup::controlExists(const LLString& name)
+{
+ void *control = mNameTable[name];
+
+ return (control != 0);
+}
+
+//-------------------------------------------------------------------
+// Set functions
+//-------------------------------------------------------------------
+
+void LLControlGroup::setBOOL(const LLString& name, BOOL val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_BOOLEAN))
+ {
+ control->set(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+
+void LLControlGroup::setS32(const LLString& name, S32 val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_S32))
+ {
+ control->set(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+
+void LLControlGroup::setF32(const LLString& name, F32 val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_F32))
+ {
+ control->set(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+
+void LLControlGroup::setU32(const LLString& name, U32 val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_U32))
+ {
+ control->set((LLSD::Integer) val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+
+void LLControlGroup::setString(const LLString& name, const LLString &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_STRING))
+ {
+ control->set(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+
+void LLControlGroup::setVector3(const LLString& name, const LLVector3 &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_VEC3))
+ {
+ control->set(val.getValue());
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+void LLControlGroup::setVector3d(const LLString& name, const LLVector3d &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_VEC3D))
+ {
+ control->set(val.getValue());
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+void LLControlGroup::setRect(const LLString& name, const LLRect &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_RECT))
+ {
+ control->set(val.getValue());
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid rect control " << name << llendl;
+ }
+}
+
+void LLControlGroup::setColor4U(const LLString& name, const LLColor4U &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_COL4U))
+ {
+ control->set(val.getValue());
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl;
+ }
+}
+
+void LLControlGroup::setColor4(const LLString& name, const LLColor4 &val)
+{
+ LLControlBase* control = mNameTable[name];
+
+ if (control && control->isType(TYPE_COL4))
+ {
+ control->set(val.getValue());
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid LLColor4 control " << name << llendl;
+ }
+}
+
+void LLControlGroup::setValue(const LLString& name, const LLSD& val)
+{
+ if (name.empty())
+ {
+ return;
+ }
+
+ LLControlBase* control = mNameTable[name];
+
+ if (control)
+ {
+ control->set(val);
+ }
+ else
+ {
+ CONTROL_ERRS << "Invalid control " << name << llendl;
+ }
+}
+
+//---------------------------------------------------------------
+// Load and save
+//---------------------------------------------------------------
+
+U32 LLControlGroup::loadFromFileLegacy(const LLString& filename, BOOL require_declaration, eControlType declare_as)
+{
+ U32 item = 0;
+ U32 validitems = 0;
+ llifstream file;
+ S32 version;
+
+ file.open(filename.c_str());
+
+ if (!file)
+ {
+ llinfos << "LLControlGroup::loadFromFile unable to open." << llendl;
+ return 0;
+ }
+
+ // Check file version
+ LLString name;
+ file >> name;
+ file >> version;
+ if (name != "version" || version != CURRENT_VERSION)
+ {
+ llinfos << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << llendl;
+ return 0;
+ }
+
+ while (!file.eof())
+ {
+ file >> name;
+
+ if (name.empty())
+ {
+ continue;
+ }
+
+ if (name.substr(0,2) == "//")
+ {
+ // This is a comment.
+ char buffer[MAX_STRING];
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ BOOL declared = mNameTable.find(name) != mNameTable.end();
+
+ if (require_declaration && !declared)
+ {
+ // Declaration required, but this name not declared.
+ // Complain about non-empty names.
+ if (!name.empty())
+ {
+ //read in to end of line
+ char buffer[MAX_STRING];
+ file.getline(buffer, MAX_STRING);
+ llwarns << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << llendl;
+ }
+ continue;
+ }
+
+ // Got an item. Load it up.
+ item++;
+
+ // If not declared, assume it's a string
+ if (!declared)
+ {
+ switch(declare_as)
+ {
+ case TYPE_COL4:
+ declareColor4(name, LLColor4::white, LLString::null, NO_PERSIST);
+ break;
+ case TYPE_COL4U:
+ declareColor4U(name, LLColor4U::white, LLString::null, NO_PERSIST);
+ break;
+ case TYPE_STRING:
+ default:
+ declareString(name, LLString::null, LLString::null, NO_PERSIST);
+ break;
+ }
+ }
+
+ // Control name has been declared in code.
+ LLControlBase *control = getControl(name);
+
+ llassert(control);
+
+ switch(control->mType)
+ {
+ case TYPE_F32:
+ {
+ F32 initial;
+
+ file >> initial;
+
+ control->set(initial);
+ validitems++;
+ }
+ break;
+ case TYPE_S32:
+ {
+ S32 initial;
+
+ file >> initial;
+
+ control->set(initial);
+ validitems++;
+ }
+ break;
+ case TYPE_U32:
+ {
+ U32 initial;
+
+ file >> initial;
+ control->set((LLSD::Integer) initial);
+ validitems++;
+ }
+ break;
+ case TYPE_BOOLEAN:
+ {
+ char boolstring[256];
+ BOOL valid = FALSE;
+ BOOL initial = FALSE;
+
+ file >> boolstring;
+ if (!strcmp("TRUE", boolstring))
+ {
+ initial = TRUE;
+ valid = TRUE;
+ }
+ else if (!strcmp("FALSE", boolstring))
+ {
+ initial = FALSE;
+ valid = TRUE;
+ }
+
+ if (valid)
+ {
+ control->set(initial);
+ }
+ else
+ {
+ llinfos << filename << "Item " << item << ": Invalid BOOL control " << name << ", " << boolstring << llendl;
+ }
+
+ validitems++;
+ }
+ break;
+ case TYPE_STRING:
+ {
+ LLString string;
+
+ file >> string;
+
+ control->set(string);
+ validitems++;
+ }
+ break;
+ case TYPE_VEC3:
+ {
+ F32 x, y, z;
+
+ file >> x >> y >> z;
+
+ LLVector3 vector(x, y, z);
+
+ control->set(vector.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_VEC3D:
+ {
+ F64 x, y, z;
+
+ file >> x >> y >> z;
+
+ LLVector3d vector(x, y, z);
+
+ control->set(vector.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_RECT:
+ {
+ S32 left, bottom, width, height;
+
+ file >> left >> bottom >> width >> height;
+
+ LLRect rect;
+ rect.setOriginAndSize(left, bottom, width, height);
+
+ control->set(rect.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL4U:
+ {
+ S32 red, green, blue, alpha;
+ LLColor4U color;
+ file >> red >> green >> blue >> alpha;
+ color.setVec(red, green, blue, alpha);
+ control->set(color.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL4:
+ {
+ LLColor4 color;
+ file >> color.mV[VRED] >> color.mV[VGREEN]
+ >> color.mV[VBLUE] >> color.mV[VALPHA];
+ control->set(color.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL3:
+ {
+ LLColor3 color;
+ file >> color.mV[VRED] >> color.mV[VGREEN]
+ >> color.mV[VBLUE];
+ control->set(color.getValue());
+ validitems++;
+ }
+ break;
+ }
+ }
+
+ file.close();
+
+ return validitems;
+}
+
+// Returns number of controls loaded, so 0 if failure
+U32 LLControlGroup::loadFromFile(const LLString& filename, BOOL require_declaration, eControlType declare_as)
+{
+ LLString name;
+
+ LLXmlTree xml_controls;
+
+ if (!xml_controls.parseFile(filename))
+ {
+ llwarns << "Unable to open control file " << filename << llendl;
+ return 0;
+ }
+
+ LLXmlTreeNode* rootp = xml_controls.getRoot();
+ if (!rootp || !rootp->hasAttribute("version"))
+ {
+ llwarns << "No valid settings header found in control file " << filename << llendl;
+ return 0;
+ }
+
+ U32 item = 0;
+ U32 validitems = 0;
+ S32 version;
+
+ rootp->getAttributeS32("version", version);
+
+ // Check file version
+ if (version != CURRENT_VERSION)
+ {
+ llinfos << filename << " does not appear to be a version " << CURRENT_VERSION << " controls file" << llendl;
+ return 0;
+ }
+
+ LLXmlTreeNode* child_nodep = rootp->getFirstChild();
+ while(child_nodep)
+ {
+ name = child_nodep->getName();
+
+ BOOL declared = (mNameTable[name].notNull());
+
+ if (require_declaration && !declared)
+ {
+ // Declaration required, but this name not declared.
+ // Complain about non-empty names.
+ if (!name.empty())
+ {
+ //read in to end of line
+ llwarns << "LLControlGroup::loadFromFile() : Trying to set \"" << name << "\", setting doesn't exist." << llendl;
+ }
+ child_nodep = rootp->getNextChild();
+ continue;
+ }
+
+ // Got an item. Load it up.
+ item++;
+
+ // If not declared, assume it's a string
+ if (!declared)
+ {
+ switch(declare_as)
+ {
+ case TYPE_COL4:
+ declareColor4(name, LLColor4::white, "", NO_PERSIST);
+ break;
+ case TYPE_COL4U:
+ declareColor4U(name, LLColor4U::white, "", NO_PERSIST);
+ break;
+ case TYPE_STRING:
+ default:
+ declareString(name, LLString::null, "", NO_PERSIST);
+ break;
+ }
+ }
+
+ // Control name has been declared in code.
+ LLControlBase *control = getControl(name);
+
+ llassert(control);
+
+ switch(control->mType)
+ {
+ case TYPE_F32:
+ {
+ F32 initial = 0.f;
+
+ child_nodep->getAttributeF32("value", initial);
+
+ control->set(initial);
+ validitems++;
+ }
+ break;
+ case TYPE_S32:
+ {
+ S32 initial = 0;
+
+ child_nodep->getAttributeS32("value", initial);
+
+ control->set(initial);
+ validitems++;
+ }
+ break;
+ case TYPE_U32:
+ {
+ U32 initial = 0;
+ child_nodep->getAttributeU32("value", initial);
+ control->set((LLSD::Integer) initial);
+ validitems++;
+ }
+ break;
+ case TYPE_BOOLEAN:
+ {
+ BOOL initial = FALSE;
+
+ child_nodep->getAttributeBOOL("value", initial);
+ control->set(initial);
+
+ validitems++;
+ }
+ break;
+ case TYPE_STRING:
+ {
+ LLString string;
+ child_nodep->getAttributeString("value", string);
+ if (string == LLString::null)
+ {
+ string = "";
+ }
+ control->set(string);
+ validitems++;
+ }
+ break;
+ case TYPE_VEC3:
+ {
+ LLVector3 vector;
+
+ child_nodep->getAttributeVector3("value", vector);
+ control->set(vector.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_VEC3D:
+ {
+ LLVector3d vector;
+
+ child_nodep->getAttributeVector3d("value", vector);
+
+ control->set(vector.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_RECT:
+ {
+ //RN: hack to support reading rectangles from a string
+ LLString rect_string;
+
+ child_nodep->getAttributeString("value", rect_string);
+ std::istringstream istream(rect_string);
+ S32 left, bottom, width, height;
+
+ istream >> left >> bottom >> width >> height;
+
+ LLRect rect;
+ rect.setOriginAndSize(left, bottom, width, height);
+
+ control->set(rect.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL4U:
+ {
+ LLColor4U color;
+
+ child_nodep->getAttributeColor4U("value", color);
+ control->set(color.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL4:
+ {
+ LLColor4 color;
+
+ child_nodep->getAttributeColor4("value", color);
+ control->set(color.getValue());
+ validitems++;
+ }
+ break;
+ case TYPE_COL3:
+ {
+ LLVector3 color;
+
+ child_nodep->getAttributeVector3("value", color);
+ control->set(LLColor3(color.mV).getValue());
+ validitems++;
+ }
+ break;
+ }
+
+ child_nodep = rootp->getNextChild();
+ }
+
+ return validitems;
+}
+
+
+U32 LLControlGroup::saveToFile(const LLString& filename, BOOL nondefault_only)
+{
+ const char ENDL = '\n';
+
+ llinfos << "Saving settings to file: " << filename << llendl;
+
+ // place the objects in a temporary container that enforces a sort
+ // order to ease manual editing of the file
+ LLLinkedList< LLControlBase > controls;
+ controls.setInsertBefore( &control_insert_before );
+ LLString name;
+ for (ctrl_name_table_t::iterator iter = mNameTable.begin();
+ iter != mNameTable.end(); iter++)
+ {
+ name = iter->first;
+ if (name.empty())
+ {
+ CONTROL_ERRS << "Control with no name found!!!" << llendl;
+ break;
+ }
+
+ LLControlBase* control = (LLControlBase *)mNameTable[name];
+
+ if (!control)
+ {
+ llwarns << "Tried to save invalid control: " << name << llendl;
+ }
+
+ if( control && control->mPersist )
+ {
+ if (!(nondefault_only && (control->mIsDefault)))
+ {
+ controls.addDataSorted( control );
+ }
+ else
+ {
+ // Debug spam
+ // llinfos << "Skipping " << control->getName() << llendl;
+ }
+ }
+ }
+
+ llofstream file;
+ file.open(filename.c_str());
+
+ if (!file.is_open())
+ {
+ // This is a warning because sometime we want to use settings files which can't be written...
+ llwarns << "LLControlGroup::saveToFile unable to open file for writing" << llendl;
+ return 0;
+ }
+
+ // Write file version
+ file << "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n";
+ file << "<settings version = \"" << CURRENT_VERSION << "\">\n";
+ for( LLControlBase* control = controls.getFirstData();
+ control != NULL;
+ control = controls.getNextData() )
+ {
+ file << "\t<!--" << control->comment() << "-->" << ENDL;
+ name = control->name();
+ switch (control->type())
+ {
+ case TYPE_U32:
+ {
+ file << "\t<" << name << " value=\"" << (U32) control->get().asInteger() << "\"/>\n";
+ break;
+ }
+ case TYPE_S32:
+ {
+ file << "\t<" << name << " value=\"" << (S32) control->get().asInteger() << "\"/>\n";
+ break;
+ }
+ case TYPE_F32:
+ {
+ file << "\t<" << name << " value=\"" << (F32) control->get().asReal() << "\"/>\n";
+ break;
+ }
+ case TYPE_VEC3:
+ {
+ LLVector3 vector(control->get());
+ file << "\t<" << name << " value=\"" << vector.mV[VX] << " " << vector.mV[VY] << " " << vector.mV[VZ] << "\"/>\n";
+ break;
+ }
+ case TYPE_VEC3D:
+ {
+ LLVector3d vector(control->get());
+ file << "\t<" << name << " value=\"" << vector.mdV[VX] << " " << vector.mdV[VY] << " " << vector.mdV[VZ] << "\"/>\n";
+ break;
+ }
+ case TYPE_RECT:
+ {
+ LLRect rect(control->get());
+ file << "\t<" << name << " value=\"" << rect.mLeft << " " << rect.mBottom << " " << rect.getWidth() << " " << rect.getHeight() << "\"/>\n";
+ break;
+ }
+ case TYPE_COL4:
+ {
+ LLColor4 color(control->get());
+ file << "\t<" << name << " value=\"" << color.mV[VRED] << ", " << color.mV[VGREEN] << ", " << color.mV[VBLUE] << ", " << color.mV[VALPHA] << "\"/>\n";
+ break;
+ }
+ case TYPE_COL3:
+ {
+ LLColor3 color(control->get());
+ file << "\t<" << name << " value=\"" << color.mV[VRED] << ", " << color.mV[VGREEN] << ", " << color.mV[VBLUE] << "\"/>\n";
+ break;
+ }
+ case TYPE_BOOLEAN:
+ {
+ file << "\t<" << name << " value=\"" << (control->get().asBoolean() ? "TRUE" : "FALSE") << "\"/>\n";
+ break;
+ }
+ case TYPE_STRING:
+ {
+ file << "\t<" << name << " value=\"" << LLSDXMLFormatter::escapeString(control->get().asString()) << "\"/>\n";
+ break;
+ }
+ default:
+ {
+ CONTROL_ERRS << "LLControlGroup::saveToFile - unknown control type!" << llendl;
+ break;
+ }
+ }
+
+ // Debug spam
+ // llinfos << name << " " << control->getValue().asString() << llendl;
+ }// next
+
+ file << "</settings>\n";
+ file.close();
+
+ return controls.getLength();
+}
+
+void LLControlGroup::applyOverrides(const std::map<std::string, std::string>& overrides)
+{
+ for (std::map<std::string, std::string>::const_iterator iter = overrides.begin();
+ iter != overrides.end(); ++iter)
+ {
+ const std::string& command = iter->first;
+ const std::string& value = iter->second;
+ LLControlBase* control = (LLControlBase *)mNameTable[command];
+ if (control)
+ {
+ switch(control->mType)
+ {
+ case TYPE_U32:
+ control->set((LLSD::Integer)atof(value.c_str()));
+ break;
+ case TYPE_S32:
+ control->set((S32)atof(value.c_str()));
+ break;
+ case TYPE_F32:
+ control->set((F32)atof(value.c_str()));
+ break;
+ case TYPE_BOOLEAN:
+ if (!LLString::compareInsensitive(value.c_str(), "TRUE"))
+ {
+ control->set(TRUE);
+ }
+ else if (!LLString::compareInsensitive(value.c_str(), "FALSE"))
+ {
+ control->set(FALSE);
+ }
+ else
+ {
+ control->set((BOOL)atof(value.c_str()));
+ }
+ break;
+ case TYPE_STRING:
+ control->set(value);
+ break;
+// // *FIX: implement this given time and need.
+// case TYPE_UUID:
+// break;
+ // we don't support command line overrides of vec3 or col4
+ // yet - requires parsing of multiple values
+ case TYPE_VEC3:
+ case TYPE_VEC3D:
+ case TYPE_COL4:
+ case TYPE_COL3:
+ default:
+ break;
+ }
+ }
+ else
+ {
+ llinfos << "There is no control variable " << command << llendl;
+ }
+ }
+}
+
+void LLControlGroup::resetToDefaults()
+{
+ ctrl_name_table_t::iterator control_iter;
+ for (control_iter = mNameTable.begin();
+ control_iter != mNameTable.end();
+ ++control_iter)
+ {
+ LLControlBase* control = (*control_iter).second;
+ control->resetToDefault();
+ }
+}
+
+//============================================================================
+// FIrst-use
+
+
+void LLControlGroup::addWarning(const LLString& name)
+{
+ LLString warnname = "Warn" + name;
+ if(!mNameTable[warnname])
+ {
+ LLString comment = LLString("Enables ") + name + LLString(" warning dialog");
+ declareBOOL(warnname, TRUE, comment);
+ mWarnings.insert(warnname);
+ }
+}
+
+BOOL LLControlGroup::getWarning(const LLString& name)
+{
+ LLString warnname = "Warn" + name;
+ return getBOOL(warnname);
+}
+
+void LLControlGroup::setWarning(const LLString& name, BOOL val)
+{
+ LLString warnname = "Warn" + name;
+ setBOOL(warnname, val);
+}
+
+void LLControlGroup::resetWarnings()
+{
+ for (std::set<LLString>::iterator iter = mWarnings.begin();
+ iter != mWarnings.end(); ++iter)
+ {
+ setBOOL(*iter, TRUE);
+ }
+}
+
+
+
+//=============================================================================
+// Listener ID generator/management
+
+void LLControlBase::releaseListenerID(S32 id)
+{
+ mFreeIDs.push_back(id);
+}
+
+S32 LLControlBase::allocateListenerID()
+{
+ if(mFreeIDs.size() == 0)
+ { //Out of IDs so generate some new ones.
+ for(int t=0;t<32;t++)
+ {
+ mFreeIDs.push_back(mTopID++);
+ }
+ }
+ S32 rtn = mFreeIDs.front();
+ mFreeIDs.pop_front();
+ mUsedIDs.push_back(rtn);
+ return rtn;
+}
+
+bool LLControlBase::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (event->desc() == "value_changed")
+ {
+ setValue(((LLValueChangedEvent*)(LLEvent*)event)->mValue);
+ return TRUE;
+ }
+ return TRUE;
+}
+
+void LLControlBase::firePropertyChanged()
+{
+ LLValueChangedEvent *evt = new LLValueChangedEvent(this, getValue());
+ fireEvent(evt, "");
+}
+
+//============================================================================
+// Used to add a listener callback that will be called on the frame that the controls value changes
+
+S32 LLControl::addListener(LLControl::tListenerCallback* cbfn)
+{
+ S32 id = allocateListenerID();
+ mListeners.push_back(cbfn);
+ mListenerIDs.push_back( id );
+ return id;
+}
+
+void LLControl::updateListeners() {
+ LLControl::tPropertyChangedListIter iter = mChangeEvents.begin();
+ while(iter!=mChangeEvents.end()){
+ LLControl::tPropertyChangedEvent& evt = *iter;
+ (*evt.mCBFN)(evt.mNewValue,evt.mID,*this);
+ iter++;
+ }
+ mChangeEvents.clear();
+}
+
+//static
+void LLControlBase::updateAllListeners()
+{
+ std::set< LLControlBase* >::iterator iter = mChangedControls.begin();
+ while(iter != mChangedControls.end()){
+ (*iter)->updateListeners();
+ iter++;
+ }
+ mChangedControls.clear();
+}
+
+LLControl::LLControl(
+ const LLString& name,
+ eControlType type,
+ LLSD initial,
+ const LLString& comment,
+ BOOL persist) :
+ LLControlBase(name, type, comment, persist),
+ mCurrent(initial),
+ mDefault(initial)
+{
+}
+
+//============================================================================
+
+#ifdef TEST_HARNESS
+void main()
+{
+ F32_CONTROL foo, getfoo;
+
+ S32_CONTROL bar, getbar;
+
+ BOOL_CONTROL baz;
+
+ U32 count = gGlobals.loadFromFile("controls.ini");
+ llinfos << "Loaded " << count << " controls" << llendl;
+
+ // test insertion
+ foo = new LLControl<F32>("gFoo", 5.f, 1.f, 20.f);
+ gGlobals.addEntry("gFoo", foo);
+
+ bar = new LLControl<S32>("gBar", 10, 2, 22);
+ gGlobals.addEntry("gBar", bar);
+
+ baz = new LLControl<BOOL>("gBaz", FALSE);
+ gGlobals.addEntry("gBaz", baz);
+
+ // test retrieval
+ getfoo = (LLControl<F32>*) gGlobals.resolveName("gFoo");
+ getfoo->dump();
+
+ getbar = (S32_CONTROL) gGlobals.resolveName("gBar");
+ getbar->dump();
+
+ // change data
+ getfoo->set(10.f);
+ getfoo->dump();
+
+ // Failure modes
+
+ // ...min > max
+ // badfoo = new LLControl<F32>("gFoo2", 100.f, 20.f, 5.f);
+
+ // ...initial > max
+ // badbar = new LLControl<S32>("gBar2", 10, 20, 100000);
+
+ // ...misspelled name
+ // getfoo = (F32_CONTROL) gGlobals.resolveName("fooMisspelled");
+ // getfoo->dump();
+
+ // ...invalid data type
+ getfoo = (F32_CONTROL) gGlobals.resolveName("gFoo");
+ getfoo->set(TRUE);
+ getfoo->dump();
+
+ // ...out of range data
+ // getfoo->set(100000000.f);
+ // getfoo->dump();
+
+ // Clean Up
+ delete foo;
+ delete bar;
+ delete baz;
+}
+#endif
+
+BOOL control_insert_before( LLControlBase* first, LLControlBase* second )
+{
+ return ( first->getName().compare(second->getName()) < 0 );
+}
+
diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h
new file mode 100644
index 0000000000..b88f388a0b
--- /dev/null
+++ b/indra/llxml/llcontrol.h
@@ -0,0 +1,255 @@
+/**
+ * @file llcontrol.h
+ * @brief A mechanism for storing "control state" for a program
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCONTROL_H
+#define LL_LLCONTROL_H
+
+#include "llevent.h"
+#include "llnametable.h"
+#include "llmap.h"
+#include "llstring.h"
+#include "llrect.h"
+
+class LLVector3;
+class LLVector3d;
+class LLColor4;
+class LLColor3;
+class LLColor4U;
+
+const BOOL NO_PERSIST = FALSE;
+
+typedef enum e_control_type
+{
+ TYPE_U32,
+ TYPE_S32,
+ TYPE_F32,
+ TYPE_BOOLEAN,
+ TYPE_STRING,
+ TYPE_VEC3,
+ TYPE_VEC3D,
+ TYPE_RECT,
+ TYPE_COL4,
+ TYPE_COL3,
+ TYPE_COL4U
+} eControlType;
+
+class LLControlBase : public LLSimpleListenerObservable
+{
+friend class LLControlGroup;
+protected:
+ LLString mName;
+ LLString mComment;
+ eControlType mType;
+ BOOL mHasRange;
+ BOOL mPersist;
+ BOOL mIsDefault;
+ static std::set<LLControlBase*> mChangedControls;
+ static std::list<S32> mFreeIDs;//These lists are used to store the ID's of registered event listeners.
+ static std::list<S32> mUsedIDs;
+ static S32 mTopID;//This is the index of the highest ID event listener ID. When the free pool is exhausted, new IDs are allocated from here.
+
+public:
+ static void releaseListenerID(S32 id);
+ static S32 allocateListenerID();
+ static void updateAllListeners();
+ virtual void updateListeners() = 0;
+
+ LLControlBase(const LLString& name, eControlType type, const LLString& comment, BOOL persist)
+ : mName(name),
+ mComment(comment),
+ mType(type),
+ mHasRange(FALSE),
+ mPersist(persist),
+ mIsDefault(TRUE)
+ {
+ if (mPersist && mComment.empty())
+ {
+ llerrs << "Must supply a comment for control " << mName << llendl;
+ }
+ sMaxControlNameLength = llmax((U32)mName.size(), sMaxControlNameLength);
+ }
+
+ virtual ~LLControlBase();
+
+ const LLString& getName() const { return mName; }
+ const LLString& getComment() const { return mComment; }
+
+ eControlType type() { return mType; }
+ BOOL isType(eControlType tp) { return tp == mType; }
+
+ // Defaults to no-op
+ virtual void resetToDefault();
+
+ LLSD registerListener(LLSimpleListenerObservable *listener, LLSD userdata = "");
+
+ virtual LLSD get() const = 0;
+ virtual LLSD getValue() const = 0;
+ virtual void setValue(LLSD value) = 0;
+ virtual void set(LLSD value) = 0;
+
+ // From LLSimpleListener
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+
+ void firePropertyChanged();
+
+ static U32 sMaxControlNameLength;
+
+protected:
+ const char* name() { return mName.c_str(); }
+ const char* comment() { return mComment.c_str(); }
+};
+
+class LLControl
+: public LLControlBase
+{
+friend class LLControlGroup;
+protected:
+ LLSD mCurrent;
+ LLSD mDefault;
+
+public:
+
+ typedef void tListenerCallback(const LLSD& newValue,S32 listenerID, LLControl& control);
+ typedef struct{
+ S32 mID;
+ LLSD mNewValue;
+ tListenerCallback* mCBFN;
+ }tPropertyChangedEvent;
+
+ typedef std::list<tPropertyChangedEvent>::iterator tPropertyChangedListIter;
+ std::list<tPropertyChangedEvent> mChangeEvents;
+ std::list< tListenerCallback* > mListeners;
+ std::list< S32 > mListenerIDs;
+
+ virtual void updateListeners();
+ S32 addListener(tListenerCallback* cbfn);
+
+ LLControl(
+ const LLString& name,
+ eControlType type,
+ LLSD initial, const
+ LLString& comment,
+ BOOL persist = TRUE);
+
+ void set(LLSD val) { setValue(val); }
+ LLSD get() const { return getValue(); }
+ LLSD getdefault() const { return mDefault; }
+ LLSD getValue() const { return mCurrent; }
+ BOOL llsd_compare(const LLSD& a, const LLSD& b);
+
+ void setValue(LLSD value)
+ {
+ if (llsd_compare(mCurrent, value) == FALSE)
+ {
+ mCurrent = value;
+ mIsDefault = llsd_compare(mCurrent, mDefault);
+ firePropertyChanged();
+ }
+ }
+
+ /*virtual*/ void resetToDefault() { mCurrent = mDefault; mIsDefault = TRUE;}
+
+ virtual ~LLControl()
+ {
+ //Remove and deregister all listeners..
+ while(mListenerIDs.size())
+ {
+ S32 id = mListenerIDs.front();
+ mListenerIDs.pop_front();
+ releaseListenerID(id);
+ }
+ }
+};
+
+//const U32 STRING_CACHE_SIZE = 10000;
+class LLControlGroup
+{
+public:
+ typedef std::map<LLString, LLPointer<LLControlBase> > ctrl_name_table_t;
+ ctrl_name_table_t mNameTable;
+ std::set<LLString> mWarnings;
+
+public:
+ LLControlGroup();
+ ~LLControlGroup();
+ void cleanup();
+
+ LLControlBase* getControl(const LLString& name);
+ LLSD registerListener(const LLString& name, LLSimpleListenerObservable *listener);
+
+ BOOL declareControl(const LLString& name, eControlType type, const LLSD initial_val, const LLString& comment, BOOL persist);
+ BOOL declareU32(const LLString& name, U32 initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareS32(const LLString& name, S32 initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareF32(const LLString& name, F32 initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareBOOL(const LLString& name, BOOL initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareString(const LLString& name, const LLString &initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareVec3(const LLString& name, const LLVector3 &initial_val,const LLString& comment, BOOL persist = TRUE);
+ BOOL declareVec3d(const LLString& name, const LLVector3d &initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareRect(const LLString& name, const LLRect &initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareColor4U(const LLString& name, const LLColor4U &initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareColor4(const LLString& name, const LLColor4 &initial_val, const LLString& comment, BOOL persist = TRUE);
+ BOOL declareColor3(const LLString& name, const LLColor3 &initial_val, const LLString& comment, BOOL persist = TRUE);
+
+ LLString findString(const LLString& name);
+
+ LLString getString(const LLString& name);
+ LLWString getWString(const LLString& name);
+ LLString getText(const LLString& name);
+ LLVector3 getVector3(const LLString& name);
+ LLVector3d getVector3d(const LLString& name);
+ LLRect getRect(const LLString& name);
+ BOOL getBOOL(const LLString& name);
+ S32 getS32(const LLString& name);
+ F32 getF32(const LLString& name);
+ U32 getU32(const LLString& name);
+ LLSD getValue(const LLString& name);
+
+
+ // Note: If an LLColor4U control exists, it will cast it to the correct
+ // LLColor4 for you.
+ LLColor4 getColor(const LLString& name);
+ LLColor4U getColor4U(const LLString& name);
+ LLColor4 getColor4(const LLString& name);
+ LLColor3 getColor3(const LLString& name);
+
+ void setBOOL(const LLString& name, BOOL val);
+ void setS32(const LLString& name, S32 val);
+ void setF32(const LLString& name, F32 val);
+ void setU32(const LLString& name, U32 val);
+ void setString(const LLString& name, const LLString& val);
+ void setVector3(const LLString& name, const LLVector3 &val);
+ void setVector3d(const LLString& name, const LLVector3d &val);
+ void setRect(const LLString& name, const LLRect &val);
+ void setColor4U(const LLString& name, const LLColor4U &val);
+ void setColor4(const LLString& name, const LLColor4 &val);
+ void setColor3(const LLString& name, const LLColor3 &val);
+ void setValue(const LLString& name, const LLSD& val);
+
+ BOOL controlExists(const LLString& name);
+
+ // Returns number of controls loaded, 0 if failed
+ // If require_declaration is false, will auto-declare controls it finds
+ // as the given type.
+ U32 loadFromFileLegacy(const LLString& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING);
+ U32 loadFromFile(const LLString& filename, BOOL require_declaration = TRUE, eControlType declare_as = TYPE_STRING);
+ U32 saveToFile(const LLString& filename, BOOL skip_if_default);
+ void applyOverrides(const std::map<std::string, std::string>& overrides);
+ void resetToDefaults();
+
+ // Ignorable Warnings
+
+ // Add a config variable to be reset on resetWarnings()
+ void addWarning(const LLString& name);
+ BOOL getWarning(const LLString& name);
+ void setWarning(const LLString& name, BOOL val);
+
+ // Resets all ignorables
+ void resetWarnings();
+};
+
+#endif
diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp
new file mode 100644
index 0000000000..7d77fa8be7
--- /dev/null
+++ b/indra/llxml/llxmlnode.cpp
@@ -0,0 +1,3008 @@
+/**
+ * @file llxmlnode.cpp
+ * @author Tom Yedwab
+ * @brief LLXMLNode implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <map>
+
+#include "llxmlnode.h"
+
+#include "v3color.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "lluuid.h"
+
+const S32 MAX_COLUMN_WIDTH = 80;
+
+// static
+BOOL LLXMLNode::sStripEscapedStrings = TRUE;
+BOOL LLXMLNode::sStripWhitespaceValues = FALSE;
+
+LLXMLNode::LLXMLNode() :
+ mID(""),
+ mIsAttribute(FALSE),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mParent(NULL),
+ mChildren(NULL),
+ mName(NULL),
+ mValue(""),
+ mDefault(NULL)
+{
+}
+
+LLXMLNode::LLXMLNode(const LLString& name, BOOL is_attribute) :
+ mID(""),
+ mIsAttribute(is_attribute),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mParent(NULL),
+ mChildren(NULL),
+ mValue(""),
+ mDefault(NULL)
+{
+ mName = gStringTable.addStringEntry(name);
+}
+
+LLXMLNode::LLXMLNode(LLStringTableEntry* name, BOOL is_attribute) :
+ mID(""),
+ mIsAttribute(is_attribute),
+ mVersionMajor(0),
+ mVersionMinor(0),
+ mLength(0),
+ mPrecision(64),
+ mType(TYPE_CONTAINER),
+ mEncoding(ENCODING_DEFAULT),
+ mParent(NULL),
+ mChildren(NULL),
+ mName(name),
+ mValue(""),
+ mDefault(NULL)
+{
+}
+
+// virtual
+LLXMLNode::~LLXMLNode()
+{
+ // Strictly speaking none of this should be required execept 'delete mChildren'...
+ if (mChildren)
+ {
+ for (LLXMLChildList::iterator iter = mChildren->map.begin();
+ iter != mChildren->map.end(); ++iter)
+ {
+ LLXMLNodePtr child = iter->second;
+ child->mParent = NULL;
+ child->mNext = NULL;
+ child->mPrev = NULL;
+ }
+ mChildren->map.clear();
+ mChildren->head = NULL;
+ mChildren->tail = NULL;
+ delete mChildren;
+ }
+ for (LLXMLAttribList::iterator iter = mAttributes.begin();
+ iter != mAttributes.end(); ++iter)
+ {
+ LLXMLNodePtr attr = iter->second;
+ attr->mParent = NULL;
+ attr->mNext = NULL;
+ attr->mPrev = NULL;
+ }
+ llassert(mParent == NULL);
+ mDefault = NULL;
+}
+
+BOOL LLXMLNode::isNull()
+{
+ return (mName == NULL);
+}
+
+// protected
+BOOL LLXMLNode::removeChild(LLXMLNode *target_child)
+{
+ if (!target_child)
+ {
+ return FALSE;
+ }
+ if (target_child->mIsAttribute)
+ {
+ LLXMLAttribList::iterator children_itr = mAttributes.find(target_child->mName);
+ if (children_itr != mAttributes.end())
+ {
+ target_child->mParent = NULL;
+ mAttributes.erase(children_itr);
+ return TRUE;
+ }
+ }
+ else if (mChildren)
+ {
+ LLXMLChildList::iterator children_itr = mChildren->map.find(target_child->mName);
+ while (children_itr != mChildren->map.end())
+ {
+ if (target_child == children_itr->second)
+ {
+ if (target_child == mChildren->head)
+ {
+ mChildren->head = target_child->mNext;
+ }
+
+ LLXMLNodePtr prev = target_child->mPrev;
+ LLXMLNodePtr next = target_child->mNext;
+ if (prev.notNull()) prev->mNext = next;
+ if (next.notNull()) next->mPrev = prev;
+
+ target_child->mPrev = NULL;
+ target_child->mNext = NULL;
+ target_child->mParent = NULL;
+ mChildren->map.erase(children_itr);
+ if (mChildren->map.empty())
+ {
+ delete mChildren;
+ mChildren = NULL;
+ }
+ return TRUE;
+ }
+ else if (children_itr->first != target_child->mName)
+ {
+ break;
+ }
+ else
+ {
+ ++children_itr;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void LLXMLNode::addChild(LLXMLNodePtr new_child)
+{
+ if (new_child->mParent != NULL)
+ {
+ if (new_child->mParent == this)
+ {
+ return;
+ }
+ new_child->mParent->removeChild(new_child);
+ }
+
+ new_child->mParent = this;
+ if (new_child->mIsAttribute)
+ {
+ mAttributes.insert(std::pair<LLStringTableEntry*, LLXMLNodePtr>(new_child->mName, new_child));
+ }
+ else
+ {
+ if (!mChildren)
+ {
+ mChildren = new LLXMLChildren();
+ mChildren->head = new_child;
+ mChildren->tail = new_child;
+ }
+ mChildren->map.insert(std::pair<LLStringTableEntry*, LLXMLNodePtr>(new_child->mName, new_child));
+
+ if (mChildren->tail != new_child)
+ {
+ mChildren->tail->mNext = new_child;
+ new_child->mPrev = mChildren->tail;
+ mChildren->tail = new_child;
+ }
+ }
+
+ new_child->updateDefault();
+}
+
+// virtual
+LLXMLNodePtr LLXMLNode::createChild(const LLString& name, BOOL is_attribute)
+{
+ return createChild(gStringTable.addStringEntry(name), is_attribute);
+}
+
+// virtual
+LLXMLNodePtr LLXMLNode::createChild(LLStringTableEntry* name, BOOL is_attribute)
+{
+ LLXMLNode* ret = new LLXMLNode(name, is_attribute);
+ ret->mID = "";
+ addChild(ret);
+ return ret;
+}
+
+BOOL LLXMLNode::deleteChild(LLXMLNode *child)
+{
+ if (removeChild(child))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLXMLNode::setParent(LLXMLNodePtr new_parent)
+{
+ if (new_parent.notNull())
+ {
+ new_parent->addChild(this);
+ }
+ else
+ {
+ if (mParent != NULL)
+ {
+ LLXMLNodePtr old_parent = mParent;
+ mParent = NULL;
+ old_parent->removeChild(this);
+ }
+ }
+}
+
+
+void LLXMLNode::updateDefault()
+{
+ if (mParent != NULL && !mParent->mDefault.isNull())
+ {
+ mDefault = NULL;
+
+ // Find default value in parent's default tree
+ if (!mParent->mDefault.isNull())
+ {
+ findDefault(mParent->mDefault);
+ }
+ }
+
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator children_itr;
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNodePtr child = (*children_itr).second;
+ child->updateDefault();
+ }
+ }
+}
+
+void XMLCALL StartXMLNode(void *userData,
+ const XML_Char *name,
+ const XML_Char **atts)
+{
+ // Create a new node
+ LLXMLNode *new_node_ptr = new LLXMLNode(name, FALSE);
+ LLXMLNodePtr new_node = new_node_ptr;
+ new_node->mID = "";
+ LLXMLNodePtr ptr_new_node = new_node;
+
+ // Set the parent-child relationship with the current active node
+ LLXMLNode* parent = (LLXMLNode *)userData;
+
+ new_node_ptr->mParser = parent->mParser;
+
+ // Set the current active node to the new node
+ XML_Parser *parser = parent->mParser;
+ XML_SetUserData(*parser, (void *)new_node_ptr);
+
+ // Parse attributes
+ U32 pos = 0;
+ while (atts[pos] != NULL)
+ {
+ LLString attr_name = atts[pos];
+ LLString attr_value = atts[pos+1];
+
+ // Special cases
+ if ('i' == attr_name[0] && "id" == attr_name)
+ {
+ new_node->mID = attr_value;
+ }
+ else if ('v' == attr_name[0] && "version" == attr_name)
+ {
+ U32 version_major = 0;
+ U32 version_minor = 0;
+ if (sscanf(attr_value.c_str(), "%d.%d", &version_major, &version_minor) > 0)
+ {
+ new_node->mVersionMajor = version_major;
+ new_node->mVersionMinor = version_minor;
+ }
+ }
+ else if (('s' == attr_name[0] && "size" == attr_name) || ('l' == attr_name[0] && "length" == attr_name))
+ {
+ U32 length;
+ if (sscanf(attr_value.c_str(), "%d", &length) > 0)
+ {
+ new_node->mLength = length;
+ }
+ }
+ else if ('p' == attr_name[0] && "precision" == attr_name)
+ {
+ U32 precision;
+ if (sscanf(attr_value.c_str(), "%d", &precision) > 0)
+ {
+ new_node->mPrecision = precision;
+ }
+ }
+ else if ('t' == attr_name[0] && "type" == attr_name)
+ {
+ if ("boolean" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_BOOLEAN;
+ }
+ else if ("integer" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_INTEGER;
+ }
+ else if ("float" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_FLOAT;
+ }
+ else if ("string" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_STRING;
+ }
+ else if ("uuid" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_UUID;
+ }
+ else if ("noderef" == attr_value)
+ {
+ new_node->mType = LLXMLNode::TYPE_NODEREF;
+ }
+ }
+ else if ('e' == attr_name[0] && "encoding" == attr_name)
+ {
+ if ("decimal" == attr_value)
+ {
+ new_node->mEncoding = LLXMLNode::ENCODING_DECIMAL;
+ }
+ else if ("hex" == attr_value)
+ {
+ new_node->mEncoding = LLXMLNode::ENCODING_HEX;
+ }
+ /*else if (attr_value == "base32")
+ {
+ new_node->mEncoding = LLXMLNode::ENCODING_BASE32;
+ }*/
+ }
+
+ // only one attribute child per description
+ LLXMLNodePtr attr_node;
+ if (!new_node->getAttribute(attr_name, attr_node, FALSE))
+ {
+ attr_node = new LLXMLNode(attr_name, TRUE);
+ }
+ attr_node->setValue(attr_value);
+ new_node->addChild(attr_node);
+
+ pos += 2;
+ }
+
+ if (parent)
+ {
+ parent->addChild(new_node);
+ }
+}
+
+void XMLCALL EndXMLNode(void *userData,
+ const XML_Char *name)
+{
+ // [FUGLY] Set the current active node to the current node's parent
+ LLXMLNode *node = (LLXMLNode *)userData;
+ XML_Parser *parser = node->mParser;
+ XML_SetUserData(*parser, (void *)node->mParent);
+ // SJB: total hack:
+ if (LLXMLNode::sStripWhitespaceValues)
+ {
+ LLString value = node->getValue();
+ BOOL is_empty = TRUE;
+ for (std::string::size_type s = 0; s < value.length(); s++)
+ {
+ char c = value[s];
+ if (c != ' ' && c != '\t' && c != '\n')
+ {
+ is_empty = FALSE;
+ break;
+ }
+ }
+ if (is_empty)
+ {
+ value.clear();
+ node->setValue(value);
+ }
+ }
+}
+
+void XMLCALL XMLData(void *userData,
+ const XML_Char *s,
+ int len)
+{
+ LLXMLNode* current_node = (LLXMLNode *)userData;
+ LLString value = current_node->getValue();
+ if (LLXMLNode::sStripEscapedStrings)
+ {
+ if (s[0] == '\"' && s[len-1] == '\"')
+ {
+ // Special-case: Escaped string.
+ LLString unescaped_string;
+ for (S32 pos=1; pos<len-1; ++pos)
+ {
+ if (s[pos] == '\\' && s[pos+1] == '\\')
+ {
+ unescaped_string.append("\\");
+ ++pos;
+ }
+ else if (s[pos] == '\\' && s[pos+1] == '\"')
+ {
+ unescaped_string.append("\"");
+ ++pos;
+ }
+ else
+ {
+ unescaped_string.append(&s[pos], 1);
+ }
+ }
+ value.append(unescaped_string);
+ current_node->setValue(value);
+ return;
+ }
+ }
+ value.append(LLString(s, 0, len));
+ current_node->setValue(value);
+}
+
+
+
+// static
+bool LLXMLNode::updateNode(
+ LLXMLNodePtr& node,
+ LLXMLNodePtr& update_node)
+{
+
+ if (!node || !update_node)
+ {
+ llwarns << "Node invalid" << llendl;
+ return FALSE;
+ }
+
+ //update the node value
+ node->mValue = update_node->mValue;
+
+ //update all attribute values
+ LLXMLAttribList::const_iterator itor;
+
+ for(itor = update_node->mAttributes.begin(); itor != update_node->mAttributes.end(); ++itor)
+ {
+ const LLStringTableEntry* attribNameEntry = (*itor).first;
+ LLXMLNodePtr updateAttribNode = (*itor).second;
+
+ LLXMLNodePtr attribNode;
+
+ node->getAttribute(attribNameEntry, attribNode, 0);
+
+ if (attribNode)
+ {
+ attribNode->mValue = updateAttribNode->mValue;
+ }
+ }
+
+ //update all of node's children with updateNodes children that match name
+ LLXMLNodePtr child;
+ LLXMLNodePtr updateChild;
+
+ for (updateChild = update_node->getFirstChild(); updateChild.notNull();
+ updateChild = updateChild->getNextSibling())
+ {
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ LLString nodeName;
+ LLString updateName;
+
+ updateChild->getAttributeString("name", updateName);
+ child->getAttributeString("name", nodeName);
+
+
+ //if it's a combobox there's no name, but there is a value
+ if (updateName.empty())
+ {
+ updateChild->getAttributeString("value", updateName);
+ child->getAttributeString("value", nodeName);
+ }
+
+ if ((nodeName != "") && (updateName == nodeName))
+ {
+ updateNode(child, updateChild);
+ break;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+
+// static
+bool LLXMLNode::parseFile(
+ LLString filename,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults_tree)
+{
+ // Read file
+ FILE* fp = LLFile::fopen(filename.c_str(), "rb");
+ if (fp == NULL)
+ {
+ node = new LLXMLNode();
+ return false;
+ }
+ fseek(fp, 0, SEEK_END);
+ U32 length = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ U8* buffer = new U8[length+1];
+ fread(buffer, 1, length, fp);
+ buffer[length] = 0;
+ fclose(fp);
+
+ bool rv = parseBuffer(buffer, length, node, defaults_tree);
+ delete [] buffer;
+ return rv;
+}
+
+// static
+bool LLXMLNode::parseBuffer(
+ U8* buffer,
+ U32 length,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults)
+{
+ // Init
+ XML_Parser my_parser = XML_ParserCreate(NULL);
+ XML_SetElementHandler(my_parser, StartXMLNode, EndXMLNode);
+ XML_SetCharacterDataHandler(my_parser, XMLData);
+
+ // Create a root node
+ LLXMLNode *file_node_ptr = new LLXMLNode("XML", FALSE);
+ LLXMLNodePtr file_node = file_node_ptr;
+
+ file_node->mParser = &my_parser;
+
+ XML_SetUserData(my_parser, (void *)file_node_ptr);
+
+ // Do the parsing
+ if (XML_Parse(my_parser, (const char *)buffer, length, TRUE) != XML_STATUS_OK)
+ {
+ llwarns << "Error parsing xml error code: "
+ << XML_ErrorString(XML_GetErrorCode(my_parser))
+ << " on lne " << XML_GetCurrentLineNumber(my_parser)
+ << llendl;
+ }
+
+ // Deinit
+ XML_ParserFree(my_parser);
+
+ if (!file_node->mChildren || file_node->mChildren->map.size() != 1)
+ {
+ llwarns << "Parse failure - wrong number of top-level nodes xml."
+ << llendl;
+ node = new LLXMLNode();
+ return false;
+ }
+
+ LLXMLNode *return_node = file_node->mChildren->map.begin()->second;
+
+ return_node->setDefault(defaults);
+ return_node->updateDefault();
+
+ node = return_node;
+ return true;
+}
+
+BOOL LLXMLNode::isFullyDefault()
+{
+ if (mDefault.isNull())
+ {
+ return FALSE;
+ }
+ BOOL has_default_value = (mValue == mDefault->mValue);
+ BOOL has_default_attribute = (mIsAttribute == mDefault->mIsAttribute);
+ BOOL has_default_type = mIsAttribute || (mType == mDefault->mType);
+ BOOL has_default_encoding = mIsAttribute || (mEncoding == mDefault->mEncoding);
+ BOOL has_default_precision = mIsAttribute || (mPrecision == mDefault->mPrecision);
+ BOOL has_default_length = mIsAttribute || (mLength == mDefault->mLength);
+
+ if (has_default_value
+ && has_default_type
+ && has_default_encoding
+ && has_default_precision
+ && has_default_length
+ && has_default_attribute)
+ {
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator children_itr;
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNodePtr child = (*children_itr).second;
+ if (!child->isFullyDefault())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// static
+void LLXMLNode::writeHeaderToFile(FILE *fOut)
+{
+ fprintf(fOut, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n");
+}
+
+void LLXMLNode::writeToFile(FILE *fOut, LLString indent)
+{
+ if (isFullyDefault())
+ {
+ // Don't write out nodes that are an exact match to defaults
+ return;
+ }
+
+ std::ostringstream ostream;
+ writeToOstream(ostream, indent);
+ LLString outstring = ostream.str();
+ fwrite(outstring.c_str(), 1, outstring.length(), fOut);
+}
+
+void LLXMLNode::writeToOstream(std::ostream& output_stream, const LLString& indent)
+{
+ if (isFullyDefault())
+ {
+ // Don't write out nodes that are an exact match to defaults
+ return;
+ }
+
+ BOOL has_default_type = mDefault.isNull()?FALSE:(mType == mDefault->mType);
+ BOOL has_default_encoding = mDefault.isNull()?FALSE:(mEncoding == mDefault->mEncoding);
+ BOOL has_default_precision = mDefault.isNull()?FALSE:(mPrecision == mDefault->mPrecision);
+ BOOL has_default_length = mDefault.isNull()?FALSE:(mLength == mDefault->mLength);
+
+ // stream the name
+ output_stream << indent.c_str() << "<" << mName->mString;
+
+ // ID
+ if (mID != "")
+ {
+ output_stream << " id=\"" << mID.c_str() << "\"";
+ }
+
+ // Type
+ if (!has_default_type)
+ {
+ switch (mType)
+ {
+ case TYPE_BOOLEAN:
+ output_stream << " type=\"boolean\"";
+ break;
+ case TYPE_INTEGER:
+ output_stream << " type=\"integer\"";
+ break;
+ case TYPE_FLOAT:
+ output_stream << " type=\"float\"";
+ break;
+ case TYPE_STRING:
+ output_stream << " type=\"string\"";
+ break;
+ case TYPE_UUID:
+ output_stream << " type=\"uuid\"";
+ break;
+ case TYPE_NODEREF:
+ output_stream << " type=\"noderef\"";
+ break;
+ default:
+ // default on switch(enum) eliminates a warning on linux
+ break;
+ };
+ }
+
+ // Encoding
+ if (!has_default_encoding)
+ {
+ switch (mEncoding)
+ {
+ case ENCODING_DECIMAL:
+ output_stream << " encoding=\"decimal\"";
+ break;
+ case ENCODING_HEX:
+ output_stream << " encoding=\"hex\"";
+ break;
+ /*case ENCODING_BASE32:
+ output_stream << " encoding=\"base32\"";
+ break;*/
+ default:
+ // default on switch(enum) eliminates a warning on linux
+ break;
+ };
+ }
+
+ // Precision
+ if (!has_default_precision && (mType == TYPE_INTEGER || mType == TYPE_FLOAT))
+ {
+ output_stream << " precision=\"" << mPrecision << "\"";
+ }
+
+ // Version
+ if (mVersionMajor > 0 || mVersionMinor > 0)
+ {
+ output_stream << " version=\"" << mVersionMajor << "." << mVersionMinor << "\"";
+ }
+
+ // Array length
+ if (!has_default_length && mLength > 0)
+ {
+ output_stream << " length=\"" << mLength << "\"";
+ }
+
+ {
+ // Write out attributes
+ S32 col_pos = 0;
+ LLXMLAttribList::const_iterator attr_itr;
+ LLXMLAttribList::const_iterator attr_end = mAttributes.end();
+ for (attr_itr = mAttributes.begin(); attr_itr != attr_end; ++attr_itr)
+ {
+ LLXMLNodePtr child = (*attr_itr).second;
+ if (child->mDefault.isNull() || child->mDefault->mValue != child->mValue)
+ {
+ LLString attr = child->mName->mString;
+ if (attr == "id" ||
+ attr == "type" ||
+ attr == "encoding" ||
+ attr == "precision" ||
+ attr == "version" ||
+ attr == "length")
+ {
+ continue; // skip built-in attributes
+ }
+
+ LLString attr_str = llformat(" %s=\"%s\"",
+ attr.c_str(),
+ escapeXML(child->mValue).c_str());
+ if (col_pos + (S32)attr_str.length() > MAX_COLUMN_WIDTH)
+ {
+ output_stream << "\n" << indent << " ";
+ col_pos = 4;
+ }
+ col_pos += attr_str.length();
+ output_stream << attr_str;
+ }
+ }
+ }
+
+ if (!mChildren && mValue == "")
+ {
+ output_stream << " />\n";
+ return;
+ }
+ else
+ {
+ output_stream << ">\n";
+ if (mChildren)
+ {
+ // stream non-attributes
+ LLString next_indent = indent + "\t";
+ for (LLXMLNode* child = getFirstChild(); child; child = child->getNextSibling())
+ {
+ child->writeToOstream(output_stream, next_indent);
+ }
+ }
+ if (!mValue.empty())
+ {
+ LLString contents = getTextContents();
+ output_stream << indent.c_str() << "\t" << escapeXML(contents) << "\n";
+ }
+ output_stream << indent.c_str() << "</" << mName->mString << ">\n";
+ }
+}
+
+void LLXMLNode::findName(const LLString& name, LLXMLNodeList &results)
+{
+ LLStringTableEntry* name_entry = gStringTable.checkStringEntry(name);
+ if (name_entry == mName)
+ {
+ results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this));
+ return;
+ }
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator children_itr;
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNodePtr child = (*children_itr).second;
+ child->findName(name_entry, results);
+ }
+ }
+}
+
+void LLXMLNode::findName(LLStringTableEntry* name, LLXMLNodeList &results)
+{
+ if (name == mName)
+ {
+ results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this));
+ return;
+ }
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator children_itr;
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNodePtr child = (*children_itr).second;
+ child->findName(name, results);
+ }
+ }
+}
+
+void LLXMLNode::findID(const LLString& id, LLXMLNodeList &results)
+{
+ if (id == mID)
+ {
+ results.insert(std::pair<LLString, LLXMLNode*>(this->mName->mString, this));
+ return;
+ }
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator children_itr;
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ for (children_itr = mChildren->map.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNodePtr child = (*children_itr).second;
+ child->findID(id, results);
+ }
+ }
+}
+
+void LLXMLNode::scrubToTree(LLXMLNode *tree)
+{
+ if (!tree || !tree->mChildren)
+ {
+ return;
+ }
+ if (mChildren)
+ {
+ std::vector<LLXMLNodePtr> to_delete_list;
+ LLXMLChildList::iterator itor = mChildren->map.begin();
+ while (itor != mChildren->map.end())
+ {
+ LLXMLNodePtr child = itor->second;
+ LLXMLNodePtr child_tree = NULL;
+ // Look for this child in the default's children
+ bool found = false;
+ LLXMLChildList::iterator itor2 = tree->mChildren->map.begin();
+ while (itor2 != tree->mChildren->map.end())
+ {
+ if (child->mName == itor2->second->mName)
+ {
+ child_tree = itor2->second;
+ found = true;
+ }
+ ++itor2;
+ }
+ if (!found)
+ {
+ to_delete_list.push_back(child);
+ }
+ else
+ {
+ child->scrubToTree(child_tree);
+ }
+ ++itor;
+ }
+ std::vector<LLXMLNodePtr>::iterator itor3;
+ for (itor3=to_delete_list.begin(); itor3!=to_delete_list.end(); ++itor3)
+ {
+ (*itor3)->setParent(NULL);
+ }
+ }
+}
+
+bool LLXMLNode::getChild(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing)
+{
+ return getChild(gStringTable.checkStringEntry(name), node, use_default_if_missing);
+}
+
+bool LLXMLNode::getChild(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing)
+{
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator child_itr = mChildren->map.find(name);
+ if (child_itr != mChildren->map.end())
+ {
+ node = (*child_itr).second;
+ return true;
+ }
+ }
+ if (use_default_if_missing && !mDefault.isNull())
+ {
+ return mDefault->getChild(name, node, FALSE);
+ }
+ node = new LLXMLNode();
+ return false;
+}
+
+void LLXMLNode::getChildren(const LLString& name, LLXMLNodeList &children, BOOL use_default_if_missing) const
+{
+ getChildren(gStringTable.checkStringEntry(name), children, use_default_if_missing);
+}
+
+void LLXMLNode::getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, BOOL use_default_if_missing) const
+{
+ if (mChildren)
+ {
+ LLXMLChildList::const_iterator child_itr = mChildren->map.find(name);
+ if (child_itr != mChildren->map.end())
+ {
+ LLXMLChildList::const_iterator children_end = mChildren->map.end();
+ while (child_itr != children_end)
+ {
+ LLXMLNodePtr child = (*child_itr).second;
+ if (name != child->mName)
+ {
+ break;
+ }
+ children.insert(std::pair<LLString, LLXMLNodePtr>(child->mName->mString, child));
+ child_itr++;
+ }
+ }
+ }
+ if (children.size() == 0 && use_default_if_missing && !mDefault.isNull())
+ {
+ mDefault->getChildren(name, children, FALSE);
+ }
+}
+
+bool LLXMLNode::getAttribute(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing)
+{
+ return getAttribute(gStringTable.checkStringEntry(name), node, use_default_if_missing);
+}
+
+bool LLXMLNode::getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing)
+{
+ LLXMLAttribList::const_iterator child_itr = mAttributes.find(name);
+ if (child_itr != mAttributes.end())
+ {
+ node = (*child_itr).second;
+ return true;
+ }
+ if (use_default_if_missing && !mDefault.isNull())
+ {
+ return mDefault->getAttribute(name, node, FALSE);
+ }
+ node = new LLXMLNode();
+ return false;
+}
+
+bool LLXMLNode::setAttributeString(const LLString& attr, const LLString& value)
+{
+ LLStringTableEntry* name = gStringTable.checkStringEntry(attr);
+ LLXMLAttribList::const_iterator child_itr = mAttributes.find(name);
+ if (child_itr != mAttributes.end())
+ {
+ LLXMLNodePtr node = (*child_itr).second;
+ node->setValue(value);
+ return true;
+ }
+ return false;
+}
+
+BOOL LLXMLNode::hasAttribute(const LLString& name )
+{
+ LLXMLNodePtr node;
+ return getAttribute(name, node);
+}
+
+BOOL LLXMLNode::getAttributeBOOL(const LLString& name, BOOL& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getBoolValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeU8(const LLString& name, U8& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getByteValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeS8(const LLString& name, S8& value )
+{
+ LLXMLNodePtr node;
+ S32 val;
+ if (!(getAttribute(name, node) && node->getIntValue(1, &val)))
+ {
+ return false;
+ }
+ value = val;
+ return true;
+}
+
+BOOL LLXMLNode::getAttributeU16(const LLString& name, U16& value )
+{
+ LLXMLNodePtr node;
+ U32 val;
+ if (!(getAttribute(name, node) && node->getUnsignedValue(1, &val)))
+ {
+ return false;
+ }
+ value = val;
+ return true;
+}
+
+BOOL LLXMLNode::getAttributeS16(const LLString& name, S16& value )
+{
+ LLXMLNodePtr node;
+ S32 val;
+ if (!(getAttribute(name, node) && node->getIntValue(1, &val)))
+ {
+ return false;
+ }
+ value = val;
+ return true;
+}
+
+BOOL LLXMLNode::getAttributeU32(const LLString& name, U32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getUnsignedValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeS32(const LLString& name, S32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getIntValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeF32(const LLString& name, F32& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeF64(const LLString& name, F64& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getDoubleValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeColor(const LLString& name, LLColor4& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mV));
+}
+
+BOOL LLXMLNode::getAttributeColor4(const LLString& name, LLColor4& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mV));
+}
+
+BOOL LLXMLNode::getAttributeColor4U(const LLString& name, LLColor4U& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getByteValue(4, value.mV));
+}
+
+BOOL LLXMLNode::getAttributeVector3(const LLString& name, LLVector3& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(3, value.mV));
+}
+
+BOOL LLXMLNode::getAttributeVector3d(const LLString& name, LLVector3d& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getDoubleValue(3, value.mdV));
+}
+
+BOOL LLXMLNode::getAttributeQuat(const LLString& name, LLQuaternion& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getFloatValue(4, value.mQ));
+}
+
+BOOL LLXMLNode::getAttributeUUID(const LLString& name, LLUUID& value )
+{
+ LLXMLNodePtr node;
+ return (getAttribute(name, node) && node->getUUIDValue(1, &value));
+}
+
+BOOL LLXMLNode::getAttributeString(const LLString& name, LLString& value )
+{
+ LLXMLNodePtr node;
+ if (!getAttribute(name, node))
+ {
+ return false;
+ }
+ value = node->getValue();
+ return true;
+}
+
+LLXMLNodePtr LLXMLNode::getRoot()
+{
+ if (mParent == NULL)
+ {
+ return this;
+ }
+ return mParent->getRoot();
+}
+
+/*static */
+const char *LLXMLNode::skipWhitespace(const char *str)
+{
+ // skip whitespace characters
+ while (str[0] == ' ' || str[0] == '\t' || str[0] == '\n') ++str;
+ return str;
+}
+
+/*static */
+const char *LLXMLNode::skipNonWhitespace(const char *str)
+{
+ // skip non-whitespace characters
+ while (str[0] != ' ' && str[0] != '\t' && str[0] != '\n' && str[0] != 0) ++str;
+ return str;
+}
+
+/*static */
+const char *LLXMLNode::parseInteger(const char *str, U64 *dest, BOOL *is_negative, U32 precision, Encoding encoding)
+{
+ *dest = 0;
+ *is_negative = FALSE;
+
+ str = skipWhitespace(str);
+
+ if (str[0] == 0) return NULL;
+
+ if (encoding == ENCODING_DECIMAL || encoding == ENCODING_DEFAULT)
+ {
+ if (str[0] == '+')
+ {
+ ++str;
+ }
+ if (str[0] == '-')
+ {
+ *is_negative = TRUE;
+ ++str;
+ }
+
+ str = skipWhitespace(str);
+
+ U64 ret = 0;
+ while (str[0] >= '0' && str[0] <= '9')
+ {
+ ret *= 10;
+ ret += str[0] - '0';
+ ++str;
+ }
+
+ if (str[0] == '.')
+ {
+ // If there is a fractional part, skip it
+ str = skipNonWhitespace(str);
+ }
+
+ *dest = ret;
+ return str;
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ U64 ret = 0;
+ str = skipWhitespace(str);
+ for (U32 pos=0; pos<(precision/4); ++pos)
+ {
+ ret <<= 4;
+ str = skipWhitespace(str);
+ if (str[0] >= '0' && str[0] <= '9')
+ {
+ ret += str[0] - '0';
+ }
+ else if (str[0] >= 'a' && str[0] <= 'f')
+ {
+ ret += str[0] - 'a' + 10;
+ }
+ else if (str[0] >= 'A' && str[0] <= 'F')
+ {
+ ret += str[0] - 'A' + 10;
+ }
+ else
+ {
+ return NULL;
+ }
+ ++str;
+ }
+
+ *dest = ret;
+ return str;
+ }
+ return NULL;
+}
+
+// 25 elements - decimal expansions of 1/(2^n), multiplied by 10 each iteration
+const U64 float_coeff_table[] =
+ { 5, 25, 125, 625, 3125,
+ 15625, 78125, 390625, 1953125, 9765625,
+ 48828125, 244140625, 1220703125, 6103515625LL, 30517578125LL,
+ 152587890625LL, 762939453125LL, 3814697265625LL, 19073486328125LL, 95367431640625LL,
+ 476837158203125LL, 2384185791015625LL, 11920928955078125LL, 59604644775390625LL, 298023223876953125LL };
+
+// 36 elements - decimal expansions of 1/(2^n) after the last 28, truncated, no multiply each iteration
+const U64 float_coeff_table_2[] =
+ { 149011611938476562LL,74505805969238281LL,
+ 37252902984619140LL, 18626451492309570LL, 9313225746154785LL, 4656612873077392LL,
+ 2328306436538696LL, 1164153218269348LL, 582076609134674LL, 291038304567337LL,
+ 145519152283668LL, 72759576141834LL, 36379788070917LL, 18189894035458LL,
+ 9094947017729LL, 4547473508864LL, 2273736754432LL, 1136868377216LL,
+ 568434188608LL, 284217094304LL, 142108547152LL, 71054273576LL,
+ 35527136788LL, 17763568394LL, 8881784197LL, 4440892098LL,
+ 2220446049LL, 1110223024LL, 555111512LL, 277555756LL,
+ 138777878, 69388939, 34694469, 17347234,
+ 8673617, 4336808, 2168404, 1084202,
+ 542101, 271050, 135525, 67762,
+ };
+
+/*static */
+const char *LLXMLNode::parseFloat(const char *str, F64 *dest, U32 precision, Encoding encoding)
+{
+ str = skipWhitespace(str);
+
+ if (str[0] == 0) return NULL;
+
+ if (encoding == ENCODING_DECIMAL || encoding == ENCODING_DEFAULT)
+ {
+ str = skipWhitespace(str);
+
+ if (memcmp(str, "inf", 3) == 0)
+ {
+ *(U64 *)dest = 0x7FF0000000000000ll;
+ return str + 3;
+ }
+ if (memcmp(str, "-inf", 4) == 0)
+ {
+ *(U64 *)dest = 0xFFF0000000000000ll;
+ return str + 4;
+ }
+ if (memcmp(str, "1.#INF", 6) == 0)
+ {
+ *(U64 *)dest = 0x7FF0000000000000ll;
+ return str + 6;
+ }
+ if (memcmp(str, "-1.#INF", 7) == 0)
+ {
+ *(U64 *)dest = 0xFFF0000000000000ll;
+ return str + 7;
+ }
+
+ F64 negative = 1.0f;
+ if (str[0] == '+')
+ {
+ ++str;
+ }
+ if (str[0] == '-')
+ {
+ negative = -1.0f;
+ ++str;
+ }
+
+ const char* base_str = str;
+ str = skipWhitespace(str);
+
+ // Parse the integer part of the expression
+ U64 int_part = 0;
+ while (str[0] >= '0' && str[0] <= '9')
+ {
+ int_part *= 10;
+ int_part += U64(str[0] - '0');
+ ++str;
+ }
+
+ U64 f_part = 0;//, f_decimal = 1;
+ if (str[0] == '.')
+ {
+ ++str;
+ U64 remainder = 0;
+ U32 pos = 0;
+ // Parse the decimal part of the expression
+ while (str[0] >= '0' && str[0] <= '9' && pos < 25)
+ {
+ remainder = (remainder*10) + U64(str[0] - '0');
+ f_part <<= 1;
+ //f_decimal <<= 1;
+ // Check the n'th bit
+ if (remainder >= float_coeff_table[pos])
+ {
+ remainder -= float_coeff_table[pos];
+ f_part |= 1;
+ }
+ ++pos;
+ ++str;
+ }
+ if (pos == 25)
+ {
+ // Drop any excessive digits
+ while (str[0] >= '0' && str[0] <= '9')
+ {
+ ++str;
+ }
+ }
+ else
+ {
+ while (pos < 25)
+ {
+ remainder *= 10;
+ f_part <<= 1;
+ //f_decimal <<= 1;
+ // Check the n'th bit
+ if (remainder >= float_coeff_table[pos])
+ {
+ remainder -= float_coeff_table[pos];
+ f_part |= 1;
+ }
+ ++pos;
+ }
+ }
+ pos = 0;
+ while (pos < 36)
+ {
+ f_part <<= 1;
+ //f_decimal <<= 1;
+ if (remainder >= float_coeff_table_2[pos])
+ {
+ remainder -= float_coeff_table_2[pos];
+ f_part |= 1;
+ }
+ ++pos;
+ }
+ }
+
+ F64 ret = F64(int_part) + (F64(f_part)/F64(1LL<<61));
+
+ F64 exponent = 1.f;
+ if (str[0] == 'e')
+ {
+ // Scientific notation!
+ ++str;
+ U64 exp;
+ BOOL is_negative;
+ str = parseInteger(str, &exp, &is_negative, 64, ENCODING_DECIMAL);
+ if (str == NULL)
+ {
+ exp = 1;
+ }
+ F64 exp_d = F64(exp) * (is_negative?-1:1);
+ exponent = pow(10.0, exp_d);
+ }
+
+ if (str == base_str)
+ {
+ // no digits parsed
+ return NULL;
+ }
+ else
+ {
+ *dest = ret*negative*exponent;
+ return str;
+ }
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ U64 bytes_dest;
+ BOOL is_negative;
+ str = parseInteger(str, (U64 *)&bytes_dest, &is_negative, precision, ENCODING_HEX);
+ // Upcast to F64
+ switch (precision)
+ {
+ case 32:
+ {
+ U32 short_dest = (U32)bytes_dest;
+ F32 ret_val = *(F32 *)&short_dest;
+ *dest = ret_val;
+ }
+ break;
+ case 64:
+ *dest = *(F64 *)&bytes_dest;
+ break;
+ default:
+ return NULL;
+ }
+ return str;
+ }
+ return NULL;
+}
+
+U32 LLXMLNode::getBoolValue(U32 expected_length, BOOL *array)
+{
+ llassert(array);
+
+ // Check type - accept booleans or strings
+ if (mType != TYPE_BOOLEAN && mType != TYPE_STRING && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ LLString *str_array = new LLString[expected_length];
+
+ U32 length = getStringValue(expected_length, str_array);
+
+ U32 ret_length = 0;
+ for (U32 i=0; i<length; ++i)
+ {
+ LLString::toLower(str_array[i]);
+ if (str_array[i] == "false")
+ {
+ array[ret_length++] = FALSE;
+ }
+ else if (str_array[i] == "true")
+ {
+ array[ret_length++] = TRUE;
+ }
+ }
+
+ delete[] str_array;
+
+#if LL_DEBUG
+ if (ret_length != expected_length)
+ {
+ lldebugs << "LLXMLNode::getBoolValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << ret_length << llendl;
+ }
+#endif
+ return ret_length;
+}
+
+U32 LLXMLNode::getByteValue(U32 expected_length, U8 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept bytes or integers (below 256 only)
+ if (mType != TYPE_INTEGER
+ && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getByteValue asked for " << expected_length
+ << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i;
+ for (i=0; i<expected_length; ++i)
+ {
+ U64 value;
+ BOOL is_negative;
+ value_string = parseInteger(value_string, &value, &is_negative, 8, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ if (value > 255 || is_negative)
+ {
+ llwarns << "getByteValue: Value outside of valid range." << llendl;
+ break;
+ }
+ array[i] = U8(value);
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getByteValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getIntValue(U32 expected_length, S32 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept bytes or integers
+ if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getIntValue asked for " << expected_length
+ << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i = 0;
+ for (i=0; i<expected_length; ++i)
+ {
+ U64 value;
+ BOOL is_negative;
+ value_string = parseInteger(value_string, &value, &is_negative, 32, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ if (value > 0x7fffffff)
+ {
+ llwarns << "getIntValue: Value outside of valid range." << llendl;
+ break;
+ }
+ array[i] = S32(value) * (is_negative?-1:1);
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getIntValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getUnsignedValue(U32 expected_length, U32 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept bytes or integers
+ if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getUnsignedValue asked for " << expected_length
+ << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i = 0;
+ // Int type
+ for (i=0; i<expected_length; ++i)
+ {
+ U64 value;
+ BOOL is_negative;
+ value_string = parseInteger(value_string, &value, &is_negative, 32, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ if (is_negative || value > 0xffffffff)
+ {
+ llwarns << "getUnsignedValue: Value outside of valid range." << llendl;
+ break;
+ }
+ array[i] = U32(value);
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getUnsignedValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+
+ return i;
+}
+
+U32 LLXMLNode::getLongValue(U32 expected_length, U64 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept bytes or integers
+ if (mType != TYPE_INTEGER && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getLongValue asked for " << expected_length << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i = 0;
+ // Int type
+ for (i=0; i<expected_length; ++i)
+ {
+ U64 value;
+ BOOL is_negative;
+ value_string = parseInteger(value_string, &value, &is_negative, 64, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ if (is_negative)
+ {
+ llwarns << "getLongValue: Value outside of valid range." << llendl;
+ break;
+ }
+ array[i] = value;
+ }
+
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getLongValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+
+ return i;
+}
+
+U32 LLXMLNode::getFloatValue(U32 expected_length, F32 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept only floats or doubles
+ if (mType != TYPE_FLOAT && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getFloatValue asked for " << expected_length << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i;
+ for (i=0; i<expected_length; ++i)
+ {
+ F64 value;
+ value_string = parseFloat(value_string, &value, 32, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ array[i] = F32(value);
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getFloatValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getDoubleValue(U32 expected_length, F64 *array, Encoding encoding)
+{
+ llassert(array);
+
+ // Check type - accept only floats or doubles
+ if (mType != TYPE_FLOAT && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getDoubleValue asked for " << expected_length << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ if (encoding == ENCODING_DEFAULT)
+ {
+ encoding = mEncoding;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i;
+ for (i=0; i<expected_length; ++i)
+ {
+ F64 value;
+ value_string = parseFloat(value_string, &value, 64, encoding);
+ if (value_string == NULL)
+ {
+ break;
+ }
+ array[i] = value;
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getDoubleValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getStringValue(U32 expected_length, LLString *array)
+{
+ llassert(array);
+
+ // Can always return any value as a string
+
+ if (mLength > 0 && mLength != expected_length)
+ {
+ llwarns << "XMLNode::getStringValue asked for " << expected_length << " elements, while node has " << mLength << llendl;
+ return 0;
+ }
+
+ U32 num_returned_strings = 0;
+
+ // Array of strings is whitespace-separated
+ const std::string sep(" \n\t");
+
+ std::string::size_type n = 0;
+ std::string::size_type m = 0;
+ while(1)
+ {
+ if (num_returned_strings >= expected_length)
+ {
+ break;
+ }
+ n = mValue.find_first_not_of(sep, m);
+ m = mValue.find_first_of(sep, n);
+ if (m == std::string::npos)
+ {
+ break;
+ }
+ array[num_returned_strings++] = mValue.substr(n,m-n);
+ }
+ if (n != std::string::npos && num_returned_strings < expected_length)
+ {
+ array[num_returned_strings++] = mValue.substr(n);
+ }
+#if LL_DEBUG
+ if (num_returned_strings != expected_length)
+ {
+ lldebugs << "LLXMLNode::getStringValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << num_returned_strings << llendl;
+ }
+#endif
+
+ return num_returned_strings;
+}
+
+U32 LLXMLNode::getUUIDValue(U32 expected_length, LLUUID *array)
+{
+ llassert(array);
+
+ // Check type
+ if (mType != TYPE_UUID && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ const char *value_string = mValue.c_str();
+
+ U32 i;
+ for (i=0; i<expected_length; ++i)
+ {
+ LLUUID uuid_value;
+ value_string = skipWhitespace(value_string);
+
+ if (strlen(value_string) < (UUID_STR_LENGTH-1))
+ {
+ break;
+ }
+ char uuid_string[UUID_STR_LENGTH];
+ memcpy(uuid_string, value_string, (UUID_STR_LENGTH-1));
+ uuid_string[(UUID_STR_LENGTH-1)] = 0;
+
+ if (!LLUUID::parseUUID(uuid_string, &uuid_value))
+ {
+ break;
+ }
+ value_string = &value_string[(UUID_STR_LENGTH-1)];
+ array[i] = uuid_value;
+ }
+#if LL_DEBUG
+ if (i != expected_length)
+ {
+ lldebugs << "LLXMLNode::getUUIDValue() failed for node named '"
+ << mName->mString << "' -- expected " << expected_length << " but "
+ << "only found " << i << llendl;
+ }
+#endif
+ return i;
+}
+
+U32 LLXMLNode::getNodeRefValue(U32 expected_length, LLXMLNode **array)
+{
+ llassert(array);
+
+ // Check type
+ if (mType != TYPE_NODEREF && mType != TYPE_UNKNOWN)
+ {
+ return 0;
+ }
+
+ LLString *string_array = new LLString[expected_length];
+
+ U32 num_strings = getStringValue(expected_length, string_array);
+
+ U32 num_returned_refs = 0;
+
+ LLXMLNodePtr root = getRoot();
+ for (U32 strnum=0; strnum<num_strings; ++strnum)
+ {
+ LLXMLNodeList node_list;
+ root->findID(string_array[strnum], node_list);
+ if (node_list.empty())
+ {
+ llwarns << "XML: Could not find node ID: " << string_array[strnum] << llendl;
+ }
+ else if (node_list.size() > 1)
+ {
+ llwarns << "XML: Node ID not unique: " << string_array[strnum] << llendl;
+ }
+ else
+ {
+ LLXMLNodeList::const_iterator list_itr = node_list.begin();
+ if (list_itr != node_list.end())
+ {
+ LLXMLNode* child = (*list_itr).second;
+
+ array[num_returned_refs++] = child;
+ }
+ }
+ }
+
+ delete[] string_array;
+
+ return num_returned_refs;
+}
+
+void LLXMLNode::setBoolValue(U32 length, const BOOL *array)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value = llformat("%s %s", new_value.c_str(), array[pos]?"true":"false");
+ }
+ else
+ {
+ new_value = array[pos]?"true":"false";
+ }
+ }
+
+ mValue = new_value;
+ mEncoding = ENCODING_DEFAULT;
+ mLength = length;
+ mType = TYPE_BOOLEAN;
+}
+
+void LLXMLNode::setByteValue(U32 length, const U8* const array, Encoding encoding)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(llformat(" %u", array[pos]));
+ }
+ else
+ {
+ new_value = llformat("%u", array[pos]);
+ }
+ }
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0 && pos % 16 == 0)
+ {
+ new_value.append(llformat(" %02X", array[pos]));
+ }
+ else
+ {
+ new_value.append(llformat("%02X", array[pos]));
+ }
+ }
+ }
+ // TODO -- Handle Base32
+
+ mValue = new_value;
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_INTEGER;
+ mPrecision = 8;
+}
+
+
+void LLXMLNode::setIntValue(U32 length, const S32 *array, Encoding encoding)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(llformat(" %d", array[pos]));
+ }
+ else
+ {
+ new_value = llformat("%d", array[pos]);
+ }
+ }
+ mValue = new_value;
+ }
+ else if (encoding == ENCODING_HEX)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0 && pos % 16 == 0)
+ {
+ new_value.append(llformat(" %08X", ((U32 *)array)[pos]));
+ }
+ else
+ {
+ new_value.append(llformat("%08X", ((U32 *)array)[pos]));
+ }
+ }
+ mValue = new_value;
+ }
+ else
+ {
+ mValue = new_value;
+ }
+ // TODO -- Handle Base32
+
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_INTEGER;
+ mPrecision = 32;
+}
+
+void LLXMLNode::setUnsignedValue(U32 length, const U32* array, Encoding encoding)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(llformat(" %u", array[pos]));
+ }
+ else
+ {
+ new_value = llformat("%u", array[pos]);
+ }
+ }
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0 && pos % 16 == 0)
+ {
+ new_value.append(llformat(" %08X", array[pos]));
+ }
+ else
+ {
+ new_value.append(llformat("%08X", array[pos]));
+ }
+ }
+ mValue = new_value;
+ }
+ // TODO -- Handle Base32
+
+ mValue = new_value;
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_INTEGER;
+ mPrecision = 32;
+}
+
+#if LL_WINDOWS
+#define PU64 "I64u"
+#else
+#define PU64 "llu"
+#endif
+
+void LLXMLNode::setLongValue(U32 length, const U64* array, Encoding encoding)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(llformat(" %" PU64, array[pos]));
+ }
+ else
+ {
+ new_value = llformat("%" PU64, array[pos]);
+ }
+ }
+ mValue = new_value;
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ U32 upper_32 = U32(array[pos]>>32);
+ U32 lower_32 = U32(array[pos]&0xffffffff);
+ if (pos > 0 && pos % 8 == 0)
+ {
+ new_value.append(llformat(" %08X%08X", upper_32, lower_32));
+ }
+ else
+ {
+ new_value.append(llformat("%08X%08X", upper_32, lower_32));
+ }
+ }
+ mValue = new_value;
+ }
+ else
+ {
+ mValue = new_value;
+ }
+ // TODO -- Handle Base32
+
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_INTEGER;
+ mPrecision = 64;
+}
+
+void LLXMLNode::setFloatValue(U32 length, const F32 *array, Encoding encoding, U32 precision)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ char format_string[10];
+ if (precision > 0)
+ {
+ if (precision > 25)
+ {
+ precision = 25;
+ }
+ sprintf(format_string, "%%.%dg", precision);
+ }
+ else
+ {
+ sprintf(format_string, "%%g");
+ }
+
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(" ");
+ new_value.append(llformat(format_string, array[pos]));
+ }
+ else
+ {
+ new_value.assign(llformat(format_string, array[pos]));
+ }
+ }
+ mValue = new_value;
+ }
+ else if (encoding == ENCODING_HEX)
+ {
+ U32 *byte_array = (U32 *)array;
+ setUnsignedValue(length, byte_array, ENCODING_HEX);
+ }
+ else
+ {
+ mValue = new_value;
+ }
+
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_FLOAT;
+ mPrecision = 32;
+}
+
+void LLXMLNode::setDoubleValue(U32 length, const F64 *array, Encoding encoding, U32 precision)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ if (encoding == ENCODING_DEFAULT || encoding == ENCODING_DECIMAL)
+ {
+ char format_string[10];
+ if (precision > 0)
+ {
+ if (precision > 25)
+ {
+ precision = 25;
+ }
+ sprintf(format_string, "%%.%dg", precision);
+ }
+ else
+ {
+ sprintf(format_string, "%%g");
+ }
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (pos > 0)
+ {
+ new_value.append(" ");
+ new_value.append(llformat(format_string, array[pos]));
+ }
+ else
+ {
+ new_value.assign(llformat(format_string, array[pos]));
+ }
+ }
+ mValue = new_value;
+ }
+ if (encoding == ENCODING_HEX)
+ {
+ U64 *byte_array = (U64 *)array;
+ setLongValue(length, byte_array, ENCODING_HEX);
+ }
+ else
+ {
+ mValue = new_value;
+ }
+ // TODO -- Handle Base32
+
+ mEncoding = encoding;
+ mLength = length;
+ mType = TYPE_FLOAT;
+ mPrecision = 64;
+}
+
+// static
+LLString LLXMLNode::escapeXML(const LLString& xml)
+{
+ LLString out;
+ for (LLString::size_type i = 0; i < xml.size(); ++i)
+ {
+ char c = xml[i];
+ switch(c)
+ {
+ case '"': out.append("&quot;"); break;
+ case '\'': out.append("&apos;"); break;
+ case '&': out.append("&amp;"); break;
+ case '<': out.append("&lt;"); break;
+ case '>': out.append("&gt;"); break;
+ default: out.push_back(c); break;
+ }
+ }
+ return out;
+}
+
+void LLXMLNode::setStringValue(U32 length, const LLString *array)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ new_value.append(escapeXML(array[pos]));
+ if (pos < length-1) new_value.append(" ");
+ }
+
+ mValue = new_value;
+ mEncoding = ENCODING_DEFAULT;
+ mLength = length;
+ mType = TYPE_STRING;
+}
+
+void LLXMLNode::setUUIDValue(U32 length, const LLUUID *array)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ new_value.append(array[pos].getString());
+ if (pos < length-1) new_value.append(" ");
+ }
+
+ mValue = new_value;
+ mEncoding = ENCODING_DEFAULT;
+ mLength = length;
+ mType = TYPE_UUID;
+}
+
+void LLXMLNode::setNodeRefValue(U32 length, const LLXMLNode **array)
+{
+ if (length == 0) return;
+
+ LLString new_value;
+ for (U32 pos=0; pos<length; ++pos)
+ {
+ if (array[pos]->mID != "")
+ {
+ new_value.append(array[pos]->mID);
+ }
+ else
+ {
+ new_value.append("(null)");
+ }
+ if (pos < length-1) new_value.append(" ");
+ }
+
+ mValue = new_value;
+ mEncoding = ENCODING_DEFAULT;
+ mLength = length;
+ mType = TYPE_NODEREF;
+}
+
+void LLXMLNode::setValue(const LLString& value)
+{
+ if (TYPE_CONTAINER == mType)
+ {
+ mType = TYPE_UNKNOWN;
+ }
+ mValue = value;
+}
+
+void LLXMLNode::setDefault(LLXMLNode *default_node)
+{
+ mDefault = default_node;
+}
+
+void LLXMLNode::findDefault(LLXMLNode *defaults_list)
+{
+ if (defaults_list)
+ {
+ LLXMLNodeList children;
+ defaults_list->getChildren(mName->mString, children);
+
+ LLXMLNodeList::const_iterator children_itr;
+ LLXMLNodeList::const_iterator children_end = children.end();
+ for (children_itr = children.begin(); children_itr != children_end; ++children_itr)
+ {
+ LLXMLNode* child = (*children_itr).second;
+ if (child->mVersionMajor == mVersionMajor &&
+ child->mVersionMinor == mVersionMinor)
+ {
+ mDefault = child;
+ return;
+ }
+ }
+ }
+ mDefault = NULL;
+}
+
+BOOL LLXMLNode::deleteChildren(const LLString& name)
+{
+ U32 removed_count = 0;
+ LLXMLNodeList node_list;
+ findName(name, node_list);
+ if (!node_list.empty())
+ {
+ // TODO -- use multimap::find()
+ // TODO -- need to watch out for invalid iterators
+ LLXMLNodeList::iterator children_itr;
+ for (children_itr = node_list.begin(); children_itr != node_list.end(); ++children_itr)
+ {
+ LLXMLNode* child = (*children_itr).second;
+ if (deleteChild(child))
+ {
+ removed_count++;
+ }
+ }
+ }
+ return removed_count > 0 ? TRUE : FALSE;
+}
+
+BOOL LLXMLNode::deleteChildren(LLStringTableEntry* name)
+{
+ U32 removed_count = 0;
+ LLXMLNodeList node_list;
+ findName(name, node_list);
+ if (!node_list.empty())
+ {
+ // TODO -- use multimap::find()
+ // TODO -- need to watch out for invalid iterators
+ LLXMLNodeList::iterator children_itr;
+ for (children_itr = node_list.begin(); children_itr != node_list.end(); ++children_itr)
+ {
+ LLXMLNode* child = (*children_itr).second;
+ if (deleteChild(child))
+ {
+ removed_count++;
+ }
+ }
+ }
+ return removed_count > 0 ? TRUE : FALSE;
+}
+
+void LLXMLNode::setAttributes(LLXMLNode::ValueType type, U32 precision, LLXMLNode::Encoding encoding, U32 length)
+{
+ mType = type;
+ mEncoding = encoding;
+ mPrecision = precision;
+ mLength = length;
+}
+
+void LLXMLNode::setName(const LLString& name)
+{
+ setName(gStringTable.addStringEntry(name));
+}
+
+void LLXMLNode::setName(LLStringTableEntry* name)
+{
+ LLXMLNode* old_parent = mParent;
+ if (mParent)
+ {
+ // we need to remove and re-add to the parent so that
+ // the multimap key agrees with this node's name
+ mParent->removeChild(this);
+ }
+ mName = name;
+ if (old_parent)
+ {
+ old_parent->addChild(this);
+ }
+}
+
+void LLXMLNode::appendValue(const LLString& value)
+{
+ mValue.append(value);
+}
+
+U32 LLXMLNode::getChildCount() const
+{
+ if (mChildren)
+ {
+ return mChildren->map.size();
+ }
+ return 0;
+}
+
+//***************************************************
+// UNIT TESTING
+//***************************************************
+
+U32 get_rand(U32 max_value)
+{
+ U32 random_num = rand() + ((U32)rand() << 16);
+ return (random_num % max_value);
+}
+
+LLXMLNode *get_rand_node(LLXMLNode *node)
+{
+ if (node->mChildren)
+ {
+ U32 num_children = node->mChildren->map.size();
+ if (get_rand(2) == 0)
+ {
+ while (true)
+ {
+ S32 child_num = S32(get_rand(num_children*2)) - num_children;
+ LLXMLChildList::iterator itor = node->mChildren->map.begin();
+ while (child_num > 0)
+ {
+ --child_num;
+ ++itor;
+ }
+ if (!itor->second->mIsAttribute)
+ {
+ return get_rand_node(itor->second);
+ }
+ }
+ }
+ }
+ return node;
+}
+
+void LLXMLNode::createUnitTest(S32 max_num_children)
+{
+ // Random ID
+ char rand_id[20];
+ U32 rand_id_len = get_rand(10)+5;
+ U32 pos = 0;
+ for (; pos<rand_id_len; ++pos)
+ {
+ rand_id[pos] = get_rand(26)+'a';
+ }
+ rand_id[pos] = 0;
+ mID = rand_id;
+
+ if (max_num_children < 2)
+ {
+ setStringValue(1, &mID);
+ return;
+ }
+
+ // Checksums
+ U32 integer_checksum = 0;
+ U64 long_checksum = 0;
+ U32 bool_true_count = 0;
+ LLUUID uuid_checksum;
+ U32 noderef_checksum = 0;
+ U32 float_checksum = 0;
+
+ // Create a random number of children
+ U32 num_children = get_rand(max_num_children)+1;
+ for (U32 child_num=0; child_num<num_children; ++child_num)
+ {
+ // Random Name
+ char child_name[20];
+ U32 child_name_len = get_rand(10)+5;
+ pos = 0;
+ for (; pos<child_name_len; ++pos)
+ {
+ child_name[pos] = get_rand(26)+'a';
+ }
+ child_name[pos] = 0;
+
+ LLXMLNode *new_child = createChild(child_name, FALSE);
+
+ // Random ID
+ char child_id[20];
+ U32 child_id_len = get_rand(10)+5;
+ pos = 0;
+ for (; pos<child_id_len; ++pos)
+ {
+ child_id[pos] = get_rand(26)+'a';
+ }
+ child_id[pos] = 0;
+ new_child->mID = child_id;
+
+ // Random Length
+ U32 array_size = get_rand(28)+1;
+
+ // Random Encoding
+ Encoding new_encoding = get_rand(2)?ENCODING_DECIMAL:ENCODING_HEX;
+
+ // Random Type
+ int type = get_rand(8);
+ switch (type)
+ {
+ case 0: // TYPE_CONTAINER
+ new_child->createUnitTest(max_num_children/2);
+ break;
+ case 1: // TYPE_BOOLEAN
+ {
+ BOOL random_bool_values[30];
+ for (U32 value=0; value<array_size; ++value)
+ {
+ random_bool_values[value] = get_rand(2);
+ if (random_bool_values[value])
+ {
+ ++bool_true_count;
+ }
+ }
+ new_child->setBoolValue(array_size, random_bool_values);
+ }
+ break;
+ case 2: // TYPE_INTEGER (32-bit)
+ {
+ U32 random_int_values[30];
+ for (U32 value=0; value<array_size; ++value)
+ {
+ random_int_values[value] = get_rand(0xffffffff);
+ integer_checksum ^= random_int_values[value];
+ }
+ new_child->setUnsignedValue(array_size, random_int_values, new_encoding);
+ }
+ break;
+ case 3: // TYPE_INTEGER (64-bit)
+ {
+ U64 random_int_values[30];
+ for (U64 value=0; value<array_size; ++value)
+ {
+ random_int_values[value] = (U64(get_rand(0xffffffff)) << 32) + get_rand(0xffffffff);
+ long_checksum ^= random_int_values[value];
+ }
+ new_child->setLongValue(array_size, random_int_values, new_encoding);
+ }
+ break;
+ case 4: // TYPE_FLOAT (32-bit)
+ {
+ F32 random_float_values[30];
+ for (U32 value=0; value<array_size; ++value)
+ {
+ S32 exponent = get_rand(256) - 128;
+ S32 fractional_part = get_rand(0xffffffff);
+ S32 sign = get_rand(2) * 2 - 1;
+ random_float_values[value] = F32(fractional_part) / F32(0xffffffff) * exp(F32(exponent)) * F32(sign);
+
+ U32 *float_bits = &((U32 *)random_float_values)[value];
+ if (*float_bits == 0x80000000)
+ {
+ *float_bits = 0x00000000;
+ }
+ float_checksum ^= (*float_bits & 0xfffff000);
+ }
+ new_child->setFloatValue(array_size, random_float_values, new_encoding, 12);
+ }
+ break;
+ case 5: // TYPE_FLOAT (64-bit)
+ {
+ F64 random_float_values[30];
+ for (U32 value=0; value<array_size; ++value)
+ {
+ S32 exponent = get_rand(2048) - 1024;
+ S32 fractional_part = get_rand(0xffffffff);
+ S32 sign = get_rand(2) * 2 - 1;
+ random_float_values[value] = F64(fractional_part) / F64(0xffffffff) * exp(F64(exponent)) * F64(sign);
+
+ U64 *float_bits = &((U64 *)random_float_values)[value];
+ if (*float_bits == 0x8000000000000000ll)
+ {
+ *float_bits = 0x0000000000000000ll;
+ }
+ float_checksum ^= ((*float_bits & 0xfffffff000000000ll) >> 32);
+ }
+ new_child->setDoubleValue(array_size, random_float_values, new_encoding, 12);
+ }
+ break;
+ case 6: // TYPE_UUID
+ {
+ LLUUID random_uuid_values[30];
+ for (U32 value=0; value<array_size; ++value)
+ {
+ random_uuid_values[value].generate();
+ for (S32 byte=0; byte<UUID_BYTES; ++byte)
+ {
+ uuid_checksum.mData[byte] ^= random_uuid_values[value].mData[byte];
+ }
+ }
+ new_child->setUUIDValue(array_size, random_uuid_values);
+ }
+ break;
+ case 7: // TYPE_NODEREF
+ {
+ LLXMLNode *random_node_array[30];
+ LLXMLNode *root = getRoot();
+ for (U32 value=0; value<array_size; ++value)
+ {
+ random_node_array[value] = get_rand_node(root);
+ const char *node_name = random_node_array[value]->mName->mString;
+ for (U32 pos=0; pos<strlen(node_name); ++pos)
+ {
+ U32 hash_contrib = U32(node_name[pos]) << ((pos % 4) * 8);
+ noderef_checksum ^= hash_contrib;
+ }
+ }
+ new_child->setNodeRefValue(array_size, (const LLXMLNode **)random_node_array);
+ }
+ break;
+ }
+ }
+
+ createChild("integer_checksum", TRUE)->setUnsignedValue(1, &integer_checksum, LLXMLNode::ENCODING_HEX);
+ createChild("long_checksum", TRUE)->setLongValue(1, &long_checksum, LLXMLNode::ENCODING_HEX);
+ createChild("bool_true_count", TRUE)->setUnsignedValue(1, &bool_true_count, LLXMLNode::ENCODING_HEX);
+ createChild("uuid_checksum", TRUE)->setUUIDValue(1, &uuid_checksum);
+ createChild("noderef_checksum", TRUE)->setUnsignedValue(1, &noderef_checksum, LLXMLNode::ENCODING_HEX);
+ createChild("float_checksum", TRUE)->setUnsignedValue(1, &float_checksum, LLXMLNode::ENCODING_HEX);
+}
+
+BOOL LLXMLNode::performUnitTest(LLString &error_buffer)
+{
+ if (!mChildren)
+ {
+ error_buffer.append(llformat("ERROR Node %s: No children found.\n", mName->mString));
+ return FALSE;
+ }
+
+ // Checksums
+ U32 integer_checksum = 0;
+ U32 bool_true_count = 0;
+ LLUUID uuid_checksum;
+ U32 noderef_checksum = 0;
+ U32 float_checksum = 0;
+ U64 long_checksum = 0;
+
+ LLXMLChildList::iterator itor;
+ for (itor=mChildren->map.begin(); itor!=mChildren->map.end(); ++itor)
+ {
+ LLXMLNode *node = itor->second;
+ if (node->mIsAttribute)
+ {
+ continue;
+ }
+ if (node->mType == TYPE_CONTAINER)
+ {
+ if (!node->performUnitTest(error_buffer))
+ {
+ error_buffer.append(llformat("Child test failed for %s.\n", mName->mString));
+ //return FALSE;
+ }
+ continue;
+ }
+ if (node->mLength < 1 || node->mLength > 30)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Invalid array length %d, child %s.\n", mName->mString, node->mLength, node->mName->mString));
+ return FALSE;
+ }
+ switch (node->mType)
+ {
+ case TYPE_CONTAINER:
+ case TYPE_UNKNOWN:
+ break;
+ case TYPE_BOOLEAN:
+ {
+ BOOL bool_array[30];
+ if (node->getBoolValue(node->mLength, bool_array) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read boolean array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ if (bool_array[pos])
+ {
+ ++bool_true_count;
+ }
+ }
+ }
+ break;
+ case TYPE_INTEGER:
+ {
+ if (node->mPrecision == 32)
+ {
+ U32 integer_array[30];
+ if (node->getUnsignedValue(node->mLength, integer_array, node->mEncoding) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read integer array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ integer_checksum ^= integer_array[pos];
+ }
+ }
+ else
+ {
+ U64 integer_array[30];
+ if (node->getLongValue(node->mLength, integer_array, node->mEncoding) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read long integer array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ long_checksum ^= integer_array[pos];
+ }
+ }
+ }
+ break;
+ case TYPE_FLOAT:
+ {
+ if (node->mPrecision == 32)
+ {
+ F32 float_array[30];
+ if (node->getFloatValue(node->mLength, float_array, node->mEncoding) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read float array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ U32 float_bits = ((U32 *)float_array)[pos];
+ float_checksum ^= (float_bits & 0xfffff000);
+ }
+ }
+ else
+ {
+ F64 float_array[30];
+ if (node->getDoubleValue(node->mLength, float_array, node->mEncoding) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read float array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ U64 float_bits = ((U64 *)float_array)[pos];
+ float_checksum ^= ((float_bits & 0xfffffff000000000ll) >> 32);
+ }
+ }
+ }
+ break;
+ case TYPE_STRING:
+ break;
+ case TYPE_UUID:
+ {
+ LLUUID uuid_array[30];
+ if (node->getUUIDValue(node->mLength, uuid_array) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read uuid array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<(U32)node->mLength; ++pos)
+ {
+ for (S32 byte=0; byte<UUID_BYTES; ++byte)
+ {
+ uuid_checksum.mData[byte] ^= uuid_array[pos].mData[byte];
+ }
+ }
+ }
+ break;
+ case TYPE_NODEREF:
+ {
+ LLXMLNode *node_array[30];
+ if (node->getNodeRefValue(node->mLength, node_array) < node->mLength)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Could not read node ref array, child %s.\n", mName->mString, node->mName->mString));
+ return FALSE;
+ }
+ for (U32 pos=0; pos<node->mLength; ++pos)
+ {
+ const char *node_name = node_array[pos]->mName->mString;
+ for (U32 pos2=0; pos2<strlen(node_name); ++pos2)
+ {
+ U32 hash_contrib = U32(node_name[pos2]) << ((pos2 % 4) * 8);
+ noderef_checksum ^= hash_contrib;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ LLXMLNodePtr checksum_node;
+
+ // Compare checksums
+ {
+ U32 node_integer_checksum = 0;
+ if (!getAttribute("integer_checksum", checksum_node, FALSE) ||
+ checksum_node->getUnsignedValue(1, &node_integer_checksum, ENCODING_HEX) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Integer checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_integer_checksum != integer_checksum)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Integer checksum mismatch: read %X / calc %X.\n", mName->mString, node_integer_checksum, integer_checksum));
+ return FALSE;
+ }
+ }
+
+ {
+ U64 node_long_checksum = 0;
+ if (!getAttribute("long_checksum", checksum_node, FALSE) ||
+ checksum_node->getLongValue(1, &node_long_checksum, ENCODING_HEX) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Long Integer checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_long_checksum != long_checksum)
+ {
+ U32 *pp1 = (U32 *)&node_long_checksum;
+ U32 *pp2 = (U32 *)&long_checksum;
+ error_buffer.append(llformat("ERROR Node %s: Long Integer checksum mismatch: read %08X%08X / calc %08X%08X.\n", mName->mString, pp1[1], pp1[0], pp2[1], pp2[0]));
+ return FALSE;
+ }
+ }
+
+ {
+ U32 node_bool_true_count = 0;
+ if (!getAttribute("bool_true_count", checksum_node, FALSE) ||
+ checksum_node->getUnsignedValue(1, &node_bool_true_count, ENCODING_HEX) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Boolean checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_bool_true_count != bool_true_count)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Boolean checksum mismatch: read %X / calc %X.\n", mName->mString, node_bool_true_count, bool_true_count));
+ return FALSE;
+ }
+ }
+
+ {
+ LLUUID node_uuid_checksum;
+ if (!getAttribute("uuid_checksum", checksum_node, FALSE) ||
+ checksum_node->getUUIDValue(1, &node_uuid_checksum) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: UUID checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_uuid_checksum != uuid_checksum)
+ {
+ error_buffer.append(llformat("ERROR Node %s: UUID checksum mismatch: read %s / calc %s.\n", mName->mString, node_uuid_checksum.getString().c_str(), uuid_checksum.getString().c_str()));
+ return FALSE;
+ }
+ }
+
+ {
+ U32 node_noderef_checksum = 0;
+ if (!getAttribute("noderef_checksum", checksum_node, FALSE) ||
+ checksum_node->getUnsignedValue(1, &node_noderef_checksum, ENCODING_HEX) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Node Ref checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_noderef_checksum != noderef_checksum)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Node Ref checksum mismatch: read %X / calc %X.\n", mName->mString, node_noderef_checksum, noderef_checksum));
+ return FALSE;
+ }
+ }
+
+ {
+ U32 node_float_checksum = 0;
+ if (!getAttribute("float_checksum", checksum_node, FALSE) ||
+ checksum_node->getUnsignedValue(1, &node_float_checksum, ENCODING_HEX) != 1)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Float checksum missing.\n", mName->mString));
+ return FALSE;
+ }
+ if (node_float_checksum != float_checksum)
+ {
+ error_buffer.append(llformat("ERROR Node %s: Float checksum mismatch: read %X / calc %X.\n", mName->mString, node_float_checksum, float_checksum));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+LLXMLNodePtr LLXMLNode::getFirstChild()
+{
+ if (!mChildren) return NULL;
+ LLXMLNodePtr ret = mChildren->head;
+ return ret;
+}
+
+LLXMLNodePtr LLXMLNode::getNextSibling()
+{
+ LLXMLNodePtr ret = mNext;
+ return ret;
+}
+
+LLString LLXMLNode::getTextContents() const
+{
+ std::string msg;
+ LLXMLNodeList p_children;
+ getChildren("p", p_children);
+ if (p_children.size() > 0)
+ {
+ // Case 1: node has <p>text</p> tags
+ LLXMLNodeList::iterator itor;
+ for (itor = p_children.begin(); itor != p_children.end(); ++itor)
+ {
+ LLXMLNodePtr p = itor->second;
+ msg += p->getValue() + "\n";
+ }
+ }
+ else
+ {
+ LLString contents = mValue;
+ std::string::size_type n = contents.find_first_not_of(" \t\n");
+ if (n != std::string::npos && contents[n] == '\"')
+ {
+ // Case 2: node has quoted text
+ S32 num_lines = 0;
+ while(1)
+ {
+ // mContents[n] == '"'
+ ++n;
+ std::string::size_type t = n;
+ std::string::size_type m = 0;
+ // fix-up escaped characters
+ while(1)
+ {
+ m = contents.find_first_of("\\\"", t); // find first \ or "
+ if ((m == std::string::npos) || (contents[m] == '\"'))
+ {
+ break;
+ }
+ contents.erase(m,1);
+ t = m+1;
+ }
+ if (m == std::string::npos)
+ {
+ break;
+ }
+ // mContents[m] == '"'
+ num_lines++;
+ msg += contents.substr(n,m-n) + "\n";
+ n = contents.find_first_of("\"", m+1);
+ if (n == std::string::npos)
+ {
+ if (num_lines == 1)
+ {
+ msg.erase(msg.size()-1); // remove "\n" if only one line
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Case 3: node has embedded text (beginning and trailing whitespace trimmed)
+ LLString::size_type start = mValue.find_first_not_of(" \t\n");
+ if (start != mValue.npos)
+ {
+ LLString::size_type end = mValue.find_last_not_of(" \t\n");
+ if (end != mValue.npos)
+ {
+ msg = mValue.substr(start, end+1-start);
+ }
+ else
+ {
+ msg = mValue.substr(start);
+ }
+ }
+ }
+ }
+ return msg;
+}
diff --git a/indra/llxml/llxmlnode.h b/indra/llxml/llxmlnode.h
new file mode 100644
index 0000000000..ca8c1d176f
--- /dev/null
+++ b/indra/llxml/llxmlnode.h
@@ -0,0 +1,266 @@
+/**
+ * @file llxmlnode.h
+ * @brief LLXMLNode definition
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXMLNODE_H
+#define LL_LLXMLNODE_H
+
+#define XML_STATIC
+#include "expat/expat.h"
+#include <map>
+
+#include "indra_constants.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "llstringtable.h"
+
+
+
+struct CompareAttributes
+{
+ bool operator()(const LLStringTableEntry* const lhs, const LLStringTableEntry* const rhs) const
+ {
+ if (lhs == NULL)
+ return TRUE;
+ if (rhs == NULL)
+ return FALSE;
+
+ return strcmp(lhs->mString, rhs->mString) < 0;
+ }
+};
+
+
+// Defines a simple node hierarchy for reading and writing task objects
+
+class LLXMLNode;
+typedef LLPointer<LLXMLNode> LLXMLNodePtr;
+typedef std::multimap<LLString, LLXMLNodePtr > LLXMLNodeList;
+typedef std::multimap<const LLStringTableEntry *, LLXMLNodePtr > LLXMLChildList;
+typedef std::map<const LLStringTableEntry *, LLXMLNodePtr, CompareAttributes> LLXMLAttribList;
+
+struct LLXMLChildren
+{
+ LLXMLChildList map; // Map of children names->pointers
+ LLXMLNodePtr head; // Head of the double-linked list
+ LLXMLNodePtr tail; // Tail of the double-linked list
+};
+
+class LLXMLNode : public LLThreadSafeRefCount
+{
+public:
+ enum ValueType
+ {
+ TYPE_CONTAINER, // A node which contains nodes
+ TYPE_UNKNOWN, // A node loaded from file without a specified type
+ TYPE_BOOLEAN, // "true" or "false"
+ TYPE_INTEGER, // any integer type: U8, U32, S32, U64, etc.
+ TYPE_FLOAT, // any floating point type: F32, F64
+ TYPE_STRING, // a string
+ TYPE_UUID, // a UUID
+ TYPE_NODEREF, // the ID of another node in the hierarchy to reference
+ };
+
+ enum Encoding
+ {
+ ENCODING_DEFAULT = 0,
+ ENCODING_DECIMAL,
+ ENCODING_HEX,
+ // ENCODING_BASE32, // Not implemented yet
+ };
+
+protected:
+ virtual ~LLXMLNode();
+
+public:
+ LLXMLNode();
+ LLXMLNode(const LLString& name, BOOL is_attribute);
+ LLXMLNode(LLStringTableEntry* name, BOOL is_attribute);
+
+ BOOL isNull();
+
+ BOOL deleteChild(LLXMLNode* child);
+ void addChild(LLXMLNodePtr new_parent);
+ void setParent(LLXMLNodePtr new_parent); // reparent if necessary
+
+ // Serialization
+ static bool parseFile(
+ LLString filename,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults_tree);
+ static bool parseBuffer(
+ U8* buffer,
+ U32 length,
+ LLXMLNodePtr& node,
+ LLXMLNode* defaults);
+ static bool updateNode(
+ LLXMLNodePtr& node,
+ LLXMLNodePtr& update_node);
+ static void writeHeaderToFile(FILE *fOut);
+ void writeToFile(FILE *fOut, LLString indent = "");
+ void writeToOstream(std::ostream& output_stream, const LLString& indent = "");
+
+ // Utility
+ void findName(const LLString& name, LLXMLNodeList &results);
+ void findName(LLStringTableEntry* name, LLXMLNodeList &results);
+ void findID(const LLString& id, LLXMLNodeList &results);
+
+
+ virtual LLXMLNodePtr createChild(const LLString& name, BOOL is_attribute);
+ virtual LLXMLNodePtr createChild(LLStringTableEntry* name, BOOL is_attribute);
+
+
+ // Getters
+ U32 getBoolValue(U32 expected_length, BOOL *array);
+ U32 getByteValue(U32 expected_length, U8 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getIntValue(U32 expected_length, S32 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getUnsignedValue(U32 expected_length, U32 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getLongValue(U32 expected_length, U64 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getFloatValue(U32 expected_length, F32 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getDoubleValue(U32 expected_length, F64 *array, Encoding encoding = ENCODING_DEFAULT);
+ U32 getStringValue(U32 expected_length, LLString *array);
+ U32 getUUIDValue(U32 expected_length, LLUUID *array);
+ U32 getNodeRefValue(U32 expected_length, LLXMLNode **array);
+
+ BOOL hasAttribute(const LLString& name );
+
+ BOOL getAttributeBOOL(const LLString& name, BOOL& value );
+ BOOL getAttributeU8(const LLString& name, U8& value );
+ BOOL getAttributeS8(const LLString& name, S8& value );
+ BOOL getAttributeU16(const LLString& name, U16& value );
+ BOOL getAttributeS16(const LLString& name, S16& value );
+ BOOL getAttributeU32(const LLString& name, U32& value );
+ BOOL getAttributeS32(const LLString& name, S32& value );
+ BOOL getAttributeF32(const LLString& name, F32& value );
+ BOOL getAttributeF64(const LLString& name, F64& value );
+ BOOL getAttributeColor(const LLString& name, LLColor4& value );
+ BOOL getAttributeColor4(const LLString& name, LLColor4& value );
+ BOOL getAttributeColor4U(const LLString& name, LLColor4U& value );
+ BOOL getAttributeVector3(const LLString& name, LLVector3& value );
+ BOOL getAttributeVector3d(const LLString& name, LLVector3d& value );
+ BOOL getAttributeQuat(const LLString& name, LLQuaternion& value );
+ BOOL getAttributeUUID(const LLString& name, LLUUID& value );
+ BOOL getAttributeString(const LLString& name, LLString& value );
+
+ const ValueType& getType() const { return mType; }
+ const U32 getLength() const { return mLength; }
+ const U32 getPrecision() const { return mPrecision; }
+ const LLString& getValue() const { return mValue; }
+ LLString getTextContents() const;
+ const LLStringTableEntry* getName() const { return mName; }
+ const BOOL hasName(LLString name) const { return mName == gStringTable.checkStringEntry(name); }
+ const LLString& getID() const { return mID; }
+
+ U32 getChildCount() const;
+ // getChild returns a Null LLXMLNode (not a NULL pointer) if there is no such child.
+ // This child has no value so any getTYPEValue() calls on it will return 0.
+ bool getChild(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE);
+ bool getChild(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE);
+ void getChildren(const LLString& name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const;
+ void getChildren(const LLStringTableEntry* name, LLXMLNodeList &children, BOOL use_default_if_missing = TRUE) const;
+
+ bool getAttribute(const LLString& name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE);
+ bool getAttribute(const LLStringTableEntry* name, LLXMLNodePtr& node, BOOL use_default_if_missing = TRUE);
+
+ // The following skip over attributes
+ LLXMLNodePtr getFirstChild();
+ LLXMLNodePtr getNextSibling();
+
+ LLXMLNodePtr getRoot();
+
+ // Setters
+
+ bool setAttributeString(const LLString& attr, const LLString& value);
+
+ void setBoolValue(const BOOL value) { setBoolValue(1, &value); }
+ void setByteValue(const U8 value, Encoding encoding = ENCODING_DEFAULT) { setByteValue(1, &value, encoding); }
+ void setIntValue(const S32 value, Encoding encoding = ENCODING_DEFAULT) { setIntValue(1, &value, encoding); }
+ void setUnsignedValue(const U32 value, Encoding encoding = ENCODING_DEFAULT) { setUnsignedValue(1, &value, encoding); }
+ void setLongValue(const U64 value, Encoding encoding = ENCODING_DEFAULT) { setLongValue(1, &value, encoding); }
+ void setFloatValue(const F32 value, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0) { setFloatValue(1, &value, encoding); }
+ void setDoubleValue(const F64 value, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0) { setDoubleValue(1, &value, encoding); }
+ void setStringValue(const LLString value) { setStringValue(1, &value); }
+ void setUUIDValue(const LLUUID value) { setUUIDValue(1, &value); }
+ void setNodeRefValue(const LLXMLNode *value) { setNodeRefValue(1, &value); }
+
+ void setBoolValue(U32 length, const BOOL *array);
+ void setByteValue(U32 length, const U8 *array, Encoding encoding = ENCODING_DEFAULT);
+ void setIntValue(U32 length, const S32 *array, Encoding encoding = ENCODING_DEFAULT);
+ void setUnsignedValue(U32 length, const U32* array, Encoding encoding = ENCODING_DEFAULT);
+ void setLongValue(U32 length, const U64 *array, Encoding encoding = ENCODING_DEFAULT);
+ void setFloatValue(U32 length, const F32 *array, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0);
+ void setDoubleValue(U32 length, const F64 *array, Encoding encoding = ENCODING_DEFAULT, U32 precision = 0);
+ void setStringValue(U32 length, const LLString *array);
+ void setUUIDValue(U32 length, const LLUUID *array);
+ void setNodeRefValue(U32 length, const LLXMLNode **array);
+ void setValue(const LLString& value);
+ void setName(const LLString& name);
+ void setName(LLStringTableEntry* name);
+
+ // Escapes " (quot) ' (apos) & (amp) < (lt) > (gt)
+ // TomY TODO: Make this private
+ static LLString escapeXML(const LLString& xml);
+
+ // Set the default node corresponding to this default node
+ void setDefault(LLXMLNode *default_node);
+
+ // Find the node within defaults_list which corresponds to this node
+ void findDefault(LLXMLNode *defaults_list);
+
+ void updateDefault();
+
+ // Delete any child nodes that aren't among the tree's children, recursive
+ void scrubToTree(LLXMLNode *tree);
+
+ BOOL deleteChildren(const LLString& name);
+ BOOL deleteChildren(LLStringTableEntry* name);
+ void setAttributes(ValueType type, U32 precision, Encoding encoding, U32 length);
+ void appendValue(const LLString& value);
+
+ // Unit Testing
+ void createUnitTest(S32 max_num_children);
+ BOOL performUnitTest(LLString &error_buffer);
+
+protected:
+ BOOL removeChild(LLXMLNode* child);
+
+public:
+ LLString mID; // The ID attribute of this node
+
+ XML_Parser *mParser; // Temporary pointer while loading
+
+ BOOL mIsAttribute; // Flag is only used for output formatting
+ U32 mVersionMajor; // Version of this tag to use
+ U32 mVersionMinor;
+ U32 mLength; // If the length is nonzero, then only return arrays of this length
+ U32 mPrecision; // The number of BITS per array item
+ ValueType mType; // The value type
+ Encoding mEncoding; // The value encoding
+
+ LLXMLNode* mParent; // The parent node
+ LLXMLChildren* mChildren; // The child nodes
+ LLXMLAttribList mAttributes; // The attribute nodes
+ LLXMLNodePtr mPrev; // Double-linked list previous node
+ LLXMLNodePtr mNext; // Double-linked list next node
+
+ static BOOL sStripEscapedStrings;
+ static BOOL sStripWhitespaceValues;
+
+protected:
+ LLStringTableEntry *mName; // The name of this node
+ LLString mValue; // The value of this node (use getters/setters only)
+
+ LLXMLNodePtr mDefault; // Mirror node in the default tree
+
+ static const char *skipWhitespace(const char *str);
+ static const char *skipNonWhitespace(const char *str);
+ static const char *parseInteger(const char *str, U64 *dest, BOOL *is_negative, U32 precision, Encoding encoding);
+ static const char *parseFloat(const char *str, F64 *dest, U32 precision, Encoding encoding);
+
+ BOOL isFullyDefault();
+};
+
+#endif // LL_LLXMLNODE
diff --git a/indra/llxml/llxmlparser.cpp b/indra/llxml/llxmlparser.cpp
new file mode 100644
index 0000000000..baaeedf586
--- /dev/null
+++ b/indra/llxml/llxmlparser.cpp
@@ -0,0 +1,398 @@
+/**
+ * @file llxmlparser.cpp
+ * @brief LLXmlParser implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$`, Linden Research, Inc.
+ * $License$
+ */
+
+// llxmlparser.cpp
+//
+// copyright 2002, linden research inc
+
+
+#include "linden_common.h"
+
+#include "llxmlparser.h"
+#include "llerror.h"
+
+
+LLXmlParser::LLXmlParser()
+ :
+ mParser( NULL ),
+ mDepth( 0 )
+{
+ strcpy( mAuxErrorString, "no error" );
+
+ // Override the document's declared encoding.
+ mParser = XML_ParserCreate(NULL);
+
+ XML_SetUserData(mParser, this);
+ XML_SetElementHandler( mParser, startElementHandler, endElementHandler);
+ XML_SetCharacterDataHandler( mParser, characterDataHandler);
+ XML_SetProcessingInstructionHandler( mParser, processingInstructionHandler);
+ XML_SetCommentHandler( mParser, commentHandler);
+
+ XML_SetCdataSectionHandler( mParser, startCdataSectionHandler, endCdataSectionHandler);
+
+ // This sets the default handler but does not inhibit expansion of internal entities.
+ // The entity reference will not be passed to the default handler.
+ XML_SetDefaultHandlerExpand( mParser, defaultDataHandler);
+
+ XML_SetUnparsedEntityDeclHandler( mParser, unparsedEntityDeclHandler);
+}
+
+LLXmlParser::~LLXmlParser()
+{
+ XML_ParserFree( mParser );
+}
+
+
+BOOL LLXmlParser::parseFile(const std::string &path)
+{
+ llassert( !mDepth );
+
+ BOOL success = TRUE;
+
+ FILE *file = LLFile::fopen(path.c_str(), "rb");
+ if( !file )
+ {
+ sprintf( mAuxErrorString, "Couldn't open file %s", path.c_str());
+ success = FALSE;
+ }
+ else
+ {
+ S32 bytes_read = 0;
+
+ fseek(file, 0L, SEEK_END);
+ S32 buffer_size = ftell(file);
+ fseek(file, 0L, SEEK_SET);
+
+ void* buffer = XML_GetBuffer(mParser, buffer_size);
+ if( !buffer )
+ {
+ sprintf( mAuxErrorString, "Unable to allocate XML buffer while reading file %s", path.c_str() );
+ success = FALSE;
+ goto exit_label;
+ }
+
+ bytes_read = (S32)fread(buffer, 1, buffer_size, file);
+ if( bytes_read <= 0 )
+ {
+ sprintf( mAuxErrorString, "Error while reading file %s", path.c_str() );
+ success = FALSE;
+ goto exit_label;
+ }
+
+ if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) )
+ {
+ sprintf( mAuxErrorString, "Error while parsing file %s", path.c_str() );
+ success = FALSE;
+ }
+
+exit_label:
+ fclose( file );
+ }
+
+
+ if( success )
+ {
+ llassert( !mDepth );
+ }
+ mDepth = 0;
+
+ if( !success )
+ {
+ llwarns << mAuxErrorString << llendl;
+ }
+
+ return success;
+}
+
+
+// Parses some input. Returns 0 if a fatal error is detected.
+// The last call must have isFinal true;
+// len may be zero for this call (or any other).
+S32 LLXmlParser::parse( const char* buf, int len, int isFinal )
+{
+ return XML_Parse(mParser, buf, len, isFinal);
+}
+
+const char* LLXmlParser::getErrorString()
+{
+ const char* error_string = XML_ErrorString(XML_GetErrorCode( mParser ));
+ if( !error_string )
+ {
+ error_string = mAuxErrorString;
+ }
+ return error_string;
+}
+
+S32 LLXmlParser::getCurrentLineNumber()
+{
+ return XML_GetCurrentLineNumber( mParser );
+}
+
+S32 LLXmlParser::getCurrentColumnNumber()
+{
+ return XML_GetCurrentColumnNumber(mParser);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Pseudo-private methods. These are only used by internal callbacks.
+
+// static
+void LLXmlParser::startElementHandler(
+ void *userData,
+ const XML_Char *name,
+ const XML_Char **atts)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->startElement( name, atts );
+ self->mDepth++;
+}
+
+// static
+void LLXmlParser::endElementHandler(
+ void *userData,
+ const XML_Char *name)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->mDepth--;
+ self->endElement( name );
+}
+
+// s is not 0 terminated.
+// static
+void LLXmlParser::characterDataHandler(
+ void *userData,
+ const XML_Char *s,
+ int len)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->characterData( s, len );
+}
+
+// target and data are 0 terminated
+// static
+void LLXmlParser::processingInstructionHandler(
+ void *userData,
+ const XML_Char *target,
+ const XML_Char *data)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->processingInstruction( target, data );
+}
+
+// data is 0 terminated
+// static
+void LLXmlParser::commentHandler(void *userData, const XML_Char *data)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->comment( data );
+}
+
+// static
+void LLXmlParser::startCdataSectionHandler(void *userData)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->mDepth++;
+ self->startCdataSection();
+}
+
+// static
+void LLXmlParser::endCdataSectionHandler(void *userData)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->endCdataSection();
+ self->mDepth++;
+}
+
+// This is called for any characters in the XML document for
+// which there is no applicable handler. This includes both
+// characters that are part of markup which is of a kind that is
+// not reported (comments, markup declarations), or characters
+// that are part of a construct which could be reported but
+// for which no handler has been supplied. The characters are passed
+// exactly as they were in the XML document except that
+// they will be encoded in UTF-8. Line boundaries are not normalized.
+// Note that a byte order mark character is not passed to the default handler.
+// There are no guarantees about how characters are divided between calls
+// to the default handler: for example, a comment might be split between
+// multiple calls.
+
+// static
+void LLXmlParser::defaultDataHandler(
+ void *userData,
+ const XML_Char *s,
+ int len)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->defaultData( s, len );
+}
+
+// This is called for a declaration of an unparsed (NDATA)
+// entity. The base argument is whatever was set by XML_SetBase.
+// The entityName, systemId and notationName arguments will never be null.
+// The other arguments may be.
+// static
+void LLXmlParser::unparsedEntityDeclHandler(
+ void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName)
+{
+ LLXmlParser* self = (LLXmlParser*) userData;
+ self->unparsedEntityDecl( entityName, base, systemId, publicId, notationName );
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////
+// Test code.
+
+/*
+class LLXmlDOMParser : public LLXmlParser
+{
+public:
+
+ LLXmlDOMParser() {}
+ virtual ~LLXmlDOMParser() {}
+
+ void tabs()
+ {
+ for ( int i = 0; i < getDepth(); i++)
+ {
+ putchar(' ');
+ }
+ }
+
+ virtual void startElement(const char *name, const char **atts)
+ {
+ tabs();
+ printf("startElement %s\n", name);
+
+ S32 i = 0;
+ while( atts[i] && atts[i+1] )
+ {
+ tabs();
+ printf( "\t%s=%s\n", atts[i], atts[i+1] );
+ i += 2;
+ }
+
+ if( atts[i] )
+ {
+ tabs();
+ printf( "\ttrailing attribute: %s\n", atts[i] );
+ }
+ }
+
+ virtual void endElement(const char *name)
+ {
+ tabs();
+ printf("endElement %s\n", name);
+ }
+
+ virtual void characterData(const char *s, int len)
+ {
+ tabs();
+
+ char* str = new char[len+1];
+ strncpy( str, s, len );
+ str[len] = '\0';
+ printf("CharacterData %s\n", str);
+ delete str;
+ }
+
+ virtual void processingInstruction(const char *target, const char *data)
+ {
+ tabs();
+ printf("processingInstruction %s\n", data);
+ }
+ virtual void comment(const char *data)
+ {
+ tabs();
+ printf("comment %s\n", data);
+ }
+
+ virtual void startCdataSection()
+ {
+ tabs();
+ printf("startCdataSection\n");
+ }
+
+ virtual void endCdataSection()
+ {
+ tabs();
+ printf("endCdataSection\n");
+ }
+
+ virtual void defaultData(const char *s, int len)
+ {
+ tabs();
+
+ char* str = new char[len+1];
+ strncpy( str, s, len );
+ str[len] = '\0';
+ printf("defaultData %s\n", str);
+ delete str;
+ }
+
+ virtual void unparsedEntityDecl(
+ const char *entityName,
+ const char *base,
+ const char *systemId,
+ const char *publicId,
+ const char *notationName)
+ {
+ tabs();
+
+ printf(
+ "unparsed entity:\n"
+ "\tentityName %s\n"
+ "\tbase %s\n"
+ "\tsystemId %s\n"
+ "\tpublicId %s\n"
+ "\tnotationName %s\n",
+ entityName,
+ base,
+ systemId,
+ publicId,
+ notationName );
+ }
+};
+
+
+int main()
+{
+ char buf[1024];
+
+ FILE* file = LLFile::fopen("test.xml", "rb");
+ if( !file )
+ {
+ return 1;
+ }
+
+ LLXmlDOMParser parser;
+ int done;
+ do {
+ size_t len = fread(buf, 1, sizeof(buf), file);
+ done = len < sizeof(buf);
+ if( 0 == parser.parse( buf, len, done) )
+ {
+ fprintf(stderr,
+ "%s at line %d\n",
+ parser.getErrorString(),
+ parser.getCurrentLineNumber() );
+ return 1;
+ }
+ } while (!done);
+
+ fclose( file );
+ return 0;
+}
+*/
+
diff --git a/indra/llxml/llxmlparser.h b/indra/llxml/llxmlparser.h
new file mode 100644
index 0000000000..2cb75591fb
--- /dev/null
+++ b/indra/llxml/llxmlparser.h
@@ -0,0 +1,109 @@
+/**
+ * @file llxmlparser.h
+ * @brief LLXmlParser class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXMLPARSER_H
+#define LL_LLXMLPARSER_H
+
+#define XML_STATIC
+#include "expat/expat.h"
+
+class LLXmlParser
+{
+public:
+ LLXmlParser();
+ virtual ~LLXmlParser();
+
+ // Parses entire file
+ BOOL parseFile(const std::string &path);
+
+ // Parses some input. Returns 0 if a fatal error is detected.
+ // The last call must have isFinal true;
+ // len may be zero for this call (or any other).
+ S32 parse( const char* buf, int len, int isFinal );
+
+ const char* getErrorString();
+
+ S32 getCurrentLineNumber();
+
+ S32 getCurrentColumnNumber();
+
+ S32 getDepth() { return mDepth; }
+
+protected:
+ // atts is array of name/value pairs, terminated by 0;
+ // names and values are 0 terminated.
+ virtual void startElement(const char *name, const char **atts) {}
+
+ virtual void endElement(const char *name) {}
+
+ // s is not 0 terminated.
+ virtual void characterData(const char *s, int len) {}
+
+ // target and data are 0 terminated
+ virtual void processingInstruction(const char *target, const char *data) {}
+
+ // data is 0 terminated
+ virtual void comment(const char *data) {}
+
+ virtual void startCdataSection() {}
+
+ virtual void endCdataSection() {}
+
+ // This is called for any characters in the XML document for
+ // which there is no applicable handler. This includes both
+ // characters that are part of markup which is of a kind that is
+ // not reported (comments, markup declarations), or characters
+ // that are part of a construct which could be reported but
+ // for which no handler has been supplied. The characters are passed
+ // exactly as they were in the XML document except that
+ // they will be encoded in UTF-8. Line boundaries are not normalized.
+ // Note that a byte order mark character is not passed to the default handler.
+ // There are no guarantees about how characters are divided between calls
+ // to the default handler: for example, a comment might be split between
+ // multiple calls.
+ virtual void defaultData(const char *s, int len) {}
+
+ // This is called for a declaration of an unparsed (NDATA)
+ // entity. The base argument is whatever was set by XML_SetBase.
+ // The entityName, systemId and notationName arguments will never be null.
+ // The other arguments may be.
+ virtual void unparsedEntityDecl(
+ const char *entityName,
+ const char *base,
+ const char *systemId,
+ const char *publicId,
+ const char *notationName) {}
+
+public:
+ ///////////////////////////////////////////////////////////////////////////////
+ // Pseudo-private methods. These are only used by internal callbacks.
+
+ static void startElementHandler(void *userData, const XML_Char *name, const XML_Char **atts);
+ static void endElementHandler(void *userData, const XML_Char *name);
+ static void characterDataHandler(void *userData, const XML_Char *s, int len);
+ static void processingInstructionHandler(void *userData, const XML_Char *target, const XML_Char *data);
+ static void commentHandler(void *userData, const XML_Char *data);
+ static void startCdataSectionHandler(void *userData);
+ static void endCdataSectionHandler(void *userData);
+ static void defaultDataHandler( void *userData, const XML_Char *s, int len);
+ static void unparsedEntityDeclHandler(
+ void *userData,
+ const XML_Char *entityName,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId,
+ const XML_Char *notationName);
+
+
+protected:
+ XML_Parser mParser;
+ int mDepth;
+ char mAuxErrorString[1024];
+};
+
+#endif // LL_LLXMLPARSER_H
diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp
new file mode 100644
index 0000000000..3d0d1ba379
--- /dev/null
+++ b/indra/llxml/llxmltree.cpp
@@ -0,0 +1,671 @@
+/**
+ * @file llxmltree.cpp
+ * @brief LLXmlTree implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "llxmltree.h"
+#include "v3color.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "lluuid.h"
+
+//////////////////////////////////////////////////////////////
+// LLXmlTree
+
+// static
+LLStdStringTable LLXmlTree::sAttributeKeys(1024);
+
+LLXmlTree::LLXmlTree()
+ : mRoot( NULL ),
+ mNodeNames(512)
+{
+}
+
+LLXmlTree::~LLXmlTree()
+{
+ cleanup();
+}
+
+void LLXmlTree::cleanup()
+{
+ delete mRoot;
+ mRoot = NULL;
+ mNodeNames.cleanup();
+}
+
+
+BOOL LLXmlTree::parseFile(const std::string &path, BOOL keep_contents)
+{
+ delete mRoot;
+ mRoot = NULL;
+
+ LLXmlTreeParser parser(this);
+ BOOL success = parser.parseFile( path, &mRoot, keep_contents );
+ if( !success )
+ {
+ S32 line_number = parser.getCurrentLineNumber();
+ const char* error = parser.getErrorString();
+ llwarns << "LLXmlTree parse failed. Line " << line_number << ": " << error << llendl;
+ }
+ return success;
+}
+
+void LLXmlTree::dump()
+{
+ if( mRoot )
+ {
+ dumpNode( mRoot, " " );
+ }
+}
+
+void LLXmlTree::dumpNode( LLXmlTreeNode* node, const LLString& prefix )
+{
+ node->dump( prefix );
+
+ LLString new_prefix = prefix + " ";
+ for( LLXmlTreeNode* child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ dumpNode( child, new_prefix );
+ }
+}
+
+//////////////////////////////////////////////////////////////
+// LLXmlTreeNode
+
+LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LLXmlTree* tree )
+ : mName(name),
+ mParent(parent),
+ mTree(tree)
+{
+}
+
+LLXmlTreeNode::~LLXmlTreeNode()
+{
+ attribute_map_t::iterator iter;
+ for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
+ delete iter->second;
+ child_list_t::iterator child_iter;
+ for (child_iter=mChildList.begin(); child_iter != mChildList.end(); child_iter++)
+ delete *child_iter;
+}
+
+void LLXmlTreeNode::dump( const LLString& prefix )
+{
+ llinfos << prefix << mName ;
+ if( !mContents.empty() )
+ {
+ llcont << " contents = \"" << mContents << "\"";
+ }
+ attribute_map_t::iterator iter;
+ for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
+ {
+ LLStdStringHandle key = iter->first;
+ const LLString* value = iter->second;
+ llcont << prefix << " " << key << "=" << (value->empty() ? "NULL" : *value);
+ }
+ llcont << llendl;
+}
+
+BOOL LLXmlTreeNode::hasAttribute(const std::string& name)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ attribute_map_t::iterator iter = mAttributes.find(canonical_name);
+ return (iter == mAttributes.end()) ? false : true;
+}
+
+void LLXmlTreeNode::addAttribute(const std::string& name, const std::string& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ const LLString *newstr = new LLString(value);
+ mAttributes[canonical_name] = newstr; // insert + copy
+}
+
+LLXmlTreeNode* LLXmlTreeNode::getFirstChild()
+{
+ mChildListIter = mChildList.begin();
+ return getNextChild();
+}
+LLXmlTreeNode* LLXmlTreeNode::getNextChild()
+{
+ if (mChildListIter == mChildList.end())
+ return 0;
+ else
+ return *mChildListIter++;
+}
+
+LLXmlTreeNode* LLXmlTreeNode::getChildByName(const std::string& name)
+{
+ LLStdStringHandle tableptr = mTree->mNodeNames.checkString(name);
+ mChildMapIter = mChildMap.lower_bound(tableptr);
+ mChildMapEndIter = mChildMap.upper_bound(tableptr);
+ return getNextNamedChild();
+}
+
+LLXmlTreeNode* LLXmlTreeNode::getNextNamedChild()
+{
+ if (mChildMapIter == mChildMapEndIter)
+ return NULL;
+ else
+ return (mChildMapIter++)->second;
+}
+
+void LLXmlTreeNode::appendContents(const std::string& str)
+{
+ mContents.append( str );
+}
+
+void LLXmlTreeNode::addChild(LLXmlTreeNode* child)
+{
+ llassert( child );
+ mChildList.push_back( child );
+
+ // Add a name mapping to this node
+ LLStdStringHandle tableptr = mTree->mNodeNames.insert(child->mName);
+ mChildMap.insert( child_map_t::value_type(tableptr, child));
+
+ child->mParent = this;
+}
+
+//////////////////////////////////////////////////////////////
+
+// These functions assume that name is already in mAttritrubteKeys
+
+BOOL LLXmlTreeNode::getFastAttributeBOOL(LLStdStringHandle canonical_name, BOOL& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToBOOL( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeU8(LLStdStringHandle canonical_name, U8& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToU8( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeS8(LLStdStringHandle canonical_name, S8& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToS8( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeS16(LLStdStringHandle canonical_name, S16& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToS16( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeU16(LLStdStringHandle canonical_name, U16& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToU16( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeU32(LLStdStringHandle canonical_name, U32& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToU32( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeS32(LLStdStringHandle canonical_name, S32& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToS32( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeF32(LLStdStringHandle canonical_name, F32& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToF32( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeF64(LLStdStringHandle canonical_name, F64& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s && LLString::convertToF64( *s, value );
+}
+
+BOOL LLXmlTreeNode::getFastAttributeColor(LLStdStringHandle canonical_name, LLColor4& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLColor4::parseColor(s->c_str(), &value) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeColor4(LLStdStringHandle canonical_name, LLColor4& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLColor4::parseColor4(s->c_str(), &value) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeColor4U(LLStdStringHandle canonical_name, LLColor4U& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLColor4U::parseColor4U(s->c_str(), &value ) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeVector3(LLStdStringHandle canonical_name, LLVector3& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLVector3::parseVector3(s->c_str(), &value ) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeVector3d(LLStdStringHandle canonical_name, LLVector3d& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLVector3d::parseVector3d(s->c_str(), &value ) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeQuat(LLStdStringHandle canonical_name, LLQuaternion& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLQuaternion::parseQuat(s->c_str(), &value ) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeUUID(LLStdStringHandle canonical_name, LLUUID& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ return s ? LLUUID::parseUUID(s->c_str(), &value ) : FALSE;
+}
+
+BOOL LLXmlTreeNode::getFastAttributeString(LLStdStringHandle canonical_name, LLString& value)
+{
+ const LLString *s = getAttribute( canonical_name );
+ if( !s )
+ {
+ return FALSE;
+ }
+
+ value = *s;
+ return TRUE;
+}
+
+
+//////////////////////////////////////////////////////////////
+
+BOOL LLXmlTreeNode::getAttributeBOOL(const std::string& name, BOOL& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeBOOL(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeU8(const std::string& name, U8& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeU8(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeS8(const std::string& name, S8& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeS8(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeS16(const std::string& name, S16& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeS16(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeU16(const std::string& name, U16& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeU16(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeU32(const std::string& name, U32& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeU32(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeS32(const std::string& name, S32& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeS32(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeF32(const std::string& name, F32& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeF32(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeF64(const std::string& name, F64& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeF64(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeColor(const std::string& name, LLColor4& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeColor(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeColor4(const std::string& name, LLColor4& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeColor4(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeColor4U(const std::string& name, LLColor4U& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeColor4U(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeVector3(const std::string& name, LLVector3& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeVector3(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeVector3d(const std::string& name, LLVector3d& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeVector3d(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeQuat(const std::string& name, LLQuaternion& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeQuat(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeUUID(const std::string& name, LLUUID& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeUUID(canonical_name, value);
+}
+
+BOOL LLXmlTreeNode::getAttributeString(const std::string& name, LLString& value)
+{
+ LLStdStringHandle canonical_name = LLXmlTree::sAttributeKeys.addString( name );
+ return getFastAttributeString(canonical_name, value);
+}
+
+/*
+ The following xml <message> nodes will all return the string from getTextContents():
+ "The quick brown fox\n Jumps over the lazy dog"
+
+ 1. HTML paragraph format:
+ <message>
+ <p>The quick brown fox</p>
+ <p> Jumps over the lazy dog</p>
+ </message>
+ 2. Each quoted section -> paragraph:
+ <message>
+ "The quick brown fox"
+ " Jumps over the lazy dog"
+ </message>
+ 3. Literal text with beginning and trailing whitespace removed:
+ <message>
+The quick brown fox
+ Jumps over the lazy dog
+ </message>
+
+*/
+
+LLString LLXmlTreeNode::getTextContents()
+{
+ std::string msg;
+ LLXmlTreeNode* p = getChildByName("p");
+ if (p)
+ {
+ // Case 1: node has <p>text</p> tags
+ while (p)
+ {
+ msg += p->getContents() + "\n";
+ p = getNextNamedChild();
+ }
+ }
+ else
+ {
+ std::string::size_type n = mContents.find_first_not_of(" \t\n");
+ if (n != std::string::npos && mContents[n] == '\"')
+ {
+ // Case 2: node has quoted text
+ S32 num_lines = 0;
+ while(1)
+ {
+ // mContents[n] == '"'
+ ++n;
+ std::string::size_type t = n;
+ std::string::size_type m = 0;
+ // fix-up escaped characters
+ while(1)
+ {
+ m = mContents.find_first_of("\\\"", t); // find first \ or "
+ if ((m == std::string::npos) || (mContents[m] == '\"'))
+ {
+ break;
+ }
+ mContents.erase(m,1);
+ t = m+1;
+ }
+ if (m == std::string::npos)
+ {
+ break;
+ }
+ // mContents[m] == '"'
+ num_lines++;
+ msg += mContents.substr(n,m-n) + "\n";
+ n = mContents.find_first_of("\"", m+1);
+ if (n == std::string::npos)
+ {
+ if (num_lines == 1)
+ {
+ msg.erase(msg.size()-1); // remove "\n" if only one line
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Case 3: node has embedded text (beginning and trailing whitespace trimmed)
+ msg = mContents;
+ }
+ }
+ return msg;
+}
+
+
+//////////////////////////////////////////////////////////////
+// LLXmlTreeParser
+
+LLXmlTreeParser::LLXmlTreeParser(LLXmlTree* tree)
+ : mTree(tree),
+ mRoot( NULL ),
+ mCurrent( NULL ),
+ mDump( FALSE )
+{
+}
+
+LLXmlTreeParser::~LLXmlTreeParser()
+{
+}
+
+BOOL LLXmlTreeParser::parseFile(const std::string &path, LLXmlTreeNode** root, BOOL keep_contents)
+{
+ llassert( !mRoot );
+ llassert( !mCurrent );
+
+ mKeepContents = keep_contents;
+
+ BOOL success = LLXmlParser::parseFile(path);
+
+ *root = mRoot;
+ mRoot = NULL;
+
+ if( success )
+ {
+ llassert( !mCurrent );
+ }
+ mCurrent = NULL;
+
+ return success;
+}
+
+
+const std::string& LLXmlTreeParser::tabs()
+{
+ static LLString s;
+ s = "";
+ S32 num_tabs = getDepth() - 1;
+ for( S32 i = 0; i < num_tabs; i++)
+ {
+ s += " ";
+ }
+ return s;
+}
+
+void LLXmlTreeParser::startElement(const char* name, const char **atts)
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "startElement " << name << llendl;
+
+ S32 i = 0;
+ while( atts[i] && atts[i+1] )
+ {
+ llinfos << tabs() << "attribute: " << atts[i] << "=" << atts[i+1] << llendl;
+ i += 2;
+ }
+ }
+
+ LLXmlTreeNode* child = CreateXmlTreeNode( std::string(name), mCurrent );
+
+ S32 i = 0;
+ while( atts[i] && atts[i+1] )
+ {
+ child->addAttribute( atts[i], atts[i+1] );
+ i += 2;
+ }
+
+ if( mCurrent )
+ {
+ mCurrent->addChild( child );
+
+ }
+ else
+ {
+ llassert( !mRoot );
+ mRoot = child;
+ }
+ mCurrent = child;
+}
+
+LLXmlTreeNode* LLXmlTreeParser::CreateXmlTreeNode(const std::string& name, LLXmlTreeNode* parent)
+{
+ return new LLXmlTreeNode(name, parent, mTree);
+}
+
+
+void LLXmlTreeParser::endElement(const char* name)
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "endElement " << name << llendl;
+ }
+
+ if( !mCurrent->mContents.empty() )
+ {
+ LLString::trim(mCurrent->mContents);
+ LLString::removeCRLF(mCurrent->mContents);
+ }
+
+ mCurrent = mCurrent->getParent();
+}
+
+void LLXmlTreeParser::characterData(const char *s, int len)
+{
+ LLString str(s, len);
+ if( mDump )
+ {
+ llinfos << tabs() << "CharacterData " << str << llendl;
+ }
+
+ if (mKeepContents)
+ {
+ mCurrent->appendContents( str );
+ }
+}
+
+void LLXmlTreeParser::processingInstruction(const char *target, const char *data)
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "processingInstruction " << data << llendl;
+ }
+}
+
+void LLXmlTreeParser::comment(const char *data)
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "comment " << data << llendl;
+ }
+}
+
+void LLXmlTreeParser::startCdataSection()
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "startCdataSection" << llendl;
+ }
+}
+
+void LLXmlTreeParser::endCdataSection()
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "endCdataSection" << llendl;
+ }
+}
+
+void LLXmlTreeParser::defaultData(const char *s, int len)
+{
+ if( mDump )
+ {
+ LLString str(s, len);
+ llinfos << tabs() << "defaultData " << str << llendl;
+ }
+}
+
+void LLXmlTreeParser::unparsedEntityDecl(
+ const char* entity_name,
+ const char* base,
+ const char* system_id,
+ const char* public_id,
+ const char* notation_name)
+{
+ if( mDump )
+ {
+ llinfos << tabs() << "unparsed entity:" << llendl;
+ llinfos << tabs() << " entityName " << entity_name << llendl;
+ llinfos << tabs() << " base " << base << llendl;
+ llinfos << tabs() << " systemId " << system_id << llendl;
+ llinfos << tabs() << " publicId " << public_id << llendl;
+ llinfos << tabs() << " notationName " << notation_name<< llendl;
+ }
+}
+
+void test_llxmltree()
+{
+ LLXmlTree tree;
+ BOOL success = tree.parseFile( "test.xml" );
+ if( success )
+ {
+ tree.dump();
+ }
+}
+
diff --git a/indra/llxml/llxmltree.h b/indra/llxml/llxmltree.h
new file mode 100644
index 0000000000..8cee425dd9
--- /dev/null
+++ b/indra/llxml/llxmltree.h
@@ -0,0 +1,216 @@
+/**
+ * @file llxmltree.h
+ * @author Aaron Yonas, Richard Nelson
+ * @brief LLXmlTree class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLXMLTREE_H
+#define LL_LLXMLTREE_H
+
+#include <map>
+#include <list>
+#include "llstring.h"
+#include "llxmlparser.h"
+#include "string_table.h"
+
+class LLColor4;
+class LLColor4U;
+class LLQuaternion;
+class LLUUID;
+class LLVector3;
+class LLVector3d;
+class LLXmlTreeNode;
+class LLXmlTreeParser;
+
+//////////////////////////////////////////////////////////////
+// LLXmlTree
+
+class LLXmlTree
+{
+ friend class LLXmlTreeNode;
+
+public:
+ LLXmlTree();
+ virtual ~LLXmlTree();
+ void cleanup();
+
+ virtual BOOL parseFile(const std::string &path, BOOL keep_contents = TRUE);
+
+ LLXmlTreeNode* getRoot() { return mRoot; }
+
+ void dump();
+ void dumpNode( LLXmlTreeNode* node, const LLString &prefix );
+
+ static LLStdStringHandle addAttributeString( const std::string& name)
+ {
+ return sAttributeKeys.addString( name );
+ }
+
+public:
+ // global
+ static LLStdStringTable sAttributeKeys;
+
+protected:
+ LLXmlTreeNode* mRoot;
+
+ // local
+ LLStdStringTable mNodeNames;
+};
+
+//////////////////////////////////////////////////////////////
+// LLXmlTreeNode
+
+class LLXmlTreeNode
+{
+ friend class LLXmlTree;
+ friend class LLXmlTreeParser;
+
+protected:
+ // Protected since nodes are only created and destroyed by friend classes and other LLXmlTreeNodes
+ LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LLXmlTree* tree );
+
+public:
+ virtual ~LLXmlTreeNode();
+
+ const std::string& getName()
+ {
+ return mName;
+ }
+ BOOL hasName( const std::string& name )
+ {
+ return mName == name;
+ }
+
+ BOOL hasAttribute( const std::string& name );
+
+ // Fast versions use cannonical_name handlee to entru in LLXmlTree::sAttributeKeys string table
+ BOOL getFastAttributeBOOL( LLStdStringHandle cannonical_name, BOOL& value );
+ BOOL getFastAttributeU8( LLStdStringHandle cannonical_name, U8& value );
+ BOOL getFastAttributeS8( LLStdStringHandle cannonical_name, S8& value );
+ BOOL getFastAttributeU16( LLStdStringHandle cannonical_name, U16& value );
+ BOOL getFastAttributeS16( LLStdStringHandle cannonical_name, S16& value );
+ BOOL getFastAttributeU32( LLStdStringHandle cannonical_name, U32& value );
+ BOOL getFastAttributeS32( LLStdStringHandle cannonical_name, S32& value );
+ BOOL getFastAttributeF32( LLStdStringHandle cannonical_name, F32& value );
+ BOOL getFastAttributeF64( LLStdStringHandle cannonical_name, F64& value );
+ BOOL getFastAttributeColor( LLStdStringHandle cannonical_name, LLColor4& value );
+ BOOL getFastAttributeColor4( LLStdStringHandle cannonical_name, LLColor4& value );
+ BOOL getFastAttributeColor4U( LLStdStringHandle cannonical_name, LLColor4U& value );
+ BOOL getFastAttributeVector3( LLStdStringHandle cannonical_name, LLVector3& value );
+ BOOL getFastAttributeVector3d( LLStdStringHandle cannonical_name, LLVector3d& value );
+ BOOL getFastAttributeQuat( LLStdStringHandle cannonical_name, LLQuaternion& value );
+ BOOL getFastAttributeUUID( LLStdStringHandle cannonical_name, LLUUID& value );
+ BOOL getFastAttributeString( LLStdStringHandle cannonical_name, LLString& value );
+
+ // Normal versions find 'name' in LLXmlTree::sAttributeKeys then call fast versions
+ virtual BOOL getAttributeBOOL( const std::string& name, BOOL& value );
+ virtual BOOL getAttributeU8( const std::string& name, U8& value );
+ virtual BOOL getAttributeS8( const std::string& name, S8& value );
+ virtual BOOL getAttributeU16( const std::string& name, U16& value );
+ virtual BOOL getAttributeS16( const std::string& name, S16& value );
+ virtual BOOL getAttributeU32( const std::string& name, U32& value );
+ virtual BOOL getAttributeS32( const std::string& name, S32& value );
+ virtual BOOL getAttributeF32( const std::string& name, F32& value );
+ virtual BOOL getAttributeF64( const std::string& name, F64& value );
+ virtual BOOL getAttributeColor( const std::string& name, LLColor4& value );
+ virtual BOOL getAttributeColor4( const std::string& name, LLColor4& value );
+ virtual BOOL getAttributeColor4U( const std::string& name, LLColor4U& value );
+ virtual BOOL getAttributeVector3( const std::string& name, LLVector3& value );
+ virtual BOOL getAttributeVector3d( const std::string& name, LLVector3d& value );
+ virtual BOOL getAttributeQuat( const std::string& name, LLQuaternion& value );
+ virtual BOOL getAttributeUUID( const std::string& name, LLUUID& value );
+ virtual BOOL getAttributeString( const std::string& name, LLString& value );
+
+ const LLString& getContents()
+ {
+ return mContents;
+ }
+ LLString getTextContents();
+
+ LLXmlTreeNode* getParent() { return mParent; }
+ LLXmlTreeNode* getFirstChild();
+ LLXmlTreeNode* getNextChild();
+ S32 getChildCount() { return (S32)mChildList.size(); }
+ LLXmlTreeNode* getChildByName( const std::string& name ); // returns first child with name, NULL if none
+ LLXmlTreeNode* getNextNamedChild(); // returns next child with name, NULL if none
+
+protected:
+ const LLString* getAttribute( LLStdStringHandle name)
+ {
+ attribute_map_t::iterator iter = mAttributes.find(name);
+ return (iter == mAttributes.end()) ? 0 : iter->second;
+ }
+
+private:
+ void addAttribute( const std::string& name, const std::string& value );
+ void appendContents( const std::string& str );
+ void addChild( LLXmlTreeNode* child );
+
+ void dump( const LLString& prefix );
+
+protected:
+ typedef std::map<LLStdStringHandle, const LLString*> attribute_map_t;
+ attribute_map_t mAttributes;
+
+private:
+ LLString mName;
+ LLString mContents;
+
+ typedef std::list<class LLXmlTreeNode *> child_list_t;
+ child_list_t mChildList;
+ child_list_t::iterator mChildListIter;
+
+ typedef std::multimap<LLStdStringHandle, LLXmlTreeNode *> child_map_t;
+ child_map_t mChildMap; // for fast name lookups
+ child_map_t::iterator mChildMapIter;
+ child_map_t::iterator mChildMapEndIter;
+
+ LLXmlTreeNode* mParent;
+ LLXmlTree* mTree;
+};
+
+//////////////////////////////////////////////////////////////
+// LLXmlTreeParser
+
+class LLXmlTreeParser : public LLXmlParser
+{
+public:
+ LLXmlTreeParser(LLXmlTree* tree);
+ virtual ~LLXmlTreeParser();
+
+ BOOL parseFile(const std::string &path, LLXmlTreeNode** root, BOOL keep_contents );
+
+protected:
+ const std::string& tabs();
+
+ // Overrides from LLXmlParser
+ virtual void startElement(const char *name, const char **attributes);
+ virtual void endElement(const char *name);
+ virtual void characterData(const char *s, int len);
+ virtual void processingInstruction(const char *target, const char *data);
+ virtual void comment(const char *data);
+ virtual void startCdataSection();
+ virtual void endCdataSection();
+ virtual void defaultData(const char *s, int len);
+ virtual void unparsedEntityDecl(
+ const char* entity_name,
+ const char* base,
+ const char* system_id,
+ const char* public_id,
+ const char* notation_name);
+
+ //template method pattern
+ virtual LLXmlTreeNode* CreateXmlTreeNode(const std::string& name, LLXmlTreeNode* parent);
+
+protected:
+ LLXmlTree* mTree;
+ LLXmlTreeNode* mRoot;
+ LLXmlTreeNode* mCurrent;
+ BOOL mDump; // Dump parse tree to llinfos as it is read.
+ BOOL mKeepContents;
+};
+
+#endif // LL_LLXMLTREE_H
diff --git a/indra/lscript/lscript_alloc.h b/indra/lscript/lscript_alloc.h
new file mode 100644
index 0000000000..f0761c0afd
--- /dev/null
+++ b/indra/lscript/lscript_alloc.h
@@ -0,0 +1,344 @@
+/**
+ * @file lscript_alloc.h
+ * @brief General heap management for scripting system
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_ALLOC_H
+#define LL_LSCRIPT_ALLOC_H
+// #define at top of file accelerates gcc compiles
+// Under gcc 2.9, the manual is unclear if comments can appear above #ifndef
+// Under gcc 3, the manual explicitly states comments can appear above the #ifndef
+
+#include "stdtypes.h"
+#include "lscript_byteconvert.h"
+#include "lscript_library.h"
+#include "llrand.h"
+#include <stdio.h>
+
+void reset_hp_to_safe_spot(const U8 *buffer);
+
+
+// supported data types
+
+// basic types
+// integer 4 bytes of integer data
+// float 4 bytes of float data
+// string data null terminated 1 byte string
+// key data null terminated 1 byte string
+// vector data 12 bytes of 3 floats
+// quaternion data 16 bytes of 4 floats
+
+// list type
+// list data 4 bytes of number of entries followed by followed by pointer
+
+// string pointer 4 bytes of address of string data on the heap (only used in list data)
+// key pointer 4 bytes of address of key data on the heap (only used in list data)
+
+// heap format
+//
+// 4 byte offset to next block (in bytes)
+// 1 byte of type of variable or empty
+// 2 bytes of reference count
+// nn bytes of data
+
+const S32 MAX_HEAP_SIZE = TOP_OF_MEMORY;
+
+class LLScriptAllocEntry
+{
+public:
+ LLScriptAllocEntry() : mSize(0), mType(LST_NULL), mReferenceCount(0) {}
+ LLScriptAllocEntry(S32 offset, U8 type) : mSize(offset), mType(type), mReferenceCount(1) {}
+ friend std::ostream& operator<<(std::ostream& s, const LLScriptAllocEntry &a)
+ {
+ s << "Size: " << a.mSize << " Type: " << LSCRIPTTypeNames[a.mType] << " Count: " << a.mReferenceCount;
+ return s;
+ }
+
+ S32 mSize;
+ U8 mType;
+ S16 mReferenceCount;
+};
+
+// this is only OK because we only load/save via accessors below
+const S32 SIZEOF_SCRIPT_ALLOC_ENTRY = 7;
+
+inline void alloc_entry2bytestream(U8 *buffer, S32 &offset, const LLScriptAllocEntry &entry)
+{
+ if ( (offset < 0)
+ ||(offset > MAX_HEAP_SIZE))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ }
+ else
+ {
+ integer2bytestream(buffer, offset, entry.mSize);
+ byte2bytestream(buffer, offset, entry.mType);
+ s162bytestream(buffer, offset, entry.mReferenceCount);
+ }
+}
+
+inline void bytestream2alloc_entry(LLScriptAllocEntry &entry, U8 *buffer, S32 &offset)
+{
+ if ( (offset < 0)
+ ||(offset > MAX_HEAP_SIZE))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ reset_hp_to_safe_spot(buffer);
+ }
+ else
+ {
+ entry.mSize = bytestream2integer(buffer, offset);
+ entry.mType = bytestream2byte(buffer, offset);
+ entry.mReferenceCount = bytestream2s16(buffer, offset);
+ }
+}
+
+// create a heap from the HR to TM
+BOOL lsa_create_heap(U8 *heap_start, S32 size);
+void lsa_fprint_heap(U8 *buffer, FILE *fp);
+
+void lsa_print_heap(U8 *buffer);
+
+// adding to heap
+// if block is empty
+// if block is at least block size + 4 larger than data
+// split block
+// insert data into first part
+// return address
+// else
+// insert data into block
+// return address
+// else
+// if next block is >= SP
+// set Stack-Heap collision
+// return NULL
+// if next block is empty
+// merge next block with current block
+// go to start of algorithm
+// else
+// move to next block
+// go to start of algorithm
+
+S32 lsa_heap_add_data(U8 *buffer, LLScriptLibData *data, S32 heapsize, BOOL b_delete);
+
+S32 lsa_heap_top(U8 *heap_start, S32 maxsize);
+
+// split block
+// set offset to point to new block
+// set offset of new block to point to original offset - block size - data size
+// set new block to empty
+// set new block reference count to 0
+void lsa_split_block(U8 *buffer, S32 &offset, S32 size, LLScriptAllocEntry &entry);
+
+// insert data
+// if data is non-list type
+// set type to basic type, set reference count to 1, copy data, return address
+// else
+// set type to list data type, set reference count to 1
+// for each list entry
+// insert data
+// return address
+
+void lsa_insert_data(U8 *buffer, S32 &offset, LLScriptLibData *data, LLScriptAllocEntry &entry, S32 heapsize);
+
+S32 lsa_create_data_block(U8 **buffer, LLScriptLibData *data, S32 base_offset);
+
+// increase reference count
+// increase reference count by 1
+
+void lsa_increase_ref_count(U8 *buffer, S32 offset);
+
+// decrease reference count
+// decrease reference count by 1
+// if reference count == 0
+// set type to empty
+
+void lsa_decrease_ref_count(U8 *buffer, S32 offset);
+
+inline S32 get_max_heap_size(U8 *buffer)
+{
+ return get_register(buffer, LREG_SP) - get_register(buffer, LREG_HR);
+}
+
+
+LLScriptLibData *lsa_get_data(U8 *buffer, S32 &offset, BOOL b_dec_ref);
+LLScriptLibData *lsa_get_list_ptr(U8 *buffer, S32 &offset, BOOL b_dec_ref);
+
+S32 lsa_cat_strings(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize);
+S32 lsa_cmp_strings(U8 *buffer, S32 offset1, S32 offset2);
+
+S32 lsa_cat_lists(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize);
+S32 lsa_cmp_lists(U8 *buffer, S32 offset1, S32 offset2);
+S32 lsa_preadd_lists(U8 *buffer, LLScriptLibData *data, S32 offset2, S32 heapsize);
+S32 lsa_postadd_lists(U8 *buffer, S32 offset1, LLScriptLibData *data, S32 heapsize);
+
+// modifying a list
+// insert new list that is modified
+// store returned address in original list's variable
+// decrease reference count on old list
+
+// list l1 = [10];
+// list l2 = l1;
+// l1 = [11];
+
+// we want l2 == [10];
+
+// more complicated example:
+// list l1 = [10, 11];
+// list l2 = l1;
+// l1[0] = 12
+
+// I think that we want l2 = [10, 11];
+
+// one option would be to use syntax like:
+// l1 = llSetList(l1, 0, 12);
+// but this would require variable argument list matching
+// which maybe is ok, but would be work
+// the other option would be changes to lists that have multiple references causes a copy to occur
+
+// popl @l1, 0, integer, 12
+//
+// would cause l1 to be copied, 12 to replace the 0th entry, and the address of the new list to be saved in l1
+//
+
+inline LLScriptLibData *lsa_bubble_sort(LLScriptLibData *src, S32 stride, S32 ascending)
+{
+ S32 number = src->getListLength();
+
+ if (number <= 0)
+ {
+ return NULL;
+ }
+
+ if (stride <= 0)
+ {
+ stride = 1;
+ }
+
+ S32 i = 0;
+
+ if (number % stride)
+ {
+ LLScriptLibData *retval = src->mListp;
+ src->mListp = NULL;
+ return retval;
+ }
+
+ LLScriptLibData **sortarray = (LLScriptLibData **)new U32[number];
+
+ LLScriptLibData *temp = src->mListp;
+ while (temp)
+ {
+ sortarray[i] = temp;
+ i++;
+ temp = temp->mListp;
+ }
+
+ S32 j, s;
+
+ for (i = 0; i < number; i += stride)
+ {
+ for (j = i; j < number; j += stride)
+ {
+ if ( ((*sortarray[i]) <= (*sortarray[j]))
+ != (ascending == TRUE))
+ {
+ for (s = 0; s < stride; s++)
+ {
+ temp = sortarray[i + s];
+ sortarray[i + s] = sortarray[j + s];
+ sortarray[j + s] = temp;
+ }
+ }
+ }
+ }
+
+ i = 1;
+ temp = sortarray[0];
+ while (i < number)
+ {
+ temp->mListp = sortarray[i++];
+ temp = temp->mListp;
+ }
+ temp->mListp = NULL;
+
+ src->mListp = NULL;
+
+ return sortarray[0];
+}
+
+
+inline LLScriptLibData *lsa_randomize(LLScriptLibData *src, S32 stride)
+{
+ S32 number = src->getListLength();
+
+ if (number <= 0)
+ {
+ return NULL;
+ }
+
+ if (stride <= 0)
+ {
+ stride = 1;
+ }
+
+ if (number % stride)
+ {
+ LLScriptLibData *retval = src->mListp;
+ src->mListp = NULL;
+ return retval;
+ }
+
+ LLScriptLibData **sortarray = (LLScriptLibData **)new U32[number];
+
+ LLScriptLibData *temp = src->mListp;
+ S32 i = 0;
+ while (temp)
+ {
+ sortarray[i] = temp;
+ i++;
+ temp = temp->mListp;
+ }
+
+ S32 k, j, s;
+
+ for (k = 0; k < 20; k++)
+ {
+ for (i = 0; i < number; i += stride)
+ {
+ for (j = i; j < number; j += stride)
+ {
+ if (frand(1.f) > 0.5)
+ {
+ for (s = 0; s < stride; s++)
+ {
+ temp = sortarray[i + s];
+ sortarray[i + s] = sortarray[j + s];
+ sortarray[j + s] = temp;
+ }
+ }
+ }
+ }
+ }
+
+ i = 1;
+ temp = sortarray[0];
+ while (i < number)
+ {
+ temp->mListp = sortarray[i++];
+ temp = temp->mListp;
+ }
+ temp->mListp = NULL;
+
+ src->mListp = NULL;
+
+ LLScriptLibData *ret_value = sortarray[0];
+ delete [] sortarray;
+
+ return ret_value;
+}
+
+#endif
diff --git a/indra/lscript/lscript_byteconvert.h b/indra/lscript/lscript_byteconvert.h
new file mode 100644
index 0000000000..d30c84b28c
--- /dev/null
+++ b/indra/lscript/lscript_byteconvert.h
@@ -0,0 +1,1087 @@
+/**
+ * @file lscript_byteconvert.h
+ * @brief Shared code for compiler and assembler for LSL
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// data shared between compiler/assembler
+// used to convert data between byte stream and outside data types
+
+#ifndef LL_LSCRIPT_BYTECONVERT_H
+#define LL_LSCRIPT_BYTECONVERT_H
+
+#include "stdtypes.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "lscript_byteformat.h"
+#include "lluuid.h"
+
+void reset_hp_to_safe_spot(const U8 *buffer);
+
+// remember that LScript byte stream is BigEndian
+void set_fault(const U8 *stream, LSCRIPTRunTimeFaults fault);
+
+inline S32 bytestream2integer(const U8 *stream, S32 &offset)
+{
+ stream += offset;
+ offset += 4;
+ return (*stream<<24) | (*(stream + 1)<<16) | (*(stream + 2)<<8) | *(stream + 3);
+}
+
+inline U32 bytestream2unsigned_integer(const U8 *stream, S32 &offset)
+{
+ stream += offset;
+ offset += 4;
+ return (*stream<<24) | (*(stream + 1)<<16) | (*(stream + 2)<<8) | *(stream + 3);
+}
+
+inline U64 bytestream2u64(const U8 *stream, S32 &offset)
+{
+ stream += offset;
+ offset += 8;
+ return ((U64)(*stream)<<56)| ((U64)(*(stream + 1))<<48) | ((U64)(*(stream + 2))<<40) | ((U64)(*(stream + 3))<<32) |
+ ((U64)(*(stream + 4))<<24) | ((U64)(*(stream + 5))<<16) | ((U64)(*(stream + 6))<<8) | (U64)(*(stream + 7));
+}
+
+inline void integer2bytestream(U8 *stream, S32 &offset, S32 integer)
+{
+ stream += offset;
+ offset += 4;
+ *(stream) = (integer >> 24);
+ *(stream + 1) = (integer >> 16) & 0xff;
+ *(stream + 2) = (integer >> 8) & 0xff;
+ *(stream + 3) = (integer) & 0xff;
+}
+
+inline void unsigned_integer2bytestream(U8 *stream, S32 &offset, U32 integer)
+{
+ stream += offset;
+ offset += 4;
+ *(stream) = (integer >> 24);
+ *(stream + 1) = (integer >> 16) & 0xff;
+ *(stream + 2) = (integer >> 8) & 0xff;
+ *(stream + 3) = (integer) & 0xff;
+}
+inline void u642bytestream(U8 *stream, S32 &offset, U64 integer)
+{
+ stream += offset;
+ offset += 8;
+ *(stream) = (U8)(integer >> 56);
+ *(stream + 1) = (U8)((integer >> 48) & 0xff);
+ *(stream + 2) = (U8)((integer >> 40) & 0xff);
+ *(stream + 3) = (U8)((integer >> 32) & 0xff);
+ *(stream + 4) = (U8)((integer >> 24) & 0xff);
+ *(stream + 5) = (U8)((integer >> 16) & 0xff);
+ *(stream + 6) = (U8)((integer >> 8) & 0xff);
+ *(stream + 7) = (U8)((integer) & 0xff);
+}
+
+inline S16 bytestream2s16(const U8 *stream, S32 &offset)
+{
+ stream += offset;
+ offset += 2;
+ return (*stream<<8) | *(stream + 1);
+}
+
+inline void s162bytestream(U8 *stream, S32 &offset, S16 integer)
+{
+ stream += offset;
+ offset += 2;
+ *(stream) = (integer >> 8);
+ *(stream + 1) = (integer) & 0xff;
+}
+
+inline U16 bytestream2u16(const U8 *stream, S32 &offset)
+{
+ stream += offset;
+ offset += 2;
+ return (*stream<<8) | *(stream + 1);
+}
+
+inline void u162bytestream(U8 *stream, S32 &offset, U16 integer)
+{
+ stream += offset;
+ offset += 2;
+ *(stream) = (integer >> 8);
+ *(stream + 1) = (integer) & 0xff;
+}
+
+inline F32 bytestream2float(const U8 *stream, S32 &offset)
+{
+ S32 value = bytestream2integer(stream, offset);
+ F32 fpvalue = *(F32 *)&value;
+ if (!llfinite(fpvalue))
+ {
+ fpvalue = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ return fpvalue;
+}
+
+inline void float2bytestream(U8 *stream, S32 &offset, F32 floatingpoint)
+{
+ S32 value = *(S32 *)&floatingpoint;
+ integer2bytestream(stream, offset, value);
+}
+
+inline void bytestream_int2float(U8 *stream, S32 &offset)
+{
+ S32 value = bytestream2integer(stream, offset);
+ offset -= 4;
+ F32 fpvalue = (F32)value;
+ if (!llfinite(fpvalue))
+ {
+ fpvalue = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ float2bytestream(stream, offset, fpvalue);
+}
+
+inline void bytestream2char(char *buffer, const U8 *stream, S32 &offset)
+{
+ while ((*buffer++ = *(stream + offset++)))
+ ;
+}
+
+inline void char2bytestream(U8 *stream, S32 &offset, char *buffer)
+{
+ while ((*(stream + offset++) = *buffer++))
+ ;
+}
+
+inline U8 bytestream2byte(const U8 *stream, S32 &offset)
+{
+ return *(stream + offset++);
+}
+
+inline void byte2bytestream(U8 *stream, S32 &offset, U8 byte)
+{
+ *(stream + offset++) = byte;
+}
+
+inline void bytestream2bytestream(U8 *dest, S32 &dest_offset, const U8 *src, S32 &src_offset, S32 count)
+{
+ while (count)
+ {
+ (*(dest + dest_offset++)) = (*(src + src_offset++));
+ count--;
+ }
+}
+
+inline void uuid2bytestream(U8 *stream, S32 &offset, const LLUUID &uuid)
+{
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ *(stream + offset++) = uuid.mData[i];
+ }
+}
+
+inline void bytestream2uuid(U8 *stream, S32 &offset, LLUUID &uuid)
+{
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ uuid.mData[i] = *(stream + offset++);
+ }
+}
+
+// vectors and quaternions and encoded in backwards order to match the way in which they are stored on the stack
+inline void bytestream2vector(LLVector3 &vector, const U8 *stream, S32 &offset)
+{
+ S32 value = bytestream2integer(stream, offset);
+ vector.mV[VZ] = *(F32 *)&value;
+ if (!llfinite(vector.mV[VZ]))
+ {
+ vector.mV[VZ] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ value = bytestream2integer(stream, offset);
+ vector.mV[VY] = *(F32 *)&value;
+ if (!llfinite(vector.mV[VY]))
+ {
+ vector.mV[VY] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ value = bytestream2integer(stream, offset);
+ vector.mV[VX] = *(F32 *)&value;
+ if (!llfinite(vector.mV[VX]))
+ {
+ vector.mV[VX] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+}
+
+inline void vector2bytestream(U8 *stream, S32 &offset, LLVector3 &vector)
+{
+ S32 value = *(S32 *)&vector.mV[VZ];
+ integer2bytestream(stream, offset, value);
+ value = *(S32 *)&vector.mV[VY];
+ integer2bytestream(stream, offset, value);
+ value = *(S32 *)&vector.mV[VX];
+ integer2bytestream(stream, offset, value);
+}
+
+inline void bytestream2quaternion(LLQuaternion &quat, const U8 *stream, S32 &offset)
+{
+ S32 value = bytestream2integer(stream, offset);
+ quat.mQ[VS] = *(F32 *)&value;
+ if (!llfinite(quat.mQ[VS]))
+ {
+ quat.mQ[VS] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ value = bytestream2integer(stream, offset);
+ quat.mQ[VZ] = *(F32 *)&value;
+ if (!llfinite(quat.mQ[VZ]))
+ {
+ quat.mQ[VZ] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ value = bytestream2integer(stream, offset);
+ quat.mQ[VY] = *(F32 *)&value;
+ if (!llfinite(quat.mQ[VY]))
+ {
+ quat.mQ[VY] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ value = bytestream2integer(stream, offset);
+ quat.mQ[VX] = *(F32 *)&value;
+ if (!llfinite(quat.mQ[VX]))
+ {
+ quat.mQ[VX] = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+}
+
+inline void quaternion2bytestream(U8 *stream, S32 &offset, LLQuaternion &quat)
+{
+ S32 value = *(S32 *)&quat.mQ[VS];
+ integer2bytestream(stream, offset, value);
+ value = *(S32 *)&quat.mQ[VZ];
+ integer2bytestream(stream, offset, value);
+ value = *(S32 *)&quat.mQ[VY];
+ integer2bytestream(stream, offset, value);
+ value = *(S32 *)&quat.mQ[VX];
+ integer2bytestream(stream, offset, value);
+}
+
+inline S32 get_register(const U8 *stream, LSCRIPTRegisters reg)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ return bytestream2integer(stream, offset);
+}
+
+inline F32 get_register_fp(U8 *stream, LSCRIPTRegisters reg)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ F32 value = bytestream2float(stream, offset);
+ if (!llfinite(value))
+ {
+ value = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ return value;
+}
+inline U64 get_register_u64(U8 *stream, LSCRIPTRegisters reg)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ return bytestream2u64(stream, offset);
+}
+
+inline U64 get_event_register(U8 *stream, LSCRIPTRegisters reg, S32 major_version)
+{
+ if (major_version == 1)
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ return (U64)bytestream2integer(stream, offset);
+ }
+ else if (major_version == 2)
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg + (LREG_NCE - LREG_CE)];
+ return bytestream2u64(stream, offset);
+ }
+ else
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ return (U64)bytestream2integer(stream, offset);
+ }
+}
+
+inline void set_register(U8 *stream, LSCRIPTRegisters reg, S32 value)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ integer2bytestream(stream, offset, value);
+}
+
+inline void set_register_fp(U8 *stream, LSCRIPTRegisters reg, F32 value)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ float2bytestream(stream, offset, value);
+}
+
+inline void set_register_u64(U8 *stream, LSCRIPTRegisters reg, U64 value)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ u642bytestream(stream, offset, value);
+}
+
+inline void set_event_register(U8 *stream, LSCRIPTRegisters reg, U64 value, S32 major_version)
+{
+ if (major_version == 1)
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ integer2bytestream(stream, offset, (S32)value);
+ }
+ else if (major_version == 2)
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg + (LREG_NCE - LREG_CE)];
+ u642bytestream(stream, offset, value);
+ }
+ else
+ {
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ integer2bytestream(stream, offset, (S32)value);
+ }
+}
+
+
+inline F32 add_register_fp(U8 *stream, LSCRIPTRegisters reg, F32 value)
+{
+ S32 offset = gLSCRIPTRegisterAddresses[reg];
+ F32 newvalue = bytestream2float(stream, offset);
+ newvalue += value;
+ if (!llfinite(newvalue))
+ {
+ newvalue = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ offset = gLSCRIPTRegisterAddresses[reg];
+ float2bytestream(stream, offset, newvalue);
+ return newvalue;
+}
+
+void lsa_print_heap(U8 *buffer);
+
+
+inline void set_fault(const U8 *stream, LSCRIPTRunTimeFaults fault)
+{
+ S32 fr = get_register(stream, LREG_FR);
+ // record the first error
+ if (!fr)
+ {
+ if ( (fault == LSRF_HEAP_ERROR)
+ ||(fault == LSRF_STACK_HEAP_COLLISION)
+ ||(fault == LSRF_BOUND_CHECK_ERROR))
+ {
+ reset_hp_to_safe_spot(stream);
+// lsa_print_heap((U8 *)stream);
+ }
+ fr = LSCRIPTRunTimeFaultBits[fault];
+ set_register((U8 *)stream, LREG_FR, fr);
+ }
+}
+
+inline BOOL set_ip(U8 *stream, S32 ip)
+{
+ // Verify that the Instruction Pointer is in a valid
+ // code area (between the Global Function Register
+ // and Heap Register).
+ S32 gfr = get_register(stream, LREG_GFR);
+ if (ip == 0)
+ {
+ set_register(stream, LREG_IP, ip);
+ return TRUE;
+ }
+ if (ip < gfr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ S32 hr = get_register(stream, LREG_HR);
+ if (ip >= hr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ set_register(stream, LREG_IP, ip);
+ return TRUE;
+}
+
+inline BOOL set_bp(U8 *stream, S32 bp)
+{
+ // Verify that the Base Pointer is in a valid
+ // data area (between the Heap Pointer and
+ // the Top of Memory, and below the
+ // Stack Pointer).
+ S32 hp = get_register(stream, LREG_HP);
+ if (bp <= hp)
+ {
+ set_fault(stream, LSRF_STACK_HEAP_COLLISION);
+ return FALSE;
+ }
+ S32 tm = get_register(stream, LREG_TM);
+ if (bp >= tm)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ S32 sp = get_register(stream, LREG_SP);
+ if (bp < sp)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ set_register(stream, LREG_BP, bp);
+ return TRUE;
+}
+
+inline BOOL set_sp(U8 *stream, S32 sp)
+{
+ // Verify that the Stack Pointer is in a valid
+ // data area (between the Heap Pointer and
+ // the Top of Memory).
+ S32 hp = get_register(stream, LREG_HP);
+ if (sp <= hp)
+ {
+ set_fault(stream, LSRF_STACK_HEAP_COLLISION);
+ return FALSE;
+ }
+ S32 tm = get_register(stream, LREG_TM);
+ if (sp >= tm)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ set_register(stream, LREG_SP, sp);
+ return TRUE;
+}
+
+inline void lscript_push(U8 *stream, U8 value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= 1;
+
+ if (set_sp(stream, sp))
+ {
+ *(stream + sp) = value;
+ }
+}
+
+inline void lscript_push(U8 *stream, S32 value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= LSCRIPTDataSize[LST_INTEGER];
+
+ if (set_sp(stream, sp))
+ {
+ integer2bytestream(stream, sp, value);
+ }
+}
+
+inline void lscript_push(U8 *stream, F32 value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= LSCRIPTDataSize[LST_FLOATINGPOINT];
+
+ if (set_sp(stream, sp))
+ {
+ float2bytestream(stream, sp, value);
+ }
+}
+
+inline void lscript_push(U8 *stream, LLVector3 &value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= LSCRIPTDataSize[LST_VECTOR];
+
+ if (set_sp(stream, sp))
+ {
+ vector2bytestream(stream, sp, value);
+ }
+}
+
+inline void lscript_push(U8 *stream, LLQuaternion &value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= LSCRIPTDataSize[LST_QUATERNION];
+
+ if (set_sp(stream, sp))
+ {
+ quaternion2bytestream(stream, sp, value);
+ }
+}
+
+inline void lscript_pusharg(U8 *stream, S32 arg)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= arg;
+
+ set_sp(stream, sp);
+}
+
+inline void lscript_poparg(U8 *stream, S32 arg)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp += arg;
+
+ set_sp(stream, sp);
+}
+
+inline U8 lscript_pop_char(U8 *stream)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ U8 value = *(stream + sp++);
+ set_sp(stream, sp);
+ return value;
+}
+
+inline S32 lscript_pop_int(U8 *stream)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ S32 value = bytestream2integer(stream, sp);
+ set_sp(stream, sp);
+ return value;
+}
+
+inline F32 lscript_pop_float(U8 *stream)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ F32 value = bytestream2float(stream, sp);
+ if (!llfinite(value))
+ {
+ value = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ set_sp(stream, sp);
+ return value;
+}
+
+inline void lscript_pop_vector(U8 *stream, LLVector3 &value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ bytestream2vector(value, stream, sp);
+ set_sp(stream, sp);
+}
+
+inline void lscript_pop_quaternion(U8 *stream, LLQuaternion &value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ bytestream2quaternion(value, stream, sp);
+ set_sp(stream, sp);
+}
+
+inline void lscript_pusharge(U8 *stream, S32 value)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ sp -= value;
+ if (set_sp(stream, sp))
+ {
+ S32 i;
+ for (i = 0; i < value; i++)
+ {
+ *(stream + sp++) = 0;
+ }
+ }
+}
+
+inline BOOL lscript_check_local(U8 *stream, S32 &address, S32 size)
+{
+ S32 sp = get_register(stream, LREG_SP);
+ S32 bp = get_register(stream, LREG_BP);
+
+ address += size;
+ address = bp - address;
+
+ if (address < sp - size)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ S32 tm = get_register(stream, LREG_TM);
+ if (address + size > tm)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+inline BOOL lscript_check_global(U8 *stream, S32 &address, S32 size)
+{
+ S32 gvr = get_register(stream, LREG_GVR);
+
+ // Possibility of overwriting registers? -- DK 09/07/04
+ if (address < 0)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+
+ address += gvr;
+ S32 gfr = get_register(stream, LREG_GFR);
+
+ if (address + size > gfr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+inline void lscript_local_store(U8 *stream, S32 address, S32 value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_INTEGER]))
+ integer2bytestream(stream, address, value);
+}
+
+inline void lscript_local_store(U8 *stream, S32 address, F32 value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_FLOATINGPOINT]))
+ float2bytestream(stream, address, value);
+}
+
+inline void lscript_local_store(U8 *stream, S32 address, LLVector3 value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_VECTOR]))
+ vector2bytestream(stream, address, value);
+}
+
+inline void lscript_local_store(U8 *stream, S32 address, LLQuaternion value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_QUATERNION]))
+ quaternion2bytestream(stream, address, value);
+}
+
+inline void lscript_global_store(U8 *stream, S32 address, S32 value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_INTEGER]))
+ integer2bytestream(stream, address, value);
+}
+
+inline void lscript_global_store(U8 *stream, S32 address, F32 value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_FLOATINGPOINT]))
+ float2bytestream(stream, address, value);
+}
+
+inline void lscript_global_store(U8 *stream, S32 address, LLVector3 value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_VECTOR]))
+ vector2bytestream(stream, address, value);
+}
+
+inline void lscript_global_store(U8 *stream, S32 address, LLQuaternion value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_QUATERNION]))
+ quaternion2bytestream(stream, address, value);
+}
+
+inline S32 lscript_local_get(U8 *stream, S32 address)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_INTEGER]))
+ return bytestream2integer(stream, address);
+ return 0;
+}
+
+inline void lscript_local_get(U8 *stream, S32 address, F32 &value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_FLOATINGPOINT]))
+ value = bytestream2float(stream, address);
+ if (!llfinite(value))
+ {
+ value = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+}
+
+inline void lscript_local_get(U8 *stream, S32 address, LLVector3 &value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_VECTOR]))
+ bytestream2vector(value, stream, address);
+}
+
+inline void lscript_local_get(U8 *stream, S32 address, LLQuaternion &value)
+{
+ if (lscript_check_local(stream, address, LSCRIPTDataSize[LST_QUATERNION]))
+ bytestream2quaternion(value, stream, address);
+}
+
+inline S32 lscript_global_get(U8 *stream, S32 address)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_INTEGER]))
+ return bytestream2integer(stream, address);
+ return 0;
+}
+
+inline void lscript_global_get(U8 *stream, S32 address, F32 &value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_FLOATINGPOINT]))
+ value = bytestream2float(stream, address);
+ if (!llfinite(value))
+ {
+ value = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+}
+
+inline void lscript_global_get(U8 *stream, S32 address, LLVector3 &value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_VECTOR]))
+ bytestream2vector(value, stream, address);
+}
+
+inline void lscript_global_get(U8 *stream, S32 address, LLQuaternion &value)
+{
+ if (lscript_check_global(stream, address, LSCRIPTDataSize[LST_QUATERNION]))
+ bytestream2quaternion(value, stream, address);
+}
+
+
+
+inline S32 get_state_event_opcoode_start(U8 *stream, S32 state, LSCRIPTStateEventType event)
+{
+ // get the start of the state table
+ S32 sr = get_register(stream, LREG_SR);
+
+ // get the position of the jump to the desired state
+ S32 value = get_register(stream, LREG_VN);
+
+ S32 state_offset_offset = 0;
+ S32 major_version = 0;
+ if (value == LSL2_VERSION1_END_NUMBER)
+ {
+ major_version = LSL2_MAJOR_VERSION_ONE;
+ state_offset_offset = sr + LSCRIPTDataSize[LST_INTEGER] + LSCRIPTDataSize[LST_INTEGER]*2*state;
+ }
+ else if (value == LSL2_VERSION_NUMBER)
+ {
+ major_version = LSL2_MAJOR_VERSION_TWO;
+ state_offset_offset = sr + LSCRIPTDataSize[LST_INTEGER] + LSCRIPTDataSize[LST_INTEGER]*3*state;
+ }
+
+ // get the actual position in memory of the desired state
+ S32 state_offset = sr + bytestream2integer(stream, state_offset_offset);
+
+ // save that value
+ S32 state_offset_base = state_offset;
+
+ // jump past the state name
+ S32 event_jump_offset = state_offset_base + bytestream2integer(stream, state_offset);
+
+ // get the location of the event offset
+ S32 event_offset = event_jump_offset + LSCRIPTDataSize[LST_INTEGER]*2*get_event_handler_jump_position(get_event_register(stream, LREG_ER, major_version), event);
+
+ // now, jump to the event
+ S32 event_start = bytestream2integer(stream, event_offset);
+ event_start += event_jump_offset;
+
+ S32 event_start_original = event_start;
+
+ // now skip past the parameters
+ S32 opcode_offset = bytestream2integer(stream, event_start);
+ return opcode_offset + event_start_original;
+}
+
+inline U64 get_handled_events(U8 *stream, S32 state)
+{
+ U64 retvalue = 0;
+ // get the start of the state table
+ S32 sr = get_register(stream, LREG_SR);
+
+ // get the position of the jump to the desired state
+ S32 value = get_register(stream, LREG_VN);
+ S32 state_handled_offset = 0;
+ if (value == LSL2_VERSION1_END_NUMBER)
+ {
+ state_handled_offset = sr + LSCRIPTDataSize[LST_INTEGER]*2*state + 2*LSCRIPTDataSize[LST_INTEGER];
+ retvalue = bytestream2integer(stream, state_handled_offset);
+ }
+ else if (value == LSL2_VERSION_NUMBER)
+ {
+ state_handled_offset = sr + LSCRIPTDataSize[LST_INTEGER]*3*state + 2*LSCRIPTDataSize[LST_INTEGER];
+ retvalue = bytestream2u64(stream, state_handled_offset);
+ }
+
+ // get the handled events
+ return retvalue;
+}
+
+inline S32 get_event_stack_size(U8 *stream, S32 state, LSCRIPTStateEventType event)
+{
+ // get the start of the state table
+ S32 sr = get_register(stream, LREG_SR);
+
+ // get state offset
+ S32 value = get_register(stream, LREG_VN);
+ S32 state_offset_offset = 0;
+ S32 major_version = 0;
+ if (value == LSL2_VERSION1_END_NUMBER)
+ {
+ major_version = LSL2_MAJOR_VERSION_ONE;
+ state_offset_offset = sr + LSCRIPTDataSize[LST_INTEGER] + LSCRIPTDataSize[LST_INTEGER]*2*state;
+ }
+ else if (value == LSL2_VERSION_NUMBER)
+ {
+ major_version = LSL2_MAJOR_VERSION_TWO;
+ state_offset_offset = sr + LSCRIPTDataSize[LST_INTEGER] + LSCRIPTDataSize[LST_INTEGER]*3*state;
+ }
+
+ S32 state_offset = bytestream2integer(stream, state_offset_offset);
+ state_offset += sr;
+
+ state_offset_offset = state_offset;
+
+ // skip to jump table
+ S32 jump_table = bytestream2integer(stream, state_offset_offset);
+
+ jump_table += state_offset;
+
+ // get the position of the jump to the desired state
+ S32 stack_size_offset = jump_table + LSCRIPTDataSize[LST_INTEGER]*2*get_event_handler_jump_position(get_event_register(stream, LREG_ER, major_version), event) + LSCRIPTDataSize[LST_INTEGER];
+
+ // get the handled events
+ S32 stack_size = bytestream2integer(stream, stack_size_offset);
+ return stack_size;
+}
+
+inline LSCRIPTStateEventType return_first_event(S32 event)
+{
+ S32 count = 1;
+ while (count < LSTT_EOF)
+ {
+ if (event & 0x1)
+ {
+ return (LSCRIPTStateEventType) count;
+ }
+ else
+ {
+ event >>= 1;
+ count++;
+ }
+ }
+ return LSTT_NULL;
+}
+
+
+// the safe instruction versions of these commands will only work if offset is between
+// GFR and HR, meaning that it is an instruction (more or less) in global functions or event handlers
+
+inline BOOL safe_instruction_check_address(U8 *stream, S32 offset, S32 size)
+{
+ S32 gfr = get_register(stream, LREG_GFR);
+ if (offset < gfr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ else
+ {
+ S32 hr = get_register(stream, LREG_HR);
+ if (offset + size > hr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+}
+
+inline BOOL safe_heap_check_address(U8 *stream, S32 offset, S32 size)
+{
+ S32 hr = get_register(stream, LREG_HR);
+ if (offset < hr)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ else
+ {
+ S32 hp = get_register(stream, LREG_HP);
+ if (offset + size > hp)
+ {
+ set_fault(stream, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+}
+
+inline U8 safe_instruction_bytestream2byte(U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, 1))
+ {
+ return *(stream + offset++);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void safe_instruction_byte2bytestream(U8 *stream, S32 &offset, U8 byte)
+{
+ if (safe_instruction_check_address(stream, offset, 1))
+ {
+ *(stream + offset++) = byte;
+ }
+}
+
+inline S32 safe_instruction_bytestream2integer(U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_INTEGER]))
+ {
+ return (bytestream2integer(stream, offset));
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void safe_instruction_integer2bytestream(U8 *stream, S32 &offset, S32 value)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_INTEGER]))
+ {
+ integer2bytestream(stream, offset, value);
+ }
+}
+
+inline U16 safe_instruction_bytestream2u16(U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, 2))
+ {
+ return (bytestream2u16(stream, offset));
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void safe_instruction_u162bytestream(U8 *stream, S32 &offset, U16 value)
+{
+ if (safe_instruction_check_address(stream, offset, 2))
+ {
+ u162bytestream(stream, offset, value);
+ }
+}
+
+inline F32 safe_instruction_bytestream2float(U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_INTEGER]))
+ {
+ F32 value = bytestream2float(stream, offset);
+ if (!llfinite(value))
+ {
+ value = 0;
+ set_fault(stream, LSRF_MATH);
+ }
+ return value;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+inline void safe_instruction_float2bytestream(U8 *stream, S32 &offset, F32 value)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_FLOATINGPOINT]))
+ {
+ float2bytestream(stream, offset, value);
+ }
+}
+
+inline void safe_instruction_bytestream2char(char *buffer, U8 *stream, S32 &offset)
+{
+ while ( (safe_instruction_check_address(stream, offset, 1))
+ &&(*buffer++ = *(stream + offset++)))
+ ;
+}
+
+inline void safe_instruction_bytestream_count_char(U8 *stream, S32 &offset)
+{
+ while ( (safe_instruction_check_address(stream, offset, 1))
+ &&(*(stream + offset++)))
+ ;
+}
+
+inline void safe_heap_bytestream_count_char(U8 *stream, S32 &offset)
+{
+ while ( (safe_heap_check_address(stream, offset, 1))
+ &&(*(stream + offset++)))
+ ;
+}
+
+inline void safe_instruction_char2bytestream(U8 *stream, S32 &offset, char *buffer)
+{
+ while ( (safe_instruction_check_address(stream, offset, 1))
+ &&(*(stream + offset++) = *buffer++))
+ ;
+}
+
+inline void safe_instruction_bytestream2vector(LLVector3 &value, U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_VECTOR]))
+ {
+ bytestream2vector(value, stream, offset);
+ }
+}
+
+inline void safe_instruction_vector2bytestream(U8 *stream, S32 &offset, LLVector3 &value)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_VECTOR]))
+ {
+ vector2bytestream(stream, offset, value);
+ }
+}
+
+inline void safe_instruction_bytestream2quaternion(LLQuaternion &value, U8 *stream, S32 &offset)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_QUATERNION]))
+ {
+ bytestream2quaternion(value, stream, offset);
+ }
+}
+
+inline void safe_instruction_quaternion2bytestream(U8 *stream, S32 &offset, LLQuaternion &value)
+{
+ if (safe_instruction_check_address(stream, offset, LSCRIPTDataSize[LST_QUATERNION]))
+ {
+ quaternion2bytestream(stream, offset, value);
+ }
+}
+
+static inline LSCRIPTType char2type(char type)
+{
+ switch(type)
+ {
+ case 'i':
+ return LST_INTEGER;
+ case 'f':
+ return LST_FLOATINGPOINT;
+ case 's':
+ return LST_STRING;
+ case 'k':
+ return LST_KEY;
+ case 'v':
+ return LST_VECTOR;
+ case 'q':
+ return LST_QUATERNION;
+ case 'l':
+ return LST_LIST;
+ default:
+ return LST_NULL;
+ }
+}
+
+#endif
diff --git a/indra/lscript/lscript_byteformat.h b/indra/lscript/lscript_byteformat.h
new file mode 100644
index 0000000000..a79f2effae
--- /dev/null
+++ b/indra/lscript/lscript_byteformat.h
@@ -0,0 +1,544 @@
+/**
+ * @file lscript_byteformat.h
+ * @brief Shared code between compiler and assembler and LSL
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_BYTEFORMAT_H
+#define LL_LSCRIPT_BYTEFORMAT_H
+
+// Data shared between compiler/assembler and lscript execution code
+
+#include "stdtypes.h"
+
+const S32 LSL2_VERSION_NUMBER = 0x0200;
+const S32 LSL2_VERSION1_END_NUMBER = 0x0101;
+const S32 LSL2_VERSION2_START_NUMBER = 0x0200;
+
+const S32 LSL2_MAJOR_VERSION_ONE = 1;
+const S32 LSL2_MAJOR_VERSION_TWO = 2;
+const S32 LSL2_CURRENT_MAJOR_VERSION = LSL2_MAJOR_VERSION_TWO;
+
+const S32 TOP_OF_MEMORY = 16384;
+
+typedef enum e_lscript_registers
+{
+ LREG_INVALID,
+ LREG_IP, // instruction pointer
+ LREG_VN, // version number
+ LREG_BP, // base pointer - what local variables are referenced from
+ LREG_SP, // stack pointer - where the top of the stack is
+ LREG_HR, // heap register - where in memory does the heap start
+ LREG_HP, // heap pointer - where is the top of the heap?
+ LREG_CS, // current state - what state are we currently in?
+ LREG_NS, // next state - what state are we currently in?
+ LREG_CE, // current events - what events are waiting to be handled?
+ LREG_IE, // in event - which event handler are we currently in?
+ LREG_ER, // event register - what events do we have active handlers for?
+ LREG_FR, // fault register - which errors are currently active?
+ LREG_SLR, // sleep register - are we sleeping?
+ LREG_GVR, // global variable register - where do global variables start
+ LREG_GFR, // global function register - where do global functions start
+ LREG_SR, // state register - where do states start
+ LREG_TM, // top of memory - where is the top of memory
+ LREG_PR, // parameter register - data passed to script from launcher
+ LREG_ESR, // energy supply register - how much energy do we have on board?
+ LREG_NCE, // 64 bit current envents - what events are waiting to be handled?
+ LREG_NIE, // 64 bit in event - which event handler are we currently in?
+ LREG_NER, // 64 bit event register - what events do we have active handlers for?
+ LREG_EOF
+} LSCRIPTRegisters;
+
+const S32 gLSCRIPTRegisterAddresses[LREG_EOF] =
+{
+ 0, // LREG_INVALID
+ 4, // LREG_IP
+ 8, // LREG_VN
+ 12, // LREG_BP
+ 16, // LREG_SP
+ 20, // LREG_HR
+ 24, // LREG_HP
+ 28, // LREG_CS
+ 32, // LREG_NS
+ 36, // LREG_CE
+ 40, // LREG_IE
+ 44, // LREG_ER
+ 48, // LREG_FR
+ 52, // LREG_SLR
+ 56, // LREG_GVR
+ 60, // LREG_GFR
+ 72, // LREG_SR
+ 0, // LREG_TM
+ 64, // LREG_PR
+ 68, // LREG_ESR
+ 76, // LREG_NCE
+ 84, // LREG_NIE
+ 92, // LREG_NER
+};
+
+const char * const gLSCRIPTRegisterNames[LREG_EOF] =
+{
+ "INVALID", // LREG_INVALID
+ "IP", // LREG_IP
+ "VN", // LREG_VN
+ "BP", // LREG_BP
+ "SP", // LREG_SP
+ "HR", // LREG_HR
+ "HP", // LREG_HP
+ "CS", // LREG_CS
+ "NS", // LREG_NS
+ "CE", // LREG_CE
+ "IE", // LREG_IE
+ "ER", // LREG_ER
+ "FR", // LREG_FR
+ "SLR", // LREG_SLR
+ "GVR", // LREG_GVR
+ "GFR", // LREG_GFR
+ "SR", // LREG_SR
+ "TM", // LREG_TM
+ "PR", // LREG_PR
+ "ESR", // LREG_ESR
+ "NCE", // LREG_NCE
+ "NIE", // LREG_NIE
+ "NER", // LREG_NER
+};
+
+typedef enum e_lscript_op_codes
+{
+ LOPC_INVALID,
+ LOPC_NOOP,
+ LOPC_POP,
+ LOPC_POPS,
+ LOPC_POPL,
+ LOPC_POPV,
+ LOPC_POPQ,
+ LOPC_POPARG,
+ LOPC_POPIP,
+ LOPC_POPBP,
+ LOPC_POPSP,
+ LOPC_POPSLR,
+ LOPC_DUP,
+ LOPC_DUPS,
+ LOPC_DUPL,
+ LOPC_DUPV,
+ LOPC_DUPQ,
+ LOPC_STORE,
+ LOPC_STORES,
+ LOPC_STOREL,
+ LOPC_STOREV,
+ LOPC_STOREQ,
+ LOPC_STOREG,
+ LOPC_STOREGS,
+ LOPC_STOREGL,
+ LOPC_STOREGV,
+ LOPC_STOREGQ,
+ LOPC_LOADP,
+ LOPC_LOADSP,
+ LOPC_LOADLP,
+ LOPC_LOADVP,
+ LOPC_LOADQP,
+ LOPC_LOADGP,
+ LOPC_LOADGLP,
+ LOPC_LOADGSP,
+ LOPC_LOADGVP,
+ LOPC_LOADGQP,
+ LOPC_PUSH,
+ LOPC_PUSHS,
+ LOPC_PUSHL,
+ LOPC_PUSHV,
+ LOPC_PUSHQ,
+ LOPC_PUSHG,
+ LOPC_PUSHGS,
+ LOPC_PUSHGL,
+ LOPC_PUSHGV,
+ LOPC_PUSHGQ,
+ LOPC_PUSHIP,
+ LOPC_PUSHBP,
+ LOPC_PUSHSP,
+ LOPC_PUSHARGB,
+ LOPC_PUSHARGI,
+ LOPC_PUSHARGF,
+ LOPC_PUSHARGS,
+ LOPC_PUSHARGV,
+ LOPC_PUSHARGQ,
+ LOPC_PUSHE,
+ LOPC_PUSHEV,
+ LOPC_PUSHEQ,
+ LOPC_PUSHARGE,
+ LOPC_ADD,
+ LOPC_SUB,
+ LOPC_MUL,
+ LOPC_DIV,
+ LOPC_MOD,
+ LOPC_EQ,
+ LOPC_NEQ,
+ LOPC_LEQ,
+ LOPC_GEQ,
+ LOPC_LESS,
+ LOPC_GREATER,
+ LOPC_BITAND,
+ LOPC_BITOR,
+ LOPC_BITXOR,
+ LOPC_BOOLAND,
+ LOPC_BOOLOR,
+ LOPC_NEG,
+ LOPC_BITNOT,
+ LOPC_BOOLNOT,
+ LOPC_JUMP,
+ LOPC_JUMPIF,
+ LOPC_JUMPNIF,
+ LOPC_STATE,
+ LOPC_CALL,
+ LOPC_RETURN,
+ LOPC_CAST,
+ LOPC_STACKTOS,
+ LOPC_STACKTOL,
+ LOPC_PRINT,
+ LOPC_CALLLIB,
+ LOPC_CALLLIB_TWO_BYTE,
+ LOPC_SHL,
+ LOPC_SHR,
+ LOPC_EOF
+} LSCRIPTOpCodesEnum;
+
+const U8 LSCRIPTOpCodes[LOPC_EOF] =
+{
+ 0x00, // LOPC_INVALID
+ 0x00, // LOPC_NOOP
+ 0x01, // LOPC_POP
+ 0x02, // LOPC_POPS
+ 0x03, // LOPC_POPL
+ 0x04, // LOPC_POPV
+ 0x05, // LOPC_POPQ
+ 0x06, // LOPC_POPARG
+ 0x07, // LOPC_POPIP
+ 0x08, // LOPC_POPBP
+ 0x09, // LOPC_POPSP
+ 0x0a, // LOPC_POPSLR
+ 0x20, // LOPC_DUP
+ 0x21, // LOPC_DUPS
+ 0x22, // LOPC_DUPL
+ 0x23, // LOPC_DUPV
+ 0x24, // LOPC_DUPQ
+ 0x30, // LOPC_STORE
+ 0x31, // LOPC_STORES
+ 0x32, // LOPC_STOREL
+ 0x33, // LOPC_STOREV
+ 0x34, // LOPC_STOREQ
+ 0x35, // LOPC_STOREG
+ 0x36, // LOPC_STOREGS
+ 0x37, // LOPC_STOREGL
+ 0x38, // LOPC_STOREGV
+ 0x39, // LOPC_STOREGQ
+ 0x3a, // LOPC_LOADP
+ 0x3b, // LOPC_LOADSP
+ 0x3c, // LOPC_LOADLP
+ 0x3d, // LOPC_LOADVP
+ 0x3e, // LOPC_LOADQP
+ 0x3f, // LOPC_LOADGP
+ 0x40, // LOPC_LOADGSP
+ 0x41, // LOPC_LOADGLP
+ 0x42, // LOPC_LOADGVP
+ 0x43, // LOPC_LOADGQP
+ 0x50, // LOPC_PUSH
+ 0x51, // LOPC_PUSHS
+ 0x52, // LOPC_PUSHL
+ 0x53, // LOPC_PUSHV
+ 0x54, // LOPC_PUSHQ
+ 0x55, // LOPC_PUSHG
+ 0x56, // LOPC_PUSHGS
+ 0x57, // LOPC_PUSHGL
+ 0x58, // LOPC_PUSHGV
+ 0x59, // LOPC_PUSHGQ
+ 0x5a, // LOPC_PUSHIP
+ 0x5b, // LOPC_PUSHBP
+ 0x5c, // LOPC_PUSHSP
+ 0x5d, // LOPC_PUSHARGB
+ 0x5e, // LOPC_PUSHARGI
+ 0x5f, // LOPC_PUSHARGF
+ 0x60, // LOPC_PUSHARGS
+ 0x61, // LOPC_PUSHARGV
+ 0x62, // LOPC_PUSHARGQ
+ 0x63, // LOPC_PUSHE
+ 0x64, // LOPC_PUSHEV
+ 0x65, // LOPC_PUSHEQ
+ 0x66, // LOPC_PUSHARGE
+ 0x70, // LOPC_ADD
+ 0x71, // LOPC_SUB
+ 0x72, // LOPC_MUL
+ 0x73, // LOPC_DIV
+ 0x74, // LOPC_MOD
+ 0x75, // LOPC_EQ
+ 0x76, // LOPC_NEQ
+ 0x77, // LOPC_LEQ
+ 0x78, // LOPC_GEQ
+ 0x79, // LOPC_LESS
+ 0x7a, // LOPC_GREATER
+ 0x7b, // LOPC_BITAND
+ 0x7c, // LOPC_BITOR
+ 0x7d, // LOPC_BITXOR
+ 0x7e, // LOPC_BOOLAND
+ 0x7f, // LOPC_BOOLOR
+ 0x80, // LOPC_NEG
+ 0x81, // LOPC_BITNOT
+ 0x82, // LOPC_BOOLNOT
+ 0x90, // LOPC_JUMP
+ 0x91, // LOPC_JUMPIF
+ 0x92, // LOPC_JUMPNIF
+ 0x93, // LOPC_STATE
+ 0x94, // LOPC_CALL
+ 0x95, // LOPC_RETURN
+ 0xa0, // LOPC_CAST
+ 0xb0, // LOPC_STACKTOS
+ 0xb1, // LOPC_STACKTOL
+ 0xc0, // LOPC_PRINT
+ 0xd0, // LOPC_CALLLIB
+ 0xd1, // LOPC_CALLLIB_TWO_BYTE
+ 0xe0, // LOPC_SHL
+ 0xe1 // LOPC_SHR
+};
+
+typedef enum e_lscript_state_event_type
+{
+ LSTT_NULL,
+ LSTT_STATE_ENTRY,
+ LSTT_STATE_EXIT,
+ LSTT_TOUCH_START,
+ LSTT_TOUCH,
+ LSTT_TOUCH_END,
+ LSTT_COLLISION_START,
+ LSTT_COLLISION,
+ LSTT_COLLISION_END,
+ LSTT_LAND_COLLISION_START,
+ LSTT_LAND_COLLISION,
+ LSTT_LAND_COLLISION_END,
+ LSTT_TIMER,
+ LSTT_CHAT,
+ LSTT_REZ,
+ LSTT_SENSOR,
+ LSTT_NO_SENSOR,
+ LSTT_CONTROL,
+ LSTT_MONEY,
+ LSTT_EMAIL,
+ LSTT_AT_TARGET,
+ LSTT_NOT_AT_TARGET,
+ LSTT_AT_ROT_TARGET,
+ LSTT_NOT_AT_ROT_TARGET,
+ LSTT_RTPERMISSIONS,
+ LSTT_INVENTORY,
+ LSTT_ATTACH,
+ LSTT_DATASERVER,
+ LSTT_LINK_MESSAGE,
+ LSTT_MOVING_START,
+ LSTT_MOVING_END,
+ LSTT_OBJECT_REZ,
+ LSTT_REMOTE_DATA,
+ LSTT_HTTP_RESPONSE,
+ LSTT_EOF,
+
+ LSTT_STATE_BEGIN = LSTT_STATE_ENTRY,
+ LSTT_STATE_END = LSTT_EOF
+} LSCRIPTStateEventType;
+
+const U64 LSCRIPTStateBitField[LSTT_EOF] =
+{
+ 0x0000000000000000, // LSTT_NULL
+ 0x0000000000000001, // LSTT_STATE_ENTRY
+ 0x0000000000000002, // LSTT_STATE_EXIT
+ 0x0000000000000004, // LSTT_TOUCH_START
+ 0x0000000000000008, // LSTT_TOUCH
+ 0x0000000000000010, // LSTT_TOUCH_END
+ 0x0000000000000020, // LSTT_COLLISION_START
+ 0x0000000000000040, // LSTT_COLLISION
+ 0x0000000000000080, // LSTT_COLLISION_END
+ 0x0000000000000100, // LSTT_LAND_COLLISION_START
+ 0x0000000000000200, // LSTT_LAND_COLLISION
+ 0x0000000000000400, // LSTT_LAND_COLLISION_END
+ 0x0000000000000800, // LSTT_TIMER
+ 0x0000000000001000, // LSTT_CHAT
+ 0x0000000000002000, // LSTT_REZ
+ 0x0000000000004000, // LSTT_SENSOR
+ 0x0000000000008000, // LSTT_NO_SENSOR
+ 0x0000000000010000, // LSTT_CONTROL
+ 0x0000000000020000, // LSTT_MONEY
+ 0x0000000000040000, // LSTT_EMAIL
+ 0x0000000000080000, // LSTT_AT_TARGET
+ 0x0000000000100000, // LSTT_NOT_AT_TARGET
+ 0x0000000000200000, // LSTT_AT_ROT_TARGET
+ 0x0000000000400000, // LSTT_NOT_AT_ROT_TARGET
+ 0x0000000000800000, // LSTT_RTPERMISSIONS
+ 0x0000000001000000, // LSTT_INVENTORY
+ 0x0000000002000000, // LSTT_ATTACH
+ 0x0000000004000000, // LSTT_DATASERVER
+ 0x0000000008000000, // LSTT_LINK_MESSAGE
+ 0x0000000010000000, // LSTT_MOVING_START
+ 0x0000000020000000, // LSTT_MOVING_END
+ 0x0000000040000000, // LSTT_OBJECT_REZ
+ 0x0000000080000000, // LSTT_REMOTE_DATA
+ 0x0000000100000000LL // LSTT_HTTP_RESPOSE
+};
+
+inline S32 get_event_handler_jump_position(U64 bit_field, LSCRIPTStateEventType type)
+{
+ S32 count = 0, position = LSTT_STATE_ENTRY;
+ while (position < type)
+ {
+ if (bit_field & 0x1)
+ {
+ count++;
+ }
+ bit_field >>= 1;
+ position++;
+ }
+ return count;
+}
+
+inline S32 get_number_of_event_handlers(U64 bit_field)
+{
+ S32 count = 0, position = 0;
+ while (position < LSTT_EOF)
+ {
+ if (bit_field & 0x1)
+ {
+ count++;
+ }
+ bit_field >>= 1;
+ position++;
+ }
+ return count;
+}
+
+typedef enum e_lscript_types
+{
+ LST_NULL,
+ LST_INTEGER,
+ LST_FLOATINGPOINT,
+ LST_STRING,
+ LST_KEY,
+ LST_VECTOR,
+ LST_QUATERNION,
+ LST_LIST,
+ LST_UNDEFINED,
+ LST_EOF
+} LSCRIPTType;
+
+const U8 LSCRIPTTypeByte[LST_EOF] =
+{
+ LST_NULL,
+ LST_INTEGER,
+ LST_FLOATINGPOINT,
+ LST_STRING,
+ LST_KEY,
+ LST_VECTOR,
+ LST_QUATERNION,
+ LST_LIST,
+ LST_NULL,
+};
+
+const U8 LSCRIPTTypeHi4Bits[LST_EOF] =
+{
+ LST_NULL,
+ LST_INTEGER << 4,
+ LST_FLOATINGPOINT << 4,
+ LST_STRING << 4,
+ LST_KEY << 4,
+ LST_VECTOR << 4,
+ LST_QUATERNION << 4,
+ LST_LIST << 4,
+};
+
+const char * const LSCRIPTTypeNames[LST_EOF] =
+{
+ "VOID",
+ "integer",
+ "float",
+ "string",
+ "key",
+ "vector",
+ "quaternion",
+ "list",
+ "invalid"
+};
+
+const S32 LSCRIPTDataSize[LST_EOF] =
+{
+ 0, // VOID
+ 4, // integer
+ 4, // float
+ 4, // string
+ 4, // key
+ 12, // vector
+ 16, // quaternion
+ 4, // list
+ 0 // invalid
+};
+
+
+typedef enum e_lscript_runtime_faults
+{
+ LSRF_INVALID,
+ LSRF_MATH,
+ LSRF_STACK_HEAP_COLLISION,
+ LSRF_BOUND_CHECK_ERROR,
+ LSRF_HEAP_ERROR,
+ LSRF_VERSION_MISMATCH,
+ LSRF_MISSING_INVENTORY,
+ LSRF_SANDBOX,
+ LSRF_CHAT_OVERRUN,
+ LSRF_TOO_MANY_LISTENS,
+ LSRF_NESTING_LISTS,
+ LSRF_EOF
+} LSCRIPTRunTimeFaults;
+
+extern char *LSCRIPTRunTimeFaultStrings[LSRF_EOF];
+
+const S32 LSCRIPTRunTimeFaultBits[LSRF_EOF] =
+{
+ 0, // LSRF_INVALID
+ 1, // LSRF_MATH
+ 2, // LSRF_STACK_HEAP_COLLISION
+ 3, // LSREF_BOUND_CHECK_ERROR
+ 4, // LSREF_HEAP_ERROR
+ 5, // LSREF_VERSION_MISMATCH
+ 6, // LSREF_MISSING_INVENTORY
+ 7, // LSRF_SANDBOX
+ 8, // LSRF_CHAT_OVERRUN
+ 9, // LSRF_TOO_MANY_LISTENS
+ 10, // LSRF_NESTING_LISTS
+};
+
+typedef enum e_lscript_runtime_permissions
+{
+ SCRIPT_PERMISSION_DEBIT,
+ SCRIPT_PERMISSION_TAKE_CONTROLS,
+ SCRIPT_PERMISSION_REMAP_CONTROLS,
+ SCRIPT_PERMISSION_TRIGGER_ANIMATION,
+ SCRIPT_PERMISSION_ATTACH,
+ SCRIPT_PERMISSION_RELEASE_OWNERSHIP,
+ SCRIPT_PERMISSION_CHANGE_LINKS,
+ SCRIPT_PERMISSION_CHANGE_JOINTS,
+ SCRIPT_PERMISSION_CHANGE_PERMISSIONS,
+ SCRIPT_PERMISSION_TRACK_CAMERA,
+ SCRIPT_PERMISSION_CONTROL_CAMERA,
+ SCRIPT_PERMISSION_EOF
+} LSCRIPTRunTimePermissions;
+
+const U32 LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_EOF] =
+{
+ (0x1 << 1), // SCRIPT_PERMISSION_DEBIT,
+ (0x1 << 2), // SCRIPT_PERMISSION_TAKE_CONTROLS,
+ (0x1 << 3), // SCRIPT_PERMISSION_REMAP_CONTROLS,
+ (0x1 << 4), // SCRIPT_PERMISSION_TRIGGER_ANIMATION,
+ (0x1 << 5), // SCRIPT_PERMISSION_ATTACH,
+ (0x1 << 6), // SCRIPT_PERMISSION_RELEASE_OWNERSHIP,
+ (0x1 << 7), // SCRIPT_PERMISSION_CHANGE_LINKS,
+ (0x1 << 8), // SCRIPT_PERMISSION_CHANGE_JOINTS,
+ (0x1 << 9), // SCRIPT_PERMISSION_CHANGE_PERMISSIONS
+ (0x1 << 10),// SCRIPT_PERMISSION_TRACK_CAMERA
+ (0x1 << 11),// SCRIPT_PERMISSION_CONTROL_CAMERA
+};
+
+#endif
+
diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l
new file mode 100644
index 0000000000..2ee219a8ee
--- /dev/null
+++ b/indra/lscript/lscript_compile/indra.l
@@ -0,0 +1,834 @@
+D [-]?[0-9]
+N [0-9]
+L [a-zA-Z_]
+H [a-fA-F0-9]
+E [Ee][+-]?{D}+
+FS (f|F)
+%e 8000
+%n 4000
+%p 5000
+
+%{
+#include <stdio.h>
+#include "stdtypes.h"
+#include "llmath.h"
+#include "lscript_tree.h"
+#include "lscript_typecheck.h"
+#include "lscript_resource.h"
+#include "llfile.h"
+#if LL_WINDOWS
+#include "ytab.h"
+#else
+#include "indra.y.h"
+#endif
+#include "lltimer.h"
+#include "indra_constants.h"
+#include "llagentconstants.h"
+#include "lllslconstants.h"
+#include "lluuid.h"
+#include "llassetstorage.h"
+#include "llpartdata.h"
+#include "llvehicleparams.h"
+#include "llpermissionsflags.h"
+#include "llfollowcamparams.h"
+#include "llparcelflags.h"
+#include "llregionflags.h"
+#include "lscript_http.h"
+
+void count();
+void comment();
+void parse_string();
+
+#define YYLMAX 16384
+#define YY_NEVER_INTERACTIVE 1 /* stops flex from calling isatty() */
+
+#if defined(__cplusplus)
+extern "C" { int yylex( void ); }
+extern "C" { int yyparse( void ); }
+extern "C" { int yyerror(const char *fmt, ...); }
+#endif
+
+%}
+
+%%
+"//" { gInternalLine++; gInternalColumn = 0; comment(); }
+
+"integer" { count(); return(INTEGER); }
+"float" { count(); return(FLOAT_TYPE); }
+"string" { count(); return(STRING); }
+"key" { count(); return(LLKEY); }
+"vector" { count(); return(VECTOR); }
+"quaternion" { count(); return(QUATERNION); }
+"rotation" { count(); return(QUATERNION); }
+"list" { count(); return(LIST); }
+
+"default" { count(); yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(STATE_DEFAULT); }
+"state" { count(); return(STATE); }
+"event" { count(); return(EVENT); }
+"jump" { count(); return(JUMP); }
+"return" { count(); return(RETURN); }
+"if" { count(); return(IF); }
+"else" { count(); return(ELSE); }
+"for" { count(); return(FOR); }
+"do" { count(); return(DO); }
+"while" { count(); return(WHILE); }
+
+"state_entry" { count(); return(STATE_ENTRY); }
+"state_exit" { count(); return(STATE_EXIT); }
+"touch_start" { count(); return(TOUCH_START); }
+"touch" { count(); return(TOUCH); }
+"touch_end" { count(); return(TOUCH_END); }
+"collision_start" { count(); return(COLLISION_START); }
+"collision" { count(); return(COLLISION); }
+"collision_end" { count(); return(COLLISION_END); }
+"land_collision_start" { count(); return(LAND_COLLISION_START); }
+"land_collision" { count(); return(LAND_COLLISION); }
+"land_collision_end" { count(); return(LAND_COLLISION_END); }
+"timer" { count(); return(TIMER); }
+"listen" { count(); return(CHAT); }
+"sensor" { count(); return(SENSOR); }
+"no_sensor" { count(); return(NO_SENSOR); }
+"control" { count(); return(CONTROL); }
+"print" { count(); return(PRINT); }
+"at_target" { count(); return(AT_TARGET); }
+"not_at_target" { count(); return(NOT_AT_TARGET); }
+"at_rot_target" { count(); return(AT_ROT_TARGET); }
+"not_at_rot_target" { count(); return(NOT_AT_ROT_TARGET); }
+"money" { count(); return(MONEY); }
+"email" { count(); return(EMAIL); }
+"run_time_permissions" { count(); return(RUN_TIME_PERMISSIONS); }
+"changed" { count(); return(INVENTORY); }
+"attach" { count(); return(ATTACH); }
+"dataserver" { count(); return(DATASERVER); }
+"moving_start" { count(); return(MOVING_START); }
+"moving_end" { count(); return(MOVING_END); }
+"link_message" { count(); return(LINK_MESSAGE); }
+"on_rez" { count(); return(REZ); }
+"object_rez" { count(); return(OBJECT_REZ); }
+"remote_data" { count(); return(REMOTE_DATA); }
+"http_response" { count(); return(HTTP_RESPONSE); }
+"." { count(); return(PERIOD); }
+
+
+0[xX]{H}+ { count(); yylval.ival = strtoul(yytext, NULL, 0); return(INTEGER_CONSTANT); }
+{D}+ { count(); yylval.ival = strtoul(yytext, NULL, 10); return(INTEGER_CONSTANT); }
+"TRUE" { count(); yylval.ival = 1; return(INTEGER_TRUE); }
+"FALSE" { count(); yylval.ival = 0; return(INTEGER_FALSE); }
+"STATUS_PHYSICS" { count(); yylval.ival = 0x1; return(INTEGER_CONSTANT); }
+"STATUS_ROTATE_X" { count(); yylval.ival = 0x2; return(INTEGER_CONSTANT); }
+"STATUS_ROTATE_Y" { count(); yylval.ival = 0x4; return(INTEGER_CONSTANT); }
+"STATUS_ROTATE_Z" { count(); yylval.ival = 0x8; return(INTEGER_CONSTANT); }
+"STATUS_PHANTOM" { count(); yylval.ival = 0x10; return(INTEGER_CONSTANT); }
+"STATUS_SANDBOX" { count(); yylval.ival = 0x20; return(INTEGER_CONSTANT); }
+"STATUS_BLOCK_GRAB" { count(); yylval.ival = 0x40; return(INTEGER_CONSTANT); }
+"STATUS_DIE_AT_EDGE" { count(); yylval.ival = 0x80; return(INTEGER_CONSTANT); }
+"STATUS_RETURN_AT_EDGE" { count(); yylval.ival = 0x100; return(INTEGER_CONSTANT); }
+"STATUS_CAST_SHADOWS" { count(); yylval.ival = 0x200; return(INTEGER_CONSTANT); }
+
+"AGENT_FLYING" { count(); yylval.ival = AGENT_FLYING; return(INTEGER_CONSTANT); }
+"AGENT_ATTACHMENTS" { count(); yylval.ival = AGENT_ATTACHMENTS; return(INTEGER_CONSTANT); }
+"AGENT_SCRIPTED" { count(); yylval.ival = AGENT_SCRIPTED; return(INTEGER_CONSTANT); }
+"AGENT_MOUSELOOK" { count(); yylval.ival = AGENT_MOUSELOOK; return(INTEGER_CONSTANT); }
+"AGENT_SITTING" { count(); yylval.ival = AGENT_SITTING; return(INTEGER_CONSTANT); }
+"AGENT_ON_OBJECT" { count(); yylval.ival = AGENT_ON_OBJECT; return(INTEGER_CONSTANT); }
+"AGENT_AWAY" { count(); yylval.ival = AGENT_AWAY; return(INTEGER_CONSTANT); }
+"AGENT_WALKING" { count(); yylval.ival = AGENT_WALKING; return(INTEGER_CONSTANT); }
+"AGENT_IN_AIR" { count(); yylval.ival = AGENT_IN_AIR; return(INTEGER_CONSTANT); }
+"AGENT_TYPING" { count(); yylval.ival = AGENT_TYPING; return(INTEGER_CONSTANT); }
+"AGENT_CROUCHING" { count(); yylval.ival = AGENT_CROUCHING; return(INTEGER_CONSTANT); }
+"AGENT_BUSY" { count(); yylval.ival = AGENT_BUSY; return(INTEGER_CONSTANT); }
+"AGENT_ALWAYS_RUN" { count(); yylval.ival = AGENT_ALWAYS_RUN; return(INTEGER_CONSTANT); }
+
+"CAMERA_PITCH" { count(); yylval.ival = FOLLOWCAM_PITCH; return(INTEGER_CONSTANT); }
+"CAMERA_FOCUS_OFFSET" { count(); yylval.ival = FOLLOWCAM_FOCUS_OFFSET; return (INTEGER_CONSTANT); }
+"CAMERA_POSITION_LAG" { count(); yylval.ival = FOLLOWCAM_POSITION_LAG; return (INTEGER_CONSTANT); }
+"CAMERA_FOCUS_LAG" { count(); yylval.ival = FOLLOWCAM_FOCUS_LAG; return (INTEGER_CONSTANT); }
+"CAMERA_DISTANCE" { count(); yylval.ival = FOLLOWCAM_DISTANCE; return (INTEGER_CONSTANT); }
+"CAMERA_BEHINDNESS_ANGLE" { count(); yylval.ival = FOLLOWCAM_BEHINDNESS_ANGLE; return (INTEGER_CONSTANT); }
+"CAMERA_BEHINDNESS_LAG" { count(); yylval.ival = FOLLOWCAM_BEHINDNESS_LAG; return (INTEGER_CONSTANT); }
+"CAMERA_POSITION_THRESHOLD" { count(); yylval.ival = FOLLOWCAM_POSITION_THRESHOLD; return (INTEGER_CONSTANT); }
+"CAMERA_FOCUS_THRESHOLD" { count(); yylval.ival = FOLLOWCAM_FOCUS_THRESHOLD; return (INTEGER_CONSTANT); }
+"CAMERA_ACTIVE" { count(); yylval.ival = FOLLOWCAM_ACTIVE; return (INTEGER_CONSTANT); }
+"CAMERA_POSITION" { count(); yylval.ival = FOLLOWCAM_POSITION; return (INTEGER_CONSTANT); }
+"CAMERA_FOCUS" { count(); yylval.ival = FOLLOWCAM_FOCUS; return (INTEGER_CONSTANT); }
+"CAMERA_POSITION_LOCKED" { count(); yylval.ival = FOLLOWCAM_POSITION_LOCKED; return (INTEGER_CONSTANT); }
+"CAMERA_FOCUS_LOCKED" { count(); yylval.ival = FOLLOWCAM_FOCUS_LOCKED; return (INTEGER_CONSTANT); }
+
+"ANIM_ON" { count(); yylval.ival = 0x1; return(INTEGER_CONSTANT); }
+"LOOP" { count(); yylval.ival = 0x2; return(INTEGER_CONSTANT); }
+"REVERSE" { count(); yylval.ival = 0x4; return(INTEGER_CONSTANT); }
+"PING_PONG" { count(); yylval.ival = 0x8; return(INTEGER_CONSTANT); }
+"SMOOTH" { count(); yylval.ival = 0x10; return(INTEGER_CONSTANT); }
+"ROTATE" { count(); yylval.ival = 0x20; return(INTEGER_CONSTANT); }
+"SCALE" { count(); yylval.ival = 0x40; return(INTEGER_CONSTANT); }
+
+"ALL_SIDES" { count(); yylval.ival = LSL_ALL_SIDES; return(INTEGER_CONSTANT); }
+"LINK_ROOT" { count(); yylval.ival = LSL_LINK_ROOT; return(INTEGER_CONSTANT); }
+"LINK_SET" { count(); yylval.ival = LSL_LINK_SET; return(INTEGER_CONSTANT); }
+"LINK_ALL_OTHERS" { count(); yylval.ival = LSL_LINK_ALL_OTHERS; return(INTEGER_CONSTANT); }
+"LINK_ALL_CHILDREN" { count(); yylval.ival = LSL_LINK_ALL_CHILDREN; return(INTEGER_CONSTANT); }
+"LINK_THIS" { count(); yylval.ival = LSL_LINK_THIS; return(INTEGER_CONSTANT); }
+
+"AGENT" { count(); yylval.ival = 0x1; return(INTEGER_CONSTANT); }
+"ACTIVE" { count(); yylval.ival = 0x2; return(INTEGER_CONSTANT); }
+"PASSIVE" { count(); yylval.ival = 0x4; return(INTEGER_CONSTANT); }
+"SCRIPTED" { count(); yylval.ival = 0x8; return(INTEGER_CONSTANT); }
+
+"CONTROL_FWD" { count(); yylval.ival = AGENT_CONTROL_AT_POS; return(INTEGER_CONSTANT); }
+"CONTROL_BACK" { count(); yylval.ival = AGENT_CONTROL_AT_NEG; return(INTEGER_CONSTANT); }
+"CONTROL_LEFT" { count(); yylval.ival = AGENT_CONTROL_LEFT_POS; return(INTEGER_CONSTANT); }
+"CONTROL_RIGHT" { count(); yylval.ival = AGENT_CONTROL_LEFT_NEG; return(INTEGER_CONSTANT); }
+"CONTROL_ROT_LEFT" { count(); yylval.ival = AGENT_CONTROL_YAW_POS; return(INTEGER_CONSTANT); }
+"CONTROL_ROT_RIGHT" { count(); yylval.ival = AGENT_CONTROL_YAW_NEG; return(INTEGER_CONSTANT); }
+"CONTROL_UP" { count(); yylval.ival = AGENT_CONTROL_UP_POS; return(INTEGER_CONSTANT); }
+"CONTROL_DOWN" { count(); yylval.ival = AGENT_CONTROL_UP_NEG; return(INTEGER_CONSTANT); }
+"CONTROL_LBUTTON" { count(); yylval.ival = AGENT_CONTROL_LBUTTON_DOWN; return(INTEGER_CONSTANT); }
+"CONTROL_ML_LBUTTON" { count(); yylval.ival = AGENT_CONTROL_ML_LBUTTON_DOWN; return(INTEGER_CONSTANT); }
+
+"PERMISSION_DEBIT" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_DEBIT]; return(INTEGER_CONSTANT); }
+"PERMISSION_TAKE_CONTROLS" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TAKE_CONTROLS]; return(INTEGER_CONSTANT); }
+"PERMISSION_REMAP_CONTROLS" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_REMAP_CONTROLS]; return(INTEGER_CONSTANT); }
+"PERMISSION_TRIGGER_ANIMATION" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TRIGGER_ANIMATION]; return(INTEGER_CONSTANT); }
+"PERMISSION_ATTACH" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_ATTACH]; return(INTEGER_CONSTANT); }
+"PERMISSION_RELEASE_OWNERSHIP" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_RELEASE_OWNERSHIP]; return(INTEGER_CONSTANT); }
+"PERMISSION_CHANGE_LINKS" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_LINKS]; return(INTEGER_CONSTANT); }
+"PERMISSION_CHANGE_JOINTS" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_JOINTS]; return(INTEGER_CONSTANT); }
+"PERMISSION_CHANGE_PERMISSIONS" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CHANGE_PERMISSIONS]; return(INTEGER_CONSTANT); }
+"PERMISSION_TRACK_CAMERA" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_TRACK_CAMERA]; return(INTEGER_CONSTANT); }
+"PERMISSION_CONTROL_CAMERA" { count(); yylval.ival = LSCRIPTRunTimePermissionBits[SCRIPT_PERMISSION_CONTROL_CAMERA]; return(INTEGER_CONSTANT); }
+
+"INVENTORY_TEXTURE" { count(); yylval.ival = LLAssetType::AT_TEXTURE; return(INTEGER_CONSTANT); }
+"INVENTORY_SOUND" { count(); yylval.ival = LLAssetType::AT_SOUND; return(INTEGER_CONSTANT); }
+"INVENTORY_OBJECT" { count(); yylval.ival = LLAssetType::AT_OBJECT; return(INTEGER_CONSTANT); }
+"INVENTORY_SCRIPT" { count(); yylval.ival = LLAssetType::AT_LSL_TEXT; return(INTEGER_CONSTANT); }
+"INVENTORY_LANDMARK" { count(); yylval.ival = LLAssetType::AT_LANDMARK; return(INTEGER_CONSTANT); }
+"INVENTORY_CLOTHING" { count(); yylval.ival = LLAssetType::AT_CLOTHING; return(INTEGER_CONSTANT); }
+"INVENTORY_NOTECARD" { count(); yylval.ival = LLAssetType::AT_NOTECARD; return(INTEGER_CONSTANT); }
+"INVENTORY_BODYPART" { count(); yylval.ival = LLAssetType::AT_BODYPART; return(INTEGER_CONSTANT); }
+"INVENTORY_ANIMATION" { count(); yylval.ival = LLAssetType::AT_ANIMATION; return(INTEGER_CONSTANT); }
+"INVENTORY_GESTURE" { count(); yylval.ival = LLAssetType::AT_GESTURE; return(INTEGER_CONSTANT); }
+"INVENTORY_ALL" { count(); yylval.ival = LLAssetType::AT_NONE; return(INTEGER_CONSTANT); }
+"INVENTORY_NONE" { count(); yylval.ival = LLAssetType::AT_NONE; return(INTEGER_CONSTANT); }
+
+"CHANGED_INVENTORY" { count(); yylval.ival = 0x1; return(INTEGER_CONSTANT); }
+"CHANGED_COLOR" { count(); yylval.ival = 0x2; return(INTEGER_CONSTANT); }
+"CHANGED_SHAPE" { count(); yylval.ival = 0x4; return(INTEGER_CONSTANT); }
+"CHANGED_SCALE" { count(); yylval.ival = 0x8; return(INTEGER_CONSTANT); }
+"CHANGED_TEXTURE" { count(); yylval.ival = 0x10; return(INTEGER_CONSTANT); }
+"CHANGED_LINK" { count(); yylval.ival = 0x20; return(INTEGER_CONSTANT); }
+"CHANGED_ALLOWED_DROP" { count(); yylval.ival = 0x40; return(INTEGER_CONSTANT); }
+"CHANGED_OWNER" { count(); yylval.ival = 0x80; return(INTEGER_CONSTANT); }
+"CHANGED_REGION" { count(); yylval.ival = 0x100; return(INTEGER_CONSTANT); }
+"CHANGED_TELEPORT" { count(); yylval.ival = 0x200; return(INTEGER_CONSTANT); }
+
+"TYPE_INTEGER" { count(); yylval.ival = LST_INTEGER; return(INTEGER_CONSTANT); }
+"TYPE_FLOAT" { count(); yylval.ival = LST_FLOATINGPOINT; return(INTEGER_CONSTANT); }
+"TYPE_STRING" { count(); yylval.ival = LST_STRING; return(INTEGER_CONSTANT); }
+"TYPE_KEY" { count(); yylval.ival = LST_KEY; return(INTEGER_CONSTANT); }
+"TYPE_VECTOR" { count(); yylval.ival = LST_VECTOR; return(INTEGER_CONSTANT); }
+"TYPE_ROTATION" { count(); yylval.ival = LST_QUATERNION; return(INTEGER_CONSTANT); }
+"TYPE_INVALID" { count(); yylval.ival = LST_NULL; return(INTEGER_CONSTANT); }
+
+"NULL_KEY" { yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "00000000-0000-0000-0000-000000000000"); return(STRING_CONSTANT); }
+"EOF" { yylval.sval = new char[UUID_STR_LENGTH]; strcpy(yylval.sval, "\n\n\n"); return(STRING_CONSTANT); }
+
+"PI" { count(); yylval.fval = F_PI; return(FP_CONSTANT); }
+"TWO_PI" { count(); yylval.fval = F_TWO_PI; return(FP_CONSTANT); }
+"PI_BY_TWO" { count(); yylval.fval = F_PI_BY_TWO; return(FP_CONSTANT); }
+"DEG_TO_RAD" { count(); yylval.fval = DEG_TO_RAD; return(FP_CONSTANT); }
+"RAD_TO_DEG" { count(); yylval.fval = RAD_TO_DEG; return(FP_CONSTANT); }
+"SQRT2" { count(); yylval.fval = F_SQRT2; return(FP_CONSTANT); }
+
+"DEBUG_CHANNEL" { count(); yylval.ival = CHAT_CHANNEL_DEBUG; return(INTEGER_CONSTANT); }
+"PUBLIC_CHANNEL" { count(); yylval.ival = 0; return(INTEGER_CONSTANT); }
+
+"ZERO_VECTOR" { count(); return(ZERO_VECTOR); }
+"ZERO_ROTATION" { count(); return(ZERO_ROTATION); }
+
+"ATTACH_CHEST" { count(); yylval.ival = 1; return(INTEGER_CONSTANT); }
+"ATTACH_HEAD" { count(); yylval.ival = 2; return(INTEGER_CONSTANT); }
+"ATTACH_LSHOULDER" { count(); yylval.ival = 3; return(INTEGER_CONSTANT); }
+"ATTACH_RSHOULDER" { count(); yylval.ival = 4; return(INTEGER_CONSTANT); }
+"ATTACH_LHAND" { count(); yylval.ival = 5; return(INTEGER_CONSTANT); }
+"ATTACH_RHAND" { count(); yylval.ival = 6; return(INTEGER_CONSTANT); }
+"ATTACH_LFOOT" { count(); yylval.ival = 7; return(INTEGER_CONSTANT); }
+"ATTACH_RFOOT" { count(); yylval.ival = 8; return(INTEGER_CONSTANT); }
+"ATTACH_BACK" { count(); yylval.ival = 9; return(INTEGER_CONSTANT); }
+"ATTACH_PELVIS" { count(); yylval.ival = 10; return(INTEGER_CONSTANT); }
+"ATTACH_MOUTH" { count(); yylval.ival = 11; return(INTEGER_CONSTANT); }
+"ATTACH_CHIN" { count(); yylval.ival = 12; return(INTEGER_CONSTANT); }
+"ATTACH_LEAR" { count(); yylval.ival = 13; return(INTEGER_CONSTANT); }
+"ATTACH_REAR" { count(); yylval.ival = 14; return(INTEGER_CONSTANT); }
+"ATTACH_LEYE" { count(); yylval.ival = 15; return(INTEGER_CONSTANT); }
+"ATTACH_REYE" { count(); yylval.ival = 16; return(INTEGER_CONSTANT); }
+"ATTACH_NOSE" { count(); yylval.ival = 17; return(INTEGER_CONSTANT); }
+"ATTACH_RUARM" { count(); yylval.ival = 18; return(INTEGER_CONSTANT); }
+"ATTACH_RLARM" { count(); yylval.ival = 19; return(INTEGER_CONSTANT); }
+"ATTACH_LUARM" { count(); yylval.ival = 20; return(INTEGER_CONSTANT); }
+"ATTACH_LLARM" { count(); yylval.ival = 21; return(INTEGER_CONSTANT); }
+"ATTACH_RHIP" { count(); yylval.ival = 22; return(INTEGER_CONSTANT); }
+"ATTACH_RULEG" { count(); yylval.ival = 23; return(INTEGER_CONSTANT); }
+"ATTACH_RLLEG" { count(); yylval.ival = 24; return(INTEGER_CONSTANT); }
+"ATTACH_LHIP" { count(); yylval.ival = 25; return(INTEGER_CONSTANT); }
+"ATTACH_LULEG" { count(); yylval.ival = 26; return(INTEGER_CONSTANT); }
+"ATTACH_LLLEG" { count(); yylval.ival = 27; return(INTEGER_CONSTANT); }
+"ATTACH_BELLY" { count(); yylval.ival = 28; return(INTEGER_CONSTANT); }
+"ATTACH_RPEC" { count(); yylval.ival = 29; return(INTEGER_CONSTANT); }
+"ATTACH_LPEC" { count(); yylval.ival = 30; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_CENTER_2" { count(); yylval.ival = 31; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_TOP_RIGHT" { count(); yylval.ival = 32; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_TOP_CENTER" { count(); yylval.ival = 33; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_TOP_LEFT" { count(); yylval.ival = 34; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_CENTER_1" { count(); yylval.ival = 35; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_BOTTOM_LEFT" { count(); yylval.ival = 36; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_BOTTOM" { count(); yylval.ival = 37; return(INTEGER_CONSTANT); }
+"ATTACH_HUD_BOTTOM_RIGHT" { count(); yylval.ival = 38; return(INTEGER_CONSTANT); }
+
+"LAND_LEVEL" { count(); yylval.ival = E_LANDBRUSH_LEVEL; return(INTEGER_CONSTANT); }
+"LAND_RAISE" { count(); yylval.ival = E_LANDBRUSH_RAISE; return(INTEGER_CONSTANT); }
+"LAND_LOWER" { count(); yylval.ival = E_LANDBRUSH_LOWER; return(INTEGER_CONSTANT); }
+"LAND_SMOOTH" { count(); yylval.ival = E_LANDBRUSH_SMOOTH; return(INTEGER_CONSTANT); }
+"LAND_NOISE" { count(); yylval.ival = E_LANDBRUSH_NOISE; return(INTEGER_CONSTANT); }
+"LAND_REVERT" { count(); yylval.ival = E_LANDBRUSH_REVERT; return(INTEGER_CONSTANT); }
+
+"LAND_SMALL_BRUSH" { count(); yylval.ival = 1; return(INTEGER_CONSTANT); }
+"LAND_MEDIUM_BRUSH" { count(); yylval.ival = 2; return(INTEGER_CONSTANT); }
+"LAND_LARGE_BRUSH" { count(); yylval.ival = 3; return(INTEGER_CONSTANT); }
+
+"DATA_ONLINE" { count(); yylval.ival = 1; return(INTEGER_CONSTANT); }
+"DATA_NAME" { count(); yylval.ival = 2; return(INTEGER_CONSTANT); }
+"DATA_BORN" { count(); yylval.ival = 3; return(INTEGER_CONSTANT); }
+"DATA_RATING" { count(); yylval.ival = 4; return(INTEGER_CONSTANT); }
+"DATA_SIM_POS" { count(); yylval.ival = 5; return(INTEGER_CONSTANT); }
+"DATA_SIM_STATUS" { count(); yylval.ival = 6; return(INTEGER_CONSTANT); }
+"DATA_SIM_RATING" { count(); yylval.ival = 7; return(INTEGER_CONSTANT); }
+"DATA_PAYINFO" { count(); yylval.ival = 8; return(INTEGER_CONSTANT); }
+
+"PAYMENT_INFO_ON_FILE" { count(); yylval.ival = 1; return(INTEGER_CONSTANT); }
+"PAYMENT_INFO_USED" { count(); yylval.ival = 2; return(INTEGER_CONSTANT); }
+
+"REMOTE_DATA_CHANNEL" { count(); yylval.ival = LSL_REMOTE_DATA_CHANNEL; return(INTEGER_CONSTANT); }
+"REMOTE_DATA_REQUEST" { count(); yylval.ival = LSL_REMOTE_DATA_REQUEST; return(INTEGER_CONSTANT); }
+"REMOTE_DATA_REPLY" { count(); yylval.ival = LSL_REMOTE_DATA_REPLY; return(INTEGER_CONSTANT); }
+
+
+"PSYS_PART_FLAGS" { count(); yylval.ival = LLPS_PART_FLAGS; return(INTEGER_CONSTANT); }
+"PSYS_PART_START_COLOR" { count(); yylval.ival = LLPS_PART_START_COLOR; return (INTEGER_CONSTANT); }
+"PSYS_PART_START_ALPHA" { count(); yylval.ival = LLPS_PART_START_ALPHA; return (INTEGER_CONSTANT); }
+"PSYS_PART_START_SCALE" { count(); yylval.ival = LLPS_PART_START_SCALE; return (INTEGER_CONSTANT); }
+"PSYS_PART_END_COLOR" { count(); yylval.ival = LLPS_PART_END_COLOR; return (INTEGER_CONSTANT); }
+"PSYS_PART_END_ALPHA" { count(); yylval.ival = LLPS_PART_END_ALPHA; return (INTEGER_CONSTANT); }
+"PSYS_PART_END_SCALE" { count(); yylval.ival = LLPS_PART_END_SCALE; return (INTEGER_CONSTANT); }
+"PSYS_PART_MAX_AGE" { count(); yylval.ival = LLPS_PART_MAX_AGE; return (INTEGER_CONSTANT); }
+
+
+"PSYS_PART_WIND_MASK" { count(); yylval.ival = LLPartData::LL_PART_WIND_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_INTERP_COLOR_MASK" { count(); yylval.ival = LLPartData::LL_PART_INTERP_COLOR_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_INTERP_SCALE_MASK" { count(); yylval.ival = LLPartData::LL_PART_INTERP_SCALE_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_BOUNCE_MASK" { count(); yylval.ival = LLPartData::LL_PART_BOUNCE_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_FOLLOW_SRC_MASK" { count(); yylval.ival = LLPartData::LL_PART_FOLLOW_SRC_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_FOLLOW_VELOCITY_MASK" { count(); yylval.ival = LLPartData::LL_PART_FOLLOW_VELOCITY_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_TARGET_POS_MASK" { count(); yylval.ival = LLPartData::LL_PART_TARGET_POS_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_EMISSIVE_MASK" { count(); yylval.ival = LLPartData::LL_PART_EMISSIVE_MASK; return(INTEGER_CONSTANT); }
+"PSYS_PART_TARGET_LINEAR_MASK" { count(); yylval.ival = LLPartData::LL_PART_TARGET_LINEAR_MASK; return(INTEGER_CONSTANT); }
+
+
+"PSYS_SRC_MAX_AGE" { count(); yylval.ival = LLPS_SRC_MAX_AGE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_PATTERN" { count(); yylval.ival = LLPS_SRC_PATTERN; return(INTEGER_CONSTANT); }
+"PSYS_SRC_INNERANGLE" { count(); yylval.ival = LLPS_SRC_INNERANGLE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_OUTERANGLE" { count(); yylval.ival = LLPS_SRC_OUTERANGLE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_ANGLE_BEGIN" { count(); yylval.ival = LLPS_SRC_ANGLE_BEGIN; return(INTEGER_CONSTANT); }
+"PSYS_SRC_ANGLE_END" { count(); yylval.ival = LLPS_SRC_ANGLE_END; return(INTEGER_CONSTANT); }
+"PSYS_SRC_BURST_RATE" { count(); yylval.ival = LLPS_SRC_BURST_RATE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_BURST_PART_COUNT" { count(); yylval.ival = LLPS_SRC_BURST_PART_COUNT; return(INTEGER_CONSTANT); }
+"PSYS_SRC_BURST_RADIUS" { count(); yylval.ival = LLPS_SRC_BURST_RADIUS; return(INTEGER_CONSTANT); }
+"PSYS_SRC_BURST_SPEED_MIN" { count(); yylval.ival = LLPS_SRC_BURST_SPEED_MIN; return(INTEGER_CONSTANT); }
+"PSYS_SRC_BURST_SPEED_MAX" { count(); yylval.ival = LLPS_SRC_BURST_SPEED_MAX; return(INTEGER_CONSTANT); }
+"PSYS_SRC_ACCEL" { count(); yylval.ival = LLPS_SRC_ACCEL; return(INTEGER_CONSTANT); }
+"PSYS_SRC_TEXTURE" { count(); yylval.ival = LLPS_SRC_TEXTURE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_TARGET_KEY" { count(); yylval.ival = LLPS_SRC_TARGET_UUID; return(INTEGER_CONSTANT); }
+"PSYS_SRC_OMEGA" { count(); yylval.ival = LLPS_SRC_OMEGA; return(INTEGER_CONSTANT); }
+
+"PSYS_SRC_OBJ_REL_MASK" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_OBJ_REL_MASK; return(INTEGER_CONSTANT); }
+
+"PSYS_SRC_PATTERN_DROP" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_DROP; return(INTEGER_CONSTANT); }
+"PSYS_SRC_PATTERN_EXPLODE" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_PATTERN_ANGLE" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_PATTERN_ANGLE_CONE" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE; return(INTEGER_CONSTANT); }
+"PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY" { count(); yylval.ival = LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE_EMPTY; return(INTEGER_CONSTANT); }
+
+
+"VEHICLE_TYPE_NONE" { count(); yylval.ival = VEHICLE_TYPE_NONE; return(INTEGER_CONSTANT); }
+"VEHICLE_TYPE_SLED" { count(); yylval.ival = VEHICLE_TYPE_SLED; return(INTEGER_CONSTANT); }
+"VEHICLE_TYPE_CAR" { count(); yylval.ival = VEHICLE_TYPE_CAR; return(INTEGER_CONSTANT); }
+"VEHICLE_TYPE_BOAT" { count(); yylval.ival = VEHICLE_TYPE_BOAT; return(INTEGER_CONSTANT); }
+"VEHICLE_TYPE_AIRPLANE" { count(); yylval.ival = VEHICLE_TYPE_AIRPLANE; return(INTEGER_CONSTANT); }
+"VEHICLE_TYPE_BALLOON" { count(); yylval.ival = VEHICLE_TYPE_BALLOON; return(INTEGER_CONSTANT); }
+
+"VEHICLE_REFERENCE_FRAME" { count(); yylval.ival = VEHICLE_REFERENCE_FRAME; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_FRICTION_TIMESCALE" { count(); yylval.ival = VEHICLE_LINEAR_FRICTION_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_ANGULAR_FRICTION_TIMESCALE" { count(); yylval.ival = VEHICLE_ANGULAR_FRICTION_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_MOTOR_DIRECTION" { count(); yylval.ival = VEHICLE_LINEAR_MOTOR_DIRECTION; return(INTEGER_CONSTANT); }
+"VEHICLE_ANGULAR_MOTOR_DIRECTION" { count(); yylval.ival = VEHICLE_ANGULAR_MOTOR_DIRECTION; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_MOTOR_OFFSET" { count(); yylval.ival = VEHICLE_LINEAR_MOTOR_OFFSET; return(INTEGER_CONSTANT); }
+
+
+
+"VEHICLE_HOVER_HEIGHT" { count(); yylval.ival = VEHICLE_HOVER_HEIGHT; return(INTEGER_CONSTANT); }
+"VEHICLE_HOVER_EFFICIENCY" { count(); yylval.ival = VEHICLE_HOVER_EFFICIENCY; return(INTEGER_CONSTANT); }
+"VEHICLE_HOVER_TIMESCALE" { count(); yylval.ival = VEHICLE_HOVER_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_BUOYANCY" { count(); yylval.ival = VEHICLE_BUOYANCY; return(INTEGER_CONSTANT); }
+
+"VEHICLE_LINEAR_DEFLECTION_EFFICIENCY" { count(); yylval.ival = VEHICLE_LINEAR_DEFLECTION_EFFICIENCY; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_DEFLECTION_TIMESCALE" { count(); yylval.ival = VEHICLE_LINEAR_DEFLECTION_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_MOTOR_TIMESCALE" { count(); yylval.ival = VEHICLE_LINEAR_MOTOR_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE" { count(); yylval.ival = VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE; return(INTEGER_CONSTANT); }
+
+"VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY" { count(); yylval.ival = VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY; return(INTEGER_CONSTANT); }
+"VEHICLE_ANGULAR_DEFLECTION_TIMESCALE" { count(); yylval.ival = VEHICLE_ANGULAR_DEFLECTION_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_ANGULAR_MOTOR_TIMESCALE" { count(); yylval.ival = VEHICLE_ANGULAR_MOTOR_TIMESCALE; return(INTEGER_CONSTANT); }
+"VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE" { count(); yylval.ival = VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE; return(INTEGER_CONSTANT); }
+
+"VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY" { count(); yylval.ival = VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY; return(INTEGER_CONSTANT); }
+"VEHICLE_VERTICAL_ATTRACTION_TIMESCALE" { count(); yylval.ival = VEHICLE_VERTICAL_ATTRACTION_TIMESCALE; return(INTEGER_CONSTANT); }
+
+"VEHICLE_BANKING_EFFICIENCY" { count(); yylval.ival = VEHICLE_BANKING_EFFICIENCY; return(INTEGER_CONSTANT); }
+"VEHICLE_BANKING_MIX" { count(); yylval.ival = VEHICLE_BANKING_MIX; return(INTEGER_CONSTANT); }
+"VEHICLE_BANKING_TIMESCALE" { count(); yylval.ival = VEHICLE_BANKING_TIMESCALE; return(INTEGER_CONSTANT); }
+
+"VEHICLE_FLAG_NO_FLY_UP" { count(); yylval.ival = VEHICLE_FLAG_NO_DEFLECTION_UP; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_NO_DEFLECTION_UP" { count(); yylval.ival = VEHICLE_FLAG_NO_DEFLECTION_UP; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_LIMIT_ROLL_ONLY" { count(); yylval.ival = VEHICLE_FLAG_LIMIT_ROLL_ONLY; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_HOVER_WATER_ONLY" { count(); yylval.ival = VEHICLE_FLAG_HOVER_WATER_ONLY; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_HOVER_TERRAIN_ONLY" { count(); yylval.ival = VEHICLE_FLAG_HOVER_TERRAIN_ONLY; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT" { count(); yylval.ival = VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_HOVER_UP_ONLY" { count(); yylval.ival = VEHICLE_FLAG_HOVER_UP_ONLY; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_LIMIT_MOTOR_UP" { count(); yylval.ival = VEHICLE_FLAG_LIMIT_MOTOR_UP; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_MOUSELOOK_STEER" { count(); yylval.ival = VEHICLE_FLAG_MOUSELOOK_STEER; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_MOUSELOOK_BANK" { count(); yylval.ival = VEHICLE_FLAG_MOUSELOOK_BANK; return(INTEGER_CONSTANT); }
+"VEHICLE_FLAG_CAMERA_DECOUPLED" { count(); yylval.ival = VEHICLE_FLAG_CAMERA_DECOUPLED; return(INTEGER_CONSTANT); }
+
+
+
+"PRIM_TYPE" { count(); yylval.ival = LSL_PRIM_TYPE; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL" { count(); yylval.ival = LSL_PRIM_MATERIAL; return(INTEGER_CONSTANT); }
+"PRIM_PHYSICS" { count(); yylval.ival = LSL_PRIM_PHYSICS; return(INTEGER_CONSTANT); }
+"PRIM_FLEXIBLE" { count(); yylval.ival = LSL_PRIM_FLEXIBLE; return(INTEGER_CONSTANT); }
+"PRIM_POINT_LIGHT" { count(); yylval.ival = LSL_PRIM_POINT_LIGHT; return(INTEGER_CONSTANT); }
+"PRIM_TEMP_ON_REZ" { count(); yylval.ival = LSL_PRIM_TEMP_ON_REZ; return(INTEGER_CONSTANT); }
+"PRIM_PHANTOM" { count(); yylval.ival = LSL_PRIM_PHANTOM; return(INTEGER_CONSTANT); }
+"PRIM_CAST_SHADOWS" { count(); yylval.ival = LSL_PRIM_CAST_SHADOWS; return(INTEGER_CONSTANT); }
+"PRIM_POSITION" { count(); yylval.ival = LSL_PRIM_POSITION; return(INTEGER_CONSTANT); }
+"PRIM_SIZE" { count(); yylval.ival = LSL_PRIM_SIZE; return(INTEGER_CONSTANT); }
+"PRIM_ROTATION" { count(); yylval.ival = LSL_PRIM_ROTATION; return(INTEGER_CONSTANT); }
+"PRIM_TEXTURE" { count(); yylval.ival = LSL_PRIM_TEXTURE; return(INTEGER_CONSTANT); }
+"PRIM_COLOR" { count(); yylval.ival = LSL_PRIM_COLOR; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_SHINY" { count(); yylval.ival = LSL_PRIM_BUMP_SHINY; return(INTEGER_CONSTANT); }
+"PRIM_FULLBRIGHT" { count(); yylval.ival = LSL_PRIM_FULLBRIGHT; return(INTEGER_CONSTANT); }
+"PRIM_TEXGEN" { count(); yylval.ival = LSL_PRIM_TEXGEN; return(INTEGER_CONSTANT); }
+
+"PRIM_TYPE_BOX" { count(); yylval.ival = LSL_PRIM_TYPE_BOX; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_CYLINDER" { count(); yylval.ival = LSL_PRIM_TYPE_CYLINDER; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_PRISM" { count(); yylval.ival = LSL_PRIM_TYPE_PRISM; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_SPHERE" { count(); yylval.ival = LSL_PRIM_TYPE_SPHERE; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_TORUS" { count(); yylval.ival = LSL_PRIM_TYPE_TORUS; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_TUBE" { count(); yylval.ival = LSL_PRIM_TYPE_TUBE; return(INTEGER_CONSTANT); }
+"PRIM_TYPE_RING" { count(); yylval.ival = LSL_PRIM_TYPE_RING; return(INTEGER_CONSTANT); }
+
+"PRIM_HOLE_DEFAULT" { count(); yylval.ival = LSL_PRIM_HOLE_DEFAULT; return(INTEGER_CONSTANT); }
+"PRIM_HOLE_CIRCLE" { count(); yylval.ival = LSL_PRIM_HOLE_CIRCLE; return(INTEGER_CONSTANT); }
+"PRIM_HOLE_SQUARE" { count(); yylval.ival = LSL_PRIM_HOLE_SQUARE; return(INTEGER_CONSTANT); }
+"PRIM_HOLE_TRIANGLE" { count(); yylval.ival = LSL_PRIM_HOLE_TRIANGLE; return(INTEGER_CONSTANT); }
+
+"PRIM_MATERIAL_STONE" { count(); yylval.ival = LSL_PRIM_MATERIAL_STONE; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_METAL" { count(); yylval.ival = LSL_PRIM_MATERIAL_METAL; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_GLASS" { count(); yylval.ival = LSL_PRIM_MATERIAL_GLASS; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_WOOD" { count(); yylval.ival = LSL_PRIM_MATERIAL_WOOD; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_FLESH" { count(); yylval.ival = LSL_PRIM_MATERIAL_FLESH; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_PLASTIC" { count(); yylval.ival = LSL_PRIM_MATERIAL_PLASTIC; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_RUBBER" { count(); yylval.ival = LSL_PRIM_MATERIAL_RUBBER; return(INTEGER_CONSTANT); }
+"PRIM_MATERIAL_LIGHT" { count(); yylval.ival = LSL_PRIM_MATERIAL_LIGHT; return(INTEGER_CONSTANT); }
+
+"PRIM_SHINY_NONE" { count(); yylval.ival = LSL_PRIM_SHINY_NONE; return(INTEGER_CONSTANT); }
+"PRIM_SHINY_LOW" { count(); yylval.ival = LSL_PRIM_SHINY_LOW; return(INTEGER_CONSTANT); }
+"PRIM_SHINY_MEDIUM" { count(); yylval.ival = LSL_PRIM_SHINY_MEDIUM; return(INTEGER_CONSTANT); }
+"PRIM_SHINY_HIGH" { count(); yylval.ival = LSL_PRIM_SHINY_HIGH; return(INTEGER_CONSTANT); }
+
+"PRIM_BUMP_NONE" { count(); yylval.ival = LSL_PRIM_BUMP_NONE; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_BRIGHT" { count(); yylval.ival = LSL_PRIM_BUMP_BRIGHT; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_DARK" { count(); yylval.ival = LSL_PRIM_BUMP_DARK; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_WOOD" { count(); yylval.ival = LSL_PRIM_BUMP_WOOD; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_BARK" { count(); yylval.ival = LSL_PRIM_BUMP_BARK; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_BRICKS" { count(); yylval.ival = LSL_PRIM_BUMP_BRICKS; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_CHECKER" { count(); yylval.ival = LSL_PRIM_BUMP_CHECKER; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_CONCRETE" { count(); yylval.ival = LSL_PRIM_BUMP_CONCRETE; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_TILE" { count(); yylval.ival = LSL_PRIM_BUMP_TILE; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_STONE" { count(); yylval.ival = LSL_PRIM_BUMP_STONE; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_DISKS" { count(); yylval.ival = LSL_PRIM_BUMP_DISKS; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_GRAVEL" { count(); yylval.ival = LSL_PRIM_BUMP_GRAVEL; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_BLOBS" { count(); yylval.ival = LSL_PRIM_BUMP_BLOBS; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_SIDING" { count(); yylval.ival = LSL_PRIM_BUMP_SIDING; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_LARGETILE" { count(); yylval.ival = LSL_PRIM_BUMP_LARGETILE; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_STUCCO" { count(); yylval.ival = LSL_PRIM_BUMP_STUCCO; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_SUCTION" { count(); yylval.ival = LSL_PRIM_BUMP_SUCTION; return(INTEGER_CONSTANT); }
+"PRIM_BUMP_WEAVE" { count(); yylval.ival = LSL_PRIM_BUMP_WEAVE; return(INTEGER_CONSTANT); }
+
+"PRIM_TEXGEN_DEFAULT" { count(); yylval.ival = LSL_PRIM_TEXGEN_DEFAULT; return(INTEGER_CONSTANT); }
+"PRIM_TEXGEN_PLANAR" { count(); yylval.ival = LSL_PRIM_TEXGEN_PLANAR; return(INTEGER_CONSTANT); }
+
+"MASK_BASE" { count(); yylval.ival = 0; return(INTEGER_CONSTANT); }
+"MASK_OWNER" { count(); yylval.ival = 1; return(INTEGER_CONSTANT); }
+"MASK_GROUP" { count(); yylval.ival = 2; return(INTEGER_CONSTANT); }
+"MASK_EVERYONE" { count(); yylval.ival = 3; return(INTEGER_CONSTANT); }
+"MASK_NEXT" { count(); yylval.ival = 4; return(INTEGER_CONSTANT); }
+
+"PERM_TRANSFER" { count(); yylval.ival = PERM_TRANSFER; return(INTEGER_CONSTANT); }
+"PERM_MODIFY" { count(); yylval.ival = PERM_MODIFY; return(INTEGER_CONSTANT); }
+"PERM_COPY" { count(); yylval.ival = PERM_COPY; return(INTEGER_CONSTANT); }
+"PERM_MOVE" { count(); yylval.ival = PERM_MOVE; return(INTEGER_CONSTANT); }
+"PERM_ALL" { count(); yylval.ival = PERM_ALL; return(INTEGER_CONSTANT); }
+
+"PARCEL_MEDIA_COMMAND_STOP" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_STOP; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_PAUSE" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_PAUSE; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_PLAY" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_PLAY; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_LOOP" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_LOOP; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_TEXTURE" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_TEXTURE; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_URL" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_URL; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_TIME" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_TIME; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_AGENT" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_AGENT; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_UNLOAD" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_UNLOAD; return(INTEGER_CONSTANT); }
+"PARCEL_MEDIA_COMMAND_AUTO_ALIGN" { count(); yylval.ival = PARCEL_MEDIA_COMMAND_AUTO_ALIGN; return(INTEGER_CONSTANT); }
+
+"LIST_STAT_MAX" { count(); yylval.ival = LIST_STAT_MAX; return(INTEGER_CONSTANT); }
+"LIST_STAT_MIN" { count(); yylval.ival = LIST_STAT_MIN; return(INTEGER_CONSTANT); }
+"LIST_STAT_MEAN" { count(); yylval.ival = LIST_STAT_MEAN; return(INTEGER_CONSTANT); }
+"LIST_STAT_MEDIAN" { count(); yylval.ival = LIST_STAT_MEDIAN; return(INTEGER_CONSTANT); }
+"LIST_STAT_STD_DEV" { count(); yylval.ival = LIST_STAT_STD_DEV; return(INTEGER_CONSTANT); }
+"LIST_STAT_SUM" { count(); yylval.ival = LIST_STAT_SUM; return(INTEGER_CONSTANT); }
+"LIST_STAT_SUM_SQUARES" { count(); yylval.ival = LIST_STAT_SUM_SQUARES; return(INTEGER_CONSTANT); }
+"LIST_STAT_NUM_COUNT" { count(); yylval.ival = LIST_STAT_NUM_COUNT; return(INTEGER_CONSTANT); }
+"LIST_STAT_GEOMETRIC_MEAN" { count(); yylval.ival = LIST_STAT_GEO_MEAN; return(INTEGER_CONSTANT); }
+"LIST_STAT_RANGE" { count(); yylval.ival = LIST_STAT_RANGE; return(INTEGER_CONSTANT); }
+
+"PAY_HIDE" { count(); yylval.ival = PAY_PRICE_HIDE; return(INTEGER_CONSTANT); }
+"PAY_DEFAULT" { count(); yylval.ival = PAY_PRICE_DEFAULT; return(INTEGER_CONSTANT); }
+
+"PARCEL_FLAG_ALLOW_FLY" { count(); yylval.ival = PF_ALLOW_FLY; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_GROUP_SCRIPTS" { count(); yylval.ival = PF_ALLOW_GROUP_SCRIPTS; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_SCRIPTS" { count(); yylval.ival = PF_ALLOW_OTHER_SCRIPTS; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_LANDMARK" { count(); yylval.ival = PF_ALLOW_LANDMARK; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_TERRAFORM" { count(); yylval.ival = PF_ALLOW_TERRAFORM; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_DAMAGE" { count(); yylval.ival = PF_ALLOW_DAMAGE; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_CREATE_OBJECTS" { count(); yylval.ival = PF_CREATE_OBJECTS; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_CREATE_GROUP_OBJECTS" { count(); yylval.ival = PF_CREATE_GROUP_OBJECTS; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_USE_ACCESS_GROUP" { count(); yylval.ival = PF_USE_ACCESS_GROUP; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_USE_ACCESS_LIST" { count(); yylval.ival = PF_USE_ACCESS_LIST; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_USE_BAN_LIST" { count(); yylval.ival = PF_USE_BAN_LIST; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_USE_LAND_PASS_LIST" { count(); yylval.ival = PF_USE_PASS_LIST; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_LOCAL_SOUND_ONLY" { count(); yylval.ival = PF_SOUND_LOCAL; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_RESTRICT_PUSHOBJECT" { count(); yylval.ival = PF_RESTRICT_PUSHOBJECT; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_GROUP_OBJECT_ENTRY" { count(); yylval.ival = PF_ALLOW_GROUP_OBJECT_ENTRY; return(INTEGER_CONSTANT); }
+"PARCEL_FLAG_ALLOW_ALL_OBJECT_ENTRY" { count(); yylval.ival = PF_ALLOW_ALL_OBJECT_ENTRY; return(INTEGER_CONSTANT); }
+
+"REGION_FLAG_ALLOW_DAMAGE" { count(); yylval.ival = REGION_FLAGS_ALLOW_DAMAGE; return(INTEGER_CONSTANT); }
+"REGION_FLAG_FIXED_SUN" { count(); yylval.ival = REGION_FLAGS_SUN_FIXED; return(INTEGER_CONSTANT); }
+"REGION_FLAG_BLOCK_TERRAFORM" { count(); yylval.ival = REGION_FLAGS_BLOCK_TERRAFORM; return(INTEGER_CONSTANT); }
+"REGION_FLAG_SANDBOX" { count(); yylval.ival = REGION_FLAGS_SANDBOX; return(INTEGER_CONSTANT); }
+"REGION_FLAG_DISABLE_COLLISIONS" { count(); yylval.ival = REGION_FLAGS_SKIP_COLLISIONS; return(INTEGER_CONSTANT); }
+"REGION_FLAG_DISABLE_PHYSICS" { count(); yylval.ival = REGION_FLAGS_SKIP_PHYSICS; return(INTEGER_CONSTANT); }
+"REGION_FLAG_BLOCK_FLY" { count(); yylval.ival = REGION_FLAGS_BLOCK_FLY; return(INTEGER_CONSTANT); }
+"REGION_FLAG_ALLOW_DIRECT_TELEPORT" { count(); yylval.ival = REGION_FLAGS_ALLOW_DIRECT_TELEPORT; return(INTEGER_CONSTANT); }
+"REGION_FLAG_RESTRICT_PUSHOBJECT" { count(); yylval.ival = REGION_FLAGS_RESTRICT_PUSHOBJECT; return(INTEGER_CONSTANT); }
+
+"HTTP_METHOD" { count(); yylval.ival = HTTP_METHOD; return(INTEGER_CONSTANT); }
+"HTTP_MIMETYPE" { count(); yylval.ival = HTTP_MIMETYPE; return(INTEGER_CONSTANT); }
+"HTTP_BODY_MAXLENGTH" { count(); yylval.ival = HTTP_BODY_MAXLENGTH; return(INTEGER_CONSTANT); }
+"HTTP_BODY_TRUNCATED" { count(); yylval.ival = HTTP_BODY_TRUNCATED; return(INTEGER_CONSTANT); }
+"HTTP_VERIFY_CERT" { count(); yylval.ival = HTTP_VERIFY_CERT; return(INTEGER_CONSTANT); }
+
+"PARCEL_COUNT_TOTAL" { count(); yylval.ival = OC_TOTAL; return(INTEGER_CONSTANT); }
+"PARCEL_COUNT_OWNER" { count(); yylval.ival = OC_OWNER; return(INTEGER_CONSTANT); }
+"PARCEL_COUNT_GROUP" { count(); yylval.ival = OC_GROUP; return(INTEGER_CONSTANT); }
+"PARCEL_COUNT_OTHER" { count(); yylval.ival = OC_OTHER; return(INTEGER_CONSTANT); }
+"PARCEL_COUNT_SELECTED" { count(); yylval.ival = OC_SELECTED; return(INTEGER_CONSTANT); }
+"PARCEL_COUNT_TEMP" { count(); yylval.ival = OC_TEMP; return(INTEGER_CONSTANT); }
+
+"PARCEL_DETAILS_NAME" { count(); yylval.ival = PARCEL_DETAILS_NAME; return(INTEGER_CONSTANT); }
+"PARCEL_DETAILS_DESC" { count(); yylval.ival = PARCEL_DETAILS_DESC; return(INTEGER_CONSTANT); }
+"PARCEL_DETAILS_OWNER" { count(); yylval.ival = PARCEL_DETAILS_OWNER; return(INTEGER_CONSTANT); }
+"PARCEL_DETAILS_GROUP" { count(); yylval.ival = PARCEL_DETAILS_GROUP; return(INTEGER_CONSTANT); }
+"PARCEL_DETAILS_AREA" { count(); yylval.ival = PARCEL_DETAILS_AREA; return(INTEGER_CONSTANT); }
+
+{L}({L}|{N})* { count(); yylval.sval = new char[strlen(yytext) + 1]; strcpy(yylval.sval, yytext); return(IDENTIFIER); }
+
+{D}+{E} { count(); yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
+{D}*"."{D}+({E})?{FS}? { count(); yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
+{D}+"."{D}*({E})?{FS}? { count(); yylval.fval = (F32)atof(yytext); return(FP_CONSTANT); }
+
+L?\"(\\.|[^\\"])*\" { parse_string(); count(); return(STRING_CONSTANT); }
+
+"++" { count(); return(INC_OP); }
+"--" { count(); return(DEC_OP); }
+"+=" { count(); return(ADD_ASSIGN); }
+"-=" { count(); return(SUB_ASSIGN); }
+"*=" { count(); return(MUL_ASSIGN); }
+"/=" { count(); return(DIV_ASSIGN); }
+"%=" { count(); return(MOD_ASSIGN); }
+";" { count(); return(';'); }
+"{" { count(); return('{'); }
+"}" { count(); return('}'); }
+"," { count(); return(','); }
+"=" { count(); return('='); }
+"(" { count(); return('('); }
+")" { count(); return(')'); }
+"-" { count(); return('-'); }
+"+" { count(); return('+'); }
+"*" { count(); return('*'); }
+"/" { count(); return('/'); }
+"%" { count(); return('%'); }
+"@" { count(); return('@'); }
+":" { count(); return(':'); }
+">" { count(); return('>'); }
+"<" { count(); return('<'); }
+"]" { count(); return(']'); }
+"[" { count(); return('['); }
+"==" { count(); return(EQ); }
+"!=" { count(); return(NEQ); }
+">=" { count(); return(GEQ); }
+"<=" { count(); return(LEQ); }
+"&" { count(); return('&'); }
+"|" { count(); return('|'); }
+"^" { count(); return('^'); }
+"~" { count(); return('~'); }
+"!" { count(); return('!'); }
+"&&" { count(); return(BOOLEAN_AND); }
+"||" { count(); return(BOOLEAN_OR); }
+"<<" { count(); return(SHIFT_LEFT); }
+">>" { count(); return(SHIFT_RIGHT); }
+
+[ \t\v\n\f] { count(); }
+. { /* ignore bad characters */ }
+
+%%
+
+LLScriptAllocationManager *gAllocationManager;
+LLScriptScript *gScriptp;
+
+// Prototype for the yacc parser entry point
+int yyparse(void);
+
+int yyerror(const char *fmt, ...)
+{
+ gErrorToText.writeError(yyout, gLine, gColumn, LSERROR_SYNTAX_ERROR);
+ return 0;
+}
+
+#define LL_MKS_YACC 1
+#if LL_WINDOWS && LL_MKS_YACC
+int yyinput(void)
+{
+ return input();
+}
+#endif
+
+//#define EMERGENCY_DEBUG_PRINTOUTS
+//#define EMIT_CIL_ASSEMBLER
+
+BOOL lscript_compile(const char* src_filename, const char* dst_filename,
+ const char* err_filename, BOOL is_god_like)
+{
+ BOOL b_parse_ok = FALSE;
+ BOOL b_dummy = FALSE;
+ U64 b_dummy_count = FALSE;
+ LSCRIPTType type = LST_NULL;
+
+ gInternalColumn = 0;
+ gInternalLine = 0;
+ gScriptp = NULL;
+
+ gErrorToText.init();
+ init_supported_expressions();
+ init_temp_jumps();
+ gAllocationManager = new LLScriptAllocationManager();
+
+ yyin = LLFile::fopen(src_filename, "r");
+ if (yyin)
+ {
+ yyout = LLFile::fopen(err_filename, "w");
+
+ // Reset the lexer's internal buffering.
+#if LL_DARWIN || LL_LINUX || !LL_MKS_YACC
+ yyrestart(yyin);
+#else
+ yy_reset();
+#endif
+ b_parse_ok = !yyparse();
+
+ if (b_parse_ok)
+ {
+#ifdef EMERGENCY_DEBUG_PRINTOUTS
+ char compiled[256];
+ sprintf(compiled, "%s.o", src_filename);
+ FILE* compfile;
+ compfile = LLFile::fopen(compiled, "w");
+#endif
+
+ if(dst_filename)
+ {
+ gScriptp->setBytecodeDest(dst_filename);
+ }
+
+ gScriptp->mGodLike = is_god_like;
+
+ gScopeStringTable = new LLStringTable(16384);
+#ifdef EMERGENCY_DEBUG_PRINTOUTS
+ gScriptp->recurse(compfile, 0, 4, LSCP_PRETTY_PRINT, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+#endif
+ gScriptp->recurse(yyout, 0, 0, LSCP_PRUNE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ gScriptp->recurse(yyout, 0, 0, LSCP_SCOPE_PASS1, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ gScriptp->recurse(yyout, 0, 0, LSCP_SCOPE_PASS2, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ gScriptp->recurse(yyout, 0, 0, LSCP_TYPE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ if (!gErrorToText.getErrors())
+ {
+ gScriptp->recurse(yyout, 0, 0, LSCP_RESOURCE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+#ifdef EMERGENCY_DEBUG_PRINTOUTS
+ gScriptp->recurse(yyout, 0, 0, LSCP_EMIT_ASSEMBLY, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+#endif
+#ifdef EMIT_CIL_ASSEMBLER
+ const char* cil_output_file_name = dst_filename? dst_filename : "lscript.cil";
+ FILE* cilout = LLFile::fopen(cil_output_file_name, "w");
+ if(NULL == cilout)
+ {
+ fprintf(yyout, "Error opening cil output file %s\n", cil_output_file_name);
+ }
+ else
+ {
+ gScriptp->recurse(cilout, 0, 0, LSCP_EMIT_CIL_ASSEMBLY, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ if(fclose(cilout) == EOF)
+ {
+ fprintf(yyout, "Error closing cil output file %s\n", cil_output_file_name);
+ }
+ }
+#endif
+ gScriptp->recurse(yyout, 0, 0, LSCP_EMIT_BYTE_CODE, LSPRUNE_INVALID, b_dummy, NULL, type, type, b_dummy_count, NULL, NULL, 0, NULL, 0, NULL);
+ }
+ delete gScopeStringTable;
+ gScopeStringTable = NULL;
+#ifdef EMERGENCY_DEBUG_PRINTOUTS
+ fclose(compfile);
+#endif
+ }
+ fclose(yyout);
+ }
+
+ fclose(yyin);
+ delete gAllocationManager;
+ delete gScopeStringTable;
+
+ return b_parse_ok && !gErrorToText.getErrors();
+}
+
+
+BOOL lscript_compile(char *filename, BOOL is_god_like = FALSE)
+{
+ char src_filename[MAX_STRING];
+ sprintf(src_filename, "%s.lsl", filename);
+ char err_filename[MAX_STRING];
+ sprintf(err_filename, "%s.out", filename);
+ return lscript_compile(src_filename, NULL, err_filename, is_god_like);
+}
+
+
+S32 yywrap()
+{
+ return(1);
+}
+
+void comment()
+{
+ char c;
+
+#if LL_DARWIN
+ while ((c = yyinput()) != '\n' && c != 0 && c != EOF)
+ ;
+#else
+ while ((c = yyinput()) != '\n' && c != 0)
+ ;
+#endif
+
+
+}
+
+void count()
+{
+ S32 i;
+
+ gColumn = gInternalColumn;
+ gLine = gInternalLine;
+
+ for (i = 0; yytext[i] != '\0'; i++)
+ if (yytext[i] == '\n')
+ {
+ gInternalLine++;
+ gInternalColumn = 0;
+ }
+ else if (yytext[i] == '\t')
+ gInternalColumn += 4 - (gInternalColumn % 8);
+ else
+ gInternalColumn++;
+}
+
+void parse_string()
+{
+ S32 length = (S32)strlen(yytext);
+ length = length - 2;
+ char *temp = yytext + 1;
+
+ S32 i;
+ S32 escapes = 0;
+ S32 tabs = 0;
+ for (i = 0; i < length; i++)
+ {
+ if (temp[i] == '\\')
+ {
+ escapes++;
+ i++;
+ if (temp[i] == 't')
+ tabs++;
+ }
+ }
+
+ S32 newlength = length - escapes + tabs*3;
+ yylval.sval = new char[newlength + 1];
+
+ char *dest = yylval.sval;
+
+ for (i = 0; i < length; i++)
+ {
+ if (temp[i] == '\\')
+ {
+ i++;
+ // linefeed
+ if (temp[i] == 'n')
+ {
+ *dest++ = 10;
+ }
+ else if (temp[i] == 't')
+ {
+ *dest++ = ' ';
+ *dest++ = ' ';
+ *dest++ = ' ';
+ *dest++ = ' ';
+ }
+ else
+ {
+ *dest++ = temp[i];
+ }
+ }
+ else
+ {
+ *dest++ = temp[i];
+ }
+ }
+ yylval.sval[newlength] = 0;
+}
diff --git a/indra/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y
new file mode 100644
index 0000000000..7744649a92
--- /dev/null
+++ b/indra/lscript/lscript_compile/indra.y
@@ -0,0 +1,1680 @@
+%{
+ #include "stdtypes.h"
+ #include "lscript_tree.h"
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ int yylex(void);
+ int yyparse( void );
+ int yyerror(const char *fmt, ...);
+
+ #if LL_LINUX
+ // broken yacc codegen... --ryan.
+ #define getenv getenv_workaround
+ #endif
+
+ #ifdef __cplusplus
+ }
+ #endif
+%}
+
+%union
+{
+ S32 ival;
+ F32 fval;
+ char *sval;
+ class LLScriptType *type;
+ class LLScriptConstant *constant;
+ class LLScriptIdentifier *identifier;
+ class LLScriptSimpleAssignable *assignable;
+ class LLScriptGlobalVariable *global;
+ class LLScriptEvent *event;
+ class LLScriptEventHandler *handler;
+ class LLScriptExpression *expression;
+ class LLScriptStatement *statement;
+ class LLScriptGlobalFunctions *global_funcs;
+ class LLScriptFunctionDec *global_decl;
+ class LLScriptState *state;
+ class LLScritpGlobalStorage *global_store;
+ class LLScriptScript *script;
+};
+
+%token INTEGER
+%token FLOAT_TYPE
+%token STRING
+%token LLKEY
+%token VECTOR
+%token QUATERNION
+%token LIST
+
+%token STATE_DEFAULT
+%token STATE
+%token EVENT
+%token JUMP
+%token RETURN
+
+%token STATE_ENTRY
+%token STATE_EXIT
+%token TOUCH_START
+%token TOUCH
+%token TOUCH_END
+%token COLLISION_START
+%token COLLISION
+%token COLLISION_END
+%token LAND_COLLISION_START
+%token LAND_COLLISION
+%token LAND_COLLISION_END
+%token TIMER
+%token CHAT
+%token SENSOR
+%token NO_SENSOR
+%token CONTROL
+%token AT_TARGET
+%token NOT_AT_TARGET
+%token AT_ROT_TARGET
+%token NOT_AT_ROT_TARGET
+%token MONEY
+%token EMAIL
+%token RUN_TIME_PERMISSIONS
+%token INVENTORY
+%token ATTACH
+%token DATASERVER
+%token MOVING_START
+%token MOVING_END
+%token REZ
+%token OBJECT_REZ
+%token LINK_MESSAGE
+%token REMOTE_DATA
+%token HTTP_RESPONSE
+
+%token <sval> IDENTIFIER
+%token <sval> STATE_DEFAULT
+
+%token <ival> INTEGER_CONSTANT
+%token <ival> INTEGER_TRUE
+%token <ival> INTEGER_FALSE
+
+%token <fval> FP_CONSTANT
+
+%token <sval> STRING_CONSTANT
+
+%token INC_OP
+%token DEC_OP
+%token ADD_ASSIGN
+%token SUB_ASSIGN
+%token MUL_ASSIGN
+%token DIV_ASSIGN
+%token MOD_ASSIGN
+
+%token EQ
+%token NEQ
+%token GEQ
+%token LEQ
+
+%token BOOLEAN_AND
+%token BOOLEAN_OR
+
+%token SHIFT_LEFT
+%token SHIFT_RIGHT
+
+%token IF
+%token ELSE
+%token FOR
+%token DO
+%token WHILE
+
+%token PRINT
+
+%token PERIOD
+
+%token ZERO_VECTOR
+%token ZERO_ROTATION
+
+%nonassoc LOWER_THAN_ELSE
+%nonassoc ELSE
+
+
+%type <script> lscript_program
+%type <global_store> globals
+%type <global_store> global
+%type <global> global_variable
+%type <assignable> simple_assignable
+%type <assignable> simple_assignable_no_list
+%type <constant> constant
+%type <assignable> special_constant
+%type <assignable> vector_constant
+%type <assignable> quaternion_constant
+%type <assignable> list_constant
+%type <assignable> list_entries
+%type <assignable> list_entry
+%type <type> typename
+%type <global_funcs> global_function
+%type <global_decl> function_parameters
+%type <global_decl> function_parameter
+%type <state> states
+%type <state> other_states
+%type <state> default
+%type <state> state
+%type <handler> state_body
+%type <handler> event
+%type <event> state_entry
+%type <event> state_exit
+%type <event> touch_start
+%type <event> touch
+%type <event> touch_end
+%type <event> collision_start
+%type <event> collision
+%type <event> collision_end
+%type <event> land_collision_start
+%type <event> land_collision
+%type <event> land_collision_end
+%type <event> at_target
+%type <event> not_at_target
+%type <event> at_rot_target
+%type <event> not_at_rot_target
+%type <event> money
+%type <event> email
+%type <event> run_time_permissions
+%type <event> inventory
+%type <event> attach
+%type <event> dataserver
+%type <event> moving_start
+%type <event> moving_end
+%type <event> rez
+%type <event> object_rez
+%type <event> remote_data
+%type <event> http_response
+%type <event> link_message
+%type <event> timer
+%type <event> chat
+%type <event> sensor
+%type <event> no_sensor
+%type <event> control
+%type <statement> compound_statement
+%type <statement> statement
+%type <statement> statements
+%type <statement> declaration
+%type <statement> ';'
+%type <statement> '@'
+%type <expression> nextforexpressionlist
+%type <expression> forexpressionlist
+%type <expression> nextfuncexpressionlist
+%type <expression> funcexpressionlist
+%type <expression> nextlistexpressionlist
+%type <expression> listexpressionlist
+%type <expression> unarypostfixexpression
+%type <expression> vector_initializer
+%type <expression> quaternion_initializer
+%type <expression> list_initializer
+%type <expression> lvalue
+%type <expression> '-'
+%type <expression> '!'
+%type <expression> '~'
+%type <expression> '='
+%type <expression> '<'
+%type <expression> '>'
+%type <expression> '+'
+%type <expression> '*'
+%type <expression> '/'
+%type <expression> '%'
+%type <expression> '&'
+%type <expression> '|'
+%type <expression> '^'
+%type <expression> ADD_ASSIGN
+%type <expression> SUB_ASSIGN
+%type <expression> MUL_ASSIGN
+%type <expression> DIV_ASSIGN
+%type <expression> MOD_ASSIGN
+%type <expression> EQ
+%type <expression> NEQ
+%type <expression> LEQ
+%type <expression> GEQ
+%type <expression> BOOLEAN_AND
+%type <expression> BOOLEAN_OR
+%type <expression> SHIFT_LEFT
+%type <expression> SHIFT_RIGHT
+%type <expression> INC_OP
+%type <expression> DEC_OP
+%type <expression> '('
+%type <expression> ')'
+%type <expression> PRINT
+%type <identifier> name_type
+%type <expression> expression
+%type <expression> unaryexpression
+%type <expression> typecast
+
+%right '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN
+%left BOOLEAN_AND BOOLEAN_OR
+%left '|'
+%left '^'
+%left '&'
+%left EQ NEQ
+%left '<' LEQ '>' GEQ
+%left SHIFT_LEFT SHIFT_RIGHT
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' '~' INC_OP DEC_OP
+%nonassoc INITIALIZER
+
+%%
+
+lscript_program
+ : globals states
+ {
+ $$ = new LLScriptScript($1, $2);
+ gAllocationManager->addAllocation($$);
+ gScriptp = $$;
+ }
+ | states
+ {
+ $$ = new LLScriptScript(NULL, $1);
+ gAllocationManager->addAllocation($$);
+ gScriptp = $$;
+ }
+ ;
+
+globals
+ : global
+ {
+ $$ = $1;
+ }
+ | global globals
+ {
+ $$ = $1;
+ $1->addGlobal($2);
+ }
+ ;
+
+global
+ : global_variable
+ {
+ $$ = new LLScritpGlobalStorage($1);
+ gAllocationManager->addAllocation($$);
+ }
+ | global_function
+ {
+ $$ = new LLScritpGlobalStorage($1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+name_type
+ : typename IDENTIFIER
+ {
+ $$ = new LLScriptIdentifier(gLine, gColumn, $2, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+global_variable
+ : name_type ';'
+ {
+ $$ = new LLScriptGlobalVariable(gLine, gColumn, $1->mType, $1, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | name_type '=' simple_assignable ';'
+ {
+ $$ = new LLScriptGlobalVariable(gLine, gColumn, $1->mType, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+simple_assignable
+ : simple_assignable_no_list
+ {
+ $$ = $1;
+ }
+ | list_constant
+ {
+ $$ = $1;
+ }
+ ;
+
+simple_assignable_no_list
+ : IDENTIFIER
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptSAIdentifier(gLine, gColumn, id);
+ gAllocationManager->addAllocation($$);
+ }
+ | constant
+ {
+ $$ = new LLScriptSAConstant(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | special_constant
+ {
+ $$ = $1;
+ }
+ ;
+
+constant
+ : INTEGER_CONSTANT
+ {
+ $$ = new LLScriptConstantInteger(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | INTEGER_TRUE
+ {
+ $$ = new LLScriptConstantInteger(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | INTEGER_FALSE
+ {
+ $$ = new LLScriptConstantInteger(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | FP_CONSTANT
+ {
+ $$ = new LLScriptConstantFloat(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | STRING_CONSTANT
+ {
+ $$ = new LLScriptConstantString(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+special_constant
+ : vector_constant
+ {
+ $$ = $1;
+ }
+ | quaternion_constant
+ {
+ $$ = $1;
+ }
+ ;
+
+vector_constant
+ : '<' simple_assignable ',' simple_assignable ',' simple_assignable '>'
+ {
+ $$ = new LLScriptSAVector(gLine, gColumn, $2, $4, $6);
+ gAllocationManager->addAllocation($$);
+ }
+ | ZERO_VECTOR
+ {
+ LLScriptConstantFloat *cf0 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf0);
+ LLScriptSAConstant *sa0 = new LLScriptSAConstant(gLine, gColumn, cf0);
+ gAllocationManager->addAllocation(sa0);
+ LLScriptConstantFloat *cf1 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf1);
+ LLScriptSAConstant *sa1 = new LLScriptSAConstant(gLine, gColumn, cf1);
+ gAllocationManager->addAllocation(sa1);
+ LLScriptConstantFloat *cf2 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf2);
+ LLScriptSAConstant *sa2 = new LLScriptSAConstant(gLine, gColumn, cf2);
+ gAllocationManager->addAllocation(sa2);
+ $$ = new LLScriptSAVector(gLine, gColumn, sa0, sa1, sa2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+quaternion_constant
+ : '<' simple_assignable ',' simple_assignable ',' simple_assignable ',' simple_assignable '>'
+ {
+ $$ = new LLScriptSAQuaternion(gLine, gColumn, $2, $4, $6, $8);
+ gAllocationManager->addAllocation($$);
+ }
+ | ZERO_ROTATION
+ {
+ LLScriptConstantFloat *cf0 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf0);
+ LLScriptSAConstant *sa0 = new LLScriptSAConstant(gLine, gColumn, cf0);
+ gAllocationManager->addAllocation(sa0);
+ LLScriptConstantFloat *cf1 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf1);
+ LLScriptSAConstant *sa1 = new LLScriptSAConstant(gLine, gColumn, cf1);
+ gAllocationManager->addAllocation(sa1);
+ LLScriptConstantFloat *cf2 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf2);
+ LLScriptSAConstant *sa2 = new LLScriptSAConstant(gLine, gColumn, cf2);
+ gAllocationManager->addAllocation(sa2);
+ LLScriptConstantFloat *cf3 = new LLScriptConstantFloat(gLine, gColumn, 1.f);
+ gAllocationManager->addAllocation(cf3);
+ LLScriptSAConstant *sa3 = new LLScriptSAConstant(gLine, gColumn, cf3);
+ gAllocationManager->addAllocation(sa3);
+ $$ = new LLScriptSAQuaternion(gLine, gColumn, sa0, sa1, sa2, sa3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+list_constant
+ : '[' list_entries ']'
+ {
+ $$ = new LLScriptSAList(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | '[' ']'
+ {
+ $$ = new LLScriptSAList(gLine, gColumn, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+list_entries
+ : list_entry
+ {
+ $$ = $1;
+ }
+ | list_entry ',' list_entries
+ {
+ $$ = $1;
+ $1->addAssignable($3);
+ }
+ ;
+
+list_entry
+ : simple_assignable_no_list
+ {
+ $$ = $1;
+ }
+ ;
+
+typename
+ : INTEGER
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_INTEGER);
+ gAllocationManager->addAllocation($$);
+ }
+ | FLOAT_TYPE
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_FLOATINGPOINT);
+ gAllocationManager->addAllocation($$);
+ }
+ | STRING
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_STRING);
+ gAllocationManager->addAllocation($$);
+ }
+ | LLKEY
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_KEY);
+ gAllocationManager->addAllocation($$);
+ }
+ | VECTOR
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_VECTOR);
+ gAllocationManager->addAllocation($$);
+ }
+ | QUATERNION
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_QUATERNION);
+ gAllocationManager->addAllocation($$);
+ }
+ | LIST
+ {
+ $$ = new LLScriptType(gLine, gColumn, LST_LIST);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+global_function
+ : IDENTIFIER '(' ')' compound_statement
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptGlobalFunctions(gLine, gColumn, NULL, id, NULL, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ | name_type '(' ')' compound_statement
+ {
+ $$ = new LLScriptGlobalFunctions(gLine, gColumn, $1->mType, $1, NULL, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ | IDENTIFIER '(' function_parameters ')' compound_statement
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptGlobalFunctions(gLine, gColumn, NULL, id, $3, $5);
+ gAllocationManager->addAllocation($$);
+ }
+ | name_type '(' function_parameters ')' compound_statement
+ {
+ $$ = new LLScriptGlobalFunctions(gLine, gColumn, $1->mType, $1, $3, $5);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+function_parameters
+ : function_parameter
+ {
+ $$ = $1;
+ }
+ | function_parameter ',' function_parameters
+ {
+ $$ = $1;
+ $1->addFunctionParameter($3);
+ }
+ ;
+
+function_parameter
+ : typename IDENTIFIER
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptFunctionDec(gLine, gColumn, $1, id);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+states
+ : default
+ {
+ $$ = $1;
+ }
+ | default other_states
+ {
+ $$ = $1;
+ $1->mNextp = $2;
+ }
+ ;
+
+other_states
+ : state
+ {
+ $$ = $1;
+ }
+ | state other_states
+ {
+ $$ = $1;
+ $1->addState($2);
+ }
+ ;
+
+default
+ : STATE_DEFAULT '{' state_body '}'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptState(gLine, gColumn, LSSTYPE_DEFAULT, id, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+state
+ : STATE IDENTIFIER '{' state_body '}'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptState(gLine, gColumn, LSSTYPE_USER, id, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+state_body
+ : event
+ {
+ $$ = $1;
+ }
+ | event state_body
+ {
+ $$ = $1;
+ $1->addEvent($2);
+ }
+ ;
+
+event
+ : state_entry compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | state_exit compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | touch_start compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | touch compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | touch_end compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | collision_start compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | collision compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | collision_end compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | land_collision_start compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | land_collision compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | land_collision_end compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | timer compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | chat compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | sensor compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | no_sensor compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | at_target compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | not_at_target compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | at_rot_target compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | not_at_rot_target compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | money compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | email compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | run_time_permissions compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | inventory compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | attach compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | dataserver compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | control compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | moving_start compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | moving_end compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | rez compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | object_rez compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | link_message compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | remote_data compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | http_response compound_statement
+ {
+ $$ = new LLScriptEventHandler(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+state_entry
+ : STATE_ENTRY '(' ')'
+ {
+ $$ = new LLScriptStateEntryEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+state_exit
+ : STATE_EXIT '(' ')'
+ {
+ $$ = new LLScriptStateExitEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+touch_start
+ : TOUCH_START '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptTouchStartEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+touch
+ : TOUCH '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptTouchEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+touch_end
+ : TOUCH_END '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptTouchEndEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+collision_start
+ : COLLISION_START '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptCollisionStartEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+collision
+ : COLLISION '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptCollisionEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+collision_end
+ : COLLISION_END '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptCollisionEndEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+land_collision_start
+ : LAND_COLLISION_START '(' VECTOR IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptLandCollisionStartEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+land_collision
+ : LAND_COLLISION '(' VECTOR IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptLandCollisionEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+land_collision_end
+ : LAND_COLLISION_END '(' VECTOR IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptLandCollisionEndEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+at_target
+ : AT_TARGET '(' INTEGER IDENTIFIER ',' VECTOR IDENTIFIER ',' VECTOR IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ $$ = new LLScriptAtTarget(gLine, gColumn, id1, id2, id3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+not_at_target
+ : NOT_AT_TARGET '(' ')'
+ {
+ $$ = new LLScriptNotAtTarget(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+at_rot_target
+ : AT_ROT_TARGET '(' INTEGER IDENTIFIER ',' QUATERNION IDENTIFIER ',' QUATERNION IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ $$ = new LLScriptAtRotTarget(gLine, gColumn, id1, id2, id3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+not_at_rot_target
+ : NOT_AT_ROT_TARGET '(' ')'
+ {
+ $$ = new LLScriptNotAtRotTarget(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+money
+ : MONEY '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ $$ = new LLScriptMoneyEvent(gLine, gColumn, id1, id2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+email
+ : EMAIL '(' STRING IDENTIFIER ',' STRING IDENTIFIER ',' STRING IDENTIFIER ',' STRING IDENTIFIER ',' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ LLScriptIdentifier *id4 = new LLScriptIdentifier(gLine, gColumn, $13);
+ gAllocationManager->addAllocation(id4);
+ LLScriptIdentifier *id5 = new LLScriptIdentifier(gLine, gColumn, $16);
+ gAllocationManager->addAllocation(id5);
+ $$ = new LLScriptEmailEvent(gLine, gColumn, id1, id2, id3, id4, id5);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+run_time_permissions
+ : RUN_TIME_PERMISSIONS '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptRTPEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+inventory
+ : INVENTORY '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptInventoryEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+attach
+ : ATTACH '(' LLKEY IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptAttachEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+dataserver
+ : DATASERVER '(' LLKEY IDENTIFIER ',' STRING IDENTIFIER')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ $$ = new LLScriptDataserverEvent(gLine, gColumn, id1, id2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+moving_start
+ : MOVING_START '(' ')'
+ {
+ $$ = new LLScriptMovingStartEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+moving_end
+ : MOVING_END '(' ')'
+ {
+ $$ = new LLScriptMovingEndEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+timer
+ : TIMER '(' ')'
+ {
+ $$ = new LLScriptTimerEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+chat
+ : CHAT '(' INTEGER IDENTIFIER ',' STRING IDENTIFIER ',' LLKEY IDENTIFIER ',' STRING IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ LLScriptIdentifier *id4 = new LLScriptIdentifier(gLine, gColumn, $13);
+ gAllocationManager->addAllocation(id4);
+ $$ = new LLScriptChatEvent(gLine, gColumn, id1, id2, id3, id4);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+sensor
+ : SENSOR '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptSensorEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+no_sensor
+ : NO_SENSOR '(' ')'
+ {
+ $$ = new LLScriptNoSensorEvent(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+control
+ : CONTROL '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ',' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ $$ = new LLScriptControlEvent(gLine, gColumn, id1, id2, id3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+rez
+ : REZ '(' INTEGER IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptRezEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+object_rez
+ : OBJECT_REZ '(' LLKEY IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ $$ = new LLScriptObjectRezEvent(gLine, gColumn, id1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+link_message
+ : LINK_MESSAGE '(' INTEGER IDENTIFIER ',' INTEGER IDENTIFIER ',' STRING IDENTIFIER ',' LLKEY IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ LLScriptIdentifier *id4 = new LLScriptIdentifier(gLine, gColumn, $13);
+ gAllocationManager->addAllocation(id4);
+ $$ = new LLScriptLinkMessageEvent(gLine, gColumn, id1, id2, id3, id4);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+remote_data
+ : REMOTE_DATA '(' INTEGER IDENTIFIER ',' LLKEY IDENTIFIER ',' LLKEY IDENTIFIER ',' STRING IDENTIFIER ',' INTEGER IDENTIFIER ',' STRING IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ LLScriptIdentifier *id4 = new LLScriptIdentifier(gLine, gColumn, $13);
+ gAllocationManager->addAllocation(id4);
+ LLScriptIdentifier *id5 = new LLScriptIdentifier(gLine, gColumn, $16);
+ gAllocationManager->addAllocation(id4);
+ LLScriptIdentifier *id6 = new LLScriptIdentifier(gLine, gColumn, $19);
+ gAllocationManager->addAllocation(id4);
+ $$ = new LLScriptRemoteEvent(gLine, gColumn, id1, id2, id3, id4, id5, id6);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+http_response
+ : HTTP_RESPONSE '(' LLKEY IDENTIFIER ',' INTEGER IDENTIFIER ',' LIST IDENTIFIER ',' STRING IDENTIFIER ')'
+ {
+ LLScriptIdentifier *id1 = new LLScriptIdentifier(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(id1);
+ LLScriptIdentifier *id2 = new LLScriptIdentifier(gLine, gColumn, $7);
+ gAllocationManager->addAllocation(id2);
+ LLScriptIdentifier *id3 = new LLScriptIdentifier(gLine, gColumn, $10);
+ gAllocationManager->addAllocation(id3);
+ LLScriptIdentifier *id4 = new LLScriptIdentifier(gLine, gColumn, $13);
+ gAllocationManager->addAllocation(id4);
+ $$ = new LLScriptHTTPResponseEvent(gLine, gColumn, id1, id2, id3, id4);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+compound_statement
+ : '{' '}'
+ {
+ $$ = new LLScriptCompoundStatement(gLine, gColumn, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | '{' statements '}'
+ {
+ $$ = new LLScriptCompoundStatement(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+statements
+ : statement
+ {
+ $$ = $1;
+ }
+ | statements statement
+ {
+ $$ = new LLScriptStatementSequence(gLine, gColumn, $1, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+statement
+ : ';'
+ {
+ $$ = new LLScriptNOOP(gLine, gColumn);
+ gAllocationManager->addAllocation($$);
+ }
+ | STATE IDENTIFIER ';'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptStateChange(gLine, gColumn, id);
+ gAllocationManager->addAllocation($$);
+ }
+ | STATE STATE_DEFAULT ';'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptStateChange(gLine, gColumn, id);
+ gAllocationManager->addAllocation($$);
+ }
+ | JUMP IDENTIFIER ';'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptJump(gLine, gColumn, id);
+ gAllocationManager->addAllocation($$);
+ }
+ | '@' IDENTIFIER ';'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptLabel(gLine, gColumn, id);
+ gAllocationManager->addAllocation($$);
+ }
+ | RETURN expression ';'
+ {
+ $$ = new LLScriptReturn(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | RETURN ';'
+ {
+ $$ = new LLScriptReturn(gLine, gColumn, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression ';'
+ {
+ $$ = new LLScriptExpressionStatement(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | declaration ';'
+ {
+ $$ = $1;
+ }
+ | compound_statement
+ {
+ $$ = $1;
+ }
+ | IF '(' expression ')' statement %prec LOWER_THAN_ELSE
+ {
+ $$ = new LLScriptIf(gLine, gColumn, $3, $5);
+ $5->mAllowDeclarations = FALSE;
+ gAllocationManager->addAllocation($$);
+ }
+ | IF '(' expression ')' statement ELSE statement
+ {
+ $$ = new LLScriptIfElse(gLine, gColumn, $3, $5, $7);
+ $5->mAllowDeclarations = FALSE;
+ $7->mAllowDeclarations = FALSE;
+ gAllocationManager->addAllocation($$);
+ }
+ | FOR '(' forexpressionlist ';' expression ';' forexpressionlist ')' statement
+ {
+ $$ = new LLScriptFor(gLine, gColumn, $3, $5, $7, $9);
+ $9->mAllowDeclarations = FALSE;
+ gAllocationManager->addAllocation($$);
+ }
+ | DO statement WHILE '(' expression ')' ';'
+ {
+ $$ = new LLScriptDoWhile(gLine, gColumn, $2, $5);
+ $2->mAllowDeclarations = FALSE;
+ gAllocationManager->addAllocation($$);
+ }
+ | WHILE '(' expression ')' statement
+ {
+ $$ = new LLScriptWhile(gLine, gColumn, $3, $5);
+ $5->mAllowDeclarations = FALSE;
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+declaration
+ : typename IDENTIFIER
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptDeclaration(gLine, gColumn, $1, id, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | typename IDENTIFIER '=' expression
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $2);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptDeclaration(gLine, gColumn, $1, id, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+forexpressionlist
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | nextforexpressionlist
+ {
+ $$ = $1;
+ }
+ ;
+
+nextforexpressionlist
+ : expression
+ {
+ $$ = new LLScriptForExpressionList(gLine, gColumn, $1, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression ',' nextforexpressionlist
+ {
+ $$ = new LLScriptForExpressionList(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+funcexpressionlist
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | nextfuncexpressionlist
+ {
+ $$ = $1;
+ }
+ ;
+
+nextfuncexpressionlist
+ : expression
+ {
+ $$ = new LLScriptFuncExpressionList(gLine, gColumn, $1, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression ',' nextfuncexpressionlist
+ {
+ $$ = new LLScriptFuncExpressionList(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+listexpressionlist
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | nextlistexpressionlist
+ {
+ $$ = $1;
+ }
+ ;
+
+nextlistexpressionlist
+ : expression
+ {
+ $$ = new LLScriptListExpressionList(gLine, gColumn, $1, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression ',' nextlistexpressionlist
+ {
+ $$ = new LLScriptListExpressionList(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+expression
+ : unaryexpression
+ {
+ $$ = $1;
+ }
+ | lvalue '=' expression
+ {
+ $$ = new LLScriptAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue ADD_ASSIGN expression
+ {
+ $$ = new LLScriptAddAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue SUB_ASSIGN expression
+ {
+ $$ = new LLScriptSubAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue MUL_ASSIGN expression
+ {
+ $$ = new LLScriptMulAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue DIV_ASSIGN expression
+ {
+ $$ = new LLScriptDivAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue MOD_ASSIGN expression
+ {
+ $$ = new LLScriptModAssignment(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression EQ expression
+ {
+ $$ = new LLScriptEquality(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression NEQ expression
+ {
+ $$ = new LLScriptNotEquals(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression LEQ expression
+ {
+ $$ = new LLScriptLessEquals(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression GEQ expression
+ {
+ $$ = new LLScriptGreaterEquals(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '<' expression
+ {
+ $$ = new LLScriptLessThan(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '>' expression
+ {
+ $$ = new LLScriptGreaterThan(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '+' expression
+ {
+ $$ = new LLScriptPlus(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '-' expression
+ {
+ $$ = new LLScriptMinus(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '*' expression
+ {
+ $$ = new LLScriptTimes(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '/' expression
+ {
+ $$ = new LLScriptDivide(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '%' expression
+ {
+ $$ = new LLScriptMod(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '&' expression
+ {
+ $$ = new LLScriptBitAnd(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '|' expression
+ {
+ $$ = new LLScriptBitOr(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression '^' expression
+ {
+ $$ = new LLScriptBitXor(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression BOOLEAN_AND expression
+ {
+ $$ = new LLScriptBooleanAnd(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression BOOLEAN_OR expression
+ {
+ $$ = new LLScriptBooleanOr(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression SHIFT_LEFT expression
+ {
+ $$ = new LLScriptShiftLeft(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | expression SHIFT_RIGHT expression
+ {
+ $$ = new LLScriptShiftRight(gLine, gColumn, $1, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+unaryexpression
+ : '-' expression
+ {
+ $$ = new LLScriptUnaryMinus(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | '!' expression
+ {
+ $$ = new LLScriptBooleanNot(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | '~' expression
+ {
+ $$ = new LLScriptBitNot(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | INC_OP lvalue
+ {
+ $$ = new LLScriptPreIncrement(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | DEC_OP lvalue
+ {
+ $$ = new LLScriptPreDecrement(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ | typecast
+ {
+ $$ = $1;
+ }
+ | unarypostfixexpression
+ {
+ $$ = $1;
+ }
+ | '(' expression ')'
+ {
+ $$ = new LLScriptParenthesis(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+typecast
+ : '(' typename ')' lvalue
+ {
+ $$ = new LLScriptTypeCast(gLine, gColumn, $2, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ | '(' typename ')' constant
+ {
+ LLScriptConstantExpression *temp = new LLScriptConstantExpression(gLine, gColumn, $4);
+ gAllocationManager->addAllocation(temp);
+ $$ = new LLScriptTypeCast(gLine, gColumn, $2, temp);
+ gAllocationManager->addAllocation($$);
+ }
+ | '(' typename ')' unarypostfixexpression
+ {
+ $$ = new LLScriptTypeCast(gLine, gColumn, $2, $4);
+ gAllocationManager->addAllocation($$);
+ }
+ | '(' typename ')' '(' expression ')'
+ {
+ $$ = new LLScriptTypeCast(gLine, gColumn, $2, $5);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+unarypostfixexpression
+ : vector_initializer
+ {
+ $$ = $1;
+ }
+ | quaternion_initializer
+ {
+ $$ = $1;
+ }
+ | list_initializer
+ {
+ $$ = $1;
+ }
+ | lvalue
+ {
+ $$ = $1;
+ }
+ | lvalue INC_OP
+ {
+ $$ = new LLScriptPostIncrement(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | lvalue DEC_OP
+ {
+ $$ = new LLScriptPostDecrement(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ | IDENTIFIER '(' funcexpressionlist ')'
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptFunctionCall(gLine, gColumn, id, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | PRINT '(' expression ')'
+ {
+ $$ = new LLScriptPrint(gLine, gColumn, $3);
+ gAllocationManager->addAllocation($$);
+ }
+ | constant
+ {
+ $$ = new LLScriptConstantExpression(gLine, gColumn, $1);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+vector_initializer
+ : '<' expression ',' expression ',' expression '>' %prec INITIALIZER
+ {
+ $$ = new LLScriptVectorInitializer(gLine, gColumn, $2, $4, $6);
+ gAllocationManager->addAllocation($$);
+ }
+ | ZERO_VECTOR
+ {
+ LLScriptConstantFloat *cf0 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf0);
+ LLScriptConstantExpression *sa0 = new LLScriptConstantExpression(gLine, gColumn, cf0);
+ gAllocationManager->addAllocation(sa0);
+ LLScriptConstantFloat *cf1 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf1);
+ LLScriptConstantExpression *sa1 = new LLScriptConstantExpression(gLine, gColumn, cf1);
+ gAllocationManager->addAllocation(sa1);
+ LLScriptConstantFloat *cf2 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf2);
+ LLScriptConstantExpression *sa2 = new LLScriptConstantExpression(gLine, gColumn, cf2);
+ gAllocationManager->addAllocation(sa2);
+ $$ = new LLScriptVectorInitializer(gLine, gColumn, sa0, sa1, sa2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+quaternion_initializer
+ : '<' expression ',' expression ',' expression ',' expression '>' %prec INITIALIZER
+ {
+ $$ = new LLScriptQuaternionInitializer(gLine, gColumn, $2, $4, $6, $8);
+ gAllocationManager->addAllocation($$);
+ }
+ | ZERO_ROTATION
+ {
+ LLScriptConstantFloat *cf0 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf0);
+ LLScriptConstantExpression *sa0 = new LLScriptConstantExpression(gLine, gColumn, cf0);
+ gAllocationManager->addAllocation(sa0);
+ LLScriptConstantFloat *cf1 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf1);
+ LLScriptConstantExpression *sa1 = new LLScriptConstantExpression(gLine, gColumn, cf1);
+ gAllocationManager->addAllocation(sa1);
+ LLScriptConstantFloat *cf2 = new LLScriptConstantFloat(gLine, gColumn, 0.f);
+ gAllocationManager->addAllocation(cf2);
+ LLScriptConstantExpression *sa2 = new LLScriptConstantExpression(gLine, gColumn, cf2);
+ gAllocationManager->addAllocation(sa2);
+ LLScriptConstantFloat *cf3 = new LLScriptConstantFloat(gLine, gColumn, 1.f);
+ gAllocationManager->addAllocation(cf3);
+ LLScriptConstantExpression *sa3 = new LLScriptConstantExpression(gLine, gColumn, cf3);
+ gAllocationManager->addAllocation(sa3);
+ $$ = new LLScriptQuaternionInitializer(gLine, gColumn, sa0, sa1, sa2, sa3);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+list_initializer
+ : '[' listexpressionlist ']' %prec INITIALIZER
+ {
+ $$ = new LLScriptListInitializer(gLine, gColumn, $2);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+lvalue
+ : IDENTIFIER
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptLValue(gLine, gColumn, id, NULL);
+ gAllocationManager->addAllocation($$);
+ }
+ | IDENTIFIER PERIOD IDENTIFIER
+ {
+ LLScriptIdentifier *id = new LLScriptIdentifier(gLine, gColumn, $1);
+ gAllocationManager->addAllocation(id);
+ LLScriptIdentifier *ac = new LLScriptIdentifier(gLine, gColumn, $3);
+ gAllocationManager->addAllocation(id);
+ $$ = new LLScriptLValue(gLine, gColumn, id, ac);
+ gAllocationManager->addAllocation($$);
+ }
+ ;
+
+%%
diff --git a/indra/lscript/lscript_compile/lscript_alloc.cpp b/indra/lscript/lscript_compile/lscript_alloc.cpp
new file mode 100644
index 0000000000..3df68c0bd4
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_alloc.cpp
@@ -0,0 +1,8 @@
+/**
+ * @file lscript_alloc.cpp
+ * @brief Allocation tracking
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
diff --git a/indra/lscript/lscript_compile/lscript_bytecode.cpp b/indra/lscript/lscript_compile/lscript_bytecode.cpp
new file mode 100644
index 0000000000..1cf8cd7f28
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_bytecode.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file lscript_bytecode.cpp
+ * @brief classes to build actual bytecode
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_bytecode.h"
+#include "lscript_error.h"
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4102) // 'yy_more' : unreferenced label
+# pragma warning(disable: 4702) // unreachable code
+#endif
+
+LLScriptJumpTable::LLScriptJumpTable()
+{
+}
+
+LLScriptJumpTable::~LLScriptJumpTable()
+{
+ mLabelMap.deleteAllData();
+ mJumpMap.deleteAllData();
+}
+
+void LLScriptJumpTable::addLabel(char *name, S32 offset)
+{
+ char *temp = gScopeStringTable->addString(name);
+ mLabelMap[temp] = new S32(offset);
+}
+
+void LLScriptJumpTable::addJump(char *name, S32 offset)
+{
+ char *temp = gScopeStringTable->addString(name);
+ mJumpMap[temp] = new S32(offset);
+}
+
+
+LLScriptByteCodeChunk::LLScriptByteCodeChunk(BOOL b_need_jumps)
+: mCodeChunk(NULL), mCurrentOffset(0), mJumpTable(NULL)
+{
+ if (b_need_jumps)
+ {
+ mJumpTable = new LLScriptJumpTable();
+ }
+}
+
+LLScriptByteCodeChunk::~LLScriptByteCodeChunk()
+{
+ delete [] mCodeChunk;
+ delete mJumpTable;
+}
+
+void LLScriptByteCodeChunk::addByte(U8 byte)
+{
+ if (mCodeChunk)
+ {
+ U8 *temp = new U8[mCurrentOffset + 1];
+ memcpy(temp, mCodeChunk, mCurrentOffset);
+ delete [] mCodeChunk;
+ mCodeChunk = temp;
+ }
+ else
+ {
+ mCodeChunk = new U8[1];
+ }
+ *(mCodeChunk + mCurrentOffset++) = byte;
+}
+
+void LLScriptByteCodeChunk::addU16(U16 data)
+{
+ U8 temp[2];
+ S32 offset = 0;
+ u162bytestream(temp, offset, data);
+ addBytes(temp, 2);
+}
+
+void LLScriptByteCodeChunk::addBytes(U8 *bytes, S32 size)
+{
+ if (mCodeChunk)
+ {
+ U8 *temp = new U8[mCurrentOffset + size];
+ memcpy(temp, mCodeChunk, mCurrentOffset);
+ delete [] mCodeChunk;
+ mCodeChunk = temp;
+ }
+ else
+ {
+ mCodeChunk = new U8[size];
+ }
+ memcpy(mCodeChunk + mCurrentOffset, bytes, size);
+ mCurrentOffset += size;
+}
+
+void LLScriptByteCodeChunk::addBytes(char *bytes, S32 size)
+{
+ if (mCodeChunk)
+ {
+ U8 *temp = new U8[mCurrentOffset + size];
+ memcpy(temp, mCodeChunk, mCurrentOffset);
+ delete [] mCodeChunk;
+ mCodeChunk = temp;
+ }
+ else
+ {
+ mCodeChunk = new U8[size];
+ }
+ memcpy(mCodeChunk + mCurrentOffset, bytes, size);
+ mCurrentOffset += size;
+}
+
+void LLScriptByteCodeChunk::addBytes(S32 size)
+{
+ if (mCodeChunk)
+ {
+ U8 *temp = new U8[mCurrentOffset + size];
+ memcpy(temp, mCodeChunk, mCurrentOffset);
+ delete [] mCodeChunk;
+ mCodeChunk = temp;
+ }
+ else
+ {
+ mCodeChunk = new U8[size];
+ }
+ memset(mCodeChunk + mCurrentOffset, 0, size);
+ mCurrentOffset += size;
+}
+
+void LLScriptByteCodeChunk::addBytesDontInc(S32 size)
+{
+ if (mCodeChunk)
+ {
+ U8 *temp = new U8[mCurrentOffset + size];
+ memcpy(temp, mCodeChunk, mCurrentOffset);
+ delete [] mCodeChunk;
+ mCodeChunk = temp;
+ }
+ else
+ {
+ mCodeChunk = new U8[size];
+ }
+ memset(mCodeChunk + mCurrentOffset, 0, size);
+}
+
+void LLScriptByteCodeChunk::addInteger(S32 value)
+{
+ U8 temp[4];
+ S32 offset = 0;
+ integer2bytestream(temp, offset, value);
+ addBytes(temp, 4);
+}
+
+void LLScriptByteCodeChunk::addFloat(F32 value)
+{
+ U8 temp[4];
+ S32 offset = 0;
+ float2bytestream(temp, offset, value);
+ addBytes(temp, 4);
+}
+
+void LLScriptByteCodeChunk::addLabel(char *name)
+{
+ if (mJumpTable)
+ {
+ mJumpTable->addLabel(name, mCurrentOffset);
+ }
+}
+
+void LLScriptByteCodeChunk::addJump(char *name)
+{
+ if (mJumpTable)
+ {
+ mJumpTable->addJump(name, mCurrentOffset);
+ }
+}
+
+// format is Byte 0: jump op code Byte 1 - 4: offset
+// the jump position points to Byte 5, so we need to add the data at
+// offset - 4, offset - 3, offset - 2, and offset - 1
+
+// offset is label - jump
+
+void LLScriptByteCodeChunk::connectJumps()
+{
+ char *jump;
+ S32 offset, jumppos;
+
+ if (mJumpTable)
+ {
+ for (jump = mJumpTable->mJumpMap.getFirstKey();
+ jump;
+ jump = mJumpTable->mJumpMap.getNextKey())
+ {
+ jumppos = *mJumpTable->mJumpMap[jump];
+ offset = *mJumpTable->mLabelMap[jump] - jumppos;
+ jumppos = jumppos - 4;
+ integer2bytestream(mCodeChunk, jumppos, offset);
+ }
+ }
+}
+
+LLScriptScriptCodeChunk::LLScriptScriptCodeChunk(S32 total_size)
+: mTotalSize(total_size), mCompleteCode(NULL)
+{
+ mRegisters = new LLScriptByteCodeChunk(FALSE);
+ mGlobalVariables = new LLScriptByteCodeChunk(FALSE);
+ mGlobalFunctions = new LLScriptByteCodeChunk(FALSE);
+ mStates = new LLScriptByteCodeChunk(FALSE);
+ mHeap = new LLScriptByteCodeChunk(FALSE);
+}
+
+LLScriptScriptCodeChunk::~LLScriptScriptCodeChunk()
+{
+ delete mRegisters;
+ delete mGlobalVariables;
+ delete mGlobalFunctions;
+ delete mStates;
+ delete mHeap;
+ delete [] mCompleteCode;
+}
+
+void LLScriptScriptCodeChunk::build(FILE *efp, FILE *bcfp)
+{
+ S32 code_data_size = mRegisters->mCurrentOffset +
+ mGlobalVariables->mCurrentOffset +
+ mGlobalFunctions->mCurrentOffset +
+ mStates->mCurrentOffset +
+ mHeap->mCurrentOffset;
+
+ S32 offset = 0;
+
+ if (code_data_size < mTotalSize)
+ {
+ mCompleteCode = new U8[mTotalSize];
+ memset(mCompleteCode, 0, mTotalSize);
+
+ memcpy(mCompleteCode, mRegisters->mCodeChunk, mRegisters->mCurrentOffset);
+ offset += mRegisters->mCurrentOffset;
+
+ set_register(mCompleteCode, LREG_IP, 0);
+ set_register(mCompleteCode, LREG_VN, LSL2_VERSION_NUMBER);
+ set_event_register(mCompleteCode, LREG_IE, 0, LSL2_CURRENT_MAJOR_VERSION);
+ set_register(mCompleteCode, LREG_BP, mTotalSize - 1);
+ set_register(mCompleteCode, LREG_SP, mTotalSize - 1);
+
+ set_register(mCompleteCode, LREG_GVR, offset);
+
+ memcpy(mCompleteCode + offset, mGlobalVariables->mCodeChunk, mGlobalVariables->mCurrentOffset);
+ offset += mGlobalVariables->mCurrentOffset;
+
+ set_register(mCompleteCode, LREG_GFR, offset);
+
+ memcpy(mCompleteCode + offset, mGlobalFunctions->mCodeChunk, mGlobalFunctions->mCurrentOffset);
+ offset += mGlobalFunctions->mCurrentOffset;
+
+ set_register(mCompleteCode, LREG_SR, offset);
+ // zero is, by definition the default state
+ set_register(mCompleteCode, LREG_CS, 0);
+ set_register(mCompleteCode, LREG_NS, 0);
+ set_event_register(mCompleteCode, LREG_CE, LSCRIPTStateBitField[LSTT_STATE_ENTRY], LSL2_CURRENT_MAJOR_VERSION);
+ S32 default_state_offset = 0;
+ if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
+ {
+ default_state_offset = 8;
+ }
+ else
+ {
+ default_state_offset = 4;
+ }
+ set_event_register(mCompleteCode, LREG_ER, bytestream2u64(mStates->mCodeChunk, default_state_offset), LSL2_CURRENT_MAJOR_VERSION);
+
+ memcpy(mCompleteCode + offset, mStates->mCodeChunk, mStates->mCurrentOffset);
+ offset += mStates->mCurrentOffset;
+
+ set_register(mCompleteCode, LREG_HR, offset);
+
+ memcpy(mCompleteCode + offset, mHeap->mCodeChunk, mHeap->mCurrentOffset);
+ offset += mHeap->mCurrentOffset;
+
+ set_register(mCompleteCode, LREG_HP, offset);
+ set_register(mCompleteCode, LREG_FR, 0);
+ set_register(mCompleteCode, LREG_SLR, 0);
+ set_register(mCompleteCode, LREG_ESR, 0);
+ set_register(mCompleteCode, LREG_PR, 0);
+ set_register(mCompleteCode, LREG_TM, mTotalSize);
+
+
+ fwrite(mCompleteCode, 1, mTotalSize, bcfp);
+ }
+ else
+ {
+ gErrorToText.writeError(efp, 0, 0, LSERROR_ASSEMBLE_OUT_OF_MEMORY);
+ }
+}
+
+LLScriptScriptCodeChunk *gScriptCodeChunk;
diff --git a/indra/lscript/lscript_compile/lscript_bytecode.h b/indra/lscript/lscript_compile/lscript_bytecode.h
new file mode 100644
index 0000000000..afe7f9411b
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_bytecode.h
@@ -0,0 +1,71 @@
+/**
+ * @file lscript_bytecode.h
+ * @brief classes to build actual bytecode
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_BYTECODE_H
+#define LL_LSCRIPT_BYTECODE_H
+
+#include "lscript_byteconvert.h"
+#include "lscript_scope.h"
+
+class LLScriptJumpTable
+{
+public:
+ LLScriptJumpTable();
+ ~LLScriptJumpTable();
+
+ void addLabel(char *name, S32 offset);
+ void addJump(char *name, S32 offset);
+
+ LLMap<char *, S32 *> mLabelMap;
+ LLMap<char *, S32 *> mJumpMap;
+};
+
+class LLScriptByteCodeChunk
+{
+public:
+ LLScriptByteCodeChunk(BOOL b_need_jumps);
+ ~LLScriptByteCodeChunk();
+
+ void addByte(U8 byte);
+ void addU16(U16 data);
+ void addBytes(U8 *bytes, S32 size);
+ void addBytes(char *bytes, S32 size);
+ void addBytes(S32 size);
+ void addBytesDontInc(S32 size);
+ void addInteger(S32 value);
+ void addFloat(F32 value);
+ void addLabel(char *name);
+ void addJump(char *name);
+ void connectJumps();
+
+ U8 *mCodeChunk;
+ S32 mCurrentOffset;
+ LLScriptJumpTable *mJumpTable;
+};
+
+class LLScriptScriptCodeChunk
+{
+public:
+ LLScriptScriptCodeChunk(S32 total_size);
+ ~LLScriptScriptCodeChunk();
+
+ void build(FILE *efp, FILE *bcfp);
+
+ LLScriptByteCodeChunk *mRegisters;
+ LLScriptByteCodeChunk *mGlobalVariables;
+ LLScriptByteCodeChunk *mGlobalFunctions;
+ LLScriptByteCodeChunk *mStates;
+ LLScriptByteCodeChunk *mHeap;
+ S32 mTotalSize;
+ U8 *mCompleteCode;
+};
+
+extern LLScriptScriptCodeChunk *gScriptCodeChunk;
+
+#endif
+
diff --git a/indra/lscript/lscript_compile/lscript_error.cpp b/indra/lscript/lscript_compile/lscript_error.cpp
new file mode 100644
index 0000000000..0bc51a65ed
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_error.cpp
@@ -0,0 +1,77 @@
+/**
+ * @file lscript_error.cpp
+ * @brief error reporting class and strings
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_error.h"
+
+S32 gColumn = 0;
+S32 gLine = 0;
+S32 gInternalColumn = 0;
+S32 gInternalLine = 0;
+
+LLScriptGenerateErrorText gErrorToText;
+
+void LLScriptFilePosition::fdotabs(FILE *fp, S32 tabs, S32 tabsize)
+{
+ S32 i;
+ for (i = 0; i < tabs * tabsize; i++)
+ {
+ fprintf(fp, " ");
+ }
+}
+
+char *gWarningText[LSWARN_EOF] =
+{
+ "INVALID",
+ "Dead code found beyond return statement"
+};
+
+char *gErrorText[LSERROR_EOF] =
+{
+ "INVALID",
+ "Syntax error",
+ "Not all code paths return a value",
+ "Function returns a value but return statement doesn't",
+ "Return statement type doesn't match function return type",
+ "Global functions can't change state",
+ "Name previously declared within scope",
+ "Name not defined within scope",
+ "Type mismatch",
+ "Expression must act on LValue",
+ "Byte code assembly failed -- out of memory",
+ "Function call mismatches type or number of arguments",
+ "Use of vector or quaternion method on incorrect type",
+ "Lists can't be included in lists",
+ "Unitialized variables can't be included in lists",
+ "Declaration requires a new scope -- use { and }"
+};
+
+void LLScriptGenerateErrorText::writeWarning(FILE *fp, LLScriptFilePosition *pos, LSCRIPTWarnings warning)
+{
+ fprintf(fp, "(%d, %d) : WARNING : %s\n", pos->mLineNumber, pos->mColumnNumber, gWarningText[warning]);
+ mTotalWarnings++;
+}
+
+void LLScriptGenerateErrorText::writeWarning(FILE *fp, S32 line, S32 col, LSCRIPTWarnings warning)
+{
+ fprintf(fp, "(%d, %d) : WARNING : %s\n", line, col, gWarningText[warning]);
+ mTotalWarnings++;
+}
+
+void LLScriptGenerateErrorText::writeError(FILE *fp, LLScriptFilePosition *pos, LSCRIPTErrors error)
+{
+ fprintf(fp, "(%d, %d) : ERROR : %s\n", pos->mLineNumber, pos->mColumnNumber, gErrorText[error]);
+ mTotalErrors++;
+}
+
+void LLScriptGenerateErrorText::writeError(FILE *fp, S32 line, S32 col, LSCRIPTErrors error)
+{
+ fprintf(fp, "(%d, %d) : ERROR : %s\n", line, col, gErrorText[error]);
+ mTotalErrors++;
+}
diff --git a/indra/lscript/lscript_compile/lscript_error.h b/indra/lscript/lscript_compile/lscript_error.h
new file mode 100644
index 0000000000..4ad7b60dd8
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_error.h
@@ -0,0 +1,132 @@
+/**
+ * @file lscript_error.h
+ * @brief error reporting class and strings
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_ERROR_H
+#define LL_LSCRIPT_ERROR_H
+
+#include <stdio.h>
+#include "stdtypes.h"
+#include "lscript_scope.h"
+
+
+typedef enum e_lscript_compile_pass
+{
+ LSCP_INVALID,
+ LSCP_PRETTY_PRINT,
+ LSCP_PRUNE,
+ LSCP_SCOPE_PASS1,
+ LSCP_SCOPE_PASS2,
+ LSCP_TYPE,
+ LSCP_RESOURCE,
+ LSCP_EMIT_ASSEMBLY,
+ LSCP_EMIT_BYTE_CODE,
+ LSCP_DETERMINE_HANDLERS,
+ LSCP_LIST_BUILD_SIMPLE,
+ LSCP_TO_STACK,
+ LSCP_BUILD_FUNCTION_ARGS,
+ LSCP_EMIT_CIL_ASSEMBLY,
+ LSCP_EOF
+} LSCRIPTCompilePass;
+
+typedef enum e_lscript_prune_type
+{
+ LSPRUNE_INVALID,
+ LSPRUNE_GLOBAL_VOIDS,
+ LSPRUNE_GLOBAL_NON_VOIDS,
+ LSPRUNE_EVENTS,
+ LSPRUNE_DEAD_CODE,
+ LSPRUNE_EOF
+} LSCRIPTPruneType;
+
+extern S32 gColumn;
+extern S32 gLine;
+extern S32 gInternalColumn;
+extern S32 gInternalLine;
+
+
+// used to describe where in the file this piece is
+class LLScriptByteCodeChunk;
+
+class LLScriptLibData;
+
+class LLScriptFilePosition
+{
+public:
+ LLScriptFilePosition(S32 line, S32 col)
+ : mLineNumber(line), mColumnNumber(col), mByteOffset(0), mByteSize(0)
+ {
+ }
+
+ virtual ~LLScriptFilePosition() {}
+
+ virtual void recurse(FILE *fp, S32 tabs, S32 tabsize,
+ LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg,
+ LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count,
+ LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata) = 0;
+ virtual S32 getSize() = 0;
+
+ void fdotabs(FILE *fp, S32 tabs, S32 tabsize);
+
+ S32 mLineNumber;
+ S32 mColumnNumber;
+
+ S32 mByteOffset;
+ S32 mByteSize;
+};
+
+typedef enum e_lscript_warnings
+{
+ LSWARN_INVALID,
+ LSWARN_DEAD_CODE,
+ LSWARN_EOF
+} LSCRIPTWarnings;
+
+typedef enum e_lscript_errors
+{
+ LSERROR_INVALID,
+ LSERROR_SYNTAX_ERROR,
+ LSERROR_NO_RETURN,
+ LSERROR_INVALID_VOID_RETURN,
+ LSERROR_INVALID_RETURN,
+ LSERROR_STATE_CHANGE_IN_GLOBAL,
+ LSERROR_DUPLICATE_NAME,
+ LSERROR_UNDEFINED_NAME,
+ LSERROR_TYPE_MISMATCH,
+ LSERROR_EXPRESSION_ON_LVALUE,
+ LSERROR_ASSEMBLE_OUT_OF_MEMORY,
+ LSERROR_FUNCTION_TYPE_ERROR,
+ LSERROR_VECTOR_METHOD_ERROR,
+ LSERROR_NO_LISTS_IN_LISTS,
+ LSERROR_NO_UNITIALIZED_VARIABLES_IN_LISTS,
+ LSERROR_NEED_NEW_SCOPE,
+ LSERROR_EOF
+} LSCRIPTErrors;
+
+class LLScriptGenerateErrorText
+{
+public:
+ LLScriptGenerateErrorText() { init(); }
+ ~LLScriptGenerateErrorText() {}
+
+ void init() { mTotalErrors = 0; mTotalWarnings = 0; }
+
+ void writeWarning(FILE *fp, LLScriptFilePosition *pos, LSCRIPTWarnings warning);
+ void writeWarning(FILE *fp, S32 line, S32 col, LSCRIPTWarnings warning);
+ void writeError(FILE *fp, LLScriptFilePosition *pos, LSCRIPTErrors error);
+ void writeError(FILE *fp, S32 line, S32 col, LSCRIPTErrors error);
+
+ BOOL getErrors() { return mTotalErrors; }
+ BOOL getWarnings() { return mTotalWarnings; }
+
+ S32 mTotalErrors;
+ S32 mTotalWarnings;
+};
+
+extern LLScriptGenerateErrorText gErrorToText;
+
+#endif
diff --git a/indra/lscript/lscript_compile/lscript_heap.cpp b/indra/lscript/lscript_compile/lscript_heap.cpp
new file mode 100644
index 0000000000..98c5fe37be
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_heap.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file lscript_heap.cpp
+ * @brief classes to manage script heap
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if 0
+
+#include "linden_common.h"
+
+#include "lscript_heap.h"
+
+LLScriptHeapEntry::LLScriptHeapEntry(U8 *entry)
+: mEntry(entry)
+{
+ S32 offset = 0;
+ mNext = bytestream2integer(entry, offset);
+ mRefCount = bytestream2integer(entry, offset);
+ mType = *(entry + offset);
+ mData = entry + offset;
+ mListOffset = offset;
+}
+
+LLScriptHeapEntry::LLScriptHeapEntry(U8 *heap, S32 offset)
+: mNext(0x9), mType(0), mRefCount(0), mEntry(heap + offset), mData(heap + offset + 0x9), mListOffset(0x9)
+{
+}
+
+LLScriptHeapEntry::~LLScriptHeapEntry()
+{
+}
+
+void LLScriptHeapEntry::addString(char *string)
+{
+ S32 size = strlen(string) + 1;
+ S32 offset = 0;
+ memcpy(mData, string, size);
+ mNext += size;
+ integer2bytestream(mEntry, offset, mNext);
+ mRefCount++;
+ integer2bytestream(mEntry, offset, mRefCount);
+ *(mEntry + offset) = LSCRIPTTypeByte[LST_STRING];
+}
+
+
+
+#endif
diff --git a/indra/lscript/lscript_compile/lscript_heap.h b/indra/lscript/lscript_compile/lscript_heap.h
new file mode 100644
index 0000000000..5b04ba768a
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_heap.h
@@ -0,0 +1,40 @@
+/**
+ * @file lscript_heap.h
+ * @brief classes to manage script heap
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if 0
+
+#ifndef LL_LSCRIPT_HEAP_H
+#define LL_LSCRIPT_HEAP_H
+
+#include "lscript_byteconvert.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "llquaternion.h"
+
+class LLScriptHeapEntry
+{
+public:
+ LLScriptHeapEntry(U8 *entry);
+ LLScriptHeapEntry(U8 *heap, S32 offset);
+ ~LLScriptHeapEntry();
+
+ void addString(char *string);
+
+ S32 mNext;
+ U8 mType;
+ S32 mRefCount;
+ S32 mListOffset;
+ U8 *mEntry;
+ U8 *mData;
+ U8 *mListEntry;
+};
+
+#endif
+
+#endif
+
diff --git a/indra/lscript/lscript_compile/lscript_resource.cpp b/indra/lscript/lscript_compile/lscript_resource.cpp
new file mode 100644
index 0000000000..147cb093b5
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_resource.cpp
@@ -0,0 +1,18 @@
+/**
+ * @file lscript_resource.cpp
+ * @brief resource determination prior to assembly
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_resource.h"
+
+void init_temp_jumps()
+{
+ gTempJumpCount = 0;
+}
+
+S32 gTempJumpCount = 0;
diff --git a/indra/lscript/lscript_compile/lscript_resource.h b/indra/lscript/lscript_compile/lscript_resource.h
new file mode 100644
index 0000000000..b0a38b81fb
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_resource.h
@@ -0,0 +1,21 @@
+/**
+ * @file lscript_resource.h
+ * @brief resource determination prior to assembly
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_RESOURCE_H
+#define LL_LSCRIPT_RESOURCE_H
+
+#include <stdio.h>
+#include "stdtypes.h"
+#include "lscript_scope.h"
+
+void init_temp_jumps();
+
+extern S32 gTempJumpCount;
+
+#endif
+
diff --git a/indra/lscript/lscript_compile/lscript_scope.cpp b/indra/lscript/lscript_compile/lscript_scope.cpp
new file mode 100644
index 0000000000..a2eeceb9c6
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_scope.cpp
@@ -0,0 +1,13 @@
+/**
+ * @file lscript_scope.cpp
+ * @brief builds nametable and checks scope
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_tree.h"
+
+LLStringTable *gScopeStringTable;
diff --git a/indra/lscript/lscript_compile/lscript_scope.h b/indra/lscript/lscript_compile/lscript_scope.h
new file mode 100644
index 0000000000..18640441af
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_scope.h
@@ -0,0 +1,388 @@
+/**
+ * @file lscript_scope.h
+ * @brief builds nametable and checks scope
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_SCOPE_H
+#define LL_LSCRIPT_SCOPE_H
+
+#include "string_table.h"
+#include "llmap.h"
+#include "lscript_byteformat.h"
+
+typedef enum e_lscript_identifier_type
+{
+ LIT_INVALID,
+ LIT_GLOBAL,
+ LIT_VARIABLE,
+ LIT_FUNCTION,
+ LIT_LABEL,
+ LIT_STATE,
+ LIT_HANDLER,
+ LIT_LIBRARY_FUNCTION,
+ LIT_EOF
+} LSCRIPTIdentifierType;
+
+const char LSCRIPTFunctionTypeStrings[LST_EOF] =
+{
+ '0',
+ 'i',
+ 'f',
+ 's',
+ 'k',
+ 'v',
+ 'q',
+ 'l',
+ '0'
+};
+
+const char * const LSCRIPTListDescription[LST_EOF] =
+{
+ "PUSHARGB 0",
+ "PUSHARGB 1",
+ "PUSHARGB 2",
+ "PUSHARGB 3",
+ "PUSHARGB 4",
+ "PUSHARGB 5",
+ "PUSHARGB 6",
+ "PUSHARGB 7",
+ "PUSHARGB 0"
+};
+
+const char * const LSCRIPTTypePush[LST_EOF] =
+{
+ "INVALID",
+ "PUSHE",
+ "PUSHE",
+ "PUSHE",
+ "PUSHE",
+ "PUSHEV",
+ "PUSHEQ",
+ "PUSHE",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeReturn[LST_EOF] =
+{
+ "INVALID",
+ "LOADP -12",
+ "LOADP -12",
+ "STORES -12\nPOP",
+ "STORES -12\nPOP",
+ "LOADVP -20",
+ "LOADQP -24",
+ "LOADLP -12",
+ "undefined"
+};
+
+const char * const LSCRIPTTypePop[LST_EOF] =
+{
+ "INVALID",
+ "POP",
+ "POP",
+ "POPS",
+ "POPS",
+ "POPV",
+ "POPQ",
+ "POPL",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeDuplicate[LST_EOF] =
+{
+ "INVALID",
+ "DUP",
+ "DUP",
+ "DUPS",
+ "DUPS",
+ "DUPV",
+ "DUPQ",
+ "DUPL",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeLocalStore[LST_EOF] =
+{
+ "INVALID",
+ "STORE ",
+ "STORE ",
+ "STORES ",
+ "STORES ",
+ "STOREV ",
+ "STOREQ ",
+ "STOREL ",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeLocalDeclaration[LST_EOF] =
+{
+ "INVALID",
+ "STOREP ",
+ "STOREP ",
+ "STORESP ",
+ "STORESP ",
+ "STOREVP ",
+ "STOREQP ",
+ "STORELP ",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeGlobalStore[LST_EOF] =
+{
+ "INVALID",
+ "STOREG ",
+ "STOREG ",
+ "STORESG ",
+ "STORESG ",
+ "STOREGV ",
+ "STOREGQ ",
+ "STORELG ",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeLocalPush[LST_EOF] =
+{
+ "INVALID",
+ "PUSH ",
+ "PUSH ",
+ "PUSHS ",
+ "PUSHS ",
+ "PUSHV ",
+ "PUSHQ ",
+ "PUSHL ",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeLocalPush1[LST_EOF] =
+{
+ "INVALID",
+ "PUSHARGI 1",
+ "PUSHARGF 1",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined",
+ "undefined"
+};
+
+const char * const LSCRIPTTypeGlobalPush[LST_EOF] =
+{
+ "INVALID",
+ "PUSHG ",
+ "PUSHG ",
+ "PUSHGS ",
+ "PUSHGS ",
+ "PUSHGV ",
+ "PUSHGQ ",
+ "PUSHGL ",
+ "undefined"
+};
+
+class LLScriptSimpleAssignable;
+
+class LLScriptArgString
+{
+public:
+ LLScriptArgString() : mString(NULL) {}
+ ~LLScriptArgString() { delete [] mString; }
+
+ LSCRIPTType getType(S32 count)
+ {
+ if (!mString)
+ return LST_NULL;
+ S32 length = (S32)strlen(mString);
+ if (count >= length)
+ {
+ return LST_NULL;
+ }
+ switch(mString[count])
+ {
+ case 'i':
+ return LST_INTEGER;
+ case 'f':
+ return LST_FLOATINGPOINT;
+ case 's':
+ return LST_STRING;
+ case 'k':
+ return LST_KEY;
+ case 'v':
+ return LST_VECTOR;
+ case 'q':
+ return LST_QUATERNION;
+ case 'l':
+ return LST_LIST;
+ default:
+ return LST_NULL;
+ }
+ }
+
+ void addType(LSCRIPTType type)
+ {
+ S32 count = 0;
+ if (mString)
+ {
+ count = (S32)strlen(mString);
+ char *temp = new char[count + 2];
+ memcpy(temp, mString, count);
+ delete [] mString;
+ mString = temp;
+ mString[count + 1] = 0;
+ }
+ else
+ {
+ mString = new char[count + 2];
+ mString[count + 1] = 0;
+ }
+ mString[count++] = LSCRIPTFunctionTypeStrings[type];
+ }
+
+ S32 getNumber()
+ {
+ if (mString)
+ return (S32)strlen(mString);
+ else
+ return 0;
+ }
+
+ char *mString;
+};
+
+class LLScriptScopeEntry
+{
+public:
+ LLScriptScopeEntry(char *identifier, LSCRIPTIdentifierType idtype, LSCRIPTType type, S32 count = 0)
+ : mIdentifier(identifier), mIDType(idtype), mType(type), mOffset(0), mSize(0), mAssignable(NULL), mCount(count), mLibraryNumber(0)
+ {
+ }
+
+ ~LLScriptScopeEntry() {}
+
+ char *mIdentifier;
+ LSCRIPTIdentifierType mIDType;
+ LSCRIPTType mType;
+ S32 mOffset;
+ S32 mSize;
+ LLScriptSimpleAssignable *mAssignable;
+ S32 mCount; // NOTE: Index for locals in CIL.
+ U16 mLibraryNumber;
+ LLScriptArgString mFunctionArgs;
+ LLScriptArgString mLocals;
+};
+
+class LLScriptScope
+{
+public:
+ LLScriptScope(LLStringTable *stable)
+ : mParentScope(NULL), mSTable(stable), mFunctionCount(0), mStateCount(0)
+ {
+ }
+
+ ~LLScriptScope()
+ {
+ mEntryMap.deleteAllData();
+ }
+
+ LLScriptScopeEntry *addEntry(char *identifier, LSCRIPTIdentifierType idtype, LSCRIPTType type)
+ {
+ char *name = mSTable->addString(identifier);
+ if (!mEntryMap.checkData(name))
+ {
+ if (idtype == LIT_FUNCTION)
+ mEntryMap[name] = new LLScriptScopeEntry(name, idtype, type, mFunctionCount++);
+ else if (idtype == LIT_STATE)
+ mEntryMap[name] = new LLScriptScopeEntry(name, idtype, type, mStateCount++);
+ else
+ mEntryMap[name] = new LLScriptScopeEntry(name, idtype, type);
+ return mEntryMap[name];
+ }
+ else
+ {
+ // identifier already exists at this scope
+ return NULL;
+ }
+ }
+
+ BOOL checkEntry(char *identifier)
+ {
+ char *name = mSTable->addString(identifier);
+ if (mEntryMap.checkData(name))
+ {
+ return TRUE;
+ }
+ else
+ {
+ // identifier already exists at this scope
+ return FALSE;
+ }
+ }
+
+ LLScriptScopeEntry *findEntry(char *identifier)
+ {
+ char *name = mSTable->addString(identifier);
+ LLScriptScope *scope = this;
+
+ while (scope)
+ {
+ if (scope->mEntryMap.checkData(name))
+ {
+ // cool, we found it at this scope
+ return scope->mEntryMap[name];
+ }
+ scope = scope->mParentScope;
+ }
+ return NULL;
+ }
+
+ LLScriptScopeEntry *findEntryTyped(char *identifier, LSCRIPTIdentifierType idtype)
+ {
+ char *name = mSTable->addString(identifier);
+ LLScriptScope *scope = this;
+
+ while (scope)
+ {
+ if (scope->mEntryMap.checkData(name))
+ {
+ // need to check type, and if type is function we need to check both types
+ if (idtype == LIT_FUNCTION)
+ {
+ if (scope->mEntryMap[name]->mIDType == LIT_FUNCTION)
+ {
+ return scope->mEntryMap[name];
+ }
+ else if (scope->mEntryMap[name]->mIDType == LIT_LIBRARY_FUNCTION)
+ {
+ return scope->mEntryMap[name];
+ }
+ }
+ else if (scope->mEntryMap[name]->mIDType == idtype)
+ {
+ // cool, we found it at this scope
+ return scope->mEntryMap[name];
+ }
+ }
+ scope = scope->mParentScope;
+ }
+ return NULL;
+ }
+
+ void addParentScope(LLScriptScope *scope)
+ {
+ mParentScope = scope;
+ }
+
+ LLMap<char *, LLScriptScopeEntry *> mEntryMap;
+ LLScriptScope *mParentScope;
+ LLStringTable *mSTable;
+ S32 mFunctionCount;
+ S32 mStateCount;
+};
+
+extern LLStringTable *gScopeStringTable;
+
+
+
+#endif
diff --git a/indra/lscript/lscript_compile/lscript_tree.cpp b/indra/lscript/lscript_compile/lscript_tree.cpp
new file mode 100644
index 0000000000..4b4a7f13f4
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_tree.cpp
@@ -0,0 +1,9998 @@
+/**
+ * @file lscript_tree.cpp
+ * @brief implements methods for lscript_tree.h classes
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// TO DO: Move print functionality from .h file to here
+
+#include "linden_common.h"
+
+#include "lscript_tree.h"
+#include "lscript_typecheck.h"
+#include "lscript_resource.h"
+#include "lscript_bytecode.h"
+#include "lscript_heap.h"
+#include "lscript_library.h"
+#include "lscript_alloc.h"
+
+//#define LSL_INCLUDE_DEBUG_INFO
+
+void print_cil_box(FILE* fp, LSCRIPTType type)
+{
+ switch(type)
+ {
+ case LST_INTEGER:
+ fprintf(fp, "box [mscorlib]System.Int32\n");
+ break;
+ case LST_FLOATINGPOINT:
+ fprintf(fp, "box [mscorlib]System.Double\n");
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ fprintf(fp, "box [mscorlib]System.String\n");
+ break;
+ case LST_VECTOR:
+ fprintf(fp, "box [LScriptLibrary]LLVector\n");
+ break;
+ case LST_QUATERNION:
+ fprintf(fp, "box [LScriptLibrary]LLQuaternion\n");
+ break;
+ default:
+ break;
+ }
+}
+
+void print_cil_type(FILE* fp, LSCRIPTType type)
+{
+ switch(type)
+ {
+ case LST_INTEGER:
+ fprintf(fp, "int32");
+ break;
+ case LST_FLOATINGPOINT:
+ fprintf(fp, "float32");
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ fprintf(fp, "string");
+ break;
+ case LST_VECTOR:
+ fprintf(fp, "valuetype [LScriptLibrary]LLVector");
+ break;
+ case LST_QUATERNION:
+ fprintf(fp, "valuetype [LScriptLibrary]LLQuaternion");
+ break;
+ case LST_LIST:
+ fprintf(fp, "class [mscorlib]System.Collections.ArrayList");
+ break;
+ case LST_NULL:
+ fprintf(fp, "void");
+ break;
+ default:
+ break;
+ }
+}
+
+void LLScriptType::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp,"%s",LSCRIPTTypeNames[mType]);
+ break;
+ case LSCP_TYPE:
+ type = mType;
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ print_cil_type(fp, mType);
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptType::getSize()
+{
+ return LSCRIPTDataSize[mType];
+}
+
+void LLScriptConstant::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp,"Script Constant Base class -- should never get here!\n");
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptConstant::getSize()
+{
+ printf("Script Constant Base class -- should never get here!\n");
+ return 0;
+}
+
+
+
+void LLScriptConstantInteger::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "%d", mValue);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "PUSHARGI %d\n", mValue);
+ break;
+ case LSCP_TYPE:
+ type = mType;
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ chunk->addInteger(mValue);
+ type = mType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(mValue);
+ type = mType;
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ *ldata = new LLScriptLibData(mValue);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "ldc.i4 %d\n", mValue);
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptConstantInteger::getSize()
+{
+ return LSCRIPTDataSize[LST_INTEGER];
+}
+
+void LLScriptConstantFloat::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "%5.5f", mValue);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "PUSHARGF %5.5f\n", mValue);
+ break;
+ case LSCP_TYPE:
+ type = mType;
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ chunk->addFloat(mValue);
+ type = mType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGF]);
+ chunk->addFloat(mValue);
+ type = mType;
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ *ldata = new LLScriptLibData(mValue);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "ldc.r8 %5.5f\n", mValue); // NOTE: Precision?
+ default:
+ break;
+ }
+}
+
+S32 LLScriptConstantFloat::getSize()
+{
+ return LSCRIPTDataSize[LST_FLOATINGPOINT];
+}
+
+void print_escape_quotes(FILE* fp, const char* str)
+{
+ putc('"', fp);
+ for(const char* c = str; *c != '\0'; ++c)
+ {
+ if(*c == '"')
+ {
+ putc('\\', fp);
+ }
+ putc(*c, fp);
+ }
+ putc('"', fp);
+}
+
+void LLScriptConstantString::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "\"%s\"", mValue);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "PUSHARGS \"%s\"\n", mValue);
+ fprintf(fp, "STACKTOS %lu\n", strlen(mValue) + 1);
+ break;
+ case LSCP_TYPE:
+ type = mType;
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ chunk->addInteger(heap->mCurrentOffset + 1);
+ LLScriptLibData *data = new LLScriptLibData(mValue);
+ U8 *temp;
+ S32 size = lsa_create_data_block(&temp, data, heap->mCurrentOffset);
+
+ heap->addBytes(temp, size);
+ delete [] temp;
+ delete data;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGS]);
+ chunk->addBytes(mValue, (S32)strlen(mValue) + 1);
+ type = mType;
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ *ldata = new LLScriptLibData(mValue);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "ldstr ");
+ print_escape_quotes(fp, mValue);
+ fprintf(fp, "\n");
+ default:
+ break;
+ }
+}
+
+S32 LLScriptConstantString::getSize()
+{
+ return (S32)strlen(mValue) + 1;
+}
+
+
+void LLScriptIdentifier::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "%s", mName);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mScopeEntry)
+ {
+ if (mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ fprintf(fp, "$BP + %d [%s]", mScopeEntry->mOffset, mName);
+ }
+ else if (mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "$GVR + %d [%s]", mScopeEntry->mOffset, mName);
+ }
+ else
+ {
+ fprintf(fp, "%s", mName);
+ }
+ }
+ break;
+ case LSCP_TYPE:
+ if (mScopeEntry)
+ type = mScopeEntry->mType;
+ else
+ type = LST_NULL;
+ break;
+ case LSCP_RESOURCE:
+ if (mScopeEntry)
+ {
+ if (mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+// fprintf(fp, "LOCAL : %d : %d : %s\n", mScopeEntry->mOffset, mScopeEntry->mSize, mName);
+ }
+ else if (mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+// fprintf(fp, "GLOBAL: %d : %d : %s\n", mScopeEntry->mOffset, mScopeEntry->mSize, mName);
+ }
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ if (mScopeEntry)
+ {
+ if (mScopeEntry->mType == LST_LIST)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_NO_LISTS_IN_LISTS);
+ }
+ else if (mScopeEntry->mAssignable)
+ {
+ mScopeEntry->mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, ldata);
+ }
+ else
+ {
+ gErrorToText.writeError(fp, this, LSERROR_NO_UNITIALIZED_VARIABLES_IN_LISTS);
+ }
+ }
+ else
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "%s", mName);
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptIdentifier::getSize()
+{
+
+ return 0;
+}
+
+
+
+void LLScriptSimpleAssignable::addAssignable(LLScriptSimpleAssignable *assign)
+{
+ if (mNextp)
+ {
+ assign->mNextp = mNextp;
+ }
+ mNextp = assign;
+}
+
+void LLScriptSimpleAssignable::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ fprintf(fp, "Simple Assignable Base Class -- should never get here!\n");
+}
+
+S32 LLScriptSimpleAssignable::getSize()
+{
+
+ printf("Simple Assignable Base Class -- should never get here!\n");
+ return 0;
+}
+
+void LLScriptSAIdentifier::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ {
+ LLScriptScopeEntry *entry = scope->findEntry(mIdentifier->mName);
+ if (!entry)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ else
+ {
+ // if we did find it, make sure this identifier is associated with the correct scope entry
+ mIdentifier->mScopeEntry = entry;
+ }
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ if (mIdentifier->mScopeEntry)
+ {
+ if(mIdentifier->mScopeEntry->mAssignable)
+ {
+ mIdentifier->mScopeEntry->mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ // Babbage: 29/8/06: If the scope entry has no mAssignable,
+ // set the default type and add the default 0 value to the
+ // chunk. Without this SAVectors and SAQuaternions will
+ // assume the arbitrary current type is the assignable type
+ // and may attempt to access a null chunk. (SL-20156)
+ type = mIdentifier->mScopeEntry->mType;
+ chunk->addBytes(LSCRIPTDataSize[type]);
+ }
+ }
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, ldata);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, &(*ldata)->mListp);
+ }
+ }
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptSAIdentifier::getSize()
+{
+ return mIdentifier->getSize();
+}
+
+void LLScriptSAConstant::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, ldata);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, &(*ldata)->mListp);
+ }
+ }
+ break;
+ default:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptSAConstant::getSize()
+{
+ return mConstant->getSize();
+}
+
+void print_cil_cast(FILE* fp, LSCRIPTType srcType, LSCRIPTType targetType)
+{
+ switch(srcType)
+ {
+ case LST_INTEGER:
+ switch(targetType)
+ {
+ case LST_FLOATINGPOINT:
+ fprintf(fp, "conv.r8\n");
+ break;
+ case LST_STRING:
+ fprintf(fp, "call string class [mscorlib]System.Convert::ToString(int32)\n");
+ break;
+ case LST_LIST:
+ fprintf(fp, "box [mscorlib]System.Int32\n");
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList class [LScriptLibrary]LScriptInternal::CreateList()\n");
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList class [LScriptLibrary]LScriptInternal::AddReturnList(object, class [mscorlib]System.Collections.ArrayList)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ switch(targetType)
+ {
+ case LST_INTEGER:
+ fprintf(fp, "conv.i4\n");
+ break;
+ case LST_STRING:
+ fprintf(fp, "call string class [mscorlib]System.Convert::ToString(float32)\n");
+ break;
+ case LST_LIST:
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList [LScriptLibrary]LScriptInternal::CreateList(object)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_STRING:
+ switch(targetType)
+ {
+ case LST_INTEGER:
+ fprintf(fp, "call int32 valuetype [mscorlib]System.Int32::Parse(string)\n");
+ break;
+ case LST_FLOATINGPOINT:
+ fprintf(fp, "call float64 valuetype [mscorlib]System.Double::Parse(string)\n");
+ break;
+ case LST_LIST:
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList [LScriptLibrary]LScriptInternal::CreateList(object)\n");
+ break;
+ case LST_VECTOR:
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'Parse'(string)\n");
+ break;
+ case LST_QUATERNION:
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'Parse'(string)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_KEY:
+ switch(targetType)
+ {
+ case LST_KEY:
+ break;
+ case LST_STRING:
+ break;
+ case LST_LIST:
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList [LScriptLibrary]LScriptInternal::CreateList(object)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_VECTOR:
+ switch(targetType)
+ {
+ case LST_VECTOR:
+ break;
+ case LST_STRING:
+ fprintf(fp, "call string valuetype [LScriptLibrary]LLVector::'ToString'(valuetype [LScriptLibrary]LLVector)\n");
+ break;
+ case LST_LIST:
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList [LScriptLibrary]LScriptInternal::CreateList(object)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_QUATERNION:
+ switch(targetType)
+ {
+ case LST_QUATERNION:
+ break;
+ case LST_STRING:
+ fprintf(fp, "call string valuetype [LScriptLibrary]LLQuaternion::'ToString'(valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+ case LST_LIST:
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList [LScriptLibrary]LScriptInternal::CreateList(object)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ case LST_LIST:
+ switch(targetType)
+ {
+ case LST_LIST:
+ break;
+ case LST_STRING:
+ fprintf(fp, "call string [LScriptLibrary]LScriptInternal::ListToString(class [mscorlib]System.Collections.ArrayList)\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+bool is_SA_constant_integer(LLScriptSimpleAssignable* sa)
+{
+ // HACK: Downcast based on type.
+ return (sa->mType == LSSAT_CONSTANT && ((LLScriptSAConstant*) sa)->mConstant->mType == LST_INTEGER);
+}
+
+void LLScriptSAVector::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "< ");
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >");
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ // vector's take floats
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = LST_VECTOR;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ LLScriptByteCodeChunk *list = new LLScriptByteCodeChunk(FALSE);
+ mEntry3->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ LLVector3 vec;
+ S32 offset = 0;
+ bytestream2vector(vec, list->mCodeChunk, offset);
+ *ldata = new LLScriptLibData(vec);
+ delete list;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, &(*ldata)->mListp);
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Load arguments.
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry1))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry3))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry3))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+
+ // Call named ctor, which leaves new Vector on stack, so it can be saved in to local or argument just like a primitive type.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'create'(float32, float32, float32)\n");
+
+ // Next.
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptSAVector::getSize()
+{
+ return mEntry1->getSize() + mEntry2->getSize() + mEntry3->getSize();
+}
+
+void LLScriptSAQuaternion::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "< ");
+ mEntry4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >");
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ // vector's take floats
+ mEntry4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = LST_QUATERNION;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ mEntry4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = chunk->mCurrentOffset - 4;
+ bytestream_int2float(chunk->mCodeChunk, offset);
+ }
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_LIST_BUILD_SIMPLE:
+ {
+ LLScriptByteCodeChunk *list = new LLScriptByteCodeChunk(FALSE);
+ mEntry4->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ mEntry3->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ mEntry1->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, list, heap, stacksize, entry, entrycount, NULL);
+ if (type == LST_INTEGER)
+ {
+ S32 offset = list->mCurrentOffset - 4;
+ bytestream_int2float(list->mCodeChunk, offset);
+ }
+ LLQuaternion quat;
+ S32 offset = 0;
+ bytestream2quaternion(quat, list->mCodeChunk, offset);
+ *ldata = new LLScriptLibData(quat);
+ delete list;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, &(*ldata)->mListp);
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Load arguments.
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry1))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry2))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry3))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+ mEntry4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(is_SA_constant_integer(mEntry4))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+
+ // Call named ctor, which leaves new Vector on stack, so it can be saved in to local or argument just like a primitive type.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'create'(float32, float32, float32, float32)\n");
+
+ // Next.
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ mEntry4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEntry3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEntry2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEntry1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptSAQuaternion::getSize()
+{
+ return mEntry1->getSize() + mEntry2->getSize() + mEntry3->getSize() + mEntry4->getSize();
+}
+
+void LLScriptSAList::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "[ ");
+ if (mEntryList)
+ mEntryList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ]");
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mEntryList)
+ mEntryList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ type = LST_LIST;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ LLScriptLibData *list_data = new LLScriptLibData;
+
+ list_data->mType = LST_LIST;
+ if (mEntryList)
+ mEntryList->recurse(fp, tabs, tabsize, LSCP_LIST_BUILD_SIMPLE, ptype, prunearg, scope, type, basetype, count, chunk, NULL, stacksize, entry, entrycount, &(list_data->mListp));
+
+ U8 *temp;
+ chunk->addInteger(heap->mCurrentOffset + 1);
+ S32 size = lsa_create_data_block(&temp, list_data, heap->mCurrentOffset);
+ heap->addBytes(temp, size);
+ delete list_data;
+ delete [] temp;
+
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, LSCP_EMIT_BYTE_CODE, ptype, prunearg, scope, type, basetype, count, chunk, NULL, stacksize, entry, entrycount, NULL);
+ }
+ }
+ break;
+ default:
+ if (mEntryList)
+ mEntryList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, ldata);
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, ldata);
+ }
+ break;
+ }
+}
+
+S32 LLScriptSAList::getSize()
+{
+ return mEntryList->getSize();
+}
+
+void LLScriptGlobalVariable::addGlobal(LLScriptGlobalVariable *global)
+{
+ if (mNextp)
+ {
+ global->mNextp = mNextp;
+ }
+ mNextp = global;
+}
+
+void LLScriptGlobalVariable::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+void LLScriptGlobalVariable::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp,"\t");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mAssignable)
+ {
+ fprintf(fp, " = ");
+ mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp,"\t");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mAssignable)
+ {
+ fprintf(fp, " = ");
+ mAssignable->recurse(fp, tabs, tabsize, LSCP_PRETTY_PRINT, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fprintf(fp, "Offset: %d Type: %d\n", mIdentifier->mScopeEntry->mOffset, (S32)LSCRIPTTypeByte[mType->mType]);
+ }
+ else
+ {
+ fprintf(fp, "\n");
+ fprintf(fp, "Offset: %d Type: %d\n", mIdentifier->mScopeEntry->mOffset, (S32)LSCRIPTTypeByte[mType->mType]);
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ if (mAssignable)
+ {
+ mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ // this needs to go after expression decent to make sure that we don't add ourselves or something silly
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_GLOBAL, mType->mType);
+ if (mIdentifier->mScopeEntry && mAssignable)
+ mIdentifier->mScopeEntry->mAssignable = mAssignable;
+ }
+ break;
+ case LSCP_TYPE:
+ // if the variable has an assignable, it must assignable to the variable's type
+ if (mAssignable)
+ {
+ mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mAssignableType = type;
+ if (!legal_assignment(mType->mType, mAssignableType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ // it also includes the name of the variable as well as the type
+ // plus 4 bytes of offset from it's apparent address to the actual data
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ count += strlen(mIdentifier->mName) + 1 + 1 + 4;
+#else
+ count += 1 + 1 + 4;
+#endif
+ mIdentifier->mScopeEntry->mOffset = (S32)count;
+ mIdentifier->mScopeEntry->mSize = mType->getSize();
+ count += mIdentifier->mScopeEntry->mSize;
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // order for global variables
+ // 0 - 4: offset to actual data
+ S32 offsetoffset = chunk->mCurrentOffset;
+ S32 offsetdelta = 0;
+ chunk->addBytes(4);
+ // type
+ char vtype;
+ vtype = LSCRIPTTypeByte[mType->mType];
+ chunk->addBytes(&vtype, 1);
+ // null terminated name
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ chunk->addBytes(mIdentifier->mName, strlen(mIdentifier->mName) + 1);
+#else
+ chunk->addBytes(1);
+#endif
+ // put correct offset delta in
+ offsetdelta = chunk->mCurrentOffset - offsetoffset;
+ integer2bytestream(chunk->mCodeChunk, offsetoffset, offsetdelta);
+
+ // now we need space for the variable itself
+ LLScriptByteCodeChunk *value = new LLScriptByteCodeChunk(FALSE);
+ if (mAssignable)
+ {
+ mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, value, heap, stacksize, entry, entrycount, NULL);
+ // need to put sneaky type conversion here
+ if (mAssignableType != mType->mType)
+ {
+ // the only legal case that is a problem is int->float
+ if (mType->mType == LST_FLOATINGPOINT && mAssignableType == LST_INTEGER)
+ {
+ S32 offset = value->mCurrentOffset - 4;
+ bytestream_int2float(value->mCodeChunk, offset);
+ }
+ }
+ }
+ else
+ {
+ if ( (mType->mType == LST_STRING)
+ ||(mType->mType == LST_KEY))
+ {
+ // string and keys (even empty ones) need heap entries
+ chunk->addInteger(heap->mCurrentOffset + 1);
+ LLScriptLibData *data = new LLScriptLibData("");
+ U8 *temp;
+ S32 size = lsa_create_data_block(&temp, data, heap->mCurrentOffset);
+
+ heap->addBytes(temp, size);
+ delete [] temp;
+ delete data;
+ }
+ else if (mType->mType == LST_LIST)
+ {
+ chunk->addInteger(heap->mCurrentOffset + 1);
+ LLScriptLibData *data = new LLScriptLibData;
+ data->mType = LST_LIST;
+ U8 *temp;
+ S32 size = lsa_create_data_block(&temp, data, heap->mCurrentOffset);
+
+ heap->addBytes(temp, size);
+ delete [] temp;
+ delete data;
+ }
+ else if (mType->mType == LST_QUATERNION)
+ {
+ chunk->addFloat(1.f);
+ chunk->addFloat(0.f);
+ chunk->addFloat(0.f);
+ chunk->addFloat(0.f);
+ }
+ else
+ {
+ value->addBytes(LSCRIPTDataSize[mType->mType]);
+ }
+ }
+ chunk->addBytes(value->mCodeChunk, value->mCurrentOffset);
+ delete value;
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Initialisation inside ctor.
+ if (mAssignable)
+ {
+ fprintf(fp, "ldarg.0\n");
+ mAssignable->recurse(fp, tabs, tabsize, LSCP_EMIT_CIL_ASSEMBLY, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "stfld ");
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp," LSL::");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+ break;
+ default:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mAssignable)
+ {
+ mAssignable->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptGlobalVariable::getSize()
+{
+ S32 return_size;
+
+ return_size = mType->getSize();
+ return return_size;
+}
+
+void LLScriptEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ fprintf(fp, "Event Base Class -- should never get here!\n");
+}
+
+S32 LLScriptEvent::getSize()
+{
+ printf("Event Base Class -- should never get here!\n");
+ return 0;
+}
+
+void LLScriptStateEntryEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "state_entry()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "state_entry()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "state_entry";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "state_entry()");
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptStateEntryEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptStateExitEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "state_exit()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "state_exit()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "state_exit";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "state_exit()");
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptStateExitEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptTouchStartEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "touch_start( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "touch_start";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mCount->mName, strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptTouchStartEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptTouchEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "touch( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "touch";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mCount->mName, strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptTouchEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptTouchEndEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "touch_end( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "touch_end";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mCount->mName, strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptTouchEndEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptCollisionStartEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "collision_start( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "collision_start";
+ chunk->addBytes(name, (S32)strlen(name) + 1);
+ chunk->addBytes(mCount->mName, (S32)strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptCollisionStartEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptCollisionEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "collision( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "collision";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mCount->mName, strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptCollisionEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptCollisionEndEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "collision_end( integer ");
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mCount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mCount->mScopeEntry = scope->addEntry(mCount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mCount->mScopeEntry)
+ {
+ mCount->mScopeEntry->mOffset = (S32)count;
+ mCount->mScopeEntry->mSize = 4;
+ count += mCount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "collision_end";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mCount->mName, strlen(mCount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mCount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptCollisionEndEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptLandCollisionStartEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "land_collision_start( vector ");
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mPosition->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mPosition->mScopeEntry = scope->addEntry(mPosition->mName, LIT_VARIABLE, LST_VECTOR);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mPosition->mScopeEntry)
+ {
+ mPosition->mScopeEntry->mOffset = (S32)count;
+ mPosition->mScopeEntry->mSize = 12;
+ count += mPosition->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "land_collision_start";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mPosition->mName, strlen(mPosition->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptLandCollisionStartEvent::getSize()
+{
+ // vector = 12
+ return 12;
+}
+
+
+
+void LLScriptLandCollisionEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "land_collision( vector ");
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mPosition->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mPosition->mScopeEntry = scope->addEntry(mPosition->mName, LIT_VARIABLE, LST_VECTOR);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mPosition->mScopeEntry)
+ {
+ mPosition->mScopeEntry->mOffset = (S32)count;
+ mPosition->mScopeEntry->mSize = 12;
+ count += mPosition->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "land_collision";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mPosition->mName, strlen(mPosition->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptLandCollisionEvent::getSize()
+{
+ // vector = 12
+ return 12;
+}
+
+
+void LLScriptLandCollisionEndEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "land_collision_end( vector ");
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mPosition->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mPosition->mScopeEntry = scope->addEntry(mPosition->mName, LIT_VARIABLE, LST_VECTOR);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mPosition->mScopeEntry)
+ {
+ mPosition->mScopeEntry->mOffset = (S32)count;
+ mPosition->mScopeEntry->mSize = 12;
+ count += mPosition->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "land_collision_end";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mPosition->mName, strlen(mPosition->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptLandCollisionEndEvent::getSize()
+{
+ // vector = 12
+ return 12;
+}
+
+
+void LLScriptInventoryEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "changed( integer ");
+ mChange->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mChange->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mChange->mScopeEntry = scope->addEntry(mChange->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mChange->mScopeEntry)
+ {
+ mChange->mScopeEntry->mOffset = (S32)count;
+ mChange->mScopeEntry->mSize = 4;
+ count += mChange->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "changed";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mChange->mName, strlen(mChange->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mChange->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptInventoryEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptAttachEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "attach( key ");
+ mAttach->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mAttach->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mAttach->mScopeEntry = scope->addEntry(mAttach->mName, LIT_VARIABLE, LST_KEY);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mAttach->mScopeEntry)
+ {
+ mAttach->mScopeEntry->mOffset = (S32)count;
+ mAttach->mScopeEntry->mSize = 4;
+ count += mAttach->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "attach";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mAttach->mName, strlen(mAttach->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mAttach->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptAttachEvent::getSize()
+{
+ // key = 4
+ return 4;
+}
+
+void LLScriptDataserverEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "dataserver( key ");
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mData->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mID->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mID->mScopeEntry = scope->addEntry(mID->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mData->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mData->mScopeEntry = scope->addEntry(mData->mName, LIT_VARIABLE, LST_STRING);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mID->mScopeEntry)
+ {
+ mID->mScopeEntry->mOffset = (S32)count;
+ mID->mScopeEntry->mSize = 4;
+ count += mID->mScopeEntry->mSize;
+ mData->mScopeEntry->mOffset = (S32)count;
+ mData->mScopeEntry->mSize = 4;
+ count += mData->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "dataserver";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mID->mName, strlen(mID->mName) + 1);
+ chunk->addBytes(mData->mName, strlen(mData->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mData->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptDataserverEvent::getSize()
+{
+ // key + string = 8
+ return 8;
+}
+
+void LLScriptTimerEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "timer()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "timer()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "timer";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptTimerEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptMovingStartEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "moving_start()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "moving_start";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptMovingStartEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptMovingEndEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "moving_end()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "moving_end";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptMovingEndEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptRTPEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "chat( integer ");
+ mRTPermissions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mRTPermissions->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mRTPermissions->mScopeEntry = scope->addEntry(mRTPermissions->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mRTPermissions->mScopeEntry)
+ {
+ mRTPermissions->mScopeEntry->mOffset = (S32)count;
+ mRTPermissions->mScopeEntry->mSize = 4;
+ count += mRTPermissions->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "chat";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mRTPermissions->mName, strlen(mRTPermissions->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mRTPermissions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptRTPEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptChatEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "chat( integer ");
+ mChannel->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", key ");
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mMessage->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mChannel->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mChannel->mScopeEntry = scope->addEntry(mChannel->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mName->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mID->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mID->mScopeEntry = scope->addEntry(mID->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mMessage->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mMessage->mScopeEntry = scope->addEntry(mMessage->mName, LIT_VARIABLE, LST_STRING);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mName->mScopeEntry)
+ {
+ mChannel->mScopeEntry->mOffset = (S32)count;
+ mChannel->mScopeEntry->mSize = 4;
+ count += mChannel->mScopeEntry->mSize;
+ mName->mScopeEntry->mOffset = (S32)count;
+ mName->mScopeEntry->mSize = 4;
+ count += mName->mScopeEntry->mSize;
+ mID->mScopeEntry->mOffset = (S32)count;
+ mID->mScopeEntry->mSize = 4;
+ count += mID->mScopeEntry->mSize;
+ mMessage->mScopeEntry->mOffset = (S32)count;
+ mMessage->mScopeEntry->mSize = 4;
+ count += mMessage->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "chat";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mChannel->mName, strlen(mChannel->mName) + 1);
+ chunk->addBytes(mName->mName, strlen(mName->mName) + 1);
+ chunk->addBytes(mID->mName, strlen(mID->mName) + 1);
+ chunk->addBytes(mMessage->mName, strlen(mMessage->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mChannel->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mMessage->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptChatEvent::getSize()
+{
+ // integer + key + string + string = 16
+ return 16;
+}
+
+void LLScriptSensorEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "sensor( integer ");
+ mNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mNumber->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mNumber->mScopeEntry = scope->addEntry(mNumber->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mNumber->mScopeEntry)
+ {
+ mNumber->mScopeEntry->mOffset = (S32)count;
+ mNumber->mScopeEntry->mSize = 4;
+ count += mNumber->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "sensor";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mNumber->mName, strlen(mNumber->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptSensorEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptObjectRezEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "object_rez( key ");
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mID->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mID->mScopeEntry = scope->addEntry(mID->mName, LIT_VARIABLE, LST_KEY);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mID->mScopeEntry)
+ {
+ mID->mScopeEntry->mOffset = (S32)count;
+ mID->mScopeEntry->mSize = 4;
+ count += mID->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "sensor";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mID->mName, strlen(mID->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptObjectRezEvent::getSize()
+{
+ // key = 4
+ return 4;
+}
+
+void LLScriptControlEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "control( key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mLevels->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mEdges->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mName->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mLevels->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mLevels->mScopeEntry = scope->addEntry(mLevels->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mEdges->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mEdges->mScopeEntry = scope->addEntry(mEdges->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mName->mScopeEntry)
+ {
+ mName->mScopeEntry->mOffset = (S32)count;
+ mName->mScopeEntry->mSize = 4;
+ count += mName->mScopeEntry->mSize;
+ mLevels->mScopeEntry->mOffset = (S32)count;
+ mLevels->mScopeEntry->mSize = 4;
+ count += mLevels->mScopeEntry->mSize;
+ mEdges->mScopeEntry->mOffset = (S32)count;
+ mEdges->mScopeEntry->mSize = 4;
+ count += mEdges->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "control";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mName->mName, strlen(mName->mName) + 1);
+ chunk->addBytes(mLevels->mName, strlen(mLevels->mName) + 1);
+ chunk->addBytes(mEdges->mName, strlen(mEdges->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLevels->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mEdges->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptControlEvent::getSize()
+{
+ // key + integer + integer = 12
+ return 12;
+}
+
+void LLScriptLinkMessageEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "link_message( integer ");
+ mSender->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mNum->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mStr->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", key ");
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mSender->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mSender->mScopeEntry = scope->addEntry(mSender->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mNum->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mNum->mScopeEntry = scope->addEntry(mNum->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mStr->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mStr->mScopeEntry = scope->addEntry(mStr->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mID->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mID->mScopeEntry = scope->addEntry(mID->mName, LIT_VARIABLE, LST_KEY);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mSender->mScopeEntry)
+ {
+ mSender->mScopeEntry->mOffset = (S32)count;
+ mSender->mScopeEntry->mSize = 4;
+ count += mSender->mScopeEntry->mSize;
+ mNum->mScopeEntry->mOffset = (S32)count;
+ mNum->mScopeEntry->mSize = 4;
+ count += mNum->mScopeEntry->mSize;
+ mStr->mScopeEntry->mOffset = (S32)count;
+ mStr->mScopeEntry->mSize = 4;
+ count += mStr->mScopeEntry->mSize;
+ mID->mScopeEntry->mOffset = (S32)count;
+ mID->mScopeEntry->mSize = 4;
+ count += mID->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "link_message";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mSender->mName, strlen(mSender->mName) + 1);
+ chunk->addBytes(mNum->mName, strlen(mNum->mName) + 1);
+ chunk->addBytes(mStr->mName, strlen(mStr->mName) + 1);
+ chunk->addBytes(mID->mName, strlen(mID->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mSender->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mNum->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStr->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptLinkMessageEvent::getSize()
+{
+ // integer + key + integer + string = 16
+ return 16;
+}
+
+void LLScriptRemoteEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "remote_event( integer ");
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", key ");
+ mChannel->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", key ");
+ mMessageID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mSender->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mIntVal->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mStrVal->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mType->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mType->mScopeEntry = scope->addEntry(mType->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mChannel->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mChannel->mScopeEntry = scope->addEntry(mChannel->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mMessageID->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mMessageID->mScopeEntry = scope->addEntry(mMessageID->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mSender->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mSender->mScopeEntry = scope->addEntry(mSender->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mIntVal->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mIntVal->mScopeEntry = scope->addEntry(mIntVal->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mStrVal->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mStrVal->mScopeEntry = scope->addEntry(mStrVal->mName, LIT_VARIABLE, LST_STRING);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mType->mScopeEntry)
+ {
+ mType->mScopeEntry->mOffset = (S32)count;
+ mType->mScopeEntry->mSize = 4;
+ count += mType->mScopeEntry->mSize;
+ mChannel->mScopeEntry->mOffset = (S32)count;
+ mChannel->mScopeEntry->mSize = 4;
+ count += mChannel->mScopeEntry->mSize;
+ mMessageID->mScopeEntry->mOffset = (S32)count;
+ mMessageID->mScopeEntry->mSize = 4;
+ count += mMessageID->mScopeEntry->mSize;
+ mSender->mScopeEntry->mOffset = (S32)count;
+ mSender->mScopeEntry->mSize = 4;
+ count += mSender->mScopeEntry->mSize;
+ mIntVal->mScopeEntry->mOffset = (S32)count;
+ mIntVal->mScopeEntry->mSize = 4;
+ count += mIntVal->mScopeEntry->mSize;
+ mStrVal->mScopeEntry->mOffset = (S32)count;
+ mStrVal->mScopeEntry->mSize = 4;
+ count += mStrVal->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "remote_event";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mType->mName, strlen(mType->mName) + 1);
+ chunk->addBytes(mChannel->mName, strlen(mChannel->mName) + 1);
+ chunk->addBytes(mMessageID->mName, strlen(mMessageID->mName) + 1);
+ chunk->addBytes(mSender->mName, strlen(mSender->mName) + 1);
+ chunk->addBytes(mIntVal->mName, strlen(mIntVal->mName) + 1);
+ chunk->addBytes(mStrVal->mName, strlen(mStrVal->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mChannel->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mMessageID->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mSender->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mIntVal->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStrVal->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptRemoteEvent::getSize()
+{
+ // integer + key + key + string + integer + string = 24
+ return 24;
+}
+
+void LLScriptHTTPResponseEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "http_response( key ");
+ mRequestId->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mStatus->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", list ");
+ mMetadata->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mRequestId->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mRequestId->mScopeEntry = scope->addEntry(mRequestId->mName, LIT_VARIABLE, LST_KEY);
+ }
+
+ if (scope->checkEntry(mStatus->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mStatus->mScopeEntry = scope->addEntry(mStatus->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+
+ if (scope->checkEntry(mMetadata->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mMetadata->mScopeEntry = scope->addEntry(mMetadata->mName, LIT_VARIABLE, LST_LIST);
+ }
+
+ if (scope->checkEntry(mBody->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mBody->mScopeEntry = scope->addEntry(mBody->mName, LIT_VARIABLE, LST_STRING);
+ }
+ break;
+
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mRequestId->mScopeEntry)
+ {
+ mRequestId->mScopeEntry->mOffset = (S32)count;
+ mRequestId->mScopeEntry->mSize = 4;
+ count += mRequestId->mScopeEntry->mSize;
+
+ mStatus->mScopeEntry->mOffset = (S32)count;
+ mStatus->mScopeEntry->mSize = 4;
+ count += mStatus->mScopeEntry->mSize;
+
+ mMetadata->mScopeEntry->mOffset = (S32)count;
+ mMetadata->mScopeEntry->mSize = 4;
+ count += mMetadata->mScopeEntry->mSize;
+
+ mBody->mScopeEntry->mOffset = (S32)count;
+ mBody->mScopeEntry->mSize = 4;
+ count += mBody->mScopeEntry->mSize;
+ }
+ }
+ break;
+
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "http_response";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mRequestId->mName, strlen(mRequestId->mName) + 1);
+ chunk->addBytes(mStatus->mName, strlen(mStatus->mName) + 1);
+ chunk->addBytes(mMetadata->mName, strlen(mMetadata->mName) + 1);
+ chunk->addBytes(mBody->mName, strlen(mBody->mName) + 1);
+#endif
+ }
+ break;
+
+ default:
+ mRequestId->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatus->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mMetadata->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptHTTPResponseEvent::getSize()
+{
+ // key + integer + list + string = 16
+ return 16;
+}
+
+
+void LLScriptMoneyEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "money( key ");
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mAmount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mName->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mName->mScopeEntry = scope->addEntry(mName->mName, LIT_VARIABLE, LST_KEY);
+ }
+ if (scope->checkEntry(mAmount->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mAmount->mScopeEntry = scope->addEntry(mAmount->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mName->mScopeEntry)
+ {
+ mName->mScopeEntry->mOffset = (S32)count;
+ mName->mScopeEntry->mSize = 4;
+ count += mName->mScopeEntry->mSize;
+ mAmount->mScopeEntry->mOffset = (S32)count;
+ mAmount->mScopeEntry->mSize = 4;
+ count += mAmount->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "money";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mName->mName, strlen(mName->mName) + 1);
+ chunk->addBytes(mAmount->mName, strlen(mAmount->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mName->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mAmount->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptMoneyEvent::getSize()
+{
+ // key + integer = 8
+ return 8;
+}
+
+void LLScriptEmailEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "email( string ");
+ mTime->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mAddress->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mSubject->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", string ");
+ mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", integer ");
+ mNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mTime->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mTime->mScopeEntry = scope->addEntry(mTime->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mAddress->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mAddress->mScopeEntry = scope->addEntry(mAddress->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mSubject->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mSubject->mScopeEntry = scope->addEntry(mSubject->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mBody->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mBody->mScopeEntry = scope->addEntry(mBody->mName, LIT_VARIABLE, LST_STRING);
+ }
+ if (scope->checkEntry(mNumber->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mNumber->mScopeEntry = scope->addEntry(mNumber->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mAddress->mScopeEntry)
+ {
+ mTime->mScopeEntry->mOffset = (S32)count;
+ mTime->mScopeEntry->mSize = 4;
+ count += mTime->mScopeEntry->mSize;
+ mAddress->mScopeEntry->mOffset = (S32)count;
+ mAddress->mScopeEntry->mSize = 4;
+ count += mAddress->mScopeEntry->mSize;
+ mSubject->mScopeEntry->mOffset = (S32)count;
+ mSubject->mScopeEntry->mSize = 4;
+ count += mSubject->mScopeEntry->mSize;
+ mBody->mScopeEntry->mOffset = (S32)count;
+ mBody->mScopeEntry->mSize = 4;
+ count += mBody->mScopeEntry->mSize;
+ mNumber->mScopeEntry->mOffset = (S32)count;
+ mNumber->mScopeEntry->mSize = 4;
+ count += mNumber->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "email";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mTime->mName, strlen(mTime->mName) + 1);
+ chunk->addBytes(mAddress->mName, strlen(mAddress->mName) + 1);
+ chunk->addBytes(mSubject->mName, strlen(mSubject->mName) + 1);
+ chunk->addBytes(mBody->mName, strlen(mBody->mName) + 1);
+ chunk->addBytes(mNumber->mName, strlen(mNumber->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mTime->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mAddress->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mSubject->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mBody->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptEmailEvent::getSize()
+{
+ // string + string + string + string + integer = 16
+ return 20;
+}
+
+void LLScriptRezEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "rez( integer ");
+ mStartParam->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mStartParam->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mStartParam->mScopeEntry = scope->addEntry(mStartParam->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mStartParam->mScopeEntry)
+ {
+ mStartParam->mScopeEntry->mOffset = (S32)count;
+ mStartParam->mScopeEntry->mSize = 4;
+ count += mStartParam->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "rez";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mStartParam->mName, strlen(mStartParam->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mStartParam->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptRezEvent::getSize()
+{
+ // integer = 4
+ return 4;
+}
+
+void LLScriptNoSensorEvent::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "no_sensor()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "no_sensor()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "no_sensor";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptNoSensorEvent::getSize()
+{
+ return 0;
+}
+
+void LLScriptAtTarget::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "at_target( integer ");
+ mTargetNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", vector ");
+ mTargetPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", vector ");
+ mOurPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mTargetNumber->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mTargetNumber->mScopeEntry = scope->addEntry(mTargetNumber->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mTargetPosition->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mTargetPosition->mScopeEntry = scope->addEntry(mTargetPosition->mName, LIT_VARIABLE, LST_VECTOR);
+ }
+ if (scope->checkEntry(mOurPosition->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mOurPosition->mScopeEntry = scope->addEntry(mOurPosition->mName, LIT_VARIABLE, LST_VECTOR);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mTargetNumber->mScopeEntry)
+ {
+ mTargetNumber->mScopeEntry->mOffset = (S32)count;
+ mTargetNumber->mScopeEntry->mSize = 4;
+ count += mTargetNumber->mScopeEntry->mSize;
+ mTargetPosition->mScopeEntry->mOffset = (S32)count;
+ mTargetPosition->mScopeEntry->mSize = 12;
+ count += mTargetPosition->mScopeEntry->mSize;
+ mOurPosition->mScopeEntry->mOffset = (S32)count;
+ mOurPosition->mScopeEntry->mSize = 12;
+ count += mOurPosition->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "at_target";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mTargetNumber->mName, strlen(mTargetNumber->mName) + 1);
+ chunk->addBytes(mTargetPosition->mName, strlen(mTargetPosition->mName) + 1);
+ chunk->addBytes(mOurPosition->mName, strlen(mOurPosition->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mTargetNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mTargetPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mOurPosition->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptAtTarget::getSize()
+{
+ // integer + vector + vector = 28
+ return 28;
+}
+
+
+
+void LLScriptNotAtTarget::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "not_at_target()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "not_at_target()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "not_at_target";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptNotAtTarget::getSize()
+{
+ return 0;
+}
+
+void LLScriptAtRotTarget::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ case LSCP_EMIT_ASSEMBLY:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "at_target( integer ");
+ mTargetNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", quaternion ");
+ mTargetRotation->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", quaternion ");
+ mOurRotation->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (scope->checkEntry(mTargetNumber->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mTargetNumber->mScopeEntry = scope->addEntry(mTargetNumber->mName, LIT_VARIABLE, LST_INTEGER);
+ }
+ if (scope->checkEntry(mTargetRotation->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mTargetRotation->mScopeEntry = scope->addEntry(mTargetRotation->mName, LIT_VARIABLE, LST_QUATERNION);
+ }
+ if (scope->checkEntry(mOurRotation->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mOurRotation->mScopeEntry = scope->addEntry(mOurRotation->mName, LIT_VARIABLE, LST_QUATERNION);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ if (mTargetNumber->mScopeEntry)
+ {
+ mTargetNumber->mScopeEntry->mOffset = (S32)count;
+ mTargetNumber->mScopeEntry->mSize = 4;
+ count += mTargetNumber->mScopeEntry->mSize;
+ mTargetRotation->mScopeEntry->mOffset = (S32)count;
+ mTargetRotation->mScopeEntry->mSize = 16;
+ count += mTargetRotation->mScopeEntry->mSize;
+ mOurRotation->mScopeEntry->mOffset = (S32)count;
+ mOurRotation->mScopeEntry->mSize = 16;
+ count += mOurRotation->mScopeEntry->mSize;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "at_rot_target";
+ chunk->addBytes(name, strlen(name) + 1);
+ chunk->addBytes(mTargetNumber->mName, strlen(mTargetNumber->mName) + 1);
+ chunk->addBytes(mTargetRotation->mName, strlen(mTargetRotation->mName) + 1);
+ chunk->addBytes(mOurRotation->mName, strlen(mOurRotation->mName) + 1);
+#endif
+ }
+ break;
+ default:
+ mTargetNumber->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mTargetRotation->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mOurRotation->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
+
+S32 LLScriptAtRotTarget::getSize()
+{
+ // integer + quaternion + quaternion = 36
+ return 36;
+}
+
+
+
+void LLScriptNotAtRotTarget::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "not_at_rot_target()\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "not_at_rot_target()\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ char name[] = "not_at_rot_target";
+ chunk->addBytes(name, strlen(name) + 1);
+#endif
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+S32 LLScriptNotAtRotTarget::getSize()
+{
+ return 0;
+}
+
+
+
+void LLScriptExpression::addExpression(LLScriptExpression *expression)
+{
+ if (mNextp)
+ {
+ expression->mNextp = mNextp;
+ }
+ mNextp = expression;
+}
+
+void LLScriptExpression::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ fprintf(fp, "Expression Base Class -- should never get here!\n");
+}
+
+S32 LLScriptExpression::getSize()
+{
+ printf("Expression Base Class -- should never get here!\n");
+ return 0;
+}
+
+void LLScriptExpression::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+void LLScriptForExpressionList::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ fprintf(fp, ", ");
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mFirstp->mReturnType)
+ {
+ fprintf(fp, "%s\n", LSCRIPTTypePop[mFirstp->mReturnType]);
+ }
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ fprintf(fp, "%s\n", LSCRIPTTypePop[mSecondp->mReturnType]);
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ switch(mFirstp->mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ default:
+ break;
+ }
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ switch(mSecondp->mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mFirstp->mReturnType)
+ {
+ fprintf(fp, "pop\n");
+ }
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ fprintf(fp, "pop\n");
+ }
+ }
+ break;
+ default:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptForExpressionList::getSize()
+{
+ return 0;
+}
+
+void LLScriptFuncExpressionList::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ fprintf(fp, ", ");
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!entry->mFunctionArgs.getType(entrycount))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ if (!legal_assignment(entry->mFunctionArgs.getType(entrycount), mFirstp->mReturnType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ count++;
+ entrycount++;
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ count++;
+ if (!entry->mFunctionArgs.getType(entrycount))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ if (!legal_assignment(entry->mFunctionArgs.getType(entrycount), mSecondp->mReturnType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ }
+ }
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ LSCRIPTType argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mFirstp->mReturnType)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mFirstp->mReturnType], LSCRIPTTypeNames[argtype]);
+ }
+ entrycount++;
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mSecondp->mReturnType)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mSecondp->mReturnType], LSCRIPTTypeNames[argtype]);
+ }
+ }
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ LSCRIPTType argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mFirstp->mReturnType)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[argtype] | LSCRIPTTypeHi4Bits[mFirstp->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ entrycount++;
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mSecondp->mReturnType)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[argtype] | LSCRIPTTypeHi4Bits[mSecondp->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ }
+ }
+ }
+ break;
+ /* TODO: Fix conflict between global/local variable determination needing caller scope and cast determination here needs callee scope...
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ LSCRIPTType argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mFirstp->mReturnType)
+ {
+ print_cil_cast(fp, mFirstp->mReturnType, argtype);
+ }
+ entrycount++;
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mReturnType)
+ {
+ argtype = entry->mFunctionArgs.getType(entrycount);
+ if (argtype != mSecondp->mReturnType)
+ {
+ print_cil_cast(fp, mFirstp->mReturnType, argtype);
+ }
+ }
+ }
+ }
+ break;
+ */
+ default:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptFuncExpressionList::getSize()
+{
+ return 0;
+}
+
+void LLScriptListExpressionList::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ fprintf(fp, ", ");
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mFirstp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ fprintf(fp, "%s\n", LSCRIPTListDescription[mFirstp->mReturnType]);
+ count++;
+ }
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ fprintf(fp, "%s\n", LSCRIPTListDescription[mSecondp->mReturnType]);
+ count++;
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mFirstp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGB]);
+ chunk->addByte(LSCRIPTTypeByte[mFirstp->mReturnType]);
+ count++;
+ }
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGB]);
+ chunk->addByte(LSCRIPTTypeByte[mSecondp->mReturnType]);
+ count++;
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ // Evaluate expressions in reverse order so first expression is on top of stack.
+ // Results can then be popped and appended to list to result in list with correct order.
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ // Box value.
+ print_cil_box(fp, mSecondp->mReturnType);
+
+ ++count;
+ }
+ }
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mFirstp->mType != LET_LIST_EXPRESSION_LIST)
+ {
+ // Box value.
+ print_cil_box(fp, mFirstp->mReturnType);
+
+ ++count;
+ }
+ break;
+ default:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mSecondp)
+ {
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptListExpressionList::getSize()
+{
+ return 0;
+}
+
+// Returns true if identifier is a parameter and false if identifier is a local variable within function_scope.
+bool is_parameter(LLScriptIdentifier* identifier, LLScriptScopeEntry* function_scope)
+{
+ // Function offset stores offset of first local.
+ // Compare variable offset with function offset to
+ // determine whether variable is local or parameter.
+ return (identifier->mScopeEntry->mOffset < function_scope->mOffset);
+}
+
+// If assignment is to global variable, pushes this pointer on to stack.
+void print_cil_load_address(FILE* fp, LLScriptExpression* exp, LLScriptScopeEntry* function_scope)
+{
+ LLScriptLValue *lvalue = (LLScriptLValue *) exp;
+ LLScriptIdentifier *ident = lvalue->mIdentifier;
+
+ // If global (member), load this pointer.
+ if(ident->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "ldarg.0\n");
+ }
+
+ // If accessor, load address of object.
+ if(lvalue->mAccessor)
+ {
+ if(ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ if(is_parameter(ident, function_scope))
+ {
+ // Parameter, load by name.
+ fprintf(fp, "ldarga.s %s\n", ident->mScopeEntry->mIdentifier);
+ }
+ else
+ {
+ // Local, load by index.
+ fprintf(fp, "ldloca.s %d\n", ident->mScopeEntry->mCount);
+ }
+ }
+ else if (ident->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "ldflda ");
+ print_cil_type(fp, ident->mScopeEntry->mType);
+ fprintf(fp, " LSL::%s\n", ident->mScopeEntry->mIdentifier);
+ }
+ }
+}
+
+void print_cil_accessor(FILE* fp, LLScriptLValue *lvalue)
+{
+ LLScriptIdentifier *ident = lvalue->mIdentifier;
+ print_cil_type(fp, lvalue->mReturnType);
+ fprintf(fp, " ");
+ print_cil_type(fp, ident->mScopeEntry->mType);
+ fprintf(fp, "::%s\n", lvalue->mAccessor->mName);
+}
+
+void print_cil_member(FILE* fp, LLScriptIdentifier *ident)
+{
+ print_cil_type(fp, ident->mScopeEntry->mType);
+ fprintf(fp, " LSL::%s\n", ident->mScopeEntry->mIdentifier);
+}
+
+void LLScriptLValue::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mAccessor)
+ {
+ fprintf(fp, ".");
+ mAccessor->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ if (mAccessor)
+ {
+ fprintf(fp, "%s%d [%s.%s]\n", LSCRIPTTypeLocalPush[mReturnType], mIdentifier->mScopeEntry->mOffset + mOffset, mIdentifier->mName, mAccessor->mName);
+ }
+ else
+ {
+ fprintf(fp, "%s%d [%s]\n", LSCRIPTTypeLocalPush[mIdentifier->mScopeEntry->mType], mIdentifier->mScopeEntry->mOffset, mIdentifier->mName);
+ }
+ }
+ else if (mIdentifier->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ if (mAccessor)
+ {
+ fprintf(fp, "%s%d [%s.%s]\n", LSCRIPTTypeGlobalPush[mReturnType], mIdentifier->mScopeEntry->mOffset + mOffset, mIdentifier->mName, mAccessor->mName);
+ }
+ else
+ {
+ fprintf(fp, "%s%d [%s]\n", LSCRIPTTypeGlobalPush[mIdentifier->mScopeEntry->mType], mIdentifier->mScopeEntry->mOffset, mIdentifier->mName);
+ }
+ }
+ else
+ {
+ fprintf(fp, "Unexpected LValue!\n");
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ {
+ LLScriptScopeEntry *entry = scope->findEntry(mIdentifier->mName);
+ if (!entry || ( (entry->mIDType != LIT_GLOBAL) && (entry->mIDType != LIT_VARIABLE)))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ else
+ {
+ // if we did find it, make sure this identifier is associated with the correct scope entry
+ mIdentifier->mScopeEntry = entry;
+ }
+ }
+ break;
+ case LSCP_TYPE:
+ // if we have an accessor, we need to change what type our identifier returns and set our offset value
+ if (mIdentifier->mScopeEntry)
+ {
+ if (mAccessor)
+ {
+ BOOL b_ok = FALSE;
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ if (mIdentifier->mScopeEntry->mType == LST_VECTOR)
+ {
+ if (!strcmp("x", mAccessor->mName))
+ {
+ mOffset = 0;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("y", mAccessor->mName))
+ {
+ mOffset = 4;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("z", mAccessor->mName))
+ {
+ mOffset = 8;
+ b_ok = TRUE;
+ }
+ }
+ else if (mIdentifier->mScopeEntry->mType == LST_QUATERNION)
+ {
+ if (!strcmp("x", mAccessor->mName))
+ {
+ mOffset = 0;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("y", mAccessor->mName))
+ {
+ mOffset = 4;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("z", mAccessor->mName))
+ {
+ mOffset = 8;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("s", mAccessor->mName))
+ {
+ mOffset = 12;
+ b_ok = TRUE;
+ }
+ }
+ }
+ else
+ {
+ if (mIdentifier->mScopeEntry->mType == LST_VECTOR)
+ {
+ if (!strcmp("x", mAccessor->mName))
+ {
+ mOffset = 8;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("y", mAccessor->mName))
+ {
+ mOffset = 4;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("z", mAccessor->mName))
+ {
+ mOffset = 0;
+ b_ok = TRUE;
+ }
+ }
+ else if (mIdentifier->mScopeEntry->mType == LST_QUATERNION)
+ {
+ if (!strcmp("x", mAccessor->mName))
+ {
+ mOffset = 12;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("y", mAccessor->mName))
+ {
+ mOffset = 8;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("z", mAccessor->mName))
+ {
+ mOffset = 4;
+ b_ok = TRUE;
+ }
+ else if (!strcmp("s", mAccessor->mName))
+ {
+ mOffset = 0;
+ b_ok = TRUE;
+ }
+ }
+ }
+ if (b_ok)
+ {
+ mReturnType = type = LST_FLOATINGPOINT;
+ }
+ else
+ {
+ gErrorToText.writeError(fp, this, LSERROR_VECTOR_METHOD_ERROR);
+ }
+ }
+ else
+ {
+ mReturnType = type = mIdentifier->mScopeEntry->mType;
+ }
+ }
+ else
+ {
+ mReturnType = type = LST_UNDEFINED;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ switch(mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSH]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHG]);
+ }
+ break;
+ case LST_KEY:
+ case LST_STRING:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHS]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHGS]);
+ }
+ break;
+ case LST_LIST:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHL]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHGL]);
+ }
+ break;
+ case LST_VECTOR:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHV]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHGV]);
+ }
+ break;
+ case LST_QUATERNION:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHQ]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHGQ]);
+ }
+ break;
+ default:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSH]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHG]);
+ }
+ break;
+ }
+ S32 address = mIdentifier->mScopeEntry->mOffset + mOffset;
+ chunk->addInteger(address);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ print_cil_load_address(fp, this, entry);
+ if(mAccessor)
+ {
+ fprintf(fp, "ldfld ");
+ print_cil_accessor(fp, this);
+ }
+ else if(mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ if(is_parameter(mIdentifier, entry))
+ {
+ // Parameter, load by name.
+ fprintf(fp, "ldarg.s %s\n", mIdentifier->mScopeEntry->mIdentifier);
+ }
+ else
+ {
+ // Local, load by index.
+ fprintf(fp, "ldloc.s %d\n", mIdentifier->mScopeEntry->mCount);
+ }
+ }
+ else if (mIdentifier->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "ldfld ");
+ print_cil_member(fp, mIdentifier);
+ }
+ else
+ {
+ fprintf(fp, "Unexpected LValue!\n");
+ }
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptLValue::getSize()
+{
+ return 0;
+}
+
+void print_asignment(FILE *fp, LLScriptExpression *exp)
+{
+ LLScriptLValue *lvalue = (LLScriptLValue *)exp;
+ LLScriptIdentifier *ident = lvalue->mIdentifier;
+ if (lvalue->mAccessor)
+ {
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ fprintf(fp, "%s%d [%s.%s]\n", LSCRIPTTypeLocalStore[ident->mScopeEntry->mType], ident->mScopeEntry->mOffset + lvalue->mOffset, ident->mName, lvalue->mAccessor->mName);
+ }
+ else if (ident->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "%s%d [%s.%s]\n", LSCRIPTTypeGlobalStore[ident->mScopeEntry->mType], ident->mScopeEntry->mOffset + lvalue->mOffset, ident->mName, lvalue->mAccessor->mName);
+ }
+ }
+ else
+ {
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ fprintf(fp, "%s%d [%s]\n", LSCRIPTTypeLocalStore[ident->mScopeEntry->mType], ident->mScopeEntry->mOffset, ident->mName);
+ }
+ else if (ident->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ fprintf(fp, "%s%d [%s]\n", LSCRIPTTypeGlobalStore[ident->mScopeEntry->mType], ident->mScopeEntry->mOffset, ident->mName);
+ }
+ }
+}
+
+void print_cil_asignment(FILE *fp, LLScriptExpression *exp, LLScriptScopeEntry* function_scope)
+{
+ LLScriptLValue *lvalue = (LLScriptLValue *) exp;
+ LLScriptIdentifier *ident = lvalue->mIdentifier;
+ if (lvalue->mAccessor)
+ {
+ // Object address loaded, store in to field.
+ fprintf(fp, "stfld ");
+ print_cil_accessor(fp, lvalue);
+
+ // Load object address.
+ print_cil_load_address(fp, exp, function_scope);
+
+ // Load field.
+ fprintf(fp, "ldfld ");
+ print_cil_accessor(fp, lvalue);
+ }
+ else
+ {
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ // Language semantics require value of assignment to be left on stack.
+ // TODO: Optimise away redundant dup/pop pairs.
+ fprintf(fp, "dup\n");
+ if(is_parameter(ident, function_scope))
+ {
+ // Parameter, store by name.
+ fprintf(fp, "starg.s %s\n", ident->mScopeEntry->mIdentifier);
+ }
+ else
+ {
+ // Local, store by index.
+ fprintf(fp, "stloc.s %d\n", ident->mScopeEntry->mCount);
+ }
+ }
+ else if (ident->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ // Object address loaded, store in to field.
+ fprintf(fp, "stfld ");
+ print_cil_member(fp, ident);
+
+ // Load object address.
+ print_cil_load_address(fp, exp, function_scope);
+
+ // Load field.
+ fprintf(fp, "ldfld ");
+ print_cil_member(fp, ident);
+ }
+ }
+}
+
+void print_cast(FILE *fp, LSCRIPTType ret_type, LSCRIPTType right_type)
+{
+ if (right_type != ret_type)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[right_type], LSCRIPTTypeNames[ret_type]);
+ }
+}
+
+void cast2stack(LLScriptByteCodeChunk *chunk, LSCRIPTType ret_type, LSCRIPTType right_type)
+{
+ if (right_type != ret_type)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[right_type] | LSCRIPTTypeHi4Bits[ret_type];
+ chunk->addByte(castbyte);
+ }
+}
+
+void operation2stack(LLScriptByteCodeChunk *chunk, LSCRIPTType ret_type, LSCRIPTType right_type)
+{
+ U8 typebyte = LSCRIPTTypeByte[right_type] | LSCRIPTTypeHi4Bits[ret_type];
+ chunk->addByte(typebyte);
+}
+
+void store2stack(LLScriptExpression *exp, LLScriptExpression *lv, LLScriptByteCodeChunk *chunk, LSCRIPTType right_type)
+{
+ LLScriptLValue *lvalue = (LLScriptLValue *)lv;
+ LLScriptIdentifier *ident = lvalue->mIdentifier;
+ LSCRIPTType rettype = exp->mReturnType;
+
+ if (exp->mRightType != LST_NULL)
+ {
+ if (legal_binary_expression(rettype, exp->mLeftType, exp->mRightType, exp->mType))
+ cast2stack(chunk, right_type, exp->mReturnType);
+ }
+ switch(exp->mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STORE]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREG]);
+ }
+ break;
+ case LST_KEY:
+ case LST_STRING:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STORES]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREGS]);
+ }
+ break;
+ case LST_LIST:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREL]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREGL]);
+ }
+ break;
+ case LST_VECTOR:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREV]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREGV]);
+ }
+ break;
+ case LST_QUATERNION:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREQ]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREGQ]);
+ }
+ break;
+ default:
+ if (ident->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STORE]);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STOREG]);
+ }
+ break;
+ }
+ S32 address = ident->mScopeEntry->mOffset + lvalue->mOffset;
+ chunk->addInteger(address);
+}
+
+void print_cil_numeric_cast(FILE* fp, LSCRIPTType currentArg, LSCRIPTType otherArg)
+{
+ if((currentArg == LST_INTEGER) && (otherArg == LST_FLOATINGPOINT))
+ {
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+ }
+}
+
+void LLScriptAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " = ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cast(fp, mReturnType, mRightType);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_assignment(mLeftType, mRightType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType = mLeftType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ store2stack(this, mLValue, chunk, mRightType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightType, mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_add(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+
+ // Numeric addition.
+ fprintf(fp, "add\n");
+ break;
+
+ case LST_STRING:
+ case LST_KEY:
+
+ // String concatenation.
+ fprintf(fp, "call string valuetype [mscorlib]System.String::Concat(string, string)");
+ break;
+
+ case LST_VECTOR:
+
+ // Vector addition.
+ // TODO: Inline (requires temporary variables, which must be identified in earlier pass).
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'add_vec'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLVector)\n");
+ break;
+
+ case LST_QUATERNION:
+
+ // Rotation addition.
+ // TODO: Inline (requires temporary variables, which must be identified in earlier pass).
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'add_quat'(valuetype [LScriptLibrary]LLQuaternion, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ case LST_LIST:
+ print_cil_box(fp, right_type);
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList class [LScriptLibrary]LScriptInternal::AddReturnList(class [mscorlib]System.Collections.ArrayList, object)\n");
+ break;
+
+ default:
+ break;
+ }
+}
+
+void LLScriptAddAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " += ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ADD %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ operation2stack(chunk, mReturnType, mRightType);
+ store2stack(this, mLValue, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLValue->mReturnType);
+ print_cil_add(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptAddAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_sub(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+
+ // Numeric subtraction.
+ fprintf(fp, "sub\n");
+ break;
+
+ case LST_VECTOR:
+
+ // Vector subtraction.
+ // TODO: Inline (requires temporary variables, which must be identified in earlier pass).
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'subtract_vec'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLVector)\n");
+ break;
+
+ case LST_QUATERNION:
+
+ // Rotation subtraction.
+ // TODO: Inline (requires temporary variables, which must be identified in earlier pass).
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'subtract_quat'(valuetype [LScriptLibrary]LLQuaternion, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ default:
+
+ // Error.
+ break;
+ }
+}
+
+void LLScriptSubAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " -= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SUB %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ operation2stack(chunk, mReturnType, mRightType);
+ store2stack(this, mLValue, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLValue->mReturnType);
+ print_cil_sub(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptSubAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_mul(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+
+ // Numeric multiplication.
+ fprintf(fp, "mul\n");
+ break;
+
+ case LST_VECTOR:
+
+ switch(right_type)
+ {
+ case LST_INTEGER:
+
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+
+ case LST_FLOATINGPOINT:
+
+ // Vector scaling.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'multiply_float'(valuetype [LScriptLibrary]LLVector, float32)\n");
+ break;
+
+ case LST_VECTOR:
+
+ // Dot product.
+ fprintf(fp, "call float32 valuetype [LScriptLibrary]LLVector::'multiply_vec'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLVector)\n");
+ break;
+
+ case LST_QUATERNION:
+
+ // Vector rotation.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'multiply_quat'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case LST_QUATERNION:
+
+ // Rotation multiplication.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'multiply_quat'(valuetype [LScriptLibrary]LLQuaternion, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ default:
+
+ // Error.
+ break;
+ }
+}
+
+void LLScriptMulAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " *= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "MUL %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_MUL]);
+ operation2stack(chunk, mReturnType, mRightType);
+ store2stack(this, mLValue, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLValue->mReturnType);
+ print_cil_mul(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptMulAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_div(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+
+ // Numeric addition.
+ fprintf(fp, "div\n");
+ break;
+
+ case LST_VECTOR:
+
+ switch(right_type)
+ {
+ case LST_INTEGER:
+
+ print_cil_cast(fp, LST_INTEGER, LST_FLOATINGPOINT);
+
+ case LST_FLOATINGPOINT:
+
+ // Scale.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'divide_float'(valuetype [LScriptLibrary]LLVector, float32)\n");
+ break;
+
+ case LST_QUATERNION:
+
+ // Inverse rotation.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'divide_quat'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case LST_QUATERNION:
+
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'divide_quat'(valuetype [LScriptLibrary]LLQuaternion, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ default:
+
+ // Error.
+ break;
+ }
+}
+
+void LLScriptDivAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " /= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "DIV %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_DIV]);
+ operation2stack(chunk, mReturnType, mRightType);
+ store2stack(this, mLValue, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLValue->mReturnType);
+ print_cil_div(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptDivAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_mod(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+
+ // Numeric remainder.
+ fprintf(fp, "rem\n");
+ break;
+
+ case LST_VECTOR:
+
+ // Vector cross product.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'mod_vec'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLVector)\n");
+ break;
+
+ default:
+
+ // Error.
+ break;
+ }
+}
+
+void LLScriptModAssignment::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " %%= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "MOD %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ print_asignment(fp, mLValue);
+ }
+ break;
+ case LSCP_TYPE:
+ {
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_MOD]);
+ operation2stack(chunk, mReturnType, mRightType);
+ store2stack(this, mLValue, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mLValue, entry);
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_mod(fp, mLValue->mReturnType, mRightSide->mReturnType);
+ print_cil_asignment(fp, mLValue, entry);
+ }
+ break;
+ default:
+ mLValue->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptModAssignment::getSize()
+{
+ return 0;
+}
+
+void print_cil_eq(FILE* fp, LSCRIPTType left_type, LSCRIPTType right_type)
+{
+ switch(left_type)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+
+ // Numeric equality.
+ fprintf(fp, "ceq\n");
+ break;
+
+ case LST_STRING:
+ case LST_KEY:
+
+ // String equality.
+ fprintf(fp, "call bool valuetype [mscorlib]System.String::op_Equality(string, string)\n");
+ break;
+
+ case LST_VECTOR:
+
+ // Vector equality.
+ fprintf(fp, "call bool [LScriptLibrary]LLVector::'equals_vec'(valuetype [LScriptLibrary]LLVector, valuetype [LScriptLibrary]LLVector)\n");
+ break;
+
+ case LST_QUATERNION:
+
+ // Rotation equality.
+ fprintf(fp, "call bool [LScriptLibrary]LLQuaternion::'equals_quat'(valuetype [LScriptLibrary]LLQuaternion, valuetype [LScriptLibrary]LLQuaternion)\n");
+ break;
+
+ case LST_LIST:
+ fprintf(fp, "call bool [LScriptLibrary]LScriptInternal::EqualsList(class [mscorlib]System.Collections.ArrayList, class [mscorlib]System.Collections.ArrayList)\n");
+ break;
+
+ default:
+
+ // Error.
+ break;
+ }
+}
+
+void LLScriptEquality::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " == ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "EQ %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_EQ]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLeftSide->mReturnType);
+ print_cil_eq(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptEquality::getSize()
+{
+ return 0;
+}
+
+void LLScriptNotEquals::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " != ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "NEQ %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_NEQ]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ceq\n");
+ fprintf(fp, "ldc.i4.0\n");
+ fprintf(fp, "ceq\n"); // Compare result of first compare equal with 0 to get compare not equal.
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptNotEquals::getSize()
+{
+ return 0;
+}
+
+void LLScriptLessEquals::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " <= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LEQ %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LEQ]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "cgt\n"); // Test greater than.
+ fprintf(fp, "ldc.i4.0\n"); // Use (b == 0) implementation of boolean not.
+ fprintf(fp, "ceq\n"); // Apply boolean not to greater than. If not greater than, then less or equal.
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptLessEquals::getSize()
+{
+ return 0;
+}
+
+void LLScriptGreaterEquals::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >= ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "GEQ %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_GEQ]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "clt\n"); // Test less than.
+ fprintf(fp, "ldc.i4.0\n"); // Use (b == 0) implementation of boolean not.
+ fprintf(fp, "ceq\n"); // Apply boolean not to less than. If not less than, then greater or equal.
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptGreaterEquals::getSize()
+{
+ return 0;
+}
+
+void LLScriptLessThan::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " < ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LESS %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LESS]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "clt\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptLessThan::getSize()
+{
+ return 0;
+}
+
+void LLScriptGreaterThan::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " > ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "GREATER %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_GREATER]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "cgt\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptGreaterThan::getSize()
+{
+ return 0;
+}
+
+void LLScriptPlus::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " + ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ADD %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLeftSide->mReturnType);
+ print_cil_add(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPlus::getSize()
+{
+ return 0;
+}
+
+void LLScriptMinus::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " - ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SUB %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLeftSide->mReturnType);
+ print_cil_sub(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptMinus::getSize()
+{
+ return 0;
+}
+
+void LLScriptTimes::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " * ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "MUL %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_MUL]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLeftSide->mReturnType);
+ print_cil_mul(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptTimes::getSize()
+{
+ return 0;
+}
+
+void LLScriptDivide::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " / ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "DIV %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_DIV]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_numeric_cast(fp, mRightSide->mReturnType, mLeftSide->mReturnType);
+ print_cil_div(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptDivide::getSize()
+{
+ return 0;
+}
+
+void LLScriptMod::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " %% ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "MOD %s, %s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mRightType] | LSCRIPTTypeHi4Bits[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_MOD]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_mod(fp, mLeftSide->mReturnType, mRightSide->mReturnType);
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptMod::getSize()
+{
+ return 0;
+}
+
+void LLScriptBitAnd::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " & ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BITAND\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BITAND]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "and\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBitAnd::getSize()
+{
+ return 0;
+}
+
+void LLScriptBitOr::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " | ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BITOR\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BITOR]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "or\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBitOr::getSize()
+{
+ return 0;
+}
+
+void LLScriptBitXor::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ^ ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BITXOR\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BITXOR]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "xor\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBitXor::getSize()
+{
+ return 0;
+}
+
+void LLScriptBooleanAnd::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " && ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BOOLAND\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BOOLAND]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "and\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBooleanAnd::getSize()
+{
+ return 0;
+}
+
+void LLScriptBooleanOr::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " || ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BOOLOR\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BOOLOR]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "or\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBooleanOr::getSize()
+{
+ return 0;
+}
+
+void LLScriptShiftLeft::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " << ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SHL\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SHL]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "shl\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptShiftLeft::getSize()
+{
+ return 0;
+}
+
+
+void LLScriptShiftRight::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >> ");
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SHR\n");
+ break;
+ case LSCP_TYPE:
+ {
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_binary_expression(mReturnType, mLeftType, mRightType, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mReturnType;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SHR]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "shr\n");
+ break;
+ default:
+ mLeftSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightSide->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptShiftRight::getSize()
+{
+ return 0;
+}
+
+void LLScriptParenthesis::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mReturnType = mLeftType = type;
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mReturnType = mLeftType = type;
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptParenthesis::getSize()
+{
+ return 0;
+}
+
+void LLScriptUnaryMinus::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "-");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "NEG %s\n", LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ U8 typebyte = LSCRIPTTypeByte[mLeftType];
+ chunk->addByte(LSCRIPTOpCodes[LOPC_NEG]);
+ chunk->addByte(typebyte);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptUnaryMinus::getSize()
+{
+ return 0;
+}
+
+void LLScriptBooleanNot::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "!");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BOOLNOT\n");
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BOOLNOT]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ldc.i4.0\n");
+ fprintf(fp, "ceq\n"); // If f(e) is (e == 0), f(e) returns 1 if e is 0 and 0 otherwise, therefore f(e) implements boolean not.
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBooleanNot::getSize()
+{
+ return 0;
+}
+
+void LLScriptBitNot::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "~");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "BITNOT\n");
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_BITNOT]);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "not\n");
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptBitNot::getSize()
+{
+ return 0;
+}
+
+void LLScriptPreIncrement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "++");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "PUSHARGI 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fprintf(fp, "ADD integer, integer\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "PUSHARGF 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fprintf(fp, "ADD float, float\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_asignment(fp, mExpression);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mExpression->mType != LET_LVALUE)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_EXPRESSION_ON_LVALUE);
+ }
+ else
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ if (mReturnType == LST_INTEGER)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(LSCRIPTTypeByte[LST_INTEGER] | LSCRIPTTypeHi4Bits[LST_INTEGER]);
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGF]);
+ chunk->addFloat(1.f);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[LST_FLOATINGPOINT]);
+ }
+ store2stack(this, mExpression, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mExpression, entry);
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "ldc.i4.1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "add\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "ldc.r8.1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "add\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_cil_asignment(fp, mExpression, entry);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPreIncrement::getSize()
+{
+ return 0;
+}
+
+void LLScriptPreDecrement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "--");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "PUSHARGI 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fprintf(fp, "SUB integer, integer\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "PUSHARGF 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fprintf(fp, "SUB float, float\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_asignment(fp, mExpression);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mExpression->mType != LET_LVALUE)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_EXPRESSION_ON_LVALUE);
+ }
+ else
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ if (mReturnType == LST_INTEGER)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ chunk->addByte(LSCRIPTTypeByte[LST_INTEGER] | LSCRIPTTypeHi4Bits[LST_INTEGER]);
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGF]);
+ chunk->addFloat(1.f);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ chunk->addByte(LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[LST_FLOATINGPOINT]);
+ }
+ store2stack(this, mExpression, chunk, mReturnType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mExpression, entry);
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "ldc.i4.1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "sub\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "ldc.r8.1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "sub\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_cil_asignment(fp, mExpression, entry);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPreDecrement::getSize()
+{
+ return 0;
+}
+
+void LLScriptTypeCast::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "( ");
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ") ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mRightType], LSCRIPTTypeNames[mType->mType]);
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mRightType = type;
+ if (!legal_casts(mType->mType, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ type = mType->mType;
+ mReturnType = mLeftType = type;
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[mType->mType] | LSCRIPTTypeHi4Bits[mRightType];
+ chunk->addByte(castbyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ print_cil_cast(fp, mRightType, mType->mType);
+ break;
+ default:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptTypeCast::getSize()
+{
+ return 0;
+}
+
+void LLScriptVectorInitializer::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "< ");
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression1->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression2->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression3->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ break;
+ case LSCP_TYPE:
+ // vector's take floats
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mReturnType = type = LST_VECTOR;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TO_STACK:
+ pass = LSCP_TO_STACK;
+ mExpression1->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression1->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression2->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression3->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Load arguments.
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression1->mReturnType, LST_FLOATINGPOINT);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression2->mReturnType, LST_FLOATINGPOINT);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression3->mReturnType, LST_FLOATINGPOINT);
+ }
+ // Call named ctor, which leaves new Vector on stack, so it can be saved in to local or argument just like a primitive type.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLVector valuetype [LScriptLibrary]LLVector::'create'(float32, float32, float32)\n");
+ break;
+ default:
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptVectorInitializer::getSize()
+{
+ return 0;
+}
+
+void LLScriptQuaternionInitializer::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "< ");
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", ");
+ mExpression4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " >");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression1->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression2->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression3->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ mExpression4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression4->mReturnType != LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "CAST %s->%s\n", LSCRIPTTypeNames[mExpression4->mReturnType], LSCRIPTTypeNames[LST_FLOATINGPOINT]);
+ }
+ break;
+ case LSCP_TYPE:
+ // vector's take floats
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mExpression4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(LST_FLOATINGPOINT, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ mReturnType = type = LST_QUATERNION;
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TO_STACK:
+ pass = LSCP_TO_STACK;
+ mExpression1->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression1->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression2->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression3->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ mExpression4->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression4->mReturnType != LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CAST]);
+ U8 castbyte = LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[mExpression4->mReturnType];
+ chunk->addByte(castbyte);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Load arguments.
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression1->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression1->mReturnType, LST_FLOATINGPOINT);
+ }
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression2->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression2->mReturnType, LST_FLOATINGPOINT);
+ }
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression3->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression3->mReturnType, LST_FLOATINGPOINT);
+ }
+ mExpression4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression4->mReturnType != LST_FLOATINGPOINT)
+ {
+ print_cil_cast(fp, mExpression4->mReturnType, LST_FLOATINGPOINT);
+ }
+
+ // Call named ctor, which leaves new Vector on stack, so it can be saved in to local or argument just like a primitive type.
+ fprintf(fp, "call valuetype [LScriptLibrary]LLQuaternion valuetype [LScriptLibrary]LLQuaternion::'create'(float32, float32, float32, float32)\n");
+ break;
+ default:
+ mExpression1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression3->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression4->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptQuaternionInitializer::getSize()
+{
+ return 0;
+}
+
+void LLScriptListInitializer::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, "[ ");
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ]");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ count = 0;
+ if (mExpressionList)
+ {
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "STACKTOL %llu\n", count);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mExpressionList)
+ {
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mReturnType = type = LST_LIST;
+ }
+ mReturnType = type = LST_LIST;
+ break;
+ case LSCP_TO_STACK:
+ if (mExpressionList)
+ {
+ pass = LSCP_TO_STACK;
+ count = 0;
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STACKTOL]);
+ chunk->addInteger((S32)count);
+ count = 0;
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STACKTOL]);
+ chunk->addInteger(0);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Push boxed elements on stack.
+ count = 0;
+ if (mExpressionList)
+ {
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+
+ // Create list on stack, consuming first boxed element.
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList class [LScriptLibrary]LScriptInternal::CreateList()\n");
+
+ // Call AddReturnList to add remaining boxed expressions.
+ for(U64 i = 0; i < count; i++)
+ {
+ fprintf(fp, "call class [mscorlib]System.Collections.ArrayList class [LScriptLibrary]LScriptInternal::AddReturnList(object, class [mscorlib]System.Collections.ArrayList)\n");
+ }
+ count = 0;
+
+ break;
+ default:
+ if (mExpressionList)
+ {
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptListInitializer::getSize()
+{
+ return 0;
+}
+
+void LLScriptPostIncrement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "++");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "PUSHARGI 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ADD integer, integer\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "PUSHARGF 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "ADD float, float\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_asignment(fp, mExpression);
+ fprintf(fp, "%s\n", LSCRIPTTypePop[mReturnType]);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mExpression->mType != LET_LVALUE)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_EXPRESSION_ON_LVALUE);
+ }
+ else
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mReturnType == LST_INTEGER)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(LSCRIPTTypeByte[LST_INTEGER] | LSCRIPTTypeHi4Bits[LST_INTEGER]);
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGF]);
+ chunk->addFloat(1.f);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[LST_FLOATINGPOINT]);
+ }
+ store2stack(this, mExpression, chunk, mReturnType);
+ switch(mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_KEY:
+ case LST_STRING:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ default:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mExpression, entry);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp,"dup\n"); // Copy expression result to use as increment operand.
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "ldc.i4.1\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "ldc.r8.1\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ fprintf(fp, "add\n");
+ print_cil_asignment(fp, mExpression, entry);
+ fprintf(fp, "pop\n"); // Pop assignment result to leave original expression result on stack. TODO: Optimise away redundant pop/dup pairs.
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPostIncrement::getSize()
+{
+ return 0;
+}
+
+void LLScriptPostDecrement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "--");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "PUSHARGI 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SUB integer, integer\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "PUSHARGF 1\n");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "SUB float, float\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ print_asignment(fp, mExpression);
+ fprintf(fp, "%s\n", LSCRIPTTypePop[mReturnType]);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mExpression->mType != LET_LVALUE)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_EXPRESSION_ON_LVALUE);
+ }
+ else
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_unary_expression(type, type, mType))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mReturnType = mLeftType = type;
+ }
+ }
+ break;
+ case LSCP_TO_STACK:
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mReturnType == LST_INTEGER)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ chunk->addByte(LSCRIPTTypeByte[LST_INTEGER] | LSCRIPTTypeHi4Bits[LST_INTEGER]);
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGF]);
+ chunk->addFloat(1.f);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_SUB]);
+ chunk->addByte(LSCRIPTTypeByte[LST_FLOATINGPOINT] | LSCRIPTTypeHi4Bits[LST_FLOATINGPOINT]);
+ }
+ store2stack(this, mExpression, chunk, mReturnType);
+ switch(mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_KEY:
+ case LST_STRING:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ default:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ print_cil_load_address(fp, mExpression, entry);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp,"dup\n"); // Copy expression result to use as decrement operand.
+ if (mReturnType == LST_INTEGER)
+ {
+ fprintf(fp, "ldc.i4.1\n");
+ }
+ else if (mReturnType == LST_FLOATINGPOINT)
+ {
+ fprintf(fp, "ldc.r8.1\n");
+ }
+ else
+ {
+ fprintf(fp, "Unexpected Type\n");
+ }
+ fprintf(fp, "sub\n");
+ print_cil_asignment(fp, mExpression, entry);
+ fprintf(fp, "pop\n"); // Pop assignment result to leave original expression result on stack. TODO: Optimise away redundant pop/dup pairs.
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPostDecrement::getSize()
+{
+ return 0;
+}
+
+// Generate arg list.
+void print_cil_arg_list(FILE *fp, LLScriptFuncExpressionList* exp_list)
+{
+ // Print first argument.
+ print_cil_type(fp, exp_list->mFirstp->mReturnType);
+
+ // Recursively print next arguments.
+ if(exp_list->mSecondp != NULL)
+ {
+ fprintf(fp, ", ");
+ print_cil_arg_list(fp, (LLScriptFuncExpressionList*) exp_list->mSecondp);
+ }
+}
+
+void LLScriptFunctionCall::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "( ");
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mIdentifier->mScopeEntry->mType)
+ fprintf(fp, "%s\n", LSCRIPTTypePush[mIdentifier->mScopeEntry->mType]);
+ fprintf(fp,"PUSHE\n");
+ fprintf(fp, "PUSHBP\n");
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, 0, NULL);
+ fprintf(fp, "PUSHARGE %d\n", mIdentifier->mScopeEntry->mSize - mIdentifier->mScopeEntry->mOffset);
+ fprintf(fp, "PUSHSP\n");
+ fprintf(fp, "PUSHARGI %d\n", mIdentifier->mScopeEntry->mSize);
+ fprintf(fp, "ADD integer, integer\n");
+ fprintf(fp, "POPBP\n");
+ if (mIdentifier->mScopeEntry->mIDType != LIT_LIBRARY_FUNCTION)
+ {
+ fprintf(fp, "CALL ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ fprintf(fp, "CALLLID ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ", %d", (U32)mIdentifier->mScopeEntry->mLibraryNumber);
+ }
+ fprintf(fp, "\n");
+ fprintf(fp, "POPBP\n");
+ break;
+ case LSCP_SCOPE_PASS1:
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_SCOPE_PASS2:
+ {
+ LLScriptScopeEntry *entry = scope->findEntryTyped(mIdentifier->mName, LIT_FUNCTION);
+ if (!entry)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ else
+ {
+ // if we did find it, make sure this identifier is associated with the correct scope entry
+ mIdentifier->mScopeEntry = entry;
+ }
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mIdentifier->mScopeEntry)
+ {
+ U64 argcount = 0;
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, argcount, chunk, heap, stacksize, mIdentifier->mScopeEntry, 0, NULL);
+
+ if (!mIdentifier->mScopeEntry->mFunctionArgs.mString)
+ {
+ if (argcount)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ }
+ else if (argcount != strlen(mIdentifier->mScopeEntry->mFunctionArgs.mString))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_FUNCTION_TYPE_ERROR);
+ }
+ }
+
+ if (mIdentifier->mScopeEntry)
+ type = mIdentifier->mScopeEntry->mType;
+ else
+ type = LST_NULL;
+ mReturnType = type;
+ break;
+ case LSCP_TO_STACK:
+ switch(mIdentifier->mScopeEntry->mType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ case LST_STRING:
+ case LST_KEY:
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHE]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHEV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHEQ]);
+ break;
+ default:
+ break;
+ }
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHE]);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHBP]);
+ if (mExpressionList)
+ {
+ // Don't let this change the count.
+ U64 dummy_count = 0;
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, dummy_count, chunk, heap, stacksize, mIdentifier->mScopeEntry, 0, NULL);
+ //mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, 0, NULL);
+ }
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGE]);
+ chunk->addInteger(mIdentifier->mScopeEntry->mSize - mIdentifier->mScopeEntry->mOffset);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHSP]);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(mIdentifier->mScopeEntry->mSize);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_ADD]);
+ chunk->addByte(LSCRIPTTypeByte[LST_INTEGER] | LSCRIPTTypeHi4Bits[LST_INTEGER]);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPBP]);
+ if (mIdentifier->mScopeEntry->mIDType != LIT_LIBRARY_FUNCTION)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CALL]);
+ chunk->addInteger(mIdentifier->mScopeEntry->mCount);
+ }
+ else
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_CALLLIB_TWO_BYTE]);
+ chunk->addU16(mIdentifier->mScopeEntry->mLibraryNumber);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ bool library_call = (mIdentifier->mScopeEntry->mIDType == LIT_LIBRARY_FUNCTION);
+ if(! library_call)
+ {
+ // Load this pointer.
+ fprintf(fp, "ldarg.0\n");
+ }
+
+ // Load args on to stack.
+ if (mExpressionList)
+ {
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry /* Needed for is_parameter calls */, 0, NULL);
+ }
+
+ // Make call.
+ if (! library_call)
+ {
+ fprintf(fp, "callvirt instance ");
+ }
+ else
+ {
+ fprintf(fp, "call ");
+ }
+ print_cil_type(fp, mIdentifier->mScopeEntry->mType);
+ fprintf(fp, " class ");
+ if (library_call)
+ {
+ fprintf(fp, "[LScriptLibrary]LScriptLibrary");
+ }
+ else
+ {
+ fprintf(fp, "LSL");
+ }
+ fprintf(fp, "::");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "(");
+ if (mExpressionList) {print_cil_arg_list(fp, (LLScriptFuncExpressionList*) mExpressionList);}
+ fprintf(fp, ")\n");
+ }
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptFunctionCall::getSize()
+{
+ return 0;
+}
+
+void LLScriptPrint::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fprintf(fp, " PRINT ( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "PRINT %s\n", LSCRIPTTypeNames[mLeftType]);
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mLeftType = type;
+ mReturnType = LST_NULL;
+ break;
+ case LSCP_TO_STACK:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PRINT]);
+ chunk->addByte(LSCRIPTTypeByte[mLeftType]);
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptPrint::getSize()
+{
+ return 0;
+}
+
+void LLScriptConstantExpression::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_TYPE:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mReturnType = type;
+ break;
+ case LSCP_TO_STACK:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ default:
+ mConstant->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptConstantExpression::getSize()
+{
+ return 0;
+}
+
+void LLScriptStatement::addStatement(LLScriptStatement *event)
+{
+ if (mNextp)
+ {
+ event->mNextp = mNextp;
+ }
+ mNextp = event;
+}
+
+void LLScriptStatement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ fprintf(fp, "Statement Base Class -- should never get here!\n");
+}
+
+S32 LLScriptStatement::getSize()
+{
+ printf("Statement Base Class -- should never get here!\n");
+ return 0;
+}
+
+void LLScriptStatement::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptStatementSequence::getSize()
+{
+ return 0;
+}
+
+void LLScriptStatementSequence::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_PRUNE:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (prunearg)
+ {
+ ptype = LSPRUNE_DEAD_CODE;
+ gErrorToText.writeWarning(fp, this, LSWARN_DEAD_CODE);
+ }
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_TYPE:
+ // pass the return type into all statements so we can check returns
+ {
+ LSCRIPTType return_type = type;
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, return_type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ return_type = type;
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, return_type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ mFirstp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mSecondp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptNOOP::getSize()
+{
+ return 0;
+}
+
+void LLScriptNOOP::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ default:
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void add_exit_pops(LLScriptByteCodeChunk *chunk, LLScriptScopeEntry *entry)
+{
+ // remember that we need to pop in reverse order
+ S32 number, i;
+
+ if (entry->mLocals.mString)
+ {
+ number = (S32)strlen(entry->mLocals.mString);
+ for (i = number - 1; i >= 0; i--)
+ {
+ switch(entry->mLocals.getType(i))
+ {
+ case LST_INTEGER:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (entry->mFunctionArgs.mString)
+ {
+ number = (S32)strlen(entry->mFunctionArgs.mString);
+ for (i = number - 1; i >= 0; i--)
+ {
+ switch(entry->mFunctionArgs.getType(i))
+ {
+ case LST_INTEGER:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+}
+
+void print_exit_pops(FILE *fp, LLScriptScopeEntry *entry)
+{
+ // remember that we need to pop in reverse order
+ S32 number, i;
+
+ if (entry->mLocals.mString)
+ {
+ number = (S32)strlen(entry->mLocals.mString);
+ for (i = number - 1; i >= 0; i--)
+ {
+ fprintf(fp, "%s", LSCRIPTTypePop[entry->mLocals.getType(i)]);
+ }
+ }
+
+ if (entry->mFunctionArgs.mString)
+ {
+ number = (S32)strlen(entry->mFunctionArgs.mString);
+ for (i = number - 1; i >= 0; i--)
+ {
+ fprintf(fp, "%s", LSCRIPTTypePop[entry->mFunctionArgs.getType(i)]);
+ }
+ }
+}
+
+
+S32 LLScriptStateChange::getSize()
+{
+ return 0;
+}
+
+void LLScriptStateChange::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "state ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ print_exit_pops(fp, entry);
+ fprintf(fp, "STATE ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ break;
+ case LSCP_PRUNE:
+ if ( (ptype == LSPRUNE_GLOBAL_VOIDS)
+ ||(ptype == LSPRUNE_GLOBAL_NON_VOIDS))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_STATE_CHANGE_IN_GLOBAL);
+ }
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_SCOPE_PASS2:
+ {
+ LLScriptScopeEntry *entry = scope->findEntryTyped(mIdentifier->mName, LIT_STATE);
+ if (!entry)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ else
+ {
+ // if we did find it, make sure this identifier is associated with the correct scope entry
+ mIdentifier->mScopeEntry = entry;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ add_exit_pops(chunk, entry);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STATE]);
+ chunk->addInteger(mIdentifier->mScopeEntry->mCount);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "ldstr \"");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\"\n");
+ fprintf(fp, "call void class [LScriptLibrary]LScriptInternal::change_state(string)\n");
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptJump::getSize()
+{
+ return 0;
+}
+
+void LLScriptJump::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "jump ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "JUMP ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_SCOPE_PASS2:
+ {
+ LLScriptScopeEntry *entry = scope->findEntryTyped(mIdentifier->mName, LIT_LABEL);
+ if (!entry)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ else
+ {
+ // if we did find it, make sure this identifier is associated with the correct scope entry
+ mIdentifier->mScopeEntry = entry;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMP]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(mIdentifier->mName);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ fprintf(fp, "br ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptLabel::getSize()
+{
+ return 0;
+}
+
+void LLScriptLabel::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "@");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ fprintf(fp, "LABEL ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ break;
+ case LSCP_PRUNE:
+ // Always clear this flag, to stop pruning after return statements. A jump
+ // might start up code at this label, so we need to stop pruning.
+ prunearg = FALSE;
+ break;
+ case LSCP_SCOPE_PASS1:
+ // add labels to scope
+ if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_LABEL, LST_NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ chunk->addLabel(mIdentifier->mName);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ":\n");
+ break;
+ default:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void add_return(LLScriptByteCodeChunk *chunk, LLScriptScopeEntry *entry)
+{
+ add_exit_pops(chunk, entry);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_RETURN]);
+}
+
+void print_return(FILE *fp, LLScriptScopeEntry *entry)
+{
+ print_exit_pops(fp, entry);
+ fprintf(fp, "RETURN\n");
+}
+
+
+S32 LLScriptReturn::getSize()
+{
+ return 0;
+}
+
+void LLScriptReturn::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mExpression)
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "return ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ }
+ else
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "return;\n");
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "%s\n", LSCRIPTTypeReturn[mType]);
+ }
+ print_return(fp, entry);
+ break;
+ case LSCP_PRUNE:
+ if ( (ptype == LSPRUNE_GLOBAL_VOIDS)
+ ||(ptype == LSPRUNE_EVENTS))
+ {
+ if (mExpression)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_INVALID_RETURN);
+ }
+ }
+ else if (ptype == LSPRUNE_GLOBAL_NON_VOIDS)
+ {
+ if (!mExpression)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_INVALID_VOID_RETURN);
+ }
+ }
+ prunearg = TRUE;
+ case LSCP_TYPE:
+ // if there is a return expression, it must be promotable to the return type of the function
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(basetype, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ else
+ {
+ mType = basetype;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ switch(mType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ chunk->addInteger(-12);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ // use normal store for reference counted types
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADSP]);
+ chunk->addInteger(-12);
+ break;
+ case LST_LIST:
+ // use normal store for reference counted types
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADLP]);
+ chunk->addInteger(-12);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADVP]);
+ chunk->addInteger(-20);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADQP]);
+ chunk->addInteger(-24);
+ break;
+ default:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ chunk->addInteger(-12);
+ break;
+ }
+ }
+ add_return(chunk, entry);
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ fprintf(fp, "ret\n");
+ break;
+ default:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptExpressionStatement::getSize()
+{
+ return 0;
+}
+
+void LLScriptExpressionStatement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression->mReturnType)
+ {
+ fprintf(fp, "%s\n", LSCRIPTTypePop[mExpression->mReturnType]);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ switch(mExpression->mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POP]);
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPS]);
+ break;
+ case LST_LIST:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPL]);
+ break;
+ case LST_VECTOR:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPV]);
+ break;
+ case LST_QUATERNION:
+ chunk->addByte(LSCRIPTOpCodes[LOPC_POPQ]);
+ break;
+ default:
+ break;
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mExpression->mReturnType)
+ {
+ fprintf(fp, "pop\n");
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptIf::getSize()
+{
+ return 0;
+}
+
+void LLScriptIf::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "if ( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ S32 tjump = gTempJumpCount++;
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMPNIF ##Temp Jump %d##\n", tjump);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mType = type;
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ char jumpname[32];
+ sprintf(jumpname, "##Temp Jump %d##", gTempJumpCount++);
+
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMPNIF]);
+ chunk->addByte(LSCRIPTTypeByte[mType]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addLabel(jumpname);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ S32 tjump = gTempJumpCount++;
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "brfalse LabelTempJump%d\n", tjump);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LabelTempJump%d:\n", tjump);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptIfElse::getSize()
+{
+ return 0;
+}
+
+void LLScriptIfElse::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "if ( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "else\n");
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMPNIF ##Temp Jump %d##\n", tjump1);
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMP ##Temp Jump %d##\n", tjump2);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump1);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump2);
+ }
+ break;
+ case LSCP_PRUNE:
+ {
+ BOOL arg1 = TRUE, arg2 = TRUE;
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, arg1, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, arg2, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ prunearg = arg1 && arg2;
+ }
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mType = type;
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ char jumpname1[32];
+ sprintf(jumpname1, "##Temp Jump %d##", gTempJumpCount++);
+ char jumpname2[32];
+ sprintf(jumpname2, "##Temp Jump %d##", gTempJumpCount++);
+
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMPNIF]);
+ chunk->addByte(LSCRIPTTypeByte[mType]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname1);
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMP]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname2);
+ chunk->addLabel(jumpname1);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addLabel(jumpname2);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "brfalse LabelTempJump%d\n", tjump1);
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "br LabelTempJump%d\n", tjump2);
+ fprintf(fp, "LabelTempJump%d:\n", tjump1);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LabelTempJump%d:\n", tjump2);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement1->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement2->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ };
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptFor::getSize()
+{
+ return 0;
+}
+
+void LLScriptFor::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "for ( ");
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ; ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ; ");
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMPNIF ##Temp Jump %d##\n", tjump2);
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMP ##Temp Jump %d##\n", tjump1);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump2);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_TYPE:
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mType = type;
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ char jumpname1[32];
+ sprintf(jumpname1, "##Temp Jump %d##", gTempJumpCount++);
+ char jumpname2[32];
+ sprintf(jumpname2, "##Temp Jump %d##", gTempJumpCount++);
+
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addLabel(jumpname1);
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMPNIF]);
+ chunk->addByte(LSCRIPTTypeByte[mType]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname2);
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMP]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname1);
+ chunk->addLabel(jumpname2);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "LabelTempJump%d:\n", tjump1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "brfalse LabelTempJump%d\n", tjump2);
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "br LabelTempJump%d\n", tjump1);
+ fprintf(fp, "LabelTempJump%d:\n", tjump2);
+ }
+ break;
+ default:
+ if(mSequence)
+ mSequence->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mExpressionList)
+ mExpressionList->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if(mStatement)
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptDoWhile::getSize()
+{
+ return 0;
+}
+
+void LLScriptDoWhile::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "do\n");
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "while( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " );\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump1);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMPIF ##Temp Jump %d##\n", tjump1);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_TYPE:
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mType = type;
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ char jumpname1[32];
+ sprintf(jumpname1, "##Temp Jump %d##", gTempJumpCount++);
+
+ chunk->addLabel(jumpname1);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMPIF]);
+ chunk->addByte(LSCRIPTTypeByte[mType]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname1);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ fprintf(fp, "LabelTempJump%d:\n", tjump1);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "brtrue LabelTempJump%d\n", tjump1);
+ }
+ break;
+ default:
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptWhile::getSize()
+{
+ return 0;
+}
+
+void LLScriptWhile::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "while( ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMPNIF ##Temp Jump %d##\n", tjump2);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "JUMP ##Temp Jump %d##\n", tjump1);
+ fprintf(fp, "LABEL ##Temp Jump %d##\n", tjump2);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_TYPE:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mType = type;
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ char jumpname1[32];
+ sprintf(jumpname1, "##Temp Jump %d##", gTempJumpCount++);
+ char jumpname2[32];
+ sprintf(jumpname2, "##Temp Jump %d##", gTempJumpCount++);
+
+ chunk->addLabel(jumpname1);
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMPNIF]);
+ chunk->addByte(LSCRIPTTypeByte[mType]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname2);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_JUMP]);
+ chunk->addBytes(LSCRIPTDataSize[LST_INTEGER]);
+ chunk->addJump(jumpname1);
+ chunk->addLabel(jumpname2);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ S32 tjump1 = gTempJumpCount++;
+ S32 tjump2 = gTempJumpCount++;
+ fprintf(fp, "LabelTempJump%d:\n", tjump1);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "brfalse LabelTempJump%d\n", tjump2);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "br LabelTempJump%d\n", tjump1);
+ fprintf(fp, "LabelTempJump%d:\n", tjump2);
+ }
+ break;
+ default:
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptDeclaration::getSize()
+{
+ return mType->getSize();
+}
+
+void LLScriptDeclaration::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mExpression)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\t");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " = ");
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ }
+ else
+ {
+ fdotabs(fp, tabs, tabsize);
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\t");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ";\n");
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ fprintf(fp, "%s%d [%s]\n", LSCRIPTTypeLocalDeclaration[mIdentifier->mScopeEntry->mType], mIdentifier->mScopeEntry->mOffset, mIdentifier->mName);
+ }
+ else if (mIdentifier->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ }
+ break;
+ case LSCP_PRUNE:
+ if (ptype == LSPRUNE_DEAD_CODE)
+ prunearg = TRUE;
+ else
+ prunearg = FALSE;
+ break;
+ case LSCP_SCOPE_PASS1:
+ // Check to see if a declaration is valid here.
+ if (!mAllowDeclarations)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_NEED_NEW_SCOPE);
+ }
+ // add labels to scope
+ else if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ // this needs to go after expression decent to make sure that we don't add ourselves or something silly
+ // check expression if it exists
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_VARIABLE, mType->mType);
+ }
+ break;
+ case LSCP_TYPE:
+ // if there is an expression, it must be promotable to variable type
+ if (mExpression && mIdentifier->mScopeEntry)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!legal_assignment(mIdentifier->mScopeEntry->mType, type))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_TYPE_MISMATCH);
+ }
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ mIdentifier->mScopeEntry->mOffset = (S32)count;
+ mIdentifier->mScopeEntry->mSize = mType->getSize();
+ count += mIdentifier->mScopeEntry->mSize;
+ // Index into locals is current number of locals. Stored in mCount member of mScopeEntry.
+ mIdentifier->mScopeEntry->mCount = entry->mLocals.getNumber();
+ entry->mLocals.addType(mType->mType);
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, LSCP_TO_STACK, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mExpression->mReturnType != mIdentifier->mScopeEntry->mType)
+ {
+ cast2stack(chunk, mExpression->mReturnType, mIdentifier->mScopeEntry->mType);
+ }
+ switch(mExpression->mReturnType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ }
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADSP]);
+ }
+ break;
+ case LST_LIST:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADLP]);
+ }
+ break;
+ case LST_VECTOR:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADVP]);
+ }
+ break;
+ case LST_QUATERNION:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADQP]);
+ }
+ break;
+ default:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ }
+ break;
+ }
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ S32 address = mIdentifier->mScopeEntry->mOffset;
+ chunk->addInteger(address);
+ }
+ }
+ else
+ {
+ switch(mIdentifier->mScopeEntry->mType)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(0);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ }
+ break;
+ case LST_STRING:
+ case LST_KEY:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGS]);
+ chunk->addBytes("", 1);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADSP]);
+ }
+ break;
+ case LST_LIST:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_STACKTOL]);
+ chunk->addInteger(0);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADLP]);
+ }
+ break;
+ case LST_VECTOR:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGV]);
+ chunk->addFloat(0);
+ chunk->addFloat(0);
+ chunk->addFloat(0);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADVP]);
+ }
+ break;
+ case LST_QUATERNION:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGQ]);
+ chunk->addFloat(1);
+ chunk->addFloat(0);
+ chunk->addFloat(0);
+ chunk->addFloat(0);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADQP]);
+ }
+ break;
+ default:
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ chunk->addByte(LSCRIPTOpCodes[LOPC_PUSHARGI]);
+ chunk->addInteger(0);
+ chunk->addByte(LSCRIPTOpCodes[LOPC_LOADP]);
+ }
+ break;
+ }
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ S32 address = mIdentifier->mScopeEntry->mOffset;
+ chunk->addInteger(address);
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ if (mExpression)
+ {
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mIdentifier->mScopeEntry->mIDType == LIT_VARIABLE)
+ {
+ if(is_parameter(mIdentifier, entry))
+ {
+ // Parameter, store by name.
+ fprintf(fp, "starg.s %s\n", mIdentifier->mScopeEntry->mIdentifier);
+ }
+ else
+ {
+ // Local, store by index.
+ fprintf(fp, "stloc.s %d\n", mIdentifier->mScopeEntry->mCount);
+ }
+ }
+ else if (mIdentifier->mScopeEntry->mIDType == LIT_GLOBAL)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_UNDEFINED_NAME);
+ }
+ }
+ break;
+ default:
+ if (mExpression)
+ {
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mExpression->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptCompoundStatement::getSize()
+{
+ return 0;
+}
+
+void LLScriptCompoundStatement::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mStatement)
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ mStatement->recurse(fp, tabs + 1, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "}\n");
+ }
+ else
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "}\n");
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs + 1, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_PRUNE:
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ prunearg = FALSE;
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ // compound statements create a new scope
+ if (mStatement)
+ {
+ mStatementScope = new LLScriptScope(gScopeStringTable);
+ mStatementScope->addParentScope(scope);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mStatementScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_SCOPE_PASS2:
+ // compound statements create a new scope
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mStatementScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs + 1, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void LLScriptEventHandler::addEvent(LLScriptEventHandler *event)
+{
+ if (mNextp)
+ {
+ event->mNextp = mNextp;
+ }
+ mNextp = event;
+}
+
+void LLScriptEventHandler::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptEventHandler::getSize()
+{
+ return mStackSpace;
+}
+
+U64 gCurrentHandler = 0;
+
+void print_cil_local_init(FILE* fp, LLScriptScopeEntry* scopeEntry)
+{
+ if(scopeEntry->mLocals.getNumber() > 0)
+ {
+ fprintf(fp, ".locals init (");
+ for(int local = 0; local < scopeEntry->mLocals.getNumber(); ++local)
+ {
+ if(local > 0)
+ {
+ fprintf(fp, ", ");
+ }
+ print_cil_type(fp, scopeEntry->mLocals.getType(local));
+ }
+ fprintf(fp, ")\n");
+ }
+}
+
+void LLScriptEventHandler::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "}\n");
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, getSize(), mScopeEntry, entrycount, NULL);
+ }
+ if (mbNeedTrailingReturn)
+ {
+ print_return(fp, mScopeEntry);
+ }
+ fprintf(fp, "\n");
+ break;
+ case LSCP_PRUNE:
+ mbNeedTrailingReturn = FALSE;
+ prunearg = TRUE;
+ mStatement->recurse(fp, tabs, tabsize, pass, LSPRUNE_EVENTS, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!prunearg)
+ {
+ // this means that we didn't end with a return statement, need to add one
+ mbNeedTrailingReturn = TRUE;
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ // create event level scope
+ mEventScope = new LLScriptScope(gScopeStringTable);
+ mEventScope->addParentScope(scope);
+
+ // add event parameters
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mEventScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mEventScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_SCOPE_PASS2:
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mEventScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_TYPE:
+ mScopeEntry = new LLScriptScopeEntry("Event", LIT_HANDLER, LST_NULL);
+ switch(mEventp->mType)
+ {
+ case LSTT_STATE_ENTRY:
+ break;
+ case LSTT_STATE_EXIT:
+ break;
+ case LSTT_TOUCH_START:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_TOUCH:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_TOUCH_END:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_COLLISION_START:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_COLLISION:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_COLLISION_END:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_LAND_COLLISION_START:
+ mScopeEntry->mFunctionArgs.addType(LST_VECTOR);
+ break;
+ case LSTT_LAND_COLLISION:
+ mScopeEntry->mFunctionArgs.addType(LST_VECTOR);
+ break;
+ case LSTT_LAND_COLLISION_END:
+ mScopeEntry->mFunctionArgs.addType(LST_VECTOR);
+ break;
+ case LSTT_INVENTORY:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_ATTACH:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ break;
+ case LSTT_DATASERVER:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ break;
+ case LSTT_TIMER:
+ break;
+ case LSTT_MOVING_START:
+ break;
+ case LSTT_MOVING_END:
+ break;
+ case LSTT_OBJECT_REZ:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ break;
+ case LSTT_REMOTE_DATA:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ break;
+ case LSTT_CHAT:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ break;
+ case LSTT_SENSOR:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_CONTROL:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_LINK_MESSAGE:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ break;
+ case LSTT_MONEY:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_EMAIL:
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_REZ:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_NO_SENSOR:
+ break;
+ case LSTT_AT_TARGET:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_VECTOR);
+ mScopeEntry->mFunctionArgs.addType(LST_VECTOR);
+ break;
+ case LSTT_NOT_AT_TARGET:
+ break;
+ case LSTT_AT_ROT_TARGET:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_QUATERNION);
+ mScopeEntry->mFunctionArgs.addType(LST_QUATERNION);
+ break;
+ case LSTT_NOT_AT_ROT_TARGET:
+ break;
+ case LSTT_RTPERMISSIONS:
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ break;
+ case LSTT_HTTP_RESPONSE:
+ mScopeEntry->mFunctionArgs.addType(LST_KEY);
+ mScopeEntry->mFunctionArgs.addType(LST_INTEGER);
+ mScopeEntry->mFunctionArgs.addType(LST_LIST);
+ mScopeEntry->mFunctionArgs.addType(LST_STRING);
+ break;
+
+ default:
+ break;
+ }
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_RESOURCE:
+ // first determine resource counts for globals
+ count = 0;
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mStatement)
+ {
+ entrycount = 0;
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mScopeEntry, entrycount, NULL);
+ fprintf(fp, "Function Args: %s\n", mScopeEntry->mFunctionArgs.mString);
+ fprintf(fp, "Local List: %s\n", mScopeEntry->mLocals.mString);
+ }
+ mStackSpace = (S32)count;
+ break;
+ case LSCP_DETERMINE_HANDLERS:
+ count |= LSCRIPTStateBitField[mEventp->mType];
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // order for event handler
+ // set jump table value
+ S32 jumpoffset;
+ jumpoffset = LSCRIPTDataSize[LST_INTEGER]*get_event_handler_jump_position(gCurrentHandler, mEventp->mType)*2;
+
+ integer2bytestream(chunk->mCodeChunk, jumpoffset, chunk->mCurrentOffset);
+
+ // 0 - 3: offset to actual data
+ S32 offsetoffset = chunk->mCurrentOffset;
+ S32 offsetdelta = 0;
+ chunk->addBytes(4);
+
+ // null terminated event name and null terminated parameters
+ if (mEventp)
+ {
+ LLScriptByteCodeChunk *event = new LLScriptByteCodeChunk(FALSE);
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, event, heap, stacksize, entry, entrycount, NULL);
+ chunk->addBytes(event->mCodeChunk, event->mCurrentOffset);
+ delete event;
+ }
+ chunk->addBytes(1);
+
+ // now we're at the first opcode
+ offsetdelta = chunk->mCurrentOffset - offsetoffset;
+ integer2bytestream(chunk->mCodeChunk, offsetoffset, offsetdelta);
+
+ // get ready to compute the number of bytes of opcode
+ offsetdelta = chunk->mCurrentOffset;
+
+ if (mStatement)
+ {
+ LLScriptByteCodeChunk *statements = new LLScriptByteCodeChunk(TRUE);
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, statements, heap, getSize(), mScopeEntry, entrycount, NULL);
+ statements->connectJumps();
+ chunk->addBytes(statements->mCodeChunk, statements->mCurrentOffset);
+ delete statements;
+ }
+ if (mbNeedTrailingReturn)
+ {
+ add_return(chunk, mScopeEntry);
+ }
+ // now stuff in the number of bytes of stack space that this routine needs
+ integer2bytestream(chunk->mCodeChunk, jumpoffset, getSize());
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Method signature prefix.
+ fprintf(fp, ".method public hidebysig instance default void ");
+
+ // Mangle event handler name by prefixing it with state name. Allows state changing by finding handlers prefixed with new state name.
+ fprintf(fp, entry->mIdentifier);
+
+ // Handler name and arguments.
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+
+ // Method signature postfix.
+ fprintf(fp, " cil managed\n");
+
+ // Function header.
+ fprintf(fp,"{\n");
+ fprintf(fp, ".maxstack 500\n"); // TODO: Calculated stack size...
+
+ // Allocate space for locals.
+ print_cil_local_init(fp, mScopeEntry);
+
+ if (mStatement)
+ {
+ // Pass scope so identifiers can determine parameter or local.
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mScopeEntry, entrycount, NULL);
+ }
+
+ // Function footer.
+ fprintf(fp, "\nret\n"); // TODO: Check whether return needed?
+ fprintf(fp, "}\n");
+
+ break;
+ default:
+ mEventp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mStatement)
+ {
+ mStatement->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void LLScriptFunctionDec::addFunctionParameter(LLScriptFunctionDec *dec)
+{
+ if (mNextp)
+ {
+ dec->mNextp = mNextp;
+ }
+ mNextp = dec;
+}
+
+void LLScriptFunctionDec::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ if (mNextp)
+ {
+ fprintf(fp, ", ");
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+
+}
+
+S32 LLScriptFunctionDec::getSize()
+{
+ return 0;
+}
+
+void LLScriptFunctionDec::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_SCOPE_PASS1:
+ // add function names into global scope
+ if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_VARIABLE, mType->mType);
+ }
+ break;
+ case LSCP_RESOURCE:
+ {
+ // we're just tryng to determine how much space the variable needs
+ mIdentifier->mScopeEntry->mOffset = (S32)count;
+ mIdentifier->mScopeEntry->mSize = mType->getSize();
+ count += mIdentifier->mScopeEntry->mSize;
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // return type
+ char typereturn;
+ if (mType)
+ {
+ typereturn = LSCRIPTTypeByte[mType->mType];
+ }
+ else
+ {
+ typereturn = LSCRIPTTypeByte[LST_NULL];
+ }
+ chunk->addBytes(&typereturn, 1);
+ // name
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ chunk->addBytes(mIdentifier->mName, strlen(mIdentifier->mName) + 1);
+#else
+ chunk->addBytes(1);
+#endif
+ }
+ break;
+ case LSCP_BUILD_FUNCTION_ARGS:
+ {
+ entry->mFunctionArgs.addType(mType->mType);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ default:
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void LLScriptGlobalFunctions::addGlobalFunction(LLScriptGlobalFunctions *global)
+{
+ if (mNextp)
+ {
+ global->mNextp = mNextp;
+ }
+ mNextp = global;
+}
+
+void LLScriptGlobalFunctions::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptGlobalFunctions::getSize()
+{
+ return 0;
+}
+
+void LLScriptGlobalFunctions::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ if (mType)
+ {
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\t");
+ }
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mParameters)
+ {
+ fprintf(fp, "( ");
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ }
+ else
+ {
+ fprintf(fp, "()\n");
+ }
+ if (mStatements)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, entrycount, NULL);
+ }
+ else
+ {
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "}\n");
+ }
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mParameters)
+ {
+ fprintf(fp, "( ");
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )\n");
+ }
+ else
+ {
+ fprintf(fp, "()\n");
+ }
+ if (mStatements)
+ {
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, mIdentifier->mScopeEntry->mSize, mIdentifier->mScopeEntry, entrycount, NULL);
+ }
+ if (mbNeedTrailingReturn)
+ {
+ print_return(fp, mIdentifier->mScopeEntry);
+ }
+ fprintf(fp, "\n");
+ break;
+ case LSCP_PRUNE:
+ mbNeedTrailingReturn = FALSE;
+ if (mType)
+ {
+ prunearg = TRUE;
+ mStatements->recurse(fp, tabs, tabsize, pass, LSPRUNE_GLOBAL_NON_VOIDS, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!prunearg)
+ {
+ gErrorToText.writeError(fp, this, LSERROR_NO_RETURN);
+ }
+ }
+ else
+ {
+ prunearg = TRUE;
+ mStatements->recurse(fp, tabs, tabsize, pass, LSPRUNE_GLOBAL_VOIDS, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (!prunearg)
+ {
+ // this means that we didn't end with a return statement, need to add one
+ mbNeedTrailingReturn = TRUE;
+ }
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ // add function names into global scope
+ if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ if (mType)
+ {
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_FUNCTION, mType->mType);
+ }
+ else
+ {
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_FUNCTION, LST_NULL);
+ }
+ }
+
+ // create function level scope
+ mFunctionScope = new LLScriptScope(gScopeStringTable);
+ mFunctionScope->addParentScope(scope);
+
+ // function parameters
+ if (mParameters)
+ {
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mFunctionScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mFunctionScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_SCOPE_PASS2:
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mFunctionScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+
+ if (mParameters)
+ {
+ if (mIdentifier->mScopeEntry)
+ {
+ mParameters->recurse(fp, tabs, tabsize, LSCP_BUILD_FUNCTION_ARGS, ptype, prunearg, mFunctionScope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, 0, NULL);
+ }
+ }
+ break;
+ case LSCP_TYPE:
+ if (mType)
+ {
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, mType->mType, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ else
+ {
+ type = LST_NULL;
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_RESOURCE:
+ // first determine resource counts for globals
+ count = 0;
+
+ if (mParameters)
+ {
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+
+ if (mIdentifier->mScopeEntry)
+ {
+ // this isn't a bug . . . Offset is used to determine how much is params vs locals
+ mIdentifier->mScopeEntry->mOffset = (S32)count;
+ }
+
+ if (mStatements)
+ {
+ entrycount = 0;
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, entrycount, NULL);
+ fprintf(fp, "Function Args: %s\n", mIdentifier->mScopeEntry->mFunctionArgs.mString);
+ fprintf(fp, "Local List: %s\n", mIdentifier->mScopeEntry->mLocals.mString);
+ if (mIdentifier->mScopeEntry)
+ {
+ mIdentifier->mScopeEntry->mSize = (S32)count;
+ }
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // order for global functions
+ // set jump table value
+ S32 jumpoffset = LSCRIPTDataSize[LST_INTEGER]*mIdentifier->mScopeEntry->mCount + LSCRIPTDataSize[LST_INTEGER];
+ integer2bytestream(chunk->mCodeChunk, jumpoffset, chunk->mCurrentOffset);
+
+ // 0 - 3: offset to actual data
+ S32 offsetoffset = chunk->mCurrentOffset;
+ S32 offsetdelta = 0;
+ chunk->addBytes(4);
+
+ // null terminated function name
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ chunk->addBytes(mIdentifier->mName, strlen(mIdentifier->mName) + 1);
+#else
+ chunk->addBytes(1);
+#endif
+ // return type
+ char typereturn;
+ if (mType)
+ {
+ typereturn = LSCRIPTTypeByte[mType->mType];
+ }
+ else
+ {
+ typereturn = LSCRIPTTypeByte[LST_NULL];
+ }
+ chunk->addBytes(&typereturn, 1);
+
+ // null terminated parameters, followed by type
+ if (mParameters)
+ {
+ LLScriptByteCodeChunk *params = new LLScriptByteCodeChunk(FALSE);
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, params, heap, stacksize, entry, entrycount, NULL);
+ chunk->addBytes(params->mCodeChunk, params->mCurrentOffset);
+ delete params;
+ }
+ chunk->addBytes(1);
+
+ // now we're at the first opcode
+ offsetdelta = chunk->mCurrentOffset - offsetoffset;
+ integer2bytestream(chunk->mCodeChunk, offsetoffset, offsetdelta);
+
+ if (mStatements)
+ {
+ LLScriptByteCodeChunk *statements = new LLScriptByteCodeChunk(TRUE);
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, statements, heap, mIdentifier->mScopeEntry->mSize, mIdentifier->mScopeEntry, entrycount, NULL);
+ statements->connectJumps();
+ chunk->addBytes(statements->mCodeChunk, statements->mCurrentOffset);
+ delete statements;
+ }
+ if (mbNeedTrailingReturn)
+ {
+ add_return(chunk, mIdentifier->mScopeEntry);
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ {
+ // Function header.
+ fprintf(fp, ".method public hidebysig instance default ");
+ print_cil_type(fp, mType ? mType->mType : LST_NULL);
+ fprintf(fp, " ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mParameters)
+ {
+ fprintf(fp, "( ");
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, " )");
+ }
+ else
+ {
+ fprintf(fp, "()");
+ }
+ fprintf(fp, " cil managed\n{\n");
+ fprintf(fp, ".maxstack 500\n"); // TODO: Calculated stack size...
+
+ // Allocate space for locals.
+ print_cil_local_init(fp, mIdentifier->mScopeEntry);
+
+ if (mStatements)
+ {
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, mIdentifier->mScopeEntry->mSize, mIdentifier->mScopeEntry, entrycount, NULL);
+ }
+
+ // Function footer.
+ if (mbNeedTrailingReturn)
+ {
+ fprintf(fp, "ret\n");
+ }
+ fprintf(fp, "}\n");
+ fprintf(fp, "\n");
+ }
+ break;
+ default:
+ if (mType)
+ {
+ mType->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mParameters)
+ {
+ mParameters->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ if (mStatements)
+ {
+ mStatements->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+void LLScriptState::addState(LLScriptState *state)
+{
+ if (mNextp)
+ {
+ state->mNextp = mNextp;
+ }
+ mNextp = state;
+}
+
+void LLScriptState::gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mNextp)
+ {
+ mNextp->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+}
+
+S32 LLScriptState::getSize()
+{
+ return 0;
+}
+
+void LLScriptState::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ fdotabs(fp, tabs, tabsize);
+ if (mType == LSSTYPE_DEFAULT)
+ {
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ }
+ else
+ {
+ fprintf(fp, "state ");
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "{\n");
+ }
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs + 1, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ fdotabs(fp, tabs, tabsize);
+ fprintf(fp, "}\n");
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, ":\n");
+ if (mEvent)
+ {
+ fprintf(fp, "EVENTS\n");
+ mEvent->recurse(fp, tabs + 1, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+ break;
+ case LSCP_SCOPE_PASS1:
+ // add state name
+ if (scope->checkEntry(mIdentifier->mName))
+ {
+ gErrorToText.writeError(fp, this, LSERROR_DUPLICATE_NAME);
+ }
+ else
+ {
+ mIdentifier->mScopeEntry = scope->addEntry(mIdentifier->mName, LIT_STATE, LST_NULL);
+ }
+ // now do the events
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_SCOPE_PASS2:
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_TYPE:
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // order for states
+ // set jump table value
+
+ S32 jumpoffset;
+ if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
+ {
+ jumpoffset = LSCRIPTDataSize[LST_INTEGER]*3*mIdentifier->mScopeEntry->mCount + LSCRIPTDataSize[LST_INTEGER];
+ }
+ else
+ {
+ jumpoffset = LSCRIPTDataSize[LST_INTEGER]*2*mIdentifier->mScopeEntry->mCount + LSCRIPTDataSize[LST_INTEGER];
+ }
+ integer2bytestream(chunk->mCodeChunk, jumpoffset, chunk->mCurrentOffset);
+
+ // need to figure out what handlers this state has registered
+ // we'll use to count to find it
+ count = 0;
+
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs, tabsize, LSCP_DETERMINE_HANDLERS, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ gCurrentHandler = count;
+ }
+
+ // add description word into chunk
+ if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
+ {
+ u642bytestream(chunk->mCodeChunk, jumpoffset, gCurrentHandler);
+ }
+ else
+ {
+ integer2bytestream(chunk->mCodeChunk, jumpoffset, (S32)gCurrentHandler);
+ }
+
+
+ // 0 - 3: offset to event jump table
+ S32 offsetoffset = chunk->mCurrentOffset;
+ S32 offsetdelta = 0;
+ chunk->addBytes(4);
+
+ // null terminated state name
+#ifdef LSL_INCLUDE_DEBUG_INFO
+ chunk->addBytes(mIdentifier->mName, strlen(mIdentifier->mName) + 1);
+#else
+ chunk->addBytes(1);
+#endif
+ // now we're at the jump table
+ offsetdelta = chunk->mCurrentOffset - offsetoffset;
+ integer2bytestream(chunk->mCodeChunk, offsetoffset, offsetdelta);
+
+ // add the events themselves
+ if (mEvent)
+ {
+ LLScriptByteCodeChunk *events = new LLScriptByteCodeChunk(FALSE);
+ // make space for event jump table
+ events->addBytes(LSCRIPTDataSize[LST_INTEGER]*get_number_of_event_handlers(gCurrentHandler)*2);
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, events, heap, stacksize, entry, entrycount, NULL);
+ chunk->addBytes(events->mCodeChunk, events->mCurrentOffset);
+ delete events;
+ }
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+ if (mEvent)
+ {
+ // Entry not used at this level, so pass state scope as entry parameter, to allow event handlers to do name mangling.
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, mIdentifier->mScopeEntry, entrycount, NULL);
+ }
+ break;
+ default:
+ if (mType == LSSTYPE_DEFAULT)
+ {
+ }
+ else
+ {
+ mIdentifier->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ if (mEvent)
+ {
+ mEvent->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ break;
+ }
+ gonext(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+}
+
+S32 LLScriptScript::getSize()
+{
+ return 0;
+}
+
+LLScriptScript::LLScriptScript(LLScritpGlobalStorage *globals,
+ LLScriptState *states) :
+ LLScriptFilePosition(0, 0),
+ mStates(states), mGlobalScope(NULL), mGlobals(NULL), mGlobalFunctions(NULL), mGodLike(FALSE)
+{
+ const char DEFAULT_BYTECODE_FILENAME[] = "lscript.lso";
+ strcpy(mBytecodeDest, DEFAULT_BYTECODE_FILENAME);
+
+ LLScriptGlobalVariable *tvar;
+ LLScriptGlobalFunctions *tfunc;
+ LLScritpGlobalStorage *temp;
+
+ temp = globals;
+ while(temp)
+ {
+ if (temp->mbGlobalFunction)
+ {
+ if (!mGlobalFunctions)
+ {
+ mGlobalFunctions = (LLScriptGlobalFunctions *)temp->mGlobal;
+ }
+ else
+ {
+ tfunc = mGlobalFunctions;
+ while(tfunc->mNextp)
+ {
+ tfunc = tfunc->mNextp;
+ }
+ tfunc->mNextp = (LLScriptGlobalFunctions *)temp->mGlobal;
+ }
+ }
+ else
+ {
+ if (!mGlobals)
+ {
+ mGlobals = (LLScriptGlobalVariable *)temp->mGlobal;
+ }
+ else
+ {
+ tvar = mGlobals;
+ while(tvar->mNextp)
+ {
+ tvar = tvar->mNextp;
+ }
+ tvar->mNextp = (LLScriptGlobalVariable *)temp->mGlobal;
+ }
+ }
+ temp = temp->mNextp;
+ }
+}
+
+void LLScriptScript::setBytecodeDest(const char* dst_filename)
+{
+ strncpy(mBytecodeDest, dst_filename, MAX_STRING);
+ mBytecodeDest[MAX_STRING-1] = '\0';
+}
+
+void print_cil_globals(FILE* fp, LLScriptGlobalVariable* global)
+{
+ fprintf(fp, ".field private ");
+ print_cil_type(fp, global->mType->mType);
+ fprintf(fp, " ");
+ fprintf(fp, global->mIdentifier->mName);
+ fprintf(fp, "\n");
+ if(NULL != global->mNextp)
+ {
+ print_cil_globals(fp, global->mNextp);
+ }
+}
+
+void LLScriptScript::recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+{
+ if (gErrorToText.getErrors())
+ {
+ return;
+ }
+ switch(pass)
+ {
+ case LSCP_PRETTY_PRINT:
+ if (mGlobals)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+
+ if (mGlobalFunctions)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+
+ fdotabs(fp, tabs, tabsize);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_PRUNE:
+ if (mGlobalFunctions)
+ {
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ }
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_SCOPE_PASS1:
+ {
+ mGlobalScope = new LLScriptScope(gScopeStringTable);
+ // zeroth, add library functions to global scope
+ S32 i;
+ char *arg;
+ LLScriptScopeEntry *sentry;
+ for (i = 0; i < gScriptLibrary.mNextNumber; i++)
+ {
+ // First, check to make sure this isn't a god only function, or that the viewer's agent is a god.
+ if (!gScriptLibrary.mFunctions[i]->mGodOnly || mGodLike)
+ {
+ if (gScriptLibrary.mFunctions[i]->mReturnType)
+ sentry = mGlobalScope->addEntry(gScriptLibrary.mFunctions[i]->mName, LIT_LIBRARY_FUNCTION, char2type(*gScriptLibrary.mFunctions[i]->mReturnType));
+ else
+ sentry = mGlobalScope->addEntry(gScriptLibrary.mFunctions[i]->mName, LIT_LIBRARY_FUNCTION, LST_NULL);
+ sentry->mLibraryNumber = i;
+ arg = gScriptLibrary.mFunctions[i]->mArgs;
+ if (arg)
+ {
+ while (*arg)
+ {
+ sentry->mFunctionArgs.addType(char2type(*arg));
+ sentry->mSize += LSCRIPTDataSize[char2type(*arg)];
+ sentry->mOffset += LSCRIPTDataSize[char2type(*arg)];
+ arg++;
+ }
+ }
+ }
+ }
+ // first go and collect all the global variables
+ if (mGlobals)
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ // second, do the global functions
+ if (mGlobalFunctions)
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ // now do states
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+ case LSCP_SCOPE_PASS2:
+ // now we're checking jumps, function calls, and state transitions
+ if (mGlobalFunctions)
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, mGlobalScope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_TYPE:
+ // first we need to check global variables
+ if (mGlobals)
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ // now do global functions and states
+ if (mGlobalFunctions)
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_RESOURCE:
+ // first determine resource counts for globals
+ count = 0;
+ if (mGlobals)
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ // now do locals
+ if (mGlobalFunctions)
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ case LSCP_EMIT_ASSEMBLY:
+
+ if (mGlobals)
+ {
+ fprintf(fp, "GLOBALS\n");
+ fdotabs(fp, tabs, tabsize);
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+
+ if (mGlobalFunctions)
+ {
+ fprintf(fp, "GLOBAL FUNCTIONS\n");
+ fdotabs(fp, tabs, tabsize);
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+
+ fprintf(fp, "STATES\n");
+ fdotabs(fp, tabs, tabsize);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ break;
+ case LSCP_EMIT_BYTE_CODE:
+ {
+ // first, create data structure to hold the whole shebang
+ LLScriptScriptCodeChunk *code = new LLScriptScriptCodeChunk(TOP_OF_MEMORY);
+
+ // ok, let's add the registers, all zeroes for now
+ S32 i;
+ S32 nooffset = 0;
+
+ for (i = LREG_IP; i < LREG_EOF; i++)
+ {
+ if (i < LREG_NCE)
+ code->mRegisters->addBytes(4);
+ else if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
+ code->mRegisters->addBytes(8);
+ }
+ // global variables
+ if (mGlobals)
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code->mGlobalVariables, code->mHeap, stacksize, entry, entrycount, NULL);
+
+ // put the ending heap block onto the heap
+ U8 *temp;
+ S32 size = lsa_create_data_block(&temp, NULL, 0);
+ code->mHeap->addBytes(temp, size);
+ delete [] temp;
+
+ // global functions
+ // make space for global function jump table
+ if (mGlobalFunctions)
+ {
+ code->mGlobalFunctions->addBytes(LSCRIPTDataSize[LST_INTEGER]*mGlobalScope->mFunctionCount + LSCRIPTDataSize[LST_INTEGER]);
+ integer2bytestream(code->mGlobalFunctions->mCodeChunk, nooffset, mGlobalScope->mFunctionCount);
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code->mGlobalFunctions, NULL, stacksize, entry, entrycount, NULL);
+ }
+
+
+ nooffset = 0;
+ // states
+ // make space for state jump/info table
+ if (LSL2_CURRENT_MAJOR_VERSION == LSL2_MAJOR_VERSION_TWO)
+ {
+ code->mStates->addBytes(LSCRIPTDataSize[LST_INTEGER]*3*mGlobalScope->mStateCount + LSCRIPTDataSize[LST_INTEGER]);
+ }
+ else
+ {
+ code->mStates->addBytes(LSCRIPTDataSize[LST_INTEGER]*2*mGlobalScope->mStateCount + LSCRIPTDataSize[LST_INTEGER]);
+ }
+ integer2bytestream(code->mStates->mCodeChunk, nooffset, mGlobalScope->mStateCount);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, code->mStates, NULL, stacksize, entry, entrycount, NULL);
+
+ // now, put it all together and spit it out
+ // we need
+ FILE *bcfp = LLFile::fopen(mBytecodeDest, "wb");
+
+ code->build(fp, bcfp);
+ fclose(bcfp);
+ }
+ break;
+ case LSCP_EMIT_CIL_ASSEMBLY:
+
+ // Output dependencies.
+ fprintf(fp, ".assembly extern mscorlib {.ver 1:0:5000:0}\n");
+ fprintf(fp, ".assembly extern LScriptLibrary {.ver 0:0:0:0}\n");
+
+ // Output assembly name.
+ fprintf(fp, ".assembly 'lsl' {.ver 0:0:0:0}\n");
+
+ // Output class header.
+ fprintf(fp, ".class public auto ansi beforefieldinit LSL extends [mscorlib]System.Object\n");
+ fprintf(fp, "{\n");
+
+ // Output globals as members.
+ if(NULL != mGlobals)
+ {
+ print_cil_globals(fp, mGlobals);
+ }
+
+ // Output "runtime". Only needed to allow stand alone execution. Not needed when compiling to DLL and using embedded runtime.
+ fprintf(fp, ".method public static hidebysig default void Main () cil managed\n");
+ fprintf(fp, "{\n");
+ fprintf(fp, ".entrypoint\n");
+ fprintf(fp, ".maxstack 2\n");
+ fprintf(fp, ".locals init (class LSL V_0)\n");
+ fprintf(fp, "newobj instance void class LSL::.ctor()\n");
+ fprintf(fp, "stloc.0\n");
+ fprintf(fp, "ldloc.0\n");
+ fprintf(fp, "callvirt instance void class LSL::defaultstate_entry()\n");
+ fprintf(fp, "ret\n");
+ fprintf(fp, "}\n");
+
+ // Output ctor header.
+ fprintf(fp, ".method public hidebysig specialname rtspecialname instance default void .ctor () cil managed\n");
+ fprintf(fp, "{\n");
+ fprintf(fp, ".maxstack 500\n");
+
+ // Initialise globals as members in ctor.
+ if (mGlobals)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+
+ // Output ctor footer.
+ fprintf(fp, "ldarg.0\n");
+ fprintf(fp, "call instance void valuetype [mscorlib]System.Object::.ctor()\n");
+ fprintf(fp, "ret\n");
+ fprintf(fp, "}\n");
+
+ // Output functions as methods.
+ if (mGlobalFunctions)
+ {
+ fdotabs(fp, tabs, tabsize);
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+ }
+
+ // Output states as name mangled methods.
+ fdotabs(fp, tabs, tabsize);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ fprintf(fp, "\n");
+
+ // Output class footer.
+ fprintf(fp, "}\n");
+
+ break;
+ default:
+ if (mGlobals)
+ mGlobals->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ if (mGlobalFunctions)
+ mGlobalFunctions->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ mStates->recurse(fp, tabs, tabsize, pass, ptype, prunearg, scope, type, basetype, count, chunk, heap, stacksize, entry, entrycount, NULL);
+ break;
+ }
+}
diff --git a/indra/lscript/lscript_compile/lscript_tree.h b/indra/lscript/lscript_compile/lscript_tree.h
new file mode 100644
index 0000000000..c36bae06b9
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_tree.h
@@ -0,0 +1,2279 @@
+/**
+ * @file lscript_tree.h
+ * @brief provides the classes required to build lscript's abstract syntax tree and symbol table
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_TREE_H
+#define LL_LSCRIPT_TREE_H
+
+#include <stdio.h>
+#include "stdtypes.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "linked_lists.h"
+#include "lscript_error.h"
+#include "lscript_typecheck.h"
+#include "lscript_byteformat.h"
+
+
+// Nota Bene: Class destructors don't delete pointed to classes because it isn't guaranteed that lex/yacc will build
+// complete data structures. Instead various chunks that are allocated are stored and deleted by allocation lists
+
+class LLScriptType : public LLScriptFilePosition
+{
+public:
+ LLScriptType(S32 line, S32 col, LSCRIPTType type)
+ : LLScriptFilePosition(line, col), mType(type)
+ {
+ }
+
+ ~LLScriptType() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTType mType;
+};
+
+// contains a literal or constant value
+class LLScriptConstant : public LLScriptFilePosition
+{
+public:
+ LLScriptConstant(S32 line, S32 col, LSCRIPTType type)
+ : LLScriptFilePosition(line, col), mType(type)
+ {
+ }
+
+ virtual ~LLScriptConstant() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTType mType;
+};
+
+class LLScriptConstantInteger : public LLScriptConstant
+{
+public:
+ LLScriptConstantInteger(S32 line, S32 col, S32 value)
+ : LLScriptConstant(line, col, LST_INTEGER), mValue(value)
+ {
+ }
+
+ ~LLScriptConstantInteger() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ S32 mValue;
+};
+
+class LLScriptConstantFloat : public LLScriptConstant
+{
+public:
+ LLScriptConstantFloat(S32 line, S32 col, F32 value)
+ : LLScriptConstant(line, col, LST_FLOATINGPOINT), mValue(value)
+ {
+ }
+
+ ~LLScriptConstantFloat() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ F32 mValue;
+};
+
+class LLScriptConstantString : public LLScriptConstant
+{
+public:
+ LLScriptConstantString(S32 line, S32 col, char *value)
+ : LLScriptConstant(line, col, LST_STRING), mValue(value)
+ {
+ }
+
+ ~LLScriptConstantString()
+ {
+ delete [] mValue;
+ mValue = NULL;
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ char *mValue;
+};
+
+// container for individual identifiers
+class LLScriptIdentifier : public LLScriptFilePosition
+{
+public:
+ LLScriptIdentifier(S32 line, S32 col, char *name, LLScriptType *type = NULL)
+ : LLScriptFilePosition(line, col), mName(name), mScopeEntry(NULL), mType(type)
+ {
+ }
+
+ ~LLScriptIdentifier()
+ {
+ delete [] mName;
+ mName = NULL;
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ char *mName;
+ LLScriptScopeEntry *mScopeEntry;
+ LLScriptType *mType;
+};
+
+typedef enum e_lscript_simple_assignable_type
+{
+ LSSAT_NULL,
+ LSSAT_IDENTIFIER,
+ LSSAT_CONSTANT,
+ LSSAT_VECTOR_CONSTANT,
+ LSSAT_QUATERNION_CONSTANT,
+ LSSAT_LIST_CONSTANT,
+ LSSAT_EOF
+} LSCRIPTSimpleAssignableType;
+
+class LLScriptSimpleAssignable : public LLScriptFilePosition
+{
+public:
+ LLScriptSimpleAssignable(S32 line, S32 col, LSCRIPTSimpleAssignableType type)
+ : LLScriptFilePosition(line, col), mType(type), mNextp(NULL)
+ {
+ }
+
+ void addAssignable(LLScriptSimpleAssignable *assign);
+
+ virtual ~LLScriptSimpleAssignable()
+ {
+ // don't delete next pointer because we're going to store allocation lists and delete from those
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTSimpleAssignableType mType;
+ LLScriptSimpleAssignable *mNextp;
+};
+
+class LLScriptSAIdentifier : public LLScriptSimpleAssignable
+{
+public:
+ LLScriptSAIdentifier(S32 line, S32 col, LLScriptIdentifier *identifier)
+ : LLScriptSimpleAssignable(line, col, LSSAT_IDENTIFIER), mIdentifier(identifier)
+ {
+ }
+
+ ~LLScriptSAIdentifier()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mIdentifier;
+};
+
+class LLScriptSAConstant : public LLScriptSimpleAssignable
+{
+public:
+ LLScriptSAConstant(S32 line, S32 col, LLScriptConstant *constant)
+ : LLScriptSimpleAssignable(line, col, LSSAT_CONSTANT), mConstant(constant)
+ {
+ }
+
+ ~LLScriptSAConstant()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptConstant *mConstant;
+};
+
+class LLScriptSAVector : public LLScriptSimpleAssignable
+{
+public:
+ LLScriptSAVector(S32 line, S32 col, LLScriptSimpleAssignable *e1,
+ LLScriptSimpleAssignable *e2,
+ LLScriptSimpleAssignable *e3)
+ : LLScriptSimpleAssignable(line, col, LSSAT_VECTOR_CONSTANT),
+ mEntry1(e1), mEntry2(e2), mEntry3(e3)
+ {
+ }
+
+ ~LLScriptSAVector()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptSimpleAssignable *mEntry1;
+ LLScriptSimpleAssignable *mEntry2;
+ LLScriptSimpleAssignable *mEntry3;
+};
+
+class LLScriptSAQuaternion : public LLScriptSimpleAssignable
+{
+public:
+ LLScriptSAQuaternion(S32 line, S32 col, LLScriptSimpleAssignable *e1,
+ LLScriptSimpleAssignable *e2,
+ LLScriptSimpleAssignable *e3,
+ LLScriptSimpleAssignable *e4)
+ : LLScriptSimpleAssignable(line, col, LSSAT_QUATERNION_CONSTANT),
+ mEntry1(e1), mEntry2(e2), mEntry3(e3), mEntry4(e4)
+ {
+ }
+
+ ~LLScriptSAQuaternion()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptSimpleAssignable *mEntry1;
+ LLScriptSimpleAssignable *mEntry2;
+ LLScriptSimpleAssignable *mEntry3;
+ LLScriptSimpleAssignable *mEntry4;
+};
+
+class LLScriptSAList : public LLScriptSimpleAssignable
+{
+public:
+ LLScriptSAList(S32 line, S32 col, LLScriptSimpleAssignable *elist)
+ : LLScriptSimpleAssignable(line, col, LSSAT_QUATERNION_CONSTANT), mEntryList(elist)
+ {
+ }
+
+ ~LLScriptSAList()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptSimpleAssignable *mEntryList;
+};
+
+// global variables
+class LLScriptGlobalVariable : public LLScriptFilePosition
+{
+public:
+ LLScriptGlobalVariable(S32 line, S32 col, LLScriptType *type,
+ LLScriptIdentifier *identifier,
+ LLScriptSimpleAssignable *assignable)
+ : LLScriptFilePosition(line, col), mType(type), mIdentifier(identifier), mAssignable(assignable), mNextp(NULL), mAssignableType(LST_NULL)
+ {
+ }
+
+ void addGlobal(LLScriptGlobalVariable *global);
+
+ ~LLScriptGlobalVariable()
+ {
+ }
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptType *mType;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptSimpleAssignable *mAssignable;
+ LLScriptGlobalVariable *mNextp;
+ LSCRIPTType mAssignableType;
+};
+
+// events
+
+class LLScriptEvent : public LLScriptFilePosition
+{
+public:
+ LLScriptEvent(S32 line, S32 col, LSCRIPTStateEventType type)
+ : LLScriptFilePosition(line, col), mType(type)
+ {
+ }
+
+ virtual ~LLScriptEvent()
+ {
+ // don't delete next pointer because we're going to store allocation lists and delete from those
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTStateEventType mType;
+};
+
+class LLScriptStateEntryEvent : public LLScriptEvent
+{
+public:
+ LLScriptStateEntryEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_STATE_ENTRY)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptStateEntryEvent() {}
+};
+
+class LLScriptStateExitEvent : public LLScriptEvent
+{
+public:
+ LLScriptStateExitEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_STATE_EXIT)
+ {
+ }
+
+ ~LLScriptStateExitEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+};
+
+class LLScriptTouchStartEvent : public LLScriptEvent
+{
+public:
+ LLScriptTouchStartEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_TOUCH_START), mCount(count)
+ {
+ }
+
+ ~LLScriptTouchStartEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptTouchEvent : public LLScriptEvent
+{
+public:
+ LLScriptTouchEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_TOUCH), mCount(count)
+ {
+ }
+
+ ~LLScriptTouchEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptTouchEndEvent : public LLScriptEvent
+{
+public:
+ LLScriptTouchEndEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_TOUCH_END), mCount(count)
+ {
+ }
+
+ ~LLScriptTouchEndEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptCollisionStartEvent : public LLScriptEvent
+{
+public:
+ LLScriptCollisionStartEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_COLLISION_START), mCount(count)
+ {
+ }
+
+ ~LLScriptCollisionStartEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptCollisionEvent : public LLScriptEvent
+{
+public:
+ LLScriptCollisionEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_COLLISION), mCount(count)
+ {
+ }
+
+ ~LLScriptCollisionEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptCollisionEndEvent : public LLScriptEvent
+{
+public:
+ LLScriptCollisionEndEvent(S32 line, S32 col, LLScriptIdentifier *count)
+ : LLScriptEvent(line, col, LSTT_COLLISION_END), mCount(count)
+ {
+ }
+
+ ~LLScriptCollisionEndEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mCount;
+};
+
+class LLScriptLandCollisionStartEvent : public LLScriptEvent
+{
+public:
+ LLScriptLandCollisionStartEvent(S32 line, S32 col, LLScriptIdentifier *pos)
+ : LLScriptEvent(line, col, LSTT_LAND_COLLISION_START), mPosition(pos)
+ {
+ }
+
+ ~LLScriptLandCollisionStartEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mPosition;
+};
+
+class LLScriptLandCollisionEvent : public LLScriptEvent
+{
+public:
+ LLScriptLandCollisionEvent(S32 line, S32 col, LLScriptIdentifier *pos)
+ : LLScriptEvent(line, col, LSTT_LAND_COLLISION), mPosition(pos)
+ {
+ }
+
+ ~LLScriptLandCollisionEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mPosition;
+};
+
+class LLScriptLandCollisionEndEvent : public LLScriptEvent
+{
+public:
+ LLScriptLandCollisionEndEvent(S32 line, S32 col, LLScriptIdentifier *pos)
+ : LLScriptEvent(line, col, LSTT_LAND_COLLISION_END), mPosition(pos)
+ {
+ }
+
+ ~LLScriptLandCollisionEndEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mPosition;
+};
+
+class LLScriptInventoryEvent : public LLScriptEvent
+{
+public:
+ LLScriptInventoryEvent(S32 line, S32 col, LLScriptIdentifier *change)
+ : LLScriptEvent(line, col, LSTT_INVENTORY), mChange(change)
+ {
+ }
+
+ ~LLScriptInventoryEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mChange;
+};
+
+class LLScriptAttachEvent : public LLScriptEvent
+{
+public:
+ LLScriptAttachEvent(S32 line, S32 col, LLScriptIdentifier *attach)
+ : LLScriptEvent(line, col, LSTT_ATTACH), mAttach(attach)
+ {
+ }
+
+ ~LLScriptAttachEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mAttach;
+};
+
+class LLScriptDataserverEvent : public LLScriptEvent
+{
+public:
+ LLScriptDataserverEvent(S32 line, S32 col, LLScriptIdentifier *id, LLScriptIdentifier *data)
+ : LLScriptEvent(line, col, LSTT_DATASERVER), mID(id), mData(data)
+ {
+ }
+
+ ~LLScriptDataserverEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mID;
+ LLScriptIdentifier *mData;
+};
+
+class LLScriptTimerEvent : public LLScriptEvent
+{
+public:
+ LLScriptTimerEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_TIMER)
+ {
+ }
+
+ ~LLScriptTimerEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+};
+
+class LLScriptMovingStartEvent : public LLScriptEvent
+{
+public:
+ LLScriptMovingStartEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_MOVING_START)
+ {
+ }
+
+ ~LLScriptMovingStartEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+};
+
+class LLScriptMovingEndEvent : public LLScriptEvent
+{
+public:
+ LLScriptMovingEndEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_MOVING_END)
+ {
+ }
+
+ ~LLScriptMovingEndEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+};
+
+class LLScriptRTPEvent : public LLScriptEvent
+{
+public:
+ LLScriptRTPEvent(S32 line, S32 col, LLScriptIdentifier *rtperm)
+ : LLScriptEvent(line, col, LSTT_RTPERMISSIONS), mRTPermissions(rtperm)
+ {
+ }
+
+ ~LLScriptRTPEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mRTPermissions;
+};
+
+class LLScriptChatEvent : public LLScriptEvent
+{
+public:
+ LLScriptChatEvent(S32 line, S32 col, LLScriptIdentifier *channel, LLScriptIdentifier *name, LLScriptIdentifier *id, LLScriptIdentifier *message)
+ : LLScriptEvent(line, col, LSTT_CHAT), mChannel(channel), mName(name), mID(id), mMessage(message)
+ {
+ }
+
+ ~LLScriptChatEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mChannel;
+ LLScriptIdentifier *mName;
+ LLScriptIdentifier *mID;
+ LLScriptIdentifier *mMessage;
+};
+
+class LLScriptObjectRezEvent : public LLScriptEvent
+{
+public:
+ LLScriptObjectRezEvent(S32 line, S32 col, LLScriptIdentifier *id)
+ : LLScriptEvent(line, col, LSTT_OBJECT_REZ), mID(id)
+ {
+ }
+
+ ~LLScriptObjectRezEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mID;
+};
+
+class LLScriptSensorEvent : public LLScriptEvent
+{
+public:
+ LLScriptSensorEvent(S32 line, S32 col, LLScriptIdentifier *number)
+ : LLScriptEvent(line, col, LSTT_SENSOR), mNumber(number)
+ {
+ }
+
+ ~LLScriptSensorEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mNumber;
+};
+
+class LLScriptControlEvent : public LLScriptEvent
+{
+public:
+ LLScriptControlEvent(S32 line, S32 col, LLScriptIdentifier *name, LLScriptIdentifier *levels, LLScriptIdentifier *edges)
+ : LLScriptEvent(line, col, LSTT_CONTROL), mName(name), mLevels(levels), mEdges(edges)
+ {
+ }
+
+ ~LLScriptControlEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mName;
+ LLScriptIdentifier *mLevels;
+ LLScriptIdentifier *mEdges;
+};
+
+class LLScriptLinkMessageEvent : public LLScriptEvent
+{
+public:
+ LLScriptLinkMessageEvent(S32 line, S32 col, LLScriptIdentifier *sender, LLScriptIdentifier *num, LLScriptIdentifier *str, LLScriptIdentifier *id)
+ : LLScriptEvent(line, col, LSTT_LINK_MESSAGE), mSender(sender), mNum(num), mStr(str), mID(id)
+ {
+ }
+
+ ~LLScriptLinkMessageEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mSender;
+ LLScriptIdentifier *mNum;
+ LLScriptIdentifier *mStr;
+ LLScriptIdentifier *mID;
+};
+
+class LLScriptRemoteEvent : public LLScriptEvent
+{
+public:
+ LLScriptRemoteEvent(S32 line, S32 col, LLScriptIdentifier *type, LLScriptIdentifier *channel, LLScriptIdentifier *message_id, LLScriptIdentifier *sender, LLScriptIdentifier *int_val, LLScriptIdentifier *str_val)
+ : LLScriptEvent(line, col, LSTT_REMOTE_DATA), mType(type), mChannel(channel), mMessageID(message_id), mSender(sender), mIntVal(int_val), mStrVal(str_val)
+ {
+ }
+
+ ~LLScriptRemoteEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mType;
+ LLScriptIdentifier *mChannel;
+ LLScriptIdentifier *mMessageID;
+ LLScriptIdentifier *mSender;
+ LLScriptIdentifier *mIntVal;
+ LLScriptIdentifier *mStrVal;
+};
+
+class LLScriptHTTPResponseEvent : public LLScriptEvent
+{
+public:
+ LLScriptHTTPResponseEvent(S32 line, S32 col,
+ LLScriptIdentifier *reqeust_id,
+ LLScriptIdentifier *status,
+ LLScriptIdentifier *metadata,
+ LLScriptIdentifier *body)
+ : LLScriptEvent(line, col, LSTT_HTTP_RESPONSE),
+ mRequestId(reqeust_id), mStatus(status), mMetadata(metadata), mBody(body)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass,
+ LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope,
+ LSCRIPTType &type, LSCRIPTType basetype, U64 &count,
+ LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap,
+ S32 stacksize, LLScriptScopeEntry *entry,
+ S32 entrycount, LLScriptLibData **ldata);
+
+ S32 getSize();
+
+ LLScriptIdentifier *mRequestId;
+ LLScriptIdentifier *mStatus;
+ LLScriptIdentifier *mMetadata;
+ LLScriptIdentifier *mBody;
+};
+
+class LLScriptRezEvent : public LLScriptEvent
+{
+public:
+ LLScriptRezEvent(S32 line, S32 col, LLScriptIdentifier *start_param)
+ : LLScriptEvent(line, col, LSTT_REZ), mStartParam(start_param)
+ {
+ }
+ ~LLScriptRezEvent() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mStartParam;
+};
+
+class LLScriptNoSensorEvent : public LLScriptEvent
+{
+public:
+ LLScriptNoSensorEvent(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_NO_SENSOR)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptNoSensorEvent() {}
+};
+
+class LLScriptAtTarget : public LLScriptEvent
+{
+public:
+ LLScriptAtTarget(S32 line, S32 col, LLScriptIdentifier *tnumber, LLScriptIdentifier *tpos, LLScriptIdentifier *ourpos)
+ : LLScriptEvent(line, col, LSTT_AT_TARGET), mTargetNumber(tnumber), mTargetPosition(tpos), mOurPosition(ourpos)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptAtTarget() {}
+
+ LLScriptIdentifier *mTargetNumber;
+ LLScriptIdentifier *mTargetPosition;
+ LLScriptIdentifier *mOurPosition;
+};
+
+class LLScriptNotAtTarget : public LLScriptEvent
+{
+public:
+ LLScriptNotAtTarget(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_NOT_AT_TARGET)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptNotAtTarget() {}
+};
+
+class LLScriptAtRotTarget : public LLScriptEvent
+{
+public:
+ LLScriptAtRotTarget(S32 line, S32 col, LLScriptIdentifier *tnumber, LLScriptIdentifier *trot, LLScriptIdentifier *ourrot)
+ : LLScriptEvent(line, col, LSTT_AT_ROT_TARGET), mTargetNumber(tnumber), mTargetRotation(trot), mOurRotation(ourrot)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptAtRotTarget() {}
+
+ LLScriptIdentifier *mTargetNumber;
+ LLScriptIdentifier *mTargetRotation;
+ LLScriptIdentifier *mOurRotation;
+};
+
+class LLScriptNotAtRotTarget : public LLScriptEvent
+{
+public:
+ LLScriptNotAtRotTarget(S32 line, S32 col)
+ : LLScriptEvent(line, col, LSTT_NOT_AT_ROT_TARGET)
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ ~LLScriptNotAtRotTarget() {}
+};
+
+class LLScriptMoneyEvent : public LLScriptEvent
+{
+public:
+ LLScriptMoneyEvent(S32 line, S32 col, LLScriptIdentifier *name, LLScriptIdentifier *amount)
+ : LLScriptEvent(line, col, LSTT_MONEY), mName(name), mAmount(amount)
+ {
+ }
+
+ ~LLScriptMoneyEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mName;
+ LLScriptIdentifier *mAmount;
+};
+
+class LLScriptEmailEvent : public LLScriptEvent
+{
+public:
+ LLScriptEmailEvent(S32 line, S32 col, LLScriptIdentifier *time, LLScriptIdentifier *address, LLScriptIdentifier *subject, LLScriptIdentifier *body, LLScriptIdentifier *number)
+ : LLScriptEvent(line, col, LSTT_EMAIL), mTime(time), mAddress(address), mSubject(subject), mBody(body), mNumber(number)
+ {
+ }
+
+ ~LLScriptEmailEvent()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mTime;
+ LLScriptIdentifier *mAddress;
+ LLScriptIdentifier *mSubject;
+ LLScriptIdentifier *mBody;
+ LLScriptIdentifier *mNumber;
+};
+
+
+class LLScriptExpression : public LLScriptFilePosition
+{
+public:
+ LLScriptExpression(S32 line, S32 col, LSCRIPTExpressionType type)
+ : LLScriptFilePosition(line, col), mType(type), mNextp(NULL), mLeftType(LST_NULL), mRightType(LST_NULL), mReturnType(LST_NULL)
+ {
+ }
+
+ void addExpression(LLScriptExpression *expression);
+
+ virtual ~LLScriptExpression()
+ {
+ // don't delete next pointer because we're going to store allocation lists and delete from those
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTExpressionType mType;
+ LLScriptExpression *mNextp;
+ LSCRIPTType mLeftType, mRightType, mReturnType;
+
+};
+
+class LLScriptForExpressionList : public LLScriptExpression
+{
+public:
+ LLScriptForExpressionList(S32 line, S32 col, LLScriptExpression *first, LLScriptExpression *second)
+ : LLScriptExpression(line, col, LET_FOR_EXPRESSION_LIST), mFirstp(first), mSecondp(second)
+ {
+ }
+
+ ~LLScriptForExpressionList()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mFirstp;
+ LLScriptExpression *mSecondp;
+};
+
+class LLScriptFuncExpressionList : public LLScriptExpression
+{
+public:
+ LLScriptFuncExpressionList(S32 line, S32 col, LLScriptExpression *first, LLScriptExpression *second)
+ : LLScriptExpression(line, col, LET_FUNC_EXPRESSION_LIST), mFirstp(first), mSecondp(second)
+ {
+ }
+
+ ~LLScriptFuncExpressionList()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mFirstp;
+ LLScriptExpression *mSecondp;
+};
+
+class LLScriptListExpressionList : public LLScriptExpression
+{
+public:
+ LLScriptListExpressionList(S32 line, S32 col, LLScriptExpression *first, LLScriptExpression *second)
+ : LLScriptExpression(line, col, LET_LIST_EXPRESSION_LIST), mFirstp(first), mSecondp(second)
+ {
+ }
+
+ ~LLScriptListExpressionList()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mFirstp;
+ LLScriptExpression *mSecondp;
+};
+
+class LLScriptLValue : public LLScriptExpression
+{
+public:
+ LLScriptLValue(S32 line, S32 col, LLScriptIdentifier *identifier, LLScriptIdentifier *accessor)
+ : LLScriptExpression(line, col, LET_LVALUE), mOffset(0), mIdentifier(identifier), mAccessor(accessor)
+ {
+ }
+
+ ~LLScriptLValue()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ S32 mOffset;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptIdentifier *mAccessor;
+};
+
+class LLScriptAssignment : public LLScriptExpression
+{
+public:
+ LLScriptAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_ASSIGNMENT), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptAddAssignment : public LLScriptExpression
+{
+public:
+ LLScriptAddAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_ADD_ASSIGN), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptAddAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptSubAssignment : public LLScriptExpression
+{
+public:
+ LLScriptSubAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_SUB_ASSIGN), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptSubAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptMulAssignment : public LLScriptExpression
+{
+public:
+ LLScriptMulAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_MUL_ASSIGN), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptMulAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptDivAssignment : public LLScriptExpression
+{
+public:
+ LLScriptDivAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_DIV_ASSIGN), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptDivAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptModAssignment : public LLScriptExpression
+{
+public:
+ LLScriptModAssignment(S32 line, S32 col, LLScriptExpression *lvalue, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_MOD_ASSIGN), mLValue(lvalue), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptModAssignment()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLValue;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptEquality : public LLScriptExpression
+{
+public:
+ LLScriptEquality(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_EQUALITY), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptEquality()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptNotEquals : public LLScriptExpression
+{
+public:
+ LLScriptNotEquals(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_NOT_EQUALS), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptNotEquals()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptLessEquals : public LLScriptExpression
+{
+public:
+ LLScriptLessEquals(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_LESS_EQUALS), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptLessEquals()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptGreaterEquals : public LLScriptExpression
+{
+public:
+ LLScriptGreaterEquals(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_GREATER_EQUALS), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptGreaterEquals()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptLessThan : public LLScriptExpression
+{
+public:
+ LLScriptLessThan(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_LESS_THAN), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptLessThan()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptGreaterThan : public LLScriptExpression
+{
+public:
+ LLScriptGreaterThan(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_GREATER_THAN), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptGreaterThan()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptPlus : public LLScriptExpression
+{
+public:
+ LLScriptPlus(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_PLUS), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptPlus()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptMinus : public LLScriptExpression
+{
+public:
+ LLScriptMinus(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_MINUS), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptMinus()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptTimes : public LLScriptExpression
+{
+public:
+ LLScriptTimes(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_TIMES), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptTimes()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptDivide : public LLScriptExpression
+{
+public:
+ LLScriptDivide(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_DIVIDE), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptDivide()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptMod : public LLScriptExpression
+{
+public:
+ LLScriptMod(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_MOD), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptMod()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptBitAnd : public LLScriptExpression
+{
+public:
+ LLScriptBitAnd(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_BIT_AND), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptBitAnd()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptBitOr : public LLScriptExpression
+{
+public:
+ LLScriptBitOr(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_BIT_OR), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptBitOr()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptBitXor : public LLScriptExpression
+{
+public:
+ LLScriptBitXor(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_BIT_XOR), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptBitXor()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptBooleanAnd : public LLScriptExpression
+{
+public:
+ LLScriptBooleanAnd(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_BOOLEAN_AND), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptBooleanAnd()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptBooleanOr : public LLScriptExpression
+{
+public:
+ LLScriptBooleanOr(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_BOOLEAN_OR), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptBooleanOr()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptShiftLeft : public LLScriptExpression
+{
+public:
+ LLScriptShiftLeft(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_SHIFT_LEFT), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptShiftLeft()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptShiftRight : public LLScriptExpression
+{
+public:
+ LLScriptShiftRight(S32 line, S32 col, LLScriptExpression *leftside, LLScriptExpression *rightside)
+ : LLScriptExpression(line, col, LET_SHIFT_RIGHT), mLeftSide(leftside), mRightSide(rightside)
+ {
+ }
+
+ ~LLScriptShiftRight()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mLeftSide;
+ LLScriptExpression *mRightSide;
+};
+
+class LLScriptParenthesis : public LLScriptExpression
+{
+public:
+ LLScriptParenthesis(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_PARENTHESIS), mExpression(expression)
+ {
+ }
+
+ ~LLScriptParenthesis()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptUnaryMinus : public LLScriptExpression
+{
+public:
+ LLScriptUnaryMinus(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_UNARY_MINUS), mExpression(expression)
+ {
+ }
+
+ ~LLScriptUnaryMinus()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptBooleanNot : public LLScriptExpression
+{
+public:
+ LLScriptBooleanNot(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_BOOLEAN_NOT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptBooleanNot()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptBitNot : public LLScriptExpression
+{
+public:
+ LLScriptBitNot(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_BIT_NOT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptBitNot()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptPreIncrement : public LLScriptExpression
+{
+public:
+ LLScriptPreIncrement(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_PRE_INCREMENT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptPreIncrement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptPreDecrement : public LLScriptExpression
+{
+public:
+ LLScriptPreDecrement(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_PRE_DECREMENT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptPreDecrement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptTypeCast : public LLScriptExpression
+{
+public:
+ LLScriptTypeCast(S32 line, S32 col, LLScriptType *type, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_CAST), mType(type), mExpression(expression)
+ {
+ }
+
+ ~LLScriptTypeCast()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptType *mType;
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptVectorInitializer : public LLScriptExpression
+{
+public:
+ LLScriptVectorInitializer(S32 line, S32 col, LLScriptExpression *expression1,
+ LLScriptExpression *expression2,
+ LLScriptExpression *expression3)
+ : LLScriptExpression(line, col, LET_VECTOR_INITIALIZER),
+ mExpression1(expression1),
+ mExpression2(expression2),
+ mExpression3(expression3)
+ {
+ }
+
+ ~LLScriptVectorInitializer()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression1;
+ LLScriptExpression *mExpression2;
+ LLScriptExpression *mExpression3;
+};
+
+class LLScriptQuaternionInitializer : public LLScriptExpression
+{
+public:
+ LLScriptQuaternionInitializer(S32 line, S32 col, LLScriptExpression *expression1,
+ LLScriptExpression *expression2,
+ LLScriptExpression *expression3,
+ LLScriptExpression *expression4)
+ : LLScriptExpression(line, col, LET_VECTOR_INITIALIZER),
+ mExpression1(expression1),
+ mExpression2(expression2),
+ mExpression3(expression3),
+ mExpression4(expression4)
+ {
+ }
+
+ ~LLScriptQuaternionInitializer()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression1;
+ LLScriptExpression *mExpression2;
+ LLScriptExpression *mExpression3;
+ LLScriptExpression *mExpression4;
+};
+
+class LLScriptListInitializer : public LLScriptExpression
+{
+public:
+ LLScriptListInitializer(S32 line, S32 col, LLScriptExpression *expressionlist)
+ : LLScriptExpression(line, col, LET_LIST_INITIALIZER), mExpressionList(expressionlist)
+ {
+ }
+
+ ~LLScriptListInitializer()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpressionList;
+};
+
+class LLScriptPostIncrement : public LLScriptExpression
+{
+public:
+ LLScriptPostIncrement(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_POST_INCREMENT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptPostIncrement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptPostDecrement : public LLScriptExpression
+{
+public:
+ LLScriptPostDecrement(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_POST_DECREMENT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptPostDecrement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptFunctionCall : public LLScriptExpression
+{
+public:
+ LLScriptFunctionCall(S32 line, S32 col, LLScriptIdentifier *identifier, LLScriptExpression *expressionlist)
+ : LLScriptExpression(line, col, LET_FUNCTION_CALL), mIdentifier(identifier), mExpressionList(expressionlist)
+ {
+ }
+
+ ~LLScriptFunctionCall()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mIdentifier;
+ LLScriptExpression *mExpressionList;
+};
+
+class LLScriptPrint : public LLScriptExpression
+{
+public:
+ LLScriptPrint(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptExpression(line, col, LET_PRINT), mExpression(expression)
+ {
+ }
+
+ ~LLScriptPrint()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptConstantExpression : public LLScriptExpression
+{
+public:
+ LLScriptConstantExpression(S32 line, S32 col, LLScriptConstant *constant)
+ : LLScriptExpression(line, col, LET_CONSTANT), mConstant(constant)
+ {
+ }
+
+ ~LLScriptConstantExpression()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptConstant *mConstant;
+};
+
+// statement
+typedef enum e_lscript_statement_types
+{
+ LSSMT_NULL,
+ LSSMT_SEQUENCE,
+ LSSMT_NOOP,
+ LSSMT_STATE_CHANGE,
+ LSSMT_JUMP,
+ LSSMT_LABEL,
+ LSSMT_RETURN,
+ LSSMT_EXPRESSION,
+ LSSMT_IF,
+ LSSMT_IF_ELSE,
+ LSSMT_FOR,
+ LSSMT_DO_WHILE,
+ LSSMT_WHILE,
+ LSSMT_DECLARATION,
+ LSSMT_COMPOUND_STATEMENT,
+ LSSMT_EOF
+} LSCRIPTStatementType;
+
+class LLScriptStatement : public LLScriptFilePosition
+{
+public:
+ LLScriptStatement(S32 line, S32 col, LSCRIPTStatementType type)
+ : LLScriptFilePosition(line, col), mType(type), mNextp(NULL), mStatementScope(NULL), mAllowDeclarations(TRUE)
+ {
+ }
+
+ virtual ~LLScriptStatement()
+ {
+ delete mStatementScope;
+ }
+
+ void addStatement(LLScriptStatement *event);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTStatementType mType;
+ LLScriptStatement *mNextp;
+ LLScriptScope *mStatementScope;
+ BOOL mAllowDeclarations;
+};
+
+class LLScriptStatementSequence : public LLScriptStatement
+{
+public:
+ LLScriptStatementSequence(S32 line, S32 col, LLScriptStatement *first, LLScriptStatement *second)
+ : LLScriptStatement(line, col, LSSMT_SEQUENCE), mFirstp(first), mSecondp(second)
+ {
+ }
+
+ ~LLScriptStatementSequence()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptStatement *mFirstp;
+ LLScriptStatement *mSecondp;
+};
+
+class LLScriptNOOP : public LLScriptStatement
+{
+public:
+ LLScriptNOOP(S32 line, S32 col)
+ : LLScriptStatement(line, col, LSSMT_NOOP)
+ {
+ }
+
+ ~LLScriptNOOP() {}
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+};
+
+class LLScriptStateChange : public LLScriptStatement
+{
+public:
+ LLScriptStateChange(S32 line, S32 col, LLScriptIdentifier *identifier)
+ : LLScriptStatement(line, col, LSSMT_STATE_CHANGE), mIdentifier(identifier)
+ {
+ }
+
+ ~LLScriptStateChange()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mIdentifier;
+};
+
+class LLScriptJump : public LLScriptStatement
+{
+public:
+ LLScriptJump(S32 line, S32 col, LLScriptIdentifier *identifier)
+ : LLScriptStatement(line, col, LSSMT_JUMP), mIdentifier(identifier)
+ {
+ }
+
+ ~LLScriptJump()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mIdentifier;
+};
+
+class LLScriptLabel : public LLScriptStatement
+{
+public:
+ LLScriptLabel(S32 line, S32 col, LLScriptIdentifier *identifier)
+ : LLScriptStatement(line, col, LSSMT_LABEL), mIdentifier(identifier)
+ {
+ }
+
+ ~LLScriptLabel()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptIdentifier *mIdentifier;
+};
+
+class LLScriptReturn : public LLScriptStatement
+{
+public:
+ LLScriptReturn(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptStatement(line, col, LSSMT_RETURN), mExpression(expression), mType(LST_NULL)
+ {
+ }
+
+ ~LLScriptReturn()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+ LSCRIPTType mType;
+};
+
+class LLScriptExpressionStatement : public LLScriptStatement
+{
+public:
+ LLScriptExpressionStatement(S32 line, S32 col, LLScriptExpression *expression)
+ : LLScriptStatement(line, col, LSSMT_EXPRESSION), mExpression(expression)
+ {
+ }
+
+ ~LLScriptExpressionStatement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptIf : public LLScriptStatement
+{
+public:
+ LLScriptIf(S32 line, S32 col, LLScriptExpression *expression, LLScriptStatement *statement)
+ : LLScriptStatement(line, col, LSSMT_IF), mType(LST_NULL), mExpression(expression), mStatement(statement)
+ {
+ }
+
+ ~LLScriptIf()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTType mType;
+ LLScriptExpression *mExpression;
+ LLScriptStatement *mStatement;
+};
+
+class LLScriptIfElse : public LLScriptStatement
+{
+public:
+ LLScriptIfElse(S32 line, S32 col, LLScriptExpression *expression, LLScriptStatement *statement1, LLScriptStatement *statement2)
+ : LLScriptStatement(line, col, LSSMT_IF_ELSE), mExpression(expression), mStatement1(statement1), mStatement2(statement2), mType(LST_NULL)
+ {
+ }
+
+ ~LLScriptIfElse()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+ LLScriptStatement *mStatement1;
+ LLScriptStatement *mStatement2;
+ LSCRIPTType mType;
+};
+
+class LLScriptFor : public LLScriptStatement
+{
+public:
+ LLScriptFor(S32 line, S32 col, LLScriptExpression *sequence, LLScriptExpression *expression, LLScriptExpression *expressionlist, LLScriptStatement *statement)
+ : LLScriptStatement(line, col, LSSMT_FOR), mSequence(sequence), mExpression(expression), mExpressionList(expressionlist), mStatement(statement), mType(LST_NULL)
+ {
+ }
+
+ ~LLScriptFor()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mSequence;
+ LLScriptExpression *mExpression;
+ LLScriptExpression *mExpressionList;
+ LLScriptStatement *mStatement;
+ LSCRIPTType mType;
+};
+
+class LLScriptDoWhile : public LLScriptStatement
+{
+public:
+ LLScriptDoWhile(S32 line, S32 col, LLScriptStatement *statement, LLScriptExpression *expression)
+ : LLScriptStatement(line, col, LSSMT_DO_WHILE), mStatement(statement), mExpression(expression), mType(LST_NULL)
+ {
+ }
+
+ ~LLScriptDoWhile()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptStatement *mStatement;
+ LLScriptExpression *mExpression;
+ LSCRIPTType mType;
+};
+
+class LLScriptWhile : public LLScriptStatement
+{
+public:
+ LLScriptWhile(S32 line, S32 col, LLScriptExpression *expression, LLScriptStatement *statement)
+ : LLScriptStatement(line, col, LSSMT_WHILE), mExpression(expression), mStatement(statement), mType(LST_NULL)
+ {
+ }
+
+ ~LLScriptWhile()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptExpression *mExpression;
+ LLScriptStatement *mStatement;
+ LSCRIPTType mType;
+};
+
+// local variables
+class LLScriptDeclaration : public LLScriptStatement
+{
+public:
+ LLScriptDeclaration(S32 line, S32 col, LLScriptType *type, LLScriptIdentifier *identifier, LLScriptExpression *expression)
+ : LLScriptStatement(line, col, LSSMT_DECLARATION), mType(type), mIdentifier(identifier), mExpression(expression)
+ {
+ }
+
+ ~LLScriptDeclaration()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptType *mType;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptExpression *mExpression;
+};
+
+class LLScriptCompoundStatement : public LLScriptStatement
+{
+public:
+ LLScriptCompoundStatement(S32 line, S32 col, LLScriptStatement *statement)
+ : LLScriptStatement(line, col, LSSMT_COMPOUND_STATEMENT), mStatement(statement)
+ {
+ }
+
+ ~LLScriptCompoundStatement()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptStatement *mStatement;
+};
+
+class LLScriptEventHandler : public LLScriptFilePosition
+{
+public:
+ LLScriptEventHandler(S32 line, S32 col, LLScriptEvent *event, LLScriptStatement *statement)
+ : LLScriptFilePosition(line, col), mEventp(event), mStatement(statement), mNextp(NULL), mEventScope(NULL), mbNeedTrailingReturn(FALSE), mScopeEntry(NULL), mStackSpace(0)
+ {
+ }
+
+ ~LLScriptEventHandler()
+ {
+ delete mEventScope;
+ delete mScopeEntry;
+ }
+
+ void addEvent(LLScriptEventHandler *event);
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptEvent *mEventp;
+ LLScriptStatement *mStatement;
+ LLScriptEventHandler *mNextp;
+ LLScriptScope *mEventScope;
+ BOOL mbNeedTrailingReturn;
+ LLScriptScopeEntry *mScopeEntry;
+
+ S32 mStackSpace;
+
+};
+
+
+// global functions
+class LLScriptFunctionDec : public LLScriptFilePosition
+{
+public:
+ LLScriptFunctionDec(S32 line, S32 col, LLScriptType *type, LLScriptIdentifier *identifier)
+ : LLScriptFilePosition(line, col), mType(type), mIdentifier(identifier), mNextp(NULL)
+ {
+ }
+
+ ~LLScriptFunctionDec()
+ {
+ }
+
+ void addFunctionParameter(LLScriptFunctionDec *dec);
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptType *mType;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptFunctionDec *mNextp;
+};
+
+class LLScriptGlobalFunctions : public LLScriptFilePosition
+{
+public:
+ LLScriptGlobalFunctions(S32 line, S32 col, LLScriptType *type,
+ LLScriptIdentifier *identifier,
+ LLScriptFunctionDec *parameters,
+ LLScriptStatement *statements)
+ : LLScriptFilePosition(line, col), mType(type), mIdentifier(identifier), mParameters(parameters), mStatements(statements), mNextp(NULL), mFunctionScope(NULL), mbNeedTrailingReturn(FALSE)
+ {
+ }
+
+ void addGlobalFunction(LLScriptGlobalFunctions *global);
+
+ ~LLScriptGlobalFunctions()
+ {
+ delete mFunctionScope;
+ }
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LLScriptType *mType;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptFunctionDec *mParameters;
+ LLScriptStatement *mStatements;
+ LLScriptGlobalFunctions *mNextp;
+ LLScriptScope *mFunctionScope;
+ BOOL mbNeedTrailingReturn;
+
+};
+
+typedef enum e_lscript_state_type
+{
+ LSSTYPE_NULL,
+ LSSTYPE_DEFAULT,
+ LSSTYPE_USER,
+ LSSTYPE_EOF
+} LSCRIPTStateType;
+
+// info on state
+class LLScriptState : public LLScriptFilePosition
+{
+public:
+ LLScriptState(S32 line, S32 col, LSCRIPTStateType type, LLScriptIdentifier *identifier, LLScriptEventHandler *event)
+ : LLScriptFilePosition(line, col), mType(type), mIdentifier(identifier), mEvent(event), mNextp(NULL)
+ {
+ }
+
+ void addState(LLScriptState *state);
+
+ ~LLScriptState()
+ {
+ }
+
+ void gonext(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ LSCRIPTStateType mType;
+ LLScriptIdentifier *mIdentifier;
+ LLScriptEventHandler *mEvent;
+ LLScriptState *mNextp;
+
+};
+
+class LLScritpGlobalStorage : public LLScriptFilePosition
+{
+public:
+
+ LLScritpGlobalStorage(LLScriptGlobalVariable *var)
+ : LLScriptFilePosition(0, 0), mGlobal(var), mbGlobalFunction(FALSE), mNextp(NULL)
+ {
+ }
+
+ LLScritpGlobalStorage(LLScriptGlobalFunctions *func)
+ : LLScriptFilePosition(0, 0), mGlobal(func), mbGlobalFunction(TRUE), mNextp(NULL)
+ {
+ }
+
+ ~LLScritpGlobalStorage()
+ {
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata)
+ {
+ }
+
+ S32 getSize()
+ {
+ return 0;
+ }
+
+ void addGlobal(LLScritpGlobalStorage *global)
+ {
+ if (mNextp)
+ {
+ global->mNextp = mNextp;
+ }
+ mNextp = global;
+ }
+
+ LLScriptFilePosition *mGlobal;
+ BOOL mbGlobalFunction;
+ LLScritpGlobalStorage *mNextp;
+};
+
+// top level container for entire script
+class LLScriptScript : public LLScriptFilePosition
+{
+public:
+ LLScriptScript(LLScritpGlobalStorage *globals,
+ LLScriptState *states);
+
+ ~LLScriptScript()
+ {
+ delete mGlobalScope;
+ }
+
+ void recurse(FILE *fp, S32 tabs, S32 tabsize, LSCRIPTCompilePass pass, LSCRIPTPruneType ptype, BOOL &prunearg, LLScriptScope *scope, LSCRIPTType &type, LSCRIPTType basetype, U64 &count, LLScriptByteCodeChunk *chunk, LLScriptByteCodeChunk *heap, S32 stacksize, LLScriptScopeEntry *entry, S32 entrycount, LLScriptLibData **ldata);
+ S32 getSize();
+
+ void setBytecodeDest(const char* dst_filename);
+
+ LLScriptState *mStates;
+ LLScriptScope *mGlobalScope;
+ LLScriptGlobalVariable *mGlobals;
+ LLScriptGlobalFunctions *mGlobalFunctions;
+ BOOL mGodLike;
+
+private:
+ char mBytecodeDest[MAX_STRING];
+};
+
+class LLScriptAllocationManager
+{
+public:
+ LLScriptAllocationManager() {}
+ ~LLScriptAllocationManager()
+ {
+ mAllocationList.deleteAllData();
+ }
+
+ void addAllocation(LLScriptFilePosition *ptr)
+ {
+ mAllocationList.addData(ptr);
+ }
+
+ void deleteAllocations()
+ {
+ mAllocationList.deleteAllData();
+ }
+
+ LLLinkedList<LLScriptFilePosition> mAllocationList;
+};
+
+extern LLScriptAllocationManager *gAllocationManager;
+extern LLScriptScript *gScriptp;
+
+#endif
diff --git a/indra/lscript/lscript_compile/lscript_typecheck.cpp b/indra/lscript/lscript_compile/lscript_typecheck.cpp
new file mode 100644
index 0000000000..b706ff6dec
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_typecheck.cpp
@@ -0,0 +1,562 @@
+/**
+ * @file lscript_typecheck.cpp
+ * @brief typechecks script
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_tree.h"
+
+/*
+ LScript automatic type casting
+
+ LST_INTEGER -> LST_INTEGER
+
+ LST_FLOATINGPOINT -> LST_FLOATINGPOINT
+ LST_INTEGER -> LST_FLOATINGPOINT
+
+ LST_FLOATINGPOINT -> LST_STRING
+ LST_INTEGER -> LST_STRING
+ LST_STRING -> LST_STRING
+ LST_VECTOR -> LST_STRING
+ LST_QUATERNION -> LST_STRING
+ LST_LIST -> LST_STRING
+
+ LST_VECTOR -> LST_VECTOR
+
+ LST_QUATERNION -> LST_QUATERNION
+
+ LST_FLOATINGPOINT -> LST_LIST
+ LST_INTEGER -> LST_LIST
+ LST_STRING -> LST_LIST
+ LST_VECTOR -> LST_LIST
+ LST_QUATERNION -> LST_LIST
+ LST_LIST -> LST_LIST
+*/
+
+LSCRIPTType implicit_casts(LSCRIPTType left_side, LSCRIPTType right_side)
+{
+ switch(left_side)
+ {
+ // shouldn't be doing an operation on void types
+ case LST_NULL:
+ return LST_NULL;
+ // shouldn't be doing an operation on undefined types
+ case LST_UNDEFINED:
+ return LST_UNDEFINED;
+ // only integers can become integers
+ case LST_INTEGER:
+ switch(right_side)
+ {
+ case LST_INTEGER:
+ return LST_INTEGER;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only integers and floats can become floats
+ case LST_FLOATINGPOINT:
+ switch(right_side)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ return LST_FLOATINGPOINT;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only strings and keys can become strings
+ case LST_STRING:
+ switch(right_side)
+ {
+ case LST_STRING:
+ case LST_KEY:
+ return LST_STRING;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only strings and keys can become keys
+ case LST_KEY:
+ switch(right_side)
+ {
+ case LST_STRING:
+ case LST_KEY:
+ return LST_KEY;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only vectors can become vectors
+ case LST_VECTOR:
+ switch(right_side)
+ {
+ case LST_VECTOR:
+ return LST_VECTOR;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only quaternions can become quaternions
+ case LST_QUATERNION:
+ switch(right_side)
+ {
+ case LST_QUATERNION:
+ return LST_QUATERNION;
+ default:
+ return LST_UNDEFINED;
+ }
+ // only lists can become lists
+ case LST_LIST:
+ switch(right_side)
+ {
+ case LST_LIST:
+ return LST_LIST;
+ default:
+ return LST_UNDEFINED;
+ }
+ default:
+ return LST_UNDEFINED;
+ }
+}
+
+LSCRIPTType promote(LSCRIPTType left_side, LSCRIPTType right_side)
+{
+ LSCRIPTType type;
+ type = implicit_casts(left_side, right_side);
+ if (type != LST_UNDEFINED)
+ {
+ return type;
+ }
+ type = implicit_casts(right_side, left_side);
+ if (type != LST_UNDEFINED)
+ {
+ return type;
+ }
+ return LST_UNDEFINED;
+}
+
+BOOL legal_assignment(LSCRIPTType left_side, LSCRIPTType right_side)
+{
+ // this is to prevent cascading errors
+ if ( (left_side == LST_UNDEFINED)
+ ||(right_side == LST_UNDEFINED))
+ {
+ return TRUE;
+ }
+
+ if (implicit_casts(left_side, right_side) != LST_UNDEFINED)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL legal_casts(LSCRIPTType cast, LSCRIPTType base)
+{
+ switch(base)
+ {
+ // shouldn't be doing an operation on void types
+ case LST_NULL:
+ return FALSE;
+ // shouldn't be doing an operation on undefined types
+ case LST_UNDEFINED:
+ return FALSE;
+ case LST_INTEGER:
+ switch(cast)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ case LST_STRING:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ switch(cast)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ case LST_STRING:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ case LST_STRING:
+ switch(cast)
+ {
+ case LST_INTEGER:
+ case LST_FLOATINGPOINT:
+ case LST_STRING:
+ case LST_KEY:
+ case LST_VECTOR:
+ case LST_QUATERNION:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ case LST_KEY:
+ switch(cast)
+ {
+ case LST_STRING:
+ case LST_KEY:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ case LST_VECTOR:
+ switch(cast)
+ {
+ case LST_VECTOR:
+ case LST_STRING:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ case LST_QUATERNION:
+ switch(cast)
+ {
+ case LST_QUATERNION:
+ case LST_STRING:
+ case LST_LIST:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ // lists can only be cast to lists and strings
+ case LST_LIST:
+ switch(cast)
+ {
+ case LST_LIST:
+ case LST_STRING:
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+}
+
+LSCRIPTType gSupportedExpressionArray[LET_EOF][LST_EOF][LST_EOF];
+
+void init_supported_expressions(void)
+{
+ S32 i, j, k;
+ // zero out, then set the ones that matter
+ for (i = 0; i < LET_EOF; i++)
+ {
+ for (j = 0; j < LST_EOF; j++)
+ {
+ for (k = 0; k < LST_EOF; k++)
+ {
+ gSupportedExpressionArray[i][j][k] = LST_NULL;
+ }
+ }
+ }
+
+ // LET_ASSIGNMENT
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_STRING][LST_STRING] = LST_STRING;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_KEY][LST_KEY] = LST_KEY;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_INTEGER] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_FLOATINGPOINT] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_STRING] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_KEY] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_VECTOR] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_QUATERNION] = LST_LIST;
+ gSupportedExpressionArray[LET_ASSIGNMENT][LST_LIST][LST_LIST] = LST_LIST;
+
+ // LET_ADD_ASSIGN
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_STRING][LST_STRING] = LST_STRING;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_INTEGER] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_FLOATINGPOINT] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_STRING] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_KEY] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_VECTOR] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_QUATERNION] = LST_LIST;
+ gSupportedExpressionArray[LET_ADD_ASSIGN][LST_LIST][LST_LIST] = LST_LIST;
+
+ // LET_SUB_ASSIGN
+ gSupportedExpressionArray[LET_SUB_ASSIGN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_SUB_ASSIGN][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_SUB_ASSIGN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_SUB_ASSIGN][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_SUB_ASSIGN][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_MUL_ASSIGN
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_VECTOR][LST_INTEGER] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_INTEGER][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_VECTOR][LST_FLOATINGPOINT] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_FLOATINGPOINT][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_VECTOR][LST_VECTOR] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_VECTOR][LST_QUATERNION] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MUL_ASSIGN][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_DIV_ASSIGN
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_VECTOR][LST_INTEGER] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_VECTOR][LST_FLOATINGPOINT] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_VECTOR][LST_QUATERNION] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIV_ASSIGN][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_MOD_ASSIGN
+ gSupportedExpressionArray[LET_MOD_ASSIGN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_MOD_ASSIGN][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+
+ // LET_EQUALITY
+ gSupportedExpressionArray[LET_EQUALITY][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_STRING][LST_STRING] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_STRING][LST_KEY] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_KEY][LST_STRING] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_KEY][LST_KEY] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_VECTOR][LST_VECTOR] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_QUATERNION][LST_QUATERNION] = LST_INTEGER;
+ gSupportedExpressionArray[LET_EQUALITY][LST_LIST][LST_LIST] = LST_INTEGER;
+
+ // LET_NOT_EQUALS
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_STRING][LST_STRING] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_STRING][LST_KEY] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_KEY][LST_STRING] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_KEY][LST_KEY] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_VECTOR][LST_VECTOR] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_QUATERNION][LST_QUATERNION] = LST_INTEGER;
+ gSupportedExpressionArray[LET_NOT_EQUALS][LST_LIST][LST_LIST] = LST_INTEGER;
+
+ // LET_LESS_EQUALS
+ gSupportedExpressionArray[LET_LESS_EQUALS][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_EQUALS][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_EQUALS][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_EQUALS][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+
+ // LET_GREATER_EQUALS
+ gSupportedExpressionArray[LET_GREATER_EQUALS][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_EQUALS][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_EQUALS][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_EQUALS][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+
+ // LET_LESS_THAN
+ gSupportedExpressionArray[LET_LESS_THAN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_THAN][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_THAN][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_LESS_THAN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+
+ // LET_GREATER_THAN
+ gSupportedExpressionArray[LET_GREATER_THAN][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_THAN][LST_INTEGER][LST_FLOATINGPOINT] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_THAN][LST_FLOATINGPOINT][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_GREATER_THAN][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_INTEGER;
+
+ // LET_PLUS
+ gSupportedExpressionArray[LET_PLUS][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PLUS][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_PLUS][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_PLUS][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_PLUS][LST_STRING][LST_STRING] = LST_STRING;
+ gSupportedExpressionArray[LET_PLUS][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_PLUS][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_INTEGER] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_FLOATINGPOINT] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_STRING] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_KEY] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_VECTOR] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_QUATERNION] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_INTEGER][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_FLOATINGPOINT][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_STRING][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_KEY][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_VECTOR][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_QUATERNION][LST_LIST] = LST_LIST;
+ gSupportedExpressionArray[LET_PLUS][LST_LIST][LST_LIST] = LST_LIST;
+
+ // LET_MINUS
+ gSupportedExpressionArray[LET_MINUS][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_MINUS][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MINUS][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MINUS][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_MINUS][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_MINUS][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_TIMES
+ gSupportedExpressionArray[LET_TIMES][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_TIMES][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_TIMES][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_TIMES][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_TIMES][LST_VECTOR][LST_INTEGER] = LST_VECTOR;
+ gSupportedExpressionArray[LET_TIMES][LST_INTEGER][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_TIMES][LST_VECTOR][LST_FLOATINGPOINT] = LST_VECTOR;
+ gSupportedExpressionArray[LET_TIMES][LST_FLOATINGPOINT][LST_VECTOR] = LST_VECTOR;
+ gSupportedExpressionArray[LET_TIMES][LST_VECTOR][LST_VECTOR] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_TIMES][LST_VECTOR][LST_QUATERNION] = LST_VECTOR;
+ gSupportedExpressionArray[LET_TIMES][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_DIVIDE
+ gSupportedExpressionArray[LET_DIVIDE][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_DIVIDE][LST_INTEGER][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_DIVIDE][LST_FLOATINGPOINT][LST_INTEGER] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_DIVIDE][LST_FLOATINGPOINT][LST_FLOATINGPOINT] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_DIVIDE][LST_VECTOR][LST_INTEGER] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIVIDE][LST_VECTOR][LST_FLOATINGPOINT] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIVIDE][LST_VECTOR][LST_QUATERNION] = LST_VECTOR;
+ gSupportedExpressionArray[LET_DIVIDE][LST_QUATERNION][LST_QUATERNION] = LST_QUATERNION;
+
+ // LET_MOD
+ gSupportedExpressionArray[LET_MOD][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+ gSupportedExpressionArray[LET_MOD][LST_VECTOR][LST_VECTOR] = LST_VECTOR;
+
+ // LET_BIT_AND
+ gSupportedExpressionArray[LET_BIT_AND][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_BIT_OR
+ gSupportedExpressionArray[LET_BIT_OR][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_BIT_XOR
+ gSupportedExpressionArray[LET_BIT_XOR][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_BOOLEAN_AND
+ gSupportedExpressionArray[LET_BOOLEAN_AND][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_BOOLEAN_OR
+ gSupportedExpressionArray[LET_BOOLEAN_OR][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_SHIFT_LEFT
+ gSupportedExpressionArray[LET_SHIFT_LEFT][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_SHIFT_RIGHT
+ gSupportedExpressionArray[LET_SHIFT_RIGHT][LST_INTEGER][LST_INTEGER] = LST_INTEGER;
+
+ // LET_PARENTHESIS
+ gSupportedExpressionArray[LET_PARENTHESIS][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PARENTHESIS][LST_FLOATINGPOINT][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PARENTHESIS][LST_STRING][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PARENTHESIS][LST_LIST][LST_NULL] = LST_INTEGER;
+
+ // LET_UNARY_MINUS
+ gSupportedExpressionArray[LET_UNARY_MINUS][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_UNARY_MINUS][LST_FLOATINGPOINT][LST_NULL] = LST_FLOATINGPOINT;
+ gSupportedExpressionArray[LET_UNARY_MINUS][LST_VECTOR][LST_NULL] = LST_VECTOR;
+ gSupportedExpressionArray[LET_UNARY_MINUS][LST_QUATERNION][LST_NULL] = LST_QUATERNION;
+
+ // LET_BOOLEAN_NOT
+ gSupportedExpressionArray[LET_BOOLEAN_NOT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+
+ // LET_BIT_NOT
+ gSupportedExpressionArray[LET_BIT_NOT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+
+ // LET_PRE_INCREMENT
+ gSupportedExpressionArray[LET_PRE_INCREMENT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PRE_INCREMENT][LST_FLOATINGPOINT][LST_NULL] = LST_FLOATINGPOINT;
+
+ // LET_PRE_DECREMENT
+ gSupportedExpressionArray[LET_PRE_DECREMENT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_PRE_DECREMENT][LST_FLOATINGPOINT][LST_NULL] = LST_FLOATINGPOINT;
+
+ // LET_POST_INCREMENT
+ gSupportedExpressionArray[LET_POST_INCREMENT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_POST_INCREMENT][LST_FLOATINGPOINT][LST_NULL] = LST_FLOATINGPOINT;
+
+ // LET_POST_DECREMENT
+ gSupportedExpressionArray[LET_POST_DECREMENT][LST_INTEGER][LST_NULL] = LST_INTEGER;
+ gSupportedExpressionArray[LET_POST_DECREMENT][LST_FLOATINGPOINT][LST_NULL] = LST_FLOATINGPOINT;
+}
+
+BOOL legal_binary_expression(LSCRIPTType &result, LSCRIPTType left_side, LSCRIPTType right_side, LSCRIPTExpressionType expression)
+{
+ if ( (left_side == LST_UNDEFINED)
+ ||(right_side == LST_UNDEFINED))
+ {
+ result = LST_UNDEFINED;
+ return TRUE;
+ }
+
+ if ( (left_side == LST_NULL)
+ ||(right_side == LST_NULL))
+ {
+ result = LST_UNDEFINED;
+ return FALSE;
+ }
+
+ result = gSupportedExpressionArray[expression][left_side][right_side];
+ if (result)
+ return TRUE;
+ else
+ {
+ result = LST_UNDEFINED;
+ return FALSE;
+ }
+}
+
+BOOL legal_unary_expression(LSCRIPTType &result, LSCRIPTType left_side, LSCRIPTExpressionType expression)
+{
+ if (left_side == LST_UNDEFINED)
+ {
+ result = LST_UNDEFINED;
+ return TRUE;
+ }
+
+ if (left_side == LST_NULL)
+ {
+ result = LST_UNDEFINED;
+ return FALSE;
+ }
+
+ result = gSupportedExpressionArray[expression][left_side][LST_NULL];
+ if (result)
+ return TRUE;
+ else
+ {
+ result = LST_UNDEFINED;
+ return FALSE;
+ }
+}
diff --git a/indra/lscript/lscript_compile/lscript_typecheck.h b/indra/lscript/lscript_compile/lscript_typecheck.h
new file mode 100644
index 0000000000..959a85e18e
--- /dev/null
+++ b/indra/lscript/lscript_compile/lscript_typecheck.h
@@ -0,0 +1,100 @@
+/**
+ * @file lscript_typecheck.h
+ * @brief typechecks script
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_TYPECHECK_H
+#define LL_LSCRIPT_TYPECHECK_H
+
+#include "lscript_error.h"
+
+LSCRIPTType implicit_casts(LSCRIPTType left_side, LSCRIPTType right_side);
+BOOL legal_casts(LSCRIPTType cast, LSCRIPTType base);
+LSCRIPTType promote(LSCRIPTType left_side, LSCRIPTType right_side);
+BOOL legal_assignment(LSCRIPTType left_side, LSCRIPTType right_side);
+
+typedef enum e_lscript_expression_types
+{
+ LET_NULL,
+ LET_ASSIGNMENT,
+ LET_ADD_ASSIGN,
+ LET_SUB_ASSIGN,
+ LET_MUL_ASSIGN,
+ LET_DIV_ASSIGN,
+ LET_MOD_ASSIGN,
+ LET_EQUALITY,
+ LET_NOT_EQUALS,
+ LET_LESS_EQUALS,
+ LET_GREATER_EQUALS,
+ LET_LESS_THAN,
+ LET_GREATER_THAN,
+ LET_PLUS,
+ LET_MINUS,
+ LET_TIMES,
+ LET_DIVIDE,
+ LET_MOD,
+ LET_BIT_AND,
+ LET_BIT_OR,
+ LET_BIT_XOR,
+ LET_BOOLEAN_AND,
+ LET_BOOLEAN_OR,
+ LET_PARENTHESIS,
+ LET_UNARY_MINUS,
+ LET_BOOLEAN_NOT,
+ LET_BIT_NOT,
+ LET_PRE_INCREMENT,
+ LET_PRE_DECREMENT,
+ LET_CAST,
+ LET_VECTOR_INITIALIZER,
+ LET_QUATERNION_INITIALIZER,
+ LET_LIST_INITIALIZER,
+ LET_LVALUE,
+ LET_POST_INCREMENT,
+ LET_POST_DECREMENT,
+ LET_FUNCTION_CALL,
+ LET_CONSTANT,
+ LET_FOR_EXPRESSION_LIST,
+ LET_FUNC_EXPRESSION_LIST,
+ LET_LIST_EXPRESSION_LIST,
+ LET_PRINT,
+ LET_SHIFT_LEFT,
+ LET_SHIFT_RIGHT,
+ LET_EOF
+} LSCRIPTExpressionType;
+
+BOOL legal_binary_expression(LSCRIPTType &result, LSCRIPTType left_side, LSCRIPTType right_side, LSCRIPTExpressionType expression);
+BOOL legal_unary_expression(LSCRIPTType &result, LSCRIPTType left_side, LSCRIPTExpressionType expression);
+
+void init_supported_expressions(void);
+
+/*
+ LScript automatic type casting
+
+ LST_INTEGER -> LST_INTEGER
+
+ LST_FLOATINGPOINT -> LST_FLOATINGPOINT
+ LST_INTEGER -> LST_FLOATINGPOINT
+
+ LST_FLOATINGPOINT -> LST_STRING
+ LST_INTEGER -> LST_STRING
+ LST_STRING -> LST_STRING
+ LST_VECTOR -> LST_STRING
+ LST_QUATERNION -> LST_STRING
+ LST_LIST -> LST_STRING
+
+ LST_VECTOR -> LST_VECTOR
+
+ LST_QUATERNION -> LST_QUATERNION
+
+ LST_FLOATINGPOINT -> LST_LIST
+ LST_INTEGER -> LST_LIST
+ LST_STRING -> LST_LIST
+ LST_VECTOR -> LST_LIST
+ LST_QUATERNION -> LST_LIST
+ LST_LIST -> LST_LIST
+*/
+
+#endif
diff --git a/indra/lscript/lscript_execute.h b/indra/lscript/lscript_execute.h
new file mode 100644
index 0000000000..84cd6e3b0a
--- /dev/null
+++ b/indra/lscript/lscript_execute.h
@@ -0,0 +1,364 @@
+/**
+ * @file lscript_execute.h
+ * @brief Classes to execute bytecode
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_EXECUTE_H
+#define LL_LSCRIPT_EXECUTE_H
+
+#include <stdio.h>
+#include "lscript_byteconvert.h"
+#include "linked_lists.h"
+#include "lscript_library.h"
+
+// Return values for run() methods
+const U32 NO_DELETE_FLAG = 0x0000;
+const U32 DELETE_FLAG = 0x0001;
+const U32 CREDIT_MONEY_FLAG = 0x0002;
+
+// list of op code execute functions
+BOOL run_noop(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pop(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pops(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_poparg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popip(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popbp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_popslr(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_dup(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_dups(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_dupl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_dupv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_dupq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_store(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_stores(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storel(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storev(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storeq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storeg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storegs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storegl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storegv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_storegq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadlp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadvp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadqp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadgp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadgsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadglp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadgvp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_loadgqp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_push(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushgs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushgl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushgv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushgq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_puship(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushbp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargb(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargi(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargf(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushargq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushe(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pushev(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pusheq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_pusharge(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_add(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_sub(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_mul(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_div(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_mod(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_eq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_neq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_leq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_geq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_less(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_greater(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_bitand(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_bitor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_bitxor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_booland(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_boolor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_shl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_shr(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_neg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_bitnot(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_boolnot(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_jump(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_jumpif(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_jumpnif(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_state(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_call(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_return(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_cast(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_stacktos(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_stacktol(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_print(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+BOOL run_calllib_two_byte(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+void unknown_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void integer_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void integer_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void integer_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void float_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void float_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void float_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void string_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void string_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void key_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void key_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void quaternion_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+
+
+void integer_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void float_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void string_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void key_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void quaternion_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void list_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+
+void integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+
+class LLScriptDataCollection
+{
+public:
+ LLScriptDataCollection(LSCRIPTStateEventType type, LLScriptLibData *data)
+ : mType(type), mData(data)
+ {
+ }
+ LLScriptDataCollection(U8 *src, S32 &offset)
+ {
+ S32 i, number;
+ mType = (LSCRIPTStateEventType)bytestream2integer(src, offset);
+ number = bytestream2integer(src, offset);
+
+ mData = new LLScriptLibData[number];
+
+ for (i = 0; i < number; i++)
+ {
+ mData[i].set(src, offset);
+ }
+
+ }
+
+ ~LLScriptDataCollection()
+ {
+ delete [] mData;
+ mData = NULL;
+ }
+
+ S32 getSavedSize()
+ {
+ S32 size = 0;
+ // mTyoe
+ size += 4;
+ // number of entries
+ size += 4;
+
+ S32 i = 0;
+ do
+ {
+ size += mData[i].getSavedSize();;
+ }
+ while (mData[i++].mType != LST_NULL);
+ return size;
+ }
+
+ S32 write2bytestream(U8 *dest)
+ {
+ S32 offset = 0;
+ // mTyoe
+ integer2bytestream(dest, offset, mType);
+ // count number of entries
+ S32 number = 0;
+ while (mData[number++].mType != LST_NULL)
+ ;
+ integer2bytestream(dest, offset, number);
+
+ // now the entries themselves
+ number = 0;
+ do
+ {
+ offset += mData[number].write2bytestream(dest + offset);
+ }
+ while (mData[number++].mType != LST_NULL);
+ return offset;
+ }
+
+
+ LSCRIPTStateEventType mType;
+ LLScriptLibData *mData;
+};
+const S32 MAX_EVENTS_IN_QUEUE = 64;
+
+class LLScriptEventData
+{
+public:
+ LLScriptEventData() {}
+ LLScriptEventData(U8 *src, S32 &offset)
+ {
+ S32 i, number = bytestream2integer(src, offset);
+ for (i = 0; i < number; i++)
+ {
+ mEventDataList.addData(new LLScriptDataCollection(src, offset));
+ }
+ }
+
+ void set(U8 *src, S32 &offset)
+ {
+ S32 i, number = bytestream2integer(src, offset);
+ for (i = 0; i < number; i++)
+ {
+ mEventDataList.addData(new LLScriptDataCollection(src, offset));
+ }
+ }
+
+ ~LLScriptEventData()
+ {
+ mEventDataList.deleteAllData();
+ }
+
+ void addEventData(LLScriptDataCollection *data)
+ {
+ if (mEventDataList.getLength() < MAX_EVENTS_IN_QUEUE)
+ mEventDataList.addDataAtEnd(data);
+ else
+ delete data;
+ }
+ LLScriptDataCollection *getNextEvent(LSCRIPTStateEventType type)
+ {
+ LLScriptDataCollection *temp;
+ for (temp = mEventDataList.getFirstData();
+ temp;
+ temp = mEventDataList.getNextData())
+ {
+ if (temp->mType == type)
+ {
+ mEventDataList.removeCurrentData();
+ return temp;
+ }
+ }
+ return NULL;
+ }
+ LLScriptDataCollection *getNextEvent()
+ {
+ LLScriptDataCollection *temp;
+ temp = mEventDataList.getFirstData();
+ if (temp)
+ {
+ mEventDataList.removeCurrentData();
+ return temp;
+ }
+ return NULL;
+ }
+ void removeEventType(LSCRIPTStateEventType type)
+ {
+ LLScriptDataCollection *temp;
+ for (temp = mEventDataList.getFirstData();
+ temp;
+ temp = mEventDataList.getNextData())
+ {
+ if (temp->mType == type)
+ {
+ mEventDataList.deleteCurrentData();
+ }
+ }
+ }
+
+ S32 getSavedSize()
+ {
+ S32 size = 0;
+ // number in linked list
+ size += 4;
+ LLScriptDataCollection *temp;
+ for (temp = mEventDataList.getFirstData();
+ temp;
+ temp = mEventDataList.getNextData())
+ {
+ size += temp->getSavedSize();
+ }
+ return size;
+ }
+
+ S32 write2bytestream(U8 *dest)
+ {
+ S32 offset = 0;
+ // number in linked list
+ S32 number = mEventDataList.getLength();
+ integer2bytestream(dest, offset, number);
+ LLScriptDataCollection *temp;
+ for (temp = mEventDataList.getFirstData();
+ temp;
+ temp = mEventDataList.getNextData())
+ {
+ offset += temp->write2bytestream(dest + offset);
+ }
+ return offset;
+ }
+
+ LLLinkedList<LLScriptDataCollection> mEventDataList;
+};
+
+class LLScriptExecute
+{
+public:
+ LLScriptExecute(FILE *fp);
+ LLScriptExecute(U8 *buffer);
+ ~LLScriptExecute();
+
+ void init();
+ U32 run(BOOL b_print, const LLUUID &id, char **errorstr, BOOL &state_transition);
+
+ BOOL (*mExecuteFuncs[0x100])(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id);
+
+ U32 mInstructionCount;
+ U8 *mBuffer;
+ LLScriptEventData mEventData;
+
+ static S64 sGlobalInstructionCount;
+};
+
+#endif
diff --git a/indra/lscript/lscript_execute/lscript_execute.cpp b/indra/lscript/lscript_execute/lscript_execute.cpp
new file mode 100644
index 0000000000..3e52334d14
--- /dev/null
+++ b/indra/lscript/lscript_execute/lscript_execute.cpp
@@ -0,0 +1,3910 @@
+/**
+ * @file lscript_execute.cpp
+ * @brief classes to execute bytecode
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <sstream>
+
+#include "lscript_execute.h"
+#include "lltimer.h"
+#include "lscript_readlso.h"
+#include "lscript_library.h"
+#include "lscript_heapruntime.h"
+#include "lscript_alloc.h"
+
+void (*binary_operations[LST_EOF][LST_EOF])(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+void (*unary_operations[LST_EOF])(U8 *buffer, LSCRIPTOpCodesEnum opcode);
+
+char *LSCRIPTRunTimeFaultStrings[LSRF_EOF] =
+{
+ "invalid", // LSRF_INVALID,
+ "Math Error", // LSRF_MATH,
+ "Stack-Heap Collision", // LSRF_STACK_HEAP_COLLISION,
+ "Bounds Check Error", // LSRF_BOUND_CHECK_ERROR,
+ "Heap Error", // LSRF_HEAP_ERROR,
+ "Version Mismatch", // LSRF_VERSION_MISMATCH,
+ "Missing Inventory", // LSRF_MISSING_INVENTORY,
+ "Hit Sandbox Limit", // LSRF_SANDBOX,
+ "Chat Overrun", // LSRF_CHAT_OVERRUN,
+ "Too Many Listens", // LSRF_TOO_MANY_LISTENS,
+ "Lists may not contain lists" // LSRF_NESTING_LISTS,
+};
+
+//static
+S64 LLScriptExecute::sGlobalInstructionCount = 0;
+
+LLScriptExecute::LLScriptExecute(FILE *fp)
+{
+ U8 sizearray[4];
+ S32 filesize;
+ S32 pos = 0;
+ fread(&sizearray, 1, 4, fp);
+ filesize = bytestream2integer(sizearray, pos);
+ mBuffer = new U8[filesize];
+ fseek(fp, 0, SEEK_SET);
+ fread(mBuffer, 1, filesize, fp);
+ fclose(fp);
+
+ init();
+}
+
+LLScriptExecute::LLScriptExecute(U8 *buffer)
+{
+ mBuffer = buffer;
+
+ init();
+}
+
+LLScriptExecute::~LLScriptExecute()
+{
+ delete [] mBuffer;
+}
+
+void LLScriptExecute::init()
+{
+ S32 i, j;
+
+ mInstructionCount = 0;
+
+ for (i = 0; i < 256; i++)
+ {
+ mExecuteFuncs[i] = run_noop;
+ }
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_NOOP]] = run_noop;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POP]] = run_pop;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPS]] = run_pops;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPL]] = run_popl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPV]] = run_popv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPQ]] = run_popq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPARG]] = run_poparg;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPIP]] = run_popip;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPBP]] = run_popbp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPSP]] = run_popsp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_POPSLR]] = run_popslr;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DUP]] = run_dup;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DUPS]] = run_dups;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DUPL]] = run_dupl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DUPV]] = run_dupv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DUPQ]] = run_dupq;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STORE]] = run_store;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STORES]] = run_stores;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREL]] = run_storel;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREV]] = run_storev;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREQ]] = run_storeq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREG]] = run_storeg;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREGL]] = run_storegl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREGS]] = run_storegs;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREGV]] = run_storegv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STOREGQ]] = run_storegq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADP]] = run_loadp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADSP]] = run_loadsp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADLP]] = run_loadlp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADVP]] = run_loadvp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADQP]] = run_loadqp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADGP]] = run_loadgp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADGSP]] = run_loadgsp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADGLP]] = run_loadglp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADGVP]] = run_loadgvp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LOADGQP]] = run_loadgqp;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSH]] = run_push;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHS]] = run_pushs;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHL]] = run_pushl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHV]] = run_pushv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHQ]] = run_pushq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHG]] = run_pushg;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHGS]] = run_pushgs;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHGL]] = run_pushgl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHGV]] = run_pushgv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHGQ]] = run_pushgq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHIP]] = run_puship;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHSP]] = run_pushsp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHBP]] = run_pushbp;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGB]] = run_pushargb;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGI]] = run_pushargi;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGF]] = run_pushargf;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGS]] = run_pushargs;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGV]] = run_pushargv;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGQ]] = run_pushargq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHE]] = run_pushe;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHEV]] = run_pushev;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHEQ]] = run_pusheq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PUSHARGE]] = run_pusharge;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_ADD]] = run_add;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_SUB]] = run_sub;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_MUL]] = run_mul;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_DIV]] = run_div;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_MOD]] = run_mod;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_EQ]] = run_eq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_NEQ]] = run_neq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LEQ]] = run_leq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_GEQ]] = run_geq;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_LESS]] = run_less;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_GREATER]] = run_greater;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BITAND]] = run_bitand;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BITOR]] = run_bitor;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BITXOR]] = run_bitxor;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BOOLAND]] = run_booland;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BOOLOR]] = run_boolor;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_SHL]] = run_shl;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_SHR]] = run_shr;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_NEG]] = run_neg;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BITNOT]] = run_bitnot;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_BOOLNOT]] = run_boolnot;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_JUMP]] = run_jump;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_JUMPIF]] = run_jumpif;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_JUMPNIF]] = run_jumpnif;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STATE]] = run_state;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_CALL]] = run_call;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_RETURN]] = run_return;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_CAST]] = run_cast;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STACKTOS]] = run_stacktos;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_STACKTOL]] = run_stacktol;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_PRINT]] = run_print;
+
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_CALLLIB]] = run_calllib;
+ mExecuteFuncs[LSCRIPTOpCodes[LOPC_CALLLIB_TWO_BYTE]] = run_calllib_two_byte;
+
+ for (i = 0; i < LST_EOF; i++)
+ {
+ for (j = 0; j < LST_EOF; j++)
+ {
+ binary_operations[i][j] = unknown_operation;
+ }
+ }
+
+ binary_operations[LST_INTEGER][LST_INTEGER] = integer_integer_operation;
+ binary_operations[LST_INTEGER][LST_FLOATINGPOINT] = integer_float_operation;
+ binary_operations[LST_INTEGER][LST_VECTOR] = integer_vector_operation;
+
+ binary_operations[LST_FLOATINGPOINT][LST_INTEGER] = float_integer_operation;
+ binary_operations[LST_FLOATINGPOINT][LST_FLOATINGPOINT] = float_float_operation;
+ binary_operations[LST_FLOATINGPOINT][LST_VECTOR] = float_vector_operation;
+
+ binary_operations[LST_STRING][LST_STRING] = string_string_operation;
+ binary_operations[LST_STRING][LST_KEY] = string_key_operation;
+
+ binary_operations[LST_KEY][LST_STRING] = key_string_operation;
+ binary_operations[LST_KEY][LST_KEY] = key_key_operation;
+
+ binary_operations[LST_VECTOR][LST_INTEGER] = vector_integer_operation;
+ binary_operations[LST_VECTOR][LST_FLOATINGPOINT] = vector_float_operation;
+ binary_operations[LST_VECTOR][LST_VECTOR] = vector_vector_operation;
+ binary_operations[LST_VECTOR][LST_QUATERNION] = vector_quaternion_operation;
+
+ binary_operations[LST_QUATERNION][LST_QUATERNION] = quaternion_quaternion_operation;
+
+ binary_operations[LST_INTEGER][LST_LIST] = integer_list_operation;
+ binary_operations[LST_FLOATINGPOINT][LST_LIST] = float_list_operation;
+ binary_operations[LST_STRING][LST_LIST] = string_list_operation;
+ binary_operations[LST_KEY][LST_LIST] = key_list_operation;
+ binary_operations[LST_VECTOR][LST_LIST] = vector_list_operation;
+ binary_operations[LST_QUATERNION][LST_LIST] = quaternion_list_operation;
+ binary_operations[LST_LIST][LST_INTEGER] = list_integer_operation;
+ binary_operations[LST_LIST][LST_FLOATINGPOINT] = list_float_operation;
+ binary_operations[LST_LIST][LST_STRING] = list_string_operation;
+ binary_operations[LST_LIST][LST_KEY] = list_key_operation;
+ binary_operations[LST_LIST][LST_VECTOR] = list_vector_operation;
+ binary_operations[LST_LIST][LST_QUATERNION] = list_quaternion_operation;
+ binary_operations[LST_LIST][LST_LIST] = list_list_operation;
+
+ for (i = 0; i < LST_EOF; i++)
+ {
+ unary_operations[i] = unknown_operation;
+ }
+
+ unary_operations[LST_INTEGER] = integer_operation;
+ unary_operations[LST_FLOATINGPOINT] = float_operation;
+ unary_operations[LST_VECTOR] = vector_operation;
+ unary_operations[LST_QUATERNION] = quaternion_operation;
+
+}
+
+S32 lscript_push_variable(LLScriptLibData *data, U8 *buffer);
+
+U32 LLScriptExecute::run(BOOL b_print, const LLUUID &id, char **errorstr, BOOL &state_transition)
+{
+ // is there a fault?
+ // if yes, print out message and exit
+ state_transition = FALSE;
+ S32 value = get_register(mBuffer, LREG_VN);
+ S32 major_version = 0;
+ if (value == LSL2_VERSION1_END_NUMBER)
+ {
+ major_version = 1;
+ }
+ else if (value == LSL2_VERSION_NUMBER)
+ {
+ major_version = 2;
+ }
+ else
+ {
+ set_fault(mBuffer, LSRF_VERSION_MISMATCH);
+ }
+ value = get_register(mBuffer, LREG_FR);
+ if (value)
+ {
+ if (b_print)
+ {
+ printf("Error!\n");
+ }
+ *errorstr = LSCRIPTRunTimeFaultStrings[value];
+ return NO_DELETE_FLAG;
+ }
+ else
+ {
+ *errorstr = NULL;
+ }
+
+ // Get IP
+ // is IP nonzero?
+ value = get_register(mBuffer, LREG_IP);
+
+ if (value)
+ {
+ // if yes, we're in opcodes, execute the next opcode by:
+ // call opcode run function pointer with buffer and IP
+ mInstructionCount++;
+ sGlobalInstructionCount++;
+ S32 tvalue = value;
+ S32 opcode = safe_instruction_bytestream2byte(mBuffer, tvalue);
+ S32 b_ret_val = mExecuteFuncs[opcode](mBuffer, value, b_print, id);
+ set_ip(mBuffer, value);
+ add_register_fp(mBuffer, LREG_ESR, -0.1f);
+ // lsa_print_heap(mBuffer);
+
+ if (b_print)
+ {
+ lsa_print_heap(mBuffer);
+ printf("ip: 0x%X\n", get_register(mBuffer, LREG_IP));
+ printf("sp: 0x%X\n", get_register(mBuffer, LREG_SP));
+ printf("bp: 0x%X\n", get_register(mBuffer, LREG_BP));
+ printf("hr: 0x%X\n", get_register(mBuffer, LREG_HR));
+ printf("hp: 0x%X\n", get_register(mBuffer, LREG_HP));
+ }
+ // update IP
+ if (b_ret_val)
+ {
+ return DELETE_FLAG | CREDIT_MONEY_FLAG;
+ }
+ else
+ {
+ return NO_DELETE_FLAG;
+ }
+ }
+ else
+ {
+ // make sure that IE is zero
+ set_event_register(mBuffer, LREG_IE, 0, major_version);
+
+ // if no, we're in a state and waiting for an event
+ S32 next_state = get_register(mBuffer, LREG_NS);
+ S32 current_state = get_register(mBuffer, LREG_CS);
+ U64 current_events = get_event_register(mBuffer, LREG_CE, major_version);
+ U64 event_register = get_event_register(mBuffer, LREG_ER, major_version);
+ // check NS to see if need to switch states (NS != CS)
+ if (next_state != current_state)
+ {
+ state_transition = TRUE;
+ // ok, blow away any pending events
+ mEventData.mEventDataList.deleteAllData();
+
+ // if yes, check state exit flag is set
+ if (current_events & LSCRIPTStateBitField[LSTT_STATE_EXIT])
+ {
+ // if yes, clear state exit flag
+ set_event_register(mBuffer, LREG_IE, LSCRIPTStateBitField[LSTT_STATE_EXIT], major_version);
+ current_events &= ~LSCRIPTStateBitField[LSTT_STATE_EXIT];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+ // check state exit event handler
+ // if there is a handler, call it
+ if (event_register & LSCRIPTStateBitField[LSTT_STATE_EXIT])
+ {
+ // push a zero to be popped
+ lscript_push(mBuffer, 0);
+ // push sp as current bp
+ S32 sp = get_register(mBuffer, LREG_SP);
+ lscript_push(mBuffer, sp);
+
+ // now, push any additional stack space
+ S32 additional_size = get_event_stack_size(mBuffer, current_state, LSTT_STATE_EXIT);
+ lscript_pusharge(mBuffer, additional_size);
+
+ sp = get_register(mBuffer, LREG_SP);
+ sp += additional_size;
+ set_bp(mBuffer, sp);
+ // set IP to the event handler
+ S32 opcode_start = get_state_event_opcoode_start(mBuffer, current_state, LSTT_STATE_EXIT);
+ set_ip(mBuffer, opcode_start);
+ return NO_DELETE_FLAG;
+ }
+ }
+ // if no handler or no state exit flag switch to new state
+ // set state entry flag and clear other CE flags
+ current_events = LSCRIPTStateBitField[LSTT_STATE_ENTRY];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+ // copy NS to CS
+ set_register(mBuffer, LREG_CS, next_state);
+ // copy new state's handled events into ER (SR + CS*4 + 4)
+ U64 handled_events = get_handled_events(mBuffer, next_state);
+ set_event_register(mBuffer, LREG_ER, handled_events, major_version);
+ }
+// check to see if any current events are covered by events handled by this state (CE & ER != 0)
+// now, we want to look like we were called like a function
+// 0x0000: 00 00 00 00 (return ip)
+// 0x0004: bp (current sp)
+// 0x0008: parameters
+// push sp
+// add parameter size
+// pop bp
+// set ip
+
+ S32 size = 0;
+// try to get next event from stack
+ BOOL b_done = FALSE;
+ LSCRIPTStateEventType event = LSTT_NULL;
+ LLScriptDataCollection *eventdata;
+
+ next_state = get_register(mBuffer, LREG_NS);
+ current_state = get_register(mBuffer, LREG_CS);
+ current_events = get_event_register(mBuffer, LREG_CE, major_version);
+ event_register = get_event_register(mBuffer, LREG_ER, major_version);
+
+ // first, check to see if state_entry or onrez are raised and handled
+ if ( (current_events & LSCRIPTStateBitField[LSTT_STATE_ENTRY])
+ &&(current_events & event_register))
+ {
+ // ok, this is easy since there isn't any data waiting, just set it
+ // push a zero to be popped
+ lscript_push(mBuffer, 0);
+// push sp as current bp
+ S32 sp = get_register(mBuffer, LREG_SP);
+ lscript_push(mBuffer, sp);
+
+ event = return_first_event((S32)LSCRIPTStateBitField[LSTT_STATE_ENTRY]);
+ set_event_register(mBuffer, LREG_IE, LSCRIPTStateBitField[event], major_version);
+ current_events &= ~LSCRIPTStateBitField[event];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+// now, push any additional stack space
+ S32 additional_size = get_event_stack_size(mBuffer, current_state, event) - size;
+ lscript_pusharge(mBuffer, additional_size);
+
+// now set the bp correctly
+ sp = get_register(mBuffer, LREG_SP);
+ sp += additional_size + size;
+ set_bp(mBuffer, sp);
+// set IP to the function
+ S32 opcode_start = get_state_event_opcoode_start(mBuffer, current_state, event);
+ set_ip(mBuffer, opcode_start);
+ b_done = TRUE;
+ }
+ else if ( (current_events & LSCRIPTStateBitField[LSTT_REZ])
+ &&(current_events & event_register))
+ {
+ for (eventdata = mEventData.mEventDataList.getFirstData(); eventdata; eventdata = mEventData.mEventDataList.getNextData())
+ {
+ if (eventdata->mType & LSCRIPTStateBitField[LSTT_REZ])
+ {
+ // push a zero to be popped
+ lscript_push(mBuffer, 0);
+ // push sp as current bp
+ S32 sp = get_register(mBuffer, LREG_SP);
+ lscript_push(mBuffer, sp);
+
+ set_event_register(mBuffer, LREG_IE, LSCRIPTStateBitField[event], major_version);
+ current_events &= ~LSCRIPTStateBitField[event];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+
+ // push any arguments that need to be pushed onto the stack
+ // last piece of data will be type LST_NULL
+ LLScriptLibData *data = eventdata->mData;
+ while (data->mType)
+ {
+ size += lscript_push_variable(data, mBuffer);
+ data++;
+ }
+ // now, push any additional stack space
+ S32 additional_size = get_event_stack_size(mBuffer, current_state, event) - size;
+ lscript_pusharge(mBuffer, additional_size);
+
+ // now set the bp correctly
+ sp = get_register(mBuffer, LREG_SP);
+ sp += additional_size + size;
+ set_bp(mBuffer, sp);
+ // set IP to the function
+ S32 opcode_start = get_state_event_opcoode_start(mBuffer, current_state, event);
+ set_ip(mBuffer, opcode_start);
+ mEventData.mEventDataList.deleteCurrentData();
+ b_done = TRUE;
+ break;
+ }
+ }
+ }
+
+ while (!b_done)
+ {
+ eventdata = mEventData.getNextEvent();
+ if (eventdata)
+ {
+ event = eventdata->mType;
+
+ // make sure that we can actually handle this one
+ if (LSCRIPTStateBitField[event] & event_register)
+ {
+ // push a zero to be popped
+ lscript_push(mBuffer, 0);
+ // push sp as current bp
+ S32 sp = get_register(mBuffer, LREG_SP);
+ lscript_push(mBuffer, sp);
+
+ set_event_register(mBuffer, LREG_IE, LSCRIPTStateBitField[event], major_version);
+ current_events &= ~LSCRIPTStateBitField[event];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+
+ // push any arguments that need to be pushed onto the stack
+ // last piece of data will be type LST_NULL
+ LLScriptLibData *data = eventdata->mData;
+ while (data->mType)
+ {
+ size += lscript_push_variable(data, mBuffer);
+ data++;
+ }
+ b_done = TRUE;
+ // now, push any additional stack space
+ S32 additional_size = get_event_stack_size(mBuffer, current_state, event) - size;
+ lscript_pusharge(mBuffer, additional_size);
+
+ // now set the bp correctly
+ sp = get_register(mBuffer, LREG_SP);
+ sp += additional_size + size;
+ set_bp(mBuffer, sp);
+ // set IP to the function
+ S32 opcode_start = get_state_event_opcoode_start(mBuffer, current_state, event);
+ set_ip(mBuffer, opcode_start);
+ }
+ else
+ {
+ llwarns << "Shit, somehow got an event that we're not registered for!" << llendl;
+ }
+ delete eventdata;
+ }
+ else
+ {
+// if no data waiting, do it the old way:
+ U64 handled_current = current_events & event_register;
+ if (handled_current)
+ {
+ // push a zero to be popped
+ lscript_push(mBuffer, 0);
+ // push sp as current bp
+ S32 sp = get_register(mBuffer, LREG_SP);
+ lscript_push(mBuffer, sp);
+
+ event = return_first_event((S32)handled_current);
+ set_event_register(mBuffer, LREG_IE, LSCRIPTStateBitField[event], major_version);
+ current_events &= ~LSCRIPTStateBitField[event];
+ set_event_register(mBuffer, LREG_CE, current_events, major_version);
+ // now, push any additional stack space
+ S32 additional_size = get_event_stack_size(mBuffer, current_state, event) - size;
+ lscript_pusharge(mBuffer, additional_size);
+
+ // now set the bp correctly
+ sp = get_register(mBuffer, LREG_SP);
+ sp += additional_size + size;
+ set_bp(mBuffer, sp);
+ // set IP to the function
+ S32 opcode_start = get_state_event_opcoode_start(mBuffer, current_state, event);
+ set_ip(mBuffer, opcode_start);
+ }
+ b_done = TRUE;
+ }
+ }
+
+ return NO_DELETE_FLAG;
+ }
+}
+
+BOOL run_noop(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tNOOP\n", offset);
+ offset++;
+ return FALSE;
+}
+
+BOOL run_pop(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOP\n", offset);
+ offset++;
+ lscript_poparg(buffer, LSCRIPTDataSize[LST_INTEGER]);
+ return FALSE;
+}
+
+BOOL run_pops(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPS\n", offset);
+ offset++;
+ S32 address = lscript_pop_int(buffer);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_popl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPL\n", offset);
+ offset++;
+ S32 address = lscript_pop_int(buffer);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_popv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPV\n", offset);
+ offset++;
+ lscript_poparg(buffer, LSCRIPTDataSize[LST_VECTOR]);
+ return FALSE;
+}
+
+BOOL run_popq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPQ\n", offset);
+ offset++;
+ lscript_poparg(buffer, LSCRIPTDataSize[LST_QUATERNION]);
+ return FALSE;
+}
+
+BOOL run_poparg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPARG ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+ lscript_poparg(buffer, arg);
+ return FALSE;
+}
+
+BOOL run_popip(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPIP\n", offset);
+ offset++;
+ offset = lscript_pop_int(buffer);
+ return FALSE;
+}
+
+BOOL run_popbp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPBP\n", offset);
+ offset++;
+ S32 bp = lscript_pop_int(buffer);
+ set_bp(buffer, bp);
+ return FALSE;
+}
+
+BOOL run_popsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPSP\n", offset);
+ offset++;
+ S32 sp = lscript_pop_int(buffer);
+ set_sp(buffer, sp);
+ return FALSE;
+}
+
+BOOL run_popslr(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPOPSLR\n", offset);
+ offset++;
+ S32 slr = lscript_pop_int(buffer);
+ set_register(buffer, LREG_SLR, slr);
+ return FALSE;
+}
+
+BOOL run_dup(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDUP\n", offset);
+ offset++;
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_dups(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDUPS\n", offset);
+ offset++;
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_dupl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDUPL\n", offset);
+ offset++;
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_dupv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDUPV\n", offset);
+ offset++;
+ S32 sp = get_register(buffer, LREG_SP);
+ LLVector3 value;
+ bytestream2vector(value, buffer, sp);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_dupq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDUPV\n", offset);
+ offset++;
+ S32 sp = get_register(buffer, LREG_SP);
+ LLQuaternion value;
+ bytestream2quaternion(value, buffer, sp);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_store(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTORE ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_stores(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTORES ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+
+ S32 address = lscript_local_get(buffer, arg);
+
+ lscript_local_store(buffer, arg, value);
+ lsa_increase_ref_count(buffer, value);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_storel(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREL ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+
+ S32 address = lscript_local_get(buffer, arg);
+
+ lscript_local_store(buffer, arg, value);
+ lsa_increase_ref_count(buffer, value);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_storev(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREV ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ S32 sp = get_register(buffer, LREG_SP);
+ bytestream2vector(value, buffer, sp);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_storeq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREQ ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ S32 sp = get_register(buffer, LREG_SP);
+ bytestream2quaternion(value, buffer, sp);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_storeg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREG ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_storegs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGS ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+
+ S32 address = lscript_global_get(buffer, arg);
+
+ lscript_global_store(buffer, arg, value);
+
+ lsa_increase_ref_count(buffer, value);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_storegl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGL ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 sp = get_register(buffer, LREG_SP);
+ S32 value = bytestream2integer(buffer, sp);
+
+ S32 address = lscript_global_get(buffer, arg);
+
+ lscript_global_store(buffer, arg, value);
+
+ lsa_increase_ref_count(buffer, value);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+ return FALSE;
+}
+
+BOOL run_storegv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGV ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ S32 sp = get_register(buffer, LREG_SP);
+ bytestream2vector(value, buffer, sp);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_storegq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGQ ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ S32 sp = get_register(buffer, LREG_SP);
+ bytestream2quaternion(value, buffer, sp);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_pop_int(buffer);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTORESP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_pop_int(buffer);
+
+ S32 address = lscript_local_get(buffer, arg);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadlp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTORELP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_pop_int(buffer);
+
+ S32 address = lscript_local_get(buffer, arg);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadvp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREVP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ lscript_pop_vector(buffer, value);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadqp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREQP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ lscript_pop_quaternion(buffer, value);
+ lscript_local_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadgp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_pop_int(buffer);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadgsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGSP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+ S32 value = lscript_pop_int(buffer);
+
+ S32 address = lscript_global_get(buffer, arg);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadglp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGLP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_pop_int(buffer);
+
+ S32 address = lscript_global_get(buffer, arg);
+ if (address)
+ lsa_decrease_ref_count(buffer, address);
+
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadgvp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGVP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ lscript_pop_vector(buffer, value);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_loadgqp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTOREGQP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ lscript_pop_quaternion(buffer, value);
+ lscript_global_store(buffer, arg, value);
+ return FALSE;
+}
+
+BOOL run_push(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSH ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_local_get(buffer, arg);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHS ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_local_get(buffer, arg);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHL ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_local_get(buffer, arg);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHV ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ lscript_local_get(buffer, arg, value);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHQ ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ lscript_local_get(buffer, arg, value);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHG ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_global_get(buffer, arg);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushgs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHGS ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_global_get(buffer, arg);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushgl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHGL ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ S32 value = lscript_global_get(buffer, arg);
+ lscript_push(buffer, value);
+ lsa_increase_ref_count(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushgv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHGV ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLVector3 value;
+ lscript_global_get(buffer, arg, value);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_pushgq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHGQ ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("0x%X\n", arg);
+ LLQuaternion value;
+ lscript_global_get(buffer, arg, value);
+ lscript_push(buffer, value);
+ return FALSE;
+}
+
+BOOL run_puship(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHIP\n", offset);
+ offset++;
+ lscript_push(buffer, offset);
+ return FALSE;
+}
+
+BOOL run_pushbp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHBP\n", offset);
+ offset++;
+ lscript_push(buffer, get_register(buffer, LREG_BP));
+ return FALSE;
+}
+
+BOOL run_pushsp(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHSP\n", offset);
+ offset++;
+ lscript_push(buffer, get_register(buffer, LREG_SP));
+ return FALSE;
+}
+
+BOOL run_pushargb(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHGARGB ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ if (b_print)
+ printf("%d\n", (U32)arg);
+ lscript_push(buffer, arg);
+ return FALSE;
+}
+
+BOOL run_pushargi(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGI ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+ lscript_push(buffer, arg);
+ return FALSE;
+}
+
+BOOL run_pushargf(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGF ", offset);
+ offset++;
+ F32 arg = safe_instruction_bytestream2float(buffer, offset);
+ if (b_print)
+ printf("%f\n", arg);
+ lscript_push(buffer, arg);
+ return FALSE;
+}
+
+BOOL run_pushargs(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGS ", offset);
+ S32 toffset = offset;
+ safe_instruction_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - offset;
+ char *arg = new char[size];
+ offset++;
+ safe_instruction_bytestream2char(arg, buffer, offset);
+ if (b_print)
+ printf("%s\n", arg);
+ S32 address = lsa_heap_add_data(buffer, new LLScriptLibData(arg), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ delete [] arg;
+ return FALSE;
+}
+
+BOOL run_pushargv(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGV ", offset);
+ offset++;
+ LLVector3 arg;
+ safe_instruction_bytestream2vector(arg, buffer, offset);
+ if (b_print)
+ printf("< %f, %f, %f >\n", arg.mV[VX], arg.mV[VY], arg.mV[VZ]);
+ lscript_push(buffer, arg);
+ return FALSE;
+}
+BOOL run_pushargq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGQ ", offset);
+ offset++;
+ LLQuaternion arg;
+ safe_instruction_bytestream2quaternion(arg, buffer, offset);
+ if (b_print)
+ printf("< %f, %f, %f, %f >\n", arg.mQ[VX], arg.mQ[VY], arg.mQ[VZ], arg.mQ[VS]);
+ lscript_push(buffer, arg);
+ return FALSE;
+}
+BOOL run_pushe(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHE\n", offset);
+ offset++;
+ lscript_pusharge(buffer, LSCRIPTDataSize[LST_INTEGER]);
+ return FALSE;
+}
+BOOL run_pushev(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHEV\n", offset);
+ offset++;
+ lscript_pusharge(buffer, LSCRIPTDataSize[LST_VECTOR]);
+ return FALSE;
+}
+BOOL run_pusheq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHEQ\n", offset);
+ offset++;
+ lscript_pusharge(buffer, LSCRIPTDataSize[LST_QUATERNION]);
+ return FALSE;
+}
+BOOL run_pusharge(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPUSHARGE ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+ lscript_pusharge(buffer, arg);
+ return FALSE;
+}
+
+void print_type(U8 type)
+{
+ if (type == LSCRIPTTypeByte[LST_INTEGER])
+ {
+ printf("integer");
+ }
+ else if (type == LSCRIPTTypeByte[LST_FLOATINGPOINT])
+ {
+ printf("float");
+ }
+ else if (type == LSCRIPTTypeByte[LST_STRING])
+ {
+ printf("string");
+ }
+ else if (type == LSCRIPTTypeByte[LST_KEY])
+ {
+ printf("key");
+ }
+ else if (type == LSCRIPTTypeByte[LST_VECTOR])
+ {
+ printf("vector");
+ }
+ else if (type == LSCRIPTTypeByte[LST_QUATERNION])
+ {
+ printf("quaternion");
+ }
+ else if (type == LSCRIPTTypeByte[LST_LIST])
+ {
+ printf("list");
+ }
+}
+
+void unknown_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ printf("Unknown arithmetic operation!\n");
+}
+
+void integer_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 result = 0;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ result = lside + rside;
+ break;
+ case LOPC_SUB:
+ result = lside - rside;
+ break;
+ case LOPC_MUL:
+ result = lside * rside;
+ break;
+ case LOPC_DIV:
+ if (rside)
+ result = lside / rside;
+ else
+ set_fault(buffer, LSRF_MATH);
+ break;
+ case LOPC_MOD:
+ if (rside)
+ result = lside % rside;
+ else
+ set_fault(buffer, LSRF_MATH);
+ break;
+ case LOPC_EQ:
+ result = (lside == rside);
+ break;
+ case LOPC_NEQ:
+ result = (lside != rside);
+ break;
+ case LOPC_LEQ:
+ result = (lside <= rside);
+ break;
+ case LOPC_GEQ:
+ result = (lside >= rside);
+ break;
+ case LOPC_LESS:
+ result = (lside < rside);
+ break;
+ case LOPC_GREATER:
+ result = (lside > rside);
+ break;
+ case LOPC_BITAND:
+ result = (lside & rside);
+ break;
+ case LOPC_BITOR:
+ result = (lside | rside);
+ break;
+ case LOPC_BITXOR:
+ result = (lside ^ rside);
+ break;
+ case LOPC_BOOLAND:
+ result = (lside && rside);
+ break;
+ case LOPC_BOOLOR:
+ result = (lside || rside);
+ break;
+ case LOPC_SHL:
+ result = (lside << rside);
+ break;
+ case LOPC_SHR:
+ result = (lside >> rside);
+ break;
+ default:
+ break;
+ }
+ lscript_push(buffer, result);
+}
+
+void integer_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ F32 rside = lscript_pop_float(buffer);
+ S32 resulti = 0;
+ F32 resultf = 0;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ resultf = lside + rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_SUB:
+ resultf = lside - rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_MUL:
+ resultf = lside * rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_DIV:
+ if (rside)
+ resultf = lside / rside;
+ else
+ set_fault(buffer, LSRF_MATH);
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_EQ:
+ resulti = (lside == rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = (lside != rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LEQ:
+ resulti = (lside <= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GEQ:
+ resulti = (lside >= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LESS:
+ resulti = (lside < rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GREATER:
+ resulti = (lside > rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void integer_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ LLVector3 rside;
+ lscript_pop_vector(buffer, rside);
+
+ switch(opcode)
+ {
+ case LOPC_MUL:
+ rside *= (F32)lside;
+ lscript_push(buffer, rside);
+ break;
+ default:
+ break;
+ }
+}
+
+void float_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ F32 lside = lscript_pop_float(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti = 0;
+ F32 resultf = 0;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ resultf = lside + rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_SUB:
+ resultf = lside - rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_MUL:
+ resultf = lside * rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_DIV:
+ if (rside)
+ resultf = lside / rside;
+ else
+ set_fault(buffer, LSRF_MATH);
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_EQ:
+ resulti = (lside == rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = (lside != rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LEQ:
+ resulti = (lside <= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GEQ:
+ resulti = (lside >= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LESS:
+ resulti = (lside < rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GREATER:
+ resulti = (lside > rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void float_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ F32 lside = lscript_pop_float(buffer);
+ F32 rside = lscript_pop_float(buffer);
+ F32 resultf = 0;
+ S32 resulti = 0;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ resultf = lside + rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_SUB:
+ resultf = lside - rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_MUL:
+ resultf = lside * rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_DIV:
+ if (rside)
+ resultf = lside / rside;
+ else
+ set_fault(buffer, LSRF_MATH);
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_EQ:
+ resulti = (lside == rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = (lside != rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LEQ:
+ resulti = (lside <= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GEQ:
+ resulti = (lside >= rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_LESS:
+ resulti = (lside < rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_GREATER:
+ resulti = (lside > rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void float_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ F32 lside = lscript_pop_float(buffer);
+ LLVector3 rside;
+ lscript_pop_vector(buffer, rside);
+
+ switch(opcode)
+ {
+ case LOPC_MUL:
+ rside *= lside;
+ lscript_push(buffer, rside);
+ break;
+ default:
+ break;
+ }
+}
+
+void string_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti;
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ address = lsa_cat_strings(buffer, lside, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ break;
+ case LOPC_EQ:
+ resulti = !lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void string_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti;
+
+ switch(opcode)
+ {
+ case LOPC_NEQ:
+ resulti = lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_EQ:
+ resulti = !lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void key_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti;
+
+ switch(opcode)
+ {
+ case LOPC_NEQ:
+ resulti = lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_EQ:
+ resulti = !lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void key_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti;
+
+ switch(opcode)
+ {
+ case LOPC_EQ:
+ resulti = !lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = lsa_cmp_strings(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ S32 rside = lscript_pop_int(buffer);
+
+ switch(opcode)
+ {
+ case LOPC_MUL:
+ lside *= (F32)rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_DIV:
+ if (rside)
+ lside *= (1.f/rside);
+ else
+ set_fault(buffer, LSRF_MATH);
+ lscript_push(buffer, lside);
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ F32 rside = lscript_pop_float(buffer);
+
+ switch(opcode)
+ {
+ case LOPC_MUL:
+ lside *= rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_DIV:
+ if (rside)
+ lside *= (1.f/rside);
+ else
+ set_fault(buffer, LSRF_MATH);
+ lscript_push(buffer, lside);
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ LLVector3 rside;
+ lscript_pop_vector(buffer, rside);
+ S32 resulti = 0;
+ F32 resultf = 0.f;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ lside += rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_SUB:
+ lside -= rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_MUL:
+ resultf = lside * rside;
+ lscript_push(buffer, resultf);
+ break;
+ case LOPC_MOD:
+ lside = lside % rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_EQ:
+ resulti = (lside == rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = (lside != rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ LLQuaternion rside;
+ lscript_pop_quaternion(buffer, rside);
+
+ switch(opcode)
+ {
+ case LOPC_MUL:
+ lside = lside * rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_DIV:
+ lside = lside * rside.conjQuat();
+ lscript_push(buffer, lside);
+ break;
+ default:
+ break;
+ }
+}
+
+void quaternion_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLQuaternion lside;
+ lscript_pop_quaternion(buffer, lside);
+ LLQuaternion rside;
+ lscript_pop_quaternion(buffer, rside);
+ S32 resulti = 0;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ lside = lside + rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_SUB:
+ lside = lside - rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_MUL:
+ lside *= rside;
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_DIV:
+ lside = lside * rside.conjQuat();
+ lscript_push(buffer, lside);
+ break;
+ case LOPC_EQ:
+ resulti = (lside == rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = (lside != rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+void integer_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(lside);
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void float_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ F32 lside = lscript_pop_float(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(lside);
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void string_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *string = lsa_get_data(buffer, lside, TRUE);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = string;
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void key_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *key = lsa_get_data(buffer, lside, TRUE);
+ // need to convert key to key, since it comes out like a string
+ if (key->mType == LST_STRING)
+ {
+ key->mKey = key->mString;
+ key->mString = NULL;
+ key->mType = LST_KEY;
+ }
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = key;
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(lside);
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void quaternion_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLQuaternion lside;
+ lscript_pop_quaternion(buffer, lside);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(lside);
+ address = lsa_preadd_lists(buffer, list, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ list->mListp = NULL;
+ delete list;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(rside);
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ F32 rside = lscript_pop_float(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(rside);
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_string_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *string = lsa_get_data(buffer, rside, TRUE);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = string;
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_key_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *key = lsa_get_data(buffer, rside, TRUE);
+ // need to convert key to key, since it comes out like a string
+ if (key->mType == LST_STRING)
+ {
+ key->mKey = key->mString;
+ key->mString = NULL;
+ key->mType = LST_KEY;
+ }
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = key;
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ LLVector3 rside;
+ lscript_pop_vector(buffer, rside);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(rside);
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ LLQuaternion rside;
+ lscript_pop_quaternion(buffer, rside);
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ {
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(rside);
+ address = lsa_postadd_lists(buffer, lside, list, get_max_heap_size(buffer));
+ list->mListp = NULL;
+ delete list;
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void list_list_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 rside = lscript_pop_int(buffer);
+ S32 resulti;
+ S32 address;
+
+ switch(opcode)
+ {
+ case LOPC_ADD:
+ address = lsa_cat_lists(buffer, lside, rside, get_max_heap_size(buffer));
+ lscript_push(buffer, address);
+ break;
+ case LOPC_EQ:
+ resulti = !lsa_cmp_lists(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ case LOPC_NEQ:
+ resulti = lsa_cmp_lists(buffer, lside, rside);
+ lscript_push(buffer, resulti);
+ break;
+ default:
+ break;
+ }
+}
+
+BOOL run_add(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tADD ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_ADD);
+ return FALSE;
+}
+
+BOOL run_sub(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSUB ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_SUB);
+ return FALSE;
+}
+BOOL run_mul(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tMUL ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_MUL);
+ return FALSE;
+}
+BOOL run_div(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tDIV ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_DIV);
+ return FALSE;
+}
+BOOL run_mod(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tMOD ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_MOD);
+ return FALSE;
+}
+
+BOOL run_eq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tEQ ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_EQ);
+ return FALSE;
+}
+BOOL run_neq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tNEQ ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_NEQ);
+ return FALSE;
+}
+BOOL run_leq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tLEQ ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_LEQ);
+ return FALSE;
+}
+BOOL run_geq(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tGEQ ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_GEQ);
+ return FALSE;
+}
+BOOL run_less(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tLESS ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_LESS);
+ return FALSE;
+}
+BOOL run_greater(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tGREATER ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 arg1 = arg >> 4;
+ U8 arg2 = arg & 0xf;
+ if (b_print)
+ {
+ print_type(arg1);
+ printf(", ");
+ print_type(arg2);
+ printf("\n");
+ }
+ binary_operations[arg1][arg2](buffer, LOPC_GREATER);
+ return FALSE;
+}
+
+BOOL run_bitand(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBITAND\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_BITAND);
+ return FALSE;
+}
+BOOL run_bitor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBITOR\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_BITOR);
+ return FALSE;
+}
+BOOL run_bitxor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBITXOR\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_BITXOR);
+ return FALSE;
+}
+BOOL run_booland(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBOOLAND\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_BOOLAND);
+ return FALSE;
+}
+BOOL run_boolor(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBOOLOR\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_BOOLOR);
+ return FALSE;
+}
+
+BOOL run_shl(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSHL\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_SHL);
+ return FALSE;
+}
+BOOL run_shr(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSHR\n", offset);
+ offset++;
+ binary_operations[LST_INTEGER][LST_INTEGER](buffer, LOPC_SHR);
+ return FALSE;
+}
+
+void integer_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ S32 lside = lscript_pop_int(buffer);
+ S32 result = 0;
+
+ switch(opcode)
+ {
+ case LOPC_NEG:
+ result = -lside;
+ break;
+ case LOPC_BITNOT:
+ result = ~lside;
+ break;
+ case LOPC_BOOLNOT:
+ result = !lside;
+ break;
+ default:
+ break;
+ }
+ lscript_push(buffer, result);
+}
+
+void float_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ F32 lside = lscript_pop_float(buffer);
+ F32 result = 0;
+
+ switch(opcode)
+ {
+ case LOPC_NEG:
+ result = -lside;
+ lscript_push(buffer, result);
+ break;
+ default:
+ break;
+ }
+}
+
+void vector_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLVector3 lside;
+ lscript_pop_vector(buffer, lside);
+ LLVector3 result;
+
+ switch(opcode)
+ {
+ case LOPC_NEG:
+ result = -lside;
+ lscript_push(buffer, result);
+ break;
+ default:
+ break;
+ }
+}
+
+void quaternion_operation(U8 *buffer, LSCRIPTOpCodesEnum opcode)
+{
+ LLQuaternion lside;
+ lscript_pop_quaternion(buffer, lside);
+ LLQuaternion result;
+
+ switch(opcode)
+ {
+ case LOPC_NEG:
+ result = -lside;
+ lscript_push(buffer, result);
+ break;
+ default:
+ break;
+ }
+}
+
+
+BOOL run_neg(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tNEG ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ if (b_print)
+ {
+ print_type(arg);
+ printf("\n");
+ }
+ unary_operations[arg](buffer, LOPC_NEG);
+ return FALSE;
+}
+
+BOOL run_bitnot(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBITNOT\n", offset);
+ offset++;
+ unary_operations[LST_INTEGER](buffer, LOPC_BITNOT);
+ return FALSE;
+}
+
+BOOL run_boolnot(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tBOOLNOT\n", offset);
+ offset++;
+ unary_operations[LST_INTEGER](buffer, LOPC_BOOLNOT);
+ return FALSE;
+}
+
+BOOL run_jump(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tJUMP ", offset);
+ offset++;
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+ offset += arg;
+ return FALSE;
+}
+BOOL run_jumpif(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tJUMPIF ", offset);
+ offset++;
+ U8 type = safe_instruction_bytestream2byte(buffer, offset);
+ if (b_print)
+ {
+ print_type(type);
+ printf(", ");
+ }
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+
+ if (type == LST_INTEGER)
+ {
+ S32 test = lscript_pop_int(buffer);
+ if (test)
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_FLOATINGPOINT)
+ {
+ F32 test = lscript_pop_float(buffer);
+ if (test)
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_VECTOR)
+ {
+ LLVector3 test;
+ lscript_pop_vector(buffer, test);
+ if (!test.isExactlyZero())
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_QUATERNION)
+ {
+ LLQuaternion test;
+ lscript_pop_quaternion(buffer, test);
+ if (!test.isIdentity())
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_STRING)
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *sdata = new char[size];
+ bytestream2char(sdata, buffer, string);
+ if (strlen(sdata))
+ {
+ offset += arg;
+ }
+ delete [] sdata;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ else if (type == LST_KEY)
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *sdata = new char[size];
+ bytestream2char(sdata, buffer, string);
+ if (strlen(sdata))
+ {
+ LLUUID id;
+ id.set(sdata);
+ if (id != LLUUID::null)
+ offset += arg;
+ }
+ delete [] sdata;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else if (type == LST_LIST)
+ {
+ S32 address = lscript_pop_int(buffer);
+ LLScriptLibData *list = lsa_get_data(buffer, address, TRUE);
+ if (list->getListLength())
+ {
+ offset += arg;
+ }
+ }
+ }
+ return FALSE;
+}
+BOOL run_jumpnif(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tJUMPNIF ", offset);
+ offset++;
+ U8 type = safe_instruction_bytestream2byte(buffer, offset);
+ if (b_print)
+ {
+ print_type(type);
+ printf(", ");
+ }
+ S32 arg = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", arg);
+
+ if (type == LST_INTEGER)
+ {
+ S32 test = lscript_pop_int(buffer);
+ if (!test)
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_FLOATINGPOINT)
+ {
+ F32 test = lscript_pop_float(buffer);
+ if (!test)
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_VECTOR)
+ {
+ LLVector3 test;
+ lscript_pop_vector(buffer, test);
+ if (test.isExactlyZero())
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_QUATERNION)
+ {
+ LLQuaternion test;
+ lscript_pop_quaternion(buffer, test);
+ if (test.isIdentity())
+ {
+ offset += arg;
+ }
+ }
+ else if (type == LST_STRING)
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *sdata = new char[size];
+ bytestream2char(sdata, buffer, string);
+ if (!strlen(sdata))
+ {
+ offset += arg;
+ }
+ delete [] sdata;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ else if (type == LST_KEY)
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *sdata = new char[size];
+ bytestream2char(sdata, buffer, string);
+ if (strlen(sdata))
+ {
+ LLUUID id;
+ id.set(sdata);
+ if (id == LLUUID::null)
+ offset += arg;
+ }
+ else
+ {
+ offset += arg;
+ }
+ delete [] sdata;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else if (type == LST_LIST)
+ {
+ S32 address = lscript_pop_int(buffer);
+ LLScriptLibData *list = lsa_get_data(buffer, address, TRUE);
+ if (!list->getListLength())
+ {
+ offset += arg;
+ }
+ }
+ }
+ return FALSE;
+}
+
+BOOL run_state(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tSTATE ", offset);
+ offset++;
+ S32 state = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", state);
+
+ S32 bp = lscript_pop_int(buffer);
+ set_bp(buffer, bp);
+
+ offset = lscript_pop_int(buffer);
+
+ S32 major_version = 0;
+ S32 value = get_register(buffer, LREG_VN);
+ if (value == LSL2_VERSION1_END_NUMBER)
+ {
+ major_version = 1;
+ }
+ else if (value == LSL2_VERSION_NUMBER)
+ {
+ major_version = 2;
+ }
+
+ S32 current_state = get_register(buffer, LREG_CS);
+ if (state != current_state)
+ {
+ U64 ce = get_event_register(buffer, LREG_CE, major_version);
+ ce |= LSCRIPTStateBitField[LSTT_STATE_EXIT];
+ set_event_register(buffer, LREG_CE, ce, major_version);
+ }
+ set_register(buffer, LREG_NS, state);
+ return FALSE;
+}
+
+BOOL run_call(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tCALL ", offset);
+ offset++;
+ S32 func = safe_instruction_bytestream2integer(buffer, offset);
+ if (b_print)
+ printf("%d\n", func);
+
+ lscript_local_store(buffer, -8, offset);
+
+ S32 minimum = get_register(buffer, LREG_GFR);
+ S32 maximum = get_register(buffer, LREG_SR);
+ S32 lookup = minimum + func*4 + 4;
+ S32 function;
+
+ if ( (lookup >= minimum)
+ &&(lookup < maximum))
+ {
+ function = bytestream2integer(buffer, lookup) + minimum;
+ if ( (lookup >= minimum)
+ &&(lookup < maximum))
+ {
+ offset = function;
+ offset += bytestream2integer(buffer, function);
+ }
+ else
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ }
+ }
+ else
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ }
+ return FALSE;
+}
+
+BOOL run_return(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tRETURN\n", offset);
+ offset++;
+ S32 bp = lscript_pop_int(buffer);
+ set_bp(buffer, bp);
+ offset = lscript_pop_int(buffer);
+ return FALSE;
+}
+
+S32 axtoi(char *hexStg)
+{
+ S32 n = 0; // position in string
+ S32 m = 0; // position in digit[] to shift
+ S32 count; // loop index
+ S32 intValue = 0; // integer value of hex string
+ S32 digit[9]; // hold values to convert
+ while (n < 8)
+ {
+ if (hexStg[n]=='\0')
+ break;
+ if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
+ digit[n] = hexStg[n] & 0x0f; //convert to int
+ else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
+ digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
+ else break;
+ n++;
+ }
+ count = n;
+ m = n - 1;
+ n = 0;
+ while(n < count)
+ {
+ // digit[n] is value of hex digit at position n
+ // (m << 2) is the number of positions to shift
+ // OR the bits into return value
+ intValue = intValue | (digit[n] << (m << 2));
+ m--; // adjust the position to set
+ n++; // next digit to process
+ }
+ return (intValue);
+}
+
+
+BOOL run_cast(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ char caststr[1024];
+ if (b_print)
+ printf("[0x%X]\tCAST ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ U8 from = arg >> 4;
+ U8 to = arg & 0xf;
+ if (b_print)
+ {
+ print_type(from);
+ printf(", ");
+ print_type(to);
+ printf("\n");
+ }
+
+ switch(from)
+ {
+ case LST_INTEGER:
+ {
+ switch(to)
+ {
+ case LST_INTEGER:
+ break;
+ case LST_FLOATINGPOINT:
+ {
+ S32 source = lscript_pop_int(buffer);
+ F32 dest = (F32)source;
+ lscript_push(buffer, dest);
+ }
+ break;
+ case LST_STRING:
+ {
+ S32 address, source = lscript_pop_int(buffer);
+ sprintf(caststr, "%d", source);
+ address = lsa_heap_add_data(buffer, new LLScriptLibData(caststr), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ case LST_LIST:
+ {
+ S32 address, source = lscript_pop_int(buffer);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(source);
+ address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ {
+ switch(to)
+ {
+ case LST_INTEGER:
+ {
+ F32 source = lscript_pop_float(buffer);
+ S32 dest = (S32)source;
+ lscript_push(buffer, dest);
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ break;
+ case LST_STRING:
+ {
+ S32 address;
+ F32 source = lscript_pop_float(buffer);
+ sprintf(caststr, "%f", source);
+ address = lsa_heap_add_data(buffer, new LLScriptLibData(caststr), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ case LST_LIST:
+ {
+ S32 address;
+ F32 source = lscript_pop_float(buffer);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(source);
+ address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_STRING:
+ {
+ switch(to)
+ {
+ case LST_INTEGER:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *arg = new char[size];
+ bytestream2char(arg, buffer, string);
+ // S32 length = strlen(arg);
+ S32 dest;
+ S32 base;
+
+ // Check to see if this is a hexidecimal number.
+ if ( (arg[0] == '0') &&
+ (arg[1] == 'x' || arg[1] == 'X') )
+ {
+ // Let strtoul do a hex conversion.
+ base = 16;
+ }
+ else
+ {
+ // Force base-10, so octal is never used.
+ base = 10;
+ }
+
+ dest = strtoul(arg, NULL, base);
+
+ lscript_push(buffer, dest);
+ delete [] arg;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *arg = new char[size];
+ bytestream2char(arg, buffer, string);
+ F32 dest = (F32)atof(arg);
+
+
+ lscript_push(buffer, dest);
+ delete [] arg;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ break;
+ case LST_STRING:
+ break;
+ case LST_LIST:
+ {
+ S32 saddress = lscript_pop_int(buffer);
+ LLScriptLibData *string = lsa_get_data(buffer, saddress, TRUE);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = string;
+ S32 address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ case LST_VECTOR:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *arg = new char[size];
+ bytestream2char(arg, buffer, string);
+ LLVector3 vec;
+ S32 num = sscanf(arg, "<%f, %f, %f>", &vec.mV[VX], &vec.mV[VY], &vec.mV[VZ]);
+ if (num != 3)
+ {
+ vec = LLVector3::zero;
+ }
+ lscript_push(buffer, vec);
+ delete [] arg;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ break;
+ case LST_QUATERNION:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *arg = new char[size];
+ bytestream2char(arg, buffer, string);
+ LLQuaternion quat;
+ S32 num = sscanf(arg, "<%f, %f, %f, %f>", &quat.mQ[VX], &quat.mQ[VY], &quat.mQ[VZ], &quat.mQ[VW]);
+ if (num != 4)
+ {
+ quat = LLQuaternion::DEFAULT;
+
+ }
+ lscript_push(buffer, quat);
+ delete [] arg;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_KEY:
+ {
+ switch(to)
+ {
+ case LST_KEY:
+ break;
+ case LST_STRING:
+ break;
+ case LST_LIST:
+ {
+ S32 saddress = lscript_pop_int(buffer);
+ LLScriptLibData *string = lsa_get_data(buffer, saddress, TRUE);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = string;
+ S32 address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_VECTOR:
+ {
+ switch(to)
+ {
+ case LST_VECTOR:
+ break;
+ case LST_STRING:
+ {
+ S32 address;
+ LLVector3 source;
+ lscript_pop_vector(buffer, source);
+ sprintf(caststr, "<%5.5f, %5.5f, %5.5f>", source.mV[VX], source.mV[VY], source.mV[VZ]);
+ address = lsa_heap_add_data(buffer, new LLScriptLibData(caststr), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ case LST_LIST:
+ {
+ S32 address;
+ LLVector3 source;
+ lscript_pop_vector(buffer, source);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(source);
+ address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_QUATERNION:
+ {
+ switch(to)
+ {
+ case LST_QUATERNION:
+ break;
+ case LST_STRING:
+ {
+ S32 address;
+ LLQuaternion source;
+ lscript_pop_quaternion(buffer, source);
+ sprintf(caststr, "<%5.5f, %5.5f, %5.5f, %5.5f>", source.mQ[VX], source.mQ[VY], source.mQ[VZ], source.mQ[VS]);
+ address = lsa_heap_add_data(buffer, new LLScriptLibData(caststr), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ case LST_LIST:
+ {
+ S32 address;
+ LLQuaternion source;
+ lscript_pop_quaternion(buffer, source);
+ LLScriptLibData *list = new LLScriptLibData;
+ list->mType = LST_LIST;
+ list->mListp = new LLScriptLibData(source);
+ address = lsa_heap_add_data(buffer, list, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ case LST_LIST:
+ {
+ switch(to)
+ {
+ case LST_LIST:
+ break;
+ case LST_STRING:
+ {
+ S32 address = lscript_pop_int(buffer);
+ LLScriptLibData *list = lsa_get_data(buffer, address, TRUE);
+ LLScriptLibData *list_root = list;
+
+ std::ostringstream dest;
+ while (list)
+ {
+ list->print(dest, FALSE);
+ list = list->mListp;
+ }
+ delete list_root;
+ char *tmp = strdup(dest.str().c_str());
+ LLScriptLibData *string = new LLScriptLibData(tmp);
+ free(tmp);
+ tmp = NULL;
+ S32 destaddress = lsa_heap_add_data(buffer, string, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, destaddress);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+BOOL run_stacktos(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ S32 length = lscript_pop_int(buffer);
+ S32 i;
+ char *arg = new char[length];
+ S32 fault;
+ for (i = 0; i < length; i++)
+ {
+ fault = get_register(buffer, LREG_FR);
+ if (fault)
+ break;
+
+ arg[length - i - 1] = lscript_pop_char(buffer);
+ }
+ S32 address = lsa_heap_add_data(buffer, new LLScriptLibData(arg), get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ delete [] arg;
+ return FALSE;
+}
+
+void lscript_stacktol_pop_variable(LLScriptLibData *data, U8 *buffer, char type)
+{
+ S32 address, string;
+ S32 base_address;
+
+ switch(type)
+ {
+ case LST_INTEGER:
+ data->mType = LST_INTEGER;
+ data->mInteger = lscript_pop_int(buffer);
+ break;
+ case LST_FLOATINGPOINT:
+ data->mType = LST_FLOATINGPOINT;
+ data->mFP = lscript_pop_float(buffer);
+ break;
+ case LST_KEY:
+ data->mType = LST_KEY;
+
+ base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ address = base_address + get_register(buffer, LREG_HR) - 1;
+
+ if (address)
+ {
+ string = address + SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ data->mKey = new char[size];
+ bytestream2char(data->mKey, buffer, string);
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else
+ {
+ data->mKey = new char[1];
+ data->mKey[0] = 0;
+ }
+ break;
+ case LST_STRING:
+ data->mType = LST_STRING;
+
+ base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ address = base_address + get_register(buffer, LREG_HR) - 1;
+
+ if (address)
+ {
+ string = address + SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ data->mString = new char[size];
+ bytestream2char(data->mString, buffer, string);
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else
+ {
+ data->mString = new char[1];
+ data->mString[0] = 0;
+ }
+ break;
+ case LST_VECTOR:
+ data->mType = LST_VECTOR;
+ lscript_pop_vector(buffer, data->mVec);
+ break;
+ case LST_QUATERNION:
+ data->mType = LST_QUATERNION;
+ lscript_pop_quaternion(buffer, data->mQuat);
+ break;
+ case LST_LIST:
+ data->mType = LST_LIST;
+ address = lscript_pop_int(buffer);
+ data->mListp = lsa_get_data(buffer, address, TRUE);
+ break;
+ }
+}
+
+BOOL run_stacktol(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ offset++;
+ S32 length = safe_instruction_bytestream2integer(buffer, offset);
+ S32 i;
+ S32 fault;
+
+ S8 type;
+
+ LLScriptLibData *data = new LLScriptLibData, *tail;
+ data->mType = LST_LIST;
+
+ for (i = 0; i < length; i++)
+ {
+ fault = get_register(buffer, LREG_FR);
+ if (fault)
+ break;
+
+ type = lscript_pop_char(buffer);
+
+ tail = new LLScriptLibData;
+
+ lscript_stacktol_pop_variable(tail, buffer, type);
+
+ tail->mListp = data->mListp;
+ data->mListp = tail;
+ }
+ S32 address = lsa_heap_add_data(buffer,data, get_max_heap_size(buffer), TRUE);
+ lscript_push(buffer, address);
+ return FALSE;
+}
+
+BOOL run_print(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tPRINT ", offset);
+ offset++;
+ U8 type = safe_instruction_bytestream2byte(buffer, offset);
+ if (b_print)
+ {
+ print_type(type);
+ printf("\n");
+ }
+ switch(type)
+ {
+ case LST_INTEGER:
+ {
+ S32 source = lscript_pop_int(buffer);
+ printf("%d\n", source);
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ {
+ F32 source = lscript_pop_float(buffer);
+ printf("%f\n", source);
+ }
+ break;
+ case LST_STRING:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ S32 address = base_address + get_register(buffer, LREG_HR) - 1;
+
+ if (address)
+ {
+ S32 string = address;
+ string += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ char *arg = new char[size];
+ bytestream2char(arg, buffer, string);
+ printf("%s\n", arg);
+ delete [] arg;
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ }
+ break;
+ case LST_VECTOR:
+ {
+ LLVector3 source;
+ lscript_pop_vector(buffer, source);
+ printf("< %f, %f, %f >\n", source.mV[VX], source.mV[VY], source.mV[VZ]);
+ }
+ break;
+ case LST_QUATERNION:
+ {
+ LLQuaternion source;
+ lscript_pop_quaternion(buffer, source);
+ printf("< %f, %f, %f, %f >\n", source.mQ[VX], source.mQ[VY], source.mQ[VZ], source.mQ[VS]);
+ }
+ break;
+ case LST_LIST:
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ LLScriptLibData *data = lsa_get_data(buffer, base_address, TRUE);
+ LLScriptLibData *print = data;
+
+ printf("list\n");
+
+ while (print)
+ {
+ switch(print->mType)
+ {
+ case LST_INTEGER:
+ {
+ printf("%d\n", print->mInteger);
+ }
+ break;
+ case LST_FLOATINGPOINT:
+ {
+ printf("%f\n", print->mFP);
+ }
+ break;
+ case LST_STRING:
+ {
+ printf("%s\n", print->mString);
+ }
+ break;
+ case LST_KEY:
+ {
+ printf("%s\n", print->mKey);
+ }
+ break;
+ case LST_VECTOR:
+ {
+ printf("< %f, %f, %f >\n", print->mVec.mV[VX], print->mVec.mV[VY], print->mVec.mV[VZ]);
+ }
+ break;
+ case LST_QUATERNION:
+ {
+ printf("< %f, %f, %f, %f >\n", print->mQuat.mQ[VX], print->mQuat.mQ[VY], print->mQuat.mQ[VZ], print->mQuat.mQ[VS]);
+ }
+ break;
+ default:
+ break;
+ }
+ print = print->mListp;
+ }
+ delete data;
+ }
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+
+void lscript_run(char *filename, BOOL b_debug)
+{
+ LLTimer timer;
+ char *error;
+ BOOL b_state;
+ LLScriptExecute *execute = NULL;
+ FILE *file = LLFile::fopen(filename, "r");
+ if (file)
+ {
+ execute = new LLScriptExecute(file);
+ fclose(file);
+ }
+ file = LLFile::fopen(filename, "r");
+ if (file)
+ {
+ FILE *fp = LLFile::fopen("lscript.parse", "w");
+ LLScriptLSOParse *parse = new LLScriptLSOParse(file);
+ parse->printData(fp);
+ fclose(file);
+ fclose(fp);
+ }
+ file = LLFile::fopen(filename, "r");
+ if (file && execute)
+ {
+ timer.reset();
+ while (!execute->run(b_debug, LLUUID::null, &error, b_state))
+ ;
+ F32 time = timer.getElapsedTimeF32();
+ F32 ips = execute->mInstructionCount / time;
+ llinfos << execute->mInstructionCount << " instructions in " << time << " seconds" << llendl;
+ llinfos << ips/1000 << "K instructions per second" << llendl;
+ printf("ip: 0x%X\n", get_register(execute->mBuffer, LREG_IP));
+ printf("sp: 0x%X\n", get_register(execute->mBuffer, LREG_SP));
+ printf("bp: 0x%X\n", get_register(execute->mBuffer, LREG_BP));
+ printf("hr: 0x%X\n", get_register(execute->mBuffer, LREG_HR));
+ printf("hp: 0x%X\n", get_register(execute->mBuffer, LREG_HP));
+ delete execute;
+ fclose(file);
+ }
+}
+
+void lscript_pop_variable(LLScriptLibData *data, U8 *buffer, char type)
+{
+ S32 address, string;
+ S32 base_address;
+
+ switch(type)
+ {
+ case 'i':
+ data->mType = LST_INTEGER;
+ data->mInteger = lscript_pop_int(buffer);
+ break;
+ case 'f':
+ data->mType = LST_FLOATINGPOINT;
+ data->mFP = lscript_pop_float(buffer);
+ break;
+ case 'k':
+ data->mType = LST_KEY;
+
+ base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ address = base_address + get_register(buffer, LREG_HR) - 1;
+
+ if (address)
+ {
+ string = address + SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ data->mKey = new char[size];
+ bytestream2char(data->mKey, buffer, string);
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else
+ {
+ data->mKey = new char[1];
+ data->mKey[0] = 0;
+ }
+ break;
+ case 's':
+ data->mType = LST_STRING;
+
+ base_address = lscript_pop_int(buffer);
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ address = base_address + get_register(buffer, LREG_HR) - 1;
+
+ if (address)
+ {
+ string = address + SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (safe_heap_check_address(buffer, string, 1))
+ {
+ S32 toffset = string;
+ safe_heap_bytestream_count_char(buffer, toffset);
+ S32 size = toffset - string;
+ data->mString = new char[size];
+ bytestream2char(data->mString, buffer, string);
+ }
+ lsa_decrease_ref_count(buffer, base_address);
+ }
+ else
+ {
+ data->mString = new char[1];
+ data->mString[0] = 0;
+ }
+ break;
+ case 'l':
+ {
+ S32 base_address = lscript_pop_int(buffer);
+ data->mType = LST_LIST;
+ data->mListp = lsa_get_list_ptr(buffer, base_address, TRUE);
+ }
+ break;
+ case 'v':
+ data->mType = LST_VECTOR;
+ lscript_pop_vector(buffer, data->mVec);
+ break;
+ case 'q':
+ data->mType = LST_QUATERNION;
+ lscript_pop_quaternion(buffer, data->mQuat);
+ break;
+ }
+}
+
+void lscript_push_return_variable(LLScriptLibData *data, U8 *buffer)
+{
+ S32 address;
+ switch(data->mType)
+ {
+ case LST_INTEGER:
+ lscript_local_store(buffer, -12, data->mInteger);
+ break;
+ case LST_FLOATINGPOINT:
+ lscript_local_store(buffer, -12, data->mFP);
+ break;
+ case LST_KEY:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_local_store(buffer, -12, address);
+ break;
+ case LST_STRING:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_local_store(buffer, -12, address);
+ break;
+ case LST_LIST:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_local_store(buffer, -12, address);
+ break;
+ case LST_VECTOR:
+ lscript_local_store(buffer, -20, data->mVec);
+ break;
+ case LST_QUATERNION:
+ lscript_local_store(buffer, -24, data->mQuat);
+ break;
+ default:
+ break;
+ }
+}
+
+S32 lscript_push_variable(LLScriptLibData *data, U8 *buffer)
+{
+ S32 address;
+ switch(data->mType)
+ {
+ case LST_INTEGER:
+ lscript_push(buffer, data->mInteger);
+ break;
+ case LST_FLOATINGPOINT:
+ lscript_push(buffer, data->mFP);
+ return 4;
+ break;
+ case LST_KEY:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_push(buffer, address);
+ return 4;
+ break;
+ case LST_STRING:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_push(buffer, address);
+ return 4;
+ break;
+ case LST_LIST:
+ address = lsa_heap_add_data(buffer, data, get_max_heap_size(buffer), FALSE);
+ lscript_push(buffer, address);
+ return 4;
+ break;
+ case LST_VECTOR:
+ lscript_push(buffer, data->mVec);
+ return 12;
+ break;
+ case LST_QUATERNION:
+ lscript_push(buffer, data->mQuat);
+ return 16;
+ break;
+ default:
+ break;
+ }
+ return 4;
+}
+
+BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tCALLLIB ", offset);
+ offset++;
+ U8 arg = safe_instruction_bytestream2byte(buffer, offset);
+ if (arg >= gScriptLibrary.mNextNumber)
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ if (b_print)
+ printf("%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg]->mName);
+
+ // pull out the arguments and the return values
+ LLScriptLibData *arguments = NULL;
+ LLScriptLibData *returnvalue = NULL;
+
+ S32 i, number;
+
+ if (gScriptLibrary.mFunctions[arg]->mReturnType)
+ {
+ returnvalue = new LLScriptLibData;
+ }
+
+ if (gScriptLibrary.mFunctions[arg]->mArgs)
+ {
+ number = (S32)strlen(gScriptLibrary.mFunctions[arg]->mArgs);
+ arguments = new LLScriptLibData[number];
+ }
+ else
+ {
+ number = 0;
+ }
+
+ for (i = number - 1; i >= 0; i--)
+ {
+ lscript_pop_variable(&arguments[i], buffer, gScriptLibrary.mFunctions[arg]->mArgs[i]);
+ }
+
+ if (b_print)
+ {
+ printf("%s\n", gScriptLibrary.mFunctions[arg]->mDesc);
+ }
+
+ {
+ // LLFastTimer time_in_libraries1(LLFastTimer::FTM_TEMP7);
+ gScriptLibrary.mFunctions[arg]->mExecFunc(returnvalue, arguments, id);
+ }
+ add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg]->mEnergyUse);
+ add_register_fp(buffer, LREG_SLR, gScriptLibrary.mFunctions[arg]->mSleepTime);
+
+ if (returnvalue)
+ {
+ returnvalue->mType = char2type(*gScriptLibrary.mFunctions[arg]->mReturnType);
+ lscript_push_return_variable(returnvalue, buffer);
+ }
+
+ delete [] arguments;
+ delete returnvalue;
+
+ // reset the BP after calling the library files
+ S32 bp = lscript_pop_int(buffer);
+ set_bp(buffer, bp);
+
+ // pop off the spot for the instruction pointer
+ lscript_poparg(buffer, 4);
+ return FALSE;
+}
+
+
+BOOL run_calllib_two_byte(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id)
+{
+ if (b_print)
+ printf("[0x%X]\tCALLLIB ", offset);
+ offset++;
+ U16 arg = safe_instruction_bytestream2u16(buffer, offset);
+ if (arg >= gScriptLibrary.mNextNumber)
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return FALSE;
+ }
+ if (b_print)
+ printf("%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg]->mName);
+
+ // pull out the arguments and the return values
+ LLScriptLibData *arguments = NULL;
+ LLScriptLibData *returnvalue = NULL;
+
+ S32 i, number;
+
+ if (gScriptLibrary.mFunctions[arg]->mReturnType)
+ {
+ returnvalue = new LLScriptLibData;
+ }
+
+ if (gScriptLibrary.mFunctions[arg]->mArgs)
+ {
+ number = (S32)strlen(gScriptLibrary.mFunctions[arg]->mArgs);
+ arguments = new LLScriptLibData[number];
+ }
+ else
+ {
+ number = 0;
+ }
+
+ for (i = number - 1; i >= 0; i--)
+ {
+ lscript_pop_variable(&arguments[i], buffer, gScriptLibrary.mFunctions[arg]->mArgs[i]);
+ }
+
+ if (b_print)
+ {
+ printf("%s\n", gScriptLibrary.mFunctions[arg]->mDesc);
+ }
+
+ {
+ // LLFastTimer time_in_libraries2(LLFastTimer::FTM_TEMP8);
+ gScriptLibrary.mFunctions[arg]->mExecFunc(returnvalue, arguments, id);
+ }
+ add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg]->mEnergyUse);
+ add_register_fp(buffer, LREG_SLR, gScriptLibrary.mFunctions[arg]->mSleepTime);
+
+ if (returnvalue)
+ {
+ returnvalue->mType = char2type(*gScriptLibrary.mFunctions[arg]->mReturnType);
+ lscript_push_return_variable(returnvalue, buffer);
+ }
+
+ delete [] arguments;
+ delete returnvalue;
+
+ // reset the BP after calling the library files
+ S32 bp = lscript_pop_int(buffer);
+ set_bp(buffer, bp);
+
+ // pop off the spot for the instruction pointer
+ lscript_poparg(buffer, 4);
+ return FALSE;
+}
diff --git a/indra/lscript/lscript_execute/lscript_heapruntime.cpp b/indra/lscript/lscript_execute/lscript_heapruntime.cpp
new file mode 100644
index 0000000000..11bad19797
--- /dev/null
+++ b/indra/lscript/lscript_execute/lscript_heapruntime.cpp
@@ -0,0 +1,501 @@
+/**
+ * @file lscript_heapruntime.cpp
+ * @brief classes to manage script heap at runtime
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if 0
+
+#include "linden_common.h"
+
+#include "lscript_heapruntime.h"
+#include "lscript_execute.h"
+
+
+/*
+ String Heap Format
+ Byte Description
+ 0x0 - 0xnn Single byte string including null terminator
+
+ List Heap Format
+ Byte Description
+ 0x0 Next Entry Type
+ 0: End of list
+ 1: Integer
+ 2: Floating point
+ 3: String
+ 4: Vector
+ 5: Quaternion
+ 6: List
+ 0x1 - 0x4 Integer, Floating Point, String Address, List Address
+ or
+ 0x1 - 0xd Vector
+ or
+ 0x1 - 0x11 Quaternion
+ . . .
+
+ Heap Block Format
+ Byte Description
+ 0x0 - 0x3 Offset to Next Block
+ 0x4 - 0x7 Object Reference Count
+ 0x8 Block Type
+ 0: Empty
+ 3: String
+ 6: List
+ 0x9 - 0xM Object Data
+
+ Heap Management
+
+ Adding Data
+
+ 1) Set last empty spot to zero.
+ 2) Go to start of the heap (HR).
+ 3) Get next 4 bytes of offset.
+ 4) If zero, we've reached the end of used memory. If empty spot is zero go to step 9. Otherwise set base offset to 0 and go to step 9.
+ 5) Skip 4 bytes.
+ 6) Get next 1 byte of entry type.
+ 7) If zero, this spot is empty. If empty spot is zero, set empty spot to this address and go to step 9. Otherwise, coalesce with last empty spot and then go to step 9.
+ 8) Skip forward by offset and go to step 3.
+ 9) If the spot is empty, check to see if the size needed == offset - 9.
+ 10) If it does, let's drop our data into this spot. Set reference count to 1. Set entry type appropriately and copy the data in.
+ 11) If size needed < offset - 9 then we can stick in data and add in an empty block.
+ 12) Otherwise, we need to keep looking. Go to step 3.
+
+ Increasing reference counts
+
+ Decreasing reference counts
+ 1) Set entry type to 0.
+ 2) If offset is non-zero and the next entry is empty, coalesce. Go to step 2.
+
+ What increases reference count:
+ Initial creation sets reference count to 1.
+ Storing the reference increases reference count by 1.
+ Pushing the reference increases reference count by 1.
+ Duplicating the reference increases reference count by 1.
+
+ What decreases the reference count:
+ Popping the reference decreases reference count by 1.
+ */
+
+
+LLScriptHeapRunTime::LLScriptHeapRunTime()
+: mLastEmpty(0), mBuffer(NULL), mCurrentPosition(0), mStackPointer(0), mHeapRegister(0), mbPrint(FALSE)
+{
+}
+
+LLScriptHeapRunTime::~LLScriptHeapRunTime()
+{
+}
+
+S32 LLScriptHeapRunTime::addData(char *string)
+{
+ if (!mBuffer)
+ return 0;
+
+ S32 size = strlen(string) + 1;
+ S32 block_offset = findOpenBlock(size + HEAP_BLOCK_HEADER_SIZE);
+
+ if (mCurrentPosition)
+ {
+ S32 offset = mCurrentPosition;
+ if (offset + block_offset + HEAP_BLOCK_HEADER_SIZE + LSCRIPTDataSize[LST_INTEGER] >= mStackPointer)
+ {
+ set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION);
+ return 0;
+ }
+ // cool, we've found a spot!
+ // set offset
+ integer2bytestream(mBuffer, offset, block_offset);
+ // set reference count
+ integer2bytestream(mBuffer, offset, 1);
+ // set type
+ *(mBuffer + offset++) = LSCRIPTTypeByte[LST_STRING];
+ // plug in data
+ char2bytestream(mBuffer, offset, string);
+ if (mbPrint)
+ printf("0x%X created ref count %d\n", mCurrentPosition - mHeapRegister, 1);
+
+ // now, zero out next offset to prevent "trouble"
+ // offset = mCurrentPosition + size + HEAP_BLOCK_HEADER_SIZE;
+ // integer2bytestream(mBuffer, offset, 0);
+ }
+ return mCurrentPosition;
+}
+
+S32 LLScriptHeapRunTime::addData(U8 *list)
+{
+ if (!mBuffer)
+ return 0;
+ return 0;
+}
+
+S32 LLScriptHeapRunTime::catStrings(S32 address1, S32 address2)
+{
+ if (!mBuffer)
+ return 0;
+
+ S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
+ S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
+
+ S32 toffset1 = dataaddress1;
+ safe_heap_bytestream_count_char(mBuffer, toffset1);
+
+ S32 toffset2 = dataaddress2;
+ safe_heap_bytestream_count_char(mBuffer, toffset2);
+
+ // calculate new string size
+ S32 size1 = toffset1 - dataaddress1;
+ S32 size2 = toffset2 - dataaddress2;
+ S32 newbuffer = size1 + size2 - 1;
+
+ char *temp = new char[newbuffer];
+
+ // get the strings
+ bytestream2char(temp, mBuffer, dataaddress1);
+ bytestream2char(temp + size1 - 1, mBuffer, dataaddress2);
+
+ decreaseRefCount(address1);
+ decreaseRefCount(address2);
+
+ S32 retaddress = addData(temp);
+
+ return retaddress;
+}
+
+S32 LLScriptHeapRunTime::cmpStrings(S32 address1, S32 address2)
+{
+ if (!mBuffer)
+ return 0;
+
+ S32 dataaddress1 = address1 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
+ S32 dataaddress2 = address2 + 2*LSCRIPTDataSize[LST_INTEGER] + 1;
+
+ S32 toffset1 = dataaddress1;
+ safe_heap_bytestream_count_char(mBuffer, toffset1);
+
+ S32 toffset2 = dataaddress2;
+ safe_heap_bytestream_count_char(mBuffer, toffset2);
+
+ // calculate new string size
+ S32 size1 = toffset1 - dataaddress1;
+ S32 size2 = toffset2 - dataaddress2;
+
+ if (size1 != size2)
+ {
+ return llmin(size1, size2);
+ }
+ else
+ {
+ return strncmp((char *)(mBuffer + dataaddress1), (char *)(mBuffer + dataaddress2), size1);
+ }
+}
+
+void LLScriptHeapRunTime::removeData(S32 address)
+{
+ if (!mBuffer)
+ return;
+
+ S32 toffset = address;
+ // read past offset (relying on function side effect
+ bytestream2integer(mBuffer, toffset);
+
+ // make sure that reference count is 0
+ integer2bytestream(mBuffer, toffset, 0);
+ // show the block as empty
+ *(mBuffer + toffset) = 0;
+
+ // now, clean up the heap
+ S32 clean = mHeapRegister;
+ S32 tclean;
+ S32 clean_offset;
+
+ S32 nclean;
+ S32 tnclean;
+ S32 next_offset;
+
+ U8 type;
+ U8 ntype;
+
+ for(;;)
+ {
+ tclean = clean;
+ clean_offset = bytestream2integer(mBuffer, tclean);
+ // is this block, empty?
+ tclean += LSCRIPTDataSize[LST_INTEGER];
+ type = *(mBuffer + tclean);
+
+ if (!clean_offset)
+ {
+ if (!type)
+ {
+ // we're done! if our block is empty, we can pull in the HP and zero out our offset
+ set_register(mBuffer, LREG_HP, clean);
+ }
+ return;
+ }
+
+
+ if (!type)
+ {
+ // if we're empty, try to coalesce with the next one
+ nclean = clean + clean_offset;
+ tnclean = nclean;
+ next_offset = bytestream2integer(mBuffer, tnclean);
+ tnclean += LSCRIPTDataSize[LST_INTEGER];
+ ntype = *(mBuffer + tnclean);
+
+ if (!next_offset)
+ {
+ // we're done! if our block is empty, we can pull in the HP and zero out our offset
+ tclean = clean;
+ integer2bytestream(mBuffer, tclean, 0);
+ set_register(mBuffer, LREG_HP, clean);
+ return;
+ }
+
+ if (!ntype)
+ {
+ // hooray! we can coalesce
+ tclean = clean;
+ integer2bytestream(mBuffer, tclean, clean_offset + next_offset);
+ // don't skip forward so that we can keep coalescing on next pass through the loop
+ }
+ else
+ {
+ clean += clean_offset;
+ }
+ }
+ else
+ {
+ // if not, move on to the next block
+ clean += clean_offset;
+ }
+ }
+}
+
+void LLScriptHeapRunTime::coalesce(S32 address1, S32 address2)
+{
+ // we need to bump the base offset by the second block's
+ S32 toffset = address1;
+ S32 offset1 = bytestream2integer(mBuffer, toffset);
+ offset1 += bytestream2integer(mBuffer, address2);
+
+ integer2bytestream(mBuffer, address1, offset1);
+}
+
+void LLScriptHeapRunTime::split(S32 address1, S32 size)
+{
+ S32 toffset = address1;
+ S32 oldoffset = bytestream2integer(mBuffer, toffset);
+
+ // add new offset and zero out reference count and block used
+ S32 newoffset = oldoffset - size;
+ S32 newblockpos = address1 + size;
+
+ // set new offset
+ integer2bytestream(mBuffer, newblockpos, newoffset);
+ // zero out reference count
+ integer2bytestream(mBuffer, newblockpos, 0);
+ // mark as empty
+ *(mBuffer + newblockpos) = 0;
+
+ // now, change the offset of the original block
+ integer2bytestream(mBuffer, address1, size + HEAP_BLOCK_HEADER_SIZE);
+}
+
+/*
+
+ For reference count changes, strings are easy. For lists, we'll need to go through the lists reducing
+ the reference counts for any included strings and lists
+
+ */
+
+void LLScriptHeapRunTime::increaseRefCount(S32 address)
+{
+ if (!mBuffer)
+ return;
+
+ if (!address)
+ {
+ // unused temp string entry
+ return;
+ }
+
+ // get current reference count
+ S32 toffset = address + 4;
+ S32 count = bytestream2integer(mBuffer, toffset);
+
+ count++;
+
+ if (mbPrint)
+ printf("0x%X inc ref count %d\n", address - mHeapRegister, count);
+
+ // see which type it is
+ U8 type = *(mBuffer + toffset);
+
+ if (type == LSCRIPTTypeByte[LST_STRING])
+ {
+ toffset = address + 4;
+ integer2bytestream(mBuffer, toffset, count);
+ }
+ // TO DO: put list stuff here!
+ else
+ {
+ set_fault(mBuffer, LSRF_HEAP_ERROR);
+ }
+}
+
+void LLScriptHeapRunTime::decreaseRefCount(S32 address)
+{
+ if (!mBuffer)
+ return;
+
+ if (!address)
+ {
+ // unused temp string entry
+ return;
+ }
+
+ // get offset
+ S32 toffset = address;
+ // read past offset (rely on function side effect)
+ bytestream2integer(mBuffer, toffset);
+
+ // get current reference count
+ S32 count = bytestream2integer(mBuffer, toffset);
+
+ // see which type it is
+ U8 type = *(mBuffer + toffset);
+
+ if (type == LSCRIPTTypeByte[LST_STRING])
+ {
+ count--;
+
+ if (mbPrint)
+ printf("0x%X dec ref count %d\n", address - mHeapRegister, count);
+
+ toffset = address + 4;
+ integer2bytestream(mBuffer, toffset, count);
+ if (!count)
+ {
+ // we can blow this one away
+ removeData(address);
+ }
+ }
+ // TO DO: put list stuff here!
+ else
+ {
+ set_fault(mBuffer, LSRF_HEAP_ERROR);
+ }
+}
+
+// if we're going to assign a variable, we need to decrement the reference count of what we were pointing at (if anything)
+void LLScriptHeapRunTime::releaseLocal(S32 address)
+{
+ S32 hr = get_register(mBuffer, LREG_HR);
+ address = lscript_local_get(mBuffer, address);
+ if ( (address >= hr)
+ &&(address < hr + get_register(mBuffer, LREG_HP)))
+ {
+ decreaseRefCount(address);
+ }
+}
+
+void LLScriptHeapRunTime::releaseGlobal(S32 address)
+{
+ // NOTA BENE: Global strings are referenced relative to the HR while local strings aren't
+ S32 hr = get_register(mBuffer, LREG_HR);
+ address = lscript_global_get(mBuffer, address) + hr;
+ if ( (address >= hr)
+ &&(address < hr + get_register(mBuffer, LREG_HP)))
+ {
+ decreaseRefCount(address);
+ }
+}
+
+
+// we know the following function has "unreachable code"
+// don't remind us every friggin' time we compile. . .
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4702) // unreachable code
+#endif
+
+S32 LLScriptHeapRunTime::findOpenBlock(S32 size)
+{
+ S32 offset;
+ S32 toffset;
+ U8 blocktype;
+
+ while(1)
+ {
+ if (mCurrentPosition + size >= mStackPointer)
+ {
+ set_fault(mBuffer, LSRF_STACK_HEAP_COLLISION);
+ mCurrentPosition = 0;
+ }
+
+ toffset = mCurrentPosition;
+ offset = bytestream2integer(mBuffer, toffset);
+ if (!offset)
+ {
+ // we've reached the end of Heap, return this location if we'll fit
+ // do we need to coalesce with last empty space?
+ if (mLastEmpty)
+ {
+ // ok, that everything from mLastEmpty to us is empty, so we don't need a block
+ // zero out the last empty's offset and return it
+ mCurrentPosition = mLastEmpty;
+ integer2bytestream(mBuffer, mLastEmpty, 0);
+ mLastEmpty = 0;
+ }
+ // now, zero out next offset to prevent "trouble"
+ offset = mCurrentPosition + size;
+ integer2bytestream(mBuffer, offset, 0);
+
+ // set HP to appropriate value
+ set_register(mBuffer, LREG_HP, mCurrentPosition + size);
+ return size;
+ }
+
+ // ok, is this slot empty?
+ toffset += LSCRIPTDataSize[LST_INTEGER];
+
+ blocktype = *(mBuffer + toffset++);
+
+ if (!blocktype)
+ {
+ // Empty block, do we need to coalesce?
+ if (mLastEmpty)
+ {
+ coalesce(mLastEmpty, mCurrentPosition);
+ mCurrentPosition = mLastEmpty;
+ toffset = mCurrentPosition;
+ offset = bytestream2integer(mBuffer, toffset);
+ }
+
+ // do we fit in this block?
+ if (offset >= size)
+ {
+ // do we need to split the block? (only split if splitting will leave > HEAP_BLOCK_SPLIT_THRESHOLD bytes of free space)
+ if (offset - HEAP_BLOCK_SPLIT_THRESHOLD >= size)
+ {
+ split(mCurrentPosition, size);
+ return size;
+ }
+ else
+ return offset;
+ }
+ }
+ // nothing found, keep looking
+ mCurrentPosition += offset;
+ }
+ // fake return to prevent warnings
+ mCurrentPosition = 0;
+ return 0;
+}
+
+LLScriptHeapRunTime gRunTime;
+#endif
diff --git a/indra/lscript/lscript_execute/lscript_heapruntime.h b/indra/lscript/lscript_execute/lscript_heapruntime.h
new file mode 100644
index 0000000000..d20ac87047
--- /dev/null
+++ b/indra/lscript/lscript_execute/lscript_heapruntime.h
@@ -0,0 +1,22 @@
+/**
+ * @file lscript_heapruntime.h
+ * @brief classes to manage script heap at runtime
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#if 0
+
+#ifndef LL_LSCRIPT_HEAPRUNTIME_H
+#define LL_LSCRIPT_HEAPRUNTIME_H
+
+#include "lscript_byteconvert.h"
+
+
+const S32 HEAP_BLOCK_HEADER_SIZE = 9;
+const S32 HEAP_BLOCK_SPLIT_THRESHOLD = 16;
+
+
+#endif
+#endif
diff --git a/indra/lscript/lscript_execute/lscript_readlso.cpp b/indra/lscript/lscript_execute/lscript_readlso.cpp
new file mode 100644
index 0000000000..2219232a3e
--- /dev/null
+++ b/indra/lscript/lscript_execute/lscript_readlso.cpp
@@ -0,0 +1,1553 @@
+/**
+ * @file lscript_readlso.cpp
+ * @brief classes to read lso file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include "lscript_readlso.h"
+#include "lscript_library.h"
+#include "lscript_alloc.h"
+
+LLScriptLSOParse::LLScriptLSOParse(FILE *fp)
+{
+ U8 sizearray[4];
+ S32 filesize;
+ S32 pos = 0;
+ fread(&sizearray, 1, 4, fp);
+ filesize = bytestream2integer(sizearray, pos);
+ mRawData = new U8[filesize];
+ fseek(fp, 0, SEEK_SET);
+ fread(mRawData, 1, filesize, fp);
+ fclose(fp);
+
+ initOpCodePrinting();
+}
+
+LLScriptLSOParse::LLScriptLSOParse(U8 *buffer)
+{
+ mRawData = buffer;
+ initOpCodePrinting();
+}
+
+LLScriptLSOParse::~LLScriptLSOParse()
+{
+ delete [] mRawData;
+}
+
+void LLScriptLSOParse::printData(FILE *fp)
+{
+
+
+
+ printNameDesc(fp);
+
+ printRegisters(fp);
+
+ printGlobals(fp);
+
+ printGlobalFunctions(fp);
+
+ printStates(fp);
+
+ printHeap(fp);
+}
+
+void LLScriptLSOParse::printNameDesc(FILE *fp)
+{
+ fprintf(fp, "=============================\n\n");
+}
+
+S32 gMajorVersion = 0;
+
+void LLScriptLSOParse::printRegisters(FILE *fp)
+{
+ // print out registers first
+ S32 i;
+
+ fprintf(fp, "=============================\n");
+ fprintf(fp, "Registers\n");
+ fprintf(fp, "=============================\n");
+ S32 version = get_register(mRawData, LREG_VN);
+ if (version == LSL2_VERSION1_END_NUMBER)
+ {
+ gMajorVersion = LSL2_MAJOR_VERSION_ONE;
+ }
+ else if (version == LSL2_VERSION_NUMBER)
+ {
+ gMajorVersion = LSL2_MAJOR_VERSION_TWO;
+ }
+ for (i = LREG_IP; i < LREG_EOF; i++)
+ {
+ if (i < LREG_NCE)
+ {
+ fprintf(fp, "%s: 0x%X\n", gLSCRIPTRegisterNames[i], get_register(mRawData, (LSCRIPTRegisters)i));
+ }
+ else if (gMajorVersion == LSL2_MAJOR_VERSION_TWO)
+ {
+ U64 data = get_register_u64(mRawData, (LSCRIPTRegisters)i);
+ fprintf(fp, "%s: 0x%X%X\n", gLSCRIPTRegisterNames[i], (U32)(data>>32), (U32)(data && 0xFFFFFFFF));
+ }
+ }
+ fprintf(fp, "=============================\n\n");
+}
+
+void LLScriptLSOParse::printGlobals(FILE *fp)
+{
+ // print out registers first
+ S32 offset, varoffset;
+ S32 ivalue;
+ F32 fpvalue;
+ LLVector3 vvalue;
+ LLQuaternion qvalue;
+ char name[256];
+ U8 type;
+
+ S32 global_v_offset = get_register(mRawData, LREG_GVR);
+ S32 global_f_offset = get_register(mRawData, LREG_GFR);
+
+ fprintf(fp, "=============================\n");
+ fprintf(fp, "[0x%X] Global Variables\n", global_v_offset);
+ fprintf(fp, "=============================\n");
+
+
+ while (global_v_offset < global_f_offset)
+ {
+
+ // get offset to skip past name
+ varoffset = global_v_offset;
+ offset = bytestream2integer(mRawData, global_v_offset);
+
+ // get typeexport
+ type = *(mRawData + global_v_offset++);
+
+ // set name
+ bytestream2char(name, mRawData, global_v_offset);
+
+ switch(type)
+ {
+ case LST_INTEGER:
+ ivalue = bytestream2integer(mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] integer %s = %d\n", varoffset, name, ivalue);
+ break;
+ case LST_FLOATINGPOINT:
+ fpvalue = bytestream2float(mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] integer %s = %f\n", varoffset, name, fpvalue);
+ break;
+ case LST_STRING:
+ ivalue = bytestream2integer(mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] string %s = 0x%X\n", varoffset, name, ivalue + get_register(mRawData, LREG_HR) - 1);
+ break;
+ case LST_KEY:
+ ivalue = bytestream2integer(mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] key %s = 0x%X\n", varoffset, name, ivalue + get_register(mRawData, LREG_HR) - 1);
+ break;
+ case LST_VECTOR:
+ bytestream2vector(vvalue, mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] vector %s = < %f, %f, %f >\n", varoffset, name, vvalue.mV[VX], vvalue.mV[VY], vvalue.mV[VZ]);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(qvalue, mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] quaternion %s = < %f, %f, %f, %f >\n", varoffset, name, qvalue.mQ[VX], qvalue.mQ[VY], qvalue.mQ[VZ], qvalue.mQ[VS]);
+ break;
+ case LST_LIST:
+ ivalue = bytestream2integer(mRawData, global_v_offset);
+ fprintf(fp, "[0x%X] list %s = 0x%X\n", varoffset, name, ivalue + get_register(mRawData, LREG_HR) - 1);
+ break;
+ default:
+ break;
+ }
+ }
+
+ fprintf(fp, "=============================\n\n");
+}
+
+void LLScriptLSOParse::printGlobalFunctions(FILE *fp)
+{
+ // print out registers first
+ S32 i, offset;
+// LLVector3 vvalue; unused
+// LLQuaternion qvalue; unused
+ char name[256];
+ U8 type;
+
+ offset = get_register(mRawData, LREG_GFR);
+ S32 start_of_state = get_register(mRawData, LREG_SR);
+ if (start_of_state == offset)
+ return;
+
+ S32 global_f_offset = get_register(mRawData, LREG_GFR);
+
+ fprintf(fp, "=============================\n");
+ fprintf(fp, "[0x%X] Global Functions\n", global_f_offset);
+ fprintf(fp, "=============================\n");
+
+
+ S32 num_functions = bytestream2integer(mRawData, offset);
+ S32 orig_function_offset;
+ S32 function_offset;
+ S32 next_function_offset = 0;
+ S32 function_number = 0;
+ S32 opcode_start;
+ S32 opcode_end;
+
+ for (i = 0; i < num_functions; i++)
+ {
+ // jump to function
+ // if this is the first function
+ if (i == 0)
+ {
+ if (i < num_functions - 1)
+ {
+ function_offset = bytestream2integer(mRawData, offset);
+ next_function_offset = bytestream2integer(mRawData, offset);
+ function_offset += global_f_offset;
+ opcode_end = next_function_offset + global_f_offset;
+ }
+ else
+ {
+ function_offset = bytestream2integer(mRawData, offset);
+ function_offset += global_f_offset;
+ opcode_end = get_register(mRawData, LREG_SR);
+ }
+ }
+ else if (i < num_functions - 1)
+ {
+ function_offset = next_function_offset;
+ next_function_offset = bytestream2integer(mRawData, offset);
+ function_offset += global_f_offset;
+ opcode_end = next_function_offset + global_f_offset;
+ }
+ else
+ {
+ function_offset = next_function_offset;
+ function_offset += global_f_offset;
+ opcode_end = get_register(mRawData, LREG_SR);
+ }
+ orig_function_offset = function_offset;
+ // where do the opcodes start
+ opcode_start = bytestream2integer(mRawData, function_offset);
+ opcode_start += orig_function_offset;
+ bytestream2char(name, mRawData, function_offset);
+ // get return type
+ type = *(mRawData + function_offset++);
+ fprintf(fp, "[Function #%d] [0x%X] %s\n", function_number, orig_function_offset, name);
+ fprintf(fp, "\tReturn Type: %s\n", LSCRIPTTypeNames[type]);
+ type = *(mRawData + function_offset++);
+ S32 params;
+ params = 0;
+ S32 pcount = 0;
+ while (type)
+ {
+ bytestream2char(name, mRawData, function_offset);
+ fprintf(fp, "\tParameter #%d: %s %s\n", pcount++, LSCRIPTTypeNames[type], name);
+ type = *(mRawData + function_offset++);
+ }
+ fprintf(fp, "\t\tOpCodes: 0x%X - 0x%X\n", opcode_start, opcode_end);
+ printOpCodeRange(fp, opcode_start, opcode_end, 2);
+ function_number++;
+ }
+
+ fprintf(fp, "=============================\n\n");
+}
+
+void LLScriptLSOParse::printStates(FILE *fp)
+{
+ // print out registers first
+ S32 i, offset;
+ U32 j, k;
+// LLVector3 vvalue; unused
+// LLQuaternion qvalue; unused
+ char name[256];
+
+ S32 state_offset = get_register(mRawData, LREG_SR);
+
+ fprintf(fp, "=============================\n");
+ fprintf(fp, "[0x%X] States\n", state_offset);
+ fprintf(fp, "=============================\n");
+
+ offset = state_offset;
+ S32 num_states = bytestream2integer(mRawData, offset);
+ S32 state_info_offset;
+ S32 event_jump_table;
+ U64 event_handlers;
+ S32 event_offset;
+ S32 original_event_offset;
+ S32 opcode_start;
+ S32 worst_case_opcode_end;
+ S32 opcode_end;
+ S32 stack_size;
+ S32 read_ahead;
+ S32 first_jump = 0;
+
+ for (i = 0; i < num_states; i++)
+ {
+ state_info_offset = bytestream2integer(mRawData, offset);
+ if (gMajorVersion == LSL2_MAJOR_VERSION_TWO)
+ event_handlers = bytestream2u64(mRawData, offset);
+ else
+ event_handlers = bytestream2integer(mRawData, offset);
+ if (!first_jump)
+ {
+ first_jump = state_info_offset;
+ }
+ read_ahead = offset;
+ if (offset < first_jump + state_offset)
+ {
+ worst_case_opcode_end = bytestream2integer(mRawData, read_ahead) + state_offset;
+ }
+ else
+ {
+ worst_case_opcode_end = get_register(mRawData, LREG_HR);
+ }
+ state_info_offset += state_offset;
+ fprintf(fp, "[0x%X] ", state_info_offset);
+ state_info_offset += LSCRIPTDataSize[LST_INTEGER];
+ bytestream2char(name, mRawData, state_info_offset);
+ fprintf(fp, "%s\n", name);
+
+ event_jump_table = state_info_offset;
+
+ // run run through the handlers
+ for (j = LSTT_STATE_BEGIN; j < LSTT_STATE_END; j++)
+ {
+ if (event_handlers & LSCRIPTStateBitField[j])
+ {
+ event_offset = bytestream2integer(mRawData, state_info_offset);
+ stack_size = bytestream2integer(mRawData, state_info_offset);
+
+ read_ahead = event_jump_table;
+
+ S32 temp_end;
+ S32 dummy;
+
+ opcode_end = worst_case_opcode_end;
+
+ for (k = LSTT_STATE_BEGIN; k < LSTT_STATE_END; k++)
+ {
+ if (event_handlers & LSCRIPTStateBitField[k])
+ {
+ temp_end = bytestream2integer(mRawData, read_ahead);
+ dummy = bytestream2integer(mRawData, read_ahead);
+ if ( (temp_end < opcode_end)
+ &&(temp_end > event_offset))
+ {
+ opcode_end = temp_end;
+ }
+ }
+ }
+
+ if (event_offset)
+ {
+ event_offset += event_jump_table;
+ if (opcode_end < worst_case_opcode_end)
+ opcode_end += event_jump_table;
+ original_event_offset = event_offset;
+
+ fprintf(fp, "\t[0x%X] ", event_offset);
+
+ opcode_start = bytestream2integer(mRawData, event_offset);
+ opcode_start += original_event_offset;
+
+ switch(j)
+ {
+ case LSTT_STATE_ENTRY: // LSTT_STATE_ENTRY
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_STATE_EXIT: // LSTT_STATE_EXIT
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_TOUCH_START: // LSTT_TOUCH_START
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_TOUCH: // LSTT_TOUCH
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_TOUCH_END: // LSTT_TOUCH_END
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_COLLISION_START: // LSTT_COLLISION_START
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_COLLISION: // LSTT_COLLISION
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_COLLISION_END: // LSTT_COLLISION_END
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_LAND_COLLISION_START: // LSTT_LAND_COLLISION_START
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_LAND_COLLISION: // LSTT_LAND_COLLISION
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_LAND_COLLISION_END: // LSTT_LAND_COLLISION_END
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_INVENTORY: // LSTT_INVENTORY
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ break;
+ case LSTT_ATTACH: // LSTT_ATTACH
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ break;
+ case LSTT_DATASERVER: // LSTT_DATASERVER
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ break;
+ case LSTT_TIMER: // LSTT_TIMER
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_MOVING_START: // LSTT_MOVING_START
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_MOVING_END: // LSTT_MOVING_END
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_CHAT: // LSTT_CHAT
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ break;
+ case LSTT_OBJECT_REZ: // LSTT_OBJECT_REZ
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ break;
+ case LSTT_REMOTE_DATA: // LSTT_REMOTE_DATA
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ break;
+ case LSTT_REZ: // LSTT_REZ
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_SENSOR: // LSTT_SENSOR
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ break;
+ case LSTT_NO_SENSOR: // LSTT_NO_SENSOR
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_CONTROL: // LSTT_CONTROL
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ break;
+ case LSTT_LINK_MESSAGE: // LSTT_LINK_MESSAGE
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ break;
+ case LSTT_MONEY: // LSTT_MONEY
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ break;
+ case LSTT_EMAIL: // LSTT_EMAIL
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ break;
+ case LSTT_AT_TARGET: // LSTT_AT_TARGET
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tvector %s\n", name);
+ break;
+ case LSTT_NOT_AT_TARGET: // LSTT_NOT_AT_TARGET
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_AT_ROT_TARGET: // LSTT_AT_ROT_TARGET
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tquaternion %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tquaternion %s\n", name);
+ break;
+ case LSTT_NOT_AT_ROT_TARGET: // LSTT_NOT_AT_TARGET
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ break;
+ case LSTT_RTPERMISSIONS: // LSTT_RTPERMISSIONS
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ break;
+ case LSTT_HTTP_RESPONSE: // LSTT_REMOTE_DATA ?!?!?!
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "%s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tkey %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tinteger %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tlist %s\n", name);
+ bytestream2char(name, mRawData, event_offset);
+ fprintf(fp, "\t\tstring %s\n", name);
+ break;
+ default:
+ break;
+ }
+ fprintf(fp, "\t\tStack Size: %d\n", stack_size);
+ fprintf(fp, "\t\t\tOpCodes: 0x%X - 0x%X\n", opcode_start, opcode_end);
+ printOpCodeRange(fp, opcode_start, opcode_end, 3);
+ }
+ }
+ }
+ }
+ fprintf(fp, "=============================\n\n");
+}
+
+void LLScriptLSOParse::printHeap(FILE *fp)
+{
+ // print out registers first
+
+ S32 heap_offset = get_register(mRawData, LREG_HR);
+ S32 heap_pointer = get_register(mRawData, LREG_HP);
+ fprintf(fp, "=============================\n");
+ fprintf(fp, "[0x%X - 0x%X] Heap\n", heap_offset, heap_pointer);
+ fprintf(fp, "=============================\n");
+
+ lsa_fprint_heap(mRawData, fp);
+
+ fprintf(fp, "=============================\n\n");
+}
+
+void lso_print_tabs(FILE *fp, S32 tabs)
+{
+ S32 i;
+ for (i = 0; i < tabs; i++)
+ {
+ fprintf(fp, "\t");
+ }
+}
+
+void LLScriptLSOParse::printOpCodes(FILE *fp, S32 &offset, S32 tabs)
+{
+ U8 opcode = *(mRawData + offset);
+ mPrintOpCodes[opcode](fp, mRawData, offset, tabs);
+}
+
+void LLScriptLSOParse::printOpCodeRange(FILE *fp, S32 start, S32 end, S32 tabs)
+{
+ while (start < end)
+ {
+ printOpCodes(fp, start, tabs);
+ }
+}
+
+void LLScriptLSOParse::initOpCodePrinting()
+{
+ S32 i;
+ for (i = 0; i < 256; i++)
+ {
+ mPrintOpCodes[i] = print_noop;
+ }
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_NOOP]] = print_noop;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POP]] = print_pop;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPS]] = print_pops;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPL]] = print_popl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPV]] = print_popv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPQ]] = print_popq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPARG]] = print_poparg;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPIP]] = print_popip;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPBP]] = print_popbp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPSP]] = print_popsp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_POPSLR]] = print_popslr;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DUP]] = print_dup;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DUPS]] = print_dups;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DUPL]] = print_dupl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DUPV]] = print_dupv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DUPQ]] = print_dupq;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STORE]] = print_store;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STORES]] = print_stores;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREL]] = print_storel;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREV]] = print_storev;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREQ]] = print_storeq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREG]] = print_storeg;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREGS]] = print_storegs;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREGL]] = print_storegl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREGV]] = print_storegv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STOREGQ]] = print_storegq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADP]] = print_loadp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADSP]] = print_loadsp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADLP]] = print_loadlp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADVP]] = print_loadvp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADQP]] = print_loadqp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADGP]] = print_loadgp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADGSP]] = print_loadgsp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADGLP]] = print_loadglp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADGVP]] = print_loadgvp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LOADGQP]] = print_loadgqp;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSH]] = print_push;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHS]] = print_pushs;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHL]] = print_pushl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHV]] = print_pushv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHQ]] = print_pushq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHG]] = print_pushg;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHGS]] = print_pushgs;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHGL]] = print_pushgl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHGV]] = print_pushgv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHGQ]] = print_pushgq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHIP]] = print_puship;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHSP]] = print_pushsp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHBP]] = print_pushbp;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGB]] = print_pushargb;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGI]] = print_pushargi;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGF]] = print_pushargf;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGS]] = print_pushargs;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGV]] = print_pushargv;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGQ]] = print_pushargq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHE]] = print_pushe;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHEV]] = print_pushev;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHEQ]] = print_pusheq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PUSHARGE]] = print_pusharge;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_ADD]] = print_add;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_SUB]] = print_sub;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_MUL]] = print_mul;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_DIV]] = print_div;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_MOD]] = print_mod;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_EQ]] = print_eq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_NEQ]] = print_neq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LEQ]] = print_leq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_GEQ]] = print_geq;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_LESS]] = print_less;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_GREATER]] = print_greater;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BITAND]] = print_bitand;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BITOR]] = print_bitor;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BITXOR]] = print_bitxor;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BOOLAND]] = print_booland;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BOOLOR]] = print_boolor;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_SHL]] = print_shl;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_SHR]] = print_shr;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_NEG]] = print_neg;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BITNOT]] = print_bitnot;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_BOOLNOT]] = print_boolnot;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_JUMP]] = print_jump;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_JUMPIF]] = print_jumpif;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_JUMPNIF]] = print_jumpnif;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STATE]] = print_state;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_CALL]] = print_call;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_RETURN]] = print_return;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_CAST]] = print_cast;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STACKTOS]] = print_stacktos;
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_STACKTOL]] = print_stacktol;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_PRINT]] = print_print;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_CALLLIB]] = print_calllib;
+
+ mPrintOpCodes[LSCRIPTOpCodes[LOPC_CALLLIB_TWO_BYTE]] = print_calllib_two_byte;
+}
+
+void print_noop(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tNOOP\n", offset++);
+}
+
+void print_pop(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOP\n", offset++);
+}
+
+void print_pops(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPS\n", offset++);
+}
+
+void print_popl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPL\n", offset++);
+}
+
+void print_popv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPV\n", offset++);
+}
+
+void print_popq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPQ\n", offset++);
+}
+
+void print_poparg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPARG ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_popip(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPIP\n", offset++);
+}
+
+void print_popbp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPBP\n", offset++);
+}
+
+void print_popsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPSP\n", offset++);
+}
+
+void print_popslr(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPOPSLR\n", offset++);
+}
+
+void print_dup(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDUP\n", offset++);
+}
+
+void print_dups(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDUPS\n", offset++);
+}
+
+void print_dupl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDUPL\n", offset++);
+}
+
+void print_dupv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDUPV\n", offset++);
+}
+
+void print_dupq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDUPQ\n", offset++);
+}
+
+void print_store(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTORE $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_stores(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTORES $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_storel(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREL $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_storev(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREV $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_storeq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREQ $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_storeg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREG ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_storegs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGS ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_storegl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGL ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_storegv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGV ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_storegq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGQ ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_loadp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREP $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_loadsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREPS $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_loadlp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREPL $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_loadvp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREVP $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_loadqp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREQP $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_loadgp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_loadgsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGSP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_loadglp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGLP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_loadgvp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGVP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_loadgqp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTOREGQP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_push(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSH $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHS $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHL $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHV $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHQ $BP + ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHG ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "0x%X\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_pushgs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHGS ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "0x%X\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_pushgl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHGL ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "0x%X\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_pushgv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHGV ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "0x%X\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_pushgq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHGQ ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "0x%X\n", arg + get_register(buffer, LREG_GVR));
+}
+
+void print_puship(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHIP\n", offset++);
+}
+
+void print_pushbp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHBP\n", offset++);
+}
+
+void print_pushsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHSP\n", offset++);
+}
+
+void print_pushargb(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGB ", offset++);
+ arg = *(buffer + offset++);
+ fprintf(fp, "%d\n", (U32)arg);
+}
+
+void print_pushargi(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGI ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_pushargf(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ F32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGF ", offset++);
+ arg = bytestream2float(buffer, offset);
+ fprintf(fp, "%f\n", arg);
+}
+
+void print_pushargs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ char arg[1024];
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGS ", offset++);
+ bytestream2char(arg, buffer, offset);
+ fprintf(fp, "%s\n", arg);
+}
+
+void print_pushargv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ LLVector3 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGV ", offset++);
+ bytestream2vector(arg, buffer, offset);
+ fprintf(fp, "< %f, %f, %f >\n", arg.mV[VX], arg.mV[VY], arg.mV[VZ]);
+}
+
+void print_pushargq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ LLQuaternion arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGV ", offset++);
+ bytestream2quaternion(arg, buffer, offset);
+ fprintf(fp, "< %f, %f, %f, %f >\n", arg.mQ[VX], arg.mQ[VY], arg.mQ[VZ], arg.mQ[VS]);
+}
+
+void print_pushe(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHE\n", offset++);
+}
+
+void print_pushev(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHEV\n", offset++);
+}
+
+void print_pusheq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHEQ\n", offset++);
+}
+
+void print_pusharge(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPUSHARGE ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+
+void print_add(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tADD ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_sub(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSUB ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_mul(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tMUL ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_div(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tDIV ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_mod(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tMOD ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_eq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tEQ ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_neq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tNEQ ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_leq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tLEQ ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_geq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tGEQ ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_less(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tLESS ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_greater(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tGREATER ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+
+void print_bitand(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBITAND\n", offset++);
+}
+
+void print_bitor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBITOR\n", offset++);
+}
+
+void print_bitxor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBITXOR\n", offset++);
+}
+
+void print_booland(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBOOLAND\n", offset++);
+}
+
+void print_boolor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBOOLOR\n", offset++);
+}
+
+void print_shl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSHL\n", offset++);
+}
+
+void print_shr(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSHR\n", offset++);
+}
+
+
+void print_neg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 type;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tNEG ", offset++);
+ type = *(buffer + offset++);
+ fprintf(fp, "%s\n", LSCRIPTTypeNames[type]);
+}
+
+void print_bitnot(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBITNOT\n", offset++);
+}
+
+void print_boolnot(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tBOOLNOT\n", offset++);
+}
+
+void print_jump(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tJUMP ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_jumpif(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ U8 type;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tJUMPIF ", offset++);
+ type = *(buffer + offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%s, %d\n", LSCRIPTTypeNames[type], arg);
+}
+
+void print_jumpnif(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ U8 type;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tJUMPNIF ", offset++);
+ type = *(buffer + offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%s, %d\n", LSCRIPTTypeNames[type], arg);
+}
+
+void print_state(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTATE ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_call(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tCALL ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_return(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tRETURN\n", offset++);
+}
+
+void print_cast(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 types;
+ U8 type1;
+ U8 type2;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tCAST ", offset++);
+ types = *(buffer + offset++);
+ type1 = types >> 4;
+ type2 = types & 0xf;
+ fprintf(fp, "%s, %s\n", LSCRIPTTypeNames[type1], LSCRIPTTypeNames[type2]);
+}
+
+void print_stacktos(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTACKTOS ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_stacktol(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ S32 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tSTACKTOL ", offset++);
+ arg = bytestream2integer(buffer, offset);
+ fprintf(fp, "%d\n", arg);
+}
+
+void print_print(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tPRINT ", offset++);
+ U8 type = *(buffer + offset++);
+ fprintf(fp, "%s\n", LSCRIPTTypeNames[type]);
+}
+
+void print_calllib(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U8 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tCALLLIB ", offset++);
+ arg = *(buffer + offset++);
+ fprintf(fp, "%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg]->mName);
+}
+
+
+void print_calllib_two_byte(FILE *fp, U8 *buffer, S32 &offset, S32 tabs)
+{
+ U16 arg;
+ lso_print_tabs(fp, tabs);
+ fprintf(fp, "[0x%X]\tCALLLIB_TWO_BYTE ", offset++);
+ arg = bytestream2u16(buffer, offset);
+ fprintf(fp, "%d (%s)\n", (U32)arg, gScriptLibrary.mFunctions[arg]->mName);
+}
+
diff --git a/indra/lscript/lscript_execute/lscript_readlso.h b/indra/lscript/lscript_execute/lscript_readlso.h
new file mode 100644
index 0000000000..652b04f2b2
--- /dev/null
+++ b/indra/lscript/lscript_execute/lscript_readlso.h
@@ -0,0 +1,147 @@
+/**
+ * @file lscript_readlso.h
+ * @brief classes to read lso file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_READLSO_H
+#define LL_LSCRIPT_READLSO_H
+
+#include "lscript_byteconvert.h"
+#include "linked_lists.h"
+
+// list of op code print functions
+void print_noop(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pop(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pops(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_poparg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popip(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popbp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_popslr(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_dup(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_dups(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_dupl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_dupv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_dupq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_store(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_stores(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storel(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storev(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storeq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storeg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storegs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storegl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storegv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_storegq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadlp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadvp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadqp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadgp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadgsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadglp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadgvp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_loadgqp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_push(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushgl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushgs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushgv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushgq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_puship(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushbp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushsp(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargb(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargi(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargf(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargs(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargv(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushargq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushe(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pushev(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pusheq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_pusharge(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_add(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_sub(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_mul(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_div(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_mod(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_eq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_neq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_leq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_geq(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_less(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_greater(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_bitand(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_bitor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_bitxor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_booland(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_boolor(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_shl(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_shr(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_neg(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_bitnot(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_boolnot(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_jump(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_jumpif(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_jumpnif(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_state(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_call(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_return(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_cast(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_stacktos(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_stacktol(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_print(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+void print_calllib(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+void print_calllib_two_byte(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+
+class LLScriptLSOParse
+{
+public:
+ LLScriptLSOParse(FILE *fp);
+ LLScriptLSOParse(U8 *buffer);
+ ~LLScriptLSOParse();
+
+ void initOpCodePrinting();
+
+ void printData(FILE *fp);
+ void printNameDesc(FILE *fp);
+ void printRegisters(FILE *fp);
+ void printGlobals(FILE *fp);
+ void printGlobalFunctions(FILE *fp);
+ void printStates(FILE *fp);
+ void printHeap(FILE *fp);
+ void printOpCodes(FILE *fp, S32 &offset, S32 tabs);
+ void printOpCodeRange(FILE *fp, S32 start, S32 end, S32 tabs);
+
+ U8 *mRawData;
+ void (*mPrintOpCodes[0x100])(FILE *fp, U8 *buffer, S32 &offset, S32 tabs);
+};
+
+
+void lso_print_tabs(FILE *fp, S32 tabs);
+
+#endif
diff --git a/indra/lscript/lscript_export.h b/indra/lscript/lscript_export.h
new file mode 100644
index 0000000000..b7f64bfbc0
--- /dev/null
+++ b/indra/lscript/lscript_export.h
@@ -0,0 +1,16 @@
+/**
+ * @file lscript_export.h
+ * @brief Export interface class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_EXPORT_H
+#define LL_LSCRIPT_EXPORT_H
+
+#include "lscript_library.h"
+
+extern LLScriptLibrary gScriptLibrary;
+
+#endif
diff --git a/indra/lscript/lscript_http.h b/indra/lscript/lscript_http.h
new file mode 100644
index 0000000000..af70596673
--- /dev/null
+++ b/indra/lscript/lscript_http.h
@@ -0,0 +1,27 @@
+/**
+ * @file lscript_http.h
+ * @brief LSL HTTP keys
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Keys used in LSL HTTP function <key,value> pair lists.
+
+#ifndef LSCRIPT_HTTP_H
+#define LSCRIPT_HTTP_H
+
+enum LLScriptHTTPRequestParameterKey
+{
+ HTTP_METHOD,
+ HTTP_MIMETYPE,
+ HTTP_BODY_MAXLENGTH,
+ HTTP_VERIFY_CERT
+};
+
+enum LLScriptHTTPResponseMetadataKey
+{
+ HTTP_BODY_TRUNCATED
+};
+
+#endif
diff --git a/indra/lscript/lscript_library.h b/indra/lscript/lscript_library.h
new file mode 100644
index 0000000000..3cb1419296
--- /dev/null
+++ b/indra/lscript/lscript_library.h
@@ -0,0 +1,382 @@
+/**
+ * @file lscript_library.h
+ * @brief External library interface
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_LIBRARY_H
+#define LL_LSCRIPT_LIBRARY_H
+
+#include "lscript_byteformat.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "lluuid.h"
+#include "lscript_byteconvert.h"
+#include <stdio.h>
+
+class LLScriptLibData;
+
+class LLScriptLibraryFunction
+{
+public:
+ LLScriptLibraryFunction(F32 eu, F32 st, void (*exec_func)(LLScriptLibData *, LLScriptLibData *, const LLUUID &), char *name, char *ret_type, char *args, char *desc, BOOL god_only = FALSE);
+ ~LLScriptLibraryFunction();
+
+ F32 mEnergyUse;
+ F32 mSleepTime;
+ void (*mExecFunc)(LLScriptLibData *, LLScriptLibData *, const LLUUID &);
+ char *mName;
+ char *mReturnType;
+ char *mArgs;
+ char *mDesc;
+ BOOL mGodOnly;
+};
+
+class LLScriptLibrary
+{
+public:
+ LLScriptLibrary();
+ ~LLScriptLibrary();
+
+ void init();
+
+ void addFunction(LLScriptLibraryFunction *func);
+ void assignExec(char *name, void (*exec_func)(LLScriptLibData *, LLScriptLibData *, const LLUUID &));
+
+ S32 mNextNumber;
+ LLScriptLibraryFunction **mFunctions;
+};
+
+extern LLScriptLibrary gScriptLibrary;
+
+class LLScriptLibData
+{
+public:
+ // TODO: Change this to a union
+ LSCRIPTType mType;
+ S32 mInteger;
+ F32 mFP;
+ char *mKey;
+ char *mString;
+ LLVector3 mVec;
+ LLQuaternion mQuat;
+ LLScriptLibData *mListp;
+
+ friend const bool operator<=(const LLScriptLibData &a, const LLScriptLibData &b)
+ {
+ if (a.mType == b.mType)
+ {
+ if (a.mType == LST_INTEGER)
+ {
+ return a.mInteger <= b.mInteger;
+ }
+ if (a.mType == LST_FLOATINGPOINT)
+ {
+ return a.mFP <= b.mFP;
+ }
+ if (a.mType == LST_STRING)
+ {
+ return strcmp(a.mString, b.mString) <= 0;
+ }
+ if (a.mType == LST_KEY)
+ {
+ return strcmp(a.mKey, b.mKey) <= 0;
+ }
+ if (a.mType == LST_VECTOR)
+ {
+ return a.mVec.magVecSquared() <= b.mVec.magVecSquared();
+ }
+ }
+ return TRUE;
+ }
+
+ friend const bool operator==(const LLScriptLibData &a, const LLScriptLibData &b)
+ {
+ if (a.mType == b.mType)
+ {
+ if (a.mType == LST_INTEGER)
+ {
+ return a.mInteger == b.mInteger;
+ }
+ if (a.mType == LST_FLOATINGPOINT)
+ {
+ return a.mFP == b.mFP;
+ }
+ if (a.mType == LST_STRING)
+ {
+ return !strcmp(a.mString, b.mString);
+ }
+ if (a.mType == LST_KEY)
+ {
+ return !strcmp(a.mKey, b.mKey);
+ }
+ if (a.mType == LST_VECTOR)
+ {
+ return a.mVec == b.mVec;
+ }
+ if (a.mType == LST_QUATERNION)
+ {
+ return a.mQuat == b.mQuat;
+ }
+ }
+ return FALSE;
+ }
+
+ S32 getListLength()
+ {
+ LLScriptLibData *data = this;
+ S32 retval = 0;
+ while (data->mListp)
+ {
+ retval++;
+ data = data->mListp;
+ }
+ return retval;
+ }
+
+ BOOL checkForMultipleLists()
+ {
+ LLScriptLibData *data = this;
+ while (data->mListp)
+ {
+ data = data->mListp;
+ if (data->mType == LST_LIST)
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ S32 getSavedSize()
+ {
+ S32 size = 0;
+ // mType
+ size += 4;
+
+ switch(mType)
+ {
+ case LST_INTEGER:
+ size += 4;
+ break;
+ case LST_FLOATINGPOINT:
+ size += 4;
+ break;
+ case LST_KEY:
+ size += (S32)strlen(mKey) + 1;
+ break;
+ case LST_STRING:
+ size += (S32)strlen(mString) + 1;
+ break;
+ case LST_LIST:
+ break;
+ case LST_VECTOR:
+ size += 12;
+ break;
+ case LST_QUATERNION:
+ size += 16;
+ break;
+ default:
+ break;
+ }
+ return size;
+ }
+
+ S32 write2bytestream(U8 *dest)
+ {
+ S32 offset = 0;
+ integer2bytestream(dest, offset, mType);
+ switch(mType)
+ {
+ case LST_INTEGER:
+ integer2bytestream(dest, offset, mInteger);
+ break;
+ case LST_FLOATINGPOINT:
+ float2bytestream(dest, offset, mFP);
+ break;
+ case LST_KEY:
+ char2bytestream(dest, offset, mKey);
+ break;
+ case LST_STRING:
+ char2bytestream(dest, offset, mString);
+ break;
+ case LST_LIST:
+ break;
+ case LST_VECTOR:
+ vector2bytestream(dest, offset, mVec);
+ break;
+ case LST_QUATERNION:
+ quaternion2bytestream(dest, offset, mQuat);
+ break;
+ default:
+ break;
+ }
+ return offset;
+ }
+
+ LLScriptLibData() : mType(LST_NULL), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ }
+
+ LLScriptLibData(const LLScriptLibData &data) : mType(data.mType), mInteger(data.mInteger), mFP(data.mFP), mKey(NULL), mString(NULL), mVec(data.mVec), mQuat(data.mQuat), mListp(NULL)
+ {
+ if (data.mKey)
+ {
+ mKey = new char[strlen(data.mKey) + 1];
+ strcpy(mKey, data.mKey);
+ }
+ if (data.mString)
+ {
+ mString = new char[strlen(data.mString) + 1];
+ strcpy(mString, data.mString);
+ }
+ }
+
+ LLScriptLibData(U8 *src, S32 &offset) : mListp(NULL)
+ {
+ static char temp[TOP_OF_MEMORY];
+ mType = (LSCRIPTType)bytestream2integer(src, offset);
+ switch(mType)
+ {
+ case LST_INTEGER:
+ mInteger = bytestream2integer(src, offset);
+ break;
+ case LST_FLOATINGPOINT:
+ mFP = bytestream2float(src, offset);
+ break;
+ case LST_KEY:
+ {
+ bytestream2char(temp, src, offset);
+ mKey = new char[strlen(temp) + 1];
+ strcpy(mKey, temp);
+ }
+ break;
+ case LST_STRING:
+ {
+ bytestream2char(temp, src, offset);
+ mString = new char[strlen(temp) + 1];
+ strcpy(mString, temp);
+ }
+ break;
+ case LST_LIST:
+ break;
+ case LST_VECTOR:
+ bytestream2vector(mVec, src, offset);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(mQuat, src, offset);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void set(U8 *src, S32 &offset)
+ {
+ static char temp[TOP_OF_MEMORY];
+ mType = (LSCRIPTType)bytestream2integer(src, offset);
+ switch(mType)
+ {
+ case LST_INTEGER:
+ mInteger = bytestream2integer(src, offset);
+ break;
+ case LST_FLOATINGPOINT:
+ mFP = bytestream2float(src, offset);
+ break;
+ case LST_KEY:
+ {
+ bytestream2char(temp, src, offset);
+ mKey = new char[strlen(temp) + 1];
+ strcpy(mKey, temp);
+ }
+ break;
+ case LST_STRING:
+ {
+ bytestream2char(temp, src, offset);
+ mString = new char[strlen(temp) + 1];
+ strcpy(mString, temp);
+ }
+ break;
+ case LST_LIST:
+ break;
+ case LST_VECTOR:
+ bytestream2vector(mVec, src, offset);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(mQuat, src, offset);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void print(std::ostream &s, BOOL b_prepend_comma);
+ void print_separator(std::ostream& ostr, BOOL b_prepend_sep, char* sep);
+
+ void setFromCSV(char *src)
+ {
+ mType = LST_STRING;
+ mString = new char[strlen(src) + 1];
+ strcpy(mString, src);
+ }
+
+ LLScriptLibData(S32 integer) : mType(LST_INTEGER), mInteger(integer), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ }
+
+ LLScriptLibData(F32 fp) : mType(LST_FLOATINGPOINT), mInteger(0), mFP(fp), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ }
+
+ LLScriptLibData(const LLUUID &id) : mType(LST_KEY), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ mKey = new char[UUID_STR_LENGTH];
+ id.toString(mKey);
+ }
+
+ LLScriptLibData(char *string) : mType(LST_STRING), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ if (!string)
+ {
+ mString = new char[1];
+ mString[0] = 0;
+ }
+ else
+ {
+ mString = new char[strlen(string) + 1];
+ strcpy(mString, string);
+ }
+ }
+
+ LLScriptLibData(const char *string) : mType(LST_STRING), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(), mListp(NULL)
+ {
+ if (!string)
+ {
+ mString = new char[1];
+ mString[0] = 0;
+ }
+ else
+ {
+ mString = new char[strlen(string) + 1];
+ strcpy(mString, string);
+ }
+ }
+
+ LLScriptLibData(LLVector3 &vec) : mType(LST_VECTOR), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(vec), mQuat(), mListp(NULL)
+ {
+ }
+
+ LLScriptLibData(LLQuaternion &quat) : mType(LST_QUATERNION), mInteger(0), mFP(0.f), mKey(NULL), mString(NULL), mVec(), mQuat(quat), mListp(NULL)
+ {
+ }
+
+ ~LLScriptLibData()
+ {
+ delete mListp;
+ delete [] mKey;
+ delete [] mString;
+ }
+
+};
+
+#endif
diff --git a/indra/lscript/lscript_library/lscript_alloc.cpp b/indra/lscript/lscript_library/lscript_alloc.cpp
new file mode 100644
index 0000000000..8230d181ce
--- /dev/null
+++ b/indra/lscript/lscript_library/lscript_alloc.cpp
@@ -0,0 +1,1102 @@
+/**
+ * @file lscript_alloc.cpp
+ * @brief general heap management for scripting system
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// #define at top of file accelerates gcc compiles
+// Under gcc 2.9, the manual is unclear if comments can appear above #ifndef
+// Under gcc 3, the manual explicitly states comments can appear above the #ifndef
+
+#include "linden_common.h"
+
+#include "lscript_alloc.h"
+
+// supported data types
+
+// basic types
+// integer 4 bytes of integer data
+// float 4 bytes of float data
+// string data null terminated 1 byte string
+// key data null terminated 1 byte string
+// vector data 12 bytes of 3 floats
+// quaternion data 16 bytes of 4 floats
+
+// list type
+// list data 4 bytes of number of entries followed by pointer
+
+// string pointer 4 bytes of address of string data on the heap (only used in list data)
+// key pointer 4 bytes of address of key data on the heap (only used in list data)
+
+// heap format
+//
+// 4 byte offset to next block (in bytes)
+// 1 byte of type of variable or empty
+// 2 bytes of reference count
+// nn bytes of data
+
+void reset_hp_to_safe_spot(const U8 *buffer)
+{
+ set_register((U8 *)buffer, LREG_HP, TOP_OF_MEMORY);
+}
+
+// create a heap from the HR to TM
+BOOL lsa_create_heap(U8 *heap_start, S32 size)
+{
+ LLScriptAllocEntry entry(size, LST_NULL);
+
+ S32 position = 0;
+
+ alloc_entry2bytestream(heap_start, position, entry);
+
+ return TRUE;
+}
+
+S32 lsa_heap_top(U8 *heap_start, S32 maxtop)
+{
+ S32 offset = 0;
+ LLScriptAllocEntry entry;
+ bytestream2alloc_entry(entry, heap_start, offset);
+
+ while (offset + entry.mSize < maxtop)
+ {
+ offset += entry.mSize;
+ bytestream2alloc_entry(entry, heap_start, offset);
+ }
+ return offset + entry.mSize;
+}
+
+
+// adding to heap
+// if block is empty
+// if block is at least block size + 4 larger than data
+// split block
+// insert data into first part
+// return address
+// else
+// insert data into block
+// return address
+// else
+// if next block is >= SP
+// set Stack-Heap collision
+// return NULL
+// if next block is empty
+// merge next block with current block
+// go to start of algorithm
+// else
+// move to next block
+// go to start of algorithm
+
+S32 lsa_heap_add_data(U8 *buffer, LLScriptLibData *data, S32 heapsize, BOOL b_delete)
+{
+ if (get_register(buffer, LREG_FR))
+ return 1;
+ LLScriptAllocEntry entry, nextentry;
+ S32 hr = get_register(buffer, LREG_HR);
+ S32 hp = get_register(buffer, LREG_HP);
+ S32 current_offset, next_offset, offset = hr;
+ S32 size = 0;
+
+ switch(data->mType)
+ {
+ case LST_INTEGER:
+ size = 4;
+ break;
+ case LST_FLOATINGPOINT:
+ size = 4;
+ break;
+ case LST_KEY:
+ size = (S32)strlen(data->mKey) + 1;
+ break;
+ case LST_STRING:
+ size = (S32)strlen(data->mString) + 1;
+ break;
+ case LST_LIST:
+ // list data 4 bytes of number of entries followed by number of pointer
+ size = 4 + 4*data->getListLength();
+ if (data->checkForMultipleLists())
+ {
+ set_fault(buffer, LSRF_NESTING_LISTS);
+ }
+ break;
+ case LST_VECTOR:
+ size = 12;
+ break;
+ case LST_QUATERNION:
+ size = 16;
+ break;
+ default:
+ break;
+ }
+
+ current_offset = offset;
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ do
+ {
+ hp = get_register(buffer, LREG_HP);
+ if (!entry.mType)
+ {
+ if (entry.mSize >= size + SIZEOF_SCRIPT_ALLOC_ENTRY + 4)
+ {
+ offset = current_offset;
+ lsa_split_block(buffer, offset, size, entry);
+ entry.mType = data->mType;
+ entry.mSize = size;
+ entry.mReferenceCount = 1;
+ offset = current_offset;
+ alloc_entry2bytestream(buffer, offset, entry);
+ lsa_insert_data(buffer, offset, data, entry, heapsize);
+ hp = get_register(buffer, LREG_HP);
+ S32 new_hp = current_offset + size + 2*SIZEOF_SCRIPT_ALLOC_ENTRY;
+ if (new_hp >= hr + heapsize)
+ {
+ break;
+ }
+ if (new_hp > hp)
+ {
+ set_register(buffer, LREG_HP, new_hp);
+ hp = get_register(buffer, LREG_HP);
+ }
+ if (b_delete)
+ delete data;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ if (current_offset <= hp)
+ return current_offset - hr + 1;
+ else
+ return hp - hr + 1;
+ }
+ else if (entry.mSize >= size)
+ {
+ entry.mType = data->mType;
+ entry.mReferenceCount = 1;
+ offset = current_offset;
+ alloc_entry2bytestream(buffer, offset, entry);
+ lsa_insert_data(buffer, offset, data, entry, heapsize);
+ hp = get_register(buffer, LREG_HP);
+ if (b_delete)
+ delete data;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ return current_offset - hr + 1;
+ }
+ }
+ offset += entry.mSize;
+ if (offset < hr + heapsize)
+ {
+ next_offset = offset;
+ bytestream2alloc_entry(nextentry, buffer, offset);
+ if (!nextentry.mType && !entry.mType)
+ {
+ entry.mSize += nextentry.mSize + SIZEOF_SCRIPT_ALLOC_ENTRY;
+ offset = current_offset;
+ alloc_entry2bytestream(buffer, offset, entry);
+ }
+ else
+ {
+ current_offset = next_offset;
+ entry = nextentry;
+ }
+
+ // this works whether we are bumping out or coming in
+ S32 new_hp = current_offset + size + 2*SIZEOF_SCRIPT_ALLOC_ENTRY;
+
+ // make sure we aren't about to be stupid
+ if (new_hp >= hr + heapsize)
+ {
+ break;
+ }
+ if (new_hp > hp)
+ {
+ set_register(buffer, LREG_HP, new_hp);
+ hp = get_register(buffer, LREG_HP);
+ }
+ }
+ else
+ {
+ break;
+ }
+ } while (1);
+ set_fault(buffer, LSRF_STACK_HEAP_COLLISION);
+ reset_hp_to_safe_spot(buffer);
+ if (b_delete)
+ delete data;
+ return 0;
+}
+
+// split block
+// set offset to point to new block
+// set offset of new block to point to original offset - block size - data size
+// set new block to empty
+// set new block reference count to 0
+void lsa_split_block(U8 *buffer, S32 &offset, S32 size, LLScriptAllocEntry &entry)
+{
+ if (get_register(buffer, LREG_FR))
+ return;
+ LLScriptAllocEntry newentry;
+
+ newentry.mSize = entry.mSize - SIZEOF_SCRIPT_ALLOC_ENTRY - size;
+ entry.mSize -= newentry.mSize + SIZEOF_SCRIPT_ALLOC_ENTRY;
+
+ alloc_entry2bytestream(buffer, offset, entry);
+ S32 orig_offset = offset + size;
+ alloc_entry2bytestream(buffer, orig_offset, newentry);
+}
+
+// insert data
+// if data is non-list type
+// set type to basic type, set reference count to 1, copy data, return address
+// else
+// set type to list data type, set reference count to 1
+// save length of list
+// for each list entry
+// insert data
+// return address
+
+void lsa_insert_data(U8 *buffer, S32 &offset, LLScriptLibData *data, LLScriptAllocEntry &entry, S32 heapsize)
+{
+ if (get_register(buffer, LREG_FR))
+ return;
+ if (data->mType != LST_LIST)
+ {
+ switch(data->mType)
+ {
+ case LST_INTEGER:
+ integer2bytestream(buffer, offset, data->mInteger);
+ break;
+ case LST_FLOATINGPOINT:
+ float2bytestream(buffer, offset, data->mFP);
+ break;
+ case LST_KEY:
+ char2bytestream(buffer, offset, data->mKey);
+ break;
+ case LST_STRING:
+ char2bytestream(buffer, offset, data->mString);
+ break;
+ case LST_VECTOR:
+ vector2bytestream(buffer, offset, data->mVec);
+ break;
+ case LST_QUATERNION:
+ quaternion2bytestream(buffer, offset, data->mQuat);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // store length of list
+ integer2bytestream(buffer, offset, data->getListLength());
+ data = data->mListp;
+ while(data)
+ {
+ // store entry and then store address if valid
+ S32 address = lsa_heap_add_data(buffer, data, heapsize, FALSE);
+ integer2bytestream(buffer, offset, address);
+ data = data->mListp;
+ }
+ }
+}
+
+S32 lsa_create_data_block(U8 **buffer, LLScriptLibData *data, S32 base_offset)
+{
+ S32 offset = 0;
+ S32 size = 0;
+
+ LLScriptAllocEntry entry;
+
+ if (!data)
+ {
+ entry.mType = LST_NULL;
+ entry.mReferenceCount = 0;
+ entry.mSize = MAX_HEAP_SIZE;
+ size = SIZEOF_SCRIPT_ALLOC_ENTRY;
+ *buffer = new U8[size];
+ alloc_entry2bytestream(*buffer, offset, entry);
+ return size;
+ }
+
+ entry.mType = data->mType;
+ entry.mReferenceCount = 1;
+
+ if (data->mType != LST_LIST)
+ {
+ if ( (data->mType != LST_STRING)
+ &&(data->mType != LST_KEY))
+ {
+ size = LSCRIPTDataSize[data->mType];
+ }
+ else
+ {
+ if (data->mType == LST_STRING)
+ {
+ if (data->mString)
+ {
+ size = (S32)strlen(data->mString) + 1;
+ }
+ else
+ {
+ size = 1;
+ }
+ }
+ if (data->mType == LST_KEY)
+ {
+ if (data->mKey)
+ {
+ size = (S32)strlen(data->mKey) + 1;
+ }
+ else
+ {
+ size = 1;
+ }
+ }
+ }
+ entry.mSize = size;
+ size += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ *buffer = new U8[size];
+ alloc_entry2bytestream(*buffer, offset, entry);
+
+ switch(data->mType)
+ {
+ case LST_INTEGER:
+ integer2bytestream(*buffer, offset, data->mInteger);
+ break;
+ case LST_FLOATINGPOINT:
+ float2bytestream(*buffer, offset, data->mFP);
+ break;
+ case LST_KEY:
+ if (data->mKey)
+ char2bytestream(*buffer, offset, data->mKey);
+ else
+ byte2bytestream(*buffer, offset, 0);
+ break;
+ case LST_STRING:
+ if (data->mString)
+ char2bytestream(*buffer, offset, data->mString);
+ else
+ byte2bytestream(*buffer, offset, 0);
+ break;
+ case LST_VECTOR:
+ vector2bytestream(*buffer, offset, data->mVec);
+ break;
+ case LST_QUATERNION:
+ quaternion2bytestream(*buffer, offset, data->mQuat);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ U8 *listbuf;
+ S32 length = data->getListLength();
+ size = 4 * length + 4;
+ entry.mSize = size;
+
+ size += SIZEOF_SCRIPT_ALLOC_ENTRY;
+ *buffer = new U8[size];
+
+ alloc_entry2bytestream(*buffer, offset, entry);
+ // store length of list
+ integer2bytestream(*buffer, offset, length);
+ data = data->mListp;
+ while(data)
+ {
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ integer2bytestream(*buffer, offset, size + base_offset + 1);
+
+ S32 listsize = lsa_create_data_block(&listbuf, data, base_offset + size);
+ if (listsize)
+ {
+ U8 *tbuff = new U8[size + listsize];
+ memcpy(tbuff, *buffer, size);
+ memcpy(tbuff + size, listbuf, listsize);
+ size += listsize;
+ delete [] *buffer;
+ delete [] listbuf;
+ *buffer = tbuff;
+ }
+ data = data->mListp;
+ }
+ }
+ return size;
+}
+
+// increase reference count
+// increase reference count by 1
+
+void lsa_increase_ref_count(U8 *buffer, S32 offset)
+{
+ if (get_register(buffer, LREG_FR))
+ return;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ offset += get_register(buffer, LREG_HR) - 1;
+ if ( (offset < get_register(buffer, LREG_HR))
+ ||(offset >= get_register(buffer, LREG_HP)))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return;
+ }
+ S32 orig_offset = offset;
+ LLScriptAllocEntry entry;
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ entry.mReferenceCount++;
+
+ alloc_entry2bytestream(buffer, orig_offset, entry);
+}
+
+// decrease reference count
+// decrease reference count by 1
+// if reference count == 0
+// set type to empty
+
+void lsa_decrease_ref_count(U8 *buffer, S32 offset)
+{
+ if (get_register(buffer, LREG_FR))
+ return;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ offset += get_register(buffer, LREG_HR) - 1;
+ if ( (offset < get_register(buffer, LREG_HR))
+ ||(offset >= get_register(buffer, LREG_HP)))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return;
+ }
+ S32 orig_offset = offset;
+ LLScriptAllocEntry entry;
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ entry.mReferenceCount--;
+
+ if (entry.mReferenceCount < 0)
+ {
+ entry.mReferenceCount = 0;
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ }
+ else if (!entry.mReferenceCount)
+ {
+ if (entry.mType == LST_LIST)
+ {
+ S32 i, num = bytestream2integer(buffer, offset);
+ for (i = 0; i < num; i++)
+ {
+ S32 list_offset = bytestream2integer(buffer, offset);
+ lsa_decrease_ref_count(buffer, list_offset);
+ }
+ }
+ entry.mType = LST_NULL;
+ }
+
+ alloc_entry2bytestream(buffer, orig_offset, entry);
+}
+
+char gLSAStringRead[16384];
+
+
+LLScriptLibData *lsa_get_data(U8 *buffer, S32 &offset, BOOL b_dec_ref)
+{
+ if (get_register(buffer, LREG_FR))
+ return (new LLScriptLibData);
+ S32 orig_offset = offset;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ offset += get_register(buffer, LREG_HR) - 1;
+ if ( (offset < get_register(buffer, LREG_HR))
+ ||(offset >= get_register(buffer, LREG_HP)))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return (new LLScriptLibData);
+ }
+ LLScriptAllocEntry entry;
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ LLScriptLibData *retval = new LLScriptLibData;
+
+ if (!entry.mType)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ return retval;
+ }
+
+ retval->mType = (LSCRIPTType)entry.mType;
+ if (entry.mType != LST_LIST)
+ {
+ switch(entry.mType)
+ {
+ case LST_INTEGER:
+ retval->mInteger = bytestream2integer(buffer, offset);
+ break;
+ case LST_FLOATINGPOINT:
+ retval->mFP = bytestream2float(buffer, offset);
+ break;
+ case LST_KEY:
+ bytestream2char(gLSAStringRead, buffer, offset);
+ retval->mKey = new char[strlen(gLSAStringRead) + 1];
+ strcpy(retval->mKey, gLSAStringRead);
+ break;
+ case LST_STRING:
+ bytestream2char(gLSAStringRead, buffer, offset);
+ retval->mString = new char[strlen(gLSAStringRead) + 1];
+ strcpy(retval->mString, gLSAStringRead);
+ break;
+ case LST_VECTOR:
+ bytestream2vector(retval->mVec, buffer, offset);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(retval->mQuat, buffer, offset);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // get length of list
+ S32 i, length = bytestream2integer(buffer, offset);
+ LLScriptLibData *tip = retval;
+
+ for (i = 0; i < length; i++)
+ {
+ S32 address = bytestream2integer(buffer, offset);
+ tip->mListp = lsa_get_data(buffer, address, FALSE);
+ tip = tip->mListp;
+ }
+ }
+ if (retval->checkForMultipleLists())
+ {
+ set_fault(buffer, LSRF_NESTING_LISTS);
+ }
+ if (b_dec_ref)
+ {
+ lsa_decrease_ref_count(buffer, orig_offset);
+ }
+ return retval;
+}
+
+LLScriptLibData *lsa_get_list_ptr(U8 *buffer, S32 &offset, BOOL b_dec_ref)
+{
+ if (get_register(buffer, LREG_FR))
+ return (new LLScriptLibData);
+ S32 orig_offset = offset;
+ // this bit of nastiness is to get around that code paths to local variables can result in lack of initialization
+ // and function clean up of ref counts isn't based on scope (a mistake, I know)
+ offset += get_register(buffer, LREG_HR) - 1;
+ if ( (offset < get_register(buffer, LREG_HR))
+ ||(offset >= get_register(buffer, LREG_HP)))
+ {
+ set_fault(buffer, LSRF_BOUND_CHECK_ERROR);
+ return (new LLScriptLibData);
+ }
+ LLScriptAllocEntry entry;
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ if (!entry.mType)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ return NULL;
+ }
+
+ LLScriptLibData base, *tip = &base;
+
+ if (entry.mType != LST_LIST)
+ {
+ return NULL;
+ }
+ else
+ {
+ // get length of list
+ S32 i, length = bytestream2integer(buffer, offset);
+
+ for (i = 0; i < length; i++)
+ {
+ S32 address = bytestream2integer(buffer, offset);
+ tip->mListp = lsa_get_data(buffer, address, FALSE);
+ tip = tip->mListp;
+ }
+ }
+ if (b_dec_ref)
+ {
+ lsa_decrease_ref_count(buffer, orig_offset);
+ }
+ tip = base.mListp;
+ base.mListp = NULL;
+ return tip;
+}
+
+S32 lsa_cat_strings(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *string1;
+ LLScriptLibData *string2;
+ if (offset1 != offset2)
+ {
+ string1 = lsa_get_data(buffer, offset1, TRUE);
+ string2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+ else
+ {
+ string1 = lsa_get_data(buffer, offset1, TRUE);
+ string2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+
+ if ( (!string1)
+ ||(!string2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete string1;
+ delete string2;
+ return 0;
+ }
+
+ char *test1 = NULL, *test2 = NULL;
+
+ if (string1->mType == LST_STRING)
+ {
+ test1 = string1->mString;
+ }
+ else if (string1->mType == LST_KEY)
+ {
+ test1 = string1->mKey;
+ }
+ if (string2->mType == LST_STRING)
+ {
+ test2 = string2->mString;
+ }
+ else if (string2->mType == LST_KEY)
+ {
+ test2 = string2->mKey;
+ }
+
+ if ( (!test1)
+ ||(!test2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete string1;
+ delete string2;
+ return 0;
+ }
+
+ S32 size = (S32)strlen(test1) + (S32)strlen(test2) + 1;
+
+ LLScriptLibData *string3 = new LLScriptLibData;
+ string3->mType = LST_STRING;
+ string3->mString = new char[size];
+ strcpy(string3->mString, test1);
+ strcat(string3->mString, test2);
+
+ delete string1;
+ delete string2;
+
+ return lsa_heap_add_data(buffer, string3, heapsize, TRUE);
+}
+
+S32 lsa_cmp_strings(U8 *buffer, S32 offset1, S32 offset2)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *string1;
+ LLScriptLibData *string2;
+
+ string1 = lsa_get_data(buffer, offset1, TRUE);
+ string2 = lsa_get_data(buffer, offset2, TRUE);
+
+ if ( (!string1)
+ ||(!string2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete string1;
+ delete string2;
+ return 0;
+ }
+
+ char *test1 = NULL, *test2 = NULL;
+
+ if (string1->mType == LST_STRING)
+ {
+ test1 = string1->mString;
+ }
+ else if (string1->mType == LST_KEY)
+ {
+ test1 = string1->mKey;
+ }
+ if (string2->mType == LST_STRING)
+ {
+ test2 = string2->mString;
+ }
+ else if (string2->mType == LST_KEY)
+ {
+ test2 = string2->mKey;
+ }
+
+ if ( (!test1)
+ ||(!test2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete string1;
+ delete string2;
+ return 0;
+ }
+ S32 retval = strcmp(test1, test2);
+
+ delete string1;
+ delete string2;
+
+ return retval;
+}
+
+void lsa_print_heap(U8 *buffer)
+{
+ S32 offset = get_register(buffer, LREG_HR);
+ S32 readoffset;
+ S32 ivalue;
+ F32 fpvalue;
+ LLVector3 vvalue;
+ LLQuaternion qvalue;
+ char string[4096];
+
+ LLScriptAllocEntry entry;
+
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ printf("HP: [0x%X]\n", get_register(buffer, LREG_HP));
+ printf("==========\n");
+
+ while (offset + entry.mSize < MAX_HEAP_SIZE)
+ {
+ printf("[0x%X] ", offset);
+ printf("%s ", LSCRIPTTypeNames[entry.mType]);
+ printf("Ref Count: %d ", entry.mReferenceCount);
+ printf("Size: %d = ", entry.mSize);
+
+ readoffset = offset;
+
+ switch(entry.mType)
+ {
+ case LST_INTEGER:
+ ivalue = bytestream2integer(buffer, readoffset);
+ printf("%d\n", ivalue);
+ break;
+ case LST_FLOATINGPOINT:
+ fpvalue = bytestream2float(buffer, readoffset);
+ printf("%f\n", fpvalue);
+ break;
+ case LST_STRING:
+ bytestream2char(string, buffer, readoffset);
+ printf("%s\n", string);
+ break;
+ case LST_KEY:
+ bytestream2char(string, buffer, readoffset);
+ printf("%s\n", string);
+ break;
+ case LST_VECTOR:
+ bytestream2vector(vvalue, buffer, readoffset);
+ printf("< %f, %f, %f >\n", vvalue.mV[VX], vvalue.mV[VY], vvalue.mV[VZ]);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(qvalue, buffer, readoffset);
+ printf("< %f, %f, %f, %f >\n", qvalue.mQ[VX], qvalue.mQ[VY], qvalue.mQ[VZ], qvalue.mQ[VS]);
+ break;
+ case LST_LIST:
+ ivalue = bytestream2integer(buffer, readoffset);
+ printf("%d\n", ivalue);
+ break;
+ default:
+ printf("\n");
+ break;
+ }
+ offset += entry.mSize;
+ bytestream2alloc_entry(entry, buffer, offset);
+ }
+ printf("[0x%X] ", offset);
+ printf("%s ", LSCRIPTTypeNames[entry.mType]);
+ printf("Ref Count: %d ", entry.mReferenceCount);
+ printf("Size: %d\n", entry.mSize);
+ printf("==========\n");
+}
+
+void lsa_fprint_heap(U8 *buffer, FILE *fp)
+{
+ S32 offset = get_register(buffer, LREG_HR);
+ S32 readoffset;
+ S32 ivalue;
+ F32 fpvalue;
+ LLVector3 vvalue;
+ LLQuaternion qvalue;
+ char string[4096];
+
+ LLScriptAllocEntry entry;
+
+ bytestream2alloc_entry(entry, buffer, offset);
+
+ while (offset + entry.mSize < MAX_HEAP_SIZE)
+ {
+ fprintf(fp, "[0x%X] ", offset);
+ fprintf(fp, "%s ", LSCRIPTTypeNames[entry.mType]);
+ fprintf(fp, "Ref Count: %d ", entry.mReferenceCount);
+ fprintf(fp, "Size: %d = ", entry.mSize);
+
+ readoffset = offset;
+
+ switch(entry.mType)
+ {
+ case LST_INTEGER:
+ ivalue = bytestream2integer(buffer, readoffset);
+ fprintf(fp, "%d\n", ivalue);
+ break;
+ case LST_FLOATINGPOINT:
+ fpvalue = bytestream2float(buffer, readoffset);
+ fprintf(fp, "%f\n", fpvalue);
+ break;
+ case LST_STRING:
+ bytestream2char(string, buffer, readoffset);
+ fprintf(fp, "%s\n", string);
+ break;
+ case LST_KEY:
+ bytestream2char(string, buffer, readoffset);
+ fprintf(fp, "%s\n", string);
+ break;
+ case LST_VECTOR:
+ bytestream2vector(vvalue, buffer, readoffset);
+ fprintf(fp, "< %f, %f, %f >\n", vvalue.mV[VX], vvalue.mV[VY], vvalue.mV[VZ]);
+ break;
+ case LST_QUATERNION:
+ bytestream2quaternion(qvalue, buffer, readoffset);
+ fprintf(fp, "< %f, %f, %f, %f >\n", qvalue.mQ[VX], qvalue.mQ[VY], qvalue.mQ[VZ], qvalue.mQ[VS]);
+ break;
+ case LST_LIST:
+ ivalue = bytestream2integer(buffer, readoffset);
+ fprintf(fp, "%d\n", ivalue);
+ break;
+ default:
+ fprintf(fp, "\n");
+ break;
+ }
+ offset += entry.mSize;
+ bytestream2alloc_entry(entry, buffer, offset);
+ }
+ fprintf(fp, "[0x%X] ", offset);
+ fprintf(fp, "%s ", LSCRIPTTypeNames[entry.mType]);
+ fprintf(fp, "Ref Count: %d ", entry.mReferenceCount);
+ fprintf(fp, "Size: %d", entry.mSize);
+ fprintf(fp, "\n");
+}
+
+S32 lsa_cat_lists(U8 *buffer, S32 offset1, S32 offset2, S32 heapsize)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *list1;
+ LLScriptLibData *list2;
+ if (offset1 != offset2)
+ {
+ list1 = lsa_get_data(buffer, offset1, TRUE);
+ list2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+ else
+ {
+ list1 = lsa_get_data(buffer, offset1, TRUE);
+ list2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+
+ if ( (!list1)
+ ||(!list2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ delete list2;
+ return 0;
+ }
+
+ if ( (list1->mType != LST_LIST)
+ ||(list2->mType != LST_LIST))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ delete list2;
+ return 0;
+ }
+
+ LLScriptLibData *runner = list1;
+
+ while (runner->mListp)
+ {
+ runner = runner->mListp;
+ }
+
+ runner->mListp = list2->mListp;
+
+ list2->mListp = NULL;
+
+ delete list2;
+
+ return lsa_heap_add_data(buffer, list1, heapsize, TRUE);
+}
+
+
+S32 lsa_cmp_lists(U8 *buffer, S32 offset1, S32 offset2)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *list1;
+ LLScriptLibData *list2;
+ if (offset1 != offset2)
+ {
+ list1 = lsa_get_data(buffer, offset1, TRUE);
+ list2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+ else
+ {
+ list1 = lsa_get_data(buffer, offset1, FALSE);
+ list2 = lsa_get_data(buffer, offset2, TRUE);
+ }
+
+ if ( (!list1)
+ ||(!list2))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ delete list2;
+ return 0;
+ }
+
+ if ( (list1->mType != LST_LIST)
+ ||(list2->mType != LST_LIST))
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ delete list2;
+ return 0;
+ }
+
+ S32 length1 = list1->getListLength();
+ S32 length2 = list2->getListLength();
+
+ if (length1 != length2)
+ {
+ return length1 - length2;
+ }
+
+ LLScriptLibData *runner1 = list1;
+ LLScriptLibData *runner2 = list2;
+
+ S32 count = 0;
+
+ while (runner1)
+ {
+ if (runner1->mType != runner2->mType)
+ return count;
+
+ switch(runner1->mType)
+ {
+ case LST_INTEGER:
+ if (runner1->mInteger != runner2->mInteger)
+ return count;
+ break;
+ case LST_FLOATINGPOINT:
+ if (runner1->mFP != runner2->mFP)
+ return count;
+ break;
+ case LST_KEY:
+ if (strcmp(runner1->mKey, runner2->mKey))
+ return count;
+ break;
+ case LST_STRING:
+ if (strcmp(runner1->mString, runner2->mString))
+ return count;
+ break;
+ case LST_VECTOR:
+ if (runner1->mVec != runner2->mVec)
+ return count;
+ case LST_QUATERNION:
+ if (runner1->mQuat != runner2->mQuat)
+ return count;
+ break;
+ default:
+ break;
+ }
+
+ runner1 = runner1->mListp;
+ runner2 = runner2->mListp;
+ }
+
+ delete list1;
+ delete list2;
+ return 0;
+}
+
+
+S32 lsa_preadd_lists(U8 *buffer, LLScriptLibData *data, S32 offset2, S32 heapsize)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *list2 = lsa_get_data(buffer, offset2, TRUE);
+
+ if (!list2)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list2;
+ return 0;
+ }
+
+ if (list2->mType != LST_LIST)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list2;
+ return 0;
+ }
+
+ LLScriptLibData *runner = data->mListp;
+
+ while (runner->mListp)
+ {
+ runner = runner->mListp;
+ }
+
+
+ runner->mListp = list2->mListp;
+ list2->mListp = data->mListp;
+
+ return lsa_heap_add_data(buffer, list2, heapsize, TRUE);
+}
+
+
+S32 lsa_postadd_lists(U8 *buffer, S32 offset1, LLScriptLibData *data, S32 heapsize)
+{
+ if (get_register(buffer, LREG_FR))
+ return 0;
+ LLScriptLibData *list1 = lsa_get_data(buffer, offset1, TRUE);
+
+ if (!list1)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ return 0;
+ }
+
+ if (list1->mType != LST_LIST)
+ {
+ set_fault(buffer, LSRF_HEAP_ERROR);
+ delete list1;
+ return 0;
+ }
+
+ LLScriptLibData *runner = list1;
+
+ while (runner->mListp)
+ {
+ runner = runner->mListp;
+ }
+
+ runner->mListp = data->mListp;
+
+ return lsa_heap_add_data(buffer, list1, heapsize, TRUE);
+}
+
diff --git a/indra/lscript/lscript_library/lscript_export.cpp b/indra/lscript/lscript_library/lscript_export.cpp
new file mode 100644
index 0000000000..a15c0e1586
--- /dev/null
+++ b/indra/lscript/lscript_library/lscript_export.cpp
@@ -0,0 +1,8 @@
+/**
+ * @file lscript_export.cpp
+ * @brief export interface class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
diff --git a/indra/lscript/lscript_library/lscript_library.cpp b/indra/lscript/lscript_library/lscript_library.cpp
new file mode 100644
index 0000000000..62183d079f
--- /dev/null
+++ b/indra/lscript/lscript_library/lscript_library.cpp
@@ -0,0 +1,557 @@
+/**
+ * @file lscript_library.cpp
+ * @brief external library interface
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// *WARNING*
+// When adding functions, they <b>MUST</b> be appended to the end of
+// the init() method. The init() associates the name with a number,
+// which is then serialized into the bytecode. Inserting a new
+// function in the middle will lead to many sim crashes. Phoenix 2006-04-10.
+
+#include "linden_common.h"
+
+#include "lscript_library.h"
+
+LLScriptLibrary::LLScriptLibrary()
+: mNextNumber(0), mFunctions(NULL)
+{
+ init();
+}
+
+LLScriptLibrary::~LLScriptLibrary()
+{
+ S32 i;
+ for (i = 0; i < mNextNumber; i++)
+ {
+ delete mFunctions[i];
+ mFunctions[i] = NULL;
+ }
+ delete [] mFunctions;
+}
+
+void dummy_func(LLScriptLibData *retval, LLScriptLibData *args, const LLUUID &id)
+{
+}
+
+void LLScriptLibrary::init()
+{
+ // IF YOU ADD NEW SCRIPT CALLS, YOU MUST PUT THEM AT THE END OF THIS LIST.
+ // Otherwise the bytecode numbers for each call will be wrong, and all
+ // existing scripts will crash.
+
+ // energy, sleep, dummy_func, name, return type, parameters, help text, gods-only
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSin", "f", "f", "float llSin(float theta)\ntheta in radians"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCos", "f", "f", "float llCos(float theta)\ntheta in radians"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTan", "f", "f", "float llTan(float theta)\ntheta radians"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAtan2", "f", "ff", "float llAtan2(float y, float x)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSqrt", "f", "f", "float llSqrt(float val)\nreturns 0 and triggers a Math Error for imaginary results"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPow", "f", "ff", "float llPow(float base, float exponent)\nreturns 0 and triggers Math Error for imaginary results"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAbs", "i", "i", "integer llAbs(integer val)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llFabs", "f", "f", "float llFabs(float val)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llFrand", "f", "f", "float llFrand(float mag)\nreturns random number in range [0,mag)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llFloor", "i", "f", "integer llFloor(float val)\nreturns largest integer value <= val"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCeil", "i", "f", "integer llCeil(float val)\nreturns smallest integer value >= val"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRound", "i", "f", "integer llRound(float val)\nreturns val rounded to the nearest integer"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llVecMag", "f", "v", "float llVecMag(vector v)\nreturns the magnitude of v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llVecNorm", "v", "v", "vector llVecNorm(vector v)\nreturns the v normalized"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llVecDist", "f", "vv", "float llVecDist(vector v1, vector v2)\nreturns the 3D distance between v1 and v2"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Euler", "v", "q", "vector llRot2Euler(rotation q)\nreturns the Euler representation (roll, pitch, yaw) of q"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llEuler2Rot", "q", "v", "rotation llEuler2Rot(vector v)\nreturns the rotation representation of Euler Angles v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAxes2Rot", "q", "vvv", "rotation llAxes2Rot(vector fwd, vector left, vector up)\nreturns the rotation defined by the coordinate axes"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Fwd", "v", "q", "vector llRot2Fwd(rotation q)\nreturns the forward vector defined by q"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Left", "v", "q", "vector llRot2Left(rotation q)\nreturns the left vector defined by q"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Up", "v", "q", "vector llRot2Up(rotation q)\nreturns the up vector defined by q"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRotBetween", "q", "vv", "rotation llRotBetween(vector v1, vector v2)\nreturns the rotation to rotate v1 to v2"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llWhisper", NULL, "is", "llWhisper(integer channel, string msg)\nwhispers msg on channel"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSay", NULL, "is", "llSay(integer channel, string msg)\nsays msg on channel"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llShout", NULL, "is", "llShout(integer channel, string msg)\nshouts msg on channel"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListen", "i", "isks", "integer llListen(integer channel, string name, key id, string msg)\nsets a callback for msg on channel from name and id (name, id, and/or msg can be empty) and returns an identifier that can be used to deactivate or remove the listen"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListenControl", NULL, "ii", "llListenControl(integer number, integer active)\nmakes a listen event callback active or inactive"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListenRemove", NULL, "i", "llListenRemove(integer number)\nremoves listen event callback number"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSensor", NULL, "skiff", "llSensor(string name, key id, integer type, float range, float arc)\nPerforms a single scan for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (name, id, and/or keytype can be empty or 0)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSensorRepeat", NULL, "skifff", "llSensorRepeat(string name, key id, integer type, float range, float arc, float rate)\nsets a callback for name and id with type (AGENT, ACTIVE, PASSIVE, and/or SCRIPTED) within range meters and arc radians of forward vector (name, id, and/or keytype can be empty or 0) and repeats every rate seconds"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSensorRemove", NULL, NULL, "llSensorRemove()\nremoves sensor"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedName", "s", "i", "string llDetectedName(integer number)\nreturns the name of detected object number (returns empty string if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedKey", "k", "i", "key llDetectedKey(integer number)\nreturns the key of detected object number (returns empty key if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedOwner", "k", "i", "key llDetectedOwner(integer number)\nreturns the key of detected object's owner (returns empty key if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedType", "i", "i", "integer llDetectedType(integer number)\nreturns the type (AGENT, ACTIVE, PASSIVE, SCRIPTED) of detected object (returns 0 if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedPos", "v", "i", "vector llDetectedPos(integer number)\nreturns the position of detected object number (returns <0,0,0> if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedVel", "v", "i", "vector llDetectedVel(integer number)\nreturns the velocity of detected object number (returns <0,0,0> if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedGrab", "v", "i", "vector llDetectedGrab(integer number)\nreturns the grab offset of the user touching object (returns <0,0,0> if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedRot", "q", "i", "rotation llDetectedRot(integer number)\nreturns the rotation of detected object number (returns <0,0,0,1> if number is not valid sensed object)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedGroup", "i", "i", "integer llDetectedGroup(integer number)\nReturns TRUE if detected object is part of same group as owner"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetectedLinkNumber", "i", "i", "integer llDetectedLinkNumber(integer number)\nreturns the link position of the triggered event for touches and collisions only"));
+ addFunction(new LLScriptLibraryFunction(0.f, 0.f, dummy_func, "llDie", NULL, NULL, "llDie()\ndeletes the object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGround", "f", "v", "float llGround(vector v)\nreturns the ground height below the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCloud", "f", "v", "float llCloud(vector v)\nreturns the cloud density at the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llWind", "v", "v", "vector llWind(vector v)\nreturns the wind velocity at the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetStatus", NULL, "ii", "llSetStatus(integer status, integer value)\nsets status (STATUS_PHYSICS, STATUS_PHANTOM, STATUS_BLOCK_GRAB,\nSTATUS_ROTATE_X, STATUS_ROTATE_Y, and/or STATUS_ROTATE_Z) to value"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetStatus", "i", "i", "integer llGetStatus(integer status)\ngets value of status (STATUS_PHYSICS, STATUS_PHANTOM, STATUS_BLOCK_GRAB,\nSTATUS_ROTATE_X, STATUS_ROTATE_Y, and/or STATUS_ROTATE_Z)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetScale", NULL, "v", "llSetScale(vector scale)\nsets the scale"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetScale", "v", NULL, "vector llGetScale()\ngets the scale"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetColor", NULL, "vi", "llSetColor(vector color, integer face)\nsets the color"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAlpha", "f", "i", "float llGetAlpha(integer face)\ngets the alpha"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetAlpha", NULL, "fi", "llSetAlpha(float alpha, integer face)\nsets the alpha"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetColor", "v", "i", "vector llGetColor(integer face)\ngets the color"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetTexture", NULL, "si", "llSetTexture(string texture, integer face)\nsets the texture of face"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llScaleTexture", NULL, "ffi", "llScaleTexture(float scales, float scalet, integer face)\nsets the texture s, t scales for the chosen face"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llOffsetTexture", NULL, "ffi", "llOffsetTexture(float offsets, float offsett, integer face)\nsets the texture s, t offsets for the chosen face"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llRotateTexture", NULL, "fi", "llOffsetTexture(float rotation, integer face)\nsets the texture rotation for the chosen face"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTexture", "s", "i", "string llGetTexture(integer face)\ngets the texture of face (if it's a texture in the object inventory, otherwise the key in a string)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetPos", NULL, "v", "llSetPos(vector pos)\nsets the position (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetPos", "v", NULL, "vector llGetPos()\ngets the position (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLocalPos", "v", NULL, "vector llGetLocalPos()\ngets the position relative to the root (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetRot", NULL, "q", "llSetRot(rotation rot)\nsets the rotation (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRot", "q", NULL, "rotation llGetRot()\ngets the rotation (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLocalRot", "q", NULL, "rotation llGetLocalRot()\ngets the rotation local to the root (if the script isn't physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetForce", NULL, "vi", "llSetForce(vector force, integer local)\nsets force on object, in local coords if local == TRUE (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetForce", "v", NULL, "vector llGetForce()\ngets the force (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTarget", "i", "vf", "integer llTarget(vector position, float range)\nset positions within range of position as a target and return an ID for the target"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTargetRemove", NULL, "i", "llTargetRemove(integer number)\nremoves target number"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRotTarget", "i", "qf", "integer llRotTarget(rotation rot, float error)\nset rotations with error of rot as a rotational target and return an ID for the rotational target"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRotTargetRemove", NULL, "i", "llRotTargetRemove(integer number)\nremoves rotational target number"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llMoveToTarget", NULL, "vf", "llMoveToTarget(vector target, float tau)\ncritically damp to target in tau seconds (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopMoveToTarget", NULL, NULL, "llStopMoveToTarget()\nStops critically damped motion"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llApplyImpulse", NULL, "vi", "llApplyImpulse(vector force, integer local)\napplies impulse to object, in local coords if local == TRUE (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llApplyRotationalImpulse", NULL, "vi", "llApplyRotationalImpulse(vector force, integer local)\napplies rotational impulse to object, in local coords if local == TRUE (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetTorque", NULL, "vi", "llSetTorque(vector torque, integer local)\nsets the torque of object, in local coords if local == TRUE (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTorque", "v", NULL, "vector llGetTorque()\ngets the torque (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetForceAndTorque", NULL, "vvi", "llSetForceAndTorque(vector force, vector torque, integer local)\nsets the force and torque of object, in local coords if local == TRUE (if the script is physical)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetVel", "v", NULL, "vector llGetVel()\ngets the velocity"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAccel", "v", NULL, "vector llGetAccel()\ngets the acceleration"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetOmega", "v", NULL, "vector llGetOmega()\ngets the omega"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTimeOfDay", "f", "", "float llGetTimeOfDay()\ngets the time in seconds since Second Life server midnight (or since server up-time; whichever is smaller)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetWallclock", "f", "", "float llGetWallclock()\ngets the time in seconds since midnight"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTime", "f", NULL, "float llGetTime()\ngets the time in seconds since creation"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llResetTime", NULL, NULL, "llResetTime()\nsets the time to zero"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAndResetTime", "f", NULL, "float llGetAndResetTime()\ngets the time in seconds since creation and sets the time to zero"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSound", NULL, "sfii", "llSound(string sound, float volume, integer queue, integer loop)\nplays sound at volume and whether it should loop or not"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPlaySound", NULL, "sf", "llPlaySound(string sound, float volume)\nplays attached sound once at volume (0.0 - 1.0)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLoopSound", NULL, "sf", "llLoopSound(string sound, float volume)\nplays attached sound looping indefinitely at volume (0.0 - 1.0)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLoopSoundMaster", NULL, "sf", "llLoopSoundMaster(string sound, float volume)\nplays attached sound looping at volume (0.0 - 1.0), declares it a sync master"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLoopSoundSlave", NULL, "sf", "llLoopSoundSlave(string sound, float volume)\nplays attached sound looping at volume (0.0 - 1.0), synced to most audible sync master"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPlaySoundSlave", NULL, "sf", "llPlaySoundSlave(string sound, float volume)\nplays attached sound once at volume (0.0 - 1.0), synced to next loop of most audible sync master"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTriggerSound", NULL, "sf", "llTriggerSound(string sound, float volume)\nplays sound at volume (0.0 - 1.0), centered at but not attached to object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopSound", NULL, "", "llStopSound()\nStops currently attached sound"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llPreloadSound", NULL, "s", "llPreloadSound(string sound)\npreloads a sound on viewers within range"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetSubString", "s", "sii", "string llGetSubString(string src, integer start, integer end)\nreturns the indicated substring"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDeleteSubString", "s", "sii", "string llDeleteSubString(string src, integer start, integer end)\nremoves the indicated substring and returns the result"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llInsertString", "s", "sis", "string llInsertString(string dst, integer position, string src)\ninserts src into dst at position and returns the result"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llToUpper", "s", "s", "string llToUpper(string src)\nconvert src to all upper case and returns the result"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llToLower", "s", "s", "string llToLower(string src)\nconvert src to all lower case and returns the result"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGiveMoney", "i", "ki", "llGiveMoney(key destination, integer amount)\ntransfer amount of money from script owner to destination"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llMakeExplosion", NULL, "iffffsv", "llMakeExplosion(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset)\nMake a round explosion of particles"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llMakeFountain", NULL, "iffffisvf", "llMakeFountain(integer particles, float scale, float vel, float lifetime, float arc, integer bounce, string texture, vector offset, float bounce_offset)\nMake a fountain of particles"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llMakeSmoke", NULL, "iffffsv", "llMakeSmoke(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset)\nMake smoke like particles"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llMakeFire", NULL, "iffffsv", "llMakeFire(integer particles, float scale, float vel, float lifetime, float arc, string texture, vector offset)\nMake fire like particles"));
+ addFunction(new LLScriptLibraryFunction(200.f, 0.1f, dummy_func, "llRezObject", NULL, "svvqi", "llRezObject(string inventory, vector pos, vector vel, rotation rot, integer param)\nInstanciate owners inventory object at pos with velocity vel and rotation rot with start parameter param"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLookAt", NULL, "vff", "llLookAt(vector target, F32 strength, F32 damping)\nCause object name to point it's forward axis towards target"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopLookAt", NULL, NULL, "llStopLookAt()\nStop causing object name to point at a target"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetTimerEvent", NULL, "f", "llSetTimerEvent(float sec)\nCause the timer event to be triggered every sec seconds"));
+ addFunction(new LLScriptLibraryFunction(0.f, 0.f, dummy_func, "llSleep", NULL, "f", "llSleep(float sec)\nPut script to sleep for sec seconds"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetMass", "f", NULL, "float llGetMass()\nGet the mass of task name that script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCollisionFilter", NULL, "ski", "llCollisionFilter(string name, key id, integer accept)\nif accept == TRUE, only accept collisions with objects name and id (either is optional), otherwise with objects not name or id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTakeControls", NULL, "iii", "llTakeControls(integer controls, integer accept, integer pass_on)\nTake controls from agent task has permissions for. If (accept == (controls & input)), send input to task. If pass_on send to agent also."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llReleaseControls", NULL, NULL, "llReleaseControls()\nStop taking inputs"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAttachToAvatar", NULL, "i", "llAttachToAvatar(integer attachment)\nAttach to avatar task has permissions for at point attachment"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDetachFromAvatar", NULL, NULL, "llDetachFromAvatar()\nDrop off of avatar"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTakeCamera", NULL, "k", "llTakeCamera(key avatar)\nMove avatar's viewpoint to task"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llReleaseCamera", NULL, "k", "llReleaseCamera(key avatar)\nReturn camera to agent"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetOwner", "k", NULL, "key llGetOwner()\nReturns the owner of the task"));
+ addFunction(new LLScriptLibraryFunction(10.f, 2.f, dummy_func, "llInstantMessage", NULL, "ks", "llInstantMessage(key user, string message)\nIMs message to the user"));
+ addFunction(new LLScriptLibraryFunction(10.f, 20.f, dummy_func, "llEmail", NULL, "sss", "llEmail(string address, string subject, string message)\nSends email to address with subject and message"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetNextEmail", NULL, "ss", "llGetNextEmail(string address, string subject)\nGet the next waiting email with appropriate address and/or subject (if blank they are ignored)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetKey", "k", NULL, "key llGetKey()\nGet the key for the task the script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetBuoyancy", NULL, "f", "llSetBuoyancy(float buoyancy)\nSet the tasks buoyancy (0 is none, < 1.0 sinks, 1.0 floats, > 1.0 rises)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetHoverHeight", NULL, "fif", "llSetHoverHeight(float height, integer water, float tau)\nCritically damps to a height (either above ground level or above the higher of land and water if water == TRUE)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopHover", NULL, NULL, "llStopHover()\nStop hovering to a height"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llMinEventDelay", NULL, "f", "llMinEventDelay(float delay)\nSet the minimum time between events being handled"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSoundPreload", NULL, "s", "llSoundPreload(string sound)\npreloads a sound on viewers within range"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRotLookAt", NULL, "qff", "llRotLookAt(rotation target, F32 strength, F32 damping)\nCause object name to point it's forward axis towards target"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStringLength", "i", "s", "integer llStringLength(string str)\nReturns the length of string"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStartAnimation", NULL, "s", "llStartAnimation(string anim)\nStart animation anim for agent that owns object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopAnimation", NULL, "s", "llStopAnimation(string anim)\nStop animation anim for agent that owns object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPointAt", NULL, "v", "llPointAt(vector pos)\nMake agent that owns object point at pos"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStopPointAt", NULL, NULL, "llStopPointAt()\nStop agent that owns object pointing"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTargetOmega", NULL, "vff", "llTargetOmega(vector axis, float spinrate, float gain)\nAttempt to spin at spinrate with strength gain"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetStartParameter", "i", NULL, "integer llGetStartParameter()\nGet's the start paramter passed to llRezObject"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGodLikeRezObject", NULL, "kv", "llGodLikeRezObject(key inventory, vector pos)\nrez directly off of a UUID if owner has dog-bit set", TRUE));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRequestPermissions", NULL, "ki", "llRequestPermissions(key agent, integer perm)\nask agent to allow the script to do perm (NB: Debit, ownership, link, joint, and permission requests can only go to the task's owner)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetPermissionsKey", "k", NULL, "key llGetPermissionsKey()\nReturn agent that permissions are enabled for. NULL_KEY if not enabled"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetPermissions", "i", NULL, "integer llGetPermissions()\nreturn what permissions have been enabled"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLinkNumber", "i", NULL, "integer llGetLinkNumber()\nReturns what number in a link set the script is attached to (0 means no link, 1 the root, 2 for first child, &c)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetLinkColor", NULL, "ivi", "llSetLinkColor(integer linknumber, vector color, integer face)\nIf a task exists in the link chain at linknumber, set face to color"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llCreateLink", NULL, "ki", "llCreateLink(key target, integer parent)\nAttempt to link task script is attached to and target (requires permission PERMISSION_CHANGE_LINKS be set). If parent == TRUE, task script is attached to is the root"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llBreakLink", NULL, "i", "llBreakLink(integer linknum)\nDelinks the task with the given link number (requires permission PERMISSION_CHANGE_LINKS be set)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llBreakAllLinks", NULL, NULL, "llBreakAllLinks()\nDelinks all tasks in the link set (requires permission PERMISSION_CHANGE_LINKS be set)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLinkKey", "k", "i", "key llGetLinkKey(integer linknum)\nGet the key of linknumber in link set"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLinkName", "s", "i", "string llGetLinkName(integer linknum)\nGet the name of linknumber in link set"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryNumber", "i", "i", "integer llGetInventoryNumber(integer type)\nGet the number of items of a given type in the task's inventory.\nValid types: INVENTORY_TEXTURE, INVENTORY_SOUND, INVENTORY_OBJECT, INVENTORY_SCRIPT, INVENTORY_CLOTHING, INVENTORY_BODYPART, INVENTORY_NOTECARD, INVENTORY_LANDMARK, INVENTORY_ALL"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryName", "s", "ii", "string llGetInventoryName(integer type, integer number)\nGet the name of the inventory item number of type"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetScriptState", NULL, "si", "llSetScriptState(string name, integer run)\nControl the state of a script name."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetEnergy", "f", NULL, "float llGetEnergy()\nReturns how much energy is in the object as a percentage of maximum"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGiveInventory", NULL, "ks", "llGiveInventory(key destination, string inventory)\nGive inventory to destination"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRemoveInventory", NULL, "s", "llRemoveInventory(string inventory)\nRemove the named inventory item"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetText", NULL, "svf", "llSetText(string text, vector color, float alpha)\nSet text floating over object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llWater", "f", "v", "float llWater(vector v)\nreturns the water height below the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPassTouches", NULL, "i", "llPassTouches(integer pass)\nif pass == TRUE, touches are passed from children on to parents (default is FALSE)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llRequestAgentData", "k", "ki", "key llRequestAgentData(key id, integer data)\nRequests data about agent id. When data is available the dataserver event will be raised"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llRequestInventoryData", "k", "s", "key llRequestInventoryData(string name)\nRequests data from object's inventory object. When data is available the dataserver event will be raised"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetDamage", NULL, "f", "llSetDamage(float damage)\nSets the amount of damage that will be done to an object that this task hits. Task will be killed."));
+ addFunction(new LLScriptLibraryFunction(100.f, 5.f, dummy_func, "llTeleportAgentHome", NULL, "k", "llTeleportAgentHome(key id)\nTeleports agent on owner's land to agent's home location"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llModifyLand", NULL, "ii", "llModifyLand(integer action, integer size)\nModify land with action (LAND_LEVEL, LAND_RAISE, LAND_LOWER, LAND_SMOOTH, LAND_NOISE, LAND_REVERT)\non size (LAND_SMALL_BRUSH, LAND_MEDIUM_BRUSH, LAND_LARGE_BRUSH)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCollisionSound", NULL, "sf", "llCollisionSound(string impact_sound, float impact_volume)\nSuppress default collision sounds, replace default impact sounds with impact_sound (empty string to just suppress)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCollisionSprite", NULL, "s", "llCollisionSprite(string impact_sprite)\nSuppress default collision sprites, replace default impact sprite with impact_sprite (empty string to just suppress)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAnimation", "s", "k", "string llGetAnimation(key id)\nGet the currently playing locomotion animation for avatar id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llResetScript", NULL, NULL, "llResetScript()\nResets the script"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llMessageLinked", NULL, "iisk", "llMessageLinked(integer linknum, integer num, string str, key id)\nSends num, str, and id to members of the link set(LINK_SET sends to all tasks,\nLINK_ALL_OTHERS to all other tasks,\nLINK_ALL_CHILDREN to all children"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPushObject", NULL, "kvvi", "llPushObject(key id, vector impulse, vector ang_impulse, integer local)\nApplies impulse and ang_impulse to object id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llPassCollisions", NULL, "i", "llPassCollisions(integer pass)\nif pass == TRUE, collisions are passed from children on to parents (default is FALSE)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetScriptName", "s", NULL, "llGetScriptName()\nReturns the script name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetNumberOfSides", "i", NULL, "integer llGetNumberOfSides()\nReturns the number of sides"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAxisAngle2Rot", "q", "vf", "rotation llAxisAngle2Rot(vector axis, float angle)\nReturns the rotation generated angle about axis"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Axis", "v", "q", "vector llRot2Axis(rotation rot)\nReturns the rotation axis represented by rot"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRot2Angle", "f", "q", "float llRot2Angle(rotation rot)\nReturns the rotation angle represented by rot"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAcos", "f", "f", "float llAcos(float val)\nReturns the arccosine in radians of val"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAsin", "f", "f", "float llAsin(float val)\nReturns the arcsine in radians of val"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAngleBetween", "f", "qq", "float llAngleBetween(rotation a, rotation b)\nReturns angle between rotation a and b"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryKey", "k", "s", "key llGetInventoryKey(string name)\nReturns the key of the inventory name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAllowInventoryDrop", NULL, "i", "llAllowInventoryDrop(integer add)\nIf add == TRUE, users without permissions can still drop inventory items onto task"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetSunDirection", "v", NULL, "vector llGetSunDirection()\nReturns the sun direction on the simulator"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTextureOffset", "v", "i", "vector llGetTextureOffset(integer side)\nReturns the texture offset of side in the x and y components of a vector"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTextureScale", "v", "i", "vector llGetTextureScale(integer side)\nReturns the texture scale of side in the x and y components of a vector"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTextureRot", "f", "i", "float llGetTextureRot(integer side)\nReturns the texture rotation of side"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSubStringIndex", "i", "ss", "integer llSubStringIndex(string source, string pattern)\nFinds index in source where pattern first appears (returns -1 if not found)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetOwnerKey", "k", "k", "key llGetOwnerKey(key id)\nFind the owner of id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetCenterOfMass", "v", NULL, "vector llGetCenterOfMass()\nGet the object's center of mass"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListSort", "l", "lii", "list llListSort(list src, integer stride, integer ascending)\nSort the list into blocks of stride in ascending order if ascending == TRUE. Note that sort only works between same types."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetListLength", "i", "l", "integer llGetListLength(list src)\nGet the number of elements in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2Integer", "i", "li", "integer llList2Integer(list src, integer index)\nCopy the integer at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2Float", "f", "li", "float llList2Float(list src, integer index)\nCopy the float at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2String", "s", "li", "string llList2String(list src, integer index)\nCopy the string at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2Key", "k", "li", "key llList2Key(list src, integer index)\nCopy the key at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2Vector", "v", "li", "vector llList2Vector(list src, integer index)\nCopy the vector at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2Rot", "q", "li", "rotation llList2Rot(list src, integer index)\nCopy the rotation at index in the list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2List", "l", "lii", "list llList2List(list src, integer start, integer end)\nCopy the slice of the list from start to end"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDeleteSubList", "l", "lii", "list llDeleteSubList(list src, integer start, integer end)\nRemove the slice from the list and return the remainder"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetListEntryType", "i", "li", "integer llGetListEntryType(list src, integer index)\nReturns the type of the index entry in the list\n(TYPE_INTEGER, TYPE_FLOAT, TYPE_STRING, TYPE_KEY, TYPE_VECTOR, TYPE_ROTATION, or TYPE_INVALID if index is off list)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2CSV", "s", "l", "string llList2CSV(list src)\nCreate a string of comma separated values from list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llCSV2List", "l", "s", "list llCSV2List(string src)\nCreate a list from a string of comma separated values"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListRandomize", "l", "li", "list llListRandomize(list src, integer stride)\nReturns a randomized list of blocks of size stride"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llList2ListStrided", "l", "liii", "list llList2ListStrided(list src, integer start, integer end, integer stride)\nCopy the strided slice of the list from start to end"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionCorner", "v", NULL, "vector llGetRegionCorner()\nReturns a vector with the south west corner x,y position of the region the object is in"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListInsertList", "l", "lli", "list llListInsertList(list dest, list src, integer start)\nInserts src into dest at position start"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListFindList", "i", "ll", "integer llListFindList(list src, list test)\nReturns the start of the first instance of test in src, -1 if not found"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetObjectName", "s", NULL, "string llGetObjectName()\nReturns the name of the object script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetObjectName", NULL, "s", "llSetObjectName(string name)\nSets the objects name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetDate", "s", NULL, "string llGetDate()\nGets the date as YYYY-MM-DD"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llEdgeOfWorld", "i", "vv", "integer llEdgeOfWorld(vector pos, vector dir)\nChecks to see whether the border hit by dir from pos is the edge of the world (has no neighboring simulator)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAgentInfo", "i", "k", "integer llGetAgentInfo(key id)\nGets information about agent ID.\nReturns AGENT_FLYING, AGENT_ATTACHMENTS, AGENT_SCRIPTED, AGENT_SITTING, AGENT_ON_OBJECT, AGENT_MOUSELOOK, AGENT_AWAY, AGENT_BUSY, AGENT_TYPING, AGENT_CROUCHING, AGENT_ALWAYS_RUN, AGENT_WALKING and/or AGENT_IN_AIR."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llAdjustSoundVolume", NULL, "f", "llAdjustSoundVolume(float volume)\nadjusts volume of attached sound (0.0 - 1.0)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetSoundQueueing", NULL, "i", "llSetSoundQueueing(integer queue)\ndetermines whether attached sound calls wait for the current sound to finish (0 = no [default], nonzero = yes)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetSoundRadius", NULL, "f", "llSetSoundRadius(float radius)\nestablishes a hard cut-off radius for audibility of scripted sounds (both attached and triggered)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llKey2Name", "s", "k", "string llKey2Name(key id)\nReturns the name of the object key, iff the object is in the current simulator, otherwise the empty string"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetTextureAnim", NULL, "iiiifff", "llSetTextureAnim(integer mode, integer face, integer sizex, integer sizey, float start, float length, float rate)\nAnimate the texture on the specified face/faces"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llTriggerSoundLimited", NULL, "sfvv", "llTriggerSoundLimited(string sound, float volume, vector tne, vector bsw)\nplays sound at volume (0.0 - 1.0), centered at but not attached to object, limited to AABB defined by vectors top-north-east and bottom-south-west"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llEjectFromLand", NULL, "k", "llEjectFromLand(key pest)\nEjects pest from land that you own"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llParseString2List", "l", "sll", "list llParseString2List(string src, list separators, list spacers)\nBreaks src into a list, discarding separators, keeping spacers (separators and spacers must be lists of strings, maximum of 8 each)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llOverMyLand", "i", "k", "integer llOverMyLand(key id)\nReturns TRUE if id is over land owner of object owns, FALSE otherwise"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetLandOwnerAt", "k", "v", "key llGetLandOwnerAt(vector pos)\nReturns the key of the land owner, NULL_KEY if public"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llGetNotecardLine", "k", "si", "key llGetNotecardLine(string name, integer line)\nReturns line line of notecard name via the dataserver event"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAgentSize", "v", "k", "vector llGetAgentSize(key id)\nIf the agent is in the same sim as the object, returns the size of the avatar"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSameGroup", "i", "k", "integer llSameGroup(key id)\nReturns TRUE if ID is in the same sim and has the same active group, otherwise FALSE"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llUnSit", NULL, "k", "key llUnSit(key id)\nIf agent identified by id is sitting on the object the script is attached to or is over land owned by the objects owner, the agent is forced to stand up"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGroundSlope", "v", "v", "vector llGroundSlope(vector v)\nreturns the ground slope below the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGroundNormal", "v", "v", "vector llGroundNormal(vector v)\nreturns the ground normal below the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGroundContour", "v", "v", "vector llGroundCountour(vector v)\nreturns the ground contour below the object position + v"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAttached", "i", NULL, "integer llGetAttached()\nreturns the object attachment point or 0 if not attached"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetFreeMemory", "i", NULL, "integer llGetFreeMemory()\nreturns the available heap space for the current script"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionName", "s", NULL, "string llGetRegionName()\nreturns the current region name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionTimeDilation", "f", NULL, "float llGetRegionTimeDilation()\nreturns the current time dilation as a float between 0 and 1"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionFPS", "f", NULL, "float llGetRegionFPS()\nreturns the mean region frames per second"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llParticleSystem", NULL, "l", "llParticleSystem(list rules)\nCreates a particle system based on rules. Empty list removes particle system from object.\nList format is [ rule1, data1, rule2, data2 . . . rulen, datan ]"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGroundRepel", NULL, "fif", "llGroundRepel(float height, integer water, float tau)\nCritically damps to height if within height*0.5 of level (either above ground level or above the higher of land and water if water == TRUE)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 3.f, dummy_func, "llGiveInventoryList", NULL, "ksl", "llGiveInventoryList(key destination, string category, list inventory)\nGive inventory to destination in a new category"));
+
+// script calls for vehicle action
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetVehicleType", NULL, "i", "llSetVehicleType(integer type)\nsets vehicle to one of the default types"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetVehicleFloatParam", NULL, "if", "llSetVehicleFloatParam(integer param, float value)\nsets the specified vehicle float parameter"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetVehicleVectorParam", NULL, "iv", "llSetVehicleVectorParam(integer param, vector vec)\nsets the specified vehicle vector parameter"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetVehicleRotationParam", NULL, "iq", "llSetVehicleVectorParam(integer param, rotation rot)\nsets the specified vehicle rotation parameter"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetVehicleFlags", NULL, "i", "llSetVehicleFlags(integer flags)\nsets the enabled bits in 'flags'"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRemoveVehicleFlags", NULL, "i", "llRemoveVehicleFlags(integer flags)\nremoves the enabled bits in 'flags'"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSitTarget", NULL, "vq", "llSitTarget(vector offset, rotation rot)\nSet the sit location for this object (if offset == <0,0,0> clear it)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llAvatarOnSitTarget", "k", NULL, "key llAvatarOnSitTarget()\nIf an avatar is sitting on the sit target, return the avatar's key, NULL_KEY otherwise"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llAddToLandPassList", NULL, "kf", "llAddToLandPassList(key avatar, float hours)\nAdd avatar to the land pass list for hours"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetTouchText", NULL, "s", "llSetTouchText(string text)\nDisplays text in pie menu that acts as a touch"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetSitText", NULL, "s", "llSetSitText(string text)\nDisplays text rather than sit in pie menu"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCameraEyeOffset", NULL, "v", "llSetCameraEyeOffset(vector offset)\nSets the camera eye offset used in this object if an avatar sits on it"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCameraAtOffset", NULL, "v", "llSetCameraAtOffset(vector offset)\nSets the camera at offset used in this object if an avatar sits on it"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llDumpList2String", "s", "ls", "string llDumpList2String(list src, string separator)\nWrite the list out in a single string using separator between values"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llScriptDanger", "i", "v", "integer llScriptDanger(vector pos)\nReturns true if pos is over public land, sandbox land, land that doesn't allow everyone to edit and build, or land that doesn't allow outside scripts"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llDialog", NULL, "ksli", "llDialog(key avatar, string message, list buttons, integer chat_channel\nShows a dialog box on the avatar's screen with the message.\nUp to 12 strings in the list form buttons.\nIf a button is clicked, the name is chatted on chat_channel."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llVolumeDetect", NULL, "i", "llVolumeDetect(integer detect)\nIf detect = TRUE, object becomes phantom but triggers collision_start and collision_end events\nwhen other objects start and stop interpenetrating.\nMust be applied to the root object."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llResetOtherScript", NULL, "s", "llResetOtherScript(string name)\nResets script name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetScriptState", "i", "s", "integer llGetScriptState(string name)\nResets TRUE if script name is running"));
+ addFunction(new LLScriptLibraryFunction(10.f, 3.f, dummy_func, "llRemoteLoadScript", NULL, "ksii", "Deprecated. Please use llRemoteLoadScriptPin instead."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetRemoteScriptAccessPin", NULL, "i", "llSetRemoteScriptAccessPin(integer pin)\nIf pin is set to a non-zero number, the task will accept remote script\nloads via llRemoteLoadScriptPin if it passes in the correct pin.\nOthersise, llRemoteLoadScriptPin is ignored."));
+ addFunction(new LLScriptLibraryFunction(10.f, 3.f, dummy_func, "llRemoteLoadScriptPin", NULL, "ksiii", "llRemoteLoadScriptPin(key target, string name, integer pin, integer running, integer start_param)\nIf the owner of the object this script is attached can modify target,\nthey are in the same region,\nand the matching pin is used,\ncopy script name onto target,\nif running == TRUE, start the script with param."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llOpenRemoteDataChannel", NULL, NULL, "llOpenRemoteDataChannel()\nCreates a channel to listen for XML-RPC calls. Will trigger a remote_data event with channel id once it is available."));
+ addFunction(new LLScriptLibraryFunction(10.f, 3.f, dummy_func, "llSendRemoteData", "k", "ksis", "key llSendRemoteData(key channel, string dest, integer idata, string sdata)\nSend an XML-RPC request to dest through channel with payload of channel (in a string), integer idata and string sdata.\nA message identifier key is returned.\nAn XML-RPC reply will trigger a remote_data event and reference the message id.\nThe message_id is returned."));
+ addFunction(new LLScriptLibraryFunction(10.f, 3.f, dummy_func, "llRemoteDataReply", NULL, "kksi", "llRemoteDataReply(key channel, key message_id, string sdata, integer idata)\nSend an XML-RPC reply to message_id on channel with payload of string sdata and integer idata"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llCloseRemoteDataChannel", NULL, "k", "llCloseRemoteDataChannel(key channel)\nCloses XML-RPC channel."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llMD5String", "s", "si", "string llMD5String(string src, integer nonce)\nPerforms a RSA Data Security, Inc. MD5 Message-Digest Algorithm on string with nonce. Returns a 32 character hex string."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetPrimitiveParams", NULL, "l", "llSetPrimitiveParams(list rules)\nSet primitive parameters based on rules."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llStringToBase64", "s", "s", "string llStringToBase64(string str)\nConverts a string to the Base 64 representation of the string."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llBase64ToString", "s", "s", "string llBase64ToString(string str)\nConverts a Base 64 string to a conventional string. If the conversion creates any unprintable characters, they are converted to spaces."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.3f, dummy_func, "llXorBase64Strings", "s", "ss", "string llXorBase64Strings(string s1, string s2)\nDEPRECATED! Please use llXorBase64StringsCorrect instead!! Incorrectly performs an exclusive or on two Base 64 strings and returns a Base 64 string. s2 repeats if it is shorter than s1. Retained for backwards compatability."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llRemoteDataSetRegion", NULL, NULL, "llRemoteDataSetRegion()\nIf an object using remote data channels changes regions, you must call this function to reregister the remote data channels.\nYou do not need to make this call if you don't change regions."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLog10", "f", "f", "float llLog10(float val)\nReturns the base 10 log of val if val > 0, otherwise returns 0."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llLog", "f", "f", "float llLog(float val)\nReturns the base e log of val if val > 0, otherwise returns 0."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetAnimationList", "l", "k", "list llGetAnimationList(key id)\nGets a list of all playing animations for avatar id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 2.f, dummy_func, "llSetParcelMusicURL", NULL, "s", "llSetParcelMusicURL(string url)\nSets the streaming audio URL for the parcel object is on"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRootPosition", "v", NULL, "vector llGetRootPosition()\nGets the global position of the root object of the object script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRootRotation", "q", NULL, "rotation llGetRootRotation()\nGets the global rotation of the root object of the object script is attached to"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetObjectDesc", "s", NULL, "string llGetObjectDesc()\nReturns the description of the object the script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetObjectDesc", NULL, "s", "llSetObjectDesc(string name)\nSets the object's description"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetCreator", "k", NULL, "key llGetCreator()\nReturns the creator of the object"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetTimestamp", "s", NULL, "string llGetTimestamp()\nGets the timestamp in the format: YYYY-MM-DDThh:mm:ss.ff..fZ"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetLinkAlpha", NULL, "ifi", "llSetLinkAlpha(integer linknumber, float alpha, integer face)\nIf a prim exists in the link chain at linknumber, set face to alpha"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetNumberOfPrims", "i", NULL, "integer llGetNumberOfPrims()\nReturns the number of prims in a link set the script is attached to"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llGetNumberOfNotecardLines", "k", "s", "key llGetNumberOfNotecardLines(string name)\nReturns number of lines in notecard 'name' via the dataserver event (cast return value to integer)"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetBoundingBox", "l", "k", "list llGetBoundingBox(key object)\nReturns the bounding box around an object (including any linked prims) relative to the root prim, in a list: [ (vector) min_corner, (vector) max_corner ]"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetGeometricCenter", "v", NULL, "vector llGetGeometricCenter()\nReturns the geometric center of the linked set the script is attached to."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llGetPrimitiveParams", "l", "l", "list llGetPrimitiveParams(list params)\nGets primitive parameters specified in the params list."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.0f, dummy_func, "llIntegerToBase64", "s", "i", "string llIntegerToBase64(integer number)\nBig endian encode of of integer as a Base64 string."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.0f, dummy_func, "llBase64ToInteger", "i", "s", "integer llBase64ToInteger(string str)\nBig endian decode of a Base64 string into an integer."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetGMTclock", "f", "", "float llGetGMTclock()\nGets the time in seconds since midnight in GMT"));
+ addFunction(new LLScriptLibraryFunction(10.f, 10.f, dummy_func, "llGetSimulatorHostname", "s", "", "string llGetSimulatorHostname()\nGets the hostname of the machine script is running on (same as string in viewer Help dialog)"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetLocalRot", NULL, "q", "llSetLocalRot(rotation rot)\nsets the rotation of a child prim relative to the root prim"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llParseStringKeepNulls", "l", "sll", "list llParseStringKeepNulls(string src, list separators, list spacers)\nBreaks src into a list, discarding separators, keeping spacers (separators and spacers must be lists of strings, maximum of 8 each), keeping any null values generated."));
+ addFunction(new LLScriptLibraryFunction(200.f, 0.1f, dummy_func, "llRezAtRoot", NULL, "svvqi", "llRezAtRoot(string inventory, vector pos, vector vel, rotation rot, integer param)\nInstantiate owner's inventory object at pos with velocity vel and rotation rot with start parameter param.\nThe last selected root object's location will be set to pos"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetObjectPermMask", "i", "i", "integer llGetObjectPermMask(integer mask)\nReturns the requested permission mask for the root object the task is attached to.", FALSE));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetObjectPermMask", NULL, "ii", "llSetObjectPermMask(integer mask, integer value)\nSets the given permission mask to the new value on the root object the task is attached to.", TRUE));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryPermMask", "i", "si", "integer llGetInventoryPermMask(string item, integer mask)\nReturns the requested permission mask for the inventory item.", FALSE));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetInventoryPermMask", NULL, "sii", "llSetInventoryPermMask(string item, integer mask, integer value)\nSets the given permission mask to the new value on the inventory item.", TRUE));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryCreator", "k", "s", "key llGetInventoryCreator(string item)\nReturns the key for the creator of the inventory item.", FALSE));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llOwnerSay", NULL, "s", "llOwnerSay(string msg)\nsays msg to owner only (if owner in sim)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llRequestSimulatorData", "k", "si", "key llRequestSimulatorData(string simulator, integer data)\nRequests data about simulator. When data is available the dataserver event will be raised"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llForceMouselook", NULL, "i", "llForceMouselook(integer mouselook)\nIf mouselook is TRUE any avatar that sits on this object is forced into mouselook mode"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetObjectMass", "f", "k", "float llGetObjectMass(key id)\nGet the mass of the object with key id"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListReplaceList", "l", "llii", "list llListReplaceList(list dest, list src, integer start, integer end)\nReplaces start through end of dest with src."));
+ addFunction(new LLScriptLibraryFunction(10.f, 10.f, dummy_func, "llLoadURL", NULL, "kss", "llLoadURL(key avatar_id, string message, string url)\nShows dialog to avatar avatar_id offering to load web page at URL. If user clicks yes, launches their web browser."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 2.f, dummy_func, "llParcelMediaCommandList", NULL, "l", "llParcelMediaCommandList(list command)\nSends a list of commands, some with arguments, to a parcel."));
+ addFunction(new LLScriptLibraryFunction(10.f, 2.f, dummy_func, "llParcelMediaQuery", "l", "l", "list llParcelMediaQuery(list query)\nSends a list of queries, returns a list of results."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llModPow", "i", "iii", "integer llModPow(integer a, integer b, integer c)\nReturns a raised to the b power, mod c. ( (a**b)%c ). b is capped at 0xFFFF (16 bits)."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetInventoryType", "i", "s", "integer llGetInventoryType(string name)\nReturns the type of the inventory name"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetPayPrice", NULL, "il", "llSetPayPrice(integer price, list quick_pay_buttons)\nSets the default amount when someone chooses to pay this object."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetCameraPos", "v", "", "vector llGetCameraPos()\nGets current camera position for agent task has permissions for."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetCameraRot", "q", "", "rotation llGetCameraRot()\nGets current camera orientation for agent task has permissions for."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 2.f, dummy_func, "llSetPrimURL", NULL, "s", "llSetPrimURL(string url)\nUpdates the URL for the web page shown on the sides of the object."));
+ addFunction(new LLScriptLibraryFunction(10.f, 20.f, dummy_func, "llRefreshPrimURL", NULL, "", "llRefreshPrimURL()\nReloads the web page shown on the sides of the object."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llEscapeURL", "s", "s", "string llEscapeURL(string url)\nReturns and escaped/encoded version of url, replacing spaces with %20 etc."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llUnescapeURL", "s", "s", "string llUnescapeURL(string url)\nReturns and unescaped/unencoded version of url, replacing %20 with spaces etc."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 1.f, dummy_func, "llMapDestination", NULL, "svv", "llMapDestination(string simname, vector pos, vector look_at)\nOpens world map centered on region with pos highlighted.\nOnly works for scripts attached to avatar, or during touch events.\n(NOTE: look_at currently does nothing)"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llAddToLandBanList", NULL, "kf", "llAddToLandBanList(key avatar, float hours)\nAdd avatar to the land ban list for hours"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llRemoveFromLandPassList", NULL, "k", "llRemoveFromLandPassList(key avatar)\nRemove avatar from the land pass list"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llRemoveFromLandBanList", NULL, "k", "llRemoveFromLandBanList(key avatar)\nRemove avatar from the land ban list"));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCameraParams", NULL, "l", "llSetCameraParams(list rules)\nSets multiple camera parameters at once.\nList format is [ rule1, data1, rule2, data2 . . . rulen, datan ]"));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llClearCameraParams", NULL, NULL, "llClearCameraParams()\nResets all camera parameters to default values and turns off scripted camera control."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llListStatistics", "f", "il", "integer llListStatistics(integer operation, list l)\nPerform statistical aggregate functions on list l using LIST_STAT_* operations."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetUnixTime", "i", NULL, "integer llGetUnixTime()\nGet the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC from the system clock."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetParcelFlags", "i", "v", "integer llGetParcelFlags(vector pos)\nGet the parcel flags (PARCEL_FLAG_*) for the parcel including the point pos."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetRegionFlags", "i", NULL, "integer llGetRegionFlags()\nGet the region flags (REGION_FLAG_*) for the region the object is in."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llXorBase64StringsCorrect", "s", "ss", "string llXorBase64StringsCorrect(string s1, string s2)\nCorrectly performs an exclusive or on two Base 64 strings and returns a Base 64 string. s2 repeats if it is shorter than s1."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llHTTPRequest", "k", "sls", "llHTTPRequest(string url, list parameters, string body)\nSend an HTTP request."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llResetLandBanList", NULL, NULL, "llResetLandBanList()\nRemoves all residents from the land ban list."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.1f, dummy_func, "llResetLandPassList", NULL, NULL, "llResetLandPassList()\nRemoves all residents from the land access/pass list."));
+
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetObjectPrimCount", "i", "k", "integer llGetObjectPrimCount(key object_id)\nReturns the total number of prims for an object."));
+ addFunction(new LLScriptLibraryFunction(10.f, 2.0f, dummy_func, "llGetParcelPrimOwners", "l", "v", "list llGetParcelPrimOwners(vector pos)\nReturns a list of all residents who own objects on the parcel and the number of objects they own.\nRequires owner-like permissions for the parcel."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetParcelPrimCount", "i", "vii","integer llGetParcelPrimCount(vector pos, integer category, integer sim_wide)\nGets the number of prims on the parcel of the given category.\nCategories: PARCEL_COUNT_TOTAL, _OWNER, _GROUP, _OTHER, _SELECTED, _TEMP."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetParcelMaxPrims", "i", "vi","integer llGetParcelMaxPrims(vector pos, integer sim_wide)\nGets the maximum number of prims allowed on the parcel at pos."));
+ addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llGetParcelDetails", "l", "vl","list llGetParcelDetails(vector pos, list params)\nGets the parcel details specified in params for the parcel at pos.\nParams is one or more of: PARCEL_DETAILS_NAME, _DESC, _OWNER, _GROUP, _AREA"));
+
+ // energy, sleep, dummy_func, name, return type, parameters, help text, gods-only
+
+ // IF YOU ADD NEW SCRIPT CALLS, YOU MUST PUT THEM AT THE END OF THIS LIST.
+ // Otherwise the bytecode numbers for each call will be wrong, and all
+ // existing scripts will crash.
+}
+
+ //Ventrella Follow Cam Script Stuff
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamPitch", NULL, "f", "llSetCamPitch(-45 to 80)\n(Adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance. Analogous to 'incidence'."));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamVerticalOffset", NULL, "f", "llSetCamVerticalOffset(-2 to 2)\nAdjusts the vertical position of the camera focus position relative to the subject"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamPositionLag", NULL, "f", "llSetCamPositionLag(0 to 3) \nHow much the camera lags as it tries to move towards its 'ideal' position"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamFocusLag", NULL, "f", "llSetCamFocusLag(0 to 3)\nHow much the camera lags as it tries to aim towards the subject"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamDistance", NULL, "f", "llSetCamDistance(0.5 to 10)\nSets how far away the camera wants to be from its subject"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamBehindnessAngle", NULL, "f", "llSetCamBehindnessAngle(0 to 180)\nSets the angle in degrees within which the camera is not constrained by changes in subject rotation"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamBehindnessLag", NULL, "f", "llSetCamBehindnessLag(0 to 3)\nSets how strongly the camera is forced to stay behind the target if outside of behindness angle"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamPositionThreshold", NULL, "f", "llSetCamPositionThreshold(0 to 4)\nSets the radius of a sphere around the camera's ideal position within which it is not affected by subject motion"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamFocusThreshold", NULL, "f", "llSetCamFocusThreshold(0 to 4)\nSets the radius of a sphere around the camera's subject position within which its focus is not affected by subject motion"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamScriptControl", NULL, "i", "llSetCamScriptControl(TRUE or FALSE)\nTurns on or off scripted control of the camera"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamPosition", NULL, "v", "llSetCamPosition(vector)\nSets the position of the camera"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamFocus", NULL, "v", "llSetCamFocus(vector focus)\nSets the focus (target position) of the camera"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamPositionLocked", NULL, "i", "llSetCamPositionLocked(TRUE or FALSE)\nLocks the camera position so it will not move"));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetCamFocusLocked", NULL, "i", "llSetCamFocusLocked(TRUE or FALSE)\nLocks the camera focus so it will not move"));
+
+// These functions are being put on hold until we think through how we want them handled (security issues). DK 02/16/05
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetLinkPrimitiveParams", NULL, "il", "llSetLinkPrimitiveParams(integer linknumber, list rules)\nSet primitive parameters for linknumber based on rules."));
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.2f, dummy_func, "llSetLinkTexture", NULL, "isi", "llSetLinkTexture(integer link_pos, string texture, integer face)\nSets the texture of face for link_pos"));
+
+ //addFunction(new LLScriptLibraryFunction(10.f, 0.f, dummy_func, "llSetForSale", "i", "ii", "integer llSetForSale(integer selltype, integer price)\nSets this object for sale in mode selltype for price. Returns TRUE if successfully set for sale."));
+
+LLScriptLibraryFunction::LLScriptLibraryFunction(F32 eu, F32 st, void (*exec_func)(LLScriptLibData *, LLScriptLibData *, const LLUUID &), char *name, char *ret_type, char *args, char *desc, BOOL god_only)
+ : mEnergyUse(eu), mSleepTime(st), mExecFunc(exec_func), mName(name), mReturnType(ret_type), mArgs(args), mGodOnly(god_only)
+{
+ mDesc = new char[512];
+ if (mSleepTime)
+ {
+ sprintf(mDesc,"%s\nSleeps script for %.1f seconds.",desc,mSleepTime);
+ }
+ else
+ {
+ strcpy(mDesc,desc);
+ }
+}
+
+LLScriptLibraryFunction::~LLScriptLibraryFunction()
+{
+ delete [] mDesc;
+}
+
+void LLScriptLibrary::addFunction(LLScriptLibraryFunction *func)
+{
+ LLScriptLibraryFunction **temp = (LLScriptLibraryFunction **)new U32[mNextNumber + 1];
+ if (mNextNumber)
+ {
+ memcpy(temp, mFunctions, sizeof(LLScriptLibraryFunction *)*mNextNumber);
+ delete [] mFunctions;
+ }
+ mFunctions = temp;
+ mFunctions[mNextNumber] = func;
+ mNextNumber++;
+}
+
+void LLScriptLibrary::assignExec(char *name, void (*exec_func)(LLScriptLibData *, LLScriptLibData *, const LLUUID &))
+{
+ S32 i;
+ for (i = 0; i < mNextNumber; i++)
+ {
+ if (!strcmp(name, mFunctions[i]->mName))
+ {
+ mFunctions[i]->mExecFunc = exec_func;
+ }
+ }
+}
+
+void LLScriptLibData::print(std::ostream &s, BOOL b_prepend_comma)
+{
+ char tmp[1024];
+ if (b_prepend_comma)
+ {
+ s << ", ";
+ }
+ switch (mType)
+ {
+ case LST_INTEGER:
+ s << mInteger;
+ break;
+ case LST_FLOATINGPOINT:
+ snprintf(tmp, 1024, "%f", mFP);
+ s << tmp;
+ break;
+ case LST_KEY:
+ s << mKey;
+ break;
+ case LST_STRING:
+ s << mString;
+ break;
+ case LST_VECTOR:
+ snprintf(tmp, 1024, "<%f, %f, %f>", mVec.mV[VX],
+ mVec.mV[VY], mVec.mV[VZ]);
+ s << tmp;
+ break;
+ case LST_QUATERNION:
+ snprintf(tmp, 1024, "<%f, %f, %f, %f>", mQuat.mQ[VX], mQuat.mQ[VY],
+ mQuat.mQ[VZ], mQuat.mQ[VS]);
+ s << tmp;
+ break;
+ default:
+ break;
+ }
+}
+
+void LLScriptLibData::print_separator(std::ostream& ostr, BOOL b_prepend_sep, char* sep)
+{
+ if (b_prepend_sep)
+ {
+ ostr << sep;
+ }
+ //print(ostr, FALSE);
+ {
+ BOOL b_prepend_comma = FALSE;
+ char tmp[1024];
+ if (b_prepend_comma)
+ {
+ ostr << ", ";
+ }
+ switch (mType)
+ {
+ case LST_INTEGER:
+ ostr << mInteger;
+ break;
+ case LST_FLOATINGPOINT:
+ snprintf(tmp, 1024, "%f", mFP);
+ ostr << tmp;
+ break;
+ case LST_KEY:
+ ostr << mKey;
+ break;
+ case LST_STRING:
+ ostr << mString;
+ break;
+ case LST_VECTOR:
+ snprintf(tmp, 1024, "<%f, %f, %f>", mVec.mV[VX],
+ mVec.mV[VY], mVec.mV[VZ]);
+ ostr << tmp;
+ break;
+ case LST_QUATERNION:
+ snprintf(tmp, 1024, "<%f, %f, %f, %f>", mQuat.mQ[VX], mQuat.mQ[VY],
+ mQuat.mQ[VZ], mQuat.mQ[VS]);
+ ostr << tmp;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+LLScriptLibrary gScriptLibrary;
diff --git a/indra/lscript/lscript_rt_interface.h b/indra/lscript/lscript_rt_interface.h
new file mode 100644
index 0000000000..1572350a35
--- /dev/null
+++ b/indra/lscript/lscript_rt_interface.h
@@ -0,0 +1,18 @@
+/**
+ * @file lscript_rt_interface.h
+ * @brief Interface between compiler library and applications
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LSCRIPT_RT_INTERFACE_H
+#define LL_LSCRIPT_RT_INTERFACE_H
+
+BOOL lscript_compile(char *filename, BOOL is_god_like = FALSE);
+BOOL lscript_compile(const char* src_filename, const char* dst_filename,
+ const char* err_filename, BOOL is_god_like = FALSE);
+void lscript_run(char *filename, BOOL b_debug);
+
+
+#endif
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
new file mode 100644
index 0000000000..5e6d9ba2f0
--- /dev/null
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -0,0 +1,669 @@
+/**
+ * @file mac_crash_logger.cpp
+ * @brief Mac OSX crash logger implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+//#include <direct.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <curl/curl.h>
+
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+
+#include "llstring.h"
+
+class LLFileEncoder
+{
+public:
+ LLFileEncoder(const char *formname, const char *filename, bool isCrashLog = false);
+
+ BOOL isValid() const { return mIsValid; }
+ LLString encodeURL(const S32 max_length = 0);
+public:
+ BOOL mIsValid;
+ LLString mFilename;
+ LLString mFormname;
+ LLString mBuf;
+};
+
+LLString encode_string(const char *formname, const LLString &str);
+
+#include <Carbon/Carbon.h>
+
+LLString gServerResponse;
+BOOL gSendReport = FALSE;
+LLString gUserserver;
+LLString gUserText;
+WindowRef gWindow = NULL;
+EventHandlerRef gEventHandler = NULL;
+BOOL gCrashInPreviousExec = FALSE;
+time_t gLaunchTime;
+
+size_t curl_download_callback(void *data, size_t size, size_t nmemb,
+ void *user_data)
+{
+ S32 bytes = size * nmemb;
+ char *cdata = (char *) data;
+ for (int i =0; i < bytes; i += 1)
+ {
+ gServerResponse += (cdata[i]);
+ }
+ return bytes;
+}
+
+OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
+{
+ OSStatus result = eventNotHandledErr;
+ OSStatus err;
+ UInt32 evtClass = GetEventClass(event);
+ UInt32 evtKind = GetEventKind(event);
+
+ if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
+ {
+ HICommand cmd;
+ err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
+
+ if(err == noErr)
+ {
+ switch(cmd.commandID)
+ {
+ case kHICommandOK:
+ {
+ char buffer[65535];
+ Size size = sizeof(buffer) - 1;
+ ControlRef textField = NULL;
+ ControlID id;
+
+ id.signature = 'text';
+ id.id = 0;
+
+ err = GetControlByID(gWindow, &id, &textField);
+ if(err == noErr)
+ {
+ // Get the user response text
+ err = GetControlData(textField, kControlNoPart, kControlEditTextTextTag, size, (Ptr)buffer, &size);
+ }
+ if(err == noErr)
+ {
+ // Make sure the string is terminated.
+ buffer[size] = 0;
+ gUserText = buffer;
+ llinfos << buffer << llendl;
+ }
+
+ // Send the report.
+ gSendReport = TRUE;
+
+ QuitAppModalLoopForWindow(gWindow);
+ result = noErr;
+ }
+ break;
+
+ case kHICommandCancel:
+ QuitAppModalLoopForWindow(gWindow);
+ result = noErr;
+ break;
+ }
+ }
+ }
+
+ return(result);
+}
+
+int main(int argc, char **argv)
+{
+ const S32 DW_MAX_SIZE = 100000; // Maximum size to transmit of the Dr. Watson log file
+ const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
+ int i;
+
+ time(&gLaunchTime);
+
+ llinfos << "Starting Second Life Viewer Crash Reporter" << llendl;
+
+ for(i=1; i<argc; i++)
+ {
+ if(!strcmp(argv[i], "-previous"))
+ {
+ gCrashInPreviousExec = TRUE;
+ }
+ if(!strcmp(argv[i], "-user"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+ gUserserver = argv[i];
+ llinfos << "Got userserver " << gUserserver << llendl;
+ }
+ }
+ }
+
+ if( gCrashInPreviousExec )
+ {
+ llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+ }
+
+ if(!gCrashInPreviousExec)
+ {
+ // Delay five seconds to let CrashReporter do its thing.
+ sleep(5);
+ }
+
+#if 1
+ // Real UI...
+ OSStatus err;
+ IBNibRef nib = NULL;
+
+ err = CreateNibReference(CFSTR("CrashReporter"), &nib);
+
+ if(err == noErr)
+ {
+ if(gCrashInPreviousExec)
+ {
+ err = CreateWindowFromNib(nib, CFSTR("CrashReporterDelayed"), &gWindow);
+ }
+ else
+ {
+ err = CreateWindowFromNib(nib, CFSTR("CrashReporter"), &gWindow);
+ }
+ }
+
+ if(err == noErr)
+ {
+ // Set focus to the edit text area
+ ControlRef textField = NULL;
+ ControlID id;
+
+ id.signature = 'text';
+ id.id = 0;
+
+ // Don't set err if any of this fails, since it's non-critical.
+ if(GetControlByID(gWindow, &id, &textField) == noErr)
+ {
+ SetKeyboardFocus(gWindow, textField, kControlFocusNextPart);
+ }
+ }
+
+ if(err == noErr)
+ {
+ ShowWindow(gWindow);
+ }
+
+ if(err == noErr)
+ {
+ // Set up an event handler for the window.
+ EventTypeSpec handlerEvents[] =
+ {
+ { kEventClassCommand, kEventCommandProcess }
+ };
+
+ InstallWindowEventHandler(
+ gWindow,
+ NewEventHandlerUPP(dialogHandler),
+ GetEventTypeCount (handlerEvents),
+ handlerEvents,
+ 0,
+ &gEventHandler);
+ }
+
+ if(err == noErr)
+ {
+ RunAppModalLoopForWindow(gWindow);
+ }
+
+ if(gWindow != NULL)
+ {
+ DisposeWindow(gWindow);
+ }
+
+ if(nib != NULL)
+ {
+ DisposeNibReference(nib);
+ }
+#else
+ // Cheap version -- just use the standard system alert.
+ SInt16 itemHit = 0;
+ AlertStdCFStringAlertParamRec params;
+ OSStatus err = noErr;
+ DialogRef alert = NULL;
+
+ params.version = kStdCFStringAlertVersionOne;
+ params.movable = false;
+ params.helpButton = false;
+ params.defaultText = CFSTR("Send Report");
+ params.cancelText = CFSTR("Don't Send Report");
+ params.otherText = 0;
+ params.defaultButton = kAlertStdAlertOKButton;
+ params.cancelButton = kAlertStdAlertCancelButton;
+ params.position = kWindowDefaultPosition;
+ params.flags = 0;
+
+ err = CreateStandardAlert(
+ kAlertCautionAlert,
+ CFSTR("Second Life appears to have crashed."),
+ CFSTR(
+ "To help us debug the problem, you can send a crash report to Linden Lab. "
+ "The report contains information about your microprocessor type, graphics card, "
+ "memory, things that happened during the last run of the program, and the Crash Log "
+ "of where the crash occurred.\r"
+ "\r"
+ "You may also report crashes in the forums, or by sending e-mail to peter@lindenlab.com"),
+ &params,
+ &alert);
+
+ if(err == noErr)
+ {
+ err = RunStandardAlert(
+ alert,
+ NULL,
+ &itemHit);
+ }
+
+ if(itemHit == kAlertStdAlertOKButton)
+ gSendReport = TRUE;
+#endif
+
+ if(!gSendReport)
+ {
+ // Only send the report if the user agreed to it.
+ llinfos << "User cancelled, not sending report" << llendl;
+
+ return(0);
+ }
+
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ int res;
+
+ // Lots of silly variable, replicated for each log file.
+ LLString db_file_name;
+ LLString sl_file_name;
+ LLString dw_file_name; // DW file name is a hack for now...
+ LLString st_file_name; // stats.log file
+ LLString si_file_name; // settings.ini file
+
+ LLFileEncoder *db_filep = NULL;
+ LLFileEncoder *sl_filep = NULL;
+ LLFileEncoder *st_filep = NULL;
+ LLFileEncoder *dw_filep = NULL;
+ LLFileEncoder *si_filep = NULL;
+
+ ///////////////////////////////////
+ //
+ // We do the parsing for the debug_info file first, as that will
+ // give us the location of the SecondLife.log file.
+ //
+
+ // Figure out the filename of the debug log
+ db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log").c_str();
+ db_filep = new LLFileEncoder("DB", db_file_name.c_str());
+
+ // Get the filename of the SecondLife.log file
+ char tmp_sl_name[MAX_PATH];
+ tmp_sl_name[0] = '\0';
+ char tmp_space[256];
+ tmp_space[0] = '\0';
+
+ // Look for it in the debug_info.log file
+ if (db_filep->isValid())
+ {
+ // This was originally scanning for "SL Log: %[^\r\n]", which happily skipped to the next line
+ // on debug logs (which don't have anything after "SL Log:" and tried to open a nonsensical filename.
+ sscanf(db_filep->mBuf.c_str(), "SL Log:%[ ]%[^\r\n]", tmp_space, tmp_sl_name);
+ }
+ else
+ {
+ delete db_filep;
+ db_filep = NULL;
+ }
+
+ // If we actually have a legitimate file name, use it.
+ if (tmp_sl_name[0])
+ {
+ sl_file_name = tmp_sl_name;
+ llinfos << "Using log file from debug log " << sl_file_name << llendl;
+ }
+ else
+ {
+ // Figure out the filename of the second life log
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log").c_str();
+ }
+
+ // Now we get the SecondLife.log file if it's there, and recent enough...
+ sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
+ if (!sl_filep->isValid())
+ {
+ delete sl_filep;
+ sl_filep = NULL;
+ }
+
+ st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log").c_str();
+ st_filep = new LLFileEncoder("ST", st_file_name.c_str());
+ if (!st_filep->isValid())
+ {
+ delete st_filep;
+ st_filep = NULL;
+ }
+
+ si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini").c_str();
+ si_filep = new LLFileEncoder("SI", si_file_name.c_str());
+ if (!si_filep->isValid())
+ {
+ delete si_filep;
+ si_filep = NULL;
+ }
+
+ // MBW -- This needs to go find "~/Library/Logs/CrashReporter/Second Life.crash.log" on 10.3
+ // or "~/Library/Logs/Second Life.crash.log" on 10.2.
+ {
+ char path[MAX_PATH];
+ FSRef folder;
+
+ if(FSFindFolder(kUserDomain, kLogsFolderType, false, &folder) == noErr)
+ {
+ // folder is an FSRef to ~/Library/Logs/
+ if(FSRefMakePath(&folder, (UInt8*)&path, sizeof(path)) == noErr)
+ {
+ struct stat dw_stat;
+// printf("path is %s\n", path);
+
+ // Try the 10.3 path first...
+ dw_file_name = LLString(path) + LLString("/CrashReporter/Second Life.crash.log");
+ res = stat(dw_file_name.c_str(), &dw_stat);
+
+ if (res)
+ {
+ // Try the 10.2 one next...
+ dw_file_name = LLString(path) + LLString("/Second Life.crash.log");
+ res = stat(dw_file_name.c_str(), &dw_stat);
+ }
+
+ if (!res)
+ {
+ dw_filep = new LLFileEncoder("DW", dw_file_name.c_str(), true);
+ if (!dw_filep->isValid())
+ {
+ delete dw_filep;
+ dw_filep = NULL;
+ }
+ }
+ else
+ {
+ llwarns << "Couldn't find any CrashReporter files..." << llendl;
+ }
+ }
+ }
+ }
+
+ LLString post_data;
+ LLString tmp_url_buf;
+
+ // Append the userserver
+ tmp_url_buf = encode_string("USER", gUserserver);
+ post_data += tmp_url_buf;
+ llinfos << "PostData:" << post_data << llendl;
+
+ if (gCrashInPreviousExec)
+ {
+ post_data.append("&");
+ tmp_url_buf = encode_string("EF", "Y");
+ post_data += tmp_url_buf;
+ }
+
+ if (db_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = db_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending DB log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending DB log file" << llendl;
+ }
+
+ if (sl_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending SL log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending SL log file" << llendl;
+ }
+
+ if (st_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending stats log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending stats log file" << llendl;
+ }
+
+ if (dw_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = dw_filep->encodeURL(DW_MAX_SIZE);
+ post_data += tmp_url_buf;
+ }
+ else
+ {
+ llinfos << "Not sending crash log file" << llendl;
+ }
+
+ if (si_filep)
+ {
+ post_data.append("&");
+ tmp_url_buf = si_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending settings log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending settings.ini file" << llendl;
+ }
+
+ if (gUserText.size())
+ {
+ post_data.append("&");
+ tmp_url_buf = encode_string("UN", gUserText);
+ post_data += tmp_url_buf;
+ }
+
+ delete db_filep;
+ db_filep = NULL;
+ delete sl_filep;
+ sl_filep = NULL;
+ delete dw_filep;
+ dw_filep = NULL;
+
+ // Debugging spam
+#if 0
+ printf("Crash report post data:\n--------\n");
+ printf("%s", post_data.getString());
+ printf("\n--------\n");
+#endif
+
+ // Send the report. Yes, it's this easy.
+ {
+ CURL *curl = curl_easy_init();
+
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
+ curl_easy_setopt(curl, CURLOPT_POST, 1);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data.c_str());
+ curl_easy_setopt(curl, CURLOPT_URL, "http://secondlife.com/cgi-bin/viewer_crash_reporter2");
+
+ llinfos << "Connecting to crash report server" << llendl;
+ CURLcode result = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+
+ if(result != CURLE_OK)
+ {
+ llinfos << "Couldn't talk to crash report server" << llendl;
+ }
+ else
+ {
+ llinfos << "Response from crash report server:" << llendl;
+ llinfos << gServerResponse << llendl;
+ }
+ }
+
+ return 0;
+}
+
+LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename, bool isCrashLog)
+{
+ mFormname = form_name;
+ mFilename = filename;
+ mIsValid = FALSE;
+
+ int res;
+
+ struct stat stat_data;
+ res = stat(mFilename.c_str(), &stat_data);
+ if (res)
+ {
+ llwarns << "File " << mFilename << " is missing!" << llendl;
+ return;
+ }
+ else
+ {
+ // Debugging spam
+// llinfos << "File " << mFilename << " is present..." << llendl;
+
+ if(!gCrashInPreviousExec && isCrashLog)
+ {
+ // Make sure the file isn't too old.
+ double age = difftime(gLaunchTime, stat_data.st_mtimespec.tv_sec);
+
+// llinfos << "age is " << age << llendl;
+
+ if(age > 60.0)
+ {
+ // The file was last modified more than 60 seconds before the crash reporter was launched. Assume it's stale.
+ llwarns << "File " << mFilename << " is too old!" << llendl;
+ return;
+ }
+ }
+
+ }
+
+ S32 buf_size = stat_data.st_size;
+ FILE *fp = fopen(mFilename.c_str(), "rb");
+ U8 *buf = new U8[buf_size + 1];
+ fread(buf, 1, buf_size, fp);
+ fclose(fp);
+ buf[buf_size] = 0;
+
+ mBuf = (char *)buf;
+
+ if(isCrashLog)
+ {
+ // Crash logs consist of a number of entries, one per crash.
+ // Each entry is preceeded by "**********" on a line by itself.
+ // We want only the most recent (i.e. last) one.
+ const char *sep = "**********";
+ const char *start = mBuf.c_str();
+ const char *cur = start;
+ const char *temp = strstr(cur, sep);
+
+ while(temp != NULL)
+ {
+ // Skip past the marker we just found
+ cur = temp + strlen(sep);
+
+ // and try to find another
+ temp = strstr(cur, sep);
+ }
+
+ // If there's more than one entry in the log file, strip all but the last one.
+ if(cur != start)
+ {
+ mBuf.erase(0, cur - start);
+ }
+ }
+
+ mIsValid = TRUE;
+ delete[] buf;
+}
+
+LLString LLFileEncoder::encodeURL(const S32 max_length)
+{
+ LLString result = mFormname;
+ result.append("=");
+
+ S32 i = 0;
+
+ if (max_length)
+ {
+ if (mBuf.size() > max_length)
+ {
+ i = mBuf.size() - max_length;
+ }
+ }
+
+#if 0
+ // Plain text version for debugging
+ result.append(mBuf);
+#else
+ // Not using LLString because of bad performance issues
+ S32 buf_size = mBuf.size();
+ S32 url_buf_size = 3*mBuf.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ for (; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", mBuf[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+#endif
+ return result;
+}
+
+LLString encode_string(const char *formname, const LLString &str)
+{
+ LLString result = formname;
+ result.append("=");
+ // Not using LLString because of bad performance issues
+ S32 buf_size = str.size();
+ S32 url_buf_size = 3*str.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ S32 i;
+ for (i = 0; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", str[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
diff --git a/indra/mac_updater/AutoUpdater.nib/classes.nib b/indra/mac_updater/AutoUpdater.nib/classes.nib
new file mode 100644
index 0000000000..ea58db1189
--- /dev/null
+++ b/indra/mac_updater/AutoUpdater.nib/classes.nib
@@ -0,0 +1,4 @@
+{
+IBClasses = ();
+IBVersion = 1;
+}
diff --git a/indra/mac_updater/AutoUpdater.nib/info.nib b/indra/mac_updater/AutoUpdater.nib/info.nib
new file mode 100644
index 0000000000..a49a92385b
--- /dev/null
+++ b/indra/mac_updater/AutoUpdater.nib/info.nib
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>103 138 356 240 0 0 1280 1002 </string>
+ <key>IBFramework Version</key>
+ <string>362.0</string>
+ <key>IBSystem Version</key>
+ <string>7D24</string>
+ <key>targetFramework</key>
+ <string>IBCarbonFramework</string>
+</dict>
+</plist>
diff --git a/indra/mac_updater/AutoUpdater.nib/objects.xib b/indra/mac_updater/AutoUpdater.nib/objects.xib
new file mode 100644
index 0000000000..310411b711
--- /dev/null
+++ b/indra/mac_updater/AutoUpdater.nib/objects.xib
@@ -0,0 +1,56 @@
+<?xml version="1.0" standalone="yes"?>
+<object class="NSIBObjectData">
+ <string name="targetFramework">IBCarbonFramework</string>
+ <object name="rootObject" class="NSCustomObject" id="1">
+ <string name="customClass">NSApplication</string>
+ </object>
+ <array count="5" name="allObjects">
+ <object class="IBCarbonWindow" id="166">
+ <string name="windowRect">405 222 533 663 </string>
+ <string name="title">Second Life Updater</string>
+ <object name="rootControl" class="IBCarbonRootControl" id="167">
+ <string name="bounds">0 0 128 441 </string>
+ <array count="3" name="subviews">
+ <object class="IBCarbonStaticText" id="181">
+ <string name="bounds">20 20 44 421 </string>
+ <ostype name="controlSignature">what</ostype>
+ <string name="title">Initializing…</string>
+ </object>
+ <object class="IBCarbonButton" id="183">
+ <string name="bounds">88 351 108 421 </string>
+ <string name="title">Cancel</string>
+ <ostype name="command">not!</ostype>
+ <int name="buttonType">2</int>
+ </object>
+ <object class="IBCarbonProgressBar" id="193">
+ <string name="bounds">51 19 70 422 </string>
+ <ostype name="controlSignature">prog</ostype>
+ <int name="initialValue">50</int>
+ </object>
+ </array>
+ </object>
+ <boolean name="isResizable">FALSE</boolean>
+ <int name="carbonWindowClass">2</int>
+ <int name="themeBrush">3</int>
+ <int name="windowPosition">7</int>
+ </object>
+ <reference idRef="167"/>
+ <reference idRef="181"/>
+ <reference idRef="183"/>
+ <reference idRef="193"/>
+ </array>
+ <array count="5" name="allParents">
+ <reference idRef="1"/>
+ <reference idRef="166"/>
+ <reference idRef="167"/>
+ <reference idRef="167"/>
+ <reference idRef="167"/>
+ </array>
+ <dictionary count="2" name="nameTable">
+ <string>File&apos;s Owner</string>
+ <reference idRef="1"/>
+ <string>Updater</string>
+ <reference idRef="166"/>
+ </dictionary>
+ <unsigned_int name="nextObjectID">194</unsigned_int>
+</object>
diff --git a/indra/mac_updater/mac_updater.cpp b/indra/mac_updater/mac_updater.cpp
new file mode 100644
index 0000000000..df054fb94a
--- /dev/null
+++ b/indra/mac_updater/mac_updater.cpp
@@ -0,0 +1,1087 @@
+/**
+ * @file mac_updater.cpp
+ * @brief
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+//#include <direct.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <curl/curl.h>
+#include <pthread.h>
+
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+#include "llfile.h"
+
+#include "llstring.h"
+
+#include <Carbon/Carbon.h>
+
+#include "MoreFilesX.h"
+#include "FSCopyObject.h"
+
+enum
+{
+ kEventClassCustom = 'Cust',
+ kEventCustomProgress = 'Prog',
+ kEventParamCustomCurValue = 'Cur ',
+ kEventParamCustomMaxValue = 'Max ',
+ kEventParamCustomText = 'Text',
+ kEventCustomDone = 'Done',
+};
+
+WindowRef gWindow = NULL;
+EventHandlerRef gEventHandler = NULL;
+OSStatus gFailure = noErr;
+Boolean gCancelled = false;
+
+char *gUserServer;
+char *gProductName;
+char gUpdateURL[2048];
+
+void *updatethreadproc(void*);
+
+pthread_t updatethread;
+
+OSStatus setProgress(int cur, int max)
+{
+ OSStatus err;
+ ControlRef progressBar = NULL;
+ ControlID id;
+
+ id.signature = 'prog';
+ id.id = 0;
+
+ err = GetControlByID(gWindow, &id, &progressBar);
+ if(err == noErr)
+ {
+ Boolean indeterminate;
+
+ if(max == 0)
+ {
+ indeterminate = true;
+ err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
+ }
+ else
+ {
+ double percentage = (double)cur / (double)max;
+ SetControlMinimum(progressBar, 0);
+ SetControlMaximum(progressBar, 100);
+ SetControlValue(progressBar, (SInt16)(percentage * 100));
+
+ indeterminate = false;
+ err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate);
+
+ Draw1Control(progressBar);
+ }
+ }
+
+ return(err);
+}
+
+OSStatus setProgressText(CFStringRef text)
+{
+ OSStatus err;
+ ControlRef progressText = NULL;
+ ControlID id;
+
+ id.signature = 'what';
+ id.id = 0;
+
+ err = GetControlByID(gWindow, &id, &progressText);
+ if(err == noErr)
+ {
+ err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text);
+ Draw1Control(progressText);
+ }
+
+ return(err);
+}
+
+OSStatus sendProgress(long cur, long max, CFStringRef text = NULL)
+{
+ OSStatus result;
+ EventRef evt;
+
+ result = CreateEvent(
+ NULL,
+ kEventClassCustom,
+ kEventCustomProgress,
+ 0,
+ kEventAttributeNone,
+ &evt);
+
+ // This event needs to be targeted at the window so it goes to the window's handler.
+ if(result == noErr)
+ {
+ EventTargetRef target = GetWindowEventTarget(gWindow);
+ result = SetEventParameter (
+ evt,
+ kEventParamPostTarget,
+ typeEventTargetRef,
+ sizeof(target),
+ &target);
+ }
+
+ if(result == noErr)
+ {
+ result = SetEventParameter (
+ evt,
+ kEventParamCustomCurValue,
+ typeLongInteger,
+ sizeof(cur),
+ &cur);
+ }
+
+ if(result == noErr)
+ {
+ result = SetEventParameter (
+ evt,
+ kEventParamCustomMaxValue,
+ typeLongInteger,
+ sizeof(max),
+ &max);
+ }
+
+ if(result == noErr)
+ {
+ if(text != NULL)
+ {
+ result = SetEventParameter (
+ evt,
+ kEventParamCustomText,
+ typeCFStringRef,
+ sizeof(text),
+ &text);
+ }
+ }
+
+ if(result == noErr)
+ {
+ // Send the event
+ PostEventToQueue(
+ GetMainEventQueue(),
+ evt,
+ kEventPriorityStandard);
+
+ }
+
+ return(result);
+}
+
+OSStatus sendDone(void)
+{
+ OSStatus result;
+ EventRef evt;
+
+ result = CreateEvent(
+ NULL,
+ kEventClassCustom,
+ kEventCustomDone,
+ 0,
+ kEventAttributeNone,
+ &evt);
+
+ // This event needs to be targeted at the window so it goes to the window's handler.
+ if(result == noErr)
+ {
+ EventTargetRef target = GetWindowEventTarget(gWindow);
+ result = SetEventParameter (
+ evt,
+ kEventParamPostTarget,
+ typeEventTargetRef,
+ sizeof(target),
+ &target);
+ }
+
+ if(result == noErr)
+ {
+ // Send the event
+ PostEventToQueue(
+ GetMainEventQueue(),
+ evt,
+ kEventPriorityStandard);
+
+ }
+
+ return(result);
+}
+
+OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata)
+{
+ OSStatus result = eventNotHandledErr;
+ OSStatus err;
+ UInt32 evtClass = GetEventClass(event);
+ UInt32 evtKind = GetEventKind(event);
+
+ if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess))
+ {
+ HICommand cmd;
+ err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd);
+
+ if(err == noErr)
+ {
+ switch(cmd.commandID)
+ {
+ case kHICommandCancel:
+ gCancelled = true;
+// QuitAppModalLoopForWindow(gWindow);
+ result = noErr;
+ break;
+ }
+ }
+ }
+ else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress))
+ {
+ // Request to update the progress dialog
+ long cur = 0;
+ long max = 0;
+ CFStringRef text = NULL;
+ (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur);
+ (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max);
+ (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text);
+
+ err = setProgress(cur, max);
+ if(err == noErr)
+ {
+ if(text != NULL)
+ {
+ setProgressText(text);
+ }
+ }
+
+ result = noErr;
+ }
+ else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone))
+ {
+ // We're done. Exit the modal loop.
+ QuitAppModalLoopForWindow(gWindow);
+ result = noErr;
+ }
+
+ return(result);
+}
+
+#if 0
+size_t curl_download_callback(void *data, size_t size, size_t nmemb,
+ void *user_data)
+{
+ S32 bytes = size * nmemb;
+ char *cdata = (char *) data;
+ for (int i =0; i < bytes; i += 1)
+ {
+ gServerResponse.append(cdata[i]);
+ }
+ return bytes;
+}
+#endif
+
+int curl_progress_callback_func(void *clientp,
+ double dltotal,
+ double dlnow,
+ double ultotal,
+ double ulnow)
+{
+ int max = (int)(dltotal / 1024.0);
+ int cur = (int)(dlnow / 1024.0);
+ sendProgress(cur, max);
+
+ if(gCancelled)
+ return(1);
+
+ return(0);
+}
+
+int parse_args(int argc, char **argv)
+{
+ // Check for old-type arguments.
+ if (2 == argc)
+ {
+ gUserServer = argv[1];
+ return 0;
+ }
+
+ int j;
+
+ for (j = 1; j < argc; j++)
+ {
+ if ((!strcmp(argv[j], "-userserver")) && (++j < argc))
+ {
+ gUserServer = argv[j];
+ }
+ else if ((!strcmp(argv[j], "-name")) && (++j < argc))
+ {
+ gProductName = argv[j];
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ /////////////////////////////////////////
+ //
+ // Process command line arguments
+ //
+ gUserServer = NULL;
+ gProductName = NULL;
+ parse_args(argc, argv);
+ if (!gUserServer)
+ {
+ llinfos << "Usage: mac_updater -userserver <server> [-name <product_name>] [-program <program_name>]" << llendl;
+ exit(1);
+ }
+ else
+ {
+ llinfos << "User server is: " << gUserServer << llendl;
+ if (gProductName)
+ {
+ llinfos << "Product name is: " << gProductName << llendl;
+ }
+ else
+ {
+ gProductName = "Second Life";
+ }
+ }
+
+ llinfos << "Starting " << gProductName << " Updater" << llendl;
+
+ // Build the URL to download the update
+ snprintf(gUpdateURL, sizeof(gUpdateURL), "http://secondlife.com/update-macos.php?userserver=%s", gUserServer);
+
+ // Real UI...
+ OSStatus err;
+ IBNibRef nib = NULL;
+
+ err = CreateNibReference(CFSTR("AutoUpdater"), &nib);
+
+ char windowTitle[MAX_PATH];
+ snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName);
+ CFStringRef windowTitleRef = NULL;
+ windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8);
+
+ if(err == noErr)
+ {
+ err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow);
+ }
+
+ if (err == noErr)
+ {
+ err = SetWindowTitleWithCFString(gWindow, windowTitleRef);
+ }
+ CFRelease(windowTitleRef);
+
+ if(err == noErr)
+ {
+ // Set up an event handler for the window.
+ EventTypeSpec handlerEvents[] =
+ {
+ { kEventClassCommand, kEventCommandProcess },
+ { kEventClassCustom, kEventCustomProgress },
+ { kEventClassCustom, kEventCustomDone }
+ };
+ InstallStandardEventHandler(GetWindowEventTarget(gWindow));
+ InstallWindowEventHandler(
+ gWindow,
+ NewEventHandlerUPP(dialogHandler),
+ GetEventTypeCount (handlerEvents),
+ handlerEvents,
+ 0,
+ &gEventHandler);
+ }
+
+ if(err == noErr)
+ {
+ ShowWindow(gWindow);
+ }
+
+ if(err == noErr)
+ {
+ pthread_create(&updatethread,
+ NULL,
+ &updatethreadproc,
+ NULL);
+
+ }
+
+ if(err == noErr)
+ {
+ RunAppModalLoopForWindow(gWindow);
+ }
+
+ void *threadresult;
+
+ pthread_join(updatethread, &threadresult);
+
+ if(!gCancelled && (gFailure != noErr))
+ {
+ // Something went wrong. Since we always just tell the user to download a new version, we don't really care what.
+ AlertStdCFStringAlertParamRec params;
+ SInt16 retval_mac = 1;
+ DialogRef alert = NULL;
+ OSStatus err;
+
+ params.version = kStdCFStringAlertVersionOne;
+ params.movable = false;
+ params.helpButton = false;
+ params.defaultText = (CFStringRef)kAlertDefaultOKText;
+ params.cancelText = 0;
+ params.otherText = 0;
+ params.defaultButton = 1;
+ params.cancelButton = 0;
+ params.position = kWindowDefaultPosition;
+ params.flags = 0;
+
+ err = CreateStandardAlert(
+ kAlertStopAlert,
+ CFSTR("Error"),
+ CFSTR("An error occurred while updating Second Life. Please download the latest version from www.secondlife.com."),
+ &params,
+ &alert);
+
+ if(err == noErr)
+ {
+ err = RunStandardAlert(
+ alert,
+ NULL,
+ &retval_mac);
+ }
+
+ }
+
+ // Don't dispose of things, just exit. This keeps the update thread from potentially getting hosed.
+ exit(0);
+
+ if(gWindow != NULL)
+ {
+ DisposeWindow(gWindow);
+ }
+
+ if(nib != NULL)
+ {
+ DisposeNibReference(nib);
+ }
+
+ return 0;
+}
+
+bool isDirWritable(FSRef &dir)
+{
+ bool result = false;
+
+ // Test for a writable directory by creating a directory, then deleting it again.
+ // This is kinda lame, but will pretty much always give the right answer.
+
+ OSStatus err = noErr;
+ char temp[PATH_MAX];
+
+ err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp));
+
+ if(err == noErr)
+ {
+ temp[0] = '\0';
+ strncat(temp, "/.test_XXXXXX", sizeof(temp) - 1);
+
+ if(mkdtemp(temp) != NULL)
+ {
+ // We were able to make the directory. This means the directory is writable.
+ result = true;
+
+ // Clean up.
+ rmdir(temp);
+ }
+ }
+
+#if 0
+ // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only.
+ UInt8 perm;
+ err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL);
+ if(err == noErr)
+ {
+ if(perm & kioACUserNoMakeChangesMask)
+ {
+ // Parent directory isn't writable.
+ llinfos << "Target parent directory not writable." << llendl;
+ err = -1;
+ replacingTarget = false;
+ }
+ }
+#endif
+
+ return result;
+}
+
+static void utf8str_to_HFSUniStr255(HFSUniStr255 *dest, const char* src)
+{
+ LLWString wstr = utf8str_to_wstring(src);
+ llutf16string utf16str = wstring_to_utf16str(wstr);
+
+ dest->length = utf16str.size();
+ if(dest->length > 255)
+ {
+ // There's onl room for 255 chars in a HFSUniStr25..
+ // Truncate to avoid stack smaching or other badness.
+ dest->length = 255;
+ }
+ memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length);
+}
+
+int restoreObject(const char* aside, const char* target, const char* path, const char* object)
+{
+ char source[PATH_MAX];
+ char dest[PATH_MAX];
+ snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object);
+ snprintf(dest, sizeof(dest), "%s/%s", target, path);
+ FSRef sourceRef;
+ FSRef destRef;
+ OSStatus err;
+ err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL);
+ if(err != noErr) return false;
+ err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL);
+ if(err != noErr) return false;
+
+ llinfos << "Copying " << source << " to " << dest << llendl;
+
+ err = FSCopyObject(
+ &sourceRef,
+ &destRef,
+ 0,
+ kFSCatInfoNone,
+ kDupeActionReplace,
+ NULL,
+ false,
+ false,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(err != noErr) return false;
+ return true;
+}
+
+// Replace any mention of "Second Life" with the product name.
+void filterFile(const char* filename)
+{
+ char temp[PATH_MAX];
+ // First copy the target's version, so we can run it through sed.
+ snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename);
+ system(temp);
+
+ // Now run it through sed.
+ snprintf(temp, sizeof(temp),
+ "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename);
+ system(temp);
+}
+
+void *updatethreadproc(void*)
+{
+ char tempDir[PATH_MAX] = "";
+ FSRef tempDirRef;
+ char temp[PATH_MAX];
+ char deviceNode[1024] = "";
+ FILE *downloadFile = NULL;
+ OSStatus err;
+ ProcessSerialNumber psn;
+ char target[PATH_MAX];
+ FSRef targetRef;
+ FSRef targetParentRef;
+ FSVolumeRefNum targetVol;
+ FSRef trashFolderRef, tempFolderRef;
+ Boolean replacingTarget = false;
+
+ memset(&tempDirRef, 0, sizeof(tempDirRef));
+ memset(&targetRef, 0, sizeof(targetRef));
+ memset(&targetParentRef, 0, sizeof(targetParentRef));
+
+ try
+ {
+ // Attempt to get a reference to the Second Life application bundle containing this updater.
+ // Any failures during this process will cause us to default to updating /Applications/Second Life.app
+ {
+ FSRef myBundle;
+
+ err = GetCurrentProcess(&psn);
+ if(err == noErr)
+ {
+ err = GetProcessBundleLocation(&psn, &myBundle);
+ }
+
+ if(err == noErr)
+ {
+ // Sanity check: Make sure the name of the item referenced by targetRef is "Second Life.app".
+ FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target));
+
+ llinfos << "Updater bundle location: " << target << llendl;
+ }
+
+ // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app
+ // so we need to go up 3 levels to get the path to the main application bundle.
+ if(err == noErr)
+ {
+ err = FSGetParentRef(&myBundle, &targetRef);
+ }
+ if(err == noErr)
+ {
+ err = FSGetParentRef(&targetRef, &targetRef);
+ }
+ if(err == noErr)
+ {
+ err = FSGetParentRef(&targetRef, &targetRef);
+ }
+
+ // And once more to get the parent of the target
+ if(err == noErr)
+ {
+ err = FSGetParentRef(&targetRef, &targetParentRef);
+ }
+
+ if(err == noErr)
+ {
+ FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
+ llinfos << "Path to target: " << target << llendl;
+ }
+
+ // Sanity check: make sure the target is a bundle with the right identifier
+ if(err == noErr)
+ {
+ CFURLRef targetURL = NULL;
+ CFBundleRef targetBundle = NULL;
+ CFStringRef targetBundleID = NULL;
+
+ // Assume the worst...
+ err = -1;
+
+ targetURL = CFURLCreateFromFSRef(NULL, &targetRef);
+
+ if(targetURL == NULL)
+ {
+ llinfos << "Error creating target URL." << llendl;
+ }
+ else
+ {
+ targetBundle = CFBundleCreate(NULL, targetURL);
+ }
+
+ if(targetBundle == NULL)
+ {
+ llinfos << "Failed to create target bundle." << llendl;
+ }
+ else
+ {
+ targetBundleID = CFBundleGetIdentifier(targetBundle);
+ }
+
+ if(targetBundleID == NULL)
+ {
+ llinfos << "Couldn't retrieve target bundle ID." << llendl;
+ }
+ else
+ {
+ if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo)
+ {
+ // This is the bundle we're looking for.
+ err = noErr;
+ replacingTarget = true;
+ }
+ else
+ {
+ llinfos << "Target bundle ID mismatch." << llendl;
+ }
+ }
+
+ // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released.
+ if(targetURL != NULL)
+ CFRelease(targetURL);
+ if(targetBundle != NULL)
+ CFRelease(targetBundle);
+
+ }
+
+ // Make sure the target's parent directory is writable.
+ if(err == noErr)
+ {
+ if(!isDirWritable(targetParentRef))
+ {
+ // Parent directory isn't writable.
+ llinfos << "Target parent directory not writable." << llendl;
+ err = -1;
+ replacingTarget = false;
+ }
+ }
+
+ if(err != noErr)
+ {
+ Boolean isDirectory;
+ llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl;
+
+ // Set up the parent directory
+ err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory);
+ if((err != noErr) || (!isDirectory))
+ {
+ // We're so hosed.
+ llinfos << "Applications directory not found, giving up." << llendl;
+ throw 0;
+ }
+
+ snprintf(target, sizeof(target), "/Applications/%s.app", gProductName);
+
+ memset(&targetRef, 0, sizeof(targetRef));
+ err = FSPathMakeRef((UInt8*)target, &targetRef, NULL);
+ if(err == fnfErr)
+ {
+ // This is fine, just means we're not replacing anything.
+ err = noErr;
+ replacingTarget = false;
+ }
+ else
+ {
+ replacingTarget = true;
+ }
+
+ // Make sure the target's parent directory is writable.
+ if(err == noErr)
+ {
+ if(!isDirWritable(targetParentRef))
+ {
+ // Parent directory isn't writable.
+ llinfos << "Target parent directory not writable." << llendl;
+ err = -1;
+ replacingTarget = false;
+ }
+ }
+
+ }
+
+ // If we haven't fixed all problems by this point, just bail.
+ if(err != noErr)
+ {
+ llinfos << "Unable to pick a target, giving up." << llendl;
+ throw 0;
+ }
+ }
+
+ // Find the volID of the volume the target resides on
+ {
+ FSCatalogInfo info;
+ err = FSGetCatalogInfo(
+ &targetParentRef,
+ kFSCatInfoVolume,
+ &info,
+ NULL,
+ NULL,
+ NULL);
+
+ if(err != noErr)
+ throw 0;
+
+ targetVol = info.volume;
+ }
+
+ // Find the temporary items and trash folders on that volume.
+ err = FSFindFolder(
+ targetVol,
+ kTrashFolderType,
+ true,
+ &trashFolderRef);
+
+ if(err != noErr)
+ throw 0;
+
+ err = FSFindFolder(
+ targetVol,
+ kTemporaryFolderType,
+ true,
+ &tempFolderRef);
+
+ if(err != noErr)
+ throw 0;
+
+ err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp));
+
+ if(err != noErr)
+ throw 0;
+
+ temp[0] = '\0';
+ strncat(temp, "/SecondLifeUpdate_XXXXXX", sizeof(temp) - 1);
+ if(mkdtemp(temp) == NULL)
+ {
+ throw 0;
+ }
+
+ strcpy(tempDir, temp);
+
+ llinfos << "tempDir is " << tempDir << llendl;
+
+ err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL);
+
+ if(err != noErr)
+ throw 0;
+
+ chdir(tempDir);
+
+ snprintf(temp, sizeof(temp), "SecondLife.dmg");
+
+ downloadFile = fopen(temp, "wb");
+ if(downloadFile == NULL)
+ {
+ throw 0;
+ }
+
+ {
+ CURL *curl = curl_easy_init();
+
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback);
+ curl_easy_setopt(curl, CURLOPT_FILE, downloadFile);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func);
+ curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+
+ sendProgress(0, 1, CFSTR("Downloading..."));
+
+ CURLcode result = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+
+ if(gCancelled)
+ {
+ llinfos << "User cancel, bailing out."<< llendl;
+ throw 0;
+ }
+
+ if(result != CURLE_OK)
+ {
+ llinfos << "Error " << result << " while downloading disk image."<< llendl;
+ throw 0;
+ }
+
+ fclose(downloadFile);
+ downloadFile = NULL;
+ }
+
+ sendProgress(0, 0, CFSTR("Mounting image..."));
+ LLFile::mkdir("mnt", 0700);
+
+ // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder,
+ // but if our cleanup fails, this makes it much harder for the user to unmount the image.
+ LLString mountOutput;
+ FILE *mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r");
+
+ if(mounter == NULL)
+ {
+ llinfos << "Failed to mount disk image, exiting."<< llendl;
+ throw 0;
+ }
+
+ // We need to scan the output from hdiutil to find the device node it uses to attach the disk image.
+ // If we don't have this information, we can't detach it later.
+ while(mounter != NULL)
+ {
+ size_t len = fread(temp, 1, sizeof(temp)-1, mounter);
+ temp[len] = 0;
+ mountOutput.append(temp);
+ if(len < sizeof(temp)-1)
+ {
+ // End of file or error.
+ if(pclose(mounter) != 0)
+ {
+ llinfos << "Failed to mount disk image, exiting."<< llendl;
+ throw 0;
+ }
+ mounter = NULL;
+ }
+ }
+
+ if(!mountOutput.empty())
+ {
+ const char *s = mountOutput.c_str();
+ char *prefix = "/dev/";
+ char *sub = strstr(s, prefix);
+
+ if(sub != NULL)
+ {
+ sub += strlen(prefix);
+ sscanf(sub, "%s", deviceNode);
+ }
+ }
+
+ if(deviceNode[0] != 0)
+ {
+ llinfos << "Disk image attached on /dev/" << deviceNode << llendl;
+ }
+ else
+ {
+ llinfos << "Disk image device node not found!" << llendl;
+ }
+
+ // Get an FSRef to the new application on the disk image
+ FSRef sourceRef;
+ snprintf(temp, sizeof(temp), "%s/mnt/Second Life.app", tempDir);
+
+ llinfos << "Source application is: " << temp << llendl;
+
+ err = FSPathMakeRef((UInt8 *)temp, &sourceRef, NULL);
+ if(err != noErr)
+ throw 0;
+
+ FSRef asideRef;
+ char aside[MAX_PATH];
+
+ // this will hold the name of the destination target
+ HFSUniStr255 appNameUniStr;
+
+ if(replacingTarget)
+ {
+ // Get the name of the target we're replacing
+ err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL);
+ if(err != noErr)
+ throw 0;
+
+ // Move aside old version (into work directory)
+ err = FSMoveObject(&targetRef, &tempDirRef, &asideRef);
+ if(err != noErr)
+ throw 0;
+
+ // Grab the path for later use.
+ err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside));
+ }
+ else
+ {
+ // Construct the name of the target based on the product name
+ char appName[MAX_PATH];
+ snprintf(appName, sizeof(appName), "%s.app", gProductName);
+ utf8str_to_HFSUniStr255( &appNameUniStr, appName );
+ }
+
+ sendProgress(0, 0, CFSTR("Copying files..."));
+
+ llinfos << "Starting copy..." << llendl;
+
+ // Copy the new version from the disk image to the target location.
+ err = FSCopyObject(
+ &sourceRef,
+ &targetParentRef,
+ 0,
+ kFSCatInfoNone,
+ kDupeActionStandard,
+ &appNameUniStr,
+ false,
+ false,
+ NULL,
+ NULL,
+ &targetRef,
+ NULL);
+
+ // Grab the path for later use.
+ err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target));
+ if(err != noErr)
+ throw 0;
+
+ llinfos << "Copy complete. Target = " << target << llendl;
+
+ if(err != noErr)
+ {
+ // Something went wrong during the copy. Attempt to put the old version back and bail.
+ (void)FSDeleteObjects(&targetRef);
+ if(replacingTarget)
+ {
+ (void)FSMoveObject(&asideRef, &targetParentRef, NULL);
+ }
+ throw 0;
+ }
+ else
+ {
+ // The update has succeeded. Clear the cache directory.
+
+ sendProgress(0, 0, CFSTR("Clearing cache..."));
+
+ llinfos << "Clearing cache..." << llendl;
+
+ char mask[LL_MAX_PATH];
+ sprintf(mask, "%s*.*", gDirUtilp->getDirDelimiter().c_str());
+ gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask);
+
+ llinfos << "Clear complete." << llendl;
+
+ }
+ }
+ catch(...)
+ {
+ if(!gCancelled)
+ if(gFailure == noErr)
+ gFailure = -1;
+ }
+
+ // Failures from here on out are all non-fatal and not reported.
+ sendProgress(0, 3, CFSTR("Cleaning up..."));
+
+ // Close disk image file if necessary
+ if(downloadFile != NULL)
+ {
+ llinfos << "Closing download file." << llendl;
+
+ fclose(downloadFile);
+ downloadFile = NULL;
+ }
+
+ sendProgress(1, 3);
+ // Unmount image
+ if(deviceNode[0] != 0)
+ {
+ llinfos << "Detaching disk image." << llendl;
+
+ snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode);
+ system(temp);
+ }
+
+ sendProgress(2, 3);
+
+ // Move work directory to the trash
+ if(tempDir[0] != 0)
+ {
+// chdir("/");
+// FSDeleteObjects(tempDirRef);
+
+ llinfos << "Moving work directory to the trash." << llendl;
+
+ err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL);
+
+// snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir);
+// printf("%s\n", temp);
+// system(temp);
+ }
+
+ if(!gCancelled && !gFailure && (target[0] != 0))
+ {
+ llinfos << "Touching application bundle." << llendl;
+
+ snprintf(temp, sizeof(temp), "touch '%s'", target);
+ system(temp);
+
+ llinfos << "Launching updated application." << llendl;
+
+ snprintf(temp, sizeof(temp), "open '%s'", target);
+ system(temp);
+ }
+
+ sendDone();
+
+ return(NULL);
+}
diff --git a/indra/newview/English.lproj/InfoPlist.strings b/indra/newview/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..8cbe635155
--- /dev/null
+++ b/indra/newview/English.lproj/InfoPlist.strings
@@ -0,0 +1,5 @@
+/* Localized versions of Info.plist keys */
+
+CFBundleName = "Second Life";
+CFBundleShortVersionString = "Second Life version 1.13.1.6";
+CFBundleGetInfoString = "Second Life version 1.13.1.6, Copyright 2004-2006 Linden Research, Inc.";
diff --git a/indra/newview/Info-SecondLife.plist b/indra/newview/Info-SecondLife.plist
new file mode 100644
index 0000000000..c2ec0313af
--- /dev/null
+++ b/indra/newview/Info-SecondLife.plist
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>Second Life</string>
+ <key>CFBundleIconFile</key>
+ <string>secondlife.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.secondlife.indra.viewer</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>Second Life</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleURLTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleURLName</key>
+ <string>Second Life URL</string>
+ <key>CFBundleURLSchemes</key>
+ <array>
+ <string>secondlife</string>
+ </array>
+ <key>LSIsAppleDefaultForScheme</key>
+ <true/>
+ </dict>
+ </array>
+ <key>CFBundleVersion</key>
+ <string>1.13.1.6</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+</dict>
+</plist>
diff --git a/indra/newview/Info-SecondLifeVorbis.plist b/indra/newview/Info-SecondLifeVorbis.plist
new file mode 100644
index 0000000000..9cb367eec1
--- /dev/null
+++ b/indra/newview/Info-SecondLifeVorbis.plist
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>SecondLifeVorbis</string>
+ <key>CFBundleGetInfoString</key>
+ <string></string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.secondlife.indra.vorbis</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>SecondLifeVorbis</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string></string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>0.0.1d1</string>
+</dict>
+</plist>
diff --git a/indra/newview/SecondLife.nib/classes.nib b/indra/newview/SecondLife.nib/classes.nib
new file mode 100644
index 0000000000..ea58db1189
--- /dev/null
+++ b/indra/newview/SecondLife.nib/classes.nib
@@ -0,0 +1,4 @@
+{
+IBClasses = ();
+IBVersion = 1;
+}
diff --git a/indra/newview/SecondLife.nib/info.nib b/indra/newview/SecondLife.nib/info.nib
new file mode 100644
index 0000000000..1b531de104
--- /dev/null
+++ b/indra/newview/SecondLife.nib/info.nib
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>85 13 356 240 0 0 1280 1002 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>27 314 247 44 0 0 1280 1002 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>362.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>191</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>7D24</string>
+ <key>targetFramework</key>
+ <string>IBCarbonFramework</string>
+</dict>
+</plist>
diff --git a/indra/newview/SecondLife.nib/objects.xib b/indra/newview/SecondLife.nib/objects.xib
new file mode 100644
index 0000000000..b7ff30f2b2
--- /dev/null
+++ b/indra/newview/SecondLife.nib/objects.xib
@@ -0,0 +1,259 @@
+<?xml version="1.0" standalone="yes"?>
+<object class="NSIBObjectData">
+ <string name="targetFramework">IBCarbonFramework</string>
+ <object name="rootObject" class="NSCustomObject" id="1">
+ <string name="customClass">NSApplication</string>
+ </object>
+ <array count="31" name="allObjects">
+ <object class="IBCarbonMenu" id="29">
+ <string name="title">SecondLife</string>
+ <array count="4" name="items">
+ <object class="IBCarbonMenuItem" id="182">
+ <string name="title">Second Life</string>
+ <object name="submenu" class="IBCarbonMenu" id="181">
+ <string name="title">Second Life</string>
+ <array count="1" name="items">
+ <object class="IBCarbonMenuItem" id="183">
+ <boolean name="disabled">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">About Second Life</string>
+ <int name="keyEquivalentModifier">0</int>
+ <ostype name="command">abou</ostype>
+ </object>
+ </array>
+ <string name="name">_NSAppleMenu</string>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="127">
+ <string name="title">File</string>
+ <object name="submenu" class="IBCarbonMenu" id="131">
+ <string name="title">File</string>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="152">
+ <string name="title">Edit</string>
+ <object name="submenu" class="IBCarbonMenu" id="147">
+ <string name="title">Edit</string>
+ <array count="10" name="items">
+ <object class="IBCarbonMenuItem" id="141">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Undo</string>
+ <string name="keyEquivalent">z</string>
+ <ostype name="command">undo</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="146">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Redo</string>
+ <string name="keyEquivalent">Z</string>
+ <ostype name="command">redo</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="142">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="143">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Cut</string>
+ <string name="keyEquivalent">x</string>
+ <ostype name="command">cut </ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="149">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Copy</string>
+ <string name="keyEquivalent">c</string>
+ <ostype name="command">copy</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="144">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Paste</string>
+ <string name="keyEquivalent">v</string>
+ <ostype name="command">past</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="151">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Delete</string>
+ <ostype name="command">clea</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="148">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Select All</string>
+ <string name="keyEquivalent">a</string>
+ <ostype name="command">sall</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="188">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="187">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Special Characters…</string>
+ <ostype name="command">chrp</ostype>
+ </object>
+ </array>
+ </object>
+ </object>
+ <object class="IBCarbonMenuItem" id="153">
+ <string name="title">Window</string>
+ <object name="submenu" class="IBCarbonMenu" id="154">
+ <string name="title">Window</string>
+ <array count="6" name="items">
+ <object class="IBCarbonMenuItem" id="155">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Minimize Window</string>
+ <string name="keyEquivalent">m</string>
+ <ostype name="command">mini</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="184">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Minimize All Windows</string>
+ <string name="keyEquivalent">m</string>
+ <int name="keyEquivalentModifier">1572864</int>
+ <ostype name="command">mina</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="186">
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Zoom</string>
+ <ostype name="command">zoom</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="156">
+ <boolean name="separator">TRUE</boolean>
+ </object>
+ <object class="IBCarbonMenuItem" id="157">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Bring All to Front</string>
+ <ostype name="command">bfrt</ostype>
+ </object>
+ <object class="IBCarbonMenuItem" id="185">
+ <boolean name="dynamic">TRUE</boolean>
+ <boolean name="updateSingleItem">TRUE</boolean>
+ <string name="title">Arrange in Front</string>
+ <int name="keyEquivalentModifier">1572864</int>
+ <ostype name="command">frnt</ostype>
+ </object>
+ </array>
+ <string name="name">_NSWindowsMenu</string>
+ </object>
+ </object>
+ </array>
+ <string name="name">_NSMainMenu</string>
+ </object>
+ <reference idRef="127"/>
+ <reference idRef="131"/>
+ <reference idRef="141"/>
+ <reference idRef="142"/>
+ <reference idRef="143"/>
+ <reference idRef="144"/>
+ <reference idRef="146"/>
+ <reference idRef="147"/>
+ <reference idRef="148"/>
+ <reference idRef="149"/>
+ <reference idRef="151"/>
+ <reference idRef="152"/>
+ <reference idRef="153"/>
+ <reference idRef="154"/>
+ <reference idRef="155"/>
+ <reference idRef="156"/>
+ <reference idRef="157"/>
+ <reference idRef="181"/>
+ <reference idRef="182"/>
+ <reference idRef="183"/>
+ <reference idRef="184"/>
+ <reference idRef="185"/>
+ <reference idRef="186"/>
+ <reference idRef="187"/>
+ <reference idRef="188"/>
+ <object class="IBCarbonRootControl" id="190">
+ <string name="bounds">0 0 482 694 </string>
+ <string name="viewFrame">0 0 694 482 </string>
+ <array count="2" name="subviews">
+ <object class="IBCarbonButton" id="192">
+ <string name="bounds">442 604 462 674 </string>
+ <string name="viewFrame">604 442 70 20 </string>
+ <string name="title">OK</string>
+ <ostype name="command">ok </ostype>
+ <object name="layoutInfo" class="IBCarbonHILayoutInfo">
+ <int name="bindingBottomKind">2</int>
+ <int name="bindingRightKind">2</int>
+ </object>
+ <int name="buttonType">1</int>
+ </object>
+ <object class="IBCarbonScrollView" id="201">
+ <string name="bounds">20 20 422 674 </string>
+ <string name="viewFrame">20 20 654 402 </string>
+ <array count="1" name="subviews">
+ <object class="IBCarbonTextView" id="200">
+ <string name="bounds">20 20 422 659 </string>
+ <string name="viewFrame">0 0 639 402 </string>
+ <ostype name="controlSignature">text</ostype>
+ <int name="fontStyle">5</int>
+ <boolean name="readOnly">TRUE</boolean>
+ </object>
+ </array>
+ <boolean name="scrollHorizontally">FALSE</boolean>
+ </object>
+ </array>
+ </object>
+ <object class="IBCarbonWindow" id="191">
+ <string name="windowRect">84 72 566 766 </string>
+ <string name="title">Release Notes</string>
+ <reference name="rootControl" idRef="190"/>
+ <boolean name="receiveUpdates">FALSE</boolean>
+ <boolean name="hasCloseBox">FALSE</boolean>
+ <boolean name="hasCollapseBox">FALSE</boolean>
+ <boolean name="hasHorizontalZoom">FALSE</boolean>
+ <boolean name="isResizable">FALSE</boolean>
+ <boolean name="hasVerticalZoom">FALSE</boolean>
+ <boolean name="liveResize">TRUE</boolean>
+ <boolean name="compositing">TRUE</boolean>
+ <int name="carbonWindowClass">4</int>
+ <int name="windowPosition">1</int>
+ <boolean name="isConstrained">FALSE</boolean>
+ </object>
+ <reference idRef="192"/>
+ <reference idRef="200"/>
+ <reference idRef="201"/>
+ </array>
+ <array count="31" name="allParents">
+ <reference idRef="1"/>
+ <reference idRef="29"/>
+ <reference idRef="127"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="152"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="29"/>
+ <reference idRef="29"/>
+ <reference idRef="153"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="182"/>
+ <reference idRef="29"/>
+ <reference idRef="181"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="154"/>
+ <reference idRef="147"/>
+ <reference idRef="147"/>
+ <reference idRef="191"/>
+ <reference idRef="1"/>
+ <reference idRef="190"/>
+ <reference idRef="201"/>
+ <reference idRef="190"/>
+ </array>
+ <dictionary count="3" name="nameTable">
+ <string>Files Owner</string>
+ <reference idRef="1"/>
+ <string>MenuBar</string>
+ <reference idRef="29"/>
+ <string>Release Notes</string>
+ <reference idRef="191"/>
+ </dictionary>
+ <unsigned_int name="nextObjectID">202</unsigned_int>
+</object>
diff --git a/indra/newview/VertexCache.h b/indra/newview/VertexCache.h
new file mode 100644
index 0000000000..e2b26ec5ee
--- /dev/null
+++ b/indra/newview/VertexCache.h
@@ -0,0 +1,87 @@
+/**
+ * @file VertexCache.h
+ * @brief VertexCache class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef VERTEX_CACHE_H
+
+#define VERTEX_CACHE_H
+
+class VertexCache
+{
+
+public:
+
+ VertexCache(int size)
+ {
+ numEntries = size;
+
+ entries = new int[numEntries];
+
+ for(int i = 0; i < numEntries; i++)
+ entries[i] = -1;
+ }
+
+ VertexCache() { VertexCache(16); }
+ ~VertexCache() { delete[] entries; entries = 0; }
+
+ bool InCache(int entry)
+ {
+ bool returnVal = false;
+ for(int i = 0; i < numEntries; i++)
+ {
+ if(entries[i] == entry)
+ {
+ returnVal = true;
+ break;
+ }
+ }
+
+ return returnVal;
+ }
+
+ int AddEntry(int entry)
+ {
+ int removed;
+
+ removed = entries[numEntries - 1];
+
+ //push everything right one
+ for(int i = numEntries - 2; i >= 0; i--)
+ {
+ entries[i + 1] = entries[i];
+ }
+
+ entries[0] = entry;
+
+ return removed;
+ }
+
+ void Clear()
+ {
+ memset(entries, -1, sizeof(int) * numEntries);
+ }
+
+ void Copy(VertexCache* inVcache)
+ {
+ for(int i = 0; i < numEntries; i++)
+ {
+ inVcache->Set(i, entries[i]);
+ }
+ }
+
+ int At(int index) { return entries[index]; }
+ void Set(int index, int value) { entries[index] = value; }
+
+private:
+
+ int *entries;
+ int numEntries;
+
+};
+
+#endif
diff --git a/indra/newview/VorbisFramework.h b/indra/newview/VorbisFramework.h
new file mode 100644
index 0000000000..8d98979d1b
--- /dev/null
+++ b/indra/newview/VorbisFramework.h
@@ -0,0 +1,62 @@
+/**
+ * @file VorbisFramework.h
+ * @author Dave Camp
+ * @date Fri Oct 10 2003
+ * @brief For the Macview project
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ogg/ogg.h"
+#include "vorbis/codec.h"
+#include "vorbis/vorbisenc.h"
+
+extern int mac_vorbis_analysis(vorbis_block *vb,ogg_packet *op);
+
+extern int mac_vorbis_analysis_headerout(vorbis_dsp_state *v,
+ vorbis_comment *vc,
+ ogg_packet *op,
+ ogg_packet *op_comm,
+ ogg_packet *op_code);
+
+extern int mac_vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi);
+
+extern int mac_vorbis_encode_ctl(vorbis_info *vi,int number,void *arg);
+
+extern int mac_vorbis_encode_setup_init(vorbis_info *vi);
+
+extern int mac_vorbis_encode_setup_managed(vorbis_info *vi,
+ long channels,
+ long rate,
+
+ long max_bitrate,
+ long nominal_bitrate,
+ long min_bitrate);
+
+extern void mac_vorbis_info_init(vorbis_info *vi);
+extern void mac_vorbis_info_clear(vorbis_info *vi);
+extern void mac_vorbis_comment_init(vorbis_comment *vc);
+extern void mac_vorbis_comment_clear(vorbis_comment *vc);
+extern int mac_vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb);
+extern int mac_vorbis_block_clear(vorbis_block *vb);
+extern void mac_vorbis_dsp_clear(vorbis_dsp_state *v);
+extern float **mac_vorbis_analysis_buffer(vorbis_dsp_state *v,int vals);
+extern int mac_vorbis_analysis_wrote(vorbis_dsp_state *v,int vals);
+extern int mac_vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb);
+
+extern int mac_ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op);
+extern int mac_ogg_stream_init(ogg_stream_state *os,int serialno);
+extern int mac_ogg_stream_flush(ogg_stream_state *os, ogg_page *og);
+extern int mac_ogg_stream_pageout(ogg_stream_state *os, ogg_page *og);
+extern int mac_ogg_page_eos(ogg_page *og);
+extern int mac_ogg_stream_clear(ogg_stream_state *os);
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/indra/newview/app_settings/CA.pem b/indra/newview/app_settings/CA.pem
new file mode 100644
index 0000000000..b14e7346ad
--- /dev/null
+++ b/indra/newview/app_settings/CA.pem
@@ -0,0 +1,1811 @@
+-----BEGIN CERTIFICATE-----
+MIIEuDCCA6CgAwIBAgIBBDANBgkqhkiG9w0BAQUFADCBtDELMAkGA1UEBhMCQlIx
+EzARBgNVBAoTCklDUC1CcmFzaWwxPTA7BgNVBAsTNEluc3RpdHV0byBOYWNpb25h
+bCBkZSBUZWNub2xvZ2lhIGRhIEluZm9ybWFjYW8gLSBJVEkxETAPBgNVBAcTCEJy
+YXNpbGlhMQswCQYDVQQIEwJERjExMC8GA1UEAxMoQXV0b3JpZGFkZSBDZXJ0aWZp
+Y2Fkb3JhIFJhaXogQnJhc2lsZWlyYTAeFw0wMTExMzAxMjU4MDBaFw0xMTExMzAy
+MzU5MDBaMIG0MQswCQYDVQQGEwJCUjETMBEGA1UEChMKSUNQLUJyYXNpbDE9MDsG
+A1UECxM0SW5zdGl0dXRvIE5hY2lvbmFsIGRlIFRlY25vbG9naWEgZGEgSW5mb3Jt
+YWNhbyAtIElUSTERMA8GA1UEBxMIQnJhc2lsaWExCzAJBgNVBAgTAkRGMTEwLwYD
+VQQDEyhBdXRvcmlkYWRlIENlcnRpZmljYWRvcmEgUmFpeiBCcmFzaWxlaXJhMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwPMudwX/hvm+Uh2b/lQAcHVA
+isamaLkWdkwP9/S/tOKIgRrL6Oy+ZIGlOUdd6uYtk9Ma/3pUpgcfNAj0vYm5gsyj
+Qo9emsc+x6m4VWwk9iqMZSCK5EQkAq/Ut4n7KuLE1+gdftwdIgxfUsPt4CyNrY50
+QV57KM2UT8x5rrmzEjr7TICGpSUAl2gVqe6xaii+bmYR1QrmWaBSAG59LrkrjrYt
+bRhFboUDe1DK+6T8s5L6k8c8okpbHpa9veMztDVC9sPJ60MWXh6anVKo1UcLcbUR
+yEeNvZneVRKAAU6ouwdjDvwlsaKydFKwed0ToQ47bmUKgcm+wV3eTRk36UOnTwID
+AQABo4HSMIHPME4GA1UdIARHMEUwQwYFYEwBAQAwOjA4BggrBgEFBQcCARYsaHR0
+cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0RQQ2FjcmFpei5wZGYwPQYDVR0f
+BDYwNDAyoDCgLoYsaHR0cDovL2FjcmFpei5pY3BicmFzaWwuZ292LmJyL0xDUmFj
+cmFpei5jcmwwHQYDVR0OBBYEFIr68VeEERM1kEL6V0lUaQ2kxPA3MA8GA1UdEwEB
+/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAZA5c1
+U/hgIh6OcgLAfiJgFWpvmDZWqlV30/bHFpj8iBobJSm5uDpt7TirYh1Uxe3fQaGl
+YjJe+9zd+izPRbBqXPVQA34EXcwk4qpWuf1hHriWfdrx8AcqSqr6CuQFwSr75Fos
+SzlwDADa70mT7wZjAmQhnZx2xJ6wfWlT9VQfS//JYeIc7Fue2JNLd00UOSMMaiK/
+t79enKNHEA2fupH3vEigf5Eh4bVAN5VohrTm6MY53x7XQZZr1ME7a55lFEnSeT0u
+mlOAjR2mAbvSM5X5oSZNrmetdzyTj2flCM8CC7MLab0kkdngRIlUBGHF1/S5nmPb
+K+9A46sd33oqK8n8
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDtTCCAp2gAwIBAgIRANAeQJAAAEZSAAAAAQAAAAQwDQYJKoZIhvcNAQEF
+BQAwgYkxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJEQzETMBEGA1UEBxMKV2Fz
+aGluZ3RvbjEXMBUGA1UEChMOQUJBLkVDT00sIElOQy4xGTAXBgNVBAMTEEFC
+QS5FQ09NIFJvb3QgQ0ExJDAiBgkqhkiG9w0BCQEWFWFkbWluQGRpZ3NpZ3Ry
+dXN0LmNvbTAeFw05OTA3MTIxNzMzNTNaFw0wOTA3MDkxNzMzNTNaMIGJMQsw
+CQYDVQQGEwJVUzELMAkGA1UECBMCREMxEzARBgNVBAcTCldhc2hpbmd0b24x
+FzAVBgNVBAoTDkFCQS5FQ09NLCBJTkMuMRkwFwYDVQQDExBBQkEuRUNPTSBS
+b290IENBMSQwIgYJKoZIhvcNAQkBFhVhZG1pbkBkaWdzaWd0cnVzdC5jb20w
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx0xHgeVVDBwhMywVC
+AOINg0Y95JO6tgbTDVm9PsHOQ2cBiiGo77zM0KLMsFWWU4RmBQDaREmA2FQK
+pSWGlO1jVv9wbKOhGdJ4vmgqRF4vz8wYXke8OrFGPR7wuSw0X4x8TAgpnUBV
+6zx9g9618PeKgw6hTLQ6pbNfWiKX7BmbwQVo/ea3qZGULOR4SCQaJRk665Wc
+OQqKz0Ky8BzVX/tr7WhWezkscjiw7pOp03t3POtxA6k4ShZsiSrK2jMTecJV
+jO2cu/LLWxD4LmE1xilMKtAqY9FlWbT4zfn0AIS2V0KFnTKo+SpU+/94Qby9
+cSj0u5C8/5Y0BONFnqFGKECBAgMBAAGjFjAUMBIGA1UdEwEB/wQIMAYBAf8C
+AQgwDQYJKoZIhvcNAQEFBQADggEBAARvJYbk5pYntNlCwNDJALF/VD6Hsm0k
+qS8Kfv2kRLD4VAe9G52dyntQJHsRW0mjpr8SdNWJt7cvmGQlFLdh6X9ggGvT
+ZOirvRrWUfrAtF13Gn9kCF55xgVM8XrdTX3O5kh7VNJhkoHWG9YA8A6eKHeg
+TYjHInYZw8eeG6Z3ePhfm1bR8PIXrI6dWeYf/le22V7hXZ9F7GFoGUHhsiAm
+/lowdiT/QHI8eZ98IkirRs3bs4Ysj78FQdPB4xTjQRcm0HyncUwZ6EoPclgx
+fexgeqMiKL0ZJGA/O4dzwGvky663qyVDslUte6sGDnVdNOVdc22esnVApVnJ
+TzFxiNmIf1Q=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID5jCCAs6gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MTEyMDE1MDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAJnej8Mlo2k06AX3dLm/WpcZuS+U0pPlLYnKhHw/EEMbjIt8hFj4JHxI
+zyr9wBXZGH6EGhfT257XyuTZ16pYUYfw8ItITuLCxFlpMGK2MKKMCxGZYTVt
+fu/FsRkGIBKOQuHfD5YQUqjPnF+VFNivO3ULMSAfRC+iYkGzuxgh28pxPIzs
+trkNn+9R7017EvILDOGsQI93f7DKeHEMXRZxcKLXwjqFzQ6axOAAsNUl6twr
+5JQtOJyJQVdkKGUZHLZEtMgxa44Be3ZZJX8VHIQIfHNlIAqhBC4aMqiaILGc
+LCFZ5/vP7nAtCMpjPiybkxlqpMKX/7eGV4iFbJ4VFitNLLMCAwEAAaNjMGEw
+DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUoTYwFsuGkABFgFOxj8jYPXy+
+XxIwHwYDVR0jBBgwFoAUoTYwFsuGkABFgFOxj8jYPXy+XxIwDgYDVR0PAQH/
+BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4IBAQCKIBilvrMvtKaEAEAwKfq0FHNM
+eUWn9nDg6H5kHgqVfGphwu9OH77/yZkfB2FK4V1Mza3u0FIy2VkyvNp5ctZ7
+CegCgTXTCt8RHcl5oIBN/lrXVtbtDyqvpxh1MwzqwWEFT2qaifKNuZ8u77Bf
+WgDrvq2g+EQFZ7zLBO+eZMXpyD8Fv8YvBxzDNnGGyjhmSs3WuEvGbKeXO/oT
+LW4jYYehY0KswsuXn2Fozy1MBJ3XJU8KDk2QixhWqJNIV9xvrr2eZ1d3iVCz
+vhGbRWeDhhmH05i9CBoWH1iCC+GWaQVLjuyDUTEH1dSf/1l7qG6Fz9NLqUmw
+X7A5KGgOc90lmt4S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF5jCCA86gAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgzELMAkGA1UEBhMC
+VVMxHTAbBgNVBAoTFEFPTCBUaW1lIFdhcm5lciBJbmMuMRwwGgYDVQQLExNB
+bWVyaWNhIE9ubGluZSBJbmMuMTcwNQYDVQQDEy5BT0wgVGltZSBXYXJuZXIg
+Um9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4XDTAyMDUyOTA2MDAw
+MFoXDTM3MDkyODIzNDMwMFowgYMxCzAJBgNVBAYTAlVTMR0wGwYDVQQKExRB
+T0wgVGltZSBXYXJuZXIgSW5jLjEcMBoGA1UECxMTQW1lcmljYSBPbmxpbmUg
+SW5jLjE3MDUGA1UEAxMuQU9MIFRpbWUgV2FybmVyIFJvb3QgQ2VydGlmaWNh
+dGlvbiBBdXRob3JpdHkgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
+ggIBALQ3WggWmRToVbEbJGv8x4vmh6mJ7ouZzU9AhqS2TcnZsdw8TQ2FTBVs
+RotSeJ/4I/1n9SQ6aF3Q92RhQVSji6UI0ilbm2BPJoPRYxJWSXakFsKlnUWs
+i4SVqBax7J/qJBrvuVdcmiQhLE0OcR+mrF1FdAOYxFSMFkpBd4aVdQxHAWZg
+/BXxD+r1FHjHDtdugRxev17nOirYlxcwfACtCJ0zr7iZYYCLqJV+FNwSbKTQ
+2O9ASQI2+W6p1h2WVgSysy0WVoaP2SBXgM1nEG2wTPDaRrbqJS5Gr42whTg0
+ixQmgiusrpkLjhTXUr2eacOGAgvqdnUxCc4zGSGFQ+aJLZ8lN2fxI2rSAG2X
++Z/nKcrdH9cG6rjJuQkhn8g/BsXS6RJGAE57COtCPStIbp1n3UsC5ETzkxml
+J85per5n0/xQpCyrw2u544BMzwVhSyvcG7mm0tCq9Stz+86QNZ8MUhy/XCFh
+EVsVS6kkUfykXPcXnbDS+gfpj1bkGoxoigTTfFrjnqKhynFbotSg5ymFXQNo
+Kk/SBtc9+cMDLz9l+WceR0DTYw/j1Y75hauXTLPXJuuWCpTehTacyH+BCQJJ
+Kg71ZDIMgtG6aoIbs0t0EfOMd9afv9w3pKdVBC/UMejTRrkDfNoSTllkt1Ex
+MVCgyhwn2RAurda9EGYrw7AiShJbAgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMB
+Af8wHQYDVR0OBBYEFE9pbQN+nZ8HGEO8txBO1b+pxCAoMB8GA1UdIwQYMBaA
+FE9pbQN+nZ8HGEO8txBO1b+pxCAoMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG
+9w0BAQUFAAOCAgEAO/Ouyuguh4X7ZVnnrREUpVe8WJ8kEle7+z802u6teio0
+cnAxa8cZmIDJgt43d15Ui47y6mdPyXSEkVYJ1eV6moG2gcKtNuTxVBFT8zRF
+ASbI5Rq8NEQh3q0l/HYWdyGQgJhXnU7q7C+qPBR7V8F+GBRn7iTGvboVsNIY
+vbdVgaxTwOjdaRITQrcCtQVBynlQboIOcXKTRuidDV29rs4prWPVVRaAMCf/
+drr3uNZK49m1+VLQTkCpx+XCMseqdiThawVQ68W/ClTluUI8JPu3B5wwn3la
+5uBAUhX0/Kr0VvlEl4ftDmVyXr4m+02kLQgH3thcoNyBM5kYJRF3p+v9WAks
+mWsbivNSPxpNSGDxoPYzAlOL7SUJuA0t7Zdz7NeWH45gDtoQmy8YJPamTQr5
+O8t1wswvziRpyQoijlmn94IM19drNZxDAGrElWe6nEXLuA4399xOAU++CrYD
+062KRffaJ00psUjf5BHklka9bAI+1lHIlRcBFanyqqryvy9lG2/QuRqT9Y41
+xICHPpQvZuTpqP9BnHAqTyo5GJUefvthATxRCC4oGKQWDzH9OmwjkyB24f0H
+hdFbP9IcczLd+rn4jM8Ch3qaluTtT4mNU0OrDhPAARW0eTjb/G49nlG2uBOL
+Z8/5fNkiHfZdxRwBL5joeiQYvITX+txyW/fBOmg=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4
+dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h
+bCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzEL
+MAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1B
+ZGRUcnVzdCBFeHRlcm5hbCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1
+c3QgRXh0ZXJuYWwgQ0EgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALf3GjPm8gAELTngTlvtH7xsD821+iO2zt6bETOXpClMfZOfvUq8
+k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9uMq/NzgtHj6RQa1wVsfwTz/oMp50
+ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzXmk6vBbOmcZSccbNQYArHE504
+B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LXa0Tkx63ubUFfclpxCDez
+eWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzNE0S3ySvdQwAl+mG5
+aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0WicCAwEAAaOB
+3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYDVR0PBAQD
+AgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0Jvf6
+xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdv
+cmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJ
+KoZIhvcNAQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZl
+j7DYd7usQWxHYINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5R
+xNKWt9x+Tu5w/Rw56wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjT
+K3rMUUKhemPR5ruhxSvCNr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1
+n6diIWgVIEM8med8vSTYqZEXc4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHx
+REzGBHNJdmAPx/i9F4BrLunMTA5amnkPIAou1Z5jJh5VkpTYghdae9C8x49O
+hgQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+HhcNMDAwNTMwMTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3Qw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwze
+xODcEyPNwTXH+9ZOEQpnXvUGW2ulCDtbKRY654eyNAbFvAWlA3yCyykQruGI
+gb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6ntGO0/7Gcrjyvd7ZWxbWroulpOj0O
+M3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyldI+Yrsj5wAYi56xz36Uu+1Lc
+sRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJchPXQhI2U0K7t4WaPW4XY5
+mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC+Mcdoq0Dlyz4zyXG
+9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0OBBYEFJWxtPCU
+tr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8EBTADAQH/
+MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBlMQsw
+CQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk
+ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAx
+IENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0
+MkhHma6X7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0Ph
+iVYrqW9yTkkz43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9
+tTEv2dB8Xfjea4MYeDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL
+/bscVjby/rK25Xa71SJlpz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlV
+g3sslgf/WSxEo8bl6ancoWOAWiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6
+tkD9xOQ14R0WHNC8K47Wcdk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCAv2gAwIBAgIBATANBgkqhkiG9w0BAQUFADBkMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSAwHgYDVQQDExdBZGRUcnVzdCBQdWJsaWMgQ0EgUm9vdDAe
+Fw0wMDA1MzAxMDQxNTBaFw0yMDA1MzAxMDQxNTBaMGQxCzAJBgNVBAYTAlNF
+MRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3QgVFRQ
+IE5ldHdvcmsxIDAeBgNVBAMTF0FkZFRydXN0IFB1YmxpYyBDQSBSb290MIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6Rowj4OIFMEg2Dybjxt+
+A3S72mnTRqX4jsIMEZBRpS9mVEBV6tsfSlbunyNu9DnLoblv8n75XYcmYZ4c
++OLspoH4IcUkzBEMP9smcnrHAZcHF/nXGCwwfQ56HmIexkvA/X1id9NEHif2
+P0tEs7c42TkfYNVRknMDtABp4/MUTu7R3AnPdzRGULD4EfL+OHn3Bzn+UZKX
+C1sIXzSGAa2Il+tmzV7R/9x98oTaunet3IAIx6eH1lWfl2royBFkuucZKT8R
+s3iQhCBSWxHveNCD9tVIkNAwHM+A+WD+eeSI8t0A65RF62WUaUC6wNW0uLp9
+BBGo6zEFlpROWCGOn9Bg/QIDAQABo4HRMIHOMB0GA1UdDgQWBBSBPjfYkrAf
+d59ctKtzquf2NGAv+jALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zCB
+jgYDVR0jBIGGMIGDgBSBPjfYkrAfd59ctKtzquf2NGAv+qFopGYwZDELMAkG
+A1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQLExRBZGRU
+cnVzdCBUVFAgTmV0d29yazEgMB4GA1UEAxMXQWRkVHJ1c3QgUHVibGljIENB
+IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBAAP3FUr4JNojVhaTdt02KLmu
+G7jD8WS6IBh4lSknVwW8fCr0uVFV2ocC3g8WFzH4qnkuCRO7r7IgGRLlk/lL
++YPoRNWyQSW/iHVv/xD8SlTQX/D67zZzfRs2RcYhbbQVuE7PnFylPVoAjgbj
+PGsye/Kf8Lb93/AoGEjwxrzQvzSAlsJKsW2Ox5BF3i9nrEUEo3rcVZLJR2bY
+GozH7ZxOmuASu7VqTITh4SINhwBk/ox9Yjllpu9CtoAlEmEBqCQTcAARJl/6
+NVDFSMwGR+gn2HCNX2TmoUQmXiLsks3/QppEIW1cxeMiHV9HEufOX1362Kqx
+My3ZdvJOOjMMK7MtkAY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJT
+RTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRU
+UCBOZXR3b3JrMSMwIQYDVQQDExpBZGRUcnVzdCBRdWFsaWZpZWQgQ0EgUm9v
+dDAeFw0wMDA1MzAxMDQ0NTBaFw0yMDA1MzAxMDQ0NTBaMGcxCzAJBgNVBAYT
+AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEdMBsGA1UECxMUQWRkVHJ1c3Qg
+VFRQIE5ldHdvcmsxIzAhBgNVBAMTGkFkZFRydXN0IFF1YWxpZmllZCBDQSBS
+b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5B6a/twJWoek
+n0e+EV+vhDTbYjx5eLfpMLXsDBwqxBb/4Oxx64r1EW7tTw2R0hIYLUkVAcKk
+IhPHEWT/IhKauY5cLwjPcWqzZwFZ8V1G87B4pfYOQnrjfxvM0PC3KP0q6p6z
+sLkEqv32x7SxuCqg+1jxGaBvcCV+PmlKfw8i2O+tCBGaKZnhqkRFmhJePp1t
+UvznoD1oL/BLcHwTOK28FSXx1s6rosAx1i+f4P8UWfyEk9mHfExUE+uf0S0R
++Bg6Ot4l2ffTQO2kBhLEO+GRwVY18BTcZTYJbqukB8c10cIDMzZbdSZtQvES
+a0NvS3GU+jQd7RNuyoB/mC9suWXY6QIDAQABo4HUMIHRMB0GA1UdDgQWBBQ5
+lYtii1zJ1IC6WA+XPxUIQ8yYpzALBgNVHQ8EBAMCAQYwDwYDVR0TAQH/BAUw
+AwEB/zCBkQYDVR0jBIGJMIGGgBQ5lYtii1zJ1IC6WA+XPxUIQ8yYp6FrpGkw
+ZzELMAkGA1UEBhMCU0UxFDASBgNVBAoTC0FkZFRydXN0IEFCMR0wGwYDVQQL
+ExRBZGRUcnVzdCBUVFAgTmV0d29yazEjMCEGA1UEAxMaQWRkVHJ1c3QgUXVh
+bGlmaWVkIENBIFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBABmrder4i2Vh
+lRO6aQTvhsoToMeqT2QbPxj2qC0sVY8FtzDqQmodwCVRLae/DLPt7wh/bDxG
+GuoYQ992zPlmhpwsaPXpF/gxsxjE1kh9I0xowX67ARRvxdlu3rsEQmr49lx9
+5dr6h+sNNVJn0J6XdgWTP5XHAeZpVTh/EGGZyeNfpso+gmNIquIISD6q8rKF
+Yqa0p9m9N5xotS1WfbC3P6CxB9bpT9zeRXEwMn8bLgn5v1Kh7sKAPgZcLlVA
+wRv1cEWw3F369nJad9Jjzc9YiQBCYz95OdBEsIJuQRno3eDBiFrRHnGTHyQw
+dOUeqN48Jzd/g66ed8/wMLH/S5noxqE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDpDCCAoygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAxMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MTExOTIwNDMwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMTCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKgv6KRpBgNHw+kqmP8ZonCa
+xlCyfqXfaE0bfA+2l2h9LaaLl+lkhsmj76CGv2BlnEtUiMJIxUo5vxTjWVXl
+GbR0yLQFOVwWpeKVBeASrlmLojNoWBym1BW32J/X3HGrfpq/m44zDyL9Hy7n
+BzbvYjnF3cu6JRQj3gzGPTzOggjmZj7aUTsWOqMFf6Dch9Wc/HKpoH145Lcx
+VR5lu9RhsCFg7RAycsWSJR74kEoYeEfffjA3PlAb2xzTa5qGUwew76wGePiE
+mf4hjUyAtgyC9mZweRrTT6PP8c9GsEsPPt2IYriMqQkoO3rHl+Ee5fSfwMCu
+JKDIodkP1nsmgmkyPacCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAdBgNV
+HQ4EFgQUAK3Zo/Z59m50qX8zPYEX10zPM94wHwYDVR0jBBgwFoAUAK3Zo/Z5
+9m50qX8zPYEX10zPM94wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUA
+A4IBAQB8itEfGDeC4Liwo+1WlchiYZwFos3CYiZhzRAW18y0ZTTQEYqtqKkF
+Zu90821fnZmv9ov761KyBZiibyrFVL0lvV+uyIbqRizBs73B6UlwGBaXCBOM
+IOAbLjpHyx7kADCVW/RFo8AasAFOq73AI25jP4BKxQft3OJvx8Fi8eNy1gTI
+dGcL+oiroQHIb/AUr9KZzVGTfu0uOMe9zkZQPXLjeSWdm4grECDdpbgyn43g
+Kd8hdIaC2y+CMMbHNYaz+ZZfRtsMRf3zUMNvxsNIrUam4SdHCh0Om7bCd39j
+8uB9Gr784N/Xx6dssPmuujz9dLQR6FgNgLzTqIA6me11zEZ7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFpDCCA4ygAwIBAgIBATANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTQW1lcmljYSBPbmxpbmUgSW5jLjE2MDQGA1UEAxMtQW1l
+cmljYSBPbmxpbmUgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAyMB4X
+DTAyMDUyODA2MDAwMFoXDTM3MDkyOTE0MDgwMFowYzELMAkGA1UEBhMCVVMx
+HDAaBgNVBAoTE0FtZXJpY2EgT25saW5lIEluYy4xNjA0BgNVBAMTLUFtZXJp
+Y2EgT25saW5lIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgMjCCAiIw
+DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMxBRR3pPU0Q9oyxQcngXssN
+t79Hc9PwVU3dxgz6sWYFas14tNwC206B89enfHG8dWOgXeMHDEjsJcQDIPT/
+DjsS/5uN4cbVG7RtIuOx238hZK+GvFciKtZHgVdEglZTvYYUAQv8f3SkWq7x
+uhG1m1hagLQ3eAkzfDJHA1zEpYNI9FdWboE2JxhP7JsowtS013wMPgwr38oE
+18aO6lhOqKSlGBxsRZijQdEt0sdtjRnxrXm3gT+9BoInLRBYBbV4Bbkv2wxr
+kJB+FFk4u5QkE+XRnRTf04JNRvCAOVIyD+OEsnpD8l7eXz8d3eOyG6ChKiMD
+bi4BFYdcpnV1x5dhvt6G3NRI270qv0pV2uh9UPu0gBe4lL8BPeraunzgWGcX
+uVjgiIZGZ2ydEEdYMtA1fHkqkKJaEBEjNa0vzORKW6fIJ/KD3l67Xnfn6KVu
+Y8INXWHQjNJsWiEOyiijzirplcdIz5ZvHZIlyMbGwcEMBawmxNJ10uEqZ8A9
+W6Wa6897GqidFEXlD6CaZd4vKL3Ob5Rmg0gp2OpljK+T2WSfVVcmv2/LNzGZ
+o2C7HK2JNDJiuEMhBnIMoVxtRsX6Kc8w3onccVvdtjc+31D1uAclJuW8tf48
+ArO3+L5DwYcRlJ4jbBeKuIonDFRH8KmzwICMoCfrHRnjB453cMor9H124Hhn
+AgMBAAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFE1FwWg4u3Op
+aaEg5+31IqEjFNeeMB8GA1UdIwQYMBaAFE1FwWg4u3OpaaEg5+31IqEjFNee
+MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQUFAAOCAgEAZ2sGuV9FOypL
+M7PmG2tZTiLMubekJcmnxPBUlgtk87FYT15R/LKXeydlwuXK5w0MJXti4/qf
+tIe3RUavg6WXSIylvfEWK5t2LHo1YGwRgJfMqZJS5ivmae2p+DYtLHe/YUjR
+Ywu5W1LtGLBDQiKmsXeu3mnFzcccobGlHBD7GL4acN3Bkku+KVqdPzW+5X1R
++FXgJXUjhx5c3LqdsKyzadsXg8n33gy8CNyRnqjQ1xU3c6U1uPx+xURABsPr
++CKAXEfOAuMRn0T//ZoyzH1kUQ7rVyZ2OuMeIjzCpjbdGe+n/BLzJsBZMYVM
+nNjP36TMzCmT/5RtdlwTCJfy7aULTd3oyWgOZtMADjMSW7yV5TKQqLPGbIOt
+d+6Lfn6xqavT4fG2wLHqiMDn05DpKJKUe2h7lyoKZy2FAjgQ5ANh1NolNscI
+WC2hp1GvMApJ9aZphwctREZ2jirlmjvXGKL8nDgQzMY70rUXOm/9riW99XJZ
+ZLF0KjhfGEzfz3EEWjbUvy+ZnOjZurGV5gJLIaFb1cFPj65pbVPbAZO1XB4Y
+3WRayhgoPmMEEf0cjQAPuDffZ4qdZqkCapH/E8ovXYO8h5Ns3CRRFgQlZvqz
+2cK6Kb6aSDiCmfS/O0oxGfm/jiEzFMpPVF/7zvuPcX/9XhmgD0uRuMRUvAaw
+RY8mkaKO/qk=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJJRTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0
+MSIwIAYDVQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUx
+MjE4NDYwMFoXDTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNV
+BAoTCUJhbHRpbW9yZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZ
+QmFsdGltb3JlIEN5YmVyVHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBAKMEuyKrmD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+h
+Xe2wCQVt2yguzmKiYv60iNoS6zjrIZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gR
+QKfRzMpijS3ljwumUNKoUMMo6vWrJYeKmpYcqWe4PwzV9/lSEy/CG9VwcPCP
+wBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSuXmD+tqYF/LTdB1kC1FkYmGP1
+pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZydc93Uk3zyZAsuT3lySNT
+Px8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/yejl0qhqdNkNwnGjkC
+AwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1BE3wMBIGA1Ud
+EwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUA
+A4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT929hkT
+I7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
+jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/
+oCr0Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67
+G7fyUIhzksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9H
+RCwBXbsdtTLSR9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENnAVljANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMTAeFw05ODEyMTAxODEwMjNaFw0xODEyMTAxODQw
+MjNaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUxMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQCgbIGpzzQeJN3+hijM3oMv+V7UQtLodGBmE5gGHKlR
+EmlvMVW5SXIACH7TpWJENySZj9mDSI+ZbZUTu0M7LklOiDfBu1h//uG9+Lth
+zfNHwJmm8fOR6Hh8AMthyUQncWlVSn5JTe2io74CTADKAqjuAQIxZA9SLRN0
+dja1erQtcQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTExDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMTAxODEwMjNagQ8yMDE4MTIx
+MDE4MTAyM1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFGp5fpFpRhgTCgJ3
+pVlbYJglDqL4MB0GA1UdDgQWBBRqeX6RaUYYEwoCd6VZW2CYJQ6i+DAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBACIS2Hod3IEGtgllsofIH160L+nEHvI8wbsEkBFKg05+k7lN
+QseSJqBcNJo4cvj9axY+IO6CizEqkzaFI4iKPANo08kJD038bKTaKHKTDomA
+sH3+gG9lbRgzl4vCa4nuYD3Im+9/KzJic5PLPON74nZ4RbyhkwS7hp86W0N6
+w4pl
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAACfAAAAAIAAAABMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDExFjAUBgNVBAMTDURTVCBSb290Q0EgWDEx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODEyMDEx
+ODE4NTVaFw0wODExMjgxODE4NTVaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDEx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDExITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANLGJrbnpT3BxGjVUG9TxW9JEwm4ryxIjRRqoxdfWvnTLnUv2Chi0ZMv/E3U
+q4flCMeZ55I/db3rJbQVwZsZPdJEjdd0IG03Ao9pk1uKxBmd9LIO/BZsubEF
+koPRhSxglD5FVaDZqwgh5mDoO3TymVBRaNADLbGAvqPYUrBEzUNKcI5YhZXh
+TizWLUFv1oTnyJhEykfbLCSlaSbPa7gnYsP0yXqSI+0TZ4KuRS5F5X5yP4Wd
+lGIQ5jyRoa13AOAV7POEgHJ6jm5gl8ckWRA0g1vhpaRptlc1HHhZxtMvOnNn
+7pTKBBMFYgZwI7P0fO5F2WQLW0mqpEPOJsREEmy43XkCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAojeyP2n714Z5VEkxlTMr89EJFEliYIalsBHiUMIdBlc+
+LegzZL6bqq1fG03UmZWii5rJYnK1aerZWKs17RWiQ9a2vAd5ZWRzfdd5ynvV
+WlHG4VMElo04z6MXrDlxawHDi1M8Y+nuecDkvpIyZHqzH5eUYr3qsiAVlfuX
+8ngvYzZAOONGDx3drJXK50uQe7FLqdTF65raqtWjlBRGjS0f8zrWkzr2Pnn8
+6Oawde3uPclwx12qgUtGJRzHbBXjlU4PqjI3lAoXJJIThFjSY28r9+ZbYgsT
+F7ANUkz+/m9c4pFuHf2kYtdo+o56T9II2pPc8JIRetDccpMMc5NihWjQ9A==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIENm7TzjANBgkqhkiG9w0BAQUFADBGMQswCQYDVQQG
+EwJVUzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMREw
+DwYDVQQLEwhEU1RDQSBFMjAeFw05ODEyMDkxOTE3MjZaFw0xODEyMDkxOTQ3
+MjZaMEYxCzAJBgNVBAYTAlVTMSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVy
+ZSBUcnVzdCBDby4xETAPBgNVBAsTCERTVENBIEUyMIGdMA0GCSqGSIb3DQEB
+AQUAA4GLADCBhwKBgQC/k48Xku8zExjrEH9OFr//Bo8qhbxe+SSmJIi2A7fB
+w18DW9Fvrn5C6mYjuGODVvsoLeE4i7TuqAHhzhy2iCoiRoX7n6dwqUcUP87e
+ZfCocfdPJmyMvMa1795JJ/9IKn3oTQPMx7JSxhcxEzu1TdvIxPbDDyQq2gyd
+55FbgM2UnQIBA6OCASQwggEgMBEGCWCGSAGG+EIBAQQEAwIABzBoBgNVHR8E
+YTBfMF2gW6BZpFcwVTELMAkGA1UEBhMCVVMxJDAiBgNVBAoTG0RpZ2l0YWwg
+U2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgRTIxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMTk5ODEyMDkxOTE3MjZagQ8yMDE4MTIw
+OTE5MTcyNlowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFB6CTShlgDzJQW6s
+NS5ay97u+DlbMB0GA1UdDgQWBBQegk0oZYA8yUFurDUuWsve7vg5WzAMBgNV
+HRMEBTADAQH/MBkGCSqGSIb2fQdBAAQMMAobBFY0LjADAgSQMA0GCSqGSIb3
+DQEBBQUAA4GBAEeNg61i8tuwnkUiBbmi1gMOOHLnnvx75pO2mqWilMg0HZHR
+xdf0CiUPPXiBng+xZ8SQTGPdXqfiup/1902lMXucKS1M/mQ+7LZT/uqb7YLb
+dHVLB3luHtgZg3Pe9T7Qtd7nS2h9Qy4qIOF+oHhEngj1mPnHfxsb1gYgAlih
+w6ID
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID2DCCAsACEQDQHkCLAAB3bQAAAAEAAAAEMA0GCSqGSIb3DQEBBQUAMIGp
+MQswCQYDVQQGEwJ1czENMAsGA1UECBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBM
+YWtlIENpdHkxJDAiBgNVBAoTG0RpZ2l0YWwgU2lnbmF0dXJlIFRydXN0IENv
+LjERMA8GA1UECxMIRFNUQ0EgWDIxFjAUBgNVBAMTDURTVCBSb290Q0EgWDIx
+ITAfBgkqhkiG9w0BCQEWEmNhQGRpZ3NpZ3RydXN0LmNvbTAeFw05ODExMzAy
+MjQ2MTZaFw0wODExMjcyMjQ2MTZaMIGpMQswCQYDVQQGEwJ1czENMAsGA1UE
+CBMEVXRhaDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxJDAiBgNVBAoTG0Rp
+Z2l0YWwgU2lnbmF0dXJlIFRydXN0IENvLjERMA8GA1UECxMIRFNUQ0EgWDIx
+FjAUBgNVBAMTDURTVCBSb290Q0EgWDIxITAfBgkqhkiG9w0BCQEWEmNhQGRp
+Z3NpZ3RydXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ANx18IzAdZaawGIfJvfE4Zrq4FZzW5nNAUSoCLbVp9oaBBg5kkp4o4HC9Xd6
+ULRw/5qrxsfKboNPQpj7Jgva3G3WqZlVUmfpKAOS3OWwBZoPFflrWXJW8vo5
+/Kpo7g8fEIMv/J36F5bdguPmRX3AS4BEH+0s4IT9kVySVGkl5WJp3OXuAFK9
+MwutdQKFp2RQLcUZGTDAJtvJ0/0uma1ZtQtN1EGuhUhDWdy3qOKi3sOP17ih
+YqZoUFLkzzGnlIXan0YyF1bl8utmPRL/Q9uY73fPy4GNNLHGUEom0eQ+QVCv
+bK4iNC7Va26Dunm4dmVI2gkpZGMiuftHdoWMhkTLCdsCAwEAATANBgkqhkiG
+9w0BAQUFAAOCAQEAtTYOXeFhKFoRZcA/gwN5Tb4opgsHAlKFzfiR0BBstWog
+WxyQ2TA8xkieil5k+aFxd+8EJx8H6+Qm93N0yUQYGmbT4EOvkTvRyyzYdFQ6
+HE3K1GjNI3wdEJ5F6fYAbqbNGf9PLCmPV03Ed5K+4EwJ+11EhmYhqLkyolbV
+6YyDfFk/xPEL553snr2cGA4+wjl5KLcDDQjLxufZATdQEOzMYRZA1K8xdHv8
+PzGn0EdzMzkbzE5q10mDEQb+64JYMzJM8FasHpwvVpp7wUocpf1VNs78lk30
+sPDst2yC7S8xmUJMqbINuBVd8d+6ybVK1GSYsyapMMj9puyrliGtf8J4tg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEgzCCA+ygAwIBAgIEOJ725DANBgkqhkiG9w0BAQQFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9HQ0NB
+X0NQUyBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAyMDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMDAy
+MDcxNjE2NDBaFw0yMDAyMDcxNjQ2NDBaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0dDQ0FfQ1BTIGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDIwMDAg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xp
+ZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCTdLS25MVL1qFof2LV7PdRV7NySpj10InJrWPNTTVRaoTU
+rcloeW+46xHbh65cJFET8VQlhK8pK5/jgOLZy93GRUk0iJBeAZfv6lOm3fzB
+3ksqJeTpNfpVBQbliXrqpBFXO/x8PTbNZzVtpKklWb1m9fkn5JVn1j+SgF7y
+NH0rhQIDAQABo4IBnjCCAZowEQYJYIZIAYb4QgEBBAQDAgAHMIHdBgNVHR8E
+gdUwgdIwgc+ggcyggcmkgcYwgcMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUAw
+PgYDVQQLFDd3d3cuZW50cnVzdC5uZXQvR0NDQV9DUFMgaW5jb3JwLiBieSBy
+ZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMjAwMCBFbnRydXN0
+Lm5ldCBMaW1pdGVkMTMwMQYDVQQDEypFbnRydXN0Lm5ldCBDbGllbnQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNVBAMTBENSTDEwKwYDVR0QBCQw
+IoAPMjAwMDAyMDcxNjE2NDBagQ8yMDIwMDIwNzE2NDY0MFowCwYDVR0PBAQD
+AgEGMB8GA1UdIwQYMBaAFISLdP3FjcD/J20gN0V8/i3OutN9MB0GA1UdDgQW
+BBSEi3T9xY3A/ydtIDdFfP4tzrrTfTAMBgNVHRMEBTADAQH/MB0GCSqGSIb2
+fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0BAQQFAAOBgQBObzWA
+O9GK9Q6nIMstZVXQkvTnhLUGJoMShAusO7JE7r3PQNsgDrpuFOow4DtifH+L
+a3xKp9U1PL6oXOpLu5OOgGarDyn9TS2/GpsKkMWr2tGzhtQvJFJcem3G8v7l
+TRowjJDyutdKPkN+1MhQGof4T4HHdguEOnKdzmVml64mXg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIElTCCA/6gAwIBAgIEOJsRPDANBgkqhkiG9w0BAQQFADCBujEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxPzA9BgNVBAsUNnd3dy5lbnRydXN0Lm5ldC9TU0xf
+Q1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
+KGMpIDIwMDAgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVz
+dC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe
+Fw0wMDAyMDQxNzIwMDBaFw0yMDAyMDQxNzUwMDBaMIG6MRQwEgYDVQQKEwtF
+bnRydXN0Lm5ldDE/MD0GA1UECxQ2d3d3LmVudHJ1c3QubmV0L1NTTF9DUFMg
+aW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykg
+MjAwMCBFbnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5l
+dCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0G
+CSqGSIb3DQEBAQUAA4GNADCBiQKBgQDHwV9OcfHO8GCGD9JYf9Mzly0XonUw
+tZZkJi9ow0SrqHXmAGc0V55lxyKbc+bT3QgON1WqJUaBbL3+qPZ1V1eMkGxK
+wz6LS0MKyRFWmponIpnPVZ5h2QLifLZ8OAfc439PmrkDQYC2dWcTC5/oVzbI
+XQA23mYU2m52H083jIITiQIDAQABo4IBpDCCAaAwEQYJYIZIAYb4QgEBBAQD
+AgAHMIHjBgNVHR8EgdswgdgwgdWggdKggc+kgcwwgckxFDASBgNVBAoTC0Vu
+dHJ1c3QubmV0MT8wPQYDVQQLFDZ3d3cuZW50cnVzdC5uZXQvU1NMX0NQUyBp
+bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAy
+MDAwIEVudHJ1c3QubmV0IExpbWl0ZWQxOjA4BgNVBAMTMUVudHJ1c3QubmV0
+IFNlY3VyZSBTZXJ2ZXIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMDAyMDQxNzIwMDBagQ8yMDIwMDIw
+NDE3NTAwMFowCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFMtswGvjuz7L/CKc
+/vuLkpyw8m4iMB0GA1UdDgQWBBTLbMBr47s+y/winP77i5KcsPJuIjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQQFAAOBgQBi24GRzsiad0Iv7L0no1MPUBvqTpLwqa+poLpIYcvv
+yQbvH9X07t9WLebKahlzqlO+krNQAraFJnJj2HVQYnUUt7NQGj/KEQALhUVp
+bbalrlHhStyCP2yMNLJ3a9kC9n8O6mUE8c1UyrrJzOCE98g+EZfTYAkYvAX/
+bIkz8OwVDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEXDCCA0SgAwIBAgIEOGO5ZjANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UE
+ChMLRW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNf
+MjA0OCBpbmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsT
+HChjKSAxOTk5IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1
+c3QubmV0IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEy
+MjQxNzUwNTFaFw0xOTEyMjQxODIwNTFaMIG0MRQwEgYDVQQKEwtFbnRydXN0
+Lm5ldDFAMD4GA1UECxQ3d3d3LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29y
+cC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMcKGMpIDE5OTkg
+RW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgpMIIBIjANBgkqhkiG9w0BAQEF
+AAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQqK0vRvwtKTY7tgHalZ7d4
+QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQesYGpjX24zGtLA/EC
+DNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuXMlBvPci6Zgzj
+/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVTXTzWnLLP
+KQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/HoZd
+enoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH
+4QIDAQABo3QwcjARBglghkgBhvhCAQEEBAMCAAcwHwYDVR0jBBgwFoAUVeSB
+0RGAvtiJuQijMfmhJAkWuXAwHQYDVR0OBBYEFFXkgdERgL7YibkIozH5oSQJ
+FrlwMB0GCSqGSIb2fQdBAAQQMA4bCFY1LjA6NC4wAwIEkDANBgkqhkiG9w0B
+AQUFAAOCAQEAWUesIYSKF8mciVMeuoCFGsY8Tj6xnLZ8xpJdGGQC49MGCBFh
+fGPjK50xA3B20qMooPS7mmNz7W3lKtvtFKkrxjYR0CvrB4ul2p5cGZ1WEvVU
+KcgF7bISKo30Axv/55IQh7A6tcOdBTcSo8f0FbnVpDkWm1M6I5HxqIKiaoho
+wXkCIryqptau37AUX7iH0N18f3v/rxzP5tsHrV7bhZ3QKw0z2wTR5klAEyt2
++z7pnIkPFc4YsIV4IU9rTw76NmfNB/L/CNDi3tm/Kq+4h4YhPATKt5Rof888
+6ZjXOP/swNlQ8C5LWK5Gb9Auw2DaclVyvUxFnmG6v4SBkgPR0ml8xQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE7TCCBFagAwIBAgIEOAOR7jANBgkqhkiG9w0BAQQFADCByTELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MUgwRgYDVQQLFD93d3cuZW50
+cnVzdC5uZXQvQ2xpZW50X0NBX0luZm8vQ1BTIGluY29ycC4gYnkgcmVmLiBs
+aW1pdHMgbGlhYi4xJTAjBgNVBAsTHChjKSAxOTk5IEVudHJ1c3QubmV0IExp
+bWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENsaWVudCBDZXJ0aWZpY2F0
+aW9uIEF1dGhvcml0eTAeFw05OTEwMTIxOTI0MzBaFw0xOTEwMTIxOTU0MzBa
+MIHJMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxSDBGBgNV
+BAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0FfSW5mby9DUFMgaW5jb3Jw
+LiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UECxMcKGMpIDE5OTkgRW50
+cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50cnVzdC5uZXQgQ2xpZW50
+IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GL
+ADCBhwKBgQDIOpleMRffrCdvkHvkGf9FozTC28GoT/Bo6oT9n3V5z8GKUZSv
+x1cDR2SerYIbWtp/N3hHuzeYEpbOxhN979IMMFGpOZ5V+Pux5zDeg7K6PvHV
+iTs7hbqqdCz+PzFur5GVbgbUB01LLFZHGARS2g4Qk79jkJvh34zmAqTmT173
+iwIBA6OCAeAwggHcMBEGCWCGSAGG+EIBAQQEAwIABzCCASIGA1UdHwSCARkw
+ggEVMIHkoIHhoIHepIHbMIHYMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50
+cnVzdC5uZXQxSDBGBgNVBAsUP3d3dy5lbnRydXN0Lm5ldC9DbGllbnRfQ0Ff
+SW5mby9DUFMgaW5jb3JwLiBieSByZWYuIGxpbWl0cyBsaWFiLjElMCMGA1UE
+CxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEGA1UEAxMqRW50
+cnVzdC5uZXQgQ2xpZW50IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYD
+VQQDEwRDUkwxMCygKqAohiZodHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9D
+bGllbnQxLmNybDArBgNVHRAEJDAigA8xOTk5MTAxMjE5MjQzMFqBDzIwMTkx
+MDEyMTkyNDMwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUxPucKXuXzUyW
+/O5bs8qZdIuV6kwwHQYDVR0OBBYEFMT7nCl7l81MlvzuW7PKmXSLlepMMAwG
+A1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
+hvcNAQEEBQADgYEAP66K8ddmAwWePvrqHEa7pFuPeJoSSJn59DXeDDYHAmsQ
+OokUgZwxpnyyQbJq5wcBoUv5nyU7lsqZwz6hURzzwy5E97BnRqqS5TvaHBkU
+ODDV4qIxJS7x7EU47fgGWANzYrAQMY9Av2TgXD7FTx/aEkP/TOYGJqibGapE
+PHayXOw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UE
+BhMCVVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50
+cnVzdC5uZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTEl
+MCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UE
+AxMxRW50cnVzdC5uZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eTAeFw05OTA1MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQsw
+CQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3
+dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1pdHMgbGlh
+Yi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVkMTow
+OAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
+b24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDNKIM0
+VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/I0dNxScZgSYMVHIN
+iC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3wkrYKZImZNHk
+mGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OCAdcwggHT
+MBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHboIHY
+pIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
+BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChs
+aW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBM
+aW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENl
+cnRpZmljYXRpb24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNo
+dHRwOi8vd3d3LmVudHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAi
+gA8xOTk5MDUyNTE2MDk0MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMC
+AQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYE
+FPAXYhNVPbP/CgBr+1CEl/PtYtAaMAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9
+B0EABAwwChsEVjQuMAMCBJAwDQYJKoZIhvcNAQEFBQADgYEAkNwwAvpkdMKn
+CqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN95K+8cPV1ZVqBLssziY2Zcgx
+xufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd2cNgQ4xYDiKWL2KjLB+6
+rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1
+cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4
+MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgx
+LTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0
+eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2R
+FGiYCh7+2gRvE4RiIcPRfM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO
+/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuv
+K9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEt
+MCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gjIBBPM5iQn9Qw
+HQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y
+7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2u
+FHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICkDCCAfmgAwIBAgIBATANBgkqhkiG9w0BAQQFADBaMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEtMCsGA1UEAxMkRXF1
+aWZheCBTZWN1cmUgR2xvYmFsIGVCdXNpbmVzcyBDQS0xMB4XDTk5MDYyMTA0
+MDAwMFoXDTIwMDYyMTA0MDAwMFowWjELMAkGA1UEBhMCVVMxHDAaBgNVBAoT
+E0VxdWlmYXggU2VjdXJlIEluYy4xLTArBgNVBAMTJEVxdWlmYXggU2VjdXJl
+IEdsb2JhbCBlQnVzaW5lc3MgQ0EtMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuucXkAJlsTRVPEnCUdXfp9E3j9HngXNBUmCbnaEXJnitx7HoJpQy
+td4zjTov2/KaelpzmKNc6fuKcxtc58O/gGzNqfTWK8D3+ZmqY6KxRwIP1ORR
+OhI8bIpaVIRw28HFkM9yRcuoWcDNM50/o5brhTMhHD4ePmBudpxnhcXIw2EC
+AwEAAaNmMGQwEQYJYIZIAYb4QgEBBAQDAgAHMA8GA1UdEwEB/wQFMAMBAf8w
+HwYDVR0jBBgwFoAUvqigdHJQa0S3ySPY+6j/s1draGwwHQYDVR0OBBYEFL6o
+oHRyUGtEt8kj2Puo/7NXa2hsMA0GCSqGSIb3DQEBBAUAA4GBADDiAVGqx+pf
+2rnQZQ8w1j7aDRRJbpGTJxQx78T3LUX47Me/okENI7SS+RkAZ70Br83gcfxa
+z2TE4JaY0KNA4gGK7ycH8WUBikQtBmV1UsCGECAhX2xrD2yuCRyv8qIYNMR1
+pHMc8Y3c7635s3a0kr/clRAevsvIO1qEYBlWlKlV
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICgjCCAeugAwIBAgIBBDANBgkqhkiG9w0BAQQFADBTMQswCQYDVQQGEwJV
+UzEcMBoGA1UEChMTRXF1aWZheCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1
+aWZheCBTZWN1cmUgZUJ1c2luZXNzIENBLTEwHhcNOTkwNjIxMDQwMDAwWhcN
+MjAwNjIxMDQwMDAwWjBTMQswCQYDVQQGEwJVUzEcMBoGA1UEChMTRXF1aWZh
+eCBTZWN1cmUgSW5jLjEmMCQGA1UEAxMdRXF1aWZheCBTZWN1cmUgZUJ1c2lu
+ZXNzIENBLTEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM4vGbwXt3fe
+k6lfWg0XTzQaDJj0ItlZ1MRoRvC0NcWFAyDGr0WlIVFFQesWWDYyb+JQYmT5
+/VGcqiTZ9J2DKocKIdMSODRsjQBuWqDZQu4aIZX5UkxVWsUPOE9G+m34LjXW
+HXzr4vCwdYDIqROsvojvOm6rXyo4YgKwEnv+j6YDAgMBAAGjZjBkMBEGCWCG
+SAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFEp4
+MlIR21kWNl7fwRQ2QGpHfEyhMB0GA1UdDgQWBBRKeDJSEdtZFjZe38EUNkBq
+R3xMoTANBgkqhkiG9w0BAQQFAAOBgQB1W6ibAxHm6VZMzfmpTMANmvPMZWnm
+JXbMWbfWVMMdzZmsGd20hdXgPfxiIKeES1hl8eL5lSE/9dR+WB5Hh1Q+WKG1
+tfgq73HnvMP2sUlG4tega+VWeponmHxGYhTnyfxuAxJ5gDgdSIKN/Bf+KpYr
+tWKmpj29f5JZzVoqgrI3eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDIDCCAomgAwIBAgIEN3DPtTANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQG
+EwJVUzEXMBUGA1UEChMORXF1aWZheCBTZWN1cmUxJjAkBgNVBAsTHUVxdWlm
+YXggU2VjdXJlIGVCdXNpbmVzcyBDQS0yMB4XDTk5MDYyMzEyMTQ0NVoXDTE5
+MDYyMzEyMTQ0NVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkVxdWlmYXgg
+U2VjdXJlMSYwJAYDVQQLEx1FcXVpZmF4IFNlY3VyZSBlQnVzaW5lc3MgQ0Et
+MjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Dk5kx5SBhsoNviyoynF
+7Y6yEb3+6+e0dMKP/wXn2Z0GvxLIPw7y1tEkshHe0XMJitSxLJgJDR5QRrKD
+pkWNYmi7hRsgcDKqQM2mll/EcTc/BPO3QSQ5BxoeLmFYoBIL5aXfxavqN3HM
+HMg3OrmXUqesxWoklE6ce8/AatbfIb0CAwEAAaOCAQkwggEFMHAGA1UdHwRp
+MGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORXF1aWZheCBT
+ZWN1cmUxJjAkBgNVBAsTHUVxdWlmYXggU2VjdXJlIGVCdXNpbmVzcyBDQS0y
+MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTkwNjIzMTIxNDQ1WjAL
+BgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUUJ4L6q9euSBIplBqy/3YIHqngnYw
+HQYDVR0OBBYEFFCeC+qvXrkgSKZQasv92CB6p4J2MAwGA1UdEwQFMAMBAf8w
+GgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GB
+AAyGgq3oThr1jokn4jVYPSm0B482UJW/bsGe68SQsoWou7dC4A8HOd/7npCy
+0cE+U58DRLB+S/Rv5Hwf5+Kx5Lia78O9zt4LMjTZ3ijtM2vE1Nc9ElirfQkt
+y3D1E4qUoSek1nDFbZS1yX2doNLGCEnZZpum0/QL3MUmV+GRMOrN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRy
+dXN0IFNvbHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3Qg
+R2xvYmFsIFJvb3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1
+MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYD
+VQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMT
+GkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQCVD6C28FCc6HrHiM3dFw4usJTQGz0O9pTAipTHBsiQl8i4
+ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTSr41tiGeA5u2ylc9yMcqlHHK6XALn
+ZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X404Wqk2kmhXBIgD8SFcd5tB8F
+LztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3rGwnpXtlR22ciYaQqPEh3
+46B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l93PR2VX2bY1QY6fDq
+81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0PlZPvy5TYnh+d
+XIVtx6quTx8itc2VrbqnzPmrC3p/
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB+jCCAWMCAgGjMA0GCSqGSIb3DQEBBAUAMEUxCzAJBgNVBAYTAlVTMRgw
+FgYDVQQKEw9HVEUgQ29ycG9yYXRpb24xHDAaBgNVBAMTE0dURSBDeWJlclRy
+dXN0IFJvb3QwHhcNOTYwMjIzMjMwMTAwWhcNMDYwMjIzMjM1OTAwWjBFMQsw
+CQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMRwwGgYDVQQD
+ExNHVEUgQ3liZXJUcnVzdCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
+iQKBgQC45k+625h8cXyvRLfTD0bZZOWTwUKOx7pJjTUteueLveUFMVnGsS8K
+DPufpz+iCWaEVh43KRuH6X4MypqfpX/1FZSj1aJGgthoTNE3FQZor734sLPw
+KfWVWgkWYXcKIiXUT0Wqx73llt/51KiOQswkwB6RJ0q1bQaAYznEol44AwID
+AQABMA0GCSqGSIb3DQEBBAUAA4GBABKzdcZfHeFhVYAA1IFLezEPI2PnPfMD
++fQ2qLvZ46WXTeorKeDWanOB5sCJo9Px4KWlIjeaY8JIILTbcuPI9tl8vrGv
+U9oUtCG41tWW4/5ODFlitppK+ULdjG+BqXH/9ApybW1EDp3zdHSo1TRJ6V6e
+6bR64eVaH4QwnNOfpSXY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYT
+AlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVz
+dCBHbG9iYWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBC
+MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEH
+CIjaWC9mOSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlC
+GDUUna2YRpIuT8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7
+csiRv8lVK83Qlz6cJmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAj
+Nvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdRe
+JivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQAB
+o1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTAephojYn7qwVkDBF9
+qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1luMrMTjANBgkq
+hkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKInZ57Qzxpe
+R+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfStQWV
+Yrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF
+PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot
+2/Unhw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeX
+xx12E6nV5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvm
+Mw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDdTCCAl2gAwIBAgILAgAAAAAA1ni3lAUwDQYJKoZIhvcNAQEEBQAwVzEL
+MAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNV
+BAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05
+ODA5MDExMjAwMDBaFw0xNDAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkw
+FwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRsw
+GQYDVQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDaDuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR
+4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc
+71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4
+bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgK
+OOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMW
+ea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DP
+AgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIABjAdBgNVHQ4EFgQUYHtmGkUNl8qJ
+UC99BM00qP/8/UswDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOC
+AQEArqqf/LfSyx9fOSkoGJ40yWxPbxrwZKJwSk8ThptgKJ7ogUmYfQq75bCd
+PTbbjwVR/wkxKh/diXeeDy5slQTthsu0AD+EAk2AaioteAuubyuig0SDH81Q
+gkwkr733pbTIWg/050deSY43lv6aiAU62cDbKYfmGZZHpzqmjIs8d/5GY6dT
+2iHRrH5Jokvmw2dZL7OKDrssvamqQnw1wdh/1acxOk5jQzmvCLBhNIzTmKlD
+NPYPhyk7ncJWWJh3w/cbrPad+D6qp1RF8PX51TFl/mtYnHGzHtdS6jIX/EBg
+Hcl5JLL2bP2oZg6C3ZjL2sJETy6ge/L3ayx2EYRGinij4w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMjIzM1oXDTE5MDYyNjAwMjIzM1owgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDMgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDjmFGWHOjVsQaBalfDcnWTq8+epvzzFlLWLU2f
+NUSoLgRNB0mKOCn1dzfnt6td3zZxFJmP3MKS8edgkpfs2Ejcv8ECIMYkpChM
+MFp2bbFc893enhBxoYjHW5tBbcqwuI4V7q0zK89HBFx1cQqYJJgpp0lZpd34
+t0NiYfPT4tBVPwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFa7AliEZwgs3x/b
+e0kz9dNnnfS0ChCzycUs4pJqcXgn8nCDQtM+z6lU9PHYkhaM0QTLS6vJn0Wu
+PIqpsHEzXcjFV9+vqDWzf4mH6eglkrh/hXqu1rweN1gqZ8mRzyqBPu3GOd/A
+PhmcGcwTTYJBtYze4D1gCCAPRX5ron+jjBXu
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICXDCCAcWgAwIBAgIQCgEBAQAAAnwAAAALAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMTAyNCBWMzAeFw0wMTAyMjIyMTAxNDlaFw0yNjAyMjIyMDAx
+NDlaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAxMDI0IFYzMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDV3f5mCc8kPD6ugU5OisRpgFtZO9+5TUzKtS3DJy08rwBCbbwoppbPf9dY
+rIMKo1W1exeQFYRMiu4mmdxY78c4pqqv0I5CyGLXq6yp+0p9v+r+Ek3d/yYt
+bzZUaMjShFbuklNhCbM/OZuoyZu9zp9+1BlqFikYvtc6adwlWzMaUQIDAQAB
+o2MwYTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSME
+GDAWgBTEwBykB5T9zU0B1FTapQxf3q4FWjAdBgNVHQ4EFgQUxMAcpAeU/c1N
+AdRU2qUMX96uBVowDQYJKoZIhvcNAQEFBQADgYEAPy1q4yZDlX2Jl2X7deRy
+HUZXxGFraZ8SmyzVWujAovBDleMf6XbN3Ou8k6BlCsdNT1+nr6JGFLkM88y9
+am63nd4lQtBU/55oc2PcJOsiv6hy8l4A4Q1OOkNumU4/iXgDmMrzVcydro7B
+qkWY+o8aoI2II/EVQQ2lRj6RP4vr93E=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDYTCCAkmgAwIBAgIQCgEBAQAAAnwAAAAKAAAAAjANBgkqhkiG9w0BAQUF
+ADA6MRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgSW5jMR0wGwYDVQQLExRSU0Eg
+U2VjdXJpdHkgMjA0OCBWMzAeFw0wMTAyMjIyMDM5MjNaFw0yNjAyMjIyMDM5
+MjNaMDoxGTAXBgNVBAoTEFJTQSBTZWN1cml0eSBJbmMxHTAbBgNVBAsTFFJT
+QSBTZWN1cml0eSAyMDQ4IFYzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEAt49VcdKA3XtpeafwGFAyPGJn9gqVB93mG/Oe2dJBVGutn3y+Gc37
+RqtBaB4Y6lXIL5F4iSj7Jylg/9+PjDvJSZu1pJTOAeo+tWN7fyb9Gd3AIb2E
+0S1PRsNO3Ng3OTsor8udGuorryGlwSMiuLgbWhOHV4PR8CDn6E8jQrAApX2J
+6elhc5SYcSa8LWrg903w8bYqODGBDSnhAMFRD0xS+ARaqn1y07iHKrtjEAMq
+s6FPDVpeRrc9DvV07Jmf+T0kgYim3WBU6JU2PcYJk5qjEoAAVZkZR73QpXzD
+uvsf9/UP+Ky5tfQ3mBMY3oVbtwyCO4dvlTlYMNpuAWgXIszACwIDAQABo2Mw
+YTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAW
+gBQHw1EwpKrpRa41JPr/JCwz0LGdjDAdBgNVHQ4EFgQUB8NRMKSq6UWuNST6
+/yQsM9CxnYwwDQYJKoZIhvcNAQEFBQADggEBAF8+hnZuuDU8TjYcHnmYv/3V
+EhF5Ug7uMYm83X/50cYVIeiKAVQNOvtUudZj1LGqlk2iQk3UUx+LEN5/Zb5g
+EydxiKRz44Rj0aRV4VCT5hsOedBnvEbIvz8XDZXmxpBp3ue0L96VfdASPz0+
+f00/FGj1EVDVwfSQpQgdMWD/YIwjVAqv/qFuxdF6Kmh4zx6CCiC0H63lhbJq
+aHVOrSU3lIW+vaHU6rcMSzyd6BIA8F+sDeGscGNz9395nzIlQnQFgCi/vcEk
+llgVsRch6YlL2weIZ/QVrXA+L02FO8K32/6YaCOJ4XQP3vTFhGMpG8zLB8kA
+pKnXwiJPZ9d37CAFYd4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+owDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAyIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANo46O0yAClxgwENv4wB3NrGrTmk
+qYov1YtcaF9QxmL1Zr3KkSLsqh1R1z2zUbKDTl3LSbDwTFXlay3HhQswHJJO
+gtTKAu33b77c4OMUuAVT8pr0VotanoWT0bSCVq5Nu6hLVxa8/vhYnvgpjbB7
+zXjJT6yLZwzxnPv8V5tXXE8NAgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBAIRS+yjf/x91AbwBvgRWl2p0QiQxg/lGsQaKic+W
+LDO/jLVfenKhhQbOhvgFjuj5Jcrag4wGrOs2bYWRNAQ29ELw+HkuCkhcq8xR
+T3h2oNmsGb0q0WkEKJHKNhAngFdb0lz1wlurZIFjdFH0l7/NEij3TWZ/p/Ac
+ASZ4smZHcFFk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDXDCCAsWgAwIBAgICA+swDQYJKoZIhvcNAQEEBQAwgbwxCzAJBgNVBAYT
+AkRFMRAwDgYDVQQIEwdIYW1idXJnMRAwDgYDVQQHEwdIYW1idXJnMTowOAYD
+VQQKEzFUQyBUcnVzdENlbnRlciBmb3IgU2VjdXJpdHkgaW4gRGF0YSBOZXR3
+b3JrcyBHbWJIMSIwIAYDVQQLExlUQyBUcnVzdENlbnRlciBDbGFzcyAzIENB
+MSkwJwYJKoZIhvcNAQkBFhpjZXJ0aWZpY2F0ZUB0cnVzdGNlbnRlci5kZTAe
+Fw05ODAzMDkxMTU5NTlaFw0xMTAxMDExMTU5NTlaMIG8MQswCQYDVQQGEwJE
+RTEQMA4GA1UECBMHSGFtYnVyZzEQMA4GA1UEBxMHSGFtYnVyZzE6MDgGA1UE
+ChMxVEMgVHJ1c3RDZW50ZXIgZm9yIFNlY3VyaXR5IGluIERhdGEgTmV0d29y
+a3MgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMyBDQTEp
+MCcGCSqGSIb3DQEJARYaY2VydGlmaWNhdGVAdHJ1c3RjZW50ZXIuZGUwgZ8w
+DQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALa0wTUFLg2N7KBAahwOJ6ZQkmtQ
+GwfeLud2zODa/ISoXoxjaitN2U4CdhHBC/KNecoAtvGwDtf7pBc9r6tpepYn
+v68zoZoqWarEtTcI8hKlMbZD9TKWcSgoq40oht+77uMMfTDWw1Krj10nnGvA
+o+cFa1dJRLNu6mTP0o56UHd3AgMBAAGjazBpMA8GA1UdEwEB/wQFMAMBAf8w
+DgYDVR0PAQH/BAQDAgGGMDMGCWCGSAGG+EIBCAQmFiRodHRwOi8vd3d3LnRy
+dXN0Y2VudGVyLmRlL2d1aWRlbGluZXMwEQYJYIZIAYb4QgEBBAQDAgAHMA0G
+CSqGSIb3DQEBBAUAA4GBABY9xs3Bu4VxhUafPiCPUSiZ7C1FIWMjWwS7TJC4
+iJIETb19AaM/9uzO8d7+feXhPrvGq14L3T2WxMup1Pkm5gZOngylerpuw3yC
+GdHHsbHD2w2Om0B8NwvxXej9H5CIpQ5ON2QhqE6NtJ/x3kit1VYYUimLRzQS
+CdS7kjXvD9s0
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDITCCAoqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCByzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3RlIFBl
+cnNvbmFsIEJhc2ljIENBMSgwJgYJKoZIhvcNAQkBFhlwZXJzb25hbC1iYXNp
+Y0B0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIzNTk1OVow
+gcsxCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQBgNV
+BAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRpbmcxKDAm
+BgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24xITAfBgNV
+BAMTGFRoYXd0ZSBQZXJzb25hbCBCYXNpYyBDQTEoMCYGCSqGSIb3DQEJARYZ
+cGVyc29uYWwtYmFzaWNAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAvLyTU23AUE+CFeZIlDWmWr5vQvoPR+53dXLdjUmbllegeNTK
+P1GzaQuRdhciB5dqxFGTS+CN7zeVoQxN2jSQHReJl+A1OFdKwPQIcOk8RHtQ
+fmGakOMj04gRRif1CwcOu93RfyAKiLlWCy4cgNrx454p7xS9CkT7G1sY0b8j
+kyECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQQFAAOB
+gQAt4plrsD16iddZopQBHyvdEktTwq1/qqcAXJFAVyVKOKqEcLnZgA+le1z7
+c8a914phXAPjLSeoF+CEhULcXpvGt7Jtu3Sv5D/Lp7ew4F2+eIMllNLbgQ95
+B21P9DkVWlIBe94y1k049hJcBlDfBVu9FEuh3ym6O0GN92NWod8isQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDLTCCApagAwIBAgIBADANBgkqhkiG9w0BAQQFADCB0TELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEkMCIGA1UEAxMbVGhhd3RlIFBl
+cnNvbmFsIEZyZWVtYWlsIENBMSswKQYJKoZIhvcNAQkBFhxwZXJzb25hbC1m
+cmVlbWFpbEB0aGF3dGUuY29tMB4XDTk2MDEwMTAwMDAwMFoXDTIwMTIzMTIz
+NTk1OVowgdExCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUx
+EjAQBgNVBAcTCUNhcGUgVG93bjEaMBgGA1UEChMRVGhhd3RlIENvbnN1bHRp
+bmcxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+JDAiBgNVBAMTG1RoYXd0ZSBQZXJzb25hbCBGcmVlbWFpbCBDQTErMCkGCSqG
+SIb3DQEJARYccGVyc29uYWwtZnJlZW1haWxAdGhhd3RlLmNvbTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA1GnX1LCUZFtx6UfYDFG26nKRsIRefS0N
+j3sS34UldSh0OkIsYyeflXtL734Zhx2G6qPduc6WZBrCFG5ErHzmj+hND3Ef
+QDimAKOHePb5lIZererAXnbr2RSjXW56fAylS1V/Bhkpf56aJtVquzgkCGqY
+x7Hao5iR/Xnb5VrEHLkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkq
+hkiG9w0BAQQFAAOBgQDH7JJ+Tvj1lqVnYiqk8E0RYNBvjWBYYawmu1I1XAjP
+MPuoSpaKH2JCI4wXD/S6ZJwXrEcp352YXtJsYHFcoqzceePnbgBHH7UNKOgC
+neSa/RP0ptl8sfjcXyMmCZGAc9AUG95DqYMl8uacLxXK/qarigd1iwzdUYRr
+5PjRzneigQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDKTCCApKgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBzzELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MRowGAYDVQQKExFUaGF3dGUgQ29uc3VsdGluZzEoMCYGA1UECxMfQ2VydGlm
+aWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEjMCEGA1UEAxMaVGhhd3RlIFBl
+cnNvbmFsIFByZW1pdW0gQ0ExKjAoBgkqhkiG9w0BCQEWG3BlcnNvbmFsLXBy
+ZW1pdW1AdGhhd3RlLmNvbTAeFw05NjAxMDEwMDAwMDBaFw0yMDEyMzEyMzU5
+NTlaMIHPMQswCQYDVQQGEwJaQTEVMBMGA1UECBMMV2VzdGVybiBDYXBlMRIw
+EAYDVQQHEwlDYXBlIFRvd24xGjAYBgNVBAoTEVRoYXd0ZSBDb25zdWx0aW5n
+MSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9uMSMw
+IQYDVQQDExpUaGF3dGUgUGVyc29uYWwgUHJlbWl1bSBDQTEqMCgGCSqGSIb3
+DQEJARYbcGVyc29uYWwtcHJlbWl1bUB0aGF3dGUuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDJZtn4B0TPuYwu8KHvE0VsBd/eJxZRNkERbGw7
+7f4QfRKe5ZtCmv5gMcNmt3M6SK5O0DI3lIi1DbbZ8/JE2dWIEt12TfIa/G8j
+Hnrx2JhFTgcQ7xZC0EN1bUre4qrJMf8fAHB8Zs8QJQi6+u4A6UYDZicRFTuq
+W/KY3TZCstqIdQIDAQABoxMwETAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3
+DQEBBAUAA4GBAGk2ifc0KjNyL2071CKyuG+axTZmDhs8obF1Wub9NdP4qPIH
+b4Vnjt4rueIXsDqg8A6iAJrf8xQVbrvIhVqYgPn/vnQdPfP+MCXRNzRn+qVx
+eTBhKXLA4CxM+1bkOqhv5TJZUtt1KFBZDPgLGeSs2a+WjS9Q2wfD6h+rM+D1
+KzGJ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDJzCCApCgAwIBAgIBATANBgkqhkiG9w0BAQQFADCBzjELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEhMB8GA1UEAxMYVGhhd3Rl
+IFByZW1pdW0gU2VydmVyIENBMSgwJgYJKoZIhvcNAQkBFhlwcmVtaXVtLXNl
+cnZlckB0aGF3dGUuY29tMB4XDTk2MDgwMTAwMDAwMFoXDTIwMTIzMTIzNTk1
+OVowgc4xCzAJBgNVBAYTAlpBMRUwEwYDVQQIEwxXZXN0ZXJuIENhcGUxEjAQ
+BgNVBAcTCUNhcGUgVG93bjEdMBsGA1UEChMUVGhhd3RlIENvbnN1bHRpbmcg
+Y2MxKDAmBgNVBAsTH0NlcnRpZmljYXRpb24gU2VydmljZXMgRGl2aXNpb24x
+ITAfBgNVBAMTGFRoYXd0ZSBQcmVtaXVtIFNlcnZlciBDQTEoMCYGCSqGSIb3
+DQEJARYZcHJlbWl1bS1zZXJ2ZXJAdGhhd3RlLmNvbTCBnzANBgkqhkiG9w0B
+AQEFAAOBjQAwgYkCgYEA0jY2aovXwlue2oFBYo847kkEVdbQ7xwblRZH7xhI
+NTpS9CtqBo87L+pW46+GjZ4X9560ZXUCTe/LCaIhUdib0GfQug2SBhRz1JPL
+lyoAnFxODLz6FVL88kRu2hFKbgifLy3j+ao6hnO2RlNYyIkFvYMRuHM/qgeN
+9EJN50CdHDcCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
+AQQFAAOBgQAmSCwWwlj66BZ0DKqqX1Q/8tfJeGBeXm43YyJ3Nn6yF8Q0ufUI
+hfzJATj/Tb7yFkJD57taRvvBxhEf8UqwKEbJw8RCfbz6q1lu1bdRiBHjpIUZ
+a4JMpAwSremkrj/xw0llmozFyD4lt5SZu5IycQfwhl7tUCemDaYj+bvLpgcU
+Qg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAnygAwIBAgIBATANBgkqhkiG9w0BAQQFADCBxDELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2FwZSBUb3du
+MR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UECxMfQ2Vy
+dGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQVGhhd3Rl
+IFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRzQHRoYXd0
+ZS5jb20wHhcNOTYwODAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBxDELMAkG
+A1UEBhMCWkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTESMBAGA1UEBxMJQ2Fw
+ZSBUb3duMR0wGwYDVQQKExRUaGF3dGUgQ29uc3VsdGluZyBjYzEoMCYGA1UE
+CxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjEZMBcGA1UEAxMQ
+VGhhd3RlIFNlcnZlciBDQTEmMCQGCSqGSIb3DQEJARYXc2VydmVyLWNlcnRz
+QHRoYXd0ZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANOkUG7I
+/1Zr5s9dtuoMaHVHoqrC2oQl/Kj0R1HahbUgdJSGHg91yekIYfUGbTBuFRkC
+6VLAYttNmZ7iagxEOM3+vuNkCXDF/rFrKbYvScg71CcEJRCXL+eQbcAoQpnX
+TEPew/UhbVSfXcNY4cDk2VuwuNy0e982OsK1ZiIS1ocNAgMBAAGjEzARMA8G
+A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAB/pMaVz7lcxG7oWD
+TSEwjsrZqG9JGubaUeNgcGyEYRGhGshIPllDfU+VPaGLtwtimHp1it2ITk6e
+QNuozDJ0uW8NxuOzRAvZim+aKZuZGCg70eNAKJpaPNW15yAbi8qkq43pUdni
+TCxZqdq5snUb9kLy78fyGPmJvKP/iiMucEc=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICoTCCAgqgAwIBAgIBADANBgkqhkiG9w0BAQQFADCBizELMAkGA1UEBhMC
+WkExFTATBgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmls
+bGUxDzANBgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmlj
+YXRpb24xHzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwHhcNOTcw
+MTAxMDAwMDAwWhcNMjAxMjMxMjM1OTU5WjCBizELMAkGA1UEBhMCWkExFTAT
+BgNVBAgTDFdlc3Rlcm4gQ2FwZTEUMBIGA1UEBxMLRHVyYmFudmlsbGUxDzAN
+BgNVBAoTBlRoYXd0ZTEdMBsGA1UECxMUVGhhd3RlIENlcnRpZmljYXRpb24x
+HzAdBgNVBAMTFlRoYXd0ZSBUaW1lc3RhbXBpbmcgQ0EwgZ8wDQYJKoZIhvcN
+AQEBBQADgY0AMIGJAoGBANYrWHhhRYZT6jR7UZztsOYuGA7+4F+oJ9O0yeB8
+WU4WDnNUYMF/9p8u6TqFJBU820cEY8OexJQaWt9MevPZQx08EHp5JduQ/vBR
+5zDWQQD9nyjfeb6Uu522FOMjhdepQeBMpHmwKxqL8vg7ij5FrHGSALSQQZj7
+X+36ty6K+Ig3AgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAZ9viwuaHPUCDhjc1fR/OmsMMZiCouqoEiYbC9RAIDb/LogWK
+0E02PvTX72nGXuSwlG9KuefeW4i2e9vjJ+V2w/A1wcu1J5szedyQpgCed/r8
+zSeUQhac0xxo7L9c3eWpexAKMnRUEzGLhQOEkbdYATAUOK8oyvyxUBkZCayJ
+SdM=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEZDCCA0ygAwIBAgIQRL4Mi1AAJLQR0zYwS8AzdzANBgkqhkiG9w0BAQUF
+ADCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0
+IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEw
+HwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xKzApBgNVBAMTIlVU
+Ti1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNhdGlvbnMwHhcNOTkwNzA5MTg0
+ODM5WhcNMTkwNzA5MTg1NzQ5WjCBozELMAkGA1UEBhMCVVMxCzAJBgNVBAgT
+AlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEeMBwGA1UEChMVVGhlIFVT
+RVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8vd3d3LnVzZXJ0cnVz
+dC5jb20xKzApBgNVBAMTIlVUTi1VU0VSRmlyc3QtTmV0d29yayBBcHBsaWNh
+dGlvbnMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz+5Gh5DZV
+hawGNFugmliy+LUPBXeDrjKxdpJo7CNKyXY/45y2N3kDuatpjQclthln5LAb
+GHNhSuh+zdMvZOOmfAz6F4CjDUeJT1FxL+78P/m4FoCHiZMlIJpDgmkkdihZ
+NaEdwH+DBmQWICzTSaSFtMBhf1EI+GgVkYDLpdXuOzr0hAReYFmnjDRy7rh4
+xdE7EkpvfmUnuaRVxblvQ6TFHSyZwFKkeEwVs0CYCGtDxgGwenv1axwiP8vv
+/6jQOkt2FZ7S0cYu49tXGzKiuG/ohqY/cKvlcJKrRB5AUPuco2LkbG6gyN7i
+gEL66S/ozjIEj3yNtxyjNTwV3Z7DrpelAgMBAAGjgZEwgY4wCwYDVR0PBAQD
+AgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFPqGydvguul49Uuo1hXf
+8NPhahQ8ME8GA1UdHwRIMEYwRKBCoECGPmh0dHA6Ly9jcmwudXNlcnRydXN0
+LmNvbS9VVE4tVVNFUkZpcnN0LU5ldHdvcmtBcHBsaWNhdGlvbnMuY3JsMA0G
+CSqGSIb3DQEBBQUAA4IBAQCk8yXM0dSRgyLQzDKrm5ZONJFUICU0YV8qAhXh
+i6r/fWRRzwr/vH3YIWp4yy9Rb/hCHTO967V7lMPDqaAt39EpHx3+jz+7qEUq
+f9FuVSTiuwL7MT++6LzsQCv4AdRWOOTKRIK1YSAhZ2X28AvnNPilwpyjXEAf
+hZOVBt5P1CeptqX8Fs1zMT+4ZSfP1FMa8Kxun08FDAOBp4QpxFq9ZFdyrTvP
+NximmMatBrTcCKME1SmklpoSZ0qMYEWd8SOasACcaLWYUNPvji6SZbFIPiG+
+FTAqDbUMo2s/rn9X9R+WfN9v3YIwLGUbQErNaLly7HF27FSOH4UMAWr6pjis
+H8SE
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NTIyMjM0OFoXDTE5MDYyNTIyMjM0OFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDEgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDYWYJ6ibiWuqYvaG9YLqdUHAZu9OqNSLwxlBfw
+8068srg1knaw0KWlAdcAAxIiGQj4/xEjm84H9b9pGib+TunRf50sQB1ZaG6m
++FiwnRqP0z/x3BkGgagO4DrdyFNFCQbmD3DD+kCmDuJWBQ8YTfwggtFzVXSN
+dnKgHZ0dwN0/cQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAFBoPUn0LBwGlN+V
+YH+Wexf+T3GtZMjdd9LvWVXoP+iOBSoh8gfStadS/pyxtuJbdxdA6nLWI8so
+gTLDAHkY7FkXicnGah5xyf23dKUlRWnFSKsZ4UWKJWsZ7uW7EvV/96aNUcPw
+nXS3qT6gpf+2SQMT2iLM7XGCK5nPOrf1LXLI
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIC5zCCAlACAQEwDQYJKoZIhvcNAQEFBQAwgbsxJDAiBgNVBAcTG1ZhbGlD
+ZXJ0IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIElu
+Yy4xNTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRp
+b24gQXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNv
+bS8xIDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMB4XDTk5MDYy
+NjAwMTk1NFoXDTE5MDYyNjAwMTk1NFowgbsxJDAiBgNVBAcTG1ZhbGlDZXJ0
+IFZhbGlkYXRpb24gTmV0d29yazEXMBUGA1UEChMOVmFsaUNlcnQsIEluYy4x
+NTAzBgNVBAsTLFZhbGlDZXJ0IENsYXNzIDIgUG9saWN5IFZhbGlkYXRpb24g
+QXV0aG9yaXR5MSEwHwYDVQQDExhodHRwOi8vd3d3LnZhbGljZXJ0LmNvbS8x
+IDAeBgkqhkiG9w0BCQEWEWluZm9AdmFsaWNlcnQuY29tMIGfMA0GCSqGSIb3
+DQEBAQUAA4GNADCBiQKBgQDOOnHK5avIWZJV16vYdA757tn2VUdZZUcOBVXc
+65g2PFxTXdMwzzjsvUGJ7SVCCSRrCl6zfN1SLUzm1NZ9WlmpZdRJEy0kTRxQ
+b7XBhVQ7/nHk01xC+YDgkRoKWzk2Z/M/VXwbP7RfZHM047QSv4dk+NoS/zcn
+wbNDu+97bi5p9wIDAQABMA0GCSqGSIb3DQEBBQUAA4GBADt/UG9vUJSZSWI4
+OB9L+KXIPqeCgfYrx+jFzug6EILLGACOTb2oWH+heQC1u+mNr0HZDzTuIYEZ
+oDJJKPTEjlbVUjP9UNV+mWwD5MlM/Mtsq2azSiGM5bUMMj4QssxsodyamEwC
+W/POuZ6lcg5Ktz885hZo+L7tdEy8W9ViH0Pd
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPTCCAaYCEQDNun9W8N/kvFT+IqyzcqpVMA0GCSqGSIb3DQEBAgUAMF8x
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UE
+CxMuQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eTAeFw05NjAxMjkwMDAwMDBaFw0yODA4MDEyMzU5NTlaMF8xCzAJBgNV
+BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE3MDUGA1UECxMuQ2xh
+c3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCB
+nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA5Rm/baNWYS2ZSHH2Z965jeu3
+noaACpEO+jglr0aIguVzqKCbJF0NH8xlbgyw0FaEGIeaBpsQoXPftFg5a27B
+9hXVqKg/qhIGjTGsf7A01480Z4gJzRQR4k5FVmkfeAKA2txHkSm7NsljXMXg
+1y2He6G3MrB7MLoqLzGq7qNn2tsCAwEAATANBgkqhkiG9w0BAQIFAAOBgQBM
+P7iLxmjf7kMzDl3ppssHhE16M/+SG/Q2rdiVIjZoEWx8QszznC7EBz8UsA9P
+/5CSdvnivErpj82ggAr3xSnxgiJduLHdgSOjeyUVRjB5FvjqBUuUfx3CHMjj
+t/QQQDwTw18fU+hI5Ia0e6E1sHslurjTjqs/OJ0ANACY89FxlA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEEzH6qqYPnHTkxD4PTqJkZIwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMSBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCq
+0Lq+Fi24g9TK0g+8djHKlNgdk4xWArzZbxpvUjZudVYKVdPfQ4chEWWKfo+9
+Id5rMj8bhDSVBZ1BNeuS65bdqlk/AVNtmU/t5eIqWpDBucSmFc/IReumXY6c
+PvBkJHalzasab7bYe1FhbqZ/h8jit+U03EGI6glAvnOSPWvndQIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAKlPww3HZ74sy9mozS11534Vnjty637rXC0Jh9Zr
+bWB85a7FkCMMXErQr7Fd88e2CtvgFZMN3QO8x3aKtd1Pw5sTdbgBwObJW2ul
+uIncrKTdcu1OofdPvAbT6shkdHvClUGcZXNY8ZCaPGqxmMnEh7zPRW1F4m4i
+P/68DzFc6PLZ
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDEgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4nN493GwTFtl63SRR
+ZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO8ESlV8dAWB6j
+Rx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjVojYJrKsh
+JlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjbPG7P
+oBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2
+6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHh
+v2Vrn5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQ
+BfGfMY1aqtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/N
+y9Sn2WCVhDr4wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUf
+xJM8/XmPBNQ+T+r3ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFM
+DSZl4kSAHsef493oCtrspSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5
+SCJ5ShkPshw+IHTZasO+8ih4E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXV
+OBRgmaNL3gaWcSzy27YfpO8/7g==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQK2jUo0aexTsoCas4XX8nIDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAx
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQC57V56Ondfzl86UvzNZPdxtW9qlsZZklWUXS9bLsER
+6iaKy6eBPPZaRN56Ey/9WlHZezcmSsAnPwQDalbBgyzhb1upVFAkSsYuekyh
+WzdUJCExH6F4GHansXDaItBq/gdiQMb39pt9DAa4S8co5GYjhFHvRreT2IEz
+y+U2rMboBQIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0xMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTEuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAHCQ3bjkvlMX
+fH8C6dX3i5mTMWCNfuZgayTvYKzSzpHegG0JpNO4OOVEynJeDS3Bd5y9LAN4
+KY2kpXeH9fErJq3MB2w6VFoo4AnzTQoEytRYaQuns/XdAaXn3PAfusFdkI2z
+6k/BEVmXarIrE7HarZehs7GgIFvKMquNzxPwHynD
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEC0b/EoXjaOR6+f/9YtFvgswDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2WoujDWojg4BrzzmH9CETMwZM
+JaLtVRKXxaeAufqDwSCg+i8VDXyhYGt+eSz6Bg86rvYbb7HS/y8oUl+DfUvE
+erf4Zh+AVPy3wo5ZShRXRtGak75BkQO7FYCTXOvnzAhsPz6zSvz/S2wj1VCC
+JkQZjiPDceoZJEcEnnW/yKYAHwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBAIob
+K/o5wXTXXtgZZKJYSi034DNHD6zt96rbHuSLBlxgJ8pFUs4W7z8GZOeUaHxg
+MxURaa+dYo2jA1Rrpr7l7gUYYAS/QoD90KioHgE796Ncr6Pc5iaAIzy4RHT3
+Cq5Ji2F4zCS/iIqnDupzGUH9TQPwiNHleI2lKk/2lw0Xd8rY
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAmwCEQC5L2DMiJ+hekYJuFtwbIqvMA0GCSqGSIb3DQEBBQUAMIHB
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6BgNV
+BAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIEluYy4g
+LSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNpZ24g
+VHJ1c3QgTmV0d29yazAeFw05ODA1MTgwMDAwMDBaFw0yODA4MDEyMzU5NTla
+MIHBMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xPDA6
+BgNVBAsTM0NsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkgLSBHMjE6MDgGA1UECxMxKGMpIDE5OTggVmVyaVNpZ24sIElu
+Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTEfMB0GA1UECxMWVmVyaVNp
+Z24gVHJ1c3QgTmV0d29yazCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA
+p4gBIXQs5xoD8JjhlzwPIQjxnNuX6Zr8wgQGE75fUsjMHiwSViy4AWkszJkf
+rbCWrnkE8hM5wXuYuggs6MKEEyyqaekJ9MepAqRCwiNPStjwDqL7MWzJ5m+Z
+Jwf15vRMeJ5t60aG+rmGyVTyssSv1EYcWskVMP8NbPUtDm3Of3cCAwEAATAN
+BgkqhkiG9w0BAQUFAAOBgQByLvl/0fFx+8Se9sVeUYpAmLho+Jscg9jinb3/
+7aHmZuovCfTK1+qlK5X2JGCGTUQug6XELaDTrnhpb3LabK4I8GOSN+a7xDAX
+rXfMSTWqz9iP0b63GJZHc2pUIjRkLbYWm1lbtFFZOrMLFPQS32eg9K0yZF6x
+RnInjBJ7xUS0rg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcox
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UE
+CxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkg
+VmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMG
+A1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZp
+Y2F0aW9uIEF1dGhvcml0eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcx
+NjIzNTk1OVowgcoxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwg
+SW5jLjEfMB0GA1UECxMWVmVyaVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UE
+CxMxKGMpIDE5OTkgVmVyaVNpZ24sIEluYy4gLSBGb3IgYXV0aG9yaXplZCB1
+c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24gQ2xhc3MgMiBQdWJsaWMgUHJp
+bWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEczMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWUJ92lvuCwTY+zYVY8
+1nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDOJxOeBUebMXoT
+2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUYwZF7C9UT
+AJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9okoqQ
+HgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN
+qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVC
+YQ/ESrg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekh
+ktdmnLfexbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf
+0xwLRtxyID+u7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydE
+p85EXdQbkJgNHkKUsQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377B
+MnMiIYtYgXsVkXq642RIsH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab
+5iXiQkWquJCtvgiPqQtCGJTPcjnhsUPgKM+351psE2tJs//jGHyJizNdrDPX
+p/naOlXJWBD5qu9ats9LS98q
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAwegAwIBAgIQCUYX5h3Y1BygDKBi6HmKpzANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDIgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODAxMDAwMDAwWhcNMDQwNzMxMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAy
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDQymMxYX9ENHwFfQs9apDLeUt3Cj9LxyPlwGItfpx+
+PoiHkdCs6E1Jh6KWkIrdBKUCP4yb6Yn+YqDiWr3I3bR45qVCkwhnAcAgTddc
+9F3as+M3plIaLExlTYqH2aij8UlUuzxcgFFoxvtJ/wtVqxXd+5rBuR10DbKM
+RF2J/J/5gwIDAQABo4IBEDCCAQwwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0yMDEGA1UdHwQqMCgwJqAkoCKGIGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTIuY3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEIGCCsGAQUF
+BwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJpc2lnbi5j
+b20vb2NzcC9zdGF0dXMwRAYDVR0gBD0wOzA5BgtghkgBhvhFAQcBATAqMCgG
+CCsGAQUFBwIBFhxodHRwczovL3d3dy52ZXJpc2lnbi5jb20vUlBBMAkGA1Ud
+EwQCMAAwCwYDVR0PBAQDAgeAMA0GCSqGSIb3DQEBBQUAA4GBAB99CW4kRnUE
+nPMmm+M5bhfvvL2iG9IChIar0ECXLMRDiDcZayKoA3FQnSDcNmAgmnMtc1Vs
+WJsswrQ0LHozQsqR2elDr88e4PXEeqs/cmMeqTfhWzuIsxOGgpBXy1f/9Fa+
+It3jl6jhvCJDwt1N2/aBnpIUnjkPE1TegtjAXjSN
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQL
+Ey5DbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk2MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69q
+RUCPhAwL0TPZ2RHP7gJYHyX3KqhEBarsAx94f56TuZoAqiN91qyFomNFx3In
+zPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/isI19wKTakyYbnsZogy1Olhec9vn2a
+/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0GCSqGSIb3DQEBAgUAA4GBALtM
+EivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Dolbwdj2wsqFHMc9ikwFPw
+TtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNycAA9WjQKZ7aKQRUzk
+uxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDM
+XtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4pO0M8RcPO/mn+SXX
+wc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg013gfqLptQ5GV
+j0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSkU01U
+bSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
+F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo
+1KpYoJ2daZH9
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDMgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8bN3O9+MlrlBIwT/A2
+R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2tKmFZpGcmTNDo
+vFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGukxUccLwg
+TS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBmCC+V
+k7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ
+Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJ
+OxWuimi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my
+/uRan2Te2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5f
+j267Cz3qWhMeDGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoA
+Wii/gt/4uhMdUIaC/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8S
+GhJouPtmmRQURVyu565pF4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbb
+o27q/a2ywtrvAkcTisDxszGtTxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh
+/sVFuq1ruQp6Tk9LhO5L8X3dEQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAwugAwIBAgIQLpaev7ZibOx76XPM42zBhDANBgkqhkiG9w0BAQUF
+ADBfMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xNzA1
+BgNVBAsTLkNsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdHkwHhcNMDAwODA0MDAwMDAwWhcNMDQwODAzMjM1OTU5WjCBpzEX
+MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRy
+dXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBhdCBodHRwczov
+L3d3dy52ZXJpc2lnbi5jb20vUlBBIChjKTAwMS4wLAYDVQQDEyVDbGFzcyAz
+IFB1YmxpYyBQcmltYXJ5IE9DU1AgUmVzcG9uZGVyMIGfMA0GCSqGSIb3DQEB
+AQUAA4GNADCBiQKBgQDx5AgOg7t140jluNum8Lmr6Txix141W9ACVBHYydFW
+uXZLuat65s269gwE1n7WsAplrE454/H3LaMlOe+wi8++2wxdbnD0B81w9zrA
+PjUW7XiMQ8/CJi5H1oZ9nPG+1mcMIiWkymXmH3p4KC8/BdsEIb/hRWb+PLeC
+7Vq4FhW5VQIDAQABo4IBFDCCARAwIAYDVR0RBBkwF6QVMBMxETAPBgNVBAMT
+CE9DU1AgMS0zMDUGA1UdHwQuMCwwKqAooCaGJGh0dHA6Ly9jcmwudmVyaXNp
+Z24uY29tL3BjYTMuMS4xLmNybDATBgNVHSUEDDAKBggrBgEFBQcDCTBCBggr
+BgEFBQcBAQQ2MDQwMgYIKwYBBQUHMAGmJhYkaHR0cDovL29jc3AudmVyaXNp
+Z24uY29tL29jc3Avc3RhdHVzMEQGA1UdIAQ9MDswOQYLYIZIAYb4RQEHAQEw
+KjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL1JQQTAJ
+BgNVHRMEAjAAMAsGA1UdDwQEAwIHgDANBgkqhkiG9w0BAQUFAAOBgQAC9lNj
+wKke8tCLMzCPSJtMsFa0g3FKvtxQ2PW24AvbvXhP6c8JNNopSZ0Bc1qRkYJU
+LBMK03cjzzf8Y96n4/a3tWlFKEnDkdyqRxypiJksBSqNjYr6YuJatwAgXTnE
+KMLL/J6oia5bPY4S6jKy/OsU1wkVGsDNG9W1FU5B1ZbjTg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDAjCCAmsCEDKIjprS9esTR/h/xCA3JfgwDQYJKoZIhvcNAQEFBQAwgcEx
+CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UE
+CxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv
+cml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAt
+IEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMB4XDTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVow
+gcExCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoG
+A1UECxMzQ2xhc3MgNCBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1
+dGhvcml0eSAtIEcyMTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5j
+LiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2ln
+biBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6
+8OTP+cSuhVS5B1f5j8V/aBH4xBewRNzjMHPVKmIquNDMHO0oW369atyzkSTK
+QWI8/AIBvxwWMZQFl3Zuoq29YRdsTjCG8FE3KlDHqGKB3FtKqsGgtG7rL+VX
+xbErQHDbWk2hjh+9Ax/YA9SPTJlxvOKCzFjomDqG04Y48wApHwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBAIWMEsGnuVAVess+rLhDityq3RS6iYF+ATwjcSGI
+L4LcY/oCRaxFWdcqWERbt5+BO5JoPeI3JPV7bI92NZYJqFmduc4jq3TWg/0y
+cyfYaT5DdPauxYma51N86Xv2S/PBZYPejYqcPIiNOVn8qj8ijaHBZlCBckzt
+ImRPT8qAkbYp
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEGjCCAwICEQDsoKeLbnVqAc/EfMwvlF7XMA0GCSqGSIb3DQEBBQUAMIHK
+MQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNV
+BAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5
+IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBD
+BgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFByaW1hcnkgQ2VydGlm
+aWNhdGlvbiBBdXRob3JpdHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3
+MTYyMzU5NTlaMIHKMQswCQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24s
+IEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNV
+BAsTMShjKSAxOTk5IFZlcmlTaWduLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQg
+dXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWduIENsYXNzIDQgUHVibGljIFBy
+aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMzCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAK3LpRFpxlmr8Y+1GQ9Wzsy1HyDkniYl
+S+BzZYlZ3tCD5PUPtbut8XzoIfzk6AzufEUiGXaStBO3IFsJ+mGuqPKljYXC
+KtbeZjbSmwL0qJJgfJxptI8kHtCGUvYynEFYHiK9zUVilQhu0GbdU6LM8BDc
+VHOLBKFGMzNcF0C5nk3T875Vg+ixiY5afJqWIpA7iCXy0lOIAgwLePLmNxdL
+MEYH5IBtptiWLugs+BGzOA1mppvqySNb247i8xOOGlktqgLw7KSHZtzBP/XY
+ufTsgsbSPZUd5cBPhMnZo0QoBmrXRazwa2rvTl/4EYIeOGM0ZlDUPpNz+jDD
+Zq3/ky2X7wMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAj/ola09b5KROJ1Wr
+IhVZPMq1CtRK26vdoV9TxaBXOcLORyu+OshWv8LZJxA6sQU8wHcxuzrTBXtt
+mhwwjIDLk5Mqg6sFUYICABFna/OIYUdfA5PVWw3g8dShMjWFsjrbsIKr0csK
+vE+MW8VLADsfKoKmfjaF3H48ZwC15DtS4KjrXRX5xm3wrR0OhbepmnMUWluP
+QSjA1egtTaRezarZ7c7c2NU8Qh0XwRJdRTjDOPP8hS6DRkiy1yBfkjaP53kP
+mF6Z6PDQpLv1U70qzlmwr25/bLvSHgCwIe34QWKCudiyxLtGUPMxxY8BqHTr
+9Xgn2uf3ZkPznoM+IKrDNWCRzg==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL
+MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu
+MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y
+aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE
+BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD
+VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb
+MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O
+LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs
+iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC
+sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw
+4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr
+l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1
+g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDnzCCAwygAwIBAgIRAP9F1SddJPuzwjkkU1fhT94wDQYJKoZIhvcNAQEF
+BQAwXzELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5
+LCBJbmMuMS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5MB4XDTAwMDgwNDAwMDAwMFoXDTA0MDgwMzIzNTk1OVowgZ4x
+FzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJpU2lnbiBU
+cnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2UgYXQgaHR0cHM6
+Ly93d3cudmVyaXNpZ24uY29tL1JQQSAoYykwMDElMCMGA1UEAxMcU2VjdXJl
+IFNlcnZlciBPQ1NQIFJlc3BvbmRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAuFGZZIUO7rMKaPC/Y3YdU/X8oXiMM+6f9L452psPTUepjyDoS0S9
+zs17kNEw6JDEJXuJKN699pMd/7n/krWpjeSuzOLDB4Nqo3IQASdiIqY1Jjkt
+ns9gDPxHpNfQQninHWzQy08VpykKtJVFxLHnWgnXOZXYHTWewr2zXcEMSx8C
+AwEAAaOCAR0wggEZMCAGA1UdEQQZMBekFTATMREwDwYDVQQDEwhPQ1NQIDEt
+NDA+BgNVHR8ENzA1MDOgMaAvhi1odHRwOi8vY3JsLnZlcmlzaWduLmNvbS9S
+U0FTZWN1cmVTZXJ2ZXItcC5jcmwwEwYDVR0lBAwwCgYIKwYBBQUHAwkwQgYI
+KwYBBQUHAQEENjA0MDIGCCsGAQUFBzABpiYWJGh0dHA6Ly9vY3NwLnZlcmlz
+aWduLmNvbS9vY3NwL3N0YXR1czBEBgNVHSAEPTA7MDkGC2CGSAGG+EUBBwEB
+MCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZlcmlzaWduLmNvbS9SUEEw
+CQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQEFBQADfgAAsxBT
+ZpxJky4xoAJC0lhXfmah/huKYRhQQCweK0Gl1tv/rAgcWgVtAlwqtpZPR9u+
+TtvOzLqGuBjOsRKRX2P380g+zPFNE+RtCZR4AJLLoyCdBgtqoEMHztEZbI8Y
+dZqfFzP9qSa44+LewqjEWop/mNYHBmvMVp6GcM7U7w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDzTCCAzagAwIBAgIQU2GyYK7bcY6nlLMTM/QHCTANBgkqhkiG9w0BAQUF
+ADCBwTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTww
+OgYDVQQLEzNDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24g
+QXV0aG9yaXR5IC0gRzIxOjA4BgNVBAsTMShjKSAxOTk4IFZlcmlTaWduLCBJ
+bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNVBAsTFlZlcmlT
+aWduIFRydXN0IE5ldHdvcmswHhcNMDAwOTI2MDAwMDAwWhcNMTAwOTI1MjM1
+OTU5WjCBpTEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl
+cmlTaWduIFRydXN0IE5ldHdvcmsxOzA5BgNVBAsTMlRlcm1zIG9mIHVzZSBh
+dCBodHRwczovL3d3dy52ZXJpc2lnbi5jb20vcnBhIChjKTAwMSwwKgYDVQQD
+EyNWZXJpU2lnbiBUaW1lIFN0YW1waW5nIEF1dGhvcml0eSBDQTCBnzANBgkq
+hkiG9w0BAQEFAAOBjQAwgYkCgYEA0hmdZ8IAIVlizrQJIkRpivglWtvtDbc2
+fk7gu5Q+kCWHwmFHKdm9VLhjzCx9abQzNvQ3B5rB3UBU/OB4naCTuQk9I1F/
+RMIUdNsKvsvJMDRAmD7Q1yUQgZS9B0+c1lQn3y6ov8uQjI11S7zi6ESHzeZB
+CiVu6PQkAsVSD27smHUCAwEAAaOB3zCB3DAPBgNVHRMECDAGAQH/AgEAMEUG
+A1UdIAQ+MDwwOgYMYIZIAYb4RQEHFwEDMCowKAYIKwYBBQUHAgEWHGh0dHBz
+Oi8vd3d3LnZlcmlzaWduLmNvbS9ycGEwMQYDVR0fBCowKDAmoCSgIoYgaHR0
+cDovL2NybC52ZXJpc2lnbi5jb20vcGNhMy5jcmwwCwYDVR0PBAQDAgEGMEIG
+CCsGAQUFBwEBBDYwNDAyBggrBgEFBQcwAaYmFiRodHRwOi8vb2NzcC52ZXJp
+c2lnbi5jb20vb2NzcC9zdGF0dXMwDQYJKoZIhvcNAQEFBQADgYEAgnBold+2
+DcIBcBlK0lRWHqzyRUyHuPU163hLBanInTsZIS5wNEqi9YngFXVF5yg3ADQn
+Keg3S/LvRJdrF1Eaw1adPBqK9kpGRjeM+sv1ZFo4aC4cw+9wzrhGBha/937n
+tag+RaypJXUie28/sJyU58dzq6wf7iWbwBbtt8pb8BQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDgDCCAmigAwIBAgICAx4wDQYJKoZIhvcNAQEFBQAwYTELMAkGA1UEBhMC
+VVMxDTALBgNVBAoTBFZJU0ExLzAtBgNVBAsTJlZpc2EgSW50ZXJuYXRpb25h
+bCBTZXJ2aWNlIEFzc29jaWF0aW9uMRIwEAYDVQQDEwlHUCBSb290IDIwHhcN
+MDAwODE2MjI1MTAwWhcNMjAwODE1MjM1OTAwWjBhMQswCQYDVQQGEwJVUzEN
+MAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNl
+cnZpY2UgQXNzb2NpYXRpb24xEjAQBgNVBAMTCUdQIFJvb3QgMjCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkBcLWqxEDwq2omYXkZAPy/mzdZ
+DK9vZBv42pWUJGkzEXDK41Z0ohdXZFwgBuHW73G3O/erwWnQSaSxBNf0V2KJ
+XLB1LRckaeNCYOTudNargFbYiCjh+20i/SN8RnNPflRzHqgsVVh1t0zzWkWl
+Ahr62p3DRcMiXvOL8WAp0sdftAw6UYPvMPjU58fy+pmjIlC++QU3o63tmsPm
+7IgbthknGziLgE3sucfFicv8GjLtI/C1AVj59o/ghalMCXI5Etuz9c9OYmTa
+xhkVOmMd6RdVoUwiPDQyRvhlV7or7zaMavrZ2UT0qt2E1w0cslSsMoW0ZA3e
+QbuxNMYBhjJk1Z8CAwEAAaNCMEAwHQYDVR0OBBYEFJ59SzS/ca3CBfYDdYDO
+qU8axCRMMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqG
+SIb3DQEBBQUAA4IBAQAhpXYUVfmtJ3CPPPTVbMjMCqujmAuKBiPFyWHbmQdp
+NSYx/scuhMKZYdQN6X0uEyt8joW2hcdLzzW2LEc9zikv2G+fiRxkk78IvXbQ
+kIqUs38oW26sTTMs7WXcFsziza6kPWKSBpUmv9+55CCmc2rBvveURNZNbyoL
+axhNdBA2aGpawWqn3TYpjLgwi08hPwAuVDAHOrqK5MOeyti12HvOdUVmB/Rt
+Ldh6yumJivIj2C/LbgA2T/vwLwHMD8AiZfSr4k5hLQOCfZEWtTDVFN5ex5D8
+ofyrEK9ca3CnB+8phuiyJccg/ybdd+95RBTEvd07xQObdyPsoOy7Wjm1zK0G
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDojCCAoqgAwIBAgIQE4Y1TR0/BvLB+WUF1ZAcYjANBgkqhkiG9w0BAQUF
+ADBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UECxMmVmlz
+YSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAaBgNVBAMT
+E1Zpc2EgZUNvbW1lcmNlIFJvb3QwHhcNMDIwNjI2MDIxODM2WhcNMjIwNjI0
+MDAxNjEyWjBrMQswCQYDVQQGEwJVUzENMAsGA1UEChMEVklTQTEvMC0GA1UE
+CxMmVmlzYSBJbnRlcm5hdGlvbmFsIFNlcnZpY2UgQXNzb2NpYXRpb24xHDAa
+BgNVBAMTE1Zpc2EgZUNvbW1lcmNlIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQCvV95WHm6h2mCxlCfLF9sHP4CFT8icttD0b0/Pmdjh
+28JIXDqsOTPHH2qLJj0rNfVIsZHBAk4ElpF7sDPwsRROEW+1QK8bRaVK7362
+rPKgH1g/EkZgPI2h4H3PVz4zHvtH8aoVlwdVZqW1LS7YgFmypw23RuwhY/81
+q6UCzyr0TP579ZRdhE2o8mCP2w4lPJ9zcc+U30rq299yOIzzlr3xF7zSujtF
+Wsan9sYXiwGd/BmoKoMWuDpI/k4+oKsGGelT84ATB+0tvz8KPFUgOSwsAGl0
+lUq8ILKpeeUYiZGo3BxN77t+Nwtd/jmliFKMAGzsGHxBvfaLdXe6YJ2E5/4t
+AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBQVOIMPPyw/cDMezUb+B4wg4NfDtzANBgkqhkiG9w0BAQUFAAOC
+AQEAX/FBfXxcCLkr4NWSR/pnXKUTwwMhmytMiUbPWU3J/qVAtmPN3XEolWcR
+zCSs00Rsca4BIGsDoo8Ytyk6feUWYFN4PMCvFYP3j1IzJL1kk5fui/fbGKht
+cbP3LBfQdCVp9/5rPJS+TUtBjE7ic9DjkCJzQ83z7+pzzkWKsKZJ/0x9nXGI
+xHYdkFsd7v3M9+79YKWxehZx0RbQfBI8bGmX265fOZpwLwU8GUYEmSA20GBu
+YQa7FkKMcPcw++DbZqMAAb3mLNqRX6BGi01qnD093QVG/na/oAo85ADmJ7f/
+hC3euiInlhBx6yLt398znM/jra6O1I7mT1GvFpLgXPYHDw==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFajCCBFKgAwIBAgIEPLU9RjANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EtQmFsdGltb3JlIEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA3Mzg1MVoXDTIyMDQxMTA3Mzg1MVowZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBLUJhbHRpbW9yZSBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALx+xDmcjOPW
+HIb/ymKt4H8wRXqOGrO4x/nRNv8i805qX4QQ+2aBw5R5MdKR4XeOGCrDFN5R
+9U+jK7wYFuK13XneIviCfsuBH/0nLI/6l2Qijvj/YaOcGx6Sj8CoCd8JEey3
+fTGaGuqDIQY8n7pc/5TqarjDa1U0Tz0yH92BFODEPM2dMPgwqZfT7syj0B9f
+HBOB1BirlNFjw55/NZKeX0Tq7PQiXLfoPX2k+YmpkbIq2eszh+6l/ePazIjm
+iSZuxyuC0F6dWdsU7JGDBcNeDsYq0ATdcT0gTlgn/FP7eHgZFLL8kFKJOGJg
+B7Sg7KxrUNb9uShr71ItOrL/8QFArDcCAwEAAaOCAh4wggIaMA8GA1UdEwEB
+/wQFMAMBAf8wggG1BgNVHSAEggGsMIIBqDCCAaQGDysGAQQBsT4AAAEJKIOR
+MTCCAY8wggFIBggrBgEFBQcCAjCCAToaggE2UmVsaWFuY2Ugb24gb3IgdXNl
+IG9mIHRoaXMgQ2VydGlmaWNhdGUgY3JlYXRlcyBhbiBhY2tub3dsZWRnbWVu
+dCBhbmQgYWNjZXB0YW5jZSBvZiB0aGUgdGhlbiBhcHBsaWNhYmxlIHN0YW5k
+YXJkIHRlcm1zIGFuZCBjb25kaXRpb25zIG9mIHVzZSwgdGhlIENlcnRpZmlj
+YXRpb24gUHJhY3RpY2UgU3RhdGVtZW50IGFuZCB0aGUgUmVseWluZyBQYXJ0
+eSBBZ3JlZW1lbnQsIHdoaWNoIGNhbiBiZSBmb3VuZCBhdCB0aGUgYmVUUlVT
+VGVkIHdlYiBzaXRlLCBodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20vcHJvZHVj
+dHNfc2VydmljZXMvaW5kZXguaHRtbDBBBggrBgEFBQcCARY1aHR0cDovL3d3
+dy5iZXRydXN0ZWQuY29tL3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWww
+HQYDVR0OBBYEFEU9w6nR3D8kVpgccxiIav+DR+22MB8GA1UdIwQYMBaAFEU9
+w6nR3D8kVpgccxiIav+DR+22MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0B
+AQUFAAOCAQEASZK8o+6svfoNyYt5hhwjdrCAWXf82n+0S9/DZEtqTg6t8n1Z
+dwWtColzsPq8y9yNAIiPpqCy6qxSJ7+hSHyXEHu67RMdmgduyzFiEuhjA6p9
+beP4G3YheBufS0OM00mG9htc9i5gFdPp43t1P9ACg9AYgkHNZTfqjjJ+vWuZ
+XTARyNtIVBw74acT02pIk/c9jH8F6M7ziCpjBLjqflh8AXtb4cV97yHgjQ5d
+UX2xZ/2jvTg2xvI4hocalmhgRvsoFEdV4aeADGvi6t9NfJBIoDa9CReJf8Py
+05yc493EG931t3GzUwWJBtDLSoDByFOQtTwxiBdQn8nEDovYqAJjDQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFLDCCBBSgAwIBAgIEOU99hzANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQG
+EwJXVzESMBAGA1UEChMJYmVUUlVTVGVkMRswGQYDVQQDExJiZVRSVVNUZWQg
+Um9vdCBDQXMxGjAYBgNVBAMTEWJlVFJVU1RlZCBSb290IENBMB4XDTAwMDYy
+MDE0MjEwNFoXDTEwMDYyMDEzMjEwNFowWjELMAkGA1UEBhMCV1cxEjAQBgNV
+BAoTCWJlVFJVU1RlZDEbMBkGA1UEAxMSYmVUUlVTVGVkIFJvb3QgQ0FzMRow
+GAYDVQQDExFiZVRSVVNUZWQgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD
+ggEPADCCAQoCggEBANS0c3oTCjhVAb6JVuGUntS+WutKNHUbYSnE4a0IYCF4
+SP+00PpeQY1hRIfo7clY+vyTmt9P6j41ffgzeubx181vSUs9Ty1uDoM6GHh3
+o8/n9E1z2Jo7Gh2+lVPPIJfCzz4kUmwMjmVZxXH/YgmPqsWPzGCgc0rXOD8V
+cr+il7dw6K/ifhYGTPWqZCZyByWtNfwYsSbX2P8ZDoMbjNx4RWc0PfSvHI3k
+bWvtILNnmrRhyxdviTX/507AMhLn7uzf/5cwdO2NR47rtMNE5qdMf1ZD6Li8
+tr76g5fmu/vEtpO+GRg+jIG5c4gW9JZDnGdzF5DYCW5jrEq2I8QBoa2k5MUC
+AwEAAaOCAfgwggH0MA8GA1UdEwEB/wQFMAMBAf8wggFZBgNVHSAEggFQMIIB
+TDCCAUgGCisGAQQBsT4BAAAwggE4MIIBAQYIKwYBBQUHAgIwgfQagfFSZWxp
+YW5jZSBvbiB0aGlzIGNlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBhc3N1bWVz
+IGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0
+ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGFuZCBjZXJ0aWZpY2F0aW9u
+IHByYWN0aWNlIHN0YXRlbWVudCwgd2hpY2ggY2FuIGJlIGZvdW5kIGF0IGJl
+VFJVU1RlZCdzIHdlYiBzaXRlLCBodHRwczovL3d3dy5iZVRSVVNUZWQuY29t
+L3ZhdWx0L3Rlcm1zMDEGCCsGAQUFBwIBFiVodHRwczovL3d3dy5iZVRSVVNU
+ZWQuY29tL3ZhdWx0L3Rlcm1zMDQGA1UdHwQtMCswKaAnoCWkIzAhMRIwEAYD
+VQQKEwliZVRSVVNUZWQxCzAJBgNVBAYTAldXMB0GA1UdDgQWBBQquZtpLjub
+2M3eKjEENGvKBxirZzAfBgNVHSMEGDAWgBQquZtpLjub2M3eKjEENGvKBxir
+ZzAOBgNVHQ8BAf8EBAMCAf4wDQYJKoZIhvcNAQEFBQADggEBAHlh26Nebhax
+6nZR+csVm8tpvuaBa58oH2U+3RGFktToQb9+M70j5/Egv6S0phkBxoyNNXxl
+pE8JpNbYIxUFE6dDea/bow6be3ga8wSGWsb2jCBHOElQBp1yZzrwmAOtlmdE
+/D8QDYZN5AA7KXvOOzuZhmElQITcE2K3+spZ1gMe1lMBzW1MaFVA4e5rxyoA
+AEiCswoBw2AqDPeCNe5IhpbkdNQ96gFxugR1QKepfzk5mlWXKWWuGVUlBXJH
+0+gY3Ljpr0NzARJ0o+FcXxVdJPP55PS2Z2cS52QiivalQaYctmBjRYoQtLpG
+EK5BV2VsPyMQPyEQWbfkQN0mDCP2qq4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIGUTCCBTmgAwIBAgIEPLVPQDANBgkqhkiG9w0BAQUFADBmMRIwEAYDVQQK
+EwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290IENBczEzMDEG
+A1UEAxMqYmVUUlVTVGVkIFJvb3QgQ0EgLSBFbnRydXN0IEltcGxlbWVudGF0
+aW9uMB4XDTAyMDQxMTA4MjQyN1oXDTIyMDQxMTA4NTQyN1owZjESMBAGA1UE
+ChMJYmVUUlVTVGVkMRswGQYDVQQLExJiZVRSVVNUZWQgUm9vdCBDQXMxMzAx
+BgNVBAMTKmJlVFJVU1RlZCBSb290IENBIC0gRW50cnVzdCBJbXBsZW1lbnRh
+dGlvbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALr0RAOqEmq1
+Q+xVkrYwfTVXDNvzDSduTPdQqJtOK2/b9a0cS12zqcH+e0TrW6MFDR/FNCsw
+ACnxeECypP869AGIF37m1CbTukzqMvtDd5eHI8XbQ6P1KqNRXuE70mVpflUV
+m3rnafdE4Fe1FehmYA8NA/uCjqPoEXtsvsdjDheT389Lrm5zdeDzqrmkwAkb
+hepxKYhBMvnwKg5sCfJ0a2ZsUhMfGLzUPvfYbiCeyv78IZTuEyhL11xeDGbu
+6bsPwTSxfwh28z0mcMmLJR1iJAzqHHVOwBLkuhMdMCktVjMFu5dZfsZJT4nX
+LySotohAtWSSU1Yk5KKghbNekLQSM80CAwEAAaOCAwUwggMBMIIBtwYDVR0g
+BIIBrjCCAaowggGmBg8rBgEEAbE+AAACCSiDkTEwggGRMIIBSQYIKwYBBQUH
+AgIwggE7GoIBN1JlbGlhbmNlIG9uIG9yIHVzZSBvZiB0aGlzIENlcnRpZmlj
+YXRlIGNyZWF0ZXMgYW4gYWNrbm93bGVkZ21lbnQgYW5kIGFjY2VwdGFuY2Ug
+b2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFyZCB0ZXJtcyBhbmQgY29u
+ZGl0aW9ucyBvZiB1c2UsIHRoZSBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0
+YXRlbWVudCBhbmQgdGhlIFJlbHlpbmcgUGFydHkgQWdyZWVtZW50LCB3aGlj
+aCBjYW4gYmUgZm91bmQgYXQgdGhlIGJlVFJVU1RlZCB3ZWIgc2l0ZSwgaHR0
+cHM6Ly93d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRl
+eC5odG1sMEIGCCsGAQUFBwIBFjZodHRwczovL3d3dy5iZXRydXN0ZWQuY29t
+L3Byb2R1Y3RzX3NlcnZpY2VzL2luZGV4Lmh0bWwwEQYJYIZIAYb4QgEBBAQD
+AgAHMIGJBgNVHR8EgYEwfzB9oHugeaR3MHUxEjAQBgNVBAoTCWJlVFJVU1Rl
+ZDEbMBkGA1UECxMSYmVUUlVTVGVkIFJvb3QgQ0FzMTMwMQYDVQQDEypiZVRS
+VVNUZWQgUm9vdCBDQSAtIEVudHJ1c3QgSW1wbGVtZW50YXRpb24xDTALBgNV
+BAMTBENSTDEwKwYDVR0QBCQwIoAPMjAwMjA0MTEwODI0MjdagQ8yMDIyMDQx
+MTA4NTQyN1owCwYDVR0PBAQDAgEGMB8GA1UdIwQYMBaAFH1w5a44iwY/qhwa
+j/nPJDCqhIQWMB0GA1UdDgQWBBR9cOWuOIsGP6ocGo/5zyQwqoSEFjAMBgNV
+HRMEBTADAQH/MB0GCSqGSIb2fQdBAAQQMA4bCFY2LjA6NC4wAwIEkDANBgkq
+hkiG9w0BAQUFAAOCAQEAKrgXzh8QlOu4mre5X+za95IkrNySO8cgjfKZ5V04
+ocI07cUTWVwFtStPYZuR+0H8/NU8TZh2BvWBfevdkObRVlTa4y0MnxEylCIB
+evZsLHRnBMylj44ss0O1lKLQfelifwa+JwGDnjr9iu6YQ0pr17WXOzq/T220
+Y/ozADQuLW2WyXvKmWO6vvT2MKAtmJbpVkQFqUSjYRDrgqFnXbxdJ3Wqiig2
+KjiS2d2kXgClzMx8KSreKJCrt+G2/30lC0DYqjSjLd4H61/OCt3Kfjp9JsFi
+aDrmLzfzgYYhxKlkqu9FNtEaZnz46TfW1mG+oq1I59/mdP7TbX3SJdysYlep
+9w==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFaDCCBFCgAwIBAgIQO1nHe81bV569N1KsdrSqGjANBgkqhkiG9w0BAQUF
+ADBiMRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBS
+b290IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1w
+bGVtZW50YXRpb24wHhcNMDIwNDExMTExODEzWhcNMjIwNDEyMTEwNzI1WjBi
+MRIwEAYDVQQKEwliZVRSVVNUZWQxGzAZBgNVBAsTEmJlVFJVU1RlZCBSb290
+IENBczEvMC0GA1UEAxMmYmVUUlVTVGVkIFJvb3QgQ0EgLSBSU0EgSW1wbGVt
+ZW50YXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkujQw
+CY5X0LkGLG9uJIAiv11DpvpPrILnHGhwhRujbrWqeNluB0s/6d/16uhUoWGK
+Di9pdRi3DOUUjXFumLhV/AyV0Jtu4S2I1DpAa5LxmZZk3tv/ePTulh1HiXzU
+vrmIdyM6CeYEnm2qXtLIvZpOGd+J6lsOfsPktPDgaTuID0GQ+NRxQyTBjyZL
+O1bp/4xsN+lFrYWMU8NghpBKlsmzVLC7F/AcRdnUGxlkVgoZ98zh/4avflhe
+rHqQH8koOUV7orbHnB/ahdQhhlkwk75TMzf270HPM8ercmsl9fNTGwxMLvF1
+S++gh/f+ihXQbNXL+WhTuXAVE8L1LvtDNXUtAgMBAAGjggIYMIICFDAMBgNV
+HRMEBTADAQH/MIIBtQYDVR0gBIIBrDCCAagwggGkBg8rBgEEAbE+AAADCSiD
+kTEwggGPMEEGCCsGAQUFBwIBFjVodHRwOi8vd3d3LmJldHJ1c3RlZC5jb20v
+cHJvZHVjdHNfc2VydmljZXMvaW5kZXguaHRtbDCCAUgGCCsGAQUFBwICMIIB
+OhqCATZSZWxpYW5jZSBvbiBvciB1c2Ugb2YgdGhpcyBDZXJ0aWZpY2F0ZSBj
+cmVhdGVzIGFuIGFja25vd2xlZGdtZW50IGFuZCBhY2NlcHRhbmNlIG9mIHRo
+ZSB0aGVuIGFwcGxpY2FibGUgc3RhbmRhcmQgdGVybXMgYW5kIGNvbmRpdGlv
+bnMgb2YgdXNlLCB0aGUgQ2VydGlmaWNhdGlvbiBQcmFjdGljZSBTdGF0ZW1l
+bnQgYW5kIHRoZSBSZWx5aW5nIFBhcnR5IEFncmVlbWVudCwgd2hpY2ggY2Fu
+IGJlIGZvdW5kIGF0IHRoZSBiZVRSVVNUZWQgd2ViIHNpdGUsIGh0dHA6Ly93
+d3cuYmV0cnVzdGVkLmNvbS9wcm9kdWN0c19zZXJ2aWNlcy9pbmRleC5odG1s
+MAsGA1UdDwQEAwIBBjAfBgNVHSMEGDAWgBSp7BR++dlDzFMrFK3P9/BZiUHN
+GTAdBgNVHQ4EFgQUqewUfvnZQ8xTKxStz/fwWYlBzRkwDQYJKoZIhvcNAQEF
+BQADggEBANuXsHXqDMTBmMpWBcCorSZIry0g6IHHtt9DwSwddUvUQo3neqh0
+3GZCWYez9Wlt2ames30cMcH1VOJZJEnl7r05pmuKmET7m9cqg5c0Lcd9NUwt
+NLg+DcTsiCevnpL9UGGCqGAHFFPMZRPB9kdEadIxyKbdLrML3kqNWz2rDcI1
+UqJWN8wyiyiFQpyRQHpwKzg21eFzGh/l+n5f3NacOzDq28BbJ1zTcwfBwvNM
+m2+fG8oeqqg4MwlYsq78B+g23FW6L09A/nq9BqaBwZMifIYRCgZ3SK41ty8y
+mmFei74pnykkiFY5LKjSq5YDWtRIn7lAhAuYaPsBQ9Yb4gmxlxw=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC
+TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0
+aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz
+MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw
+IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR
+dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp
+li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D
+rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ
+WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug
+F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU
+xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC
+Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv
+dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw
+ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl
+IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh
+c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy
+ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh
+Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI
+KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T
+KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq
+y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p
+dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD
+VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL
+MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk
+fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8
+7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R
+cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y
+mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW
+xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK
+SnQ2+Q==
+-----END CERTIFICATE-----
+
+-----BEGIN CERTIFICATE-----
+MIIETzCCAzegAwIBAgIEO63vKTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIzMTQxODE3WhcNMTEw
+OTIzMTMxODE3WjB1MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWdu
+ZXQgLSBDQSBLbGFzYSAxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC4SRW9Q58g5DY1Hw7h
+gCRKBEdPdGn0MFHsfw7rlu/oQm7IChI/uWd9q5wwo77YojtTDjRnpgZsjqBeynX8T90vFILqsY2K
+5CF1OESalwvVr3sZiQX79lisuFKat92u6hBFikFIVxfHHB67Af+g7u0dEHdDW7lwy81MwFYxBTRy
+9wIDAQABo4IBbTCCAWkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwggEEBgNVHSAE
+gfwwgfkwgfYGDSsGAQQBvj8CAQoBAQAwgeQwgZoGCCsGAQUFBwICMIGNGoGKQ2VydHlmaWthdCB3
+eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtOiAiUG9saXR5a2EgQ2VydHlmaWthY2ppIGRs
+YSBSb290Q0EiLiBDZXJ0eWZpa2F0IHd5c3Rhd2lvbnkgcHJ6ZXogUm9vdENBIHcgaGllcmFyY2hp
+aSBDQyBTaWduZXQuMEUGCCsGAQUFBwIBFjlodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0b3Jp
+dW0vZG9rdW1lbnR5L3BjX3Jvb3RjYS50eHQwHwYDVR0jBBgwFoAUwJvFIw0C4aZOSGsfAOnjmhQb
+sa8wHQYDVR0OBBYEFMODHtVZd1T7TftXR/nEI1zR54njMA0GCSqGSIb3DQEBBQUAA4IBAQBRIHQB
+FIGh8Jpxt87AgSLwIEEk4+oGy769u3NtoaR0R3WNMdmt7fXTi0tyTQ9V4AIszxVjhnUPaKnF1KYy
+f8Tl+YTzk9ZfFkZ3kCdSaILZAOIrmqWNLPmjUQ5/JiMGho0e1YmWUcMci84+pIisTsytFzVP32/W
++sz2H4FQAvOIMmxB7EJX9AdbnXn9EXZ+4nCqi0ft5z96ZqOJJiCB3vSaoYg+wdkcvb6souMJzuc2
+uptXtR1Xf3ihlHaGW+hmnpcwFA6AoNrom6Vgzk6U1ienx0Cw28BhRSKqzKkyXkuK8gRflZUx84uf
+tXncwKJrMiE3lvgOOBITRzcahirLer4c
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIE9zCCA9+gAwIBAgIEPL/xoTANBgkqhkiG9w0BAQUFADB2MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MSAwHgYDVQQDExdDQyBTaWduZXQgLSBQQ0EgS2xhc2EgMjAeFw0wMjA0MTkxMDI5NTNa
+Fw0xNzA0MTgxMjUzMDdaMHUxCzAJBgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4g
+eiBvLm8uMSQwIgYDVQQLExtDZW50cnVtIENlcnR5ZmlrYWNqaSBTaWduZXQxHzAdBgNVBAMTFkND
+IFNpZ25ldCAtIENBIEtsYXNhIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqgLJu
+QqY4yavbSgHg8CyfKTx4BokNSDOVz4eD9vptUr11Kqd06ED1hlH7Sg0goBFAfntNU/QTKwSBaNui
+me7C4sSEdgsKrPoAhGb4Mq8y7Ty7RqZz7mkzNMqzL2L2U4yQ2QjvpH8MH0IBqOWEcpSkpwnrCDIm
+RoTfd+YlZWKi2JceQixUUYIQ45Ox8+x8hHbvvZdgqtcvo8PW27qoHkp/7hMuJ44kDAGrmxffBXl/
+OBRZp0uO1CSLcMcVJzyr2phKhy406MYdWrtNPEluGs0GFDzd0nrIctiWAO4cmct4S72S9Q6e//0G
+O9f3/Ca5Kb2I1xYLj/xE+HgjHX9aD2MhAgMBAAGjggGMMIIBiDAPBgNVHRMBAf8EBTADAQH/MA4G
+A1UdDwEB/wQEAwIBBjCB4wYDVR0gBIHbMIHYMIHVBg0rBgEEAb4/AhQKAQEAMIHDMHUGCCsGAQUF
+BwICMGkaZ0NlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBQQ0EyIC0gQ2VydHlmaWthdHkgVXJ6ZWRvdyBLbGFzeSAyIi4wSgYI
+KwYBBQUHAgEWPmh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9kb2t1bWVudHkva2xh
+c2EyL3BjX3BjYTIudHh0MD8GA1UdHwQ4MDYwNKAyoDCGLmh0dHA6Ly93d3cuc2lnbmV0LnBsL3Jl
+cG96eXRvcml1bS9jcmwvcGNhMi5jcmwwHwYDVR0jBBgwFoAUwGxGyl2CfpYHRonE82AVXO08kMIw
+HQYDVR0OBBYEFLtFBlILy4HNKVSzvHxBTM0HDowlMA0GCSqGSIb3DQEBBQUAA4IBAQBWTsCbqXrX
+hBBev5v5cIuc6gJM8ww7oR0uMQRZoFSqvQUPWBYM2/TLI/f8UM9hSShUVj3zEsSj/vFHagUVmzuV
+Xo5u0WK8iaqATSyEVBhADHrPG6wYcLKJlagge/ILA0m+SieyP2sjYD9MUB9KZIEyBKv0429UuDTw
+6P7pslxMWJBSNyQxaLIs0SRKsqZZWkc7ZYAj2apSkBMX2Is1oHA+PwkF6jQMwCao/+CndXPUzfCF
+6caa9WwW31W26MlXCvSmJgfiTPwGvm4PkPmOnmWZ3CczzhHl4q7ztHFzshJH3sZWDnrWwBFjzz5e
+Pr3WHV1wA7EY6oT4zBx+2gT9XBTB
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUzCCAzugAwIBAgIEPq+qjzANBgkqhkiG9w0BAQUFADBhMQswCQYDVQQGEwJQTDE3MDUGA1UE
+ChMuQ1ppQyBDZW50cmFzdCBTQSB3IGltaWVuaXUgTWluaXN0cmEgR29zcG9kYXJraTEZMBcGA1UE
+AxMQQ1ppQyBDZW50cmFzdCBTQTAeFw0wMzA0MzAxMDUwNTVaFw0wODA0MjgxMDUwNTVaMGgxCzAJ
+BgNVBAYTAlBMMR8wHQYDVQQKExZUUCBJbnRlcm5ldCBTcC4geiBvLm8uMR8wHQYDVQQDExZDQyBT
+aWduZXQgLSBDQSBLbGFzYSAzMRcwFQYDVQQFEw5OdW1lciB3cGlzdTogNDCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBALVdeOM62cPH2NERFxbS5FIp/HSv3fgesdVsTUFxZbGtE+/E0RMl
+KZQJHH9emx7vRYubsi4EOLCjYsCOTFvgGRIpZzx7R7T5c0Di5XFkRU4gjBl7aHJoKb5SLzGlWdoX
+GsekVtl6keEACrizV2EafqjI8cnBWY7OxQ1ooLQp5AeFjXg+5PT0lO6TUZAubqjFbhVbxSWjqvdj
+93RGfyYE76MnNn4c2xWySD07n7uno06TC0IJe6+3WSX1h+76VsIFouWBXOoM7cxxiLjoqdBVu24+
+P8e81SukE7qEvOwDPmk9ZJFtt1nBNg8a1kaixcljrA/43XwOPz6qnJ+cIj/xywECAwEAAaOCAQow
+ggEGMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMDMGA1UdIAEB/wQpMCcwJQYEVR0g
+ADAdMBsGCCsGAQUFBwIBFg93d3cuY2VudHJhc3QucGwwgY4GA1UdIwSBhjCBg4AU2a7r85Cp1iJN
+W0Ca1LR6VG3996ShZaRjMGExCzAJBgNVBAYTAlBMMTcwNQYDVQQKEy5DWmlDIENlbnRyYXN0IFNB
+IHcgaW1pZW5pdSBNaW5pc3RyYSBHb3Nwb2RhcmtpMRkwFwYDVQQDExBDWmlDIENlbnRyYXN0IFNB
+ggQ9/0sQMB0GA1UdDgQWBBR7Y8wZkHq0zrY7nn1tFSdQ0PlJuTANBgkqhkiG9w0BAQUFAAOCAQEA
+ldt/svO5c1MU08FKgrOXCGEbEPbQxhpM0xcd6Iv3dCo6qugEgjEs9Qm5CwUNKMnFsvR27cJWUvZb
+MVcvwlwCwclOdwF6u/QRS8bC2HYErhYo9bp9yuxxzuow2A94c5fPqfVrjXy+vDouchAm6+A5Wjzv
+J8wxVFDCs+9iGACmyUWr/JGXCYiQIbQkwlkRKHHlan9ymKf1NvIej/3EpeT8fKr6ywxGuhAfqofW
+pg3WJY/RCB4lTzD8vZGNwfMFGkWhJkypad3i9w3lGmDVpsHaWtCgGfd0H7tUtWPkP+t7EjIRCD9J
+HYnTR+wbbewc5vOI+UobR15ynGfFIaSIiMTVtQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEejCCA2KgAwIBAgIEP4vk6TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2Vu
+dHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBD
+QSBLbGFzYSAyMB4XDTAzMTAxNDExNTgyMloXDTE3MDQxODEyNTMwN1owdzELMAkG
+A1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6IG8uby4xJDAiBgNV
+BAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEhMB8GA1UEAxMYQ0MgU2ln
+bmV0IC0gT0NTUCBLbGFzYSAyMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCo
+VCsaBStblXQYVNthe3dvaCrfvKpPXngh4almm988iIlEv9CVTaAdCfaJNihvA+Vs
+Qw8++ix1VqteMQE474/MV/YaXigP0Zr0QB+g+/7PWVlv+5U9Gzp9+Xx4DJay8AoI
+iB7Iy5Qf9iZiHm5BiPRIuUXT4ZRbZRYPh0/76vgRsQIDAQABo4IBkjCCAY4wDgYD
+VR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMJMEEGA1UdHwQ6MDgwNqA0
+oDKGMGh0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9jcmwva2xhc2Ey
+LmNybDCB2AYDVR0gBIHQMIHNMIHKBg4rBgEEAb4/AoFICgwBADCBtzBsBggrBgEF
+BQcCAjBgGl5DZXJ0eWZpa2F0IHd5ZGFueSB6Z29kbmllIHogZG9rdW1lbnRlbSAi
+UG9saXR5a2EgQ2VydHlmaWthY2ppIC0gQ2VydHlmaWthdHkgcmVzcG9uZGVyb3cg
+T0NTUCIuMEcGCCsGAQUFBwIBFjtodHRwOi8vd3d3LnNpZ25ldC5wbC9yZXBvenl0
+b3JpdW0vZG9rdW1lbnR5L3BjX29jc3BfMV8wLnBkZjAfBgNVHSMEGDAWgBS7RQZS
+C8uBzSlUs7x8QUzNBw6MJTAdBgNVHQ4EFgQUKEVrOY7cEHvsVgvoyZdytlbtgwEw
+CQYDVR0TBAIwADANBgkqhkiG9w0BAQUFAAOCAQEAQrRg5MV6dxr0HU2IsLInxhvt
+iUVmSFkIUsBCjzLoewOXA16d2oDyHhI/eE+VgAsp+2ANjZu4xRteHIHoYMsN218M
+eD2MLRsYS0U9xxAFK9gDj/KscPbrrdoqLvtPSMhUb4adJS9HLhvUe6BicvBf3A71
+iCNe431axGNDWKnpuj2KUpj4CFHYsWCXky847YtTXDjri9NIwJJauazsrSjK+oXp
+ngRS506mdQ7vWrtApkh8zhhWp7duCkjcCo1O8JxqYr2qEW1fXmgOISe010v2mmuv
+hHxPyVwoAU4KkOw0nbXZn53yak0is5+XmAjh0wWue44AssHrjC9nUh3mkLt6eQ==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEezCCA2OgAwIBAgIEP4vnLzANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJQ
+TDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEfMB0GA1UEAxMWQ0Mg
+U2lnbmV0IC0gQ0EgS2xhc2EgMzEXMBUGA1UEBRMOTnVtZXIgd3Bpc3U6IDQwHhcN
+MDMxMDE0MTIwODAwWhcNMDgwNDI4MTA1MDU1WjB3MQswCQYDVQQGEwJQTDEfMB0G
+A1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBD
+ZXJ0eWZpa2FjamkgU2lnbmV0MSEwHwYDVQQDExhDQyBTaWduZXQgLSBPQ1NQIEts
+YXNhIDMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM/9GwvARNuCVN+PqZmO
+4FqH8vTqhenUyqRkmAVT4YhLu0a9AXeLAYVDu+NTkYzsAUMAfu55rIKHNLlm6WbF
+KvLiKKz4p4pbUr+ToPcwl/TDotidloUdBAxDg0SL+PmQqACZDe3seJho2IYf2vDL
+/G4TLMbKmNB0mlWFuN0f4fJNAgMBAAGjggGgMIIBnDAOBgNVHQ8BAf8EBAMCB4Aw
+EwYDVR0lBAwwCgYIKwYBBQUHAwkwTwYDVR0fBEgwRjBEoEKgQIY+aHR0cDovL3d3
+dy5zaWduZXQucGwva3dhbGlmaWtvd2FuZS9yZXBvenl0b3JpdW0vY3JsL2tsYXNh
+My5jcmwwgdgGA1UdIASB0DCBzTCBygYOKwYBBAG+PwKCLAoCAQAwgbcwbAYIKwYB
+BQUHAgIwYBpeQ2VydHlmaWthdCB3eWRhbnkgemdvZG5pZSB6IGRva3VtZW50ZW0g
+IlBvbGl0eWthIENlcnR5ZmlrYWNqaSAtIENlcnR5ZmlrYXR5IHJlc3BvbmRlcm93
+IE9DU1AiLjBHBggrBgEFBQcCARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5
+dG9yaXVtL2Rva3VtZW50eS9wY19vY3NwXzFfMC5wZGYwHwYDVR0jBBgwFoAUe2PM
+GZB6tM62O559bRUnUND5SbkwHQYDVR0OBBYEFG4jnCMvBALRQXtmDn9TyXQ/EKP+
+MAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADggEBACXrKG5Def5lpRwmZom3UEDq
+bl7y4U3qomG4B+ok2FVZGgPZti+ZgvrenPj7PtbYCUBPsCSTNrznKinoT3gD9lQQ
+xkEHwdc6VD1GlFp+qI64u0+wS9Epatrdf7aBnizrOIB4LJd4E2TWQ6trspetjMIU
+upyWls1BmYUxB91R7QkTiAUSNZ87s3auhZuG4f0V0JLVCcg2rn7AN1rfMkgxCbHk
+GxiQbYWFljl6aatxR3odnnzVUe1I8uoY2JXpmmUcOG4dNGuQYziyKG3mtXCQWvug
+5qi9Mf3KUh1oSTKx6HfLjjNl1+wMB5Mdb8LF0XyZLdJM9yIZh7SBRsYm9QiXevY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPL7eEDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwNDE4MTQ1NDA4WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM7BrBlbN5ma
+M5eg0BOTqoZ+9NBDvU8Lm5rTdrMswFTCathzpVVLK/JD4K3+4oCZ9SRAspEXE4gvwb08ASY6w5s+
+HpRkeJw8YzMFR5kDZD5adgnCAy4vDfIXYZgppXPaTQ8wnfUZ7BZ7Zfa7QBemUIcJIzJBB0UqgtxW
+Ceol9IekpBRVmuuSA6QG0Jkm+pGDJ05yj2eQG8jTcBENM7sVA8rGRMyFA4skSZ+D0OG6FS2xC1i9
+JyN0ag1yII/LPx8HK5J4W9MaPRNjAEeaa2qI9EpchwrOxnyVbQfSedCG1VRJfAsE/9tT9CMUPZ3x
+W20QjQcSZJqVcmGW9gVsXKQOVLsCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQEBMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUwGxGyl2CfpYHRonE
+82AVXO08kMIwDQYJKoZIhvcNAQEFBQADggEBABp1TAUsa+BeVWg4cjowc8yTJ5XN3GvN96GObMkx
+UGY7U9kVrLI71xBgoNVyzXTiMNDBvjh7vdPWjpl5SDiRpnnKiOFXA43HvNWzUaOkTu1mxjJsZsan
+ot1Xt6j0ZDC+03FjLHdYMyM9kSWp6afb4980EPYZCcSzgM5TOGfJmNii5Tq468VFKrX+52Aou1G2
+2Ohu+EEOlOrG7ylKv1hHUJJCjwN0ZVEIn1nDbrU9FeGCz8J9ihVUvnENEBbBkU37PWqWuHitKQDV
+tcwTwJJdR8cmKq3NmkwAm9fPacidQLpaw0WkuGrS+fEDhu1Nhy9xELP6NA9GRTCNxm/dXlcwnmY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFGjCCBAKgAwIBAgIEPV0tNDANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDIwODE2MTY0OTU2WhcNMjYw
+OTIxMTU0MjE5WjB2MQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MSAwHgYDVQQDExdDQyBTaWdu
+ZXQgLSBQQ0EgS2xhc2EgMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALN3LanJtdue
+Ne6geWUTFENa+lEuzqELcoqhYB+a/tJcPEkc6TX/bYPzalRRjqs+quMP6KZTU0DixOrV+K7iWaqA
+iQ913HX5IBLmKDCrTVW/ZvSDpiBKbxlHfSNuJxAuVT6HdbzK7yAW38ssX+yS2tZYHZ5FhZcfqzPE
+OpO94mAKcBUhk6T/ki0evXX/ZvvktwmF3hKattzwtM4JMLurAEl8SInyEYULw5JdlfcBez2Tg6Db
+w34hA1A+ckTwhxzecrB8TUe2BnQKOs9vr2cCACpFFcOmPkM0Drtjctr1QHm1tYSqRFRf9VcV5tfC
+3P8QqoK4ONjtLPHc9x5NE1uK/FMCAwEAAaOCAbMwggGvMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMIIBBAYDVR0gBIH8MIH5MIH2Bg0rBgEEAb4/AgEKAQECMIHkMIGaBggrBgEFBQcC
+AjCBjRqBikNlcnR5ZmlrYXQgd3lzdGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0
+eWthIENlcnR5ZmlrYWNqaSBkbGEgUm9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6
+IFJvb3RDQSB3IGhpZXJhcmNoaWkgQ0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5z
+aWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY19yb290Y2EudHh0MEQGA1UdHwQ9MDsw
+OaA3oDWGM2h0dHA6Ly93d3cuc2lnbmV0LnBsL3JlcG96eXRvcml1bS9yb290Y2Evcm9vdGNhLmNy
+bDAfBgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAdBgNVHQ4EFgQUXvthcPHlH5BgGhlM
+ErJNXWlhlgAwDQYJKoZIhvcNAQEFBQADggEBACIce95Mvn710KCAISA0CuHD4aznTU6pLoCDShW4
+7OR+GTpJUm1coTcUqlBHV9mra4VFrBcBuOkHZoBLq/jmE0QJWnpSEULDcH9J3mF0nqO9SM+mWyJG
+dsJF/XU/7smummgjMNQXwzQTtWORF+6v5KUbWX85anO2wR+M6YTBWC55zWpWi4RG3vkHFs5Ze2oF
+JTlpuxw9ZgxTnWlwI9QR2MvEhYIUMKMOWxw1nt0kKj+5TCNQQGh/VJJ1dsiroGh/io1DOcePEhKz
+1Ag52y6Wf0nJJB9yk0sFakqZH18F7eQecQImgZyyeRtsG95leNugB3BXWCW+KxwiBrtQTXv4dTE=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEzzCCA7egAwIBAgIEO6ocGTANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MRswGQYDVQQDExJDQyBTaWduZXQgLSBSb290Q0EwHhcNMDEwOTIwMTY0MjE5WhcNMjYw
+OTIxMTU0MjE5WjBxMQswCQYDVQQGEwJQTDEfMB0GA1UEChMWVFAgSW50ZXJuZXQgU3AuIHogby5v
+LjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2FjamkgU2lnbmV0MRswGQYDVQQDExJDQyBTaWdu
+ZXQgLSBSb290Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrr2vydnNpELfGW3Ks
+ARiDhJvwDtUe4AbWev+OfMc3+vA29nX8ZmIwno3gmItjo5DbUCCRiCMq5c9epcGu+kg4a3BJChVX
+REl8gVh0ST15rr3RKrSc4VgsvQzl0ZUraeQLl8JoRT5PLsUj3qwF78jUCQVckiiLVcnGfZtFCm+D
+CJXliQBDMB9XFAUEiO/DtEBs0B7wJGx7lgJeJpQUcGiaOPjcJDYOk7rNAYmmD2gWeSlepufO8luU
+YG/YDxTC4mqhRqfa4MnVO5dqy+ICj2UvUpHbZDB0KfGRibgBYeQP1kuqgIzJN4UqknVAJb0aMBSP
+l+9k2fAUdchx1njlbdcbAgMBAAGjggFtMIIBaTAPBgNVHRMBAf8EBTADAQH/MIIBBAYDVR0gBIH8
+MIH5MIH2Bg0rBgEEAb4/AgEKAQEAMIHkMIGaBggrBgEFBQcCAjCBjRqBikNlcnR5ZmlrYXQgd3lz
+dGF3aW9ueSB6Z29kbmllIHogZG9rdW1lbnRlbTogIlBvbGl0eWthIENlcnR5ZmlrYWNqaSBkbGEg
+Um9vdENBIi4gQ2VydHlmaWthdCB3eXN0YXdpb255IHByemV6IFJvb3RDQSB3IGhpZXJhcmNoaWkg
+Q0MgU2lnbmV0LjBFBggrBgEFBQcCARY5aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVt
+L2Rva3VtZW50eS9wY19yb290Y2EudHh0MB0GA1UdDgQWBBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAf
+BgNVHSMEGDAWgBTAm8UjDQLhpk5Iax8A6eOaFBuxrzAOBgNVHQ8BAf8EBAMCAQYwDQYJKoZIhvcN
+AQEFBQADggEBAGnY5QmYqnnO9OqFOWZxxb25UHRnaRF6IV9aaGit5BZufZj2Tq3v8L3SgE34GOoI
+cdRMMG5JEpEU4mN/Ef3oY6Eo+7HfqaPHI4KFmbDSPiK5s+wmf+bQSm0Yq5/h4ZOdcAESlLQeLSt1
+CQk2JoKQJ6pyAf6xJBgWEIlm4RXE4J3324PUiOp83kW6MDvaa1xY976WyInr4rwoLgxVl11LZeKW
+ha0RJJxJgw/NyWpKG7LWCm1fglF8JH51vZNndGYq1iKtfnrIOvLZq6bzaCiZm1EurD8HE6P7pmAB
+KK6o3C2OXlNfNIgwkDN/cDqk5TYsTkrpfriJPdxXBH8hQOkW89g=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIID/TCCA2agAwIBAgIEP4/gkTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJQTDEfMB0GA1UE
+ChMWVFAgSW50ZXJuZXQgU3AuIHogby5vLjEkMCIGA1UECxMbQ2VudHJ1bSBDZXJ0eWZpa2Fjamkg
+U2lnbmV0MR8wHQYDVQQDExZDQyBTaWduZXQgLSBDQSBLbGFzYSAxMB4XDTAzMTAxNzEyMjkwMloX
+DTExMDkyMzExMTgxN1owdjELMAkGA1UEBhMCUEwxHzAdBgNVBAoTFlRQIEludGVybmV0IFNwLiB6
+IG8uby4xJDAiBgNVBAsTG0NlbnRydW0gQ2VydHlmaWthY2ppIFNpZ25ldDEgMB4GA1UEAxMXQ0Mg
+U2lnbmV0IC0gVFNBIEtsYXNhIDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAOJYrISEtSsd
+uHajROh5/n7NGrkpYTT9NEaPe9+ucuQ37KxIbfJwXJjgUc1dw4wCkcQ12FJarD1X6mSQ4cfN/60v
+LfKI5ZD4nhJTMKlAj1pX9ScQ/MuyvKStCbn5WTkjPhjRAM0tdwXSnzuTEunfw0Oup559y3Iqxg1c
+ExflB6cfAgMBAAGjggGXMIIBkzBBBgNVHR8EOjA4MDagNKAyhjBodHRwOi8vd3d3LnNpZ25ldC5w
+bC9yZXBvenl0b3JpdW0vY3JsL2tsYXNhMS5jcmwwDgYDVR0PAQH/BAQDAgeAMBYGA1UdJQEB/wQM
+MAoGCCsGAQUFBwMIMIHaBgNVHSAEgdIwgc8wgcwGDSsGAQQBvj8CZAoRAgEwgbowbwYIKwYBBQUH
+AgIwYxphQ2VydHlmaWthdCB3eXN0YXdpb255IHpnb2RuaWUgeiBkb2t1bWVudGVtICJQb2xpdHlr
+YSBDZXJ0eWZpa2FjamkgQ0MgU2lnbmV0IC0gWm5ha293YW5pZSBjemFzZW0iLjBHBggrBgEFBQcC
+ARY7aHR0cDovL3d3dy5zaWduZXQucGwvcmVwb3p5dG9yaXVtL2Rva3VtZW50eS9wY190c2ExXzJf
+MS5wZGYwHwYDVR0jBBgwFoAUw4Me1Vl3VPtN+1dH+cQjXNHnieMwHQYDVR0OBBYEFJdDwEqtcavO
+Yd9u9tej53vWXwNBMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEFBQADgYEAnpiQkqLCJQYXUrqMHUEz
++z3rOqS0XzSFnVVLhkVssvXc8S3FkJIiQTUrkScjI4CToCzujj3EyfNxH6yiLlMbskF8I31JxIeB
+vueqV+s+o76CZm3ycu9hb0I4lswuxoT+q5ZzPR8Irrb51rZXlolR+7KtwMg4sFDJZ8RNgOf7tbA=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEFTCCA36gAwIBAgIBADANBgkqhkiG9w0BAQQFADCBvjELMAkGA1UEBhMCVVMx
+EDAOBgNVBAgTB0luZGlhbmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UE
+ChMfU29mdHdhcmUgaW4gdGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9z
+dG1hc3RlcjEgMB4GA1UEAxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkq
+hkiG9w0BCQEWFmhvc3RtYXN0ZXJAc3BpLWluYy5vcmcwHhcNMDMwMTE1MTYyOTE3
+WhcNMDcwMTE0MTYyOTE3WjCBvjELMAkGA1UEBhMCVVMxEDAOBgNVBAgTB0luZGlh
+bmExFTATBgNVBAcTDEluZGlhbmFwb2xpczEoMCYGA1UEChMfU29mdHdhcmUgaW4g
+dGhlIFB1YmxpYyBJbnRlcmVzdDETMBEGA1UECxMKaG9zdG1hc3RlcjEgMB4GA1UE
+AxMXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxJTAjBgkqhkiG9w0BCQEWFmhvc3Rt
+YXN0ZXJAc3BpLWluYy5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPB6
+rdoiLR3RodtM22LMcfwfqb5OrJNl7fwmvskgF7yP6sdD2bOfDIXhg9852jhY8/kL
+VOFe1ELAL2OyN4RAxk0rliZQVgeTgqvgkOVIBbNwgnjN6mqtuWzFiPL+NXQExq40
+I3whM+4lEiwSHaV+MYxWanMdhc+kImT50LKfkxcdAgMBAAGjggEfMIIBGzAdBgNV
+HQ4EFgQUB63oQR1/vda/G4F6P4xLiN4E0vowgesGA1UdIwSB4zCB4IAUB63oQR1/
+vda/G4F6P4xLiN4E0vqhgcSkgcEwgb4xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdJ
+bmRpYW5hMRUwEwYDVQQHEwxJbmRpYW5hcG9saXMxKDAmBgNVBAoTH1NvZnR3YXJl
+IGluIHRoZSBQdWJsaWMgSW50ZXJlc3QxEzARBgNVBAsTCmhvc3RtYXN0ZXIxIDAe
+BgNVBAMTF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MSUwIwYJKoZIhvcNAQkBFhZo
+b3N0bWFzdGVyQHNwaS1pbmMub3JnggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcN
+AQEEBQADgYEAm/Abn8c2y1nO3fgpAIslxvi9iNBZDhQtJ0VQZY6wgSfANyDOR4DW
+iexO/AlorB49KnkFS7TjCAoLOZhcg5FaNiKnlstMI5krQmau1Qnb/vGSNsE/UGms
+1ts+QYPUs0KmGEAFUri2XzLy+aQo9Kw74VBvqnxvaaMeY5yMcKNOieY=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIEUDCCA7mgAwIBAgIJAN4ppNGwj6yIMA0GCSqGSIb3DQEBBAUAMIHMMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5j
+aXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5jLjEpMCcGA1UECxMgTGluZGVu
+IExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAnBgNVBAMTIExpbmRlbiBMYWIg
+Q2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZIhvcNAQkBFhBjYUBsaW5kZW5s
+YWIuY29tMB4XDTA1MDQyMTAyNDAzMVoXDTI1MDQxNjAyNDAzMVowgcwxCzAJBgNV
+BAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNp
+c2NvMRkwFwYDVQQKExBMaW5kZW4gTGFiLCBJbmMuMSkwJwYDVQQLEyBMaW5kZW4g
+TGFiIENlcnRpZmljYXRlIEF1dGhvcml0eTEpMCcGA1UEAxMgTGluZGVuIExhYiBD
+ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgkqhkiG9w0BCQEWEGNhQGxpbmRlbmxh
+Yi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKXh1MThucdTbMg9bYBO
+rAm8yWns32YojB0PRfbq8rUjepEhTm3/13s0u399Uc202v4ejcGhkIDWJZd2NZMF
+oKrhmRfxGHSKPCuFaXC3jh0lRECj7k8FoPkcmaPjSyodrDFDUUuv+C06oYJoI+rk
+8REyal9NwgHvqCzOrZtiTXAdAgMBAAGjggE2MIIBMjAdBgNVHQ4EFgQUO1zK2e1f
+1wO1fHAjq6DTJobKDrcwggEBBgNVHSMEgfkwgfaAFDtcytntX9cDtXxwI6ug0yaG
+yg63oYHSpIHPMIHMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
+MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZMBcGA1UEChMQTGluZGVuIExhYiwgSW5j
+LjEpMCcGA1UECxMgTGluZGVuIExhYiBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxKTAn
+BgNVBAMTIExpbmRlbiBMYWIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYJKoZI
+hvcNAQkBFhBjYUBsaW5kZW5sYWIuY29tggkA3imk0bCPrIgwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQQFAAOBgQA/ZkgfvwHYqk1UIAKZS3kMCxz0HvYuEQtviwnu
+xA39CIJ65Zozs28Eg1aV9/Y+Of7TnWhW+U3J3/wD/GghaAGiKK6vMn9gJBIdBX/9
+e6ef37VGyiOEFFjnUIbuk0RWty0orN76q/lI/xjCi15XSA/VSq2j4vmnwfZcPTDu
+glmQ1A==
+-----END CERTIFICATE-----
diff --git a/indra/newview/app_settings/anim.ini b/indra/newview/app_settings/anim.ini
new file mode 100644
index 0000000000..63c84e544d
--- /dev/null
+++ b/indra/newview/app_settings/anim.ini
@@ -0,0 +1,87 @@
+Translations 1.0
+
+[hip]
+ relativepos = firstkey
+ relativerot = firstkey
+ outname = mPelvis
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[abdomen]
+ outname = mTorso
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[chest]
+ outname = mChest
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[neckDummy]
+ ignore = true
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[neck]
+ outname = mNeck
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[head]
+ outname = mHead
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[figureHair]
+ ignore = true
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lCollar]
+ outname = mCollarLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lShldr]
+ outname = mShoulderLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lForeArm]
+ outname = mElbowLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lHand]
+ outname = mWristLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rCollar]
+ outname = mCollarRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rShldr]
+ outname = mShoulderRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rForeArm]
+ outname = mElbowRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rHand]
+ outname = mWristRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lThigh]
+ outname = mHipLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lShin]
+ outname = mKneeLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[lFoot]
+ outname = mAnkleLeft
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rThigh]
+ outname = mHipRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rShin]
+ outname = mKneeRight
+ frame = 0 1 0, 0 0 1, 1 0 0
+
+[rFoot]
+ outname = mAnkleRight
+ frame = 0 1 0, 0 0 1, 1 0 0 \ No newline at end of file
diff --git a/indra/newview/app_settings/grass.xml b/indra/newview/app_settings/grass.xml
new file mode 100644
index 0000000000..7cc29fa771
--- /dev/null
+++ b/indra/newview/app_settings/grass.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<grass_defs>
+ <grass
+ name="Grass 0"
+ species_id="0"
+ texture_name="grass_texture_2.tga"
+ blade_size_x="1.35"
+ blade_size_y="1.35"
+ />
+ <grass
+ name="Grass 1"
+ species_id="1"
+ texture_name="grass_texture_1.tga"
+ blade_size_x="1.0"
+ blade_size_y="0.66"
+ />
+ <grass
+ name="Grass 2"
+ species_id="2"
+ texture_name="grass_texture_2.tga"
+ blade_size_x="1.8"
+ blade_size_y="1.8"
+ />
+ <grass
+ name="Grass 3"
+ species_id="3"
+ texture_name="grass_texture_3.tga"
+ blade_size_x="1.0"
+ blade_size_y="1.0"
+ />
+ <grass
+ name="Grass 4"
+ species_id="4"
+ texture_name="grass_texture_4.tga"
+ blade_size_x="2.25"
+ blade_size_y="2.25"
+ />
+ <grass
+ name="undergrowth_1"
+ species_id="5"
+ texture_id="8f458549-173b-23ff-d4ff-bfaa5ea2371b"
+ blade_size_x="2.0"
+ blade_size_y="2.0"
+ />
+
+
+</grass_defs>
diff --git a/indra/newview/app_settings/keys.ini b/indra/newview/app_settings/keys.ini
new file mode 100644
index 0000000000..f6232abb47
--- /dev/null
+++ b/indra/newview/app_settings/keys.ini
@@ -0,0 +1,310 @@
+# keys.ini
+#
+# keyboard binding initialization
+#
+# comments must have # in the first column
+# blank lines OK
+#
+# Format:
+# mode key mask function
+#
+# mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR, or CONVERSATION
+# key must be upper case, or SPACE, HOME, END, PGUP, PGDN, LEFT, RIGHT, UP, DOWN,
+# or one of ,.;'[]
+# mask must be NONE, SHIFT, ALT, ALT_SHIFT.
+# Control is reserved for user commands.
+# function must be a function named in llkeyboard.cpp
+
+FIRST_PERSON A NONE slide_left
+FIRST_PERSON D NONE slide_right
+FIRST_PERSON W NONE push_forward
+FIRST_PERSON S NONE push_backward
+FIRST_PERSON E NONE jump
+FIRST_PERSON C NONE push_down
+FIRST_PERSON F NONE toggle_fly
+
+FIRST_PERSON LEFT NONE slide_left
+FIRST_PERSON RIGHT NONE slide_right
+FIRST_PERSON UP NONE push_forward
+FIRST_PERSON DOWN NONE push_backward
+FIRST_PERSON PGUP NONE jump
+FIRST_PERSON PGDN NONE push_down
+FIRST_PERSON HOME NONE toggle_fly
+
+FIRST_PERSON PAD_LEFT NONE slide_left
+FIRST_PERSON PAD_RIGHT NONE slide_right
+FIRST_PERSON PAD_UP NONE push_forward
+FIRST_PERSON PAD_DOWN NONE push_backward
+FIRST_PERSON PAD_PGUP NONE jump
+FIRST_PERSON PAD_PGDN NONE push_down
+FIRST_PERSON PAD_HOME NONE toggle_fly
+FIRST_PERSON PAD_CENTER NONE stop_moving
+FIRST_PERSON PAD_ENTER NONE start_chat
+
+FIRST_PERSON A SHIFT slide_left
+FIRST_PERSON D SHIFT slide_right
+FIRST_PERSON W SHIFT push_forward
+FIRST_PERSON S SHIFT push_backward
+FIRST_PERSON E SHIFT jump
+FIRST_PERSON C SHIFT push_down
+FIRST_PERSON F SHIFT toggle_fly
+
+FIRST_PERSON SPACE NONE stop_moving
+FIRST_PERSON ENTER NONE start_chat
+
+FIRST_PERSON LEFT SHIFT slide_left
+FIRST_PERSON RIGHT SHIFT slide_right
+FIRST_PERSON UP SHIFT push_forward
+FIRST_PERSON DOWN SHIFT push_backward
+FIRST_PERSON PGUP SHIFT jump
+FIRST_PERSON PGDN SHIFT push_down
+
+FIRST_PERSON PAD_LEFT SHIFT slide_left
+FIRST_PERSON PAD_RIGHT SHIFT slide_right
+FIRST_PERSON PAD_UP SHIFT push_forward
+FIRST_PERSON PAD_DOWN SHIFT push_backward
+FIRST_PERSON PAD_PGUP SHIFT jump
+FIRST_PERSON PAD_PGDN SHIFT push_down
+FIRST_PERSON PAD_HOME SHIFT toggle_fly
+FIRST_PERSON PAD_ENTER SHIFT start_chat
+
+THIRD_PERSON A NONE turn_left
+THIRD_PERSON D NONE turn_right
+THIRD_PERSON A SHIFT slide_left
+THIRD_PERSON D SHIFT slide_right
+THIRD_PERSON W NONE push_forward
+THIRD_PERSON S NONE push_backward
+THIRD_PERSON W SHIFT push_forward
+THIRD_PERSON S SHIFT push_backward
+THIRD_PERSON E NONE jump
+THIRD_PERSON C NONE push_down
+THIRD_PERSON E SHIFT jump
+THIRD_PERSON C SHIFT push_down
+
+THIRD_PERSON F NONE toggle_fly
+THIRD_PERSON F SHIFT toggle_fly
+
+THIRD_PERSON SPACE NONE stop_moving
+THIRD_PERSON ENTER NONE start_chat
+
+THIRD_PERSON LEFT NONE turn_left
+THIRD_PERSON LEFT SHIFT slide_left
+THIRD_PERSON RIGHT NONE turn_right
+THIRD_PERSON RIGHT SHIFT slide_right
+THIRD_PERSON UP NONE push_forward
+THIRD_PERSON DOWN NONE push_backward
+THIRD_PERSON UP SHIFT push_forward
+THIRD_PERSON DOWN SHIFT push_backward
+THIRD_PERSON PGUP NONE jump
+THIRD_PERSON PGDN NONE push_down
+THIRD_PERSON PGUP SHIFT jump
+THIRD_PERSON PGDN SHIFT push_down
+THIRD_PERSON HOME SHIFT toggle_fly
+THIRD_PERSON HOME NONE toggle_fly
+
+THIRD_PERSON PAD_LEFT NONE turn_left
+THIRD_PERSON PAD_LEFT SHIFT slide_left
+THIRD_PERSON PAD_RIGHT NONE turn_right
+THIRD_PERSON PAD_RIGHT SHIFT slide_right
+THIRD_PERSON PAD_UP NONE push_forward
+THIRD_PERSON PAD_DOWN NONE push_backward
+THIRD_PERSON PAD_UP SHIFT push_forward
+THIRD_PERSON PAD_DOWN SHIFT push_backward
+THIRD_PERSON PAD_PGUP NONE jump
+THIRD_PERSON PAD_PGDN NONE push_down
+THIRD_PERSON PAD_PGUP SHIFT jump
+THIRD_PERSON PAD_PGDN SHIFT push_down
+THIRD_PERSON PAD_HOME NONE toggle_fly
+THIRD_PERSON PAD_HOME SHIFT toggle_fly
+THIRD_PERSON PAD_CENTER NONE stop_moving
+THIRD_PERSON PAD_CENTER SHIFT stop_moving
+THIRD_PERSON PAD_ENTER NONE start_chat
+THIRD_PERSON PAD_ENTER SHIFT start_chat
+
+# Camera controls in third person on Alt
+THIRD_PERSON LEFT ALT spin_around_cw
+THIRD_PERSON RIGHT ALT spin_around_ccw
+THIRD_PERSON UP ALT move_forward
+THIRD_PERSON DOWN ALT move_backward
+THIRD_PERSON PGUP ALT spin_over
+THIRD_PERSON PGDN ALT spin_under
+
+THIRD_PERSON PAD_LEFT ALT spin_around_cw
+THIRD_PERSON PAD_RIGHT ALT spin_around_ccw
+THIRD_PERSON PAD_UP ALT move_forward
+THIRD_PERSON PAD_DOWN ALT move_backward
+THIRD_PERSON PAD_PGUP ALT spin_over
+THIRD_PERSON PAD_PGDN ALT spin_under
+THIRD_PERSON PAD_ENTER ALT start_chat
+
+# mimic alt zoom behavior with keyboard only
+THIRD_PERSON A CTL_ALT spin_around_cw
+THIRD_PERSON D CTL_ALT spin_around_ccw
+THIRD_PERSON W CTL_ALT spin_over
+THIRD_PERSON S CTL_ALT spin_under
+THIRD_PERSON E CTL_ALT spin_over
+THIRD_PERSON C CTL_ALT spin_under
+
+THIRD_PERSON LEFT CTL_ALT spin_around_cw
+THIRD_PERSON RIGHT CTL_ALT spin_around_ccw
+THIRD_PERSON UP CTL_ALT spin_over
+THIRD_PERSON DOWN CTL_ALT spin_under
+THIRD_PERSON PGUP CTL_ALT spin_over
+THIRD_PERSON PGDN CTL_ALT spin_under
+
+THIRD_PERSON PAD_LEFT CTL_ALT spin_around_cw
+THIRD_PERSON PAD_RIGHT CTL_ALT spin_around_ccw
+THIRD_PERSON PAD_UP CTL_ALT spin_over
+THIRD_PERSON PAD_DOWN CTL_ALT spin_under
+THIRD_PERSON PAD_PGUP CTL_ALT spin_over
+THIRD_PERSON PAD_PGDN CTL_ALT spin_under
+THIRD_PERSON PAD_ENTER CTL_ALT start_chat
+
+# Therefore pan on Alt-Shift
+THIRD_PERSON A CTL_ALT_SHIFT pan_left
+THIRD_PERSON D CTL_ALT_SHIFT pan_right
+THIRD_PERSON W CTL_ALT_SHIFT pan_up
+THIRD_PERSON S CTL_ALT_SHIFT pan_down
+
+THIRD_PERSON LEFT CTL_ALT_SHIFT pan_left
+THIRD_PERSON RIGHT CTL_ALT_SHIFT pan_right
+THIRD_PERSON UP CTL_ALT_SHIFT pan_up
+THIRD_PERSON DOWN CTL_ALT_SHIFT pan_down
+
+THIRD_PERSON PAD_LEFT CTL_ALT_SHIFT pan_left
+THIRD_PERSON PAD_RIGHT CTL_ALT_SHIFT pan_right
+THIRD_PERSON PAD_UP CTL_ALT_SHIFT pan_up
+THIRD_PERSON PAD_DOWN CTL_ALT_SHIFT pan_down
+THIRD_PERSON PAD_ENTER CTL_ALT_SHIFT start_chat
+
+# Basic editing camera control
+EDIT A NONE spin_around_cw
+EDIT D NONE spin_around_ccw
+EDIT W NONE move_forward
+EDIT S NONE move_backward
+EDIT E NONE spin_over
+EDIT C NONE spin_under
+EDIT ENTER NONE start_chat
+EDIT PAD_ENTER NONE start_chat
+
+EDIT LEFT NONE spin_around_cw
+EDIT RIGHT NONE spin_around_ccw
+EDIT UP NONE move_forward
+EDIT DOWN NONE move_backward
+EDIT PGUP NONE spin_over
+EDIT PGDN NONE spin_under
+
+EDIT A SHIFT pan_left
+EDIT D SHIFT pan_right
+EDIT W SHIFT pan_up
+EDIT S SHIFT pan_down
+
+EDIT LEFT SHIFT pan_left
+EDIT RIGHT SHIFT pan_right
+EDIT UP SHIFT pan_up
+EDIT DOWN SHIFT pan_down
+
+# Walking works with ALT held down.
+EDIT A ALT slide_left
+EDIT D ALT slide_right
+EDIT W ALT push_forward
+EDIT S ALT push_backward
+EDIT E ALT jump
+EDIT C ALT push_down
+
+EDIT LEFT ALT slide_left
+EDIT RIGHT ALT slide_right
+EDIT UP ALT push_forward
+EDIT DOWN ALT push_backward
+EDIT PGUP ALT jump
+EDIT PGDN ALT push_down
+EDIT HOME ALT toggle_fly
+
+EDIT PAD_LEFT ALT slide_left
+EDIT PAD_RIGHT ALT slide_right
+EDIT PAD_UP ALT push_forward
+EDIT PAD_DOWN ALT push_backward
+EDIT PAD_PGUP ALT jump
+EDIT PAD_PGDN ALT push_down
+EDIT PAD_ENTER ALT start_chat
+
+SITTING LEFT ALT spin_around_cw
+SITTING RIGHT ALT spin_around_ccw
+SITTING UP ALT move_forward
+SITTING DOWN ALT move_backward
+SITTING PGUP ALT spin_over
+SITTING PGDN ALT spin_under
+
+SITTING A NONE spin_around_cw_sitting
+SITTING D NONE spin_around_ccw_sitting
+SITTING W NONE move_forward_sitting
+SITTING S NONE move_backward_sitting
+SITTING E NONE spin_over_sitting
+SITTING C NONE spin_under_sitting
+
+SITTING LEFT NONE spin_around_cw_sitting
+SITTING RIGHT NONE spin_around_ccw_sitting
+SITTING UP NONE move_forward_sitting
+SITTING DOWN NONE move_backward_sitting
+SITTING PGUP NONE spin_over_sitting
+SITTING PGDN NONE spin_under_sitting
+
+SITTING PAD_LEFT NONE spin_around_cw_sitting
+SITTING PAD_RIGHT NONE spin_around_ccw_sitting
+SITTING PAD_UP NONE move_forward_sitting
+SITTING PAD_DOWN NONE move_backward_sitting
+SITTING PAD_PGUP NONE spin_over_sitting
+SITTING PAD_PGDN NONE spin_under_sitting
+SITTING PAD_CENTER NONE stop_moving
+SITTING PAD_ENTER NONE start_chat
+
+# these are for passing controls when sitting on vehicles
+SITTING A SHIFT slide_left
+SITTING D SHIFT slide_right
+SITTING LEFT SHIFT slide_left
+SITTING RIGHT SHIFT slide_right
+
+SITTING PAD_LEFT SHIFT slide_left
+SITTING PAD_RIGHT SHIFT slide_right
+SITTING PAD_ENTER SHIFT start_chat
+
+# pan on Alt-Shift
+SITTING A CTL_ALT_SHIFT pan_left
+SITTING D CTL_ALT_SHIFT pan_right
+SITTING W CTL_ALT_SHIFT pan_up
+SITTING S CTL_ALT_SHIFT pan_down
+
+SITTING LEFT CTL_ALT_SHIFT pan_left
+SITTING RIGHT CTL_ALT_SHIFT pan_right
+SITTING UP CTL_ALT_SHIFT pan_up
+SITTING DOWN CTL_ALT_SHIFT pan_down
+
+SITTING PAD_LEFT CTL_ALT_SHIFT pan_left
+SITTING PAD_RIGHT CTL_ALT_SHIFT pan_right
+SITTING PAD_UP CTL_ALT_SHIFT pan_up
+SITTING PAD_DOWN CTL_ALT_SHIFT pan_down
+SITTING PAD_ENTER CTL_ALT_SHIFT start_chat
+
+SITTING ENTER NONE start_chat
+
+# Avatar editing camera controls
+EDIT_AVATAR A NONE edit_avatar_spin_cw
+EDIT_AVATAR D NONE edit_avatar_spin_ccw
+EDIT_AVATAR W NONE edit_avatar_move_forward
+EDIT_AVATAR S NONE edit_avatar_move_backward
+EDIT_AVATAR E NONE edit_avatar_spin_over
+EDIT_AVATAR C NONE edit_avatar_spin_under
+EDIT_AVATAR LEFT NONE edit_avatar_spin_cw
+EDIT_AVATAR RIGHT NONE edit_avatar_spin_ccw
+EDIT_AVATAR UP NONE edit_avatar_move_forward
+EDIT_AVATAR DOWN NONE edit_avatar_move_backward
+EDIT_AVATAR PGUP NONE edit_avatar_spin_over
+EDIT_AVATAR PGDN NONE edit_avatar_spin_under
+EDIT_AVATAR ENTER NONE start_chat
+EDIT_AVATAR PAD_LEFT NONE edit_avatar_spin_cw
+EDIT_AVATAR PAD_RIGHT NONE edit_avatar_spin_ccw
+EDIT_AVATAR PAD_UP NONE edit_avatar_move_forward
+EDIT_AVATAR PAD_DOWN NONE edit_avatar_move_backward
+EDIT_AVATAR PAD_PGUP NONE edit_avatar_spin_over
+EDIT_AVATAR PAD_PGDN NONE edit_avatar_spin_under
+EDIT_AVATAR PAD_ENTER NONE start_chat
diff --git a/indra/newview/app_settings/keywords.ini b/indra/newview/app_settings/keywords.ini
new file mode 100644
index 0000000000..0545c38959
--- /dev/null
+++ b/indra/newview/app_settings/keywords.ini
@@ -0,0 +1,513 @@
+llkeywords version 2
+
+# sections
+[word .5, .1, .3]
+default Name of default state that all scripts must have
+state Keyword to indicate state block or state transition
+
+# data types
+[word .1, .3, .1]
+integer Integer type
+float Floating-point type
+string String type
+key Key type. Use NULL_KEY to test for empty keys.
+vector Vector type of 3 floats. Used to represent 3D motion, Euler angles, and color.:Access components by .x, .y. or .z
+rotation Rotation type of 4 floats. Used to represent rotation.:Access components by .x, .y., .z, or .w
+list List of various data types
+
+# events
+[word 0, .3, .5]
+state_entry state_entry():Triggered on any state transition and startup
+state_exit state_exit():Triggered on any state transition
+touch_start touch_start(integer num_detected):Triggered by the start of agent clicking on task
+touch touch(integer num_detected):Triggered while agent is clicking on task
+touch_end touch_end(integer num_detected):Triggered when agent stops clicking on task
+collision_start collision_start(integer num_detected):Triggered when task starts colliding with another task
+collision collision(integer num_detected):Triggered while task is colliding with another task
+collision_end collision_end(integer num_detected):Triggered when task stops colliding with another task
+land_collision_start land_collision_start(vector pos):Triggered when task starts colliding with land
+land_collision land_collision(vector pos):Triggered when task is colliding with land
+land_collision_end land_collision_end(vector pos):Triggered when task stops colliding with land
+timer timer():Result of the llSetTimerEvent library function call.
+listen listen(integer channel, string name, key id, string message):Result of the llListen library function call
+sensor sensor(integer num_detected):Result of the llSensor library function call
+no_sensor no_sensor():Result of the llSensor library function call
+control control(key id, integer level, integer edge):Result of llTakeControls library function call
+at_target at_target(integer tnum, vector targetpos, vector ourpos):Result of llTarget library function call
+not_at_target not_at_target():Result of llTarget library function call
+at_rot_target at_rot_target(integer tnum, rotation targetrot, rotation ourrot):Result of LLRotTarget library function call
+not_at_rot_target not_at_rot_target():Result of LLRotTarget library function call
+money money(key id, integer amount):Triggered when money is given to task
+email email(string time, string address, string subj, string message, integer num_left):Triggered when task receives email
+run_time_permissions run_time_permissions(integer perm):Triggered when an agent grants run time permissions to task
+attach attach(key id):Triggered when an agent attaches or detaches from agent
+dataserver dataserver(key queryid, string data):Triggered when task receives asynchronous data
+moving_start moving_start():Triggered when task begins moving
+moving_end moving_end():Triggered when task stops moving
+on_rez on_rez(integer start_param):Triggered when task is rezed in from inventory or another task
+object_rez object_rez(key id):Triggered when task rezes in another task
+link_message link_message(integer sender_num, integer num, string str, key id):Triggered when task receives a link message via LLMessageLinked library function call
+changed changed( integer change ):Triggered various event change the task:(test change with CHANGED_INVENTORY, CHANGED_COLOR, CHANGED_SHAPE, CHANGED_SCALE, CHANGED_TEXTURE, CHANGED_LINK, CHANGED_ALLOWED_DROP, CHANGED_OWNER, CHANGED_REGION, CHANGED_TELEPORT)
+remote_data remote_data(integer event_type, key channel, key message_id, string sender,integer idata, string sdata):Triggered by various XML-RPC calls (event_type will be one of REMOTE_DATA_CHANNEL, REMOTE_DATA_REQUEST, REMOTE_DATA_REPLY)
+http_response http_response(key request_id, integer status, list metadata, string body):Triggered when task receives a response to one of its llHTTPRequests
+
+# integer constants
+[word .1, .1, .5]
+TRUE Integer constant for Boolean operations
+FALSE Integer constant for Boolean operations
+STATUS_PHYSICS Passed in the llSetStatus library function. If TRUE, object moves physically
+STATUS_PHANTOM Passed in the llSetStatus library function. If TRUE, object doesn't collide with other objects
+STATUS_ROTATE_X Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local X axis
+STATUS_ROTATE_Y Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local Y axis
+STATUS_ROTATE_Z Passed in the llSetStatus library function. If FALSE, object doesn't rotate around local Z axis
+STATUS_SANDBOX Passed in the llSetStatus library function. If TRUE, object can't cross region boundaries or move more than 10 meters from its start location
+STATUS_BLOCK_GRAB Passed in the llSetStatus library function. If TRUE, object can't be grabbed and physically dragged
+STATUS_DIE_AT_EDGE Passed in the llSetStatus library function. If TRUE, objects that reach the edge of the world just die:rather than teleporting back to the owner
+STATUS_RETURN_AT_EDGE Passed in the llSetStatus library function. If TRUE, script rezzed objects that reach the edge of the world:are returned rather than killed:STATUS_RETURN_AT_EDGE trumps STATUS_DIE_AT_EDGE if both are set
+STATUS_CAST_SHADOWS Passed in the llSetStatus library function. If TRUE, object casts shadows on other objects
+AGENT Passed in llSensor library function to look for other Agents
+ACTIVE Passed in llSensor library function to look for moving objects
+PASSIVE Passed in llSensor library function to look for objects that aren't moving
+SCRIPTED Passed in llSensor library function to look for scripted objects
+CONTROL_FWD Passed to llTakeControls library function and used control event handler to test for agent forward control
+CONTROL_BACK Passed to llTakeControls library function and used control event handler to test for agent back control
+CONTROL_LEFT Passed to llTakeControls library function and used control event handler to test for agent left control
+CONTROL_RIGHT Passed to llTakeControls library function and used control event handler to test for agent right control
+CONTROL_ROT_LEFT Passed to llTakeControls library function and used control event handler to test for agent rotate left control
+CONTROL_ROT_RIGHT Passed to llTakeControls library function and used control event handler to test for agent rotate right control
+CONTROL_UP Passed to llTakeControls library function and used control event handler to test for agent up control
+CONTROL_DOWN Passed to llTakeControls library function and used control event handler to test for agent down control
+CONTROL_LBUTTON Passed to llTakeControls library function and used control event handler to test for agent left button control
+CONTROL_ML_LBUTTON Passed to llTakeControls library function and used control event handler to test for agent left button control with the agent in mouse look
+PERMISSION_DEBIT Passed to llRequestPermissions library function to request permission to take money from agent's account
+PERMISSION_TAKE_CONTROLS Passed to llRequestPermissions library function to request permission to take agent's controls
+# PERMISSION_REMAP_CONTROLS Passed to llRequestPermissions library function to request permission to remap agent's controls (not implemented yet)
+PERMISSION_TRIGGER_ANIMATION Passed to llRequestPermissions library function to request permission to trigger animation on agent
+PERMISSION_ATTACH Passed to llRequestPermissions library function to request permission to attach/detach from agent
+# PERMISSION_RELEASE_OWNERSHIP Passed to llRequestPermissions library function to request permission to release ownership (not implemented)
+PERMISSION_CHANGE_LINKS Passed to llRequestPermissions library function to request permission to change links
+# PERMISSION_CHANGE_JOINTS Passed to llRequestPermissions library function to request permission to change joints (not implemented)
+# PERMISSION_CHANGE_PERMISSIONS Passed to llRequestPermissions library function to request permission to change permissions
+PERMISSION_TRACK_CAMERA Passed to llRequestPermissions library function to request permission to track agent's camera
+PERMISSION_CONTROL_CAMERA Passed to llRequestPermissions library function to request permission to change agent's camera
+
+DEBUG_CHANNEL Chat channel reserved for debug and error messages from scripts
+PUBLIC_CHANNEL Chat channel that broadcasts to all nearby users
+
+AGENT_FLYING Returned by llGetAgentInfo if the Agent is flying
+AGENT_ATTACHMENTS Returned by llGetAgentInfo if the Agent has attachments
+AGENT_SCRIPTED Returned by llGetAgentInfo if the Agent has scripted attachments
+AGENT_SITTING Returned by llGetAgentInfo if the Agent is sitting
+AGENT_ON_OBJECT Returned by llGetAgentInfo if the Agent is sitting on an object
+AGENT_MOUSELOOK Returned by llGetAgentInfo if the Agent is in mouselook
+AGENT_AWAY Returned by llGetAgentInfo if the Agent is in away mode
+AGENT_WALKING Returned by llGetAgentInfo if the Agent is walking
+AGENT_IN_AIR Returned by llGetAgentInfo if the Agent is in the air
+AGENT_TYPING Returned by llGetAgentInfo if the Agent is typing
+AGENT_CROUCHING Returned by llGetAgentInfo if the Agent is crouching
+AGENT_BUSY Returned by llGetAgentInfo if the Agent is busy
+AGENT_ALWAYS_RUN Returned by llGetAgentInfo if the Agent has 'Always Run' enabled
+
+PSYS_PART_FLAGS
+PSYS_PART_START_COLOR
+PSYS_PART_START_ALPHA
+PSYS_PART_START_SCALE
+PSYS_PART_END_COLOR
+PSYS_PART_END_ALPHA
+PSYS_PART_END_SCALE
+PSYS_PART_MAX_AGE
+
+PSYS_PART_BOUNCE_MASK
+PSYS_PART_WIND_MASK
+PSYS_PART_INTERP_COLOR_MASK
+PSYS_PART_INTERP_SCALE_MASK
+PSYS_PART_FOLLOW_SRC_MASK
+PSYS_PART_FOLLOW_VELOCITY_MASK
+PSYS_PART_TARGET_POS_MASK
+PSYS_PART_EMISSIVE_MASK
+PSYS_PART_TARGET_LINEAR_MASK
+
+PSYS_SRC_PATTERN
+PSYS_SRC_INNERANGLE Deprecated -- Use PSYS_SRC_ANGLE_BEGIN
+PSYS_SRC_OUTERANGLE Deprecated -- Use PSYS_SRC_ANGLE_END
+PSYS_SRC_ANGLE_BEGIN
+PSYS_SRC_ANGLE_END
+PSYS_SRC_BURST_RATE
+PSYS_SRC_BURST_PART_COUNT
+PSYS_SRC_BURST_RADIUS
+PSYS_SRC_BURST_SPEED_MIN
+PSYS_SRC_BURST_SPEED_MAX
+PSYS_SRC_MAX_AGE
+PSYS_SRC_ACCEL
+PSYS_SRC_TEXTURE
+PSYS_SRC_TARGET_KEY
+PSYS_SRC_OMEGA
+
+PSYS_SRC_PATTERN_DROP
+PSYS_SRC_PATTERN_EXPLODE
+PSYS_SRC_PATTERN_ANGLE
+PSYS_SRC_PATTERN_ANGLE_CONE
+PSYS_SRC_PATTERN_ANGLE_CONE_EMPTY
+
+# some vehicle params
+VEHICLE_TYPE_NONE
+VEHICLE_TYPE_SLED
+VEHICLE_TYPE_CAR
+VEHICLE_TYPE_BOAT
+VEHICLE_TYPE_AIRPLANE
+VEHICLE_TYPE_BALLOON
+
+VEHICLE_REFERENCE_FRAME Rotation of vehicle axes relative to local frame
+
+VEHICLE_LINEAR_FRICTION_TIMESCALE A vector of timescales for exponential decay of linear velocity along the three vehicle axes
+VEHICLE_ANGULAR_FRICTION_TIMESCALE A vector of timescales for exponential decay of angular velocity about the three vehicle axes
+VEHICLE_LINEAR_MOTOR_DIRECTION The linear velocity that the vehicle will try to achieve
+VEHICLE_LINEAR_MOTOR_OFFSET An offset from the center of mass of the vehicle where the linear motor is applied
+VEHICLE_ANGULAR_MOTOR_DIRECTION The angular velocity that the vehicle will try to achieve
+
+VEHICLE_HOVER_HEIGHT The height the vehicle will try to hover
+VEHICLE_HOVER_EFFICIENCY A slider between 0 (bouncy) and 1 (critically damped) hover behavior
+VEHICLE_HOVER_TIMESCALE The period of time for the vehicle to achieve its hover height
+VEHICLE_BUOYANCY A slider between 0 (no anti-gravity) and 1 (full anti-gravity)
+
+VEHICLE_LINEAR_DEFLECTION_EFFICIENCY A slider between 0 (no deflection) and 1 (maximum strength)
+VEHICLE_LINEAR_DEFLECTION_TIMESCALE The exponential timescale for the vehicle to redirect its velocity to be along its x-axis
+
+VEHICLE_LINEAR_MOTOR_TIMESCALE The exponential timescale for the vehicle to achive its full linear motor velocity
+VEHICLE_LINEAR_MOTOR_DECAY_TIMESCALE The exponential timescale for the linear motor's effectiveness to decay toward zero
+
+VEHICLE_ANGULAR_DEFLECTION_EFFICIENCY A slider between 0 (no deflection) and 1 (maximum strength)
+VEHICLE_ANGULAR_DEFLECTION_TIMESCALE The exponential timescale for the vehicle to achieve full angular deflection
+
+VEHICLE_ANGULAR_MOTOR_TIMESCALE The exponential timescale for the vehicle to achive its full angular motor velocity
+VEHICLE_ANGULAR_MOTOR_DECAY_TIMESCALE The exponential timescale for the angular motor's effectiveness to decay toward zero
+
+VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY A slider between 0 (bouncy) and 1 (critically damped) attraction of vehicle z-axis to world z-axis (vertical)
+VEHICLE_VERTICAL_ATTRACTION_TIMESCALE The exponential timescale for the vehicle to align its z-axis to the world z-axis (vertical)
+
+VEHICLE_BANKING_EFFICIENCY A slider between -1 (leans out of turns), 0 (no banking), and +1 (leans into turns)
+VEHICLE_BANKING_MIX A slider betwen 0 (static banking) and 1 (dynamic banking)
+VEHICLE_BANKING_TIMESCALE The exponential timescale for the banking behavior to take full effect
+
+VEHICLE_FLAG_NO_DEFLECTION_UP Prevents linear deflection along world-z axis
+VEHICLE_FLAG_LIMIT_ROLL_ONLY Removes vertical attraction for changes in vehicle pitch
+VEHICLE_FLAG_HOVER_WATER_ONLY Hover only pays attention to water level
+VEHICLE_FLAG_HOVER_TERRAIN_ONLY Hover only pays attention to terrain height
+VEHICLE_FLAG_HOVER_GLOBAL_HEIGHT Hover only pays attention to global height
+VEHICLE_FLAG_HOVER_UP_ONLY Hover only pushes up
+VEHICLE_FLAG_LIMIT_MOTOR_UP Prevents ground vehicles from motoring into the sky
+VEHICLE_FLAG_MOUSELOOK_STEER Makes vehicle try to turn toward mouselook direction.
+VEHICLE_FLAG_MOUSELOOK_BANK Makes vehicle try to turn toward mouselook direction assuming banking is enabled.
+VEHICLE_FLAG_CAMERA_DECOUPLED Causes the camera look-at axis to NOT move when the vehicle rotates.
+
+CAMERA_PITCH (-45 to 80) (Adjusts the angular amount that the camera aims straight ahead vs. straight down, maintaining the same distance. Analogous to 'incidence'.")
+CAMERA_FOCUS_OFFSET (-10 to 10) A vector that adjusts the position of the camera focus position relative to the subject
+CAMERA_POSITION_LAG (0.0 to 3.0) How much the camera lags as it tries to move towards its 'ideal' position
+CAMERA_FOCUS_LAG (0.0 to 3.0) How much the camera lags as it tries to aim towards the subject
+CAMERA_DISTANCE (0.5 to 10) Sets how far away the camera wants to be from its subject
+CAMERA_BEHINDNESS_ANGLE (0 to 180) Sets the angle in degrees within which the camera is not constrained by changes in subject rotation
+CAMERA_BEHINDNESS_LAG (0.0 to 3.0) Sets how strongly the camera is forced to stay behind the target if outside of behindness angle
+CAMERA_POSITION_THRESHOLD (0.0 to 4.0) Sets the radius of a sphere around the camera's ideal position within which it is not affected by subject motion
+CAMERA_FOCUS_THRESHOLD (0.0 to 4.0) Sets the radius of a sphere around the camera's subject position within which its focus is not affected by subject motion
+CAMERA_ACTIVE (0 or 1) Turns on or off scripted control of the camera
+CAMERA_POSITION Sets the position of the camera
+CAMERA_FOCUS Sets the focus (target position) of the camera
+CAMERA_POSITION_LOCKED (0 or 1) Locks the camera position so it will not move
+CAMERA_FOCUS_LOCKED (0 or 1) Locks the camera focus so it will not move
+
+INVENTORY_TEXTURE Passed to task inventory library functions to reference textures
+INVENTORY_SOUND Passed to task inventory library functions to reference sounds
+INVENTORY_OBJECT Passed to task inventory library functions to reference objects
+INVENTORY_SCRIPT Passed to task inventory library functions to reference scripts
+INVENTORY_LANDMARK Passed to task inventory library functions to reference landmarks
+INVENTORY_CLOTHING Passed to task inventory library functions to reference clothing
+INVENTORY_NOTECARD Passed to task inventory library functions to reference notecards
+INVENTORY_BODYPART Passed to task inventory library functions to reference body parts
+INVENTORY_ANIMATION Passed to task inventory library functions to reference animations
+INVENTORY_GESTURE Passed to task inventory library functions to reference gestures
+INVENTORY_ALL Passed to task inventory library functions to reference all inventory items
+INVENTORY_NONE Returned by llGetInventoryType when no item is found.
+
+ATTACH_CHEST Passed to llAttachToAvatar to attach task to chest
+ATTACH_HEAD Passed to llAttachToAvatar to attach task to head
+ATTACH_LSHOULDER Passed to llAttachToAvatar to attach task to left shoulder
+ATTACH_RSHOULDER Passed to llAttachToAvatar to attach task to right shoulder
+ATTACH_LHAND Passed to llAttachToAvatar to attach task to left hand
+ATTACH_RHAND Passed to llAttachToAvatar to attach task to right hand
+ATTACH_LFOOT Passed to llAttachToAvatar to attach task to left foot
+ATTACH_RFOOT Passed to llAttachToAvatar to attach task to right foot
+ATTACH_BACK Passed to llAttachToAvatar to attach task to back
+ATTACH_PELVIS Passed to llAttachToAvatar to attach task to pelvis
+ATTACH_MOUTH Passed to llAttachToAvatar to attach task to mouth
+ATTACH_CHIN Passed to llAttachToAvatar to attach task to chin
+ATTACH_LEAR Passed to llAttachToAvatar to attach task to left ear
+ATTACH_REAR Passed to llAttachToAvatar to attach task to right ear
+ATTACH_LEYE Passed to llAttachToAvatar to attach task to left eye
+ATTACH_REYE Passed to llAttachToAvatar to attach task to right eye
+ATTACH_NOSE Passed to llAttachToAvatar to attach task to noce
+ATTACH_RUARM Passed to llAttachToAvatar to attach task to right upper arm
+ATTACH_RLARM Passed to llAttachToAvatar to attach task to right lower arm
+ATTACH_LUARM Passed to llAttachToAvatar to attach task to left upper arm
+ATTACH_LLARM Passed to llAttachToAvatar to attach task to left lower arm
+ATTACH_RHIP Passed to llAttachToAvatar to attach task to right hip
+ATTACH_RULEG Passed to llAttachToAvatar to attach task to right upper leg
+ATTACH_RLLEG Passed to llAttachToAvatar to attach task to right lower leg
+ATTACH_LHIP Passed to llAttachToAvatar to attach task to left hip
+ATTACH_LULEG Passed to llAttachToAvatar to attach task to left upper leg
+ATTACH_LLLEG Passed to llAttachToAvatar to attach task to left lower leg
+ATTACH_BELLY Passed to llAttachToAvatar to attach task to belly
+ATTACH_RPEC Passed to llAttachToAvatar to attach task to right pectoral
+ATTACH_LPEC Passed to llAttachToAvatar to attach task to left pectoral
+
+LAND_LEVEL Passed to llModifyLand to level terrain
+LAND_RAISE Passed to llModifyLand to raise terrain
+LAND_LOWER Passed to llModifyLand to lower terrain
+LAND_SMOOTH Passed to llModifyLand to smooth terrain
+LAND_NOISE Passed to llModifyLand to randomize terrain
+LAND_REVERT Passed to llModifyLand to revert terrain toward original state
+LAND_SMALL_BRUSH Passed to llModifyLand to modify small land areas
+LAND_MEDIUM_BRUSH Passed to llModifyLand to modify medium land areas
+LAND_LARGE_BRUSH Passed to llModifyLand to modify large land areas
+
+DATA_PAYINFO Passed to llRequestAgentData to get payment status of an agent
+DATA_ONLINE Passed to llRequestAgentData to determine if agent is online
+DATA_NAME Passed to llRequestAgentData to get full agent name
+DATA_BORN Passed to llRequestAgentData to get born on date as a string
+DATA_RATING Passed to llRequestAgentData to get a comma separated sting of integer ratings
+DATA_SIM_POS Passed to llRequestSimulatorData to get a string (cast to vector) of a simulator's global position
+DATA_SIM_STATUS Passed to llRequestSimulatorData to get the status of a simulator
+DATA_SIM_RATING Passed to llRequestSimulatorData to get the rating of a simulator
+
+PAYMENT_INFO_ON_FILE Used with llRequestAgentData to tell if Agent is of "Payment Info On File" status
+PAYMENT_INFO_USED Used with llRequestAgentData to tell if Agent is of "Payment Info Used" status
+
+ANIM_ON Enable texture animation
+LOOP Loop when animating textures
+REVERSE Animate in the reverse direction
+PING_PONG Animate forward, then reverse.
+SMOOTH Textures slides, instead of stepping
+ROTATE Rotates the texture, instead of using frames
+SCALE Scales the texture, instead of using frames
+
+ALL_SIDES Passed to various texture and color library functions to modify all sides
+
+LINK_SET Passed to various link functions to modify all blocks in the object
+LINK_ROOT Passed to various link functions to modify only the root block (no effect on single block objects)
+LINK_ALL_OTHERS Passed to various link functions to modify all other blocks in the object
+LINK_ALL_CHILDREN Passed to various link functions to modify all child blocks in the object
+LINK_THIS Passed to various link functions to modify only the calling block
+
+CHANGED_INVENTORY Parameter of changed event handler used to indicate change to task's inventory
+CHANGED_COLOR Parameter of changed event handler used to indicate change to task's color
+CHANGED_SHAPE Parameter of changed event handler used to indicate change to task's shape parameters
+CHANGED_SCALE Parameter of changed event handler used to indicate change to task's scale
+CHANGED_TEXTURE Parameter of changed event handler used to indicate change to task's texture
+CHANGED_LINK Parameter of changed event handler used to indicate change to task's link status
+CHANGED_ALLOWED_DROP Parameter of changed event handler used to indicate a user dropped an inventory item:onto task that was allowed only by llAllowInventoryDrop function call
+CHANGED_OWNER Parameter of changed event handler used to indicate change to task's owner ONLY when an object is sold as original or deeded to group
+CHANGED_REGION Parameter of changed event handler used to indicate the region has changed
+CHANGED_TELEPORT Parameter of changed event handler used to indicate teleport has completed
+
+TYPE_INTEGER Indicates that the list entry is holding an integer
+TYPE_FLOAT Indicates that the list entry is holding an float
+TYPE_STRING Indicates that the list entry is holding an string
+TYPE_KEY Indicates that the list entry is holding an key
+TYPE_VECTOR Indicates that the list entry is holding an vector
+TYPE_ROTATION Indicates that the list entry is holding an rotation
+TYPE_INVALID Indicates that this wasn't a valid list entry
+
+
+REMOTE_DATA_CHANNEL Value of event_type in remote_event after successful llOpenRemoteDataChannel
+REMOTE_DATA_REQUEST Value of event_type in remote_event if XML-RPC request is received
+REMOTE_DATA_REPLY Value of event_type in remote_event if XML-RPC reply is received
+
+
+PRIM_TYPE Followed by PRIM_TYPE_BOX, PRIM_TYPE_CYLINDER, PRIM_TYPE_PRISM, PRIM_TYPE_SPHERE,:PRIM_TYPE_TORUS, or PRIM_TYPE_TUBE and their arguments
+PRIM_MATERIAL Sets material to PRIM_MATERIAL_STONE, PRIM_MATERIAL_METAL, PRIM_MATERIAL_GLASS,:PRIM_MATERIAL_WOOD, PRIM_MATERIAL_FLESH, PRIM_MATERIAL_PLASTIC,:PRIM_MATERIAL_RUBBER, or PRIM_MATERIAL_LIGHT
+PRIM_PHYSICS Sets physics to TRUE or FALSE
+PRIM_FLEXIBLE Sets primitive flexibility to TRUE or FALSE
+PRIM_POINT_LIGHT Sets light emission to TRUE or FALSE
+PRIM_TEMP_ON_REZ Sets temporay on rez to TRUE or FALSE
+PRIM_PHANTOM Sets phantom to TRUE or FALSE
+PRIM_CAST_SHADOWS Enables or disables shadow casting for the primitive
+PRIM_POSITION Sets primitive position to a vector position
+PRIM_SIZE Sets primitive size to a vector size
+PRIM_ROTATION Sets primitive rotation
+PRIM_TEXTURE Followed by an integer face, key id, vector repeats, vector offsets,:and float rotation in radians
+PRIM_COLOR Followed by an integer face, vector color, and float alpha
+PRIM_BUMP_SHINY Followed by an integer face, one of PRIM_SHINY_NONE, PRIM_SHINY_LOW,:PRIM_SHINY_MEDIUM, or PRIM_SHINY_HIGH,:and one of PRIM_BUMP_NONE, PRIM_BUMP_BRIGHT, PRIM_BUMP_DARK, etc
+PRIM_FULLBRIGHT Followed by an integer face, and TRUE or FALSE
+PRIM_TEXGEN Followed by an integer face, and one of PRIM_TEXGEN_DEFAULT or PRIM_TEXGEN_PLANAR
+
+PRIM_TYPE_BOX Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_CYLINDER Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_PRISM Followed by integer hole shape, vector cut, float hollow, vector twist,:vector top size, and vector top shear
+PRIM_TYPE_SPHERE Followed by integer hole shape, vector cut, float hollow, vector twist,:and vector dimple
+PRIM_TYPE_TORUS Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+PRIM_TYPE_TUBE Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+PRIM_TYPE_RING Followed by integer hole shape, vector cut, float hollow, vector twist,:vector hole size, vector top shear, vector advanced cut, vector taper,:float revolutions, float radius offset, and float skew
+
+PRIM_HOLE_DEFAULT Sets hole type to match the prim type.
+PRIM_HOLE_SQUARE Sets hole type to square.
+PRIM_HOLE_CIRCLE Sets hole type to circle.
+PRIM_HOLE_TRIANGLE Sets hole type to triangle.
+
+PRIM_MATERIAL_STONE Sets material to stone
+PRIM_MATERIAL_METAL Sets material to metal
+PRIM_MATERIAL_GLASS Sets material to glass
+PRIM_MATERIAL_WOOD Sets material to wood
+PRIM_MATERIAL_FLESH Sets material to flesh
+PRIM_MATERIAL_PLASTIC Sets material to plastic
+PRIM_MATERIAL_RUBBER Sets material to rubber
+PRIM_MATERIAL_LIGHT Sets material to light
+
+PRIM_SHINY_NONE No shininess
+PRIM_SHINY_LOW Low shininess
+PRIM_SHINY_MEDIUM Medium shininess
+PRIM_SHINY_HIGH High shininess
+
+PRIM_BUMP_NONE No bump map
+PRIM_BUMP_BRIGHT Generate bump map from highlights
+PRIM_BUMP_DARK Generate bump map from lowlights
+PRIM_BUMP_WOOD Wood bump map
+PRIM_BUMP_BARK Bark bump map
+PRIM_BUMP_BRICKS Brick bump map
+PRIM_BUMP_CHECKER Checker bump map
+PRIM_BUMP_CONCRETE Concrete bump map
+PRIM_BUMP_TILE Tile bump map
+PRIM_BUMP_STONE Stone bump map
+PRIM_BUMP_DISKS Disk bump map
+PRIM_BUMP_GRAVEL Gravel bump map
+PRIM_BUMP_BLOBS Blob bump map
+PRIM_BUMP_SIDING Siding bump map
+PRIM_BUMP_LARGETILE Large tile bump map
+PRIM_BUMP_STUCCO Stucco bump map
+PRIM_BUMP_SUCTION Suction cup bump map
+PRIM_BUMP_WEAVE Weave bump map
+
+PRIM_TEXGEN_DEFAULT Default texture mapping
+PRIM_TEXGEN_PLANAR Planar texture mapping
+
+MASK_BASE Base permissions
+MASK_OWNER Owner permissions
+MASK_GROUP Group permissions
+MASK_EVERYONE Everyone permissions
+MASK_NEXT Next owner permissions
+
+PERM_TRANSFER Transfer permission
+PERM_MODIFY Modify permission
+PERM_COPY Copy permission
+PERM_MOVE Move permission
+PERM_ALL Move/Modify/Copy/Transfer permissions
+
+PARCEL_MEDIA_COMMAND_STOP Stop media stream
+PARCEL_MEDIA_COMMAND_PAUSE Pause media stream
+PARCEL_MEDIA_COMMAND_PLAY Play media stream
+PARCEL_MEDIA_COMMAND_LOOP Loop media stream
+PARCEL_MEDIA_COMMAND_TEXTURE Get or set the parcel's media texture
+PARCEL_MEDIA_COMMAND_URL Get or set the parcel's media url
+PARCEL_MEDIA_COMMAND_TIME Set media stream to specific time
+PARCEL_MEDIA_COMMAND_AGENT Allows media stream commands to apply to only one agent
+PARCEL_MEDIA_COMMAND_UNLOAD Unloads the media stream
+PARCEL_MEDIA_COMMAND_AUTO_ALIGN Auto aligns the media stream to the texture size. May cause a performance hit and loss of some visual quality.
+
+PAY_HIDE Used with llSetPayPrice to hide a button
+PAY_DEFAULT Used with llSetPayPrice to use the default price for a button
+
+LIST_STAT_MAX Used with llListStatistics to find the largest number in a list
+LIST_STAT_MIN Used with llListStatistics to find the smallest number in a list
+LIST_STAT_MEAN Used with llListStatistics to find the mean of the numbers in a list
+LIST_STAT_MEDIAN Used with llListStatistics to find the median of the numbers in a list
+LIST_STAT_STD_DEV Used with llListStatistics to find the standard deviation of the numbers in a list
+LIST_STAT_SUM Used with llListStatistics to find the sum of the numbers in a list
+LIST_STAT_SUM_SQUARES Used with llListStatistics to find the sum of the squares of the numbers in a list
+LIST_STAT_NUM_COUNT Used with llListStatistics to find how many numbers are in a list
+LIST_STAT_GEOMETRIC_MEAN Used with llListStatistics to find the geometric mean of the numbers in a list (all numbers must be > 0)
+LIST_STAT_RANGE Used with llListStatistics to find the range of the numbers in a list
+
+PARCEL_FLAG_ALLOW_FLY Used with llGetParcelFlags to find if a parcel allows flying
+PARCEL_FLAG_ALLOW_GROUP_SCRIPTS Used with llGetParcelFlags to find if a parcel allows group scripts
+PARCEL_FLAG_ALLOW_SCRIPTS Used with llGetParcelFlags to find if a parcel allows outside scripts
+PARCEL_FLAG_ALLOW_LANDMARK Used with llGetParcelFlags to find if a parcel allows landmarks to be created
+PARCEL_FLAG_ALLOW_TERRAFORM Used with llGetParcelFlags to find if a parcel allows anyone to terraform the land
+PARCEL_FLAG_ALLOW_DAMAGE Used with llGetParcelFlags to find if a parcel allows damage
+PARCEL_FLAG_ALLOW_CREATE_OBJECTS Used with llGetParcelFlags to find if a parcel allows anyone to create objects
+PARCEL_FLAG_USE_ACCESS_GROUP Used with llGetParcelFlags to find if a parcel limits access to a group
+PARCEL_FLAG_USE_ACCESS_LIST Used with llGetParcelFlags to find if a parcel limits access to a list of residents
+PARCEL_FLAG_USE_BAN_LIST Used with llGetParcelFlags to find if a parcel uses a ban list
+PARCEL_FLAG_USE_LAND_PASS_LIST Used with llGetParcelFlags to find if a parcel allows passes to be purchased
+PARCEL_FLAG_LOCAL_SOUND_ONLY Used with llGetParcelFlags to find if a parcel restricts spacialized sound to the parcel
+PARCEL_FLAG_RESTRICT_PUSHOBJECT Used with llGetParcelFlags to find if a parcel restricts llPushObject() calls
+
+REGION_FLAG_ALLOW_DAMAGE Used with llGetRegionFlags to find if a region is entirely damage enabled
+REGION_FLAG_FIXED_SUN Used with llGetRegionFlags to find if a region has a fixed sun position
+REGION_FLAG_BLOCK_TERRAFORM Used with llGetRegionFlags to find if a region terraforming disabled
+REGION_FLAG_SANDBOX Used with llGetRegionFlags to find if a region is a sandbox
+REGION_FLAG_DISABLE_COLLISIONS Used with llGetRegionFlags to find if a region has disabled collisions
+REGION_FLAG_DISABLE_PHYSICS Used with llGetRegionFlags to find if a region has disabled physics
+REGION_FLAG_BLOCK_FLY Used with llGetRegionFlags to find if a region blocks flying
+REGION_FLAG_ALLOW_DIRECT_TELEPORT Used with llGetRegionFlags to find if a region allows direct teleports
+REGION_FLAG_RESTRICT_PUSHOBJECT Used with llGetRegionFlags to find if a region restricts llPushObject() calls
+
+HTTP_METHOD Used with llHTTPRequest to specify the method, such as "GET" or "POST"
+HTTP_MIMETYPE Used with llHTTPRequest to specify the MIME type, defaults to "text/plain"
+HTTP_BODY_MAXLENGTH Used with llHTTPRequest to specify the maxium reponse body to return
+HTTP_VERIFY_CERT Used with llHTTPRequest to specify SSL certificate verification
+HTTP_BODY_TRUNCATED Used with http_response to indicate truncation point in bytes
+
+PARCEL_COUNT_TOTAL Used with llGetParcelPrimCount to get the total number of prims on the parcel
+PARCEL_COUNT_OWNER Used with llGetParcelPrimCount to get the number of prims on the parcel owned by the owner
+PARCEL_COUNT_GROUP Used with llGetParcelPrimCount to get the number of prims on the parcel owned by the group
+PARCEL_COUNT_OTHER Used with llGetParcelPrimCount to get the number of prims on the parcel owned by others
+PARCEL_COUNT_SELECTED Used with llGetParcelPrimCount to get the number of prims on the parcel currently selected or sat upon
+PARCEL_COUNT_TEMP Used with llGetParcelPrimCount to get the number of prims on the parcel that are temp on rez
+
+PARCEL_DETAILS_NAME Used with llGetParcelDetails to get the parcel name.
+PARCEL_DETAILS_DESC Used with llGetParcelDetails to get the parcel description.
+PARCEL_DETAILS_OWNER Used with llGetParcelDetails to get the parcel owner id.
+PARCEL_DETAILS_GROUP Used with llGetParcelDetails to get the parcel group id.
+PARCEL_DETAILS_AREA Used with llGetParcelDetails to get the parcel area in square meters.
+
+# string constants
+[word .1, .3, .5]
+NULL_KEY Indicates an empty key
+EOF Indicates the last line of a notecard was read
+
+# float constants
+[word .3, .1, .5]
+PI 3.1415926535897932384626433832795
+TWO_PI 6.283185307179586476925286766559
+PI_BY_TWO 1.5707963267948966192313216916398
+DEG_TO_RAD To convert from degrees to radians
+RAD_TO_DEG To convert from radians to degrees
+SQRT2 1.4142135623730950488016887242097
+
+# compound constants
+[word .4, .2, .4]
+ZERO_VECTOR <0.0, 0.0, 0.0>
+ZERO_ROTATION <0.0, 0.0, 0.0, 1.0>
+
+
+# flow control keywords
+[word 0, 0, .8]
+for for loop:for (initializer; test; iteration):{: statements:}
+do do loop:do:{: statements:} while (test);
+while while loop:while (test):{ statements:}
+if if statement:if (test):{ statements:}
+else else clause:if (test):{ statements:}:else:{ statements:}
+jump jump statement:jump label;:
+return Leave current function or event handler
+
+# flow control label
+[line 0, 0, .8]
+@ Label:Target for jump statement
+
+# Comment
+[one_sided_delimiter .8, .3, .15]
+// Comment:Non-functional commentary or disabled code
+
+# String literals
+[two_sided_delimiter 0, .2, 0]
+" String literal
+
+#functions are supplied by the program now.
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
new file mode 100644
index 0000000000..5731add4d5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarF.glsl
@@ -0,0 +1,7 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
new file mode 100644
index 0000000000..1fcc001911
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarSkinV.glsl
@@ -0,0 +1,19 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
+
+attribute vec4 weight; //1
+
+uniform vec4 matrixPalette[45];
+
+mat4 getSkinnedTransform()
+{
+ mat4 ret;
+ int i = int(floor(weight.x));
+ float x = fract(weight.x);
+
+ ret[0] = mix(matrixPalette[i+0], matrixPalette[i+1], x);
+ ret[1] = mix(matrixPalette[i+15],matrixPalette[i+16], x);
+ ret[2] = mix(matrixPalette[i+30],matrixPalette[i+31], x);
+ ret[3] = vec4(0,0,0,1);
+
+ return ret;
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
new file mode 100644
index 0000000000..50f9b0192e
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/avatarV.glsl
@@ -0,0 +1,35 @@
+void default_scatter(vec3 viewVec, vec3 lightDir);
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+mat4 getSkinnedTransform();
+vec2 getScatterCoord(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ vec4 pos;
+ vec3 norm;
+
+ mat4 trans = getSkinnedTransform();
+ pos.x = dot(trans[0], gl_Vertex);
+ pos.y = dot(trans[1], gl_Vertex);
+ pos.z = dot(trans[2], gl_Vertex);
+ pos.w = 1.0;
+
+ norm.x = dot(trans[0].xyz, gl_Normal);
+ norm.y = dot(trans[1].xyz, gl_Normal);
+ norm.z = dot(trans[2].xyz, gl_Normal);
+ norm = normalize(norm);
+
+ gl_Position = gl_ProjectionMatrix * pos;
+
+ //gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color);
+ gl_FrontColor = color;
+
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
new file mode 100644
index 0000000000..5731add4d5
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballF.glsl
@@ -0,0 +1,7 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
new file mode 100644
index 0000000000..d436b4e00a
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/eyeballV.glsl
@@ -0,0 +1,20 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ vec4 color = calcLighting(pos, norm, materialColor, gl_Color.rgb);
+ default_scatter(pos, gl_LightSource[0].position.xyz);
+
+ gl_FrontColor = color;
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
new file mode 100644
index 0000000000..b311afb59c
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarF.glsl
@@ -0,0 +1,6 @@
+uniform sampler2D diffuseMap;
+
+void main()
+{
+ gl_FragColor = vec4(gl_Color.rgb, texture2D(diffuseMap, gl_TexCoord[0].xy).a);
+}
diff --git a/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
new file mode 100644
index 0000000000..b6dcbe1693
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/avatar/pickAvatarV.glsl
@@ -0,0 +1,17 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color);
+mat4 getSkinnedTransform();
+
+void main()
+{
+ vec4 pos;
+
+ mat4 trans = getSkinnedTransform();
+ pos.x = dot(trans[0], gl_Vertex);
+ pos.y = dot(trans[1], gl_Vertex);
+ pos.z = dot(trans[2], gl_Vertex);
+ pos.w = 1.0;
+
+ gl_FrontColor = gl_Color;
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_Position = gl_ProjectionMatrix * pos;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
new file mode 100644
index 0000000000..fde370155d
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainF.glsl
@@ -0,0 +1,19 @@
+void terrain_lighting(inout vec3 color);
+
+uniform sampler2D detail0; //0
+uniform sampler2D detail1; //2
+uniform sampler2D alphaRamp; //1
+
+
+void main()
+{
+ float a = texture2D(alphaRamp, gl_TexCoord[1].xy).a;
+ vec3 color = mix(texture2D(detail1, gl_TexCoord[2].xy).rgb,
+ texture2D(detail0, gl_TexCoord[0].xy).rgb,
+ a);
+
+ terrain_lighting(color);
+
+ gl_FragColor.rgb = color;
+ gl_FragColor.a = texture2D(alphaRamp, gl_TexCoord[3].xy).a;
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
new file mode 100644
index 0000000000..3153a80e93
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/terrainV.glsl
@@ -0,0 +1,37 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1)
+{
+ vec4 tcoord;
+
+ tcoord.x = dot(vpos, tp0);
+ tcoord.y = dot(vpos, tp1);
+ tcoord.z = tc.z;
+ tcoord.w = tc.w;
+
+ tcoord = mat * tcoord;
+
+ return tcoord;
+}
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+ vec4 pos = gl_ModelViewMatrix * gl_Vertex;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color);
+
+ gl_FrontColor = color;
+
+ gl_TexCoord[0] = texgen_object(gl_Vertex,gl_MultiTexCoord0,gl_TextureMatrix[0],gl_ObjectPlaneS[0],gl_ObjectPlaneT[0]);
+ gl_TexCoord[1] = gl_TextureMatrix[1]*gl_MultiTexCoord1;
+ gl_TexCoord[2] = texgen_object(gl_Vertex,gl_MultiTexCoord2,gl_TextureMatrix[2],gl_ObjectPlaneS[2],gl_ObjectPlaneT[2]);
+ gl_TexCoord[3] = gl_TextureMatrix[3]*gl_MultiTexCoord3;
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterF.glsl b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
new file mode 100644
index 0000000000..f8b8031ce6
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/waterF.glsl
@@ -0,0 +1,22 @@
+void water_lighting(inout vec3 diff);
+
+uniform samplerCube environmentMap;
+uniform sampler2D diffuseMap;
+uniform sampler2D bumpMap;
+
+varying vec4 specular;
+
+void main()
+{
+ vec4 depth = texture2D(diffuseMap, gl_TexCoord[0].xy);
+ vec4 diff = texture2D(bumpMap, gl_TexCoord[1].xy);
+ vec3 ref = textureCube(environmentMap, gl_TexCoord[2].xyz).rgb;
+
+ diff.rgb *= depth.rgb;
+
+ vec3 col = mix(diff.rgb, ref, specular.a)+specular.rgb*diff.rgb;
+
+ water_lighting(col.rgb);
+ gl_FragColor.rgb = col.rgb;
+ gl_FragColor.a = (gl_Color.a+depth.a)*0.5;
+}
diff --git a/indra/newview/app_settings/shaders/class1/environment/waterV.glsl b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
new file mode 100644
index 0000000000..873a6fcb34
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/environment/waterV.glsl
@@ -0,0 +1,41 @@
+void default_scatter(vec3 viewVec, vec3 lightDir);
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol);
+vec2 getScatterCoord(vec3 viewVec, vec3 lightDir);
+
+varying vec4 specular;
+
+vec4 texgen_object(vec4 vpos, vec4 tc, mat4 mat, vec4 tp0, vec4 tp1)
+{
+ vec4 tcoord;
+
+ tcoord.x = dot(vpos, tp0);
+ tcoord.y = dot(vpos, tp1);
+ tcoord.z = tc.z;
+ tcoord.w = tc.w;
+
+ tcoord = mat * tcoord;
+
+ return tcoord;
+}
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+ gl_TexCoord[1] = texgen_object(gl_Vertex, gl_MultiTexCoord1, gl_TextureMatrix[1], gl_ObjectPlaneS[1],gl_ObjectPlaneT[1]);
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+ vec4 spec = gl_Color;
+ gl_FrontColor.rgb = calcLightingSpecular(pos, norm, gl_Color, spec, vec3(0.0, 0.0, 0.0)).rgb;
+ gl_FrontColor.a = gl_Color.a;
+ specular = spec;
+ specular.a = gl_Color.a*0.5;
+ vec3 ref = reflect(pos,norm);
+
+ gl_TexCoord[2] = gl_TextureMatrix[2]*vec4(ref,1);
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
new file mode 100644
index 0000000000..1e342fb51b
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightF.glsl
@@ -0,0 +1,6 @@
+uniform sampler2D diffuseMap;
+
+void main()
+{
+ gl_FragColor = gl_Color*texture2D(diffuseMap, gl_TexCoord[0].xy);
+}
diff --git a/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
new file mode 100644
index 0000000000..bb6707b2a9
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/interface/highlightV.glsl
@@ -0,0 +1,20 @@
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ pos = normalize(pos);
+ float d = dot(pos, normalize(gl_NormalMatrix * gl_Normal));
+ d *= d;
+ d = 1.0 - d;
+ d *= d;
+
+ d = min(d, materialColor.a*2.0);
+
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+ gl_FrontColor.rgb = materialColor.rgb;
+ gl_FrontColor.a = max(d, materialColor.a);
+}
+
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
new file mode 100644
index 0000000000..b2a6d67621
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightF.glsl
@@ -0,0 +1,31 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+
+void default_lighting()
+{
+ vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
+ applyScatter(color.rgb);
+ gl_FragColor = color;
+}
+
+void alpha_lighting()
+{
+ default_lighting();
+}
+
+void water_lighting(inout vec3 diff)
+{
+ applyScatter(diff);
+}
+
+void terrain_lighting(inout vec3 color)
+{
+ color.rgb *= gl_Color.rgb;
+ applyScatter(color);
+}
+
+vec4 getLightColor()
+{
+ return gl_Color;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
new file mode 100644
index 0000000000..e3816318a1
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/lighting/lightV.glsl
@@ -0,0 +1,99 @@
+
+float calcDirectionalLight(vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcPointLight(vec3 v, vec3 n, vec3 l, float r, float pw)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = max((r-d)/r, 0.0);
+
+ //da = pow(da, pw);
+
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+
+ return da;
+}
+
+float calcDirectionalSpecular(vec3 view, vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da)
+{
+
+ specular.rgb += calcDirectionalSpecular(view,n,l)*lightCol*da;
+ return calcDirectionalLight(n,l);
+}
+
+vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp((r-d)/r, 0.0, 1.0);
+
+ //da = pow(da, pw);
+
+ //angular attenuation
+ da *= calcDirectionalLightSpecular(specular, view, n, lv, lightCol, da);
+
+ return da*lightCol;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseLight)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb + baseLight.rgb;
+
+ col.rgb += gl_LightSource[0].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[0].position.xyz);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+
+ return col;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseLight)
+{
+ return calcLighting(pos, norm, color, vec4(baseLight, 1.0));
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color)
+{
+ return calcLighting(pos, norm, color, vec3(0.0,0.0,0.0));
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol)
+{
+ specularColor.rgb = vec3(0.0, 0.0, 0.0);
+ return calcLighting(pos, norm, color, baseCol);
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol)
+{
+ return calcLightingSpecular(pos, norm, color, specularColor, vec4(baseCol, 1.0));
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
new file mode 100644
index 0000000000..ce5ab12b74
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleF.glsl
@@ -0,0 +1,6 @@
+void default_lighting();
+
+void main()
+{
+ default_lighting();
+}
diff --git a/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
new file mode 100644
index 0000000000..2aa3521931
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class1/objects/simpleV.glsl
@@ -0,0 +1,21 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ default_scatter(pos, gl_LightSource[0].position.xyz);
+
+ vec4 color = calcLighting(pos, norm, materialColor, gl_Color);
+ gl_FrontColor = color;
+
+ gl_FogFragCoord = pos.z;
+}
diff --git a/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
new file mode 100644
index 0000000000..7957eddb31
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/avatar/eyeballV.glsl
@@ -0,0 +1,23 @@
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol);
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor;
+attribute vec4 specularColor;
+
+void main()
+{
+ //transform vertex
+ gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+ gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
+
+ vec3 pos = (gl_ModelViewMatrix * gl_Vertex).xyz;
+ vec3 norm = normalize(gl_NormalMatrix * gl_Normal);
+
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+ vec4 specular = specularColor;
+ vec4 color = calcLightingSpecular(pos, norm, materialColor, specular, gl_Color.rgb);
+
+ gl_FrontColor = color;
+ gl_FogFragCoord = pos.z;
+}
+
diff --git a/indra/newview/app_settings/shaders/class2/environment/waterF.glsl b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
new file mode 100644
index 0000000000..e0e79e95ba
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/environment/waterF.glsl
@@ -0,0 +1,136 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+uniform sampler2D bumpMap;
+uniform samplerCube environmentMap; //: TEXUNIT4, // Environment map texture
+uniform sampler2D screenTex; // : TEXUNIT5
+
+uniform vec3 lightDir;
+uniform vec3 specular;
+uniform float lightExp;
+uniform vec2 fbScale;
+uniform float refScale;
+
+float msin(float x) {
+ float k = sin(x)+1.0;
+ k *= 0.5;
+ k *= k;
+ return 2.0 * k;
+}
+
+float mcos(float x) {
+ float k = cos(x)+1.0;
+ k *= 0.5;
+ k *= k;
+ return 2.0 * k;
+}
+
+float waveS(vec2 v, float t, float a, float f, vec2 d, float s, sampler1D sinMap)
+{
+ return texture1D(sinMap, (dot(d, v)*f + t*s)*f).r*a;
+}
+
+float waveC(vec2 v, float t, float a, float f, vec2 d, float s, sampler1D sinMap)
+{
+ return texture1D(sinMap, (dot(d, v)*f + t*s)*f).g*a*2.0-1.0;
+}
+
+float magnitude(vec3 vec) {
+ return sqrt(dot(vec,vec));
+}
+
+vec3 mreflect(vec3 i, vec3 n) {
+ return i + n * 2.0 * abs(dot(n,i))+vec3(0.0,0.0,0.5);
+}
+
+void main()
+{
+ vec2 texCoord = gl_TexCoord[0].xy; // Texture coordinates
+ vec2 littleWave1 = gl_TexCoord[0].zw;
+ vec2 littleWave2 = gl_TexCoord[1].xy;
+ vec2 bigWave = gl_TexCoord[1].zw;
+ vec3 viewVec = gl_TexCoord[2].xyz;
+ vec4 refCoord = gl_TexCoord[3];
+ vec4 col = gl_Color;
+ vec4 color;
+
+ //get color from alpha map (alpha denotes water depth), rgb denotes water color
+ vec4 wcol = texture2D(diffuseMap, texCoord.xy);
+
+ //store texture alpha
+ float da = wcol.a;
+
+ //modulate by incoming water color
+ //wcol.a *= refCoord.w;
+
+ //scale wcol.a (water depth) for steep transition
+ wcol.a *= wcol.a;
+
+ //normalize view vector
+ viewVec = normalize(viewVec);
+
+ //get bigwave normal
+ vec3 wavef = texture2D(bumpMap, bigWave).xyz*2.0;
+
+ vec3 view = vec3(viewVec.x, viewVec.y, viewVec.z);
+
+ float dx = 1.0-(dot(wavef*2.0-vec3(1.0), view))*da;
+ dx *= 0.274;
+
+ //get detail normals
+ vec3 dcol = texture2D(bumpMap, littleWave1+dx*view.xy).rgb*0.75;
+ dcol += texture2D(bumpMap, littleWave2+view.xy*dx*0.1).rgb*1.25;
+
+ //interpolate between big waves and little waves (big waves in deep water)
+ wavef = wavef*wcol.a + dcol*(1.0-wcol.a);
+
+ //crunch normal to range [-1,1]
+ wavef -= vec3(1,1,1);
+
+ //get base fresnel component
+ float df = dot(viewVec,wavef);
+ //reposition fresnel to latter half of [0,1]
+ df = 1.0-clamp(df,0.0,1.0);
+
+ //set output alpha based on fresnel
+ color.a = clamp((df+da)*0.5,0.0,1.0);
+
+ //calculate reflection vector
+ vec3 ref = reflect(viewVec.xyz, wavef);
+
+ //get specular component
+ float spec = clamp(dot(lightDir, normalize(ref)),0.0,1.0);
+
+ //fudge reflection to be more noisy at good angles
+ ref.z = ref.z*ref.z+df*df*0.5;
+
+ //get diffuse component
+ float diff = clamp((abs(dot(ref, wavef))),0.0,1.0)*0.9;
+
+ //fudge diffuse for extra contrast and ambience
+ diff *= diff;
+ diff += 0.4;
+
+ //set diffuse color contribution
+ color.rgb = textureCube(environmentMap, ref).rgb*diff;
+
+ //harden specular
+ spec = pow(spec, lightExp);
+
+ //add specular color contribution
+ color.rgb += spec * specular;
+
+ //figure out distortion vector (ripply)
+ vec2 distort = clamp(((refCoord.xy/refCoord.z) * 0.5 + 0.5 + wavef.xy*refScale),0.0,0.99);
+
+ //read from framebuffer (offset)
+ vec4 fb = texture2D(screenTex, distort*fbScale);
+
+ //tint by framebuffer
+ color.rgb = color.a*color.rgb + (1.0-color.a)*fb.rgb;
+
+ //apply fog
+ applyScatter(color.rgb);
+
+ gl_FragColor = color;
+}
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
new file mode 100644
index 0000000000..0ef1129253
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightF.glsl
@@ -0,0 +1,36 @@
+void applyScatter(inout vec3 color);
+
+uniform sampler2D diffuseMap;
+
+void default_lighting()
+{
+ vec4 color = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].xy);
+ applyScatter(color.rgb);
+ gl_FragColor = color;
+}
+
+void alpha_lighting()
+{
+ vec4 diff = texture2D(diffuseMap, gl_TexCoord[0].xy);
+ vec3 color = gl_Color.rgb * diff.rgb;
+ applyScatter(color);
+ gl_FragColor.rgb = color;
+ gl_FragColor.a = diff.a * gl_Color.a;
+}
+
+void water_lighting(inout vec3 diff)
+{
+ diff = (diff*0.9 + gl_Color.rgb*0.1);
+ applyScatter(diff);
+}
+
+void terrain_lighting(inout vec3 color)
+{
+ color.rgb *= gl_Color.rgb;
+ applyScatter(color);
+}
+
+vec4 getLightColor()
+{
+ return gl_Color;
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
new file mode 100644
index 0000000000..b15960dea2
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class2/lighting/lightV.glsl
@@ -0,0 +1,126 @@
+// All lights, no specular highlights
+
+float calcDirectionalLight(vec3 n, vec3 l)
+{
+ float a = max(dot(n,l),0.0);
+ return a;
+}
+
+float calcPointLight(vec3 v, vec3 n, vec4 lp, float la)
+{
+ //get light vector
+ vec3 lv = lp.xyz-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp(1.0/(la * d), 0.0, 1.0);
+
+ //angular attenuation
+ da *= calcDirectionalLight(n, lv);
+
+ return da;
+}
+
+float calcDirectionalSpecular(vec3 view, vec3 n, vec3 l)
+{
+ return pow(max(dot(reflect(view, n),l), 0.0),8.0);
+}
+
+float calcDirectionalLightSpecular(inout vec4 specular, vec3 view, vec3 n, vec3 l, vec3 lightCol, float da)
+{
+
+ specular.rgb += calcDirectionalSpecular(view,n,l)*lightCol*da;
+ return calcDirectionalLight(n,l);
+}
+
+vec3 calcPointLightSpecular(inout vec4 specular, vec3 view, vec3 v, vec3 n, vec3 l, float r, float pw, vec3 lightCol)
+{
+ //get light vector
+ vec3 lv = l-v;
+
+ //get distance
+ float d = length(lv);
+
+ //normalize light vector
+ lv *= 1.0/d;
+
+ //distance attenuation
+ float da = clamp(1.0/(r * d), 0.0, 1.0);
+
+ //angular attenuation
+
+ da *= calcDirectionalLightSpecular(specular, view, n, lv, lightCol, da);
+
+ return da*lightCol;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec4 baseLight)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb + baseLight.rgb;
+
+ col.rgb += gl_LightSource[0].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[0].position.xyz);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLight(norm, gl_LightSource[1].position.xyz);
+ col.rgb += gl_LightSource[2].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[2].position, gl_LightSource[2].linearAttenuation);
+ col.rgb += gl_LightSource[3].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[3].position, gl_LightSource[3].linearAttenuation);
+ col.rgb += gl_LightSource[4].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[4].position, gl_LightSource[4].linearAttenuation);
+ col.rgb += gl_LightSource[5].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[5].position, gl_LightSource[5].linearAttenuation);
+ col.rgb += gl_LightSource[6].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[6].position, gl_LightSource[6].linearAttenuation);
+ col.rgb += gl_LightSource[7].diffuse.rgb*calcPointLight(pos, norm, gl_LightSource[7].position, gl_LightSource[7].linearAttenuation);
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+
+ gl_FrontColor = vec4(col.rgb, col.a);
+ return col;
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseLight)
+{
+ return calcLighting(pos, norm, color, vec4(baseLight, 1.0));
+}
+
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color)
+{
+ return calcLighting(pos, norm, color, vec3(0.0,0.0,0.0));
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec4 baseCol)
+{
+ vec4 col;
+ col.a = color.a;
+
+ col.rgb = gl_LightModel.ambient.rgb;
+
+ vec3 view = normalize(pos);
+
+ vec4 specular = specularColor;
+ specularColor.rgb = vec3(0.0, 0.0, 0.0);
+
+ col.rgb += baseCol.a*gl_LightSource[0].diffuse.rgb*calcDirectionalLightSpecular(specularColor, view, norm, gl_LightSource[0].position.xyz,gl_LightSource[0].diffuse.rgb*baseCol.a, 1.0);
+ col.rgb += gl_LightSource[1].diffuse.rgb*calcDirectionalLightSpecular(specularColor, view, norm, gl_LightSource[1].position.xyz,gl_LightSource[1].diffuse.rgb, 1.0);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[2].position.xyz, gl_LightSource[2].linearAttenuation, gl_LightSource[2].quadraticAttenuation,gl_LightSource[2].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[3].position.xyz, gl_LightSource[3].linearAttenuation, gl_LightSource[3].quadraticAttenuation,gl_LightSource[3].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[4].position.xyz, gl_LightSource[4].linearAttenuation, gl_LightSource[4].quadraticAttenuation,gl_LightSource[4].diffuse.rgb);
+ col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[5].position.xyz, gl_LightSource[5].linearAttenuation, gl_LightSource[5].quadraticAttenuation,gl_LightSource[5].diffuse.rgb);
+ //col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[6].position.xyz, gl_LightSource[6].linearAttenuation, gl_LightSource[6].quadraticAttenuation,gl_LightSource[6].diffuse.rgb);
+ //col.rgb += calcPointLightSpecular(specularColor, view, pos, norm, gl_LightSource[7].position.xyz, gl_LightSource[7].linearAttenuation, gl_LightSource[7].quadraticAttenuation,gl_LightSource[7].diffuse.rgb);
+ col.rgb += baseCol.rgb;
+
+ col.rgb = min(col.rgb*color.rgb, 1.0);
+ specularColor.rgb = min(specularColor.rgb*specular.rgb, 1.0);
+
+ gl_FrontColor = vec4(col.rgb+specularColor.rgb,col.a);
+ return col;
+}
+
+vec4 calcLightingSpecular(vec3 pos, vec3 norm, vec4 color, inout vec4 specularColor, vec3 baseCol)
+{
+ return calcLightingSpecular(pos, norm, color, specularColor, vec4(baseCol, 1.0));
+}
diff --git a/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
new file mode 100644
index 0000000000..2505afea65
--- /dev/null
+++ b/indra/newview/app_settings/shaders/class3/avatar/avatarV.glsl
@@ -0,0 +1,128 @@
+vec4 calcLighting(vec3 pos, vec3 norm, vec4 color, vec3 baseCol);
+mat4 getSkinnedTransform();
+void default_scatter(vec3 viewVec, vec3 lightDir);
+
+attribute vec4 materialColor; //2
+
+attribute vec4 binormal; //6
+attribute vec4 clothing; //4
+
+attribute vec4 gWindDir; //7
+attribute vec4 gSinWaveParams; //3
+attribute vec4 gGravity; //5
+
+const vec4 gMinMaxConstants = vec4(1.0, 0.166666, 0.0083143, .00018542); // #minimax-generated coefficients
+const vec4 gPiConstants = vec4(0.159154943, 6.28318530, 3.141592653, 1.5707963); // # {1/2PI, 2PI, PI, PI/2}
+
+void main()
+{
+ gl_TexCoord[0] = gl_MultiTexCoord0;
+
+ vec4 pos;
+ mat4 trans = getSkinnedTransform();
+
+ vec3 norm;
+ norm.x = dot(trans[0].xyz, gl_Normal);
+ norm.y = dot(trans[1].xyz, gl_Normal);
+ norm.z = dot(trans[2].xyz, gl_Normal);
+ norm = normalize(norm);
+
+ vec3 binorm;
+ binorm.x = dot(trans[0].xyz, binormal.xyz);
+ binorm.y = dot(trans[1].xyz, binormal.xyz);
+ binorm.z = dot(trans[2].xyz, binormal.xyz);
+ norm = normalize(norm);
+
+ //wind
+ vec4 windEffect;
+ windEffect = vec4(dot(norm, gWindDir.xyz)); // DP3 windEffect, blendNorm, gWindDir;
+ pos.x = dot(trans[2].xyz, gl_Vertex.xyz); // DP3 blendPos.x, blendMatZ, iPos;
+ windEffect.xyz = pos.x * vec3(0.015, 0.015, 0.015)
+ + windEffect.xyz; // MAD windEffect.xyz, blendPos.x, {0.015, 0.015, 0.015, 0}, windEffect;
+ windEffect.w = windEffect.w * 2.0 + 1.0; // MAD windEffect.w, windEffect, {0, 0, 0, 2}, {0, 0, 0, 1}; # move wind offset value to [-1, 3]
+ windEffect.w = windEffect.w*gWindDir.w; // MUL windEffect.w, windEffect, gWindDir; # modulate wind strength
+
+ windEffect.xyz = windEffect.xyz*gSinWaveParams.xyz
+ +vec3(gSinWaveParams.w); // MAD windEffect.xyz, windEffect, gSinWaveParams, gSinWaveParams.w; # use sin wave params to scale and offset input
+
+
+ //reduce to period of 2 PI
+ vec4 temp1, temp0, temp2, offsetPos;
+ temp1.xyz = windEffect.xyz * gPiConstants.x; // MUL temp1.xyz, windEffect, gPiConstants.x; # change input as multiple of [0-2PI] to [0-1]
+ temp0.y = mod(temp1.x,1.0); // EXP temp0, temp1.x; # find mod(x, 1)
+ windEffect.x = temp0.y * gPiConstants.y; // MUL windEffect.x, temp0.y, gPiConstants.y; # scale from [0,1] to [0, 2PI]
+ temp1.z = temp1.z - gPiConstants.w; // ADD temp1.z, temp1.z, -gPiConstants.w; # shift normal oscillation by PI/2
+ temp0.y = mod(temp1.z,1.0); // EXP temp0, temp1.z; # find mod(x, 1)
+
+ windEffect.z = temp0.y * gPiConstants.y; // MUL windEffect.z, temp0.y, gPiConstants.y; # scale from [0,1] to [0, 2PI]
+ windEffect.xyz = windEffect.xyz + vec3(-3.141592); // # offset to [-PI, PI]
+ // ADD windEffect.xyz, windEffect, {-3.141592, -3.141592, -3.141592, -3.141592};
+
+ //calculate sinusoid
+ vec4 sinWave;
+ temp1 = windEffect*windEffect; // MUL temp1, windEffect, windEffect; # x^2
+ sinWave = -temp1 * gMinMaxConstants.w
+ + vec4(gMinMaxConstants.z); // MAD sinWave, -temp1, gMinMaxConstants.w, gMinMaxConstants.z; # y = -(x^2)/7! + 1/5!
+ sinWave = sinWave * -temp1 + vec4(gMinMaxConstants.y); // MAD sinWave, sinWave, -temp1, gMinMaxConstants.y; # y = -(x^2) * (-(x^2)/7! + 1/5!) + 1/3!
+ sinWave = sinWave * -temp1 + vec4(gMinMaxConstants.x); // MAD sinWave, sinWave, -temp1, gMinMaxConstants.x; # y = -(x^2) * (-(x^2) * (-(x^2)/7! + 1/5!) + 1/3!) + 1
+ sinWave = sinWave * windEffect; // MUL sinWave, sinWave, windEffect; # y = x * (-(x^2) * (-(x^2) * (-(x^2)/7! + 1/5!) + 1/3!) + 1)
+
+ // sinWave.x holds sin(norm . wind_direction) with primary frequency
+ // sinWave.y holds sin(norm . wind_direction) with secondary frequency
+ // sinWave.z hold cos(norm . wind_direction) with primary frequency
+ sinWave.xyz = sinWave.xyz * gWindDir.w
+ + vec3(windEffect.w); // MAD sinWave.xyz, sinWave, gWindDir.w, windEffect.w; # multiply by wind strength in gWindDir.w [-wind, wind]
+
+ // add normal facing bias offset [-wind,wind] -> [-wind - .25, wind + 1]
+ temp1 = vec4(dot(norm, gGravity.xyz)); // DP3 temp1, blendNorm, gGravity; # how much is this normal facing in direction of gGravity?
+ temp1 = min(temp1, vec4(0.2,0.0,0.0,0.0)); // MIN temp1, temp1, {0.2, 0, 0, 0}; # clamp [-1, 1] to [-1, 0.2]
+ temp1 = temp1*vec4(1.5,0.0,0.0,0.0); // MUL temp1, temp1, {1.5, 0, 0, 0}; # scale from [-1,0.2] to [-1.5, 0.3]
+ sinWave.x = sinWave.x + temp1.x; // ADD sinWave.x, sinWave, temp1; # add gGravity effect to sinwave (only primary frequency)
+ sinWave.xyz = sinWave.xyz * clothing.w; // MUL sinWave.xyz, sinWave, iClothing.w; # modulate by clothing coverage
+
+ sinWave.xyz = max(sinWave.xyz, vec3(-1.0, -1.0, -1.0)); // MAX sinWave.xyz, sinWave, {-1, -1, -1, -1}; # clamp to underlying body shape
+ offsetPos = clothing * sinWave.x; // MUL offsetPos, iClothing, sinWave.x; # multiply wind effect times clothing displacement
+ temp2 = gWindDir*sinWave.z + vec4(norm,0); // MAD temp2, gWindDir, sinWave.z, blendNorm; # calculate normal offset due to wind oscillation
+ offsetPos = vec4(1.0,1.0,1.0,0.0)*offsetPos+gl_Vertex; // MAD offsetPos, {1.0, 1.0, 1.0, 0.0}, offsetPos, iPos; # add to offset vertex position, and zero out effect from w
+ norm += temp2.xyz*2.0; // MAD blendNorm, temp2, {2, 2, 2, 2}, blendNorm; # add sin wave effect on normals (exaggerated)
+
+ //add "backlighting" effect
+ float colorAcc;
+ colorAcc = 1.0 - clothing.w; // SUB colorAcc, {1, 1, 1, 1}, iClothing;
+ norm.z -= colorAcc * 0.2; // MAD blendNorm, colorAcc.w, {0, 0, -0.2, 0}, blendNorm;
+
+ //renormalize normal (again)
+ norm = normalize(norm); // DP3 divisor.w, blendNorm, blendNorm;
+ // RSQ divisor.xyz, divisor.w;
+ // MUL blendNorm.xyz, blendNorm, divisor;
+
+ //project binormal to normal plane to ensure orthogonality
+ temp2 = vec4(dot(norm, binorm)); // DP3 temp2, blendNorm, blendBinorm;
+ binorm = binorm - temp2.xyz; // SUB blendBinorm, blendBinorm, temp2;
+
+ //renormalize binormal
+ binorm = normalize(binorm); // DP3 divisor.w, blendBinorm, blendBinorm;
+ // RSQ divisor.xyz, divisor.w;
+ // MUL blendBinorm.xyz, blendBinorm, divisor;
+
+ pos.x = dot(trans[0], offsetPos);
+ pos.y = dot(trans[1], offsetPos);
+ pos.z = dot(trans[2], offsetPos);
+ pos.w = 1.0;
+
+ vec4 color = calcLighting(pos.xyz, norm, materialColor, gl_Color.rgb);
+ gl_FrontColor = color;
+
+ gl_Position = gl_ProjectionMatrix * pos;
+
+ vec3 N = norm;
+ vec3 B = binorm;
+ vec3 T = cross(N,B);
+
+ //gl_TexCoord[1].xy = gl_MultiTexCoord0.xy + 1.0/512.0 * vec2(dot(T,gl_LightSource[0].position.xyz),
+ // dot(B,gl_LightSource[0].position.xyz));
+
+ gl_TexCoord[2] = vec4(pos.xyz, 1.0);
+ default_scatter(pos.xyz, gl_LightSource[0].position.xyz);
+
+} \ No newline at end of file
diff --git a/indra/newview/app_settings/std_bump.ini b/indra/newview/app_settings/std_bump.ini
new file mode 100644
index 0000000000..9509c357ff
--- /dev/null
+++ b/indra/newview/app_settings/std_bump.ini
@@ -0,0 +1,18 @@
+LLStandardBumpmap version 1
+
+
+woodgrain bump_woodgrain.tga
+bark bump_bark.tga
+bricks bump_bricks.tga
+checker bump_checker.tga
+concrete bump_concrete.tga
+crustytile bump_crustytile.tga
+cutstone bump_cutstone.tga
+discs bump_discs.tga
+gravel bump_gravel.tga
+petridish bump_petridish.tga
+siding bump_siding.tga
+stonetile bump_stonetile.tga
+stucco bump_stucco.tga
+suction bump_suction.tga
+weave bump_weave.tga
diff --git a/indra/newview/app_settings/trees.xml b/indra/newview/app_settings/trees.xml
new file mode 100644
index 0000000000..213ee85fc7
--- /dev/null
+++ b/indra/newview/app_settings/trees.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes"?>
+<tree_defs>
+ <tree name="Pine 1" species_id="0" texture_id="0187babf-6c0d-5891-ebed-4ecab1426683" droop="60.0" twist="5.0" branches="5.0" depth="1" scale_step="0.7" trunk_depth="6" branch_length="8.0" trunk_length="11.5" leaf_scale="22.0" billboard_scale="39.5" billboard_ratio="1.1" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="20.0" noise_mag="0.5" noise_scale="2.5" taper="0.8" repeat_z="3" />
+ <tree name="Oak" species_id="1" texture_id="8a515889-eac9-fb55-8eba-d2dc09eb32c8" droop="35.0" twist="3.0" branches="4.0" depth="3" scale_step="0.7" trunk_depth="0" branch_length="3.0" trunk_length="3.8" leaf_scale="7.0" billboard_scale="10.25" billboard_ratio="1.0" trunk_aspect="0.15" branch_aspect="0.07" leaf_rotate="0.0" noise_mag="1.2" noise_scale="4.0" taper="0.3" repeat_z="4" />
+ <tree name="Tropical Bush 1" species_id="2" texture_id="5bc11cd6-2f40-071e-a8da-0903394204f9" droop="10.0" twist="0.0" branches="6.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="7.5" billboard_scale="5.0" billboard_ratio="1.25" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Palm 1" species_id="3" texture_id="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="9.0" leaf_scale="10.0" billboard_scale="13.25" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.7" repeat_z="10" />
+ <tree name="Dogwood" species_id="4" texture_id="64367bd1-697e-b3e6-0b65-3f862a577366" droop="30.0" twist="0.0" branches="3.0" depth="2" scale_step="0.7" trunk_depth="1" branch_length="2.75" trunk_length="4.0" leaf_scale="5.5" billboard_scale="10.0" billboard_ratio="1.0" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.5" noise_scale="2.0" taper="0.8" repeat_z="3" />
+ <tree name="Tropical Bush 2" species_id="5" texture_id="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8" droop="10.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="6.0" billboard_scale="4.5" billboard_ratio="0.9" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Palm 2" species_id="6" texture_id="2d784476-d0db-9979-0cff-9408745a7cf3" droop="0.0" twist="0.0" branches="3.0" depth="1" scale_step="0.5" trunk_depth="0" branch_length="0.7" trunk_length="10.0" leaf_scale="7.5" billboard_scale="13.5" billboard_ratio="1.0" trunk_aspect="0.035" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="0.2" noise_scale="6.0" taper="0.6" repeat_z="12" />
+ <tree name="Cypress 1" species_id="7" texture_id="fb2ae204-3fd1-df33-594f-c9f882830e66" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.5" trunk_depth="0" branch_length="10.0" trunk_length="10.0" leaf_scale="70.0" billboard_scale="22.5" billboard_ratio="1.0" trunk_aspect="0.05" branch_aspect="0.03" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="6" />
+ <tree name="Cypress 2" species_id="8" texture_id="30047cec-269d-408e-0c30-b2603b887268" droop="30.0" twist="0.0" branches="3.0" depth="4" scale_step="0.6" trunk_depth="3.0" branch_length="7.5" trunk_length="10.0" leaf_scale="35.0" billboard_scale="25.0" billboard_ratio="0.8" trunk_aspect="0.05" branch_aspect="0.04" leaf_rotate="0.0" noise_mag="1.2" noise_scale="1.0" taper="0.5" repeat_z="5" />
+ <tree name="Pine 2" species_id="9" texture_id="d691a01c-13b7-578d-57c0-5caef0b4e7e1" droop="50.0" twist="7.5" branches="4.0" depth="2" scale_step="0.7" trunk_depth="6.0" branch_length="6.0" trunk_length="10.0" leaf_scale="15.5" billboard_scale="33.0" billboard_ratio="1.35" trunk_aspect="0.1" branch_aspect="0.08" leaf_rotate="5.0" noise_mag="0.5" noise_scale="2.5" taper="0.7" repeat_z="3" />
+ <tree name="Plumeria" species_id="10" texture_id="6de37e4e-7029-61f5-54b8-f5e63f983f58" droop="8.0" twist="7.0" branches="3.0" depth="2" scale_step="0.6" trunk_depth="0" branch_length="3.0" trunk_length="0.1" leaf_scale="20" billboard_scale="10.0" billboard_ratio="1.35" trunk_aspect="0.10" branch_aspect="0.075" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.85" repeat_z="2" />
+ <tree name="Winter Pine 1" species_id="11" texture_id="10d2a01a-0818-84b9-4b96-c2eb63256519" droop="90.0" twist="2.5" branches="6.0" depth="1" scale_step="0.66" trunk_depth="8" branch_length="0.0" trunk_length="4" leaf_scale="6.75" billboard_scale="12.5" billboard_ratio="0.6" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.0" noise_scale="2.5" taper="0.85" repeat_z="2" />
+ <tree name="Winter Aspen" species_id="12" texture_id="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a" droop="85.0" twist="3.0" branches="5" depth="1" scale_step="0.6" trunk_depth="8" branch_length="3.0" trunk_length="4.5" leaf_scale="8" billboard_scale="12" billboard_ratio=".675" trunk_aspect="0.06" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.75" noise_scale="2.5" taper="0.8" repeat_z="2" />
+ <tree name="Winter Pine 2" species_id="13" texture_id="67931331-0c02-4876-1255-28770896c6a2" droop="140.0" twist="5.0" branches="6.0" depth="1" scale_step="0.6" trunk_depth="7" branch_length="0.0" trunk_length="3" leaf_scale="5.0" billboard_scale="7.5" billboard_ratio="0.5" trunk_aspect="0.1" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="0.750" noise_scale="2.5" taper="0.5" repeat_z="2" />
+ <tree name="Eucalyptus" species_id="14" texture_id="a6162133-724b-54df-a12f-51cd070ad6f3" droop="20.0" twist="5.0" branches="3.6" depth="4" scale_step="0.6" trunk_depth="0" branch_length="12.0" trunk_length="8.0" leaf_scale="33.0" billboard_scale="24" billboard_ratio="1.3" trunk_aspect="0.15" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="0" noise_scale="0" taper="0.675" repeat_z="3" />
+ <tree name="Fern" species_id="15" texture_id="8872f2b8-31db-42d8-580a-b3e4a91262de" droop="12.0" twist="0.0" branches="7.0" depth="1" scale_step="0.5" trunk_depth="0.1" branch_length="0.01" trunk_length="0.0" leaf_scale="4" billboard_scale="3.5" billboard_ratio="0.85" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Eelgrass" species_id="16" texture_id="96b4de31-f4fa-337d-ec78-451e3609769e" droop="0.0" twist="0.0" branches="5.0" depth="1" scale_step="0.5" trunk_depth="1" branch_length="0.5" trunk_length="0.15" leaf_scale="5.0" billboard_scale="3.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="0.08" leaf_rotate="0.0" noise_mag="1.0" noise_scale="1.0" taper="0.2" repeat_z="1" />
+ <tree name="Sea Sword" species_id="17" texture_id="5894e2e7-ab8d-edfa-e61c-18cf16854ba3" droop="0.0" twist="0.0" branches="6.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="2.0" billboard_scale="2.0" billboard_ratio="1.0" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
+ <tree name="Kelp 1" species_id="18" texture_id="2caf1179-7861-6ff3-4b7d-46e17780bdfa" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="0.75" leaf_scale="1.85" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.04" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
+ <tree name="Beach Grass 1" species_id="19" texture_id="18fb888b-e8f1-dce7-7da7-321d651ea6b0" droop="0.0" twist="0.0" branches="4.0" depth="1" scale_step="0.7" trunk_depth="1" branch_length="0.0" trunk_length="0.0" leaf_scale="4.0" billboard_scale="2.5" billboard_ratio="1.2" trunk_aspect="1.0" branch_aspect="1.0" leaf_rotate="0.0" noise_mag="0.5" noise_scale="0.0" taper="0.0" repeat_z="1" />
+ <tree name="Kelp 2" species_id="20" texture_id="2a4880b6-b7a3-690a-2049-bfbe38eafb9f" droop="-15.0" twist="0.0" branches="1.0" depth="1" scale_step="1.0" trunk_depth="3" branch_length="2.5" trunk_length="1.35" leaf_scale="2.0" billboard_scale="4.9" billboard_ratio="1.0" trunk_aspect="0.025" branch_aspect="0.05" leaf_rotate="0.0" noise_mag="1.0" noise_scale="2.0" taper="0.8" repeat_z="2" />
+</tree_defs>
diff --git a/indra/newview/app_settings/viewerart.xml b/indra/newview/app_settings/viewerart.xml
new file mode 100644
index 0000000000..dbd22278a7
--- /dev/null
+++ b/indra/newview/app_settings/viewerart.xml
@@ -0,0 +1,504 @@
+<settings version = "101">
+ <closebox.tga value="47a8c844-cd2a-4b1a-be01-df8b1612fe5d"/>
+ <close_in_blue.tga value="e5821134-23c0-4bd0-af06-7fa95b9fb01a"/>
+ <tearoffbox.tga value="74e1a96f-4833-a24d-a1bb-1bce1468b0e7"/>
+ <tearoff_pressed.tga value="d2524c13-4ba6-af7c-e305-8ac6cc18d86a"/>
+ <resize_handle_bottom_right_blue.tga value="e3690e25-9690-4f6c-a745-e7dcd885285a"/>
+ <scrollbutton_up_out_blue.tga value="dad084d7-9a46-452a-b0ff-4b9f1cefdde9"/>
+ <scrollbutton_up_in_blue.tga value="a93abdf3-27b5-4e22-a8fa-c48216cd2e3a"/>
+ <scrollbutton_down_out_blue.tga value="b4ecdecf-5c8d-44e7-b882-17a77e88ed55"/>
+ <scrollbutton_down_in_blue.tga value="d2421bab-2eaf-4863-b8f6-5e4c52519247"/>
+ <scrollbutton_left_out_blue.tga value="43773e8d-49aa-48e0-80f3-a04715f4677a"/>
+ <scrollbutton_left_in_blue.tga value="ea137a32-6718-4d05-9c22-7d570d27b2cd"/>
+ <scrollbutton_right_out_blue.tga value="3d700d19-e708-465d-87f2-46c8c0ee7938"/>
+ <scrollbutton_right_in_blue.tga value="b749de64-e903-4c3c-ac0b-25fb6fa39cb5"/>
+ <spin_up_out_blue.tga value="56576e6e-6710-4e66-89f9-471b59122794"/>
+ <spin_up_in_blue.tga value="c8450082-96a0-4319-8090-d3ff900b4954"/>
+ <spin_down_out_blue.tga value="b6d240dd-5602-426f-b606-bbb49a30726d"/>
+ <spin_down_in_blue.tga value="a985ac71-052f-48e6-9c33-d931c813ac92"/>
+ <radio_active_false.tga value="7a1ba9b8-1047-4d1e-9cfc-bc478c80b63f"/>
+ <radio_active_true.tga value="52f09e07-5816-4052-953c-94c6c10479b7"/>
+ <radio_inactive_false.tga value="90688481-67ff-4af0-be69-4aa084bcad1e"/>
+ <radio_inactive_true.tga value="1975db39-aa29-4251-aea0-409ac09d414d"/>
+ <checkbox_enabled_false.tga value="05bb64ee-96fd-4243-b74e-f40a41bc53ba"/>
+ <checkbox_enabled_true.tga value="cf4a2ed7-1533-4686-9dde-df9a37ddca55"/>
+ <checkbox_disabled_false.tga value="7d94cb59-32a2-49bf-a516-9e5a2045f9d9"/>
+ <checkbox_disabled_true.tga value="c817c642-9abd-4236-9287-ae0513fe7d2b"/>
+ <tab_top_blue.tga value="1ed83f57-41cf-4052-a3b4-2e8bb78d8191"/>
+ <tab_top_selected_blue.tga value="16d032e8-817b-4368-8a4e-b7b947ae3889"/>
+ <tab_bottom_blue.tga value="bf0a8779-689b-48c3-bb9a-6af546366ef4"/>
+ <tab_bottom_selected_blue.tga value="c001d8fd-a869-4b6f-86a1-fdcb106df9c7"/>
+ <tab_left.tga value="1097dcb3-aef9-8152-f471-431d840ea89e"/>
+ <tab_left_selected.tga value="bea77041-5835-1661-f298-47e2d32b7a70"/>
+ <crosshairs.tga value="6e1a3980-bf2d-4274-8970-91e60d85fb52"/>
+ <move_backward_in.tga value="db11d956-5e7d-4aa5-b39d-7774d339fc5c"/>
+ <move_backward_out.tga value="3ae8bb18-ed97-4cd3-ae5c-d54bc8479fe7"/>
+ <move_down_in.tga value="b92a70b9-c841-4c94-b4b3-cee9eb460d48"/>
+ <move_down_out.tga value="b5abc9fa-9e62-4e03-bc33-82c4c1b6b689"/>
+ <move_forward_in.tga value="54197a61-f5d1-4c29-95d2-c071d08849cb"/>
+ <move_forward_out.tga value="a0eb4021-1b20-4a53-892d-8faa9265a6f5"/>
+ <move_left_in.tga value="724996f5-b956-46f6-9844-4fcfce1d5e83"/>
+ <move_left_out.tga value="82476321-0374-4c26-9567-521535ab4cd7"/>
+ <move_right_in.tga value="7eeb57d2-3f37-454d-a729-8b217b8be443"/>
+ <move_right_out.tga value="1fbe4e60-0607-44d1-a50a-032eff56ae75"/>
+ <move_turn_left_in.tga value="95463c78-aaa6-464d-892d-3a805b6bb7bf"/>
+ <move_turn_left_out.tga value="13a93910-6b44-45eb-ad3a-4d1324c59bac"/>
+ <move_turn_right_in.tga value="5e616d0d-4335-476f-9977-560bccd009da"/>
+ <move_turn_right_out.tga value="5a44fd04-f52b-4c30-8b00-4a31e27614bd"/>
+ <move_up_out.tga value="f887146d-829f-4e39-9211-cf872b78f97c"/>
+ <move_up_in.tga value="49b4b357-e430-4b56-b9e0-05b8759c3c82"/>
+ <cam_rotate_out.tga value="88745b46-da05-11d5-8ac0-0003477c4611"/>
+ <cam_rotate_in.tga value="70bf2262-3eed-4996-88ac-076907e8921d"/>
+ <cam_zoom_out.tga value="bb02e941-cb3b-4dd3-892a-6841b5de6e45"/>
+ <cam_zoom_plus_in.tga value="c7aefd32-ce13-4242-82cc-2631d44ff9d3"/>
+ <cam_zoom_minus_in.tga value="deed3f4b-93e9-4183-a3b0-a5a98a6de1bb"/>
+ <cam_tracking_out.tga value="95c4ea0e-e3c2-4904-b847-7d7676139ebb"/>
+ <cam_tracking_in.tga value="fe2fc73b-5a64-4a8e-aacc-46fa81faf96a"/>
+ <direction_arrow.tga value="586383e8-4d9b-4fba-9196-2b5938e79c2c"/>
+ <minimize.tga value="34c9398d-bb78-4643-9633-46a2fa3e9637"/>
+ <minimize_inactive.tga value="6e72abba-1378-437f-bf7a-f0c15f3e99a3"/>
+ <minimize_pressed.tga value="39801651-26cb-4926-af57-7af9352c273c"/>
+ <restore.tga value="111b39de-8928-4690-b7b2-e17d5c960277"/>
+ <restore_inactive.tga value="0eafa471-70af-4882-b8c1-40a310929744"/>
+ <restore_pressed.tga value="90a0ed5c-2e7b-4845-9958-a64a1b30f312"/>
+ <combobox_arrow.tga value="b31c1335-0e9c-4927-bc90-53277777d9c1"/>
+ <white.tga value="5748decc-f629-461c-9a36-a35a221fe21f"/>
+ <darkgray.tga value="267e26d3-e0e1-41b8-91b1-3b337102928d"/>
+ <lightgray.tga value="c520bf46-cc5d-412b-a60b-9f1bd245189f"/>
+ <eyes.tga value="6522e74d-1660-4e7f-b601-6f48c1659a77"/>
+ <hair.tga value="7ca39b4c-bd19-4699-aff7-f93fd03d3e7b"/>
+ <black.tga value="e2244626-f22f-4839-8123-1e7baddeb659"/>
+ <close_inactive_blue.tga value="779e4fa3-9b13-f74a-fba9-3886fe9c86ba"/>
+ <button_disabled_32x128.tga value="f8124d60-2875-c358-7847-2acb63e5400c"/>
+ <button_enabled_32x128.tga value="d8faf8cb-ee6e-b0b5-abd9-bde873ad3461"/>
+ <button_enabled_selected_32x128.tga value="1eddba75-b682-110a-104e-6cdcce616a25"/>
+ <button_anim_play_selected.tga value="119c37bb-24af-45fe-ae11-3a6bc3c85138"/>
+ <button_anim_pause_selected.tga value="ad65d67a-777b-fbfa-693d-4bdcfca2acca"/>
+ <button_anim_pause.tga value="db2d9c2d-0bbd-21e2-e83a-103ea2def7a8"/>
+ <button_anim_play.tga value="2a7f6738-5d82-2ff3-d419-30ed09cbb72b"/>
+ <button_anim_stop.tga value="e10c9e36-d9f6-c8b4-de96-557dccce9205"/>
+ <button_anim_stop_selected.tga value="b8c0e0aa-2771-439e-c919-d2f5dad69a1c"/>
+ <rounded_square.tga value="38ce8b3c-fb30-5c59-9926-bd643613f606"/>
+ <rounded_square_soft.tga value="4c95e6bc-fe77-9cb4-b58a-909848042c1e"/>
+ <badge_ok.tga value="211035a7-c313-378d-478c-e80bbd0fde63"/>
+ <badge_note.tga value="13f6e639-b3f9-28da-a1e6-e990a43052b6"/>
+ <badge_warn.tga value="0992d4bc-7af8-4a1f-f2e6-e6c4083b066e"/>
+ <badge_error.tga value="00c50485-8491-ab70-2ea8-43f26fd028e2"/>
+ <status_money.tga value="5863eb7a-1546-6501-533a-6061f73a36b7"/>
+ <status_health.tga value="4330e8ce-b39b-1eb8-c2ec-a97c0b3947b5"/>
+ <status_fly.tga value="0e058115-5b8f-c3d7-dcaa-9623d92885d1"/>
+ <status_build.tga value="175a6b75-45c9-c2c2-4765-bf37a3909b53"/>
+ <status_busy.tga value="beb0d821-6725-abdf-032d-1f70cdabde82"/>
+ <status_scripts.tga value="4cc1afcd-04dd-178f-e074-0f9dc730ab45"/>
+ <status_buy_currency.tga value="f43a535a-59ac-26e3-84bc-c786735fabe4"/>
+ <status_buy_currency_pressed.tga value="bfa5be70-37c7-8126-fecd-df55390954d5"/>
+ <status_buy_land.tga value="1a0edac5-3e50-fc9b-2752-70c1f69cb959"/>
+ <status_buy_land_pressed.tga value="257647b7-199f-99ff-8be9-f6753289a3aa"/>
+ <terrain_dirt.tga value="b8d3965a-ad78-bf43-699b-bff8eca6c975"/>
+ <terrain_grass.tga value="abb783e6-3e93-26c0-248a-247666855da3"/>
+ <terrain_mountain.tga value="179cdabd-398a-9b6b-1391-4dc333ba321f"/>
+ <terrain_rock.tga value="beb169c7-11ea-fff2-efe5-0f24dc881df2"/>
+ <terrain_dirt_detail.tga value="0bc58228-74a0-7e83-89bc-5c23464bcec5"/>
+ <terrain_grass_detail.tga value="63338ede-0037-c4fd-855b-015d77112fc8"/>
+ <terrain_mountain_detail.tga value="303cd381-8560-7579-23f1-f0a880799740"/>
+ <terrain_rock_detail.tga value="53a2f406-4895-1d13-d541-d2e3b86bc19c"/>
+ <square_btn_32x128.tga value="b28df901-6b8d-d31c-7903-4eb9676d4bfc"/>
+ <square_btn_selected_32x128.tga value="c48c9e95-191b-96d3-08b2-6e8ada58b651"/>
+ <tree_pine_1.tga value="0187babf-6c0d-5891-ebed-4ecab1426683"/>
+ <tree_oak.tga value="8a515889-eac9-fb55-8eba-d2dc09eb32c8"/>
+ <tree_tropical_1.tga value="5bc11cd6-2f40-071e-a8da-0903394204f9"/>
+ <tree_palm_1.tga value="ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c"/>
+ <tree_dogwood.tga value="64367bd1-697e-b3e6-0b65-3f862a577366"/>
+ <tree_tropical_2.tga value="cdd9a9fc-6d0b-f90d-8416-c72b6019bca8"/>
+ <tree_palm_2.tga value="2d784476-d0db-9979-0cff-9408745a7cf3"/>
+ <tree_cypress_1.tga value="fb2ae204-3fd1-df33-594f-c9f882830e66"/>
+ <tree_cypress_2.tga value="30047cec-269d-408e-0c30-b2603b887268"/>
+ <tree_pine_2.tga value="d691a01c-13b7-578d-57c0-5caef0b4e7e1"/>
+ <tree_plumeria.tga value="6de37e4e-7029-61f5-54b8-f5e63f983f58"/>
+ <winter_tree_aspen.tga value="7c0cf89b-44b1-1ce2-dd74-07102a98ac2a"/>
+ <winter_tree_pine_1.tga value="10d2a01a-0818-84b9-4b96-c2eb63256519"/>
+ <winter_tree_pine_2.tga value="67931331-0c02-4876-1255-28770896c6a2"/>
+ <tree_eucalyptus.tga value="a6162133-724b-54df-a12f-51cd070ad6f3"/>
+ <tree_fern.tga value="8872f2b8-31db-42d8-580a-b3e4a91262de"/>
+ <tree_eelgrass.tga value="96b4de31-f4fa-337d-ec78-451e3609769e"/>
+ <tree_sea_sword.tga value="5894e2e7-ab8d-edfa-e61c-18cf16854ba3"/>
+ <tree_kelp_1.tga value="2caf1179-7861-6ff3-4b7d-46e17780bdfa"/>
+ <tree_kelp_2.tga value="2a4880b6-b7a3-690a-2049-bfbe38eafb9f"/>
+ <tree_beach_grass_1.tga value="18fb888b-e8f1-dce7-7da7-321d651ea6b0"/>
+ <tool_dozer.tga value="d2a0d4d4-54eb-4d16-be4b-4eae43845c74"/>
+ <tool_dozer_active.tga value="d4afdbbe-1550-4b7d-91de-95731f47e8e3"/>
+ <tool_land.tga value="86fe4df4-0ecb-4382-b9ae-475925a92388"/>
+ <tool_land_active.tga value="34e60587-0791-4a07-8918-f5995fcc22a3"/>
+ <tool_zoom.tga value="27eb8829-fe65-45ed-a49a-73aac42f4b38"/>
+ <tool_zoom_active.tga value="69445f58-5c8e-44e0-9d2e-47408bb43b39"/>
+ <tool_orbit.tga value="06964fe4-033f-448a-95c9-30dc41d1be8b"/>
+ <tool_orbit_active.tga value="ee4e07db-3f72-4098-bd4c-aef34515a7bc"/>
+ <tool_pan.tga value="a32aa302-0a15-48d2-b2b1-4d69f1161173"/>
+ <tool_pan_active.tga value="24d9ad33-0b42-4eb5-99a3-659d838bc5c0"/>
+ <inv_folder_texture.tga value="743f035b-a049-43f4-16c7-7ec8daa2c481"/>
+ <inv_folder_sound.tga value="e10cb910-1e71-da47-bd12-8c53f7793714"/>
+ <inv_folder_callingcard.tga value="a3735971-e2b2-d78a-580d-d265cd8f2484"/>
+ <inv_folder_landmark.tga value="9f921155-7c8c-e276-d5ec-03ac9340584d"/>
+ <inv_folder_script.tga value="baa5c310-6a6d-cc48-51eb-65196ba31d77"/>
+ <inv_folder_object.tga value="113e5133-fd0d-ee51-4a59-9d67ca10e8a7"/>
+ <inv_folder_notecard.tga value="a9e75d84-5073-9cb7-10a9-1ca68ef5c7ba"/>
+ <inv_folder_clothing.tga value="f1427d3d-b2e8-97c4-69ab-1f36d4c0e8f0"/>
+ <inv_folder_bodypart.tga value="1fe05580-1d2f-0345-b28b-52b6e3a20e5d"/>
+ <inv_folder_trash.tga value="88ad072e-ea0b-aabd-5ac0-b37862a6eb66"/>
+ <inv_folder_plain_closed.tga value="86f00960-c3e9-9680-145d-3beffd743e9c"/>
+ <inv_folder_plain_open.tga value="d15dc243-2d0b-47af-0ce1-ec376464bdc8"/>
+ <inv_folder_snapshot.tga value="6efe85e7-800f-1843-296c-a5b7adffe091"/>
+ <inv_folder_lostandfound.tga value="9a371a04-297d-bacf-0d16-5f49753efe1d"/>
+ <inv_folder_animation.tga value="4d59b3ee-f29d-b912-2bcc-9bb1f8a07ec6"/>
+ <inv_folder_gesture.tga value="4de9129a-9fc1-d759-d739-364293906ba2"/>
+ <inv_item_texture.tga value="19f452d7-4eee-9f46-76cc-5497d17f1dd9"/>
+ <inv_item_sound.tga value="eb414d69-c77d-d4e7-66e6-6c2e6f6c1976"/>
+ <inv_item_callingcard_online.tga value="672cc53e-8dc0-ba91-2a4e-574104cf071c"/>
+ <inv_item_callingcard_offline.tga value="d0afe86b-2489-7600-55b7-6abb0a63d9f9"/>
+ <inv_item_landmark.tga value="bf25a2a0-85da-7fa0-0993-e461768d0221"/>
+ <inv_item_landmark_visited.tga value="229fac85-5428-4ab7-adeb-eb8389e91092"/>
+ <inv_item_script.tga value="59a3df81-ed76-06c9-7264-6dada535e7a3"/>
+ <inv_item_clothing.tga value="34dfe476-8e26-0e3a-11cf-76cc4a7126ce"/>
+ <inv_item_object.tga value="0f0780a0-89c4-742a-ef28-26405a41cf85"/>
+ <inv_item_notecard.tga value="23ce8a2c-9ea2-d863-6572-806f0645b0c7"/>
+ <inv_item_bodypart.tga value="d2a5362d-5c55-57dd-a9e9-5c814d1ddc16"/>
+ <inv_item_attach.tga value="5bcae41e-aa5d-02f8-edf1-605ebdd875ab"/>
+ <inv_item_snapshot.tga value="3810d584-b092-7caa-57e0-010f192b9659"/>
+ <inv_item_eyes.tga value="eaa5fd96-5c25-06ef-2280-7ef20203e167"/>
+ <inv_item_gloves.tga value="117b11cb-c04e-5081-13da-1a8846070fd0"/>
+ <inv_item_hair.tga value="6bca3bf4-ed6d-d438-63a0-2a7066d03a0b"/>
+ <inv_item_jacket.tga value="8df59386-56e0-c811-0443-840da3acb3a5"/>
+ <inv_item_pants.tga value="a87a58ca-f857-63b1-0acf-072711ed1bdb"/>
+ <inv_item_shape.tga value="4463e433-4db5-79ef-c1b0-4821b03ddb07"/>
+ <inv_item_shirt.tga value="e2ffb62b-6abc-22d6-952d-764759b4d636"/>
+ <inv_item_shoes.tga value="cf384fa5-1edd-c37c-2134-283dd4fe3396"/>
+ <inv_item_skirt.tga value="0b43f826-2abc-2944-7d72-10777a51d19b"/>
+ <inv_item_socks.tga value="22137c6d-6ec5-6eee-9a2e-2d7a9e6cbcd4"/>
+ <inv_item_underpants.tga value="2f15dc09-4385-526c-aa5d-d9d516ec7d99"/>
+ <inv_item_undershirt.tga value="f72ab629-a3ab-de0c-35c0-5285e27478ce"/>
+ <inv_item_animation.tga value="b5cda0d6-d196-ce48-63db-d04323ef8931"/>
+ <inv_item_gesture.tga value="5579245d-d5bf-5f13-46b0-8624490de24c"/>
+ <pixiesmall.tga value="168e6813-096e-07ea-97ae-fd416826f627"/>
+ <legend.tga value="ca7609c6-6ec6-32d9-332e-0d8f437ef644"/>
+ <propertyline.tga value="e3548c46-8d5e-03da-fcab-4fc36ad818bb"/>
+ <startup_logo.tga value="66864f3c-e095-d9c8-058d-d6575e6ed1b8"/>
+ <grass_texture_1.tga value="79504bf5-c3ec-0763-6563-d843de66d0a1"/>
+ <grass_texture_2.tga value="6c4727b8-ac79-ba44-3b81-f9aa887b47eb"/>
+ <grass_texture_3.tga value="99bd60a2-3250-efc9-2e39-2fbcadefbecc"/>
+ <grass_texture_4.tga value="7a2b3a4a-53c2-53ac-5716-aac7d743c020"/>
+ <undergrowth_1.tga value="8f458549-173b-23ff-d4ff-bfaa5ea2371b"/>
+ <silhouette.tga value="da5d4079-7819-6b53-d2a4-dc9929381d7d"/>
+ <avatar_thumb_bkgrnd.tga value="3a7f4f0d-be14-ee78-29e3-fc8b0b2a68d3"/>
+ <missing_asset.tga value="32dfd1c8-7ff6-5909-d983-6d4adfb4255d"/>
+ <alpha_gradient.tga value="e97cf410-8e61-7005-ec06-629eba4cd1fb"/>
+ <alpha_gradient_2d.tga value="38b86f85-2575-52a9-a531-23108d8da837"/>
+ <alpha_noise.tga value="b9e1cf8a-9660-c020-0c69-18f1ea27268a"/>
+ <alpha_sizzle.tga value="e121e2fc-7573-740f-edfd-0d45a9ba486e"/>
+ <bump_woodgrain.tga value="058c75c0-a0d5-f2f8-43f3-e9699a89c2fc"/>
+ <bump_bark.tga value="6c9fa78a-1c69-2168-325b-3e03ffa348ce"/>
+ <bump_bricks.tga value="b8eed5f0-64b7-6e12-b67f-43fa8e773440"/>
+ <bump_checker.tga value="9deab416-9c63-78d6-d558-9a156f12044c"/>
+ <bump_concrete.tga value="db9d39ec-a896-c287-1ced-64566217021e"/>
+ <bump_crustytile.tga value="f2d7b6f6-4200-1e9a-fd5b-96459e950f94"/>
+ <bump_cutstone.tga value="d9258671-868f-7511-c321-7baef9e948a4"/>
+ <bump_discs.tga value="d21e44ca-ff1c-a96e-b2ef-c0753426b7d9"/>
+ <bump_gravel.tga value="4726f13e-bd07-f2fb-feb0-bfa2ac58ab61"/>
+ <bump_petridish.tga value="e569711a-27c2-aad4-9246-0c910239a179"/>
+ <bump_siding.tga value="073c9723-540c-5449-cdd4-0e87fdc159e3"/>
+ <bump_stonetile.tga value="ae874d1a-93ef-54fb-5fd3-eb0cb156afc0"/>
+ <bump_stucco.tga value="92e66e00-f56f-598a-7997-048aa64cde18"/>
+ <bump_suction.tga value="83b77fc6-10b4-63ec-4de7-f40629f238c5"/>
+ <bump_weave.tga value="735198cf-6ea0-2550-e222-21d3c6a341ae"/>
+ <icon_avatar_online.tga value="529ed15b-3d41-dcc1-79de-90bf21770b5b"/>
+ <icon_avatar_offline.tga value="34648c67-5bfb-5790-e05e-8bd6600fd087"/>
+ <icon_event.tga value="be235ae0-53cf-1d68-b3ae-cf375ed1fb58"/>
+ <icon_event_mature.tga value="cc090999-1b3e-2e97-7a38-c9f4afd10297"/>
+ <icon_group.tga value="04237108-a879-5a95-9b0c-b18fd09bc447"/>
+ <icon_place.tga value="ba0bac4e-815e-14e1-2895-5065b8c703b3"/>
+ <icon_top_pick.tga value="77ca91a2-4431-aeaf-6249-3dd99c7dd86d"/>
+ <icon_popular.tga value="bdd47da5-5b5b-c906-37ad-16aaa64f096f"/>
+ <icon_for_sale.tga value="f20728fd-1670-3771-2293-e0dd3f0bcaab"/>
+ <icon_auction.tga value="96abf5b1-335c-6b76-61e3-74ada07f3cb8"/>
+ <icon_land_for_landless.tga value="c421ddf2-b9d7-b373-503c-f4c423f37f1c"/>
+ <icon_day_cycle.tga value="5b30a285-f1e3-92b1-dcd3-0d07366ced3e"/>
+ <icon_lock.tga value="9beb8cdd-3dce-53c2-b28e-e1f3bc2ec0a4"/>
+ <noentrylines.tga value="5d3e196b-fd4d-ada7-e4c1-99f8e9f1cfbf"/>
+ <noentrypasslines.tga value="ac8f8627-6a30-8da8-d4bd-958668eea7a0"/>
+ <notify_tip_icon.tga value="74ba3584-58ea-9984-5b76-62d37942ab77"/>
+ <notify_box_icon.tga value="b2ef2d31-9714-a07b-6ca7-31638166364b"/>
+ <notify_next.tga value="07d0ea4c-af0c-aad1-dbbf-c24020ff2b80"/>
+ <map_avatar_you_8.tga value="02fbdc40-5e07-a6e1-228b-58e10f8335b7"/>
+ <map_avatar_8.tga value="0be58a91-8065-c02b-7a12-2cc14dddbc37"/>
+ <map_avatar_16.tga value="db0dadd5-026a-88cf-f5c1-523a0a2daa3e"/>
+ <map_telehub.tga value="bf1b2bb0-13b1-40ae-3354-b1b93761bdb4"/>
+ <map_infohub.tga value="85b1a79a-7f6c-9df3-4d6c-17b1a4efb55a"/>
+ <map_home.tga value="ae9b8f5f-03a1-2e71-db77-6eb27a1ba181"/>
+ <map_event.tga value="6008be5e-9267-2a3a-9798-e81b076c22ca"/>
+ <map_event_mature.tga value="f9cdba28-a227-d613-2f16-ce06209314ae"/>
+ <map_track_8.tga value="bfdc7bf6-e2ee-1754-f4df-cc25887714ad"/>
+ <map_track_16.tga value="a3878395-ef00-a0e6-ee9a-f45ed6b9ce59"/>
+ <object_cone.tga value="c2b8c90a-7dca-26e3-1a63-7aa4a0389cf9"/>
+ <object_cone_active.tga value="cf69c64b-f19e-e1f3-a586-42fef31a23be"/>
+ <object_cube.tga value="70c747ac-1de3-a8b9-514d-101753ca6ccb"/>
+ <object_cube_active.tga value="f9c5e213-1076-7a7d-7889-52388aad2c1a"/>
+ <object_cylinder.tga value="13e35d95-5f6c-9a91-1766-49dedf9b1267"/>
+ <object_cylinder_active.tga value="3f3e4932-8412-e2a7-cfe9-92caf5978b1b"/>
+ <object_grass.tga value="7ca8e672-920b-4653-3970-1abc91abef58"/>
+ <object_grass_active.tga value="d0fc7cc9-646a-6860-cf7c-1d9e58cd6dab"/>
+ <object_hemi_cone.tga value="69d5e60c-739a-40b1-b526-84072121e394"/>
+ <object_hemi_cone_active.tga value="2e0c5435-95bb-1c0d-5da1-42336fb1cfc0"/>
+ <object_hemi_cylinder.tga value="f4be3e06-24a8-f86e-acc7-7daefc0572b7"/>
+ <object_hemi_cylinder_active.tga value="67279486-cfc1-3633-de42-85db65db373c"/>
+ <object_hemi_sphere.tga value="b67251ab-1716-b9fb-f911-967ba3fe027b"/>
+ <object_hemi_sphere_active.tga value="6c489466-3058-6475-6b1b-e5fc1d49f1f3"/>
+ <object_pyramid.tga value="9dde8b56-2cc4-a932-b63a-38c3a83221ad"/>
+ <object_pyramid_active.tga value="e7217b1a-e3d8-e339-d28a-d7714d0b5bee"/>
+ <object_sphere.tga value="7fa122c0-b994-460e-8636-cdc451d67268"/>
+ <object_sphere_active.tga value="f2c3bcbc-2904-41a5-1c22-688f176fd1ee"/>
+ <object_tetrahedron.tga value="e17db404-9fc5-9534-1038-777c82b2771f"/>
+ <object_tetrahedron_active.tga value="2792ea3b-c052-85fe-d168-a62b2f4e9d7c"/>
+ <object_tree.tga value="710d1bec-fb33-28f1-e77e-ddbb5b51f5ed"/>
+ <object_tree_active.tga value="da4835c7-b12a-41dd-11db-dae452f040c2"/>
+ <object_prism.tga value="02935f3a-dcda-3b42-1874-da89d4c12870"/>
+ <object_prism_active.tga value="223aac97-bd2f-ec2e-ad45-5641b77c78f9"/>
+ <object_torus.tga value="19e1f4c9-6aa6-4414-981d-59a1343a6472"/>
+ <object_torus_active.tga value="ef2bca77-5004-4547-b00a-3b96e463f89f"/>
+ <object_tube.tga value="7ce81316-a478-480f-961c-435fcbdecaf0"/>
+ <object_tube_active.tga value="55c3e4d1-cfdc-48a8-af32-a34844b91832"/>
+ <object_ring_active.tga value="2c955a73-fa31-237b-a4a1-5c8ede3bae50"/>
+ <object_ring.tga value="a7610e41-4647-16d8-0e0e-85a1211c1596"/>
+ <container_animation.tga value="c4e657a1-4c86-0159-2da0-32ff948484e6"/>
+ <container_bodypart.tga value="770cb2df-758d-34d5-36c7-e3de06db5b5d"/>
+ <container_clothing.tga value="dd90406f-4c8f-a3f9-41df-d562f94f09e0"/>
+ <container_gesture.tga value="59cd31c0-2791-3c48-f740-f0a36c68653e"/>
+ <container_landmark.tga value="24c63386-04f7-ce6f-4ff2-dfb215d2e21f"/>
+ <container_many_things.tga value="849d3292-d9fa-7186-5465-dd7b5fc1ec48"/>
+ <container_object.tga value="ad887ae1-2bee-f2c9-6786-5599de3c95c4"/>
+ <container_script.tga value="b93bd494-c4bd-bcdf-4a59-35a9497d03f3"/>
+ <container_sound.tga value="5ddea031-cfa3-2776-43e3-c7146c1b4cd6"/>
+ <container_texture.tga value="b3f95caf-bd62-bef3-0ded-dea752920629"/>
+ <avatar_aim_l_bow.bvh value="46bb4359-de38-4ed8-6a22-f1f52fe8f506"/>
+ <avatar_aim_r_bazooka.bvh value="b5b4a67d-0aee-30d2-72cd-77b333e932ef"/>
+ <avatar_aim_r_handgun.bvh value="3147d815-6338-b932-f011-16b56d9ac18b"/>
+ <avatar_aim_r_rifle.bvh value="ea633413-8006-180a-c3ba-96dd1d756720"/>
+ <avatar_angry_fingerwag.bvh value="c1bc7f36-3ba0-d844-f93c-93be945d644f"/>
+ <avatar_angry_tantrum.bvh value="11000694-3f41-adc2-606b-eee1d66f3724"/>
+ <avatar_away.bvh value="fd037134-85d4-f241-72c6-4f42164fedee"/>
+ <avatar_backflip.bvh value="c4ca6188-9127-4f31-0158-23c4e2f93304"/>
+ <avatar_blowkiss.bvh value="db84829b-462c-ee83-1e27-9bbee66bd624"/>
+ <avatar_bow.bvh value="82e99230-c906-1403-4d9c-3889dd98daba"/>
+ <avatar_brush.bvh value="349a3801-54f9-bf2c-3bd0-1ac89772af01"/>
+ <avatar_clap.bvh value="9b0c1c4e-8ac7-7969-1494-28c874c4f668"/>
+ <avatar_courtbow.bvh value="9ba1c942-08be-e43a-fb29-16ad440efc50"/>
+ <avatar_crouch.bvh value="201f3fdf-cb1f-dbec-201f-7333e328ae7c"/>
+ <avatar_crouchwalk.bvh value="47f5f6fb-22e5-ae44-f871-73aaaf4a6022"/>
+ <avatar_dance1.bvh value="b68a3d7c-de9e-fc87-eec8-543d787e5b0d"/>
+ <avatar_dance2.bvh value="928cae18-e31d-76fd-9cc9-2f55160ff818"/>
+ <avatar_dance3.bvh value="30047778-10ea-1af7-6881-4db7a3a5a114"/>
+ <avatar_dance4.bvh value="951469f4-c7b2-c818-9dee-ad7eea8c30b7"/>
+ <avatar_dance5.bvh value="4bd69a1d-1114-a0b4-625f-84e0a5237155"/>
+ <avatar_dance6.bvh value="cd28b69b-9c95-bb78-3f94-8d605ff1bb12"/>
+ <avatar_dance7.bvh value="a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6"/>
+ <avatar_dance8.bvh value="b0dc417c-1f11-af36-2e80-7e7489fa7cdc"/>
+ <avatar_dead.bvh value="57abaae6-1d17-7b1b-5f98-6d11a6411276"/>
+ <avatar_drink.bvh value="0f86e355-dd31-a61c-fdb0-3a96b9aad05f"/>
+ <avatar_express_afraid.bvh value="6b61c8e8-4747-0d75-12d7-e49ff207a4ca"/>
+ <avatar_express_anger.bvh value="5747a48e-073e-c331-f6f3-7c2149613d3e"/>
+ <avatar_express_bored.bvh value="b906c4ba-703b-1940-32a3-0c7f7d791510"/>
+ <avatar_express_cry.bvh value="92624d3e-1068-f1aa-a5ec-8244585193ed"/>
+ <avatar_express_embarrased.bvh value="514af488-9051-044a-b3fc-d4dbf76377c6"/>
+ <avatar_express_laugh.bvh value="18b3a4b5-b463-bd48-e4b6-71eaac76c515"/>
+ <avatar_express_repulsed.bvh value="36f81a92-f076-5893-dc4b-7c3795e487cf"/>
+ <avatar_express_sad.bvh value="0eb702e2-cc5a-9a88-56a5-661a55c0676a"/>
+ <avatar_express_shrug.bvh value="70ea714f-3a97-d742-1b01-590a8fcd1db5"/>
+ <avatar_express_surprise.bvh value="313b9881-4302-73c0-c7d0-0e7a36b6c224"/>
+ <avatar_express_wink.bvh value="869ecdad-a44b-671e-3266-56aef2e3ac2e"/>
+ <avatar_express_worry.bvh value="9f496bd2-589a-709f-16cc-69bf7df1d36c"/>
+ <avatar_falldown.bvh value="666307d9-a860-572d-6fd4-c3ab8865c094"/>
+ <avatar_female_walk.bvh value="f5fc7433-043d-e819-8298-f519a119b688"/>
+ <avatar_fist_pump.bvh value="7db00ccd-f380-f3ee-439d-61968ec69c8a"/>
+ <avatar_fly.bvh value="aec4610c-757f-bc4e-c092-c6e9caf18daf"/>
+ <avatar_flyslow.bvh value="2b5a38b2-5e00-3a97-a495-4c826bc443e6"/>
+ <avatar_hello.bvh value="9b29cd61-c45b-5689-ded2-91756b8d76a9"/>
+ <avatar_hold_l_bow.bvh value="8b102617-bcba-037b-86c1-b76219f90c88"/>
+ <avatar_hold_r_bazooka.bvh value="ef62d355-c815-4816-2474-b1acc21094a6"/>
+ <avatar_hold_r_handgun.bvh value="efdc1727-8b8a-c800-4077-975fc27ee2f2"/>
+ <avatar_hold_r_rifle.bvh value="3d94bad0-c55b-7dcc-8763-033c59405d33"/>
+ <avatar_hover.bvh value="4ae8016b-31b9-03bb-c401-b1ea941db41d"/>
+ <avatar_hover_down.bvh value="20f063ea-8306-2562-0b07-5c853b37b31e"/>
+ <avatar_hover_up.bvh value="62c5de58-cb33-5743-3d07-9e4cd4352864"/>
+ <avatar_impatient.bvh value="5ea3991f-c293-392e-6860-91dfa01278a3"/>
+ <avatar_jump.bvh value="2305bd75-1ca9-b03b-1faa-b176b8a8c49e"/>
+ <avatar_jumpforjoy.bvh value="709ea28e-1573-c023-8bf8-520c8bc637fa"/>
+ <avatar_kick_roundhouse_r.bvh value="49aea43b-5ac3-8a44-b595-96100af0beda"/>
+ <avatar_kissmybutt.bvh value="19999406-3a3a-d58c-a2ac-d72e555dcf51"/>
+ <avatar_land.bvh value="7a17b059-12b2-41b1-570a-186368b6aa6f"/>
+ <avatar_laugh_short.bvh value="ca5b3f14-3194-7a2b-c894-aa699b718d1f"/>
+ <avatar_motorcycle_sit.bvh value="08464f78-3a8e-2944-cba5-0c94aff3af29"/>
+ <avatar_musclebeach.bvh value="315c3a41-a5f3-0ba4-27da-f893f769e69b"/>
+ <avatar_no_head.bvh value="5a977ed9-7f72-44e9-4c4c-6e913df8ae74"/>
+ <avatar_no_unhappy.bvh value="d83fa0e5-97ed-7eb2-e798-7bd006215cb4"/>
+ <avatar_nyanya.bvh value="f061723d-0a18-754f-66ee-29a44795a32f"/>
+ <avatar_peace.bvh value="b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9"/>
+ <avatar_point_me.bvh value="17c024cc-eef2-f6a0-3527-9869876d7752"/>
+ <avatar_point_you.bvh value="ec952cca-61ef-aa3b-2789-4d1344f016de"/>
+ <avatar_prejump.bvh value="7a4e87fe-de39-6fcb-6223-024b00893244"/>
+ <avatar_punch_l.bvh value="f3300ad9-3462-1d07-2044-0fef80062da0"/>
+ <avatar_punch_onetwo.bvh value="eefc79be-daae-a239-8c04-890f5d23654a"/>
+ <avatar_punch_r.bvh value="c8e42d32-7310-6906-c903-cab5d4a34656"/>
+ <avatar_rps_countdown.bvh value="35db4f7e-28c2-6679-cea9-3ee108f7fc7f"/>
+ <avatar_rps_paper.bvh value="0836b67f-7f7b-f37b-c00a-460dc1521f5a"/>
+ <avatar_rps_rock.bvh value="42dd95d5-0bc6-6392-f650-777304946c0f"/>
+ <avatar_rps_scissors.bvh value="16803a9f-5140-e042-4d7b-d28ba247c325"/>
+ <avatar_run.bvh value="05ddbff8-aaa9-92a1-2b74-8fe77a29b445"/>
+ <avatar_salute.bvh value="cd7668a6-7011-d7e2-ead8-fc69eff1a104"/>
+ <avatar_shoot_l_bow.bvh value="e04d450d-fdb5-0432-fd68-818aaf5935f8"/>
+ <avatar_shout.bvh value="6bd01860-4ebd-127a-bb3d-d1427e8e0c42"/>
+ <avatar_sit.bvh value="1a5fe8ac-a804-8a5d-7cbd-56bd83184568"/>
+ <avatar_sit_female.bvh value="b1709c8d-ecd3-54a1-4f28-d55ac0840782"/>
+ <avatar_sit_generic.bvh value="245f3c54-f1c0-bf2e-811f-46d8eeb386e7"/>
+ <avatar_sit_ground.bvh value="1c7600d6-661f-b87b-efe2-d7421eb93c86"/>
+ <avatar_sit_ground_constrained.bvh value="1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e"/>
+ <avatar_sit_to_stand.bvh value="a8dee56f-2eae-9e7a-05a2-6fb92b97e21e"/>
+ <avatar_sleep.bvh value="f2bed5f9-9d44-39af-b0cd-257b2a17fe40"/>
+ <avatar_smoke_idle.bvh value="d2f2ee58-8ad1-06c9-d8d3-3827ba31567a"/>
+ <avatar_smoke_inhale.bvh value="6802d553-49da-0778-9f85-1599a2266526"/>
+ <avatar_smoke_throw_down.bvh value="0a9fb970-8b44-9114-d3a9-bf69cfe804d6"/>
+ <avatar_snapshot.bvh value="eae8905b-271a-99e2-4c0e-31106afd100c"/>
+ <avatar_soft_land.bvh value="f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57"/>
+ <avatar_stand.bvh value="2408fe9e-df1d-1d7d-f4ff-1384fa7b350f"/>
+ <avatar_stand_1.bvh value="15468e00-3400-bb66-cecc-646d7c14458e"/>
+ <avatar_stand_2.bvh value="370f3a20-6ca6-9971-848c-9a01bc42ae3c"/>
+ <avatar_stand_3.bvh value="42b46214-4b44-79ae-deb8-0df61424ff4b"/>
+ <avatar_stand_4.bvh value="f22fed8b-a5ed-2c93-64d5-bdd8b93c889f"/>
+ <avatar_standup.bvh value="3da1d753-028a-5446-24f3-9c9b856d9422"/>
+ <avatar_stretch.bvh value="80700431-74ec-a008-14f8-77575e73693f"/>
+ <avatar_stride.bvh value="1cb562b0-ba21-2202-efb3-30f82cdf9595"/>
+ <avatar_surf.bvh value="41426836-7437-7e89-025d-0aa4d10f1d69"/>
+ <avatar_sword_strike_r.bvh value="85428680-6bf9-3e64-b489-6f81087c24bd"/>
+ <avatar_talk.bvh value="5c682a95-6da4-a463-0bf6-0f5b7be129d1"/>
+ <avatar_throw_r.bvh value="aa134404-7dac-7aca-2cba-435f9db875ca"/>
+ <avatar_tryon_shirt.bvh value="83ff59fe-2346-f236-9009-4e3608af64c1"/>
+ <avatar_turn_180.bvh value="038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53"/>
+ <avatar_turnback_180.bvh value="6883a61a-b27b-5914-a61e-dda118a9ee2c"/>
+ <avatar_turnleft.bvh value="56e0ba0d-4a9f-7f27-6117-32f2ebbf6135"/>
+ <avatar_turnright.bvh value="2d6daa51-3192-6794-8e2e-a15f8338ec30"/>
+ <avatar_type.bvh value="c541c47f-e0c0-058b-ad1a-d6ae3a4584d9"/>
+ <avatar_walk.bvh value="6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0"/>
+ <avatar_whisper.bvh value="7693f268-06c7-ea71-fa21-2b30d6533f8f"/>
+ <avatar_whistle.bvh value="b1ed7982-c68e-a982-7561-52a88a5298c0"/>
+ <avatar_wink_hollywood.bvh value="c0c4030f-c02b-49de-24ba-2331f43fe41c"/>
+ <avatar_yes_happy.bvh value="b8c8b2a3-9008-1771-3bfc-90924955ab2d"/>
+ <avatar_yes_head.bvh value="15dd911d-be82-2856-26db-27659b142875"/>
+ <avatar_yoga_float.bvh value="42ecd00b-9947-a97c-400a-bbc9174c7aeb"/>
+ <fringe.tga value="8ac54e9d-ec09-d804-60ab-47404a9b4a36"/>
+ <foot_shadow.tga value="14e8a47d-1055-0a68-5d55-eafd9ad3da5b"/>
+ <img_smoke_poof.tga value="c734da52-f2ba-f0ba-d59e-15ea49f3d5e9"/>
+ <img_shot.tga value="173b05c7-53a9-4cf8-ce6b-5eec21c5c63f"/>
+ <folder_arrow.tga value="09a324a8-acc1-d9cd-2cbd-7465d90d3a98"/>
+ <color_swatch_alpha.tga value="f13db22f-c55c-8bdf-7b1c-221e56fde253"/>
+ <script_error.tga value="e5a0ec29-f59e-d29e-2c59-ed66c187c26c"/>
+ <status_script_debug.tga value="7775b5cc-93a5-6efd-0d9b-4e079afac217"/>
+ <water_normal.tga value="822ded49-9a6c-f61c-cb89-6df54f42cdf4"/>
+ <icon_groupnotice.tga value="21579c81-a85e-f11c-2d80-33a4c007d88c"/>
+ <icon_groupnoticeinventory.tga value="8fcca699-08e7-3d58-2f05-86c9d52bbe82"/>
+ <tab_background_lightgrey.tga value="c769e547-c307-43ca-2b6a-51cad6d1c527"/>
+ <tab_background_purple.tga value="0ae8a2e9-aff4-249c-fc4a-0f41f89847dd"/>
+ <tab_background_darkpurple.tga value="38ff4f7e-3078-a749-8302-d6cc94b404c4"/>
+ <smicon_warn.tga value="f47c17a3-8bfb-3c9f-22b8-77923de7eed9"/>
+ <uv_test1.tga value="f43b75f5-9aa5-18ec-d5d9-e6d1b8442613"/>
+ <uv_test2.tga value="300ce95f-3d3f-7c1a-3a22-3fc48f873fb9"/>
+ <eye_button_active.tga value="2b42b375-f9b4-788e-46c7-7ef38762d0ba"/>
+ <eye_button_inactive.tga value="be1b7225-98b5-eb2a-2c86-ddaae3328a6e"/>
+ <account_id_blue.tga value="6ab9179a-7308-58db-6c9d-893d3b52bece"/>
+ <account_id_orange.tga value="fbe89371-1251-4e77-d2d8-8eeccffe3ca8"/>
+ <account_id_green.tga value="3bf64d5a-38d3-b752-cf52-3d9f8fca353a"/>
+ <status_push.tga value="07d1f523-e327-4d10-20d6-8bc22a6e8f56"/>
+ <ff_visible_online.tga value="d609a41f-34c0-7aae-b2c6-2fc3ab26d916"/>
+ <ff_visible_map.tga value="20b52706-c1ab-414a-9dea-1cb788ad5689"/>
+ <ff_edit_mine.tga value="1baee0b9-4b89-39eb-8815-866d82300ab5"/>
+ <ff_edit_theirs.tga value="32e981cd-4700-da5a-7fc7-d573ec3742f4"/>
+ <inv_item_script_dangerous.tga value="0b502db8-6fcd-c442-ecfe-483a0dce875e"/>
+ <ff_visible_map_button.tga value="c1079bef-5cf9-90f3-6dcd-48989851c252"/>
+ <ff_visible_online_button.tga value="36749b47-93d6-2c5e-7ebd-d38d30311163"/>
+ <ff_edit_theirs_button.tga value="ca229f65-d7e0-133e-1bc2-674abc33f3d5"/>
+ <ff_edit_mine_button.tga value="57f05b46-63d8-c3d5-66d6-8b915746b956"/>
+ <ff_online_status_button.tga value="3b1b6a53-9c8c-568a-22c5-2a8f3e5286f5"/>
+ <oi_hud_cen_0_0.tga value="3c650257-9caf-7cad-b26c-84c9eca560f1"/>
+ <oi_hud_intro.tga value="7611fb3d-9ff2-abd3-d98f-805c1c87e757"/>
+ <oi_hud_underwater.tga value="cde61aea-83c2-3001-d598-6b348f7a8e0b"/>
+ <oi_hud_got_passport.tga value="1271838d-d777-b811-7c4c-2a00308bd80a"/>
+ <oi_hud_texture_off_edge.tga value="852be205-b1ea-6356-58c8-8c5ee5a841a6"/>
+ <oi_hud_texture_on_edge.tga value="ab11e6ff-a732-be70-67df-c43131274562"/>
+ <oi_hud_flyingabovewater.tga value="c9d150d6-2739-5f8b-cce6-3cf98242920a"/>
+ <oi_hud_walkingabovewater.tga value="78284eeb-05f3-ff25-11a0-3cc9dbb30f0c"/>
+ <oi_hud_landmark.tga value="6cd9c221-9d42-a283-256b-09a113a87271"/>
+ <oi_hud_cus_5_3.tga value="7c12f4fb-f502-26d1-a2f3-cdb6aff61663"/>
+ <oi_hud_cus_5_2.tga value="c52c9c94-adc0-0f4e-6658-ed33d6ea8829"/>
+ <oi_hud_cus_5_1.tga value="9f6d5d11-6ca9-608c-e8a6-b77989350292"/>
+ <oi_hud_cus_5_0.tga value="2000cff1-119f-2023-66c0-ac5630d2f96e"/>
+ <oi_hud_cus_4_5.tga value="f302a935-ccd1-e2f5-3a38-e185cc262f3a"/>
+ <oi_hud_cus_4_3.tga value="af8d5b3c-b40f-cea5-b0b2-440fbd84a11a"/>
+ <oi_hud_cus_4_2.tga value="11b26901-8207-12bc-5224-10a12ac4c651"/>
+ <oi_hud_cus_4_1.tga value="41baadb7-1b94-907e-9443-54e92bba77cd"/>
+ <oi_hud_cus_4_0.tga value="9d627f8e-092c-5d32-6c12-ef76ab81cedc"/>
+ <oi_hud_cus_3_4.tga value="b196486e-d0d2-4fd7-529a-c84b4495fc74"/>
+ <oi_hud_cus_3_2.tga value="0b81c4bb-de33-e493-7bcb-e7221d97e5e7"/>
+ <oi_hud_cus_3_1.tga value="436dab74-25ae-8b60-c648-50663b7faa1d"/>
+ <oi_hud_cus_3_0.tga value="6c1594de-1e66-273c-a2ab-8f0ffa8b4633"/>
+ <oi_hud_cus_2_4.tga value="bb31fe48-8566-eec0-e96b-64025f832b63"/>
+ <oi_hud_cus_2_2.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
+ <oi_hud_cus_2_1.tga value="c946959a-26ae-eb66-efa0-20154057789d"/>
+ <oi_hud_cus_2_0.tga value="d7833106-b4a8-7666-bde1-64886de289f9"/>
+ <oi_hud_cus_1_0.tga value="811ded22-5940-940c-4821-6fbbfb6611d6"/>
+ <oi_hud_cus_1_1.tga value="eda8513b-a343-5109-1fd6-f1c7ad89b703"/>
+ <oi_hud_cus_1_2.tga value="7a4ce18c-e715-34d4-dfee-704c270a8ac8"/>
+ <oi_hud_cus_1_4.tga value="d3771c15-ac03-b762-b992-d9fd2fedf38a"/>
+ <oi_hud_com_4_4.tga value="d9e1e90d-3cc3-6269-128e-67f7a2b32d26"/>
+ <oi_hud_com_4_2.tga value="0f649a26-6fdb-c73b-ffac-e50fc311d5ce"/>
+ <oi_hud_com_4_1.tga value="ae5b1ce6-a2d2-22d2-f532-6280b3bc6adb"/>
+ <oi_hud_com_4_0.tga value="12cda3a0-58c7-dfa8-7f9b-380e5bb8baf9"/>
+ <oi_hud_com_3_4.tga value="ff326257-0530-356a-e0f8-be535044e540"/>
+ <oi_hud_com_3_2.tga value="66740ddb-1d56-89f9-f0c9-ae5eb7bb9537"/>
+ <oi_hud_com_3_1.tga value="55d662f4-6a28-6388-7c75-af1c9fd33055"/>
+ <oi_hud_com_3_0.tga value="de9d318f-b69e-82f9-0c61-43b868c5ca6b"/>
+ <oi_hud_com_2_4.tga value="01d47e68-400a-d0e1-afb7-d6806d1d477e"/>
+ <oi_hud_com_2_0.tga value="09c98850-27d4-6a12-abae-4af4bba23b6b"/>
+ <oi_hud_com_1_3.tga value="5c2049b9-f797-6608-ca71-758f3716aa90"/>
+ <oi_hud_com_1_1.tga value="1116ff68-cdc4-1cfc-e137-30f8426afeda"/>
+ <oi_hud_com_1_0.tga value="bd847d31-f5af-95f7-2b9c-af47d8ba53bd"/>
+ <oi_hud_nav_4_5.tga value="66194280-b087-db94-35d9-41e8f7518515"/>
+ <oi_hud_nav_4_4.tga value="180c4241-e309-4c05-13ee-9080ab69498d"/>
+ <oi_hud_nav_4_3.tga value="e98a6ba6-99c6-fa15-84b6-9afadea6c467"/>
+ <oi_hud_nav_4_2.tga value="2e19f352-1893-59a9-949b-4d2cfd3a8222"/>
+ <oi_hud_nav_4_1.tga value="13a1675b-fb5a-19b3-b5a3-74b0a6765f7d"/>
+ <oi_hud_nav_4_0.tga value="e7526e8d-b085-b26c-b0ae-2708ec231401"/>
+ <oi_hud_nav_3_5.tga value="5e67b0d0-29a2-6a08-c85e-b12d59e53d6e"/>
+ <oi_hud_nav_3_4.tga value="2ed8fbc2-5c4d-53c2-b289-88baffceab1a"/>
+ <oi_hud_nav_3_3.tga value="e0a72f1a-282e-1c1a-2cb7-6423feb41759"/>
+ <oi_hud_nav_3_2.tga value="4bcebb23-da5e-47d9-eac1-e4453f762c8c"/>
+ <oi_hud_nav_3_1.tga value="6ac87575-330e-3a2d-3b80-a34e7b277e50"/>
+ <oi_hud_nav_3_0.tga value="f1451e8e-7310-9152-47d5-5d037c28fef3"/>
+ <oi_hud_nav_2_6.tga value="c60b42ff-ee60-98e4-e603-ca2470141d4b"/>
+ <oi_hud_nav_2_5.tga value="a02b5a1a-bbdb-5556-ae5b-a2e68494755a"/>
+ <oi_hud_nav_2_4.tga value="625535ab-8abf-b3e7-48fb-43f728b77c79"/>
+ <oi_hud_nav_2_3.tga value="00a609c3-5750-3b5a-3ce3-458bdf632203"/>
+ <oi_hud_nav_2_2.tga value="94903387-d37f-092c-e4d2-c190f68577b8"/>
+ <oi_hud_nav_2_1.tga value="ee0cd82c-6ce8-8e73-307b-6d0dc77b19e8"/>
+ <oi_hud_nav_2_0.tga value="3e10b379-ed2c-7424-1fe7-bef3558c7536"/>
+ <oi_hud_nav_1_4.tga value="bf8d0be8-2012-1664-3ea5-e69a71c206e9"/>
+ <oi_hud_nav_1_2.tga value="72100f87-18a7-fc4a-4793-de281e8b02cc"/>
+ <oi_hud_nav_1_1.tga value="b048faf3-60ce-c3a2-d034-36613449d377"/>
+ <oi_hud_nav_1_0.tga value="0ad45106-3b26-6448-0b90-feae8bd46c38"/>
+ <oi_hud_mov_4_5.tga value="7c4a45c2-37dd-312c-c6ab-20896dd0a5a6"/>
+ <oi_hud_mov_4_3.tga value="8a88da1c-3735-c71e-d48a-016df0798de4"/>
+ <oi_hud_mov_4_2.tga value="f55ae4d3-7d6a-e6ac-4cf7-03014ce14390"/>
+ <oi_hud_mov_4_1.tga value="1cc3fcf1-35c0-e222-27d2-6905cf5c4cee"/>
+ <oi_hud_mov_4_0.tga value="1ae592dc-46f4-616e-b7c6-0dff3e6f40e5"/>
+ <oi_hud_mov_3_4.tga value="831b39be-99fc-45bd-ba85-708f9dc93bfd"/>
+ <oi_hud_mov_3_2.tga value="9f7e7373-92a9-d66a-ad5a-afb55ca6ac1f"/>
+ <oi_hud_mov_3_1.tga value="ab37ed0d-7e66-1f77-3acf-b0fe4b74dbe8"/>
+ <oi_hud_mov_3_0.tga value="f5ff1f08-4c92-8606-1854-cc5b9d3e445c"/>
+ <oi_hud_mov_1_2.tga value="1e3abeed-e893-c44e-1f9d-5ecc76d21e5d"/>
+ <oi_hud_mov_1_0.tga value="e300fc95-aa94-8e31-c501-ce903cac8b7c"/>
+</settings> \ No newline at end of file
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif
new file mode 100644
index 0000000000..a20893b1ee
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROW.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif
new file mode 100644
index 0000000000..ab84bfbcc5
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROWDRAG.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif
new file mode 100644
index 0000000000..400ae42943
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_ARROWLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif
new file mode 100644
index 0000000000..1cae801ee3
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_GRABLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_NO.tif b/indra/newview/cursors_mac/UI_CURSOR_NO.tif
new file mode 100644
index 0000000000..bc4a7a75e2
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_NO.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif b/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif
new file mode 100644
index 0000000000..62332fc956
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_NOLOCKED.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif
new file mode 100644
index 0000000000..de2c28bbbc
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENESW.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif
new file mode 100644
index 0000000000..2676c78a4f
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENS.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif
new file mode 100644
index 0000000000..33df3392f3
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZENWSE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif b/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif
new file mode 100644
index 0000000000..8952054c23
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_SIZEWE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif
new file mode 100644
index 0000000000..39d89987aa
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLBUY.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif
new file mode 100644
index 0000000000..bbf415a2f1
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLCAMERA.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif
new file mode 100644
index 0000000000..2ab71c8af6
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLCREATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif
new file mode 100644
index 0000000000..db4ca17de9
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLFOCUS.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif
new file mode 100644
index 0000000000..366630d6b2
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLGRAB.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif
new file mode 100644
index 0000000000..ad5a9ca06d
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLLAND.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif
new file mode 100644
index 0000000000..864c8dee7a
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLOPEN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif
new file mode 100644
index 0000000000..0a89f9bd91
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLPAN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif
new file mode 100644
index 0000000000..55317f19aa
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLPICKOBJECT3.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif
new file mode 100644
index 0000000000..98881561cb
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLROTATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif
new file mode 100644
index 0000000000..316dd38c69
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLSCALE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif
new file mode 100644
index 0000000000..d6d835a90b
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLSIT.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif
new file mode 100644
index 0000000000..0cd06379e8
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLTRANSLATE.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif
new file mode 100644
index 0000000000..65f3b6de28
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_TOOLZOOMIN.tif
Binary files differ
diff --git a/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif b/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif
new file mode 100644
index 0000000000..c49f16a1c4
--- /dev/null
+++ b/indra/newview/cursors_mac/UI_CURSOR_WORKING.tif
Binary files differ
diff --git a/indra/newview/featuretable.txt b/indra/newview/featuretable.txt
new file mode 100644
index 0000000000..3675fc3618
--- /dev/null
+++ b/indra/newview/featuretable.txt
@@ -0,0 +1,170 @@
+version 9
+
+// NOTE: This is mostly identical to featuretable_mac.txt with a few differences
+// Should be combined into one table
+
+//
+// Generates lists of feature mask that can be applied on top of each other.
+//
+// // Begin comments
+// list <name>
+// Starts a feature list named <name>
+// <name> <available> <recommended>
+// <name> is the name of a feature
+// <available> is 0 or 1, whether the feature is available
+// <recommended> is an S32 which is the recommended value
+//
+// For now, the first list read sets up all of the default values
+//
+
+
+//
+// All contains everything at their default settings for high end machines
+// NOTE: All settings are set to the MIN of applied values, including 'all'!
+//
+list all
+RenderAGP 1 1
+RenderAniso 1 0
+RenderAvatarMode 1 2
+RenderAvatarVP 1 1
+RenderDistance 1 128
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderParticleCount 1 4096
+RenderRippleWater 1 1
+RenderTerrainDetail 1 2
+VertexShaderEnable 1 1
+
+//
+// Class 0 Hardware (Unknown or just old)
+//
+list Class0
+VertexShaderEnable 1 0
+RenderDistance 1 64
+RenderAvatarVP 1 0
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 1 Hardware
+//
+list Class1
+VertexShaderEnable 1 0
+RenderDistance 1 96
+RenderAvatarVP 1 1
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 2 Hardware (make it purty)
+//
+list Class2
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 1
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// Class 3 Hardware (make it purty)
+//
+list Class3
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 1
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// No Pixel Shaders available
+//
+list NoPixelShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// No Vertex Shaders available
+//
+list NoVertexShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// "Default" setups for safe, low, medium, high
+//
+list safe
+RenderAGP 1 0
+RenderAniso 1 0
+RenderAvatarVP 0 0
+RenderLighting 1 0
+RenderParticleCount 1 1024
+RenderTerrainDetail 1 0
+
+
+list low
+RenderAGP 1 1
+RenderAniso 1 0
+RenderLighting 1 0
+
+list medium
+RenderLighting 1 0
+
+
+//
+// CPU based feature masks
+//
+
+// 1Ghz or less (equiv)
+list CPUSlow
+RenderParticleCount 1 1024
+
+
+//
+// RAM based feature masks
+//
+list RAM256MB
+RenderObjectBump 0 0
+
+
+//
+// Graphics card based feature masks
+//
+list Brookdale
+RenderAniso 1 0
+RenderLighting 1 0
+RenderTerrainDetail 1 0
+
+list GeForce2
+RenderAniso 1 0
+RenderLighting 1 0
+RenderParticleCount 1 2048
+RenderTerrainDetail 1 0
+
+list GeForce3
+
+list ATI
+
+// Hacked to be paranoid "safe"
+// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
+list Radeon8500
+RenderAGP 0 0
+RenderLighting 1 0
+RenderParticleCount 1 4096
+
+// Hacked to be paranoid "safe"
+list Radeon9700
+RenderParticleCount 1 4096
+
+// Hacked to be paranoid "safe"
+// Disable AGP entirely, in Catalyst 4.3 it's at least 50% slower
+list MobilityRadeon9000
+RenderLighting 1 0
+RenderParticleCount 1 4096
+
+list GeForceFX
diff --git a/indra/newview/featuretable_mac.txt b/indra/newview/featuretable_mac.txt
new file mode 100644
index 0000000000..25228fc999
--- /dev/null
+++ b/indra/newview/featuretable_mac.txt
@@ -0,0 +1,151 @@
+version 9
+
+// NOTE: This is mostly identical to featuretable.txt with a few differences
+// Should be combined into one table
+
+//
+// Generates lists of feature mask that can be applied on top of each other.
+//
+// // Begin comments
+// list <name>
+// Starts a feature list named <name>
+// <name> <available> <recommended>
+// <name> is the name of a feature
+// <available> is 0 or 1, whether the feature is available
+// <recommended> is an S32 which is the recommended value
+//
+// For now, the first list read sets up all of the default values
+//
+
+
+//
+// All contains everything at their default settings for high end machines
+// NOTE: All settings are set to the MIN of applied values, including 'all'!
+//
+// Mac specific: RenderAvatarVP not enabled at all
+list all
+RenderAGP 1 1
+RenderAniso 1 0
+RenderAvatarMode 1 2
+RenderAvatarVP 1 0
+RenderDistance 1 128
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderParticleCount 1 4096
+RenderRippleWater 1 1
+RenderTerrainDetail 1 2
+VertexShaderEnable 1 1
+
+
+//
+// Class 0 Hardware (Unknown or just old)
+//
+list Class0
+VertexShaderEnable 1 0
+RenderAvatarVP 1 0
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 1 Hardware
+//
+list Class1
+VertexShaderEnable 1 0
+RenderAvatarVP 1 1
+RenderAvatarMode 1 0
+RenderLighting 1 0
+RenderObjectBump 1 0
+RenderRippleWater 1 0
+
+//
+// Class 2 Hardware (make it purty)
+//
+list Class2
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 2
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// Class 3 Hardware (make it purty)
+//
+list Class3
+VertexShaderEnable 1 1
+RenderAvatarVP 1 1
+RenderAvatarMode 1 2
+RenderLighting 1 1
+RenderObjectBump 1 1
+RenderRippleWater 1 1
+
+//
+// No Pixel Shaders available
+//
+list NoPixelShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// No Vertex Shaders available
+//
+list NoVertexShaders
+VertexShaderEnable 0 0
+RenderAvatarVP 0 0
+
+//
+// "Default" setups for safe, low, medium, high
+//
+list safe
+RenderAGP 1 0
+RenderAniso 1 0
+RenderAvatarVP 0 0
+RenderDistance 1 64
+RenderLighting 1 0
+RenderParticleCount 1 1024
+RenderTerrainDetail 1 0
+
+
+list low
+RenderAGP 1 1
+RenderAniso 1 0
+RenderDistance 1 96
+RenderLighting 1 0
+
+list medium
+RenderLighting 1 0
+
+
+//
+// CPU based feature masks
+//
+
+// 1Ghz or less (equiv)
+list CPUSlow
+RenderDistance 1 96
+RenderParticleCount 1 1024
+
+
+//
+// RAM based feature masks
+//
+list RAM256MB
+RenderDistance 1 96
+RenderObjectBump 0 0
+
+// nVidia settings
+list NVIDIA
+
+list GeForce2
+RenderAniso 1 0
+RenderDistance 1 64
+RenderLighting 1 0
+RenderParticleCount 1 2048
+RenderTerrainDetail 1 0
+
+//
+// ATI and AGP now work okay.
+//
+list ATI
diff --git a/indra/newview/fmod_hidden_symbols.exp b/indra/newview/fmod_hidden_symbols.exp
new file mode 100644
index 0000000000..c0d5bed859
--- /dev/null
+++ b/indra/newview/fmod_hidden_symbols.exp
@@ -0,0 +1,140 @@
+__book_maptype1_quantvals
+__book_unquantize
+__float32_pack
+__float32_unpack
+__ilog
+__make_words
+_lpc_clear
+_lpc_init
+__vorbis_block_alloc
+__vorbis_block_ripcord
+__vorbis_apply_window
+__vorbis_window_get
+_vorbis_analysis_blockout
+_vorbis_analysis_buffer
+_vorbis_analysis_wrote
+_vorbis_block_clear
+_vorbis_block_init
+_vorbis_dsp_clear
+_vorbis_synthesis_blockin
+_vorbis_synthesis_init
+_vorbis_synthesis_pcmout
+_vorbis_synthesis_read
+_vorbis_packet_blocksize
+_vorbis_synthesis
+_vorbis_book_clear
+_vorbis_book_decode
+_vorbis_book_decodev_add
+_vorbis_book_decodev_set
+_vorbis_book_decodevs_add
+_vorbis_book_decodevv_add
+_vorbis_book_init_decode
+_vorbis_comment_add
+_vorbis_comment_add_tag
+_vorbis_comment_clear
+_vorbis_comment_init
+_vorbis_comment_query
+_vorbis_comment_query_count
+_vorbis_coslook
+_vorbis_fromdBlook
+_vorbis_info_blocksize
+_vorbis_info_clear
+_vorbis_info_init
+_vorbis_invsq2explook
+_vorbis_invsqlook
+_vorbis_lpc_from_curve
+_vorbis_lpc_from_data
+_vorbis_lpc_predict
+_vorbis_lsp_to_curve
+_vorbis_staticbook_clear
+_vorbis_staticbook_destroy
+_vorbis_staticbook_unpack
+_vorbis_synthesis_headerin
+_vorbis_synthesis_lapout
+_vorbis_synthesis_restart
+_vorbis_synthesis_trackonly
+_vorbis_window
+_ogg_packet_clear
+_ogg_page_bos
+_ogg_page_checksum_set
+_ogg_page_continued
+_ogg_page_eos
+_ogg_page_granulepos
+_ogg_page_packets
+_ogg_page_pageno
+_ogg_page_serialno
+_ogg_page_version
+_ogg_stream_reset_serialno
+_ogg_stream_clear
+_ogg_stream_destroy
+_ogg_stream_eos
+_ogg_stream_flush
+_ogg_stream_init
+_ogg_stream_packetout
+_ogg_stream_packetpeek
+_ogg_stream_pagein
+_ogg_stream_pageout
+_ogg_stream_reset
+_ogg_sync_buffer
+_ogg_sync_clear
+_ogg_sync_destroy
+_ogg_sync_init
+_ogg_sync_pageout
+_ogg_sync_pageseek
+_ogg_sync_reset
+_ogg_sync_wrote
+_ov_bitrate
+_ov_bitrate_instant
+_ov_clear
+_ov_comment
+_ov_info
+_ov_open
+_ov_open_callbacks
+_ov_pcm_seek
+_ov_pcm_seek_page
+_ov_pcm_tell
+_ov_pcm_total
+_ov_raw_seek
+_ov_raw_tell
+_ov_raw_total
+_ov_read
+_ov_read_float
+_ov_seekable
+_ov_serialnumber
+_ov_streams
+_ov_test
+_ov_test_callbacks
+_ov_test_open
+_ov_time_seek
+_ov_time_seek_page
+_ov_time_tell
+_ov_time_total
+_ogg_toupper
+_oggpackB_adv
+_oggpackB_adv1
+_oggpackB_bits
+_oggpackB_bytes
+_oggpackB_get_buffer
+_oggpackB_look
+_oggpackB_look1
+_oggpackB_read
+_oggpackB_read1
+_oggpackB_readinit
+_oggpackB_reset
+_oggpack_adv
+_oggpack_adv1
+_oggpack_bits
+_oggpack_bytes
+_oggpack_get_buffer
+_oggpack_look
+_oggpack_look1
+_oggpack_read
+_oggpack_read1
+_oggpack_readinit
+_oggpack_reset
+_ov_crosslap
+_ov_pcm_seek_lap
+_ov_pcm_seek_page_lap
+_ov_raw_seek_lap
+_ov_time_seek_lap
+_ov_time_seek_page_lap
diff --git a/indra/newview/fmodwrapper.cpp b/indra/newview/fmodwrapper.cpp
new file mode 100644
index 0000000000..99fd95434f
--- /dev/null
+++ b/indra/newview/fmodwrapper.cpp
@@ -0,0 +1,19 @@
+/**
+ * @file fmodwrapper.cpp
+ * @brief dummy source file for building a shared library to wrap libfmod.a
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+extern "C"
+{
+ void FSOUND_Init(void);
+}
+
+void* fmodwrapper(void)
+{
+ // When building the fmodwrapper library, the linker doesn't seem to want to bring in libfmod.a unless I explicitly
+ // reference at least one symbol in the library. This seemed like the simplest way.
+ return (void*)&FSOUND_Init;
+} \ No newline at end of file
diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt
new file mode 100644
index 0000000000..1af40f8932
--- /dev/null
+++ b/indra/newview/gpu_table.txt
@@ -0,0 +1,118 @@
+//
+// Categorizes graphics chips into various classes by name
+//
+// The table contains chip names regular expressions to match
+// against driver strings and a class number.
+//
+// Class Numbers:
+// 0 - Compatibility rendering only
+// 1 - Shaders available off by default
+// 2 - Shaders enabled by default
+// 3 - Everything on.
+//
+// Format:
+// <chip name> <regexp> <class>
+//
+
+3Dfx .*3Dfx.* 0
+3Dlabs .*3Dlabs.* 0
+ATI All-in-Wonder PCI-E .*ATI.*All-in-Wonder.*PCI-E.* 1
+ATI All-in-Wonder X1800 .*ATI.*All-in-Wonder X18.* 3
+ATI All-in-Wonder X1900 .*ATI.*All-in-Wonder X19.* 3
+ATI ASUS X1300 .*ATI.*ASUS X13.* 3
+ATI ASUS X1600 .*ATI.*ASUS X16.* 3
+ATI Diamond X1300 .*ATI.*Diamond X13.* 3
+ATI FireGL .*ATI.*Fire.*GL.* 0
+ATI FireMV .*ATI.*FireMV.* 0
+ATI Generic .*ATI.*Generic.* 0
+ATI Radeon 7000 .*ATI.*Radeon 7.* 0
+ATI Radeon 8000 .*ATI.*Radeon 8.* 0
+ATI Radeon 9000 .*ATI.*Radeon 90.* 1
+ATI Radeon 9100 .*ATI.*Radeon 91.* 1
+ATI Radeon 9200 .*ATI.*Radeon 92.* 1
+ATI Radeon 9500 .*ATI.*Radeon 95.* 1
+ATI Radeon 9600 .*ATI.*Radeon 96.* 1
+ATI Radeon 9700 .*ATI.*Radeon 97.* 1
+ATI Radeon 9800 .*ATI.*Radeon 98.* 1
+ATI Radeon X1300 .*ATI.*Radeon X13.* 3
+ATI Radeon X1600 .*ATI.*Radeon X16.* 3
+ATI Radeon X1800 .*ATI.*Radeon X18.* 3
+ATI Radeon X1900 .*ATI.*Radeon X19.* 3
+ATI Radeon X300 .*ATI.*Radeon X3.* 2
+ATI Radeon X500 .*ATI.*Radeon X5.* 2
+ATI Radeon X600 .*ATI.*Radeon X6.* 2
+ATI Radeon X700 .*ATI.*Radeon X7.* 2
+ATI Radeon X800 .*ATI.*Radeon X8.* 2
+ATI Radeon Xpress .*ATI.*Radeon Xpress.* 1
+ATI Rage 128 .*ATI.*Rage 128.* 0
+Intel 830M .*Intel.*830M 0
+Intel 845G .*Intel.*845G 0
+Intel 855GM .*Intel.*855GM 0
+Intel 865G .*Intel.*865G 0
+Intel 900 .*Intel.*900.*900 0
+Intel 915G .*Intel.*915G 0
+Intel 915GM .*Intel.*915GM 0
+Intel 945G .*Intel.*945G 0
+Intel 945GM .*Intel.*945GM 0
+Intel 950 .*Intel.*950.*950 0
+Intel Brookdale .*Intel.*Brookdale.* 0
+Intel Montara .*Intel.*Montara.* 0
+Intel Springdale .*Intel.*Springdale.* 0
+Matrox .*Matrox.* 0
+NVIDIA GeForce .*GeForce 256.* 0
+NVIDIA GeForce 2 .*GeForce2.* 0
+NVIDIA GeForce 3 .*GeForce3.* 0
+NVIDIA GeForce 4 Go .*NVIDIA.*GeForce4.*Go.* 0
+NVIDIA GeForce 4 MX .*NVIDIA.*GeForce4 MX.* 0
+NVIDIA GeForce 4 Ti .*NVIDIA.*GeForce4 Ti.* 0
+NVIDIA GeForce 6100 .*NVIDIA.*GeForce 61.* 2
+NVIDIA GeForce 6200 .*NVIDIA.*GeForce 62.* 2
+NVIDIA GeForce 6500 .*NVIDIA.*GeForce 65.* 2
+NVIDIA GeForce 6600 .*NVIDIA.*GeForce 66.* 2
+NVIDIA GeForce 6700 .*NVIDIA.*GeForce 67.* 2
+NVIDIA GeForce 6800 .*NVIDIA.*GeForce 68.* 2
+NVIDIA GeForce 7300 .*NVIDIA.*GeForce 73.* 3
+NVIDIA GeForce 7600 .*NVIDIA.*GeForce 76.* 3
+NVIDIA GeForce 7800 .*NVIDIA.*GeForce 78.* 3
+NVIDIA GeForce 7900 .*NVIDIA.*GeForce 79.* 3
+NVIDIA GeForce FX 5100 .*NVIDIA.*GeForce FX 51.* 1
+NVIDIA GeForce FX 5200 .*NVIDIA.*GeForce FX 52.* 1
+NVIDIA GeForce FX 5500 .*NVIDIA.*GeForce FX 55.* 1
+NVIDIA GeForce FX 5600 .*NVIDIA.*GeForce FX 56.* 1
+NVIDIA GeForce FX 5700 .*NVIDIA.*GeForce FX 57.* 1
+NVIDIA GeForce FX 5800 .*NVIDIA.*GeForce FX 58.* 1
+NVIDIA GeForce FX 5900 .*NVIDIA.*GeForce FX 59.* 1
+NVIDIA GeForce FX Go5100 .*NVIDIA.*GeForce FX Go51.* 1
+NVIDIA GeForce FX Go5200 .*NVIDIA.*GeForce FX Go52.* 1
+NVIDIA GeForce FX Go5300 .*NVIDIA.*GeForce FX Go53.* 1
+NVIDIA GeForce FX Go5500 .*NVIDIA.*GeForce FX Go55.* 1
+NVIDIA GeForce FX Go5600 .*NVIDIA.*GeForce FX Go56.* 1
+NVIDIA GeForce FX Go5700 .*NVIDIA.*GeForce FX Go57.* 1
+NVIDIA GeForce FX Go5800 .*NVIDIA.*GeForce FX Go58.* 1
+NVIDIA GeForce FX Go5900 .*NVIDIA.*GeForce FX Go59.* 1
+NVIDIA GeForce Go 6 .*GeForce Go 6.* 2
+NVIDIA GeForce Go 6100 .*NVIDIA.*GeForce Go 61.* 2
+NVIDIA GeForce Go 6200 .*NVIDIA.*GeForce Go 62.* 2
+NVIDIA GeForce Go 6500 .*NVIDIA.*GeForce Go 65.* 2
+NVIDIA GeForce Go 6600 .*NVIDIA.*GeForce Go 66.* 2
+NVIDIA GeForce Go 6700 .*NVIDIA.*GeForce Go 67.* 2
+NVIDIA GeForce Go 6800 .*NVIDIA.*GeForce Go 68.* 2
+NVIDIA GeForce Go 7300 .*NVIDIA.*GeForce Go 73.* 3
+NVIDIA GeForce Go 7400 .*NVIDIA.*GeForce Go 74.* 3
+NVIDIA GeForce Go 7600 .*NVIDIA.*GeForce Go 76.* 3
+NVIDIA GeForce Go 7800 .*NVIDIA.*GeForce Go 78.* 3
+NVIDIA GeForce Go 7900 .*NVIDIA.*GeForce Go 79.* 3
+NVIDIA GeForce PCX .*GeForce PCX.* 1
+NVIDIA Generic .*NVIDIA.*NV.* 0
+NVIDIA Generic .*NVIDIA.*Unknown.* 0
+NVIDIA Quadro 2 .*Quadro2.* 0
+NVIDIA Quadro 4 .*Quadro4.* 0
+NVIDIA Quadro DCC .*Quadro DCC.* 0
+NVIDIA Quadro FX .*Quadro FX.* 1
+NVIDIA Quadro NVS .*Quadro NVS.* 0
+NVIDIA RIVA TNT .*RIVA TNT.* 0
+S3 .*S3 Graphics.* 0
+SiS SiS.* 0
+Trident Trident.* 0
+Tungsten Graphics Tungsten.* 0
+XGI XGI.* 0
diff --git a/indra/newview/licenses-linux.txt b/indra/newview/licenses-linux.txt
new file mode 100644
index 0000000000..f7ccf27a12
--- /dev/null
+++ b/indra/newview/licenses-linux.txt
@@ -0,0 +1,437 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+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 OF THIRD PARTY RIGHTS. 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.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+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.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+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
+BRIAN PAUL 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.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===========
+SDL License
+===========
+
+SDL - Simple DirectMedia Layer
+Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with this library; if not, write to the Free
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Sam Lantinga
+slouken@libsdl.org
+
+The GNU Library GPL is available at http://www.gnu.org/copyleft/lesser.html
+
+=============
+ELFIO License
+=============
+
+ELFIO.h - ELF reader and producer.
+Copyright (C) 2001 Serge Lamikhov-Center <to_serge@users.sourceforge.net>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+The GNU Library GPL is available at http://www.gnu.org/copyleft/lesser.html
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/licenses-mac.txt b/indra/newview/licenses-mac.txt
new file mode 100644
index 0000000000..f761ed6cbb
--- /dev/null
+++ b/indra/newview/licenses-mac.txt
@@ -0,0 +1,388 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+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 OF THIRD PARTY RIGHTS. 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.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+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.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+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
+BRIAN PAUL 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.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/licenses-win32.txt b/indra/newview/licenses-win32.txt
new file mode 100644
index 0000000000..f761ed6cbb
--- /dev/null
+++ b/indra/newview/licenses-win32.txt
@@ -0,0 +1,388 @@
+===========
+APR License
+===========
+
+Copyright 2000-2004 The Apache Software Foundation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+==========
+Cg License
+==========
+
+Copyright (c) 2002, NVIDIA Corporation.
+
+
+
+NVIDIA Corporation("NVIDIA") supplies this software to you in consideration
+of your agreement to the following terms, and your use, installation,
+modification or redistribution of this NVIDIA software constitutes
+acceptance of these terms. If you do not agree with these terms, please do
+not use, install, modify or redistribute this NVIDIA software.
+
+
+
+In consideration of your agreement to abide by the following terms, and
+subject to these terms, NVIDIA grants you a personal, non-exclusive license,
+under NVIDIA's copyrights in this original NVIDIA software (the "NVIDIA
+Software"), to use, reproduce, modify and redistribute the NVIDIA
+Software, with or without modifications, in source and/or binary forms;
+provided that if you redistribute the NVIDIA Software, you must retain the
+copyright notice of NVIDIA, this notice and the following text and
+disclaimers in all such redistributions of the NVIDIA Software. Neither the
+name, trademarks, service marks nor logos of NVIDIA Corporation may be used
+to endorse or promote products derived from the NVIDIA Software without
+specific prior written permission from NVIDIA. Except as expressly stated
+in this notice, no other rights or licenses express or implied, are granted
+by NVIDIA herein, including but not limited to any patent rights that may be
+infringed by your derivative works or by other works in which the NVIDIA
+Software may be incorporated. No hardware is licensed hereunder.
+
+
+
+THE NVIDIA SOFTWARE IS BEING PROVIDED ON AN "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR ITS USE AND OPERATION
+EITHER ALONE OR IN COMBINATION WITH OTHER PRODUCTS.
+
+
+
+IN NO EVENT SHALL NVIDIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL,
+EXEMPLARY, CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, LOST
+PROFITS; PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) OR ARISING IN ANY WAY OUT OF THE USE,
+REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION OF THE NVIDIA SOFTWARE,
+HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING
+NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF NVIDIA HAS BEEN ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+============
+cURL License
+============
+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2002, Daniel Stenberg, <daniel@haxx.se>.
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright
+notice and this permission notice appear in all copies.
+
+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 OF THIRD PARTY RIGHTS. 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.
+
+Except as contained in this notice, the name of a copyright holder shall not
+be used in advertising or otherwise to promote the sale, use or other dealings
+in this Software without prior written authorization of the copyright holder.
+
+
+=============
+expat License
+=============
+
+Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd
+
+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.
+
+================
+FreeType License
+================
+
+Portions of this software are copyright (c) 2003 The FreeType
+Project (www.freetype.org). All rights reserved.
+
+
+==========
+GL License
+==========
+
+Mesa 3-D graphics library
+Version: 6.2
+
+Copyright (C) 1999-2004 Brian Paul All Rights Reserved.
+
+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
+BRIAN PAUL 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.
+
+=======================
+JPEG Library 6b License
+=======================
+
+This software is based in part on the work of the Independent JPEG Group
+
+================
+JPEG2000 License
+================
+
+Copyright 2001, David Taubman, The University of New South Wales (UNSW)
+The copyright owner is Unisearch Ltd, Australia (commercial arm of UNSW)
+Neither this copyright statement, nor the licensing details below
+may be removed from this file or dissociated from its contents.
+
+Licensee: Linden Research, Inc.
+License number: 00024
+The licensee has been granted a COMMERCIAL license to the contents of
+this source file. A brief summary of this license appears below. This
+summary is not to be relied upon in preference to the full text of the
+license agreement, accepted at purchase of the license.
+1. The Licensee has the right to Commercial Use of the Kakadu software,
+ including distribution of one or more Applications built using the
+ software.
+2. The Licensee has the right to Internal Use of the Kakadu software,
+ including use by employees of the Licensee or an Affiliate for the
+ purpose of performing services on behalf of the Licensee or Affiliate,
+ or in the performance of services for Third Parties who engage Licensee
+ or an Affiliate for such services.
+3. The Licensee has the right to distribute Reusable Code (including
+ source code and dynamically or statically linked libraries) to a Third
+ Party, provided the Third Party possesses a license to use the Kakadu
+ software.
+
+==================
+ogg/vorbis License
+==================
+
+Copyright (c) 2001, Xiphophorus
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+- Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+- Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+
+- Neither the name of the Xiphophorus nor the names of its contributors
+may be used to endorse or promote products derived from this software
+without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============
+OpenSSL License
+===============
+
+Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+====================================================================
+
+This product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com). This product includes software written by Tim
+Hudson (tjh@cryptsoft.com).
+
+
+
+=======================
+Original SSLeay License
+=======================
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+
+==================
+xmlrpc-epi License
+==================
+
+Copyright 2000 Epinions, Inc.
+
+Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well.
+
+1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software.
+
+2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT.
+
+3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+
+============
+zlib License
+============
+
+'zlib' general purpose compression library version 1.1.4, March 11th, 2002
+
+Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler
+
+This software is provided 'as-is', without any express or implied warranty.
+In no event will the authors be held liable for any damages arising from the
+use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+The origin of this software must not be misrepresented; you must not claim
+that you wrote the original software. If you use this software in a product,
+an acknowledgment in the product documentation would be appreciated but is
+not required.
+
+Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+
+This notice may not be removed or altered from any source distribution.
+
+Jean-loup Gailly
+jloup@gzip.org
+
+Mark Adler
+madler@alumni.caltech.edu
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
new file mode 100644
index 0000000000..4748eaffbe
--- /dev/null
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -0,0 +1,202 @@
+Second Life - Linux Alpha README
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+This document contains information about the Second Life Linux
+client, and isn't meant to serve as an introduction to Second
+Life itself - please see <http://www.secondlife.com/whatis/>.
+
+1. Introduction
+2. System Requirements
+3. Installing & Running
+4. Known Issues
+5. Troubleshooting
+6. Advanced Troubleshooting
+7. Getting more help, and reporting problems
+
+
+1. INTRODUCTION
+-=-=-=-=-=-=-=-
+
+Hi! This is an ALPHA release of the Second Life client for Linux.
+The 'alpha' status means that not everything is implemented yet and
+we're still hard at work on this version of the client, but many
+residents find that it already works well 'out of the box' for accessing
+Second Life.
+
+We encourage you to try it out and let us know of its compatibility
+with your system. Be aware that although the client itself is provided
+for testing purposes, any changes you make within the Second Life world
+are permanent.
+
+Please enjoy!
+
+
+2. SYSTEM REQUIREMENTS
+-=-=-=-=-=-=-=-=-=-=-=
+
+Minimum requirements:
+ * Internet Connection: Cable or DSL
+ * Computer Processor: 800MHz Pentium III or Athlon, or better
+ * Computer Memory: 256MB or better
+ * Linux Operating System: A reasonably modern 32-bit Linux environment
+ is required. If you are running a 64-bit Linux distribution, you
+ may need a set of 32-bit compatibility libraries.
+ * Video/Graphics Card:
+ o nVidia GeForce 2, GeForce 4mx, or better
+ o OR ATI Radeon 8500, 9250, or better
+
+ **NOTE**: Second Life absolutely requires you to have recent, correctly-
+ configured OpenGL 3D drivers for your hardware - the graphics drivers
+ that came with your operating system may not be good enough! See the
+ TROUBLESHOOTING section if you encounter problems starting Second Life.
+
+For a more comfortable experience, the RECOMMENDED hardware for the Second
+Life Linux client is very similar to that for Windows, as detailed at:
+<https://secondlife.com/corporate/sysreqs.php>
+
+
+3. INSTALLING & RUNNING
+-=-=-=-=-=-=-=-=-=-=-=-
+
+The Second Life Linux client entirely runs out of the directory you have
+unpacked it into - no installation step is required.
+
+Run ./secondlife from the installation directory to start Second Life.
+
+User data is stored in the hidden directory ~/.secondlife by default; you may
+override this location with the SECONDLIFE_USER_DIR environment variable if
+you wish.
+
+
+4. KNOWN ISSUES
+-=-=-=-=-=-=-=-
+
+The following user-visible features are currently not fully implemented on
+the Linux client and are therefore known not to work properly:
+ * QuickTime movie playback and movie recording
+ * F1 Help
+ * Embedded Web Profiles and HTML Login Screen
+ * Video memory detection
+ * Full Unicode font rendering
+ * Auto-updater
+
+* UPDATING - when the client detects that a new version of Second Life
+ is available, it will ask you if you wish to download the new version.
+ This option is not implemented; to upgrade, you should manually download a
+ new version from the Second Life web site, <http://www.secondlife.com/>.
+
+* UPLOAD / SAVE / COLOR-PICKER DIALOGS - These only function when the client
+ is in 'windowed' mode, not 'fullscreen' mode.
+
+* GRAPHICAL SPEED/QUALITY - as many Linux graphics drivers are not as robust
+ as their counterparts for some other operating systems, advanced Second Life
+ graphical features have been disabled to aid stability. See PROBLEM 3 in the
+ TROUBLESHOOTING section if you wish to turn these on to enhance your
+ experience.
+
+
+5. TROUBLESHOOTING
+-=-=-=-=-=-=-=-=-=
+
+The client prints a lot of diagnostic information to the console it was
+run from. Most of this is also replicated in ~/.secondlife/logs/SecondLife.log
+- this is helpful to read when troubleshooting, especially 'WARNING' lines.
+
+PROBLEM 1:- Second Life fails to start up, with a warning on the console like:
+ 'Error creating window.' or
+ 'Unable to create window, be sure screen is set at 32-bit color' or
+ 'SDL: Couldn't find matching GLX visual.'
+SOLUTION:- Usually this indicates that your graphics card does not meet
+ the minimum requirements, or that your system's OpenGL 3D graphics driver is
+ not updated and configured correctly. If you believe that your graphics
+ card DOES meet the minimum requirements then you likely need to install the
+ official so-called 'non-free' nVidia or ATI (fglrx) graphics drivers; we
+ suggest one of the following options:
+ * Consult your Linux distribution's documentation for installing these
+ official drivers. For example, Ubuntu provides documentation here:
+ <https://help.ubuntu.com/community/BinaryDriverHowto>
+ * If your distribution does not make it easy, then you can download the
+ required Linux drivers straight from your graphics card manufacturer:
+ - nVidia cards: <http://www.nvidia.com/object/unix.html>
+ - ATI cards: <http://ati.amd.com/support/driver.html>
+
+PROBLEM 2:- My whole system seems to hang when running Second Life.
+SOLUTION:- This is typically a hardware/driver issue. The first thing to
+ do is to check that you have the most recent official drivers for your
+ graphics card.
+SOLUTION:- Some residents with ATI cards have reported that running
+ 'sudo aticonfig --locked-userpages=off' before running Second Life solves
+ their stability issues.
+SOLUTION:- As a last resort, you can disable most of Second Life's advanced
+ graphics features by editing the 'secondlife' script and removing the '#'
+ from the line which reads '#export LL_GL_NOEXT=x'
+
+PROBLEM 3:- Performance or graphical quality are not as high as I expect.
+PROBLEM:- I can't turn on Anisotropic Filtering, Ripple Water, or AGP.
+PROBLEM:- 'Shiny' doesn't work.
+SOLUTION:- Some graphics performance features in Second Life are disabled
+ by default for the Linux version due to stability issues with some common
+ Linux graphic drivers. You can re-enable these features at the slight
+ risk of decreasing system stability. To do so:
+ * Edit the 'secondlife' script. Comment-out these lines by putting a '#'
+ in front of them: 'export LL_GL_BASICEXT=x', 'export LL_GL_NOEXT=x',
+ 'export LL_GL_BLACKLIST=abcdefghijklmno'.
+ * Now start Second Life. Some advanced performance features will now be
+ automatically used, and some new options in Preferences will now be
+ available to you; there is no guarantee, however, that they will
+ positively affect performance!
+
+PROBLEM 4:- Sound effects seem to 'lag' a fraction of a second behind
+ actions.
+SOLUTION:- You may uncomment the 'LL_BAD_ESD' line in the 'secondlife' script
+ to get more responsive audio. However, if you do this then you may
+ encounter audio issues or a hang during login, so beware.
+
+
+6. ADVANCED TROUBLESHOOTING
+-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+The 'secondlife' script which launches Second Life contains some
+configuration options for advanced troubleshooters.
+
+* AUDIO - Edit the 'secondlife' script and you will see three audio
+ options: LL_BAD_ESD, LL_BAD_OSS, LL_BAD_ALSA. Second Life tries to
+ use ESD, OSS, then ALSA audio drivers in this order; you may uncomment
+ the corresponding LL_BAD_* option to skip an audio driver which you
+ believe may be causing you trouble.
+
+* OPENGL - For advanced troubleshooters, the LL_GL_BLACKLIST option lets
+ you disable specific GL extensions, each of which is represented by a
+ letter ("a"-"o"). If you can narrow down a stability problem on your system
+ to just one or two GL extensions then please post details of your hardware
+ (and drivers) to the Linux Client Alpha Testers forum (see link below) along
+ with the minimal LL_GL_BLACKLIST which solves your problems. This will help
+ us to improve stability for your hardware while minimally impacting
+ performance.
+ LL_GL_BASICEXT and LL_GL_NOEXT should be commented-out for this to be useful.
+
+
+7. GETTING MORE HELP AND REPORTING PROBLEMS
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+For general help and support with Second Life:
+<http://secondlife.com/community/support.php>
+
+In-world help: Please use the 'Help' menu in the client for general
+non-Linux-specific Second Life help including live support from the fabulous
+Live Help team.
+
+In-world discussion: There is a 'Linux Client Users' group
+inside Second Life which is free to join. You can find it by pressing
+the 'Search' button at the bottom of the window and then selecting the
+'Groups' tab and searching for 'Linux'. This group is useful for discussing
+Linux issues with fellow Linux client users who are online.
+
+Linux Client Alpha Testers forum:
+<http://forums.secondlife.com/forumdisplay.php?forumid=263>
+This is a good place for discussing Linux-specific Second Life problems
+if you find that the Troubleshooting section in this file hasn't helped.
+When reporting problems here, please include information about the
+Second Life version you are running, your graphics card, graphics driver,
+and Linux distribution.
+
diff --git a/indra/newview/linux_tools/launch_url.sh b/indra/newview/linux_tools/launch_url.sh
new file mode 100755
index 0000000000..564e834939
--- /dev/null
+++ b/indra/newview/linux_tools/launch_url.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+# bash v1.14+ expected
+
+# This script loads a web page in the 'default' graphical web browser.
+# It MUST return immediately (or soon), so the browser should be
+# launched in the background (thus no text-only browsers).
+# This script does not trust the URL to be well-escaped or shell-safe.
+#
+# On Unixoids we try, in order of decreasing priority:
+# - $BROWSER if set (preferred)
+# - kfmclient openURL
+# - x-www-browser
+# - opera
+# - firefox
+# - mozilla
+# - netscape
+
+URL="$1"
+
+if [ -z "$URL" ]; then
+ echo Usage: $0 URL
+ exit
+fi
+
+# if $BROWSER is defined, use it.
+XBROWSER=`echo "$BROWSER" |cut -f1 -d:`
+if [ ! -z "$XBROWSER" ]; then
+ XBROWSER_CMD=`echo "$XBROWSER" |cut -f1 -d' '`
+ # look for $XBROWSER_CMD either literally or in PATH
+ if [ -x "$XBROWSER_CMD" ] || which $XBROWSER_CMD >/dev/null; then
+ # check for %s string, avoiding bash2-ism of [[ ]]
+ if echo "$XBROWSER" | grep %s >/dev/null; then
+ # $XBROWSER has %s which needs substituting
+ echo "$URL" | xargs -r -i%s $XBROWSER &
+ exit
+ fi
+ # $XBROWSER has no %s, tack URL on the end instead
+ $XBROWSER "$URL" &
+ exit
+ fi
+ echo "Couldn't find the browser specified by \$BROWSER ($BROWSER)"
+ echo "Trying some others..."
+fi
+
+# else kfmclient
+# (embodies KDE concept of 'preferred browser')
+if which kfmclient >/dev/null; then
+ kfmclient openURL "$URL" &
+ exit
+fi
+
+# else x-www-browser
+# (Debianesque idea of a working X browser)
+if which x-www-browser >/dev/null; then
+ x-www-browser "$URL" &
+ exit
+fi
+
+# else opera
+# (if user has opera in their path, they probably went to the
+# trouble of installing it -> prefer it)
+if which opera >/dev/null; then
+ opera "$URL" &
+ exit
+fi
+
+# else firefox
+if which firefox >/dev/null; then
+ firefox "$URL" &
+ exit
+fi
+
+# else mozilla
+if which mozilla >/dev/null; then
+ mozilla "$URL" &
+ exit
+fi
+
+# else netscape
+if which netscape >/dev/null; then
+ netscape "$URL" &
+ exit
+fi
+
+echo 'Failed to find a known browser. Please consider setting the $BROWSER environment variable.'
+
+# end.
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
new file mode 100755
index 0000000000..222ce98c73
--- /dev/null
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+## Here are some configuration options for Linux Client Alpha Testers.
+## These options are for self-assisted troubleshooting during this alpha
+## testing phase; you should not usually need to touch them.
+
+## - Avoids using the ESD audio driver.
+#export LL_BAD_ESD=x
+
+## - Avoids using the OSS audio driver.
+#export LL_BAD_OSS=x
+
+## - Avoids using the ALSA audio driver.
+#export LL_BAD_ALSA=x
+
+## - Avoids the optional OpenGL extensions which have proven most problematic
+## on some hardware. Disabling this option may cause crashes and hangs on
+## some unstable combinations of drivers and hardware.
+export LL_GL_BASICEXT=x
+
+## - Avoids *all* optional OpenGL extensions. This is the safest and least-
+## exciting option. Enable this if you experience stability issues, and
+## report whether it helps in the Linux Client Alpha Testers forum.
+#export LL_GL_NOEXT=x
+
+## - For advanced troubleshooters, this lets you disable specific GL
+## extensions, each of which is represented by a letter a-o. If you can
+## narrow down a stability problem on your system to just one or two
+## extensions then please post details of your hardware (and drivers) to
+## the Linux Client Alpha Testers forum along with the minimal
+## LL_GL_BLACKLIST which solves your problems.
+#export LL_GL_BLACKLIST=abcdefghijklmno
+
+## - Avoids an often-buggy X feature that doesn't really benefit us anyway.
+export SDL_VIDEO_X11_DGAMOUSE=0
+
+## Nothing worth editing below this line.
+##-------------------------------------------------------------------
+
+RUN_PATH=`dirname "$0" || echo .`
+cd "${RUN_PATH}"
+LD_LIBRARY_PATH="`pwd`"/lib:"${LD_LIBRARY_PATH}" bin/do-not-directly-run-secondlife-bin `cat gridargs.dat` $@ | cat
+
+echo
+echo '*********************************************************'
+echo 'This is an ALPHA release of the Second Life linux client.'
+echo 'Thank you for testing!'
+echo 'You can visit the Linux Client Alpha Testers forum at:'
+echo 'http://forums.secondlife.com/forumdisplay.php?forumid=263'
+echo 'Please see README-linux.txt before reporting problems.'
+echo
diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp
new file mode 100644
index 0000000000..e46800fff1
--- /dev/null
+++ b/indra/newview/llagent.cpp
@@ -0,0 +1,7233 @@
+/**
+ * @file llagent.cpp
+ * @brief LLAgent class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "stdtypes.h"
+#include "stdenums.h"
+
+#include "llagent.h"
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcriticaldamp.h"
+#include "llfocusmgr.h"
+#include "llglheaders.h"
+#include "llparcel.h"
+#include "llpermissions.h"
+#include "llregionhandle.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "message.h"
+#include "llquaternion.h"
+#include "v3math.h"
+#include "v4math.h"
+//#include "vmath.h"
+
+#include "imageids.h"
+#include "llbox.h"
+#include "llbutton.h"
+#include "llcameraview.h"
+#include "llcallingcard.h"
+#include "llchatbar.h"
+#include "llconsole.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llgroupmgr.h"
+#include "llhudeffectlookat.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lljoystickbutton.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llnotify.h"
+#include "llquantize.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltool.h"
+#include "lltoolcomp.h" // for gToolGun
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolview.h"
+#include "llui.h" // for make_ui_sound
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "pipeline.h"
+#include "roles_constants.h"
+#include "viewer.h"
+
+// Ventrella
+#include "llfollowcam.h"
+// end Ventrella
+
+extern LLMenuBarGL* gMenuBarView;
+extern F32 gMinObjectDistance;
+extern U8 gLastPickAlpha;
+extern F32 gFrameDTClamped;
+
+//drone wandering constants
+const F32 MAX_WANDER_TIME = 20.f; // seconds
+const F32 MAX_HEADING_HALF_ERROR = 0.2f; // radians
+const F32 WANDER_MAX_SLEW_RATE = 2.f * DEG_TO_RAD; // radians / frame
+const F32 WANDER_TARGET_MIN_DISTANCE = 10.f; // meters
+
+// Autopilot constants
+const F32 AUTOPILOT_HEADING_HALF_ERROR = 10.f * DEG_TO_RAD; // radians
+const F32 AUTOPILOT_MAX_SLEW_RATE = 1.f * DEG_TO_RAD; // radians / frame
+const F32 AUTOPILOT_STOP_DISTANCE = 2.f; // meters
+const F32 AUTOPILOT_HEIGHT_ADJUST_DISTANCE = 8.f; // meters
+const F32 AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND = 1.f; // meters
+const F32 AUTOPILOT_MAX_TIME_NO_PROGRESS = 1.5f; // seconds
+
+// face editing constants
+const LLVector3d FACE_EDIT_CAMERA_OFFSET(0.4f, -0.05f, 0.07f);
+const LLVector3d FACE_EDIT_TARGET_OFFSET(0.f, 0.f, 0.05f);
+
+// Mousewheel camera zoom
+const F32 MIN_ZOOM_FRACTION = 0.25f;
+const F32 INITIAL_ZOOM_FRACTION = 1.f;
+const F32 MAX_ZOOM_FRACTION = 8.f;
+const F32 METERS_PER_WHEEL_CLICK = 1.f;
+
+const F32 MAX_TIME_DELTA = 1.f;
+
+const F32 CAMERA_ZOOM_HALF_LIFE = 0.07f; // seconds
+const F32 FOV_ZOOM_HALF_LIFE = 0.07f; // seconds
+
+const F32 CAMERA_FOCUS_HALF_LIFE = 0.f;//0.02f;
+const F32 CAMERA_LAG_HALF_LIFE = 0.25f;
+const F32 MIN_CAMERA_LAG = 0.5f;
+const F32 MAX_CAMERA_LAG = 5.f;
+
+const F32 CAMERA_COLLIDE_EPSILON = 0.1f;
+const F32 MIN_CAMERA_DISTANCE = 0.1f;
+const F32 AVATAR_ZOOM_MIN_X_FACTOR = 0.55f;
+const F32 AVATAR_ZOOM_MIN_Y_FACTOR = 0.7f;
+const F32 AVATAR_ZOOM_MIN_Z_FACTOR = 1.15f;
+
+const F32 MAX_CAMERA_DISTANCE_FROM_AGENT = 50.f;
+
+const F32 HEAD_BUFFER_SIZE = 0.3f;
+const F32 CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP = 0.2f;
+
+const F32 LAND_MIN_ZOOM = 0.15f;
+const F32 AVATAR_MIN_ZOOM = 0.5f;
+const F32 OBJECT_MIN_ZOOM = 0.02f;
+
+const F32 APPEARANCE_MIN_ZOOM = 0.39f;
+const F32 APPEARANCE_MAX_ZOOM = 8.f;
+
+// fidget constants
+const F32 MIN_FIDGET_TIME = 8.f; // seconds
+const F32 MAX_FIDGET_TIME = 20.f; // seconds
+
+const S32 MAX_NUM_CHAT_POSITIONS = 10;
+const F32 GROUND_TO_AIR_CAMERA_TRANSITION_TIME = 0.5f;
+const F32 GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME = 0.5f;
+
+const F32 MAX_VELOCITY_AUTO_LAND_SQUARED = 4.f * 4.f;
+
+const F32 MAX_FOCUS_OFFSET = 20.f;
+
+const F32 OBJECT_EXTENTS_PADDING = 0.5f;
+
+const F32 MIN_RADIUS_ALPHA_SIZZLE = 0.5f;
+
+const F64 CHAT_AGE_FAST_RATE = 3.0;
+
+const S32 MAX_WEARABLES_PER_LAYERSET = 7;
+
+const EWearableType WEARABLE_BAKE_TEXTURE_MAP[BAKED_TEXTURE_COUNT][MAX_WEARABLES_PER_LAYERSET] =
+{
+ { WT_SHAPE, WT_SKIN, WT_HAIR, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_HEAD_BAKED
+ { WT_SHAPE, WT_SKIN, WT_SHIRT, WT_JACKET, WT_GLOVES, WT_UNDERSHIRT, WT_INVALID }, // TEX_UPPER_BAKED
+ { WT_SHAPE, WT_SKIN, WT_PANTS, WT_SHOES, WT_SOCKS, WT_JACKET, WT_UNDERPANTS }, // TEX_LOWER_BAKED
+ { WT_EYES, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID }, // TEX_EYES_BAKED
+ { WT_SKIRT, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID, WT_INVALID } // TEX_SKIRT_BAKED
+};
+
+const LLUUID BAKED_TEXTURE_HASH[BAKED_TEXTURE_COUNT] =
+{
+ LLUUID("18ded8d6-bcfc-e415-8539-944c0f5ea7a6"),
+ LLUUID("338c29e3-3024-4dbb-998d-7c04cf4fa88f"),
+ LLUUID("91b4a2c7-1b1a-ba16-9a16-1f8f8dcc1c3f"),
+ LLUUID("b2cf28af-b840-1071-3c6a-78085d8128b5"),
+ LLUUID("ea800387-ea1a-14e0-56cb-24f2022f969a")
+};
+
+//
+// Statics
+//
+BOOL LLAgent::sDebugDisplayTarget = FALSE;
+
+const F32 LLAgent::TYPING_TIMEOUT_SECS = 5.f;
+
+class LLAgentFriendObserver : public LLFriendObserver
+{
+public:
+ LLAgentFriendObserver() {}
+ virtual ~LLAgentFriendObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLAgentFriendObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLFriendObserver::POWERS)) != 0)
+ {
+ gAgent.friendsChanged();
+ }
+}
+
+// ************************************************************
+// Enabled this definition to compile a 'hacked' viewer that
+// locally believes the end user has godlike powers.
+// #define HACKED_GODLIKE_VIEWER
+// For a toggled version, see viewer.h for the
+// TOGGLE_HACKED_GODLIKE_VIEWER define, instead.
+// ************************************************************
+
+// Constructors and Destructors
+
+// JC - Please try to make this order match the order in the header
+// file. Otherwise it's hard to find variables that aren't initialized.
+//-----------------------------------------------------------------------------
+// LLAgent()
+//-----------------------------------------------------------------------------
+LLAgent::LLAgent()
+: mViewerPort(NET_USE_OS_ASSIGNED_PORT),
+ mDrawDistance( DEFAULT_FAR_PLANE ),
+ mAccess(SIM_ACCESS_PG),
+ mGroupPowers(0),
+ mGroupID(),
+ //mGroupInsigniaID(),
+ mMapOriginX(0),
+ mMapOriginY(0),
+ mMapWidth(0),
+ mMapHeight(0),
+ mLookAt(NULL),
+ mPointAt(NULL),
+ mInitialized(FALSE),
+ mNumPendingQueries(0),
+ mForceMouselook(FALSE),
+ mTeleportState( TELEPORT_NONE ),
+ mRegionp(NULL),
+
+ mAgentOriginGlobal(),
+ mPositionGlobal(),
+
+ mDistanceTraveled(0),
+ mLastPositionGlobal(LLVector3d::zero),
+
+ mAvatarObject(NULL),
+
+ mRenderState(0),
+ mTypingTimer(),
+
+ mCameraMode( CAMERA_MODE_THIRD_PERSON ),
+ mLastCameraMode( CAMERA_MODE_THIRD_PERSON ),
+ mViewsPushed(FALSE),
+
+ mbAlwaysRun(FALSE),
+ mShowAvatar(TRUE),
+
+ mCameraAnimating( FALSE ),
+ mAnimationCameraStartGlobal(),
+ mAnimationFocusStartGlobal(),
+ mAnimationTimer(),
+ mAnimationDuration(0.33f),
+ mCameraFOVZoomFactor(0.f),
+ mCameraCurrentFOVZoomFactor(0.f),
+ mCameraFocusOffset(),
+ mCameraOffsetDefault(),
+// mCameraOffsetNorm(),
+ mCameraCollidePlane(),
+ mCurrentCameraDistance(2.f), // meters, set in init()
+ mTargetCameraDistance(2.f),
+ mCameraZoomFraction(1.f), // deprecated
+ mThirdPersonHeadOffset(0.f, 0.f, 1.f),
+ mSitCameraEnabled(FALSE),
+ mFocusOnAvatar(TRUE),
+ mFocusGlobal(),
+ mFocusTargetGlobal(),
+ mFocusObject(NULL),
+ mFocusObjectOffset(),
+ mFocusDotRadius( 0.1f ), // meters
+ mTrackFocusObject(TRUE),
+
+ mFrameAgent(),
+
+ mCrouching(FALSE),
+ mIsBusy(FALSE),
+
+ // movement keys below
+
+ mControlFlags(0x00000000),
+ mbFlagsDirty(FALSE),
+ mbFlagsNeedReset(FALSE),
+
+ mbJump(FALSE),
+
+ mWanderTimer(),
+ mWanderTargetGlobal( LLVector3d::zero ),
+
+ mAutoPilot(FALSE),
+ mAutoPilotFlyOnStop(FALSE),
+ mAutoPilotTargetGlobal(),
+ mAutoPilotStopDistance(1.f),
+ mAutoPilotUseRotation(FALSE),
+ mAutoPilotTargetFacing(LLVector3::zero),
+ mAutoPilotTargetDist(0.f),
+ mAutoPilotFinishedCallback(NULL),
+ mAutoPilotCallbackData(NULL),
+
+
+ mEffectColor(0.f, 1.f, 1.f, 1.f),
+ mHaveHomePosition(FALSE),
+ mHomeRegionHandle( 0 ),
+ mNearChatRadius(10.f),
+ mGodLevel( GOD_NOT ),
+
+
+ mNextFidgetTime(0.f),
+ mCurrentFidget(0),
+ mFirstLogin(FALSE),
+ mGenderChosen(FALSE),
+ mAgentWearablesUpdateSerialNum(0),
+ mWearablesLoaded(FALSE),
+ mTextureCacheQueryID(0),
+ mAppearanceSerialNum(0)
+{
+
+ U32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ mControlsTakenCount[i] = 0;
+ mControlsTakenPassedOnCount[i] = 0;
+ }
+
+ // Initialize movement keys
+ mAtKey = 0; // Either 1, 0, or -1... indicates that movement-key is pressed
+ mWalkKey = 0; // like AtKey, but causes less forward thrust
+ mLeftKey = 0;
+ mUpKey = 0;
+ mYawKey = 0.f;
+ mPitchKey = 0;
+
+ mOrbitLeftKey = 0.f;
+ mOrbitRightKey = 0.f;
+ mOrbitUpKey = 0.f;
+ mOrbitDownKey = 0.f;
+ mOrbitInKey = 0.f;
+ mOrbitOutKey = 0.f;
+
+ mPanUpKey = 0.f;
+ mPanDownKey = 0.f;
+ mPanLeftKey = 0.f;
+ mPanRightKey = 0.f;
+ mPanInKey = 0.f;
+ mPanOutKey = 0.f;
+
+ mActiveCacheQueries = new S32[BAKED_TEXTURE_COUNT];
+ for (i = 0; i < (U32)BAKED_TEXTURE_COUNT; i++)
+ {
+ mActiveCacheQueries[i] = 0;
+ }
+
+ //Ventrella
+ mCameraUpVector = LLVector3::z_axis;// default is straight up
+ mFollowCam.setMaxCameraDistantFromSubject( MAX_CAMERA_DISTANCE_FROM_AGENT );
+ //end ventrella
+}
+
+// Requires gSavedSettings to be initialized.
+//-----------------------------------------------------------------------------
+// init()
+//-----------------------------------------------------------------------------
+void LLAgent::init()
+{
+ mDrawDistance = gSavedSettings.getF32("RenderFarClip");
+
+ gCamera = new LLViewerCamera();
+
+ gCamera->setView(DEFAULT_FIELD_OF_VIEW);
+ // Leave at 0.1 meters until we have real near clip management
+ gCamera->setNear(0.1f);
+ gCamera->setFar(mDrawDistance); // if you want to change camera settings, do so in camera.h
+ gCamera->setAspect( gViewerWindow->getDisplayAspectRatio() ); // default, overridden in LLViewerWindow::reshape
+ gCamera->setViewHeightInPixels(768); // default, overridden in LLViewerWindow::reshape
+
+ setFlying( gSavedSettings.getBOOL("FlyingAtExit") );
+
+ mCameraFocusOffsetTarget = LLVector4(gSavedSettings.getVector3("CameraOffsetBuild"));
+ mCameraOffsetDefault = gSavedSettings.getVector3("CameraOffsetDefault");
+// mCameraOffsetNorm = mCameraOffsetDefault;
+// mCameraOffsetNorm.normVec();
+ mCameraCollidePlane.clearVec();
+ mCurrentCameraDistance = mCameraOffsetDefault.magVec();
+ mTargetCameraDistance = mCurrentCameraDistance;
+ mCameraZoomFraction = 1.f;
+ mTrackFocusObject = gSavedSettings.getBOOL("TrackFocusObject");
+
+// LLDebugVarMessageBox::show("Camera Lag", &CAMERA_FOCUS_HALF_LIFE, 0.5f, 0.01f);
+ gSavedSettings.getControl("RenderHideGroupTitle")->addListener(&mHideGroupTitleListener);
+ gSavedSettings.getControl("EffectColor")->addListener(&mEffectColorListener);
+
+ mEffectColor = gSavedSettings.getColor4("EffectColor");
+
+ mInitialized = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// cleanup()
+//-----------------------------------------------------------------------------
+void LLAgent::cleanup()
+{
+ setSitCamera(LLUUID::null);
+ mAvatarObject = NULL;
+ mLookAt = NULL;
+ mPointAt = NULL;
+ mRegionp = NULL;
+ setFocusObject(NULL);
+ mFadeObjects.clear();
+}
+
+//-----------------------------------------------------------------------------
+// LLAgent()
+//-----------------------------------------------------------------------------
+LLAgent::~LLAgent()
+{
+ cleanup();
+
+ delete [] mActiveCacheQueries;
+ mActiveCacheQueries = NULL;
+
+ delete gCamera;
+ gCamera = NULL;
+}
+
+// Change camera back to third person, stop the autopilot,
+// deselect stuff, etc.
+//-----------------------------------------------------------------------------
+// resetView()
+//-----------------------------------------------------------------------------
+void LLAgent::resetView(BOOL reset_camera)
+{
+ if (mAutoPilot)
+ {
+ stopAutoPilot(TRUE);
+ }
+
+ if (!gNoRender)
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->unhighlightAll();
+
+ // By popular request, keep land selection while walking around. JC
+ // gParcelMgr->deselectLand();
+
+ // Hide all popup menus
+ gPieSelf->hide(FALSE);
+ gPieAvatar->hide(FALSE);
+ gPieObject->hide(FALSE);
+ gPieLand->hide(FALSE);
+ }
+
+ if (reset_camera && !gSavedSettings.getBOOL("FreezeTime"))
+ {
+ if (!gViewerWindow->getLeftMouseDown() && cameraThirdPerson())
+ {
+ // leaving mouse-steer mode
+ LLVector3 agent_at_axis = getAtAxis();
+ agent_at_axis -= projected_vec(agent_at_axis, getReferenceUpVector());
+ agent_at_axis.normVec();
+ gAgent.resetAxes(lerp(getAtAxis(), agent_at_axis, LLCriticalDamp::getInterpolant(0.3f)));
+ }
+
+ setFocusOnAvatar(TRUE, ANIMATE);
+ }
+
+ if (mAvatarObject.notNull())
+ {
+ mAvatarObject->mHUDTargetZoom = 1.f;
+ }
+}
+
+void LLAgent::ageChat()
+{
+ if (mAvatarObject)
+ {
+ // get amount of time since I last chatted
+ F64 elapsed_time = (F64)mAvatarObject->mChatTimer.getElapsedTimeF32();
+ // add in frame time * 3 (so it ages 4x)
+ mAvatarObject->mChatTimer.setAge(elapsed_time + (F64)gFrameDTClamped * (CHAT_AGE_FAST_RATE - 1.0));
+ }
+}
+
+// Allow camera to be moved somewhere other than behind avatar.
+//-----------------------------------------------------------------------------
+// unlockView()
+//-----------------------------------------------------------------------------
+void LLAgent::unlockView()
+{
+ if (getFocusOnAvatar())
+ {
+ if (mAvatarObject)
+ {
+ setFocusGlobal( LLVector3d::zero, mAvatarObject->mID );
+ }
+ setFocusOnAvatar(FALSE, FALSE); // no animation
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// moveAt()
+//-----------------------------------------------------------------------------
+void LLAgent::moveAt(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mAtKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_AT_POS | AGENT_CONTROL_FAST_AT);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_AT_NEG | AGENT_CONTROL_FAST_AT);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveAtNudge()
+//-----------------------------------------------------------------------------
+void LLAgent::moveAtNudge(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mWalkKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_AT_POS);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_AT_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveLeft()
+//-----------------------------------------------------------------------------
+void LLAgent::moveLeft(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mLeftKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_LEFT_POS | AGENT_CONTROL_FAST_LEFT);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_LEFT_NEG | AGENT_CONTROL_FAST_LEFT);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveLeftNudge()
+//-----------------------------------------------------------------------------
+void LLAgent::moveLeftNudge(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mLeftKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_LEFT_POS);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_NUDGE_LEFT_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveUp()
+//-----------------------------------------------------------------------------
+void LLAgent::moveUp(S32 direction)
+{
+ // age chat timer so it fades more quickly when you are intentionally moving
+ ageChat();
+
+ setKey(direction, mUpKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_UP_POS | AGENT_CONTROL_FAST_UP);
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_UP_NEG | AGENT_CONTROL_FAST_UP);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// moveYaw()
+//-----------------------------------------------------------------------------
+void LLAgent::moveYaw(F32 mag)
+{
+ mYawKey = mag;
+
+ if (mag > 0)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else if (mag < 0)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+
+ resetView();
+}
+
+//-----------------------------------------------------------------------------
+// movePitch()
+//-----------------------------------------------------------------------------
+void LLAgent::movePitch(S32 direction)
+{
+ setKey(direction, mPitchKey);
+
+ if (direction > 0)
+ {
+ setControlFlags(AGENT_CONTROL_PITCH_POS );
+ }
+ else if (direction < 0)
+ {
+ setControlFlags(AGENT_CONTROL_PITCH_NEG);
+ }
+}
+
+
+// Does this parcel allow you to fly?
+BOOL LLAgent::canFly()
+{
+ if (isGodlike()) return TRUE;
+
+ if (!gParcelMgr) return FALSE;
+
+ LLViewerRegion* regionp = getRegion();
+ if (regionp && regionp->getBlockFly()) return FALSE;
+
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (!parcel) return FALSE;
+
+ // Allow owners to fly on their own land.
+ if (LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_ALLOW_FLY))
+ {
+ return TRUE;
+ }
+
+ return parcel->getAllowFly();
+}
+
+
+//-----------------------------------------------------------------------------
+// setFlying()
+//-----------------------------------------------------------------------------
+void LLAgent::setFlying(BOOL fly)
+{
+ if (mAvatarObject.notNull())
+ {
+ if(mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_STANDUP) != mAvatarObject->mSignaledAnimations.end())
+ {
+ return;
+ }
+
+ // don't allow taking off while sitting
+ if (fly && mAvatarObject->mIsSitting)
+ {
+ return;
+ }
+ }
+
+ if (fly)
+ {
+ BOOL was_flying = getFlying();
+ if (gParcelMgr)
+ {
+ if (!canFly() && !was_flying)
+ {
+ // parcel doesn't let you start fly
+ // gods can always fly
+ // and it's OK if you're already flying
+ make_ui_sound("UISndBadKeystroke");
+ return;
+ }
+ }
+ if( !was_flying )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_FLY_COUNT);
+ }
+ setControlFlags(AGENT_CONTROL_FLY);
+ gSavedSettings.setBOOL("FlyBtnState", TRUE);
+ }
+ else
+ {
+ clearControlFlags(AGENT_CONTROL_FLY);
+ gSavedSettings.setBOOL("FlyBtnState", FALSE);
+ }
+ mbFlagsDirty = TRUE;
+}
+
+
+// UI based mechanism of setting fly state
+//-----------------------------------------------------------------------------
+// toggleFlying()
+//-----------------------------------------------------------------------------
+void LLAgent::toggleFlying()
+{
+ BOOL fly = !(mControlFlags & AGENT_CONTROL_FLY);
+
+ setFlying( fly );
+ resetView();
+}
+
+
+//-----------------------------------------------------------------------------
+// setRegion()
+//-----------------------------------------------------------------------------
+void LLAgent::setRegion(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+ if (mRegionp != regionp)
+ {
+ // JC - Avoid this, causes out-of-bounds array write deep within
+ // Windows.
+ // char host_name[MAX_STRING];
+ // regionp->getHost().getHostName(host_name, MAX_STRING);
+
+ char ip[MAX_STRING];
+ regionp->getHost().getString(ip, MAX_STRING);
+ llinfos << "Moving agent into region: " << regionp->getName()
+ << " located at " << ip << llendl;
+ if (mRegionp)
+ {
+ // We've changed regions, we're now going to change our agent coordinate frame.
+ mAgentOriginGlobal = regionp->getOriginGlobal();
+ LLVector3d agent_offset_global = mRegionp->getOriginGlobal();
+
+ LLVector3 delta;
+ delta.setVec(regionp->getOriginGlobal() - mRegionp->getOriginGlobal());
+
+ setPositionAgent(getPositionAgent() - delta);
+ LLVector3 camera_position_agent = gCamera->getOrigin();
+ gCamera->setOrigin(camera_position_agent - delta);
+
+ // Update all of the regions.
+ gWorldPointer->updateAgentOffset(agent_offset_global);
+
+ // Hack to keep sky in the agent's region, otherwise it may get deleted - DJS 08/02/02
+ if (gSky.mVOSkyp)
+ {
+ gSky.mVOSkyp->setRegion(regionp);
+ }
+ if (gSky.mVOStarsp)
+ {
+ gSky.mVOStarsp->setRegion(regionp);
+ }
+ if (gSky.mVOGroundp)
+ {
+ gSky.mVOGroundp->setRegion(regionp);
+ }
+
+ }
+ else
+ {
+ // First time initialization.
+ // We've changed regions, we're now going to change our agent coordinate frame.
+ mAgentOriginGlobal = regionp->getOriginGlobal();
+
+ LLVector3 delta;
+ delta.setVec(regionp->getOriginGlobal());
+
+ setPositionAgent(getPositionAgent() - delta);
+ LLVector3 camera_position_agent = gCamera->getOrigin();
+ gCamera->setOrigin(camera_position_agent - delta);
+
+ // Update all of the regions.
+ gWorldPointer->updateAgentOffset(mAgentOriginGlobal);
+ }
+ }
+ mRegionp = regionp;
+
+ // Must shift hole-covering water object locations because local
+ // coordinate frame changed.
+ gWorldPointer->updateWaterObjects();
+
+ // keep a list of regions we've been too
+ // this is just an interesting stat, logged at the dataserver
+ // we could trake this at the dataserver side, but that's harder
+ U64 handle = regionp->getHandle();
+ mRegionsVisited.insert(handle);
+}
+
+
+//-----------------------------------------------------------------------------
+// getRegion()
+//-----------------------------------------------------------------------------
+LLViewerRegion *LLAgent::getRegion() const
+{
+ return mRegionp;
+}
+
+
+const LLHost& LLAgent::getRegionHost() const
+{
+ if (mRegionp)
+ {
+ return mRegionp->getHost();
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// inPrelude()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::inPrelude()
+{
+ return mRegionp && mRegionp->isPrelude();
+}
+
+
+//-----------------------------------------------------------------------------
+// canManageEstate()
+//-----------------------------------------------------------------------------
+
+BOOL LLAgent::canManageEstate() const
+{
+ return mRegionp && mRegionp->canManageEstate();
+}
+//-----------------------------------------------------------------------------
+// sendMessage()
+//-----------------------------------------------------------------------------
+void LLAgent::sendMessage()
+{
+ if (gDisconnected)
+ {
+ llwarns << "Trying to send message when disconnected!" << llendl;
+ return;
+ }
+ if (!mRegionp)
+ {
+ llerrs << "No region for agent yet!" << llendl;
+ }
+ gMessageSystem->sendMessage(mRegionp->getHost());
+}
+
+
+//-----------------------------------------------------------------------------
+// sendReliableMessage()
+//-----------------------------------------------------------------------------
+void LLAgent::sendReliableMessage()
+{
+ if (gDisconnected)
+ {
+ llwarns << "Trying to send message when disconnected!" << llendl;
+ return;
+ }
+ if (!mRegionp)
+ {
+ llwarns << "LLAgent::sendReliableMessage No region for agent yet, not sending message!" << llendl;
+ return;
+ }
+ gMessageSystem->sendReliable(mRegionp->getHost());
+}
+
+//-----------------------------------------------------------------------------
+// getVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getVelocity() const
+{
+ if (mAvatarObject)
+ {
+ return mAvatarObject->getVelocity();
+ }
+ else
+ {
+ return LLVector3::zero;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setPositionAgent()
+//-----------------------------------------------------------------------------
+void LLAgent::setPositionAgent(const LLVector3 &pos_agent)
+{
+ if (!pos_agent.isFinite())
+ {
+ llerrs << "setPositionAgent is not a number" << llendl;
+ }
+
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLVector3 pos_agent_sitting;
+ LLVector3d pos_agent_d;
+ LLViewerObject *parent = (LLViewerObject*)mAvatarObject->getParent();
+
+ pos_agent_sitting = mAvatarObject->getPosition() * parent->getRotation() + parent->getPositionAgent();
+ pos_agent_d.setVec(pos_agent_sitting);
+
+ mFrameAgent.setOrigin(pos_agent_sitting);
+ mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
+ }
+ else
+ {
+ mFrameAgent.setOrigin(pos_agent);
+
+ LLVector3d pos_agent_d;
+ pos_agent_d.setVec(pos_agent);
+ mPositionGlobal = pos_agent_d + mAgentOriginGlobal;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// slamLookAt()
+//-----------------------------------------------------------------------------
+void LLAgent::slamLookAt(const LLVector3 &look_at)
+{
+ LLVector3 look_at_norm = look_at;
+ look_at_norm.mV[VZ] = 0.f;
+ look_at_norm.normVec();
+ resetAxes(look_at_norm);
+}
+
+//-----------------------------------------------------------------------------
+// getPositionGlobal()
+//-----------------------------------------------------------------------------
+const LLVector3d &LLAgent::getPositionGlobal()
+{
+ if (!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull())
+ {
+ mPositionGlobal = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());
+ }
+ else
+ {
+ mPositionGlobal = getPosGlobalFromAgent(mFrameAgent.getOrigin());
+ }
+
+ return mPositionGlobal;
+}
+
+//-----------------------------------------------------------------------------
+// getPositionAgent()
+//-----------------------------------------------------------------------------
+const LLVector3 &LLAgent::getPositionAgent()
+{
+ if(!mAvatarObject.isNull() && !mAvatarObject->mDrawable.isNull())
+ {
+ mFrameAgent.setOrigin(mAvatarObject->getRenderPosition());
+ }
+
+ return mFrameAgent.getOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// getRegionsVisited()
+//-----------------------------------------------------------------------------
+const S32 LLAgent::getRegionsVisited() const
+{
+ return mRegionsVisited.size();
+}
+
+//-----------------------------------------------------------------------------
+// getDistanceTraveled()
+//-----------------------------------------------------------------------------
+const F64 LLAgent::getDistanceTraveled() const
+{
+ return mDistanceTraveled;
+}
+
+
+//-----------------------------------------------------------------------------
+// getPosAgentFromGlobal()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getPosAgentFromGlobal(const LLVector3d &pos_global) const
+{
+ LLVector3 pos_agent;
+ pos_agent.setVec(pos_global - mAgentOriginGlobal);
+ return pos_agent;
+}
+
+
+//-----------------------------------------------------------------------------
+// getPosGlobalFromAgent()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::getPosGlobalFromAgent(const LLVector3 &pos_agent) const
+{
+ LLVector3d pos_agent_d;
+ pos_agent_d.setVec(pos_agent);
+ return pos_agent_d + mAgentOriginGlobal;
+}
+
+
+//-----------------------------------------------------------------------------
+// resetAxes()
+//-----------------------------------------------------------------------------
+void LLAgent::resetAxes()
+{
+ mFrameAgent.resetAxes();
+}
+
+
+// Copied from LLCamera::setOriginAndLookAt
+// Look_at must be unit vector
+//-----------------------------------------------------------------------------
+// resetAxes()
+//-----------------------------------------------------------------------------
+void LLAgent::resetAxes(const LLVector3 &look_at)
+{
+ LLVector3 skyward = getReferenceUpVector();
+
+ // if look_at has zero length, fail
+ // if look_at and skyward are parallel, fail
+ //
+ // Test both of these conditions with a cross product.
+ LLVector3 cross(look_at % skyward);
+ if (cross.isNull())
+ {
+ llinfos << "LLAgent::resetAxes cross-product is zero" << llendl;
+ return;
+ }
+
+ // Make sure look_at and skyward are not parallel
+ // and neither are zero length
+ LLVector3 left(skyward % look_at);
+ LLVector3 up(look_at % left);
+
+ mFrameAgent.setAxes(look_at, left, up);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(F32 angle, const LLVector3 &axis)
+{
+ mFrameAgent.rotate(angle, axis);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(F32 angle, F32 x, F32 y, F32 z)
+{
+ mFrameAgent.rotate(angle, x, y, z);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(const LLMatrix3 &matrix)
+{
+ mFrameAgent.rotate(matrix);
+}
+
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLAgent::rotate(const LLQuaternion &quaternion)
+{
+ mFrameAgent.rotate(quaternion);
+}
+
+
+//-----------------------------------------------------------------------------
+// getReferenceUpVector()
+//-----------------------------------------------------------------------------
+LLVector3 LLAgent::getReferenceUpVector()
+{
+ // this vector is in the coordinate frame of the avatar's parent object, or the world if none
+ LLVector3 up_vector = LLVector3::z_axis;
+ if (mAvatarObject.notNull() &&
+ mAvatarObject->getParent() &&
+ mAvatarObject->mDrawable.notNull())
+ {
+ U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode;
+ // and in third person...
+ if (camera_mode == CAMERA_MODE_THIRD_PERSON)
+ {
+ // make the up vector point to the absolute +z axis
+ up_vector = up_vector * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ }
+ else if (camera_mode == CAMERA_MODE_MOUSELOOK)
+ {
+ // make the up vector point to the avatar's +z axis
+ up_vector = up_vector * mAvatarObject->mDrawable->getRotation();
+ }
+ }
+
+ return up_vector;
+}
+
+
+// Radians, positive is forward into ground
+//-----------------------------------------------------------------------------
+// pitch()
+//-----------------------------------------------------------------------------
+void LLAgent::pitch(F32 angle)
+{
+ // don't let user pitch if pointed almost all the way down or up
+
+ // A dot B = mag(A) * mag(B) * cos(angle between A and B)
+ // so... cos(angle between A and B) = A dot B / mag(A) / mag(B)
+ // = A dot B for unit vectors
+
+ LLVector3 skyward = getReferenceUpVector();
+
+ F32 look_down_limit;
+ F32 look_up_limit = 10.f * DEG_TO_RAD;
+
+ F32 angle_from_skyward = acos( mFrameAgent.getAtAxis() * skyward );
+
+ if (mAvatarObject.notNull() && mAvatarObject->mIsSitting)
+ {
+ look_down_limit = 130.f * DEG_TO_RAD;
+ }
+ else
+ {
+ look_down_limit = 170.f * DEG_TO_RAD;
+ }
+
+ // clamp pitch to limits
+ if ((angle >= 0.f) && (angle_from_skyward + angle > look_down_limit))
+ {
+ angle = look_down_limit - angle_from_skyward;
+ }
+ else if ((angle < 0.f) && (angle_from_skyward + angle < look_up_limit))
+ {
+ angle = look_up_limit - angle_from_skyward;
+ }
+
+ mFrameAgent.pitch(angle);
+}
+
+
+//-----------------------------------------------------------------------------
+// roll()
+//-----------------------------------------------------------------------------
+void LLAgent::roll(F32 angle)
+{
+ mFrameAgent.roll(angle);
+}
+
+
+//-----------------------------------------------------------------------------
+// yaw()
+//-----------------------------------------------------------------------------
+void LLAgent::yaw(F32 angle)
+{
+ if (!rotateGrabbed())
+ {
+ mFrameAgent.rotate(angle, getReferenceUpVector());
+ }
+}
+
+
+// Returns a quat that represents the rotation of the agent in the absolute frame
+//-----------------------------------------------------------------------------
+// getQuat()
+//-----------------------------------------------------------------------------
+LLQuaternion LLAgent::getQuat() const
+{
+ return mFrameAgent.getQuaternion();
+}
+
+
+//-----------------------------------------------------------------------------
+// calcFocusOffset()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcFocusOffset(LLViewerObject *object, S32 x, S32 y)
+{
+ // calculate offset based on view direction
+ BOOL is_avatar = object->isAvatar();
+ LLMatrix4 obj_matrix = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldMatrix() : object->getRenderMatrix();
+ LLQuaternion obj_rot = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldRotation() : object->getRenderRotation();
+ LLVector3 obj_pos = is_avatar ? ((LLVOAvatar*)object)->mPelvisp->getWorldPosition() : object->getRenderPosition();
+ LLQuaternion inv_obj_rot = ~obj_rot;
+
+ LLVector3 obj_dir_abs = obj_pos - gCamera->getOrigin();
+ obj_dir_abs.rotVec(inv_obj_rot);
+ obj_dir_abs.normVec();
+ obj_dir_abs.abs();
+
+ LLVector3 object_extents = object->getScale();
+ // make sure they object extents are non-zero
+ object_extents.clamp(0.001f, F32_MAX);
+ LLVector3 object_half_extents = object_extents * 0.5f;
+
+ obj_dir_abs.mV[VX] = obj_dir_abs.mV[VX] / object_extents.mV[VX];
+ obj_dir_abs.mV[VY] = obj_dir_abs.mV[VY] / object_extents.mV[VY];
+ obj_dir_abs.mV[VZ] = obj_dir_abs.mV[VZ] / object_extents.mV[VZ];
+
+ LLVector3 normal;
+ if (obj_dir_abs.mV[VX] > obj_dir_abs.mV[VY] && obj_dir_abs.mV[VX] > obj_dir_abs.mV[VZ])
+ {
+ normal.setVec(obj_matrix.getFwdRow4());
+ }
+ else if (obj_dir_abs.mV[VY] > obj_dir_abs.mV[VZ])
+ {
+ normal.setVec(obj_matrix.getLeftRow4());
+ }
+ else
+ {
+ normal.setVec(obj_matrix.getUpRow4());
+ }
+ normal.normVec();
+
+ LLVector3d focus_pt_global;
+ // RN: should we check return value for valid pick?
+ gViewerWindow->mousePointOnPlaneGlobal(focus_pt_global, x, y, gAgent.getPosGlobalFromAgent(obj_pos), normal);
+ LLVector3 focus_pt = gAgent.getPosAgentFromGlobal(focus_pt_global);
+ // find vector from camera to focus point in object coordinates
+ LLVector3 camera_focus_vec = focus_pt - gCamera->getOrigin();
+ // convert to object-local space
+ camera_focus_vec.rotVec(inv_obj_rot);
+
+ // find vector from object origin to focus point in object coordinates
+ LLVector3 focus_delta = focus_pt - obj_pos;
+ // convert to object-local space
+ focus_delta.rotVec(inv_obj_rot);
+
+ // calculate clip percentage needed to get focus offset back in bounds along the camera_focus axis
+ LLVector3 clip_fraction;
+
+ for (U32 axis = VX; axis <= VZ; axis++)
+ {
+ F32 clip_amt;
+ if (focus_delta.mV[axis] > 0.f)
+ {
+ clip_amt = llmax(0.f, focus_delta.mV[axis] - object_half_extents.mV[axis]);
+ }
+ else
+ {
+ clip_amt = llmin(0.f, focus_delta.mV[axis] + object_half_extents.mV[axis]);
+ }
+
+ // don't divide by very small nunber
+ if (llabs(camera_focus_vec.mV[axis]) < 0.0001f)
+ {
+ clip_fraction.mV[axis] = 0.f;
+ }
+ else
+ {
+ clip_fraction.mV[axis] = clip_amt / camera_focus_vec.mV[axis];
+ }
+ }
+
+ LLVector3 abs_clip_fraction = clip_fraction;
+ abs_clip_fraction.abs();
+
+ // find greatest shrinkage factor and
+ // rescale focus offset to inside object extents
+ if (abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VY] &&
+ abs_clip_fraction.mV[VX] > abs_clip_fraction.mV[VZ])
+ {
+ focus_delta -= clip_fraction.mV[VX] * camera_focus_vec;
+ }
+ else if (abs_clip_fraction.mV[VY] > abs_clip_fraction.mV[VZ])
+ {
+ focus_delta -= clip_fraction.mV[VY] * camera_focus_vec;
+ }
+ else
+ {
+ focus_delta -= clip_fraction.mV[VZ] * camera_focus_vec;
+ }
+
+ // convert back to world space
+ focus_delta.rotVec(obj_rot);
+
+ if (!is_avatar)
+ {
+ //unproject relative clicked coordinate from window coordinate using GL
+ glPushMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glLoadMatrixf((const GLfloat*) gCamera->getProjection().mMatrix);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadMatrixf((const GLfloat*) gCamera->getModelview().mMatrix);
+ glMultMatrixf((const GLfloat*) obj_matrix.mMatrix);
+
+ GLint viewport[4];
+ GLdouble modelview[16];
+ GLdouble projection[16];
+ GLfloat winX, winY, winZ;
+ GLdouble posX, posY, posZ;
+
+ glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
+ glGetDoublev( GL_PROJECTION_MATRIX, projection );
+ glGetIntegerv( GL_VIEWPORT, viewport );
+
+ winX = ((F32)x) * gViewerWindow->getDisplayScale().mV[VX];
+ winY = ((F32)y) * gViewerWindow->getDisplayScale().mV[VY];
+ glReadPixels( llfloor(winX), llfloor(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
+
+ gluUnProject( winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
+
+ glPopMatrix();
+
+ LLVector3 obj_rel = LLVector3((F32)posX, (F32)posY, (F32)posZ);
+ LLVector3 obj_center = LLVector3(0, 0, 0);
+ obj_rel = obj_rel * object->getRenderMatrix(); //mDrawable->getWorldMatrix();
+ obj_center = obj_center * object->getRenderMatrix();//mDrawable->getWorldMatrix();
+ obj_rel -= object->getRenderPosition();//mDrawable->getWorldPosition();
+
+ //now that we have the object relative position, we should bias toward the center of the object
+ //based on the distance of the camera to the focus point vs. the distance of the camera to the focus
+
+ F32 relDist = llabs(obj_rel * gCamera->getAtAxis());
+ F32 viewDist = dist_vec(obj_center + obj_rel, gCamera->getOrigin());
+
+
+ LLBBox obj_bbox = object->getBoundingBoxAgent();
+ F32 bias = 0.f;
+
+ LLVector3 virtual_camera_pos = gAgent.getPosAgentFromGlobal(mFocusTargetGlobal + (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor));
+
+ if(obj_bbox.containsPointAgent(virtual_camera_pos))
+ {
+ // if the camera is inside the object (large, hollow objects, for example)
+ // force focus point all the way to destination depth, away from object center
+ bias = 1.f;
+ }
+ else
+ {
+ // perform magic number biasing of focus point towards surface vs. planar center
+ bias = clamp_rescale(relDist/viewDist, 0.1f, 0.7f, 0.0f, 1.0f);
+ }
+
+ obj_rel = lerp(focus_delta, obj_rel, bias);
+
+ return LLVector3d(obj_rel);
+ }
+
+ return LLVector3d(focus_delta.mV[VX], focus_delta.mV[VY], focus_delta.mV[VZ]);
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraMinDistance()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::calcCameraMinDistance(F32 &obj_min_distance)
+{
+ BOOL soft_limit = FALSE; // is the bounding box to be treated literally (volumes) or as an approximation (avatars)
+
+ if (!mFocusObject || mFocusObject->isDead())
+ {
+ obj_min_distance = 0.f;
+ return TRUE;
+ }
+
+ if (mFocusObject->mDrawable.isNull())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Focus object with no drawable!" << llendl;
+#else
+ mFocusObject->dump();
+ llerrs << "Focus object with no drawable!" << llendl;
+#endif
+ obj_min_distance = 0.f;
+ return TRUE;
+ }
+
+ LLQuaternion inv_object_rot = ~mFocusObject->getRenderRotation();
+ LLVector3 target_offset_origin = mFocusObjectOffset;
+ LLVector3 camera_offset_target(getCameraPositionAgent() - getPosAgentFromGlobal(mFocusTargetGlobal));
+
+ // convert offsets into object local space
+ camera_offset_target.rotVec(inv_object_rot);
+ target_offset_origin.rotVec(inv_object_rot);
+
+ // push around object extents based on target offset
+ LLVector3 object_extents = mFocusObject->getScale();
+ if (mFocusObject->isAvatar())
+ {
+ // fudge factors that lets you zoom in on avatars a bit more (which don't do FOV zoom)
+ object_extents.mV[VX] *= AVATAR_ZOOM_MIN_X_FACTOR;
+ object_extents.mV[VY] *= AVATAR_ZOOM_MIN_Y_FACTOR;
+ object_extents.mV[VZ] *= AVATAR_ZOOM_MIN_Z_FACTOR;
+ soft_limit = TRUE;
+ }
+ LLVector3 abs_target_offset = target_offset_origin;
+ abs_target_offset.abs();
+
+ LLVector3 target_offset_dir = target_offset_origin;
+ F32 object_radius = mFocusObject->getVObjRadius();
+
+ BOOL target_outside_object_extents = FALSE;
+
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (abs_target_offset.mV[i] * 2.f > object_extents.mV[i] + OBJECT_EXTENTS_PADDING)
+ {
+ target_outside_object_extents = TRUE;
+ }
+ if (camera_offset_target.mV[i] > 0.f)
+ {
+ object_extents.mV[i] -= target_offset_origin.mV[i] * 2.f;
+ }
+ else
+ {
+ object_extents.mV[i] += target_offset_origin.mV[i] * 2.f;
+ }
+ }
+
+ // don't shrink the object extents so far that the object inverts
+ object_extents.clamp(0.001f, F32_MAX);
+
+ // move into first octant
+ LLVector3 camera_offset_target_abs_norm = camera_offset_target;
+ camera_offset_target_abs_norm.abs();
+ // make sure offset is non-zero
+ camera_offset_target_abs_norm.clamp(0.001f, F32_MAX);
+ camera_offset_target_abs_norm.normVec();
+
+ // find camera position relative to normalized object extents
+ LLVector3 camera_offset_target_scaled = camera_offset_target_abs_norm;
+ camera_offset_target_scaled.mV[VX] /= object_extents.mV[VX];
+ camera_offset_target_scaled.mV[VY] /= object_extents.mV[VY];
+ camera_offset_target_scaled.mV[VZ] /= object_extents.mV[VZ];
+
+ if (camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VY] &&
+ camera_offset_target_scaled.mV[VX] > camera_offset_target_scaled.mV[VZ])
+ {
+ if (camera_offset_target_abs_norm.mV[VX] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VX] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VX] * 0.5f / camera_offset_target_abs_norm.mV[VX];
+ }
+ }
+ else if (camera_offset_target_scaled.mV[VY] > camera_offset_target_scaled.mV[VZ])
+ {
+ if (camera_offset_target_abs_norm.mV[VY] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VY] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VY] * 0.5f / camera_offset_target_abs_norm.mV[VY];
+ }
+ }
+ else
+ {
+ if (camera_offset_target_abs_norm.mV[VZ] < 0.001f)
+ {
+ obj_min_distance = object_extents.mV[VZ] * 0.5f;
+ }
+ else
+ {
+ obj_min_distance = object_extents.mV[VZ] * 0.5f / camera_offset_target_abs_norm.mV[VZ];
+ }
+ }
+
+ LLVector3 object_split_axis;
+ LLVector3 target_offset_scaled = target_offset_origin;
+ target_offset_scaled.abs();
+ target_offset_scaled.normVec();
+ target_offset_scaled.mV[VX] /= object_extents.mV[VX];
+ target_offset_scaled.mV[VY] /= object_extents.mV[VY];
+ target_offset_scaled.mV[VZ] /= object_extents.mV[VZ];
+
+ if (target_offset_scaled.mV[VX] > target_offset_scaled.mV[VY] &&
+ target_offset_scaled.mV[VX] > target_offset_scaled.mV[VZ])
+ {
+ object_split_axis = LLVector3::x_axis;
+ }
+ else if (target_offset_scaled.mV[VY] > target_offset_scaled.mV[VZ])
+ {
+ object_split_axis = LLVector3::y_axis;
+ }
+ else
+ {
+ object_split_axis = LLVector3::z_axis;
+ }
+
+ LLVector3 camera_offset_object(getCameraPositionAgent() - mFocusObject->getPositionAgent());
+
+ // length projected orthogonal to target offset
+ F32 camera_offset_dist = (camera_offset_object - target_offset_dir * (camera_offset_object * target_offset_dir)).magVec();
+
+ // calculate whether the target point would be "visible" if it were outside the bounding box
+ // on the opposite of the splitting plane defined by object_split_axis;
+ BOOL exterior_target_visible = FALSE;
+ if (camera_offset_dist > object_radius)
+ {
+ // target is visible from camera, so turn off fov zoom
+ exterior_target_visible = TRUE;
+ }
+
+ F32 camera_offset_clip = camera_offset_object * object_split_axis;
+ F32 target_offset_clip = target_offset_dir * object_split_axis;
+
+ // target has moved outside of object extents
+ // check to see if camera and target are on same side
+ if (target_outside_object_extents)
+ {
+ if (camera_offset_clip > 0.f && target_offset_clip > 0.f)
+ {
+ return FALSE;
+ }
+ else if (camera_offset_clip < 0.f && target_offset_clip < 0.f)
+ {
+ return FALSE;
+ }
+ }
+
+ // clamp obj distance to diagonal of 10 by 10 cube
+ obj_min_distance = llmin(obj_min_distance, 10.f * F_SQRT3);
+
+ obj_min_distance += gCamera->getNear() + (soft_limit ? 0.1f : 0.2f);
+
+ return TRUE;
+}
+
+F32 LLAgent::getCameraZoomFraction()
+{
+ // 0.f -> camera zoomed all the way out
+ // 1.f -> camera zoomed all the way in
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // already [0,1]
+ return mAvatarObject->mHUDTargetZoom;
+ }
+ else if (mFocusOnAvatar && cameraThirdPerson())
+ {
+ return clamp_rescale(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION, 1.f, 0.f);
+ }
+ else if (cameraCustomizeAvatar())
+ {
+ F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
+ return clamp_rescale(distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM, 1.f, 0.f );
+ }
+ else
+ {
+ F32 min_zoom;
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_zoom = gWorldPointer ? llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE,
+ MAX_CAMERA_DISTANCE_FROM_AGENT) : MAX_CAMERA_DISTANCE_FROM_AGENT;
+
+ F32 distance = (F32)mCameraFocusOffsetTarget.magVec();
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+ else
+ {
+ min_zoom = LAND_MIN_ZOOM;
+ }
+
+ return clamp_rescale(distance, min_zoom, max_zoom, 1.f, 0.f);
+ }
+}
+
+void LLAgent::setCameraZoomFraction(F32 fraction)
+{
+ // 0.f -> camera zoomed all the way out
+ // 1.f -> camera zoomed all the way in
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mAvatarObject->mHUDTargetZoom = fraction;
+ }
+ else if (mFocusOnAvatar && cameraThirdPerson())
+ {
+ mCameraZoomFraction = rescale(fraction, 0.f, 1.f, MAX_ZOOM_FRACTION, MIN_ZOOM_FRACTION);
+ }
+ else if (cameraCustomizeAvatar())
+ {
+ LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
+ camera_offset_dir.normVec();
+ mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, APPEARANCE_MAX_ZOOM, APPEARANCE_MIN_ZOOM);
+ }
+ else
+ {
+ F32 min_zoom = LAND_MIN_ZOOM;
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_zoom = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE,
+ MAX_CAMERA_DISTANCE_FROM_AGENT);
+
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+ }
+
+ LLVector3d camera_offset_dir = mCameraFocusOffsetTarget;
+ camera_offset_dir.normVec();
+ mCameraFocusOffsetTarget = camera_offset_dir * rescale(fraction, 0.f, 1.f, max_zoom, min_zoom);
+ }
+ startCameraAnimation();
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraOrbitAround()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitAround(const F32 radians)
+{
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // do nothing for hud selection
+ }
+ else if (mFocusOnAvatar && (mCameraMode == CAMERA_MODE_THIRD_PERSON || mCameraMode == CAMERA_MODE_FOLLOW))
+ {
+ mFrameAgent.rotate(radians, getReferenceUpVector());
+ }
+ else
+ {
+ mCameraFocusOffsetTarget.rotVec(radians, 0.f, 0.f, 1.f);
+
+ cameraZoomIn(1.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraOrbitOver()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitOver(const F32 angle)
+{
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // do nothing for hud selection
+ }
+ else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ pitch(angle);
+ }
+ else
+ {
+ LLVector3 camera_offset_unit(mCameraFocusOffsetTarget);
+ camera_offset_unit.normVec();
+
+ F32 angle_from_up = acos( camera_offset_unit * getReferenceUpVector() );
+
+ LLVector3d left_axis;
+ left_axis.setVec(gCamera->getLeftAxis());
+ F32 new_angle = llclamp(angle_from_up - angle, 1.f * DEG_TO_RAD, 179.f * DEG_TO_RAD);
+ mCameraFocusOffsetTarget.rotVec(angle_from_up - new_angle, left_axis);
+
+ cameraZoomIn(1.f);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// cameraZoomIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraZoomIn(const F32 fraction)
+{
+ if (gDisconnected)
+ {
+ return;
+ }
+
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ // just update hud zoom level
+ mAvatarObject->mHUDTargetZoom /= fraction;
+ return;
+ }
+
+
+ LLVector3d camera_offset(mCameraFocusOffsetTarget);
+ LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
+ F32 min_zoom = LAND_MIN_ZOOM;
+ F32 current_distance = (F32)camera_offset_unit.normVec();
+ F32 new_distance = current_distance * fraction;
+
+ // Don't move through focus point
+ if (mFocusObject)
+ {
+ LLVector3 camera_offset_dir((F32)camera_offset_unit.mdV[VX], (F32)camera_offset_unit.mdV[VY], (F32)camera_offset_unit.mdV[VZ]);
+
+ if (mFocusObject->isAvatar())
+ {
+ calcCameraMinDistance(min_zoom);
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+
+ new_distance = llmax(new_distance, min_zoom);
+
+ // Don't zoom too far back
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_distance = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE );
+
+ if (new_distance > max_distance)
+ {
+ new_distance = max_distance;
+
+ /*
+ // Unless camera is unlocked
+ if (!LLViewerCamera::sDisableCameraConstraints)
+ {
+ return;
+ }
+ */
+ }
+
+ if( cameraCustomizeAvatar() )
+ {
+ new_distance = llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
+ }
+
+ mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
+}
+
+//-----------------------------------------------------------------------------
+// cameraOrbitIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraOrbitIn(const F32 meters)
+{
+ if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ F32 camera_offset_dist = llmax(0.001f, mCameraOffsetDefault.magVec());
+
+ mCameraZoomFraction = (mTargetCameraDistance - meters) / camera_offset_dist;
+
+ if (!gSavedSettings.getBOOL("FreezeTime") && mCameraZoomFraction < MIN_ZOOM_FRACTION && meters > 0.f)
+ {
+ // No need to animate, camera is already there.
+ changeCameraToMouselook(FALSE);
+ }
+
+ mCameraZoomFraction = llclamp(mCameraZoomFraction, MIN_ZOOM_FRACTION, MAX_ZOOM_FRACTION);
+ }
+ else
+ {
+ LLVector3d camera_offset(mCameraFocusOffsetTarget);
+ LLVector3d camera_offset_unit(mCameraFocusOffsetTarget);
+ F32 current_distance = (F32)camera_offset_unit.normVec();
+ F32 new_distance = current_distance - meters;
+ F32 min_zoom = LAND_MIN_ZOOM;
+
+ // Don't move through focus point
+ if (mFocusObject.notNull())
+ {
+ if (mFocusObject->isAvatar())
+ {
+ min_zoom = AVATAR_MIN_ZOOM;
+ }
+ else
+ {
+ min_zoom = OBJECT_MIN_ZOOM;
+ }
+ }
+
+ new_distance = llmax(new_distance, min_zoom);
+
+ // Don't zoom too far back
+ const F32 DIST_FUDGE = 16.f; // meters
+ F32 max_distance = llmin(mDrawDistance - DIST_FUDGE,
+ gWorldPointer->getRegionWidthInMeters() - DIST_FUDGE );
+
+ if (new_distance > max_distance)
+ {
+ // Unless camera is unlocked
+ if (!LLViewerCamera::sDisableCameraConstraints)
+ {
+ return;
+ }
+ }
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode() )
+ {
+ llclamp( new_distance, APPEARANCE_MIN_ZOOM, APPEARANCE_MAX_ZOOM );
+ }
+
+ // Compute new camera offset
+ mCameraFocusOffsetTarget = new_distance * camera_offset_unit;
+ cameraZoomIn(1.f);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// cameraPanIn()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanIn(F32 meters)
+{
+ LLVector3d at_axis;
+ at_axis.setVec(gCamera->getAtAxis());
+
+ mFocusTargetGlobal += meters * at_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ // don't enforce zoom constraints as this is the only way for users to get past them easily
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// cameraPanLeft()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanLeft(F32 meters)
+{
+ LLVector3d left_axis;
+ left_axis.setVec(gCamera->getLeftAxis());
+
+ mFocusTargetGlobal += meters * left_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ cameraZoomIn(1.f);
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// cameraPanUp()
+//-----------------------------------------------------------------------------
+void LLAgent::cameraPanUp(F32 meters)
+{
+ LLVector3d up_axis;
+ up_axis.setVec(gCamera->getUpAxis());
+
+ mFocusTargetGlobal += meters * up_axis;
+ mFocusGlobal = mFocusTargetGlobal;
+ cameraZoomIn(1.f);
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// setKey()
+//-----------------------------------------------------------------------------
+void LLAgent::setKey(const S32 direction, S32 &key)
+{
+ if (direction > 0)
+ {
+ key = 1;
+ }
+ else if (direction < 0)
+ {
+ key = -1;
+ }
+ else
+ {
+ key = 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getControlFlags()
+//-----------------------------------------------------------------------------
+U32 LLAgent::getControlFlags()
+{
+/*
+ // HACK -- avoids maintenance of control flags when camera mode is turned on or off,
+ // only worries about it when the flags are measured
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ if ( !(mControlFlags & AGENT_CONTROL_MOUSELOOK) )
+ {
+ mControlFlags |= AGENT_CONTROL_MOUSELOOK;
+ }
+ }
+*/
+ return mControlFlags;
+}
+
+//-----------------------------------------------------------------------------
+// setControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::setControlFlags(U32 mask)
+{
+ mControlFlags |= mask;
+ mbFlagsDirty = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// clearControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::clearControlFlags(U32 mask)
+{
+ U32 old_flags = mControlFlags;
+ mControlFlags &= ~mask;
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// controlFlagsDirty()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::controlFlagsDirty() const
+{
+ return mbFlagsDirty;
+}
+
+//-----------------------------------------------------------------------------
+// enableControlFlagReset()
+//-----------------------------------------------------------------------------
+void LLAgent::enableControlFlagReset()
+{
+ mbFlagsNeedReset = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// resetControlFlags()
+//-----------------------------------------------------------------------------
+void LLAgent::resetControlFlags()
+{
+ if (mbFlagsNeedReset)
+ {
+ mbFlagsNeedReset = FALSE;
+ mbFlagsDirty = FALSE;
+ // reset all of the ephemeral flags
+ // some flags are managed elsewhere
+ mControlFlags &= AGENT_CONTROL_AWAY | AGENT_CONTROL_FLY | AGENT_CONTROL_MOUSELOOK;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setAFK()
+//-----------------------------------------------------------------------------
+void LLAgent::setAFK()
+{
+ // Drones can't go AFK
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!gAllowAFK)
+ {
+ return;
+ }
+
+ if (!gAgent.getRegion())
+ {
+ // Don't set AFK if we're not talking to a region yet.
+ return;
+ }
+
+ if (!(mControlFlags & AGENT_CONTROL_AWAY))
+ {
+ sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_START);
+ setControlFlags(AGENT_CONTROL_AWAY | AGENT_CONTROL_STOP);
+ gAwayTimer.start();
+ if (gAFKMenu)
+ {
+ gAFKMenu->setLabel("Set Not Away");
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearAFK()
+//-----------------------------------------------------------------------------
+void LLAgent::clearAFK()
+{
+ gAwayTriggerTimer.reset();
+
+ // Gods can sometimes get into away state (via gestures)
+ // without setting the appropriate control flag. JC
+ LLVOAvatar* av = mAvatarObject;
+ if (mControlFlags & AGENT_CONTROL_AWAY
+ || (av
+ && (av->mSignaledAnimations.find(ANIM_AGENT_AWAY) != av->mSignaledAnimations.end())))
+ {
+ sendAnimationRequest(ANIM_AGENT_AWAY, ANIM_REQUEST_STOP);
+ clearControlFlags(AGENT_CONTROL_AWAY);
+ if (gAFKMenu)
+ {
+ gAFKMenu->setLabel("Set Away");
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAFK()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::getAFK() const
+{
+ return (mControlFlags & AGENT_CONTROL_AWAY) != 0;
+}
+
+//-----------------------------------------------------------------------------
+// setBusy()
+//-----------------------------------------------------------------------------
+void LLAgent::setBusy()
+{
+ sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_START);
+ mIsBusy = TRUE;
+ if (gBusyMenu)
+ {
+ gBusyMenu->setLabel("Set Not Busy");
+ }
+ if (gFloaterMute)
+ {
+ gFloaterMute->updateButtons();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearBusy()
+//-----------------------------------------------------------------------------
+void LLAgent::clearBusy()
+{
+ mIsBusy = FALSE;
+ sendAnimationRequest(ANIM_AGENT_BUSY, ANIM_REQUEST_STOP);
+ if (gBusyMenu)
+ {
+ gBusyMenu->setLabel("Set Busy");
+ }
+ if (gFloaterMute)
+ {
+ gFloaterMute->updateButtons();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getBusy()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::getBusy() const
+{
+ return mIsBusy;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateWanderTarget()
+//-----------------------------------------------------------------------------
+void LLAgent::updateWanderTarget()
+{
+ S32 num_regions;
+ LLViewerRegion* rand_region;
+ F32 rand_x;
+ F32 rand_y;
+
+ if (mWanderTimer.checkExpirationAndReset(frand(MAX_WANDER_TIME)))
+ {
+ // Pick a random spot to wander towards
+ num_regions = gWorldPointer->mActiveRegionList.getLength();
+ S32 region_num = llround(frand(1.f) * num_regions);
+ rand_region = gWorldPointer->mActiveRegionList.getFirstData();
+ S32 i = 0;
+ while (i < region_num)
+ {
+ rand_region = gWorldPointer->mActiveRegionList.getNextData();
+ i++;
+ }
+ rand_x = frand(rand_region->getWidth());
+ rand_y = frand(rand_region->getWidth());
+
+ stopAutoPilot();
+ startAutoPilotGlobal(rand_region->getPosGlobalFromRegion(LLVector3(rand_x, rand_y, 0.f)));
+ }
+}
+
+//-----------------------------------------------------------------------------
+// startAutoPilotGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::startAutoPilotGlobal(const LLVector3d &target_global, const std::string& behavior_name, const LLQuaternion *target_rotation, void (*finish_callback)(BOOL, void *), void *callback_data, F32 stop_distance, F32 rot_threshold)
+{
+ if (!gAgent.getAvatarObject() || !gWorldPointer)
+ {
+ return;
+ }
+
+ mAutoPilotFinishedCallback = finish_callback;
+ mAutoPilotCallbackData = callback_data;
+ mAutoPilotRotationThreshold = rot_threshold;
+ mAutoPilotBehaviorName = behavior_name;
+
+ LLVector3d delta_pos( target_global );
+ delta_pos -= getPositionGlobal();
+ F64 distance = delta_pos.magVec();
+ LLVector3d trace_target = target_global;
+
+ trace_target.mdV[VZ] -= 10.f;
+
+ LLVector3d intersection;
+ LLVector3 normal;
+ LLViewerObject *hit_obj;
+ F32 heightDelta = gWorldPointer->resolveStepHeightGlobal(NULL, target_global, trace_target, intersection, normal, &hit_obj);
+
+ if (stop_distance > 0.f)
+ {
+ mAutoPilotStopDistance = stop_distance;
+ }
+ else
+ {
+ // Guess at a reasonable stop distance.
+ mAutoPilotStopDistance = fsqrtf( distance );
+ if (mAutoPilotStopDistance < 0.5f)
+ {
+ mAutoPilotStopDistance = 0.5f;
+ }
+ }
+
+ mAutoPilotFlyOnStop = getFlying();
+
+ if (distance > 30.0)
+ {
+ setFlying(TRUE);
+ }
+
+ if ( distance > 1.f && heightDelta > (sqrtf(mAutoPilotStopDistance) + 1.f))
+ {
+ setFlying(TRUE);
+ mAutoPilotFlyOnStop = TRUE;
+ }
+
+ mAutoPilot = TRUE;
+ mAutoPilotTargetGlobal = target_global;
+
+ // trace ray down to find height of destination from ground
+ LLVector3d traceEndPt = target_global;
+ traceEndPt.mdV[VZ] -= 20.f;
+
+ LLVector3d targetOnGround;
+ LLVector3 groundNorm;
+ LLViewerObject *obj;
+
+ gWorldPointer->resolveStepHeightGlobal(NULL, target_global, traceEndPt, targetOnGround, groundNorm, &obj);
+ F64 target_height = llmax((F64)gAgent.getAvatarObject()->getPelvisToFoot(), target_global.mdV[VZ] - targetOnGround.mdV[VZ]);
+
+ // clamp z value of target to minimum height above ground
+ mAutoPilotTargetGlobal.mdV[VZ] = targetOnGround.mdV[VZ] + target_height;
+ mAutoPilotTargetDist = (F32)dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal);
+ if (target_rotation)
+ {
+ mAutoPilotUseRotation = TRUE;
+ mAutoPilotTargetFacing = LLVector3::x_axis * *target_rotation;
+ mAutoPilotTargetFacing.mV[VZ] = 0.f;
+ mAutoPilotTargetFacing.normVec();
+ }
+ else
+ {
+ mAutoPilotUseRotation = FALSE;
+ }
+
+ mAutoPilotNoProgressFrameCount = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// startFollowPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::startFollowPilot(const LLUUID &leader_id)
+{
+ if (!mAutoPilot) return;
+
+ mLeaderID = leader_id;
+ if ( mLeaderID.isNull() ) return;
+
+ LLViewerObject* object = gObjectList.findObject(mLeaderID);
+ if (!object)
+ {
+ mLeaderID = LLUUID::null;
+ return;
+ }
+
+ startAutoPilotGlobal(object->getPositionGlobal());
+}
+
+
+//-----------------------------------------------------------------------------
+// stopAutoPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::stopAutoPilot(BOOL user_cancel)
+{
+ if (mAutoPilot)
+ {
+ mAutoPilot = FALSE;
+ if (mAutoPilotUseRotation && !user_cancel)
+ {
+ resetAxes(mAutoPilotTargetFacing);
+ }
+ //NB: auto pilot can terminate for a reason other than reaching the destination
+ //TODO: enforce rotation constraint here as well
+ if (mAutoPilotFinishedCallback &&
+ ((mAutoPilotTargetDist < mAutoPilotStopDistance) || (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)))
+ {
+ mAutoPilotFinishedCallback(!user_cancel && dist_vec(gAgent.getPositionGlobal(), mAutoPilotTargetGlobal) < mAutoPilotTargetDist, mAutoPilotCallbackData);
+ }
+ mLeaderID = LLUUID::null;
+
+ // If the user cancelled, don't change the fly state
+ if (!user_cancel)
+ {
+ setFlying(mAutoPilotFlyOnStop);
+ }
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ if (user_cancel && !mAutoPilotBehaviorName.empty())
+ {
+ if (mAutoPilotBehaviorName == "Sit")
+ LLNotifyBox::showXml("CancelledSit");
+ else if (mAutoPilotBehaviorName == "Attach")
+ LLNotifyBox::showXml("CancelledAttach");
+ else
+ LLNotifyBox::showXml("Cancelled");
+ }
+ }
+}
+
+
+// Returns necessary agent pitch and yaw changes, radians.
+//-----------------------------------------------------------------------------
+// autoPilot()
+//-----------------------------------------------------------------------------
+void LLAgent::autoPilot(F32 *delta_yaw)
+{
+ if (mAutoPilot)
+ {
+ if (!mLeaderID.isNull())
+ {
+ LLViewerObject* object = gObjectList.findObject(mLeaderID);
+ if (!object)
+ {
+ stopAutoPilot();
+ return;
+ }
+ mAutoPilotTargetGlobal = object->getPositionGlobal();
+ }
+
+ if (!mAvatarObject)
+ {
+ return;
+ }
+
+ if (mAvatarObject->mInAir)
+ {
+ setFlying(TRUE);
+ }
+
+ LLVector3 at;
+ at.setVec(mFrameAgent.getAtAxis());
+ LLVector3 target_agent = getPosAgentFromGlobal(mAutoPilotTargetGlobal);
+ LLVector3 direction = target_agent - getPositionAgent();
+
+ F32 target_dist = direction.magVec();
+
+ if (target_dist >= mAutoPilotTargetDist)
+ {
+ mAutoPilotNoProgressFrameCount++;
+ if (mAutoPilotNoProgressFrameCount > AUTOPILOT_MAX_TIME_NO_PROGRESS * gFPSClamped)
+ {
+ stopAutoPilot();
+ return;
+ }
+ }
+
+ mAutoPilotTargetDist = target_dist;
+
+ // Make this a two-dimensional solution
+ at.mV[VZ] = 0.f;
+ direction.mV[VZ] = 0.f;
+
+ at.normVec();
+ F32 xy_distance = direction.normVec();
+
+ F32 yaw = 0.f;
+ if (mAutoPilotTargetDist > mAutoPilotStopDistance)
+ {
+ yaw = angle_between(mFrameAgent.getAtAxis(), direction);
+ }
+ else if (mAutoPilotUseRotation)
+ {
+ // we're close now just aim at target facing
+ yaw = angle_between(at, mAutoPilotTargetFacing);
+ direction = mAutoPilotTargetFacing;
+ }
+
+ yaw = 4.f * yaw / gFPSClamped;
+
+ // figure out which direction to turn
+ LLVector3 scratch(at % direction);
+
+ if (scratch.mV[VZ] > 0.f)
+ {
+ setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else
+ {
+ yaw = -yaw;
+ setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+
+ *delta_yaw = yaw;
+
+ // Compute when to start slowing down and when to stop
+ F32 stop_distance = mAutoPilotStopDistance;
+ F32 slow_distance;
+ if (getFlying())
+ {
+ slow_distance = llmax(6.f, mAutoPilotStopDistance + 5.f);
+ stop_distance = llmax(2.f, mAutoPilotStopDistance);
+ }
+ else
+ {
+ slow_distance = llmax(3.f, mAutoPilotStopDistance + 2.f);
+ }
+
+ // If we're flying, handle autopilot points above or below you.
+ if (getFlying() && xy_distance < AUTOPILOT_HEIGHT_ADJUST_DISTANCE)
+ {
+ if (mAvatarObject)
+ {
+ F64 current_height = mAvatarObject->getPositionGlobal().mdV[VZ];
+ F32 delta_z = (F32)(mAutoPilotTargetGlobal.mdV[VZ] - current_height);
+ F32 slope = delta_z / xy_distance;
+ if (slope > 0.45f && delta_z > 6.f)
+ {
+ setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_POS);
+ }
+ else if (slope > 0.002f && delta_z > 0.5f)
+ {
+ setControlFlags(AGENT_CONTROL_UP_POS);
+ }
+ else if (slope < -0.45f && delta_z < -6.f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
+ {
+ setControlFlags(AGENT_CONTROL_FAST_UP | AGENT_CONTROL_UP_NEG);
+ }
+ else if (slope < -0.002f && delta_z < -0.5f && current_height > AUTOPILOT_MIN_TARGET_HEIGHT_OFF_GROUND)
+ {
+ setControlFlags(AGENT_CONTROL_UP_NEG);
+ }
+ }
+ }
+
+ // calculate delta rotation to target heading
+ F32 delta_target_heading = angle_between(mFrameAgent.getAtAxis(), mAutoPilotTargetFacing);
+
+ if (xy_distance > slow_distance && yaw < (F_PI / 10.f))
+ {
+ // walking/flying fast
+ setControlFlags(AGENT_CONTROL_FAST_AT | AGENT_CONTROL_AT_POS);
+ }
+ else if (mAutoPilotTargetDist > mAutoPilotStopDistance)
+ {
+ // walking/flying slow
+ if (at * direction > 0.9f)
+ {
+ setControlFlags(AGENT_CONTROL_AT_POS);
+ }
+ else if (at * direction < -0.9f)
+ {
+ setControlFlags(AGENT_CONTROL_AT_NEG);
+ }
+ }
+
+ // check to see if we need to keep rotating to target orientation
+ if (mAutoPilotTargetDist < mAutoPilotStopDistance)
+ {
+ setControlFlags(AGENT_CONTROL_STOP);
+ if(!mAutoPilotUseRotation || (delta_target_heading < mAutoPilotRotationThreshold))
+ {
+ stopAutoPilot();
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// propagate()
+//-----------------------------------------------------------------------------
+void LLAgent::propagate(const F32 dt)
+{
+ // Update UI based on agent motion
+ LLFloaterMove *floater_move = LLFloaterMove::getInstance();
+ if (floater_move)
+ {
+ floater_move->mForwardButton ->setToggleState( mAtKey > 0 || mWalkKey > 0 );
+ floater_move->mBackwardButton ->setToggleState( mAtKey < 0 || mWalkKey < 0 );
+ floater_move->mSlideLeftButton ->setToggleState( mLeftKey > 0 );
+ floater_move->mSlideRightButton->setToggleState( mLeftKey < 0 );
+ floater_move->mTurnLeftButton ->setToggleState( mYawKey > 0.f );
+ floater_move->mTurnRightButton ->setToggleState( mYawKey < 0.f );
+ floater_move->mMoveUpButton ->setToggleState( mUpKey > 0 );
+ floater_move->mMoveDownButton ->setToggleState( mUpKey < 0 );
+ }
+
+ // handle rotation based on keyboard levels
+ const F32 YAW_RATE = 90.f * DEG_TO_RAD; // radians per second
+ yaw( YAW_RATE * mYawKey * dt );
+
+ const F32 PITCH_RATE = 90.f * DEG_TO_RAD; // radians per second
+ pitch(PITCH_RATE * (F32) mPitchKey * dt);
+
+ // handle auto-land behavior
+ if (mAvatarObject)
+ {
+ BOOL in_air = mAvatarObject->mInAir;
+ LLVector3 land_vel = getVelocity();
+ land_vel.mV[VZ] = 0.f;
+
+ if (!in_air
+ && mUpKey < 0
+ && land_vel.magVecSquared() < MAX_VELOCITY_AUTO_LAND_SQUARED
+ && gSavedSettings.getBOOL("AutomaticFly"))
+ {
+ // land automatically
+ setFlying(FALSE);
+ }
+ }
+
+ // clear keys
+ mAtKey = 0;
+ mWalkKey = 0;
+ mLeftKey = 0;
+ mUpKey = 0;
+ mYawKey = 0.f;
+ mPitchKey = 0;
+}
+
+//-----------------------------------------------------------------------------
+// updateAgentPosition()
+//-----------------------------------------------------------------------------
+void LLAgent::updateAgentPosition(const F32 dt, const F32 yaw_radians, const S32 mouse_x, const S32 mouse_y)
+{
+ propagate(dt);
+
+ // static S32 cameraUpdateCount = 0;
+
+ rotate(yaw_radians, 0, 0, 1);
+
+ //
+ // Check for water and land collision, set underwater flag
+ //
+
+ updateLookAt(mouse_x, mouse_y);
+}
+
+//-----------------------------------------------------------------------------
+// updateLookAt()
+//-----------------------------------------------------------------------------
+void LLAgent::updateLookAt(const S32 mouse_x, const S32 mouse_y)
+{
+ static LLVector3 last_at_axis;
+
+
+ if ( mAvatarObject.isNull() )
+ {
+ return;
+ }
+
+ LLQuaternion av_inv_rot = ~mAvatarObject->mRoot.getWorldRotation();
+ LLVector3 root_at = LLVector3::x_axis * mAvatarObject->mRoot.getWorldRotation();
+
+ if ((gViewerWindow->getMouseVelocityStat()->getCurrent() < 0.01f) &&
+ (root_at * last_at_axis > 0.95f ))
+ {
+ LLVector3 vel = mAvatarObject->getVelocity();
+ if (vel.magVecSquared() > 4.f)
+ {
+ setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, vel * av_inv_rot);
+ }
+ else
+ {
+ //FIXME: rotate mframeagent by sit object's rotation?
+ LLQuaternion look_rotation = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion(); // use camera's current rotation
+ LLVector3 look_offset = LLVector3(2.f, 0.f, 0.f) * look_rotation * av_inv_rot;
+ setLookAt(LOOKAT_TARGET_IDLE, mAvatarObject, look_offset);
+ }
+ last_at_axis = root_at;
+ return;
+ }
+
+ last_at_axis = root_at;
+
+ if (CAMERA_MODE_CUSTOMIZE_AVATAR == getCameraMode())
+ {
+ setLookAt(LOOKAT_TARGET_NONE, mAvatarObject, LLVector3(-2.f, 0.f, 0.f));
+ }
+ else
+ {
+ // Move head based on cursor position
+ ELookAtType lookAtType = LOOKAT_TARGET_NONE;
+ LLVector3 headLookAxis;
+ LLCoordFrame frameCamera = *((LLCoordFrame*)gCamera);
+
+ F32 x_from_center = mouse_x_from_center( mouse_x ); // range from -0.5 to 0.5
+ F32 y_from_center = mouse_y_from_center( mouse_y ); // range from -0.5 to 0.5
+
+ if (cameraMouselook())
+ {
+ lookAtType = LOOKAT_TARGET_MOUSELOOK;
+ }
+ else if (cameraThirdPerson())
+ {
+ frameCamera.yaw( - x_from_center * gSavedSettings.getF32("YawFromMousePosition") * DEG_TO_RAD);
+ frameCamera.pitch( - y_from_center * gSavedSettings.getF32("PitchFromMousePosition") * DEG_TO_RAD);
+ lookAtType = LOOKAT_TARGET_FREELOOK;
+ }
+
+ headLookAxis = frameCamera.getAtAxis();
+ // RN: we use world-space offset for mouselook and freelook
+ //headLookAxis = headLookAxis * av_inv_rot;
+ setLookAt(lookAtType, mAvatarObject, headLookAxis);
+ }
+}
+
+// friends and operators
+
+std::ostream& operator<<(std::ostream &s, const LLAgent &agent)
+{
+ // This is unfinished, but might never be used.
+ // We'll just leave it for now; we can always delete it.
+ s << " { "
+ << " Frame = " << agent.mFrameAgent << "\n"
+ << " }";
+ return s;
+}
+
+
+// ------------------- Beginning of legacy LLCamera hack ----------------------
+// This section is included for legacy LLCamera support until
+// it is no longer needed. Some legacy code must exist in
+// non-legacy functions, and is labeled with "// legacy" comments.
+
+//-----------------------------------------------------------------------------
+// setAvatarObject()
+//-----------------------------------------------------------------------------
+void LLAgent::setAvatarObject(LLVOAvatar *avatar)
+{
+ mAvatarObject = avatar;
+
+ if (!avatar)
+ {
+ llinfos << "Setting LLAgent::mAvatarObject to NULL" << llendl;
+ return;
+ }
+
+ if (!mLookAt)
+ {
+ mLookAt = (LLHUDEffectLookAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
+ }
+ if (!mPointAt)
+ {
+ mPointAt = (LLHUDEffectPointAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
+ }
+
+ if (!mLookAt.isNull())
+ {
+ mLookAt->setSourceObject(avatar);
+ }
+ if (!mPointAt.isNull())
+ {
+ mPointAt->setSourceObject(avatar);
+ }
+
+ sendAgentWearablesRequest();
+}
+
+// TRUE if your own avatar needs to be rendered. Usually only
+// in third person and build.
+//-----------------------------------------------------------------------------
+// needsRenderAvatar()
+//-----------------------------------------------------------------------------
+BOOL LLAgent::needsRenderAvatar()
+{
+ if (cameraMouselook() && !LLVOAvatar::sVisibleInFirstPerson)
+ {
+ return FALSE;
+ }
+
+ return mShowAvatar && mGenderChosen;
+}
+
+// TRUE if we need to render your own avatar's head.
+BOOL LLAgent::needsRenderHead()
+{
+ return mShowAvatar && !cameraMouselook();
+}
+
+//-----------------------------------------------------------------------------
+// startTyping()
+//-----------------------------------------------------------------------------
+void LLAgent::startTyping()
+{
+ mTypingTimer.reset();
+
+ if (getRenderState() & AGENT_STATE_TYPING)
+ {
+ // already typing, don't trigger a different animation
+ return;
+ }
+ setRenderState(AGENT_STATE_TYPING);
+
+ if (mChatTimer.getElapsedTimeF32() < 2.f)
+ {
+ LLViewerObject* chatter = gObjectList.findObject(mLastChatterID);
+ if (chatter && chatter->isAvatar())
+ {
+ gAgent.setLookAt(LOOKAT_TARGET_RESPOND, chatter, LLVector3::zero);
+ }
+ }
+
+ if (gSavedSettings.getBOOL("PlayTypingAnim"))
+ {
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_START);
+ }
+ gChatBar->sendChatFromViewer("", CHAT_TYPE_START, FALSE);
+}
+
+//-----------------------------------------------------------------------------
+// stopTyping()
+//-----------------------------------------------------------------------------
+void LLAgent::stopTyping()
+{
+ if (mRenderState & AGENT_STATE_TYPING)
+ {
+ clearRenderState(AGENT_STATE_TYPING);
+ sendAnimationRequest(ANIM_AGENT_TYPE, ANIM_REQUEST_STOP);
+ gChatBar->sendChatFromViewer("", CHAT_TYPE_STOP, FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setRenderState()
+//-----------------------------------------------------------------------------
+void LLAgent::setRenderState(U8 newstate)
+{
+ mRenderState |= newstate;
+}
+
+//-----------------------------------------------------------------------------
+// clearRenderState()
+//-----------------------------------------------------------------------------
+void LLAgent::clearRenderState(U8 clearstate)
+{
+ mRenderState &= ~clearstate;
+}
+
+
+//-----------------------------------------------------------------------------
+// getRenderState()
+//-----------------------------------------------------------------------------
+U8 LLAgent::getRenderState()
+{
+ if (gNoRender || gToolMgr == NULL || gSelectMgr == NULL || gKeyboard == NULL)
+ {
+ return 0;
+ }
+
+ // FIXME: don't do stuff in a getter! This is infinite loop city!
+ if ((mTypingTimer.getElapsedTimeF32() > TYPING_TIMEOUT_SECS)
+ && (mRenderState & AGENT_STATE_TYPING))
+ {
+ stopTyping();
+ }
+
+ if ((!gSelectMgr->isEmpty() && gSelectMgr->shouldShowSelection())
+ || gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->isEditing() )
+ {
+ setRenderState(AGENT_STATE_EDITING);
+ }
+ else
+ {
+ clearRenderState(AGENT_STATE_EDITING);
+ }
+
+ return mRenderState;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+static const LLFloaterView::skip_list_t& get_skip_list()
+{
+ static LLFloaterView::skip_list_t skip_list;
+ skip_list.insert(gFloaterMap);
+ return skip_list;
+}
+
+//-----------------------------------------------------------------------------
+// endAnimationUpdateUI()
+//-----------------------------------------------------------------------------
+void LLAgent::endAnimationUpdateUI()
+{
+ if (mCameraMode == mLastCameraMode)
+ {
+ // We're already done endAnimationUpdateUI for this transition.
+ return;
+ }
+
+ // clean up UI from mode we're leaving
+ if ( mLastCameraMode == CAMERA_MODE_MOUSELOOK )
+ {
+ // show mouse cursor
+ gViewerWindow->showCursor();
+ // show menus
+ gMenuBarView->setVisible(TRUE);
+ gStatusBar->setVisibleForMouselook(true);
+
+ gCurrentToolset = gBasicToolset;
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ // Only pop if we have pushed...
+ if (TRUE == mViewsPushed)
+ {
+ mViewsPushed = FALSE;
+ gFloaterView->popVisibleAll(get_skip_list());
+ }
+
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ if( gMorphView )
+ {
+ gMorphView->setVisible( FALSE );
+ }
+
+ // Disable mouselook-specific animations
+ if (mAvatarObject)
+ {
+ if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
+ {
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_AIM_BOW_L) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_START);
+ }
+ }
+ }
+ }
+ else
+ if( mLastCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR )
+ {
+ // make sure we ask to save changes
+
+ gCurrentToolset = gBasicToolset;
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ // HACK: If we're quitting, and we were in customize avatar, don't
+ // let the mini-map go visible again. JC
+ if (!gQuitRequested)
+ {
+ gFloaterMap->popVisible();
+ }
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible( FALSE );
+ }
+
+ if (mAvatarObject)
+ {
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE_DONE, ANIM_REQUEST_START);
+ }
+ setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+
+ //---------------------------------------------------------------------
+ // Set up UI for mode we're entering
+ //---------------------------------------------------------------------
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ // hide menus
+ gMenuBarView->setVisible(FALSE);
+ gStatusBar->setVisibleForMouselook(false);
+
+ // clear out camera lag effect
+ mCameraLag.clearVec();
+
+ // JC - Added for always chat in third person option
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+ gCurrentToolset = gMouselookToolset;
+ gToolMgr->useSelectedTool( gMouselookToolset );
+
+ mViewsPushed = TRUE;
+
+ gFloaterView->pushVisibleAll(FALSE, get_skip_list());
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible(FALSE);
+ }
+
+ gIMView->setFloaterOpen( FALSE );
+ gConsole->setVisible( TRUE );
+
+ if (mAvatarObject)
+ {
+ // Trigger mouselook-specific animations
+ if( mAvatarObject->isAnyAnimationSignaled(AGENT_GUN_HOLD_ANIMS, NUM_AGENT_GUN_HOLD_ANIMS) )
+ {
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_RIFLE_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_RIFLE_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_RIFLE_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_HANDGUN_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_HANDGUN_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_HANDGUN_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BAZOOKA_R) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_BAZOOKA_R, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_BAZOOKA_R, ANIM_REQUEST_START);
+ }
+ if (mAvatarObject->mSignaledAnimations.find(ANIM_AGENT_HOLD_BOW_L) != mAvatarObject->mSignaledAnimations.end())
+ {
+ sendAnimationRequest(ANIM_AGENT_HOLD_BOW_L, ANIM_REQUEST_STOP);
+ sendAnimationRequest(ANIM_AGENT_AIM_BOW_L, ANIM_REQUEST_START);
+ }
+ }
+ if (mAvatarObject->getParent())
+ {
+ LLVector3 at_axis = gCamera->getAtAxis();
+ LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot();
+ if (root_object->flagCameraDecoupled())
+ {
+ resetAxes(at_axis);
+ }
+ else
+ {
+ resetAxes(at_axis * ~((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation());
+ }
+ }
+ }
+
+ }
+ else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR)
+ {
+ gCurrentToolset = gFaceEditToolset;
+ gToolMgr->useSelectedTool( gFaceEditToolset );
+
+ gFloaterMap->pushVisible(FALSE);
+ /*
+ LLView *view;
+ for (view = gFloaterView->getFirstChild(); view; view = gFloaterView->getNextChild())
+ {
+ view->pushVisible(FALSE);
+ }
+ */
+
+ if( gMorphView )
+ {
+ gMorphView->setVisible( TRUE );
+ }
+
+ // freeze avatar
+ if (mAvatarObject)
+ {
+ mPauseRequest = mAvatarObject->requestPause();
+ }
+ }
+
+ if (getAvatarObject())
+ {
+ getAvatarObject()->updateAttachmentVisibility(mCameraMode);
+ }
+
+ gFloaterTools->dirty();
+
+ // Don't let this be called more than once if the camera
+ // mode hasn't changed. --JC
+ mLastCameraMode = mCameraMode;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::updateCamera()
+{
+ //Ventrella - changed camera_skyward to the new global "mCameraUpVector"
+ mCameraUpVector = LLVector3::z_axis;
+ //LLVector3 camera_skyward(0.f, 0.f, 1.f);
+ //end Ventrella
+
+ U32 camera_mode = mCameraAnimating ? mLastCameraMode : mCameraMode;
+
+ validateFocusObject();
+
+ if (!mAvatarObject.isNull() &&
+ mAvatarObject->mIsSitting &&
+ camera_mode == CAMERA_MODE_MOUSELOOK)
+ {
+ //Ventrella
+ //changed camera_skyward to the new global "mCameraUpVector"
+ mCameraUpVector = mCameraUpVector * mAvatarObject->getRenderRotation();
+ //end Ventrella
+ }
+
+ if (cameraThirdPerson() && mFocusOnAvatar && LLFollowCamMgr::getActiveFollowCamParams())
+ {
+ changeCameraToFollow();
+ }
+
+ //Ventrella
+ //NOTE - this needs to be integrated into a general upVector system here within llAgent.
+ if ( camera_mode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ mCameraUpVector = mFollowCam.getUpVector();
+ }
+ //end Ventrella
+
+ if (mSitCameraEnabled)
+ {
+ if (mSitCameraReferenceObject->isDead())
+ {
+ setSitCamera(LLUUID::null);
+ }
+ }
+
+ // Update UI with our camera inputs
+ if (gFloaterCamera)
+ {
+ gFloaterCamera->mRotate->setToggleState(
+ mOrbitRightKey > 0.f, // left
+ mOrbitUpKey > 0.f, // top
+ mOrbitLeftKey > 0.f, // right
+ mOrbitDownKey > 0.f); // bottom
+
+ gFloaterCamera->mZoom->setToggleState(
+ mOrbitInKey > 0.f, // top
+ mOrbitOutKey > 0.f); // bottom
+
+ gFloaterCamera->mTrack->setToggleState(
+ mPanLeftKey > 0.f, // left
+ mPanUpKey > 0.f, // top
+ mPanRightKey > 0.f, // right
+ mPanDownKey > 0.f); // bottom
+ }
+
+ // Handle camera movement based on keyboard.
+ const F32 ORBIT_OVER_RATE = 90.f * DEG_TO_RAD; // radians per second
+ const F32 ORBIT_AROUND_RATE = 90.f * DEG_TO_RAD; // radians per second
+ const F32 PAN_RATE = 5.f; // meters per second
+
+ if( mOrbitUpKey || mOrbitDownKey )
+ {
+ F32 input_rate = mOrbitUpKey - mOrbitDownKey;
+ cameraOrbitOver( input_rate * ORBIT_OVER_RATE / gFPSClamped );
+ }
+
+ if( mOrbitLeftKey || mOrbitRightKey)
+ {
+ F32 input_rate = mOrbitLeftKey - mOrbitRightKey;
+ cameraOrbitAround( input_rate * ORBIT_AROUND_RATE / gFPSClamped );
+ }
+
+ if( mOrbitInKey || mOrbitOutKey )
+ {
+ F32 input_rate = mOrbitInKey - mOrbitOutKey;
+
+ LLVector3d to_focus = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - calcFocusPositionTargetGlobal();
+ F32 distance_to_focus = (F32)to_focus.magVec();
+ // Move at distance (in meters) meters per second
+ cameraOrbitIn( input_rate * distance_to_focus / gFPSClamped );
+ }
+
+ if( mPanInKey || mPanOutKey )
+ {
+ F32 input_rate = mPanInKey - mPanOutKey;
+ cameraPanIn( input_rate * PAN_RATE / gFPSClamped );
+ }
+
+ if( mPanRightKey || mPanLeftKey )
+ {
+ F32 input_rate = mPanRightKey - mPanLeftKey;
+ cameraPanLeft( input_rate * -PAN_RATE / gFPSClamped );
+ }
+
+ if( mPanUpKey || mPanDownKey )
+ {
+ F32 input_rate = mPanUpKey - mPanDownKey;
+ cameraPanUp( input_rate * PAN_RATE / gFPSClamped );
+ }
+
+ // Clear camera keyboard keys.
+ mOrbitLeftKey = 0.f;
+ mOrbitRightKey = 0.f;
+ mOrbitUpKey = 0.f;
+ mOrbitDownKey = 0.f;
+ mOrbitInKey = 0.f;
+ mOrbitOutKey = 0.f;
+
+ mPanRightKey = 0.f;
+ mPanLeftKey = 0.f;
+ mPanUpKey = 0.f;
+ mPanDownKey = 0.f;
+ mPanInKey = 0.f;
+ mPanOutKey = 0.f;
+
+ // lerp camera focus offset
+ mCameraFocusOffset = lerp(mCameraFocusOffset, mCameraFocusOffsetTarget, LLCriticalDamp::getInterpolant(CAMERA_FOCUS_HALF_LIFE));
+
+ //Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW )
+ {
+ if ( !mAvatarObject.isNull() )
+ {
+ //--------------------------------------------------------------------------------
+ // this is where the avatar's position and rotation are given to followCam, and
+ // where it is updated. All three of its attributes are updated: (1) position,
+ // (2) focus, and (3) upvector. They can then be queried elsewhere in llAgent.
+ //--------------------------------------------------------------------------------
+ //FIXME: use combined rotation of frameagent and sit object
+ LLQuaternion avatarRotationForFollowCam = mAvatarObject->mIsSitting ? mAvatarObject->getRenderRotation() : mFrameAgent.getQuaternion();
+
+ LLFollowCamParams* current_cam = LLFollowCamMgr::getActiveFollowCamParams();
+ if (current_cam)
+ {
+ mFollowCam.copyParams(*current_cam);
+ mFollowCam.setSubjectPositionAndRotation( mAvatarObject->getRenderPosition(), avatarRotationForFollowCam );
+ mFollowCam.update();
+ }
+ else
+ {
+ changeCameraToThirdPerson(TRUE);
+ }
+ }
+ }
+ // end Ventrella
+
+ BOOL hit_limit;
+ LLVector3d camera_pos_global;
+ LLVector3d camera_target_global = calcCameraPositionTargetGlobal(&hit_limit);
+ mCameraVirtualPositionAgent = getPosAgentFromGlobal(camera_target_global);
+ LLVector3d focus_target_global = calcFocusPositionTargetGlobal();
+
+ // perform field of view correction
+ mCameraFOVZoomFactor = calcCameraFOVZoomFactor();
+ camera_target_global = focus_target_global + (camera_target_global - focus_target_global) * (1.f + mCameraFOVZoomFactor);
+
+ // do alpha fade on focus object
+ F32 fade_increment = mFocusObjectFadeTimer.getElapsedTimeAndResetF32();
+
+ if (mFocusObject.notNull() && !mFocusObject->isAttachment() && mFocusObject->mDrawable.notNull())
+ {
+ F32 increment = fade_increment;
+ if (mFocusObjectDist < -0.2f)
+ {
+ increment *= -1.f;
+ }
+
+ if (mFocusObject->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE)
+ {
+ S32 num_faces = mFocusObject->mDrawable->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLFace* facep = mFocusObject->mDrawable->getFace(i);
+ F32 fade = facep->mAlphaFade;
+ fade = llclamp(fade + increment, 0.f, 1.f);
+ facep->mAlphaFade = fade;
+ }
+ }
+ }
+
+ // do alpha fade in on fade objects
+ std::set< LLPointer<LLViewerObject> >::iterator fade_object_it;
+ for (fade_object_it = mFadeObjects.begin(); fade_object_it != mFadeObjects.end(); )
+ {
+ LLViewerObject* fade_object = *fade_object_it;
+ if (fade_object->isDead())
+ {
+ // remove from list
+ mFadeObjects.erase(fade_object_it++);
+ }
+ else
+ {
+ LLDrawable* drawablep = fade_object->mDrawable;
+ if (drawablep && fade_object->getVObjRadius() > MIN_RADIUS_ALPHA_SIZZLE)
+ {
+ S32 num_faces = drawablep->getNumFaces();
+ BOOL fade_done = TRUE;
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLFace* facep = drawablep->getFace(i);
+ F32 fade = facep->mAlphaFade;
+ fade = llclamp(fade - fade_increment, 0.f, 1.f);
+ facep->mAlphaFade = fade;
+ if (fade > 0.f)
+ {
+ fade_done = FALSE;
+ }
+ }
+ if (fade_done)
+ {
+ mFadeObjects.erase(fade_object_it++);
+ }
+ else
+ {
+ fade_object_it++;
+ }
+ }
+ else
+ {
+ fade_object_it++;
+ }
+ }
+ }
+
+ mShowAvatar = TRUE; // can see avatar by default
+
+ // Adjust position for animation
+ if (mCameraAnimating)
+ {
+ F32 time = mAnimationTimer.getElapsedTimeF32();
+
+ // yet another instance of critically damped motion, hooray!
+ // F32 fraction_of_animation = 1.f - pow(2.f, -time / CAMERA_ZOOM_HALF_LIFE);
+
+ // linear interpolation
+ F32 fraction_of_animation = time / mAnimationDuration;
+
+ BOOL isfirstPerson = mCameraMode == CAMERA_MODE_MOUSELOOK;
+ BOOL wasfirstPerson = mLastCameraMode == CAMERA_MODE_MOUSELOOK;
+ F32 fraction_animation_to_skip;
+
+ if (mAnimationCameraStartGlobal == camera_target_global)
+ {
+ fraction_animation_to_skip = 0.f;
+ }
+ else
+ {
+ LLVector3d cam_delta = mAnimationCameraStartGlobal - camera_target_global;
+ fraction_animation_to_skip = HEAD_BUFFER_SIZE / (F32)cam_delta.magVec();
+ }
+ F32 animation_start_fraction = (wasfirstPerson) ? fraction_animation_to_skip : 0.f;
+ F32 animation_finish_fraction = (isfirstPerson) ? (1.f - fraction_animation_to_skip) : 1.f;
+
+ if (fraction_of_animation < animation_finish_fraction)
+ {
+ if (fraction_of_animation < animation_start_fraction || fraction_of_animation > animation_finish_fraction )
+ {
+ mShowAvatar = FALSE;
+ }
+
+ // ...adjust position for animation
+ camera_pos_global = lerp(mAnimationCameraStartGlobal, camera_target_global, fraction_of_animation);
+ mFocusGlobal = lerp(mAnimationFocusStartGlobal, focus_target_global, fraction_of_animation);
+ }
+ else
+ {
+ // ...animation complete
+ mCameraAnimating = FALSE;
+
+ camera_pos_global = camera_target_global;
+ mFocusGlobal = focus_target_global;
+
+ endAnimationUpdateUI();
+ mShowAvatar = TRUE;
+ }
+
+ if (getAvatarObject() && mCameraMode != CAMERA_MODE_MOUSELOOK)
+ {
+ getAvatarObject()->updateAttachmentVisibility(mCameraMode);
+ }
+ }
+ else
+ {
+ camera_pos_global = camera_target_global;
+ mFocusGlobal = focus_target_global;
+ mShowAvatar = TRUE;
+ }
+
+ mCameraCurrentFOVZoomFactor = lerp(mCameraCurrentFOVZoomFactor, mCameraFOVZoomFactor, LLCriticalDamp::getInterpolant(FOV_ZOOM_HALF_LIFE));
+
+// llinfos << "Current FOV Zoom: " << mCameraCurrentFOVZoomFactor << " Target FOV Zoom: " << mCameraFOVZoomFactor << " Object penetration: " << mFocusObjectDist << llendl;
+
+ F32 ui_offset = 0.f;
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
+ {
+ ui_offset = calcCustomizeAvatarUIOffset( camera_pos_global );
+ }
+
+
+ LLVector3 focus_agent = getPosAgentFromGlobal(mFocusGlobal);
+
+ mCameraPositionAgent = getPosAgentFromGlobal(camera_pos_global);
+
+ // Move the camera
+
+ //Ventrella
+ gCamera->updateCameraLocation(mCameraPositionAgent, mCameraUpVector, focus_agent);
+ //gCamera->updateCameraLocation(mCameraPositionAgent, camera_skyward, focus_agent);
+ //end Ventrella
+
+ //RN: translate UI offset after camera is oriented properly
+ gCamera->translate(gCamera->getLeftAxis() * ui_offset);
+
+ // Change FOV
+ gCamera->setView(gCamera->getDefaultFOV() / (1.f + mCameraCurrentFOVZoomFactor));
+
+ // follow camera when in customize mode
+ if (cameraCustomizeAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, mCameraPositionAgent);
+ }
+
+ // update the travel distance stat
+ // this isn't directly related to the camera
+ // but this seemed like the best place to do this
+ LLVector3d global_pos = getPositionGlobal();
+ if (! mLastPositionGlobal.isExactlyZero())
+ {
+ LLVector3d delta = global_pos - mLastPositionGlobal;
+ mDistanceTraveled += delta.magVec();
+ }
+ mLastPositionGlobal = global_pos;
+
+ if (LLVOAvatar::sVisibleInFirstPerson && mAvatarObject.notNull() && !mAvatarObject->mIsSitting && cameraMouselook())
+ {
+ LLVector3 head_pos = mAvatarObject->mHeadp->getWorldPosition() +
+ LLVector3(0.08f, 0.f, 0.05f) * mAvatarObject->mHeadp->getWorldRotation() +
+ LLVector3(0.1f, 0.f, 0.f) * mAvatarObject->mPelvisp->getWorldRotation();
+ LLVector3 diff = mCameraPositionAgent - head_pos;
+ diff = diff * ~mAvatarObject->mRoot.getWorldRotation();
+
+ LLJoint* torso_joint = mAvatarObject->mTorsop;
+ LLJoint* chest_joint = mAvatarObject->mChestp;
+ LLVector3 torso_scale = torso_joint->getScale();
+ LLVector3 chest_scale = chest_joint->getScale();
+
+ // shorten avatar skeleton to avoid foot interpenetration
+ if (!mAvatarObject->mInAir)
+ {
+ LLVector3 chest_offset = LLVector3(0.f, 0.f, chest_joint->getPosition().mV[VZ]) * torso_joint->getWorldRotation();
+ F32 z_compensate = llclamp(-diff.mV[VZ], -0.2f, 1.f);
+ F32 scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / chest_offset.mV[VZ]), 0.5f, 1.2f);
+ torso_joint->setScale(LLVector3(1.f, 1.f, scale_factor));
+
+ LLJoint* neck_joint = mAvatarObject->mNeckp;
+ LLVector3 neck_offset = LLVector3(0.f, 0.f, neck_joint->getPosition().mV[VZ]) * chest_joint->getWorldRotation();
+ scale_factor = llclamp(1.f - ((z_compensate * 0.5f) / neck_offset.mV[VZ]), 0.5f, 1.2f);
+ chest_joint->setScale(LLVector3(1.f, 1.f, scale_factor));
+ diff.mV[VZ] = 0.f;
+ }
+
+ mAvatarObject->mPelvisp->setPosition(mAvatarObject->mPelvisp->getPosition() + diff);
+
+ mAvatarObject->mRoot.updateWorldMatrixChildren();
+
+ for(LLViewerJointAttachment *attachment = mAvatarObject->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAvatarObject->mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull())
+ {
+ // clear any existing "early" movements of attachment
+ attached_object->mDrawable->clearState(LLDrawable::EARLY_MOVE);
+ gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
+ attached_object->updateText();
+ }
+ }
+
+ torso_joint->setScale(torso_scale);
+ chest_joint->setScale(chest_scale);
+ }
+}
+
+void LLAgent::updateFocusOffset()
+{
+ validateFocusObject();
+ if (mFocusObject.notNull())
+ {
+ LLVector3d obj_pos = getPosGlobalFromAgent(mFocusObject->getRenderPosition());
+ mFocusObjectOffset.setVec(mFocusTargetGlobal - obj_pos);
+ }
+}
+
+void LLAgent::validateFocusObject()
+{
+ if (mFocusObject.notNull() &&
+ (mFocusObject->isDead()))
+ {
+ mFocusObjectOffset.clearVec();
+ clearFocusObject();
+ mCameraFOVZoomFactor = 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCustomizeAvatarUIOffset()
+//-----------------------------------------------------------------------------
+F32 LLAgent::calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global )
+{
+ F32 ui_offset = 0.f;
+
+ if( gFloaterCustomize )
+ {
+ const LLRect& rect = gFloaterCustomize->getRect();
+
+ // Move the camera so that the avatar isn't covered up by this floater.
+ F32 fraction_of_fov = 0.5f - (0.5f * (1.f - llmin(1.f, ((F32)rect.getWidth() / (F32)gViewerWindow->getWindowWidth()))));
+ F32 apparent_angle = fraction_of_fov * gCamera->getView() * gCamera->getAspect(); // radians
+ F32 offset = tan(apparent_angle);
+
+ if( rect.mLeft < (gViewerWindow->getWindowWidth() - rect.mRight) )
+ {
+ // Move the avatar to the right (camera to the left)
+ ui_offset = offset;
+ }
+ else
+ {
+ // Move the avatar to the left (camera to the right)
+ ui_offset = -offset;
+ }
+ }
+ F32 range = (F32)dist_vec(camera_pos_global, gAgent.getFocusGlobal());
+ mUIOffset = lerp(mUIOffset, ui_offset, LLCriticalDamp::getInterpolant(0.05f));
+ return mUIOffset * range;
+}
+
+//-----------------------------------------------------------------------------
+// calcFocusPositionTargetGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcFocusPositionTargetGlobal()
+{
+ if (mFocusObject.notNull() && mFocusObject->isDead())
+ {
+ clearFocusObject();
+ }
+
+ // Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ mFocusTargetGlobal = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedFocus());
+ return mFocusTargetGlobal;
+ }// End Ventrella
+ else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ LLVector3d at_axis(1.0, 0.0, 0.0);
+ LLQuaternion agent_rot = mFrameAgent.getQuaternion();
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLViewerObject* root_object = (LLViewerObject*)mAvatarObject->getRoot();
+ if (!root_object->flagCameraDecoupled())
+ {
+ agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation();
+ }
+ }
+ at_axis = at_axis * agent_rot;
+ mFocusTargetGlobal = calcCameraPositionTargetGlobal() + at_axis;
+ return mFocusTargetGlobal;
+ }
+ else if (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR)
+ {
+ return mFocusTargetGlobal;
+ }
+ else if (!mFocusOnAvatar)
+ {
+ if (mFocusObject.notNull() && !mFocusObject->isDead() && mFocusObject->mDrawable.notNull())
+ {
+ LLDrawable* drawablep = mFocusObject->mDrawable;
+
+ if (mTrackFocusObject && drawablep && drawablep->isActive())
+ {
+ if (mFocusObject->isSelected())
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(drawablep);
+ }
+ }
+ }
+ // if not tracking object, update offset based on new object position
+ else
+ {
+ updateFocusOffset();
+ }
+ LLVector3 focus_agent = mFocusObject->getRenderPosition() + mFocusObjectOffset;
+ mFocusTargetGlobal.setVec(getPosGlobalFromAgent(focus_agent));
+ //FIXME: get camera pointat behavior working
+ //if (mTrackFocusObject)
+ //{
+ // mCameraFocusOffset = gAgent.getPosGlobalFromAgent(gCamera->getOrigin()) - mFocusTargetGlobal;
+ //}
+ }
+ return mFocusTargetGlobal;
+ }
+ else if (mSitCameraEnabled && mAvatarObject.notNull() && mAvatarObject->mIsSitting && mSitCameraReferenceObject.notNull())
+ {
+ // sit camera
+ LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition();
+ LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation();
+
+ LLVector3 target_pos = object_pos + (mSitCameraFocus * object_rot);
+ return getPosGlobalFromAgent(target_pos);
+ }
+ else
+ {
+ // ...offset from avatar
+ LLVector3d focus_offset;
+ focus_offset.setVec(gSavedSettings.getVector3("FocusOffsetDefault"));
+
+ LLQuaternion agent_rot = mFrameAgent.getQuaternion();
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ agent_rot *= ((LLViewerObject*)(mAvatarObject->getParent()))->getRenderRotation();
+ }
+
+ focus_offset = focus_offset * agent_rot;
+
+ return getPositionGlobal() + focus_offset;
+ }
+}
+
+void LLAgent::setupSitCamera()
+{
+ // agent frame entering this function is in world coordinates
+ if (mAvatarObject.notNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ // slam agent coordinate frame to proper parent local version
+ LLVector3 at_axis = mFrameAgent.getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~parent_rot);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getCameraPositionAgent()
+//-----------------------------------------------------------------------------
+const LLVector3 &LLAgent::getCameraPositionAgent() const
+{
+ return gCamera->getOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// getCameraPositionGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::getCameraPositionGlobal() const
+{
+ if (gCamera)
+ {
+ return getPosGlobalFromAgent(gCamera->getOrigin());
+ }
+ else
+ {
+ return (LLVector3d::zero);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraFOVZoomFactor()
+//-----------------------------------------------------------------------------
+F32 LLAgent::calcCameraFOVZoomFactor()
+{
+ LLVector3 camera_offset_dir;
+ camera_offset_dir.setVec(mCameraFocusOffset);
+
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ return 0.f;
+ }
+ else if (mFocusObject.notNull() && !mFocusObject->isAvatar())
+ {
+ // don't FOV zoom on mostly transparent objects
+ LLVector3 focus_offset = mFocusObjectOffset;
+ F32 obj_min_dist = 0.f;
+ calcCameraMinDistance(obj_min_dist);
+ F32 current_distance = llmax(0.001f, camera_offset_dir.magVec());
+
+ mFocusObjectDist = obj_min_dist - current_distance;
+
+ F32 new_fov_zoom = llclamp(mFocusObjectDist / current_distance, 0.f, 1000.f);
+ return new_fov_zoom;
+ }
+ else // focusing on land or avatar
+ {
+ // keep old field of view until user changes focus explicitly
+ return mCameraFOVZoomFactor;
+ //return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcCameraPositionTargetGlobal()
+//-----------------------------------------------------------------------------
+LLVector3d LLAgent::calcCameraPositionTargetGlobal(BOOL *hit_limit)
+{
+ // Compute base camera position and look-at points.
+ F32 camera_land_height;
+ LLVector3d frame_center_global = mAvatarObject.isNull() ? getPositionGlobal()
+ : getPosGlobalFromAgent(mAvatarObject->mRoot.getWorldPosition());
+
+ LLVector3 upAxis = getUpAxis();
+ BOOL isConstrained = FALSE;
+ LLVector3d head_offset;
+ head_offset.setVec(mThirdPersonHeadOffset);
+
+ LLVector3d camera_position_global;
+
+ // Ventrella
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && mFocusOnAvatar )
+ {
+ camera_position_global = gAgent.getPosGlobalFromAgent(mFollowCam.getSimulatedPosition());
+ }// End Ventrella
+ else if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ if (mAvatarObject.isNull() || mAvatarObject->mDrawable.isNull())
+ {
+ llwarns << "Null avatar drawable!" << llendl;
+ return LLVector3d::zero;
+ }
+ head_offset.clearVec();
+ if (mAvatarObject->mIsSitting && mAvatarObject->getParent())
+ {
+ mAvatarObject->updateHeadOffset();
+ head_offset.mdV[VX] = mAvatarObject->mHeadOffset.mV[VX];
+ head_offset.mdV[VY] = mAvatarObject->mHeadOffset.mV[VY];
+ head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ] + 0.1f;
+ const LLMatrix4& mat = ((LLViewerObject*) mAvatarObject->getParent())->getRenderMatrix();
+ camera_position_global = getPosGlobalFromAgent
+ ((mAvatarObject->getPosition()+
+ LLVector3(head_offset)*mAvatarObject->getRotation()) * mat);
+ }
+ else
+ {
+ head_offset.mdV[VZ] = mAvatarObject->mHeadOffset.mV[VZ];
+ if (mAvatarObject->mIsSitting)
+ {
+ head_offset.mdV[VZ] += 0.1;
+ }
+ camera_position_global = getPosGlobalFromAgent(mAvatarObject->getRenderPosition());//frame_center_global;
+ head_offset = head_offset * mAvatarObject->getRenderRotation();
+ camera_position_global = camera_position_global + head_offset;
+ }
+ }
+ else if (mCameraMode == CAMERA_MODE_THIRD_PERSON && mFocusOnAvatar)
+ {
+ LLVector3 local_camera_offset;
+ F32 camera_distance = 0.f;
+
+ if (mSitCameraEnabled
+ && mAvatarObject.notNull()
+ && mAvatarObject->mIsSitting
+ && mSitCameraReferenceObject.notNull())
+ {
+ // sit camera
+ LLVector3 object_pos = mSitCameraReferenceObject->getRenderPosition();
+ LLQuaternion object_rot = mSitCameraReferenceObject->getRenderRotation();
+
+ LLVector3 target_pos = object_pos + (mSitCameraPos * object_rot);
+
+ camera_position_global = getPosGlobalFromAgent(target_pos);
+ }
+ else
+ {
+ local_camera_offset = mCameraZoomFraction * mCameraOffsetDefault;
+
+ // are we sitting down?
+ if (mAvatarObject.notNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion parent_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ // slam agent coordinate frame to proper parent local version
+ LLVector3 at_axis = mFrameAgent.getAtAxis() * parent_rot;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~parent_rot);
+
+ local_camera_offset = local_camera_offset * mFrameAgent.getQuaternion() * parent_rot;
+ }
+ else
+ {
+ local_camera_offset = mFrameAgent.rotateToAbsolute( local_camera_offset );
+ }
+
+ if (!mCameraCollidePlane.isExactlyZero() && (mAvatarObject.isNull() || !mAvatarObject->mIsSitting))
+ {
+ LLVector3 plane_normal;
+ plane_normal.setVec(mCameraCollidePlane.mV);
+
+ F32 offset_dot_norm = local_camera_offset * plane_normal;
+ if (llabs(offset_dot_norm) < 0.001f)
+ {
+ offset_dot_norm = 0.001f;
+ }
+
+ camera_distance = local_camera_offset.normVec();
+
+ F32 pos_dot_norm = getPosAgentFromGlobal(frame_center_global + head_offset) * plane_normal;
+
+ // if agent is outside the colliding half-plane
+ if (pos_dot_norm > mCameraCollidePlane.mV[VW])
+ {
+ // check to see if camera is on the opposite side (inside) the half-plane
+ if (offset_dot_norm + pos_dot_norm < mCameraCollidePlane.mV[VW])
+ {
+ // diminish offset by factor to push it back outside the half-plane
+ camera_distance *= (pos_dot_norm - mCameraCollidePlane.mV[VW] - CAMERA_COLLIDE_EPSILON) / -offset_dot_norm;
+ }
+ }
+ else
+ {
+ if (offset_dot_norm + pos_dot_norm > mCameraCollidePlane.mV[VW])
+ {
+ camera_distance *= (mCameraCollidePlane.mV[VW] - pos_dot_norm - CAMERA_COLLIDE_EPSILON) / offset_dot_norm;
+ }
+ }
+ }
+ else
+ {
+ camera_distance = local_camera_offset.normVec();
+ }
+
+ mTargetCameraDistance = llmax(camera_distance, MIN_CAMERA_DISTANCE);
+
+ if (mTargetCameraDistance != mCurrentCameraDistance)
+ {
+ F32 camera_lerp_amt = LLCriticalDamp::getInterpolant(CAMERA_ZOOM_HALF_LIFE);
+
+ mCurrentCameraDistance = lerp(mCurrentCameraDistance, mTargetCameraDistance, camera_lerp_amt);
+ }
+
+ // Make the camera distance current
+ local_camera_offset *= mCurrentCameraDistance;
+
+ // set the global camera position
+ LLVector3d camera_offset;
+
+ LLVector3 av_pos = mAvatarObject.isNull() ? LLVector3::zero : mAvatarObject->getRenderPosition();
+ camera_offset.setVec( local_camera_offset );
+ camera_position_global = frame_center_global + head_offset + camera_offset;
+
+ if (!mAvatarObject.isNull())
+ {
+ LLVector3d camera_lag_d;
+ F32 lag_interp = LLCriticalDamp::getInterpolant(CAMERA_LAG_HALF_LIFE);
+ LLVector3 target_lag;
+ LLVector3 vel = getVelocity();
+
+ // lag by appropriate amount for flying
+ F32 time_in_air = mAvatarObject->mTimeInAir.getElapsedTimeF32();
+ if(!mCameraAnimating && mAvatarObject->mInAir && time_in_air > GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME)
+ {
+ LLVector3 frame_at_axis = mFrameAgent.getAtAxis();
+ frame_at_axis -= projected_vec(frame_at_axis, getReferenceUpVector());
+ frame_at_axis.normVec();
+
+ //transition smoothly in air mode, to avoid camera pop
+ F32 u = (time_in_air - GROUND_TO_AIR_CAMERA_TRANSITION_START_TIME) / GROUND_TO_AIR_CAMERA_TRANSITION_TIME;
+ u = llclamp(u, 0.f, 1.f);
+
+ lag_interp *= u;
+
+ if (gViewerWindow->getLeftMouseDown() && gLastHitObjectID == mAvatarObject->getID())
+ {
+ // disable camera lag when using mouse-directed steering
+ target_lag.clearVec();
+ }
+ else
+ {
+ target_lag = vel * gSavedSettings.getF32("DynamicCameraStrength") / 30.f;
+ }
+
+ mCameraLag = lerp(mCameraLag, target_lag, lag_interp);
+
+ F32 lag_dist = mCameraLag.magVec();
+ if (lag_dist > MAX_CAMERA_LAG)
+ {
+ mCameraLag = mCameraLag * MAX_CAMERA_LAG / lag_dist;
+ }
+
+ // clamp camera lag so that avatar is always in front
+ F32 dot = (mCameraLag - (frame_at_axis * (MIN_CAMERA_LAG * u))) * frame_at_axis;
+ if (dot < -(MIN_CAMERA_LAG * u))
+ {
+ mCameraLag -= (dot + (MIN_CAMERA_LAG * u)) * frame_at_axis;
+ }
+ }
+ else
+ {
+ mCameraLag = lerp(mCameraLag, LLVector3::zero, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ camera_lag_d.setVec(mCameraLag);
+ camera_position_global = camera_position_global - camera_lag_d;
+ }
+ }
+ }
+ else
+ {
+ LLVector3d focusPosGlobal = calcFocusPositionTargetGlobal();
+ // camera gets pushed out later wrt mCameraFOVZoomFactor...this is "raw" value
+ camera_position_global = focusPosGlobal + mCameraFocusOffset;
+ }
+
+ if (!LLViewerCamera::sDisableCameraConstraints && !gAgent.isGodlike())
+ {
+ LLViewerRegion* regionp = gWorldPointer->getRegionFromPosGlobal(
+ camera_position_global);
+ bool constrain = true;
+ if(regionp && regionp->canManageEstate())
+ {
+ constrain = false;
+ }
+ if(constrain)
+ {
+ F32 max_dist = ( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode ) ?
+ APPEARANCE_MAX_ZOOM : MAX_CAMERA_DISTANCE_FROM_AGENT;
+
+ LLVector3d camera_offset = camera_position_global
+ - gAgent.getPositionGlobal();
+ F32 camera_distance = (F32)camera_offset.magVec();
+
+ if(camera_distance > max_dist)
+ {
+ camera_position_global = gAgent.getPositionGlobal() +
+ (max_dist / camera_distance) * camera_offset;
+ isConstrained = TRUE;
+ }
+ }
+
+// JC - Could constrain camera based on parcel stuff here.
+// LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(camera_position_global);
+//
+// if (regionp && !regionp->mParcelOverlay->isBuildCameraAllowed(regionp->getPosRegionFromGlobal(camera_position_global)))
+// {
+// camera_position_global = last_position_global;
+//
+// isConstrained = TRUE;
+// }
+ }
+
+ // Don't let camera go underground
+ F32 camera_min_off_ground = getCameraMinOffGround();
+
+ if (gWorldPointer)
+ {
+ camera_land_height = gWorldPointer->resolveLandHeightGlobal(camera_position_global);
+ }
+ else
+ {
+ camera_land_height = 0.f;
+ }
+
+ if (camera_position_global.mdV[VZ] < camera_land_height + camera_min_off_ground)
+ {
+ camera_position_global.mdV[VZ] = camera_land_height + camera_min_off_ground;
+
+ gMinObjectDistance = MIN_NEAR_PLANE;
+ isConstrained = TRUE;
+ }
+
+
+ if (hit_limit)
+ {
+ *hit_limit = isConstrained;
+ }
+
+ return camera_position_global;
+}
+
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+void LLAgent::handleScrollWheel(S32 clicks)
+{
+ if ( mCameraMode == CAMERA_MODE_FOLLOW && gAgent.getFocusOnAvatar())
+ {
+ if ( ! mFollowCam.getPositionLocked() ) // not if the followCam position is locked in place
+ {
+ mFollowCam.zoom( clicks );
+ if ( mFollowCam.isZoomedToMinimumDistance() )
+ {
+ changeCameraToMouselook(FALSE);
+ }
+ }
+ }
+ else
+ {
+ const F32 ROOT_ROOT_TWO = sqrt(F_SQRT2);
+
+ // Block if camera is animating
+ if (mCameraAnimating)
+ {
+ return;
+ }
+
+ if (gSelectMgr->getObjectCount() && gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom_factor = (F32)pow(0.8, -clicks);
+ cameraZoomIn(zoom_factor);
+ }
+ else if (mFocusOnAvatar && mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ F32 current_zoom_fraction = mTargetCameraDistance / mCameraOffsetDefault.magVec();
+ current_zoom_fraction *= 1.f - pow(ROOT_ROOT_TWO, clicks);
+
+ cameraOrbitIn(current_zoom_fraction * mCameraOffsetDefault.magVec());
+ }
+ else
+ {
+ F32 current_zoom_fraction = (F32)mCameraFocusOffsetTarget.magVec();
+ cameraOrbitIn(current_zoom_fraction * (1.f - pow(ROOT_ROOT_TWO, clicks)));
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getCameraMinOffGround()
+//-----------------------------------------------------------------------------
+F32 LLAgent::getCameraMinOffGround()
+{
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ return 0.f;
+ }
+ else
+ {
+ if (LLViewerCamera::sDisableCameraConstraints)
+ {
+ return -1000.f;
+ }
+ else
+ {
+ return 0.5f;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// resetCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::resetCamera()
+{
+ // Remove any pitch from the avatar
+ LLVector3 at = mFrameAgent.getAtAxis();
+ at.mV[VZ] = 0.f;
+ at.normVec();
+ gAgent.resetAxes(at);
+ // have to explicitly clear field of view zoom now
+ mCameraFOVZoomFactor = 0.f;
+
+ updateCamera();
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToMouselook()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToMouselook(BOOL animate)
+{
+ // visibility changes at end of animation
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ gCurrentToolset = gMouselookToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", TRUE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->stopMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->stopMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ //gViewerWindow->stopGrab();
+ gSelectMgr->deselectAll();
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+
+ if( mCameraMode != CAMERA_MODE_MOUSELOOK )
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_MOUSELOOK;
+ U32 old_flags = mControlFlags;
+ setControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// changeCameraToDefault()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToDefault()
+{
+ if (LLFollowCamMgr::getActiveFollowCamParams())
+ {
+ changeCameraToFollow();
+ }
+ else
+ {
+ changeCameraToThirdPerson();
+ }
+}
+
+
+// Ventrella
+//-----------------------------------------------------------------------------
+// changeCameraToFollow()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToFollow(BOOL animate)
+{
+ if( mCameraMode != CAMERA_MODE_FOLLOW )
+ {
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ animate = FALSE;
+ }
+ startCameraAnimation();
+
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_FOLLOW;
+
+ // bang-in the current focus, position, and up vector of the follow cam
+ mFollowCam.reset( mCameraPositionAgent, gCamera->getPointOfInterest(), LLVector3::z_axis );
+
+ if (gBasicToolset)
+ {
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->mPelvisp->setPosition(LLVector3::zero);
+ mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras
+ //gViewerWindow->setKeyboardFocus( NULL, NULL );
+ //gViewerWindow->setMouseCapture( NULL, NULL );
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToThirdPerson()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToThirdPerson(BOOL animate)
+{
+//printf( "changeCameraToThirdPerson\n" );
+
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ mCameraZoomFraction = INITIAL_ZOOM_FRACTION;
+
+ if (mAvatarObject)
+ {
+ mAvatarObject->mPelvisp->setPosition(LLVector3::zero);
+ mAvatarObject->startMotion( ANIM_AGENT_BODY_NOISE );
+ mAvatarObject->startMotion( ANIM_AGENT_BREATHE_ROT );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", TRUE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ LLVector3 at_axis;
+
+ // unpause avatar animation
+ mPauseRequest = NULL;
+
+ if( mCameraMode != CAMERA_MODE_THIRD_PERSON )
+ {
+ if (gBasicToolset)
+ {
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ mCameraLag.clearVec();
+ if (mCameraMode == CAMERA_MODE_MOUSELOOK)
+ {
+ mCurrentCameraDistance = MIN_CAMERA_DISTANCE;
+ mTargetCameraDistance = MIN_CAMERA_DISTANCE;
+ animate = FALSE;
+ }
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_THIRD_PERSON;
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ //RN: this doesn't seem to be necessary and destroys the UE for script-driven cameras
+ //gViewerWindow->setKeyboardFocus( NULL, NULL );
+ //gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+
+ // Remove any pitch from the avatar
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~obj_rot);
+ }
+ else
+ {
+ at_axis = mFrameAgent.getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis);
+ }
+
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// changeCameraToCustomizeAvatar()
+//-----------------------------------------------------------------------------
+void LLAgent::changeCameraToCustomizeAvatar(BOOL animate)
+{
+ setControlFlags(AGENT_CONTROL_STAND_UP); // force stand up
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ if (gFaceEditToolset)
+ {
+ gCurrentToolset = gFaceEditToolset;
+ gCurrentToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+
+ gSavedSettings.setBOOL("FirstPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("MouselookBtnState", FALSE);
+ gSavedSettings.setBOOL("ThirdPersonBtnState", FALSE);
+ gSavedSettings.setBOOL("BuildBtnState", FALSE);
+
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+
+ // Remove any pitch from the avatar
+ LLVector3 at = mFrameAgent.getAtAxis();
+ at.mV[VZ] = 0.f;
+ at.normVec();
+ gAgent.resetAxes(at);
+
+ if( mCameraMode != CAMERA_MODE_CUSTOMIZE_AVATAR )
+ {
+ mLastCameraMode = mCameraMode;
+ mCameraMode = CAMERA_MODE_CUSTOMIZE_AVATAR;
+ U32 old_flags = mControlFlags;
+ clearControlFlags(AGENT_CONTROL_MOUSELOOK);
+ if (old_flags != mControlFlags)
+ {
+ mbFlagsDirty = TRUE;
+ }
+
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ gViewerWindow->setMouseCapture( NULL, NULL );
+
+ LLVOAvatar::onCustomizeStart();
+ }
+
+ if (animate && !mAvatarObject.isNull())
+ {
+ sendAnimationRequest(ANIM_AGENT_CUSTOMIZE, ANIM_REQUEST_START);
+ LLMotion* turn_motion = mAvatarObject->findMotion(ANIM_AGENT_CUSTOMIZE);
+ if (turn_motion)
+ {
+ mAnimationDuration = turn_motion->getDuration() + CUSTOMIZE_AVATAR_CAMERA_ANIM_SLOP;
+ }
+ else
+ {
+ mAnimationDuration = gSavedSettings.getF32("ZoomTime");
+ }
+ gAgent.setFocusGlobal(LLVector3d::zero);
+ }
+ else
+ {
+ mCameraAnimating = FALSE;
+ endAnimationUpdateUI();
+ }
+
+}
+
+
+//
+// Focus point management
+//
+
+//-----------------------------------------------------------------------------
+// startCameraAnimation()
+//-----------------------------------------------------------------------------
+void LLAgent::startCameraAnimation()
+{
+ mAnimationCameraStartGlobal = getCameraPositionGlobal();
+ mAnimationFocusStartGlobal = mFocusGlobal;
+ mAnimationTimer.reset();
+ mCameraAnimating = TRUE;
+ mAnimationDuration = gSavedSettings.getF32("ZoomTime");
+}
+
+//-----------------------------------------------------------------------------
+// stopCameraAnimation()
+//-----------------------------------------------------------------------------
+void LLAgent::stopCameraAnimation()
+{
+ mCameraAnimating = FALSE;
+}
+
+void LLAgent::clearFocusObject()
+{
+ if (mFocusObject.notNull())
+ {
+ startCameraAnimation();
+
+ setFocusObject(NULL);
+ mFocusObjectOffset.clearVec();
+ }
+}
+
+void LLAgent::setFocusObject(LLViewerObject* object)
+{
+ if (mFocusObject.notNull() &&
+ mFocusObject->mDrawable.notNull() &&
+ mFocusObject->getPCode() == LL_PCODE_VOLUME &&
+ mFocusObject != object)
+ {
+ LLPointer<LLViewerObject> fade_object_ptr(mFocusObject);
+
+ if (fade_object_ptr.notNull() && mFadeObjects.find(fade_object_ptr) == mFadeObjects.end())
+ {
+ mFadeObjects.insert(fade_object_ptr);
+ }
+ }
+
+ mFocusObject = object;
+}
+
+// Focus on a point, but try to keep camera position stable.
+//-----------------------------------------------------------------------------
+// setFocusGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::setFocusGlobal(const LLVector3d& focus, const LLUUID &object_id)
+{
+ setFocusObject(gObjectList.findObject(object_id));
+ LLVector3d old_focus = mFocusTargetGlobal;
+ LLViewerObject *focus_obj = mFocusObject;
+
+ // if focus has changed
+ if (old_focus != focus)
+ {
+ if (focus.isExactlyZero())
+ {
+ if (!mAvatarObject.isNull())
+ {
+ mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ mFocusTargetGlobal = getPositionGlobal();
+ }
+ mCameraFocusOffsetTarget = getCameraPositionGlobal() - mFocusTargetGlobal;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+ setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+ else
+ {
+ mFocusTargetGlobal = focus;
+ if (!focus_obj)
+ {
+ mCameraFOVZoomFactor = 0.f;
+ }
+
+ mCameraFocusOffsetTarget = gAgent.getPosGlobalFromAgent(mCameraVirtualPositionAgent) - mFocusTargetGlobal;
+
+ startCameraAnimation();
+
+ if (focus_obj)
+ {
+ if (focus_obj->isAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, focus_obj);
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, focus_obj, (getPosAgentFromGlobal(focus) - focus_obj->getRenderPosition()) * ~focus_obj->getRenderRotation());
+ }
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal));
+ }
+ }
+ }
+ else // focus == mFocusTargetGlobal
+ {
+ if (focus.isExactlyZero())
+ {
+ if (!mAvatarObject.isNull())
+ {
+ mFocusTargetGlobal = getPosGlobalFromAgent(mAvatarObject->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ mFocusTargetGlobal = getPositionGlobal();
+ }
+ }
+ mCameraFocusOffsetTarget = (getCameraPositionGlobal() - mFocusTargetGlobal) / (1.f + mCameraFOVZoomFactor);;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+ }
+
+ if (mFocusObject.notNull())
+ {
+ // for attachments, make offset relative to avatar, not the attachment
+ if (mFocusObject->isAttachment())
+ {
+ while (!mFocusObject->isAvatar())
+ {
+ mFocusObject = (LLViewerObject*) mFocusObject->getParent();
+ }
+ setFocusObject((LLViewerObject*)mFocusObject);
+ }
+ updateFocusOffset();
+ }
+}
+
+// Used for avatar customization
+//-----------------------------------------------------------------------------
+// setCameraPosAndFocusGlobal()
+//-----------------------------------------------------------------------------
+void LLAgent::setCameraPosAndFocusGlobal(const LLVector3d& camera_pos, const LLVector3d& focus, const LLUUID &object_id)
+{
+ LLVector3d old_focus = mFocusTargetGlobal;
+
+ F64 focus_delta_squared = (old_focus - focus).magVecSquared();
+ const F64 ANIM_EPSILON_SQUARED = 0.0001;
+ if( focus_delta_squared > ANIM_EPSILON_SQUARED )
+ {
+ startCameraAnimation();
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == mCameraMode )
+ {
+ // Compensate for the fact that the camera has already been offset to make room for LLFloaterCustomize.
+ mAnimationCameraStartGlobal -= LLVector3d(gCamera->getLeftAxis() * calcCustomizeAvatarUIOffset( mAnimationCameraStartGlobal ));
+ }
+ }
+
+ //gCamera->setOrigin( gAgent.getPosAgentFromGlobal( camera_pos ) );
+ setFocusObject(gObjectList.findObject(object_id));
+ mFocusTargetGlobal = focus;
+ mCameraFocusOffsetTarget = camera_pos - focus;
+ mCameraFocusOffset = mCameraFocusOffsetTarget;
+
+ if (mFocusObject)
+ {
+ if (mFocusObject->isAvatar())
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject);
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, mFocusObject, (getPosAgentFromGlobal(focus) - mFocusObject->getRenderPosition()) * ~mFocusObject->getRenderRotation());
+ }
+ }
+ else
+ {
+ setLookAt(LOOKAT_TARGET_FOCUS, NULL, getPosAgentFromGlobal(mFocusTargetGlobal));
+ }
+
+ if( mCameraAnimating )
+ {
+ const F64 ANIM_METERS_PER_SECOND = 10.0;
+ const F64 MIN_ANIM_SECONDS = 0.5;
+ F64 anim_duration = llmax( MIN_ANIM_SECONDS, sqrt(focus_delta_squared) / ANIM_METERS_PER_SECOND );
+ setAnimationDuration( (F32)anim_duration );
+ }
+
+ updateFocusOffset();
+}
+
+//-----------------------------------------------------------------------------
+// setSitCamera()
+//-----------------------------------------------------------------------------
+void LLAgent::setSitCamera(const LLUUID &object_id, const LLVector3 &camera_pos, const LLVector3 &camera_focus)
+{
+ BOOL camera_enabled = !object_id.isNull();
+
+ if (camera_enabled)
+ {
+ LLViewerObject *reference_object = gObjectList.findObject(object_id);
+ if (reference_object)
+ {
+ //convert to root object relative?
+ mSitCameraPos = camera_pos;
+ mSitCameraFocus = camera_focus;
+ mSitCameraReferenceObject = reference_object;
+ mSitCameraEnabled = TRUE;
+ }
+ }
+ else
+ {
+ mSitCameraPos.clearVec();
+ mSitCameraFocus.clearVec();
+ mSitCameraReferenceObject = NULL;
+ mSitCameraEnabled = FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setFocusOnAvatar()
+//-----------------------------------------------------------------------------
+void LLAgent::setFocusOnAvatar(BOOL focus_on_avatar, BOOL animate)
+{
+ if (focus_on_avatar != mFocusOnAvatar)
+ {
+ if (animate)
+ {
+ startCameraAnimation();
+ }
+ else
+ {
+ stopCameraAnimation();
+ }
+ }
+
+ //RN: when focused on the avatar, we're not "looking" at it
+ // looking implies intent while focusing on avatar means
+ // you're just walking around with a camera on you...eesh.
+ if (focus_on_avatar && !mFocusOnAvatar)
+ {
+ setFocusGlobal(LLVector3d::zero);
+ mCameraFOVZoomFactor = 0.f;
+ if (mCameraMode == CAMERA_MODE_THIRD_PERSON)
+ {
+ LLVector3 at_axis;
+ if (!mAvatarObject.isNull() && mAvatarObject->getParent())
+ {
+ LLQuaternion obj_rot = ((LLViewerObject*)mAvatarObject->getParent())->getRenderRotation();
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis * ~obj_rot);
+ }
+ else
+ {
+ at_axis = gCamera->getAtAxis();
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ resetAxes(at_axis);
+ }
+ }
+ }
+
+ mFocusOnAvatar = focus_on_avatar;
+}
+
+//-----------------------------------------------------------------------------
+// heardChat()
+//-----------------------------------------------------------------------------
+void LLAgent::heardChat(const LLChat& chat)
+{
+ if (chat.mChatType == CHAT_TYPE_START
+ || chat.mChatType == CHAT_TYPE_STOP)
+ {
+ return;
+ }
+
+ mLastChatterID = chat.mFromID;
+ mChatTimer.reset();
+
+ mNearChatRadius = CHAT_NORMAL_RADIUS / 2.f;
+}
+
+//-----------------------------------------------------------------------------
+// lookAtLastChat()
+//-----------------------------------------------------------------------------
+void LLAgent::lookAtLastChat()
+{
+ // Block if camera is animating or not in normal third person camera mode
+ if (mCameraAnimating || !cameraThirdPerson())
+ {
+ return;
+ }
+
+ LLViewerObject *chatter = gObjectList.findObject(mLastChatterID);
+ if (chatter)
+ {
+ LLVector3 delta_pos;
+ if (chatter->isAvatar())
+ {
+ LLVOAvatar *chatter_av = (LLVOAvatar*)chatter;
+ if (!mAvatarObject.isNull() && chatter_av->mHeadp)
+ {
+ delta_pos = chatter_av->mHeadp->getWorldPosition() - mAvatarObject->mHeadp->getWorldPosition();
+ }
+ else
+ {
+ delta_pos = chatter->getPositionAgent() - getPositionAgent();
+ }
+ delta_pos.normVec();
+
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ changeCameraToThirdPerson();
+
+ LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition();
+ LLVector3 left = delta_pos % LLVector3::z_axis;
+ left.normVec();
+ LLVector3 up = left % delta_pos;
+ up.normVec();
+ new_camera_pos -= delta_pos * 0.4f;
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
+ if (chatter_av->mHeadp)
+ {
+ setFocusGlobal(getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition()), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - gAgent.getPosGlobalFromAgent(chatter_av->mHeadp->getWorldPosition());
+ }
+ else
+ {
+ setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
+ }
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+ else
+ {
+ delta_pos = chatter->getRenderPosition() - getPositionAgent();
+ delta_pos.normVec();
+
+ setControlFlags(AGENT_CONTROL_STOP);
+
+ changeCameraToThirdPerson();
+
+ LLVector3 new_camera_pos = mAvatarObject->mHeadp->getWorldPosition();
+ LLVector3 left = delta_pos % LLVector3::z_axis;
+ left.normVec();
+ LLVector3 up = left % delta_pos;
+ up.normVec();
+ new_camera_pos -= delta_pos * 0.4f;
+ new_camera_pos += left * 0.3f;
+ new_camera_pos += up * 0.2f;
+
+ setFocusGlobal(chatter->getPositionGlobal(), mLastChatterID);
+ mCameraFocusOffsetTarget = getPosGlobalFromAgent(new_camera_pos) - chatter->getPositionGlobal();
+ setFocusOnAvatar(FALSE, TRUE);
+ }
+ }
+}
+
+const F32 SIT_POINT_EXTENTS = 0.2f;
+
+// Grabs current position
+void LLAgent::setStartPosition(U32 location_id)
+{
+ LLViewerObject *object;
+
+ if ( !(gAgentID == LLUUID::null) )
+ {
+ // we've got an ID for an agent viewerobject
+ object = gObjectList.findObject(gAgentID);
+ if (object)
+ {
+ // we've got the viewer object
+ // Sometimes the agent can be velocity interpolated off of
+ // this simulator. Clamp it to the region the agent is
+ // in, a little bit in on each side.
+ const F32 INSET = 0.5f; //meters
+ const F32 REGION_WIDTH = gWorldPointer->getRegionWidthInMeters();
+
+ LLVector3 agent_pos = getPositionAgent();
+
+ if (mAvatarObject)
+ {
+ // the z height is at the agent's feet
+ agent_pos.mV[VZ] -= 0.5f * mAvatarObject->mBodySize.mV[VZ];
+ }
+
+ agent_pos.mV[VX] = llclamp( agent_pos.mV[VX], INSET, REGION_WIDTH - INSET );
+ agent_pos.mV[VY] = llclamp( agent_pos.mV[VY], INSET, REGION_WIDTH - INSET );
+
+ // Don't let them go below ground, or too high.
+ agent_pos.mV[VZ] = llclamp( agent_pos.mV[VZ],
+ mRegionp->getLandHeightRegion( agent_pos ),
+ gWorldPointer->getRegionMaxHeight() );
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetStartLocationRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->nextBlockFast( _PREHASH_StartLocationData);
+ // corrected by sim
+ msg->addStringFast(_PREHASH_SimName, "");
+ msg->addU32Fast(_PREHASH_LocationID, location_id);
+ msg->addVector3Fast(_PREHASH_LocationPos, agent_pos);
+ msg->addVector3Fast(_PREHASH_LocationLookAt,mFrameAgent.getAtAxis());
+
+ // Reliable only helps when setting home location. Last
+ // location is sent on quit, and we don't have time to ack
+ // the packets.
+ msg->sendReliable(mRegionp->getHost());
+
+ const U32 HOME_INDEX = 1;
+ if( HOME_INDEX == location_id )
+ {
+ setHomePosRegion( mRegionp->getHandle(), getPositionAgent() );
+ }
+ }
+ else
+ {
+ llinfos << "setStartPosition - Can't find agent viewerobject id " << gAgentID << llendl;
+ }
+ }
+}
+
+void LLAgent::requestStopMotion( LLMotion* motion )
+{
+ // Notify all avatars that a motion has stopped.
+ // This is needed to clear the animation state bits
+ LLUUID anim_state = motion->getID();
+
+ // if motion is not looping, it could have stopped by running out of time
+ // so we need to tell the server this
+// llinfos << "Sending stop for motion " << motion->getName() << llendl;
+ sendAnimationRequest( anim_state, ANIM_REQUEST_STOP );
+
+ // handle automatic state transitions (based on completion of animation playback)
+ if (anim_state == ANIM_AGENT_STANDUP)
+ {
+ // send stand up command
+ setControlFlags(AGENT_CONTROL_FINISH_ANIM);
+
+ // now trigger dusting self off animation
+ if (mAvatarObject.notNull() && !mAvatarObject->mBelowWater && rand() % 3 == 0)
+ sendAnimationRequest( ANIM_AGENT_BRUSH, ANIM_REQUEST_START );
+ }
+ else if (anim_state == ANIM_AGENT_PRE_JUMP || anim_state == ANIM_AGENT_LAND || anim_state == ANIM_AGENT_MEDIUM_LAND)
+ {
+ setControlFlags(AGENT_CONTROL_FINISH_ANIM);
+ }
+}
+
+BOOL LLAgent::isGodlike() const
+{
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+ if(mAdminOverride) return TRUE;
+ return mGodLevel > GOD_NOT;
+#endif
+}
+
+U8 LLAgent::getGodLevel() const
+{
+#ifdef HACKED_GODLIKE_VIEWER
+ return GOD_MAINTENANCE;
+#else
+ if(mAdminOverride) return GOD_FULL;
+ return mGodLevel;
+#endif
+}
+
+
+void LLAgent::buildFullname(std::string& name) const
+{
+ if (mAvatarObject)
+ {
+ name = mAvatarObject->getFullname();
+ }
+}
+
+void LLAgent::buildFullnameAndTitle(std::string& name) const
+{
+ if (isGroupMember())
+ {
+ name = mGroupTitle;
+ name += ' ';
+ }
+ else
+ {
+ name.erase(0, name.length());
+ }
+
+ if (mAvatarObject)
+ {
+ name += mAvatarObject->getFullname();
+ }
+}
+
+BOOL LLAgent::isInGroup(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// This implementation should mirror LLAgentInfo::hasPowerInGroup
+BOOL LLAgent::hasPowerInGroup(const LLUUID& group_id, U64 power) const
+{
+ // GP_NO_POWERS can also mean no power is enough to grant an ability.
+ if (GP_NO_POWERS == power) return FALSE;
+
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return (BOOL)((mGroups.get(i).mPowers & power) > 0);
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::hasPowerInActiveGroup(U64 power) const
+{
+ return (mGroupID.notNull() && (hasPowerInGroup(mGroupID, power)));
+}
+
+U64 LLAgent::getPowerInGroup(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ return (mGroups.get(i).mPowers);
+ }
+ }
+
+ return GP_NO_POWERS;
+}
+
+BOOL LLAgent::getGroupData(const LLUUID& group_id, LLGroupData& data) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ data = mGroups.get(i);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+S32 LLAgent::getGroupContribution(const LLUUID& group_id) const
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ S32 contribution = mGroups.get(i).mContribution;
+ return contribution;
+ }
+ }
+ return 0;
+}
+
+BOOL LLAgent::setGroupContribution(const LLUUID& group_id, S32 contribution)
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ mGroups.get(i).mContribution = contribution;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SetGroupContribution");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgentID);
+ msg->addUUID("SessionID", gAgentSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id);
+ msg->addS32("Contribution", contribution);
+ sendReliableMessage();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::setGroupAcceptNotices(const LLUUID& group_id, BOOL accept_notices)
+{
+ S32 count = mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(mGroups.get(i).mID == group_id)
+ {
+ mGroups.get(i).mAcceptNotices = accept_notices;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SetGroupAcceptNotices");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgentID);
+ msg->addUUID("SessionID", gAgentSessionID);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id);
+ msg->addBOOL("AcceptNotices", accept_notices);
+ sendReliableMessage();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// utility to build a location string
+void LLAgent::buildLocationString(std::string& str)
+{
+ const LLVector3& agent_pos_region = getPositionAgent();
+ S32 pos_x = S32(agent_pos_region.mV[VX]);
+ S32 pos_y = S32(agent_pos_region.mV[VY]);
+ S32 pos_z = S32(agent_pos_region.mV[VZ]);
+
+ // Round the numbers based on the velocity
+ LLVector3 agent_velocity = getVelocity();
+ F32 velocity_mag_sq = agent_velocity.magVecSquared();
+
+ const F32 FLY_CUTOFF = 6.f; // meters/sec
+ const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
+ const F32 WALK_CUTOFF = 1.5f; // meters/sec
+ const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
+
+ if (velocity_mag_sq > FLY_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 4;
+ pos_y -= pos_y % 4;
+ }
+ else if (velocity_mag_sq > WALK_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 2;
+ pos_y -= pos_y % 2;
+ }
+
+ // create a defult name and description for the landmark
+ std::string buffer;
+ if( !strcmp("", gParcelMgr->getAgentParcelName()) )
+ {
+ // the parcel doesn't have a name
+ buffer = llformat("%.32s (%d, %d, %d)",
+ getRegion()->getName().c_str(),
+ pos_x, pos_y, pos_z);
+ }
+ else
+ {
+ // the parcel has a name, so include it in the landmark name
+ buffer = llformat("%.32s, %.32s (%d, %d, %d)",
+ gParcelMgr->getAgentParcelName(),
+ getRegion()->getName().c_str(),
+ pos_x, pos_y, pos_z);
+ }
+ str = buffer;
+}
+
+LLQuaternion LLAgent::getHeadRotation()
+{
+ if (mAvatarObject.isNull() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp)
+ {
+ return LLQuaternion::DEFAULT;
+ }
+
+ if (!gAgent.cameraMouselook())
+ {
+ return mAvatarObject->getRotation();
+ }
+
+ // We must be in mouselook
+ LLVector3 look_dir( gCamera->getAtAxis() );
+ LLVector3 up = look_dir % mFrameAgent.getLeftAxis();
+ LLVector3 left = up % look_dir;
+
+ LLQuaternion rot(look_dir, left, up);
+ if (mAvatarObject->getParent())
+ {
+ rot = rot * ~mAvatarObject->getParent()->getRotation();
+ }
+
+ return rot;
+}
+
+void LLAgent::sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request)
+{
+ if (gAgentID.isNull())
+ {
+ return;
+ }
+
+ S32 num_valid_anims = 0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentAnimation);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ for (S32 i = 0; i < anim_ids.count(); i++)
+ {
+ if (anim_ids[i].isNull())
+ {
+ continue;
+ }
+ msg->nextBlockFast(_PREHASH_AnimationList);
+ msg->addUUIDFast(_PREHASH_AnimID, (anim_ids[i]) );
+ msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
+ num_valid_anims++;
+ }
+
+ if (num_valid_anims)
+ {
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request)
+{
+ if (gAgentID.isNull() || anim_id.isNull() || !mRegionp)
+ {
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentAnimation);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ msg->nextBlockFast(_PREHASH_AnimationList);
+ msg->addUUIDFast(_PREHASH_AnimID, (anim_id) );
+ msg->addBOOLFast(_PREHASH_StartAnim, (request == ANIM_REQUEST_START) ? TRUE : FALSE);
+
+ sendReliableMessage();
+}
+
+void LLAgent::friendsChanged()
+{
+ LLCollectProxyBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ mProxyForAgents = collector.mProxy;
+}
+
+BOOL LLAgent::isGrantedProxy(const LLPermissions& perm)
+{
+ return (mProxyForAgents.count(perm.getOwner()) > 0);
+}
+
+BOOL LLAgent::allowOperation(PermissionBit op,
+ const LLPermissions& perm,
+ U64 group_proxy_power,
+ U8 god_minimum)
+{
+ // Check god level.
+ if (getGodLevel() >= god_minimum) return TRUE;
+
+ if (!perm.isOwned()) return FALSE;
+
+ // A group member with group_proxy_power can act as owner.
+ BOOL is_group_owned;
+ LLUUID owner_id;
+ perm.getOwnership(owner_id, is_group_owned);
+ LLUUID group_id(perm.getGroup());
+ LLUUID agent_proxy(getID());
+
+ if (is_group_owned)
+ {
+ if (hasPowerInGroup(group_id, group_proxy_power))
+ {
+ // Let the member assume the group's id for permission requests.
+ agent_proxy = owner_id;
+ }
+ }
+ else
+ {
+ // Check for granted mod permissions.
+ if ((PERM_OWNER != op) && isGrantedProxy(perm))
+ {
+ agent_proxy = owner_id;
+ }
+ }
+
+ // This is the group id to use for permission requests.
+ // Only group members may use this field.
+ LLUUID group_proxy = LLUUID::null;
+ if (group_id.notNull() && isInGroup(group_id))
+ {
+ group_proxy = group_id;
+ }
+
+ // We now have max ownership information.
+ if (PERM_OWNER == op)
+ {
+ // This this was just a check for ownership, we can now return the answer.
+ return (agent_proxy == owner_id);
+ }
+
+ return perm.allowOperationBy(op, agent_proxy, group_proxy);
+}
+
+
+void LLAgent::getName(LLString& name)
+{
+ // Note: assumes that name points to a buffer of at least DB_FULL_NAME_BUF_SIZE bytes.
+ name.clear();
+
+ if (mAvatarObject)
+ {
+ LLNameValue *first_nv = mAvatarObject->getNVPair("FirstName");
+ LLNameValue *last_nv = mAvatarObject->getNVPair("LastName");
+ if (first_nv && last_nv)
+ {
+ name = first_nv->printData() + " " + last_nv->printData();
+ }
+ else
+ {
+ llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl;
+ }
+ }
+ else
+ {
+ name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName");
+ }
+}
+
+const LLColor4 &LLAgent::getEffectColor()
+{
+ return mEffectColor;
+}
+
+void LLAgent::setEffectColor(const LLColor4 &color)
+{
+ mEffectColor = color;
+}
+
+void LLAgent::initOriginGlobal(const LLVector3d &origin_global)
+{
+ mAgentOriginGlobal = origin_global;
+}
+
+void update_group_floaters(const LLUUID& group_id)
+{
+ // *HACK: added to do a live update of the groups floater if it is
+ // open.
+ LLFloaterGroups* fg = LLFloaterGroups::getInstance(gAgent.getID());
+ if(fg)
+ {
+ fg->reset();
+ }
+
+ LLFloaterGroupInfo::refreshGroup(group_id);
+
+ // update avatar info
+ LLFloaterAvatarInfo* fa = LLFloaterAvatarInfo::getInstance(gAgent.getID());
+ if(fa)
+ {
+ fa->resetGroupList();
+ }
+
+ if (gIMView)
+ {
+ // update the talk view
+ gIMView->refresh();
+ }
+}
+
+// static
+void LLAgent::processAgentDropGroup(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentDropGroup for agent other than me" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ // Remove the group if it already exists remove it and add the new data to pick up changes.
+ LLGroupData gd;
+ gd.mID = group_id;
+ S32 index = gAgent.mGroups.find(gd);
+ if (index != -1)
+ {
+ gAgent.mGroups.remove(index);
+ if (gAgent.getGroupID() == group_id)
+ {
+ gAgent.mGroupID.setNull();
+ gAgent.mGroupPowers = 0;
+ gAgent.mGroupName[0] = '\0';
+ gAgent.mGroupTitle[0] = '\0';
+ }
+
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // close the floater for this group, if any.
+ LLFloaterGroupInfo::closeGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+ else
+ {
+ llwarns << "processAgentDropGroup, agent is not part of group " << group_id << llendl;
+ }
+}
+
+// static
+void LLAgent::processAgentGroupDataUpdate(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentGroupDataUpdate for agent other than me" << llendl;
+ return;
+ }
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ LLGroupData group;
+ S32 index = -1;
+ bool need_floater_update = false;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group.mID, i);
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupInsigniaID, group.mInsigniaID, i);
+ msg->getU64(_PREHASH_GroupData, "GroupPowers", group.mPowers, i);
+ msg->getBOOL(_PREHASH_GroupData, "AcceptNotices", group.mAcceptNotices, i);
+ msg->getS32(_PREHASH_GroupData, "Contribution", group.mContribution, i);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, group_name, i);
+ group.mName.assign(group_name);
+
+ if(group.mID.notNull())
+ {
+ need_floater_update = true;
+ // Remove the group if it already exists remove it and add the new data to pick up changes.
+ index = gAgent.mGroups.find(group);
+ if (index != -1)
+ {
+ gAgent.mGroups.remove(index);
+ }
+ gAgent.mGroups.put(group);
+ }
+ if (need_floater_update)
+ {
+ update_group_floaters(group.mID);
+ }
+ }
+
+}
+
+// static
+void LLAgent::processAgentDataUpdate(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ if (agent_id != gAgentID)
+ {
+ llwarns << "processAgentDataUpdate for agent other than me" << llendl;
+ return;
+ }
+
+ msg->getStringFast(_PREHASH_AgentData, _PREHASH_GroupTitle, DB_GROUP_TITLE_BUF_SIZE, gAgent.mGroupTitle);
+ LLUUID active_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_ActiveGroupID, active_id);
+
+
+ if(active_id.notNull())
+ {
+ gAgent.mGroupID = active_id;
+ msg->getU64(_PREHASH_AgentData, "GroupPowers", gAgent.mGroupPowers);
+ msg->getString(_PREHASH_AgentData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, gAgent.mGroupName);
+ }
+ else
+ {
+ gAgent.mGroupID.setNull();
+ gAgent.mGroupPowers = 0;
+ gAgent.mGroupName[0] = '\0';
+ }
+
+ update_group_floaters(active_id);
+
+ gAgent.fireEvent(new LLEvent(&gAgent, "new group"), "");
+}
+
+// static
+void LLAgent::processScriptControlChange(LLMessageSystem *msg, void **)
+{
+ S32 block_count = msg->getNumberOfBlocks("Data");
+ for (S32 block_index = 0; block_index < block_count; block_index++)
+ {
+ BOOL take_controls;
+ U32 controls;
+ BOOL passon;
+ U32 i;
+ msg->getBOOL("Data", "TakeControls", take_controls, block_index);
+ if (take_controls)
+ {
+ // take controls
+ msg->getU32("Data", "Controls", controls, block_index );
+ msg->getBOOL("Data", "PassToAgent", passon, block_index );
+ U32 total_count = 0;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]++;
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]++;
+ }
+ total_count++;
+ }
+ }
+
+ // Any control taken? If so, might be first time.
+ if (total_count > 0)
+ {
+ LLFirstUse::useOverrideKeys();
+ }
+ }
+ else
+ {
+ // release controls
+ msg->getU32("Data", "Controls", controls, block_index );
+ msg->getBOOL("Data", "PassToAgent", passon, block_index );
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]--;
+ if (gAgent.mControlsTakenPassedOnCount[i] < 0)
+ {
+ gAgent.mControlsTakenPassedOnCount[i] = 0;
+ }
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]--;
+ if (gAgent.mControlsTakenCount[i] < 0)
+ {
+ gAgent.mControlsTakenCount[i] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+// static
+void LLAgent::processControlTake(LLMessageSystem *msg, void **)
+{
+ U32 controls;
+ msg->getU32("Data", "Controls", controls );
+ U32 passon;
+ msg->getBOOL("Data", "PassToAgent", passon );
+
+ S32 i;
+ S32 total_count = 0;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]++;
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]++;
+ }
+ total_count++;
+ }
+ }
+
+ // Any control taken? If so, might be first time.
+ if (total_count > 0)
+ {
+ LLFirstUse::useOverrideKeys();
+ }
+}
+
+// static
+void LLAgent::processControlRelease(LLMessageSystem *msg, void **)
+{
+ U32 controls;
+ msg->getU32("Data", "Controls", controls );
+ U32 passon;
+ msg->getBOOL("Data", "PassToAgent", passon );
+
+ S32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (controls & ( 1 << i))
+ {
+ if (passon)
+ {
+ gAgent.mControlsTakenPassedOnCount[i]--;
+ if (gAgent.mControlsTakenPassedOnCount[i] < 0)
+ {
+ gAgent.mControlsTakenPassedOnCount[i] = 0;
+ }
+ }
+ else
+ {
+ gAgent.mControlsTakenCount[i]--;
+ if (gAgent.mControlsTakenCount[i] < 0)
+ {
+ gAgent.mControlsTakenCount[i] = 0;
+ }
+ }
+ }
+ }
+}
+*/
+
+//static
+void LLAgent::processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data)
+{
+ gAgent.mNumPendingQueries--;
+
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp || avatarp->isDead())
+ {
+ llwarns << "No avatar for user in cached texture update!" << llendl;
+ return;
+ }
+
+ if (gAgent.cameraCustomizeAvatar())
+ {
+ // ignore baked textures when in customize mode
+ return;
+ }
+
+ S32 query_id;
+ mesgsys->getS32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, query_id);
+
+ S32 num_texture_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_WearableData);
+
+
+ S32 num_results = 0;
+ for (S32 texture_block = 0; texture_block < num_texture_blocks; texture_block++)
+ {
+ LLUUID texture_id;
+ U8 texture_index;
+
+ mesgsys->getUUIDFast(_PREHASH_WearableData, _PREHASH_TextureID, texture_id, texture_block);
+ mesgsys->getU8Fast(_PREHASH_WearableData, _PREHASH_TextureIndex, texture_index, texture_block);
+
+ if (texture_id.notNull()
+ && (S32)texture_index < BAKED_TEXTURE_COUNT
+ && gAgent.mActiveCacheQueries[ texture_index ] == query_id)
+ {
+ //llinfos << "Received cached texture " << (U32)texture_index << ": " << texture_id << llendl;
+ avatarp->setCachedBakedTexture((LLVOAvatar::ETextureIndex)LLVOAvatar::sBakedTextureIndices[texture_index], texture_id);
+ //avatarp->setTETexture( LLVOAvatar::sBakedTextureIndices[texture_index], texture_id );
+ gAgent.mActiveCacheQueries[ texture_index ] = 0;
+ num_results++;
+ }
+ }
+
+ llinfos << "Received cached texture response for " << num_results << " textures." << llendl;
+
+ avatarp->updateMeshTextures();
+
+ if (gAgent.mNumPendingQueries == 0)
+ {
+ // RN: not sure why composites are disabled at this point
+ avatarp->setCompositeUpdatesEnabled(TRUE);
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+BOOL LLAgent::anyControlGrabbed() const
+{
+ U32 i;
+ for (i = 0; i < TOTAL_CONTROLS; i++)
+ {
+ if (gAgent.mControlsTakenCount[i] > 0)
+ return TRUE;
+ if (gAgent.mControlsTakenPassedOnCount[i] > 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::isControlGrabbed(S32 control_index) const
+{
+ return mControlsTakenCount[control_index] > 0;
+}
+
+void LLAgent::forceReleaseControls()
+{
+ gMessageSystem->newMessage("ForceScriptControlRelease");
+ gMessageSystem->nextBlock("AgentData");
+ gMessageSystem->addUUID("AgentID", getID());
+ gMessageSystem->addUUID("SessionID", getSessionID());
+ sendReliableMessage();
+}
+
+void LLAgent::setHomePosRegion( const U64& region_handle, const LLVector3& pos_region)
+{
+ mHaveHomePosition = TRUE;
+ mHomeRegionHandle = region_handle;
+ mHomePosRegion = pos_region;
+}
+
+BOOL LLAgent::getHomePosGlobal( LLVector3d* pos_global )
+{
+ if(!mHaveHomePosition)
+ {
+ return FALSE;
+ }
+ F32 x = 0;
+ F32 y = 0;
+ from_region_handle( mHomeRegionHandle, &x, &y);
+ pos_global->setVec( x + mHomePosRegion.mV[VX], y + mHomePosRegion.mV[VY], mHomePosRegion.mV[VZ] );
+ return TRUE;
+}
+
+void LLAgent::clearVisualParams(void *data)
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp)
+ {
+ avatarp->clearVisualParamWeights();
+ avatarp->updateVisualParams();
+ }
+}
+
+//---------------------------------------------------------------------------
+// Teleport
+//---------------------------------------------------------------------------
+
+// teleportCore() - stuff to do on any teleport
+// protected
+bool LLAgent::teleportCore(bool is_local)
+{
+ if(TELEPORT_NONE != mTeleportState)
+ {
+ llwarns << "Attempt to teleport when already teleporting." << llendl;
+ return false;
+ }
+
+ // Don't call LLFirstUse::useTeleport because we don't know
+ // yet if the teleport will succeed. Look in
+ // process_teleport_location_reply
+
+ // close the map and find panels so we can see our destination
+ LLFloaterWorldMap::hide(NULL);
+ LLFloaterDirectory::hide(NULL);
+
+ // Close all pie menus, deselect land, etc.
+ // Don't change the camera until we know teleport succeeded. JC
+ resetView(FALSE);
+
+ // local logic
+ gViewerStats->incStat(LLViewerStats::ST_TELEPORT_COUNT);
+ if (!is_local)
+ {
+ gTeleportDisplay = TRUE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_START );
+ }
+ make_ui_sound("UISndTeleportOut");
+ return true;
+}
+
+void LLAgent::teleportRequest(
+ const U64& region_handle,
+ const LLVector3& pos_local)
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ llinfos << "TeleportRequest: '" << region_handle << "':" << pos_local
+ << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TeleportLocationRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU64("RegionHandle", region_handle);
+ msg->addVector3("Position", pos_local);
+ LLVector3 look_at(0,1,0);
+ msg->addVector3("LookAt", look_at);
+ sendReliableMessage();
+ }
+}
+
+// Landmark ID = LLUUID::null means teleport home
+void LLAgent::teleportViaLandmark(const LLUUID& landmark_id)
+{
+ LLViewerRegion *regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLandmarkRequest);
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->addUUIDFast(_PREHASH_LandmarkID, landmark_id);
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::teleportViaLure(const LLUUID& lure_id, BOOL godlike)
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp && teleportCore())
+ {
+ U32 teleport_flags = 0x0;
+ if (godlike)
+ {
+ teleport_flags |= TELEPORT_FLAGS_VIA_GODLIKE_LURE;
+ teleport_flags |= TELEPORT_FLAGS_DISABLE_CANCEL;
+ }
+ else
+ {
+ teleport_flags |= TELEPORT_FLAGS_VIA_LURE;
+ }
+
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLureRequest);
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ msg->addUUIDFast(_PREHASH_LureID, lure_id);
+ msg->addU32("TeleportFlags", teleport_flags);
+ sendReliableMessage();
+ }
+}
+
+
+// James Cook, July 28, 2005
+void LLAgent::teleportCancel()
+{
+ LLViewerRegion* regionp = getRegion();
+ if(regionp)
+ {
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TeleportCancel");
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ sendReliableMessage();
+ }
+ gTeleportDisplay = FALSE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_CANCELLING );
+}
+
+
+void LLAgent::teleportViaLocation(const LLVector3d& pos_global)
+{
+ LLViewerRegion* regionp = getRegion();
+ LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if(regionp && info)
+ {
+ U32 x_pos;
+ U32 y_pos;
+ from_region_handle(info->mHandle, &x_pos, &y_pos);
+ LLVector3 pos_local(
+ (F32)(pos_global.mdV[VX] - x_pos),
+ (F32)(pos_global.mdV[VY] - y_pos),
+ (F32)(pos_global.mdV[VZ]));
+ teleportRequest(info->mHandle, pos_local);
+ }
+ else if(regionp &&
+ teleportCore(regionp->getHandle() == to_region_handle_global((F32)pos_global.mdV[VX], (F32)pos_global.mdV[VY])))
+ {
+ llwarns << "Using deprecated teleportlocationrequest." << llendl;
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TeleportLocationRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ msg->nextBlockFast(_PREHASH_Info);
+ F32 width = regionp->getWidth();
+ LLVector3 pos(fmod((F32)pos_global.mdV[VX], width),
+ fmod((F32)pos_global.mdV[VY], width),
+ (F32)pos_global.mdV[VZ]);
+ F32 region_x = (F32)(pos_global.mdV[VX]);
+ F32 region_y = (F32)(pos_global.mdV[VY]);
+ U64 region_handle = to_region_handle_global(region_x, region_y);
+ msg->addU64Fast(_PREHASH_RegionHandle, region_handle);
+ msg->addVector3Fast(_PREHASH_Position, pos);
+ pos.mV[VX] += 1;
+ msg->addVector3Fast(_PREHASH_LookAt, pos);
+ sendReliableMessage();
+ }
+}
+
+void LLAgent::setTeleportState(ETeleportState state)
+{
+ mTeleportState = state;
+ if (mTeleportState > TELEPORT_NONE && gSavedSettings.getBOOL("FreezeTime"))
+ {
+ LLFloaterSnapshot::hide(0);
+ }
+}
+
+void LLAgent::fidget()
+{
+ if (!getAFK())
+ {
+ F32 curTime = mFidgetTimer.getElapsedTimeF32();
+ if (curTime > mNextFidgetTime)
+ {
+ // pick a random fidget anim here
+ S32 oldFidget = mCurrentFidget;
+
+ mCurrentFidget = gLindenLabRandomNumber.llrand(NUM_AGENT_STAND_ANIMS);
+
+ if (mCurrentFidget != oldFidget)
+ {
+ LLAgent::stopFidget();
+
+
+ switch(mCurrentFidget)
+ {
+ case 0:
+ mCurrentFidget = 0;
+ break;
+ case 1:
+ sendAnimationRequest(ANIM_AGENT_STAND_1, ANIM_REQUEST_START);
+ mCurrentFidget = 1;
+ break;
+ case 2:
+ sendAnimationRequest(ANIM_AGENT_STAND_2, ANIM_REQUEST_START);
+ mCurrentFidget = 2;
+ break;
+ case 3:
+ sendAnimationRequest(ANIM_AGENT_STAND_3, ANIM_REQUEST_START);
+ mCurrentFidget = 3;
+ break;
+ case 4:
+ sendAnimationRequest(ANIM_AGENT_STAND_4, ANIM_REQUEST_START);
+ mCurrentFidget = 4;
+ break;
+ }
+ }
+
+ // calculate next fidget time
+ mNextFidgetTime = curTime + gLindenLabRandomNumber.llfrand(MAX_FIDGET_TIME - MIN_FIDGET_TIME) + MIN_FIDGET_TIME;
+ }
+ }
+}
+
+void LLAgent::stopFidget()
+{
+ LLDynamicArray<LLUUID> anims;
+ anims.put(ANIM_AGENT_STAND_1);
+ anims.put(ANIM_AGENT_STAND_2);
+ anims.put(ANIM_AGENT_STAND_3);
+ anims.put(ANIM_AGENT_STAND_4);
+
+ gAgent.sendAnimationRequests(anims, ANIM_REQUEST_STOP);
+}
+
+
+void LLAgent::requestEnterGodMode()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestGodlikePowers);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RequestBlock);
+ msg->addBOOLFast(_PREHASH_Godlike, TRUE);
+ msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
+
+ // simulator and userserver need to know about your request
+ sendReliableMessage();
+ msg->sendReliable(gUserServer);
+}
+
+void LLAgent::requestLeaveGodMode()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestGodlikePowers);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RequestBlock);
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE);
+ msg->addUUIDFast(_PREHASH_Token, LLUUID::null);
+
+ // simulator and userserver need to know about your request
+ sendReliableMessage();
+ msg->sendReliable(gUserServer);
+}
+
+// wearables
+LLAgent::createStandardWearablesAllDoneCallback::~createStandardWearablesAllDoneCallback()
+{
+ gAgent.createStandardWearablesAllDone();
+}
+
+LLAgent::sendAgentWearablesUpdateCallback::~sendAgentWearablesUpdateCallback()
+{
+ gAgent.sendAgentWearablesUpdate();
+}
+
+LLAgent::addWearableToAgentInventoryCallback::addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount> cb, S32 index, LLWearable* wearable, U32 todo) :
+ mIndex(index),
+ mWearable(wearable),
+ mTodo(todo),
+ mCB(cb)
+{
+}
+
+void LLAgent::addWearableToAgentInventoryCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ gAgent.addWearabletoAgentInventoryDone(mIndex, inv_item, mWearable);
+
+ if (mTodo & CALL_UPDATE)
+ {
+ gAgent.sendAgentWearablesUpdate();
+ }
+ if (mTodo & CALL_RECOVERDONE)
+ {
+ gAgent.recoverMissingWearableDone();
+ }
+ /*
+ * Do this for every one in the loop
+ */
+ if (mTodo & CALL_CREATESTANDARDDONE)
+ {
+ gAgent.createStandardWearablesDone(mIndex);
+ }
+ if (mTodo & CALL_MAKENEWOUTFITDONE)
+ {
+ gAgent.makeNewOutfitDone(mIndex);
+ }
+}
+
+void LLAgent::addWearabletoAgentInventoryDone(
+ S32 index,
+ const LLUUID& item_id,
+ LLWearable* wearable)
+{
+ if (item_id.isNull())
+ return;
+
+ LLUUID old_item_id = mWearableEntry[index].mItemID;
+ mWearableEntry[index].mItemID = item_id;
+ mWearableEntry[index].mWearable = wearable;
+ if (old_item_id.notNull())
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if(item && wearable)
+ {
+ // We're changing the asset id, so we both need to set it
+ // locally via setAssetUUID() and via setTransactionID() which
+ // will be decoded on the server. JC
+ item->setAssetUUID(wearable->getID());
+ item->setTransactionID(wearable->getTransactionID());
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item_id);
+ item->updateServer(FALSE);
+ }
+ gInventory.notifyObservers();
+}
+
+void LLAgent::sendAgentWearablesUpdate()
+{
+ // First make sure that we have inventory items for each wearable
+ S32 i;
+ for(i=0; i < WT_COUNT; ++i)
+ {
+ LLWearable* wearable = mWearableEntry[ i ].mWearable;
+ if (wearable)
+ {
+ if( mWearableEntry[ i ].mItemID.isNull() )
+ {
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ i,
+ wearable,
+ addWearableToAgentInventoryCallback::CALL_NONE);
+ addWearableToAgentInventory(cb, wearable);
+ }
+ else
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL,
+ mWearableEntry[i].mItemID );
+ }
+ }
+ }
+
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.notifyObservers();
+
+ // Send the AgentIsNowWearing
+ gMessageSystem->newMessageFast(_PREHASH_AgentIsNowWearing);
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ lldebugs << "sendAgentWearablesUpdate()" << llendl;
+ for(i=0; i < WT_COUNT; ++i)
+ {
+ gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+
+ U8 type_u8 = (U8)i;
+ gMessageSystem->addU8Fast(_PREHASH_WearableType, type_u8 );
+
+ LLWearable* wearable = mWearableEntry[ i ].mWearable;
+ if( wearable )
+ {
+ //llinfos << "Sending wearable " << wearable->getName() << llendl;
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, mWearableEntry[ i ].mItemID );
+ }
+ else
+ {
+ //llinfos << "Not wearing wearable type " << LLWearable::typeToTypeName((EWearableType)i) << llendl;
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, LLUUID::null );
+ }
+
+ lldebugs << " " << LLWearable::typeToTypeLabel((EWearableType)i) << ": " << (wearable ? wearable->getID() : LLUUID::null) << llendl;
+ }
+ gAgent.sendReliableMessage();
+}
+
+void LLAgent::saveWearable( EWearableType type, BOOL send_update )
+{
+ LLWearable* old_wearable = mWearableEntry[(S32)type].mWearable;
+ if( old_wearable && (old_wearable->isDirty() || old_wearable->isOldVersion()) )
+ {
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
+ mWearableEntry[(S32)type].mWearable = new_wearable;
+
+ LLInventoryItem* item = gInventory.getItem(mWearableEntry[(S32)type].mItemID);
+ if( item )
+ {
+ // Update existing inventory item
+ LLPointer<LLViewerInventoryItem> template_item =
+ new LLViewerInventoryItem(item->getUUID(),
+ item->getParentUUID(),
+ item->getPermissions(),
+ new_wearable->getID(),
+ new_wearable->getAssetType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ item->getSaleInfo(),
+ item->getFlags(),
+ item->getCreationDate());
+ template_item->setTransactionID(new_wearable->getTransactionID());
+ template_item->updateServer(FALSE);
+ gInventory.updateItem(template_item);
+ }
+ else
+ {
+ // Add a new inventory item (shouldn't ever happen here)
+ U32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
+ if (send_update)
+ {
+ todo |= addWearableToAgentInventoryCallback::CALL_UPDATE;
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ (S32)type,
+ new_wearable,
+ todo);
+ addWearableToAgentInventory(cb, new_wearable);
+ return;
+ }
+
+ if( send_update )
+ {
+ sendAgentWearablesUpdate();
+ }
+ }
+}
+
+void LLAgent::saveWearableAs(
+ EWearableType type,
+ const std::string& new_name,
+ BOOL save_in_lost_and_found)
+{
+ if(!isWearableCopyable(type))
+ {
+ llwarns << "LLAgent::saveWearableAs() not copyable." << llendl;
+ return;
+ }
+ LLWearable* old_wearable = getWearable(type);
+ if(!old_wearable)
+ {
+ llwarns << "LLAgent::saveWearableAs() no old wearable." << llendl;
+ return;
+ }
+ LLInventoryItem* item = gInventory.getItem(mWearableEntry[type].mItemID);
+ if(!item)
+ {
+ llwarns << "LLAgent::saveWearableAs() no inventory item." << llendl;
+ return;
+ }
+ std::string trunc_name(new_name);
+ LLString::truncate(trunc_name, DB_INV_ITEM_NAME_STR_LEN);
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar(
+ old_wearable,
+ trunc_name);
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ new_wearable,
+ addWearableToAgentInventoryCallback::CALL_UPDATE);
+ LLUUID category_id;
+ if (save_in_lost_and_found)
+ {
+ category_id = gInventory.findCategoryUUIDForType(
+ LLAssetType::AT_LOST_AND_FOUND);
+ }
+ else
+ {
+ // put in same folder as original
+ category_id = item->getParentUUID();
+ }
+
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ category_id,
+ new_name,
+ cb);
+
+/*
+ LLWearable* old_wearable = getWearable( type );
+ if( old_wearable )
+ {
+ LLString old_name = old_wearable->getName();
+ old_wearable->setName( new_name );
+ LLWearable* new_wearable = gWearableList.createCopyFromAvatar( old_wearable );
+ old_wearable->setName( old_name );
+
+ LLUUID category_id;
+ LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
+ if( item )
+ {
+ new_wearable->setPermissions(item->getPermissions());
+ if (save_in_lost_and_found)
+ {
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ }
+ else
+ {
+ // put in same folder as original
+ category_id = item->getParentUUID();
+ }
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO);
+ }
+ }
+
+ mWearableEntry[ type ].mWearable = new_wearable;
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ addWearableToAgentInventoryCallback::CALL_UPDATE);
+ addWearableToAgentInventory(cb, new_wearable, category_id);
+ }
+*/
+}
+
+void LLAgent::revertWearable( EWearableType type )
+{
+ LLWearable* wearable = mWearableEntry[(S32)type].mWearable;
+ if( wearable )
+ {
+ wearable->writeToAvatar( TRUE );
+ }
+ sendAgentSetAppearance();
+}
+
+void LLAgent::revertAllWearables()
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ revertWearable( (EWearableType)i );
+ }
+}
+
+void LLAgent::saveAllWearables()
+{
+ //if(!gInventory.isLoaded())
+ //{
+ // return;
+ //}
+
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ saveWearable( (EWearableType)i, FALSE );
+ }
+ sendAgentWearablesUpdate();
+}
+
+// Called when the user changes the name of a wearable inventory item that is currenlty being worn.
+void LLAgent::setWearableName( const LLUUID& item_id, const std::string& new_name )
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ if( mWearableEntry[i].mItemID == item_id )
+ {
+ LLWearable* old_wearable = mWearableEntry[i].mWearable;
+ llassert( old_wearable );
+
+ LLString old_name = old_wearable->getName();
+ old_wearable->setName( new_name );
+ LLWearable* new_wearable = gWearableList.createCopy( old_wearable );
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ new_wearable->setPermissions(item->getPermissions());
+ }
+ old_wearable->setName( old_name );
+
+ mWearableEntry[i].mWearable = new_wearable;
+ sendAgentWearablesUpdate();
+ break;
+ }
+ }
+}
+
+
+BOOL LLAgent::isWearableModifiable(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item && item->getPermissions().allowModifyBy(gAgent.getID(),
+ gAgent.getGroupID()))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+BOOL LLAgent::isWearableCopyable(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item && item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID()))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+U32 LLAgent::getWearablePermMask(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ if(!item_id.isNull())
+ {
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ return item->getPermissions().getMaskOwner();
+ }
+ }
+ return PERM_NONE;
+}
+
+LLInventoryItem* LLAgent::getWearableInventoryItem(EWearableType type)
+{
+ LLUUID item_id = getWearableItem(type);
+ LLInventoryItem* item = NULL;
+ if(item_id.notNull())
+ {
+ item = gInventory.getItem(item_id);
+ }
+ return item;
+}
+
+LLWearable* LLAgent::getWearableFromWearableItem( const LLUUID& item_id )
+{
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ if( mWearableEntry[i].mItemID == item_id )
+ {
+ return mWearableEntry[i].mWearable;
+ }
+ }
+ return NULL;
+}
+
+
+void LLAgent::sendAgentWearablesRequest()
+{
+ gMessageSystem->newMessageFast(_PREHASH_AgentWearablesRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ sendReliableMessage();
+}
+
+// Used to enable/disable menu items.
+// static
+BOOL LLAgent::selfHasWearable( void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+ return gAgent.getWearable( type ) != NULL;
+}
+
+BOOL LLAgent::isWearingItem( const LLUUID& item_id )
+{
+ return (getWearableFromWearableItem( item_id ) != NULL);
+}
+
+
+// static
+void LLAgent::processAgentInitialWearablesUpdate( LLMessageSystem* mesgsys, void** user_data )
+{
+ // We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates
+ // that may result from AgentWearablesRequest having been sent more than once.
+ static BOOL first = TRUE;
+ if( first )
+ {
+ first = FALSE;
+ }
+ else
+ {
+ return;
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ LLUUID agent_id;
+ gMessageSystem->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && (agent_id == avatar->getID()) )
+ {
+ gMessageSystem->getU32Fast(_PREHASH_AgentData, _PREHASH_SerialNum, gAgent.mAgentWearablesUpdateSerialNum );
+
+ S32 num_wearables = gMessageSystem->getNumberOfBlocksFast(_PREHASH_WearableData);
+ if( num_wearables < 4 )
+ {
+ // Transitional state. Avatars should always have at least their body parts (hair, eyes, shape and skin).
+ // The fact that they don't have any here (only a dummy is sent) implies that this account existed
+ // before we had wearables, or that the database has gotten messed up.
+ // Deal with this by creating new body parts.
+ //avatar->createStandardWearables();
+
+ // no, deal with it by noting that we need to choose a
+ // gender.
+ gAgent.setGenderChosen(FALSE);
+ return;
+ }
+
+ //lldebugs << "processAgentInitialWearablesUpdate()" << llendl;
+ // Add wearables
+ LLUUID asset_id_array[ WT_COUNT ];
+ S32 i;
+ for( i=0; i < num_wearables; i++ )
+ {
+ U8 type_u8 = 0;
+ gMessageSystem->getU8Fast(_PREHASH_WearableData, _PREHASH_WearableType, type_u8, i );
+ if( type_u8 >= WT_COUNT )
+ {
+ continue;
+ }
+ EWearableType type = (EWearableType) type_u8;
+
+ LLUUID item_id;
+ gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_ItemID, item_id, i );
+
+ LLUUID asset_id;
+ gMessageSystem->getUUIDFast(_PREHASH_WearableData, _PREHASH_AssetID, asset_id, i );
+ if( asset_id.isNull() )
+ {
+ LLWearable::removeFromAvatar( type, FALSE );
+ }
+ else
+ {
+ LLAssetType::EType asset_type = LLWearable::typeToAssetType( type );
+ if( asset_type == LLAssetType::AT_NONE )
+ {
+ continue;
+ }
+
+ gAgent.mWearableEntry[type].mItemID = item_id;
+ asset_id_array[type] = asset_id;
+ }
+
+ lldebugs << " " << LLWearable::typeToTypeLabel(type) << llendl;
+ }
+
+ // now that we have the asset ids...request the wearable assets
+ for( i = 0; i < WT_COUNT; i++ )
+ {
+ if( !gAgent.mWearableEntry[i].mItemID.isNull() )
+ {
+ gWearableList.getAsset(
+ asset_id_array[i],
+ LLString::null,
+ LLWearable::typeToAssetType( (EWearableType) i ),
+ LLAgent::onInitialWearableAssetArrived, (void*)(intptr_t)i );
+ }
+ }
+ }
+}
+
+// A single wearable that the avatar was wearing on start-up has arrived from the database.
+// static
+void LLAgent::onInitialWearableAssetArrived( LLWearable* wearable, void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return;
+ }
+
+ if( wearable )
+ {
+ llassert( type == wearable->getType() );
+ gAgent.mWearableEntry[ type ].mWearable = wearable;
+
+ // disable composites if initial textures are baked
+ avatar->setupComposites();
+ gAgent.queryWearableCache();
+
+ wearable->writeToAvatar( FALSE );
+ avatar->setCompositeUpdatesEnabled(TRUE);
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, gAgent.mWearableEntry[type].mItemID );
+ }
+ else
+ {
+ // Somehow the asset doesn't exist in the database.
+ gAgent.recoverMissingWearable( type );
+ }
+
+ gInventory.notifyObservers();
+
+ // Have all the wearables that the avatar was wearing at log-in arrived?
+ if( !gAgent.mWearablesLoaded )
+ {
+ gAgent.mWearablesLoaded = TRUE;
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( !gAgent.mWearableEntry[i].mItemID.isNull() && !gAgent.mWearableEntry[i].mWearable )
+ {
+ gAgent.mWearablesLoaded = FALSE;
+ break;
+ }
+ }
+ }
+
+ if( gAgent.mWearablesLoaded )
+ {
+ // Make sure that the server's idea of the avatar's wearables actually match the wearables.
+ gAgent.sendAgentSetAppearance();
+
+ // Check to see if there are any baked textures that we hadn't uploaded before we logged off last time.
+ // If there are any, schedule them to be uploaded as soon as the layer textures they depend on arrive.
+ if( !gAgent.cameraCustomizeAvatar() )
+ {
+ avatar->requestLayerSetUploads();
+ }
+ }
+}
+
+// Normally, all wearables referred to "AgentWearablesUpdate" will correspond to actual assets in the
+// database. If for some reason, we can't load one of those assets, we can try to reconstruct it so that
+// the user isn't left without a shape, for example. (We can do that only after the inventory has loaded.)
+void LLAgent::recoverMissingWearable( EWearableType type )
+{
+ // Try to recover by replacing missing wearable with a new one.
+ LLNotifyBox::showXml("ReplacedMissingWearable");
+ lldebugs << "Wearable " << LLWearable::typeToTypeLabel( type ) << " could not be downloaded. Replaced inventory item with default wearable." << llendl;
+ LLWearable* new_wearable = gWearableList.createNewWearable(type);
+
+ S32 type_s32 = (S32) type;
+ mWearableEntry[type_s32].mWearable = new_wearable;
+ new_wearable->writeToAvatar( TRUE );
+
+ // Add a new one in the lost and found folder.
+ // (We used to overwrite the "not found" one, but that could potentially
+ // destory content.) JC
+ LLUUID lost_and_found_id =
+ gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type_s32,
+ new_wearable,
+ addWearableToAgentInventoryCallback::CALL_RECOVERDONE);
+ addWearableToAgentInventory( cb, new_wearable, lost_and_found_id, TRUE);
+}
+
+void LLAgent::recoverMissingWearableDone()
+{
+ // Have all the wearables that the avatar was wearing at log-in arrived or been fabricated?
+ mWearablesLoaded = TRUE;
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( !mWearableEntry[i].mItemID.isNull() && !mWearableEntry[i].mWearable )
+ {
+ mWearablesLoaded = FALSE;
+ break;
+ }
+ }
+
+ if( mWearablesLoaded )
+ {
+ // Make sure that the server's idea of the avatar's wearables actually match the wearables.
+ sendAgentSetAppearance();
+ }
+ else
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, LLUUID::null );
+ gInventory.notifyObservers();
+ }
+}
+
+void LLAgent::createStandardWearables(BOOL female)
+{
+ llwarns << "Creating Standard " << (female ? "female" : "male" )
+ << " Wearables" << llendl;
+
+ if (mAvatarObject.isNull())
+ {
+ return;
+ }
+
+ if(female) mAvatarObject->setSex(SEX_FEMALE);
+ else mAvatarObject->setSex(SEX_MALE);
+
+ BOOL create[WT_COUNT] =
+ {
+ TRUE, //WT_SHAPE
+ TRUE, //WT_SKIN
+ TRUE, //WT_HAIR
+ TRUE, //WT_EYES
+ TRUE, //WT_SHIRT
+ TRUE, //WT_PANTS
+ TRUE, //WT_SHOES
+ TRUE, //WT_SOCKS
+ FALSE, //WT_JACKET
+ FALSE, //WT_GLOVES
+ TRUE, //WT_UNDERSHIRT
+ TRUE, //WT_UNDERPANTS
+ FALSE //WT_SKIRT
+ };
+
+ for( S32 i=0; i < WT_COUNT; i++ )
+ {
+ bool once = false;
+ LLPointer<LLRefCount> donecb = NULL;
+ if( create[i] )
+ {
+ if (!once)
+ {
+ once = true;
+ donecb = new createStandardWearablesAllDoneCallback;
+ }
+ llassert( mWearableEntry[i].mWearable == NULL );
+ LLWearable* wearable = gWearableList.createNewWearable((EWearableType)i);
+ mWearableEntry[i].mWearable = wearable;
+ // no need to update here...
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ donecb,
+ i,
+ wearable,
+ addWearableToAgentInventoryCallback::CALL_CREATESTANDARDDONE);
+ addWearableToAgentInventory(cb, wearable, LLUUID::null, FALSE);
+ }
+ }
+}
+void LLAgent::createStandardWearablesDone(S32 index)
+{
+ LLWearable* wearable = mWearableEntry[index].mWearable;
+
+ if (wearable)
+ {
+ wearable->writeToAvatar(TRUE);
+ }
+}
+
+void LLAgent::createStandardWearablesAllDone()
+{
+ // ... because sendAgentWearablesUpdate will notify inventory
+ // observers.
+ mWearablesLoaded = TRUE;
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+
+ // Treat this as the first texture entry message, if none received yet
+ mAvatarObject->onFirstTEMessageReceived();
+}
+
+void LLAgent::makeNewOutfit(
+ const std::string& new_folder_name,
+ const LLDynamicArray<S32>& wearables_to_include,
+ const LLDynamicArray<S32>& attachments_to_include,
+ BOOL rename_clothing)
+{
+ if (mAvatarObject.isNull())
+ {
+ return;
+ }
+
+ // First, make a folder in the Clothes directory.
+ LLUUID folder_id = gInventory.createNewCategory(
+ gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING),
+ LLAssetType::AT_NONE,
+ new_folder_name);
+
+ bool found_first_item = false;
+
+ ///////////////////
+ // Wearables
+
+ if( wearables_to_include.count() )
+ {
+ // Then, iterate though each of the wearables and save copies of them in the folder.
+ S32 i;
+ S32 count = wearables_to_include.count();
+ LLDynamicArray<LLUUID> delete_items;
+ LLPointer<LLRefCount> cbdone = NULL;
+ for( i = 0; i < count; ++i )
+ {
+ S32 index = wearables_to_include[i];
+ LLWearable* old_wearable = mWearableEntry[ index ].mWearable;
+ if( old_wearable )
+ {
+ std::string new_name;
+ LLWearable* new_wearable;
+ new_wearable = gWearableList.createCopy(old_wearable);
+ if (rename_clothing)
+ {
+ new_name = new_folder_name;
+ new_name.append(" ");
+ new_name.append(old_wearable->getTypeLabel());
+ LLString::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN);
+ new_wearable->setName(new_name);
+ }
+
+ LLViewerInventoryItem* item = gInventory.getItem(mWearableEntry[index].mItemID);
+ S32 todo = addWearableToAgentInventoryCallback::CALL_NONE;
+ if (!found_first_item)
+ {
+ found_first_item = true;
+ /* set the focus to the first item */
+ todo |= addWearableToAgentInventoryCallback::CALL_MAKENEWOUTFITDONE;
+ /* send the agent wearables update when done */
+ cbdone = new sendAgentWearablesUpdateCallback;
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ cbdone,
+ index,
+ new_wearable,
+ todo);
+ if (isWearableCopyable((EWearableType)index))
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ folder_id,
+ new_name,
+ cb);
+ }
+ else
+ {
+ move_inventory_item(
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getUUID(),
+ folder_id,
+ new_name,
+ cb);
+ }
+ }
+ }
+ gInventory.notifyObservers();
+ }
+
+
+ ///////////////////
+ // Attachments
+
+ if( attachments_to_include.count() )
+ {
+ BOOL msg_started = FALSE;
+ LLMessageSystem* msg = gMessageSystem;
+ S32 i;
+ for( i = 0; i < attachments_to_include.count(); i++ )
+ {
+ S32 attachment_pt = attachments_to_include[i];
+ LLViewerJointAttachment* attachment = mAvatarObject->mAttachmentPoints.getIfThere( attachment_pt );
+ if(!attachment) continue;
+ LLViewerObject* attached_object = attachment->getObject(0);
+ if(!attached_object) continue;
+ const LLUUID& item_id = attachment->getItemID();
+ if(item_id.isNull()) continue;
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if(!item) continue;
+ if(!msg_started)
+ {
+ msg_started = TRUE;
+ msg->newMessage("CreateNewOutfitAttachments");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", getID());
+ msg->addUUID("SessionID", getSessionID());
+ msg->nextBlock("HeaderData");
+ msg->addUUID("NewFolderID", folder_id);
+ }
+ msg->nextBlock("ObjectData");
+ msg->addUUID("OldItemID", item_id);
+ msg->addUUID("OldFolderID", item->getParentUUID());
+ }
+
+ if( msg_started )
+ {
+ sendReliableMessage();
+ }
+
+ }
+}
+
+void LLAgent::makeNewOutfitDone(S32 index)
+{
+ LLUUID first_item_id = mWearableEntry[index].mItemID;
+ // Open the inventory and select the first item we added.
+ if( first_item_id.notNull() )
+ {
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(first_item_id, TAKE_FOCUS_NO);
+ }
+ }
+}
+
+
+void LLAgent::addWearableToAgentInventory(
+ LLPointer<LLInventoryCallback> cb,
+ LLWearable* wearable,
+ const LLUUID& category_id,
+ BOOL notify)
+{
+ create_inventory_item(
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ category_id,
+ wearable->getTransactionID(),
+ wearable->getName(),
+ wearable->getDescription(),
+ wearable->getAssetType(),
+ LLInventoryType::IT_WEARABLE,
+ wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ cb);
+}
+
+//-----------------------------------------------------------------------------
+// sendAgentSetAppearance()
+//-----------------------------------------------------------------------------
+void LLAgent::sendAgentSetAppearance()
+{
+ if (mAvatarObject.isNull()) return;
+
+ if (mNumPendingQueries > 0 && !gAgent.cameraCustomizeAvatar())
+ {
+ return;
+ }
+
+ llinfos << "TAT: Sent AgentSetAppearance: " <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) <<
+ (( mAvatarObject->getTEImage( LLVOAvatar::TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl;
+ //dumpAvatarTEs( "sendAgentSetAppearance()" );
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentSetAppearance);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, getID());
+ msg->addUUIDFast(_PREHASH_SessionID, getSessionID());
+
+ // correct for the collisiton tolerance (to make it look like the
+ // agent is actually walking on the ground/object)
+ // NOTE -- when we start correcting all of the other Havok geometry
+ // to compensate for the COLLISION_TOLERANCE ugliness we will have
+ // to tweak this number again
+ LLVector3 body_size = mAvatarObject->mBodySize;
+ msg->addVector3Fast(_PREHASH_Size, body_size);
+
+ // To guard against out of order packets
+ // Note: always start by sending 1. This resets the server's count. 0 on the server means "uninitialized"
+ mAppearanceSerialNum++;
+ msg->addU32Fast(_PREHASH_SerialNum, mAppearanceSerialNum );
+
+ // is texture data current relative to wearables?
+ // KLW - TAT this will probably need to check the local queue.
+ BOOL textures_current = !mAvatarObject->hasPendingBakedUploads() && mWearablesLoaded;
+
+ S32 baked_texture_index;
+ for( baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++ )
+ {
+ S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index];
+
+ // if we're not wearing a skirt, we don't need the texture to be baked
+ if (tex_index == LLVOAvatar::TEX_SKIRT_BAKED && !mAvatarObject->isWearingWearableType(WT_SKIRT))
+ {
+ continue;
+ }
+
+ // IMG_DEFAULT_AVATAR means not baked
+ if (mAvatarObject->getTEImage( tex_index)->getID() == IMG_DEFAULT_AVATAR)
+ {
+ textures_current = FALSE;
+ break;
+ }
+ }
+
+ // only update cache entries if we have all our baked textures
+ if (textures_current)
+ {
+ llinfos << "TAT: Sending cached texture data" << llendl;
+ for (baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++)
+ {
+ LLUUID hash;
+
+ for( S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++ )
+ {
+ EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num];
+
+ LLWearable* wearable = getWearable( wearable_type );
+ if (wearable)
+ {
+ hash ^= wearable->getID();
+ }
+ }
+
+ if (hash.notNull())
+ {
+ hash ^= BAKED_TEXTURE_HASH[baked_texture_index];
+ }
+
+ S32 tex_index = LLVOAvatar::sBakedTextureIndices[baked_texture_index];
+
+ msg->nextBlockFast(_PREHASH_WearableData);
+ msg->addUUIDFast(_PREHASH_CacheID, hash);
+ msg->addU8Fast(_PREHASH_TextureIndex, (U8)tex_index);
+ }
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ mAvatarObject->packTEMessage( gMessageSystem );
+
+ S32 transmitted_params = 0;
+ for (LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarObject->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*)mAvatarObject->getNextVisualParam())
+ {
+ F32 param_value = param->getWeight();
+
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ msg->nextBlockFast(_PREHASH_VisualParam );
+
+ // We don't send the param ids. Instead, we assume that the receiver has the same params in the same sequence.
+ U8 new_weight = F32_to_U8(param_value, param->getMinWeight(), param->getMaxWeight());
+ msg->addU8Fast(_PREHASH_ParamValue, new_weight );
+ transmitted_params++;
+ }
+ }
+
+// llinfos << "Avatar XML num VisualParams transmitted = " << transmitted_params << llendl;
+ sendReliableMessage();
+}
+
+void LLAgent::sendAgentDataUpdateRequest()
+{
+ gMessageSystem->newMessageFast(_PREHASH_AgentDataUpdateRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ sendReliableMessage();
+}
+
+void LLAgent::removeWearable( EWearableType type )
+{
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+
+ if ( (gAgent.mAccess < SIM_ACCESS_MATURE)
+ && (type == WT_UNDERSHIRT || type == WT_UNDERPANTS))
+ {
+ // Can't take off underclothing in simple UI mode or on PG accounts
+ return;
+ }
+
+ if( old_wearable )
+ {
+ if( old_wearable->isDirty() )
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("RemoveWearableSave", LLAgent::onRemoveWearableDialog, (void*)(S32)type );
+ return;
+ }
+ else
+ {
+ removeWearableFinal( type );
+ }
+ }
+}
+
+// static
+void LLAgent::onRemoveWearableDialog( S32 option, void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+ switch( option )
+ {
+ case 0: // "Save"
+ gAgent.saveWearable( type );
+ gAgent.removeWearableFinal( type );
+ break;
+
+ case 1: // "Don't Save"
+ gAgent.removeWearableFinal( type );
+ break;
+
+ case 2: // "Cancel"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+// Called by removeWearable() and onRemoveWearableDialog() to actually do the removal.
+void LLAgent::removeWearableFinal( EWearableType type )
+{
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, mWearableEntry[type].mItemID );
+
+ mWearableEntry[ type ].mWearable = NULL;
+ mWearableEntry[ type ].mItemID.setNull();
+
+ queryWearableCache();
+
+ if( old_wearable )
+ {
+ old_wearable->removeFromAvatar( TRUE );
+ }
+
+ // Update the server
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+ gInventory.notifyObservers();
+}
+
+void LLAgent::copyWearableToInventory( EWearableType type )
+{
+ LLWearable* wearable = mWearableEntry[ type ].mWearable;
+ if( wearable )
+ {
+ // Save the old wearable if it has changed.
+ if( wearable->isDirty() )
+ {
+ wearable = gWearableList.createCopyFromAvatar( wearable );
+ mWearableEntry[ type ].mWearable = wearable;
+ }
+
+ // Make a new entry in the inventory. (Put it in the same folder as the original item if possible.)
+ LLUUID category_id;
+ LLInventoryItem* item = gInventory.getItem( mWearableEntry[ type ].mItemID );
+ if( item )
+ {
+ category_id = item->getParentUUID();
+ wearable->setPermissions(item->getPermissions());
+ }
+ LLPointer<LLInventoryCallback> cb =
+ new addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount>(NULL),
+ type,
+ wearable);
+ addWearableToAgentInventory(cb, wearable, category_id);
+ }
+}
+
+
+// A little struct to let setWearable() communicate more than one value with onSetWearableDialog().
+struct LLSetWearableData
+{
+ LLSetWearableData( const LLUUID& new_item_id, LLWearable* new_wearable ) :
+ mNewItemID( new_item_id ), mNewWearable( new_wearable ) {}
+ LLUUID mNewItemID;
+ LLWearable* mNewWearable;
+};
+
+BOOL LLAgent::needsReplacement(EWearableType wearableType, S32 remove)
+{
+ return TRUE;
+ /*if (remove) return TRUE;
+
+ return getWearable(wearableType) ? TRUE : FALSE;*/
+}
+
+// Assumes existing wearables are not dirty.
+void LLAgent::setWearableOutfit(
+ const LLInventoryItem::item_array_t& items,
+ const LLDynamicArray< LLWearable* >& wearables,
+ BOOL remove )
+{
+ lldebugs << "setWearableOutfit() start" << llendl;
+
+ BOOL wearables_to_remove[WT_COUNT];
+ wearables_to_remove[WT_SHAPE] = FALSE;
+ wearables_to_remove[WT_SKIN] = FALSE;
+ wearables_to_remove[WT_HAIR] = FALSE;
+ wearables_to_remove[WT_EYES] = FALSE;
+ wearables_to_remove[WT_SHIRT] = remove;
+ wearables_to_remove[WT_PANTS] = remove;
+ wearables_to_remove[WT_SHOES] = remove;
+ wearables_to_remove[WT_SOCKS] = remove;
+ wearables_to_remove[WT_JACKET] = remove;
+ wearables_to_remove[WT_GLOVES] = remove;
+ wearables_to_remove[WT_UNDERSHIRT] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove;
+ wearables_to_remove[WT_UNDERPANTS] = (gAgent.mAccess >= SIM_ACCESS_MATURE) & remove;
+ wearables_to_remove[WT_SKIRT] = remove;
+
+ S32 count = wearables.count();
+ llassert( items.count() == count );
+
+ S32 i;
+ for( i = 0; i < count; i++ )
+ {
+ LLWearable* new_wearable = wearables[i];
+ LLPointer<LLInventoryItem> new_item = items[i];
+
+ EWearableType type = new_wearable->getType();
+ wearables_to_remove[type] = FALSE;
+
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+ if( old_wearable )
+ {
+ const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
+ if( (old_wearable->getID() == new_wearable->getID()) &&
+ (old_item_id == new_item->getUUID()) )
+ {
+ lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
+ continue;
+ }
+
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, old_item_id);
+
+ // Assumes existing wearables are not dirty.
+ if( old_wearable->isDirty() )
+ {
+ llassert(0);
+ continue;
+ }
+ }
+
+ mWearableEntry[ type ].mItemID = new_item->getUUID();
+ mWearableEntry[ type ].mWearable = new_wearable;
+ }
+
+ std::vector<LLWearable*> wearables_being_removed;
+
+ for( i = 0; i < WT_COUNT; i++ )
+ {
+ if( wearables_to_remove[i] )
+ {
+ wearables_being_removed.push_back(mWearableEntry[ i ].mWearable);
+ mWearableEntry[ i ].mWearable = NULL;
+
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, mWearableEntry[ i ].mItemID);
+ mWearableEntry[ i ].mItemID.setNull();
+ }
+ }
+
+ gInventory.notifyObservers();
+
+ queryWearableCache();
+
+ std::vector<LLWearable*>::iterator wearable_iter;
+
+ for( wearable_iter = wearables_being_removed.begin();
+ wearable_iter != wearables_being_removed.end();
+ ++wearable_iter)
+ {
+ LLWearable* wearablep = *wearable_iter;
+ if (wearablep)
+ {
+ wearablep->removeFromAvatar( TRUE );
+ }
+ }
+
+ for( i = 0; i < count; i++ )
+ {
+ wearables[i]->writeToAvatar( TRUE );
+ }
+
+ LLFloaterCustomize::setCurrentWearableType( WT_SHAPE );
+
+ // Start rendering & update the server
+ mWearablesLoaded = TRUE;
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+
+ lldebugs << "setWearableOutfit() end" << llendl;
+}
+
+
+// User has picked "wear on avatar" from a menu.
+void LLAgent::setWearable( LLInventoryItem* new_item, LLWearable* new_wearable )
+{
+ EWearableType type = new_wearable->getType();
+
+ LLWearable* old_wearable = mWearableEntry[ type ].mWearable;
+ if( old_wearable )
+ {
+ const LLUUID& old_item_id = mWearableEntry[ type ].mItemID;
+ if( (old_wearable->getID() == new_wearable->getID()) &&
+ (old_item_id == new_item->getUUID()) )
+ {
+ lldebugs << "No change to wearable asset and item: " << LLWearable::typeToTypeName( type ) << llendl;
+ return;
+ }
+
+ if( old_wearable->isDirty() )
+ {
+ // Bring up modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml( "SetWearableSave", LLAgent::onSetWearableDialog,
+ new LLSetWearableData( new_item->getUUID(), new_wearable ));
+ return;
+ }
+ }
+
+ setWearableFinal( new_item, new_wearable );
+}
+
+// static
+void LLAgent::onSetWearableDialog( S32 option, void* userdata )
+{
+ LLSetWearableData* data = (LLSetWearableData*)userdata;
+ LLInventoryItem* new_item = gInventory.getItem( data->mNewItemID );
+ if( !new_item )
+ {
+ delete data;
+ return;
+ }
+
+ switch( option )
+ {
+ case 0: // "Save"
+ gAgent.saveWearable( data->mNewWearable->getType() );
+ gAgent.setWearableFinal( new_item, data->mNewWearable );
+ break;
+
+ case 1: // "Don't Save"
+ gAgent.setWearableFinal( new_item, data->mNewWearable );
+ break;
+
+ case 2: // "Cancel"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+
+ delete data;
+}
+
+// Called from setWearable() and onSetWearableDialog() to actually set the wearable.
+void LLAgent::setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable )
+{
+ EWearableType type = new_wearable->getType();
+
+ // Replace the old wearable with a new one.
+ llassert( new_item->getAssetUUID() == new_wearable->getID() );
+ LLUUID old_item_id = mWearableEntry[ type ].mItemID;
+ mWearableEntry[ type ].mItemID = new_item->getUUID();
+ mWearableEntry[ type ].mWearable = new_wearable;
+
+ if (old_item_id.notNull())
+ {
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, old_item_id );
+ gInventory.notifyObservers();
+ }
+
+ //llinfos << "LLVOAvatar::setWearable()" << llendl;
+ queryWearableCache();
+ new_wearable->writeToAvatar( TRUE );
+
+ // Update the server
+ sendAgentWearablesUpdate();
+ sendAgentSetAppearance();
+}
+
+void LLAgent::queryWearableCache()
+{
+ if (!mWearablesLoaded)
+ {
+ return;
+ }
+
+ // Look up affected baked textures.
+ // If they exist:
+ // disallow updates for affected layersets (until dataserver responds with cache request.)
+ // If cache miss…turn updates back on and invalidate composite.
+ // If cache hit, modify baked texture entries.
+ //
+ // Cache requests contain list of hashes for each baked texture entry.
+ // Response is list of valid baked texture assets. (same message)
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentCachedTexture);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, getSessionID());
+ gMessageSystem->addS32Fast(_PREHASH_SerialNum, mTextureCacheQueryID);
+
+ S32 num_queries = 0;
+ for (S32 baked_texture_index = 0; baked_texture_index < BAKED_TEXTURE_COUNT; baked_texture_index++)
+ {
+ LLUUID hash;
+ for (S32 wearable_num = 0; wearable_num < MAX_WEARABLES_PER_LAYERSET; wearable_num++)
+ {
+ EWearableType wearable_type = WEARABLE_BAKE_TEXTURE_MAP[baked_texture_index][wearable_num];
+
+ LLWearable* wearable = getWearable( wearable_type );
+ if (wearable)
+ {
+ hash ^= wearable->getID();
+ }
+ }
+ if (hash.notNull())
+ {
+ hash ^= BAKED_TEXTURE_HASH[baked_texture_index];
+ num_queries++;
+ //FIXME: make sure at least one request gets packed
+
+ //llinfos << "Requesting texture for hash " << hash << " in baked texture slot " << baked_texture_index << llendl;
+ gMessageSystem->nextBlockFast(_PREHASH_WearableData);
+ gMessageSystem->addUUIDFast(_PREHASH_ID, hash);
+ gMessageSystem->addU8Fast(_PREHASH_TextureIndex, (U8)baked_texture_index);
+ }
+
+ mActiveCacheQueries[ baked_texture_index ] = mTextureCacheQueryID;
+ }
+
+ llinfos << "Requesting texture cache entry for " << num_queries << " baked textures" << llendl;
+ gMessageSystem->sendReliable(getRegion()->getHost());
+ mNumPendingQueries++;
+ mTextureCacheQueryID++;
+}
+
+// User has picked "remove from avatar" from a menu.
+// static
+void LLAgent::userRemoveWearable( void* userdata )
+{
+ EWearableType type = (EWearableType)(intptr_t)userdata;
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&&
+ //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ gAgent.removeWearable( type );
+ }
+}
+
+void LLAgent::userRemoveAllClothes( void* userdata )
+{
+ // We have to do this up front to avoid having to deal with the case of multiple wearables being dirty.
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty( LLAgent::userRemoveAllClothesStep2, NULL );
+ }
+ else
+ {
+ LLAgent::userRemoveAllClothesStep2( TRUE, NULL );
+ }
+}
+
+void LLAgent::userRemoveAllClothesStep2( BOOL proceed, void* userdata )
+{
+ if( proceed )
+ {
+ gAgent.removeWearable( WT_SHIRT );
+ gAgent.removeWearable( WT_PANTS );
+ gAgent.removeWearable( WT_SHOES );
+ gAgent.removeWearable( WT_SOCKS );
+ gAgent.removeWearable( WT_JACKET );
+ gAgent.removeWearable( WT_GLOVES );
+ gAgent.removeWearable( WT_UNDERSHIRT );
+ gAgent.removeWearable( WT_UNDERPANTS );
+ gAgent.removeWearable( WT_SKIRT );
+ }
+}
+
+void LLAgent::userRemoveAllAttachments( void* userdata )
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if(!avatarp)
+ {
+ llwarns << "No avatar found." << llendl;
+ return;
+ }
+
+ gMessageSystem->newMessage("ObjectDetach");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ LLViewerJointAttachment* attachment;
+ for (attachment = avatarp->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = avatarp->mAttachmentPoints.getNextData())
+ {
+ LLViewerObject* objectp = attachment->getObject(0);
+ if (objectp)
+ {
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, objectp->getLocalID());
+ }
+ }
+ gMessageSystem->sendReliable( gAgent.getRegionHost() );
+}
+
+bool LLAgent::LLHideGroupTitleListener::handleEvent(LLPointer<LLEvent> event, const LLSD &userdata)
+{
+ gAgent.setHideGroupTitle(event->getValue());
+ return true;
+
+}
+
+bool LLAgent::LLEffectColorListener::handleEvent(LLPointer<LLEvent> event, const LLSD &userdata)
+{
+ gAgent.setEffectColor(LLColor4(event->getValue()));
+ return true;
+}
+
+void LLAgent::observeFriends()
+{
+ if(!mFriendObserver)
+ {
+ mFriendObserver = new LLAgentFriendObserver;
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+ friendsChanged();
+ }
+}
+
+// EOF
diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h
new file mode 100644
index 0000000000..5fe6cd3ab4
--- /dev/null
+++ b/indra/newview/llagent.h
@@ -0,0 +1,896 @@
+/**
+ * @file llagent.h
+ * @brief LLAgent class header file
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENT_H
+#define LL_LLAGENT_H
+
+#include <set>
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcoordframe.h"
+#include "llevent.h"
+#include "llagentconstants.h"
+#include "llanimationstates.h"
+#include "lldbstrings.h"
+#include "llhudeffectlookat.h"
+#include "llhudeffectpointat.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "lltimer.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4color.h"
+#include "v4math.h"
+//#include "vmath.h"
+#include "stdenums.h"
+#include "llwearable.h"
+#include "llcharacter.h"
+#include "llinventory.h"
+#include "llviewerinventory.h"
+#include "llagentdata.h"
+
+// Ventrella
+#include "llfollowcam.h"
+// end Ventrella
+
+const U8 AGENT_STATE_TYPING = 0x04; // Typing indication
+const U8 AGENT_STATE_EDITING = 0x10; // Set when agent has objects selected
+
+const BOOL ANIMATE = TRUE;
+
+typedef enum e_camera_modes
+{
+ CAMERA_MODE_THIRD_PERSON,
+ CAMERA_MODE_MOUSELOOK,
+ CAMERA_MODE_CUSTOMIZE_AVATAR,
+ CAMERA_MODE_FOLLOW
+} ECameraMode;
+
+typedef enum e_anim_request
+{
+ ANIM_REQUEST_START,
+ ANIM_REQUEST_STOP
+} EAnimRequest;
+
+class LLChat;
+class LLVOAvatar;
+class LLViewerRegion;
+class LLMotion;
+class LLToolset;
+class LLMessageSystem;
+class LLPermissions;
+class LLHost;
+class LLFriendObserver;
+
+struct LLGroupData
+{
+ LLUUID mID;
+ LLUUID mInsigniaID;
+ U64 mPowers;
+ BOOL mAcceptNotices;
+ S32 mContribution;
+ std::string mName;
+};
+
+inline bool operator==(const LLGroupData &a, const LLGroupData &b)
+{
+ return (a.mID == b.mID);
+}
+
+// forward declarations
+
+//
+
+class LLAgent : public LLObservable
+{
+public:
+ // When the agent hasn't typed anything for this duration, it leaves the
+ // typing state (for both chat and IM).
+ static const F32 TYPING_TIMEOUT_SECS;
+
+ LLAgent();
+ ~LLAgent();
+
+ void init();
+ void cleanup();
+
+ //
+ // MANIPULATORS
+ //
+ // TODO: Put all non-const functions here.
+
+ // Called whenever the agent moves. Puts camera back in default position,
+ // deselects items, etc.
+ void resetView(BOOL reset_camera = TRUE);
+
+ // Called on camera movement, to allow the camera to be unlocked from the
+ // default position behind the avatar.
+ void unlockView();
+
+ void sendMessage(); // Send message to this agent's region.
+ void sendReliableMessage();
+
+ LLVector3d calcCameraPositionTargetGlobal(BOOL *hit_limit = NULL); // Calculate the camera position target
+ LLVector3d calcFocusPositionTargetGlobal(); // target for this mode
+ LLVector3d getCameraPositionGlobal() const;
+ const LLVector3 &getCameraPositionAgent() const;
+ F32 calcCameraFOVZoomFactor();
+ F32 getCameraMinOffGround(); // minimum height off ground for this mode, meters
+ void endAnimationUpdateUI();
+ void setKey(const S32 direction, S32 &key); // sets key to +1 for +direction, -1 for -direction
+ void handleScrollWheel(S32 clicks); // mousewheel driven zoom
+
+ void setAvatarObject(LLVOAvatar *avatar);
+
+ // rendering state bitmask helpers
+ void startTyping();
+ void stopTyping();
+ void setRenderState(U8 newstate);
+ void clearRenderState(U8 clearstate);
+ U8 getRenderState();
+
+ // Set the home data
+ void setRegion(LLViewerRegion *regionp);
+ LLViewerRegion *getRegion() const;
+ const LLHost& getRegionHost() const;
+
+ void updateAgentPosition(const F32 dt, const F32 yaw, const S32 mouse_x, const S32 mouse_y); // call once per frame to update position, angles radians
+ void updateLookAt(const S32 mouse_x, const S32 mouse_y);
+
+
+ void updateCamera(); // call once per frame to update camera location/orientation
+ void resetCamera(); // slam camera into its default position
+ void setupSitCamera();
+ void setCameraCollidePlane(LLVector4 &plane) { mCameraCollidePlane = plane; }
+
+ void changeCameraToDefault();
+ void changeCameraToMouselook(BOOL animate = TRUE);
+ void changeCameraToThirdPerson(BOOL animate = TRUE);
+ void changeCameraToCustomizeAvatar(BOOL animate = TRUE); // trigger transition animation
+ // Ventrella
+ void changeCameraToFollow(BOOL animate = TRUE);
+ //end Ventrella
+
+ void setFocusGlobal(const LLVector3d &focus, const LLUUID &object_id = LLUUID::null);
+ void setFocusOnAvatar(BOOL focus, BOOL animate);
+ void setCameraPosAndFocusGlobal(const LLVector3d& pos, const LLVector3d& focus, const LLUUID &object_id);
+ void setSitCamera(const LLUUID &object_id, const LLVector3 &camera_pos = LLVector3::zero, const LLVector3 &camera_focus = LLVector3::zero);
+ void clearFocusObject();
+ void setFocusObject(LLViewerObject* object);
+ void setObjectTracking(BOOL track) { mTrackFocusObject = track; }
+// void setLookingAtAvatar(BOOL looking);
+
+ void heardChat(const LLChat& chat);
+ void lookAtLastChat();
+ LLUUID getLastChatter() { return mLastChatterID; }
+ F32 getTypingTime() { return mTypingTimer.getElapsedTimeF32(); }
+
+ void setAFK();
+ void clearAFK();
+ BOOL getAFK() const;
+
+ void setAlwaysRun() { mbAlwaysRun = TRUE; }
+ void clearAlwaysRun() { mbAlwaysRun = FALSE; }
+ BOOL getAlwaysRun() const { return mbAlwaysRun; }
+
+ void setBusy();
+ void clearBusy();
+ BOOL getBusy() const;
+
+ void setAdminOverride(BOOL b) { mAdminOverride = b; }
+ void setGodLevel(U8 god_level) { mGodLevel = god_level; }
+ void setFirstLogin(BOOL b) { mFirstLogin = b; }
+ void setGenderChosen(BOOL b) { mGenderChosen = b; }
+
+ BOOL getAdminOverride() const { return mAdminOverride; }
+ // update internal datastructures and update the server with the
+ // new contribution level. Returns true if the group id was found
+ // and contribution could be set.
+ BOOL setGroupContribution(const LLUUID& group_id, S32 contribution);
+ BOOL setGroupAcceptNotices(const LLUUID& group_id, BOOL accept_notices);
+ void setHideGroupTitle(BOOL hide) { mHideGroupTitle = hide; }
+
+ //
+ // ACCESSORS
+ //
+ // TODO: Put all read functions here, make them const
+
+ const LLUUID& getID() const { return gAgentID; }
+ const LLUUID& getSessionID() const { return gAgentSessionID; }
+
+ const LLUUID& getSecureSessionID() const { return mSecureSessionID; }
+ // Note: NEVER send this value in the clear or over any weakly
+ // encrypted channel (such as simple XOR masking). If you are unsure
+ // ask Aaron or MarkL.
+
+ BOOL isGodlike() const;
+ U8 getGodLevel() const;
+ BOOL isGroupTitleHidden() const { return mHideGroupTitle; }
+ BOOL isGroupMember() const { return !mGroupID.isNull(); } // This is only used for building titles!
+ const LLUUID &getGroupID() const { return mGroupID; }
+ ECameraMode getCameraMode() const { return mCameraMode; }
+ BOOL getFocusOnAvatar() const { return mFocusOnAvatar; }
+ LLViewerObject* getFocusObject() const { return mFocusObject; }
+ F32 getFocusObjectDist() const { return mFocusObjectDist; }
+ BOOL inPrelude();
+ BOOL canManageEstate() const;
+
+ const LLUUID& getInventoryRootID() const { return mInventoryRootID; }
+
+ void buildFullname(std::string &name) const;
+ void buildFullnameAndTitle(std::string &name) const;
+
+ // Check against all groups in the entire agent group list.
+ BOOL isInGroup(const LLUUID& group_id) const;
+ BOOL hasPowerInGroup(const LLUUID& group_id, U64 power) const;
+ // Check for power in just the active group.
+ BOOL hasPowerInActiveGroup(const U64 power) const;
+ U64 getPowerInGroup(const LLUUID& group_id) const;
+
+ // Get group information by group_id. if not in group, data is
+ // left unchanged and method returns FALSE. otherwise, values are
+ // copied and returns TRUE.
+ BOOL getGroupData(const LLUUID& group_id, LLGroupData& data) const;
+ // Get just the agent's contribution to the given group.
+ S32 getGroupContribution(const LLUUID& group_id) const;
+
+ // return TRUE if the database reported this login as the first
+ // for this particular user.
+ BOOL isFirstLogin() const { return mFirstLogin; }
+
+ // On the very first login, gender isn't chosen until the user clicks
+ // in a dialog. We don't render the avatar until they choose.
+ BOOL isGenderChosen() const { return mGenderChosen; }
+
+ // utility to build a location string
+ void buildLocationString(std::string& str);
+
+ LLQuaternion getHeadRotation();
+ LLVOAvatar *getAvatarObject() const { return mAvatarObject; }
+
+ BOOL needsRenderAvatar(); // TRUE when camera mode is such that your own avatar should draw
+ // Not const because timers can't be accessed in const-fashion.
+ BOOL needsRenderHead();
+ BOOL cameraThirdPerson() const { return (mCameraMode == CAMERA_MODE_THIRD_PERSON && mLastCameraMode == CAMERA_MODE_THIRD_PERSON); }
+ BOOL cameraMouselook() const { return (mCameraMode == CAMERA_MODE_MOUSELOOK && mLastCameraMode == CAMERA_MODE_MOUSELOOK); }
+ BOOL cameraCustomizeAvatar() const { return (mCameraMode == CAMERA_MODE_CUSTOMIZE_AVATAR /*&& !mCameraAnimating*/); }
+ BOOL cameraFollow() const { return (mCameraMode == CAMERA_MODE_FOLLOW && mLastCameraMode == CAMERA_MODE_FOLLOW); }
+
+ LLVector3 getPosAgentFromGlobal(const LLVector3d &pos_global) const;
+ LLVector3d getPosGlobalFromAgent(const LLVector3 &pos_agent) const;
+
+ // Get the data members
+ const LLVector3& getAtAxis() const { return mFrameAgent.getAtAxis(); } // direction avatar is looking, not camera
+ const LLVector3& getUpAxis() const { return mFrameAgent.getUpAxis(); } // direction avatar is looking, not camera
+ const LLVector3& getLeftAxis() const { return mFrameAgent.getLeftAxis(); } // direction avatar is looking, not camera
+
+ LLCoordFrame getFrameAgent() const { return mFrameAgent; }
+ LLVector3 getVelocity() const;
+ F32 getVelocityZ() const { return getVelocity().mV[VZ]; } // a hack
+
+ const LLVector3d &getPositionGlobal();
+ const LLVector3 &getPositionAgent();
+ const S32 getRegionsVisited() const;
+ const F64 getDistanceTraveled() const;
+
+ const LLVector3d &getFocusGlobal() const { return mFocusGlobal; }
+ const LLVector3d &getFocusTargetGlobal() const { return mFocusTargetGlobal; }
+
+ BOOL getJump() const { return mbJump; }
+ BOOL getAutoPilot() const { return mAutoPilot; }
+ LLVector3d getAutoPilotTargetGlobal() const { return mAutoPilotTargetGlobal; }
+
+ LLQuaternion getQuat() const; // returns the quat that represents the rotation
+ // of the agent in the absolute frame
+// BOOL getLookingAtAvatar() const;
+
+ void getName(LLString& name);
+
+ const LLColor4 &getEffectColor();
+ void setEffectColor(const LLColor4 &color);
+ //
+ // UTILITIES
+ //
+
+ // Set the physics data
+ void slamLookAt(const LLVector3 &look_at);
+
+ void setPositionAgent(const LLVector3 &center);
+
+ void resetAxes();
+ void resetAxes(const LLVector3 &look_at); // makes reasonable left and up
+
+ // Move the avatar's frame
+ void rotate(F32 angle, const LLVector3 &axis);
+ void rotate(F32 angle, F32 x, F32 y, F32 z);
+ void rotate(const LLMatrix3 &matrix);
+ void rotate(const LLQuaternion &quaternion);
+ void pitch(F32 angle);
+ void roll(F32 angle);
+ void yaw(F32 angle);
+ LLVector3 getReferenceUpVector();
+
+ void setThirdPersonHeadOffset(LLVector3 offset) { mThirdPersonHeadOffset = offset; }
+ // Flight management
+ BOOL getFlying() const { return mControlFlags & AGENT_CONTROL_FLY; }
+ void setFlying(BOOL fly);
+ void toggleFlying();
+
+ // Does this parcel allow you to fly?
+ BOOL canFly();
+
+ // Animation functions
+ void requestStopMotion( LLMotion* motion );
+
+ void sendAnimationRequests(LLDynamicArray<LLUUID> &anim_ids, EAnimRequest request);
+ void sendAnimationRequest(const LLUUID &anim_id, EAnimRequest request);
+
+ LLVector3d calcFocusOffset(LLViewerObject *object, S32 x, S32 y);
+ BOOL calcCameraMinDistance(F32 &obj_min_distance);
+
+ void startCameraAnimation();
+ void stopCameraAnimation();
+
+ void cameraZoomIn(const F32 factor); // zoom in by fraction of current distance
+ void cameraOrbitAround(const F32 radians); // rotate camera CCW radians about build focus point
+ void cameraOrbitOver(const F32 radians); // rotate camera forward radians over build focus point
+ void cameraOrbitIn(const F32 meters); // move camera in toward build focus point
+
+ F32 getCameraZoomFraction(); // get camera zoom as fraction of minimum and maximum zoom
+ void setCameraZoomFraction(F32 fraction); // set camera zoom as fraction of minimum and maximum zoom
+
+ void cameraPanIn(const F32 meters);
+ void cameraPanLeft(const F32 meters);
+ void cameraPanUp(const F32 meters);
+
+ void updateFocusOffset();
+ void validateFocusObject();
+
+ void setUsingFollowCam( bool using_follow_cam);
+
+ F32 calcCustomizeAvatarUIOffset( const LLVector3d& camera_pos_global );
+
+ // marks current location as start, sends information to servers
+ void setStartPosition(U32 location_id);
+
+ // Movement from user input. All set the appropriate animation flags.
+ // All turn off autopilot and make sure the camera is behind the avatar.
+ // direction is either positive, zero, or negative
+ void moveAt(S32 direction);
+ void moveAtNudge(S32 direction);
+ void moveLeft(S32 direction);
+ void moveLeftNudge(S32 direction);
+ void moveUp(S32 direction);
+ void moveYaw(F32 mag);
+ void movePitch(S32 direction);
+
+ void setOrbitLeftKey(F32 mag) { mOrbitLeftKey = mag; }
+ void setOrbitRightKey(F32 mag) { mOrbitRightKey = mag; }
+ void setOrbitUpKey(F32 mag) { mOrbitUpKey = mag; }
+ void setOrbitDownKey(F32 mag) { mOrbitDownKey = mag; }
+ void setOrbitInKey(F32 mag) { mOrbitInKey = mag; }
+ void setOrbitOutKey(F32 mag) { mOrbitOutKey = mag; }
+
+ void setPanLeftKey(F32 mag) { mPanLeftKey = mag; }
+ void setPanRightKey(F32 mag) { mPanRightKey = mag; }
+ void setPanUpKey(F32 mag) { mPanUpKey = mag; }
+ void setPanDownKey(F32 mag) { mPanDownKey = mag; }
+ void setPanInKey(F32 mag) { mPanInKey = mag; }
+ void setPanOutKey(F32 mag) { mPanOutKey = mag; }
+
+ U32 getControlFlags();
+ void setControlFlags(U32 mask); // performs bitwise mControlFlags |= mask
+ void clearControlFlags(U32 mask); // performs bitwise mControlFlags &= mask
+ BOOL controlFlagsDirty() const;
+ void enableControlFlagReset();
+ void resetControlFlags();
+
+ void propagate(const F32 dt); // BUG: should roll into updateAgentPosition
+
+ void updateWanderTarget(); // drones will pick points in the world to autopilot towards
+
+ void startAutoPilotGlobal(const LLVector3d &pos_global, const std::string& behavior_name = "", const LLQuaternion *target_rotation = NULL,
+ void (*finish_callback)(BOOL, void *) = NULL, void *callback_data = NULL, F32 stop_distance = 0.f, F32 rotation_threshold = 0.03f);
+
+ void startFollowPilot(const LLUUID &leader_id);
+ void stopAutoPilot(BOOL user_cancel = FALSE);
+ void setAutoPilotGlobal(const LLVector3d &pos_global);
+ void autoPilot(F32 *delta_yaw); // autopilot walking action, angles in radians
+ void renderAutoPilotTarget();
+
+ //
+ // teportation methods
+ //
+
+ // go to a named location home
+ void teleportRequest(
+ const U64& region_handle,
+ const LLVector3& pos_local);
+
+ // teleport to a landmark
+ void teleportViaLandmark(const LLUUID& landmark_id);
+
+ // go home
+ void teleportHome() { teleportViaLandmark(LLUUID::null); }
+
+ // to an invited location
+ void teleportViaLure(const LLUUID& lure_id, BOOL godlike);
+
+ // to a global location - this will probably need to be
+ // deprecated.
+ void teleportViaLocation(const LLVector3d& pos_global);
+
+ // cancel the teleport, may or may not be allowed by server
+ void teleportCancel();
+
+ void setTargetVelocity(const LLVector3 &vel);
+ const LLVector3 &getTargetVelocity() const;
+
+
+ // Setting the ability for this avatar to proxy for another avatar.
+ //static void processAddModifyAbility(LLMessageSystem* msg, void**);
+ //static void processGrantedProxies(LLMessageSystem* msg, void**);
+ //static void processRemoveModifyAbility(LLMessageSystem* msg, void**);
+ //BOOL isProxyFor(const LLUUID& agent_id);// *FIX should be const
+
+ static void processAgentDataUpdate(LLMessageSystem *msg, void **);
+ static void processAgentGroupDataUpdate(LLMessageSystem *msg, void **);
+ static void processAgentDropGroup(LLMessageSystem *msg, void **);
+ static void processScriptControlChange(LLMessageSystem *msg, void **);
+ static void processAgentCachedTextureResponse(LLMessageSystem *mesgsys, void **user_data);
+ //static void processControlTake(LLMessageSystem *msg, void **);
+ //static void processControlRelease(LLMessageSystem *msg, void **);
+
+ // This method checks to see if this agent can modify an object
+ // based on the permissions and the agent's proxy status.
+ BOOL isGrantedProxy(const LLPermissions& perm);
+
+ BOOL allowOperation(PermissionBit op,
+ const LLPermissions& perm,
+ U64 group_proxy_power = 0,
+ U8 god_minimum = GOD_MAINTENANCE);
+
+ friend std::ostream& operator<<(std::ostream &s, const LLAgent &sphere);
+
+ void initOriginGlobal(const LLVector3d &origin_global); // Only to be used in ONE place! - djs 08/07/02
+
+ BOOL leftButtonGrabbed() const { return ( (!cameraMouselook() && mControlsTakenCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
+ ||(cameraMouselook() && mControlsTakenCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)
+ ||(!cameraMouselook() && mControlsTakenPassedOnCount[CONTROL_LBUTTON_DOWN_INDEX] > 0)
+ ||(cameraMouselook() && mControlsTakenPassedOnCount[CONTROL_ML_LBUTTON_DOWN_INDEX] > 0)); }
+ BOOL rotateGrabbed() const { return ( (mControlsTakenCount[CONTROL_YAW_POS_INDEX] > 0)
+ ||(mControlsTakenCount[CONTROL_YAW_NEG_INDEX] > 0)); }
+ BOOL forwardGrabbed() const { return ( (mControlsTakenCount[CONTROL_AT_POS_INDEX] > 0)); }
+ BOOL backwardGrabbed() const { return ( (mControlsTakenCount[CONTROL_AT_NEG_INDEX] > 0)); }
+ BOOL upGrabbed() const { return ( (mControlsTakenCount[CONTROL_UP_POS_INDEX] > 0)); }
+ BOOL downGrabbed() const { return ( (mControlsTakenCount[CONTROL_UP_NEG_INDEX] > 0)); }
+
+ // True iff a script has taken over a control.
+ BOOL anyControlGrabbed() const;
+
+ BOOL isControlGrabbed(S32 control_index) const;
+
+ // Send message to simulator to force grabbed controls to be
+ // released, in case of a poorly written script.
+ void forceReleaseControls();
+
+ BOOL sitCameraEnabled() { return mSitCameraEnabled; }
+
+ F32 getCurrentCameraBuildOffset() { return (F32)mCameraFocusOffset.magVec(); }
+
+ // look at behavior
+ BOOL setLookAt(ELookAtType target_type, LLViewerObject *object = NULL, LLVector3 position = LLVector3::zero);
+ ELookAtType getLookAtType();
+
+ // point at behavior
+ BOOL setPointAt(EPointAtType target_type, LLViewerObject *object = NULL, LLVector3 position = LLVector3::zero);
+ EPointAtType getPointAtType();
+
+ void setHomePosRegion( const U64& region_handle, const LLVector3& pos_region );
+ BOOL getHomePosGlobal( LLVector3d* pos_global );
+ void setCameraAnimating( BOOL b ) { mCameraAnimating = b; }
+ void setAnimationDuration( F32 seconds ) { mAnimationDuration = seconds; }
+
+ F32 getNearChatRadius() { return mNearChatRadius; }
+
+ enum ETeleportState
+ {
+ TELEPORT_NONE = 0, // No teleport in progress
+ TELEPORT_START = 1, // Transition to REQUESTED. Viewer has sent a TeleportRequest to the source simulator
+ TELEPORT_REQUESTED = 2, // Waiting for source simulator to respond
+ TELEPORT_MOVING = 3, // Viewer has received destination location from source simulator
+ TELEPORT_START_ARRIVAL = 4, // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator
+ TELEPORT_ARRIVING = 5, // Make the user wait while content "pre-caches"
+ TELEPORT_CANCELLING = 6 // used only if user clicks "cancel" button
+ };
+
+ ETeleportState getTeleportState() const { return mTeleportState; }
+ void setTeleportState( ETeleportState state );
+ const LLString& getTeleportMessage() const { return mTeleportMessage; }
+ void setTeleportMessage(const LLString& message)
+ {
+ mTeleportMessage = message;
+ }
+
+ // trigger random fidget animations
+ void fidget();
+
+ void requestEnterGodMode();
+ void requestLeaveGodMode();
+
+ void sendAgentSetAppearance();
+
+ void sendAgentDataUpdateRequest();
+
+ // Ventrella
+ LLFollowCam mFollowCam;
+ // end Ventrella
+
+ //--------------------------------------------------------------------
+ // Wearables
+ //--------------------------------------------------------------------
+ BOOL getWearablesLoaded() const { return mWearablesLoaded; }
+
+ void setWearable( LLInventoryItem* new_item, LLWearable* wearable );
+ static void onSetWearableDialog( S32 option, void* userdata );
+ void setWearableFinal( LLInventoryItem* new_item, LLWearable* new_wearable );
+ void setWearableOutfit( const LLInventoryItem::item_array_t& items, const LLDynamicArray< LLWearable* >& wearables, BOOL remove );
+ void queryWearableCache();
+
+ BOOL isWearableModifiable(EWearableType type);
+ BOOL isWearableCopyable(EWearableType type);
+ BOOL needsReplacement(EWearableType wearableType, S32 remove);
+ U32 getWearablePermMask(EWearableType type);
+
+ LLInventoryItem* getWearableInventoryItem(EWearableType type);
+
+ LLWearable* getWearable( EWearableType type ) { return (type < WT_COUNT) ? mWearableEntry[ type ].mWearable : NULL; }
+ BOOL isWearingItem( const LLUUID& item_id );
+ LLWearable* getWearableFromWearableItem( const LLUUID& item_id );
+ const LLUUID& getWearableItem( EWearableType type ) { return (type < WT_COUNT) ? mWearableEntry[ type ].mItemID : LLUUID::null; }
+
+ static EWearableType getTEWearableType( S32 te );
+ static LLUUID getDefaultTEImageID( S32 te );
+
+ void copyWearableToInventory( EWearableType type );
+
+ void makeNewOutfit(
+ const std::string& new_folder_name,
+ const LLDynamicArray<S32>& wearables_to_include,
+ const LLDynamicArray<S32>& attachments_to_include,
+ BOOL rename_clothing);
+ void makeNewOutfitDone(S32 index);
+
+ void removeWearable( EWearableType type );
+ static void onRemoveWearableDialog( S32 option, void* userdata );
+ void removeWearableFinal( EWearableType type );
+
+ void sendAgentWearablesUpdate();
+
+ /**
+ * @brief Only public because of addWearableToAgentInventoryCallback.
+ *
+ * NOTE: Do not call this method unless you are the inventory callback.
+ * NOTE: This can suffer from race conditions when working on the
+ * same values for index.
+ * @param index The index in mWearableEntry.
+ * @param item_id The inventory item id of the new wearable to wear.
+ * @param wearable The actual wearable data.
+ */
+ void addWearabletoAgentInventoryDone(
+ S32 index,
+ const LLUUID& item_id,
+ LLWearable* wearable);
+
+ void saveWearableAs( EWearableType type, const std::string& new_name, BOOL save_in_lost_and_found );
+ void saveWearable( EWearableType type, BOOL send_update = TRUE );
+ void saveAllWearables();
+
+ void revertWearable( EWearableType type );
+ void revertAllWearables();
+
+ void setWearableName( const LLUUID& item_id, const std::string& new_name );
+ void createStandardWearables(BOOL female);
+ void createStandardWearablesDone(S32 index);
+ void createStandardWearablesAllDone();
+
+ BOOL areWearablesLoaded() { return mWearablesLoaded; }
+
+ void observeFriends();
+ void friendsChanged();
+
+ // statics
+ static void stopFidget();
+ static void processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data);
+ static void userRemoveWearable( void* userdata ); // userdata is EWearableType
+ static void userRemoveAllClothes( void* userdata ); // userdata is NULL
+ static void userRemoveAllClothesStep2(BOOL proceed, void* userdata ); // userdata is NULL
+ static void userRemoveAllAttachments( void* userdata); // userdata is NULL
+ static BOOL selfHasWearable( void* userdata ); // userdata is EWearableType
+
+ //debug methods
+ static void clearVisualParams(void *);
+
+protected:
+ // stuff to do for any sort of teleport. Returns true if the
+ // teleport can proceed.
+ bool teleportCore(bool is_local = false);
+
+ // helper function to prematurely age chat when agent is moving
+ void ageChat();
+
+ // internal wearable functions
+ void sendAgentWearablesRequest();
+ static void onInitialWearableAssetArrived(LLWearable* wearable, void* userdata);
+ void recoverMissingWearable(EWearableType type);
+ void recoverMissingWearableDone();
+ void addWearableToAgentInventory(LLPointer<LLInventoryCallback> cb,
+ LLWearable* wearable, const LLUUID& category_id = LLUUID::null,
+ BOOL notify = TRUE);
+public:
+ // TODO: Make these private!
+ U32 mViewerPort; // Port this agent transmits on.
+ LLUUID mSecureSessionID; // secure token for this login session
+
+ F32 mDrawDistance;
+
+ // Access or "maturity" level
+ U8 mAccess; // SIM_ACCESS_MATURE or SIM_ACCESS_PG
+ U64 mGroupPowers;
+ BOOL mHideGroupTitle;
+ char mGroupTitle[DB_GROUP_TITLE_BUF_SIZE]; // honorific, like "Sir"
+ char mGroupName[DB_GROUP_NAME_BUF_SIZE];
+ LLUUID mGroupID;
+ //LLUUID mGroupInsigniaID;
+ LLUUID mInventoryRootID;
+ LLUUID mMapID;
+ F64 mMapOriginX; // Global x coord of mMapID's bottom left corner.
+ F64 mMapOriginY; // Global y coord of mMapID's bottom left corner.
+ S32 mMapWidth; // Width of map in meters
+ S32 mMapHeight; // Height of map in meters
+ std::string mMOTD; // message of the day
+
+ LLPointer<LLHUDEffectLookAt> mLookAt;
+ LLPointer<LLHUDEffectPointAt> mPointAt;
+
+ LLDynamicArray<LLGroupData> mGroups;
+
+ BOOL mInitialized;
+
+ static BOOL sDebugDisplayTarget;
+ S32 mNumPendingQueries;
+ S32* mActiveCacheQueries;
+
+ BOOL mForceMouselook;
+
+private:
+ ETeleportState mTeleportState;
+ LLString mTeleportMessage;
+
+ S32 mControlsTakenCount[TOTAL_CONTROLS];
+ S32 mControlsTakenPassedOnCount[TOTAL_CONTROLS];
+
+ LLViewerRegion *mRegionp;
+ LLVector3d mAgentOriginGlobal; // Origin of agent coords from global coords
+ mutable LLVector3d mPositionGlobal;
+
+ std::set<U64> mRegionsVisited; // stat - what distinct regions has the avatar been to?
+ F64 mDistanceTraveled; // stat - how far has the avatar moved?
+ LLVector3d mLastPositionGlobal; // Used to calculate travel distance
+
+ LLPointer<LLVOAvatar> mAvatarObject; // NULL until avatar object sent down from simulator
+
+ U8 mRenderState; // Current behavior state of agent
+ LLFrameTimer mTypingTimer;
+
+ ECameraMode mCameraMode; // target mode after transition animation is done
+ ECameraMode mLastCameraMode;
+ BOOL mViewsPushed; // keep track of whether or not we have pushed views.
+
+ BOOL mbAlwaysRun; // should the avatar run rather than walk
+ BOOL mShowAvatar; // should we render the avatar?
+ BOOL mCameraAnimating; // camera is transitioning from one mode to another
+ LLVector3d mAnimationCameraStartGlobal; // camera start position, global coords
+ LLVector3d mAnimationFocusStartGlobal; // camera focus point, global coords
+ LLFrameTimer mAnimationTimer; // seconds that transition animation has been active
+ F32 mAnimationDuration; // seconds
+ F32 mCameraFOVZoomFactor; // amount of fov zoom applied to camera when zeroing in on an object
+ F32 mCameraCurrentFOVZoomFactor; // interpolated fov zoom
+ F32 mCameraFOVDefault; // default field of view that is basis for FOV zoom effect
+ LLVector3d mCameraFocusOffset; // offset from focus point in build mode
+ LLVector3d mCameraFocusOffsetTarget; // target towards which we are lerping the camera's focus offset
+ LLVector3 mCameraOffsetDefault; // default third-person camera offset
+ LLVector4 mCameraCollidePlane; // colliding plane for camera
+ F32 mCurrentCameraDistance; // current camera offset from avatar
+ F32 mTargetCameraDistance; // target camera offset from avatar
+ F32 mCameraZoomFraction; // mousewheel driven fraction of zoom
+ LLVector3 mCameraLag; // third person camera lag
+ LLVector3 mThirdPersonHeadOffset; // head offset for third person camera position
+ LLVector3 mCameraPositionAgent; // camera position in agent coordinates
+ LLVector3 mCameraVirtualPositionAgent; // camera virtual position (target) before performing FOV zoom
+ BOOL mSitCameraEnabled; // use provided camera information when sitting?
+ LLVector3 mSitCameraPos; // root relative camera pos when sitting
+ LLVector3 mSitCameraFocus; // root relative camera target when sitting
+
+ //Ventrella
+ LLVector3 mCameraUpVector; // camera's up direction in world coordinates (determines the 'roll' of the view)
+ //End Ventrella
+
+ LLPointer<LLViewerObject> mSitCameraReferenceObject; // object to which camera is related when sitting
+
+ BOOL mFocusOnAvatar;
+ LLVector3d mFocusGlobal;
+ LLVector3d mFocusTargetGlobal;
+ LLPointer<LLViewerObject> mFocusObject;
+ F32 mFocusObjectDist;
+ LLVector3 mFocusObjectOffset;
+ F32 mFocusDotRadius; // meters
+ BOOL mTrackFocusObject;
+ F32 mUIOffset;
+
+ LLCoordFrame mFrameAgent; // Agent position and view, agent-region coordinates
+
+ BOOL mCrouching;
+ BOOL mIsBusy;
+
+ S32 mAtKey; // Either 1, 0, or -1... indicates that movement-key is pressed
+ S32 mWalkKey; // like AtKey, but causes less forward thrust
+ S32 mLeftKey;
+ S32 mUpKey;
+ F32 mYawKey;
+ S32 mPitchKey;
+
+ F32 mOrbitLeftKey;
+ F32 mOrbitRightKey;
+ F32 mOrbitUpKey;
+ F32 mOrbitDownKey;
+ F32 mOrbitInKey;
+ F32 mOrbitOutKey;
+
+ F32 mPanUpKey;
+ F32 mPanDownKey;
+ F32 mPanLeftKey;
+ F32 mPanRightKey;
+ F32 mPanInKey;
+ F32 mPanOutKey;
+
+ U32 mControlFlags; // replacement for the mFooKey's
+ BOOL mbFlagsDirty;
+ BOOL mbFlagsNeedReset; // HACK for preventing incorrect flags sent when crossing region boundaries
+
+ BOOL mbJump;
+
+ LLFrameTimer mWanderTimer;
+ LLVector3d mWanderTargetGlobal;
+
+ BOOL mAutoPilot;
+ BOOL mAutoPilotFlyOnStop;
+ LLVector3d mAutoPilotTargetGlobal;
+ F32 mAutoPilotStopDistance;
+ BOOL mAutoPilotUseRotation;
+ LLVector3 mAutoPilotTargetFacing;
+ F32 mAutoPilotTargetDist;
+ S32 mAutoPilotNoProgressFrameCount;
+ F32 mAutoPilotRotationThreshold;
+ std::string mAutoPilotBehaviorName;
+ void (*mAutoPilotFinishedCallback)(BOOL, void *);
+ void* mAutoPilotCallbackData;
+ LLUUID mLeaderID;
+
+ std::set<LLUUID> mProxyForAgents;
+
+ LLColor4 mEffectColor;
+
+ BOOL mHaveHomePosition;
+ U64 mHomeRegionHandle;
+ LLVector3 mHomePosRegion;
+ LLFrameTimer mChatTimer;
+ LLUUID mLastChatterID;
+ F32 mNearChatRadius;
+ BOOL mAdminOverride;
+
+ // See indra_constants.h for values.
+ U8 mGodLevel;
+ LLFrameTimer mFidgetTimer;
+ LLFrameTimer mFocusObjectFadeTimer;
+ std::set<LLPointer <LLViewerObject> > mFadeObjects;
+ F32 mNextFidgetTime;
+ S32 mCurrentFidget;
+ BOOL mFirstLogin;
+ BOOL mGenderChosen;
+
+ //--------------------------------------------------------------------
+ // Wearables
+ //--------------------------------------------------------------------
+ struct LLWearableEntry
+ {
+ LLWearableEntry() : mItemID( LLUUID::null ), mWearable( NULL ) {}
+
+ LLUUID mItemID; // ID of the inventory item in the agent's inventory.
+ LLWearable* mWearable;
+ };
+ LLWearableEntry mWearableEntry[ WT_COUNT ];
+ U32 mAgentWearablesUpdateSerialNum;
+ BOOL mWearablesLoaded;
+ S32 mTextureCacheQueryID;
+ U32 mAppearanceSerialNum;
+ LLAnimPauseRequest mPauseRequest;
+
+ class createStandardWearablesAllDoneCallback : public LLRefCount
+ {
+ public:
+ ~createStandardWearablesAllDoneCallback();
+ };
+ class sendAgentWearablesUpdateCallback : public LLRefCount
+ {
+ public:
+ ~sendAgentWearablesUpdateCallback();
+ };
+
+ class addWearableToAgentInventoryCallback : public LLInventoryCallback
+ {
+ public:
+ enum {
+ CALL_NONE = 0,
+ CALL_UPDATE = 1,
+ CALL_RECOVERDONE = 2,
+ CALL_CREATESTANDARDDONE = 4,
+ CALL_MAKENEWOUTFITDONE = 8
+ } EType;
+
+ /**
+ * @brief Construct a callback for dealing with the wearables.
+ *
+ * Would like to pass the agent in here, but we can't safely
+ * count on it being around later. Just use gAgent directly.
+ * @param cb callback to execute on completion (??? unused ???)
+ * @param index Index for the wearable in the agent
+ * @param wearable The wearable data.
+ * @param todo Bitmask of actions to take on completion.
+ */
+ addWearableToAgentInventoryCallback(
+ LLPointer<LLRefCount> cb,
+ S32 index,
+ LLWearable* wearable,
+ U32 todo = CALL_NONE);
+ ~addWearableToAgentInventoryCallback() {};
+ virtual void fire(const LLUUID& inv_item);
+
+ private:
+ S32 mIndex;
+ LLWearable* mWearable;
+ U32 mTodo;
+ LLPointer<LLRefCount> mCB;
+ };
+
+ //control listeners
+ class LLHideGroupTitleListener: public LLSimpleListener
+ {
+ public:
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD &userdata);
+ };
+
+ class LLEffectColorListener: public LLSimpleListener
+ {
+ public:
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD &userdata);
+ };
+
+ LLHideGroupTitleListener mHideGroupTitleListener;
+ LLEffectColorListener mEffectColorListener;
+ LLFriendObserver* mFriendObserver;
+};
+
+extern LLAgent gAgent;
+
+#endif
diff --git a/indra/newview/llagentdata.cpp b/indra/newview/llagentdata.cpp
new file mode 100644
index 0000000000..39ed421628
--- /dev/null
+++ b/indra/newview/llagentdata.cpp
@@ -0,0 +1,15 @@
+/**
+ * @file llagentdata.cpp
+ * @brief Contains commonly used agent data.
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagentdata.h"
+
+LLUUID gAgentID;
+LLUUID gAgentSessionID;
diff --git a/indra/newview/llagentdata.h b/indra/newview/llagentdata.h
new file mode 100644
index 0000000000..eb120c7572
--- /dev/null
+++ b/indra/newview/llagentdata.h
@@ -0,0 +1,16 @@
+/**
+ * @file llagentdata.h
+ * @brief Contains commonly used agent data
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTDATA_H
+#define LL_LLAGENTDATA_H
+
+extern LLUUID gAgentID;
+extern LLUUID gAgentSessionID;
+
+#endif
diff --git a/indra/newview/llagentpilot.cpp b/indra/newview/llagentpilot.cpp
new file mode 100644
index 0000000000..2a6353fe2e
--- /dev/null
+++ b/indra/newview/llagentpilot.cpp
@@ -0,0 +1,250 @@
+/**
+ * @file llagentpilot.cpp
+ * @brief LLAgentPilot class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+
+#include "llagentpilot.h"
+#include "llagent.h"
+#include "llframestats.h"
+#include "viewer.h"
+#include "llviewercontrol.h"
+
+LLAgentPilot gAgentPilot;
+
+BOOL LLAgentPilot::sLoop = TRUE;
+
+LLAgentPilot::LLAgentPilot()
+{
+ mRecording = FALSE;
+ mPlaying = FALSE;
+ mStarted = FALSE;
+ mNumRuns = -1;
+}
+
+LLAgentPilot::~LLAgentPilot()
+{
+}
+
+void LLAgentPilot::load(const char *filename)
+{
+ llifstream file;
+
+ file.open(filename);
+
+ if (!file)
+ {
+ llinfos << "Couldn't open " << filename << ", aborting agentpilot load!" << llendl;
+ return;
+ }
+ else
+ {
+ llinfos << "Opening pilot file " << filename << llendl;
+ }
+
+ S32 num_actions;
+
+ file >> num_actions;
+
+ S32 i;
+ for (i = 0; i < num_actions; i++)
+ {
+ S32 action_type;
+ Action new_action;
+ file >> new_action.mTime >> action_type;
+ file >> new_action.mTarget.mdV[VX] >> new_action.mTarget.mdV[VY] >> new_action.mTarget.mdV[VZ];
+ new_action.mType = (EActionType)action_type;
+ mActions.put(new_action);
+ }
+
+ file.close();
+}
+
+void LLAgentPilot::save(const char *filename)
+{
+ llofstream file;
+ file.open(filename);
+
+ if (!file)
+ {
+ llinfos << "Couldn't open " << filename << ", aborting agentpilot save!" << llendl;
+ }
+
+ file << mActions.count() << '\n';
+
+ S32 i;
+ for (i = 0; i < mActions.count(); i++)
+ {
+ file << mActions[i].mTime << "\t" << mActions[i].mType << "\t";
+ file << std::setprecision(32) << mActions[i].mTarget.mdV[VX] << "\t" << mActions[i].mTarget.mdV[VY] << "\t" << mActions[i].mTarget.mdV[VZ] << '\n';
+ }
+
+ file.close();
+}
+
+void LLAgentPilot::startRecord()
+{
+ mActions.reset();
+ mTimer.reset();
+ addAction(STRAIGHT);
+ mRecording = TRUE;
+}
+
+void LLAgentPilot::stopRecord()
+{
+ gAgentPilot.addAction(STRAIGHT);
+ gAgentPilot.save(gSavedSettings.getString("StatsPilotFile").c_str());
+ mRecording = FALSE;
+}
+
+void LLAgentPilot::addAction(enum EActionType action_type)
+{
+ llinfos << "Adding waypoint: " << gAgent.getPositionGlobal() << llendl;
+ Action action;
+ action.mType = action_type;
+ action.mTarget = gAgent.getPositionGlobal();
+ action.mTime = mTimer.getElapsedTimeF32();
+ mLastRecordTime = (F32)action.mTime;
+ mActions.put(action);
+}
+
+void LLAgentPilot::startPlayback()
+{
+ if (!mPlaying)
+ {
+ mPlaying = TRUE;
+ mCurrentAction = 0;
+ mTimer.reset();
+
+ if (mActions.count())
+ {
+ llinfos << "Starting playback, moving to waypoint 0" << llendl;
+ gAgent.startAutoPilotGlobal(mActions[0].mTarget);
+ mStarted = FALSE;
+ }
+ else
+ {
+ llinfos << "No autopilot data, cancelling!" << llendl;
+ mPlaying = FALSE;
+ }
+ }
+}
+
+void LLAgentPilot::stopPlayback()
+{
+ if (mPlaying)
+ {
+ mPlaying = FALSE;
+ mCurrentAction = 0;
+ mTimer.reset();
+ gAgent.stopAutoPilot();
+ }
+}
+
+void LLAgentPilot::updateTarget()
+{
+ if (mPlaying)
+ {
+ if (mCurrentAction < mActions.count())
+ {
+ if (0 == mCurrentAction)
+ {
+ if (gAgent.getAutoPilot())
+ {
+ // Wait until we get to the first location before starting.
+ return;
+ }
+ else
+ {
+ if (!mStarted)
+ {
+ llinfos << "At start, beginning playback" << llendl;
+ mTimer.reset();
+ LLFrameStats::startLogging(NULL);
+ mStarted = TRUE;
+ }
+ }
+ }
+ if (mTimer.getElapsedTimeF32() > mActions[mCurrentAction].mTime)
+ {
+ //gAgent.stopAutoPilot();
+ mCurrentAction++;
+
+ if (mCurrentAction < mActions.count())
+ {
+ gAgent.startAutoPilotGlobal(mActions[mCurrentAction].mTarget);
+ }
+ else
+ {
+ stopPlayback();
+ LLFrameStats::stopLogging(NULL);
+ mNumRuns--;
+ if (sLoop)
+ {
+ if ((mNumRuns < 0) || (mNumRuns > 0))
+ {
+ llinfos << "Looping, restarting playback" << llendl;
+ startPlayback();
+ }
+ else if (mQuitAfterRuns)
+ {
+ llinfos << "Done with all runs, quitting viewer!" << llendl;
+ app_force_quit(NULL);
+ }
+ else
+ {
+ llinfos << "Done with all runs, disabling pilot" << llendl;
+ stopPlayback();
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ stopPlayback();
+ }
+ }
+ else if (mRecording)
+ {
+ if (mTimer.getElapsedTimeF32() - mLastRecordTime > 1.f)
+ {
+ addAction(STRAIGHT);
+ }
+ }
+}
+
+// static
+void LLAgentPilot::startRecord(void *)
+{
+ gAgentPilot.startRecord();
+}
+
+void LLAgentPilot::saveRecord(void *)
+{
+ gAgentPilot.stopRecord();
+}
+
+void LLAgentPilot::addWaypoint(void *)
+{
+ gAgentPilot.addAction(STRAIGHT);
+}
+
+void LLAgentPilot::startPlayback(void *)
+{
+ gAgentPilot.mNumRuns = -1;
+ gAgentPilot.startPlayback();
+}
+
+void LLAgentPilot::stopPlayback(void *)
+{
+ gAgentPilot.stopPlayback();
+}
diff --git a/indra/newview/llagentpilot.h b/indra/newview/llagentpilot.h
new file mode 100644
index 0000000000..c4223ba490
--- /dev/null
+++ b/indra/newview/llagentpilot.h
@@ -0,0 +1,77 @@
+/**
+ * @file llagentpilot.h
+ * @brief LLAgentPilot class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAGENTPILOT_H
+#define LL_LLAGENTPILOT_H
+
+#include "stdtypes.h"
+#include "lltimer.h"
+#include "v3dmath.h"
+#include "lldarray.h"
+
+// Class that drives the agent around according to a "script".
+
+class LLAgentPilot
+{
+public:
+ enum EActionType
+ {
+ STRAIGHT,
+ TURN
+ };
+
+ LLAgentPilot();
+ virtual ~LLAgentPilot();
+
+ void load(const char *filename);
+ void save(const char *filename);
+
+ void startRecord();
+ void stopRecord();
+ void addAction(enum EActionType action);
+
+ void startPlayback();
+ void stopPlayback();
+
+ void updateTarget();
+
+ static void startRecord(void *);
+ static void addWaypoint(void *);
+ static void saveRecord(void *);
+ static void startPlayback(void *);
+ static void stopPlayback(void *);
+ static BOOL sLoop;
+
+ S32 mNumRuns;
+ BOOL mQuitAfterRuns;
+private:
+ void setAutopilotTarget(const S32 id);
+
+ BOOL mRecording;
+ F32 mLastRecordTime;
+
+ BOOL mStarted;
+ BOOL mPlaying;
+ S32 mCurrentAction;
+
+ class Action
+ {
+ public:
+
+ EActionType mType;
+ LLVector3d mTarget;
+ F64 mTime;
+ };
+
+ LLDynamicArray<Action> mActions;
+ LLTimer mTimer;
+};
+
+extern LLAgentPilot gAgentPilot;
+
+#endif // LL_LLAGENTPILOT_H
diff --git a/indra/newview/llappearance.h b/indra/newview/llappearance.h
new file mode 100644
index 0000000000..b198429865
--- /dev/null
+++ b/indra/newview/llappearance.h
@@ -0,0 +1,33 @@
+/**
+ * @file llappearance.h
+ * @brief LLAppearance class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAPPEARANCE_H
+#define LL_LLAPPEARANCE_H
+
+#include "llskiplist.h"
+#include "lluuid.h"
+
+class LLAppearance
+{
+public:
+ LLAppearance() {}
+ ~LLAppearance() { mParamMap.deleteAllData(); }
+
+ void addParam( S32 id, F32 value ) { mParamMap.addData( id, new F32(value) ); }
+ F32* getParam( S32 id ) { F32* temp = mParamMap.getIfThere( id ); return temp; } // temp works around an invalid warning.
+
+ void addTexture( S32 te, const LLUUID& uuid ) { if( te < LLVOAvatar::TEX_NUM_ENTRIES ) mTextures[te] = uuid; }
+ const LLUUID& getTexture( S32 te ) { return ( te < LLVOAvatar::TEX_NUM_ENTRIES ) ? mTextures[te] : LLUUID::null; }
+
+ void clear() { mParamMap.deleteAllData(); for( S32 i=0; i<LLVOAvatar::TEX_NUM_ENTRIES; i++ ) mTextures[i].setNull(); }
+
+ LLPtrSkipMap<S32, F32*> mParamMap;
+ LLUUID mTextures[LLVOAvatar::TEX_NUM_ENTRIES];
+};
+
+#endif // LL_LLAPPEARANCE_H
diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp
new file mode 100644
index 0000000000..cb96be2401
--- /dev/null
+++ b/indra/newview/llassetuploadresponders.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llmapresponders.h
+ * @brief Processes responses received for asset upload requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llassetuploadresponders.h"
+
+#include "llagent.h"
+#include "llfloaterbuycurrency.h"
+#include "lleconomy.h"
+#include "llfilepicker.h"
+#include "llfocusmgr.h"
+#include "llnotify.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llpermissionsflags.h"
+#include "lluploaddialog.h"
+#include "llviewermenu.h" // FIXME -- for upload_new_resource()
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+LLNewAgentInventoryResponder::LLNewAgentInventoryResponder(const LLUUID& uuid,
+ const LLSD &post_data)
+ : LLHTTPClient::Responder()
+{
+ mUUID = uuid;
+ mPostData = post_data;
+}
+
+// virtual
+void LLNewAgentInventoryResponder::error(U32 statusNum, const std::string& reason)
+{
+ llinfos << "LLNewAgentInventoryResponder::error " << statusNum << llendl;
+ LLStringBase<char>::format_map_t args;
+ switch(statusNum)
+ {
+ case 400:
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "invalid parameters in upload request";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ break;
+ case 402:
+ //(result["message"].asString() == "insufficient funds")
+ LLFloaterBuyCurrency::buyCurrency("Uploading costs", gGlobalEconomy->getPriceUpload());
+ break;
+ case 500:
+ default:
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "the server is experiencing unexpected difficulties";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ break;
+ }
+ LLUploadDialog::modalUploadFinished();
+}
+
+//virtual
+void LLNewAgentInventoryResponder::result(const LLSD& result)
+{
+ lldebugs << "LLNewAgentInventoryResponder::result from capabilities" << llendl;
+
+ if (!result["success"])
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = mPostData["inventory_type"].asString();
+ args["[REASON]"] = "the server is experiencing unexpected difficulties";
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ return;
+ }
+
+ std::string uploader = result["uploader"];
+ LLAssetType::EType asset_type = LLAssetType::lookup(mPostData["asset_type"].asString().c_str());
+ LLInventoryType::EType inventory_type = LLInventoryType::lookup(mPostData["inventory_type"].asString().c_str());
+ // request succeeded
+ if (!uploader.empty())
+ {
+ LLHTTPClient::postFile(uploader, mUUID, asset_type, this);
+ }
+ // upload succeeded
+ else
+ {
+ // rename the file in the VFS to the actual asset id
+ gVFS->renameFile(mUUID, asset_type, result["new_asset"].asUUID(), asset_type);
+
+ // TODO: only request for textures, sound, and animation uploads
+ // Update money and ownership credit information
+ // since it probably changed on the server
+ if (mPostData["asset_type"].asString() == "texture" ||
+ mPostData["asset_type"].asString() == "sound" ||
+ mPostData["asset_type"].asString() == "animatn")
+ {
+ gMessageSystem->newMessageFast(_PREHASH_MoneyBalanceRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_MoneyData);
+ gMessageSystem->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
+ gAgent.sendReliableMessage();
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",gGlobalEconomy->getPriceUpload());
+ LLNotifyBox::showXml("UploadPayment", args);
+ }
+ // Actually add the upload to viewer inventory
+ llinfos << "Adding " << result["new_inventory_item"].asUUID() << " "
+ << result["new_asset"].asUUID() << " to inventory." << llendl;
+ if(mPostData["folder_id"].asUUID().notNull())
+ {
+ LLPermissions perm;
+ U32 next_owner_perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ if (mPostData["inventory_type"].asString() == "snapshot")
+ {
+ next_owner_perm = PERM_ALL;
+ }
+ else
+ {
+ next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ }
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, next_owner_perm);
+ S32 creation_date_now = time_corrected();
+ LLPointer<LLViewerInventoryItem> item
+ = new LLViewerInventoryItem(result["new_inventory_item"].asUUID(),
+ mPostData["folder_id"].asUUID(),
+ perm,
+ result["new_asset"].asUUID(),
+ asset_type,
+ inventory_type,
+ mPostData["name"].asString(),
+ mPostData["description"].asString(),
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ creation_date_now);
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+
+ // Show the preview panel for textures and sounds to let
+ // user know that the image (or snapshot) arrived intact.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+
+ view->getPanel()->setSelection(result["new_inventory_item"].asUUID(), TAKE_FOCUS_NO);
+ if((LLAssetType::AT_TEXTURE == asset_type)
+ || (LLAssetType::AT_SOUND == asset_type))
+ {
+ view->getPanel()->openSelected();
+ }
+ //LLInventoryView::dumpSelectionInformation((void*)view);
+ // restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+
+ // remove the "Uploading..." message
+ LLUploadDialog::modalUploadFinished();
+
+ // *FIX: This is a pretty big hack. What this does is check the
+ // file picker if there are any more pending uploads. If so,
+ // upload that file.
+ const char* next_file = LLFilePicker::instance().getNextFile();
+ if(next_file)
+ {
+ const char* name = LLFilePicker::instance().getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(next_file, asset_name, asset_name,
+ 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ }
+ }
+}
diff --git a/indra/newview/llassetuploadresponders.h b/indra/newview/llassetuploadresponders.h
new file mode 100644
index 0000000000..b4d5377601
--- /dev/null
+++ b/indra/newview/llassetuploadresponders.h
@@ -0,0 +1,26 @@
+/**
+ * @file llmapresponders.h
+ * @brief Processes responses received for asset upload requests.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNEWAGENTINVENTORYRESPONDER_H
+#define LL_LLNEWAGENTINVENTORYRESPONDER_H
+
+#include "llhttpclient.h"
+
+class LLNewAgentInventoryResponder : public LLHTTPClient::Responder
+{
+public:
+ LLNewAgentInventoryResponder(const LLUUID& uuid, const LLSD& post_data);
+ void error(U32 statusNum, const std::string& reason);
+ virtual void result(const LLSD& content);
+
+private:
+ LLUUID mUUID;
+ LLSD mPostData;
+};
+
+#endif // LL_LLNEWAGENTINVENTORYRESPONDER_H
diff --git a/indra/newview/llaudiosourcevo.cpp b/indra/newview/llaudiosourcevo.cpp
new file mode 100644
index 0000000000..86cc1e206e
--- /dev/null
+++ b/indra/newview/llaudiosourcevo.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file llaudiosourcevo.cpp
+ * @author Douglas Soo, James Cook
+ * @brief Audio sources attached to viewer objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llaudiosourcevo.h"
+
+#include "llagent.h"
+#include "llmutelist.h"
+#include "llviewerparcelmgr.h"
+
+LLAudioSourceVO::LLAudioSourceVO(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, LLViewerObject *objectp)
+: LLAudioSource(sound_id, owner_id, gain),
+ mObjectp(objectp),
+ mActualGain(gain)
+{
+ setAmbient(FALSE);
+ updateGain();
+ update();
+}
+
+LLAudioSourceVO::~LLAudioSourceVO()
+{
+ if (mObjectp)
+ {
+ mObjectp->clearAttachedSound();
+ }
+ mObjectp = NULL;
+}
+
+void LLAudioSourceVO::setGain(const F32 gain)
+{
+ mActualGain = llclamp(gain, 0.f, 1.f);
+ updateGain();
+}
+
+void LLAudioSourceVO::updateGain()
+{
+ if (!mObjectp)
+ {
+ return;
+ }
+
+ BOOL mute = FALSE;
+ if (gParcelMgr)
+ {
+ LLVector3d pos_global = mObjectp->getPositionGlobal();
+ if (!gParcelMgr->canHearSound(pos_global))
+ {
+ mute = TRUE;
+ }
+ }
+
+ if (!mute && gMuteListp)
+ {
+ if (gMuteListp->isMuted(mObjectp->getID()))
+ {
+ mute = TRUE;
+ }
+ else if (gMuteListp->isMuted(mOwnerID))
+ {
+ mute = TRUE;
+ }
+ else if (mObjectp->isAttachment())
+ {
+ LLViewerObject* parent = mObjectp;
+ while (parent
+ && !parent->isAvatar())
+ {
+ parent = (LLViewerObject*)parent->getParent();
+ }
+ if (parent
+ && gMuteListp->isMuted(parent->getID()))
+ {
+ mute = TRUE;
+ }
+ }
+ }
+
+ if (!mute)
+ {
+ mGain = mActualGain;
+ }
+ else
+ {
+ mGain = 0.f;
+ }
+}
+
+
+void LLAudioSourceVO::update()
+{
+ if (!mObjectp)
+ {
+ return;
+ }
+
+ if (mObjectp->isDead())
+ {
+ mObjectp = NULL;
+ return;
+ }
+
+ updateGain();
+ if (mObjectp->isHUDAttachment())
+ {
+ mPositionGlobal = gAgent.getCameraPositionGlobal();
+ }
+ else
+ {
+ mPositionGlobal = mObjectp->getPositionGlobal();
+ }
+ if (mObjectp->getSubParent())
+ {
+ mVelocity = mObjectp->getSubParent()->getVelocity();
+ }
+ else
+ {
+ mVelocity = mObjectp->getVelocity();
+ }
+
+ LLAudioSource::update();
+}
diff --git a/indra/newview/llaudiosourcevo.h b/indra/newview/llaudiosourcevo.h
new file mode 100644
index 0000000000..b16a70ec98
--- /dev/null
+++ b/indra/newview/llaudiosourcevo.h
@@ -0,0 +1,34 @@
+/**
+ * @file llaudiosourcevo.h
+ * @author Douglas Soo, James Cook
+ * @brief Audio sources attached to viewer objects
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLAUDIOSOURCEVO_H
+#define LL_LLAUDIOSOURCEVO_H
+
+#include "audioengine.h"
+#include "llviewerobject.h"
+
+class LLViewerObject;
+
+class LLAudioSourceVO : public LLAudioSource
+{
+public:
+ LLAudioSourceVO(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, LLViewerObject *objectp);
+ virtual ~LLAudioSourceVO();
+ /*virtual*/ void update();
+ /*virtual*/ void setGain(const F32 gain);
+
+private:
+ void updateGain();
+
+private:
+ LLPointer<LLViewerObject> mObjectp;
+ F32 mActualGain; // The "real" gain, when not off due to parcel effects
+};
+
+#endif // LL_LLAUDIOSOURCEVO_H
diff --git a/indra/newview/llbox.cpp b/indra/newview/llbox.cpp
new file mode 100644
index 0000000000..3d5a6b6fea
--- /dev/null
+++ b/indra/newview/llbox.cpp
@@ -0,0 +1,105 @@
+/**
+ * @file llbox.cpp
+ * @brief Draws a box using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llbox.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+
+LLBox gBox;
+
+// These routines support multiple textures on a box
+void LLBox::prerender()
+{
+ F32 size = 1.f;
+
+ mTriangleCount = 6 * 2;
+
+ mVertex[0][0] = mVertex[1][0] = mVertex[2][0] = mVertex[3][0] = -size / 2;
+ mVertex[4][0] = mVertex[5][0] = mVertex[6][0] = mVertex[7][0] = size / 2;
+ mVertex[0][1] = mVertex[1][1] = mVertex[4][1] = mVertex[5][1] = -size / 2;
+ mVertex[2][1] = mVertex[3][1] = mVertex[6][1] = mVertex[7][1] = size / 2;
+ mVertex[0][2] = mVertex[3][2] = mVertex[4][2] = mVertex[7][2] = -size / 2;
+ mVertex[1][2] = mVertex[2][2] = mVertex[5][2] = mVertex[6][2] = size / 2;
+}
+
+// These routines support multiple textures on a box
+void LLBox::cleanupGL()
+{
+ // No GL state, a noop.
+}
+
+void LLBox::renderface(S32 which_face)
+{
+ static F32 normals[6][3] =
+ {
+ {-1.0f, 0.0f, 0.0f},
+ { 0.0f, 1.0f, 0.0f},
+ { 1.0f, 0.0f, 0.0f},
+ { 0.0f, -1.0f, 0.0f},
+ { 0.0f, 0.0f, 1.0f},
+ { 0.0f, 0.0f, -1.0f}
+ };
+ static S32 faces[6][4] =
+ {
+ {0, 1, 2, 3},
+ {3, 2, 6, 7},
+ {7, 6, 5, 4},
+ {4, 5, 1, 0},
+ {5, 6, 2, 1},
+ {7, 4, 0, 3}
+ };
+
+ glBegin(GL_QUADS);
+ glNormal3fv(&normals[which_face][0]);
+ glTexCoord2f(1,0);
+ glVertex3fv(&mVertex[ faces[which_face][0] ][0]);
+ glTexCoord2f(1,1);
+ glVertex3fv(&mVertex[ faces[which_face][1] ][0]);
+ glTexCoord2f(0,1);
+ glVertex3fv(&mVertex[ faces[which_face][2] ][0]);
+ glTexCoord2f(0,0);
+ glVertex3fv(&mVertex[ faces[which_face][3] ][0]);
+ glEnd();
+}
+
+void LLBox::render()
+{
+ // This is a flattend representation of the box as render here
+ // .
+ // (-++) (+++) /|\t
+ // +------------+ | (texture coordinates)
+ // |2 1| |
+ // | 4 | (*) --->s
+ // | TOP |
+ // | |
+ // (-++) (--+)|3 0|(+-+) (+++) (-++)
+ // +------------+------------+------------+------------+
+ // |2 1|2 1|2 1|2 1|
+ // | 0 | 1 | 2 | 3 |
+ // | BACK | RIGHT | FRONT | LEFT |
+ // | | | | |
+ // |3 0|3 0|3 0|3 0|
+ // +------------+------------+------------+------------+
+ // (-+-) (---)|2 1|(+--) (++-) (-+-)
+ // | 5 |
+ // | BOTTOM |
+ // | |
+ // |3 0|
+ // +------------+
+ // (-+-) (++-)
+
+ renderface(5);
+ renderface(4);
+ renderface(3);
+ renderface(2);
+ renderface(1);
+ renderface(0);
+}
diff --git a/indra/newview/llbox.h b/indra/newview/llbox.h
new file mode 100644
index 0000000000..038d82a4b6
--- /dev/null
+++ b/indra/newview/llbox.h
@@ -0,0 +1,32 @@
+/**
+ * @file llbox.h
+ * @brief Draws a box using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLBOX_H
+#define LL_LLBOX_H
+
+#include "stdtypes.h"
+
+class LLBox
+{
+protected:
+// GLuint mDisplayList;
+ F32 mVertex[8][3];
+ U32 mTriangleCount;
+public:
+ void prerender();
+ void cleanupGL();
+
+ void renderface(S32 which_face);
+ void render();
+
+ U32 getTriangleCount() { return mTriangleCount; }
+};
+
+extern LLBox gBox;
+
+#endif
diff --git a/indra/newview/llcallbacklist.cpp b/indra/newview/llcallbacklist.cpp
new file mode 100644
index 0000000000..156a064a41
--- /dev/null
+++ b/indra/newview/llcallbacklist.cpp
@@ -0,0 +1,173 @@
+/**
+ * @file llcallbacklist.cpp
+ * @brief A simple list of callback functions to call.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcallbacklist.h"
+
+// Library includes
+#include "llerror.h"
+
+
+//
+// Globals
+//
+LLCallbackList gIdleCallbacks;
+
+//
+// Member functions
+//
+
+LLCallbackList::LLCallbackList()
+{
+ // nothing
+}
+
+LLCallbackList::~LLCallbackList()
+{
+}
+
+
+void LLCallbackList::addFunction( callback_t func, void *data)
+{
+ if (!func)
+ {
+ llerrs << "LLCallbackList::addFunction - function is NULL" << llendl;
+ return;
+ }
+
+ // only add one callback per func/data pair
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter == mCallbackList.end())
+ {
+ mCallbackList.push_back(t);
+ }
+}
+
+
+BOOL LLCallbackList::containsFunction( callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter != mCallbackList.end())
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+BOOL LLCallbackList::deleteFunction( callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = std::find(mCallbackList.begin(), mCallbackList.end(), t);
+ if (iter != mCallbackList.end())
+ {
+ mCallbackList.erase(iter);
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+void LLCallbackList::deleteAllFunctions()
+{
+ mCallbackList.clear();
+}
+
+
+void LLCallbackList::callFunctions()
+{
+ for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); )
+ {
+ callback_list_t::iterator curiter = iter++;
+ curiter->first(curiter->second);
+ }
+}
+
+#ifdef _DEBUG
+
+void test1(void *data)
+{
+ S32 *s32_data = (S32 *)data;
+ llinfos << "testfunc1 " << *s32_data << llendl;
+}
+
+
+void test2(void *data)
+{
+ S32 *s32_data = (S32 *)data;
+ llinfos << "testfunc2 " << *s32_data << llendl;
+}
+
+
+void
+LLCallbackList::test()
+{
+ S32 a = 1;
+ S32 b = 2;
+ LLCallbackList *list = new LLCallbackList;
+
+ llinfos << "Testing LLCallbackList" << llendl;
+
+ if (!list->deleteFunction(NULL))
+ {
+ llinfos << "passed 1" << llendl;
+ }
+ else
+ {
+ llinfos << "error, removed function from empty list" << llendl;
+ }
+
+ // llinfos << "This should crash" << llendl;
+ // list->addFunction(NULL);
+
+ list->addFunction(&test1, &a);
+ list->addFunction(&test1, &a);
+
+ llinfos << "Expect: test1 1, test1 1" << llendl;
+ list->callFunctions();
+
+ list->addFunction(&test1, &b);
+ list->addFunction(&test2, &b);
+
+ llinfos << "Expect: test1 1, test1 1, test1 2, test2 2" << llendl;
+ list->callFunctions();
+
+ if (list->deleteFunction(&test1, &b))
+ {
+ llinfos << "passed 3" << llendl;
+ }
+ else
+ {
+ llinfos << "error removing function" << llendl;
+ }
+
+ llinfos << "Expect: test1 1, test1 1, test2 2" << llendl;
+ list->callFunctions();
+
+ list->deleteAllFunctions();
+
+ llinfos << "Expect nothing" << llendl;
+ list->callFunctions();
+
+ llinfos << "nothing :-)" << llendl;
+
+ delete list;
+
+ llinfos << "test complete" << llendl;
+}
+
+#endif // _DEBUG
diff --git a/indra/newview/llcallbacklist.h b/indra/newview/llcallbacklist.h
new file mode 100644
index 0000000000..e647f50644
--- /dev/null
+++ b/indra/newview/llcallbacklist.h
@@ -0,0 +1,39 @@
+/**
+ * @file llcallbacklist.h
+ * @brief A simple list of callback functions to call.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCALLBACKLIST_H
+#define LL_LLCALLBACKLIST_H
+
+#include "llstl.h"
+
+class LLCallbackList
+{
+public:
+ typedef void (*callback_t)(void*);
+
+ LLCallbackList();
+ ~LLCallbackList();
+
+ void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data)
+ BOOL containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair
+ BOOL deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found
+ void callFunctions(); // calls all functions
+ void deleteAllFunctions();
+
+ static void test();
+
+protected:
+ // Use a list so that the callbacks are ordered in case that matters
+ typedef std::pair<callback_t,void*> callback_pair_t;
+ typedef std::list<callback_pair_t > callback_list_t;
+ callback_list_t mCallbackList;
+};
+
+extern LLCallbackList gIdleCallbacks;
+
+#endif
diff --git a/indra/newview/llcallingcard.cpp b/indra/newview/llcallingcard.cpp
new file mode 100644
index 0000000000..c53d1d9ebc
--- /dev/null
+++ b/indra/newview/llcallingcard.cpp
@@ -0,0 +1,797 @@
+/**
+ * @file llcallingcard.cpp
+ * @brief Implementation of the LLPreviewCallingCard class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#if LL_WINDOWS
+#pragma warning( disable : 4800 ) // performance warning in <functional>
+#endif
+
+#include "llcallingcard.h"
+
+#include <vector>
+#include <algorithm>
+//#include <iterator>
+
+#include "indra_constants.h"
+#include "llcachename.h"
+#include "llstl.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+//#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llimview.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+class LLTrackingData
+{
+public:
+ LLTrackingData(const LLUUID& avatar_id, const std::string& name);
+ bool haveTrackingInfo();
+ void setTrackedCoarseLocation(const LLVector3d& global_pos);
+ void agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos);
+
+public:
+ LLUUID mAvatarID;
+ LLString mName;
+ LLVector3d mGlobalPositionEstimate;
+ bool mHaveInfo;
+ bool mHaveCoarseInfo;
+ LLTimer mCoarseLocationTimer;
+ LLTimer mUpdateTimer;
+ LLTimer mAgentGone;
+};
+
+const F32 COARSE_FREQUENCY = 2.2f;
+const F32 FIND_FREQUENCY = 29.7f; // This results in a database query, so cut these back
+const F32 OFFLINE_SECONDS = FIND_FREQUENCY + 8.0f;
+
+// static
+LLAvatarTracker LLAvatarTracker::sInstance;
+
+/*
+class LLAvatarTrackerInventoryObserver : public LLInventoryObserver
+{
+public:
+ LLAvatarTrackerInventoryObserver(LLAvatarTracker* at) :
+ mAT(at) {}
+ virtual ~LLAvatarTrackerInventoryObserver() {}
+ virtual void changed(U32 mask);
+protected:
+ LLAvatarTracker* mAT;
+};
+*/
+
+/*
+void LLAvatarTrackerInventoryObserver::changed(U32 mask)
+{
+ // if there's a calling card change, just do it.
+ if((mask & LLInventoryObserver::CALLING_CARD) != 0)
+ {
+ mAT->inventoryChanged();
+ }
+}
+*/
+
+///----------------------------------------------------------------------------
+/// Class LLAvatarTracker
+///----------------------------------------------------------------------------
+
+LLAvatarTracker::LLAvatarTracker() :
+ mTrackingData(NULL),
+ mTrackedAgentValid(false),
+ //mInventory(NULL),
+ //mInventoryObserver(NULL),
+ mModifyMask(0x0)
+{
+}
+
+LLAvatarTracker::~LLAvatarTracker()
+{
+ deleteTrackingData();
+ std::for_each(mObservers.begin(), mObservers.end(), DeletePointer());
+}
+
+void LLAvatarTracker::track(const LLUUID& avatar_id, const std::string& name)
+{
+ deleteTrackingData();
+ mTrackedAgentValid = false;
+ mTrackingData = new LLTrackingData(avatar_id, name);
+ findAgent();
+
+ // We track here because findAgent() is called on a timer (for now).
+ if(avatar_id.notNull())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TrackAgent);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_PreyID, avatar_id);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLAvatarTracker::untrack(const LLUUID& avatar_id)
+{
+ if (mTrackingData && mTrackingData->mAvatarID == avatar_id)
+ {
+ deleteTrackingData();
+ mTrackedAgentValid = false;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_TrackAgent);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_PreyID, LLUUID::null);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLAvatarTracker::setTrackedCoarseLocation(const LLVector3d& global_pos)
+{
+ if(mTrackingData)
+ {
+ mTrackingData->setTrackedCoarseLocation(global_pos);
+ }
+}
+
+bool LLAvatarTracker::haveTrackingInfo()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->haveTrackingInfo();
+ }
+ return false;
+}
+
+LLVector3d LLAvatarTracker::getGlobalPos()
+{
+ if(!mTrackedAgentValid) return LLVector3d();
+ LLVector3d global_pos;
+
+ LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
+ if(object && !object->isDead())
+ {
+ global_pos = object->getPositionGlobal();
+ // HACK - for making the tracker point above the avatar's head
+ // rather than its groin
+ global_pos.mdV[VZ] += 0.7f * ((LLVOAvatar *)object)->mBodySize.mV[VZ];
+
+ mTrackingData->mGlobalPositionEstimate = global_pos;
+ }
+ else
+ {
+ global_pos = mTrackingData->mGlobalPositionEstimate;
+ }
+
+ return global_pos;
+}
+
+void LLAvatarTracker::getDegreesAndDist(F32& rot,
+ F64& horiz_dist,
+ F64& vert_dist)
+{
+ if(!mTrackingData) return;
+
+ LLVector3d global_pos;
+
+ LLViewerObject* object = gObjectList.findObject(mTrackingData->mAvatarID);
+ if(object && !object->isDead())
+ {
+ global_pos = object->getPositionGlobal();
+ mTrackingData->mGlobalPositionEstimate = global_pos;
+ }
+ else
+ {
+ global_pos = mTrackingData->mGlobalPositionEstimate;
+ }
+ LLVector3d to_vec = global_pos - gAgent.getPositionGlobal();
+ horiz_dist = sqrt(to_vec.mdV[VX] * to_vec.mdV[VX] + to_vec.mdV[VY] * to_vec.mdV[VY]);
+ vert_dist = to_vec.mdV[VZ];
+ rot = F32(RAD_TO_DEG * atan2(to_vec.mdV[VY], to_vec.mdV[VX]));
+}
+
+const LLString& LLAvatarTracker::getName()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->mName;
+ }
+ else
+ {
+ return LLString::null;
+ }
+}
+
+const LLUUID& LLAvatarTracker::getAvatarID()
+{
+ if(mTrackingData)
+ {
+ return mTrackingData->mAvatarID;
+ }
+ else
+ {
+ return LLUUID::null;
+ }
+}
+
+S32 LLAvatarTracker::addBuddyList(const LLAvatarTracker::buddy_map_t& buds)
+{
+ using namespace std;
+
+ U32 new_buddy_count = 0;
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ LLUUID agent_id;
+ for(buddy_map_t::const_iterator itr = buds.begin(); itr != buds.end(); ++itr)
+ {
+ agent_id = (*itr).first;
+ if(mBuddyInfo.find(agent_id) == mBuddyInfo.end())
+ {
+ ++new_buddy_count;
+ mBuddyInfo[agent_id] = (*itr).second;
+ gCacheName->getName(agent_id, first, last);
+ mModifyMask |= LLFriendObserver::ADD;
+ }
+ }
+
+ return new_buddy_count;
+}
+
+
+void LLAvatarTracker::copyBuddyList(buddy_map_t& buddies) const
+{
+ buddy_map_t::const_iterator it = mBuddyInfo.begin();
+ buddy_map_t::const_iterator end = mBuddyInfo.end();
+ for(; it != end; ++it)
+ {
+ buddies[(*it).first] = (*it).second;
+ }
+}
+
+void LLAvatarTracker::terminateBuddy(const LLUUID& id)
+{
+ lldebugs << "LLAvatarTracker::terminateBuddy()" << llendl;
+ LLRelationship* buddy = get_ptr_in_map(mBuddyInfo, id);
+ if(!buddy) return;
+ mBuddyInfo.erase(id);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("TerminateFriendship");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ExBlock");
+ msg->addUUID("OtherID", id);
+ gAgent.sendReliableMessage();
+ mModifyMask |= LLFriendObserver::REMOVE;
+ delete buddy;
+}
+
+// get all buddy info
+const LLRelationship* LLAvatarTracker::getBuddyInfo(const LLUUID& id) const
+{
+ if(id.isNull()) return NULL;
+ return get_ptr_in_map(mBuddyInfo, id);
+}
+
+// online status
+void LLAvatarTracker::setBuddyOnline(const LLUUID& id, bool is_online)
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ info->online(is_online);
+ mModifyMask |= LLFriendObserver::ONLINE;
+ }
+}
+
+bool LLAvatarTracker::isBuddyOnline(const LLUUID& id) const
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ return info->isOnline();
+ }
+ return false;
+}
+
+// empowered status
+void LLAvatarTracker::setBuddyEmpowered(const LLUUID& id, bool is_empowered)
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ info->grantRights(LLRelationship::GRANT_MODIFY_OBJECTS, 0);
+ mModifyMask |= LLFriendObserver::POWERS;
+ }
+}
+
+bool LLAvatarTracker::isBuddyEmpowered(const LLUUID& id) const
+{
+ LLRelationship* info = get_ptr_in_map(mBuddyInfo, id);
+ if(info)
+ {
+ return info->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS);
+ }
+ return false;
+}
+
+void LLAvatarTracker::empower(const LLUUID& id, bool grant)
+{
+ // wrapper for ease of use in some situations.
+ buddy_map_t list;
+ /*
+ list.insert(id);
+ empowerList(list, grant);
+ */
+}
+
+void LLAvatarTracker::empowerList(const buddy_map_t& list, bool grant)
+{
+ llwarns << "LLAvatarTracker::empowerList() not implemented." << llendl;
+/*
+ LLMessageSystem* msg = gMessageSystem;
+ const char* message_name;
+ const char* block_name;
+ const char* field_name;
+ if(grant)
+ {
+ message_name = _PREHASH_GrantModification;
+ block_name = _PREHASH_EmpoweredBlock;
+ field_name = _PREHASH_EmpoweredID;
+ }
+ else
+ {
+ message_name = _PREHASH_RevokeModification;
+ block_name = _PREHASH_RevokedBlock;
+ field_name = _PREHASH_RevokedID;
+ }
+
+ std::string name;
+ gAgent.buildFullnameAndTitle(name);
+
+ bool start_new_message = true;
+ buddy_list_t::const_iterator it = list.begin();
+ buddy_list_t::const_iterator end = list.end();
+ for(; it != end; ++it)
+ {
+ if(NULL == get_ptr_in_map(mBuddyInfo, (*it))) continue;
+ setBuddyEmpowered((*it), grant);
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(message_name);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addStringFast(_PREHASH_GranterName, name);
+ }
+ msg->nextBlockFast(block_name);
+ msg->addUUIDFast(field_name, (*it));
+ if(msg->isSendFullFast(block_name))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+*/
+}
+
+void LLAvatarTracker::deleteTrackingData()
+{
+ delete mTrackingData;
+ mTrackingData = NULL;
+}
+
+void LLAvatarTracker::findAgent()
+{
+ if (!mTrackingData) return;
+ if (mTrackingData->mAvatarID.isNull()) return;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_FindAgent); // Request
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_Hunter, gAgentID);
+ msg->addUUIDFast(_PREHASH_Prey, mTrackingData->mAvatarID);
+ msg->addU32Fast(_PREHASH_SpaceIP, 0); // will get filled in by userserver
+ msg->nextBlockFast(_PREHASH_LocationBlock);
+ const F64 NO_LOCATION = 0.0;
+ msg->addF64Fast(_PREHASH_GlobalX, NO_LOCATION);
+ msg->addF64Fast(_PREHASH_GlobalY, NO_LOCATION);
+ gAgent.sendReliableMessage();
+}
+
+void LLAvatarTracker::addObserver(LLFriendObserver* observer)
+{
+ if(observer)
+ {
+ mObservers.push_back(observer);
+ }
+}
+
+void LLAvatarTracker::removeObserver(LLFriendObserver* observer)
+{
+ mObservers.erase(
+ std::remove(mObservers.begin(), mObservers.end(), observer),
+ mObservers.end());
+}
+
+void LLAvatarTracker::notifyObservers()
+{
+ observer_list_t observers(mObservers);
+ observer_list_t::iterator it = observers.begin();
+ observer_list_t::iterator end = observers.end();
+ for(; it != end; ++it)
+ {
+ (*it)->changed(mModifyMask);
+ }
+ mModifyMask = LLFriendObserver::NONE;
+}
+
+void LLAvatarTracker::applyFunctor(LLRelationshipFunctor& f)
+{
+ buddy_map_t::iterator it = mBuddyInfo.begin();
+ buddy_map_t::iterator end = mBuddyInfo.end();
+ for(; it != end; ++it)
+ {
+ f((*it).first, (*it).second);
+ }
+}
+
+void LLAvatarTracker::registerCallbacks(LLMessageSystem* msg)
+{
+ msg->setHandlerFuncFast(_PREHASH_FindAgent, processAgentFound);
+ msg->setHandlerFuncFast(_PREHASH_OnlineNotification,
+ processOnlineNotification);
+ msg->setHandlerFuncFast(_PREHASH_OfflineNotification,
+ processOfflineNotification);
+ //msg->setHandlerFuncFast(_PREHASH_GrantedProxies,
+ // processGrantedProxies);
+ msg->setHandlerFunc("TerminateFriendship", processTerminateFriendship);
+ msg->setHandlerFunc(_PREHASH_ChangeUserRights, processChangeUserRights);
+}
+
+// static
+void LLAvatarTracker::processAgentFound(LLMessageSystem* msg, void**)
+{
+ LLUUID id;
+
+
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Hunter, id);
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_Prey, id);
+ // *FIX: should make sure prey id matches.
+ LLVector3d estimated_global_pos;
+ msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalX,
+ estimated_global_pos.mdV[VX]);
+ msg->getF64Fast(_PREHASH_LocationBlock, _PREHASH_GlobalY,
+ estimated_global_pos.mdV[VY]);
+ LLAvatarTracker::instance().agentFound(id, estimated_global_pos);
+}
+
+void LLAvatarTracker::agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos)
+{
+ if(!mTrackingData) return;
+ //if we get a valid reply from the server, that means the agent
+ //is our friend and mappable, so enable interest list based updates
+ LLAvatarTracker::instance().setTrackedAgentValid(true);
+ mTrackingData->agentFound(prey, estimated_global_pos);
+}
+
+// static
+void LLAvatarTracker::processOnlineNotification(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processOnlineNotification()" << llendl;
+ instance().processNotify(msg, true);
+}
+
+// static
+void LLAvatarTracker::processOfflineNotification(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processOfflineNotification()" << llendl;
+ instance().processNotify(msg, false);
+}
+
+void LLAvatarTracker::processChange(LLMessageSystem* msg)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_Rights);
+ LLUUID agent_id, agent_related;
+ S32 new_rights;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ for(int i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_Rights, _PREHASH_AgentRelated, agent_related, i);
+ msg->getS32Fast(_PREHASH_Rights,_PREHASH_RelatedRights, new_rights, i);
+ if(agent_id == gAgent.getID())
+ {
+ if(mBuddyInfo.find(agent_related) != mBuddyInfo.end())
+ {
+ (mBuddyInfo[agent_related])->setRightsTo(new_rights);
+ }
+ }
+ else
+ {
+ if(mBuddyInfo.find(agent_id) != mBuddyInfo.end())
+ {
+ if((mBuddyInfo[agent_id]->getRightsGrantedFrom() ^ new_rights) & LLRelationship::GRANT_MODIFY_OBJECTS)
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ LLStringBase<char>::format_map_t args;
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ if(LLRelationship::GRANT_MODIFY_OBJECTS & new_rights)
+ {
+ gViewerWindow->alertXml("GrantedModifyRights",args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("RevokedModifyRights",args);
+ }
+ }
+ (mBuddyInfo[agent_id])->setRightsFrom(new_rights);
+ }
+ }
+ }
+ mModifyMask |= LLFriendObserver::POWERS;
+
+ notifyObservers();
+}
+
+void LLAvatarTracker::processChangeUserRights(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLAvatarTracker::processChangeUserRights()" << llendl;
+ instance().processChange(msg);
+}
+
+void LLAvatarTracker::processNotify(LLMessageSystem* msg, bool online)
+{
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_AgentBlock);
+ BOOL chat_notify = gSavedSettings.getBOOL("ChatOnlineNotification");
+
+ if(count > 0)
+ {
+ LLUUID agent_id;
+ const LLRelationship* info = NULL;
+ LLUUID tracking_id;
+ if(mTrackingData)
+ {
+ tracking_id = mTrackingData->mAvatarID;
+ }
+ BOOL notify = FALSE;
+ LLString::format_map_t args;
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_AgentID, agent_id, i);
+ info = getBuddyInfo(agent_id);
+ if(info)
+ {
+ setBuddyOnline(agent_id,online);
+ if(chat_notify)
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ notify = TRUE;
+ args["[FIRST]"] = first;
+ args["[LAST]"] = last;
+ }
+ }
+ }
+ if(tracking_id == agent_id)
+ {
+ // we were tracking someone who went offline
+ deleteTrackingData();
+ }
+ //FIXME get actual inventory id
+ gInventory.addChangedMask(LLInventoryObserver::CALLING_CARD, LLUUID::null);
+ }
+ if(notify)
+ {
+ LLNotifyBox::showXml(online ? "FriendOnline" : "FriendOffline", args);
+ }
+
+ mModifyMask |= LLFriendObserver::ONLINE;
+ instance().notifyObservers();
+ gInventory.notifyObservers();
+ }
+}
+
+// XUI:translate
+void LLAvatarTracker::formFriendship(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ LLRelationship* buddy_info = get_ptr_in_map(instance().mBuddyInfo, id);
+ if(!buddy_info)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ //The default for relationship establishment is to have both parties
+ //visible online to each other.
+ buddy_info = new LLRelationship(LLRelationship::GRANT_ONLINE_STATUS,LLRelationship::GRANT_ONLINE_STATUS, false);
+ at.mBuddyInfo[id] = buddy_info;
+ at.mModifyMask |= LLFriendObserver::ADD;
+ at.notifyObservers();
+ }
+ }
+}
+
+void LLAvatarTracker::processTerminateFriendship(LLMessageSystem* msg, void**)
+{
+ LLUUID id;
+ msg->getUUID("ExBlock", "OtherID", id);
+ if(id.notNull())
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLRelationship* buddy = get_ptr_in_map(at.mBuddyInfo, id);
+ if(!buddy) return;
+ at.mBuddyInfo.erase(id);
+ at.mModifyMask |= LLFriendObserver::REMOVE;
+ delete buddy;
+ at.notifyObservers();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Tracking Data
+///----------------------------------------------------------------------------
+
+LLTrackingData::LLTrackingData(const LLUUID& avatar_id, const std::string& name)
+: mAvatarID(avatar_id),
+ mHaveInfo(false),
+ mHaveCoarseInfo(false)
+{
+ mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ if(!name.empty())
+ {
+ mName = name;
+ }
+}
+
+void LLTrackingData::agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos)
+{
+ if(prey != mAvatarID)
+ {
+ llwarns << "LLTrackingData::agentFound() - found " << prey
+ << " but looking for " << mAvatarID << llendl;
+ }
+ mHaveInfo = true;
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ mGlobalPositionEstimate = estimated_global_pos;
+}
+
+bool LLTrackingData::haveTrackingInfo()
+{
+ LLViewerObject* object = gObjectList.findObject(mAvatarID);
+ if(object && !object->isDead())
+ {
+ mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY);
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ mHaveInfo = true;
+ return true;
+ }
+ if(mHaveCoarseInfo &&
+ !mCoarseLocationTimer.checkExpirationAndReset(COARSE_FREQUENCY))
+ {
+ // if we reach here, then we have a 'recent' coarse update
+ mUpdateTimer.setTimerExpirySec(FIND_FREQUENCY);
+ mAgentGone.setTimerExpirySec(OFFLINE_SECONDS);
+ return true;
+ }
+ if(mUpdateTimer.checkExpirationAndReset(FIND_FREQUENCY))
+ {
+ LLAvatarTracker::instance().findAgent();
+ mHaveCoarseInfo = false;
+ }
+ if(mAgentGone.checkExpirationAndReset(OFFLINE_SECONDS))
+ {
+ mHaveInfo = false;
+ mHaveCoarseInfo = false;
+ }
+ return mHaveInfo;
+}
+
+void LLTrackingData::setTrackedCoarseLocation(const LLVector3d& global_pos)
+{
+ mCoarseLocationTimer.setTimerExpirySec(COARSE_FREQUENCY);
+ mGlobalPositionEstimate = global_pos;
+ mHaveInfo = true;
+ mHaveCoarseInfo = true;
+}
+
+///----------------------------------------------------------------------------
+// various buddy functors
+///----------------------------------------------------------------------------
+
+bool LLCollectProxyBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ if(buddy->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ mProxy.insert(buddy_id);
+ }
+ return true;
+}
+
+bool LLCollectMappableBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline() && buddy->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ mMappable.insert(value);
+ }
+ return true;
+}
+
+bool LLCollectOnlineBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline())
+ {
+ mOnline.insert(value);
+ }
+ return true;
+}
+
+bool LLCollectAllBuddies::operator()(const LLUUID& buddy_id, LLRelationship* buddy)
+{
+ mFirst[0] = '\0';
+ mLast[0] = '\0';
+ gCacheName->getName(buddy_id, mFirst, mLast);
+ std::ostringstream fullname;
+ fullname << mFirst << " " << mLast;
+ buddy_map_t::value_type value(fullname.str(), buddy_id);
+ if(buddy->isOnline())
+ {
+ mOnline.insert(value);
+ }
+ else
+ {
+ mOffline.insert(value);
+ }
+ return true;
+}
+
diff --git a/indra/newview/llcallingcard.h b/indra/newview/llcallingcard.h
new file mode 100644
index 0000000000..b557de1c77
--- /dev/null
+++ b/indra/newview/llcallingcard.h
@@ -0,0 +1,224 @@
+/**
+ * @file llcallingcard.h
+ * @brief Definition of the LLPreviewCallingCard class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCALLINGCARD_H
+#define LL_LLCALLINGCARD_H
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include "lluserrelations.h"
+#include "lluuid.h"
+#include "v3dmath.h"
+
+//class LLInventoryModel;
+//class LLInventoryObserver;
+class LLMessageSystem;
+class LLTrackingData;
+class LLFriendObserver
+{
+public:
+ // This enumeration is a way to refer to what changed in a more
+ // human readable format. You can mask the value provided by
+ // chaged() to see if the observer is interested in the change.
+ enum
+ {
+ NONE = 0,
+ ADD = 1,
+ REMOVE = 2,
+ ONLINE = 4,
+ POWERS = 8,
+ ALL = 0xffffffff
+ };
+ virtual ~LLFriendObserver() {}
+ virtual void changed(U32 mask) = 0;
+};
+
+/*
+struct LLBuddyInfo
+{
+ bool mIsOnline;
+ bool mIsEmpowered;
+ LLBuddyInfo() : mIsOnline(false), mIsEmpowered(false) {}
+};
+*/
+
+// This is used as a base class for doing operations on all buddies.
+class LLRelationshipFunctor
+{
+public:
+ virtual ~LLRelationshipFunctor() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy) = 0;
+};
+
+
+class LLAvatarTracker
+{
+public:
+ static LLAvatarTracker& instance() { return sInstance; }
+
+ void track(const LLUUID& avatar_id, const std::string& name);
+ void untrack(const LLUUID& avatar_id);
+ bool isTrackedAgentValid() { return mTrackedAgentValid; }
+ void setTrackedAgentValid(bool valid) { mTrackedAgentValid = valid; }
+ void findAgent();
+
+ // coarse update information
+ void setTrackedCoarseLocation(const LLVector3d& global_pos);
+
+ // dealing with the tracked agent location
+ bool haveTrackingInfo();
+ void getDegreesAndDist(F32& rot, F64& horiz_dist, F64& vert_dist);
+ LLVector3d getGlobalPos();
+
+ // Get the name passed in, returns null string if uninitialized.
+ const LLString& getName();
+
+ // Get the avatar being tracked, returns LLUUID::null if uninitialized
+ const LLUUID& getAvatarID();
+
+ // Deal with inventory
+ //void observe(LLInventoryModel* model);
+ //void inventoryChanged();
+
+ // add or remove agents from buddy list. Each method takes a set
+ // of buddies and returns how many were actually added or removed.
+ typedef std::map<LLUUID, LLRelationship*> buddy_map_t;
+
+ S32 addBuddyList(const buddy_map_t& buddies);
+ //S32 removeBuddyList(const buddy_list_t& exes);
+ void copyBuddyList(buddy_map_t& buddies) const;
+
+ // deal with termination of friendhsip
+ void terminateBuddy(const LLUUID& id);
+
+ // get full info
+ const LLRelationship* getBuddyInfo(const LLUUID& id) const;
+
+ // online status
+ void setBuddyOnline(const LLUUID& id, bool is_online);
+ bool isBuddyOnline(const LLUUID& id) const;
+
+ // simple empowered status
+ void setBuddyEmpowered(const LLUUID& id, bool is_empowered);
+ bool isBuddyEmpowered(const LLUUID& id) const;
+
+ // set the empower bit & message the server.
+ void empowerList(const buddy_map_t& list, bool grant);
+ void empower(const LLUUID& id, bool grant); // wrapper for above
+
+ // register callbacks
+ void registerCallbacks(LLMessageSystem* msg);
+
+ // Add/remove an observer. If the observer is destroyed, be sure
+ // to remove it. On destruction of the tracker, it will delete any
+ // observers left behind.
+ void addObserver(LLFriendObserver* observer);
+ void removeObserver(LLFriendObserver* observer);
+ void notifyObservers();
+
+ // Apply the functor to every buddy. Do not actually modify the
+ // buddy list in the functor or bad things will happen.
+ void applyFunctor(LLRelationshipFunctor& f);
+
+ static void formFriendship(const LLUUID& friend_id);
+
+protected:
+ void deleteTrackingData();
+ void agentFound(const LLUUID& prey,
+ const LLVector3d& estimated_global_pos);
+
+ // Message system functionality
+ static void processAgentFound(LLMessageSystem* msg, void**);
+ static void processOnlineNotification(LLMessageSystem* msg, void**);
+ static void processOfflineNotification(LLMessageSystem* msg, void**);
+ //static void processGrantedProxies(LLMessageSystem* msg, void**);
+ static void processTerminateFriendship(LLMessageSystem* msg, void**);
+ static void processChangeUserRights(LLMessageSystem* msg, void**);
+
+ void processNotify(LLMessageSystem* msg, bool online);
+ void processChange(LLMessageSystem* msg);
+
+protected:
+ static LLAvatarTracker sInstance;
+ LLTrackingData* mTrackingData;
+ bool mTrackedAgentValid;
+ U32 mModifyMask;
+ //LLInventoryModel* mInventory;
+ //LLInventoryObserver* mInventoryObserver;
+
+ buddy_map_t mBuddyInfo;
+
+ typedef std::vector<LLFriendObserver*> observer_list_t;
+ observer_list_t mObservers;
+
+private:
+ // do not implement
+ LLAvatarTracker(const LLAvatarTracker&);
+ bool operator==(const LLAvatarTracker&);
+
+public:
+ // don't you dare create or delete this object
+ LLAvatarTracker();
+ ~LLAvatarTracker();
+};
+
+// collect set of LLUUIDs we're a proxy for
+class LLCollectProxyBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectProxyBuddies() {}
+ virtual ~LLCollectProxyBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::set<LLUUID> buddy_list_t;
+ buddy_list_t mProxy;
+};
+
+// collect dictionary sorted map of name -> agent_id for every online buddy
+class LLCollectMappableBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectMappableBuddies() {}
+ virtual ~LLCollectMappableBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mMappable;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+// collect dictionary sorted map of name -> agent_id for every online buddy
+class LLCollectOnlineBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectOnlineBuddies() {}
+ virtual ~LLCollectOnlineBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mOnline;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+// collect dictionary sorted map of name -> agent_id for every buddy,
+// one map is offline and the other map is online.
+class LLCollectAllBuddies : public LLRelationshipFunctor
+{
+public:
+ LLCollectAllBuddies() {}
+ virtual ~LLCollectAllBuddies() {}
+ virtual bool operator()(const LLUUID& buddy_id, LLRelationship* buddy);
+ typedef std::map<std::string, LLUUID, LLDictionaryLess> buddy_map_t;
+ buddy_map_t mOnline;
+ buddy_map_t mOffline;
+ char mFirst[DB_FIRST_NAME_BUF_SIZE];
+ char mLast[DB_LAST_NAME_BUF_SIZE];
+};
+
+#endif // LL_LLCALLINGCARD_H
diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp
new file mode 100644
index 0000000000..208a14a6c6
--- /dev/null
+++ b/indra/newview/llchatbar.cpp
@@ -0,0 +1,706 @@
+/**
+ * @file llchatbar.cpp
+ * @brief LLChatBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llchatbar.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llparcel.h"
+#include "llstring.h"
+#include "message.h"
+#include "llfocusmgr.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "llfloaterchat.h"
+#include "llgesturemgr.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermenu.h" // for deleting object with DEL key
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llframetimer.h"
+#include "llresmgr.h"
+#include "llworld.h"
+#include "llinventorymodel.h"
+#include "llmultigesture.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+
+
+//
+// Globals
+//
+const F32 AGENT_TYPING_TIMEOUT = 5.f; // seconds
+
+LLChatBar *gChatBar = NULL;
+
+LLChatBarGestureObserver* LLChatBar::sObserver = NULL;
+
+
+class LLChatBarGestureObserver : public LLGestureManagerObserver
+{
+public:
+ LLChatBarGestureObserver() {}
+ virtual ~LLChatBarGestureObserver() {}
+ virtual void changed() { gChatBar->refreshGestures(); }
+};
+
+
+//
+// Functions
+//
+
+LLChatBar::LLChatBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, rect, BORDER_NO),
+ mInputEditor(NULL),
+ mGestureLabelTimer(),
+ mLastSpecialChatChannel(0),
+ mIsBuilt(FALSE)
+{
+ setIsChrome(TRUE);
+
+ gUICtrlFactory->buildPanel(this,"panel_chat_bar.xml");
+
+ mIsFocusRoot = TRUE;
+
+ setRect(rect); // override xml rect
+
+ setBackgroundOpaque(TRUE);
+ setBackgroundVisible(TRUE);
+
+ // Start visible if we left the app while chatting.
+ setVisible( gSavedSettings.getBOOL("ChatVisible") );
+
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "Chat Editor");
+ if (mInputEditor)
+ {
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke);
+ mInputEditor->setFocusLostCallback(&onInputEditorFocusLost);
+ mInputEditor->setFocusReceivedCallback( &onInputEditorGainFocus );
+ mInputEditor->setCommitOnFocusLost( FALSE );
+ mInputEditor->setIgnoreTab(TRUE);
+ mInputEditor->setPassDelete(TRUE);
+ }
+
+ mInputEditor->setMaxTextLength(1023);
+ // Build the list of gestures
+ refreshGestures();
+
+ sObserver = new LLChatBarGestureObserver;
+ gGestureManager.addObserver(sObserver);
+
+ mIsBuilt = TRUE;
+
+ // Apply custom layout.
+ layout();
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ childDisplayNotFound();
+#endif
+
+}
+
+
+LLChatBar::~LLChatBar()
+{
+ delete sObserver;
+ sObserver = NULL;
+ // LLView destructor cleans up children
+}
+
+BOOL LLChatBar::postBuild()
+{
+ childSetAction("History", LLFloaterChat::toggle, this);
+ childSetAction("Say", onClickSay, this);
+ childSetAction("Shout", onClickShout, this);
+ childSetCommitCallback("Gesture", onCommitGesture, this);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------
+// Overrides
+//-----------------------------------------------------------------------
+
+// virtual
+void LLChatBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape(width, height, called_from_parent);
+ if (mIsBuilt)
+ {
+ layout();
+ }
+}
+
+// virtual
+BOOL LLChatBar::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ if( getVisible() && getEnabled() && !called_from_parent)
+ {
+ // ALT-RETURN is reserved for windowed/fullscreen toggle
+ if( KEY_RETURN == key )
+ {
+ //if (childGetValue("Chat Editor").asString().empty())
+ //{
+ // // no text, just close chat bar
+ // stopChat();
+ // return TRUE;
+ //}
+
+ if (mask == MASK_CONTROL)
+ {
+ // shout
+ sendChat(CHAT_TYPE_SHOUT);
+ handled = TRUE;
+ }
+ else if (mask == MASK_NONE)
+ {
+ // say
+ sendChat( CHAT_TYPE_NORMAL );
+ handled = TRUE;
+ }
+ }
+ else if ( KEY_ESCAPE == key )
+ {
+ stopChat();
+
+ handled = TRUE;
+ }
+ }
+ return handled;
+}
+
+
+void LLChatBar::layout()
+{
+ S32 rect_width = mRect.getWidth();
+ S32 count = 9; // number of elements in LLToolBar
+ S32 pad = 4;
+
+ LLRect gesture_rect;
+ S32 gesture_width = 0;
+ if (childGetRect("Gesture", gesture_rect))
+ {
+ gesture_width = gesture_rect.getWidth();
+ }
+ F32 segment_width = (F32)(rect_width - (pad + gesture_width)) / (F32)count;
+
+ S32 btn_width = lltrunc(segment_width-pad);
+
+ S32 x = 0;
+ S32 y = 1;
+ LLRect r;
+
+ x = llround(0 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("History", r);
+
+ x = llround(1 * segment_width);
+ // Hack this one up so it looks nice.
+ if (mInputEditor)
+ {
+ r.setOriginAndSize(x, y+2, llfloor(6*segment_width-pad), 18);
+ mInputEditor->reshape(r.getWidth(), r.getHeight(), TRUE);
+ mInputEditor->setRect(r);
+ }
+
+ x = llround(7 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("Say", r);
+
+ x = llround(8 * segment_width);
+ r.setOriginAndSize(x, y, btn_width, BTN_HEIGHT);
+ childSetRect("Shout", r);
+
+ x = rect_width - (pad + gesture_width);
+ r.setOriginAndSize(x, y, gesture_width, BTN_HEIGHT);
+ childSetRect("Gesture", r);
+}
+
+
+void LLChatBar::refresh()
+{
+ //BOOL chat_mode = gSavedSettings.getBOOL("ChatVisible");
+
+ //// Grab focus when no one else has it, and we're in chat mode.
+ //if (!gFocusMgr.getKeyboardFocus()
+ // && chat_mode)
+ //{
+ // childSetFocus("Chat Editor", TRUE);
+ //}
+
+ // Only show this view when user wants to be chatting
+ //setVisible(chat_mode);
+
+ // hide in mouselook, but keep previous visibility state
+ //BOOL mouselook = gAgent.cameraMouselook();
+ // call superclass setVisible so that we don't overwrite the saved setting
+ LLPanel::setVisible(gSavedSettings.getBOOL("ChatVisible"));
+
+ // HACK: Leave the name of the gesture in place for a few seconds.
+ const F32 SHOW_GESTURE_NAME_TIME = 2.f;
+ if (mGestureLabelTimer.getStarted() && mGestureLabelTimer.getElapsedTimeF32() > SHOW_GESTURE_NAME_TIME)
+ {
+ LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ if (gestures) gestures->selectFirstItem();
+ mGestureLabelTimer.stop();
+ }
+
+ if ((gAgent.getTypingTime() > AGENT_TYPING_TIMEOUT) && (gAgent.getRenderState() & AGENT_STATE_TYPING))
+ {
+ gAgent.stopTyping();
+ }
+
+ childSetEnabled("Say", mInputEditor->getText().size());
+ childSetEnabled("Shout", mInputEditor->getText().size());
+
+}
+
+void LLChatBar::refreshGestures()
+{
+ LLCtrlListInterface* gestures = childGetListInterface("Gesture");
+ if (gestures)
+ {
+ //store current selection so we can maintain it
+ LLString cur_gesture = childGetValue("Gesture").asString();
+ gestures->selectFirstItem();
+ LLString label = childGetValue("Gesture").asString();
+ // clear
+ gestures->clearRows();
+ // add gestures
+ LLGestureManager::item_map_t::iterator it;
+ for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+ if (gesture)
+ {
+ if (!gesture->mTrigger.empty())
+ {
+ gestures->addSimpleElement(gesture->mTrigger);
+ }
+ }
+ }
+ gestures->sortByColumn(0, TRUE);
+ // Insert label after sorting
+ gestures->addSimpleElement(label, ADD_TOP);
+
+ if (!cur_gesture.empty())
+ {
+ gestures->selectByValue(LLSD(cur_gesture));
+ }
+ else
+ {
+ gestures->selectFirstItem();
+ }
+ }
+}
+
+// Move the cursor to the correct input field.
+void LLChatBar::setKeyboardFocus(BOOL focus)
+{
+ if (focus)
+ {
+ if (mInputEditor)
+ {
+ mInputEditor->setFocus(TRUE);
+ mInputEditor->selectAll();
+ }
+ }
+ else if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ if (mInputEditor)
+ {
+ mInputEditor->deselect();
+ }
+ setFocus(FALSE);
+ }
+}
+
+
+// Ignore arrow keys in chat bar
+void LLChatBar::setIgnoreArrowKeys(BOOL b)
+{
+ if (mInputEditor)
+ {
+ mInputEditor->setIgnoreArrowKeys(b);
+ }
+}
+
+BOOL LLChatBar::inputEditorHasFocus()
+{
+ return mInputEditor && mInputEditor->hasFocus();
+}
+
+LLString LLChatBar::getCurrentChat()
+{
+ return mInputEditor ? mInputEditor->getText() : LLString::null;
+}
+
+//-----------------------------------------------------------------------
+// Internal functions
+//-----------------------------------------------------------------------
+
+// If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
+// Otherwise returns input and channel 0.
+LLWString LLChatBar::stripChannelNumber(const LLWString &mesg, S32* channel)
+{
+ if (mesg[0] == '/'
+ && mesg[1] == '/')
+ {
+ // This is a "repeat channel send"
+ *channel = mLastSpecialChatChannel;
+ return mesg.substr(2, mesg.length() - 2);
+ }
+ else if (mesg[0] == '/'
+ && mesg[1]
+ && isdigit(mesg[1]))
+ {
+ // This a special "/20" speak on a channel
+ S32 pos = 0;
+
+ // Copy the channel number into a string
+ llwchar channel_string[64];
+ llwchar c;
+ do
+ {
+ c = mesg[pos+1];
+ channel_string[pos] = c;
+ pos++;
+ }
+ while(c && pos < 64 && isdigit(c));
+
+ // Move the pointer forward to the first non-whitespace char
+ // Check isspace before looping, so we can handle "/33foo"
+ // as well as "/33 foo"
+ while(c && iswspace(c))
+ {
+ c = mesg[pos+1];
+ pos++;
+ }
+
+
+ mLastSpecialChatChannel = strtol(wstring_to_utf8str(channel_string).c_str(), NULL, 10);
+ *channel = mLastSpecialChatChannel;
+ return mesg.substr(pos, mesg.length() - pos);
+ }
+ else
+ {
+ // This is normal chat.
+ *channel = 0;
+ return mesg;
+ }
+}
+
+
+void LLChatBar::sendChat( EChatType type )
+{
+ LLWString text;
+ if (mInputEditor) text = mInputEditor->getWText();
+ LLWString::trim(text);
+
+ if (!text.empty())
+ {
+ // Check if this is destined for another channel
+ S32 channel = 0;
+ stripChannelNumber(text, &channel);
+
+ std::string utf8text = wstring_to_utf8str(text);
+ // Try to trigger a gesture, if not chat to a script.
+ std::string utf8_revised_text;
+ if (0 == channel)
+ {
+ // discard returned "found" boolean
+ gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text);
+ }
+ else
+ {
+ utf8_revised_text = utf8text;
+ }
+
+ utf8_revised_text = utf8str_trim(utf8_revised_text);
+
+ if (!utf8_revised_text.empty())
+ {
+ // Chat with animation
+ sendChatFromViewer(utf8_revised_text, type, TRUE);
+ }
+ }
+ childSetValue("Chat Editor", LLSD(LLString::null));
+
+ gAgent.stopTyping();
+
+ // If the user wants to stop chatting on hitting return, lose focus
+ // and go out of chat mode.
+ if (gSavedSettings.getBOOL("CloseChatOnReturn"))
+ {
+ stopChat();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// Static functions
+//-----------------------------------------------------------------------
+
+// static
+void LLChatBar::startChat(void* userdata)
+{
+ const char* line = (const char*)userdata;
+
+ gChatBar->setVisible(TRUE);
+ gChatBar->setKeyboardFocus(TRUE);
+ gSavedSettings.setBOOL("ChatVisible", TRUE);
+
+ if (line && gChatBar->mInputEditor)
+ {
+ std::string line_string(line);
+ gChatBar->mInputEditor->setText(line_string);
+ }
+ // always move cursor to end so users don't obliterate chat when accidentally hitting WASD
+ gChatBar->mInputEditor->setCursorToEnd();
+}
+
+
+// Exit "chat mode" and do the appropriate focus changes
+// static
+void LLChatBar::stopChat()
+{
+ // In simple UI mode, we never release focus from the chat bar
+ gChatBar->setKeyboardFocus(FALSE);
+
+ // If we typed a movement key and pressed return during the
+ // same frame, the keyboard handlers will see the key as having
+ // gone down this frame and try to move the avatar.
+ gKeyboard->resetKeys();
+ gKeyboard->resetMaskKeys();
+
+ // stop typing animation
+ gAgent.stopTyping();
+
+ // hide chat bar so it doesn't grab focus back
+ gChatBar->setVisible(FALSE);
+}
+
+void LLChatBar::setVisible(BOOL visible)
+{
+ gSavedSettings.setBOOL("ChatVisible", visible);
+ LLPanel::setVisible(visible);
+}
+
+// static
+void LLChatBar::onInputEditorKeystroke( LLLineEditor* caller, void* userdata )
+{
+ LLChatBar* self = (LLChatBar *)userdata;
+
+ LLWString raw_text;
+ if (self->mInputEditor) raw_text = self->mInputEditor->getWText();
+
+ // Can't trim the end, because that will cause autocompletion
+ // to eat trailing spaces that might be part of a gesture.
+ LLWString::trimHead(raw_text);
+
+ S32 length = raw_text.length();
+
+ if( (length > 0) && (raw_text[0] != '/') ) // forward slash is used for escape (eg. emote) sequences
+ {
+ gAgent.startTyping();
+ }
+ else
+ {
+ gAgent.stopTyping();
+ }
+
+ /* Doesn't work -- can't tell the difference between a backspace
+ that killed the selection vs. backspace at the end of line.
+ if (length > 1
+ && text[0] == '/'
+ && key == KEY_BACKSPACE)
+ {
+ // the selection will already be deleted, but we need to trim
+ // off the character before
+ LLString new_text = raw_text.substr(0, length-1);
+ self->mInputEditor->setText( new_text );
+ self->mInputEditor->setCursorToEnd();
+ length = length - 1;
+ }
+ */
+
+ KEY key = gKeyboard->currentKey();
+
+ // Ignore "special" keys, like backspace, arrows, etc.
+ if (length > 1
+ && raw_text[0] == '/'
+ && key < KEY_SPECIAL)
+ {
+ // we're starting a gesture, attempt to autocomplete
+
+ std::string utf8_trigger = wstring_to_utf8str(raw_text);
+ std::string utf8_out_str(utf8_trigger);
+
+ if (gGestureManager.matchPrefix(utf8_trigger, &utf8_out_str))
+ {
+ if (self->mInputEditor)
+ {
+ self->mInputEditor->setText(utf8_out_str);
+ S32 outlength = self->mInputEditor->getLength(); // in characters
+
+ // Select to end of line, starting from the character
+ // after the last one the user typed.
+ self->mInputEditor->setSelection(length, outlength);
+ }
+ }
+
+ //llinfos << "GESTUREDEBUG " << trigger
+ // << " len " << length
+ // << " outlen " << out_str.getLength()
+ // << llendl;
+ }
+ // make sure we don't do UI-only render as it is apparent avatar isn't animating
+ gViewerWindow->finishFastFrame();
+}
+
+// static
+void LLChatBar::onInputEditorFocusLost( LLLineEditor* caller, void* userdata)
+{
+ // stop typing animation
+ gAgent.stopTyping();
+}
+
+// static
+void LLChatBar::onInputEditorGainFocus( LLUICtrl* caller, void* userdata )
+{
+ LLFloaterChat::setHistoryCursorAndScrollToEnd();
+}
+
+// static
+void LLChatBar::onClickSay( void* userdata )
+{
+ LLChatBar* self = (LLChatBar*) userdata;
+ self->sendChat( CHAT_TYPE_NORMAL );
+}
+
+// static
+void LLChatBar::onClickShout( void* userdata )
+{
+ LLChatBar *self = (LLChatBar *)userdata;
+ self->sendChat( CHAT_TYPE_SHOUT );
+}
+
+void LLChatBar::sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate)
+{
+ sendChatFromViewer(utf8str_to_wstring(utf8text), type, animate);
+}
+
+void LLChatBar::sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Look for "/20 foo" channel chats.
+ S32 channel = 0;
+ LLWString out_text = stripChannelNumber(wtext, &channel);
+ std::string utf8_out_text = wstring_to_utf8str(out_text);
+ std::string utf8_text = wstring_to_utf8str(wtext);
+
+ utf8_text = utf8str_trim(utf8_text);
+ if (!utf8_text.empty())
+ {
+ utf8_text = utf8str_truncate(utf8_text, MAX_STRING - 1);
+ }
+
+ // Don't animate for chats people can't hear (chat to scripts)
+ if (animate && (channel == 0))
+ {
+ if (type == CHAT_TYPE_WHISPER)
+ {
+ lldebugs << "You whisper " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_WHISPER, ANIM_REQUEST_START);
+ }
+ else if (type == CHAT_TYPE_NORMAL)
+ {
+ lldebugs << "You say " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_TALK, ANIM_REQUEST_START);
+ }
+ else if (type == CHAT_TYPE_SHOUT)
+ {
+ lldebugs << "You shout " << utf8_text << llendl;
+ gAgent.sendAnimationRequest(ANIM_AGENT_SHOUT, ANIM_REQUEST_START);
+ }
+ else
+ {
+ llinfos << "send_chat_from_viewer() - invalid volume" << llendl;
+ return;
+ }
+ }
+ else
+ {
+ if (type != CHAT_TYPE_START && type != CHAT_TYPE_STOP)
+ {
+ lldebugs << "Channel chat: " << utf8_text << llendl;
+ }
+ }
+
+ msg->newMessageFast(_PREHASH_ChatFromViewer);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ChatData);
+ msg->addStringFast(_PREHASH_Message, utf8_out_text);
+ msg->addU8Fast(_PREHASH_Type, type);
+ msg->addS32("Channel", channel);
+
+ gAgent.sendReliableMessage();
+
+ gViewerStats->incStat(LLViewerStats::ST_CHAT_COUNT);
+}
+
+
+// static
+void LLChatBar::onCommitGesture(LLUICtrl* ctrl, void* data)
+{
+ LLChatBar* self = (LLChatBar*)data;
+ LLCtrlListInterface* gestures = self->childGetListInterface("Gesture");
+ if (gestures)
+ {
+ S32 index = gestures->getFirstSelectedIndex();
+ if (index == 0)
+ {
+ return;
+ }
+ const std::string& trigger = gestures->getSimpleSelectedValue().asString();
+
+ // pretend the user chatted the trigger string, to invoke
+ // substitution and logging.
+ std::string text(trigger);
+ std::string revised_text;
+ gGestureManager.triggerAndReviseString(text, &revised_text);
+
+ revised_text = utf8str_trim(revised_text);
+ if (!revised_text.empty())
+ {
+ // Don't play nodding animation
+ self->sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE);
+ }
+ }
+ self->mGestureLabelTimer.start();
+ // free focus back to chat bar
+ self->childSetFocus("Gesture", FALSE);
+}
diff --git a/indra/newview/llchatbar.h b/indra/newview/llchatbar.h
new file mode 100644
index 0000000000..e0f31ae124
--- /dev/null
+++ b/indra/newview/llchatbar.h
@@ -0,0 +1,98 @@
+/**
+ * @file llchatbar.h
+ * @brief LLChatBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCHATBAR_H
+#define LL_LLCHATBAR_H
+
+#include "llpanel.h"
+#include "llframetimer.h"
+#include "llchat.h"
+
+class LLButton;
+class LLComboBox;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+class LLChatBarGestureObserver;
+
+class LLChatBar
+: public LLPanel
+{
+public:
+ LLChatBar(const std::string& name, const LLRect& rect );
+ ~LLChatBar();
+ virtual BOOL postBuild();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+ // Adjust buttons and input field for width
+ void layout();
+
+ void refresh();
+ void refreshGestures();
+
+ // Move cursor into chat input field.
+ void setKeyboardFocus(BOOL b);
+
+ // Ignore arrow keys for chat bar
+ void setIgnoreArrowKeys(BOOL b);
+
+ BOOL inputEditorHasFocus();
+ LLString getCurrentChat();
+
+ // Send a chat (after stripping /20foo channel chats).
+ // "Animate" means the nodding animation for regular text.
+ void sendChatFromViewer(const LLWString &wtext, EChatType type, BOOL animate);
+ void sendChatFromViewer(const std::string &utf8text, EChatType type, BOOL animate);
+
+ // If input of the form "/20foo" or "/20 foo", returns "foo" and channel 20.
+ // Otherwise returns input and channel 0.
+ LLWString stripChannelNumber(const LLWString &mesg, S32* channel);
+
+ // callbacks
+ static void onClickHistory( void* userdata );
+ static void onClickSay( void* userdata );
+ static void onClickShout( void* userdata );
+
+ static void onTabClick( void* userdata );
+ static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onInputEditorFocusLost(LLLineEditor* caller,void* userdata);
+ static void onInputEditorGainFocus(LLUICtrl* caller,void* userdata);
+
+ static void onCommitGesture(LLUICtrl* ctrl, void* data);
+
+ static void startChat(void*);
+ static void stopChat();
+
+ /*virtual*/ void setVisible(BOOL visible);
+protected:
+ void sendChat(EChatType type);
+ void updateChat();
+
+protected:
+ LLLineEditor* mInputEditor;
+
+ LLFrameTimer mGestureLabelTimer;
+
+ // Which non-zero channel did we last chat on?
+ S32 mLastSpecialChatChannel;
+
+ BOOL mIsBuilt;
+
+ static LLChatBarGestureObserver* sObserver;
+};
+
+extern LLChatBar *gChatBar;
+
+#endif
diff --git a/indra/newview/llclassifiedinfo.cpp b/indra/newview/llclassifiedinfo.cpp
new file mode 100644
index 0000000000..768145b63c
--- /dev/null
+++ b/indra/newview/llclassifiedinfo.cpp
@@ -0,0 +1,49 @@
+/**
+ * @file llclassifiedinfo.cpp
+ * @brief LLClassifiedInfo class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llclassifiedinfo.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "lluuid.h"
+
+LLClassifiedInfo::cat_map LLClassifiedInfo::sCategories;
+
+// static
+void LLClassifiedInfo::loadCategories(LLUserAuth::options_t classified_options)
+{
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = classified_options.begin();
+ resp_it != classified_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLUserAuth::response_t::const_iterator option_it;
+
+ S32 cat_id = 0;
+ option_it = response.find("category_id");
+ if (option_it != response.end())
+ {
+ cat_id = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ continue;
+ }
+
+ // Add the category id/name pair
+ option_it = response.find("category_name");
+ if (option_it != response.end())
+ {
+ LLClassifiedInfo::sCategories[cat_id] = option_it->second;
+ }
+
+ }
+
+}
diff --git a/indra/newview/llclassifiedinfo.h b/indra/newview/llclassifiedinfo.h
new file mode 100644
index 0000000000..5c58048db0
--- /dev/null
+++ b/indra/newview/llclassifiedinfo.h
@@ -0,0 +1,31 @@
+/**
+ * @file llclassifiedinfo.h
+ * @brief LLClassifiedInfo class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLASSIFIEDINFO_H
+#define LL_LLCLASSIFIEDINFO_H
+
+#include <map>
+
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "lluserauth.h"
+
+class LLMessageSystem;
+
+class LLClassifiedInfo
+{
+public:
+ LLClassifiedInfo() {}
+
+ static void loadCategories(LLUserAuth::options_t event_options);
+
+ typedef std::map<U32, std::string> cat_map;
+ static cat_map sCategories;
+};
+
+#endif // LL_LLCLASSIFIEDINFO_H
diff --git a/indra/newview/llcloud.cpp b/indra/newview/llcloud.cpp
new file mode 100644
index 0000000000..c7e69a043f
--- /dev/null
+++ b/indra/newview/llcloud.cpp
@@ -0,0 +1,555 @@
+/**
+ * @file llcloud.cpp
+ * @brief Implementation of viewer LLCloudLayer class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "v4color.h"
+
+#include "llwind.h"
+#include "llcloud.h"
+#include "llgl.h"
+#include "llviewerobjectlist.h"
+#include "llvoclouds.h"
+#include "llvosky.h"
+#include "llsky.h"
+#include "llviewerregion.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "llglheaders.h"
+#include "pipeline.h"
+#include "lldrawpool.h"
+#include "llworld.h"
+
+extern LLPipeline gPipeline;
+
+const F32 CLOUD_UPDATE_RATE = 1.0f; // Global time dilation for clouds
+const F32 CLOUD_GROW_RATE = 0.05f;
+const F32 CLOUD_DECAY_RATE = -0.05f;
+const F32 CLOUD_VELOCITY_SCALE = 0.01f;
+const F32 CLOUD_DENSITY = 25.f;
+const S32 CLOUD_COUNT_MAX = 20;
+const F32 CLOUD_HEIGHT_RANGE = 48.f;
+const F32 CLOUD_HEIGHT_MEAN = 192.f;
+
+enum
+{
+ LL_PUFF_GROWING = 0,
+ LL_PUFF_DYING = 1
+};
+
+// Used for patch decoder
+S32 gBuffer[16*16];
+
+
+//static
+S32 LLCloudPuff::sPuffCount = 0;
+
+LLCloudPuff::LLCloudPuff() :
+ mAlpha(0.01f),
+ mRate(CLOUD_GROW_RATE*CLOUD_UPDATE_RATE),
+ mLifeState(LL_PUFF_GROWING)
+{
+}
+
+LLCloudGroup::LLCloudGroup() :
+ mCloudLayerp(NULL),
+ mDensity(0.f),
+ mTargetPuffCount(0),
+ mVOCloudsp(NULL)
+{
+}
+
+void LLCloudGroup::cleanup()
+{
+ if (mVOCloudsp)
+ {
+ if (!mVOCloudsp->isDead())
+ {
+ gObjectList.killObject(mVOCloudsp);
+ }
+ mVOCloudsp = NULL;
+ }
+}
+
+void LLCloudGroup::setCenterRegion(const LLVector3 &center)
+{
+ mCenterRegion = center;
+}
+
+void LLCloudGroup::updatePuffs(const F32 dt)
+{
+ mDensity = mCloudLayerp->getDensityRegion(mCenterRegion);
+
+ if (!mVOCloudsp)
+ {
+ mVOCloudsp = (LLVOClouds *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_CLOUDS, mCloudLayerp->getRegion());
+ mVOCloudsp->setCloudGroup(this);
+ mVOCloudsp->setPositionRegion(mCenterRegion);
+ mVOCloudsp->setScale(LLVector3(256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
+ 256.f/CLOUD_GROUPS_PER_EDGE + CLOUD_PUFF_WIDTH,
+ CLOUD_HEIGHT_RANGE + CLOUD_PUFF_HEIGHT));
+ gPipeline.addObject(mVOCloudsp);
+ }
+
+ S32 i;
+
+ LLVector3 velocity;
+ LLVector3d vel_d;
+ // Update the positions of all of the clouds
+ for (i = 0; i < mCloudPuffs.count(); i++)
+ {
+ LLCloudPuff &puff = mCloudPuffs[i];
+ velocity = mCloudLayerp->getRegion()->mWind.getCloudVelocity(mCloudLayerp->getRegion()->getPosRegionFromGlobal(puff.mPositionGlobal));
+ velocity *= CLOUD_VELOCITY_SCALE*CLOUD_UPDATE_RATE;
+ vel_d.setVec(velocity);
+ mCloudPuffs[i].mPositionGlobal += vel_d;
+ mCloudPuffs[i].mAlpha += mCloudPuffs[i].mRate * dt;
+ mCloudPuffs[i].mAlpha = llmin(1.f, mCloudPuffs[i].mAlpha);
+ mCloudPuffs[i].mAlpha = llmax(0.f, mCloudPuffs[i].mAlpha);
+ }
+}
+
+void LLCloudGroup::updatePuffOwnership()
+{
+ S32 i = 0;
+ while (i < mCloudPuffs.count())
+ {
+ if (mCloudPuffs[i].getLifeState() == LL_PUFF_DYING)
+ {
+ i++;
+ continue;
+ }
+ if (inGroup(mCloudPuffs[i]))
+ {
+ i++;
+ continue;
+ }
+
+ //llinfos << "Cloud moving to new group" << llendl;
+ LLCloudGroup *new_cgp = gWorldPointer->findCloudGroup(mCloudPuffs[i]);
+ if (!new_cgp)
+ {
+ //llinfos << "Killing puff not in group" << llendl;
+ mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
+ mCloudPuffs[i].mRate = CLOUD_DECAY_RATE*CLOUD_UPDATE_RATE;
+ i++;
+ continue;
+ }
+ //llinfos << "Puff handed off!" << llendl;
+ LLCloudPuff *puffp = new_cgp->mCloudPuffs.reserve_block(1);
+ puffp->mPositionGlobal = mCloudPuffs[i].mPositionGlobal;
+ puffp->mAlpha = mCloudPuffs[i].mAlpha;
+ mCloudPuffs.remove(i);
+ }
+
+ //llinfos << "Puff count: " << LLCloudPuff::sPuffCount << llendl;
+}
+
+void LLCloudGroup::updatePuffCount()
+{
+ if (!mVOCloudsp)
+ {
+ return;
+ }
+ S32 i;
+ S32 target_puff_count = 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 = mCloudPuffs.count();
+ // Create a new cloud if we need one
+ if (current_puff_count < target_puff_count)
+ {
+ LLVector3d puff_pos_global;
+ mCloudPuffs.resize(target_puff_count);
+ for (i = current_puff_count; i < target_puff_count; i++)
+ {
+ puff_pos_global = mVOCloudsp->getPositionGlobal();
+ F32 x = frand(256.f/CLOUD_GROUPS_PER_EDGE) - 128.f/CLOUD_GROUPS_PER_EDGE;
+ F32 y = frand(256.f/CLOUD_GROUPS_PER_EDGE) - 128.f/CLOUD_GROUPS_PER_EDGE;
+ F32 z = frand(CLOUD_HEIGHT_RANGE) - 0.5f*CLOUD_HEIGHT_RANGE;
+ puff_pos_global += LLVector3d(x, y, z);
+ mCloudPuffs[i].mPositionGlobal = puff_pos_global;
+ mCloudPuffs[i].mAlpha = 0.01f;
+ LLCloudPuff::sPuffCount++;
+ }
+ }
+
+ // Count the number of live puffs
+ S32 live_puff_count = 0;
+ for (i = 0; i < mCloudPuffs.count(); i++)
+ {
+ if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
+ {
+ live_puff_count++;
+ }
+ }
+
+
+ // Start killing enough puffs so the live puff count == target puff count
+ S32 new_dying_count = llmax(0, live_puff_count - target_puff_count);
+ i = 0;
+ while (new_dying_count > 0)
+ {
+ if (mCloudPuffs[i].getLifeState() != LL_PUFF_DYING)
+ {
+ //llinfos << "Killing extra live cloud" << llendl;
+ mCloudPuffs[i].setLifeState(LL_PUFF_DYING);
+ mCloudPuffs[i].mRate = CLOUD_DECAY_RATE*CLOUD_UPDATE_RATE;
+ new_dying_count--;
+ }
+ i++;
+ }
+
+ // Remove fully dead puffs
+ i = 0;
+ while (i < mCloudPuffs.count())
+ {
+ if (mCloudPuffs[i].isDead())
+ {
+ //llinfos << "Removing dead puff!" << llendl;
+ mCloudPuffs.remove(i);
+ LLCloudPuff::sPuffCount--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+BOOL LLCloudGroup::inGroup(const LLCloudPuff &puff) const
+{
+ // Do min/max check on center of the cloud puff
+ F32 min_x, min_y, max_x, max_y;
+ F32 delta = 128.f/CLOUD_GROUPS_PER_EDGE;
+ min_x = mCenterRegion.mV[VX] - delta;
+ min_y = mCenterRegion.mV[VY] - delta;
+ max_x = mCenterRegion.mV[VX] + delta;
+ max_y = mCenterRegion.mV[VY] + delta;
+
+ LLVector3 pos_region = mCloudLayerp->getRegion()->getPosRegionFromGlobal(puff.getPositionGlobal());
+
+ if ((pos_region.mV[VX] < min_x)
+ || (pos_region.mV[VY] < min_y)
+ || (pos_region.mV[VX] > max_x)
+ || (pos_region.mV[VY] > max_y))
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+LLCloudLayer::LLCloudLayer()
+: mOriginGlobal(0.0f, 0.0f, 0.0f),
+ mMetersPerEdge(1.0f),
+ mMetersPerGrid(1.0f),
+ mWindp(NULL),
+ mDensityp(NULL)
+{
+ S32 i, j;
+ for (i = 0; i < 4; i++)
+ {
+ mNeighbors[i] = NULL;
+ }
+
+ F32 x, y;
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ y = (0.5f + i)*(256.f/CLOUD_GROUPS_PER_EDGE);
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ x = (0.5f + j)*(256.f/CLOUD_GROUPS_PER_EDGE);
+
+ mCloudGroups[i][j].setCloudLayerp(this);
+ mCloudGroups[i][j].setCenterRegion(LLVector3(x, y, CLOUD_HEIGHT_MEAN));
+ }
+ }
+}
+
+
+
+LLCloudLayer::~LLCloudLayer()
+{
+ destroy();
+}
+
+
+void LLCloudLayer::create(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+
+ mRegionp = regionp;
+ mDensityp = new F32 [CLOUD_GRIDS_PER_EDGE * CLOUD_GRIDS_PER_EDGE];
+
+ U32 i;
+ for (i = 0; i < CLOUD_GRIDS_PER_EDGE*CLOUD_GRIDS_PER_EDGE; i++)
+ {
+ mDensityp[i] = 0.f;
+ }
+}
+
+void LLCloudLayer::setRegion(LLViewerRegion *regionp)
+{
+ mRegionp = regionp;
+}
+
+void LLCloudLayer::destroy()
+{
+ // Kill all of the existing puffs
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].cleanup();
+ }
+ }
+
+ delete [] mDensityp;
+ mDensityp = NULL;
+ mWindp = NULL;
+}
+
+
+void LLCloudLayer::reset()
+{
+}
+
+
+void LLCloudLayer::setWindPointer(LLWind *windp)
+{
+ if (mWindp)
+ {
+ mWindp->setCloudDensityPointer(NULL);
+ }
+ mWindp = windp;
+ if (mWindp)
+ {
+ mWindp->setCloudDensityPointer(mDensityp);
+ }
+}
+
+
+void LLCloudLayer::setWidth(F32 width)
+{
+ mMetersPerEdge = width;
+ mMetersPerGrid = width / CLOUD_GRIDS_PER_EDGE;
+}
+
+
+F32 LLCloudLayer::getDensityRegion(const LLVector3 &pos_region)
+{
+ // "position" is region-local
+ S32 i, j, ii, jj;
+
+ i = lltrunc(pos_region.mV[VX] / mMetersPerGrid);
+ j = lltrunc(pos_region.mV[VY] / mMetersPerGrid);
+ ii = i + 1;
+ jj = j + 1;
+
+
+ // clamp
+ if (i >= (S32)CLOUD_GRIDS_PER_EDGE)
+ {
+ i = CLOUD_GRIDS_PER_EDGE - 1;
+ ii = i;
+ }
+ else if (i < 0)
+ {
+ i = 0;
+ ii = i;
+ }
+ else if (ii >= (S32)CLOUD_GRIDS_PER_EDGE || ii < 0)
+ {
+ ii = i;
+ }
+
+ if (j >= (S32)CLOUD_GRIDS_PER_EDGE)
+ {
+ j = CLOUD_GRIDS_PER_EDGE - 1;
+ jj = j;
+ }
+ else if (j < 0)
+ {
+ j = 0;
+ jj = j;
+ }
+ else if (jj >= (S32)CLOUD_GRIDS_PER_EDGE || jj < 0)
+ {
+ jj = j;
+ }
+
+ F32 dx = (pos_region.mV[VX] - (F32) i * mMetersPerGrid) / mMetersPerGrid;
+ F32 dy = (pos_region.mV[VY] - (F32) j * mMetersPerGrid) / mMetersPerGrid;
+ F32 omdx = 1.0f - dx;
+ F32 omdy = 1.0f - dy;
+
+ F32 density = dx * dy * *(mDensityp + ii + jj * CLOUD_GRIDS_PER_EDGE) +
+ dx * omdy * *(mDensityp + i + jj * CLOUD_GRIDS_PER_EDGE) +
+ omdx * dy * *(mDensityp + ii + j * CLOUD_GRIDS_PER_EDGE) +
+ omdx * omdy * *(mDensityp + i + j * CLOUD_GRIDS_PER_EDGE);
+
+ return density;
+}
+
+// a debug method that may yet be useful
+void LLCloudLayer::renderDensityField()
+{
+// F32 x, y, z;
+// U32 i, j, k;
+// LLGLSNoTexture gls_ui_no_texture;
+// // Render a bunch of triangles to represent the cloud density field
+// glBegin(GL_TRIANGLES);
+// for(j=0; j<CLOUD_GRIDS_PER_EDGE-1; j++)
+// {
+// y = j * mMetersPerGrid;
+
+// for(i=0; i<CLOUD_GRIDS_PER_EDGE; i++)
+// {
+// k = i + j*CLOUD_GRIDS_PER_EDGE;
+// x = i * mMetersPerGrid;
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k + CLOUD_GRIDS_PER_EDGE);
+// glColor3f(1.0f - *(mDensityp + k + CLOUD_GRIDS_PER_EDGE), *(mDensityp + k + CLOUD_GRIDS_PER_EDGE), 0.0f);
+// glVertex3f(x, y+mMetersPerGrid, z);
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k);
+// glColor3f(1.0f - *(mDensityp + k), *(mDensityp + k), 0.0f);
+// glVertex3f(x, y, z);
+
+// z = 0.5f * CLOUD_MAX_HEIGHT + 40.0f * *(mDensityp + k + 1);
+// glColor3f(1.0f - *(mDensityp + k + 1), *(mDensityp + k + 1), 0.0f);
+// glVertex3f(x+mMetersPerGrid, y, z);
+
+// }
+// }
+// glEnd();
+}
+
+
+void LLCloudLayer::decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp)
+{
+ LLPatchHeader patch_header;
+
+ init_patch_decompressor(group_headerp->patch_size);
+
+ // Don't use the packed group_header stride because the strides used on
+ // simulator and viewer are not equal.
+ group_headerp->stride = group_headerp->patch_size; // offset required to step up one row
+ set_group_of_patch_header(group_headerp);
+
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, gBuffer);
+ decompress_patch(mDensityp, gBuffer, &patch_header);
+}
+
+void LLCloudLayer::updatePuffs(const F32 dt)
+{
+ // We want to iterate through all of the cloud groups
+ // and update their density targets
+
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffs(dt);
+ }
+ }
+}
+
+void LLCloudLayer::updatePuffOwnership()
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffOwnership();
+ }
+ }
+}
+
+void LLCloudLayer::updatePuffCount()
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ mCloudGroups[i][j].updatePuffCount();
+ }
+ }
+}
+
+LLCloudGroup *LLCloudLayer::findCloudGroup(const LLCloudPuff &puff)
+{
+ S32 i, j;
+
+ for (i = 0; i < CLOUD_GROUPS_PER_EDGE; i++)
+ {
+ for (j = 0; j < CLOUD_GROUPS_PER_EDGE; j++)
+ {
+ if (mCloudGroups[i][j].inGroup(puff))
+ {
+ return &(mCloudGroups[i][j]);
+ }
+ }
+ }
+ return NULL;
+}
+
+
+
+void LLCloudLayer::connectNeighbor(LLCloudLayer *cloudp, U32 direction)
+{
+ if (direction >= 4)
+ {
+ // Only care about cardinal 4 directions.
+ return;
+ }
+
+ mNeighbors[direction] = cloudp;
+ if (cloudp)
+ mNeighbors[direction]->mNeighbors[gDirOpposite[direction]] = this;
+}
+
+
+void LLCloudLayer::disconnectNeighbor(U32 direction)
+{
+ if (direction >= 4)
+ {
+ // Only care about cardinal 4 directions.
+ return;
+ }
+
+ if (mNeighbors[direction])
+ {
+ mNeighbors[direction]->mNeighbors[gDirOpposite[direction]] = NULL;
+ mNeighbors[direction] = NULL;
+ }
+}
+
+
+void LLCloudLayer::disconnectAllNeighbors()
+{
+ S32 i;
+ for (i = 0; i < 4; i++)
+ {
+ disconnectNeighbor(i);
+ }
+}
diff --git a/indra/newview/llcloud.h b/indra/newview/llcloud.h
new file mode 100644
index 0000000000..06c2b5c9ff
--- /dev/null
+++ b/indra/newview/llcloud.h
@@ -0,0 +1,182 @@
+/**
+ * @file llcloud.h
+ * @brief Description of viewer LLCloudLayer class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCLOUD_H
+#define LL_LLCLOUD_H
+
+// Some ideas on how clouds should work
+//
+// Each region has a cloud layer
+// Each cloud layer has pre-allocated space for N clouds
+// The LLSky class knows the max number of clouds to render M.
+// All clouds use the same texture, but the tex-coords can take on 8 configurations
+// (four rotations, front and back)
+//
+// The sky's part
+// --------------
+// The sky knows that A clouds have been assigned to regions and there are B left over.
+// Divide B by number of active regions to get C.
+// Ask each region to add C more clouds and return total number D.
+// Add up all the D's to get a new A.
+//
+// The cloud layer's part
+// ----------------------
+// The cloud layer is a grid of possibility. Each grid's value represents the probablility
+// (0.0 to 1.0) that a cloud placement query will succeed.
+//
+// The sky asks the region to add C more clouds.
+// The cloud layer tries a total of E times to place clouds and returns total cloud count.
+//
+// Clouds move according to local wind velocity.
+// If a cloud moves out of region then it's location is sent to neighbor region
+// or it is allowed to drift and decay.
+//
+// The clouds in non-visible regions do not propagate every frame.
+// Each frame one non-visible region is allowed to propagate it's clouds
+// (might have to check to see if incoming cloud was already visible or not).
+//
+//
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "llmemory.h"
+#include "lldarray.h"
+
+#include "llframetimer.h"
+
+const U32 CLOUD_GRIDS_PER_EDGE = 16;
+
+const F32 CLOUD_PUFF_WIDTH = 64.f;
+const F32 CLOUD_PUFF_HEIGHT = 48.f;
+
+class LLWind;
+class LLVOClouds;
+class LLViewerRegion;
+class LLCloudLayer;
+class LLBitPack;
+class LLGroupHeader;
+
+const S32 CLOUD_GROUPS_PER_EDGE = 4;
+
+class LLCloudPuff
+{
+public:
+ LLCloudPuff();
+
+ const LLVector3d &getPositionGlobal() const { return mPositionGlobal; }
+ friend class LLCloudGroup;
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+
+ F32 getAlpha() const { return mAlpha; }
+ U32 getLifeState() const { return mLifeState; }
+ void setLifeState(const U32 state) { mLifeState = state; }
+ BOOL isDead() const { return mAlpha <= 0.f; }
+
+
+ static S32 sPuffCount;
+protected:
+ F32 mAlpha;
+ F32 mRate;
+ LLVector3d mPositionGlobal;
+
+ BOOL mLifeState;
+};
+
+class LLCloudGroup
+{
+public:
+ LLCloudGroup();
+
+ void cleanup();
+
+ void setCloudLayerp(LLCloudLayer *clp) { mCloudLayerp = clp; }
+ void setCenterRegion(const LLVector3 &center);
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+ void updatePuffCount();
+
+ BOOL inGroup(const LLCloudPuff &puff) const;
+
+ F32 getDensity() const { return mDensity; }
+ S32 getNumPuffs() const { return mCloudPuffs.count(); }
+ const LLCloudPuff &getPuff(const S32 i) { return mCloudPuffs[i]; }
+protected:
+ LLCloudLayer *mCloudLayerp;
+ LLVector3 mCenterRegion;
+ F32 mDensity;
+ S32 mTargetPuffCount;
+
+ LLDynamicArray<LLCloudPuff> mCloudPuffs;
+ LLPointer<LLVOClouds> mVOCloudsp;
+};
+
+
+class LLCloudLayer
+{
+public:
+ LLCloudLayer();
+ ~LLCloudLayer();
+
+ void create(LLViewerRegion *regionp);
+ void destroy();
+
+ void reset(); // Clears all active cloud puffs
+
+
+ void updatePuffs(const F32 dt);
+ void updatePuffOwnership();
+ void updatePuffCount();
+
+ LLCloudGroup *findCloudGroup(const LLCloudPuff &puff);
+
+ void setRegion(LLViewerRegion *regionp);
+ LLViewerRegion* getRegion() const { return mRegionp; }
+ void setWindPointer(LLWind *windp);
+ void setOriginGlobal(const LLVector3d &origin_global) { mOriginGlobal = origin_global; }
+ void setWidth(F32 width);
+
+ void setBrightness(F32 brightness);
+ void setSunColor(const LLColor4 &color);
+
+ F32 getDensityRegion(const LLVector3 &pos_region); // "position" is in local coordinates
+
+ void renderDensityField();
+ void decompress(LLBitPack &bitpack, LLGroupHeader *group_header);
+
+ LLCloudLayer* getNeighbor(const S32 n) const { return mNeighbors[n]; }
+
+ void connectNeighbor(LLCloudLayer *cloudp, U32 direction);
+ void disconnectNeighbor(U32 direction);
+ void disconnectAllNeighbors();
+
+public:
+ LLVector3d mOriginGlobal;
+ F32 mMetersPerEdge;
+ F32 mMetersPerGrid;
+
+
+ F32 mMaxAlpha; // The max cloud puff _render_ alpha
+
+protected:
+ LLCloudLayer *mNeighbors[4];
+ LLWind *mWindp;
+ LLViewerRegion *mRegionp;
+ F32 *mDensityp; // the probability density grid
+
+ LLCloudGroup mCloudGroups[CLOUD_GROUPS_PER_EDGE][CLOUD_GROUPS_PER_EDGE];
+};
+
+
+#endif
diff --git a/indra/newview/llcolorswatch.cpp b/indra/newview/llcolorswatch.cpp
new file mode 100644
index 0000000000..4dade7f683
--- /dev/null
+++ b/indra/newview/llcolorswatch.cpp
@@ -0,0 +1,386 @@
+/**
+ * @file llcolorswatch.cpp
+ * @brief LLColorSwatch class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File include
+#include "llcolorswatch.h"
+
+// Linden library includes
+#include "v4color.h"
+
+// Project includes
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+#include "llfloatercolorpicker.h"
+#include "llviewborder.h"
+#include "llviewerimagelist.h"
+#include "llfocusmgr.h"
+
+LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const LLColor4& color,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* userdata )
+: LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mValid( TRUE ),
+ mColor( color ),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mCanApplyImmediately(FALSE),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL)
+{
+ mCaption = new LLTextBox( name,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ LLString::null,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ // Scalable UI made this off-by-one, I don't know why. JC
+ LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
+ MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
+}
+
+LLColorSwatchCtrl::LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLColor4& color,
+ void (*commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* userdata )
+: LLUICtrl(name, rect, TRUE, commit_callback, userdata, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mValid( TRUE ),
+ mColor( color ),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mCanApplyImmediately(FALSE),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL)
+{
+ mCaption = new LLTextBox( label,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ LLString::null,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ // Scalable UI made this off-by-one, I don't know why. JC
+ LLRect border_rect(0, mRect.getHeight()-1, mRect.getWidth()-1, 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ mAlphaGradientImage = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("color_swatch_alpha.tga")),
+ MIPMAP_FALSE, TRUE, GL_ALPHA8, GL_ALPHA);
+}
+
+LLColorSwatchCtrl::~LLColorSwatchCtrl ()
+{
+ // parent dialog is destroyed so we are too and we need to cancel selection
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->cancelSelection();
+ pickerp->close();
+ }
+ mAlphaGradientImage = NULL;
+}
+
+BOOL LLColorSwatchCtrl::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return handleMouseDown(x, y, mask);
+}
+
+BOOL LLColorSwatchCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+}
+
+BOOL LLColorSwatchCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
+ {
+ showPicker(TRUE);
+ }
+ return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
+}
+
+// forces color of this swatch and any associated floater to the input value, if currently invalid
+void LLColorSwatchCtrl::setOriginal(const LLColor4& color)
+{
+ mColor = color;
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->setOrigRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
+ }
+}
+
+void LLColorSwatchCtrl::set(const LLColor4& color, BOOL update_picker, BOOL from_event)
+{
+ mColor = color;
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp && update_picker)
+ {
+ pickerp->setCurRgb(mColor.mV[VRED], mColor.mV[VGREEN], mColor.mV[VBLUE]);
+ }
+ if (!from_event)
+ {
+ setControlValue(mColor.getValue());
+ }
+}
+
+void LLColorSwatchCtrl::setLabel(const std::string& label)
+{
+ mCaption->setText(label);
+}
+
+BOOL LLColorSwatchCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ // No handler is needed for capture lost since this object has no state that depends on it.
+ gViewerWindow->setMouseCapture( this, NULL );
+
+ return TRUE;
+}
+
+
+BOOL LLColorSwatchCtrl::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // We only handle the click if the click both started and ended within us
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ // Release the mouse
+ gViewerWindow->setMouseCapture( NULL, NULL );
+
+ // If mouseup in the widget, it's been clicked
+ if ( pointInView(x, y) )
+ {
+ llassert(mEnabled);
+ llassert(getVisible());
+
+ showPicker(FALSE);
+ }
+ }
+
+ return TRUE;
+}
+
+
+// assumes GL state is set for 2D
+void LLColorSwatchCtrl::draw()
+{
+ if( getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+ // Draw border
+ LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
+ gl_rect_2d( border, mBorderColor, FALSE );
+
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ // Check state
+ if ( mValid )
+ {
+ LLGLSTexture gls_texture;
+
+ // Draw the color swatch
+ gl_rect_2d_checkerboard( interior );
+ gl_rect_2d(interior, mColor, TRUE);
+ LLColor4 opaque_color = mColor;
+ opaque_color.mV[VALPHA] = 1.f;
+ glColor4fv(opaque_color.mV);
+ if (mAlphaGradientImage.notNull())
+ {
+ glPushMatrix();
+ {
+ glTranslatef((F32)interior.mLeft, (F32)interior.mBottom, 0.f);
+ LLViewerImage::bindTexture(mAlphaGradientImage);
+ gl_rect_2d_simple_tex(interior.getWidth(), interior.getHeight());
+ }
+ glPopMatrix();
+ }
+ }
+ else
+ {
+ // Draw grey and an X
+ gl_rect_2d(interior, LLColor4::grey, TRUE);
+
+ gl_draw_x(interior, LLColor4::black);
+ }
+
+ LLUICtrl::draw();
+ }
+}
+
+void LLColorSwatchCtrl::setEnabled( BOOL enabled )
+{
+ mCaption->setEnabled( enabled );
+ LLUICtrl::setEnabled( enabled );
+
+ if (!enabled)
+ {
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->cancelSelection();
+ pickerp->close();
+ }
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// called when parent filters down a visibility changed message
+void LLColorSwatchCtrl::onVisibilityChange ( BOOL curVisibilityIn )
+{
+ // visibility changed - moved away to different tab for instance - cancel selection
+ //if ( ! curVisibilityIn)
+ //{
+ // LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ // if (pickerp)
+ // {
+ // pickerp->cancelSelection();
+ // }
+ //}
+}
+
+void LLColorSwatchCtrl::setValue(const LLSD& value)
+{
+ set(LLColor4(value), TRUE, TRUE);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called (infrequently) when the color changes so the subject of the swatch can be updated.
+void LLColorSwatchCtrl::onColorChanged ( void* data, EColorPickOp pick_op )
+{
+ LLColorSwatchCtrl* subject = ( LLColorSwatchCtrl* )data;
+ if ( subject )
+ {
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(subject->mPickerHandle);
+ if (pickerp)
+ {
+ // move color across from selector to internal widget storage
+ LLColor4 updatedColor ( pickerp->getCurR (),
+ pickerp->getCurG (),
+ pickerp->getCurB (),
+ subject->mColor.mV[VALPHA] ); // keep current alpha
+ subject->mColor = updatedColor;
+ subject->setControlValue(updatedColor.getValue());
+
+ if (pick_op == COLOR_CANCEL && subject->mOnCancelCallback)
+ {
+ subject->mOnCancelCallback(subject, subject->mCallbackUserData);
+ }
+ else if (pick_op == COLOR_SELECT && subject->mOnSelectCallback)
+ {
+ subject->mOnSelectCallback(subject, subject->mCallbackUserData);
+ }
+ else
+ {
+ // just commit change
+ subject->onCommit ();
+ }
+ }
+ };
+}
+
+void LLColorSwatchCtrl::setValid(BOOL valid )
+{
+ mValid = valid;
+
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (pickerp)
+ {
+ pickerp->setActive(valid);
+ }
+}
+
+void LLColorSwatchCtrl::showPicker(BOOL take_focus)
+{
+ LLFloaterColorPicker* pickerp = (LLFloaterColorPicker*)LLFloater::getFloaterByHandle(mPickerHandle);
+ if (!pickerp)
+ {
+ pickerp = new LLFloaterColorPicker(this, mCanApplyImmediately);
+ gFloaterView->getParentFloater(this)->addDependentFloater(pickerp);
+ mPickerHandle = pickerp->getHandle();
+ }
+
+ // initialize picker singleton with current color
+ pickerp->initUI ( mColor.mV [ VRED ], mColor.mV [ VGREEN ], mColor.mV [ VBLUE ] );
+
+ // display it
+ pickerp->showUI ();
+
+ if (take_focus)
+ {
+ pickerp->setFocus(TRUE);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLColorSwatchCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("color", TRUE)->setFloatValue(4, mColor.mV);
+
+ node->createChild("border_color", TRUE)->setFloatValue(4, mBorderColor.mV);
+
+ if (mCaption)
+ {
+ node->createChild("label", TRUE)->setStringValue(mCaption->getText());
+ }
+
+ node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately);
+
+ return node;
+}
+
+LLView* LLColorSwatchCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("colorswatch");
+ node->getAttributeString("name", name);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLColor4 color(1.f, 1.f, 1.f, 1.f);
+ node->getAttributeColor("initial_color", color);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL can_apply_immediately = FALSE;
+ node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);
+
+ LLUICtrlCallback callback = NULL;
+
+ if (label.empty())
+ {
+ label.assign(node->getValue());
+ }
+
+ LLColorSwatchCtrl* color_swatch = new LLColorSwatchCtrl(
+ name,
+ rect,
+ label,
+ color,
+ callback,
+ NULL );
+
+ color_swatch->setCanApplyImmediately(can_apply_immediately);
+ color_swatch->initFromXML(node, parent);
+
+ return color_swatch;
+}
diff --git a/indra/newview/llcolorswatch.h b/indra/newview/llcolorswatch.h
new file mode 100644
index 0000000000..020f700dfb
--- /dev/null
+++ b/indra/newview/llcolorswatch.h
@@ -0,0 +1,88 @@
+/**
+ * @file llcolorswatch.h
+ * @brief LLColorSwatch class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOLORSWATCH_H
+#define LL_LLCOLORSWATCH_H
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "llfloater.h"
+
+//
+// Classes
+//
+class LLColor4;
+class LLTextBox;
+class LLFloaterColorPicker;
+class LLViewerImage;
+
+class LLColorSwatchCtrl
+: public LLUICtrl
+{
+public:
+ typedef enum e_color_pick_op
+ {
+ COLOR_CHANGE,
+ COLOR_SELECT,
+ COLOR_CANCEL
+ } EColorPickOp;
+
+ LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const LLColor4& color,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata);
+ LLColorSwatchCtrl(const std::string& name, const LLRect& rect, const std::string& label, const LLColor4& color,
+ void (*on_commit_callback)(LLUICtrl* ctrl, void* userdata),
+ void* callback_userdata);
+
+ ~LLColorSwatchCtrl ();
+
+ virtual void setValue(const LLSD& value);
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_COLOR_SWATCH; }
+ virtual LLString getWidgetTag() const { return LL_COLOR_SWATCH_CTRL_TAG; }
+ virtual LLSD getValue() const { return mColor.getValue(); }
+ const LLColor4& get() { return mColor; }
+
+ void set(const LLColor4& color, BOOL update_picker = FALSE, BOOL from_event = FALSE);
+ void setOriginal(const LLColor4& color);
+ void setValid(BOOL valid);
+ void setLabel(const std::string& label);
+ void setCanApplyImmediately(BOOL apply) { mCanApplyImmediately = apply; }
+ void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; }
+ void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; }
+
+ void showPicker(BOOL take_focus);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x,S32 y,MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ virtual void draw();
+ virtual void setEnabled( BOOL enabled );
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual void onVisibilityChange ( BOOL curVisibilityIn );
+
+ static void onColorChanged ( void* data, EColorPickOp pick_op = COLOR_CHANGE );
+
+protected:
+ BOOL mValid;
+ LLColor4 mColor;
+ LLColor4 mBorderColor;
+ LLTextBox* mCaption;
+ LLViewHandle mPickerHandle;
+ LLViewBorder* mBorder;
+ BOOL mCanApplyImmediately;
+ LLUICtrlCallback mOnCancelCallback;
+ LLUICtrlCallback mOnSelectCallback;
+
+ LLPointer<LLViewerImage> mAlphaGradientImage;
+};
+
+#endif // LL_LLBUTTON_H
diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp
new file mode 100644
index 0000000000..3d9ecd7c51
--- /dev/null
+++ b/indra/newview/llcompilequeue.cpp
@@ -0,0 +1,756 @@
+/**
+ * @file llcompilequeue.cpp
+ * @brief LLCompileQueueData class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * Implementation of the script queue which keeps an array of object
+ * UUIDs and manipulates all of the scripts on each of them.
+ *
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcompilequeue.h"
+
+#include "llagent.h"
+#include "llchat.h"
+#include "llviewerwindow.h"
+#include "llcallbacklist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "lscript_rt_interface.h"
+#include "llviewercontrol.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llresmgr.h"
+#include "llbutton.h"
+#include "lldir.h"
+#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llvieweruictrlfactory.h"
+
+// XUI:translate
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+// XUI:translate
+const char* COMPILE_QUEUE_TITLE = "Recompilation Progress";
+const char* COMPILE_START_STRING = "recompile";
+const char* RESET_QUEUE_TITLE = "Reset Progress";
+const char* RESET_START_STRING = "reset";
+const char* RUN_QUEUE_TITLE = "Set Running Progress";
+const char* RUN_START_STRING = "set running";
+const char* NOT_RUN_QUEUE_TITLE = "Set Not Running Progress";
+const char* NOT_RUN_START_STRING = "set not running";
+
+struct LLCompileQueueData
+{
+ LLUUID mQueueID;
+ LLUUID mOldAssetID;
+ LLCompileQueueData(const LLUUID& q_id, const LLUUID& old_asset_id) :
+ mQueueID(q_id), mOldAssetID(old_asset_id) {}
+};
+
+struct LLScriptQueueData
+{
+ LLUUID mQueueID;
+ LLString mScriptName;
+ LLScriptQueueData(const LLUUID& q_id, const char* name) :
+ mQueueID(q_id), mScriptName(name) {}
+};
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterScriptQueue
+///----------------------------------------------------------------------------
+
+// static
+LLMap<LLUUID, LLFloaterScriptQueue*> LLFloaterScriptQueue::sInstances;
+
+
+// Default constructor
+LLFloaterScriptQueue::LLFloaterScriptQueue(const std::string& name,
+ const LLRect& rect,
+ const char* title,
+ const char* start_string) :
+ LLFloater(name, rect, title,
+ RESIZE_YES, DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT,
+ DRAG_ON_TOP, MINIMIZE_YES, CLOSE_YES)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_script_queue.xml");
+
+ childSetAction("close",onCloseBtn,this);
+ childSetEnabled("close",FALSE);
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ mStartString = start_string;
+ mDone = FALSE;
+ sInstances.addData(mID, this);
+}
+
+// Destroys the object
+LLFloaterScriptQueue::~LLFloaterScriptQueue()
+{
+ sInstances.removeData(mID);
+}
+
+// find an instance by ID. Return NULL if it does not exist.
+// static
+LLFloaterScriptQueue* LLFloaterScriptQueue::findInstance(const LLUUID& id)
+{
+ if(sInstances.checkData(id))
+ {
+ return sInstances.getData(id);
+ }
+ return NULL;
+}
+
+
+// This is the callback method for the viewer object currently being
+// worked on.
+// NOT static, virtual!
+void LLFloaterScriptQueue::inventoryChanged(LLViewerObject* viewer_object,
+ InventoryObjectList* inv,
+ S32,
+ void* q_id)
+{
+ llinfos << "LLFloaterScriptQueue::inventoryChanged() for object "
+ << viewer_object->getID() << llendl;
+
+ //Remove this listener from the object since its
+ //listener callback is now being executed.
+
+ //We remove the listener here because the function
+ //removeVOInventoryListener removes the listener from a ViewerObject
+ //which it internally stores.
+
+ //If we call this further down in the function, calls to handleInventory
+ //and nextObject may update the interally stored viewer object causing
+ //the removal of the incorrect listener from an incorrect object.
+
+ //Fixes SL-6119:Recompile scripts fails to complete
+ removeVOInventoryListener();
+
+ if (viewer_object && inv && (viewer_object->getID() == mCurrentObjectID) )
+ {
+ handleInventory(viewer_object, inv);
+ }
+ else
+ {
+ // something went wrong...
+ // note that we're not working on this one, and move onto the
+ // next object in the list.
+ llwarns << "No inventory for " << mCurrentObjectID
+ << llendl;
+ nextObject();
+ }
+}
+
+
+// static
+void LLFloaterScriptQueue::onCloseBtn(void* user_data)
+{
+ LLFloaterScriptQueue* self = (LLFloaterScriptQueue*)user_data;
+ self->close();
+}
+
+void LLFloaterScriptQueue::addObject(const LLUUID& id)
+{
+ mObjectIDs.put(id);
+}
+
+BOOL LLFloaterScriptQueue::start()
+{
+ //llinfos << "LLFloaterCompileQueue::start()" << llendl;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Starting %s of %d items.", mStartString, mObjectIDs.count());
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ list->addSimpleItem(buffer);
+
+ return nextObject();
+}
+
+BOOL LLFloaterScriptQueue::isDone() const
+{
+ return (mCurrentObjectID.isNull() && (mObjectIDs.count() == 0));
+}
+
+// go to the next object. If no objects left, it falls out silently
+// and waits to be killed by the window being closed.
+BOOL LLFloaterScriptQueue::nextObject()
+{
+ S32 count;
+ BOOL successful_start = FALSE;
+ do
+ {
+ count = mObjectIDs.count();
+ llinfos << "LLFloaterScriptQueue::nextObject() - " << count
+ << " objects left to process." << llendl;
+ mCurrentObjectID.setNull();
+ if(count > 0)
+ {
+ successful_start = popNext();
+ }
+ llinfos << "LLFloaterScriptQueue::nextObject() "
+ << (successful_start ? "successful" : "unsuccessful")
+ << llendl;
+ } while((mObjectIDs.count() > 0) && !successful_start);
+ if(isDone() && !mDone)
+ {
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+
+ mDone = TRUE;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Done.");
+ list->addSimpleItem(buffer);
+ childSetEnabled("close",TRUE);
+ }
+ return successful_start;
+}
+
+// returns true if the queue has started, otherwise false. This
+// method pops the top object off of the queue.
+BOOL LLFloaterScriptQueue::popNext()
+{
+ // get the first element off of the container, and attempt to get
+ // the inventory.
+ BOOL rv = FALSE;
+ S32 count = mObjectIDs.count();
+ if(mCurrentObjectID.isNull() && (count > 0))
+ {
+ mCurrentObjectID = mObjectIDs.get(0);
+ llinfos << "LLFloaterScriptQueue::popNext() - mCurrentID: "
+ << mCurrentObjectID << llendl;
+ mObjectIDs.remove(0);
+ LLViewerObject* obj = gObjectList.findObject(mCurrentObjectID);
+ if(obj)
+ {
+ llinfos << "LLFloaterScriptQueue::popNext() requesting inv for "
+ << mCurrentObjectID << llendl;
+ LLUUID* id = new LLUUID(mID);
+ registerVOInventoryListener(obj,id);
+ requestVOInventory();
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterCompileQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterCompileQueue* LLFloaterCompileQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterCompileQueue* new_queue = new LLFloaterCompileQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterCompileQueue::LLFloaterCompileQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, COMPILE_QUEUE_TITLE, COMPILE_START_STRING)
+{ }
+
+LLFloaterCompileQueue::~LLFloaterCompileQueue()
+{
+}
+
+void LLFloaterCompileQueue::handleInventory(LLViewerObject *viewer_object,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+
+ typedef std::map<LLUUID, LLPointer<LLInventoryItem> > uuid_item_map;
+ uuid_item_map asset_item_map;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ // Check permissions before allowing the user to retrieve data.
+ if (item->getPermissions().allowModifyBy(gAgent.getID()) &&
+ item->getPermissions().allowCopyBy(gAgent.getID()) )
+ {
+ LLPointer<LLViewerInventoryItem> script = new LLViewerInventoryItem(item);
+ mCurrentScripts.put(script);
+
+ if (!asset_item_map.count(item->getAssetUUID()))
+ {
+ // No entry, put in an entry for this supposedly permissive script
+ asset_item_map[item->getAssetUUID()] = item;
+ }
+ }
+ }
+ }
+
+ if (asset_item_map.empty())
+ {
+ // There are no scripts in this object. move on.
+ nextObject();
+ }
+ else
+ {
+ // request all of the assets.
+ uuid_item_map::iterator iter;
+ for(iter = asset_item_map.begin(); iter != asset_item_map.end(); iter++)
+ {
+ LLInventoryItem *itemp = iter->second;
+ LLScriptQueueData* datap = new LLScriptQueueData(getID(), itemp->getName().c_str());
+
+ //llinfos << "ITEM NAME 2: " << names.get(i) << llendl;
+ gAssetStorage->getInvItemAsset(viewer_object->getRegion()->getHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ itemp->getPermissions().getOwner(),
+ viewer_object->getID(),
+ itemp->getUUID(),
+ itemp->getAssetUUID(),
+ itemp->getType(),
+ LLFloaterCompileQueue::scriptArrived,
+ (void*)datap);
+ }
+ }
+}
+
+
+// This is the callback for when each script arrives
+// static
+void LLFloaterCompileQueue::scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLFloaterCompileQueue::scriptArrived()" << llendl;
+ LLScriptQueueData* data = (LLScriptQueueData*)user_data;
+ if(!data) return;
+ LLFloaterCompileQueue* queue = static_cast<LLFloaterCompileQueue*>
+ (LLFloaterScriptQueue::findInstance(data->mQueueID));
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ if(queue && (0 == status))
+ {
+ //llinfos << "ITEM NAME 3: " << data->mScriptName << llendl;
+
+ // Dump this into a file on the local disk so we can compile it.
+ char filename[LL_MAX_PATH] = "";
+ LLVFile file(vfs, asset_id, type);
+ char uuid_str[UUID_STR_LENGTH];
+ asset_id.toString(uuid_str);
+ sprintf(filename,"%s.%s",gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str).c_str(),LLAssetType::lookup(type));
+
+ FILE *fp = LLFile::fopen(filename, "wb");
+ if (fp)
+ {
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while (file.read(copy_buf, buf_size))
+ {
+ if (fwrite(copy_buf, file.getLastBytesRead(), 1, fp) < 1)
+ {
+ // return a bad file error if we can't write the whole thing
+ status = LL_ERR_CANNOT_OPEN_FILE;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ // It's now in the file, now compile it.
+ sprintf(buffer, "Downloaded, now compiling '%s'.", data->mScriptName.c_str());
+ queue->compile(filename, asset_id);
+
+ // Delete it after we're done compiling?
+ LLFile::remove(filename);
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
+ {
+ LLChat chat("Script not found on server.");
+ LLFloaterChat::addChat(chat);
+ sprintf(buffer, "Problem downloading %s.",
+ data->mScriptName.c_str());
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLChat chat("Insufficient permissions to download a script.");
+ LLFloaterChat::addChat(chat);
+ sprintf(buffer, "Insufficient permissions for '%s'.",
+ data->mScriptName.c_str());
+ }
+ else
+ {
+ sprintf(buffer, "Unknown failure to download %s.",
+ data->mScriptName.c_str());
+ }
+
+ llwarns << "Problem downloading script asset." << llendl;
+ if(queue) queue->removeItemByAssetID(asset_id);
+ }
+ if(queue)
+ {
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(queue, "queue output");
+ list->addSimpleItem(buffer);
+ }
+ delete data;
+}
+
+// static
+void LLFloaterCompileQueue::onSaveTextComplete(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ llinfos << "LLFloaterCompileQueue::onSaveTextComplete()" << llendl;
+ if (status)
+ {
+ llwarns << "Unable to save text for script." << llendl;
+ LLString::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveText", args);
+ }
+}
+
+// static
+void LLFloaterCompileQueue::onSaveBytecodeComplete(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ llinfos << "LLFloaterCompileQueue::onSaveBytecodeComplete()" << llendl;
+ LLCompileQueueData* data = (LLCompileQueueData*)user_data;
+ LLFloaterCompileQueue* queue = static_cast<LLFloaterCompileQueue*>
+ (LLFloaterScriptQueue::findInstance(data->mQueueID));
+ if(queue && (0 == status) && data)
+ {
+ queue->updateAssetID(data->mOldAssetID, asset_id);
+ queue->saveItemByAssetID(asset_id);
+ queue->removeItemByAssetID(asset_id);
+ }
+ else
+ {
+ llwarns << "Unable to save bytecode for script." << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveBytecode", args);
+ }
+ delete data;
+ data = NULL;
+}
+
+// compile the file given and save it out.
+void LLFloaterCompileQueue::compile(const char* filename,
+ const LLUUID& asset_id)
+{
+ LLUUID new_asset_id;
+ LLTransactionID tid;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ char uuid_string[UUID_STR_LENGTH];
+ new_asset_id.toString(uuid_string);
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+
+ gAssetStorage->storeAssetData(filename, tid,
+ LLAssetType::AT_LSL_TEXT,
+ &onSaveTextComplete, NULL, FALSE);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ llwarns << "compile failed" << llendl;
+ removeItemByAssetID(asset_id);
+ }
+ else
+ {
+ llinfos << "compile successful." << llendl;
+ // Save the bytecode
+ LLCompileQueueData* data = new LLCompileQueueData(mID, asset_id);
+ gAssetStorage->storeAssetData(dst_filename, tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &onSaveBytecodeComplete,
+ (void*)data, FALSE);
+ }
+}
+
+void LLFloaterCompileQueue::removeItemByAssetID(const LLUUID& asset_id)
+{
+ llinfos << "LLFloaterCompileQueue::removeItemByAssetID()" << llendl;
+ for(S32 i = 0; i < mCurrentScripts.count(); )
+ {
+ if(asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ mCurrentScripts.remove(i);
+ }
+ else
+ {
+ ++i;
+ }
+ }
+ if(mCurrentScripts.count() == 0)
+ {
+ nextObject();
+ }
+}
+
+void LLFloaterCompileQueue::saveItemByAssetID(const LLUUID& asset_id)
+{
+ llinfos << "LLFloaterCompileQueue::saveItemByAssetID()" << llendl;
+ LLViewerObject* viewer_object = gObjectList.findObject(mCurrentObjectID);
+ if(viewer_object)
+ {
+ S32 count = mCurrentScripts.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ // *FIX: this auto-resets active to TRUE. That might
+ // be a bad idea.
+ viewer_object->saveScript(mCurrentScripts.get(i), TRUE, false);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Unable to finish save!" << llendl;
+ }
+}
+
+// find old_asst_id, and set the asset id to new_asset_id
+void LLFloaterCompileQueue::updateAssetID(const LLUUID& old_asset_id,
+ const LLUUID& new_asset_id)
+{
+ S32 count = mCurrentScripts.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(old_asset_id == mCurrentScripts.get(i)->getAssetUUID())
+ {
+ mCurrentScripts.get(i)->setAssetUUID(new_asset_id);
+ }
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterResetQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterResetQueue* LLFloaterResetQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterResetQueue* new_queue = new LLFloaterResetQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterResetQueue::LLFloaterResetQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, RESET_QUEUE_TITLE, RESET_START_STRING)
+{ }
+
+LLFloaterResetQueue::~LLFloaterResetQueue()
+{
+}
+
+void LLFloaterResetQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Resetting '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ScriptReset);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterRunQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterRunQueue* LLFloaterRunQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterRunQueue* new_queue = new LLFloaterRunQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterRunQueue::LLFloaterRunQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, RUN_QUEUE_TITLE, RUN_START_STRING)
+{ }
+
+LLFloaterRunQueue::~LLFloaterRunQueue()
+{
+}
+
+void LLFloaterRunQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Running '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->addBOOLFast(_PREHASH_Running, TRUE);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterNotRunQueue
+///----------------------------------------------------------------------------
+
+// static
+LLFloaterNotRunQueue* LLFloaterNotRunQueue::create()
+{
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("CompileOutputRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLFloaterNotRunQueue* new_queue = new LLFloaterNotRunQueue("queue",
+ rect);
+ new_queue->open();
+ return new_queue;
+}
+
+LLFloaterNotRunQueue::LLFloaterNotRunQueue(const std::string& name, const LLRect& rect)
+: LLFloaterScriptQueue(name, rect, NOT_RUN_QUEUE_TITLE, NOT_RUN_START_STRING)
+{ }
+
+LLFloaterNotRunQueue::~LLFloaterNotRunQueue()
+{
+}
+
+void LLFloaterNotRunQueue::handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv)
+{
+ // find all of the lsl, leaving off duplicates. We'll remove
+ // all matching asset uuids on compilation success.
+ LLDynamicArray<const char*> names;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getType() == LLAssetType::AT_LSL_TEXT)
+ {
+ LLViewerObject* object = gObjectList.findObject(viewer_obj->getID());
+
+ if (object)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "queue output");
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Not running '%s'.", item->getName().c_str());
+ list->addSimpleItem(buffer);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, viewer_obj->getID());
+ msg->addUUIDFast(_PREHASH_ItemID, (*it)->getUUID());
+ msg->addBOOLFast(_PREHASH_Running, FALSE);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ }
+ }
+
+ nextObject();
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llcompilequeue.h b/indra/newview/llcompilequeue.h
new file mode 100644
index 0000000000..fc0a872907
--- /dev/null
+++ b/indra/newview/llcompilequeue.h
@@ -0,0 +1,211 @@
+/**
+ * @file llcompilequeue.h
+ * @brief LLCompileQueue class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCOMPILEQUEUE_H
+#define LL_LLCOMPILEQUEUE_H
+
+#include "lldarray.h"
+#include "llinventory.h"
+#include "llviewerobject.h"
+#include "llvoinventorylistener.h"
+#include "llmap.h"
+#include "lluuid.h"
+
+#include "llfloater.h"
+#include "llscrolllistctrl.h"
+
+#include "llviewerinventory.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterScriptQueue
+//
+// This class provides a mechanism of adding objects to a list that
+// will go through and execute action for the scripts on each object. The
+// objects will be accessed serially and the scripts may be
+// manipulated in parallel. For example, selecting two objects each
+// with three scripts will result in the first object having all three
+// scripts manipulated.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterScriptQueue : public LLFloater, public LLVOInventoryListener
+{
+public:
+ // addObject() accepts an object id.
+ void addObject(const LLUUID& id);
+
+ // start() returns TRUE if the queue has started, otherwise FALSE.
+ BOOL start();
+
+protected:
+ LLFloaterScriptQueue(const std::string& name, const LLRect& rect,
+ const char* title, const char* start_string);
+ virtual ~LLFloaterScriptQueue();
+
+ // This is the callback method for the viewer object currently
+ // being worked on.
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* queue);
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv) = 0;
+
+ static void onCloseBtn(void* user_data);
+
+ // returns true if this is done
+ BOOL isDone() const;
+
+ // go to the next object. If no objects left, it falls out
+ // silently and waits to be killed by the deleteIfDone() callback.
+ BOOL nextObject();
+ BOOL popNext();
+
+ // Get this instances ID.
+ const LLUUID& getID() const { return mID; }
+
+ // find an instance by ID. Return NULL if it does not exist.
+ static LLFloaterScriptQueue* findInstance(const LLUUID& id);
+
+protected:
+ // UI
+ LLScrollListCtrl* mMessages;
+ LLButton* mCloseBtn;
+
+ // Object Queue
+ LLDynamicArray<LLUUID> mObjectIDs;
+ LLUUID mCurrentObjectID;
+ BOOL mDone;
+
+ LLUUID mID;
+ static LLMap<LLUUID, LLFloaterScriptQueue*> sInstances;
+
+ const char* mStartString;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterCompileQueue
+//
+// This script queue will recompile each script.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterCompileQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a compile queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterCompileQueue* create();
+
+protected:
+ LLFloaterCompileQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterCompileQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+ // This is the callback for when each script arrives
+ static void scriptArrived(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ static void onSaveTextComplete(const LLUUID& asset_id, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_id,
+ void* user_data,
+ S32 status);
+
+ // compile the file given and save it out.
+ void compile(const char* filename, const LLUUID& asset_id);
+
+ // remove any object in mScriptScripts with the matching uuid.
+ void removeItemByAssetID(const LLUUID& asset_id);
+
+ // save the items indicatd by the asset id.
+ void saveItemByAssetID(const LLUUID& asset_id);
+
+ // find old_asst_id, and set the asset id to new_asset_id
+ void updateAssetID(const LLUUID& old_asset_id, const LLUUID& new_asset_id);
+
+protected:
+ LLViewerInventoryItem::item_array_t mCurrentScripts;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterResetQueue
+//
+// This script queue will reset each script.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterResetQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a reset queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterResetQueue* create();
+
+protected:
+ LLFloaterResetQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterResetQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterRunQueue
+//
+// This script queue will set each script as running.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterRunQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a run queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterRunQueue* create();
+
+protected:
+ LLFloaterRunQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterRunQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterNotRunQueue
+//
+// This script queue will set each script as not running.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterNotRunQueue : public LLFloaterScriptQueue
+{
+public:
+ // Use this method to create a not run queue. Once created, it
+ // will be responsible for it's own destruction.
+ static LLFloaterNotRunQueue* create();
+
+protected:
+ LLFloaterNotRunQueue(const std::string& name, const LLRect& rect);
+ virtual ~LLFloaterNotRunQueue();
+
+ // This is called by inventoryChanged
+ virtual void handleInventory(LLViewerObject* viewer_obj,
+ InventoryObjectList* inv);
+
+protected:
+};
+
+#endif // LL_LLCOMPILEQUEUE_H
diff --git a/indra/newview/llconfirmationmanager.cpp b/indra/newview/llconfirmationmanager.cpp
new file mode 100644
index 0000000000..e47fcde225
--- /dev/null
+++ b/indra/newview/llconfirmationmanager.cpp
@@ -0,0 +1,100 @@
+/**
+ * @file llconfirmationmanager.cpp
+ * @brief LLConfirmationManager class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llconfirmationmanager.h"
+
+#include "lluictrlfactory.h"
+
+// viewer includes
+#include "llviewerwindow.h"
+#include "lllineeditor.h"
+#include "llstring.h"
+
+LLConfirmationManager::ListenerBase::~ListenerBase()
+{
+}
+
+
+static void onConfirmAlert(S32 option, void* data)
+{
+ LLConfirmationManager::ListenerBase* listener
+ = (LLConfirmationManager::ListenerBase*)data;
+
+ if (option == 0)
+ {
+ listener->confirmed("");
+ }
+
+ delete listener;
+}
+
+static void onConfirmAlertPassword(
+ S32 option, const LLString& text, void* data)
+{
+ LLConfirmationManager::ListenerBase* listener
+ = (LLConfirmationManager::ListenerBase*)data;
+
+ if (option == 0)
+ {
+ listener->confirmed(text);
+ }
+
+ delete listener;
+}
+
+
+void LLConfirmationManager::confirm(Type type,
+ const std::string& action,
+ ListenerBase* listener)
+{
+ LLString::format_map_t args;
+ args["[ACTION]"] = action;
+
+ switch (type)
+ {
+ case TYPE_CLICK:
+ gViewerWindow->alertXml("ConfirmPurchase", args,
+ onConfirmAlert, listener);
+ break;
+
+ case TYPE_PASSWORD:
+ gViewerWindow->alertXmlEditText("ConfirmPurchasePassword", args,
+ NULL, NULL,
+ onConfirmAlertPassword, listener,
+ LLString::format_map_t(),
+ TRUE);
+ break;
+ case TYPE_NONE:
+ default:
+ listener->confirmed("");
+ break;
+ }
+}
+
+
+void LLConfirmationManager::confirm(
+ const std::string& type,
+ const std::string& action,
+ ListenerBase* listener)
+{
+ Type decodedType = TYPE_NONE;
+
+ if (type == "click")
+ {
+ decodedType = TYPE_CLICK;
+ }
+ else if (type == "password")
+ {
+ decodedType = TYPE_PASSWORD;
+ }
+
+ confirm(decodedType, action, listener);
+}
+
diff --git a/indra/newview/llconfirmationmanager.h b/indra/newview/llconfirmationmanager.h
new file mode 100644
index 0000000000..3aa33774e5
--- /dev/null
+++ b/indra/newview/llconfirmationmanager.h
@@ -0,0 +1,67 @@
+/**
+ * @file llconfirmationmanager.h
+ * @brief LLConfirmationManager class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCONFIRMATIONMANAGER_H
+#define LL_LLCONFIRMATIONMANAGER_H
+
+class LLConfirmationManager
+{
+public:
+ class ListenerBase
+ {
+ public:
+ virtual ~ListenerBase();
+ virtual void confirmed(const std::string& password) = 0;
+ };
+
+ enum Type { TYPE_NONE, TYPE_CLICK, TYPE_PASSWORD };
+
+ static void confirm(Type type,
+ const std::string& purchase, ListenerBase* listener);
+ static void confirm(const std::string& type,
+ const std::string& purchase, ListenerBase* listener);
+ // note: these take control of, and delete the listener when done
+
+ template <class T>
+ class Listener : public ListenerBase
+ {
+ public:
+ typedef void (T::*ConfirmationMemberFunction)(const std::string&);
+
+ Listener(T& object, ConfirmationMemberFunction function)
+ : mObject(object), mFunction(function)
+ { }
+
+ void confirmed(const std::string& password)
+ {
+ (mObject.*mFunction)(password);
+ }
+
+ private:
+ T& mObject;
+ ConfirmationMemberFunction mFunction;
+ };
+
+ template <class T>
+ static void confirm(Type type,
+ const std::string& action,
+ T& obj, void(T::*func)(const std::string&))
+ {
+ confirm(type, action, new Listener<T>(obj, func));
+ }
+
+ template <class T>
+ static void confirm(const std::string& type,
+ const std::string& action,
+ T& obj, void(T::*func)(const std::string&))
+ {
+ confirm(type, action, new Listener<T>(obj, func));
+ }
+};
+
+#endif
diff --git a/indra/newview/llcurrencyuimanager.cpp b/indra/newview/llcurrencyuimanager.cpp
new file mode 100644
index 0000000000..ffea2928c2
--- /dev/null
+++ b/indra/newview/llcurrencyuimanager.cpp
@@ -0,0 +1,497 @@
+/**
+ * @file llcurrencyuimanager.cpp
+ * @brief LLCurrencyUIManager class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lluictrlfactory.h"
+#include "lltextbox.h"
+#include "lllineeditor.h"
+
+#include "llcurrencyuimanager.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llconfirmationmanager.h"
+#include "llframetimer.h"
+#include "lllineeditor.h"
+#include "llviewchildren.h"
+#include "llxmlrpctransaction.h"
+#include "viewer.h"
+
+
+
+const F64 CURRENCY_ESTIMATE_FREQUENCY = 2.0;
+ // how long of a pause in typing a currency buy amount before an
+ // esimate is fetched from the server
+
+class LLCurrencyUIManager::Impl
+{
+public:
+ Impl(LLPanel& dialog);
+ virtual ~Impl();
+
+ LLPanel& mPanel;
+
+ bool mHidden;
+
+ bool mError;
+ std::string mErrorMessage;
+ std::string mErrorURI;
+
+ std::string mZeroMessage;
+
+ // user's choices
+ S32 mUserCurrencyBuy;
+ bool mUserEnteredCurrencyBuy;
+
+ // from website
+ bool mSiteCurrencyEstimated;
+ S32 mSiteCurrencyEstimatedCost;
+ std::string mSiteConfirm;
+
+ bool mBought;
+
+ enum TransactionType
+ {
+ TransactionNone,
+ TransactionCurrency,
+ TransactionBuy
+ };
+
+ TransactionType mTransactionType;
+ LLXMLRPCTransaction* mTransaction;
+
+ bool mCurrencyChanged;
+ LLFrameTimer mCurrencyKeyTimer;
+
+
+ void updateCurrencyInfo();
+ void finishCurrencyInfo();
+
+ void startCurrencyBuy(const std::string& password);
+ void finishCurrencyBuy();
+
+ void startTransaction(TransactionType type,
+ const char* method, LLXMLRPCValue params);
+ bool checkTransaction();
+ // return true if update needed
+
+ void setError(const std::string& message, const std::string& uri);
+ void clearError();
+
+ bool considerUpdateCurrency();
+ // return true if update needed
+ void currencyKey(S32);
+ static void onCurrencyKey(LLLineEditor* caller, void* data);
+
+ void prepare();
+ void updateUI();
+};
+
+// is potentially not fully constructed.
+LLCurrencyUIManager::Impl::Impl(LLPanel& dialog)
+ : mPanel(dialog),
+ mHidden(false),
+ mError(false),
+ mUserCurrencyBuy(1000), mUserEnteredCurrencyBuy(false),
+ mSiteCurrencyEstimated(false),
+ mBought(false),
+ mTransactionType(TransactionNone), mTransaction(0),
+ mCurrencyChanged(false)
+{
+}
+
+LLCurrencyUIManager::Impl::~Impl()
+{
+ delete mTransaction;
+}
+
+void LLCurrencyUIManager::Impl::updateCurrencyInfo()
+{
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+ mBought = false;
+ mCurrencyChanged = false;
+
+ if (mUserCurrencyBuy == 0)
+ {
+ mSiteCurrencyEstimated = true;
+ return;
+ }
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId",
+ gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionCurrency, "getCurrencyQuote", params);
+}
+
+void LLCurrencyUIManager::Impl::finishCurrencyInfo()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ setError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ LLXMLRPCValue currency = result["currency"];
+ mSiteCurrencyEstimated = true;
+ mSiteCurrencyEstimatedCost = currency["estimatedCost"].asInt();
+
+ S32 newCurrencyBuy = currency["currencyBuy"].asInt();
+ if (newCurrencyBuy != mUserCurrencyBuy)
+ {
+ mUserCurrencyBuy = newCurrencyBuy;
+ mUserEnteredCurrencyBuy = false;
+ }
+
+ mSiteConfirm = result["confirm"].asString();
+}
+
+void LLCurrencyUIManager::Impl::startCurrencyBuy(const std::string& password)
+{
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+ mCurrencyChanged = false;
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId",
+ gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("currencyBuy", mUserCurrencyBuy);
+ keywordArgs.appendInt("estimatedCost", mSiteCurrencyEstimatedCost);
+ keywordArgs.appendString("confirm", mSiteConfirm);
+ if (!password.empty())
+ {
+ keywordArgs.appendString("password", password);
+ }
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionBuy, "buyCurrency", params);
+}
+
+void LLCurrencyUIManager::Impl::finishCurrencyBuy()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ setError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ }
+ else
+ {
+ mUserCurrencyBuy = 0;
+ mUserEnteredCurrencyBuy = false;
+ mBought = true;
+ }
+}
+
+void LLCurrencyUIManager::Impl::startTransaction(TransactionType type,
+ const char* method, LLXMLRPCValue params)
+{
+ static std::string transactionURI;
+ if (transactionURI.empty())
+ {
+ transactionURI = getHelperURI() + "currency.php";
+ }
+
+ delete mTransaction;
+
+ mTransactionType = type;
+ mTransaction = new LLXMLRPCTransaction(
+ transactionURI,
+ method,
+ params,
+ false /* don't use gzip */
+ );
+
+ clearError();
+}
+
+bool LLCurrencyUIManager::Impl::checkTransaction()
+{
+ if (!mTransaction)
+ {
+ return false;
+ }
+
+ if (!mTransaction->process())
+ {
+ return false;
+ }
+
+ if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
+ {
+ setError(mTransaction->statusMessage(), mTransaction->statusURI());
+ }
+ else {
+ switch (mTransactionType)
+ {
+ case TransactionCurrency: finishCurrencyInfo(); break;
+ case TransactionBuy: finishCurrencyBuy(); break;
+ default: ;
+ }
+ }
+
+ delete mTransaction;
+ mTransaction = NULL;
+ mTransactionType = TransactionNone;
+
+ return true;
+}
+
+void LLCurrencyUIManager::Impl::setError(
+ const std::string& message, const std::string& uri)
+{
+ mError = true;
+ mErrorMessage = message;
+ mErrorURI = uri;
+}
+
+void LLCurrencyUIManager::Impl::clearError()
+{
+ mError = false;
+ mErrorMessage.clear();
+ mErrorURI.clear();
+}
+
+bool LLCurrencyUIManager::Impl::considerUpdateCurrency()
+{
+ if (mCurrencyChanged
+ && !mTransaction
+ && mCurrencyKeyTimer.getElapsedTimeF32() >= CURRENCY_ESTIMATE_FREQUENCY)
+ {
+ updateCurrencyInfo();
+ return true;
+ }
+
+ return false;
+}
+
+void LLCurrencyUIManager::Impl::currencyKey(S32 value)
+{
+ mUserEnteredCurrencyBuy = true;
+ mCurrencyKeyTimer.reset();
+
+ if (mUserCurrencyBuy == value)
+ {
+ return;
+ }
+
+ mUserCurrencyBuy = value;
+
+ if (mSiteCurrencyEstimated) {
+ mSiteCurrencyEstimated = false;
+ //cannot just simply refresh the whole UI, as the edit field will
+ // get reset and the cursor will change...
+
+ mPanel.childHide("currency_est");
+ }
+
+ mCurrencyChanged = true;
+}
+
+// static
+void LLCurrencyUIManager::Impl::onCurrencyKey(
+ LLLineEditor* caller, void* data)
+{
+ S32 value = atoi(caller->getText().c_str());
+ LLCurrencyUIManager::Impl* self = (LLCurrencyUIManager::Impl*)data;
+ self->currencyKey(value);
+}
+
+void LLCurrencyUIManager::Impl::prepare()
+{
+ LLLineEditor* lindenAmount = LLUICtrlFactory::getLineEditorByName(&mPanel,"currency_amt");
+ if (lindenAmount)
+ {
+ lindenAmount->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
+ lindenAmount->setKeystrokeCallback(onCurrencyKey);
+ lindenAmount->setCallbackUserData(this);
+ }
+}
+
+void LLCurrencyUIManager::Impl::updateUI()
+{
+ if (mHidden)
+ {
+ mPanel.childHide("currency_action");
+ mPanel.childHide("currency_amt");
+ mPanel.childHide("currency_est");
+ return;
+ }
+
+ mPanel.childShow("currency_action");
+
+ LLLineEditor* lindenAmount = LLUICtrlFactory::getLineEditorByName(&mPanel,"currency_amt");
+ if (lindenAmount)
+ {
+ lindenAmount->setVisible(true);
+ lindenAmount->setLabel(mZeroMessage);
+
+ if (!mUserEnteredCurrencyBuy)
+ {
+ if (!mZeroMessage.empty() && mUserCurrencyBuy == 0)
+ {
+ lindenAmount->setText("");
+ }
+ else
+ {
+ lindenAmount->setText(llformat("%d", mUserCurrencyBuy));
+ }
+
+ lindenAmount->selectAll();
+ }
+ }
+
+ mPanel.childSetTextArg("currency_est", "[USD]", llformat("%#.2f", mSiteCurrencyEstimatedCost / 100.0));
+ mPanel.childSetVisible("currency_est", mSiteCurrencyEstimated && mUserCurrencyBuy > 0);
+}
+
+
+
+LLCurrencyUIManager::LLCurrencyUIManager(LLPanel& dialog)
+ : impl(* new Impl(dialog))
+{
+}
+
+LLCurrencyUIManager::~LLCurrencyUIManager()
+{
+ delete &impl;
+}
+
+void LLCurrencyUIManager::setAmount(int amount, bool noEstimate)
+{
+ impl.mUserCurrencyBuy = amount;
+ impl.mUserEnteredCurrencyBuy = false;
+ impl.updateUI();
+
+ impl.mCurrencyChanged = !noEstimate;
+}
+
+int LLCurrencyUIManager::getAmount()
+{
+ return impl.mUserCurrencyBuy;
+}
+
+void LLCurrencyUIManager::setZeroMessage(const std::string& message)
+{
+ impl.mZeroMessage = message;
+}
+
+void LLCurrencyUIManager::setEstimate(int amount)
+{
+ impl.mSiteCurrencyEstimatedCost = amount;
+ impl.mSiteCurrencyEstimated = true;
+ impl.updateUI();
+
+ impl.mCurrencyChanged = false;
+}
+
+int LLCurrencyUIManager::getEstimate()
+{
+ return impl.mSiteCurrencyEstimated ? impl.mSiteCurrencyEstimatedCost : 0;
+}
+
+void LLCurrencyUIManager::prepare()
+{
+ impl.prepare();
+}
+
+void LLCurrencyUIManager::updateUI(bool show)
+{
+ impl.mHidden = !show;
+ impl.updateUI();
+}
+
+bool LLCurrencyUIManager::process()
+{
+ bool changed = false;
+ changed |= impl.checkTransaction();
+ changed |= impl.considerUpdateCurrency();
+ return changed;
+}
+
+void LLCurrencyUIManager::buy()
+{
+ if (!canBuy())
+ {
+ return;
+ }
+
+ // XUI:translate
+ LLConfirmationManager::confirm(impl.mSiteConfirm,
+ llformat("Buy L$ %d for approx. US$ %#.2f\n",
+ impl.mUserCurrencyBuy,
+ impl.mSiteCurrencyEstimatedCost / 100.0),
+ impl,
+ &LLCurrencyUIManager::Impl::startCurrencyBuy);
+}
+
+
+bool LLCurrencyUIManager::inProcess()
+{
+ return impl.mTransactionType != Impl::TransactionNone;
+}
+
+bool LLCurrencyUIManager::canCancel()
+{
+ return impl.mTransactionType != Impl::TransactionBuy;
+}
+
+bool LLCurrencyUIManager::canBuy()
+{
+ return impl.mTransactionType == Impl::TransactionNone
+ && impl.mSiteCurrencyEstimated
+ && impl.mUserCurrencyBuy > 0;
+}
+
+bool LLCurrencyUIManager::buying()
+{
+ return impl.mTransactionType == Impl::TransactionBuy;
+}
+
+bool LLCurrencyUIManager::bought()
+{
+ return impl.mBought;
+}
+
+bool LLCurrencyUIManager::hasError()
+{
+ return impl.mError;
+}
+
+std::string LLCurrencyUIManager::errorMessage()
+{
+ return impl.mErrorMessage;
+}
+
+std::string LLCurrencyUIManager::errorURI()
+{
+ return impl.mErrorURI;
+}
+
diff --git a/indra/newview/llcurrencyuimanager.h b/indra/newview/llcurrencyuimanager.h
new file mode 100644
index 0000000000..3c5ad22ee5
--- /dev/null
+++ b/indra/newview/llcurrencyuimanager.h
@@ -0,0 +1,72 @@
+/**
+ * @file llcurrencyuimanager.h
+ * @brief LLCurrencyUIManager class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCURRENCYUIMANAGER_H
+#define LL_LLCURRENCYUIMANAGER_H
+
+class LLPanel;
+
+
+class LLCurrencyUIManager
+ // manages the currency purchase portion of any dialog
+ // takes control of, and assumes repsonsibility for several
+ // fields:
+ // 'currency_action' - the text "Buy L$" before the entry field
+ // 'currency_amt' - the line editor for the entry amount
+ // 'currency_est' - the estimated cost from the web site
+{
+public:
+ LLCurrencyUIManager(LLPanel& parent);
+ virtual ~LLCurrencyUIManager();
+
+ void setAmount(int, bool noEstimate = false);
+ int getAmount();
+ // the amount in L$ to purchase
+ // setting it overwrites the user's entry
+ // if noEstimate is true, than no web request is made
+
+ void setZeroMessage(const std::string& message);
+ // sets the gray message to show when zero
+
+ void setEstimate(int);
+ int getEstimate();
+ // the amount in US$ * 100 (in otherwords, in cents)
+ // use set when you get this information from elsewhere
+
+ void prepare();
+ // call once after dialog is built, from postBuild()
+ void updateUI(bool show = true);
+ // update all UI elements, if show is false, they are all set not visible
+ // normally, this is done automatically, but you can force it
+ // the show/hidden state is remembered
+ bool process();
+ // call periodically, for example, from draw()
+ // returns true if the UI needs to be updated
+
+ void buy();
+ // call to initiate the purchase
+
+ bool inProcess(); // is a transaction in process
+ bool canCancel(); // can we cancel it (by destructing this object)
+ bool canBuy(); // can the user choose to buy now?
+ bool buying(); // are we in the process of buying?
+ bool bought(); // did the buy() transaction complete successfully
+
+ bool hasError();
+ std::string errorMessage();
+ std::string errorURI();
+ // error information for the user, the URI may be blank
+ // the technical error details will have already been logged
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+#endif
+
diff --git a/indra/newview/llcylinder.cpp b/indra/newview/llcylinder.cpp
new file mode 100644
index 0000000000..1774ff4ccf
--- /dev/null
+++ b/indra/newview/llcylinder.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file llcylinder.cpp
+ * @brief Draws a cylinder using display lists for speed.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcylinder.h"
+
+#include "llerror.h"
+#include "math.h"
+#include "llmath.h"
+#include "noise.h"
+#include "v3math.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+
+LLCylinder gCylinder;
+LLCone gCone;
+
+GLUquadricObj* gQuadObj = NULL;
+
+// draws a cylinder or cone
+// returns approximate number of triangles required
+U32 draw_cylinder_side(GLint slices, GLint stacks, GLfloat base_radius, GLfloat top_radius)
+{
+ U32 triangles = 0;
+ GLfloat height = 1.0f;
+
+ if (!gQuadObj)
+ {
+ gQuadObj = gluNewQuadric();
+ if (!gQuadObj) llerror("draw_cylindrical_body couldn't allocated quadric", 0);
+ }
+
+ gluQuadricDrawStyle(gQuadObj, GLU_FILL);
+ gluQuadricNormals(gQuadObj, GLU_SMOOTH);
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ gluQuadricTexture(gQuadObj, GL_TRUE);
+ gluCylinder(gQuadObj, base_radius, top_radius, height, slices, stacks);
+ triangles += stacks * (slices * 2);
+
+
+ return triangles;
+}
+
+
+// Returns number of triangles required to draw
+// Need to know if top or not to set lighting normals
+const BOOL TOP = TRUE;
+const BOOL BOTTOM = FALSE;
+U32 draw_cylinder_cap(GLint slices, GLfloat base_radius, BOOL is_top)
+{
+ U32 triangles = 0;
+
+ if (!gQuadObj)
+ {
+ gQuadObj = gluNewQuadric();
+ if (!gQuadObj) llerror("draw_cylinder_base couldn't allocated quadric", 0);
+ }
+
+ gluQuadricDrawStyle(gQuadObj, GLU_FILL);
+ gluQuadricNormals(gQuadObj, GLU_SMOOTH);
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ gluQuadricTexture(gQuadObj, GL_TRUE);
+
+ // no hole in the middle of the disk, and just one ring
+ GLdouble inner_radius = 0.0;
+ GLint rings = 1;
+
+ // normals point in +z for top, -z for base
+ if (is_top)
+ {
+ gluQuadricOrientation(gQuadObj, GLU_OUTSIDE);
+ }
+ else
+ {
+ gluQuadricOrientation(gQuadObj, GLU_INSIDE);
+ }
+ gluDisk(gQuadObj, inner_radius, base_radius, slices, rings);
+ triangles += slices;
+
+ return triangles;
+}
+
+
+void LLCylinder::prerender()
+{
+ GLint stacks = 2;
+ GLfloat radius = 0.5f;
+ GLint slices[CYLINDER_LEVELS_OF_DETAIL] = { 30, 20, 12, 6 }; // same as sphere slices
+
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ mTriangleCount[detail] = 0;
+
+ mDisplayListSide[detail] = glGenLists(1);
+ glNewList(mDisplayListSide[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, radius );
+ glEndList();
+
+ mDisplayListTop[detail] = glGenLists(1);
+ glNewList( mDisplayListTop[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, TOP );
+ glEndList();
+
+ mDisplayListBottom[detail] = glGenLists(1);
+ glNewList( mDisplayListBottom[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
+ glEndList();
+ }
+}
+
+void LLCylinder::cleanupGL()
+{
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ glDeleteLists(mDisplayListSide[detail], 1);
+ mDisplayListSide[detail] = 0;
+ glDeleteLists(mDisplayListTop[detail], 1);
+ mDisplayListTop[detail] = 0;
+ glDeleteLists(mDisplayListBottom[detail], 1);
+ mDisplayListBottom[detail] = 0;
+ }
+
+ if (gQuadObj)
+ {
+ gluDeleteQuadric(gQuadObj);
+ gQuadObj = NULL;
+ }
+}
+
+void LLCylinder::render(F32 pixel_area)
+{
+ renderface(pixel_area, 0);
+ renderface(pixel_area, 1);
+ renderface(pixel_area, 2);
+}
+
+
+void LLCylinder::renderface(F32 pixel_area, S32 face)
+{
+ if (face < 0 || face > 2)
+ {
+ llerror("LLCylinder::renderface() invalid face number", face);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ S32 level_of_detail;
+
+ if (pixel_area > 20000.f)
+ {
+ level_of_detail = 0;
+ }
+ else if (pixel_area > 1600.f)
+ {
+ level_of_detail = 1;
+ }
+ else if (pixel_area > 200.f)
+ {
+ level_of_detail = 2;
+ }
+ else
+ {
+ level_of_detail = 3;
+ }
+
+ if (level_of_detail < 0 || CYLINDER_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCylinder::renderface() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ switch(face)
+ {
+ case 0:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListSide[level_of_detail]);
+ break;
+ case 1:
+ glTranslatef(0.0f, 0.f, 0.5f);
+ glCallList(mDisplayListTop[level_of_detail]);
+ break;
+ case 2:
+ glTranslatef(0.0f, 0.f, -0.5f);
+ glCallList(mDisplayListBottom[level_of_detail]);
+ break;
+ default:
+ llerror("LLCylinder::renderface() fell out of switch", 0);
+ break;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+
+//
+// Cones
+//
+
+void LLCone::prerender()
+{
+ GLint stacks = 2;
+ GLfloat radius = 0.5f;
+ GLint slices[CONE_LEVELS_OF_DETAIL] = { 32, 18, 12, 6 };
+
+ for (S32 detail = 0; detail < CONE_LEVELS_OF_DETAIL; detail++)
+ {
+ mTriangleCount[detail] = 0;
+
+ mDisplayListSide[detail] = glGenLists(1);
+ glNewList(mDisplayListSide[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_side( slices[detail], stacks, radius, 0.f );
+ glEndList();
+
+ mDisplayListBottom[detail] = glGenLists(1);
+ glNewList( mDisplayListBottom[detail], GL_COMPILE);
+ mTriangleCount[detail] += draw_cylinder_cap( slices[detail], radius, BOTTOM );
+ glEndList();
+ }
+}
+
+void LLCone::cleanupGL()
+{
+ for (S32 detail = 0; detail < CYLINDER_LEVELS_OF_DETAIL; detail++)
+ {
+ glDeleteLists(mDisplayListSide[detail], 1);
+ mDisplayListSide[detail] = 0;
+
+ glDeleteLists(mDisplayListBottom[detail], 1);
+ mDisplayListBottom[detail] = 0;
+ }
+
+ if (gQuadObj)
+ {
+ gluDeleteQuadric(gQuadObj);
+ gQuadObj = NULL;
+ }
+}
+
+
+void LLCone::render(S32 level_of_detail)
+{
+ GLfloat height = 1.0f;
+
+ if (level_of_detail < 0 || CONE_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCone::render() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ // center object at 0
+ glTranslatef(0.f, 0.f, - height / 2.0f);
+
+ glCallList(mDisplayListSide[level_of_detail]);
+ glCallList(mDisplayListBottom[level_of_detail]);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+
+void LLCone::renderface(S32 level_of_detail, S32 face)
+{
+ if (face < 0 || face > 1)
+ {
+ llerror("LLCone::renderface() invalid face number", face);
+ return;
+ }
+
+ if (level_of_detail < 0 || CONE_LEVELS_OF_DETAIL <= level_of_detail)
+ {
+ llerror("LLCone::renderface() invalid level of detail", level_of_detail);
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ switch(face)
+ {
+ case 0:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListSide[level_of_detail]);
+ break;
+ case 1:
+ glTranslatef(0.f, 0.f, -0.5f);
+ glCallList(mDisplayListBottom[level_of_detail]);
+ break;
+ default:
+ llerror("LLCylinder::renderface() fell out of switch", 0);
+ break;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
diff --git a/indra/newview/llcylinder.h b/indra/newview/llcylinder.h
new file mode 100644
index 0000000000..9150db4fb1
--- /dev/null
+++ b/indra/newview/llcylinder.h
@@ -0,0 +1,67 @@
+/**
+ * @file llcylinder.h
+ * @brief Draws a cylinder, and a cone, which is a special case cylinder
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLCYLINDER_H
+#define LL_LLCYLINDER_H
+
+//#include "stdtypes.h"
+//#include "llgl.h"
+
+//
+// Cylinders
+//
+const S32 CYLINDER_LEVELS_OF_DETAIL = 4;
+const S32 CYLINDER_FACES = 3;
+
+class LLCylinder
+{
+protected:
+ U32 mDisplayListSide[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mDisplayListTop[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mDisplayListBottom[CYLINDER_LEVELS_OF_DETAIL];
+ U32 mTriangleCount[CYLINDER_LEVELS_OF_DETAIL];
+
+public:
+ void prerender();
+ void cleanupGL();
+
+ void render(F32 pixel_area);
+ void renderface(F32 pixel_area, S32 face);
+
+ U32 getTriangleCount(S32 level_of_detail) { return mTriangleCount[level_of_detail]; }
+};
+
+
+//
+// Cones
+//
+
+const S32 CONE_LOD_HIGHEST = 0;
+const S32 CONE_LEVELS_OF_DETAIL = 4;
+const S32 CONE_FACES = 2;
+
+class LLCone
+{
+protected:
+ U32 mDisplayListSide[CONE_LEVELS_OF_DETAIL];
+ U32 mDisplayListBottom[CONE_LEVELS_OF_DETAIL];
+ U32 mTriangleCount[CONE_LEVELS_OF_DETAIL];
+
+public:
+ void prerender();
+ void cleanupGL();
+
+ void render(S32 level_of_detail);
+ void renderface(S32 level_of_detail, S32 face);
+
+ U32 getTriangleCount(S32 level_of_detail) { return mTriangleCount[level_of_detail]; }
+};
+
+extern LLCylinder gCylinder;
+extern LLCone gCone;
+#endif
diff --git a/indra/newview/lldebugmessagebox.cpp b/indra/newview/lldebugmessagebox.cpp
new file mode 100644
index 0000000000..fbfe9e3864
--- /dev/null
+++ b/indra/newview/lldebugmessagebox.cpp
@@ -0,0 +1,226 @@
+/**
+ * @file lldebugmessagebox.cpp
+ * @brief Implementation of a simple, non-modal message box.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldebugmessagebox.h"
+
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llbutton.h"
+#include "llsliderctrl.h"
+#include "llcheckboxctrl.h"
+#include "lltextbox.h"
+#include "llcallbacklist.h"
+#include "lllineeditor.h"
+#include "llfocusmgr.h"
+
+///----------------------------------------------------------------------------
+/// Class LLDebugVarMessageBox
+///----------------------------------------------------------------------------
+
+std::map<LLString, LLDebugVarMessageBox*> LLDebugVarMessageBox::sInstances;
+
+LLDebugVarMessageBox::LLDebugVarMessageBox(const std::string& title, EDebugVarType var_type, void *var) :
+ LLFloater("msg box", LLRect(10,160,400,10), title),
+ mVarType(var_type), mVarData(var), mAnimate(FALSE)
+{
+ switch(var_type)
+ {
+ case VAR_TYPE_F32:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, *((F32*)var), -100.f, 100.f, 0.1f, NULL);
+ mSlider1->setPrecision(3);
+ addChild(mSlider1);
+ mSlider2 = NULL;
+ mSlider3 = NULL;
+ break;
+ case VAR_TYPE_S32:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,100,190,80), title, NULL, 70, 130, TRUE, TRUE, NULL, NULL, (F32)*((S32*)var), -255.f, 255.f, 1.f, NULL);
+ mSlider1->setPrecision(0);
+ addChild(mSlider1);
+ mSlider2 = NULL;
+ mSlider3 = NULL;
+ break;
+ case VAR_TYPE_VEC3:
+ mSlider1 = new LLSliderCtrl("slider 1", LLRect(20,130,190,110), "x: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VX], -100.f, 100.f, 0.1f, NULL);
+ mSlider1->setPrecision(3);
+ mSlider2 = new LLSliderCtrl("slider 2", LLRect(20,100,190,80), "y: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VY], -100.f, 100.f, 0.1f, NULL);
+ mSlider2->setPrecision(3);
+ mSlider3 = new LLSliderCtrl("slider 3", LLRect(20,70,190,50), "z: ", NULL, 70, 130, TRUE, TRUE, NULL, NULL, ((LLVector3*)var)->mV[VZ], -100.f, 100.f, 0.1f, NULL);
+ mSlider3->setPrecision(3);
+ addChild(mSlider1);
+ addChild(mSlider2);
+ addChild(mSlider3);
+ break;
+ }
+
+ mAnimateButton = new LLButton("Animate", LLRect(20, 45, 180, 25), "", onAnimateClicked, this);
+ addChild(mAnimateButton);
+
+ mText = new LLTextBox("value", LLRect(20,20,190,0));
+ addChild(mText);
+
+ //disable hitting enter closes dialog
+ setDefaultBtn();
+}
+
+LLDebugVarMessageBox::~LLDebugVarMessageBox()
+{
+ sInstances.erase(mTitle);
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, F32 *var, F32 max_value, F32 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_F32, (void*)var);
+ max_value = llabs(max_value);
+ box->mSlider1->setMaxValue(max_value);
+ box->mSlider1->setMinValue(-max_value);
+ box->mSlider1->setIncrement(increment);
+ if (!gFocusMgr.childHasKeyboardFocus(box))
+ {
+ box->mSlider1->setValue(*var);
+ }
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+#endif
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, S32 *var, S32 max_value, S32 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_S32, (void*)var);
+ F32 max_val = llabs((F32)max_value);
+ box->mSlider1->setMaxValue(max_val);
+ box->mSlider1->setMinValue(-max_val);
+ box->mSlider1->setIncrement((F32)increment);
+ if (!gFocusMgr.childHasKeyboardFocus(box))
+ {
+ box->mSlider1->setValue((F32)*var);
+ }
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+#endif
+}
+
+void LLDebugVarMessageBox::show(const std::string& title, LLVector3 *var, LLVector3 max_value, LLVector3 increment)
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLDebugVarMessageBox* box = show(title, VAR_TYPE_VEC3, (LLVector3*)var);
+ max_value.abs();
+ box->mSlider1->setMaxValue(max_value.mV[VX]);
+ box->mSlider1->setMinValue(-max_value.mV[VX]);
+ box->mSlider1->setIncrement(increment.mV[VX]);
+ box->mSlider1->setCommitCallback(slider_changed);
+ box->mSlider1->setCallbackUserData(box);
+
+ box->mSlider2->setMaxValue(max_value.mV[VX]);
+ box->mSlider2->setMinValue(-max_value.mV[VX]);
+ box->mSlider2->setIncrement(increment.mV[VX]);
+ box->mSlider2->setCommitCallback(slider_changed);
+ box->mSlider2->setCallbackUserData(box);
+
+ box->mSlider3->setMaxValue(max_value.mV[VX]);
+ box->mSlider3->setMinValue(-max_value.mV[VX]);
+ box->mSlider3->setIncrement(increment.mV[VX]);
+ box->mSlider3->setCommitCallback(slider_changed);
+ box->mSlider3->setCallbackUserData(box);
+#endif
+}
+
+LLDebugVarMessageBox* LLDebugVarMessageBox::show(const std::string& title, EDebugVarType var_type, void *var)
+{
+ LLString title_string(title);
+
+ LLDebugVarMessageBox *box = sInstances[title_string];
+ if (!box)
+ {
+ box = new LLDebugVarMessageBox(title, var_type, var);
+ sInstances[title_string] = box;
+ gFloaterView->addChild(box);
+ box->reshape(200,150);
+ box->open();
+ box->mTitle = title_string;
+ }
+
+ return box;
+}
+
+void LLDebugVarMessageBox::slider_changed(LLUICtrl* ctrl, void* user_data)
+{
+ LLDebugVarMessageBox *msg_box = (LLDebugVarMessageBox*)user_data;
+ if (!msg_box || !msg_box->mVarData) return;
+
+ switch(msg_box->mVarType)
+ {
+ case VAR_TYPE_F32:
+ *((F32*)msg_box->mVarData) = (F32)msg_box->mSlider1->getValue().asReal();
+ break;
+ case VAR_TYPE_S32:
+ *((S32*)msg_box->mVarData) = (S32)msg_box->mSlider1->getValue().asInteger();
+ break;
+ case VAR_TYPE_VEC3:
+ LLVector3* vec_p = (LLVector3*)msg_box->mVarData;
+ vec_p->setVec((F32)msg_box->mSlider1->getValue().asReal(),
+ (F32)msg_box->mSlider2->getValue().asReal(),
+ (F32)msg_box->mSlider3->getValue().asReal());
+ break;
+ }
+}
+
+void LLDebugVarMessageBox::onAnimateClicked(void* user_data)
+{
+ LLDebugVarMessageBox* msg_boxp = (LLDebugVarMessageBox*)user_data;
+ msg_boxp->mAnimate = !msg_boxp->mAnimate;
+ msg_boxp->mAnimateButton->setToggleState(msg_boxp->mAnimate);
+}
+
+void LLDebugVarMessageBox::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+void LLDebugVarMessageBox::draw()
+{
+ char text[128];
+ switch(mVarType)
+ {
+ case VAR_TYPE_F32:
+ sprintf(text, "%.3f", *((F32*)mVarData));
+ break;
+ case VAR_TYPE_S32:
+ sprintf(text, "%d", *((S32*)mVarData));
+ break;
+ case VAR_TYPE_VEC3:
+ LLVector3* vec_p = (LLVector3*)mVarData;
+ sprintf(text, "%.3f %.3f %.3f", vec_p->mV[VX], vec_p->mV[VY], vec_p->mV[VZ]);
+ break;
+ }
+ mText->setText(text);
+
+ if(mAnimate)
+ {
+ F32 animated_val = clamp_rescale(fmodf((F32)LLFrameTimer::getElapsedSeconds() / 5.f, 1.f), 0.f, 1.f, 0.f, mSlider1->getMaxValue());
+ if (mSlider1)
+ {
+ mSlider1->setValue(animated_val);
+ slider_changed(mSlider1, this);
+ }
+ if (mSlider2)
+ {
+ mSlider2->setValue(animated_val);
+ slider_changed(mSlider2, this);
+ }
+ if (mSlider3)
+ {
+ mSlider3->setValue(animated_val);
+ slider_changed(mSlider3, this);
+ }
+ }
+ LLFloater::draw();
+}
diff --git a/indra/newview/lldebugmessagebox.h b/indra/newview/lldebugmessagebox.h
new file mode 100644
index 0000000000..fa34c7cc41
--- /dev/null
+++ b/indra/newview/lldebugmessagebox.h
@@ -0,0 +1,72 @@
+/**
+ * @file lldebugmessagebox.h
+ * @brief Debug message box.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLDEBUGMESSAGEBOX_H
+#define LL_LLDEBUGMESSAGEBOX_H
+
+#include "lldarray.h"
+#include "llfloater.h"
+#include "v3math.h"
+#include "lltextbox.h"
+#include "llstring.h"
+#include "llframetimer.h"
+#include <vector>
+#include <map>
+
+class LLSliderCtrl;
+
+//----------------------------------------------------------------------------
+// LLDebugVarMessageBox
+//----------------------------------------------------------------------------
+
+typedef enum e_debug_var_type
+{
+ VAR_TYPE_F32,
+ VAR_TYPE_S32,
+ VAR_TYPE_VEC2,
+ VAR_TYPE_VEC3,
+ VAR_TYPE_VEC4,
+ VAR_TYPE_COUNT
+} EDebugVarType;
+
+class LLDebugVarMessageBox : public LLFloater
+{
+protected:
+ LLDebugVarMessageBox(const std::string& title, EDebugVarType var_type, void *var);
+ ~LLDebugVarMessageBox();
+
+ static LLDebugVarMessageBox* show(const std::string& title, EDebugVarType var_type, void *var);
+ static void slider_changed(LLUICtrl* ctrl, void* user_data);
+ static void onAnimateClicked(void* user_data);
+
+public:
+ static void show(const std::string& title, F32 *var, F32 max_value = 100.f, F32 increment = 0.1f);
+ static void show(const std::string& title, S32 *var, S32 max_value = 255, S32 increment = 1);
+ static void show(const std::string& title, LLVector2 *var, LLVector2 max_value = LLVector2(100.f, 100.f), LLVector2 increment = LLVector2(0.1f, 0.1f));
+ static void show(const std::string& title, LLVector3 *var, LLVector3 max_value = LLVector3(100.f, 100.f, 100.f), LLVector3 increment = LLVector3(0.1f, 0.1f, 0.1f));
+ //static void show(const std::string& title, LLVector4 *var, LLVector4 max_value = LLVector4(100.f, 100.f, 100.f, 100.f), LLVector4 increment = LLVector4(0.1f, 0.1f, 0.1f, 0.1f));
+
+ virtual void onClose(bool app_quitting);
+ virtual void draw();
+
+protected:
+ EDebugVarType mVarType;
+ void* mVarData;
+ LLSliderCtrl* mSlider1;
+ LLSliderCtrl* mSlider2;
+ LLSliderCtrl* mSlider3;
+ LLButton* mAnimateButton;
+ LLTextBox* mText;
+ LLString mTitle;
+ BOOL mAnimate;
+
+ static std::map<LLString, LLDebugVarMessageBox*> sInstances;
+};
+
+#endif // LL_LLMESSAGEBOX_H
diff --git a/indra/newview/lldebugview.cpp b/indra/newview/lldebugview.cpp
new file mode 100644
index 0000000000..d20c431987
--- /dev/null
+++ b/indra/newview/lldebugview.cpp
@@ -0,0 +1,133 @@
+/**
+ * @file lldebugview.cpp
+ * @brief A view containing UI elements only visible in build mode.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldebugview.h"
+
+// library includes
+#include "llframestatview.h"
+#include "llfasttimerview.h"
+#include "llmemoryview.h"
+#include "llconsole.h"
+#include "lltextureview.h"
+#include "llresmgr.h"
+#include "llaudiostatus.h"
+#include "imageids.h"
+#include "llvelocitybar.h"
+#include "llviewerwindow.h"
+
+//
+// Globals
+//
+
+LLDebugView* gDebugView = NULL;
+
+//
+// Methods
+//
+
+LLDebugView::LLDebugView(const std::string& name, const LLRect &rect)
+: LLView(name, rect, FALSE)
+{
+ LLRect r;
+
+ r.set(0, rect.getHeight() - 100, rect.getWidth()/2, 100);
+ mDebugConsolep = new LLConsole("debug console", 20, r, -1, 0.f );
+ mDebugConsolep->setFollowsBottom();
+ mDebugConsolep->setFollowsLeft();
+ mDebugConsolep->setVisible( FALSE );
+ addChild(mDebugConsolep);
+
+ r.set(150 - 25, rect.getHeight() - 50, rect.getWidth()/2 - 25, rect.getHeight() - 450);
+ mFrameStatView = new LLFrameStatView("frame stat", r);
+ mFrameStatView->setFollowsTop();
+ mFrameStatView->setFollowsLeft();
+ mFrameStatView->setVisible(FALSE); // start invisible
+ addChild(mFrameStatView);
+
+ r.set(25, rect.getHeight() - 50, (S32) (gViewerWindow->getVirtualWindowRect().getWidth() * 0.75f),
+ (S32) (gViewerWindow->getVirtualWindowRect().getHeight() * 0.75f));
+ mFastTimerView = new LLFastTimerView("fast timers", r);
+ mFastTimerView->setFollowsTop();
+ mFastTimerView->setFollowsLeft();
+ mFastTimerView->setVisible(FALSE); // start invisible
+ addChild(mFastTimerView);
+
+ r.set(25, rect.getHeight() - 50, rect.getWidth()/2, rect.getHeight() - 450);
+ mMemoryView = new LLMemoryView("memory", r);
+ mMemoryView->setFollowsTop();
+ mMemoryView->setFollowsLeft();
+ mMemoryView->setVisible(FALSE); // start invisible
+ addChild(mMemoryView);
+
+ r.set(150, rect.getHeight() - 50, 820, 100);
+ gTextureView = new LLTextureView("gTextureView", r);
+ gTextureView->setRect(r);
+ gTextureView->setFollowsBottom();
+ gTextureView->setFollowsLeft();
+ addChild(gTextureView);
+ //gTextureView->reshape(r.getWidth(), r.getHeight(), TRUE);
+
+ //
+ // Debug statistics
+ //
+ r.set(rect.getWidth() - 250,
+ rect.getHeight(),
+ rect.getWidth(),
+ rect.getHeight() - 400);
+ mStatViewp = new LLContainerView("statistics", r);
+ mStatViewp->setLabel("Statistics");
+ mStatViewp->setFollowsTop();
+ mStatViewp->setFollowsRight();
+ // Default to off
+ mStatViewp->setVisible(FALSE);
+ addChild(mStatViewp);
+
+ //
+ // Audio debugging stuff
+ //
+ const S32 AUDIO_STATUS_LEFT = rect.getWidth()/2-100;
+ const S32 AUDIO_STATUS_WIDTH = 320;
+ const S32 AUDIO_STATUS_TOP = (rect.getHeight()/2)+400;
+ const S32 AUDIO_STATUS_HEIGHT = 320;
+ r.setLeftTopAndSize( AUDIO_STATUS_LEFT, AUDIO_STATUS_TOP, AUDIO_STATUS_WIDTH, AUDIO_STATUS_HEIGHT );
+ LLAudiostatus* gAudioStatus = new LLAudiostatus("AudioStatus", r);
+ gAudioStatus->setFollowsTop();
+ gAudioStatus->setFollowsRight();
+ addChild(gAudioStatus);
+
+ const S32 VELOCITY_LEFT = 10; // 370;
+ const S32 VELOCITY_WIDTH = 500;
+ const S32 VELOCITY_TOP = 140;
+ const S32 VELOCITY_HEIGHT = 45;
+ r.setLeftTopAndSize( VELOCITY_LEFT, VELOCITY_TOP, VELOCITY_WIDTH, VELOCITY_HEIGHT );
+ gVelocityBar = new LLVelocityBar("Velocity Bar", r);
+ gVelocityBar->setFollowsBottom();
+ gVelocityBar->setFollowsLeft();
+ addChild(gVelocityBar);
+}
+
+
+LLDebugView::~LLDebugView()
+{
+ // These have already been deleted. Fix the globals appropriately.
+ gDebugView = NULL;
+ gTextureView = NULL;
+}
+
+EWidgetType LLDebugView::getWidgetType() const
+{
+ return WIDGET_TYPE_DEBUG_VIEW;
+}
+
+LLString LLDebugView::getWidgetTag() const
+{
+ return LL_DEBUG_VIEW_TAG;
+}
+
diff --git a/indra/newview/lldebugview.h b/indra/newview/lldebugview.h
new file mode 100644
index 0000000000..5406099189
--- /dev/null
+++ b/indra/newview/lldebugview.h
@@ -0,0 +1,46 @@
+/**
+ * @file lldebugview.h
+ * @brief A view containing debug UI elements
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDEBUGVIEW_H
+#define LL_LLDEBUGVIEW_H
+
+// requires:
+// stdtypes.h
+
+#include "llview.h"
+
+// declarations
+class LLButton;
+class LLToolView;
+class LLStatusPanel;
+class LLFrameStatView;
+class LLFastTimerView;
+class LLMemoryView;
+class LLConsole;
+class LLTextureView;
+class LLContainerView;
+
+class LLDebugView : public LLView
+{
+public:
+ LLDebugView(const std::string& name, const LLRect &rect);
+ ~LLDebugView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ LLFrameStatView* mFrameStatView;
+ LLFastTimerView* mFastTimerView;
+ LLMemoryView* mMemoryView;
+ LLConsole* mDebugConsolep;
+ LLContainerView* mStatViewp;
+};
+
+extern LLDebugView* gDebugView;
+
+#endif
diff --git a/indra/newview/lldirpicker.cpp b/indra/newview/lldirpicker.cpp
new file mode 100644
index 0000000000..5a01bfdc84
--- /dev/null
+++ b/indra/newview/lldirpicker.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file lldirpicker.cpp
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldirpicker.h"
+//#include "viewer.h"
+//#include "llviewermessage.h"
+#include "llworld.h"
+#include "llviewerwindow.h"
+#include "llkeyboard.h"
+#include "lldir.h"
+#include "llframetimer.h"
+
+//
+// Globals
+//
+
+LLDirPicker LLDirPicker::sInstance;
+
+#if LL_WINDOWS
+#include <shlobj.h>
+#endif
+
+//
+// Implementation
+//
+#if LL_WINDOWS
+
+LLDirPicker::LLDirPicker()
+{
+}
+
+LLDirPicker::~LLDirPicker()
+{
+ // nothing
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+
+ // Modal, so pause agent
+ send_agent_pause();
+
+ BROWSEINFO bi;
+ memset(&bi, 0, sizeof(bi));
+
+ bi.ulFlags = BIF_USENEWUI;
+ bi.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());;
+ bi.lpszTitle = NULL;
+
+ ::OleInitialize(NULL);
+
+ LPITEMIDLIST pIDL = ::SHBrowseForFolder(&bi);
+
+ if(pIDL != NULL)
+ {
+ WCHAR buffer[_MAX_PATH] = {'\0'};
+
+ if(::SHGetPathFromIDList(pIDL, buffer) != 0)
+ {
+ // Set the string value.
+
+ mDir = utf16str_to_utf8str(llutf16string(buffer));
+ success = TRUE;
+ }
+
+ // free the item id list
+ CoTaskMemFree(pIDL);
+ }
+
+ ::OleUninitialize();
+
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return mDir;
+}
+
+/////////////////////////////////////////////DARWIN
+#elif LL_DARWIN
+
+LLDirPicker::LLDirPicker()
+{
+ reset();
+
+ memset(&mNavOptions, 0, sizeof(mNavOptions));
+ OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions);
+ if (error == noErr)
+ {
+ mNavOptions.modality = kWindowModalityAppModal;
+ }
+}
+
+LLDirPicker::~LLDirPicker()
+{
+ // nothing
+}
+
+//static
+pascal void LLDirPicker::doNavCallbackEvent(NavEventCallbackMessage callBackSelector,
+ NavCBRecPtr callBackParms, void* callBackUD)
+{
+ switch(callBackSelector)
+ {
+ case kNavCBStart:
+ {
+ if (!sInstance.mFileName) break;
+
+ OSStatus error = noErr;
+ AEDesc theLocation = {typeNull, NULL};
+ FSSpec outFSSpec;
+
+ //Convert string to a FSSpec
+ FSRef myFSRef;
+
+ const char* filename=sInstance.mFileName->c_str();
+
+ error = FSPathMakeRef ((UInt8*)filename, &myFSRef, NULL);
+
+ if (error != noErr) break;
+
+ error = FSGetCatalogInfo (&myFSRef, kFSCatInfoNone, NULL, NULL, &outFSSpec, NULL);
+
+ if (error != noErr) break;
+
+ error = AECreateDesc(typeFSS, &outFSSpec, sizeof(FSSpec), &theLocation);
+
+ if (error != noErr) break;
+
+ error = NavCustomControl(callBackParms->context,
+ kNavCtlSetLocation, (void*)&theLocation);
+
+ }
+ }
+}
+
+OSStatus LLDirPicker::doNavChooseDialog()
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+
+ // NOTE: we are passing the address of a local variable here.
+ // This is fine, because the object this call creates will exist for less than the lifetime of this function.
+ // (It is destroyed by NavDialogDispose() below.)
+
+ error = NavCreateChooseFolderDialog(&mNavOptions, &doNavCallbackEvent, NULL, NULL, &navRef);
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+ char path[LL_MAX_PATH];
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
+
+ if (error == noErr)
+ mDir = path;
+ }
+
+ return error;
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ if( mLocked ) return FALSE;
+ BOOL success = FALSE;
+ OSStatus error = noErr;
+
+ mFileName = filename;
+
+// mNavOptions.saveFileName
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog();
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mDir.length() > 0)
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return mDir;
+}
+
+void LLDirPicker::reset()
+{
+ mLocked = FALSE;
+ mDir = NULL;
+}
+
+#else // not implemented
+
+LLDirPicker::LLDirPicker()
+{
+ reset();
+}
+
+LLDirPicker::~LLDirPicker()
+{
+}
+
+
+void LLDirPicker::reset()
+{
+}
+
+BOOL LLDirPicker::getDir(LLString* filename)
+{
+ return FALSE;
+}
+
+LLString LLDirPicker::getDirName()
+{
+ return "";
+}
+
+#endif
diff --git a/indra/newview/lldirpicker.h b/indra/newview/lldirpicker.h
new file mode 100644
index 0000000000..70e124f6c4
--- /dev/null
+++ b/indra/newview/lldirpicker.h
@@ -0,0 +1,84 @@
+/**
+ * @dir lldirpicker.h
+ * @brief OS-specific dir picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// OS specific dir selection dialog. This is implemented as a
+// singleton class, so call the instance() method to get the working
+// instance.
+
+#ifndef LL_LLDIRPICKER_H
+#define LL_LLDIRPICKER_H
+
+#include "stdtypes.h"
+
+#if LL_DARWIN
+#include <Carbon/Carbon.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+#include <vector>
+#include "llstring.h"
+
+#endif
+
+// Need commdlg.h for OPENDIRNAMEA
+#ifdef LL_WINDOWS
+#include <commdlg.h>
+#endif
+
+class LLDirPicker
+{
+public:
+ // calling this before main() is undefined
+ static LLDirPicker& instance( void ) { return sInstance; }
+
+ BOOL getDir(LLString* filename);
+ LLString getDirName();
+
+ // clear any lists of buffers or whatever, and make sure the dir
+ // picker isn't locked.
+ void reset();
+
+private:
+ enum
+ {
+ SINGLE_DIRNAME_BUFFER_SIZE = 1024,
+ //DIRNAME_BUFFER_SIZE = 65536
+ DIRNAME_BUFFER_SIZE = 65000
+ };
+
+ void buildDirname( void );
+
+#if LL_WINDOWS
+//FIXME
+#endif
+
+#if LL_DARWIN
+ NavDialogCreationOptions mNavOptions;
+ static pascal void doNavCallbackEvent(NavEventCallbackMessage callBackSelector,
+ NavCBRecPtr callBackParms, void* callBackUD);
+ OSStatus doNavChooseDialog();
+
+#endif
+
+ char mDirs[DIRNAME_BUFFER_SIZE];
+ LLString* mFileName;
+ LLString mDir;
+ BOOL mLocked;
+
+ static LLDirPicker sInstance;
+
+public:
+ // don't call these directly please.
+ LLDirPicker();
+ ~LLDirPicker();
+};
+
+#endif
diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp
new file mode 100644
index 0000000000..a198886d37
--- /dev/null
+++ b/indra/newview/lldrawable.cpp
@@ -0,0 +1,1320 @@
+/**
+ * @file lldrawable.cpp
+ * @brief LLDrawable class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawable.h"
+
+// library includes
+#include "material_codes.h"
+
+// viewer includes
+#include "llagparray.h"
+#include "llagparray.inl"
+#include "llcriticaldamp.h"
+#include "llface.h"
+#include "lllightconstants.h"
+#include "llsky.h"
+#include "llsurfacepatch.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "llvolume.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "llvosurfacepatch.h" // for debugging
+#include "llworld.h"
+#include "pipeline.h"
+#include "llspatialpartition.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+
+const F32 MIN_INTERPOLATE_DISTANCE_SQUARED = 0.001f * 0.001f;
+const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f;
+const F32 OBJECT_DAMPING_TIME_CONSTANT = 0.06f;
+const F32 MIN_SHADOW_CASTER_RADIUS = 2.0f;
+
+////////////////////////
+//
+// Inline implementations.
+//
+//
+
+
+
+//////////////////////////////
+//
+// Drawable code
+//
+//
+
+// static
+U32 LLDrawable::sCurVisible = 0;
+U32 LLDrawable::sNumZombieDrawables = 0;
+F32 LLDrawable::sCurPixelAngle = 0;
+LLDynamicArrayPtr<LLPointer<LLDrawable> > LLDrawable::sDeadList;
+
+// static
+void LLDrawable::incrementVisible()
+{
+ sCurVisible++;
+ sCurPixelAngle = (F32) gViewerWindow->getWindowDisplayHeight()/gCamera->getView();
+}
+void LLDrawable::init()
+{
+ // mXform
+ mParent = NULL;
+ mRenderType = 0;
+ mCurrentScale = LLVector3(1,1,1);
+ mDistanceWRTCamera = 0.0f;
+ // mUVRect
+ mUVZ = 0.f;
+ // mLightSet
+ // mBlockSet
+ // mSavePos
+ mQuietCount = 0;
+
+ mState = 0;
+ mVObjp = NULL;
+ // mFaces
+ mSpatialGroupp = NULL;
+ mSpatialGroupOffset = -1;
+ mVisible = 0;
+ mRadius = 0.f;
+ mSunShadowFactor = 1.f;
+
+ mGeneration = -1;
+ mBinRadius = 1.f;
+ mSpatialBridge = NULL;
+}
+
+// static
+void LLDrawable::initClass()
+{
+}
+
+
+void LLDrawable::destroy()
+{
+ if (isDead())
+ {
+ sNumZombieDrawables--;
+ }
+
+ std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
+ mFaces.clear();
+
+ /*
+ if (!(sNumZombieDrawables % 10))
+ {
+ llinfos << "- Zombie drawables: " << sNumZombieDrawables << llendl;
+ }
+ */
+}
+
+void LLDrawable::markDead()
+{
+ if (isDead())
+ {
+ llwarns << "Warning! Marking dead multiple times!" << llendl;
+ return;
+ }
+
+ if (mSpatialBridge)
+ {
+ mSpatialBridge->markDead();
+ mSpatialBridge = NULL;
+ }
+
+ sNumZombieDrawables++;
+
+ // We're dead. Free up all of our references to other objects
+ setState(DEAD);
+ cleanupReferences();
+// sDeadList.put(this);
+}
+
+LLVOVolume* LLDrawable::getVOVolume() const
+{
+ LLViewerObject* objectp = mVObjp;
+ if ( !isDead() && objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ return ((LLVOVolume*)objectp);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLDrawable::isLight() const
+{
+ LLViewerObject* objectp = mVObjp;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME) && !isDead())
+ {
+ return ((LLVOVolume*)objectp)->getIsLight();
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLDrawable::clearLightSet()
+{
+ // Remove this object from any object which has it as a light
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ LLDrawable *targetp = *iter;
+ if (targetp != this && !targetp->isDead())
+ {
+ targetp->mLightSet.erase(this);
+ gPipeline.markRelight(targetp);
+ }
+ }
+ mLightSet.clear();
+}
+
+void LLDrawable::cleanupReferences()
+{
+ LLFastTimer t(LLFastTimer::FTM_PIPELINE);
+
+ std::for_each(mFaces.begin(), mFaces.end(), DeletePointer());
+ mFaces.clear();
+
+ clearLightSet();
+
+ gObjectList.removeDrawable(this);
+
+ mBlockSet.clear();
+
+ gPipeline.unlinkDrawable(this);
+
+ // Cleanup references to other objects
+ mVObjp = NULL;
+ mParent = NULL;
+}
+
+void LLDrawable::cleanupDeadDrawables()
+{
+ /*
+ S32 i;
+ for (i = 0; i < sDeadList.count(); i++)
+ {
+ if (sDeadList[i]->getNumRefs() > 1)
+ {
+ llwarns << "Dead drawable has " << sDeadList[i]->getNumRefs() << " remaining refs" << llendl;
+ gPipeline.findReferences(sDeadList[i]);
+ }
+ }
+ */
+ sDeadList.reset();
+}
+
+S32 LLDrawable::findReferences(LLDrawable *drawablep)
+{
+ S32 count = 0;
+ if (mLightSet.count(drawablep) > 0)
+ {
+ llinfos << this << ": lightset reference" << llendl;
+ count++;
+ }
+ if (mBlockSet.count(drawablep) > 0)
+ {
+ llinfos << this << ": blockset reference" << llendl;
+ count++;
+ }
+ if (mParent == drawablep)
+ {
+ llinfos << this << ": parent reference" << llendl;
+ count++;
+ }
+ return count;
+}
+
+#if 0
+// SJB: This is SLOW, so we don't want to allow it (we don't currently use it)
+void LLDrawable::removeFace(const S32 i)
+{
+ LLFace *face= mFaces[i];
+
+ if (face)
+ {
+ mFaces.erase(mFaces.begin() + i);
+ delete face;
+ }
+}
+#endif
+
+LLFace* LLDrawable::addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+
+ LLFace *face = new LLFace(this, mVObjp);
+
+ if (face)
+ {
+ mFaces.push_back(face);
+ face->setPool(poolp, texturep);
+
+ if (shared_geom)
+ {
+ face->setState(LLFace::SHARED_GEOM);
+ }
+ else if (!isVisible())
+ {
+ face->setState(LLFace::BACKLIST);
+ }
+ if (isState(UNLIT))
+ {
+ face->setState(LLFace::FULLBRIGHT);
+ }
+ }
+ return face;
+}
+
+void LLDrawable::setNumFaces(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+{
+ if (newFaces == (S32)mFaces.size())
+ {
+ return;
+ }
+ else if (newFaces < (S32)mFaces.size())
+ {
+ std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
+ mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
+ }
+ else // (newFaces > mFaces.size())
+ {
+ mFaces.reserve(newFaces);
+ for (int i = mFaces.size(); i<newFaces; i++)
+ {
+ addFace(poolp, texturep);
+ }
+ }
+}
+
+void LLDrawable::setNumFacesFast(const S32 newFaces, LLDrawPool *poolp, LLViewerImage *texturep)
+{
+ if (newFaces <= (S32)mFaces.size() && newFaces >= (S32)mFaces.size()/2)
+ {
+ return;
+ }
+ else if (newFaces < (S32)mFaces.size())
+ {
+ std::for_each(mFaces.begin() + newFaces, mFaces.end(), DeletePointer());
+ mFaces.erase(mFaces.begin() + newFaces, mFaces.end());
+ }
+ else // (newFaces > mFaces.size())
+ {
+ mFaces.reserve(newFaces);
+ for (int i = mFaces.size(); i<newFaces; i++)
+ {
+ addFace(poolp, texturep);
+ }
+ }
+}
+
+void LLDrawable::mergeFaces(LLDrawable* src)
+{
+ U32 face_count = mFaces.size() + src->mFaces.size();
+
+ mFaces.reserve(face_count);
+ for (U32 i = 0; i < src->mFaces.size(); i++)
+ {
+ LLFace* facep = src->mFaces[i];
+ facep->setDrawable(this);
+ mFaces.push_back(facep);
+ }
+ src->mFaces.clear();
+}
+
+void LLDrawable::deleteFaces(S32 offset, S32 count)
+{
+ face_list_t::iterator face_begin = mFaces.begin() + offset;
+ face_list_t::iterator face_end = face_begin + count;
+ std::for_each(face_begin, face_end, DeletePointer());
+ mFaces.erase(face_begin, face_end);
+}
+
+void LLDrawable::update()
+{
+ llerrs << "Shouldn't be called!" << llendl;
+}
+
+
+void LLDrawable::updateMaterial()
+{
+}
+
+void LLDrawable::makeActive()
+{
+ if (!isState(ACTIVE)) // && mGeneration > 0)
+ {
+ setState(ACTIVE);
+
+ if (!isRoot() && !mParent->isActive())
+ {
+ mParent->makeActive();
+ }
+
+ gPipeline.setActive(this, TRUE);
+
+ //all child objects must also be active
+ for (U32 i = 0; i < getChildCount(); i++)
+ {
+ getChild(i)->makeActive();
+ }
+
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ if (mVObjp->getVolume()->getPathType() == LL_PCODE_PATH_FLEXIBLE)
+ {
+ return;
+ }
+ }
+
+ clearState(LLDrawable::LIGHTING_BUILT);
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ }
+ mQuietCount = 0;
+}
+
+
+void LLDrawable::makeStatic()
+{
+ if (isState(ACTIVE))
+ {
+ clearState(ACTIVE);
+ gPipeline.setActive(this, FALSE);
+
+ if (mParent.notNull() && mParent->isActive())
+ {
+ llerrs << "Drawable became static with active parent!" << llendl;
+ }
+
+ S32 child_count = mVObjp->mChildList.size();
+ for (S32 child_num = 0; child_num < child_count; child_num++)
+ {
+ LLDrawable* child_drawable = mVObjp->mChildList[child_num]->mDrawable;
+ if (child_drawable)
+ {
+ child_drawable->makeStatic();
+ }
+ }
+
+ gPipeline.markRelight(this);
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ if (mSpatialBridge)
+ {
+ mSpatialBridge->markDead();
+ setSpatialBridge(NULL);
+ }
+ }
+}
+
+// Returns "distance" between target destination and resulting xfrom
+F32 LLDrawable::updateXform(BOOL undamped)
+{
+ BOOL damped = !undamped;
+
+ // Position
+ LLVector3 old_pos(mXform.getPosition());
+ LLVector3 target_pos;
+ if (mXform.isRoot())
+ {
+ // get root position in your agent's region
+ target_pos = mVObjp->getPositionAgent();
+ }
+ else
+ {
+ // parent-relative position
+ target_pos = mVObjp->getPosition();
+ }
+
+ // Rotation
+ LLQuaternion old_rot(mXform.getRotation());
+ LLQuaternion target_rot = mVObjp->getRotation();
+ //scaling
+ LLVector3 target_scale = mVObjp->getScale();
+ LLVector3 old_scale = mCurrentScale;
+ LLVector3 dest_scale = target_scale;
+
+ // Damping
+ F32 dist_squared = 0.f;
+ F32 scaled = 0.f;
+
+ if (damped && mDistanceWRTCamera > 0.0f)
+ {
+ F32 lerp_amt = llclamp(LLCriticalDamp::getInterpolant(OBJECT_DAMPING_TIME_CONSTANT), 0.f, 1.f);
+ LLVector3 new_pos = lerp(old_pos, target_pos, lerp_amt);
+ dist_squared = dist_vec_squared(new_pos, target_pos);
+
+ LLQuaternion new_rot = nlerp(lerp_amt, old_rot, target_rot);
+ dist_squared += (1.f - dot(new_rot, target_rot)) * 10.f;
+
+ LLVector3 new_scale = lerp(old_scale, target_scale, lerp_amt);
+ scaled = dist_vec_squared(new_scale, target_scale);
+
+ dist_squared += scaled;
+ F32 camdist2 = (mDistanceWRTCamera * mDistanceWRTCamera);
+ if ((dist_squared >= MIN_INTERPOLATE_DISTANCE_SQUARED * camdist2) &&
+ (dist_squared <= MAX_INTERPOLATE_DISTANCE_SQUARED))
+ {
+ // interpolate
+ target_pos = new_pos;
+ target_rot = new_rot;
+ target_scale = new_scale;
+
+ if (scaled >= MIN_INTERPOLATE_DISTANCE_SQUARED)
+ {
+ //scaling requires an immediate rebuild
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ }
+ else
+ {
+ // snap to final position
+ dist_squared = 0.0f;
+ }
+ }
+
+ // Update
+ mXform.setPosition(target_pos);
+ mXform.setRotation(target_rot);
+ mXform.setScale(LLVector3(1,1,1)); //no scale in drawable transforms (IT'S A RULE!)
+ mXform.updateMatrix();
+
+ mCurrentScale = target_scale;
+
+ if (!getVOVolume())
+ {
+ movePartition();
+ }
+ else if (mSpatialBridge)
+ {
+ gPipeline.markMoved(mSpatialBridge, FALSE);
+ }
+ else
+ {
+ //a child prim moved and needs its verts regenerated
+ gPipeline.markRebuild(this, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return dist_squared;
+}
+
+void LLDrawable::setRadius(F32 radius)
+{
+ if (mRadius != radius)
+ {
+ mRadius = radius;
+ updateBinRadius();
+ if (!getVOVolume())
+ {
+ movePartition();
+ }
+ }
+}
+
+void LLDrawable::moveUpdatePipeline(BOOL moved)
+{
+ makeActive();
+
+ // Update the face centers.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ getFace(i)->updateCenterAgent();
+ }
+
+ if (moved || !isState(LLDrawable::BUILT)) // moved since last frame
+ {
+ LLVector3 tmp = mSavePos - mXform.getPositionW();
+ F32 dist = tmp.magVecSquared(); // moved since last _update_
+
+ if (dist > 1.0f || !isState(LLDrawable::BUILT) || isLight())
+ {
+ mSavePos = mXform.getPositionW();
+ gPipeline.markRelight(this);
+ }
+ }
+}
+
+void LLDrawable::movePartition()
+{
+ if (getSpatialGroup() || getVOVolume())
+ {
+ LLSpatialPartition* part = getSpatialPartition();
+ if (part)
+ {
+ part->move(this, getSpatialGroup());
+ }
+ }
+}
+
+BOOL LLDrawable::updateMove()
+{
+ if (isDead())
+ {
+ llwarns << "Update move on dead drawable!" << llendl;
+ return TRUE;
+ }
+
+ if (mVObjp.isNull())
+ {
+ return FALSE;
+ }
+
+ makeActive();
+
+ BOOL done;
+
+ if (isState(MOVE_UNDAMPED))
+ {
+ done = updateMoveUndamped();
+ }
+ else
+ {
+ done = updateMoveDamped();
+ }
+ return done;
+}
+
+BOOL LLDrawable::updateMoveUndamped()
+{
+ F32 dist_squared = updateXform(TRUE);
+
+ mGeneration++;
+
+ if (!isState(LLDrawable::INVISIBLE))
+ {
+ BOOL moved = (dist_squared > 0.001f && dist_squared < 255.99f);
+ moveUpdatePipeline(moved);
+ mVObjp->updateText();
+ }
+
+ mVObjp->clearChanged(LLXform::MOVED);
+
+ return TRUE;
+}
+
+BOOL LLDrawable::updateMoveDamped()
+{
+ F32 dist_squared = updateXform(FALSE);
+
+ mGeneration++;
+
+ if (!isState(LLDrawable::INVISIBLE))
+ {
+ BOOL moved = (dist_squared > 0.001f && dist_squared < 128.0f);
+ moveUpdatePipeline(moved);
+ mVObjp->updateText();
+ }
+
+ BOOL done_moving = (dist_squared == 0.0f) ? TRUE : FALSE;
+
+ if (done_moving)
+ {
+ mVObjp->clearChanged(LLXform::MOVED);
+ }
+
+ return done_moving;
+}
+
+void LLDrawable::updateDistance(LLCamera& camera)
+{
+ if (mVObjp->isHUDAttachment())
+ {
+ mDistanceWRTCamera = 1.0f;
+ if (sCurVisible % 16 == 0)
+ {
+ mVObjp->updateLOD();
+ }
+ return;
+ }
+
+ LLVector3 pos(getPositionGroup());
+
+ pos -= camera.getOrigin();
+ mDistanceWRTCamera = pos.magVec();
+
+ //switch LOD with the spatial group to avoid artifacts
+ LLSpatialGroup* sg = getSpatialGroup();
+ if (!sg || sg->changeLOD())
+ {
+ mVObjp->updateLOD();
+ }
+}
+
+void LLDrawable::updateTexture()
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+
+ if (isDead())
+ {
+ llwarns << "Dead drawable updating texture!" << llendl;
+ return;
+ }
+
+ //FIXME: this updates textures on all faces in this drawable, not just the viewer object we care about
+ if (mVObjp->getNumTEs())
+ {
+ // For each face in this drawable, change the drawpool if necessary.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = mFaces[i];
+ U32 pool_type = facep->getPool()->getType();
+
+ if ((pool_type == LLDrawPool::POOL_SIMPLE) ||
+ (pool_type == LLDrawPool::POOL_ALPHA) ||
+ (pool_type == LLDrawPool::POOL_HUD) ||
+ (pool_type == LLDrawPool::POOL_MEDIA) ||
+ (pool_type == LLDrawPool::POOL_BUMP))
+ {
+ LLViewerObject* objp = facep->getViewerObject();
+ S32 te_offset = facep->getTEOffset();
+
+ if (te_offset >= objp->getNumTEs()) // Shouldn't happen
+ {
+ llwarns << "TE offsets don't match!" << llendl;
+ facep->setTEOffset(-1);
+ continue;
+ }
+
+ LLDrawPool* poolp = NULL;
+ LLViewerImage* imagep = (te_offset >= 0) ? objp->getTEImage(te_offset) : facep->getTexture();
+ if (facep->isState(LLFace::HUD_RENDER))
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
+ }
+ else if (te_offset >= 0)
+ {
+ // This face actually uses texture entries...
+ const LLTextureEntry* te = facep->getTextureEntry();
+ poolp = LLPipeline::getPoolFromTE(te, imagep);
+ }
+ else
+ {
+ // No texture entry for this face.
+ if (!imagep)
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, NULL);
+ }
+ else if ((imagep->getComponents() == 4) || (imagep->getComponents() == 2))
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ }
+ else
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
+ }
+ }
+ facep->setPool(poolp, imagep);
+ }
+ }
+ }
+}
+
+
+BOOL LLDrawable::updateGeometry(BOOL priority)
+{
+ llassert(mVObjp.notNull());
+ BOOL res = mVObjp->updateGeometry(this);
+ if (isState(REBUILD_LIGHTING))
+ {
+ updateLighting(priority ? FALSE : TRUE); // only do actual lighting for non priority updates
+ if (priority)
+ {
+ gPipeline.markRelight(this); // schedule non priority update
+ }
+ else
+ {
+ clearState(REBUILD_LIGHTING);
+ }
+ }
+ return res;
+}
+
+void LLDrawable::shiftPos(const LLVector3 &shift_vector)
+{
+ if (isDead())
+ {
+ llwarns << "Shifting dead drawable" << llendl;
+ return;
+ }
+
+ if (mParent)
+ {
+ mXform.setPosition(mVObjp->getPosition());
+ }
+ else
+ {
+ mXform.setPosition(mVObjp->getPositionAgent());
+ }
+
+ mXform.setRotation(mVObjp->getRotation());
+ mXform.setScale(1,1,1);
+ mXform.updateMatrix();
+
+ if (isStatic() || //DP FIXME: don't know why this is happening, but
+ //some terrain patches are becoming active
+ //(earth quake, maybe?)
+ getRenderType() == LLPipeline::RENDER_TYPE_TERRAIN)
+ {
+ LLStrider<LLVector3> verticesp;
+
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = getFace(i);
+ facep->mCenterAgent += shift_vector;
+ facep->mExtents[0] += shift_vector;
+ facep->mExtents[1] += shift_vector;
+
+ if (facep->hasGeometry() && !facep->isState(LLFace::SHARED_GEOM))
+ {
+ S32 index = facep->getVertices(verticesp);
+ if (index >= 0)
+ {
+ S32 vertex_count = facep->getGeomCount();
+ for (S32 j = 0; j < vertex_count; j++)
+ {
+ *verticesp += shift_vector;
+ verticesp++;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // Update the face centers.
+ for (S32 i = 0; i < getNumFaces(); i++)
+ {
+ LLFace *facep = getFace(i);
+ facep->mCenterAgent += shift_vector;
+ }
+ }
+
+ //update spatial extents
+ if (!getVOVolume() || isStatic())
+ {
+ mExtents[0] += shift_vector;
+ mExtents[1] += shift_vector;
+ mPositionGroup += LLVector3d(shift_vector);
+ }
+ else if (mSpatialBridge)
+ {
+ mSpatialBridge->shiftPos(shift_vector);
+ }
+
+ mSavePos = mXform.getPositionW();
+
+ mVObjp->onShift(shift_vector);
+}
+
+const LLVector3& LLDrawable::getBounds(LLVector3& min, LLVector3& max) const
+{
+ mXform.getMinMax(min,max);
+ return mXform.getPositionW();
+}
+
+const LLVector3* LLDrawable::getSpatialExtents() const
+{
+ return mExtents;
+}
+
+void LLDrawable::setSpatialExtents(LLVector3 min, LLVector3 max)
+{
+ LLVector3 size = max - min;
+ mExtents[0] = min;
+ mExtents[1] = max;
+}
+
+void LLDrawable::setPositionGroup(const LLVector3d& pos)
+{
+ mPositionGroup.setVec(pos);
+}
+
+void LLDrawable::updateSpatialExtents()
+{
+ if (mVObjp)
+ {
+ mVObjp->updateSpatialExtents(mExtents[0], mExtents[1]);
+ }
+
+ if (mSpatialBridge.notNull())
+ {
+ mPositionGroup.setVec(0,0,0);
+ }
+}
+
+
+void LLDrawable::updateBinRadius()
+{
+ S32 binLOD = mVObjp ? mVObjp->getLOD() : 2;
+ static F64 detail_bins[] = { 8, 4, 2, 1 };
+ F32 radius = getVOVolume() && isStatic() ?
+ (mExtents[1]-mExtents[0]).magVec() : getRadius();
+ mBinRadius = detail_bins[binLOD] * llmax((F64) radius, (3-binLOD)*0.25);
+}
+
+void LLDrawable::updateLightSet()
+{
+ if (isDead())
+ {
+ llwarns << "Updating light set for dead drawable!" << llendl;
+ return;
+ }
+
+ LLVOVolume* light = getVOVolume();
+ if (isLight() && light)
+ {
+ // mLightSet points to lit objects
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ gPipeline.markRelight(*iter);
+ }
+ mLightSet.clear();
+ gPipeline.mObjectPartition->getObjects(getPositionAgent(), light->getLightRadius(), mLightSet);
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ gPipeline.markRelight(*iter);
+ }
+ }
+ else
+ {
+ // mLightSet points to nearby lights
+ mLightSet.clear();
+ gPipeline.mObjectPartition->getLights(getPositionAgent(), getRadius(), mLightSet);
+ const U32 max_lights = 16;
+ if (mLightSet.size() > max_lights)
+ {
+ typedef std::set<std::pair<F32,LLPointer<LLDrawable> > > sorted_pair_set_t;
+ sorted_pair_set_t sorted_set;
+ for (drawable_set_t::iterator iter = mLightSet.begin(); iter != mLightSet.end(); iter++)
+ {
+ LLDrawable* drawable = *iter;
+ LLVector3 dvec = drawable->getPositionAgent() - getPositionAgent();
+ F32 dist2 = dvec.magVecSquared();
+ sorted_set.insert(std::make_pair(dist2, drawable));
+ }
+ mLightSet.clear();
+ S32 count = 0;
+ for (sorted_pair_set_t::iterator iter = sorted_set.begin(); iter != sorted_set.end(); iter++)
+ {
+ if (++count > 16)
+ break;
+ mLightSet.insert((*iter).second);
+ }
+ }
+ }
+}
+
+void LLDrawable::updateSpecialHoverCursor(BOOL enabled)
+{
+ // TODO: maintain a list of objects that have special
+ // hover cursors, then use that list for per-frame
+ // hover cursor selection. JC
+}
+
+BOOL LLDrawable::updateLighting(BOOL do_lighting)
+{
+ if (do_lighting)
+ {
+ if (gPipeline.getLightingDetail() >= 2 && (getLit() || isLight()))
+ {
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_LIGHTS);
+ updateLightSet();
+ do_lighting = isLight() ? FALSE : TRUE;
+ }
+ else
+ {
+ do_lighting = FALSE;
+ }
+ }
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEO_LIGHT);
+ if (mVObjp->updateLighting(do_lighting))
+ {
+ setState(LIGHTING_BUILT);
+ }
+ }
+
+ return TRUE;
+}
+
+void LLDrawable::applyLightsAsPoint(LLColor4& result)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ LLVector3 point_agent(getPositionAgent());
+ LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
+
+ F32 sun_int = normal * gPipeline.mSunDir;
+ LLColor4 color(gSky.getTotalAmbientColor());
+ color += gPipeline.mSunDiffuse * sun_int;
+
+ for (drawable_set_t::iterator iter = mLightSet.begin();
+ iter != mLightSet.end(); ++iter)
+ {
+ LLDrawable* drawable = *iter;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(point_agent, normal, light_color);
+ color += light_color;
+ }
+
+ // Clamp the color...
+ color.mV[0] = llmax(color.mV[0], 0.f);
+ color.mV[1] = llmax(color.mV[1], 0.f);
+ color.mV[2] = llmax(color.mV[2], 0.f);
+
+ F32 max_color = llmax(color.mV[0], color.mV[1], color.mV[2]);
+ if (max_color > 1.f)
+ {
+ color *= 1.f/max_color;
+ }
+
+ result = color;
+}
+
+F32 LLDrawable::getVisibilityRadius() const
+{
+ if (isDead())
+ {
+ return 0.f;
+ }
+ else if (isLight())
+ {
+ return llmax(getRadius(), getVOVolume()->getLightRadius());
+ }
+ else
+ {
+ return getRadius();
+ }
+}
+
+void LLDrawable::updateUVMinMax()
+{
+}
+
+void LLDrawable::setSpatialGroup(LLSpatialGroup *groupp, const S32 offset)
+{
+ mSpatialGroupp = groupp;
+
+ if (mSpatialGroupp)
+ {
+ mSpatialGroupOffset = offset;
+ }
+ else
+ {
+ mSpatialGroupOffset = -1;
+ }
+}
+
+LLSpatialPartition* LLDrawable::getSpatialPartition()
+{
+ LLSpatialPartition* retval = NULL;
+
+ if (mVObjp->isHUDAttachment())
+ { //HUD attachments don't get space partitioned
+ return NULL;
+ }
+
+ if (!mVObjp ||
+ !getVOVolume() ||
+ isStatic())
+ {
+ retval = gPipeline.mObjectPartition;
+ }
+
+ //must be an active volume
+ if (!retval && isRoot())
+ {
+ if (!mSpatialBridge)
+ {
+ setSpatialBridge(new LLSpatialBridge(this));
+ }
+ return mSpatialBridge->asPartition();
+ }
+ else if (!retval)
+ {
+ retval = getParent()->getSpatialPartition();
+ }
+
+ if (retval && mSpatialBridge.notNull())
+ {
+ mSpatialBridge->markDead();
+ setSpatialBridge(NULL);
+ }
+
+ return retval;
+}
+
+//=======================================
+// Spatial Partition Bridging Drawable
+//=======================================
+
+LLSpatialBridge::LLSpatialBridge(LLDrawable* root)
+{
+ mDrawable = root;
+ root->setSpatialBridge(this);
+
+ mRenderType = mDrawable->mRenderType; //w00! magic!
+
+ mOctree->balance();
+
+ gPipeline.mObjectPartition->put(this);
+}
+
+LLSpatialBridge::~LLSpatialBridge()
+{
+ if (getSpatialGroup())
+ {
+ gPipeline.mObjectPartition->remove(this, getSpatialGroup());
+ }
+}
+
+void LLSpatialBridge::updateSpatialExtents()
+{
+ LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0);
+
+ if (mOctree->getChildCount() > 0)
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ root->rebound();
+ }
+
+ LLXformMatrix* mat = mDrawable->getXform();
+
+ LLVector3 offset = root->mBounds[0];
+ LLVector3 size = root->mBounds[1];
+
+ LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
+ LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+
+ offset *= rotation;
+ center += offset;
+
+ LLVector3 v[4];
+ //get 4 corners of bounding box
+ v[0] = (size * rotation);
+ v[1] = (LLVector3(-size.mV[0], -size.mV[1], size.mV[2]) * rotation);
+ v[2] = (LLVector3(size.mV[0], -size.mV[1], -size.mV[2]) * rotation);
+ v[3] = (LLVector3(-size.mV[0], size.mV[1], -size.mV[2]) * rotation);
+
+ LLVector3& newMin = mExtents[0];
+ LLVector3& newMax = mExtents[1];
+
+ newMin = newMax = center;
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ for (U32 j = 0; j < 3; j++)
+ {
+ F32 delta = fabsf(v[i].mV[j]);
+ F32 min = center.mV[j] - delta;
+ F32 max = center.mV[j] + delta;
+
+ if (min < newMin.mV[j])
+ {
+ newMin.mV[j] = min;
+ }
+
+ if (max > newMax.mV[j])
+ {
+ newMax.mV[j] = max;
+ }
+ }
+ }
+
+ mPositionGroup.setVec((newMin + newMax) * 0.5f);
+ updateBinRadius();
+}
+
+void LLSpatialBridge::updateBinRadius()
+{
+ F32 rad = ((mExtents[1]-mExtents[0])*0.5f).magVec();
+ mBinRadius = llmax(rad, 2.f);
+ mRadius = rad;
+}
+
+LLCamera LLSpatialBridge::transformCamera(LLCamera& camera)
+{
+ LLCamera ret = camera;
+ LLXformMatrix* mat = mDrawable->getXform();
+ LLVector3 center = LLVector3(0,0,0) * mat->getWorldMatrix();
+ //LLQuaternion rotation = LLQuaternion(mat->getWorldMatrix());
+
+ //ret.rotate(~mat->getRotation());
+ LLVector3 delta = ret.getOrigin() - center;
+ delta *= ~mat->getRotation();
+ ret.setOrigin(delta);
+
+ return ret;
+}
+
+void LLDrawable::setVisible(LLCamera& camera, std::vector<LLDrawable*>* results, BOOL for_select)
+{
+ mVisible = sCurVisible;
+}
+
+void LLSpatialBridge::setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results, BOOL for_select)
+{
+ LLVector3 center = (mExtents[0] + mExtents[1]) * 0.5f;
+ LLVector3 size = (mExtents[1]-mExtents[0]) * 0.5f;
+
+ if (camera_in.AABBInFrustum(center, size))
+ {
+ LLVector3 lookAt = center - camera_in.getOrigin();
+ F32 distSqr = lookAt.magVecSquared();
+ F32 objRad = size.magVecSquared();
+
+ if (objRad/distSqr < SG_MIN_DIST_RATIO*4)
+ {
+ return;
+ }
+
+ LLDrawable::setVisible(camera_in);
+
+ if (for_select)
+ {
+ results->push_back(mDrawable);
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ results->push_back(mDrawable->getChild(i));
+ }
+ }
+ else
+ {
+ const LLVector3* extents = mDrawable->getSpatialExtents();
+ objRad = mDrawable->getRadius();
+ objRad *= objRad;
+
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(mDrawable, camera_in);
+ }
+
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ LLDrawable* child = mDrawable->getChild(i);
+ extents = child->getSpatialExtents();
+ objRad = child->getRadius();
+ objRad *= objRad;
+
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(mDrawable->getChild(i), camera_in);
+ }
+ }
+ }
+ }
+}
+
+void LLSpatialBridge::updateDistance(LLCamera& camera_in)
+{
+ LLCamera camera = transformCamera(camera_in);
+
+ mDrawable->updateDistance(camera);
+
+ for (U32 i = 0; i < mDrawable->getChildCount(); ++i)
+ {
+ mDrawable->getChild(i)->updateDistance(camera);
+ }
+}
+
+void LLSpatialBridge::makeActive()
+{ //it is an error to make a spatial bridge active (it's already active)
+ llerrs << "makeActive called on spatial bridge" << llendl;
+}
+
+void LLSpatialBridge::makeStatic()
+{
+ llerrs << "makeStatic called on spatial bridge" << llendl;
+}
+
+void LLSpatialBridge::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
+{
+ LLSpatialPartition::move(drawablep, curp, immediate);
+ gPipeline.markMoved(this, FALSE);
+}
+
+BOOL LLSpatialBridge::updateMove()
+{
+ mOctree->balance();
+ gPipeline.mObjectPartition->move(this, getSpatialGroup(), TRUE);
+ return TRUE;
+}
+
+void LLSpatialBridge::shiftPos(const LLVector3& vec)
+{
+ mExtents[0] += vec;
+ mExtents[1] += vec;
+ mPositionGroup += LLVector3d(vec);
+}
+
+void LLSpatialBridge::cleanupReferences()
+{
+ LLDrawable::cleanupReferences();
+ if (mDrawable)
+ {
+ mDrawable->setSpatialGroup(NULL, -1);
+ for (U32 i = 0; i < mDrawable->getChildCount(); i++)
+ {
+ LLDrawable* drawable = mDrawable->getChild(i);
+ if (drawable && drawable->getVOVolume())
+ {
+ drawable->setSpatialGroup(NULL, -1);
+ }
+ }
+
+ LLDrawable* drawablep = mDrawable;
+ mDrawable = NULL;
+ drawablep->setSpatialBridge(NULL);
+ }
+}
+
+const LLVector3 LLDrawable::getPositionAgent() const
+{
+ if (getVOVolume())
+ {
+ if (isActive())
+ {
+ if (isRoot())
+ {
+ return LLVector3(0,0,0) * getWorldMatrix();
+ }
+ else
+ {
+ return mVObjp->getPosition() * getParent()->getWorldMatrix();
+ }
+ }
+ else
+ {
+ return mVObjp->getPositionAgent();
+ }
+ }
+ else
+ {
+ return getWorldPosition();
+ }
+}
+
diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h
new file mode 100644
index 0000000000..cb9f970106
--- /dev/null
+++ b/indra/newview/lldrawable.h
@@ -0,0 +1,311 @@
+/**
+ * @file lldrawable.h
+ * @brief LLDrawable class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_DRAWABLE_H
+#define LL_DRAWABLE_H
+
+#include <vector>
+#include <map>
+
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "v4coloru.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "llprimitive.h"
+#include "llviewerimage.h"
+#include "lldarray.h"
+#include "llstat.h"
+#include "llviewerobject.h"
+#include "llrect.h"
+
+class LLDrawPool;
+class LLDrawable;
+class LLFace;
+class LLSpatialGroup;
+class LLSpatialBridge;
+class LLSpatialPartition;
+class LLVOVolume;
+
+extern F32 gFrameTimeSeconds;
+
+// Can have multiple silhouettes for each object
+const U32 SILHOUETTE_HIGHLIGHT = 0;
+
+
+// All data for new renderer goes into this class.
+class LLDrawable : public LLRefCount
+{
+public:
+ static void initClass();
+
+ LLDrawable() { init(); }
+ MEM_TYPE_NEW(LLMemType::MTYPE_DRAWABLE);
+
+ void markDead(); // Mark this drawable as dead
+ BOOL isDead() const { return isState(DEAD); }
+ BOOL isNew() const { return !isState(BUILT); }
+
+ BOOL isLight() const;
+
+ BOOL isVisible() const { return (mVisible == sCurVisible); }
+ virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, BOOL for_select = FALSE);
+
+
+ const LLViewerRegion* getRegion() const { return mVObjp->getRegion(); }
+ const LLTextureEntry* getTextureEntry(U8 which) const { return mVObjp->getTE(which); }
+ LLViewerObject* getVObj() { return mVObjp; }
+ const LLViewerObject* getVObj() const { return mVObjp; }
+ LLVOVolume* getVOVolume() const; // cast mVObjp tp LLVOVolume if OK
+
+ const LLMatrix4& getWorldMatrix() const { return mXform.getWorldMatrix(); }
+ const LLMatrix4& getRenderMatrix() const { return isRoot() ? getWorldMatrix() : getParent()->getWorldMatrix(); }
+ const void setPosition(LLVector3 v) const { }
+ const LLVector3& getPosition() const { return mXform.getPosition(); }
+ const LLVector3& getWorldPosition() const { return mXform.getPositionW(); }
+ const LLVector3 getPositionAgent() const;
+ const LLVector3d& getPositionGroup() const { return mPositionGroup; }
+ const LLVector3& getScale() const { return mCurrentScale; }
+ const LLQuaternion& getWorldRotation() const { return mXform.getWorldRotation(); }
+ const LLQuaternion& getRotation() const { return mXform.getRotation(); }
+ const F32 getIntensity() const { return llmin(mXform.getScale().mV[0], 4.f); }
+ const S32 getLOD() const { return mVObjp ? mVObjp->getLOD() : 1; }
+ const F64 getBinRadius() const { return mBinRadius; }
+ void getMinMax(LLVector3& min,LLVector3& max) const { mXform.getMinMax(min,max); }
+ LLXformMatrix* getXform() { return &mXform; }
+
+ const U32 getState() const { return mState; }
+ const BOOL isState (U32 bits) const { return ((mState & bits) != 0); }
+ void setState (U32 bits) { mState |= bits; }
+ void clearState(U32 bits) { mState &= ~bits; }
+
+ BOOL isAvatar() const { return mVObjp.notNull() && mVObjp->isAvatar(); }
+ BOOL isRoot() const { return !mParent || mParent->isAvatar(); }
+ BOOL isSpatialRoot() const { return !mParent || mParent->isAvatar(); }
+ virtual BOOL isSpatialBridge() const { return FALSE; }
+ virtual LLSpatialPartition* asPartition() { return NULL; }
+ LLDrawable* getParent() const { return mParent; }
+ LLDrawable* getChild(U32 index) { return mVObjp->mChildList[index]->mDrawable; }
+ U32 getChildCount() { return mVObjp ? mVObjp->mChildList.size() : 0; }
+
+ // must set parent through LLViewerObject:: ()
+ //BOOL setParent(LLDrawable *parent);
+
+ inline LLFace* getFace(const S32 i) const;
+ inline S32 getNumFaces() const;
+
+ //void removeFace(const S32 i); // SJB: Avoid using this, it's slow
+ LLFace* addFace(LLDrawPool *poolp, LLViewerImage *texturep, const BOOL shared_geom = FALSE);
+ void deleteFaces(S32 offset, S32 count);
+ void setNumFaces(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
+ void setNumFacesFast(const S32 numFaces, LLDrawPool *poolp, LLViewerImage *texturep);
+ void mergeFaces(LLDrawable* src);
+
+ void init();
+ void destroy();
+
+ void update();
+ F32 updateXform(BOOL undamped);
+
+ virtual void makeActive();
+ virtual void makeStatic();
+
+ BOOL isActive() const { return isState(ACTIVE); }
+ BOOL isStatic() const { return !isActive(); }
+ virtual BOOL updateMove();
+ virtual void movePartition();
+
+ void updateTexture();
+ void updateMaterial();
+ virtual void updateDistance(LLCamera& camera);
+ BOOL updateGeometry(BOOL priority);
+ BOOL updateLighting(BOOL priority);
+ void updateLightSet();
+
+ F32 getSunShadowFactor() const { return mSunShadowFactor; }
+ void setSunShadowFactor(F32 factor) { mSunShadowFactor = factor; }
+ void applyLightsAsPoint(LLColor4& result);
+ void updateSpecialHoverCursor(BOOL enabled);
+
+ virtual void shiftPos(const LLVector3 &shift_vector);
+
+ S32 getGeneration() const { return mGeneration; }
+
+ BOOL getLit() const { return isState(UNLIT) ? FALSE : TRUE; }
+ void setLit(BOOL lit) { lit ? clearState(UNLIT) : setState(UNLIT); }
+
+ void clearLightSet();
+ virtual void cleanupReferences();
+
+ void setRadius(const F32 radius);
+ F32 getRadius() const { return mRadius; }
+ F32 getVisibilityRadius() const;
+
+ void updateUVMinMax(); // Updates the cache of sun space bounding box.
+
+ const LLVector3& getBounds(LLVector3& min, LLVector3& max) const;
+ virtual void updateSpatialExtents();
+ virtual void updateBinRadius();
+ const LLVector3* getSpatialExtents() const;
+ void setSpatialExtents(LLVector3 min, LLVector3 max);
+ void setPositionGroup(const LLVector3d& pos);
+ void setPositionGroup(const LLVector3& pos) { setPositionGroup(LLVector3d(pos)); }
+
+ void setRenderType(S32 type) { mRenderType = type; }
+ BOOL isRenderType(S32 type) { return mRenderType == type; }
+ S32 getRenderType() { return mRenderType; }
+
+ // Debugging methods
+ S32 findReferences(LLDrawable *drawablep); // Not const because of @#$! iterators...
+
+ void setSpatialGroup(LLSpatialGroup *groupp, const S32 offset);
+ LLSpatialGroup *getSpatialGroup() const { return mSpatialGroupp; }
+ LLSpatialPartition* getSpatialPartition();
+
+ // Statics
+ static void incrementVisible();
+ static void cleanupDeadDrawables();
+
+protected:
+ virtual ~LLDrawable() { destroy(); }
+ void moveUpdatePipeline(BOOL moved);
+ BOOL updateMoveDamped();
+ BOOL updateMoveUndamped();
+
+public:
+ friend class LLPipeline;
+ friend class LLDrawPool;
+ friend class LLSpatialBridge;
+
+ typedef std::set<LLPointer<LLDrawable> > drawable_set_t;
+ typedef std::vector<LLPointer<LLDrawable> > drawable_vector_t;
+ typedef std::list<LLPointer<LLDrawable> > drawable_list_t;
+
+ struct CompareDistanceGreater
+ {
+ bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
+ {
+ return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
+ }
+ };
+
+ struct CompareDistanceGreaterVisibleFirst
+ {
+ bool operator()(const LLDrawable* const& lhs, const LLDrawable* const& rhs)
+ {
+ if (lhs->isVisible() && !rhs->isVisible())
+ {
+ return TRUE; //visible things come first
+ }
+ else if (!lhs->isVisible() && rhs->isVisible())
+ {
+ return FALSE; //rhs is visible, comes first
+ }
+
+ return lhs->mDistanceWRTCamera < rhs->mDistanceWRTCamera; // farthest = last
+ }
+ };
+
+ typedef enum e_drawable_flags
+ {
+// TEXTURE = 0x00000001,
+ IN_REBUILD_Q1 = 0x00000002,
+ IN_REBUILD_Q2 = 0x00000004,
+ IN_LIGHT_Q = 0x00000008,
+ EARLY_MOVE = 0x00000010,
+ MOVE_UNDAMPED = 0x00000020,
+ ON_MOVE_LIST = 0x00000040,
+ USE_BACKLIGHT = 0x00000080,
+ UV = 0x00000100,
+ UNLIT = 0x00000200,
+ LIGHT = 0x00000400,
+ LIGHTING_BUILT = 0x00000800,
+ REBUILD_VOLUME = 0x00001000,
+ REBUILD_TCOORD = 0x00002000,
+ REBUILD_GEOMETRY= REBUILD_VOLUME|REBUILD_TCOORD,
+ REBUILD_LIGHTING= 0x00008000,
+ REBUILD_ALL = REBUILD_GEOMETRY|REBUILD_LIGHTING,
+ ON_SHIFT_LIST = 0x00100000,
+// NO_INTERP_COLOR = 0x00200000,
+ BLOCKER = 0x00400000,
+ ACTIVE = 0x00800000,
+ DEAD = 0x01000000,
+ INVISIBLE = 0x02000000, // stay invisible until flag is cleared
+ NEARBY_LIGHT = 0x04000000, // In gPipeline.mNearbyLightSet
+ BUILT = 0x08000000,
+ FORCE_INVISIBLE = 0x10000000, // stay invis until CLEAR_INVISIBLE is set (set of orphaned)
+ CLEAR_INVISIBLE = 0x20000000, // clear FORCE_INVISIBLE next draw frame
+ REBUILD_SHADOW = 0x40000000
+ } EDrawableFlags;
+
+ LLXformMatrix mXform;
+
+ // vis data
+ LLPointer<LLDrawable> mParent;
+
+ F32 mDistanceWRTCamera;
+
+ LLRectf mUVRect;
+ F32 mUVZ;
+
+ drawable_set_t mLightSet;
+ drawable_set_t mBlockSet;
+
+ LLVector3 mSavePos;
+ S32 mQuietCount;
+
+ static S32 getCurrentFrame() { return sCurVisible; }
+
+ void setSpatialBridge(LLSpatialBridge* bridge) { mSpatialBridge = (LLDrawable*) bridge; }
+ LLSpatialBridge* getSpatialBridge() { return (LLSpatialBridge*) (LLDrawable*) mSpatialBridge; }
+
+protected:
+ typedef std::vector<LLFace*> face_list_t;
+
+ U32 mState;
+ S32 mRenderType;
+ LLPointer<LLViewerObject> mVObjp;
+ face_list_t mFaces;
+ LLSpatialGroup* mSpatialGroupp;
+ LLPointer<LLDrawable> mSpatialBridge;
+ S32 mSpatialGroupOffset;
+
+ U32 mVisible;
+ F32 mRadius;
+ LLVector3 mExtents[2];
+ LLVector3d mPositionGroup;
+ F64 mBinRadius;
+ S32 mGeneration;
+
+ F32 mSunShadowFactor;
+
+ LLVector3 mCurrentScale;
+
+ static U32 sCurVisible; // Counter for what value of mVisible means currently visible
+ static F32 sCurPixelAngle; //current pixels per radian
+
+ static U32 sNumZombieDrawables;
+ static LLDynamicArrayPtr<LLPointer<LLDrawable> > sDeadList;
+};
+
+
+inline LLFace* LLDrawable::getFace(const S32 i) const
+{
+ llassert((U32)i < mFaces.size());
+ return mFaces[i];
+}
+
+
+inline S32 LLDrawable::getNumFaces()const
+{
+ return (S32)mFaces.size();
+}
+
+#endif
diff --git a/indra/newview/lldrawpool.cpp b/indra/newview/lldrawpool.cpp
new file mode 100644
index 0000000000..0ef9eed7a3
--- /dev/null
+++ b/indra/newview/lldrawpool.cpp
@@ -0,0 +1,1402 @@
+/**
+ * @file lldrawpool.cpp
+ * @brief LLDrawPool class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpool.h"
+
+#include "llfasttimer.h"
+#include "llviewercontrol.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolbump.h"
+#include "lldrawpoolclouds.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolmedia.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolstars.h"
+#include "lldrawpooltree.h"
+#include "lldrawpooltreenew.h"
+#include "lldrawpoolterrain.h"
+#include "lldrawpoolwater.h"
+#include "lldrawpoolhud.h"
+#include "llface.h"
+#include "llviewerobjectlist.h" // For debug listing.
+#include "llvotreenew.h"
+#include "pipeline.h"
+
+#include "llagparray.inl"
+
+U32 LLDrawPool::sDataSizes[LLDrawPool::DATA_MAX_TYPES] =
+{
+ 12, // DATA_VERTICES
+ 8, // DATA_TEX_COORDS0
+ 8, // DATA_TEX_COORDS1
+ 8, // DATA_TEX_COORDS2
+ 8, // DATA_TEX_COORDS3
+ 12, // DATA_NORMALS
+ 4, // DATA_VERTEX_WEIGHTS,
+ 16, // DATA_CLOTHING_WEIGHTS
+ 12, // DATA_BINORMALS
+ 4, // DATA_COLORS
+};
+
+S32 LLDrawPool::sNumDrawPools = 0;
+
+LLDrawPool *LLDrawPool::createPool(const U32 type, LLViewerImage *tex0)
+{
+ LLDrawPool *poolp = NULL;
+ switch (type)
+ {
+ case POOL_SIMPLE:
+ poolp = new LLDrawPoolSimple(tex0);
+ break;
+ case POOL_ALPHA:
+ poolp = new LLDrawPoolAlpha();
+ break;
+ case POOL_AVATAR:
+ poolp = new LLDrawPoolAvatar();
+ break;
+ case POOL_TREE:
+ poolp = new LLDrawPoolTree(tex0);
+ break;
+ case POOL_TREE_NEW:
+ poolp = new LLDrawPoolTreeNew(tex0);
+ break;
+ case POOL_TERRAIN:
+ poolp = new LLDrawPoolTerrain(tex0);
+ break;
+ case POOL_SKY:
+ poolp = new LLDrawPoolSky();
+ break;
+ case POOL_STARS:
+ poolp = new LLDrawPoolStars();
+ break;
+ case POOL_CLOUDS:
+ poolp = new LLDrawPoolClouds();
+ break;
+ case POOL_WATER:
+ poolp = new LLDrawPoolWater();
+ break;
+ case POOL_GROUND:
+ poolp = new LLDrawPoolGround();
+ break;
+ case POOL_BUMP:
+ poolp = new LLDrawPoolBump(tex0);
+ break;
+ case POOL_MEDIA:
+ poolp = new LLDrawPoolMedia(tex0);
+ break;
+ case POOL_HUD:
+ poolp = new LLDrawPoolHUD();
+ break;
+ default:
+ llerrs << "Unknown draw pool type!" << llendl;
+ return NULL;
+ }
+
+ llassert(poolp->mType == type);
+ return poolp;
+}
+
+LLDrawPool::LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil)
+{
+ llassert(data_mask_il & DATA_VERTICES_MASK);
+ S32 i;
+ mType = type;
+ sNumDrawPools++;
+ mId = sNumDrawPools;
+
+ mDataMaskIL = data_mask_il;
+ mDataMaskNIL = data_mask_nil;
+
+ U32 cur_mask = 0x01;
+ U32 cur_offset = 0;
+ for (i = 0; i < DATA_MAX_TYPES; i++)
+ {
+ mDataOffsets[i] = cur_offset;
+ if (cur_mask & mDataMaskIL)
+ {
+ cur_offset += sDataSizes[i];
+ }
+ cur_mask <<= 1;
+ }
+
+ mStride = cur_offset;
+
+ mCleanupUnused = FALSE;
+ mIndicesDrawn = 0;
+ mRebuildFreq = 128 + rand() % 5;
+ mRebuildTime = 0;
+ mGeneration = 1;
+ mSkippedVertices = 0;
+
+ resetDrawOrders();
+ resetVertexData(0);
+
+ if (gGLManager.mHasATIVAO && !gGLManager.mIsRadeon9700)
+ {
+ // ATI 8500 doesn't like indices > 15 bit.
+ mMaxVertices = DEFAULT_MAX_VERTICES/2;
+ }
+ else
+ {
+ mMaxVertices = DEFAULT_MAX_VERTICES;
+ }
+
+ // JC: This must happen last, as setUseAGP reads many of the
+ // above variables.
+ mUseAGP = FALSE;
+ setUseAGP(gPipeline.usingAGP());
+
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ mFreeListGeomHead[i] = -1;
+ mFreeListIndHead[i] = -1;
+ }
+ mVertexShaderLevel = 0;
+}
+
+void LLDrawPool::destroy()
+{
+ if (!mReferences.empty())
+ {
+ llinfos << mReferences.size() << " references left on deletion of draw pool!" << llendl;
+ }
+}
+
+
+LLDrawPool::~LLDrawPool()
+{
+ destroy();
+
+ llassert( gPipeline.findPool( getType(), getTexture() ) == NULL );
+}
+
+BOOL LLDrawPool::setUseAGP(BOOL use_agp)
+{
+ BOOL ok = TRUE;
+ S32 vertex_count = mMemory.count() / mStride;
+ if (vertex_count > mMaxVertices && use_agp)
+ {
+#ifdef DEBUG_AGP
+ llwarns << "Allocating " << vertex_count << " vertices in pool type " << getType() << ", disabling AGP!" << llendl
+#endif
+ use_agp = FALSE;
+ ok = FALSE;
+ }
+
+ if (mUseAGP != use_agp)
+ {
+ mUseAGP = use_agp;
+
+ BOOL ok = TRUE;
+ ok &= mMemory.setUseAGP(use_agp);
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ ok &= mWeights.setUseAGP(use_agp);
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ ok &= mClothingWeights.setUseAGP(use_agp);
+ }
+
+ if (!ok)
+ {
+ // Disable AGP if any one of these doesn't have AGP, we don't want to try
+ // mixing AGP and non-agp arrays in a single pool.
+#ifdef DEBUG_AGP
+ llinfos << "Aborting using AGP because set failed on a mem block!" << llendl;
+#endif
+ setUseAGP(FALSE);
+ ok = FALSE;
+ }
+ }
+ return ok;
+}
+
+void LLDrawPool::flushAGP()
+{
+ mMemory.flushAGP();
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.flushAGP();
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.flushAGP();
+ }
+}
+
+void LLDrawPool::syncAGP()
+{
+ if (!getVertexCount())
+ {
+ return;
+ }
+ setUseAGP(gPipeline.usingAGP());
+
+ BOOL all_agp_on = TRUE;
+ mMemory.sync();
+ all_agp_on &= mMemory.isAGP();
+
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.sync();
+ all_agp_on &= mWeights.isAGP();
+ }
+
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.sync();
+ all_agp_on &= mClothingWeights.isAGP();
+ }
+
+ // Since sometimes AGP allocation is done during syncs, we need
+ // to make sure that if AGP allocation fails, we fallback to non-agp.
+ if (mUseAGP && !all_agp_on)
+ {
+#ifdef DEBUG_AGP
+ llinfos << "setUseAGP false because of AGP sync failure!" << llendl;
+#endif
+ setUseAGP(FALSE);
+ }
+}
+
+void LLDrawPool::dirtyTexture(const LLViewerImage *imagep)
+{
+}
+
+BOOL LLDrawPool::moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data)
+{
+ return TRUE;
+}
+
+// static
+S32 LLDrawPool::drawLoop(face_array_t& face_list, const U32* index_array)
+{
+ S32 res = 0;
+ if (!face_list.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ }
+ }
+ return res;
+}
+
+// static
+S32 LLDrawPool::drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage)
+{
+ S32 res = 0;
+ if (!face_list.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ facep->bindTexture(stage);
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ }
+ }
+ return res;
+}
+
+void LLDrawPool::drawLoop()
+{
+ const U32* index_array = getRawIndices();
+ if (!mDrawFace.empty())
+ {
+ mIndicesDrawn += drawLoop(mDrawFace, index_array);
+ }
+}
+
+BOOL LLDrawPool::getVertexStrider(LLStrider<LLVector3> &vertices, const U32 index)
+{
+ llassert(mDataMaskIL & LLDrawPool::DATA_VERTICES_MASK);
+ vertices = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride);
+ vertices.setStride(mStride);
+ return TRUE;
+}
+
+BOOL LLDrawPool::getTexCoordStrider(LLStrider<LLVector2> &tex_coords, const U32 index, const U32 pass)
+{
+ llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
+ tex_coords = (LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride);
+ tex_coords.setStride(mStride);
+ return TRUE;
+}
+
+
+BOOL LLDrawPool::getVertexWeightStrider(LLStrider<F32> &vertex_weights, const U32 index)
+{
+ llassert(mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK);
+
+ vertex_weights = &mWeights[index];
+ vertex_weights.setStride( 0 );
+ return TRUE;
+}
+
+BOOL LLDrawPool::getClothingWeightStrider(LLStrider<LLVector4> &clothing_weights, const U32 index)
+{
+ llassert(mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK);
+
+ clothing_weights= &mClothingWeights[index];
+ clothing_weights.setStride( 0 );
+
+ return TRUE;
+}
+
+BOOL LLDrawPool::getNormalStrider(LLStrider<LLVector3> &normals, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_NORMALS_MASK);
+
+ normals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride);
+
+ normals.setStride( mStride );
+
+ return TRUE;
+}
+
+
+BOOL LLDrawPool::getBinormalStrider(LLStrider<LLVector3> &binormals, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_BINORMALS_MASK);
+
+ binormals = (LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride);
+
+ binormals.setStride( mStride );
+
+ return TRUE;
+}
+
+BOOL LLDrawPool::getColorStrider(LLStrider<LLColor4U> &colors, const U32 index)
+{
+ llassert((mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK);
+
+ colors = (LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride);
+
+ colors.setStride( mStride );
+
+ return TRUE;
+}
+
+//virtual
+void LLDrawPool::beginRenderPass( S32 pass )
+{
+}
+
+//virtual
+void LLDrawPool::endRenderPass( S32 pass )
+{
+ glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
+ glDisableClientState ( GL_COLOR_ARRAY );
+ glDisableClientState ( GL_NORMAL_ARRAY );
+}
+void LLDrawPool::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+}
+
+void LLDrawPool::renderVisibility()
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ // SJB: Note: This may be broken now. If you need it, fix it :)
+
+ glLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glTranslatef(-0.4f,-0.3f,0);
+
+ float table[7][3] = {
+ { 1,0,0 },
+ { 0,1,0 },
+ { 1,1,0 },
+ { 0,0,1 },
+ { 1,0,1 },
+ { 0,1,1 },
+ { 1,1,1 }
+ };
+
+ glColor4f(0,0,0,0.5);
+ glBegin(GL_POLYGON);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glEnd();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+
+ S32 geom_count = face->getGeomCount();
+ for (S32 j=0;j<geom_count;j++)
+ {
+ LLVector3 p1;
+ LLVector3 p2;
+
+ intptr_t p = ((intptr_t)face*13) % 7;
+ F32 r = table[p][0];
+ F32 g = table[p][1];
+ F32 b = table[p][2];
+
+ //p1.mV[1] = y;
+ //p2.mV[1] = y;
+
+ p1.mV[2] = 1.0;
+ p2.mV[2] = 1.0;
+
+ glColor4f(r,g,b,0.5f);
+
+ glBegin(GL_LINE_STRIP);
+ glVertex3fv(p1.mV);
+ glVertex3fv(p2.mV);
+ glEnd();
+
+ }
+ }
+
+ glColor4f(1,1,1,1);
+ glBegin(GL_LINE_STRIP);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,-0.5f,1.0f);
+ glVertex3f(+0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,+0.5f,1.0f);
+ glVertex3f(-0.5f,-0.5f,1.0f);
+ glEnd();
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+}
+
+void LLDrawPool::enqueue(LLFace* facep)
+{
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+#if ENABLE_FACE_LINKING
+ facep->mSkipRender = FALSE;
+ facep->mNextFace = NULL;
+
+ if (mDrawFace.size() > 0)
+ {
+ LLFace* last_face = mDrawFace[mDrawFace.size()-1];
+ if (match(last_face, facep))
+ {
+ last_face->link(facep);
+ }
+ }
+#endif
+ mDrawFace.put(facep);
+ }
+}
+
+void LLDrawPool::bindGLVertexPointer()
+{
+ mMemory.bindGLVertexPointer(getStride(DATA_VERTICES), mDataOffsets[DATA_VERTICES]);
+}
+
+void LLDrawPool::bindGLTexCoordPointer(const U32 pass)
+{
+ mMemory.bindGLTexCoordPointer(getStride(DATA_TEX_COORDS0+pass), mDataOffsets[DATA_TEX_COORDS0+pass]);
+}
+
+void LLDrawPool::bindGLNormalPointer()
+{
+ mMemory.bindGLNormalPointer(getStride(DATA_NORMALS), mDataOffsets[DATA_NORMALS]);
+}
+
+void LLDrawPool::bindGLBinormalPointer(S32 index)
+{
+ mMemory.bindGLBinormalPointer(index, getStride(DATA_BINORMALS), mDataOffsets[DATA_BINORMALS]);
+}
+
+void LLDrawPool::bindGLColorPointer()
+{
+ mMemory.bindGLColorPointer(getStride(DATA_COLORS), mDataOffsets[DATA_COLORS]);
+}
+
+void LLDrawPool::bindGLVertexWeightPointer(S32 index)
+{
+ mWeights.bindGLVertexWeightPointer(index, 0, 0);
+}
+
+void LLDrawPool::bindGLVertexClothingWeightPointer(S32 index)
+{
+ mClothingWeights.bindGLVertexClothingWeightPointer(index, 0, 0);
+}
+
+
+U32* LLDrawPool::getIndices(S32 index)
+{
+ return &mIndices[index];
+}
+
+const LLVector3& LLDrawPool::getVertex(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_VERTICES_MASK);
+ llassert(index < mMemory.count());
+ llassert(mMemory.getMem());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_VERTICES] + index * mStride);
+}
+
+const LLVector2& LLDrawPool::getTexCoord(const S32 index, const U32 pass)
+{
+ llassert(mDataMaskIL & (LLDrawPool::DATA_TEX_COORDS0_MASK << pass));
+ llassert(index < mMemory.count());
+ return *(LLVector2*)(mMemory.getMem() + mDataOffsets[DATA_TEX_COORDS0 + pass] + index * mStride);
+}
+
+const LLVector3& LLDrawPool::getNormal(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_NORMALS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_NORMALS] + index * mStride);
+}
+
+const LLVector3& LLDrawPool::getBinormal(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_BINORMALS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLVector3*)(mMemory.getMem() + mDataOffsets[DATA_BINORMALS] + index * mStride);
+}
+
+const LLColor4U& LLDrawPool::getColor(const S32 index)
+{
+ llassert(mDataMaskIL & DATA_COLORS_MASK);
+ llassert(index < mMemory.count());
+ return *(LLColor4U*)(mMemory.getMem() + mDataOffsets[DATA_COLORS] + index * mStride);
+}
+
+const F32& LLDrawPool::getVertexWeight(const S32 index)
+{
+ llassert(mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK);
+ llassert(index < mWeights.count());
+ llassert(mWeights.getMem());
+ return mWeights[index];
+}
+
+const LLVector4& LLDrawPool::getClothingWeight(const S32 index)
+{
+ llassert(mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK);
+ llassert(index < mClothingWeights.count());
+ llassert(mClothingWeights.getMem());
+ return mClothingWeights[index];
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define USE_FREE_LIST 0
+#define DEBUG_FREELIST 0
+
+struct tFreeListNode
+{
+ U32 count;
+ S32 next;
+};
+
+#if DEBUG_FREELIST
+static void check_list(U8 *pool, S32 stride, S32 head, S32 max)
+{
+ int count = 0;
+
+ while (head >= 0)
+ {
+ tFreeListNode *node = (tFreeListNode *)(pool + head*stride);
+ count++;
+ if ((count > max) || ((node->count>>20) != 0xabc) || ((node->count&0xfffff) < 2))
+ llerrs << "Bad Ind List" << llendl;
+ head = node->next;
+ }
+}
+#define CHECK_LIST(x) check_list##x
+#else
+#define CHECK_LIST(x)
+#endif
+
+// DEBUG!
+void LLDrawPool::CheckIntegrity()
+{
+#if DEBUG_FREELIST
+ int bucket;
+ for (bucket=0; bucket<NUM_BUCKETS; bucket++)
+ {
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[bucket], mMemory.count() / mStride));
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[bucket], mIndices.count()));
+ }
+#endif
+}
+
+int LLDrawPool::freeListBucket(U32 count)
+{
+ int bucket;
+
+ // llassert(NUM_BUCKETS == 8)
+
+ if (count & ~511) // >= 512
+ bucket = 7;
+ else if (count & 256) // 256-511
+ bucket = 6;
+ else if (count & 128)
+ bucket = 5;
+ else if (count & 64)
+ bucket = 4;
+ else if (count & 32)
+ bucket = 3;
+ else if (count & 16)
+ bucket = 2;
+ else if (count & 8) // 8-15
+ bucket = 1;
+ else // 0-7
+ bucket = 0;
+ return bucket;
+}
+
+void remove_node(int nodeidx, int pidx, U8 *membase, int stride, int *head)
+{
+ LLDrawPool::FreeListNode *node = (LLDrawPool::FreeListNode *)(membase + nodeidx*stride);
+ if (pidx >= 0)
+ {
+ LLDrawPool::FreeListNode *pnode = (LLDrawPool::FreeListNode *)(membase + pidx*stride);
+ pnode->next = node->next;
+ }
+ else
+ {
+ *head = node->next;
+ }
+}
+
+void LLDrawPool::freeListAddGeom(S32 index, U32 count)
+{
+#if USE_FREE_LIST
+ int i;
+ U8 *membase = (U8*)mMemory.getMem();
+ // See if next block or previous block is free, if so combine them
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ int pidx = -1;
+ int nodeidx = mFreeListGeomHead[i];
+ while(nodeidx >= 0)
+ {
+ int change = 0;
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
+ int nodecount = node->count & 0xffff;
+ // Check for prev block
+ if (nodeidx + nodecount == index)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+ // Combine nodes
+ index = nodeidx;
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ // Check for next block
+ if (nodeidx == index + count)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+ // Combine nodes
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ if (change)
+ break;
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+ // Add (extended) block to free list
+ if (count >= 2) // need 2 words to store free list (theoreticly mStride could = 4)
+ {
+ CheckIntegrity();
+ if ((index + count)*mStride >= mMemory.count())
+ {
+ mMemory.shrinkTo(index*mStride);
+ }
+ else
+ {
+ int bucket = freeListBucket(count);
+ FreeListNode *node = (FreeListNode *)(membase + index*mStride);
+ node->count = count | (0xabc<<20);
+ node->next = mFreeListGeomHead[bucket];
+ mFreeListGeomHead[bucket] = index;
+ }
+ CheckIntegrity();
+ }
+#endif
+}
+
+void LLDrawPool::freeListAddInd(S32 index, U32 count)
+{
+#if USE_FREE_LIST
+ int i;
+ const U32 *membase = mIndices.getMem();
+ // See if next block or previous block is free, if so combine them
+ for (i=0; i<NUM_BUCKETS; i++)
+ {
+ int pidx = -1;
+ int nodeidx = mFreeListIndHead[i];
+ while(nodeidx >= 0)
+ {
+ int change = 0;
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx);
+ int nodecount = node->count & 0xffff;
+ // Check for prev block
+ if (nodeidx + nodecount == index)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+ // Combine nodes
+ index = nodeidx;
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ // Check for next block
+ if (nodeidx == index + count)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+ // Combine nodes
+ count += nodecount;
+ i = 0; // start over ; i = NUM_BUCKETS // done
+ change = 1;
+ //break;
+ }
+ if (change)
+ break;
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+ // Add (extended) block to free list
+ if (count >= 2) // need 2 words to store free list
+ {
+ CheckIntegrity();
+ if (index + count >= mIndices.count())
+ {
+ mIndices.shrinkTo(index);
+ }
+ else
+ {
+ int bucket = freeListBucket(count);
+ FreeListNode *node = (FreeListNode *)(membase + index);
+ node->count = count | (0xabc<<20);
+ node->next = mFreeListIndHead[bucket];
+ mFreeListIndHead[bucket] = index;
+ }
+ CheckIntegrity();
+ }
+#endif
+}
+
+S32 LLDrawPool::freeListFindGeom(U32 count)
+{
+#if USE_FREE_LIST
+ int i, nodeidx, pidx;
+ int firstbucket = freeListBucket(count);
+ U8 *membase = (U8*)mMemory.getMem();
+ for (i=firstbucket; i<NUM_BUCKETS; i++)
+ {
+ pidx = -1;
+ nodeidx = mFreeListGeomHead[i];
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[i], mMemory.count() / mStride));
+ while(nodeidx >= 0)
+ {
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx*mStride);
+ int nodecount = node->count & 0xffff;
+ llassert((node->count>>20) == 0xabc);
+ if (nodecount >= count)
+ {
+ remove_node(nodeidx, pidx, membase, mStride, &mFreeListGeomHead[i]);
+#if 1
+ if (nodecount > count)
+ {
+ int leftover = nodecount - count;
+ freeListAddGeom(nodeidx + count, leftover);
+ }
+#endif
+ return nodeidx;
+ }
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+#endif // USE_FREE_LIST
+ return -1;
+}
+
+S32 LLDrawPool::freeListFindInd(U32 count)
+{
+#if USE_FREE_LIST
+ int i, nodeidx, pidx;
+ int firstbucket = freeListBucket(count);
+ U32 *membase = (U32 *)mIndices.getMem();
+ for (i=firstbucket; i<NUM_BUCKETS; i++)
+ {
+ pidx = -1;
+ nodeidx = mFreeListIndHead[i];
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[i], mIndices.count()));
+ while(nodeidx >= 0)
+ {
+ FreeListNode *node = (FreeListNode *)(membase + nodeidx);
+ int nodecount = node->count & 0xffff;
+ llassert((node->count>>20) == 0xabc);
+ if (nodecount >= count)
+ {
+ remove_node(nodeidx, pidx, (U8*)membase, 4, &mFreeListIndHead[i]);
+#if 1
+ if (nodecount > count)
+ {
+ int leftover = nodecount - count;
+ freeListAddInd(nodeidx + count, leftover);
+ }
+#endif
+ return nodeidx;
+ }
+ pidx = nodeidx;
+ nodeidx = node->next;
+ }
+ }
+#endif // USE_FREE_LIST
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+S32 LLDrawPool::reserveGeom(const U32 geom_count)
+{
+ LLFastTimer t(LLFastTimer::FTM_GEO_RESERVE);
+
+ S32 index;
+ index = freeListFindGeom(geom_count);
+ if (index < 0)
+ {
+ index = mMemory.count() / mStride;
+ if (!geom_count)
+ {
+ llwarns << "Attempting to reserve zero bytes!" << llendl;
+ return index;
+ }
+
+ S32 bytes = geom_count * mStride;
+
+ if ((index + (S32)geom_count) > (S32)mMaxVertices)
+ {
+ //
+ // Various drivers have issues with the number of indices being greater than a certain number.
+ // if you're using AGP. Disable AGP if we've got more vertices than in the pool.
+ //
+#ifdef DEBUG_AGP
+ llinfos << "setUseAGP false because of large vertex count in reserveGeom" << llendl;
+#endif
+ setUseAGP(FALSE);
+ }
+
+ mMemory.reserve_block(bytes);
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.reserve_block(geom_count);
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.reserve_block(geom_count);
+ }
+ }
+ CHECK_LIST(((U8 *)mMemory.getMem(), mStride, mFreeListGeomHead[0], mMemory.count() / mStride));
+ return index;
+}
+
+S32 LLDrawPool::reserveInd(U32 indCount)
+{
+ S32 index;
+ index = freeListFindInd(indCount);
+ if (index < 0)
+ {
+ index = mIndices.count();
+
+ if (indCount)
+ {
+ mIndices.reserve_block(indCount);
+ }
+ }
+ for (U32 i=0;i<indCount;i++)
+ {
+ mIndices[index+i]=0;
+ }
+ CHECK_LIST(((U8 *)mIndices.getMem(), 4, mFreeListIndHead[0], mIndices.count()));
+ return index;
+}
+
+S32 LLDrawPool::unReserveGeom(const S32 index, const U32 count)
+{
+ if (index < 0 || count == 0)
+ return -1;
+
+ freeListAddGeom(index, count);
+
+#if 0
+ int i;
+ S32 bytes,words;
+ U32 *memp;
+ // Fill mem with bad data (for testing only)
+ bytes = count * mStride;
+ bytes -= sizeof(FreeListNode);
+ memp = (U32*)(mMemory.getMem() + index * mStride);
+ memp += sizeof(FreeListNode)>>2;
+ words = bytes >> 2;
+ for (i=0; i<words; i++)
+ *memp++ = 0xffffffff;
+
+ words = count; // (sizeof each array is a word)
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ memp = (U32*)(&mWeights[index]);
+ for (i=0; i<words; i++)
+ *memp++ = 0xffffffff;
+ }
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ memp = (U32*)(&mClothingWeights[index]);
+ for (i=0; i<count; i++)
+ *memp++ = 0xffffffff;
+ }
+#endif
+ return -1;
+}
+
+S32 LLDrawPool::unReserveInd(const S32 index, const U32 count)
+{
+ if (index < 0 || count == 0)
+ return -1;
+
+ freeListAddInd(index, count);
+
+#if 0
+ int i;
+ U32 *memp = &mIndices[index];
+ for (i=0; i<count; i++)
+ *memp++ = 0xffffffff;
+#endif
+ return -1;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+const U32 LLDrawPool::getIndexCount() const
+{
+ return mIndices.count();
+}
+
+const U32 LLDrawPool::getVertexCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getTexCoordCount(U32 pass) const
+{
+ return mMemory.count() / mStride;
+}
+
+
+const U32 LLDrawPool::getNormalCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+
+const U32 LLDrawPool::getBinormalCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getColorCount() const
+{
+ return mMemory.count() / mStride;
+}
+
+const U32 LLDrawPool::getVertexWeightCount() const
+{
+ return mWeights.count();
+}
+
+// virtual
+BOOL LLDrawPool::addFace(LLFace *facep)
+{
+ addFaceReference(facep);
+ return TRUE;
+}
+
+// virtual
+BOOL LLDrawPool::removeFace(LLFace *facep)
+{
+ removeFaceReference(facep);
+
+ vector_replace_with_last(mDrawFace, facep);
+
+ facep->unReserve();
+
+ return TRUE;
+}
+
+// Not absolutely sure if we should be resetting all of the chained pools as well - djs
+void LLDrawPool::resetDrawOrders()
+{
+ mDrawFace.resize(0);
+}
+
+void LLDrawPool::resetIndices(S32 indices_count)
+{
+ mIndices.reset(indices_count);
+ for (S32 i=0; i<NUM_BUCKETS; i++)
+ mFreeListIndHead[i] = -1;
+}
+
+void LLDrawPool::resetVertexData(S32 reserve_count)
+{
+ mMemory.reset(reserve_count*mStride);
+
+ for (S32 i=0; i<NUM_BUCKETS; i++)
+ {
+ mFreeListGeomHead[i] = -1;
+ }
+ if (mDataMaskNIL & DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mWeights.reset(reserve_count);
+ }
+
+ if (mDataMaskNIL & DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mClothingWeights.reset(reserve_count);
+ }
+}
+
+void LLDrawPool::resetAll()
+{
+ resetDrawOrders();
+ resetVertexData(0);
+ mGeneration++;
+
+}
+
+S32 LLDrawPool::rebuild()
+{
+ mRebuildTime++;
+
+ BOOL needs_rebuild = FALSE;
+ S32 rebuild_cost = 0;
+
+ if (mUseAGP)
+ {
+ if (getVertexCount() > 0.75f*DEFAULT_MAX_VERTICES)
+ {
+ if (mRebuildTime > 8)
+ {
+ needs_rebuild = TRUE;
+ }
+#ifdef DEBUG_AGP
+ llwarns << "More than " << DEFAULT_MAX_VERTICES << " in pool type " << (S32)mType << " at rebuild!" << llendl;
+#endif
+ }
+ }
+
+ // rebuild de-allocates 'stale' objects, so we still need to do a rebuild periodically
+ if (mRebuildFreq > 0 && mRebuildTime >= mRebuildFreq)
+ {
+ needs_rebuild = TRUE;
+ }
+
+ if (needs_rebuild)
+ {
+ mGeneration++;
+
+ if (mReferences.empty())
+ {
+ resetIndices(0);
+ resetVertexData(0);
+ }
+ else
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->hasGeometry() && !facep->isState(LLFace::BACKLIST | LLFace::SHARED_GEOM))
+ {
+ facep->backup();
+ }
+ }
+ S32 tot_verts = 0;
+ S32 tot_indices = 0;
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ tot_verts += facep->getGeomCount();
+ tot_indices += facep->getIndicesCount();
+ }
+ }
+ for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
+ iter != mMoveFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ tot_verts += facep->getGeomCount();
+ tot_indices += facep->getIndicesCount();
+ }
+ }
+
+ resetIndices(tot_indices);
+ flushAGP();
+ resetVertexData(tot_verts);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ llassert(facep->getPool() == this);
+ facep->restore();
+ }
+ }
+ mRebuildTime = 0;
+ setDirty();
+ }
+
+ if (!mMoveFace.empty())
+ {
+ for (std::vector<LLFace*>::iterator iter = mMoveFace.begin();
+ iter != mMoveFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ facep->restore();
+ enqueue(facep);
+ }
+ setDirty();
+ mMoveFace.reset();
+ rebuild_cost++;
+ }
+ return rebuild_cost;
+}
+
+LLViewerImage *LLDrawPool::getTexture()
+{
+ return NULL;
+}
+
+LLViewerImage *LLDrawPool::getDebugTexture()
+{
+ return NULL;
+}
+
+void LLDrawPool::removeFaceReference(LLFace *facep)
+{
+ if (facep->getReferenceIndex() != -1)
+ {
+ if (facep->getReferenceIndex() != (S32)mReferences.size())
+ {
+ LLFace *back = mReferences.back();
+ mReferences[facep->getReferenceIndex()] = back;
+ back->setReferenceIndex(facep->getReferenceIndex());
+ }
+ mReferences.pop_back();
+ }
+ facep->setReferenceIndex(-1);
+}
+
+void LLDrawPool::addFaceReference(LLFace *facep)
+{
+ if (-1 == facep->getReferenceIndex())
+ {
+ facep->setReferenceIndex(mReferences.size());
+ mReferences.push_back(facep);
+ }
+}
+
+U32 LLDrawPool::getTrianglesDrawn() const
+{
+ return mIndicesDrawn / 3;
+}
+
+void LLDrawPool::resetTrianglesDrawn()
+{
+ mIndicesDrawn = 0;
+}
+
+void LLDrawPool::addIndicesDrawn(const U32 indices)
+{
+ mIndicesDrawn += indices;
+}
+
+BOOL LLDrawPool::verify() const
+{
+ BOOL ok = TRUE;
+ // Verify all indices in the pool are in the right range
+ const U32 *indicesp = getRawIndices();
+ for (U32 i = 0; i < getIndexCount(); i++)
+ {
+ if (indicesp[i] > getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Bad index in tree pool!" << llendl;
+ }
+ }
+
+ for (std::vector<LLFace*>::const_iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ const LLFace* facep = *iter;
+ if (facep->getPool() != this)
+ {
+ llinfos << "Face in wrong pool!" << llendl;
+ facep->printDebugInfo();
+ ok = FALSE;
+ }
+ else if (!facep->verify())
+ {
+ ok = FALSE;
+ }
+ }
+
+ return ok;
+}
+
+void LLDrawPool::printDebugInfo() const
+{
+ llinfos << "Pool " << this << " Type: " << getType() << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "Vertex count: " << getVertexCount() << llendl;
+ llinfos << "Normal count: " << getNormalCount() << llendl;
+ llinfos << "Indices count: " << getIndexCount() << llendl;
+ llinfos << llendl;
+}
+
+
+S32 LLDrawPool::getMemUsage(const BOOL print)
+{
+ S32 mem_usage = 0;
+
+ mem_usage += sizeof(this);
+
+ // Usage beyond the pipeline allocated data (color and mMemory)
+ mem_usage += mIndices.getMax() * sizeof(U32);
+ mem_usage += mDrawFace.capacity() * sizeof(LLFace *);
+ mem_usage += mMoveFace.capacity() * sizeof(LLFace *);
+ mem_usage += mReferences.capacity() * sizeof(LLFace *);
+
+ mem_usage += mMemory.getSysMemUsage();
+ mem_usage += mWeights.getSysMemUsage();
+ mem_usage += mClothingWeights.getSysMemUsage();
+
+ return mem_usage;
+}
+
+LLColor3 LLDrawPool::getDebugColor() const
+{
+ return LLColor3(0.f, 0.f, 0.f);
+}
+
+void LLDrawPool::setDirty()
+{
+ mMemory.setDirty();
+ mWeights.setDirty();
+ mClothingWeights.setDirty();
+}
+
+BOOL LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor = FALSE;
+
+void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4& color)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4fvARB(mPool->getMaterialAttribIndex(), color.mV);
+ }
+ else
+ {
+ glColor4fv(color.mV);
+ }
+}
+
+void LLDrawPool::LLOverrideFaceColor::setColor(const LLColor4U& color)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4ubvARB(mPool->getMaterialAttribIndex(), color.mV);
+ }
+ else
+ {
+ glColor4ubv(color.mV);
+ }
+}
+
+void LLDrawPool::LLOverrideFaceColor::setColor(F32 r, F32 g, F32 b, F32 a)
+{
+ if (mPool->mVertexShaderLevel > 0 && mPool->getMaterialAttribIndex() > 0)
+ {
+ glVertexAttrib4fARB(mPool->getMaterialAttribIndex(), r,g,b,a);
+ }
+ else
+ {
+ glColor4f(r,g,b,a);
+ }
+}
+
+// virtual
+void LLDrawPool::enableShade()
+{ }
+
+// virtual
+void LLDrawPool::disableShade()
+{ }
+
+// virtual
+void LLDrawPool::setShade(F32 shade)
+{ }
diff --git a/indra/newview/lldrawpool.h b/indra/newview/lldrawpool.h
new file mode 100644
index 0000000000..a66d7d14c7
--- /dev/null
+++ b/indra/newview/lldrawpool.h
@@ -0,0 +1,348 @@
+/**
+ * @file lldrawpool.h
+ * @brief LLDrawPool class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOL_H
+#define LL_LLDRAWPOOL_H
+
+#include "llagparray.h"
+#include "lldarray.h"
+#include "lldlinked.h"
+#include "llstrider.h"
+#include "llviewerimage.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llstrider.h"
+
+class LLFace;
+class LLImageGL;
+class LLViewerImage;
+
+#define DEFAULT_MAX_VERTICES 65535
+
+class LLDrawPool
+{
+public:
+ typedef LLDynamicArray<LLFace*, 128> face_array_t;
+
+ enum
+ {
+ SHADER_LEVEL_SCATTERING = 2
+ };
+
+public:
+ LLDrawPool(const U32 type, const U32 data_mask_il, const U32 data_mask_nil);
+ virtual ~LLDrawPool();
+
+ static LLDrawPool* createPool(const U32 type, LLViewerImage *tex0 = NULL);
+
+ void flushAGP(); // Flush the AGP buffers so they can be repacked and reallocated.
+ void syncAGP();
+
+ virtual LLDrawPool *instancePool() = 0; // Create an empty new instance of the pool.
+ virtual void beginRenderPass( S32 pass );
+ virtual void endRenderPass( S32 pass );
+ virtual S32 getNumPasses() { return 1; }
+ virtual void render(S32 pass = 0) = 0;
+ virtual void renderForSelect() = 0;
+ virtual BOOL match(LLFace* last_face, LLFace* facep) { return FALSE; }
+ virtual void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+
+ virtual void prerender() = 0;
+ virtual S32 rebuild();
+
+ virtual S32 getMaterialAttribIndex() = 0;
+
+ virtual LLViewerImage *getTexture();
+ virtual LLViewerImage *getDebugTexture();
+ virtual void dirtyTexture(const LLViewerImage* texturep);
+
+ virtual void enqueue(LLFace *face);
+ virtual BOOL addFace(LLFace *face);
+ virtual BOOL removeFace(LLFace *face);
+
+ virtual BOOL verify() const; // Verify that all data in the draw pool is correct!
+ virtual LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual void resetDrawOrders();
+ virtual void resetVertexData(S32 reserve_count);
+ virtual void resetIndices(S32 num_indices);
+ void resetAll();
+
+ BOOL moveFace(LLFace *face, LLDrawPool *poolp, BOOL copy_data = FALSE);
+
+
+ S32 getId() const { return mId; }
+ U32 getType() const { return mType; }
+
+ const U32 getStride() const;
+ inline const U32 getStride(const U32 data_type) const;
+ inline const U32 getOffset(const U32 data_type) const;
+
+ S32 reserveGeom(U32 count);
+ S32 reserveInd (U32 count);
+ S32 unReserveGeom(const S32 index, const U32 count);
+ S32 unReserveInd(const S32 index, const U32 count);
+
+ void bindGLVertexPointer();
+ void bindGLTexCoordPointer(const U32 pass=0);
+ void bindGLNormalPointer();
+ void bindGLBinormalPointer(S32 index);
+ void bindGLColorPointer();
+ void bindGLVertexWeightPointer(S32 index);
+ void bindGLVertexClothingWeightPointer(S32 index);
+
+ const U32 getIndexCount() const;
+ const U32 getTexCoordCount(const U32 pass=0) const;
+ const U32 getVertexCount() const;
+ const U32 getNormalCount() const;
+ const U32 getBinormalCount() const;
+ const U32 getColorCount() const;
+ const U32 getVertexWeightCount() const;
+
+ void setDirty();
+ void setDirtyMemory() { mMemory.setDirty(); }
+ void setDirtyWeights() { mWeights.setDirty(); }
+
+ const U32* getRawIndices() const { return mIndices.getMem(); }
+
+ U32 getIndex(const S32 index) { return mIndices[index]; } // Use to get one index
+ U32 *getIndices(const S32 index); // Used to get an array of indices for reading/writing
+ void CheckIntegrity(); // DEBUG
+
+ const LLVector3& getVertex(const S32 index);
+ const LLVector2& getTexCoord(const S32 index, const U32 pass);
+ const LLVector3& getNormal(const S32 index);
+ const LLVector3& getBinormal(const S32 index);
+ const LLColor4U& getColor(const S32 index);
+ const F32& getVertexWeight(const S32 index);
+ const LLVector4& getClothingWeight(const S32 index);
+
+ void setRebuild(const BOOL rebuild);
+
+ void destroy();
+
+ void buildEdges();
+
+ static S32 drawLoop(face_array_t& face_list, const U32* index_array);
+ static S32 drawLoopSetTex(face_array_t& face_list, const U32* index_array, S32 stage);
+ void drawLoop();
+
+ void renderVisibility();
+
+ void addFaceReference(LLFace *facep);
+ void removeFaceReference(LLFace *facep);
+ U32 getTrianglesDrawn() const;
+ void resetTrianglesDrawn();
+ void addIndicesDrawn(const U32 indices);
+
+ void printDebugInfo() const;
+ S32 getMemUsage(const BOOL print = FALSE);
+
+ BOOL setUseAGP(BOOL use_agp);
+ BOOL canUseAGP() const { return mMemory.isAGP(); } // Return TRUE if this pool can use AGP
+
+ S32 getMaxVertices() const { return mMaxVertices; }
+ S32 getVertexShaderLevel() const { return mVertexShaderLevel; }
+
+ friend class LLFace;
+ friend class LLPipeline;
+public:
+
+ enum
+ {
+ // Correspond to LLPipeline render type
+ POOL_SKY = 1,
+ POOL_STARS,
+ POOL_GROUND,
+ POOL_TERRAIN,
+ POOL_SIMPLE,
+ POOL_MEDIA,
+ POOL_BUMP,
+ POOL_AVATAR,
+ POOL_TREE,
+ POOL_TREE_NEW,
+ POOL_WATER,
+ POOL_CLOUDS,
+ POOL_ALPHA,
+ POOL_HUD,
+ };
+
+
+ // If you change the order or add params to these, you also need to adjust the sizes in the
+ // mDataSizes array defined in lldrawpool.cpp
+ typedef enum e_data_type
+ {
+ DATA_VERTICES = 0,
+ DATA_TEX_COORDS0 = 1,
+ DATA_TEX_COORDS1 = 2,
+ DATA_TEX_COORDS2 = 3,
+ DATA_TEX_COORDS3 = 4,
+ DATA_NORMALS = 5,
+ DATA_VERTEX_WEIGHTS = 6,
+ DATA_CLOTHING_WEIGHTS = 7,
+ DATA_BINORMALS = 8,
+ DATA_COLORS = 9,
+ DATA_MAX_TYPES = 10
+ } EDataType;
+
+ typedef enum e_data_mask
+ {
+ DATA_VERTICES_MASK = 1 << DATA_VERTICES,
+ DATA_TEX_COORDS0_MASK = 1 << DATA_TEX_COORDS0,
+ DATA_TEX_COORDS1_MASK = 1 << DATA_TEX_COORDS1,
+ DATA_TEX_COORDS2_MASK = 1 << DATA_TEX_COORDS2,
+ DATA_TEX_COORDS3_MASK = 1 << DATA_TEX_COORDS3,
+ DATA_NORMALS_MASK = 1 << DATA_NORMALS,
+ DATA_VERTEX_WEIGHTS_MASK = 1 << DATA_VERTEX_WEIGHTS,
+ DATA_CLOTHING_WEIGHTS_MASK = 1 << DATA_CLOTHING_WEIGHTS,
+ DATA_BINORMALS_MASK = 1 << DATA_BINORMALS,
+ DATA_COLORS_MASK = 1 << DATA_COLORS,
+
+ // Masks for standard types.
+ // IL for interleaved, NIL for non-interleaved.
+ DATA_SIMPLE_IL_MASK = DATA_VERTICES_MASK | DATA_TEX_COORDS0_MASK | DATA_NORMALS_MASK,
+ DATA_SIMPLE_NIL_MASK = 0,
+ DATA_BUMP_IL_MASK = DATA_SIMPLE_IL_MASK | DATA_BINORMALS_MASK | DATA_TEX_COORDS1_MASK,
+ } EDataMask;
+
+ face_array_t mDrawFace;
+ face_array_t mMoveFace;
+ face_array_t mReferences;
+
+ U32 mDataMaskIL; // Interleaved data
+ U32 mDataMaskNIL; // Non-interleaved data
+ U32 mDataOffsets[DATA_MAX_TYPES];
+ S32 mStride;
+
+ S32 mRebuildFreq;
+ S32 mRebuildTime;
+ S32 mGeneration;
+
+
+ S32 mSkippedVertices;
+
+ static U32 sDataSizes[DATA_MAX_TYPES];
+ static S32 sNumDrawPools;
+
+protected:
+ LLAGPArray<U8> mMemory;
+ LLAGPArray<F32> mWeights;
+ LLAGPArray<LLVector4> mClothingWeights;
+ LLAGPArray<U32> mIndices;
+
+public:
+
+ BOOL getVertexStrider (LLStrider<LLVector3> &vertices, const U32 index = 0);
+ BOOL getTexCoordStrider (LLStrider<LLVector2> &tex_coords, const U32 index = 0, const U32 pass=0);
+ BOOL getNormalStrider (LLStrider<LLVector3> &normals, const U32 index = 0);
+ BOOL getBinormalStrider (LLStrider<LLVector3> &binormals, const U32 index = 0);
+ BOOL getColorStrider (LLStrider<LLColor4U> &colors, const U32 index = 0);
+ BOOL getVertexWeightStrider(LLStrider<F32> &vertex_weights, const U32 index = 0);
+ BOOL getClothingWeightStrider(LLStrider<LLVector4> &clothing_weights, const U32 index = 0);
+
+public:
+ enum { NUM_BUCKETS = 8 }; // Need to change freeListBucket() if NUM_BUCKETS changes
+ struct FreeListNode
+ {
+ U32 count;
+ S32 next;
+ };
+protected:
+ int freeListBucket(U32 count);
+ void freeListAddGeom(S32 index, U32 count);
+ void freeListAddInd(S32 index, U32 count);
+ S32 freeListFindGeom(U32 count);
+ S32 freeListFindInd(U32 count);
+
+protected:
+ BOOL mUseAGP;
+ S32 mVertexShaderLevel;
+ S32 mId;
+ U32 mType; // Type of draw pool
+ S32 mMaxVertices;
+ S32 mIndicesDrawn;
+ BOOL mCleanupUnused; // Cleanup unused data when too full
+
+ S32 mFreeListGeomHead[8];
+ S32 mFreeListIndHead[8];
+
+public:
+ class LLOverrideFaceColor
+ {
+ public:
+ LLOverrideFaceColor(LLDrawPool* pool)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, const LLColor4& color)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(color);
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, const LLColor4U& color)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(color);
+ }
+ LLOverrideFaceColor(LLDrawPool* pool, F32 r, F32 g, F32 b, F32 a)
+ : mOverride(sOverrideFaceColor), mPool(pool)
+ {
+ sOverrideFaceColor = TRUE;
+ setColor(r, g, b, a);
+ }
+ ~LLOverrideFaceColor()
+ {
+ sOverrideFaceColor = mOverride;
+ }
+ void setColor(const LLColor4& color);
+ void setColor(const LLColor4U& color);
+ void setColor(F32 r, F32 g, F32 b, F32 a);
+ BOOL mOverride;
+ LLDrawPool* mPool;
+ static BOOL sOverrideFaceColor;
+ };
+
+ virtual void enableShade();
+ virtual void disableShade();
+ virtual void setShade(F32 shade);
+
+};
+
+inline const U32 LLDrawPool::getStride() const
+{
+ return mStride;
+}
+
+inline const U32 LLDrawPool::getOffset(const U32 data_type) const
+{
+ return mDataOffsets[data_type];
+}
+
+inline const U32 LLDrawPool::getStride(const U32 data_type) const
+{
+ if (mDataMaskIL & (1 << data_type))
+ {
+ return mStride;
+ }
+ else if (mDataMaskNIL & (1 << data_type))
+ {
+ return 0;
+ }
+ else
+ {
+ llerrs << "Getting stride for unsupported data type " << data_type << llendl;
+ return 0;
+ }
+}
+
+#endif //LL_LLDRAWPOOL_H
diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp
new file mode 100644
index 0000000000..d34695a7be
--- /dev/null
+++ b/indra/newview/lldrawpoolalpha.cpp
@@ -0,0 +1,584 @@
+/**
+ * @file lldrawpoolalpha.cpp
+ * @brief LLDrawPoolAlpha class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolalpha.h"
+
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llfasttimer.h"
+
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "llsky.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h" // For debugging
+#include "llviewerobjectlist.h" // For debugging
+#include "llviewerwindow.h"
+#include "pipeline.h"
+
+const F32 MAX_DIST = 512.f;
+const F32 ALPHA_FALLOFF_START_DISTANCE = 0.8f;
+
+BOOL LLDrawPoolAlpha::sShowDebugAlpha = FALSE;
+
+LLDrawPoolAlpha::LLDrawPoolAlpha() :
+ LLDrawPool(POOL_ALPHA,
+ DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
+ DATA_SIMPLE_NIL_MASK)
+{
+ mRebuiltLastFrame = FALSE;
+ mMinDistance = 0.f;
+ mMaxDistance = MAX_DIST;
+ mInvBinSize = NUM_ALPHA_BINS/(mMaxDistance - mMinDistance);
+ mCleanupUnused = TRUE;
+ //mRebuildFreq = -1 ; // Only rebuild if nearly full
+
+// for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+// {
+// mDistanceBins[i].realloc(200);
+// }
+}
+
+LLDrawPoolAlpha::~LLDrawPoolAlpha()
+{
+}
+
+LLDrawPool *LLDrawPoolAlpha::instancePool()
+{
+ llerrs << "Should never be calling instancePool on an alpha pool!" << llendl;
+ return NULL;
+}
+
+void LLDrawPoolAlpha::enqueue(LLFace *facep)
+{
+ if (!facep->isState(LLFace::GLOBAL))
+ {
+ facep->mCenterAgent = facep->mCenterLocal * facep->getRenderMatrix();
+ }
+ facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
+
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+ mDrawFace.put(facep);
+ }
+
+ {
+ S32 dist_bin = lltrunc( (mMaxDistance - (facep->mDistance+32))*mInvBinSize );
+
+ if (dist_bin >= NUM_ALPHA_BINS)
+ {
+ mDistanceBins[NUM_ALPHA_BINS-1].put(facep);
+ //mDistanceBins[NUM_ALPHA_BINS-1].push(facep, (U32)(void*)facep->getTexture());
+ }
+ else if (dist_bin > 0)
+ {
+ mDistanceBins[dist_bin].put(facep);
+ //mDistanceBins[dist_bin].push(facep, (U32)(void*)facep->getTexture());
+ }
+ else
+ {
+ mDistanceBins[0].put(facep);
+ //mDistanceBins[0].push(facep, (U32)(void*)facep->getTexture());
+ }
+ }
+}
+
+BOOL LLDrawPoolAlpha::removeFace(LLFace *facep)
+{
+ BOOL removed = FALSE;
+
+ LLDrawPool::removeFace(facep);
+
+ {
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ if (mDistanceBins[i].removeObj(facep) != -1)
+ {
+ if (removed)
+ {
+ llerrs << "Warning! " << "Face in multiple distance bins on removal" << llendl;
+ }
+ removed = TRUE;
+ }
+ }
+ }
+ if (removed)
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLDrawPoolAlpha::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolAlpha::beginRenderPass(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ // No alpha objects, early exit.
+ return;
+ }
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+
+void LLDrawPoolAlpha::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA);
+
+ if (mDrawFace.empty())
+ {
+ // No alpha objects, early exit.
+ return;
+ }
+
+ GLfloat shiny[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ GLint specularIndex = (mVertexShaderLevel > 0) ?
+ gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR] : 0;
+
+ S32 diffTex = 0;
+ S32 envTex = -1;
+
+ if (mVertexShaderLevel > 0) //alpha pass uses same shader as shiny/bump
+ {
+ envTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (envTex >= 0 && cube_map)
+ {
+ cube_map->bind();
+ cube_map->setMatrix(1);
+ }
+
+ if (specularIndex > 0)
+ {
+ glVertexAttrib4fARB(specularIndex, 0, 0, 0, 0);
+ }
+
+ S32 scatterTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ diffTex = gPipeline.mObjectAlphaProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ S32 i, j;
+ glAlphaFunc(GL_GREATER,0.01f);
+ // This needs to be turned off or there will be lots of artifacting with the clouds - djs
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ LLDynamicArray<LLFace*>* distance_bins;
+ distance_bins = mDistanceBins;
+
+ S32 num_bins_no_alpha_test = ((gPickAlphaThreshold != 0.f) && gUsePickAlpha) ?
+ (NUM_ALPHA_BINS - llmax(2, (S32)(ALPHA_FALLOFF_START_DISTANCE * mInvBinSize))) :
+ NUM_ALPHA_BINS;
+
+ typedef std::vector<LLFace*> face_list_t;
+
+ for (i = 0; i < num_bins_no_alpha_test; i++)
+ {
+ S32 obj_count = distance_bins[i].count();
+
+ if (!obj_count)
+ {
+ continue;
+ }
+ else if (i > (NUM_ALPHA_BINS / 2) && obj_count < 100)
+ {
+ face_list_t pri_queue;
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+
+ for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
+ {
+ const LLFace &face = *(*iter);
+ face.enableLights();
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ else
+ {
+ S32 count = distance_bins[i].count();
+ for (j = 0; j < count; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ face.enableLights();
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ }
+
+ GLfloat ogl_matrix[16];
+ gCamera->getOpenGLTransform(ogl_matrix);
+
+ for (i = num_bins_no_alpha_test; i < NUM_ALPHA_BINS; i++)
+ {
+ BOOL use_pri_queue = distance_bins[i].count() < 100;
+
+ face_list_t pri_queue;
+
+ if (use_pri_queue)
+ {
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+ }
+
+ S32 count = distance_bins[i].count();
+ for (j = 0; j < count; j++)
+ {
+ const LLFace &face = use_pri_queue ? *pri_queue[j] : *distance_bins[i][j];
+ F32 fade_value = face.mAlphaFade * gPickAlphaThreshold;
+
+ face.enableLights();
+
+ if (fade_value < 1.f)
+ {
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ glAlphaFunc(GL_LESS, fade_value);
+ glBlendFunc(GL_ZERO, GL_ONE);
+ LLViewerImage::bindTexture(gPipeline.mAlphaSizzleImagep, diffTex);
+ LLVector4 s_params(ogl_matrix[2], ogl_matrix[6], ogl_matrix[10], ogl_matrix[14]);
+ LLVector4 t_params(ogl_matrix[1], ogl_matrix[5], ogl_matrix[9], ogl_matrix[13]);
+
+ LLGLEnable gls_texgen_s(GL_TEXTURE_GEN_S);
+ LLGLEnable gls_texgen_t(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, s_params.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, t_params.mV);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ }
+
+ {
+ // should get GL_GREATER to work, as it's faster
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LESS);
+ glAlphaFunc(GL_GEQUAL, fade_value);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ face.bindTexture(diffTex);
+ if ((mVertexShaderLevel > 0) && face.getTextureEntry() && specularIndex > 0)
+ {
+ U8 s = face.getTextureEntry()->getShiny();
+ glVertexAttrib4fARB(specularIndex, shiny[s], shiny[s], shiny[s], shiny[s]);
+ }
+ face.renderIndexed(getRawIndices());
+ }
+ }
+
+ // render opaque portion of actual texture
+ glAlphaFunc(GL_GREATER, 0.98f);
+
+ face.bindTexture(diffTex);
+ face.renderIndexed(getRawIndices());
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+
+ if (mVertexShaderLevel > 0) //single pass shader driven shiny/bump
+ {
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (envTex >= 0 && cube_map)
+ {
+ cube_map->restoreMatrix();
+ }
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectAlphaProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ if (sShowDebugAlpha)
+ {
+ gPipeline.disableLights();
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.bind();
+ }
+
+ LLViewerImage::sSmokeImagep->bind();
+ LLOverrideFaceColor override_color(this, 1.f, 0.f, 0.f, 1.f);
+ glColor4f(1.f, 0.f, 0.f, 1.f); // in case vertex shaders are enabled
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ if (distance_bins[i].count() < 100)
+ {
+ face_list_t pri_queue;
+ pri_queue.reserve(distance_bins[i].count());
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ pri_queue.push_back(distance_bins[i][j]);
+ }
+ std::sort(pri_queue.begin(), pri_queue.end(), LLFace::CompareDistanceGreater());
+
+ for (face_list_t::iterator iter = pri_queue.begin(); iter != pri_queue.end(); iter++)
+ {
+ const LLFace &face = *(*iter);
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ else
+ {
+ for (j = 0; j < distance_bins[i].count(); j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ face.renderIndexed(getRawIndices());
+ mIndicesDrawn += face.getIndicesCount();
+ }
+ }
+ }
+
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.unbind();
+ }
+
+ }
+
+}
+
+void LLDrawPoolAlpha::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ // force faces on focus object to proper alpha cutoff based on object bbox distance
+ if (gAgent.getFocusObject())
+ {
+ LLDrawable* drawablep = gAgent.getFocusObject()->mDrawable;
+
+ if (drawablep)
+ {
+ const S32 num_faces = drawablep->getNumFaces();
+
+ for (S32 f = 0; f < num_faces; f++)
+ {
+ LLFace* facep = drawablep->getFace(f);
+ facep->mDistance = gAgent.getFocusObjectDist();
+ }
+ }
+ }
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSObjectSelectAlpha gls_alpha;
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glAlphaFunc(gPickTransparent ? GL_GEQUAL : GL_GREATER, 0.f);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+
+ LLDynamicArray<LLFace*>* distance_bins;
+ distance_bins = mDistanceBins;
+
+ S32 j;
+ S32 num_bins_no_alpha_test = (gPickAlphaThreshold != 0.f) ?
+ (NUM_ALPHA_BINS - llmax(2, (S32)(ALPHA_FALLOFF_START_DISTANCE * mInvBinSize))) :
+ NUM_ALPHA_BINS;
+
+ S32 i;
+ for (i = 0; i < num_bins_no_alpha_test; i++)
+ {
+ S32 distance_bin_size = distance_bins[i].count();
+ for (j = 0; j < distance_bin_size; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+ if (face.getDrawable() && !face.getDrawable()->isDead() && (face.getViewerObject()->mGLName))
+ {
+ face.bindTexture();
+ face.renderForSelect();
+ }
+ }
+ }
+
+ for (i = num_bins_no_alpha_test; i < NUM_ALPHA_BINS; i++)
+ {
+ S32 distance_bin_size = distance_bins[i].count();
+ if (distance_bin_size)
+ {
+ for (j = 0; j < distance_bin_size; j++)
+ {
+ const LLFace &face = *distance_bins[i][j];
+
+ glAlphaFunc(GL_GEQUAL, face.mAlphaFade * gPickAlphaTargetThreshold);
+
+ if (face.getDrawable() && !face.getDrawable()->isDead() && (face.getViewerObject()->mGLName))
+ {
+ face.bindTexture();
+ face.renderForSelect();
+ }
+ }
+ }
+ }
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+}
+
+
+void LLDrawPoolAlpha::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolAlpha::resetDrawOrders()
+{
+ LLDrawPool::resetDrawOrders();
+
+ for (S32 i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ mDistanceBins[i].resize(0);
+ }
+}
+
+BOOL LLDrawPoolAlpha::verify() const
+{
+ S32 i, j;
+ BOOL ok;
+ ok = LLDrawPool::verify();
+ for (i = 0; i < NUM_ALPHA_BINS; i++)
+ {
+ for (j = 0; j < mDistanceBins[i].count(); j++)
+ {
+ const LLFace &face = *mDistanceBins[i][j];
+ if (!face.verify())
+ {
+ ok = FALSE;
+ }
+ }
+ }
+ return ok;
+}
+
+LLViewerImage *LLDrawPoolAlpha::getDebugTexture()
+{
+ return LLViewerImage::sSmokeImagep;
+}
+
+
+LLColor3 LLDrawPoolAlpha::getDebugColor() const
+{
+ return LLColor3(1.f, 0.f, 0.f);
+}
+
+S32 LLDrawPoolAlpha::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectAlphaProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolAlpha::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolAlpha::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolAlpha::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolalpha.h b/indra/newview/lldrawpoolalpha.h
new file mode 100644
index 0000000000..6f24959e50
--- /dev/null
+++ b/indra/newview/lldrawpoolalpha.h
@@ -0,0 +1,64 @@
+/**
+ * @file lldrawpoolalpha.h
+ * @brief LLDrawPoolAlpha class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLALPHA_H
+#define LL_LLDRAWPOOLALPHA_H
+
+#include "lldrawpool.h"
+#include "llviewerimage.h"
+#include "llframetimer.h"
+
+class LLFace;
+class LLColor4;
+
+class LLDrawPoolAlpha: public LLDrawPool
+{
+public:
+ LLDrawPoolAlpha();
+ /*virtual*/ ~LLDrawPoolAlpha();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void beginRenderPass(S32 pass = 0);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+
+ /*virtual*/ void enqueue(LLFace *face);
+ /*virtual*/ BOOL removeFace(LLFace *face);
+ /*virtual*/ void resetDrawOrders();
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+
+ virtual S32 getMaterialAttribIndex();
+
+ BOOL mRebuiltLastFrame;
+ enum
+ {
+ NUM_ALPHA_BINS = 1024
+ };
+
+ /*virtual*/ BOOL verify() const;
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ static BOOL sShowDebugAlpha;
+protected:
+ F32 mMinDistance;
+ F32 mMaxDistance;
+ F32 mInvBinSize;
+
+ LLDynamicArray<LLFace*> mDistanceBins[NUM_ALPHA_BINS];
+};
+
+#endif // LL_LLDRAWPOOLALPHA_H
diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp
new file mode 100644
index 0000000000..dfe75084b5
--- /dev/null
+++ b/indra/newview/lldrawpoolavatar.cpp
@@ -0,0 +1,610 @@
+/**
+ * @file lldrawpoolavatar.cpp
+ * @brief LLDrawPoolAvatar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolavatar.h"
+
+#include "llvoavatar.h"
+#include "m3math.h"
+
+#include "llagparray.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "noise.h"
+#include "pipeline.h"
+
+extern F32 gFrameDTClamped;
+extern BOOL gUseGLPick;
+
+F32 CLOTHING_GRAVITY_EFFECT = 0.7f;
+F32 CLOTHING_ACCEL_FORCE_FACTOR = 0.2f;
+const S32 NUM_TEST_AVATARS = 30;
+const S32 MIN_PIXEL_AREA_2_PASS_SKINNING = 500000000;
+
+// Format for gAGPVertices
+// vertex format for bumpmapping:
+// vertices 12
+// pad 4
+// normals 12
+// pad 4
+// texcoords0 8
+// texcoords1 8
+// total 48
+//
+// for no bumpmapping
+// vertices 12
+// texcoords 8
+// normals 12
+// total 32
+//
+
+S32 AVATAR_OFFSET_POS = 0;
+S32 AVATAR_OFFSET_NORMAL = 16;
+S32 AVATAR_OFFSET_TEX0 = 32;
+S32 AVATAR_OFFSET_TEX1 = 40;
+S32 AVATAR_VERTEX_BYTES = 48;
+
+
+BOOL gAvatarEmbossBumpMap = FALSE;
+
+LLDrawPoolAvatar::LLDrawPoolAvatar() :
+LLDrawPool(POOL_AVATAR,
+ DATA_SIMPLE_IL_MASK,
+ DATA_VERTEX_WEIGHTS_MASK | DATA_CLOTHING_WEIGHTS_MASK )
+{
+ mCleanupUnused = FALSE;
+
+ // Overide the data layout
+ mDataMaskIL = 0;
+ mStride = 0;
+ for (S32 i = 0; i < DATA_MAX_TYPES; i++)
+ {
+ mDataOffsets[i] = 0;
+ }
+
+ // Note: padding is to speed up SSE code
+ mDataMaskIL |= DATA_VERTICES_MASK;
+ mDataOffsets[DATA_VERTICES] = mStride;
+ mStride += sDataSizes[DATA_VERTICES];
+
+ mStride += 4;
+
+ mDataMaskIL |= DATA_NORMALS_MASK;
+ mDataOffsets[DATA_NORMALS] = mStride;
+ mStride += sDataSizes[DATA_NORMALS];
+
+ mStride += 4;
+
+ // Note: binormals are stripped off in software blending
+ mDataMaskIL |= DATA_BINORMALS_MASK;
+ mDataOffsets[DATA_BINORMALS] = mStride;
+ mStride += sDataSizes[DATA_BINORMALS];
+
+ mStride += 4; // To keep the structure 16-byte aligned (for SSE happiness)
+
+ mDataMaskIL |= DATA_TEX_COORDS0_MASK;
+ mDataOffsets[DATA_TEX_COORDS0] = mStride;
+ mStride += sDataSizes[DATA_TEX_COORDS0];
+
+ mDataMaskIL |= DATA_TEX_COORDS1_MASK;
+ mDataOffsets[DATA_TEX_COORDS1] = mStride;
+ mStride += sDataSizes[DATA_TEX_COORDS1];
+
+ //LLDebugVarMessageBox::show("acceleration", &CLOTHING_ACCEL_FORCE_FACTOR, 10.f, 0.1f);
+ //LLDebugVarMessageBox::show("gravity", &CLOTHING_GRAVITY_EFFECT, 10.f, 0.1f);
+
+}
+
+//-----------------------------------------------------------------------------
+// instancePool()
+//-----------------------------------------------------------------------------
+LLDrawPool *LLDrawPoolAvatar::instancePool()
+{
+ return new LLDrawPoolAvatar();
+}
+
+
+S32 LLDrawPoolAvatar::rebuild()
+{
+ mRebuildTime++;
+ if (mRebuildTime > mRebuildFreq)
+ {
+ flushAGP();
+
+ mRebuildTime = 0;
+ }
+
+ return 0;
+}
+
+BOOL gRenderAvatar = TRUE;
+
+void LLDrawPoolAvatar::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLDrawPoolAvatar::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS);
+ renderAvatars(NULL); // render all avatars
+}
+
+void LLDrawPoolAvatar::renderAvatars(LLVOAvatar* single_avatar, BOOL no_shaders)
+{
+ if (no_shaders)
+ {
+ mVertexShaderLevel = 0;
+ }
+
+ if (!gRenderAvatar)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty() && !single_avatar)
+ {
+ return;
+ }
+
+ LLVOAvatar *avatarp;
+
+ if (single_avatar)
+ {
+ avatarp = single_avatar;
+ }
+ else
+ {
+ const LLFace *facep = mDrawFace[0];
+ if (!facep->getDrawable())
+ {
+ return;
+ }
+ avatarp = (LLVOAvatar *)(facep->getDrawable()->getVObj());
+ }
+
+ if (avatarp->isDead() || avatarp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLOverrideFaceColor color(this, 1.0f, 1.0f, 1.0f, 1.0f);
+
+ if( !single_avatar || (avatarp == single_avatar) )
+ {
+ if (LLVOAvatar::sShowCollisionVolumes)
+ {
+ LLGLSNoTexture no_texture;
+ avatarp->renderCollisionVolumes();
+ }
+
+ LLGLEnable normalize(GL_NORMALIZE);
+
+ if (avatarp->mIsSelf && LLAgent::sDebugDisplayTarget)
+ {
+ LLGLSNoTexture gls_no_texture;
+ LLVector3 pos = avatarp->getPositionAgent();
+
+ color.setColor(1.0f, 0.0f, 0.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mDrawable->getPositionAgent();
+ color.setColor(1.0f, 0.0f, 0.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mRoot.getWorldPosition();
+ color.setColor(1.0f, 1.0f, 1.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ pos = avatarp->mPelvisp->getWorldPosition();
+ color.setColor(0.0f, 0.0f, 1.0f, 0.8f);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv((pos - LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.2f, 0.f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.2f, 0.f)).mV);
+ glVertex3fv((pos - LLVector3(0.f, 0.f, 0.2f)).mV);
+ glVertex3fv((pos + LLVector3(0.f, 0.f, 0.2f)).mV);
+ }glEnd();
+
+ color.setColor(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSLShader* vertex_program = &gPipeline.mAvatarProgram;
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+ }
+
+ //--------------------------------------------------------------------------------
+ // this is where we first hit the software blending path
+ // if enabled, we need to set up the proper buffers and avoid setting other state
+ //--------------------------------------------------------------------------------
+ if (!(mVertexShaderLevel > 0))
+ {
+
+ // performance could be increased by better utilizing the buffers, for example, only using 1k buffers for lo-res
+ // avatars. But the only problem with using fewer buffers is that we're more likely to wait for a fence to complete
+
+ // vertex format:
+ // vertices 12
+ // texcoords 8
+ // normals 12
+ // binormals 12
+ // padding 4
+ // total 48
+
+ // Rotate to the next buffer, round-robin.
+ gPipeline.bufferRotate();
+
+ // Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
+ gPipeline.bufferWaitFence();
+
+ // Need to do this because we may be rendering without AGP even in AGP mode
+ U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
+ glVertexPointer( 3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
+ glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
+ glNormalPointer( GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
+
+ }
+
+ if ((mVertexShaderLevel > 0)) // for hardware blending
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer(0);
+ }
+
+ if ((mVertexShaderLevel > 0))
+ { //eyeballs render with the specular shader
+ gPipeline.mAvatarEyeballProgram.bind();
+ gPipeline.mMaterialIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+ gPipeline.mSpecularIndex = gPipeline.mAvatarEyeballProgram.mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+
+ S32 index = gPipeline.mAvatarEyeballProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gSky.mVOSkyp->getScatterMap()->bind(index);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if (avatarp->mSpecialRenderMode == 0) // normal
+ {
+ gPipeline.enableLightsAvatar(avatarp->mDrawable->getSunShadowFactor());
+ }
+ else if (avatarp->mSpecialRenderMode == 1) // anim preview
+ {
+ gPipeline.enableLightsAvatarEdit(LLColor4(0.7f, 0.6f, 0.3f, 1.f));
+ }
+ else // 2=image preview, 3=morph view
+ {
+ gPipeline.enableLightsAvatarEdit(LLColor4(.5f, .5f, .5f, 1.f));
+ }
+
+ // render rigid meshes (eyeballs) first
+ mIndicesDrawn += avatarp->renderRigid();
+
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mAvatarEyeballProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
+ {
+ LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
+ LLVector3 next_pos_root = orig_pos_root;
+ for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
+ {
+ next_pos_root.mV[VX] += 1.f;
+ if (i % 5 == 0)
+ {
+ next_pos_root.mV[VY] += 1.f;
+ next_pos_root.mV[VX] = orig_pos_root.mV[VX];
+ }
+
+ avatarp->mRoot.setPosition(next_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+
+ mIndicesDrawn += avatarp->renderRigid();
+ }
+ avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+ }
+
+ if ((mVertexShaderLevel > 0)) // for hardware blending
+ {
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ bindGLTexCoordPointer(1);
+
+ bindGLBinormalPointer(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ gPipeline.mMaterialIndex = vertex_program->mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+ gPipeline.mSpecularIndex = vertex_program->mAttribute[LLPipeline::GLSL_SPECULAR_COLOR];
+ }
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer(0);
+ vertex_program->bind();
+ bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ bindGLVertexClothingWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ enable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ }
+ enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ enable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ }
+
+ vertex_program->enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ S32 index = vertex_program->enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gSky.mVOSkyp->getScatterMap()->bind(index);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ }
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ LLMatrix4 rot_mat;
+ gCamera->getMatrixToLocal(rot_mat);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ rot_mat *= cfr;
+
+ LLVector4 wind;
+ wind.setVec(avatarp->mWindVec);
+ wind.mV[VW] = 0;
+ wind = wind * rot_mat;
+ wind.mV[VW] = avatarp->mWindVec.mV[VW];
+
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_WIND, wind.mV);
+ F32 phase = -1.f * (avatarp->mRipplePhase);
+
+ F32 freq = 7.f + (noise1(avatarp->mRipplePhase) * 2.f);
+ LLVector4 sin_params(freq, freq, freq, phase);
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_SINWAVE, sin_params.mV);
+
+ LLVector4 gravity(0.f, 0.f, -CLOTHING_GRAVITY_EFFECT, 0.f);
+ gravity = gravity * rot_mat;
+ vertex_program->vertexAttrib4fv(LLPipeline::GLSL_AVATAR_GRAVITY, gravity.mV);
+ }
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+
+ if (!gRenderForSelect && avatarp->mIsSelf && LLVOAvatar::sAvatarLoadTest)
+ {
+ LLVector3 orig_pos_root = avatarp->mRoot.getPosition();
+ LLVector3 next_pos_root = orig_pos_root;
+ for (S32 i = 0; i < NUM_TEST_AVATARS; i++)
+ {
+ next_pos_root.mV[VX] += 1.f;
+ if (i % 5 == 0)
+ {
+ next_pos_root.mV[VY] += 1.f;
+ next_pos_root.mV[VX] = orig_pos_root.mV[VX];
+ }
+
+ avatarp->mRoot.setPosition(next_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+ }
+ avatarp->mRoot.setPosition(orig_pos_root); // avatar load test
+ avatarp->mRoot.updateWorldMatrixChildren(); // avatar load test
+ }
+
+ // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+ if (!(mVertexShaderLevel > 0))
+ {
+ // want for the previously bound fence to finish
+ gPipeline.bufferSendFence();
+ }
+ else
+ {
+ vertex_program->disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ vertex_program->disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ if ((mVertexShaderLevel >= SHADER_LEVEL_BUMP))
+ {
+ disable_binormals(vertex_program->mAttribute[LLPipeline::GLSL_BINORMAL]);
+ }
+ if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ disable_cloth_weights(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_CLOTHING]);
+ }
+
+ vertex_program->unbind();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// renderForSelect()
+//-----------------------------------------------------------------------------
+void LLDrawPoolAvatar::renderForSelect()
+{
+ if (gUseGLPick)
+ {
+ return;
+ }
+ //gGLSObjectSelectDepthAlpha.set();
+
+ if (!gRenderAvatar)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ const LLFace *facep = mDrawFace[0];
+ if (!facep->getDrawable())
+ {
+ return;
+ }
+ LLVOAvatar *avatarp = (LLVOAvatar *)(facep->getDrawable()->getVObj());
+
+ if (avatarp->isDead() || avatarp->mIsDummy || avatarp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLGLSLShader* vertex_program = &gPipeline.mAvatarPickProgram;
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mAvatarMatrixParam = vertex_program->mUniform[LLPipeline::GLSL_AVATAR_MATRIX];
+ }
+ glAlphaFunc(GL_GEQUAL, 0.2f);
+ glBlendFunc(GL_ONE, GL_ZERO);
+
+ //--------------------------------------------------------------------------------
+ // this is where we first hit the software blending path
+ // if enabled, we need to set up the proper buffers and avoid setting other state
+ //--------------------------------------------------------------------------------
+ if (!(mVertexShaderLevel > 0) || gUseGLPick)
+ {
+
+ // Rotate to the next buffer, round-robin.
+ gPipeline.bufferRotate();
+
+ // Wait until the hardware is done reading the last set of vertices from the buffer before writing the next set.
+ gPipeline.bufferWaitFence();
+
+ // Need to do this because we may be rendering without AGP even in AGP mode
+ U8* buffer_offset_start = gPipeline.bufferGetScratchMemory();
+ glVertexPointer( 3, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_POS);
+ glTexCoordPointer(2, GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_TEX0);
+ glNormalPointer( GL_FLOAT, AVATAR_VERTEX_BYTES, buffer_offset_start + AVATAR_OFFSET_NORMAL);
+ }
+
+ S32 name = avatarp->mDrawable->getVObj()->mGLName;
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+ glColor4ubv(color.mV);
+
+ if ((mVertexShaderLevel > 0) && !gUseGLPick) // for hardware blending
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer(0);
+ }
+
+ // render rigid meshes (eyeballs) first
+ mIndicesDrawn += avatarp->renderRigid();
+
+ if ((mVertexShaderLevel > 0) && !gUseGLPick) // for hardware blending
+ {
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer(0);
+ vertex_program->bind();
+ bindGLVertexWeightPointer(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ /*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ bindGLVertexClothingWeightPointer();
+ enable_cloth_weights();
+ }*/
+ enable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+ }
+
+ mIndicesDrawn += avatarp->renderSkinned(AVATAR_RENDER_PASS_SINGLE);
+
+ // if we're in software-blending, remember to set the fence _after_ we draw so we wait till this rendering is done
+ if (!(mVertexShaderLevel > 0) || gUseGLPick)
+ {
+ // want for the previously bound fence to finish
+ gPipeline.bufferSendFence();
+ }
+ else
+ {
+ vertex_program->unbind();
+ disable_vertex_weighting(vertex_program->mAttribute[LLPipeline::GLSL_AVATAR_WEIGHT]);
+
+ /*if ((mVertexShaderLevel >= SHADER_LEVEL_CLOTH))
+ {
+ disable_cloth_weights();
+ }*/
+ }
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // restore texture mode
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+//-----------------------------------------------------------------------------
+// getDebugTexture()
+//-----------------------------------------------------------------------------
+LLViewerImage *LLDrawPoolAvatar::getDebugTexture()
+{
+ if (mReferences.empty())
+ {
+ return NULL;
+ }
+ LLFace *face = mReferences[0];
+ if (!face->getDrawable())
+ {
+ return NULL;
+ }
+ const LLViewerObject *objectp = face->getDrawable()->getVObj();
+
+ // Avatar should always have at least 1 (maybe 3?) TE's.
+ return objectp->getTEImage(0);
+}
+
+
+LLColor3 LLDrawPoolAvatar::getDebugColor() const
+{
+ return LLColor3(0.f, 1.f, 0.f);
+}
diff --git a/indra/newview/lldrawpoolavatar.h b/indra/newview/lldrawpoolavatar.h
new file mode 100644
index 0000000000..0d706b012b
--- /dev/null
+++ b/indra/newview/lldrawpoolavatar.h
@@ -0,0 +1,54 @@
+/**
+ * @file lldrawpoolavatar.h
+ * @brief LLDrawPoolAvatar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLAVATAR_H
+#define LL_LLDRAWPOOLAVATAR_H
+
+#include "lldrawpool.h"
+
+class LLVOAvatar;
+
+class LLDrawPoolAvatar : public LLDrawPool
+{
+protected:
+ S32 mNumFaces;
+public:
+ enum
+ {
+ SHADER_LEVEL_BUMP = 2,
+ SHADER_LEVEL_CLOTH = 3
+ };
+
+ LLDrawPoolAvatar();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ S32 rebuild();
+
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+
+ void renderAvatars(LLVOAvatar *single_avatar, BOOL no_shaders = FALSE); // renders only one avatar if single_avatar is not null.
+};
+
+
+
+extern S32 AVATAR_OFFSET_POS;
+extern S32 AVATAR_OFFSET_NORMAL;
+extern S32 AVATAR_OFFSET_TEX0;
+extern S32 AVATAR_OFFSET_TEX1;
+extern S32 AVATAR_VERTEX_BYTES;
+const S32 AVATAR_BUFFER_ELEMENTS = 8192; // Needs to be enough to store all avatar vertices.
+
+extern BOOL gAvatarEmbossBumpMap;
+#endif // LL_LLDRAWPOOLAVATAR_H
diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp
new file mode 100644
index 0000000000..b9d1cb17a0
--- /dev/null
+++ b/indra/newview/lldrawpoolbump.cpp
@@ -0,0 +1,1135 @@
+/**
+ * @file lldrawpoolbump.cpp
+ * @brief LLDrawPoolBump class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolbump.h"
+
+#include "llstl.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llimagegl.h"
+#include "m3math.h"
+#include "m4math.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "lldrawable.h"
+#include "lldrawpoolsimple.h"
+#include "llface.h"
+#include "llgl.h"
+#include "llsky.h"
+#include "lltextureentry.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "pipeline.h"
+
+
+//#include "llimagebmp.h"
+//#include "../tools/imdebug/imdebug.h"
+
+// static
+LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
+
+// static
+U32 LLStandardBumpmap::sStandardBumpmapCount = 0;
+
+// static
+LLBumpImageList gBumpImageList;
+
+const S32 STD_BUMP_LATEST_FILE_VERSION = 1;
+
+S32 LLDrawPoolBump::sBumpTex = -1;
+S32 LLDrawPoolBump::sDiffTex = -1;
+S32 LLDrawPoolBump::sEnvTex = -1;
+
+// static
+void LLStandardBumpmap::init()
+{
+ LLStandardBumpmap::restoreGL();
+}
+
+// static
+void LLStandardBumpmap::shutdown()
+{
+ LLStandardBumpmap::destroyGL();
+}
+
+// static
+void LLStandardBumpmap::restoreGL()
+{
+ llassert( LLStandardBumpmap::sStandardBumpmapCount == 0 );
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("None"); // BE_NO_BUMP
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Brightness"); // BE_BRIGHTNESS
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount++] = LLStandardBumpmap("Darkness"); // BE_DARKNESS
+
+ std::string file_name = gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "std_bump.ini" );
+ FILE* file = LLFile::fopen( file_name.c_str(), "rt" );
+ if( !file )
+ {
+ llwarns << "Could not open std_bump <" << file_name << ">" << llendl;
+ return;
+ }
+
+ S32 file_version = 0;
+
+ S32 fields_read = fscanf( file, "LLStandardBumpmap version %d", &file_version );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad LLStandardBumpmap header" << llendl;
+ return;
+ }
+
+ if( file_version > STD_BUMP_LATEST_FILE_VERSION )
+ {
+ llwarns << "LLStandardBumpmap has newer version (" << file_version << ") than viewer (" << STD_BUMP_LATEST_FILE_VERSION << ")" << llendl;
+ return;
+ }
+
+ while( !feof(file) && (LLStandardBumpmap::sStandardBumpmapCount < (U32)TEM_BUMPMAP_COUNT) )
+ {
+ char label[2048] = "";
+ char bump_file[2048] = "";
+ fields_read = fscanf( file, "\n%s %s", label, bump_file);
+ if( EOF == fields_read )
+ {
+ break;
+ }
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad LLStandardBumpmap entry" << llendl;
+ return;
+ }
+
+ llinfos << "Loading bumpmap: " << bump_file << " from viewerart" << llendl;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mLabel = label;
+ gStandardBumpmapList[LLStandardBumpmap::sStandardBumpmapCount].mImage = gImageList.getImage( LLUUID(gViewerArt.getString(bump_file)) );
+ LLStandardBumpmap::sStandardBumpmapCount++;
+ }
+
+ fclose( file );
+}
+
+// static
+void LLStandardBumpmap::destroyGL()
+{
+ for( U32 i = 0; i < LLStandardBumpmap::sStandardBumpmapCount; i++ )
+ {
+ gStandardBumpmapList[i].mLabel.assign("");
+ gStandardBumpmapList[i].mImage = NULL;
+ }
+ sStandardBumpmapCount = 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////
+
+LLDrawPoolBump::LLDrawPoolBump(LLViewerImage *texturep) :
+ LLDrawPool(POOL_BUMP, DATA_BUMP_IL_MASK | DATA_COLORS_MASK, DATA_SIMPLE_NIL_MASK),
+ mTexturep(texturep)
+{
+}
+
+LLDrawPool *LLDrawPoolBump::instancePool()
+{
+ return new LLDrawPoolBump(mTexturep);
+}
+
+
+void LLDrawPoolBump::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+BOOL LLDrawPoolBump::match(LLFace* last_face, LLFace* facep)
+{
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
+ !last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ !facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
+ facep->getRenderColor() == last_face->getRenderColor() &&
+ facep->getTextureEntry()->getShiny() == last_face->getTextureEntry()->getShiny() &&
+ facep->getTextureEntry()->getBumpmap() == last_face->getTextureEntry()->getBumpmap())
+ {
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ if (last_face->isState(LLFace::GLOBAL))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!last_face->isState(LLFace::GLOBAL))
+ {
+ if (last_face->getRenderMatrix() == facep->getRenderMatrix())
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+// static
+S32 LLDrawPoolBump::numBumpPasses()
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0)
+ {
+ return 1; // single pass for shaders
+ }
+ else
+ {
+ if (gSavedSettings.getBOOL("RenderObjectBump"))
+ return 3;
+ else
+ return 1;
+ }
+}
+
+S32 LLDrawPoolBump::getNumPasses()
+{
+ return numBumpPasses();
+}
+
+void LLDrawPoolBump::beginRenderPass(S32 pass)
+{
+ switch( pass )
+ {
+ case 0:
+ beginPass0(this);
+ break;
+ case 1:
+ beginPass1();
+ break;
+ case 2:
+ beginPass2();
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+void LLDrawPoolBump::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ if (!mTexturep)
+ {
+ return;
+ }
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ const U32* index_array = getRawIndices();
+
+ S32 indices = 0;
+ switch( pass )
+ {
+ case 0:
+ {
+ stop_glerror();
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ stop_glerror();
+
+ LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
+ LLGLState blend(GL_BLEND, FALSE);
+ LLViewerImage* tex = getTexture();
+ if (tex && tex->getPrimaryFormat() == GL_ALPHA)
+ {
+ // Enable Invisibility Hack
+ alpha_test.enable();
+ blend.enable();
+ }
+ indices += renderPass0(this, mDrawFace, index_array, mTexturep);
+ break;
+ }
+ case 1:
+ {
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ indices += renderPass1(mDrawFace, index_array, mTexturep);
+ break;
+ }
+ case 2:
+ {
+ bindGLVertexPointer();
+ // Texture unit 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ bindGLTexCoordPointer();
+ // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ bindGLTexCoordPointer(1);
+ indices += renderPass2(mDrawFace, index_array, mTexturep);
+ break;
+ }
+ default:
+ {
+ llassert(0);
+ break;
+ }
+ }
+ mIndicesDrawn += indices;
+}
+
+void LLDrawPoolBump::endRenderPass(S32 pass)
+{
+ switch( pass )
+ {
+ case 0:
+ endPass0(this);
+ break;
+ case 1:
+ endPass1();
+ break;
+ case 2:
+ endPass2();
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+//static
+void LLDrawPoolBump::beginPass0(LLDrawPool* pool)
+{
+ stop_glerror();
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ enable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ sEnvTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (sEnvTex >= 0 && cube_map)
+ {
+ cube_map->bind();
+ cube_map->setMatrix(1);
+ }
+
+ sBumpTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ sDiffTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ S32 scatterTex = gPipeline.mObjectBumpProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ }
+ stop_glerror();
+}
+
+//static
+S32 LLDrawPoolBump::renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ if (!tex)
+ {
+ return 0;
+ }
+
+ if (face_list.empty())
+ {
+ return 0;
+ }
+
+ stop_glerror();
+
+ S32 res = 0;
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ pool->bindGLBinormalPointer(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ LLViewerImage::bindTexture(tex, sDiffTex);
+
+ //single pass shader driven shiny/bump
+ LLGLDisable(GL_ALPHA_TEST);
+
+ LLViewerImage::sWhiteImagep->bind(sBumpTex);
+
+ GLfloat alpha[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ LLImageGL* last_bump = NULL;
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ if (te)
+ {
+ U8 index = te->getShiny();
+ LLColor4 col = te->getColor();
+
+ gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,
+ col.mV[0], col.mV[1], col.mV[2], alpha[index]);
+ gPipeline.mObjectBumpProgram.vertexAttrib4f(LLPipeline::GLSL_SPECULAR_COLOR,
+ alpha[index], alpha[index], alpha[index], alpha[index]);
+
+ LLImageGL* bump = getBumpMap(te, tex);
+ if (bump != last_bump)
+ {
+ if (bump)
+ {
+ bump->bind(sBumpTex);
+ }
+ else
+ {
+ LLViewerImage::sWhiteImagep->bind(sBumpTex);
+ }
+ }
+ last_bump = bump;
+
+ // Draw the geometry
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ else
+ {
+ llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
+ }
+ }
+ }
+ else
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
+ LLViewerImage::bindTexture(tex);
+ res = LLDrawPool::drawLoop(face_list, index_array);
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass0(LLDrawPool* pool)
+{
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if (sEnvTex >= 0 && cube_map)
+ {
+ cube_map->restoreMatrix();
+ }
+
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ gPipeline.mObjectBumpProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ disable_binormals(gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_BINORMAL]);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+}
+
+
+//static
+void LLDrawPoolBump::beginPass1()
+{
+ // Second pass: environment map
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if( cube_map )
+ {
+ cube_map->enable(0);
+ cube_map->setMatrix(0);
+ cube_map->bind();
+ }
+}
+
+//static
+S32 LLDrawPoolBump::renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY);
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
+ {
+ return 0;
+ }
+
+ S32 res = 0;
+ if( gSky.mVOSkyp->getCubeMap() )
+ {
+ //LLGLSPipelineAlpha gls;
+ //LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
+ LLGLEnable blend_enable(GL_BLEND);
+
+ GLfloat alpha[4] =
+ {
+ 0.00f,
+ 0.25f,
+ 0.5f,
+ 0.75f
+ };
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ if (te)
+ {
+ U8 index = te->getShiny();
+ if( index > 0 )
+ {
+ LLOverrideFaceColor override_color(facep->getPool(), 1, 1, 1, alpha[index]);
+
+ // Draw the geometry
+ facep->enableLights();
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ }
+ else
+ {
+ llwarns << "DrawPoolBump has face with invalid texture entry." << llendl;
+ }
+ }
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass1()
+{
+ LLCubeMap* cube_map = gSky.mVOSkyp->getCubeMap();
+ if( cube_map )
+ {
+ cube_map->disable();
+ cube_map->restoreMatrix();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+
+// static
+LLImageGL* LLDrawPoolBump::getBumpMap(const LLTextureEntry* te, LLViewerImage* tex)
+{
+ U32 bump_code = te->getBumpmap();
+ LLImageGL* bump = NULL;
+
+ switch( bump_code )
+ {
+ case BE_NO_BUMP:
+ bump = NULL;
+ break;
+ case BE_BRIGHTNESS:
+ case BE_DARKNESS:
+ if( tex )
+ {
+ bump = gBumpImageList.getBrightnessDarknessImage( tex, bump_code );
+ }
+ break;
+
+ default:
+ if( bump_code < LLStandardBumpmap::sStandardBumpmapCount )
+ {
+ bump = gStandardBumpmapList[bump_code].mImage;
+ }
+ break;
+ }
+
+ return bump;
+}
+
+//static
+void LLDrawPoolBump::beginPass2()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP);
+ // Optional third pass: emboss bump map
+ stop_glerror();
+
+ // TEXTURE UNIT 0
+ // Output.rgb = texture at texture coord 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_ALPHA);
+
+ // Don't care about alpha output
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ // TEXTURE UNIT 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Don't care about alpha output
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ // src = tex0 + (1 - tex1) - 0.5
+ // = (bump0/2 + 0.5) + (1 - (bump1/2 + 0.5)) - 0.5
+ // = (1 + bump0 - bump1) / 2
+
+
+ // Blend: src * dst + dst * src
+ // = 2 * src * dst
+ // = 2 * ((1 + bump0 - bump1) / 2) * dst [0 - 2 * dst]
+ // = (1 + bump0 - bump1) * dst.rgb
+ // = dst.rgb + dst.rgb * (bump0 - bump1)
+ glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
+// glBlendFunc(GL_ONE, GL_ZERO); // temp
+
+ stop_glerror();
+}
+
+//static
+S32 LLDrawPoolBump::renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex)
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) > 0) //everything happens in pass0
+ {
+ return 0;
+ }
+
+ LLGLDisable fog(GL_FOG);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_EQUAL);
+ LLGLEnable tex2d(GL_TEXTURE_2D);
+ LLGLEnable blend(GL_BLEND);
+ S32 res = 0;
+
+ LLImageGL* last_bump = NULL;
+
+ for (std::vector<LLFace*>::iterator iter = face_list.begin();
+ iter != face_list.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->mSkipRender)
+ {
+ continue;
+ }
+ LLOverrideFaceColor override_color(facep->getPool(), 1,1,1,1);
+
+ const LLTextureEntry* te = facep->getTextureEntry();
+ LLImageGL* bump = getBumpMap(te, tex);
+
+ if( bump )
+ {
+ if( bump != last_bump )
+ {
+ last_bump = bump;
+
+ // Texture unit 0
+ bump->bind(0);
+ stop_glerror();
+
+ // Texture unit 1
+ bump->bind(1);
+ stop_glerror();
+ }
+
+ // Draw the geometry
+ res += facep->renderIndexed(index_array);
+ stop_glerror();
+ }
+ else
+ {
+// llwarns << "Skipping invalid bump code " << (S32) te->getBumpmap() << llendl;
+ }
+ }
+ return res;
+}
+
+//static
+void LLDrawPoolBump::endPass2()
+{
+ // Disable texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // Disable texture unit 0
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+}
+
+
+void LLDrawPoolBump::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep->getDrawable() && !facep->getDrawable()->isDead() && (facep->getViewerObject()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+
+void LLDrawPoolBump::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolBump::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolBump::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolBump::getDebugTexture()
+{
+ return mTexturep;
+}
+
+LLColor3 LLDrawPoolBump::getDebugColor() const
+{
+ return LLColor3(1.f, 1.f, 0.f);
+}
+
+////////////////////////////////////////////////////////////////
+// List of one-component bump-maps created from other texures.
+
+
+//const LLUUID TEST_BUMP_ID("3d33eaf2-459c-6f97-fd76-5fce3fc29447");
+
+void LLBumpImageList::init()
+{
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+
+ LLStandardBumpmap::init();
+}
+
+void LLBumpImageList::shutdown()
+{
+ mBrightnessEntries.clear();
+ mDarknessEntries.clear();
+ LLStandardBumpmap::shutdown();
+}
+
+void LLBumpImageList::destroyGL()
+{
+ mBrightnessEntries.clear();
+ mDarknessEntries.clear();
+ LLStandardBumpmap::destroyGL();
+}
+
+void LLBumpImageList::restoreGL()
+{
+ // Images will be recreated as they are needed.
+ LLStandardBumpmap::restoreGL();
+}
+
+
+LLBumpImageList::~LLBumpImageList()
+{
+ // Shutdown should have already been called.
+ llassert( mBrightnessEntries.size() == 0 );
+ llassert( mDarknessEntries.size() == 0 );
+}
+
+
+// Note: Does nothing for entries in gStandardBumpmapList that are not actually standard bump images (e.g. none, brightness, and darkness)
+void LLBumpImageList::addTextureStats(U8 bump, const LLUUID& base_image_id,
+ F32 pixel_area, F32 texel_area_ratio, F32 cos_center_angle)
+{
+ bump &= TEM_BUMP_MASK;
+ LLViewerImage* bump_image = gStandardBumpmapList[bump].mImage;
+ if( bump_image )
+ {
+ bump_image->addTextureStats(pixel_area, texel_area_ratio, cos_center_angle);
+ }
+}
+
+
+void LLBumpImageList::updateImages()
+{
+ for (bump_image_map_t::iterator iter = mBrightnessEntries.begin(); iter != mBrightnessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLImageGL* image = curiter->second;
+ if( image )
+ {
+ BOOL destroy = TRUE;
+ if( image->getHasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = FALSE;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //llinfos << "*** Destroying bright " << (void*)image << llendl;
+ mBrightnessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+
+ for (bump_image_map_t::iterator iter = mDarknessEntries.begin(); iter != mDarknessEntries.end(); )
+ {
+ bump_image_map_t::iterator curiter = iter++;
+ LLImageGL* image = curiter->second;
+ if( image )
+ {
+ BOOL destroy = TRUE;
+ if( image->getHasGLTexture())
+ {
+ if( image->getBoundRecently() )
+ {
+ destroy = FALSE;
+ }
+ else
+ {
+ image->destroyGLTexture();
+ }
+ }
+
+ if( destroy )
+ {
+ //llinfos << "*** Destroying dark " << (void*)image << llendl;;
+ mDarknessEntries.erase(curiter); // deletes the image thanks to reference counting
+ }
+ }
+ }
+
+}
+
+
+// Note: the caller SHOULD NOT keep the pointer that this function returns. It may be updated as more data arrives.
+LLImageGL* LLBumpImageList::getBrightnessDarknessImage(LLViewerImage* src_image, U8 bump_code )
+{
+ llassert( (bump_code == BE_BRIGHTNESS) || (bump_code == BE_DARKNESS) );
+
+ LLImageGL* bump = NULL;
+ const F32 BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD = 1000;
+ if( src_image->mMaxVirtualSize > BRIGHTNESS_DARKNESS_PIXEL_AREA_THRESHOLD )
+ {
+ bump_image_map_t* entries_list = NULL;
+ void (*callback_func)( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata ) = NULL;
+
+ switch( bump_code )
+ {
+ case BE_BRIGHTNESS:
+ entries_list = &mBrightnessEntries;
+ callback_func = LLBumpImageList::onSourceBrightnessLoaded;
+ break;
+ case BE_DARKNESS:
+ entries_list = &mDarknessEntries;
+ callback_func = LLBumpImageList::onSourceDarknessLoaded;
+ break;
+ default:
+ llassert(0);
+ return NULL;
+ }
+
+ bump_image_map_t::iterator iter = entries_list->find(src_image->getID());
+ if (iter != entries_list->end())
+ {
+ bump = iter->second;
+ }
+ else
+ {
+ LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,1);
+ raw->clear(0x77, 0x77, 0x77, 0xFF);
+ bump = new LLImageGL( raw, TRUE);
+ bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+ (*entries_list)[src_image->getID()] = bump;
+
+ // Note: this may create an LLImageGL immediately
+ src_image->setLoadedCallback( callback_func, 0, TRUE, new LLUUID(src_image->getID()) );
+ bump = (*entries_list)[src_image->getID()]; // In case callback was called immediately and replaced the image
+
+// bump_total++;
+// llinfos << "*** Creating " << (void*)bump << " " << bump_total << llendl;
+ }
+ }
+
+ return bump;
+}
+
+
+// static
+void LLBumpImageList::onSourceBrightnessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_BRIGHTNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+// static
+void LLBumpImageList::onSourceDarknessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID* source_asset_id = (LLUUID*)userdata;
+ LLBumpImageList::onSourceLoaded( success, src_vi, src, *source_asset_id, BE_DARKNESS );
+ if( final )
+ {
+ delete source_asset_id;
+ }
+}
+
+
+// static
+void LLBumpImageList::onSourceLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump_code )
+{
+ if( success )
+ {
+ bump_image_map_t& entries_list(bump_code == BE_BRIGHTNESS ? gBumpImageList.mBrightnessEntries : gBumpImageList.mDarknessEntries );
+ bump_image_map_t::iterator iter = entries_list.find(source_asset_id);
+ if (iter != entries_list.end())
+ {
+ LLPointer<LLImageRaw> dst_image = new LLImageRaw(src->getWidth(), src->getHeight(), 1);
+ U8* dst_data = dst_image->getData();
+ S32 dst_data_size = dst_image->getDataSize();
+
+ U8* src_data = src->getData();
+ S32 src_data_size = src->getDataSize();
+
+ S32 src_components = src->getComponents();
+
+ // Convert to luminance and then scale and bias that to get ready for
+ // embossed bump mapping. (0-255 maps to 127-255)
+
+ // Convert to fixed point so we don't have to worry about precision/clamping.
+ const S32 FIXED_PT = 8;
+ const S32 R_WEIGHT = S32(0.2995f * (1<<FIXED_PT));
+ const S32 G_WEIGHT = S32(0.5875f * (1<<FIXED_PT));
+ const S32 B_WEIGHT = S32(0.1145f * (1<<FIXED_PT));
+
+ S32 minimum = 255;
+ S32 maximum = 0;
+
+ switch( src_components )
+ {
+ case 1:
+ case 2:
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ dst_data[i] = src_data[j];
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ break;
+ case 3:
+ case 4:
+ if( src_data_size == dst_data_size * src_components )
+ {
+ for( S32 i = 0, j=0; i < dst_data_size; i++, j+= src_components )
+ {
+ // RGB to luminance
+ dst_data[i] = (R_WEIGHT * src_data[j] + G_WEIGHT * src_data[j+1] + B_WEIGHT * src_data[j+2]) >> FIXED_PT;
+ //llassert( dst_data[i] <= 255 );true because it's 8bit
+ if( dst_data[i] < minimum )
+ {
+ minimum = dst_data[i];
+ }
+ if( dst_data[i] > maximum )
+ {
+ maximum = dst_data[i];
+ }
+ }
+ }
+ else
+ {
+ llassert(0);
+ dst_image->clear();
+ }
+ break;
+ default:
+ llassert(0);
+ dst_image->clear();
+ break;
+ }
+
+ if( maximum > minimum )
+ {
+ U8 bias_and_scale_lut[256];
+ F32 twice_one_over_range = 2.f / (maximum - minimum);
+ S32 i;
+
+ const F32 ARTIFICIAL_SCALE = 2.f; // Advantage: exagerates the effect in midrange. Disadvantage: clamps at the extremes.
+ if( BE_DARKNESS == bump_code )
+ {
+ 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));
+ }
+ }
+ else
+ {
+ // BE_LIGHTNESS
+ 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));
+ }
+ }
+
+ for( i = 0; i < dst_data_size; i++ )
+ {
+ dst_data[i] = bias_and_scale_lut[dst_data[i]];
+ }
+ }
+
+ LLImageGL* bump = new LLImageGL( TRUE);
+ bump->setExplicitFormat(GL_ALPHA8, GL_ALPHA);
+ bump->createGLTexture(0, dst_image);
+ iter->second = bump; // derefs (and deletes) old image
+ }
+ else
+ {
+ // entry should have been added in LLBumpImageList::getImage().
+
+ // Not a legit assertion - the bump texture could have been flushed by the bump image manager
+ //llassert(0);
+ }
+ }
+}
+
+S32 LLDrawPoolBump::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectBumpProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolBump::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolBump::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolBump::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolbump.h b/indra/newview/lldrawpoolbump.h
new file mode 100644
index 0000000000..b74acb4561
--- /dev/null
+++ b/indra/newview/lldrawpoolbump.h
@@ -0,0 +1,141 @@
+/**
+ * @file lldrawpoolbump.h
+ * @brief LLDrawPoolBump class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLBUMP_H
+#define LL_LLDRAWPOOLBUMP_H
+
+#include "lldrawpool.h"
+#include "llstring.h"
+#include "lltextureentry.h"
+#include "lluuid.h"
+
+class LLImageRaw;
+
+class LLDrawPoolBump : public LLDrawPool
+{
+protected:
+ LLPointer<LLViewerImage> mTexturep; // The primary texture, not the bump texture
+
+public:
+ LLDrawPoolBump(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void beginRenderPass( S32 pass );
+ /*virtual*/ void endRenderPass( S32 pass );
+ /*virtual*/ S32 getNumPasses();
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+ /*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
+
+ virtual S32 getMaterialAttribIndex();
+ static S32 numBumpPasses();
+
+ static void beginPass0(LLDrawPool* pool);
+ static S32 renderPass0(LLDrawPool* pool, face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass0(LLDrawPool* pool);
+
+ static void beginPass1();
+ static S32 renderPass1(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass1();
+
+ static void beginPass2();
+ static S32 renderPass2(face_array_t& face_list, const U32* index_array, LLViewerImage* tex);
+ static void endPass2();
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+protected:
+ static LLImageGL* getBumpMap(const LLTextureEntry* te, LLViewerImage* tex);
+
+public:
+ static S32 sBumpTex;
+ static S32 sDiffTex;
+ static S32 sEnvTex;
+};
+
+enum EBumpEffect
+{
+ BE_NO_BUMP = 0,
+ BE_BRIGHTNESS = 1,
+ BE_DARKNESS = 2,
+ BE_STANDARD_0 = 3, // Standard must always be the last one
+ BE_COUNT = 4
+};
+
+////////////////////////////////////////////////////////////////
+// List of standard bumpmaps that are specificed by LLTextureEntry::mBump's lower bits
+
+class LLStandardBumpmap
+{
+public:
+ LLStandardBumpmap() : mLabel("") {}
+ LLStandardBumpmap( const char* label ) : mLabel(label) {}
+
+ LLString mLabel;
+ LLPointer<LLViewerImage> mImage;
+
+ static U32 sStandardBumpmapCount; // Number of valid values in gStandardBumpmapList[]
+
+ static void init();
+ static void shutdown();
+ static void restoreGL();
+ static void destroyGL();
+};
+
+extern LLStandardBumpmap gStandardBumpmapList[TEM_BUMPMAP_COUNT];
+
+////////////////////////////////////////////////////////////////
+// List of one-component bump-maps created from other texures.
+
+struct LLBumpImageEntry;
+
+class LLBumpImageList
+{
+public:
+ LLBumpImageList() {}
+ ~LLBumpImageList();
+
+ void init();
+ void shutdown();
+ void destroyGL();
+ void restoreGL();
+ void updateImages();
+
+
+ LLImageGL* getBrightnessDarknessImage(LLViewerImage* src_image, U8 bump_code);
+// LLImageGL* getTestImage();
+ void addTextureStats(U8 bump, const LLUUID& base_image_id,
+ F32 pixel_area, F32 texel_area_ratio, F32 cos_center_angle);
+
+ static void onSourceBrightnessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void onSourceDarknessLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+
+private:
+ static void onSourceLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLUUID& source_asset_id, EBumpEffect bump );
+
+private:
+ typedef std::map<LLUUID, LLPointer<LLImageGL> > bump_image_map_t;
+ bump_image_map_t mBrightnessEntries;
+ bump_image_map_t mDarknessEntries;
+};
+
+extern LLBumpImageList gBumpImageList;
+
+
+
+#endif // LL_LLDRAWPOOLBUMP_H
diff --git a/indra/newview/lldrawpoolclouds.cpp b/indra/newview/lldrawpoolclouds.cpp
new file mode 100644
index 0000000000..c279f085d5
--- /dev/null
+++ b/indra/newview/lldrawpoolclouds.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file lldrawpoolclouds.cpp
+ * @brief LLDrawPoolClouds class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolclouds.h"
+
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llvoclouds.h"
+#include "pipeline.h"
+
+LLDrawPoolClouds::LLDrawPoolClouds() :
+ LLDrawPool(POOL_CLOUDS, DATA_SIMPLE_IL_MASK, 0)
+{
+}
+
+LLDrawPool *LLDrawPoolClouds::instancePool()
+{
+ return new LLDrawPoolClouds();
+}
+
+void LLDrawPoolClouds::enqueue(LLFace *facep)
+{
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ mMoveFace.put(facep);
+ }
+ else
+ {
+ mDrawFace.push_back(facep);
+ }
+ facep->mDistance = (facep->mCenterAgent - gCamera->getOrigin()) * gCamera->getAtAxis();
+}
+
+void LLDrawPoolClouds::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+}
+
+void LLDrawPoolClouds::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolClouds::render(S32 pass)
+{
+ LLFastTimer ftm(LLFastTimer::FTM_RENDER_CLOUDS);
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ {
+ return;
+ }
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ glAlphaFunc(GL_GREATER,0.01f);
+
+ gPipeline.enableLightsFullbright(LLColor4(1.f,1.f,1.f));
+
+ mDrawFace[0]->bindTexture();
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+
+ std::sort(mDrawFace.begin(), mDrawFace.end(), LLFace::CompareDistanceGreater());
+
+ drawLoop();
+}
+
+
+void LLDrawPoolClouds::renderForSelect()
+{
+}
diff --git a/indra/newview/lldrawpoolclouds.h b/indra/newview/lldrawpoolclouds.h
new file mode 100644
index 0000000000..d333444400
--- /dev/null
+++ b/indra/newview/lldrawpoolclouds.h
@@ -0,0 +1,28 @@
+/**
+ * @file lldrawpoolclouds.h
+ * @brief LLDrawPoolClouds class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLCLOUDS_H
+#define LL_LLDRAWPOOLCLOUDS_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolClouds : public LLDrawPool
+{
+public:
+ LLDrawPoolClouds();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ LLDrawPool *instancePool();
+ /*virtual*/ void enqueue(LLFace *face);
+ /*virtual*/ void beginRenderPass(S32 pass);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+#endif // LL_LLDRAWPOOLSKY_H
diff --git a/indra/newview/lldrawpoolground.cpp b/indra/newview/lldrawpoolground.cpp
new file mode 100644
index 0000000000..91e92bab6e
--- /dev/null
+++ b/indra/newview/lldrawpoolground.cpp
@@ -0,0 +1,85 @@
+/**
+ * @file lldrawpoolground.cpp
+ * @brief LLDrawPoolGround class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolground.h"
+
+#include "llviewercontrol.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+LLDrawPoolGround::LLDrawPoolGround() :
+ LLDrawPool(POOL_GROUND, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+}
+
+LLDrawPool *LLDrawPoolGround::instancePool()
+{
+ return new LLDrawPoolGround();
+}
+
+void LLDrawPoolGround::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolGround::render(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ LLGLSPipelineSkyBox gls_skybox;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ glMatrixMode( GL_PROJECTION );
+
+ glPushMatrix();
+ gViewerWindow->setup3DRender();
+
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLState tex2d(GL_TEXTURE_2D, (mVertexShaderLevel > 0) ? TRUE : FALSE);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), 0);
+
+ LLFace *facep = mDrawFace[0];
+
+ if (!(mVertexShaderLevel > 0))
+ {
+ gPipeline.disableLights();
+ }
+
+ glColor4fv(facep->getFaceColor().mV);
+
+ facep->renderIndexed(getRawIndices());
+
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+}
+
+void LLDrawPoolGround::renderForSelect()
+{
+}
+
diff --git a/indra/newview/lldrawpoolground.h b/indra/newview/lldrawpoolground.h
new file mode 100644
index 0000000000..8b2dbf4353
--- /dev/null
+++ b/indra/newview/lldrawpoolground.h
@@ -0,0 +1,28 @@
+/**
+ * @file lldrawpoolground.h
+ * @brief LLDrawPoolGround class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLGROUND_H
+#define LL_LLDRAWPOOLGROUND_H
+
+#include "lldrawpool.h"
+
+
+class LLDrawPoolGround : public LLDrawPool
+{
+public:
+ LLDrawPoolGround();
+
+ /*virtual*/ LLDrawPool *instancePool();
+ virtual S32 getMaterialAttribIndex() { return 0; }
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+};
+
+#endif // LL_LLDRAWPOOLGROUND_H
diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp
new file mode 100644
index 0000000000..600e4b1fc6
--- /dev/null
+++ b/indra/newview/lldrawpoolsimple.cpp
@@ -0,0 +1,227 @@
+/**
+ * @file lldrawpoolsimple.cpp
+ * @brief LLDrawPoolSimple class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolsimple.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "pipeline.h"
+
+S32 LLDrawPoolSimple::sDiffTex = 0;
+
+LLDrawPoolSimple::LLDrawPoolSimple(LLViewerImage *texturep) :
+ LLDrawPool(POOL_SIMPLE,
+ DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK,
+ DATA_SIMPLE_NIL_MASK), // ady temp
+ mTexturep(texturep)
+{
+}
+
+LLDrawPool *LLDrawPoolSimple::instancePool()
+{
+ return new LLDrawPoolSimple(mTexturep);
+}
+
+BOOL LLDrawPoolSimple::match(LLFace* last_face, LLFace* facep)
+{
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES) &&
+ !last_face->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ !facep->isState(LLFace::LIGHT | LLFace::FULLBRIGHT) &&
+ facep->getIndicesStart() == last_face->getIndicesStart()+last_face->getIndicesCount() &&
+ facep->getRenderColor() == last_face->getRenderColor())
+ {
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ if (last_face->isState(LLFace::GLOBAL))
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (!last_face->isState(LLFace::GLOBAL))
+ {
+ if (last_face->getRenderMatrix() == facep->getRenderMatrix())
+ {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+void LLDrawPoolSimple::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolSimple::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ }
+
+ if (mVertexShaderLevel > 0)
+ {
+ S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+}
+
+
+void LLDrawPoolSimple::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE);
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ bindGLColorPointer();
+ }
+
+ LLViewerImage* tex = getTexture();
+ LLGLState alpha_test(GL_ALPHA_TEST, FALSE);
+ LLGLState blend(GL_BLEND, FALSE);
+
+ if (tex)
+ {
+ LLViewerImage::bindTexture(tex,sDiffTex);
+ if (tex->getPrimaryFormat() == GL_ALPHA)
+ {
+ // Enable Invisibility Hack
+ alpha_test.enable();
+ blend.enable();
+ }
+ }
+ else
+ {
+ LLImageGL::unbindTexture(sDiffTex, GL_TEXTURE_2D);
+ }
+
+ drawLoop();
+}
+
+void LLDrawPoolSimple::endRenderPass(S32 pass)
+{
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glDisableClientState(GL_COLOR_ARRAY);
+ }
+}
+
+void LLDrawPoolSimple::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ LLDrawable *drawable = facep->getDrawable();
+ if (drawable && !drawable->isDead() && (facep->getViewerObject()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+
+void LLDrawPoolSimple::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ facep->renderSelected(image, color, index_offset, index_count);
+}
+
+
+void LLDrawPoolSimple::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolSimple::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolSimple::getDebugTexture()
+{
+ return mTexturep;
+}
+
+LLColor3 LLDrawPoolSimple::getDebugColor() const
+{
+ return LLColor3(1.f, 1.f, 1.f);
+}
+
+S32 LLDrawPoolSimple::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectSimpleProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
+
+// virtual
+void LLDrawPoolSimple::enableShade()
+{
+ glDisableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolSimple::disableShade()
+{
+ glEnableClientState(GL_COLOR_ARRAY);
+}
+
+// virtual
+void LLDrawPoolSimple::setShade(F32 shade)
+{
+ glColor4f(0,0,0,shade);
+}
diff --git a/indra/newview/lldrawpoolsimple.h b/indra/newview/lldrawpoolsimple.h
new file mode 100644
index 0000000000..41c4580cea
--- /dev/null
+++ b/indra/newview/lldrawpoolsimple.h
@@ -0,0 +1,74 @@
+/**
+ * @file lldrawpoolsimple.h
+ * @brief LLDrawPoolSimple class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLSIMPLE_H
+#define LL_LLDRAWPOOLSIMPLE_H
+
+#include "lldrawpool.h"
+
+class LLFRInfo
+{
+public:
+ U32 mPrimType;
+ U32 mGeomIndex;
+ U32 mGeomIndexEnd;
+ U32 mNumIndices;
+ U32 mIndicesStart;
+
+ LLFRInfo()
+ {
+ }
+
+ LLFRInfo(const U32 pt, const U32 gi, const U32 gc, const U32 ni, const U32 is) :
+ mPrimType(pt),
+ mGeomIndex(gi),
+ mGeomIndexEnd(gi+gc),
+ mNumIndices(ni),
+ mIndicesStart(is)
+ {
+ }
+};
+
+class LLDrawPoolSimple : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ enum
+ {
+ SHADER_LEVEL_LOCAL_LIGHTS = 2
+ };
+
+ LLDrawPoolSimple(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void beginRenderPass(S32 pass);
+ /*virtual*/ void endRenderPass(S32 pass);
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+ /*virtual*/ BOOL match(LLFace* last_face, LLFace* facep);
+
+ /*virtual*/ void enableShade();
+ /*virtual*/ void disableShade();
+ /*virtual*/ void setShade(F32 shade);
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDiffTex;
+};
+
+#endif // LL_LLDRAWPOOLSIMPLE_H
diff --git a/indra/newview/lldrawpoolsky.cpp b/indra/newview/lldrawpoolsky.cpp
new file mode 100644
index 0000000000..96eb8ea721
--- /dev/null
+++ b/indra/newview/lldrawpoolsky.cpp
@@ -0,0 +1,180 @@
+/**
+ * @file lldrawpoolsky.cpp
+ * @brief LLDrawPoolSky class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolsky.h"
+
+#include "imageids.h"
+
+#include "llagparray.h"
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvosky.h"
+#include "llworld.h" // To get water height
+#include "pipeline.h"
+
+LLDrawPoolSky::LLDrawPoolSky() :
+ LLDrawPool(POOL_SKY, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+}
+
+LLDrawPool *LLDrawPoolSky::instancePool()
+{
+ return new LLDrawPoolSky();
+}
+
+void LLDrawPoolSky::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+}
+
+void LLDrawPoolSky::render(S32 pass)
+{
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+ LLGLSPipelineSkyBox gls_skybox;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ if (gCamera->getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight())
+ //gWorldPointer->getWaterHeight())
+ {
+ //gGLSFog.set();
+ }
+
+ gPipeline.disableLights();
+
+ glMatrixMode( GL_PROJECTION );
+
+ glPushMatrix();
+ gViewerWindow->setup3DRender();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ S32 face_count = (S32)mDrawFace.size();
+
+ for (S32 i = 0; i < llmin(6, face_count); ++i)
+ {
+ renderSkyCubeFace(i);
+ }
+
+ const LLFace *hbfaces[3];
+ hbfaces[0] = NULL;
+ hbfaces[1] = NULL;
+ hbfaces[2] = NULL;
+ for (S32 curr_face = 0; curr_face < face_count; curr_face++)
+ {
+ const LLFace* facep = mDrawFace[curr_face];
+ if (voskyp->isSameFace(LLVOSky::FACE_SUN, facep))
+ {
+ hbfaces[0] = facep;
+ }
+ if (voskyp->isSameFace(LLVOSky::FACE_MOON, facep))
+ {
+ hbfaces[1] = facep;
+ }
+ if (voskyp->isSameFace(LLVOSky::FACE_BLOOM, facep))
+ {
+ hbfaces[2] = facep;
+ }
+ }
+
+ LLGLEnable blend(GL_BLEND);
+
+ if (hbfaces[2])
+ {
+ renderSunHalo(hbfaces[2]);
+ }
+ if (hbfaces[0])
+ {
+ renderHeavenlyBody(0, hbfaces[0]);
+ }
+ if (hbfaces[1])
+ {
+ renderHeavenlyBody(1, hbfaces[1]);
+ }
+
+
+ glMatrixMode( GL_PROJECTION );
+ glPopMatrix();
+ glMatrixMode( GL_MODELVIEW );
+}
+
+void LLDrawPoolSky::renderSkyCubeFace(U8 side)
+{
+ const LLFace &face = *mDrawFace[LLVOSky::FACE_SIDE0 + side];
+ if (!face.getGeomCount())
+ {
+ return;
+ }
+
+ mSkyTex[side].bindTexture(TRUE);
+
+ face.renderIndexed(getRawIndices());
+
+ if (LLSkyTex::doInterpolate())
+ {
+ LLGLEnable blend(GL_BLEND);
+ mSkyTex[side].bindTexture(FALSE);
+ glColor4f(1, 1, 1, LLSkyTex::getInterpVal()); // lighting is disabled
+ face.renderIndexed(getRawIndices());
+ }
+
+ mIndicesDrawn += face.getIndicesCount();
+}
+
+void LLDrawPoolSky::renderHeavenlyBody(U8 hb, const LLFace* face)
+{
+ if ( !mHB[hb]->getDraw() ) return;
+ if (! face->getGeomCount()) return;
+
+ LLImageGL* tex = face->getTexture();
+ tex->bind();
+ LLColor4 color(mHB[hb]->getInterpColor());
+ LLOverrideFaceColor override(this, color);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+}
+
+
+
+void LLDrawPoolSky::renderSunHalo(const LLFace* face)
+{
+ if (! mHB[0]->getDraw()) return;
+ if (! face->getGeomCount()) return;
+
+ LLImageGL* tex = face->getTexture();
+ tex->bind();
+ LLColor4 color(mHB[0]->getInterpColor());
+ color.mV[3] = llclamp(mHB[0]->getHaloBrighness(), 0.f, 1.f);
+
+ LLOverrideFaceColor override(this, color);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+}
+
+
+void LLDrawPoolSky::renderForSelect()
+{
+}
+
diff --git a/indra/newview/lldrawpoolsky.h b/indra/newview/lldrawpoolsky.h
new file mode 100644
index 0000000000..881ce6d542
--- /dev/null
+++ b/indra/newview/lldrawpoolsky.h
@@ -0,0 +1,42 @@
+/**
+ * @file lldrawpoolsky.h
+ * @brief LLDrawPoolSky class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLSKY_H
+#define LL_LLDRAWPOOLSKY_H
+
+#include "lldrawpool.h"
+
+class LLSkyTex;
+class LLHeavenBody;
+
+class LLDrawPoolSky : public LLDrawPool
+{
+private:
+ LLSkyTex *mSkyTex;
+ LLHeavenBody *mHB[2]; // Sun and Moon
+
+public:
+ LLDrawPoolSky();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderForSelect();
+ void setSkyTex(LLSkyTex* const st) { mSkyTex = st; }
+ void setSun(LLHeavenBody* sun) { mHB[0] = sun; }
+ void setMoon(LLHeavenBody* moon) { mHB[1] = moon; }
+
+ void renderSkyCubeFace(U8 side);
+ void renderHeavenlyBody(U8 hb, const LLFace* face);
+ void renderSunHalo(const LLFace* face);
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+#endif // LL_LLDRAWPOOLSKY_H
diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp
new file mode 100644
index 0000000000..3e51e01699
--- /dev/null
+++ b/indra/newview/lldrawpoolterrain.cpp
@@ -0,0 +1,1091 @@
+/**
+ * @file lldrawpoolterrain.cpp
+ * @brief LLDrawPoolTerrain class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpoolterrain.h"
+
+#include "llfasttimer.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "llviewerparcelmgr.h" // for gRenderParcelOwnership
+#include "llviewerparceloverlay.h"
+#include "llvosurfacepatch.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h" // To get alpha gradients
+#include "llworld.h"
+#include "pipeline.h"
+
+const F32 DETAIL_SCALE = 1.f/16.f;
+int DebugDetailMap = 0;
+
+S32 LLDrawPoolTerrain::sDetailMode = 1;
+F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE;
+
+LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerImage *texturep) :
+ LLDrawPool(POOL_TERRAIN, DATA_SIMPLE_IL_MASK | DATA_COLORS_MASK | DATA_TEX_COORDS1_MASK, DATA_SIMPLE_NIL_MASK),
+ mTexturep(texturep)
+{
+ // Hack!
+ sDetailScale = 1.f/gSavedSettings.getF32("RenderTerrainScale");
+ sDetailMode = gSavedSettings.getS32("RenderTerrainDetail");
+ mAlphaRampImagep = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("alpha_gradient.tga")),
+ TRUE, TRUE, GL_ALPHA8, GL_ALPHA);
+ mAlphaRampImagep->bind(0);
+ mAlphaRampImagep->setClamp(TRUE, TRUE);
+
+ m2DAlphaRampImagep = gImageList.getImageFromUUID(LLUUID(gViewerArt.getString("alpha_gradient_2d.tga")),
+ TRUE, TRUE, GL_ALPHA8, GL_ALPHA);
+ m2DAlphaRampImagep->bind(0);
+ m2DAlphaRampImagep->setClamp(TRUE, TRUE);
+
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+LLDrawPoolTerrain::~LLDrawPoolTerrain()
+{
+ llassert( gPipeline.findPool( getType(), getTexture() ) == NULL );
+}
+
+
+LLDrawPool *LLDrawPoolTerrain::instancePool()
+{
+ return new LLDrawPoolTerrain(mTexturep);
+}
+
+
+void LLDrawPoolTerrain::prerender()
+{
+#if 0 // 1.9.2
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+#endif
+}
+
+void LLDrawPoolTerrain::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN);
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // No mulititexture, render simple land.
+ renderSimple(); // Render without multitexture
+ return;
+ }
+ // Render simplified land if video card can't do sufficient multitexturing
+ if (!gGLManager.mHasARBEnvCombine || (gGLManager.mNumTextureUnits < 2))
+ {
+ renderSimple(); // Render without multitexture
+ return;
+ }
+
+ LLGLSPipeline gls;
+ LLOverrideFaceColor override(this, 1.f, 1.f, 1.f, 1.f);
+
+ if (mVertexShaderLevel > 0)
+ {
+ gPipeline.enableLightsDynamic(1.f);
+ renderFull4TUShader();
+ }
+ else
+ {
+ gPipeline.enableLightsStatic(1.f);
+ switch (sDetailMode)
+ {
+ case 0:
+ renderSimple();
+ break;
+ default:
+ if (gGLManager.mNumTextureUnits < 4)
+ {
+ renderFull2TU();
+ }
+ else
+ {
+ renderFull4TU();
+ }
+ break;
+ }
+ }
+
+ // Special-case for land ownership feedback
+ if (gSavedSettings.getBOOL("ShowParcelOwners"))
+ {
+ gPipeline.disableLights();
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mHighlightProgram.bind();
+ gPipeline.mHighlightProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,1,1,1,1);
+ renderOwnership();
+ gPipeline.mTerrainProgram.bind();
+ }
+ else
+ {
+ renderOwnership();
+ }
+ }
+}
+
+
+void LLDrawPoolTerrain::renderFull4TUShader()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ glEnableClientState(GL_COLOR_ARRAY);
+ bindGLColorPointer();
+ }
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ static F32 dp = 0.f;
+ static LLFrameTimer timer;
+ dp += timer.getElapsedTimeAndResetF32();
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ //
+ // Stage 0: detail texture 0
+ //
+
+ S32 detailTex0 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
+ S32 detailTex1 = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
+ S32 rampTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
+ S32 scatterTex = gPipeline.mTerrainProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+
+ LLViewerImage::bindTexture(detail_texture0p,detailTex0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 1: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,rampTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ //
+ // Stage 2: Interpolate detail1 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture1p,detailTex1);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ // Stage 4: Haze
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ //
+ // Stage 3: Modulate with primary color for lighting
+ //
+ //LLViewerImage::bindTexture(detail_texture1p,3); // bind any texture
+ //glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ // GL_BLEND disabled by default
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Second pass
+
+ //
+ // Stage 0: Write detail3 into base
+ //
+ LLViewerImage::bindTexture(detail_texture2p,detailTex0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 1: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,rampTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ //
+ // Stage 2: Interpolate detail2 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture3p,detailTex1);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ //
+ // Stage 3: Generate alpha ramp for detail1/detail2 transition
+ //
+ //LLViewerImage::bindTexture(m2DAlphaRampImagep,3);
+
+ //glEnable(GL_TEXTURE_2D); // Texture unit 3
+
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Disable multitexture
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_ALPHARAMP);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL0);
+ gPipeline.mTerrainProgram.disableTexture(LLPipeline::GLSL_TERRAIN_DETAIL1);
+
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glActiveTextureARB(GL_TEXTURE3_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glEnable(GL_TEXTURE_2D);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void LLDrawPoolTerrain::renderFull4TU()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ //
+ // Stage 0: detail texture 0
+ //
+ LLViewerImage::bindTexture(detail_texture0p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ //
+ // Stage 1: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 2: Interpolate detail1 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture1p,2);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 3: Modulate with primary (vertex) color for lighting
+ //
+ LLViewerImage::bindTexture(detail_texture1p,3); // bind any texture
+ glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+
+ // Set alpha texture and do lighting modulation
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ // GL_BLEND disabled by default
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Second pass
+
+ // Stage 0: Write detail3 into base
+ //
+ LLViewerImage::bindTexture(detail_texture3p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+
+ //
+ // Stage 1: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,1);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ //
+ // Stage 2: Interpolate detail2 with existing based on ramp
+ //
+ LLViewerImage::bindTexture(detail_texture2p,2);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+
+
+ //
+ // Stage 3: Generate alpha ramp for detail1/detail2 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,3);
+
+ glEnable(GL_TEXTURE_2D); // Texture unit 3
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ // Set alpha texture and do lighting modulation
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Disable multitexture
+ LLImageGL::unbindTexture(3, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE3_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 3
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLImageGL::unbindTexture(2, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE2_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 2
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+void LLDrawPoolTerrain::renderFull2TU()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ // Hack! Get the region that this draw pool is rendering from!
+ LLViewerRegion *regionp = mDrawFace[0]->getDrawable()->getVObj()->getRegion();
+ LLVLComposition *compp = regionp->getComposition();
+ for (S32 i = 0; i < 4; i++)
+ {
+ compp->mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN);
+ compp->mDetailTextures[i]->addTextureStats(1024.f*1024.f); // assume large pixel area
+ }
+
+ LLViewerImage *detail_texture0p = compp->mDetailTextures[0];
+ LLViewerImage *detail_texture1p = compp->mDetailTextures[1];
+ LLViewerImage *detail_texture2p = compp->mDetailTextures[2];
+ LLViewerImage *detail_texture3p = compp->mDetailTextures[3];
+
+ LLVector3d region_origin_global = gAgent.getRegion()->getOriginGlobal();
+ F32 offset_x = (F32)fmod(region_origin_global.mdV[VX], 1.0/(F64)sDetailScale)*sDetailScale;
+ F32 offset_y = (F32)fmod(region_origin_global.mdV[VY], 1.0/(F64)sDetailScale)*sDetailScale;
+
+ LLVector4 tp0, tp1;
+
+ tp0.setVec(sDetailScale, 0.0f, 0.0f, offset_x);
+ tp1.setVec(0.0f, sDetailScale, 0.0f, offset_y);
+
+ glBlendFunc(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
+
+ //----------------------------------------------------------------------------
+ // Pass 1/4
+
+ //
+ // Stage 0: Render detail 0 into base
+ //
+ LLViewerImage::bindTexture(detail_texture0p,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Pass 2/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail0/detail1 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+
+ //
+ // Stage 1: Write detail1
+ //
+ LLViewerImage::bindTexture(detail_texture1p,1); // Texture unit 1
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+ //----------------------------------------------------------------------------
+ // Pass 3/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail1/detail2 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-1.f, 0.f, 0.f);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ //
+ // Stage 1: Write detail2
+ //
+
+ LLViewerImage::bindTexture(detail_texture2p,1);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ //----------------------------------------------------------------------------
+ // Pass 4/4
+
+ //
+ // Stage 0: Generate alpha ramp for detail2/detail3 transition
+ //
+ LLViewerImage::bindTexture(m2DAlphaRampImagep,0);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ // Set the texture matrix
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glTranslatef(-2.f, 0.f, 0.f);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ bindGLTexCoordPointer(1);
+
+ // Care about alpha only
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ // Stage 1: Write detail3
+
+ LLViewerImage::bindTexture(detail_texture3p,1);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ {
+ LLGLEnable blend(GL_BLEND);
+ drawLoop();
+ }
+
+ // Restore blend state
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Disable multitexture
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+
+void LLDrawPoolTerrain::renderSimple()
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+
+ LLVector4 tp0, tp1;
+
+ //----------------------------------------------------------------------------
+ // Pass 1/1
+
+ // Stage 0: Base terrain texture pass
+ mTexturep->addTextureStats(1024.f*1024.f);
+ mTexturep->bind(0);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 2
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+
+ LLVector3 origin_agent = mDrawFace[0]->getDrawable()->getVObj()->getRegion()->getOriginAgent();
+ F32 tscale = 1.f/256.f;
+ tp0.setVec(tscale, 0.f, 0.0f, -1.f*(origin_agent.mV[0]/256.f));
+ tp1.setVec(0.f, tscale, 0.0f, -1.f*(origin_agent.mV[1]/256.f));
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_GEN_S);
+ glEnable(GL_TEXTURE_GEN_T);
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0.mV);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1.mV);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ drawLoop();
+
+ //----------------------------------------------------------------------------
+ // Restore Texture Unit 0 defaults
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisable(GL_TEXTURE_GEN_S);
+ glDisable(GL_TEXTURE_GEN_T);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+}
+
+//============================================================================
+
+void LLDrawPoolTerrain::renderOwnership()
+{
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ llassert(!mDrawFace.empty());
+
+ // Each terrain pool is associated with a single region.
+ // We need to peek back into the viewer's data to find out
+ // which ownership overlay texture to use.
+ LLFace *facep = mDrawFace[0];
+ LLDrawable *drawablep = facep->getDrawable();
+ const LLViewerObject *objectp = drawablep->getVObj();
+ const LLVOSurfacePatch *vo_surface_patchp = (LLVOSurfacePatch *)objectp;
+ LLSurfacePatch *surface_patchp = vo_surface_patchp->getPatch();
+ LLSurface *surfacep = surface_patchp->getSurface();
+ LLViewerRegion *regionp = surfacep->getRegion();
+ LLViewerParcelOverlay *overlayp = regionp->getParcelOverlay();
+ LLImageGL *texturep = overlayp->getTexture();
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer(0);
+
+ LLViewerImage::bindTexture(texturep);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ // HACK: Because the region is 256 meters wide, but has 257 pixels, the
+ // texture coordinates for pixel 256x256 is not 1,1. This makes the
+ // ownership map not line up with the selection. Fix this with a texture
+ // matrix multiply.
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+
+ const F32 TEXTURE_FUDGE = 257.f / 256.f;
+ glScalef( TEXTURE_FUDGE, TEXTURE_FUDGE, 1.f );
+
+ const U32* index_array = getRawIndices();
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ facep->renderIndexed(index_array);
+ }
+
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ // Restore non Texture Unit specific defaults
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+
+void LLDrawPoolTerrain::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+
+ bindGLVertexPointer();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (!facep->getDrawable()->isDead() && (facep->getDrawable()->getVObj()->mGLName))
+ {
+ facep->renderForSelect();
+ }
+ }
+}
+
+void LLDrawPoolTerrain::dirtyTexture(const LLViewerImage *texturep)
+{
+ if (mTexturep == texturep)
+ {
+ for (std::vector<LLFace*>::iterator iter = mReferences.begin();
+ iter != mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ gPipeline.markTextured(facep->getDrawable());
+ }
+ }
+}
+
+LLViewerImage *LLDrawPoolTerrain::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolTerrain::getDebugTexture()
+{
+ return mTexturep;
+}
+
+
+LLColor3 LLDrawPoolTerrain::getDebugColor() const
+{
+ return LLColor3(0.f, 0.f, 1.f);
+}
+
+S32 LLDrawPoolTerrain::getMaterialAttribIndex()
+{
+ return gPipeline.mTerrainProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
diff --git a/indra/newview/lldrawpoolterrain.h b/indra/newview/lldrawpoolterrain.h
new file mode 100644
index 0000000000..b5fe0a30fd
--- /dev/null
+++ b/indra/newview/lldrawpoolterrain.h
@@ -0,0 +1,49 @@
+/**
+ * @file lldrawpoolterrain.h
+ * @brief LLDrawPoolTerrain class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLTERRAIN_H
+#define LL_LLDRAWPOOLTERRAIN_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolTerrain : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ LLDrawPoolTerrain(LLViewerImage *texturep);
+ virtual ~LLDrawPoolTerrain();
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void dirtyTexture(const LLViewerImage *texturep);
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ LLPointer<LLViewerImage> mAlphaRampImagep;
+ LLPointer<LLViewerImage> m2DAlphaRampImagep;
+ LLPointer<LLViewerImage> mAlphaNoiseImagep;
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDetailMode;
+ static F32 sDetailScale; // meters per texture
+protected:
+ void renderSimple();
+ void renderOwnership();
+
+ void renderFull2TU();
+ void renderFull4TU();
+ void renderFull4TUShader();
+};
+
+#endif // LL_LLDRAWPOOLSIMPLE_H
diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp
new file mode 100644
index 0000000000..c41ceedac7
--- /dev/null
+++ b/indra/newview/lldrawpooltree.cpp
@@ -0,0 +1,342 @@
+/**
+ * @file lldrawpooltree.cpp
+ * @brief LLDrawPoolTree class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawpooltree.h"
+
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewerwindow.h"
+#include "llvotree.h"
+#include "pipeline.h"
+#include "llviewercamera.h"
+
+S32 LLDrawPoolTree::sDiffTex = 0;
+
+LLDrawPoolTree::LLDrawPoolTree(LLViewerImage *texturep) :
+ LLDrawPool(POOL_TREE, DATA_SIMPLE_IL_MASK, 0),
+ mTexturep(texturep)
+{
+ mTexturep->bind(0);
+ mTexturep->setClamp(FALSE, FALSE);
+}
+
+LLDrawPool *LLDrawPoolTree::instancePool()
+{
+ return new LLDrawPoolTree(mTexturep);
+}
+
+void LLDrawPoolTree::prerender()
+{
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT);
+}
+
+void LLDrawPoolTree::beginRenderPass(S32 pass)
+{
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ if ((mVertexShaderLevel > 0))
+ {
+ S32 scatterTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+ sDiffTex = gPipeline.mObjectSimpleProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ }
+}
+
+void LLDrawPoolTree::render(S32 pass)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TREES);
+
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ gPipeline.enableLightsDynamic(1.f);
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+ bindGLNormalPointer();
+
+ LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f);
+
+ renderTree();
+
+}
+
+void LLDrawPoolTree::endRenderPass(S32 pass)
+{
+ if ((mVertexShaderLevel > 0))
+ {
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mObjectSimpleProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ }
+
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolTree::renderForSelect()
+{
+ if (mDrawFace.empty() || !mMemory.count())
+ {
+ return;
+ }
+
+ glEnableClientState (GL_VERTEX_ARRAY);
+ glEnableClientState (GL_TEXTURE_COORD_ARRAY);
+
+ LLOverrideFaceColor color(this, 1.f, 1.f, 1.f, 1.f);
+
+ LLGLSObjectSelectAlpha gls_alpha;
+
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glAlphaFunc(gPickTransparent ? GL_GEQUAL : GL_GREATER, 0.f);
+
+ bindGLVertexPointer();
+ bindGLTexCoordPointer();
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_ARB, GL_SRC_ALPHA);
+
+ renderTree(TRUE);
+
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glDisableClientState (GL_TEXTURE_COORD_ARRAY);
+}
+
+void LLDrawPoolTree::renderTree(BOOL selecting)
+{
+ LLGLState normalize(GL_NORMALIZE, TRUE);
+
+ // Bind the texture for this tree.
+ LLViewerImage::bindTexture(mTexturep,sDiffTex);
+ if (mTexturep)
+ {
+ if (mTexturep->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ }
+ if (mTexturep->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ LLDrawable *drawablep = face->getDrawable();
+
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+
+ // Render each of the trees
+ LLVOTree *treep = (LLVOTree *)drawablep->getVObj();
+
+ LLColor4U color(255,255,255,255);
+
+ if (!selecting || treep->mGLName != 0)
+ {
+ if (selecting)
+ {
+ S32 name = treep->mGLName;
+
+ color = LLColor4U((U8)(name >> 16), (U8)(name >> 8), (U8)name, 255);
+ }
+
+ glPushMatrix();
+
+ // Translate to tree base HACK - adjustment in Z plants tree underground
+ const LLVector3 &pos_agent = treep->getPositionAgent();
+ glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ] - 0.1f);
+
+ // Rotate to tree position
+ F32 angle_radians, x, y, z;
+ treep->getRotation().getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ // Rotate and bend for current trunk/wind
+ // Note that trunk stiffness controls the amount of bend at the trunk as
+ // opposed to the crown of the tree
+ //
+ glRotatef(90.f, 0, 0, 1);
+ const F32 TRUNK_STIFF = 22.f;
+ glRotatef(treep->mTrunkBend.magVec()*TRUNK_STIFF, treep->mTrunkBend.mV[VX], treep->mTrunkBend.mV[VY], 0);
+
+ F32 radius = treep->getScale().magVec()*0.5f;
+ radius *= 0.1f;
+ glScalef(radius, radius, radius);
+
+ const F32 THRESH_ANGLE_FOR_BILLBOARD = 15.f;
+ const F32 BLEND_RANGE_FOR_BILLBOARD = 3.f;
+
+ F32 droop = treep->mDroop + 25.f*(1.f - treep->mTrunkBend.magVec());
+
+ S32 stop_depth = 0;
+ F32 app_angle = treep->getAppAngle()*LLVOTree::sTreeFactor;
+ F32 alpha = 1.0;
+ S32 trunk_LOD = 0;
+
+ for (S32 j = 0; j < 4; j++)
+ {
+
+ if (app_angle > LLVOTree::sLODAngles[j])
+ {
+ trunk_LOD = j;
+ break;
+ }
+ }
+
+ if (app_angle > (THRESH_ANGLE_FOR_BILLBOARD + BLEND_RANGE_FOR_BILLBOARD))
+ {
+ //
+ // Draw only the full geometry tree
+ //
+ //stop_depth = (app_angle < THRESH_ANGLE_FOR_RECURSION_REDUCTION);
+ glAlphaFunc(GL_GREATER, 0.5f);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ else if (app_angle < (THRESH_ANGLE_FOR_BILLBOARD - BLEND_RANGE_FOR_BILLBOARD))
+ {
+ //
+ // Draw only the billboard
+ //
+ // Only the billboard, can use closer to normal alpha func.
+ stop_depth = -1;
+ glAlphaFunc(GL_GREATER, 0.4f);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, stop_depth, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ else
+ {
+ //
+ // Draw a blended version including both billboard and full tree
+ //
+ alpha = (app_angle - THRESH_ANGLE_FOR_BILLBOARD)/BLEND_RANGE_FOR_BILLBOARD;
+ BOOL billboard_depth = TRUE; // billboard gets alpha
+ if (alpha > 0.5f)
+ {
+ billboard_depth = FALSE;
+ }
+ alpha = alpha/2.f + 0.5f;
+
+ glAlphaFunc(GL_GREATER, alpha*0.5f);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_FALSE : GL_TRUE);
+ color.mV[3] = (U8) (llclamp(alpha, 0.0f, 1.0f) * 255);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, 0, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, alpha);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, billboard_depth ? GL_TRUE : GL_FALSE);
+ glAlphaFunc(GL_GREATER, (1.f - alpha)*0.1f);
+ color.mV[3] = (U8) (llclamp(1.f-alpha, 0.0f, 1.0f) * 255);
+ LLDrawPool::LLOverrideFaceColor clr(this, color);
+ treep->drawBranchPipeline(this, trunk_LOD, -1, treep->mDepth, treep->mTrunkDepth, 1.0, treep->mTwist, droop, treep->mBranches, 1.f - alpha);
+ }
+ }
+ glPopMatrix();
+ }
+ }
+
+ if (mTexturep)
+ {
+ if (mTexturep->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ }
+ if (mTexturep->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+ glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+
+S32 LLDrawPoolTree::rebuild()
+{
+ mRebuildTime++;
+ if (mRebuildTime > mRebuildFreq)
+ {
+ // Flush AGP to force an AGP realloc and reduce AGP fragmentation
+ flushAGP();
+ mRebuildTime = 0;
+ }
+
+ return 0;
+}
+
+BOOL LLDrawPoolTree::verify() const
+{
+ BOOL ok = TRUE;
+
+ // shared geometry. Just verify that it's there and correct.
+
+ // Verify all indices in the pool are in the right range
+ const U32 *indicesp = getRawIndices();
+ for (U32 i = 0; i < getIndexCount(); i++)
+ {
+ if (indicesp[i] > getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Bad index in tree pool!" << llendl;
+ }
+ }
+
+ if (!ok)
+ {
+ printDebugInfo();
+ }
+ return ok;
+}
+
+LLViewerImage *LLDrawPoolTree::getTexture()
+{
+ return mTexturep;
+}
+
+LLViewerImage *LLDrawPoolTree::getDebugTexture()
+{
+ return mTexturep;
+}
+
+
+LLColor3 LLDrawPoolTree::getDebugColor() const
+{
+ return LLColor3(1.f, 0.f, 1.f);
+}
+
+S32 LLDrawPoolTree::getMaterialAttribIndex()
+{
+ return gPipeline.mObjectSimpleProgram.mAttribute[LLPipeline::GLSL_MATERIAL_COLOR];
+}
diff --git a/indra/newview/lldrawpooltree.h b/indra/newview/lldrawpooltree.h
new file mode 100644
index 0000000000..228b11a981
--- /dev/null
+++ b/indra/newview/lldrawpooltree.h
@@ -0,0 +1,41 @@
+/**
+ * @file lldrawpooltree.h
+ * @brief LLDrawPoolTree class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLTREE_H
+#define LL_LLDRAWPOOLTREE_H
+
+#include "lldrawpool.h"
+
+class LLDrawPoolTree : public LLDrawPool
+{
+ LLPointer<LLViewerImage> mTexturep;
+public:
+ LLDrawPoolTree(LLViewerImage *texturep);
+
+ /*virtual*/ LLDrawPool *instancePool();
+
+ /*virtual*/ void prerender();
+ /*virtual*/ void beginRenderPass( S32 pass );
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void endRenderPass( S32 pass );
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ S32 rebuild();
+ /*virtual*/ BOOL verify() const;
+ /*virtual*/ LLViewerImage *getTexture();
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ virtual S32 getMaterialAttribIndex();
+
+ static S32 sDiffTex;
+
+private:
+ void renderTree(BOOL selecting = FALSE);
+};
+
+#endif // LL_LLDRAWPOOLTREE_H
diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp
new file mode 100644
index 0000000000..108048efbc
--- /dev/null
+++ b/indra/newview/lldrawpoolwater.cpp
@@ -0,0 +1,729 @@
+/**
+ * @file lldrawpoolwater.cpp
+ * @brief LLDrawPoolWater class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfeaturemanager.h"
+#include "lldrawpoolwater.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llerror.h"
+#include "m3math.h"
+
+#include "llagent.h" // for gAgent for getRegion for getWaterHeight
+#include "llagparray.h"
+#include "llcubemap.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h" // to get OGL_TO_CFR_ROTATION
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llvosky.h"
+#include "llvowater.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h" // gSunTextureID, gMoonTextureID
+
+const LLUUID WATER_TEST("2bfd3884-7e27-69b9-ba3a-3e673f680004");
+
+static float sTime;
+
+int nhpo2(int v)
+{
+ int r = 1;
+ while (r < v) {
+ r *= 2;
+ }
+ return r;
+}
+
+static GLuint sScreenTex = 0;
+
+LLDrawPoolWater::LLDrawPoolWater() :
+ LLDrawPool(POOL_WATER, DATA_SIMPLE_IL_MASK, DATA_SIMPLE_NIL_MASK)
+{
+ mCleanupUnused = TRUE;
+ mHBTex[0] = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mHBTex[0]->bind();
+ mHBTex[0]->setClamp(TRUE, TRUE);
+
+ mHBTex[1] = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mHBTex[1]->bind();
+ mHBTex[1]->setClamp(TRUE, TRUE);
+
+ mWaterImagep = gImageList.getImage(WATER_TEST);
+ mWaterNormp = gImageList.getImage(LLUUID(gViewerArt.getString("water_normal.tga")));
+
+ restoreGL();
+}
+
+LLDrawPoolWater::~LLDrawPoolWater()
+{
+}
+
+//static
+void LLDrawPoolWater::restoreGL()
+{
+ if (gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) >= SHADER_LEVEL_RIPPLE)
+ {
+ //build screen texture
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glGenTextures(1, &sScreenTex);
+ LLGLEnable gl_texture_2d(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, sScreenTex);
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ GLuint resX = nhpo2(viewport[2]);
+ GLuint resY = nhpo2(viewport[3]);
+
+ gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+}
+
+
+LLDrawPool *LLDrawPoolWater::instancePool()
+{
+ llerrs << "Should never be calling instancePool on a water pool!" << llendl;
+ return NULL;
+}
+
+
+void LLDrawPoolWater::prerender()
+{
+#if 1 // 1.9.1
+ mVertexShaderLevel = gSavedSettings.getBOOL("RenderRippleWater") ?
+ gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) : 0;
+#else
+ mVertexShaderLevel = gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT);
+#endif
+}
+
+extern LLColor4U MAX_WATER_COLOR;
+
+void LLDrawPoolWater::render(S32 pass)
+{
+ LLFastTimer ftm(LLFastTimer::FTM_RENDER_WATER);
+ if (mDrawFace.empty())
+ {
+ return;
+ }
+
+ LLGLSPipelineAlpha alphaState;
+
+ if ((mVertexShaderLevel >= SHADER_LEVEL_RIPPLE))
+ {
+ shade();
+ return;
+ }
+
+ if ((mVertexShaderLevel > 0))
+ {
+ renderShaderSimple();
+ return;
+ }
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ stop_glerror();
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // Ack! No multitexture! Bail!
+ return;
+ }
+
+ const LLFace* refl_face = voskyp->getReflFace();
+
+ gPipeline.disableLights();
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLDisable cullFace(GL_CULL_FACE);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ // Set up second pass first
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ mWaterImagep->addTextureStats(1024.f*1024.f);
+ mWaterImagep->bind(1);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+
+ LLColor4 water_color;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ }
+
+ glColor4fv(water_color.mV);
+
+ // Automatically generate texture coords for detail map
+ glEnable(GL_TEXTURE_GEN_S); //texture unit 1
+ glEnable(GL_TEXTURE_GEN_T); //texture unit 1
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ // Slowly move over time.
+ F32 offset = fmod(gFrameTimeSeconds*2.f, 100.f);
+ F32 tp0[4] = {16.f/256.f, 0.0f, 0.0f, offset*0.01f};
+ F32 tp1[4] = {0.0f, 16.f/256.f, 0.0f, offset*0.01f};
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glClearStencil(1);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable gls_stencil(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+ face->bindTexture();
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+
+ // Now, disable texture coord generation on texture state 1
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_2D); // Texture unit 1
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glDisable(GL_TEXTURE_GEN_S); //texture unit 1
+ glDisable(GL_TEXTURE_GEN_T); //texture unit 1
+
+ // Disable texture coordinate and color arrays
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ stop_glerror();
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gSky.mVOSkyp->getCubeMap()->enable(0);
+ gSky.mVOSkyp->getCubeMap()->bind();
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+
+ glLoadMatrixf((F32 *)camera_rot.mMatrix);
+
+ glMatrixMode(GL_MODELVIEW);
+ LLOverrideFaceColor overrid(this, 1.f, 1.f, 1.f, 0.5f*up_dot);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ /*glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);*/
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ //refl_face = face;
+ continue;
+ }
+
+ if (face->getGeomCount() > 0)
+ {
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+ }
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gSky.mVOSkyp->getCubeMap()->disable();
+ }
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glEnable(GL_TEXTURE_2D);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ }
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ if (refl_face)
+ {
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
+ renderReflection(refl_face);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+}
+
+
+void LLDrawPoolWater::renderShaderSimple()
+{
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ stop_glerror();
+
+ if (!gGLManager.mHasMultitexture)
+ {
+ // Ack! No multitexture! Bail!
+ return;
+ }
+
+ const LLFace* refl_face = voskyp->getReflFace();
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLGLDisable cullFace(GL_CULL_FACE);
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ // Set up second pass first
+ S32 bumpTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ mWaterImagep->addTextureStats(1024.f*1024.f);
+ mWaterImagep->bind(bumpTex);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+
+ LLColor4 water_color;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ }
+
+ glColor4fv(water_color.mV);
+
+ // Automatically generate texture coords for detail map
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_GEN_S); //texture unit 1
+ glEnable(GL_TEXTURE_GEN_T); //texture unit 1
+ glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+ glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
+
+ // Slowly move over time.
+ F32 offset = fmod(gFrameTimeSeconds*2.f, 100.f);
+ F32 tp0[4] = {16.f/256.f, 0.0f, 0.0f, offset*0.01f};
+ F32 tp1[4] = {0.0f, 16.f/256.f, 0.0f, offset*0.01f};
+ glTexGenfv(GL_S, GL_OBJECT_PLANE, tp0);
+ glTexGenfv(GL_T, GL_OBJECT_PLANE, tp1);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glClearStencil(1);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable gls_stencil(GL_STENCIL_TEST);
+ glStencilOp(GL_KEEP, GL_REPLACE, GL_KEEP);
+ glStencilFunc(GL_ALWAYS, 0, 0xFFFFFFFF);
+
+ S32 envTex = -1;
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ envTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ gSky.mVOSkyp->getCubeMap()->bind();
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+
+ glLoadMatrixf((F32 *)camera_rot.mMatrix);
+
+ glMatrixMode(GL_MODELVIEW);
+ }
+
+ S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ gPipeline.mWaterProgram.bind();
+
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+ face->bindTexture(diffTex);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+
+ if (gSky.mVOSkyp->getCubeMap())
+ {
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ }
+
+ // Now, disable texture coord generation on texture state 1
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ LLImageGL::unbindTexture(bumpTex, GL_TEXTURE_2D);
+
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glDisable(GL_TEXTURE_GEN_S); //texture unit 1
+ glDisable(GL_TEXTURE_GEN_T); //texture unit 1
+
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+
+ // Disable texture coordinate and color arrays
+ LLImageGL::unbindTexture(diffTex, GL_TEXTURE_2D);
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ stop_glerror();
+
+ glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ glUseProgramObjectARB(0);
+ gPipeline.disableLights();
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+
+ if (refl_face)
+ {
+ glStencilFunc(GL_NOTEQUAL, 0, 0xFFFFFFFF);
+ renderReflection(refl_face);
+ }
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+}
+
+void LLDrawPoolWater::renderReflection(const LLFace* face)
+{
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ if (!voskyp)
+ {
+ return;
+ }
+
+ if (!face->getGeomCount())
+ {
+ return;
+ }
+
+ S8 dr = voskyp->getDrawRefl();
+ if (dr < 0)
+ {
+ return;
+ }
+
+ LLGLSNoFog noFog;
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ LLViewerImage::bindTexture(mHBTex[dr]);
+
+ LLOverrideFaceColor override(this, face->getFaceColor().mV);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+}
+
+void bindScreenToTexture()
+{
+ GLint viewport[4];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ GLuint resX = nhpo2(viewport[2]);
+ GLuint resY = nhpo2(viewport[3]);
+
+ glBindTexture(GL_TEXTURE_2D, sScreenTex);
+ GLint cResX;
+ GLint cResY;
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &cResX);
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &cResY);
+
+ if (cResX != (GLint)resX || cResY != (GLint)resY)
+ {
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, resX, resY, 0, GL_RGB, GL_FLOAT, NULL);
+ gImageList.updateMaxResidentTexMem(-1, resX*resY*3);
+ }
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, viewport[0], viewport[1], 0, 0, viewport[2], viewport[3]);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ float scale[2];
+ scale[0] = (float) viewport[2]/resX;
+ scale[1] = (float) viewport[3]/resY;
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_FBSCALE], 1, scale);
+
+ LLImageGL::sBoundTextureMemory += resX * resY * 3;
+}
+
+void LLDrawPoolWater::shade()
+{
+ static LLVector2 d1( 0.5f, -0.17f );
+ static LLVector2 d2( 0.58f, -0.67f );
+ static LLVector2 d3( 0.5f, 0.25f );
+
+ static LLVector3 wave1(1,0.42f,1);
+ static LLVector3 wave2(0.58f,0.42f,0.17f);
+ static LLVector3 wave3(0.42f,0.67f,0.33f);
+
+ /*static LLVector2 d1( 0.83f, -1 );
+ static LLVector2 d2( 0.58f, 1 );
+ static LLVector2 d3( 1, -0.88f );
+
+ static LLVector4 wave1(0.75f,0.08f,0.5f,0.67f);
+ static LLVector4 wave2(0.17f,0.33f,0.53f,0.62f);
+ static LLVector4 wave3(0.17f,0.6f,0.67f,1);*/
+
+ /*LLDebugVarMessageBox::show("Wave Direction 1", &d1, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave Direction 2", &d2, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave Direction 3", &d3, LLVector2(1,1), LLVector2(0.01f, 0.01f));
+
+ LLDebugVarMessageBox::show("Wave 1", &wave1, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave 2", &wave2, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));
+ LLDebugVarMessageBox::show("Wave 3", &wave3, LLVector3(2,1,4), LLVector3(0.01f, 0.01f, 0.01f));*/
+
+ LLVOSky *voskyp = gSky.mVOSkyp;
+
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ LLGLDisable blend(GL_BLEND);
+ bindGLVertexPointer();
+ bindGLNormalPointer();
+ bindGLTexCoordPointer();
+
+ LLColor3 light_diffuse(0,0,0);
+ F32 light_exp = 0.0f;
+ LLVector3 light_dir;
+
+ if (gSky.getSunDirection().mV[2] > NIGHTTIME_ELEVATION_COS)
+ {
+ light_dir = gSky.getSunDirection();
+ light_dir.normVec();
+ light_diffuse = gSky.mVOSkyp->getSun().getColorCached();
+ light_diffuse.normVec();
+ light_exp = light_dir * LLVector3(light_dir.mV[0], light_dir.mV[1], 0);
+ light_diffuse *= light_exp + 0.25f;
+ }
+ else
+ {
+ light_dir = gSky.getMoonDirection();
+ light_dir.normVec();
+ light_diffuse = gSky.mVOSkyp->getMoon().getColorCached();
+ light_diffuse.normVec();
+ light_diffuse *= 0.5f;
+ light_exp = light_dir * LLVector3(light_dir.mV[0], light_dir.mV[1], 0);
+ }
+
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= light_exp;
+ light_exp *= 512.f;
+ light_exp = light_exp > 32.f ? light_exp : 32.f;
+
+ sTime = (F32)LLFrameTimer::getElapsedSeconds()*0.5f;
+
+ LLCubeMap* skyMap = gSky.mVOSkyp->getCubeMap();
+
+ gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ skyMap->bind();
+
+ //bind normal map
+ S32 bumpTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_BUMP_MAP);
+ mWaterNormp->addTextureStats(1024.f*1024.f);
+ mWaterNormp->bind(bumpTex);
+
+ gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_WATER_SCREENTEX);
+
+ gPipeline.mWaterProgram.bind();
+
+ bindScreenToTexture();
+
+ S32 scatterTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ LLViewerImage::bindTexture(gSky.mVOSkyp->getScatterMap(), scatterTex);
+
+ S32 diffTex = gPipeline.mWaterProgram.enableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_TIME], sTime);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_SPECULAR], 1, light_diffuse.mV);
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_SPECULAR_EXP], light_exp);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_EYEVEC], 1, gCamera->getOrigin().mV);
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_WAVE_DIR1], 1, d1.mV);
+ glUniform2fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_WAVE_DIR2], 1, d2.mV);
+ glUniform3fvARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_LIGHT_DIR], 1, light_dir.mV);
+
+ LLColor4 water_color;
+ LLVector3 camera_up = gCamera->getUpAxis();
+ F32 up_dot = camera_up * LLVector3::z_axis;
+ if (gCamera->cameraUnderWater())
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.4f);
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_REFSCALE], 0.25f);
+ }
+ else
+ {
+ water_color.setVec(1.f, 1.f, 1.f, 0.5f*(1.f + up_dot));
+ glUniform1fARB(gPipeline.mWaterProgram.mUniform[LLPipeline::GLSL_WATER_REFSCALE], 0.01f);
+ }
+ if (water_color.mV[3] > 0.9f)
+ {
+ water_color.mV[3] = 0.9f;
+ }
+
+ glColor4fv(water_color.mV);
+
+ {
+ LLGLDisable cullface(GL_CULL_FACE);
+ for (std::vector<LLFace*>::iterator iter = mDrawFace.begin();
+ iter != mDrawFace.end(); iter++)
+ {
+ LLFace *face = *iter;
+
+ if (voskyp->isReflFace(face))
+ {
+ continue;
+ }
+
+ face->bindTexture(diffTex);
+ face->renderIndexed(getRawIndices());
+ mIndicesDrawn += face->getIndicesCount();
+ }
+ }
+
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_ENVIRONMENT_MAP, GL_TEXTURE_CUBE_MAP_ARB);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_WATER_SCREENTEX);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_BUMP_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_SCATTER_MAP);
+ gPipeline.mWaterProgram.disableTexture(LLPipeline::GLSL_DIFFUSE_MAP);
+
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glEnable(GL_TEXTURE_2D);
+ glUseProgramObjectARB(0);
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+
+ /*glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);*/
+}
+
+void LLDrawPoolWater::renderForSelect()
+{
+ // Can't select water!
+ return;
+}
+
+
+void LLDrawPoolWater::renderFaceSelected(LLFace *facep,
+ LLImageGL *image,
+ const LLColor4 &color,
+ const S32 index_offset, const S32 index_count)
+{
+ // Can't select water
+ return;
+}
+
+
+LLViewerImage *LLDrawPoolWater::getDebugTexture()
+{
+ return LLViewerImage::sSmokeImagep;
+}
+
+LLColor3 LLDrawPoolWater::getDebugColor() const
+{
+ return LLColor3(0.f, 1.f, 1.f);
+}
diff --git a/indra/newview/lldrawpoolwater.h b/indra/newview/lldrawpoolwater.h
new file mode 100644
index 0000000000..7e661b3718
--- /dev/null
+++ b/indra/newview/lldrawpoolwater.h
@@ -0,0 +1,57 @@
+/**
+ * @file lldrawpoolwater.h
+ * @brief LLDrawPoolWater class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRAWPOOLWATER_H
+#define LL_LLDRAWPOOLWATER_H
+
+#include "lldrawpool.h"
+
+
+class LLFace;
+class LLHeavenBody;
+class LLWaterSurface;
+
+class LLDrawPoolWater: public LLDrawPool
+{
+protected:
+ LLPointer<LLViewerImage> mHBTex[2];
+ LLPointer<LLViewerImage> mWaterImagep;
+ LLPointer<LLViewerImage> mWaterNormp;
+
+ const LLWaterSurface *mWaterSurface;
+public:
+ enum
+ {
+ SHADER_LEVEL_RIPPLE = 2,
+ };
+
+ LLDrawPoolWater();
+ /*virtual*/ ~LLDrawPoolWater();
+
+ /*virtual*/ LLDrawPool *instancePool();
+ static void restoreGL();
+
+ /*virtual*/ void render(S32 pass = 0);
+ /*virtual*/ void renderFaceSelected(LLFace *facep, LLImageGL *image, const LLColor4 &color,
+ const S32 index_offset = 0, const S32 index_count = 0);
+ /*virtual*/ void prerender();
+ /*virtual*/ void renderForSelect();
+
+ /*virtual*/ LLViewerImage *getDebugTexture();
+ /*virtual*/ LLColor3 getDebugColor() const; // For AGP debug display
+
+ void renderReflection(const LLFace* face);
+ void shade();
+ void renderShaderSimple();
+
+ virtual S32 getMaterialAttribIndex() { return 0; }
+};
+
+void cgErrorCallback();
+
+#endif // LL_LLDRAWPOOLWATER_H
diff --git a/indra/newview/lldriverparam.cpp b/indra/newview/lldriverparam.cpp
new file mode 100644
index 0000000000..e0d1792e6c
--- /dev/null
+++ b/indra/newview/lldriverparam.cpp
@@ -0,0 +1,435 @@
+/**
+ * @file lldriverparam.cpp
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldriverparam.h"
+
+#include "llfasttimer.h"
+
+//-----------------------------------------------------------------------------
+// LLDriverParamInfo
+//-----------------------------------------------------------------------------
+
+LLDriverParamInfo::LLDriverParamInfo()
+{
+}
+
+BOOL LLDriverParamInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_driver" ) );
+
+ if( !LLViewerVisualParamInfo::parseXml( node ))
+ return FALSE;
+
+ LLXmlTreeNode* param_driver_node = node->getChildByName( "param_driver" );
+ if( !param_driver_node )
+ return FALSE;
+
+ for (LLXmlTreeNode* child = param_driver_node->getChildByName( "driven" );
+ child;
+ child = param_driver_node->getNextNamedChild())
+ {
+ S32 driven_id;
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if( child->getFastAttributeS32( id_string, driven_id ) )
+ {
+ F32 min1 = mMinWeight;
+ F32 max1 = mMaxWeight;
+ F32 max2 = max1;
+ F32 min2 = max1;
+
+ // driven ________ //
+ // ^ /| |\ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ // | / | | \ //
+ //-------|----|-------|----|-------> driver //
+ // | min1 max1 max2 min2
+
+ static LLStdStringHandle min1_string = LLXmlTree::addAttributeString("min1");
+ child->getFastAttributeF32( min1_string, min1 ); // optional
+ static LLStdStringHandle max1_string = LLXmlTree::addAttributeString("max1");
+ child->getFastAttributeF32( max1_string, max1 ); // optional
+ static LLStdStringHandle max2_string = LLXmlTree::addAttributeString("max2");
+ child->getFastAttributeF32( max2_string, max2 ); // optional
+ static LLStdStringHandle min2_string = LLXmlTree::addAttributeString("min2");
+ child->getFastAttributeF32( min2_string, min2 ); // optional
+
+ // Push these on the front of the deque, so that we can construct
+ // them in order later (faster)
+ mDrivenInfoList.push_front( LLDrivenEntryInfo( driven_id, min1, max1, max2, min2 ) );
+ }
+ else
+ {
+ llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLDriverParam
+//-----------------------------------------------------------------------------
+
+LLDriverParam::LLDriverParam(LLVOAvatar *avatarp)
+ : mCurrentDistortionParam( NULL ), mAvatarp(avatarp)
+{
+}
+
+LLDriverParam::~LLDriverParam()
+{
+}
+
+BOOL LLDriverParam::setInfo(LLDriverParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLDriverParamInfo::entry_info_list_t::iterator iter;
+ mDriven.reserve(getInfo()->mDrivenInfoList.size());
+ for (iter = getInfo()->mDrivenInfoList.begin(); iter != getInfo()->mDrivenInfoList.end(); iter++)
+ {
+ LLDrivenEntryInfo *driven_info = &(*iter);
+ S32 driven_id = driven_info->mDrivenID;
+ LLViewerVisualParam* param = (LLViewerVisualParam*)mAvatarp->getVisualParam( driven_id );
+ if (param)
+ {
+ mDriven.push_back(LLDrivenEntry( param, driven_info ));
+ }
+ else
+ {
+ llerrs << "<driven> Unable to resolve driven parameter: " << driven_id << llendl;
+ mInfo = NULL;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+BOOL LLDriverParam::parseData(LLXmlTreeNode* node)
+{
+ LLDriverParamInfo* info = new LLDriverParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+void LLDriverParam::setWeight(F32 weight, BOOL set_by_user)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ if (mIsAnimating)
+ {
+ // allow overshoot when animating
+ mCurWeight = weight;
+ }
+ else
+ {
+ mCurWeight = llclamp(weight, min_weight, max_weight);
+ }
+
+ // driven ________
+ // ^ /| |\ ^
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ // | / | | \ |
+ //-------|----|-------|----|-------> driver
+ // | min1 max1 max2 min2
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if (mIsAnimating)
+ {
+ // driven param doesn't interpolate (textures, for example)
+ if (!driven->mParam->getAnimating())
+ {
+ continue;
+ }
+ if( mCurWeight < info->mMin1 )
+ {
+ if (info->mMin1 == min_weight)
+ {
+ if (info->mMin1 == info->mMax1)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //up slope extrapolation
+ F32 t = (mCurWeight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ continue;
+ }
+ else
+ if ( mCurWeight > info->mMin2 )
+ {
+ if (info->mMin2 == max_weight)
+ {
+ if (info->mMin2 == info->mMax2)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ //down slope extrapolation
+ F32 t = (mCurWeight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ continue;
+ }
+ }
+
+ driven_weight = getDrivenWeight(driven, mCurWeight);
+ driven->mParam->setWeight( driven_weight, set_by_user );
+ }
+}
+
+F32 LLDriverParam::getTotalDistortion()
+{
+ F32 sum = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getTotalDistortion();
+ }
+
+ return sum;
+}
+
+const LLVector3 &LLDriverParam::getAvgDistortion()
+{
+ // It's not actually correct to take the average of averages, but it good enough here.
+ LLVector3 sum;
+ S32 count = 0;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getAvgDistortion();
+ count++;
+ }
+ sum /= (F32)count;
+
+ mDefaultVec = sum;
+ return mDefaultVec;
+}
+
+F32 LLDriverParam::getMaxDistortion()
+{
+ F32 max = 0.f;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 param_max = driven->mParam->getMaxDistortion();
+ if( param_max > max )
+ {
+ max = param_max;
+ }
+ }
+
+ return max;
+}
+
+
+LLVector3 LLDriverParam::getVertexDistortion(S32 index, LLPolyMesh *poly_mesh)
+{
+ LLVector3 sum;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ sum += driven->mParam->getVertexDistortion( index, poly_mesh );
+ }
+ return sum;
+}
+
+const LLVector3* LLDriverParam::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ mCurrentDistortionParam = NULL;
+ const LLVector3* v = NULL;
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+
+ return v;
+};
+
+const LLVector3* LLDriverParam::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ llassert( mCurrentDistortionParam );
+ if( !mCurrentDistortionParam )
+ {
+ return NULL;
+ }
+
+ LLDrivenEntry* driven = NULL;
+ entry_list_t::iterator iter;
+
+ // Set mDriven iteration to the right point
+ for( iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ if( driven->mParam == mCurrentDistortionParam )
+ {
+ break;
+ }
+ }
+
+ // We're already in the middle of a param's distortions, so get the next one.
+ const LLVector3* v = driven->mParam->getNextDistortion( index, poly_mesh );
+ if( !v )
+ {
+ // This param is finished, so start the next param. It might not have any
+ // distortions, though, so we have to loop to find the next param that does.
+ for( iter++; iter != mDriven.end(); iter++ )
+ {
+ driven = &(*iter);
+ v = driven->mParam->getFirstDistortion( index, poly_mesh );
+ if( v )
+ {
+ mCurrentDistortionParam = driven->mParam;
+ break;
+ }
+ }
+ }
+
+ return v;
+};
+
+//-----------------------------------------------------------------------------
+// setAnimationTarget()
+//-----------------------------------------------------------------------------
+void LLDriverParam::setAnimationTarget( F32 target_value, BOOL set_by_user )
+{
+ LLVisualParam::setAnimationTarget(target_value, set_by_user);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ F32 driven_weight = getDrivenWeight(driven, mTargetWeight);
+
+ // this isn't normally necessary, as driver params handle interpolation of their driven params
+ // but texture params need to know to assume their final value at beginning of interpolation
+ driven->mParam->setAnimationTarget(driven_weight, set_by_user);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// stopAnimating()
+//-----------------------------------------------------------------------------
+void LLDriverParam::stopAnimating(BOOL set_by_user)
+{
+ LLVisualParam::stopAnimating(set_by_user);
+
+ for( entry_list_t::iterator iter = mDriven.begin(); iter != mDriven.end(); iter++ )
+ {
+ LLDrivenEntry* driven = &(*iter);
+ driven->mParam->setAnimating(FALSE);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getDrivenWeight()
+//-----------------------------------------------------------------------------
+F32 LLDriverParam::getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight)
+{
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ const LLDrivenEntryInfo* info = driven->mInfo;
+
+ F32 driven_weight = 0.f;
+ F32 driven_min = driven->mParam->getMinWeight();
+ F32 driven_max = driven->mParam->getMaxWeight();
+
+ if( input_weight <= info->mMin1 )
+ {
+ if( info->mMin1 == info->mMax1 &&
+ info->mMin1 <= min_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+ else
+ if( input_weight <= info->mMax1 )
+ {
+ F32 t = (input_weight - info->mMin1) / (info->mMax1 - info->mMin1 );
+ driven_weight = driven_min + t * (driven_max - driven_min);
+ }
+ else
+ if( input_weight <= info->mMax2 )
+ {
+ driven_weight = driven_max;
+ }
+ else
+ if( input_weight <= info->mMin2 )
+ {
+ F32 t = (input_weight - info->mMax2) / (info->mMin2 - info->mMax2 );
+ driven_weight = driven_max + t * (driven_min - driven_max);
+ }
+ else
+ {
+ if (info->mMax2 >= max_weight)
+ {
+ driven_weight = driven_max;
+ }
+ else
+ {
+ driven_weight = driven_min;
+ }
+ }
+
+ return driven_weight;
+}
diff --git a/indra/newview/lldriverparam.h b/indra/newview/lldriverparam.h
new file mode 100644
index 0000000000..06850e3eaf
--- /dev/null
+++ b/indra/newview/lldriverparam.h
@@ -0,0 +1,90 @@
+/**
+ * @file lldriverparam.h
+ * @brief A visual parameter that drives (controls) other visual parameters.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDRIVERPARAM_H
+#define LL_LLDRIVERPARAM_H
+
+#include "llvoavatar.h"
+#include "llviewervisualparam.h"
+
+//-----------------------------------------------------------------------------
+
+struct LLDrivenEntryInfo
+{
+ LLDrivenEntryInfo( S32 id, F32 min1, F32 max1, F32 max2, F32 min2 )
+ : mDrivenID( id ), mMin1( min1 ), mMax1( max1 ), mMax2( max2 ), mMin2( min2 ) {}
+ S32 mDrivenID;
+ F32 mMin1;
+ F32 mMax1;
+ F32 mMax2;
+ F32 mMin2;
+};
+
+struct LLDrivenEntry
+{
+ LLDrivenEntry( LLViewerVisualParam* param, LLDrivenEntryInfo *info )
+ : mParam( param ), mInfo( info ) {}
+ LLViewerVisualParam* mParam;
+ LLDrivenEntryInfo* mInfo;
+};
+
+//-----------------------------------------------------------------------------
+
+class LLDriverParamInfo : public LLViewerVisualParamInfo
+{
+ friend class LLDriverParam;
+public:
+ LLDriverParamInfo();
+ /*virtual*/ ~LLDriverParamInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::deque<LLDrivenEntryInfo> entry_info_list_t;
+ entry_info_list_t mDrivenInfoList;
+};
+
+//-----------------------------------------------------------------------------
+
+class LLDriverParam : public LLViewerVisualParam
+{
+public:
+ LLDriverParam(LLVOAvatar *avatarp);
+ ~LLDriverParam();
+
+ // Special: These functions are overridden by child classes
+ LLDriverParamInfo* getInfo() const { return (LLDriverParamInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLDriverParamInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex ) {} // apply is called separately for each driven param.
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget( F32 target_value, BOOL set_by_user );
+ /*virtual*/ void stopAnimating(BOOL set_by_user);
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector3& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+protected:
+ F32 getDrivenWeight(const LLDrivenEntry* driven, F32 input_weight);
+
+
+ LLVector3 mDefaultVec; // temp holder
+ typedef std::vector<LLDrivenEntry> entry_list_t;
+ entry_list_t mDriven;
+ LLViewerVisualParam* mCurrentDistortionParam;
+ LLVOAvatar* mAvatarp;
+};
+
+#endif // LL_LLDRIVERPARAM_H
diff --git a/indra/newview/lldynamictexture.cpp b/indra/newview/lldynamictexture.cpp
new file mode 100644
index 0000000000..49fcce4911
--- /dev/null
+++ b/indra/newview/lldynamictexture.cpp
@@ -0,0 +1,215 @@
+/**
+ * @file lldynamictexture.cpp
+ * @brief Implementation of LLDynamicTexture class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldynamictexture.h"
+#include "linked_lists.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerimage.h"
+
+// static
+LLLinkedList<LLDynamicTexture> LLDynamicTexture::sInstances[ LLDynamicTexture::ORDER_COUNT ];
+S32 LLDynamicTexture::sNumRenders = 0;
+
+//-----------------------------------------------------------------------------
+// LLDynamicTexture()
+//-----------------------------------------------------------------------------
+LLDynamicTexture::LLDynamicTexture(S32 width, S32 height, S32 components, EOrder order, BOOL clamp) :
+ mWidth(width),
+ mHeight(height),
+ mComponents(components),
+ mTexture(NULL),
+ mLastBindTime(0),
+ mClamp(clamp)
+{
+ llassert((1 <= components) && (components <= 4));
+
+ generateGLTexture();
+
+ llassert( 0 <= order && order < ORDER_COUNT );
+ LLDynamicTexture::sInstances[ order ].addData(this);
+}
+
+//-----------------------------------------------------------------------------
+// LLDynamicTexture()
+//-----------------------------------------------------------------------------
+LLDynamicTexture::~LLDynamicTexture()
+{
+ for( S32 order = 0; order < ORDER_COUNT; order++ )
+ {
+ LLDynamicTexture::sInstances[order].removeData(this); // will fail in all but one case.
+ }
+}
+
+//-----------------------------------------------------------------------------
+// generateGLTexture()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::generateGLTexture()
+{
+ if (mComponents < 1 || mComponents > 4)
+ {
+ llerrs << "Bad number of components in dynamic texture: " << mComponents << llendl;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw(mWidth, mHeight, mComponents);
+ mTexture = new LLImageGL(mWidth, mHeight, mComponents, FALSE);
+ mTexture->createGLTexture(0, raw_image);
+ mTexture->setClamp(mClamp, mClamp);
+}
+
+void LLDynamicTexture::generateGLTexture(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes)
+{
+ if (mComponents < 1 || mComponents > 4)
+ {
+ llerrs << "Bad number of components in dynamic texture: " << mComponents << llendl;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw(mWidth, mHeight, mComponents);
+ mTexture = new LLImageGL(mWidth, mHeight, mComponents, FALSE);
+ mTexture->setExplicitFormat(internal_format, primary_format, type_format, swap_bytes);
+ mTexture->createGLTexture(0, raw_image);
+ mTexture->setClamp(mClamp, mClamp);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLDynamicTexture::render()
+{
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// preRender()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::preRender(BOOL clear_depth)
+{
+ {
+ // force rendering to on-screen portion of frame buffer
+ LLCoordScreen window_pos;
+ gViewerWindow->getWindow()->getPosition( &window_pos );
+ mOrigin.set(0, gViewerWindow->getWindowDisplayHeight() - mHeight); // top left corner
+
+ if (window_pos.mX < 0)
+ {
+ mOrigin.mX = -window_pos.mX;
+ }
+ if (window_pos.mY < 0)
+ {
+ mOrigin.mY += window_pos.mY;
+ }
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ // Set up camera
+ mCamera.setOrigin(*gCamera);
+ mCamera.setAxes(*gCamera);
+ mCamera.setAspect(gCamera->getAspect());
+ mCamera.setView(gCamera->getView());
+ mCamera.setNear(gCamera->getNear());
+
+ glViewport(mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ if (clear_depth)
+ {
+ glClear(GL_DEPTH_BUFFER_BIT);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// postRender()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::postRender(BOOL success)
+{
+ {
+ if (success)
+ {
+ success = mTexture->setSubImageFromFrameBuffer(0, 0, mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ }
+ }
+
+ // restore viewport
+ gViewerWindow->setupViewport();
+
+ // restore camera
+ gCamera->setOrigin(mCamera);
+ gCamera->setAxes(mCamera);
+ gCamera->setAspect(mCamera.getAspect());
+ gCamera->setView(mCamera.getView());
+ gCamera->setNear(mCamera.getNear());
+}
+
+//-----------------------------------------------------------------------------
+// bindTexture()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::bindTexture()
+{
+ LLViewerImage::bindTexture(mTexture,0);
+}
+
+void LLDynamicTexture::unbindTexture()
+{
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+//-----------------------------------------------------------------------------
+// static
+// updateDynamicTextures()
+// Calls update on each dynamic texture. Calls each group in order: "first," then "middle," then "last."
+//-----------------------------------------------------------------------------
+BOOL LLDynamicTexture::updateAllInstances()
+{
+ sNumRenders = 0;
+ if (gGLManager.mIsDisabled)
+ {
+ return TRUE;
+ }
+
+ BOOL result = FALSE;
+ for( S32 order = 0; order < ORDER_COUNT; order++ )
+ {
+ for (LLDynamicTexture *dynamicTexture = LLDynamicTexture::sInstances[order].getFirstData();
+ dynamicTexture;
+ dynamicTexture = LLDynamicTexture::sInstances[order].getNextData())
+ {
+ if (dynamicTexture->needsRender())
+ {
+ dynamicTexture->preRender();
+ if (dynamicTexture->render())
+ {
+ result = TRUE;
+ gViewerWindow->finishFastFrame();
+ sNumRenders++;
+ }
+ dynamicTexture->postRender(result);
+ }
+ }
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// static
+// destroyGL()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::destroyGL()
+{
+}
+
+//-----------------------------------------------------------------------------
+// static
+// restoreGL()
+//-----------------------------------------------------------------------------
+void LLDynamicTexture::restoreGL()
+{
+}
diff --git a/indra/newview/lldynamictexture.h b/indra/newview/lldynamictexture.h
new file mode 100644
index 0000000000..6ff457e9a9
--- /dev/null
+++ b/indra/newview/lldynamictexture.h
@@ -0,0 +1,67 @@
+/**
+ * @file lldynamictexture.h
+ * @brief Implementation of LLDynamicTexture class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLDYNAMICTEXTURE_H
+#define LL_LLDYNAMICTEXTURE_H
+
+#include "llgl.h"
+#include "linked_lists.h"
+#include "llcamera.h"
+#include "llcoord.h"
+#include "llimagegl.h"
+
+class LLDynamicTexture
+{
+public:
+ enum EOrder { ORDER_FIRST = 0, ORDER_MIDDLE = 1, ORDER_LAST = 2, ORDER_RESET = 3, ORDER_COUNT = 4 };
+
+ LLDynamicTexture(S32 width,
+ S32 height,
+ S32 components, // = 4,
+ EOrder order, // = ORDER_MIDDLE,
+ BOOL clamp);
+ virtual ~LLDynamicTexture();
+
+ S32 getOriginX() { return mOrigin.mX; }
+ S32 getOriginY() { return mOrigin.mY; }
+ S32 getWidth() { return mWidth; }
+ S32 getHeight() { return mHeight; }
+ S32 getComponents() { return mComponents; }
+ S32 getSize() { return mWidth * mHeight * mComponents; }
+
+ virtual BOOL needsRender() { return TRUE; }
+ virtual void preRender(BOOL clear_depth = TRUE);
+ virtual BOOL render();
+ virtual void postRender(BOOL success);
+ virtual void bindTexture();
+ virtual void unbindTexture();
+
+ static BOOL updateAllInstances();
+
+ static void destroyGL();
+ static void restoreGL();
+
+protected:
+ void generateGLTexture();
+ void generateGLTexture(LLGLint internal_format, LLGLenum primary_format, LLGLenum type_format, BOOL swap_bytes = FALSE);
+
+protected:
+ S32 mWidth;
+ S32 mHeight;
+ S32 mComponents;
+ LLPointer<LLImageGL> mTexture;
+ F32 mLastBindTime;
+ BOOL mClamp;
+ LLCoordGL mOrigin;
+
+ LLCamera mCamera;
+ static LLLinkedList<LLDynamicTexture> LLDynamicTexture::sInstances[ LLDynamicTexture::ORDER_COUNT ];
+ static S32 sNumRenders;
+};
+
+#endif
diff --git a/indra/newview/llemote.cpp b/indra/newview/llemote.cpp
new file mode 100644
index 0000000000..108410b275
--- /dev/null
+++ b/indra/newview/llemote.cpp
@@ -0,0 +1,126 @@
+/**
+ * @file llemote.cpp
+ * @brief Implementation of LLEmote class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llemote.h"
+#include "llcharacter.h"
+#include "m3math.h"
+#include "llvoavatar.h"
+#include "llagent.h"
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLEmote()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLEmote::LLEmote(const LLUUID &id) : LLMotion(id)
+{
+ mCharacter = NULL;
+
+ //RN: flag face joint as highest priority for now, until we implement a proper animation track
+ mJointSignature[0][LL_FACE_JOINT_NUM] = 0xff;
+ mJointSignature[1][LL_FACE_JOINT_NUM] = 0xff;
+ mJointSignature[2][LL_FACE_JOINT_NUM] = 0xff;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLEmote()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLEmote::~LLEmote()
+{
+}
+
+//-----------------------------------------------------------------------------
+// LLEmote::onInitialize(LLCharacter *character)
+//-----------------------------------------------------------------------------
+LLMotion::LLMotionInitStatus LLEmote::onInitialize(LLCharacter *character)
+{
+ mCharacter = character;
+ return STATUS_SUCCESS;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onActivate()
+//-----------------------------------------------------------------------------
+BOOL LLEmote::onActivate()
+{
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ default_param->setWeight( default_param->getMaxWeight(), FALSE );
+ }
+
+ mParam = mCharacter->getVisualParam(mName.c_str());
+ if (mParam)
+ {
+ mParam->setWeight(0.f, FALSE);
+ mCharacter->updateVisualParams();
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onUpdate()
+//-----------------------------------------------------------------------------
+BOOL LLEmote::onUpdate(F32 time, U8* joint_mask)
+{
+ if( mParam )
+ {
+ F32 weight = mParam->getMinWeight() + mPose.getWeight() * (mParam->getMaxWeight() - mParam->getMinWeight());
+ mParam->setWeight(weight, FALSE);
+
+ // Cross fade against the default parameter
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ F32 default_param_weight = default_param->getMinWeight() +
+ (1.f - mPose.getWeight()) * ( default_param->getMaxWeight() - default_param->getMinWeight() );
+
+ default_param->setWeight( default_param_weight, FALSE );
+ }
+
+ mCharacter->updateVisualParams();
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLEmote::onDeactivate()
+//-----------------------------------------------------------------------------
+void LLEmote::onDeactivate()
+{
+ if( mParam )
+ {
+ mParam->setWeight( mParam->getDefaultWeight(), FALSE );
+ }
+
+ LLVisualParam* default_param = mCharacter->getVisualParam( "Express_Closed_Mouth" );
+ if( default_param )
+ {
+ default_param->setWeight( default_param->getMaxWeight(), FALSE );
+ }
+
+ mCharacter->updateVisualParams();
+}
+
+
+// End
diff --git a/indra/newview/llemote.h b/indra/newview/llemote.h
new file mode 100644
index 0000000000..c330d0bfc0
--- /dev/null
+++ b/indra/newview/llemote.h
@@ -0,0 +1,104 @@
+/**
+ * @file llemote.h
+ * @brief Definition of LLEmote class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEMOTE_H
+#define LL_LLEMOTE_H
+
+//-----------------------------------------------------------------------------
+// Header files
+//-----------------------------------------------------------------------------
+#include "llmotion.h"
+#include "lltimer.h"
+
+#define MIN_REQUIRED_PIXEL_AREA_EMOTE 2000.f
+
+#define EMOTE_MORPH_FADEIN_TIME 0.3f
+#define EMOTE_MORPH_IN_TIME 1.1f
+#define EMOTE_MORPH_FADEOUT_TIME 1.4f
+
+class LLVisualParam;
+
+//-----------------------------------------------------------------------------
+// class LLEmote
+//-----------------------------------------------------------------------------
+class LLEmote :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLEmote(const LLUUID &id);
+
+ // Destructor
+ virtual ~LLEmote();
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLEmote(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return FALSE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return EMOTE_MORPH_FADEIN_TIME + EMOTE_MORPH_IN_TIME + EMOTE_MORPH_FADEOUT_TIME; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return EMOTE_MORPH_FADEIN_TIME; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return EMOTE_MORPH_FADEOUT_TIME; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_EMOTE; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character);
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate();
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask);
+
+ // called when a motion is deactivated
+ virtual void onDeactivate();
+
+ static BOOL getIndexFromName( const char* name, U32* index );
+
+protected:
+
+ LLCharacter* mCharacter;
+
+ LLVisualParam* mParam;
+};
+
+
+
+#endif // LL_LLEMOTE_H
+
+
diff --git a/indra/newview/lleventinfo.cpp b/indra/newview/lleventinfo.cpp
new file mode 100644
index 0000000000..8f63b61fa1
--- /dev/null
+++ b/indra/newview/lleventinfo.cpp
@@ -0,0 +1,134 @@
+/**
+ * @file lleventinfo.cpp
+ * @brief LLEventInfo class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "lleventinfo.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "lluuid.h"
+#include "message.h"
+
+LLEventInfo::cat_map LLEventInfo::sCategories;
+
+LLEventInfo::LLEventInfo(F32 global_x, F32 global_y,
+ const char* name,
+ U32 id,
+ S32 unix_time,
+ U32 event_flags)
+: mName( name ),
+ mID( id ),
+ mPosGlobal( global_x, global_y, 40.0 ),
+ mUnixTime( unix_time ),
+ mEventFlags(event_flags),
+ mSelected( FALSE )
+{
+ struct tm* timep;
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(unix_time, gPacificDaylightTime);
+
+ S32 display_hour = timep->tm_hour % 12;
+ if (display_hour == 0) display_hour = 12;
+
+ mTimeStr = llformat("% 2d/% 2d % 2d:%02d %s",
+ timep->tm_mon+1,
+ timep->tm_year-100,
+ display_hour,
+ timep->tm_min,
+ (timep->tm_hour < 12 ? "AM" : "PM") );
+}
+
+
+void LLEventInfo::unpack(LLMessageSystem *msg)
+{
+ const U32 MAX_DESC_LENGTH = 1024;
+
+ U32 event_id;
+ msg->getU32("EventData", "EventID", event_id);
+ mID = event_id;
+
+ char buffer[MAX_DESC_LENGTH];
+ msg->getString("EventData", "Name", MAX_DESC_LENGTH, buffer);
+ mName = buffer;
+
+ msg->getString("EventData", "Category", MAX_DESC_LENGTH, buffer);
+ mCategoryStr = buffer;
+
+ msg->getString("EventData", "Date", MAX_DESC_LENGTH, buffer);
+ // *FIX: evil hack to let users know that we don't localize
+ // time information. Hack! This is WRONG.
+ mTimeStr = buffer;
+
+ U32 duration;
+ msg->getU32("EventData","Duration",duration);
+ mDuration = duration;
+
+ msg->getU32("EventData", "DateUTC", mUnixTime);
+
+ msg->getString("EventData", "Desc", MAX_DESC_LENGTH, buffer);
+ mDesc = buffer;
+
+ msg->getString("EventData", "Creator", MAX_DESC_LENGTH, buffer);
+ mRunByID = LLUUID(buffer);
+
+ U32 foo;
+ msg->getU32("EventData", "Cover", foo);
+
+ mHasCover = foo ? TRUE : FALSE;
+ if (mHasCover)
+ {
+ U32 cover;
+ msg->getU32("EventData", "Amount", cover);
+ mCover = cover;
+ }
+
+ char sim_name[256];
+ msg->getString("EventData", "SimName", 256, sim_name);
+ mSimName.assign(sim_name);
+
+ msg->getVector3d("EventData", "GlobalPos", mPosGlobal);
+
+ // Mature content
+ U32 event_flags;
+ msg->getU32("EventData", "EventFlags", event_flags);
+ mEventFlags = event_flags;
+}
+
+// static
+void LLEventInfo::loadCategories(LLUserAuth::options_t event_options)
+{
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = event_options.begin();
+ resp_it != event_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLUserAuth::response_t::const_iterator option_it;
+
+ S32 cat_id = 0;
+ option_it = response.find("category_id");
+ if (option_it != response.end())
+ {
+ cat_id = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ continue;
+ }
+
+ // Add the category id/name pair
+ option_it = response.find("category_name");
+ if (option_it != response.end())
+ {
+ LLEventInfo::sCategories[cat_id] = option_it->second;
+ }
+
+ }
+
+}
diff --git a/indra/newview/lleventinfo.h b/indra/newview/lleventinfo.h
new file mode 100644
index 0000000000..d8149a8e27
--- /dev/null
+++ b/indra/newview/lleventinfo.h
@@ -0,0 +1,50 @@
+/**
+ * @file lleventinfo.h
+ * @brief LLEventInfo class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTINFO_H
+#define LL_LLEVENTINFO_H
+
+#include <map>
+
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "lluserauth.h"
+
+class LLMessageSystem;
+
+class LLEventInfo
+{
+public:
+ LLEventInfo() {}
+ LLEventInfo(F32 global_x, F32 global_y, const char* name, U32 id, S32 unix_time, U32 event_flags);
+
+ void unpack(LLMessageSystem *msg);
+
+ static void loadCategories(LLUserAuth::options_t event_options);
+
+public:
+ std::string mName;
+ U32 mID;
+ std::string mDesc;
+ std::string mCategoryStr;
+ U32 mDuration;
+ std::string mTimeStr;
+ LLUUID mRunByID;
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+ U32 mUnixTime; // seconds from 1970
+ BOOL mHasCover;
+ U32 mCover;
+ U32 mEventFlags;
+ BOOL mSelected;
+
+ typedef std::map<U32, std::string> cat_map;
+ static cat_map sCategories;
+};
+
+#endif // LL_LLEVENTINFO_H
diff --git a/indra/newview/lleventnotifier.cpp b/indra/newview/lleventnotifier.cpp
new file mode 100644
index 0000000000..72654b9a59
--- /dev/null
+++ b/indra/newview/lleventnotifier.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file lleventnotifier.cpp
+ * @brief Viewer code for managing event notifications
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lleventnotifier.h"
+
+#include "message.h"
+
+#include "llnotify.h"
+#include "lleventinfo.h"
+#include "llfloaterdirectory.h"
+#include "llfloaterworldmap.h"
+#include "llagent.h"
+
+LLEventNotifier gEventNotifier;
+
+LLEventNotifier::LLEventNotifier()
+{
+}
+
+
+LLEventNotifier::~LLEventNotifier()
+{
+ en_map::iterator iter;
+
+ for (iter = mEventNotifications.begin();
+ iter != mEventNotifications.end();
+ iter++)
+ {
+ delete iter->second;
+ }
+}
+
+
+void LLEventNotifier::update()
+{
+ if (mNotificationTimer.getElapsedTimeF32() > 30.f)
+ {
+ // Check our notifications again and send out updates
+ // if they happen.
+
+ U32 alert_time = time_corrected() + 5 * 60;
+ en_map::iterator iter;
+ for (iter = mEventNotifications.begin();
+ iter != mEventNotifications.end();)
+ {
+ LLEventNotification *np = iter->second;
+
+ if (np->getEventDate() < (alert_time))
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = np->getEventName();
+ args["[DATE]"] = np->getEventDateStr();
+ LLNotifyBox::showXml("EventNotification", args,
+ notifyCallback, np);
+ mEventNotifications.erase(iter++);
+ }
+ else
+ {
+ iter++;
+ }
+ }
+ mNotificationTimer.reset();
+ }
+}
+
+void LLEventNotifier::load(const LLUserAuth::options_t& event_options)
+{
+ LLUserAuth::options_t::const_iterator resp_it;
+ for (resp_it = event_options.begin();
+ resp_it != event_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+
+ LLEventNotification *new_enp = new LLEventNotification();
+
+ if (!new_enp->load(response))
+ {
+ delete new_enp;
+ continue;
+ }
+
+ mEventNotifications[new_enp->getEventID()] = new_enp;
+ }
+}
+
+
+BOOL LLEventNotifier::hasNotification(const U32 event_id)
+{
+ if (mEventNotifications.find(event_id) != mEventNotifications.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLEventNotifier::add(LLEventInfo &event_info)
+{
+ // We need to tell the simulator that we want to pay attention to
+ // this event, as well as add it to our list.
+
+ if (mEventNotifications.find(event_info.mID) != mEventNotifications.end())
+ {
+ // We already have a notification for this event, don't bother.
+ return;
+ }
+
+ // Push up a message to tell the server we have this notification.
+ gMessageSystem->newMessage("EventNotificationAddRequest");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlock("EventData");
+ gMessageSystem->addU32("EventID", event_info.mID);
+ gAgent.sendReliableMessage();
+
+ LLEventNotification *enp = new LLEventNotification;
+ enp->load(event_info);
+ mEventNotifications[event_info.mID] = enp;
+}
+
+void LLEventNotifier::remove(const U32 event_id)
+{
+ en_map::iterator iter;
+ iter = mEventNotifications.find(event_id);
+ if (iter == mEventNotifications.end())
+ {
+ // We don't have a notification for this event, don't bother.
+ return;
+ }
+
+ // Push up a message to tell the server to remove this notification.
+ gMessageSystem->newMessage("EventNotificationRemoveRequest");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlock("EventData");
+ gMessageSystem->addU32("EventID", event_id);
+ gAgent.sendReliableMessage();
+
+ delete iter->second;
+ mEventNotifications.erase(iter);
+}
+
+//static
+void LLEventNotifier::notifyCallback(S32 option, void *user_data)
+{
+ LLEventNotification *np = (LLEventNotification *)user_data;
+ if (!np)
+ {
+ llwarns << "Event notification callback without data!" << llendl;
+ return;
+ }
+ switch (option)
+ {
+ case 0:
+ gAgent.teleportViaLocation(np->getEventPosGlobal());
+ gFloaterWorldMap->trackLocation(np->getEventPosGlobal());
+ break;
+ case 1:
+ gDisplayEventHack = TRUE;
+ LLFloaterDirectory::showEvents(np->getEventID());
+ break;
+ case 2:
+ break;
+ }
+
+ // We could clean up the notification on the server now if we really wanted to.
+}
+
+
+
+LLEventNotification::LLEventNotification() :
+ mEventID(0),
+ mEventName("")
+{
+}
+
+
+LLEventNotification::~LLEventNotification()
+{
+}
+
+
+BOOL LLEventNotification::load(const LLUserAuth::response_t &response)
+{
+
+ LLUserAuth::response_t::const_iterator option_it;
+ BOOL event_ok = TRUE;
+ option_it = response.find("event_id");
+ if (option_it != response.end())
+ {
+ mEventID = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("event_name");
+ if (option_it != response.end())
+ {
+ llinfos << "Event: " << option_it->second << llendl;
+ mEventName = option_it->second;
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+
+ option_it = response.find("event_date");
+ if (option_it != response.end())
+ {
+ llinfos << "EventDate: " << option_it->second << llendl;
+ mEventDateStr = option_it->second;
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("event_date_ut");
+ if (option_it != response.end())
+ {
+ llinfos << "EventDate: " << option_it->second << llendl;
+ mEventDate = strtoul(option_it->second.c_str(), NULL, 10);
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ S32 grid_x = 0;
+ S32 grid_y = 0;
+ S32 x_region = 0;
+ S32 y_region = 0;
+
+ option_it = response.find("grid_x");
+ if (option_it != response.end())
+ {
+ llinfos << "GridX: " << option_it->second << llendl;
+ grid_x= atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("grid_y");
+ if (option_it != response.end())
+ {
+ llinfos << "GridY: " << option_it->second << llendl;
+ grid_y = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("x_region");
+ if (option_it != response.end())
+ {
+ llinfos << "RegionX: " << option_it->second << llendl;
+ x_region = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ option_it = response.find("y_region");
+ if (option_it != response.end())
+ {
+ llinfos << "RegionY: " << option_it->second << llendl;
+ y_region = atoi(option_it->second.c_str());
+ }
+ else
+ {
+ event_ok = FALSE;
+ }
+
+ mEventPosGlobal.mdV[VX] = grid_x * 256 + x_region;
+ mEventPosGlobal.mdV[VY] = grid_y * 256 + y_region;
+ mEventPosGlobal.mdV[VZ] = 0.f;
+
+ return event_ok;
+}
+
+BOOL LLEventNotification::load(const LLEventInfo &event_info)
+{
+
+ mEventID = event_info.mID;
+ mEventName = event_info.mName;
+ mEventDateStr = event_info.mTimeStr;
+ mEventDate = event_info.mUnixTime;
+ mEventPosGlobal = event_info.mPosGlobal;
+ return TRUE;
+}
+
diff --git a/indra/newview/lleventnotifier.h b/indra/newview/lleventnotifier.h
new file mode 100644
index 0000000000..90a11ed81d
--- /dev/null
+++ b/indra/newview/lleventnotifier.h
@@ -0,0 +1,68 @@
+/**
+ * @file lleventnotifier.h
+ * @brief Viewer code for managing event notifications
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTNOTIFIER_H
+#define LL_LLEVENTNOTIFIER_H
+
+#include "llframetimer.h"
+#include "lluserauth.h"
+#include "v3dmath.h"
+
+class LLEventInfo;
+class LLEventNotification;
+
+
+class LLEventNotifier
+{
+public:
+ LLEventNotifier();
+ virtual ~LLEventNotifier();
+
+ void update(); // Notify the user of the event if it's coming up
+
+ void load(const LLUserAuth::options_t& event_options); // In the format that it comes in from LLUserAuth
+ void add(LLEventInfo &event_info); // Add a new notification for an event
+ void remove(U32 event_id);
+
+ BOOL hasNotification(const U32 event_id);
+
+ typedef std::map<U32, LLEventNotification *> en_map;
+
+ static void notifyCallback(S32 option, void *user_data);
+protected:
+ en_map mEventNotifications;
+ LLFrameTimer mNotificationTimer;
+};
+
+
+class LLEventNotification
+{
+public:
+ LLEventNotification();
+ virtual ~LLEventNotification();
+
+ BOOL load(const LLUserAuth::response_t &en); // In the format it comes in from LLUserAuth
+ BOOL load(const LLEventInfo &event_info); // From existing event_info on the viewer.
+ //void setEventID(const U32 event_id);
+ //void setEventName(std::string &event_name);
+ U32 getEventID() const { return mEventID; }
+ const std::string &getEventName() const { return mEventName; }
+ U32 getEventDate() const { return mEventDate; }
+ const std::string &getEventDateStr() const { return mEventDateStr; }
+ LLVector3d getEventPosGlobal() const { return mEventPosGlobal; }
+protected:
+ U32 mEventID; // EventID for this event
+ std::string mEventName;
+ std::string mEventDateStr;
+ U32 mEventDate;
+ LLVector3d mEventPosGlobal;
+};
+
+extern LLEventNotifier gEventNotifier;
+
+#endif //LL_LLEVENTNOTIFIER_H
diff --git a/indra/newview/lleventpoll.cpp b/indra/newview/lleventpoll.cpp
new file mode 100644
index 0000000000..5e12916975
--- /dev/null
+++ b/indra/newview/lleventpoll.cpp
@@ -0,0 +1,166 @@
+/**
+ * @file lleventpoll.cpp
+ * @brief Implementation of the LLEventPoll class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lleventpoll.h"
+
+#include "llhttpclient.h"
+#include "llhttpnode.h"
+#include "llsdserialize.h"
+
+
+
+class LLEventPoll::Impl : LLHTTPClient::Responder
+{
+public:
+ static Impl& start(
+ const std::string& pollURL, const LLHTTPNode& treeRoot)
+ {
+ Impl* i = new Impl(pollURL, treeRoot);
+ llinfos << "LLEventPoll::Impl::start <" << i->mCount << "> "
+ << pollURL << llendl;
+ return *i;
+ }
+
+ void stop()
+ {
+ llinfos << "LLEventPoll::Impl::stop <" << mCount << "> "
+ << mPollURL << llendl;
+ // there should be a way to stop a LLHTTPClient request in progress
+ mDone = true;
+ mPtr = NULL;
+ }
+
+private:
+ Impl(const std::string& pollURL, const LLHTTPNode& treeRoot)
+ : mPtr(NULL), mDone(false),
+ mPollURL(pollURL), mTreeRoot(treeRoot),
+ mCount(++sCount)
+ {
+ mPtr = this;
+ makeRequest();
+ }
+
+ ~Impl()
+ {
+ lldebugs << "LLEventPoll::Impl::~Impl <" << mCount << "> "
+ << mPollURL << llendl;
+ }
+
+
+ void makeRequest()
+ {
+ LLSD request;
+ request["ack"] = mAcknowledge;
+ request["done"] = mDone;
+
+ lldebugs << "LLEventPoll::Impl::makeRequest <" << mCount << "> ack = "
+ << LLSDXMLStreamer(mAcknowledge) << llendl;
+ LLHTTPClient::post(mPollURL, request, mPtr);
+ }
+
+ void handleMessage(const LLSD& content)
+ {
+ std::string message = content["message"];
+ if (message.empty())
+ {
+ llwarns << "LLEventPoll::Impl::handleMessage <" << mCount
+ << "> empty message name" << llendl;
+ return;
+ }
+
+ std::string path = "/message/" + message;
+
+ LLSD context;
+ const LLHTTPNode* handler = mTreeRoot.traverse(path, context);
+ if (!handler)
+ {
+ llwarns << "LLEventPoll::Impl::handleMessage <" << mCount
+ << "> no handler for " << path << llendl;
+ return;
+ }
+ LLPointer<LLSimpleResponse> responsep = LLSimpleResponse::create();
+ handler->post((LLHTTPNode::ResponsePtr)responsep, context, content["body"]);
+
+ lldebugs << "LLEventPoll::Impl::handleMessage handled <" << mCount << "> "
+ << message << ": " << *responsep << llendl;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ lldebugs << "LLEventPoll::Impl::error <" << mCount << "> got "
+ << status << ": " << reason
+ << (mDone ? " -- done" : "") << llendl;
+
+ if (mDone) return;
+
+ if (status == 404)
+ {
+ // the capability has been revoked
+ stop();
+ return;
+ }
+
+ makeRequest();
+ }
+
+
+ virtual void result(const LLSD& content)
+ {
+ lldebugs << "LLEventPoll::Impl::result <" << mCount << ">"
+ << (mDone ? " -- done" : "") << llendl;
+
+ if (mDone) return;
+
+ mAcknowledge = content["id"];
+ LLSD events = content["events"];
+
+ lldebugs << "LLEventPoll::Impl::completed <" << mCount << "> ack = "
+ << LLSDXMLStreamer(mAcknowledge) << llendl;
+
+ LLSD::array_const_iterator i = events.beginArray();
+ LLSD::array_const_iterator end = events.endArray();
+ for (; i != end; ++i)
+ {
+ if (i->has("message"))
+ {
+ handleMessage(*i);
+ }
+ }
+
+ makeRequest();
+ }
+
+private:
+ typedef LLHTTPClient::ResponderPtr Ptr;
+
+ Ptr mPtr;
+ bool mDone;
+
+ std::string mPollURL;
+ const LLHTTPNode& mTreeRoot;
+
+ LLSD mAcknowledge;
+
+ // these are only here for debugging so we can see which poller is which
+ static int sCount;
+ int mCount;
+};
+
+int LLEventPoll::Impl::sCount = 0;
+
+
+LLEventPoll::LLEventPoll(const std::string& pollURL, const LLHTTPNode& treeRoot)
+ : impl(Impl::start(pollURL, treeRoot))
+ { }
+
+LLEventPoll::~LLEventPoll()
+{
+ impl.stop();
+}
diff --git a/indra/newview/lleventpoll.h b/indra/newview/lleventpoll.h
new file mode 100644
index 0000000000..c5024b2b95
--- /dev/null
+++ b/indra/newview/lleventpoll.h
@@ -0,0 +1,37 @@
+/**
+ * @file lleventpoll.h
+ * @brief LLEvDescription of the LLEventPoll class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLEVENTPOLL_H
+#define LL_LLEVENTPOLL_H
+
+class LLHTTPNode;
+
+
+class LLEventPoll
+ ///< implements the viewer side of server-to-viewer pushed events.
+{
+public:
+ LLEventPoll(const std::string& pollURL, const LLHTTPNode& treeRoot);
+ /**< Start polling the URL.
+
+ The object will automatically responde to events
+ by calling handlers in the tree.
+ */
+
+
+ virtual ~LLEventPoll();
+ ///< will stop polling, cancelling any poll in progress.
+
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+#endif // LL_LLEVENTPOLL_H
diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp
new file mode 100644
index 0000000000..a1b7300ec9
--- /dev/null
+++ b/indra/newview/llface.cpp
@@ -0,0 +1,1939 @@
+/**
+ * @file llface.cpp
+ * @brief LLFace class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lldrawable.h" // lldrawable needs to be included before llface
+#include "llface.h"
+
+#include "llviewercontrol.h"
+#include "llvolume.h"
+#include "m3math.h"
+#include "v3color.h"
+
+#include "llagparray.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
+#include "llgl.h"
+#include "lllightconstants.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llvosky.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+#include "llagparray.inl"
+
+#define LL_MAX_INDICES_COUNT 1000000
+
+extern BOOL gPickFaces;
+
+BOOL LLFace::sSafeRenderSelect = TRUE; // FALSE
+
+#define DOTVEC(a,b) (a.mV[0]*b.mV[0] + a.mV[1]*b.mV[1] + a.mV[2]*b.mV[2])
+
+
+/*
+For each vertex, given:
+ B - binormal
+ T - tangent
+ N - normal
+ P - position
+
+The resulting texture coordinate <u,v> is:
+
+ u = 2(B dot P)
+ v = 2(T dot P)
+*/
+void planarProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //DONE!
+ LLVector3 binormal;
+ float d = vd.mNormal * LLVector3(1,0,0);
+ if (d >= 0.5f || d <= -0.5f)
+ {
+ binormal = LLVector3(0,1,0);
+ if (vd.mNormal.mV[0] < 0)
+ {
+ binormal = -binormal;
+ }
+ }
+ else
+ {
+ binormal = LLVector3(1,0,0);
+ if (vd.mNormal.mV[1] > 0)
+ {
+ binormal = -binormal;
+ }
+ }
+ LLVector3 tangent = binormal % vd.mNormal;
+
+ tc.mV[1] = -((tangent*vec)*2 - 0.5f);
+ tc.mV[0] = 1.0f+((binormal*vec)*2 - 0.5f);
+}
+
+void sphericalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //BROKEN
+ /*tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/3.14159f;
+
+ tc.mV[1] = acosf(vd.mNormal * LLVector3(0,0,1))/6.284f;
+ if (vd.mNormal.mV[1] > 0)
+ {
+ tc.mV[1] = 1.0f-tc.mV[1];
+ }*/
+}
+
+void cylindricalProjection(LLVector2 &tc, const LLVolumeFace::VertexData &vd, const LLVector3 &mCenter, const LLVector3& vec)
+{ //BROKEN
+ /*LLVector3 binormal;
+ float d = vd.mNormal * LLVector3(1,0,0);
+ if (d >= 0.5f || d <= -0.5f)
+ {
+ binormal = LLVector3(0,1,0);
+ }
+ else{
+ binormal = LLVector3(1,0,0);
+ }
+ LLVector3 tangent = binormal % vd.mNormal;
+
+ tc.mV[1] = -((tangent*vec)*2 - 0.5f);
+
+ tc.mV[0] = acosf(vd.mNormal * LLVector3(1,0,0))/6.284f;
+
+ if (vd.mNormal.mV[1] < 0)
+ {
+ tc.mV[0] = 1.0f-tc.mV[0];
+ }*/
+}
+
+////////////////////
+//
+// LLFace implementation
+//
+
+void LLFace::init(LLDrawable* drawablep, LLViewerObject* objp)
+{
+ mGeneration = DIRTY;
+ mState = GLOBAL;
+ mDrawPoolp = NULL;
+ mGeomIndex = -1;
+ mSkipRender = FALSE;
+ mNextFace = NULL;
+ // mCenterLocal
+ // mCenterAgent
+ mDistance = 0.f;
+
+ mPrimType = LLTriangles;
+ mGeomCount = 0;
+ mIndicesCount = 0;
+ mIndicesIndex = -1;
+ mTexture = NULL;
+ mTEOffset = -1;
+
+ mBackupMem = NULL;
+
+ setDrawable(drawablep);
+ mVObjp = objp;
+
+ mReferenceIndex = -1;
+ mAlphaFade = 0.f;
+
+ mFaceColor = LLColor4(1,0,0,1);
+}
+
+
+void LLFace::destroy()
+{
+ mDrawablep = NULL;
+ mVObjp = NULL;
+
+ if (mDrawPoolp)
+ {
+ mDrawPoolp->removeFace(this);
+ mDrawPoolp = NULL;
+ }
+
+ // Remove light and blocker list references
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+}
+
+
+// static
+void LLFace::initClass()
+{
+}
+
+void LLFace::setWorldMatrix(const LLMatrix4 &mat)
+{
+ llerrs << "Faces on this drawable are not independently modifiable\n" << llendl;
+}
+
+
+void LLFace::setDirty()
+{
+ mGeneration = DIRTY;
+}
+
+void LLFace::setPool(LLDrawPool* new_pool, LLViewerImage *texturep)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!new_pool)
+ {
+ llerrs << "Setting pool to null!" << llendl;
+ }
+
+ if (new_pool != mDrawPoolp)
+ {
+ // Remove from old pool
+ if (mDrawPoolp)
+ {
+ mDrawPoolp->removeFace(this);
+ mSkipRender = FALSE;
+ mNextFace = NULL;
+
+ // Invalidate geometry (will get rebuilt next frame)
+ setDirty();
+ if (mDrawablep)
+ {
+ gPipeline.markRebuild(mDrawablep, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+ if (isState(BACKLIST))
+ {
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+ }
+ mGeomIndex = -1;
+
+ // Add to new pool
+ if (new_pool)
+ {
+ new_pool->addFace(this);
+ }
+ mDrawPoolp = new_pool;
+
+ }
+ mTexture = texturep;
+}
+
+
+void LLFace::setTEOffset(const S32 te_offset)
+{
+ mTEOffset = te_offset;
+}
+
+
+void LLFace::setFaceColor(const LLColor4& color)
+{
+ mFaceColor = color;
+ setState(USE_FACE_COLOR);
+}
+
+void LLFace::unsetFaceColor()
+{
+ clearState(USE_FACE_COLOR);
+}
+
+void LLFace::setDrawable(LLDrawable *drawable)
+{
+ mDrawablep = drawable;
+ mXform = &drawable->mXform;
+}
+
+S32 LLFace::allocBackupMem()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ S32 size = 0;
+ size += mIndicesCount * 4;
+ size += mGeomCount * mDrawPoolp->getStride();
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_VERTEX_WEIGHTS];
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ size += mGeomCount * mDrawPoolp->sDataSizes[LLDrawPool::DATA_CLOTHING_WEIGHTS];
+ }
+
+ delete[] mBackupMem;
+ mBackupMem = new U8[size];
+ return size;
+}
+
+
+void LLFace::setSize(const S32 num_vertices, const S32 num_indices)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (getState() & SHARED_GEOM)
+ {
+ mGeomCount = num_vertices;
+ mIndicesCount = num_indices;
+ return; // Shared, don't allocate or do anything with memory
+ }
+ if (num_vertices != (S32)mGeomCount || num_indices != (S32)mIndicesCount)
+ {
+ setDirty();
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+
+ mGeomCount = num_vertices;
+ mIndicesCount = num_indices;
+ }
+
+}
+
+BOOL LLFace::reserveIfNeeded()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (getDirty())
+ {
+ if (isState(BACKLIST))
+ {
+ llwarns << "Reserve on backlisted object!" << llendl;
+ }
+
+ if (0 == mGeomCount)
+ {
+ //llwarns << "Reserving zero bytes for face!" << llendl;
+ mGeomCount = 0;
+ mIndicesCount = 0;
+ return FALSE;
+ }
+
+ mGeomIndex = mDrawPoolp->reserveGeom(mGeomCount);
+ // (reserveGeom() always returns a valid index)
+ mIndicesIndex = mDrawPoolp->reserveInd (mIndicesCount);
+ mGeneration = mDrawPoolp->mGeneration;
+ }
+
+ return TRUE;
+}
+
+void LLFace::unReserve()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!(isState(SHARED_GEOM)))
+ {
+ mGeomIndex = mDrawPoolp->unReserveGeom(mGeomIndex, mGeomCount);
+ mIndicesIndex = mDrawPoolp->unReserveInd(mIndicesIndex, mIndicesCount);
+ }
+}
+
+//============================================================================
+
+S32 LLFace::getGeometryAvatar(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &tex_coords,
+ LLStrider<F32> &vertex_weights,
+ LLStrider<LLVector4> &clothing_weights)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ llerrs << "No backup memory for backlist" << llendl;
+ }
+
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ tex_coords = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ clothing_weights = (LLVector4*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_CLOTHING_WEIGHTS]);
+ vertex_weights = (F32*)(mBackupMem + (4 * mIndicesCount) + (mGeomCount * mDrawPoolp->getStride()));
+ tex_coords.setStride( mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ binormals.setStride( mDrawPoolp->getStride());
+ clothing_weights.setStride( mDrawPoolp->getStride());
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider (vertices, mGeomIndex);
+ mDrawPoolp->getNormalStrider (normals, mGeomIndex);
+ mDrawPoolp->getBinormalStrider (binormals, mGeomIndex);
+ mDrawPoolp->getTexCoordStrider (tex_coords, mGeomIndex);
+ mDrawPoolp->getVertexWeightStrider(vertex_weights, mGeomIndex);
+ mDrawPoolp->getClothingWeightStrider(clothing_weights, mGeomIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometryTerrain(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLColor4U> &colors,
+ LLStrider<LLVector2> &texcoords0,
+ LLStrider<LLVector2> &texcoords1,
+ U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ colors = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
+ texcoords0= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ texcoords1= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
+ texcoords0.setStride(mDrawPoolp->getStride());
+ texcoords1.setStride(mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ colors.setStride( mDrawPoolp->getStride());
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ llinfos << "Get geometry failed!" << llendl;
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ mDrawPoolp->getColorStrider(colors, mGeomIndex);
+ mDrawPoolp->getTexCoordStrider(texcoords0, mGeomIndex, 0);
+ mDrawPoolp->getTexCoordStrider(texcoords1, mGeomIndex, 1);
+
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometry(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ tex_coords= (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ tex_coords.setStride(mDrawPoolp->getStride());
+ vertices.setStride( mDrawPoolp->getStride());
+ normals.setStride( mDrawPoolp->getStride());
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
+ {
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords, mGeomIndex);
+ }
+
+ indicesp =mDrawPoolp->getIndices (mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getGeometryColors(LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, LLStrider<LLColor4U> &colors,
+ U32 *&indicesp)
+{
+ S32 res = getGeometry(vertices, normals, tex_coords, indicesp);
+ if (res >= 0)
+ {
+ getColors(colors);
+ }
+ return res;
+}
+
+S32 LLFace::getGeometryMultiTexture(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &tex_coords0,
+ LLStrider<LLVector2> &tex_coords1,
+ U32 *&indicesp)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mGeomCount <= 0)
+ {
+ return -1;
+ }
+
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ tex_coords0 = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0]);
+ tex_coords0.setStride( mDrawPoolp->getStride() );
+ vertices.setStride( mDrawPoolp->getStride() );
+ normals.setStride( mDrawPoolp->getStride() );
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ binormals.setStride( mDrawPoolp->getStride() );
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
+ {
+ tex_coords1 = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS1]);
+ tex_coords1.setStride( mDrawPoolp->getStride() );
+ }
+ indicesp = (U32*)mBackupMem;
+
+ return 0;
+ }
+ else
+ {
+ if (!reserveIfNeeded())
+ {
+ return -1;
+ }
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_NORMALS_MASK)
+ {
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS0_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords0, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ mDrawPoolp->getBinormalStrider(binormals, mGeomIndex);
+ }
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_TEX_COORDS1_MASK)
+ {
+ mDrawPoolp->getTexCoordStrider(tex_coords1, mGeomIndex, 1);
+ }
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+
+ mDrawPoolp->setDirty();
+
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+void LLFace::updateCenterAgent()
+{
+ mCenterAgent = mCenterLocal * getRenderMatrix();
+}
+
+void LLFace::renderForSelect() const
+{
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return;
+ }
+ if (mVObjp->mGLName)
+ {
+ S32 name = mVObjp->mGLName;
+
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+#if 0 // FIXME: Postponing this fix until we have texcoord pick info...
+ if (mTEOffset != -1)
+ {
+ color.mV[VALPHA] = (U8)(getTextureEntry()->getColor().mV[VALPHA] * 255.f);
+ }
+#endif
+ glColor4ubv(color.mV);
+
+ if (mVObjp->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVOVolume *volp;
+ volp = (LLVOVolume *)(LLViewerObject*)mVObjp;
+ if (volp->getNumFaces() == 1 && !volp->getVolumeChanged())
+ {
+ // We need to special case the coalesced face model.
+ S32 num_vfs = volp->getVolume()->getNumFaces();
+ S32 offset = 0;
+ S32 i;
+
+ for (i = 0; i < num_vfs; i++)
+ {
+ if (gPickFaces)
+ {
+ // mask off high 4 bits (16 total possible faces)
+ color.mV[0] &= 0x0f;
+ color.mV[0] |= (i & 0x0f) << 4;
+ glColor4ubv(color.mV);
+ }
+ S32 count = volp->getVolume()->getVolumeFace(i).mIndices.size();
+ if (isState(GLOBAL))
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ glPopMatrix();
+ }
+ offset += count;
+ }
+ // We're done, return.
+ return;
+ }
+
+ // We don't have coalesced faces, do this the normal way.
+ }
+
+ if (gPickFaces && mTEOffset != -1)
+ {
+ // mask off high 4 bits (16 total possible faces)
+ color.mV[0] &= 0x0f;
+ color.mV[0] |= (mTEOffset & 0x0f) << 4;
+ glColor4ubv(color.mV);
+ }
+
+ if (mIndicesCount)
+ {
+ if (isState(GLOBAL))
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ glPopMatrix();
+ }
+ }
+ else if (mGeomCount > 0)
+ {
+ if (isState(GLOBAL))
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ glPopMatrix();
+ }
+ }
+ }
+}
+
+void LLFace::renderSelected(LLImageGL *imagep, const LLColor4& color, const S32 offset, const S32 count)
+{
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return;
+ }
+ if (mGeomCount > 0)
+ {
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ glColor4fv(color.mV);
+
+ LLViewerImage::bindTexture(imagep);
+ if (!isState(GLOBAL))
+ {
+ // Apply the proper transform for non-global objects.
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ }
+
+ if (sSafeRenderSelect)
+ {
+ glBegin(mPrimType);
+ if (count)
+ {
+ for (S32 i = offset; i < offset + count; i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glNormal3fv(normal.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ else
+ {
+ for (U32 i = 0; i < getIndicesCount(); i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glNormal3fv(normal.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ glEnd();
+
+ if( gSavedSettings.getBOOL("ShowTangentBasis") )
+ {
+ S32 start;
+ S32 end;
+ if (count)
+ {
+ start = offset;
+ end = offset + count;
+ }
+ else
+ {
+ start = 0;
+ end = getIndicesCount();
+ }
+
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(1, 1, 1, 1);
+ glBegin(GL_LINES);
+ for (S32 i = start; i < end; i++)
+ {
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ LLVector3 normal = mDrawPoolp->getNormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv( (vertex + normal * 0.1f).mV );
+ }
+ glEnd();
+
+ if (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK)
+ {
+ glColor4f(0, 1, 0, 1);
+ glBegin(GL_LINES);
+ for (S32 i = start; i < end; i++)
+ {
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ LLVector3 binormal = mDrawPoolp->getBinormal(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv( (vertex + binormal * 0.1f).mV );
+ }
+ glEnd();
+ }
+ }
+ }
+ else
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_NORMAL_ARRAY);
+ if (count)
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ llerrs << "Rendering non-indexed volume face!" << llendl;
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ else
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ }
+
+ if (!isState(GLOBAL))
+ {
+ // Restore the tranform for non-global objects
+ glPopMatrix();
+ }
+ }
+}
+
+void LLFace::renderSelectedUV(const S32 offset, const S32 count)
+{
+ LLUUID uv_img_red_blue_id(gViewerArt.getString("uv_test1.tga"));
+ LLUUID uv_img_green_id(gViewerArt.getString("uv_test2.tga"));
+ LLViewerImage* red_blue_imagep = gImageList.getImage(uv_img_red_blue_id, TRUE, TRUE);
+ LLViewerImage* green_imagep = gImageList.getImage(uv_img_green_id, TRUE, TRUE);
+
+ LLGLSObjectSelect object_select;
+ LLGLEnable blend(GL_BLEND);
+ LLGLEnable texture(GL_TEXTURE_2D);
+
+ if (!mDrawPoolp || !getIndicesCount() || getIndicesStart() < 0)
+ {
+ return;
+ }
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ static F32 bias = 0.f;
+ static F32 factor = -10.f;
+ if (mGeomCount > 0)
+ {
+ glColor4fv(LLColor4::white.mV);
+
+ if (pass == 0)
+ {
+ LLViewerImage::bindTexture(red_blue_imagep);
+ }
+ else // pass == 1
+ {
+ glBlendFunc(GL_ONE, GL_ONE);
+ LLViewerImage::bindTexture(green_imagep);
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glScalef(256.f, 256.f, 1.f);
+ }
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
+
+ if (!isState(GLOBAL))
+ {
+ // Apply the proper transform for non-global objects.
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ }
+
+ glEnable(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset(factor, bias);
+ if (sSafeRenderSelect)
+ {
+ glBegin(mPrimType);
+ if (count)
+ {
+ for (S32 i = offset; i < offset + count; i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ else
+ {
+ for (U32 i = 0; i < getIndicesCount(); i++)
+ {
+ LLVector2 tc = mDrawPoolp->getTexCoord(mDrawPoolp->getIndex(getIndicesStart() + i), 0);
+ glTexCoord2fv(tc.mV);
+ LLVector3 vertex = mDrawPoolp->getVertex(mDrawPoolp->getIndex(getIndicesStart() + i));
+ glVertex3fv(vertex.mV);
+ }
+ }
+ glEnd();
+ }
+ else
+ {
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnableClientState(GL_VERTEX_ARRAY);
+ llassert(mGeomIndex >= 0);
+ if (count)
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, count, GL_UNSIGNED_INT, getRawIndices() + offset);
+ }
+ else
+ {
+ llerrs << "Rendering non-indexed volume face!" << llendl;
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ else
+ {
+ if (mIndicesCount > 0)
+ {
+ glDrawElements(mPrimType, mIndicesCount, GL_UNSIGNED_INT, getRawIndices());
+ }
+ else
+ {
+ glDrawArrays(mPrimType, mGeomIndex, mGeomCount);
+ }
+ }
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ }
+
+ glDisable(GL_POLYGON_OFFSET_FILL);
+ if (!isState(GLOBAL))
+ {
+ // Restore the tranform for non-global objects
+ glPopMatrix();
+ }
+ if (pass == 1)
+ {
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glBlendFunc(GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA);
+ }
+ }
+ }
+
+ //restore blend func
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+}
+
+
+void LLFace::printDebugInfo() const
+{
+ LLDrawPool *poolp = getPool();
+ llinfos << "Object: " << getViewerObject()->mID << llendl;
+ if (getDrawable())
+ {
+ llinfos << "Type: " << LLPrimitive::pCodeToString(getDrawable()->getVObj()->getPCode()) << llendl;
+ }
+ if (getTexture())
+ {
+ llinfos << "Texture: " << getTexture() << " Comps: " << (U32)getTexture()->getComponents() << llendl;
+ }
+ else
+ {
+ llinfos << "No texture: " << llendl;
+ }
+
+ llinfos << "Face: " << this << llendl;
+ if (isState(BACKLIST))
+ {
+ llinfos << "Backlisted!" << llendl;
+ }
+ llinfos << "State: " << getState() << llendl;
+ llinfos << "Geom Index Data:" << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "GI: " << mGeomIndex << " Count:" << mGeomCount << llendl;
+ llinfos << "Face Index Data:" << llendl;
+ llinfos << "--------------------" << llendl;
+ llinfos << "II: " << mIndicesIndex << " Count:" << mIndicesCount << llendl;
+ llinfos << llendl;
+
+ poolp->printDebugInfo();
+
+ S32 pool_references = 0;
+ for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
+ iter != poolp->mReferences.end(); iter++)
+ {
+ LLFace *facep = *iter;
+ if (facep == this)
+ {
+ llinfos << "Pool reference: " << pool_references << llendl;
+ pool_references++;
+ }
+ }
+
+ if (pool_references != 1)
+ {
+ llinfos << "Incorrect number of pool references!" << llendl;
+ }
+
+
+ llinfos << "Indices:" << llendl;
+ llinfos << "--------------------" << llendl;
+
+ const U32 *indicesp = getRawIndices();
+ S32 indices_count = getIndicesCount();
+ S32 geom_start = getGeomStart();
+
+ for (S32 i = 0; i < indices_count; i++)
+ {
+ llinfos << i << ":" << indicesp[i] << ":" << (S32)(indicesp[i] - geom_start) << llendl;
+ }
+ llinfos << llendl;
+
+ llinfos << "Vertices:" << llendl;
+ llinfos << "--------------------" << llendl;
+ for (S32 i = 0; i < mGeomCount; i++)
+ {
+ llinfos << mGeomIndex + i << ":" << poolp->getVertex(mGeomIndex + i) << llendl;
+ }
+ llinfos << llendl;
+}
+
+S32 LLFace::backup()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (isState(BACKLIST))
+ {
+ llwarns << "Face is already backed up in LLFace::backup!" << llendl;
+ return mGeomCount;
+ }
+ if (mGeomIndex < 0)
+ {
+ // flexible objects can cause this
+ //llwarns << "No geometry to back-up" << llendl;
+ return 0;
+ }
+
+ S32 size = 0;
+ if (!mBackupMem)
+ {
+ size = allocBackupMem();
+ }
+ else
+ {
+ llerrs << "Memory already backed up!" << llendl;
+ }
+
+ // Need to flag this, because we can allocate a non-zero backup mem if we have indices and no geometry.
+
+ if (mGeomCount || mIndicesCount)
+ {
+ setState(BACKLIST);
+#if !RELEASE_FOR_DOWNLOAD
+ if (mGeomIndex < 0 || mIndicesIndex < 0)
+ {
+ llerrs << "LLFace::backup" << llendl;
+ }
+#endif
+
+ U32 *backup = (U32*)mBackupMem;
+ S32 stride = mDrawPoolp->getStride();
+
+ U32 *index = mDrawPoolp->getIndices(mIndicesIndex);
+ for (U32 i=0;i<mIndicesCount;i++)
+ {
+ *backup++ = index[i] - mGeomIndex;
+ index[i] = 0;
+ }
+
+ if (!mGeomCount)
+ {
+ return mGeomCount;
+ }
+ //
+ // Don't change the order of these unles you change the corresponding getGeometry calls that read out of
+ // backup memory, and also the other of the backup/restore pair!
+ //
+ memcpy(backup, (mDrawPoolp->mMemory.getMem() + mGeomIndex * stride), mGeomCount * stride);
+ backup += mGeomCount * stride / 4;
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ memcpy(backup, &mDrawPoolp->getClothingWeight(mGeomIndex), mGeomCount * sizeof(LLVector4));
+ backup += mGeomCount*4;
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ memcpy(backup, &mDrawPoolp->getVertexWeight(mGeomIndex), mGeomCount * sizeof(F32));
+ backup += mGeomCount;
+ }
+
+ llassert((U8*)backup - mBackupMem == size);
+
+ unReserve();
+ }
+ return mGeomCount;
+}
+
+void LLFace::restore()
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!isState(BACKLIST))
+ {
+ // flexible objects can cause this
+// printDebugInfo();
+// llwarns << "not backlisted for restore" << llendl;
+ return;
+ }
+ if (!mGeomCount || !mBackupMem)
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llwarns << "no backmem for restore" << llendl;
+ }
+
+ clearState(BACKLIST);
+ return;
+ }
+
+ S32 stride = mDrawPoolp->getStride();
+ mGeomIndex = mDrawPoolp->reserveGeom(mGeomCount);
+ mIndicesIndex = mDrawPoolp->reserveInd (mIndicesCount);
+ mGeneration = mDrawPoolp->mGeneration;
+
+ llassert(mGeomIndex >= 0);
+ llassert(mIndicesIndex >= 0);
+
+ U32 *backup = (U32*)mBackupMem;
+ U32 *index = mDrawPoolp->getIndices(mIndicesIndex);
+
+ for (U32 i=0;i<mIndicesCount;i++)
+ {
+ S32 ind = mGeomIndex + *backup;
+ index[i] = ind;
+ backup++;
+ }
+
+ mDrawPoolp->mMemory.copyToMem(mGeomIndex * stride, (U8 *)backup, mGeomCount * stride);
+ backup += mGeomCount * stride / 4;
+
+ //
+ // Don't change the order of these unles you change the corresponding getGeometry calls that read out of
+ // backup memory, and also the other of the backup/restore pair!
+ //
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_CLOTHING_WEIGHTS_MASK)
+ {
+ mDrawPoolp->mClothingWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
+ backup += mGeomCount*4;
+ }
+
+ if (mDrawPoolp->mDataMaskNIL & LLDrawPool::DATA_VERTEX_WEIGHTS_MASK)
+ {
+ mDrawPoolp->mWeights.copyToMem(mGeomIndex, (U8 *)backup, mGeomCount);
+ backup += mGeomCount;
+ }
+
+ delete[] mBackupMem;
+ mBackupMem = NULL;
+ clearState(BACKLIST);
+}
+
+// Transform the texture coordinates for this face.
+static void xform(LLVector2 &tex_coord, F32 cosAng, F32 sinAng, F32 offS, F32 offT, F32 magS, F32 magT)
+{
+ // New, good way
+ F32 s = tex_coord.mV[0];
+ F32 t = tex_coord.mV[1];
+
+ // Texture transforms are done about the center of the face.
+ s -= 0.5;
+ t -= 0.5;
+
+ // Handle rotation
+ F32 temp = s;
+ s = s * cosAng + t * sinAng;
+ t = -temp * sinAng + t * cosAng;
+
+ // Then scale
+ s *= magS;
+ t *= magT;
+
+ // Then offset
+ s += offS + 0.5f;
+ t += offT + 0.5f;
+
+ tex_coord.mV[0] = s;
+ tex_coord.mV[1] = t;
+}
+
+
+BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 f,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume)
+{
+ const LLVolumeFace &vf = volume.getVolumeFace(f);
+ S32 num_vertices = (S32)vf.mVertices.size();
+ S32 num_indices = (S32)vf.mIndices.size();
+ setSize(num_vertices, num_indices);
+
+ return genVolumeTriangles(volume, f, f, mat, inv_trans_mat, global_volume);
+}
+
+BOOL LLFace::genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, const BOOL global_volume)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (!mDrawablep)
+ {
+ return TRUE;
+ }
+
+ S32 index_offset;
+ F32 r, os, ot, ms, mt, cos_ang, sin_ang;
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector3> binormals;
+ LLStrider<LLVector2> tex_coords;
+ LLStrider<LLVector2> tex_coords2;
+ U32 *indicesp = NULL;
+
+ BOOL bump = mDrawPoolp && (mDrawPoolp->mDataMaskIL & LLDrawPool::DATA_BINORMALS_MASK);
+ BOOL is_static = mDrawablep->isStatic();
+ BOOL is_global = is_static;
+
+ if (bump)
+ {
+ index_offset = getGeometryMultiTexture(vertices, normals, binormals, tex_coords, tex_coords2, indicesp);
+ }
+ else
+ {
+ index_offset = getGeometry(vertices, normals, tex_coords, indicesp);
+ }
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ LLVector3 center_sum(0.f, 0.f, 0.f);
+
+ LLVector3 render_pos;
+
+ if (mDrawablep->isState(LLDrawable::REBUILD_TCOORD) &&
+ global_volume)
+ {
+ render_pos = mVObjp->getRenderPosition();
+ }
+
+ setPrimType(LLTriangles);
+
+ if (is_global)
+ {
+ setState(GLOBAL);
+ }
+ else
+ {
+ clearState(GLOBAL);
+ }
+
+ LLVector3 min, max;
+ LLVector2 tmin, tmax;
+
+ BOOL grab_first_vert = TRUE;
+ BOOL grab_first_tcoord = TRUE;
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume.getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+ S32 num_indices = (S32)vf.mIndices.size();
+ llassert(num_indices > 0);
+
+ U8 bump_code;
+ const LLTextureEntry *tep = mVObjp->getTE(vol_face);
+
+ if (tep)
+ {
+ bump_code = tep->getBumpmap();
+ r = tep->getRotation();
+ os = tep->mOffsetS;
+ ot = tep->mOffsetT;
+ ms = tep->mScaleS;
+ mt = tep->mScaleT;
+ cos_ang = cos(r);
+ sin_ang = sin(r);
+ }
+ else
+ {
+ bump_code = 0;
+ cos_ang = 1.0f;
+ sin_ang = 0.0f;
+ os = 0.0f;
+ ot = 0.0f;
+ ms = 1.0f;
+ mt = 1.0f;
+ }
+
+ if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ // VERTICES & NORMALS
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 v;
+ v = vf.mVertices[i].mPosition * mat_vert;
+
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+ *normals++ = normal;
+
+ *vertices++ = v;
+
+ if (grab_first_vert)
+ {
+ grab_first_vert = FALSE;
+ min = max = v;
+ }
+ else
+ {
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (v.mV[j] < min.mV[j])
+ {
+ min.mV[j] = v.mV[j];
+ }
+ if (v.mV[j] > max.mV[j])
+ {
+ max.mV[j] = v.mV[j];
+ }
+ }
+ }
+ }
+ for (S32 i = 0; i < num_indices; i++)
+ {
+ S32 index = vf.mIndices[i] + index_offset;
+ llassert(index >= 0 && (i != 1 || *(indicesp-1)!=(U32)index));
+ *indicesp++ = index;
+ }
+ }
+
+ if ((mDrawablep->isState(LLDrawable::REBUILD_TCOORD)) ||
+ ((bump || getTextureEntry()->getTexGen() != 0) && mDrawablep->isState(LLDrawable::REBUILD_VOLUME)))
+ {
+ // TEX COORDS AND BINORMALS
+ LLVector3 binormal_dir( -sin_ang, cos_ang, 0 );
+ LLVector3 bump_s_primary_light_ray;
+ LLVector3 bump_t_primary_light_ray;
+ if (bump)
+ {
+ F32 offset_multiple;
+ switch( bump_code )
+ {
+ case BE_NO_BUMP:
+ offset_multiple = 0.f;
+ break;
+ case BE_BRIGHTNESS:
+ case BE_DARKNESS:
+ if( mTexture.notNull() && mTexture->getHasGLTexture())
+ {
+ // Offset by approximately one texel
+ S32 cur_discard = mTexture->getDiscardLevel();
+ S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() );
+ max_size <<= cur_discard;
+ const F32 ARTIFICIAL_OFFSET = 2.f;
+ offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size;
+ }
+ else
+ {
+ offset_multiple = 1.f/256;
+ }
+ break;
+
+ default: // Standard bumpmap textures. Assumed to be 256x256
+ offset_multiple = 1.f / 256;
+ break;
+ }
+
+ F32 s_scale = 1.f;
+ F32 t_scale = 1.f;
+ if( tep )
+ {
+ tep->getScale( &s_scale, &t_scale );
+ }
+ LLVector3 sun_ray = gSky.getSunDirection();
+ LLVector3 moon_ray = gSky.getMoonDirection();
+ LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray;
+ bump_s_primary_light_ray = offset_multiple * s_scale * primary_light_ray;
+ bump_t_primary_light_ray = offset_multiple * t_scale * primary_light_ray;
+ }
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector2 tc = vf.mVertices[i].mTexCoord;
+
+ U8 texgen = getTextureEntry()->getTexGen();
+ if (texgen != LLTextureEntry::TEX_GEN_DEFAULT)
+ {
+
+ LLVector3 vec = vf.mVertices[i].mPosition; //-vf.mCenter;
+
+ if (global_volume)
+ {
+ vec -= render_pos;
+ }
+ else
+ {
+ vec.scaleVec(mVObjp->getScale());
+ }
+
+ switch (texgen)
+ {
+ case LLTextureEntry::TEX_GEN_PLANAR:
+ planarProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ case LLTextureEntry::TEX_GEN_SPHERICAL:
+ sphericalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ case LLTextureEntry::TEX_GEN_CYLINDRICAL:
+ cylindricalProjection(tc, vf.mVertices[i], vf.mCenter, vec);
+ break;
+ default:
+ break;
+ }
+ }
+ xform(tc, cos_ang, sin_ang, os, ot, ms, mt);
+ *tex_coords++ = tc;
+ if (grab_first_tcoord)
+ {
+ grab_first_tcoord = FALSE;
+ tmin = tmax = tc;
+ }
+ else
+ {
+ for (U32 j = 0; j < 2; j++)
+ {
+ if (tmin.mV[j] > tc.mV[j])
+ {
+ tmin.mV[j] = tc.mV[j];
+ }
+ else if (tmax.mV[j] < tc.mV[j])
+ {
+ tmax.mV[j] = tc.mV[j];
+ }
+ }
+ }
+ if (bump)
+ {
+ LLVector3 tangent = vf.mVertices[i].mBinormal % vf.mVertices[i].mNormal;
+ LLMatrix3 tangent_to_object;
+ tangent_to_object.setRows(tangent, vf.mVertices[i].mBinormal, vf.mVertices[i].mNormal);
+ LLVector3 binormal = binormal_dir * tangent_to_object;
+
+ if (!global_volume)
+ {
+ binormal = binormal * mat_normal;
+ }
+ binormal.normVec();
+ tangent.normVec();
+
+ tc += LLVector2( bump_s_primary_light_ray * tangent, bump_t_primary_light_ray * binormal );
+ *tex_coords2++ = tc;
+
+ *binormals++ = binormal;
+ }
+ }
+ }
+
+ index_offset += num_vertices;
+
+ center_sum += vf.mCenter * mat_vert;
+ }
+
+ center_sum /= (F32)(fend-fstart+1);
+
+ if (is_static)
+ {
+ mCenterAgent = center_sum;
+ mCenterLocal = mCenterAgent - mDrawablep->getPositionAgent();
+ }
+ else
+ {
+ mCenterLocal = center_sum;
+ updateCenterAgent();
+ }
+
+ if (!grab_first_vert && mDrawablep->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ mExtents[0] = min;
+ mExtents[1] = max;
+ }
+
+ if (!grab_first_tcoord && mDrawablep->isState(LLDrawable::REBUILD_TCOORD))
+ {
+ mTexExtents[0] = tmin;
+ mTexExtents[1] = tmax;
+ }
+
+ return TRUE;
+}
+
+BOOL LLFace::genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting)
+{
+ if (drawablep->isLight())
+ {
+ do_lighting = FALSE;
+ }
+
+ if (!((mDrawPoolp->mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK))
+ {
+ return FALSE;
+ }
+ if (mGeomIndex < 0)
+ {
+ return FALSE; // no geometry
+ }
+ LLStrider<LLColor4U> colorsp;
+ S32 idx = getColors(colorsp);
+ if (idx < 0)
+ {
+ return FALSE;
+ }
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume->getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+
+ if (isState(FULLBRIGHT) || !do_lighting)
+ {
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ (*colorsp++).setToBlack();
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 vertex = vf.mVertices[i].mPosition * mat_vert;
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+
+ LLColor4 color;
+ for (LLDrawable::drawable_set_t::const_iterator iter = drawablep->mLightSet.begin();
+ iter != drawablep->mLightSet.end(); ++iter)
+ {
+ LLDrawable* light_drawable = *iter;
+ LLVOVolume* light = light_drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(vertex, normal, light_color);
+ color += light_color;
+ }
+
+ color.mV[3] = 1.0f;
+
+ (*colorsp++).setVecScaleClamp(color);
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLFace::genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL use_shadow_factor)
+{
+ if (drawablep->isLight())
+ {
+ return FALSE;
+ }
+
+ if (!((mDrawPoolp->mDataMaskIL) & LLDrawPool::DATA_COLORS_MASK))
+ {
+ return FALSE;
+ }
+ if (mGeomIndex < 0)
+ {
+ return FALSE; // no geometry
+ }
+ LLStrider<LLColor4U> colorsp;
+ S32 idx = getColors(colorsp);
+ if (idx < 0)
+ {
+ return FALSE;
+ }
+
+ for (S32 vol_face = fstart; vol_face <= fend; vol_face++)
+ {
+ const LLVolumeFace &vf = volume->getVolumeFace(vol_face);
+ S32 num_vertices = (S32)vf.mVertices.size();
+
+ if (isState(FULLBRIGHT))
+ {
+ continue;
+ }
+
+ for (S32 i = 0; i < num_vertices; i++)
+ {
+ LLVector3 vertex = vf.mVertices[i].mPosition * mat_vert;
+ LLVector3 normal = vf.mVertices[i].mNormal * mat_normal;
+ normal.normVec();
+
+ U8 shadow;
+
+ if (use_shadow_factor)
+ {
+ shadow = (U8) (drawablep->getSunShadowFactor() * 255);
+ }
+ else
+ {
+ shadow = 255;
+ }
+
+ (*colorsp++).mV[3] = shadow;
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLFace::verify(const U32* indices_array) const
+{
+ BOOL ok = TRUE;
+ // First, check whether the face data fits within the pool's range.
+ if ((mGeomIndex < 0) || (mGeomIndex + mGeomCount) > (S32)getPool()->getVertexCount())
+ {
+ ok = FALSE;
+ llinfos << "Face not within pool range!" << llendl;
+ }
+
+ S32 indices_count = (S32)getIndicesCount();
+ S32 geom_start = getGeomStart();
+ S32 geom_count = mGeomCount;
+
+ if (!indices_count)
+ {
+ return TRUE;
+ }
+
+ if (indices_count > LL_MAX_INDICES_COUNT)
+ {
+ ok = FALSE;
+ llinfos << "Face has bogus indices count" << llendl;
+ }
+
+ const U32 *indicesp = indices_array ? indices_array + mIndicesIndex : getRawIndices();
+
+ for (S32 i = 0; i < indices_count; i++)
+ {
+ S32 delta = indicesp[i] - geom_start;
+ if (0 > delta)
+ {
+ llwarns << "Face index too low!" << llendl;
+ llinfos << "i:" << i << " Index:" << indicesp[i] << " GStart: " << geom_start << llendl;
+ ok = FALSE;
+ }
+ else if (delta >= geom_count)
+ {
+ llwarns << "Face index too high!" << llendl;
+ llinfos << "i:" << i << " Index:" << indicesp[i] << " GEnd: " << geom_start + geom_count << llendl;
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ printDebugInfo();
+ }
+ return ok;
+}
+
+
+void LLFace::setViewerObject(LLViewerObject* objp)
+{
+ mVObjp = objp;
+}
+
+void LLFace::enableLights() const
+{
+ if (isState(FULLBRIGHT|HUD_RENDER))
+ {
+ gPipeline.enableLightsFullbright(LLColor4::white);
+ }
+ else if (mDrawablep->isState(LLDrawable::LIGHTING_BUILT))
+ {
+ gPipeline.enableLightsStatic(1.f);
+ }
+ else
+ {
+ gPipeline.enableLightsDynamic(1.f);
+ }
+ if (isState(LIGHT))
+ {
+ const LLVOVolume* vovolume = (const LLVOVolume*)(mDrawablep->getVObj());
+ gPipeline.setAmbient(vovolume->getLightColor());
+ }
+}
+
+const LLColor4& LLFace::getRenderColor() const
+{
+ if (isState(USE_FACE_COLOR))
+ {
+ return mFaceColor; // Face Color
+ }
+ else
+ {
+ const LLTextureEntry* tep = getTextureEntry();
+ return (tep ? tep->getColor() : LLColor4::white);
+ }
+}
+
+void LLFace::renderSetColor() const
+{
+ if (!LLDrawPool::LLOverrideFaceColor::sOverrideFaceColor)
+ {
+ const LLColor4* color = &(getRenderColor());
+
+ if ((mDrawPoolp->mVertexShaderLevel > 0) && (mDrawPoolp->getMaterialAttribIndex() != 0))
+ {
+ glVertexAttrib4fvARB(mDrawPoolp->getMaterialAttribIndex(), color->mV);
+ }
+ else
+ {
+ glColor4fv(color->mV);
+ }
+ }
+}
+
+S32 LLFace::pushVertices(const U32* index_array) const
+{
+ U32 indices_count = mIndicesCount;
+ S32 ret = 0;
+#if ENABLE_FACE_LINKING
+ LLFace* next = mNextFace;
+#endif
+
+ if (mGeomCount < gGLManager.mGLMaxVertexRange && (S32) indices_count < gGLManager.mGLMaxIndexRange)
+ {
+ LLFace* current = (LLFace*) this;
+ S32 geom_count = mGeomCount;
+#if ENABLE_FACE_LINKING
+ while (current)
+ {
+ //chop up batch into implementation recommended sizes
+ while (next &&
+ (current == next ||
+ ((S32) (indices_count + next->mIndicesCount) < gGLManager.mGLMaxIndexRange &&
+ geom_count + next->mGeomCount < gGLManager.mGLMaxVertexRange)))
+ {
+ indices_count += next->mIndicesCount;
+ geom_count += next->mGeomCount;
+ next = next->mNextFace;
+ }
+#endif
+ if (indices_count)
+ {
+ glDrawRangeElements(mPrimType, current->mGeomIndex, current->mGeomIndex + geom_count, indices_count,
+ GL_UNSIGNED_INT, index_array + current->mIndicesIndex);
+ }
+ ret += (S32) indices_count;
+ indices_count = 0;
+ geom_count = 0;
+#if ENABLE_FACE_LINKING
+ current = next;
+ }
+#endif
+ }
+ else
+ {
+#if ENABLE_FACE_LINKING
+ while (next)
+ {
+ indices_count += next->mIndicesCount;
+ next = next->mNextFace;
+ }
+#endif
+ if (indices_count)
+ {
+ glDrawElements(mPrimType, indices_count, GL_UNSIGNED_INT, index_array + mIndicesIndex);
+ }
+ ret += (S32) indices_count;
+ }
+
+ return ret;
+
+}
+
+const LLMatrix4& LLFace::getRenderMatrix() const
+{
+ return mDrawablep->getRenderMatrix();
+}
+
+S32 LLFace::renderElements(const U32 *index_array) const
+{
+ S32 ret = 0;
+
+ if (isState(GLOBAL))
+ {
+ ret = pushVertices(index_array);
+ }
+ else
+ {
+ glPushMatrix();
+ glMultMatrixf((float*)getRenderMatrix().mMatrix);
+ ret = pushVertices(index_array);
+ glPopMatrix();
+ }
+
+ return ret;
+}
+
+S32 LLFace::renderIndexed(const U32 *index_array) const
+{
+ if (mSkipRender)
+ {
+ return 0;
+ }
+
+ if(mGeomIndex < 0 || mDrawablep.isNull())
+ {
+ return 0;
+ }
+
+ renderSetColor();
+ return renderElements(index_array);
+}
+
+//============================================================================
+// From llface.inl
+
+S32 LLFace::getVertices(LLStrider<LLVector3> &vertices)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ vertices.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ if (mGeomIndex >= 0) // flexible objects may not have geometry
+ {
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->setDirty();
+ }
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getColors(LLStrider<LLColor4U> &colors)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ llassert(mBackupMem);
+ colors = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_COLORS]);
+ colors.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getColorStrider(colors, mGeomIndex);
+ return mGeomIndex;
+ }
+}
+
+S32 LLFace::getIndices(U32* &indicesp)
+{
+ if (isState(BACKLIST))
+ {
+ indicesp = (U32*)mBackupMem;
+ return 0;
+ }
+ else
+ {
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+ llassert(mGeomIndex >= 0 && indicesp[0] != indicesp[1]);
+ return mGeomIndex;
+ }
+}
+
+void LLFace::link(LLFace* facep)
+{
+#if ENABLE_FACE_LINKING
+ mNextFace = facep;
+ facep->mSkipRender = TRUE;
+#endif
+}
+
+LLVector3 LLFace::getPositionAgent() const
+{
+ if (mDrawablep->isStatic())
+ {
+ return mCenterAgent;
+ }
+ else
+ {
+ return mCenterLocal * getRenderMatrix();
+ }
+}
diff --git a/indra/newview/llface.h b/indra/newview/llface.h
new file mode 100644
index 0000000000..0d017d6efe
--- /dev/null
+++ b/indra/newview/llface.h
@@ -0,0 +1,250 @@
+/**
+ * @file llface.h
+ * @brief LLFace class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFACE_H
+#define LL_LLFACE_H
+
+#include "llstrider.h"
+
+#include "v2math.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "m4math.h"
+#include "v4coloru.h"
+#include "llquaternion.h"
+#include "xform.h"
+#include "lldarrayptr.h"
+#include "llpagemem.h"
+#include "llstat.h"
+#include "lldrawable.h"
+
+#define ENABLE_FACE_LINKING 1 // Causes problems with snapshot rendering
+
+class LLDrawPool;
+class LLVolume;
+class LLViewerImage;
+class LLTextureEntry;
+class LLVertexProgram;
+class LLViewerImage;
+
+class LLFace
+{
+public:
+
+ enum EMasks
+ {
+ SHARED_GEOM = 0x0001,
+ LIGHT = 0x0002,
+ REBUILD = 0x0004,
+ GLOBAL = 0x0008,
+ VISIBLE = 0x0010,
+ BACKLIST = 0x0020,
+ INTERP = 0x0040,
+ FULLBRIGHT = 0x0080,
+ HUD_RENDER = 0x0100,
+ USE_FACE_COLOR = 0x0200,
+
+ POINT_SPRITE = 0x10000,
+ BOARD_SPRITE = 0x20000,
+ FIXED_SPRITE = 0x40000,
+ DEPTH_SPRITE = 0x80000
+ };
+
+ enum EDirty
+ {
+ DIRTY = -2
+ };
+
+ static void initClass();
+
+public:
+ LLFace(LLDrawable* drawablep, LLViewerObject* objp) { init(drawablep, objp); }
+ ~LLFace() { destroy(); }
+
+ const LLMatrix4& getWorldMatrix() const { return mVObjp->getWorldMatrix(mXform); }
+ const LLMatrix4& getRenderMatrix() const;
+ const U32 getIndicesCount() const { return mIndicesCount; };
+ const S32 getIndicesStart() const { return mIndicesIndex; };
+ const S32 getGeomCount() const { return mGeomCount; } // vertex count for this face
+ const S32 getGeomIndex() const { return mGeomIndex; } // index into draw pool
+ const U32 getGeomStart() const { return mGeomIndex; } // index into draw pool
+ LLViewerImage* getTexture() const { return mTexture; }
+ LLXformMatrix* getXform() const { return mXform; }
+ BOOL hasGeometry() const { return mGeomCount > 0; }
+ LLVector3 getPositionAgent() const;
+ void setPrimType(U32 primType) { mPrimType = primType; }
+ const U32 getPrimType() const { return mPrimType; }
+
+ U32 getState() const { return mState; }
+ void setState(U32 state) { mState |= state; }
+ void clearState(U32 state) { mState &= ~state; }
+ BOOL isState(U32 state) const { return ((mState & state) != 0); }
+
+ void bindTexture(S32 stage = 0) const { LLViewerImage::bindTexture(mTexture, stage); }
+
+ void enableLights() const;
+ void renderSetColor() const;
+ S32 renderElements(const U32 *index_array) const;
+ S32 renderIndexed (const U32 *index_array) const;
+ S32 pushVertices(const U32* index_array) const;
+
+ void setWorldMatrix(const LLMatrix4& mat);
+ const LLTextureEntry* getTextureEntry() const { return mVObjp->getTE(mTEOffset); }
+
+ LLDrawPool* getPool() const { return mDrawPoolp; }
+ S32 getStride() const { return mDrawPoolp->getStride(); }
+ const U32* getRawIndices() const { return &mDrawPoolp->mIndices[mIndicesIndex]; }
+ LLDrawable* getDrawable() const { return mDrawablep; }
+ LLViewerObject* getViewerObject() const { return mVObjp; }
+
+ void clearDirty() { mGeneration = mDrawPoolp->mGeneration; };
+
+ S32 backup();
+ void restore();
+
+ void setViewerObject(LLViewerObject* object);
+ void setPool(LLDrawPool *pool, LLViewerImage *texturep);
+ void setDrawable(LLDrawable *drawable);
+ void setTEOffset(const S32 te_offset);
+ S32 getTEOffset() { return mTEOffset; }
+
+ void setFaceColor(const LLColor4& color); // override material color
+ void unsetFaceColor(); // switch back to material color
+ const LLColor4& getFaceColor() const { return mFaceColor; }
+ const LLColor4& getRenderColor() const;
+
+ void unReserve(); // Set Removed from pool
+
+ BOOL reserveIfNeeded(); // Reserves data if dirty.
+
+ // For avatar
+ S32 getGeometryAvatar(
+ LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &texCoords,
+ LLStrider<F32> &vertex_weights,
+ LLStrider<LLVector4> &clothing_weights);
+
+ // For terrain
+ S32 getGeometryTerrain(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLColor4U> &colors,
+ LLStrider<LLVector2> &texCoords0,
+ LLStrider<LLVector2> &texCoords1,
+ U32* &indices);
+
+ // For volumes, etc.
+ S32 getGeometry(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &texCoords,
+ U32* &indices);
+
+ S32 getGeometryColors(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &texCoords,
+ LLStrider<LLColor4U> &colors,
+ U32* &indices);
+
+ S32 getGeometryMultiTexture(LLStrider<LLVector3> &vertices,
+ LLStrider<LLVector3> &normals,
+ LLStrider<LLVector3> &binormals,
+ LLStrider<LLVector2> &texCoords0,
+ LLStrider<LLVector2> &texCoords1,
+ U32* &indices);
+
+
+ S32 getVertices (LLStrider<LLVector3> &vertices);
+ S32 getColors (LLStrider<LLColor4U> &colors);
+ S32 getIndices (U32* &indices);
+
+ void setSize(const S32 numVertices, const S32 num_indices = 0);
+ BOOL getDirty() const { return (mGeneration != mDrawPoolp->mGeneration); }
+
+ BOOL genVolumeTriangles(const LLVolume &volume, S32 f,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
+ BOOL genVolumeTriangles(const LLVolume &volume, S32 fstart, S32 fend,
+ const LLMatrix4& mat, const LLMatrix3& inv_trans_mat, BOOL global_volume = FALSE);
+ BOOL genLighting(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL do_lighting);
+
+ BOOL genShadows(const LLVolume* volume, const LLDrawable* drawablep, S32 fstart, S32 fend,
+ const LLMatrix4& mat_vert, const LLMatrix3& mat_normal, BOOL use_shadow_factor);
+
+ void init(LLDrawable* drawablep, LLViewerObject* objp);
+ void destroy();
+ void update();
+
+ void updateCenterAgent(); // Update center when xform has changed.
+ void renderSelectedUV(const S32 offset = 0, const S32 count = 0);
+
+ void renderForSelect() const;
+ void renderSelected(LLImageGL *image, const LLColor4 &color, const S32 offset = 0, const S32 count = 0);
+
+ F32 getKey() const { return mDistance; }
+
+ S32 getGeneration() const { return mGeneration; }
+ S32 getReferenceIndex() const { return mReferenceIndex; }
+ void setReferenceIndex(const S32 index) { mReferenceIndex = index; }
+
+ BOOL verify(const U32* indices_array = NULL) const;
+ void printDebugInfo() const;
+
+ void link(LLFace* facep);
+
+protected:
+ S32 allocBackupMem(); // Allocate backup memory based on the draw pool information.
+ void setDirty();
+
+public:
+ LLVector3 mCenterLocal;
+ LLVector3 mCenterAgent;
+ LLVector3 mExtents[2];
+ LLVector2 mTexExtents[2];
+ F32 mDistance;
+ F32 mAlphaFade;
+ LLFace* mNextFace;
+ BOOL mSkipRender;
+
+protected:
+ S32 mGeneration;
+ U32 mState;
+ LLDrawPool* mDrawPoolp;
+ S32 mGeomIndex; // index into draw pool
+ LLColor4 mFaceColor; // overrides material color if state |= USE_FACE_COLOR
+
+ U32 mPrimType;
+ S32 mGeomCount; // vertex count for this face
+ U32 mIndicesCount;
+ S32 mIndicesIndex; // index into draw pool for indices (yeah, I know!)
+ LLXformMatrix* mXform;
+ LLPointer<LLViewerImage> mTexture;
+
+ U8 *mBackupMem;
+
+ LLPointer<LLDrawable> mDrawablep;
+ LLPointer<LLViewerObject> mVObjp;
+ S32 mTEOffset;
+
+ S32 mReferenceIndex;
+
+protected:
+ static BOOL sSafeRenderSelect;
+
+public:
+ struct CompareDistanceGreater
+ {
+ bool operator()(const LLFace* const& lhs, const LLFace* const& rhs)
+ {
+ return lhs->mDistance > rhs->mDistance; // farthest = first
+ }
+ };
+
+};
+
+#endif // LL_LLFACE_H
diff --git a/indra/newview/llface.inl b/indra/newview/llface.inl
new file mode 100644
index 0000000000..08beef5fd8
--- /dev/null
+++ b/indra/newview/llface.inl
@@ -0,0 +1,222 @@
+/**
+ * @file llface.inl
+ * @brief Inline functions for LLFace
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFACE_INL
+#define LL_LLFACE_INL
+
+#include "llglheaders.h"
+
+inline BOOL LLFace::getDirty() const
+{
+ return (mGeneration != mDrawPoolp->mGeneration);
+}
+
+inline void LLFace::clearDirty()
+{
+ mGeneration = mDrawPoolp->mGeneration;
+}
+
+inline const LLTextureEntry* LLFace::getTextureEntry() const
+{
+ return mVObjp->getTE(mTEOffset);
+}
+
+inline LLDrawPool* LLFace::getPool() const
+{
+ return mDrawPoolp;
+}
+
+inline S32 LLFace::getStride() const
+{
+ return mDrawPoolp->getStride();
+}
+
+inline LLDrawable* LLFace::getDrawable() const
+{
+ return mDrawablep;
+}
+
+inline LLViewerObject* LLFace::getViewerObject() const
+{
+ return mVObjp;
+}
+
+
+
+inline S32 LLFace::getVertices(LLStrider<LLVector3> &vertices)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ vertices = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_VERTICES]);
+ vertices.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getVertexStrider(vertices, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getNormals(LLStrider<LLVector3> &normals)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ normals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_NORMALS]);
+ normals.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getNormalStrider(normals, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getBinormals(LLStrider<LLVector3> &binormals)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ binormals = (LLVector3*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_BINORMALS]);
+ binormals.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getBinormalStrider(binormals, mGeomIndex);
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+
+inline S32 LLFace::getColors (LLStrider<LLColor4U> &colors)
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ LLColor4U *colorp = NULL;
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ colorp = (LLColor4U*)(mBackupMem + (4 * mIndicesCount) + (mGeomCount * mDrawPoolp->getStride()));
+ colors = colorp;
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ if (!mDrawPoolp->getColorStrider(colors, mGeomIndex))
+ {
+ printDebugInfo();
+ llerrs << "No color pointer for a color strider!" << llendl;
+ }
+ mDrawPoolp->setDirtyColors();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getTexCoords (LLStrider<LLVector2> &texCoords, S32 pass )
+{
+ if (!mGeomCount)
+ {
+ return -1;
+ }
+ if (isState(BACKLIST))
+ {
+ if (!mBackupMem)
+ {
+ printDebugInfo();
+ llerrs << "No backup memory for face" << llendl;
+ }
+ texCoords = (LLVector2*)(mBackupMem + (4 * mIndicesCount) + mDrawPoolp->mDataOffsets[LLDrawPool::DATA_TEX_COORDS0 + pass]);
+ texCoords.setStride( mDrawPoolp->getStride());
+ return 0;
+ }
+ else
+ {
+ llassert(mGeomIndex >= 0);
+ mDrawPoolp->getTexCoordStrider(texCoords, mGeomIndex, pass );
+ mDrawPoolp->setDirty();
+ return mGeomIndex;
+ }
+}
+
+inline S32 LLFace::getIndices (U32* &indicesp)
+{
+ if (isState(BACKLIST))
+ {
+ indicesp = (U32*)mBackupMem;
+ return 0;
+ }
+ else
+ {
+ indicesp = mDrawPoolp->getIndices(mIndicesIndex);
+ llassert(mGeomIndex >= 0);
+ return mGeomIndex;
+ }
+}
+
+inline const U32* LLFace::getRawIndices() const
+{
+ llassert(!isState(BACKLIST));
+
+ return &mDrawPoolp->mIndices[mIndicesIndex];
+}
+
+
+inline void LLFace::bindTexture(S32 stage) const
+{
+ if (mTexture)
+ {
+ mTexture->bindTexture(stage);
+ }
+ else
+ {
+ LLImageGL::unbindTexture(stage, GL_TEXTURE_2D);
+ }
+}
+
+#endif
diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp
new file mode 100644
index 0000000000..c8c1d0d878
--- /dev/null
+++ b/indra/newview/llfasttimerview.cpp
@@ -0,0 +1,1084 @@
+/**
+ * @file llfasttimerview.cpp
+ * @brief LLFastTimerView class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "indra_constants.h"
+#include "llfasttimerview.h"
+#include "llviewerwindow.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llmath.h"
+#include "llfontgl.h"
+
+#include "viewer.h"
+#include "llviewerimagelist.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llstat.h"
+
+#include "llfasttimer.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+static const S32 MAX_VISIBLE_HISTORY = 10;
+static const S32 LINE_GRAPH_HEIGHT = 240;
+
+struct ft_display_info {
+ int timer;
+ const char *desc;
+ LLColor4 *color;
+ S32 disabled; // initialized to 0
+ int level; // calculated based on desc
+ int parent; // calculated
+};
+
+static LLColor4 red0(0.5f, 0.0f, 0.0f, 1.0f);
+static LLColor4 green0(0.0f, 0.5f, 0.0f, 1.0f);
+static LLColor4 blue0(0.0f, 0.0f, 0.5f, 1.0f);
+static LLColor4 blue7(0.0f, 0.0f, 0.5f, 1.0f);
+
+static LLColor4 green7(0.6f, 1.0f, 0.4f, 1.0f);
+static LLColor4 green8(0.4f, 1.0f, 0.6f, 1.0f);
+static LLColor4 green9(0.6f, 1.0f, 0.6f, 1.0f);
+
+// green(6), blue, yellow, orange, pink(2), cyan
+// red (5) magenta (4)
+static struct ft_display_info ft_display_table[] =
+{
+ { LLFastTimer::FTM_FRAME, "Frame", &LLColor4::white, 0 },
+ { LLFastTimer::FTM_MESSAGES, " Messages", &LLColor4::grey1, 0 },
+ { LLFastTimer::FTM_SLEEP, " Sleep", &LLColor4::grey2, 0 },
+ { LLFastTimer::FTM_IDLE, " Idle", &blue0, 0 },
+ { LLFastTimer::FTM_INVENTORY, " Inventory Update", &LLColor4::purple6, 1 },
+ { LLFastTimer::FTM_AUTO_SELECT, " Open and Select", &LLColor4::red, 0 },
+ { LLFastTimer::FTM_FILTER, " Filter", &LLColor4::red2, 0 },
+ { LLFastTimer::FTM_ARRANGE, " Arrange", &LLColor4::red3, 0 },
+ { LLFastTimer::FTM_REFRESH, " Refresh", &LLColor4::red4, 0 },
+ { LLFastTimer::FTM_SORT, " Sort", &LLColor4::red5, 0 },
+ { LLFastTimer::FTM_RESET_DRAWORDER, " ResetDrawOrder", &LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_WORLD_UPDATE, " World Update", &LLColor4::blue1, 1 },
+ { LLFastTimer::FTM_UPDATE_MOVE, " Move Objects", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_OCTREE_BALANCE, " Octree Balance", &LLColor4::red3, 0 },
+ { LLFastTimer::FTM_CULL, " Object Cull", &LLColor4::blue2, 0 },
+ { LLFastTimer::FTM_CULL_REBOUND, " Rebound", &LLColor4::blue3, 0 },
+ { LLFastTimer::FTM_HUD_EFFECTS, " HUD Effects", &LLColor4::orange1, 0 },
+ { LLFastTimer::FTM_HUD_UPDATE, " HUD Update", &LLColor4::orange2, 0 },
+ { LLFastTimer::FTM_OCCLUSION, " Object Occlude",&LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_OCCLUSION_READBACK, " Occlusion Read",&LLColor4::red2, 0 },
+ { LLFastTimer::FTM_GEO_UPDATE, " Geo Update", &LLColor4::blue3, 0 },
+ { LLFastTimer::FTM_UPDATE_PRIMITIVES, " Volumes", &LLColor4::blue4, 0 },
+ { LLFastTimer::FTM_GEN_VOLUME, " Gen Volume", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_GEN_FLEX, " Flexible", &LLColor4::yellow4, 0 },
+ { LLFastTimer::FTM_GEN_TRIANGLES, " Triangles", &LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_GEO_LIGHT, " Lighting", &LLColor4::yellow1, 0 },
+ { LLFastTimer::FTM_GEO_SHADOW, " Shadow", &LLColor4::black, 0 },
+ { LLFastTimer::FTM_UPDATE_PARTICLES, " Particles", &LLColor4::blue5, 0 },
+ { LLFastTimer::FTM_GEO_RESERVE, " Reserve", &LLColor4::blue6, 0 },
+ { LLFastTimer::FTM_UPDATE_LIGHTS, " Lights", &LLColor4::yellow2, 0 },
+ { LLFastTimer::FTM_UPDATE_SKY, " Sky Update", &LLColor4::cyan1, 0 },
+ { LLFastTimer::FTM_OBJECTLIST_UPDATE, " Object Update", &LLColor4::purple1, 1 },
+ { LLFastTimer::FTM_AVATAR_UPDATE, " Avatars", &LLColor4::purple2, 0 },
+ { LLFastTimer::FTM_JOINT_UPDATE, " Joints", &LLColor4::purple3, 0 },
+ { LLFastTimer::FTM_ATTACHMENT_UPDATE, " Attachments", &LLColor4::purple4, 0 },
+ { LLFastTimer::FTM_UPDATE_ANIMATION, " Animation", &LLColor4::purple5, 0 },
+ { LLFastTimer::FTM_FLEXIBLE_UPDATE, " Flex Update", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_LOD_UPDATE, " LOD Update", &LLColor4::magenta1, 0 },
+ { LLFastTimer::FTM_REGION_UPDATE, " Region Update", &LLColor4::cyan2, 0 },
+ { LLFastTimer::FTM_NETWORK, " Network", &LLColor4::orange1, 1 },
+ { LLFastTimer::FTM_IDLE_NETWORK, " Decode Msgs", &LLColor4::orange2, 0 },
+ { LLFastTimer::FTM_PROCESS_MESSAGES, " Process Msgs", &LLColor4::orange3, 0 },
+ { LLFastTimer::FTM_PROCESS_OBJECTS, " Object Updates",&LLColor4::orange4, 0 },
+ { LLFastTimer::FTM_CREATE_OBJECT, " Create Obj", &LLColor4::orange5, 0 },
+// { LLFastTimer::FTM_LOAD_AVATAR, " Load Avatar", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_PROCESS_IMAGES, " Image Updates",&LLColor4::orange6, 0 },
+ { LLFastTimer::FTM_PIPELINE, " Pipeline", &LLColor4::magenta4, 0 },
+ { LLFastTimer::FTM_CLEANUP, " Cleanup", &LLColor4::cyan3, 0 },
+ { LLFastTimer::FTM_AUDIO_UPDATE, " Audio Update", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_IMAGE_UPDATE, " Image Update", &LLColor4::yellow4, 1 },
+ { LLFastTimer::FTM_IMAGE_CREATE, " Image CreateGL",&LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_IMAGE_DECODE, " Image Decode", &LLColor4::yellow6, 0 },
+ { LLFastTimer::FTM_VFILE_WAIT, " VFile Wait", &LLColor4::cyan6, 0 },
+// { LLFastTimer::FTM_IDLE_CB, " Callbacks", &LLColor4::pink1, 0 },
+ { LLFastTimer::FTM_RENDER, " Render", &green0, 0 },
+ { LLFastTimer::FTM_REBUILD, " Rebuild", &LLColor4::green1, 1 },
+ { LLFastTimer::FTM_STATESORT, " State Sort", &LLColor4::orange1, 1 },
+ { LLFastTimer::FTM_RENDER_GEOMETRY, " Geometry", &LLColor4::green2, 1 },
+ { LLFastTimer::FTM_POOLS, " Pools", &LLColor4::green3, 0 },
+ { LLFastTimer::FTM_POOLRENDER, " RenderPool", &LLColor4::green4, 0 },
+ { LLFastTimer::FTM_RENDER_TERRAIN, " Terrain", &LLColor4::green6, 0 },
+ { LLFastTimer::FTM_RENDER_CHARACTERS, " Avatars", &LLColor4::yellow1, 0 },
+ { LLFastTimer::FTM_RENDER_SIMPLE, " Simple", &LLColor4::yellow2, 0 },
+ { LLFastTimer::FTM_RENDER_SHINY, " Shiny", &LLColor4::yellow3, 0 },
+ { LLFastTimer::FTM_RENDER_BUMP, " Bump", &LLColor4::yellow4, 0 },
+ { LLFastTimer::FTM_RENDER_TREES, " Trees", &LLColor4::yellow8, 0 },
+ { LLFastTimer::FTM_RENDER_OCCLUSION, " Occlusion", &LLColor4::red1, 0 },
+ { LLFastTimer::FTM_RENDER_CLOUDS, " Clouds", &LLColor4::yellow5, 0 },
+ { LLFastTimer::FTM_RENDER_ALPHA, " Alpha", &LLColor4::yellow6, 0 },
+ { LLFastTimer::FTM_RENDER_HUD, " HUD", &LLColor4::yellow7, 0 },
+ { LLFastTimer::FTM_RENDER_WATER, " Water", &LLColor4::yellow9, 0 },
+ { LLFastTimer::FTM_RENDER_UI, " UI", &LLColor4::cyan4, 1 },
+ { LLFastTimer::FTM_RENDER_TIMER, " Timers", &LLColor4::cyan5, 1, 0 },
+// { LLFastTimer::FTM_RENDER_FONTS, " Fonts", &LLColor4::pink1, 0 },
+// { LLFastTimer::FTM_UPDATE_TEXTURES, " Textures", &LLColor4::pink2, 0 },
+ { LLFastTimer::FTM_SWAP, " Swap", &LLColor4::pink1, 0 },
+
+// { LLFastTimer::FTM_TEMP1, " Temp1", &LLColor4::red1, 0 },
+// { LLFastTimer::FTM_TEMP2, " Temp2", &LLColor4::magenta1, 0 },
+// { LLFastTimer::FTM_TEMP3, " Temp3", &LLColor4::red2, 0 },
+// { LLFastTimer::FTM_TEMP4, " Temp4", &LLColor4::magenta2, 0 },
+// { LLFastTimer::FTM_TEMP5, " Temp5", &LLColor4::red3, 0 },
+// { LLFastTimer::FTM_TEMP6, " Temp6", &LLColor4::magenta3, 0 },
+// { LLFastTimer::FTM_TEMP7, " Temp7", &LLColor4::red4, 0 },
+// { LLFastTimer::FTM_TEMP8, " Temp8", &LLColor4::magenta4, 0 },
+
+ { LLFastTimer::FTM_OTHER, " Other", &red0 }
+};
+static int ft_display_didcalc = 0;
+static const int FTV_DISPLAY_NUM = (sizeof(ft_display_table)/sizeof(ft_display_table[0]));
+
+S32 ft_display_idx[FTV_DISPLAY_NUM]; // line of table entry for display purposes (for collapse)
+
+LLFastTimerView::LLFastTimerView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, TRUE)
+{
+ setVisible(FALSE);
+ mDisplayMode = 0;
+ mAvgCountTotal = 0;
+ mMaxCountTotal = 0;
+ mDisplayCenter = 1;
+ mDisplayCalls = 0;
+ mScrollIndex = 0;
+ mHoverIndex = -1;
+ mHoverBarIndex = -1;
+ mBarStart = new S32[(MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM];
+ memset(mBarStart, 0, (MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM * sizeof(S32));
+ mBarEnd = new S32[(MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM];
+ memset(mBarEnd, 0, (MAX_VISIBLE_HISTORY + 1) * FTV_DISPLAY_NUM * sizeof(S32));
+ mSubtractHidden = 0;
+ mPrintStats = -1;
+
+ // One-time setup
+ if (!ft_display_didcalc)
+ {
+ int pidx[FTV_DISPLAY_NUM];
+ int pdisabled[FTV_DISPLAY_NUM];
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ int level = 0;
+ const char *text = ft_display_table[i].desc;
+ while(text[0] == ' ')
+ {
+ text++;
+ level++;
+ }
+ llassert(level < FTV_DISPLAY_NUM);
+ ft_display_table[i].desc = text;
+ ft_display_table[i].level = level;
+ if (level > 0)
+ {
+ ft_display_table[i].parent = pidx[level-1];
+ if (pdisabled[level-1])
+ {
+ ft_display_table[i].disabled = 3;
+ }
+ }
+ else
+ {
+ ft_display_table[i].parent = -1;
+ }
+ ft_display_idx[i] = i;
+ pidx[level] = i;
+ pdisabled[level] = ft_display_table[i].disabled;
+ }
+ ft_display_didcalc = 1;
+ }
+}
+
+LLFastTimerView::~LLFastTimerView()
+{
+ delete[] mBarStart;
+ delete[] mBarEnd;
+}
+
+EWidgetType LLFastTimerView::getWidgetType() const
+{
+ return WIDGET_TYPE_FAST_TIMER_VIEW;
+}
+
+LLString LLFastTimerView::getWidgetTag() const
+{
+ return LL_FAST_TIMER_VIEW_TAG;
+}
+
+BOOL LLFastTimerView::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mBarRect.pointInRect(x, y))
+ {
+ S32 bar_idx = MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight());
+ bar_idx = llclamp(bar_idx, 0, MAX_VISIBLE_HISTORY);
+ mPrintStats = bar_idx;
+// return TRUE; // for now, pass all mouse events through
+ }
+ return FALSE;
+}
+
+S32 LLFastTimerView::getLegendIndex(S32 y)
+{
+ S32 idx = (mRect.getHeight() - y) / ((S32) LLFontGL::sMonospace->getLineHeight()+2) - 5;
+ if (idx >= 0 && idx < FTV_DISPLAY_NUM)
+ {
+ return ft_display_idx[idx];
+ }
+
+ return -1;
+}
+
+BOOL LLFastTimerView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (x < mBarRect.mLeft)
+ {
+ S32 legend_index = getLegendIndex(y);
+ if (legend_index >= 0 && legend_index < FTV_DISPLAY_NUM)
+ {
+ S32 disabled = ft_display_table[legend_index].disabled;
+ if (++disabled > 2)
+ disabled = 0;
+ ft_display_table[legend_index].disabled = disabled;
+ S32 level = ft_display_table[legend_index].level;
+
+ // propagate enable/disable to all children
+ legend_index++;
+ while (legend_index < FTV_DISPLAY_NUM && ft_display_table[legend_index].level > level)
+ {
+ ft_display_table[legend_index].disabled = disabled ? 3 : 0;
+ legend_index++;
+ }
+ }
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ if (++mDisplayMode > 3)
+ mDisplayMode = 0;
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ if (++mDisplayCenter > 2)
+ mDisplayCenter = 0;
+ }
+ else
+ {
+ // pause/unpause
+ LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
+ // reset scroll to bottom when unpausing
+ if (!LLFastTimer::sPauseHistory)
+ {
+ mScrollIndex = 0;
+ }
+ }
+ // RN: for now, pass all mouse events through
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+
+BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y))
+ {
+ mHoverIndex = -1;
+ mHoverBarIndex = MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight());
+ if (mHoverBarIndex == 0)
+ {
+ return TRUE;
+ }
+ else if (mHoverBarIndex == -1)
+ {
+ mHoverBarIndex = 0;
+ }
+ for (S32 i = 0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (x > mBarStart[mHoverBarIndex * FTV_DISPLAY_NUM + i] &&
+ x < mBarEnd[mHoverBarIndex * FTV_DISPLAY_NUM + i] &&
+ ft_display_table[i].disabled <= 1)
+ {
+ mHoverIndex = i;
+ }
+ }
+ }
+ else if (x < mBarRect.mLeft)
+ {
+ S32 legend_index = getLegendIndex(y);
+ if (legend_index >= 0 && legend_index < FTV_DISPLAY_NUM)
+ {
+ mHoverIndex = legend_index;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getVisible() && pointInView(x, y))
+ {
+ LLFastTimer::sPauseHistory = TRUE;
+ mScrollIndex = llclamp(mScrollIndex - clicks,
+ 0, llmin(LLFastTimer::sLastFrameIndex, (S32)LLFastTimer::FTM_HISTORY_NUM-MAX_VISIBLE_HISTORY));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLFastTimerView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ // Otherwise space key gets eaten from the rest of the UI. JC
+ if (getVisible())
+ {
+ switch (key)
+ {
+ case '=':
+ mDisplayCalls = !mDisplayCalls;
+ return TRUE;
+ case '-':
+ mSubtractHidden = !mSubtractHidden;
+ return TRUE;
+ case ' ':
+ // pause/unpause
+ LLFastTimer::sPauseHistory = !LLFastTimer::sPauseHistory;
+ // reset scroll to bottom when unpausing
+ if (!LLFastTimer::sPauseHistory)
+ {
+ mScrollIndex = 0;
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+void LLFastTimerView::draw()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_TIMER);
+
+ std::string tdesc;
+
+ F64 clock_freq = (F64)LLFastTimer::countsPerSecond();
+ F64 iclock_freq = 1000.0 / clock_freq;
+
+ S32 margin = 10;
+ S32 height = (S32) (gViewerWindow->getVirtualWindowRect().getHeight()*0.75f);
+ S32 width = (S32) (gViewerWindow->getVirtualWindowRect().getWidth() * 0.75f);
+
+ mRect.setLeftTopAndSize(mRect.mLeft, mRect.mTop, width, height);
+
+ S32 left, top, right, bottom;
+ S32 x, y, barw, barh, dx, dy;
+ S32 texth, textw;
+ LLViewerImage* box_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ // Make sure all timers are accounted for
+ // Set 'FTM_OTHER' to unaccounted ticks last frame
+ {
+ S32 display_timer[LLFastTimer::FTM_NUM_TYPES];
+ S32 hidx = LLFastTimer::sLastFrameIndex % LLFastTimer::FTM_HISTORY_NUM;
+ for (S32 i=0; i < LLFastTimer::FTM_NUM_TYPES; i++)
+ {
+ display_timer[i] = 0;
+ }
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ S32 tidx = ft_display_table[i].timer;
+ display_timer[tidx] = 1;
+ }
+ LLFastTimer::sCountHistory[hidx][LLFastTimer::FTM_OTHER] = 0;
+ LLFastTimer::sCallHistory[hidx][LLFastTimer::FTM_OTHER] = 0;
+ for (S32 tidx = 0; tidx < LLFastTimer::FTM_NUM_TYPES; tidx++)
+ {
+ U64 counts = LLFastTimer::sCountHistory[hidx][tidx];
+ if (counts > 0 && display_timer[tidx] == 0)
+ {
+ LLFastTimer::sCountHistory[hidx][LLFastTimer::FTM_OTHER] += counts;
+ LLFastTimer::sCallHistory[hidx][LLFastTimer::FTM_OTHER] += 1;
+ }
+ }
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] = 0;
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] = 0;
+ for (S32 h = 0; h < LLFastTimer::FTM_HISTORY_NUM; h++)
+ {
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] += LLFastTimer::sCountHistory[h][LLFastTimer::FTM_OTHER];
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] += LLFastTimer::sCallHistory[h][LLFastTimer::FTM_OTHER];
+ }
+ LLFastTimer::sCountAverage[LLFastTimer::FTM_OTHER] /= LLFastTimer::FTM_HISTORY_NUM;
+ LLFastTimer::sCallAverage[LLFastTimer::FTM_OTHER] /= LLFastTimer::FTM_HISTORY_NUM;
+ }
+
+ // Draw the window background
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+ }
+
+ S32 xleft = margin;
+ S32 ytop = margin;
+
+ // Draw some help
+ {
+
+ x = xleft;
+ y = height - ytop;
+ texth = (S32)LLFontGL::sMonospace->getLineHeight();
+
+ char modedesc[][32] = {
+ "2 x Average ",
+ "Max ",
+ "Recent Max ",
+ "100 ms "
+ };
+ char centerdesc[][32] = {
+ "Left ",
+ "Centered ",
+ "Ordered "
+ };
+
+ tdesc = llformat("Full bar = %s [Click to pause/reset] [SHIFT-Click to toggle]",modedesc[mDisplayMode]);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+ textw = LLFontGL::sMonospace->getWidth(tdesc);
+
+ x = xleft, y -= (texth + 2);
+ tdesc = llformat("Justification = %s [CTRL-Click to toggle]",centerdesc[mDisplayCenter]);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ y -= (texth + 2);
+
+ LLFontGL::sMonospace->renderUTF8("[Right-click to log selected] [= to toggle counts] [- to subtract hidden]",
+ 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ y -= (texth + 2);
+ }
+
+ // Calc the total ticks
+ S32 histmax = llmin(LLFastTimer::sLastFrameIndex+1, MAX_VISIBLE_HISTORY);
+ U64 ticks_sum[LLFastTimer::FTM_HISTORY_NUM+1][FTV_DISPLAY_NUM];
+ for (S32 j=-1; j<LLFastTimer::FTM_HISTORY_NUM; j++)
+ {
+ S32 hidx;
+ if (j >= 0)
+ hidx = (LLFastTimer::sLastFrameIndex+j) % LLFastTimer::FTM_HISTORY_NUM;
+ else
+ hidx = -1;
+
+ // calculate tick info by adding child ticks to parents
+ for (S32 i=0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ // Get ticks
+ S32 tidx = ft_display_table[i].timer;
+ if (hidx >= 0)
+ ticks_sum[j+1][i] = LLFastTimer::sCountHistory[hidx][tidx];
+ else
+ ticks_sum[j+1][i] = LLFastTimer::sCountAverage[tidx];
+ S32 pidx = ft_display_table[i].parent;
+ // Add ticks to parents
+ while (pidx >= 0)
+ {
+ ticks_sum[j+1][pidx] += ticks_sum[j+1][i];
+ pidx = ft_display_table[pidx].parent;
+ }
+ }
+ }
+
+ // Draw the legend
+
+ S32 legendwidth = 0;
+ xleft = margin;
+ ytop = y;
+
+ y -= (texth + 2);
+
+ S32 cur_line = 0;
+ S32 display_line[FTV_DISPLAY_NUM];
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ S32 disabled = ft_display_table[i].disabled;
+ if (disabled == 3)
+ {
+ continue; // skip row
+ }
+ display_line[i] = cur_line;
+ ft_display_idx[cur_line] = i;
+ cur_line++;
+ S32 level = ft_display_table[i].level;
+ S32 parent = ft_display_table[i].parent;
+
+ x = xleft;
+
+ left = x; right = x + texth;
+ top = y; bottom = y - texth;
+ S32 scale_offset = 0;
+ if (i == mHoverIndex)
+ {
+ scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 2.f);
+ }
+ gl_rect_2d(left - scale_offset, top + scale_offset, right + scale_offset, bottom - scale_offset, *ft_display_table[i].color);
+
+ int tidx = ft_display_table[i].timer;
+ F32 ms = 0;
+ S32 calls = 0;
+ if (mHoverBarIndex > 0 && mHoverIndex >= 0)
+ {
+ S32 hidx = (LLFastTimer::sLastFrameIndex + (mHoverBarIndex - 1) - mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ S32 bidx = LLFastTimer::FTM_HISTORY_NUM - mScrollIndex - mHoverBarIndex;
+ U64 ticks = ticks_sum[bidx+1][i]; // : LLFastTimer::sCountHistory[hidx][tidx];
+ ms = (F32)((F64)ticks * iclock_freq);
+ calls = (S32)LLFastTimer::sCallHistory[hidx][tidx];
+ }
+ else
+ {
+ U64 ticks = disabled >= 1 ? ticks_sum[0][i] : LLFastTimer::sCountAverage[tidx];
+ ms = (F32)((F64)ticks * iclock_freq);
+ calls = (S32)LLFastTimer::sCallAverage[tidx];
+ }
+ if (mDisplayCalls)
+ {
+ tdesc = llformat("%s (%d)",ft_display_table[i].desc,calls);
+ }
+ else
+ {
+ tdesc = llformat("%s [%.1f]",ft_display_table[i].desc,ms);
+ }
+ dx = (texth+4) + level*8;
+
+ LLColor4 color = disabled > 1 ? LLColor4::grey : LLColor4::white;
+ if (level > 0)
+ {
+ S32 line_start_y = (top + bottom) / 2;
+ S32 line_end_y = line_start_y + ((texth + 2) * (display_line[i] - display_line[parent])) - (texth / 2);
+ gl_line_2d(x + dx - 8, line_start_y, x + dx, line_start_y, color);
+ S32 line_x = x + (texth + 4) + ((level - 1) * 8);
+ gl_line_2d(line_x, line_start_y, line_x, line_end_y, color);
+ if (disabled == 1)
+ {
+ gl_line_2d(line_x+4, line_start_y-3, line_x+4, line_start_y+4, color);
+ }
+ }
+
+ x += dx;
+ BOOL is_child_of_hover_item = (i == mHoverIndex);
+ S32 next_parent = ft_display_table[i].parent;
+ while(!is_child_of_hover_item && next_parent >= 0)
+ {
+ is_child_of_hover_item = (mHoverIndex == next_parent);
+ next_parent = ft_display_table[next_parent].parent;
+ }
+
+ if (is_child_of_hover_item)
+ {
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, color, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::BOLD);
+ }
+ else
+ {
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, color, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+ y -= (texth + 2);
+
+ textw = dx + LLFontGL::sMonospace->getWidth(ft_display_table[i].desc) + 40;
+ if (textw > legendwidth)
+ legendwidth = textw;
+ }
+ for (S32 i=cur_line; i<FTV_DISPLAY_NUM; i++)
+ {
+ ft_display_idx[i] = -1;
+ }
+ xleft += legendwidth + 8;
+ // ytop = ytop;
+
+ // update rectangle that includes timer bars
+ mBarRect.mLeft = xleft;
+ mBarRect.mRight = mRect.mRight - xleft;
+ mBarRect.mTop = ytop - ((S32)LLFontGL::sMonospace->getLineHeight() + 4);
+ mBarRect.mBottom = margin + LINE_GRAPH_HEIGHT;
+
+ y = ytop;
+ barh = (ytop - margin - LINE_GRAPH_HEIGHT) / (MAX_VISIBLE_HISTORY + 2);
+ dy = barh>>2; // spacing between bars
+ if (dy < 1) dy = 1;
+ barh -= dy;
+ barw = width - xleft - margin;
+
+ // Draw the history bars
+ if (LLFastTimer::sLastFrameIndex >= 0)
+ {
+ U64 totalticks;
+ if (!LLFastTimer::sPauseHistory)
+ {
+ U64 ticks = 0;
+ int hidx = (LLFastTimer::sLastFrameIndex - mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ int tidx = ft_display_table[i].timer;
+ ticks += LLFastTimer::sCountHistory[hidx][tidx];
+ }
+ if (LLFastTimer::sCurFrameIndex >= 10)
+ {
+ U64 framec = LLFastTimer::sCurFrameIndex;
+ U64 avg = (U64)mAvgCountTotal;
+ mAvgCountTotal = (avg*framec + ticks) / (framec + 1);
+ if (ticks > mMaxCountTotal)
+ {
+ mMaxCountTotal = ticks;
+ }
+ }
+#if 1
+ if (ticks < mAvgCountTotal/100 || ticks > mAvgCountTotal*100)
+ LLFastTimer::sResetHistory = 1;
+#endif
+ if (LLFastTimer::sCurFrameIndex < 10 || LLFastTimer::sResetHistory)
+ {
+ mAvgCountTotal = ticks;
+ mMaxCountTotal = ticks;
+ }
+ }
+
+ if (mDisplayMode == 0)
+ {
+ totalticks = mAvgCountTotal*2;
+ }
+ else if (mDisplayMode == 1)
+ {
+ totalticks = mMaxCountTotal;
+ }
+ else if (mDisplayMode == 2)
+ {
+ // Calculate the max total ticks for the current history
+ totalticks = 0;
+ for (S32 j=0; j<histmax; j++)
+ {
+ U64 ticks = 0;
+ for (S32 i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (mSubtractHidden && ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+ int tidx = ft_display_table[i].timer;
+ ticks += LLFastTimer::sCountHistory[j][tidx];
+ }
+ if (ticks > totalticks)
+ totalticks = ticks;
+ }
+ }
+ else
+ {
+ totalticks = (U64)(clock_freq * .1); // 100 ms
+ }
+
+ // Draw MS ticks
+ {
+ U32 ms = (U32)((F64)totalticks * iclock_freq) ;
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.25f);
+ x = xleft + barw/4 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.50f);
+ x = xleft + barw/2 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat("%.1f ms |", (F32)ms*.75f);
+ x = xleft + (barw*3)/4 - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ tdesc = llformat( "%d ms |", ms);
+ x = xleft + barw - LLFontGL::sMonospace->getWidth(tdesc);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ LLRect graph_rect;
+ // Draw borders
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ glColor4f(0.5f,0.5f,0.5f,0.5f);
+
+ S32 by = y + 2;
+
+ y -= ((S32)LLFontGL::sMonospace->getLineHeight() + 4);
+
+ //heading
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, y+5, FALSE);
+
+ //tree view
+ gl_rect_2d(5, by, xleft-10, 5, FALSE);
+
+ by = y + 5;
+ //average bar
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, by-barh-dy-5, FALSE);
+
+ by -= barh*2+dy;
+
+ //current frame bar
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, by-barh-dy-2, FALSE);
+
+ by -= barh+dy+1;
+
+ //history bars
+ gl_rect_2d(xleft-5, by, mRect.getWidth()-5, LINE_GRAPH_HEIGHT-barh-dy-2, FALSE);
+
+ by = LINE_GRAPH_HEIGHT-barh-dy-7;
+
+ //line graph
+ graph_rect = LLRect(xleft-5, by, mRect.getWidth()-5, 5);
+
+ gl_rect_2d(graph_rect, FALSE);
+ }
+
+ // Draw bars for each history entry
+ // Special: -1 = show running average
+ LLViewerImage::bindTexture(box_imagep);
+ for (S32 j=-1; j<histmax && y > LINE_GRAPH_HEIGHT; j++)
+ {
+ int sublevel_dx[FTV_DISPLAY_NUM+1];
+ int sublevel_left[FTV_DISPLAY_NUM+1];
+ int sublevel_right[FTV_DISPLAY_NUM+1];
+ S32 tidx;
+ if (j >= 0)
+ {
+ tidx = LLFastTimer::FTM_HISTORY_NUM - j - 1 - mScrollIndex;
+ }
+ else
+ {
+ tidx = -1;
+ }
+
+ x = xleft;
+
+ // draw the bars for each stat
+ int xpos[FTV_DISPLAY_NUM+1];
+ int deltax[FTV_DISPLAY_NUM+1];
+ xpos[0] = xleft;
+
+ for (S32 i = 0; i < FTV_DISPLAY_NUM; i++)
+ {
+ if (ft_display_table[i].disabled > 1)
+ {
+ continue;
+ }
+
+ F32 frac = (F32)ticks_sum[tidx+1][i] / (F32)totalticks;
+
+ dx = llround(frac * (F32)barw);
+ deltax[i] = dx;
+
+ int level = ft_display_table[i].level;
+ int parent = ft_display_table[i].parent;
+ llassert(level < FTV_DISPLAY_NUM);
+ llassert(parent < FTV_DISPLAY_NUM);
+
+ left = xpos[level];
+
+ S32 prev_idx = i - 1;
+ while (prev_idx > 0)
+ {
+ if (ft_display_table[prev_idx].disabled <= 1)
+ {
+ break;
+ }
+ prev_idx--;
+ }
+ S32 next_idx = i + 1;
+ while (next_idx < FTV_DISPLAY_NUM)
+ {
+ if (ft_display_table[next_idx].disabled <= 1)
+ {
+ break;
+ }
+ next_idx++;
+ }
+
+ if (level == 0)
+ {
+ sublevel_left[level] = xleft;
+ sublevel_dx[level] = dx;
+ sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
+ }
+ else if (i==0 || ft_display_table[prev_idx].level < level)
+ {
+ // If we are the first entry at a new sublevel block, calc the
+ // total width of this sublevel and modify left to align block.
+ U64 sublevelticks = ticks_sum[tidx+1][i];
+ for (S32 k=i+1; k<FTV_DISPLAY_NUM; k++)
+ {
+ if (ft_display_table[k].level < level)
+ break;
+ if (ft_display_table[k].disabled <= 1 && ft_display_table[k].level == level)
+ sublevelticks += ticks_sum[tidx+1][k];
+ }
+ F32 subfrac = (F32)sublevelticks / (F32)totalticks;
+ sublevel_dx[level] = (int)(subfrac * (F32)barw + .5f);
+
+ if (mDisplayCenter == 1) // center aligned
+ {
+ left += (deltax[parent] - sublevel_dx[level])/2;
+ }
+ else if (mDisplayCenter == 2) // right aligned
+ {
+ left += (deltax[parent] - sublevel_dx[level]);
+ }
+
+ sublevel_left[level] = left;
+ sublevel_right[level] = sublevel_left[level] + sublevel_dx[level];
+ }
+
+ right = left + dx;
+ xpos[level] = right;
+ xpos[level+1] = left;
+
+ mBarStart[(j + 1) * FTV_DISPLAY_NUM + i] = left;
+ mBarEnd[(j + 1) * FTV_DISPLAY_NUM + i] = right;
+
+ top = y;
+ bottom = y - barh;
+
+ if (right > left)
+ {
+ //U32 rounded_edges = 0;
+ LLColor4 color = *ft_display_table[i].color;
+ S32 scale_offset = 0;
+
+ BOOL is_child_of_hover_item = (i == mHoverIndex);
+ S32 next_parent = ft_display_table[i].parent;
+ while(!is_child_of_hover_item && next_parent >= 0)
+ {
+ is_child_of_hover_item = (mHoverIndex == next_parent);
+ next_parent = ft_display_table[next_parent].parent;
+ }
+
+ if (i == mHoverIndex)
+ {
+ scale_offset = llfloor(sinf(mHighlightTimer.getElapsedTimeF32() * 6.f) * 3.f);
+ //color = lerp(color, LLColor4::black, -0.4f);
+ }
+ else if (mHoverIndex >= 0 && !is_child_of_hover_item)
+ {
+ color = lerp(color, LLColor4::grey, 0.8f);
+ }
+
+ glColor4fv(color.mV);
+ F32 start_fragment = llclamp((F32)(left - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
+ F32 end_fragment = llclamp((F32)(right - sublevel_left[level]) / (F32)sublevel_dx[level], 0.f, 1.f);
+ gl_segmented_rect_2d_fragment_tex(sublevel_left[level], top - level + scale_offset, sublevel_right[level], bottom + level - scale_offset, box_imagep->getWidth(), box_imagep->getHeight(), 16, start_fragment, end_fragment);
+
+ }
+
+ }
+ y -= (barh + dy);
+ if (j < 0)
+ y -= barh;
+ }
+
+ //draw line graph history
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable scissor(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(graph_rect);
+
+ //normalize based on last frame's maximum
+ static U64 last_max = 0;
+ static F32 alpha_interp = 0.f;
+ U64 max_ticks = llmax(last_max, (U64) 1);
+ F32 ms = (F32)((F64)max_ticks * iclock_freq);
+
+ //display y-axis range
+ LLString tdesc = llformat("%4.2f ms", ms);
+ x = graph_rect.mRight - LLFontGL::sMonospace->getWidth(tdesc)-5;
+ y = graph_rect.mTop - ((S32)LLFontGL::sMonospace->getLineHeight());
+
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white,
+ LLFontGL::LEFT, LLFontGL::TOP);
+
+ //highlight visible range
+ {
+ S32 first_frame = LLFastTimer::FTM_HISTORY_NUM - mScrollIndex;
+ S32 last_frame = first_frame - MAX_VISIBLE_HISTORY;
+
+ F32 frame_delta = ((F32) (graph_rect.getWidth()))/(LLFastTimer::FTM_HISTORY_NUM-1);
+
+ F32 right = (F32) graph_rect.mLeft + frame_delta*first_frame;
+ F32 left = (F32) graph_rect.mLeft + frame_delta*last_frame;
+
+ glColor4f(0.5f,0.5f,0.5f,0.3f);
+ gl_rect_2d((S32) left, graph_rect.mTop, (S32) right, graph_rect.mBottom);
+
+ if (mHoverBarIndex >= 0)
+ {
+ S32 bar_frame = first_frame - mHoverBarIndex;
+ F32 bar = (F32) graph_rect.mLeft + frame_delta*bar_frame;
+
+ glColor4f(0.5f,0.5f,0.5f,1);
+
+ glBegin(GL_LINES);
+ glVertex2i((S32)bar, graph_rect.mBottom);
+ glVertex2i((S32)bar, graph_rect.mTop);
+ glEnd();
+ }
+ }
+
+ U64 cur_max = 0;
+ for (S32 idx = 0; idx < FTV_DISPLAY_NUM; ++idx)
+ {
+ if (ft_display_table[idx].disabled > 1)
+ { //skip disabled timers
+ continue;
+ }
+
+ //fatten highlighted timer
+ if (mHoverIndex == idx)
+ {
+ glLineWidth(3);
+ }
+
+ F32* col = ft_display_table[idx].color->mV;
+
+ F32 alpha = 1.f;
+
+ if (mHoverIndex >= 0 &&
+ idx != mHoverIndex)
+ { //fade out non-hihglighted timers
+ if (ft_display_table[idx].parent != mHoverIndex)
+ {
+ alpha = alpha_interp;
+ }
+ }
+
+ glColor4f(col[0], col[1], col[2], alpha);
+ glBegin(GL_LINE_STRIP);
+ for (U32 j = 0; j < LLFastTimer::FTM_HISTORY_NUM; j++)
+ {
+ U64 ticks = ticks_sum[j+1][idx];
+ if (alpha == 1.f)
+ { //normalize to highlighted timer
+ cur_max = llmax(cur_max, ticks);
+ }
+ F32 x = graph_rect.mLeft + ((F32) (graph_rect.getWidth()))/(LLFastTimer::FTM_HISTORY_NUM-1)*j;
+ F32 y = graph_rect.mBottom + (F32) graph_rect.getHeight()/max_ticks*ticks;
+ glVertex2f(x,y);
+ }
+ glEnd();
+
+ if (mHoverIndex == idx)
+ {
+ glLineWidth(1);
+ }
+ }
+
+ //interpolate towards new maximum
+ F32 dt = gFrameIntervalSeconds*3.f;
+ last_max = (U64) ((F32) last_max + ((F32) cur_max- (F32) last_max) * dt);
+ F32 alpha_target = last_max > cur_max ?
+ llmin((F32) last_max/ (F32) cur_max - 1.f,1.f) :
+ llmin((F32) cur_max/ (F32) last_max - 1.f,1.f);
+
+ alpha_interp = alpha_interp + (alpha_target-alpha_interp) * dt;
+
+ }
+ }
+
+ // Output stats for clicked bar to log
+ if (mPrintStats >= 0)
+ {
+ LLString legend_stat;
+ S32 stat_num;
+ S32 first = 1;
+ for (stat_num = 0; stat_num < FTV_DISPLAY_NUM; stat_num++)
+ {
+ if (ft_display_table[stat_num].disabled > 1)
+ continue;
+ if (!first)
+ legend_stat += ", ";
+ first=0;
+ legend_stat += ft_display_table[stat_num].desc;
+ }
+ llinfos << legend_stat << llendl;
+
+ LLString timer_stat;
+ first = 1;
+ for (stat_num = 0; stat_num < FTV_DISPLAY_NUM; stat_num++)
+ {
+ S32 disabled = ft_display_table[stat_num].disabled;
+ if (disabled > 1)
+ continue;
+ if (!first)
+ timer_stat += ", ";
+ first=0;
+ U64 ticks;
+ S32 tidx = ft_display_table[stat_num].timer;
+ if (mPrintStats > 0)
+ {
+ S32 hidx = (LLFastTimer::sLastFrameIndex+(mPrintStats-1)-mScrollIndex) % LLFastTimer::FTM_HISTORY_NUM;
+ ticks = disabled >= 1 ? ticks_sum[mPrintStats][stat_num] : LLFastTimer::sCountHistory[hidx][tidx];
+ }
+ else
+ {
+ ticks = disabled >= 1 ? ticks_sum[0][stat_num] : LLFastTimer::sCountAverage[tidx];
+ }
+ F32 ms = (F32)((F64)ticks * iclock_freq);
+
+ timer_stat += llformat("%.1f",ms);
+ }
+ llinfos << timer_stat << llendl;
+ mPrintStats = -1;
+ }
+
+ mHoverIndex = -1;
+ mHoverBarIndex = -1;
+
+ LLView::draw();
+}
+
+F64 LLFastTimerView::getTime(LLFastTimer::EFastTimerType tidx)
+{
+ // Find table index
+ S32 i;
+ for (i=0; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (tidx == ft_display_table[i].timer)
+ {
+ break;
+ }
+ }
+ S32 table_idx = i;
+
+ // Add child ticks to parent
+ U64 ticks = LLFastTimer::sCountAverage[tidx];
+ S32 level = ft_display_table[table_idx].level;
+ for (i=table_idx+1; i<FTV_DISPLAY_NUM; i++)
+ {
+ if (ft_display_table[i].level <= level)
+ {
+ break;
+ }
+ ticks += LLFastTimer::sCountAverage[ft_display_table[i].timer];
+ }
+
+ return (F64)ticks / (F64)LLFastTimer::countsPerSecond();
+}
diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h
new file mode 100644
index 0000000000..ea83c38a84
--- /dev/null
+++ b/indra/newview/llfasttimerview.h
@@ -0,0 +1,52 @@
+/**
+ * @file llfasttimerview.h
+ * @brief LLFastTimerView class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFASTTIMERVIEW_H
+#define LL_LLFASTTIMERVIEW_H
+
+#include "llview.h"
+#include "llframetimer.h"
+
+class LLFastTimerView : public LLView
+{
+public:
+ LLFastTimerView(const std::string& name, const LLRect& rect);
+ virtual ~LLFastTimerView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void draw();
+
+ S32 getLegendIndex(S32 y);
+ F64 getTime(LLFastTimer::EFastTimerType tidx);
+
+private:
+ S32* mBarStart;
+ S32* mBarEnd;
+ S32 mDisplayMode;
+ S32 mDisplayCenter;
+ S32 mDisplayCalls;
+ U64 mAvgCountTotal;
+ U64 mMaxCountTotal;
+ LLRect mBarRect;
+ S32 mScrollIndex;
+ S32 mHoverIndex;
+ S32 mHoverBarIndex;
+ LLFrameTimer mHighlightTimer;
+ S32 mSubtractHidden;
+ S32 mPrintStats;
+};
+
+#endif
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
new file mode 100644
index 0000000000..be816deebc
--- /dev/null
+++ b/indra/newview/llfeaturemanager.cpp
@@ -0,0 +1,842 @@
+/**
+ * @file llfeaturemanager.cpp
+ * @brief LLFeatureManager class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iostream>
+#include <fstream>
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfeaturemanager.h"
+#include "lldir.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llsecondlifeurls.h"
+
+#include "llviewercontrol.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "lldrawpoolterrain.h"
+#include "llviewerimagelist.h"
+#include "llwindow.h"
+#include "llui.h"
+
+#if LL_WINDOWS
+#include "lldxhardware.h"
+#endif
+
+//
+// externs
+//
+extern LLMemoryInfo gSysMemory;
+extern LLCPUInfo gSysCPU;
+extern void write_debug(const char *str);
+extern void write_debug(const std::string& str);
+
+#if LL_DARWIN
+const char FEATURE_TABLE_FILENAME[] = "featuretable_mac.txt";
+#else
+const char FEATURE_TABLE_FILENAME[] = "featuretable.txt";
+#endif
+
+const char GPU_TABLE_FILENAME[] = "gpu_table.txt";
+
+LLFeatureManager *gFeatureManagerp = NULL;
+
+LLFeatureInfo::LLFeatureInfo(const char *name, const BOOL available, const S32 level) : mValid(TRUE)
+{
+ mName = name;
+ mAvailable = available;
+ mRecommendedLevel = level;
+}
+
+LLFeatureList::LLFeatureList(const char *name)
+{
+ mName = name;
+}
+
+LLFeatureList::~LLFeatureList()
+{
+}
+
+void LLFeatureList::addFeature(const char *name, const BOOL available, const S32 level)
+{
+ if (mFeatures.count(name))
+ {
+ llwarns << "LLFeatureList::Attempting to add preexisting feature " << name << llendl;
+ }
+
+ LLFeatureInfo fi(name, available, level);
+ mFeatures[name] = fi;
+}
+
+BOOL LLFeatureList::isFeatureAvailable(const char *name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mAvailable;
+ }
+
+ llwarns << "Feature " << name << " not on feature list!" << llendl;
+ return FALSE;
+}
+
+S32 LLFeatureList::getRecommendedLevel(const char *name)
+{
+ if (mFeatures.count(name))
+ {
+ return mFeatures[name].mRecommendedLevel;
+ }
+
+ llwarns << "Feature " << name << " not on feature list!" << llendl;
+ return -1;
+}
+
+BOOL LLFeatureList::maskList(LLFeatureList &mask)
+{
+ //llinfos << "Masking with " << mask.mName << llendl;
+ //
+ // Lookup the specified feature mask, and overlay it on top of the
+ // current feature mask.
+ //
+
+ LLFeatureInfo mask_fi;
+
+ feature_map_t::iterator feature_it;
+ for (feature_it = mask.mFeatures.begin(); feature_it != mask.mFeatures.end(); ++feature_it)
+ {
+ mask_fi = feature_it->second;
+ //
+ // Look for the corresponding feature
+ //
+ if (!mFeatures.count(mask_fi.mName))
+ {
+ llwarns << "Feature " << mask_fi.mName << " in mask not in top level!" << llendl;
+ continue;
+ }
+
+ LLFeatureInfo &cur_fi = mFeatures[mask_fi.mName];
+ if (mask_fi.mAvailable && !cur_fi.mAvailable)
+ {
+ llwarns << "Mask attempting to reenabling disabled feature, ignoring " << cur_fi.mName << llendl;
+ continue;
+ }
+ cur_fi.mAvailable = mask_fi.mAvailable;
+ cur_fi.mRecommendedLevel = llmin(cur_fi.mRecommendedLevel, mask_fi.mRecommendedLevel);
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llinfos << "Feature mask " << mask.mName
+ << " Feature " << mask_fi.mName
+ << " Mask: " << mask_fi.mRecommendedLevel
+ << " Now: " << cur_fi.mRecommendedLevel << llendl;
+#endif
+ }
+
+#if 0 && !LL_RELEASE_FOR_DOWNLOAD
+ llinfos << "After appling mask " << mask.mName << llendl;
+ dump();
+#endif
+ return TRUE;
+}
+
+void LLFeatureList::dump()
+{
+ llinfos << "Feature list: " << mName << llendl;
+ llinfos << "--------------" << llendl;
+
+ LLFeatureInfo fi;
+ feature_map_t::iterator feature_it;
+ for (feature_it = mFeatures.begin(); feature_it != mFeatures.end(); ++feature_it)
+ {
+ fi = feature_it->second;
+ llinfos << fi.mName << "\t\t" << fi.mAvailable << ":" << fi.mRecommendedLevel << llendl;
+ }
+ llinfos << llendl;
+}
+
+LLFeatureList *LLFeatureManager::findMask(const char *name)
+{
+ if (mMaskList.count(name))
+ {
+ return mMaskList[name];
+ }
+
+ return NULL;
+}
+
+BOOL LLFeatureManager::maskFeatures(const char *name)
+{
+ LLFeatureList *maskp = findMask(name);
+ if (!maskp)
+ {
+ llwarns << "Unknown feature mask " << name << llendl;
+ return FALSE;
+ }
+ llinfos << "Applying Feature Mask: " << name << llendl;
+ return maskList(*maskp);
+}
+
+BOOL LLFeatureManager::loadFeatureTables()
+{
+ std::string data_path = gDirUtilp->getAppRODataDir();
+
+ data_path += gDirUtilp->getDirDelimiter();
+
+ data_path += FEATURE_TABLE_FILENAME;
+
+
+ char name[MAX_STRING+1];
+
+ llifstream file;
+ U32 version;
+
+ file.open(data_path.c_str());
+
+ if (!file)
+ {
+ llwarns << "Unable to open feature table!" << llendl;
+ return FALSE;
+ }
+
+ // Check file version
+ file >> name;
+ file >> version;
+ if (strcmp(name, "version"))
+ {
+ llwarns << data_path << " does not appear to be a valid feature table!" << llendl;
+ return FALSE;
+ }
+
+ mTableVersion = version;
+
+ LLFeatureList *flp = NULL;
+ while (!file.eof())
+ {
+ char buffer[MAX_STRING];
+ name[0] = 0;
+
+ file >> name;
+
+ if (strlen(name) >= 2 &&
+ name[0] == '/' &&
+ name[1] == '/')
+ {
+ // This is a comment.
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (strlen(name) == 0)
+ {
+ // This is a blank line
+ file.getline(buffer, MAX_STRING);
+ continue;
+ }
+
+ if (!strcmp(name, "list"))
+ {
+ if (flp)
+ {
+ //flp->dump();
+ }
+ // It's a new mask, create it.
+ file >> name;
+ if (mMaskList.count(name))
+ {
+ llerrs << "Overriding mask " << name << ", this is invalid!" << llendl;
+ }
+
+ if (!flp)
+ {
+ //
+ // The first one is always the default
+ //
+ flp = this;
+ }
+ else
+ {
+ flp = new LLFeatureList(name);
+ mMaskList[name] = flp;
+ }
+
+ }
+ else
+ {
+ if (!flp)
+ {
+ llerrs << "Specified parameter before <list> keyword!" << llendl;
+ }
+ S32 available, recommended;
+ file >> available >> recommended;
+ flp->addFeature(name, available, recommended);
+ }
+ }
+ file.close();
+ //flp->dump();
+
+ return TRUE;
+}
+
+S32 LLFeatureManager::getGPUClass()
+{
+ return mGPUClass;
+}
+
+S32 LLFeatureManager::loadGPUClass()
+{
+ std::string data_path = gDirUtilp->getAppRODataDir();
+
+ data_path += gDirUtilp->getDirDelimiter();
+
+ data_path += GPU_TABLE_FILENAME;
+
+ llifstream file;
+
+ file.open(data_path.c_str());
+
+ if (!file)
+ {
+ llwarns << "Unable to open GPU table: " << data_path << "!" << llendl;
+ return 0;
+ }
+
+ std::string renderer = gGLManager.getRawGLString();
+ for (std::string::iterator i = renderer.begin(); i != renderer.end(); ++i)
+ {
+ *i = tolower(*i);
+ }
+
+ while (!file.eof())
+ {
+ char buffer[MAX_STRING];
+ buffer[0] = 0;
+
+ file.getline(buffer, MAX_STRING);
+
+ if (strlen(buffer) >= 2 &&
+ buffer[0] == '/' &&
+ buffer[1] == '/')
+ {
+ // This is a comment.
+ continue;
+ }
+
+ if (strlen(buffer) == 0)
+ {
+ // This is a blank line
+ continue;
+ }
+
+ char* cls, *label, *expr;
+
+ label = strtok(buffer, "\t");
+ expr = strtok(NULL, "\t");
+ cls = strtok(NULL, "\t");
+
+ if (label == NULL || expr == NULL || cls == NULL)
+ {
+ continue;
+ }
+
+ for (U32 i = 0; i < strlen(expr); i++)
+ {
+ expr[i] = tolower(expr[i]);
+ }
+
+ char* ex = strtok(expr, ".*");
+ char* rnd = (char*) renderer.c_str();
+
+ while (ex != NULL && rnd != NULL)
+ {
+ rnd = strstr(rnd, ex);
+ ex = strtok(NULL, ".*");
+ }
+
+ if (rnd != NULL)
+ {
+ file.close();
+ llinfos << "GPU is " << label << llendl;
+ return (S32) strtol(cls, NULL, 10);
+ }
+ }
+ file.close();
+ //flp->dump();
+
+ llwarns << "Couldn't match GPU to a class: " << gGLManager.getRawGLString() << llendl;
+ return 0;
+}
+
+void LLFeatureManager::cleanupFeatureTables()
+{
+ std::for_each(mMaskList.begin(), mMaskList.end(), DeletePairedPointer());
+ mMaskList.clear();
+}
+
+
+void LLFeatureManager::initCPUFeatureMasks()
+{
+ if (gSysMemory.getPhysicalMemory() <= 256*1024*1024)
+ {
+ maskFeatures("RAM256MB");
+ }
+ else if (gSysMemory.getPhysicalMemory() <= 512*1024*1024)
+ {
+ //maskFeatures("RAM512MB");
+ }
+
+ if (gSysCPU.getMhz() < 1100)
+ {
+ maskFeatures("CPUSlow");
+ }
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
+
+void LLFeatureManager::initGraphicsFeatureMasks()
+{
+ mGPUClass = loadGPUClass();
+
+ if (mGPUClass >= 0 && mGPUClass < 4)
+ {
+ const char* class_table[] =
+ {
+ "Class0",
+ "Class1",
+ "Class2",
+ "Class3"
+ };
+
+ llinfos << "Setting GPU Class to " << class_table[mGPUClass] << llendl;
+ maskFeatures(class_table[mGPUClass]);
+ }
+
+ if (!gGLManager.mHasFragmentShader)
+ {
+ maskFeatures("NoPixelShaders");
+ }
+ if (!gGLManager.mHasVertexShader)
+ {
+ maskFeatures("NoVertexShaders");
+ }
+ if (gGLManager.mIsNVIDIA)
+ {
+ maskFeatures("NVIDIA");
+ }
+ if (gGLManager.mIsGF2or4MX)
+ {
+ maskFeatures("GeForce2");
+ }
+ if (gGLManager.mIsATI)
+ {
+ maskFeatures("ATI");
+ }
+ if (gGLManager.mIsRadeon8500)
+ {
+ maskFeatures("Radeon8500");
+ }
+ if (gGLManager.mIsRadeon9700)
+ {
+ maskFeatures("Radeon9700");
+ }
+ if (gGLManager.mIsGFFX)
+ {
+ maskFeatures("GeForceFX");
+ }
+ if (gGLManager.mIsIntel)
+ {
+ maskFeatures("Brookdale");
+ }
+
+ if (gGLManager.mIsMobilityRadeon9000)
+ {
+ maskFeatures("MobilityRadeon9000");
+ }
+ if (isSafe())
+ {
+ maskFeatures("safe");
+ }
+}
+
+extern LLOSInfo gSysOS;
+
+
+BOOL bad_hardware_dialog(const LLString &info_str, const LLString &url)
+{
+ if (!gSavedSettings.getWarning("AboutBadPCI"))
+ {
+ return FALSE;
+ }
+
+ // XUI:translate
+ std::string msg = llformat(
+ "[SECOND_LIFE] has detected that there may be a problem with.\n"
+ "hardware or drivers on your computer. Often resolving these\n"
+ "issues can result in enhanced stability and performance.\n"
+ " \n"
+ "%s\n"
+ " \n"
+ "Would you like to view a web page with more detailed\n"
+ "information on this problem?\n", info_str.c_str());
+
+ // Warn them that runnin without DirectX 9 will
+ // not allow us to tell them about driver issues
+ S32 button = OSMessageBox(msg.c_str(),
+ "Warning",
+ OSMB_YESNO);
+ if (OSBTN_YES== button)
+ {
+ llinfos << "User quitting after detecting bad drivers" << llendl;
+ spawn_web_browser(url.c_str());
+ return TRUE;
+ }
+ else
+ {
+ // Don't warn about bad PCI stuff again, they've clicked past it.
+ gSavedSettings.setWarning("AboutBadPCI", FALSE);
+ }
+ return FALSE;
+}
+
+BOOL LLFeatureManager::initPCIFeatureMasks()
+{
+#if LL_WINDOWS
+ BOOL exit_after_bad = FALSE;
+
+ BOOL is_2000 = FALSE;
+ BOOL is_xp = FALSE;
+
+ if (gSysOS.mMajorVer != 5)
+ {
+ // Unknown windows version number, exit!"
+ llwarns << "Unknown Windows major version " << gSysOS.mMajorVer << ", aborting detection!" << llendl;
+ return FALSE;
+ }
+ if (gSysOS.mMinorVer == 0)
+ {
+ is_2000 = TRUE;
+ }
+ else if (gSysOS.mMinorVer == 1)
+ {
+ is_xp = TRUE;
+ }
+ else
+ {
+ llwarns << "Unknown Windows minor version " << gSysOS.mMinorVer << ", aborting detection!" << llendl;
+ return FALSE;
+ }
+
+ // This only works on Win32, as it relies on DX9 hardware detection
+ // The PCI masks are actually the inverse of the normal masks
+ // We actually look through the masks,and see if any hardware matches it.
+ // This is because the masks encode logic about
+
+ // Check for the broken AMD AGP controllers (751, 761, 762)
+
+ // Horrible cruddy fixed lookup table.
+ // Figure out what OS we're on, the version numbers are different. Sigh...
+
+ LLDXDriverFile *dfilep = NULL;
+ LLDXDevice *devp = NULL;
+
+ // AMD(1022) AGP controllers
+ // 7007 AMD-751 AGP Controller
+ // 700F AMD-761 AGP Controller
+ // 700D AMD-762 AGP Controller
+ devp = gDXHardware.findDevice("VEN_1022", "DEV_7007|DEV_700F|DEV_700D");
+ if (devp)
+ {
+ // We're just pretty much screwed here, there are big problems with this hardware
+ // We've got trouble with the most recent nVidia drivers. Check for this and warn.
+
+ // Note: Need to detect that we're running with older nVidia hardware, probably
+ exit_after_bad |= bad_hardware_dialog("AMD AGP Controller",
+ AMD_AGP_URL);
+ }
+
+ // VIA(1106) AGP Controllers
+ // These need upgrading on both Win2K and WinXP
+ //
+ // 8305 VT8363/8365 CPU to AGP - Apollo KT133/KM133
+ // 8598 VT82C598MVP/694X CPU to AGP - Apollo MVP3/Pro133A
+ // 8605 VT8605 CPU to AGP - Apollo PM133
+ // B091 VT8633 CPU to AGP - Apollo Pro 266
+ // B099 VT8366/A/T CPU to AGP - Apollo KT266/A/333
+ // B168 VT8235 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X333 chipset
+ // B188 VT8237 CPU to AGP (AGP 2.0/3.0) - K8T800
+ // B198 VT8237 CPU to AGP (AGP 2.0/3.0) - ProSavageDDR P4X600 chipset
+
+ devp = gDXHardware.findDevice("VEN_1106",
+ "DEV_8305|DEV_8598|DEV_8605|DEV_B091|"
+ "DEV_B099|DEV_B168|DEV_B188|DEV_B198");
+ if (devp)
+ {
+ BOOL out_of_date = FALSE;
+ // Wanted driver: VIAAGP1.SYS
+ // Version Format: M.mm.0000.vvvv
+ // M.mm - Major/minor OS version (5.0 for Win2000, 5.1 for WinXP)
+ // vvvv - driver version number
+ //
+ // Notes:
+ // 3442 is most recent as of 2/25/04, probably want at least 3430 (seems to be a common version)
+
+ // These are DELIBERATE assignments inside if statements, blech.
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version: 5.01.2600.xxxx
+ //
+ // Notes:
+ // Default WinXP driver for B168, B198?
+
+ // Old driver: pci.sys
+ // Version: 5.01.2195.xxxx
+ //
+ // Notes:
+ // Default Win2K driver for 8305?
+
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ else if (dfilep = devp->findDriver("VIAAGP.SYS"))
+ {
+ // Old driver: VIAAGP.SYS
+ // Version: 5.01.2600.xxxx
+ //
+ // Notes:
+ // Default WinXP driver for B09x?
+
+ llwarns << "Detected VIAAGP.SYS" << llendl;
+ write_debug("Old driver (VIAAGP.SYS) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ else if (dfilep = devp->findDriver("VIAAGP1.SYS"))
+ {
+ if (dfilep->mVersion.getField(3) < 3430)
+ {
+ // They're using a pretty old version of the VIA AGP drivers
+ // Maybe they want to upgrade?
+ llwarns << "Detected VIAAGP1.SYS" << llendl;
+ write_debug("Old driver (VIAAGP1.SYS) for VIA detected!");
+ out_of_date = TRUE;
+ }
+ }
+ if (out_of_date)
+ {
+ exit_after_bad |= bad_hardware_dialog("Out of date VIA AGP chipset driver",
+ VIA_URL);
+ }
+ }
+
+ // Intel(8086) AGP controllers (Win2K)
+ // These particular controllers only may need drivers on Win2K
+ //
+ // 1A31 82845[MP|MZ] Processor to AGP Controller
+ // 2532 82850/860 Processor to AGP Controller
+ if (is_2000)
+ {
+ devp = gDXHardware.findDevice("VEN_8086",
+ "DEV_1A31");
+ if (devp)
+ {
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for Intel 82845/850 on Win2K detected!");
+ exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
+ INTEL_CHIPSET_URL);
+ }
+ }
+ }
+
+ /* Removed 4/3/2006 by JC
+ After talking with Doug, we don't know what the proper driver
+ and/or version number should be for Intel 865. Regardless, this
+ code would _always_ complain if you had that chipset.
+
+ // Intel(8086) AGP controllers (All)
+ // These particular controllers may need drivers on both Win2K and WinXP
+ //
+ // 2561 82845G/GL/GE/PE/GV Processor to AGP Controller
+ // 2571 82865G/PE/P/GV/28248P Processor to AGP Controller
+ devp = gDXHardware.findDevice("VEN_8086",
+ "DEV_2571");
+ if (devp)
+ {
+ // Wanted driver: AGP440.SYS(?)
+ //
+ // Notes:
+ // Not sure, need to verify with an actual 82865/75 (Dell 8300?)
+
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ exit_after_bad |= bad_hardware_dialog("Out of date Intel chipset driver",
+ INTEL_CHIPSET_URL);
+ }
+ */
+
+
+ // SiS(1039) AGP controllers (All)
+ // These particular controllers may need drivers on both Win2K and WinXP
+ //
+ // 0001 SiS 530
+ // 0002 SiS SG86C202(???)
+ // 0003 SiS 648FX
+ devp = gDXHardware.findDevice("VEN_1039",
+ "DEV_0001|DEV_0002|DEV_0003");
+ if (devp)
+ {
+ BOOL out_of_date = FALSE;
+ // Wanted driver: SISAGPX.SYS
+ //
+ // Notes:
+ // Not sure, need to verify with an actual 82865/75 (Dell 8300?)
+
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ if (dfilep = devp->findDriver("pci.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ llwarns << "Detected pci.sys" << llendl;
+ write_debug("Old driver (pci.sys) for SiS detected!");
+ out_of_date = TRUE;
+ }
+
+ if (dfilep = devp->findDriver("sisagp.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 5.01.21[9|6]5.xxxx
+ //
+ llwarns << "Detected sisagp.sys" << llendl;
+ write_debug("Old driver (sisagp.sys) for SiS detected!");
+ out_of_date = TRUE;
+ }
+
+ if (dfilep = devp->findDriver("sisagpx.sys"))
+ {
+ // Old driver: pci.sys
+ // Version 7.02.0000.xxxx
+ //
+ // Notes:
+ // Default driver for Win2K? Not sure what the "correct" driver is -
+ // maybe some variant of AGP440.SYS?
+ if (dfilep->mVersion.getField(3) < 1160)
+ {
+ out_of_date = TRUE;
+ llwarns << "Detected sisagpx.sys" << llendl;
+ write_debug("Old driver (sisagpx.sys) for SiS detected!");
+ }
+ }
+ if (out_of_date)
+ {
+ exit_after_bad |= bad_hardware_dialog("Out of date SiS chipset driver",
+ SIS_CHIPSET_URL);
+ }
+ }
+
+ return exit_after_bad;
+#else
+ return TRUE;
+#endif
+}
+
+void LLFeatureManager::applyRecommendedFeatures()
+{
+ // see featuretable.txt
+
+ llinfos << "Applying Recommended Features" << llendl;
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ dump();
+#endif
+
+ // Enabling AGP
+ if (getRecommendedLevel("RenderAGP"))
+ {
+ gSavedSettings.setBOOL("RenderUseAGP", TRUE);
+ }
+ else
+ {
+ gSavedSettings.setBOOL("RenderUseAGP", FALSE);
+ }
+
+ // Anisotropic rendering
+ BOOL aniso = getRecommendedLevel("RenderAniso");
+ LLImageGL::sGlobalUseAnisotropic = aniso;
+ gSavedSettings.setBOOL("RenderAnisotropic", LLImageGL::sGlobalUseAnisotropic);
+
+ // Render Avatar Mode
+ BOOL avatar_vp = getRecommendedLevel("RenderAvatarVP");
+ S32 avatar_mode = getRecommendedLevel("RenderAvatarMode");
+ if (avatar_vp == FALSE)
+ avatar_mode = 0;
+ gSavedSettings.setBOOL("RenderAvatarVP", avatar_vp);
+ gSavedSettings.setS32("RenderAvatarMode", avatar_mode);
+
+ // Render Distance
+ S32 far_clip = getRecommendedLevel("RenderDistance");
+ gSavedSettings.setF32("RenderFarClip", (F32)far_clip);
+
+ // Lighting
+ S32 lighting = getRecommendedLevel("RenderLighting");
+ gSavedSettings.setS32("RenderLightingDetail", lighting);
+
+ // ObjectBump
+ BOOL bump = getRecommendedLevel("RenderObjectBump");
+ gSavedSettings.setBOOL("RenderObjectBump", bump);
+
+ // Particle Count
+ S32 max_parts = getRecommendedLevel("RenderParticleCount");
+ gSavedSettings.setS32("RenderMaxPartCount", max_parts);
+ LLViewerPartSim::setMaxPartCount(max_parts);
+
+ // RippleWater
+ BOOL ripple = getRecommendedLevel("RenderRippleWater");
+ gSavedSettings.setBOOL("RenderRippleWater", ripple);
+
+ // Vertex Shaders
+ S32 shaders = getRecommendedLevel("VertexShaderEnable");
+ gSavedSettings.setBOOL("VertexShaderEnable", shaders);
+
+ // Terrain
+ S32 terrain = getRecommendedLevel("RenderTerrainDetail");
+ gSavedSettings.setS32("RenderTerrainDetail", terrain);
+ LLDrawPoolTerrain::sDetailMode = terrain;
+
+ // Set the amount of VRAM we have available
+ if (isSafe())
+ {
+ gSavedSettings.setS32("GraphicsCardMemorySetting", 1); // 32 MB in 'safe' mode
+ }
+ else
+ {
+ S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
+ // -1 indicates use default (max), don't change
+ if (idx != -1)
+ {
+ idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
+ gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
+ }
+ }
+}
diff --git a/indra/newview/llfeaturemanager.h b/indra/newview/llfeaturemanager.h
new file mode 100644
index 0000000000..98f9317b01
--- /dev/null
+++ b/indra/newview/llfeaturemanager.h
@@ -0,0 +1,98 @@
+/**
+ * @file llfeaturemanager.h
+ * @brief LLFeatureManager class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFEATUREMANAGER_H
+#define LL_LLFEATUREMANAGER_H
+
+#include "stdtypes.h"
+
+#include "llstring.h"
+#include "llskipmap.h"
+#include <map>
+
+class LLFeatureInfo
+{
+public:
+ LLFeatureInfo() : mValid(FALSE), mAvailable(FALSE), mRecommendedLevel(-1) {}
+ LLFeatureInfo(const char *name, const BOOL available, const S32 level);
+
+ BOOL isValid() const { return mValid; };
+
+public:
+ BOOL mValid;
+ LLString mName;
+ BOOL mAvailable;
+ S32 mRecommendedLevel;
+};
+
+
+class LLFeatureList
+{
+public:
+ LLFeatureList(const char *name = "default");
+ virtual ~LLFeatureList();
+
+ BOOL isFeatureAvailable(const char *name);
+ S32 getRecommendedLevel(const char *name);
+
+ void setFeatureAvailable(const char *name, const BOOL available);
+ void setRecommendedLevel(const char *name, const S32 level);
+
+ BOOL loadFeatureList(FILE *fp);
+
+ BOOL maskList(LLFeatureList &mask);
+
+ void addFeature(const char *name, const BOOL available, const S32 level);
+
+ void dump();
+protected:
+ LLString mName;
+ typedef std::map<LLString, LLFeatureInfo> feature_map_t;
+ feature_map_t mFeatures;
+};
+
+
+class LLFeatureManager : public LLFeatureList
+{
+public:
+ LLFeatureManager() : mInited(FALSE), mTableVersion(0), mSafe(FALSE), mGPUClass(0) {}
+
+ void maskCurrentList(const char *name); // Mask the current feature list with the named list
+
+ BOOL loadFeatureTables();
+ S32 getGPUClass();
+ S32 loadGPUClass();
+
+ void cleanupFeatureTables();
+
+ S32 getVersion() const { return mTableVersion; }
+ void setSafe(const BOOL safe) { mSafe = safe; }
+ BOOL isSafe() const { return mSafe; }
+
+ LLFeatureList *findMask(const char *name);
+ BOOL maskFeatures(const char *name);
+
+
+ void initCPUFeatureMasks();
+ void initGraphicsFeatureMasks();
+ BOOL initPCIFeatureMasks();
+
+ void applyRecommendedFeatures();
+protected:
+ void initBaseMask();
+
+ std::map<LLString, LLFeatureList *> mMaskList;
+ BOOL mInited;
+ S32 mTableVersion;
+ BOOL mSafe; // Reinitialize everything to the "safe" mask
+ S32 mGPUClass;
+};
+
+extern LLFeatureManager *gFeatureManagerp;
+
+#endif // LL_LLFEATUREMANAGER_H
diff --git a/indra/newview/llfilepicker.cpp b/indra/newview/llfilepicker.cpp
new file mode 100644
index 0000000000..776a2e4dc2
--- /dev/null
+++ b/indra/newview/llfilepicker.cpp
@@ -0,0 +1,1339 @@
+/**
+ * @file llfilepicker.cpp
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfilepicker.h"
+//#include "viewer.h"
+//#include "llviewermessage.h"
+#include "llworld.h"
+#include "llviewerwindow.h"
+#include "llkeyboard.h"
+#include "lldir.h"
+#include "llframetimer.h"
+
+//
+// Globals
+//
+
+LLFilePicker LLFilePicker::sInstance;
+
+#if LL_WINDOWS
+#define SOUND_FILTER L"Sounds (*.wav)\0*.wav\0"
+#define IMAGE_FILTER L"Images (*.tga; *.bmp; *.jpg; *.jpeg)\0*.tga;*.bmp;*.jpg;*.jpeg\0"
+#define ANIM_FILTER L"Animations (*.bvh)\0*.bvh\0"
+#ifdef _CORY_TESTING
+#define GEOMETRY_FILTER L"SL Geometry (*.slg)\0*.slg\0"
+#endif
+#define XML_FILTER L"XML files (*.xml)\0*.xml\0"
+#define SLOBJECT_FILTER L"Objects (*.slobject)\0*.slobject\0"
+#define RAW_FILTER L"RAW files (*.raw)\0*.raw\0"
+#endif
+
+//
+// Implementation
+//
+#if LL_WINDOWS
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+
+ mOFN.lStructSize = sizeof(OPENFILENAMEW);
+ mOFN.hwndOwner = NULL; // Set later
+ mOFN.hInstance = NULL;
+ mOFN.lpstrCustomFilter = NULL;
+ mOFN.nMaxCustFilter = 0;
+ mOFN.lpstrFile = NULL; // set in open and close
+ mOFN.nMaxFile = LL_MAX_PATH;
+ mOFN.lpstrFileTitle = NULL;
+ mOFN.nMaxFileTitle = 0;
+ mOFN.lpstrInitialDir = NULL;
+ mOFN.lpstrTitle = NULL;
+ mOFN.Flags = 0; // set in open and close
+ mOFN.nFileOffset = 0;
+ mOFN.nFileExtension = 0;
+ mOFN.lpstrDefExt = NULL;
+ mOFN.lCustData = 0L;
+ mOFN.lpfnHook = NULL;
+ mOFN.lpTemplateName = NULL;
+}
+
+LLFilePicker::~LLFilePicker()
+{
+ // nothing
+}
+
+BOOL LLFilePicker::setupFilter(ELoadFilter filter)
+{
+ BOOL res = TRUE;
+ switch (filter)
+ {
+ case FFLOAD_ALL:
+ mOFN.lpstrFilter = L"All Files (*.*)\0*.*\0" \
+ SOUND_FILTER \
+ IMAGE_FILTER \
+ ANIM_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_WAV:
+ mOFN.lpstrFilter = SOUND_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_IMAGE:
+ mOFN.lpstrFilter = IMAGE_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_ANIM:
+ mOFN.lpstrFilter = ANIM_FILTER \
+ L"\0";
+ break;
+#ifdef _CORY_TESTING
+ case FFLOAD_GEOMETRY:
+ mOFN.lpstrFilter = GEOMETRY_FILTER \
+ L"\0";
+ break;
+#endif
+ case FFLOAD_XML:
+ mOFN.lpstrFilter = XML_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_SLOBJECT:
+ mOFN.lpstrFilter = SLOBJECT_FILTER \
+ L"\0";
+ break;
+ case FFLOAD_RAW:
+ mOFN.lpstrFilter = RAW_FILTER \
+ L"\0";
+ break;
+ default:
+ res = FALSE;
+ break;
+ }
+ return res;
+}
+
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ // don't provide default file selection
+ mFilesW[0] = '\0';
+
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+ mOFN.lpstrFile = mFilesW;
+ mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR ;
+ mOFN.nFilterIndex = 1;
+
+ setupFilter(filter);
+
+ // Modal, so pause agent
+ send_agent_pause();
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetOpenFileName(&mOFN);
+ if (success)
+ {
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ mCurrentFile = mFiles;
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ // don't provide default file selection
+ mFilesW[0] = '\0';
+
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+ mOFN.lpstrFile = mFilesW;
+ mOFN.nFilterIndex = 1;
+ mOFN.nMaxFile = FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR |
+ OFN_EXPLORER | OFN_ALLOWMULTISELECT;
+
+ setupFilter(filter);
+
+ // Modal, so pause agent
+ send_agent_pause();
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetOpenFileName(&mOFN); // pauses until ok or cancel.
+ if( success )
+ {
+ // The getopenfilename api doesn't tell us if we got more than
+ // one file, so we have to test manually by checking string
+ // lengths.
+ if( wcslen(mOFN.lpstrFile) > mOFN.nFileOffset )
+ {
+ mMultiFile = FALSE;
+ mCurrentFile = mFiles;
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ }
+ else
+ {
+ mMultiFile = TRUE;
+ mCurrentFile = 0;
+ mLocked = TRUE;
+ WCHAR* tptrw = mFilesW;
+ char* tptr = mFiles;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ while(1)
+ {
+ if (*tptrw == 0 && *(tptrw+1) == 0) // double '\0'
+ break;
+ if (*tptrw == 0 && !mCurrentFile)
+ mCurrentFile = tptr+1;
+ S32 tlen16,tlen8;
+ tlen16 = utf16chars_to_utf8chars(tptrw, tptr, &tlen8);
+ tptrw += tlen16;
+ tptr += tlen8;
+ }
+ }
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const char* filename)
+{
+ if( mLocked )
+ {
+ return FALSE;
+ }
+ BOOL success = FALSE;
+ mMultiFile = FALSE;
+
+ mOFN.lpstrFile = mFilesW;
+ if (filename)
+ {
+ llutf16string tstring = utf8str_to_utf16str(filename);
+ wcsncpy(mFilesW, tstring.c_str(), FILENAME_BUFFER_SIZE); }
+ else
+ {
+ mFilesW[0] = '\0';
+ }
+ mOFN.hwndOwner = llwindow_get_hwnd(gViewerWindow->getWindow());
+
+ switch( filter )
+ {
+ case FFSAVE_ALL:
+ mOFN.lpstrDefExt = NULL;
+ mOFN.lpstrFilter =
+ L"All Files (*.*)\0*.*\0" \
+ L"WAV Sounds (*.wav)\0*.wav\0" \
+ L"Targa, Bitmap Images (*.tga; *.bmp)\0*.tga;*.bmp\0" \
+ L"\0";
+ break;
+ case FFSAVE_WAV:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.wav", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"wav";
+ L"WAV Sounds (*.wav)\0*.wav\0" \
+ L"\0";
+ break;
+ case FFSAVE_TGA:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.tga", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"tga";
+ mOFN.lpstrFilter =
+ L"Targa Images (*.tga)\0*.tga\0" \
+ L"\0";
+ break;
+ case FFSAVE_BMP:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.bmp", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"bmp";
+ mOFN.lpstrFilter =
+ L"Bitmap Images (*.bmp)\0*.bmp\0" \
+ L"\0";
+ break;
+ case FFSAVE_AVI:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.avi", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"avi";
+ mOFN.lpstrFilter =
+ L"AVI Movie File (*.avi)\0*.avi\0" \
+ L"\0";
+ break;
+ case FFSAVE_ANIM:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.xaf", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"xaf";
+ mOFN.lpstrFilter =
+ L"XAF Anim File (*.xaf)\0*.xaf\0" \
+ L"\0";
+ break;
+#ifdef _CORY_TESTING
+ case FFSAVE_GEOMETRY:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.slg", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"slg";
+ mOFN.lpstrFilter =
+ L"SLG SL Geometry File (*.slg)\0*.slg\0" \
+ L"\0";
+ break;
+#endif
+ case FFSAVE_XML:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.xml", FILENAME_BUFFER_SIZE);
+ }
+
+ mOFN.lpstrDefExt = L"xml";
+ mOFN.lpstrFilter =
+ L"XML File (*.xml)\0*.xml\0" \
+ L"\0";
+ break;
+ case FFSAVE_COLLADA:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.collada", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"collada";
+ mOFN.lpstrFilter =
+ L"COLLADA File (*.collada)\0*.collada\0" \
+ L"\0";
+ break;
+ case FFSAVE_RAW:
+ if (!filename)
+ {
+ wcsncpy( mFilesW,L"untitled.raw", FILENAME_BUFFER_SIZE);
+ }
+ mOFN.lpstrDefExt = L"raw";
+ mOFN.lpstrFilter = RAW_FILTER \
+ L"\0";
+ break;
+ default:
+ return FALSE;
+ }
+
+
+ mOFN.nMaxFile = SINGLE_FILENAME_BUFFER_SIZE;
+ mOFN.Flags = OFN_OVERWRITEPROMPT | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ // NOTA BENE: hitting the file dialog triggers a window focus event, destroying the selection manager!!
+ success = GetSaveFileName(&mOFN);
+ if (success)
+ {
+ LLString tstr = utf16str_to_utf8str(llutf16string(mFilesW));
+ memcpy(mFiles, tstr.c_str(), tstr.size()+1);
+ mCurrentFile = mFiles;
+ }
+ gKeyboard->resetKeys();
+ }
+ send_agent_resume();
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ if(mMultiFile)
+ {
+ buildFilename();
+ return mFilename;
+ }
+ return mFiles;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if(mMultiFile)
+ {
+ mCurrentFile += strlen(mCurrentFile) + 1;
+ if( '\0' != mCurrentFile[0] )
+ {
+ buildFilename();
+ return mFilename;
+ }
+ else
+ {
+ mLocked = FALSE;
+ }
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ if( '\0' != mCurrentFile[0] )
+ {
+ return mCurrentFile;
+ }
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ mLocked = FALSE;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ memset( mFilename, 0, LL_MAX_PATH );
+ mCurrentFile = mFiles;
+}
+
+void LLFilePicker::buildFilename( void )
+{
+ strncpy( mFilename, mFiles, LL_MAX_PATH );
+ S32 len = strlen( mFilename );
+
+ strcat(mFilename,gDirUtilp->getDirDelimiter().c_str());
+ len += strlen(gDirUtilp->getDirDelimiter().c_str());
+
+// mFilename[len++] = '\\';
+ LLString::copy( mFilename + len, mCurrentFile, LL_MAX_PATH - len );
+}
+
+#elif LL_DARWIN
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+
+ memset(&mNavOptions, 0, sizeof(mNavOptions));
+ OSStatus error = NavGetDefaultDialogCreationOptions(&mNavOptions);
+ if (error == noErr)
+ {
+ mNavOptions.modality = kWindowModalityAppModal;
+ }
+ mFileIndex = 0;
+}
+
+LLFilePicker::~LLFilePicker()
+{
+ // nothing
+}
+
+Boolean LLFilePicker::navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode)
+{
+ Boolean result = true;
+ ELoadFilter filter = *((ELoadFilter*) callBackUD);
+ OSStatus error = noErr;
+
+ if (filterMode == kNavFilteringBrowserList && filter != FFLOAD_ALL && (theItem->descriptorType == typeFSRef || theItem->descriptorType == typeFSS))
+ {
+ // navInfo is only valid for typeFSRef and typeFSS
+ NavFileOrFolderInfo *navInfo = (NavFileOrFolderInfo*) info;
+ if (!navInfo->isFolder)
+ {
+ AEDesc desc;
+ error = AECoerceDesc(theItem, typeFSRef, &desc);
+ if (error == noErr)
+ {
+ FSRef fileRef;
+ error = AEGetDescData(&desc, &fileRef, sizeof(fileRef));
+ if (error == noErr)
+ {
+ LSItemInfoRecord fileInfo;
+ error = LSCopyItemInfoForRef(&fileRef, kLSRequestExtension | kLSRequestTypeCreator, &fileInfo);
+ if (error == noErr)
+ {
+ if (filter == FFLOAD_IMAGE)
+ {
+ if (fileInfo.filetype != 'JPEG' && fileInfo.filetype != 'JPG ' &&
+ fileInfo.filetype != 'BMP ' && fileInfo.filetype != 'TGA ' &&
+ fileInfo.filetype != 'BMPf' && fileInfo.filetype != 'TPIC' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("jpeg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("jpg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("bmp"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("tga"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+ else if (filter == FFLOAD_WAV)
+ {
+ if (fileInfo.filetype != 'WAVE' && fileInfo.filetype != 'WAV ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("wave"), kCFCompareCaseInsensitive) != kCFCompareEqualTo &&
+ CFStringCompare(fileInfo.extension, CFSTR("wav"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+ else if (filter == FFLOAD_ANIM)
+ {
+ if (fileInfo.filetype != 'BVH ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("bvh"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+#ifdef _CORY_TESTING
+ else if (filter == FFLOAD_GEOMETRY)
+ {
+ if (fileInfo.filetype != 'SLG ' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("slg"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+#endif
+ else if (filter == FFLOAD_SLOBJECT)
+ {
+ llwarns << "*** navOpenFilterProc: FFLOAD_SLOBJECT NOT IMPLEMENTED ***" << llendl;
+ }
+ else if (filter == FFLOAD_RAW)
+ {
+ if (fileInfo.filetype != '\?\?\?\?' &&
+ (fileInfo.extension && (CFStringCompare(fileInfo.extension, CFSTR("raw"), kCFCompareCaseInsensitive) != kCFCompareEqualTo))
+ )
+ {
+ result = false;
+ }
+ }
+
+ if (fileInfo.extension)
+ {
+ CFRelease(fileInfo.extension);
+ }
+ }
+ }
+ AEDisposeDesc(&desc);
+ }
+ }
+ }
+ return result;
+}
+
+OSStatus LLFilePicker::doNavChooseDialog(ELoadFilter filter)
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+ mFiles[0] = '\0';
+ mFileVector.clear();
+
+ // NOTE: we are passing the address of a local variable here.
+ // This is fine, because the object this call creates will exist for less than the lifetime of this function.
+ // (It is destroyed by NavDialogDispose() below.)
+ error = NavCreateChooseFileDialog(&mNavOptions, NULL, NULL, NULL, navOpenFilterProc, (void*)(&filter), &navRef);
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ SInt32 count = 0;
+ SInt32 index;
+
+ // AE indexes are 1 based...
+ error = AECountItems(&navReply.selection, &count);
+ for (index = 1; index <= count; index++)
+ {
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+ char path[MAX_PATH];
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, index, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ error = FSRefMakePath(&fsRef, (UInt8*) path, sizeof(path));
+
+ if (error == noErr)
+ mFileVector.push_back(LLString(path));
+ }
+ }
+
+ return error;
+}
+
+OSStatus LLFilePicker::doNavSaveDialog(ESaveFilter filter, const char* filename)
+{
+ OSStatus error = noErr;
+ NavDialogRef navRef = NULL;
+ NavReplyRecord navReply;
+
+ memset(&navReply, 0, sizeof(navReply));
+ mFiles[0] = '\0';
+ mFileVector.clear();
+
+ // Setup the type, creator, and extension
+ OSType type, creator;
+ CFStringRef extension = NULL;
+ switch (filter)
+ {
+ case FFSAVE_WAV:
+ type = 'WAVE';
+ creator = 'TVOD';
+ extension = CFSTR(".wav");
+ break;
+
+ case FFSAVE_TGA:
+ type = 'TPIC';
+ creator = 'prvw';
+ extension = CFSTR(".tga");
+ break;
+
+ case FFSAVE_BMP:
+ type = 'BMPf';
+ creator = 'prvw';
+ extension = CFSTR(".bmp");
+ break;
+
+ case FFSAVE_AVI:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".mov");
+ break;
+
+ case FFSAVE_ANIM:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".xaf");
+ break;
+
+#ifdef _CORY_TESTING
+ case FFSAVE_GEOMETRY:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".slg");
+ break;
+#endif
+ case FFSAVE_RAW:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR(".raw");
+ break;
+
+ case FFSAVE_ALL:
+ default:
+ type = '\?\?\?\?';
+ creator = '\?\?\?\?';
+ extension = CFSTR("");
+ break;
+ }
+
+ // Create the dialog
+ error = NavCreatePutFileDialog(&mNavOptions, type, creator, NULL, NULL, &navRef);
+ if (error == noErr)
+ {
+ CFStringRef nameString = NULL;
+ bool hasExtension = true;
+
+ // Create a CFString of the initial file name
+ if (filename)
+ nameString = CFStringCreateWithCString(NULL, filename, kCFStringEncodingUTF8);
+ else
+ nameString = CFSTR("Untitled");
+
+ // Add the extension if one was not provided
+ if (nameString && !CFStringHasSuffix(nameString, extension))
+ {
+ CFStringRef tempString = nameString;
+ hasExtension = false;
+ nameString = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@%@"), tempString, extension);
+ CFRelease(tempString);
+ }
+
+ // Set the name in the dialog
+ if (nameString)
+ {
+ error = NavDialogSetSaveFileName(navRef, nameString);
+ CFRelease(nameString);
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ // Run the dialog
+ if (error == noErr)
+ error = NavDialogRun(navRef);
+
+ gViewerWindow->mWindow->afterDialog();
+
+ if (error == noErr)
+ error = NavDialogGetReply(navRef, &navReply);
+
+ if (navRef)
+ NavDialogDispose(navRef);
+
+ if (error == noErr && navReply.validRecord)
+ {
+ SInt32 count = 0;
+
+ // AE indexes are 1 based...
+ error = AECountItems(&navReply.selection, &count);
+ if (count > 0)
+ {
+ // Get the FSRef to the containing folder
+ FSRef fsRef;
+ AEKeyword theAEKeyword;
+ DescType typeCode;
+ Size actualSize = 0;
+
+ memset(&fsRef, 0, sizeof(fsRef));
+ error = AEGetNthPtr(&navReply.selection, 1, typeFSRef, &theAEKeyword, &typeCode, &fsRef, sizeof(fsRef), &actualSize);
+
+ if (error == noErr)
+ {
+ char path[PATH_MAX];
+ char newFileName[SINGLE_FILENAME_BUFFER_SIZE];
+
+ error = FSRefMakePath(&fsRef, (UInt8*)path, PATH_MAX);
+ if (error == noErr)
+ {
+ if (CFStringGetCString(navReply.saveFileName, newFileName, sizeof(newFileName), kCFStringEncodingUTF8))
+ {
+ mFileVector.push_back(LLString(path) + LLString("/") + LLString(newFileName));
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+ else
+ {
+ error = paramErr;
+ }
+ }
+ }
+ }
+
+ return error;
+}
+
+BOOL LLFilePicker::getOpenFile(ELoadFilter filter)
+{
+ if( mLocked ) return FALSE;
+ mMultiFile = FALSE;
+ BOOL success = FALSE;
+
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog(filter);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles(ELoadFilter filter)
+{
+ if( mLocked ) return FALSE;
+ mMultiFile = TRUE;
+ BOOL success = FALSE;
+
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mNavOptions.optionFlags |= kNavAllowMultipleFiles;
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavChooseDialog(filter);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ if (mFileVector.size() > 1)
+ mLocked = TRUE;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+void LLFilePicker::getFilePath(SInt32 index)
+{
+ mFiles[0] = 0;
+ if (mFileVector.size())
+ strcpy(mFiles, mFileVector[index].c_str());
+}
+
+void LLFilePicker::getFileName(SInt32 index)
+{
+ mFilename[0] = 0;
+ if (mFileVector.size())
+ {
+ char *start = strrchr(mFileVector[index].c_str(), '/');
+ if (start && ((start + 1 - mFileVector[index].c_str()) < (mFileVector[index].size())))
+ strcpy(mFilename, start + 1);
+ }
+}
+
+BOOL LLFilePicker::getSaveFile(ESaveFilter filter, const char* filename)
+{
+ if( mLocked ) return FALSE;
+ BOOL success = FALSE;
+ OSStatus error = noErr;
+
+ mFileVector.clear();
+ mMultiFile = FALSE;
+ mNavOptions.optionFlags &= ~kNavAllowMultipleFiles;
+
+ // Modal, so pause agent
+ send_agent_pause();
+ {
+ error = doNavSaveDialog(filter, filename);
+ }
+ send_agent_resume();
+ if (error == noErr)
+ {
+ if (mFileVector.size())
+ success = true;
+ }
+
+ // Account for the fact that the app has been stalled.
+ LLFrameTimer::updateFrameTime();
+ return success;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ mFileIndex = 0;
+ getFilePath(mFileIndex);
+ return mFiles;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if(mMultiFile)
+ {
+ mFileIndex++;
+ if (mFileIndex < mFileVector.size())
+ {
+ getFilePath(mFileIndex);
+ return mFiles;
+ }
+ else
+ {
+ mLocked = FALSE;
+ }
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ if (mFileIndex < mFileVector.size())
+ {
+ getFileName(mFileIndex);
+ return mFilename;
+ }
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ mLocked = FALSE;
+ memset( mFiles, 0, FILENAME_BUFFER_SIZE );
+ memset( mFilename, 0, LL_MAX_PATH );
+ mCurrentFile = mFiles;
+
+ mFileIndex = 0;
+ mFileVector.clear();
+}
+
+#elif LL_LINUX
+
+# if LL_GTK
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+static void store_filenames(GtkWidget *widget, gpointer user_data) {
+ StoreFilenamesStruct *sfs = (StoreFilenamesStruct*) user_data;
+ GtkWidget *win = sfs->win;
+
+ llinfos <<"store_filesnames: marker A" << llendl;
+
+ // get NULL-terminated list of strings allocated for us by GTK
+ gchar** string_list =
+ gtk_file_selection_get_selections(GTK_FILE_SELECTION(win));
+
+ llinfos <<"store_filesnames: marker B" << llendl;
+
+ int idx = 0;
+ while (string_list[idx])
+ {
+ // platform-string to utf8
+ gchar* filename_utf8 = g_filename_from_utf8(string_list[idx],
+ -1, NULL,
+ NULL,
+ NULL);
+ sfs->fileVector.push_back(LLString(filename_utf8));
+ ++idx;
+ }
+
+ llinfos <<"store_filesnames: marker C" << llendl;
+
+ g_strfreev(string_list);
+
+ llinfos <<"store_filesnames: marker D" << llendl;
+
+ llinfos << sfs->fileVector.size() << " filename(s) selected:" << llendl;
+ U32 x;
+ for (x=0; x<sfs->fileVector.size(); ++x)
+ llinfos << x << ":" << sfs->fileVector[x] << llendl;
+}
+
+GtkWindow* LLFilePicker::buildFilePicker(void)
+{
+ gtk_disable_setlocale();
+ if (gtk_init_check(NULL, NULL) &&
+ ! gViewerWindow->getWindow()->getFullscreen())
+ {
+ GtkWidget *win = NULL;
+
+ win = gtk_file_selection_new(NULL);
+ mStoreFilenames.win = win;
+
+# if LL_X11
+ // Make GTK tell the window manager to associate this
+ // dialog with our non-GTK raw X11 window, which should try
+ // to keep it on top etc.
+ Window *XWindowID_ptr = (Window*) gViewerWindow->
+ getWindow()->getPlatformWindow();
+ if (XWindowID_ptr && None != *XWindowID_ptr)
+ {
+ gtk_widget_realize(GTK_WIDGET(win)); // so we can get its gdkwin
+ GdkWindow *gdkwin = gdk_window_foreign_new(*XWindowID_ptr);
+ gdk_window_set_transient_for(GTK_WIDGET(win)->window,
+ gdkwin);
+ }
+ else
+ {
+ llwarns << "Hmm, couldn't get xwid from LLWindow." << llendl;
+ }
+# endif //LL_X11
+
+ g_signal_connect (G_OBJECT(win), "destroy",
+ G_CALLBACK(gtk_main_quit), NULL);
+
+ // on 'ok', grab the file selection list
+ g_signal_connect (GTK_FILE_SELECTION(win)->ok_button,
+ "clicked",
+ G_CALLBACK(store_filenames),
+ &mStoreFilenames);
+
+ // both 'ok' and 'cancel' will end the dialog
+ g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(win)->
+ ok_button),
+ "clicked",
+ G_CALLBACK(gtk_widget_destroy),
+ G_OBJECT(win));
+ g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(win)->
+ cancel_button),
+ "clicked",
+ G_CALLBACK(gtk_widget_destroy),
+ G_OBJECT(win));
+
+ gtk_file_selection_show_fileop_buttons(GTK_FILE_SELECTION(win));
+ gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(win),
+ FALSE);
+
+ gtk_window_set_modal(GTK_WINDOW(win), TRUE);
+
+ return GTK_WINDOW(win);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ std::string suggest_name = "untitled";
+ std::string suggest_ext = "";
+ std::string caption = "Save ";
+ switch (filter)
+ {
+ case FFSAVE_WAV:
+ caption += "Sounds (*.wav)";
+ suggest_ext += ".wav";
+ break;
+ case FFSAVE_TGA:
+ caption += "Targa Images (*.tga)";
+ suggest_ext += ".tga";
+ break;
+ case FFSAVE_BMP:
+ caption += "Bitmap Images (*.bmp)";
+ suggest_ext += ".bmp";
+ break;
+ case FFSAVE_AVI:
+ caption += "AVI Movie File (*.avi)";
+ suggest_ext += ".avi";
+ break;
+ case FFSAVE_ANIM:
+ caption += "XAF Anim File (*.xaf)";
+ suggest_ext += ".xaf";
+ break;
+ case FFSAVE_XML:
+ caption += "XML File (*.xml)";
+ suggest_ext += ".xml";
+ break;
+ case FFSAVE_RAW:
+ caption += "RAW File (*.raw)";
+ suggest_ext += ".raw";
+ break;
+ default:;
+ break;
+ }
+
+ gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
+
+ if (!filename)
+ {
+ suggest_name += suggest_ext;
+
+ gtk_file_selection_set_filename
+ (GTK_FILE_SELECTION(picker),
+ g_filename_from_utf8(suggest_name.c_str(),
+ -1, NULL,
+ NULL,
+ NULL));
+ gtk_editable_select_region(GTK_EDITABLE(GTK_FILE_SELECTION(picker)->selection_entry), 0, suggest_name.length() - suggest_ext.length() );
+ }
+ else
+ {
+ gtk_file_selection_set_filename
+ (GTK_FILE_SELECTION(picker),
+ g_filename_from_utf8(filename,
+ -1, NULL,
+ NULL,
+ NULL));
+ gtk_editable_select_region(GTK_EDITABLE(GTK_FILE_SELECTION(picker)->selection_entry), 0, -1 );
+ }
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = (mStoreFilenames.fileVector.size() == 1);
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ std::string caption = "Load ";
+ switch (filter)
+ {
+ case FFLOAD_WAV:
+ caption += "Sounds (*.wav)"; break;
+ case FFLOAD_ANIM:
+ caption += "Animations (*.bvh)"; break;
+ case FFLOAD_IMAGE:
+ caption += "Images (*.tga; *.bmp; *.jpg; *.jpeg)"; break;
+ default:;
+ break;
+ }
+
+ gtk_window_set_title(GTK_WINDOW(picker), caption.c_str());
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = (mStoreFilenames.fileVector.size() == 1);
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ BOOL rtn = FALSE;
+
+ gViewerWindow->mWindow->beforeDialog();
+
+ reset();
+ GtkWindow* picker = buildFilePicker();
+
+ if (picker)
+ {
+ gtk_file_selection_set_select_multiple(GTK_FILE_SELECTION(picker),
+ TRUE);
+
+ gtk_window_set_title(GTK_WINDOW(picker), "Load Files");
+
+ gtk_widget_show_all(GTK_WIDGET(picker));
+ gtk_main();
+ rtn = !mStoreFilenames.fileVector.empty();
+ }
+
+ gViewerWindow->mWindow->afterDialog();
+
+ return rtn;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ mNextFileIndex = 0;
+ return getNextFile();
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ if (mStoreFilenames.fileVector.size() > mNextFileIndex)
+ return mStoreFilenames.fileVector[mNextFileIndex++].c_str();
+ else
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ // getDirname is badly named... it really means getBasename.
+ S32 index = mNextFileIndex - 1; // want index before the 'next' cursor
+ if (index >= 0 && index < (S32)mStoreFilenames.fileVector.size())
+ {
+ // we do this using C strings so we don't have to
+ // convert a LLString/std::string character offset into a
+ // byte-offset for the return (which is a C string anyway).
+ const char* dirsep = gDirUtilp->getDirDelimiter().c_str();
+ const char* fullpath = mStoreFilenames.fileVector[index].c_str();
+ const char* finalpart = NULL;
+ const char* thispart = fullpath;
+ // walk through the string looking for the final dirsep, i.e. /
+ do
+ {
+ thispart = strstr(thispart, dirsep);
+ if (NULL != thispart)
+ finalpart = thispart = &thispart[1];
+ }
+ while (NULL != thispart);
+ return finalpart;
+ }
+ else
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+ llinfos << "GTK LLFilePicker::reset()" << llendl;
+ mNextFileIndex = 0;
+ mStoreFilenames.win = NULL;
+ mStoreFilenames.fileVector.clear();
+}
+
+# else // LL_GTK
+
+// Hacky stubs designed to facilitate fake getSaveFile and getOpenFile with
+// static results, when we don't have a real filepicker.
+
+static LLString hackyfilename;
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ llinfos << "getSaveFile suggested filename is [" << filename
+ << "]" << llendl;
+ if (filename && filename[0])
+ {
+ hackyfilename.assign(gDirUtilp->getLindenUserDir());
+ hackyfilename += gDirUtilp->getDirDelimiter();
+ hackyfilename += filename;
+ return TRUE;
+ }
+ hackyfilename.clear();
+ return FALSE;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ // HACK: Static filenames for 'open' until we implement filepicker
+ hackyfilename.assign(gDirUtilp->getLindenUserDir());
+ hackyfilename += gDirUtilp->getDirDelimiter();
+ hackyfilename += "upload";
+ switch (filter)
+ {
+ case FFLOAD_WAV: hackyfilename += ".wav"; break;
+ case FFLOAD_IMAGE: hackyfilename += ".tga"; break;
+ case FFLOAD_ANIM: hackyfilename += ".bvh"; break;
+ default: break;
+ }
+ llinfos << "getOpenFile: Will try to open file: " << hackyfilename
+ << llendl;
+ return TRUE;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ hackyfilename.clear();
+ return FALSE;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ if (!hackyfilename.empty())
+ {
+ return hackyfilename.c_str();
+ }
+ return NULL;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ hackyfilename.clear();
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+}
+#endif // LL_GTK
+
+#else // not implemented
+
+LLFilePicker::LLFilePicker()
+{
+ reset();
+}
+
+LLFilePicker::~LLFilePicker()
+{
+}
+
+BOOL LLFilePicker::getSaveFile( ESaveFilter filter, const char* filename )
+{
+ return FALSE;
+}
+
+BOOL LLFilePicker::getOpenFile( ELoadFilter filter )
+{
+ return FALSE;
+}
+
+BOOL LLFilePicker::getMultipleOpenFiles( ELoadFilter filter )
+{
+ return FALSE;
+}
+
+const char* LLFilePicker::getFirstFile()
+{
+ return NULL;
+}
+
+const char* LLFilePicker::getNextFile()
+{
+ return NULL;
+}
+
+const char* LLFilePicker::getDirname()
+{
+ return NULL;
+}
+
+void LLFilePicker::reset()
+{
+}
+
+#endif
diff --git a/indra/newview/llfilepicker.h b/indra/newview/llfilepicker.h
new file mode 100644
index 0000000000..718af39c5d
--- /dev/null
+++ b/indra/newview/llfilepicker.h
@@ -0,0 +1,167 @@
+/**
+ * @file llfilepicker.h
+ * @brief OS-specific file picker
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// OS specific file selection dialog. This is implemented as a
+// singleton class, so call the instance() method to get the working
+// instance. When you call getMultipleOpenFile(), it locks the picker
+// until you iterate to the end of the list of selected files with
+// getNextFile() or call reset().
+
+#ifndef LL_LLFILEPICKER_H
+#define LL_LLFILEPICKER_H
+
+#include "stdtypes.h"
+
+#if LL_DARWIN
+#include <Carbon/Carbon.h>
+
+// AssertMacros.h does bad things.
+#undef verify
+#undef check
+#undef require
+
+#include <vector>
+#include "llstring.h"
+
+#endif
+
+// Need commdlg.h for OPENFILENAMEA
+#ifdef LL_WINDOWS
+#include <commdlg.h>
+#endif
+
+// mostly for Linux, possible on others
+#if LL_GTK
+# include "gtk/gtk.h"
+#endif // LL_GTK
+
+// also mostly for Linux, for some X11-specific filepicker usability tweaks
+#if LL_X11
+#include "SDL/SDL_syswm.h"
+#endif
+
+#if LL_GTK
+// we use an aggregate structure so we can pass its pointer through a C callback
+typedef struct {
+ GtkWidget *win;
+ std::vector<LLString> fileVector;
+} StoreFilenamesStruct;
+#endif // LL_GTK
+
+class LLFilePicker
+{
+public:
+ // calling this before main() is undefined
+ static LLFilePicker& instance( void ) { return sInstance; }
+
+ enum ELoadFilter
+ {
+ FFLOAD_ALL = 1,
+ FFLOAD_WAV = 2,
+ FFLOAD_IMAGE = 3,
+ FFLOAD_ANIM = 4,
+#ifdef _CORY_TESTING
+ FFLOAD_GEOMETRY = 5,
+#endif
+ FFLOAD_XML = 6,
+ FFLOAD_SLOBJECT = 7,
+ FFLOAD_RAW = 8,
+ };
+
+ enum ESaveFilter
+ {
+ FFSAVE_ALL = 1,
+ FFSAVE_WAV = 3,
+ FFSAVE_TGA = 4,
+ FFSAVE_BMP = 5,
+ FFSAVE_AVI = 6,
+ FFSAVE_ANIM = 7,
+#ifdef _CORY_TESTING
+ FFSAVE_GEOMETRY = 8,
+#endif
+ FFSAVE_XML = 9,
+ FFSAVE_COLLADA = 10,
+ FFSAVE_RAW = 11,
+ };
+
+ // open the dialog. This is a modal operation
+ BOOL getSaveFile( ESaveFilter filter = FFSAVE_ALL, const char* filename = NULL );
+ BOOL getOpenFile( ELoadFilter filter = FFLOAD_ALL );
+ BOOL getMultipleOpenFiles( ELoadFilter filter = FFLOAD_ALL );
+
+ // Get the filename(s) found. getFirstFile() sets the pointer to
+ // the start of the structure and allows the start of iteration.
+ const char* getFirstFile();
+
+ // getNextFile() increments the internal representation and
+ // returns the next file specified by the user. Returns NULL when
+ // no more files are left. Further calls to getNextFile() are
+ // undefined.
+ const char* getNextFile();
+
+ // This utility function extracts the directory name but doesn't
+ // do any incrementing. This is currently only supported when
+ // you're opening multiple files.
+ const char* getDirname();
+
+ // clear any lists of buffers or whatever, and make sure the file
+ // picker isn't locked.
+ void reset();
+
+private:
+ enum
+ {
+ SINGLE_FILENAME_BUFFER_SIZE = 1024,
+ //FILENAME_BUFFER_SIZE = 65536
+ FILENAME_BUFFER_SIZE = 65000
+ };
+
+ void buildFilename( void );
+
+#if LL_WINDOWS
+ OPENFILENAMEW mOFN; // for open and save dialogs
+ char *mOpenFilter;
+ WCHAR mFilesW[FILENAME_BUFFER_SIZE];
+
+ BOOL setupFilter(ELoadFilter filter);
+#endif
+
+#if LL_DARWIN
+ NavDialogCreationOptions mNavOptions;
+ std::vector<LLString> mFileVector;
+ UInt32 mFileIndex;
+
+ OSStatus doNavChooseDialog(ELoadFilter filter);
+ OSStatus doNavSaveDialog(ESaveFilter filter, const char* filename);
+ void getFilePath(SInt32 index);
+ void getFileName(SInt32 index);
+ static Boolean navOpenFilterProc(AEDesc *theItem, void *info, void *callBackUD, NavFilterModes filterMode);
+#endif
+
+#if LL_GTK
+ StoreFilenamesStruct mStoreFilenames;
+
+ GtkWindow* buildFilePicker(void);
+ U32 mNextFileIndex;
+#endif
+
+ char mFiles[FILENAME_BUFFER_SIZE];
+ char mFilename[LL_MAX_PATH];
+ char* mCurrentFile;
+ BOOL mLocked;
+ BOOL mMultiFile;
+
+ static LLFilePicker sInstance;
+
+public:
+ // don't call these directly please.
+ LLFilePicker();
+ ~LLFilePicker();
+};
+
+#endif
diff --git a/indra/newview/llfirstuse.cpp b/indra/newview/llfirstuse.cpp
new file mode 100644
index 0000000000..4a4e1a9ed4
--- /dev/null
+++ b/indra/newview/llfirstuse.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llfirstuse.cpp
+ * @brief Methods that spawn "first-use" dialogs
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfirstuse.h"
+
+// library includes
+#include "indra_constants.h"
+
+// viewer includes
+#include "llnotify.h"
+#include "llviewercontrol.h"
+#include "llui.h"
+#include "viewer.h"
+
+// static
+std::set<LLString> LLFirstUse::sConfigVariables;
+
+// static
+void LLFirstUse::addConfigVariable(const LLString& var)
+{
+ gSavedSettings.addWarning(var);
+ sConfigVariables.insert(var);
+}
+
+// static
+void LLFirstUse::disableFirstUse()
+{
+ // Set all first-use warnings to disabled
+ for (std::set<LLString>::iterator iter = sConfigVariables.begin();
+ iter != sConfigVariables.end(); ++iter)
+ {
+ gSavedSettings.setWarning(*iter, FALSE);
+ }
+}
+
+// Called whenever the viewer detects that your balance went up
+void LLFirstUse::useBalanceIncrease(S32 delta)
+{
+ if (gSavedSettings.getWarning("FirstBalanceIncrease"))
+ {
+ gSavedSettings.setWarning("FirstBalanceIncrease", FALSE);
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",delta);
+ LLNotifyBox::showXml("FirstBalanceIncrease", args);
+ }
+}
+
+
+// Called whenever the viewer detects your balance went down
+void LLFirstUse::useBalanceDecrease(S32 delta)
+{
+ if (gSavedSettings.getWarning("FirstBalanceDecrease"))
+ {
+ gSavedSettings.setWarning("FirstBalanceDecrease", FALSE);
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d",-delta);
+ LLNotifyBox::showXml("FirstBalanceDecrease", args);
+ }
+}
+
+
+// static
+void LLFirstUse::useSit()
+{
+ if (gSavedSettings.getWarning("FirstSit"))
+ {
+ gSavedSettings.setWarning("FirstSit", FALSE);
+
+ LLNotifyBox::showXml("FirstSit");
+ }
+}
+
+// static
+void LLFirstUse::useMap()
+{
+ if (gSavedSettings.getWarning("FirstMap"))
+ {
+ gSavedSettings.setWarning("FirstMap", FALSE);
+
+ LLNotifyBox::showXml("FirstMap");
+ }
+}
+
+// static
+void LLFirstUse::useGoTo()
+{
+ // nothing for now JC
+}
+
+// static
+void LLFirstUse::useBuild()
+{
+ if (gSavedSettings.getWarning("FirstBuild"))
+ {
+ gSavedSettings.setWarning("FirstBuild", FALSE);
+
+ LLNotifyBox::showXml("FirstBuild");
+ }
+}
+
+// static
+void LLFirstUse::useLeftClickNoHit()
+{
+ if (gSavedSettings.getWarning("FirstLeftClickNoHit"))
+ {
+ gSavedSettings.setWarning("FirstLeftClickNoHit", FALSE);
+
+ LLNotifyBox::showXml("FirstLeftClickNoHit");
+ }
+}
+
+// static
+void LLFirstUse::useTeleport()
+{
+ if (gSavedSettings.getWarning("FirstTeleport"))
+ {
+ gSavedSettings.setWarning("FirstTeleport", FALSE);
+
+ LLNotifyBox::showXml("FirstTeleport");
+ }
+}
+
+// static
+void LLFirstUse::useOverrideKeys()
+{
+ if (gSavedSettings.getWarning("FirstOverrideKeys"))
+ {
+ gSavedSettings.setWarning("FirstOverrideKeys", FALSE);
+
+ LLNotifyBox::showXml("FirstOverrideKeys");
+ }
+}
+
+// static
+void LLFirstUse::useAttach()
+{
+ // nothing for now
+}
+
+// static
+void LLFirstUse::useAppearance()
+{
+ if (gSavedSettings.getWarning("FirstAppearance"))
+ {
+ gSavedSettings.setWarning("FirstAppearance", FALSE);
+
+ LLNotifyBox::showXml("FirstAppearance");
+ }
+}
+
+// static
+void LLFirstUse::useInventory()
+{
+ if (gSavedSettings.getWarning("FirstInventory"))
+ {
+ gSavedSettings.setWarning("FirstInventory", FALSE);
+
+ LLNotifyBox::showXml("FirstInventory");
+ }
+}
+
+
+// static
+void LLFirstUse::useSandbox()
+{
+ if (gSavedSettings.getWarning("FirstSandbox"))
+ {
+ gSavedSettings.setWarning("FirstSandbox", FALSE);
+
+ LLString::format_map_t args;
+ args["[HOURS]"] = llformat("%d",SANDBOX_CLEAN_FREQ);
+ args["[TIME]"] = llformat("%d",SANDBOX_FIRST_CLEAN_HOUR);
+ LLNotifyBox::showXml("FirstSandbox", args);
+ }
+}
+
+// static
+void LLFirstUse::useFlexible()
+{
+ if (gSavedSettings.getWarning("FirstFlexible"))
+ {
+ gSavedSettings.setWarning("FirstFlexible", FALSE);
+
+ LLNotifyBox::showXml("FirstFlexible");
+ }
+}
+
diff --git a/indra/newview/llfirstuse.h b/indra/newview/llfirstuse.h
new file mode 100644
index 0000000000..af545a05a9
--- /dev/null
+++ b/indra/newview/llfirstuse.h
@@ -0,0 +1,83 @@
+/**
+ * @file llfirstuse.h
+ * @brief Methods that spawn "first-use" dialogs.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFIRSTUSE_H
+#define LL_LLFIRSTUSE_H
+
+#include <vector>
+#include "llstring.h"
+
+/*
+1. On first use of 'sit here', explain how to get up and rotate view.
+
+2. On first use of map, explain dbl-click = telport, how hubs/beacons work,
+click-drag to move map
+
+3. First use of pie 'Go To', explain other ways to move around
+
+4. First use of 'Create' or 'Edit', explain build toolbar, that you can create
+things if build enabled, edit things you own, and that you can ESC to exit it.
+
+5. First use of 'Talk to' explain difference between that and regular chat,
+reduced range, how to leave conversation, arrow keys to orbit.
+
+6. First lft-click that does nothing (land,object): Explain that rgt-click
+gives menu, lft-click interacts or moves if physical
+
+7. On first receipt of money (not rez/derez) explain that objects or people may
+give you money, and how to give someone or something money ('Pay...').
+
+8. After first teleporting and being sent to nearest hub, a dialog explaining
+how to find and move toward the beacon.
+
+9. On first accept/auto-accept permissions, explain that some objects may be
+activated by entering mouselook 'M', or may override your movement keys with
+other functions.
+
+10. FIrst use of 'wear' or drag object from inventory onto self: 'You can
+attach objects to your body by dragging ontl yourelf of rgt-clk->wear from
+object or from inventory.
+
+11. FIrst time you run the client on a system without QuickTime installed.
+
+12. First time you create a flexible object.
+*/
+
+class LLFirstUse
+{
+public:
+ // Add a config variable to be reset on resetFirstUse()
+ static void addConfigVariable(const LLString& var);
+
+ // Sets all controls back to show the dialogs.
+ static void disableFirstUse();
+
+ // These methods are called each time the appropriate action is
+ // taken. The functions themselves handle only showing the dialog
+ // the first time, or subsequent times if the user wishes.
+ static void useBalanceIncrease(S32 delta);
+ static void useBalanceDecrease(S32 delta);
+ static void useSit();
+ static void useMap();
+ static void useGoTo();
+ static void useBuild();
+ static void useLeftClickNoHit();
+ static void useTeleport();
+ static void useOverrideKeys();
+ static void useAttach();
+ static void useAppearance();
+ static void useInventory();
+ static void useSandbox();
+ static void useFlexible();
+
+protected:
+ static std::set<LLString> sConfigVariables;
+};
+
+#endif
+// EOF
diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp
new file mode 100644
index 0000000000..4f821a7978
--- /dev/null
+++ b/indra/newview/llflexibleobject.cpp
@@ -0,0 +1,832 @@
+/**
+ * @file llflexibleobject.cpp
+ * @brief Flexible object implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "pipeline.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+#include "llflexibleobject.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llviewerobject.h"
+#include "llimagegl.h"
+#include "llagent.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+
+/*static*/ LLVolumeImplFlexible::lodset_t LLVolumeImplFlexible::sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
+/*static*/ U64 LLVolumeImplFlexible::sCurrentUpdateFrame = 0;
+/*static*/ U32 LLVolumeImplFlexible::sDebugInserted = 0;
+/*static*/ U32 LLVolumeImplFlexible::sDebugVisible = 0;
+
+/*static*/ F32 LLVolumeImplFlexible::sUpdateFactor = 1.0f;
+
+// LLFlexibleObjectData::pack/unpack now in llprimitive.cpp
+
+//-----------------------------------------------
+// constructor
+//-----------------------------------------------
+LLVolumeImplFlexible::LLVolumeImplFlexible(LLViewerObject* vo, LLFlexibleObjectData* attributes) :
+ mVO(vo), mAttributes(attributes)
+{
+ mInitialized = FALSE;
+ mUpdated = FALSE;
+ mJustShifted = FALSE;
+ mInitializedRes = -1;
+ mSimulateRes = 0;
+ mFrameNum = 0;
+ mLastUpdate = 0;
+
+}//-----------------------------------------------
+
+LLVector3 LLVolumeImplFlexible::getFramePosition() const
+{
+ return mVO->getRenderPosition();
+}
+
+LLQuaternion LLVolumeImplFlexible::getFrameRotation() const
+{
+ return mVO->getRenderRotation();
+}
+
+void LLVolumeImplFlexible::onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin)
+{
+ if (param_type == LLNetworkData::PARAMS_FLEXIBLE)
+ {
+ mAttributes = (LLFlexibleObjectData*)data;
+ setAttributesOfAllSections();
+ }
+}
+
+void LLVolumeImplFlexible::onShift(const LLVector3 &shift_vector)
+{
+ for (int section = 0; section < (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1; ++section)
+ {
+ mSection[section].mPosition += shift_vector;
+ }
+ mVO->getVolume()->mBounds[0] += shift_vector;
+}
+
+//-----------------------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setParentPositionAndRotationDirectly( LLVector3 p, LLQuaternion r )
+{
+ mParentPosition = p;
+ mParentRotation = r;
+
+}//-----------------------------------------------------------------------------------------------------
+
+void LLVolumeImplFlexible::remapSections(LLFlexibleObjectSection *source, S32 source_sections,
+ LLFlexibleObjectSection *dest, S32 dest_sections)
+{
+ S32 num_output_sections = 1<<dest_sections;
+ LLVector3 scale = mVO->mDrawable->getScale();
+ F32 source_section_length = scale.mV[VZ] / (F32)(1<<source_sections);
+ F32 section_length = scale.mV[VZ] / (F32)num_output_sections;
+ if (source_sections == -1)
+ {
+ // Generate all from section 0
+ dest[0] = source[0];
+ for (S32 section=0; section<num_output_sections; ++section)
+ {
+ dest[section+1] = dest[section];
+ dest[section+1].mPosition += dest[section].mDirection * section_length;
+ dest[section+1].mVelocity.setVec( LLVector3::zero );
+ }
+ }
+ else if (source_sections > dest_sections)
+ {
+ // Copy, skipping sections
+
+ S32 num_steps = 1<<(source_sections-dest_sections);
+
+ // Copy from left to right since it may be an in-place computation
+ for (S32 section=0; section<num_output_sections; ++section)
+ {
+ dest[section+1] = source[(section+1)*num_steps];
+ }
+ dest[0] = source[0];
+ }
+ else if (source_sections < dest_sections)
+ {
+ // Interpolate section info
+ // Iterate from right to left since it may be an in-place computation
+ S32 step_shift = dest_sections-source_sections;
+ S32 num_steps = 1<<step_shift;
+ for (S32 section=num_output_sections-num_steps; section>=0; section -= num_steps)
+ {
+ LLFlexibleObjectSection *last_source_section = &source[section>>step_shift];
+ LLFlexibleObjectSection *source_section = &source[(section>>step_shift)+1];
+
+ // Cubic interpolation of position
+ // At^3 + Bt^2 + Ct + D = f(t)
+ LLVector3 D = last_source_section->mPosition;
+ LLVector3 C = last_source_section->mdPosition * source_section_length;
+ LLVector3 Y = source_section->mdPosition * source_section_length - C; // Helper var
+ LLVector3 X = (source_section->mPosition - D - C); // Helper var
+ LLVector3 A = Y - 2*X;
+ LLVector3 B = X - A;
+
+ F32 t_inc = 1.f/F32(num_steps);
+ F32 t = t_inc;
+ for (S32 step=1; step<num_steps; ++step)
+ {
+ dest[section+step].mScale =
+ lerp(last_source_section->mScale, source_section->mScale, t);
+ dest[section+step].mAxisRotation =
+ slerp(t, last_source_section->mAxisRotation, source_section->mAxisRotation);
+
+ // Evaluate output interpolated values
+ F32 t_sq = t*t;
+ dest[section+step].mPosition = t_sq*(t*A + B) + t*C + D;
+ dest[section+step].mRotation =
+ slerp(t, last_source_section->mRotation, source_section->mRotation);
+ dest[section+step].mVelocity = lerp(last_source_section->mVelocity, source_section->mVelocity, t);
+ dest[section+step].mDirection = lerp(last_source_section->mDirection, source_section->mDirection, t);
+ dest[section+step].mdPosition = lerp(last_source_section->mdPosition, source_section->mdPosition, t);
+ dest[section+num_steps] = *source_section;
+ t += t_inc;
+ }
+ }
+ dest[0] = source[0];
+ }
+ else
+ {
+ // numbers are equal. copy info
+ for (S32 section=0; section <= num_output_sections; ++section)
+ {
+ dest[section] = source[section];
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+void LLVolumeImplFlexible::setAttributesOfAllSections()
+{
+ LLVector2 bottom_scale, top_scale;
+ F32 begin_rot = 0, end_rot = 0;
+ if (mVO->getVolume())
+ {
+ const LLPathParams &params = mVO->getVolume()->getParams().getPathParams();
+ bottom_scale = params.getBeginScale();
+ top_scale = params.getEndScale();
+ begin_rot = F_PI * params.getTwistBegin();
+ end_rot = F_PI * params.getTwist();
+ }
+
+ S32 num_sections = 1 << mSimulateRes;
+
+
+ LLVector3 scale = mVO->mDrawable->getScale();
+
+ mSection[0].mPosition = getAnchorPosition();
+ mSection[0].mDirection = LLVector3::z_axis * getFrameRotation();
+ mSection[0].mdPosition = mSection[0].mDirection;
+ mSection[0].mScale.setVec(scale.mV[VX]*bottom_scale.mV[0], scale.mV[VY]*bottom_scale.mV[1]);
+ mSection[0].mVelocity.setVec(0,0,0);
+ mSection[0].mAxisRotation.setQuat(begin_rot,0,0,1);
+
+ LLVector3 parentSectionPosition = mSection[0].mPosition;
+ LLVector3 last_direction = mSection[0].mDirection;
+
+ remapSections(mSection, mInitializedRes, mSection, mSimulateRes);
+ mInitializedRes = mSimulateRes;
+
+ F32 t_inc = 1.f/F32(num_sections);
+ F32 t = t_inc;
+ for ( int i=1; i<= num_sections; i++)
+ {
+ mSection[i].mAxisRotation.setQuat(lerp(begin_rot,end_rot,t),0,0,1);
+ mSection[i].mScale = LLVector2(
+ scale.mV[VX] * lerp(bottom_scale.mV[0], top_scale.mV[0], t),
+ scale.mV[VY] * lerp(bottom_scale.mV[1], top_scale.mV[1], t));
+ t += t_inc;
+ }
+ mLastUpdate = 0;
+}//-----------------------------------------------------------------------------------
+
+
+void LLVolumeImplFlexible::onSetVolume(const LLVolumeParams &volume_params, const S32 detail)
+{
+ doIdleUpdate(gAgent, *gWorldp, 0.0);
+ if (mVO && mVO->mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mVO->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ gPipeline.markMoved(mVO->mDrawable);
+ }
+}
+
+//---------------------------------------------------------------------------------
+// This calculates the physics of the flexible object. Note that it has to be 0
+// updated every time step. In the future, perhaps there could be an
+// optimization similar to what Havok does for objects that are stationary.
+//---------------------------------------------------------------------------------
+BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mVO->mDrawable.isNull())
+ {
+ // Don't do anything until we have a drawable
+ return TRUE;
+ }
+
+ //flexible objects never go static
+ mVO->mDrawable->mQuietCount = 0;
+ if (!mVO->mDrawable->isRoot())
+ {
+ mVO->mDrawable->getParent()->mQuietCount = 0;
+ }
+
+ if (((LLVOVolume*)mVO)->mLODChanged ||
+ mVO->mDrawable->isState(LLDrawable::IN_REBUILD_Q1))
+ {
+ mLastUpdate = 0; // Force an immediate update
+ }
+ // Relegate invisible objects to the lowest priority bin
+ S32 lod = 0;
+ F32 app_angle = mVO->getAppAngle()*DEG_TO_RAD/gCamera->getView();
+ if (mVO->mDrawable->isVisible())
+ {
+ sDebugVisible++;
+ if (mVO->isSelected())
+ {
+ // Force selected objects to update *every* frame
+ lod = FLEXIBLE_OBJECT_MAX_LOD-1;
+ }
+ else
+ {
+ if (app_angle > 0)
+ {
+ lod = 5 - (S32)(1.0f/sqrtf(app_angle));
+ if (lod < 1)
+ {
+ lod = 1;
+ }
+ }
+
+ if (mVO->isAttachment())
+ {
+ lod += 3;
+ }
+ }
+ }
+
+ S32 new_res = mAttributes->getSimulateLOD();
+ // Rendering sections increases with visible angle on the screen
+ mRenderRes = (S32)(FLEXIBLE_OBJECT_MAX_SECTIONS*4*app_angle);
+ if (mRenderRes > FLEXIBLE_OBJECT_MAX_SECTIONS)
+ {
+ mRenderRes = FLEXIBLE_OBJECT_MAX_SECTIONS;
+ }
+ // Bottom cap at 1/4 the original number of sections
+ if (mRenderRes < mAttributes->getSimulateLOD()-1)
+ {
+ mRenderRes = mAttributes->getSimulateLOD()-1;
+ }
+ // Throttle back simulation of segments we're not rendering
+ if (mRenderRes < new_res)
+ {
+ new_res = mRenderRes;
+ }
+
+ if (!mInitialized || (mSimulateRes != new_res))
+ {
+ mSimulateRes = new_res;
+ setAttributesOfAllSections();
+ mInitialized = TRUE;
+ }
+
+ sLODBins[lod].insert(this);
+ sDebugInserted++;
+ return TRUE;
+}
+
+// static
+void LLVolumeImplFlexible::resetUpdateBins()
+{
+ U32 lod;
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ sLODBins[lod].clear();
+ }
+ ++sCurrentUpdateFrame;
+ sDebugInserted = 0;
+ sDebugVisible = 0;
+}
+
+inline S32 log2(S32 x)
+{
+ S32 ret = 0;
+ while (x > 1)
+ {
+ ++ret;
+ x >>= 1;
+ }
+ return ret;
+}
+
+// static
+void LLVolumeImplFlexible::doFlexibleUpdateBins()
+{
+ U32 lod;
+ U32 updated = 0;
+ U32 regen = 0;
+ U32 newflexies = 0;
+ F32 time_alloc[FLEXIBLE_OBJECT_MAX_LOD];
+ F32 total_time_alloc = 0;
+
+ bool new_objects_only = false;
+
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE))
+ {
+ new_objects_only = true;
+ }
+
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ int count = sLODBins[lod].size();
+ if (count > 0)
+ {
+ time_alloc[lod] = (F32)((lod+1)*(log2(count)));
+ }
+ else
+ {
+ time_alloc[lod] = 0;
+ }
+ total_time_alloc += time_alloc[lod];
+ }
+ total_time_alloc = FLEXIBLE_OBJECT_TIMESLICE * (sUpdateFactor+0.01f) / total_time_alloc;
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_FLEXIBLE_UPDATE);
+ LLTimer timer;
+ for (lod=0; lod<FLEXIBLE_OBJECT_MAX_LOD; ++lod)
+ {
+ LLVolumeImplFlexible::lodset_t::iterator itor = sLODBins[lod].begin();
+ int bin_count = 0;
+ if (!new_objects_only)
+ {
+ timer.reset();
+ double end_time = time_alloc[lod] * total_time_alloc;
+ for (; itor!=sLODBins[lod].end(); ++itor)
+ {
+
+ (*itor)->doFlexibleUpdate();
+ ++updated;
+ (*itor)->doFlexibleRebuild();
+ ++bin_count;
+ ++regen;
+ if (timer.getElapsedTimeF64() > end_time)
+ {
+ break;
+ }
+ }
+ }
+ for (; itor != sLODBins[lod].end(); ++itor)
+ {
+ if ((*itor)->getLastUpdate() == 0)
+ {
+ // *Always* update newly-created objects, or objects which have changed LOD
+ (*itor)->doFlexibleUpdate();
+ (*itor)->doFlexibleRebuild();
+ ++newflexies;
+ }
+ }
+ }
+ }
+}
+
+void LLVolumeImplFlexible::doFlexibleUpdate()
+{
+ LLPath *path = &mVO->getVolume()->getPath();
+ S32 num_sections = 1 << mSimulateRes;
+
+ F32 secondsThisFrame = mTimer.getElapsedTimeAndResetF32();
+ if (secondsThisFrame > 0.2f)
+ {
+ secondsThisFrame = 0.2f;
+ }
+
+ LLVector3 BasePosition = getFramePosition();
+ LLQuaternion BaseRotation = getFrameRotation();
+ LLQuaternion parentSegmentRotation = BaseRotation;
+ LLVector3 anchorDirectionRotated = LLVector3::z_axis * parentSegmentRotation;
+ LLVector3 anchorScale = mVO->mDrawable->getScale();
+
+ F32 section_length = anchorScale.mV[VZ] / (F32)num_sections;
+ F32 inv_section_length = 1.f / section_length;
+
+ S32 i;
+
+ // ANCHOR position is offset from BASE position (centroid) by half the length
+ LLVector3 AnchorPosition = BasePosition - (anchorScale.mV[VZ]/2 * anchorDirectionRotated);
+
+ mSection[0].mPosition = AnchorPosition;
+ mSection[0].mDirection = anchorDirectionRotated;
+ mSection[0].mRotation = BaseRotation;
+
+ LLQuaternion deltaRotation;
+
+ LLVector3 lastPosition;
+
+ // Coefficients which are constant across sections
+ F32 t_factor = mAttributes->getTension() * 0.1f;
+ t_factor = t_factor*(1 - pow(0.85f, secondsThisFrame*30));
+ if ( t_factor > FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE )
+ {
+ t_factor = FLEXIBLE_OBJECT_MAX_INTERNAL_TENSION_FORCE;
+ }
+
+ F32 friction_coeff = (mAttributes->getAirFriction()*2+1);
+ friction_coeff = pow(10.f, friction_coeff*secondsThisFrame);
+ friction_coeff = (friction_coeff > 1) ? friction_coeff : 1;
+ F32 momentum = 1.0f / friction_coeff;
+
+ F32 wind_factor = (mAttributes->getWindSensitivity()*0.1f) * section_length * secondsThisFrame;
+ F32 max_angle = atan(section_length*2.f);
+
+ F32 force_factor = section_length * secondsThisFrame;
+
+ // Update simulated sections
+ for (i=1; i<=num_sections; ++i)
+ {
+ LLVector3 parentSectionVector;
+ LLVector3 parentSectionPosition;
+ LLVector3 parentDirection;
+
+ //---------------------------------------------------
+ // save value of position as lastPosition
+ //---------------------------------------------------
+ lastPosition = mSection[i].mPosition;
+
+ //------------------------------------------------------------------------------------------
+ // gravity
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition.mV[2] -= mAttributes->getGravity() * force_factor;
+
+ //------------------------------------------------------------------------------------------
+ // wind force
+ //------------------------------------------------------------------------------------------
+ if (mAttributes->getWindSensitivity() > 0.001f)
+ {
+ mSection[i].mPosition += gAgent.getRegion()->mWind.getVelocity( mSection[i].mPosition ) * wind_factor;
+ }
+
+ //------------------------------------------------------------------------------------------
+ // user-defined force
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition += mAttributes->getUserForce() * force_factor;
+
+ //---------------------------------------------------
+ // tension (rigidity, stiffness)
+ //---------------------------------------------------
+ parentSectionPosition = mSection[i-1].mPosition;
+ parentDirection = mSection[i-1].mDirection;
+
+ if ( i == 1 )
+ {
+ parentSectionVector = mSection[0].mDirection;
+ }
+ else
+ {
+ parentSectionVector = mSection[i-2].mDirection;
+ }
+
+ LLVector3 currentVector = mSection[i].mPosition - parentSectionPosition;
+
+ LLVector3 difference = (parentSectionVector*section_length) - currentVector;
+ LLVector3 tensionForce = difference * t_factor;
+
+ mSection[i].mPosition += tensionForce;
+
+ //------------------------------------------------------------------------------------------
+ // sphere collision, currently not used
+ //------------------------------------------------------------------------------------------
+ /*if ( mAttributes->mUsingCollisionSphere )
+ {
+ LLVector3 vectorToCenterOfCollisionSphere = mCollisionSpherePosition - mSection[i].mPosition;
+ if ( vectorToCenterOfCollisionSphere.magVecSquared() < mCollisionSphereRadius * mCollisionSphereRadius )
+ {
+ F32 distanceToCenterOfCollisionSphere = vectorToCenterOfCollisionSphere.magVec();
+ F32 penetration = mCollisionSphereRadius - distanceToCenterOfCollisionSphere;
+
+ LLVector3 normalToCenterOfCollisionSphere;
+
+ if ( distanceToCenterOfCollisionSphere > 0.0f )
+ {
+ normalToCenterOfCollisionSphere = vectorToCenterOfCollisionSphere / distanceToCenterOfCollisionSphere;
+ }
+ else // rare
+ {
+ normalToCenterOfCollisionSphere = LLVector3::x_axis; // arbitrary
+ }
+
+ // push the position out to the surface of the collision sphere
+ mSection[i].mPosition -= normalToCenterOfCollisionSphere * penetration;
+ }
+ }*/
+
+ //------------------------------------------------------------------------------------------
+ // inertia
+ //------------------------------------------------------------------------------------------
+ mSection[i].mPosition += mSection[i].mVelocity * momentum;
+
+ //------------------------------------------------------------------------------------------
+ // clamp length & rotation
+ //------------------------------------------------------------------------------------------
+ mSection[i].mDirection = mSection[i].mPosition - parentSectionPosition;
+ mSection[i].mDirection.normVec();
+ deltaRotation.shortestArc( parentDirection, mSection[i].mDirection );
+
+ F32 angle;
+ LLVector3 axis;
+ deltaRotation.getAngleAxis(&angle, axis);
+ if (angle > F_PI) angle -= 2.f*F_PI;
+ if (angle < -F_PI) angle += 2.f*F_PI;
+ if (angle > max_angle)
+ {
+ //angle = 0.5f*(angle+max_angle);
+ deltaRotation.setQuat(max_angle, axis);
+ } else if (angle < -max_angle)
+ {
+ //angle = 0.5f*(angle-max_angle);
+ deltaRotation.setQuat(-max_angle, axis);
+ }
+ LLQuaternion segment_rotation = parentSegmentRotation * deltaRotation;
+ parentSegmentRotation = segment_rotation;
+
+ mSection[i].mDirection = (parentDirection * deltaRotation);
+ mSection[i].mPosition = parentSectionPosition + mSection[i].mDirection * section_length;
+ mSection[i].mRotation = segment_rotation;
+
+ if (i > 1)
+ {
+ // Propogate half the rotation up to the parent
+ LLQuaternion halfDeltaRotation(angle/2, axis);
+ mSection[i-1].mRotation = mSection[i-1].mRotation * halfDeltaRotation;
+ }
+
+ //------------------------------------------------------------------------------------------
+ // calculate velocity
+ //------------------------------------------------------------------------------------------
+ mSection[i].mVelocity = mSection[i].mPosition - lastPosition;
+ }
+
+ // Calculate derivatives (not necessary until normals are automagically generated)
+ mSection[0].mdPosition = (mSection[1].mPosition - mSection[0].mPosition) * inv_section_length;
+ // i = 1..NumSections-1
+ for (i=1; i<num_sections; ++i)
+ {
+ // Quadratic numerical derivative of position
+
+ // f(-L1) = aL1^2 - bL1 + c = f1
+ // f(0) = c = f2
+ // f(L2) = aL2^2 + bL2 + c = f3
+ // f = ax^2 + bx + c
+ // d/dx f = 2ax + b
+ // d/dx f(0) = b
+
+ // c = f2
+ // a = [(f1-c)/L1 + (f3-c)/L2] / (L1+L2)
+ // b = (f3-c-aL2^2)/L2
+
+ LLVector3 a = (mSection[i-1].mPosition-mSection[i].mPosition +
+ mSection[i+1].mPosition-mSection[i].mPosition) * 0.5f * inv_section_length * inv_section_length;
+ LLVector3 b = (mSection[i+1].mPosition-mSection[i].mPosition - a*(section_length*section_length));
+ b *= inv_section_length;
+
+ mSection[i].mdPosition = b;
+ }
+
+ // i = NumSections
+ mSection[i].mdPosition = (mSection[i].mPosition - mSection[i-1].mPosition) * inv_section_length;
+
+ // Create points
+ S32 num_render_sections = 1<<mRenderRes;
+ path->resizePath(num_render_sections+1);
+
+ LLPath::PathPt *new_point;
+
+ LLFlexibleObjectSection newSection[ (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1 ];
+ remapSections(mSection, mSimulateRes, newSection, mRenderRes);
+
+ for (i=0; i<=num_render_sections; ++i)
+ {
+ new_point = &path->mPath[i];
+ new_point->mPos = newSection[i].mPosition;
+ new_point->mRot = mSection[i].mAxisRotation * newSection[i].mRotation;
+ new_point->mScale = newSection[i].mScale;
+ new_point->mTexT = ((F32)i)/(num_render_sections);
+ }
+
+ mLastSegmentRotation = parentSegmentRotation;
+}
+
+void LLVolumeImplFlexible::doFlexibleRebuild()
+{
+ mVO->getVolume()->regen();
+
+ mVO->markForUpdate(TRUE);
+
+ mUpdated = TRUE;
+
+ mLastUpdate = sCurrentUpdateFrame;
+}//------------------------------------------------------------------
+
+void LLVolumeImplFlexible::onSetScale(const LLVector3& scale, BOOL damped)
+{
+ setAttributesOfAllSections();
+}
+
+BOOL LLVolumeImplFlexible::doUpdateGeometry(LLDrawable *drawable)
+{
+ BOOL compiled = FALSE;
+
+ LLVOVolume *volume = (LLVOVolume*)mVO;
+
+ if (volume->mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ volume->calcAllTEsSame();
+
+ if (volume->mVolumeChanged || volume->mFaceMappingChanged)
+ {
+ compiled = TRUE;
+ volume->regenFaces();
+ }
+ else if (volume->mLODChanged)
+ {
+ LLPointer<LLVolume> old_volumep, new_volumep;
+ F32 old_lod, new_lod;
+
+ old_volumep = volume->getVolume();
+ old_lod = old_volumep->getDetail();
+
+ LLVolumeParams volume_params = volume->getVolume()->getParams();
+ volume->setVolume(volume_params, 0);
+ doFlexibleUpdate();
+ volume->getVolume()->regen();
+
+ new_volumep = volume->getVolume();
+ new_lod = new_volumep->getDetail();
+
+ if (new_lod != old_lod)
+ {
+ compiled = TRUE;
+ if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
+ {
+ volume->regenFaces();
+ }
+ }
+ }
+
+ if (mUpdated)
+ {
+ compiled = TRUE;
+ mUpdated = FALSE;
+ }
+
+ if(compiled)
+ {
+ volume->updateRelativeXform(isVolumeGlobal());
+ volume->genTriangles(isVolumeGlobal());
+ LLPipeline::sCompiles++;
+ }
+
+ volume->mVolumeChanged = FALSE;
+ volume->mLODChanged = FALSE;
+ volume->mFaceMappingChanged = FALSE;
+
+ // clear UV flag
+ drawable->clearState(LLDrawable::UV);
+
+ drawable->movePartition();
+
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setCollisionSphere( LLVector3 p, F32 r )
+{
+ mCollisionSpherePosition = p;
+ mCollisionSphereRadius = r;
+
+}//------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setUsingCollisionSphere( bool u )
+{
+}//------------------------------------------------------------------
+
+
+//----------------------------------------------------------------------------------
+void LLVolumeImplFlexible::setRenderingCollisionSphere( bool r )
+{
+}//------------------------------------------------------------------
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getEndPosition()
+{
+ S32 num_sections = 1 << mAttributes->getSimulateLOD();
+ return mSection[ num_sections ].mPosition;
+
+}//------------------------------------------------------------------
+
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getNodePosition( int nodeIndex )
+{
+ S32 num_sections = 1 << mAttributes->getSimulateLOD();
+ if ( nodeIndex > num_sections - 1 )
+ {
+ nodeIndex = num_sections - 1;
+ }
+ else if ( nodeIndex < 0 )
+ {
+ nodeIndex = 0;
+ }
+
+ return mSection[ nodeIndex ].mPosition;
+
+}//------------------------------------------------------------------
+
+LLVector3 LLVolumeImplFlexible::getPivotPosition() const
+{
+ return getAnchorPosition();
+}
+
+//------------------------------------------------------------------
+LLVector3 LLVolumeImplFlexible::getAnchorPosition() const
+{
+ LLVector3 BasePosition = getFramePosition();
+ LLQuaternion parentSegmentRotation = getFrameRotation();
+ LLVector3 anchorDirectionRotated = LLVector3::z_axis * parentSegmentRotation;
+ LLVector3 anchorScale = mVO->mDrawable->getScale();
+ return BasePosition - (anchorScale.mV[VZ]/2 * anchorDirectionRotated);
+
+}//------------------------------------------------------------------
+
+
+//------------------------------------------------------------------
+LLQuaternion LLVolumeImplFlexible::getEndRotation()
+{
+ return mLastSegmentRotation;
+
+}//------------------------------------------------------------------
+
+
+void LLVolumeImplFlexible::updateRelativeXform(BOOL global_volume)
+{
+ LLVOVolume* vo = (LLVOVolume*) mVO;
+
+ LLVector3 delta_scale = LLVector3(1,1,1);
+ LLVector3 delta_pos;
+ LLQuaternion delta_rot;
+
+ if (!mVO->mDrawable->isRoot())
+ { //global to parent relative
+ LLViewerObject* parent = (LLViewerObject*) vo->getParent();
+ delta_rot = ~parent->getRenderRotation();
+ delta_pos = -parent->getRenderPosition()*delta_rot;
+ }
+ else
+ { //global to local
+ delta_rot = ~getFrameRotation();
+ delta_pos = -getFramePosition()*delta_rot;
+ }
+
+ // Vertex transform (4x4)
+ LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+ LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+ vo->mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(delta_pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ vo->mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+
+}
+
+const LLMatrix4& LLVolumeImplFlexible::getWorldMatrix(LLXformMatrix* xform) const
+{
+ return xform->getWorldMatrix();
+}
diff --git a/indra/newview/llflexibleobject.h b/indra/newview/llflexibleobject.h
new file mode 100644
index 0000000000..0d782d96ac
--- /dev/null
+++ b/indra/newview/llflexibleobject.h
@@ -0,0 +1,154 @@
+/**
+ * @file llflexibleobject.h
+ * @author JJ Ventrella, Andrew Meadows, Tom Yedwab
+ * @brief Flexible object definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * This is for specifying objects in the world that are animated and
+ * rendered locally - on the viewer. Flexible Objects are linear arrays
+ * of positions, which stay at a fixed distance from each other. One
+ * position is fixed as an "anchor" and is attached to some other object
+ * in the world, determined by the server. All the other positions are
+ * updated according to local physics.
+ */
+
+#ifndef LL_LLFLEXIBLEOBJECT_H
+#define LL_LLFLEXIBLEOBJECT_H
+
+#include "llmemory.h"
+#include "llprimitive.h"
+#include "llvovolume.h"
+#include "llwind.h"
+
+// 10 ms for the whole thing!
+const F32 FLEXIBLE_OBJECT_TIMESLICE = 0.003f;
+const U32 FLEXIBLE_OBJECT_MAX_LOD = 10;
+
+// See llprimitive.h for LLFlexibleObjectData and DEFAULT/MIN/MAX values
+
+//-------------------------------------------------------------------
+
+struct LLFlexibleObjectSection
+{
+ // Input parameters
+ LLVector2 mScale;
+ LLQuaternion mAxisRotation;
+ // Simulated state
+ LLVector3 mPosition;
+ LLVector3 mVelocity;
+ LLVector3 mDirection;
+ LLQuaternion mRotation;
+ // Derivatives (Not all currently used, will come back with LLVolume changes to automagically generate normals)
+ LLVector3 mdPosition;
+ //LLMatrix4 mRotScale;
+ //LLMatrix4 mdRotScale;
+};
+
+//---------------------------------------------------------
+// The LLVolumeImplFlexible class
+//---------------------------------------------------------
+class LLVolumeImplFlexible : public LLVolumeInterface
+{
+ public:
+ LLVolumeImplFlexible(LLViewerObject* volume, LLFlexibleObjectData* attributes);
+
+ // Implements LLVolumeInterface
+ LLVector3 getFramePosition() const;
+ LLQuaternion getFrameRotation() const;
+ LLVolumeInterfaceType getInterfaceType() const { return INTERFACE_FLEXIBLE; }
+ BOOL doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ BOOL doUpdateGeometry(LLDrawable *drawable);
+ LLVector3 getPivotPosition() const;
+ void onSetVolume(const LLVolumeParams &volume_params, const S32 detail);
+ void onSetScale(const LLVector3 &scale, BOOL damped);
+ void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin);
+ void onShift(const LLVector3 &shift_vector);
+ bool isVolumeUnique() const { return true; }
+ bool isVolumeGlobal() const { return true; }
+ bool isActive() const { return true; }
+ const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
+ void updateRelativeXform(BOOL global_volume = FALSE);
+ void doFlexibleUpdate(); // Called to update the simulation
+ void doFlexibleRebuild(); // Called to rebuild the geometry
+ static void resetUpdateBins();
+ static void doFlexibleUpdateBins();
+
+ //void setAttributes( LLFlexibleObjectData );
+ void setParentPositionAndRotationDirectly( LLVector3 p, LLQuaternion r );
+ void setUsingCollisionSphere( bool u );
+ void setCollisionSphere( LLVector3 position, F32 radius );
+ void setRenderingCollisionSphere( bool r);
+
+ LLVector3 getEndPosition();
+ LLQuaternion getEndRotation();
+ LLVector3 getNodePosition( int nodeIndex );
+ LLVector3 getAnchorPosition() const;
+
+ private:
+ //--------------------------------------
+ // private members
+ //--------------------------------------
+ LLViewerObject* mVO;
+ LLTimer mTimer;
+ LLVector3 mAnchorPosition;
+ LLVector3 mParentPosition;
+ LLQuaternion mParentRotation;
+ LLQuaternion mInitialAxisRotation;
+ LLQuaternion mLastSegmentRotation;
+ BOOL mInitialized;
+ BOOL mUpdated;
+ LLFlexibleObjectData* mAttributes;
+ LLFlexibleObjectSection mSection [ (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1 ];
+ S32 mInitializedRes;
+ S32 mSimulateRes;
+ S32 mRenderRes;
+ U32 mFrameNum;
+ LLVector3 mCollisionSpherePosition;
+ F32 mCollisionSphereRadius;
+
+ U64 mLastUpdate;
+
+ BOOL mJustShifted;
+
+ //--------------------------------------
+ // private methods
+ //--------------------------------------
+ void setAttributesOfAllSections ();
+
+ void remapSections(LLFlexibleObjectSection *source, S32 source_sections,
+ LLFlexibleObjectSection *dest, S32 dest_sections);
+
+ U64 getLastUpdate() const { return mLastUpdate; }
+
+ // LOD Bins
+ struct FlexCompare
+ {
+ bool operator()(LLVolumeImplFlexible* a, LLVolumeImplFlexible* b) const
+ {
+ U64 a_update = a->getLastUpdate();
+ U64 b_update = b->getLastUpdate();
+ if (a_update == b_update)
+ {
+ return a < b; // compare pointers
+ }
+ return a_update < b_update;
+ }
+ };
+ typedef std::set<LLVolumeImplFlexible*, FlexCompare> lodset_t;
+ static lodset_t sLODBins[ FLEXIBLE_OBJECT_MAX_LOD ];
+ static U64 sCurrentUpdateFrame;
+ static U32 sDebugInserted;
+ static U32 sDebugVisible;
+
+public:
+ // Global setting for update rate
+ static F32 sUpdateFactor;
+
+};// end of class definition
+
+
+#endif // LL_LLFLEXIBLEOBJECT_H
diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp
new file mode 100644
index 0000000000..d3c927a11c
--- /dev/null
+++ b/indra/newview/llfloaterabout.cpp
@@ -0,0 +1,167 @@
+/**
+ * @file llfloaterabout.cpp
+ * @author James Cook
+ * @brief The about box from Help->About
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterabout.h"
+
+#include "llsys.h"
+#include "llgl.h"
+#include "llui.h" // for tr()
+#include "v3dmath.h"
+
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llagent.h"
+#include "llviewerstats.h"
+#include "llviewerregion.h"
+#include "llversion.h"
+#include "llviewerbuild.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h" // for gViewerDigest
+
+#include "llmozlib.h"
+#include "llglheaders.h"
+
+extern LLCPUInfo gSysCPU;
+extern LLMemoryInfo gSysMemory;
+extern LLOSInfo gSysOS;
+extern U32 gPacketsIn;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLFloaterAbout* LLFloaterAbout::sInstance = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterAbout
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterAbout::LLFloaterAbout()
+: LLFloater("floater_about", "FloaterAboutRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_about.xml");
+
+ // Support for changing product name.
+ LLString title("About ");
+ title += gSecondLife;
+ setTitle(title);
+
+ LLString support;
+
+ // Version string
+ LLString version = gSecondLife
+ + llformat(" %d.%d.%d (%d) %s %s",
+ LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD,
+ __DATE__, __TIME__);
+ support.append(version);
+ support.append("\n\n");
+
+ // Position
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ //XUI:translate
+ const LLVector3d &pos = gAgent.getPositionGlobal();
+ LLString pos_text = llformat("You are at %.1f, %.1f, %.1f ",
+ pos.mdV[VX], pos.mdV[VY], pos.mdV[VZ]);
+ support.append(pos_text);
+
+ LLString region_text = llformat("in %s located at ",
+ gAgent.getRegion()->getName().c_str());
+ support.append(region_text);
+
+ char buffer[MAX_STRING];
+ gAgent.getRegion()->getHost().getHostName(buffer, MAX_STRING);
+ support.append(buffer);
+ support.append(" (");
+ gAgent.getRegion()->getHost().getString(buffer, MAX_STRING);
+ support.append(buffer);
+ support.append(")\n\n");
+ }
+
+ // CPU
+ support.append("CPU: ");
+ support.append( gSysCPU.getCPUStringTerse() );
+ support.append("\n");
+
+ U32 memory = gSysMemory.getPhysicalMemory() / 1024 / 1024;
+ // For some reason, the reported amount of memory is always wrong by one meg
+ memory++;
+
+ LLString mem_text = llformat("Memory: %u MB\n", memory );
+ support.append(mem_text);
+
+ support.append("OS Version: ");
+ support.append( gSysOS.getOSString().c_str() );
+ support.append("\n");
+
+ support.append("Graphics Card Vendor: ");
+ support.append( (const char*) glGetString(GL_VENDOR) );
+ support.append("\n");
+
+ support.append("Graphics Card: ");
+ support.append( (const char*) glGetString(GL_RENDERER) );
+ support.append("\n");
+
+ support.append("OpenGL Version: ");
+ support.append( (const char*) glGetString(GL_VERSION) );
+ support.append("\n");
+
+#if LL_LIBXUL_ENABLED
+ support.append("LLMozLib Version: ");
+ support.append( (const char*) LLMozLib::getInstance()->getVersion().c_str() );
+ support.append("\n");
+#endif // LL_LIBXUL_ENABLED
+
+ if (gViewerStats
+ && gPacketsIn > 0)
+ {
+ LLString packet_loss = llformat("Packets Lost: %.0f/%.0f (%.1f%%)",
+ gViewerStats->mPacketsLostStat.getCurrent(),
+ F32(gPacketsIn),
+ 100.f*gViewerStats->mPacketsLostStat.getCurrent() / F32(gPacketsIn) );
+ support.append(packet_loss);
+ support.append("\n");
+ }
+
+ // MD5 digest of executable
+ support.append("Viewer Digest: ");
+ char viewer_digest_string[UUID_STR_LENGTH];
+ gViewerDigest.toString( viewer_digest_string );
+ support.append(viewer_digest_string);
+
+ // Fix views
+ childDisable("credits_editor");
+ childDisable("support_editor");
+ childSetText("support_editor", support);
+
+ center();
+
+ sInstance = this;
+}
+
+// Destroys the object
+LLFloaterAbout::~LLFloaterAbout()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterAbout::show(void*)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterAbout();
+ }
+
+ sInstance->open();
+}
diff --git a/indra/newview/llfloaterabout.h b/indra/newview/llfloaterabout.h
new file mode 100644
index 0000000000..f7d73e34d6
--- /dev/null
+++ b/indra/newview/llfloaterabout.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterabout.h
+ * @brief The about box from Help -> About
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERABOUT_H
+#define LL_LLFLOATERABOUT_H
+
+#include "llfloater.h"
+
+class LLFloaterAbout
+: public LLFloater
+{
+public:
+ LLFloaterAbout();
+ virtual ~LLFloaterAbout();
+
+ static void show(void*);
+
+private:
+ static LLFloaterAbout* sInstance;
+};
+
+
+#endif // LL_LLFLOATERABOUT_H
diff --git a/indra/newview/llfloateranimpreview.cpp b/indra/newview/llfloateranimpreview.cpp
new file mode 100644
index 0000000000..5df1acac53
--- /dev/null
+++ b/indra/newview/llfloateranimpreview.cpp
@@ -0,0 +1,1139 @@
+/**
+ * @file llfloateranimpreview.cpp
+ * @brief LLFloaterAnimPreview class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateranimpreview.h"
+
+#include "llbvhloader.h"
+#include "lldatapacker.h"
+#include "lldir.h"
+#include "llvfile.h"
+#include "llapr.h"
+
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llkeyframemotion.h"
+#include "lllineeditor.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llviewermenu.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+S32 LLFloaterAnimPreview::sUploadAmount = 10;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+const F32 PREVIEW_CAMERA_DISTANCE = 4.f;
+
+const F32 MIN_CAMERA_ZOOM = 0.5f;
+const F32 MAX_CAMERA_ZOOM = 10.f;
+
+//-----------------------------------------------------------------------------
+// LLFloaterAnimPreview()
+//-----------------------------------------------------------------------------
+LLFloaterAnimPreview::LLFloaterAnimPreview(const char* filename) :
+ LLFloaterNameDesc(filename)
+{
+ mLastSliderValue = 0.f;
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+
+ mIDList["Standing"] = ANIM_AGENT_STAND;
+ mIDList["Walking"] = ANIM_AGENT_FEMALE_WALK;
+ mIDList["Sitting"] = ANIM_AGENT_SIT_FEMALE;
+ mIDList["Flying"] = ANIM_AGENT_HOVER;
+
+ mIDList["[None]"] = LLUUID::null;
+ mIDList["Aaaaah"] = ANIM_AGENT_EXPRESS_OPEN_MOUTH;
+ mIDList["Afraid"] = ANIM_AGENT_EXPRESS_AFRAID;
+ mIDList["Angry"] = ANIM_AGENT_EXPRESS_ANGER;
+ mIDList["Big Smile"] = ANIM_AGENT_EXPRESS_TOOTHSMILE;
+ mIDList["Bored"] = ANIM_AGENT_EXPRESS_BORED;
+ mIDList["Cry"] = ANIM_AGENT_EXPRESS_CRY;
+ mIDList["Disdain"] = ANIM_AGENT_EXPRESS_DISDAIN;
+ mIDList["Embarrassed"] = ANIM_AGENT_EXPRESS_EMBARRASSED;
+ mIDList["Frown"] = ANIM_AGENT_EXPRESS_FROWN;
+ mIDList["Kiss"] = ANIM_AGENT_EXPRESS_KISS;
+ mIDList["Laugh"] = ANIM_AGENT_EXPRESS_LAUGH;
+ mIDList["Plllppt"] = ANIM_AGENT_EXPRESS_TONGUE_OUT;
+ mIDList["Repulsed"] = ANIM_AGENT_EXPRESS_REPULSED;
+ mIDList["Sad"] = ANIM_AGENT_EXPRESS_SAD;
+ mIDList["Shrug"] = ANIM_AGENT_EXPRESS_SHRUG;
+ mIDList["Smile"] = ANIM_AGENT_EXPRESS_SMILE;
+ mIDList["Surprise"] = ANIM_AGENT_EXPRESS_SURPRISE;
+ mIDList["Wink"] = ANIM_AGENT_EXPRESS_WINK;
+ mIDList["Worry"] = ANIM_AGENT_EXPRESS_WORRY;
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::postBuild()
+{
+ LLRect r;
+ LLKeyframeMotion* motionp = NULL;
+ LLBVHLoader* loaderp = NULL;
+
+ if (!LLFloaterNameDesc::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetCommitCallback("name_form", onCommitName, this);
+
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+ childSetAction("ok_btn", onBtnOK, this);
+ setDefaultBtn();
+
+ mPreviewRect.set(PREVIEW_HPAD,
+ PREVIEW_TEXTURE_HEIGHT,
+ getRect().getWidth() - PREVIEW_HPAD,
+ PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f);
+
+ S32 y = mPreviewRect.mTop + BTN_HEIGHT;
+ S32 btn_left = PREVIEW_HPAD;
+
+ r.set( btn_left, y, btn_left + 32, y - BTN_HEIGHT );
+ mPlayButton = LLViewerUICtrlFactory::getButtonByName(this, "play_btn");
+ if (!mPlayButton)
+ {
+ mPlayButton = new LLButton("play_btn", LLRect(0,0,0,0));
+ }
+ mPlayButton->setClickedCallback(onBtnPlay);
+ mPlayButton->setCallbackUserData(this);
+
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+ mPlayButton->setDisabledImages("","");
+
+ mPlayButton->setScaleImage(TRUE);
+ mPlayButton->setFixedBorder(0, 0);
+
+ mStopButton = LLViewerUICtrlFactory::getButtonByName(this, "stop_btn");
+ if (!mStopButton)
+ {
+ mStopButton = new LLButton("stop_btn", LLRect(0,0,0,0));
+ }
+ mStopButton->setClickedCallback(onBtnStop);
+ mStopButton->setCallbackUserData(this);
+
+ mStopButton->setImages("button_anim_stop.tga",
+ "button_anim_stop_selected.tga");
+ mStopButton->setDisabledImages("","");
+
+ mStopButton->setScaleImage(TRUE);
+ mStopButton->setFixedBorder(0, 0);
+
+ r.set(r.mRight + PREVIEW_HPAD, y, getRect().getWidth() - PREVIEW_HPAD, y - BTN_HEIGHT);
+ childSetCommitCallback("playback_slider", onSliderMove, this);
+
+ childHide("bad_animation_text");
+
+ childSetCommitCallback("preview_base_anim", onCommitBaseAnim, this);
+ childSetValue("preview_base_anim", "Standing");
+
+ childSetCommitCallback("priority", onCommitPriority, this);
+ childSetCommitCallback("loop_check", onCommitLoop, this);
+ childSetCommitCallback("loop_in_point", onCommitLoopIn, this);
+ childSetValidate("loop_in_point", validateLoopIn);
+ childSetCommitCallback("loop_out_point", onCommitLoopOut, this);
+ childSetValidate("loop_out_point", validateLoopOut);
+
+ childSetCommitCallback("hand_pose_combo", onCommitHandPose, this);
+
+ childSetCommitCallback("emote_combo", onCommitEmote, this);
+ childSetValue("emote_combo", "[None]");
+
+ childSetCommitCallback("ease_in_time", onCommitEaseIn, this);
+ childSetValidate("ease_in_time", validateEaseIn);
+ childSetCommitCallback("ease_out_time", onCommitEaseOut, this);
+ childSetValidate("ease_out_time", validateEaseOut);
+
+ if (!stricmp(strrchr(mFilename.c_str(), '.'), ".bvh"))
+ {
+ // loading a bvh file
+
+ // now load bvh file
+ S32 file_size;
+ apr_file_t* fp = ll_apr_file_open(mFilenameAndPath, LL_APR_RB, &file_size);
+
+ if (!fp)
+ {
+ llwarns << "Can't open BVH file:" << mFilename << llendl;
+ }
+ else
+ {
+ char* file_buffer;
+
+ file_buffer = new char[file_size + 1];
+
+ if (file_size == ll_apr_file_read(fp, file_buffer, file_size))
+ {
+ file_buffer[file_size] = '\0';
+ llinfos << "Loading BVH file " << mFilename << llendl;
+ loaderp = new LLBVHLoader(file_buffer);
+ }
+
+ apr_file_close(fp);
+ delete file_buffer;
+ }
+ }
+
+ if (loaderp && loaderp->isInitialized() && loaderp->getDuration() <= MAX_ANIM_DURATION)
+ {
+ // generate unique id for this motion
+ mTransactionID.generate();
+ mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+ mAnimPreview = new LLPreviewAnimation(256, 256);
+
+ // motion will be returned, but it will be in a load-pending state, as this is a new motion
+ // this motion will not request an asset transfer until next update, so we have a chance to
+ // load the keyframe data locally
+ motionp = (LLKeyframeMotion*)mAnimPreview->getDummyAvatar()->createMotion(mMotionID);
+
+ // create data buffer for keyframe initialization
+ S32 buffer_size = loaderp->getOutputSize();
+ U8* buffer = new U8[buffer_size];
+
+ LLDataPackerBinaryBuffer dp(buffer, buffer_size);
+
+ // pass animation data through memory buffer
+ loaderp->serialize(dp);
+ dp.reset();
+ BOOL success = motionp->deserialize(dp);
+
+ delete []buffer;
+
+ if (motionp && success)
+ {
+ const LLBBoxLocal &pelvis_bbox = motionp->getPelvisBBox();
+
+ LLVector3 temp = pelvis_bbox.getCenter();
+ // only consider XY?
+ //temp.mV[VZ] = 0.f;
+ F32 pelvis_offset = temp.magVec();
+
+ temp = pelvis_bbox.getExtent();
+ //temp.mV[VZ] = 0.f;
+ F32 pelvis_max_displacement = pelvis_offset + (temp.magVec() * 0.5f) + 1.f;
+
+ F32 camera_zoom = gCamera->getDefaultFOV() / (2.f * atan(pelvis_max_displacement / PREVIEW_CAMERA_DISTANCE));
+
+ mAnimPreview->setZoom(camera_zoom);
+
+ motionp->setName(childGetValue("name_form").asString());
+ mAnimPreview->getDummyAvatar()->startMotion(mMotionID);
+ childSetMinValue("playback_slider", 0.0);
+ childSetMaxValue("playback_slider", 1.0);
+
+ childSetValue("loop_check", LLSD(motionp->getLoop()));
+ childSetValue("loop_in_point", LLSD(motionp->getLoopIn() / motionp->getDuration() * 100.f));
+ childSetValue("loop_out_point", LLSD(motionp->getLoopOut() / motionp->getDuration() * 100.f));
+ childSetValue("priority", LLSD((F32)motionp->getPriority()));
+ childSetValue("hand_pose_combo", LLHandMotion::getHandPoseName(motionp->getHandPose()));
+ childSetValue("ease_in_time", LLSD(motionp->getEaseInDuration()));
+ childSetValue("ease_out_time", LLSD(motionp->getEaseOutDuration()));
+ mEnabled = TRUE;
+ char seconds_string[128];
+ sprintf(seconds_string, " - %.2f seconds", motionp->getDuration());
+
+ setTitle(mFilename + LLString(seconds_string));
+ }
+ else
+ {
+ delete mAnimPreview;
+ mAnimPreview = NULL;
+ mMotionID.setNull();
+ // XUI:translate
+ childSetValue("bad_animation_text", LLSD("Failed to initialize motion."));
+ mEnabled = FALSE;
+ }
+ }
+ else
+ {
+ if ( loaderp )
+ {
+ if (loaderp->getDuration() > MAX_ANIM_DURATION)
+ {
+ char output_str[256];
+
+ sprintf(output_str, "Animation file is %.1f seconds in length.\n\nMaximum animation length is %.1f seconds.\n",
+ loaderp->getDuration(), MAX_ANIM_DURATION);
+ childSetValue("bad_animation_text", LLSD(output_str));
+ }
+ else
+ {
+ char* status = loaderp->getStatus();
+ LLString error_string("Unable to read animation file.\n\n");
+ error_string += LLString(status);
+ childSetValue("bad_animation_text", LLSD(error_string));
+ }
+ }
+
+ mEnabled = FALSE;
+ mMotionID.setNull();
+ mAnimPreview = NULL;
+ }
+
+ refresh();
+
+ delete loaderp;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterAnimPreview()
+//-----------------------------------------------------------------------------
+LLFloaterAnimPreview::~LLFloaterAnimPreview()
+{
+ delete mAnimPreview;
+ mAnimPreview = NULL;
+
+ mEnabled = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::draw()
+{
+ LLFloater::draw();
+ LLRect r = getRect();
+
+ refresh();
+
+ if (mMotionID.notNull() && mAnimPreview)
+ {
+ glColor3f(1.f, 1.f, 1.f);
+ mAnimPreview->bindTexture();
+
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ mAnimPreview->unbindTexture();
+
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ if (!avatarp->areAnimationsPaused())
+ {
+ mAnimPreview->requestUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resetMotion()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::resetMotion()
+{
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ BOOL paused = avatarp->areAnimationsPaused();
+
+ mPauseRequest = NULL;
+
+ LLUUID anim_id = mIDList[childGetValue("preview_base_anim").asString()];
+ avatarp->stopMotion(anim_id, TRUE);
+ avatarp->stopMotion(mMotionID, TRUE);
+ avatarp->startMotion(anim_id, 5.f);
+ avatarp->startMotion(mMotionID);
+ childSetValue("playback_slider", 0.0);
+ mLastSliderValue = 0.0f;
+
+ if (paused)
+ {
+ mPauseRequest = avatarp->requestPause();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPreviewRect.pointInRect(x, y))
+ {
+ bringToFront( x, y );
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ gViewerWindow->hideCursor();
+ mLastMouseX = x;
+ mLastMouseY = y;
+ return TRUE;
+ }
+
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setMouseCapture(FALSE, NULL);
+ gViewerWindow->showCursor();
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ MASK local_mask = mask & ~MASK_ALT;
+
+ if (mAnimPreview && gViewerWindow->hasMouseCapture(this))
+ {
+ if (local_mask == MASK_PAN)
+ {
+ // pan here
+ mAnimPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAnimPreview->rotate(yaw_radians, pitch_radians);
+ }
+ else
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAnimPreview->rotate(yaw_radians, 0.f);
+ mAnimPreview->zoom(zoom_amt);
+ }
+
+ mAnimPreview->requestUpdate();
+
+ LLUI::setCursorPositionLocal(this, mLastMouseX, mLastMouseY);
+ }
+
+ if (!mPreviewRect.pointInRect(x, y) || !mAnimPreview)
+ {
+ return LLFloater::handleHover(x, y, mask);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if (local_mask == MASK_PAN)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ mAnimPreview->zoom((F32)clicks * -0.2f);
+ mAnimPreview->requestUpdate();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onMouseCaptureLost(LLMouseHandler* handler)
+{
+ gViewerWindow->showCursor();
+}
+
+//-----------------------------------------------------------------------------
+// onBtnPlay()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnPlay(void* user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mMotionID.notNull() && previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ if(!avatarp->isMotionActive(previewp->mMotionID))
+ {
+ previewp->resetMotion();
+ previewp->mPauseRequest = NULL;
+ }
+ else
+ {
+ if (avatarp->areAnimationsPaused())
+ {
+ previewp->mPauseRequest = NULL;
+ }
+ else
+ {
+ previewp->mPauseRequest = avatarp->requestPause();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onBtnStop()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnStop(void* user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mMotionID.notNull() && previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ // is the motion looping and have we passed the loop in point?
+ if (previewp->childGetValue("loop_check").asBoolean() &&
+ (F32)previewp->childGetValue("loop_in_point").asReal() <= (F32)previewp->childGetValue("playback_slider").asReal() * 100.f)
+ {
+ avatarp->stopMotion(previewp->mMotionID, FALSE);
+ }
+ else
+ {
+ avatarp->stopMotion(previewp->mMotionID, FALSE);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onSliderMove()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onSliderMove(LLUICtrl* ctrl, void*user_data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)user_data;
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLMotion* motionp = avatarp->findMotion(previewp->mMotionID);
+ LLMotion* base_motionp =
+ avatarp->findMotion(previewp->mIDList[previewp->childGetValue("preview_base_anim").asString()]);
+
+ if (motionp && base_motionp)
+ {
+ if (!avatarp->isMotionActive(previewp->mMotionID))
+ {
+ previewp->resetMotion();
+ }
+
+ previewp->mPauseRequest = avatarp->requestPause();
+ F32 original_activation_time = motionp->mActivationTimestamp;
+ motionp->mActivationTimestamp -= ((F32)previewp->childGetValue("playback_slider").asReal() - previewp->mLastSliderValue) *
+ motionp->getDuration();
+ base_motionp->mActivationTimestamp -= ((F32)previewp->childGetValue("playback_slider").asReal() - previewp->mLastSliderValue) *
+ base_motionp->getDuration();
+
+ if (motionp->mSendStopTimestamp != F32_MIN)
+ {
+ motionp->mSendStopTimestamp = motionp->mSendStopTimestamp - original_activation_time + motionp->mActivationTimestamp;
+ }
+
+ if (motionp->mStopTimestamp != F32_MIN)
+ {
+ motionp->mStopTimestamp = motionp->mStopTimestamp - original_activation_time + motionp->mActivationTimestamp;
+ }
+ previewp->refresh();
+ }
+ }
+
+ previewp->mLastSliderValue = (F32)previewp->childGetValue("playback_slider").asReal();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitBaseAnim()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitBaseAnim(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return;
+
+ if (previewp->mAnimPreview)
+ {
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+
+ BOOL paused = avatarp->areAnimationsPaused();
+
+ // stop all other possible base motions
+ avatarp->stopMotion(ANIM_AGENT_STAND, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_WALK, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_SIT, TRUE);
+ avatarp->stopMotion(ANIM_AGENT_HOVER, TRUE);
+
+ previewp->resetMotion();
+
+ if (!paused)
+ {
+ previewp->mPauseRequest = NULL;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoop()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoop(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return;
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoop(previewp->childGetValue("loop_check").asBoolean());
+ motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() * 0.01f * motionp->getDuration());
+ motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration());
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoopIn()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoopIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoopIn((F32)previewp->childGetValue("loop_in_point").asReal() / 100.f);
+ previewp->resetMotion();
+ previewp->childSetValue("loop_check", LLSD(TRUE));
+ onCommitLoop(ctrl, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitLoopOut()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitLoopOut(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setLoopOut((F32)previewp->childGetValue("loop_out_point").asReal() * 0.01f * motionp->getDuration());
+ previewp->resetMotion();
+ previewp->childSetValue("loop_check", LLSD(TRUE));
+ onCommitLoop(ctrl, data);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onCommitName()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitName(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (motionp)
+ {
+ motionp->setName(previewp->childGetValue("name_form").asString());
+ }
+
+ LLFloaterNameDesc::doCommit(ctrl, data);
+}
+
+//-----------------------------------------------------------------------------
+// onCommitHandPose()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitHandPose(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setHandPose(LLHandMotion::getHandPose(previewp->childGetValue("hand_pose_combo").asString()));
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEmote()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEmote(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEmote(previewp->mIDList[previewp->childGetValue("emote_combo").asString()]);
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitPriority()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitPriority(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setPriority(llfloor((F32)previewp->childGetValue("priority").asReal()));
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEaseIn()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEaseIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEaseIn((F32)previewp->childGetValue("ease_in_time").asReal());
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// onCommitEaseOut()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onCommitEaseOut(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+ if (!previewp->mEnabled) return;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ motionp->setEaseOut((F32)previewp->childGetValue("ease_out_time").asReal());
+ previewp->resetMotion();
+}
+
+//-----------------------------------------------------------------------------
+// validateEaseIn()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateEaseIn(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (!motionp->getLoop())
+ {
+ F32 new_ease_in = llclamp((F32)previewp->childGetValue("ease_in_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseOutDuration());
+ previewp->childSetValue("ease_in_time", LLSD(new_ease_in));
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateEaseOut()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateEaseOut(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ LLVOAvatar* avatarp = previewp->mAnimPreview->getDummyAvatar();
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(previewp->mMotionID);
+
+ if (!motionp->getLoop())
+ {
+ F32 new_ease_out = llclamp((F32)previewp->childGetValue("ease_out_time").asReal(), 0.f, motionp->getDuration() - motionp->getEaseInDuration());
+ previewp->childSetValue("ease_out_time", LLSD(new_ease_out));
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateLoopIn()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateLoopIn(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal();
+ F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal();
+
+ if (loop_in_value < 0.f)
+ {
+ loop_in_value = 0.f;
+ }
+ else if (loop_in_value > 100.f)
+ {
+ loop_in_value = 100.f;
+ }
+ else if (loop_in_value > loop_out_value)
+ {
+ loop_in_value = loop_out_value;
+ }
+
+ previewp->childSetValue("loop_in_point", LLSD(loop_in_value));
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// validateLoopOut()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterAnimPreview::validateLoopOut(LLUICtrl* spin, void* data)
+{
+ LLFloaterAnimPreview* previewp = (LLFloaterAnimPreview*)data;
+
+ if (!previewp->mEnabled) return FALSE;
+
+ F32 loop_out_value = (F32)previewp->childGetValue("loop_out_point").asReal();
+ F32 loop_in_value = (F32)previewp->childGetValue("loop_in_point").asReal();
+
+ if (loop_out_value < 0.f)
+ {
+ loop_out_value = 0.f;
+ }
+ else if (loop_out_value > 100.f)
+ {
+ loop_out_value = 100.f;
+ }
+ else if (loop_out_value < loop_in_value)
+ {
+ loop_out_value = loop_in_value;
+ }
+
+ previewp->childSetValue("loop_out_point", LLSD(loop_out_value));
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::refresh()
+{
+ if (!mAnimPreview)
+ {
+ childShow("bad_animation_text");
+ mPlayButton->setEnabled(FALSE);
+ mStopButton->setEnabled(FALSE);
+ childDisable("ok_btn");
+ }
+ else
+ {
+ childHide("bad_animation_text");
+ mPlayButton->setEnabled(TRUE);
+ LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar();
+ if (avatarp->isMotionActive(mMotionID))
+ {
+ mStopButton->setEnabled(TRUE);
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID);
+ if (avatarp->areAnimationsPaused())
+ {
+
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+
+ }
+ else
+ {
+ if (motionp)
+ {
+ F32 fraction_complete;
+ fraction_complete = motionp->getLastUpdateTime() / motionp->getDuration();
+
+ childSetValue("playback_slider", fraction_complete);
+ mLastSliderValue = fraction_complete;
+ }
+ mPlayButton->setImages("button_anim_pause.tga",
+ "button_anim_pause_selected.tga");
+
+ }
+ }
+ else
+ {
+ mPauseRequest = avatarp->requestPause();
+ mPlayButton->setImages("button_anim_play.tga",
+ "button_anim_play_selected.tga");
+
+ mStopButton->setEnabled(FALSE);
+ }
+ childEnable("ok_btn");
+ mAnimPreview->requestUpdate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onBtnOK()
+//-----------------------------------------------------------------------------
+void LLFloaterAnimPreview::onBtnOK(void* userdata)
+{
+ LLFloaterAnimPreview* floaterp = (LLFloaterAnimPreview*)userdata;
+ if (!floaterp->mEnabled) return;
+
+ if (floaterp->mAnimPreview)
+ {
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)floaterp->mAnimPreview->getDummyAvatar()->findMotion(floaterp->mMotionID);
+
+ S32 file_size = motionp->getFileSize();
+ U8* buffer = new U8[file_size];
+
+ LLDataPackerBinaryBuffer dp(buffer, file_size);
+ if (motionp->serialize(dp))
+ {
+ LLVFile file(gVFS, motionp->getID(), LLAssetType::AT_ANIMATION, LLVFile::APPEND);
+
+ S32 size = dp.getCurrentSize();
+ file.setMaxSize(size);
+ if (file.write((U8*)buffer, size))
+ {
+ std::string name = floaterp->childGetValue("name_form").asString();
+ std::string desc = floaterp->childGetValue("description_form").asString();
+ upload_new_resource(floaterp->mTransactionID, // tid
+ LLAssetType::AT_ANIMATION,
+ name,
+ desc,
+ 0,
+ LLAssetType::AT_NONE,
+ LLInventoryType::IT_ANIMATION,
+ PERM_NONE,
+ name);
+ }
+ else
+ {
+ llwarns << "Failure writing animation data." << llendl;
+ gViewerWindow->alertXml("WriteAnimationFail");
+ }
+ }
+
+ delete [] buffer;
+ // clear out cache for motion data
+ floaterp->mAnimPreview->getDummyAvatar()->removeMotion(floaterp->mMotionID);
+ LLKeyframeDataCache::removeKeyframeData(floaterp->mMotionID);
+ }
+
+ floaterp->onClose(false);
+}
+
+//-----------------------------------------------------------------------------
+// LLPreviewAnimation
+//-----------------------------------------------------------------------------
+LLPreviewAnimation::LLPreviewAnimation(S32 width, S32 height) : LLDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE)
+{
+ mNeedsUpdate = TRUE;
+ mCameraDistance = PREVIEW_CAMERA_DISTANCE;
+ mCameraYaw = 0.f;
+ mCameraPitch = 0.f;
+ mCameraZoom = 1.f;
+
+ mDummyAvatar = (LLVOAvatar*)gObjectList.createObjectViewer(LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
+ mDummyAvatar->createDrawable(&gPipeline);
+ mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar->mSpecialRenderMode = 1;
+ mDummyAvatar->setPositionAgent(LLVector3::zero);
+ mDummyAvatar->slamPosition();
+ mDummyAvatar->updateJointLODs();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ mDummyAvatar->startMotion(ANIM_AGENT_STAND, 5.f);
+ mDummyAvatar->mSkirtLOD.setVisible(FALSE, TRUE);
+ gPipeline.markVisible(mDummyAvatar->mDrawable);
+
+ // stop extraneous animations
+ mDummyAvatar->stopMotion( ANIM_AGENT_HEAD_ROT, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_EYE, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_BODY_NOISE, TRUE );
+ mDummyAvatar->stopMotion( ANIM_AGENT_BREATHE_ROT, TRUE );
+}
+
+//-----------------------------------------------------------------------------
+// LLPreviewAnimation()
+//-----------------------------------------------------------------------------
+LLPreviewAnimation::~LLPreviewAnimation()
+{
+ mDummyAvatar->markDead();
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+BOOL LLPreviewAnimation::render()
+{
+ mNeedsUpdate = FALSE;
+ LLVOAvatar* avatarp = mDummyAvatar;
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault def;
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(0.15f, 0.2f, 0.3f, 1.f);
+
+ gl_rect_2d_simple( mWidth, mHeight );
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ LLVector3 target_pos = avatarp->mRoot.getWorldPosition();
+
+ LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+ LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+ LLQuaternion av_rot = avatarp->mRoot.getWorldRotation() * camera_rot;
+ gCamera->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos + (mCameraOffset * av_rot) ); // point of interest
+
+ gCamera->setView(gCamera->getDefaultFOV() / mCameraZoom);
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ mCameraRelPos = gCamera->getOrigin() - avatarp->mHeadp->getWorldPosition();
+
+ //avatarp->setAnimationData("LookAtPoint", (void *)&mCameraRelPos);
+
+ //RN: timestep must be zero, because paused animations will never initialize
+ // av skeleton otherwise
+ avatarp->setTimeStep(0.f);
+ if (avatarp->areAnimationsPaused())
+ {
+ avatarp->updateMotion(TRUE);
+ }
+ else
+ {
+ avatarp->updateMotion();
+ }
+
+ avatarp->mRoot.updateWorldMatrixChildren();
+
+ stop_glerror();
+
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
+ {
+ gPipeline.bindAGP();
+ }
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// requestUpdate()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::requestUpdate()
+{
+ mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+ mCameraYaw = mCameraYaw + yaw_radians;
+
+ mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::zoom(F32 zoom_delta)
+{
+ setZoom(mCameraZoom + zoom_delta);
+}
+
+//-----------------------------------------------------------------------------
+// setZoom()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::setZoom(F32 zoom_amt)
+{
+ mCameraZoom = llclamp(zoom_amt, MIN_CAMERA_ZOOM, MAX_CAMERA_ZOOM);
+}
+
+//-----------------------------------------------------------------------------
+// pan()
+//-----------------------------------------------------------------------------
+void LLPreviewAnimation::pan(F32 right, F32 up)
+{
+ mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+ mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
+
diff --git a/indra/newview/llfloateranimpreview.h b/indra/newview/llfloateranimpreview.h
new file mode 100644
index 0000000000..40ec96f73d
--- /dev/null
+++ b/indra/newview/llfloateranimpreview.h
@@ -0,0 +1,108 @@
+/**
+ * @file llfloateranimpreview.h
+ * @brief LLFloaterAnimPreview class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERANIMPREVIEW_H
+#define LL_LLFLOATERANIMPREVIEW_H
+
+#include "llfloaternamedesc.h"
+#include "lldynamictexture.h"
+#include "llcharacter.h"
+#include "llquaternion.h"
+
+class LLVOAvatar;
+class LLViewerJointMesh;
+
+class LLPreviewAnimation : public LLDynamicTexture
+{
+public:
+ LLPreviewAnimation(S32 width, S32 height);
+ virtual ~LLPreviewAnimation();
+
+ BOOL render();
+ void requestUpdate();
+ void rotate(F32 yaw_radians, F32 pitch_radians);
+ void zoom(F32 zoom_delta);
+ void setZoom(F32 zoom_amt);
+ void pan(F32 right, F32 up);
+ virtual BOOL needsUpdate() { return mNeedsUpdate; }
+
+ LLVOAvatar* getDummyAvatar() { return mDummyAvatar; }
+
+protected:
+ BOOL mNeedsUpdate;
+ F32 mCameraDistance;
+ F32 mCameraYaw;
+ F32 mCameraPitch;
+ F32 mCameraZoom;
+ LLVector3 mCameraOffset;
+ LLVector3 mCameraRelPos;
+ LLVOAvatar* mDummyAvatar;
+};
+
+class LLFloaterAnimPreview : public LLFloaterNameDesc
+{
+public:
+ LLFloaterAnimPreview(const char* filename);
+ virtual ~LLFloaterAnimPreview();
+
+ BOOL postBuild();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ void refresh();
+
+ static void onMouseCaptureLost(LLMouseHandler*);
+ static void onBtnPlay(void*);
+ static void onBtnStop(void*);
+ static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+ static void onSliderMove(LLUICtrl*, void*);
+ static void onCommitBaseAnim(LLUICtrl*, void*);
+ static void onCommitLoop(LLUICtrl*, void*);
+ static void onCommitLoopIn(LLUICtrl*, void*);
+ static void onCommitLoopOut(LLUICtrl*, void*);
+ static BOOL validateLoopIn(LLUICtrl*, void*);
+ static BOOL validateLoopOut(LLUICtrl*, void*);
+ static void onCommitName(LLUICtrl*, void*);
+ static void onCommitHandPose(LLUICtrl*, void*);
+ static void onCommitEmote(LLUICtrl*, void*);
+ static void onCommitPriority(LLUICtrl*, void*);
+ static void onCommitEaseIn(LLUICtrl*, void*);
+ static void onCommitEaseOut(LLUICtrl*, void*);
+ static BOOL validateEaseIn(LLUICtrl*, void*);
+ static BOOL validateEaseOut(LLUICtrl*, void*);
+ static void onBtnOK(void*);
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status);
+protected:
+ void draw();
+ void resetMotion();
+
+ LLPreviewAnimation* mAnimPreview;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ LLButton* mPlayButton;
+ LLButton* mStopButton;
+ F32 mLastSliderValue;
+ LLRect mPreviewRect;
+ LLRectf mPreviewImageRect;
+ LLAssetID mMotionID;
+ LLTransactionID mTransactionID;
+ BOOL mEnabled;
+ LLAnimPauseRequest mPauseRequest;
+
+ std::map<LLString, LLUUID> mIDList;
+
+ static S32 sUploadAmount;
+};
+
+#endif // LL_LLFLOATERANIMPREVIEW_H
diff --git a/indra/newview/llfloaterauction.cpp b/indra/newview/llfloaterauction.cpp
new file mode 100644
index 0000000000..c86b9abfda
--- /dev/null
+++ b/indra/newview/llfloaterauction.cpp
@@ -0,0 +1,307 @@
+/**
+ * @file llfloaterauction.cpp
+ * @author James Cook, Ian Wilkes
+ * @brief Implementation of the auction floater.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterauction.h"
+
+#include "lldir.h"
+#include "llgl.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llparcel.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#include "llagent.h"
+#include "llcombobox.h"
+#include "llnotify.h"
+#include "llsavedsettingsglue.h"
+#include "llviewerimagelist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llui.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+void auction_j2c_upload_done(const LLUUID& asset_id,
+ void* user_data, S32 status);
+void auction_tga_upload_done(const LLUUID& asset_id,
+ void* user_data, S32 status);
+
+///----------------------------------------------------------------------------
+/// Class llfloaterauction
+///----------------------------------------------------------------------------
+
+LLFloaterAuction* LLFloaterAuction::sInstance = NULL;
+
+// Default constructor
+LLFloaterAuction::LLFloaterAuction() :
+ LLFloater("floater_auction"),
+ mParcelID(-1)
+{
+ gUICtrlFactory->buildFloater(this, "floater_auction.xml");
+
+ childSetValue("fence_check",
+ LLSD( gSavedSettings.getBOOL("AuctionShowFence") ) );
+ childSetCommitCallback("fence_check",
+ LLSavedSettingsGlue::setBOOL, (void*)"AuctionShowFence");
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "saletype_combo");
+ if (combo)
+ {
+ combo->selectFirstItem();
+ }
+
+ childSetAction("snapshot_btn", onClickSnapshot, this);
+ childSetAction("ok_btn", onClickOK, this);
+}
+
+// Destroys the object
+LLFloaterAuction::~LLFloaterAuction()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterAuction::show()
+{
+ if(!sInstance)
+ {
+ sInstance = new LLFloaterAuction();
+ sInstance->center();
+ sInstance->setFocus(TRUE);
+ }
+ sInstance->initialize();
+ sInstance->open();
+}
+
+void LLFloaterAuction::initialize()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(parcel && region && !parcel->getForSale())
+ {
+ mParcelHost = region->getHost();
+ mParcelID = parcel->getLocalID();
+
+ childSetText("parcel_text", parcel->getName());
+ childEnable("snapshot_btn");
+ childEnable("ok_btn");
+ }
+ else
+ {
+ mParcelHost.invalidate();
+ if(parcel && parcel->getForSale())
+ {
+ childSetText("parcel_text", childGetText("already for sale"));
+ }
+ else
+ {
+ childSetText("parcel_text", "");
+ }
+ mParcelID = -1;
+ childSetEnabled("snapshot_btn", false);
+ childSetEnabled("ok_btn", false);
+ }
+ mImageID.setNull();
+ mImage = NULL;
+}
+
+void LLFloaterAuction::draw()
+{
+ LLFloater::draw();
+
+ if(getVisible() && !isMinimized() && mImage.notNull())
+ {
+ LLRect rect;
+ if (childGetRect("snapshot_icon", rect))
+ {
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
+ rect.stretch(-1);
+ }
+ {
+ LLGLSUIDefault gls_ui;
+ glColor3f(1.f, 1.f, 1.f);
+ gl_draw_scaled_image(rect.mLeft,
+ rect.mBottom,
+ rect.getWidth(),
+ rect.getHeight(),
+ mImage);
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterAuction::onClickSnapshot(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ gForceRenderLandFence = self->childGetValue("fence_check").asBoolean();
+ BOOL success = gViewerWindow->rawSnapshot(raw,
+ gViewerWindow->getWindowWidth(),
+ gViewerWindow->getWindowHeight(),
+ TRUE,
+ FALSE, FALSE);
+ gForceRenderLandFence = FALSE;
+
+ if (success)
+ {
+ self->mTransactionID.generate();
+ self->mImageID = self->mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+ gViewerWindow->playSnapshotAnimAndSound();
+ llinfos << "Writing TGA..." << llendl;
+
+ LLPointer<LLImageTGA> tga = new LLImageTGA;
+ tga->encode(raw);
+ LLVFile::writeFile(tga->getData(), tga->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_IMAGE_TGA);
+
+ raw->biasedScaleToPowerOfTwo(LLViewerImage::MAX_IMAGE_SIZE_DEFAULT);
+
+ llinfos << "Writing J2C..." << llendl;
+
+ LLPointer<LLImageJ2C> j2c = new LLImageJ2C;
+ j2c->encode(raw);
+ LLVFile::writeFile(j2c->getData(), j2c->getDataSize(), gVFS, self->mImageID, LLAssetType::AT_TEXTURE);
+
+ self->mImage = new LLImageGL((LLImageRaw*)raw, FALSE);
+ self->mImage->bind();
+ self->mImage->setClamp(TRUE, TRUE);
+ }
+ else
+ {
+ llwarns << "Unable to take snapshot" << llendl;
+ }
+}
+
+// static
+void LLFloaterAuction::onClickOK(void* data)
+{
+ LLFloaterAuction* self = (LLFloaterAuction*)(data);
+ bool is_auction = false;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(self, "saletype_combo");
+ if (combo
+ && combo->getCurrentIndex() == 0)
+ {
+ is_auction = true;
+ }
+ if(self->mImageID.notNull())
+ {
+ LLSD parcel_name = self->childGetValue("parcel_text");
+
+ // create the asset
+ if(is_auction)
+ {
+ // only need the tga if it is an auction.
+ LLString* name = new LLString(parcel_name.asString());
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_IMAGE_TGA,
+ &auction_tga_upload_done,
+ (void*)name,
+ FALSE);
+ self->getWindow()->incBusyCount();
+ }
+
+ LLString* j2c_name = new LLString(parcel_name.asString());
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_TEXTURE,
+ &auction_j2c_upload_done,
+ (void*)j2c_name,
+ FALSE);
+ self->getWindow()->incBusyCount();
+
+ if(is_auction)
+ {
+ LLNotifyBox::showXml("UploadingAuctionSnapshot");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UploadingSnapshot");
+ }
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ if(is_auction)
+ {
+ msg->newMessage("ViewerStartAuction");
+ }
+ else
+ {
+ msg->newMessage("ParcelGodReserveForNewbie");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addS32("LocalID", self->mParcelID);
+ msg->addUUID("SnapshotID", self->mImageID);
+ msg->sendReliable(self->mParcelHost);
+
+ // clean up floater, and get out
+ self->mImageID.setNull();
+ self->mImage = NULL;
+ self->mParcelID = -1;
+ self->mParcelHost.invalidate();
+ self->close();
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+void auction_tga_upload_done(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLString* name = (LLString*)(user_data);
+ llinfos << "Upload of asset '" << *name << "' " << asset_id
+ << " returned " << status << llendl;
+ delete name;
+
+ gViewerWindow->getWindow()->decBusyCount();
+
+ if (0 == status)
+ {
+ LLNotifyBox::showXml("UploadWebSnapshotDone");
+ }
+ else
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("UploadAuctionSnapshotFail", args);
+ }
+}
+
+void auction_j2c_upload_done(const LLUUID& asset_id, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLString* name = (LLString*)(user_data);
+ llinfos << "Upload of asset '" << *name << "' " << asset_id
+ << " returned " << status << llendl;
+ delete name;
+
+ gViewerWindow->getWindow()->decBusyCount();
+
+ if (0 == status)
+ {
+ LLNotifyBox::showXml("UploadSnapshotDone");
+ }
+ else
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("UploadAuctionSnapshotFail", args);
+ }
+}
diff --git a/indra/newview/llfloaterauction.h b/indra/newview/llfloaterauction.h
new file mode 100644
index 0000000000..fe5ce8f9d9
--- /dev/null
+++ b/indra/newview/llfloaterauction.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloaterauction.h
+ * @author James Cook, Ian Wilkes
+ * @brief llfloaterauction class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERAUCTION_H
+#define LL_LLFLOATERAUCTION_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterAuction
+//
+// Class which holds the functionality to start auctions.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFloaterAuction : public LLFloater
+{
+public:
+ // LLFloater interface
+ /*virtual*/ void onClose(bool app_quitting) { setVisible(FALSE); }
+ /*virtual*/ void draw();
+
+ // LLFloaterAuction interface
+ static void show();
+
+private:
+ LLFloaterAuction();
+ ~LLFloaterAuction();
+ void initialize();
+
+ static void onClickSnapshot(void* data);
+ static void onClickOK(void* data);
+
+ static LLFloaterAuction* sInstance;
+
+private:
+ LLTransactionID mTransactionID;
+ LLAssetID mImageID;
+ LLPointer<LLImageGL> mImage;
+ S32 mParcelID;
+ LLHost mParcelHost;
+};
+
+
+#endif // LL_LLFLOATERAUCTION_H
diff --git a/indra/newview/llfloateravatarpicker.cpp b/indra/newview/llfloateravatarpicker.cpp
new file mode 100644
index 0000000000..adcc3c49ab
--- /dev/null
+++ b/indra/newview/llfloateravatarpicker.cpp
@@ -0,0 +1,374 @@
+/**
+ * @file llfloateravatarpicker.cpp
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateravatarpicker.h"
+
+#include "message.h"
+
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llinventoryview.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+
+const S32 MIN_WIDTH = 200;
+const S32 MIN_HEIGHT = 340;
+const LLRect FLOATER_RECT(0, 380, 240, 0);
+const char FLOATER_TITLE[] = "Choose Person";
+
+// static
+LLFloaterAvatarPicker* LLFloaterAvatarPicker::sInstance = NULL;
+
+
+LLFloaterAvatarPicker* LLFloaterAvatarPicker::show(callback_t callback,
+ void* userdata,
+ BOOL allow_multiple,
+ BOOL closeOnSelect)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterAvatarPicker();
+ sInstance->mCallback = callback;
+ sInstance->mCallbackUserdata = userdata;
+ sInstance->mCloseOnSelect = FALSE;
+
+ sInstance->open();
+ sInstance->center();
+ sInstance->setAllowMultiple(allow_multiple);
+ }
+ else
+ {
+ sInstance->open();
+ sInstance->mCallback = callback;
+ sInstance->mCallbackUserdata = userdata;
+ sInstance->setAllowMultiple(allow_multiple);
+ }
+
+ sInstance->mCloseOnSelect = closeOnSelect;
+ return sInstance;
+}
+
+// Default constructor
+LLFloaterAvatarPicker::LLFloaterAvatarPicker() :
+ LLFloater("avatarpicker", FLOATER_RECT, FLOATER_TITLE, TRUE, MIN_WIDTH, MIN_HEIGHT),
+ mResultsReturned(FALSE),
+ mCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+ gUICtrlFactory->buildFloater(this, "floater_avatar_picker.xml", NULL);
+}
+
+BOOL LLFloaterAvatarPicker::postBuild()
+{
+ childSetKeystrokeCallback("Edit", editKeystroke, this);
+
+ childSetAction("Find", onBtnFind, this);
+ childDisable("Find");
+
+ mListNames = LLViewerUICtrlFactory::getScrollListByName(this, "Names");
+ childSetDoubleClickCallback("Names",onBtnAdd);
+ childSetCommitCallback("Names", onList, this);
+ childDisable("Names");
+
+ childSetAction("Select", onBtnAdd, this);
+ childDisable("Select");
+
+ childSetAction("Close", onBtnClose, this);
+
+ childSetFocus("Edit");
+
+ if (mListNames)
+ {
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ row->addColumn("No results", LLFontGL::sSansSerif);
+ mListNames->addItem(row);
+ }
+
+ mInventoryPanel = (LLInventoryPanel*)this->getCtrlByNameAndType("Inventory Panel", WIDGET_TYPE_INVENTORY_PANEL);
+ if(mInventoryPanel)
+ {
+ mInventoryPanel->setFilterTypes(0x1 << LLInventoryType::IT_CALLINGCARD);
+ mInventoryPanel->setFollowsAll();
+ mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_CALLINGCARD);
+ mInventoryPanel->setSelectCallback(LLFloaterAvatarPicker::onSelectionChange, this);
+ }
+
+ setAllowMultiple(FALSE);
+
+ return TRUE;
+}
+
+// Destroys the object
+LLFloaterAvatarPicker::~LLFloaterAvatarPicker()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstance = NULL;
+}
+
+void LLFloaterAvatarPicker::onBtnFind(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if(self) self->find();
+}
+
+void LLFloaterAvatarPicker::onBtnAdd(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+
+ if(self->mCallback)
+ {
+ self->mCallback(self->mAvatarNames, self->mAvatarIDs, self->mCallbackUserdata);
+ }
+ if (self->mInventoryPanel)
+ {
+ self->mInventoryPanel->setSelection(LLUUID::null, FALSE);
+ }
+ if (self->mListNames)
+ {
+ self->mListNames->deselectAllItems(TRUE);
+ }
+ if(self->mCloseOnSelect)
+ {
+ self->mCloseOnSelect = FALSE;
+ self->close();
+ }
+}
+
+void LLFloaterAvatarPicker::onBtnClose(void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if(self) self->close();
+}
+
+void LLFloaterAvatarPicker::onList(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)userdata;
+ if (!self)
+ {
+ return;
+ }
+
+ self->mAvatarIDs.clear();
+ self->mAvatarNames.clear();
+
+ if (!self->mListNames)
+ {
+ return;
+ }
+
+ std::vector<LLScrollListItem*> items = self->mListNames->getAllSelected();
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = items.begin(); itor != items.end(); ++itor)
+ {
+ self->mAvatarNames.push_back((*itor)->getColumn(0)->getText());
+ self->mAvatarIDs.push_back((*itor)->getUUID());
+ self->childSetEnabled("Select", TRUE);
+ }
+}
+
+void LLFloaterAvatarPicker::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)data;
+ if (!self)
+ {
+ return;
+ }
+
+ self->mAvatarIDs.clear();
+ self->mAvatarNames.clear();
+
+ self->childSetEnabled("Select", FALSE);
+
+ if (!self->mListNames)
+ {
+ return;
+ }
+
+ std::deque<LLFolderViewItem*>::const_iterator item_it;
+ for (item_it = items.begin(); item_it != items.end(); ++item_it)
+ {
+ LLFolderViewEventListener* listenerp = (*item_it)->getListener();
+ if (listenerp->getInventoryType() == LLInventoryType::IT_CALLINGCARD)
+ {
+ LLInventoryItem* item = gInventory.getItem(listenerp->getUUID());
+
+ if (item)
+ {
+ self->mAvatarIDs.push_back(item->getCreatorUUID());
+ self->mAvatarNames.push_back(listenerp->getName());
+ self->childSetEnabled("Select", TRUE);
+ self->mListNames->deselectAllItems();
+ }
+ }
+ }
+}
+
+
+void LLFloaterAvatarPicker::find()
+{
+ const LLString& text = childGetValue("Edit").asString();
+
+ mQueryID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("AvatarPickerRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", agent_get_id());
+ msg->addUUID("SessionID", agent_get_session_id());
+ msg->addUUID("QueryID", mQueryID); // not used right now
+ msg->nextBlock("Data");
+ msg->addString("Name", text);
+
+ agent_send_reliable_message();
+
+ if (mListNames)
+ {
+ mListNames->deleteAllItems();
+
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ row->addColumn("Searching...", LLFontGL::sSansSerif);
+ mListNames->addItem(row);
+ }
+
+ childSetEnabled("Select", FALSE);
+ mResultsReturned = FALSE;
+}
+
+void LLFloaterAvatarPicker::setAllowMultiple(BOOL allow_multiple)
+{
+ mAllowMultiple = allow_multiple;
+ if (mInventoryPanel)
+ {
+ mInventoryPanel->setAllowMultiSelect(mAllowMultiple);
+ }
+ if (mListNames)
+ {
+ mListNames->setAllowMultipleSelection(mAllowMultiple);
+ }
+}
+
+// static
+void LLFloaterAvatarPicker::processAvatarPickerReply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ LLUUID query_id;
+ LLUUID avatar_id;
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ msg->getUUID("AgentData", "QueryID", query_id);
+
+ // Not for us
+ if (agent_id != agent_get_id()) return;
+
+ // Dialog already closed
+ LLFloaterAvatarPicker *self = sInstance;
+ if (!self) return;
+
+ // these are not results from our last request
+ if (query_id != self->mQueryID)
+ {
+ return;
+ }
+
+ if (!self->mResultsReturned)
+ {
+ // clear "Searching" label on first results
+ if (self->mListNames)
+ {
+ self->mListNames->deleteAllItems();
+ }
+ }
+ self->mResultsReturned = TRUE;
+
+ if (self->mListNames)
+ {
+ BOOL found_one = FALSE;
+ S32 num_new_rows = msg->getNumberOfBlocks("Data");
+ for (S32 i = 0; i < num_new_rows; i++)
+ {
+ msg->getUUIDFast( _PREHASH_Data,_PREHASH_AvatarID, avatar_id, i);
+ msg->getStringFast(_PREHASH_Data,_PREHASH_FirstName, DB_FIRST_NAME_BUF_SIZE, first_name, i);
+ msg->getStringFast(_PREHASH_Data,_PREHASH_LastName, DB_LAST_NAME_BUF_SIZE, last_name, i);
+
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, avatar_id );
+
+ if (avatar_id.isNull())
+ {
+ self->childSetTextArg("NotFound", "[TEXT]", self->childGetText("Edit"));
+ LLString msg = self->childGetValue("NotFound").asString();
+ row->addColumn(msg, LLFontGL::sSansSerif);
+ self->mListNames->setEnabled(FALSE);
+ }
+ else
+ {
+ LLString buffer = LLString(first_name) + " " + last_name;
+ row->addColumn(buffer, LLFontGL::sSansSerif);
+ self->mListNames->setEnabled(TRUE);
+ found_one = TRUE;
+ }
+ self->mListNames->addItem(row);
+ }
+
+ if (found_one)
+ {
+ self->mListNames->selectFirstItem();
+ self->onList(self->mListNames, self);
+ self->mListNames->setFocus(TRUE);
+ }
+ }
+}
+
+//static
+void LLFloaterAvatarPicker::editKeystroke(LLLineEditor* caller, void* user_data)
+{
+ LLFloaterAvatarPicker* self = (LLFloaterAvatarPicker*)user_data;
+ if (caller->getText().size() >= 3)
+ {
+ self->childSetEnabled("Find",TRUE);
+ }
+ else
+ {
+ self->childSetEnabled("Find",FALSE);
+ }
+}
+
+// virtual
+BOOL LLFloaterAvatarPicker::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (key == KEY_RETURN
+ && mask == MASK_NONE)
+ {
+ if (childHasFocus("Edit"))
+ {
+ onBtnFind(this);
+ return TRUE;
+ }
+ else
+ {
+ onBtnAdd(this);
+ return TRUE;
+ }
+ }
+ else if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ close();
+ return TRUE;
+ }
+
+ return LLFloater::handleKeyHere(key, mask, called_from_parent);
+}
diff --git a/indra/newview/llfloateravatarpicker.h b/indra/newview/llfloateravatarpicker.h
new file mode 100644
index 0000000000..694b760f96
--- /dev/null
+++ b/indra/newview/llfloateravatarpicker.h
@@ -0,0 +1,78 @@
+/**
+ * @file llfloateravatarpicker.h
+ * @brief was llavatarpicker.h
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLFLOATERAVATARPICKER_H
+#define LLFLOATERAVATARPICKER_H
+
+#include "llfloater.h"
+
+#include <vector>
+
+class LLUICtrl;
+class LLTextBox;
+class LLLineEditor;
+class LLButton;
+class LLScrollListCtrl;
+class LLMessageSystem;
+class LLInventoryPanel;
+class LLFolderViewItem;
+
+class LLFloaterAvatarPicker : public LLFloater
+{
+public:
+ // Call this to select an avatar.
+ // The callback function will be called with an avatar name and UUID.
+ typedef void(*callback_t)(const std::vector<std::string>&, const std::vector<LLUUID>&, void*);
+ static LLFloaterAvatarPicker* show(callback_t callback,
+ void* userdata,
+ BOOL allow_multiple = FALSE,
+ BOOL closeOnSelect = FALSE);
+ virtual BOOL postBuild();
+
+ static void processAvatarPickerReply(LLMessageSystem* msg, void**);
+ static void editKeystroke(LLLineEditor* caller, void* user_data);
+
+protected:
+ static void* createInventoryPanel(void* userdata);
+
+ static void onBtnFind(void* userdata);
+ static void onBtnAdd(void* userdata);
+ static void onBtnClose(void* userdata);
+ static void onList(LLUICtrl* ctrl, void* userdata);
+ static void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+
+ void find();
+ void setAllowMultiple(BOOL allow_multiple);
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+protected:
+ LLScrollListCtrl* mListNames;
+ LLInventoryPanel* mInventoryPanel;
+
+ std::vector<LLUUID> mAvatarIDs;
+ std::vector<std::string> mAvatarNames;
+ BOOL mAllowMultiple;
+ LLUUID mQueryID;
+ BOOL mResultsReturned;
+ BOOL mCloseOnSelect;
+
+ void (*mCallback)(const std::vector<std::string>& name, const std::vector<LLUUID>& id, void* userdata);
+ void* mCallbackUserdata;
+
+protected:
+ static LLFloaterAvatarPicker* sInstance;
+
+protected:
+ // do not call these directly
+ LLFloaterAvatarPicker();
+ virtual ~LLFloaterAvatarPicker();
+};
+
+
+#endif
diff --git a/indra/newview/llfloateravatartextures.cpp b/indra/newview/llfloateravatartextures.cpp
new file mode 100644
index 0000000000..cb4d8dd8e0
--- /dev/null
+++ b/indra/newview/llfloateravatartextures.cpp
@@ -0,0 +1,180 @@
+/**
+ * @file llfloateravatartextures.cpp
+ * @brief Debugging view showing underlying avatar textures and baked textures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateravatartextures.h"
+
+#include "lltexturectrl.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+
+LLFloaterAvatarTextures::LLFloaterAvatarTextures(const LLUUID& id) :
+ LLFloater("avatar_texture_debug"),
+ mID(id)
+{
+}
+
+LLFloaterAvatarTextures::~LLFloaterAvatarTextures()
+{
+}
+
+LLFloaterAvatarTextures* LLFloaterAvatarTextures::show(const LLUUID &id)
+{
+
+ LLFloaterAvatarTextures* floaterp = new LLFloaterAvatarTextures(id);
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(floaterp, "floater_avatar_textures.xml");
+
+ gFloaterView->addChild(floaterp);
+ floaterp->open();
+
+ gFloaterView->adjustToFitScreen(floaterp, FALSE);
+
+ return floaterp;
+}
+
+BOOL LLFloaterAvatarTextures::postBuild()
+{
+ mBakedHead = (LLTextureCtrl*)getChildByName("baked_head");
+ mBakedEyes = (LLTextureCtrl*)getChildByName("baked_eyes");
+ mBakedUpper = (LLTextureCtrl*)getChildByName("baked_upper_body");
+ mBakedLower = (LLTextureCtrl*)getChildByName("baked_lower_body");
+ mBakedSkirt = (LLTextureCtrl*)getChildByName("baked_skirt");
+ mHair = (LLTextureCtrl*)getChildByName("hair");
+ mMakeup = (LLTextureCtrl*)getChildByName("head_bodypaint");
+ mEye = (LLTextureCtrl*)getChildByName("eye_texture");
+ mShirt = (LLTextureCtrl*)getChildByName("shirt");
+ mUpperTattoo = (LLTextureCtrl*)getChildByName("upper_bodypaint");
+ mUpperJacket = (LLTextureCtrl*)getChildByName("upper_jacket");
+ mGloves = (LLTextureCtrl*)getChildByName("gloves");
+ mUndershirt = (LLTextureCtrl*)getChildByName("undershirt");
+ mPants = (LLTextureCtrl*)getChildByName("pants");
+ mLowerTattoo = (LLTextureCtrl*)getChildByName("lower_bodypaint");
+ mShoes = (LLTextureCtrl*)getChildByName("shoes");
+ mSocks = (LLTextureCtrl*)getChildByName("socks");
+ mJacket = (LLTextureCtrl*)getChildByName("jacket");
+ mUnderpants = (LLTextureCtrl*)getChildByName("underpants");
+ mSkirt = (LLTextureCtrl*)getChildByName("skirt_texture");
+ mTitle = getTitle();
+
+ childSetAction("Dump", onClickDump, this);
+
+ refresh();
+ return TRUE;
+}
+
+void LLFloaterAvatarTextures::draw()
+{
+ refresh();
+ LLFloater::draw();
+}
+
+static void update_texture_ctrl(LLVOAvatar* avatarp,
+ LLTextureCtrl* ctrl,
+ LLVOAvatar::ETextureIndex te)
+{
+ LLUUID id = avatarp->getTE(te)->getID();
+ if (id == IMG_DEFAULT_AVATAR)
+ {
+ ctrl->setImageAssetID(LLUUID::null);
+ ctrl->setToolTip("IMG_DEFAULT_AVATAR");
+ }
+ else
+ {
+ ctrl->setImageAssetID(id);
+ ctrl->setToolTip(id.getString());
+ }
+}
+
+static LLVOAvatar* find_avatar(const LLUUID& id)
+{
+ LLViewerObject *obj = gObjectList.findObject(id);
+ while (obj && obj->isAttachment())
+ {
+ obj = (LLViewerObject *)obj->getParent();
+ }
+
+ if (obj && obj->isAvatar())
+ {
+ return (LLVOAvatar*)obj;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLFloaterAvatarTextures::refresh()
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ LLVOAvatar *avatarp = find_avatar(mID);
+ if (avatarp)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ if (gCacheName->getName(avatarp->getID(), firstname, lastname))
+ {
+ LLString name;
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+
+ setTitle(mTitle + ": " + name);
+ }
+ update_texture_ctrl(avatarp, mBakedHead, LLVOAvatar::TEX_HEAD_BAKED);
+ update_texture_ctrl(avatarp, mBakedEyes, LLVOAvatar::TEX_EYES_BAKED);
+ update_texture_ctrl(avatarp, mBakedUpper, LLVOAvatar::TEX_UPPER_BAKED);
+ update_texture_ctrl(avatarp, mBakedLower, LLVOAvatar::TEX_LOWER_BAKED);
+ update_texture_ctrl(avatarp, mBakedSkirt, LLVOAvatar::TEX_SKIRT_BAKED);
+
+ update_texture_ctrl(avatarp, mMakeup, LLVOAvatar::TEX_HEAD_BODYPAINT);
+ update_texture_ctrl(avatarp, mHair, LLVOAvatar::TEX_HAIR);
+ update_texture_ctrl(avatarp, mEye, LLVOAvatar::TEX_EYES_IRIS);
+
+ update_texture_ctrl(avatarp, mShirt, LLVOAvatar::TEX_UPPER_SHIRT);
+ update_texture_ctrl(avatarp, mUpperTattoo, LLVOAvatar::TEX_UPPER_BODYPAINT);
+ update_texture_ctrl(avatarp, mUpperJacket, LLVOAvatar::TEX_UPPER_JACKET);
+ update_texture_ctrl(avatarp, mGloves, LLVOAvatar::TEX_UPPER_GLOVES);
+ update_texture_ctrl(avatarp, mUndershirt, LLVOAvatar::TEX_UPPER_UNDERSHIRT);
+
+ update_texture_ctrl(avatarp, mPants, LLVOAvatar::TEX_LOWER_PANTS);
+ update_texture_ctrl(avatarp, mLowerTattoo, LLVOAvatar::TEX_LOWER_BODYPAINT);
+ update_texture_ctrl(avatarp, mShoes, LLVOAvatar::TEX_LOWER_SHOES);
+ update_texture_ctrl(avatarp, mSocks, LLVOAvatar::TEX_LOWER_SOCKS);
+ update_texture_ctrl(avatarp, mJacket, LLVOAvatar::TEX_LOWER_JACKET);
+ update_texture_ctrl(avatarp, mUnderpants, LLVOAvatar::TEX_LOWER_UNDERPANTS);
+ update_texture_ctrl(avatarp, mSkirt, LLVOAvatar::TEX_SKIRT);
+ }
+ else
+ {
+ setTitle(mTitle + ": INVALID AVATAR (" + mID.getString() + ")");
+ }
+#endif
+}
+
+// static
+void LLFloaterAvatarTextures::onClickDump(void* data)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ LLFloaterAvatarTextures* self = (LLFloaterAvatarTextures*)data;
+ LLVOAvatar* avatarp = find_avatar(self->mID);
+ if (!avatarp) return;
+
+ for (S32 i = 0; i < avatarp->getNumTEs(); i++)
+ {
+ const LLTextureEntry* te = avatarp->getTE(i);
+ if (!te) continue;
+
+ llinfos << "Avatar TE " << i << " id " << te->getID() << llendl;
+ }
+#endif
+}
diff --git a/indra/newview/llfloateravatartextures.h b/indra/newview/llfloateravatartextures.h
new file mode 100644
index 0000000000..1366b21534
--- /dev/null
+++ b/indra/newview/llfloateravatartextures.h
@@ -0,0 +1,59 @@
+/**
+ * @file llfloateravatartextures.h
+ * @brief Debugging view showing underlying avatar textures and baked textures.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERAVATARTEXTURES_H
+#define LL_LLFLOATERAVATARTEXTURES_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "llstring.h"
+
+class LLTextureCtrl;
+
+class LLFloaterAvatarTextures : public LLFloater
+{
+public:
+ LLFloaterAvatarTextures(const LLUUID& id);
+ virtual ~LLFloaterAvatarTextures();
+
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ static LLFloaterAvatarTextures* show(const LLUUID& id);
+
+private:
+ static void onClickDump(void*);
+
+private:
+ LLUUID mID;
+ LLString mTitle;
+ LLTextureCtrl* mBakedHead;
+ LLTextureCtrl* mBakedEyes;
+ LLTextureCtrl* mBakedUpper;
+ LLTextureCtrl* mBakedLower;
+ LLTextureCtrl* mBakedSkirt;
+ LLTextureCtrl* mHair;
+ LLTextureCtrl* mMakeup;
+ LLTextureCtrl* mEye;
+ LLTextureCtrl* mShirt;
+ LLTextureCtrl* mUpperTattoo;
+ LLTextureCtrl* mUpperJacket;
+ LLTextureCtrl* mGloves;
+ LLTextureCtrl* mUndershirt;
+ LLTextureCtrl* mPants;
+ LLTextureCtrl* mLowerTattoo;
+ LLTextureCtrl* mShoes;
+ LLTextureCtrl* mSocks;
+ LLTextureCtrl* mJacket;
+ LLTextureCtrl* mUnderpants;
+ LLTextureCtrl* mSkirt;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuildoptions.cpp b/indra/newview/llfloaterbuildoptions.cpp
new file mode 100644
index 0000000000..de58497430
--- /dev/null
+++ b/indra/newview/llfloaterbuildoptions.cpp
@@ -0,0 +1,73 @@
+/**
+ * @file llfloaterbuildoptions.cpp
+ * @brief LLFloaterBuildOptions class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Panel for setting global object-editing options, specifically
+ * grid size and spacing.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuildoptions.h"
+#include "llvieweruictrlfactory.h"
+
+// library includes
+#include "llfontgl.h"
+#include "llcheckboxctrl.h"
+#include "llspinctrl.h"
+#include "llsliderctrl.h"
+
+// newview includes
+#include "llresmgr.h"
+#include "llviewercontrol.h"
+
+//
+// Globals
+//
+LLFloaterBuildOptions *LLFloaterBuildOptions::sInstance = NULL;
+
+//
+// Methods
+//
+LLFloaterBuildOptions::LLFloaterBuildOptions( )
+: LLFloater("build options floater")
+{
+ sInstance = this;
+}
+
+LLFloaterBuildOptions::~LLFloaterBuildOptions()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterBuildOptions::show(void*)
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterBuildOptions* floater = new LLFloaterBuildOptions();
+
+ gUICtrlFactory->buildFloater(floater, "floater_build_options.xml");
+ floater->open();
+ }
+}
+
+LLFloaterBuildOptions* LLFloaterBuildOptions::getInstance()
+{
+ return sInstance;
+}
+
+// static
+BOOL LLFloaterBuildOptions::visible(void*)
+{
+ return (sInstance != NULL);
+}
diff --git a/indra/newview/llfloaterbuildoptions.h b/indra/newview/llfloaterbuildoptions.h
new file mode 100644
index 0000000000..d3483dd6d4
--- /dev/null
+++ b/indra/newview/llfloaterbuildoptions.h
@@ -0,0 +1,36 @@
+/**
+ * @file llfloaterbuildoptions.h
+ * @brief LLFloaterBuildOptions class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Panel for setting global object-editing options, specifically
+ * grid size and spacing.
+ */
+
+#ifndef LL_LLFLOATERBUILDOPTIONS_H
+#define LL_LLFLOATERBUILDOPTIONS_H
+
+#include "llfloater.h"
+
+
+class LLFloaterBuildOptions
+: public LLFloater
+{
+protected:
+ LLFloaterBuildOptions();
+ ~LLFloaterBuildOptions();
+
+public:
+ static void show(void*);
+ static LLFloaterBuildOptions* getInstance();
+ static BOOL visible(void*);
+
+protected:
+ static LLFloaterBuildOptions* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbump.cpp b/indra/newview/llfloaterbump.cpp
new file mode 100644
index 0000000000..8b81c661d8
--- /dev/null
+++ b/indra/newview/llfloaterbump.cpp
@@ -0,0 +1,138 @@
+/**
+ * @file llfloaterbump.cpp
+ * @brief Floater showing recent bumps, hits with objects, pushes, etc.
+ * @author Cory Ondrejka, James Cook
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbump.h"
+
+#include "llscrolllistctrl.h"
+
+#include "llvieweruictrlfactory.h"
+#include "viewer.h" // gPacificDaylightTime
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+extern LLLinkedList<LLMeanCollisionData> gMeanCollisionList;
+
+LLFloaterBump* LLFloaterBump::sInstance = NULL;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterBump
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterBump::LLFloaterBump()
+: LLFloater()
+{
+ sInstance = this;
+
+ gUICtrlFactory->buildFloater(this, "floater_bumps.xml");
+}
+
+
+// Destroys the object
+LLFloaterBump::~LLFloaterBump()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterBump::show(void *contents)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterBump();
+ }
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "bump_list");
+ if (!list) return;
+ list->deleteAllItems();
+
+ if (gMeanCollisionList.isEmpty())
+ {
+ LLString none_detected = sInstance->childGetText("none_detected");
+ LLScrollListItem *item = new LLScrollListItem();
+ item->addColumn(none_detected, LLFontGL::sSansSerifBold);
+ list->addItem(item);
+ }
+ else
+ {
+ for (LLMeanCollisionData* mcd = gMeanCollisionList.getFirstData();
+ mcd;
+ mcd = gMeanCollisionList.getNextData())
+ {
+ LLFloaterBump::add(list, mcd);
+ }
+ }
+
+ sInstance->open();
+}
+
+void LLFloaterBump::add(LLScrollListCtrl* list, LLMeanCollisionData* mcd)
+{
+ if (!sInstance)
+ {
+ new LLFloaterBump();
+ }
+
+ if (!mcd->mFirstName[0]
+ || list->getItemCount() >= 20)
+ {
+ return;
+ }
+
+ // There's only one internal tm buffer.
+ struct tm* timep;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(mcd->mTime, gPacificDaylightTime);
+
+ LLString time = llformat("[%d:%02d]", timep->tm_hour, timep->tm_min);
+
+ LLString action;
+ switch(mcd->mType)
+ {
+ case MEAN_BUMP:
+ action = "bump";
+ break;
+ case MEAN_LLPUSHOBJECT:
+ action = "llpushobject";
+ break;
+ case MEAN_SELECTED_OBJECT_COLLIDE:
+ action = "selected_object_collide";
+ break;
+ case MEAN_SCRIPTED_OBJECT_COLLIDE:
+ action = "scripted_object_collide";
+ break;
+ case MEAN_PHYSICAL_OBJECT_COLLIDE:
+ action = "physical_object_collide";
+ break;
+ default:
+ llinfos << "LLFloaterBump::add unknown mean collision type "
+ << mcd->mType << llendl;
+ return;
+ }
+
+ // All above action strings are in XML file
+ LLUIString text = sInstance->childGetText(action);
+ text.setArg("[TIME]", time);
+ text.setArg("[FIRST]", mcd->mFirstName);
+ text.setArg("[LAST]", mcd->mLastName);
+
+ LLScrollListItem *item = new LLScrollListItem(TRUE, NULL, mcd->mPerp);
+ item->addColumn(text, LLFontGL::sSansSerifBold);
+ list->addItem(item);
+}
diff --git a/indra/newview/llfloaterbump.h b/indra/newview/llfloaterbump.h
new file mode 100644
index 0000000000..39f98dfb9f
--- /dev/null
+++ b/indra/newview/llfloaterbump.h
@@ -0,0 +1,33 @@
+/**
+ * @file llfloaterbump.h
+ * @brief Floater showing recent bumps, hits with objects, pushes, etc.
+ * @author Cory Ondrejka, James Cook
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUMP_H
+#define LL_LLFLOATERBUMP_H
+
+#include "llfloater.h"
+
+class LLMeanCollisionData;
+class LLScrollListCtrl;
+
+class LLFloaterBump
+: public LLFloater
+{
+public:
+ static void show(void *);
+
+private:
+ LLFloaterBump();
+ virtual ~LLFloaterBump();
+ static void add(LLScrollListCtrl* list, LLMeanCollisionData *mcd);
+
+private:
+ static LLFloaterBump* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuy.cpp b/indra/newview/llfloaterbuy.cpp
new file mode 100644
index 0000000000..221c58cff3
--- /dev/null
+++ b/indra/newview/llfloaterbuy.cpp
@@ -0,0 +1,288 @@
+/**
+ * @file llfloaterbuy.cpp
+ * @author James Cook
+ * @brief LLFloaterBuy class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Floater that appears when buying an object, giving a preview
+ * of its contents and their permissions.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuy.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llinventorymodel.h" // for gInventory
+#include "llinventoryview.h" // for get_item_icon
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "llviewerobject.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterBuy* LLFloaterBuy::sInstance = NULL;
+
+LLFloaterBuy::LLFloaterBuy()
+: LLFloater("floater_buy_object", "FloaterBuyRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_buy_object.xml");
+
+ childDisable("object_list");
+ childDisable("item_list");
+
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("buy_btn", onClickBuy, this);
+
+ setDefaultBtn("buy_btn");
+}
+
+LLFloaterBuy::~LLFloaterBuy()
+{
+ gSelectMgr->deselectAll();
+
+ sInstance = NULL;
+}
+
+void LLFloaterBuy::reset()
+{
+ LLScrollListCtrl* object_list = LLUICtrlFactory::getScrollListByName(this, "object_list");
+ if (object_list) object_list->deleteAllItems();
+
+ LLScrollListCtrl* item_list = LLUICtrlFactory::getScrollListByName(this, "item_list");
+ if (item_list) item_list->deleteAllItems();
+}
+
+// static
+void LLFloaterBuy::show(const LLSaleInfo& sale_info)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("BuyOneObjectOnly");
+ return;
+ }
+
+ // Create a new instance only if one doesn't exist
+ if (sInstance)
+ {
+ // Clean up the lists...
+ sInstance->reset();
+ }
+ else
+ {
+ sInstance = new LLFloaterBuy();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+ sInstance->mSaleInfo = sale_info;
+
+ // Always center the dialog. User can change the size,
+ // but purchases are important and should be center screen.
+ // This also avoids problems where the user resizes the application window
+ // mid-session and the saved rect is off-center.
+ sInstance->center();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+
+ // Set title based on sale type
+ std::ostringstream title;
+ switch (sale_info.getSaleType())
+ {
+ case LLSaleInfo::FS_ORIGINAL:
+ title << "Buy " << node->mName; // XUI:translate
+ break;
+ case LLSaleInfo::FS_COPY:
+ default:
+ title << "Buy Copy of " << node->mName; // XUI:translate
+ break;
+ }
+ sInstance->setTitle(title.str());
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ gViewerWindow->alertXml("BuyObjectOneOwner");
+ return;
+ }
+
+ LLCtrlListInterface *object_list = sInstance->childGetListInterface("object_list");
+ if (!object_list)
+ {
+ return;
+ }
+
+ // Update the display
+ // Display next owner permissions
+ LLSD row;
+
+ // Compute icon for this item
+ LLUUID icon_id = get_item_icon_uuid(LLAssetType::AT_OBJECT,
+ LLInventoryType::IT_OBJECT,
+ 0x0);
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = node->mPermissions->getMaskNextOwner();
+ LLString text = node->mName;
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)"); // XUI:translate
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ // Add after columns added so appropriate heights are correct.
+ object_list->addElement(row);
+
+ sInstance->childSetTextArg("buy_text", "[AMOUNT]", llformat("%d", sale_info.getSalePrice()));
+ sInstance->childSetTextArg("buy_text", "[NAME]", owner_name);
+
+ // Must do this after the floater is created, because
+ // sometimes the inventory is already there and
+ // the callback is called immediately.
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ sInstance->registerVOInventoryListener(obj,NULL);
+ sInstance->requestVOInventory();
+}
+
+
+void LLFloaterBuy::inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data)
+{
+ if (!obj)
+ {
+ llwarns << "No object in LLFloaterBuy::inventoryChanged" << llendl;
+ return;
+ }
+
+ if (!inv)
+ {
+ llwarns << "No inventory in LLFloaterBuy::inventoryChanged"
+ << llendl;
+ removeVOInventoryListener();
+ return;
+ }
+
+ LLCtrlListInterface *item_list = childGetListInterface("item_list");
+ if (!item_list)
+ {
+ removeVOInventoryListener();
+ return;
+ }
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+ for ( ; it != end; ++it )
+ {
+ LLInventoryObject* obj = (LLInventoryObject*)(*it);
+
+ // Skip folders, so we know we have inventory items only
+ if (obj->getType() == LLAssetType::AT_CATEGORY)
+ continue;
+
+ // Skip root folders, so we know we have inventory items only
+ if (obj->getType() == LLAssetType::AT_ROOT_CATEGORY)
+ continue;
+
+ // Skip the mysterious blank InventoryObject
+ if (obj->getType() == LLAssetType::AT_NONE)
+ continue;
+
+
+ LLInventoryItem* inv_item = (LLInventoryItem*)(obj);
+
+ // Skip items we can't transfer
+ if (!inv_item->getPermissions().allowTransferTo(gAgent.getID()))
+ continue;
+
+ // Create the line in the list
+ LLSD row;
+
+ // Compute icon for this item
+ LLUUID icon_id = get_item_icon_uuid(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = inv_item->getPermissions().getMaskNextOwner();
+ LLString text = obj->getName();
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)");
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)");
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)");
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ item_list->addElement(row);
+ }
+ removeVOInventoryListener();
+}
+
+
+// static
+void LLFloaterBuy::onClickBuy(void*)
+{
+ if (!sInstance)
+ {
+ llinfos << "LLFloaterBuy::onClickBuy no sInstance!" << llendl;
+ return;
+ }
+
+ // Put the items where we put new folders.
+ LLUUID category_id;
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+
+ // FIXME: doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ gSelectMgr->sendBuy(gAgent.getID(), category_id, sInstance->mSaleInfo );
+
+ sInstance->close();
+}
+
+
+// static
+void LLFloaterBuy::onClickCancel(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+}
diff --git a/indra/newview/llfloaterbuy.h b/indra/newview/llfloaterbuy.h
new file mode 100644
index 0000000000..e90a77b7c9
--- /dev/null
+++ b/indra/newview/llfloaterbuy.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloaterbuy.h
+ * @author James Cook
+ * @brief LLFloaterBuy class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Floater that appears when buying an object, giving a preview
+ * of its contents and their permissions.
+ */
+
+#ifndef LL_LLFLOATERBUY_H
+#define LL_LLFLOATERBUY_H
+
+#include "llfloater.h"
+#include "llvoinventorylistener.h"
+#include "llsaleinfo.h"
+#include "llinventory.h"
+
+class LLViewerObject;
+class LLSaleInfo;
+
+class LLFloaterBuy
+: public LLFloater, public LLVOInventoryListener
+{
+public:
+ static void show(const LLSaleInfo& sale_info);
+
+protected:
+ LLFloaterBuy();
+ ~LLFloaterBuy();
+
+ void reset();
+
+ void requestObjectInventories();
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data);
+
+ static void onClickBuy(void*);
+ static void onClickCancel(void*);
+
+private:
+ static LLFloaterBuy* sInstance;
+
+ LLSaleInfo mSaleInfo;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuycontents.cpp b/indra/newview/llfloaterbuycontents.cpp
new file mode 100644
index 0000000000..c0ca919344
--- /dev/null
+++ b/indra/newview/llfloaterbuycontents.cpp
@@ -0,0 +1,274 @@
+/**
+ * @file llfloaterbuycontents.cpp
+ * @author James Cook
+ * @brief LLFloaterBuyContents class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuycontents.h"
+
+#include "llcachename.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llcheckboxctrl.h"
+#include "llinventorymodel.h" // for gInventory
+#include "llinventoryview.h" // for get_item_icon
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterBuyContents* LLFloaterBuyContents::sInstance = NULL;
+
+LLFloaterBuyContents::LLFloaterBuyContents()
+: LLFloater("floater_buy_contents", "FloaterBuyContentsRect", "")
+{
+ gUICtrlFactory->buildFloater(this, "floater_buy_contents.xml");
+
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("buy_btn", onClickBuy, this);
+
+ childDisable("item_list");
+ childDisable("buy_btn");
+ childDisable("wear_check");
+
+ setDefaultBtn("buy_btn");
+}
+
+LLFloaterBuyContents::~LLFloaterBuyContents()
+{
+ gSelectMgr->deselectAll();
+
+ sInstance = NULL;
+}
+
+
+// static
+void LLFloaterBuyContents::show(const LLSaleInfo& sale_info)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("BuyContentsOneOnly");
+ return;
+ }
+
+ // Create a new instance only if needed
+ if (sInstance)
+ {
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "item_list");
+ if (list) list->deleteAllItems();
+ }
+ else
+ {
+ sInstance = new LLFloaterBuyContents();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+
+ // Always center the dialog. User can change the size,
+ // but purchases are important and should be center screen.
+ // This also avoids problems where the user resizes the application window
+ // mid-session and the saved rect is off-center.
+ sInstance->center();
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ gViewerWindow->alertXml("BuyContentsOneOwner");
+ return;
+ }
+
+ sInstance->mSaleInfo = sale_info;
+
+ // Update the display
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+ if(node->mPermissions->isGroupOwned())
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(owner_id, group_name);
+ owner_name.assign(group_name);
+ }
+
+ sInstance->childSetTextArg("contains_text", "[NAME]", node->mName);
+ sInstance->childSetTextArg("buy_text", "[AMOUNT]", llformat("%d", sale_info.getSalePrice()));
+ sInstance->childSetTextArg("buy_text", "[NAME]", owner_name);
+
+ // Must do this after the floater is created, because
+ // sometimes the inventory is already there and
+ // the callback is called immediately.
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ sInstance->registerVOInventoryListener(obj,NULL);
+ sInstance->requestVOInventory();
+}
+
+
+void LLFloaterBuyContents::inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data)
+{
+ if (!obj)
+ {
+ llwarns << "No object in LLFloaterBuyContents::inventoryChanged" << llendl;
+ return;
+ }
+
+ if (!inv)
+ {
+ llwarns << "No inventory in LLFloaterBuyContents::inventoryChanged"
+ << llendl;
+ removeVOInventoryListener();
+ return;
+ }
+
+ LLCtrlListInterface *item_list = childGetListInterface("item_list");
+ if (!item_list)
+ {
+ removeVOInventoryListener();
+ return;
+ }
+
+ // default to turning off the buy button.
+ childDisable("buy_btn");
+
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ LLAssetType::EType asset_type;
+ LLInventoryType::EType inv_type;
+ S32 wearable_count = 0;
+
+ InventoryObjectList::const_iterator it = inv->begin();
+ InventoryObjectList::const_iterator end = inv->end();
+
+ for ( ; it != end; ++it )
+ {
+ asset_type = (*it)->getType();
+
+ // Skip folders, so we know we have inventory items only
+ if (asset_type == LLAssetType::AT_CATEGORY)
+ continue;
+
+ // Skip root folders, so we know we have inventory items only
+ if (asset_type == LLAssetType::AT_ROOT_CATEGORY)
+ continue;
+
+ LLInventoryItem* inv_item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ inv_type = inv_item->getInventoryType();
+
+ // Count clothing items for later
+ if (LLInventoryType::IT_WEARABLE == inv_type)
+ {
+ wearable_count++;
+ }
+
+ // Skip items the object's owner can't copy (and hence can't sell)
+ if (!inv_item->getPermissions().getOwnership(owner_id, is_group_owned))
+ continue;
+
+ if (!inv_item->getPermissions().allowCopyBy(owner_id, owner_id))
+ continue;
+
+ // Skip items we can't transfer
+ if (!inv_item->getPermissions().allowTransferTo(gAgent.getID()))
+ continue;
+
+ // There will be at least one item shown in the display, so go
+ // ahead and enable the buy button.
+ childEnable("buy_btn");
+
+ // Create the line in the list
+ LLSD row;
+ LLUUID icon_id = get_item_icon_uuid(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+ row["columns"][0]["column"] = "icon";
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+
+ // Append the permissions that you will acquire (not the current
+ // permissions).
+ U32 next_owner_mask = inv_item->getPermissions().getMaskNextOwner();
+ LLString text = (*it)->getName();
+
+ // *TODO: Move into shared library function.
+ if (!(next_owner_mask & PERM_COPY))
+ {
+ text.append(" (no copy)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_MODIFY))
+ {
+ text.append(" (no modify)"); // XUI:translate
+ }
+ if (!(next_owner_mask & PERM_TRANSFER))
+ {
+ text.append(" (no transfer)"); // XUI:translate
+ }
+
+ row["columns"][1]["column"] = "text";
+ row["columns"][1]["value"] = text;
+ row["columns"][1]["font"] = "SANSSERIF";
+
+ item_list->addElement(row);
+ }
+
+ if (wearable_count > 0)
+ {
+ childEnable("wear_check");
+ childSetValue("wear_check", LLSD(false) );
+ }
+
+ removeVOInventoryListener();
+}
+
+
+// static
+void LLFloaterBuyContents::onClickBuy(void*)
+{
+ // Make sure this wasn't selected through other mechanisms
+ // (ie, being the default button and pressing enter.
+ if(!sInstance->childIsEnabled("buy_btn"))
+ {
+ // We shouldn't be enabled. Just close.
+ sInstance->close();
+ return;
+ }
+
+ // We may want to wear this item
+ if (sInstance->childGetValue("wear_check"))
+ {
+ LLInventoryView::sWearNewClothing = TRUE;
+ }
+
+ // Put the items where we put new folders.
+ LLUUID category_id;
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CATEGORY);
+
+ // FIXME: doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ gSelectMgr->sendBuy(gAgent.getID(), category_id, sInstance->mSaleInfo);
+
+ sInstance->close();
+}
+
+
+// static
+void LLFloaterBuyContents::onClickCancel(void*)
+{
+ sInstance->close();
+}
diff --git a/indra/newview/llfloaterbuycontents.h b/indra/newview/llfloaterbuycontents.h
new file mode 100644
index 0000000000..1f834da668
--- /dev/null
+++ b/indra/newview/llfloaterbuycontents.h
@@ -0,0 +1,49 @@
+/**
+ * @file llfloaterbuycontents.h
+ * @author James Cook
+ * @brief LLFloaterBuyContents class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#ifndef LL_LLFLOATERBUYCONTENTS_H
+#define LL_LLFLOATERBUYCONTENTS_H
+
+#include "llfloater.h"
+#include "llvoinventorylistener.h"
+#include "llinventory.h"
+
+class LLViewerObject;
+
+class LLFloaterBuyContents
+: public LLFloater, public LLVOInventoryListener
+{
+public:
+ static void show(const LLSaleInfo& sale_info);
+
+protected:
+ LLFloaterBuyContents();
+ ~LLFloaterBuyContents();
+
+ void requestObjectInventories();
+ /*virtual*/ void inventoryChanged(LLViewerObject* obj,
+ InventoryObjectList* inv,
+ S32 serial_num,
+ void* data);
+
+ static void onClickBuy(void*);
+ static void onClickCancel(void*);
+
+protected:
+ static LLFloaterBuyContents* sInstance;
+
+ LLSaleInfo mSaleInfo;
+};
+
+#endif
diff --git a/indra/newview/llfloaterbuycurrency.cpp b/indra/newview/llfloaterbuycurrency.cpp
new file mode 100644
index 0000000000..55667b55e1
--- /dev/null
+++ b/indra/newview/llfloaterbuycurrency.cpp
@@ -0,0 +1,356 @@
+/**
+ * @file llfloaterbuycurrency.cpp
+ * @brief LLFloaterBuyCurrency class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuycurrency.h"
+
+// viewer includes
+#include "llcurrencyuimanager.h"
+#include "llfloater.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llviewchildren.h"
+#include "llviewerwindow.h"
+#include "llvieweruictrlfactory.h"
+#include "llweb.h"
+#include "llwindow.h"
+#include "viewer.h"
+
+static const S32 STANDARD_BUY_AMOUNT = 1000;
+static const S32 MINIMUM_BALANCE_AMOUNT = 0;
+
+class LLFloaterBuyCurrencyUI
+: public LLFloater
+{
+public:
+ static LLFloaterBuyCurrencyUI* soleInstance(bool createIfNeeded);
+
+private:
+ static LLFloaterBuyCurrencyUI* sInstance;
+
+ LLFloaterBuyCurrencyUI();
+ virtual ~LLFloaterBuyCurrencyUI();
+
+
+public:
+ LLViewChildren mChildren;
+ LLCurrencyUIManager mManager;
+
+ bool mHasTarget;
+ std::string mTargetName;
+ S32 mTargetPrice;
+
+public:
+ void noTarget();
+ void target(const std::string& name, S32 price);
+
+ virtual BOOL postBuild();
+
+ void updateUI();
+
+ virtual void draw();
+ virtual BOOL canClose();
+ virtual void onClose(bool app_quitting);
+
+ static void onClickBuy(void* data);
+ static void onClickCancel(void* data);
+ static void onClickErrorWeb(void* data);
+};
+
+
+// static
+LLFloaterBuyCurrencyUI* LLFloaterBuyCurrencyUI::sInstance = NULL;
+
+// static
+LLFloaterBuyCurrencyUI* LLFloaterBuyCurrencyUI::soleInstance(bool createIfNeeded)
+{
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterBuyCurrencyUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_buy_currency.xml");
+ sInstance->center();
+ }
+
+ return sInstance;
+}
+
+
+#if LL_WINDOWS
+// passing 'this' during construction generates a warning. The callee
+// only uses the pointer to hold a reference to 'this' which is
+// already valid, so this call does the correct thing. Disable the
+// warning so that we can compile without generating a warning.
+#pragma warning(disable : 4355)
+#endif
+LLFloaterBuyCurrencyUI::LLFloaterBuyCurrencyUI()
+: LLFloater("Buy Currency"),
+ mChildren(*this),
+ mManager(*this)
+{
+}
+
+LLFloaterBuyCurrencyUI::~LLFloaterBuyCurrencyUI()
+{
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+
+void LLFloaterBuyCurrencyUI::noTarget()
+{
+ mHasTarget = false;
+ mManager.setAmount(STANDARD_BUY_AMOUNT);
+}
+
+void LLFloaterBuyCurrencyUI::target(const std::string& name, S32 price)
+{
+ mHasTarget = true;
+ mTargetName = name;
+ mTargetPrice = price;
+
+ S32 balance = gStatusBar->getBalance();
+ S32 need = price - balance;
+ if (need < 0)
+ {
+ need = 0;
+ }
+
+ mManager.setAmount(need + MINIMUM_BALANCE_AMOUNT);
+}
+
+
+// virtual
+BOOL LLFloaterBuyCurrencyUI::postBuild()
+{
+ mManager.prepare();
+
+ childSetAction("buy_btn", onClickBuy, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("error_web", onClickErrorWeb, this);
+
+ updateUI();
+
+ return TRUE;
+}
+
+void LLFloaterBuyCurrencyUI::draw()
+{
+ if (mManager.process())
+ {
+ if (mManager.bought())
+ {
+ close();
+ return;
+ }
+
+ updateUI();
+ }
+
+ LLFloater::draw();
+}
+
+BOOL LLFloaterBuyCurrencyUI::canClose()
+{
+ return mManager.canCancel();
+}
+
+void LLFloaterBuyCurrencyUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+void LLFloaterBuyCurrencyUI::updateUI()
+{
+ bool hasError = mManager.hasError();
+ mManager.updateUI(!hasError && !mManager.buying());
+
+ // section zero: title area
+ {
+ childSetVisible("info_buying", false);
+ childSetVisible("info_cannot_buy", false);
+ childSetVisible("info_need_more", false);
+ if (hasError)
+ {
+ childSetVisible("info_cannot_buy", true);
+ }
+ else if (mHasTarget)
+ {
+ childSetVisible("info_need_more", true);
+ }
+ else
+ {
+ childSetVisible("info_buying", true);
+ }
+ }
+
+ // error section
+ if (hasError)
+ {
+ mChildren.setBadge("step_error", LLViewChildren::BADGE_ERROR);
+
+ LLTextBox* message = LLUICtrlFactory::getTextBoxByName(this, "error_message");
+ if (message)
+ {
+ message->setVisible(true);
+ message->setWrappedText(mManager.errorMessage());
+ }
+
+ childSetVisible("error_web", !mManager.errorURI().empty());
+ }
+ else
+ {
+ childHide("step_error");
+ childHide("error_message");
+ childHide("error_web");
+ }
+
+
+ // currency
+ childSetVisible("contacting", false);
+ childSetVisible("buy_action", false);
+ childSetVisible("buy_action_unknown", false);
+
+ if (!hasError)
+ {
+ mChildren.setBadge("step_1", LLViewChildren::BADGE_NOTE);
+
+ if (mManager.buying())
+ {
+ childSetVisible("contacting", true);
+ }
+ else
+ {
+ if (mHasTarget)
+ {
+ childSetVisible("buy_action", true);
+ childSetTextArg("buy_action", "[NAME]", mTargetName);
+ childSetTextArg("buy_action", "[PRICE]", llformat("%d",mTargetPrice));
+ }
+ else
+ {
+ childSetVisible("buy_action_unknown", true);
+ }
+ }
+
+ S32 balance = gStatusBar->getBalance();
+ childShow("balance_label");
+ childShow("balance_amount");
+ childSetTextArg("balance_amount", "[AMT]", llformat("%d", balance));
+
+ S32 buying = mManager.getAmount();
+ childShow("buying_label");
+ childShow("buying_amount");
+ childSetTextArg("buying_amount", "[AMT]", llformat("%d", buying));
+
+ S32 total = balance + buying;
+ childShow("total_label");
+ childShow("total_amount");
+ childSetTextArg("total_amount", "[AMT]", llformat("%d", total));
+
+ childSetVisible("purchase_warning_repurchase", false);
+ childSetVisible("purchase_warning_notenough", false);
+ if (mHasTarget)
+ {
+ if (total >= mTargetPrice)
+ {
+ childSetVisible("purchase_warning_repurchase", true);
+ }
+ else
+ {
+ childSetVisible("purchase_warning_notenough", true);
+ }
+ }
+ }
+ else
+ {
+ childHide("step_1");
+ childHide("balance_label");
+ childHide("balance_amount");
+ childHide("buying_label");
+ childHide("buying_amount");
+ childHide("total_label");
+ childHide("total_amount");
+ childHide("purchase_warning_repurchase");
+ childHide("purchase_warning_notenough");
+ }
+
+ childSetEnabled("buy_btn", mManager.canBuy());
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickBuy(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ self->mManager.buy();
+ self->updateUI();
+ // JC: updateUI() doesn't get called again until progress is made
+ // with transaction processing, so the "Purchase" button would be
+ // left enabled for some time. Pre-emptively disable.
+ self->childSetEnabled("buy_btn", false);
+ }
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickCancel(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ self->close();
+ }
+}
+
+// static
+void LLFloaterBuyCurrencyUI::onClickErrorWeb(void* data)
+{
+ LLFloaterBuyCurrencyUI* self = LLFloaterBuyCurrencyUI::soleInstance(false);
+ if (self)
+ {
+ LLWeb::loadURLExternal(self->mManager.errorURI());
+ self->close();
+ }
+}
+
+// static
+void LLFloaterBuyCurrency::buyCurrency()
+{
+ if (gHideLinks)
+ {
+ return;
+ }
+
+ LLFloaterBuyCurrencyUI* ui = LLFloaterBuyCurrencyUI::soleInstance(true);
+ ui->noTarget();
+ ui->updateUI();
+ ui->open();
+}
+
+void LLFloaterBuyCurrency::buyCurrency(const std::string& name, S32 price)
+{
+ if (gHideLinks)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = name.c_str();
+ args["[PRICE]"] = price;
+ gViewerWindow->alertXml("NotEnoughCurrency", args);
+ return;
+ }
+
+ LLFloaterBuyCurrencyUI* ui = LLFloaterBuyCurrencyUI::soleInstance(true);
+ ui->target(name, price);
+ ui->updateUI();
+ ui->open();
+}
+
diff --git a/indra/newview/llfloaterbuycurrency.h b/indra/newview/llfloaterbuycurrency.h
new file mode 100644
index 0000000000..aaa9a5328f
--- /dev/null
+++ b/indra/newview/llfloaterbuycurrency.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterbuycurrency.h
+ * @brief LLFloaterBuyCurrency class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUYCURRENCY_H
+#define LL_LLFLOATERBUYCURRENCY_H
+
+#include "stdtypes.h"
+
+class LLFloaterBuyCurrency
+{
+public:
+ static void buyCurrency();
+ static void buyCurrency(const std::string& name, S32 price);
+ /* name should be a noun phrase of the object or service being bought:
+ "That object costs"
+ "Trying to give"
+ "Uploading costs"
+ a space and the price will be appended
+ */
+};
+
+
+#endif
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp
new file mode 100644
index 0000000000..6c8b7368a0
--- /dev/null
+++ b/indra/newview/llfloaterbuyland.cpp
@@ -0,0 +1,1342 @@
+/**
+ * @file llfloaterbuyland.cpp
+ * @brief LLFloaterBuyLand class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterbuyland.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcachename.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llconfirmationmanager.h"
+#include "llcurrencyuimanager.h"
+#include "llfloater.h"
+#include "llfloatertools.h"
+#include "llframetimer.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llparcel.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "llviewchildren.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llweb.h"
+#include "llwindow.h"
+#include "llworld.h"
+#include "llxmlrpctransaction.h"
+#include "viewer.h"
+#include "roles_constants.h"
+
+// NOTE: This is duplicated in lldatamoney.cpp ...
+const F32 GROUP_LAND_BONUS_FACTOR = 1.1f;
+const F64 CURRENCY_ESTIMATE_FREQUENCY = 0.5;
+ // how long of a pause in typing a currency buy amount before an
+ // esimate is fetched from the server
+
+class LLFloaterBuyLandUI
+: public LLFloater
+{
+private:
+ LLFloaterBuyLandUI();
+ virtual ~LLFloaterBuyLandUI();
+
+ LLViewerRegion* mRegion;
+ LLParcel* mParcel;
+ bool mIsClaim;
+ bool mIsForGroup;
+
+ bool mCanBuy;
+ bool mCannotBuyIsError;
+ std::string mCannotBuyReason;
+ std::string mCannotBuyURI;
+
+ bool mBought;
+
+ // information about the agent
+ S32 mAgentCommittedTier;
+ S32 mAgentCashBalance;
+ bool mAgentHasNeverOwnedLand;
+
+ // information about the parcel
+ bool mParcelValid;
+ bool mParcelIsForSale;
+ bool mParcelIsFirstLand;
+ bool mParcelIsGroupLand;
+ S32 mParcelGroupContribution;
+ S32 mParcelPrice;
+ S32 mParcelActualArea;
+ S32 mParcelBillableArea;
+ S32 mParcelSupportedObjects;
+ bool mParcelSoldWithObjects;
+ std::string mParcelLocation;
+ LLUUID mParcelSnapshot;
+ std::string mParcelSellerName;
+
+ // user's choices
+ S32 mUserPlanChoice;
+
+ // from website
+ bool mSiteValid;
+ bool mSiteMembershipUpgrade;
+ std::string mSiteMembershipAction;
+ std::vector<std::string>
+ mSiteMembershipPlanIDs;
+ std::vector<std::string>
+ mSiteMembershipPlanNames;
+ bool mSiteLandUseUpgrade;
+ std::string mSiteLandUseAction;
+ std::string mSiteConfirm;
+
+ // values in current Preflight transaction... used to avoid extra
+ // preflights when the parcel manager goes update crazy
+ S32 mPreflightAskBillableArea;
+ S32 mPreflightAskCurrencyBuy;
+
+ LLViewChildren mChildren;
+ LLCurrencyUIManager mCurrency;
+
+ enum TransactionType
+ {
+ TransactionPreflight,
+ TransactionCurrency,
+ TransactionBuy
+ };
+ LLXMLRPCTransaction* mTransaction;
+ TransactionType mTransactionType;
+
+ LLViewerParcelMgr::ParcelBuyInfo* mParcelBuyInfo;
+
+ static LLFloaterBuyLandUI* sInstance;
+
+public:
+ static LLFloaterBuyLandUI* soleInstance(bool createIfNeeded);
+
+ void setForGroup(bool is_for_group);
+ void setParcel(LLViewerRegion* region, LLParcel* parcel);
+
+ void updateAgentInfo();
+ void updateParcelInfo();
+ void updateCovenantInfo();
+ static void onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data);
+ void updateCovenantText(const std::string& string, const LLUUID &asset_id);
+ void updateEstateName(const std::string& name);
+ void updateLastModified(const std::string& text);
+ void updateEstateOwnerName(const std::string& name);
+ void updateWebSiteInfo();
+ void finishWebSiteInfo();
+
+ void runWebSitePrep(const std::string& password);
+ void finishWebSitePrep();
+ void sendBuyLand();
+
+ void updateNames();
+
+ void refreshUI();
+
+ void startTransaction(TransactionType type, LLXMLRPCValue params);
+ bool checkTransaction();
+
+ void tellUserError(const std::string& message, const std::string& uri);
+
+ virtual BOOL postBuild();
+
+ void startBuyPreConfirm();
+ void startBuyPostConfirm(const std::string& password);
+
+ static void onClickBuy(void* data);
+ static void onClickCancel(void* data);
+ static void onClickErrorWeb(void* data);
+
+ virtual void draw();
+ virtual BOOL canClose();
+ virtual void onClose(bool app_quitting);
+
+private:
+ class SelectionObserver : public LLParcelObserver
+ {
+ public:
+ virtual void changed();
+ };
+};
+
+static void cacheNameUpdateRefreshesBuyLand(const LLUUID&,
+ const char*, const char*, BOOL, void* data)
+{
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false);
+ if (ui)
+ {
+ ui->updateNames();
+ }
+}
+
+// static
+void LLFloaterBuyLand::buyLand(
+ LLViewerRegion* region, LLParcel* parcel, bool is_for_group)
+{
+ if(is_for_group && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+ {
+ gViewerWindow->alertXml("OnlyOfficerCanBuyLand");
+ return;
+ }
+
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(true);
+ ui->setForGroup(is_for_group);
+ ui->setParcel(region, parcel);
+ ui->open();
+}
+
+// static
+void LLFloaterBuyLand::updateCovenantText(const std::string& string, const LLUUID &asset_id)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateCovenantText(string, asset_id);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateEstateName(const std::string& name)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateEstateName(name);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateLastModified(const std::string& text)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateLastModified(text);
+ }
+}
+
+// static
+void LLFloaterBuyLand::updateEstateOwnerName(const std::string& name)
+{
+ LLFloaterBuyLandUI* floater = LLFloaterBuyLandUI::soleInstance(FALSE);
+ if (floater)
+ {
+ floater->updateEstateOwnerName(name);
+ }
+}
+
+// static
+LLFloaterBuyLandUI* LLFloaterBuyLandUI::sInstance = NULL;
+
+// static
+LLFloaterBuyLandUI* LLFloaterBuyLandUI::soleInstance(bool createIfNeeded)
+{
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (createIfNeeded)
+ {
+ delete sInstance;
+ sInstance = NULL;
+ }
+#endif
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterBuyLandUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_buy_land.xml");
+ sInstance->center();
+
+ static bool observingCacheName = false;
+ if (!observingCacheName)
+ {
+ gCacheName->addObserver(cacheNameUpdateRefreshesBuyLand);
+ observingCacheName = true;
+ }
+
+ static SelectionObserver* parcelSelectionObserver = NULL;
+ if (!parcelSelectionObserver)
+ {
+ parcelSelectionObserver = new SelectionObserver;
+ gParcelMgr->addObserver(parcelSelectionObserver);
+ }
+ }
+
+ return sInstance;
+}
+
+
+#if LL_WINDOWS
+// passing 'this' during construction generates a warning. The callee
+// only uses the pointer to hold a reference to 'this' which is
+// already valid, so this call does the correct thing. Disable the
+// warning so that we can compile without generating a warning.
+#pragma warning(disable : 4355)
+#endif
+LLFloaterBuyLandUI::LLFloaterBuyLandUI()
+: LLFloater("Buy Land"),
+ mParcel(0),
+ mBought(false),
+ mParcelValid(false), mSiteValid(false),
+ mChildren(*this), mCurrency(*this), mTransaction(0),
+ mParcelBuyInfo(0)
+{
+}
+
+LLFloaterBuyLandUI::~LLFloaterBuyLandUI()
+{
+ delete mTransaction;
+
+ gParcelMgr->deleteParcelBuy(mParcelBuyInfo);
+
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+void LLFloaterBuyLandUI::SelectionObserver::changed()
+{
+ LLFloaterBuyLandUI* ui = LLFloaterBuyLandUI::soleInstance(false);
+ if (ui)
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ ui->close();
+ }
+ else {
+ ui->setParcel(
+ gParcelMgr->getSelectionRegion(),
+ gParcelMgr->getSelectedParcel());
+ }
+ }
+}
+
+
+void LLFloaterBuyLandUI::updateAgentInfo()
+{
+ mAgentCommittedTier = gStatusBar->getSquareMetersCommitted();
+ mAgentCashBalance = gStatusBar->getBalance();
+ mAgentHasNeverOwnedLand = mAgentCommittedTier == 0;
+ // FIXME: this is an approximation, see SL-10728
+}
+
+void LLFloaterBuyLandUI::updateParcelInfo()
+{
+ mParcelValid = mParcel && mRegion;
+ mParcelIsForSale = false;
+ mParcelIsFirstLand = false;
+ mParcelIsGroupLand = false;
+ mParcelGroupContribution = 0;
+ mParcelPrice = 0;
+ mParcelActualArea = 0;
+ mParcelBillableArea = 0;
+ mParcelSupportedObjects = 0;
+ mParcelSoldWithObjects = false;
+ mParcelLocation = "";
+ mParcelSnapshot.setNull();
+ mParcelSellerName = "";
+
+ mCanBuy = false;
+ mCannotBuyIsError = false;
+
+ if (!mParcelValid)
+ {
+ mCannotBuyReason= childGetText("no_land_selected");
+ return;
+ }
+
+ if (gParcelMgr->getMultipleOwners())
+ {
+ mCannotBuyReason = childGetText("multiple_parcels_selected");
+ return;
+ }
+
+
+ const LLUUID& parcelOwner = mParcel->getOwnerID();
+
+ mIsClaim = mParcel->isPublic();
+ if (!mIsClaim)
+ {
+ mParcelActualArea = mParcel->getArea();
+ mParcelIsForSale = mParcel->getForSale();
+ mParcelIsFirstLand = mParcel->getReservedForNewbie();
+ mParcelIsGroupLand = mParcel->getIsGroupOwned();
+ mParcelPrice = mParcelIsForSale ? mParcel->getSalePrice() : 0;
+
+ if (mParcelIsGroupLand)
+ {
+ LLUUID group_id = mParcel->getGroupID();
+ mParcelGroupContribution = gAgent.getGroupContribution(group_id);
+ }
+ }
+ else
+ {
+ mParcelActualArea = gParcelMgr->getClaimableArea();
+ mParcelIsForSale = true;
+ mParcelPrice = mParcelActualArea * mParcel->getClaimPricePerMeter();
+ }
+
+ mParcelBillableArea =
+ llround(mRegion->getBillableFactor() * mParcelActualArea);
+
+ mParcelSupportedObjects = mParcel->getMaxPrimCapacity();
+ mParcelSoldWithObjects = mParcel->getSellWithObjects();
+
+ LLVector3 center = mParcel->getCenterpoint();
+ mParcelLocation = llformat("%s %d,%d",
+ mRegion->getName().c_str(),
+ (int)center[VX], (int)center[VY]
+ );
+
+ mParcelSnapshot = mParcel->getSnapshotID();
+
+ updateNames();
+
+ bool haveEnoughCash = mParcelPrice <= mAgentCashBalance;
+ S32 cashBuy = haveEnoughCash ? 0 : (mParcelPrice - mAgentCashBalance);
+ mCurrency.setAmount(cashBuy, true);
+ mCurrency.setZeroMessage(haveEnoughCash ? childGetText("none_needed") : "");
+
+ // checks that we can buy the land
+
+ if(mIsForGroup && !gAgent.hasPowerInActiveGroup(GP_LAND_DEED))
+ {
+ mCannotBuyReason = childGetText("cant_buy_for_group");
+ return;
+ }
+
+ if (!mIsClaim)
+ {
+ const LLUUID& authorizedBuyer = mParcel->getAuthorizedBuyerID();
+ const LLUUID buyer = gAgent.getID();
+ const LLUUID newOwner = mIsForGroup ? gAgent.getGroupID() : buyer;
+
+ if (!mParcelIsForSale
+ || (mParcelPrice == 0 && authorizedBuyer.isNull()))
+ {
+
+ mCannotBuyReason = childGetText("parcel_not_for_sale");
+ return;
+ }
+
+ if (parcelOwner == newOwner)
+ {
+ if (mIsForGroup)
+ {
+ mCannotBuyReason = childGetText("group_already_owns");
+ }
+ else
+ {
+ mCannotBuyReason = childGetText("you_already_own");
+ }
+ return;
+ }
+
+ if (!authorizedBuyer.isNull() && buyer != authorizedBuyer)
+ {
+ mCannotBuyReason = childGetText("set_to_sell_to_other");
+ return;
+ }
+ }
+ else
+ {
+ if (mParcelActualArea == 0)
+ {
+ mCannotBuyReason = childGetText("no_public_land");
+ return;
+ }
+
+ if (gParcelMgr->hasOthersSelected())
+ {
+ // Policy: Must not have someone else's land selected
+ mCannotBuyReason = childGetText("not_owned_by_you");
+ return;
+ }
+ }
+
+ /*
+ if ((mRegion->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && !gAgent.isGodlike())
+ {
+ mCannotBuyReason = llformat(
+ "The region %s does not allow transfer of land.",
+ mRegion->getName().c_str() );
+ return;
+ }
+ */
+
+ if (mParcel->getReservedForNewbie())
+ {
+ if (mIsForGroup)
+ {
+ mCannotBuyReason = childGetText("first_time_group");
+ return;
+ }
+
+ if (gStatusBar->getSquareMetersCommitted() > 0)
+ {
+ mCannotBuyReason == childGetText("first_time");
+ return;
+ }
+
+ // FIXME: There should be a check based on the database value
+ // indra.user.ever_owned_land, only that value never makes it
+ // to the viewer, see SL-10728
+ }
+
+ mCanBuy = true;
+}
+
+void LLFloaterBuyLandUI::updateCovenantInfo()
+{
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(!region) return;
+
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ if(check)
+ {
+ check->set(false);
+ check->setEnabled(true);
+ check->setCallbackUserData(this);
+ check->setCommitCallback(onChangeAgreeCovenant);
+ }
+
+ LLTextBox* box = (LLTextBox*)getChildByName("covenant_text");
+ if(box)
+ {
+ box->setVisible(FALSE);
+ }
+
+ // send EstateCovenantInfo message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)user_data;
+ if(self)
+ {
+ self->refreshUI();
+ }
+}
+
+void LLFloaterBuyLandUI::updateCovenantText(const std::string &string, const LLUUID& asset_id)
+{
+ LLViewerTextEditor* editor = (LLViewerTextEditor*)getChildByName("covenant_editor");
+ if (editor)
+ {
+ editor->setHandleEditKeysDirectly(FALSE);
+ editor->setText(string);
+
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ LLTextBox* box = (LLTextBox*)getChildByName("covenant_text");
+ if(check && box)
+ {
+ if (asset_id.isNull())
+ {
+ check->set(true);
+ check->setEnabled(false);
+ refreshUI();
+
+ // remove the line stating that you must agree
+ box->setVisible(FALSE);
+ }
+ else
+ {
+ check->setEnabled(true);
+
+ // remove the line stating that you must agree
+ box->setVisible(TRUE);
+ }
+ }
+ }
+}
+
+void LLFloaterBuyLandUI::updateEstateName(const std::string& name)
+{
+ LLTextBox* box = (LLTextBox*)getChildByName("estate_name_text");
+ if (box) box->setText(name);
+}
+
+void LLFloaterBuyLandUI::updateLastModified(const std::string& text)
+{
+ LLTextBox* editor = (LLTextBox*)getChildByName("covenant_timestamp_text");
+ if (editor) editor->setText(text);
+}
+
+void LLFloaterBuyLandUI::updateEstateOwnerName(const std::string& name)
+{
+ LLTextBox* box = (LLTextBox*)getChildByName("estate_owner_text");
+ if (box) box->setText(name);
+}
+
+void LLFloaterBuyLandUI::updateWebSiteInfo()
+{
+ S32 askBillableArea = mIsForGroup ? 0 : mParcelBillableArea;
+ S32 askCurrencyBuy = mCurrency.getAmount();
+
+ if (mTransaction && mTransactionType == TransactionPreflight
+ && mPreflightAskBillableArea == askBillableArea
+ && mPreflightAskCurrencyBuy == askCurrencyBuy)
+ {
+ return;
+ }
+
+ mPreflightAskBillableArea = askBillableArea;
+ mPreflightAskCurrencyBuy = askCurrencyBuy;
+
+#if 0
+ // enable this code if you want the details to blank while we're talking
+ // to the web site... it's kind of jarring
+ mSiteValid = false;
+ mSiteMembershipUpgrade = false;
+ mSiteMembershipAction = "(waiting)";
+ mSiteMembershipPlanIDs.clear();
+ mSiteMembershipPlanNames.clear();
+ mSiteLandUseUpgrade = false;
+ mSiteLandUseAction = "(waiting)";
+ mSiteCurrencyEstimated = false;
+ mSiteCurrencyEstimatedCost = 0;
+#endif
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId", gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendInt("billableArea", mPreflightAskBillableArea);
+ keywordArgs.appendInt("currencyBuy", mPreflightAskCurrencyBuy);
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionPreflight, params);
+}
+
+void LLFloaterBuyLandUI::finishWebSiteInfo()
+{
+
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ mSiteValid = result["success"].asBool();
+ if (!mSiteValid)
+ {
+ tellUserError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ LLXMLRPCValue membership = result["membership"];
+ mSiteMembershipUpgrade = membership["upgrade"].asBool();
+ mSiteMembershipAction = membership["action"].asString();
+ mSiteMembershipPlanIDs.clear();
+ mSiteMembershipPlanNames.clear();
+ LLXMLRPCValue levels = membership["levels"];
+ for (LLXMLRPCValue level = levels.rewind();
+ level.isValid();
+ level = levels.next())
+ {
+ mSiteMembershipPlanIDs.push_back(level["id"].asString());
+ mSiteMembershipPlanNames.push_back(level["description"].asString());
+ }
+ mUserPlanChoice = 0;
+
+ LLXMLRPCValue landUse = result["landUse"];
+ mSiteLandUseUpgrade = landUse["upgrade"].asBool();
+ mSiteLandUseAction = landUse["action"].asString();
+
+ LLXMLRPCValue currency = result["currency"];
+ mCurrency.setEstimate(currency["estimatedCost"].asInt());
+
+ mSiteConfirm = result["confirm"].asString();
+}
+
+void LLFloaterBuyLandUI::runWebSitePrep(const std::string& password)
+{
+ if (!mCanBuy)
+ {
+ return;
+ }
+
+ BOOL remove_contribution = childGetValue("remove_contribution").asBoolean();
+ mParcelBuyInfo = gParcelMgr->setupParcelBuy(gAgent.getID(), gAgent.getSessionID(),
+ gAgent.getGroupID(), mIsForGroup, mIsClaim, remove_contribution);
+
+ if (!mSiteMembershipUpgrade
+ && !mSiteLandUseUpgrade
+ && mCurrency.getAmount() == 0
+ && mSiteConfirm != "password")
+ {
+ sendBuyLand();
+ return;
+ }
+
+
+ std::string newLevel = "noChange";
+
+ if (mSiteMembershipUpgrade)
+ {
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ mUserPlanChoice = levels->getCurrentIndex();
+ newLevel = mSiteMembershipPlanIDs[mUserPlanChoice];
+ }
+ }
+
+ LLXMLRPCValue keywordArgs = LLXMLRPCValue::createStruct();
+ keywordArgs.appendString("agentId", gAgent.getID().getString());
+ keywordArgs.appendString("secureSessionId",
+ gAgent.getSecureSessionID().getString());
+ keywordArgs.appendString("levelId", newLevel);
+ keywordArgs.appendInt("billableArea",
+ mIsForGroup ? 0 : mParcelBillableArea);
+ keywordArgs.appendInt("currencyBuy", mCurrency.getAmount());
+ keywordArgs.appendInt("estimatedCost", mCurrency.getEstimate());
+ keywordArgs.appendString("confirm", mSiteConfirm);
+ if (!password.empty())
+ {
+ keywordArgs.appendString("password", password);
+ }
+
+ LLXMLRPCValue params = LLXMLRPCValue::createArray();
+ params.append(keywordArgs);
+
+ startTransaction(TransactionBuy, params);
+}
+
+void LLFloaterBuyLandUI::finishWebSitePrep()
+{
+ LLXMLRPCValue result = mTransaction->responseValue();
+
+ bool success = result["success"].asBool();
+ if (!success)
+ {
+ tellUserError(
+ result["errorMessage"].asString(),
+ result["errorURI"].asString()
+ );
+ return;
+ }
+
+ sendBuyLand();
+}
+
+void LLFloaterBuyLandUI::sendBuyLand()
+{
+ gParcelMgr->sendParcelBuy(mParcelBuyInfo);
+ gParcelMgr->deleteParcelBuy(mParcelBuyInfo);
+
+ mBought = true;
+}
+
+void LLFloaterBuyLandUI::updateNames()
+{
+ if (!mParcelValid)
+ {
+ mParcelSellerName = "";
+ return;
+ }
+
+ if (mIsClaim)
+ {
+ mParcelSellerName = "Linden Lab";
+ }
+ else if (mParcel->getIsGroupOwned())
+ {
+ char groupName[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getGroupName(mParcel->getGroupID(), &groupName[0]);
+ mParcelSellerName = groupName;
+ }
+ else
+ {
+ char firstName[DB_LAST_NAME_BUF_SIZE];
+ char lastName[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getName(mParcel->getOwnerID(), firstName, lastName);
+ mParcelSellerName = llformat("%s %s", firstName, lastName);
+ }
+}
+
+
+void LLFloaterBuyLandUI::startTransaction(TransactionType type,
+ LLXMLRPCValue params)
+{
+ delete mTransaction;
+ mTransaction = NULL;
+
+ mTransactionType = type;
+
+ // Select a URI and method appropriate for the transaction type.
+ static std::string transaction_uri;
+ if (transaction_uri.empty())
+ {
+ transaction_uri = getHelperURI() + "landtool.php";
+ }
+
+ const char* method;
+ switch (mTransactionType)
+ {
+ case TransactionPreflight:
+ method = "preflightBuyLandPrep";
+ break;
+ case TransactionBuy:
+ method = "buyLandPrep";
+ break;
+ default:
+ llwarns << "LLFloaterBuyLandUI: Unknown transaction type!" << llendl;
+ return;
+ }
+
+ mTransaction = new LLXMLRPCTransaction(
+ transaction_uri,
+ method,
+ params,
+ false /* don't use gzip */
+ );
+}
+
+bool LLFloaterBuyLandUI::checkTransaction()
+{
+ if (!mTransaction)
+ {
+ return false;
+ }
+
+ if (!mTransaction->process())
+ {
+ return false;
+ }
+
+ if (mTransaction->status(NULL) != LLXMLRPCTransaction::StatusComplete)
+ {
+ tellUserError(mTransaction->statusMessage(), mTransaction->statusURI());
+ }
+ else {
+ switch (mTransactionType)
+ {
+ case TransactionPreflight: finishWebSiteInfo(); break;
+ case TransactionBuy: finishWebSitePrep(); break;
+ default: ;
+ }
+ }
+
+ delete mTransaction;
+ mTransaction = NULL;
+
+ return true;
+}
+
+void LLFloaterBuyLandUI::tellUserError(
+ const std::string& message, const std::string& uri)
+{
+ mCanBuy = false;
+ mCannotBuyIsError = true;
+ mCannotBuyReason = childGetText("fetching_error");
+ mCannotBuyReason += message;
+ mCannotBuyURI = uri;
+}
+
+
+// virtual
+BOOL LLFloaterBuyLandUI::postBuild()
+{
+ mCurrency.prepare();
+
+ childSetAction("buy_btn", onClickBuy, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("error_web", onClickErrorWeb, this);
+
+ return TRUE;
+}
+
+void LLFloaterBuyLandUI::setParcel(LLViewerRegion* region, LLParcel* parcel)
+{
+ if (mTransaction && mTransactionType == TransactionBuy)
+ {
+ // the user is buying, don't change the selection
+ return;
+ }
+
+ mRegion = region;
+ mParcel = parcel;
+
+ updateAgentInfo();
+ updateParcelInfo();
+ updateCovenantInfo();
+ if (mCanBuy)
+ {
+ updateWebSiteInfo();
+ }
+ refreshUI();
+}
+
+void LLFloaterBuyLandUI::setForGroup(bool forGroup)
+{
+ mIsForGroup = forGroup;
+}
+
+void LLFloaterBuyLandUI::draw()
+{
+ LLFloater::draw();
+
+ bool needsUpdate = false;
+ needsUpdate |= checkTransaction();
+ needsUpdate |= mCurrency.process();
+
+ if (mBought)
+ {
+ close();
+ }
+ else if (needsUpdate)
+ {
+ if (mCanBuy && mCurrency.hasError())
+ {
+ tellUserError(mCurrency.errorMessage(), mCurrency.errorURI());
+ }
+
+ refreshUI();
+ }
+}
+
+BOOL LLFloaterBuyLandUI::canClose()
+{
+ return (mTransaction ? FALSE : TRUE) && mCurrency.canCancel();
+}
+
+void LLFloaterBuyLandUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+
+void LLFloaterBuyLandUI::refreshUI()
+{
+ // section zero: title area
+ {
+ LLTextureCtrl* snapshot = LLViewerUICtrlFactory::getTexturePickerByName(this, "info_image");
+ if (snapshot)
+ {
+ snapshot->setImageAssetID(
+ mParcelValid ? mParcelSnapshot : LLUUID::null);
+ }
+
+
+
+ if (mParcelValid)
+ {
+ childSetText("info_parcel", mParcelLocation);
+
+
+ childSetTextArg("meters_supports_object", "[AMOUNT]", llformat("%d", mParcelActualArea));
+ childSetTextArg("meters_supports_object", "[AMOUNT2]", llformat("%d", mParcelSupportedObjects));
+
+ childSetText("info_size", childGetText("meters_supports_object"));
+
+
+ childSetText("info_price",
+ llformat(
+ "L$ %d%s",
+ mParcelPrice,
+ mParcelSoldWithObjects
+ ? "\nsold with objects"
+ : ""));
+ childSetVisible("info_price", mParcelIsForSale);
+ }
+ else
+ {
+ childSetText("info_parcel", "(no parcel selected)");
+ childSetText("info_size", "");
+ childSetText("info_price", "");
+ }
+
+ childSetText("info_action",
+ mCanBuy
+ ?
+ mIsForGroup
+ ? childGetText("buying_for_group")//"Buying land for group:"
+ : childGetText("buying_will")//"Buying this land will:"
+ :
+ mCannotBuyIsError
+ ? childGetText("cannot_buy_now")//"Cannot buy now:"
+ : childGetText("not_for_sale")//"Not for sale:"
+
+ );
+ }
+
+ bool showingError = !mCanBuy || !mSiteValid;
+
+ // error section
+ if (showingError)
+ {
+ mChildren.setBadge("step_error",
+ mCannotBuyIsError
+ ? LLViewChildren::BADGE_ERROR
+ : LLViewChildren::BADGE_WARN);
+
+ LLTextBox* message = LLUICtrlFactory::getTextBoxByName(this, "error_message");
+ if (message)
+ {
+ message->setVisible(true);
+ message->setWrappedText(
+ !mCanBuy ? mCannotBuyReason : "(waiting for data)"
+ );
+ }
+
+ childSetVisible("error_web",
+ mCannotBuyIsError && !mCannotBuyURI.empty());
+ }
+ else
+ {
+ childHide("step_error");
+ childHide("error_message");
+ childHide("error_web");
+ }
+
+
+ // section one: account
+ if (!showingError)
+ {
+ mChildren.setBadge("step_1",
+ mSiteMembershipUpgrade
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+ childSetText("account_action", mSiteMembershipAction);
+ childSetText("account_reason",
+ mSiteMembershipUpgrade
+ ? childGetText("must_upgrade")
+ : childGetText("cant_own_land")
+ );
+
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ levels->setVisible(mSiteMembershipUpgrade);
+
+ levels->removeall();
+ for(std::vector<std::string>::const_iterator i
+ = mSiteMembershipPlanNames.begin();
+ i != mSiteMembershipPlanNames.end();
+ ++i)
+ {
+ levels->add(*i);
+ }
+
+ levels->setCurrentByIndex(mUserPlanChoice);
+ }
+
+ childShow("step_1");
+ childShow("account_action");
+ childShow("account_reason");
+ }
+ else
+ {
+ childHide("step_1");
+ childHide("account_action");
+ childHide("account_reason");
+ childHide("account_level");
+ }
+
+ // section two: land use fees
+ if (!showingError)
+ {
+ mChildren.setBadge("step_2",
+ mSiteLandUseUpgrade
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+ childSetText("land_use_action", mSiteLandUseAction);
+
+ std::string message;
+
+ if (mIsForGroup)
+ {
+ childSetTextArg("insufficient_land_credits", "[GROUP]", gAgent.mGroupName);
+
+
+ message += childGetText("insufficient_land_credits");
+
+ }
+ else if (mAgentHasNeverOwnedLand)
+ {
+ if (mParcelIsFirstLand)
+ {
+ message += childGetText("first_purchase");
+ }
+ else
+ {
+ message += childGetText("first_time_but_not_first_land");
+ }
+ }
+ else
+ {
+
+ childSetTextArg("land_holdings", "[BUYER]", llformat("%d", mAgentCommittedTier));
+ message += childGetText("land_holdings");
+
+ }
+
+ if (!mParcelValid)
+ {
+ message += "(no parcel selected)";
+ }
+ else if (mParcelBillableArea == mParcelActualArea)
+ {
+ childSetTextArg("parcel_meters", "[AMOUNT]", llformat("%d", mParcelActualArea));
+ message += childGetText("parcel_meters");
+ }
+ else
+ {
+
+ if (mParcelBillableArea > mParcelActualArea)
+ {
+ childSetTextArg("premium_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) );
+ message += childGetText("premium_land");
+ }
+ else
+ {
+ childSetTextArg("discounted_land", "[AMOUNT]", llformat("%d", mParcelBillableArea) );
+ message += childGetText("discounted_land");
+ }
+ }
+
+ childSetWrappedText("land_use_reason", message);
+
+ childShow("step_2");
+ childShow("land_use_action");
+ childShow("land_use_reason");
+ }
+ else
+ {
+ childHide("step_2");
+ childHide("land_use_action");
+ childHide("land_use_reason");
+ }
+
+ // section three: purchase & currency
+ S32 finalBalance = mAgentCashBalance + mCurrency.getAmount() - mParcelPrice;
+ bool willHaveEnough = finalBalance >= 0;
+ bool haveEnough = mAgentCashBalance >= mParcelPrice;
+ S32 minContribution = llceil((F32)mParcelBillableArea / GROUP_LAND_BONUS_FACTOR);
+ bool groupContributionEnough = mParcelGroupContribution >= minContribution;
+
+ mCurrency.updateUI(!showingError && !haveEnough);
+
+ if (!showingError)
+ {
+ mChildren.setBadge("step_3",
+ !willHaveEnough
+ ? LLViewChildren::BADGE_WARN
+ : mCurrency.getAmount() > 0
+ ? LLViewChildren::BADGE_NOTE
+ : LLViewChildren::BADGE_OK);
+
+ childSetText("purchase_action",
+ llformat(
+ "Pay L$ %d to %s for this land",
+ mParcelPrice,
+ mParcelSellerName.c_str()
+ ));
+ childSetVisible("purchase_action", mParcelValid);
+
+ std::string reasonString;
+
+ if (haveEnough)
+ {
+
+ childSetTextArg("have_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance));
+ childSetText("currency_reason", childGetText("have_enough_lindens"));
+ }
+ else
+ {
+ childSetTextArg("not_enough_lindens", "[AMOUNT]", llformat("%d", mAgentCashBalance));
+ childSetTextArg("not_enough_lindens", "[AMOUNT2]", llformat("%d", mParcelPrice - mAgentCashBalance));
+
+ childSetText("currency_reason", childGetText("not_enough_lindens"));
+
+ childSetTextArg("currency_est", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0));
+
+ }
+
+ if (willHaveEnough)
+ {
+
+ childSetTextArg("balance_left", "[AMOUNT]", llformat("%d", finalBalance));
+
+ childSetText("currency_balance", childGetText("balance_left"));
+
+ }
+ else
+ {
+
+ childSetTextArg("balance_needed", "[AMOUNT]", llformat("%d", mParcelPrice - mAgentCashBalance));
+
+ childSetText("currency_balance", childGetText("balance_needed"));
+
+ }
+
+ //remove_contribution not in XML - ?!
+ childSetValue("remove_contribution", LLSD(groupContributionEnough));
+ childSetEnabled("remove_contribution", groupContributionEnough);
+ bool showRemoveContribution = mParcelIsGroupLand
+ && (mParcelGroupContribution > 0);
+ childSetText("remove_contribution",
+ llformat("Remove %d square meters of contribution from group",
+ minContribution));
+ childSetVisible("remove_contribution", showRemoveContribution);
+
+ childShow("step_3");
+ childShow("purchase_action");
+ childShow("currency_reason");
+ childShow("currency_balance");
+ }
+ else
+ {
+ childHide("step_3");
+ childHide("purchase_action");
+ childHide("currency_reason");
+ childHide("currency_balance");
+ childHide("remove_group_donation");
+ }
+
+
+ bool agrees_to_covenant = false;
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)getChildByName("agree_covenant");
+ if (check)
+ {
+ agrees_to_covenant = check->get();
+ }
+
+ childSetEnabled("buy_btn",
+ mCanBuy && mSiteValid && willHaveEnough && !mTransaction && agrees_to_covenant);
+}
+
+void LLFloaterBuyLandUI::startBuyPreConfirm()
+{
+ std::string action;
+
+ if (mSiteMembershipUpgrade)
+ {
+ action += mSiteMembershipAction;
+ action += "\n";
+
+ LLComboBox* levels = LLUICtrlFactory::getComboBoxByName(this, "account_level");
+ if (levels)
+ {
+ action += " * ";
+ action += mSiteMembershipPlanNames[levels->getCurrentIndex()];
+ action += "\n";
+ }
+ }
+ if (mSiteLandUseUpgrade)
+ {
+ action += mSiteLandUseAction;
+ action += "\n";
+ }
+ if (mCurrency.getAmount() > 0)
+ {
+
+ childSetTextArg("buy_for_US", "[AMOUNT]", llformat("%d", mCurrency.getAmount()));
+ childSetTextArg("buy_for_US", "[AMOUNT2]", llformat("%#.2f", mCurrency.getEstimate() / 100.0));
+
+ action += childGetText("buy_for_US");
+ }
+
+ childSetTextArg("pay_to_for_land", "[AMOUNT]", llformat("%d", mParcelPrice));
+ childSetTextArg("pay_to_for_land", "[SELLER]", mParcelSellerName.c_str());
+
+ action += childGetText("pay_to_for_land");
+
+
+ LLConfirmationManager::confirm(mSiteConfirm,
+ action,
+ *this,
+ &LLFloaterBuyLandUI::startBuyPostConfirm);
+}
+
+void LLFloaterBuyLandUI::startBuyPostConfirm(const std::string& password)
+{
+ runWebSitePrep(password);
+
+ mCanBuy = false;
+ mCannotBuyReason = childGetText("processing");
+ refreshUI();
+}
+
+
+// static
+void LLFloaterBuyLandUI::onClickBuy(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ self->startBuyPreConfirm();
+}
+
+// static
+void LLFloaterBuyLandUI::onClickCancel(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ self->close();
+}
+
+// static
+void LLFloaterBuyLandUI::onClickErrorWeb(void* data)
+{
+ LLFloaterBuyLandUI* self = (LLFloaterBuyLandUI*)data;
+ LLWeb::loadURLExternal(self->mCannotBuyURI);
+ self->close();
+}
+
diff --git a/indra/newview/llfloaterbuyland.h b/indra/newview/llfloaterbuyland.h
new file mode 100644
index 0000000000..092c95802a
--- /dev/null
+++ b/indra/newview/llfloaterbuyland.h
@@ -0,0 +1,28 @@
+/**
+ * @file llfloaterbuyland.h
+ * @brief LLFloaterBuyLand class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERBUYLAND_H
+#define LL_LLFLOATERBUYLAND_H
+
+class LLParcel;
+class LLViewerRegion;
+class LLViewerTextEditor;
+
+class LLFloaterBuyLand
+{
+public:
+ static void buyLand(LLViewerRegion* region,
+ LLParcel* parcel,
+ bool is_for_group);
+ static void updateCovenantText(const std::string& string, const LLUUID& asset_id);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+};
+
+#endif
diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp
new file mode 100644
index 0000000000..3d8912d001
--- /dev/null
+++ b/indra/newview/llfloaterchat.cpp
@@ -0,0 +1,420 @@
+/**
+ * @file llfloaterchat.cpp
+ * @brief LLFloaterChat class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Actually the "Chat History" floater.
+ * Should be llfloaterchathistory, not llfloaterchat.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterchat.h"
+#include "llfloaterscriptdebug.h"
+
+#include "llchat.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+// project include
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llconsole.h"
+#include "llfloatermute.h"
+#include "llkeyboard.h"
+//#include "lllineeditor.h"
+#include "llmutelist.h"
+//#include "llresizehandle.h"
+#include "llstatusbar.h"
+#include "llviewertexteditor.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermessage.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llchatbar.h"
+#include "lllogchat.h"
+#include "lltexteditor.h"
+#include "llfloaterhtml.h"
+#include "llweb.h"
+
+//
+// Constants
+//
+const char FLOATER_TITLE[] = "Chat History";
+const F32 INSTANT_MSG_SIZE = 8.0f;
+const F32 CHAT_MSG_SIZE = 8.0f;
+const LLColor4 INSTANT_MSG_COLOR(1, 1, 1, 1);
+const LLColor4 MUTED_MSG_COLOR(0.5f, 0.5f, 0.5f, 1.f);
+const S32 MAX_CHATTER_COUNT = 16;
+
+//
+// Global statics
+//
+LLFloaterChat* gFloaterChat = NULL;
+
+LLColor4 get_text_color(const LLChat& chat);
+
+//
+// Member Functions
+//
+LLFloaterChat::LLFloaterChat()
+: LLFloater("chat floater", "FloaterChatRect", FLOATER_TITLE,
+ RESIZE_YES, 440, 100, DRAG_ON_TOP, MINIMIZE_NO, CLOSE_YES)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_chat_history.xml");
+
+ childSetAction("Mute resident",onClickMute,this);
+ childSetAction("Chat", onClickChat, this);
+ childSetCommitCallback("chatter combobox",onCommitUserSelect,this);
+ childSetCommitCallback("show mutes",onClickToggleShowMute,this); //show mutes
+ childSetVisible("Chat History Editor with mute",FALSE);
+ setDefaultBtn("Chat");
+}
+
+LLFloaterChat::~LLFloaterChat()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLFloaterChat::setVisible(BOOL visible)
+{
+ LLFloater::setVisible( visible );
+
+ gSavedSettings.setBOOL("ShowChatHistory", visible);
+
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( !visible );
+}
+
+
+// public virtual
+void LLFloaterChat::onClose(bool app_quitting)
+{
+ LLFloater::setVisible( FALSE );
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowChatHistory", FALSE);
+ }
+
+ // Hide the chat overlay when our history is visible.
+ gConsole->setVisible( TRUE );
+}
+
+
+// public
+void LLFloaterChat::show()
+{
+ open();
+}
+
+void add_timestamped_line(LLViewerTextEditor* edit, const LLString& line, const LLColor4& color)
+{
+ bool prepend_newline = true;
+ if (gSavedSettings.getBOOL("ChatShowTimestamps"))
+ {
+ edit->appendTime(prepend_newline);
+ prepend_newline = false;
+ }
+ edit->appendColoredText(line, false, prepend_newline, color);
+}
+
+// static
+void LLFloaterChat::addChatHistory(const LLChat& chat, bool log_to_file)
+{
+ if ( gSavedPerAccountSettings.getBOOL("LogChat") && log_to_file)
+ {
+ LLLogChat::saveHistory("chat",chat.mText);
+ }
+
+ LLColor4 color = get_text_color(chat);
+
+ if (!log_to_file) color = LLColor4::grey; //Recap from log file.
+
+ if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
+ {
+ LLFloaterScriptDebug::addScriptLine(chat.mText,
+ chat.mFromName,
+ color,
+ chat.mFromID);
+ if (!gSavedSettings.getBOOL("ScriptErrorsAsChat"))
+ {
+ return;
+ }
+ }
+
+ // could flash the chat button in the status bar here. JC
+ if (!gFloaterChat) return;
+
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+
+ HistoryEditor->setParseHTML(TRUE);
+ HistoryEditorWithMute->setParseHTML(TRUE);
+
+ if (!chat.mMuted)
+ {
+ add_timestamped_line(HistoryEditor, chat.mText, color);
+ add_timestamped_line(HistoryEditorWithMute, chat.mText, color);
+ }
+ else
+ {
+ // desaturate muted chat
+ LLColor4 muted_color = lerp(color, LLColor4::grey, 0.5f);
+ add_timestamped_line(HistoryEditorWithMute, chat.mText, color);
+ }
+
+ if (!chat.mMuted
+ && chat.mSourceType != CHAT_SOURCE_SYSTEM
+ && chat.mFromID.notNull()
+ && chat.mFromID != gAgent.getID())
+ {
+
+ LLComboBox* ChatterCombo = LLUICtrlFactory::getComboBoxByName(gFloaterChat,"chatter combobox");
+
+ if(!ChatterCombo)
+ return;
+
+ if (!ChatterCombo->setCurrentByID(chat.mFromID))
+ {
+ // if we have too many items...
+ if (ChatterCombo->getItemCount() >= MAX_CHATTER_COUNT)
+ {
+ ChatterCombo->remove(0);
+ }
+
+ LLMute mute(chat.mFromID, chat.mFromName);
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT)
+ {
+ mute.mType = LLMute::OBJECT;
+ }
+ else if (chat.mSourceType == CHAT_SOURCE_AGENT)
+ {
+ mute.mType = LLMute::AGENT;
+ }
+ LLString item = mute.getDisplayName();
+ ChatterCombo->add(item, chat.mFromID);
+ ChatterCombo->setCurrentByIndex(ChatterCombo->getItemCount() - 1);
+ gFloaterChat->childSetEnabled("Mute resident",TRUE);
+ }
+ }
+}
+
+// static
+void LLFloaterChat::setHistoryCursorAndScrollToEnd()
+{
+ if (gFloaterChat)
+ {
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)gFloaterChat->getChildByName("Chat History Editor with mute");
+
+ HistoryEditor->setCursorAndScrollToEnd();
+ HistoryEditorWithMute->setCursorAndScrollToEnd();
+ }
+}
+
+
+// static
+void LLFloaterChat::toggle(void*)
+{
+ if (gFloaterChat->getVisible())
+ {
+ gFloaterChat->close();
+ }
+ else
+ {
+ gFloaterChat->show();
+ }
+}
+
+// static
+BOOL LLFloaterChat::visible(void*)
+{
+ return (gFloaterChat && gFloaterChat->getVisible());
+}
+
+//static
+void LLFloaterChat::onClickMute(void *data)
+{
+ LLFloaterChat* self = (LLFloaterChat*)data;
+
+ LLComboBox* ChatterCombo = LLUICtrlFactory::getComboBoxByName(self,"chatter combobox");
+
+ const LLString& name = ChatterCombo->getSimple();
+ LLUUID id = ChatterCombo->getCurrentID();
+
+ if (name.empty()) return;
+
+ LLMute mute(id);
+ mute.setFromDisplayName(name);
+ gMuteListp->add(mute);
+
+ if (gFloaterMute)
+ {
+ gFloaterMute->show();
+ }
+}
+
+//static
+void LLFloaterChat::onClickChat(void*)
+{
+ // we need this function as a level of indirection because otherwise startChat would
+ // cast the data pointer to a character string, and dump garbage in the chat
+ LLChatBar::startChat(NULL);
+}
+
+//static
+void LLFloaterChat::onCommitUserSelect(LLUICtrl* caller, void* data)
+{
+ LLFloaterChat* floater = (LLFloaterChat*)data;
+ LLComboBox* combo = (LLComboBox*)caller;
+
+ if (combo->getCurrentIndex() == -1)
+ {
+ floater->childSetEnabled("Mute resident",FALSE);
+ }
+ else
+ {
+ floater->childSetEnabled("Mute resident",TRUE);
+ }
+}
+
+//static
+void LLFloaterChat::onClickToggleShowMute(LLUICtrl* caller, void *data)
+{
+ LLFloaterChat* floater = (LLFloaterChat*)data;
+
+
+ //LLCheckBoxCtrl*
+ BOOL show_mute = LLUICtrlFactory::getCheckBoxByName(floater,"show mutes")->get();
+ LLViewerTextEditor* HistoryEditor = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor");
+ LLViewerTextEditor* HistoryEditorWithMute = (LLViewerTextEditor*)floater->getChildByName("Chat History Editor with mute");
+
+ if (!HistoryEditor || !HistoryEditorWithMute)
+ return;
+
+ //BOOL show_mute = floater->mShowMuteCheckBox->get();
+ if (show_mute)
+ {
+ HistoryEditor->setVisible(FALSE);
+ HistoryEditorWithMute->setVisible(TRUE);
+ HistoryEditorWithMute->setCursorAndScrollToEnd();
+ }
+ else
+ {
+ HistoryEditor->setVisible(TRUE);
+ HistoryEditorWithMute->setVisible(FALSE);
+ HistoryEditor->setCursorAndScrollToEnd();
+ }
+}
+
+// Put a line of chat in all the right places
+void LLFloaterChat::addChat(const LLChat& chat,
+ BOOL from_instant_message,
+ BOOL local_agent)
+{
+ LLColor4 text_color = get_text_color(chat);
+
+ BOOL invisible_script_debug_chat =
+ chat.mChatType == CHAT_TYPE_DEBUG_MSG
+ && !gSavedSettings.getBOOL("ScriptErrorsAsChat");
+
+ if (!invisible_script_debug_chat
+ && !chat.mMuted
+ && gConsole
+ && !local_agent)
+ {
+ F32 size = CHAT_MSG_SIZE;
+ if(from_instant_message)
+ {
+ text_color = INSTANT_MSG_COLOR;
+ size = INSTANT_MSG_SIZE;
+ }
+ gConsole->addLine(chat.mText, size, text_color);
+ }
+
+ if( !from_instant_message || gSavedSettings.getBOOL("IMInChatHistory") )
+ {
+ addChatHistory(chat);
+ }
+}
+
+LLColor4 get_text_color(const LLChat& chat)
+{
+ LLColor4 text_color;
+
+ if(chat.mMuted)
+ {
+ text_color.setVec(0.8f, 0.8f, 0.8f, 1.f);
+ }
+ else
+ {
+ switch(chat.mSourceType)
+ {
+ case CHAT_SOURCE_SYSTEM:
+ text_color = gSavedSettings.getColor4("SystemChatColor");
+ break;
+ case CHAT_SOURCE_AGENT:
+ if (chat.mFromID.isNull())
+ {
+ text_color = gSavedSettings.getColor4("SystemChatColor");
+ }
+ else
+ {
+ text_color = gSavedSettings.getColor4("AgentChatColor");
+ }
+ break;
+ case CHAT_SOURCE_OBJECT:
+ if (chat.mChatType == CHAT_TYPE_DEBUG_MSG)
+ {
+ text_color = gSavedSettings.getColor4("ScriptErrorColor");
+ }
+ else
+ {
+ text_color = gSavedSettings.getColor4("ObjectChatColor");
+ }
+ break;
+ default:
+ text_color.setToWhite();
+ }
+
+ if (!chat.mPosAgent.isExactlyZero())
+ {
+ LLVector3 pos_agent = gAgent.getPositionAgent();
+ F32 distance = dist_vec(pos_agent, chat.mPosAgent);
+ if (distance > gAgent.getNearChatRadius())
+ {
+ // diminish far-off chat
+ text_color.mV[VALPHA] = 0.8f;
+ }
+ }
+ }
+
+ return text_color;
+}
+
+//static
+void LLFloaterChat::loadHistory()
+{
+ LLLogChat::loadHistory("chat", &chatFromLogFile, (void *)gFloaterChat);
+}
+
+//static
+void LLFloaterChat::chatFromLogFile(LLString line, void* userdata)
+{
+ LLChat chat;
+
+ chat.mText = line;
+ addChatHistory(chat, FALSE);
+}
diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h
new file mode 100644
index 0000000000..bf93afb455
--- /dev/null
+++ b/indra/newview/llfloaterchat.h
@@ -0,0 +1,61 @@
+/**
+ * @file llfloaterchat.h
+ * @brief LLFloaterChat class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Actually the "Chat History" floater.
+ * Should be llfloaterchathistory, not llfloaterchat.
+ */
+
+#ifndef LL_LLFLOATERCHAT_H
+#define LL_LLFLOATERCHAT_H
+
+#include "llfloater.h"
+
+class LLButton;
+class LLChat;
+class LLComboBox;
+class LLLineEditor;
+class LLViewerTextEditor;
+class LLMessageSystem;
+class LLUUID;
+class LLCheckBoxCtrl;
+
+class LLFloaterChat
+: public LLFloater
+{
+public:
+ LLFloaterChat();
+ ~LLFloaterChat();
+
+ void show();
+ virtual void onClose(bool app_quitting);
+ virtual void setVisible( BOOL b );
+
+ static void setHistoryCursorAndScrollToEnd();
+
+ // Add chat to console and history list.
+ // Color based on source, type, distance.
+ static void addChat(const LLChat& chat, BOOL from_im = FALSE, BOOL local_agent = FALSE);
+
+ // Add chat to history alone.
+ static void addChatHistory(const LLChat& chat, bool log_to_file = true);
+
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ static void onClickMute(void *data);
+ static void onClickChat(void *);
+ static void onCommitUserSelect(LLUICtrl* caller, void* data);
+ static void onClickToggleShowMute(LLUICtrl* caller, void *data);
+ static void chatFromLogFile(LLString line, void* userdata);
+ static void loadHistory();
+};
+
+extern LLFloaterChat* gFloaterChat;
+
+#endif
diff --git a/indra/newview/llfloatercolorpicker.cpp b/indra/newview/llfloatercolorpicker.cpp
new file mode 100644
index 0000000000..181a66c2ef
--- /dev/null
+++ b/indra/newview/llfloatercolorpicker.cpp
@@ -0,0 +1,1259 @@
+/**
+ * @file llfloatercolorpicker.cpp
+ * @brief Generic system color picker
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+#include <iomanip>
+
+#include "llfloatercolorpicker.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+#include "llui.h"
+#include "lllineeditor.h"
+#include "v4coloru.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llgl.h"
+#include "llmemory.h"
+#include "llimage.h"
+#include "llmousehandler.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llcheckboxctrl.h"
+#include "llworld.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llfocusmgr.h"
+#include "lltoolmgr.h"
+#include "lltoolpipette.h"
+#include "lldraghandle.h"
+
+const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
+const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Class LLFloaterColorPicker
+//
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// default ctor
+LLFloaterColorPicker::
+LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show_apply_immediate )
+ : LLFloater ("Color Picker Floater"),
+ mComponents ( 3 ),
+ mMouseDownInLumRegion ( FALSE ),
+ mMouseDownInHueRegion ( FALSE ),
+ mMouseDownInSwatch ( FALSE ),
+ mRGBViewerImageLeft ( 140 ),
+ mRGBViewerImageTop ( 356 ),
+ mRGBViewerImageWidth ( 256 ),
+ mRGBViewerImageHeight ( 256 ),
+ mLumRegionLeft ( mRGBViewerImageLeft + mRGBViewerImageWidth + 16 ),
+ mLumRegionTop ( mRGBViewerImageTop ),
+ mLumRegionWidth ( 16 ),
+ mLumRegionHeight ( mRGBViewerImageHeight ),
+ mLumMarkerSize ( 6 ),
+ mSwatchRegionLeft ( 12 ),
+ mSwatchRegionTop ( 160 + 16 - 4 ), // get help text baseline to line up with bottom of RGB viewer
+ mSwatchRegionWidth ( 110 ),
+ mSwatchRegionHeight ( 60 ),
+ mSwatchView ( NULL ),
+ numPaletteColumns ( 16 ),
+ numPaletteRows ( 2 ),
+ highlightEntry ( -1 ),
+ mPaletteRegionLeft ( mSwatchRegionLeft - 1 ),
+ mPaletteRegionTop ( 100 - 8 ),
+ mPaletteRegionWidth ( mLumRegionLeft + mLumRegionWidth - mSwatchRegionLeft + 2 ),
+ mPaletteRegionHeight ( 40 ),
+ mSwatch ( swatch ),
+ mActive ( TRUE ),
+ mCanApplyImmediately ( show_apply_immediate ),
+ mContextConeOpacity ( 0.f )
+{
+ // create user interface for this picker
+ createUI ();
+
+ if (!mCanApplyImmediately)
+ {
+ mApplyImmediateCheck->setEnabled(FALSE);
+ mApplyImmediateCheck->set(FALSE);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// dtor
+LLFloaterColorPicker::
+~LLFloaterColorPicker()
+{
+ // destroy the UI we created
+ destroyUI ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+createUI ()
+{
+ // build the majority of the gui using the factory builder
+ gUICtrlFactory->buildFloater ( this, "floater_color_picker.xml" );
+ setVisible ( FALSE );
+
+ // create RGB type area (not really RGB but it's got R,G & B in it.,..
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw ( mRGBViewerImageWidth, mRGBViewerImageHeight, mComponents );
+ U8* bits = raw->getData();
+ S32 linesize = mRGBViewerImageWidth * mComponents;
+ for ( S32 y = 0; y < mRGBViewerImageHeight; ++y )
+ {
+ for ( S32 x = 0; x < linesize; x += mComponents )
+ {
+ F32 rVal, gVal, bVal;
+
+ hslToRgb ( (F32)x / (F32) ( linesize - 1 ),
+ (F32)y / (F32) ( mRGBViewerImageHeight - 1 ),
+ 0.5f,
+ rVal,
+ gVal,
+ bVal );
+
+ * ( bits + x + y * linesize + 0 ) = ( U8 )( rVal * 255.0f );
+ * ( bits + x + y * linesize + 1 ) = ( U8 )( gVal * 255.0f );
+ * ( bits + x + y * linesize + 2 ) = ( U8 )( bVal * 255.0f );
+ }
+ }
+ mRGBImage = new LLImageGL ( (LLImageRaw*)raw, FALSE );
+ mRGBImage->bind();
+ mRGBImage->setClamp(TRUE, TRUE);
+
+ // create palette
+ for ( S32 each = 0; each < numPaletteColumns * numPaletteRows; ++each )
+ {
+ std::ostringstream codec;
+ codec << "ColorPaletteEntry" << std::setfill ( '0' ) << std::setw ( 2 ) << each + 1;
+
+ // argh!
+ const std::string s ( codec.str () );
+ mPalette.push_back ( new LLColor4 ( gSavedSettings.getColor4 ( s ) ) );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+showUI ()
+{
+ setVisible ( TRUE );
+ setFocus ( TRUE );
+ open();
+
+ // HACK: if system color picker is required - close the SL one we made and use default system dialog
+ if ( gSavedSettings.getBOOL ( "UseDefaultColorPicker" ) )
+ {
+ LLColorSwatchCtrl* swatch = getSwatch ();
+
+ setVisible ( FALSE );
+
+ // code that will get switched in for default system color picker
+ if ( swatch )
+ {
+ LLColor4 curCol = swatch->get ();
+ send_agent_pause();
+ gViewerWindow->getWindow ()->dialog_color_picker ( &curCol [ 0 ], &curCol [ 1 ], &curCol [ 2 ] );
+ send_agent_resume();
+
+ setOrigRgb ( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
+ setCurRgb( curCol [ 0 ], curCol [ 1 ], curCol [ 2 ] );
+
+ LLColorSwatchCtrl::onColorChanged ( swatch, LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+
+ close();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called after the dialog is rendered
+BOOL
+LLFloaterColorPicker::
+postBuild()
+{
+ mCancelBtn = LLViewerUICtrlFactory::getButtonByName( this, "cancel_btn" );
+ mCancelBtn->setClickedCallback ( onClickCancel );
+ mCancelBtn->setCallbackUserData ( this );
+
+ mSelectBtn = LLViewerUICtrlFactory::getButtonByName( this, "select_btn");
+ mSelectBtn->setClickedCallback ( onClickSelect );
+ mSelectBtn->setCallbackUserData ( this );
+ mSelectBtn->setFocus ( TRUE );
+
+ mPipetteBtn = LLViewerUICtrlFactory::getButtonByName ( this, "color_pipette" );
+
+ mPipetteBtn->setImages("eye_button_inactive.tga", "eye_button_active.tga");
+
+ mPipetteBtn->setClickedCallback( onClickPipette );
+ mPipetteBtn->setCallbackUserData ( this );
+
+ mApplyImmediateCheck = LLViewerUICtrlFactory::getCheckBoxByName( this, "apply_immediate");
+ mApplyImmediateCheck->set(gSavedSettings.getBOOL("ApplyColorImmediately"));
+ mApplyImmediateCheck->setCommitCallback(onImmediateCheck);
+ mApplyImmediateCheck->setCallbackUserData(this);
+
+ childSetCommitCallback("rspin", onTextCommit, (void*)this );
+ childSetCommitCallback("gspin", onTextCommit, (void*)this );
+ childSetCommitCallback("bspin", onTextCommit, (void*)this );
+ childSetCommitCallback("hspin", onTextCommit, (void*)this );
+ childSetCommitCallback("sspin", onTextCommit, (void*)this );
+ childSetCommitCallback("lspin", onTextCommit, (void*)this );
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+initUI ( F32 rValIn, F32 gValIn, F32 bValIn )
+{
+ // start catching lose-focus events from entry widgets
+ enableTextCallbacks ( TRUE );
+
+ // under some circumstances, we get rogue values that can be calmed by clamping...
+ rValIn = llclamp ( rValIn, 0.0f, 1.0f );
+ gValIn = llclamp ( gValIn, 0.0f, 1.0f );
+ bValIn = llclamp ( bValIn, 0.0f, 1.0f );
+
+ // store initial value in case cancel or revert is selected
+ setOrigRgb ( rValIn, gValIn, bValIn );
+
+ // starting point for current value to
+ setCurRgb ( rValIn, gValIn, bValIn );
+
+ // unpdate text entry fields
+ updateTextEntry ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+destroyUI ()
+{
+ // shut down pipette tool if active
+ stopUsingPipette();
+
+ // delete palette we created
+ std::vector < LLColor4* >::iterator iter = mPalette.begin ();
+ while ( iter != mPalette.end () )
+ {
+ delete ( *iter );
+ ++iter;
+ }
+
+ if ( mSwatchView )
+ {
+ this->removeChild ( mSwatchView );
+ delete mSwatchView;
+ mSwatchView = NULL;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+rgbToHsl ( F32 rValIn, F32 gValIn, F32 bValIn, F32& hValOut, F32& sValOut, F32& lValOut )
+{
+ F32 var_R = ( rValIn );
+ F32 var_G = ( gValIn );
+ F32 var_B = ( bValIn );
+
+ F32 var_Min = ( var_R < ( var_G < var_B ? var_G : var_B ) ? var_R : ( var_G < var_B ? var_G : var_B ) );
+ F32 var_Max = ( var_R > ( var_G > var_B ? var_G : var_B ) ? var_R : ( var_G > var_B ? var_G : var_B ) );
+
+ F32 del_Max = var_Max - var_Min;
+
+ F32 L = ( var_Max + var_Min ) / 2.0f;
+ F32 H = 0.0f;
+ F32 S = 0.0f;
+
+ if ( del_Max == 0.0f )
+ {
+ H = 0.0f;
+ S = 0.0f;
+ }
+ else
+ {
+ if ( L < 0.5 )
+ S = del_Max / ( var_Max + var_Min );
+ else
+ S = del_Max / ( 2.0f - var_Max - var_Min );
+
+ F32 del_R = ( ( ( var_Max - var_R ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+ F32 del_G = ( ( ( var_Max - var_G ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+ F32 del_B = ( ( ( var_Max - var_B ) / 6.0f ) + ( del_Max / 2.0f ) ) / del_Max;
+
+ if ( var_R >= var_Max )
+ H = del_B - del_G;
+ else
+ if ( var_G >= var_Max )
+ H = ( 1.0f / 3.0f ) + del_R - del_B;
+ else
+ if ( var_B >= var_Max )
+ H = ( 2.0f / 3.0f ) + del_G - del_R;
+
+ if ( H < 0.0f ) H += 1.0f;
+ if ( H > 1.0f ) H -= 1.0f;
+ }
+
+ // scale to meet calculation requirements
+ hValOut = H * 360.0f / 360.0f;
+ sValOut = S * 100.0f / 100.0f;
+ lValOut = L * 100.0f / 100.0f;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+F32
+LLFloaterColorPicker::
+hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn )
+{
+ if ( valHUeIn < 0.0f ) valHUeIn += 1.0f;
+ if ( valHUeIn > 1.0f ) valHUeIn -= 1.0f;
+ if ( ( 6.0f * valHUeIn ) < 1.0f ) return ( val1In + ( val2In - val1In ) * 6.0f * valHUeIn );
+ if ( ( 2.0f * valHUeIn ) < 1.0f ) return ( val2In );
+ if ( ( 3.0f * valHUeIn ) < 2.0f ) return ( val1In + ( val2In - val1In ) * ( ( 2.0f / 3.0f ) - valHUeIn ) * 6.0f );
+ return ( val1In );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+hslToRgb ( F32 hValIn, F32 sValIn, F32 lValIn, F32& rValOut, F32& gValOut, F32& bValOut )
+{
+ if ( sValIn < 0.00001f )
+ {
+ rValOut = lValIn;
+ gValOut = lValIn;
+ bValOut = lValIn;
+ }
+ else
+ {
+ F32 interVal1;
+ F32 interVal2;
+
+ if ( lValIn < 0.5f )
+ interVal2 = lValIn * ( 1.0f + sValIn );
+ else
+ interVal2 = ( lValIn + sValIn ) - ( sValIn * lValIn );
+
+ interVal1 = 2.0f * lValIn - interVal2;
+
+ rValOut = hueToRgb ( interVal1, interVal2, hValIn + ( 1.f / 3.f ) );
+ gValOut = hueToRgb ( interVal1, interVal2, hValIn );
+ bValOut = hueToRgb ( interVal1, interVal2, hValIn - ( 1.f / 3.f ) );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for original RGB value
+void
+LLFloaterColorPicker::
+setOrigRgb ( F32 origRIn, F32 origGIn, F32 origBIn )
+{
+ origR = origRIn;
+ origG = origGIn;
+ origB = origBIn;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for original RGB value
+void
+LLFloaterColorPicker::
+getOrigRgb ( F32& origROut, F32& origGOut, F32& origBOut )
+{
+ origROut = origR;
+ origGOut = origG;
+ origBOut = origB;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for current RGB value
+void
+LLFloaterColorPicker::
+setCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn )
+{
+ // save current RGB
+ curR = curRIn;
+ curG = curGIn;
+ curB = curBIn;
+
+ // update corresponding HSL values and
+ rgbToHsl ( curR, curG, curB, curH, curS, curL );
+
+ // color changed so update text fields (fixes SL-16968)
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ // CP: this was required when I first wrote the code but this may not be necessary anymore - leaving it there just in case
+ enableTextCallbacks( FALSE );
+ updateTextEntry();
+ enableTextCallbacks( TRUE );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for current RGB value
+void
+LLFloaterColorPicker::
+getCurRgb ( F32& curROut, F32& curGOut, F32& curBOut )
+{
+ curROut = curR;
+ curGOut = curG;
+ curBOut = curB;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// mutator for current HSL value
+void
+LLFloaterColorPicker::
+setCurHsl ( F32 curHIn, F32 curSIn, F32 curLIn )
+{
+ // save current HSL
+ curH = curHIn;
+ curS = curSIn;
+ curL = curLIn;
+
+ // update corresponding RGB values and
+ hslToRgb ( curH, curS, curL, curR, curG, curB );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// accessor for current HSL value
+void
+LLFloaterColorPicker::
+getCurHsl ( F32& curHOut, F32& curSOut, F32& curLOut )
+{
+ curHOut = curH;
+ curSOut = curS;
+ curLOut = curL;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'cancel' clicked
+void
+LLFloaterColorPicker::
+onClickCancel ( void* data )
+{
+ if (data)
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self )
+ {
+ self->cancelSelection ();
+ self->close();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'select' clicked
+void
+LLFloaterColorPicker::
+onClickSelect ( void* data )
+{
+ if (data)
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self )
+ {
+ // apply to selection
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_SELECT );
+ self->close();
+ }
+ }
+}
+
+void LLFloaterColorPicker::onClickPipette( void* data )
+{
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+
+ if ( self && gToolMgr)
+ {
+ BOOL pipette_active = self->mPipetteBtn->getToggleState();
+ pipette_active = !pipette_active;
+ if (pipette_active)
+ {
+ gToolPipette->setSelectCallback(onColorSelect, self);
+ gToolMgr->setTransientTool(gToolPipette);
+ }
+ else
+ {
+ gToolMgr->clearTransientTool();
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// called when 'text is committed' - i,e. focus moves from a text field
+void
+LLFloaterColorPicker::
+onTextCommit ( LLUICtrl* ctrl, void* data )
+{
+ if ( data )
+ {
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+ if ( self )
+ {
+ self->onTextEntryChanged ( ctrl );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onImmediateCheck( LLUICtrl* ctrl, void* data)
+{
+ LLFloaterColorPicker* self = ( LLFloaterColorPicker* )data;
+ if (self)
+ {
+ gSavedSettings.setBOOL("ApplyColorImmediately", self->mApplyImmediateCheck->get());
+
+ if (self->mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onColorSelect( const LLTextureEntry& te, void *data )
+{
+ LLFloaterColorPicker* self = (LLFloaterColorPicker*)data;
+ if (self)
+ {
+ self->setCurRgb(te.getColor().mV[VRED], te.getColor().mV[VGREEN], te.getColor().mV[VBLUE]);
+ if (self->mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( self->getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+}
+
+void LLFloaterColorPicker::onMouseCaptureLost(LLMouseHandler *old_captor)
+{
+ LLFloaterColorPicker* self = (LLFloaterColorPicker*)old_captor;
+ self->setMouseDownInHueRegion(FALSE);
+ self->setMouseDownInLumRegion(FALSE);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void LLFloaterColorPicker::draw()
+{
+ LLRect swatch_rect;
+ mSwatch->localRectToOtherView(mSwatch->getLocalRect(), &swatch_rect, this);
+ // draw context cone connecting color picker with color swatch in parent floater
+ LLRect local_rect = getLocalRect();
+ if (gFocusMgr.childHasKeyboardFocus(this) && mSwatch->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable(GL_CULL_FACE);
+ glBegin(GL_QUADS);
+ {
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mTop);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mBottom);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mTop);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mBottom);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(swatch_rect.mRight, swatch_rect.mBottom);
+ glVertex2i(swatch_rect.mLeft, swatch_rect.mBottom);
+ }
+ glEnd();
+ }
+
+ if (gFocusMgr.childHasMouseCapture(mDragHandle))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ mPipetteBtn->setEnabled(gToolMgr != NULL);
+ mPipetteBtn->setToggleState(gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette);
+ mApplyImmediateCheck->setEnabled(mActive && mCanApplyImmediately);
+ mSelectBtn->setEnabled(mActive);
+
+ // base floater stuff
+ LLFloater::draw ();
+
+ // draw image for RGB area (not really RGB but you'll see what I mean...
+ gl_draw_image ( mRGBViewerImageLeft, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBImage, LLColor4::white );
+
+ // update 'cursor' into RGB Section
+ S32 xPos = ( S32 ) ( ( F32 )mRGBViewerImageWidth * getCurH () ) - 8;
+ S32 yPos = ( S32 ) ( ( F32 )mRGBViewerImageHeight * getCurS () ) - 8;
+ gl_line_2d ( mRGBViewerImageLeft + xPos,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 8,
+ mRGBViewerImageLeft + xPos + 16,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 8,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ) );
+
+ gl_line_2d ( mRGBViewerImageLeft + xPos + 8,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos,
+ mRGBViewerImageLeft + xPos + 8,
+ mRGBViewerImageTop - mRGBViewerImageHeight + yPos + 16,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ) );
+
+ // create rgb area outline
+ gl_rect_2d ( mRGBViewerImageLeft,
+ mRGBViewerImageTop - mRGBViewerImageHeight,
+ mRGBViewerImageLeft + mRGBViewerImageWidth,
+ mRGBViewerImageTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // draw luminance slider
+ for ( S32 y = 0; y < mLumRegionHeight; ++y )
+ {
+ F32 rValSlider, gValSlider, bValSlider;
+ hslToRgb ( getCurH (), getCurS (), ( F32 )y / ( F32 )mLumRegionHeight, rValSlider, gValSlider, bValSlider );
+
+ gl_rect_2d( mLumRegionLeft,
+ mLumRegionTop - mLumRegionHeight + y,
+ mLumRegionLeft + mLumRegionWidth,
+ mLumRegionTop - mLumRegionHeight + y - 1,
+ LLColor4 ( rValSlider, gValSlider, bValSlider, 1.0f ) );
+ }
+
+
+ // draw luninance marker
+ S32 startX = mLumRegionLeft + mLumRegionWidth;
+ S32 startY = mLumRegionTop - mLumRegionHeight + ( S32 ) ( mLumRegionHeight * getCurL () );
+ gl_triangle_2d ( startX, startY,
+ startX + mLumMarkerSize, startY - mLumMarkerSize,
+ startX + mLumMarkerSize, startY + mLumMarkerSize,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), TRUE );
+
+ // draw luminance slider outline
+ gl_rect_2d ( mLumRegionLeft,
+ mLumRegionTop - mLumRegionHeight,
+ mLumRegionLeft + mLumRegionWidth,
+ mLumRegionTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // draw selected color swatch
+ gl_rect_2d ( mSwatchRegionLeft,
+ mSwatchRegionTop - mSwatchRegionHeight,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop,
+ LLColor4 ( getCurR (), getCurG (), getCurB (), 1.0f ),
+ TRUE );
+
+ // draw selected color swatch outline
+ gl_rect_2d ( mSwatchRegionLeft,
+ mSwatchRegionTop - mSwatchRegionHeight,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop,
+ LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ),
+ FALSE );
+
+ // color palette code is a little more involved so break it out into its' own method
+ drawPalette ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// find a complimentary color to the one passed in that can be used to highlight
+LLColor4&
+LLFloaterColorPicker::
+getComplimentaryColor ( LLColor4& backgroundColor )
+{
+ // going to base calculation on luminance
+ F32 hVal, sVal, lVal;
+ rgbToHsl ( backgroundColor [ 0 ], backgroundColor [ 1 ], backgroundColor [ 2 ], hVal, sVal, lVal );
+
+ // fairly simple heuristic for now...!
+ if ( lVal < 0.5f )
+ {
+ return LLColor4::white;
+ }
+
+ return LLColor4::black;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// draw color palette
+void
+LLFloaterColorPicker::
+drawPalette ()
+{
+ S32 curEntry = 0;
+
+ for ( S32 y = 0; y < numPaletteRows; ++y )
+ {
+ for ( S32 x = 0; x < numPaletteColumns; ++x )
+ {
+ // calculate position
+ S32 x1 = mPaletteRegionLeft + ( mPaletteRegionWidth * x ) / numPaletteColumns;
+ S32 y1 = mPaletteRegionTop - ( mPaletteRegionHeight * y ) / numPaletteRows;
+ S32 x2 = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( x + 1 ) ) / numPaletteColumns );
+ S32 y2 = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( y + 1 ) ) / numPaletteRows );
+
+ // draw palette entry color
+ if ( mPalette [ curEntry ] )
+ {
+ gl_rect_2d ( x1 + 2, y1 - 2, x2 - 2, y2 + 2, *mPalette [ curEntry++ ], TRUE );
+ gl_rect_2d ( x1 + 1, y1 - 1, x2 - 1, y2 + 1, LLColor4 ( 0.0f, 0.0f, 0.0f, 1.0f ), FALSE );
+ }
+ }
+ }
+
+ // if there is something to highlight (mouse down in swatch & hovering over palette)
+ if ( highlightEntry >= 0 )
+ {
+ // extract row/column from palette index
+ S32 entryColumn = highlightEntry % numPaletteColumns;
+ S32 entryRow = highlightEntry / numPaletteColumns;
+
+ // calculate position of this entry
+ S32 x1 = mPaletteRegionLeft + ( mPaletteRegionWidth * entryColumn ) / numPaletteColumns;
+ S32 y1 = mPaletteRegionTop - ( mPaletteRegionHeight * entryRow ) / numPaletteRows;
+ S32 x2 = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( entryColumn + 1 ) ) / numPaletteColumns );
+ S32 y2 = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( entryRow + 1 ) ) / numPaletteRows );
+
+ // center position of entry
+ S32 xCenter = x1 + ( x2 - x1 ) / 2;
+ S32 yCenter = y1 - ( y1 - y2 ) / 2;
+
+ // find a color that works well as a highlight color
+ LLColor4 hlColor ( getComplimentaryColor ( *mPalette [ highlightEntry ] ) );
+
+ // mark a cross for entry that is being hovered
+ gl_line_2d ( xCenter - 4, yCenter - 4, xCenter + 4, yCenter + 4, hlColor );
+ gl_line_2d ( xCenter + 4, yCenter - 4, xCenter - 4, yCenter + 4, hlColor );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// update text entry values for RGB/HSL (can't be done in ::draw () since this overwrites input
+void
+LLFloaterColorPicker::
+updateTextEntry ()
+{
+ // set values in spinners
+ childSetValue("rspin", ( getCurR () * 255.0f ) );
+ childSetValue("gspin", ( getCurG () * 255.0f ) );
+ childSetValue("bspin", ( getCurB () * 255.0f ) );
+ childSetValue("hspin", ( getCurH () * 360.0f ) );
+ childSetValue("sspin", ( getCurS () * 100.0f ) );
+ childSetValue("lspin", ( getCurL () * 100.0f ) );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// turns on or off text entry commit call backs
+void
+LLFloaterColorPicker::
+enableTextCallbacks ( BOOL stateIn )
+{
+ if ( stateIn )
+ {
+ childSetCommitCallback("rspin", onTextCommit, (void*)this );
+ childSetCommitCallback("gspin", onTextCommit, (void*)this );
+ childSetCommitCallback("bspin", onTextCommit, (void*)this );
+ childSetCommitCallback("hspin", onTextCommit, (void*)this );
+ childSetCommitCallback("sspin", onTextCommit, (void*)this );
+ childSetCommitCallback("lspin", onTextCommit, (void*)this );
+ }
+ else
+ {
+ childSetCommitCallback("rspin", 0, (void*)this );
+ childSetCommitCallback("gspin", 0, (void*)this );
+ childSetCommitCallback("bspin", 0, (void*)this );
+ childSetCommitCallback("hspin", 0, (void*)this );
+ childSetCommitCallback("sspin", 0, (void*)this );
+ childSetCommitCallback("lspin", 0, (void*)this );
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+void
+LLFloaterColorPicker::
+onTextEntryChanged ( LLUICtrl* ctrl )
+{
+ // value in RGB boxes changed
+ LLString name = ctrl->getName();
+ if ( ( name == "rspin" ) || ( name == "gspin" ) || ( name == "bspin" ) )
+ {
+ // get current RGB
+ F32 rVal, gVal, bVal;
+ getCurRgb ( rVal, gVal, bVal );
+
+ // update component value with new value from text
+ if ( name == "rspin" )
+ {
+ rVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+ else
+ if ( name == "gspin" )
+ {
+ gVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+ else
+ if ( name == "bspin" )
+ {
+ bVal = (F32)ctrl->getValue().asReal() / 255.0f;
+ }
+
+ // update current RGB (and implicitly HSL)
+ setCurRgb ( rVal, gVal, bVal );
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+ else
+ // value in HSL boxes changed
+ if ( ( name == "hspin" ) || ( name == "sspin" ) || ( name == "lspin" ) )
+ {
+ // get current HSL
+ F32 hVal, sVal, lVal;
+ getCurHsl ( hVal, sVal, lVal );
+
+ // update component value with new value from text
+ if ( name == "hspin" )
+ hVal = (F32)ctrl->getValue().asReal() / 360.0f;
+ else
+ if ( name == "sspin" )
+ sVal = (F32)ctrl->getValue().asReal() / 100.0f;
+ else
+ if ( name == "lspin" )
+ lVal = (F32)ctrl->getValue().asReal() / 100.0f;
+
+ // update current HSL (and implicitly RGB)
+ setCurHsl ( hVal, sVal, lVal );
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+updateRgbHslFromPoint ( S32 xPosIn, S32 yPosIn )
+{
+ if ( xPosIn >= mRGBViewerImageLeft &&
+ xPosIn <= mRGBViewerImageLeft + mRGBViewerImageWidth &&
+ yPosIn <= mRGBViewerImageTop &&
+ yPosIn >= mRGBViewerImageTop - mRGBViewerImageHeight )
+ {
+ // update HSL (and therefore RGB) based on new H & S and current L
+ setCurHsl ( ( ( F32 )xPosIn - ( F32 )mRGBViewerImageLeft ) / ( F32 )mRGBViewerImageWidth,
+ ( ( F32 )yPosIn - ( ( F32 )mRGBViewerImageTop - ( F32 )mRGBViewerImageHeight ) ) / ( F32 )mRGBViewerImageHeight,
+ getCurL () );
+
+ // indicate a value changed
+ return TRUE;
+ }
+ else
+ if ( xPosIn >= mLumRegionLeft &&
+ xPosIn <= mLumRegionLeft + mLumRegionWidth &&
+ yPosIn <= mLumRegionTop &&
+ yPosIn >= mLumRegionTop - mLumRegionHeight )
+ {
+
+ // update HSL (and therefore RGB) based on current HS and new L
+ setCurHsl ( getCurH (),
+ getCurS (),
+ ( ( F32 )yPosIn - ( ( F32 )mRGBViewerImageTop - ( F32 )mRGBViewerImageHeight ) ) / ( F32 )mRGBViewerImageHeight );
+
+ // indicate a value changed
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+handleMouseDown ( S32 x, S32 y, MASK mask )
+{
+ // if this window is in the foreground
+ if ( mForeground )
+ {
+ // make it the frontmost
+ gFloaterView->bringToFront(this);
+
+ // rect containing RGB area
+ LLRect rgbAreaRect ( mRGBViewerImageLeft,
+ mRGBViewerImageTop,
+ mRGBViewerImageLeft + mRGBViewerImageWidth,
+ mRGBViewerImageTop - mRGBViewerImageHeight );
+
+ if ( rgbAreaRect.pointInRect ( x, y ) )
+ {
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ // mouse button down
+ setMouseDownInHueRegion ( TRUE );
+
+ // update all values based on initial click
+ updateRgbHslFromPoint ( x, y );
+
+ // required by base class
+ return TRUE;
+ }
+
+ // rect containing RGB area
+ LLRect lumAreaRect ( mLumRegionLeft,
+ mLumRegionTop,
+ mLumRegionLeft + mLumRegionWidth + mLumMarkerSize,
+ mLumRegionTop - mLumRegionHeight );
+
+ if ( lumAreaRect.pointInRect ( x, y ) )
+ {
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ // mouse button down
+ setMouseDownInLumRegion ( TRUE );
+
+ // required by base class
+ return TRUE;
+ }
+
+ // rect containing swatch area
+ LLRect swatchRect ( mSwatchRegionLeft,
+ mSwatchRegionTop,
+ mSwatchRegionLeft + mSwatchRegionWidth,
+ mSwatchRegionTop - mSwatchRegionHeight );
+
+ setMouseDownInSwatch( FALSE );
+ if ( swatchRect.pointInRect ( x, y ) )
+ {
+ setMouseDownInSwatch( TRUE );
+
+ // required - dont drag windows here.
+ return TRUE;
+ }
+
+ // rect containing palette area
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ // release keyboard focus so we can change text values
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ mSelectBtn->setFocus(TRUE);
+ }
+
+ // calculate which palette index we selected
+ S32 c = ( ( x - mPaletteRegionLeft ) * numPaletteColumns ) / mPaletteRegionWidth;
+ S32 r = ( ( y - ( mPaletteRegionTop - mPaletteRegionHeight ) ) * numPaletteRows ) / mPaletteRegionHeight;
+
+ U32 index = ( numPaletteRows - r - 1 ) * numPaletteColumns + c;
+
+ if ( index <= mPalette.size () )
+ {
+ LLColor4 selected = *mPalette [ index ];
+
+ setCurRgb ( selected [ 0 ], selected [ 1 ], selected [ 2 ] );
+
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+
+ // HACK: turn off the call back wilst we update the text or we recurse ourselves into oblivion
+ enableTextCallbacks ( FALSE );
+ updateTextEntry ();
+ enableTextCallbacks ( TRUE );
+ }
+
+ return TRUE;
+ }
+ }
+ // dispatch to base class for the rest of things
+ return LLFloater::handleMouseDown ( x, y, mask );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+BOOL
+LLFloaterColorPicker::
+handleHover ( S32 x, S32 y, MASK mask )
+{
+ // if we're the front most window
+ if ( isFrontmost () )
+ {
+ // mouse was pressed within region
+ if ( getMouseDownInHueRegion() || getMouseDownInLumRegion())
+ {
+ S32 clamped_x, clamped_y;
+ if (getMouseDownInHueRegion())
+ {
+ clamped_x = llclamp(x, mRGBViewerImageLeft, mRGBViewerImageLeft + mRGBViewerImageWidth);
+ clamped_y = llclamp(y, mRGBViewerImageTop - mRGBViewerImageHeight, mRGBViewerImageTop);
+ }
+ else
+ {
+ clamped_x = llclamp(x, mLumRegionLeft, mLumRegionLeft + mLumRegionWidth);
+ clamped_y = llclamp(y, mLumRegionTop - mLumRegionHeight, mLumRegionTop);
+ }
+
+ // update the stored RGB/HSL values using the mouse position - returns TRUE if RGB was updated
+ if ( updateRgbHslFromPoint ( clamped_x, clamped_y ) )
+ {
+ // update text entry fields
+ updateTextEntry ();
+
+ // RN: apparently changing color when dragging generates too much traffic and results in sporadic updates
+ //// commit changed color to swatch subject
+ //// REVIEW: this gets sent each time a color changes - is this okay ?
+ //if (mApplyImmediateCheck->get())
+ //{
+ // LLColorSwatchCtrl::onColorChanged ( getSwatch () );
+ //}
+ }
+ }
+
+ highlightEntry = -1;
+
+ if ( mMouseDownInSwatch )
+ {
+ getWindow()->setCursor ( UI_CURSOR_ARROWDRAG );
+
+ // if cursor if over a palette entry
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ // find row/column in palette
+ S32 xOffset = ( ( x - mPaletteRegionLeft ) * numPaletteColumns ) / mPaletteRegionWidth;
+ S32 yOffset = ( ( mPaletteRegionTop - y - 1 ) * numPaletteRows ) / mPaletteRegionHeight;
+
+ // calculate the entry 0..n-1 to highlight and set variable to next draw() picks it up
+ highlightEntry = xOffset + yOffset * numPaletteColumns;
+ }
+
+ return TRUE;
+ }
+ }
+
+ // dispatch to base class for the rest of things
+ return LLFloater::handleHover ( x, y, mask );
+}
+
+void LLFloaterColorPicker::onClose(bool app_quitting)
+{
+ //RN: this is consistent with texture picker in that closing the window leaves the current selection
+ // to change this to "close to cancel", uncomment the following line
+ //cancelSelection();
+ LLFloater::onClose(app_quitting);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// reverts state once mouse button is released
+BOOL
+LLFloaterColorPicker::
+handleMouseUp ( S32 x, S32 y, MASK mask )
+{
+ getWindow()->setCursor ( UI_CURSOR_ARROW );
+
+ if (getMouseDownInHueRegion() || getMouseDownInLumRegion())
+ {
+ if (mApplyImmediateCheck->get())
+ {
+ LLColorSwatchCtrl::onColorChanged ( getSwatch (), LLColorSwatchCtrl::COLOR_CHANGE );
+ }
+ }
+
+ // rect containing palette area
+ LLRect paletteRect ( mPaletteRegionLeft,
+ mPaletteRegionTop,
+ mPaletteRegionLeft + mPaletteRegionWidth,
+ mPaletteRegionTop - mPaletteRegionHeight );
+
+ if ( paletteRect.pointInRect ( x, y ) )
+ {
+ if ( mMouseDownInSwatch )
+ {
+ S32 curEntry = 0;
+ for ( S32 row = 0; row < numPaletteRows; ++row )
+ {
+ for ( S32 column = 0; column < numPaletteColumns; ++column )
+ {
+ S32 left = mPaletteRegionLeft + ( mPaletteRegionWidth * column ) / numPaletteColumns;
+ S32 top = mPaletteRegionTop - ( mPaletteRegionHeight * row ) / numPaletteRows;
+ S32 right = ( mPaletteRegionLeft + ( mPaletteRegionWidth * ( column + 1 ) ) / numPaletteColumns );
+ S32 bottom = ( mPaletteRegionTop - ( mPaletteRegionHeight * ( row + 1 ) ) / numPaletteRows );
+
+ // rect is flipped vertically when testing here
+ LLRect dropRect ( left, top, right, bottom );
+
+ if ( dropRect.pointInRect ( x, y ) )
+ {
+ if ( mPalette [ curEntry ] )
+ {
+ delete mPalette [ curEntry ];
+
+ mPalette [ curEntry ] = new LLColor4 ( getCurR (), getCurG (), getCurB (), 1.0f );
+
+ // save off color
+ std::ostringstream codec;
+ codec << "ColorPaletteEntry" << std::setfill ( '0' ) << std::setw ( 2 ) << curEntry + 1;
+ const std::string s ( codec.str () );
+ gSavedSettings.setColor4( s, *mPalette [ curEntry ] );
+ }
+ }
+
+ ++curEntry;
+ }
+ }
+ }
+ }
+
+ // mouse button not down anymore
+ setMouseDownInHueRegion ( FALSE );
+ setMouseDownInLumRegion ( FALSE );
+
+ // mouse button not down in color swatch anymore
+ mMouseDownInSwatch = false;
+
+ if (gViewerWindow->hasMouseCapture(this))
+ {
+ gViewerWindow->setMouseCapture(NULL, NULL);
+ }
+
+ // dispatch to base class for the rest of things
+ return LLFloater::handleMouseUp ( x, y, mask );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// cancel current color selection, revert to original and close picker
+void
+LLFloaterColorPicker::
+cancelSelection ()
+{
+ // restore the previous colour selection
+ setCurRgb ( getOrigR (), getOrigG (), getOrigB () );
+
+ // we're going away and when we do and the entry widgets lose focus, they do bad things so turn them off
+ enableTextCallbacks ( FALSE );
+
+ // update in world item with original color via current swatch
+ LLColorSwatchCtrl::onColorChanged( getSwatch(), LLColorSwatchCtrl::COLOR_CANCEL );
+
+ // hide picker dialog
+ this->setVisible ( FALSE );
+}
+
+void LLFloaterColorPicker::setMouseDownInHueRegion ( BOOL mouse_down_in_region )
+{
+ mMouseDownInHueRegion = mouse_down_in_region;
+ if (mouse_down_in_region)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setMouseDownInLumRegion ( BOOL mouse_down_in_region )
+{
+ mMouseDownInLumRegion = mouse_down_in_region;
+ if (mouse_down_in_region)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setMouseDownInSwatch (BOOL mouse_down_in_swatch)
+{
+ mMouseDownInSwatch = mouse_down_in_swatch;
+ if (mouse_down_in_swatch)
+ {
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ // get focus out of spinners so that they can update freely
+ mSelectBtn->setFocus(TRUE);
+ }
+ }
+}
+
+void LLFloaterColorPicker::setActive(BOOL active)
+{
+ // shut down pipette tool if active
+ if (!active && mPipetteBtn->getToggleState())
+ {
+ stopUsingPipette();
+ }
+ mActive = active;
+}
+
+void LLFloaterColorPicker::stopUsingPipette()
+{
+ if (gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette)
+ {
+ gToolMgr->clearTransientTool();
+ }
+}
diff --git a/indra/newview/llfloatercolorpicker.h b/indra/newview/llfloatercolorpicker.h
new file mode 100644
index 0000000000..aa406c0dec
--- /dev/null
+++ b/indra/newview/llfloatercolorpicker.h
@@ -0,0 +1,179 @@
+/**
+ * @file llfloatercolorpicker.h
+ * @brief Generic system color picker
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERCOLORPICKER_H
+#define LL_LLFLOATERCOLORPICKER_H
+
+#include <vector>
+
+#include "llfloater.h"
+#include "llmemory.h"
+#include "llcolorswatch.h"
+#include "llspinctrl.h"
+#include "lltextureentry.h"
+
+class LLButton;
+class LLLineEditor;
+class LLCheckBoxCtrl;
+
+//////////////////////////////////////////////////////////////////////////////
+// floater class
+class LLFloaterColorPicker
+ : public LLFloater
+{
+ public:
+ LLFloaterColorPicker (LLColorSwatchCtrl* swatch, BOOL show_apply_immediate = FALSE);
+ virtual ~LLFloaterColorPicker ();
+
+ // overrides
+ virtual BOOL postBuild ();
+ virtual void draw ();
+ virtual BOOL handleMouseDown ( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp ( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover ( S32 x, S32 y, MASK mask );
+ virtual void onClose(bool app_quitting);
+
+ // implicit methods
+ void createUI ();
+ void initUI ( F32 rValIn, F32 gValIn, F32 bValIn );
+ void showUI ();
+ void destroyUI ();
+ void cancelSelection ();
+ LLColorSwatchCtrl* getSwatch () { return mSwatch; };
+
+ // mutator / accessor for original RGB value
+ void setOrigRgb ( F32 origRIn, F32 origGIn, F32 origBIn );
+ void getOrigRgb ( F32& origROut, F32& origGOut, F32& origBOut );
+ F32 getOrigR () { return origR; };
+ F32 getOrigG () { return origG; };
+ F32 getOrigB () { return origB; };
+
+ // mutator / accessors for currernt RGB value
+ void setCurRgb ( F32 curRIn, F32 curGIn, F32 curBIn );
+ void getCurRgb ( F32& curROut, F32& curGOut, F32& curBOut );
+ F32 getCurR () { return curR; };
+ F32 getCurG () { return curG; };
+ F32 getCurB () { return curB; };
+
+ // mutator / accessors for currernt HSL value
+ void setCurHsl ( F32 curHIn, F32 curSIn, F32 curLIn );
+ void getCurHsl ( F32& curHOut, F32& curSOut, F32& curLOut );
+ F32 getCurH () { return curH; };
+ F32 getCurS () { return curS; };
+ F32 getCurL () { return curL; };
+
+ // updates current RGB/HSL values based on point in picker
+ BOOL updateRgbHslFromPoint ( S32 xPosIn, S32 yPosIn );
+
+ // updates text entry fields with current RGB/HSL
+ void updateTextEntry ();
+
+ void stopUsingPipette();
+
+ // mutator / accessor for mouse button pressed in region
+ void setMouseDownInHueRegion ( BOOL mouse_down_in_region );
+ BOOL getMouseDownInHueRegion () { return mMouseDownInHueRegion; };
+
+ void setMouseDownInLumRegion ( BOOL mouse_down_in_region );
+ BOOL getMouseDownInLumRegion () { return mMouseDownInLumRegion; };
+
+ void setMouseDownInSwatch (BOOL mouse_down_in_swatch);
+ BOOL getMouseDownInSwatch () { return mMouseDownInSwatch; }
+
+ // called when text entries (RGB/HSL etc.) are changed by user
+ void onTextEntryChanged ( LLUICtrl* ctrl );
+
+ // convert RGB to HSL and vice-versa
+ void hslToRgb ( F32 hValIn, F32 sValIn, F32 lValIn, F32& rValOut, F32& gValOut, F32& bValOut );
+ void rgbToHsl ( F32 rValIn, F32 gValIn, F32 bValIn, F32& hValOut, F32& sValOut, F32& lValOut );
+ F32 hueToRgb ( F32 val1In, F32 val2In, F32 valHUeIn );
+
+ void setActive(BOOL active);
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+
+ protected:
+ // callbacks
+ static void onClickCancel ( void* data );
+ static void onClickSelect ( void* data );
+ static void onClickPipette ( void* data );
+ static void onTextCommit ( LLUICtrl* ctrl, void* data );
+ static void onImmediateCheck ( LLUICtrl* ctrl, void* data );
+ static void onColorSelect( const LLTextureEntry& te, void *data );
+ private:
+ // turns on or off text entry commit call backs
+ void enableTextCallbacks ( BOOL stateIn );
+
+ // draws color selection palette
+ void drawPalette ();
+
+ // find a complimentary color to the one passed in that can be used to highlight
+ LLColor4& getComplimentaryColor ( LLColor4& backgroundColor );
+
+ // original RGB values
+ F32 origR, origG, origB;
+
+ // current RGB/HSL values
+ F32 curR, curG, curB;
+ F32 curH, curS, curL;
+
+ const S32 mComponents;
+
+ BOOL mMouseDownInLumRegion;
+ BOOL mMouseDownInHueRegion;
+ BOOL mMouseDownInSwatch;
+
+ const S32 mRGBViewerImageLeft;
+ const S32 mRGBViewerImageTop;
+ const S32 mRGBViewerImageWidth;
+ const S32 mRGBViewerImageHeight;
+
+ const S32 mLumRegionLeft;
+ const S32 mLumRegionTop;
+ const S32 mLumRegionWidth;
+ const S32 mLumRegionHeight;
+ const S32 mLumMarkerSize;
+
+ // Preview of the current color.
+ const S32 mSwatchRegionLeft;
+ const S32 mSwatchRegionTop;
+ const S32 mSwatchRegionWidth;
+ const S32 mSwatchRegionHeight;
+
+ LLView* mSwatchView;
+
+ const S32 numPaletteColumns;
+ const S32 numPaletteRows;
+ std::vector < LLColor4* > mPalette;
+ S32 highlightEntry;
+ const S32 mPaletteRegionLeft;
+ const S32 mPaletteRegionTop;
+ const S32 mPaletteRegionWidth;
+ const S32 mPaletteRegionHeight;
+
+ // image used to compose color grid
+ LLPointer<LLImageGL> mRGBImage;
+
+ // current swatch in use
+ LLColorSwatchCtrl* mSwatch;
+
+ // are we actively tied to some output?
+ BOOL mActive;
+
+ // enable/disable immediate updates
+ LLCheckBoxCtrl* mApplyImmediateCheck;
+ BOOL mCanApplyImmediately;
+
+ LLButton* mSelectBtn;
+ LLButton* mCancelBtn;
+
+ LLButton* mPipetteBtn;
+
+ F32 mContextConeOpacity;
+};
+
+#endif // LL_LLFLOATERCOLORPICKER_H
diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp
new file mode 100644
index 0000000000..e5aac8b252
--- /dev/null
+++ b/indra/newview/llfloaterfriends.cpp
@@ -0,0 +1,736 @@
+/**
+ * @file llfloaterfriends.cpp
+ * @author Phoenix
+ * @date 2005-01-13
+ * @brief Implementation of the friends floater
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterfriends.h"
+
+#include <sstream>
+
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llfloateravatarinfo.h"
+#include "llinventorymodel.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llimview.h"
+#include "llvieweruictrlfactory.h"
+#include "llmenucommands.h"
+#include "llviewercontrol.h"
+#include "llviewermessage.h"
+#include "lltimer.h"
+#include "lltextbox.h"
+
+//Maximum number of people you can select to do an operation on at once.
+#define MAX_FRIEND_SELECT 20
+#define RIGHTS_CHANGE_TIMEOUT 5.0
+
+// simple class to observe the calling cards.
+class LLLocalFriendsObserver : public LLFriendObserver
+{
+public:
+ LLLocalFriendsObserver(LLFloaterFriends* floater) : mFloater(floater) {}
+ virtual ~LLLocalFriendsObserver() { mFloater = NULL; }
+ virtual void changed(U32 mask)
+ {
+ mFloater->updateFriends(mask);
+ }
+protected:
+ LLFloaterFriends* mFloater;
+};
+
+LLFloaterFriends* LLFloaterFriends::sInstance = NULL;
+
+LLFloaterFriends::LLFloaterFriends() :
+ LLFloater("Friends"),
+ LLEventTimer(1000000),
+ mObserver(NULL),
+ mMenuState(0),
+ mShowMaxSelectWarning(TRUE),
+ mAllowRightsChange(TRUE),
+ mNumRightsChanged(0)
+{
+ mTimer.stop();
+ sInstance = this;
+ mObserver = new LLLocalFriendsObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+ gSavedSettings.setBOOL("ShowFriends", TRUE);
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(this, "floater_friends.xml");
+}
+
+LLFloaterFriends::~LLFloaterFriends()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+ sInstance = NULL;
+ gSavedSettings.setBOOL("ShowFriends", FALSE);
+}
+
+void LLFloaterFriends::tick()
+{
+ mTimer.stop();
+ mPeriod = 1000000;
+ mAllowRightsChange = TRUE;
+ updateFriends(LLFriendObserver::ADD);
+}
+
+// static
+void LLFloaterFriends::show(void*)
+{
+ if(sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterFriends* self = new LLFloaterFriends;
+ self->open();
+ }
+}
+
+
+// static
+BOOL LLFloaterFriends::visible(void*)
+{
+ return sInstance && sInstance->getVisible();
+}
+
+
+// static
+void LLFloaterFriends::toggle(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show();
+ }
+}
+
+
+void LLFloaterFriends::updateFriends(U32 changed_mask)
+{
+ LLUUID selected_id;
+ LLCtrlListInterface *friends_list = sInstance->childGetListInterface("friend_list");
+ if (!friends_list) return;
+ LLCtrlScrollInterface *friends_scroll = sInstance->childGetScrollInterface("friend_list");
+ if (!friends_scroll) return;
+
+ // We kill the selection warning, otherwise we'll spam with warning popups
+ // if the maximum amount of friends are selected
+ mShowMaxSelectWarning = false;
+
+ LLDynamicArray<LLUUID> selected_friends = sInstance->getSelectedIDs();
+ if(changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ {
+ refreshNames();
+ }
+ else if(LLFriendObserver::POWERS)
+ {
+ --mNumRightsChanged;
+ if(mNumRightsChanged > 0)
+ {
+ mPeriod = RIGHTS_CHANGE_TIMEOUT;
+ mTimer.start();
+ mTimer.reset();
+ mAllowRightsChange = FALSE;
+ }
+ else
+ {
+ tick();
+ }
+ }
+ if(selected_friends.size() > 0)
+ {
+ // only non-null if friends was already found. This may fail,
+ // but we don't really care here, because refreshUI() will
+ // clean up the interface.
+ friends_list->setCurrentByID(selected_id);
+ for(LLDynamicArray<LLUUID>::iterator itr = selected_friends.begin(); itr != selected_friends.end(); ++itr)
+ {
+ friends_list->setSelectedByValue(*itr, true);
+ }
+ }
+
+ refreshUI();
+ mShowMaxSelectWarning = true;
+}
+
+// virtual
+BOOL LLFloaterFriends::postBuild()
+{
+
+ mFriendsList = LLUICtrlFactory::getScrollListByName(this, "friend_list");
+ mFriendsList->setMaxSelectable(MAX_FRIEND_SELECT);
+ mFriendsList->setMaxiumumSelectCallback(onMaximumSelect);
+ childSetCommitCallback("friend_list", onSelectName, this);
+ childSetDoubleClickCallback("friend_list", onClickIM);
+
+ refreshNames();
+
+ childSetCommitCallback("online_status_cb", onClickOnlineStatus, this);
+ childSetCommitCallback("map_status_cb", onClickMapStatus, this);
+ childSetCommitCallback("modify_status_cb", onClickModifyStatus, this);
+ childSetAction("im_btn", onClickIM, this);
+ childSetAction("profile_btn", onClickProfile, this);
+ childSetAction("offer_teleport_btn", onClickOfferTeleport, this);
+ childSetAction("pay_btn", onClickPay, this);
+ childSetAction("add_btn", onClickAddFriend, this);
+ childSetAction("remove_btn", onClickRemove, this);
+ childSetAction("close_btn", onClickClose, this);
+
+ refreshUI();
+ return TRUE;
+}
+
+
+void LLFloaterFriends::addFriend(const std::string& name, const LLUUID& agent_id)
+{
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ const LLRelationship* relationInfo = at.getBuddyInfo(agent_id);
+ if(!relationInfo) return;
+ BOOL online = relationInfo->isOnline();
+
+ LLSD element;
+ element["id"] = agent_id;
+ element["columns"][LIST_FRIEND_NAME]["column"] = "friend_name";
+ element["columns"][LIST_FRIEND_NAME]["value"] = name.c_str();
+ element["columns"][LIST_FRIEND_NAME]["font"] = "SANSSERIF";
+ element["columns"][LIST_FRIEND_NAME]["font-style"] = "NORMAL";
+ element["columns"][LIST_ONLINE_STATUS]["column"] = "icon_online_status";
+ element["columns"][LIST_ONLINE_STATUS]["type"] = "text";
+ if (online)
+ {
+ element["columns"][LIST_FRIEND_NAME]["font-style"] = "BOLD";
+ element["columns"][LIST_ONLINE_STATUS]["type"] = "icon";
+ element["columns"][LIST_ONLINE_STATUS]["value"] = gViewerArt.getString("icon_avatar_online.tga");
+ }
+
+
+ element["columns"][LIST_VISIBLE_ONLINE]["column"] = "icon_visible_online";
+ element["columns"][LIST_VISIBLE_ONLINE]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+ {
+ element["columns"][LIST_VISIBLE_ONLINE]["type"] = "icon";
+ element["columns"][LIST_VISIBLE_ONLINE]["value"] = gViewerArt.getString("ff_visible_online.tga");
+ }
+ element["columns"][LIST_VISIBLE_MAP]["column"] = "icon_visible_map";
+ element["columns"][LIST_VISIBLE_MAP]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ element["columns"][LIST_VISIBLE_MAP]["type"] = "icon";
+ element["columns"][LIST_VISIBLE_MAP]["value"] = gViewerArt.getString("ff_visible_map.tga");
+ }
+ element["columns"][LIST_EDIT_MINE]["column"] = "icon_edit_mine";
+ element["columns"][LIST_EDIT_MINE]["type"] = "text";
+ if(relationInfo->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ element["columns"][LIST_EDIT_MINE]["type"] = "icon";
+ element["columns"][LIST_EDIT_MINE]["value"] = gViewerArt.getString("ff_edit_mine.tga");
+ }
+ element["columns"][LIST_EDIT_THEIRS]["column"] = "icon_edit_theirs";
+ element["columns"][LIST_EDIT_THEIRS]["type"] = "text";
+ if(relationInfo->isRightGrantedFrom(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ element["columns"][LIST_EDIT_THEIRS]["type"] = "icon";
+ element["columns"][LIST_EDIT_THEIRS]["value"] = gViewerArt.getString("ff_edit_theirs.tga");
+ }
+ mFriendsList->addElement(element, ADD_BOTTOM);
+}
+
+void LLFloaterFriends::reloadNames()
+{
+}
+
+void LLFloaterFriends::refreshRightsChangeList(U8 state)
+{
+ LLDynamicArray<LLUUID> friends = getSelectedIDs();
+ const LLRelationship* friend_status = NULL;
+ if(friends.size() > 0) friend_status = LLAvatarTracker::instance().getBuddyInfo(friends[0]);
+
+ LLSD row;
+ bool can_change_visibility = false;
+ bool can_change_modify = false;
+ bool can_change_online_multiple = true;
+ bool can_change_map_multiple = true;
+ LLTextBox* processing_label = LLUICtrlFactory::getTextBoxByName(this, "process_rights_label");
+ if(!mAllowRightsChange)
+ {
+ if(processing_label)
+ {
+ processing_label->setVisible(true);
+ state = 0;
+ }
+ }
+ else
+ {
+ if(processing_label)
+ {
+ processing_label->setVisible(false);
+ }
+ }
+ if(state == 1)
+ {
+ if(!friend_status->isOnline())
+ {
+ childSetEnabled("offer_teleport_btn", false);
+ }
+ can_change_visibility = true;
+ can_change_modify = true;
+ }
+ else if (state == 2)
+ {
+ can_change_visibility = true;
+ can_change_modify = false;
+ for(LLDynamicArray<LLUUID>::iterator itr = friends.begin(); itr != friends.end(); ++itr)
+ {
+ friend_status = LLAvatarTracker::instance().getBuddyInfo(*itr);
+ if(!friend_status->isOnline())
+ {
+ childSetEnabled("offer_teleport_btn", false);
+ }
+ if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS))
+ {
+ can_change_online_multiple = false;
+ can_change_map_multiple = false;
+ }
+ else if(!friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION))
+ {
+ can_change_map_multiple = false;
+ }
+ }
+
+ }
+
+
+ LLCheckboxCtrl* check;
+ mMenuState = 0;
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "online_status_cb");
+ check->setEnabled(can_change_visibility);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(friend_status)
+ {
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_online_multiple);
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS)) mMenuState |= LLRelationship::GRANT_ONLINE_STATUS;
+ }
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "map_status_cb");
+ check->setEnabled(false);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(friend_status)
+ {
+ check->setEnabled(friend_status->isRightGrantedTo(LLRelationship::GRANT_ONLINE_STATUS) && can_change_visibility && can_change_online_multiple);
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION) && can_change_map_multiple);
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MAP_LOCATION)) mMenuState |= LLRelationship::GRANT_MAP_LOCATION;
+ }
+
+ check = LLUICtrlFactory::getCheckBoxByName(this, "modify_status_cb");
+ check->setEnabled(can_change_modify);
+ check->set(FALSE);
+ if(!mAllowRightsChange) check->setVisible(FALSE);
+ else check->setVisible(TRUE);
+ if(can_change_modify)
+ {
+ if(friend_status)
+ {
+ check->set(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS));
+ if(friend_status->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS)) mMenuState |= LLRelationship::GRANT_MODIFY_OBJECTS;
+ }
+ }
+}
+
+void LLFloaterFriends::refreshNames()
+{
+ S32 pos = mFriendsList->getScrollPos();
+ mFriendsList->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLCollectAllBuddies collect;
+ LLAvatarTracker::instance().applyFunctor(collect);
+ LLCollectAllBuddies::buddy_map_t::const_iterator it = collect.mOnline.begin();
+ LLCollectAllBuddies::buddy_map_t::const_iterator end = collect.mOnline.end();
+ for ( ; it != end; ++it)
+ {
+ const std::string& name = it->first;
+ const LLUUID& agent_id = it->second;
+ addFriend(name, agent_id);
+ }
+ it = collect.mOffline.begin();
+ end = collect.mOffline.end();
+ for ( ; it != end; ++it)
+ {
+ const std::string& name = it->first;
+ const LLUUID& agent_id = it->second;
+ addFriend(name, agent_id);
+ }
+ mFriendsList->setScrollPos(pos);
+}
+
+
+void LLFloaterFriends::refreshUI()
+{
+ BOOL single_selected = FALSE;
+ BOOL multiple_selected = FALSE;
+ int num_selected = mFriendsList->getAllSelected().size();
+ if(num_selected > 0)
+ {
+ single_selected = TRUE;
+ if(num_selected > 1) multiple_selected = TRUE;
+ }
+ //Options that can only be performed with one friend selected
+ childSetEnabled("profile_btn", single_selected && !multiple_selected);
+ childSetEnabled("pay_btn", single_selected && !multiple_selected);
+
+ //Options that can be performed with up to MAX_FRIEND_SELECT friends selected
+ //(single_selected will always be true in this situations)
+ childSetEnabled("remove_btn", single_selected);
+ childSetEnabled("im_btn", single_selected);
+ childSetEnabled("friend_rights", single_selected);
+
+ //Note: We reset this in refreshRightsChangeList since we already have to iterate
+ //through all selected friends there
+ childSetEnabled("offer_teleport_btn", single_selected);
+
+ refreshRightsChangeList((single_selected + multiple_selected));
+}
+
+// static
+LLDynamicArray<LLUUID> LLFloaterFriends::getSelectedIDs()
+{
+ LLUUID selected_id;
+ LLDynamicArray<LLUUID> friend_ids;
+ if(sInstance)
+ {
+ std::vector<LLScrollListItem*> selected = sInstance->mFriendsList->getAllSelected();
+ for(std::vector<LLScrollListItem*>::iterator itr = selected.begin(); itr != selected.end(); ++itr)
+ {
+ friend_ids.push_back((*itr)->getUUID());
+ }
+ }
+ return friend_ids;
+}
+
+// static
+void LLFloaterFriends::onSelectName(LLUICtrl* ctrl, void* user_data)
+{
+ if(sInstance)
+ {
+ sInstance->refreshUI();
+ }
+}
+
+//static
+void LLFloaterFriends::onMaximumSelect(void* user_data)
+{
+ LLString::format_map_t args;
+ args["[MAX_SELECT]"] = llformat("%d", MAX_FRIEND_SELECT);
+ LLNotifyBox::showXml("MaxListSelectMessage", args);
+};
+
+// static
+void LLFloaterFriends::onClickProfile(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickProfile()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ LLUUID agent_id = ids[0];
+ BOOL online;
+ online = LLAvatarTracker::instance().isBuddyOnline(agent_id);
+ LLFloaterAvatarInfo::showFromFriend(agent_id, online);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickIM(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickIM()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ const LLRelationship* info = LLAvatarTracker::instance().getBuddyInfo(agent_id);
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(info && gCacheName->getName(agent_id, first, last))
+ {
+ char buffer[MAX_STRING];
+ snprintf(buffer, MAX_STRING, "%s %s", first, last);
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(
+ buffer,
+ IM_NOTHING_SPECIAL,
+ agent_id);
+ }
+ }
+ else
+ {
+ LLUUID session_id;
+ session_id.generate();
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(
+ "Friends Conference",
+ IM_SESSION_ADD,
+ session_id,
+ ids);
+ }
+ }
+}
+
+// static
+void LLFloaterFriends::requestFriendship(const LLUUID& target_id, const LLString& target_name)
+{
+ // HACK: folder id stored as "message"
+ LLUUID calling_card_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ std::string message = calling_card_folder_id.getString();
+
+ send_improved_im(target_id,
+ target_name.c_str(),
+ message.c_str(),
+ IM_ONLINE,
+ IM_FRIENDSHIP_OFFERED);
+}
+
+// static
+void LLFloaterFriends::callbackAddFriend(S32 option, void* user_data)
+{
+ if (option == 0)
+ {
+ LLFloaterFriends* self = (LLFloaterFriends*)user_data;
+ requestFriendship(self->mAddFriendID, self->mAddFriendName);
+ }
+}
+
+// static
+void LLFloaterFriends::onPickAvatar(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& ids,
+ void* user_data)
+{
+ if (names.empty()) return;
+ if (ids.empty()) return;
+
+ LLFloaterFriends* self = (LLFloaterFriends*)user_data;
+ self->mAddFriendID = ids[0];
+ self->mAddFriendName = names[0];
+
+ if(ids[0] == gAgentID)
+ {
+ LLNotifyBox::showXml("AddSelfFriend");
+ return;
+ }
+
+ // TODO: accept a line of text with this dialog
+ LLString::format_map_t args;
+ args["[NAME]"] = names[0];
+ gViewerWindow->alertXml("AddFriend", args, callbackAddFriend, user_data);
+}
+
+// static
+void LLFloaterFriends::onClickAddFriend(void* user_data)
+{
+ LLFloaterAvatarPicker::show(onPickAvatar, user_data, FALSE, TRUE);
+}
+
+// static
+void LLFloaterFriends::onClickRemove(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickRemove()" << llendl;
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLStringBase<char>::format_map_t args;
+ if(ids.size() > 0)
+ {
+ LLString msgType = "RemoveFromFriends";
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ }
+ else
+ {
+ msgType = "RemoveMultipleFromFriends";
+ }
+ gViewerWindow->alertXml(msgType,
+ args,
+ &handleRemove,
+ (void*)new LLDynamicArray<LLUUID>(ids));
+ }
+}
+
+// static
+void LLFloaterFriends::onClickOfferTeleport(void*)
+{
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() > 0)
+ {
+ handle_lure(ids);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickPay(void*)
+{
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ if(ids.size() == 1)
+ {
+ handle_pay_by_id(ids[0]);
+ }
+}
+
+// static
+void LLFloaterFriends::onClickClose(void* user_data)
+{
+ //llinfos << "LLFloaterFriends::onClickClose()" << llendl;
+ if(sInstance)
+ {
+ sInstance->onClose(false);
+ }
+}
+
+void LLFloaterFriends::onClickOnlineStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ sInstance->updateMenuState(LLRelationship::GRANT_ONLINE_STATUS, checked);
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_ONLINE_STATUS, checked);
+}
+
+void LLFloaterFriends::onClickMapStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ sInstance->updateMenuState(LLRelationship::GRANT_MAP_LOCATION, checked);
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_MAP_LOCATION, checked);
+}
+
+void LLFloaterFriends::onClickModifyStatus(LLUICtrl* ctrl, void* user_data)
+{
+ bool checked = ctrl->getValue();
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ LLStringBase<char>::format_map_t args;
+ if(ids.size() > 0)
+ {
+ if(ids.size() == 1)
+ {
+ LLUUID agent_id = ids[0];
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(gCacheName->getName(agent_id, first, last))
+ {
+ args["[FIRST_NAME]"] = first;
+ args["[LAST_NAME]"] = last;
+ }
+ if(checked) gViewerWindow->alertXml("GrantModifyRights", args, handleModifyRights, NULL);
+ else gViewerWindow->alertXml("RevokeModifyRights", args, handleModifyRights, NULL);
+ }
+ else return;
+ }
+}
+
+void LLFloaterFriends::handleModifyRights(S32 option, void* user_data)
+{
+ if(sInstance)
+ {
+ if(!option)
+ {
+ sInstance->updateMenuState(LLRelationship::GRANT_MODIFY_OBJECTS, !((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ sInstance->applyRightsToFriends(LLRelationship::GRANT_MODIFY_OBJECTS, ((sInstance->getMenuState() & LLRelationship::GRANT_MODIFY_OBJECTS) != 0));
+ }
+ sInstance->refreshUI();
+ }
+}
+
+void LLFloaterFriends::updateMenuState(S32 flag, BOOL value)
+{
+ if(value) mMenuState |= flag;
+ else mMenuState &= ~flag;
+}
+
+void LLFloaterFriends::applyRightsToFriends(S32 flag, BOOL value)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GrantUserRights);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUID(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+
+ LLDynamicArray<LLUUID> ids = getSelectedIDs();
+ S32 rights;
+ for(LLDynamicArray<LLUUID>::iterator itr = ids.begin(); itr != ids.end(); ++itr)
+ {
+ rights = LLAvatarTracker::instance().getBuddyInfo(*itr)->getRightsGrantedTo();
+ if(LLAvatarTracker::instance().getBuddyInfo(*itr)->isRightGrantedTo(flag) != (bool)value)
+ {
+ if(value) rights |= flag;
+ else rights &= ~flag;
+ msg->nextBlockFast(_PREHASH_Rights);
+ msg->addUUID(_PREHASH_AgentRelated, *itr);
+ msg->addS32(_PREHASH_RelatedRights, rights);
+ }
+ }
+ mNumRightsChanged = ids.size();
+ gAgent.sendReliableMessage();
+}
+
+
+
+// static
+void LLFloaterFriends::handleRemove(S32 option, void* user_data)
+{
+ LLDynamicArray<LLUUID>* ids = static_cast<LLDynamicArray<LLUUID>*>(user_data);
+ for(LLDynamicArray<LLUUID>::iterator itr = ids->begin(); itr != ids->end(); ++itr)
+ {
+ LLUUID id = (*itr);
+ const LLRelationship* ip = LLAvatarTracker::instance().getBuddyInfo(id);
+ if(ip)
+ {
+ switch(option)
+ {
+ case 0: // YES
+ if( ip->isRightGrantedTo(LLRelationship::GRANT_MODIFY_OBJECTS))
+ {
+ LLAvatarTracker::instance().empower(id, FALSE);
+ LLAvatarTracker::instance().notifyObservers();
+ }
+ LLAvatarTracker::instance().terminateBuddy(id);
+ LLAvatarTracker::instance().notifyObservers();
+ gInventory.addChangedMask(LLInventoryObserver::LABEL | LLInventoryObserver::CALLING_CARD, LLUUID::null);
+ gInventory.notifyObservers();
+ break;
+
+ case 1: // NO
+ default:
+ llinfos << "No removal performed." << llendl;
+ break;
+ }
+ }
+
+ }
+ delete ids;
+}
diff --git a/indra/newview/llfloaterfriends.h b/indra/newview/llfloaterfriends.h
new file mode 100644
index 0000000000..a71b53f206
--- /dev/null
+++ b/indra/newview/llfloaterfriends.h
@@ -0,0 +1,124 @@
+/**
+ * @file llfloaterfriends.h
+ * @author Phoenix
+ * @date 2005-01-13
+ * @brief Declaration of class for displaying the local agent's friends.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERFRIENDS_H
+#define LL_LLFLOATERFRIENDS_H
+
+#include "llfloater.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "lltimer.h"
+
+class LLFriendObserver;
+
+
+/**
+ * @class LLFloaterFriends
+ * @brief An instance of this class is used for displaying your friends
+ * and gives you quick access to all agents which a user relationship.
+ *
+ * @sa LLFloater
+ */
+class LLFloaterFriends : public LLFloater, public LLEventTimer
+{
+public:
+ virtual ~LLFloaterFriends();
+
+ /**
+ * @brief This method either creates or brings to the front the
+ * current instantiation of this floater. There is only once since
+ * you can currently only look at your local friends.
+ */
+ static void show(void* ignored = NULL);
+
+ virtual void tick();
+
+ /**
+ * @brief This method is called in response to the LLAvatarTracker
+ * sending out a changed() message.
+ */
+ void updateFriends(U32 changed_mask);
+
+ virtual BOOL postBuild();
+
+ static BOOL visible(void* unused = NULL);
+
+ // Toggles visibility of floater
+ static void toggle(void* unused = NULL);
+
+ static void requestFriendship(const LLUUID& target_id, const LLString& target_name);
+
+private:
+
+ enum FRIENDS_COLUMN_ORDER
+ {
+ LIST_ONLINE_STATUS,
+ LIST_FRIEND_NAME,
+ LIST_VISIBLE_ONLINE,
+ LIST_VISIBLE_MAP,
+ LIST_EDIT_MINE,
+ LIST_EDIT_THEIRS
+ };
+
+ // protected members
+ LLFloaterFriends();
+
+ void reloadNames();
+ void refreshNames();
+ void refreshUI();
+ void refreshRightsChangeList(U8 state);
+ void applyRightsToFriends(S32 flag, BOOL value);
+ void updateMenuState(S32 flag, BOOL value);
+ S32 getMenuState() { return mMenuState; }
+ void addFriend(const std::string& name, const LLUUID& agent_id);
+
+ // return LLUUID::null if nothing is selected
+ static LLDynamicArray<LLUUID> getSelectedIDs();
+
+ // callback methods
+ static void onSelectName(LLUICtrl* ctrl, void* user_data);
+ static void callbackAddFriend(S32 option, void* user_data);
+ static void onPickAvatar(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* user_data);
+ static void onMaximumSelect(void* user_data);
+
+ static void onClickIM(void* user_data);
+ static void onClickProfile(void* user_data);
+ static void onClickAddFriend(void* user_data);
+ static void onClickRemove(void* user_data);
+
+ static void onClickOfferTeleport(void* user_data);
+ static void onClickPay(void* user_data);
+
+ static void onClickClose(void* user_data);
+
+ static void onClickOnlineStatus(LLUICtrl* ctrl, void* user_data);
+ static void onClickMapStatus(LLUICtrl* ctrl, void* user_data);
+ static void onClickModifyStatus(LLUICtrl* ctrl, void* user_data);
+
+ static void handleRemove(S32 option, void* user_data);
+ static void handleModifyRights(S32 option, void* user_data);
+
+private:
+ // static data
+ static LLFloaterFriends* sInstance;
+
+ // member data
+ LLFriendObserver* mObserver;
+ LLUUID mAddFriendID;
+ LLString mAddFriendName;
+ LLScrollListCtrl* mFriendsList;
+ S32 mMenuState;
+ BOOL mShowMaxSelectWarning;
+ BOOL mAllowRightsChange;
+ S32 mNumRightsChanged;
+};
+
+
+#endif // LL_LLFLOATERFRIENDS_H
diff --git a/indra/newview/llfloatergesture.cpp b/indra/newview/llfloatergesture.cpp
new file mode 100644
index 0000000000..a14c9f11cd
--- /dev/null
+++ b/indra/newview/llfloatergesture.cpp
@@ -0,0 +1,396 @@
+/**
+ * @file llfloatergesture.cpp
+ * @brief Read-only list of gestures from your inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergesture.h"
+
+#include "lldir.h"
+#include "llinventory.h"
+#include "llmultigesture.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llgesturemgr.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llpreviewgesture.h"
+#include "llresizehandle.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llvoavatar.h"
+#include "llviewercontrol.h"
+
+// static
+LLFloaterGesture* LLFloaterGesture::sInstance = NULL;
+LLFloaterGestureObserver* LLFloaterGesture::sObserver = NULL;
+
+BOOL item_name_precedes( LLInventoryItem* a, LLInventoryItem* b )
+{
+ return LLString::precedesDict( a->getName(), b->getName() );
+}
+
+class LLFloaterGestureObserver : public LLGestureManagerObserver
+{
+public:
+ LLFloaterGestureObserver() {}
+ virtual ~LLFloaterGestureObserver() {}
+ virtual void changed() { LLFloaterGesture::refreshAll(); }
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterGesture
+//---------------------------------------------------------------------------
+LLFloaterGesture::LLFloaterGesture()
+: LLFloater("Gesture Floater")
+{
+ sInstance = this;
+
+ sObserver = new LLFloaterGestureObserver;
+ gGestureManager.addObserver(sObserver);
+}
+
+// virtual
+LLFloaterGesture::~LLFloaterGesture()
+{
+ gGestureManager.removeObserver(sObserver);
+ delete sObserver;
+ sObserver = NULL;
+
+ sInstance = NULL;
+
+ // Custom saving rectangle, since load must be done
+ // after postBuild.
+ gSavedSettings.setRect("FloaterGestureRect", mRect);
+}
+
+// virtual
+BOOL LLFloaterGesture::postBuild()
+{
+ LLString label;
+
+ // Translate title
+ label = getTitle();
+
+ setTitle(label);
+
+ childSetCommitCallback("gesture_list", onCommitList, this);
+ childSetDoubleClickCallback("gesture_list", onClickPlay);
+
+ childSetAction("inventory_btn", onClickInventory, this);
+
+ childSetAction("edit_btn", onClickEdit, this);
+
+ childSetAction("play_btn", onClickPlay, this);
+ childSetAction("stop_btn", onClickPlay, this);
+
+ childSetAction("new_gesture_btn", onClickNew, this);
+
+ childSetVisible("play_btn", true);
+ childSetVisible("stop_btn", false);
+ setDefaultBtn("play_btn");
+
+ return TRUE;
+}
+
+
+// static
+void LLFloaterGesture::show()
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ return;
+ }
+
+ LLFloaterGesture *self = new LLFloaterGesture();
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_gesture.xml");
+
+ // Fix up rectangle
+ LLRect rect = gSavedSettings.getRect("FloaterGestureRect");
+ self->reshape(rect.getWidth(), rect.getHeight());
+ self->setRect(rect);
+
+ self->buildGestureList();
+
+ self->childSetFocus("gesture_list");
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (list) list->selectFirstItem();
+
+ self->mSelectedID = LLUUID::null;
+
+ // Update button labels
+ onCommitList(NULL, self);
+ self->open();
+}
+
+// static
+void LLFloaterGesture::toggleVisibility()
+{
+ if(sInstance && sInstance->getVisible())
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show();
+ }
+}
+
+// static
+void LLFloaterGesture::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->buildGestureList();
+
+ LLCtrlListInterface *list = sInstance->childGetListInterface("gesture_list");
+ if (!list) return;
+
+ if (sInstance->mSelectedID.isNull())
+ {
+ list->selectFirstItem();
+ }
+ else
+ {
+ if (list->setCurrentByID(sInstance->mSelectedID))
+ {
+ LLCtrlScrollInterface *scroll = sInstance->childGetScrollInterface("gesture_list");
+ if (scroll) scroll->scrollToShowSelected();
+ }
+ else
+ {
+ list->selectFirstItem();
+ }
+ }
+
+ // Update button labels
+ onCommitList(NULL, sInstance);
+ }
+}
+
+void LLFloaterGesture::buildGestureList()
+{
+ LLCtrlListInterface *list = childGetListInterface("gesture_list");
+ if (!list) return;
+
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLGestureManager::item_map_t::iterator it;
+ for (it = gGestureManager.mActive.begin(); it != gGestureManager.mActive.end(); ++it)
+ {
+ const LLUUID& item_id = (*it).first;
+ LLMultiGesture* gesture = (*it).second;
+
+ // Note: Can have NULL item if inventory hasn't arrived yet.
+ std::string item_name = "Loading...";
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if (item)
+ {
+ item_name = item->getName();
+ }
+
+ LLString font_style = "NORMAL";
+ // If gesture is playing, bold it
+
+ LLSD element;
+ element["id"] = item_id;
+
+ if (gesture)
+ {
+ if (gesture->mPlaying)
+ {
+ font_style = "BOLD";
+ }
+
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = gesture->mTrigger;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+
+ LLString key_string = LLKeyboard::stringFromKey(gesture->mKey);
+ LLString buffer;
+
+ {
+ if (gesture->mKey == KEY_NONE)
+ {
+ buffer = "---";
+ key_string = "~~~"; // alphabetize to end
+ }
+ else
+ {
+ if (gesture->mMask & MASK_CONTROL) buffer.append("Ctrl-");
+ if (gesture->mMask & MASK_ALT) buffer.append("Alt-");
+ if (gesture->mMask & MASK_SHIFT) buffer.append("Shift-");
+ if ((gesture->mMask & (MASK_CONTROL|MASK_ALT|MASK_SHIFT)) &&
+ (key_string[0] == '-' || key_string[0] == '='))
+ {
+ buffer.append(" ");
+ }
+ buffer.append(key_string);
+ }
+ }
+ element["columns"][1]["column"] = "shortcut";
+ element["columns"][1]["value"] = buffer;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][1]["font-style"] = font_style;
+
+ // hidden column for sorting
+ element["columns"][2]["column"] = "key";
+ element["columns"][2]["value"] = key_string;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][2]["font-style"] = font_style;
+
+ // Only add "playing" if we've got the name, less confusing. JC
+ if (item && gesture->mPlaying)
+ {
+ item_name += " (Playing)";
+ }
+ element["columns"][3]["column"] = "name";
+ element["columns"][3]["value"] = item_name;
+ element["columns"][3]["font"] = "SANSSERIF";
+ element["columns"][3]["font-style"] = font_style;
+ }
+ else
+ {
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = "";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+ element["columns"][0]["column"] = "trigger";
+ element["columns"][0]["value"] = "---";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = font_style;
+ element["columns"][2]["column"] = "key";
+ element["columns"][2]["value"] = "~~~";
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][2]["font-style"] = font_style;
+ element["columns"][3]["column"] = "name";
+ element["columns"][3]["value"] = item_name;
+ element["columns"][3]["font"] = "SANSSERIF";
+ element["columns"][3]["font-style"] = font_style;
+ }
+ list->addElement(element, ADD_BOTTOM);
+ }
+}
+
+// static
+void LLFloaterGesture::onClickInventory(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ LLInventoryView* inv = LLInventoryView::showAgentInventory();
+ if (!inv) return;
+ inv->getPanel()->setSelection(item_id, TRUE);
+}
+
+// static
+void LLFloaterGesture::onClickPlay(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ if (gGestureManager.isGesturePlaying(item_id))
+ {
+ gGestureManager.stopGesture(item_id);
+ }
+ else
+ {
+ gGestureManager.playGesture(item_id);
+ }
+}
+
+class GestureShowCallback : public LLInventoryCallback
+{
+public:
+ GestureShowCallback(LLString &title)
+ {
+ mTitle = title;
+ }
+ ~GestureShowCallback() {}
+ void fire(const LLUUID &inv_item)
+ {
+ LLPreviewGesture::show(mTitle.c_str(), inv_item, LLUUID::null);
+ }
+private:
+ LLString mTitle;
+};
+
+// static
+void LLFloaterGesture::onClickNew(void* data)
+{
+ LLString title("Gesture: ");
+ title.append("New Gesture");
+ LLPointer<LLInventoryCallback> cb = new GestureShowCallback(title);
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ LLUUID::null, LLTransactionID::tnull, "New Gesture", "", LLAssetType::AT_GESTURE,
+ LLInventoryType::IT_GESTURE, NOT_WEARABLE, PERM_MOVE | PERM_TRANSFER, cb);
+}
+
+
+// static
+void LLFloaterGesture::onClickEdit(void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ LLCtrlListInterface *list = self->childGetListInterface("gesture_list");
+ if (!list) return;
+ const LLUUID& item_id = list->getCurrentID();
+
+ LLInventoryItem* item = gInventory.getItem(item_id);
+ if (!item) return;
+
+ LLString title("Gesture: ");
+ title.append(item->getName());
+
+ LLPreviewGesture* previewp = LLPreviewGesture::show(title, item_id, LLUUID::null);
+ if (!previewp->getHost())
+ {
+ previewp->setRect(gFloaterView->findNeighboringPosition(self, previewp));
+ }
+}
+
+// static
+void LLFloaterGesture::onCommitList(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterGesture* self = (LLFloaterGesture*)data;
+
+ const LLUUID& item_id = self->childGetValue("gesture_list").asUUID();
+
+ self->mSelectedID = item_id;
+ if (gGestureManager.isGesturePlaying(item_id))
+ {
+ self->childSetVisible("play_btn", false);
+ self->childSetVisible("stop_btn", true);
+ }
+ else
+ {
+ self->childSetVisible("play_btn", true);
+ self->childSetVisible("stop_btn", false);
+ }
+}
diff --git a/indra/newview/llfloatergesture.h b/indra/newview/llfloatergesture.h
new file mode 100644
index 0000000000..7f78754577
--- /dev/null
+++ b/indra/newview/llfloatergesture.h
@@ -0,0 +1,64 @@
+/**
+ * @file llfloatergesture.h
+ * @brief Read-only list of gestures from your inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * (Also has legacy gesture editor for testing.)
+ */
+
+#ifndef LL_LLFLOATERGESTURE_H
+#define LL_LLFLOATERGESTURE_H
+
+#include "llfloater.h"
+
+#include "lldarray.h"
+
+class LLScrollableContainerView;
+class LLView;
+class LLButton;
+class LLLineEditor;
+class LLComboBox;
+class LLViewerGesture;
+class LLGestureOptions;
+class LLScrollListCtrl;
+class LLFloaterGestureObserver;
+class LLFloaterGestureInventoryObserver;
+
+class LLFloaterGesture
+: public LLFloater
+{
+public:
+ LLFloaterGesture();
+ virtual ~LLFloaterGesture();
+
+ virtual BOOL postBuild();
+
+ static void show();
+ static void toggleVisibility();
+ static void refreshAll();
+
+protected:
+ // Reads from the gesture manager's list of active gestures
+ // and puts them in this list.
+ void buildGestureList();
+
+ static void onClickInventory(void* data);
+ static void onClickEdit(void* data);
+ static void onClickPlay(void* data);
+ static void onClickNew(void* data);
+ static void onCommitList(LLUICtrl* ctrl, void* data);
+
+protected:
+ LLUUID mSelectedID;
+
+ static LLFloaterGesture* sInstance;
+ static LLFloaterGestureObserver* sObserver;
+ static LLFloaterGestureInventoryObserver* sInventoryObserver;
+};
+
+
+#endif
diff --git a/indra/newview/llfloatergodtools.cpp b/indra/newview/llfloatergodtools.cpp
new file mode 100644
index 0000000000..7c29460f0e
--- /dev/null
+++ b/indra/newview/llfloatergodtools.cpp
@@ -0,0 +1,1446 @@
+/**
+ * @file llfloatergodtools.cpp
+ * @brief The on-screen rectangle with tool options.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergodtools.h"
+
+#include "llcoord.h"
+#include "llfontgl.h"
+#include "llframetimer.h"
+#include "llgl.h"
+#include "llhost.h"
+#include "llregionflags.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldraghandle.h"
+#include "llfloater.h"
+#include "llfocusmgr.h"
+#include "llfloatertopobjects.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llspinctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lluictrl.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llfloateravatarpicker.h"
+#include "llnotify.h"
+#include "llxfermanager.h"
+#include "llvlcomposition.h"
+#include "llsurface.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lltransfertargetfile.h"
+#include "lltransfersourcefile.h"
+
+const F32 SECONDS_BETWEEN_UPDATE_REQUESTS = 5.0f;
+
+static LLFloaterGodTools* sGodTools = NULL;
+
+//*****************************************************************************
+// LLFloaterGodTools
+//*****************************************************************************
+
+// static
+LLFloaterGodTools* LLFloaterGodTools::instance()
+{
+ if (!sGodTools)
+ {
+ sGodTools = new LLFloaterGodTools();
+ sGodTools->open();
+ sGodTools->center();
+ sGodTools->setFocus(TRUE);
+ }
+ return sGodTools;
+}
+
+
+// static
+void LLFloaterGodTools::refreshAll()
+{
+ LLFloaterGodTools* god_tools = instance();
+ if (god_tools)
+ {
+ if (gAgent.getRegionHost() != god_tools->mCurrentHost)
+ {
+ // we're in a new region
+ god_tools->sendRegionInfoRequest();
+ }
+ }
+}
+
+
+
+LLFloaterGodTools::LLFloaterGodTools()
+: LLFloater("godtools floater"),
+ mCurrentHost(LLHost::invalid),
+ mUpdateTimer()
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["grid"] = LLCallbackMap(createPanelGrid, this);
+ factory_map["region"] = LLCallbackMap(createPanelRegion, this);
+ factory_map["objects"] = LLCallbackMap(createPanelObjects, this);
+ factory_map["request"] = LLCallbackMap(createPanelRequest, this);
+ gUICtrlFactory->buildFloater(this, "floater_god_tools.xml", &factory_map);
+
+ childSetTabChangeCallback("GodTools Tabs", "grid", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "region", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "objects", onTabChanged, this);
+ childSetTabChangeCallback("GodTools Tabs", "request", onTabChanged, this);
+
+ sendRegionInfoRequest();
+
+ childShowTab("GodTools Tabs", "region");
+}
+
+// static
+void* LLFloaterGodTools::createPanelGrid(void *userdata)
+{
+ return new LLPanelGridTools("grid");
+}
+
+// static
+void* LLFloaterGodTools::createPanelRegion(void *userdata)
+{
+ LLFloaterGodTools* self = (LLFloaterGodTools*)userdata;
+ self->mPanelRegionTools = new LLPanelRegionTools("region");
+ return self->mPanelRegionTools;
+}
+
+// static
+void* LLFloaterGodTools::createPanelObjects(void *userdata)
+{
+ LLFloaterGodTools* self = (LLFloaterGodTools*)userdata;
+ self->mPanelObjectTools = new LLPanelObjectTools("objects");
+ return self->mPanelObjectTools;
+}
+
+// static
+void* LLFloaterGodTools::createPanelRequest(void *userdata)
+{
+ return new LLPanelRequestTools("region");
+}
+
+LLFloaterGodTools::~LLFloaterGodTools()
+{
+ // children automatically deleted
+}
+
+U32 LLFloaterGodTools::computeRegionFlags() const
+{
+ U32 flags = gAgent.getRegion()->getRegionFlags();
+ if (mPanelRegionTools) flags = mPanelRegionTools->computeRegionFlags(flags);
+ if (mPanelObjectTools) flags = mPanelObjectTools->computeRegionFlags(flags);
+ return flags;
+}
+
+
+void LLFloaterGodTools::updatePopup(LLCoordGL center, MASK mask)
+{
+}
+
+// virtual
+void LLFloaterGodTools::onClose(bool app_quitting)
+{
+ if (sGodTools)
+ {
+ sGodTools->setVisible(FALSE);
+ }
+}
+
+// virtual
+void LLFloaterGodTools::draw()
+{
+ if (mCurrentHost == LLHost::invalid)
+ {
+ if (mUpdateTimer.getElapsedTimeF32() > SECONDS_BETWEEN_UPDATE_REQUESTS)
+ {
+ sendRegionInfoRequest();
+ }
+ }
+ else if (gAgent.getRegionHost() != mCurrentHost)
+ {
+ sendRegionInfoRequest();
+ }
+ LLFloater::draw();
+}
+
+// static
+void LLFloaterGodTools::show(void *)
+{
+ LLFloaterGodTools* god_tools = instance();
+ god_tools->open();
+ LLPanel *panel = god_tools->childGetVisibleTab("GodTools Tabs");
+ if (panel) panel->setFocus(TRUE);
+ if (god_tools->mPanelObjectTools) god_tools->mPanelObjectTools->setTargetAvatar(LLUUID::null);
+
+ if (gAgent.getRegionHost() != god_tools->mCurrentHost)
+ {
+ // we're in a new region
+ god_tools->sendRegionInfoRequest();
+ }
+}
+
+void LLFloaterGodTools::showPanel(const LLString& panel_name)
+{
+ childShowTab("GodTools Tabs", panel_name);
+ open();
+ LLPanel *panel = childGetVisibleTab("GodTools Tabs");
+ if (panel) panel->setFocus(TRUE);
+}
+
+
+// static
+void LLFloaterGodTools::onTabChanged(void* data, bool from_click)
+{
+ LLPanel* panel = (LLPanel*)data;
+ if (panel)
+ {
+ panel->setFocus(TRUE);
+ }
+}
+
+
+// static
+void LLFloaterGodTools::processRegionInfo(LLMessageSystem* msg)
+{
+ LLHost host = msg->getSender();
+ if (host != gAgent.getRegionHost())
+ {
+ // update is for a different region than the one we're in
+ return;
+ }
+
+ //const S32 SIM_NAME_BUF = 256;
+ U32 region_flags;
+ U8 sim_access;
+ U8 agent_limit;
+ char sim_name[MAX_STRING];
+ U32 estate_id;
+ U32 parent_estate_id;
+ F32 water_height;
+ F32 billable_factor;
+ F32 object_bonus_factor;
+ F32 terrain_raise_limit;
+ F32 terrain_lower_limit;
+ S32 price_per_meter;
+ S32 redirect_grid_x;
+ S32 redirect_grid_y;
+ LLUUID cache_id;
+
+ msg->getStringFast(_PREHASH_RegionInfo, _PREHASH_SimName, MAX_STRING, sim_name);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_EstateID, estate_id);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_ParentEstateID, parent_estate_id);
+ msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_RegionFlags, region_flags);
+ msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_SimAccess, sim_access);
+ msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_MaxAgents, agent_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_ObjectBonusFactor, object_bonus_factor);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_BillableFactor, billable_factor);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, water_height);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, terrain_raise_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, terrain_lower_limit);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_PricePerMeter, price_per_meter);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridX, redirect_grid_x);
+ msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridY, redirect_grid_y);
+
+ // push values to the current LLViewerRegion
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ regionp->setRegionNameAndZone(sim_name);
+ regionp->setRegionFlags(region_flags);
+ regionp->setSimAccess(sim_access);
+ regionp->setWaterHeight(water_height);
+ regionp->setBillableFactor(billable_factor);
+ }
+
+ // push values to god tools, if available
+ if (sGodTools
+ && sGodTools->mPanelRegionTools
+ && sGodTools->mPanelObjectTools
+ && msg
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* rtool = sGodTools->mPanelRegionTools;
+ sGodTools->mCurrentHost = host;
+
+ // store locally
+ rtool->setSimName(sim_name);
+ rtool->setEstateID(estate_id);
+ rtool->setParentEstateID(parent_estate_id);
+ rtool->setCheckFlags(region_flags);
+ rtool->setBillableFactor(billable_factor);
+ rtool->setPricePerMeter(price_per_meter);
+ rtool->setRedirectGridX(redirect_grid_x);
+ rtool->setRedirectGridY(redirect_grid_y);
+ rtool->enableAllWidgets();
+
+ LLPanelObjectTools *otool = sGodTools->mPanelObjectTools;
+ otool->setCheckFlags(region_flags);
+ otool->enableAllWidgets();
+
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if ( !regionp )
+ {
+ // -1 implies non-existent
+ rtool->setGridPosX(-1);
+ rtool->setGridPosY(-1);
+ }
+ else
+ {
+ //compute the grid position of the region
+ LLVector3d global_pos = regionp->getPosGlobalFromRegion(LLVector3::zero);
+ S32 grid_pos_x = (S32) (global_pos.mdV[VX] / 256.0f);
+ S32 grid_pos_y = (S32) (global_pos.mdV[VY] / 256.0f);
+
+ rtool->setGridPosX(grid_pos_x);
+ rtool->setGridPosY(grid_pos_y);
+ }
+ }
+}
+
+
+void LLFloaterGodTools::sendRegionInfoRequest()
+{
+ if (mPanelRegionTools) mPanelRegionTools->clearAllWidgets();
+ if (mPanelObjectTools) mPanelObjectTools->clearAllWidgets();
+ mCurrentHost = LLHost::invalid;
+ mUpdateTimer.reset();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RequestRegionInfo");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+
+void LLFloaterGodTools::sendGodUpdateRegionInfo()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (gAgent.isGodlike()
+ && sGodTools->mPanelRegionTools
+ && regionp
+ && gAgent.getRegionHost() == mCurrentHost)
+ {
+ LLMessageSystem *msg = gMessageSystem;
+ LLPanelRegionTools *rtool = sGodTools->mPanelRegionTools;
+
+ msg->newMessage("GodUpdateRegionInfo");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_RegionInfo);
+ msg->addStringFast(_PREHASH_SimName, rtool->getSimName().c_str());
+ msg->addU32Fast(_PREHASH_EstateID, rtool->getEstateID());
+ msg->addU32Fast(_PREHASH_ParentEstateID, rtool->getParentEstateID());
+ msg->addU32Fast(_PREHASH_RegionFlags, computeRegionFlags());
+ msg->addF32Fast(_PREHASH_BillableFactor, rtool->getBillableFactor());
+ msg->addS32Fast(_PREHASH_PricePerMeter, rtool->getPricePerMeter());
+ msg->addS32Fast(_PREHASH_RedirectGridX, rtool->getRedirectGridX());
+ msg->addS32Fast(_PREHASH_RedirectGridY, rtool->getRedirectGridY());
+
+ gAgent.sendReliableMessage();
+ }
+}
+
+//*****************************************************************************
+// LLPanelRegionTools
+//*****************************************************************************
+
+
+// || Region |______________________________________
+// | |
+// | Sim Name: [________________________________] |
+// | ^ ^ |
+// | LEFT R1 Estate id: [----] |
+// | Parent id: [----] |
+// | [X] Prelude Grid Pos: [--] [--] |
+// | [X] Visible Redirect Pos: [--] [--] |
+// | [X] Damage Bill Factor [8_______] |
+// | [X] Block Terraform PricePerMeter[8_______] |
+// | [Apply] |
+// | |
+// | [Bake Terrain] [Select Region] |
+// | [Revert Terrain] [Autosave Now] |
+// | [Swap Terrain] |
+// | |
+// |________________________________________________|
+// ^ ^ ^
+// LEFT R2 RIGHT
+
+
+// Floats because spinners only support floats. JC
+const F32 BILLABLE_FACTOR_DEFAULT = 1;
+const F32 BILLABLE_FACTOR_MIN = 0.0f;
+const F32 BILLABLE_FACTOR_MAX = 4.f;
+
+// floats because spinners only understand floats. JC
+const F32 PRICE_PER_METER_DEFAULT = 1.f;
+const F32 PRICE_PER_METER_MIN = 0.f;
+const F32 PRICE_PER_METER_MAX = 100.f;
+
+
+LLPanelRegionTools::LLPanelRegionTools(const std::string& title)
+: LLPanel(title)
+{
+}
+
+BOOL LLPanelRegionTools::postBuild()
+{
+ childSetCommitCallback("region name", onChangeAnything, this);
+ childSetKeystrokeCallback("region name", onChangeSimName, this);
+ childSetPrevalidate("region name", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ childSetCommitCallback("check prelude", onChangePrelude, this);
+ childSetCommitCallback("check fixed sun", onChangeAnything, this);
+ childSetCommitCallback("check reset home", onChangeAnything, this);
+ childSetCommitCallback("check visible", onChangeAnything, this);
+ childSetCommitCallback("check damage", onChangeAnything, this);
+ childSetCommitCallback("block dwell", onChangeAnything, this);
+ childSetCommitCallback("block terraform", onChangeAnything, this);
+ childSetCommitCallback("allow transfer", onChangeAnything, this);
+ childSetCommitCallback("is sandbox", onChangeAnything, this);
+
+ childSetAction("Bake Terrain", onBakeTerrain, this);
+ childSetAction("Revert Terrain", onRevertTerrain, this);
+ childSetAction("Swap Terrain", onSwapTerrain, this);
+
+ childSetCommitCallback("estate", onChangeAnything, this);
+ childSetPrevalidate("estate", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("parentestate", onChangeAnything, this);
+ childSetPrevalidate("parentestate", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("parentestate");
+
+ childSetCommitCallback("gridposx", onChangeAnything, this);
+ childSetPrevalidate("gridposx", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("gridposx");
+
+ childSetCommitCallback("gridposy", onChangeAnything, this);
+ childSetPrevalidate("gridposy", &LLLineEditor::prevalidatePositiveS32);
+ childDisable("gridposy");
+
+ childSetCommitCallback("redirectx", onChangeAnything, this);
+ childSetPrevalidate("redirectx", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("redirecty", onChangeAnything, this);
+ childSetPrevalidate("redirecty", &LLLineEditor::prevalidatePositiveS32);
+
+ childSetCommitCallback("billable factor", onChangeAnything, this);
+
+ childSetCommitCallback("land cost", onChangeAnything, this);
+
+ childSetAction("Refresh", onRefresh, this);
+ childSetAction("Apply", onApplyChanges, this);
+
+ childSetAction("Select Region", onSelectRegion, this);
+ childSetAction("Autosave now", onSaveState, this);
+
+ return TRUE;
+}
+
+// Destroys the object
+LLPanelRegionTools::~LLPanelRegionTools()
+{
+ // base class will take care of everything
+}
+
+U32 LLPanelRegionTools::computeRegionFlags(U32 flags) const
+{
+ flags &= getRegionFlagsMask();
+ flags |= getRegionFlags();
+ return flags;
+}
+
+
+void LLPanelRegionTools::refresh()
+{
+}
+
+
+void LLPanelRegionTools::clearAllWidgets()
+{
+ // clear all widgets
+ childSetValue("region name", "unknown");
+ childSetFocus("region name", FALSE);
+
+ childSetValue("check prelude", FALSE);
+ childDisable("check prelude");
+
+ childSetValue("check fixed sun", FALSE);
+ childDisable("check fixed sun");
+
+ childSetValue("check reset home", FALSE);
+ childDisable("check reset home");
+
+ childSetValue("check damage", FALSE);
+ childDisable("check damage");
+
+ childSetValue("check visible", FALSE);
+ childDisable("check visible");
+
+ childSetValue("block terraform", FALSE);
+ childDisable("block terraform");
+
+ childSetValue("block dwell", FALSE);
+ childDisable("block dwell");
+
+ childSetValue("is sandbox", FALSE);
+ childDisable("is sandbox");
+
+ childSetValue("billable factor", BILLABLE_FACTOR_DEFAULT);
+ childDisable("billable factor");
+
+ childSetValue("land cost", PRICE_PER_METER_DEFAULT);
+ childDisable("land cost");
+
+ childDisable("Apply");
+ childDisable("Bake Terrain");
+ childDisable("Autosave now");
+}
+
+
+void LLPanelRegionTools::enableAllWidgets()
+{
+ // enable all of the widgets
+
+ childEnable("check prelude");
+ childEnable("check fixed sun");
+ childEnable("check reset home");
+ childEnable("check damage");
+ childDisable("check visible"); // use estates to update...
+ childEnable("block terraform");
+ childEnable("block dwell");
+ childEnable("is sandbox");
+
+ childEnable("billable factor");
+ childEnable("land cost");
+
+ childDisable("Apply"); // don't enable this one
+ childEnable("Bake Terrain");
+ childEnable("Autosave now");
+}
+
+
+// static
+void LLPanelRegionTools::onSaveState(void* userdata)
+{
+ if (gAgent.isGodlike())
+ {
+ // Send message to save world state
+ gMessageSystem->newMessageFast(_PREHASH_StateSave);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_DataBlock);
+ gMessageSystem->addStringFast(_PREHASH_Filename, NULL);
+ gAgent.sendReliableMessage();
+ }
+}
+
+const std::string LLPanelRegionTools::getSimName() const
+{
+ return childGetValue("region name");
+}
+
+U32 LLPanelRegionTools::getEstateID() const
+{
+ U32 id = (U32)childGetValue("estate").asInteger();
+ return id;
+}
+
+U32 LLPanelRegionTools::getParentEstateID() const
+{
+ U32 id = (U32)childGetValue("parentestate").asInteger();
+ return id;
+}
+
+S32 LLPanelRegionTools::getRedirectGridX() const
+{
+ return childGetValue("redirectx").asInteger();
+}
+
+S32 LLPanelRegionTools::getRedirectGridY() const
+{
+ return childGetValue("redirecty").asInteger();
+}
+
+S32 LLPanelRegionTools::getGridPosX() const
+{
+ return childGetValue("gridposx").asInteger();
+}
+
+S32 LLPanelRegionTools::getGridPosY() const
+{
+ return childGetValue("gridposy").asInteger();
+}
+
+U32 LLPanelRegionTools::getRegionFlags() const
+{
+ U32 flags = 0x0;
+ flags = childGetValue("check prelude").asBoolean()
+ ? set_prelude_flags(flags)
+ : unset_prelude_flags(flags);
+
+ // override prelude
+ if (childGetValue("check fixed sun").asBoolean())
+ {
+ flags |= REGION_FLAGS_SUN_FIXED;
+ }
+ if (childGetValue("check reset home").asBoolean())
+ {
+ flags |= REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ if (childGetValue("check visible").asBoolean())
+ {
+ flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+ if (childGetValue("check damage").asBoolean())
+ {
+ flags |= REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ if (childGetValue("block terraform").asBoolean())
+ {
+ flags |= REGION_FLAGS_BLOCK_TERRAFORM;
+ }
+ if (childGetValue("block dwell").asBoolean())
+ {
+ flags |= REGION_FLAGS_BLOCK_DWELL;
+ }
+ if (childGetValue("is sandbox").asBoolean())
+ {
+ flags |= REGION_FLAGS_SANDBOX;
+ }
+ return flags;
+}
+
+U32 LLPanelRegionTools::getRegionFlagsMask() const
+{
+ U32 flags = 0xffffffff;
+ flags = childGetValue("check prelude").asBoolean()
+ ? set_prelude_flags(flags)
+ : unset_prelude_flags(flags);
+
+ if (!childGetValue("check fixed sun").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_SUN_FIXED;
+ }
+ if (!childGetValue("check reset home").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ if (!childGetValue("check visible").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+ if (!childGetValue("check damage").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ if (!childGetValue("block terraform").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_BLOCK_TERRAFORM;
+ }
+ if (!childGetValue("block dwell").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_BLOCK_DWELL;
+ }
+ if (!childGetValue("is sandbox").asBoolean())
+ {
+ flags &= ~REGION_FLAGS_SANDBOX;
+ }
+ return flags;
+}
+
+F32 LLPanelRegionTools::getBillableFactor() const
+{
+ return (F32)childGetValue("billable factor").asReal();
+}
+
+S32 LLPanelRegionTools::getPricePerMeter() const
+{
+ return childGetValue("land cost");
+}
+
+void LLPanelRegionTools::setSimName(char *name)
+{
+ childSetValue("region name", name);
+}
+
+void LLPanelRegionTools::setEstateID(U32 id)
+{
+ childSetValue("estate", (S32)id);
+}
+
+void LLPanelRegionTools::setGridPosX(S32 pos)
+{
+ childSetValue("gridposx", pos);
+}
+
+void LLPanelRegionTools::setGridPosY(S32 pos)
+{
+ childSetValue("gridposy", pos);
+}
+
+void LLPanelRegionTools::setRedirectGridX(S32 pos)
+{
+ childSetValue("redirectx", pos);
+}
+
+void LLPanelRegionTools::setRedirectGridY(S32 pos)
+{
+ childSetValue("redirecty", pos);
+}
+
+void LLPanelRegionTools::setParentEstateID(U32 id)
+{
+ childSetValue("parentestate", (S32)id);
+}
+
+void LLPanelRegionTools::setCheckFlags(U32 flags)
+{
+ childSetValue("check prelude", is_prelude(flags) ? TRUE : FALSE);
+ childSetValue("check fixed sun", flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE);
+ childSetValue("check reset home", flags & REGION_FLAGS_RESET_HOME_ON_TELEPORT ? TRUE : FALSE);
+ childSetValue("check damage", flags & REGION_FLAGS_ALLOW_DAMAGE ? TRUE : FALSE);
+ childSetValue("check visible", flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE);
+ childSetValue("block terraform", flags & REGION_FLAGS_BLOCK_TERRAFORM ? TRUE : FALSE);
+ childSetValue("block dwell", flags & REGION_FLAGS_BLOCK_DWELL ? TRUE : FALSE);
+ childSetValue("is sandbox", flags & REGION_FLAGS_SANDBOX ? TRUE : FALSE );
+}
+
+void LLPanelRegionTools::setBillableFactor(F32 billable_factor)
+{
+ childSetValue("billable factor", billable_factor);
+}
+
+void LLPanelRegionTools::setPricePerMeter(S32 price)
+{
+ childSetValue("land cost", price);
+}
+
+// static
+void LLPanelRegionTools::onChangeAnything(LLUICtrl* ctrl, void* userdata)
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+ region_tools->childEnable("Apply");
+ }
+}
+
+// static
+void LLPanelRegionTools::onChangePrelude(LLUICtrl* ctrl, void* data)
+{
+ // checking prelude auto-checks fixed sun
+ LLPanelRegionTools* self = (LLPanelRegionTools*)data;
+ if (self->childGetValue("check prelude").asBoolean())
+ {
+ self->childSetValue("check fixed sun", TRUE);
+ self->childSetValue("check reset home", TRUE);
+ }
+ // pass on to default onChange handler
+ onChangeAnything(ctrl, data);
+}
+
+// static
+void LLPanelRegionTools::onChangeSimName(LLLineEditor* caller, void* userdata )
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+ region_tools->childEnable("Apply");
+ }
+}
+
+//static
+void LLPanelRegionTools::onRefresh(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && gAgent.isGodlike())
+ {
+ sGodTools->sendRegionInfoRequest();
+ }
+}
+
+// static
+void LLPanelRegionTools::onApplyChanges(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelRegionTools* region_tools = (LLPanelRegionTools*) userdata;
+
+ region_tools->childDisable("Apply");
+ sGodTools->sendGodUpdateRegionInfo();
+ }
+}
+
+// static
+void LLPanelRegionTools::onBakeTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "bake", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onRevertTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "revert", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onSwapTerrain(void *userdata)
+{
+ LLPanelRequestTools::sendRequest("terrain", "swap", gAgent.getRegionHost());
+}
+
+// static
+void LLPanelRegionTools::onSelectRegion(void* userdata)
+{
+ llinfos << "LLPanelRegionTools::onSelectRegion" << llendl;
+
+ if (!gWorldp)
+ {
+ return;
+ }
+ LLViewerRegion *regionp = gWorldp->getRegionFromPosGlobal(gAgent.getPositionGlobal());
+ if (!regionp)
+ {
+ return;
+ }
+
+ LLVector3d north_east(REGION_WIDTH_METERS, REGION_WIDTH_METERS, 0);
+ gParcelMgr->selectLand(regionp->getOriginGlobal(),
+ regionp->getOriginGlobal() + north_east, FALSE);
+
+}
+
+
+//*****************************************************************************
+// Class LLPanelGridTools
+//*****************************************************************************
+
+// || Grid |_____________________________________
+// | |
+// | |
+// | Sun Phase: >--------[]---------< [________] |
+// | |
+// | ^ ^ |
+// | LEFT R1 |
+// | |
+// | [Kick all users] |
+// | |
+// | |
+// | |
+// | |
+// | |
+// |_______________________________________________|
+// ^ ^ ^
+// LEFT R2 RIGHT
+
+const F32 HOURS_TO_RADIANS = (2.f*F_PI)/24.f;
+const char FLOATER_GRID_ADMIN_TITLE[] = "Grid Administration";
+
+
+LLPanelGridTools::LLPanelGridTools(const std::string& name) :
+ LLPanel(name)
+{
+}
+
+// Destroys the object
+LLPanelGridTools::~LLPanelGridTools()
+{
+}
+
+BOOL LLPanelGridTools::postBuild()
+{
+ childSetAction("Kick all users", onClickKickAll, this);
+ childSetAction("Flush This Region's Map Visibility Caches", onClickFlushMapVisibilityCaches, this);
+
+ return TRUE;
+}
+
+void LLPanelGridTools::refresh()
+{
+}
+
+
+// static
+void LLPanelGridTools::onClickKickAll(void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect(left, top, left+400, top-300);
+
+ gViewerWindow->alertXmlEditText("KickAllUsers", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelGridTools::confirmKick, self);
+}
+
+
+void LLPanelGridTools::confirmKick(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ if (option == 0)
+ {
+ self->mKickMessage = text;
+ gViewerWindow->alertXml("ConfirmKick",LLPanelGridTools::finishKick, self);
+ }
+}
+
+
+// static
+void LLPanelGridTools::finishKick(S32 option, void* userdata)
+{
+ LLPanelGridTools* self = (LLPanelGridTools*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, LL_UUID_ALL_AGENTS );
+ msg->addU32("KickFlags", KICK_FLAGS_DEFAULT );
+ msg->addStringFast(_PREHASH_Reason, self->mKickMessage.c_str() );
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+// static
+void LLPanelGridTools::onClickFlushMapVisibilityCaches(void* data)
+{
+ gViewerWindow->alertXml("FlushMapVisibilityCaches",
+ flushMapVisibilityCachesConfirm, data);
+}
+
+// static
+void LLPanelGridTools::flushMapVisibilityCachesConfirm(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelGridTools* self = (LLPanelGridTools*)data;
+ if (!self) return;
+
+ // HACK: Send this as an EstateOwnerRequest so it gets routed
+ // correctly by the spaceserver. JC
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "refreshmapvisibility");
+ msg->addUUID("Invoice", LLUUID::null);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", gAgent.getID().getString());
+ gAgent.sendReliableMessage();
+}
+
+
+//*****************************************************************************
+// LLPanelObjectTools
+//*****************************************************************************
+
+
+// || Object |_______________________________________________________
+// | |
+// | Sim Name: Foo |
+// | ^ ^ |
+// | LEFT R1 |
+// | |
+// | [X] Disable Scripts [X] Disable Collisions [X] Disable Physics |
+// | [ Apply ] |
+// | |
+// | [Set Target Avatar] Avatar Name |
+// | [Delete Target's Objects on Public Land ] |
+// | [Delete All Target's Objects ] |
+// | [Delete All Scripted Objects on Public Land] |
+// | [Get Top Colliders ] |
+// | [Get Top Scripts ] |
+// |_________________________________________________________________|
+// ^ ^
+// LEFT RIGHT
+
+// Default constructor
+LLPanelObjectTools::LLPanelObjectTools(const std::string& title)
+: LLPanel(title), mTargetAvatar()
+{
+}
+
+// Destroys the object
+LLPanelObjectTools::~LLPanelObjectTools()
+{
+ // base class will take care of everything
+}
+
+BOOL LLPanelObjectTools::postBuild()
+{
+ childSetCommitCallback("disable scripts", onChangeAnything, this);
+ childSetCommitCallback("disable collisions", onChangeAnything, this);
+ childSetCommitCallback("disable physics", onChangeAnything, this);
+
+ childSetAction("Apply", onApplyChanges, this);
+
+ childSetAction("Set Target", onClickSet, this);
+
+ childSetAction("Delete Target's Scripted Objects On Others Land", onClickDeletePublicOwnedBy, this);
+ childSetAction("Delete Target's Scripted Objects On *Any* Land", onClickDeleteAllScriptedOwnedBy, this);
+ childSetAction("Delete *ALL* Of Target's Objects", onClickDeleteAllOwnedBy, this);
+
+ childSetAction("Get Top Colliders", onGetTopColliders, this);
+ childSetAction("Get Top Scripts", onGetTopScripts, this);
+ childSetAction("Scripts digest", onGetScriptDigest, this);
+
+ return TRUE;
+}
+
+void LLPanelObjectTools::setTargetAvatar(const LLUUID &target_id)
+{
+ mTargetAvatar = target_id;
+ if (target_id.isNull())
+ {
+ childSetValue("target_avatar_name", "(no target)");
+ }
+}
+
+
+void LLPanelObjectTools::refresh()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ childSetText("region name", regionp->getName());
+ }
+}
+
+
+U32 LLPanelObjectTools::computeRegionFlags(U32 flags) const
+{
+ if (childGetValue("disable scripts").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_SCRIPTS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_SCRIPTS;
+ }
+ if (childGetValue("disable collisions").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_COLLISIONS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_COLLISIONS;
+ }
+ if (childGetValue("disable physics").asBoolean())
+ {
+ flags |= REGION_FLAGS_SKIP_PHYSICS;
+ }
+ else
+ {
+ flags &= ~REGION_FLAGS_SKIP_PHYSICS;
+ }
+ return flags;
+}
+
+
+void LLPanelObjectTools::setCheckFlags(U32 flags)
+{
+ childSetValue("disable scripts", flags & REGION_FLAGS_SKIP_SCRIPTS ? TRUE : FALSE);
+ childSetValue("disable collisions", flags & REGION_FLAGS_SKIP_COLLISIONS ? TRUE : FALSE);
+ childSetValue("disable physics", flags & REGION_FLAGS_SKIP_PHYSICS ? TRUE : FALSE);
+}
+
+
+void LLPanelObjectTools::clearAllWidgets()
+{
+ childSetValue("disable scripts", FALSE);
+ childDisable("disable scripts");
+
+ childDisable("Apply");
+ childDisable("Set Target");
+ childDisable("Delete Target's Scripted Objects On Others Land");
+ childDisable("Delete Target's Scripted Objects On *Any* Land");
+ childDisable("Delete *ALL* Of Target's Objects");
+}
+
+
+void LLPanelObjectTools::enableAllWidgets()
+{
+ childEnable("disable scripts");
+
+ childDisable("Apply"); // don't enable this one
+ childEnable("Set Target");
+ childEnable("Delete Target's Scripted Objects On Others Land");
+ childEnable("Delete Target's Scripted Objects On *Any* Land");
+ childEnable("Delete *ALL* Of Target's Objects");
+ childEnable("Get Top Colliders");
+ childEnable("Get Top Scripts");
+}
+
+
+// static
+void LLPanelObjectTools::onGetTopColliders(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ LLFloaterTopObjects::show();
+ LLFloaterTopObjects::setMode(STAT_REPORT_TOP_COLLIDERS);
+ LLFloaterTopObjects::onRefresh(NULL);
+ }
+}
+
+// static
+void LLPanelObjectTools::onGetTopScripts(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ LLFloaterTopObjects::show();
+ LLFloaterTopObjects::setMode(STAT_REPORT_TOP_SCRIPTS);
+ LLFloaterTopObjects::onRefresh(NULL);
+ }
+}
+
+// static
+void LLPanelObjectTools::onGetScriptDigest(void* userdata)
+{
+ if (sGodTools
+ && gAgent.isGodlike())
+ {
+ // get the list of scripts and number of occurences of each
+ // (useful for finding self-replicating objects)
+ LLPanelRequestTools::sendRequest("scriptdigest","0",gAgent.getRegionHost());
+ }
+}
+
+void LLPanelObjectTools::onClickDeletePublicOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags =
+ SWD_SCRIPTED_ONLY | SWD_OTHERS_LAND_ONLY;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllScriptedPublicObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::onClickDeleteAllScriptedOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags = SWD_SCRIPTED_ONLY;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllScriptedObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::onClickDeleteAllOwnedBy(void* userdata)
+{
+ // Bring up view-modal dialog
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*)userdata;
+ if (!panelp->mTargetAvatar.isNull())
+ {
+ panelp->mSimWideDeletesFlags = 0;
+
+ LLStringBase<char>::format_map_t args;
+ args["[AVATAR_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+
+ gViewerWindow->alertXml( "GodDeleteAllObjectsByUser",
+ args,
+ callbackSimWideDeletes,
+ userdata);
+ }
+}
+
+// static
+void LLPanelObjectTools::callbackSimWideDeletes( S32 option, void* userdata )
+{
+ if (option == 0)
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ if (!object_tools->mTargetAvatar.isNull())
+ {
+ send_sim_wide_deletes(object_tools->mTargetAvatar,
+ object_tools->mSimWideDeletesFlags);
+ }
+ }
+}
+
+void LLPanelObjectTools::onClickSet(void* data)
+{
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*) data;
+ // grandparent is a floater, which can have a dependent
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data));
+}
+
+void LLPanelObjectTools::onClickSetBySelection(void* data)
+{
+ LLPanelObjectTools* panelp = (LLPanelObjectTools*) data;
+ if (!panelp) return;
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) node = gSelectMgr->getFirstNode();
+ if (!node) return;
+
+ LLString owner_name;
+ LLUUID owner_id;
+ gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ panelp->mTargetAvatar = owner_id;
+ LLString name = "Object " + node->mName + " owned by " + owner_name;
+ panelp->childSetValue("target_avatar_name", name);
+}
+
+// static
+void LLPanelObjectTools::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) data;
+ if (ids.empty() || names.empty()) return;
+ object_tools->mTargetAvatar = ids[0];
+ object_tools->childSetValue("target_avatar_name", names[0]);
+ object_tools->refresh();
+}
+
+
+// static
+void LLPanelObjectTools::onChangeAnything(LLUICtrl* ctrl, void* userdata)
+{
+ if (sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ object_tools->childEnable("Apply");
+ }
+}
+
+// static
+void LLPanelObjectTools::onApplyChanges(void* userdata)
+{
+ LLViewerRegion *region = gAgent.getRegion();
+ if (region
+ && sGodTools
+ && userdata
+ && gAgent.isGodlike())
+ {
+ LLPanelObjectTools* object_tools = (LLPanelObjectTools*) userdata;
+ // TODO -- implement this
+
+ object_tools->childDisable("Apply");
+ sGodTools->sendGodUpdateRegionInfo();
+ }
+}
+
+
+// --------------------
+// LLPanelRequestTools
+// --------------------
+
+const char SELECTION[] = "Selection";
+const char AGENT_REGION[] = "Agent Region";
+
+LLPanelRequestTools::LLPanelRequestTools(const std::string& name):
+ LLPanel(name)
+{
+}
+
+LLPanelRequestTools::~LLPanelRequestTools()
+{
+}
+
+BOOL LLPanelRequestTools::postBuild()
+{
+ childSetAction("Make Request", onClickRequest, this);
+
+ refresh();
+
+ return TRUE;
+}
+
+void LLPanelRequestTools::refresh()
+{
+ std::string buffer = childGetValue("destination");
+ LLCtrlListInterface *list = childGetListInterface("destination");
+ if (!list) return;
+
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ list->addSimpleElement(SELECTION);
+ list->addSimpleElement(AGENT_REGION);
+ LLViewerRegion* regionp;
+ for(regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp != NULL;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ LLString name = regionp->getName();
+ if (!name.empty())
+ {
+ list->addSimpleElement(name);
+ }
+ }
+ if(!buffer.empty())
+ {
+ list->selectByValue(buffer);
+ }
+ else
+ {
+ list->selectByValue(SELECTION);
+ }
+}
+
+
+// static
+void LLPanelRequestTools::sendRequest(const char *request,
+ const char *parameter,
+ const LLHost& host)
+{
+ llinfos << "Sending request '" << request << "', '"
+ << parameter << "' to " << host << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GodlikeMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", request);
+ msg->addUUID("Invoice", LLUUID::null);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", parameter);
+ msg->sendReliable(host);
+}
+
+// static
+void LLPanelRequestTools::onClickRequest(void* data)
+{
+ LLPanelRequestTools* self = (LLPanelRequestTools*)data;
+ const std::string dest = self->childGetValue("destination").asString();
+ if(dest == SELECTION)
+ {
+ std::string req = self->childGetValue("request");
+ req = req.substr(0, req.find_first_of(" "));
+ std::string param = self->childGetValue("parameter");
+ gSelectMgr->sendGodlikeRequest(req, param);
+ }
+ else if(dest == AGENT_REGION)
+ {
+ self->sendRequest(gAgent.getRegionHost());
+ }
+ else
+ {
+ // find region by name
+ LLViewerRegion* regionp;
+ for(regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp != NULL;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ if(dest == regionp->getName())
+ {
+ // found it
+ self->sendRequest(regionp->getHost());
+ }
+ }
+ }
+}
+
+void terrain_download_done(void** data, S32 status)
+{
+ LLNotifyBox::showXml("TerrainDownloaded");
+}
+
+
+void test_callback(const LLTSCode status)
+{
+ llinfos << "Test transfer callback returned!" << llendl;
+}
+
+
+void LLPanelRequestTools::sendRequest(const LLHost& host)
+{
+
+ // intercept viewer local actions here
+ std::string req = childGetValue("request");
+ if (req == "terrain download")
+ {
+ gXferManager->requestFile("terrain.raw", "terrain.raw", LL_PATH_NONE,
+ host,
+ FALSE,
+ terrain_download_done,
+ NULL);
+ }
+ else
+ {
+ req = req.substr(0, req.find_first_of(" "));
+ sendRequest(req.c_str(), childGetValue("parameter").asString().c_str(), host);
+ }
+}
+
+// Flags are SWD_ flags.
+void send_sim_wide_deletes(const LLUUID& owner_id, U32 flags)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SimWideDeletes);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_DataBlock);
+ msg->addUUIDFast(_PREHASH_TargetID, owner_id);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ gAgent.sendReliableMessage();
+}
diff --git a/indra/newview/llfloatergodtools.h b/indra/newview/llfloatergodtools.h
new file mode 100644
index 0000000000..f6034aee39
--- /dev/null
+++ b/indra/newview/llfloatergodtools.h
@@ -0,0 +1,262 @@
+/**
+ * @file llfloatergodtools.h
+ * @brief The on-screen rectangle with tool options.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERGODTOOLS_H
+#define LL_LLFLOATERGODTOOLS_H
+
+#include "llcoord.h"
+#include "llhost.h"
+#include "llframetimer.h"
+
+#include "llfloater.h"
+#include "llpanel.h"
+#include <vector>
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLUICtrl;
+class LLLineEditor;
+class LLPanelGridTools;
+class LLPanelRegionTools;
+class LLPanelObjectTools;
+class LLPanelRequestTools;
+//class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTabContainer;
+class LLTextBox;
+class LLMessageSystem;
+
+class LLFloaterGodTools
+: public LLFloater
+{
+public:
+
+ static LLFloaterGodTools* instance();
+
+ enum EGodPanel
+ {
+ PANEL_GRID,
+ PANEL_REGION,
+ PANEL_OBJECT,
+ PANEL_REQUEST,
+ PANEL_COUNT
+ };
+
+ static void show(void *);
+ static void hide(void *);
+
+ static void* createPanelGrid(void *userdata);
+ static void* createPanelRegion(void *userdata);
+ static void* createPanelObjects(void *userdata);
+ static void* createPanelRequest(void *userdata);
+
+ static void refreshAll();
+
+ void showPanel(const LLString& panel_name);
+
+ virtual void onClose(bool app_quitting);
+
+ virtual void draw();
+
+ // call this once per frame to handle visibility, rect location,
+ // button highlights, etc.
+ void updatePopup(LLCoordGL center, MASK mask);
+
+ // Get data to populate UI.
+ void sendRegionInfoRequest();
+
+ // get and process region info if necessary.
+ static void processRegionInfo(LLMessageSystem* msg);
+
+ // Send possibly changed values to simulator.
+ void sendGodUpdateRegionInfo();
+
+ static void onTabChanged(void *data, bool from_click);
+
+protected:
+ U32 computeRegionFlags() const;
+
+protected:
+ LLFloaterGodTools();
+ ~LLFloaterGodTools();
+
+ void setStatusText(const std::string& text);
+
+ // When the floater is going away, reset any options that need to be
+ // cleared.
+ void resetToolState();
+
+ static LLFloaterGodTools* sInstance;
+
+public:
+ LLPanelRegionTools *mPanelRegionTools;
+ LLPanelObjectTools *mPanelObjectTools;
+
+ LLHost mCurrentHost;
+ LLFrameTimer mUpdateTimer;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelRegionTools
+//-----------------------------------------------------------------------------
+
+class LLPanelRegionTools
+: public LLPanel
+{
+public:
+ LLPanelRegionTools(const std::string& name);
+ /*virtual*/ ~LLPanelRegionTools();
+
+ BOOL postBuild();
+
+ /*virtual*/ void refresh();
+
+ static void onSaveState(void* data);
+ static void onChangeAnything(LLUICtrl* ctrl, void* userdata);
+ static void onChangePrelude(LLUICtrl* ctrl, void* data);
+ static void onChangeSimName(LLLineEditor* caller, void* userdata);
+ static void onApplyChanges(void* userdata);
+ static void onBakeTerrain(void *userdata);
+ static void onRevertTerrain(void *userdata);
+ static void onSwapTerrain(void *userdata);
+ static void onSelectRegion(void *userdata);
+ static void onRefresh(void* userdata);
+
+ // set internal checkboxes/spinners/combos
+ const std::string getSimName() const;
+ U32 getEstateID() const;
+ U32 getParentEstateID() const;
+ U32 getRegionFlags() const;
+ U32 getRegionFlagsMask() const;
+ F32 getBillableFactor() const;
+ S32 getPricePerMeter() const;
+ S32 getGridPosX() const;
+ S32 getGridPosY() const;
+ S32 getRedirectGridX() const;
+ S32 getRedirectGridY() const;
+
+ // set internal checkboxes/spinners/combos
+ void setSimName(char *name);
+ void setEstateID(U32 id);
+ void setParentEstateID(U32 id);
+ void setCheckFlags(U32 flags);
+ void setBillableFactor(F32 billable_factor);
+ void setPricePerMeter(S32 price);
+ void setGridPosX(S32 pos);
+ void setGridPosY(S32 pos);
+ void setRedirectGridX(S32 pos);
+ void setRedirectGridY(S32 pos);
+
+ U32 computeRegionFlags(U32 initial_flags) const;
+ void clearAllWidgets();
+ void enableAllWidgets();
+
+protected:
+ // gets from internal checkboxes/spinners/combos
+ void updateCurrentRegion() const;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelGridTools
+//-----------------------------------------------------------------------------
+
+class LLPanelGridTools
+: public LLPanel
+{
+public:
+ LLPanelGridTools(const std::string& name);
+ virtual ~LLPanelGridTools();
+
+ BOOL postBuild();
+
+ void refresh();
+
+ static void onClickKickAll(void *data);
+ static void confirmKick(S32 option, const LLString& text, void* userdata);
+ static void finishKick(S32 option, void* userdata);
+ static void onDragSunPhase(LLUICtrl *ctrl, void *userdata);
+ static void onClickFlushMapVisibilityCaches(void* data);
+ static void flushMapVisibilityCachesConfirm(S32 option, void* data);
+
+protected:
+ LLString mKickMessage; // Message to send on kick
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelObjectTools
+//-----------------------------------------------------------------------------
+
+class LLPanelObjectTools
+: public LLPanel
+{
+public:
+ LLPanelObjectTools(const std::string& name);
+ /*virtual*/ ~LLPanelObjectTools();
+
+ BOOL postBuild();
+
+ /*virtual*/ void refresh();
+
+ void setTargetAvatar(const LLUUID& target_id);
+ U32 computeRegionFlags(U32 initial_flags) const;
+ void clearAllWidgets();
+ void enableAllWidgets();
+ void setCheckFlags(U32 flags);
+
+ static void onChangeAnything(LLUICtrl* ctrl, void* data);
+ static void onApplyChanges(void* data);
+ static void onClickSet(void* data);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void onClickDeletePublicOwnedBy(void* data);
+ static void onClickDeleteAllScriptedOwnedBy(void* data);
+ static void onClickDeleteAllOwnedBy(void* data);
+ static void callbackSimWideDeletes(S32 option, void* userdata);
+ static void onGetTopColliders(void* data);
+ static void onGetTopScripts(void* data);
+ static void onGetScriptDigest(void* data);
+ static void onClickSetBySelection(void* data);
+
+protected:
+ LLUUID mTargetAvatar;
+
+ // For all delete dialogs, store flags here for message.
+ U32 mSimWideDeletesFlags;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLPanelRequestTools
+//-----------------------------------------------------------------------------
+
+class LLPanelRequestTools : public LLPanel
+{
+public:
+ LLPanelRequestTools(const std::string& name);
+ /*virtual*/ ~LLPanelRequestTools();
+
+ BOOL postBuild();
+
+ void refresh();
+
+ static void sendRequest(const char* request,
+ const char* parameter,
+ const LLHost& host);
+
+protected:
+ static void onClickRequest(void *data);
+ void sendRequest(const LLHost& host);
+};
+
+// Flags are SWD_ flags.
+void send_sim_wide_deletes(const LLUUID& owner_id, U32 flags);
+
+#endif
diff --git a/indra/newview/llfloatergroupinvite.cpp b/indra/newview/llfloatergroupinvite.cpp
new file mode 100644
index 0000000000..b21711a96f
--- /dev/null
+++ b/indra/newview/llfloatergroupinvite.cpp
@@ -0,0 +1,118 @@
+/**
+ * @file llfloatergroupinvite.cpp
+ * @brief Floater to invite new members into a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergroupinvite.h"
+#include "llpanelgroupinvite.h"
+
+const char FLOATER_TITLE[] = "Group Invitation";
+const LLRect FGI_RECT(0, 380, 210, 0);
+
+class LLFloaterGroupInvite::impl
+{
+public:
+ impl(const LLUUID& group_id);
+ ~impl();
+
+ static void closeFloater(void* data);
+
+public:
+ LLUUID mGroupID;
+ LLPanelGroupInvite* mInvitePanelp;
+
+ static std::map<LLUUID, LLFloaterGroupInvite*> sInstances;
+};
+
+//
+// Globals
+//
+std::map<LLUUID, LLFloaterGroupInvite*> LLFloaterGroupInvite::impl::sInstances;
+
+LLFloaterGroupInvite::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+}
+
+LLFloaterGroupInvite::impl::~impl()
+{
+}
+
+//static
+void LLFloaterGroupInvite::impl::closeFloater(void* data)
+{
+ LLFloaterGroupInvite* floaterp = (LLFloaterGroupInvite*) data;
+
+ if ( floaterp ) floaterp->close();
+}
+
+//-----------------------------------------------------------------------------
+// Implementation
+//-----------------------------------------------------------------------------
+LLFloaterGroupInvite::LLFloaterGroupInvite(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ const LLUUID& group_id)
+: LLFloater(name, rect, title)
+{
+ LLRect contents(mRect);
+ contents.mTop -= LLFLOATER_HEADER_SIZE;
+
+ mImpl = new impl(group_id);
+
+ mImpl->mInvitePanelp = new LLPanelGroupInvite("Group Invite Panel",
+ group_id);
+
+ mImpl->mInvitePanelp->setCloseCallback(impl::closeFloater, this);
+
+ mImpl->mInvitePanelp->setRect(contents);
+ addChild(mImpl->mInvitePanelp);
+}
+
+// virtual
+LLFloaterGroupInvite::~LLFloaterGroupInvite()
+{
+ if (mImpl->mGroupID.notNull())
+ {
+ impl::sInstances.erase(mImpl->mGroupID);
+ }
+
+ delete mImpl->mInvitePanelp;
+ delete mImpl;
+}
+
+// static
+void LLFloaterGroupInvite::showForGroup(const LLUUID& group_id)
+{
+ // Make sure group_id isn't null
+ if (group_id.isNull())
+ {
+ llwarns << "LLFloaterGroupInvite::showForGroup with null group_id!" << llendl;
+ return;
+ }
+
+ // If we don't have a floater for this group, create one.
+ LLFloaterGroupInvite *fgi = get_if_there(impl::sInstances,
+ group_id,
+ (LLFloaterGroupInvite*)NULL);
+ if (!fgi)
+ {
+ fgi = new LLFloaterGroupInvite("groupinfo",
+ FGI_RECT,
+ FLOATER_TITLE,
+ group_id);
+
+ impl::sInstances[group_id] = fgi;
+
+ fgi->mImpl->mInvitePanelp->clear();
+ }
+
+ fgi->center();
+ fgi->open();
+ fgi->mImpl->mInvitePanelp->update();
+}
diff --git a/indra/newview/llfloatergroupinvite.h b/indra/newview/llfloatergroupinvite.h
new file mode 100644
index 0000000000..fb47654476
--- /dev/null
+++ b/indra/newview/llfloatergroupinvite.h
@@ -0,0 +1,34 @@
+/**
+ * @file llfloatergroupinvite.h
+ * @brief This floater is just a wrapper for LLPanelGroupInvite, which
+ * is used to invite members to a specific group
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERGROUPINVITE_H
+#define LL_LLFLOATERGROUPINVITE_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+
+class LLFloaterGroupInvite
+: public LLFloater
+{
+public:
+ virtual ~LLFloaterGroupInvite();
+
+ static void showForGroup(const LLUUID &group_id);
+
+protected:
+ LLFloaterGroupInvite(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ const LLUUID& group_id = LLUUID::null);
+
+ class impl;
+ impl* mImpl;
+};
+
+#endif
diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp
new file mode 100644
index 0000000000..f03d0735a4
--- /dev/null
+++ b/indra/newview/llfloatergroups.cpp
@@ -0,0 +1,454 @@
+/**
+ * @file llfloatergroups.cpp
+ * @brief LLFloaterGroups class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shown from Edit -> Groups...
+ * Shows the agent's groups and allows the edit window to be invoked.
+ * Also overloaded to allow picking of a single group for assigning
+ * objects and land to groups.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatergroups.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterdirectory.h"
+#include "llfocusmgr.h"
+#include "llalertdialog.h"
+#include "llselectmgr.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+const LLRect FLOATER_RECT(0, 258, 280, 0);
+const char FLOATER_TITLE[] = "Groups";
+
+// static
+LLMap<const LLUUID, LLFloaterGroups*> LLFloaterGroups::sInstances;
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterGroups
+///----------------------------------------------------------------------------
+
+//LLEventListener
+//virtual
+bool LLFloaterGroups::handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+{
+ if (event->desc() == "new group")
+ {
+ reset();
+ return true;
+ }
+
+ return LLView::handleEvent(event, userdata);
+}
+
+// Call this with an agent id and AGENT_GROUPS for an agent's
+// groups, otherwise, call with an object id and SET_OBJECT_GROUP
+// when modifying an object.
+// static
+LLFloaterGroups* LLFloaterGroups::show(const LLUUID& id, EGroupDialog type)
+{
+ LLFloaterGroups* instance = NULL;
+ if(sInstances.checkData(id))
+ {
+ instance = sInstances.getData(id);
+ if (instance->getType() != type)
+ {
+ // not the type we want ==> destroy it and rebuild below
+ instance->destroy();
+ instance = NULL;
+ }
+ else
+ {
+ // Move the existing view to the front
+ instance->open();
+ }
+ }
+
+ if (!instance)
+ {
+ S32 left = 0;
+ S32 top = 0;
+ LLRect rect = FLOATER_RECT;
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ instance = new LLFloaterGroups("groups", rect, FLOATER_TITLE, id);
+ if(instance)
+ {
+ sInstances.addData(id, instance);
+ //instance->init(type);
+ instance->mType = type;
+ switch (type)
+ {
+ case AGENT_GROUPS:
+ gUICtrlFactory->buildFloater(instance, "floater_groups.xml");
+ break;
+ case CHOOSE_ONE:
+ gUICtrlFactory->buildFloater(instance, "floater_choose_group.xml");
+ break;
+ }
+ instance->center();
+ instance->open();
+ }
+ }
+ return instance;
+}
+
+// static
+LLFloaterGroups* LLFloaterGroups::getInstance(const LLUUID& id)
+{
+ if(sInstances.checkData(id))
+ {
+ return sInstances.getData(id);
+ }
+ return NULL;
+}
+
+// Default constructor
+LLFloaterGroups::LLFloaterGroups(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& id) :
+ LLFloater(name, rect, title),
+ mID(id),
+ mType(AGENT_GROUPS),
+ mOKCallback(NULL),
+ mCallbackUserdata(NULL)
+{
+}
+
+// Destroys the object
+LLFloaterGroups::~LLFloaterGroups()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstances.removeData(mID);
+}
+
+// clear the group list, and get a fresh set of info.
+void LLFloaterGroups::reset()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ if (group_list)
+ {
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+
+ initAgentGroups(gAgent.getGroupID());
+ enableButtons();
+}
+
+void LLFloaterGroups::setOkCallback(void (*callback)(LLUUID, void*),
+ void* userdata)
+{
+ mOKCallback = callback;
+ mCallbackUserdata = userdata;
+}
+
+BOOL LLFloaterGroups::postBuild()
+{
+ childSetCommitCallback("group list", onGroupList, this);
+
+ if(mType == AGENT_GROUPS)
+ {
+ childSetTextArg("groupcount", "[COUNT]", llformat("%d",gAgent.mGroups.count()));
+ childSetTextArg("groupcount", "[MAX]", llformat("%d",MAX_AGENT_GROUPS));
+
+ initAgentGroups(gAgent.getGroupID());
+
+ childSetAction("Activate", onBtnActivate, this);
+
+ childSetAction("Info", onBtnInfo, this);
+
+ childSetAction("Leave", onBtnLeave, this);
+
+ childSetAction("Create", onBtnCreate, this);
+
+ childSetAction("Search...", onBtnSearch, this);
+
+ childSetAction("Close", onBtnCancel, this);
+
+ setDefaultBtn("Info");
+
+ childSetDoubleClickCallback("group list", onBtnInfo);
+ childSetUserData("group list", this);
+ }
+ else
+ {
+ initAgentGroups(gAgent.getGroupID());
+
+ childSetAction("OK", onBtnOK, this);
+
+ childSetAction("Cancel", onBtnCancel, this);
+
+ setDefaultBtn("OK");
+
+ childSetDoubleClickCallback("group list", onBtnOK);
+ childSetUserData("group list", this);
+ }
+
+ enableButtons();
+
+ return TRUE;
+}
+
+void LLFloaterGroups::initAgentGroups(const LLUUID& highlight_id)
+{
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ if (!group_list) return;
+
+ group_list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ id = gAgent.mGroups.get(i).mID;
+ LLGroupData* group_datap = &gAgent.mGroups.get(i);
+ LLString style = "NORMAL";
+ if(highlight_id == id)
+ {
+ style = "BOLD";
+ }
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = group_datap->mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_SORTED);
+ }
+
+ {
+ LLString style = "NORMAL";
+ if (highlight_id.isNull())
+ {
+ style = "BOLD";
+ }
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = "none";
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][0]["font-style"] = style;
+
+ group_list->addElement(element, ADD_TOP);
+ }
+
+ group_list->selectByValue(highlight_id);
+
+ childSetFocus("group list");
+}
+
+void LLFloaterGroups::enableButtons()
+{
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ if(mType == AGENT_GROUPS)
+ {
+ if(group_id != gAgent.getGroupID())
+ {
+ childEnable("Activate");
+ }
+ else
+ {
+ childDisable("Activate");
+ }
+ if (group_id.notNull())
+ {
+ childEnable("Info");
+ childEnable("Leave");
+ }
+ else
+ {
+ childDisable("Info");
+ childDisable("Leave");
+ }
+ if(gAgent.mGroups.count() < MAX_AGENT_GROUPS)
+ {
+ childEnable("Create");
+ }
+ else
+ {
+ childDisable("Create");
+ }
+ }
+ else
+ {
+ childEnable("OK");
+ }
+}
+
+
+void LLFloaterGroups::onBtnCreate(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->create();
+}
+
+void LLFloaterGroups::onBtnActivate(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->activate();
+}
+
+void LLFloaterGroups::onBtnInfo(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->info();
+}
+
+void LLFloaterGroups::onBtnLeave(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->leave();
+}
+
+void LLFloaterGroups::onBtnSearch(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->search();
+}
+
+void LLFloaterGroups::onBtnOK(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->ok();
+}
+
+void LLFloaterGroups::onBtnCancel(void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->close();
+}
+
+void LLFloaterGroups::onGroupList(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterGroups* self = (LLFloaterGroups*)userdata;
+ if(self) self->highlightGroupList(ctrl);
+}
+
+void LLFloaterGroups::create()
+{
+ llinfos << "LLFloaterGroups::create" << llendl;
+ LLFloaterGroupInfo::showCreateGroup(NULL);
+}
+
+void LLFloaterGroups::activate()
+{
+ llinfos << "LLFloaterGroups::activate" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ActivateGroup);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, group_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLFloaterGroups::info()
+{
+ llinfos << "LLFloaterGroups::info" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+}
+
+void LLFloaterGroups::leave()
+{
+ llinfos << "LLFloaterGroups::leave" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list && (group_id = group_list->getCurrentID()).notNull())
+ {
+ S32 count = gAgent.mGroups.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ if(gAgent.mGroups.get(i).mID == group_id)
+ break;
+ }
+ if(i < count)
+ {
+ LLUUID* cb_data = new LLUUID((const LLUUID&)group_id);
+ LLString::format_map_t args;
+ args["[GROUP]"] = gAgent.mGroups.get(i).mName;
+ gViewerWindow->alertXml("GroupLeaveConfirmMember", args, callbackLeaveGroup, (void*)cb_data);
+ }
+ }
+}
+
+void LLFloaterGroups::search()
+{
+ LLFloaterDirectory::showGroups();
+}
+
+// static
+void LLFloaterGroups::callbackLeaveGroup(S32 option, void* userdata)
+{
+ LLUUID* group_id = (LLUUID*)userdata;
+ if(option == 0 && group_id)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_LeaveGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, *group_id);
+ gAgent.sendReliableMessage();
+ }
+ delete group_id;
+}
+
+void LLFloaterGroups::ok()
+{
+ llinfos << "LLFloaterGroups::ok" << llendl;
+ LLCtrlListInterface *group_list = childGetListInterface("group list");
+ LLUUID group_id;
+ if (group_list)
+ {
+ group_id = group_list->getCurrentID();
+ }
+ if(mOKCallback)
+ {
+ mOKCallback(group_id, mCallbackUserdata);
+ }
+
+ close();
+}
+
+void LLFloaterGroups::highlightGroupList(LLUICtrl*)
+{
+ llinfos << "LLFloaterGroups::highlightGroupList" << llendl;
+ enableButtons();
+}
diff --git a/indra/newview/llfloatergroups.h b/indra/newview/llfloatergroups.h
new file mode 100644
index 0000000000..6f73c97553
--- /dev/null
+++ b/indra/newview/llfloatergroups.h
@@ -0,0 +1,110 @@
+/**
+ * @file llfloatergroups.h
+ * @brief LLFloaterGroups class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shown from Edit -> Groups...
+ * Shows the agent's groups and allows the edit window to be invoked.
+ * Also overloaded to allow picking of a single group for assigning
+ * objects and land to groups.
+ */
+
+#ifndef LL_LLFLOATERGROUPS_H
+#define LL_LLFLOATERGROUPS_H
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class llfloatergroups
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+#include "lluuid.h"
+#include "llmap.h"
+#include "llevent.h"
+#include "llfloater.h"
+
+class LLUICtrl;
+class LLTextBox;
+class LLScrollListCtrl;
+class LLButton;
+
+class LLFloaterGroups : public LLFloater
+{
+public:
+ //LLEventListener
+ /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata);
+
+ enum EGroupDialog
+ {
+ AGENT_GROUPS,
+ CHOOSE_ONE
+ };
+ // Call this with an agent id and AGENT_GROUPS for an agent's
+ // groups, otherwise, call with an object id and SET_OBJECT_GROUP
+ // when modifying an object.
+ static LLFloaterGroups* show(const LLUUID& id, EGroupDialog type);
+
+ // Return the instance requested if it already exists. Otherwise,
+ // return NULL.
+ static LLFloaterGroups* getInstance(const LLUUID& id);
+
+ // clear the group list, and get a fresh set of info.
+ void reset();
+
+ void setOkCallback( void (*callback)(LLUUID, void*),
+ void* userdata);
+
+ EGroupDialog getType() const { return mType; }
+
+protected:
+ // initialize based on the type
+ BOOL postBuild();
+
+ // highlight_id is a group id to highlight
+ void initAgentGroups(const LLUUID& highlight_id);
+ void enableButtons();
+
+ static void onBtnCreate(void* userdata);
+ static void onBtnActivate(void* userdata);
+ static void onBtnInfo(void* userdata);
+ static void onBtnLeave(void* userdata);
+ static void onBtnSearch(void* userdata);
+ static void onBtnVote(void* userdata);
+ static void onBtnOK(void* userdata);
+ static void onBtnCancel(void* userdata);
+ static void onGroupList(LLUICtrl* ctrl, void* userdata);
+ static void onDoubleClickGroup(void* userdata);
+
+ void create();
+ void activate();
+ void info();
+ void leave();
+ void search();
+ void callVote();
+ void ok();
+ void highlightGroupList(LLUICtrl*);
+
+ static void callbackLeaveGroup(S32 option, void* userdata);
+
+protected:
+ LLUUID mID;
+
+ EGroupDialog mType;
+
+ void (*mOKCallback)(LLUUID id, void* userdata);
+ void* mCallbackUserdata;
+
+protected:
+ static LLMap<const LLUUID, LLFloaterGroups*> sInstances;
+
+public:
+ // do not call these directly
+ LLFloaterGroups(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& id);
+ virtual ~LLFloaterGroups();
+};
+
+
+#endif // LL_LLFLOATERGROUPS_H
diff --git a/indra/newview/llfloaterimagepreview.cpp b/indra/newview/llfloaterimagepreview.cpp
new file mode 100644
index 0000000000..68a5a90bcc
--- /dev/null
+++ b/indra/newview/llfloaterimagepreview.cpp
@@ -0,0 +1,679 @@
+/**
+ * @file llfloaterimagepreview.cpp
+ * @brief LLFloaterImagePreview class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterimagepreview.h"
+
+#include "llimagebmp.h"
+#include "llimagetga.h"
+#include "llimagejpeg.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "lltextbox.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+//static
+S32 LLFloaterImagePreview::sUploadAmount = 10;
+
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREF_BUTTON_HEIGHT = 16;
+const S32 PREVIEW_TEXTURE_HEIGHT = 300;
+
+//-----------------------------------------------------------------------------
+// LLFloaterImagePreview()
+//-----------------------------------------------------------------------------
+LLFloaterImagePreview::LLFloaterImagePreview(const char* filename) :
+ LLFloaterNameDesc(filename)
+{
+ mLastMouseX = 0;
+ mLastMouseY = 0;
+ mGLName = 0;
+ loadImage(mFilenameAndPath.c_str());
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::postBuild()
+{
+ if (!LLFloaterNameDesc::postBuild())
+ {
+ return FALSE;
+ }
+
+ childSetLabelArg("ok_btn", "[AMOUNT]", llformat("%d",sUploadAmount));
+
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface)
+ {
+ iface->selectFirstItem();
+ }
+ childSetCommitCallback("clothing_type_combo", onPreviewTypeCommit, this);
+
+ mPreviewRect.set(PREVIEW_HPAD,
+ PREVIEW_TEXTURE_HEIGHT,
+ getRect().getWidth() - PREVIEW_HPAD,
+ PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ mPreviewImageRect.set(0.f, 1.f, 1.f, 0.f);
+
+ childHide("bad_image_text");
+
+ if (mRawImagep.notNull())
+ {
+ mAvatarPreview = new LLImagePreviewAvatar(256, 256);
+ mAvatarPreview->setPreviewTarget("mPelvis", "mUpperBodyMesh0", mRawImagep, 2.f, FALSE);
+ }
+ else
+ {
+ mAvatarPreview = NULL;
+ childShow("bad_image_text");
+ childDisable("clothing_type_combo");
+ childDisable("ok_btn");
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterImagePreview()
+//-----------------------------------------------------------------------------
+LLFloaterImagePreview::~LLFloaterImagePreview()
+{
+ mRawImagep = NULL;
+ delete mAvatarPreview;
+ if (mGLName)
+ {
+ glDeleteTextures(1, &mGLName );
+ }
+}
+
+//static
+//-----------------------------------------------------------------------------
+// onPreviewTypeCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::onPreviewTypeCommit(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterImagePreview *fp =(LLFloaterImagePreview *)userdata;
+
+ if (!fp->mAvatarPreview)
+ {
+ return;
+ }
+
+ S32 which_mode = 0;
+
+ LLCtrlSelectionInterface* iface = fp->childGetSelectionInterface("clothing_type_combo");
+ if (iface)
+ {
+ which_mode = iface->getFirstSelectedIndex();
+ }
+
+ switch(which_mode)
+ {
+ case 0:
+ break;
+ case 1:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHairMesh0", fp->mRawImagep, 0.4f, FALSE);
+ break;
+ case 2:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHeadMesh0", fp->mRawImagep, 0.4f, FALSE);
+ break;
+ case 3:
+ fp->mAvatarPreview->setPreviewTarget("mChest", "mUpperBodyMesh0", fp->mRawImagep, 1.0f, FALSE);
+ break;
+ case 4:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mLowerBodyMesh0", fp->mRawImagep, 1.2f, FALSE);
+ break;
+ case 5:
+ fp->mAvatarPreview->setPreviewTarget("mSkull", "mHeadMesh0", fp->mRawImagep, 0.4f, TRUE);
+ break;
+ case 6:
+ fp->mAvatarPreview->setPreviewTarget("mChest", "mUpperBodyMesh0", fp->mRawImagep, 1.2f, TRUE);
+ break;
+ case 7:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mLowerBodyMesh0", fp->mRawImagep, 1.2f, TRUE);
+ break;
+ case 8:
+ fp->mAvatarPreview->setPreviewTarget("mKneeLeft", "mSkirtMesh0", fp->mRawImagep, 1.3f, FALSE);
+ break;
+ default:
+ break;
+ }
+ fp->mAvatarPreview->refresh();
+ //gViewerWindow->requestFastFrame(fp);
+}
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::draw()
+{
+ LLFloater::draw();
+ LLRect r = getRect();
+
+ if (mRawImagep.notNull())
+ {
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ gl_rect_2d_checkerboard(mPreviewRect);
+ LLGLDisable gls_alpha(GL_ALPHA_TEST);
+
+ GLenum format_options[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
+ GLenum format = format_options[mRawImagep->getComponents()-1];
+
+ GLenum internal_format_options[4] = { GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8 };
+ GLenum internal_format = internal_format_options[mRawImagep->getComponents()-1];
+
+ if (mGLName)
+ {
+ LLImageGL::bindExternalTexture( mGLName, 0, GL_TEXTURE_2D );
+ }
+ else
+ {
+ glGenTextures(1, &mGLName );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( mGLName, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ mRawImagep->getWidth(), mRawImagep->getHeight(),
+ 0, format, GL_UNSIGNED_BYTE, mRawImagep->getData());
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (mAvatarPreview)
+ {
+ mAvatarPreview->setTexture(mGLName);
+ }
+ }
+
+ glColor3f(1.f, 1.f, 1.f);
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(mPreviewImageRect.mLeft, mPreviewImageRect.mTop);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(mPreviewImageRect.mLeft, mPreviewImageRect.mBottom);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(mPreviewImageRect.mRight, mPreviewImageRect.mBottom);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(mPreviewImageRect.mRight, mPreviewImageRect.mTop);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ stop_glerror();
+ }
+ else
+ {
+ if (mAvatarPreview)
+ {
+ glColor3f(1.f, 1.f, 1.f);
+ mAvatarPreview->bindTexture();
+
+ glBegin( GL_QUADS );
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_HPAD + PREF_BUTTON_HEIGHT + PREVIEW_HPAD);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(r.getWidth() - PREVIEW_HPAD, PREVIEW_TEXTURE_HEIGHT);
+ }
+ glEnd();
+
+ mAvatarPreview->unbindTexture();
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadImage()
+//-----------------------------------------------------------------------------
+bool LLFloaterImagePreview::loadImage(const char *src_filename)
+{
+ // U32 length = strlen(src_filename);
+ const char* ext = strrchr(src_filename, '.');
+ char error_message[MAX_STRING];
+ error_message[0] = '\0';
+
+ U32 codec = IMG_CODEC_INVALID;
+ LLString temp_str;
+ if( 0 == strnicmp(ext, ".bmp", 4) )
+ {
+ codec = IMG_CODEC_BMP;
+ }
+ else if( 0 == strnicmp(ext, ".tga", 4) )
+ {
+ codec = IMG_CODEC_TGA;
+ }
+ else if( 0 == strnicmp(ext, ".jpg", 4) || 0 == strnicmp(ext, ".jpeg", 5))
+ {
+ codec = IMG_CODEC_JPEG;
+ }
+
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+
+ switch (codec)
+ {
+ case IMG_CODEC_BMP:
+ {
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+
+ if (!bmp_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!bmp_image->decode(raw_image))
+ {
+ return false;
+ }
+ }
+ break;
+ case IMG_CODEC_TGA:
+ {
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ if (!tga_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!tga_image->decode(raw_image))
+ {
+ return false;
+ }
+
+ if( (tga_image->getComponents() != 3) &&
+ (tga_image->getComponents() != 4) )
+ {
+ tga_image->setLastError( "Image files with less than 3 or more than 4 components are not supported." );
+ return false;
+ }
+ }
+ break;
+ case IMG_CODEC_JPEG:
+ {
+ LLPointer<LLImageJPEG> jpeg_image = new LLImageJPEG;
+
+ if (!jpeg_image->load(src_filename))
+ {
+ return false;
+ }
+
+ if (!jpeg_image->decode(raw_image))
+ {
+ return false;
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+
+ raw_image->biasedScaleToPowerOfTwo(1024);
+ mRawImagep = raw_image;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseDown()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mPreviewRect.pointInRect(x, y))
+ {
+ bringToFront( x, y );
+ gViewerWindow->setMouseCapture(this, onMouseCaptureLost);
+ gViewerWindow->hideCursor();
+ mLastMouseX = x;
+ mLastMouseY = y;
+ return TRUE;
+ }
+
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleMouseUp()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setMouseCapture(FALSE, NULL);
+ gViewerWindow->showCursor();
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+//-----------------------------------------------------------------------------
+// handleHover()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ MASK local_mask = mask & ~MASK_ALT;
+
+ if (mAvatarPreview && gViewerWindow->hasMouseCapture(this))
+ {
+ if (local_mask == MASK_PAN)
+ {
+ // pan here
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ mPreviewImageRect.translate((F32)(x - mLastMouseX) * -0.005f * mPreviewImageRect.getWidth(),
+ (F32)(y - mLastMouseY) * -0.005f * mPreviewImageRect.getHeight());
+ }
+ else
+ {
+ mAvatarPreview->pan((F32)(x - mLastMouseX) * -0.005f, (F32)(y - mLastMouseY) * -0.005f);
+ }
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 pitch_radians = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAvatarPreview->rotate(yaw_radians, pitch_radians);
+ }
+ else
+ {
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ F32 zoom_amt = (F32)(y - mLastMouseY) * -0.002f;
+ mPreviewImageRect.stretch(zoom_amt);
+ }
+ else
+ {
+ F32 yaw_radians = (F32)(x - mLastMouseX) * -0.01f;
+ F32 zoom_amt = (F32)(y - mLastMouseY) * 0.02f;
+
+ mAvatarPreview->rotate(yaw_radians, 0.f);
+ mAvatarPreview->zoom(zoom_amt);
+ }
+ }
+
+ LLCtrlSelectionInterface* iface = childGetSelectionInterface("clothing_type_combo");
+ if (iface && iface->getFirstSelectedIndex() <= 0)
+ {
+ if (mPreviewImageRect.getWidth() > 1.f)
+ {
+ mPreviewImageRect.stretch((1.f - mPreviewImageRect.getWidth()) * 0.5f);
+ }
+ else if (mPreviewImageRect.getWidth() < 0.1f)
+ {
+ mPreviewImageRect.stretch((0.1f - mPreviewImageRect.getWidth()) * 0.5f);
+ }
+
+ if (mPreviewImageRect.getHeight() > 1.f)
+ {
+ mPreviewImageRect.stretch((1.f - mPreviewImageRect.getHeight()) * 0.5f);
+ }
+ else if (mPreviewImageRect.getHeight() < 0.1f)
+ {
+ mPreviewImageRect.stretch((0.1f - mPreviewImageRect.getHeight()) * 0.5f);
+ }
+
+ if (mPreviewImageRect.mLeft < 0.f)
+ {
+ mPreviewImageRect.translate(-mPreviewImageRect.mLeft, 0.f);
+ }
+ else if (mPreviewImageRect.mRight > 1.f)
+ {
+ mPreviewImageRect.translate(1.f - mPreviewImageRect.mRight, 0.f);
+ }
+
+ if (mPreviewImageRect.mBottom < 0.f)
+ {
+ mPreviewImageRect.translate(0.f, -mPreviewImageRect.mBottom);
+ }
+ else if (mPreviewImageRect.mTop > 1.f)
+ {
+ mPreviewImageRect.translate(0.f, 1.f - mPreviewImageRect.mTop);
+ }
+ }
+ else
+ {
+ mAvatarPreview->refresh();
+ }
+
+ LLUI::setCursorPositionLocal(this, mLastMouseX, mLastMouseY);
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ if (!mPreviewRect.pointInRect(x, y) || !mAvatarPreview)
+ {
+ return LLFloater::handleHover(x, y, mask);
+ }
+ else if (local_mask == MASK_ORBIT)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if (local_mask == MASK_PAN)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// handleScrollWheel()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterImagePreview::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (mPreviewRect.pointInRect(x, y) && mAvatarPreview)
+ {
+ mAvatarPreview->zoom((F32)clicks * -0.2f);
+ mAvatarPreview->refresh();
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// onMouseCaptureLost()
+//-----------------------------------------------------------------------------
+void LLFloaterImagePreview::onMouseCaptureLost(LLMouseHandler* handler)
+{
+ gViewerWindow->showCursor();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLImagePreviewAvatar
+//-----------------------------------------------------------------------------
+LLImagePreviewAvatar::LLImagePreviewAvatar(S32 width, S32 height) : LLDynamicTexture(width, height, 3, ORDER_MIDDLE, FALSE)
+{
+ mNeedsUpdate = TRUE;
+ mTargetJoint = NULL;
+ mTargetMesh = NULL;
+ mCameraDistance = 0.f;
+ mCameraYaw = 0.f;
+ mCameraPitch = 0.f;
+ mCameraZoom = 1.f;
+
+ mDummyAvatar = new LLVOAvatar(LLUUID::null, LL_PCODE_LEGACY_AVATAR, gAgent.getRegion());
+ mDummyAvatar->createDrawable(&gPipeline);
+ mDummyAvatar->mIsDummy = TRUE;
+ mDummyAvatar->mSpecialRenderMode = 2;
+ mDummyAvatar->setPositionAgent(LLVector3::zero);
+ mDummyAvatar->slamPosition();
+ mDummyAvatar->updateJointLODs();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ gPipeline.markVisible(mDummyAvatar->mDrawable);
+
+ mTextureName = 0;
+}
+
+LLImagePreviewAvatar::~LLImagePreviewAvatar()
+{
+ mDummyAvatar->markDead();
+}
+
+
+void LLImagePreviewAvatar::setPreviewTarget(const char* joint_name, const char* mesh_name, LLImageRaw* imagep, F32 distance, BOOL male)
+{
+ mTargetJoint = mDummyAvatar->mRoot.findJoint(joint_name);
+ // clear out existing test mesh
+ if (mTargetMesh)
+ {
+ mTargetMesh->setTestTexture(0);
+ }
+
+ if (male)
+ {
+ mDummyAvatar->setVisualParamWeight( "male", 1.f );
+ mDummyAvatar->updateVisualParams();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ }
+ else
+ {
+ mDummyAvatar->setVisualParamWeight( "male", 0.f );
+ mDummyAvatar->updateVisualParams();
+ mDummyAvatar->updateGeometry(mDummyAvatar->mDrawable);
+ }
+ mDummyAvatar->mRoot.setVisible(FALSE, TRUE);
+
+ mTargetMesh = (LLViewerJointMesh*)mDummyAvatar->mRoot.findJoint(mesh_name);
+ mTargetMesh->setTestTexture(mTextureName);
+ mTargetMesh->setVisible(TRUE, FALSE);
+ mCameraDistance = distance;
+ mCameraZoom = 1.f;
+ mCameraPitch = 0.f;
+ mCameraYaw = 0.f;
+ mCameraOffset.clearVec();
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+BOOL LLImagePreviewAvatar::render()
+{
+ mNeedsUpdate = FALSE;
+ LLVOAvatar* avatarp = mDummyAvatar;
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault def;
+ glColor4f(0.15f, 0.2f, 0.3f, 1.f);
+
+ gl_rect_2d_simple( mWidth, mHeight );
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ LLVector3 target_pos = mTargetJoint->getWorldPosition();
+
+ LLQuaternion camera_rot = LLQuaternion(mCameraPitch, LLVector3::y_axis) *
+ LLQuaternion(mCameraYaw, LLVector3::z_axis);
+
+ LLQuaternion av_rot = avatarp->mPelvisp->getWorldRotation() * camera_rot;
+ gCamera->setOriginAndLookAt(
+ target_pos + ((LLVector3(mCameraDistance, 0.f, 0.f) + mCameraOffset) * av_rot), // camera
+ LLVector3::z_axis, // up
+ target_pos + (mCameraOffset * av_rot) ); // point of interest
+
+ stop_glerror();
+
+ gCamera->setView(gCamera->getDefaultFOV() / mCameraZoom);
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ // make sure alpha=0 shows avatar material color
+ LLGLDisable no_blend(GL_BLEND);
+
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ if (avatarPoolp->canUseAGP() && gPipeline.usingAGP())
+ {
+ gPipeline.bindAGP();
+ }
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// refresh()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::refresh()
+{
+ mNeedsUpdate = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// rotate()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::rotate(F32 yaw_radians, F32 pitch_radians)
+{
+ mCameraYaw = mCameraYaw + yaw_radians;
+
+ mCameraPitch = llclamp(mCameraPitch + pitch_radians, F_PI_BY_TWO * -0.8f, F_PI_BY_TWO * 0.8f);
+}
+
+//-----------------------------------------------------------------------------
+// zoom()
+//-----------------------------------------------------------------------------
+void LLImagePreviewAvatar::zoom(F32 zoom_amt)
+{
+ mCameraZoom = llclamp(mCameraZoom + zoom_amt, 1.f, 10.f);
+}
+
+void LLImagePreviewAvatar::pan(F32 right, F32 up)
+{
+ mCameraOffset.mV[VY] = llclamp(mCameraOffset.mV[VY] + right * mCameraDistance / mCameraZoom, -1.f, 1.f);
+ mCameraOffset.mV[VZ] = llclamp(mCameraOffset.mV[VZ] + up * mCameraDistance / mCameraZoom, -1.f, 1.f);
+}
diff --git a/indra/newview/llfloaterimagepreview.h b/indra/newview/llfloaterimagepreview.h
new file mode 100644
index 0000000000..4978a6ea0b
--- /dev/null
+++ b/indra/newview/llfloaterimagepreview.h
@@ -0,0 +1,83 @@
+/**
+ * @file llfloaterimagepreview.h
+ * @brief LLFloaterImagePreview class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERIMAGEPREVIEW_H
+#define LL_LLFLOATERIMAGEPREVIEW_H
+
+#include "llfloaternamedesc.h"
+#include "lldynamictexture.h"
+#include "llquaternion.h"
+
+class LLComboBox;
+class LLJoint;
+class LLViewerJointMesh;
+class LLVOAvatar;
+class LLTextBox;
+
+class LLImagePreviewAvatar : public LLDynamicTexture
+{
+public:
+ LLImagePreviewAvatar(S32 width, S32 height);
+ virtual ~LLImagePreviewAvatar();
+
+ void setPreviewTarget(const char* joint_name, const char *mesh_name, LLImageRaw* imagep, F32 distance, BOOL male);
+ void setTexture(U32 name) { mTextureName = name; }
+
+ BOOL render();
+ void refresh();
+ void rotate(F32 yaw_radians, F32 pitch_radians);
+ void zoom(F32 zoom_amt);
+ void pan(F32 right, F32 up);
+ virtual BOOL needsRender() { return mNeedsUpdate; }
+
+protected:
+ BOOL mNeedsUpdate;
+ LLJoint* mTargetJoint;
+ LLViewerJointMesh* mTargetMesh;
+ F32 mCameraDistance;
+ F32 mCameraYaw;
+ F32 mCameraPitch;
+ F32 mCameraZoom;
+ LLVector3 mCameraOffset;
+ LLPointer<LLVOAvatar> mDummyAvatar;
+ U32 mTextureName;
+};
+
+class LLFloaterImagePreview : public LLFloaterNameDesc
+{
+public:
+ LLFloaterImagePreview(const char* filename);
+ virtual ~LLFloaterImagePreview();
+
+ virtual BOOL postBuild();
+
+ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ BOOL handleHover(S32 x, S32 y, MASK mask);
+ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ static void onMouseCaptureLost(LLMouseHandler*);
+ static void setUploadAmount(S32 amount) { sUploadAmount = amount; }
+
+protected:
+ static void onPreviewTypeCommit(LLUICtrl*,void*);
+ void draw();
+ bool loadImage(const char* filename);
+
+ LLPointer<LLImageRaw> mRawImagep;
+ LLImagePreviewAvatar* mAvatarPreview;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ LLRect mPreviewRect;
+ LLRectf mPreviewImageRect;
+ GLuint mGLName;
+
+ static S32 sUploadAmount;
+};
+
+#endif // LL_LLFLOATERIMAGEPREVIEW_H
diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp
new file mode 100644
index 0000000000..6897e49dd9
--- /dev/null
+++ b/indra/newview/llfloaterland.cpp
@@ -0,0 +1,3208 @@
+/**
+ * @file llfloaterland.cpp
+ * @brief "About Land" floater, allowing display and editing of land parcel properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+#include <time.h>
+
+#include "llfloaterland.h"
+
+#include "llcachename.h"
+#include "llfocusmgr.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloaterauction.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroups.h"
+#include "llfloatergroupinfo.h"
+#include "lllineeditor.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llradiogroup.h"
+#include "llscrolllistctrl.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewermessage.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llmediaengine.h"
+#include "llviewercontrol.h"
+#include "roles_constants.h"
+
+static const S32 EDIT_HEIGHT = 16;
+static const S32 LEFT = HPAD;
+static const S32 BOTTOM = VPAD;
+static const S32 RULER0 = LEFT;
+static const S32 RULER05 = RULER0 + 24;
+static const S32 RULER1 = RULER05 + 16;
+static const S32 RULER15 = RULER1 + 20;
+static const S32 RULER2 = RULER1 + 32;
+static const S32 RULER205= RULER2 + 32;
+static const S32 RULER20 = RULER2 + 64;
+static const S32 RULER21 = RULER20 + 16;
+static const S32 RULER22 = RULER21 + 32;
+static const S32 RULER225 = RULER20 + 64;
+static const S32 RULER23 = RULER22 + 64;
+static const S32 RULER24 = RULER23 + 26;
+static const S32 RULER3 = RULER2 + 102;
+static const S32 RULER4 = RULER3 + 8;
+static const S32 RULER5 = RULER4 + 50;
+static const S32 RULER6 = RULER5 + 52;
+static const S32 RULER7 = RULER6 + 24;
+static const S32 RIGHT = LEFT + 278;
+static const S32 FAR_RIGHT = LEFT + 324 + 40;
+
+static const char PRICE[] = "Price:";
+static const char NO_PRICE[] = "";
+static const char AREA[] = "Area:";
+
+static const char OWNER_ONLINE[] = "0";
+static const char OWNER_OFFLINE[] = "1";
+static const char OWNER_GROUP[] = "2";
+
+static const char NEED_TIER_TO_MODIFY_STRING[] = "You must approve your purchase to modify this land.";
+
+static const char WEB_PAGE[] = "Web page";
+static const char QUICKTIME_MOVIE[] = "QuickTime movie";
+static const char RAW_HTML[] = "Raw HTML";
+
+// constants used in callbacks below - syntactic sugar.
+static const BOOL BUY_GROUP_LAND = TRUE;
+static const BOOL BUY_PERSONAL_LAND = FALSE;
+
+// Statics
+LLFloaterLand* LLFloaterLand::sInstance = NULL;
+LLParcelSelectionObserver* LLFloaterLand::sObserver = NULL;
+S32 LLFloaterLand::sLastTab = 0;
+BOOL LLFloaterLand::sRequestReplyOnUpdate = TRUE;
+
+// Local classes
+class LLParcelSelectionObserver : public LLParcelObserver
+{
+public:
+ virtual void changed() { LLFloaterLand::refreshAll(); }
+};
+
+//---------------------------------------------------------------------------
+// LLFloaterLand
+//---------------------------------------------------------------------------
+
+void send_parcel_select_objects(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* return_ids = NULL)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ // Since new highlight will be coming in, drop any highlights
+ // that exist right now.
+ gSelectMgr->unhighlightAll();
+
+ msg->newMessageFast(_PREHASH_ParcelSelectObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addS32Fast(_PREHASH_ReturnType, return_type);
+
+ // Throw all return ids into the packet.
+ // TODO: Check for too many ids.
+ if (return_ids)
+ {
+ uuid_list_t::iterator end = return_ids->end();
+ for (uuid_list_t::iterator it = return_ids->begin();
+ it != end;
+ ++it)
+ {
+ msg->nextBlockFast(_PREHASH_ReturnIDs);
+ msg->addUUIDFast(_PREHASH_ReturnID, (*it));
+ }
+ }
+ else
+ {
+ // Put in a null key so that the message is complete.
+ msg->nextBlockFast(_PREHASH_ReturnIDs);
+ msg->addUUIDFast(_PREHASH_ReturnID, LLUUID::null);
+ }
+
+ msg->sendReliable(region->getHost());
+}
+
+
+// static
+void LLFloaterLand::show()
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterLand();
+
+ // Select tab from last view
+ sInstance->mTabLand->selectTab(sLastTab);
+
+ sObserver = new LLParcelSelectionObserver();
+ gParcelMgr->addObserver( sObserver );
+ }
+
+ sInstance->open();
+
+ // Done automatically when the selected parcel's properties arrive
+ // (and hence we have the local id).
+ // gParcelMgr->sendParcelAccessListRequest(AL_ACCESS | AL_BAN | AL_RENTER);
+
+ // If we've already got the parcel data, fill the
+ // floater with it.
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (parcel)
+ {
+ sInstance->refresh();
+ }
+
+ sRequestReplyOnUpdate = TRUE;
+}
+
+//static
+LLPanelLandObjects* LLFloaterLand::getCurrentPanelLandObjects()
+{
+ if (!sInstance)
+ {
+ return NULL;
+ }
+
+ return sInstance->mPanelObjects;
+}
+
+//static
+LLPanelLandCovenant* LLFloaterLand::getCurrentPanelLandCovenant()
+{
+ if (!sInstance)
+ {
+ return NULL;
+ }
+
+ return sInstance->mPanelCovenant;
+}
+
+// static
+void LLFloaterLand::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->refresh();
+ }
+}
+
+
+// virtual
+BOOL LLFloaterLand::canClose()
+{
+ // canClose is checked as the first step of attempting to close
+ // the window, before focus is released from controls. Since we're
+ // closing the window and deselecting the land, we
+ // don't want replies to the upstream messages that get sent
+ // (because the reply will cause the land to be selected again).
+ sRequestReplyOnUpdate = FALSE;
+ return TRUE;
+}
+
+// virtual
+void LLFloaterLand::onClose(bool app_quitting)
+{
+ gParcelMgr->removeObserver( sObserver );
+ delete sObserver;
+ sObserver = NULL;
+
+ // Must do this after removing observer, otherwise
+ // infinite loops notifying and closing.
+ gParcelMgr->deselectLand();
+
+ // Might have been showing owned objects
+ gSelectMgr->unhighlightAll();
+
+ // Save which panel we had open
+ sLastTab = mTabLand->getCurrentPanelIndex();
+
+ destroy();
+}
+
+
+LLFloaterLand::LLFloaterLand()
+: LLFloater("floaterland", "FloaterLandRect5", "About Land")
+{
+
+
+ std::map<LLString, LLCallbackMap> factory_map;
+ factory_map["land_general_panel"] = LLCallbackMap(createPanelLandGeneral, this);
+
+
+ factory_map["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this);
+ factory_map["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this);
+ factory_map["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this);
+ factory_map["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this);
+ factory_map["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this);
+ factory_map["land_ban_panel"] = LLCallbackMap(createPanelLandBan, this);
+
+ gUICtrlFactory->buildFloater(this, "floater_about_land.xml", &factory_map);
+
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(this, "landtab");
+
+ mTabLand = (LLTabContainer*) tab;
+
+
+ if (tab)
+ {
+ tab->selectTab(sLastTab);
+ }
+}
+
+
+// virtual
+LLFloaterLand::~LLFloaterLand()
+{
+ sInstance = NULL;
+}
+
+
+// public
+void LLFloaterLand::refresh()
+{
+ mPanelGeneral->refresh();
+ mPanelObjects->refresh();
+ mPanelOptions->refresh();
+ mPanelMedia->refresh();
+ mPanelAccess->refresh();
+ mPanelBan->refresh();
+}
+
+
+
+void* LLFloaterLand::createPanelLandGeneral(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelGeneral = new LLPanelLandGeneral();
+ return self->mPanelGeneral;
+}
+
+// static
+
+
+void* LLFloaterLand::createPanelLandCovenant(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelCovenant = new LLPanelLandCovenant();
+ return self->mPanelCovenant;
+}
+
+
+// static
+void* LLFloaterLand::createPanelLandObjects(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelObjects = new LLPanelLandObjects();
+ return self->mPanelObjects;
+}
+
+// static
+void* LLFloaterLand::createPanelLandOptions(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelOptions = new LLPanelLandOptions();
+ return self->mPanelOptions;
+}
+
+// static
+void* LLFloaterLand::createPanelLandMedia(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelMedia = new LLPanelLandMedia();
+ return self->mPanelMedia;
+}
+
+// static
+void* LLFloaterLand::createPanelLandAccess(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelAccess = new LLPanelLandAccess();
+ return self->mPanelAccess;
+}
+
+// static
+void* LLFloaterLand::createPanelLandBan(void* data)
+{
+ LLFloaterLand* self = (LLFloaterLand*)data;
+ self->mPanelBan = new LLPanelLandBan();
+ return self->mPanelBan;
+}
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandGeneral
+//---------------------------------------------------------------------------
+
+
+LLPanelLandGeneral::LLPanelLandGeneral()
+: LLPanel("land_general_panel"),
+ mUncheckedSell(FALSE)
+{
+}
+
+BOOL LLPanelLandGeneral::postBuild()
+{
+
+ mEditName = LLUICtrlFactory::getLineEditorByName(this, "Name");
+ mEditName->setCommitCallback(onCommitAny);
+ childSetPrevalidate("Name", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("Name", this);
+
+
+ mEditDesc = LLUICtrlFactory::getLineEditorByName(this, "Description");
+ mEditDesc->setCommitCallback(onCommitAny);
+ childSetPrevalidate("Description", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("Description", this);
+
+
+ mTextSalePending = LLUICtrlFactory::getTextBoxByName(this, "SalePending");
+ mTextOwnerLabel = LLUICtrlFactory::getTextBoxByName(this, "Owner:");
+ mTextOwner = LLUICtrlFactory::getTextBoxByName(this, "OwnerText");
+
+
+ mBtnProfile = LLUICtrlFactory::getButtonByName(this, "Profile...");
+ mBtnProfile->setClickedCallback(onClickProfile, this);
+
+
+ mTextGroupLabel = LLUICtrlFactory::getTextBoxByName(this, "Group:");
+ mTextGroup = LLUICtrlFactory::getTextBoxByName(this, "GroupText");
+
+
+ mBtnSetGroup = LLUICtrlFactory::getButtonByName(this, "Set...");
+ mBtnSetGroup->setClickedCallback(onClickSetGroup, this);
+
+
+
+ mCheckDeedToGroup = LLUICtrlFactory::getCheckBoxByName(this, "check deed");
+ childSetCommitCallback("check deed", onCommitAny, this);
+
+
+ mBtnDeedToGroup = LLUICtrlFactory::getButtonByName(this, "Deed...");
+ mBtnDeedToGroup->setClickedCallback(onClickDeed, this);
+
+
+ mCheckContributeWithDeed = LLUICtrlFactory::getCheckBoxByName(this, "check contib");
+ childSetCommitCallback("check contib", onCommitAny, this);
+
+
+
+ mSaleInfoNotForSale = LLUICtrlFactory::getTextBoxByName(this, "Not for sale.");
+
+ mSaleInfoForSale1 = LLUICtrlFactory::getTextBoxByName(this, "For Sale: Price L$[PRICE].");
+
+
+ mBtnSellLand = LLUICtrlFactory::getButtonByName(this, "Sell Land...");
+ mBtnSellLand->setClickedCallback(onClickSellLand, this);
+
+ mSaleInfoForSale2 = LLUICtrlFactory::getTextBoxByName(this, "For sale to");
+
+ mSaleInfoForSaleObjects = LLUICtrlFactory::getTextBoxByName(this, "Sell with landowners objects in parcel.");
+
+ mSaleInfoForSaleNoObjects = LLUICtrlFactory::getTextBoxByName(this, "Selling with no objects in parcel.");
+
+
+ mBtnStopSellLand = LLUICtrlFactory::getButtonByName(this, "Cancel Land Sale");
+ mBtnStopSellLand->setClickedCallback(onClickStopSellLand, this);
+
+
+ mTextClaimDateLabel = LLUICtrlFactory::getTextBoxByName(this, "Claimed:");
+ mTextClaimDate = LLUICtrlFactory::getTextBoxByName(this, "DateClaimText");
+
+
+ mTextPriceLabel = LLUICtrlFactory::getTextBoxByName(this, "PriceLabel");
+ mTextPrice = LLUICtrlFactory::getTextBoxByName(this, "PriceText");
+
+
+ mTextDwell = LLUICtrlFactory::getTextBoxByName(this, "DwellText");
+
+
+ mBtnBuyLand = LLUICtrlFactory::getButtonByName(this, "Buy Land...");
+ mBtnBuyLand->setClickedCallback(onClickBuyLand, (void*)&BUY_PERSONAL_LAND);
+
+ mBtnBuyGroupLand = LLUICtrlFactory::getButtonByName(this, "Buy For Group...");
+ mBtnBuyGroupLand->setClickedCallback(onClickBuyLand, (void*)&BUY_GROUP_LAND);
+
+
+ static BOOL deselect_when_done = FALSE;
+ mBtnBuyPass = LLUICtrlFactory::getButtonByName(this, "Buy Pass...");
+ mBtnBuyPass->setClickedCallback(onClickBuyPass, &deselect_when_done);
+
+ mBtnReleaseLand = LLUICtrlFactory::getButtonByName(this, "Abandon Land...");
+ mBtnReleaseLand->setClickedCallback(onClickRelease, NULL);
+
+ mBtnReclaimLand = LLUICtrlFactory::getButtonByName(this, "Reclaim Land...");
+ mBtnReclaimLand->setClickedCallback(onClickReclaim, NULL);
+
+ mBtnStartAuction = LLUICtrlFactory::getButtonByName(this, "Linden Sale...");
+ mBtnStartAuction->setClickedCallback(onClickStartAuction, NULL);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandGeneral::~LLPanelLandGeneral()
+{ }
+
+
+// public
+void LLPanelLandGeneral::refresh()
+{
+ mBtnStartAuction->setVisible(gAgent.isGodlike());
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ bool region_owner = false;
+ LLViewerRegion* regionp = gParcelMgr->getSelectionRegion();
+ if(regionp && (regionp->getOwner() == gAgent.getID()))
+ {
+ region_owner = true;
+ mBtnReleaseLand->setVisible(FALSE);
+ mBtnReclaimLand->setVisible(TRUE);
+ }
+ else
+ {
+ mBtnReleaseLand->setVisible(TRUE);
+ mBtnReclaimLand->setVisible(FALSE);
+ }
+ if (!parcel)
+ {
+ // nothing selected, disable panel
+ mEditName->setEnabled(FALSE);
+ mEditName->setText("");
+
+ mEditDesc->setEnabled(FALSE);
+ mEditDesc->setText("");
+
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+
+ mBtnDeedToGroup->setEnabled(FALSE);
+ mBtnSetGroup->setEnabled(FALSE);
+ mBtnStartAuction->setEnabled(FALSE);
+
+ mCheckDeedToGroup ->set(FALSE);
+ mCheckDeedToGroup ->setEnabled(FALSE);
+ mCheckContributeWithDeed->set(FALSE);
+ mCheckContributeWithDeed->setEnabled(FALSE);
+
+ mTextOwner->setText("");
+ mBtnProfile->setLabelSelected("Profile...");
+ mBtnProfile->setLabelUnselected("Profile...");
+ mBtnProfile->setEnabled(FALSE);
+
+ mTextClaimDate->setText("");
+ mTextGroup->setText("");
+ mTextPrice->setText("");
+
+ mSaleInfoForSale1->setVisible(FALSE);
+ mSaleInfoForSale2->setVisible(FALSE);
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ mSaleInfoNotForSale->setVisible(FALSE);
+ mBtnSellLand->setVisible(FALSE);
+ mBtnStopSellLand->setVisible(FALSE);
+
+ mTextPriceLabel->setText(NO_PRICE);
+ mTextDwell->setText("");
+
+ mBtnBuyLand->setEnabled(FALSE);
+ mBtnBuyGroupLand->setEnabled(FALSE);
+ mBtnReleaseLand->setEnabled(FALSE);
+ mBtnReclaimLand->setEnabled(FALSE);
+ mBtnBuyPass->setEnabled(FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+ BOOL is_leased = (LLParcel::OS_LEASED == parcel->getOwnershipStatus());
+ BOOL region_xfer = FALSE;
+ if(regionp
+ && !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL))
+ {
+ region_xfer = TRUE;
+ }
+
+ // estate owner/manager cannot edit other parts of the parcel
+ BOOL estate_manager_sellable = !parcel->getAuctionID()
+ && gAgent.canManageEstate()
+ // estate manager/owner can only sell parcels owned by estate owner
+ && regionp
+ && (parcel->getOwnerID() == regionp->getOwner());
+ BOOL owner_sellable = region_xfer && !parcel->getAuctionID()
+ && LLViewerParcelMgr::isParcelModifiableByAgent(
+ parcel, GP_LAND_SET_SALE_INFO);
+ BOOL can_be_sold = owner_sellable || estate_manager_sellable;
+
+ const LLUUID &owner_id = parcel->getOwnerID();
+ BOOL is_public = parcel->isPublic();
+
+ // Is it owned?
+ if (is_public)
+ {
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+ mTextOwner->setText("(public)");
+ mTextOwner->setEnabled(FALSE);
+ mBtnProfile->setEnabled(FALSE);
+ mTextClaimDate->setText("");
+ mTextClaimDate->setEnabled(FALSE);
+ mTextGroup->setText("(none)");
+ mTextGroup->setEnabled(FALSE);
+ mBtnStartAuction->setEnabled(FALSE);
+ }
+ else
+ {
+ if(!is_leased && (owner_id == gAgent.getID()))
+ {
+ mTextSalePending->setText(NEED_TIER_TO_MODIFY_STRING);
+ mTextSalePending->setEnabled(TRUE);
+ }
+ else if(parcel->getAuctionID())
+ {
+ char auction_str[MAX_STRING];
+ sprintf(auction_str, "Auction ID: %u", parcel->getAuctionID());
+ mTextSalePending->setText(auction_str);
+ mTextSalePending->setEnabled(TRUE);
+ }
+ else
+ {
+ // not the owner, or it is leased
+ mTextSalePending->setText("");
+ mTextSalePending->setEnabled(FALSE);
+ }
+ //refreshNames();
+ mTextOwner->setEnabled(TRUE);
+
+ // We support both group and personal profiles
+ mBtnProfile->setEnabled(TRUE);
+
+ if (parcel->getGroupID().isNull())
+ {
+ // Not group owned, so "Profile"
+ mBtnProfile->setLabelSelected("Profile...");
+ mBtnProfile->setLabelUnselected("Profile...");
+
+ mTextGroup->setText("(none)");
+ mTextGroup->setEnabled(FALSE);
+ }
+ else
+ {
+ // Group owned, so "Info"
+ mBtnProfile->setLabelSelected("Info...");
+ mBtnProfile->setLabelUnselected("Info...");
+
+ //mTextGroup->setText("HIPPOS!");//parcel->getGroupName());
+ mTextGroup->setEnabled(TRUE);
+ }
+
+ // Display claim date
+ time_t claim_date = parcel->getClaimDate();
+ char time_buf[TIME_STR_LENGTH];
+ mTextClaimDate->setText(formatted_time(claim_date, time_buf));
+ mTextClaimDate->setEnabled(is_leased);
+
+ BOOL enable_auction = (gAgent.getGodLevel() >= GOD_LIAISON)
+ && (owner_id == GOVERNOR_LINDEN_ID)
+ && (parcel->getAuctionID() == 0);
+ mBtnStartAuction->setEnabled(enable_auction);
+ }
+
+ // Display options
+ BOOL can_edit_identity = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_IDENTITY);
+ mEditName->setEnabled(can_edit_identity);
+ mEditDesc->setEnabled(can_edit_identity);
+
+ BOOL can_edit_agent_only = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_NO_POWERS);
+ mBtnSetGroup->setEnabled(can_edit_agent_only && !parcel->getIsGroupOwned());
+
+ const LLUUID& group_id = parcel->getGroupID();
+
+ // Can only allow deeding if you own it and it's got a group.
+ BOOL enable_deed = (owner_id == gAgent.getID()
+ && group_id.notNull()
+ && gAgent.isInGroup(group_id));
+ // You don't need special powers to allow your object to
+ // be deeded to the group.
+ mCheckDeedToGroup->setEnabled(enable_deed);
+ mCheckDeedToGroup->set( parcel->getAllowDeedToGroup() );
+ mCheckContributeWithDeed->setEnabled(enable_deed && parcel->getAllowDeedToGroup());
+ mCheckContributeWithDeed->set(parcel->getContributeWithDeed());
+
+ // Actually doing the deeding requires you to have GP_LAND_DEED
+ // powers in the group.
+ BOOL can_deed = gAgent.hasPowerInGroup(group_id, GP_LAND_DEED);
+ mBtnDeedToGroup->setEnabled( parcel->getAllowDeedToGroup()
+ && group_id.notNull()
+ && can_deed
+ && !parcel->getIsGroupOwned()
+ );
+
+ mEditName->setText( parcel->getName() );
+ mEditDesc->setText( parcel->getDesc() );
+
+ BOOL for_sale = parcel->getForSale();
+
+ mBtnSellLand->setVisible(FALSE);
+ mBtnStopSellLand->setVisible(FALSE);
+
+ if (for_sale)
+ {
+ mSaleInfoForSale1->setVisible(TRUE);
+ mSaleInfoForSale2->setVisible(TRUE);
+ if (parcel->getSellWithObjects())
+ {
+ mSaleInfoForSaleObjects->setVisible(TRUE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ }
+ else
+ {
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(TRUE);
+ }
+ mSaleInfoNotForSale->setVisible(FALSE);
+ mSaleInfoForSale1->setTextArg("[PRICE]", llformat("%d", parcel->getSalePrice()));
+ if (can_be_sold)
+ {
+ mBtnStopSellLand->setVisible(TRUE);
+ }
+ }
+ else
+ {
+ mSaleInfoForSale1->setVisible(FALSE);
+ mSaleInfoForSale2->setVisible(FALSE);
+ mSaleInfoForSaleObjects->setVisible(FALSE);
+ mSaleInfoForSaleNoObjects->setVisible(FALSE);
+ mSaleInfoNotForSale->setVisible(TRUE);
+ if (can_be_sold)
+ {
+ mBtnSellLand->setVisible(TRUE);
+ }
+ }
+
+ refreshNames();
+
+ mBtnBuyLand->setEnabled(
+ gParcelMgr->canAgentBuyParcel(parcel, false));
+ mBtnBuyGroupLand->setEnabled(
+ gParcelMgr->canAgentBuyParcel(parcel, true));
+
+ // show pricing information
+ char price[64];
+ const char* label = NULL;
+ S32 area;
+ S32 claim_price;
+ S32 rent_price;
+ F32 dwell;
+ gParcelMgr->getDisplayInfo(&area,
+ &claim_price,
+ &rent_price,
+ &for_sale,
+ &dwell);
+
+ // Area
+ sprintf(price, "%d sq. m.", area);
+ label = AREA;
+
+ mTextPriceLabel->setText(label);
+ mTextPrice->setText(price);
+
+ sprintf(price, "%.0f", dwell);
+ mTextDwell->setText(price);
+
+ if(region_owner)
+ {
+ mBtnReclaimLand->setEnabled(
+ !is_public && (parcel->getOwnerID() != gAgent.getID()));
+ }
+ else
+ {
+ BOOL is_owner_release = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_RELEASE);
+ BOOL is_manager_release = (gAgent.canManageEstate() &&
+ regionp &&
+ (parcel->getOwnerID() != regionp->getOwner()));
+ BOOL can_release = is_owner_release || is_manager_release;
+ mBtnReleaseLand->setEnabled( can_release );
+ }
+
+ BOOL use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST) && !gParcelMgr->isCollisionBanned();;
+ mBtnBuyPass->setEnabled(use_pass);
+ }
+}
+
+// public
+void LLPanelLandGeneral::refreshNames()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ mTextOwner->setText("");
+ return;
+ }
+
+ char buffer[MAX_STRING];
+ if (parcel->getIsGroupOwned())
+ {
+ buffer[0] = '\0';
+ strcat(buffer, "(Group Owned)");
+ }
+ else
+ {
+ // Figure out the owner's name
+ char owner_first[MAX_STRING];
+ char owner_last[MAX_STRING];
+ gCacheName->getName(parcel->getOwnerID(), owner_first, owner_last);
+ sprintf(buffer, "%s %s", owner_first, owner_last);
+ }
+
+ if(LLParcel::OS_LEASE_PENDING == parcel->getOwnershipStatus())
+ {
+ strcat(buffer, " (Sale Pending)");
+ }
+ mTextOwner->setText(buffer);
+
+ if(!parcel->getGroupID().isNull())
+ {
+ gCacheName->getGroupName(parcel->getGroupID(), buffer);
+ }
+ else
+ {
+ buffer[0] = '\0';
+ }
+ mTextGroup->setText(buffer);
+
+ const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
+ if(auth_buyer_id.notNull())
+ {
+ LLString name;
+ char firstname[MAX_STRING];
+ char lastname[MAX_STRING];
+ gCacheName->getName(auth_buyer_id, firstname, lastname);
+ name.assign(firstname);
+ name.append(" ");
+ name.append(lastname);
+
+ mSaleInfoForSale2->setTextArg("[BUYER]", name);
+ }
+ else if(parcel->getReservedForNewbie())
+ {
+ mSaleInfoForSale2->setTextArg("[BUYER]", childGetText("new users only"));
+ }
+ else
+ {
+ mSaleInfoForSale2->setTextArg("[BUYER]", childGetText("anyone"));
+ }
+}
+
+
+// virtual
+void LLPanelLandGeneral::draw()
+{
+ refreshNames();
+ LLPanel::draw();
+}
+
+// static
+void LLPanelLandGeneral::onClickSetGroup(void* userdata)
+{
+ LLFloaterGroups* fg;
+ fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ fg->setOkCallback( cbGroupID, userdata );
+}
+
+// static
+void LLPanelLandGeneral::onClickProfile(void* data)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ if (parcel->getIsGroupOwned())
+ {
+ const LLUUID& group_id = parcel->getGroupID();
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+ else
+ {
+ const LLUUID& avatar_id = parcel->getOwnerID();
+ LLFloaterAvatarInfo::showFromObject(avatar_id);
+ }
+}
+
+// static
+void LLPanelLandGeneral::cbGroupID(LLUUID group_id, void* userdata)
+{
+ LLPanelLandGeneral* self = (LLPanelLandGeneral*)userdata;
+ self->setGroup(group_id);
+}
+
+// public
+void LLPanelLandGeneral::setGroup(const LLUUID& group_id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ // Set parcel properties and send message
+ parcel->setGroupID(group_id);
+ //parcel->setGroupName(group_name);
+ //mTextGroup->setText(group_name);
+
+ // Send update
+ gParcelMgr->sendParcelPropertiesUpdate(parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ // Update UI
+ refresh();
+}
+
+// static
+void LLPanelLandGeneral::onClickBuyLand(void* data)
+{
+ BOOL* for_group = (BOOL*)data;
+ gParcelMgr->startBuyLand(*for_group);
+}
+
+BOOL LLPanelLandGeneral::enableDeedToGroup(void*)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ return (parcel != NULL) && (parcel->getParcelFlag(PF_ALLOW_DEED_TO_GROUP));
+}
+
+// static
+void LLPanelLandGeneral::onClickDeed(void*)
+{
+ //LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ //if (parcel)
+ //{
+ gParcelMgr->startDeedLandToGroup();
+ //}
+}
+
+// static
+void LLPanelLandGeneral::onClickRelease(void*)
+{
+ gParcelMgr->startReleaseLand();
+}
+
+// static
+void LLPanelLandGeneral::onClickReclaim(void*)
+{
+ lldebugs << "LLPanelLandGeneral::onClickReclaim()" << llendl;
+ gParcelMgr->reclaimParcel();
+}
+
+// static
+BOOL LLPanelLandGeneral::enableBuyPass(void*)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ return (parcel != NULL) && (parcel->getParcelFlag(PF_USE_PASS_LIST) && !gParcelMgr->isCollisionBanned());
+}
+
+
+// static
+void LLPanelLandGeneral::onClickBuyPass(void* deselect_when_done)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ S32 pass_price = parcel->getPassPrice();
+ const char* parcel_name = parcel->getName();
+ F32 pass_hours = parcel->getPassHours();
+
+ char cost[256], time[256];
+ sprintf(cost, "%d", pass_price);
+ sprintf(time, "%.2f", pass_hours);
+
+ LLStringBase<char>::format_map_t args;
+ args["[COST]"] = cost;
+ args["[PARCEL_NAME]"] = parcel_name;
+ args["[TIME]"] = time;
+
+ gViewerWindow->alertXml("LandBuyPass", args, cbBuyPass, deselect_when_done);
+}
+
+// static
+void LLPanelLandGeneral::onClickStartAuction(void*)
+{
+ LLParcel* parcelp = gParcelMgr->getSelectedParcel();
+ if(parcelp)
+ {
+ if(parcelp->getForSale())
+ {
+ gViewerWindow->alertXml("CannotStartAuctionAlreadForSale");
+ }
+ else
+ {
+ LLFloaterAuction::show();
+ }
+ }
+}
+
+// static
+void LLPanelLandGeneral::cbBuyPass(S32 option, void* data)
+{
+ BOOL deselect_when_done = (BOOL)(intptr_t)data;
+
+ if (0 == option)
+ {
+ // User clicked OK
+ gParcelMgr->buyPass();
+ }
+
+ if (deselect_when_done)
+ {
+ gParcelMgr->deselectLand();
+ }
+}
+
+
+// static
+void LLPanelLandGeneral::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandGeneral *panelp = (LLPanelLandGeneral *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ std::string name = panelp->mEditName->getText();
+ std::string desc = panelp->mEditDesc->getText();
+
+ // Valid data from UI
+
+ // Stuff data into selected parcel
+ parcel->setName(name.c_str());
+ parcel->setDesc(desc.c_str());
+
+ BOOL allow_deed_to_group= panelp->mCheckDeedToGroup->get();
+ BOOL contribute_with_deed = panelp->mCheckContributeWithDeed->get();
+
+ parcel->setParcelFlag(PF_ALLOW_DEED_TO_GROUP, allow_deed_to_group);
+ parcel->setContributeWithDeed(contribute_with_deed);
+
+ // Send update to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ panelp->refresh();
+}
+
+// static
+void LLPanelLandGeneral::onClickSellLand(void* data)
+{
+ gParcelMgr->startSellLand();
+}
+
+// static
+void LLPanelLandGeneral::onClickStopSellLand(void* data)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+
+ parcel->setParcelFlag(PF_FOR_SALE, FALSE);
+ parcel->setSalePrice(0);
+ parcel->setAuthorizedBuyerID(LLUUID::null);
+
+ gParcelMgr->sendParcelPropertiesUpdate(parcel, LLFloaterLand::sRequestReplyOnUpdate);
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandObjects
+//---------------------------------------------------------------------------
+LLPanelLandObjects::LLPanelLandObjects()
+: LLPanel("land_objects_panel")
+{
+}
+
+
+
+BOOL LLPanelLandObjects::postBuild()
+{
+
+ mFirstReply = TRUE;
+ mParcelObjectBonus = LLUICtrlFactory::getTextBoxByName(this, "Simulator Primitive Bonus Factor: 1.00");
+
+ mSWTotalObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Simulator primitive usage:");
+ mSWTotalObjects = LLUICtrlFactory::getTextBoxByName(this, "0 out of 0 available");
+
+ mObjectContributionLabel = LLUICtrlFactory::getTextBoxByName(this, "Primitives parcel supports:");
+ mObjectContribution = LLUICtrlFactory::getTextBoxByName(this, "object_contrib_text");
+
+
+ mTotalObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Primitives on parcel:");
+ mTotalObjects = LLUICtrlFactory::getTextBoxByName(this, "total_objects_text");
+
+
+ mOwnerObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Owned by parcel owner:");
+ mOwnerObjects = LLUICtrlFactory::getTextBoxByName(this, "owner_objects_text");
+
+
+ mBtnShowOwnerObjects = LLUICtrlFactory::getButtonByName(this, "ShowOwner");
+ mBtnShowOwnerObjects->setClickedCallback(onClickShowOwnerObjects, this);
+
+ mBtnReturnOwnerObjects = LLUICtrlFactory::getButtonByName(this, "ReturnOwner...");
+ mBtnReturnOwnerObjects->setClickedCallback(onClickReturnOwnerObjects, this);
+
+
+ mGroupObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Set to group:");
+ mGroupObjects = LLUICtrlFactory::getTextBoxByName(this, "group_objects_text");
+
+
+ mBtnShowGroupObjects = LLUICtrlFactory::getButtonByName(this, "ShowGroup");
+ mBtnShowGroupObjects->setClickedCallback(onClickShowGroupObjects, this);
+
+ mBtnReturnGroupObjects = LLUICtrlFactory::getButtonByName(this, "ReturnGroup...");
+ mBtnReturnGroupObjects->setClickedCallback(onClickReturnGroupObjects, this);
+
+
+ mOtherObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Owned by others:");
+ mOtherObjects = LLUICtrlFactory::getTextBoxByName(this, "other_objects_text");
+
+ mBtnShowOtherObjects = LLUICtrlFactory::getButtonByName(this, "ShowOther");
+ mBtnShowOtherObjects->setClickedCallback(onClickShowOtherObjects, this);
+
+ mBtnReturnOtherObjects = LLUICtrlFactory::getButtonByName(this, "ReturnOther...");
+ mBtnReturnOtherObjects->setClickedCallback(onClickReturnOtherObjects, this);
+
+ mSelectedObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Selected / sat upon:");
+ mSelectedObjects = LLUICtrlFactory::getTextBoxByName(this, "selected_objects_text");
+
+ mCleanOtherObjectsLabel = LLUICtrlFactory::getTextBoxByName(this, "Autoreturn other resident's objects (minutes, 0 for off):");
+
+ mCleanOtherObjectsTime = LLUICtrlFactory::getLineEditorByName(this, "clean other time");
+ mCleanOtherObjectsTime->setFocusLostCallback(onLostFocus);
+ childSetPrevalidate("clean other time", LLLineEditor::prevalidatePrintableNotPipe);
+ childSetUserData("clean other time", this);
+
+ mOwnerListText = LLUICtrlFactory::getTextBoxByName(this, "Object Owners:");
+
+
+ mBtnRefresh = LLUICtrlFactory::getButtonByName(this, "Refresh List");
+ mBtnRefresh->setClickedCallback(onClickRefresh, this);
+
+
+ mBtnReturnOwnerList = LLUICtrlFactory::getButtonByName(this, "Return objects...");
+ mBtnReturnOwnerList->setClickedCallback(onClickReturnOwnerList, this);
+
+ LLUUID image_id;
+
+ image_id.set( gViewerArt.getString("icon_avatar_online.tga") );
+ mIconAvatarOnline = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_avatar_offline.tga") );
+ mIconAvatarOffline = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_group.tga") );
+ mIconGroup = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ mCurrentSortColumn = 3; // sort by number of objects by default.
+ mCurrentSortAscending = FALSE;
+
+ // Column widths for various columns
+ const S32 SORTER_WIDTH = 308;
+ const S32 DESC_BTN_WIDTH = 64;
+ const S32 ICON_WIDTH = 24;
+ mColWidth[0] = ICON_WIDTH; // type icon
+ mColWidth[1] = -1; // hidden type code
+ mColWidth[2] = SORTER_WIDTH - mColWidth[0] - DESC_BTN_WIDTH;
+ mColWidth[3] = DESC_BTN_WIDTH; // info
+ mColWidth[4] = -1; // type data 1
+ mColWidth[5] = -1;
+ mColWidth[6] = -1; // type data 3
+ mColWidth[7] = -1; // type data 4
+ mColWidth[8] = -1; // type data 5
+
+ // Adjust description for other widths
+ S32 sum = 0;
+ for (S32 i = 0; i < 8; i++)
+ {
+ if (mColWidth[i] > 0)
+ {
+ sum += mColWidth[i];
+ }
+ }
+ mColWidth[8] = mRect.getWidth() - HPAD - sum - HPAD - HPAD;
+
+ mBtnType = LLUICtrlFactory::getButtonByName(this, "Type");
+ mBtnType->setClickedCallback(onClickType, this);
+
+ mBtnName = LLUICtrlFactory::getButtonByName(this, "Name");
+ mBtnName->setClickedCallback(onClickName, this);
+
+ mBtnDescription = LLUICtrlFactory::getButtonByName(this, "Count");
+ mBtnDescription->setClickedCallback(onClickDesc, this);
+
+ mOwnerList = LLUICtrlFactory::getNameListByName(this, "owner list");
+ childSetCommitCallback("owner list", onCommitList, this);
+ mOwnerList->setDoubleClickCallback(onDoubleClickOwner);
+
+ return TRUE;
+}
+
+
+
+
+// virtual
+LLPanelLandObjects::~LLPanelLandObjects()
+{ }
+
+// static
+void LLPanelLandObjects::onDoubleClickOwner(void *userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+
+ LLScrollListItem* item = self->mOwnerList->getFirstSelected();
+ if (item)
+ {
+ LLUUID owner_id = item->getUUID();
+ // Look up the selected name, for future dialog box use.
+ const LLScrollListCell* cell;
+ cell = item->getColumn(1);
+ if (!cell)
+ {
+ return;
+ }
+ // Is this a group?
+ BOOL is_group = cell->getText() == OWNER_GROUP;
+ if (is_group)
+ {
+ LLFloaterGroupInfo::showFromUUID(owner_id);
+ }
+ else
+ {
+ LLFloaterAvatarInfo::showFromDirectory(owner_id);
+ }
+ }
+}
+
+// public
+void LLPanelLandObjects::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ mBtnShowOwnerObjects->setEnabled(FALSE);
+ mBtnShowGroupObjects->setEnabled(FALSE);
+ mBtnShowOtherObjects->setEnabled(FALSE);
+ mBtnReturnOwnerObjects->setEnabled(FALSE);
+ mBtnReturnGroupObjects->setEnabled(FALSE);
+ mBtnReturnOtherObjects->setEnabled(FALSE);
+ mCleanOtherObjectsTime->setEnabled(FALSE);
+ mBtnRefresh-> setEnabled(FALSE);
+ mBtnReturnOwnerList-> setEnabled(FALSE);
+
+ mSelectedOwners.clear();
+ mOwnerList->deleteAllItems();
+ mOwnerList->setEnabled(FALSE);
+
+ if (!parcel)
+ {
+ mSWTotalObjects->setText("0 out of 0 available");
+ mObjectContribution->setText("0");
+ mTotalObjects->setText("0");
+ mOwnerObjects->setText("0");
+ mGroupObjects->setText("0");
+ mOtherObjects->setText("0");
+ mSelectedObjects->setText("0");
+ }
+ else
+ {
+ char count[MAX_STRING];
+ S32 sw_max;
+ S32 sw_total;
+ S32 max;
+ S32 total;
+ S32 owned;
+ S32 group;
+ S32 other;
+ S32 selected;
+ F32 parcel_object_bonus;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total,
+ max, total, owned, group, other, selected,
+ parcel_object_bonus, mOtherTime);
+
+ // Can't have more than region max tasks, regardless of parcel
+ // object bonus factor.
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (region)
+ {
+ S32 max_tasks_per_region = (S32)region->getMaxTasks();
+ sw_max = llmin(sw_max, max_tasks_per_region);
+ max = llmin(max, max_tasks_per_region);
+ }
+
+ if (parcel_object_bonus != 1.0f)
+ {
+ sprintf(count, "Region Object Bonus Factor: %.2f",
+ parcel_object_bonus);
+ mParcelObjectBonus->setText(count);
+ }
+ else
+ {
+ mParcelObjectBonus->setText("");
+ }
+
+ if (sw_total > sw_max)
+ {
+ sprintf(count, "%d out of %d (%d will be deleted)",
+ sw_total, sw_max, sw_total - sw_max);
+ }
+ else
+ {
+ sprintf(count, "%d out of %d (%d available)",
+ sw_total, sw_max, sw_max - sw_total);
+ }
+ mSWTotalObjects->setText(count);
+
+ sprintf(count, "%d", max);
+ mObjectContribution->setText(count);
+
+ sprintf(count, "%d", total);
+ mTotalObjects->setText(count);
+
+ sprintf(count, "%d", owned);
+ mOwnerObjects->setText(count);
+
+ sprintf(count, "%d", group);
+ mGroupObjects->setText(count);
+
+ sprintf(count, "%d", other);
+ mOtherObjects->setText(count);
+
+ sprintf(count, "%d", selected);
+ mSelectedObjects->setText(count);
+
+ sprintf(count, "%d", mOtherTime);
+ mCleanOtherObjectsTime->setText(count);
+
+ BOOL can_return_owned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_OWNED);
+ BOOL can_return_group_set = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_GROUP_SET);
+ BOOL can_return_other = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_RETURN_NON_GROUP);
+
+ if (can_return_owned || can_return_group_set || can_return_other)
+ {
+ if (owned && can_return_owned)
+ {
+ mBtnShowOwnerObjects->setEnabled(TRUE);
+ mBtnReturnOwnerObjects->setEnabled(TRUE);
+ }
+ if (group && can_return_group_set)
+ {
+ mBtnShowGroupObjects->setEnabled(TRUE);
+ mBtnReturnGroupObjects->setEnabled(TRUE);
+ }
+ if (other && can_return_other)
+ {
+ mBtnShowOtherObjects->setEnabled(TRUE);
+ mBtnReturnOtherObjects->setEnabled(TRUE);
+ }
+
+ mCleanOtherObjectsTime->setEnabled(TRUE);
+ mBtnRefresh->setEnabled(TRUE);
+ }
+ }
+}
+
+// virtual
+void LLPanelLandObjects::draw()
+{
+ LLPanel::draw();
+}
+
+void send_other_clean_time_message(S32 parcel_local_id, S32 other_clean_time)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ msg->newMessageFast(_PREHASH_ParcelSetOtherCleanTime);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addS32Fast(_PREHASH_OtherCleanTime, other_clean_time);
+
+ msg->sendReliable(region->getHost());
+}
+
+void send_return_objects_message(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* owner_ids = NULL)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ msg->newMessageFast(_PREHASH_ParcelReturnObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel_local_id);
+ msg->addU32Fast(_PREHASH_ReturnType, (U32) return_type);
+
+ // Dummy task id, not used
+ msg->nextBlock("TaskIDs");
+ msg->addUUID("TaskID", LLUUID::null);
+
+ // Throw all return ids into the packet.
+ // TODO: Check for too many ids.
+ if (owner_ids)
+ {
+ uuid_list_t::iterator end = owner_ids->end();
+ for (uuid_list_t::iterator it = owner_ids->begin();
+ it != end;
+ ++it)
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, (*it));
+ }
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_OwnerIDs);
+ msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null);
+ }
+
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOwnerObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ LLUUID owner_id = parcel->getOwnerID();
+ LLString::format_map_t args;
+ if (owner_id == gAgentID)
+ {
+ LLNotifyBox::showXml("OwnedObjectsReturned");
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ args["[FIRST]"] = first;
+ args["[LAST]"] = last;
+ LLNotifyBox::showXml("OtherObjectsReturned", args);
+ }
+ send_return_objects_message(parcel->getLocalID(), RT_OWNER);
+ }
+ }
+
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnGroupObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ LLString::format_map_t args;
+ args["[GROUPNAME]"] = group_name;
+ LLNotifyBox::showXml("GroupObjectsReturned", args);
+ send_return_objects_message(parcel->getLocalID(), RT_GROUP);
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOtherObjects(S32 option, void* userdata)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ LLNotifyBox::showXml("UnOwnedObjectsReturned");
+ send_return_objects_message(parcel->getLocalID(), RT_OTHER);
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ lop->refresh();
+}
+
+// static
+void LLPanelLandObjects::callbackReturnOwnerList(S32 option, void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (0 == option)
+ {
+ if (parcel)
+ {
+ // Make sure we have something selected.
+ uuid_list_t::iterator selected = self->mSelectedOwners.begin();
+ if (selected != self->mSelectedOwners.end())
+ {
+ LLString::format_map_t args;
+ if (self->mSelectedIsGroup)
+ {
+ args["[GROUPNAME]"] = self->mSelectedName;
+ LLNotifyBox::showXml("GroupObjectsReturned", args);
+ }
+ else
+ {
+ // XUI:translate NAME -> FIRST LAST
+ args["[NAME]"] = self->mSelectedName;
+ LLNotifyBox::showXml("OtherObjectsReturned2", args);
+ }
+
+ send_return_objects_message(parcel->getLocalID(), RT_LIST, &(self->mSelectedOwners));
+ }
+ }
+ }
+ gSelectMgr->unhighlightAll();
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+ self->refresh();
+}
+
+
+// static
+void LLPanelLandObjects::onClickReturnOwnerList(void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects *)userdata;
+
+ S32 sw_max, sw_total;
+ S32 max, total;
+ S32 owned, group, other, selected;
+ F32 parcel_object_bonus;
+ S32 other_time;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcelp = gParcelMgr->getSelectedParcel();
+ if (!parcelp) return;
+
+ // Make sure we have something selected.
+ if (self->mSelectedOwners.empty())
+ {
+ return;
+ }
+ //uuid_list_t::iterator selected_itr = self->mSelectedOwners.begin();
+ //if (selected_itr == self->mSelectedOwners.end()) return;
+
+ send_parcel_select_objects(parcelp->getLocalID(), RT_LIST, &(self->mSelectedOwners));
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = self->mSelectedName;
+ args["[N]"] = llformat("%d",self->mSelectedCount);
+ if (self->mSelectedIsGroup)
+ {
+ gViewerWindow->alertXml("ReturnObjectsDeededToGroup", args, callbackReturnOwnerList, userdata);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ReturnObjectsOwnedByUser", args, callbackReturnOwnerList, userdata);
+ }
+}
+
+
+// static
+void LLPanelLandObjects::onClickRefresh(void* userdata)
+{
+ LLPanelLandObjects *self = (LLPanelLandObjects*)userdata;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if (!region) return;
+
+ // ready the list for results
+ self->mOwnerList->deleteAllItems();
+ self->mOwnerList->addSimpleItem("Searching...");
+ self->mOwnerList->setEnabled(FALSE);
+ self->mFirstReply = TRUE;
+
+ // send the message
+ msg->newMessageFast(_PREHASH_ParcelObjectOwnersRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID());
+
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandObjects::processParcelObjectOwnersReply(LLMessageSystem *msg, void **)
+{
+ LLPanelLandObjects* self = LLFloaterLand::getCurrentPanelLandObjects();
+
+ const LLFontGL* FONT = LLFontGL::sSansSerif;
+
+ // Extract all of the owners.
+ S32 rows = msg->getNumberOfBlocksFast(_PREHASH_Data);
+ //uuid_list_t return_ids;
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ S32 object_count;
+ BOOL is_online;
+ char object_count_str[MAX_STRING];
+ //BOOL b_need_refresh = FALSE;
+
+ // If we were waiting for the first reply, clear the "Searching..." text.
+ if (self->mFirstReply)
+ {
+ self->mOwnerList->deleteAllItems();
+ self->mFirstReply = FALSE;
+ }
+
+ for(S32 i = 0; i < rows; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_OwnerID, owner_id, i);
+ msg->getBOOLFast(_PREHASH_Data, _PREHASH_IsGroupOwned, is_group_owned, i);
+ msg->getS32Fast (_PREHASH_Data, _PREHASH_Count, object_count, i);
+ msg->getBOOLFast(_PREHASH_Data, _PREHASH_OnlineStatus, is_online, i);
+
+ if (owner_id.isNull())
+ {
+ continue;
+ }
+
+ LLScrollListItem *row = new LLScrollListItem( TRUE, NULL, owner_id);
+ if (is_group_owned)
+ {
+ row->addColumn(self->mIconGroup, self->mColWidth[0]);
+ row->addColumn(OWNER_GROUP, FONT, self->mColWidth[1]);
+ }
+ else if (is_online)
+ {
+ row->addColumn(self->mIconAvatarOnline, self->mColWidth[0]);
+ row->addColumn(OWNER_ONLINE, FONT, self->mColWidth[1]);
+ }
+ else // offline
+ {
+ row->addColumn(self->mIconAvatarOffline, self->mColWidth[0]);
+ row->addColumn(OWNER_OFFLINE, FONT, self->mColWidth[1]);
+ }
+ // Placeholder for name.
+ row->addColumn("", FONT, self->mColWidth[2]);
+
+ sprintf(object_count_str, "%d", object_count);
+ row->addColumn(object_count_str, FONT, self->mColWidth[3]);
+
+ if (is_group_owned)
+ {
+ self->mOwnerList->addGroupNameItem(row, ADD_BOTTOM);
+ }
+ else
+ {
+ self->mOwnerList->addNameItem(row, ADD_BOTTOM);
+ }
+
+ lldebugs << "object owner " << owner_id << " (" << (is_group_owned ? "group" : "agent")
+ << ") owns " << object_count << " objects." << llendl;
+ }
+ self->mOwnerList->sortByColumn(self->mCurrentSortColumn, self->mCurrentSortAscending);
+
+ // check for no results
+ if (0 == self->mOwnerList->getItemCount())
+ {
+ self->mOwnerList->addSimpleItem("None found.");
+ }
+ else
+ {
+ self->mOwnerList->setEnabled(TRUE);
+ }
+}
+
+void LLPanelLandObjects::sortBtnCore(S32 column)
+{
+ if (column == (S32)mCurrentSortColumn) // is this already our sorted column?
+ {
+ mCurrentSortAscending = !mCurrentSortAscending;
+ }
+ else // default to ascending first time a column is clicked
+ {
+ mCurrentSortColumn = column;
+ mCurrentSortAscending = TRUE;
+ }
+
+ mOwnerList->sortByColumn(column, mCurrentSortAscending);
+}
+
+// static
+void LLPanelLandObjects::onCommitList(LLUICtrl* ctrl, void* data)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)data;
+
+ if (FALSE == self->mOwnerList->getCanSelect())
+ {
+ return;
+ }
+ LLScrollListItem *item = self->mOwnerList->getFirstSelected();
+ if (item)
+ {
+ // Look up the selected name, for future dialog box use.
+ const LLScrollListCell* cell;
+ cell = item->getColumn(1);
+ if (!cell)
+ {
+ return;
+ }
+ // Is this a group?
+ self->mSelectedIsGroup = cell->getText() == OWNER_GROUP;
+ cell = item->getColumn(2);
+ self->mSelectedName = cell->getText();
+ cell = item->getColumn(3);
+ self->mSelectedCount = atoi(cell->getText().c_str());
+
+ // Set the selection, and enable the return button.
+ self->mSelectedOwners.clear();
+ self->mSelectedOwners.insert(item->getUUID());
+ self->mBtnReturnOwnerList->setEnabled(TRUE);
+
+ // Highlight this user's objects
+ clickShowCore(RT_LIST, &(self->mSelectedOwners));
+ }
+}
+
+// static
+void LLPanelLandObjects::onClickType(void* userdata)
+{
+ // Sort on hidden type column
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(1);
+}
+
+// static
+void LLPanelLandObjects::onClickDesc(void* userdata)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(3);
+}
+
+// static
+void LLPanelLandObjects::onClickName(void* userdata)
+{
+ LLPanelLandObjects* self = (LLPanelLandObjects*)userdata;
+ self->sortBtnCore(2);
+}
+
+// static
+void LLPanelLandObjects::clickShowCore(S32 return_type, uuid_list_t* list)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), return_type, list);
+}
+
+// static
+void LLPanelLandObjects::onClickShowOwnerObjects(void*)
+{
+ clickShowCore(RT_OWNER);
+}
+
+// static
+void LLPanelLandObjects::onClickShowGroupObjects(void*)
+{
+ clickShowCore(RT_GROUP);
+}
+
+// static
+void LLPanelLandObjects::onClickShowOtherObjects(void*)
+{
+ clickShowCore(RT_OTHER);
+}
+
+// static
+void LLPanelLandObjects::onClickReturnOwnerObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_OWNER);
+
+ LLUUID owner_id = parcel->getOwnerID();
+
+ LLStringBase<char>::format_map_t args;
+ args["[N]"] = llformat("%d",owned);
+
+ if (owner_id == gAgent.getID())
+ {
+ gViewerWindow->alertXml("ReturnObjectsOwnedBySelf", args, callbackReturnOwnerObjects, userdata);
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ std::string name = first;
+ name += " ";
+ name += last;
+ args["[NAME]"] = name;
+ gViewerWindow->alertXml("ReturnObjectsOwnedByUser", args, callbackReturnOwnerObjects, userdata);
+ }
+}
+
+// static
+void LLPanelLandObjects::onClickReturnGroupObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_GROUP);
+
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = group_name;
+ args["[N]"] = llformat("%d",group);
+
+ // create and show confirmation textbox
+ gViewerWindow->alertXml("ReturnObjectsDeededToGroup", args, callbackReturnGroupObjects, userdata);
+}
+
+// static
+void LLPanelLandObjects::onClickReturnOtherObjects(void* userdata)
+{
+ S32 sw_max=0, sw_total=0;
+ S32 max=0, total=0;
+ S32 owned=0, group=0, other=0, selected=0;
+ F32 parcel_object_bonus=0;
+ S32 other_time=0;
+
+ gParcelMgr->getPrimInfo(sw_max, sw_total, max, total, owned, group, other, selected, parcel_object_bonus, other_time);
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ send_parcel_select_objects(parcel->getLocalID(), RT_OTHER);
+
+ LLStringBase<char>::format_map_t args;
+ args["[N]"] = llformat("%d", other);
+
+ if (parcel->getIsGroupOwned())
+ {
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ args["[NAME]"] = group_name;
+
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedByGroup", args, callbackReturnOtherObjects, userdata);
+ }
+ else
+ {
+ LLUUID owner_id = parcel->getOwnerID();
+
+ if (owner_id == gAgent.getID())
+ {
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedBySelf", args, callbackReturnOtherObjects, userdata);
+ }
+ else
+ {
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first, last);
+ std::string name;
+ name += first;
+ name += " ";
+ name += last;
+ args["[NAME]"] = name;
+
+ gViewerWindow->alertXml("ReturnObjectsNotOwnedByUser", args, callbackReturnOtherObjects, userdata);
+ }
+ }
+}
+
+// static
+void LLPanelLandObjects::onLostFocus(LLLineEditor *caller, void* user_data)
+{
+ LLPanelLandObjects *lop = (LLPanelLandObjects *)user_data;
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ if (parcel)
+ {
+ lop->mOtherTime = atoi(lop->mCleanOtherObjectsTime->getText().c_str());
+
+ parcel->setCleanOtherTime(lop->mOtherTime);
+ send_other_clean_time_message(parcel->getLocalID(), lop->mOtherTime);
+ }
+}
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandOptions
+//---------------------------------------------------------------------------
+
+LLPanelLandOptions::LLPanelLandOptions()
+: LLPanel("land_options_panel"),
+ mCheckEditObjects(NULL),
+ mCheckEditGroupObjects(NULL),
+ mCheckAllObjectEntry(NULL),
+ mCheckGroupObjectEntry(NULL),
+ mCheckEditLand(NULL),
+ mCheckSafe(NULL),
+ mCheckFly(NULL),
+ mCheckGroupScripts(NULL),
+ mCheckOtherScripts(NULL),
+ mCheckLandmark(NULL),
+ mCheckShowDirectory(NULL),
+ mCategoryCombo(NULL),
+ mLandingTypeCombo(NULL),
+ mSnapshotCtrl(NULL),
+ mLocationText(NULL),
+ mSetBtn(NULL),
+ mClearBtn(NULL),
+ mAllowPublishCtrl(NULL),
+ mMatureCtrl(NULL),
+ mPushRestrictionCtrl(NULL),
+ mPublishHelpButton(NULL)
+{
+
+}
+
+
+BOOL LLPanelLandOptions::postBuild()
+{
+
+
+ mCheckEditObjects = LLUICtrlFactory::getCheckBoxByName(this, "edit objects check");
+ childSetCommitCallback("edit objects check", onCommitAny, this);
+
+ mCheckEditGroupObjects = LLUICtrlFactory::getCheckBoxByName(this, "edit group objects check");
+ childSetCommitCallback("edit group objects check", onCommitAny, this);
+
+ mCheckAllObjectEntry = LLUICtrlFactory::getCheckBoxByName(this, "all object entry check");
+ childSetCommitCallback("all object entry check", onCommitAny, this);
+
+ mCheckGroupObjectEntry = LLUICtrlFactory::getCheckBoxByName(this, "group object entry check");
+ childSetCommitCallback("group object entry check", onCommitAny, this);
+
+ mCheckEditLand = LLUICtrlFactory::getCheckBoxByName(this, "edit land check");
+ childSetCommitCallback("edit land check", onCommitAny, this);
+
+
+ mCheckLandmark = LLUICtrlFactory::getCheckBoxByName(this, "check landmark");
+ childSetCommitCallback("check landmark", onCommitAny, this);
+
+
+ mCheckGroupScripts = LLUICtrlFactory::getCheckBoxByName(this, "check group scripts");
+ childSetCommitCallback("check group scripts", onCommitAny, this);
+
+
+ mCheckFly = LLUICtrlFactory::getCheckBoxByName(this, "check fly");
+ childSetCommitCallback("check fly", onCommitAny, this);
+
+
+ mCheckOtherScripts = LLUICtrlFactory::getCheckBoxByName(this, "check other scripts");
+ childSetCommitCallback("check other scripts", onCommitAny, this);
+
+
+ mCheckSafe = LLUICtrlFactory::getCheckBoxByName(this, "check safe");
+ childSetCommitCallback("check safe", onCommitAny, this);
+
+
+ mPushRestrictionCtrl = LLUICtrlFactory::getCheckBoxByName(this, "PushRestrictCheck");
+ childSetCommitCallback("PushRestrictCheck", onCommitAny, this);
+
+ mCheckShowDirectory = LLUICtrlFactory::getCheckBoxByName(this, "ShowDirectoryCheck");
+ childSetCommitCallback("ShowDirectoryCheck", onCommitAny, this);
+
+
+ mCategoryCombo = LLUICtrlFactory::getComboBoxByName(this, "land category");
+ childSetCommitCallback("land category", onCommitAny, this);
+
+ mAllowPublishCtrl = LLUICtrlFactory::getCheckBoxByName(this, "PublishCheck");
+ childSetCommitCallback("PublishCheck", onCommitAny, this);
+
+
+ mMatureCtrl = LLUICtrlFactory::getCheckBoxByName(this, "MatureCheck");
+ childSetCommitCallback("MatureCheck", onCommitAny, this);
+
+ mPublishHelpButton = LLUICtrlFactory::getButtonByName(this, "?");
+ mPublishHelpButton->setClickedCallback(onClickPublishHelp, this);
+
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ mAllowPublishCtrl->setVisible(FALSE);
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setVisible(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ mMatureCtrl->setVisible(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ }
+
+ // Load up the category list
+ //now in xml file
+ /*
+ S32 i;
+ for (i = 0; i < LLParcel::C_COUNT; i++)
+ {
+ LLParcel::ECategory cat = (LLParcel::ECategory)i;
+
+ // Selecting Linden Location when you're not a god
+ // is also blocked on the server.
+ BOOL enabled = TRUE;
+ if (!gAgent.isGodlike()
+ && i == LLParcel::C_LINDEN)
+ {
+ enabled = FALSE;
+ }
+
+ mCategoryCombo->add( LLParcel::getCategoryUIString(cat), ADD_BOTTOM, enabled );
+ }*/
+
+
+ mSnapshotCtrl = LLUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback( onCommitAny );
+ mSnapshotCtrl->setCallbackUserData( this );
+ mSnapshotCtrl->setAllowNoTexture ( TRUE );
+ mSnapshotCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ mSnapshotCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+
+
+
+ mLocationText = LLUICtrlFactory::getTextBoxByName(this, "Landing Point: (none)");
+
+ mSetBtn = LLUICtrlFactory::getButtonByName(this, "Set");
+ mSetBtn->setClickedCallback(onClickSet, this);
+
+
+ mClearBtn = LLUICtrlFactory::getButtonByName(this, "Clear");
+ mClearBtn->setClickedCallback(onClickClear, this);
+
+
+ mLandingTypeCombo = LLUICtrlFactory::getComboBoxByName(this, "landing type");
+ childSetCommitCallback("landing type", onCommitAny, this);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandOptions::~LLPanelLandOptions()
+{ }
+
+
+// public
+void LLPanelLandOptions::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (!parcel)
+ {
+ mCheckEditObjects ->set(FALSE);
+ mCheckEditObjects ->setEnabled(FALSE);
+
+ mCheckEditGroupObjects ->set(FALSE);
+ mCheckEditGroupObjects ->setEnabled(FALSE);
+
+ mCheckAllObjectEntry ->set(FALSE);
+ mCheckAllObjectEntry ->setEnabled(FALSE);
+
+ mCheckGroupObjectEntry ->set(FALSE);
+ mCheckGroupObjectEntry ->setEnabled(FALSE);
+
+ mCheckEditLand ->set(FALSE);
+ mCheckEditLand ->setEnabled(FALSE);
+
+ mCheckSafe ->set(FALSE);
+ mCheckSafe ->setEnabled(FALSE);
+
+ mCheckFly ->set(FALSE);
+ mCheckFly ->setEnabled(FALSE);
+
+ mCheckLandmark ->set(FALSE);
+ mCheckLandmark ->setEnabled(FALSE);
+
+ mCheckGroupScripts ->set(FALSE);
+ mCheckGroupScripts ->setEnabled(FALSE);
+
+ mCheckOtherScripts ->set(FALSE);
+ mCheckOtherScripts ->setEnabled(FALSE);
+
+ mCheckShowDirectory ->set(FALSE);
+ mCheckShowDirectory ->setEnabled(FALSE);
+
+ mPushRestrictionCtrl->set(FALSE);
+ mPushRestrictionCtrl->setEnabled(FALSE);
+
+ const char* none_string = LLParcel::getCategoryUIString(LLParcel::C_NONE);
+ mCategoryCombo->setSimple(none_string);
+ mCategoryCombo->setEnabled(FALSE);
+
+ mLandingTypeCombo->setCurrentByIndex(0);
+ mLandingTypeCombo->setEnabled(FALSE);
+
+ mSnapshotCtrl->setImageAssetID(LLUUID::null);
+ mSnapshotCtrl->setEnabled(FALSE);
+
+ mLocationText->setText("Landing Point: (none)");
+ mSetBtn->setEnabled(FALSE);
+ mClearBtn->setEnabled(FALSE);
+
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+
+ // Display options
+ BOOL can_change_options = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_OPTIONS);
+ mCheckEditObjects ->set( parcel->getAllowModify() );
+ mCheckEditObjects ->setEnabled( can_change_options );
+
+ mCheckEditGroupObjects ->set( parcel->getAllowGroupModify() || parcel->getAllowModify());
+ mCheckEditGroupObjects ->setEnabled( can_change_options && !parcel->getAllowModify() ); // If others edit is enabled, then this is explicitly enabled.
+
+ mCheckAllObjectEntry ->set( parcel->getAllowAllObjectEntry() );
+ mCheckAllObjectEntry ->setEnabled( can_change_options );
+
+ mCheckGroupObjectEntry ->set( parcel->getAllowGroupObjectEntry() || parcel->getAllowAllObjectEntry());
+ mCheckGroupObjectEntry ->setEnabled( can_change_options && !parcel->getAllowAllObjectEntry() );
+
+ BOOL can_change_terraform = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_EDIT);
+ mCheckEditLand ->set( parcel->getAllowTerraform() );
+ mCheckEditLand ->setEnabled( can_change_terraform );
+
+ mCheckSafe ->set( !parcel->getAllowDamage() );
+ mCheckSafe ->setEnabled( can_change_options );
+
+ mCheckFly ->set( parcel->getAllowFly() );
+ mCheckFly ->setEnabled( can_change_options );
+
+ mCheckLandmark ->set( parcel->getAllowLandmark() );
+ mCheckLandmark ->setEnabled( can_change_options );
+
+ mCheckGroupScripts ->set( parcel->getAllowGroupScripts() || parcel->getAllowOtherScripts());
+ mCheckGroupScripts ->setEnabled( can_change_options && !parcel->getAllowOtherScripts());
+
+ mCheckOtherScripts ->set( parcel->getAllowOtherScripts() );
+ mCheckOtherScripts ->setEnabled( can_change_options );
+
+ BOOL can_change_identity = LLViewerParcelMgr::isParcelModifiableByAgent(parcel,
+ GP_LAND_CHANGE_IDENTITY);
+ mCheckShowDirectory ->set( parcel->getParcelFlag(PF_SHOW_DIRECTORY));
+ mCheckShowDirectory ->setEnabled( can_change_identity );
+
+ mPushRestrictionCtrl->set( parcel->getRestrictPushObject() );
+ if(parcel->getRegionPushOverride())
+ {
+ mPushRestrictionCtrl->setLabel("Restrict Pushing (Region Override)");
+ mPushRestrictionCtrl->setEnabled(false);
+ mPushRestrictionCtrl->set(TRUE);
+ }
+ else
+ {
+ mPushRestrictionCtrl->setLabel("Restrict Pushing");
+ mPushRestrictionCtrl->setEnabled(can_change_options);
+ }
+
+ // Set by string in case the order in UI doesn't match the order
+ // by index.
+ LLParcel::ECategory cat = parcel->getCategory();
+ const char* category_string = LLParcel::getCategoryUIString(cat);
+ mCategoryCombo->setSimple(category_string);
+ mCategoryCombo->setEnabled( can_change_identity );
+
+ BOOL can_change_landing_point = LLViewerParcelMgr::isParcelModifiableByAgent(parcel,
+ GP_LAND_SET_LANDING_POINT);
+ mLandingTypeCombo->setCurrentByIndex((S32)parcel->getLandingType());
+ mLandingTypeCombo->setEnabled( can_change_landing_point );
+
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ mSnapshotCtrl->setEnabled( can_change_identity );
+
+ LLVector3 pos = parcel->getUserLocation();
+ if (pos.isExactlyZero())
+ {
+ mLocationText->setText("Landing Point: (none)");
+ }
+ else
+ {
+ char buffer[256];
+ sprintf(buffer, "Landing Point: %d, %d, %d",
+ llround(pos.mV[VX]),
+ llround(pos.mV[VY]),
+ llround(pos.mV[VZ]));
+ mLocationText->setText(buffer);
+ }
+
+ mSetBtn->setEnabled( can_change_landing_point );
+ mClearBtn->setEnabled( can_change_landing_point );
+
+ mAllowPublishCtrl->set(parcel->getAllowPublish());
+ mAllowPublishCtrl->setEnabled( can_change_identity );
+ mMatureCtrl->set(parcel->getMaturePublish());
+ mMatureCtrl->setEnabled( can_change_identity );
+ mPublishHelpButton->setEnabled( can_change_identity );
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ mAllowPublishCtrl->setVisible(FALSE);
+ mAllowPublishCtrl->setEnabled(FALSE);
+ mPublishHelpButton->setVisible(FALSE);
+ mPublishHelpButton->setEnabled(FALSE);
+ mMatureCtrl->setVisible(FALSE);
+ mMatureCtrl->setEnabled(FALSE);
+ }
+ }
+}
+
+
+// static
+void LLPanelLandOptions::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandOptions *self = (LLPanelLandOptions *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL create_objects = self->mCheckEditObjects->get();
+ BOOL create_group_objects = self->mCheckEditGroupObjects->get() || self->mCheckEditObjects->get();
+ BOOL all_object_entry = self->mCheckAllObjectEntry->get();
+ BOOL group_object_entry = self->mCheckGroupObjectEntry->get() || self->mCheckAllObjectEntry->get();
+ BOOL allow_terraform = self->mCheckEditLand->get();
+ BOOL allow_damage = !self->mCheckSafe->get();
+ BOOL allow_fly = self->mCheckFly->get();
+ BOOL allow_landmark = self->mCheckLandmark->get();
+ BOOL allow_group_scripts = self->mCheckGroupScripts->get() || self->mCheckOtherScripts->get();
+ BOOL allow_other_scripts = self->mCheckOtherScripts->get();
+ BOOL allow_publish = self->mAllowPublishCtrl->get();
+ BOOL mature_publish = self->mMatureCtrl->get();
+ BOOL push_restriction = self->mPushRestrictionCtrl->get();
+ BOOL show_directory = self->mCheckShowDirectory->get();
+ S32 category_index = self->mCategoryCombo->getCurrentIndex();
+ S32 landing_type_index = self->mLandingTypeCombo->getCurrentIndex();
+ LLUUID snapshot_id = self->mSnapshotCtrl->getImageAssetID();
+ LLViewerRegion* region;
+ region = gParcelMgr->getSelectionRegion();
+
+ if (!allow_other_scripts && region && region->getAllowDamage())
+ {
+
+ gViewerWindow->alertXml("UnableToDisableOutsideScripts");
+ return;
+ }
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_CREATE_OBJECTS, create_objects);
+ parcel->setParcelFlag(PF_CREATE_GROUP_OBJECTS, create_group_objects);
+ parcel->setParcelFlag(PF_ALLOW_ALL_OBJECT_ENTRY, all_object_entry);
+ parcel->setParcelFlag(PF_ALLOW_GROUP_OBJECT_ENTRY, group_object_entry);
+ parcel->setParcelFlag(PF_ALLOW_TERRAFORM, allow_terraform);
+ parcel->setParcelFlag(PF_ALLOW_DAMAGE, allow_damage);
+ parcel->setParcelFlag(PF_ALLOW_FLY, allow_fly);
+ parcel->setParcelFlag(PF_ALLOW_LANDMARK, allow_landmark);
+ parcel->setParcelFlag(PF_ALLOW_GROUP_SCRIPTS, allow_group_scripts);
+ parcel->setParcelFlag(PF_ALLOW_OTHER_SCRIPTS, allow_other_scripts);
+ parcel->setParcelFlag(PF_SHOW_DIRECTORY, show_directory);
+ parcel->setParcelFlag(PF_ALLOW_PUBLISH, allow_publish);
+ parcel->setParcelFlag(PF_MATURE_PUBLISH, mature_publish);
+ parcel->setParcelFlag(PF_RESTRICT_PUSHOBJECT, push_restriction);
+ parcel->setCategory((LLParcel::ECategory)category_index);
+ parcel->setLandingType((LLParcel::ELandingType)landing_type_index);
+ parcel->setSnapshotID(snapshot_id);
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+
+// static
+void LLPanelLandOptions::onClickSet(void* userdata)
+{
+ LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;
+
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+ if (!selected_parcel) return;
+
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (!agent_parcel) return;
+
+ if (agent_parcel->getLocalID() != selected_parcel->getLocalID())
+ {
+ gViewerWindow->alertXml("MustBeInParcel");
+ return;
+ }
+
+ LLVector3 pos_region = gAgent.getPositionAgent();
+ selected_parcel->setUserLocation(pos_region);
+ selected_parcel->setUserLookAt(gAgent.getFrameAgent().getAtAxis());
+
+ gParcelMgr->sendParcelPropertiesUpdate(selected_parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ self->refresh();
+}
+
+void LLPanelLandOptions::onClickClear(void* userdata)
+{
+ LLPanelLandOptions* self = (LLPanelLandOptions*)userdata;
+
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+ if (!selected_parcel) return;
+
+ // yes, this magic number of 0,0,0 means that it is clear
+ LLVector3 zero_vec(0.f, 0.f, 0.f);
+ selected_parcel->setUserLocation(zero_vec);
+ selected_parcel->setUserLookAt(zero_vec);
+
+ gParcelMgr->sendParcelPropertiesUpdate(selected_parcel, LLFloaterLand::sRequestReplyOnUpdate);
+
+ self->refresh();
+}
+
+// static
+void LLPanelLandOptions::onClickPublishHelp(void*)
+{
+ gViewerWindow->alertXml("ClickPublishHelpLand");
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandMedia
+//---------------------------------------------------------------------------
+
+LLPanelLandMedia::LLPanelLandMedia()
+: LLPanel("land_media_panel")
+{
+}
+
+
+
+
+BOOL LLPanelLandMedia::postBuild()
+{
+
+ mCheckSoundLocal = LLUICtrlFactory::getCheckBoxByName(this, "check sound local");
+ childSetCommitCallback("check sound local", onCommitAny, this);
+
+ mMusicURLEdit = LLUICtrlFactory::getLineEditorByName(this, "music_url");
+ childSetCommitCallback("music_url", onCommitAny, this);
+
+
+ mMediaTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this, "media texture");
+ mMediaTextureCtrl->setCommitCallback( onCommitAny );
+ mMediaTextureCtrl->setCallbackUserData( this );
+ mMediaTextureCtrl->setAllowNoTexture ( TRUE );
+ mMediaTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ mMediaTextureCtrl->setNonImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+
+ mMediaAutoScaleCheck = LLUICtrlFactory::getCheckBoxByName(this, "media_auto_scale");
+ childSetCommitCallback("media_auto_scale", onCommitAny, this);
+
+ mMediaURLEdit = LLUICtrlFactory::getLineEditorByName(this, "media_url");
+ childSetCommitCallback("media_url", onCommitAny, this);
+
+ return TRUE;
+}
+
+
+// virtual
+LLPanelLandMedia::~LLPanelLandMedia()
+{ }
+
+
+// public
+void LLPanelLandMedia::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (!parcel)
+ {
+ mCheckSoundLocal->set(FALSE);
+ mCheckSoundLocal->setEnabled(FALSE);
+
+ mMusicURLEdit->setText("");
+ mMusicURLEdit->setEnabled(FALSE);
+
+ mMediaURLEdit->setText("");
+ mMediaURLEdit->setEnabled(FALSE);
+
+ mMediaAutoScaleCheck->set ( FALSE );
+ mMediaAutoScaleCheck->setEnabled(FALSE);
+
+ mMediaTextureCtrl->clear();
+ mMediaTextureCtrl->setEnabled(FALSE);
+
+ #if 0
+ mMediaStopButton->setEnabled ( FALSE );
+ mMediaStartButton->setEnabled ( FALSE );
+ #endif
+ }
+ else
+ {
+ // something selected, hooray!
+
+ // Display options
+ BOOL can_change_media = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA);
+
+ mCheckSoundLocal->set( parcel->getSoundLocal() );
+ mCheckSoundLocal->setEnabled( can_change_media );
+
+ // don't display urls if you're not able to change it
+ // much requested change in forums so people can't 'steal' urls
+ // NOTE: bug#2009 means this is still vunerable - however, bug
+ // should be closed since this bug opens up major security issues elsewhere.
+ if ( can_change_media )
+ {
+ mMusicURLEdit->setDrawAsterixes ( FALSE );
+ mMediaURLEdit->setDrawAsterixes ( FALSE );
+ }
+ else
+ {
+ mMusicURLEdit->setDrawAsterixes ( TRUE );
+ mMediaURLEdit->setDrawAsterixes ( TRUE );
+ }
+
+ mMusicURLEdit->setText(parcel->getMusicURL());
+ mMusicURLEdit->setEnabled( can_change_media );
+
+ mMediaURLEdit->setText(parcel->getMediaURL());
+ mMediaURLEdit->setEnabled( can_change_media );
+
+ mMediaAutoScaleCheck->set ( parcel->getMediaAutoScale () );
+ mMediaAutoScaleCheck->setEnabled ( can_change_media );
+
+ LLUUID tmp = parcel->getMediaID();
+ mMediaTextureCtrl->setImageAssetID ( parcel->getMediaID() );
+ mMediaTextureCtrl->setEnabled( can_change_media );
+
+ #if 0
+ // there is a media url and a media texture selected
+ if ( ( ! ( std::string ( parcel->getMediaURL() ).empty () ) ) && ( ! ( parcel->getMediaID ().isNull () ) ) )
+ {
+ // turn on transport controls if allowed for this parcel
+ mMediaStopButton->setEnabled ( editable );
+ mMediaStartButton->setEnabled ( editable );
+ }
+ else
+ {
+ // no media url or no media texture
+ mMediaStopButton->setEnabled ( FALSE );
+ mMediaStartButton->setEnabled ( FALSE );
+ };
+ #endif
+ }
+}
+
+// static
+void LLPanelLandMedia::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandMedia *self = (LLPanelLandMedia *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL sound_local = self->mCheckSoundLocal->get();
+ std::string music_url = self->mMusicURLEdit->getText();
+ std::string media_url = self->mMediaURLEdit->getText();
+ U8 media_auto_scale = self->mMediaAutoScaleCheck->get();
+ LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID();
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local);
+ parcel->setMusicURL(music_url.c_str());
+ parcel->setMediaURL(media_url.c_str());
+ parcel->setMediaID(media_id);
+ parcel->setMediaAutoScale ( media_auto_scale );
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+void LLPanelLandMedia::onClickStopMedia ( void* data )
+{
+ LLMediaEngine::getInstance ()->stop ();
+}
+
+void LLPanelLandMedia::onClickStartMedia ( void* data )
+{
+ // force a commit
+ gFocusMgr.setKeyboardFocus ( NULL, NULL );
+
+ // force a reload
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, false, std::string());
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandAccess
+//---------------------------------------------------------------------------
+
+LLPanelLandAccess::LLPanelLandAccess()
+: LLPanel("land_access_panel")
+{
+}
+
+
+
+BOOL LLPanelLandAccess::postBuild()
+{
+
+
+ mCheckGroup = LLUICtrlFactory::getCheckBoxByName(this, "GroupCheck");
+ childSetCommitCallback("GroupCheck", onCommitAny, this);
+
+ mCheckAccess = LLUICtrlFactory::getCheckBoxByName(this, "AccessCheck");
+ childSetCommitCallback("AccessCheck", onCommitAny, this);
+
+ mListAccess = LLUICtrlFactory::getNameListByName(this, "AccessList");
+
+ mBtnAddAccess = LLUICtrlFactory::getButtonByName(this, "Add...");
+
+ mBtnAddAccess->setClickedCallback(onClickAdd, this);
+
+ mBtnRemoveAccess = LLUICtrlFactory::getButtonByName(this, "Remove");
+
+ mBtnRemoveAccess->setClickedCallback(onClickRemove, this);
+
+ mCheckPass = LLUICtrlFactory::getCheckBoxByName(this, "PassCheck");
+ childSetCommitCallback("PassCheck", onCommitAny, this);
+
+
+ mSpinPrice = LLUICtrlFactory::getSpinnerByName(this, "PriceSpin");
+ childSetCommitCallback("PriceSpin", onCommitAny, this);
+
+ mSpinHours = LLUICtrlFactory::getSpinnerByName(this, "HoursSpin");
+ childSetCommitCallback("HoursSpin", onCommitAny, this);
+
+
+ return TRUE;
+}
+
+
+LLPanelLandAccess::~LLPanelLandAccess()
+{ }
+
+void LLPanelLandAccess::refresh()
+{
+ mListAccess->deleteAllItems();
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel)
+ {
+ char label[256];
+
+ // Display options
+ BOOL use_group = parcel->getParcelFlag(PF_USE_ACCESS_GROUP);
+ mCheckGroup->set( use_group );
+
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ sprintf(label, "Group: %s", group_name);
+ mCheckGroup->setLabel( label );
+
+ S32 count = parcel->mAccessList.size();
+
+ BOOL use_list = parcel->getParcelFlag(PF_USE_ACCESS_LIST);
+ mCheckAccess->set( use_list );
+ sprintf(label, "Avatars: (%d listed, %d max)",
+ count, PARCEL_MAX_ACCESS_LIST);
+ mCheckAccess->setLabel( label );
+
+ access_map_const_iterator cit = parcel->mAccessList.begin();
+ access_map_const_iterator end = parcel->mAccessList.end();
+
+ for (; cit != end; ++cit)
+ {
+ const LLAccessEntry& entry = (*cit).second;
+ LLString suffix;
+ if (entry.mTime != 0)
+ {
+ S32 now = time(NULL);
+ S32 seconds = entry.mTime - now;
+ if (seconds < 0) seconds = 0;
+ suffix.assign(" (");
+ if (seconds >= 120)
+ {
+ char buf[30];
+ sprintf(buf, "%d minutes", (seconds/60));
+ suffix.append(buf);
+ }
+ else if (seconds >= 60)
+ {
+ suffix.append("1 minute");
+ }
+ else
+ {
+ char buf[30];
+ sprintf(buf, "%d seconds", seconds);
+ suffix.append(buf);
+ }
+ suffix.append(" remaining)");
+ }
+ mListAccess->addNameItem(entry.mID, ADD_BOTTOM, TRUE, suffix);
+ }
+
+ BOOL can_manage_allowed = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_ALLOWED);
+
+ BOOL enable_add = can_manage_allowed && (count < PARCEL_MAX_ACCESS_LIST);
+ mBtnAddAccess->setEnabled(enable_add);
+
+ BOOL enable_remove = can_manage_allowed && (count > 0);
+ mBtnRemoveAccess->setEnabled(enable_remove);
+
+ // Can only sell passes when limiting the access.
+ BOOL can_manage_passes = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_PASSES);
+ mCheckPass->setEnabled( (use_group || use_list) && can_manage_passes );
+
+ BOOL use_pass = parcel->getParcelFlag(PF_USE_PASS_LIST);
+ mCheckPass->set( use_pass );
+
+ BOOL enable_pass = can_manage_passes && use_pass;
+ mSpinPrice->setEnabled( enable_pass );
+ mSpinHours->setEnabled( enable_pass );
+
+ S32 pass_price = parcel->getPassPrice();
+ mSpinPrice->set( F32(pass_price) );
+
+ F32 pass_hours = parcel->getPassHours();
+ mSpinHours->set( pass_hours );
+
+ mCheckGroup->setEnabled( can_manage_allowed );
+ mCheckAccess->setEnabled( can_manage_allowed );
+
+ }
+ else
+ {
+ mCheckGroup->set(FALSE);
+ mCheckGroup->setLabel("Group:");
+ mCheckAccess->set(FALSE);
+ mCheckAccess->setLabel("Avatars:");
+ mBtnAddAccess->setEnabled(FALSE);
+ mBtnRemoveAccess->setEnabled(FALSE);
+ mSpinPrice->set((F32)PARCEL_PASS_PRICE_DEFAULT);
+ mSpinPrice->setEnabled(FALSE);
+ mSpinHours->set( PARCEL_PASS_HOURS_DEFAULT );
+ mSpinHours->setEnabled(FALSE);
+ mCheckGroup->setEnabled(FALSE);
+ mCheckAccess->setEnabled(FALSE);
+ }
+}
+
+// public
+void LLPanelLandAccess::refreshNames()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ group_name[0] = '\0';
+ if(parcel)
+ {
+ gCacheName->getGroupName(parcel->getGroupID(), group_name);
+ }
+ char label[MAX_STRING];
+ snprintf(label, MAX_STRING, "Group: %s", group_name);
+ mCheckGroup->setLabel(label);
+}
+
+
+// virtual
+void LLPanelLandAccess::draw()
+{
+ refreshNames();
+ LLPanel::draw();
+}
+
+
+void LLPanelLandAccess::onAccessLevelChange(LLUICtrl*, void *userdata)
+{
+ LLPanelLandAccess::onCommitAny(NULL, userdata);
+}
+
+// static
+void LLPanelLandAccess::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandAccess *self = (LLPanelLandAccess *)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL use_access_group = self->mCheckGroup->get();
+ BOOL use_access_list = self->mCheckAccess->get();
+ BOOL use_pass_list = self->mCheckPass->get();
+
+
+
+ // Must be limiting access to sell passes
+ if (!use_access_group && !use_access_list)
+ {
+ use_pass_list = FALSE;
+ }
+
+ S32 pass_price = llfloor(self->mSpinPrice->get());
+ F32 pass_hours = self->mSpinHours->get();
+
+ // Validate extracted data
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_USE_ACCESS_GROUP, use_access_group);
+ parcel->setParcelFlag(PF_USE_ACCESS_LIST, use_access_list);
+ parcel->setParcelFlag(PF_USE_PASS_LIST, use_pass_list);
+
+ parcel->setPassPrice( pass_price );
+ parcel->setPassHours( pass_hours );
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+// static
+void LLPanelLandAccess::onClickAdd(void* data)
+{
+ LLPanelLandAccess* panelp = (LLPanelLandAccess*)data;
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data) );
+}
+
+// static
+void LLPanelLandAccess::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ LLPanelLandAccess* self = (LLPanelLandAccess*)userdata;
+ if (names.empty() || ids.empty()) return;
+ self->addAvatar(ids[0]);
+}
+
+
+void LLPanelLandAccess::addAvatar(LLUUID id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ parcel->addToAccessList(id, 0);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_ACCESS);
+
+ refresh();
+}
+
+
+// static
+void LLPanelLandAccess::onClickRemove(void* data)
+{
+ LLPanelLandAccess* self = (LLPanelLandAccess*)data;
+ if (!self) return;
+
+ LLScrollListItem* item = self->mListAccess->getFirstSelected();
+ if (!item) return;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ const LLUUID& agent_id = item->getUUID();
+
+ parcel->removeFromAccessList(agent_id);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_ACCESS);
+
+ self->refresh();
+}
+
+
+
+//---------------------------------------------------------------------------
+// LLPanelLandBan
+//---------------------------------------------------------------------------
+LLPanelLandBan::LLPanelLandBan()
+: LLPanel("land_ban_panel")
+{
+
+}
+
+
+
+BOOL LLPanelLandBan::postBuild()
+{
+
+ mCheck = LLUICtrlFactory::getCheckBoxByName(this, "LandBanCheck");
+ childSetCommitCallback("LandBanCheck", onCommitAny, this);
+
+ mList = LLUICtrlFactory::getNameListByName(this, "LandBanList");
+
+ mBtnAdd = LLUICtrlFactory::getButtonByName(this, "Add...");
+
+ mBtnAdd->setClickedCallback(onClickAdd, this);
+
+ mBtnRemove = LLUICtrlFactory::getButtonByName(this, "Remove");
+
+ mBtnRemove->setClickedCallback(onClickRemove, this);
+
+ mCheckDenyAnonymous = LLUICtrlFactory::getCheckBoxByName(this, "DenyAnonymousCheck");
+ childSetCommitCallback("DenyAnonymousCheck", onCommitAny, this);
+
+ mCheckDenyIdentified = LLUICtrlFactory::getCheckBoxByName(this, "DenyIdentifiedCheck");
+ childSetCommitCallback("DenyIdentifiedCheck", onCommitAny, this);
+
+ mCheckDenyTransacted = LLUICtrlFactory::getCheckBoxByName(this, "DenyTransactedCheck");
+ childSetCommitCallback("DenyTransactedCheck", onCommitAny, this);
+
+ return TRUE;
+
+}
+
+
+LLPanelLandBan::~LLPanelLandBan()
+{ }
+
+void LLPanelLandBan::refresh()
+{
+ mList->deleteAllItems();
+
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel)
+ {
+ char label[256];
+
+ // Display options
+
+ S32 count = parcel->mBanList.size();
+
+ BOOL use_ban = parcel->getParcelFlag(PF_USE_BAN_LIST);
+ mCheck->set( use_ban );
+
+ sprintf(label, "Ban these avatars: (%d listed, %d max)",
+ count, PARCEL_MAX_ACCESS_LIST);
+ mCheck->setLabel( label );
+
+ access_map_const_iterator cit = parcel->mBanList.begin();
+ access_map_const_iterator end = parcel->mBanList.end();
+ for ( ; cit != end; ++cit)
+ {
+ const LLAccessEntry& entry = (*cit).second;
+ LLString suffix;
+ if (entry.mTime != 0)
+ {
+ S32 now = time(NULL);
+ S32 seconds = entry.mTime - now;
+ if (seconds < 0) seconds = 0;
+ suffix.assign(" (");
+ if (seconds >= 120)
+ {
+ char buf[30];
+ sprintf(buf, "%d minutes", (seconds/60));
+ suffix.append(buf);
+ }
+ else if (seconds >= 60)
+ {
+ suffix.append("1 minute");
+ }
+ else
+ {
+ char buf[30];
+ sprintf(buf, "%d seconds", seconds);
+ suffix.append(buf);
+ }
+ suffix.append(" remaining)");
+ }
+ mList->addNameItem(entry.mID, ADD_BOTTOM, TRUE, suffix);
+ }
+
+ BOOL can_manage_banned = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_MANAGE_BANNED);
+ mCheck->setEnabled( can_manage_banned );
+ mCheckDenyAnonymous->setEnabled( FALSE );
+ mCheckDenyIdentified->setEnabled( FALSE );
+ mCheckDenyTransacted->setEnabled( FALSE );
+
+ if(parcel->getRegionDenyAnonymousOverride())
+ {
+ mCheckDenyAnonymous->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyAnonymous->setEnabled(TRUE);
+ mCheckDenyAnonymous->set(parcel->getParcelFlag(PF_DENY_ANONYMOUS));
+ }
+ if(parcel->getRegionDenyIdentifiedOverride())
+ {
+ mCheckDenyIdentified->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyIdentified->setEnabled(TRUE);
+ mCheckDenyIdentified->set(parcel->getParcelFlag(PF_DENY_IDENTIFIED));
+ }
+ if(parcel->getRegionDenyTransactedOverride())
+ {
+ mCheckDenyTransacted->set(TRUE);
+ }
+ else if(can_manage_banned)
+ {
+ mCheckDenyTransacted->setEnabled(TRUE);
+ mCheckDenyTransacted->set(parcel->getParcelFlag(PF_DENY_TRANSACTED));
+ }
+
+
+ BOOL enable_add = can_manage_banned && (count < PARCEL_MAX_ACCESS_LIST);
+ mBtnAdd->setEnabled(enable_add);
+
+ BOOL enable_remove = can_manage_banned && (count > 0);
+ mBtnRemove->setEnabled(enable_remove);
+ }
+ else
+ {
+ mCheck->set(FALSE);
+ mCheck->setLabel("Ban these avatars:");
+ mCheck->setEnabled(FALSE);
+ mBtnAdd->setEnabled(FALSE);
+ mBtnRemove->setEnabled(FALSE);
+ mCheckDenyAnonymous->set(FALSE);
+ mCheckDenyAnonymous->setEnabled(FALSE);
+ mCheckDenyIdentified->set(FALSE);
+ mCheckDenyIdentified->setEnabled(FALSE);
+ mCheckDenyTransacted->set(FALSE);
+ mCheckDenyTransacted->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLPanelLandBan::onCommitAny(LLUICtrl *ctrl, void *userdata)
+{
+ LLPanelLandBan *self = (LLPanelLandBan*)userdata;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel)
+ {
+ return;
+ }
+
+ // Extract data from UI
+ BOOL use_ban_list = self->mCheck->get();
+ BOOL deny_access_anonymous = self->mCheckDenyAnonymous->get();
+ BOOL deny_access_identified = self->mCheckDenyIdentified->get();
+ BOOL deny_access_transacted = self->mCheckDenyTransacted->get();
+
+ // Push data into current parcel
+ parcel->setParcelFlag(PF_USE_BAN_LIST, use_ban_list);
+ parcel->setParcelFlag(PF_DENY_ANONYMOUS, deny_access_anonymous);
+ parcel->setParcelFlag(PF_DENY_IDENTIFIED, deny_access_identified);
+ parcel->setParcelFlag(PF_DENY_TRANSACTED, deny_access_transacted);
+
+ // Send current parcel data upstream to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ // Might have changed properties, so let's redraw!
+ self->refresh();
+}
+
+// static
+void LLPanelLandBan::onClickAdd(void* data)
+{
+ LLPanelLandBan* panelp = (LLPanelLandBan*)data;
+ gFloaterView->getParentFloater(panelp)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, data) );
+}
+
+// static
+void LLPanelLandBan::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ LLPanelLandBan* self = (LLPanelLandBan*)userdata;
+ if (names.empty() || ids.empty()) return;
+ self->addAvatar(ids[0]);
+}
+
+
+void LLPanelLandBan::addAvatar(LLUUID id)
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ parcel->addToBanList(id, 0);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_BAN);
+
+ refresh();
+}
+
+
+// static
+void LLPanelLandBan::onClickRemove(void* data)
+{
+ LLPanelLandBan* self = (LLPanelLandBan*)data;
+ if (!self) return;
+
+ LLScrollListItem* item = self->mList->getFirstSelected();
+ if (!item) return;
+
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ if (!parcel) return;
+
+ const LLUUID& agent_id = item->getUUID();
+
+ parcel->removeFromBanList(agent_id);
+
+ gParcelMgr->sendParcelAccessListUpdate(AL_BAN);
+
+ self->refresh();
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandRenters
+//---------------------------------------------------------------------------
+LLPanelLandRenters::LLPanelLandRenters()
+: LLPanel("landrenters", LLRect(0,500,500,0))
+{
+ const S32 BTN_WIDTH = 64;
+
+ S32 x = LEFT;
+ S32 y = mRect.getHeight() - VPAD;
+
+ LLCheckBoxCtrl* check = NULL;
+ LLButton* btn = NULL;
+ LLNameListCtrl* list = NULL;
+
+ check = new LLCheckBoxCtrl("RentersCheck",
+ LLRect(RULER0, y, RIGHT, y-LINE),
+ "Rent space to these avatars:");
+ addChild(check);
+ mCheckRenters = check;
+
+ y -= VPAD + LINE;
+
+ const S32 ITEMS = 5;
+ const BOOL NO_MULTIPLE_SELECT = FALSE;
+
+ list = new LLNameListCtrl("RentersList",
+ LLRect(RULER05, y, RIGHT, y-LINE*ITEMS),
+ NULL, NULL,
+ NO_MULTIPLE_SELECT);
+ list->addSimpleItem("foo tester");
+ list->addSimpleItem("bar tester");
+ list->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(list);
+ mListRenters = list;
+
+ y -= VPAD + LINE*ITEMS;
+
+ x = RULER05;
+ btn = new LLButton("Add...",
+ LLRect(x, y, x+BTN_WIDTH, y-LINE),
+ "",
+ onClickAdd, this);
+ btn->setFont( LLFontGL::sSansSerifSmall );
+ btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(btn);
+ mBtnAddRenter = btn;
+
+ x += HPAD + BTN_WIDTH;
+
+ btn = new LLButton("Remove",
+ LLRect(x, y, x+BTN_WIDTH, y-LINE),
+ "",
+ onClickRemove, this);
+ btn->setFont( LLFontGL::sSansSerifSmall );
+ btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ addChild(btn);
+ mBtnRemoveRenter = btn;
+}
+
+LLPanelLandRenters::~LLPanelLandRenters()
+{ }
+
+void LLPanelLandRenters::refresh()
+{ }
+
+// static
+void LLPanelLandRenters::onClickAdd(void*)
+{
+}
+
+// static
+void LLPanelLandRenters::onClickRemove(void*)
+{
+}
+
+//---------------------------------------------------------------------------
+// LLPanelLandCovenant
+//---------------------------------------------------------------------------
+LLPanelLandCovenant::LLPanelLandCovenant()
+: LLPanel("land_covenant_panel")
+{
+}
+
+LLPanelLandCovenant::~LLPanelLandCovenant()
+{
+}
+
+BOOL LLPanelLandCovenant::postBuild()
+{
+ refresh();
+ return TRUE;
+}
+
+// virtual
+void LLPanelLandCovenant::refresh()
+{
+ LLViewerRegion* region = gParcelMgr->getSelectionRegion();
+ if(!region) return;
+
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ // send EstateCovenantInfo message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+}
+
+// static
+void LLPanelLandCovenant::updateCovenantText(const std::string &string)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLViewerTextEditor* editor = (LLViewerTextEditor*)self->getChildByName("covenant_editor");
+ if (editor)
+ {
+ editor->setHandleEditKeysDirectly(TRUE);
+ editor->setText(string);
+ }
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateEstateName(const std::string& name)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("estate_name_text");
+ if (editor) editor->setText(name);
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateLastModified(const std::string& text)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("covenant_timestamp_text");
+ if (editor) editor->setText(text);
+ }
+}
+
+// static
+void LLPanelLandCovenant::updateEstateOwnerName(const std::string& name)
+{
+ LLPanelLandCovenant* self = LLFloaterLand::getCurrentPanelLandCovenant();
+ if (self)
+ {
+ LLTextBox* editor = (LLTextBox*)self->getChildByName("estate_owner_text");
+ if (editor) editor->setText(name);
+ }
+}
diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h
new file mode 100644
index 0000000000..72f1e8ca8d
--- /dev/null
+++ b/indra/newview/llfloaterland.h
@@ -0,0 +1,472 @@
+/**
+ * @file llfloaterland.h
+ * @author James Cook
+ * @brief "About Land" floater, allowing display and editing of land parcel properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERLAND_H
+#define LL_LLFLOATERLAND_H
+
+#include <set>
+#include <vector>
+
+#include "llfloater.h"
+#include "llviewerimagelist.h"
+
+typedef std::set<LLUUID, lluuid_less> uuid_list_t;
+const F32 CACHE_REFRESH_TIME = 2.5f;
+
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLButton;
+class LLNameListCtrl;
+class LLSpinCtrl;
+class LLLineEditor;
+class LLRadioGroup;
+class LLParcelSelectionObserver;
+class LLTabContainer;
+class LLTextureCtrl;
+class LLViewerTextEditor;
+
+class LLPanelLandGeneral;
+class LLPanelLandObjects;
+class LLPanelLandOptions;
+class LLPanelLandMedia;
+class LLPanelLandAccess;
+class LLPanelLandBan;
+class LLPanelLandRenters;
+class LLPanelLandCovenant;
+
+class LLFloaterLand
+: public LLFloater
+{
+public:
+ // Call show() to open a land floater.
+ // Will query the viewer parcel manager to see what is selected.
+ static void show();
+ static BOOL floaterVisible() { return sInstance && sInstance->getVisible(); }
+ static void refreshAll();
+
+ static LLPanelLandObjects* getCurrentPanelLandObjects();
+ static LLPanelLandCovenant* getCurrentPanelLandCovenant();
+
+ // Returns TRUE, but does some early prep work for closing the window.
+ virtual BOOL canClose();
+
+ // Destroys itself on close.
+ virtual void onClose(bool app_quitting);
+
+protected:
+ // Does its own instance management, so clients not allowed
+ // to allocate or destroy.
+ LLFloaterLand();
+ virtual ~LLFloaterLand();
+
+ void refresh();
+
+
+ static void* createPanelLandGeneral(void* data);
+ static void* createPanelLandCovenant(void* data);
+ static void* createPanelLandObjects(void* data);
+ static void* createPanelLandOptions(void* data);
+ static void* createPanelLandMedia(void* data);
+ static void* createPanelLandAccess(void* data);
+ static void* createPanelLandBan(void* data);
+
+
+protected:
+ static LLFloaterLand* sInstance;
+ static LLParcelSelectionObserver* sObserver;
+ static S32 sLastTab;
+
+ LLTabContainer* mTabLand;
+ LLPanelLandGeneral* mPanelGeneral;
+ LLPanelLandObjects* mPanelObjects;
+ LLPanelLandOptions* mPanelOptions;
+ LLPanelLandMedia* mPanelMedia;
+ LLPanelLandAccess* mPanelAccess;
+ LLPanelLandBan* mPanelBan;
+ LLPanelLandCovenant* mPanelCovenant;
+ LLPanelLandRenters* mPanelRenters;
+
+public:
+ // When closing the dialog, we want to deselect the land. But when
+ // we send an update to the simulator, it usually replies with the
+ // parcel information, causing the land to be reselected. This allows
+ // us to suppress that behavior.
+ static BOOL sRequestReplyOnUpdate;
+};
+
+
+class LLPanelLandGeneral
+: public LLPanel
+{
+public:
+ LLPanelLandGeneral();
+ virtual ~LLPanelLandGeneral();
+ void refresh();
+ void refreshNames();
+ virtual void draw();
+
+ void setGroup(const LLUUID& group_id);
+ static void onClickProfile(void*);
+ static void onClickSetGroup(void*);
+ static void cbGroupID(LLUUID group_id, void* userdata);
+ static BOOL enableDeedToGroup(void*);
+ static void onClickDeed(void*);
+ static void onClickBuyLand(void* data);
+ static void onClickRelease(void*);
+ static void onClickReclaim(void*);
+ static void onClickBuyPass(void* deselect_when_done);
+ static BOOL enableBuyPass(void*);
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void finalizeCommit(void * userdata);
+ static void onForSaleChange(LLUICtrl *ctrl, void * userdata);
+ static void finalizeSetSellChange(void * userdata);
+ static void onSalePriceChange(LLUICtrl *ctrl, void * userdata);
+
+ static void cbBuyPass(S32 option, void*);
+
+ static void onClickSellLand(void* data);
+ static void onClickStopSellLand(void* data);
+ static void onClickSet(void* data);
+ static void onClickClear(void* data);
+ static void onClickShow(void* data);
+ static void callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void finalizeAvatarPick(void* data);
+ static void callbackHighlightTransferable(S32 option, void* userdata);
+ static void onClickStartAuction(void*);
+ // sale change confirmed when "is for sale", "sale price", "sell to whom" fields are changed
+ static void confirmSaleChange(S32 landSize, S32 salePrice, std::string authorizedName, void(*callback)(void*), void* userdata);
+ static void callbackConfirmSaleChange(S32 option, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ BOOL mUncheckedSell; // True only when verifying land information when land is for sale on sale info change
+
+ LLTextBox* mLabelName;
+ LLLineEditor* mEditName;
+ LLTextBox* mLabelDesc;
+ LLLineEditor* mEditDesc;
+
+ LLTextBox* mTextSalePending;
+
+ LLButton* mBtnDeedToGroup;
+ LLButton* mBtnSetGroup;
+
+ LLTextBox* mTextOwnerLabel;
+ LLTextBox* mTextOwner;
+ LLButton* mBtnProfile;
+
+ LLTextBox* mTextGroup;
+ LLTextBox* mTextGroupLabel;
+ LLTextBox* mTextClaimDateLabel;
+ LLTextBox* mTextClaimDate;
+
+ LLTextBox* mTextPriceLabel;
+ LLTextBox* mTextPrice;
+
+ LLCheckBoxCtrl* mCheckDeedToGroup;
+ LLCheckBoxCtrl* mCheckContributeWithDeed;
+
+ LLTextBox* mSaleInfoForSale1;
+ LLTextBox* mSaleInfoForSale2;
+ LLTextBox* mSaleInfoForSaleObjects;
+ LLTextBox* mSaleInfoForSaleNoObjects;
+ LLTextBox* mSaleInfoNotForSale;
+ LLButton* mBtnSellLand;
+ LLButton* mBtnStopSellLand;
+
+ LLTextBox* mTextDwell;
+
+ LLButton* mBtnBuyLand;
+ LLButton* mBtnBuyGroupLand;
+
+ // these buttons share the same location, but
+ // reclaim is in exactly the same visual place,
+ // ond is only shown for estate owners on their
+ // estate since they cannot release land.
+ LLButton* mBtnReleaseLand;
+ LLButton* mBtnReclaimLand;
+
+ LLButton* mBtnBuyPass;
+ LLButton* mBtnStartAuction;
+};
+
+class LLPanelLandObjects
+: public LLPanel
+{
+public:
+ LLPanelLandObjects();
+ virtual ~LLPanelLandObjects();
+ void refresh();
+ virtual void draw();
+
+ static void callbackReturnOwnerObjects(S32, void*);
+ static void callbackReturnGroupObjects(S32, void*);
+ static void callbackReturnOtherObjects(S32, void*);
+ static void callbackReturnOwnerList(S32, void*);
+
+ static void clickShowCore(S32 return_type, uuid_list_t* list = 0);
+ static void onClickShowOwnerObjects(void*);
+ static void onClickShowGroupObjects(void*);
+ static void onClickShowOtherObjects(void*);
+
+ static void onClickReturnOwnerObjects(void*);
+ static void onClickReturnGroupObjects(void*);
+ static void onClickReturnOtherObjects(void*);
+ static void onClickReturnOwnerList(void*);
+ static void onClickRefresh(void*);
+ static void onClickType(void*);
+ static void onClickName(void*);
+ static void onClickDesc(void*);
+
+ static void onDoubleClickOwner(void*);
+
+ static void onCommitList(LLUICtrl* ctrl, void* data);
+ static void onLostFocus(LLLineEditor* caller, void* user_data);
+
+ static void processParcelObjectOwnersReply(LLMessageSystem *msg, void **);
+
+ virtual BOOL postBuild();
+
+protected:
+ void sortBtnCore(S32 column);
+
+ LLTextBox *mSWTotalObjectsLabel;
+ LLTextBox *mSWTotalObjects;
+
+ LLTextBox *mParcelObjectBonus;
+
+ LLTextBox *mObjectContributionLabel;
+ LLTextBox *mObjectContribution;
+ LLTextBox *mTotalObjectsLabel;
+ LLTextBox *mTotalObjects;
+
+ LLTextBox *mOwnerObjectsLabel;
+ LLTextBox *mOwnerObjects;
+ LLButton *mBtnShowOwnerObjects;
+ LLButton *mBtnReturnOwnerObjects;
+
+ LLTextBox *mGroupObjectsLabel;
+ LLTextBox *mGroupObjects;
+ LLButton *mBtnShowGroupObjects;
+ LLButton *mBtnReturnGroupObjects;
+
+ LLTextBox *mOtherObjectsLabel;
+ LLTextBox *mOtherObjects;
+ LLButton *mBtnShowOtherObjects;
+ LLButton *mBtnReturnOtherObjects;
+
+ LLTextBox *mSelectedObjectsLabel;
+ LLTextBox *mSelectedObjects;
+
+ LLTextBox *mCleanOtherObjectsLabel;
+ LLLineEditor *mCleanOtherObjectsTime;
+ S32 mOtherTime;
+
+ LLTextBox *mOwnerListText;
+ LLButton *mBtnRefresh;
+ LLButton *mBtnReturnOwnerList;
+ LLButton *mBtnType; // column 0
+ LLButton *mBtnName; // column 2
+ LLButton *mBtnDescription; // column 3
+ LLNameListCtrl *mOwnerList;
+
+ LLPointer<LLViewerImage> mIconAvatarOnline;
+ LLPointer<LLViewerImage> mIconAvatarOffline;
+ LLPointer<LLViewerImage> mIconGroup;
+
+ U32 mCurrentSortColumn;
+ BOOL mCurrentSortAscending;
+ S32 mColWidth[12];
+
+ BOOL mFirstReply;
+
+ uuid_list_t mSelectedOwners;
+ LLString mSelectedName;
+ S32 mSelectedCount;
+ BOOL mSelectedIsGroup;
+};
+
+
+class LLPanelLandOptions
+: public LLPanel
+{
+public:
+ LLPanelLandOptions();
+ virtual ~LLPanelLandOptions();
+ void refresh();
+
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickSet(void* userdata);
+ static void onClickClear(void* userdata);
+ static void onClickPublishHelp(void*);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLCheckBoxCtrl* mCheckEditObjects;
+ LLCheckBoxCtrl* mCheckEditGroupObjects;
+ LLCheckBoxCtrl* mCheckAllObjectEntry;
+ LLCheckBoxCtrl* mCheckGroupObjectEntry;
+ LLCheckBoxCtrl* mCheckEditLand;
+ LLCheckBoxCtrl* mCheckSafe;
+ LLCheckBoxCtrl* mCheckFly;
+ LLCheckBoxCtrl* mCheckGroupScripts;
+ LLCheckBoxCtrl* mCheckOtherScripts;
+ LLCheckBoxCtrl* mCheckLandmark;
+
+ LLCheckBoxCtrl* mCheckShowDirectory;
+ LLComboBox* mCategoryCombo;
+ LLComboBox* mLandingTypeCombo;
+
+ LLTextureCtrl* mSnapshotCtrl;
+
+ LLTextBox* mLocationText;
+ LLButton* mSetBtn;
+ LLButton* mClearBtn;
+
+ LLCheckBoxCtrl *mAllowPublishCtrl;
+ LLCheckBoxCtrl *mMatureCtrl;
+ LLCheckBoxCtrl *mPushRestrictionCtrl;
+ LLButton *mPublishHelpButton;
+};
+
+
+class LLPanelLandMedia
+: public LLPanel
+{
+public:
+ LLPanelLandMedia();
+ virtual ~LLPanelLandMedia();
+ void refresh();
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickStopMedia ( void* data );
+ static void onClickStartMedia ( void* data );
+
+ virtual BOOL postBuild();
+
+protected:
+ LLCheckBoxCtrl* mCheckSoundLocal;
+ LLLineEditor* mMusicURLEdit;
+ LLLineEditor* mMediaURLEdit;
+ LLTextureCtrl* mMediaTextureCtrl;
+ LLCheckBoxCtrl* mMediaAutoScaleCheck;
+ //LLButton* mMediaStopButton;
+ //LLButton* mMediaStartButton;
+};
+
+
+
+class LLPanelLandAccess
+: public LLPanel
+{
+public:
+ LLPanelLandAccess();
+ virtual ~LLPanelLandAccess();
+ void refresh();
+ void refreshNames();
+ virtual void draw();
+
+ void addAvatar(LLUUID id);
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onAccessLevelChange(LLUICtrl* ctrl, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLTextBox* mLabelTitle;
+
+ LLCheckBoxCtrl* mCheckGroup;
+
+ LLCheckBoxCtrl* mCheckAccess;
+ LLNameListCtrl* mListAccess;
+ LLButton* mBtnAddAccess;
+ LLButton* mBtnRemoveAccess;
+
+ LLCheckBoxCtrl* mCheckPass;
+ LLSpinCtrl* mSpinPrice;
+ LLSpinCtrl* mSpinHours;
+
+ LLCheckBoxCtrl* mCheckIdentified;
+ LLCheckBoxCtrl* mCheckTransacted;
+ LLRadioGroup* mCheckStatusLevel;
+
+};
+
+
+class LLPanelLandBan
+: public LLPanel
+{
+public:
+ LLPanelLandBan();
+ virtual ~LLPanelLandBan();
+ void refresh();
+
+ void addAvatar(LLUUID id);
+
+ static void onCommitAny(LLUICtrl* ctrl, void *userdata);
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+
+ virtual BOOL postBuild();
+
+protected:
+ LLTextBox* mLabelTitle;
+
+ LLCheckBoxCtrl* mCheck;
+ LLNameListCtrl* mList;
+ LLButton* mBtnAdd;
+ LLButton* mBtnRemove;
+ LLCheckBoxCtrl* mCheckDenyAnonymous;
+ LLCheckBoxCtrl* mCheckDenyIdentified;
+ LLCheckBoxCtrl* mCheckDenyTransacted;
+};
+
+
+class LLPanelLandRenters
+: public LLPanel
+{
+public:
+ LLPanelLandRenters();
+ virtual ~LLPanelLandRenters();
+ void refresh();
+
+ static void onClickAdd(void*);
+ static void onClickRemove(void*);
+
+protected:
+ LLCheckBoxCtrl* mCheckRenters;
+ LLNameListCtrl* mListRenters;
+ LLButton* mBtnAddRenter;
+ LLButton* mBtnRemoveRenter;
+};
+
+class LLPanelLandCovenant
+: public LLPanel
+{
+public:
+ LLPanelLandCovenant();
+ virtual ~LLPanelLandCovenant();
+ virtual BOOL postBuild();
+ void refresh();
+ static void updateCovenantText(const std::string& string);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+};
+
+#endif
diff --git a/indra/newview/llfloaterlandholdings.cpp b/indra/newview/llfloaterlandholdings.cpp
new file mode 100644
index 0000000000..496c31d219
--- /dev/null
+++ b/indra/newview/llfloaterlandholdings.cpp
@@ -0,0 +1,305 @@
+/**
+ * @file llfloaterlandholdings.cpp
+ * @brief "My Land" floater showing all your land parcels.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterlandholdings.h"
+
+#include "indra_constants.h"
+#include "llfontgl.h"
+#include "llqueryflags.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterworldmap.h"
+//#include "llinventoryview.h" // for mOpenNextNewItem
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llscrolllistctrl.h"
+#include "llbutton.h"
+#include "lluiconstants.h"
+#include "llviewermessage.h"
+#include "llvieweruictrlfactory.h"
+
+// statics
+LLFloaterLandHoldings* LLFloaterLandHoldings::sInstance = NULL;
+
+
+// static
+void LLFloaterLandHoldings::show(void*)
+{
+ LLFloaterLandHoldings* floater = new LLFloaterLandHoldings();
+ gUICtrlFactory->buildFloater(floater, "floater_land_holdings.xml");
+ floater->center();
+
+ // query_id null is known to be us
+ const LLUUID& query_id = LLUUID::null;
+
+ // look only for parcels we own
+ U32 query_flags = DFQ_AGENT_OWNED;
+
+ send_places_query(query_id,
+ LLUUID::null,
+ "",
+ query_flags,
+ LLParcel::C_ANY,
+ "");
+
+ // TODO: request updated money balance?
+ floater->open();
+}
+
+
+// protected
+LLFloaterLandHoldings::LLFloaterLandHoldings()
+: LLFloater("land holdings floater"),
+ mActualArea(0),
+ mBillableArea(0),
+ mFirstPacketReceived(FALSE),
+ mSortColumn(""),
+ mSortAscending(TRUE)
+{
+ // Instance management.
+ sInstance = this;
+}
+
+BOOL LLFloaterLandHoldings::postBuild()
+{
+ childSetAction("Teleport", onClickTeleport, this);
+ childSetAction("Show on Map", onClickMap, this);
+
+ // Grant list
+ childSetDoubleClickCallback("grant list", onGrantList);
+ childSetUserData("grant list", this);
+
+ LLCtrlListInterface *list = childGetListInterface("grant list");
+ if (!list) return TRUE;
+
+ S32 count = gAgent.mGroups.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLUUID id(gAgent.mGroups.get(i).mID);
+
+ LLSD element;
+ element["id"] = id;
+ element["columns"][0]["column"] = "group";
+ element["columns"][0]["value"] = gAgent.mGroups.get(i).mName;
+ element["columns"][0]["font"] = "SANSSERIF";
+
+ element["columns"][1]["column"] = "area";
+ element["columns"][1]["value"] = llformat("%d sq. meters", gAgent.mGroups.get(i).mContribution);
+ element["columns"][1]["font"] = "SANSSERIF";
+
+ list->addElement(element, ADD_SORTED);
+ }
+
+ return TRUE;
+}
+
+
+// protected
+LLFloaterLandHoldings::~LLFloaterLandHoldings()
+{
+ sInstance = NULL;
+}
+
+
+void LLFloaterLandHoldings::draw()
+{
+ refresh();
+
+ LLFloater::draw();
+}
+
+
+// public
+void LLFloaterLandHoldings::refresh()
+{
+ LLCtrlSelectionInterface *list = childGetSelectionInterface("parcel list");
+ BOOL enable_btns = FALSE;
+ if (list && list->getFirstSelectedIndex()> -1)
+ {
+ enable_btns = TRUE;
+ }
+
+ childSetEnabled("Teleport", enable_btns);
+ childSetEnabled("Show on Map", enable_btns);
+
+ refreshAggregates();
+}
+
+
+// static
+void LLFloaterLandHoldings::processPlacesReply(LLMessageSystem* msg, void**)
+{
+ LLFloaterLandHoldings* self = sInstance;
+
+ // Is this packet from an old, closed window?
+ if (!self)
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("parcel list");
+ if (!list) return;
+
+ // If this is the first packet, clear out the "loading..." indicator
+ if (!self->mFirstPacketReceived)
+ {
+ self->mFirstPacketReceived = TRUE;
+ list->operateOnAll(LLCtrlSelectionInterface::OP_DELETE);
+ }
+
+ LLUUID owner_id;
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ char sim_name[MAX_STRING];
+
+ S32 i;
+ S32 count = msg->getNumberOfBlocks("QueryData");
+ for (i = 0; i < count; i++)
+ {
+ msg->getUUID("QueryData", "OwnerID", owner_id, i);
+ msg->getString("QueryData", "Name", MAX_STRING, name, i);
+ msg->getString("QueryData", "Desc", MAX_STRING, desc, i);
+ msg->getS32("QueryData", "ActualArea", actual_area, i);
+ msg->getS32("QueryData", "BillableArea", billable_area, i);
+ msg->getU8("QueryData", "Flags", flags, i);
+ msg->getF32("QueryData", "GlobalX", global_x, i);
+ msg->getF32("QueryData", "GlobalY", global_y, i);
+ msg->getString("QueryData", "SimName", MAX_STRING, sim_name, i);
+
+ 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;
+
+ char location[MAX_STRING];
+ sprintf(location, "%s (%d, %d)", sim_name, region_x, region_y);
+
+ char area[MAX_STRING];
+ if(billable_area == actual_area)
+ {
+ sprintf(area, "%d", billable_area);
+ }
+ else
+ {
+ sprintf(area, "%d / %d", billable_area, actual_area);
+ }
+
+ char hidden[MAX_STRING];
+ sprintf(hidden, "%f %f", global_x, global_y);
+
+ LLSD element;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = name;
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][1]["column"] = "location";
+ element["columns"][1]["value"] = location;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][2]["column"] = "area";
+ element["columns"][2]["value"] = area;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][3]["column"] = "hidden";
+ element["columns"][3]["value"] = hidden;
+ element["columns"][3]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+ }
+
+ self->refreshAggregates();
+}
+
+void LLFloaterLandHoldings::buttonCore(S32 which)
+{
+ LLScrollListCtrl *list = LLUICtrlFactory::getScrollListByName(this, "parcel list");
+ if (!list) return;
+
+ S32 index = list->getFirstSelectedIndex();
+ if (index < 0) return;
+
+ LLString location = list->getSimpleSelectedItem(3);
+
+ F32 global_x = 0.f;
+ F32 global_y = 0.f;
+ sscanf(location.c_str(), "%f %f", &global_x, &global_y);
+
+ // Hack: Use the agent's z-height
+ F64 global_z = gAgent.getPositionGlobal().mdV[VZ];
+
+ LLVector3d pos_global(global_x, global_y, global_z);
+
+ switch(which)
+ {
+ case 0:
+ gAgent.teleportViaLocation(pos_global);
+ gFloaterWorldMap->trackLocation(pos_global);
+ break;
+ case 1:
+ gFloaterWorldMap->trackLocation(pos_global);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLFloaterLandHoldings::onClickTeleport(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ self->buttonCore(0);
+ self->close();
+}
+
+
+// static
+void LLFloaterLandHoldings::onClickMap(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ self->buttonCore(1);
+}
+
+// static
+void LLFloaterLandHoldings::onGrantList(void* data)
+{
+ LLFloaterLandHoldings* self = (LLFloaterLandHoldings*)data;
+ LLCtrlSelectionInterface *list = self->childGetSelectionInterface("grant list");
+ if (!list) return;
+ LLUUID group_id = list->getCurrentID();
+ if (group_id.notNull())
+ {
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+}
+
+void LLFloaterLandHoldings::refreshAggregates()
+{
+ S32 allowed_area = gStatusBar->getSquareMetersCredit();
+ S32 current_area = gStatusBar->getSquareMetersCommitted();
+ S32 available_area = gStatusBar->getSquareMetersLeft();
+
+ char buffer[MAX_STRING];
+
+ sprintf(buffer, "%d sq. meters", allowed_area);
+ childSetValue("allowed_text", LLSD(buffer));
+
+ sprintf(buffer, "%d sq. meters", current_area);
+ childSetValue("current_text", LLSD(buffer));
+
+ sprintf(buffer, "%d sq. meters", available_area);
+ childSetValue("available_text", LLSD(buffer));
+}
diff --git a/indra/newview/llfloaterlandholdings.h b/indra/newview/llfloaterlandholdings.h
new file mode 100644
index 0000000000..4fb3d60451
--- /dev/null
+++ b/indra/newview/llfloaterlandholdings.h
@@ -0,0 +1,62 @@
+/**
+ * @file llfloaterlandholdings.h
+ * @brief "My Land" floater showing all your land parcels.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERLANDHOLDINGS_H
+#define LL_LLFLOATERLANDHOLDINGS_H
+
+#include "llfloater.h"
+
+class LLMessageSystem;
+class LLTextBox;
+class LLScrollListCtrl;
+class LLButton;
+
+class LLFloaterLandHoldings
+: public LLFloater
+{
+public:
+ BOOL postBuild();
+
+ static void show(void*);
+
+ virtual void draw();
+
+ void refresh();
+
+ void buttonCore(S32 which);
+
+ static void processPlacesReply(LLMessageSystem* msg, void**);
+
+ static void onClickTeleport(void*);
+ static void onClickMap(void*);
+ static void onClickLandmark(void*);
+
+ static void onGrantList(void* data);
+
+protected:
+ LLFloaterLandHoldings();
+ virtual ~LLFloaterLandHoldings();
+
+ void refreshAggregates();
+
+protected:
+ static LLFloaterLandHoldings* sInstance;
+
+ // Sum up as packets arrive the total holdings
+ S32 mActualArea;
+ S32 mBillableArea;
+
+ // Has a packet of data been received?
+ // Used to clear out the mParcelList's "Loading..." indicator
+ BOOL mFirstPacketReceived;
+
+ LLString mSortColumn;
+ BOOL mSortAscending;
+};
+
+#endif
diff --git a/indra/newview/llfloatermap.cpp b/indra/newview/llfloatermap.cpp
new file mode 100644
index 0000000000..bbac916331
--- /dev/null
+++ b/indra/newview/llfloatermap.cpp
@@ -0,0 +1,199 @@
+/**
+ * @file llfloatermap.cpp
+ * @brief The "mini-map" or radar in the upper right part of the screen.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// self include
+#include "llfloatermap.h"
+
+// Library includes
+#include "llfontgl.h"
+#include "llinventory.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "lleconomy.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llnetmap.h"
+#include "llregionhandle.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llsky.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llstatgraph.h"
+#include "llstatusbar.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+
+#include "llglheaders.h"
+
+//
+// Constants
+//
+const S32 LEGEND_SIZE = 16;
+
+const S32 HPAD = 4;
+
+const S32 MAP_COMBOBOX_WIDTH = 128;
+const S32 MAP_COMBOBOX_HEIGHT = 20;
+const S32 GO_BTN_WIDTH = 20;
+const S32 SLIDER_WIDTH = LEGEND_SIZE + MAP_COMBOBOX_WIDTH + HPAD + GO_BTN_WIDTH - HPAD;
+const S32 SLIDER_HEIGHT = 16;
+
+const S32 SIM_STAT_WIDTH = 8;
+
+const S32 MAP_SCALE_MIN = 35; // in pixels per sim
+const S32 MAP_SCALE_MAX = 180; // in pixels per sim
+const S32 MAP_SCALE_INCREMENT = 5;
+
+const char MAP_TITLE[] = "";
+
+const S32 NETMAP_MIN_WIDTH = 128;
+const S32 NETMAP_MIN_HEIGHT = 128;
+
+const S32 FLOATERMAP_MIN_WIDTH =
+ SLIDER_WIDTH +
+ 2 * (HPAD + LLPANEL_BORDER_WIDTH);
+
+const S32 MORE_BTN_VPAD = 2;
+
+const S32 SPIN_VPAD = 4;
+
+const S32 TRACKING_LABEL_HEIGHT = 16;
+
+const S32 FLOATERMAP_MIN_HEIGHT_LARGE = 60;
+
+//
+// Globals
+//
+LLFloaterMap *gFloaterMap = NULL;
+
+
+
+
+//
+// Member functions
+//
+
+LLFloaterMap::LLFloaterMap(const std::string& name)
+ :
+ LLFloater(name,
+ "FloaterMapRect",
+ MAP_TITLE,
+ TRUE,
+ FLOATERMAP_MIN_WIDTH,
+ FLOATERMAP_MIN_HEIGHT_LARGE,
+ FALSE,
+ FALSE,
+ TRUE) // close button
+{
+ const S32 LEFT = LLPANEL_BORDER_WIDTH;
+ const S32 TOP = mRect.getHeight();
+
+ S32 y = 0;
+
+ // Map itself
+ LLRect map_rect(
+ LEFT,
+ TOP - LLPANEL_BORDER_WIDTH,
+ mRect.getWidth() - LLPANEL_BORDER_WIDTH,
+ y );
+ LLColor4 bg_color = gColors.getColor( "NetMapBackgroundColor" );
+ mMap = new LLNetMap("Net Map", map_rect, bg_color);
+ mMap->setFollowsAll();
+ addChildAtEnd(mMap);
+
+ // Get the drag handle all the way in back
+ removeChild(mDragHandle);
+ addChildAtEnd(mDragHandle);
+
+ setIsChrome(TRUE);
+}
+
+
+LLFloaterMap::~LLFloaterMap()
+{
+}
+
+
+// virtual
+void LLFloaterMap::setVisible(BOOL visible)
+{
+ LLFloater::setVisible(visible);
+
+ gSavedSettings.setBOOL("ShowMiniMap", visible);
+}
+
+
+// virtual
+void LLFloaterMap::onClose(bool app_quitting)
+{
+ LLFloater::setVisible(FALSE);
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowMiniMap", FALSE);
+ }
+}
+
+BOOL LLFloaterMap::canClose()
+{
+ return !gQuit;
+}
+
+
+// virtual
+void LLFloaterMap::draw()
+{
+ if( getVisible() )
+ {
+ // Note: we can't just gAgent.check cameraMouselook() because the transition states are wrong.
+ if( gAgent.cameraMouselook())
+ {
+ setMouseOpaque(FALSE);
+ mDragHandle->setMouseOpaque(FALSE);
+
+ drawChild(mMap);
+ }
+ else
+ {
+ setMouseOpaque(TRUE);
+ mDragHandle->setMouseOpaque(TRUE);
+
+ LLFloater::draw();
+ }
+ }
+}
+
+// static
+void LLFloaterMap::toggle(void*)
+{
+ if( gFloaterMap )
+ {
+ if (gFloaterMap->getVisible())
+ {
+ gFloaterMap->close();
+ }
+ else
+ {
+ gFloaterMap->open();
+ }
+ }
+}
diff --git a/indra/newview/llfloatermap.h b/indra/newview/llfloatermap.h
new file mode 100644
index 0000000000..00a483d17c
--- /dev/null
+++ b/indra/newview/llfloatermap.h
@@ -0,0 +1,50 @@
+/**
+ * @file llfloatermap.h
+ * @brief The "mini-map" or radar in the upper right part of the screen.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERMAP_H
+#define LL_LLFLOATERMAP_H
+
+#include "llfloater.h"
+
+class LLNetMap;
+class LLSliderCtrl;
+class LLStatGraph;
+class LLTextBox;
+class LLUICtrl;
+class LLViewerImage;
+
+//
+// Classes
+//
+class LLFloaterMap
+: public LLFloater
+{
+public:
+ LLFloaterMap(const std::string& name);
+ virtual ~LLFloaterMap();
+
+ static void toggle(void*);
+
+ /*virtual*/ void setVisible(BOOL visible);
+ /*virtual*/ void draw();
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ BOOL canClose();
+
+protected:
+ LLNetMap* mMap;
+};
+
+
+//
+// Globals
+//
+
+extern LLFloaterMap *gFloaterMap;
+
+
+#endif // LL_LLFLOATERMAP_H
diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp
new file mode 100644
index 0000000000..a8d90b082e
--- /dev/null
+++ b/indra/newview/llfloaternamedesc.cpp
@@ -0,0 +1,202 @@
+/**
+ * @file llfloaternamedesc.cpp
+ * @brief LLFloaterNameDesc class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaternamedesc.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+#include "llradiogroup.h"
+#include "llassetstorage.h"
+#include "lldbstrings.h"
+#include "lldir.h"
+#include "llviewercontrol.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+
+const S32 PREVIEW_LINE_HEIGHT = 19;
+const S32 PREVIEW_CLOSE_BOX_SIZE = 16;
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_VPAD = 2;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREVIEW_HEADER_SIZE = 3 * PREVIEW_LINE_HEIGHT + PREVIEW_VPAD;
+const S32 PREF_BUTTON_WIDTH = 64;
+const S32 PREF_BUTTON_HEIGHT = 16;
+
+//-----------------------------------------------------------------------------
+// LLFloaterNameDesc()
+//-----------------------------------------------------------------------------
+LLFloaterNameDesc::LLFloaterNameDesc(const std::string& filename )
+ :
+ LLFloater("Name/Description Floater")
+{
+ mFilenameAndPath = filename;
+ mFilename.assign(strrchr( filename.c_str(), gDirUtilp->getDirDelimiter()[0]) + 1);
+ // SL-5521 Maintain capitalization of filename when making the inventory item. JC
+ //LLString::toLower(mFilename);
+ mIsAudio = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// postBuild()
+//-----------------------------------------------------------------------------
+BOOL LLFloaterNameDesc::postBuild()
+{
+ LLRect r;
+
+ LLString asset_name = mFilename;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+ else
+ if( !stricmp( end_p, ".wav") )
+ {
+ mIsAudio = TRUE;
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ setTitle(mFilename);
+
+ centerWindow();
+
+ S32 line_width = mRect.getWidth() - 2 * PREVIEW_HPAD;
+ S32 y = mRect.getHeight() - PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+ y -= PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+
+ childSetCommitCallback("name_form", doCommit, this);
+ childSetValue("name_form", LLSD(asset_name));
+
+ LLLineEditor *NameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "name_form");
+ if (NameEditor)
+ {
+ NameEditor->setMaxTextLength(DB_INV_ITEM_NAME_STR_LEN);
+ NameEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f);
+ y -= PREVIEW_LINE_HEIGHT;
+
+ r.setLeftTopAndSize( PREVIEW_HPAD, y, line_width, PREVIEW_LINE_HEIGHT );
+ childSetCommitCallback("description_form", doCommit, this);
+ LLLineEditor *DescEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "description_form");
+ if (DescEditor)
+ {
+ DescEditor->setMaxTextLength(DB_INV_ITEM_DESC_STR_LEN);
+ DescEditor->setPrevalidate(&LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ y -= llfloor(PREVIEW_LINE_HEIGHT * 1.2f);
+
+ if (mIsAudio)
+ {
+ LLSD bitrate = gSavedSettings.getS32("AudioDefaultBitrate");
+
+ childSetValue("bitrate", bitrate);
+ }
+
+ // Cancel button
+ childSetAction("cancel_btn", onBtnCancel, this);
+
+ // OK button
+ childSetAction("ok_btn", onBtnOK, this);
+ setDefaultBtn("ok_btn");
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLFloaterNameDesc()
+//-----------------------------------------------------------------------------
+LLFloaterNameDesc::~LLFloaterNameDesc()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+}
+
+//-----------------------------------------------------------------------------
+// centerWindow()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::centerWindow()
+{
+ LLRect window_rect = gViewerWindow->getRootView()->getRect();
+
+ S32 dialog_left = window_rect.mLeft + (window_rect.getWidth() - mRect.getWidth()) / 2;
+ S32 dialog_bottom = window_rect.mBottom + (window_rect.getHeight() - mRect.getHeight()) / 2;
+
+ translate( dialog_left - mRect.mLeft, dialog_bottom - mRect.mBottom );
+}
+
+// Sub-classes should override this function if they allow editing
+//-----------------------------------------------------------------------------
+// onCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onCommit()
+{
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onCommit()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::doCommit( class LLUICtrl *, void* userdata )
+{
+ LLFloaterNameDesc* self = (LLFloaterNameDesc*) userdata;
+ self->onCommit();
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onBtnOK()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onBtnOK( void* userdata )
+{
+ LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
+
+ fp->childDisable("ok_btn"); // don't allow inadvertent extra uploads
+
+ S32 bitrate = 0;
+
+ if (fp->mIsAudio)
+ {
+ bitrate = fp->childGetValue("bitrate").asInteger();
+ }
+ upload_new_resource(fp->mFilenameAndPath, // file
+ fp->childGetValue("name_form").asString(),
+ fp->childGetValue("description_form").asString(),
+ bitrate, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ fp->onClose(false);
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onBtnCancel()
+//-----------------------------------------------------------------------------
+void LLFloaterNameDesc::onBtnCancel( void* userdata )
+{
+ LLFloaterNameDesc *fp =(LLFloaterNameDesc *)userdata;
+ fp->onClose(false);
+}
diff --git a/indra/newview/llfloaternamedesc.h b/indra/newview/llfloaternamedesc.h
new file mode 100644
index 0000000000..b6f4a489f3
--- /dev/null
+++ b/indra/newview/llfloaternamedesc.h
@@ -0,0 +1,42 @@
+/**
+ * @file llfloaternamedesc.h
+ * @brief LLFloaterNameDesc class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERNAMEDESC_H
+#define LL_LLFLOATERNAMEDESC_H
+
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llstring.h"
+
+class LLLineEditor;
+class LLButton;
+class LLRadioGroup;
+
+class LLFloaterNameDesc : public LLFloater
+{
+public:
+ LLFloaterNameDesc(const std::string& filename);
+ virtual ~LLFloaterNameDesc();
+ virtual BOOL postBuild();
+
+ static void doCommit(class LLUICtrl *, void* userdata);
+protected:
+ virtual void onCommit();
+ virtual void centerWindow();
+
+protected:
+ BOOL mIsAudio;
+
+ LLString mFilenameAndPath;
+ LLString mFilename;
+
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+};
+
+#endif // LL_LLFLOATERNAMEDESC_H
diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp
new file mode 100644
index 0000000000..fb5c795ac3
--- /dev/null
+++ b/indra/newview/llfloateropenobject.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llfloateropenobject.cpp
+ * @brief LLFloaterOpenObject class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shows the contents of an object.
+ * A floater wrapper for llpanelinventory
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloateropenobject.h"
+
+#include "llcachename.h"
+#include "llbutton.h"
+#include "lltextbox.h"
+
+#include "llagent.h" // for agent id
+#include "llalertdialog.h"
+#include "llinventoryview.h"
+#include "llinventorymodel.h"
+#include "llpanelinventory.h"
+#include "llselectmgr.h"
+#include "lluiconstants.h"
+#include "llviewerobject.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+
+LLFloaterOpenObject* LLFloaterOpenObject::sInstance = NULL;
+
+LLFloaterOpenObject::LLFloaterOpenObject()
+: LLFloater("object_contents"),
+ mPanelInventory(NULL),
+ mDirty(TRUE)
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["object_contents"] = LLCallbackMap(createPanelInventory, this);
+ gUICtrlFactory->buildFloater(this,"floater_openobject.xml",&factory_map);
+
+ childSetAction("copy_to_inventory_button", onClickMoveToInventory, this);
+ childSetAction("copy_and_wear_button", onClickMoveAndWear, this);
+ childSetTextArg("object_name", "[DESC]", "Object");
+}
+
+LLFloaterOpenObject::~LLFloaterOpenObject()
+{
+ gSelectMgr->deselectAll();
+ sInstance = NULL;
+}
+
+void LLFloaterOpenObject::refresh()
+{
+ mPanelInventory->refresh();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ std::string name = node->mName;
+ childSetTextArg("object_name", "[DESC]", name);
+ }
+}
+
+void LLFloaterOpenObject::draw()
+{
+ if (mDirty)
+ {
+ refresh();
+ mDirty = FALSE;
+ }
+ LLFloater::draw();
+}
+
+// static
+void LLFloaterOpenObject::dirty()
+{
+ if (sInstance) sInstance->mDirty = TRUE;
+}
+
+// static
+void LLFloaterOpenObject::show()
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("UnableToViewContentsMoreThanOne");
+ return;
+ }
+
+ // Create a new instance only if needed
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterOpenObject();
+ sInstance->center();
+ }
+
+ sInstance->open();
+ sInstance->setFocus(TRUE);
+}
+
+
+// static
+void LLFloaterOpenObject::moveToInventory(bool wear)
+{
+ if (gSelectMgr->getRootObjectCount() != 1)
+ {
+ gViewerWindow->alertXml("OnlyCopyContentsOfSingleItem");
+ return;
+ }
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return;
+ LLViewerObject* object = node->getObject();
+ if (!object) return;
+
+ LLUUID object_id = object->getID();
+ std::string name = node->mName;
+
+ // Either create a sub-folder of clothing, or of the root folder.
+ LLUUID parent_category_id;
+ if (wear)
+ {
+ parent_category_id = gInventory.findCategoryUUIDForType(
+ LLAssetType::AT_CLOTHING);
+ }
+ else
+ {
+ parent_category_id = gAgent.getInventoryRootID();
+ }
+ LLUUID category_id = gInventory.createNewCategory(parent_category_id,
+ LLAssetType::AT_NONE,
+ name);
+
+ LLCatAndWear* data = new LLCatAndWear;
+ data->mCatID = category_id;
+ data->mWear = wear;
+
+ // Copy and/or move the items into the newly created folder.
+ // Ignore any "you're going to break this item" messages.
+ BOOL success = move_inv_category_world_to_agent(object_id, category_id, TRUE,
+ callbackMoveInventory,
+ (void*)data);
+ if (!success)
+ {
+ delete data;
+ data = NULL;
+
+ gViewerWindow->alertXml("OpenObjectCannotCopy");
+ }
+}
+
+// static
+void LLFloaterOpenObject::callbackMoveInventory(S32 result, void* data)
+{
+ LLCatAndWear* cat = (LLCatAndWear*)data;
+
+ if (result == 0)
+ {
+ LLInventoryView::showAgentInventory();
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if (view)
+ {
+ view->getPanel()->setSelection(cat->mCatID, TAKE_FOCUS_NO);
+ }
+ }
+
+ delete cat;
+}
+
+
+// static
+void LLFloaterOpenObject::onClickMoveToInventory(void* data)
+{
+ LLFloaterOpenObject* self = (LLFloaterOpenObject*)data;
+ moveToInventory(false);
+ self->close();
+}
+
+// static
+void LLFloaterOpenObject::onClickMoveAndWear(void* data)
+{
+ LLFloaterOpenObject* self = (LLFloaterOpenObject*)data;
+ moveToInventory(true);
+ self->close();
+}
+
+//static
+void* LLFloaterOpenObject::createPanelInventory(void* data)
+{
+ LLFloaterOpenObject* floater = (LLFloaterOpenObject*)data;
+ floater->mPanelInventory = new LLPanelInventory("Object Contents", LLRect());
+ return floater->mPanelInventory;
+}
diff --git a/indra/newview/llfloateropenobject.h b/indra/newview/llfloateropenobject.h
new file mode 100644
index 0000000000..8de8832ca9
--- /dev/null
+++ b/indra/newview/llfloateropenobject.h
@@ -0,0 +1,56 @@
+/**
+ * @file llfloateropenobject.h
+ * @brief LLFloaterOpenObject class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Shows the contents of an object and their permissions when you
+ * click "Buy..." on an object with "Sell Contents" checked.
+ */
+
+#ifndef LL_LLFLOATEROPENOBJECT_H
+#define LL_LLFLOATEROPENOBJECT_H
+
+#include "llfloater.h"
+
+
+
+class LLPanelInventory;
+
+class LLFloaterOpenObject
+: public LLFloater
+{
+public:
+ static void show();
+ static void dirty();
+
+ struct LLCatAndWear
+ {
+ LLUUID mCatID;
+ bool mWear;
+ };
+
+protected:
+ LLFloaterOpenObject();
+ ~LLFloaterOpenObject();
+
+ void refresh();
+ void draw();
+
+ static void onClickMoveToInventory(void* data);
+ static void onClickMoveAndWear(void* data);
+ static void moveToInventory(bool wear);
+ static void callbackMoveInventory(S32 result, void* data);
+ static void* createPanelInventory(void* data);
+
+protected:
+ static LLFloaterOpenObject* sInstance;
+
+ LLPanelInventory* mPanelInventory;
+ BOOL mDirty;
+};
+
+#endif
diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp
new file mode 100644
index 0000000000..18473c8d98
--- /dev/null
+++ b/indra/newview/llfloaterpostcard.cpp
@@ -0,0 +1,319 @@
+/**
+ * @file llfloaterpostcard.cpp
+ * @brief Postcard send floater, allows setting name, e-mail address, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterpostcard.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llui.h"
+#include "lllineeditor.h"
+#include "llviewertexteditor.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llstatusbar.h"
+#include "llviewerregion.h"
+#include "lleconomy.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llimagejpeg.h"
+#include "llimagej2c.h"
+#include "llvfile.h"
+#include "llvfs.h"
+#include "viewer.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLLinkedList<LLFloaterPostcard> LLFloaterPostcard::sInstances;
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterPostcard
+///----------------------------------------------------------------------------
+
+LLFloaterPostcard::LLFloaterPostcard(LLImageJPEG* jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global)
+: LLFloater("Postcard Floater"),
+ mViewerImage(img),
+ mJPEGImage(jpeg),
+ mImageScale(img_scale),
+ mPosTakenGlobal(pos_taken_global)
+{
+ init();
+}
+
+
+void LLFloaterPostcard::init()
+{
+ // pick up the user's up-to-date email address
+ if(!gAgent.getID().isNull())
+ {
+ // we're logged in, so we can get this info.
+ gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ }
+
+ sInstances.addData(this);
+}
+
+// Destroys the object
+LLFloaterPostcard::~LLFloaterPostcard()
+{
+ sInstances.removeData(this);
+ mJPEGImage = NULL; // deletes image
+}
+
+BOOL LLFloaterPostcard::postBuild()
+{
+ childSetAction("cancel_btn", onClickCancel, this);
+ childSetAction("send_btn", onClickSend, this);
+
+ childDisable("from_form");
+ childSetAction("publish_help_btn", onClickPublishHelp, this);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Disable these buttons if they are PG (Teen) users
+ childDisable("allow_publish_check");
+ childHide("allow_publish_check");
+ childDisable("publish_help_btn");
+ childHide("publish_help_btn");
+ childDisable("mature_check");
+ childHide("mature_check");
+ }
+
+ LLString name_string;
+ gAgent.buildFullname(name_string);
+
+ childSetValue("name_form", LLSD(name_string));
+
+ // XUI:translate
+ LLString msg("Postcard from ");
+ msg += gSecondLife;
+ childSetValue("subject_form", LLSD(msg));
+
+ LLTextEditor *MsgField = LLUICtrlFactory::getTextEditorByName(this, "msg_form");
+ if (MsgField)
+ {
+ MsgField->setText("Check this out!");
+ MsgField->setWordWrap(TRUE);
+ }
+
+ childSetFocus("to_form", TRUE);
+
+ return TRUE;
+}
+
+
+// static
+LLFloaterPostcard* LLFloaterPostcard::showFromSnapshot(LLImageJPEG *jpeg, LLImageGL *img, const LLVector2 &image_scale, const LLVector3d& pos_taken_global)
+{
+ // Take the images from the caller
+ // It's now our job to clean them up
+ LLFloaterPostcard *instance = new LLFloaterPostcard(jpeg, img, image_scale, pos_taken_global);
+
+ gUICtrlFactory->buildFloater(instance, "floater_postcard.xml");
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ instance->setOrigin(left, top - instance->getRect().getHeight());
+
+ instance->open();
+
+ return instance;
+}
+
+void LLFloaterPostcard::draw()
+{
+ LLGLSUIDefault gls_ui;
+ LLFloater::draw();
+
+ if(getVisible() && !mMinimized && mViewerImage.notNull() && mJPEGImage.notNull())
+ {
+ LLRect rect(mRect);
+
+ // first set the max extents of our preview
+ rect.translate(-rect.mLeft, -rect.mBottom);
+ rect.mLeft += 280;
+ rect.mRight -= 10;
+ rect.mTop -= 20;
+ rect.mBottom = rect.mTop - 130;
+
+ // then fix the aspect ratio
+ F32 ratio = (F32)mJPEGImage->getWidth() / (F32)mJPEGImage->getHeight();
+ if ((F32)rect.getWidth() / (F32)rect.getHeight() >= ratio)
+ {
+ rect.mRight = (S32)((F32)rect.mLeft + ((F32)rect.getHeight() * ratio));
+ }
+ else
+ {
+ rect.mBottom = (S32)((F32)rect.mTop - ((F32)rect.getWidth() / ratio));
+ }
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(rect, LLColor4(0.f, 0.f, 0.f, 1.f));
+ rect.stretch(-1);
+ }
+ {
+
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ {
+ glScalef(mImageScale.mV[VX], mImageScale.mV[VY], 1.f);
+ glMatrixMode(GL_MODELVIEW);
+ gl_draw_scaled_image(rect.mLeft,
+ rect.mBottom,
+ rect.getWidth(),
+ rect.getHeight(),
+ mViewerImage,
+ LLColor4::white);
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickCancel(void* data)
+{
+ if (data)
+ {
+ LLFloaterPostcard *self = (LLFloaterPostcard *)data;
+
+ self->onClose(false);
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickSend(void* data)
+{
+ if (data)
+ {
+ LLFloaterPostcard *self = (LLFloaterPostcard *)data;
+
+ LLString from(self->childGetValue("from_form").asString().c_str());
+ LLString to(self->childGetValue("to_form").asString().c_str());
+
+ if (to.empty() || to.find('@') == std::string::npos)
+ {
+ gViewerWindow->alertXml("PromptRecipientEmail");
+ return;
+ }
+
+ if (from.empty() || from.find('@') == std::string::npos)
+ {
+ gViewerWindow->alertXml("PromptSelfEmail");
+ return;
+ }
+
+ if (self->mJPEGImage.notNull())
+ {
+ // upload the image
+ self->mTransactionID.generate();
+ self->mAssetID = self->mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+ LLVFile::writeFile(self->mJPEGImage->getData(), self->mJPEGImage->getDataSize(), gVFS, self->mAssetID, LLAssetType::AT_IMAGE_JPEG);
+
+ gAssetStorage->storeAssetData(self->mTransactionID, LLAssetType::AT_IMAGE_JPEG, &uploadCallback, (void *)self, FALSE);
+
+ LLUploadDialog::modalUploadDialog("Uploading...\n\nPostcard");
+
+ // don't destroy the window until the upload is done
+ // this way we keep the information in the form
+ self->setVisible(FALSE);
+
+ // also remove any dependency on another floater
+ // so that we can be sure to outlive it while we
+ // need to.
+ LLFloater* dependee = self->getDependee();
+ if (dependee)
+ dependee->removeDependentFloater(self);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ErrorProcessingSnapshot");
+ }
+ }
+}
+
+// static
+void LLFloaterPostcard::onClickPublishHelp(void* data)
+{
+ gViewerWindow->alertXml("ClickPublishHelpPostcard");
+}
+
+// static
+void LLFloaterPostcard::uploadCallback(const LLUUID& asset_id, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLFloaterPostcard *self = (LLFloaterPostcard *)user_data;
+
+ LLUploadDialog::modalUploadFinished();
+
+ if (result)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("ErrorUploadingPostcard", args);
+ }
+ else
+ {
+ // only create the postcard once the upload succeeds
+
+ // request the postcard
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("SendPostcard");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("AssetID", self->mAssetID);
+ msg->addVector3d("PosGlobal", self->mPosTakenGlobal);
+ msg->addString("To", self->childGetValue("to_form").asString());
+ msg->addString("From", self->childGetValue("from_form").asString());
+ msg->addString("Name", self->childGetValue("name_form").asString());
+ msg->addString("Subject", self->childGetValue("subject_form").asString());
+ msg->addString("Msg", self->childGetValue("msg_form").asString());
+ msg->addBOOL("AllowPublish", self->childGetValue("allow_publish_check").asBoolean());
+ msg->addBOOL("MaturePublish", self->childGetValue("mature_check").asBoolean());
+ gAgent.sendReliableMessage();
+ }
+
+ self->close();
+}
+
+// static
+void LLFloaterPostcard::updateUserInfo(const char *email)
+{
+ LLFloaterPostcard *instance;
+
+ sInstances.resetList();
+ while ((instance = sInstances.getNextData()))
+ {
+ const LLString& text = instance->childGetValue("from_form").asString();
+ if (text.empty())
+ {
+ // there's no text in this field yet, pre-populate
+ instance->childSetValue("from_form", LLSD(email));
+ }
+ }
+}
diff --git a/indra/newview/llfloaterpostcard.h b/indra/newview/llfloaterpostcard.h
new file mode 100644
index 0000000000..542d117403
--- /dev/null
+++ b/indra/newview/llfloaterpostcard.h
@@ -0,0 +1,58 @@
+/**
+ * @file llfloaterpostcard.h
+ * @brief Postcard send floater, allows setting name, e-mail address, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERPOSTCARD_H
+#define LL_LLFLOATERPOSTCARD_H
+
+#include "llfloater.h"
+#include "llcheckboxctrl.h"
+
+#include "llmemory.h"
+#include "llimagegl.h"
+
+class LLTextEditor;
+class LLLineEditor;
+class LLButton;
+
+class LLFloaterPostcard
+: public LLFloater
+{
+public:
+ LLFloaterPostcard(LLImageJPEG* jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
+ virtual ~LLFloaterPostcard();
+
+ virtual void init();
+ virtual BOOL postBuild();
+ virtual void draw();
+
+ static LLFloaterPostcard* showFromSnapshot(LLImageJPEG *jpeg, LLImageGL *img, const LLVector2& img_scale, const LLVector3d& pos_taken_global);
+
+ static void onClickCancel(void* data);
+ static void onClickSend(void* data);
+ static void onClickPublishHelp(void *data);
+
+ static void uploadCallback(const LLUUID& asset_id,
+ void *user_data,
+ S32 result);
+
+ static void updateUserInfo(const char *email);
+
+protected:
+
+ LLPointer<LLImageJPEG> mJPEGImage;
+ LLPointer<LLImageGL> mViewerImage;
+ LLTransactionID mTransactionID;
+ LLAssetID mAssetID;
+ LLVector2 mImageScale;
+ LLVector3d mPosTakenGlobal;
+
+ static LLLinkedList<LLFloaterPostcard> sInstances;
+};
+
+
+#endif // LL_LLFLOATERPOSTCARD_H
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
new file mode 100644
index 0000000000..ded8f1df4a
--- /dev/null
+++ b/indra/newview/llfloaterpreference.cpp
@@ -0,0 +1,365 @@
+/**
+ * @file llfloaterpreference.cpp
+ * @brief LLPreferenceCore class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * App-wide preferences. Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterpreference.h"
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lldir.h"
+#include "llfocusmgr.h"
+#include "llscrollbar.h"
+#include "llspinctrl.h"
+#include "message.h"
+
+#include "llfloaterabout.h"
+#include "llpanelnetwork.h"
+#include "llpanelaudioprefs.h"
+#include "llpaneldisplay.h"
+#include "llpaneldebug.h"
+#include "llpanelgeneral.h"
+#include "llpanelinput.h"
+#include "llpanelmsgs.h"
+//#include "llpanelweb.h"
+#include "llprefschat.h"
+#include "llprefsim.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llassetstorage.h"
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llviewernetwork.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+//#include "viewer.h"
+#include "llkeyboard.h"
+#include "llscrollcontainer.h"
+
+
+const S32 PREF_BORDER = 4;
+const S32 PREF_PAD = 5;
+const S32 PREF_BUTTON_WIDTH = 70;
+const S32 PREF_CATEGORY_WIDTH = 150;
+
+const S32 PREF_FLOATER_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 96;
+
+LLFloaterPreference* LLFloaterPreference::sInstance = NULL;
+
+// Must be done at run time, not compile time. JC
+S32 pref_min_width()
+{
+ return
+ 2 * PREF_BORDER +
+ 2 * PREF_BUTTON_WIDTH +
+ PREF_PAD + RESIZE_HANDLE_WIDTH +
+ PREF_CATEGORY_WIDTH +
+ PREF_PAD;
+}
+
+S32 pref_min_height()
+{
+ return
+ 2 * PREF_BORDER +
+ 3*(BTN_HEIGHT + PREF_PAD) +
+ PREF_FLOATER_MIN_HEIGHT;
+}
+
+
+LLPreferenceCore::LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton * default_btn) :
+ mTabContainer(tab_container),
+ mGeneralPanel(NULL),
+ mInputPanel(NULL),
+ mNetworkPanel(NULL),
+ mDisplayPanel(NULL),
+ mDisplayPanel2(NULL),
+ mAudioPanel(NULL),
+ mMsgPanel(NULL)
+{
+ mGeneralPanel = new LLPanelGeneral();
+ mTabContainer->addTabPanel(mGeneralPanel, mGeneralPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mGeneralPanel->setDefaultBtn(default_btn);
+
+ mInputPanel = new LLPanelInput();
+ mTabContainer->addTabPanel(mInputPanel, mInputPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mInputPanel->setDefaultBtn(default_btn);
+
+ mNetworkPanel = new LLPanelNetwork();
+ mTabContainer->addTabPanel(mNetworkPanel, mNetworkPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mNetworkPanel->setDefaultBtn(default_btn);
+
+ mDisplayPanel = new LLPanelDisplay();
+ mTabContainer->addTabPanel(mDisplayPanel, mDisplayPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel->setDefaultBtn(default_btn);
+
+ mDisplayPanel3 = new LLPanelDisplay3();
+ mTabContainer->addTabPanel(mDisplayPanel3, mDisplayPanel3->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel3->setDefaultBtn(default_btn);
+
+ mDisplayPanel2 = new LLPanelDisplay2();
+ mTabContainer->addTabPanel(mDisplayPanel2, mDisplayPanel2->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mDisplayPanel2->setDefaultBtn(default_btn);
+
+ mAudioPanel = new LLPanelAudioPrefs();
+ mTabContainer->addTabPanel(mAudioPanel, mAudioPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mAudioPanel->setDefaultBtn(default_btn);
+
+ mPrefsChat = new LLPrefsChat();
+ mTabContainer->addTabPanel(mPrefsChat->getPanel(), mPrefsChat->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsChat->getPanel()->setDefaultBtn(default_btn);
+
+ mPrefsIM = new LLPrefsIM();
+ mTabContainer->addTabPanel(mPrefsIM->getPanel(), mPrefsIM->getPanel()->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mPrefsIM->getPanel()->setDefaultBtn(default_btn);
+
+ mMsgPanel = new LLPanelMsgs();
+ gUICtrlFactory->buildPanel(mMsgPanel, "panel_settings_msgbox.xml");
+ mTabContainer->addTabPanel(mMsgPanel, mMsgPanel->getLabel(), FALSE, onTabChanged, mTabContainer);
+ mMsgPanel->setDefaultBtn(default_btn);
+
+ mTabContainer->selectTab(gSavedSettings.getS32("LastPrefTab"));
+
+// Web prefs removed from Loopy build
+// mWebPanel = new LLPanelWeb();
+// gUICtrlFactory->buildPanel(mWebPanel, "panel_settings_web.xml");
+// addTabPanel(mWebPanel, "Web", FALSE, onTabChanged, this);
+}
+
+void LLPreferenceCore::apply()
+{
+ mGeneralPanel->apply();
+ mInputPanel->apply();
+ mNetworkPanel->apply();
+ mDisplayPanel->apply();
+ mDisplayPanel2->apply();
+ mDisplayPanel3->apply();
+ mAudioPanel->apply();
+ mPrefsChat->apply();
+ mPrefsIM->apply();
+ mMsgPanel->apply();
+// mWebPanel->apply();
+}
+
+
+void LLPreferenceCore::cancel()
+{
+ mGeneralPanel->cancel();
+ mInputPanel->cancel();
+ mNetworkPanel->cancel();
+ mDisplayPanel->cancel();
+ mDisplayPanel2->cancel();
+ mDisplayPanel3->cancel();
+ mAudioPanel->cancel();
+ mPrefsChat->cancel();
+ mPrefsIM->cancel();
+ mMsgPanel->cancel();
+// mWebPanel->cancel();
+}
+
+// static
+void LLPreferenceCore::onTabChanged(void* user_data, bool from_click)
+{
+ LLTabContainerCommon* self = (LLTabContainerCommon*)user_data;
+
+ gSavedSettings.setS32("LastPrefTab", self->getCurrentPanelIndex());
+}
+
+
+void LLPreferenceCore::setPersonalInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email)
+{
+ mPrefsIM->setPersonalInfo(visibility, im_via_email, email);
+}
+
+//////////////////////////////////////////////
+// LLFloaterPreference
+
+LLFloaterPreference::LLFloaterPreference()
+{
+ gUICtrlFactory->buildFloater(this, "floater_preferences.xml");
+}
+
+BOOL LLFloaterPreference::postBuild()
+{
+ requires("About...", WIDGET_TYPE_BUTTON);
+ requires("OK", WIDGET_TYPE_BUTTON);
+ requires("Cancel", WIDGET_TYPE_BUTTON);
+ requires("Apply", WIDGET_TYPE_BUTTON);
+ requires("pref core", WIDGET_TYPE_TAB_CONTAINER);
+
+ if (!checkRequirements())
+ {
+ return FALSE;
+ }
+
+ mAboutBtn = LLUICtrlFactory::getButtonByName(this, "About...");
+ mAboutBtn->setClickedCallback(onClickAbout, this);
+
+ mApplyBtn = LLUICtrlFactory::getButtonByName(this, "Apply");
+ mApplyBtn->setClickedCallback(onBtnApply, this);
+
+ mCancelBtn = LLUICtrlFactory::getButtonByName(this, "Cancel");
+ mCancelBtn->setClickedCallback(onBtnCancel, this);
+
+ mOKBtn = LLUICtrlFactory::getButtonByName(this, "OK");
+ mOKBtn->setClickedCallback(onBtnOK, this);
+
+ mPreferenceCore = new LLPreferenceCore(
+ LLUICtrlFactory::getTabContainerByName(this, "pref core"),
+ static_cast<LLButton *>(getChildByName("OK"))
+ );
+
+ sInstance = this;
+
+ return TRUE;
+}
+
+
+LLFloaterPreference::~LLFloaterPreference()
+{
+ sInstance = NULL;
+}
+
+
+void LLFloaterPreference::draw()
+{
+ if( getVisible() )
+ {
+ LLFloater::draw();
+ }
+}
+
+
+void LLFloaterPreference::apply()
+{
+ this->mPreferenceCore->apply();
+}
+
+
+void LLFloaterPreference::cancel()
+{
+ this->mPreferenceCore->cancel();
+}
+
+
+// static
+void LLFloaterPreference::show(void*)
+{
+ if (!sInstance)
+ {
+ new LLFloaterPreference();
+ sInstance->center();
+ }
+
+ sInstance->open();
+
+ if(!gAgent.getID().isNull())
+ {
+ // we're logged in, so we can get this info.
+ gMessageSystem->newMessageFast(_PREHASH_UserInfoRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+// static
+void LLFloaterPreference::onClickAbout(void*)
+{
+ LLFloaterAbout::show(NULL);
+}
+
+
+// static
+void LLFloaterPreference::onBtnOK( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ // commit any outstanding text entry
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+
+ if (fp->canClose())
+ {
+ fp->apply();
+ fp->onClose(false);
+
+ gSavedSettings.saveToFile( gSettingsFileName, TRUE );
+
+ std::string crash_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+ // save all settings, even if equals defaults
+ gCrashSettings.saveToFile(crash_settings_filename.c_str(), FALSE);
+ }
+ else
+ {
+ // Show beep, pop up dialog, etc.
+ llinfos << "Can't close preferences!" << llendl;
+ }
+}
+
+
+// static
+void LLFloaterPreference::onBtnApply( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+ fp->apply();
+}
+
+
+// static
+void LLFloaterPreference::onBtnCancel( void* userdata )
+{
+ LLFloaterPreference *fp =(LLFloaterPreference *)userdata;
+ if (fp->hasFocus())
+ {
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus->acceptsTextInput())
+ {
+ cur_focus->onCommit();
+ }
+ }
+ fp->cancel();
+ fp->close();
+}
+
+
+// static
+void LLFloaterPreference::updateUserInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email)
+{
+ if(sInstance && sInstance->mPreferenceCore)
+ {
+ sInstance->mPreferenceCore->setPersonalInfo(
+ visibility, im_via_email, email);
+ }
+}
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
new file mode 100644
index 0000000000..8c33aba476
--- /dev/null
+++ b/indra/newview/llfloaterpreference.h
@@ -0,0 +1,106 @@
+/**
+ * @file llfloaterpreference.h
+ * @brief LLPreferenceCore class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * App-wide preferences. Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#ifndef LL_LLFLOATERPREFERENCE_H
+#define LL_LLFLOATERPREFERENCE_H
+
+#include "llfloater.h"
+#include "lltabcontainervertical.h"
+
+class LLPanelGeneral;
+class LLPanelInput;
+class LLPanelDisplay;
+class LLPanelDisplay2;
+class LLPanelDisplay3;
+class LLPanelAudioPrefs;
+class LLPanelDebug;
+class LLPanelNetwork;
+class LLMessageSystem;
+class LLPrefsChat;
+class LLPrefsIM;
+class LLPanelMsgs;
+//class LLPanelWeb; // Web prefs removed from Loopy build
+class LLScrollListCtrl;
+
+class LLPreferenceCore
+{
+
+public:
+ LLPreferenceCore(LLTabContainerCommon* tab_container, LLButton * default_btn);
+
+ void apply();
+ void cancel();
+
+ LLTabContainerCommon* getTabContainer() { return mTabContainer; }
+
+ void setPersonalInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email);
+
+ static void onTabChanged(void* user_data, bool from_click);
+
+private:
+ LLTabContainerCommon *mTabContainer;
+ LLPanelGeneral *mGeneralPanel;
+ LLPanelInput *mInputPanel;
+ LLPanelNetwork *mNetworkPanel;
+ LLPanelDisplay *mDisplayPanel;
+ LLPanelDisplay2 *mDisplayPanel2;
+ LLPanelDisplay3 *mDisplayPanel3;
+ LLPanelAudioPrefs *mAudioPanel;
+// LLPanelDebug *mDebugPanel;
+ LLPrefsChat *mPrefsChat;
+ LLPrefsIM *mPrefsIM;
+ LLPanelMsgs *mMsgPanel;
+// LLPanelWeb* mWebPanel;
+};
+
+// Floater to control preferences (display, audio, bandwidth, general.
+class LLFloaterPreference : public LLFloater
+{
+public:
+ LLFloaterPreference();
+ ~LLFloaterPreference();
+
+ void apply();
+ void cancel();
+ virtual BOOL postBuild();
+ static void show(void*);
+
+ // static data update, called from message handler
+ static void updateUserInfo(
+ const char* visibility,
+ BOOL im_via_email,
+ const char* email);
+
+protected:
+ LLPreferenceCore *mPreferenceCore;
+
+ /*virtual*/ void draw();
+
+ LLButton* mAboutBtn;
+ LLButton *mOKBtn;
+ LLButton *mCancelBtn;
+ LLButton *mApplyBtn;
+
+ static void onClickAbout(void*);
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+ static void onBtnApply(void*);
+
+ static LLFloaterPreference* sInstance;
+};
+
+#endif // LL_LLPREFERENCEFLOATER_H
diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp
new file mode 100644
index 0000000000..4311a899c4
--- /dev/null
+++ b/indra/newview/llfloaterproperties.cpp
@@ -0,0 +1,927 @@
+/**
+ * @file llfloaterproperties.cpp
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterproperties.h"
+
+#include <algorithm>
+#include <functional>
+#include "llcachename.h"
+#include "lldbstrings.h"
+#include "llinventory.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinfo.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llresmgr.h"
+#include "roles_constants.h"
+#include "llselectmgr.h"
+#include "lltextbox.h"
+#include "lluiconstants.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewercontrol.h"
+
+#include "llvieweruictrlfactory.h"
+
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const char* YOU_CAN = "You can:";
+const char* OWNER_CAN = "Owner can: ";
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPropertiesObserver
+//
+// helper class to watch the inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLPropertiesObserver : public LLInventoryObserver
+{
+public:
+ LLPropertiesObserver() {}
+ virtual ~LLPropertiesObserver() {}
+ virtual void changed(U32 mask);
+};
+
+LLPropertiesObserver* gPropertiesObserver = NULL;
+void LLPropertiesObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::LABEL | LLInventoryObserver::INTERNAL | LLInventoryObserver::REMOVE)) != 0)
+ {
+ LLFloaterProperties::dirtyAll();
+ }
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterProperties
+///----------------------------------------------------------------------------
+
+LLFloaterProperties::instance_map LLFloaterProperties::sInstances;
+
+// static
+LLFloaterProperties* LLFloaterProperties::find(const LLUUID& item_id,
+ const LLUUID& object_id)
+{
+ // for simplicity's sake, we key the properties window with a
+ // single uuid. However, the items are keyed by item and object
+ // (obj == null -> agent inventory). So, we xor the two ids, and
+ // use that as a lookup key
+ instance_map::iterator it = sInstances.find(item_id ^ object_id);
+ if(it != sInstances.end())
+ {
+ return (*it).second;
+ }
+ return NULL;
+}
+
+// static
+LLFloaterProperties* LLFloaterProperties::show(const LLUUID& item_id,
+ const LLUUID& object_id)
+{
+ LLFloaterProperties* instance = find(item_id, object_id);
+ if(instance)
+ {
+ if (LLFloater::getFloaterHost() && LLFloater::getFloaterHost() != instance->getHost())
+ {
+ // this properties window is being opened in a new context
+ // needs to be rehosted
+ LLFloater::getFloaterHost()->addFloater(instance, TRUE);
+ }
+
+ instance->refresh();
+ instance->open();
+ }
+ return instance;
+}
+
+void LLFloaterProperties::dirtyAll()
+{
+ // ...this is more clear. Possibly more correct, because the
+ // refresh method may delete the object.
+ for(instance_map::iterator it = sInstances.begin(); it!=sInstances.end(); )
+ {
+ (*it++).second->dirty();
+ }
+}
+
+// Default constructor
+LLFloaterProperties::LLFloaterProperties(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_id, const LLUUID& object_id) :
+ LLFloater(name, rect, title),
+ mItemID(item_id),
+ mObjectID(object_id),
+ mDirty(TRUE)
+{
+ gUICtrlFactory->buildFloater(this,"floater_inventory_item_properties.xml");
+
+ // hack to make sure these floaters are observing the inventory.
+ if(!gPropertiesObserver)
+ {
+ gPropertiesObserver = new LLPropertiesObserver;
+ gInventory.addObserver(gPropertiesObserver);
+ }
+ // add the object to the static structure
+ LLUUID key = mItemID ^ mObjectID;
+ sInstances.insert(instance_map::value_type(key, this));
+ // build the UI
+ // item name & description
+ childSetPrevalidate("LabelItemName",&LLLineEditor::prevalidatePrintableNotPipe);
+ childSetCommitCallback("LabelItemName",onCommitName,this);
+ childSetPrevalidate("LabelItemDesc",&LLLineEditor::prevalidatePrintableNotPipe);
+ // Creator information
+ childSetAction("BtnCreator",onClickCreator,this);
+ // owner information
+ childSetAction("BtnOwner",onClickOwner,this);
+ // acquired date
+ // owner permissions
+ // Permissions debug text
+ // group permissions
+ childSetCommitCallback("CheckShareWithGroup",&onCommitPermissions, this);
+ // everyone permissions
+ childSetCommitCallback("CheckEveryoneCopy",&onCommitPermissions, this);
+ // next owner permissions
+ childSetCommitCallback("CheckNextOwnerModify",&onCommitPermissions, this);
+ childSetCommitCallback("CheckNextOwnerCopy",&onCommitPermissions, this);
+ childSetCommitCallback("CheckNextOwnerTransfer",&onCommitPermissions, this);
+ // Mark for sale or not, and sale info
+ childSetCommitCallback("CheckPurchase",&onCommitSaleInfo, this);
+ childSetCommitCallback("RadioSaleType",&onCommitSaleType, this);
+ // "Price" label for edit
+ childSetCommitCallback("EditPrice",&onCommitSaleInfo, this);
+ // The UI has been built, now fill in all the values
+ refresh();
+}
+
+// Destroys the object
+LLFloaterProperties::~LLFloaterProperties()
+{
+ // clean up the static data.
+ instance_map::iterator it = sInstances.find(mItemID ^ mObjectID);
+ if(it != sInstances.end())
+ {
+ sInstances.erase(it);
+ }
+}
+
+void LLFloaterProperties::refresh()
+{
+ LLInventoryItem* item = findItem();
+ if(item)
+ {
+ refreshFromItem(item);
+ }
+ else
+ {
+ //RN: it is possible that the container object is in the middle of an inventory refresh
+ // causing findItem() to fail, so just temporarily disable everything
+
+ mDirty = TRUE;
+
+ const char* enableNames[]={
+ "LabelItemName",
+ "LabelItemDesc",
+ "LabelCreatorName",
+ "BtnCreator",
+ "LabelOwnerName",
+ "BtnOwner",
+ "CheckOwnerModify",
+ "CheckOwnerCopy",
+ "CheckOwnerTransfer",
+ "CheckShareWithGroup",
+ "CheckEveryoneCopy",
+ "CheckNextOwnerModify",
+ "CheckNextOwnerCopy",
+ "CheckNextOwnerTransfer",
+ "CheckPurchase",
+ "RadioSaleType",
+ "EditPrice"
+ };
+ for(int t=0;t<sizeof(enableNames)/sizeof(char*);t++)
+ {
+ childSetEnabled(enableNames[t],false);
+ }
+ const char* hideNames[]={
+ "BaseMaskDebug",
+ "OwnerMaskDebug",
+ "GroupMaskDebug",
+ "EveryoneMaskDebug",
+ "NextMaskDebug"
+ };
+ for(int t=0;t<sizeof(hideNames)/sizeof(char*);t++)
+ {
+ childSetVisible(hideNames[t],false);
+ }
+ }
+}
+
+void LLFloaterProperties::draw()
+{
+ if (mDirty)
+ {
+ // RN: clear dirty first because refresh can set dirty to TRUE
+ mDirty = FALSE;
+ refresh();
+ }
+
+ LLFloater::draw();
+}
+
+void LLFloaterProperties::refreshFromItem(LLInventoryItem* item)
+{
+ ////////////////////////
+ // PERMISSIONS LOOKUP //
+ ////////////////////////
+
+ // do not enable the UI for incomplete items.
+ LLViewerInventoryItem* i = (LLViewerInventoryItem*)item;
+ BOOL is_complete = i->isComplete();
+
+ const LLPermissions& perm = item->getPermissions();
+ BOOL can_agent_manipulate = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_MANIPULATE);
+ BOOL can_agent_sell = gAgent.allowOperation(PERM_OWNER, perm,
+ GP_OBJECT_SET_SALE);
+
+ // You need permission to modify the object to modify an inventory
+ // item in it.
+ LLViewerObject* object = NULL;
+ if(!mObjectID.isNull()) object = gObjectList.findObject(mObjectID);
+ BOOL is_obj_modify = TRUE;
+ if(object)
+ {
+ is_obj_modify = object->permOwnerModify();
+ }
+
+ //////////////////////
+ // ITEM NAME & DESC //
+ //////////////////////
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY, perm,
+ GP_OBJECT_MANIPULATE)
+ && is_obj_modify && is_complete;
+
+ childSetEnabled("LabelItemNameTitle",TRUE);
+ childSetEnabled("LabelItemName",is_modifiable);
+ const char EMPTY_STRING[1] = "";
+ const char* txt = EMPTY_STRING;
+ if(!item->getName().empty())
+ {
+ txt = item->getName().c_str();
+ }
+ childSetText("LabelItemName",txt);
+ childSetEnabled("LabelItemDescTitle",TRUE);
+ childSetEnabled("LabelItemDesc",is_modifiable);
+ childSetVisible("IconLocked",!is_modifiable);
+ txt = EMPTY_STRING;
+ if(!item->getDescription().empty())
+ {
+ txt = item->getDescription().c_str();
+ }
+ childSetText("LabelItemDesc",txt);
+
+ //////////////////
+ // CREATOR NAME //
+ //////////////////
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+ if(!gCacheName) return;
+ if(!gAgent.getRegion()) return;
+
+ if (item->getCreatorUUID().notNull())
+ {
+ gCacheName->getName(item->getCreatorUUID(), first_name, last_name);
+ LLString name(first_name);
+ name.append(1, ' ');
+ name.append(last_name);
+
+ childSetEnabled("BtnCreator",TRUE);
+ childSetEnabled("LabelCreatorTitle",TRUE);
+ childSetEnabled("LabelCreatorName",TRUE);
+ childSetText("LabelCreatorName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnCreator",FALSE);
+ childSetEnabled("LabelCreatorTitle",FALSE);
+ childSetEnabled("LabelCreatorName",FALSE);
+ childSetText("LabelCreatorName","(unknown)"); // XUI:translate
+ }
+
+ ////////////////
+ // OWNER NAME //
+ ////////////////
+ if(perm.isOwned())
+ {
+ LLString name;
+ if (perm.isGroupOwned())
+ {
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(perm.getGroup(), group_name);
+ name.assign(group_name);
+ }
+ else
+ {
+ gCacheName->getName(perm.getOwner(), first_name, last_name);
+ name.assign(first_name);
+ name.append(1, ' ');
+ name.append(last_name);
+ }
+ childSetEnabled("BtnOwner",TRUE);
+ childSetEnabled("LabelOwnerTitle",TRUE);
+ childSetEnabled("LabelOwnerName",TRUE);
+ childSetText("LabelOwnerName",name);
+ }
+ else
+ {
+ childSetEnabled("BtnOwner",FALSE);
+ childSetEnabled("LabelOwnerTitle",FALSE);
+ childSetEnabled("LabelOwnerName",FALSE);
+ childSetText("LabelOwnerName","(public)"); // XUI:translate
+ }
+
+ //////////////////
+ // ACQUIRE DATE //
+ //////////////////
+ time_t time_utc = (time_t)item->getCreationDate();
+ if (0 == time_utc)
+ {
+ childSetText("LabelAcquiredDate","(unknown)");
+ }
+ else
+ {
+ childSetText("LabelAcquiredDate", ctime(&time_utc) );
+ }
+
+ ///////////////////////
+ // OWNER PERMISSIONS //
+ ///////////////////////
+ if(can_agent_manipulate)
+ {
+ childSetText("OwnerLabel",YOU_CAN);
+ }
+ else
+ {
+ childSetText("OwnerLabel",OWNER_CAN);
+ }
+
+ U32 base_mask = perm.getMaskBase();
+ U32 owner_mask = perm.getMaskOwner();
+ U32 group_mask = perm.getMaskGroup();
+ U32 everyone_mask = perm.getMaskEveryone();
+ U32 next_owner_mask = perm.getMaskNextOwner();
+
+ childSetEnabled("OwnerLabel",TRUE);
+ childSetEnabled("CheckOwnerModify",FALSE);
+ childSetValue("CheckOwnerModify",LLSD((BOOL)(owner_mask & PERM_MODIFY)));
+ childSetEnabled("CheckOwnerCopy",FALSE);
+ childSetValue("CheckOwnerCopy",LLSD((BOOL)(owner_mask & PERM_COPY)));
+ childSetEnabled("CheckOwnerTransfer",FALSE);
+ childSetValue("CheckOwnerTransfer",LLSD((BOOL)(owner_mask & PERM_TRANSFER)));
+
+ ///////////////////////
+ // DEBUG PERMISSIONS //
+ ///////////////////////
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ BOOL slam_perm = FALSE;
+ BOOL overwrite_group = FALSE;
+ BOOL overwrite_everyone = FALSE;
+
+ if (item->getType() == LLAssetType::AT_OBJECT)
+ {
+ U32 flags = item->getFlags();
+ slam_perm = flags & LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ overwrite_everyone = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ overwrite_group = flags & LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+
+ char perm_string[11];
+
+ sprintf(perm_string, "B: ");
+ mask_to_string(base_mask, perm_string+3);
+ childSetText("BaseMaskDebug",perm_string);
+ childSetVisible("BaseMaskDebug",TRUE);
+
+ sprintf(perm_string, "O: ");
+ mask_to_string(owner_mask, perm_string+3);
+ childSetText("OwnerMaskDebug",perm_string);
+ childSetVisible("OwnerMaskDebug",TRUE);
+
+ sprintf(perm_string, "G%s: ", overwrite_group ? "*" : "");
+ mask_to_string(group_mask, perm_string + (overwrite_group ? 4 : 3));
+ childSetText("GroupMaskDebug",perm_string);
+ childSetVisible("GroupMaskDebug",TRUE);
+
+ sprintf(perm_string, "E%s: ", overwrite_everyone ? "*" : "");
+ mask_to_string(everyone_mask, perm_string + (overwrite_everyone ? 4 : 3));
+ childSetText("EveryoneMaskDebug",perm_string);
+ childSetVisible("EveryoneMaskDebug",TRUE);
+
+ sprintf(perm_string, "N%s: ", slam_perm ? "*" : "");
+ mask_to_string(next_owner_mask, perm_string + (slam_perm ? 4 : 3));
+ childSetText("NextMaskDebug",perm_string);
+ childSetVisible("NextMaskDebug",TRUE);
+ }
+ else
+ {
+ childSetVisible("BaseMaskDebug",FALSE);
+ childSetVisible("OwnerMaskDebug",FALSE);
+ childSetVisible("GroupMaskDebug",FALSE);
+ childSetVisible("EveryoneMaskDebug",FALSE);
+ childSetVisible("NextMaskDebug",FALSE);
+ }
+
+ /////////////
+ // SHARING //
+ /////////////
+
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_manipulate)
+ {
+ childSetEnabled("CheckShareWithGroup",TRUE);
+ childSetEnabled("CheckEveryoneCopy",(owner_mask & PERM_COPY) && (owner_mask & PERM_TRANSFER));
+ }
+ else
+ {
+ childSetEnabled("CheckShareWithGroup",FALSE);
+ childSetEnabled("CheckEveryoneCopy",FALSE);
+ }
+
+ // Set values.
+ BOOL is_group_copy = (group_mask & PERM_COPY) ? TRUE : FALSE;
+ BOOL is_group_modify = (group_mask & PERM_MODIFY) ? TRUE : FALSE;
+ BOOL is_group_move = (group_mask & PERM_MOVE) ? TRUE : FALSE;
+
+ if (is_group_copy && is_group_modify && is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)TRUE));
+
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else if (!is_group_copy && !is_group_modify && !is_group_move)
+ {
+ childSetValue("CheckShareWithGroup",LLSD((BOOL)FALSE));
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(FALSE);
+ }
+ }
+ else
+ {
+ LLCheckBoxCtrl* ctl = LLUICtrlFactory::getCheckBoxByName(this,"CheckShareWithGroup");
+ if(ctl)
+ {
+ ctl->setTentative(TRUE);
+ ctl->set(TRUE);
+ }
+ }
+
+ childSetValue("CheckEveryoneCopy",LLSD((BOOL)(everyone_mask & PERM_COPY)));
+
+ ///////////////
+ // SALE INFO //
+ ///////////////
+
+ const LLSaleInfo& sale_info = item->getSaleInfo();
+ BOOL is_for_sale = sale_info.isForSale();
+ // Check for ability to change values.
+ if (is_obj_modify && can_agent_sell
+ && gAgent.allowOperation(PERM_TRANSFER, perm, GP_OBJECT_MANIPULATE))
+ {
+ childSetEnabled("SaleLabel",is_complete);
+ childSetEnabled("CheckPurchase",is_complete);
+
+ childSetEnabled("NextOwnerLabel",TRUE);
+ childSetEnabled("CheckNextOwnerModify",base_mask & PERM_MODIFY);
+ childSetEnabled("CheckNextOwnerCopy",base_mask & PERM_COPY);
+ childSetEnabled("CheckNextOwnerTransfer",next_owner_mask & PERM_COPY);
+
+ childSetEnabled("RadioSaleType",is_complete && is_for_sale);
+ childSetEnabled("TextPrice",is_complete && is_for_sale);
+ childSetEnabled("EditPrice",is_complete && is_for_sale);
+ }
+ else
+ {
+ childSetEnabled("SaleLabel",FALSE);
+ childSetEnabled("CheckPurchase",FALSE);
+
+ childSetEnabled("NextOwnerLabel",FALSE);
+ childSetEnabled("CheckNextOwnerModify",FALSE);
+ childSetEnabled("CheckNextOwnerCopy",FALSE);
+ childSetEnabled("CheckNextOwnerTransfer",FALSE);
+
+ childSetEnabled("RadioSaleType",FALSE);
+ childSetEnabled("TextPrice",FALSE);
+ childSetEnabled("EditPrice",FALSE);
+ }
+
+ // Set values.
+ childSetValue("CheckPurchase", is_for_sale);
+ childSetValue("CheckNextOwnerModify",LLSD(BOOL(next_owner_mask & PERM_MODIFY)));
+ childSetValue("CheckNextOwnerCopy",LLSD(BOOL(next_owner_mask & PERM_COPY)));
+ childSetValue("CheckNextOwnerTransfer",LLSD(BOOL(next_owner_mask & PERM_TRANSFER)));
+
+ LLRadioGroup* radioSaleType = LLUICtrlFactory::getRadioGroupByName(this,"RadioSaleType");
+ if (is_for_sale)
+ {
+ radioSaleType->setSelectedIndex((S32)sale_info.getSaleType() - 1);
+ char numerical_price[MAX_STRING];
+ sprintf(numerical_price, "%d", sale_info.getSalePrice());
+ childSetText("EditPrice",numerical_price);
+ }
+ else
+ {
+ radioSaleType->setSelectedIndex(-1);
+ childSetText("EditPrice","");
+ }
+}
+
+// static
+void LLFloaterProperties::onClickCreator(void* data)
+{
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLInventoryItem* item = self->findItem();
+ if(!item) return;
+ if(!item->getCreatorUUID().isNull())
+ {
+ LLFloaterAvatarInfo::showFromObject(item->getCreatorUUID());
+ }
+}
+
+// static
+void LLFloaterProperties::onClickOwner(void* data)
+{
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLInventoryItem* item = self->findItem();
+ if(!item) return;
+ if(item->getPermissions().isGroupOwned())
+ {
+ LLFloaterGroupInfo::showFromUUID(item->getPermissions().getGroup());
+ }
+ else
+ {
+ if(!item->getPermissions().getOwner().isNull())
+ {
+ LLFloaterAvatarInfo::showFromObject(item->getPermissions().getOwner());
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitName(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitName()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self)
+ {
+ return;
+ }
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item)
+ {
+ return;
+ }
+ LLLineEditor* labelItemName = LLUICtrlFactory::getLineEditorByName(self,"LabelItemName");
+
+ if(labelItemName&&
+ (item->getName() != labelItemName->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)) )
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(labelItemName->getText());
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitDescription(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitDescription()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item) return;
+
+ LLLineEditor* labelItemDesc = LLUICtrlFactory::getLineEditorByName(self,"LabelItemDesc");
+ if(!labelItemDesc)
+ {
+ return;
+ }
+ if((item->getDescription() != labelItemDesc->getText()) &&
+ (gAgent.allowOperation(PERM_MODIFY, item->getPermissions(), GP_OBJECT_MANIPULATE)))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+
+ new_item->setDescription(labelItemDesc->getText());
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitPermissions(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitPermissions()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->findItem();
+ if(!item) return;
+ LLPermissions perm(item->getPermissions());
+
+
+ LLCheckBoxCtrl* CheckShareWithGroup = LLUICtrlFactory::getCheckBoxByName(self,"CheckShareWithGroup");
+
+ if(CheckShareWithGroup)
+ {
+ perm.setGroupBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckShareWithGroup->get(),
+ PERM_MODIFY | PERM_MOVE | PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckEveryoneCopy = LLUICtrlFactory::getCheckBoxByName(self,"CheckEveryoneCopy");
+ if(CheckEveryoneCopy)
+ {
+ perm.setEveryoneBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckEveryoneCopy->get(), PERM_COPY);
+ }
+
+ LLCheckBoxCtrl* CheckNextOwnerModify = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerModify");
+ if(CheckNextOwnerModify)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerModify->get(), PERM_MODIFY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerCopy = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerCopy");
+ if(CheckNextOwnerCopy)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerCopy->get(), PERM_COPY);
+ }
+ LLCheckBoxCtrl* CheckNextOwnerTransfer = LLUICtrlFactory::getCheckBoxByName(self,"CheckNextOwnerTransfer");
+ if(CheckNextOwnerTransfer)
+ {
+ perm.setNextOwnerBits(gAgent.getID(), gAgent.getGroupID(),
+ CheckNextOwnerTransfer->get(), PERM_TRANSFER);
+ }
+ if(perm != item->getPermissions()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ U32 flags = new_item->getFlags();
+ // If next owner permissions have changed (and this is an object)
+ // then set the slam permissions flag so that they are applied on rez.
+ if((perm.getMaskNextOwner()!=item->getPermissions().getMaskNextOwner())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_SLAM_PERM;
+ }
+ // If everyone permissions have changed (and this is an object)
+ // then set the overwrite everyone permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskEveryone()!=item->getPermissions().getMaskEveryone())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
+ }
+ // If group permissions have changed (and this is an object)
+ // then set the overwrite group permissions flag so they
+ // are applied on rez.
+ if ((perm.getMaskGroup()!=item->getPermissions().getMaskGroup())
+ && (item->getType() == LLAssetType::AT_OBJECT))
+ {
+ flags |= LLInventoryItem::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
+ }
+ new_item->setFlags(flags);
+ if(self->mObjectID.isNull())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(self->mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ self->refresh();
+ }
+}
+
+// static
+void LLFloaterProperties::onCommitSaleInfo(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitSaleInfo()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ self->updateSaleInfo();
+}
+
+// static
+void LLFloaterProperties::onCommitSaleType(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLFloaterProperties::onCommitSaleType()" << llendl;
+ LLFloaterProperties* self = (LLFloaterProperties*)data;
+ if(!self) return;
+ self->updateSaleInfo();
+}
+
+void LLFloaterProperties::updateSaleInfo()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)findItem();
+ if(!item) return;
+ LLSaleInfo sale_info(item->getSaleInfo());
+ if(!gAgent.allowOperation(PERM_TRANSFER, item->getPermissions(), GP_OBJECT_SET_SALE))
+ {
+ childSetValue("CheckPurchase",LLSD((BOOL)FALSE));
+ }
+
+ if((BOOL)childGetValue("CheckPurchase"))
+ {
+ // turn on sale info
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_COPY;
+
+ LLRadioGroup* RadioSaleType = LLUICtrlFactory::getRadioGroupByName(this,"RadioSaleType");
+ if(RadioSaleType)
+ {
+ switch (RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+
+ if (sale_type == LLSaleInfo::FS_COPY
+ && !gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_SET_SALE))
+ {
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ }
+
+ LLLineEditor* EditPrice = LLUICtrlFactory::getLineEditorByName(this,"EditPrice");
+
+ S32 price = -1;
+ if(EditPrice)
+ {
+ price = atoi(EditPrice->getText().c_str());
+ }
+ // Invalid data - turn off the sale
+ if (price < 0)
+ {
+ sale_type = LLSaleInfo::FS_NOT;
+ price = 0;
+ }
+
+ sale_info.setSaleType(sale_type);
+ sale_info.setSalePrice(price);
+ }
+ else
+ {
+ sale_info.setSaleType(LLSaleInfo::FS_NOT);
+ }
+ if(sale_info != item->getSaleInfo()
+ && item->isComplete())
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setSaleInfo(sale_info);
+ if(mObjectID.isNull())
+ {
+ // This is in the agent's inventory.
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ // This is in an object's contents.
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ }
+ else
+ {
+ // need to make sure we don't just follow the click
+ refresh();
+ }
+}
+
+LLInventoryItem* LLFloaterProperties::findItem() const
+{
+ LLInventoryItem* item = NULL;
+ if(mObjectID.isNull())
+ {
+ // it is in agent inventory
+ item = gInventory.getItem(mItemID);
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ item = (LLInventoryItem*)object->getInventoryObject(mItemID);
+ }
+ }
+ return item;
+}
+
+void LLFloaterProperties::closeByID(const LLUUID& item_id, const LLUUID &object_id)
+{
+ LLFloaterProperties* floaterp = find(item_id, object_id);
+
+ if (floaterp)
+ {
+ floaterp->close();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// LLMultiProperties
+///----------------------------------------------------------------------------
+
+LLMultiProperties::LLMultiProperties(const LLRect &rect) : LLMultiFloater("Properties", rect)
+{
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llfloaterproperties.h b/indra/newview/llfloaterproperties.h
new file mode 100644
index 0000000000..bd3c8b32c7
--- /dev/null
+++ b/indra/newview/llfloaterproperties.h
@@ -0,0 +1,82 @@
+/**
+ * @file llfloaterproperties.h
+ * @brief A floater which shows an inventory item's properties.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERPROPERTIES_H
+#define LL_LLFLOATERPROPERTIES_H
+
+#include <map>
+#include "llfloater.h"
+#include "lliconctrl.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFloaterProperties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLInventoryItem;
+class LLLineEditor;
+class LLRadioGroup;
+class LLTextBox;
+
+class LLFloaterProperties : public LLFloater
+{
+public:
+ static LLFloaterProperties* find(const LLUUID& item_id,
+ const LLUUID& object_id);
+ static LLFloaterProperties* show(const LLUUID& item_id,
+ const LLUUID& object_id);
+ static void dirtyAll();
+
+ static void closeByID(const LLUUID& item_id, const LLUUID& object_id);
+
+ LLFloaterProperties(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_id, const LLUUID& object_id);
+ virtual ~LLFloaterProperties();
+
+ // do everything necessary
+ void dirty() { mDirty = TRUE; }
+ void refresh();
+
+protected:
+ // ui callbacks
+ static void onClickCreator(void* data);
+ static void onClickOwner(void* data);
+ static void onCommitName(LLUICtrl* ctrl, void* data);
+ static void onCommitDescription(LLUICtrl* ctrl, void* data);
+ static void onCommitPermissions(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleInfo(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleType(LLUICtrl* ctrl, void* data);
+ void updateSaleInfo();
+
+ LLInventoryItem* findItem() const;
+
+ void refreshFromItem(LLInventoryItem* item);
+ virtual void draw();
+
+protected:
+ // The item id of the inventory item in question.
+ LLUUID mItemID;
+
+ // mObjectID will have a value if it is associated with a task in
+ // the world, and will be == LLUUID::null if it's in the agent
+ // inventory.
+ LLUUID mObjectID;
+
+ BOOL mDirty;
+
+ typedef std::map<LLUUID, LLFloaterProperties*, lluuid_less> instance_map;
+ static instance_map sInstances;
+};
+
+class LLMultiProperties : public LLMultiFloater
+{
+public:
+ LLMultiProperties(const LLRect& rect);
+};
+
+#endif // LL_LLFLOATERPROPERTIES_H
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
new file mode 100644
index 0000000000..4bd3a4ee49
--- /dev/null
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -0,0 +1,2947 @@
+/**
+ * @file llfloaterregioninfo.cpp
+ * @author Aaron Brashears
+ * @brief Implementation of the region info and controls floater and panels.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfloaterregioninfo.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "llcachename.h"
+#include "lldir.h"
+#include "lldispatcher.h"
+#include "llglheaders.h"
+#include "llregionflags.h"
+#include "llstl.h"
+#include "indra_constants.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfilepicker.h"
+#include "llfloatergodtools.h" // for send_sim_wide_deletes()
+#include "llfloatergroups.h"
+#include "llfloatertelehub.h"
+#include "lllineeditor.h"
+#include "llalertdialog.h"
+#include "llnamelistctrl.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "llinventory.h"
+#include "lltexturectrl.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llvlcomposition.h"
+
+const S32 TERRAIN_TEXTURE_COUNT = 4;
+const S32 CORNER_COUNT = 4;
+
+#define LL_ENABLE_MAINLAND_VISIBLE_CONTROL 0
+
+///----------------------------------------------------------------------------
+/// Local class declaration
+///----------------------------------------------------------------------------
+
+/*
+class LLDispatchSetEstateOwner : public LLDispatchHandler
+{
+public:
+ LLDispatchSetEstateOwner() {}
+ virtual ~LLDispatchSetEstateOwner() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const sparam_t& strings,
+ const iparam_t& integers);
+};
+*/
+
+class LLDispatchEstateUpdateInfo : public LLDispatchHandler
+{
+public:
+ LLDispatchEstateUpdateInfo() {}
+ virtual ~LLDispatchEstateUpdateInfo() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings);
+};
+
+class LLDispatchSetEstateAccess : public LLDispatchHandler
+{
+public:
+ LLDispatchSetEstateAccess() {}
+ virtual ~LLDispatchSetEstateAccess() {}
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings);
+};
+
+
+/*
+void unpack_request_params(
+ LLMessageSystem* msg,
+ LLDispatcher::sparam_t& strings,
+ LLDispatcher::iparam_t& integers)
+{
+ char str_buf[MAX_STRING];
+ S32 str_count = msg->getNumberOfBlocksFast(_PREHASH_StringData);
+ S32 i;
+ for (i = 0; i < str_count; ++i)
+ {
+ // we treat the SParam as binary data (since it might be an
+ // LLUUID in compressed form which may have embedded \0's,)
+ str_buf[0] = '\0';
+ S32 data_size = msg->getSizeFast(_PREHASH_StringData, i, _PREHASH_SParam);
+ msg->getBinaryDataFast(_PREHASH_StringData, _PREHASH_SParam,
+ str_buf, data_size, i, MAX_STRING - 1);
+ strings.push_back(std::string(str_buf, data_size));
+ }
+
+ U32 int_buf;
+ S32 int_count = msg->getNumberOfBlocksFast(_PREHASH_IntegerData);
+ for (i = 0; i < int_count; ++i)
+ {
+ msg->getU32("IntegerData", "IParam", int_buf, i);
+ integers.push_back(int_buf);
+ }
+}
+*/
+
+
+
+bool estate_dispatch_initialized = false;
+
+
+///----------------------------------------------------------------------------
+/// LLFloaterRegionInfo
+///----------------------------------------------------------------------------
+
+LLFloaterRegionInfo* LLFloaterRegionInfo::sInstance = NULL;
+//S32 LLFloaterRegionInfo::sRequestSerial = 0;
+LLUUID LLFloaterRegionInfo::sRequestInvoice;
+
+LLFloaterRegionInfo::LLFloaterRegionInfo(const LLRect& rect) :
+ LLFloater("regioninfo", rect, "Region/Estate")
+{
+ LLRect tr(0, rect.getHeight() - LLFLOATER_HEADER_SIZE, rect.getWidth(), 0);
+ mTab = new LLTabContainer("tab", tr, LLTabContainer::TOP, NULL,NULL,"");
+ mTab->setBorderVisible(FALSE);
+ addChild(mTab);
+
+ // contruct the panels
+ LLPanel* panel;
+ panel = new LLPanelRegionGeneralInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_general.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), TRUE);
+
+ panel = new LLPanelRegionDebugInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_debug.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelRegionTextureInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_texture.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelRegionTerrainInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_terrain.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelEstateInfo;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_estate.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+
+ panel = new LLPanelEstateCovenant;
+ mInfoPanels.push_back((LLPanelRegionInfo*)panel);
+ gUICtrlFactory->buildPanel(panel, "panel_region_covenant.xml");
+ mTab->addTabPanel(panel, panel->getLabel(), FALSE);
+}
+
+LLFloaterRegionInfo::~LLFloaterRegionInfo()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterRegionInfo::show(LLViewerRegion* region)
+{
+ if (!sInstance)
+ {
+ LLRect rect = gSavedSettings.getRect("FloaterRegionInfo");
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ rect.translate(left,top);
+ sInstance = new LLFloaterRegionInfo(rect);
+ gMessageSystem->setHandlerFunc(
+ "EstateOwnerMessage",
+ &processEstateOwnerRequest);
+ }
+ sInstance->open();
+ sInstance->refreshFromRegion(region);
+
+ // Must allow anyone to request the RegionInfo data
+ // so non-owners/non-gods can see the values.
+ // Therefore can't use an EstateOwnerMessage JC
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("RequestRegionInfo");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+// static
+void LLFloaterRegionInfo::show(void*)
+{
+ show(gAgent.getRegion());
+}
+
+// static
+LLFloaterRegionInfo* LLFloaterRegionInfo::getInstance()
+{
+ return sInstance;
+}
+
+// static
+void LLFloaterRegionInfo::processEstateOwnerRequest(LLMessageSystem* msg,void**)
+{
+ static LLDispatcher dispatch;
+ if(!sInstance) return;
+
+ if (!estate_dispatch_initialized)
+ {
+ LLPanelEstateInfo::initDispatch(dispatch);
+ }
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab");
+ if (!tab) return;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate");
+ if (!panel) return;
+
+ // unpack the message
+ std::string request;
+ LLUUID invoice;
+ LLDispatcher::sparam_t strings;
+ LLDispatcher::unpackMessage(msg, request, invoice, strings);
+ if(invoice != getLastInvoice())
+ {
+ llwarns << "Mismatched Estate message: " << request.c_str() << llendl;
+ return;
+ }
+
+ //dispatch the message
+ dispatch.dispatch(request, invoice, strings);
+}
+
+
+// static
+void LLFloaterRegionInfo::processRegionInfo(LLMessageSystem* msg)
+{
+ LLPanel* panel;
+
+ llinfos << "LLFloaterRegionInfo::processRegionInfo" << llendl;
+ if(!sInstance) return;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(sInstance, "tab");
+ if(!tab) return;
+
+ // extract message
+ char sim_name[MAX_STRING];
+ U32 region_flags;
+ U8 agent_limit;
+ F32 object_bonus_factor;
+ U8 sim_access;
+ F32 water_height;
+ F32 terrain_raise_limit;
+ F32 terrain_lower_limit;
+ BOOL use_estate_sun;
+ F32 sun_hour;
+ msg->getString("RegionInfo", "SimName", MAX_STRING, sim_name);
+ msg->getU32("RegionInfo", "RegionFlags", region_flags);
+ msg->getU8("RegionInfo", "MaxAgents", agent_limit);
+ msg->getF32("RegionInfo", "ObjectBonusFactor", object_bonus_factor);
+ msg->getU8("RegionInfo", "SimAccess", sim_access);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, water_height);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, terrain_raise_limit);
+ msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, terrain_lower_limit);
+ msg->getBOOL("RegionInfo", "UseEstateSun", use_estate_sun);
+ // actually the "last set" sun hour, not the current sun hour. JC
+ msg->getF32("RegionInfo", "SunHour", sun_hour);
+
+ // GENERAL PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "General");
+ if(!panel) return;
+ panel->childSetValue("region_text", LLSD(sim_name));
+
+ panel->childSetValue("block_terraform_check", (region_flags & REGION_FLAGS_BLOCK_TERRAFORM) ? TRUE : FALSE );
+ panel->childSetValue("block_fly_check", (region_flags & REGION_FLAGS_BLOCK_FLY) ? TRUE : FALSE );
+ panel->childSetValue("allow_damage_check", (region_flags & REGION_FLAGS_ALLOW_DAMAGE) ? TRUE : FALSE );
+ panel->childSetValue("restrict_pushobject", (region_flags & REGION_FLAGS_RESTRICT_PUSHOBJECT) ? TRUE : FALSE );
+ panel->childSetValue("allow_land_resell_check", (region_flags & REGION_FLAGS_BLOCK_LAND_RESELL) ? FALSE : TRUE );
+ panel->childSetValue("allow_parcel_changes_check", (region_flags & REGION_FLAGS_ALLOW_PARCEL_CHANGES) ? TRUE : FALSE );
+
+ panel->childSetValue("agent_limit_spin", LLSD((F32)agent_limit) );
+ panel->childSetValue("object_bonus_spin", LLSD(object_bonus_factor) );
+
+ panel->childSetValue("access_combo", LLSD(LLViewerRegion::accessToString(sim_access)) );
+
+ // DEBUG PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "Debug");
+ if(!panel) return;
+
+ panel->childSetValue("region_text", LLSD(sim_name) );
+ panel->childSetValue("disable_scripts_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_SCRIPTS)) );
+ panel->childSetValue("disable_collisions_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_COLLISIONS)) );
+ panel->childSetValue("disable_physics_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SKIP_PHYSICS)) );
+
+ // TERRAIN PANEL
+ panel = LLUICtrlFactory::getPanelByName(tab, "Terrain");
+ if(!panel) return;
+
+ panel->childSetValue("region_text", LLSD(sim_name));
+ panel->childSetValue("water_height_spin", LLSD(water_height));
+ panel->childSetValue("terrain_raise_spin", LLSD(terrain_raise_limit));
+ panel->childSetValue("terrain_lower_spin", LLSD(terrain_lower_limit));
+ panel->childSetValue("use_estate_sun_check", LLSD(use_estate_sun));
+
+ LLViewerRegion* region = gAgent.getRegion();
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ panel->childSetValue("fixed_sun_check", LLSD((BOOL)(region_flags & REGION_FLAGS_SUN_FIXED)));
+ panel->childSetEnabled("fixed_sun_check", allow_modify && !use_estate_sun);
+ panel->childSetValue("sun_hour_slider", LLSD(sun_hour));
+ panel->childSetEnabled("sun_hour_slider", allow_modify && !use_estate_sun);
+}
+
+// static
+LLPanelEstateInfo* LLFloaterRegionInfo::getPanelEstate()
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return NULL;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return NULL;
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab,"Estate");
+ return panel;
+}
+
+// static
+LLPanelEstateCovenant* LLFloaterRegionInfo::getPanelCovenant()
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return NULL;
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return NULL;
+ LLPanelEstateCovenant* panel = (LLPanelEstateCovenant*)LLUICtrlFactory::getPanelByName(tab, "Covenant");
+ return panel;
+}
+
+void LLFloaterRegionInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ // call refresh from region on all panels
+ std::for_each(
+ mInfoPanels.begin(),
+ mInfoPanels.end(),
+ llbind2nd(
+#if LL_WINDOWS
+ std::mem_fun1(&LLPanelRegionInfo::refreshFromRegion),
+#else
+ std::mem_fun(&LLPanelRegionInfo::refreshFromRegion),
+#endif
+ region));
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Local class implementation
+///----------------------------------------------------------------------------
+
+//
+// LLPanelRegionInfo
+//
+
+// static
+void LLPanelRegionInfo::onBtnSet(void* user_data)
+{
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*)user_data;
+ if(!panel) return;
+ if (panel->sendUpdate())
+ {
+ panel->disableButton("apply_btn");
+ }
+}
+
+//static
+void LLPanelRegionInfo::onChangeChildCtrl(LLUICtrl* ctrl, void* user_data)
+{
+ if (ctrl)
+ {
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*) ctrl->getParent();
+ panel->updateChild(ctrl);
+ }
+}
+
+// static
+// Enables the "set" button if it is not already enabled
+void LLPanelRegionInfo::onChangeAnything(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionInfo* panel = (LLPanelRegionInfo*)user_data;
+ if(panel)
+ {
+ panel->enableButton("apply_btn");
+ }
+}
+
+// virtual
+BOOL LLPanelRegionInfo::postBuild()
+{
+ childSetAction("apply_btn", onBtnSet, this);
+ childDisable("apply_btn");
+ return TRUE;
+}
+
+// virtual
+void LLPanelRegionInfo::updateChild(LLUICtrl* child_ctr)
+{
+}
+
+// virtual
+bool LLPanelRegionInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ if (region) mHost = region->getHost();
+ return true;
+}
+
+void LLPanelRegionInfo::sendEstateOwnerMessage(
+ LLMessageSystem* msg,
+ const char* request,
+ const LLUUID& invoice,
+ const strings_t& strings)
+{
+ llinfos << "Sending estate request '" << request << "'" << llendl;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", request);
+ msg->addUUID("Invoice", invoice);
+ if(strings.empty())
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", NULL);
+ }
+ else
+ {
+ strings_t::const_iterator it = strings.begin();
+ strings_t::const_iterator end = strings.end();
+ for(; it != end; ++it)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", (*it).c_str());
+ }
+ }
+ msg->sendReliable(mHost);
+}
+
+void LLPanelRegionInfo::enableButton(const char* btn_name, BOOL enable)
+{
+ childSetEnabled(btn_name, enable);
+}
+
+void LLPanelRegionInfo::disableButton(const char* btn_name)
+{
+ childDisable(btn_name);
+}
+
+void LLPanelRegionInfo::initCtrl(const char* name)
+{
+ childSetCommitCallback(name, onChangeAnything, this);
+}
+
+void LLPanelRegionInfo::initHelpBtn(const char* name, const char* xml_alert)
+{
+ childSetAction(name, onClickHelp, (void*)xml_alert);
+}
+
+// static
+void LLPanelRegionInfo::onClickHelp(void* data)
+{
+ const char* xml_alert = (const char*)data;
+ gViewerWindow->alertXml(xml_alert);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionGeneralInfo
+//
+bool LLPanelRegionGeneralInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+ childSetEnabled("access_text", allow_modify);
+ childSetEnabled("access_combo", allow_modify);
+ childSetEnabled("kick_btn", allow_modify);
+ childSetEnabled("kick_all_btn", allow_modify);
+ childSetEnabled("im_btn", allow_modify);
+ childSetEnabled("manage_telehub_btn", allow_modify);
+
+ // Data gets filled in by processRegionInfo
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+BOOL LLPanelRegionGeneralInfo::postBuild()
+{
+ // Enable the "Apply" button if something is changed. JC
+ initCtrl("block_terraform_check");
+ initCtrl("block_fly_check");
+ initCtrl("allow_damage_check");
+ initCtrl("allow_land_resell_check");
+ initCtrl("allow_parcel_changes_check");
+ initCtrl("agent_limit_spin");
+ initCtrl("object_bonus_spin");
+ initCtrl("access_combo");
+ initCtrl("restrict_pushobject");
+
+ initHelpBtn("terraform_help", "HelpRegionBlockTerraform");
+ initHelpBtn("fly_help", "HelpRegionBlockFly");
+ initHelpBtn("damage_help", "HelpRegionAllowDamage");
+ initHelpBtn("agent_limit_help", "HelpRegionAgentLimit");
+ initHelpBtn("object_bonus_help", "HelpRegionObjectBonus");
+ initHelpBtn("access_help", "HelpRegionMaturity");
+ initHelpBtn("restrict_pushobject_help", "HelpRegionRestrictPushObject");
+ initHelpBtn("land_resell_help", "HelpRegionLandResell");
+ initHelpBtn("parcel_changes_help", "HelpParcelChanges");
+
+ childSetAction("kick_btn", onClickKick, this);
+ childSetAction("kick_all_btn", onClickKickAll, this);
+ childSetAction("im_btn", onClickMessage, this);
+ childSetAction("manage_telehub_btn", onClickManageTelehub, this);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickKick(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickKick" << llendl;
+ LLPanelRegionGeneralInfo* panelp = (LLPanelRegionGeneralInfo*)userdata;
+
+ // this depends on the grandparent view being a floater
+ // in order to set up floater dependency
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+ LLFloater* child_floater = LLFloaterAvatarPicker::show(onKickCommit, userdata, FALSE, TRUE);
+ parent_floater->addDependentFloater(child_floater);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onKickCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ if (names.empty() || ids.empty()) return;
+ if(ids[0].notNull())
+ {
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ strings_t strings;
+ // [0] = our agent id
+ // [1] = target agent id
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+
+ ids[0].toString(buffer);
+ strings.push_back(strings_t::value_type(buffer));
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "teleporthomeuser", invoice, strings);
+ }
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickKickAll(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickKickAll" << llendl;
+ gViewerWindow->alertXml("KickUsersFromRegion", onKickAllCommit, userdata);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onKickAllCommit(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ strings_t strings;
+ // [0] = our agent id
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ // historical message name
+ self->sendEstateOwnerMessage(gMessageSystem, "teleporthomeallusers", invoice, strings);
+ }
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickMessage(void* userdata)
+{
+ llinfos << "LLPanelRegionGeneralInfo::onClickMessage" << llendl;
+ gViewerWindow->alertXmlEditText("MessageRegion", LLString::format_map_t(),
+ NULL, NULL,
+ onMessageCommit, userdata);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onMessageCommit(S32 option, const LLString& text, void* userdata)
+{
+ if(option != 0) return;
+ if(text.empty()) return;
+ LLPanelRegionGeneralInfo* self = (LLPanelRegionGeneralInfo*)userdata;
+ if(!self) return;
+ llinfos << "Message to everyone: " << text << llendl;
+ strings_t strings;
+ // [0] grid_x, unused here
+ // [1] grid_y, unused here
+ // [2] agent_id of sender
+ // [3] sender name
+ // [4] message
+ strings.push_back("-1");
+ strings.push_back("-1");
+ char buffer[MAX_STRING];
+ gAgent.getID().toString(buffer);
+ strings.push_back(buffer);
+ std::string name;
+ gAgent.buildFullname(name);
+ strings.push_back(strings_t::value_type(name));
+ strings.push_back(strings_t::value_type(text));
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "simulatormessage", invoice, strings);
+}
+
+// static
+void LLPanelRegionGeneralInfo::onClickManageTelehub(void* data)
+{
+ LLFloaterRegionInfo::getInstance()->close();
+
+ LLFloaterTelehub::show();
+}
+
+// setregioninfo
+// strings[0] = 'Y' - block terraform, 'N' - not
+// strings[1] = 'Y' - block fly, 'N' - not
+// strings[2] = 'Y' - allow damage, 'N' - not
+// strings[3] = 'Y' - allow land sale, 'N' - not
+// strings[4] = agent limit
+// strings[5] = object bonus
+// strings[6] = sim access (0 = unknown, 13 = PG, 21 = Mature)
+// strings[7] = restrict pushobject
+// strings[8] = 'Y' - allow parcel subdivide, 'N' - not
+BOOL LLPanelRegionGeneralInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionGeneralInfo::sendUpdate()" << llendl;
+ strings_t strings;
+ //integers_t integers;
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%s", (childGetValue("block_terraform_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("block_fly_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_damage_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_land_resell_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ F32 value = (F32)childGetValue("agent_limit_spin").asReal();
+ sprintf(buffer, "%f", value);
+ strings.push_back(strings_t::value_type(buffer));
+
+ value = (F32)childGetValue("object_bonus_spin").asReal();
+ sprintf(buffer, "%f", value);
+ strings.push_back(strings_t::value_type(buffer));
+
+ U8 access = LLViewerRegion::stringToAccess(childGetValue("access_combo").asString().c_str());
+ sprintf(buffer, "%d", (S32)access);
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("restrict_pushobject").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ sprintf(buffer, "%s", (childGetValue("allow_parcel_changes_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(strings_t::value_type(buffer));
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ sendEstateOwnerMessage(gMessageSystem, "setregioninfo", invoice, strings);
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region
+ && access != region->getSimAccess() )
+ {
+ gViewerWindow->alertXml("RegionMaturityChange");
+ }
+
+ return TRUE;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionDebugInfo
+/////////////////////////////////////////////////////////////////////////////
+BOOL LLPanelRegionDebugInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+ initCtrl("disable_scripts_check");
+ initCtrl("disable_collisions_check");
+ initCtrl("disable_physics_check");
+
+ initHelpBtn("disable_scripts_help", "HelpRegionDisableScripts");
+ initHelpBtn("disable_collisions_help", "HelpRegionDisableCollisions");
+ initHelpBtn("disable_physics_help", "HelpRegionDisablePhysics");
+ initHelpBtn("top_colliders_help", "HelpRegionTopColliders");
+ initHelpBtn("top_scripts_help", "HelpRegionTopScripts");
+ initHelpBtn("restart_help", "HelpRegionRestart");
+
+ childSetAction("choose_avatar_btn", onClickChooseAvatar, this);
+ childSetAction("return_scripted_other_land_btn", onClickReturnScriptedOtherLand, this);
+ childSetAction("return_scripted_all_btn", onClickReturnScriptedAll, this);
+ childSetAction("top_colliders_btn", onClickTopColliders, this);
+ childSetAction("top_scripts_btn", onClickTopScripts, this);
+ childSetAction("restart_btn", onClickRestart, this);
+ childSetAction("cancel_restart_btn", onClickCancelRestart, this);
+
+ return TRUE;
+}
+
+// virtual
+bool LLPanelRegionDebugInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+
+ childSetEnabled("choose_avatar_btn", allow_modify);
+ childSetEnabled("return_scripted_other_land_btn", allow_modify && !mTargetAvatar.isNull());
+ childSetEnabled("return_scripted_all_btn", allow_modify && !mTargetAvatar.isNull());
+ childSetEnabled("top_colliders_btn", allow_modify);
+ childSetEnabled("top_scripts_btn", allow_modify);
+ childSetEnabled("restart_btn", allow_modify);
+ childSetEnabled("cancel_restart_btn", allow_modify);
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+// virtual
+BOOL LLPanelRegionDebugInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionDebugInfo::sendUpdate" << llendl;
+ strings_t strings;
+ char buffer[MAX_STRING];
+
+ sprintf(buffer, "%s", (childGetValue("disable_scripts_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ sprintf(buffer, "%s", (childGetValue("disable_collisions_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ sprintf(buffer, "%s", (childGetValue("disable_physics_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ sendEstateOwnerMessage(gMessageSystem, "setregiondebug", invoice, strings);
+ return TRUE;
+}
+
+void LLPanelRegionDebugInfo::onClickChooseAvatar(void* data)
+{
+ LLFloaterAvatarPicker::show(callbackAvatarID, data, FALSE, TRUE);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) data;
+ if (ids.empty() || names.empty()) return;
+ self->mTargetAvatar = ids[0];
+ self->childSetValue("target_avatar_name", LLSD(names[0]));
+ self->refreshFromRegion( gAgent.getRegion() );
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickReturnScriptedOtherLand(void* data)
+{
+ LLPanelRegionDebugInfo* panelp = (LLPanelRegionDebugInfo*) data;
+ if (panelp->mTargetAvatar.isNull()) return;
+
+ LLString::format_map_t args;
+ args["[USER_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+ gViewerWindow->alertXml("ReturnScriptedOnOthersLand", args, callbackReturnScriptedOtherLand, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackReturnScriptedOtherLand( S32 option, void* userdata )
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) userdata;
+ if (!self->mTargetAvatar.isNull())
+ {
+ U32 flags = 0;
+ flags = flags | SWD_OTHERS_LAND_ONLY;
+ flags = flags | SWD_ALWAYS_RETURN_OBJECTS;
+ flags |= SWD_SCRIPTED_ONLY;
+
+ send_sim_wide_deletes(self->mTargetAvatar, flags);
+ }
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickReturnScriptedAll(void* data)
+{
+ LLPanelRegionDebugInfo* panelp = (LLPanelRegionDebugInfo*) data;
+ if (panelp->mTargetAvatar.isNull()) return;
+
+
+ LLString::format_map_t args;
+ args["[USER_NAME]"] = panelp->childGetValue("target_avatar_name").asString();
+ gViewerWindow->alertXml("ReturnScriptedOnAllLand", args, callbackReturnScriptedAll, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackReturnScriptedAll( S32 option, void* userdata )
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*) userdata;
+ if (!self->mTargetAvatar.isNull())
+ {
+ U32 flags = 0;
+ flags |= SWD_ALWAYS_RETURN_OBJECTS;
+ flags |= SWD_SCRIPTED_ONLY;
+
+ send_sim_wide_deletes(self->mTargetAvatar, flags);
+ }
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickTopColliders(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("1"); // one physics step
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "colliders", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickTopScripts(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("6"); // top 5 scripts
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "scripts", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickRestart(void* data)
+{
+ gViewerWindow->alertXml("ConfirmRestart", callbackRestart, data);
+}
+
+// static
+void LLPanelRegionDebugInfo::callbackRestart(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("120");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "restart", invoice, strings);
+}
+
+// static
+void LLPanelRegionDebugInfo::onClickCancelRestart(void* data)
+{
+ LLPanelRegionDebugInfo* self = (LLPanelRegionDebugInfo*)data;
+ strings_t strings;
+ strings.push_back("-1");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "restart", invoice, strings);
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionTextureInfo
+//
+LLPanelRegionTextureInfo::LLPanelRegionTextureInfo() : LLPanelRegionInfo()
+{
+ // nothing.
+}
+
+bool LLPanelRegionTextureInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL allow_modify = gAgent.isGodlike() || (region && region->canManageEstate());
+ setCtrlsEnabled(allow_modify);
+ childDisable("apply_btn");
+
+ if (region)
+ {
+ childSetValue("region_text", LLSD(region->getName()));
+ }
+ else
+ {
+ childSetValue("region_text", LLSD(""));
+ }
+
+ if (!region) return LLPanelRegionInfo::refreshFromRegion(region);
+
+ LLVLComposition* compp = region->getComposition();
+ LLTextureCtrl* texture_ctrl;
+ char buffer[MAX_STRING];
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if(texture_ctrl)
+ {
+ lldebugs << "Detail Texture " << i << ": "
+ << compp->getDetailTextureID(i) << llendl;
+ LLUUID tmp_id(compp->getDetailTextureID(i));
+ texture_ctrl->setImageAssetID(tmp_id);
+ }
+ }
+
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ childSetValue(buffer, LLSD(compp->getStartHeight(i)));
+ sprintf(buffer, "height_range_spin_%d", i);
+ childSetValue(buffer, LLSD(compp->getHeightRange(i)));
+ }
+
+ // Call the parent for common book-keeping
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+
+BOOL LLPanelRegionTextureInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+ char buffer[MAX_STRING];
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ initCtrl(buffer);
+ }
+
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ initCtrl(buffer);
+ sprintf(buffer, "height_range_spin_%d", i);
+ initCtrl(buffer);
+ }
+
+// LLButton* btn = new LLButton("dump", LLRect(0, 20, 100, 0), "", onClickDump, this);
+// btn->setFollows(FOLLOWS_TOP|FOLLOWS_LEFT);
+// addChild(btn);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+void LLPanelRegionTextureInfo::draw()
+{
+ if(getVisible())
+ {
+ LLPanel::draw();
+ }
+}
+
+BOOL LLPanelRegionTextureInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionTextureInfo::sendUpdate()" << llendl;
+
+ // Make sure user hasn't chosen wacky textures.
+ if (!validateTextureSizes())
+ {
+ return FALSE;
+ }
+
+ LLTextureCtrl* texture_ctrl;
+ char buffer[MAX_STRING];
+ char buffer2[MAX_STRING];
+ char id_str[UUID_STR_LENGTH];
+ LLMessageSystem* msg = gMessageSystem;
+ strings_t strings;
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ sprintf(buffer, "texture_detail_%d", i);
+ texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if(texture_ctrl)
+ {
+ LLUUID tmp_id(texture_ctrl->getImageAssetID());
+ tmp_id.toString(id_str);
+ sprintf(buffer, "%d %s", i, id_str);
+ strings.push_back(strings_t::value_type(buffer));
+ }
+ }
+ sendEstateOwnerMessage(msg, "texturedetail", invoice, strings);
+ strings.clear();
+ for(S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ sprintf(buffer, "height_start_spin_%d", i);
+ sprintf(buffer2, "height_range_spin_%d", i);
+ sprintf(buffer, "%d %f %f", i, (F32)childGetValue(buffer).asReal(), (F32)childGetValue(buffer2).asReal());
+ strings.push_back(strings_t::value_type(buffer));
+ }
+ sendEstateOwnerMessage(msg, "textureheights", invoice, strings);
+ strings.clear();
+ sendEstateOwnerMessage(msg, "texturecommit", invoice, strings);
+ return TRUE;
+}
+
+BOOL LLPanelRegionTextureInfo::validateTextureSizes()
+{
+ for(S32 i = 0; i < TERRAIN_TEXTURE_COUNT; ++i)
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "texture_detail_%d", i);
+ LLTextureCtrl* texture_ctrl = LLViewerUICtrlFactory::getTexturePickerByName(this, buffer);
+ if (!texture_ctrl) continue;
+
+ LLUUID image_asset_id = texture_ctrl->getImageAssetID();
+ LLViewerImage* img = gImageList.getImage(image_asset_id);
+ S32 components = img->getComponents();
+ // Must ask for highest resolution version's width. JC
+ S32 width = img->getWidth(0);
+ S32 height = img->getHeight(0);
+
+ //llinfos << "texture detail " << i << " is " << width << "x" << height << "x" << components << llendl;
+
+ if (components != 3)
+ {
+ LLString::format_map_t args;
+ args["[TEXTURE_NUM]"] = llformat("%d",i+1);
+ args["[TEXTURE_BIT_DEPTH]"] = llformat("%d",components * 8);
+ gViewerWindow->alertXml("InvalidTerrainBitDepth", args);
+ return FALSE;
+ }
+
+ if (width > 512 || height > 512)
+ {
+
+ LLString::format_map_t args;
+ args["[TEXTURE_NUM]"] = i+1;
+ args["[TEXTURE_SIZE_X]"] = llformat("%d",width);
+ args["[TEXTURE_SIZE_Y]"] = llformat("%d",height);
+ gViewerWindow->alertXml("InvalidTerrainSize", args);
+ return FALSE;
+
+ }
+ }
+
+ return TRUE;
+}
+
+
+// static
+void LLPanelRegionTextureInfo::onClickDump(void* data)
+{
+ llinfos << "LLPanelRegionTextureInfo::onClickDump()" << llendl;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelRegionTerrainInfo
+/////////////////////////////////////////////////////////////////////////////
+BOOL LLPanelRegionTerrainInfo::postBuild()
+{
+ LLPanelRegionInfo::postBuild();
+
+ initHelpBtn("water_height_help", "HelpRegionWaterHeight");
+ initHelpBtn("terrain_raise_help", "HelpRegionTerrainRaise");
+ initHelpBtn("terrain_lower_help", "HelpRegionTerrainLower");
+ initHelpBtn("upload_raw_help", "HelpRegionUploadRaw");
+ initHelpBtn("download_raw_help", "HelpRegionDownloadRaw");
+ initHelpBtn("use_estate_sun_help", "HelpRegionUseEstateSun");
+ initHelpBtn("fixed_sun_help", "HelpRegionFixedSun");
+ initHelpBtn("bake_terrain_help", "HelpRegionBakeTerrain");
+
+ initCtrl("water_height_spin");
+ initCtrl("terrain_raise_spin");
+ initCtrl("terrain_lower_spin");
+
+ initCtrl("fixed_sun_check");
+ childSetCommitCallback("fixed_sun_check", onChangeFixedSun, this);
+ childSetCommitCallback("use_estate_sun_check", onChangeUseEstateTime, this);
+ childSetCommitCallback("sun_hour_slider", onChangeSunHour, this);
+
+ childSetAction("download_raw_btn", onClickDownloadRaw, this);
+ childSetAction("upload_raw_btn", onClickUploadRaw, this);
+ childSetAction("bake_terrain_btn", onClickBakeTerrain, this);
+
+ return TRUE;
+}
+
+// virtual
+bool LLPanelRegionTerrainInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ llinfos << "LLPanelRegionTerrainInfo::refreshFromRegion" << llendl;
+
+ BOOL owner_or_god = gAgent.isGodlike()
+ || (region && (region->getOwner() == gAgent.getID()));
+ BOOL owner_or_god_or_manager = owner_or_god
+ || (region && region->isEstateManager());
+ setCtrlsEnabled(owner_or_god_or_manager);
+ childDisable("apply_btn");
+
+ childSetEnabled("download_raw_btn", owner_or_god);
+ childSetEnabled("upload_raw_btn", owner_or_god);
+ childSetEnabled("bake_terrain_btn", owner_or_god);
+
+ return LLPanelRegionInfo::refreshFromRegion(region);
+}
+
+// virtual
+BOOL LLPanelRegionTerrainInfo::sendUpdate()
+{
+ llinfos << "LLPanelRegionTerrainInfo::sendUpdate" << llendl;
+ char buffer[MAX_STRING];
+ strings_t strings;
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+
+ sprintf(buffer, "%f", (F32)childGetValue("water_height_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("terrain_raise_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("terrain_lower_spin").asReal());
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (childGetValue("use_estate_sun_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (childGetValue("fixed_sun_check").asBoolean() ? "Y" : "N"));
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", (F32)childGetValue("sun_hour_slider").asReal() );
+ strings.push_back(buffer);
+
+ // Grab estate information in case the user decided to set the
+ // region back to estate time. JC
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return true;
+
+ LLTabContainerCommon* tab = LLUICtrlFactory::getTabContainerByName(floater, "tab");
+ if (!tab) return true;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)LLUICtrlFactory::getPanelByName(tab, "Estate");
+ if (!panel) return true;
+
+ BOOL estate_global_time = panel->getGlobalTime();
+ BOOL estate_fixed_sun = panel->getFixedSun();
+ F32 estate_sun_hour;
+ if (estate_global_time)
+ {
+ estate_sun_hour = 0.f;
+ }
+ else
+ {
+ estate_sun_hour = panel->getSunHour();
+ }
+
+ sprintf(buffer, "%s", (estate_global_time ? "Y" : "N") );
+ strings.push_back(buffer);
+ sprintf(buffer, "%s", (estate_fixed_sun ? "Y" : "N") );
+ strings.push_back(buffer);
+ sprintf(buffer, "%f", estate_sun_hour);
+ strings.push_back(buffer);
+
+ sendEstateOwnerMessage(gMessageSystem, "setregionterrain", invoice, strings);
+ return TRUE;
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeUseEstateTime(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) user_data;
+ if (!panel) return;
+ BOOL use_estate_sun = panel->childGetValue("use_estate_sun_check").asBoolean();
+ panel->childSetEnabled("fixed_sun_check", !use_estate_sun);
+ panel->childSetEnabled("sun_hour_slider", !use_estate_sun);
+ if (use_estate_sun)
+ {
+ panel->childSetValue("fixed_sun_check", LLSD(FALSE));
+ panel->childSetValue("sun_hour_slider", LLSD(0.f));
+ }
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeFixedSun(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) user_data;
+ if (!panel) return;
+ // Just enable the apply button. We let the sun-hour slider be enabled
+ // for both fixed-sun and non-fixed-sun. JC
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onChangeSunHour(LLUICtrl* ctrl, void*)
+{
+ // can't use userdata to get panel, slider uses it internally
+ LLPanelRegionTerrainInfo* panel = (LLPanelRegionTerrainInfo*) ctrl->getParent();
+ if (!panel) return;
+ panel->childEnable("apply_btn");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickDownloadRaw(void* data)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_RAW, "terrain.raw"))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ LLString filepath = picker.getFirstFile();
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("download filename");
+ strings.push_back(filepath);
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickUploadRaw(void* data)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(LLFilePicker::FFLOAD_RAW))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ LLString filepath = picker.getFirstFile();
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("upload filename");
+ strings.push_back(filepath);
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+
+ gViewerWindow->alertXml("RawUploadStarted");
+}
+
+// static
+void LLPanelRegionTerrainInfo::onClickBakeTerrain(void* data)
+{
+ gViewerWindow->alertXml("ConfirmBakeTerrain",
+ callbackBakeTerrain, data);
+}
+
+// static
+void LLPanelRegionTerrainInfo::callbackBakeTerrain(S32 option, void* data)
+{
+ if (option != 0) return;
+
+ LLPanelRegionTerrainInfo* self = (LLPanelRegionTerrainInfo*)data;
+ strings_t strings;
+ strings.push_back("bake");
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "terrain", invoice, strings);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// LLPanelEstateInfo
+//
+
+LLPanelEstateInfo::LLPanelEstateInfo()
+: LLPanelRegionInfo(),
+ mEstateID(0) // invalid
+{
+}
+
+// static
+void LLPanelEstateInfo::initDispatch(LLDispatcher& dispatch)
+{
+ std::string name;
+
+// name.assign("setowner");
+// static LLDispatchSetEstateOwner set_owner;
+// dispatch.addHandler(name, &set_owner);
+
+ name.assign("estateupdateinfo");
+ static LLDispatchEstateUpdateInfo estate_update_info;
+ dispatch.addHandler(name, &estate_update_info);
+
+ name.assign("setaccess");
+ static LLDispatchSetEstateAccess set_access;
+ dispatch.addHandler(name, &set_access);
+
+ estate_dispatch_initialized = true;
+}
+
+// static
+// Disables the sun-hour slider and the use fixed time check if the use global time is check
+void LLPanelEstateInfo::onChangeUseGlobalTime(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*) user_data;
+ if (panel)
+ {
+ bool enabled = !panel->childGetValue("use_global_time_check").asBoolean();
+ panel->childSetEnabled("sun_hour_slider", enabled);
+ panel->childSetEnabled("fixed_sun_check", enabled);
+ panel->childSetValue("fixed_sun_check", LLSD(FALSE));
+ panel->enableButton("apply_btn");
+ }
+}
+
+// Enables the sun-hour slider if the fixed-sun checkbox is set
+void LLPanelEstateInfo::onChangeFixedSun(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*) user_data;
+ if (panel)
+ {
+ bool enabled = !panel->childGetValue("fixed_sun_check").asBoolean();
+ panel->childSetEnabled("use_global_time_check", enabled);
+ panel->childSetValue("use_global_time_check", LLSD(FALSE));
+ panel->enableButton("apply_btn");
+ }
+}
+
+//---------------------------------------------------------------------------
+// Add/Remove estate access button callbacks
+//---------------------------------------------------------------------------
+
+// static
+void LLPanelEstateInfo::onClickAddAllowedAgent(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("allowed_avatar_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ //args
+
+ LLString::format_map_t args;
+ args["[MAX_AGENTS]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxAllowedAgentOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_ALLOWED_AGENT_ADD, "EstateAllowedAgentAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveAllowedAgent(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_ALLOWED_AGENT_REMOVE, "EstateAllowedAgentRemove", "allowed_avatar_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddAllowedGroup(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("allowed_group_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_GROUPS]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxAllowedGroupsOnRegion", args);
+ return;
+ }
+ if (isLindenEstate())
+ {
+ gViewerWindow->alertXml("ChangeLindenAccess", addAllowedGroup, user_data);
+ }
+ else
+ {
+ addAllowedGroup(0, user_data);
+ }
+}
+
+// static
+void LLPanelEstateInfo::addAllowedGroup(S32 option, void* user_data)
+{
+ if (option != 0) return;
+
+ LLFloaterGroups* widget;
+ widget = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ if (widget)
+ {
+ widget->setOkCallback(addAllowedGroup2, user_data);
+ }
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveAllowedGroup(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_ALLOWED_GROUP_REMOVE, "EstateAllowedGroupRemove", "allowed_group_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddBannedAgent(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("banned_avatar_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_ACCESS_IDS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_BANNED]"] = llformat("%d",ESTATE_MAX_ACCESS_IDS);
+ gViewerWindow->alertXml("MaxBannedAgentsOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_BANNED_AGENT_ADD, "EstateBannedAgentAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveBannedAgent(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_BANNED_AGENT_REMOVE, "EstateBannedAgentRemove", "banned_avatar_name_list");
+}
+
+// static
+void LLPanelEstateInfo::onClickAddEstateManager(void* user_data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)user_data;
+ LLCtrlListInterface *list = self->childGetListInterface("estate_manager_name_list");
+ if (!list) return;
+ if (list->getItemCount() >= ESTATE_MAX_MANAGERS)
+ {
+ LLString::format_map_t args;
+ args["[MAX_MANAGER]"] = llformat("%d",ESTATE_MAX_MANAGERS);
+ gViewerWindow->alertXml("MaxManagersOnRegion", args);
+ return;
+ }
+ accessAddCore(ESTATE_ACCESS_MANAGER_ADD, "EstateManagerAdd");
+}
+
+// static
+void LLPanelEstateInfo::onClickRemoveEstateManager(void* user_data)
+{
+ accessRemoveCore(ESTATE_ACCESS_MANAGER_REMOVE, "EstateManagerRemove", "estate_manager_name_list");
+}
+
+//---------------------------------------------------------------------------
+// Kick from estate methods
+//---------------------------------------------------------------------------
+struct LLKickFromEstateInfo
+{
+ LLPanelEstateInfo *mEstatePanelp;
+ LLString mDialogName;
+ LLUUID mAgentID;
+};
+
+void LLPanelEstateInfo::onClickKickUser(void *user_data)
+{
+ LLPanelEstateInfo* panelp = (LLPanelEstateInfo*)user_data;
+
+ // this depends on the grandparent view being a floater
+ // in order to set up floater dependency
+ LLFloater* parent_floater = gFloaterView->getParentFloater(panelp);
+ LLFloater* child_floater = LLFloaterAvatarPicker::show(LLPanelEstateInfo::onKickUserCommit, user_data, FALSE, TRUE);
+ parent_floater->addDependentFloater(child_floater);
+}
+
+void LLPanelEstateInfo::onKickUserCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata)
+{
+ if (names.empty() || ids.empty()) return;
+
+ //check to make sure there is one valid user and id
+ if( (ids[0].isNull()) ||
+ (names[0].length() == 0) )
+ {
+ return;
+ }
+
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)userdata;
+ if(!self) return;
+
+ //keep track of what user they want to kick and other misc info
+ LLKickFromEstateInfo *kick_info = new LLKickFromEstateInfo();
+ kick_info->mEstatePanelp = self;
+ kick_info->mDialogName = "EstateKickUser";
+ kick_info->mAgentID = ids[0];
+
+ //Bring up a confirmation dialog
+ LLString::format_map_t args;
+ args["[EVIL_USER]"] = names[0];
+ gViewerWindow->alertXml(kick_info->mDialogName, args, LLPanelEstateInfo::kickUserConfirm, (void*)kick_info);
+
+}
+
+void LLPanelEstateInfo::kickUserConfirm(S32 option, void* userdata)
+{
+ //extract the callback parameter
+ LLKickFromEstateInfo *kick_info = (LLKickFromEstateInfo*) userdata;
+ if (!kick_info) return;
+
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ strings_t strings;
+ char buffer[MAX_STRING];
+
+ switch(option)
+ {
+ case 0:
+ //Kick User
+ kick_info->mAgentID.toString(buffer);
+ strings.push_back(strings_t::value_type(buffer));
+
+ kick_info->mEstatePanelp->sendEstateOwnerMessage(gMessageSystem, "kickestate", invoice, strings);
+ break;
+ default:
+ break;
+ }
+
+ delete kick_info;
+ kick_info = NULL;
+}
+
+//---------------------------------------------------------------------------
+// Core Add/Remove estate access methods
+//---------------------------------------------------------------------------
+std::string all_estates_text()
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return "(error)";
+
+ std::string owner = panel->getOwnerName();
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (gAgent.isGodlike())
+ {
+ return llformat("all estates\nowned by %s", owner.c_str());
+ }
+ else if (region && region->getOwner() == gAgent.getID())
+ {
+ return "all estates you own";
+ }
+ else if (region && region->isEstateManager())
+ {
+ return llformat("all estates that\nyou manage for %s", owner.c_str());
+ }
+ else
+ {
+ return "(error)";
+ }
+}
+
+// static
+bool LLPanelEstateInfo::isLindenEstate()
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return false;
+
+ U32 estate_id = panel->getEstateID();
+ return (estate_id <= ESTATE_LAST_LINDEN);
+}
+
+struct LLEstateAccessChangeInfo
+{
+ U32 mOperationFlag; // ESTATE_ACCESS_BANNED_AGENT_ADD, _REMOVE, etc.
+ LLString mDialogName;
+ LLUUID mAgentOrGroupID;
+};
+
+// Special case callback for groups, since it has different callback format than names
+// static
+void LLPanelEstateInfo::addAllowedGroup2(LLUUID id, void* user_data)
+{
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mOperationFlag = ESTATE_ACCESS_ALLOWED_GROUP_ADD;
+ change_info->mDialogName = "EstateAllowedGroupAdd";
+ change_info->mAgentOrGroupID = id;
+
+ if (isLindenEstate())
+ {
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName, args, accessCoreConfirm, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore(U32 operation_flag, const char* dialog_name)
+{
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mOperationFlag = operation_flag;
+ change_info->mDialogName = dialog_name;
+ // agent id filled in after avatar picker
+
+ if (isLindenEstate())
+ {
+ gViewerWindow->alertXml("ChangeLindenAccess", accessAddCore2, change_info);
+ }
+ else
+ {
+ // same as clicking "OK"
+ accessAddCore2(0, change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore2(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (option != 0)
+ {
+ // abort change
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ // avatar picker no multi-select, yes close-on-select
+ LLFloaterAvatarPicker::show(accessAddCore3, (void*)change_info, FALSE, TRUE);
+}
+
+// static
+void LLPanelEstateInfo::accessAddCore3(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (!change_info) return;
+ if (ids.empty())
+ {
+ // User didn't select a name.
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+ // User did select a name.
+ change_info->mAgentOrGroupID = ids[0];
+
+ // Can't put estate owner on ban list
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return;
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return;
+
+ if ((change_info->mOperationFlag & ESTATE_ACCESS_BANNED_AGENT_ADD)
+ && (region->getOwner() == change_info->mAgentOrGroupID))
+ {
+ gViewerWindow->alertXml("OwnerCanNotBeDenied");
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ if (isLindenEstate())
+ {
+ // just apply to this estate
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ // ask if this estate or all estates with this owner
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName, args, accessCoreConfirm, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessRemoveCore(U32 operation_flag, const char* dialog_name, const char* list_ctrl_name)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return;
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(panel, list_ctrl_name);
+ if (!name_list) return;
+ LLScrollListItem* item = name_list->getFirstSelected();
+ if (!item) return;
+ LLUUID agent_id = item->getUUID();
+
+ LLEstateAccessChangeInfo* change_info = new LLEstateAccessChangeInfo;
+ change_info->mAgentOrGroupID = agent_id;
+ change_info->mOperationFlag = operation_flag;
+ change_info->mDialogName = dialog_name;
+
+ if (isLindenEstate())
+ {
+ // warn on change linden estate
+ gViewerWindow->alertXml("ChangeLindenAccess",
+ accessRemoveCore2,
+ (void*)change_info);
+ }
+ else
+ {
+ // just proceed, as if clicking OK
+ accessRemoveCore2(0, (void*)change_info);
+ }
+}
+
+// static
+void LLPanelEstateInfo::accessRemoveCore2(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ if (option != 0)
+ {
+ // abort
+ delete change_info;
+ change_info = NULL;
+ return;
+ }
+
+ // If Linden estate, can only apply to "this" estate, not all estates
+ // owned by NULL.
+ if (isLindenEstate())
+ {
+ accessCoreConfirm(0, (void*)change_info);
+ }
+ else
+ {
+ LLString::format_map_t args;
+ args["[ALL_ESTATES]"] = all_estates_text();
+ gViewerWindow->alertXml(change_info->mDialogName,
+ args,
+ accessCoreConfirm,
+ (void*)change_info);
+ }
+}
+
+// Used for both access add and remove operations, depending on the mOperationFlag
+// passed in (ESTATE_ACCESS_BANNED_AGENT_ADD, ESTATE_ACCESS_ALLOWED_AGENT_REMOVE, etc.)
+// static
+void LLPanelEstateInfo::accessCoreConfirm(S32 option, void* data)
+{
+ LLEstateAccessChangeInfo* change_info = (LLEstateAccessChangeInfo*)data;
+ U32 flags = change_info->mOperationFlag;
+ switch(option)
+ {
+ case 0:
+ // This estate
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ break;
+ case 1:
+ {
+ // All estates, either than I own or manage for this owner.
+ // This will be verified on simulator. JC
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) break;
+ if (region->getOwner() == gAgent.getID()
+ || gAgent.isGodlike())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_ALL_ESTATES;
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ }
+ else if (region->isEstateManager())
+ {
+ flags |= ESTATE_ACCESS_APPLY_TO_MANAGED_ESTATES;
+ sendEstateAccessDelta(flags, change_info->mAgentOrGroupID);
+ }
+ break;
+ }
+ case 2:
+ default:
+ break;
+ }
+ delete change_info;
+ change_info = NULL;
+}
+
+// key = "estateaccessdelta"
+// str(estate_id) will be added to front of list by forward_EstateOwnerRequest_to_dataserver
+// str[0] = str(agent_id) requesting the change
+// str[1] = str(flags) (ESTATE_ACCESS_DELTA_*)
+// str[2] = str(agent_id) to add or remove
+// static
+void LLPanelEstateInfo::sendEstateAccessDelta(U32 flags, const LLUUID& agent_or_group_id)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estateaccessdelta");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ char buf[MAX_STRING];
+ gAgent.getID().toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ sprintf(buf, "%u", flags);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ agent_or_group_id.toString(buf);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+
+ if (flags & (ESTATE_ACCESS_ALLOWED_AGENT_ADD | ESTATE_ACCESS_ALLOWED_AGENT_REMOVE |
+ ESTATE_ACCESS_BANNED_AGENT_ADD | ESTATE_ACCESS_BANNED_AGENT_REMOVE))
+ {
+
+ panel->clearAccessLists();
+ }
+
+ gAgent.sendReliableMessage();
+}
+
+
+bool LLPanelEstateInfo::refreshFromRegion(LLViewerRegion* region)
+{
+ BOOL god = gAgent.isGodlike();
+ BOOL owner = (region && (region->getOwner() == gAgent.getID()));
+ BOOL manager = (region && region->isEstateManager());
+ setCtrlsEnabled(god || owner || manager);
+ childDisable("apply_btn");
+ childSetEnabled("add_allowed_avatar_btn", god || owner || manager);
+ childSetEnabled("remove_allowed_avatar_btn", god || owner || manager);
+ childSetEnabled("add_allowed_group_btn", god || owner || manager);
+ childSetEnabled("remove_allowed_group_btn", god || owner || manager);
+ childSetEnabled("add_banned_avatar_btn", god || owner || manager);
+ childSetEnabled("remove_banned_avatar_btn", god || owner || manager);
+ childSetEnabled("message_estate_btn", god || owner || manager);
+ childSetEnabled("kick_user_from_estate_btn", god || owner || manager);
+
+ // estate managers can't add estate managers
+ childSetEnabled("add_estate_manager_btn", god || owner);
+ childSetEnabled("remove_estate_manager_btn", god || owner);
+ childSetEnabled("estate_manager_name_list", god || owner);
+
+ // let the parent class handle the general data collection.
+ bool rv = LLPanelRegionInfo::refreshFromRegion(region);
+
+ // We want estate info. To make sure it works across region
+ // boundaries and multiple packets, we add a serial number to the
+ // integers and track against that on update.
+ strings_t strings;
+ //integers_t integers;
+ //LLFloaterRegionInfo::incrementSerial();
+ LLFloaterRegionInfo::nextInvoice();
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ //integers.push_back(LLFloaterRegionInfo::());::getPanelEstate();
+
+
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ panel->clearAccessLists();
+
+
+ sendEstateOwnerMessage(gMessageSystem, "getinfo", invoice, strings);
+
+
+
+
+ return rv;
+}
+
+void LLPanelEstateInfo::updateChild(LLUICtrl* child_ctrl)
+{
+ if (checkRemovalButton(child_ctrl->getName()))
+ {
+ // do nothing
+ }
+ else if (checkSunHourSlider(child_ctrl))
+ {
+ // do nothing
+ }
+}
+
+bool LLPanelEstateInfo::estateUpdate(LLMessageSystem* msg)
+{
+ llinfos << "LLPanelEstateInfo::estateUpdate()" << llendl;
+ return false;
+}
+
+
+BOOL LLPanelEstateInfo::postBuild()
+{
+ // set up the callbacks for the generic controls
+ initCtrl("externally_visible_check");
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ initCtrl("mainland_visible_check");
+#endif
+ initCtrl("use_global_time_check");
+ initCtrl("fixed_sun_check");
+ initCtrl("allow_direct_teleport");
+ initCtrl("deny_anonymous");
+ initCtrl("deny_identified");
+ initCtrl("deny_transacted");
+
+ initHelpBtn("estate_manager_help", "HelpEstateEstateManager");
+ initHelpBtn("use_global_time_help", "HelpEstateUseGlobalTime");
+ initHelpBtn("fixed_sun_help", "HelpEstateFixedSun");
+ initHelpBtn("externally_visible_help", "HelpEstateExternallyVisible");
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ initHelpBtn("mainland_visible_help", "HelpEstateMainlandVisible");
+#endif
+ initHelpBtn("allow_direct_teleport_help", "HelpEstateAllowDirectTeleport");
+ initHelpBtn("allow_resident_help", "HelpEstateAllowResident");
+ initHelpBtn("allow_group_help", "HelpEstateAllowGroup");
+ initHelpBtn("ban_resident_help", "HelpEstateBanResident");
+
+ // set up the use global time checkbox
+ childSetCommitCallback("use_global_time_check", onChangeUseGlobalTime, this);
+ childSetCommitCallback("fixed_sun_check", onChangeFixedSun, this);
+ childSetCommitCallback("sun_hour_slider", onChangeChildCtrl, this);
+
+ childSetCommitCallback("allowed_avatar_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl *avatar_name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_avatar_name_list");
+ if (avatar_name_list)
+ {
+ avatar_name_list->setCommitOnSelectionChange(TRUE);
+ avatar_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_allowed_avatar_btn", onClickAddAllowedAgent, this);
+ childSetAction("remove_allowed_avatar_btn", onClickRemoveAllowedAgent, this);
+
+ childSetCommitCallback("allowed_group_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* group_name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_group_name_list");
+ if (group_name_list)
+ {
+ group_name_list->setCommitOnSelectionChange(TRUE);
+ group_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_allowed_group_btn", onClickAddAllowedGroup, this);
+ childSetAction("remove_allowed_group_btn", onClickRemoveAllowedGroup, this);
+
+ childSetCommitCallback("banned_avatar_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* banned_name_list = LLViewerUICtrlFactory::getNameListByName(this, "banned_avatar_name_list");
+ if (banned_name_list)
+ {
+ banned_name_list->setCommitOnSelectionChange(TRUE);
+ banned_name_list->setMaxItemCount(ESTATE_MAX_ACCESS_IDS);
+ }
+
+ childSetAction("add_banned_avatar_btn", onClickAddBannedAgent, this);
+ childSetAction("remove_banned_avatar_btn", onClickRemoveBannedAgent, this);
+
+ childSetCommitCallback("estate_manager_name_list", onChangeChildCtrl, this);
+ LLNameListCtrl* manager_name_list = LLViewerUICtrlFactory::getNameListByName(this, "estate_manager_name_list");
+ if (manager_name_list)
+ {
+ manager_name_list->setCommitOnSelectionChange(TRUE);
+ manager_name_list->setMaxItemCount(ESTATE_MAX_MANAGERS);
+ }
+
+ childSetAction("add_estate_manager_btn", onClickAddEstateManager, this);
+ childSetAction("remove_estate_manager_btn", onClickRemoveEstateManager, this);
+ childSetAction("message_estate_btn", onClickMessageEstate, this);
+ childSetAction("kick_user_from_estate_btn", onClickKickUser, this);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+
+BOOL LLPanelEstateInfo::sendUpdate()
+{
+ llinfos << "LLPanelEsateInfo::sendUpdate()" << llendl;
+
+ if (getEstateID() <= ESTATE_LAST_LINDEN)
+ {
+ // trying to change reserved estate, warn
+ gViewerWindow->alertXml("ChangeLindenEstate",
+ callbackChangeLindenEstate,
+ this);
+ }
+ else
+ {
+ // for normal estates, just make the change
+ callbackChangeLindenEstate(0, this);
+ }
+ return TRUE;
+}
+
+// static
+void LLPanelEstateInfo::callbackChangeLindenEstate(S32 option, void* data)
+{
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)data;
+ switch(option)
+ {
+ case 0:
+ // send the update
+ LLFloaterRegionInfo::nextInvoice();
+ self->commitEstateInfo();
+ break;
+ case 1:
+ default:
+ // do nothing
+ break;
+ }
+}
+
+
+/*
+// Request = "getowner"
+// SParam[0] = "" (empty string)
+// IParam[0] = serial
+void LLPanelEstateInfo::getEstateOwner()
+{
+ // TODO -- disable the panel
+ // and call this function whenever we cross a region boundary
+ // re-enable when owner matches, and get new estate info
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_EstateOwnerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addStringFast(_PREHASH_Request, "getowner");
+
+ // we send an empty string so that the variable block is not empty
+ msg->nextBlockFast(_PREHASH_StringData);
+ msg->addStringFast(_PREHASH_SParam, "");
+
+ msg->nextBlockFast(_PREHASH_IntegerData);
+ msg->addS32Fast(_PREHASH_IParam, LLFloaterRegionInfo::getSerial());
+
+ gAgent.sendMessage();
+}
+*/
+
+// key = "estatechangeinfo"
+// strings[0] = str(estate_id) (added by simulator before relay - not here)
+// strings[1] = estate_name
+// strings[2] = str(estate_flags)
+// strings[3] = str((S32)(sun_hour * 1024.f))
+void LLPanelEstateInfo::commitEstateInfo()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estatechangeinfo");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", getEstateName());
+
+ char buf[MAX_STRING];
+ sprintf(buf, "%u", computeEstateFlags());
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ F32 sun_hour = getSunHour();
+ if (childGetValue("use_global_time_check").asBoolean())
+ {
+ sun_hour = 0.f; // 0 = global time
+ }
+
+ sprintf(buf, "%d", (S32)(sun_hour*1024.0f));
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buf);
+
+ gAgent.sendMessage();
+}
+
+void LLPanelEstateInfo::setEstateFlags(U32 flags)
+{
+ childSetValue("externally_visible_check", LLSD(flags & REGION_FLAGS_EXTERNALLY_VISIBLE ? TRUE : FALSE) );
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ childSetValue("mainland_visible_check", LLSD(flags & REGION_FLAGS_MAINLAND_VISIBLE ? TRUE : FALSE) );
+#endif
+ childSetValue("fixed_sun_check", LLSD(flags & REGION_FLAGS_SUN_FIXED ? TRUE : FALSE) );
+ childSetValue("allow_direct_teleport", LLSD(flags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT ? TRUE : FALSE) );
+ childSetValue("deny_anonymous", LLSD(flags & REGION_FLAGS_DENY_ANONYMOUS ? TRUE : FALSE) );
+ childSetValue("deny_identified", LLSD(flags & REGION_FLAGS_DENY_IDENTIFIED ? TRUE : FALSE) );
+ childSetValue("deny_transacted", LLSD(flags & REGION_FLAGS_DENY_TRANSACTED ? TRUE : FALSE) );
+
+}
+
+U32 LLPanelEstateInfo::computeEstateFlags()
+{
+ U32 flags = 0;
+
+ if (childGetValue("externally_visible_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_EXTERNALLY_VISIBLE;
+ }
+#if LL_ENABLE_MAINLAND_VISIBLE_CONTROL
+ // This flag is ignored by everything. 2006-11-17 Phoenix.
+ if (childGetValue("mainland_visible_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_MAINLAND_VISIBLE;
+ }
+#else
+ flags |= REGION_FLAGS_MAINLAND_VISIBLE;
+#endif
+
+ if (childGetValue("allow_direct_teleport").asBoolean())
+ {
+ flags |= REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+
+ if (childGetValue("fixed_sun_check").asBoolean())
+ {
+ flags |= REGION_FLAGS_SUN_FIXED;
+ }
+
+ if (childGetValue("deny_anonymous").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_ANONYMOUS;
+ }
+
+ if (childGetValue("deny_identified").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_IDENTIFIED;
+ }
+
+ if (childGetValue("deny_transacted").asBoolean())
+ {
+ flags |= REGION_FLAGS_DENY_TRANSACTED;
+ }
+
+ return flags;
+}
+
+BOOL LLPanelEstateInfo::getGlobalTime()
+{
+ return childGetValue("use_global_time_check").asBoolean();
+}
+
+void LLPanelEstateInfo::setGlobalTime(bool b)
+{
+ childSetValue("use_global_time_check", LLSD(b));
+ childSetEnabled("fixed_sun_check", LLSD(!b));
+ childSetEnabled("sun_hour_slider", LLSD(!b));
+ if (b)
+ {
+ childSetValue("sun_hour_slider", LLSD(0.f));
+ }
+}
+
+
+BOOL LLPanelEstateInfo::getFixedSun()
+{
+ return childGetValue("fixed_sun_check").asBoolean();
+}
+
+void LLPanelEstateInfo::setSunHour(F32 sun_hour)
+{
+ if(sun_hour < 6.0f)
+ {
+ sun_hour = 24.0f + sun_hour;
+ }
+ childSetValue("sun_hour_slider", LLSD(sun_hour));
+}
+
+F32 LLPanelEstateInfo::getSunHour()
+{
+ if (childIsEnabled("sun_hour_slider"))
+ {
+ return (F32)childGetValue("sun_hour_slider").asReal();
+ }
+ return 0.f;
+}
+
+const std::string LLPanelEstateInfo::getEstateName() const
+{
+ return childGetValue("estate_name").asString();
+}
+
+void LLPanelEstateInfo::setEstateName(const std::string& name)
+{
+ childSetValue("estate_name", LLSD(name));
+}
+
+const std::string LLPanelEstateInfo::getOwnerName() const
+{
+ return childGetValue("estate_owner").asString();
+}
+
+void LLPanelEstateInfo::setOwnerName(const std::string& name)
+{
+ childSetValue("estate_owner", LLSD(name));
+}
+
+void LLPanelEstateInfo::setAccessAllowedEnabled(bool enable_agent,
+ bool enable_group,
+ bool enable_ban)
+{
+ childSetEnabled("allow_resident_label", enable_agent);
+ childSetEnabled("allowed_avatar_name_list", enable_agent);
+ childSetVisible("allowed_avatar_name_list", enable_agent);
+ childSetEnabled("add_allowed_avatar_btn", enable_agent);
+ childSetEnabled("remove_allowed_avatar_btn", enable_agent);
+
+ // Groups
+ childSetEnabled("allow_group_label", enable_group);
+ childSetEnabled("allowed_group_name_list", enable_group);
+ childSetVisible("allowed_group_name_list", enable_group);
+ childSetEnabled("add_allowed_group_btn", enable_group);
+ childSetEnabled("remove_allowed_group_btn", enable_group);
+
+ // Ban
+ childSetEnabled("ban_resident_label", enable_ban);
+ childSetEnabled("banned_avatar_name_list", enable_ban);
+ childSetVisible("banned_avatar_name_list", enable_ban);
+ childSetEnabled("add_banned_avatar_btn", enable_ban);
+ childSetEnabled("remove_banned_avatar_btn", enable_ban);
+
+ // Update removal buttons if needed
+ if (enable_agent)
+ {
+ checkRemovalButton("allowed_avatar_name_list");
+ }
+
+ if (enable_group)
+ {
+ checkRemovalButton("allowed_group_name_list");
+ }
+
+ if (enable_ban)
+ {
+ checkRemovalButton("banned_avatar_name_list");
+ }
+}
+
+// static
+void LLPanelEstateInfo::callbackCacheName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*)
+{
+ LLPanelEstateInfo* self = LLFloaterRegionInfo::getPanelEstate();
+ if (!self) return;
+
+ std::string name;
+
+ if (id.isNull())
+ {
+ name = "(none)";
+ }
+ else
+ {
+ name = first;
+ name += " ";
+ name += last;
+ }
+
+ self->setOwnerName(name);
+}
+
+void LLPanelEstateInfo::clearAccessLists()
+{
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(this, "allowed_avatar_name_list");
+ if (name_list)
+ {
+ name_list->deleteAllItems();
+ }
+
+ name_list = LLViewerUICtrlFactory::getNameListByName(this, "banned_avatar_name_list");
+ if (name_list)
+ {
+ name_list->deleteAllItems();
+ }
+}
+
+// enables/disables the "remove" button for the various allow/ban lists
+BOOL LLPanelEstateInfo::checkRemovalButton(std::string name)
+{
+ std::string btn_name = "";
+ if (name == "allowed_avatar_name_list")
+ {
+ btn_name = "remove_allowed_avatar_btn";
+ }
+ else if (name == "allowed_group_name_list")
+ {
+ btn_name = "remove_allowed_group_btn";
+ }
+ else if (name == "banned_avatar_name_list")
+ {
+ btn_name = "remove_banned_avatar_btn";
+ }
+ else if (name == "estate_manager_name_list")
+ {
+ btn_name = "remove_estate_manager_btn";
+ }
+
+ // enable the remove button if something is selected
+ LLNameListCtrl* name_list = LLViewerUICtrlFactory::getNameListByName(this, name);
+ childSetEnabled(btn_name.c_str(), name_list && name_list->getFirstSelected() ? TRUE : FALSE);
+
+ return (btn_name != "");
+}
+
+BOOL LLPanelEstateInfo::checkSunHourSlider(LLUICtrl* child_ctrl)
+{
+ BOOL found_child_ctrl = FALSE;
+ if (child_ctrl->getName() == "sun_hour_slider")
+ {
+ enableButton("apply_btn");
+ found_child_ctrl = TRUE;
+ }
+ return found_child_ctrl;
+}
+
+// static
+void LLPanelEstateInfo::onClickMessageEstate(void* userdata)
+{
+ llinfos << "LLPanelEstateInfo::onClickMessageEstate" << llendl;
+ gViewerWindow->alertXmlEditText("MessageEstate", LLString::format_map_t(),
+ NULL, NULL,
+ onMessageCommit, userdata);
+}
+
+// static
+void LLPanelEstateInfo::onMessageCommit(S32 option, const LLString& text, void* userdata)
+{
+ if(option != 0) return;
+ if(text.empty()) return;
+ LLPanelEstateInfo* self = (LLPanelEstateInfo*)userdata;
+ if(!self) return;
+ llinfos << "Message to everyone: " << text << llendl;
+ strings_t strings;
+ //integers_t integers;
+ std::string name;
+ gAgent.buildFullname(name);
+ strings.push_back(strings_t::value_type(name));
+ strings.push_back(strings_t::value_type(text));
+ LLUUID invoice(LLFloaterRegionInfo::getLastInvoice());
+ self->sendEstateOwnerMessage(gMessageSystem, "instantmessage", invoice, strings);
+}
+
+LLPanelEstateCovenant::LLPanelEstateCovenant()
+: mCovenantID(LLUUID::null)
+{
+}
+
+// virtual
+bool LLPanelEstateCovenant::refreshFromRegion(LLViewerRegion* region)
+{
+ LLTextBox* region_name = (LLTextBox*)getChildByName("region_name_text");
+ if (region_name)
+ {
+ region_name->setText(region->getName());
+ }
+
+ LLTextBox* resellable_clause = (LLTextBox*)getChildByName("resellable_clause");
+ if (resellable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ {
+ resellable_clause->setText(childGetText("can_not_resell"));
+ }
+ else
+ {
+ resellable_clause->setText(childGetText("can_resell"));
+ }
+ }
+
+ LLTextBox* changeable_clause = (LLTextBox*)getChildByName("changeable_clause");
+ if (changeable_clause)
+ {
+ if (region->getRegionFlags() & REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ changeable_clause->setText(childGetText("can_change"));
+ }
+ else
+ {
+ changeable_clause->setText(childGetText("can_not_change"));
+ }
+ }
+
+ // let the parent class handle the general data collection.
+ bool rv = LLPanelRegionInfo::refreshFromRegion(region);
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessage("EstateCovenantRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->sendReliable(region->getHost());
+ return rv;
+}
+
+// virtual
+bool LLPanelEstateCovenant::estateUpdate(LLMessageSystem* msg)
+{
+ llinfos << "LLPanelEstateCovenant::estateUpdate()" << llendl;
+ return true;
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::postBuild()
+{
+ initHelpBtn("covenant_help", "HelpEstateCovenant");
+ mEstateNameText = (LLTextBox*)getChildByName("estate_name_text");
+ mEstateOwnerText = (LLTextBox*)getChildByName("estate_owner_text");
+ mLastModifiedText = (LLTextBox*)getChildByName("covenant_timestamp_text");
+ mEditor = (LLViewerTextEditor*)getChildByName("covenant_editor");
+ if (mEditor) mEditor->setHandleEditKeysDirectly(TRUE);
+ LLButton* reset_button = (LLButton*)getChildByName("reset_covenant");
+ reset_button->setEnabled(gAgent.canManageEstate());
+ reset_button->setClickedCallback(LLPanelEstateCovenant::resetCovenantID, NULL);
+
+ return LLPanelRegionInfo::postBuild();
+}
+
+// virtual
+void LLPanelEstateCovenant::updateChild(LLUICtrl* child_ctrl)
+{
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+
+ if (!gAgent.canManageEstate())
+ {
+ *accept = ACCEPT_NO;
+ return TRUE;
+ }
+
+ switch(cargo_type)
+ {
+ case DAD_NOTECARD:
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if (item && drop)
+ {
+ gViewerWindow->alertXml("EstateChangeCovenant",
+ LLPanelEstateCovenant::confirmChangeCovenantCallback,
+ item);
+ }
+ break;
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+
+ return TRUE;
+}
+
+// static
+void LLPanelEstateCovenant::confirmChangeCovenantCallback(S32 option, void* userdata)
+{
+ LLInventoryItem* item = (LLInventoryItem*)userdata;
+ LLPanelEstateCovenant* self = LLFloaterRegionInfo::getPanelCovenant();
+
+ if (!item || !self) return;
+
+ switch(option)
+ {
+ case 0:
+ self->loadInvItem(item);
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLPanelEstateCovenant::resetCovenantID(void* userdata)
+{
+ gViewerWindow->alertXml("EstateChangeCovenant",
+ LLPanelEstateCovenant::confirmResetCovenantCallback,
+ NULL);
+}
+
+// static
+void LLPanelEstateCovenant::confirmResetCovenantCallback(S32 option, void* userdata)
+{
+ LLPanelEstateCovenant* self = LLFloaterRegionInfo::getPanelCovenant();
+ if (!self) return;
+
+ switch(option)
+ {
+ case 0:
+ self->loadInvItem(NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+void LLPanelEstateCovenant::loadInvItem(LLInventoryItem *itemp)
+{
+ const BOOL high_priority = TRUE;
+ if (itemp)
+ {
+ gAssetStorage->getInvItemAsset(gAgent.getRegionHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ itemp->getPermissions().getOwner(),
+ LLUUID::null,
+ itemp->getUUID(),
+ itemp->getAssetUUID(),
+ itemp->getType(),
+ onLoadComplete,
+ (void*)this,
+ high_priority);
+ mAssetStatus = ASSET_LOADING;
+ }
+ else
+ {
+ mAssetStatus = ASSET_LOADED;
+ setCovenantTextEditor("There is no Covenant provided for this Estate.");
+ sendChangeCovenantID(LLUUID::null);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLPanelEstateCovenant::onLoadComplete()" << llendl;
+ LLPanelEstateCovenant* panelp = (LLPanelEstateCovenant*)user_data;
+ if( panelp )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ if( !panelp->mEditor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing estate covenant." << llendl;
+ gViewerWindow->alertXml("ProblemImportingEstateCovenant");
+ }
+ else
+ {
+ panelp->sendChangeCovenantID(asset_uuid);
+ }
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ panelp->sendChangeCovenantID(asset_uuid);
+ }
+ delete[] buffer;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ gViewerWindow->alertXml("MissingNotecardAssetID");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ gViewerWindow->alertXml("NotAllowedToViewNotecard");
+ }
+ else
+ {
+ gViewerWindow->alertXml("UnableToLoadNotecard");
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ }
+ panelp->mAssetStatus = ASSET_LOADED;
+ panelp->setCovenantID(asset_uuid);
+ }
+}
+
+// key = "estatechangecovenantid"
+// strings[0] = str(estate_id) (added by simulator before relay - not here)
+// strings[1] = str(covenant_id)
+void LLPanelEstateCovenant::sendChangeCovenantID(const LLUUID &asset_id)
+{
+ if (asset_id != getCovenantID())
+ {
+ setCovenantID(asset_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("EstateOwnerMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "estatechangecovenantid");
+ msg->addUUID("Invoice", LLFloaterRegionInfo::getLastInvoice());
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", getCovenantID().getString().c_str());
+ gAgent.sendReliableMessage();
+ }
+}
+
+// virtual
+BOOL LLPanelEstateCovenant::sendUpdate()
+{
+ return TRUE;
+}
+
+const std::string& LLPanelEstateCovenant::getEstateName() const
+{
+ return mEstateNameText->getText();
+}
+
+void LLPanelEstateCovenant::setEstateName(const std::string& name)
+{
+ mEstateNameText->setText(name);
+}
+
+// static
+void LLPanelEstateCovenant::updateCovenantText(const std::string& string, const LLUUID& asset_id)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEditor->setText(string);
+ panelp->setCovenantID(asset_id);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateEstateName(const std::string& name)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEstateNameText->setText(name);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateLastModified(const std::string& text)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mLastModifiedText->setText(text);
+ }
+}
+
+// static
+void LLPanelEstateCovenant::updateEstateOwnerName(const std::string& name)
+{
+ LLPanelEstateCovenant* panelp = LLFloaterRegionInfo::getPanelCovenant();
+ if( panelp )
+ {
+ panelp->mEstateOwnerText->setText(name);
+ }
+}
+
+const std::string& LLPanelEstateCovenant::getOwnerName() const
+{
+ return mEstateOwnerText->getText();
+}
+
+void LLPanelEstateCovenant::setOwnerName(const std::string& name)
+{
+ mEstateOwnerText->setText(name);
+}
+
+void LLPanelEstateCovenant::setCovenantTextEditor(const std::string& text)
+{
+ mEditor->setText(text);
+}
+
+/*
+// AgentData = agent_id
+// RequestData = "setowner"
+// StringData[0] = compressed owner id
+// IntegerData[0] = serial
+bool LLDispatchSetEstateOwner::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const sparam_t& strings,
+ const iparam_t& integers)
+{
+ LLFloaterRegionInfo* floater = LLFloaterRegionInfo::getInstance();
+ if (!floater) return true;
+
+ LLTabContainer* tab = (LLTabContainer*)(floater->getChildByName("tab"));
+ if (!tab) return true;
+
+ LLPanelEstateInfo* panel = (LLPanelEstateInfo*)(tab->getChildByName("Estate"));
+ if (!panel) return true;
+
+ // TODO -- check owner, and disable floater if owner
+ // does not match
+
+ return true;
+}
+*/
+
+// key = "estateupdateinfo"
+// strings[0] = estate name
+// strings[1] = str(owner_id)
+// strings[2] = str(estate_id)
+// strings[3] = str(estate_flags)
+// strings[4] = str((S32)(sun_hour * 1024))
+// strings[5] = str(parent_estate_id)
+// strings[6] = str(covenant_id)
+// strings[7] = str(covenant_timestamp)
+// strings[8] = str(send_to_agent_only)
+bool LLDispatchEstateUpdateInfo::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return true;
+
+ // NOTE: LLDispatcher extracts strings with an extra \0 at the
+ // end. If we pass the std::string direct to the UI/renderer
+ // it draws with a weird character at the end of the string.
+ std::string estate_name = strings[0].c_str();
+ panel->setEstateName(estate_name);
+
+
+ LLViewerRegion* regionp = gAgent.getRegion();
+
+ LLUUID owner_id(strings[1].c_str());
+ regionp->setOwner(owner_id);
+ // Update estate owner name in UI
+ const BOOL is_group = FALSE;
+ gCacheName->get(owner_id, is_group, LLPanelEstateInfo::callbackCacheName);
+
+ U32 estate_id = strtoul(strings[2].c_str(), NULL, 10);
+ panel->setEstateID(estate_id);
+
+ U32 flags = strtoul(strings[3].c_str(), NULL, 10);
+ panel->setEstateFlags(flags);
+
+ F32 sun_hour = ((F32)(strtod(strings[4].c_str(), NULL)))/1024.0f;
+ if(sun_hour == 0 && (flags & REGION_FLAGS_SUN_FIXED ? FALSE : TRUE))
+ {
+ panel->setGlobalTime(TRUE);
+ }
+ else
+ {
+ panel->setGlobalTime(FALSE);
+ panel->setSunHour(sun_hour);
+ }
+
+ bool visible_from_mainland = (bool)(flags & REGION_FLAGS_EXTERNALLY_VISIBLE);
+ bool god = gAgent.isGodlike();
+ bool linden_estate = (estate_id <= ESTATE_LAST_LINDEN);
+
+ // If visible from mainland, disable the access allowed
+ // UI, as anyone can teleport there.
+ // However, gods need to be able to edit the access list for
+ // linden estates, regardless of visibility, to allow object
+ // and money transfers.
+ bool enable_agent = (!visible_from_mainland || (god && linden_estate));
+ bool enable_group = enable_agent;
+ bool enable_ban = !linden_estate;
+ panel->setAccessAllowedEnabled(enable_agent, enable_group, enable_ban);
+
+ return true;
+}
+
+
+// key = "setaccess"
+// strings[0] = str(estate_id)
+// strings[1] = str(packed_access_lists)
+// strings[2] = str(num allowed agent ids)
+// strings[3] = str(num allowed group ids)
+// strings[4] = str(num banned agent ids)
+// strings[5] = str(num estate manager agent ids)
+// strings[6] = bin(uuid)
+// strings[7] = bin(uuid)
+// strings[8] = bin(uuid)
+// ...
+bool LLDispatchSetEstateAccess::operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+{
+ LLPanelEstateInfo* panel = LLFloaterRegionInfo::getPanelEstate();
+ if (!panel) return true;
+
+ S32 index = 1; // skip estate_id
+ U32 access_flags = strtoul(strings[index++].c_str(), NULL,10);
+ S32 num_allowed_agents = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_allowed_groups = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_banned_agents = strtol(strings[index++].c_str(), NULL, 10);
+ S32 num_estate_managers = strtol(strings[index++].c_str(), NULL, 10);
+
+ // sanity ckecks
+ if (num_allowed_agents > 0
+ && !(access_flags & ESTATE_ACCESS_ALLOWED_AGENTS))
+ {
+ llwarns << "non-zero count for allowed agents, but no corresponding flag" << llendl;
+ }
+ if (num_allowed_groups > 0
+ && !(access_flags & ESTATE_ACCESS_ALLOWED_GROUPS))
+ {
+ llwarns << "non-zero count for allowed groups, but no corresponding flag" << llendl;
+ }
+ if (num_banned_agents > 0
+ && !(access_flags & ESTATE_ACCESS_BANNED_AGENTS))
+ {
+ llwarns << "non-zero count for banned agents, but no corresponding flag" << llendl;
+ }
+ if (num_estate_managers > 0
+ && !(access_flags & ESTATE_ACCESS_MANAGERS))
+ {
+ llwarns << "non-zero count for managers, but no corresponding flag" << llendl;
+ }
+
+ // grab the UUID's out of the string fields
+ if (access_flags & ESTATE_ACCESS_ALLOWED_AGENTS)
+ {
+ LLNameListCtrl* allowed_agent_name_list;
+ allowed_agent_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "allowed_avatar_name_list");
+
+ int totalAllowedAgents = num_allowed_agents;
+
+ if (allowed_agent_name_list)
+ {
+ totalAllowedAgents += allowed_agent_name_list->getItemCount();
+ }
+
+ std::string msg = llformat("Allowed residents: (%d, max %d)",
+ totalAllowedAgents,
+ ESTATE_MAX_ACCESS_IDS);
+ panel->childSetValue("allow_resident_label", LLSD(msg));
+
+ if (allowed_agent_name_list)
+ {
+ //allowed_agent_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_allowed_agents && i < ESTATE_MAX_ACCESS_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ allowed_agent_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_allowed_avatar_btn", allowed_agent_name_list->getFirstSelected() ? TRUE : FALSE);
+ allowed_agent_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_ALLOWED_GROUPS)
+ {
+ LLNameListCtrl* allowed_group_name_list;
+ allowed_group_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "allowed_group_name_list");
+
+ std::string msg = llformat("Allowed groups: (%d, max %d)",
+ num_allowed_groups,
+ (S32) ESTATE_MAX_GROUP_IDS);
+ panel->childSetValue("allow_group_label", LLSD(msg));
+
+ if (allowed_group_name_list)
+ {
+ allowed_group_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_allowed_groups && i < ESTATE_MAX_GROUP_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ allowed_group_name_list->addGroupNameItem(id);
+ }
+ panel->childSetEnabled("remove_allowed_group_btn", allowed_group_name_list->getFirstSelected() ? TRUE : FALSE);
+ allowed_group_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_BANNED_AGENTS)
+ {
+ LLNameListCtrl* banned_agent_name_list;
+ banned_agent_name_list = LLViewerUICtrlFactory::getNameListByName(panel, "banned_avatar_name_list");
+
+ int totalBannedAgents = num_banned_agents;
+
+ if (banned_agent_name_list)
+ {
+ totalBannedAgents += banned_agent_name_list->getItemCount();
+ }
+
+
+ std::string msg = llformat("Banned residents: (%d, max %d)",
+ totalBannedAgents,
+ ESTATE_MAX_ACCESS_IDS);
+ panel->childSetValue("ban_resident_label", LLSD(msg));
+
+ if (banned_agent_name_list)
+ {
+ //banned_agent_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_banned_agents && i < ESTATE_MAX_ACCESS_IDS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ banned_agent_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_banned_avatar_btn", banned_agent_name_list->getFirstSelected() ? TRUE : FALSE);
+ banned_agent_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ if (access_flags & ESTATE_ACCESS_MANAGERS)
+ {
+ std::string msg = llformat("Estate Managers: (%d, max %d)",
+ num_estate_managers,
+ ESTATE_MAX_MANAGERS);
+ panel->childSetValue("estate_manager_label", LLSD(msg));
+
+ LLNameListCtrl* estate_manager_name_list =
+ LLViewerUICtrlFactory::getNameListByName(panel, "estate_manager_name_list");
+ if (estate_manager_name_list)
+ {
+ estate_manager_name_list->deleteAllItems();
+ for (S32 i = 0; i < num_estate_managers && i < ESTATE_MAX_MANAGERS; i++)
+ {
+ LLUUID id;
+ memcpy(id.mData, strings[index++].data(), UUID_BYTES);
+ estate_manager_name_list->addNameItem(id);
+ }
+ panel->childSetEnabled("remove_estate_manager_btn", estate_manager_name_list->getFirstSelected() ? TRUE : FALSE);
+ estate_manager_name_list->sortByColumn(0, TRUE);
+ }
+ }
+
+ return true;
+}
diff --git a/indra/newview/llfloaterregioninfo.h b/indra/newview/llfloaterregioninfo.h
new file mode 100644
index 0000000000..8ab479d619
--- /dev/null
+++ b/indra/newview/llfloaterregioninfo.h
@@ -0,0 +1,387 @@
+/**
+ * @file llfloaterregioninfo.h
+ * @author Aaron Brashears
+ * @brief Declaration of the region info and controls floater and panels.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERREGIONINFO_H
+#define LL_LLFLOATERREGIONINFO_H
+
+#include <vector>
+#include "llfloater.h"
+#include "llpanel.h"
+
+class LLLineEditor;
+class LLMessageSystem;
+class LLPanelRegionInfo;
+class LLTabContainer;
+class LLViewerRegion;
+class LLViewerTextEditor;
+class LLInventoryItem;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLNameListCtrl;
+class LLSliderCtrl;
+class LLSpinCtrl;
+class LLTextBox;
+
+class LLPanelRegionGeneralInfo;
+class LLPanelRegionDebugInfo;
+class LLPanelRegionTextureInfo;
+class LLPanelRegionTerrainInfo;
+class LLPanelEstateInfo;
+class LLPanelEstateCovenant;
+
+class LLFloaterRegionInfo : public LLFloater
+{
+public:
+ ~LLFloaterRegionInfo();
+
+ static void show(LLViewerRegion* region);
+ static void show(void*);
+ static LLFloaterRegionInfo* getInstance();
+ static void processEstateOwnerRequest(LLMessageSystem* msg, void**);
+
+ // get and process region info if necessary.
+ static void processRegionInfo(LLMessageSystem* msg);
+
+ static const LLUUID& getLastInvoice() { return sRequestInvoice; }
+ static void nextInvoice() { sRequestInvoice.generate(); }
+ //static S32 getSerial() { return sRequestSerial; }
+ //static void incrementSerial() { sRequestSerial++; }
+
+ static LLPanelEstateInfo* getPanelEstate();
+ static LLPanelEstateCovenant* getPanelCovenant();
+
+protected:
+ LLFloaterRegionInfo(const LLRect& rect);
+ void refreshFromRegion(LLViewerRegion* region);
+
+ // static data
+ static LLFloaterRegionInfo* sInstance;
+
+ // member data
+ LLTabContainer* mTab;
+ typedef std::vector<LLPanelRegionInfo*> info_panels_t;
+ info_panels_t mInfoPanels;
+ //static S32 sRequestSerial; // serial # of last EstateOwnerRequest
+ static LLUUID sRequestInvoice;
+};
+
+
+// Base class for all region information panels.
+class LLPanelRegionInfo : public LLPanel
+{
+public:
+ LLPanelRegionInfo() : LLPanel("Region Info Panel") {}
+ static void onBtnSet(void* user_data);
+ static void onChangeChildCtrl(LLUICtrl* ctrl, void* user_data);
+ static void onChangeAnything(LLUICtrl* ctrl, void* user_data);
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg) { return true; }
+
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+
+ void enableButton(const char* btn_name, BOOL enable = TRUE);
+ void disableButton(const char* btn_name);
+
+protected:
+ void initCtrl(const char* name);
+ void initHelpBtn(const char* name, const char* xml_alert);
+
+ // Callback for all help buttons, data is name of XML alert to show.
+ static void onClickHelp(void* data);
+
+ // Returns TRUE if update sent and apply button should be
+ // disabled.
+ virtual BOOL sendUpdate() { return TRUE; }
+
+ typedef std::vector<std::string> strings_t;
+ //typedef std::vector<U32> integers_t;
+ void sendEstateOwnerMessage(
+ LLMessageSystem* msg,
+ const char* request,
+ const LLUUID& invoice,
+ const strings_t& strings);
+
+ // member data
+ LLHost mHost;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+// Actual panels start here
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionGeneralInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionGeneralInfo()
+ : LLPanelRegionInfo() {}
+ ~LLPanelRegionGeneralInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+ // LLPanel
+ virtual BOOL postBuild();
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickKick(void* userdata);
+ static void onKickCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onClickKickAll(void* userdata);
+ static void onKickAllCommit(S32 option, void* userdata);
+ static void onClickMessage(void* userdata);
+ static void onMessageCommit(S32 option, const LLString& text, void* userdata);
+ static void onClickManageTelehub(void* data);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionDebugInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionDebugInfo()
+ : LLPanelRegionInfo(), mTargetAvatar() {}
+ ~LLPanelRegionDebugInfo() {}
+ // LLPanel
+ virtual BOOL postBuild();
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickChooseAvatar(void*);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+ static void onClickReturnScriptedOtherLand(void*);
+ static void callbackReturnScriptedOtherLand(S32 option, void*);
+ static void onClickReturnScriptedAll(void*);
+ static void callbackReturnScriptedAll(S32 option, void*);
+ static void onClickTopColliders(void*);
+ static void onClickTopScripts(void*);
+ static void onClickRestart(void* data);
+ static void callbackRestart(S32 option, void* data);
+ static void onClickCancelRestart(void* data);
+
+private:
+ LLUUID mTargetAvatar;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionTextureInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionTextureInfo();
+ ~LLPanelRegionTextureInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+ // LLPanel && LLView
+ virtual BOOL postBuild();
+ virtual void draw();
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onClickDump(void* data);
+ BOOL validateTextureSizes();
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelRegionTerrainInfo : public LLPanelRegionInfo
+{
+public:
+ LLPanelRegionTerrainInfo()
+ : LLPanelRegionInfo() {}
+ ~LLPanelRegionTerrainInfo() {}
+ // LLPanel
+ virtual BOOL postBuild();
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+
+protected:
+ virtual BOOL sendUpdate();
+
+ static void onChangeUseEstateTime(LLUICtrl* ctrl, void* user_data);
+ static void onChangeFixedSun(LLUICtrl* ctrl, void* user_data);
+ static void onChangeSunHour(LLUICtrl* ctrl, void*);
+
+ static void onClickDownloadRaw(void*);
+ static void onClickUploadRaw(void*);
+ static void onClickBakeTerrain(void*);
+ static void callbackBakeTerrain(S32 option, void* data);
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelEstateInfo : public LLPanelRegionInfo
+{
+public:
+ static void initDispatch(LLDispatcher& dispatch);
+
+ static void onChangeFixedSun(LLUICtrl* ctrl, void* user_data);
+ static void onChangeUseGlobalTime(LLUICtrl* ctrl, void* user_data);
+
+ static void onClickAddAllowedAgent(void* user_data);
+ static void onClickRemoveAllowedAgent(void* user_data);
+ static void onClickAddAllowedGroup(void* user_data);
+ static void onClickRemoveAllowedGroup(void* user_data);
+ static void onClickAddBannedAgent(void* user_data);
+ static void onClickRemoveBannedAgent(void* user_data);
+ static void onClickAddEstateManager(void* user_data);
+ static void onClickRemoveEstateManager(void* user_data);
+ static void onClickKickUser(void* userdata);
+
+ // Group picker callback is different, can't use core methods below
+ static void addAllowedGroup(S32 option, void* data);
+ static void addAllowedGroup2(LLUUID id, void* data);
+
+ // Core methods for all above add/remove button clicks
+ static void accessAddCore(U32 operation_flag, const char* dialog_name);
+ static void accessAddCore2(S32 option, void* data);
+ static void accessAddCore3(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+ static void accessRemoveCore(U32 operation_flag, const char* dialog_name, const char* list_ctrl_name);
+ static void accessRemoveCore2(S32 option, void* data);
+
+ // used for both add and remove operations
+ static void accessCoreConfirm(S32 option, void* data);
+ static void kickUserConfirm(S32 option, void* userdata);
+
+ // Send the actual EstateOwnerRequest "estateaccessdelta" message
+ static void sendEstateAccessDelta(U32 flags, const LLUUID& agent_id);
+
+ static void onKickUserCommit(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* userdata);
+ static void onClickMessageEstate(void* data);
+ static void onMessageCommit(S32 option, const LLString& text, void* data);
+
+ LLPanelEstateInfo();
+ ~LLPanelEstateInfo() {}
+
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg);
+
+ // LLPanel
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+
+ U32 computeEstateFlags();
+ void setEstateFlags(U32 flags);
+
+ BOOL getGlobalTime();
+ void setGlobalTime(bool b);
+
+ BOOL getFixedSun();
+
+ F32 getSunHour();
+ void setSunHour(F32 sun_hour);
+
+ const std::string getEstateName() const;
+ void setEstateName(const std::string& name);
+
+ U32 getEstateID() const { return mEstateID; }
+ void setEstateID(U32 estate_id) { mEstateID = estate_id; }
+ static bool isLindenEstate();
+
+ const std::string getOwnerName() const;
+ void setOwnerName(const std::string& name);
+
+ // If visible from mainland, allowed agent and allowed groups
+ // are ignored, so must disable UI.
+ void setAccessAllowedEnabled(bool enable_agent, bool enable_group, bool enable_ban);
+
+ // this must have the same function signature as
+ // llmessage/llcachename.h:LLCacheNameCallback
+ static void callbackCacheName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*);
+
+protected:
+ virtual BOOL sendUpdate();
+ // confirmation dialog callback
+ static void callbackChangeLindenEstate(S32 opt, void* data);
+
+ void commitEstateInfo();
+ void commitEstateAccess();
+ void commitEstateManagers();
+
+ void clearAccessLists();
+ BOOL checkRemovalButton(std::string name);
+ BOOL checkSunHourSlider(LLUICtrl* child_ctrl);
+
+ U32 mEstateID;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class LLPanelEstateCovenant : public LLPanelRegionInfo
+{
+public:
+ LLPanelEstateCovenant();
+ ~LLPanelEstateCovenant() {}
+
+ // LLPanel
+ virtual BOOL postBuild();
+ virtual void updateChild(LLUICtrl* child_ctrl);
+ virtual bool refreshFromRegion(LLViewerRegion* region);
+ virtual bool estateUpdate(LLMessageSystem* msg);
+
+ // LLView overrides
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept,
+ LLString& tooltip_msg);
+ static void confirmChangeCovenantCallback(S32 option, void* userdata);
+ static void resetCovenantID(void* userdata);
+ static void confirmResetCovenantCallback(S32 option, void* userdata);
+ void sendChangeCovenantID(const LLUUID &asset_id);
+ void loadInvItem(LLInventoryItem *itemp);
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ // Accessor functions
+ static void updateCovenantText(const std::string& string, const LLUUID& asset_id);
+ static void updateEstateName(const std::string& name);
+ static void updateLastModified(const std::string& text);
+ static void updateEstateOwnerName(const std::string& name);
+
+ const LLUUID& getCovenantID() const { return mCovenantID; }
+ void setCovenantID(const LLUUID& id) { mCovenantID = id; }
+ const std::string& getEstateName() const;
+ void setEstateName(const std::string& name);
+ const std::string& getOwnerName() const;
+ void setOwnerName(const std::string& name);
+ void setCovenantTextEditor(const std::string& text);
+
+ typedef enum e_asset_status
+ {
+ ASSET_ERROR,
+ ASSET_UNLOADED,
+ ASSET_LOADING,
+ ASSET_LOADED
+ } EAssetStatus;
+
+protected:
+ virtual BOOL sendUpdate();
+ LLTextBox* mEstateNameText;
+ LLTextBox* mEstateOwnerText;
+ LLTextBox* mLastModifiedText;
+ // CovenantID from sim
+ LLUUID mCovenantID;
+ LLViewerTextEditor* mEditor;
+ EAssetStatus mAssetStatus;
+};
+
+#endif
diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp
new file mode 100644
index 0000000000..3b76aa038b
--- /dev/null
+++ b/indra/newview/llfloaterreporter.cpp
@@ -0,0 +1,819 @@
+/**
+ * @file llfloaterreporter.cpp
+ * @brief Bug and abuse reports.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <sstream>
+
+// self include
+#include "llfloaterreporter.h"
+
+// linden library includes
+#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llfontgl.h"
+#include "llgl.h" // for renderer
+#include "llinventory.h"
+#include "llstring.h"
+#include "llsys.h"
+#include "llversion.h"
+#include "message.h"
+#include "v3math.h"
+
+// viewer project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "lltexturectrl.h"
+#include "llscrolllistctrl.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llcombobox.h"
+#include "llfloaterrate.h"
+#include "lltooldraganddrop.h"
+#include "llfloatermap.h"
+#include "lluiconstants.h"
+#include "llcallingcard.h"
+#include "llviewerobjectlist.h"
+#include "llagent.h"
+#include "lltoolobjpicker.h"
+#include "lltoolmgr.h"
+#include "llviewermenu.h" // for LLResourceData
+#include "llviewerwindow.h"
+#include "llviewerimagelist.h"
+#include "llfilepicker.h"
+#include "llfloateravatarpicker.h"
+#include "lldir.h"
+#include "llselectmgr.h"
+#include "llviewerbuild.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+
+
+const U32 INCLUDE_SCREENSHOT = 0x01 << 0;
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+
+// this map keeps track of current reporter instances
+// there can only be one instance of each reporter type
+LLMap< EReportType, LLFloaterReporter* > gReporterInstances;
+
+//-----------------------------------------------------------------------------
+// Member functions
+//-----------------------------------------------------------------------------
+LLFloaterReporter::LLFloaterReporter(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ EReportType report_type)
+ :
+ LLFloater(name, rect, title),
+ mReportType(report_type),
+ mObjectID( ),
+ mScreenID(),
+ mDeselectOnClose( FALSE ),
+ mPicking( FALSE),
+ mPosition(),
+ mCopyrightWarningSeen( FALSE )
+{
+ if (report_type == BUG_REPORT)
+ {
+ gUICtrlFactory->buildFloater(this, "floater_report_bug.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(this, "floater_report_abuse.xml");
+ }
+
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ childSetText("sim_field", regionp->getName() );
+ }
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(this, "pick_btn");
+ if (pick_btn)
+ {
+ // XUI: Why aren't these in viewerart.ini?
+ pick_btn->setImages( "UIImgFaceUUID",
+ "UIImgFaceSelectedUUID" );
+ childSetAction("pick_btn", onClickObjPicker, this);
+ }
+
+ if (report_type != BUG_REPORT)
+ {
+ LLLineEditor* le = (LLLineEditor*)getCtrlByNameAndType("abuser_name_edit", WIDGET_TYPE_LINE_EDITOR);
+ le->setEnabled( FALSE );
+ }
+
+ childSetAction("select_abuser", onClickSelectAbuser, this);
+
+ childSetAction("send_btn", onClickSend, this);
+ childSetAction("cancel_btn", onClickCancel, this);
+
+ enableControls(TRUE);
+
+ // convert the position to a string
+ LLVector3d pos = gAgent.getPositionGlobal();
+ if (regionp)
+ {
+ pos -= regionp->getOriginGlobal();
+ }
+ setPosBox(pos);
+
+ gReporterInstances.addData(report_type, this);
+
+ // Upload a screenshot, but don't draw this floater.
+ setVisible(FALSE);
+ uploadScreenshot();
+ setVisible(TRUE);
+
+ // Default text to be blank
+ childSetText("object_name", "");
+ childSetText("owner_name", "");
+
+ childSetFocus("summary_edit");
+
+ mDefaultSummary = childGetText("details_edit");
+}
+
+
+// virtual
+LLFloaterReporter::~LLFloaterReporter()
+{
+ gReporterInstances.removeData(mReportType);
+ // child views automatically deleted
+ mObjectID = LLUUID::null;
+
+ if (mPicking)
+ {
+ closePickTool(this);
+ }
+
+ mPosition.setVec(0.0f, 0.0f, 0.0f);
+
+ std::for_each(mMCDList.begin(), mMCDList.end(), DeletePointer() );
+ mMCDList.clear();
+
+ if (gSelectMgr)
+ {
+ gSelectMgr->deselectTransient();
+ }
+}
+
+
+void LLFloaterReporter::enableControls(BOOL enable)
+{
+ childSetEnabled("category_combo", enable);
+ // bug reports never include the chat history
+ if (mReportType != BUG_REPORT)
+ {
+ childSetEnabled("chat_check", enable);
+ }
+ childSetEnabled("screen_check", enable);
+ childDisable("screenshot");
+ childSetEnabled("pick_btn", enable);
+ childSetEnabled("summary_edit", enable);
+ childSetEnabled("details_edit", enable);
+ childSetEnabled("send_btn", enable);
+ childSetEnabled("cancel_btn", enable);
+}
+
+
+void LLFloaterReporter::getObjectInfo(const LLUUID& object_id)
+{
+ // TODO --
+ // 1 need to send to correct simulator if object is not
+ // in same simulator as agent
+ // 2 display info in widget window that gives feedback that
+ // we have recorded the object info
+ // 3 can pick avatar ==> might want to indicate when a picked
+ // object is an avatar, attachment, or other category
+
+ mObjectID = object_id;
+
+ if (LLUUID::null != mObjectID)
+ {
+ // get object info for the user's benefit
+ LLViewerObject* objectp = NULL;
+ objectp = gObjectList.findObject( mObjectID );
+ if (objectp)
+ {
+ if ( objectp->isAttachment() )
+ {
+ objectp = (LLViewerObject*)objectp->getRoot();
+ }
+
+ // correct the region and position information
+ LLViewerRegion *regionp = objectp->getRegion();
+ if (regionp)
+ {
+ childSetText("sim_field", regionp->getName());
+ LLVector3d global_pos;
+ global_pos.setVec(objectp->getPositionRegion());
+ setPosBox(global_pos);
+ }
+
+ if (objectp->isAvatar())
+ {
+ // we have the information we need
+ LLString object_owner;
+
+ LLNameValue* firstname = objectp->getNVPair("FirstName");
+ LLNameValue* lastname = objectp->getNVPair("LastName");
+ if (firstname && lastname)
+ {
+ object_owner.append(firstname->getString());
+ object_owner.append(1, ' ');
+ object_owner.append(lastname->getString());
+ }
+ else
+ {
+ object_owner.append("Unknown");
+ }
+ childSetText("object_name", object_owner);
+ childSetText("owner_name", object_owner);
+ }
+ else
+ {
+ // we have to query the simulator for information
+ // about this object
+ LLMessageSystem* msg = gMessageSystem;
+ U32 request_flags = (mReportType == BUG_REPORT) ? BUG_REPORT_REQUEST : COMPLAINT_REPORT_REQUEST;
+ msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_RequestFlags, request_flags );
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ LLViewerRegion* regionp = objectp->getRegion();
+ msg->sendReliable( regionp->getHost() );
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterReporter::onClickSelectAbuser(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ gFloaterView->getParentFloater(self)->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarID, userdata, FALSE, TRUE ));
+}
+
+void LLFloaterReporter::callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLFloaterReporter* self = (LLFloaterReporter*) data;
+
+ if (ids.empty() || names.empty()) return;
+
+ // this should never be called in a bug report but here for safety.
+ if ( self->mReportType != BUG_REPORT )
+ {
+ self->childSetText("abuser_name_edit", names[0] );
+
+ self->refresh();
+ };
+}
+
+// static
+void LLFloaterReporter::onClickSend(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ // only do this for abuse reports
+ if ( self->mReportType != BUG_REPORT )
+ {
+ if ( ! self->mCopyrightWarningSeen )
+ {
+ LLString details_lc = self->childGetText("details_edit");
+ LLString::toLower( details_lc );
+ LLString summary_lc = self->childGetText("summary_edit");
+ LLString::toLower( summary_lc );
+ if ( details_lc.find( "copyright" ) != std::string::npos ||
+ summary_lc.find( "copyright" ) != std::string::npos )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseContainsCopyright");
+ self->mCopyrightWarningSeen = TRUE;
+ return;
+ };
+ };
+ };
+
+ if (self->mPicking)
+ {
+ closePickTool(self);
+ }
+ self->sendReport();
+}
+
+
+// static
+void LLFloaterReporter::onClickCancel(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ // reset flag in case the next report also contains this text
+ self->mCopyrightWarningSeen = FALSE;
+
+ if (self->mPicking)
+ {
+ closePickTool(self);
+ }
+ self->close();
+}
+
+
+// static
+void LLFloaterReporter::onClickObjPicker(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+ gToolObjPicker->setExitCallback(LLFloaterReporter::closePickTool, self);
+ gToolMgr->setTransientTool(gToolObjPicker);
+ self->mPicking = TRUE;
+ self->childSetText("object_name", "");
+ self->childSetText("owner_name", "");
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
+ if (pick_btn) pick_btn->setToggleState(TRUE);
+}
+
+
+// static
+void LLFloaterReporter::closePickTool(void *userdata)
+{
+ LLFloaterReporter *self = (LLFloaterReporter *)userdata;
+
+ LLUUID object_id = gToolObjPicker->getObjectID();
+ self->getObjectInfo(object_id);
+
+ gToolMgr->clearTransientTool();
+ self->mPicking = FALSE;
+ LLButton* pick_btn = LLUICtrlFactory::getButtonByName(self, "pick_btn");
+ if (pick_btn) pick_btn->setToggleState(FALSE);
+}
+
+
+// static
+void LLFloaterReporter::showFromMenu(EReportType report_type)
+{
+ if (gReporterInstances.checkData(report_type))
+ {
+ // ...bring that window to front
+ LLFloaterReporter *f = gReporterInstances.getData(report_type);
+ f->open();
+ }
+ else
+ {
+ LLFloaterReporter *f;
+ if (BUG_REPORT == report_type)
+ {
+ f = LLFloaterReporter::createNewBugReporter();
+ }
+ else if (COMPLAINT_REPORT == report_type)
+ {
+ f = LLFloaterReporter::createNewAbuseReporter();
+ }
+ else
+ {
+ llwarns << "Unknown LLViewerReporter type : " << report_type << llendl;
+ return;
+ }
+
+ f->center();
+
+ if (report_type == BUG_REPORT)
+ {
+ gViewerWindow->alertXml("HelpReportBug");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportAbuse");
+ }
+
+ // grab the user's name
+ LLString fullname;
+ gAgent.buildFullname(fullname);
+ f->childSetText("reporter_field", fullname);
+ }
+}
+
+
+// static
+void LLFloaterReporter::showFromObject(const LLUUID& object_id)
+{
+ LLFloaterReporter* f = createNewAbuseReporter();
+ f->center();
+ f->setFocus(TRUE);
+
+ // grab the user's name
+ LLString fullname;
+ gAgent.buildFullname(fullname);
+ f->childSetText("reporter_field", fullname);
+
+ // Request info for this object
+ f->getObjectInfo(object_id);
+
+ // Need to deselect on close
+ f->mDeselectOnClose = TRUE;
+
+ f->open();
+}
+
+
+// static
+LLFloaterReporter* LLFloaterReporter::getReporter(EReportType report_type)
+{
+ LLFloaterReporter *self = NULL;
+ if (gReporterInstances.checkData(report_type))
+ {
+ // ...bring that window to front
+ self = gReporterInstances.getData(report_type);
+ }
+ return self;
+}
+
+LLFloaterReporter* LLFloaterReporter::createNewAbuseReporter()
+{
+ return new LLFloaterReporter("complaint_reporter",
+ LLRect(),
+ "Report Abuse",
+ COMPLAINT_REPORT);
+}
+
+//static
+LLFloaterReporter* LLFloaterReporter::createNewBugReporter()
+{
+ return new LLFloaterReporter("bug_reporter",
+ LLRect(),
+ "Report Bug",
+ BUG_REPORT);
+}
+
+
+
+void LLFloaterReporter::setPickedObjectProperties(const char *object_name, const char *owner_name)
+{
+ childSetText("object_name", object_name);
+ childSetText("owner_name", owner_name);
+}
+
+void LLFloaterReporter::sendReport()
+{
+ LLViewerRegion *regionp = gAgent.getRegion();
+ if (!regionp) return;
+ // Ensure user selected a category from the list
+ LLSD category_sd = childGetValue("category_combo");
+ U8 category = (U8)category_sd.asInteger();
+ if (category == 0)
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseSelectCategory");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugSelectCategory");
+ }
+ return;
+ }
+
+ if ( mReportType != BUG_REPORT )
+ {
+ if ( childGetText("abuser_name_edit").empty() )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseAbuserNameEmpty");
+ return;
+ };
+
+ if ( childGetText("abuse_location_edit").empty() )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseAbuserLocationEmpty");
+ return;
+ };
+ };
+
+ if ( childGetText("summary_edit").empty() )
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseSummaryEmpty");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugSummaryEmpty");
+ }
+ return;
+ };
+
+ if ( childGetText("details_edit") == mDefaultSummary )
+ {
+ if ( mReportType != BUG_REPORT )
+ {
+ gViewerWindow->alertXml("HelpReportAbuseDetailsEmpty");
+ }
+ else
+ {
+ gViewerWindow->alertXml("HelpReportBugDetailsEmpty");
+ }
+ return;
+ };
+
+ // reset flag in case the next report also contains this text
+ mCopyrightWarningSeen = FALSE;
+
+ U32 check_flags = 0;
+ if (childGetValue("screen_check"))
+ {
+ check_flags |= INCLUDE_SCREENSHOT;
+ }
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UserReport);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ReportData);
+ msg->addU8Fast(_PREHASH_ReportType, (U8) mReportType);
+ msg->addU8(_PREHASH_Category, category);
+ msg->addVector3Fast(_PREHASH_Position, mPosition);
+ msg->addU8Fast(_PREHASH_CheckFlags, (U8) check_flags);
+ LLSD screenshot_id = childGetValue("screenshot");
+ msg->addUUIDFast(_PREHASH_ScreenshotID, screenshot_id);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+
+ std::ostringstream summary;
+ if (!gInProductionGrid)
+ {
+ summary << "Preview ";
+ }
+
+ LLString category_name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "category_combo");
+ if (combo)
+ {
+ category_name = combo->getSimpleSelectedItem(); // want label, not value
+ }
+
+#if LL_WINDOWS
+ const char* platform = "Win";
+ const char* short_platform = "O:W";
+#elif LL_DARWIN
+ const char* platform = "Mac";
+ const char* short_platform = "O:M";
+#elif LL_LINUX
+ const char* platform = "Lnx";
+ const char* short_platform = "O:L";
+#else
+ const char* platform = "???";
+ const char* short_platform = "O:?";
+#endif
+
+
+ if ( mReportType == BUG_REPORT)
+ {
+ summary << short_platform << " V" << LL_VERSION_MAJOR << "."
+ << LL_VERSION_MINOR << "."
+ << LL_VERSION_PATCH << "."
+ << LL_VIEWER_BUILD
+ << " (" << regionp->getName() << ")"
+ << "[" << category_name << "] "
+ << "\"" << childGetValue("summary_edit").asString() << "\"";
+ }
+ else
+ {
+ summary << ""
+ << " |" << regionp->getName() << "|" // region reporter is currently in.
+ << " (" << childGetText("abuse_location_edit") << ")" // region abuse occured in (freeform text - no LLRegionPicker tool)
+ << " [" << category_name << "] " // updated category
+ << " {" << childGetText("abuser_name_edit") << "} " // name of abuse entered in report (chosen using LLAvatarPicker)
+ << " \"" << childGetValue("summary_edit").asString() << "\""; // summary as entered
+ };
+ msg->addStringFast(_PREHASH_Summary, summary.str().c_str());
+
+ std::ostringstream details;
+ if (mReportType != BUG_REPORT)
+ {
+ details << "V" << LL_VERSION_MAJOR << "." // client version moved to body of email for abuse reports
+ << LL_VERSION_MINOR << "."
+ << LL_VERSION_PATCH << "."
+ << LL_VIEWER_BUILD << std::endl << std::endl;
+ }
+ LLString object_name = childGetText("object_name");
+ LLString owner_name = childGetText("owner_name");
+ if (!object_name.empty() && !owner_name.empty())
+ {
+ details << "Object: " << object_name << "\n";
+ details << "Owner: " << owner_name << "\n";
+ }
+
+ if ( mReportType != BUG_REPORT )
+ {
+ details << "Abuser name: " << childGetText("abuser_name_edit") << " \n";
+ details << " Abuser location: " << childGetText("abuse_location_edit") << " \n";
+ };
+
+ details << childGetValue("details_edit").asString();
+ msg->addStringFast(_PREHASH_Details, details.str() );
+
+ char version_string[MAX_STRING];
+ sprintf(version_string,
+ "%d.%d.%d %s %s %s %s",
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ platform,
+ gSysCPU.getFamily().c_str(),
+ gGLManager.mGLRenderer.c_str(),
+ gGLManager.mDriverVersionVendorString.c_str());
+ msg->addString("VersionString", version_string);
+
+ std::list<LLMeanCollisionData*>::iterator it;
+ for (it = mMCDList.begin(); it != mMCDList.end(); ++it)
+ {
+ LLMeanCollisionData *mcd = *it;
+ msg->nextBlockFast(_PREHASH_MeanCollision);
+ msg->addUUIDFast(_PREHASH_Perp, mcd->mPerp);
+ msg->addU32Fast(_PREHASH_Time, mcd->mTime);
+ msg->addF32Fast(_PREHASH_Mag, mcd->mMag);
+ msg->addU8Fast(_PREHASH_Type, mcd->mType);
+ }
+ msg->sendReliable(regionp->getHost());
+
+ close();
+}
+
+
+void LLFloaterReporter::uploadScreenshot()
+{
+ const S32 IMAGE_WIDTH = 1024;
+ const S32 IMAGE_HEIGHT = 768;
+ LLString filename("report_screenshot.bmp");
+
+ if( !gViewerWindow->saveSnapshot( filename, IMAGE_WIDTH, IMAGE_HEIGHT, TRUE, FALSE ) )
+ {
+ return;
+ }
+
+ // Generate the temporary filename
+ std::string temp_filename = gDirUtilp->getTempFilename();
+
+ // try to create the upload file
+ if (!LLViewerImageList::createUploadFile(filename,
+ temp_filename,
+ IMG_CODEC_BMP ))
+ {
+ llwarns << "Unable to upload report screenshot " << filename << ":\n\n" << LLImageBase::getLastError() << "\n" << llendl;
+ if(LLFile::remove(temp_filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+ }
+ else
+ {
+ // create a resource data
+ LLResourceData* data = NULL;
+ data = new LLResourceData;
+ data->mInventoryType = LLInventoryType::IT_NONE;
+ data->mAssetInfo.mTransactionID.generate();
+ data->mAssetInfo.mUuid = data->mAssetInfo.mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+ if (BUG_REPORT == mReportType)
+ {
+ //data->mAssetInfo.mType = LLAssetType::AT_BUG_REPORT_SCREENSHOT;
+ data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
+ data->mPreferredLocation = LLAssetType::EType(-1);
+ }
+ else if (COMPLAINT_REPORT == mReportType)
+ {
+ //data->mAssetInfo.mType = LLAssetType::AT_COMPLAINT_REPORT_SCREENSHOT;
+ data->mAssetInfo.mType = LLAssetType::AT_TEXTURE;
+ data->mPreferredLocation = LLAssetType::EType(-2);
+ }
+ else
+ {
+ llwarns << "Unknown LLFloaterReporter type" << llendl;
+ }
+ data->mAssetInfo.mCreatorID = gAgentID;
+ data->mAssetInfo.setName("screenshot_name");
+ data->mAssetInfo.setDescription("screenshot_descr");
+
+ llinfos << "*** Uploading: " << llendl;
+ llinfos << "Type: " << LLAssetType::lookup(data->mAssetInfo.mType) << llendl;
+ llinfos << "File: " << filename << llendl;
+ llinfos << "Dest: " << temp_filename << llendl;
+ llinfos << "Name: " << data->mAssetInfo.getName() << llendl;
+ llinfos << "Desc: " << data->mAssetInfo.getDescription() << llendl;
+
+ gAssetStorage->storeAssetData(temp_filename.c_str(),
+ data->mAssetInfo.mTransactionID,
+ data->mAssetInfo.mType,
+ LLFloaterReporter::uploadDoneCallback,
+ (void*)data, TRUE);
+ }
+}
+
+
+// static
+void LLFloaterReporter::uploadDoneCallback(const LLUUID &uuid, void *user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLResourceData* data = (LLResourceData*)user_data;
+
+ if(result >= 0)
+ {
+ EReportType report_type = UNKNOWN_REPORT;
+ if (data->mPreferredLocation == -1)
+ {
+ report_type = BUG_REPORT;
+ }
+ else if (data->mPreferredLocation == -2)
+ {
+ report_type = COMPLAINT_REPORT;
+ }
+ else
+ {
+ llwarns << "Unknown report type : " << data->mPreferredLocation << llendl;
+ }
+
+ LLFloaterReporter *self = getReporter(report_type);
+ if (self)
+ {
+ LLTexturePicker* texture = LLUICtrlFactory::getTexturePickerByName(self, "screenshot");
+ if (texture)
+ {
+ texture->setImageAssetID(uuid);
+ texture->setDefaultImageAssetID(uuid);
+ texture->setCaption("Screenshot");
+ }
+ self->mScreenID = uuid;
+ llinfos << "Got screen shot " << uuid << llendl;
+ }
+ }
+ else // if(result >= 0)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("ErrorUploadingReportScreenshot", args);
+
+ std::string err_msg("There was a problem uploading a report screenshot");
+ err_msg += " due to the following reason: " + args["[REASON]"];
+ llwarns << err_msg << llendl;
+ }
+ delete data;
+}
+
+
+void LLFloaterReporter::setPosBox(const LLVector3d &pos)
+{
+ mPosition.setVec(pos);
+ LLString pos_string = llformat("{%.1f, %.1f, %.1f}",
+ mPosition.mV[VX],
+ mPosition.mV[VY],
+ mPosition.mV[VZ]);
+ childSetText("pos_field", pos_string);
+}
+
+void LLFloaterReporter::setDescription(const LLString& description, LLMeanCollisionData *mcd)
+{
+ LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
+ if (self)
+ {
+ self->childSetText("details_edit", description);
+
+ for_each(self->mMCDList.begin(), self->mMCDList.end(), DeletePointer());
+ self->mMCDList.clear();
+ if (mcd)
+ {
+ self->mMCDList.push_back(new LLMeanCollisionData(mcd));
+ }
+ }
+}
+
+void LLFloaterReporter::addDescription(const LLString& description, LLMeanCollisionData *mcd)
+{
+ LLFloaterReporter *self = gReporterInstances[COMPLAINT_REPORT];
+ if (self)
+ {
+ LLTextEditor* text = LLUICtrlFactory::getTextEditorByName(self, "details_edit");
+ if (text)
+ {
+ text->insertText(description);
+ }
+ if (mcd)
+ {
+ self->mMCDList.push_back(new LLMeanCollisionData(mcd));
+ }
+ }
+}
diff --git a/indra/newview/llfloaterreporter.h b/indra/newview/llfloaterreporter.h
new file mode 100644
index 0000000000..a23424f9b4
--- /dev/null
+++ b/indra/newview/llfloaterreporter.h
@@ -0,0 +1,107 @@
+/**
+ * @file llfloaterreporter.h
+ * @author Andrew Meadows
+ * @brief Bug and abuse reports.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERREPORTER_H
+#define LL_LLFLOATERREPORTER_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "v3math.h"
+
+class LLMessageSystem;
+class LLViewerImage;
+class LLInventoryItem;
+class LLViewerObject;
+class LLAgent;
+class LLToolObjPicker;
+class LLMeanCollisionData;
+
+// these flags are used to label info requests to the server
+const U32 BUG_REPORT_REQUEST = 0x01 << 0;
+const U32 COMPLAINT_REPORT_REQUEST = 0x01 << 1;
+
+// ************************************************************
+// THESE ENUMS ARE IN THE DATABASE!!!
+//
+// The process for adding a new report type is to:
+// 1. Issue a command to the database to insert the new value:
+// insert into user_report_type (description)
+// values ('${new type name}');
+// 2. Record the integer value assigned:
+// select type from user_report_type
+// where description='${new type name}';
+// 3. Add it here.
+// ${NEW TYPE NAME}_REPORT = ${type_number};
+//
+// Failure to follow this process WILL result in incorrect
+// queries on user reports.
+// ************************************************************
+enum EReportType
+{
+ NULL_REPORT = 0, // don't use this value anywhere
+ UNKNOWN_REPORT = 1,
+ BUG_REPORT = 2,
+ COMPLAINT_REPORT = 3,
+ CS_REQUEST_REPORT = 4
+};
+
+
+class LLFloaterReporter
+: public LLFloater
+{
+public:
+ LLFloaterReporter(const std::string& name,
+ const LLRect &rect,
+ const std::string& title,
+ EReportType = UNKNOWN_REPORT);
+ /*virtual*/ ~LLFloaterReporter();
+
+ // Enables all buttons
+ static void showFromMenu(EReportType report_type);
+
+ static void showFromObject(const LLUUID& object_id);
+
+ static void onClickSend (void *userdata);
+ static void onClickCancel (void *userdata);
+ static void onClickObjPicker (void *userdata);
+ static void onClickSelectAbuser (void *userdata);
+ static void closePickTool (void *userdata);
+ static void uploadDoneCallback(const LLUUID &uuid, void* user_data, S32 result);
+ static void addDescription(const LLString& description, LLMeanCollisionData *mcd = NULL);
+ static void setDescription(const LLString& description, LLMeanCollisionData *mcd = NULL);
+
+ // returns a pointer to reporter of report_type
+ static LLFloaterReporter* getReporter(EReportType report_type);
+ static LLFloaterReporter* createNewAbuseReporter();
+ static LLFloaterReporter* createNewBugReporter();
+
+ void setPickedObjectProperties(const char *object_name, const char *owner_name);
+ void uploadScreenshot();
+
+private:
+ void setReporterID();
+ void sendReport();
+ void setPosBox(const LLVector3d &pos);
+ void enableControls(BOOL own_avatar);
+ void getObjectInfo(const LLUUID& object_id);
+ static void callbackAvatarID(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+private:
+ EReportType mReportType;
+ LLUUID mObjectID;
+ LLUUID mScreenID;
+ BOOL mDeselectOnClose;
+ BOOL mPicking;
+ LLVector3 mPosition;
+ BOOL mCopyrightWarningSeen;
+ std::list<LLMeanCollisionData*> mMCDList;
+ LLString mDefaultSummary;
+};
+
+#endif
diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp
new file mode 100644
index 0000000000..d80dbca310
--- /dev/null
+++ b/indra/newview/llfloaterscriptdebug.cpp
@@ -0,0 +1,230 @@
+/**
+ * @file llfloaterscriptdebug.cpp
+ * @brief Chat window for showing script errors and warnings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llfloaterscriptdebug.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+// project include
+#include "llviewertexteditor.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+#include "llviewerimagelist.h"
+
+//
+// Statics
+//
+LLFloaterScriptDebug* LLFloaterScriptDebug::sInstance = NULL;
+
+//
+// Member Functions
+//
+LLFloaterScriptDebug::LLFloaterScriptDebug()
+: LLMultiFloater()
+{
+
+}
+
+LLFloaterScriptDebug::~LLFloaterScriptDebug()
+{
+ sInstance = NULL;
+}
+
+void LLFloaterScriptDebug::show(const LLUUID& object_id)
+{
+ LLFloater* floaterp = addOutputWindow(object_id);
+ if (sInstance)
+ {
+ sInstance->open();
+ sInstance->showFloater(floaterp);
+ }
+}
+
+BOOL LLFloaterScriptDebug::postBuild()
+{
+ LLMultiFloater::postBuild();
+
+ if (mTabContainer)
+ {
+ // FIXME: apparantly fails for tab containers?
+// mTabContainer->requires("all_scripts", WIDGET_TYPE_FLOATER);
+// mTabContainer->checkRequirements();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void* getOutputWindow(void* data)
+{
+ return new LLFloaterScriptDebugOutput();
+}
+
+LLFloater* LLFloaterScriptDebug::addOutputWindow(const LLUUID &object_id)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterScriptDebug();
+ LLCallbackMap::map_t factory_map;
+ factory_map["all_scripts"] = LLCallbackMap(getOutputWindow, NULL);
+ gUICtrlFactory->buildFloater(sInstance, "floater_script_debug.xml", &factory_map);
+ sInstance->setVisible(FALSE);
+ }
+
+ LLFloater* floaterp = NULL;
+ LLFloater::setFloaterHost(sInstance);
+ {
+ floaterp = LLFloaterScriptDebugOutput::show(object_id);
+ }
+ LLFloater::setFloaterHost(NULL);
+
+ return floaterp;
+}
+
+void LLFloaterScriptDebug::addScriptLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color, const LLUUID& source_id)
+{
+ LLViewerObject* objectp = gObjectList.findObject(source_id);
+ LLString floater_label;
+
+ if (objectp)
+ {
+ objectp->setIcon(gImageList.getImage(LLUUID(gViewerArt.getString("script_error.tga"))));
+ floater_label = llformat("%s(%.2f, %.2f)", user_name.c_str(), objectp->getPositionRegion().mV[VX], objectp->getPositionRegion().mV[VY]);
+ }
+ else
+ {
+ floater_label = user_name;
+ }
+
+ addOutputWindow(LLUUID::null);
+ addOutputWindow(source_id);
+
+ // add to "All" floater
+ LLFloaterScriptDebugOutput* floaterp = LLFloaterScriptDebugOutput::getFloaterByID(LLUUID::null);
+ floaterp->addLine(utf8mesg, user_name, color);
+
+ // add to specific script instance floater
+ floaterp = LLFloaterScriptDebugOutput::getFloaterByID(source_id);
+ floaterp->addLine(utf8mesg, floater_label, color);
+}
+
+//
+// LLFloaterScriptDebugOutput
+//
+
+std::map<LLUUID, LLFloaterScriptDebugOutput*> LLFloaterScriptDebugOutput::sInstanceMap;
+
+LLFloaterScriptDebugOutput::LLFloaterScriptDebugOutput()
+: mObjectID(LLUUID::null)
+{
+ sInstanceMap[mObjectID] = this;
+}
+
+LLFloaterScriptDebugOutput::LLFloaterScriptDebugOutput(const LLUUID& object_id)
+: LLFloater("script instance floater", LLRect(0, 200, 200, 0), "Script", TRUE), mObjectID(object_id)
+{
+ S32 y = mRect.getHeight() - LLFLOATER_HEADER_SIZE - LLFLOATER_VPAD;
+ S32 x = LLFLOATER_HPAD;
+ // History editor
+ // Give it a border on the top
+ LLRect history_editor_rect(
+ x,
+ y,
+ mRect.getWidth() - LLFLOATER_HPAD,
+ LLFLOATER_VPAD );
+ mHistoryEditor = new LLViewerTextEditor( "Chat History Editor",
+ history_editor_rect, S32_MAX, "", LLFontGL::sSansSerif);
+ mHistoryEditor->setWordWrap( TRUE );
+ mHistoryEditor->setFollowsAll();
+ mHistoryEditor->setEnabled( FALSE );
+ mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history.
+ addChild(mHistoryEditor);
+}
+
+void LLFloaterScriptDebugOutput::init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn)
+{
+ LLFloater::init(title, resizable, min_width, min_height, drag_on_left, minimizable, close_btn);
+ S32 y = mRect.getHeight() - LLFLOATER_HEADER_SIZE - LLFLOATER_VPAD;
+ S32 x = LLFLOATER_HPAD;
+ // History editor
+ // Give it a border on the top
+ LLRect history_editor_rect(
+ x,
+ y,
+ mRect.getWidth() - LLFLOATER_HPAD,
+ LLFLOATER_VPAD );
+ mHistoryEditor = new LLViewerTextEditor( "Chat History Editor",
+ history_editor_rect, S32_MAX, "", LLFontGL::sSansSerif);
+ mHistoryEditor->setWordWrap( TRUE );
+ mHistoryEditor->setFollowsAll();
+ mHistoryEditor->setEnabled( FALSE );
+ mHistoryEditor->setTakesFocus( TRUE ); // We want to be able to cut or copy from the history.
+ addChild(mHistoryEditor);
+}
+
+LLFloaterScriptDebugOutput::~LLFloaterScriptDebugOutput()
+{
+ sInstanceMap.erase(mObjectID);
+}
+
+void LLFloaterScriptDebugOutput::addLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color)
+{
+ if (mObjectID.isNull())
+ {
+ //setTitle("[All scripts]");
+ setCanTearOff(FALSE);
+ setCanClose(FALSE);
+ }
+ else
+ {
+ setTitle(user_name);
+ }
+
+ mHistoryEditor->appendColoredText(utf8mesg, false, true, color);
+}
+
+//static
+LLFloaterScriptDebugOutput* LLFloaterScriptDebugOutput::show(const LLUUID& object_id)
+{
+ LLFloaterScriptDebugOutput* floaterp = NULL;
+ instance_map_t::iterator found_it = sInstanceMap.find(object_id);
+ if (found_it == sInstanceMap.end())
+ {
+ floaterp = new LLFloaterScriptDebugOutput(object_id);
+ sInstanceMap[object_id] = floaterp;
+ floaterp->open();
+ }
+ else
+ {
+ floaterp = found_it->second;
+ }
+
+ return floaterp;
+}
+
+//static
+LLFloaterScriptDebugOutput* LLFloaterScriptDebugOutput::getFloaterByID(const LLUUID& object_id)
+{
+ LLFloaterScriptDebugOutput* floaterp = NULL;
+ instance_map_t::iterator found_it = sInstanceMap.find(object_id);
+ if (found_it != sInstanceMap.end())
+ {
+ floaterp = found_it->second;
+ }
+
+ return floaterp;
+}
diff --git a/indra/newview/llfloaterscriptdebug.h b/indra/newview/llfloaterscriptdebug.h
new file mode 100644
index 0000000000..49a6a5c1a2
--- /dev/null
+++ b/indra/newview/llfloaterscriptdebug.h
@@ -0,0 +1,60 @@
+/**
+ * @file llfloaterscriptdebug.h
+ * @brief Shows error and warning output from scripts
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSCRIPTDEBUG_H
+#define LL_LLFLOATERSCRIPTDEBUG_H
+
+#include "llfloater.h"
+
+class LLTextEditor;
+class LLUUID;
+
+class LLFloaterScriptDebug : public LLMultiFloater
+{
+public:
+ virtual ~LLFloaterScriptDebug();
+ virtual void onClose(bool app_quitting) { setVisible(FALSE); }
+ virtual BOOL postBuild();
+ static void show(const LLUUID& object_id);
+ static void addScriptLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color, const LLUUID& source_id);
+
+protected:
+ LLFloaterScriptDebug();
+
+ static LLFloater* addOutputWindow(const LLUUID& object_id);
+
+protected:
+ static LLFloaterScriptDebug* sInstance;
+};
+
+class LLFloaterScriptDebugOutput : public LLFloater
+{
+public:
+ LLFloaterScriptDebugOutput();
+ LLFloaterScriptDebugOutput(const LLUUID& object_id);
+ ~LLFloaterScriptDebugOutput();
+
+ virtual void init(const LLString& title, BOOL resizable,
+ S32 min_width, S32 min_height, BOOL drag_on_left,
+ BOOL minimizable, BOOL close_btn);
+
+ void addLine(const std::string &utf8mesg, const std::string &user_name, const LLColor4& color);
+
+ static LLFloaterScriptDebugOutput* show(const LLUUID& object_id);
+ static LLFloaterScriptDebugOutput* getFloaterByID(const LLUUID& id);
+
+protected:
+ LLTextEditor* mHistoryEditor;
+
+ typedef std::map<LLUUID, LLFloaterScriptDebugOutput*> instance_map_t;
+ static instance_map_t sInstanceMap;
+
+ LLUUID mObjectID;
+};
+
+#endif // LL_LLFLOATERSCRIPTDEBUG_H
diff --git a/indra/newview/llfloatersellland.cpp b/indra/newview/llfloatersellland.cpp
new file mode 100755
index 0000000000..3c63ad69cb
--- /dev/null
+++ b/indra/newview/llfloatersellland.cpp
@@ -0,0 +1,486 @@
+/**
+ * @file llfloatersellland.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersellland.h"
+
+#include "llfloateravatarpicker.h"
+#include "llfloater.h"
+#include "llfloaterland.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llparcel.h"
+#include "llselectmgr.h"
+#include "lltexturectrl.h"
+#include "llviewercontrol.h"
+#include "llviewerparcelmgr.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+// defined in llfloaterland.cpp
+void send_parcel_select_objects(S32 parcel_local_id, S32 return_type,
+ uuid_list_t* return_ids = NULL);
+
+enum Badge { BADGE_OK, BADGE_NOTE, BADGE_WARN, BADGE_ERROR };
+
+class LLFloaterSellLandUI
+: public LLFloater
+{
+private:
+ LLFloaterSellLandUI();
+ virtual ~LLFloaterSellLandUI();
+
+ LLViewerRegion* mRegion;
+ LLParcel* mParcel;
+ bool mParcelIsForSale;
+ bool mSellToBuyer;
+ bool mChoseSellTo;
+ S32 mParcelPrice;
+ S32 mParcelActualArea;
+ LLUUID mParcelSnapshot;
+ LLUUID mAuthorizedBuyer;
+ bool mParcelSoldWithObjects;
+
+ void updateParcelInfo();
+ void refreshUI();
+ void setBadge(const char* id, Badge badge);
+
+ static LLFloaterSellLandUI* sInstance;
+
+ static void onChangeValue(LLUICtrl *ctrl, void *userdata);
+ static void doSelectAgent(void *userdata);
+ static void doCancel(void *userdata);
+ static void doSellLand(void *userdata);
+ static void onConfirmSale(S32 option, void *userdata);
+ static void doShowObjects(void *userdata);
+ static void callbackHighlightTransferable(S32 option, void* userdata);
+
+ static void callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data);
+
+public:
+ virtual BOOL postBuild();
+ virtual void onClose(bool app_quitting);
+
+ static LLFloaterSellLandUI* soleInstance(bool createIfNeeded);
+
+ bool setParcel(LLViewerRegion* region, LLParcel* parcel);
+};
+
+// static
+void LLFloaterSellLand::sellLand(
+ LLViewerRegion* region, LLParcel* parcel)
+{
+ LLFloaterSellLandUI* ui = LLFloaterSellLandUI::soleInstance(true);
+ if (ui->setParcel(region, parcel))
+ {
+ ui->open();
+ }
+}
+
+// static
+LLFloaterSellLandUI* LLFloaterSellLandUI::sInstance = NULL;
+
+// static
+LLFloaterSellLandUI* LLFloaterSellLandUI::soleInstance(bool createIfNeeded)
+{
+ if (!sInstance && createIfNeeded)
+ {
+ sInstance = new LLFloaterSellLandUI();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_sell_land.xml");
+ sInstance->center();
+ }
+
+ return sInstance;
+}
+
+LLFloaterSellLandUI::LLFloaterSellLandUI()
+: LLFloater("Sell Land"),
+ mRegion(0), mParcel(0)
+{
+}
+
+LLFloaterSellLandUI::~LLFloaterSellLandUI()
+{
+ if (sInstance == this)
+ {
+ sInstance = NULL;
+ }
+}
+
+
+void LLFloaterSellLandUI::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+ delete this;
+}
+
+BOOL LLFloaterSellLandUI::postBuild()
+{
+ childSetCommitCallback("sell_to", onChangeValue, this);
+ childSetCommitCallback("price", onChangeValue, this);
+ childSetPrevalidate("price", LLLineEditor::prevalidateNonNegativeS32);
+ childSetCommitCallback("sell_objects", onChangeValue, this);
+ childSetAction("sell_to_select_agent", doSelectAgent, this);
+ childSetAction("cancel_btn", doCancel, this);
+ childSetAction("sell_btn", doSellLand, this);
+ childSetAction("show_objects", doShowObjects, this);
+ return TRUE;
+}
+
+bool LLFloaterSellLandUI::setParcel(LLViewerRegion* region, LLParcel* parcel)
+{
+ if (!parcel) // || !can_agent_modify_parcel(parcel)) // can_agent_modify_parcel was deprecated by GROUPS
+ {
+ return false;
+ }
+
+ mRegion = region;
+ mParcel = parcel;
+ mChoseSellTo = false;
+
+ updateParcelInfo();
+ refreshUI();
+
+ return true;
+}
+
+void LLFloaterSellLandUI::updateParcelInfo()
+{
+ mParcelActualArea = mParcel->getArea();
+ mParcelIsForSale = mParcel->getForSale();
+ if (mParcelIsForSale)
+ {
+ mChoseSellTo = true;
+ }
+ mParcelPrice = mParcelIsForSale ? mParcel->getSalePrice() : 0;
+ mParcelSoldWithObjects = mParcel->getSellWithObjects();
+ if (mParcelIsForSale)
+ {
+ childSetValue("price", mParcelPrice);
+ if (mParcelSoldWithObjects)
+ {
+ childSetValue("sell_objects", "yes");
+ }
+ else
+ {
+ childSetValue("sell_objects", "no");
+ }
+ }
+ else
+ {
+ childSetValue("price", "");
+ childSetValue("sell_objects", "none");
+ }
+
+ mParcelSnapshot = mParcel->getSnapshotID();
+
+ mAuthorizedBuyer = mParcel->getAuthorizedBuyerID();
+ mSellToBuyer = mAuthorizedBuyer.notNull();
+
+ if(mSellToBuyer)
+ {
+ LLString name;
+ char firstname[MAX_STRING];
+ char lastname[MAX_STRING];
+ gCacheName->getName(mAuthorizedBuyer, firstname, lastname);
+ name.assign(firstname);
+ name.append(" ");
+ name.append(lastname);
+
+ childSetText("sell_to_agent", name);
+ }
+}
+
+void LLFloaterSellLandUI::setBadge(const char* id, Badge badge)
+{
+ static LLUUID badgeOK(gViewerArt.getString("badge_ok.tga"));
+ static LLUUID badgeNote(gViewerArt.getString("badge_note.tga"));
+ static LLUUID badgeWarn(gViewerArt.getString("badge_warn.tga"));
+ static LLUUID badgeError(gViewerArt.getString("badge_error.tga"));
+
+ LLUUID badgeUUID;
+ switch (badge)
+ {
+ default:
+ case BADGE_OK: badgeUUID = badgeOK; break;
+ case BADGE_NOTE: badgeUUID = badgeNote; break;
+ case BADGE_WARN: badgeUUID = badgeWarn; break;
+ case BADGE_ERROR: badgeUUID = badgeError; break;
+ }
+
+ childSetValue(id, badgeUUID);
+}
+
+void LLFloaterSellLandUI::refreshUI()
+{
+ LLTextureCtrl* snapshot = LLViewerUICtrlFactory::getTexturePickerByName(this, "info_image");
+ if (snapshot)
+ {
+ snapshot->setImageAssetID(mParcelSnapshot);
+ }
+
+ childSetText("info_parcel", mParcel->getName());
+ childSetTextArg("info_size", "[AREA]", llformat("%d", mParcelActualArea));
+
+ LLString price_str = childGetValue("price").asString();
+ bool valid_price = false;
+ valid_price = (price_str != "") && LLLineEditor::prevalidateNonNegativeS32(utf8str_to_wstring(price_str));
+
+ if (valid_price && mParcelActualArea > 0)
+ {
+ F32 per_meter_price = 0;
+ per_meter_price = F32(mParcelPrice) / F32(mParcelActualArea);
+ childSetTextArg("price_per_m", "[PER_METER]", llformat("%0.2f", per_meter_price));
+ childShow("price_per_m");
+
+ setBadge("step_price", BADGE_OK);
+ }
+ else
+ {
+ childHide("price_per_m");
+
+ if ("" == price_str)
+ {
+ setBadge("step_price", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_price", BADGE_ERROR);
+ }
+ }
+
+ if (mSellToBuyer)
+ {
+ childSetValue("sell_to", "user");
+ childShow("sell_to_agent");
+ childShow("sell_to_select_agent");
+ }
+ else
+ {
+ if (mChoseSellTo)
+ {
+ childSetValue("sell_to", "anyone");
+ }
+ else
+ {
+ childSetValue("sell_to", "select");
+ }
+ childHide("sell_to_agent");
+ childHide("sell_to_select_agent");
+ }
+
+ // Must select Sell To: Anybody, or User (with a specified username)
+ LLString sell_to = childGetValue("sell_to").asString();
+ bool valid_sell_to = "select" != sell_to &&
+ ("user" != sell_to || mAuthorizedBuyer.notNull());
+
+ if (!valid_sell_to)
+ {
+ setBadge("step_sell_to", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_sell_to", BADGE_OK);
+ }
+
+ bool valid_sell_objects = ("none" != childGetValue("sell_objects").asString());
+
+ if (!valid_sell_objects)
+ {
+ setBadge("step_sell_objects", BADGE_NOTE);
+ }
+ else
+ {
+ setBadge("step_sell_objects", BADGE_OK);
+ }
+
+ if (valid_sell_to && valid_price && valid_sell_objects)
+ {
+ childEnable("sell_btn");
+ }
+ else
+ {
+ childDisable("sell_btn");
+ }
+}
+
+// static
+void LLFloaterSellLandUI::onChangeValue(LLUICtrl *ctrl, void *userdata)
+{
+ LLFloaterSellLandUI *self = (LLFloaterSellLandUI *)userdata;
+
+ LLString sell_to = self->childGetValue("sell_to").asString();
+
+ if (sell_to == "user")
+ {
+ self->mChoseSellTo = true;
+ self->mSellToBuyer = true;
+ if (self->mAuthorizedBuyer.isNull())
+ {
+ doSelectAgent(self);
+ }
+ }
+ else if (sell_to == "anyone")
+ {
+ self->mChoseSellTo = true;
+ self->mSellToBuyer = false;
+ }
+
+ self->mParcelPrice = self->childGetValue("price");
+
+ if ("yes" == self->childGetValue("sell_objects").asString())
+ {
+ self->mParcelSoldWithObjects = true;
+ }
+ else
+ {
+ self->mParcelSoldWithObjects = false;
+ }
+
+ self->refreshUI();
+}
+
+// static
+void LLFloaterSellLandUI::doSelectAgent(void *userdata)
+{
+ LLFloaterSellLandUI* floaterp = (LLFloaterSellLandUI*)userdata;
+ // grandparent is a floater, in order to set up dependency
+ floaterp->addDependentFloater(LLFloaterAvatarPicker::show(callbackAvatarPick, floaterp, FALSE, TRUE));
+}
+
+// static
+void LLFloaterSellLandUI::callbackAvatarPick(const std::vector<std::string>& names, const std::vector<LLUUID>& ids, void* data)
+{
+ LLFloaterSellLandUI* floaterp = (LLFloaterSellLandUI*)data;
+ LLParcel* parcel = floaterp->mParcel;
+
+ if (names.empty() || ids.empty()) return;
+
+ LLUUID id = ids[0];
+ parcel->setAuthorizedBuyerID(id);
+
+ floaterp->mAuthorizedBuyer = ids[0];
+
+ floaterp->childSetText("sell_to_agent", names[0]);
+
+ floaterp->refreshUI();
+}
+
+// static
+void LLFloaterSellLandUI::doCancel(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ self->close();
+}
+
+// static
+void LLFloaterSellLandUI::doShowObjects(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ send_parcel_select_objects(self->mParcel->getLocalID(), RT_SELL);
+
+ LLNotifyBox::showXml("TransferObjectsHighlighted",
+ callbackHighlightTransferable,
+ userdata);
+}
+
+// static
+void LLFloaterSellLandUI::callbackHighlightTransferable(S32 option, void* userdata)
+{
+ gSelectMgr->unhighlightAll();
+}
+
+// static
+void LLFloaterSellLandUI::doSellLand(void *userdata)
+{
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+
+ LLParcel* parcel = self->mParcel;
+
+ // Do a confirmation
+ if (!parcel->getForSale())
+ {
+ S32 sale_price = self->childGetValue("price");
+ S32 area = parcel->getArea();
+ std::string authorizedBuyerName = "Anyone";
+ bool sell_to_anyone = true;
+ if ("user" == self->childGetValue("sell_to").asString())
+ {
+ authorizedBuyerName = self->childGetText("sell_to_agent");
+ sell_to_anyone = false;
+ }
+
+ // must sell to someone if indicating sale to anyone
+ if ((sale_price == 0) && sell_to_anyone)
+ {
+ gViewerWindow->alertXml("SalePriceRestriction");
+ return;
+ }
+
+ LLStringBase<char>::format_map_t args;
+ args["[LAND_SIZE]"] = llformat("%d",area);
+ args["[SALE_PRICE]"] = llformat("%d",sale_price);
+ args["[NAME]"] = authorizedBuyerName;
+
+ gViewerWindow->alertXml("ConfirmLandSaleChange", args, onConfirmSale, self);
+ }
+ else
+ {
+ onConfirmSale(-1, self);
+ }
+}
+
+// static
+void LLFloaterSellLandUI::onConfirmSale(S32 option, void *userdata)
+{
+ if (option != 0)
+ {
+ return;
+ }
+ LLFloaterSellLandUI* self = (LLFloaterSellLandUI*)userdata;
+ S32 sale_price = self->childGetValue("price");
+
+ // Valid extracted data
+ if (sale_price < 0)
+ {
+ // TomY TODO: Throw an error
+ return;
+ }
+
+ LLParcel* parcel = self->mParcel;
+
+ // can_agent_modify_parcel deprecated by GROUPS
+// if (!can_agent_modify_parcel(parcel))
+// {
+// self->close();
+// return;
+// }
+
+ parcel->setParcelFlag(PF_FOR_SALE, TRUE);
+ parcel->setSalePrice(sale_price);
+ bool sell_with_objects = false;
+ if ("yes" == self->childGetValue("sell_objects").asString())
+ {
+ sell_with_objects = true;
+ }
+ parcel->setSellWithObjects(sell_with_objects);
+ if ("user" == self->childGetValue("sell_to").asString())
+ {
+ parcel->setAuthorizedBuyerID(self->mAuthorizedBuyer);
+ }
+ else
+ {
+ parcel->setAuthorizedBuyerID(LLUUID::null);
+ }
+
+ // Send update to server
+ gParcelMgr->sendParcelPropertiesUpdate( parcel, LLFloaterLand::sRequestReplyOnUpdate );
+
+ self->close();
+}
diff --git a/indra/newview/llfloatersellland.h b/indra/newview/llfloatersellland.h
new file mode 100755
index 0000000000..077a775ffc
--- /dev/null
+++ b/indra/newview/llfloatersellland.h
@@ -0,0 +1,21 @@
+/**
+ * @file llfloatersellland.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSELLLAND_H
+#define LL_LLFLOATERSELLLAND_H
+
+class LLParcel;
+class LLViewerRegion;
+
+class LLFloaterSellLand
+{
+public:
+ static void sellLand(LLViewerRegion* region,
+ LLParcel* parcel);
+};
+
+#endif // LL_LLFLOATERSELLLAND_H
diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp
new file mode 100644
index 0000000000..8219ae8844
--- /dev/null
+++ b/indra/newview/llfloatersnapshot.cpp
@@ -0,0 +1,1523 @@
+/**
+ * @file llfloatersnapshot.cpp
+ * @brief Snapshot preview window, allowing saving, e-mailing, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatersnapshot.h"
+
+#include "llfontgl.h"
+#include "llsys.h"
+#include "llgl.h"
+#include "v3dmath.h"
+#include "lldir.h"
+#include "llsdserialize.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llcriticaldamp.h"
+#include "llui.h"
+#include "llviewertexteditor.h"
+#include "llfocusmgr.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llviewercontrol.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerstats.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "llviewermenu.h"
+#include "llfloaterpostcard.h"
+#include "llcheckboxctrl.h"
+#include "llradiogroup.h"
+#include "lltoolfocus.h"
+#include "lltoolmgr.h"
+#include "llworld.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llimagejpeg.h"
+#include "llimagej2c.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+LLSnapshotFloaterView* gSnapshotFloaterView = NULL;
+
+LLFloaterSnapshot* LLFloaterSnapshot::sInstance = NULL;
+const F32 SNAPSHOT_TIME_DELAY = 1.f;
+
+F32 SHINE_TIME = 0.5f;
+F32 SHINE_WIDTH = 0.6f;
+F32 SHINE_OPACITY = 0.3f;
+F32 FALL_TIME = 0.6f;
+S32 BORDER_WIDTH = 6;
+
+const S32 MAX_POSTCARD_DATASIZE = 1024 * 1024; // one megabyte
+
+///----------------------------------------------------------------------------
+/// Class LLSnapshotLivePreview
+///----------------------------------------------------------------------------
+class LLSnapshotLivePreview : public LLView
+{
+public:
+ enum ESnapshotType
+ {
+ SNAPSHOT_POSTCARD,
+ SNAPSHOT_TEXTURE,
+ SNAPSHOT_BITMAP
+ };
+
+ LLSnapshotLivePreview(const LLRect& rect);
+ ~LLSnapshotLivePreview();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void setSize(S32 w, S32 h);
+ void getSize(S32& w, S32& h) const;
+ S32 getDataSize() const { return mDataSize; }
+
+ ESnapshotType getSnapshotType() const { return mSnapshotType; }
+ BOOL getSnapshotUpToDate() const { return mSnapshotUpToDate; }
+ BOOL isSnapshotActive() { return mSnapshotActive; }
+ LLImageGL* getCurrentImage();
+ F32 getImageAspect();
+ LLRect getImageRect();
+ BOOL isImageScaled();
+
+ void setSnapshotType(ESnapshotType type) { mSnapshotType = type; }
+ void setSnapshotQuality(S32 quality);
+ void setSnapshotBufferType(LLViewerWindow::ESnapshotType type) { mSnapshotBufferType = type; }
+ void updateSnapshot(BOOL new_snapshot);
+ LLFloaterPostcard* savePostcard();
+ void saveTexture();
+ void saveLocal();
+
+ static void onIdle( void* snapshot_preview );
+
+protected:
+ LLColor4 mColor;
+ LLPointer<LLImageGL> mViewerImage[2];
+ LLRect mImageRect[2];
+ S32 mWidth[2];
+ S32 mHeight[2];
+ BOOL mImageScaled[2];
+
+ S32 mCurImageIndex;
+ LLPointer<LLImageRaw> mRawImage;
+ LLPointer<LLImageRaw> mRawImageEncoded;
+ LLPointer<LLImageJPEG> mJPEGImage;
+ LLFrameTimer mSnapshotDelayTimer;
+ S32 mShineCountdown;
+ LLFrameTimer mShineAnimTimer;
+ F32 mFlashAlpha;
+ BOOL mNeedsFlash;
+ LLVector3d mPosTakenGlobal;
+ S32 mSnapshotQuality;
+ S32 mDataSize;
+ ESnapshotType mSnapshotType;
+ BOOL mSnapshotUpToDate;
+ LLFrameTimer mFallAnimTimer;
+ LLVector3 mCameraPos;
+ LLQuaternion mCameraRot;
+ BOOL mSnapshotActive;
+ LLViewerWindow::ESnapshotType mSnapshotBufferType;
+
+public:
+ static std::set<LLSnapshotLivePreview*> sList;
+};
+
+std::set<LLSnapshotLivePreview*> LLSnapshotLivePreview::sList;
+
+LLSnapshotLivePreview::LLSnapshotLivePreview (const LLRect& rect) :
+ LLView("snapshot_live_preview", rect, FALSE),
+ mColor(1.f, 0.f, 0.f, 0.5f),
+ mCurImageIndex(0),
+ mRawImage(NULL),
+ mRawImageEncoded(NULL),
+ mJPEGImage(NULL),
+ mShineCountdown(0),
+ mFlashAlpha(0.f),
+ mNeedsFlash(TRUE),
+ mSnapshotQuality(gSavedSettings.getS32("SnapshotQuality")),
+ mDataSize(0),
+ mSnapshotType(SNAPSHOT_POSTCARD),
+ mSnapshotUpToDate(FALSE),
+ mCameraPos(gCamera->getOrigin()),
+ mCameraRot(gCamera->getQuaternion()),
+ mSnapshotActive(FALSE),
+ mSnapshotBufferType(LLViewerWindow::SNAPSHOT_TYPE_COLOR)
+{
+ mSnapshotDelayTimer.setTimerExpirySec(0.0f);
+ mSnapshotDelayTimer.start();
+// gIdleCallbacks.addFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.insert(this);
+ setFollowsAll();
+ mWidth[0] = gViewerWindow->getWindowDisplayWidth();
+ mWidth[1] = gViewerWindow->getWindowDisplayWidth();
+ mHeight[0] = gViewerWindow->getWindowDisplayHeight();
+ mHeight[1] = gViewerWindow->getWindowDisplayHeight();
+ mImageScaled[0] = FALSE;
+ mImageScaled[1] = FALSE;
+}
+
+LLSnapshotLivePreview::~LLSnapshotLivePreview()
+{
+ // delete images
+ mRawImage = NULL;
+ mRawImageEncoded = NULL;
+ mJPEGImage = NULL;
+
+// gIdleCallbacks.deleteFunction( &LLSnapshotLivePreview::onIdle, (void*)this );
+ sList.erase(this);
+}
+
+LLImageGL* LLSnapshotLivePreview::getCurrentImage()
+{
+ return mViewerImage[mCurImageIndex];
+}
+
+F32 LLSnapshotLivePreview::getImageAspect()
+{
+ if (!mViewerImage[mCurImageIndex])
+ {
+ return 0.f;
+ }
+
+ F32 image_aspect_ratio = ((F32)mWidth[mCurImageIndex]) / ((F32)mHeight[mCurImageIndex]);
+ F32 window_aspect_ratio = ((F32)mRect.getWidth()) / ((F32)mRect.getHeight());
+
+ if (gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ return image_aspect_ratio;
+ }
+ else
+ {
+ return window_aspect_ratio;
+ }
+}
+
+LLRect LLSnapshotLivePreview::getImageRect()
+{
+ return mImageRect[mCurImageIndex];
+}
+
+BOOL LLSnapshotLivePreview::isImageScaled()
+{
+ return mImageScaled[mCurImageIndex];
+}
+
+void LLSnapshotLivePreview::updateSnapshot(BOOL new_snapshot)
+{
+ if (mSnapshotUpToDate)
+ {
+ S32 old_image_index = mCurImageIndex;
+ mCurImageIndex = (mCurImageIndex + 1) % 2;
+ mWidth[mCurImageIndex] = mWidth[old_image_index];
+ mHeight[mCurImageIndex] = mHeight[old_image_index];
+ mFallAnimTimer.start();
+ }
+ mSnapshotUpToDate = FALSE;
+ mShineAnimTimer.stop();
+ if (new_snapshot)
+ {
+ mSnapshotDelayTimer.start();
+ mSnapshotDelayTimer.setTimerExpirySec(SNAPSHOT_TIME_DELAY);
+ }
+
+ LLRect& rect = mImageRect[mCurImageIndex];
+ rect.set(0, mRect.getHeight(), mRect.getWidth(), 0);
+
+ F32 image_aspect_ratio = ((F32)mWidth[mCurImageIndex]) / ((F32)mHeight[mCurImageIndex]);
+ F32 window_aspect_ratio = ((F32)mRect.getWidth()) / ((F32)mRect.getHeight());
+
+ if (gSavedSettings.getBOOL("KeepAspectForSnapshot"))
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ // trim off top and bottom
+ S32 new_height = llround((F32)mRect.getWidth() / image_aspect_ratio);
+ rect.mBottom += (mRect.getHeight() - new_height) / 2;
+ rect.mTop -= (mRect.getHeight() - new_height) / 2;
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ // trim off left and right
+ S32 new_width = llround((F32)mRect.getHeight() * image_aspect_ratio);
+ rect.mLeft += (mRect.getWidth() - new_width) / 2;
+ rect.mRight -= (mRect.getWidth() - new_width) / 2;
+ }
+ }
+}
+
+void LLSnapshotLivePreview::setSnapshotQuality(S32 quality)
+{
+ if (quality != mSnapshotQuality)
+ {
+ mSnapshotQuality = quality;
+ gSavedSettings.setS32("SnapshotQuality", quality);
+ }
+}
+
+EWidgetType LLSnapshotLivePreview::getWidgetType() const
+{
+ return WIDGET_TYPE_SNAPSHOT_LIVE_PREVIEW;
+}
+
+LLString LLSnapshotLivePreview::getWidgetTag() const
+{
+ return LL_SNAPSHOT_LIVE_PREVIEW_TAG;
+}
+
+void LLSnapshotLivePreview::draw()
+{
+ if(getVisible())
+ {
+ if (mViewerImage[mCurImageIndex].notNull() &&
+ mRawImageEncoded.notNull() &&
+ mSnapshotUpToDate)
+ {
+ LLColor4 bg_color(0.f, 0.f, 0.3f, 0.4f);
+ gl_rect_2d(mRect, bg_color);
+ LLRect &rect = mImageRect[mCurImageIndex];
+ LLRect shadow_rect = mImageRect[mCurImageIndex];
+ shadow_rect.stretch(BORDER_WIDTH);
+ gl_drop_shadow(shadow_rect.mLeft, shadow_rect.mTop, shadow_rect.mRight, shadow_rect.mBottom, LLColor4(0.f, 0.f, 0.f, mNeedsFlash ? 0.f :0.5f), 10);
+
+ LLGLSTexture set_texture;
+ LLColor4 image_color(1.f, 1.f, 1.f, 1.f);
+ glColor4fv(image_color.mV);
+ LLViewerImage::bindTexture(mViewerImage[mCurImageIndex]);
+ // calculate UV scale
+ F32 uv_width = mImageScaled[mCurImageIndex] ? 1.f : llmin((F32)mWidth[mCurImageIndex] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f);
+ F32 uv_height = mImageScaled[mCurImageIndex] ? 1.f : llmin((F32)mHeight[mCurImageIndex] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f);
+ glPushMatrix();
+ {
+ glTranslatef((F32)rect.mLeft, (F32)rect.mBottom, 0.f);
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(uv_width, uv_height);
+ glVertex2i(rect.getWidth(), rect.getHeight() );
+
+ glTexCoord2f(0.f, uv_height);
+ glVertex2i(0, rect.getHeight() );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(uv_width, 0.f);
+ glVertex2i(rect.getWidth(), 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+
+ glColor4f(1.f, 1.f, 1.f, mFlashAlpha);
+ gl_rect_2d(mRect);
+ if (mNeedsFlash)
+ {
+ if (mFlashAlpha < 1.f)
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 1.f, LLCriticalDamp::getInterpolant(0.02f));
+ }
+ else
+ {
+ mNeedsFlash = FALSE;
+ }
+ }
+ else
+ {
+ mFlashAlpha = lerp(mFlashAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ if (mShineCountdown > 0)
+ {
+ mShineCountdown--;
+ if (mShineCountdown == 0)
+ {
+ mShineAnimTimer.start();
+ }
+ }
+ else if (mShineAnimTimer.getStarted())
+ {
+ //LLDebugVarMessageBox::show("Shine time", &SHINE_TIME, 10.f, 0.1f);
+ //LLDebugVarMessageBox::show("Shine width", &SHINE_WIDTH, 2.f, 0.05f);
+ //LLDebugVarMessageBox::show("Shine opacity", &SHINE_OPACITY, 1.f, 0.05f);
+
+ F32 shine_interp = llmin(1.f, mShineAnimTimer.getElapsedTimeF32() / SHINE_TIME);
+
+ // draw "shine" effect
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+ {
+ // draw diagonal stripe with gradient that passes over screen
+ S32 x1 = gViewerWindow->getWindowWidth() * llround((clamp_rescale(shine_interp, 0.f, 1.f, -1.f - SHINE_WIDTH, 1.f)));
+ S32 x2 = x1 + llround(gViewerWindow->getWindowWidth() * SHINE_WIDTH);
+ S32 x3 = x2 + llround(gViewerWindow->getWindowWidth() * SHINE_WIDTH);
+ S32 y1 = 0;
+ S32 y2 = gViewerWindow->getWindowHeight();
+
+ LLGLSNoTexture no_texture;
+ glBegin(GL_QUADS);
+ {
+ glColor4f(1.f, 1.f, 1.f, 0.f);
+ glVertex2i(x1, y1);
+ glVertex2i(x1 + gViewerWindow->getWindowWidth(), y2);
+ glColor4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ glVertex2i(x2 + gViewerWindow->getWindowWidth(), y2);
+ glVertex2i(x2, y1);
+
+ glColor4f(1.f, 1.f, 1.f, SHINE_OPACITY);
+ glVertex2i(x2, y1);
+ glVertex2i(x2 + gViewerWindow->getWindowWidth(), y2);
+ glColor4f(1.f, 1.f, 1.f, 0.f);
+ glVertex2i(x3 + gViewerWindow->getWindowWidth(), y2);
+ glVertex2i(x3, y1);
+ }
+ glEnd();
+ }
+
+ if (mShineAnimTimer.getElapsedTimeF32() > SHINE_TIME)
+ {
+ mShineAnimTimer.stop();
+ }
+ }
+ }
+
+ // draw framing rectangle
+ {
+ LLGLSNoTexture no_texture;
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ LLRect outline_rect = mImageRect[mCurImageIndex];
+ glBegin(GL_QUADS);
+ {
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight, outline_rect.mTop);
+ glVertex2i(outline_rect.mLeft, outline_rect.mTop);
+
+ glVertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+
+ glVertex2i(outline_rect.mLeft, outline_rect.mTop);
+ glVertex2i(outline_rect.mLeft, outline_rect.mBottom);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ glVertex2i(outline_rect.mLeft - BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+
+ glVertex2i(outline_rect.mRight, outline_rect.mBottom);
+ glVertex2i(outline_rect.mRight, outline_rect.mTop);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mTop + BORDER_WIDTH);
+ glVertex2i(outline_rect.mRight + BORDER_WIDTH, outline_rect.mBottom - BORDER_WIDTH);
+ }
+ glEnd();
+ }
+
+ // draw old image dropping away
+ if (mFallAnimTimer.getStarted())
+ {
+ S32 old_image_index = (mCurImageIndex + 1) % 2;
+ if (mViewerImage[old_image_index].notNull() && mFallAnimTimer.getElapsedTimeF32() < FALL_TIME)
+ {
+ LLGLSTexture texture_set;
+
+ F32 fall_interp = mFallAnimTimer.getElapsedTimeF32() / FALL_TIME;
+ F32 alpha = clamp_rescale(fall_interp, 0.f, 1.f, 0.8f, 0.4f);
+ LLColor4 image_color(1.f, 1.f, 1.f, alpha);
+ glColor4fv(image_color.mV);
+ LLViewerImage::bindTexture(mViewerImage[old_image_index]);
+ // calculate UV scale
+ //FIXME get this to work with old image
+ BOOL rescale = !mImageScaled[old_image_index] && mViewerImage[mCurImageIndex].notNull();
+ F32 uv_width = rescale ? llmin((F32)mWidth[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getWidth(), 1.f) : 1.f;
+ F32 uv_height = rescale ? llmin((F32)mHeight[old_image_index] / (F32)mViewerImage[mCurImageIndex]->getHeight(), 1.f) : 1.f;
+ glPushMatrix();
+ {
+ LLRect& rect = mImageRect[old_image_index];
+ glTranslatef((F32)rect.mLeft, (F32)rect.mBottom - llround(mRect.getHeight() * 2.f * (fall_interp * fall_interp)), 0.f);
+ glRotatef(-45.f * fall_interp, 0.f, 0.f, 1.f);
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(uv_width, uv_height);
+ glVertex2i(rect.getWidth(), rect.getHeight() );
+
+ glTexCoord2f(0.f, uv_height);
+ glVertex2i(0, rect.getHeight() );
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(uv_width, 0.f);
+ glVertex2i(rect.getWidth(), 0);
+ }
+ glEnd();
+ }
+ glPopMatrix();
+ }
+ }
+ }
+}
+
+/*virtual*/
+void LLSnapshotLivePreview::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLRect old_rect = mRect;
+ LLView::reshape(width, height, called_from_parent);
+ if (old_rect.getWidth() != width || old_rect.getHeight() != height)
+ {
+ updateSnapshot(getSnapshotUpToDate());
+ }
+}
+
+//static
+void LLSnapshotLivePreview::onIdle( void* snapshot_preview )
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)snapshot_preview;
+
+ LLVector3 new_camera_pos = gCamera->getOrigin();
+ LLQuaternion new_camera_rot = gCamera->getQuaternion();
+ if (gSavedSettings.getBOOL("FreezeTime") &&
+ (new_camera_pos != previewp->mCameraPos || dot(new_camera_rot, previewp->mCameraRot) < 0.995f))
+ {
+ previewp->mCameraPos = new_camera_pos;
+ previewp->mCameraRot = new_camera_rot;
+ // request a new snapshot whenever the camera moves, with a time delay
+ previewp->updateSnapshot(gSavedSettings.getBOOL("AutoSnapshot"));
+ }
+
+ previewp->mSnapshotActive = (previewp->mSnapshotDelayTimer.getStarted() &&
+ previewp->mSnapshotDelayTimer.hasExpired());
+
+ // don't take snapshots while ALT-zoom active
+ if (gFocusMgr.getMouseCapture() == gToolCamera)
+ {
+ previewp->mSnapshotActive = FALSE;
+ }
+
+ if (previewp->mSnapshotActive)
+ {
+ if (!previewp->mRawImage)
+ {
+ previewp->mRawImage = new LLImageRaw;
+ }
+
+ if (!previewp->mRawImageEncoded)
+ {
+ previewp->mRawImageEncoded = new LLImageRaw;
+ }
+
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+
+ previewp->getWindow()->incBusyCount();
+ previewp->mImageScaled[previewp->mCurImageIndex] = FALSE;
+
+ // do update
+ if(gViewerWindow->rawSnapshot(previewp->mRawImage,
+ previewp->mWidth[previewp->mCurImageIndex],
+ previewp->mHeight[previewp->mCurImageIndex],
+ !gSavedSettings.getBOOL("KeepAspectForSnapshot"),
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE,
+ previewp->mSnapshotBufferType))
+ {
+ previewp->mRawImageEncoded->resize(previewp->mRawImage->getWidth(), previewp->mRawImage->getHeight(), previewp->mRawImage->getComponents());
+
+ if (previewp->getSnapshotType() == SNAPSHOT_POSTCARD)
+ {
+ //FIXME: just resize and reuse existing jpeg?
+ previewp->mJPEGImage = NULL; // deletes image
+ previewp->mJPEGImage = new LLImageJPEG();
+ previewp->mJPEGImage->setEncodeQuality(llclamp(previewp->mSnapshotQuality, 0, 100));
+ if (previewp->mJPEGImage->encode(previewp->mRawImage))
+ {
+ previewp->mDataSize = previewp->mJPEGImage->getDataSize();
+ previewp->mJPEGImage->decode(previewp->mRawImageEncoded);
+ }
+ }
+ else if (previewp->getSnapshotType() == SNAPSHOT_TEXTURE)
+ {
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(previewp->mRawImage->getData(),
+ previewp->mRawImage->getWidth(),
+ previewp->mRawImage->getHeight(),
+ previewp->mRawImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(512);
+ previewp->mImageScaled[previewp->mCurImageIndex] = TRUE;
+ if (formatted->encode(scaled))
+ {
+ previewp->mDataSize = formatted->getDataSize();
+ formatted->decode(previewp->mRawImageEncoded);
+ }
+ }
+ else
+ {
+ previewp->mRawImageEncoded->copy(previewp->mRawImage);
+ previewp->mDataSize = previewp->mRawImage->getDataSize();
+ }
+
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(previewp->mRawImageEncoded->getData(),
+ previewp->mRawImageEncoded->getWidth(),
+ previewp->mRawImageEncoded->getHeight(),
+ previewp->mRawImageEncoded->getComponents());
+
+ // leave original image dimensions, just scale up texture buffer
+ if (previewp->mRawImageEncoded->getWidth() > 1024 || previewp->mRawImageEncoded->getHeight() > 1024)
+ {
+ // go ahead and shrink image to appropriate power of 2 for display
+ scaled->biasedScaleToPowerOfTwo(1024);
+ previewp->mImageScaled[previewp->mCurImageIndex] = TRUE;
+ }
+ else
+ {
+ // expand image but keep original image data intact
+ scaled->expandToPowerOfTwo(1024, FALSE);
+ }
+
+ previewp->mViewerImage[previewp->mCurImageIndex] = new LLImageGL(scaled, FALSE);
+ previewp->mViewerImage[previewp->mCurImageIndex]->setMipFilterNearest(previewp->getSnapshotType() != SNAPSHOT_TEXTURE);
+ LLViewerImage::bindTexture(previewp->mViewerImage[previewp->mCurImageIndex]);
+ previewp->mViewerImage[previewp->mCurImageIndex]->setClamp(TRUE, TRUE);
+
+ previewp->mSnapshotUpToDate = TRUE;
+
+ previewp->mPosTakenGlobal = gAgent.getCameraPositionGlobal();
+ previewp->mShineCountdown = 4; // wait a few frames to avoid animation glitch due to readback this frame
+ }
+ previewp->getWindow()->decBusyCount();
+ // only show fullscreen preview when in freeze frame mode
+ previewp->setVisible(gSavedSettings.getBOOL("UseFreezeFrame"));
+ previewp->mSnapshotDelayTimer.stop();
+ previewp->mSnapshotActive = FALSE;
+ }
+}
+
+void LLSnapshotLivePreview::setSize(S32 w, S32 h)
+{
+ mWidth[mCurImageIndex] = w;
+ mHeight[mCurImageIndex] = h;
+}
+
+void LLSnapshotLivePreview::getSize(S32& w, S32& h) const
+{
+ w = mWidth[mCurImageIndex];
+ h = mHeight[mCurImageIndex];
+}
+
+LLFloaterPostcard* LLSnapshotLivePreview::savePostcard()
+{
+ // calculate and pass in image scale in case image data only use portion
+ // of viewerimage buffer
+ LLVector2 image_scale(1.f, 1.f);
+ if (!isImageScaled())
+ {
+ image_scale.setVec(llmin(1.f, (F32)mWidth[mCurImageIndex] / (F32)getCurrentImage()->getWidth()), llmin(1.f, (F32)mHeight[mCurImageIndex] / (F32)getCurrentImage()->getHeight()));
+ }
+
+
+ LLFloaterPostcard* floater = LLFloaterPostcard::showFromSnapshot(mJPEGImage, mViewerImage[mCurImageIndex], image_scale, mPosTakenGlobal);
+ // relinquish lifetime of viewerimage and jpeg image to postcard floater
+ mViewerImage[mCurImageIndex] = NULL;
+ mJPEGImage = NULL;
+
+ return floater;
+}
+
+void LLSnapshotLivePreview::saveTexture()
+{
+ // gen a new uuid for this asset
+ LLTransactionID tid;
+ tid.generate();
+ LLAssetID new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLPointer<LLImageJ2C> formatted = new LLImageJ2C;
+ LLPointer<LLImageRaw> scaled = new LLImageRaw(mRawImage->getData(),
+ mRawImage->getWidth(),
+ mRawImage->getHeight(),
+ mRawImage->getComponents());
+
+ scaled->biasedScaleToPowerOfTwo(512);
+
+ if (formatted->encode(scaled))
+ {
+ LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE);
+ std::string pos_string;
+ gAgent.buildLocationString(pos_string);
+ upload_new_resource(tid, // tid
+ LLAssetType::AT_TEXTURE,
+ "Snapshot",
+ pos_string,
+ 0,
+ LLAssetType::AT_SNAPSHOT_CATEGORY,
+ LLInventoryType::IT_SNAPSHOT,
+ PERM_ALL,
+ "Snapshot");
+ }
+ else
+ {
+ gViewerWindow->alertXml("ErrorEncodingSnapshot");
+ llwarns << "Error encoding snapshot" << llendl;
+ }
+
+ gViewerStats->incStat(LLViewerStats::ST_SNAPSHOT_COUNT );
+}
+
+void LLSnapshotLivePreview::saveLocal()
+{
+ gViewerWindow->saveImageNumbered(mRawImage);
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterSnapshot::Impl
+///----------------------------------------------------------------------------
+
+class LLFloaterSnapshot::Impl
+{
+public:
+ Impl() : mLastToolset(NULL)
+ {
+ }
+ ~Impl()
+ {
+ //unpause avatars
+ mAvatarPauseHandles.clear();
+
+ }
+ static void onClickDiscard(void* data);
+ static void onClickKeep(void* data);
+ static void onClickNewSnapshot(void* data);
+ static void onClickAutoSnap(LLUICtrl *ctrl, void* data);
+ static void onClickUICheck(LLUICtrl *ctrl, void* data);
+ static void onClickHUDCheck(LLUICtrl *ctrl, void* data);
+ static void onClickKeepOpenCheck(LLUICtrl *ctrl, void* data);
+ static void onClickKeepAspectCheck(LLUICtrl *ctrl, void* data);
+ static void onCommitQuality(LLUICtrl* ctrl, void* data);
+ static void onCommitResolution(LLUICtrl* ctrl, void* data);
+ static void onCommitFreezeFrame(LLUICtrl* ctrl, void* data);
+ static void onCommitLayerTypes(LLUICtrl* ctrl, void*data);
+ static void onCommitSnapshotType(LLUICtrl* ctrl, void* data);
+ static void onCommitCustomResolution(LLUICtrl *ctrl, void* data);
+
+ static LLSnapshotLivePreview* getPreviewView(LLFloaterSnapshot *floater);
+ static void setResolution(LLFloaterSnapshot* floater, const std::string& comboname);
+ static void updateControls(LLFloaterSnapshot* floater);
+ static void updateLayout(LLFloaterSnapshot* floater);
+
+ static LLViewHandle sPreviewHandle;
+
+private:
+ static LLSnapshotLivePreview::ESnapshotType getTypeIndex(LLFloaterSnapshot* floater);
+ static LLViewerWindow::ESnapshotType getLayerType(LLFloaterSnapshot* floater);
+ static void comboSetCustom(LLFloaterSnapshot *floater, const std::string& comboname);
+ static void checkAutoSnapshot(LLSnapshotLivePreview* floater);
+
+public:
+ std::vector<LLAnimPauseRequest> mAvatarPauseHandles;
+
+ LLToolset* mLastToolset;
+};
+
+// static
+LLViewHandle LLFloaterSnapshot::Impl::sPreviewHandle;
+
+// static
+LLSnapshotLivePreview* LLFloaterSnapshot::Impl::getPreviewView(LLFloaterSnapshot *floater)
+{
+ LLSnapshotLivePreview* previewp = (LLSnapshotLivePreview*)LLView::getViewByHandle(sPreviewHandle);
+ return previewp;
+}
+
+// static
+LLSnapshotLivePreview::ESnapshotType LLFloaterSnapshot::Impl::getTypeIndex(LLFloaterSnapshot* floater)
+{
+ LLSnapshotLivePreview::ESnapshotType index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
+ LLSD value = floater->childGetValue("snapshot_type_radio");
+ const std::string id = value.asString();
+ if (id == "postcard")
+ index = LLSnapshotLivePreview::SNAPSHOT_POSTCARD;
+ else if (id == "texture")
+ index = LLSnapshotLivePreview::SNAPSHOT_TEXTURE;
+ else if (id == "local")
+ index = LLSnapshotLivePreview::SNAPSHOT_BITMAP;
+ return index;
+}
+
+// static
+LLViewerWindow::ESnapshotType LLFloaterSnapshot::Impl::getLayerType(LLFloaterSnapshot* floater)
+{
+ LLViewerWindow::ESnapshotType type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ LLSD value = floater->childGetValue("layer_types");
+ const std::string id = value.asString();
+ if (id == "colors")
+ type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ else if (id == "depth")
+ type = LLViewerWindow::SNAPSHOT_TYPE_DEPTH;
+ else if (id == "objects")
+ type = LLViewerWindow::SNAPSHOT_TYPE_OBJECT_ID;
+ return type;
+}
+
+// static
+void LLFloaterSnapshot::Impl::setResolution(LLFloaterSnapshot* floater, const std::string& comboname)
+{
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(floater, comboname);
+ if (combo)
+ {
+ combo->setVisible(TRUE);
+ onCommitResolution(combo, floater);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::updateLayout(LLFloaterSnapshot* floaterp)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView(floaterp);
+ if (floaterp->childGetValue("freeze_frame_check").asBoolean())
+ {
+ // stop all mouse events at fullscreen preview layer
+ floaterp->getParent()->setMouseOpaque(TRUE);
+
+ // shrink to smaller layout
+ floaterp->reshape(floaterp->mRect.getWidth(), 410);
+
+ // can see and interact with fullscreen preview now
+ if (previewp)
+ {
+ previewp->setVisible(TRUE);
+ previewp->setEnabled(TRUE);
+ }
+
+ //RN: freeze all avatars
+ LLCharacter* avatarp;
+ for (avatarp = LLCharacter::sInstances.getFirstData(); avatarp; avatarp = LLCharacter::sInstances.getNextData())
+ {
+ sInstance->impl.mAvatarPauseHandles.push_back(avatarp->requestPause());
+ }
+
+ // freeze everything else
+ gSavedSettings.setBOOL("FreezeTime", TRUE);
+
+ if (gCurrentToolset != gCameraToolset)
+ {
+ sInstance->impl.mLastToolset = gCurrentToolset;
+ gCurrentToolset = gCameraToolset;
+ if (gToolMgr)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+ }
+ else // turning off freeze frame mode
+ {
+ floaterp->getParent()->setMouseOpaque(FALSE);
+ floaterp->reshape(floaterp->mRect.getWidth(), 510);
+ if (previewp)
+ {
+ previewp->setVisible(FALSE);
+ previewp->setEnabled(FALSE);
+ }
+
+ //RN: thaw all avatars
+ sInstance->impl.mAvatarPauseHandles.clear();
+
+ // thaw everything else
+ gSavedSettings.setBOOL("FreezeTime", FALSE);
+
+ // restore last tool (e.g. pie menu, etc)
+ if (sInstance->impl.mLastToolset)
+ {
+ gCurrentToolset = sInstance->impl.mLastToolset;
+ if (gToolMgr)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+ }
+}
+
+
+// static
+void LLFloaterSnapshot::Impl::updateControls(LLFloaterSnapshot* floater)
+{
+ LLRadioGroup* snapshot_type_radio = LLUICtrlFactory::getRadioGroupByName(floater, "snapshot_type_radio");
+ snapshot_type_radio->setSelectedIndex(gSavedSettings.getS32("LastSnapshotType"));
+ LLSnapshotLivePreview::ESnapshotType shot_type = getTypeIndex(floater);
+ LLViewerWindow::ESnapshotType layer_type = getLayerType(floater);
+
+ floater->childSetVisible("postcard_size_combo", FALSE);
+ floater->childSetVisible("texture_size_combo", FALSE);
+ floater->childSetVisible("local_size_combo", FALSE);
+
+ LLComboBox* combo;
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "postcard_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotPostcardLastResolution"));
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "texture_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotTextureLastResolution"));
+ combo = LLUICtrlFactory::getComboBoxByName(floater, "local_size_combo");
+ if (combo) combo->selectNthItem(gSavedSettings.getS32("SnapshotLocalLastResolution"));
+
+
+ floater->childSetVisible("upload_btn", FALSE);
+ floater->childSetVisible("send_btn", FALSE);
+ floater->childSetVisible("save_btn", FALSE);
+
+ switch(shot_type)
+ {
+ case LLSnapshotLivePreview::SNAPSHOT_POSTCARD:
+ layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ floater->childSetValue("layer_types", "colors");
+ floater->childSetEnabled("layer_types", FALSE);
+ floater->childSetEnabled("image_quality_slider", TRUE);
+ setResolution(floater, "postcard_size_combo");
+ floater->childSetVisible("send_btn", TRUE);
+ break;
+ case LLSnapshotLivePreview::SNAPSHOT_TEXTURE:
+ layer_type = LLViewerWindow::SNAPSHOT_TYPE_COLOR;
+ floater->childSetValue("layer_types", "colors");
+ floater->childSetEnabled("layer_types", FALSE);
+ floater->childSetEnabled("image_quality_slider", FALSE);
+ setResolution(floater, "texture_size_combo");
+ floater->childSetVisible("upload_btn", TRUE);
+ break;
+ case LLSnapshotLivePreview::SNAPSHOT_BITMAP:
+ floater->childSetEnabled("layer_types", TRUE);
+ floater->childSetEnabled("image_quality_slider", FALSE);
+ setResolution(floater, "local_size_combo");
+ floater->childSetVisible("save_btn", TRUE);
+ break;
+ default:
+ break;
+ }
+ LLSnapshotLivePreview* previewp = getPreviewView(floater);
+ if (previewp)
+ {
+ previewp->setSnapshotType(shot_type);
+ previewp->setSnapshotBufferType(layer_type);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::checkAutoSnapshot(LLSnapshotLivePreview* previewp)
+{
+ if (previewp)
+ {
+ previewp->updateSnapshot(gSavedSettings.getBOOL("AutoSnapshot"));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickDiscard(void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ view->close();
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeep(void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+
+ if (previewp)
+ {
+ if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_POSTCARD)
+ {
+ LLFloaterPostcard* floater = previewp->savePostcard();
+ // if still in snapshot mode, put postcard floater in snapshot floaterview
+ // and link it to snapshot floater
+ if (!gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
+ {
+ gFloaterView->removeChild(floater);
+ gSnapshotFloaterView->addChild(floater);
+ view->addDependentFloater(floater, FALSE);
+ }
+ }
+ else if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_TEXTURE)
+ {
+ previewp->saveTexture();
+ }
+ else
+ {
+ previewp->saveLocal();
+ }
+
+ if (gSavedSettings.getBOOL("CloseSnapshotOnKeep"))
+ {
+ view->close();
+ // only plays sound and anim when keeping a snapshot, and closing the snapshot UI
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ else
+ {
+ checkAutoSnapshot(previewp);
+ }
+ }
+
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickNewSnapshot(void* data)
+{
+ LLSnapshotLivePreview* previewp = getPreviewView((LLFloaterSnapshot *)data);
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (previewp && view)
+ {
+ previewp->updateSnapshot(TRUE);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickAutoSnap(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "AutoSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickUICheck(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickHUDCheck(LLUICtrl *ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "RenderHUDInSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeepOpenCheck(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+
+ gSavedSettings.setBOOL( "CloseSnapshotOnKeep", !check->get() );
+}
+
+// static
+void LLFloaterSnapshot::Impl::onClickKeepAspectCheck(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ gSavedSettings.setBOOL( "KeepAspectForSnapshot", check->get() );
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ checkAutoSnapshot(getPreviewView(view));
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitQuality(LLUICtrl* ctrl, void* data)
+{
+ LLSliderCtrl* slider = (LLSliderCtrl*)ctrl;
+ S32 quality_val = llfloor((F32)slider->getValue().asReal());
+
+ LLSnapshotLivePreview* previewp = getPreviewView((LLFloaterSnapshot *)data);
+ if (previewp)
+ {
+ previewp->setSnapshotQuality(quality_val);
+ }
+ checkAutoSnapshot(previewp);
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitFreezeFrame(LLUICtrl* ctrl, void* data)
+{
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (!view || !check_box)
+ {
+ return;
+ }
+
+ gSavedSettings.setBOOL("UseFreezeFrame", check_box->get());
+
+ updateLayout(view);
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitResolution(LLUICtrl* ctrl, void* data)
+{
+ LLComboBox* combobox = (LLComboBox*)ctrl;
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (!view || !combobox)
+ {
+ return;
+ }
+
+ // save off all selected resolution values
+ LLComboBox* combo;
+ combo = LLUICtrlFactory::getComboBoxByName(view, "postcard_size_combo");
+ gSavedSettings.setS32("SnapshotPostcardLastResolution", combo->getCurrentIndex());
+ combo = LLUICtrlFactory::getComboBoxByName(view, "texture_size_combo");
+ gSavedSettings.setS32("SnapshotTextureLastResolution", combo->getCurrentIndex());
+ combo = LLUICtrlFactory::getComboBoxByName(view, "local_size_combo");
+ gSavedSettings.setS32("SnapshotLocalLastResolution", combo->getCurrentIndex());
+
+ std::string sdstring = combobox->getSimpleSelectedValue();
+ LLSD sdres;
+ std::stringstream sstream(sdstring);
+ LLSDSerialize::fromNotation(sdres, sstream);
+
+ S32 width = sdres[0];
+ S32 height = sdres[1];
+
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp && combobox->getCurrentIndex() >= 0)
+ {
+ if (width == 0 || height == 0)
+ {
+ previewp->setSize(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight());
+ }
+ else if (width == -1 || height == -1)
+ {
+ // leave width and height when entering custom value
+ }
+ else
+ {
+ previewp->setSize(width, height);
+ }
+
+ previewp->getSize(width, height);
+ view->childSetValue("snapshot_width", width);
+ view->childSetValue("snapshot_height", height);
+ // hide old preview as the aspect ratio could be wrong
+ checkAutoSnapshot(previewp);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::onCommitLayerTypes(LLUICtrl* ctrl, void*data)
+{
+ LLComboBox* combobox = (LLComboBox*)ctrl;
+
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+
+ if (view)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ previewp->setSnapshotBufferType((LLViewerWindow::ESnapshotType)combobox->getCurrentIndex());
+ }
+ checkAutoSnapshot(previewp);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::onCommitSnapshotType(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ gSavedSettings.setS32("LastSnapshotType", getTypeIndex(view));
+ getPreviewView(view)->updateSnapshot(TRUE);
+ updateControls(view);
+ }
+}
+
+// static
+void LLFloaterSnapshot::Impl::comboSetCustom(LLFloaterSnapshot* floater, const std::string& comboname)
+{
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(floater, comboname);
+ if (combo)
+ {
+ combo->setCurrentByIndex(combo->getItemCount() - 1);
+ }
+}
+
+//static
+void LLFloaterSnapshot::Impl::onCommitCustomResolution(LLUICtrl *ctrl, void* data)
+{
+ LLFloaterSnapshot *view = (LLFloaterSnapshot *)data;
+ if (view)
+ {
+ LLSnapshotLivePreview* previewp = getPreviewView(view);
+ if (previewp)
+ {
+ S32 curw,curh;
+ previewp->getSize(curw, curh);
+
+ S32 w = llfloor((F32)view->childGetValue("snapshot_width").asReal());
+ S32 h = llfloor((F32)view->childGetValue("snapshot_height").asReal());
+
+ if (w != curw || h != curh)
+ {
+ previewp->setSize(w,h);
+ checkAutoSnapshot(previewp);
+ comboSetCustom(view, "postcard_size_combo");
+ comboSetCustom(view, "texture_size_combo");
+ comboSetCustom(view, "local_size_combo");
+ }
+ }
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFloaterSnapshot
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFloaterSnapshot::LLFloaterSnapshot()
+ : LLFloater("Snapshot Floater"),
+ impl (*(new Impl))
+{
+}
+
+// Destroys the object
+LLFloaterSnapshot::~LLFloaterSnapshot()
+{
+ if (sInstance == this)
+ {
+ delete LLView::getViewByHandle(Impl::sPreviewHandle);
+ Impl::sPreviewHandle = LLViewHandle::sDeadHandle;
+ sInstance = NULL;
+ }
+
+ //unfreeze everything else
+ gSavedSettings.setBOOL("FreezeTime", FALSE);
+
+ if (impl.mLastToolset)
+ {
+ gCurrentToolset = impl.mLastToolset;
+ if (gToolMgr && gCurrentToolset)
+ {
+ gToolMgr->useSelectedTool( gCurrentToolset );
+ }
+ }
+
+ delete &impl;
+}
+
+BOOL LLFloaterSnapshot::postBuild()
+{
+ childSetCommitCallback("snapshot_type_radio", Impl::onCommitSnapshotType, this);
+
+ childSetAction("new_snapshot_btn", Impl::onClickNewSnapshot, this);
+
+ childSetValue("auto_snapshot_check", gSavedSettings.getBOOL("AutoSnapshot"));
+ childSetCommitCallback("auto_snapshot_check", Impl::onClickAutoSnap, this);
+
+ childSetAction("upload_btn", Impl::onClickKeep, this);
+ childSetAction("send_btn", Impl::onClickKeep, this);
+ childSetAction("save_btn", Impl::onClickKeep, this);
+ childSetAction("discard_btn", Impl::onClickDiscard, this);
+
+ childSetCommitCallback("image_quality_slider", Impl::onCommitQuality, this);
+ childSetValue("image_quality_slider", gSavedSettings.getS32("SnapshotQuality"));
+
+ childSetCommitCallback("snapshot_width", Impl::onCommitCustomResolution, this);
+
+ childSetCommitCallback("snapshot_height", Impl::onCommitCustomResolution, this);
+
+ childSetCommitCallback("ui_check", Impl::onClickUICheck, this);
+
+ childSetCommitCallback("hud_check", Impl::onClickHUDCheck, this);
+ childSetValue("hud_check", gSavedSettings.getBOOL("RenderHUDInSnapshot"));
+
+ childSetCommitCallback("keep_open_check", Impl::onClickKeepOpenCheck, this);
+ childSetValue("keep_open_check", !gSavedSettings.getBOOL("CloseSnapshotOnKeep"));
+
+ childSetCommitCallback("keep_aspect_check", Impl::onClickKeepAspectCheck, this);
+ childSetValue("keep_aspect_check", gSavedSettings.getBOOL("KeepAspectForSnapshot"));
+
+ childSetCommitCallback("layer_types", Impl::onCommitLayerTypes, this);
+ childSetValue("layer_types", "colors");
+ childSetEnabled("layer_types", FALSE);
+
+ childSetValue("snapshot_width", gViewerWindow->getWindowDisplayWidth());
+ childSetValue("snapshot_height", gViewerWindow->getWindowDisplayHeight());
+
+ childSetValue("freeze_frame_check", gSavedSettings.getBOOL("UseFreezeFrame"));
+ childSetCommitCallback("freeze_frame_check", Impl::onCommitFreezeFrame, this);
+
+ childSetCommitCallback("postcard_size_combo", Impl::onCommitResolution, this);
+ childSetCommitCallback("texture_size_combo", Impl::onCommitResolution, this);
+ childSetCommitCallback("local_size_combo", Impl::onCommitResolution, this);
+
+ // create preview window
+ LLRect full_screen_rect = sInstance->getRootView()->getRect();
+ LLSnapshotLivePreview* previewp = new LLSnapshotLivePreview(full_screen_rect);
+ sInstance->getRootView()->removeChild(gSnapshotFloaterView);
+ // make sure preview is below snapshot floater
+ sInstance->getRootView()->addChild(previewp);
+ sInstance->getRootView()->addChild(gSnapshotFloaterView);
+
+ Impl::sPreviewHandle = previewp->mViewHandle;
+
+ impl.updateControls(this);
+
+ return TRUE;
+}
+
+void LLFloaterSnapshot::draw()
+{
+ LLSnapshotLivePreview* previewp = impl.getPreviewView(this);
+
+ if (previewp && previewp->isSnapshotActive())
+ {
+ // don't render snapshot window in snapshot, even if "show ui" is turned on
+ return;
+ }
+
+ if(getVisible() && !mMinimized)
+ {
+ if (previewp && previewp->getDataSize() > 0)
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ LLString bytes_string;
+ if (previewp->getSnapshotType() == LLSnapshotLivePreview::SNAPSHOT_POSTCARD &&
+ previewp->getDataSize() > MAX_POSTCARD_DATASIZE)
+ {
+ childSetColor("file_size_label", LLColor4::red);
+ childSetEnabled("send_btn", FALSE);
+ }
+ else
+ {
+ childSetColor("file_size_label", gColors.getColor( "LabelTextColor" ));
+ childSetEnabled("send_btn", previewp->getSnapshotUpToDate());
+ }
+
+ //XUI:translate
+ if (previewp->getSnapshotUpToDate())
+ {
+ LLString bytes_string;
+ gResMgr->getIntegerString(bytes_string, previewp->getDataSize());
+ childSetTextArg("file_size_label", "[SIZE]", llformat("%s bytes", bytes_string.c_str()));
+ }
+ else
+ {
+ childSetTextArg("file_size_label", "[SIZE]", "unknown");
+ childSetColor("file_size_label", gColors.getColor( "LabelTextColor" ));
+ }
+ childSetEnabled("upload_btn", previewp->getSnapshotUpToDate());
+ childSetEnabled("save_btn", previewp->getSnapshotUpToDate());
+
+ }
+ else
+ {
+ childSetTextArg("file_size_label", "[SIZE]", "unknown");
+ childSetEnabled("upload_btn", FALSE);
+ childSetEnabled("send_btn", FALSE);
+ childSetEnabled("save_btn", FALSE);
+ }
+
+ BOOL ui_in_snapshot = gSavedSettings.getBOOL("RenderUIInSnapshot");
+ childSetValue("ui_check", ui_in_snapshot);
+ childSetToolTip("ui_check", "If selected shows the UI in the snapshot");
+ }
+
+ LLFloater::draw();
+
+ // draw snapshot thumbnail if not in fullscreen preview mode
+ if (!gSavedSettings.getBOOL("UseFreezeFrame") && previewp && previewp->getCurrentImage() && previewp->getSnapshotUpToDate())
+ {
+ F32 aspect = previewp->getImageAspect();
+ // UI size for thumbnail
+ S32 max_width = mRect.getWidth() - 20;
+ S32 max_height = 90;
+
+ S32 img_render_width = 0;
+ S32 img_render_height = 0;
+ if (aspect > max_width / max_height)
+ {
+ // image too wide, shrink to width
+ img_render_width = max_width;
+ img_render_height = llround((F32)max_width / aspect);
+ }
+ else
+ {
+ // image too tall, shrink to height
+ img_render_height = max_height;
+ img_render_width = llround((F32)max_height * aspect);
+ }
+ S32 image_width, image_height;
+ previewp->getSize(image_width, image_height);
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ {
+ // handle case where image is only a portion of image buffer
+ if (!previewp->isImageScaled())
+ {
+ glScalef(llmin(1.f, (F32)image_width / (F32)previewp->getCurrentImage()->getWidth()), llmin(1.f, (F32)image_height / (F32)previewp->getCurrentImage()->getHeight()), 1.f);
+ }
+ glMatrixMode(GL_MODELVIEW);
+ gl_draw_scaled_image((mRect.getWidth() - img_render_width) / 2, 35 + (max_height - img_render_height) / 2, img_render_width, img_render_height, previewp->getCurrentImage(), LLColor4::white);
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+void LLFloaterSnapshot::onClose(bool app_quitting)
+{
+ gSnapshotFloaterView->setEnabled(FALSE);
+ destroy();
+}
+
+// static
+void LLFloaterSnapshot::show(void*)
+{
+ if (!sInstance)
+ {
+ sInstance = new LLFloaterSnapshot();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_snapshot.xml", NULL, FALSE);
+ //move snapshot floater to special purpose snapshotfloaterview
+ gFloaterView->removeChild(sInstance);
+ gSnapshotFloaterView->addChild(sInstance);
+
+ sInstance->impl.updateLayout(sInstance);
+ }
+
+ sInstance->open();
+ sInstance->focusFirstItem(FALSE);
+ gSnapshotFloaterView->setEnabled(TRUE);
+ gSnapshotFloaterView->adjustToFitScreen(sInstance, FALSE);
+}
+
+void LLFloaterSnapshot::hide(void*)
+{
+ if (sInstance && !sInstance->isDead())
+ {
+ sInstance->close();
+ }
+}
+
+//static
+void LLFloaterSnapshot::update()
+{
+ for (std::set<LLSnapshotLivePreview*>::iterator iter = LLSnapshotLivePreview::sList.begin();
+ iter != LLSnapshotLivePreview::sList.end(); ++iter)
+ {
+ LLSnapshotLivePreview::onIdle(*iter);
+ }
+}
+
+//============================================================================
+
+LLSnapshotFloaterView::LLSnapshotFloaterView( const LLString& name, const LLRect& rect ) : LLFloaterView(name, rect)
+{
+ setMouseOpaque(TRUE);
+ setEnabled(FALSE);
+}
+
+LLSnapshotFloaterView::~LLSnapshotFloaterView()
+{
+}
+
+BOOL LLSnapshotFloaterView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleKey(key, mask, called_from_parent);
+ }
+
+ if (!getEnabled())
+ {
+ return FALSE;
+ }
+ else
+ {
+ if (called_from_parent)
+ {
+ // pass all keystrokes down
+ LLFloaterView::handleKey(key, mask, called_from_parent);
+ }
+ else
+ {
+ // bounce keystrokes back down
+ LLFloaterView::handleKey(key, mask, TRUE);
+ }
+ return TRUE;
+ }
+}
+
+BOOL LLSnapshotFloaterView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleMouseDown(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleMouseDown(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask );
+ }
+ return TRUE;
+}
+
+BOOL LLSnapshotFloaterView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleMouseUp(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleMouseUp(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleMouseUp( x, y, mask );
+ }
+ return TRUE;
+}
+
+BOOL LLSnapshotFloaterView::handleHover(S32 x, S32 y, MASK mask)
+{
+ // use default handler when not in freeze-frame mode
+ if(!gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return LLFloaterView::handleHover(x, y, mask);
+ }
+ // give floater a change to handle mouse, else camera tool
+ if (childrenHandleHover(x, y, mask) == NULL)
+ {
+ gToolMgr->getCurrentTool(mask)->handleHover( x, y, mask );
+ }
+ return TRUE;
+}
diff --git a/indra/newview/llfloatersnapshot.h b/indra/newview/llfloatersnapshot.h
new file mode 100644
index 0000000000..85b18a99d3
--- /dev/null
+++ b/indra/newview/llfloatersnapshot.h
@@ -0,0 +1,53 @@
+/**
+ * @file llfloatersnapshot.h
+ * @brief Snapshot preview window, allowing saving, e-mailing, etc.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERSNAPSHOT_H
+#define LL_LLFLOATERSNAPSHOT_H
+
+#include "llfloater.h"
+
+#include "llmemory.h"
+#include "llimagegl.h"
+#include "llcharacter.h"
+
+class LLFloaterSnapshot : public LLFloater
+{
+public:
+ LLFloaterSnapshot();
+ virtual ~LLFloaterSnapshot();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void onClose(bool app_quitting);
+
+ static void show(void*);
+ static void hide(void*);
+
+ static void update();
+
+private:
+ class Impl;
+ Impl& impl;
+
+ static LLFloaterSnapshot* sInstance;
+};
+
+class LLSnapshotFloaterView : public LLFloaterView
+{
+public:
+ LLSnapshotFloaterView( const LLString& name, const LLRect& rect );
+ virtual ~LLSnapshotFloaterView();
+
+ /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+};
+
+extern LLSnapshotFloaterView* gSnapshotFloaterView;
+#endif // LL_LLFLOATERSNAPSHOT_H
diff --git a/indra/newview/llfloatertelehub.cpp b/indra/newview/llfloatertelehub.cpp
new file mode 100644
index 0000000000..f27cf55b3a
--- /dev/null
+++ b/indra/newview/llfloatertelehub.cpp
@@ -0,0 +1,289 @@
+/**
+ * @file llfloatertelehub.cpp
+ * @author James Cook
+ * @brief LLFloaterTelehub class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertelehub.h"
+
+#include "message.h"
+#include "llfontgl.h"
+
+#include "llagent.h"
+#include "llfloatertools.h"
+#include "llscrolllistctrl.h"
+#include "llselectmgr.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llvieweruictrlfactory.h"
+
+LLFloaterTelehub* LLFloaterTelehub::sInstance = NULL;
+
+
+// static
+void LLFloaterTelehub::show()
+{
+ if (sInstance)
+ {
+ sInstance->setVisibleAndFrontmost();
+ return;
+ }
+
+ sInstance = new LLFloaterTelehub();
+
+ // Show tools floater by selecting translate (select) tool
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolTranslate );
+
+ // Find tools floater, glue to bottom
+ if (gFloaterTools)
+ {
+ gFloaterTools->showMore(FALSE);
+ LLRect tools_rect = gFloaterTools->getRect();
+ S32 our_width = sInstance->getRect().getWidth();
+ S32 our_height = sInstance->getRect().getHeight();
+ LLRect our_rect;
+ our_rect.setLeftTopAndSize(tools_rect.mLeft, tools_rect.mBottom, our_width, our_height);
+ sInstance->setRect(our_rect);
+ }
+
+ sInstance->sendTelehubInfoRequest();
+}
+
+LLFloaterTelehub::LLFloaterTelehub()
+: LLFloater("telehub"),
+ mTelehubObjectID(),
+ mTelehubObjectName(),
+ mTelehubPos(),
+ mTelehubRot(),
+ mNumSpawn(0)
+{
+ sInstance = this;
+
+ gMessageSystem->setHandlerFunc("TelehubInfo", processTelehubInfo);
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_telehub.xml");
+
+ childSetAction("connect_btn", onClickConnect, this);
+ childSetAction("disconnect_btn", onClickDisconnect, this);
+ childSetAction("add_spawn_point_btn", onClickAddSpawnPoint, this);
+ childSetAction("remove_spawn_point_btn", onClickRemoveSpawnPoint, this);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ // otherwise you can't walk with arrow keys while floater is up
+ list->setAllowKeyboardMovement(FALSE);
+ }
+}
+
+LLFloaterTelehub::~LLFloaterTelehub()
+{
+ sInstance = NULL;
+
+ // no longer interested in this message
+ gMessageSystem->setHandlerFunc("TelehubInfo", NULL);
+}
+
+void LLFloaterTelehub::draw()
+{
+ if (getVisible() && !isMinimized())
+ {
+ refresh();
+ }
+ LLFloater::draw();
+}
+
+// Per-frame updates, because we don't have a selection manager observer.
+void LLFloaterTelehub::refresh()
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+
+ BOOL have_selection = (object != NULL);
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+ childSetEnabled("connect_btn", have_selection && all_volume);
+
+ BOOL have_telehub = mTelehubObjectID.notNull();
+ childSetEnabled("disconnect_btn", have_telehub);
+
+ BOOL space_avail = (mNumSpawn < MAX_SPAWNPOINTS_PER_TELEHUB);
+ childSetEnabled("add_spawn_point_btn", have_selection && all_volume && space_avail);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ BOOL enable_remove = (list->getFirstSelected() != NULL);
+ childSetEnabled("remove_spawn_point_btn", enable_remove);
+ }
+}
+
+// static
+BOOL LLFloaterTelehub::renderBeacons()
+{
+ // only render if we've got a telehub
+ return sInstance && sInstance->mTelehubObjectID.notNull();
+}
+
+// static
+void LLFloaterTelehub::addBeacons()
+{
+ if (!sInstance) return;
+
+ // Find the telehub position, either our cached old position, or
+ // an updated one based on the actual object position.
+ LLVector3 hub_pos_region = sInstance->mTelehubPos;
+ LLQuaternion hub_rot = sInstance->mTelehubRot;
+ LLViewerObject* obj = gObjectList.findObject(sInstance->mTelehubObjectID);
+ if (obj)
+ {
+ hub_pos_region = obj->getPositionRegion();
+ hub_rot = obj->getRotationRegion();
+ }
+ // Draw nice thick 3-pixel lines.
+ gObjectList.addDebugBeacon(hub_pos_region, "", LLColor4::yellow, LLColor4::white, 4);
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "spawn_points_list");
+ if (list)
+ {
+ S32 spawn_index = list->getFirstSelectedIndex();
+ if (spawn_index >= 0)
+ {
+ LLVector3 spawn_pos = hub_pos_region + (sInstance->mSpawnPointPos[spawn_index] * hub_rot);
+ gObjectList.addDebugBeacon(spawn_pos, "", LLColor4::orange, LLColor4::white, 4);
+ }
+ }
+}
+
+void LLFloaterTelehub::sendTelehubInfoRequest()
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "info ui");
+}
+
+// static
+void LLFloaterTelehub::onClickConnect(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "connect");
+}
+
+// static
+void LLFloaterTelehub::onClickDisconnect(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "delete");
+}
+
+// static
+void LLFloaterTelehub::onClickAddSpawnPoint(void* data)
+{
+ gSelectMgr->sendGodlikeRequest("telehub", "spawnpoint add");
+ gSelectMgr->deselectAll();
+}
+
+// static
+void LLFloaterTelehub::onClickRemoveSpawnPoint(void* data)
+{
+ if (!sInstance) return;
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(sInstance, "spawn_points_list");
+ if (!list) return;
+
+ S32 spawn_index = list->getFirstSelectedIndex();
+ if (spawn_index < 0) return; // nothing selected
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Could be god or estate owner. If neither, server will reject message.
+ if (gAgent.isGodlike())
+ {
+ msg->newMessage("GodlikeMessage");
+ }
+ else
+ {
+ msg->newMessage("EstateOwnerMessage");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "telehub");
+ msg->addUUID("Invoice", LLUUID::null);
+
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", "spawnpoint remove");
+
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d", spawn_index);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+
+ gAgent.sendReliableMessage();
+}
+
+// static
+void LLFloaterTelehub::processTelehubInfo(LLMessageSystem* msg, void**)
+{
+ if (sInstance)
+ {
+ sInstance->unpackTelehubInfo(msg);
+ }
+}
+
+void LLFloaterTelehub::unpackTelehubInfo(LLMessageSystem* msg)
+{
+ char buffer[MAX_STRING];
+
+ msg->getUUID("TelehubBlock", "ObjectID", mTelehubObjectID);
+ msg->getString("TelehubBlock", "ObjectName", MAX_STRING, buffer);
+ mTelehubObjectName = buffer;
+ msg->getVector3("TelehubBlock", "TelehubPos", mTelehubPos);
+ msg->getQuat("TelehubBlock", "TelehubRot", mTelehubRot);
+
+ mNumSpawn = msg->getNumberOfBlocks("SpawnPointBlock");
+ for (S32 i = 0; i < mNumSpawn; i++)
+ {
+ msg->getVector3("SpawnPointBlock", "SpawnPointPos", mSpawnPointPos[i], i);
+ }
+
+ // Update parts of the UI that change only when message received.
+
+ if (mTelehubObjectID.isNull())
+ {
+ childSetVisible("status_text_connected", false);
+ childSetVisible("status_text_not_connected", true);
+ childSetVisible("help_text_connected", false);
+ childSetVisible("help_text_not_connected", true);
+ }
+ else
+ {
+ childSetTextArg("status_text_connected", "[OBJECT]", mTelehubObjectName);
+ childSetVisible("status_text_connected", true);
+ childSetVisible("status_text_not_connected", false);
+ childSetVisible("help_text_connected", true);
+ childSetVisible("help_text_not_connected", false);
+ }
+
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "spawn_points_list");
+ if (list)
+ {
+ list->deleteAllItems();
+ for (S32 i = 0; i < mNumSpawn; i++)
+ {
+ LLString pos = llformat("%.1f, %.1f, %.1f",
+ mSpawnPointPos[i].mV[VX],
+ mSpawnPointPos[i].mV[VY],
+ mSpawnPointPos[i].mV[VZ]);
+ list->addSimpleItem(pos);
+ }
+ list->selectNthItem(mNumSpawn - 1);
+ }
+}
diff --git a/indra/newview/llfloatertelehub.h b/indra/newview/llfloatertelehub.h
new file mode 100644
index 0000000000..39f12f9443
--- /dev/null
+++ b/indra/newview/llfloatertelehub.h
@@ -0,0 +1,57 @@
+/**
+ * @file llfloatertelehub.h
+ * @author James Cook
+ * @brief LLFloaterTelehub class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTELEHUB_H
+#define LL_LLFLOATERTELEHUB_H
+
+#include "llfloater.h"
+
+class LLMessageSystem;
+
+const S32 MAX_SPAWNPOINTS_PER_TELEHUB = 16;
+
+class LLFloaterTelehub : public LLFloater
+{
+public:
+ // Opens the floater on screen.
+ static void show();
+
+ virtual void draw();
+
+ static BOOL renderBeacons();
+ static void addBeacons();
+
+private:
+ LLFloaterTelehub();
+ ~LLFloaterTelehub();
+
+ void refresh();
+ void sendTelehubInfoRequest();
+
+ static void onClickConnect(void* data);
+ static void onClickDisconnect(void* data);
+ static void onClickAddSpawnPoint(void* data);
+ static void onClickRemoveSpawnPoint(void* data);
+
+ static void processTelehubInfo(LLMessageSystem* msg, void**);
+ void unpackTelehubInfo(LLMessageSystem* msg);
+
+private:
+ LLUUID mTelehubObjectID; // null if no telehub
+ LLString mTelehubObjectName;
+ LLVector3 mTelehubPos; // region local, fallback if viewer can't see the object
+ LLQuaternion mTelehubRot;
+
+ S32 mNumSpawn;
+ LLVector3 mSpawnPointPos[MAX_SPAWNPOINTS_PER_TELEHUB];
+
+ static LLFloaterTelehub* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloatertools.cpp b/indra/newview/llfloatertools.cpp
new file mode 100644
index 0000000000..6f675236b3
--- /dev/null
+++ b/indra/newview/llfloatertools.cpp
@@ -0,0 +1,931 @@
+/**
+ * @file llfloatertools.cpp
+ * @brief The edit tools, including move, position, land, etc.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertools.h"
+
+#include "llfontgl.h"
+#include "llcoord.h"
+#include "llgl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldraghandle.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloateropenobject.h"
+#include "llfocusmgr.h"
+#include "llmenugl.h"
+#include "llpanelcontents.h"
+#include "llpanelface.h"
+#include "llpanelland.h"
+#include "llpanelinventory.h"
+#include "llpanelobject.h"
+#include "llpanelvolume.h"
+#include "llpanelpermissions.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltoolbrush.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolgrab.h"
+#include "lltoolindividual.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolpipette.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llvolumesliderctrl.h"
+#include "viewer.h"
+
+#include "llvieweruictrlfactory.h"
+
+// Globals
+LLFloaterTools *gFloaterTools = NULL;
+
+
+const LLString PANEL_NAMES[LLFloaterTools::PANEL_COUNT] =
+{
+ LLString("General"), // PANEL_GENERAL,
+ LLString("Object"), // PANEL_OBJECT,
+ LLString("Features"), // PANEL_FEATURES,
+ LLString("Texture"), // PANEL_FACE,
+ LLString("Content"), // PANEL_CONTENTS,
+};
+
+// Local prototypes
+void commit_select_tool(LLUICtrl *ctrl, void *data);
+void commit_select_component(LLUICtrl *ctrl, void *data);
+void click_show_more(void*);
+void click_popup_info(void*);
+void click_popup_done(void*);
+void click_popup_minimize(void*);
+void click_popup_grab_drag(LLUICtrl *, void*);
+void click_popup_grab_lift(LLUICtrl *, void*);
+void click_popup_grab_spin(LLUICtrl *, void*);
+void click_popup_rotate_left(void*);
+void click_popup_rotate_reset(void*);
+void click_popup_rotate_right(void*);
+void click_popup_dozer_mode(LLUICtrl *, void *user);
+void click_popup_dozer_size(LLUICtrl *, void *user);
+void click_dozer_size(LLUICtrl *, void*);
+void click_apply_to_selection(void*);
+void commit_radio_zoom(LLUICtrl *, void*);
+void commit_radio_orbit(LLUICtrl *, void*);
+void commit_radio_pan(LLUICtrl *, void*);
+void commit_grid_mode(LLUICtrl *, void*);
+void commit_slider_zoom(LLUICtrl *, void*);
+
+
+//static
+void* LLFloaterTools::createPanelPermissions(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelPermissions = new LLPanelPermissions("General");
+ return floater->mPanelPermissions;
+}
+//static
+void* LLFloaterTools::createPanelObject(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelObject = new LLPanelObject("Object");
+ return floater->mPanelObject;
+}
+
+//static
+void* LLFloaterTools::createPanelVolume(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelVolume = new LLPanelVolume("Features");
+ return floater->mPanelVolume;
+}
+
+//static
+void* LLFloaterTools::createPanelFace(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelFace = new LLPanelFace("Texture");
+ return floater->mPanelFace;
+}
+
+//static
+void* LLFloaterTools::createPanelContents(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelContents = new LLPanelContents("Contents");
+ return floater->mPanelContents;
+}
+
+//static
+void* LLFloaterTools::createPanelContentsInventory(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelContents->mPanelInventory = new LLPanelInventory("ContentsInventory", LLRect());
+ return floater->mPanelContents->mPanelInventory;
+}
+
+//static
+void* LLFloaterTools::createPanelLandInfo(void* data)
+{
+ LLFloaterTools* floater = (LLFloaterTools*)data;
+ floater->mPanelLandInfo = new LLPanelLandInfo("land info panel");
+ return floater->mPanelLandInfo;
+}
+
+BOOL LLFloaterTools::postBuild()
+{
+
+ // Hide until tool selected
+ setVisible(FALSE);
+
+ // Since we constantly show and hide this during drags, don't
+ // make sounds on visibility changes.
+ setSoundFlags(LLView::SILENT);
+
+ mDragHandle->setEnabled( !gSavedSettings.getBOOL("ToolboxAutoMove") );
+
+ LLRect rect;
+ mBtnFocus = LLUICtrlFactory::getButtonByName(this,"button focus");//btn;
+ childSetAction("button focus",select_tool, (void*)gToolCamera);
+ mBtnMove = LLUICtrlFactory::getButtonByName(this,"button move");
+ childSetAction("button move",select_tool, (void*)gToolGrab);
+ mBtnEdit = LLUICtrlFactory::getButtonByName(this,"button edit");
+ childSetAction("button edit",select_tool, (void*)gToolTranslate);
+ mBtnCreate = LLUICtrlFactory::getButtonByName(this,"button create");
+ childSetAction("button create",select_tool, (void*)gToolCreate);
+ mBtnLand = LLUICtrlFactory::getButtonByName(this, "button land" );
+ childSetAction("button land",select_tool, (void*)gToolParcel);
+ mTextStatus = LLUICtrlFactory::getTextBoxByName(this,"text status");
+ mRadioZoom = LLUICtrlFactory::getCheckBoxByName(this,"radio zoom");
+ mSliderZoom = LLViewerUICtrlFactory::getVolumeSliderByName(this,"slider zoom");
+ childSetCommitCallback("slider zoom",commit_slider_zoom,this);
+ mRadioOrbit = LLUICtrlFactory::getCheckBoxByName(this,"radio orbit");
+ childSetCommitCallback("radio orbit",commit_radio_orbit,this);
+ mRadioPan = LLUICtrlFactory::getCheckBoxByName(this,"radio pan");
+ childSetCommitCallback("radio pan",commit_radio_pan,this);
+ mRadioMove = LLUICtrlFactory::getCheckBoxByName(this,"radio move");
+ childSetCommitCallback("radio move",click_popup_grab_drag,this);
+ mRadioLift = LLUICtrlFactory::getCheckBoxByName(this,"radio lift");
+ childSetCommitCallback("radio lift",click_popup_grab_lift,this);
+ mRadioSpin = LLUICtrlFactory::getCheckBoxByName(this,"radio spin");
+ childSetCommitCallback("radio spin",click_popup_grab_spin,NULL);
+ mRadioPosition = LLUICtrlFactory::getCheckBoxByName(this,"radio position");
+ childSetCommitCallback("radio position",commit_select_tool,gToolTranslate);
+ mRadioRotate = LLUICtrlFactory::getCheckBoxByName(this,"radio rotate");
+ childSetCommitCallback("radio rotate",commit_select_tool,gToolRotate);
+ mRadioStretch = LLUICtrlFactory::getCheckBoxByName(this,"radio stretch");
+ childSetCommitCallback("radio stretch",commit_select_tool,gToolStretch);
+ mRadioSelectFace = LLUICtrlFactory::getCheckBoxByName(this,"radio select face");
+ childSetCommitCallback("radio select face",commit_select_tool,gToolFace);
+ mCheckSelectIndividual = LLUICtrlFactory::getCheckBoxByName(this,"checkbox edit linked parts");
+ childSetValue("checkbox edit linked parts",(BOOL)!gSavedSettings.getBOOL("SelectLinkedSet"));
+ childSetCommitCallback("checkbox edit linked parts",commit_select_component,this);
+ mCheckSnapToGrid = LLUICtrlFactory::getCheckBoxByName(this,"checkbox snap to grid");
+ childSetValue("checkbox snap to grid",(BOOL)gSavedSettings.getBOOL("SnapEnabled"));
+ mBtnGridOptions = LLUICtrlFactory::getButtonByName(this,"Options...");
+ childSetAction("Options...",onClickGridOptions, this);
+ mCheckStretchUniform = LLUICtrlFactory::getCheckBoxByName(this,"checkbox uniform");
+ childSetValue("checkbox uniform",(BOOL)gSavedSettings.getBOOL("ScaleUniform"));
+ mCheckStretchTexture = LLUICtrlFactory::getCheckBoxByName(this,"checkbox stretch textures");
+ childSetValue("checkbox stretch textures",(BOOL)gSavedSettings.getBOOL("ScaleStretchTextures"));
+ mTextGridMode = LLUICtrlFactory::getTextBoxByName(this,"text ruler mode");
+ mComboGridMode = LLUICtrlFactory::getComboBoxByName(this,"combobox grid mode");
+ childSetCommitCallback("combobox grid mode",commit_grid_mode, this);
+ //
+ // Create Buttons
+ //
+
+ static const LLString toolNames[]={
+ "ToolCube",
+ "ToolPrism",
+ "ToolPyramid",
+ "ToolTetrahedron",
+ "ToolCylinder",
+ "ToolHemiCylinder",
+ "ToolCone",
+ "ToolHemiCone",
+ "ToolSphere",
+ "ToolHemiSphere",
+ "ToolTorus",
+ "ToolTube",
+ "ToolRing",
+ "ToolTree",
+ "ToolGrass"};
+ void* toolData[]={
+ &LLToolPlacerPanel::sCube,
+ &LLToolPlacerPanel::sPrism,
+ &LLToolPlacerPanel::sPyramid,
+ &LLToolPlacerPanel::sTetrahedron,
+ &LLToolPlacerPanel::sCylinder,
+ &LLToolPlacerPanel::sCylinderHemi,
+ &LLToolPlacerPanel::sCone,
+ &LLToolPlacerPanel::sConeHemi,
+ &LLToolPlacerPanel::sSphere,
+ &LLToolPlacerPanel::sSphereHemi,
+ &LLToolPlacerPanel::sTorus,
+ &LLToolPlacerPanel::sSquareTorus,
+ &LLToolPlacerPanel::sTriangleTorus,
+ &LLToolPlacerPanel::sTree,
+ &LLToolPlacerPanel::sGrass};
+ for(int t=0;t<sizeof(toolNames)/sizeof(toolNames[0]);t++)
+ {
+ LLButton *found = LLViewerUICtrlFactory::getButtonByName(this,toolNames[t]);
+ if(found)
+ {
+ found->setClickedCallback(setObjectType,toolData[t]);
+ mButtons.push_back( found );
+ }else{
+ llwarns << "Tool button not found! DOA Pending." << llendl;
+ }
+ }
+ mCheckCopySelection = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy selection");
+ childSetValue("checkbox copy selection",(BOOL)gSavedSettings.getBOOL("CreateToolCopySelection"));
+ mCheckSticky = LLUICtrlFactory::getCheckBoxByName(this,"checkbox sticky");
+ childSetValue("checkbox sticky",(BOOL)gSavedSettings.getBOOL("CreateToolKeepSelected"));
+ mCheckCopyCenters = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy centers");
+ childSetValue("checkbox copy centers",(BOOL)gSavedSettings.getBOOL("CreateToolCopyCenters"));
+ mCheckCopyRotates = LLUICtrlFactory::getCheckBoxByName(this,"checkbox copy rotates");
+ childSetValue("checkbox copy rotates",(BOOL)gSavedSettings.getBOOL("CreateToolCopyRotates"));
+ mRadioSelectLand = LLUICtrlFactory::getCheckBoxByName(this,"radio select land");
+ childSetCommitCallback("radio select land",commit_select_tool, gToolParcel);
+ mRadioDozerFlatten = LLUICtrlFactory::getCheckBoxByName(this,"radio flatten");
+ childSetCommitCallback("radio flatten",click_popup_dozer_mode, (void*)0);
+ mRadioDozerRaise = LLUICtrlFactory::getCheckBoxByName(this,"radio raise");
+ childSetCommitCallback("radio raise",click_popup_dozer_mode, (void*)1);
+ mRadioDozerLower = LLUICtrlFactory::getCheckBoxByName(this,"radio lower");
+ childSetCommitCallback("radio lower",click_popup_dozer_mode, (void*)2);
+ mRadioDozerSmooth = LLUICtrlFactory::getCheckBoxByName(this,"radio smooth");
+ childSetCommitCallback("radio smooth",click_popup_dozer_mode, (void*)3);
+ mRadioDozerNoise = LLUICtrlFactory::getCheckBoxByName(this,"radio noise");
+ childSetCommitCallback("radio noise",click_popup_dozer_mode, (void*)4);
+ mRadioDozerRevert = LLUICtrlFactory::getCheckBoxByName(this,"radio revert");
+ childSetCommitCallback("radio revert",click_popup_dozer_mode, (void*)5);
+ mComboDozerSize = LLUICtrlFactory::getComboBoxByName(this,"combobox brush size");
+ childSetCommitCallback("combobox brush size",click_dozer_size, (void*)0);
+ if(mComboDozerSize) mComboDozerSize->setCurrentByIndex(0);
+ mBtnApplyToSelection = LLUICtrlFactory::getButtonByName(this,"button apply to selection");
+ childSetAction("button apply to selection",click_apply_to_selection, (void*)0);
+ mCheckShowOwners = LLUICtrlFactory::getCheckBoxByName(this,"checkbox show owners");
+ childSetValue("checkbox show owners",gSavedSettings.getBOOL("ShowParcelOwners"));
+ childSetAction("button more", click_show_more, this);
+ childSetAction("button less", click_show_more, this);
+ mTab = LLUICtrlFactory::getTabContainerByName(this,"Object Info Tabs");
+ if(mTab)
+ {
+ mTab->setVisible( gSavedSettings.getBOOL("ToolboxShowMore") );
+ mTab->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
+ mTab->setVisible( gSavedSettings.getBOOL("ToolboxShowMore") );
+ mTab->setBorderVisible(FALSE);
+ }
+ mTab->selectFirstTab();
+ return TRUE;
+}
+
+// Create the popupview with a dummy center. It will be moved into place
+// during LLViewerWindow's per-frame hover processing.
+LLFloaterTools::LLFloaterTools()
+: LLFloater("toolbox floater"),
+ mBtnFocus(NULL),
+ mBtnMove(NULL),
+ mBtnEdit(NULL),
+ mBtnCreate(NULL),
+ mBtnLand(NULL),
+ mTextStatus(NULL),
+
+ mRadioOrbit(NULL),
+ mRadioZoom(NULL),
+ mRadioPan(NULL),
+
+ mRadioMove(NULL),
+ mRadioLift(NULL),
+ mRadioSpin(NULL),
+
+ mRadioPosition(NULL),
+ mRadioRotate(NULL),
+ mRadioStretch(NULL),
+ mRadioSelectFace(NULL),
+ mCheckSelectIndividual(NULL),
+
+ mCheckSnapToGrid(NULL),
+ mBtnGridOptions(NULL),
+ mTextGridMode(NULL),
+ mComboGridMode(NULL),
+ mCheckStretchUniform(NULL),
+ mCheckStretchTexture(NULL),
+
+ mBtnRotateLeft(NULL),
+ mBtnRotateReset(NULL),
+ mBtnRotateRight(NULL),
+
+ mBtnDelete(NULL),
+ mBtnDuplicate(NULL),
+ mBtnDuplicateInPlace(NULL),
+
+ mCheckSticky(NULL),
+ mCheckCopySelection(NULL),
+ mCheckCopyCenters(NULL),
+ mCheckCopyRotates(NULL),
+ mRadioSelectLand(NULL),
+ mRadioDozerFlatten(NULL),
+ mRadioDozerRaise(NULL),
+ mRadioDozerLower(NULL),
+ mRadioDozerSmooth(NULL),
+ mRadioDozerNoise(NULL),
+ mRadioDozerRevert(NULL),
+ mComboDozerSize(NULL),
+ mBtnApplyToSelection(NULL),
+ mCheckShowOwners(NULL),
+
+
+ mTab(NULL),
+ mPanelPermissions(NULL),
+ mPanelObject(NULL),
+ mPanelVolume(NULL),
+ mPanelContents(NULL),
+ mPanelFace(NULL),
+ mPanelLandInfo(NULL),
+
+ mTabLand(NULL),
+ mDirty(TRUE)
+{
+ mAutoFocus = FALSE;
+ LLCallbackMap::map_t factory_map;
+ factory_map["General"] = LLCallbackMap(createPanelPermissions, this);//LLPanelPermissions
+ factory_map["Object"] = LLCallbackMap(createPanelObject, this);//LLPanelObject
+ factory_map["Features"] = LLCallbackMap(createPanelVolume, this);//LLPanelVolume
+ factory_map["Texture"] = LLCallbackMap(createPanelFace, this);//LLPanelFace
+ factory_map["Contents"] = LLCallbackMap(createPanelContents, this);//LLPanelContents
+ factory_map["ContentsInventory"] = LLCallbackMap(createPanelContentsInventory, this);//LLPanelContents
+ factory_map["land info panel"] = LLCallbackMap(createPanelLandInfo, this);//LLPanelLandInfo
+
+ gUICtrlFactory->buildFloater(this,"floater_tools.xml",&factory_map);
+
+ mLargeHeight = getRect().getHeight();
+ mSmallHeight = mLargeHeight;
+ if (mTab) mSmallHeight -= mTab->getRect().getHeight();
+
+ gSavedSettings.setBOOL("ToolboxShowMore", TRUE); // force a toggle initially
+ showMore(FALSE);
+}
+
+LLFloaterTools::~LLFloaterTools()
+{
+ showMore(FALSE);
+ // children automatically deleted
+}
+
+
+void LLFloaterTools::setStatusText(const LLString& text)
+{
+ mTextStatus->setText(text);
+}
+
+void LLFloaterTools::refresh()
+{
+ const S32 INFO_WIDTH = mRect.getWidth();
+ const S32 INFO_HEIGHT = 384;
+ LLRect object_info_rect(0, 0, INFO_WIDTH, -INFO_HEIGHT);
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+
+ S32 idx_features = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_FEATURES]);
+ S32 idx_face = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_FACE]);
+ S32 idx_contents = mTab->getPanelIndexByTitle(PANEL_NAMES[PANEL_CONTENTS]);
+
+ S32 selected_index = mTab->getCurrentPanelIndex();
+
+ if (!all_volume && (selected_index == idx_features || selected_index == idx_face ||
+ selected_index == idx_contents))
+ {
+ mTab->selectFirstTab();
+ }
+
+ mTab->enableTabButton(idx_features, all_volume);
+ mTab->enableTabButton(idx_face, all_volume);
+ mTab->enableTabButton(idx_contents, all_volume);
+
+ mPanelPermissions->refresh();
+ mPanelObject->refresh();
+ mPanelVolume->refresh();
+ mPanelFace->refresh();
+ mPanelContents->refresh();
+ mPanelLandInfo->refresh();
+}
+
+void LLFloaterTools::draw()
+{
+ if (mDirty)
+ {
+ refresh();
+ mDirty = FALSE;
+ }
+
+ mCheckSelectIndividual->set(!gSavedSettings.getBOOL("SelectLinkedSet"));
+ LLFloater::draw();
+}
+
+void LLFloaterTools::dirty()
+{
+ mDirty = TRUE;
+ LLFloaterOpenObject::dirty();
+}
+
+// Clean up any tool state that should not persist when the
+// floater is closed.
+void LLFloaterTools::resetToolState()
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = FALSE;
+
+ gGrabBtnSpin = FALSE;
+ gGrabBtnVertical = FALSE;
+}
+
+void LLFloaterTools::updatePopup(LLCoordGL center, MASK mask)
+{
+ LLTool *tool = gToolMgr->getCurrentTool( mask );
+
+ // HACK to allow seeing the buttons when you have the app in a window.
+ // Keep the visibility the same as it
+ if (tool == gToolNull)
+ {
+ return;
+ }
+
+ // Focus buttons
+ BOOL focus_visible = ( tool == gToolCamera );
+
+ mBtnFocus ->setToggleState( focus_visible );
+
+ mRadioZoom ->setVisible( focus_visible );
+ mRadioOrbit ->setVisible( focus_visible );
+ mRadioPan ->setVisible( focus_visible );
+ mSliderZoom ->setVisible( focus_visible );
+
+ mRadioZoom ->set( !gCameraBtnOrbit &&
+ !gCameraBtnPan &&
+ !(mask == MASK_ORBIT) &&
+ !(mask == (MASK_ORBIT | MASK_ALT)) &&
+ !(mask == MASK_PAN) &&
+ !(mask == (MASK_PAN | MASK_ALT)) );
+
+ mRadioOrbit ->set( gCameraBtnOrbit ||
+ (mask == MASK_ORBIT) ||
+ (mask == (MASK_ORBIT | MASK_ALT)) );
+
+ mRadioPan ->set( gCameraBtnPan ||
+ (mask == MASK_PAN) ||
+ (mask == (MASK_PAN | MASK_ALT)) );
+
+ // multiply by correction factor because volume sliders go [0, 0.5]
+ mSliderZoom ->setValue( gAgent.getCameraZoomFraction() * 0.5f);
+
+ // Move buttons
+ BOOL move_visible = (tool == gToolGrab);
+
+ if (mBtnMove) mBtnMove ->setToggleState( move_visible );
+
+ // HACK - highlight buttons for next click
+ if (mRadioMove)
+ {
+ mRadioMove ->setVisible( move_visible );
+ mRadioMove ->set( !gGrabBtnSpin &&
+ !gGrabBtnVertical &&
+ !(mask == MASK_VERTICAL) &&
+ !(mask == MASK_SPIN) );
+ }
+
+ if (mRadioLift)
+ {
+ mRadioLift ->setVisible( move_visible );
+ mRadioLift ->set( gGrabBtnVertical ||
+ (mask == MASK_VERTICAL) );
+ }
+
+ if (mRadioSpin)
+ {
+ mRadioSpin ->setVisible( move_visible );
+ mRadioSpin ->set( gGrabBtnSpin ||
+ (mask == MASK_SPIN) );
+ }
+
+ // Edit buttons
+ BOOL edit_visible = tool == gToolTranslate ||
+ tool == gToolRotate ||
+ tool == gToolStretch ||
+ tool == gToolFace ||
+ tool == gToolIndividual ||
+ tool == gToolPipette;
+
+ mBtnEdit ->setToggleState( edit_visible );
+
+ mRadioPosition ->setVisible( edit_visible );
+ mRadioRotate ->setVisible( edit_visible );
+ mRadioStretch ->setVisible( edit_visible );
+ if (mRadioSelectFace)
+ {
+ mRadioSelectFace->setVisible( edit_visible );
+ mRadioSelectFace->set( tool == gToolFace );
+ }
+
+ if (mCheckSelectIndividual)
+ {
+ mCheckSelectIndividual->setVisible(edit_visible);
+ mCheckSelectIndividual->set(!gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ mRadioPosition ->set( tool == gToolTranslate );
+ mRadioRotate ->set( tool == gToolRotate );
+ mRadioStretch ->set( tool == gToolStretch );
+
+ if (mComboGridMode)
+ {
+ mComboGridMode ->setVisible( edit_visible );
+ S32 index = mComboGridMode->getCurrentIndex();
+ mComboGridMode->removeall();
+
+ switch (gSelectMgr->getSelectType())
+ {
+ case SELECT_TYPE_HUD:
+ mComboGridMode->add("Screen");
+ mComboGridMode->add("Local");
+ //mComboGridMode->add("Reference");
+ break;
+ case SELECT_TYPE_WORLD:
+ mComboGridMode->add("World");
+ mComboGridMode->add("Local");
+ mComboGridMode->add("Reference");
+ break;
+ case SELECT_TYPE_ATTACHMENT:
+ mComboGridMode->add("Attachment");
+ mComboGridMode->add("Local");
+ mComboGridMode->add("Reference");
+ break;
+ }
+
+ mComboGridMode->setCurrentByIndex(index);
+ }
+ if (mTextGridMode) mTextGridMode->setVisible( edit_visible );
+
+ // Snap to grid disabled for grab tool - very confusing
+ if (mCheckSnapToGrid) mCheckSnapToGrid->setVisible( edit_visible /* || tool == gToolGrab */ );
+ if (mBtnGridOptions) mBtnGridOptions->setVisible( edit_visible /* || tool == gToolGrab */ );
+
+ //mCheckSelectLinked ->setVisible( edit_visible );
+ if (mCheckStretchUniform) mCheckStretchUniform->setVisible( edit_visible );
+ if (mCheckStretchTexture) mCheckStretchTexture->setVisible( edit_visible );
+
+ // Create buttons
+ BOOL create_visible = (tool == gToolCreate);
+
+ mBtnCreate ->setToggleState( tool == gToolCreate );
+
+ if (mCheckCopySelection
+ && mCheckCopySelection->get())
+ {
+ // don't highlight any placer button
+ for (std::vector<LLButton*>::size_type i = 0; i < mButtons.size(); i++)
+ {
+ mButtons[i]->setToggleState(FALSE);
+ mButtons[i]->setVisible( create_visible );
+ }
+ }
+ else
+ {
+ // Highlight the correct placer button
+ for( std::vector<LLButton*>::size_type i = 0; i < mButtons.size(); i++ )
+ {
+ LLPCode pcode = LLToolPlacer::getObjectType();
+ void *userdata = mButtons[i]->getCallbackUserData();
+ LLPCode *cur = (LLPCode*) userdata;
+
+ BOOL state = (pcode == *cur);
+ mButtons[i]->setToggleState( state );
+ mButtons[i]->setVisible( create_visible );
+ }
+ }
+
+ if (mCheckSticky) mCheckSticky ->setVisible( create_visible );
+ if (mCheckCopySelection) mCheckCopySelection ->setVisible( create_visible );
+ if (mCheckCopyCenters) mCheckCopyCenters ->setVisible( create_visible );
+ if (mCheckCopyRotates) mCheckCopyRotates ->setVisible( create_visible );
+
+ if (mCheckCopyCenters) mCheckCopyCenters->setEnabled( mCheckCopySelection->get() );
+ if (mCheckCopyRotates) mCheckCopyRotates->setEnabled( mCheckCopySelection->get() );
+
+ // Land buttons
+ BOOL land_visible = (tool == gToolLand || tool == gToolParcel );
+
+ if (mBtnLand) mBtnLand ->setToggleState( land_visible );
+
+ // mRadioEditLand ->set( tool == gToolLand );
+ if (mRadioSelectLand) mRadioSelectLand->set( tool == gToolParcel );
+
+ // mRadioEditLand ->setVisible( land_visible );
+ if (mRadioSelectLand) mRadioSelectLand->setVisible( land_visible );
+
+ S32 dozer_mode = gSavedSettings.getS32("RadioLandBrushAction");
+ S32 dozer_size = gSavedSettings.getS32("RadioLandBrushSize");
+
+ if (mRadioDozerFlatten)
+ {
+ mRadioDozerFlatten ->set( tool == gToolLand && dozer_mode == 0);
+ mRadioDozerFlatten ->setVisible( land_visible );
+ }
+ if (mRadioDozerRaise)
+ {
+ mRadioDozerRaise ->set( tool == gToolLand && dozer_mode == 1);
+ mRadioDozerRaise ->setVisible( land_visible );
+ }
+ if (mRadioDozerLower)
+ {
+ mRadioDozerLower ->set( tool == gToolLand && dozer_mode == 2);
+ mRadioDozerLower ->setVisible( land_visible );
+ }
+ if (mRadioDozerSmooth)
+ {
+ mRadioDozerSmooth ->set( tool == gToolLand && dozer_mode == 3);
+ mRadioDozerSmooth ->setVisible( land_visible );
+ }
+ if (mRadioDozerNoise)
+ {
+ mRadioDozerNoise ->set( tool == gToolLand && dozer_mode == 4);
+ mRadioDozerNoise ->setVisible( land_visible );
+ }
+ if (mRadioDozerRevert)
+ {
+ mRadioDozerRevert ->set( tool == gToolLand && dozer_mode == 5);
+ mRadioDozerRevert ->setVisible( land_visible );
+ }
+ if (mComboDozerSize)
+ {
+ mComboDozerSize ->setCurrentByIndex(dozer_size);
+ mComboDozerSize ->setVisible( land_visible );
+ mComboDozerSize ->setEnabled( tool == gToolLand );
+ }
+ if (mBtnApplyToSelection)
+ {
+ mBtnApplyToSelection->setVisible( land_visible );
+ mBtnApplyToSelection->setEnabled( land_visible && !gParcelMgr->selectionEmpty() && tool != gToolParcel);
+ }
+ if (mCheckShowOwners)
+ {
+ mCheckShowOwners ->setVisible( land_visible );
+ }
+
+ //
+ // More panel visibility
+ //
+ BOOL show_more = gSavedSettings.getBOOL("ToolboxShowMore");
+
+ mTab->setVisible(show_more && tool != gToolLand && tool != gToolParcel);
+ mPanelLandInfo->setVisible(show_more && (tool == gToolLand || tool == gToolParcel));
+}
+
+
+// virtual
+BOOL LLFloaterTools::canClose()
+{
+ // don't close when quitting, so camera will stay put
+ return !gQuit;
+}
+
+// virtual
+void LLFloaterTools::onClose(bool app_quitting)
+{
+ setMinimized(FALSE);
+ setVisible(FALSE);
+ mTab->setVisible(FALSE);
+
+ // Different from handle_reset_view in that it doesn't actually
+ // move the camera if EditCameraMovement is not set.
+ gAgent.resetView(gSavedSettings.getBOOL("EditCameraMovement"));
+
+ // exit component selection mode
+ gSelectMgr->promoteSelectionToRoot();
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+
+ gViewerWindow->showCursor();
+
+ resetToolState();
+
+ // Switch back to basic toolset
+ gCurrentToolset = gBasicToolset;
+ gBasicToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gBasicToolset );
+}
+
+void LLFloaterTools::showMore(BOOL show_more)
+{
+ BOOL showing_more = gSavedSettings.getBOOL("ToolboxShowMore");
+ if (show_more == showing_more)
+ {
+ return;
+ }
+
+ gSavedSettings.setBOOL("ToolboxShowMore", show_more);
+
+ // Visibility updated next frame - JC
+ // mTab->setVisible(show_more);
+
+ if (show_more)
+ {
+ reshape( mRect.getWidth(), mLargeHeight, TRUE);
+ translate( 0, mSmallHeight - mLargeHeight );
+ childSetVisible("button less", true);
+ childSetVisible("button more", false);
+ }
+ else
+ {
+ reshape( mRect.getWidth(), mSmallHeight, TRUE);
+ translate( 0, mLargeHeight - mSmallHeight );
+ childSetVisible("button less", false);
+ childSetVisible("button more", true);
+ }
+}
+
+void LLFloaterTools::showPanel(EInfoPanel panel)
+{
+ llassert(panel >= 0 && panel < PANEL_COUNT);
+ mTab->selectTabByName(PANEL_NAMES[panel]);
+ showMore(TRUE);
+}
+
+void click_show_more(void *userdata)
+{
+ LLFloaterTools *f = (LLFloaterTools *)userdata;
+ BOOL show_more = !gSavedSettings.getBOOL("ToolboxShowMore");
+ f->showMore( show_more );
+}
+
+void click_popup_info(void*)
+{
+// gBuildView->setPropertiesPanelOpen(TRUE);
+}
+
+void click_popup_done(void*)
+{
+ handle_reset_view();
+}
+
+void click_popup_grab_drag(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = FALSE;
+}
+
+void click_popup_grab_lift(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = TRUE;
+ gGrabBtnSpin = FALSE;
+}
+
+void click_popup_grab_spin(LLUICtrl*, void*)
+{
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = TRUE;
+}
+
+void commit_radio_zoom(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = FALSE;
+}
+
+void commit_radio_orbit(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = TRUE;
+ gCameraBtnPan = FALSE;
+}
+
+void commit_radio_pan(LLUICtrl *, void*)
+{
+ gCameraBtnOrbit = FALSE;
+ gCameraBtnPan = TRUE;
+}
+
+void commit_slider_zoom(LLUICtrl *ctrl, void*)
+{
+ LLVolumeSliderCtrl* slider = (LLVolumeSliderCtrl*)ctrl;
+ // renormalize value, since max "volume" level is 0.5 for some reason
+ F32 zoom_level = (F32)slider->getValue().asReal() * 2.f; // / 0.5f;
+ gAgent.setCameraZoomFraction(zoom_level);
+}
+
+void click_popup_rotate_left(void*)
+{
+ gSelectMgr->selectionRotateAroundZ( 45.f );
+ dialog_refresh_all();
+}
+
+void click_popup_rotate_reset(void*)
+{
+ gSelectMgr->selectionResetRotation();
+ dialog_refresh_all();
+}
+
+void click_popup_rotate_right(void*)
+{
+ gSelectMgr->selectionRotateAroundZ( -45.f );
+ dialog_refresh_all();
+}
+
+
+void click_popup_dozer_mode(LLUICtrl *, void *user)
+{
+ S32 show_owners = gSavedSettings.getBOOL("ShowParcelOwners");
+ S32 mode = (S32)(intptr_t) user;
+ select_tool( gToolLand );
+ gSavedSettings.setS32("RadioLandBrushAction", mode);
+ gSavedSettings.setBOOL("ShowParcelOwners", show_owners);
+}
+
+void click_popup_dozer_size(LLUICtrl *, void *user)
+{
+ S32 size = (S32)(intptr_t) user;
+ gSavedSettings.setS32("RadioLandBrushSize", size);
+}
+
+void click_dozer_size(LLUICtrl *ctrl, void *user)
+{
+ S32 size = ((LLComboBox*) ctrl)->getCurrentIndex();
+ gSavedSettings.setS32("RadioLandBrushSize", size);
+}
+
+void click_apply_to_selection(void* user)
+{
+ gToolLand->modifyLandInSelectionGlobal();
+}
+
+void commit_select_tool(LLUICtrl *ctrl, void *data)
+{
+ S32 show_owners = gSavedSettings.getBOOL("ShowParcelOwners");
+ select_tool(data);
+ gSavedSettings.setBOOL("ShowParcelOwners", show_owners);
+}
+
+void commit_select_component(LLUICtrl *ctrl, void *data)
+{
+ LLFloaterTools* floaterp = (LLFloaterTools*)data;
+
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(floaterp))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ BOOL select_individuals = floaterp->mCheckSelectIndividual->get();
+ gSavedSettings.setBOOL("SelectLinkedSet", !select_individuals);
+ floaterp->dirty();
+
+ if (select_individuals)
+ {
+ gSelectMgr->demoteSelectionToIndividuals();
+ }
+ else
+ {
+ gSelectMgr->promoteSelectionToRoot();
+ }
+}
+
+void commit_grid_mode(LLUICtrl *ctrl, void *data)
+{
+ LLComboBox* combo = (LLComboBox*)ctrl;
+
+ gSelectMgr->setGridMode((EGridMode)combo->getCurrentIndex());
+}
+
+// static
+void LLFloaterTools::setObjectType( void* data )
+{
+ LLPCode pcode = *(LLPCode*) data;
+ LLToolPlacer::setObjectType( pcode );
+ gSavedSettings.setBOOL("CreateToolCopySelection", FALSE);
+ gViewerWindow->setMouseCapture(NULL, NULL);
+}
+
+// static
+void LLFloaterTools::onClickGridOptions(void* data)
+{
+ //LLFloaterTools* floaterp = (LLFloaterTools*)data;
+ LLFloaterBuildOptions::show(NULL);
+ // RN: this makes grid options dependent on build tools window
+ //floaterp->addDependentFloater(LLFloaterBuildOptions::getInstance(), FALSE);
+}
diff --git a/indra/newview/llfloatertools.h b/indra/newview/llfloatertools.h
new file mode 100644
index 0000000000..092a8d7715
--- /dev/null
+++ b/indra/newview/llfloatertools.h
@@ -0,0 +1,164 @@
+/**
+ * @file llfloatertools.h
+ * @brief The edit tools, including move, position, land, etc.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOOLS_H
+#define LL_LLFLOATERTOOLS_H
+
+#include "llfloater.h"
+#include "llcoord.h"
+
+class LLButton;
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLTabContainer;
+class LLPanelPermissions;
+class LLPanelObject;
+class LLPanelVolume;
+class LLPanelContents;
+class LLPanelFace;
+class LLPanelLandInfo;
+class LLComboBox;
+class LLVolumeSliderCtrl;
+
+class LLFloaterTools
+: public LLFloater
+{
+public:
+ virtual BOOL postBuild();
+ static void* createPanelPermissions(void* vdata);
+ static void* createPanelObject(void* vdata);
+ static void* createPanelVolume(void* vdata);
+ static void* createPanelFace(void* vdata);
+ static void* createPanelContents(void* vdata);
+ static void* createPanelContentsInventory(void* vdata);
+ static void* createPanelLandInfo(void* vdata);
+
+ LLFloaterTools();
+ virtual ~LLFloaterTools();
+
+ virtual void onClose(bool app_quitting);
+ virtual BOOL canClose();
+
+ // call this once per frame to handle visibility, rect location,
+ // button highlights, etc.
+ void updatePopup(LLCoordGL center, MASK mask);
+
+ // When the floater is going away, reset any options that need to be
+ // cleared.
+ void resetToolState();
+
+ enum EInfoPanel
+ {
+ PANEL_GENERAL=0,
+ PANEL_OBJECT,
+ PANEL_FEATURES,
+ PANEL_FACE,
+ PANEL_CONTENTS,
+ PANEL_COUNT
+ };
+
+ /*virtual*/ void draw();
+
+ void dirty();
+ void showMore(BOOL show_more);
+ void showPanel(EInfoPanel panel);
+
+ void setStatusText(const LLString& text);
+
+private:
+ static void setObjectType( void* data );
+ void refresh();
+
+ static void onClickGridOptions(void* data);
+
+public:
+
+ LLButton *mBtnFocus;
+ LLButton *mBtnMove;
+ LLButton *mBtnEdit;
+ LLButton *mBtnCreate;
+ LLButton *mBtnLand;
+
+ LLTextBox *mTextStatus;
+
+ // Focus buttons
+ LLCheckBoxCtrl *mRadioOrbit;
+ LLCheckBoxCtrl *mRadioZoom;
+ LLCheckBoxCtrl *mRadioPan;
+ LLVolumeSliderCtrl *mSliderZoom;
+
+ // Move buttons
+ LLCheckBoxCtrl *mRadioMove;
+ LLCheckBoxCtrl *mRadioLift;
+ LLCheckBoxCtrl *mRadioSpin;
+
+ // Edit buttons
+ LLCheckBoxCtrl *mRadioPosition;
+ LLCheckBoxCtrl *mRadioRotate;
+ LLCheckBoxCtrl *mRadioStretch;
+ LLCheckBoxCtrl *mRadioSelectFace;
+
+ LLCheckBoxCtrl *mCheckSelectIndividual;
+
+ LLCheckBoxCtrl* mCheckSnapToGrid;
+ LLButton* mBtnGridOptions;
+ LLTextBox* mTextGridMode;
+ LLComboBox* mComboGridMode;
+ LLCheckBoxCtrl* mCheckStretchUniform;
+ LLCheckBoxCtrl* mCheckStretchTexture;
+
+ LLButton *mBtnRotateLeft;
+ LLButton *mBtnRotateReset;
+ LLButton *mBtnRotateRight;
+
+ LLButton *mBtnDelete;
+ LLButton *mBtnDuplicate;
+ LLButton *mBtnDuplicateInPlace;
+
+ // Create buttons
+ LLCheckBoxCtrl *mCheckSticky;
+ LLCheckBoxCtrl *mCheckCopySelection;
+ LLCheckBoxCtrl *mCheckCopyCenters;
+ LLCheckBoxCtrl *mCheckCopyRotates;
+
+ // Land buttons
+// LLCheckBoxCtrl *mRadioEditLand;
+ LLCheckBoxCtrl *mRadioSelectLand;
+
+ LLCheckBoxCtrl *mRadioDozerFlatten;
+ LLCheckBoxCtrl *mRadioDozerRaise;
+ LLCheckBoxCtrl *mRadioDozerLower;
+ LLCheckBoxCtrl *mRadioDozerSmooth;
+ LLCheckBoxCtrl *mRadioDozerNoise;
+ LLCheckBoxCtrl *mRadioDozerRevert;
+
+ LLComboBox *mComboDozerSize;
+ LLButton *mBtnApplyToSelection;
+ LLCheckBoxCtrl *mCheckShowOwners;
+
+ std::vector<LLButton*> mButtons;//[ 15 ];
+
+ LLTabContainerCommon *mTab;
+ LLPanelPermissions *mPanelPermissions;
+ LLPanelObject *mPanelObject;
+ LLPanelVolume *mPanelVolume;
+ LLPanelContents *mPanelContents;
+ LLPanelFace *mPanelFace;
+ LLPanelLandInfo *mPanelLandInfo;
+
+ LLTabContainer* mTabLand;
+
+private:
+ BOOL mDirty;
+ S32 mSmallHeight;
+ S32 mLargeHeight;
+};
+
+extern LLFloaterTools *gFloaterTools;
+
+#endif
diff --git a/indra/newview/llfloatertopobjects.cpp b/indra/newview/llfloatertopobjects.cpp
new file mode 100644
index 0000000000..f7850f38db
--- /dev/null
+++ b/indra/newview/llfloatertopobjects.cpp
@@ -0,0 +1,441 @@
+/**
+ * @file llfloatertopobjects.cpp
+ * @brief Shows top colliders, top scripts, etc.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertopobjects.h"
+
+#include "message.h"
+#include "llfontgl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloatergodtools.h"
+#include "llparcel.h"
+#include "llscrolllistctrl.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "lltracker.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+
+LLFloaterTopObjects* LLFloaterTopObjects::sInstance = NULL;
+
+
+// static
+void LLFloaterTopObjects::show()
+{
+ if (sInstance)
+ {
+ sInstance->setVisibleAndFrontmost();
+ return;
+ }
+
+ sInstance = new LLFloaterTopObjects();
+ gUICtrlFactory->buildFloater(sInstance, "floater_top_objects.xml");
+ sInstance->center();
+}
+
+LLFloaterTopObjects::LLFloaterTopObjects()
+: LLFloater("top_objects"),
+ mInitialized(FALSE),
+ mtotalScore(0.f)
+{
+ sInstance = this;
+}
+
+LLFloaterTopObjects::~LLFloaterTopObjects()
+{
+ sInstance = NULL;
+}
+
+// virtual
+BOOL LLFloaterTopObjects::postBuild()
+{
+ childSetCommitCallback("objects_list", onCommitObjectsList, this);
+ childSetDoubleClickCallback("objects_list", onDoubleClickObjectsList);
+ childSetFocus("objects_list");
+ LLScrollListCtrl *objects_list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+ if (objects_list)
+ {
+ objects_list->setCommitOnSelectionChange(TRUE);
+ }
+
+ childSetAction("show_beacon_btn", onClickShowBeacon, this);
+ setDefaultBtn("show_beacon_btn");
+
+ childSetAction("return_selected_btn", onReturnSelected, this);
+ childSetAction("return_all_btn", onReturnAll, this);
+ childSetAction("disable_selected_btn", onDisableSelected, this);
+ childSetAction("disable_all_btn", onDisableAll, this);
+ childSetAction("refresh_btn", onRefresh, this);
+
+
+ childSetAction("filter_object_btn", onGetByObjectNameClicked, this);
+ childSetAction("filter_owner_btn", onGetByOwnerNameClicked, this);
+
+
+ /*
+ LLLineEditor* line_editor = LLUICtrlFactory::getLineEditorByName(this, "owner_name_editor");
+ if (line_editor)
+ {
+ line_editor->setCommitOnFocusLost(FALSE);
+ line_editor->setCommitCallback(onGetByOwnerName);
+ line_editor->setCallbackUserData(this);
+ }
+
+ line_editor = LLUICtrlFactory::getLineEditorByName(this, "object_name_editor");
+ if (line_editor)
+ {
+ line_editor->setCommitOnFocusLost(FALSE);
+ line_editor->setCommitCallback(onGetByObjectName);
+ line_editor->setCallbackUserData(this);
+ }*/
+
+ mCurrentMode = STAT_REPORT_TOP_SCRIPTS;
+ mFlags = 0;
+ mFilter = "";
+
+ return TRUE;
+}
+
+void LLFloaterTopObjects::handle_land_reply(LLMessageSystem* msg, void** data)
+{
+ // Make sure dialog is on screen
+ show();
+ sInstance->handleReply(msg, data);
+
+ //HACK: for some reason sometimes top scripts originally comes back
+ //with no results even though they're there
+ if (!sInstance->mObjectListIDs.size() && !sInstance->mInitialized)
+ {
+ sInstance->onRefresh(NULL);
+ sInstance->mInitialized = TRUE;
+ }
+
+}
+
+void LLFloaterTopObjects::handleReply(LLMessageSystem *msg, void** data)
+{
+ U32 request_flags;
+ U32 total_count;
+
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_RequestFlags, request_flags);
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_TotalObjectCount, total_count);
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ReportType, mCurrentMode);
+
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+ if (!list) return;
+
+ S32 block_count = msg->getNumberOfBlocks("ReportData");
+ for (S32 block = 0; block < block_count; ++block)
+ {
+ U32 task_local_id;
+ LLUUID task_id;
+ F32 location_x, location_y, location_z;
+ F32 score;
+ char name_buf[MAX_STRING];
+ char owner_buf[MAX_STRING];
+
+ msg->getU32Fast(_PREHASH_ReportData, _PREHASH_TaskLocalID, task_local_id, block);
+ msg->getUUIDFast(_PREHASH_ReportData, _PREHASH_TaskID, task_id, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationX, location_x, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationY, location_y, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_LocationZ, location_z, block);
+ msg->getF32Fast(_PREHASH_ReportData, _PREHASH_Score, score, block);
+ msg->getStringFast(_PREHASH_ReportData, _PREHASH_TaskName, MAX_STRING, name_buf, block);
+ msg->getStringFast(_PREHASH_ReportData, _PREHASH_OwnerName, MAX_STRING, owner_buf, block);
+
+ LLSD element;
+
+ element["id"] = task_id;
+ element["object_name"] = LLString(name_buf);
+ element["owner_name"] = LLString(owner_buf);
+ element["columns"][0]["column"] = "score";
+ element["columns"][0]["value"] = llformat("%0.3f", score);
+ element["columns"][0]["font"] = "SANSSERIF";
+ element["columns"][1]["column"] = "name";
+ element["columns"][1]["value"] = name_buf;
+ element["columns"][1]["font"] = "SANSSERIF";
+ element["columns"][2]["column"] = "owner";
+ element["columns"][2]["value"] = owner_buf;
+ element["columns"][2]["font"] = "SANSSERIF";
+ element["columns"][3]["column"] = "location";
+ element["columns"][3]["value"] = llformat("<%0.1f,%0.1f,%0.1f>", location_x, location_y, location_z);
+ element["columns"][3]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+
+ mObjectListData.append(element);
+ mObjectListIDs.push_back(task_id);
+
+ mtotalScore += score;
+ }
+
+ if (total_count == 0 && list->getItemCount() == 0)
+ {
+ LLSD element;
+ element["id"] = LLUUID::null;
+ element["columns"][0]["column"] = "name";
+ element["columns"][0]["value"] = childGetText("none_descriptor");
+ element["columns"][0]["font"] = "SANSSERIF";
+
+ list->addElement(element);
+ }
+ else
+ {
+ list->selectFirstItem();
+ }
+
+ if (mCurrentMode == STAT_REPORT_TOP_SCRIPTS)
+ {
+ setTitle(childGetText("top_scripts_title"));
+ list->setColumnLabel("score", childGetText("scripts_score_label"));
+
+ LLUIString format = childGetText("top_scripts_text");
+ format.setArg("[COUNT]", llformat("%d", total_count));
+ format.setArg("[TIME]", llformat("%0.1f", mtotalScore));
+ childSetValue("title_text", LLSD(format));
+ }
+ else
+ {
+ setTitle(childGetText("top_colliders_title"));
+ list->setColumnLabel("score", childGetText("colliders_score_label"));
+ LLUIString format = childGetText("top_colliders_text");
+ format.setArg("[COUNT]", llformat("%d", total_count));
+ childSetValue("title_text", LLSD(format));
+ }
+}
+
+// static
+void LLFloaterTopObjects::onCommitObjectsList(LLUICtrl* ctrl, void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+
+ self->updateSelectionInfo();
+}
+
+void LLFloaterTopObjects::updateSelectionInfo()
+{
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+
+ if (!list) return;
+
+ LLUUID object_id = list->getCurrentID();
+ if (object_id.isNull()) return;
+
+ LLString object_id_string = object_id.getString();
+
+ childSetValue("id_editor", LLSD(object_id_string));
+ childSetValue("object_name_editor", list->getFirstSelected()->getColumn(1)->getText());
+ childSetValue("owner_name_editor", list->getFirstSelected()->getColumn(2)->getText());
+}
+
+// static
+void LLFloaterTopObjects::onDoubleClickObjectsList(void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+ self->showBeacon();
+}
+
+// static
+void LLFloaterTopObjects::onClickShowBeacon(void* data)
+{
+ LLFloaterTopObjects* self = (LLFloaterTopObjects*)data;
+ if (!self) return;
+ self->showBeacon();
+}
+
+void LLFloaterTopObjects::doToObjects(int action, bool all)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ LLViewerRegion* region = gAgent.getRegion();
+ if (!region) return;
+
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+ if (!list || list->getItemCount() == 0) return;
+
+ std::vector<LLUUID>::iterator id_itor;
+
+ bool start_message = true;
+
+ for (id_itor = mObjectListIDs.begin(); id_itor != mObjectListIDs.end(); ++id_itor)
+ {
+ LLUUID task_id = *id_itor;
+ if (!all && !list->isSelected(task_id))
+ {
+ // Selected only
+ continue;
+ }
+ if (start_message)
+ {
+ if (action == ACTION_RETURN)
+ {
+ msg->newMessageFast(_PREHASH_ParcelReturnObjects);
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_ParcelDisableObjects);
+ }
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1); // Whole region
+ msg->addS32Fast(_PREHASH_ReturnType, RT_NONE);
+ start_message = false;
+ }
+
+ msg->nextBlockFast(_PREHASH_TaskIDs);
+ msg->addUUIDFast(_PREHASH_TaskID, task_id);
+
+ if (msg->isSendFullFast(_PREHASH_TaskIDs))
+ {
+ msg->sendReliable(region->getHost());
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ msg->sendReliable(region->getHost());
+ }
+}
+
+//static
+void LLFloaterTopObjects::callbackReturnAll(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ sInstance->doToObjects(ACTION_RETURN, true);
+ }
+}
+
+void LLFloaterTopObjects::onReturnAll(void* data)
+{
+ gViewerWindow->alertXml("ReturnAllTopObjects", callbackReturnAll, NULL);
+}
+
+
+void LLFloaterTopObjects::onReturnSelected(void* data)
+{
+ sInstance->doToObjects(ACTION_RETURN, false);
+}
+
+
+//static
+void LLFloaterTopObjects::callbackDisableAll(S32 option, void* userdata)
+{
+ if (option == 0)
+ {
+ sInstance->doToObjects(ACTION_DISABLE, true);
+ }
+}
+
+void LLFloaterTopObjects::onDisableAll(void* data)
+{
+ gViewerWindow->alertXml("DisableAllTopObjects", callbackDisableAll, NULL);
+}
+
+void LLFloaterTopObjects::onDisableSelected(void* data)
+{
+ sInstance->doToObjects(ACTION_DISABLE, false);
+}
+
+void LLFloaterTopObjects::clearList()
+{
+ LLCtrlListInterface *list = childGetListInterface("objects_list");
+
+ if (list)
+ {
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+
+ mObjectListData.clear();
+ mObjectListIDs.clear();
+}
+
+void LLFloaterTopObjects::onRefresh(void* data)
+{
+ U32 mode = STAT_REPORT_TOP_SCRIPTS;
+ U32 flags = 0;
+ LLString filter = "";
+
+ if (sInstance)
+ {
+ mode = sInstance->mCurrentMode;
+ flags = sInstance->mFlags;
+ filter = sInstance->mFilter;
+ sInstance->clearList();
+ }
+
+ sInstance->mtotalScore = 0.f;
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_LandStatRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addU32Fast(_PREHASH_ReportType, mode);
+ msg->addU32Fast(_PREHASH_RequestFlags, flags);
+ msg->addStringFast(_PREHASH_Filter, filter);
+ msg->addS32Fast(_PREHASH_ParcelLocalID, 0);
+
+ msg->sendReliable(gAgent.getRegionHost());
+
+ if (sInstance)
+ {
+ sInstance->mFilter = "";
+ sInstance->mFlags = 0;
+ }
+}
+
+void LLFloaterTopObjects::onGetByObjectName(LLUICtrl* ctrl, void* data)
+{
+ if (sInstance)
+ {
+ sInstance->mFlags = STAT_FILTER_BY_OBJECT;
+ sInstance->mFilter = sInstance->childGetText("object_name_editor");
+ onRefresh(NULL);
+ }
+}
+
+void LLFloaterTopObjects::onGetByOwnerName(LLUICtrl* ctrl, void* data)
+{
+ if (sInstance)
+ {
+ sInstance->mFlags = STAT_FILTER_BY_OWNER;
+ sInstance->mFilter = sInstance->childGetText("owner_name_editor");
+ onRefresh(NULL);
+ }
+}
+
+void LLFloaterTopObjects::showBeacon()
+{
+ LLScrollListCtrl* list = LLUICtrlFactory::getScrollListByName(this, "objects_list");
+ if (!list) return;
+
+ LLScrollListItem* first_selected = list->getFirstSelected();
+ if (!first_selected) return;
+
+ LLString name = first_selected->getColumn(1)->getText();
+ LLString pos_string = first_selected->getColumn(3)->getText();
+
+ F32 x, y, z;
+ S32 matched = sscanf(pos_string.c_str(), "<%g,%g,%g>", &x, &y, &z);
+ if (matched != 3) return;
+
+ LLVector3 pos_agent(x, y, z);
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ LLString tooltip("");
+ LLTracker::trackLocation(pos_global, name, tooltip, LLTracker::LOCATION_ITEM);
+}
diff --git a/indra/newview/llfloatertopobjects.h b/indra/newview/llfloatertopobjects.h
new file mode 100644
index 0000000000..071e0bc5cd
--- /dev/null
+++ b/indra/newview/llfloatertopobjects.h
@@ -0,0 +1,86 @@
+/**
+ * @file llfloatertopobjects.h
+ * @brief Shows top colliders or top scripts
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOPOBJECTS_H
+#define LL_LLFLOATERTOPOBJECTS_H
+
+#include "llfloater.h"
+
+class LLUICtrl;
+
+class LLFloaterTopObjects : public LLFloater
+{
+public:
+ // Opens the floater on screen.
+ static void show();
+
+ // Opens the floater if it's not on-screen.
+ // Juggles the UI based on method = "scripts" or "colliders"
+ static void handle_land_reply(LLMessageSystem* msg, void** data);
+ void handleReply(LLMessageSystem* msg, void** data);
+
+ void clearList();
+ void updateSelectionInfo();
+ virtual BOOL postBuild();
+
+ static void onRefresh(void* data);
+
+ static void setMode(U32 mode) { if (sInstance) sInstance->mCurrentMode = mode; }
+
+private:
+ LLFloaterTopObjects();
+ ~LLFloaterTopObjects();
+
+ void initColumns(LLCtrlListInterface *list);
+
+ static void onCommitObjectsList(LLUICtrl* ctrl, void* data);
+ static void onDoubleClickObjectsList(void* data);
+ static void onClickShowBeacon(void* data);
+
+ void doToObjects(int action, bool all);
+
+ static void onReturnAll(void* data);
+ static void onReturnSelected(void* data);
+ static void onDisableAll(void* data);
+ static void onDisableSelected(void* data);
+
+ static void callbackReturnAll(S32 option, void* userdata);
+ static void callbackDisableAll(S32 option, void* userdata);
+
+ static void onGetByOwnerName(LLUICtrl* ctrl, void* data);
+ static void onGetByObjectName(LLUICtrl* ctrl, void* data);
+
+ static void onGetByOwnerNameClicked(void* data) { onGetByOwnerName(NULL, data); };
+ static void onGetByObjectNameClicked(void* data) { onGetByObjectName(NULL, data); };
+
+ void showBeacon();
+
+private:
+ LLString mMethod;
+
+ LLSD mObjectListData;
+ std::vector<LLUUID> mObjectListIDs;
+
+ U32 mCurrentMode;
+ U32 mFlags;
+ LLString mFilter;
+
+ BOOL mInitialized;
+
+ F32 mtotalScore;
+
+ enum
+ {
+ ACTION_RETURN = 0,
+ ACTION_DISABLE
+ };
+
+ static LLFloaterTopObjects* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp
new file mode 100644
index 0000000000..149a585822
--- /dev/null
+++ b/indra/newview/llfloatertos.cpp
@@ -0,0 +1,269 @@
+/**
+ * @file llfloatertos.cpp
+ * @brief Terms of Service Agreement dialog
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatertos.h"
+
+#include "llbutton.h"
+#include "llradiogroup.h"
+#include "llvfile.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "viewer.h"
+#include "llstartup.h"
+#include "message.h"
+#include "llagent.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llviewerstats.h"
+#include "llui.h"
+#include "llhttpclient.h"
+#include "llradiogroup.h"
+#include "llwebbrowserctrl.h"
+
+// static
+LLFloaterTOS* LLFloaterTOS::sInstance = NULL;
+
+// static
+LLFloaterTOS* LLFloaterTOS::show(ETOSType type, const std::string & message)
+{
+ if( !LLFloaterTOS::sInstance )
+ {
+ LLFloaterTOS::sInstance = new LLFloaterTOS(type, message);
+ }
+
+ if (type == TOS_TOS)
+ {
+ gUICtrlFactory->buildFloater(LLFloaterTOS::sInstance, "floater_tos.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(LLFloaterTOS::sInstance, "floater_critical.xml");
+ }
+
+ return LLFloaterTOS::sInstance;
+}
+
+
+LLFloaterTOS::LLFloaterTOS(ETOSType type, const std::string & message)
+: LLModalDialog( " ", 100, 100 ),
+ mType(type),
+ mMessage(message),
+ mWebBrowserWindowId( 0 ),
+ mLoadCompleteCount( 0 )
+{
+}
+
+// helper class that trys to download a URL from a web site and calls a method
+// on parent class indicating if the web server is working or not
+class LLIamHere : public LLHTTPClient::Responder
+{
+ private:
+ LLIamHere( LLFloaterTOS* parent ) :
+ mParent( parent )
+ {}
+
+ LLFloaterTOS* mParent;
+
+ public:
+
+ static boost::intrusive_ptr< LLIamHere > build( LLFloaterTOS* parent )
+ {
+ return boost::intrusive_ptr< LLIamHere >( new LLIamHere( parent ) );
+ };
+
+ virtual void setParent( LLFloaterTOS* parentIn )
+ {
+ mParent = parentIn;
+ };
+
+ virtual void result( const LLSD& content )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( true );
+ };
+
+ virtual void error( U32 status, const std::string& reason )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( false );
+ };
+};
+
+// this is global and not a class member to keep crud out of the header file
+namespace {
+ boost::intrusive_ptr< LLIamHere > gResponsePtr = 0;
+};
+
+BOOL LLFloaterTOS::postBuild()
+{
+ childSetAction("Continue", onContinue, this);
+ childSetAction("Cancel", onCancel, this);
+ childSetCommitCallback("tos_agreement", updateAgree, this);
+
+ // this displays the critical message
+ if ( mType != TOS_TOS )
+ {
+ LLTextEditor *Editor = LLUICtrlFactory::getTextEditorByName(this, "tos_text");
+ if (Editor)
+ {
+ Editor->setHandleEditKeysDirectly( TRUE );
+ Editor->setEnabled( FALSE );
+ Editor->setReadOnlyFgColor(LLColor4::white);
+ Editor->setWordWrap(TRUE);
+ Editor->setFocus(TRUE);
+ }
+ childSetValue("tos_text", LLSD(mMessage));
+ };
+
+ #if LL_LIBXUL_ENABLED
+ // disable Agree to TOS radio button until the page has fully loaded
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( false );
+ };
+
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "tos_html");
+ if ( web_browser )
+ {
+ // save the window id for this web browser widget
+ mWebBrowserWindowId = web_browser->getEmbeddedBrowserWindowId();
+
+ // start to observe it so we see navigate complete events
+ if ( mWebBrowserWindowId )
+ LLMozLib::getInstance()->addObserver( mWebBrowserWindowId, this );
+
+ gResponsePtr = LLIamHere::build( this );
+ LLHTTPClient::get( childGetValue( "real_url" ).asString(), gResponsePtr );
+ };
+ #else
+ LLTextEditor *Editor = LLUICtrlFactory::getTextEditorByName(this, "tos_text");
+ if (Editor)
+ {
+ Editor->setHandleEditKeysDirectly( TRUE );
+ Editor->setEnabled( FALSE );
+ Editor->setReadOnlyFgColor(LLColor4::white);
+ Editor->setWordWrap(TRUE);
+ Editor->setFocus(TRUE);
+ }
+ childSetValue("tos_text", LLSD(mMessage));
+ #endif
+ return TRUE;
+}
+
+void LLFloaterTOS::setSiteIsAlive( bool alive )
+{
+ // only do this for TOS pages
+ if ( mType == TOS_TOS )
+ {
+#if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "tos_html");
+ // if the contents of the site was retrieved
+ if ( alive )
+ {
+ if ( web_browser )
+ {
+ // navigate to the "real" page
+ web_browser->navigateTo( childGetValue( "real_url" ).asString() );
+ };
+ }
+ else
+ {
+ // normally this is set when navigation to TOS page navigation completes (so you can't accept before TOS loads)
+ // but if the page is unavailable, we need to do this now
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( true );
+ };
+
+ if ( web_browser )
+ {
+ // hide browser control (revealing default text message)
+ web_browser->setVisible( FALSE );
+ };
+ };
+#endif // LL_LIBXUL_ENABLED
+ };
+}
+
+LLFloaterTOS::~LLFloaterTOS()
+{
+#if LL_LIBXUL_ENABLED
+ // stop obsaerving events
+ if ( mWebBrowserWindowId )
+ LLMozLib::getInstance()->addObserver( mWebBrowserWindowId, this );
+#endif // LL_LIBXUL_ENABLED
+
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ LLFloaterTOS::sInstance = NULL;
+}
+
+// virtual
+void LLFloaterTOS::draw()
+{
+ // draw children
+ LLModalDialog::draw();
+}
+
+// static
+void LLFloaterTOS::updateAgree(LLUICtrl*, void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ std::string agree = self->childGetValue("tos_agreement").asString();
+ self->childSetEnabled("Continue", (agree == "radio_agree") );
+}
+
+// static
+void LLFloaterTOS::onContinue( void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ llinfos << "User agrees with TOS." << llendl;
+ if (self->mType == TOS_TOS)
+ {
+ gAcceptTOS = TRUE;
+ }
+ else
+ {
+ gAcceptCriticalMessage = TRUE;
+ }
+ gStartupState++;
+ self->close(); // destroys this object
+}
+
+// static
+void LLFloaterTOS::onCancel( void* userdata )
+{
+ LLFloaterTOS* self = (LLFloaterTOS*) userdata;
+ llinfos << "User disagrees with TOS." << llendl;
+ gViewerWindow->alertXml("MustAgreeToLogIn", login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ self->mLoadCompleteCount = 0; // reset counter for next time we come to TOS
+ self->close(); // destroys this object
+}
+
+//virtual
+void LLFloaterTOS::onNavigateComplete( const EventType& eventIn )
+{
+ // skip past the loading screen navigate complete
+ if ( ++mLoadCompleteCount == 2 )
+ {
+ llinfos << "NAVIGATE COMPLETE" << llendl;
+ // enable Agree to TOS radio button now that page has loaded
+ LLRadioGroup* tos_agreement = LLUICtrlFactory::getRadioGroupByName(this, "tos_agreement");
+ if ( tos_agreement )
+ {
+ tos_agreement->setEnabled( true );
+ };
+ };
+}
diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h
new file mode 100644
index 0000000000..1f504b4edb
--- /dev/null
+++ b/indra/newview/llfloatertos.h
@@ -0,0 +1,64 @@
+/**
+ * @file llfloatertos.h
+ * @brief Terms of Service Agreement dialog
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLFLOATERTOS_H
+#define LL_LLFLOATERTOS_H
+
+#include "llmodaldialog.h"
+#include "llassetstorage.h"
+#include "llmozlib.h"
+
+class LLButton;
+class LLRadioGroup;
+class LLVFS;
+class LLTextEditor;
+class LLUUID;
+
+class LLFloaterTOS :
+ public LLModalDialog,
+ public LLEmbeddedBrowserWindowObserver
+{
+public:
+ virtual ~LLFloaterTOS();
+
+ // Types of dialog.
+ enum ETOSType
+ {
+ TOS_TOS = 0,
+ TOS_CRITICAL_MESSAGE = 1
+ };
+
+ // Asset_id is overwritten with LLUUID::null when agree is clicked.
+ static LLFloaterTOS* show(ETOSType type, const std::string & message);
+
+ BOOL postBuild();
+
+ virtual void draw();
+
+ static void updateAgree( LLUICtrl *, void* userdata );
+ static void onContinue( void* userdata );
+ static void onCancel( void* userdata );
+
+ void setSiteIsAlive( bool alive );
+
+ virtual void onNavigateComplete( const EventType& eventIn );
+
+private:
+ // Asset_id is overwritten with LLUUID::null when agree is clicked.
+ LLFloaterTOS(ETOSType type, const std::string & message);
+
+private:
+ ETOSType mType;
+ LLString mMessage;
+ int mWebBrowserWindowId;
+ int mLoadCompleteCount;
+
+ static LLFloaterTOS* sInstance;
+};
+
+#endif // LL_LLFLOATERTOS_H
diff --git a/indra/newview/llfloaterworldmap.cpp b/indra/newview/llfloaterworldmap.cpp
new file mode 100644
index 0000000000..3492a36353
--- /dev/null
+++ b/indra/newview/llfloaterworldmap.cpp
@@ -0,0 +1,1579 @@
+/**
+ * @file llfloaterworldmap.cpp
+ * @author James Cook, Tom Yedwab
+ * @brief LLFloaterWorldMap class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Map of the entire world, with multiple background images,
+ * avatar tracking, teleportation by double-click, etc.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloaterworldmap.h"
+
+// Library includes
+#include "llfontgl.h"
+#include "llinventory.h"
+#include "lllineeditor.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "lldraghandle.h"
+#include "lleconomy.h"
+#include "llfirstuse.h"
+#include "llfocusmgr.h"
+#include "lliconctrl.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lllandmarklist.h"
+#include "llnetmap.h"
+#include "llpreviewlandmark.h"
+#include "llradiogroup.h"
+#include "llregionhandle.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llscrolllistctrl.h"
+#include "llsliderctrl.h"
+#include "llspinctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltracker.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llworldmap.h"
+#include "llworldmapview.h"
+#include "llurl.h"
+#include "llvieweruictrlfactory.h"
+#include "viewer.h"
+#include "llmapimagetype.h"
+#include "llweb.h"
+
+#include "llglheaders.h"
+
+//---------------------------------------------------------------------------
+// Constants
+//---------------------------------------------------------------------------
+static const F32 MAP_ZOOM_TIME = 0.2f;
+
+enum EPanDirection
+{
+ PAN_UP,
+ PAN_DOWN,
+ PAN_LEFT,
+ PAN_RIGHT
+};
+
+// Values in pixels per region
+static const F32 ZOOM_MIN = -8.f; // initial value, updated by adjustZoomSlider
+static const F32 ZOOM_MAX = 0.f;
+static const F32 ZOOM_INC = 0.2f;
+
+static const F32 SIM_COORD_MIN = 0.f;
+static const F32 SIM_COORD_MAX = 255.f;
+static const F32 SIM_COORD_DEFAULT = 128.f;
+
+static const F64 MAX_FLY_DISTANCE = 363.f; // Diagonal size of one sim.
+static const F64 MAX_FLY_DISTANCE_SQUARED = MAX_FLY_DISTANCE * MAX_FLY_DISTANCE;
+
+//---------------------------------------------------------------------------
+// Globals
+//---------------------------------------------------------------------------
+
+LLFloaterWorldMap* gFloaterWorldMap = NULL;
+
+class LLMapInventoryObserver : public LLInventoryObserver
+{
+public:
+ LLMapInventoryObserver() {}
+ virtual ~LLMapInventoryObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLMapInventoryObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLInventoryObserver::CALLING_CARD | LLInventoryObserver::ADD |
+ LLInventoryObserver::REMOVE)) != 0)
+ {
+ gFloaterWorldMap->inventoryChanged();
+ }
+}
+
+class LLMapFriendObserver : public LLFriendObserver
+{
+public:
+ LLMapFriendObserver() {}
+ virtual ~LLMapFriendObserver() {}
+ virtual void changed(U32 mask);
+};
+
+void LLMapFriendObserver::changed(U32 mask)
+{
+ // if there's a change we're interested in.
+ if((mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE | LLFriendObserver::POWERS)) != 0)
+ {
+ gFloaterWorldMap->friendsChanged();
+ }
+}
+
+//---------------------------------------------------------------------------
+// Statics
+//---------------------------------------------------------------------------
+
+// Used as a pretend asset and inventory id to mean "landmark at my home location."
+const LLUUID LLFloaterWorldMap::sHomeID( "10000000-0000-0000-0000-000000000001" );
+
+//---------------------------------------------------------------------------
+// Construction and destruction
+//---------------------------------------------------------------------------
+
+
+LLFloaterWorldMap::LLFloaterWorldMap()
+: LLFloater("worldmap", "FloaterWorldMapRect", "World Map",
+ TRUE, // resize
+ 410, // min-width
+ 520, // min-height
+ FALSE, // drag on left
+ TRUE, // minimize
+ TRUE), // close
+ mInventory(NULL),
+ mInventoryObserver(NULL),
+ mFriendObserver(NULL),
+ mCompletingRegionName(""),
+ mWaitingForTracker(FALSE),
+ mExactMatch(FALSE),
+ mTrackedLocation(0,0,0),
+ mTrackedStatus(LLTracker::TRACKING_NOTHING)
+{
+ LLCallbackMap::map_t factory_map;
+ factory_map["objects_mapview"] = LLCallbackMap(createWorldMapView, NULL);
+ factory_map["terrain_mapview"] = LLCallbackMap(createWorldMapView, NULL);
+ gUICtrlFactory->buildFloater(this, "floater_world_map.xml", &factory_map);
+}
+
+// static
+void* LLFloaterWorldMap::createWorldMapView(void* data)
+{
+ return new LLWorldMapView("mapview", LLRect(0,300,400,0));
+}
+
+BOOL LLFloaterWorldMap::postBuild()
+{
+ mTabs = LLUICtrlFactory::getTabContainerByName(this, "maptab");
+ if (!mTabs) return FALSE;
+
+ LLPanel *panel;
+
+ panel = LLUICtrlFactory::getPanelByName(mTabs, "objects_mapview");
+ if (panel)
+ {
+ mTabs->setTabChangeCallback(panel, onCommitBackground);
+ mTabs->setTabUserData(panel, this);
+ }
+ panel = LLUICtrlFactory::getPanelByName(mTabs, "terrain_mapview");
+ if (panel)
+ {
+ mTabs->setTabChangeCallback(panel, onCommitBackground);
+ mTabs->setTabUserData(panel, this);
+ }
+
+ onCommitBackground((void*)this, false);
+
+ childSetCommitCallback("friend combo", onAvatarComboCommit, this);
+
+ LLComboBox *avatar_combo = LLUICtrlFactory::getComboBoxByName(this, "friend combo");
+ if (avatar_combo)
+ {
+ avatar_combo->selectFirstItem();
+ avatar_combo->setPrearrangeCallback( onAvatarComboPrearrange );
+ }
+
+ childSetAction("DoSearch", onLocationCommit, this);
+
+ childSetFocusChangedCallback("location", updateSearchEnabled);
+ childSetKeystrokeCallback("location", (void (*)(LLLineEditor*,void*))updateSearchEnabled, NULL);
+
+ childSetCommitCallback("search_results", onCommitSearchResult, this);
+ childSetDoubleClickCallback("search_results", onTeleportBtn);
+ childSetCommitCallback("spin x", onCommitLocation, this);
+ childSetCommitCallback("spin y", onCommitLocation, this);
+ childSetCommitCallback("spin z", onCommitLocation, this);
+
+ childSetCommitCallback("landmark combo", onLandmarkComboCommit, this);
+
+ LLComboBox *landmark_combo = LLUICtrlFactory::getComboBoxByName(this, "landmark combo");
+ if (landmark_combo)
+ {
+ landmark_combo->selectFirstItem();
+ landmark_combo->setPrearrangeCallback( onLandmarkComboPrearrange );
+ }
+
+ childSetAction("Go Home", onGoHome, this);
+
+ childSetAction("Teleport", onTeleportBtn, this);
+
+ childSetAction("Show Destination", onShowTargetBtn, this);
+ childSetAction("Show My Location", onShowAgentBtn, this);
+ childSetAction("Clear", onClearBtn, this);
+ childSetAction("copy_slurl", onCopySLURL, this);
+
+ mCurZoomVal = log(gMapScale)/log(2.f);
+ childSetValue("zoom slider", gMapScale);
+
+ setDefaultBtn("");
+
+ if ( gAgent.mAccess <= SIM_ACCESS_PG )
+ {
+ // Hide Mature Events controls
+ childHide("events_mature_icon");
+ childHide("events_mature_label");
+ childHide("event_mature_chk");
+ }
+
+ mZoomTimer.stop();
+
+ return TRUE;
+}
+
+// virtual
+LLFloaterWorldMap::~LLFloaterWorldMap()
+{
+ // All cleaned up by LLView destructor
+ mTabs = NULL;
+
+ // Inventory deletes all observers on shutdown
+ mInventory = NULL;
+ mInventoryObserver = NULL;
+
+ // avatar tracker will delete this for us.
+ mFriendObserver = NULL;
+}
+
+
+// virtual
+void LLFloaterWorldMap::onClose(bool app_quitting)
+{
+ setVisible(FALSE);
+}
+
+// Allow us to download landmarks quickly when map is shown
+class LLLandmarkFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ virtual void done()
+ {
+ // We need to find landmarks in all folders, so get the main
+ // background download going.
+ gInventory.startBackgroundFetch();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// static
+void LLFloaterWorldMap::show(void*, BOOL center_on_target)
+{
+ BOOL was_visible = gFloaterWorldMap->getVisible();
+
+ gFloaterWorldMap->mIsClosing = FALSE;
+ gFloaterWorldMap->open();
+
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)gFloaterWorldMap->mTabs->getCurrentPanel();
+ map_panel->clearLastClick();
+
+ if (!was_visible)
+ {
+ // reset pan on show, so it centers on you again
+ if (!center_on_target)
+ {
+ LLWorldMapView::setPan(0, 0, TRUE);
+ }
+ map_panel->updateVisibleBlocks();
+
+ // Reload the agent positions when we show the window
+ gWorldMap->eraseItems();
+
+ // Reload any maps that may have changed
+ gWorldMap->clearSimFlags();
+
+ const S32 panel_num = gFloaterWorldMap->mTabs->getCurrentPanelIndex();
+ const bool request_from_sim = true;
+ gWorldMap->setCurrentLayer(panel_num, request_from_sim);
+
+ // We may already have a bounding box for the regions of the world,
+ // so use that to adjust the view.
+ gFloaterWorldMap->adjustZoomSliderBounds();
+
+ // Could be first show
+ LLFirstUse::useMap();
+
+ // Start speculative download of landmarks
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLUUID landmark_folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ gInventory.startBackgroundFetch(landmark_folder_id);
+
+ gFloaterWorldMap->childSetFocus("location", TRUE);
+ gFocusMgr.triggerFocusFlash();
+
+ gFloaterWorldMap->buildAvatarIDList();
+ gFloaterWorldMap->buildLandmarkIDLists();
+ }
+
+ if (center_on_target)
+ {
+ gFloaterWorldMap->centerOnTarget(FALSE);
+ }
+}
+
+
+
+// static
+void LLFloaterWorldMap::reloadIcons(void*)
+{
+ gWorldMap->eraseItems();
+
+ gWorldMap->sendMapLayerRequest();
+}
+
+
+// static
+void LLFloaterWorldMap::toggle(void*)
+{
+ BOOL visible = gFloaterWorldMap->getVisible();
+
+ if (!visible)
+ {
+ show(NULL, FALSE);
+ }
+ else
+ {
+ gFloaterWorldMap->mIsClosing = TRUE;
+ gFloaterWorldMap->close();
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::hide(void*)
+{
+ gFloaterWorldMap->mIsClosing = TRUE;
+ gFloaterWorldMap->close();
+}
+
+
+// virtual
+void LLFloaterWorldMap::setVisible( BOOL visible )
+{
+ LLFloater::setVisible( visible );
+
+ gSavedSettings.setBOOL( "ShowWorldMap", visible );
+
+ if( !visible )
+ {
+ // While we're not visible, discard the overlay images we're using
+ if (gWorldMap)
+ {
+ gWorldMap->clearImageRefs();
+ }
+ }
+}
+
+
+// virtual
+BOOL LLFloaterWorldMap::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled;
+ handled = LLFloater::handleHover(x, y, mask);
+ return handled;
+}
+
+BOOL LLFloaterWorldMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getVisible() && !isMinimized() && isFrontmost())
+ {
+ F32 slider_value = (F32)childGetValue("zoom slider").asReal();
+ slider_value += ((F32)clicks * -0.3333f);
+ childSetValue("zoom slider", LLSD(slider_value));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+// virtual
+void LLFloaterWorldMap::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+ LLFloater::reshape( width, height, called_from_parent );
+
+ // Might have changed size of world display area
+ // JC: Technically, this is correct, but it makes the slider "pop"
+ // if you resize the window, then draw the slider. Just leaving it
+ // the way it was when you opened the window seems better.
+ // adjustZoomSliderBounds();
+}
+
+
+// virtual
+void LLFloaterWorldMap::draw()
+{
+ if( !getVisible() )
+ {
+ return;
+ }
+
+ updateLocation();
+
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if (LLTracker::TRACKING_AVATAR == tracking_status)
+ {
+ childSetColor("avatar_icon", gTrackColor);
+ }
+ else
+ {
+ childSetColor("avatar_icon", gDisabledTrackColor);
+ }
+
+ if (LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ childSetColor("landmark_icon", gTrackColor);
+ }
+ else
+ {
+ childSetColor("landmark_icon", gDisabledTrackColor);
+ }
+
+ if (LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ childSetColor("location_icon", gTrackColor);
+ }
+ else
+ {
+ if (mCompletingRegionName != "")
+ {
+ F64 seconds = LLTimer::getElapsedSeconds();
+ double value = fmod(seconds, 2);
+ value = 0.5 + 0.5*cos(value * 3.14159f);
+ LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
+ childSetColor("location_icon", loading_color);
+ }
+ else
+ {
+ childSetColor("location_icon", gDisabledTrackColor);
+ }
+ }
+
+ // check for completion of tracking data
+ if (mWaitingForTracker)
+ {
+ centerOnTarget(TRUE);
+ }
+
+ childSetEnabled("Teleport", (BOOL)tracking_status);
+// childSetEnabled("Clear", (BOOL)tracking_status);
+ childSetEnabled("Show Destination", (BOOL)tracking_status || gWorldMap->mIsTrackingUnknownLocation);
+ childSetEnabled("copy_slurl", (BOOL)tracking_status);
+
+ setMouseOpaque(TRUE);
+ mDragHandle->setMouseOpaque(TRUE);
+
+ //RN: snaps to zoom value because interpolation caused jitter in the text rendering
+ if (!mZoomTimer.getStarted() && mCurZoomVal != (F32)childGetValue("zoom slider").asReal())
+ {
+ mZoomTimer.start();
+ }
+ F32 interp = mZoomTimer.getElapsedTimeF32() / MAP_ZOOM_TIME;
+ if (interp > 1.f)
+ {
+ interp = 1.f;
+ mZoomTimer.stop();
+ }
+ mCurZoomVal = lerp(mCurZoomVal, (F32)childGetValue("zoom slider").asReal(), interp);
+ F32 map_scale = 256.f*pow(2.f, mCurZoomVal);
+ LLWorldMapView::setScale( map_scale );
+
+ LLFloater::draw();
+}
+
+
+//-------------------------------------------------------------------------
+// Internal utility functions
+//-------------------------------------------------------------------------
+
+
+void LLFloaterWorldMap::trackAvatar( const LLUUID& avatar_id, const LLString& name )
+{
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
+ if (!iface) return;
+
+ buildAvatarIDList();
+ if(iface->setCurrentByID(avatar_id) || gAgent.isGodlike())
+ {
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map.
+ if(gAgent.isGodlike())
+ {
+ childSetValue("spin z", LLSD(200.f));
+ }
+ // Don't re-request info if we already have it or we won't have it in time to teleport
+ if (mTrackedStatus != LLTracker::TRACKING_AVATAR || name != mTrackedAvatarName)
+ {
+ mTrackedStatus = LLTracker::TRACKING_AVATAR;
+ mTrackedAvatarName = name;
+ LLTracker::trackAvatar(avatar_id, name);
+ }
+ }
+ else
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackLandmark( const LLUUID& landmark_item_id )
+{
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("landmark combo");
+ if (!iface) return;
+
+ buildLandmarkIDLists();
+ BOOL found = FALSE;
+ S32 idx;
+ for (idx = 0; idx < mLandmarkItemIDList.count(); idx++)
+ {
+ if ( mLandmarkItemIDList.get(idx) == landmark_item_id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found && iface->setCurrentByID( landmark_item_id ) )
+ {
+ LLUUID asset_id = mLandmarkAssetIDList.get( idx );
+ LLString name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "landmark combo");
+ if (combo) name = combo->getSimple();
+ mTrackedStatus = LLTracker::TRACKING_LANDMARK;
+ LLTracker::trackLandmark(mLandmarkAssetIDList.get( idx ), // assetID
+ mLandmarkItemIDList.get( idx ), // itemID
+ name); // name
+
+ if( asset_id != sHomeID )
+ {
+ // start the download process
+ gLandmarkList.getAsset( asset_id);
+ }
+
+ // We have to download both region info and landmark data, so set busy. JC
+// getWindow()->incBusyCount();
+ }
+ else
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ setDefaultBtn("Teleport");
+}
+
+
+void LLFloaterWorldMap::trackEvent(const LLItemInfo &event_info)
+{
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(event_info.mPosGlobal, event_info.mName, event_info.mToolTip, LLTracker::LOCATION_EVENT);
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackGenericItem(const LLItemInfo &item)
+{
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(item.mPosGlobal, item.mName, item.mToolTip, LLTracker::LOCATION_ITEM);
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::trackLocation(const LLVector3d& pos_global)
+{
+ LLSimInfo* sim_info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (!sim_info)
+ {
+ gWorldMap->mIsTrackingUnknownLocation = TRUE;
+ gWorldMap->mInvalidLocation = FALSE;
+ gWorldMap->mUnknownLocation = pos_global;
+ LLTracker::stopTracking(NULL);
+ S32 world_x = S32(pos_global.mdV[0] / 256);
+ S32 world_y = S32(pos_global.mdV[1] / 256);
+ gWorldMap->sendMapBlockRequest(world_x, world_y, world_x, world_y, true);
+ setDefaultBtn("");
+ return;
+ }
+ if (sim_info->mAccess == SIM_ACCESS_DOWN)
+ {
+ // Down sim. Show the blue circle of death!
+ gWorldMap->mIsTrackingUnknownLocation = TRUE;
+ gWorldMap->mUnknownLocation = pos_global;
+ gWorldMap->mInvalidLocation = TRUE;
+ LLTracker::stopTracking(NULL);
+ setDefaultBtn("");
+ return;
+ }
+
+ LLString sim_name = gWorldMap->simNameFromPosGlobal( pos_global );
+ F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
+ F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
+ LLString full_name = llformat("%s (%d, %d, %d)",
+ sim_name.c_str(),
+ llround(region_x),
+ llround(region_y),
+ llround((F32)pos_global.mdV[VZ]));
+
+ LLString tooltip("");
+ mTrackedStatus = LLTracker::TRACKING_LOCATION;
+ LLTracker::trackLocation(pos_global, full_name, tooltip);
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+ gWorldMap->mIsTrackingDoubleClick = FALSE;
+ gWorldMap->mIsTrackingCommit = FALSE;
+
+ setDefaultBtn("Teleport");
+}
+
+void LLFloaterWorldMap::updateLocation()
+{
+ // These values may get updated by a message, so need to check them every frame
+ // The fields may be changed by the user, so only update them if the data changes
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (pos_global.isExactlyZero())
+ {
+ return; // invalid location
+ }
+ LLTracker::ETrackingStatus status = LLTracker::getTrackingStatus();
+ LLString sim_name = gWorldMap->simNameFromPosGlobal( pos_global );
+ if ((status != LLTracker::TRACKING_NOTHING) &&
+ (status != mTrackedStatus || pos_global != mTrackedLocation || sim_name != mTrackedSimName))
+ {
+ mTrackedStatus = status;
+ mTrackedLocation = pos_global;
+ mTrackedSimName = sim_name;
+
+ if (status == LLTracker::TRACKING_AVATAR)
+ {
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map.
+ if(gAgent.isGodlike())
+ {
+ pos_global[2] = 200;
+ }
+ }
+
+ childSetValue("location", sim_name);
+
+ F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS );
+ F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS );
+ childSetValue("spin x", LLSD(region_x) );
+ childSetValue("spin y", LLSD(region_y) );
+ childSetValue("spin z", LLSD((F32)pos_global.mdV[VZ]) );
+
+ mSLURL = LLWeb::escapeURL(llformat("http://slurl.com/secondlife/%s/%d/%d/%d",
+ sim_name.c_str(), llround(region_x), llround(region_y), llround((F32)pos_global.mdV[VZ])));
+ }
+}
+
+void LLFloaterWorldMap::trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord)
+{
+ LLSimInfo* sim_info = gWorldMap->simInfoFromName(region_name);
+ z_coord = llclamp(z_coord, 0, 1000);
+ if (sim_info)
+ {
+ LLVector3 local_pos;
+ local_pos.mV[VX] = (F32)x_coord;
+ local_pos.mV[VY] = (F32)y_coord;
+ local_pos.mV[VZ] = (F32)z_coord;
+ LLVector3d global_pos = sim_info->getGlobalPos(local_pos);
+ trackLocation(global_pos);
+ setDefaultBtn("Teleport");
+ }
+ else
+ {
+ // fill in UI based on URL
+ gFloaterWorldMap->childSetValue("location", region_name);
+ childSetValue("spin x", LLSD((F32)x_coord));
+ childSetValue("spin y", LLSD((F32)y_coord));
+ childSetValue("spin z", LLSD((F32)z_coord));
+
+ // pass sim name to combo box
+ gFloaterWorldMap->mCompletingRegionName = region_name;
+ gWorldMap->sendNamedRegionRequest(region_name);
+ LLString::toLower(gFloaterWorldMap->mCompletingRegionName);
+ gWorldMap->mIsTrackingCommit = TRUE;
+ }
+}
+
+void LLFloaterWorldMap::observeInventory(LLInventoryModel* model)
+{
+ if(mInventory)
+ {
+ mInventory->removeObserver(mInventoryObserver);
+ delete mInventoryObserver;
+ mInventory = NULL;
+ mInventoryObserver = NULL;
+ }
+ if(model)
+ {
+ mInventory = model;
+ mInventoryObserver = new LLMapInventoryObserver;
+ // Inventory deletes all observers on shutdown
+ mInventory->addObserver(mInventoryObserver);
+ inventoryChanged();
+ }
+}
+
+void LLFloaterWorldMap::inventoryChanged()
+{
+ if(!LLTracker::getTrackedLandmarkItemID().isNull())
+ {
+ LLUUID item_id = LLTracker::getTrackedLandmarkItemID();
+ buildLandmarkIDLists();
+ trackLandmark(item_id);
+ }
+}
+
+void LLFloaterWorldMap::observeFriends()
+{
+ if(!mFriendObserver)
+ {
+ mFriendObserver = new LLMapFriendObserver;
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+ friendsChanged();
+ }
+}
+
+void LLFloaterWorldMap::friendsChanged()
+{
+ LLAvatarTracker& t = LLAvatarTracker::instance();
+ const LLUUID& avatar_id = t.getAvatarID();
+ buildAvatarIDList();
+ if(avatar_id.notNull())
+ {
+ LLCtrlSelectionInterface *iface = childGetSelectionInterface("friend combo");
+ if(!iface || !iface->setCurrentByID(avatar_id) ||
+ !t.getBuddyInfo(avatar_id)->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION) || gAgent.isGodlike())
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ }
+}
+
+// No longer really builds a list. Instead, just updates mAvatarCombo.
+void LLFloaterWorldMap::buildAvatarIDList()
+{
+ LLCtrlListInterface *list = childGetListInterface("friend combo");
+ if (!list) return;
+
+ // Delete all but the "None" entry
+ S32 list_size = list->getItemCount();
+ while (list_size > 1)
+ {
+ list->selectNthItem(1);
+ list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ --list_size;
+ }
+
+ LLSD default_column;
+ default_column["name"] = "friend name";
+ default_column["label"] = "Friend Name";
+ default_column["width"] = 500;
+ list->addColumn(default_column);
+
+ // Get all of the calling cards for avatar that are currently online
+ LLCollectMappableBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ LLCollectMappableBuddies::buddy_map_t::iterator it;
+ LLCollectMappableBuddies::buddy_map_t::iterator end;
+ it = collector.mMappable.begin();
+ end = collector.mMappable.end();
+ for( ; it != end; ++it)
+ {
+ list->addSimpleElement((*it).first, ADD_BOTTOM, (*it).second);
+ }
+
+ list->setCurrentByID( LLAvatarTracker::instance().getAvatarID() );
+ list->selectFirstItem();
+}
+
+
+void LLFloaterWorldMap::buildLandmarkIDLists()
+{
+ LLCtrlListInterface *list = childGetListInterface("landmark combo");
+ if (!list) return;
+
+ // Delete all but the "None" entry
+ S32 list_size = list->getItemCount();
+ while (list_size > 1)
+ {
+ list->selectNthItem(1);
+ list->operateOnSelection(LLCtrlListInterface::OP_DELETE);
+ --list_size;
+ }
+
+ mLandmarkItemIDList.reset();
+ mLandmarkAssetIDList.reset();
+
+ // Get all of the current landmarks
+ mLandmarkAssetIDList.put( LLUUID::null );
+ mLandmarkItemIDList.put( LLUUID::null );
+
+ mLandmarkAssetIDList.put( sHomeID );
+ mLandmarkItemIDList.put( sHomeID );
+
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLIsType is_landmark(LLAssetType::AT_LANDMARK);
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_landmark);
+ std::sort(items.begin(), items.end(), LLViewerInventoryItem::comparePointers());
+
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLInventoryItem* item = items.get(i);
+
+ list->addSimpleElement(item->getName(), ADD_BOTTOM, item->getUUID());
+
+ mLandmarkAssetIDList.put( item->getAssetUUID() );
+ mLandmarkItemIDList.put( item->getUUID() );
+ }
+ list->sortByColumn("landmark name", TRUE);
+ list->selectFirstItem();
+}
+
+
+F32 LLFloaterWorldMap::getDistanceToDestination(const LLVector3d &destination,
+ F32 z_attenuation) const
+{
+ LLVector3d delta = destination - gAgent.getPositionGlobal();
+ // by attenuating the z-component we effectively
+ // give more weight to the x-y plane
+ delta.mdV[VZ] *= z_attenuation;
+ F32 distance = (F32)delta.magVec();
+ return distance;
+}
+
+
+void LLFloaterWorldMap::clearLocationSelection(BOOL clear_ui)
+{
+ LLCtrlListInterface *list = childGetListInterface("search_results");
+ if (list)
+ {
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+ }
+ if (!childHasKeyboardFocus("spin x"))
+ {
+ childSetValue("spin x", SIM_COORD_DEFAULT);
+ }
+ if (!childHasKeyboardFocus("spin y"))
+ {
+ childSetValue("spin y", SIM_COORD_DEFAULT);
+ }
+ if (!childHasKeyboardFocus("spin z"))
+ {
+ childSetValue("spin z", 0);
+ }
+ gWorldMap->mIsTrackingCommit = FALSE;
+ mCompletingRegionName = "";
+ mExactMatch = FALSE;
+}
+
+
+void LLFloaterWorldMap::clearLandmarkSelection(BOOL clear_ui)
+{
+ if (clear_ui || !childHasKeyboardFocus("landmark combo"))
+ {
+ LLCtrlListInterface *list = childGetListInterface("landmark combo");
+ if (list)
+ {
+ list->selectByValue( "None" );
+ }
+ }
+}
+
+
+void LLFloaterWorldMap::clearAvatarSelection(BOOL clear_ui)
+{
+ if (clear_ui || !childHasKeyboardFocus("friend combo"))
+ {
+ mTrackedStatus = LLTracker::TRACKING_NOTHING;
+ LLCtrlListInterface *list = childGetListInterface("friend combo");
+ if (list)
+ {
+ list->selectByValue( "None" );
+ }
+ }
+}
+
+
+// Adjust the maximally zoomed out limit of the zoom slider so you
+// can see the whole world, plus a little.
+void LLFloaterWorldMap::adjustZoomSliderBounds()
+{
+ // World size in regions
+ S32 world_width_regions = gWorldMap->getWorldWidth() / REGION_WIDTH_UNITS;
+ S32 world_height_regions = gWorldMap->getWorldHeight() / REGION_WIDTH_UNITS;
+
+ // Pad the world size a little bit, so we have a nice border on
+ // the edge
+ world_width_regions++;
+ world_height_regions++;
+
+ // Find how much space we have to display the world
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)mTabs->getCurrentPanel();
+ LLRect view_rect = map_panel->getRect();
+
+ // View size in pixels
+ S32 view_width = view_rect.getWidth();
+ S32 view_height = view_rect.getHeight();
+
+ // Pixels per region to display entire width/height
+ F32 width_pixels_per_region = (F32) view_width / (F32) world_width_regions;
+ F32 height_pixels_per_region = (F32) view_height / (F32) world_height_regions;
+
+ F32 pixels_per_region = llmin(width_pixels_per_region,
+ height_pixels_per_region);
+
+ // Round pixels per region to an even number of slider increments
+ S32 slider_units = llfloor(pixels_per_region / 0.2f);
+ pixels_per_region = slider_units * 0.2f;
+
+ // Make sure the zoom slider can be moved at least a little bit.
+ // Likewise, less than the increment pixels per region is just silly.
+ pixels_per_region = llclamp(pixels_per_region, 1.f, (F32)(pow(2.f, ZOOM_MAX) * 128.f));
+
+ F32 min_power = log(pixels_per_region/256.f)/log(2.f);
+ childSetMinValue("zoom slider", min_power);
+}
+
+
+//-------------------------------------------------------------------------
+// User interface widget callbacks
+//-------------------------------------------------------------------------
+
+// static
+void LLFloaterWorldMap::onPanBtn( void* userdata )
+{
+ if( !gFloaterWorldMap ) return;
+
+ EPanDirection direction = (EPanDirection)(intptr_t)userdata;
+
+ S32 pan_x = 0;
+ S32 pan_y = 0;
+ switch( direction )
+ {
+ case PAN_UP: pan_y = -1; break;
+ case PAN_DOWN: pan_y = 1; break;
+ case PAN_LEFT: pan_x = 1; break;
+ case PAN_RIGHT: pan_x = -1; break;
+ default: llassert(0); return;
+ }
+
+ LLWorldMapView* map_panel;
+ map_panel = (LLWorldMapView*)gFloaterWorldMap->mTabs->getCurrentPanel();
+ map_panel->translatePan( pan_x, pan_y );
+}
+
+// static
+void LLFloaterWorldMap::onGoHome(void*)
+{
+ gAgent.teleportHome();
+ gFloaterWorldMap->close();
+}
+
+
+// static
+void LLFloaterWorldMap::onLandmarkComboPrearrange( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("landmark combo");
+ if (!list) return;
+
+ LLUUID current_choice = list->getCurrentID();
+
+ gFloaterWorldMap->buildLandmarkIDLists();
+
+ if( current_choice.isNull() || !list->setCurrentByID( current_choice ) )
+ {
+ LLTracker::stopTracking(NULL);
+ }
+
+}
+
+// static
+void LLFloaterWorldMap::onLandmarkComboCommit( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = gFloaterWorldMap->childGetListInterface("landmark combo");
+ if (!list) return;
+
+ LLUUID asset_id;
+ LLUUID item_id = list->getCurrentID();
+
+ LLTracker::stopTracking(NULL);
+
+ //RN: stopTracking() clears current combobox selection, need to reassert it here
+ list->setCurrentByID(item_id);
+
+ if( item_id.isNull() )
+ {
+ }
+ else if( item_id == sHomeID )
+ {
+ asset_id = sHomeID;
+ }
+ else
+ {
+ LLInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ asset_id = item->getAssetUUID();
+ }
+ else
+ {
+ // Something went wrong, so revert to a safe value.
+ item_id.setNull();
+ }
+ }
+
+ self->trackLandmark( item_id);
+ onShowTargetBtn(self);
+}
+
+// static
+void LLFloaterWorldMap::onAvatarComboPrearrange( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = self->childGetListInterface("friend combo");
+ if (!list) return;
+
+ LLUUID current_choice;
+
+ if( LLAvatarTracker::instance().haveTrackingInfo() )
+ {
+ current_choice = LLAvatarTracker::instance().getAvatarID();
+ }
+
+ self->buildAvatarIDList();
+
+ if( !list->setCurrentByID( current_choice ) || current_choice.isNull() )
+ {
+ LLTracker::stopTracking(NULL);
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::onAvatarComboCommit( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap* self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = gFloaterWorldMap->childGetListInterface("friend combo");
+ if (!list) return;
+
+ const LLUUID& new_avatar_id = list->getCurrentID();
+ if (new_avatar_id.notNull())
+ {
+ LLString name;
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(gFloaterWorldMap, "friend combo");
+ if (combo) name = combo->getSimple();
+ self->trackAvatar(new_avatar_id, name);
+ onShowTargetBtn(self);
+ }
+}
+
+// static
+void LLFloaterWorldMap::updateSearchEnabled( LLUICtrl* ctrl, void* userdata )
+{
+ LLFloaterWorldMap *self = gFloaterWorldMap;
+ if (self->childHasKeyboardFocus("location") &&
+ self->childGetValue("location").asString().length() > 0)
+ {
+ self->setDefaultBtn("DoSearch");
+ }
+ else
+ {
+ self->setDefaultBtn("");
+ }
+}
+
+// static
+void LLFloaterWorldMap::onLocationCommit( void* userdata )
+{
+ LLFloaterWorldMap *self = gFloaterWorldMap;
+ if( !self || self->mIsClosing )
+ {
+ return;
+ }
+
+ self->clearLocationSelection(FALSE);
+ self->mCompletingRegionName = "";
+ self->mLastRegionName = "";
+
+ LLString str = self->childGetValue("location").asString();
+
+ LLString::toLower(str);
+ gFloaterWorldMap->mCompletingRegionName = str;
+ gWorldMap->mIsTrackingCommit = TRUE;
+ self->mExactMatch = FALSE;
+ if (str.length() >= 3)
+ {
+ gWorldMap->sendNamedRegionRequest(str);
+ }
+ else
+ {
+ str += "#";
+ gWorldMap->sendNamedRegionRequest(str);
+ }
+}
+
+
+// static
+void LLFloaterWorldMap::onClearBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->mTrackedStatus = LLTracker::TRACKING_NOTHING;
+ LLTracker::stopTracking((void *)(intptr_t)TRUE);
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+}
+
+// static
+void LLFloaterWorldMap::onFlyBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->fly();
+}
+
+void LLFloaterWorldMap::onShowTargetBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->centerOnTarget(TRUE);
+}
+
+void LLFloaterWorldMap::onShowAgentBtn(void* data)
+{
+ LLWorldMapView::setPan( 0, 0, FALSE); // FALSE == animate
+}
+
+// static
+void LLFloaterWorldMap::onTeleportBtn(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ self->teleport();
+}
+
+// static
+void LLFloaterWorldMap::onCopySLURL(void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(self->mSLURL));
+
+ LLString::format_map_t args;
+ args["[SLURL]"] = self->mSLURL;
+
+ LLAlertDialog::showXml("CopySLURL", args);
+}
+
+void LLFloaterWorldMap::onCheckEvents(LLUICtrl*, void* data)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*)data;
+ if(!self) return;
+ self->childSetEnabled("event_mature_chk", self->childGetValue("event_chk"));
+}
+
+// protected
+void LLFloaterWorldMap::centerOnTarget(BOOL animate)
+{
+ LLVector3d pos_global;
+ if(LLTracker::getTrackingStatus() != LLTracker::TRACKING_NOTHING)
+ {
+ LLVector3d tracked_position = LLTracker::getTrackedPositionGlobal();
+ //RN: tracker doesn't allow us to query completion, so we check for a tracking position of
+ // absolute zero, and keep trying in the draw loop
+ if (tracked_position.isExactlyZero())
+ {
+ mWaitingForTracker = TRUE;
+ return;
+ }
+ else
+ {
+ // We've got the position finally, so we're no longer busy. JC
+// getWindow()->decBusyCount();
+ pos_global = LLTracker::getTrackedPositionGlobal() - gAgent.getCameraPositionGlobal();
+ }
+ }
+ else if(gWorldMap->mIsTrackingUnknownLocation)
+ {
+ pos_global = gWorldMap->mUnknownLocation - gAgent.getCameraPositionGlobal();;
+ }
+ else
+ {
+ // default behavior = center on agent
+ pos_global.clearVec();
+ }
+
+ LLWorldMapView::setPan( -llfloor((F32)(pos_global.mdV[VX] * (F64)LLWorldMapView::sPixelsPerMeter)),
+ -llfloor((F32)(pos_global.mdV[VY] * (F64)LLWorldMapView::sPixelsPerMeter)),
+ !animate);
+ mWaitingForTracker = FALSE;
+}
+
+// protected
+void LLFloaterWorldMap::fly()
+{
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+
+ // Start the autopilot and close the floater,
+ // so we can see where we're flying
+ if (!pos_global.isExactlyZero())
+ {
+ gAgent.startAutoPilotGlobal( pos_global );
+ close();
+ }
+ else
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+}
+
+
+// protected
+void LLFloaterWorldMap::teleport()
+{
+ BOOL teleport_home = FALSE;
+ LLVector3d pos_global;
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if (LLTracker::TRACKING_AVATAR == tracking_status
+ && av_tracker.haveTrackingInfo() )
+ {
+ pos_global = av_tracker.getGlobalPos();
+ pos_global.mdV[VZ] = childGetValue("spin z");
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
+ {
+ teleport_home = TRUE;
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
+ LLUUID region_id;
+ if(landmark
+ && !landmark->getGlobalPos(pos_global)
+ && landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ }
+ else if ( LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ pos_global = LLTracker::getTrackedPositionGlobal();
+ }
+ else
+ {
+ make_ui_sound("UISndInvalidOp");
+ }
+
+ // Do the teleport, which will also close the floater
+ if (teleport_home)
+ {
+ gAgent.teleportHome();
+ }
+ else if (!pos_global.isExactlyZero())
+ {
+ if(LLTracker::TRACKING_LANDMARK == tracking_status)
+ {
+ gAgent.teleportViaLandmark(LLTracker::getTrackedLandmarkAssetID());
+ }
+ else
+ {
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+}
+
+// static
+void LLFloaterWorldMap::onGoToLandmarkDialog( S32 option, void* userdata )
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+ switch( option )
+ {
+ case 0:
+ self->teleportToLandmark();
+ break;
+ case 1:
+ self->flyToLandmark();
+ break;
+ default:
+ // nothing
+ break;
+ }
+}
+
+void LLFloaterWorldMap::flyToLandmark()
+{
+ LLVector3d destination_pos_global;
+ if( !LLTracker::getTrackedLandmarkAssetID().isNull() )
+ {
+ if (LLTracker::hasLandmarkPosition())
+ {
+ gAgent.startAutoPilotGlobal( LLTracker::getTrackedPositionGlobal() );
+ }
+ }
+}
+
+void LLFloaterWorldMap::teleportToLandmark()
+{
+ BOOL has_destination = FALSE;
+ LLUUID destination_id; // Null means "home"
+
+ if( LLTracker::getTrackedLandmarkAssetID() == sHomeID )
+ {
+ has_destination = TRUE;
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset( LLTracker::getTrackedLandmarkAssetID() );
+ LLVector3d global_pos;
+ if(landmark && landmark->getGlobalPos(global_pos))
+ {
+ destination_id = LLTracker::getTrackedLandmarkAssetID();
+ has_destination = TRUE;
+ }
+ else if(landmark)
+ {
+ // pop up an anonymous request request.
+ LLUUID region_id;
+ if(landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ }
+
+ if( has_destination )
+ {
+ gAgent.teleportViaLandmark( destination_id );
+ }
+}
+
+
+void LLFloaterWorldMap::teleportToAvatar()
+{
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if(av_tracker.haveTrackingInfo())
+ {
+ LLVector3d pos_global = av_tracker.getGlobalPos();
+ gAgent.teleportViaLocation( pos_global );
+ }
+}
+
+
+void LLFloaterWorldMap::flyToAvatar()
+{
+ if( LLAvatarTracker::instance().haveTrackingInfo() )
+ {
+ gAgent.startAutoPilotGlobal( LLAvatarTracker::instance().getGlobalPos() );
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitBackground(void* userdata, bool from_click)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+
+ // Find my index
+ S32 index = self->mTabs->getCurrentPanelIndex();
+
+ if (gWorldMap)
+ {
+ gWorldMap->setCurrentLayer(index);
+ }
+}
+
+void LLFloaterWorldMap::updateSims(bool found_null_sim)
+{
+ if (mCompletingRegionName == "")
+ {
+ return;
+ }
+
+ LLCtrlListInterface *list = childGetListInterface("search_results");
+ if (!list) return;
+ list->operateOnAll(LLCtrlListInterface::OP_DELETE);
+
+ LLSD selected_value = list->getSimpleSelectedValue();
+
+ S32 name_length = mCompletingRegionName.length();
+
+ BOOL match_found = FALSE;
+ S32 num_results = 0;
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = gWorldMap->mSimInfoMap.begin(); it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ LLString sim_name = info->mName;
+ LLString sim_name_lower = sim_name;
+ LLString::toLower(sim_name_lower);
+
+ if (sim_name_lower.substr(0, name_length) == mCompletingRegionName)
+ {
+ if (gWorldMap->mIsTrackingCommit)
+ {
+ if (sim_name_lower == mCompletingRegionName)
+ {
+ selected_value = sim_name;
+ match_found = TRUE;
+ }
+ }
+
+ LLSD value;
+ value["id"] = sim_name;
+ value["columns"][0]["column"] = "sim_name";
+ value["columns"][0]["value"] = sim_name;
+ list->addElement(value);
+ num_results++;
+ }
+ }
+
+ list->selectByValue(selected_value);
+
+ if (found_null_sim)
+ {
+ mCompletingRegionName = "";
+ }
+
+ if (match_found)
+ {
+ mExactMatch = TRUE;
+ childSetFocus("search_results");
+ onCommitSearchResult(NULL, this);
+ }
+ else if (!mExactMatch && num_results > 0)
+ {
+ list->selectFirstItem(); // select first item by default
+ childSetFocus("search_results");
+ onCommitSearchResult(NULL, this);
+ }
+ else
+ {
+ list->addSimpleElement("None found.");
+ list->operateOnAll(LLCtrlListInterface::OP_DESELECT);
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitLocation(LLUICtrl* ctrl, void* userdata)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_LOCATION == tracking_status)
+ {
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ F64 local_x = self->childGetValue("spin x");
+ F64 local_y = self->childGetValue("spin y");
+ F64 local_z = self->childGetValue("spin z");
+ pos_global.mdV[VX] += -fmod(pos_global.mdV[VX], 256.0) + local_x;
+ pos_global.mdV[VY] += -fmod(pos_global.mdV[VY], 256.0) + local_y;
+ pos_global.mdV[VZ] = local_z;
+ self->trackLocation(pos_global);
+ }
+}
+
+// static
+void LLFloaterWorldMap::onCommitSearchResult(LLUICtrl*, void* userdata)
+{
+ LLFloaterWorldMap* self = (LLFloaterWorldMap*) userdata;
+
+ LLCtrlListInterface *list = self->childGetListInterface("search_results");
+ if (!list) return;
+
+ LLSD selected_value = list->getSimpleSelectedValue();
+ LLString sim_name = selected_value.asString();
+ if (sim_name.empty())
+ {
+ return;
+ }
+ LLString::toLower(sim_name);
+
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = gWorldMap->mSimInfoMap.begin(); it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ LLString info_sim_name = info->mName;
+ LLString::toLower(info_sim_name);
+
+ if (sim_name == info_sim_name)
+ {
+ LLVector3d pos_global = from_region_handle( info->mHandle );
+ F64 local_x = self->childGetValue("spin x");
+ F64 local_y = self->childGetValue("spin y");
+ F64 local_z = self->childGetValue("spin z");
+ pos_global.mdV[VX] += local_x;
+ pos_global.mdV[VY] += local_y;
+ pos_global.mdV[VZ] = local_z;
+
+ self->childSetValue("location", sim_name);
+ self->trackLocation(pos_global);
+ self->setDefaultBtn("Teleport");
+ break;
+ }
+ }
+
+ onShowTargetBtn(self);
+}
diff --git a/indra/newview/llfloaterworldmap.h b/indra/newview/llfloaterworldmap.h
new file mode 100644
index 0000000000..32112a995f
--- /dev/null
+++ b/indra/newview/llfloaterworldmap.h
@@ -0,0 +1,170 @@
+/**
+ * @file llfloaterworldmap.h
+ * @brief LLFloaterWorldMap class definition
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * Map of the entire world, with multiple background images,
+ * avatar tracking, teleportation by double-click, etc.
+ */
+
+#ifndef LL_LLFLOATERWORLDMAP_H
+#define LL_LLFLOATERWORLDMAP_H
+
+#include "lldarray.h"
+#include "llfloater.h"
+#include "llhudtext.h"
+#include "llmapimagetype.h"
+#include "lltracker.h"
+
+class LLEventInfo;
+class LLFriendObserver;
+class LLInventoryModel;
+class LLInventoryObserver;
+class LLItemInfo;
+class LLTabContainer;
+class LLWorldMapView;
+
+class LLFloaterWorldMap : public LLFloater
+{
+public:
+ LLFloaterWorldMap();
+ virtual ~LLFloaterWorldMap();
+
+ static void *createWorldMapView(void* data);
+ BOOL postBuild();
+
+ virtual void onClose(bool app_quitting);
+
+ static void show(void*, BOOL center_on_target );
+ static void reloadIcons(void*);
+ static void toggle(void*);
+ static void hide(void*);
+
+ virtual void reshape( S32 width, S32 height, BOOL called_from_parent = TRUE );
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void setVisible(BOOL visible);
+ virtual void draw();
+
+ // methods for dealing with inventory. The observe() method is
+ // called during program startup. inventoryUpdated() will be
+ // called by a helper object when an interesting change has
+ // occurred.
+ void observeInventory(LLInventoryModel* inventory);
+ void inventoryChanged();
+
+ // Calls for dealing with changes in friendship
+ void observeFriends();
+ void friendsChanged();
+
+ // tracking methods
+ void trackAvatar( const LLUUID& avatar_id, const LLString& name );
+ void trackLandmark( const LLUUID& landmark_item_id );
+ void trackLocation(const LLVector3d& pos);
+ void trackEvent(const LLItemInfo &event_info);
+ void trackGenericItem(const LLItemInfo &item);
+ void trackURL(const LLString& region_name, S32 x_coord, S32 y_coord, S32 z_coord);
+
+ static const LLUUID& getHomeID() { return sHomeID; }
+
+ // A z_attenuation of 0.0f collapses the distance into the X-Y plane
+ F32 getDistanceToDestination(const LLVector3d& pos_global, F32 z_attenuation = 0.5f) const;
+
+ void clearLocationSelection(BOOL clear_ui = FALSE);
+ void clearAvatarSelection(BOOL clear_ui = FALSE);
+ void clearLandmarkSelection(BOOL clear_ui = FALSE);
+
+ // Adjust the maximally zoomed out limit of the zoom slider so you can
+ // see the whole world, plus a little.
+ void adjustZoomSliderBounds();
+
+ // Catch changes in the sim list
+ void updateSims(bool found_null_sim);
+
+protected:
+ static void onPanBtn( void* userdata );
+
+ static void onGoHome(void* data);
+
+ static void onLandmarkComboPrearrange( LLUICtrl* ctrl, void* data );
+ static void onLandmarkComboCommit( LLUICtrl* ctrl, void* data );
+
+ static void onAvatarComboPrearrange( LLUICtrl* ctrl, void* data );
+ static void onAvatarComboCommit( LLUICtrl* ctrl, void* data );
+
+ static void onCommitBackground(void* data, bool from_click);
+
+ static void onClearBtn(void*);
+ static void onFlyBtn(void*);
+ static void onTeleportBtn(void*);
+ static void onShowTargetBtn(void*);
+ static void onShowAgentBtn(void*);
+ static void onCopySLURL(void*);
+
+ static void onCheckEvents(LLUICtrl* ctrl, void*);
+
+ void centerOnTarget(BOOL animate);
+ void updateLocation();
+
+ // fly to the tracked item, if there is one
+ void fly();
+
+ // teleport to the tracked item, if there is one
+ void teleport();
+
+ void buildLandmarkIDLists();
+ static void onGoToLandmarkDialog(S32 option,void* userdata);
+ void flyToLandmark();
+ void teleportToLandmark();
+ void setLandmarkVisited();
+
+ void buildAvatarIDList();
+ void flyToAvatar();
+ void teleportToAvatar();
+
+ static void updateSearchEnabled( LLUICtrl* ctrl, void* userdata );
+ static void onLocationCommit( void* userdata );
+ static void onCommitLocation( LLUICtrl* ctrl, void* userdata );
+ static void onCommitSearchResult( LLUICtrl* ctrl, void* userdata );
+
+ void cacheLandmarkPosition();
+
+protected:
+ LLTabContainerCommon* mTabs;
+
+ // Sets gMapScale, in pixels per region
+ F32 mCurZoomVal;
+ LLFrameTimer mZoomTimer;
+
+ LLDynamicArray<LLUUID> mLandmarkAssetIDList;
+ LLDynamicArray<LLUUID> mLandmarkItemIDList;
+ BOOL mHasLandmarkPosition;
+
+ static const LLUUID sHomeID;
+
+ LLInventoryModel* mInventory;
+ LLInventoryObserver* mInventoryObserver;
+ LLFriendObserver* mFriendObserver;
+
+ LLString mCompletingRegionName;
+ LLString mLastRegionName;
+ BOOL mWaitingForTracker;
+ BOOL mExactMatch;
+
+ BOOL mIsClosing;
+
+ LLVector3d mTrackedLocation;
+ LLTracker::ETrackingStatus mTrackedStatus;
+ LLString mTrackedSimName;
+ LLString mTrackedAvatarName;
+ LLString mSLURL;
+};
+
+extern LLFloaterWorldMap* gFloaterWorldMap;
+
+#endif
+
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp
new file mode 100644
index 0000000000..e9d7808fd2
--- /dev/null
+++ b/indra/newview/llfolderview.cpp
@@ -0,0 +1,4899 @@
+/**
+ * @file llfolderview.cpp
+ * @brief Implementation of the folder view collection of classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfolderview.h"
+
+#include <algorithm>
+
+#include "llviewercontrol.h"
+#include "lldbstrings.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llinventory.h"
+
+#include "llcallbacklist.h"
+#include "llinventoryclipboard.h" // *FIX: remove this!!!!
+#include "llinventoryview.h"// hacked in for the bonus context menu items.
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llresmgr.h"
+#include "llpreview.h"
+#include "llscrollcontainer.h" // hack to allow scrolling
+#include "lltooldraganddrop.h"
+#include "llui.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerjointattachment.h"
+#include "llviewermenu.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llfloaterproperties.h"
+
+//RN: HACK
+#include "llagent.h"
+#include "viewer.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 LEFT_PAD = 5;
+const S32 LEFT_INDENTATION = 13;
+const S32 ICON_PAD = 2;
+const S32 ICON_WIDTH = 16;
+const S32 TEXT_PAD = 1;
+const S32 ARROW_SIZE = 12;
+const S32 RENAME_WIDTH_PAD = 4;
+const S32 RENAME_HEIGHT_PAD = 6;
+const S32 AUTO_OPEN_STACK_DEPTH = 16;
+const S32 MIN_ITEM_WIDTH_VISIBLE = ICON_WIDTH + ICON_PAD + ARROW_SIZE + TEXT_PAD + /*first few characters*/ 40;
+const S32 MINIMUM_RENAMER_WIDTH = 80;
+const F32 FOLDER_CLOSE_TIME_CONSTANT = 0.02f;
+const F32 FOLDER_OPEN_TIME_CONSTANT = 0.03f;
+const S32 MAX_FOLDER_ITEM_OVERLAP = 2;
+
+F32 LLFolderView::sAutoOpenTime = 1.f;
+
+void delete_selected_item(void* user_data);
+void copy_selected_item(void* user_data);
+void open_selected_items(void* user_data);
+void properties_selected_items(void* user_data);
+void paste_items(void* user_data);
+void top_view_lost( LLView* handler );
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewItem
+///----------------------------------------------------------------------------
+
+// statics
+const LLFontGL* LLFolderViewItem::sFont = NULL;
+const LLFontGL* LLFolderViewItem::sSmallFont = NULL;
+LLColor4 LLFolderViewItem::sFgColor;
+LLColor4 LLFolderViewItem::sHighlightBgColor;
+LLColor4 LLFolderViewItem::sHighlightFgColor;
+LLColor4 LLFolderViewItem::sFilterBGColor;
+LLColor4 LLFolderViewItem::sFilterTextColor;
+
+// Default constructor
+LLFolderViewItem::LLFolderViewItem( const LLString& name, LLViewerImage* icon,
+ S32 creation_date,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ) :
+ LLUICtrl( name, LLRect(0, 0, 0, 0), TRUE, NULL, NULL, FOLLOWS_LEFT|FOLLOWS_TOP|FOLLOWS_RIGHT),
+ mLabel( name ),
+ mLabelWidth(0),
+ mCreationDate(creation_date),
+ mParentFolder( NULL ),
+ mListener( listener ),
+ mIsSelected( FALSE ),
+ mIsCurSelection( FALSE ),
+ mSelectPending(FALSE),
+ mLabelStyle( LLFontGL::NORMAL ),
+ mHasVisibleChildren(FALSE),
+ mIndentation(0),
+ mNumDescendantsSelected(0),
+ mFiltered(FALSE),
+ mLastFilterGeneration(-1),
+ mStringMatchOffset(LLString::npos),
+ mControlLabelRotation(0.f),
+ mRoot( root ),
+ mDragAndDropTarget(FALSE)
+{
+ setIcon(icon);
+ if( !LLFolderViewItem::sFont )
+ {
+ LLFolderViewItem::sFont = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ }
+
+ if (!LLFolderViewItem::sSmallFont)
+ {
+ LLFolderViewItem::sSmallFont = gResMgr->getRes( LLFONT_SMALL );
+ }
+
+ // HACK: Can't be set above because gSavedSettings might not be constructed.
+ LLFolderViewItem::sFgColor = gColors.getColor( "MenuItemEnabledColor" );
+ LLFolderViewItem::sHighlightBgColor = gColors.getColor( "MenuItemHighlightBgColor" );
+ LLFolderViewItem::sHighlightFgColor = gColors.getColor( "MenuItemHighlightFgColor" );
+ LLFolderViewItem::sFilterBGColor = gColors.getColor( "FilterBackgroundColor" );
+ LLFolderViewItem::sFilterTextColor = gColors.getColor( "FilterTextColor" );
+
+ mArrowImage = gImageList.getImage(LLUUID(gViewerArt.getString("folder_arrow.tga")), MIPMAP_FALSE, TRUE);
+ mBoxImage = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ refresh();
+ setTabStop(FALSE);
+}
+
+// Destroys the object
+LLFolderViewItem::~LLFolderViewItem( void )
+{
+ delete mListener;
+ mListener = NULL;
+ mArrowImage = NULL;
+ mBoxImage = NULL;
+}
+
+LLFolderView* LLFolderViewItem::getRoot()
+{
+ return mRoot;
+}
+
+// Returns true if this object is a child (or grandchild, etc.) of potential_ancestor.
+BOOL LLFolderViewItem::isDescendantOf( const LLFolderViewFolder* potential_ancestor )
+{
+ LLFolderViewItem* root = this;
+ while( root->mParentFolder )
+ {
+ if( root->mParentFolder == potential_ancestor )
+ {
+ return TRUE;
+ }
+ root = root->mParentFolder;
+ }
+ return FALSE;
+}
+
+LLFolderViewItem* LLFolderViewItem::getNextOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getNextFromChild( this, include_children );
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getNextFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit last item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+LLFolderViewItem* LLFolderViewItem::getPreviousOpenNode(BOOL include_children)
+{
+ if (!mParentFolder)
+ {
+ return NULL;
+ }
+
+ LLFolderViewItem* itemp = mParentFolder->getPreviousFromChild( this, include_children );
+ while(itemp && !itemp->getVisible())
+ {
+ LLFolderViewItem* next_itemp = itemp->mParentFolder->getPreviousFromChild( itemp, include_children );
+ if (itemp == next_itemp)
+ {
+ // hit first item
+ return itemp->getVisible() ? itemp : this;
+ }
+ itemp = next_itemp;
+ }
+
+ return itemp;
+}
+
+BOOL LLFolderViewItem::getFiltered()
+{
+ return mFiltered && mLastFilterGeneration >= mRoot->getFilter()->getMinRequiredGeneration();
+}
+
+BOOL LLFolderViewItem::getFiltered(S32 filter_generation)
+{
+ return mFiltered && mLastFilterGeneration >= filter_generation;
+}
+
+void LLFolderViewItem::setFiltered(BOOL filtered, S32 filter_generation)
+{
+ mFiltered = filtered;
+ mLastFilterGeneration = filter_generation;
+}
+
+void LLFolderViewItem::setIcon(LLViewerImage* icon)
+{
+ mIcon = icon;
+ if (mIcon)
+ {
+ mIcon->setBoostLevel(LLViewerImage::BOOST_UI);
+ }
+}
+
+// refresh information from the listener
+void LLFolderViewItem::refresh()
+{
+ if(mListener)
+ {
+ const char* label = mListener->getDisplayName().c_str();
+ mLabel = label ? label : "";
+ setIcon(mListener->getIcon());
+ U32 creation_date = mListener->getCreationDate();
+ if (mCreationDate != creation_date)
+ {
+ mCreationDate = mListener->getCreationDate();
+ dirtyFilter();
+ }
+ mLabelStyle = mListener->getLabelStyle();
+ mLabelSuffix = mListener->getLabelSuffix();
+
+ LLString searchable_label(mLabel);
+ searchable_label.append(mLabelSuffix);
+ LLString::toUpper(searchable_label);
+
+ if (mSearchableLabel.compare(searchable_label))
+ {
+ mSearchableLabel.assign(searchable_label);
+ dirtyFilter();
+ // some part of label has changed, so overall width has potentially changed
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ S32 label_width = sFont->getWidth(mLabel);
+ if( mLabelSuffix.size() )
+ {
+ label_width += sFont->getWidth( mLabelSuffix );
+ }
+
+ mLabelWidth = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + label_width;
+ }
+}
+
+void LLFolderViewItem::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
+{
+ functor(mListener);
+}
+
+// This function is called when items are added or view filters change. It's
+// implemented here but called by derived classes when folding the
+// views.
+void LLFolderViewItem::filterFromRoot( void )
+{
+ LLFolderViewItem* root = getRoot();
+
+ root->filter(*((LLFolderView*)root)->getFilter());
+}
+
+// This function is called when the folder view is dirty. It's
+// implemented here but called by derived classes when folding the
+// views.
+void LLFolderViewItem::arrangeFromRoot()
+{
+ LLFolderViewItem* root = getRoot();
+
+ S32 height = 0;
+ S32 width = 0;
+ root->arrange( &width, &height, 0 );
+}
+
+// This function clears the currently selected item, and records the
+// specified selected item appropriately for display and use in the
+// UI. If open is TRUE, then folders are opened up along the way to
+// the selection.
+void LLFolderViewItem::setSelectionFromRoot(LLFolderViewItem* selection,
+ BOOL open,
+ BOOL take_keyboard_focus)
+{
+ getRoot()->setSelection(selection, open, take_keyboard_focus);
+}
+
+// helper function to change the selection from the root.
+void LLFolderViewItem::changeSelectionFromRoot(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ getRoot()->changeSelection(selection, selected);
+}
+
+void LLFolderViewItem::extendSelectionFromRoot(LLFolderViewItem* selection)
+{
+ LLDynamicArray<LLFolderViewItem*> selected_items;
+
+ getRoot()->extendSelection(selection, NULL, selected_items);
+}
+
+EWidgetType LLFolderViewItem::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_ITEM;
+}
+
+LLString LLFolderViewItem::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_ITEM_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewItem::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return FALSE;
+ }
+ mParentFolder = folder;
+ root->addItemID(getListener()->getUUID(), this);
+ return folder->addItem(this);
+}
+
+
+// Finds width and height of this object and it's children. Also
+// makes sure that this view and it's children are the right size.
+S32 LLFolderViewItem::arrange( S32* width, S32* height, S32 filter_generation)
+{
+ mIndentation = mParentFolder ? mParentFolder->getIndentation() + LEFT_INDENTATION : 0;
+ *width = llmax(*width, mLabelWidth + mIndentation);
+ *height = getItemHeight();
+ return *height;
+}
+
+S32 LLFolderViewItem::getItemHeight()
+{
+ S32 icon_height = mIcon->getHeight();
+ S32 label_height = llround(sFont->getLineHeight());
+ return llmax( icon_height, label_height ) + ICON_PAD;
+}
+
+void LLFolderViewItem::filter( LLInventoryFilter& filter)
+{
+ BOOL filtered = mListener && filter.check(this);
+
+ // if our visibility will change as a result of this filter, then
+ // we need to be rearranged in our parent folder
+ if (getVisible() != filtered)
+ {
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+ }
+
+ setFiltered(filtered, filter.getCurrentGeneration());
+ mStringMatchOffset = filter.getStringMatchOffset();
+ filter.decrementFilterCount();
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ }
+}
+
+void LLFolderViewItem::dirtyFilter()
+{
+ mLastFilterGeneration = -1;
+ // bubble up dirty flag all the way to root
+ if (getParentFolder())
+ {
+ getParentFolder()->setCompletedFilterGeneration(-1, TRUE);
+ }
+}
+
+// *FIX: This can be optimized a lot by simply recording that it is selected in
+// the appropriate places, and assuming that set selection means 'deselect' for a
+// leaf item. Do this optimization after multiple selection is implemented to
+// make sure it all plays nice together.
+BOOL LLFolderViewItem::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ mIsSelected = TRUE;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ }
+ else
+ {
+ mIsSelected = FALSE;
+ }
+ return mIsSelected;
+}
+
+BOOL LLFolderViewItem::changeSelection(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ if(selection == this && mIsSelected != selected)
+ {
+ mIsSelected = selected;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderViewItem::recursiveDeselect(BOOL deselect_self)
+{
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+}
+
+
+BOOL LLFolderViewItem::isMovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemMovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLFolderViewItem::isRemovable()
+{
+ if( mListener )
+ {
+ return mListener->isItemRemovable();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+void LLFolderViewItem::destroyView()
+{
+ if (mParentFolder)
+ {
+ // removeView deletes me
+ mParentFolder->removeView(this);
+ }
+}
+
+// Call through to the viewed object and return true if it can be
+// removed.
+//BOOL LLFolderViewItem::removeRecursively(BOOL single_item)
+BOOL LLFolderViewItem::remove()
+{
+ if(!isRemovable())
+ {
+ return FALSE;
+ }
+ if(mListener)
+ {
+ return mListener->removeItem();
+ }
+ return TRUE;
+}
+
+// Build an appropriate context menu for the item.
+void LLFolderViewItem::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ if(mListener)
+ {
+ mListener->buildContextMenu(menu, flags);
+ }
+}
+
+void LLFolderViewItem::open( void )
+{
+ if( mListener )
+ {
+ mListener->openItem();
+ }
+}
+
+void LLFolderViewItem::preview( void )
+{
+ if (mListener)
+ {
+ mListener->previewItem();
+ }
+}
+
+void LLFolderViewItem::rename(const LLString& new_name)
+{
+ if( !new_name.empty() )
+ {
+ mLabel = new_name.c_str();
+ BOOL is_renamed = TRUE;
+ if( mListener )
+ {
+ is_renamed = mListener->renameItem(new_name);
+ }
+ if(mParentFolder && is_renamed)
+ {
+ mParentFolder->resort(this);
+ }
+ //refresh();
+ }
+}
+
+const LLString& LLFolderViewItem::getSearchableLabel() const
+{
+ return mSearchableLabel;
+}
+
+const LLString& LLFolderViewItem::getName( void ) const
+{
+ if(mListener)
+ {
+ return mListener->getName();
+ }
+ return mLabel;
+}
+
+LLFolderViewFolder* LLFolderViewItem::getParentFolder( void )
+{
+ return mParentFolder;
+}
+
+LLFolderViewEventListener* LLFolderViewItem::getListener( void )
+{
+ return mListener;
+}
+
+// LLView functionality
+BOOL LLFolderViewItem::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ if(!mIsSelected)
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ gViewerWindow->setMouseCapture( this, NULL );
+
+ if (!mIsSelected)
+ {
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ make_ui_sound("UISndClick");
+ }
+ else
+ {
+ mSelectPending = TRUE;
+ }
+
+ if( isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart( screen_x, screen_y );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleHover( S32 x, S32 y, MASK mask )
+{
+ if( gViewerWindow->hasMouseCapture( this ) && isMovable() )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ BOOL can_drag = TRUE;
+ if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
+ {
+ LLFolderView* root = getRoot();
+
+ if(root->getCurSelectedItem())
+ {
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_WORLD;
+
+ //FIXME: push this into listener and remove dependency on llagent
+ if(mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gAgent.getInventoryRootID()))
+ {
+ src = LLToolDragAndDrop::SOURCE_AGENT;
+ }
+ else if (mListener && gInventory.isObjectDescendentOf(mListener->getUUID(), gInventoryLibraryRoot))
+ {
+ src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ }
+
+ can_drag = root->startDrag(src);
+ if (can_drag)
+ {
+ // if (mListener) mListener->startDrag();
+ // RN: when starting drag and drop, clear out last auto-open
+ root->autoOpenTest(NULL);
+ root->setShowSelectionContext(TRUE);
+
+ // Release keyboard focus, so that if stuff is dropped into the
+ // world, pressing the delete key won't blow away the inventory
+ // item.
+ gViewerWindow->setKeyboardFocus(NULL, NULL);
+
+ return gToolDragAndDrop->handleHover( x, y, mask );
+ }
+ }
+ }
+
+ if (can_drag)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
+ }
+ return TRUE;
+ }
+ else
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ // let parent handle this then...
+ return FALSE;
+ }
+}
+
+
+BOOL LLFolderViewItem::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ preview();
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (getParent())
+ {
+ return getParent()->handleScrollWheel(x, y, clicks);
+ }
+ return FALSE;
+}
+
+BOOL LLFolderViewItem::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ // if mouse hasn't moved since mouse down...
+ if ( pointInView(x, y) && mSelectPending )
+ {
+ //...then select
+ if(mask & MASK_CONTROL)
+ {
+ changeSelectionFromRoot(this, !mIsSelected);
+ }
+ else if (mask & MASK_SHIFT)
+ {
+ extendSelectionFromRoot(this);
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ }
+ }
+
+ mSelectPending = FALSE;
+
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ getRoot()->setShowSelectionContext(FALSE);
+ gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+ return TRUE;
+}
+
+BOOL LLFolderViewItem::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = FALSE;
+ BOOL handled = FALSE;
+ if(mListener)
+ {
+ accepted = mListener->dragOrDrop(mask,drop,cargo_type,cargo_data);
+ handled = accepted;
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ if(mParentFolder && !handled)
+ {
+ handled = mParentFolder->handleDragAndDropFromChild(mask,drop,cargo_type,cargo_data,accept,tooltip_msg);
+ }
+ if (handled)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewItem" << llendl;
+ }
+
+ return handled;
+}
+
+
+void LLFolderViewItem::draw()
+{
+ if( getVisible() )
+ {
+ bool possibly_has_children = false;
+ bool up_to_date = mListener && mListener->isUpToDate();
+ if((up_to_date && hasVisibleChildren() ) || // we fetched our children and some of them have passed the filter...
+ (!up_to_date && mListener && mListener->hasChildren())) // ...or we know we have children but haven't fetched them (doesn't obey filter)
+ {
+ possibly_has_children = true;
+ }
+ if(/*mControlLabel[0] != '\0' && */possibly_has_children)
+ {
+ LLGLSTexture gls_texture;
+ if (mArrowImage)
+ {
+ gl_draw_scaled_rotated_image(mIndentation, mRect.getHeight() - ARROW_SIZE - TEXT_PAD,
+ ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, mArrowImage, sFgColor);
+ }
+ }
+
+ F32 text_left = (F32)(ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD + mIndentation);
+
+ // If we have keyboard focus, draw selection filled
+ BOOL show_context = getRoot()->getShowSelectionContext();
+ BOOL filled = show_context || (gFocusMgr.getKeyboardFocus() == getRoot());
+
+ // always render "current" item, only render other selected items if
+ // mShowSingleSelection is FALSE
+ if( mIsSelected )
+ {
+ LLGLSNoTexture gls_no_texture;
+ LLColor4 bg_color = sHighlightBgColor;
+ //const S32 TRAILING_PAD = 5; // It just looks better with this.
+ if (!mIsCurSelection)
+ {
+ // do time-based fade of extra objects
+ F32 fade_time = getRoot()->getSelectionFadeElapsedTime();
+ if (getRoot()->getShowSingleSelection())
+ {
+ // fading out
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f);
+ }
+ else
+ {
+ // fading in
+ bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]);
+ }
+ }
+
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ bg_color, filled);
+ if (mIsCurSelection)
+ {
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightFgColor, FALSE);
+ }
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightFgColor, FALSE);
+ if (show_context)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, TRUE);
+ }
+ }
+ }
+ if (mDragAndDropTarget)
+ {
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(
+ 0,
+ mRect.getHeight(),
+ mRect.getWidth() - 2,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD),
+ sHighlightBgColor, FALSE);
+
+ if (mRect.getHeight() > llround(sFont->getLineHeight()) + ICON_PAD + 2)
+ {
+ gl_rect_2d(
+ 0,
+ llfloor(mRect.getHeight() - sFont->getLineHeight() - ICON_PAD) - 2,
+ mRect.getWidth() - 2,
+ 2,
+ sHighlightBgColor, FALSE);
+ }
+ mDragAndDropTarget = FALSE;
+ }
+
+
+ if(mIcon)
+ {
+ gl_draw_image(mIndentation + ARROW_SIZE + TEXT_PAD, mRect.getHeight() - mIcon->getHeight(), mIcon);
+ mIcon->addTextureStats( (F32)(mIcon->getWidth() * mIcon->getHeight()));
+ }
+
+ if (!mLabel.empty())
+ {
+ // highlight filtered text
+ BOOL debug_filters = getRoot()->getDebugFilters();
+ LLColor4 color = ( (mIsSelected && filled) ? sHighlightFgColor : sFgColor );
+ F32 right_x;
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+
+ if (debug_filters)
+ {
+ if (!getFiltered() && !possibly_has_children)
+ {
+ color.mV[VALPHA] *= 0.5f;
+ }
+
+ LLColor4 filter_color = mLastFilterGeneration >= getRoot()->getFilter()->getCurrentGeneration() ? LLColor4(0.5f, 0.8f, 0.5f, 1.f) : LLColor4(0.8f, 0.5f, 0.5f, 1.f);
+ sSmallFont->renderUTF8(mStatusText, 0, text_left, y, filter_color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ text_left = right_x;
+ }
+
+ sFont->renderUTF8( mLabel, 0, text_left, y, color,
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ if (!mLabelSuffix.empty())
+ {
+ sFont->renderUTF8( mLabelSuffix, 0, right_x, y, LLColor4(0.75f, 0.85f, 0.85f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ S32_MAX, S32_MAX, &right_x, FALSE );
+ }
+
+ if (mBoxImage.notNull() && mStringMatchOffset != LLString::npos)
+ {
+ // don't draw backgrounds for zero-length strings
+ S32 filter_string_length = mRoot->getFilterSubString().size();
+ if (filter_string_length > 0)
+ {
+ LLString combined_string = mLabel + mLabelSuffix;
+ S32 left = llround(text_left) + sFont->getWidth(combined_string, 0, mStringMatchOffset) - 1;
+ S32 right = left + sFont->getWidth(combined_string, mStringMatchOffset, filter_string_length) + 2;
+ S32 bottom = llfloor(mRect.getHeight() - sFont->getLineHeight() - 3);
+ S32 top = mRect.getHeight();
+
+ LLViewerImage::bindTexture(mBoxImage);
+ glColor4fv(sFilterBGColor.mV);
+ gl_segmented_rect_2d_tex(left, top, right, bottom, mBoxImage->getWidth(), mBoxImage->getHeight(), 16);
+ F32 match_string_left = text_left + sFont->getWidthF32(combined_string, 0, mStringMatchOffset);
+ F32 y = (F32)mRect.getHeight() - sFont->getLineHeight() - (F32)TEXT_PAD;
+ sFont->renderUTF8( combined_string, mStringMatchOffset, match_string_left, y,
+ sFilterTextColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle,
+ filter_string_length, S32_MAX, &right_x, FALSE );
+ }
+ }
+ }
+
+ if( sDebugRects )
+ {
+ drawDebugRect();
+ }
+ }
+ else if (mStatusText.size())
+ {
+ // just draw status text
+ sFont->renderUTF8( mStatusText, 0, 0, 1, sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewFolder
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFolderViewFolder::LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener ):
+ LLFolderViewItem( name, icon, 0, root, listener ), // 0 = no create time
+ mSortFunction(sort_item_name),
+ mIsOpen(FALSE),
+ mExpanderHighlighted(FALSE),
+ mCurHeight(0.f),
+ mTargetHeight(0.f),
+ mAutoOpenCountdown(0.f),
+ mSubtreeCreationDate(0),
+ mAmTrash(LLFolderViewFolder::UNKNOWN),
+ mLastArrangeGeneration( -1 ),
+ mLastCalculatedWidth(0),
+ mCompletedFilterGeneration(-1),
+ mMostFilteredDescendantGeneration(-1)
+{
+ mType = "(folder)";
+
+ //mItems.setInsertBefore( &sort_item_name );
+ //mFolders.setInsertBefore( &folder_insert_before );
+}
+
+// Destroys the object
+LLFolderViewFolder::~LLFolderViewFolder( void )
+{
+ // The LLView base class takes care of object destruction. make sure that we
+ // don't have mouse or keyboard focus
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ //mItems.reset();
+ //mItems.removeAllNodes();
+ //mFolders.removeAllNodes();
+}
+
+EWidgetType LLFolderViewFolder::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER;
+}
+
+LLString LLFolderViewFolder::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_FOLDER_TAG;
+}
+
+// addToFolder() returns TRUE if it succeeds. FALSE otherwise
+BOOL LLFolderViewFolder::addToFolder(LLFolderViewFolder* folder, LLFolderView* root)
+{
+ if (!folder)
+ {
+ return FALSE;
+ }
+ mParentFolder = folder;
+ root->addItemID(getListener()->getUUID(), this);
+ return folder->addFolder(this);
+}
+
+// Finds width and height of this object and it's children. Also
+// makes sure that this view and it's children are the right size.
+S32 LLFolderViewFolder::arrange( S32* width, S32* height, S32 filter_generation)
+{
+ mHasVisibleChildren = hasFilteredDescendants(filter_generation);
+
+ LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
+
+ // calculate height as a single item (without any children), and reshapes rectangle to match
+ LLFolderViewItem::arrange( width, height, filter_generation );
+
+ // clamp existing animated height so as to never get smaller than a single item
+ mCurHeight = llmax((F32)*height, mCurHeight);
+
+ // initialize running height value as height of single item in case we have no children
+ *height = getItemHeight();
+ F32 running_height = (F32)*height;
+ F32 target_height = (F32)*height;
+
+ // are my children visible?
+ if (needsArrange())
+ {
+ // set last arrange generation first, in case children are animating
+ // and need to be arranged again
+ mLastArrangeGeneration = mRoot->getArrangeGeneration();
+ if (mIsOpen)
+ {
+ // Add sizes of children
+ S32 parent_item_height = mRect.getHeight();
+
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+ for(; fit < fend; ++fit)
+ {
+ LLFolderViewFolder* folderp = (*fit);
+ if (getRoot()->getDebugFilters())
+ {
+ folderp->setVisible(TRUE);
+ }
+ else
+ {
+ folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
+ (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
+ }
+
+ if (folderp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - llround(running_height);
+
+ target_height += folderp->arrange( &child_width, &child_height, filter_generation );
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ folderp->setOrigin( 0, child_top - folderp->getRect().getHeight() );
+ }
+ }
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+ for(;iit < iend; ++iit)
+ {
+ LLFolderViewItem* itemp = (*iit);
+ if (getRoot()->getDebugFilters())
+ {
+ itemp->setVisible(TRUE);
+ }
+ else
+ {
+ itemp->setVisible(itemp->getFiltered(filter_generation));
+ }
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = *width;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - 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
+ itemp->reshape( itemp->getRect().getWidth(), child_height);
+
+ running_height += (F32)child_height;
+ *width = llmax(*width, child_width);
+ itemp->setOrigin( 0, child_top - itemp->getRect().getHeight() );
+ }
+ }
+ }
+
+ mTargetHeight = target_height;
+ // cache this width so next time we can just return it
+ mLastCalculatedWidth = *width;
+ }
+ else
+ {
+ // just use existing width
+ *width = mLastCalculatedWidth;
+ }
+
+ // animate current height towards target height
+ if (llabs(mCurHeight - mTargetHeight) > 1.f)
+ {
+ mCurHeight = lerp(mCurHeight, mTargetHeight, LLCriticalDamp::getInterpolant(mIsOpen ? FOLDER_OPEN_TIME_CONSTANT : FOLDER_CLOSE_TIME_CONSTANT));
+
+ requestArrange();
+
+ // hide child elements that fall out of current animated height
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // number of pixels that bottom of folder label is from top of parent folder
+ if (mRect.getHeight() - (*fit)->getRect().mTop + (*fit)->getItemHeight()
+ > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
+ {
+ // hide if beyond current folder height
+ (*fit)->setVisible(FALSE);
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ // number of pixels that bottom of item label is from top of parent folder
+ if (mRect.getHeight() - (*iit)->getRect().mBottom
+ > llround(mCurHeight) + MAX_FOLDER_ITEM_OVERLAP)
+ {
+ (*iit)->setVisible(FALSE);
+ }
+ }
+ }
+ else
+ {
+ mCurHeight = mTargetHeight;
+ }
+
+ // don't change width as this item is already as wide as its parent folder
+ reshape(mRect.getWidth(),llround(mCurHeight));
+
+ // pass current height value back to parent
+ *height = llround(mCurHeight);
+
+ return llround(mTargetHeight);
+}
+
+BOOL LLFolderViewFolder::needsArrange()
+{
+ return mLastArrangeGeneration < mRoot->getArrangeGeneration();
+}
+
+void LLFolderViewFolder::setCompletedFilterGeneration(S32 generation, BOOL recurse_up)
+{
+ mMostFilteredDescendantGeneration = llmin(mMostFilteredDescendantGeneration, generation);
+ mCompletedFilterGeneration = generation;
+ // only aggregate up if we are a lower (older) value
+ if (recurse_up && mParentFolder && generation < mParentFolder->getCompletedFilterGeneration())
+ {
+ mParentFolder->setCompletedFilterGeneration(generation, TRUE);
+ }
+}
+
+void LLFolderViewFolder::filter( LLInventoryFilter& filter)
+{
+ S32 filter_generation = filter.getCurrentGeneration();
+ // if failed to pass filter newer than must_pass_generation
+ // you will automatically fail this time, so we only
+ // check against items that have passed the filter
+ S32 must_pass_generation = filter.getMustPassGeneration();
+
+ // if we have already been filtered against this generation, skip out
+ if (getCompletedFilterGeneration() >= filter_generation)
+ {
+ return;
+ }
+
+ // filter folder itself
+ if (getLastFilterGeneration() < filter_generation)
+ {
+ if (getLastFilterGeneration() >= must_pass_generation && // folder has been compared to a valid precursor filter
+ !mFiltered) // and did not pass the filter
+ {
+ // go ahead and flag this folder as done
+ mLastFilterGeneration = filter_generation;
+ }
+ else
+ {
+ // filter self only on first pass through
+ LLFolderViewItem::filter( filter );
+ }
+ }
+
+ if (getRoot()->getDebugFilters())
+ {
+ mStatusText = llformat("%d", mLastFilterGeneration);
+ mStatusText += llformat("(%d)", mCompletedFilterGeneration);
+ mStatusText += llformat("+%d", mMostFilteredDescendantGeneration);
+ }
+
+ // all descendants have been filtered later than must pass generation
+ // but none passed
+ if(getCompletedFilterGeneration() >= must_pass_generation && !hasFilteredDescendants(must_pass_generation))
+ {
+ // don't traverse children if we've already filtered them since must_pass_generation
+ // and came back with nothing
+ return;
+ }
+
+ // we entered here with at least one filter iteration left
+ // check to see if we have any more before continuing on to children
+ if (filter.getFilterCount() < 0)
+ {
+ return;
+ }
+
+ // when applying a filter, matching folders get their contents downloaded first
+ if (getRoot()->isFilterActive() && getFiltered(filter.getMinRequiredGeneration()) && !gInventory.isCategoryComplete(mListener->getUUID()))
+ {
+ gInventory.startBackgroundFetch(mListener->getUUID());
+ }
+
+ // now query children
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ // have we run out of iterations this frame?
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+
+ // mMostFilteredDescendantGeneration might have been reset
+ // in which case we need to update it even for folders that
+ // don't need to be filtered anymore
+ if ((*fit)->getCompletedFilterGeneration() >= filter_generation)
+ {
+ // track latest generation to pass any child items
+ if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ // just skip it, it has already been filtered
+ continue;
+ }
+
+ // update this folders filter status (and children)
+ (*fit)->filter( filter );
+
+ // track latest generation to pass any child items
+ if ((*fit)->getFiltered() || (*fit)->hasFilteredDescendants(filter_generation))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ if (mRoot->needsAutoSelect())
+ {
+ (*fit)->setOpenArrangeRecursively(TRUE);
+ }
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if (filter.getFilterCount() < 0)
+ {
+ break;
+ }
+ if ((*iit)->getLastFilterGeneration() >= filter_generation)
+ {
+ if ((*iit)->getFiltered())
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ continue;
+ }
+
+ if ((*iit)->getLastFilterGeneration() >= must_pass_generation &&
+ !(*iit)->getFiltered(must_pass_generation))
+ {
+ // failed to pass an earlier filter that was a subset of the current one
+ // go ahead and flag this item as done
+ (*iit)->setFiltered(FALSE, filter_generation);
+ continue;
+ }
+
+ (*iit)->filter( filter );
+
+ if ((*iit)->getFiltered(filter.getMinRequiredGeneration()))
+ {
+ mMostFilteredDescendantGeneration = filter_generation;
+ }
+ }
+
+ // if we didn't use all filter iterations
+ // that means we filtered all of our descendants
+ // instead of exhausting the filter count for this frame
+ if (filter.getFilterCount() > 0)
+ {
+ // flag this folder as having completed filter pass for all descendants
+ setCompletedFilterGeneration(filter_generation, FALSE/*dont recurse up to root*/);
+ }
+}
+
+void LLFolderViewFolder::setFiltered(BOOL filtered, S32 filter_generation)
+{
+ // if this folder is now filtered, but wasn't before
+ // (it just passed)
+ if (filtered && !mFiltered)
+ {
+ // reset current height, because last time we drew it
+ // it might have had more visible items than now
+ mCurHeight = 0.f;
+ }
+
+ LLFolderViewItem::setFiltered(filtered, filter_generation);
+}
+
+void LLFolderViewFolder::dirtyFilter()
+{
+ // we're a folder, so invalidate our completed generation
+ setCompletedFilterGeneration(-1, FALSE);
+ LLFolderViewItem::dirtyFilter();
+}
+
+BOOL LLFolderViewFolder::hasFilteredDescendants()
+{
+ return mMostFilteredDescendantGeneration >= mRoot->getFilter()->getCurrentGeneration();
+}
+
+// Passes selection information on to children and record selection
+// information if necessary.
+BOOL LLFolderViewFolder::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ BOOL rv = FALSE;
+ if( selection == this )
+ {
+ mIsSelected = TRUE;
+ if(mListener)
+ {
+ mListener->selectItem();
+ }
+ rv = TRUE;
+ }
+ else
+ {
+ mIsSelected = FALSE;
+ rv = FALSE;
+ }
+ BOOL child_selected = FALSE;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->setSelection(selection, open, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ mNumDescendantsSelected++;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->setSelection(selection, open, take_keyboard_focus))
+ {
+ rv = TRUE;
+ child_selected = TRUE;
+ mNumDescendantsSelected++;
+ }
+ }
+ if(open && child_selected)
+ {
+ setOpenArrangeRecursively(TRUE);
+ }
+ return rv;
+}
+
+// This method is used to change the selection of an item. If
+// selection is 'this', then note selection as true. Returns TRUE
+// if this or a child is now selected.
+BOOL LLFolderViewFolder::changeSelection(LLFolderViewItem* selection,
+ BOOL selected)
+{
+ BOOL rv = FALSE;
+ if(selection == this)
+ {
+ mIsSelected = selected;
+ if(mListener && selected)
+ {
+ mListener->selectItem();
+ }
+ rv = TRUE;
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if((*fit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if((*iit)->changeSelection(selection, selected))
+ {
+ if (selected)
+ {
+ mNumDescendantsSelected++;
+ }
+ else
+ {
+ mNumDescendantsSelected--;
+ }
+ rv = TRUE;
+ }
+ }
+ return rv;
+}
+
+S32 LLFolderViewFolder::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& selected_items)
+{
+ S32 num_selected = 0;
+
+ // pass on to child folders first
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ num_selected += (*fit)->extendSelection(selection, last_selected, selected_items);
+ mNumDescendantsSelected += num_selected;
+ }
+
+ // handle selection of our immediate children...
+ BOOL reverse_select = FALSE;
+ BOOL found_last_selected = FALSE;
+ BOOL found_selection = FALSE;
+ LLDynamicArray<LLFolderViewItem*> items_to_select;
+ LLFolderViewItem* item;
+
+ //...folders first...
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ item = (*fit);
+ if(item == selection)
+ {
+ found_selection = TRUE;
+ }
+ else if (item == last_selected)
+ {
+ found_last_selected = TRUE;
+ if (found_selection)
+ {
+ reverse_select = TRUE;
+ }
+ }
+
+ if (found_selection || found_last_selected)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+
+ if (!(found_selection && found_last_selected))
+ {
+ //,,,then items
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ item = (*iit);
+ if(item == selection)
+ {
+ found_selection = TRUE;
+ }
+ else if (item == last_selected)
+ {
+ found_last_selected = TRUE;
+ if (found_selection)
+ {
+ reverse_select = TRUE;
+ }
+ }
+
+ if (found_selection || found_last_selected)
+ {
+ // deselect currently selected items so they can be pushed back on queue
+ if (item->isSelected())
+ {
+ item->changeSelection(item, FALSE);
+ }
+ items_to_select.put(item);
+ }
+
+ if (found_selection && found_last_selected)
+ {
+ break;
+ }
+ }
+ }
+
+ if (found_last_selected && found_selection)
+ {
+ // we have a complete selection inside this folder
+ for (S32 index = reverse_select ? items_to_select.getLength() - 1 : 0;
+ reverse_select ? index >= 0 : index < items_to_select.getLength(); reverse_select ? index-- : index++)
+ {
+ LLFolderViewItem* item = items_to_select[index];
+ if (item->changeSelection(item, TRUE))
+ {
+ selected_items.put(item);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+ }
+ else if (found_selection)
+ {
+ // last selection was not in this folder....go ahead and select just the new item
+ if (selection->changeSelection(selection, TRUE))
+ {
+ selected_items.put(selection);
+ mNumDescendantsSelected++;
+ num_selected++;
+ }
+ }
+
+ return num_selected;
+}
+
+void LLFolderViewFolder::recursiveDeselect(BOOL deselect_self)
+{
+ // make sure we don't have negative values
+ llassert(mNumDescendantsSelected >= 0);
+
+ if (mIsSelected && deselect_self)
+ {
+ mIsSelected = FALSE;
+
+ // update ancestors' count of selected descendents
+ LLFolderViewFolder* parent_folder = getParentFolder();
+ while(parent_folder)
+ {
+ parent_folder->mNumDescendantsSelected--;
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+
+ if (0 == mNumDescendantsSelected)
+ {
+ return;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* item = (*iit);
+ item->recursiveDeselect(TRUE);
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ LLFolderViewFolder* folder = (*fit);
+ folder->recursiveDeselect(TRUE);
+ }
+
+}
+
+void LLFolderViewFolder::destroyView()
+{
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* item = (*iit);
+ getRoot()->removeItemID(item->getListener()->getUUID());
+ }
+
+ std::for_each(mItems.begin(), mItems.end(), DeletePointer());
+ mItems.clear();
+
+ while (!mFolders.empty())
+ {
+ LLFolderViewFolder *folderp = mFolders.back();
+ folderp->destroyView();
+ }
+
+ mFolders.clear();
+
+ deleteAllChildren();
+
+ if (mParentFolder)
+ {
+ mParentFolder->removeView(this);
+ }
+}
+
+// remove the specified item (and any children) if possible. Return
+// TRUE if the item was deleted.
+BOOL LLFolderViewFolder::removeItem(LLFolderViewItem* item)
+{
+ if(item->remove())
+ {
+ removeView(item);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// simply remove the view (and any children) Don't bother telling the
+// listeners.
+void LLFolderViewFolder::removeView(LLFolderViewItem* item)
+{
+ if (!item)
+ {
+ return;
+ }
+ // deselect without traversing hierarchy
+ item->recursiveDeselect(TRUE);
+ getRoot()->removeFromSelectionList(item);
+ extractItem(item);
+ delete item;
+}
+
+// extractItem() removes the specified item from the folder, but
+// doesn't delete it.
+void LLFolderViewFolder::extractItem( LLFolderViewItem* item )
+{
+ items_t::iterator it = std::find(mItems.begin(), mItems.end(), item);
+ if(it == mItems.end())
+ {
+ // This is an evil downcast. However, it's only doing
+ // pointer comparison to find if (which it should be ) the
+ // item is in the container, so it's pretty safe.
+ LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
+ folders_t::iterator ft;
+ ft = std::find(mFolders.begin(), mFolders.end(), f);
+ if(ft != mFolders.end())
+ {
+ mFolders.erase(ft);
+ }
+ }
+ else
+ {
+ mItems.erase(it);
+ }
+ //item has been removed, need to update filter
+ dirtyFilter();
+ //because an item is going away regardless of filter status, force rearrange
+ requestArrange();
+ getRoot()->removeItemID(item->getListener()->getUUID());
+ removeChild(item);
+}
+
+// This function is called by a child that needs to be resorted.
+// This is only called for renaming an object because it won't work for date
+void LLFolderViewFolder::resort(LLFolderViewItem* item)
+{
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ //if(mItems.removeData(item))
+ //{
+ // mItems.addDataSorted(item);
+ //}
+ //else
+ //{
+ // // This is an evil downcast. However, it's only doing
+ // // pointer comparison to find if (which it should be ) the
+ // // item is in the container, so it's pretty safe.
+ // LLFolderViewFolder* f = reinterpret_cast<LLFolderViewFolder*>(item);
+ // if(mFolders.removeData(f))
+ // {
+ // mFolders.addDataSorted(f);
+ // }
+ //}
+}
+
+bool LLFolderViewFolder::isTrash()
+{
+ if (mAmTrash == LLFolderViewFolder::UNKNOWN)
+ {
+ mAmTrash = mListener->getUUID() == gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH) ? LLFolderViewFolder::TRASH : LLFolderViewFolder::NOT_TRASH;
+ }
+ return mAmTrash == LLFolderViewFolder::TRASH;
+}
+
+void LLFolderViewFolder::sortBy(U32 order)
+{
+ BOOL sort_order_changed = FALSE;
+ if (!(order & LLInventoryFilter::SO_DATE))
+ {
+ if (mSortFunction != sort_item_name)
+ {
+ mSortFunction = sort_item_name;
+ sort_order_changed = TRUE;
+ }
+ }
+ else
+ {
+ if (mSortFunction != sort_item_date)
+ {
+ mSortFunction = sort_item_date;
+ sort_order_changed = TRUE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->sortBy(order);
+ }
+ if (order & LLInventoryFilter::SO_FOLDERS_BY_NAME)
+ {
+ // sort folders by name if always by name
+ std::sort(mFolders.begin(), mFolders.end(), sort_item_name);
+ }
+ else
+ {
+ // sort folders by the default sort ordering
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+
+ // however, if we are at the root of the inventory and we are sorting by date
+ if (mListener->getUUID() == gAgent.getInventoryRootID() && order & LLInventoryFilter::SO_DATE)
+ {
+ // pull the trash folder and stick it on the end of the list
+ LLFolderViewFolder *t = NULL;
+ for (folders_t::iterator fit = mFolders.begin();
+ fit != mFolders.end(); ++fit)
+ {
+ if ((*fit)->isTrash())
+ {
+ t = *fit;
+ mFolders.erase(fit);
+ break;
+ }
+ }
+ if (t)
+ {
+ mFolders.push_back(t);
+ }
+ }
+ }
+ if (sort_order_changed)
+ {
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+ }
+
+ if (order & LLInventoryFilter::SO_DATE)
+ {
+ U32 latest = 0;
+
+ if (!mItems.empty())
+ {
+ LLFolderViewItem* item = *(mItems.begin());
+ latest = item->getCreationDate();
+ }
+
+ if (!mFolders.empty())
+ {
+ LLFolderViewFolder* folder = *(mFolders.begin());
+ if (folder->getCreationDate() > latest)
+ {
+ latest = folder->getCreationDate();
+ }
+ }
+ mSubtreeCreationDate = latest;
+ }
+}
+
+void LLFolderViewFolder::setItemSortFunction(sort_order_f ordering)
+{
+ mSortFunction = ordering;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setItemSortFunction(ordering);
+ }
+
+ std::sort(mFolders.begin(), mFolders.end(), *mSortFunction);
+ std::sort(mItems.begin(), mItems.end(), *mSortFunction);
+}
+
+BOOL LLFolderViewFolder::isMovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemMovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isMovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::isRemovable()
+{
+ if( mListener )
+ {
+ if( !(mListener->isItemRemovable()) )
+ {
+ return FALSE;
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ if(!(*iit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ if(!(*fit)->isRemovable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addItem(LLFolderViewItem* item)
+{
+ items_t::iterator it = std::lower_bound(
+ mItems.begin(),
+ mItems.end(),
+ item,
+ mSortFunction);
+ mItems.insert(it,item);
+ item->setRect(LLRect(0, 0, mRect.getWidth(), 0));
+ item->setVisible(FALSE);
+ addChild( item );
+ item->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+// this is an internal method used for adding items to folders.
+BOOL LLFolderViewFolder::addFolder(LLFolderViewFolder* folder)
+{
+ folders_t::iterator it = std::lower_bound(
+ mFolders.begin(),
+ mFolders.end(),
+ folder,
+ mSortFunction);
+ mFolders.insert(it,folder);
+ folder->setOrigin(0, 0);
+ folder->reshape(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ requestArrange();
+ return TRUE;
+}
+
+void LLFolderViewFolder::requestArrange()
+{
+ mLastArrangeGeneration = -1;
+ // flag all items up to root
+ if (mParentFolder)
+ {
+ mParentFolder->requestArrange();
+ }
+}
+
+void LLFolderViewFolder::toggleOpen()
+{
+ setOpen(!mIsOpen);
+}
+
+// Force a folder open or closed
+void LLFolderViewFolder::setOpen(BOOL open)
+{
+ setOpenArrangeRecursively(open);
+}
+
+void LLFolderViewFolder::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ BOOL was_open = mIsOpen;
+ mIsOpen = open;
+ if(!was_open && open)
+ {
+ if(mListener)
+ {
+ mListener->openItem();
+ }
+ }
+ if (recurse == RECURSE_DOWN || recurse == RECURSE_UP_DOWN)
+ {
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->setOpenArrangeRecursively(open, RECURSE_DOWN);
+ }
+ }
+ if (mParentFolder && (recurse == RECURSE_UP || recurse == RECURSE_UP_DOWN))
+ {
+ mParentFolder->setOpenArrangeRecursively(open, RECURSE_UP);
+ }
+
+ if (was_open != mIsOpen)
+ {
+ requestArrange();
+ }
+}
+
+BOOL LLFolderViewFolder::handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType c_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = mListener && mListener->dragOrDrop(mask,drop,c_type,cargo_data);
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ // drag and drop to child item, so clear pending auto-opens
+ getRoot()->autoOpenTest(NULL);
+
+ return TRUE;
+}
+
+void LLFolderViewFolder::open( void )
+{
+ toggleOpen();
+}
+
+void LLFolderViewFolder::applyFunctorRecursively(LLFolderViewFunctor& functor)
+{
+ functor.doFolder(this);
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->applyFunctorRecursively(functor);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ functor.doItem((*iit));
+ }
+}
+
+void LLFolderViewFolder::applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor)
+{
+ functor(mListener);
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->applyListenerFunctorRecursively(functor);
+ }
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ (*iit)->applyListenerFunctorRecursively(functor);
+ }
+}
+
+// LLView functionality
+BOOL LLFolderViewFolder::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLFolderView* root_view = getRoot();
+
+ BOOL handled = FALSE;
+ if(mIsOpen)
+ {
+ handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type,
+ cargo_data, accept, tooltip_msg) != NULL;
+ }
+
+ if (!handled)
+ {
+ BOOL accepted = mListener && mListener->dragOrDrop(mask, drop,cargo_type,cargo_data);
+
+ if (accepted)
+ {
+ mDragAndDropTarget = TRUE;
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ if (!drop && accepted)
+ {
+ root_view->autoOpenTest(this);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderViewFolder" << llendl;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLFolderViewFolder::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( getVisible() )
+ {
+ // fetch contents of this folder, as context menu can depend on contents
+ // still, user would have to open context menu again to see the changes
+ gInventory.fetchDescendentsOf(mListener->getUUID());
+
+ if( mIsOpen )
+ {
+ handled = childrenHandleRightMouseDown( x, y, mask ) != NULL;
+ }
+ if (!handled)
+ {
+ handled = LLFolderViewItem::handleRightMouseDown( x, y, mask );
+ }
+ }
+ return handled;
+}
+
+
+BOOL LLFolderViewFolder::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLView::handleHover(x, y, mask);
+
+ if (!handled)
+ {
+ // this doesn't do child processing
+ handled = LLFolderViewItem::handleHover(x, y, mask);
+ }
+
+ //if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD && y > mRect.getHeight() - )
+ //{
+ // gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ // mExpanderHighlighted = TRUE;
+ // handled = TRUE;
+ //}
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ BOOL handled = FALSE;
+ if( mIsOpen )
+ {
+ handled = childrenHandleMouseDown(x,y,mask) != NULL;
+ }
+ if( !handled )
+ {
+ if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
+ {
+ toggleOpen();
+ handled = TRUE;
+ }
+ else
+ {
+ // do normal selection logic
+ handled = LLFolderViewItem::handleMouseDown(x, y, mask);
+ }
+ }
+
+ return handled;
+}
+
+BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+ BOOL rv = false;
+ if( mIsOpen )
+ {
+ rv = childrenHandleDoubleClick( x, y, mask ) != NULL;
+ }
+ if( !rv )
+ {
+ if(x < LEFT_INDENTATION + mIndentation && x > mIndentation - LEFT_PAD)
+ {
+ // don't select when user double-clicks plus sign
+ // so as not to contradict single-click behavior
+ toggleOpen();
+ }
+ else
+ {
+ setSelectionFromRoot(this, FALSE);
+ toggleOpen();
+ }
+ return TRUE;
+ }
+ return rv;
+}
+
+void LLFolderViewFolder::draw()
+{
+ if (mAutoOpenCountdown != 0.f)
+ {
+ mControlLabelRotation = mAutoOpenCountdown * -90.f;
+ }
+ else if (mIsOpen)
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f));
+ }
+ else
+ {
+ mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f));
+ }
+
+ LLFolderViewItem::draw();
+ if( mIsOpen )
+ {
+ LLView::draw();
+ }
+
+// if (mExpanderHighlighted)
+// {
+// gl_rect_2d(mIndentation - TEXT_PAD, llfloor(mRect.getHeight() - TEXT_PAD), mIndentation + sFont->getWidth(mControlLabel) + TEXT_PAD, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, FALSE);
+// //sFont->renderUTF8( mControlLabel, 0, mIndentation, llfloor(mRect.getHeight() - sFont->getLineHeight() - TEXT_PAD), sFgColor, LLFontGL::LEFT, LLFontGL::BOTTOM, mLabelStyle, S32_MAX, S32_MAX, NULL, FALSE );
+// }
+ mExpanderHighlighted = FALSE;
+}
+
+U32 LLFolderViewFolder::getCreationDate() const
+{
+ return llmax<U32>(mCreationDate, mSubtreeCreationDate);
+}
+
+
+// this does prefix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getNextFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at beginning
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::iterator fit = mFolders.begin();
+ folders_t::iterator fend = mFolders.end();
+
+ items_t::iterator iit = mItems.begin();
+ items_t::iterator iend = mItems.end();
+
+ // if not trivially starting at the beginning, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among folders, since they are always above items
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // if we are on downwards traversal
+ if (include_children && (*fit)->isOpen())
+ {
+ // look for first descendant
+ return (*fit)->getNextFromChild(NULL, TRUE);
+ }
+ // otherwise advance to next folder
+ ++fit;
+ include_children = TRUE;
+ break;
+ }
+ }
+
+ // didn't find in folders? Check items...
+ if (!found_item)
+ {
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found folder, continue through folders
+ // searching for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ // turn on downwards traversal for next folder
+ ++fit;
+ }
+
+ if (fit != fend)
+ {
+ result = (*fit);
+ }
+ else
+ {
+ // otherwise, scan for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ // check to see if we have a valid item
+ if (iit != iend)
+ {
+ result = (*iit);
+ }
+ }
+
+ if( !result && mParentFolder )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // and skip children for this folder, as we've already discounted them
+ result = mParentFolder->getNextFromChild(this, FALSE);
+ }
+
+ return result;
+}
+
+// this does postfix traversal, as folders are listed above their contents
+LLFolderViewItem* LLFolderViewFolder::getPreviousFromChild( LLFolderViewItem* item, BOOL include_children )
+{
+ BOOL found_item = FALSE;
+
+ LLFolderViewItem* result = NULL;
+ // when not starting from a given item, start at end
+ if(item == NULL)
+ {
+ found_item = TRUE;
+ }
+
+ // find current item among children
+ folders_t::reverse_iterator fit = mFolders.rbegin();
+ folders_t::reverse_iterator fend = mFolders.rend();
+
+ items_t::reverse_iterator iit = mItems.rbegin();
+ items_t::reverse_iterator iend = mItems.rend();
+
+ // if not trivially starting at the end, we have to find the current item
+ if (!found_item)
+ {
+ // first, look among items, since they are always below the folders
+ for(; iit != iend; ++iit)
+ {
+ if(item == (*iit))
+ {
+ found_item = TRUE;
+ // point to next item
+ ++iit;
+ break;
+ }
+ }
+
+ // didn't find in items? Check folders...
+ if (!found_item)
+ {
+ for(; fit != fend; ++fit)
+ {
+ if(item == (*fit))
+ {
+ found_item = TRUE;
+ // point to next folder
+ ++fit;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!found_item)
+ {
+ // you should never call this method with an item that isn't a child
+ // so we should always find something
+ llassert(FALSE);
+ return NULL;
+ }
+
+ // at this point, either iit or fit point to a candidate "next" item
+ // if both are out of range, we need to punt up to our parent
+
+ // now, starting from found item, continue through items
+ // searching for next visible item
+ while(iit != iend && !(*iit)->getVisible())
+ {
+ ++iit;
+ }
+
+ if (iit != iend)
+ {
+ // we found an appropriate item
+ result = (*iit);
+ }
+ else
+ {
+ // otherwise, scan for next visible folder
+ while(fit != fend && !(*fit)->getVisible())
+ {
+ ++fit;
+ }
+
+ // check to see if we have a valid folder
+ if (fit != fend)
+ {
+ // try selecting child element of this folder
+ if ((*fit)->isOpen())
+ {
+ result = (*fit)->getPreviousFromChild(NULL);
+ }
+ else
+ {
+ result = (*fit);
+ }
+ }
+ }
+
+ if( !result )
+ {
+ // If there are no siblings or children to go to, recurse up one level in the tree
+ // which gets back to this folder, which will only be visited if it is a valid, visible item
+ result = this;
+ }
+
+ return result;
+}
+
+
+//---------------------------------------------------------------------------
+
+// Tells all folders in a folderview to sort their items
+// (and only their items, not folders) by a certain function.
+class LLSetItemSortFunction : public LLFolderViewFunctor
+{
+public:
+ LLSetItemSortFunction(sort_order_f ordering)
+ : mSortFunction(ordering) {}
+ virtual ~LLSetItemSortFunction() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ sort_order_f mSortFunction;
+};
+
+
+// Set the sort order.
+void LLSetItemSortFunction::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setItemSortFunction(mSortFunction);
+}
+
+// Do nothing.
+void LLSetItemSortFunction::doItem(LLFolderViewItem* item)
+{
+ return;
+}
+
+//---------------------------------------------------------------------------
+
+// Tells all folders in a folderview to close themselves
+// For efficiency, calls setOpenArrangeRecursively().
+// The calling function must then call:
+// LLFolderView* root = getRoot();
+// if( root )
+// {
+// root->arrange( NULL, NULL );
+// root->scrollToShowSelection();
+// }
+// to patch things up.
+class LLCloseAllFoldersFunctor : public LLFolderViewFunctor
+{
+public:
+ LLCloseAllFoldersFunctor(BOOL close) { mOpen = !close; }
+ virtual ~LLCloseAllFoldersFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder);
+ virtual void doItem(LLFolderViewItem* item);
+
+ BOOL mOpen;
+};
+
+
+// Set the sort order.
+void LLCloseAllFoldersFunctor::doFolder(LLFolderViewFolder* folder)
+{
+ folder->setOpenArrangeRecursively(mOpen);
+}
+
+// Do nothing.
+void LLCloseAllFoldersFunctor::doItem(LLFolderViewItem* item)
+{ }
+
+///----------------------------------------------------------------------------
+/// Class LLFolderView
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLFolderView::LLFolderView( const LLString& name, LLViewerImage* root_folder_icon,
+ const LLRect& rect, const LLUUID& source_id, LLView *parent_view ) :
+#if LL_WINDOWS
+#pragma warning( push )
+#pragma warning( disable : 4355 ) // warning C4355: 'this' : used in base member initializer list
+#endif
+ LLFolderViewFolder( name, root_folder_icon, this, NULL ),
+#if LL_WINDOWS
+#pragma warning( pop )
+#endif
+ mScrollContainer( NULL ),
+ mPopupMenuHandle( LLViewHandle::sDeadHandle ),
+ mAllowMultiSelect(TRUE),
+ mShowFolderHierarchy(FALSE),
+ mSourceID(source_id),
+ mRenameItem( NULL ),
+ mNeedsScroll( FALSE ),
+ mLastScrollItem( NULL ),
+ mNeedsAutoSelect( FALSE ),
+ mAutoSelectOverride(FALSE),
+ mDebugFilters(FALSE),
+ mSortOrder(LLInventoryFilter::SO_FOLDERS_BY_NAME), // This gets overridden by a pref immediately
+ mFilter(name),
+ mShowSelectionContext(FALSE),
+ mShowSingleSelection(FALSE),
+ mArrangeGeneration(0),
+ mSelectCallback(NULL),
+ mMinWidth(0),
+ mDragAndDropThisFrame(FALSE)
+{
+ LLRect new_rect(rect.mLeft, rect.mBottom + mRect.getHeight(), rect.mLeft + mRect.getWidth(), rect.mBottom);
+ setRect( rect );
+ reshape(rect.getWidth(), rect.getHeight());
+ mIsOpen = TRUE; // this view is always open.
+ mAutoOpenItems.setDepth(AUTO_OPEN_STACK_DEPTH);
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+ mKeyboardSelection = FALSE;
+ mIndentation = -LEFT_INDENTATION; // children start at indentation 0
+ gIdleCallbacks.addFunction(idle, this);
+
+ //clear label
+ // go ahead and render root folder as usual
+ // just make sure the label ("Inventory Folder") never shows up
+ mLabel = LLString::null;
+
+ mRenamer = new LLLineEditor("ren", mRect, "", sFont,
+ DB_INV_ITEM_NAME_STR_LEN,
+ &LLFolderView::commitRename,
+ NULL,
+ NULL,
+ this,
+ &LLLineEditor::prevalidatePrintableNotPipe,
+ LLViewBorder::BEVEL_NONE,
+ LLViewBorder::STYLE_LINE,
+ 2);
+ mRenamer->setWriteableBgColor(LLColor4::white);
+ // Escape is handled by reverting the rename, not commiting it (default behavior)
+ mRenamer->setCommitOnFocusLost(TRUE);
+ mRenamer->setVisible(FALSE);
+ addChild(mRenamer);
+
+ // make the popup menu available
+ LLMenuGL* menu = gUICtrlFactory->buildMenu("menu_inventory.xml", parent_view);
+ if (!menu)
+ {
+ menu = new LLMenuGL("");
+ }
+ menu->setBackgroundColor(gColors.getColor("MenuPopupBgColor"));
+ menu->setVisible(FALSE);
+ mPopupMenuHandle = menu->mViewHandle;
+
+ setTabStop(TRUE);
+}
+
+// Destroys the object
+LLFolderView::~LLFolderView( void )
+{
+ // The release focus call can potentially call the
+ // scrollcontainer, which can potentially be called with a partly
+ // destroyed scollcontainer. Just null it out here, and no worries
+ // about calling into the invalid scroll container.
+ // Same with the renamer.
+ mScrollContainer = NULL;
+ mRenameItem = NULL;
+ mRenamer = NULL;
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ gIdleCallbacks.deleteFunction(idle, this);
+
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+
+ if(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+
+ mAutoOpenItems.removeAllNodes();
+ clearSelection();
+ mItems.clear();
+ mFolders.clear();
+
+ mItemMap.clear();
+}
+
+EWidgetType LLFolderView::getWidgetType() const
+{
+ return WIDGET_TYPE_FOLDER_VIEW;
+}
+
+LLString LLFolderView::getWidgetTag() const
+{
+ return LL_FOLDER_VIEW_TAG;
+}
+
+BOOL LLFolderView::canFocusChildren() const
+{
+ return FALSE;
+}
+
+void LLFolderView::checkTreeResortForModelChanged()
+{
+ if (mSortOrder & LLInventoryFilter::SO_DATE && !(mSortOrder & LLInventoryFilter::SO_FOLDERS_BY_NAME))
+ {
+ // This is the case where something got added or removed. If we are date sorting
+ // everything including folders, then we need to rebuild the whole tree.
+ // Just set to something not SO_DATE to force the folder most resent date resort.
+ mSortOrder = mSortOrder & ~LLInventoryFilter::SO_DATE;
+ setSortOrder(mSortOrder | LLInventoryFilter::SO_DATE);
+ }
+}
+
+void LLFolderView::setSortOrder(U32 order)
+{
+ if (order != mSortOrder)
+ {
+ LLFastTimer t(LLFastTimer::FTM_SORT);
+ mSortOrder = order;
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ (*fit)->sortBy(order);
+ }
+
+ arrangeAll();
+ }
+}
+
+
+U32 LLFolderView::getSortOrder() const
+{
+ return mSortOrder;
+}
+
+BOOL LLFolderView::addFolder( LLFolderViewFolder* folder)
+{
+ // enforce sort order of My Inventory followed by Library
+ if (folder->getListener()->getUUID() == gInventoryLibraryRoot)
+ {
+ mFolders.push_back(folder);
+ }
+ else
+ {
+ mFolders.insert(mFolders.begin(), folder);
+ }
+ folder->setOrigin(0, 0);
+ folder->reshape(mRect.getWidth(), 0);
+ folder->setVisible(FALSE);
+ addChild( folder );
+ folder->dirtyFilter();
+ return TRUE;
+}
+
+void LLFolderView::closeAllFolders()
+{
+ // Close all the folders
+ setOpenArrangeRecursively(FALSE, LLFolderViewFolder::RECURSE_DOWN);
+}
+
+void LLFolderView::openFolder(const LLString& foldername)
+{
+ LLFolderViewFolder* inv = (LLFolderViewFolder*)getChildByName(foldername);
+ if (inv)
+ {
+ setSelection(inv, FALSE, FALSE);
+ inv->setOpen(TRUE);
+ }
+}
+
+void LLFolderView::setOpenArrangeRecursively(BOOL open, ERecurseType recurse)
+{
+ // call base class to do proper recursion
+ LLFolderViewFolder::setOpenArrangeRecursively(open, recurse);
+ // make sure root folder is always open
+ mIsOpen = TRUE;
+}
+
+// This view grows and shinks to enclose all of its children items and folders.
+S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation )
+{
+ LLFastTimer t2(LLFastTimer::FTM_ARRANGE);
+
+ filter_generation = mFilter.getMinRequiredGeneration();
+ mMinWidth = 0;
+
+ mHasVisibleChildren = hasFilteredDescendants(filter_generation);
+ // arrange always finishes, so optimistically set the arrange generation to the most current
+ mLastArrangeGeneration = mRoot->getArrangeGeneration();
+
+ LLInventoryFilter::EFolderShow show_folder_state = getRoot()->getShowFolderState();
+
+ S32 total_width = LEFT_PAD;
+ S32 running_height = mDebugFilters ? llceil(sSmallFont->getLineHeight()) : 0;
+ S32 target_height = running_height;
+ S32 parent_item_height = mRect.getHeight();
+
+ for (folders_t::iterator iter = mFolders.begin();
+ iter != mFolders.end();)
+ {
+ folders_t::iterator fit = iter++;
+ LLFolderViewFolder* folderp = (*fit);
+ if (getDebugFilters())
+ {
+ folderp->setVisible(TRUE);
+ }
+ else
+ {
+ folderp->setVisible(show_folder_state == LLInventoryFilter::SHOW_ALL_FOLDERS || // always show folders?
+ (folderp->getFiltered(filter_generation) || folderp->hasFilteredDescendants(filter_generation))); // passed filter or has descendants that passed filter
+ }
+ if (folderp->getVisible())
+ {
+ S32 child_height = 0;
+ S32 child_width = 0;
+ S32 child_top = parent_item_height - running_height;
+
+ target_height += folderp->arrange( &child_width, &child_height, filter_generation );
+
+ mMinWidth = llmax(mMinWidth, child_width);
+ total_width = llmax( total_width, child_width );
+ running_height += child_height;
+ folderp->setOrigin( ICON_PAD, child_top - (*fit)->getRect().getHeight() );
+ }
+ }
+
+ for (items_t::iterator iter = mItems.begin();
+ iter != mItems.end();)
+ {
+ items_t::iterator iit = iter++;
+ LLFolderViewItem* itemp = (*iit);
+ itemp->setVisible(itemp->getFiltered(filter_generation));
+
+ if (itemp->getVisible())
+ {
+ S32 child_width = 0;
+ S32 child_height = 0;
+ S32 child_top = parent_item_height - running_height;
+
+ target_height += itemp->arrange( &child_width, &child_height, filter_generation );
+ itemp->reshape(itemp->getRect().getWidth(), child_height);
+
+ mMinWidth = llmax(mMinWidth, child_width);
+ total_width = llmax( total_width, child_width );
+ running_height += child_height;
+ itemp->setOrigin( ICON_PAD, child_top - itemp->getRect().getHeight() );
+ }
+ }
+
+ S32 dummy_s32;
+ BOOL dummy_bool;
+ S32 min_width;
+ mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool);
+ reshape( llmax(min_width, total_width), running_height );
+
+ S32 new_min_width;
+ mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool);
+ if (new_min_width != min_width)
+ {
+ reshape( llmax(min_width, total_width), running_height );
+ }
+
+ mTargetHeight = (F32)target_height;
+ return llround(mTargetHeight);
+}
+
+const LLString LLFolderView::getFilterSubString(BOOL trim)
+{
+ return mFilter.getFilterSubString(trim);
+}
+
+void LLFolderView::filter( LLInventoryFilter& filter )
+{
+ LLFastTimer t2(LLFastTimer::FTM_FILTER);
+ filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000));
+
+ if (getCompletedFilterGeneration() < filter.getCurrentGeneration())
+ {
+ mFiltered = FALSE;
+ mMinWidth = 0;
+ LLFolderViewFolder::filter(filter);
+ }
+}
+
+void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ S32 min_width = 0;
+ S32 dummy_height;
+ BOOL dummy_bool;
+ if (mScrollContainer)
+ {
+ mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool);
+ }
+ width = llmax(mMinWidth, min_width);
+ LLView::reshape(width, height, called_from_parent);
+}
+
+void LLFolderView::addToSelectionList(LLFolderViewItem* item)
+{
+ if (item->isSelected())
+ {
+ removeFromSelectionList(item);
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+ item->setIsCurSelection(TRUE);
+ mSelectedItems.push_back(item);
+}
+
+void LLFolderView::removeFromSelectionList(LLFolderViewItem* item)
+{
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(FALSE);
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end();)
+ {
+ if (*item_iter == item)
+ {
+ item_iter = mSelectedItems.erase(item_iter);
+ }
+ else
+ {
+ ++item_iter;
+ }
+ }
+ if (mSelectedItems.size())
+ {
+ mSelectedItems.back()->setIsCurSelection(TRUE);
+ }
+}
+
+LLFolderViewItem* LLFolderView::getCurSelectedItem( void )
+{
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* itemp = mSelectedItems.back();
+ llassert(itemp->getIsCurSelection());
+ return itemp;
+ }
+ return NULL;
+}
+
+
+// Record the selected item and pass it down the hierachy.
+BOOL LLFolderView::setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus)
+{
+ if( selection == this )
+ {
+ return FALSE;
+ }
+
+ if( selection && take_keyboard_focus)
+ {
+ setFocus(TRUE);
+ }
+
+ // clear selection down here because change of keyboard focus can potentially
+ // affect selection
+ clearSelection();
+
+ if(selection)
+ {
+ addToSelectionList(selection);
+ }
+
+ BOOL rv = LLFolderViewFolder::setSelection(selection, open, take_keyboard_focus);
+ if(open)
+ {
+ selection->getParentFolder()->requestArrange();
+ }
+
+ llassert(mSelectedItems.size() <= 1);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+BOOL LLFolderView::changeSelection(LLFolderViewItem* selection, BOOL selected)
+{
+ BOOL rv = FALSE;
+
+ // can't select root folder
+ if(!selection || selection == this)
+ {
+ return FALSE;
+ }
+
+ if (!mAllowMultiSelect)
+ {
+ clearSelection();
+ }
+
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ if (*item_iter == selection)
+ {
+ break;
+ }
+ }
+
+ BOOL on_list = (item_iter != mSelectedItems.end());
+ if (on_list && mSelectedItems.size() == 1)
+ {
+ // we are trying to select/deselect the only selected item
+ return FALSE;
+ }
+
+ if(selected && !on_list)
+ {
+ mNumDescendantsSelected++;
+ addToSelectionList(selection);
+ }
+ if(!selected && on_list)
+ {
+ mNumDescendantsSelected--;
+ removeFromSelectionList(selection);
+ }
+
+ rv = LLFolderViewFolder::changeSelection(selection, selected);
+
+ mSelectionChanged = TRUE;
+
+ return rv;
+}
+
+S32 LLFolderView::extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items)
+{
+ S32 rv = 0;
+
+ // now store resulting selection
+ if (mAllowMultiSelect)
+ {
+ LLFolderViewItem *cur_selection = getCurSelectedItem();
+ rv = LLFolderViewFolder::extendSelection(selection, cur_selection, items);
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ addToSelectionList(items[i]);
+ rv++;
+ }
+ }
+ else
+ {
+ setSelection(selection, FALSE, FALSE);
+ rv++;
+ }
+
+ mSelectionChanged = TRUE;
+ return rv;
+}
+
+void LLFolderView::sanitizeSelection()
+{
+ std::vector<LLFolderViewItem*> items_to_remove;
+ selected_items_t::iterator item_iter;
+ for (item_iter = mSelectedItems.begin(); item_iter != mSelectedItems.end(); ++item_iter)
+ {
+ LLFolderViewItem* item = *item_iter;
+
+ BOOL visible = item->getVisible();
+ LLFolderViewFolder* parent_folder = item->getParentFolder();
+ while(visible && parent_folder)
+ {
+ visible = visible && parent_folder->isOpen() && parent_folder->getVisible();
+ parent_folder = parent_folder->getParentFolder();
+ }
+ if (!visible || item->getNumSelectedDescendants() > 0)
+ {
+ // only deselect self if not visible
+ // check to see if item failed the filter but was checked against most recent generation
+ if ((!item->getFiltered() && item->getLastFilterGeneration() >= getFilter()->getMinRequiredGeneration())
+ || (item->getParentFolder() && !item->getParentFolder()->isOpen()))
+ {
+ item->recursiveDeselect(TRUE);
+ items_to_remove.push_back(item);
+ }
+ else
+ {
+ item->recursiveDeselect(FALSE);
+ }
+
+ selected_items_t::iterator other_item_iter;
+ for (other_item_iter = mSelectedItems.begin(); other_item_iter != mSelectedItems.end(); ++other_item_iter)
+ {
+ LLFolderViewItem* other_item = *other_item_iter;
+ LLFolderViewFolder* parent_folder = other_item->getParentFolder();
+ while (parent_folder)
+ {
+ if (parent_folder == item)
+ {
+ // this is a descendent of the current folder, remove from list
+ items_to_remove.push_back(other_item);
+ break;
+ }
+ parent_folder = parent_folder->getParentFolder();
+ }
+ }
+ }
+ }
+
+ std::vector<LLFolderViewItem*>::iterator item_it;
+ for (item_it = items_to_remove.begin(); item_it != items_to_remove.end(); ++item_it )
+ {
+ removeFromSelectionList(*item_it);
+ }
+}
+
+void LLFolderView::clearSelection()
+{
+ if (mSelectedItems.size() > 0)
+ {
+ recursiveDeselect(FALSE);
+ mSelectedItems.clear();
+ }
+}
+
+BOOL LLFolderView::getSelectionList(std::set<LLUUID> &selection)
+{
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ selection.insert((*item_it)->getListener()->getUUID());
+ }
+
+ return (selection.size() != 0);
+}
+
+BOOL LLFolderView::startDrag(LLToolDragAndDrop::ESource source)
+{
+ std::vector<EDragAndDropType> types;
+ std::vector<LLUUID> cargo_ids;
+ selected_items_t::iterator item_it;
+ BOOL can_drag = TRUE;
+ if (!mSelectedItems.empty())
+ {
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ EDragAndDropType type = DAD_NONE;
+ LLUUID id = LLUUID::null;
+ can_drag = can_drag && (*item_it)->getListener()->startDrag(&type, &id);
+
+ types.push_back(type);
+ cargo_ids.push_back(id);
+ }
+
+ gToolDragAndDrop->beginMultiDrag(types, cargo_ids, source, mSourceID);
+ }
+ return can_drag;
+}
+
+void LLFolderView::commitRename( LLUICtrl* renamer, void* user_data )
+{
+ LLFolderView* root = reinterpret_cast<LLFolderView*>(user_data);
+ if( root )
+ {
+ root->finishRenamingItem();
+ }
+}
+
+void LLFolderView::draw()
+{
+ if (mDebugFilters)
+ {
+ LLString current_filter_string = llformat("Current Filter: %d, Least Filter: %d, Auto-accept Filter: %d",
+ mFilter.getCurrentGeneration(), mFilter.getMinRequiredGeneration(), mFilter.getMustPassGeneration());
+ sSmallFont->renderUTF8(current_filter_string, 0, 2,
+ mRect.getHeight() - sSmallFont->getLineHeight(), LLColor4(0.5f, 0.5f, 0.8f, 1.f),
+ LLFontGL::LEFT, LLFontGL::BOTTOM, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+
+ // if cursor has moved off of me during drag and drop
+ // close all auto opened folders
+ if (!mDragAndDropThisFrame)
+ {
+ closeAutoOpenedFolders();
+ }
+ if(gViewerWindow->hasKeyboardFocus(this) && !getVisible())
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+
+ // while dragging, update selection rendering to reflect single/multi drag status
+ if (gToolDragAndDrop->hasMouseCapture())
+ {
+ EAcceptance last_accept = gToolDragAndDrop->getLastAccept();
+ if (last_accept == ACCEPT_YES_SINGLE || last_accept == ACCEPT_YES_COPY_SINGLE)
+ {
+ setShowSingleSelection(TRUE);
+ }
+ else
+ {
+ setShowSingleSelection(FALSE);
+ }
+ }
+ else
+ {
+ setShowSingleSelection(FALSE);
+ }
+
+
+ if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout") || !mSearchString.size())
+ {
+ mSearchString.clear();
+ }
+
+ if (hasVisibleChildren() || getShowFolderState() == LLInventoryFilter::SHOW_ALL_FOLDERS)
+ {
+ setStatusText("");
+ }
+ else
+ {
+ if (gInventory.backgroundFetchActive() || mCompletedFilterGeneration < mFilter.getMinRequiredGeneration())
+ {
+ setStatusText("Searching...");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ else
+ {
+ setStatusText("No matching items found in inventory.");
+ sFont->renderUTF8(mStatusText, 0, 2, 1, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP, LLFontGL::NORMAL, S32_MAX, S32_MAX, NULL, FALSE );
+ }
+ }
+
+ LLFolderViewFolder::draw();
+
+ mDragAndDropThisFrame = FALSE;
+}
+
+void LLFolderView::finishRenamingItem( void )
+{
+ if(!mRenamer)
+ {
+ return;
+ }
+ if( mRenameItem )
+ {
+ mRenameItem->rename( mRenamer->getText().c_str() );
+ }
+
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, NULL );
+
+ if( mRenameItem )
+ {
+ setSelectionFromRoot( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+
+ // List is re-sorted alphabeticly, so scroll to make sure the selected item is visible.
+ scrollToShowSelection();
+}
+
+void LLFolderView::revertRenamingItem( void )
+{
+ mRenamer->setCommitOnFocusLost( FALSE );
+ mRenamer->setFocus( FALSE );
+ mRenamer->setVisible( FALSE );
+ mRenamer->setCommitOnFocusLost( TRUE );
+ gViewerWindow->setTopView( NULL, NULL );
+
+ if( mRenameItem )
+ {
+ setSelectionFromRoot( mRenameItem, TRUE );
+ mRenameItem = NULL;
+ }
+}
+
+void LLFolderView::removeSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ // just in case we're removing the renaming item.
+ mRenameItem = NULL;
+
+ // create a temporary structure which we will use to remove
+ // items, since the removal will futz with internal data
+ // structures.
+ LLDynamicArray<LLFolderViewItem*> items;
+ S32 count = mSelectedItems.size();
+ if(count == 0) return;
+ LLFolderViewItem* item = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ item = *item_it;
+ if(item->isRemovable())
+ {
+ items.put(item);
+ }
+ else
+ {
+ llinfos << "Cannot delete " << item->getName() << llendl;
+ return;
+ }
+ }
+
+ // iterate through the new container.
+ count = items.count();
+ LLUUID new_selection_id;
+ if(count == 1)
+ {
+ LLFolderViewItem* item_to_delete = items.get(0);
+ LLFolderViewFolder* parent = item_to_delete->getParentFolder();
+ LLFolderViewItem* new_selection = item_to_delete->getNextOpenNode(FALSE);
+ if (!new_selection)
+ {
+ new_selection = item_to_delete->getPreviousOpenNode(FALSE);
+ }
+ if (new_selection)
+ {
+ setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ if(parent)
+ {
+ parent->removeItem(item_to_delete);
+ }
+ arrangeAll();
+ }
+ else if (count > 1)
+ {
+ LLDynamicArray<LLFolderViewEventListener*> listeners;
+ LLFolderViewEventListener* listener;
+ LLFolderViewItem* last_item = items.get(count - 1);
+ LLFolderViewItem* new_selection = last_item->getNextOpenNode(FALSE);
+ while(new_selection && new_selection->isSelected())
+ {
+ new_selection = new_selection->getNextOpenNode(FALSE);
+ }
+ if (!new_selection)
+ {
+ new_selection = last_item->getPreviousOpenNode(FALSE);
+ while (new_selection && new_selection->isSelected())
+ {
+ new_selection = new_selection->getPreviousOpenNode(FALSE);
+ }
+ }
+ if (new_selection)
+ {
+ setSelectionFromRoot(new_selection, new_selection->isOpen(), gViewerWindow->childHasKeyboardFocus(this));
+ }
+ else
+ {
+ setSelectionFromRoot(NULL, gViewerWindow->childHasKeyboardFocus(this));
+ }
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ listener = items.get(i)->getListener();
+ if(listener && (listeners.find(listener) == LLDynamicArray<LLFolderViewEventListener*>::FAIL))
+ {
+ listeners.put(listener);
+ }
+ }
+ listener = listeners.get(0);
+ if(listener)
+ {
+ listener->removeBatch(listeners);
+ }
+ }
+ arrangeAll();
+ scrollToShowSelection();
+ }
+}
+
+// open the selected item.
+void LLFolderView::openSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ mSelectedItems.front()->open();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiPreview* multi_previewp = new LLMultiPreview(LLRect(left, top, left + 300, top - 100));
+
+ LLFloater::setFloaterHost(multi_previewp);
+
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ (*item_it)->open();
+ }
+
+ LLFloater::setFloaterHost(NULL);
+ multi_previewp->open();
+ }
+ }
+}
+
+void LLFolderView::propertiesSelectedItems( void )
+{
+ if(getVisible() && mEnabled)
+ {
+ if (mSelectedItems.size() == 1)
+ {
+ LLFolderViewItem* folder_item = mSelectedItems.front();
+ if(!folder_item) return;
+ folder_item->getListener()->showProperties();
+ }
+ else
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+
+ LLMultiProperties* multi_propertiesp = new LLMultiProperties(LLRect(left, top, left + 100, top - 100));
+
+ LLFloater::setFloaterHost(multi_propertiesp);
+
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ (*item_it)->getListener()->showProperties();
+ }
+
+ LLFloater::setFloaterHost(NULL);
+ multi_propertiesp->open();
+ }
+ }
+}
+
+void LLFolderView::autoOpenItem( LLFolderViewFolder* item )
+{
+ if (mAutoOpenItems.check() == item || mAutoOpenItems.getDepth() >= (U32)AUTO_OPEN_STACK_DEPTH)
+ {
+ return;
+ }
+
+ // close auto-opened folders
+ LLFolderViewFolder* close_item = mAutoOpenItems.check();
+ while (close_item && close_item != item->getParentFolder())
+ {
+ mAutoOpenItems.pop();
+ close_item->setOpenArrangeRecursively(FALSE);
+ close_item = mAutoOpenItems.check();
+ }
+
+ item->requestArrange();
+
+ mAutoOpenItems.push(item);
+
+ item->setOpen(TRUE);
+ scrollToShowItem(item);
+}
+
+void LLFolderView::closeAutoOpenedFolders()
+{
+ while (mAutoOpenItems.check())
+ {
+ LLFolderViewFolder* close_item = mAutoOpenItems.pop();
+ close_item->setOpen(FALSE);
+ }
+
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = NULL;
+ mAutoOpenTimer.stop();
+}
+
+BOOL LLFolderView::autoOpenTest(LLFolderViewFolder* folder)
+{
+ if (folder && mAutoOpenCandidate == folder)
+ {
+ if (mAutoOpenTimer.getStarted())
+ {
+ if (!mAutoOpenCandidate->isOpen())
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(clamp_rescale(mAutoOpenTimer.getElapsedTimeF32(), 0.f, sAutoOpenTime, 0.f, 1.f));
+ }
+ if (mAutoOpenTimer.getElapsedTimeF32() > sAutoOpenTime)
+ {
+ autoOpenItem(folder);
+ mAutoOpenTimer.stop();
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ // otherwise new candidate, restart timer
+ if (mAutoOpenCandidate)
+ {
+ mAutoOpenCandidate->setAutoOpenCountdown(0.f);
+ }
+ mAutoOpenCandidate = folder;
+ mAutoOpenTimer.start();
+ return FALSE;
+}
+
+BOOL LLFolderView::canCopy()
+{
+ if (!(getVisible() && mEnabled && (mSelectedItems.size() > 0)))
+ {
+ return FALSE;
+ }
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ if (!item->getListener()->isItemCopyable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// copy selected item
+void LLFolderView::copy()
+{
+ // *FIX: total hack to clear the inventory clipboard
+ LLInventoryClipboard::instance().reset();
+ S32 count = mSelectedItems.size();
+ if(getVisible() && mEnabled && (count > 0))
+ {
+ LLFolderViewEventListener* listener = NULL;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ listener = (*item_it)->getListener();
+ if(listener)
+ {
+ listener->copyToClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+BOOL LLFolderView::canCut()
+{
+ return FALSE;
+}
+
+void LLFolderView::cut()
+{
+ // implement Windows-style cut-and-leave
+}
+
+BOOL LLFolderView::canPaste()
+{
+ if (mSelectedItems.empty())
+ {
+ return FALSE;
+ }
+
+ if(getVisible() && mEnabled)
+ {
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ //FIXME: only check folders and parent folders of items
+ LLFolderViewItem* item = (*item_it);
+ LLFolderViewEventListener* listener = item->getListener();
+ if(!listener || !listener->isClipboardPasteable())
+ {
+ LLFolderViewFolder* folderp = item->getParentFolder();
+ listener = folderp->getListener();
+ if (!listener || !listener->isClipboardPasteable())
+ {
+ return FALSE;
+ }
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// paste selected item
+void LLFolderView::paste()
+{
+ if(getVisible() && mEnabled)
+ {
+ // find set of unique folders to paste into
+ std::set<LLFolderViewItem*> folder_set;
+
+ selected_items_t::iterator selected_it;
+ for (selected_it = mSelectedItems.begin(); selected_it != mSelectedItems.end(); ++selected_it)
+ {
+ LLFolderViewItem* item = *selected_it;
+ LLFolderViewEventListener* listener = item->getListener();
+ if (listener->getInventoryType() != LLInventoryType::IT_CATEGORY)
+ {
+ item = item->getParentFolder();
+ }
+ folder_set.insert(item);
+ }
+
+ std::set<LLFolderViewItem*>::iterator set_iter;
+ for(set_iter = folder_set.begin(); set_iter != folder_set.end(); ++set_iter)
+ {
+ LLFolderViewEventListener* listener = (*set_iter)->getListener();
+ if(listener && listener->isClipboardPasteable())
+ {
+ listener->pasteFromClipboard();
+ }
+ }
+ }
+ mSearchString.clear();
+}
+
+// public rename functionality - can only start the process
+void LLFolderView::startRenamingSelectedItem( void )
+{
+ // make sure selection is visible
+ scrollToShowSelection();
+
+ S32 count = mSelectedItems.size();
+ LLFolderViewItem* item = NULL;
+ if(count > 0)
+ {
+ item = mSelectedItems.front();
+ }
+ if(getVisible() && mEnabled && (count == 1) && item && item->getListener() &&
+ item->getListener()->isItemRenameable())
+ {
+ mRenameItem = item;
+
+ S32 x = ARROW_SIZE + TEXT_PAD + ICON_WIDTH + ICON_PAD - 1 + item->getIndentation();
+ S32 y = llfloor(item->getRect().getHeight()-sFont->getLineHeight()-2);
+ item->localPointToScreen( x, y, &x, &y );
+ screenPointToLocal( x, y, &x, &y );
+ mRenamer->setOrigin( x, y );
+
+ S32 scroller_height = 0;
+ S32 scroller_width = gViewerWindow->getWindowWidth();
+ BOOL dummy_bool;
+ if (mScrollContainer)
+ {
+ mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool);
+ }
+
+ S32 width = llmax(llmin(item->getRect().getWidth() - x, scroller_width - x - mRect.mLeft), MINIMUM_RENAMER_WIDTH);
+ S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD);
+ mRenamer->reshape( width, height, TRUE );
+
+ mRenamer->setText(item->getName());
+ mRenamer->selectAll();
+ mRenamer->setVisible( TRUE );
+ // set focus will fail unless item is visible
+ mRenamer->setFocus( TRUE );
+ gViewerWindow->setTopView( mRenamer, top_view_lost );
+ }
+}
+
+void LLFolderView::setFocus(BOOL focus)
+{
+ if (focus)
+ {
+ // select "My Inventory" if nothing selected
+ if (!getCurSelectedItem())
+ {
+ LLFolderViewItem* itemp = getItemByID(gAgent.getInventoryRootID());
+ if (itemp)
+ {
+ setSelection(itemp, FALSE, FALSE);
+ }
+ }
+
+ if (mRenamer->getVisible())
+ {
+ //RN: commit rename changes when focus is moved, only revert on ESC
+ finishRenamingItem();
+ }
+ if(!hasFocus())
+ {
+ gEditMenuHandler = this;
+ }
+ }
+
+ LLFolderViewFolder::setFocus(focus);
+}
+
+BOOL LLFolderView::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+
+ LLView *item = NULL;
+ if (getChildCount() > 0)
+ {
+ item = *(getChildList()->begin());
+ }
+
+ if( getVisible() && mEnabled && !called_from_parent )
+ {
+ switch( key )
+ {
+ case KEY_F2:
+ mSearchString.clear();
+ startRenamingSelectedItem();
+ handled = TRUE;
+ break;
+
+ case KEY_RETURN:
+ if (mask == MASK_NONE)
+ {
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ finishRenamingItem();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ else
+ {
+ LLFolderView::openSelectedItems();
+ handled = TRUE;
+ }
+ }
+ break;
+
+ case KEY_ESCAPE:
+ // mark flag don't commit
+ if( mRenameItem && mRenamer->getVisible() )
+ {
+ revertRenamingItem();
+ handled = TRUE;
+ }
+ else
+ {
+ if( gViewerWindow->childHasKeyboardFocus( this ) )
+ {
+ gViewerWindow->setKeyboardFocus( NULL, NULL );
+ }
+ }
+ mSearchString.clear();
+ break;
+
+ case KEY_PAGE_UP:
+ mSearchString.clear();
+ mScrollContainer->pageUp(30);
+ handled = TRUE;
+ break;
+
+ case KEY_PAGE_DOWN:
+ mSearchString.clear();
+ mScrollContainer->pageDown(30);
+ handled = TRUE;
+ break;
+
+ case KEY_HOME:
+ mSearchString.clear();
+ mScrollContainer->goToTop();
+ handled = TRUE;
+ break;
+
+ case KEY_END:
+ mSearchString.clear();
+ mScrollContainer->goToBottom();
+ break;
+
+ case KEY_DOWN:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* next = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ next = last_selected->getNextOpenNode(FALSE);
+ if (next)
+ {
+ if (next->isSelected())
+ {
+ // shrink selection
+ changeSelectionFromRoot(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == next->getParentFolder())
+ {
+ // grow selection
+ changeSelectionFromRoot(next, TRUE);
+ }
+ }
+ }
+ else
+ {
+ next = last_selected->getNextOpenNode();
+ if( next )
+ {
+ if (next == last_selected)
+ {
+ return FALSE;
+ }
+ setSelection( next, FALSE, TRUE );
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_UP:
+ if((mSelectedItems.size() > 0) && mScrollContainer)
+ {
+ LLFolderViewItem* last_selected = mSelectedItems.back();
+
+ if (!mKeyboardSelection)
+ {
+ setSelection(last_selected, FALSE, TRUE);
+ mKeyboardSelection = TRUE;
+ }
+
+ LLFolderViewItem* prev = NULL;
+ if (mask & MASK_SHIFT)
+ {
+ // don't shift select down to children of folders (they are implicitly selected through parent)
+ prev = last_selected->getPreviousOpenNode(FALSE);
+ if (prev)
+ {
+ if (prev->isSelected())
+ {
+ // shrink selection
+ changeSelectionFromRoot(last_selected, FALSE);
+ }
+ else if (last_selected->getParentFolder() == prev->getParentFolder())
+ {
+ // grow selection
+ changeSelectionFromRoot(prev, TRUE);
+ }
+ }
+ }
+ else
+ {
+ prev = last_selected->getPreviousOpenNode();
+ if( prev )
+ {
+ if (prev == this)
+ {
+ return FALSE;
+ }
+ setSelection( prev, FALSE, TRUE );
+ }
+ }
+ scrollToShowSelection();
+ mSearchString.clear();
+
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_RIGHT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ last_selected->setOpen( TRUE );
+ mSearchString.clear();
+ handled = TRUE;
+ }
+ break;
+
+ case KEY_LEFT:
+ if(mSelectedItems.size())
+ {
+ LLFolderViewItem* last_selected = getCurSelectedItem();
+ LLFolderViewItem* parent_folder = last_selected->getParentFolder();
+ if (!last_selected->isOpen() && parent_folder && parent_folder->getParentFolder())
+ {
+ setSelection(parent_folder, FALSE, TRUE);
+ }
+ else
+ {
+ last_selected->setOpen( FALSE );
+ }
+ mSearchString.clear();
+ scrollToShowSelection();
+ handled = TRUE;
+ }
+ break;
+ }
+ }
+
+ if (!handled && gFocusMgr.childHasKeyboardFocus(getRoot()))
+ {
+ if (key == KEY_BACKSPACE)
+ {
+ mSearchTimer.reset();
+ if (mSearchString.size())
+ {
+ mSearchString.erase(mSearchString.size() - 1, 1);
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'N')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getNextOpenNode();
+ }
+ search(selection, mSearchString.c_str(), FALSE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ else if (mask & MASK_CONTROL && key == 'P')
+ {
+ LLFolderViewItem* selection = getCurSelectedItem();
+ if (selection)
+ {
+ selection = selection->getPreviousOpenNode();
+ }
+ search(selection, mSearchString.c_str(), TRUE);
+ mSearchTimer.reset();
+ handled = TRUE;
+ }
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+ return handled;
+}
+
+
+BOOL LLFolderView::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if ((uni_char < 0x20) || (uni_char == 0x7F)) // Control character or DEL
+ {
+ return FALSE;
+ }
+
+ if (uni_char > 0x7f)
+ {
+ llwarns << "LLFolderView::handleUnicodeCharHere - Don't handle non-ascii yet, aborting" << llendl;
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+ if (gFocusMgr.childHasKeyboardFocus(getRoot()))
+ {
+ //do text search
+ if (mSearchTimer.getElapsedTimeF32() > gSavedSettings.getF32("TypeAheadTimeout"))
+ {
+ mSearchString.clear();
+ }
+ mSearchTimer.reset();
+ if (mSearchString.size() < 128)
+ {
+ mSearchString += uni_char;
+ }
+ search(getCurSelectedItem(), mSearchString.c_str(), FALSE);
+
+ handled = TRUE;
+ }
+
+ if (handled)
+ {
+ gViewerWindow->requestFastFrame(this);
+ }
+
+ return handled;
+}
+
+
+BOOL LLFolderView::canDoDelete()
+{
+ if (mSelectedItems.size() == 0) return FALSE;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ if (!(*item_it)->getListener()->isItemRemovable())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLFolderView::doDelete()
+{
+ if(mSelectedItems.size() > 0)
+ {
+ removeSelectedItems();
+ }
+}
+
+
+BOOL LLFolderView::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ mKeyboardSelection = FALSE;
+ mSearchString.clear();
+
+ setFocus(TRUE);
+
+ return LLView::handleMouseDown( x, y, mask );
+}
+
+void LLFolderView::onFocusLost( )
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+BOOL LLFolderView::search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward)
+{
+ // get first selected item
+ LLFolderViewItem* search_item = first_item;
+
+ // make sure search string is upper case
+ LLString upper_case_string = search_string;
+ LLString::toUpper(upper_case_string);
+
+ // if nothing selected, select first item in folder
+ if (!first_item)
+ {
+ // start from first item
+ first_item = getNextFromChild(NULL);
+ }
+
+ // search over all open nodes for first substring match (with wrapping)
+ BOOL found = FALSE;
+ LLFolderViewItem* original_search_item = search_item;
+ do
+ {
+ // wrap at end
+ if (!search_item)
+ {
+ if (backward)
+ {
+ search_item = getPreviousFromChild(NULL);
+ }
+ else
+ {
+ search_item = getNextFromChild(NULL);
+ }
+ if (!search_item || search_item == original_search_item)
+ {
+ break;
+ }
+ }
+
+ const LLString current_item_label(search_item->getSearchableLabel());
+ S32 search_string_length = llmin(upper_case_string.size(), current_item_label.size());
+ if (!current_item_label.compare(0, search_string_length, upper_case_string))
+ {
+ found = TRUE;
+ break;
+ }
+ if (backward)
+ {
+ search_item = search_item->getPreviousOpenNode();
+ }
+ else
+ {
+ search_item = search_item->getNextOpenNode();
+ }
+
+ } while(search_item != original_search_item);
+
+
+ if (found)
+ {
+ setSelection(search_item, FALSE, TRUE);
+ scrollToShowSelection();
+ }
+
+ return found;
+}
+
+BOOL LLFolderView::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if (!getVisible())
+ {
+ return FALSE;
+ }
+
+ return LLView::handleDoubleClick( x, y, mask );
+}
+
+BOOL LLFolderView::handleRightMouseDown( S32 x, S32 y, MASK mask )
+{
+ // all user operations move keyboard focus to inventory
+ // this way, we know when to stop auto-updating a search
+ setFocus(TRUE);
+
+ BOOL handled = childrenHandleRightMouseDown(x, y, mask) != NULL;
+ S32 count = mSelectedItems.size();
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if(handled && (count > 0) && menu)
+ {
+ //menu->empty();
+ const LLView::child_list_t *list = menu->getChildList();
+
+ LLView::child_list_t::const_iterator menu_itor;
+ for (menu_itor = list->begin(); menu_itor != list->end(); ++menu_itor)
+ {
+ (*menu_itor)->setVisible(TRUE);
+ (*menu_itor)->setEnabled(TRUE);
+ }
+
+ // Successively filter out invalid options
+ selected_items_t::iterator item_itor;
+ U32 flags = FIRST_SELECTED_ITEM;
+ for (item_itor = mSelectedItems.begin(); item_itor != mSelectedItems.end(); ++item_itor)
+ {
+ (*item_itor)->buildContextMenu(*menu, flags);
+ flags = 0x0;
+ }
+
+ menu->arrange();
+ menu->updateParent(gMenuHolder);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ else
+ {
+ if(menu && menu->getVisible())
+ {
+ menu->setVisible(FALSE);
+ }
+ setSelection(NULL, FALSE, TRUE);
+ }
+ return handled;
+}
+
+BOOL LLFolderView::handleHover( S32 x, S32 y, MASK mask )
+{
+ return LLView::handleHover( x, y, mask );
+}
+
+BOOL LLFolderView::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ mDragAndDropThisFrame = TRUE;
+ BOOL handled = LLView::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data,
+ accept, tooltip_msg);
+
+ if (handled)
+ {
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFolderView" << llendl;
+ }
+
+ return handled;
+}
+
+BOOL LLFolderView::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (mScrollContainer)
+ {
+ return mScrollContainer->handleScrollWheel(x, y, clicks);
+ }
+ return FALSE;
+}
+
+void LLFolderView::deleteAllChildren()
+{
+ if(gViewerWindow->hasTopView(mRenamer))
+ {
+ gViewerWindow->setTopView(NULL, NULL);
+ }
+ LLView::deleteViewByHandle(mPopupMenuHandle);
+ mPopupMenuHandle = LLViewHandle::sDeadHandle;
+ mRenamer = NULL;
+ mRenameItem = NULL;
+ clearSelection();
+ LLView::deleteAllChildren();
+}
+
+void LLFolderView::scrollToShowSelection()
+{
+ if (mSelectedItems.size())
+ {
+ mNeedsScroll = TRUE;
+ }
+}
+
+// If the parent is scroll containter, scroll it to make the selection
+// is maximally visible.
+void LLFolderView::scrollToShowItem(LLFolderViewItem* item)
+{
+ // don't scroll to items when mouse is being used to scroll/drag and drop
+ if (gFocusMgr.childHasMouseCapture(mScrollContainer))
+ {
+ mNeedsScroll = FALSE;
+ return;
+ }
+ if(item && mScrollContainer)
+ {
+ LLRect local_rect = item->getRect();
+ LLRect item_scrolled_rect; // item position relative to display area of scroller
+
+ S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight();
+ S32 label_height = llround(sFont->getLineHeight());
+ // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder
+ S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight();
+ item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer);
+ item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer);
+
+ item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight);
+ LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft,
+ mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1);
+
+ S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight();
+ if (item != mLastScrollItem || // if we're scrolling to focus on a new item
+ // or the item has just appeared on screen and it wasn't onscreen before
+ (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset &&
+ (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset)))
+ {
+ // we now have a position on screen that we want to keep stable
+ // offset of selection relative to top of visible area
+ mLastScrollOffset = scroll_offset;
+ mLastScrollItem = item;
+ }
+
+ mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset );
+
+ // after scrolling, store new offset
+ // in case we don't have room to maintain the original position
+ LLCoordGL new_item_left_top;
+ item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer);
+ mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1);
+ }
+}
+
+LLRect LLFolderView::getVisibleRect()
+{
+ S32 visible_height = mScrollContainer->getRect().getHeight();
+ S32 visible_width = mScrollContainer->getRect().getWidth();
+ LLRect visible_rect;
+ visible_rect.setLeftTopAndSize(-mRect.mLeft, visible_height - mRect.mBottom, visible_width, visible_height);
+ return visible_rect;
+}
+
+BOOL LLFolderView::getShowSelectionContext()
+{
+ if (mShowSelectionContext)
+ {
+ return TRUE;
+ }
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if (menu && menu->getVisible())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderView::setShowSingleSelection(BOOL show)
+{
+ if (show != mShowSingleSelection)
+ {
+ mMultiSelectionFadeTimer.reset();
+ mShowSingleSelection = show;
+ }
+}
+
+void LLFolderView::addItemID(const LLUUID& id, LLFolderViewItem* itemp)
+{
+ mItemMap[id] = itemp;
+}
+
+void LLFolderView::removeItemID(const LLUUID& id)
+{
+ mItemMap.erase(id);
+}
+
+LLFolderViewItem* LLFolderView::getItemByID(const LLUUID& id)
+{
+ if (id.isNull())
+ {
+ return this;
+ }
+
+ std::map<LLUUID, LLFolderViewItem*>::iterator map_it;
+ map_it = mItemMap.find(id);
+ if (map_it != mItemMap.end())
+ {
+ return map_it->second;
+ }
+
+ return NULL;
+}
+
+//static
+void LLFolderView::idle(void* user_data)
+{
+ LLFastTimer t2(LLFastTimer::FTM_INVENTORY);
+ LLFolderView* self = (LLFolderView*)user_data;
+
+ BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters");
+ if (debug_filters != self->getDebugFilters())
+ {
+ self->mDebugFilters = debug_filters;
+ self->arrangeAll();
+ }
+
+ self->mFilter.clearModified();
+ BOOL filter_modified_and_active = self->mCompletedFilterGeneration < self->mFilter.getCurrentGeneration() &&
+ self->mFilter.isActive();
+ self->mNeedsAutoSelect = filter_modified_and_active &&
+ !(gFocusMgr.childHasKeyboardFocus(self) || gFocusMgr.getMouseCapture());
+
+ // filter to determine visiblity before arranging
+ self->filterFromRoot();
+
+ self->sanitizeSelection();
+
+ // automatically show matching items, and select first one
+ // do this every frame until user puts keyboard focus into the inventory window
+ // signaling the end of the automatic update
+ // only do this when mNeedsFilter is set, meaning filtered items have
+ // potentially changed
+ if (self->mNeedsAutoSelect)
+ {
+ LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT);
+ // select new item only if a filtered item not currently selected
+ LLFolderViewItem* selected_itemp = self->mSelectedItems.empty() ? NULL : self->mSelectedItems.back();
+ if ((!selected_itemp || !selected_itemp->getFiltered()) && !self->mAutoSelectOverride)
+ {
+ // select first filtered item
+ LLSelectFirstFilteredItem filter;
+ self->applyFunctorRecursively(filter);
+ }
+ self->scrollToShowSelection();
+ }
+
+ if( self->needsArrange() && self->isInVisibleChain())
+ {
+ self->arrangeFromRoot();
+ }
+
+ if (self->mSelectedItems.size() && self->mNeedsScroll)
+ {
+ self->scrollToShowItem(self->mSelectedItems.back());
+ // continue scrolling until animated layout change is done
+ if (!self->needsArrange() || !self->isInVisibleChain())
+ {
+ self->mNeedsScroll = FALSE;
+ }
+ }
+
+ if (self->mSelectionChanged && self->mSelectCallback)
+ {
+ //RN: we use keyboard focus as a proxy for user-explicit actions
+ self->mSelectCallback(self->mSelectedItems, gFocusMgr.childHasKeyboardFocus(self), self->mUserData);
+ }
+ self->mSelectionChanged = FALSE;
+}
+
+void LLFolderView::dumpSelectionInformation()
+{
+ llinfos << "LLFolderView::dumpSelectionInformation()" << llendl;
+ llinfos << "****************************************" << llendl;
+ selected_items_t::iterator item_it;
+ for (item_it = mSelectedItems.begin(); item_it != mSelectedItems.end(); ++item_it)
+ {
+ llinfos << " " << (*item_it)->getName() << llendl;
+ }
+ llinfos << "****************************************" << llendl;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ S32 compare = LLString::compareDict(a->getLabel(), b->getLabel());
+ if (0 == compare)
+ {
+ return (a->getCreationDate() > b->getCreationDate());
+ }
+ else
+ {
+ return (compare < 0);
+ }
+}
+
+// BUG: This is very very slow. The getCreationDate() is log n in number
+// of inventory items.
+bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b)
+{
+ U32 first_create = a->getCreationDate();
+ U32 second_create = b->getCreationDate();
+ if (first_create == second_create)
+ {
+ return (LLString::compareDict(a->getLabel(), b->getLabel()) < 0);
+ }
+ else
+ {
+ return (first_create > second_create);
+ }
+}
+
+void top_view_lost( LLView* view )
+{
+ if( view ) view->setVisible( FALSE );
+}
+
+void delete_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->removeSelectedItems();
+ }
+}
+
+void copy_selected_item(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->copy();
+ }
+}
+
+void paste_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->paste();
+ }
+}
+
+void open_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->openSelectedItems();
+ }
+}
+
+void properties_selected_items(void* user_data)
+{
+ if(user_data)
+ {
+ LLFolderView* fv = reinterpret_cast<LLFolderView*>(user_data);
+ fv->propertiesSelectedItems();
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Class LLFolderViewEventListener
+///----------------------------------------------------------------------------
+
+void LLFolderViewEventListener::arrangeAndSet(LLFolderViewItem* focus,
+ BOOL set_selection,
+ BOOL take_keyboard_focus)
+{
+ if(!focus) return;
+ LLFolderView* root = focus->getRoot();
+ focus->getParentFolder()->requestArrange();
+ if(set_selection)
+ {
+ focus->setSelectionFromRoot(focus, TRUE, take_keyboard_focus);
+ if(root)
+ {
+ root->scrollToShowSelection();
+ }
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryFilter
+///----------------------------------------------------------------------------
+LLInventoryFilter::LLInventoryFilter(const LLString& name) :
+ mName(name),
+ mModified(FALSE),
+ mNeedTextRebuild(TRUE)
+{
+ mFilterOps.mFilterTypes = 0xffffffff;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_MAX;
+ mFilterOps.mHoursAgo = 0;
+ mFilterOps.mShowFolderState = SHOW_NON_EMPTY_FOLDERS;
+ mFilterOps.mPermissions = PERM_NONE;
+
+ mOrder = SO_FOLDERS_BY_NAME; // This gets overridden by a pref immediately
+
+ mSubStringMatchOffset = 0;
+ mFilterSubString = "";
+ mFilterGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ mMinRequiredGeneration = 0;
+ mNextFilterGeneration = mFilterGeneration + 1;
+
+ mLastLogoff = gSavedPerAccountSettings.getU32("LastLogoff");
+}
+
+LLInventoryFilter::~LLInventoryFilter()
+{
+}
+
+BOOL LLInventoryFilter::check(LLFolderViewItem* item)
+{
+ U32 earliest;
+
+ earliest = time_corrected() - mFilterOps.mHoursAgo * 3600;
+ if (mFilterOps.mMinDate && mFilterOps.mMinDate < earliest)
+ {
+ earliest = mFilterOps.mMinDate;
+ }
+ else if (!mFilterOps.mHoursAgo)
+ {
+ earliest = 0;
+ }
+ LLFolderViewEventListener* listener = item->getListener();
+ mSubStringMatchOffset = mFilterSubString.size() ? item->getSearchableLabel().find(mFilterSubString) : LLString::npos;
+ BOOL passed = (0x1 << listener->getInventoryType() & mFilterOps.mFilterTypes || listener->getInventoryType() == LLInventoryType::IT_NONE)
+ && (mFilterSubString.size() == 0 || mSubStringMatchOffset != LLString::npos)
+ && ((listener->getPermissionMask() & mFilterOps.mPermissions) == mFilterOps.mPermissions)
+ && (listener->getCreationDate() >= earliest && listener->getCreationDate() <= mFilterOps.mMaxDate);
+ return passed;
+}
+
+const LLString LLInventoryFilter::getFilterSubString(BOOL trim)
+{
+ return mFilterSubString;
+}
+
+std::string::size_type LLInventoryFilter::getStringMatchOffset() const
+{
+ return mSubStringMatchOffset;
+}
+
+BOOL LLInventoryFilter::isActive()
+{
+ return mFilterOps.mFilterTypes != 0xffffffff
+ || mFilterSubString.size()
+ || mFilterOps.mPermissions != PERM_NONE
+ || mFilterOps.mMinDate != 0
+ || mFilterOps.mMaxDate != U32_MAX
+ || mFilterOps.mHoursAgo != 0;
+}
+
+BOOL LLInventoryFilter::isModified()
+{
+ return mModified;
+}
+
+BOOL LLInventoryFilter::isModifiedAndClear()
+{
+ BOOL ret = mModified;
+ mModified = FALSE;
+ return ret;
+}
+
+void LLInventoryFilter::setFilterTypes(U32 types)
+{
+ if (mFilterOps.mFilterTypes != types)
+ {
+ // keep current items only if no type bits getting turned off
+ BOOL fewer_bits_set = (mFilterOps.mFilterTypes & ~types);
+ BOOL more_bits_set = (~mFilterOps.mFilterTypes & types);
+
+ mFilterOps.mFilterTypes = types;
+ if (more_bits_set && fewer_bits_set)
+ {
+ // neither less or more restrive, both simultaneously
+ // so we need to filter from scratch
+ setModified(FILTER_RESTART);
+ }
+ else if (more_bits_set)
+ {
+ // target is only one of all requested types so more type bits == less restrictive
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (fewer_bits_set)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+
+ }
+}
+
+void LLInventoryFilter::setFilterSubString(const LLString& string)
+{
+ if (mFilterSubString != string)
+ {
+ // hitting BACKSPACE, for example
+ BOOL less_restrictive = mFilterSubString.size() >= string.size() && !mFilterSubString.substr(0, string.size()).compare(string);
+ // appending new characters
+ BOOL more_restrictive = mFilterSubString.size() < string.size() && !string.substr(0, mFilterSubString.size()).compare(mFilterSubString);
+ mFilterSubString = string;
+ LLString::toUpper(mFilterSubString);
+ LLString::trimHead(mFilterSubString);
+
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+
+void LLInventoryFilter::setFilterPermissions(PermissionMask perms)
+{
+ if (mFilterOps.mPermissions != perms)
+ {
+ // keep current items only if no perm bits getting turned off
+ BOOL fewer_bits_set = (mFilterOps.mPermissions & ~perms);
+ BOOL more_bits_set = (~mFilterOps.mPermissions & perms);
+ mFilterOps.mPermissions = perms;
+
+ if (more_bits_set && fewer_bits_set)
+ {
+ setModified(FILTER_RESTART);
+ }
+ else if (more_bits_set)
+ {
+ // target must have all requested permission bits, so more bits == more restrictive
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else if (fewer_bits_set)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ }
+}
+
+void LLInventoryFilter::setDateRange(U32 min_date, U32 max_date)
+{
+ mFilterOps.mHoursAgo = 0;
+ if (mFilterOps.mMinDate != min_date)
+ {
+ mFilterOps.mMinDate = min_date;
+ setModified();
+ }
+ if (mFilterOps.mMaxDate != llmax(mFilterOps.mMinDate, max_date))
+ {
+ mFilterOps.mMaxDate = llmax(mFilterOps.mMinDate, max_date);
+ setModified();
+ }
+}
+
+void LLInventoryFilter::setDateRangeLastLogoff(BOOL sl)
+{
+ if (sl && !isSinceLogoff())
+ {
+ setDateRange(mLastLogoff, U32_MAX);
+ setModified();
+ }
+ if (!sl && isSinceLogoff())
+ {
+ setDateRange(0, U32_MAX);
+ setModified();
+ }
+}
+
+BOOL LLInventoryFilter::isSinceLogoff()
+{
+ return mFilterOps.mMinDate == mLastLogoff && mFilterOps.mMaxDate == U32_MAX;
+}
+
+void LLInventoryFilter::setHoursAgo(U32 hours)
+{
+ if (mFilterOps.mHoursAgo != hours)
+ {
+ //FIXME: need to cache last filter time, in case filter goes stale
+ BOOL less_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours > mFilterOps.mHoursAgo);
+ BOOL more_restrictive = (mFilterOps.mMinDate == 0 && mFilterOps.mMaxDate == U32_MAX && hours <= mFilterOps.mHoursAgo);
+ mFilterOps.mHoursAgo = hours;
+ mFilterOps.mMinDate = 0;
+ mFilterOps.mMaxDate = U32_MAX;
+ if (less_restrictive)
+ {
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else if (more_restrictive)
+ {
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else
+ {
+ setModified(FILTER_RESTART);
+ }
+ }
+}
+void LLInventoryFilter::setShowFolderState(EFolderShow state)
+{
+ if (mFilterOps.mShowFolderState != state)
+ {
+ mFilterOps.mShowFolderState = state;
+ if (state == SHOW_NON_EMPTY_FOLDERS)
+ {
+ // showing fewer folders than before
+ setModified(FILTER_MORE_RESTRICTIVE);
+ }
+ else if (state == SHOW_ALL_FOLDERS)
+ {
+ // showing same folders as before and then some
+ setModified(FILTER_LESS_RESTRICTIVE);
+ }
+ else
+ {
+ setModified();
+ }
+ }
+}
+
+void LLInventoryFilter::setSortOrder(U32 order)
+{
+ if (mOrder != order)
+ {
+ mOrder = order;
+ setModified();
+ }
+}
+
+void LLInventoryFilter::markDefault()
+{
+ mDefaultFilterOps = mFilterOps;
+}
+
+void LLInventoryFilter::resetDefault()
+{
+ mFilterOps = mDefaultFilterOps;
+ setModified();
+}
+
+void LLInventoryFilter::setModified(EFilterBehavior behavior)
+{
+ mModified = TRUE;
+ mNeedTextRebuild = TRUE;
+ mFilterGeneration = mNextFilterGeneration++;
+
+ if (mFilterBehavior == FILTER_NONE)
+ {
+ mFilterBehavior = behavior;
+ }
+ else if (mFilterBehavior != behavior)
+ {
+ // trying to do both less restrictive and more restrictive filter
+ // basically means restart from scratch
+ mFilterBehavior = FILTER_RESTART;
+ }
+
+ if (isActive())
+ {
+ // if not keeping current filter results, update last valid as well
+ switch(mFilterBehavior)
+ {
+ case FILTER_RESTART:
+ mMustPassGeneration = mFilterGeneration;
+ mMinRequiredGeneration = mFilterGeneration;
+ break;
+ case FILTER_LESS_RESTRICTIVE:
+ mMustPassGeneration = mFilterGeneration;
+ break;
+ case FILTER_MORE_RESTRICTIVE:
+ mMinRequiredGeneration = mFilterGeneration;
+ // must have passed either current filter generation (meaningless, as it hasn't been run yet)
+ // or some older generation, so keep the value
+ mMustPassGeneration = llmin(mMustPassGeneration, mFilterGeneration);
+ break;
+ default:
+ llerrs << "Bad filter behavior specified" << llendl;
+ }
+ }
+ else
+ {
+ // shortcut disabled filters to show everything immediately
+ mMinRequiredGeneration = 0;
+ mMustPassGeneration = S32_MAX;
+ }
+}
+
+BOOL LLInventoryFilter::isFilterWith(LLInventoryType::EType t)
+{
+ return mFilterOps.mFilterTypes & (0x01 << t);
+}
+
+LLString LLInventoryFilter::getFilterText()
+{
+ if (!mNeedTextRebuild)
+ {
+ return mFilterText;
+ }
+
+ mNeedTextRebuild = FALSE;
+ LLString filtered_types;
+ LLString not_filtered_types;
+ BOOL filtered_by_type = FALSE;
+ BOOL filtered_by_all_types = TRUE;
+ S32 num_filter_types = 0;
+ mFilterText = "";
+
+ if (isFilterWith(LLInventoryType::IT_ANIMATION))
+ {
+ filtered_types += " Animations,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Animations,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_CALLINGCARD))
+ {
+ filtered_types += " Calling Cards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Calling Cards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_WEARABLE))
+ {
+ filtered_types += " Clothing,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Clothing,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_GESTURE))
+ {
+ filtered_types += " Gestures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Gestures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LANDMARK))
+ {
+ filtered_types += " Landmarks,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Landmarks,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_NOTECARD))
+ {
+ filtered_types += " Notecards,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Notecards,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_OBJECT) && isFilterWith(LLInventoryType::IT_ATTACHMENT))
+ {
+ filtered_types += " Objects,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Objects,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_LSL))
+ {
+ filtered_types += " Scripts,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Scripts,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SOUND))
+ {
+ filtered_types += " Sounds,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Sounds,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_TEXTURE))
+ {
+ filtered_types += " Textures,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Textures,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (isFilterWith(LLInventoryType::IT_SNAPSHOT))
+ {
+ filtered_types += " Snapshots,";
+ filtered_by_type = TRUE;
+ num_filter_types++;
+ }
+ else
+ {
+ not_filtered_types += " Snapshots,";
+ filtered_by_all_types = FALSE;
+ }
+
+ if (!gInventory.backgroundFetchActive() && filtered_by_type && !filtered_by_all_types)
+ {
+ mFilterText += " - ";
+ if (num_filter_types < 5)
+ {
+ mFilterText += filtered_types;
+ }
+ else
+ {
+ mFilterText += "No ";
+ mFilterText += not_filtered_types;
+ }
+ // remove the ',' at the end
+ mFilterText.erase(mFilterText.size() - 1, 1);
+ }
+
+ if (isSinceLogoff())
+ {
+ mFilterText += " - Since Logoff";
+ }
+ return mFilterText;
+}
diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h
new file mode 100644
index 0000000000..08185e24fb
--- /dev/null
+++ b/indra/newview/llfolderview.h
@@ -0,0 +1,864 @@
+/**
+ * @file llfolderview.h
+ * @brief Definition of the folder view collection of classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * The folder view collection of classes provides an interface for
+ * making a 'folder view' similar to the way the a single pane file
+ * folder interface works.
+ *
+ */
+
+#ifndef LL_LLFOLDERVIEW_H
+#define LL_LLFOLDERVIEW_H
+
+#include <vector>
+#include <map>
+#include <deque>
+
+#include "lluictrl.h"
+#include "v4color.h"
+#include "lldarray.h"
+//#include "llviewermenu.h"
+#include "stdenums.h"
+#include "llfontgl.h"
+#include "lleditmenuhandler.h"
+#include "llviewerimage.h"
+#include "lldepthstack.h"
+#include "lltooldraganddrop.h"
+
+class LLMenuGL;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewEventListener
+//
+// This is an abstract base class that users of the folderview classes
+// would use to catch the useful events emitted from the folder
+// views.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class LLFolderViewItem;
+class LLFolderView;
+class LLInventoryModel;
+class LLScrollableContainerView;
+typedef BOOL (*LLFolderSearchFunction)(LLFolderViewItem* first_item, const char *find_text, BOOL backward);
+
+class LLFolderViewEventListener
+{
+public:
+ virtual ~LLFolderViewEventListener( void ) {}
+ virtual const LLString& getName() const = 0;
+ virtual const LLString& getDisplayName() const = 0;
+ virtual const LLUUID& getUUID() const = 0;
+ virtual U32 getCreationDate() const = 0; // UTC seconds
+ virtual PermissionMask getPermissionMask() const = 0;
+ virtual LLViewerImage* getIcon() const = 0;
+ virtual LLFontGL::StyleFlags getLabelStyle() const = 0;
+ virtual LLString getLabelSuffix() const = 0;
+ virtual void openItem( void ) = 0;
+ virtual void previewItem( void ) = 0;
+ virtual void selectItem(void) = 0;
+ virtual void showProperties(void) = 0;
+ virtual BOOL isItemRenameable() const = 0;
+ virtual BOOL renameItem(const LLString& new_name) = 0;
+ virtual BOOL isItemMovable( void ) = 0; // Can be moved to another folder
+ virtual BOOL isItemRemovable( void ) = 0; // Can be destroyed
+ virtual BOOL removeItem() = 0;
+ virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch) = 0;
+ virtual void move( LLFolderViewEventListener* parent_listener ) = 0;
+ virtual BOOL isItemCopyable() const = 0;
+ virtual BOOL copyToClipboard() const = 0;
+ virtual void cutToClipboard() = 0;
+ virtual BOOL isClipboardPasteable() const = 0;
+ virtual void pasteFromClipboard() = 0;
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags) = 0;
+ virtual BOOL isUpToDate() const = 0;
+ virtual BOOL hasChildren() const = 0;
+ virtual LLInventoryType::EType getInventoryType() const = 0;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action) {}
+
+ // This method should be called when a drag begins. returns TRUE
+ // if the drag can begin, otherwise FALSE.
+ virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id) = 0;
+
+ // This method will be called to determine if a drop can be
+ // performed, and will set drop to TRUE if a drop is
+ // requested. Returns TRUE if a drop is possible/happened,
+ // otherwise FALSE.
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data) = 0;
+
+ // This method is called when the object being referenced by the
+ // bridge is actually dropped. This allows for cleanup of the old
+ // view, reference counting, etc.
+// virtual void dropped() = 0;
+
+ // this method accesses the parent and arranges and sets it as
+ // specified.
+ void arrangeAndSet(LLFolderViewItem* focus, BOOL set_selection,
+ BOOL take_keyboard_focus = TRUE);
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewListenerFunctor
+//
+// This simple abstract base class can be used to applied to all
+// listeners in a hierarchy.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewListenerFunctor
+{
+public:
+ virtual ~LLFolderViewListenerFunctor() {}
+ virtual void operator()(LLFolderViewEventListener* listener) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFunctor
+//
+// Simple abstract base class for applying a functor to folders and
+// items in a folder view hierarchy. This is suboptimal for algorithms
+// that only work folders or only work on items, but I'll worry about
+// that later when it's determined to be too slow.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewItem;
+class LLFolderViewFolder;
+
+class LLFolderViewFunctor
+{
+public:
+ virtual ~LLFolderViewFunctor() {}
+ virtual void doFolder(LLFolderViewFolder* folder) = 0;
+ virtual void doItem(LLFolderViewItem* item) = 0;
+};
+
+class LLInventoryFilter
+{
+public:
+ typedef enum e_folder_show
+ {
+ SHOW_ALL_FOLDERS,
+ SHOW_NON_EMPTY_FOLDERS,
+ SHOW_NO_FOLDERS
+ } EFolderShow;
+
+ typedef enum e_filter_behavior
+ {
+ FILTER_NONE, // nothing to do, already filtered
+ FILTER_RESTART, // restart filtering from scratch
+ FILTER_LESS_RESTRICTIVE, // existing filtered items will certainly pass this filter
+ FILTER_MORE_RESTRICTIVE // if you didn't pass the previous filter, you definitely won't pass this one
+ } EFilterBehavior;
+
+ static const U32 SO_DATE = 1;
+ static const U32 SO_FOLDERS_BY_NAME = 2;
+
+ LLInventoryFilter(const LLString& name);
+ virtual ~LLInventoryFilter();
+
+ void setFilterTypes(U32 types);
+ U32 getFilterTypes() const { return mFilterOps.mFilterTypes; }
+
+ void setFilterSubString(const LLString& string);
+ const LLString getFilterSubString(BOOL trim = FALSE);
+
+ void setFilterPermissions(PermissionMask perms);
+ PermissionMask getFilterPermissions() const { return mFilterOps.mPermissions; }
+
+ void setDateRange(U32 min_date, U32 max_date);
+ void setDateRangeLastLogoff(BOOL sl);
+ U32 getMinDate() const { return mFilterOps.mMinDate; }
+ U32 getMaxDate() const { return mFilterOps.mMaxDate; }
+
+ void setHoursAgo(U32 hours);
+ U32 getHoursAgo() const { return mFilterOps.mHoursAgo; }
+
+ void setShowFolderState( EFolderShow state);
+ EFolderShow getShowFolderState() { return mFilterOps.mShowFolderState; }
+
+ void setSortOrder(U32 order);
+ U32 getSortOrder() { return mOrder; }
+
+ BOOL check(LLFolderViewItem* item);
+ std::string::size_type getStringMatchOffset() const;
+ BOOL isActive();
+ BOOL isModified();
+ BOOL isModifiedAndClear();
+ BOOL isSinceLogoff();
+ void clearModified() { mModified = FALSE; mFilterBehavior = FILTER_NONE; }
+ const LLString getName() const { return mName; }
+ LLString getFilterText();
+
+ void setFilterCount(S32 count) { mFilterCount = count; }
+ S32 getFilterCount() { return mFilterCount; }
+ void decrementFilterCount() { mFilterCount--; }
+
+ void markDefault();
+ void resetDefault();
+
+ BOOL isFilterWith(LLInventoryType::EType t);
+
+ S32 getCurrentGeneration() const { return mFilterGeneration; }
+ S32 getMinRequiredGeneration() const { return mMinRequiredGeneration; }
+ S32 getMustPassGeneration() const { return mMustPassGeneration; }
+
+ //RN: this is public to allow system to externally force a global refilter
+ void setModified(EFilterBehavior behavior = FILTER_RESTART);
+
+protected:
+ struct filter_ops
+ {
+ U32 mFilterTypes;
+ U32 mMinDate;
+ U32 mMaxDate;
+ U32 mHoursAgo;
+ EFolderShow mShowFolderState;
+ PermissionMask mPermissions;
+ };
+ filter_ops mFilterOps;
+ filter_ops mDefaultFilterOps;
+ std::string::size_type mSubStringMatchOffset;
+ LLString mFilterSubString;
+ U32 mOrder;
+ const LLString mName;
+ S32 mFilterGeneration;
+ S32 mMustPassGeneration;
+ S32 mMinRequiredGeneration;
+ S32 mFilterCount;
+ S32 mNextFilterGeneration;
+ EFilterBehavior mFilterBehavior;
+
+private:
+ U32 mLastLogoff;
+ BOOL mModified;
+ BOOL mNeedTextRebuild;
+ LLString mFilterText;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewItem
+//
+// An instance of this class represents a single item in a folder view
+// such as an inventory item or a file.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFontGL;
+class LLFolderViewFolder;
+class LLFolderView;
+
+class LLFolderViewItem : public LLUICtrl
+{
+protected:
+ friend class LLFolderViewEventListener;
+
+ static const LLFontGL* sFont;
+ static const LLFontGL* sSmallFont;
+ static LLColor4 sFgColor;
+ static LLColor4 sHighlightBgColor;
+ static LLColor4 sHighlightFgColor;
+ static LLColor4 sFilterBGColor;
+ static LLColor4 sFilterTextColor;
+
+ LLString mLabel;
+ LLString mSearchableLabel;
+ LLString mType;
+ S32 mLabelWidth;
+ U32 mCreationDate;
+ LLFolderViewFolder* mParentFolder;
+ LLFolderViewEventListener* mListener;
+ BOOL mIsSelected;
+ BOOL mIsCurSelection;
+ BOOL mSelectPending;
+ LLFontGL::StyleFlags mLabelStyle;
+ LLString mLabelSuffix;
+ LLPointer<LLViewerImage> mIcon;
+ LLString mStatusText;
+ BOOL mHasVisibleChildren;
+ S32 mIndentation;
+ S32 mNumDescendantsSelected;
+ BOOL mFiltered;
+ S32 mLastFilterGeneration;
+ std::string::size_type mStringMatchOffset;
+ F32 mControlLabelRotation;
+ LLFolderView* mRoot;
+ BOOL mDragAndDropTarget;
+ LLPointer<LLViewerImage> mArrowImage;
+ LLPointer<LLViewerImage> mBoxImage;
+
+ // This function clears the currently selected item, and records
+ // the specified selected item appropriately for display and use
+ // in the UI. If open is TRUE, then folders are opened up along
+ // the way to the selection.
+ void setSelectionFromRoot(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus = TRUE);
+
+ // helper function to change the selection from the root.
+ void changeSelectionFromRoot(LLFolderViewItem* selection, BOOL selected);
+
+ // helper function to change the selection from the root.
+ void extendSelectionFromRoot(LLFolderViewItem* selection);
+
+ // this is an internal method used for adding items to folders. A
+ // no-op at this leve, but reimplemented in derived classes.
+ virtual BOOL addItem(LLFolderViewItem*) { return FALSE; }
+ virtual BOOL addFolder(LLFolderViewFolder*) { return FALSE; }
+
+public:
+ // This function is called when the folder view is dirty. It's
+ // implemented here but called by derived classes when folding the
+ // views.
+ void arrangeFromRoot();
+ void filterFromRoot( void );
+
+ // creation_date is in UTC seconds
+ LLFolderViewItem( const LLString& name, LLViewerImage* icon, S32 creation_date, LLFolderView* root, LLFolderViewEventListener* listener );
+ virtual ~LLFolderViewItem( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ enum { ARRANGE = TRUE, DO_NOT_ARRANGE = FALSE };
+ virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+ virtual S32 getItemHeight();
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+
+ // updates filter serial number and optionally propagated value up to root
+ S32 getLastFilterGeneration() { return mLastFilterGeneration; }
+
+ virtual void dirtyFilter();
+
+ // If the selection is 'this' then note that otherwise
+ // ignore. Returns TRUE if this object was affected. If open is
+ // TRUE, then folders are opened up along the way to the
+ // selection.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to toggle the selection of an item. If
+ // selection is 'this', then note selection, and return TRUE.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to group select items
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items){ return FALSE; }
+
+ // this method is used to group select items
+ virtual void recursiveDeselect(BOOL deselect_self);
+
+ // gets multiple-element selection
+ virtual BOOL getSelectionList(std::set<LLUUID> &selection){return TRUE;}
+
+ // Returns true is this object and all of its children can be removed (deleted by user)
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this item recursively
+ virtual void destroyView();
+
+ S32 getNumSelectedDescendants() { return mNumDescendantsSelected; }
+
+ BOOL isSelected() { return mIsSelected; }
+
+ void setIsCurSelection(BOOL select) { mIsCurSelection = select; }
+
+ BOOL getIsCurSelection() { return mIsCurSelection; }
+
+ BOOL hasVisibleChildren() { return mHasVisibleChildren; }
+
+ // Call through to the viewed object and return true if it can be
+ // removed. Returns true if it's removed.
+ //virtual BOOL removeRecursively(BOOL single_item);
+ BOOL remove();
+
+ // Build an appropriate context menu for the item. Flags unused.
+ void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+ // This method returns the actual name of the thing being
+ // viewed. This method will ask the viewed object itself.
+ const LLString& getName( void ) const;
+
+ const LLString& getSearchableLabel( void ) const;
+
+ // This method returns the label displayed on the view. This
+ // method was primarily added to allow sorting on the folder
+ // contents possible before the entire view has been constructed.
+ const char* getLabel() const { return mLabel.c_str(); }
+
+ // Used for sorting, like getLabel() above.
+ virtual U32 getCreationDate() const { return mCreationDate; }
+
+ LLFolderViewFolder* getParentFolder( void );
+ LLFolderViewItem* getNextOpenNode( BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousOpenNode( BOOL include_children = TRUE );
+
+ LLFolderViewEventListener* getListener( void );
+
+ // just rename the object.
+ void rename(const LLString& new_name);
+
+ // open
+ virtual void open( void );
+ virtual void preview(void);
+
+ // Show children (unfortunate that this is called "open")
+ virtual void setOpen(BOOL open = TRUE) {};
+
+ virtual BOOL isOpen() { return FALSE; }
+
+ LLFolderView* getRoot();
+ BOOL isDescendantOf( const LLFolderViewFolder* potential_ancestor );
+ S32 getIndentation() { return mIndentation; }
+
+ virtual void setStatusText(const LLString& text) { mStatusText = text; }
+
+ BOOL getFiltered();
+ BOOL getFiltered(S32 filter_generation);
+ virtual void setFiltered(BOOL filtered, S32 filter_generation);
+
+ // change the icon
+ void setIcon(LLViewerImage* icon);
+
+ // refresh information from the object being viewed.
+ virtual void refresh();
+
+ virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor);
+
+ // LLView functionality
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+
+ // virtual void handleDropped();
+ virtual void draw();
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+};
+
+
+// function used for sorting.
+typedef bool (*sort_order_f)(LLFolderViewItem* a, LLFolderViewItem* b);
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderViewFolder
+//
+// An instance of an LLFolderViewFolder represents a collection of
+// more folders and items. This is used to build the hierarchy of
+// items found in the folder view. :)
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLFolderViewFolder : public LLFolderViewItem
+{
+public:
+ typedef enum e_trash
+ {
+ UNKNOWN, TRASH, NOT_TRASH
+ } ETrash;
+
+protected:
+ typedef std::vector<LLFolderViewItem*> items_t;
+ typedef std::vector<LLFolderViewFolder*> folders_t;
+ items_t mItems;
+ folders_t mFolders;
+ sort_order_f mSortFunction;
+
+ BOOL mIsOpen;
+ BOOL mExpanderHighlighted;
+ F32 mCurHeight;
+ F32 mTargetHeight;
+ F32 mAutoOpenCountdown;
+ U32 mSubtreeCreationDate;
+ ETrash mAmTrash;
+ S32 mLastArrangeGeneration;
+ S32 mLastCalculatedWidth;
+ S32 mCompletedFilterGeneration;
+ S32 mMostFilteredDescendantGeneration;
+public:
+ typedef enum e_recurse_type
+ {
+ RECURSE_NO,
+ RECURSE_UP,
+ RECURSE_DOWN,
+ RECURSE_UP_DOWN
+ } ERecurseType;
+
+ LLFolderViewFolder( const LLString& name, LLViewerImage* icon,
+ LLFolderView* root,
+ LLFolderViewEventListener* listener );
+ virtual ~LLFolderViewFolder( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ LLFolderViewItem* getNextFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+ LLFolderViewItem* getPreviousFromChild( LLFolderViewItem*, BOOL include_children = TRUE );
+
+ // addToFolder() returns TRUE if it succeeds. FALSE otherwise
+ virtual BOOL addToFolder(LLFolderViewFolder* folder, LLFolderView* root);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+
+ BOOL needsArrange();
+
+ virtual void setCompletedFilterGeneration(S32 generation, BOOL recurse_up);
+ virtual S32 getCompletedFilterGeneration() { return mCompletedFilterGeneration; }
+
+ BOOL hasFilteredDescendants(S32 filter_generation) { return mMostFilteredDescendantGeneration >= filter_generation; }
+ BOOL hasFilteredDescendants();
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+ virtual void setFiltered(BOOL filtered, S32 filter_generation);
+ virtual void dirtyFilter();
+
+ // Passes selection information on to children and record
+ // selection information if necessary. Returns TRUE if this object
+ // (or a child) was affected.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to change the selection of an item. If
+ // selection is 'this', then note selection as true. Returns TRUE
+ // if this or a child is now selected.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ // this method is used to group select items
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items);
+
+ virtual void recursiveDeselect(BOOL deselect_self);
+
+ // Returns true is this object and all of its children can be removed.
+ virtual BOOL isRemovable();
+
+ // Returns true is this object and all of its children can be moved
+ virtual BOOL isMovable();
+
+ // destroys this folder, and all children
+ virtual void destroyView();
+
+ // If this folder can be removed, remove all children that can be
+ // removed, return TRUE if this is empty after the operation and
+ // it's viewed folder object can be removed.
+ //virtual BOOL removeRecursively(BOOL single_item);
+ //virtual BOOL remove();
+
+ // remove the specified item (and any children) if
+ // possible. Return TRUE if the item was deleted.
+ BOOL removeItem(LLFolderViewItem* item);
+
+ // simply remove the view (and any children) Don't bother telling
+ // the listeners.
+ void removeView(LLFolderViewItem* item);
+
+ // extractItem() removes the specified item from the folder, but
+ // doesn't delete it.
+ void extractItem( LLFolderViewItem* item );
+
+ // This function is called by a child that needs to be resorted.
+ void resort(LLFolderViewItem* item);
+
+ void setItemSortFunction(sort_order_f ordering);
+ void sortBy(U32);
+ //BOOL (*func)(LLFolderViewItem* a, LLFolderViewItem* b));
+
+ void setAutoOpenCountdown(F32 countdown) { mAutoOpenCountdown = countdown; }
+
+ // folders can be opened. This will usually be called by internal
+ // methods.
+ virtual void toggleOpen();
+
+ // Force a folder open or closed
+ virtual void setOpen(BOOL open = TRUE);
+
+ // Called when a child is refreshed.
+ virtual void requestArrange();
+
+ // internal method which doesn't update the entire view. This
+ // method was written because the list iterators destroy the state
+ // of other iterations, thus, we can't arrange while iterating
+ // through the children (such as when setting which is selected.
+ virtual void setOpenArrangeRecursively(BOOL open, ERecurseType recurse = RECURSE_NO);
+
+ // Get the current state of the folder.
+ virtual BOOL isOpen() { return mIsOpen; }
+
+ // special case if an object is dropped on the child.
+ BOOL handleDragAndDropFromChild(MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ void applyFunctorRecursively(LLFolderViewFunctor& functor);
+ virtual void applyListenerFunctorRecursively(LLFolderViewListenerFunctor& functor);
+
+ virtual void open( void );
+ virtual BOOL addItem(LLFolderViewItem* item);
+ virtual BOOL addFolder( LLFolderViewFolder* folder);
+
+ // LLView functionality
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ virtual void draw();
+
+ U32 getCreationDate() const;
+ bool isTrash();
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLFolderView
+//
+// Th LLFolderView represents the root level folder view object. It
+// manages the screen region of the folder view.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLUICtrl;
+class LLLineEditor;
+
+class LLFolderView : public LLFolderViewFolder, LLEditMenuHandler
+{
+public:
+ typedef void (*SelectCallback)(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+
+ static F32 sAutoOpenTime;
+
+ LLFolderView( const LLString& name, LLViewerImage* root_folder_icon, const LLRect& rect,
+ const LLUUID& source_id, LLView *parent_view );
+ virtual ~LLFolderView( void );
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual BOOL canFocusChildren() const;
+
+ // FolderViews default to sort by name. This will change that,
+ // and resort the items if necessary.
+ void setSortOrder(U32 order);
+ void checkTreeResortForModelChanged();
+ void setFilterPermMask(PermissionMask filter_perm_mask) { mFilter.setFilterPermissions(filter_perm_mask); }
+ void setSelectCallback(SelectCallback callback, void* user_data) { mSelectCallback = callback, mUserData = user_data; }
+ void setAllowMultiSelect(BOOL allow) { mAllowMultiSelect = allow; }
+
+ LLInventoryFilter* getFilter() { return &mFilter; }
+ const LLString getFilterSubString(BOOL trim = FALSE);
+ U32 getFilterTypes() const { return mFilter.getFilterTypes(); }
+ PermissionMask getFilterPermissions() const { return mFilter.getFilterPermissions(); }
+ LLInventoryFilter::EFolderShow getShowFolderState() { return mFilter.getShowFolderState(); }
+ U32 getSortOrder() const;
+ BOOL isFilterActive() { return mFilter.isActive(); }
+ BOOL getAllowMultiSelect() { return mAllowMultiSelect; }
+
+ // Close all folders in the view
+ void closeAllFolders();
+ void openFolder(const LLString& foldername);
+
+ virtual void toggleOpen() {};
+ virtual void setOpenArrangeRecursively(BOOL open, ERecurseType recurse);
+ virtual BOOL addFolder( LLFolderViewFolder* folder);
+
+ // Finds width and height of this object and it's children. Also
+ // makes sure that this view and it's children are the right size.
+ virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
+
+ void arrangeAll() { mArrangeGeneration++; }
+ S32 getArrangeGeneration() { return mArrangeGeneration; }
+
+ // applies filters to control visibility of inventory items
+ virtual void filter( LLInventoryFilter& filter);
+
+ // get the last selected item
+ virtual LLFolderViewItem* getCurSelectedItem( void );
+
+ // Record the selected item and pass it down the hierachy.
+ virtual BOOL setSelection(LLFolderViewItem* selection, BOOL open,
+ BOOL take_keyboard_focus);
+
+ // This method is used to toggle the selection of an item. Walks
+ // children, and keeps track of selected objects.
+ virtual BOOL changeSelection(LLFolderViewItem* selection, BOOL selected);
+
+ virtual S32 extendSelection(LLFolderViewItem* selection, LLFolderViewItem* last_selected, LLDynamicArray<LLFolderViewItem*>& items);
+
+ virtual BOOL getSelectionList(std::set<LLUUID> &selection);
+
+ // make sure if ancestor is selected, descendents are not
+ void sanitizeSelection();
+ void clearSelection();
+ void addToSelectionList(LLFolderViewItem* item);
+ void removeFromSelectionList(LLFolderViewItem* item);
+
+ BOOL startDrag(LLToolDragAndDrop::ESource source);
+ void setDragAndDropThisFrame() { mDragAndDropThisFrame = TRUE; }
+
+ // deletion functionality
+ void removeSelectedItems();
+
+ // open the selected item.
+ void openSelectedItems( void );
+ void propertiesSelectedItems( void );
+
+ void autoOpenItem(LLFolderViewFolder* item);
+ void closeAutoOpenedFolders();
+ BOOL autoOpenTest(LLFolderViewFolder* item);
+
+ // copy & paste
+ virtual void copy();
+ virtual BOOL canCopy();
+
+ virtual void cut();
+ virtual BOOL canCut();
+
+ virtual void paste();
+ virtual BOOL canPaste();
+
+ virtual void doDelete();
+ virtual BOOL canDoDelete();
+
+ // public rename functionality - can only start the process
+ void startRenamingSelectedItem( void );
+
+ // These functions were used when there was only one folderview,
+ // and relied on that concept. This functionality is now handled
+ // by the listeners and the lldraganddroptool.
+ //LLFolderViewItem* getMovingItem() { return mMovingItem; }
+ //void setMovingItem( LLFolderViewItem* item ) { mMovingItem = item; }
+ //void dragItemIntoFolder( LLFolderViewItem* moving_item, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept );
+ //void dragFolderIntoFolder( LLFolderViewFolder* moving_folder, LLFolderViewFolder* dst_folder, BOOL drop, BOOL* accept );
+
+ // LLUICtrl Functionality
+ /*virtual*/ void setFocus(BOOL focus);
+
+ // LLView functionality
+ ///*virtual*/ BOOL handleKey( KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask, BOOL called_from_parent );
+ /*virtual*/ BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+ /*virtual*/ BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+ /*virtual*/ void onFocusLost();
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void draw();
+ virtual void deleteAllChildren();
+
+ void scrollToShowSelection();
+ void scrollToShowItem(LLFolderViewItem* item);
+ void setScrollContainer( LLScrollableContainerView* parent ) { mScrollContainer = parent; }
+ LLRect getVisibleRect();
+
+ BOOL search(LLFolderViewItem* first_item, const LLString &search_string, BOOL backward);
+ void setShowSelectionContext(BOOL show) { mShowSelectionContext = show; }
+ BOOL getShowSelectionContext();
+ void setShowSingleSelection(BOOL show);
+ BOOL getShowSingleSelection() { return mShowSingleSelection; }
+ F32 getSelectionFadeElapsedTime() { return mMultiSelectionFadeTimer.getElapsedTimeF32(); }
+
+ void addItemID(const LLUUID& id, LLFolderViewItem* itemp);
+ void removeItemID(const LLUUID& id);
+ LLFolderViewItem* getItemByID(const LLUUID& id);
+
+ static void idle(void* user_data);
+
+ void setAutoSelectOverride(bool override) { mAutoSelectOverride = override; }
+ BOOL needsAutoSelect() { return mNeedsAutoSelect && !mAutoSelectOverride; }
+ BOOL getDebugFilters() { return mDebugFilters; }
+
+ // DEBUG only
+ void dumpSelectionInformation();
+
+protected:
+ LLScrollableContainerView* mScrollContainer; // NULL if this is not a child of a scroll container.
+
+ static void commitRename( LLUICtrl* renamer, void* user_data );
+ void finishRenamingItem( void );
+ void revertRenamingItem( void );
+
+protected:
+ LLViewHandle mPopupMenuHandle;
+
+ typedef std::deque<LLFolderViewItem*> selected_items_t;
+ selected_items_t mSelectedItems;
+ BOOL mKeyboardSelection;
+ BOOL mAllowMultiSelect;
+ BOOL mShowFolderHierarchy;
+ LLUUID mSourceID;
+
+ // Renaming variables and methods
+ LLFolderViewItem* mRenameItem; // The item currently being renamed
+ LLLineEditor* mRenamer;
+
+ BOOL mNeedsScroll;
+ LLFolderViewItem* mLastScrollItem;
+ LLCoordGL mLastScrollOffset;
+ BOOL mNeedsAutoSelect;
+ BOOL mAutoSelectOverride;
+
+ BOOL mDebugFilters;
+ U32 mSortOrder;
+ LLDepthStack<LLFolderViewFolder> mAutoOpenItems;
+ LLFolderViewFolder* mAutoOpenCandidate;
+ LLFrameTimer mAutoOpenTimer;
+ LLFrameTimer mSearchTimer;
+ LLString mSearchString;
+ LLInventoryFilter mFilter;
+ BOOL mShowSelectionContext;
+ BOOL mShowSingleSelection;
+ LLFrameTimer mMultiSelectionFadeTimer;
+ S32 mArrangeGeneration;
+
+ void* mUserData;
+ SelectCallback mSelectCallback;
+ BOOL mSelectionChanged;
+ S32 mMinWidth;
+ std::map<LLUUID, LLFolderViewItem*> mItemMap;
+ BOOL mDragAndDropThisFrame;
+
+};
+
+bool sort_item_name(LLFolderViewItem* a, LLFolderViewItem* b);
+bool sort_item_date(LLFolderViewItem* a, LLFolderViewItem* b);
+
+// Flags for buildContextMenu()
+const U32 SUPPRESS_OPEN_ITEM = 0x1;
+const U32 FIRST_SELECTED_ITEM = 0x2;
+
+#endif // LL_LLFOLDERVIEW_H
diff --git a/indra/newview/llfollowcam.cpp b/indra/newview/llfollowcam.cpp
new file mode 100644
index 0000000000..6f1addcebb
--- /dev/null
+++ b/indra/newview/llfollowcam.cpp
@@ -0,0 +1,882 @@
+/**
+ * @file llfollowcam.cpp
+ * @author Jeffrey Ventrella
+ * @brief LLFollowCam class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llfollowcam.h"
+#include "llagent.h"
+
+//-------------------------------------------------------
+// class statics
+//-------------------------------------------------------
+std::map<LLUUID, LLFollowCamParams*> LLFollowCamMgr::sParamMap;
+std::vector<LLFollowCamParams*> LLFollowCamMgr::sParamStack;
+
+//-------------------------------------------------------
+// constants
+//-------------------------------------------------------
+const F32 ONE_HALF = 0.5;
+const F32 FOLLOW_CAM_ZOOM_FACTOR = 0.1f;
+const F32 FOLLOW_CAM_MIN_ZOOM_AMOUNT = 0.1f;
+const F32 DISTANCE_EPSILON = 0.0001f;
+const F32 DEFAULT_MAX_DISTANCE_FROM_SUBJECT = 1000.0; // this will be correctly set on me by my caller
+
+//----------------------------------------------------------------------------------------
+// This is how slowly the camera position moves to its ideal position
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_POSITION_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_POSITION_LAG = 0.1f;
+const F32 FOLLOW_CAM_MAX_POSITION_LAG = 3.0f;
+
+//----------------------------------------------------------------------------------------
+// This is how slowly the camera focus moves to its subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_FOCUS_LAG = 0.1f;
+const F32 FOLLOW_CAM_MAX_FOCUS_LAG = 3.0f;
+
+//----------------------------------------------------------------------------------------
+// This is far the position can get from its IDEAL POSITION before it starts getting pulled
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_POSITION_THRESHOLD = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD = 1.0f;
+const F32 FOLLOW_CAM_MAX_POSITION_THRESHOLD = 4.0f;
+
+//----------------------------------------------------------------------------------------
+// This is far the focus can get from the subject before it starts getting pulled
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_THRESHOLD = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD = 1.0f;
+const F32 FOLLOW_CAM_MAX_FOCUS_THRESHOLD = 4.0f;
+
+//----------------------------------------------------------------------------------------
+// This is the distance the camera wants to be from the subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_DISTANCE = 0.5f;
+const F32 FOLLOW_CAM_DEFAULT_DISTANCE = 3.0f;
+//const F32 FOLLOW_CAM_MAX_DISTANCE = 10.0f; // from now on I am using mMaxCameraDistantFromSubject
+
+//----------------------------------------------------------------------------------------
+// this is an angluar value
+// It affects the angle that the camera rises (pitches) in relation
+// to the horizontal plane
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_PITCH = -45.0f;
+const F32 FOLLOW_CAM_DEFAULT_PITCH = 0.0f;
+const F32 FOLLOW_CAM_MAX_PITCH = 80.0f; // keep under 90 degrees - avoid gimble lock!
+
+
+//----------------------------------------------------------------------------------------
+// how high or low the camera considers its ideal focus to be (relative to its subject)
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_FOCUS_OFFSET = -10.0f;
+const LLVector3 FOLLOW_CAM_DEFAULT_FOCUS_OFFSET = LLVector3(1.0f, 0.f, 0.f);
+const F32 FOLLOW_CAM_MAX_FOCUS_OFFSET = 10.0f;
+
+//----------------------------------------------------------------------------------------
+// This affects the rate at which the camera adjusts to stay behind the subject
+//----------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_BEHINDNESS_LAG = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG = 0.f;
+const F32 FOLLOW_CAM_MAX_BEHINDNESS_LAG = 3.0f;
+
+//---------------------------------------------------------------------------------------------------------------------
+// in degrees: This is the size of the pie slice behind the subject matter within which the camera is free to move
+//---------------------------------------------------------------------------------------------------------------------
+const F32 FOLLOW_CAM_MIN_BEHINDNESS_ANGLE = 0.0f;
+const F32 FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE = 10.0f;
+const F32 FOLLOW_CAM_MAX_BEHINDNESS_ANGLE = 180.0f;
+const F32 FOLLOW_CAM_BEHINDNESS_EPSILON = 1.0f;
+
+//------------------------------------
+// Constructor
+//------------------------------------
+LLFollowCamParams::LLFollowCamParams()
+{
+ mMaxCameraDistantFromSubject = DEFAULT_MAX_DISTANCE_FROM_SUBJECT;
+ mPositionLocked = false;
+ mFocusLocked = false;
+ mUsePosition = false;
+ mUseFocus = false;
+
+ //------------------------------------------------------
+ // setting the attributes to their defaults
+ //------------------------------------------------------
+ setPositionLag ( FOLLOW_CAM_DEFAULT_POSITION_LAG );
+ setFocusLag ( FOLLOW_CAM_DEFAULT_FOCUS_LAG );
+ setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
+ setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
+ setBehindnessLag ( FOLLOW_CAM_DEFAULT_BEHINDNESS_LAG );
+ setDistance ( FOLLOW_CAM_DEFAULT_DISTANCE );
+ setPitch ( FOLLOW_CAM_DEFAULT_PITCH );
+ setFocusOffset ( FOLLOW_CAM_DEFAULT_FOCUS_OFFSET );
+ setBehindnessAngle ( FOLLOW_CAM_DEFAULT_BEHINDNESS_ANGLE );
+ setPositionThreshold( FOLLOW_CAM_DEFAULT_POSITION_THRESHOLD );
+ setFocusThreshold ( FOLLOW_CAM_DEFAULT_FOCUS_THRESHOLD );
+
+}
+
+LLFollowCamParams::~LLFollowCamParams() { }
+
+//---------------------------------------------------------
+// buncho set methods
+//---------------------------------------------------------
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionLag( F32 p )
+{
+ mPositionLag = llclamp(p, FOLLOW_CAM_MIN_POSITION_LAG, FOLLOW_CAM_MAX_POSITION_LAG);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusLag( F32 f )
+{
+ mFocusLag = llclamp(f, FOLLOW_CAM_MIN_FOCUS_LAG, FOLLOW_CAM_MAX_FOCUS_LAG);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionThreshold( F32 p )
+{
+ mPositionThreshold = llclamp(p, FOLLOW_CAM_MIN_POSITION_THRESHOLD, FOLLOW_CAM_MAX_POSITION_THRESHOLD);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusThreshold( F32 f )
+{
+ mFocusThreshold = llclamp(f, FOLLOW_CAM_MIN_FOCUS_THRESHOLD, FOLLOW_CAM_MAX_FOCUS_THRESHOLD);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPitch( F32 p )
+{
+ mPitch = llclamp(p, FOLLOW_CAM_MIN_PITCH, FOLLOW_CAM_MAX_PITCH);
+}
+
+
+//---------------------------------------------------------
+void LLFollowCamParams::setBehindnessLag( F32 b )
+{
+ mBehindnessLag = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_LAG, FOLLOW_CAM_MAX_BEHINDNESS_LAG);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setBehindnessAngle( F32 b )
+{
+ mBehindnessMaxAngle = llclamp(b, FOLLOW_CAM_MIN_BEHINDNESS_ANGLE, FOLLOW_CAM_MAX_BEHINDNESS_ANGLE);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setDistance( F32 d )
+{
+ mDistance = llclamp(d, FOLLOW_CAM_MIN_DISTANCE, mMaxCameraDistantFromSubject);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPositionLocked( bool l )
+{
+ mPositionLocked = l;
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusLocked( bool l )
+{
+ mFocusLocked = l;
+
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocusOffset( const LLVector3& v )
+{
+ mFocusOffset = v;
+ mFocusOffset.clamp(FOLLOW_CAM_MIN_FOCUS_OFFSET, FOLLOW_CAM_MAX_FOCUS_OFFSET);
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setPosition( const LLVector3& p )
+{
+ mUsePosition = true;
+ mPosition = p;
+}
+
+//---------------------------------------------------------
+void LLFollowCamParams::setFocus( const LLVector3& f )
+{
+ mUseFocus = true;
+ mFocus = f;
+}
+
+//---------------------------------------------------------
+// buncho get methods
+//---------------------------------------------------------
+F32 LLFollowCamParams::getPositionLag () const { return mPositionLag; }
+F32 LLFollowCamParams::getFocusLag () const { return mFocusLag; }
+F32 LLFollowCamParams::getPositionThreshold () const { return mPositionThreshold; }
+F32 LLFollowCamParams::getFocusThreshold () const { return mFocusThreshold; }
+F32 LLFollowCamParams::getDistance () const { return mDistance; }
+F32 LLFollowCamParams::getPitch () const { return mPitch; }
+LLVector3 LLFollowCamParams::getFocusOffset () const { return mFocusOffset; }
+F32 LLFollowCamParams::getBehindnessAngle () const { return mBehindnessMaxAngle; }
+F32 LLFollowCamParams::getBehindnessLag () const { return mBehindnessLag; }
+LLVector3 LLFollowCamParams::getPosition () const { return mPosition; }
+LLVector3 LLFollowCamParams::getFocus () const { return mFocus; }
+bool LLFollowCamParams::getPositionLocked () const { return mPositionLocked; }
+bool LLFollowCamParams::getFocusLocked () const { return mFocusLocked; }
+
+//------------------------------------
+// Constructor
+//------------------------------------
+LLFollowCam::LLFollowCam() : LLFollowCamParams()
+{
+ mUpVector = LLVector3::z_axis;
+ mSubjectPosition = LLVector3::zero;
+ mSubjectRotation = LLQuaternion::DEFAULT;
+
+ mZoomedToMinimumDistance = false;
+ mPitchSineAndCosineNeedToBeUpdated = true;
+
+ mSimulatedDistance = mDistance;
+}
+
+void LLFollowCam::copyParams(LLFollowCamParams& params)
+{
+ setPositionLag(params.getPositionLag());
+ setFocusLag(params.getFocusLag());
+ setFocusThreshold( params.getFocusThreshold());
+ setPositionThreshold(params.getPositionThreshold());
+ setPitch(params.getPitch());
+ setFocusOffset(params.getFocusOffset());
+ setBehindnessAngle(params.getBehindnessAngle());
+ setBehindnessLag(params.getBehindnessLag());
+
+ setPositionLocked(params.getPositionLocked());
+ setFocusLocked(params.getFocusLocked());
+
+ setDistance(params.getDistance());
+ if (params.getUsePosition())
+ {
+ setPosition(params.getPosition());
+ }
+ if (params.getUseFocus())
+ {
+ setFocus(params.getFocus());
+ }
+}
+
+//---------------------------------------------------------------------------------------------------------
+void LLFollowCam::update()
+{
+ //####################################################################################
+ // update Focus
+ //####################################################################################
+ LLVector3 offsetSubjectPosition = mSubjectPosition + (mFocusOffset * mSubjectRotation);
+
+ LLVector3 simulated_pos_agent = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
+ LLVector3 vectorFromCameraToSubject = offsetSubjectPosition - simulated_pos_agent;
+ F32 distanceFromCameraToSubject = vectorFromCameraToSubject.magVec();
+
+ LLVector3 whereFocusWantsToBe = mFocus;
+ LLVector3 focus_pt_agent = gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal);
+ if ( mFocusLocked ) // if focus is locked, only relative focus needs to be updated
+ {
+ mRelativeFocus = (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation;
+ }
+ else
+ {
+ LLVector3 focusOffset = offsetSubjectPosition - focus_pt_agent;
+ F32 focusOffsetDistance = focusOffset.magVec();
+
+ LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
+ whereFocusWantsToBe = focus_pt_agent +
+ (focusOffsetDirection * (focusOffsetDistance - mFocusThreshold));
+ if ( focusOffsetDistance > mFocusThreshold )
+ {
+ // this version normalizes focus threshold by distance
+ // so that the effect is not changed with distance
+ /*
+ F32 focusThresholdNormalizedByDistance = distanceFromCameraToSubject * mFocusThreshold;
+ if ( focusOffsetDistance > focusThresholdNormalizedByDistance )
+ {
+ LLVector3 focusOffsetDirection = focusOffset / focusOffsetDistance;
+ F32 force = focusOffsetDistance - focusThresholdNormalizedByDistance;
+ */
+
+ F32 focusLagLerp = LLCriticalDamp::getInterpolant( mFocusLag );
+ focus_pt_agent = lerp( focus_pt_agent, whereFocusWantsToBe, focusLagLerp );
+ mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(focus_pt_agent);
+ }
+ mRelativeFocus = lerp(mRelativeFocus, (focus_pt_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
+ }// if focus is not locked ---------------------------------------------
+
+
+ LLVector3 whereCameraPositionWantsToBe = gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal);
+ if ( mPositionLocked )
+ {
+ mRelativePos = (whereCameraPositionWantsToBe - mSubjectPosition) * ~mSubjectRotation;
+ }
+ else
+ {
+ //####################################################################################
+ // update Position
+ //####################################################################################
+ //-------------------------------------------------------------------------
+ // I determine the horizontal vector from the camera to the subject
+ //-------------------------------------------------------------------------
+ LLVector3 horizontalVectorFromCameraToSubject = vectorFromCameraToSubject;
+ horizontalVectorFromCameraToSubject.mV[VZ] = 0.0f;
+
+ //---------------------------------------------------------
+ // Now I determine the horizontal distance
+ //---------------------------------------------------------
+ F32 horizontalDistanceFromCameraToSubject = horizontalVectorFromCameraToSubject.magVec();
+
+ //---------------------------------------------------------
+ // Then I get the (normalized) horizontal direction...
+ //---------------------------------------------------------
+ LLVector3 horizontalDirectionFromCameraToSubject;
+ if ( horizontalDistanceFromCameraToSubject < DISTANCE_EPSILON )
+ {
+ // make sure we still have a normalized vector if distance is really small
+ // (this case is rare and fleeting)
+ horizontalDirectionFromCameraToSubject = LLVector3::z_axis;
+ }
+ else
+ {
+ // I'm not using the "normalize" method, because I can just divide by horizontalDistanceFromCameraToSubject
+ horizontalDirectionFromCameraToSubject = horizontalVectorFromCameraToSubject / horizontalDistanceFromCameraToSubject;
+ }
+
+ //------------------------------------------------------------------------------------------------------------
+ // Here is where I determine an offset relative to subject position in oder to set the ideal position.
+ //------------------------------------------------------------------------------------------------------------
+ if ( mPitchSineAndCosineNeedToBeUpdated )
+ {
+ calculatePitchSineAndCosine();
+ mPitchSineAndCosineNeedToBeUpdated = false;
+ }
+
+ LLVector3 positionOffsetFromSubject;
+ positionOffsetFromSubject.setVec
+ (
+ horizontalDirectionFromCameraToSubject.mV[ VX ] * mPitchCos,
+ horizontalDirectionFromCameraToSubject.mV[ VY ] * mPitchCos,
+ -mPitchSin
+ );
+
+ positionOffsetFromSubject *= mSimulatedDistance;
+
+ //----------------------------------------------------------------------
+ // Finally, ideal position is set by taking the subject position and
+ // extending the positionOffsetFromSubject from that
+ //----------------------------------------------------------------------
+ LLVector3 idealCameraPosition = offsetSubjectPosition - positionOffsetFromSubject;
+
+ //--------------------------------------------------------------------------------
+ // Now I prepare to move the current camera position towards its ideal position...
+ //--------------------------------------------------------------------------------
+ LLVector3 vectorFromPositionToIdealPosition = idealCameraPosition - simulated_pos_agent;
+ F32 distanceFromPositionToIdealPosition = vectorFromPositionToIdealPosition.magVec();
+
+ //put this inside of the block?
+ LLVector3 normalFromPositionToIdealPosition = vectorFromPositionToIdealPosition / distanceFromPositionToIdealPosition;
+
+ whereCameraPositionWantsToBe = simulated_pos_agent +
+ (normalFromPositionToIdealPosition * (distanceFromPositionToIdealPosition - mPositionThreshold));
+ //-------------------------------------------------------------------------------------------------
+ // The following method takes the target camera position and resets it so that it stays "behind" the subject,
+ // using behindness angle and behindness force as parameters affecting the exact behavior
+ //-------------------------------------------------------------------------------------------------
+ if ( distanceFromPositionToIdealPosition > mPositionThreshold )
+ {
+ F32 positionPullLerp = LLCriticalDamp::getInterpolant( mPositionLag );
+ simulated_pos_agent = lerp( simulated_pos_agent, whereCameraPositionWantsToBe, positionPullLerp );
+ }
+
+ //--------------------------------------------------------------------
+ // don't let the camera get farther than its official max distance
+ //--------------------------------------------------------------------
+ if ( distanceFromCameraToSubject > mMaxCameraDistantFromSubject )
+ {
+ LLVector3 directionFromCameraToSubject = vectorFromCameraToSubject / distanceFromCameraToSubject;
+ simulated_pos_agent = offsetSubjectPosition - directionFromCameraToSubject * mMaxCameraDistantFromSubject;
+ }
+
+ ////-------------------------------------------------------------------------------------------------
+ //// The following method takes mSimulatedPositionGlobal and resets it so that it stays "behind" the subject,
+ //// using behindness angle and behindness force as parameters affecting the exact behavior
+ ////-------------------------------------------------------------------------------------------------
+ updateBehindnessConstraint(gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal), simulated_pos_agent);
+ mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(simulated_pos_agent);
+
+ mRelativePos = lerp(mRelativePos, (simulated_pos_agent - mSubjectPosition) * ~mSubjectRotation, LLCriticalDamp::getInterpolant(0.05f));
+ } // if position is not locked -----------------------------------------------------------
+
+
+ //####################################################################################
+ // update UpVector
+ //####################################################################################
+ // this just points upward for now, but I anticipate future effects requiring
+ // some rolling ("banking" effects for fun, swoopy vehicles, etc.)
+ mUpVector = LLVector3::z_axis;
+}
+
+
+
+//-------------------------------------------------------------------------------------
+BOOL LLFollowCam::updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position)
+{
+ BOOL constraint_active = FALSE;
+ // only apply this stuff if the behindness angle is something other than opened up all the way
+ if ( mBehindnessMaxAngle < FOLLOW_CAM_MAX_BEHINDNESS_ANGLE - FOLLOW_CAM_BEHINDNESS_EPSILON )
+ {
+ //--------------------------------------------------------------
+ // horizontalized vector from focus to camera
+ //--------------------------------------------------------------
+ LLVector3 horizontalVectorFromFocusToCamera;
+ horizontalVectorFromFocusToCamera.setVec(cam_position - focus);
+ horizontalVectorFromFocusToCamera.mV[ VZ ] = 0.0f;
+ F32 cameraZ = cam_position.mV[ VZ ];
+
+ //--------------------------------------------------------------
+ // distance of horizontalized vector
+ //--------------------------------------------------------------
+ F32 horizontalDistance = horizontalVectorFromFocusToCamera.magVec();
+
+ //--------------------------------------------------------------------------------------------------
+ // calculate horizontalized back vector of the subject and scale by horizontalDistance
+ //--------------------------------------------------------------------------------------------------
+ LLVector3 horizontalSubjectBack( -1.0f, 0.0f, 0.0f );
+ horizontalSubjectBack *= mSubjectRotation;
+ horizontalSubjectBack.mV[ VZ ] = 0.0f;
+ horizontalSubjectBack.normVec(); // because horizontalizing might make it shorter than 1
+ horizontalSubjectBack *= horizontalDistance;
+
+ //--------------------------------------------------------------------------------------------------
+ // find the angle (in degrees) between these vectors
+ //--------------------------------------------------------------------------------------------------
+ F32 cameraOffsetAngle = 0.f;
+ LLVector3 cameraOffsetRotationAxis;
+ LLQuaternion camera_offset_rotation;
+ camera_offset_rotation.shortestArc(horizontalSubjectBack, horizontalVectorFromFocusToCamera);
+ camera_offset_rotation.getAngleAxis(&cameraOffsetAngle, cameraOffsetRotationAxis);
+ cameraOffsetAngle *= RAD_TO_DEG;
+
+ if ( cameraOffsetAngle > mBehindnessMaxAngle )
+ {
+ F32 fraction = ((cameraOffsetAngle - mBehindnessMaxAngle) / cameraOffsetAngle) * LLCriticalDamp::getInterpolant(mBehindnessLag);
+ cam_position = focus + horizontalSubjectBack * (slerp(fraction, camera_offset_rotation, LLQuaternion::DEFAULT));
+ cam_position.mV[VZ] = cameraZ; // clamp z value back to what it was before we started messing with it
+ constraint_active = TRUE;
+ }
+ }
+ return constraint_active;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::calculatePitchSineAndCosine()
+{
+ F32 radian = mPitch * DEG_TO_RAD;
+ mPitchCos = cos( radian );
+ mPitchSin = sin( radian );
+}
+
+//---------------------------------------------------------
+void LLFollowCam::setSubjectPositionAndRotation( const LLVector3 p, const LLQuaternion r )
+{
+ mSubjectPosition = p;
+ mSubjectRotation = r;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::zoom( S32 z )
+{
+ F32 zoomAmount = z * mSimulatedDistance * FOLLOW_CAM_ZOOM_FACTOR;
+
+ if (( zoomAmount < FOLLOW_CAM_MIN_ZOOM_AMOUNT )
+ && ( zoomAmount > -FOLLOW_CAM_MIN_ZOOM_AMOUNT ))
+ {
+ if ( zoomAmount < 0.0f )
+ {
+ zoomAmount = -FOLLOW_CAM_MIN_ZOOM_AMOUNT;
+ }
+ else
+ {
+ zoomAmount = FOLLOW_CAM_MIN_ZOOM_AMOUNT;
+ }
+ }
+
+ mSimulatedDistance += zoomAmount;
+
+ mZoomedToMinimumDistance = false;
+ if ( mSimulatedDistance < FOLLOW_CAM_MIN_DISTANCE )
+ {
+ mSimulatedDistance = FOLLOW_CAM_MIN_DISTANCE;
+
+ // if zoomAmount is negative (i.e., getting closer), then
+ // this signifies having hit the minimum:
+ if ( zoomAmount < 0.0f )
+ {
+ mZoomedToMinimumDistance = true;
+ }
+ }
+ else if ( mSimulatedDistance > mMaxCameraDistantFromSubject )
+ {
+ mSimulatedDistance = mMaxCameraDistantFromSubject;
+ }
+}
+
+
+//---------------------------------------------------------
+bool LLFollowCam::isZoomedToMinimumDistance()
+{
+ return mZoomedToMinimumDistance;
+}
+
+
+//---------------------------------------------------------
+void LLFollowCam::reset( const LLVector3 p, const LLVector3 f , const LLVector3 u )
+{
+ setPosition(p);
+ setFocus(f);
+ mUpVector = u;
+}
+
+//---------------------------------------------------------
+void LLFollowCam::setMaxCameraDistantFromSubject( F32 m )
+{
+ mMaxCameraDistantFromSubject = m;
+}
+
+
+void LLFollowCam::setPitch( F32 p )
+{
+ LLFollowCamParams::setPitch(p);
+ mPitchSineAndCosineNeedToBeUpdated = true; // important
+}
+
+void LLFollowCam::setDistance( F32 d )
+{
+ if (d != mDistance)
+ {
+ LLFollowCamParams::setDistance(d);
+ mSimulatedDistance = d;
+ mZoomedToMinimumDistance = false;
+ }
+}
+
+void LLFollowCam::setPosition( const LLVector3& p )
+{
+ if (p != mPosition)
+ {
+ LLFollowCamParams::setPosition(p);
+ mSimulatedPositionGlobal = gAgent.getPosGlobalFromAgent(mPosition);
+ if (mPositionLocked)
+ {
+ mRelativePos = (mPosition - mSubjectPosition) * ~mSubjectRotation;
+ }
+ }
+}
+
+void LLFollowCam::setFocus( const LLVector3& f )
+{
+ if (f != mFocus)
+ {
+ LLFollowCamParams::setFocus(f);
+ mSimulatedFocusGlobal = gAgent.getPosGlobalFromAgent(f);
+ if (mFocusLocked)
+ {
+ mRelativeFocus = (mFocus - mSubjectPosition) * ~mSubjectRotation;
+ }
+ }
+}
+
+void LLFollowCam::setPositionLocked( bool locked )
+{
+ LLFollowCamParams::setPositionLocked(locked);
+ if (locked)
+ {
+ // propagate set position to relative position
+ mRelativePos = (gAgent.getPosAgentFromGlobal(mSimulatedPositionGlobal) - mSubjectPosition) * ~mSubjectRotation;
+ }
+}
+
+void LLFollowCam::setFocusLocked( bool locked )
+{
+ LLFollowCamParams::setFocusLocked(locked);
+ if (locked)
+ {
+ // propagate set position to relative position
+ mRelativeFocus = (gAgent.getPosAgentFromGlobal(mSimulatedFocusGlobal) - mSubjectPosition) * ~mSubjectRotation;
+ }
+}
+
+
+LLVector3 LLFollowCam::getSimulatedPosition() const
+{
+ // return simulated position
+ return mSubjectPosition + (mRelativePos * mSubjectRotation);
+}
+
+LLVector3 LLFollowCam::getSimulatedFocus() const
+{
+ // return simulated focus point
+ return mSubjectPosition + (mRelativeFocus * mSubjectRotation);
+}
+
+LLVector3 LLFollowCam::getUpVector()
+{
+ return mUpVector;
+}
+
+
+//------------------------------------
+// Destructor
+//------------------------------------
+LLFollowCam::~LLFollowCam()
+{
+}
+
+
+//-------------------------------------------------------
+// LLFollowCamMgr
+//-------------------------------------------------------
+//static
+void LLFollowCamMgr::cleanupClass()
+{
+ for (param_map_t::iterator iter = sParamMap.begin(); iter != sParamMap.end(); ++iter)
+ {
+ LLFollowCamParams* params = iter->second;
+ delete params;
+ }
+ sParamMap.clear();
+}
+
+//static
+void LLFollowCamMgr::setPositionLag( const LLUUID& source, F32 lag)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionLag(lag);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusLag( const LLUUID& source, F32 lag)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusLag(lag);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusThreshold( const LLUUID& source, F32 threshold)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusThreshold(threshold);
+ }
+
+}
+
+//static
+void LLFollowCamMgr::setPositionThreshold( const LLUUID& source, F32 threshold)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionThreshold(threshold);
+ }
+}
+
+//static
+void LLFollowCamMgr::setDistance( const LLUUID& source, F32 distance)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setDistance(distance);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPitch( const LLUUID& source, F32 pitch)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPitch(pitch);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusOffset( const LLUUID& source, const LLVector3& offset)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusOffset(offset);
+ }
+}
+
+//static
+void LLFollowCamMgr::setBehindnessAngle( const LLUUID& source, F32 angle)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setBehindnessAngle(angle);
+ }
+}
+
+//static
+void LLFollowCamMgr::setBehindnessLag( const LLUUID& source, F32 force)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setBehindnessLag(force);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPosition( const LLUUID& source, const LLVector3 position)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPosition(position);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocus( const LLUUID& source, const LLVector3 focus)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocus(focus);
+ }
+}
+
+//static
+void LLFollowCamMgr::setPositionLocked( const LLUUID& source, bool locked)
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setPositionLocked(locked);
+ }
+}
+
+//static
+void LLFollowCamMgr::setFocusLocked( const LLUUID& source, bool locked )
+{
+ LLFollowCamParams* paramsp = getParamsForID(source);
+ if (paramsp)
+ {
+ paramsp->setFocusLocked(locked);
+ }
+}
+
+//static
+LLFollowCamParams* LLFollowCamMgr::getParamsForID(const LLUUID& source)
+{
+ LLFollowCamParams* params = NULL;
+
+ param_map_t::iterator found_it = sParamMap.find(source);
+ if (found_it == sParamMap.end()) // didn't find it?
+ {
+ params = new LLFollowCamParams();
+ sParamMap[source] = params;
+ }
+ else
+ {
+ params = found_it->second;
+ }
+
+ return params;
+}
+
+//static
+LLFollowCamParams* LLFollowCamMgr::getActiveFollowCamParams()
+{
+ if (sParamStack.empty())
+ {
+ return NULL;
+ }
+
+ return sParamStack.back();
+}
+
+//static
+void LLFollowCamMgr::setCameraActive( const LLUUID& source, bool active )
+{
+ LLFollowCamParams* params = getParamsForID(source);
+ param_stack_t::iterator found_it = std::find(sParamStack.begin(), sParamStack.end(), params);
+ if (found_it != sParamStack.end())
+ {
+ sParamStack.erase(found_it);
+ }
+ // put on top of stack
+ if(active)
+ {
+ sParamStack.push_back(params);
+ }
+}
+
+//static
+void LLFollowCamMgr::removeFollowCamParams(const LLUUID& source)
+{
+ setCameraActive(source, FALSE);
+ LLFollowCamParams* params = getParamsForID(source);
+ sParamMap.erase(source);
+ delete params;
+}
+
+//static
+bool LLFollowCamMgr::isScriptedCameraSource(const LLUUID& source)
+{
+ param_map_t::iterator found_it = sParamMap.find(source);
+ return (found_it != sParamMap.end());
+}
+
+//static
+void LLFollowCamMgr::dump()
+{
+ S32 param_count = 0;
+ llinfos << "Scripted camera active stack" << llendl;
+ for (param_stack_t::iterator param_it = sParamStack.begin();
+ param_it != sParamStack.end();
+ ++param_it)
+ {
+ llinfos << param_count++ <<
+ " rot_limit: " << (*param_it)->getBehindnessAngle() <<
+ " rot_lag: " << (*param_it)->getBehindnessLag() <<
+ " distance: " << (*param_it)->getDistance() <<
+ " focus: " << (*param_it)->getFocus() <<
+ " foc_lag: " << (*param_it)->getFocusLag() <<
+ " foc_lock: " << ((*param_it)->getFocusLocked() ? "Y" : "N") <<
+ " foc_offset: " << (*param_it)->getFocusOffset() <<
+ " foc_thresh: " << (*param_it)->getFocusThreshold() <<
+ " pitch: " << (*param_it)->getPitch() <<
+ " pos: " << (*param_it)->getPosition() <<
+ " pos_lag: " << (*param_it)->getPositionLag() <<
+ " pos_lock: " << ((*param_it)->getPositionLocked() ? "Y" : "N") <<
+ " pos_thresh: " << (*param_it)->getPositionThreshold() << llendl;
+ }
+}
+
diff --git a/indra/newview/llfollowcam.h b/indra/newview/llfollowcam.h
new file mode 100644
index 0000000000..22450d2792
--- /dev/null
+++ b/indra/newview/llfollowcam.h
@@ -0,0 +1,216 @@
+/**
+ * @file llfollowcam.h
+ * @author Jeffrey Ventrella
+ * @brief LLFollowCam class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//--------------------------------------------------------------------
+// FollowCam
+//
+// The FollowCam controls three dynamic variables which determine
+// a camera orientation and position for a "loose" third-person view
+// (orientation being derived from a combination of focus and up
+// vector). It is good for fast-moving vehicles that change
+// acceleration a lot, but it can also be general-purpose, like for
+// avatar navigation. It has a handful of parameters allowing it to
+// be tweaked to assume different styles of tracking objects.
+//
+//--------------------------------------------------------------------
+#ifndef LL_FOLLOWCAM_H
+#define LL_FOLLOWCAM_H
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "lltimer.h"
+#include "llquaternion.h"
+#include "llcriticaldamp.h"
+#include <map>
+#include <vector>
+
+class LLFollowCamParams
+{
+public:
+ LLFollowCamParams();
+ virtual ~LLFollowCamParams();
+
+ //--------------------------------------
+ // setty setty set set
+ //--------------------------------------
+ virtual void setPositionLag ( F32 );
+ virtual void setFocusLag ( F32 );
+ virtual void setFocusThreshold ( F32 );
+ virtual void setPositionThreshold ( F32 );
+ virtual void setDistance ( F32 );
+ virtual void setPitch ( F32 );
+ virtual void setFocusOffset ( const LLVector3& );
+ virtual void setBehindnessAngle ( F32 );
+ virtual void setBehindnessLag ( F32 );
+ virtual void setPosition ( const LLVector3& );
+ virtual void setFocus ( const LLVector3& );
+ virtual void setPositionLocked ( bool );
+ virtual void setFocusLocked ( bool );
+
+
+ //--------------------------------------
+ // getty getty get get
+ //--------------------------------------
+ virtual F32 getPositionLag() const;
+ virtual F32 getFocusLag() const;
+ virtual F32 getPositionThreshold() const;
+ virtual F32 getFocusThreshold() const;
+ virtual F32 getDistance() const;
+ virtual F32 getPitch() const;
+ virtual LLVector3 getFocusOffset() const;
+ virtual F32 getBehindnessAngle() const;
+ virtual F32 getBehindnessLag() const;
+ virtual LLVector3 getPosition() const;
+ virtual LLVector3 getFocus() const;
+ virtual bool getFocusLocked() const;
+ virtual bool getPositionLocked() const;
+ virtual bool getUseFocus() const { return mUseFocus; }
+ virtual bool getUsePosition() const { return mUsePosition; }
+
+protected:
+ F32 mPositionLag;
+ F32 mFocusLag;
+ F32 mFocusThreshold;
+ F32 mPositionThreshold;
+ F32 mDistance;
+ F32 mPitch;
+ LLVector3 mFocusOffset;
+ F32 mBehindnessMaxAngle;
+ F32 mBehindnessLag;
+ F32 mMaxCameraDistantFromSubject;
+
+ bool mPositionLocked;
+ bool mFocusLocked;
+ bool mUsePosition; // specific camera point specified by script
+ bool mUseFocus; // specific focus point specified by script
+ LLVector3 mPosition; // where the camera is (in world-space)
+ LLVector3 mFocus; // what the camera is aimed at (in world-space)
+};
+
+class LLFollowCam : public LLFollowCamParams
+{
+public:
+ //--------------------
+ // Contructor
+ //--------------------
+ LLFollowCam();
+
+ //--------------------
+ // Destructor
+ //--------------------
+ virtual ~LLFollowCam();
+
+ //---------------------------------------------------------------------------------------
+ // The following methods must be called every time step. However, if you know for
+ // sure that your subject matter (what the camera is looking at) is not moving,
+ // then you can get away with not calling "update" But keep in mind that "update"
+ // may still be needed after the subject matter has stopped moving because the
+ // camera may still need to animate itself catching up to its ideal resting place.
+ //---------------------------------------------------------------------------------------
+ void setSubjectPositionAndRotation ( const LLVector3 p, const LLQuaternion r );
+ void update();
+
+ // initialize from another instance of llfollowcamparams
+ void copyParams(LLFollowCamParams& params);
+
+ //-----------------------------------------------------------------------------------
+ // this is how to bang the followCam into a specific configuration. Keep in mind
+ // that it will immediately try to adjust these values according to its attributes.
+ //-----------------------------------------------------------------------------------
+ void reset( const LLVector3 position, const LLVector3 focus, const LLVector3 upVector );
+
+ void setMaxCameraDistantFromSubject ( F32 m ); // this should be determined by llAgent
+ bool isZoomedToMinimumDistance();
+ LLVector3 getUpVector();
+ void zoom( S32 );
+
+ // overrides for setters and getters
+ virtual void setPitch( F32 );
+ virtual void setDistance( F32 );
+ virtual void setPosition(const LLVector3& pos);
+ virtual void setFocus(const LLVector3& focus);
+ virtual void setPositionLocked ( bool );
+ virtual void setFocusLocked ( bool );
+
+ LLVector3 getSimulatedPosition() const;
+ LLVector3 getSimulatedFocus() const;
+
+ //------------------------------------------
+ // protected members of FollowCam
+ //------------------------------------------
+protected:
+ F32 mPositionLagTimeScale; // derived from mPositionLag
+ F32 mFocusLagTimeScale; // derived from mFocusLag
+ F32 mPitchCos; // derived from mPitch
+ F32 mPitchSin; // derived from mPitch
+ LLGlobalVec mSimulatedPositionGlobal; // where the camera is (global coordinates), simulated
+ LLGlobalVec mSimulatedFocusGlobal; // what the camera is aimed at (global coordinates), simulated
+ F32 mSimulatedDistance;
+
+ //---------------------
+ // dynamic variables
+ //---------------------
+ bool mZoomedToMinimumDistance;
+ LLFrameTimer mTimer;
+ LLVector3 mSubjectPosition; // this is the position of what I'm looking at
+ LLQuaternion mSubjectRotation; // this is the rotation of what I'm looking at
+ LLVector3 mUpVector; // the camera's up vector in world-space (determines roll)
+ LLVector3 mRelativeFocus;
+ LLVector3 mRelativePos;
+
+ bool mPitchSineAndCosineNeedToBeUpdated;
+
+ //------------------------------------------
+ // protected methods of FollowCam
+ //------------------------------------------
+protected:
+ void calculatePitchSineAndCosine();
+ BOOL updateBehindnessConstraint(LLVector3 focus, LLVector3& cam_position);
+
+};// end of FollowCam class
+
+
+class LLFollowCamMgr
+{
+public:
+ static void cleanupClass ( );
+
+ static void setPositionLag ( const LLUUID& source, F32 lag);
+ static void setFocusLag ( const LLUUID& source, F32 lag);
+ static void setFocusThreshold ( const LLUUID& source, F32 threshold);
+ static void setPositionThreshold ( const LLUUID& source, F32 threshold);
+ static void setDistance ( const LLUUID& source, F32 distance);
+ static void setPitch ( const LLUUID& source, F32 pitch);
+ static void setFocusOffset ( const LLUUID& source, const LLVector3& offset);
+ static void setBehindnessAngle ( const LLUUID& source, F32 angle);
+ static void setBehindnessLag ( const LLUUID& source, F32 lag);
+ static void setPosition ( const LLUUID& source, const LLVector3 position);
+ static void setFocus ( const LLUUID& source, const LLVector3 focus);
+ static void setPositionLocked ( const LLUUID& source, bool locked);
+ static void setFocusLocked ( const LLUUID& source, bool locked );
+
+ static void setCameraActive ( const LLUUID& source, bool active );
+
+ static LLFollowCamParams* getActiveFollowCamParams();
+ static LLFollowCamParams* getParamsForID(const LLUUID& source);
+ static void removeFollowCamParams(const LLUUID& source);
+ static bool isScriptedCameraSource(const LLUUID& source);
+ static void dump();
+
+protected:
+
+ typedef std::map<LLUUID, LLFollowCamParams*> param_map_t;
+ static param_map_t sParamMap;
+
+ typedef std::vector<LLFollowCamParams*> param_stack_t;
+ static param_stack_t sParamStack;
+};
+
+#endif //LL_FOLLOWCAM_H
diff --git a/indra/newview/llgesturemgr.cpp b/indra/newview/llgesturemgr.cpp
new file mode 100644
index 0000000000..b9ae06963f
--- /dev/null
+++ b/indra/newview/llgesturemgr.cpp
@@ -0,0 +1,1067 @@
+/**
+ * @file llgesturemgr.cpp
+ * @brief Manager for playing gestures on the viewer
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llgesturemgr.h"
+
+// system
+#include <functional>
+#include <algorithm>
+#include <boost/tokenizer.hpp>
+
+// library
+#include "lldatapacker.h"
+#include "llinventory.h"
+#include "llmultigesture.h"
+#include "llstl.h"
+#include "llstring.h" // todo: remove
+#include "llvfile.h"
+#include "message.h"
+
+// newview
+#include "llagent.h"
+#include "llchatbar.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llviewermessage.h"
+#include "llvoavatar.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+
+LLGestureManager gGestureManager;
+
+// Longest time, in seconds, to wait for all animations to stop playing
+const F32 MAX_WAIT_ANIM_SECS = 30.f;
+
+
+// Lightweight constructor.
+// init() does the heavy lifting.
+LLGestureManager::LLGestureManager()
+: mValid(FALSE),
+ mPlaying(),
+ mActive(),
+ mLoadingCount(0)
+{ }
+
+
+// We own the data for gestures, so clean them up.
+LLGestureManager::~LLGestureManager()
+{
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+
+ delete gesture;
+ gesture = NULL;
+ }
+}
+
+
+void LLGestureManager::init()
+{
+ // TODO
+}
+
+
+// Use this version when you have the item_id but not the asset_id,
+// and you KNOW the inventory is loaded.
+void LLGestureManager::activateGesture(const LLUUID& item_id)
+{
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if (!item) return;
+
+ LLUUID asset_id = item->getAssetUUID();
+
+ mLoadingCount = 1;
+ mDeactivateSimilarNames.clear();
+
+ const BOOL inform_server = TRUE;
+ const BOOL deactivate_similar = TRUE;
+ activateGestureWithAsset(item_id, asset_id, inform_server, deactivate_similar);
+}
+
+
+void LLGestureManager::activateGestures(LLViewerInventoryItem::item_array_t& items)
+{
+ // Load up the assets
+ S32 count = 0;
+ LLViewerInventoryItem::item_array_t::const_iterator it;
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+ else
+ { // Make gesture active and persistent through login sessions. -spatters 07-12-06
+ activateGesture(item->getUUID());
+ }
+
+ count++;
+ }
+
+ mLoadingCount = count;
+ mDeactivateSimilarNames.clear();
+
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+
+ // Don't inform server, we'll do that in bulk
+ const BOOL no_inform_server = FALSE;
+ const BOOL deactivate_similar = TRUE;
+ activateGestureWithAsset(item->getUUID(), item->getAssetUUID(),
+ no_inform_server,
+ deactivate_similar);
+ }
+
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+
+ BOOL start_message = TRUE;
+
+ for (it = items.begin(); it != items.end(); ++it)
+ {
+ LLViewerInventoryItem* item = *it;
+
+ if (isGestureActive(item->getUUID()))
+ {
+ continue;
+ }
+
+ if (start_message)
+ {
+ msg->newMessage("ActivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+ start_message = FALSE;
+ }
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item->getUUID());
+ msg->addUUID("AssetID", item->getAssetUUID());
+ msg->addU32("GestureFlags", 0x0);
+
+ if (msg->getCurrentSendTotal() > MTUBYTES)
+ {
+ gAgent.sendReliableMessage();
+ start_message = TRUE;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+
+struct LLLoadInfo
+{
+ LLUUID mItemID;
+ BOOL mInformServer;
+ BOOL mDeactivateSimilar;
+};
+
+// If inform_server is true, will send a message upstream to update
+// the user_gesture_active table.
+void LLGestureManager::activateGestureWithAsset(const LLUUID& item_id,
+ const LLUUID& asset_id,
+ BOOL inform_server,
+ BOOL deactivate_similar)
+{
+ // If gesture is already active, nothing to do.
+ if (isGestureActive(item_id))
+ {
+ llwarns << "Tried to loadGesture twice " << item_id << llendl;
+ return;
+ }
+
+// if (asset_id.isNull())
+// {
+// llwarns << "loadGesture() - gesture has no asset" << llendl;
+// return;
+// }
+
+ // For now, put NULL into the item map. We'll build a gesture
+ // class object when the asset data arrives.
+ mActive[item_id] = NULL;
+
+ // Copy the UUID
+ if (asset_id.notNull())
+ {
+ LLLoadInfo* info = new LLLoadInfo;
+ info->mItemID = item_id;
+ info->mInformServer = inform_server;
+ info->mDeactivateSimilar = deactivate_similar;
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void*)info,
+ high_priority);
+ }
+ else
+ {
+ notifyObservers();
+ }
+}
+
+
+void LLGestureManager::deactivateGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end())
+ {
+ llwarns << "deactivateGesture for inactive gesture " << item_id << llendl;
+ return;
+ }
+
+ // mActive owns this gesture pointer, so clean up memory.
+ LLMultiGesture* gesture = (*it).second;
+
+ // Can be NULL gestures in the map
+ if (gesture)
+ {
+ stopGesture(gesture);
+
+ delete gesture;
+ gesture = NULL;
+ }
+
+ mActive.erase(it);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("DeactivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item_id);
+ msg->addU32("GestureFlags", 0x0);
+
+ gAgent.sendReliableMessage();
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::deactivateSimilarGestures(LLMultiGesture* in, const LLUUID& in_item_id)
+{
+ std::vector<LLUUID> gest_item_ids;
+
+ // Deactivate all gestures that match
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); )
+ {
+ const LLUUID& item_id = (*it).first;
+ LLMultiGesture* gest = (*it).second;
+
+ // Don't deactivate the gesture we are looking for duplicates of
+ // (for replaceGesture)
+ if (!gest || item_id == in_item_id)
+ {
+ // legal, can have null pointers in list
+ ++it;
+ }
+ else if ((!gest->mTrigger.empty() && gest->mTrigger == in->mTrigger)
+ || (gest->mKey != KEY_NONE && gest->mKey == in->mKey && gest->mMask == in->mMask))
+ {
+ gest_item_ids.push_back(item_id);
+
+ stopGesture(gest);
+
+ delete gest;
+ gest = NULL;
+
+ mActive.erase(it++);
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ // Inform database of the change
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_message = TRUE;
+ std::vector<LLUUID>::const_iterator vit = gest_item_ids.begin();
+ while (vit != gest_item_ids.end())
+ {
+ if (start_message)
+ {
+ msg->newMessage("DeactivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+ start_message = FALSE;
+ }
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", *vit);
+ msg->addU32("GestureFlags", 0x0);
+
+ if (msg->getCurrentSendTotal() > MTUBYTES)
+ {
+ gAgent.sendReliableMessage();
+ start_message = TRUE;
+ }
+
+ ++vit;
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ // Add to the list of names for the user.
+ for (vit = gest_item_ids.begin(); vit != gest_item_ids.end(); ++vit)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*vit);
+ if (!item) continue;
+
+ mDeactivateSimilarNames.append(item->getName());
+ mDeactivateSimilarNames.append("\n");
+ }
+
+ notifyObservers();
+}
+
+
+BOOL LLGestureManager::isGestureActive(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ return (it != mActive.end());
+}
+
+
+BOOL LLGestureManager::isGesturePlaying(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return FALSE;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return FALSE;
+
+ return gesture->mPlaying;
+}
+
+void LLGestureManager::replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end())
+ {
+ llwarns << "replaceGesture for inactive gesture " << item_id << llendl;
+ return;
+ }
+
+ LLMultiGesture* old_gesture = (*it).second;
+ stopGesture(old_gesture);
+
+ mActive.erase(item_id);
+
+ mActive[item_id] = new_gesture;
+
+ delete old_gesture;
+ old_gesture = NULL;
+
+ if (asset_id.notNull())
+ {
+ mLoadingCount = 1;
+ mDeactivateSimilarNames.clear();
+
+ LLLoadInfo* info = new LLLoadInfo;
+ info->mItemID = item_id;
+ info->mInformServer = TRUE;
+ info->mDeactivateSimilar = TRUE;
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void*)info,
+ high_priority);
+ }
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::playGesture(LLMultiGesture* gesture)
+{
+ if (!gesture) return;
+
+ // Reset gesture to first step
+ gesture->mCurrentStep = 0;
+
+ // Add to list of playing
+ gesture->mPlaying = TRUE;
+ mPlaying.push_back(gesture);
+
+ // And get it going
+ stepGesture(gesture);
+
+ notifyObservers();
+}
+
+
+// Convenience function that looks up the item_id for you.
+void LLGestureManager::playGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return;
+
+ playGesture(gesture);
+}
+
+
+// Iterates through space delimited tokens in string, triggering any gestures found.
+// Generates a revised string that has the found tokens replaced by their replacement strings
+// and (as a minor side effect) has multiple spaces in a row replaced by single spaces.
+BOOL LLGestureManager::triggerAndReviseString(const std::string &utf8str, std::string* revised_string)
+{
+ LLString tokenized = LLString(utf8str.c_str());
+
+ BOOL found_gestures = FALSE;
+ BOOL first_token = TRUE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(tokenized, sep);
+ tokenizer::iterator token_iter;
+
+ for( token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ const char* cur_token = token_iter->c_str();
+ LLMultiGesture* gesture = NULL;
+
+ // Only pay attention to the first gesture in the string.
+ if( !found_gestures )
+ {
+ LLString cur_token_lower = cur_token;
+ LLString::toLower(cur_token_lower);
+
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ gesture = (*it).second;
+
+ // Gesture asset data might not have arrived yet
+ if (!gesture) continue;
+
+ if (!stricmp(gesture->mTrigger.c_str(), cur_token_lower.c_str()))
+ {
+ playGesture(gesture);
+
+ if (!gesture->mReplaceText.empty())
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+
+ // Don't muck with the user's capitalization if we don't have to.
+ LLString output = gesture->mReplaceText.c_str();
+ LLString output_lower = output;
+ LLString::toLower(output_lower);
+ if( cur_token_lower == output_lower )
+ {
+ revised_string->append( cur_token );
+ }
+ else
+ {
+ revised_string->append( output );
+ }
+ }
+ found_gestures = TRUE;
+ break;
+ }
+ gesture = NULL;
+ }
+ }
+
+ if( !gesture )
+ {
+ if( !first_token )
+ {
+ revised_string->append( " " );
+ }
+ revised_string->append( cur_token );
+ }
+
+ first_token = FALSE;
+ }
+ return found_gestures;
+}
+
+
+BOOL LLGestureManager::triggerGesture(KEY key, MASK mask)
+{
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+
+ // asset data might not have arrived yet
+ if (!gesture) continue;
+
+ if (gesture->mKey == key
+ && gesture->mMask == mask)
+ {
+ playGesture(gesture);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+S32 LLGestureManager::getPlayingCount() const
+{
+ return mPlaying.size();
+}
+
+
+struct IsGesturePlaying : public std::unary_function<LLMultiGesture*, bool>
+{
+ bool operator()(const LLMultiGesture* gesture) const
+ {
+ return gesture->mPlaying ? true : false;
+ }
+};
+
+void LLGestureManager::update()
+{
+ S32 i;
+ for (i = 0; i < (S32)mPlaying.size(); ++i)
+ {
+ stepGesture(mPlaying[i]);
+ }
+
+ // Clear out gestures that are done, by moving all the
+ // ones that are still playing to the front.
+ std::vector<LLMultiGesture*>::iterator new_end;
+ new_end = std::partition(mPlaying.begin(),
+ mPlaying.end(),
+ IsGesturePlaying());
+
+ // Something finished playing
+ if (new_end != mPlaying.end())
+ {
+ // Delete the completed gestures that want deletion
+ std::vector<LLMultiGesture*>::iterator it;
+ for (it = new_end; it != mPlaying.end(); ++it)
+ {
+ LLMultiGesture* gesture = *it;
+
+ if (gesture->mDoneCallback)
+ {
+ gesture->mDoneCallback(gesture, gesture->mCallbackData);
+
+ // callback might have deleted gesture, can't
+ // rely on this pointer any more
+ gesture = NULL;
+ }
+ }
+
+ // And take done gestures out of the playing list
+ mPlaying.erase(new_end, mPlaying.end());
+
+ notifyObservers();
+ }
+}
+
+
+// Run all steps until you're either done or hit a wait.
+void LLGestureManager::stepGesture(LLMultiGesture* gesture)
+{
+ if (!gesture)
+ {
+ return;
+ }
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar) return;
+
+ // Of the ones that started playing, have any stopped?
+
+ std::set<LLUUID>::iterator gest_it;
+ for (gest_it = gesture->mPlayingAnimIDs.begin();
+ gest_it != gesture->mPlayingAnimIDs.end();
+ )
+ {
+ // look in signaled animations (simulator's view of what is
+ // currently playing.
+ LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
+ if (play_it != avatar->mSignaledAnimations.end())
+ {
+ ++gest_it;
+ }
+ else
+ {
+ // not found, so not currently playing or scheduled to play
+ // delete from the triggered set
+ gesture->mPlayingAnimIDs.erase(gest_it++);
+ }
+ }
+
+ // Of all the animations that we asked the sim to start for us,
+ // pick up the ones that have actually started.
+ for (gest_it = gesture->mRequestedAnimIDs.begin();
+ gest_it != gesture->mRequestedAnimIDs.end();
+ )
+ {
+ LLVOAvatar::AnimIterator play_it = avatar->mSignaledAnimations.find(*gest_it);
+ if (play_it != avatar->mSignaledAnimations.end())
+ {
+ // Hooray, this animation has started playing!
+ // Copy into playing.
+ gesture->mPlayingAnimIDs.insert(*gest_it);
+ gesture->mRequestedAnimIDs.erase(gest_it++);
+ }
+ else
+ {
+ // nope, not playing yet
+ ++gest_it;
+ }
+ }
+
+ // Run the current steps
+ BOOL waiting = FALSE;
+ while (!waiting && gesture->mPlaying)
+ {
+ // Get the current step, if there is one.
+ // Otherwise enter the waiting at end state.
+ LLGestureStep* step = NULL;
+ if (gesture->mCurrentStep < (S32)gesture->mSteps.size())
+ {
+ step = gesture->mSteps[gesture->mCurrentStep];
+ llassert(step != NULL);
+ }
+ else
+ {
+ // step stays null, we're off the end
+ gesture->mWaitingAtEnd = TRUE;
+ }
+
+
+ // If we're waiting at the end, wait for all gestures to stop
+ // playing.
+ // TODO: Wait for all sounds to complete as well.
+ if (gesture->mWaitingAtEnd)
+ {
+ // Neither do we have any pending requests, nor are they
+ // still playing.
+ if ((gesture->mRequestedAnimIDs.empty()
+ && gesture->mPlayingAnimIDs.empty()))
+ {
+ // all animations are done playing
+ gesture->mWaitingAtEnd = FALSE;
+ gesture->mPlaying = FALSE;
+ }
+ else
+ {
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // If we're waiting on our animations to stop, poll for
+ // completion.
+ if (gesture->mWaitingAnimations)
+ {
+ // Neither do we have any pending requests, nor are they
+ // still playing.
+ if ((gesture->mRequestedAnimIDs.empty()
+ && gesture->mPlayingAnimIDs.empty()))
+ {
+ // all animations are done playing
+ gesture->mWaitingAnimations = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else if (gesture->mWaitTimer.getElapsedTimeF32() > MAX_WAIT_ANIM_SECS)
+ {
+ // we've waited too long for an animation
+ llinfos << "Waited too long for animations to stop, continuing gesture."
+ << llendl;
+ gesture->mWaitingAnimations = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else
+ {
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // If we're waiting a fixed amount of time, check for timer
+ // expiration.
+ if (gesture->mWaitingTimer)
+ {
+ // We're waiting for a certain amount of time to pass
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+
+ F32 elapsed = gesture->mWaitTimer.getElapsedTimeF32();
+ if (elapsed > wait_step->mWaitSeconds)
+ {
+ // wait is done, continue execution
+ gesture->mWaitingTimer = FALSE;
+ gesture->mCurrentStep++;
+ }
+ else
+ {
+ // we're waiting, so execution is done for now
+ waiting = TRUE;
+ }
+ continue;
+ }
+
+ // Not waiting, do normal execution
+ runStep(gesture, step);
+ }
+}
+
+
+void LLGestureManager::runStep(LLMultiGesture* gesture, LLGestureStep* step)
+{
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (anim_step->mAnimAssetID.isNull())
+ {
+ gesture->mCurrentStep++;
+ }
+
+ if (anim_step->mFlags & ANIM_FLAG_STOP)
+ {
+ gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_STOP);
+ // remove it from our request set in case we just requested it
+ std::set<LLUUID>::iterator set_it = gesture->mRequestedAnimIDs.find(anim_step->mAnimAssetID);
+ if (set_it != gesture->mRequestedAnimIDs.end())
+ {
+ gesture->mRequestedAnimIDs.erase(set_it);
+ }
+ }
+ else
+ {
+ gAgent.sendAnimationRequest(anim_step->mAnimAssetID, ANIM_REQUEST_START);
+ // Indicate that we've requested this animation to play as
+ // part of this gesture (but it won't start playing for at
+ // least one round-trip to simulator).
+ gesture->mRequestedAnimIDs.insert(anim_step->mAnimAssetID);
+ }
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ const LLUUID& sound_id = sound_step->mSoundAssetID;
+ const F32 volume = 1.f;
+ send_sound_trigger(sound_id, volume);
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ std::string chat_text = chat_step->mChatText;
+ // Don't animate the nodding, as this might not blend with
+ // other playing animations.
+ const BOOL animate = FALSE;
+
+ gChatBar->sendChatFromViewer(chat_text, CHAT_TYPE_NORMAL, animate);
+ gesture->mCurrentStep++;
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ if (wait_step->mFlags & WAIT_FLAG_TIME)
+ {
+ gesture->mWaitingTimer = TRUE;
+ gesture->mWaitTimer.reset();
+ }
+ else if (wait_step->mFlags & WAIT_FLAG_ALL_ANIM)
+ {
+ gesture->mWaitingAnimations = TRUE;
+ // Use the wait timer as a deadlock breaker for animation
+ // waits.
+ gesture->mWaitTimer.reset();
+ }
+ else
+ {
+ gesture->mCurrentStep++;
+ }
+ // Don't increment instruction pointer until wait is complete.
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+// static
+void LLGestureManager::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLLoadInfo* info = (LLLoadInfo*)user_data;
+
+ LLUUID item_id = info->mItemID;
+ BOOL inform_server = info->mInformServer;
+ BOOL deactivate_similar = info->mDeactivateSimilar;
+
+ delete info;
+ info = NULL;
+
+ gGestureManager.mLoadingCount--;
+
+ if (0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size+1];
+ file.read((U8*)buffer, size);
+ // ensure there's a trailing NULL so strlen will work.
+ buffer[size] = '\0';
+
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ LLDataPackerAsciiBuffer dp(buffer, size+1);
+ BOOL ok = gesture->deserialize(dp);
+
+ if (ok)
+ {
+ if (deactivate_similar)
+ {
+ gGestureManager.deactivateSimilarGestures(gesture, item_id);
+
+ // Display deactivation message if this was the last of the bunch.
+ if (gGestureManager.mLoadingCount == 0
+ && gGestureManager.mDeactivateSimilarNames.length() > 0)
+ {
+ // we're done with this set of deactivations
+ LLString::format_map_t args;
+ args["[NAMES]"] = gGestureManager.mDeactivateSimilarNames;
+ LLNotifyBox::showXml("DeactivatedGesturesTrigger", args);
+ }
+ }
+
+ // Everything has been successful. Add to the active list.
+ gGestureManager.mActive[item_id] = gesture;
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ if (inform_server)
+ {
+ // Inform the database of this change
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ActivateGestures");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addU32("Flags", 0x0);
+
+ msg->nextBlock("Data");
+ msg->addUUID("ItemID", item_id);
+ msg->addUUID("AssetID", asset_uuid);
+ msg->addU32("GestureFlags", 0x0);
+
+ gAgent.sendReliableMessage();
+ }
+
+ gGestureManager.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Unable to load gesture" << llendl;
+
+ gGestureManager.mActive.erase(item_id);
+
+ delete gesture;
+ gesture = NULL;
+ }
+
+ delete [] buffer;
+ buffer = NULL;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("GestureMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadGesture");
+ }
+
+ llwarns << "Problem loading gesture: " << status << llendl;
+
+ gGestureManager.mActive.erase(item_id);
+ }
+}
+
+
+void LLGestureManager::stopGesture(LLMultiGesture* gesture)
+{
+ if (!gesture) return;
+
+ // Stop any animations that this gesture is currently playing
+ std::set<LLUUID>::const_iterator set_it;
+ for (set_it = gesture->mRequestedAnimIDs.begin(); set_it != gesture->mRequestedAnimIDs.end(); ++set_it)
+ {
+ const LLUUID& anim_id = *set_it;
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
+ }
+ for (set_it = gesture->mPlayingAnimIDs.begin(); set_it != gesture->mPlayingAnimIDs.end(); ++set_it)
+ {
+ const LLUUID& anim_id = *set_it;
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_STOP);
+ }
+
+ std::vector<LLMultiGesture*>::iterator it;
+ it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
+ while (it != mPlaying.end())
+ {
+ mPlaying.erase(it);
+ it = std::find(mPlaying.begin(), mPlaying.end(), gesture);
+ }
+
+ gesture->reset();
+
+ if (gesture->mDoneCallback)
+ {
+ gesture->mDoneCallback(gesture, gesture->mCallbackData);
+
+ // callback might have deleted gesture, can't
+ // rely on this pointer any more
+ gesture = NULL;
+ }
+
+ notifyObservers();
+}
+
+
+void LLGestureManager::stopGesture(const LLUUID& item_id)
+{
+ item_map_t::iterator it = mActive.find(item_id);
+ if (it == mActive.end()) return;
+
+ LLMultiGesture* gesture = (*it).second;
+ if (!gesture) return;
+
+ stopGesture(gesture);
+}
+
+
+void LLGestureManager::addObserver(LLGestureManagerObserver* observer)
+{
+ mObservers.push_back(observer);
+}
+
+void LLGestureManager::removeObserver(LLGestureManagerObserver* observer)
+{
+ std::vector<LLGestureManagerObserver*>::iterator it;
+ it = std::find(mObservers.begin(), mObservers.end(), observer);
+ if (it != mObservers.end())
+ {
+ mObservers.erase(it);
+ }
+}
+
+// Call this method when it's time to update everyone on a new state.
+// Copy the list because an observer could respond by removing itself
+// from the list.
+void LLGestureManager::notifyObservers()
+{
+ lldebugs << "LLGestureManager::notifyObservers" << llendl;
+
+ std::vector<LLGestureManagerObserver*> observers = mObservers;
+
+ std::vector<LLGestureManagerObserver*>::iterator it;
+ for (it = observers.begin(); it != observers.end(); ++it)
+ {
+ LLGestureManagerObserver* observer = *it;
+ observer->changed();
+ }
+}
+
+BOOL LLGestureManager::matchPrefix(const std::string& in_str, std::string* out_str)
+{
+ S32 in_len = in_str.length();
+
+ item_map_t::iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ LLMultiGesture* gesture = (*it).second;
+ if (gesture)
+ {
+ const std::string& trigger = gesture->getTrigger();
+
+ if (in_len > (S32)trigger.length())
+ {
+ // too short, bail out
+ continue;
+ }
+
+ LLString trigger_trunc = trigger;
+ LLString::truncate(trigger_trunc, in_len);
+ if (!LLString::compareInsensitive(in_str.c_str(), trigger_trunc.c_str()))
+ {
+ *out_str = trigger;
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+void LLGestureManager::getItemIDs(std::vector<LLUUID>* ids)
+{
+ item_map_t::const_iterator it;
+ for (it = mActive.begin(); it != mActive.end(); ++it)
+ {
+ ids->push_back(it->first);
+ }
+}
diff --git a/indra/newview/llgesturemgr.h b/indra/newview/llgesturemgr.h
new file mode 100644
index 0000000000..b79dc0687f
--- /dev/null
+++ b/indra/newview/llgesturemgr.h
@@ -0,0 +1,138 @@
+/**
+ * @file llgesturemgr.h
+ * @brief Manager for playing gestures on the viewer
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGESTUREMGR_H
+#define LL_LLGESTUREMGR_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "llassetstorage.h" // LLAssetType
+#include "llviewerinventory.h"
+
+class LLMultiGesture;
+class LLGestureStep;
+class LLUUID;
+class LLVFS;
+
+class LLGestureManagerObserver
+{
+public:
+ virtual ~LLGestureManagerObserver() { };
+ virtual void changed() = 0;
+};
+
+class LLGestureManager
+{
+public:
+ LLGestureManager();
+ ~LLGestureManager();
+
+ void init();
+
+ // Call once per frame to manage gestures
+ void update();
+
+ // Loads a gesture out of inventory into the in-memory active form
+ // Note that the inventory item must exist, so we can look up the
+ // asset id.
+ void activateGesture(const LLUUID& item_id);
+
+ // Activate a list of gestures
+ void activateGestures(LLViewerInventoryItem::item_array_t& items);
+
+ // If you change a gesture, you need to build a new multigesture
+ // and call this method.
+ void replaceGesture(const LLUUID& item_id, LLMultiGesture* new_gesture, const LLUUID& asset_id);
+
+ // Load gesture into in-memory active form.
+ // Can be called even if the inventory item isn't loaded yet.
+ // inform_server TRUE will send message upstream to update database
+ // user_gesture_active table, which isn't necessary on login.
+ // deactivate_similar will cause other gestures with the same trigger phrase
+ // or keybinding to be deactivated.
+ void activateGestureWithAsset(const LLUUID& item_id, const LLUUID& asset_id, BOOL inform_server, BOOL deactivate_similar);
+
+ // Takes gesture out of active list and deletes it.
+ void deactivateGesture(const LLUUID& item_id);
+
+ // Deactivates all gestures that match either this trigger phrase,
+ // or this hot key.
+ void deactivateSimilarGestures(LLMultiGesture* gesture, const LLUUID& in_item_id);
+
+ BOOL isGestureActive(const LLUUID& item_id);
+
+ BOOL isGesturePlaying(const LLUUID& item_id);
+
+ // Force a gesture to be played, for example, if it is being
+ // previewed.
+ void playGesture(LLMultiGesture* gesture);
+ void playGesture(const LLUUID& item_id);
+
+ // Stop all requested or playing anims for this gesture
+ // Also remove from playing list
+ void stopGesture(LLMultiGesture* gesture);
+ void stopGesture(const LLUUID& item_id);
+
+ // Trigger the first gesture that matches this key.
+ // Returns TRUE if it finds a gesture bound to that key.
+ BOOL triggerGesture(KEY key, MASK mask);
+
+ // Trigger all gestures referenced as substrings in this string
+ BOOL triggerAndReviseString(const std::string &str, std::string *revised_string);
+
+ // Does some gesture have this key bound?
+ BOOL isKeyBound(KEY key, MASK mask);
+
+ S32 getPlayingCount() const;
+
+ void addObserver(LLGestureManagerObserver* observer);
+ void removeObserver(LLGestureManagerObserver* observer);
+ void notifyObservers();
+
+ BOOL matchPrefix(const std::string& in_str, std::string* out_str);
+
+ // Copy item ids into the vector
+ void getItemIDs(std::vector<LLUUID>* ids);
+
+protected:
+ // Handle the processing of a single gesture
+ void stepGesture(LLMultiGesture* gesture);
+
+ // Do a single step in a gesture
+ void runStep(LLMultiGesture* gesture, LLGestureStep* step);
+
+ // Used by loadGesture
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+public:
+ BOOL mValid;
+ std::vector<LLMultiGesture*> mPlaying;
+
+ // Maps inventory item_id to gesture
+ typedef std::map<LLUUID, LLMultiGesture*> item_map_t;
+
+ // Active gestures.
+ // NOTE: The gesture pointer CAN BE NULL. This means that
+ // there is a gesture with that item_id, but the asset data
+ // is still on its way down from the server.
+ item_map_t mActive;
+
+ S32 mLoadingCount;
+ std::string mDeactivateSimilarNames;
+
+ std::vector<LLGestureManagerObserver*> mObservers;
+};
+
+extern LLGestureManager gGestureManager;
+
+#endif
diff --git a/indra/newview/llglsandbox.cpp b/indra/newview/llglsandbox.cpp
new file mode 100644
index 0000000000..b51bdd057d
--- /dev/null
+++ b/indra/newview/llglsandbox.cpp
@@ -0,0 +1,1222 @@
+/**
+ * @file llglsandbox.cpp
+ * @brief GL functionality access
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Contains ALL methods which directly access GL functionality
+ * except for core rendering engine functionality.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercontrol.h"
+
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llparcel.h"
+#include "llui.h"
+
+#include "lldrawable.h"
+#include "lltextureentry.h"
+#include "llviewercamera.h"
+
+#include "llvoavatar.h"
+#include "llagent.h"
+#include "lltoolmgr.h"
+#include "llselectmgr.h"
+#include "llhudmanager.h"
+#include "llsphere.h"
+#include "llviewerobjectlist.h"
+#include "lltoolselectrect.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llcompass.h"
+#include "llsurface.h"
+#include "llwind.h"
+#include "llworld.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "pipeline.h"
+
+BOOL LLAgent::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if(object && object->isAttachment())
+ {
+ LLViewerObject* parent = object;
+ while(parent)
+ {
+ if (parent == mAvatarObject)
+ {
+ // looking at an attachment on ourselves, which we don't want to do
+ object = mAvatarObject;
+ position.clearVec();
+ }
+ parent = (LLViewerObject*)parent->getParent();
+ }
+ }
+ if(!mLookAt || mLookAt->isDead())
+ {
+ mLookAt = (LLHUDEffectLookAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_LOOKAT);
+ mLookAt->setSourceObject(mAvatarObject);
+ }
+
+ return mLookAt->setLookAt(target_type, object, position);
+}
+
+BOOL LLAgent::setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ // disallow pointing at attachments and avatars
+ if (object && (object->isAttachment() || object->isAvatar()))
+ {
+ return FALSE;
+ }
+
+ if(!mPointAt || mPointAt->isDead())
+ {
+ mPointAt = (LLHUDEffectPointAt *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINTAT);
+ mPointAt->setSourceObject(mAvatarObject);
+ }
+
+ return mPointAt->setPointAt(target_type, object, position);
+}
+
+ELookAtType LLAgent::getLookAtType()
+{
+ if (mLookAt)
+ {
+ return mLookAt->getLookAtType();
+ }
+
+ return LOOKAT_TARGET_NONE;
+}
+
+EPointAtType LLAgent::getPointAtType()
+{
+ if (mPointAt)
+ {
+ return mPointAt->getPointAtType();
+ }
+
+ return POINTAT_TARGET_NONE;
+}
+
+// Draw a representation of current autopilot target
+void LLAgent::renderAutoPilotTarget()
+{
+ if (mAutoPilot)
+ {
+ F32 height_meters;
+ LLVector3d target_global;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ // not textured
+ LLGLSNoTexture no_texture;
+
+ // lovely green
+ glColor4f(0.f, 1.f, 1.f, 1.f);
+
+ target_global = mAutoPilotTargetGlobal;
+
+ glTranslatef((F32)(target_global.mdV[VX]), (F32)(target_global.mdV[VY]), (F32)(target_global.mdV[VZ]));
+
+ /*
+ LLVector3 offset = target_global - mCamera.getOrigin();
+ F32 range = offset.magVec();
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = height_pixels / (F32) mCamera.getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * mCamera.getView();
+ height_meters = range * tan(apparent_angle);
+ }
+ else
+ {
+ // range == zero
+ height_meters = 1.0f;
+ }
+ */
+ height_meters = 1.f;
+
+ glScalef(height_meters, height_meters, height_meters);
+
+ gSphere.render(1500.f);
+
+ glPopMatrix();
+ }
+}
+
+extern BOOL gDebugSelect;
+
+// Returns true if you got at least one object
+void LLToolSelectRect::handleRectangleSelection(S32 x, S32 y, MASK mask)
+{
+ LLVector3 av_pos = gAgent.getPositionAgent();
+ F32 select_dist_squared = gSavedSettings.getF32("MaxSelectDistance");
+ select_dist_squared = select_dist_squared * select_dist_squared;
+
+ x = llround((F32)x * LLUI::sGLScaleFactor.mV[VX]);
+ y = llround((F32)y * LLUI::sGLScaleFactor.mV[VY]);
+
+ BOOL deselect = (mask == MASK_CONTROL);
+ S32 left = llmin(x, mDragStartX);
+ S32 right = llmax(x, mDragStartX);
+ S32 top = llmax(y, mDragStartY);
+ S32 bottom =llmin(y, mDragStartY);
+
+ F32 old_far_plane = gCamera->getFar();
+ F32 old_near_plane = gCamera->getNear();
+
+ S32 width = right - left + 1;
+ S32 height = top - bottom + 1;
+
+ BOOL grow_selection = FALSE;
+ BOOL shrink_selection = FALSE;
+
+ if (height > mDragLastHeight || width > mDragLastWidth)
+ {
+ grow_selection = TRUE;
+ }
+ if (height < mDragLastHeight || width < mDragLastWidth)
+ {
+ shrink_selection = TRUE;
+ }
+
+ if (!grow_selection && !shrink_selection)
+ {
+ // nothing to do
+ return;
+ }
+
+ mDragLastHeight = height;
+ mDragLastWidth = width;
+
+ S32 center_x = (left + right) / 2;
+ S32 center_y = (top + bottom) / 2;
+
+ // save drawing mode
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+
+ BOOL limit_select_distance = gSavedSettings.getBOOL("LimitSelectDistance");
+ if (limit_select_distance)
+ {
+ // ...select distance from control
+ LLVector3 relative_av_pos = av_pos;
+ relative_av_pos -= gCamera->getOrigin();
+
+ F32 new_far = relative_av_pos * gCamera->getAtAxis() + gSavedSettings.getF32("MaxSelectDistance");
+ F32 new_near = relative_av_pos * gCamera->getAtAxis() - gSavedSettings.getF32("MaxSelectDistance");
+
+ new_near = llmax(new_near, 0.1f);
+
+ gCamera->setFar(new_far);
+ gCamera->setNear(new_near);
+ }
+ gCamera->setPerspective(FOR_SELECTION,
+ center_x-width/2, center_y-height/2, width, height,
+ limit_select_distance);
+
+ if (shrink_selection)
+ {
+ for (LLViewerObject* vobjp = gSelectMgr->getFirstHighlightedObject();
+ vobjp;
+ vobjp = gSelectMgr->getNextHighlightedObject())
+ {
+ LLDrawable* drawable = vobjp->mDrawable;
+ if (!drawable || vobjp->getPCode() != LL_PCODE_VOLUME || vobjp->isAttachment())
+ {
+ continue;
+ }
+
+ S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+ switch (result)
+ {
+ case 0:
+ gSelectMgr->unhighlightObjectOnly(vobjp);
+ break;
+ case 1:
+ // check vertices
+ if (!gCamera->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
+ {
+ gSelectMgr->unhighlightObjectOnly(vobjp);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (grow_selection)
+ {
+ std::vector<LLDrawable*> potentials;
+
+ if (gPipeline.mObjectPartition)
+ {
+ gPipeline.mObjectPartition->cull(*gCamera, &potentials, TRUE);
+ }
+
+ for (std::vector<LLDrawable*>::iterator iter = potentials.begin();
+ iter != potentials.end(); iter++)
+ {
+ LLDrawable* drawable = *iter;
+ LLViewerObject* vobjp = drawable->getVObj();
+
+ if (!drawable || !vobjp ||
+ vobjp->getPCode() != LL_PCODE_VOLUME ||
+ vobjp->isAttachment() ||
+ (deselect && !vobjp->isSelected()))
+ {
+ continue;
+ }
+
+ if (limit_select_distance && dist_vec_squared(drawable->getWorldPosition(), av_pos) > select_dist_squared)
+ {
+ continue;
+ }
+
+ S32 result = gCamera->sphereInFrustum(drawable->getWorldPosition(), drawable->getRadius());
+ if (result)
+ {
+ switch (result)
+ {
+ case 1:
+ // check vertices
+ if (gCamera->areVertsVisible(vobjp, LLSelectMgr::sRectSelectInclusive))
+ {
+ gSelectMgr->highlightObjectOnly(vobjp);
+ }
+ break;
+ case 2:
+ gSelectMgr->highlightObjectOnly(vobjp);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // restore drawing mode
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ // restore camera
+ gCamera->setFar(old_far_plane);
+ gCamera->setNear(old_near_plane);
+ gViewerWindow->setup3DRender();
+}
+
+
+const F32 COMPASS_SIZE = 64;
+static const F32 COMPASS_RANGE = 0.33f;
+
+void LLCompass::draw()
+{
+// S32 left, top, right, bottom;
+
+ if (!getVisible()) return;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+
+ S32 width = 32;
+ S32 height = 32;
+
+ LLGLSUIDefault gls_ui;
+
+ glTranslatef( COMPASS_SIZE/2.f, COMPASS_SIZE/2.f, 0.f);
+
+ if (mBkgndTexture)
+ {
+ mBkgndTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(-width, height);
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(-width, -height);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, -height);
+
+ glEnd();
+ }
+
+ // rotate subsequent draws to agent rotation
+ F32 rotation = atan2( gAgent.getFrameAgent().getAtAxis().mV[VX], gAgent.getFrameAgent().getAtAxis().mV[VY] );
+ glRotatef( - rotation * RAD_TO_DEG, 0.f, 0.f, -1.f);
+
+ if (mTexture)
+ {
+ mTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+
+ glBegin(GL_QUADS);
+
+ glTexCoord2f(1.f, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(0.f, 1.f);
+ glVertex2i(-width, height);
+
+ glTexCoord2f(0.f, 0.f);
+ glVertex2i(-width, -height);
+
+ glTexCoord2f(1.f, 0.f);
+ glVertex2i(width, -height);
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+}
+
+
+
+void LLHorizontalCompass::draw()
+{
+ if (!getVisible()) return;
+
+ LLGLSUIDefault gls_ui;
+
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+ S32 half_width = width / 2;
+
+ if( mTexture )
+ {
+ const LLVector3& at_axis = gCamera->getAtAxis();
+ F32 center = atan2( at_axis.mV[VX], at_axis.mV[VY] );
+
+ center += F_PI;
+ center = llclamp( center, 0.0f, F_TWO_PI ); // probably not necessary...
+ center /= F_TWO_PI;
+ F32 left = center - COMPASS_RANGE;
+ F32 right = center + COMPASS_RANGE;
+
+ mTexture->bind();
+ glColor4f(1.0f, 1.0f, 1.0f, 1.0f );
+ glBegin( GL_QUADS );
+
+ glTexCoord2f(right, 1.f);
+ glVertex2i(width, height);
+
+ glTexCoord2f(left, 1.f);
+ glVertex2i(0, height);
+
+ glTexCoord2f(left, 0.f);
+ glVertex2i(0, 0);
+
+ glTexCoord2f(right, 0.f);
+ glVertex2i(width, 0);
+
+ glEnd();
+ }
+
+ // Draw the focus line
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( mFocusColor.mV );
+ gl_line_2d( half_width, 0, half_width, height );
+ }
+}
+
+
+const F32 WIND_ALTITUDE = 180.f;
+
+
+void LLWind::renderVectors()
+{
+ // Renders the wind as vectors (used for debug)
+ S32 i,j;
+ F32 x,y;
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ LLGLSNoTexture gls_no_texture;
+ glPushMatrix();
+ LLVector3 origin_agent;
+ origin_agent = gAgent.getPosAgentFromGlobal(mOriginGlobal);
+ glTranslatef(origin_agent.mV[VX], origin_agent.mV[VY], WIND_ALTITUDE);
+ for (j = 0; j < mSize; j++)
+ {
+ for (i = 0; i < mSize; i++)
+ {
+ x = mCloudVelX[i + j*mSize] * WIND_SCALE_HACK;
+ y = mCloudVelY[i + j*mSize] * WIND_SCALE_HACK;
+ glPushMatrix();
+ glTranslatef((F32)i * region_width_meters/mSize, (F32)j * region_width_meters/mSize, 0.0);
+ glColor3f(0,1,0);
+ glBegin(GL_POINTS);
+ glVertex3f(0,0,0);
+ glEnd();
+ glColor3f(1,0,0);
+ glBegin(GL_LINES);
+ glVertex3f(x * 0.1f, y * 0.1f ,0.f);
+ glVertex3f(x, y, 0.f);
+ glEnd();
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+}
+
+
+
+
+// Used by lltoolselectland
+void LLViewerParcelMgr::renderRect(const LLVector3d &west_south_bottom_global,
+ const LLVector3d &east_north_top_global )
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ LLVector3 west_south_bottom_agent = gAgent.getPosAgentFromGlobal(west_south_bottom_global);
+ F32 west = west_south_bottom_agent.mV[VX];
+ F32 south = west_south_bottom_agent.mV[VY];
+// F32 bottom = west_south_bottom_agent.mV[VZ] - 1.f;
+
+ LLVector3 east_north_top_agent = gAgent.getPosAgentFromGlobal(east_north_top_global);
+ F32 east = east_north_top_agent.mV[VX];
+ F32 north = east_north_top_agent.mV[VY];
+// F32 top = east_north_top_agent.mV[VZ] + 1.f;
+
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 FUDGE = 0.01f;
+
+ F32 sw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
+ F32 se_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
+ F32 ne_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
+ F32 nw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
+
+ F32 sw_top = sw_bottom + PARCEL_POST_HEIGHT;
+ F32 se_top = se_bottom + PARCEL_POST_HEIGHT;
+ F32 ne_top = ne_bottom + PARCEL_POST_HEIGHT;
+ F32 nw_top = nw_bottom + PARCEL_POST_HEIGHT;
+
+ LLUI::setLineWidth(2.f);
+ glColor4f(1.f, 1.f, 0.f, 1.f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_LINES);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+
+ glEnd();
+
+ glColor4f(1.f, 1.f, 0.f, 0.2f);
+ glBegin(GL_QUADS);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, north, ne_bottom);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, south, se_top);
+ glVertex3f(east, south, se_bottom);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, south, sw_bottom);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(west, north, nw_bottom);
+
+ glEnd();
+
+ LLUI::setLineWidth(1.f);
+}
+
+/*
+void LLViewerParcelMgr::renderParcel(LLParcel* parcel )
+{
+ S32 i;
+ S32 count = parcel->getBoxCount();
+ for (i = 0; i < count; i++)
+ {
+ const LLParcelBox& box = parcel->getBox(i);
+
+ F32 west = box.mMin.mV[VX];
+ F32 south = box.mMin.mV[VY];
+
+ F32 east = box.mMax.mV[VX];
+ F32 north = box.mMax.mV[VY];
+
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 FUDGE = 0.01f;
+
+ F32 sw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, south, 0.f ) );
+ F32 se_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, south, 0.f ) );
+ F32 ne_bottom = gWorldp->resolveLandHeightAgent( LLVector3( east-FUDGE, north-FUDGE, 0.f ) );
+ F32 nw_bottom = gWorldp->resolveLandHeightAgent( LLVector3( west, north-FUDGE, 0.f ) );
+
+ // little hack to make nearby lines not Z-fight
+ east -= 0.1f;
+ north -= 0.1f;
+
+ F32 sw_top = sw_bottom + POST_HEIGHT;
+ F32 se_top = se_bottom + POST_HEIGHT;
+ F32 ne_top = ne_bottom + POST_HEIGHT;
+ F32 nw_top = nw_bottom + POST_HEIGHT;
+
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ LLUI::setLineWidth(2.f);
+ glColor4f(0.f, 1.f, 1.f, 1.f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_LINES);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+
+ glEnd();
+
+ glColor4f(0.f, 1.f, 1.f, 0.2f);
+ glBegin(GL_QUADS);
+
+ glVertex3f(west, north, nw_bottom);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, north, ne_bottom);
+
+ glVertex3f(east, north, ne_bottom);
+ glVertex3f(east, north, ne_top);
+ glVertex3f(east, south, se_top);
+ glVertex3f(east, south, se_bottom);
+
+ glVertex3f(east, south, se_bottom);
+ glVertex3f(east, south, se_top);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, south, sw_bottom);
+
+ glVertex3f(west, south, sw_bottom);
+ glVertex3f(west, south, sw_top);
+ glVertex3f(west, north, nw_top);
+ glVertex3f(west, north, nw_bottom);
+
+ glEnd();
+
+ LLUI::setLineWidth(1.f);
+ }
+}
+*/
+
+
+// north = a wall going north/south. Need that info to set up texture
+// coordinates correctly.
+void LLViewerParcelMgr::renderOneSegment(F32 x1, F32 y1, F32 x2, F32 y2, F32 height, U8 direction, LLViewerRegion* regionp)
+{
+ // HACK: At edge of last region of world, we need to make sure the region
+ // resolves correctly so we can get a height value.
+ const F32 BORDER = REGION_WIDTH_METERS - 0.1f;
+
+ F32 clamped_x1 = x1;
+ F32 clamped_y1 = y1;
+ F32 clamped_x2 = x2;
+ F32 clamped_y2 = y2;
+
+ if (clamped_x1 > BORDER) clamped_x1 = BORDER;
+ if (clamped_y1 > BORDER) clamped_y1 = BORDER;
+ if (clamped_x2 > BORDER) clamped_x2 = BORDER;
+ if (clamped_y2 > BORDER) clamped_y2 = BORDER;
+
+ F32 z;
+ F32 z1;
+ F32 z2;
+
+ z1 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x1, clamped_y1, 0.f ) );
+ z2 = regionp->getLand().resolveHeightRegion( LLVector3( clamped_x2, clamped_y2, 0.f ) );
+
+ // Convert x1 and x2 from region-local to agent coords.
+ LLVector3 origin = regionp->getOriginAgent();
+ x1 += origin.mV[VX];
+ x2 += origin.mV[VX];
+ y1 += origin.mV[VY];
+ y2 += origin.mV[VY];
+
+ if (height < 1.f)
+ {
+ z = z1+height;
+ glVertex3f(x1, y1, z);
+
+ glVertex3f(x1, y1, z1);
+
+ glVertex3f(x2, y2, z2);
+
+ z = z2+height;
+ glVertex3f(x2, y2, z);
+ }
+ else
+ {
+ F32 tex_coord1;
+ F32 tex_coord2;
+
+ if (WEST_MASK == direction)
+ {
+ tex_coord1 = y1;
+ tex_coord2 = y2;
+ }
+ else if (SOUTH_MASK == direction)
+ {
+ tex_coord1 = x1;
+ tex_coord2 = x2;
+ }
+ else if (EAST_MASK == direction)
+ {
+ tex_coord1 = y2;
+ tex_coord2 = y1;
+ }
+ else /* (NORTH_MASK == direction) */
+ {
+ tex_coord1 = x2;
+ tex_coord2 = x1;
+ }
+
+
+ glTexCoord2f(tex_coord1*0.5f+0.5f, z1*0.5f);
+ glVertex3f(x1, y1, z1);
+
+ glTexCoord2f(tex_coord2*0.5f+0.5f, z2*0.5f);
+ glVertex3f(x2, y2, z2);
+
+ // top edge stairsteps
+ z = llmax(z2+height, z1+height);
+ glTexCoord2f(tex_coord2*0.5f+0.5f, z*0.5f);
+ glVertex3f(x2, y2, z);
+
+ glTexCoord2f(tex_coord1*0.5f+0.5f, z*0.5f);
+ glVertex3f(x1, y1, z);
+ }
+}
+
+
+void LLViewerParcelMgr::renderHighlightSegments(const U8* segments, LLViewerRegion* regionp)
+{
+ S32 x, y;
+ F32 x1, y1; // start point
+ F32 x2, y2; // end point
+
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ glColor4f(1.f, 1.f, 0.f, 0.2f);
+
+ // Cheat and give this the same pick-name as land
+ glBegin(GL_QUADS);
+
+ const S32 STRIDE = (mParcelsPerEdge+1);
+ for (y = 0; y < STRIDE; y++)
+ {
+ for (x = 0; x < STRIDE; x++)
+ {
+ U8 segment_mask = segments[x + y*STRIDE];
+
+ if (segment_mask & SOUTH_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1 + PARCEL_GRID_STEP_METERS;
+ y2 = y1;
+
+ renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, SOUTH_MASK, regionp);
+ }
+
+ if (segment_mask & WEST_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1;
+ y2 = y1 + PARCEL_GRID_STEP_METERS;
+
+ renderOneSegment(x1, y1, x2, y2, PARCEL_POST_HEIGHT, WEST_MASK, regionp);
+ }
+ }
+ }
+
+ glEnd();
+}
+
+
+void LLViewerParcelMgr::renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp)
+{
+
+ S32 x, y;
+ F32 x1, y1; // start point
+ F32 x2, y2; // end point
+ F32 alpha = 0;
+ F32 dist = 0;
+ F32 dx, dy;
+ F32 collision_height;
+
+ const S32 STRIDE = (mParcelsPerEdge+1);
+
+ LLVector3 pos = gAgent.getPositionAgent();
+
+ F32 pos_x = pos.mV[VX];
+ F32 pos_y = pos.mV[VY];
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLDisable cull(GL_CULL_FACE);
+
+ if (mCollisionBanned == BA_BANNED)
+ {
+ collision_height = BAN_HEIGHT;
+ }
+ else
+ {
+ collision_height = PARCEL_HEIGHT;
+ }
+
+
+ if (use_pass && (mCollisionBanned == BA_NOT_ON_LIST))
+ {
+ LLViewerImage::bindTexture(mPassImage);
+ }
+ else
+ {
+ LLViewerImage::bindTexture(mBlockedImage);
+ }
+
+ glBegin(GL_QUADS);
+
+ for (y = 0; y < STRIDE; y++)
+ {
+ for (x = 0; x < STRIDE; x++)
+ {
+ U8 segment_mask = segments[x + y*STRIDE];
+ U8 direction;
+ const F32 MAX_ALPHA = 0.95f;
+ const S32 DIST_OFFSET = 5;
+ const S32 MIN_DIST_SQ = DIST_OFFSET*DIST_OFFSET;
+ const S32 MAX_DIST_SQ = 169;
+
+ if (segment_mask & SOUTH_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1 + PARCEL_GRID_STEP_METERS;
+ y2 = y1;
+
+ if (gRenderForSelect)
+ {
+ LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL);
+ glColor4ubv(color.mV);
+ }
+ else
+ {
+ dy = (pos_y - y1) + DIST_OFFSET;
+
+ if (pos_x < x1)
+ dx = pos_x - x1;
+ else if (pos_x > x2)
+ dx = pos_x - x2;
+ else
+ dx = 0;
+
+ dist = dx*dx+dy*dy;
+
+ if (dist < MIN_DIST_SQ)
+ alpha = MAX_ALPHA;
+ else if (dist > MAX_DIST_SQ)
+ alpha = 0.0f;
+ else
+ alpha = 30/dist;
+
+ alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
+
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ }
+
+ if ((pos_y - y1) < 0) direction = SOUTH_MASK;
+ else direction = NORTH_MASK;
+
+ // avoid Z fighting
+ renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
+
+ }
+
+ if (segment_mask & WEST_MASK)
+ {
+ x1 = x * PARCEL_GRID_STEP_METERS;
+ y1 = y * PARCEL_GRID_STEP_METERS;
+
+ x2 = x1;
+ y2 = y1 + PARCEL_GRID_STEP_METERS;
+
+ if (gRenderForSelect)
+ {
+ LLColor4U color((U8)(GL_NAME_PARCEL_WALL >> 16), (U8)(GL_NAME_PARCEL_WALL >> 8), (U8)GL_NAME_PARCEL_WALL);
+ glColor4ubv(color.mV);
+ }
+ else
+ {
+ dx = (pos_x - x1) + DIST_OFFSET;
+
+ if (pos_y < y1)
+ dy = pos_y - y1;
+ else if (pos_y > y2)
+ dy = pos_y - y2;
+ else
+ dy = 0;
+
+ dist = dx*dx+dy*dy;
+
+ if (dist < MIN_DIST_SQ)
+ alpha = MAX_ALPHA;
+ else if (dist > MAX_DIST_SQ)
+ alpha = 0.0f;
+ else
+ alpha = 30/dist;
+
+ alpha = llclamp(alpha, 0.0f, MAX_ALPHA);
+
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ }
+
+ if ((pos_x - x1) > 0) direction = WEST_MASK;
+ else direction = EAST_MASK;
+
+ // avoid Z fighting
+ renderOneSegment(x1+0.1f, y1+0.1f, x2+0.1f, y2+0.1f, collision_height, direction, regionp);
+
+ }
+ }
+ }
+
+ glEnd();
+}
+
+
+const S32 CLIENT_RECT_VPAD = 4;
+void LLPreviewTexture::draw()
+{
+ if( getVisible() )
+ {
+ updateAspectRatio();
+
+ LLPreview::draw();
+
+ if (!mMinimized)
+ {
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_notex;
+
+ const LLRect& border = mClientRect;
+ LLRect interior = mClientRect;
+ interior.stretch( -PREVIEW_BORDER_WIDTH );
+
+ // ...border
+ gl_rect_2d( border, LLColor4(0.f, 0.f, 0.f, 1.f));
+ gl_rect_2d_checkerboard( interior );
+
+ if ( mImage.notNull() )
+ {
+ LLGLSTexture gls_no_texture;
+ // Draw the texture
+ glColor3f( 1.f, 1.f, 1.f );
+ gl_draw_scaled_image(interior.mLeft,
+ interior.mBottom,
+ interior.getWidth(),
+ interior.getHeight(),
+ mImage);
+
+ // Pump the texture priority
+ F32 pixel_area = mLoadingFullImage ? (F32)MAX_IMAGE_AREA : (F32)(interior.getWidth() * interior.getHeight() );
+ mImage->addTextureStats( pixel_area );
+
+ // Don't bother decoding more than we can display, unless
+ // we're loading the full image.
+ if (!mLoadingFullImage)
+ {
+ S32 int_width = interior.getWidth();
+ S32 int_height = interior.getHeight();
+ mImage->setKnownDrawSize(int_width, int_height);
+ }
+ else
+ {
+ // Don't use this feature
+ mImage->setKnownDrawSize(0, 0);
+ }
+
+ if( mLoadingFullImage )
+ {
+ LLFontGL::sSansSerif->renderUTF8("Receiving:", 0,
+ interior.mLeft + 4,
+ interior.mBottom + 4,
+ LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::DROP_SHADOW);
+
+ F32 data_progress = 0.0f;
+ F32 decode_progress = mImage->getDecodeProgress(&data_progress);
+
+ // Draw the progress bar.
+ const S32 BAR_HEIGHT = 12;
+ const S32 BAR_LEFT_PAD = 80;
+ S32 left = interior.mLeft + 4 + BAR_LEFT_PAD;
+ S32 bar_width = mRect.getWidth() - left - RESIZE_HANDLE_WIDTH - 2;
+ S32 top = interior.mBottom + 4 + BAR_HEIGHT;
+ S32 right = left + bar_width;
+ S32 bottom = top - BAR_HEIGHT;
+
+ LLColor4 background_color(0.f, 0.f, 0.f, 0.75f);
+ LLColor4 decoded_color(0.f, 1.f, 0.f, 1.0f);
+ LLColor4 downloaded_color(0.f, 0.5f, 0.f, 1.0f);
+
+ gl_rect_2d(left, top, right, bottom, background_color);
+
+ if (data_progress > 0.0f)
+ {
+ // Decoded bytes
+ right = left + llfloor(decode_progress * (F32)bar_width);
+
+ if (left < right)
+ {
+ gl_rect_2d(left, top, right, bottom, decoded_color);
+ }
+
+ // Downloaded bytes
+ left = right;
+ right = left + llfloor((data_progress - decode_progress) * (F32)bar_width);
+
+ if (left < right)
+ {
+ gl_rect_2d(left, top, right, bottom, downloaded_color);
+ }
+ }
+ }
+ else
+ if( !mSavedFileTimer.hasExpired() )
+ {
+ LLFontGL::sSansSerif->renderUTF8("File Saved", 0,
+ interior.mLeft + 4,
+ interior.mBottom + 4,
+ LLColor4::white, LLFontGL::LEFT, LLFontGL::BOTTOM,
+ LLFontGL::DROP_SHADOW);
+ }
+ }
+ }
+ }
+}
+
+
+void draw_line_cube(F32 width, const LLVector3& center)
+{
+ width = 0.5f * width;
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] + width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] - width ,center.mV[VY] - width,center.mV[VZ] - width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] + width);
+ glVertex3f(center.mV[VX] + width ,center.mV[VY] - width,center.mV[VZ] - width);
+}
+
+
+void LLViewerObjectList::renderObjectBeacons()
+{
+ S32 i;
+ //const LLFontGL *font = gResMgr->getRes(LLFONT_SANSSERIF);
+
+ LLGLSUIDefault gls_ui;
+
+ S32 last_line_width = -1;
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ glBegin(GL_LINES);
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ const LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+ LLColor4 color = debug_beacon.mColor;
+ color.mV[3] *= 0.25f;
+ S32 line_width = debug_beacon.mLineWidth;
+ if (line_width != last_line_width)
+ {
+ glEnd();
+ glLineWidth( (F32)line_width );
+ last_line_width = line_width;
+ glBegin(GL_LINES);
+ }
+
+ const LLVector3 &thisline = debug_beacon.mPositionAgent;
+ glColor4fv(color.mV);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] - 50.f);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] + 50.f);
+ glVertex3f(thisline.mV[VX] - 2.f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX] + 2.f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] - 2.f,thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] + 2.f,thisline.mV[VZ]);
+
+ draw_line_cube(0.10f, thisline);
+ }
+ glEnd();
+ }
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+
+ glBegin(GL_LINES);
+ last_line_width = -1;
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ const LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+
+ S32 line_width = debug_beacon.mLineWidth;
+ if (line_width != last_line_width)
+ {
+ glEnd();
+ glLineWidth( (F32)line_width );
+ last_line_width = line_width;
+ glBegin(GL_LINES);
+ }
+
+ const LLVector3 &thisline = debug_beacon.mPositionAgent;
+ glColor4fv(debug_beacon.mColor.mV);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] - 0.5f);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY],thisline.mV[VZ] + 0.5f);
+ glVertex3f(thisline.mV[VX] - 0.5f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX] + 0.5f,thisline.mV[VY],thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] - 0.5f,thisline.mV[VZ]);
+ glVertex3f(thisline.mV[VX],thisline.mV[VY] + 0.5f,thisline.mV[VZ]);
+
+ draw_line_cube(0.10f, thisline);
+ }
+ glEnd();
+
+ glLineWidth(1.f);
+
+ for (i = 0; i < mDebugBeacons.count(); i++)
+ {
+ LLDebugBeacon &debug_beacon = mDebugBeacons[i];
+ if (debug_beacon.mString == "")
+ {
+ continue;
+ }
+ LLHUDText *hud_textp = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+
+ hud_textp->setZCompare(FALSE);
+ LLColor4 color;
+ color = debug_beacon.mTextColor;
+ color.mV[3] *= 1.f;
+
+ hud_textp->setString(utf8str_to_wstring(debug_beacon.mString.c_str()));
+ hud_textp->setColor(color);
+ hud_textp->setPositionAgent(debug_beacon.mPositionAgent);
+ debug_beacon.mHUDObject = hud_textp;
+ }
+ }
+}
+
+
+void pre_show_depth_buffer()
+{
+ glClear(GL_STENCIL_BUFFER_BIT);
+ glEnable(GL_STENCIL_TEST);
+ glStencilFunc(GL_ALWAYS,0,0);
+ glStencilOp(GL_INCR,GL_INCR,GL_INCR);
+}
+
+void post_show_depth_buffer()
+{
+ int xsize =500, ysize =500;
+ U8 *buf = new U8[xsize*ysize];
+
+ glReadPixels(0,0,xsize,ysize,GL_STENCIL_INDEX,GL_UNSIGNED_BYTE, buf);
+
+ int total = 0;
+ int i;
+ for (i=0;i<xsize*ysize;i++)
+ {
+ total += buf[i];
+ buf[i] <<= 3;
+ }
+
+ float DC = (float)total/(float)(ysize*xsize);
+ int DCline = llfloor((xsize-20) * DC / 10.0f);
+ int stride = xsize / 10;
+
+ int y = 2;
+
+ for (i=0;i<DCline;i++)
+ {
+ if (i % stride == 0) i+=2;
+ if (i > xsize) y=6;
+ buf[ysize*(y+0)+i]=255;
+ buf[ysize*(y+1)+i]=255;
+ buf[ysize*(y+2)+i]=255;
+ }
+ glDrawPixels(xsize,ysize,GL_RED,GL_UNSIGNED_BYTE,buf);
+
+ delete buf;
+}
diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp
new file mode 100644
index 0000000000..106369ac01
--- /dev/null
+++ b/indra/newview/llgroupmgr.cpp
@@ -0,0 +1,1816 @@
+/**
+ * @file llgroupmgr.cpp
+ * @brief LLGroupMgr class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Manager for aggregating all client knowledge for specific groups
+ * Keeps a cache of group information.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llgroupmgr.h"
+
+#include <vector>
+#include <algorithm>
+
+#include "llagent.h"
+#include "llui.h"
+#include "message.h"
+#include "roles_constants.h"
+#include "lltransactiontypes.h"
+#include "llstatusbar.h"
+#include "viewer.h"
+#include "lleconomy.h"
+#include "llviewerwindow.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroupinfo.h"
+
+LLGroupMgr sGroupMgr; // use local instance so that it gets cleaned up on application exit
+LLGroupMgr* gGroupMgr = &sGroupMgr;
+
+const U32 MAX_CACHED_GROUPS = 10;
+
+//
+// LLRoleActionSet
+//
+LLRoleActionSet::LLRoleActionSet()
+: mActionSetData(NULL)
+{ }
+
+LLRoleActionSet::~LLRoleActionSet()
+{
+ delete mActionSetData;
+ std::for_each(mActions.begin(), mActions.end(), DeletePointer());
+}
+
+//
+// LLGroupMemberData
+//
+
+LLGroupMemberData::LLGroupMemberData(const LLUUID& id,
+ S32 contribution,
+ U64 agent_powers,
+ const std::string& title,
+ const std::string& online_status,
+ BOOL is_owner) :
+ mID(id),
+ mContribution(contribution),
+ mAgentPowers(agent_powers),
+ mTitle(title),
+ mOnlineStatus(online_status),
+ mIsOwner(is_owner)
+{
+}
+
+LLGroupMemberData::~LLGroupMemberData()
+{
+}
+
+void LLGroupMemberData::addRole(const LLUUID& role, LLGroupRoleData* rd)
+{
+ mRoles[role] = rd;
+}
+
+bool LLGroupMemberData::removeRole(const LLUUID& role)
+{
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = mRoles.find(role);
+
+ if (it != mRoles.end())
+ {
+ mRoles.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+//
+// LLGroupRoleData
+//
+
+LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
+ const std::string& role_name,
+ const std::string& role_title,
+ const std::string& role_desc,
+ const U64 role_powers,
+ const S32 member_count) :
+ mRoleID(role_id),
+ mMemberCount(member_count),
+ mMembersNeedsSort(FALSE)
+{
+ mRoleData.mRoleName = role_name;
+ mRoleData.mRoleTitle = role_title;
+ mRoleData.mRoleDescription = role_desc;
+ mRoleData.mRolePowers = role_powers;
+ mRoleData.mChangeType = RC_UPDATE_NONE;
+}
+
+LLGroupRoleData::LLGroupRoleData(const LLUUID& role_id,
+ LLRoleData role_data,
+ const S32 member_count) :
+ mRoleID(role_id),
+ mRoleData(role_data),
+ mMemberCount(member_count),
+ mMembersNeedsSort(FALSE)
+{
+
+}
+
+LLGroupRoleData::~LLGroupRoleData()
+{
+}
+
+S32 LLGroupRoleData::getMembersInRole(std::vector<LLUUID> members,
+ BOOL needs_sort)
+{
+ if (mRoleID.isNull())
+ {
+ // This is the everyone role, just return the size of members,
+ // because everyone is in the everyone role.
+ return members.size();
+ }
+
+ // Sort the members list, if needed.
+ if (mMembersNeedsSort)
+ {
+ std::sort(mMemberIDs.begin(), mMemberIDs.end());
+ mMembersNeedsSort = FALSE;
+ }
+ if (needs_sort)
+ {
+ // Sort the members parameter.
+ std::sort(members.begin(), members.end());
+ }
+
+ // Return the number of members in the intersection.
+ S32 max_size = llmin( members.size(), mMemberIDs.size() );
+ std::vector<LLUUID> in_role( max_size );
+ std::vector<LLUUID>::iterator in_role_end;
+ in_role_end = std::set_intersection(mMemberIDs.begin(), mMemberIDs.end(),
+ members.begin(), members.end(),
+ in_role.begin());
+ return in_role_end - in_role.begin();
+}
+
+void LLGroupRoleData::addMember(const LLUUID& member)
+{
+ mMembersNeedsSort = TRUE;
+ mMemberIDs.push_back(member);
+}
+
+bool LLGroupRoleData::removeMember(const LLUUID& member)
+{
+ std::vector<LLUUID>::iterator it = std::find(mMemberIDs.begin(),mMemberIDs.end(),member);
+
+ if (it != mMemberIDs.end())
+ {
+ mMembersNeedsSort = TRUE;
+ mMemberIDs.erase(it);
+ return true;
+ }
+
+ return false;
+}
+
+void LLGroupRoleData::clearMembers()
+{
+ mMembersNeedsSort = FALSE;
+ mMemberIDs.clear();
+}
+
+
+//
+// LLGroupMgrGroupData
+//
+
+LLGroupMgrGroupData::LLGroupMgrGroupData(const LLUUID& id) :
+ mID(id),
+ mShowInList(TRUE),
+ mOpenEnrollment(FALSE),
+ mMembershipFee(0),
+ mAllowPublish(FALSE),
+ mMaturePublish(FALSE),
+ mChanged(FALSE),
+ mMemberCount(0),
+ mRoleCount(0),
+ mReceivedRoleMemberPairs(0),
+ mMemberDataComplete(FALSE),
+ mRoleDataComplete(FALSE),
+ mRoleMemberDataComplete(FALSE),
+ mGroupPropertiesDataComplete(FALSE),
+ mPendingRoleMemberRequest(FALSE)
+{
+}
+
+BOOL LLGroupMgrGroupData::getRoleData(const LLUUID& role_id, LLRoleData& role_data)
+{
+ std::map<LLUUID,LLRoleData>::const_iterator it;
+
+ // Do we have changes for it?
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end())
+ {
+ if ((*it).second.mChangeType == RC_DELETE) return FALSE;
+
+ role_data = (*it).second;
+ return TRUE;
+ }
+
+ // Ok, no changes, hasn't been deleted, isn't a new role, just find the role.
+ role_list::const_iterator rit = mRoles.find(role_id);
+ if (rit != mRoles.end())
+ {
+ role_data = (*rit).second->getRoleData();
+ return TRUE;
+ }
+
+ // This role must not exist.
+ return FALSE;
+}
+
+
+void LLGroupMgrGroupData::setRoleData(const LLUUID& role_id, LLRoleData role_data)
+{
+ // If this is a newly created group, we need to change the data in the created list.
+ std::map<LLUUID,LLRoleData>::iterator it;
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end())
+ {
+ if ((*it).second.mChangeType == RC_CREATE)
+ {
+ role_data.mChangeType = RC_CREATE;
+ mRoleChanges[role_id] = role_data;
+ return;
+ }
+ else if ((*it).second.mChangeType == RC_DELETE)
+ {
+ // Don't do anything for a role being deleted.
+ return;
+ }
+ }
+
+ // Not a new role, so put it in the changes list.
+ LLRoleData old_role_data;
+ role_iter rit = mRoles.find(role_id);
+ if (rit != mRoles.end())
+ {
+ bool data_change = ( ((*rit).second->mRoleData.mRoleDescription != role_data.mRoleDescription)
+ || ((*rit).second->mRoleData.mRoleName != role_data.mRoleName)
+ || ((*rit).second->mRoleData.mRoleTitle != role_data.mRoleTitle) );
+ bool powers_change = ((*rit).second->mRoleData.mRolePowers != role_data.mRolePowers);
+
+ if (!data_change && !powers_change)
+ {
+ // We are back to the original state, the changes have been 'undone' so take out the change.
+ mRoleChanges.erase(role_id);
+ return;
+ }
+
+ if (data_change && powers_change)
+ {
+ role_data.mChangeType = RC_UPDATE_ALL;
+ }
+ else if (data_change)
+ {
+ role_data.mChangeType = RC_UPDATE_DATA;
+ }
+ else
+ {
+ role_data.mChangeType = RC_UPDATE_POWERS;
+ }
+
+ mRoleChanges[role_id] = role_data;
+ }
+ else
+ {
+ llwarns << "Change being made to non-existant role " << role_id << llendl;
+ }
+}
+
+BOOL LLGroupMgrGroupData::pendingRoleChanges()
+{
+ return (!mRoleChanges.empty());
+}
+
+// This is a no-op if the role has already been created.
+void LLGroupMgrGroupData::createRole(const LLUUID& role_id, LLRoleData role_data)
+{
+ if (mRoleChanges.find(role_id) != mRoleChanges.end())
+ {
+ llwarns << "create role for existing role! " << role_id << llendl;
+ }
+ else
+ {
+ role_data.mChangeType = RC_CREATE;
+ mRoleChanges[role_id] = role_data;
+ }
+}
+
+void LLGroupMgrGroupData::deleteRole(const LLUUID& role_id)
+{
+ std::map<LLUUID,LLRoleData>::iterator it;
+
+ // If this was a new role, just discard it.
+ it = mRoleChanges.find(role_id);
+ if (it != mRoleChanges.end()
+ && (*it).second.mChangeType == RC_CREATE)
+ {
+ mRoleChanges.erase(it);
+ return;
+ }
+
+ LLRoleData rd;
+ rd.mChangeType = RC_DELETE;
+ mRoleChanges[role_id] = rd;
+}
+
+void LLGroupMgrGroupData::addRolePower(const LLUUID &role_id, U64 power)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ rd.mRolePowers |= power;
+ setRoleData(role_id,rd);
+ }
+ else
+ {
+ llwarns << "addRolePower: no role data found for " << role_id << llendl;
+ }
+}
+
+void LLGroupMgrGroupData::removeRolePower(const LLUUID &role_id, U64 power)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ rd.mRolePowers &= ~power;
+ setRoleData(role_id,rd);
+ }
+ else
+ {
+ llwarns << "removeRolePower: no role data found for " << role_id << llendl;
+ }
+}
+
+U64 LLGroupMgrGroupData::getRolePowers(const LLUUID& role_id)
+{
+ LLRoleData rd;
+ if (getRoleData(role_id,rd))
+ {
+ return rd.mRolePowers;
+ }
+ else
+ {
+ llwarns << "getRolePowers: no role data found for " << role_id << llendl;
+ return GP_NO_POWERS;
+ }
+}
+
+void LLGroupMgrGroupData::removeData()
+{
+ // Remove member data first, because removeRoleData will walk the member list
+ removeMemberData();
+ removeRoleData();
+}
+
+void LLGroupMgrGroupData::removeMemberData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ delete mi->second;
+ }
+ mMembers.clear();
+ mMemberDataComplete = FALSE;
+}
+
+void LLGroupMgrGroupData::removeRoleData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->clearRoles();
+ }
+ }
+
+ for (role_iter ri = mRoles.begin(); ri != mRoles.end(); ++ri)
+ {
+ LLGroupRoleData* data = ri->second;
+ delete data;
+ }
+ mRoles.clear();
+ mReceivedRoleMemberPairs = 0;
+ mRoleDataComplete = FALSE;
+ mRoleMemberDataComplete = FALSE;
+}
+
+void LLGroupMgrGroupData::removeRoleMemberData()
+{
+ for (member_iter mi = mMembers.begin(); mi != mMembers.end(); ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->clearRoles();
+ }
+ }
+
+ for (role_iter ri = mRoles.begin(); ri != mRoles.end(); ++ri)
+ {
+ LLGroupRoleData* data = ri->second;
+ if (data)
+ {
+ data->clearMembers();
+ }
+ }
+
+ mReceivedRoleMemberPairs = 0;
+ mRoleMemberDataComplete = FALSE;
+}
+
+LLGroupMgrGroupData::~LLGroupMgrGroupData()
+{
+ removeData();
+}
+
+bool LLGroupMgrGroupData::changeRoleMember(const LLUUID& role_id,
+ const LLUUID& member_id,
+ LLRoleMemberChangeType rmc)
+{
+ role_iter ri = mRoles.find(role_id);
+ member_iter mi = mMembers.find(member_id);
+
+ if (ri == mRoles.end()
+ || mi == mMembers.end() )
+ {
+ if (ri == mRoles.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find role " << role_id << llendl;
+ if (mi == mMembers.end()) llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't find member " << member_id << llendl;
+ return false;
+ }
+
+ LLGroupRoleData* grd = ri->second;
+ LLGroupMemberData* gmd = mi->second;
+
+ if (!grd || !gmd)
+ {
+ llwarns << "LLGroupMgrGroupData::changeRoleMember couldn't get member or role data." << llendl;
+ return false;
+ }
+
+ if (RMC_ADD == rmc)
+ {
+ llinfos << " adding member to role." << llendl;
+ grd->addMember(member_id);
+ gmd->addRole(role_id,grd);
+
+ //TODO move this into addrole function
+ //see if they added someone to the owner role and update isOwner
+ gmd->mIsOwner = (role_id == mOwnerRole) ? TRUE : gmd->mIsOwner;
+ }
+ else if (RMC_REMOVE == rmc)
+ {
+ llinfos << " removing member from role." << llendl;
+ grd->removeMember(member_id);
+ gmd->removeRole(role_id);
+
+ //see if they removed someone from the owner role and update isOwner
+ gmd->mIsOwner = (role_id == mOwnerRole) ? FALSE : gmd->mIsOwner;
+ }
+
+ lluuid_pair role_member;
+ role_member.first = role_id;
+ role_member.second = member_id;
+
+ change_map::iterator it = mRoleMemberChanges.find(role_member);
+ if (it != mRoleMemberChanges.end())
+ {
+ // There was already a role change for this role_member
+ if (it->second.mChange == rmc)
+ {
+ // Already recorded this change? Weird.
+ llinfos << "Received duplicate change for "
+ << " role: " << role_id << " member " << member_id
+ << " change " << (rmc == RMC_ADD ? "ADD" : "REMOVE") << llendl;
+ }
+ else
+ {
+ // The only two operations (add and remove) currently cancel each other out
+ // If that changes this will need more logic
+ if (rmc == RMC_NONE)
+ {
+ llwarns << "changeRoleMember: existing entry with 'RMC_NONE' change! This shouldn't happen." << llendl;
+ LLRoleMemberChange rc(role_id,member_id,rmc);
+ mRoleMemberChanges[role_member] = rc;
+ }
+ else
+ {
+ mRoleMemberChanges.erase(it);
+ }
+ }
+ }
+ else
+ {
+ LLRoleMemberChange rc(role_id,member_id,rmc);
+ mRoleMemberChanges[role_member] = rc;
+ }
+
+ recalcAgentPowers(member_id);
+
+ mChanged = TRUE;
+ return true;
+}
+
+void LLGroupMgrGroupData::recalcAllAgentPowers()
+{
+ LLGroupMemberData* gmd;
+
+ member_iter mit = mMembers.begin();
+ member_iter mend = mMembers.end();
+ for( ; mit != mend; ++mit)
+ {
+ gmd = mit->second;
+ if (!gmd) continue;
+
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = gmd->mRoles.begin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end = gmd->mRoles.end();
+
+ gmd->mAgentPowers = 0;
+ for ( ; it != end; ++it)
+ {
+ LLGroupRoleData* grd = (*it).second;
+ if (!grd) continue;
+
+ gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
+ }
+ }
+}
+
+void LLGroupMgrGroupData::recalcAgentPowers(const LLUUID& agent_id)
+{
+ member_iter mi = mMembers.find(agent_id);
+ if (mi == mMembers.end()) return;
+
+ LLGroupMemberData* gmd = mi->second;
+
+ if (!gmd) return;
+
+ std::map<LLUUID,LLGroupRoleData*>::iterator it = gmd->mRoles.begin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end = gmd->mRoles.end();
+
+ gmd->mAgentPowers = 0;
+ for ( ; it != end; ++it)
+ {
+ LLGroupRoleData* grd = (*it).second;
+ if (!grd) continue;
+
+ gmd->mAgentPowers |= grd->mRoleData.mRolePowers;
+ }
+}
+
+bool packRoleUpdateMessageBlock(LLMessageSystem* msg,
+ const LLUUID& group_id,
+ const LLUUID& role_id,
+ const LLRoleData& role_data,
+ bool start_message)
+{
+ if (start_message)
+ {
+ msg->newMessage("GroupRoleUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("RoleData");
+ msg->addUUID("RoleID",role_id);
+ msg->addString("Name", role_data.mRoleName);
+ msg->addString("Description", role_data.mRoleDescription);
+ msg->addString("Title", role_data.mRoleTitle);
+ msg->addU64("Powers", role_data.mRolePowers);
+ msg->addU8("UpdateType", (U8)role_data.mChangeType);
+
+ if (msg->isSendFullFast())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+
+ return start_message;
+}
+
+void LLGroupMgrGroupData::sendRoleChanges()
+{
+ // Commit changes locally
+ std::map<LLUUID,LLRoleData>::iterator it;
+ std::map<LLUUID,LLRoleData>::iterator end;
+ LLGroupRoleData* grd;
+ role_iter role_it;
+ LLMessageSystem* msg = gMessageSystem;
+ bool start_message = true;
+
+ bool need_role_cleanup = false;
+ bool need_role_data = false;
+ bool need_power_recalc = false;
+
+ // Apply all changes
+ it = mRoleChanges.begin();
+ end = mRoleChanges.end();
+ for ( ; it != end; ++it)
+ {
+ const LLUUID& role_id = (*it).first;
+ const LLRoleData& role_data = (*it).second;
+
+ // Commit to local data set
+ role_it = mRoles.find((*it).first);
+ LLGroupRoleData* group_role_data = (*role_it).second;
+ if ( (mRoles.end() == role_it
+ && RC_CREATE != role_data.mChangeType)
+ || (mRoles.end() != role_it
+ && RC_CREATE == role_data.mChangeType))
+ {
+ continue;
+ }
+
+ switch (role_data.mChangeType)
+ {
+ case RC_CREATE:
+ {
+ grd = new LLGroupRoleData(role_id, role_data, 0);
+ mRoles[role_id] = grd;
+ need_role_data = true;
+ break;
+ }
+ case RC_DELETE:
+ {
+ delete group_role_data;
+ mRoles.erase(role_it);
+ need_role_cleanup = true;
+ need_power_recalc = true;
+ break;
+ }
+ case RC_UPDATE_ALL:
+ case RC_UPDATE_POWERS:
+ need_power_recalc = true;
+ case RC_UPDATE_DATA:
+ default:
+ {
+ group_role_data->setRoleData(role_data);
+ break;
+ }
+ }
+
+ // Update dataserver
+ start_message = packRoleUpdateMessageBlock(msg,getID(),role_id,role_data,start_message);
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ // If we delete a role then all the role-member pairs are invalid!
+ if (need_role_cleanup)
+ {
+ removeRoleMemberData();
+ }
+
+ // If we create a new role, then we need to re-fetch all the role data.
+ if (need_role_data)
+ {
+ gGroupMgr->sendGroupRoleDataRequest(getID());
+ }
+
+ // Clean up change lists
+ mRoleChanges.clear();
+
+ // Recalculate all the agent powers because role powers have now changed.
+ if (need_power_recalc)
+ {
+ recalcAllAgentPowers();
+ }
+}
+
+void LLGroupMgrGroupData::cancelRoleChanges()
+{
+ // Clear out all changes!
+ mRoleChanges.clear();
+}
+//
+// LLGroupMgr
+//
+
+LLGroupMgr::LLGroupMgr()
+{
+}
+
+LLGroupMgr::~LLGroupMgr()
+{
+ clearGroups();
+}
+
+void LLGroupMgr::clearGroups()
+{
+ std::for_each(mRoleActionSets.begin(), mRoleActionSets.end(), DeletePointer());
+ mRoleActionSets.clear();
+ std::for_each(mGroups.begin(), mGroups.end(), DeletePairedPointer());
+ mGroups.clear();
+ mObservers.clear();
+}
+
+void LLGroupMgr::clearGroupData(const LLUUID& group_id)
+{
+ std::map<LLUUID, LLGroupMgrGroupData*>::iterator iter = mGroups.find(group_id);
+ if (iter != mGroups.end())
+ {
+ delete (*iter).second;
+ mGroups.erase(iter);
+ }
+}
+
+void LLGroupMgr::addObserver(LLGroupMgrObserver* observer)
+{
+ mObservers.insert(std::pair<LLUUID, LLGroupMgrObserver*>(observer->getID(), observer));
+}
+
+void LLGroupMgr::removeObserver(LLGroupMgrObserver* observer)
+{
+ if (!observer)
+ {
+ return;
+ }
+ observer_iter it;
+ it = mObservers.find(observer->getID());
+ while (it != mObservers.end())
+ {
+ if (it->second == observer)
+ {
+ mObservers.erase(it);
+ break;
+ }
+ ++it;
+ }
+}
+
+LLGroupMgrGroupData* LLGroupMgr::getGroupData(const LLUUID& id)
+{
+ group_iter gi = mGroups.find(id);
+
+ if (gi != mGroups.end())
+ {
+ return gi->second;
+ }
+ return NULL;
+}
+
+// static
+void LLGroupMgr::processGroupMembersReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupMembersReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->createGroupData(group_id);
+ if (group_datap->mMemberRequestID != request_id)
+ {
+ llwarns << "processGroupMembersReply: Received incorrect (stale?) request id" << llendl;
+ return;
+ }
+
+ msg->getS32(_PREHASH_GroupData, "MemberCount", group_datap->mMemberCount );
+
+ if (group_datap->mMemberCount > 0)
+ {
+ S32 contribution = 0;
+ char online_status[DB_DATETIME_BUF_SIZE];
+ char title[DB_GROUP_TITLE_BUF_SIZE];
+ U64 agent_powers = 0;
+ BOOL is_owner = FALSE;
+
+ S32 num_members = msg->getNumberOfBlocksFast(_PREHASH_MemberData);
+ for (S32 i = 0; i < num_members; i++)
+ {
+ LLUUID member_id;
+
+ msg->getUUIDFast(_PREHASH_MemberData, _PREHASH_AgentID, member_id, i );
+ msg->getS32(_PREHASH_MemberData, _PREHASH_Contribution, contribution, i);
+ msg->getU64(_PREHASH_MemberData, "AgentPowers", agent_powers, i);
+ msg->getStringFast(_PREHASH_MemberData, _PREHASH_OnlineStatus, DB_DATETIME_BUF_SIZE, online_status, i);
+ msg->getString(_PREHASH_MemberData, "Title", DB_GROUP_TITLE_BUF_SIZE, title, i);
+ msg->getBOOL(_PREHASH_MemberData,"IsOwner",is_owner,i);
+
+ if (member_id.notNull())
+ {
+ //llinfos << "Member " << member_id << " has powers " << std::hex << agent_powers << std::dec << llendl;
+ LLGroupMemberData* newdata = new LLGroupMemberData(member_id,
+ contribution,
+ agent_powers,
+ std::string(title),
+ std::string(online_status),
+ is_owner);
+#if LL_DEBUG
+ LLGroupMgrGroupData::member_iter mit = group_datap->mMembers.find(member_id);
+ if (mit != group_datap->mMembers.end())
+ {
+ llinfos << " *** Received duplicate member data for agent " << member_id << llendl;
+ }
+#endif
+ group_datap->mMembers[member_id] = newdata;
+ }
+ else
+ {
+ llinfos << "Received null group member data." << llendl;
+ }
+ }
+ }
+
+ if (group_datap->mMembers.size() == group_datap->mMemberCount)
+ {
+ group_datap->mMemberDataComplete = TRUE;
+ group_datap->mMemberRequestID.setNull();
+ // We don't want to make role-member data requests until we have all the members
+ if (group_datap->mPendingRoleMemberRequest)
+ {
+ group_datap->mPendingRoleMemberRequest = FALSE;
+ gGroupMgr->sendGroupRoleMembersRequest(group_datap->mID);
+ }
+ }
+
+ group_datap->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_MEMBER_DATA);
+}
+
+//static
+void LLGroupMgr::processGroupPropertiesReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupPropertiesReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ char name[DB_GROUP_NAME_BUF_SIZE];
+ char charter[DB_GROUP_CHARTER_BUF_SIZE];
+ BOOL show_in_list = FALSE;
+ LLUUID founder_id;
+ U64 powers_mask = GP_NO_POWERS;
+ S32 money = 0;
+ char member_title[DB_GROUP_TITLE_BUF_SIZE];
+ LLUUID insignia_id;
+ LLUUID owner_role;
+ U32 membership_fee = 0;
+ BOOL open_enrollment = FALSE;
+ S32 num_group_members = 0;
+ S32 num_group_roles = 0;
+ BOOL allow_publish = FALSE;
+ BOOL mature = FALSE;
+
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_FounderID, founder_id);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_Name, DB_GROUP_NAME_BUF_SIZE, name );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_Charter, DB_GROUP_CHARTER_BUF_SIZE, charter );
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_ShowInList, show_in_list );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_MemberTitle, DB_GROUP_TITLE_BUF_SIZE, member_title );
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_InsigniaID, insignia_id );
+ msg->getU64Fast(_PREHASH_GroupData, _PREHASH_PowersMask, powers_mask );
+ msg->getU32Fast(_PREHASH_GroupData, _PREHASH_MembershipFee, membership_fee );
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_OpenEnrollment, open_enrollment );
+ msg->getS32Fast(_PREHASH_GroupData, _PREHASH_GroupMembershipCount, num_group_members);
+ msg->getS32(_PREHASH_GroupData, "GroupRolesCount", num_group_roles);
+ msg->getS32Fast(_PREHASH_GroupData, _PREHASH_Money, money);
+ msg->getBOOL("GroupData", "AllowPublish", allow_publish);
+ msg->getBOOL("GroupData", "MaturePublish", mature);
+ msg->getUUID(_PREHASH_GroupData, "OwnerRole", owner_role);
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->createGroupData(group_id);
+
+ group_datap->mName = name;
+ group_datap->mCharter = charter;
+ group_datap->mShowInList = show_in_list;
+ group_datap->mInsigniaID = insignia_id;
+ group_datap->mFounderID = founder_id;
+ group_datap->mMembershipFee = membership_fee;
+ group_datap->mOpenEnrollment = open_enrollment;
+ group_datap->mAllowPublish = allow_publish;
+ group_datap->mMaturePublish = mature;
+ group_datap->mOwnerRole = owner_role;
+ group_datap->mMemberCount = num_group_members;
+ group_datap->mRoleCount = num_group_roles + 1; // Add the everyone role.
+
+ group_datap->mGroupPropertiesDataComplete = TRUE;
+ group_datap->mChanged = TRUE;
+
+ gGroupMgr->notifyObservers(GC_PROPERTIES);
+}
+
+// static
+void LLGroupMgr::processGroupRoleDataReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupRoleDataReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id );
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_RequestID, request_id);
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+ if (group_data->mRoleDataRequestID != request_id)
+ {
+ llwarns << "processGroupRoleDataReply: Received incorrect (stale?) request id" << llendl;
+ return;
+ }
+
+ msg->getS32(_PREHASH_GroupData, "RoleCount", group_data->mRoleCount );
+
+ char name[DB_GROUP_NAME_BUF_SIZE];
+ char title[DB_GROUP_TITLE_BUF_SIZE];
+ char desc[DB_GROUP_CHARTER_BUF_SIZE];
+ U64 powers = 0;
+ U32 member_count = 0;
+ LLUUID role_id;
+
+ U32 num_blocks = msg->getNumberOfBlocks("RoleData");
+ U32 i = 0;
+ for (i=0; i< num_blocks; ++i)
+ {
+ msg->getUUID("RoleData", "RoleID", role_id, i );
+
+ msg->getString("RoleData","Name",DB_GROUP_NAME_BUF_SIZE,name,i);
+ msg->getString("RoleData","Title",DB_GROUP_TITLE_BUF_SIZE,title,i);
+ msg->getString("RoleData","Description",DB_GROUP_CHARTER_BUF_SIZE,desc,i);
+ msg->getU64("RoleData","Powers",powers,i);
+ msg->getU32("RoleData","Members",member_count,i);
+
+ lldebugs << "Adding role data: " << name << " {" << role_id << "}" << llendl;
+ LLGroupRoleData* rd = new LLGroupRoleData(role_id,name,title,desc,powers,member_count);
+ group_data->mRoles[role_id] = rd;
+ }
+
+ if (group_data->mRoles.size() == group_data->mRoleCount)
+ {
+ group_data->mRoleDataComplete = TRUE;
+ group_data->mRoleDataRequestID.setNull();
+ // We don't want to make role-member data requests until we have all the role data
+ if (group_data->mPendingRoleMemberRequest)
+ {
+ group_data->mPendingRoleMemberRequest = FALSE;
+ gGroupMgr->sendGroupRoleMembersRequest(group_data->mID);
+ }
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_ROLE_DATA);
+}
+
+// static
+void LLGroupMgr::processGroupRoleMembersReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupRoleMembersReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ U32 total_pairs;
+ msg->getU32(_PREHASH_AgentData, "TotalPairs", total_pairs);
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+
+ if (group_data->mRoleMembersRequestID != request_id)
+ {
+ llwarns << "processGroupRoleMembersReply: Received incorrect (stale?) role member request id" << llendl;
+ return;
+ }
+
+ U32 num_blocks = msg->getNumberOfBlocks("MemberData");
+ U32 i;
+ LLUUID member_id;
+ LLUUID role_id;
+ LLGroupRoleData* rd = NULL;
+ LLGroupMemberData* md = NULL;
+
+ LLGroupMgrGroupData::role_iter ri;
+ LLGroupMgrGroupData::member_iter mi;
+
+ // If total_pairs == 0, there are no members in any custom roles.
+ if (total_pairs > 0)
+ {
+ for (i = 0;i < num_blocks; ++i)
+ {
+ msg->getUUID("MemberData","RoleID",role_id,i);
+ msg->getUUID("MemberData","MemberID",member_id,i);
+
+ if (role_id.notNull() && member_id.notNull() )
+ {
+ rd = NULL;
+ ri = group_data->mRoles.find(role_id);
+ if (ri != group_data->mRoles.end())
+ {
+ rd = ri->second;
+ }
+
+ md = NULL;
+ mi = group_data->mMembers.find(member_id);
+ if (mi != group_data->mMembers.end())
+ {
+ md = mi->second;
+ }
+
+ if (rd && md)
+ {
+ lldebugs << "Adding role-member pair: " << role_id << ", " << member_id << llendl;
+ rd->addMember(member_id);
+ md->addRole(role_id,rd);
+ }
+ else
+ {
+ if (!rd) llwarns << "Received role data for unkown role " << role_id << " in group " << group_id << llendl;
+ if (!md) llwarns << "Received role data for unkown member " << member_id << " in group " << group_id << llendl;
+ }
+ }
+ }
+
+ group_data->mReceivedRoleMemberPairs += num_blocks;
+ }
+
+ if (group_data->mReceivedRoleMemberPairs == total_pairs)
+ {
+ // Add role data for the 'everyone' role to all members
+ LLGroupRoleData* everyone = group_data->mRoles[LLUUID::null];
+ if (!everyone)
+ {
+ llwarns << "Everyone role not found!" << llendl;
+ }
+ else
+ {
+ LLGroupMgrGroupData::member_iter mi = group_data->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = group_data->mMembers.end();
+ for ( ; mi != end; ++mi)
+ {
+ LLGroupMemberData* data = mi->second;
+ if (data)
+ {
+ data->addRole(LLUUID::null,everyone);
+ }
+ }
+ }
+
+ group_data->mRoleMemberDataComplete = TRUE;
+ group_data->mRoleMembersRequestID.setNull();
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_ROLE_MEMBER_DATA);
+}
+
+// static
+void LLGroupMgr::processGroupTitlesReply(LLMessageSystem* msg, void** data)
+{
+ llinfos << "LLGroupMgr::processGroupTitlesReply" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group properties reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+
+ LLGroupMgrGroupData* group_data = gGroupMgr->createGroupData(group_id);
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_RequestID, request_id);
+
+ if (group_data->mTitlesRequestID != request_id)
+ {
+ llwarns << "processGroupTitlesReply: Received incorrect (stale?) title request id" << llendl;
+ return;
+ }
+
+ char title_buf[DB_GROUP_TITLE_BUF_SIZE];
+
+ LLGroupTitle title;
+
+ S32 i = 0;
+ S32 blocks = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ for (i=0; i<blocks; ++i)
+ {
+ msg->getString("GroupData","Title",DB_GROUP_TITLE_BUF_SIZE,title_buf,i);
+ title.mTitle = title_buf;
+ msg->getUUID("GroupData","RoleID",title.mRoleID,i);
+ msg->getBOOL("GroupData","Selected",title.mSelected,i);
+
+ if (title_buf[0] != '\0')
+ {
+ lldebugs << "LLGroupMgr adding title: " << title.mTitle << ", " << title.mRoleID << ", " << (title.mSelected ? 'Y' : 'N') << llendl;
+ group_data->mTitles.push_back(title);
+ }
+ }
+
+ group_data->mChanged = TRUE;
+ gGroupMgr->notifyObservers(GC_TITLES);
+}
+
+// static
+void LLGroupMgr::processEjectGroupMemberReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processEjectGroupMemberReply" << llendl;
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ BOOL success;
+ msg->getBOOLFast(_PREHASH_EjectData, _PREHASH_Success, success);
+
+ // If we had a failure, the group panel needs to be updated.
+ if (!success)
+ {
+ LLFloaterGroupInfo::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processJoinGroupReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processJoinGroupReply" << llendl;
+ LLUUID group_id;
+ BOOL success;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // refresh the floater for this group, if any.
+ LLFloaterGroupInfo::refreshGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processLeaveGroupReply(LLMessageSystem* msg, void ** data)
+{
+ llinfos << "processLeaveGroupReply" << llendl;
+ LLUUID group_id;
+ BOOL success;
+ msg->getUUIDFast(_PREHASH_GroupData, _PREHASH_GroupID, group_id);
+ msg->getBOOLFast(_PREHASH_GroupData, _PREHASH_Success, success);
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ gGroupMgr->clearGroupData(group_id);
+ // close the floater for this group, if any.
+ LLFloaterGroupInfo::closeGroup(group_id);
+ // refresh the group panel of the search window, if necessary.
+ LLFloaterDirectory::refreshGroup(group_id);
+ }
+}
+
+// static
+void LLGroupMgr::processCreateGroupReply(LLMessageSystem* msg, void ** data)
+{
+ LLUUID group_id;
+ BOOL success;
+ char message[MAX_STRING];
+
+ msg->getUUIDFast(_PREHASH_ReplyData, _PREHASH_GroupID, group_id );
+
+ msg->getBOOLFast(_PREHASH_ReplyData, _PREHASH_Success, success );
+ msg->getStringFast(_PREHASH_ReplyData, _PREHASH_Message, MAX_STRING, message );
+
+ if (success)
+ {
+ // refresh all group information
+ gAgent.sendAgentDataUpdateRequest();
+
+ // HACK! We haven't gotten the agent group update yet, so ... um ... fake it.
+ // This is so when we go to modify the group we will be able to do so.
+ // This isn't actually too bad because real data will come down in 2 or 3 miliseconds and replace this.
+ LLGroupData gd;
+ gd.mAcceptNotices = TRUE;
+ gd.mContribution = 0;
+ gd.mID = group_id;
+ gd.mName = "new group";
+ gd.mPowers = GP_ALL_POWERS;
+
+ gAgent.mGroups.push_back(gd);
+
+ LLFloaterGroupInfo::closeCreateGroup();
+ LLFloaterGroupInfo::showFromUUID(group_id,"roles_tab");
+ }
+ else
+ {
+ // XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = message;
+ gViewerWindow->alertXml("UnableToCreateGroup", args);
+ }
+}
+
+LLGroupMgrGroupData* LLGroupMgr::createGroupData(const LLUUID& id)
+{
+ LLGroupMgrGroupData* group_datap;
+ group_iter existing_group = gGroupMgr->mGroups.find(id);
+ if (existing_group == gGroupMgr->mGroups.end())
+ {
+ group_datap = new LLGroupMgrGroupData(id);
+ gGroupMgr->addGroup(group_datap);
+ }
+ else
+ {
+ group_datap = existing_group->second;
+ }
+
+ return group_datap;
+}
+
+void LLGroupMgr::notifyObservers(LLGroupChange gc)
+{
+ for (group_iter gi = mGroups.begin(); gi != mGroups.end(); ++gi)
+ {
+ if (gi->second->mChanged)
+ {
+ // find all observers for this group id
+ observer_iter oi = mObservers.find(gi->first);
+ for (; oi != mObservers.end(); ++oi)
+ {
+ oi->second->changed(gc);
+ }
+ gi->second->mChanged = FALSE;
+ }
+ }
+}
+
+void LLGroupMgr::addGroup(LLGroupMgrGroupData* group_datap)
+{
+ mGroups[group_datap->getID()] = group_datap;
+ if (mGroups.size() > MAX_CACHED_GROUPS)
+ {
+ // get rid of groups that aren't observed
+ for (group_iter gi = mGroups.begin(); gi != mGroups.end() && mGroups.size() > MAX_CACHED_GROUPS / 2; )
+ {
+ observer_iter oi = mObservers.find(gi->first);
+ if (oi == mObservers.end())
+ {
+ // not observed
+ LLGroupMgrGroupData* group_datap = gi->second;
+ delete group_datap;
+ mGroups.erase(gi++);
+ }
+ else
+ {
+ ++gi;
+ }
+ }
+ }
+}
+
+
+void LLGroupMgr::sendGroupPropertiesRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupPropertiesRequest" << llendl;
+ // This will happen when we get the reply
+ //LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupProfileRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendGroupMembersRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupMembersRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ if (group_datap->mMemberRequestID.isNull())
+ {
+ group_datap->removeMemberData();
+ group_datap->mMemberRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupMembersRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mMemberRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleDataRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleDataRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ if (group_datap->mRoleDataRequestID.isNull())
+ {
+ group_datap->removeRoleData();
+ group_datap->mRoleDataRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupRoleDataRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mRoleDataRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleMembersRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleMembersRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ if (group_datap->mRoleMembersRequestID.isNull())
+ {
+ // Don't send the request if we don't have all the member or role data
+ if (!group_datap->isMemberDataComplete()
+ || !group_datap->isRoleDataComplete())
+ {
+ // TODO: KLW FIXME: Should we start a member or role data request?
+ llinfos << " Pending: " << (group_datap->mPendingRoleMemberRequest ? "Y" : "N")
+ << " MemberDataComplete: " << (group_datap->mMemberDataComplete ? "Y" : "N")
+ << " RoleDataComplete: " << (group_datap->mRoleDataComplete ? "Y" : "N") << llendl;
+ group_datap->mPendingRoleMemberRequest = TRUE;
+ return;
+ }
+
+ group_datap->removeRoleMemberData();
+ group_datap->mRoleMembersRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupRoleMembersRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mRoleMembersRequestID);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupTitlesRequest(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupTitlesRequest" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ group_datap->mTitles.clear();
+ group_datap->mTitlesRequestID.generate();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupTitlesRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("RequestID",group_datap->mTitlesRequestID);
+
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id)
+{
+ llinfos << "LLGroupMgr::sendGroupTitleUpdate" << llendl;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupTitleUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->addUUID("GroupID",group_id);
+ msg->addUUID("TitleRoleID",title_role_id);
+
+ gAgent.sendReliableMessage();
+
+ // Save the change locally
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+ std::vector<LLGroupTitle>::iterator iter = group_datap->mTitles.begin();
+ std::vector<LLGroupTitle>::iterator end = group_datap->mTitles.end();
+
+ for ( ; iter != end; ++iter)
+ {
+ if (iter->mRoleID == title_role_id)
+ {
+ iter->mSelected = TRUE;
+ }
+ else if (iter->mSelected)
+ {
+ iter->mSelected = FALSE;
+ }
+ }
+}
+
+// static
+void LLGroupMgr::sendCreateGroupRequest(const std::string& name,
+ const std::string& charter,
+ U8 show_in_list,
+ const LLUUID& insignia,
+ S32 membership_fee,
+ BOOL open_enrollment,
+ BOOL allow_publish,
+ BOOL mature_publish)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("CreateGroupRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+
+ msg->nextBlock("GroupData");
+ msg->addString("Name",name);
+ msg->addString("Charter",charter);
+ msg->addBOOL("ShowInList",show_in_list);
+ msg->addUUID("InsigniaID",insignia);
+ msg->addS32("MembershipFee",membership_fee);
+ msg->addBOOL("OpenEnrollment",open_enrollment);
+ msg->addBOOL("AllowPublish",allow_publish);
+ msg->addBOOL("MaturePublish",mature_publish);
+
+ gAgent.sendReliableMessage();
+}
+
+void LLGroupMgr::sendUpdateGroupInfo(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendUpdateGroupInfo" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_UpdateGroupInfo);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID,group_datap->getID());
+ msg->addStringFast(_PREHASH_Charter,group_datap->mCharter);
+ msg->addBOOLFast(_PREHASH_ShowInList,group_datap->mShowInList);
+ msg->addUUIDFast(_PREHASH_InsigniaID,group_datap->mInsigniaID);
+ msg->addS32Fast(_PREHASH_MembershipFee,group_datap->mMembershipFee);
+ msg->addBOOLFast(_PREHASH_OpenEnrollment,group_datap->mOpenEnrollment);
+ msg->addBOOLFast(_PREHASH_AllowPublish,group_datap->mAllowPublish);
+ msg->addBOOLFast(_PREHASH_MaturePublish,group_datap->mMaturePublish);
+
+ gAgent.sendReliableMessage();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_PROPERTIES);
+}
+
+void LLGroupMgr::sendGroupRoleMemberChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleMemberChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = createGroupData(group_id);
+
+ if (group_datap->mRoleMemberChanges.empty()) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ change_map::const_iterator citer = group_datap->mRoleMemberChanges.begin();
+ change_map::const_iterator end = group_datap->mRoleMemberChanges.end();
+ bool start_message = true;
+
+ for ( ; citer != end; ++citer)
+ {
+ if (start_message)
+ {
+ msg->newMessage("GroupRoleChanges");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID,gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID,gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID,group_id);
+ start_message = false;
+ }
+ msg->nextBlock("RoleChange");
+ msg->addUUID("RoleID",citer->second.mRole);
+ msg->addUUID("MemberID",citer->second.mMember);
+ msg->addU32("Change",(U32)citer->second.mChange);
+
+ if (msg->isSendFullFast())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ group_datap->mRoleMemberChanges.clear();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_ROLE_MEMBER_DATA);
+}
+
+//static
+void LLGroupMgr::sendGroupMemberJoin(const LLUUID& group_id)
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_JoinGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, group_id);
+
+ gAgent.sendReliableMessage();
+}
+
+// member_role_pairs is <member_id,role_id>
+// static
+void LLGroupMgr::sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& member_role_pairs)
+{
+ bool start_message = true;
+ LLMessageSystem* msg = gMessageSystem;
+
+ std::map<LLUUID,LLUUID>::iterator it = member_role_pairs.begin();
+ std::map<LLUUID,LLUUID>::iterator end = member_role_pairs.end();
+ for ( ; it != end; ++it)
+ {
+ if (start_message)
+ {
+ msg->newMessage("InviteGroupRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("InviteData");
+ msg->addUUID("InviteeID",(*it).first);
+ msg->addUUID("RoleID",(*it).second);
+
+ if (msg->isSendFull())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+//static
+void LLGroupMgr::sendGroupMemberEjects(const LLUUID& group_id,
+ std::vector<LLUUID>& member_ids)
+{
+ bool start_message = true;
+ LLMessageSystem* msg = gMessageSystem;
+
+ LLGroupMgrGroupData* group_datap = gGroupMgr->getGroupData(group_id);
+ if (!group_datap) return;
+
+ std::vector<LLUUID>::iterator it = member_ids.begin();
+ std::vector<LLUUID>::iterator end = member_ids.end();
+ for ( ; it != end; ++it)
+ {
+ // Can't use 'eject' to leave a group.
+ if ((*it) == gAgent.getID()) continue;
+
+ // Make sure they are in the group, and we need the member data
+ LLGroupMgrGroupData::member_iter mit = group_datap->mMembers.find(*it);
+ if (mit != group_datap->mMembers.end())
+ {
+ // Add them to the message
+ if (start_message)
+ {
+ msg->newMessage("EjectGroupMemberRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("GroupData");
+ msg->addUUID("GroupID",group_id);
+ start_message = false;
+ }
+
+ msg->nextBlock("EjectData");
+ msg->addUUID("EjecteeID",(*it));
+
+ if (msg->isSendFull())
+ {
+ gAgent.sendReliableMessage();
+ start_message = true;
+ }
+
+ // Clean up groupmgr
+ std::map<LLUUID,LLGroupRoleData*>::iterator rit = (*mit).second->roleBegin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator rend = (*mit).second->roleEnd();
+ for ( ; rit != rend; ++rit)
+ {
+ if ((*rit).first.notNull())
+ {
+ (*rit).second->removeMember(*it);
+ }
+ }
+ delete (*mit).second;
+ group_datap->mMembers.erase(*it);
+ }
+ }
+
+ if (!start_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLGroupMgr::sendGroupRoleChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::sendGroupRoleChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = getGroupData(group_id);
+
+ if (group_datap && group_datap->pendingRoleChanges())
+ {
+ group_datap->sendRoleChanges();
+
+ // Not expecting a response, so let anyone else watching know the data has changed.
+ group_datap->mChanged = TRUE;
+ notifyObservers(GC_ROLE_DATA);
+ }
+}
+
+void LLGroupMgr::cancelGroupRoleChanges(const LLUUID& group_id)
+{
+ llinfos << "LLGroupMgr::cancelGroupRoleChanges" << llendl;
+ LLGroupMgrGroupData* group_datap = getGroupData(group_id);
+
+ if (group_datap) group_datap->cancelRoleChanges();
+}
+
+//static
+bool LLGroupMgr::parseRoleActions(const LLString& xml_filename)
+{
+ LLXmlTree xml_tree;
+ LLString xml_file = LLUI::locateSkin(xml_filename);
+ BOOL success = xml_tree.parseFile(xml_file, TRUE );
+ LLXmlTreeNode* root = xml_tree.getRoot();
+ if (!success || !root || !root->hasName( "role_actions" ))
+ {
+ llerrs << "Problem reading UI role_actions file: " << xml_filename << llendl;
+ return false;
+ }
+
+ for (LLXmlTreeNode* action_set = root->getChildByName("action_set");
+ action_set != NULL; action_set = root->getNextNamedChild())
+ {
+ LLRoleActionSet* role_action_set = new LLRoleActionSet();
+ LLRoleAction* role_action_data = new LLRoleAction();
+ // name=
+ LLString action_set_name;
+ if (action_set->getAttributeString("name", action_set_name))
+ {
+ lldebugs << "Loading action set " << action_set_name << llendl;
+ role_action_data->mName = action_set_name;
+ }
+ else
+ {
+ llwarns << "Unable to parse action set with no name" << llendl;
+ delete role_action_set;
+ delete role_action_data;
+ continue;
+ }
+ // description=
+ LLString set_description;
+ if (action_set->getAttributeString("description", set_description))
+ {
+ role_action_data->mDescription = set_description;
+ }
+ // long description=
+ LLString set_longdescription;
+ if (action_set->getAttributeString("longdescription", set_longdescription))
+ {
+ role_action_data->mLongDescription = set_longdescription;
+ }
+
+ // power mask=
+ U64 set_power_mask = 0;
+
+ for (LLXmlTreeNode* action = action_set->getChildByName("action");
+ action != NULL; action = action_set->getNextNamedChild())
+ {
+ LLRoleAction* role_action = new LLRoleAction();
+
+ // name=
+ LLString action_name;
+ if (action->getAttributeString("name", action_name))
+ {
+ lldebugs << "Loading action " << action_name << llendl;
+ role_action->mName = action_name;
+ }
+ else
+ {
+ llwarns << "Unable to parse action with no name" << llendl;
+ delete role_action;
+ continue;
+ }
+ // description=
+ LLString description;
+ if (action->getAttributeString("description", description))
+ {
+ role_action->mDescription = description;
+ }
+ // long description=
+ LLString longdescription;
+ if (action->getAttributeString("longdescription", longdescription))
+ {
+ role_action->mLongDescription = longdescription;
+ }
+ // description=
+ S32 power_bit = 0;
+ if (action->getAttributeS32("value", power_bit))
+ {
+ if (0 <= power_bit && power_bit < 64)
+ {
+ role_action->mPowerBit = 0x1LL << power_bit;
+ }
+ }
+
+ set_power_mask |= role_action->mPowerBit;
+
+ role_action_set->mActions.push_back(role_action);
+ }
+
+ role_action_data->mPowerBit = set_power_mask;
+ role_action_set->mActionSetData = role_action_data;
+
+ gGroupMgr->mRoleActionSets.push_back(role_action_set);
+ }
+ return true;
+}
+
+// static
+void LLGroupMgr::debugClearAllGroups(void*)
+{
+ gGroupMgr->clearGroups();
+ LLGroupMgr::parseRoleActions("role_actions.xml");
+}
+
diff --git a/indra/newview/llgroupmgr.h b/indra/newview/llgroupmgr.h
new file mode 100644
index 0000000000..7c06acbc1f
--- /dev/null
+++ b/indra/newview/llgroupmgr.h
@@ -0,0 +1,343 @@
+/**
+ * @file llgroupmgr.h
+ * @brief Manager for aggregating all client knowledge for specific groups
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLGROUPMGR_H
+#define LL_LLGROUPMGR_H
+
+#include "lluuid.h"
+#include "roles_constants.h"
+#include <vector>
+#include <string>
+#include <map>
+
+class LLMessageSystem;
+
+class LLGroupMgrObserver
+{
+public:
+ LLGroupMgrObserver(const LLUUID& id) : mID(id){};
+ virtual ~LLGroupMgrObserver(){};
+ virtual void changed(LLGroupChange gc) = 0;
+ const LLUUID& getID() { return mID; }
+protected:
+ LLUUID mID;
+};
+
+class LLGroupRoleData;
+
+class LLGroupMemberData
+{
+friend class LLGroupMgrGroupData;
+
+public:
+ LLGroupMemberData(const LLUUID& id,
+ S32 contribution,
+ U64 agent_powers,
+ const std::string& title,
+ const std::string& online_status,
+ BOOL is_owner);
+
+ ~LLGroupMemberData();
+
+ const LLUUID& getID() const { return mID; }
+ S32 getContribution() const { return mContribution; }
+ U64 getAgentPowers() const { return mAgentPowers; }
+ BOOL isOwner() const { return mIsOwner; }
+ const std::string& getTitle() const { return mTitle; }
+ const std::string& getOnlineStatus() const { return mOnlineStatus; }
+ void addRole(const LLUUID& role, LLGroupRoleData* rd);
+ bool removeRole(const LLUUID& role);
+ void clearRoles() { mRoles.clear(); };
+ std::map<LLUUID,LLGroupRoleData*>::iterator roleBegin() { return mRoles.begin(); }
+ std::map<LLUUID,LLGroupRoleData*>::iterator roleEnd() { return mRoles.end(); }
+
+ BOOL isInRole(const LLUUID& role_id) { return (mRoles.find(role_id) != mRoles.end()); }
+
+protected:
+ LLUUID mID;
+ S32 mContribution;
+ U64 mAgentPowers;
+ std::string mTitle;
+ std::string mOnlineStatus;
+ BOOL mIsOwner;
+ std::map<LLUUID,LLGroupRoleData*> mRoles;
+};
+
+struct LLRoleData
+{
+ LLRoleData() : mRolePowers(0), mChangeType(RC_UPDATE_NONE) { }
+ LLRoleData(const LLRoleData& rd)
+ : mRoleName(rd.mRoleName),
+ mRoleTitle(rd.mRoleTitle),
+ mRoleDescription(rd.mRoleDescription),
+ mRolePowers(rd.mRolePowers),
+ mChangeType(rd.mChangeType) { }
+
+ std::string mRoleName;
+ std::string mRoleTitle;
+ std::string mRoleDescription;
+ U64 mRolePowers;
+ LLRoleChangeType mChangeType;
+};
+
+class LLGroupRoleData
+{
+friend class LLGroupMgrGroupData;
+
+public:
+ LLGroupRoleData(const LLUUID& role_id,
+ const std::string& role_name,
+ const std::string& role_title,
+ const std::string& role_desc,
+ const U64 role_powers,
+ const S32 member_count);
+
+ LLGroupRoleData(const LLUUID& role_id,
+ LLRoleData role_data,
+ const S32 member_count);
+
+ ~LLGroupRoleData();
+
+ const LLUUID& getID() const { return mRoleID; }
+
+ const std::vector<LLUUID>& getRoleMembers() const { return mMemberIDs; }
+ S32 getMembersInRole(std::vector<LLUUID> members, BOOL needs_sort = TRUE);
+ S32 getTotalMembersInRole() { return mMemberIDs.size(); }
+
+ LLRoleData getRoleData() const { return mRoleData; }
+ void setRoleData(LLRoleData data) { mRoleData = data; }
+
+ void addMember(const LLUUID& member);
+ bool removeMember(const LLUUID& member);
+ void clearMembers();
+
+ const std::vector<LLUUID>::const_iterator getMembersBegin() const
+ { return mMemberIDs.begin(); }
+
+ const std::vector<LLUUID>::const_iterator getMembersEnd() const
+ { return mMemberIDs.end(); }
+
+
+protected:
+ LLGroupRoleData()
+ : mMemberCount(0), mMembersNeedsSort(FALSE) {}
+
+ LLUUID mRoleID;
+ LLRoleData mRoleData;
+
+ std::vector<LLUUID> mMemberIDs;
+ S32 mMemberCount;
+
+private:
+ BOOL mMembersNeedsSort;
+};
+
+struct LLRoleMemberChange
+{
+ LLRoleMemberChange() : mChange(RMC_NONE) { }
+ LLRoleMemberChange(const LLUUID& role, const LLUUID& member, LLRoleMemberChangeType change)
+ : mRole(role), mMember(member), mChange(change) { }
+ LLRoleMemberChange(const LLRoleMemberChange& rc)
+ : mRole(rc.mRole), mMember(rc.mMember), mChange(rc.mChange) { }
+ LLUUID mRole;
+ LLUUID mMember;
+ LLRoleMemberChangeType mChange;
+};
+
+typedef std::pair<LLUUID,LLUUID> lluuid_pair;
+
+struct lluuid_pair_less
+{
+ bool operator()(const lluuid_pair& lhs, const lluuid_pair& rhs) const
+ {
+ if (lhs.first == rhs.first)
+ return lhs.second < rhs.second;
+ else
+ return lhs.first < rhs.first;
+ }
+};
+
+typedef std::map<lluuid_pair,LLRoleMemberChange,lluuid_pair_less> change_map;
+
+struct LLGroupTitle
+{
+ std::string mTitle;
+ LLUUID mRoleID;
+ BOOL mSelected;
+};
+
+class LLGroupMgr;
+
+class LLGroupMgrGroupData
+{
+friend class LLGroupMgr;
+
+public:
+ LLGroupMgrGroupData(const LLUUID& id);
+ ~LLGroupMgrGroupData();
+
+ const LLUUID& getID() { return mID; }
+
+ BOOL getRoleData(const LLUUID& role_id, LLRoleData& role_data);
+ void setRoleData(const LLUUID& role_id, LLRoleData role_data);
+ void createRole(const LLUUID& role_id, LLRoleData role_data);
+ void deleteRole(const LLUUID& role_id);
+ BOOL pendingRoleChanges();
+
+ void addRolePower(const LLUUID& role_id, U64 power);
+ void removeRolePower(const LLUUID& role_id, U64 power);
+ U64 getRolePowers(const LLUUID& role_id);
+
+ void removeData();
+ void removeRoleData();
+ void removeMemberData();
+ void removeRoleMemberData();
+
+ bool changeRoleMember(const LLUUID& role_id, const LLUUID& member_id, LLRoleMemberChangeType rmc);
+ void recalcAllAgentPowers();
+ void recalcAgentPowers(const LLUUID& agent_id);
+
+ BOOL isMemberDataComplete() { return mMemberDataComplete; }
+ BOOL isRoleDataComplete() { return mRoleDataComplete; }
+ BOOL isRoleMemberDataComplete() { return mRoleMemberDataComplete; }
+ BOOL isGroupPropertiesDataComplete() { return mGroupPropertiesDataComplete; }
+
+public:
+ typedef std::map<LLUUID,LLGroupMemberData*> member_list;
+ typedef member_list::iterator member_iter;
+ typedef std::map<LLUUID,LLGroupRoleData*> role_list;
+ typedef role_list::iterator role_iter;
+
+ member_list mMembers;
+ role_list mRoles;
+
+
+ change_map mRoleMemberChanges;
+ std::map<LLUUID,LLRoleData> mRoleChanges;
+
+ std::vector<LLGroupTitle> mTitles;
+
+ LLUUID mID;
+ LLUUID mOwnerRole;
+ std::string mName;
+ std::string mCharter;
+ BOOL mShowInList;
+ LLUUID mInsigniaID;
+ LLUUID mFounderID;
+ BOOL mOpenEnrollment;
+ S32 mMembershipFee;
+ BOOL mAllowPublish;
+ BOOL mMaturePublish;
+ BOOL mChanged;
+ S32 mMemberCount;
+ S32 mRoleCount;
+
+protected:
+ void sendRoleChanges();
+ void cancelRoleChanges();
+
+private:
+ LLUUID mMemberRequestID;
+ LLUUID mRoleDataRequestID;
+ LLUUID mRoleMembersRequestID;
+ LLUUID mTitlesRequestID;
+ U32 mReceivedRoleMemberPairs;
+
+ BOOL mMemberDataComplete;
+ BOOL mRoleDataComplete;
+ BOOL mRoleMemberDataComplete;
+ BOOL mGroupPropertiesDataComplete;
+
+ BOOL mPendingRoleMemberRequest;
+};
+
+struct LLRoleAction
+{
+ std::string mName;
+ std::string mDescription;
+ std::string mLongDescription;
+ U64 mPowerBit;
+};
+
+struct LLRoleActionSet
+{
+ LLRoleActionSet();
+ ~LLRoleActionSet();
+ LLRoleAction* mActionSetData;
+ std::vector<LLRoleAction*> mActions;
+};
+
+class LLGroupMgr
+{
+public:
+ LLGroupMgr();
+ ~LLGroupMgr();
+
+ void addObserver(LLGroupMgrObserver* observer);
+ void removeObserver(LLGroupMgrObserver* observer);
+ LLGroupMgrGroupData* getGroupData(const LLUUID& id);
+
+ void sendGroupPropertiesRequest(const LLUUID& group_id);
+ void sendGroupRoleDataRequest(const LLUUID& group_id);
+ void sendGroupRoleMembersRequest(const LLUUID& group_id);
+ void sendGroupMembersRequest(const LLUUID& group_id);
+ void sendGroupTitlesRequest(const LLUUID& group_id);
+ void sendGroupTitleUpdate(const LLUUID& group_id, const LLUUID& title_role_id);
+ void sendUpdateGroupInfo(const LLUUID& group_id);
+ void sendGroupRoleMemberChanges(const LLUUID& group_id);
+ void sendGroupRoleChanges(const LLUUID& group_id);
+
+ static void sendCreateGroupRequest(const std::string& name,
+ const std::string& charter,
+ U8 show_in_list,
+ const LLUUID& insignia,
+ S32 membership_fee,
+ BOOL open_enrollment,
+ BOOL allow_publish,
+ BOOL mature_publish);
+
+ static void sendGroupMemberJoin(const LLUUID& group_id);
+ static void sendGroupMemberInvites(const LLUUID& group_id, std::map<LLUUID,LLUUID>& role_member_pairs);
+ static void sendGroupMemberEjects(const LLUUID& group_id,
+ std::vector<LLUUID>& member_ids);
+
+ void cancelGroupRoleChanges(const LLUUID& group_id);
+
+ static void processGroupPropertiesReply(LLMessageSystem* msg, void** data);
+ static void processGroupMembersReply(LLMessageSystem* msg, void** data);
+ static void processGroupRoleDataReply(LLMessageSystem* msg, void** data);
+ static void processGroupRoleMembersReply(LLMessageSystem* msg, void** data);
+ static void processGroupTitlesReply(LLMessageSystem* msg, void** data);
+ static void processCreateGroupReply(LLMessageSystem* msg, void** data);
+ static void processJoinGroupReply(LLMessageSystem* msg, void ** data);
+ static void processEjectGroupMemberReply(LLMessageSystem* msg, void ** data);
+ static void processLeaveGroupReply(LLMessageSystem* msg, void ** data);
+
+ static bool parseRoleActions(const LLString& xml_filename);
+
+ std::vector<LLRoleActionSet*> mRoleActionSets;
+
+ static void debugClearAllGroups(void*);
+ void clearGroups();
+ void clearGroupData(const LLUUID& group_id);
+protected:
+ void notifyObservers(LLGroupChange gc);
+ void addGroup(LLGroupMgrGroupData* group_datap);
+ LLGroupMgrGroupData* createGroupData(const LLUUID &id);
+
+protected:
+ typedef std::multimap<LLUUID,LLGroupMgrObserver*>::iterator observer_iter;
+ std::multimap<LLUUID,LLGroupMgrObserver*> mObservers;
+ typedef std::map<LLUUID, LLGroupMgrGroupData*>::iterator group_iter;
+ std::map<LLUUID, LLGroupMgrGroupData*> mGroups;
+};
+
+extern LLGroupMgr* gGroupMgr;
+
+#endif
+
diff --git a/indra/newview/llhudeffect.cpp b/indra/newview/llhudeffect.cpp
new file mode 100644
index 0000000000..4f80fffb01
--- /dev/null
+++ b/indra/newview/llhudeffect.cpp
@@ -0,0 +1,115 @@
+/**
+ * @file llhudeffect.cpp
+ * @brief LLHUDEffect class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffect.h"
+
+#include "message.h"
+#include "llgl.h"
+#include "llagent.h"
+#include "llsphere.h"
+#include "llimagegl.h"
+
+#include "llviewerobjectlist.h"
+#include "lldrawable.h"
+
+LLHUDEffect::LLHUDEffect(const U8 type)
+: LLHUDObject(type),
+ mID(),
+ mDuration(1.f),
+ mColor()
+{
+ mNeedsSendToSim = FALSE;
+ mOriginatedHere = FALSE;
+ mDead = FALSE;
+}
+
+LLHUDEffect::~LLHUDEffect()
+{
+}
+
+
+void LLHUDEffect::packData(LLMessageSystem *mesgsys)
+{
+ mesgsys->addUUIDFast(_PREHASH_ID, mID);
+ mesgsys->addU8Fast(_PREHASH_Type, mType);
+ mesgsys->addF32Fast(_PREHASH_Duration, mDuration);
+ mesgsys->addBinaryData(_PREHASH_Color, mColor.mV, 4);
+}
+
+void LLHUDEffect::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, mID, blocknum);
+ mesgsys->getU8Fast(_PREHASH_Effect, _PREHASH_Type, mType, blocknum);
+ mesgsys->getF32Fast(_PREHASH_Effect, _PREHASH_Duration, mDuration, blocknum);
+ mesgsys->getBinaryDataFast(_PREHASH_Effect,_PREHASH_Color, mColor.mV, 4, blocknum);
+}
+
+void LLHUDEffect::render()
+{
+ llerrs << "Never call this!" << llendl;
+}
+
+void LLHUDEffect::setID(const LLUUID &id)
+{
+ mID = id;
+}
+
+const LLUUID &LLHUDEffect::getID() const
+{
+ return mID;
+}
+
+void LLHUDEffect::setColor(const LLColor4U &color)
+{
+ mColor = color;
+}
+
+void LLHUDEffect::setDuration(const F32 duration)
+{
+ mDuration = duration;
+}
+
+void LLHUDEffect::setNeedsSendToSim(const BOOL send_to_sim)
+{
+ mNeedsSendToSim = send_to_sim;
+}
+
+BOOL LLHUDEffect::getNeedsSendToSim() const
+{
+ return mNeedsSendToSim;
+}
+
+
+void LLHUDEffect::setOriginatedHere(const BOOL orig_here)
+{
+ mOriginatedHere = orig_here;
+}
+
+BOOL LLHUDEffect::getOriginatedHere() const
+{
+ return mOriginatedHere;
+}
+
+//static
+void LLHUDEffect::getIDType(LLMessageSystem *mesgsys, S32 blocknum, LLUUID &id, U8 &type)
+{
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, id, blocknum);
+ mesgsys->getU8Fast(_PREHASH_Effect, _PREHASH_Type, type, blocknum);
+}
+
+BOOL LLHUDEffect::isDead() const
+{
+ return mDead;
+}
+
+void LLHUDEffect::update()
+{
+ // do nothing
+}
diff --git a/indra/newview/llhudeffect.h b/indra/newview/llhudeffect.h
new file mode 100644
index 0000000000..948670ceed
--- /dev/null
+++ b/indra/newview/llhudeffect.h
@@ -0,0 +1,62 @@
+/**
+ * @file llhudeffect.h
+ * @brief LLHUDEffect class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECT_H
+#define LL_LLHUDEFFECT_H
+
+#include "llhudobject.h"
+
+#include "lluuid.h"
+#include "v4coloru.h"
+#include "llinterp.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+const F32 LL_HUD_DUR_SHORT = 1.f;
+
+class LLMessageSystem;
+
+
+class LLHUDEffect : public LLHUDObject
+{
+public:
+ void setNeedsSendToSim(const BOOL send_to_sim);
+ BOOL getNeedsSendToSim() const;
+ void setOriginatedHere(const BOOL orig_here);
+ BOOL getOriginatedHere() const;
+
+ void setDuration(const F32 duration);
+ void setColor(const LLColor4U &color);
+ void setID(const LLUUID &id);
+ const LLUUID &getID() const;
+
+ BOOL isDead() const;
+
+ friend class LLHUDManager;
+protected:
+ LLHUDEffect(const U8 type);
+ ~LLHUDEffect();
+
+ /*virtual*/ void render();
+
+ virtual void packData(LLMessageSystem *mesgsys);
+ virtual void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+ virtual void update();
+
+ static void getIDType(LLMessageSystem *mesgsys, S32 blocknum, LLUUID &uuid, U8 &type);
+
+protected:
+ LLUUID mID;
+ F32 mDuration;
+ LLColor4U mColor;
+
+ BOOL mNeedsSendToSim;
+ BOOL mOriginatedHere;
+};
+
+#endif // LL_LLHUDEFFECT_H
diff --git a/indra/newview/llhudeffectbeam.cpp b/indra/newview/llhudeffectbeam.cpp
new file mode 100644
index 0000000000..069986eb77
--- /dev/null
+++ b/indra/newview/llhudeffectbeam.cpp
@@ -0,0 +1,371 @@
+/**
+ * @file llhudeffectbeam.cpp
+ * @brief LLHUDEffectBeam class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectbeam.h"
+#include "message.h"
+
+#include "llviewerobjectlist.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llimagegl.h"
+#include "llsphere.h"
+#include "llviewercamera.h"
+#include "llvoavatar.h"
+#include "llviewercontrol.h"
+
+const F32 BEAM_SPACING = 0.075f;
+
+LLHUDEffectBeam::LLHUDEffectBeam(const U8 type) : LLHUDEffect(type)
+{
+ mKillTime = mDuration;
+
+ // Initialize all of these to defaults
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ mInterp[i].setStartTime(BEAM_SPACING*i);
+ mInterp[i].setEndTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i);
+ mInterp[i].start();
+ mInterpFade[i].setStartTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i - 0.5f*NUM_POINTS*BEAM_SPACING);
+ mInterpFade[i].setEndTime(BEAM_SPACING*NUM_POINTS + BEAM_SPACING*i);
+ mInterpFade[i].setStartVal(1.f);
+ mInterpFade[i].setEndVal(0.f);
+ }
+
+ // Setup default timeouts and fade animations.
+ F32 fade_length;
+ fade_length = llmin(0.5f, mDuration);
+ mFadeInterp.setStartTime(mKillTime - fade_length);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(0.f);
+}
+
+LLHUDEffectBeam::~LLHUDEffectBeam()
+{
+}
+
+void LLHUDEffectBeam::packData(LLMessageSystem *mesgsys)
+{
+ if (!mSourceObject)
+ {
+ llwarns << "Missing source object!" << llendl;
+ }
+
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ // 16 + 24 + 1 = 41
+ U8 packed_data[41];
+ memset(packed_data, 0, 41);
+ if (mSourceObject)
+ {
+ htonmemcpy(packed_data, mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+
+ if (mTargetObject)
+ {
+ packed_data[16] = 1;
+ }
+ else
+ {
+ packed_data[16] = 0;
+ }
+
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[17]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[17]), mTargetPos.mdV, MVT_LLVector3d, 24);
+ }
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 41);
+}
+
+void LLHUDEffectBeam::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ llerrs << "Got beam!" << llendl;
+ BOOL use_target_object;
+ LLVector3d new_target;
+ U8 packed_data[41];
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 41)
+ {
+ llwarns << "Beam effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 41, blocknum);
+
+ htonmemcpy(source_id.mData, packed_data, MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp)
+ {
+ setSourceObject(objp);
+ }
+
+ use_target_object = packed_data[16];
+
+ if (use_target_object)
+ {
+ htonmemcpy(target_id.mData, &packed_data[17], MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(target_id);
+ if (objp)
+ {
+ setTargetObject(objp);
+ }
+ }
+ else
+ {
+ htonmemcpy(new_target.mdV, &(packed_data[17]), MVT_LLVector3d, 24);
+ setTargetPos(new_target);
+ }
+
+ // We've received an update for the effect, update the various timeouts
+ // and fade animations.
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+ F32 fade_length;
+ fade_length = llmin(0.5f, mDuration);
+ mFadeInterp.setStartTime(mKillTime - fade_length);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(0.f);
+}
+
+void LLHUDEffectBeam::setSourceObject(LLViewerObject *objp)
+{
+ if (objp->isDead())
+ {
+ llwarns << "HUDEffectBeam: Source object is dead!" << llendl;
+ mSourceObject = NULL;
+ return;
+ }
+
+ if (mSourceObject == objp)
+ {
+ return;
+ }
+
+ mSourceObject = objp;
+ if (mSourceObject)
+ {
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ if (mSourceObject->isAvatar())
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ LLVector3d hand_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ mInterp[i].setStartVal(hand_pos_global);
+ mInterp[i].start();
+ }
+ else
+ {
+ mInterp[i].setStartVal(mSourceObject->getPositionGlobal());
+ mInterp[i].start();
+ }
+ }
+ }
+}
+
+
+void LLHUDEffectBeam::setTargetObject(LLViewerObject *objp)
+{
+ if (mTargetObject->isDead())
+ {
+ llwarns << "HUDEffectBeam: Target object is dead!" << llendl;
+ }
+
+ mTargetObject = objp;
+}
+
+void LLHUDEffectBeam::setTargetPos(const LLVector3d &pos_global)
+{
+ mTargetPos = pos_global;
+ mTargetObject = NULL;
+}
+
+void LLHUDEffectBeam::render()
+{
+ if (!mSourceObject)
+ {
+ markDead();
+ return;
+ }
+ if (mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // Kill us if our time is over...
+ if (mKillTime < time)
+ {
+ markDead();
+ return;
+ }
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+
+ // Interpolate the global fade alpha
+ mFadeInterp.update(time);
+
+ if (mTargetObject.notNull() && mTargetObject->mDrawable.notNull())
+ {
+ // use viewer object position on freshly created objects
+ if (mTargetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = mTargetObject->getPositionGlobal();
+ }
+ // otherwise use drawable
+ else
+ {
+ mTargetPos = gAgent.getPosGlobalFromAgent(mTargetObject->mDrawable->getPositionAgent());
+ }
+ }
+
+
+ // Init the color of the particles
+ LLColor4U coloru = mColor;
+
+ /*
+ // This is disabled for now - DJS
+
+ // Fade the alpha
+ coloru.mV[3] = mFadeInterp.getCurVal()*mColor.mV[3];
+
+ // Draw a regular "beam" that connects the source and target
+
+ // First, figure out start and end positions relative to the camera
+ LLVector3 start_pos_agent;
+ if (mSourceObject->getPCode() == LL_PCODE_LEGACY_AVATAR)
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ LLVector3d hand_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ start_pos_agent = gAgent.getPosAgentFromGlobal(hand_pos_global);
+ }
+ else
+ {
+ start_pos_agent = mSourceObject->getPositionAgent();
+ }
+ LLVector3 start_pos_camera = (start_pos_agent - gAgent.getCameraPositionAgent());
+ LLVector3 target_pos_agent = gAgent.getPosAgentFromGlobal(mTargetPos);
+ LLVector3 target_pos_camera = target_pos_agent - gAgent.getCameraPositionAgent();
+
+ // Generate the right "up" vector which is perpendicular to the beam, make it 1/10 meter wide, going to a point.
+ LLVector3 camera_up = gCamera->getUpAxis();
+ LLVector3 camera_at = gCamera->getAtAxis();
+ LLVector3 up = target_pos_camera % start_pos_camera;
+ up.normVec();
+ up *= 0.1f;
+
+ // Draw the triangle for the beam.
+ LLVector3 vertex;
+ glColor4ubv(coloru.mV);
+ glBegin(GL_TRIANGLE_STRIP);
+ vertex = start_pos_agent + up;
+ glVertex3fv(vertex.mV);
+ vertex = start_pos_agent - up;
+ glVertex3fv(vertex.mV);
+ vertex = target_pos_agent;
+ glVertex3fv(vertex.mV);
+ glEnd();
+ */
+
+ // Draw the particles
+ S32 i;
+ for (i = 0; i < NUM_POINTS; i++)
+ {
+ mInterp[i].update(time);
+ if (!mInterp[i].isActive())
+ {
+ continue;
+ }
+ mInterpFade[i].update(time);
+
+ if (mInterp[i].isDone())
+ {
+ // Reinitialize the particle when the particle has finished its animation.
+ setupParticle(i);
+ }
+
+ F32 frac = mInterp[i].getCurFrac();
+ F32 scale = 0.025f + fabs(0.05f*sin(2.f*F_PI*(frac - time)));
+ scale *= mInterpFade[i].getCurVal();
+
+ LLVector3 pos_agent = gAgent.getPosAgentFromGlobal(mInterp[i].getCurVal());
+
+ F32 alpha = mFadeInterp.getCurVal()*mColor.mV[3];
+ alpha *= mInterpFade[i].getCurVal();
+ coloru.mV[3] = (U8)alpha;
+ glColor4ubv(coloru.mV);
+
+ glPushMatrix();
+ glTranslatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]);
+ glScalef(scale, scale, scale);
+ gSphere.render(0);
+ glPopMatrix();
+ }
+}
+
+void LLHUDEffectBeam::setupParticle(const S32 i)
+{
+ LLVector3d start_pos_global;
+ if (mSourceObject->getPCode() == LL_PCODE_LEGACY_AVATAR)
+ {
+ LLViewerObject *objp = mSourceObject;
+ LLVOAvatar *avatarp = (LLVOAvatar *)objp;
+ start_pos_global = gAgent.getPosGlobalFromAgent(avatarp->mWristLeftp->getWorldPosition());
+ }
+ else
+ {
+ start_pos_global = mSourceObject->getPositionGlobal();
+ }
+
+ // Generate a random offset for the target point.
+ const F32 SCALE = 0.5f;
+ F32 x, y, z;
+ x = frand(SCALE) - 0.5f*SCALE;
+ y = frand(SCALE) - 0.5f*SCALE;
+ z = frand(SCALE) - 0.5f*SCALE;
+
+ LLVector3d target_pos_global(mTargetPos);
+ target_pos_global += LLVector3d(x, y, z);
+
+ mInterp[i].setStartTime(mInterp[i].getEndTime());
+ mInterp[i].setEndTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS);
+ mInterp[i].setStartVal(start_pos_global);
+ mInterp[i].setEndVal(target_pos_global);
+ mInterp[i].start();
+
+
+ // Setup the interpolator that fades out the alpha.
+ mInterpFade[i].setStartTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS - 0.5f*NUM_POINTS*BEAM_SPACING);
+ mInterpFade[i].setEndTime(mInterp[i].getStartTime() + BEAM_SPACING*NUM_POINTS - 0.05f);
+ mInterpFade[i].start();
+}
diff --git a/indra/newview/llhudeffectbeam.h b/indra/newview/llhudeffectbeam.h
new file mode 100644
index 0000000000..9cb28ac1dc
--- /dev/null
+++ b/indra/newview/llhudeffectbeam.h
@@ -0,0 +1,51 @@
+/**
+ * @file llhudeffectbeam.h
+ * @brief LLHUDEffectBeam class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTBEAM_H
+#define LL_LLHUDEFFECTBEAM_H
+
+#include "llhudeffect.h"
+
+#include "llframetimer.h"
+
+#include "llinterp.h"
+
+class LLViewerObject;
+
+const S32 NUM_POINTS = 5;
+
+class LLHUDEffectBeam : public LLHUDEffect
+{
+public:
+ /*virtual*/ void setSourceObject(LLViewerObject *objp);
+
+ // A beam can have either a target object or a target position
+ void setTargetObject(LLViewerObject *objp);
+ void setTargetPos(const LLVector3d &target_pos_global);
+
+ friend class LLHUDObject;
+protected:
+ LLHUDEffectBeam(const U8 type);
+ ~LLHUDEffectBeam();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+private:
+ void setupParticle(const S32 i);
+
+
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLInterpLinear<LLVector3d> mInterp[NUM_POINTS];
+ LLInterpLinear<F32> mInterpFade[NUM_POINTS];
+ LLInterpLinear<F32> mFadeInterp;
+ LLVector3d mTargetPos;
+};
+
+#endif // LL_LLHUDEFFECTBEAM_H
diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp
new file mode 100644
index 0000000000..9f1b80e1ba
--- /dev/null
+++ b/indra/newview/llhudeffectlookat.cpp
@@ -0,0 +1,501 @@
+/**
+ * @file llhudeffectlookat.cpp
+ * @brief LLHUDEffectLookAt class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectlookat.h"
+
+#include "message.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "lldrawable.h"
+#include "llviewerobjectlist.h"
+#include "llsphere.h"
+#include "llselectmgr.h"
+#include "llglheaders.h"
+
+BOOL LLHUDEffectLookAt::sDebugLookAt = FALSE;
+
+// packet layout
+const S32 SOURCE_AVATAR = 0;
+const S32 TARGET_OBJECT = 16;
+const S32 TARGET_POS = 32;
+const S32 LOOKAT_TYPE = 56;
+const S32 PKT_SIZE = 57;
+
+// throttle
+const F32 MAX_SENDS_PER_SEC = 4.f;
+
+const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f;
+const F32 MIN_TARGET_OFFSET_SQUARED = 0.0001f;
+
+// timeouts
+// can't use actual F32_MAX, because we add this to the current frametime
+const F32 MAX_TIMEOUT = F32_MAX / 2.f;
+
+const F32 LOOKAT_TIMEOUTS[LOOKAT_NUM_TARGETS] =
+{
+ MAX_TIMEOUT, //LOOKAT_TARGET_NONE
+ 3.f, //LOOKAT_TARGET_IDLE
+ 4.f, //LOOKAT_TARGET_AUTO_LISTEN
+ 2.f, //LOOKAT_TARGET_FREELOOK
+ 4.f, //LOOKAT_TARGET_RESPOND
+ 1.f, //LOOKAT_TARGET_HOVER
+ MAX_TIMEOUT, //LOOKAT_TARGET_CONVERSATION
+ MAX_TIMEOUT, //LOOKAT_TARGET_SELECT
+ MAX_TIMEOUT, //LOOKAT_TARGET_FOCUS
+ MAX_TIMEOUT, //LOOKAT_TARGET_MOUSELOOK
+ 0.f, //LOOKAT_TARGET_CLEAR
+};
+
+const S32 LOOKAT_PRIORITIES[LOOKAT_NUM_TARGETS] =
+{
+ 0, //LOOKAT_TARGET_NONE
+ 1, //LOOKAT_TARGET_IDLE
+ 3, //LOOKAT_TARGET_AUTO_LISTEN
+ 2, //LOOKAT_TARGET_FREELOOK
+ 3, //LOOKAT_TARGET_RESPOND
+ 4, //LOOKAT_TARGET_HOVER
+ 5, //LOOKAT_TARGET_CONVERSATION
+ 6, //LOOKAT_TARGET_SELECT
+ 6, //LOOKAT_TARGET_FOCUS
+ 7, //LOOKAT_TARGET_MOUSELOOK
+ 8, //LOOKAT_TARGET_CLEAR
+};
+
+const char *LOOKAT_STRINGS[] =
+{
+ "None", //LOOKAT_TARGET_NONE
+ "Idle", //LOOKAT_TARGET_IDLE
+ "AutoListen", //LOOKAT_TARGET_AUTO_LISTEN
+ "FreeLook", //LOOKAT_TARGET_FREELOOK
+ "Respond", //LOOKAT_TARGET_RESPOND
+ "Hover", //LOOKAT_TARGET_HOVER
+ "Conversation", //LOOKAT_TARGET_CONVERSATION
+ "Select", //LOOKAT_TARGET_SELECT
+ "Focus", //LOOKAT_TARGET_FOCUS
+ "Mouselook", //LOOKAT_TARGET_MOUSELOOK
+ "Clear", //LOOKAT_TARGET_CLEAR
+};
+
+const LLColor3 LOOKAT_COLORS[LOOKAT_NUM_TARGETS] =
+{
+ LLColor3(0.3f, 0.3f, 0.3f), //LOOKAT_TARGET_NONE
+ LLColor3(0.5f, 0.5f, 0.5f), //LOOKAT_TARGET_IDLE
+ LLColor3(0.5f, 0.5f, 0.5f), //LOOKAT_TARGET_AUTO_LISTEN
+ LLColor3(0.5f, 0.5f, 0.9f), //LOOKAT_TARGET_FREELOOK
+ LLColor3(0.f, 0.f, 0.f), //LOOKAT_TARGET_RESPOND
+ LLColor3(0.5f, 0.9f, 0.5f), //LOOKAT_TARGET_HOVER
+ LLColor3(0.1f, 0.1f, 0.5f), //LOOKAT_TARGET_CONVERSATION
+ LLColor3(0.9f, 0.5f, 0.5f), //LOOKAT_TARGET_SELECT
+ LLColor3(0.9f, 0.5f, 0.9f), //LOOKAT_TARGET_FOCUS
+ LLColor3(0.9f, 0.9f, 0.5f), //LOOKAT_TARGET_MOUSELOOK
+ LLColor3(1.f, 1.f, 1.f), //LOOKAT_TARGET_CLEAR
+};
+
+//-----------------------------------------------------------------------------
+// LLHUDEffectLookAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectLookAt::LLHUDEffectLookAt(const U8 type) :
+ LLHUDEffect(type),
+ mKillTime(0.f),
+ mLastSendTime(0.f)
+{
+ clearLookAtTarget();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLHUDEffectLookAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectLookAt::~LLHUDEffectLookAt()
+{
+}
+
+//-----------------------------------------------------------------------------
+// packData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ U8 packed_data[PKT_SIZE];
+ memset(packed_data, 0, PKT_SIZE);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ // pack both target object and position
+ // position interpreted as offset if target object is non-null
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24);
+
+ U8 lookAtTypePacked = (U8)mTargetType;
+
+ htonmemcpy(&(packed_data[LOOKAT_TYPE]), &lookAtTypePacked, MVT_U8, 1);
+
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
+
+ mLastSendTime = mTimer.getElapsedTimeF32();
+}
+
+//-----------------------------------------------------------------------------
+// unpackData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ LLVector3d new_target;
+ U8 packed_data[PKT_SIZE];
+
+ LLUUID dataId;
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum);
+
+ if (!gAgent.mLookAt.isNull() && dataId == gAgent.mLookAt->getID())
+ {
+ return;
+ }
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != PKT_SIZE)
+ {
+ llwarns << "LookAt effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
+
+ htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp && objp->isAvatar())
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ //llwarns << "Could not find source avatar for lookat effect" << llendl;
+ return;
+ }
+
+ htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
+
+ objp = gObjectList.findObject(target_id);
+
+ htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24);
+
+ if (objp)
+ {
+ setTargetObjectAndOffset(objp, new_target);
+ }
+ else if (target_id.isNull())
+ {
+ setTargetPosGlobal(new_target);
+ }
+ else
+ {
+ //llwarns << "Could not find target object for lookat effect" << llendl;
+ }
+
+ U8 lookAtTypeUnpacked = 0;
+ htonmemcpy(&lookAtTypeUnpacked, &(packed_data[LOOKAT_TYPE]), MVT_U8, 1);
+ mTargetType = (ELookAtType)lookAtTypeUnpacked;
+
+ if (mTargetType == LOOKAT_TARGET_NONE)
+ {
+ clearLookAtTarget();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setTargetObjectAndOffset()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset)
+{
+ mTargetObject = objp;
+ mTargetOffsetGlobal = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setTargetPosGlobal()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::setTargetPosGlobal(const LLVector3d &target_pos_global)
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal = target_pos_global;
+}
+
+//-----------------------------------------------------------------------------
+// setLookAt()
+// called by agent logic to set look at behavior locally, and propagate to sim
+//-----------------------------------------------------------------------------
+BOOL LLHUDEffectLookAt::setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if (!mSourceObject)
+ {
+ return FALSE;
+ }
+
+ llassert(target_type < LOOKAT_NUM_TARGETS);
+
+ // must be same or higher priority than existing effect
+ if (LOOKAT_PRIORITIES[target_type] < LOOKAT_PRIORITIES[mTargetType])
+ {
+ return FALSE;
+ }
+
+ F32 current_time = mTimer.getElapsedTimeF32();
+
+ // type of lookat behavior or target object has changed
+ BOOL lookAtChanged = (target_type != mTargetType) ||
+ (object != mTargetObject);
+
+ // lookat position has moved a certain amount and we haven't just sent an update
+ lookAtChanged = lookAtChanged || (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) &&
+ ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC));
+
+ if (lookAtChanged)
+ {
+ mLastSentOffsetGlobal = position;
+ setDuration(LOOKAT_TIMEOUTS[target_type]);
+ setNeedsSendToSim(TRUE);
+ }
+
+ if (target_type == LOOKAT_TARGET_CLEAR)
+ {
+ clearLookAtTarget();
+ }
+ else
+ {
+ mTargetType = target_type;
+ mTargetObject = object;
+ if (object)
+ {
+ mTargetOffsetGlobal.setVec(position);
+ }
+ else
+ {
+ mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position);
+ }
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ update();
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// clearLookAtTarget()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::clearLookAtTarget()
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal.clearVec();
+ mTargetType = LOOKAT_TARGET_NONE;
+ if (mSourceObject.notNull())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->stopMotion(ANIM_AGENT_HEAD_ROT);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// markDead()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::markDead()
+{
+ if (mSourceObject.notNull())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("LookAtPoint");
+ }
+
+ mSourceObject = NULL;
+ clearLookAtTarget();
+ LLHUDEffect::markDead();
+}
+
+void LLHUDEffectLookAt::setSourceObject(LLViewerObject* objectp)
+{
+ // restrict source objects to avatars
+ if (objectp && objectp->isAvatar())
+ {
+ LLHUDEffect::setSourceObject(objectp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::render()
+{
+ if (sDebugLookAt && mSourceObject.notNull())
+ {
+ LLGLSNoTexture gls_no_texture;
+
+ LLVector3 target = mTargetPos + ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(target.mV[VX], target.mV[VY], target.mV[VZ]);
+ glScalef(0.3f, 0.3f, 0.3f);
+ glBegin(GL_LINES);
+ {
+ LLColor3 color = LOOKAT_COLORS[mTargetType];
+ glColor3f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE]);
+ glVertex3f(-1.f, 0.f, 0.f);
+ glVertex3f(1.f, 0.f, 0.f);
+
+ glVertex3f(0.f, -1.f, 0.f);
+ glVertex3f(0.f, 1.f, 0.f);
+
+ glVertex3f(0.f, 0.f, -1.f);
+ glVertex3f(0.f, 0.f, 1.f);
+ } glEnd();
+ glPopMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+void LLHUDEffectLookAt::update()
+{
+ // If the target object is dead, set the target object to NULL
+ if (!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ clearLookAtTarget();
+ }
+
+ // if source avatar is null or dead, mark self as dead and return
+ if (mSourceObject.isNull() || mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // clear out the effect if time is up
+ if (mKillTime != 0.f && time > mKillTime)
+ {
+ if (mTargetType != LOOKAT_TARGET_NONE)
+ {
+ clearLookAtTarget();
+ // look at timed out (only happens on own avatar), so tell everyone
+ setNeedsSendToSim(TRUE);
+ }
+ }
+
+ if (mTargetType != LOOKAT_TARGET_NONE)
+ {
+ calcTargetPosition();
+
+ LLMotion* head_motion = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->findMotion(ANIM_AGENT_HEAD_ROT);
+ if (!head_motion || head_motion->isStopped())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_HEAD_ROT);
+ }
+ }
+
+ if (sDebugLookAt)
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->addDebugText(LOOKAT_STRINGS[mTargetType]);
+ }
+}
+
+void LLHUDEffectLookAt::calcTargetPosition()
+{
+ LLViewerObject *targetObject = (LLViewerObject *)mTargetObject;
+ LLVector3 local_offset;
+
+ if (targetObject)
+ {
+ local_offset.setVec(mTargetOffsetGlobal);
+ }
+ else
+ {
+ local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal);
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+ LLVector3 head_position = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->mHeadp->getWorldPosition();
+
+ if (targetObject && targetObject->mDrawable.notNull())
+ {
+ LLQuaternion objRot;
+ if (targetObject->isAvatar())
+ {
+ LLVOAvatar *avatarp = (LLVOAvatar *)targetObject;
+
+ BOOL looking_at_self = ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->isSelf() && avatarp->isSelf();
+
+ // if selecting self, stare forward
+ if (looking_at_self && mTargetOffsetGlobal.magVecSquared() < MIN_TARGET_OFFSET_SQUARED)
+ {
+ //sets the lookat point in front of the avatar
+ mTargetOffsetGlobal.setVec(5.0, 0.0, 0.0);
+ }
+
+ mTargetPos = avatarp->mHeadp->getWorldPosition();
+ if (mTargetType == LOOKAT_TARGET_MOUSELOOK || mTargetType == LOOKAT_TARGET_FREELOOK)
+ {
+ // mouselook and freelook target offsets are absolute
+ objRot = LLQuaternion::DEFAULT;
+ }
+ else if (looking_at_self && gAgent.cameraCustomizeAvatar())
+ {
+ //FIXME: have animation overrides for lookat behavior and then we don't need to do this
+ objRot = avatarp->mPelvisp->getWorldRotation();
+ }
+ else
+ {
+ objRot = avatarp->mRoot.getWorldRotation();
+ }
+ }
+ else
+ {
+ if (targetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = targetObject->getPositionAgent();
+ objRot = targetObject->getWorldRotation();
+ }
+ else
+ {
+ mTargetPos = targetObject->getRenderPosition();
+ objRot = targetObject->getRenderRotation();
+ }
+ }
+
+ mTargetPos += (local_offset * objRot);
+ }
+ else
+ {
+ mTargetPos = local_offset;
+ }
+
+ mTargetPos -= head_position;
+
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->setAnimationData("LookAtPoint", (void *)&mTargetPos);
+}
diff --git a/indra/newview/llhudeffectlookat.h b/indra/newview/llhudeffectlookat.h
new file mode 100644
index 0000000000..1b50b7d566
--- /dev/null
+++ b/indra/newview/llhudeffectlookat.h
@@ -0,0 +1,76 @@
+/**
+ * @file llhudeffectlookat.h
+ * @brief LLHUDEffectLookAt class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTLOOKAT_H
+#define LL_LLHUDEFFECTLOOKAT_H
+
+#include "llhudeffect.h"
+#include "llskiplist.h"
+
+class LLViewerObject;
+class LLVOAvatar;
+
+typedef enum e_lookat_type
+{
+ LOOKAT_TARGET_NONE,
+ LOOKAT_TARGET_IDLE,
+ LOOKAT_TARGET_AUTO_LISTEN,
+ LOOKAT_TARGET_FREELOOK,
+ LOOKAT_TARGET_RESPOND,
+ LOOKAT_TARGET_HOVER,
+ LOOKAT_TARGET_CONVERSATION, // conversation mode deprecated
+ LOOKAT_TARGET_SELECT,
+ LOOKAT_TARGET_FOCUS,
+ LOOKAT_TARGET_MOUSELOOK,
+ LOOKAT_TARGET_CLEAR,
+ LOOKAT_NUM_TARGETS
+} ELookAtType;
+
+class LLHUDEffectLookAt : public LLHUDEffect
+{
+public:
+ friend class LLHUDObject;
+
+ /*virtual*/ void markDead();
+ /*virtual*/ void setSourceObject(LLViewerObject* objectp);
+
+ BOOL setLookAt(ELookAtType target_type, LLViewerObject *object, LLVector3 position);
+ void clearLookAtTarget();
+
+ ELookAtType getLookAtType() { return mTargetType; }
+ const LLVector3& getTargetPos() { return mTargetPos; }
+ const LLVector3d& getTargetOffset() { return mTargetOffsetGlobal; }
+ void calcTargetPosition();
+
+protected:
+ LLHUDEffectLookAt(const U8 type);
+ ~LLHUDEffectLookAt();
+
+ /*virtual*/ void update();
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+
+ // lookat behavior has either target position or target object with offset
+ void setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset);
+ void setTargetPosGlobal(const LLVector3d &target_pos_global);
+
+public:
+ static BOOL sDebugLookAt;
+
+private:
+ ELookAtType mTargetType;
+ LLVector3d mTargetOffsetGlobal;
+ LLVector3 mLastSentOffsetGlobal;
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLVector3 mTargetPos;
+ F32 mLastSendTime;
+};
+
+#endif // LL_LLHUDEFFECTLOOKAT_H
diff --git a/indra/newview/llhudeffectpointat.cpp b/indra/newview/llhudeffectpointat.cpp
new file mode 100644
index 0000000000..f72c5b06de
--- /dev/null
+++ b/indra/newview/llhudeffectpointat.cpp
@@ -0,0 +1,432 @@
+/**
+ * @file llhudeffectpointat.cpp
+ * @brief LLHUDEffectPointAt class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffectpointat.h"
+
+#include "llgl.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "message.h"
+
+// packet layout
+const S32 SOURCE_AVATAR = 0;
+const S32 TARGET_OBJECT = 16;
+const S32 TARGET_POS = 32;
+const S32 POINTAT_TYPE = 56;
+const S32 PKT_SIZE = 57;
+
+// throttle
+const F32 MAX_SENDS_PER_SEC = 4.f;
+
+const F32 MIN_DELTAPOS_FOR_UPDATE = 0.05f;
+
+// timeouts
+// can't use actual F32_MAX, because we add this to the current frametime
+const F32 MAX_TIMEOUT = F32_MAX / 4.f;
+
+const F32 POINTAT_TIMEOUTS[POINTAT_NUM_TARGETS] =
+{
+ MAX_TIMEOUT, //POINTAT_TARGET_NONE
+ MAX_TIMEOUT, //POINTAT_TARGET_SELECT
+ MAX_TIMEOUT, //POINTAT_TARGET_GRAB
+ 0.f, //POINTAT_TARGET_CLEAR
+};
+
+const S32 POINTAT_PRIORITIES[POINTAT_NUM_TARGETS] =
+{
+ 0, //POINTAT_TARGET_NONE
+ 1, //POINTAT_TARGET_SELECT
+ 2, //POINTAT_TARGET_GRAB
+ 3, //POINTAT_TARGET_CLEAR
+};
+
+// statics
+
+BOOL LLHUDEffectPointAt::sDebugPointAt;
+
+
+//-----------------------------------------------------------------------------
+// LLHUDEffectPointAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectPointAt::LLHUDEffectPointAt(const U8 type) :
+ LLHUDEffect(type),
+ mKillTime(0.f),
+ mLastSendTime(0.f)
+{
+ clearPointAtTarget();
+}
+
+//-----------------------------------------------------------------------------
+// ~LLHUDEffectPointAt()
+//-----------------------------------------------------------------------------
+LLHUDEffectPointAt::~LLHUDEffectPointAt()
+{
+}
+
+//-----------------------------------------------------------------------------
+// packData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::packData(LLMessageSystem *mesgsys)
+{
+ // Pack the default data
+ LLHUDEffect::packData(mesgsys);
+
+ // Pack the type-specific data. Uses a fun packed binary format. Whee!
+ U8 packed_data[PKT_SIZE];
+ memset(packed_data, 0, PKT_SIZE);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[SOURCE_AVATAR]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ // pack both target object and position
+ // position interpreted as offset if target object is non-null
+ if (mTargetObject)
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ else
+ {
+ htonmemcpy(&(packed_data[TARGET_OBJECT]), LLUUID::null.mData, MVT_LLUUID, 16);
+ }
+
+ htonmemcpy(&(packed_data[TARGET_POS]), mTargetOffsetGlobal.mdV, MVT_LLVector3d, 24);
+
+ U8 pointAtTypePacked = (U8)mTargetType;
+ htonmemcpy(&(packed_data[POINTAT_TYPE]), &pointAtTypePacked, MVT_U8, 1);
+
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, PKT_SIZE);
+
+ mLastSendTime = mTimer.getElapsedTimeF32();
+}
+
+//FIXME: avatar selection does a weird double take
+
+//-----------------------------------------------------------------------------
+// unpackData()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ LLVector3d new_target;
+ U8 packed_data[PKT_SIZE];
+
+ LLUUID dataId;
+ mesgsys->getUUIDFast(_PREHASH_Effect, _PREHASH_ID, dataId, blocknum);
+
+ // ignore messages from ourselves
+ if (!gAgent.mPointAt.isNull() && dataId == gAgent.mPointAt->getID())
+ {
+ return;
+ }
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID source_id;
+ LLUUID target_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != PKT_SIZE)
+ {
+ llwarns << "PointAt effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, PKT_SIZE, blocknum);
+
+ htonmemcpy(source_id.mData, &(packed_data[SOURCE_AVATAR]), MVT_LLUUID, 16);
+
+ LLViewerObject *objp = gObjectList.findObject(source_id);
+ if (objp && objp->isAvatar())
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ //llwarns << "Could not find source avatar for pointat effect" << llendl;
+ return;
+ }
+
+ htonmemcpy(target_id.mData, &(packed_data[TARGET_OBJECT]), MVT_LLUUID, 16);
+
+ objp = gObjectList.findObject(target_id);
+
+ htonmemcpy(new_target.mdV, &(packed_data[TARGET_POS]), MVT_LLVector3d, 24);
+
+ if (objp)
+ {
+ setTargetObjectAndOffset(objp, new_target);
+ }
+ else if (target_id.isNull())
+ {
+ setTargetPosGlobal(new_target);
+ }
+
+ U8 pointAtTypeUnpacked = 0;
+ htonmemcpy(&pointAtTypeUnpacked, &(packed_data[POINTAT_TYPE]), MVT_U8, 1);
+ mTargetType = (EPointAtType)pointAtTypeUnpacked;
+
+// mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+ update();
+}
+
+//-----------------------------------------------------------------------------
+// setTargetObjectAndOffset()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset)
+{
+ mTargetObject = objp;
+ mTargetOffsetGlobal = offset;
+}
+
+//-----------------------------------------------------------------------------
+// setTargetPosGlobal()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::setTargetPosGlobal(const LLVector3d &target_pos_global)
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal = target_pos_global;
+}
+
+//-----------------------------------------------------------------------------
+// setPointAt()
+// called by agent logic to set look at behavior locally, and propagate to sim
+//-----------------------------------------------------------------------------
+BOOL LLHUDEffectPointAt::setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position)
+{
+ if (!mSourceObject)
+ {
+ return FALSE;
+ }
+
+ llassert(target_type < POINTAT_NUM_TARGETS);
+
+ // must be same or higher priority than existing effect
+ if (POINTAT_PRIORITIES[target_type] < POINTAT_PRIORITIES[mTargetType])
+ {
+ return FALSE;
+ }
+
+ F32 current_time = mTimer.getElapsedTimeF32();
+
+ // type of pointat behavior or target object has changed
+ BOOL targetTypeChanged = (target_type != mTargetType) ||
+ (object != mTargetObject);
+
+ BOOL targetPosChanged = (dist_vec(position, mLastSentOffsetGlobal) > MIN_DELTAPOS_FOR_UPDATE) &&
+ ((current_time - mLastSendTime) > (1.f / MAX_SENDS_PER_SEC));
+
+ if (targetTypeChanged || targetPosChanged)
+ {
+ mLastSentOffsetGlobal = position;
+ setDuration(POINTAT_TIMEOUTS[target_type]);
+ setNeedsSendToSim(TRUE);
+// llinfos << "Sending pointat data" << llendl;
+ }
+
+ if (target_type == POINTAT_TARGET_CLEAR)
+ {
+ clearPointAtTarget();
+ }
+ else
+ {
+ mTargetType = target_type;
+ mTargetObject = object;
+ if (object)
+ {
+ mTargetOffsetGlobal.setVec(position);
+ }
+ else
+ {
+ mTargetOffsetGlobal = gAgent.getPosGlobalFromAgent(position);
+ }
+
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ //set up requisite animation data
+ update();
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// clearPointAtTarget()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::clearPointAtTarget()
+{
+ mTargetObject = NULL;
+ mTargetOffsetGlobal.clearVec();
+ mTargetType = POINTAT_TARGET_NONE;
+}
+
+//-----------------------------------------------------------------------------
+// markDead()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::markDead()
+{
+ if (!mSourceObject.isNull() && mSourceObject->isAvatar())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("PointAtPoint");
+ }
+
+ clearPointAtTarget();
+ LLHUDEffect::markDead();
+}
+
+void LLHUDEffectPointAt::setSourceObject(LLViewerObject* objectp)
+{
+ // restrict source objects to avatars
+ if (objectp && objectp->isAvatar())
+ {
+ LLHUDEffect::setSourceObject(objectp);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::render()
+{
+ update();
+ if (sDebugPointAt && mTargetType != POINTAT_TARGET_NONE)
+ {
+ LLGLSNoTexture gls_no_texture;
+
+ LLVector3 target = mTargetPos + mSourceObject->getRenderPosition();
+ glPushMatrix();
+ glTranslatef(target.mV[VX], target.mV[VY], target.mV[VZ]);
+ glScalef(0.3f, 0.3f, 0.3f);
+ glBegin(GL_LINES);
+ {
+ glColor3f(1.f, 0.f, 0.f);
+ glVertex3f(-1.f, 0.f, 0.f);
+ glVertex3f(1.f, 0.f, 0.f);
+
+ glVertex3f(0.f, -1.f, 0.f);
+ glVertex3f(0.f, 1.f, 0.f);
+
+ glVertex3f(0.f, 0.f, -1.f);
+ glVertex3f(0.f, 0.f, 1.f);
+ } glEnd();
+ glPopMatrix();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// update()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::update()
+{
+ // If the target object is dead, set the target object to NULL
+ if (!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ clearPointAtTarget();
+ }
+
+ if (mSourceObject.isNull() || mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+
+ // clear out the effect if time is up
+ if (mKillTime != 0.f && time > mKillTime)
+ {
+ mTargetType = POINTAT_TARGET_NONE;
+ }
+
+ if (mSourceObject->isAvatar())
+ {
+ if (mTargetType == POINTAT_TARGET_NONE)
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->removeAnimationData("PointAtPoint");
+ }
+ else
+ {
+ calcTargetPosition();
+
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->startMotion(ANIM_AGENT_EDITING);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcTargetPosition()
+//-----------------------------------------------------------------------------
+void LLHUDEffectPointAt::calcTargetPosition()
+{
+ LLViewerObject *targetObject = (LLViewerObject *)mTargetObject;
+ LLVector3 local_offset;
+
+ if (targetObject)
+ {
+ local_offset.setVec(mTargetOffsetGlobal);
+ }
+ else
+ {
+ local_offset = gAgent.getPosAgentFromGlobal(mTargetOffsetGlobal);
+ }
+
+ if (targetObject && targetObject->mDrawable.notNull())
+ {
+ LLQuaternion objRot;
+ if (targetObject->isAvatar())
+ {
+ LLVOAvatar *avatarp = (LLVOAvatar *)targetObject;
+ mTargetPos = avatarp->mHeadp->getWorldPosition();
+ objRot = avatarp->mPelvisp->getWorldRotation();
+ }
+ else
+ {
+ if (targetObject->mDrawable->getGeneration() == -1)
+ {
+ mTargetPos = targetObject->getPositionAgent();
+ objRot = targetObject->getWorldRotation();
+ }
+ else
+ {
+ mTargetPos = targetObject->getRenderPosition();
+ objRot = targetObject->getRenderRotation();
+ }
+ }
+
+ mTargetPos += (local_offset * objRot);
+ }
+ else
+ {
+ mTargetPos = local_offset;
+ }
+
+ mTargetPos -= mSourceObject->getRenderPosition();
+
+ if (mSourceObject->isAvatar())
+ {
+ ((LLVOAvatar*)(LLViewerObject*)mSourceObject)->setAnimationData("PointAtPoint", (void *)&mTargetPos);
+ }
+}
+
+const LLVector3d LLHUDEffectPointAt::getPointAtPosGlobal()
+{
+ LLVector3d global_pos;
+ global_pos.setVec(mTargetPos);
+ if (mSourceObject.notNull())
+ {
+ global_pos += mSourceObject->getPositionGlobal();
+ }
+
+ return global_pos;
+}
diff --git a/indra/newview/llhudeffectpointat.h b/indra/newview/llhudeffectpointat.h
new file mode 100644
index 0000000000..a4254501eb
--- /dev/null
+++ b/indra/newview/llhudeffectpointat.h
@@ -0,0 +1,65 @@
+/**
+ * @file llhudeffectpointat.h
+ * @brief LLHUDEffectPointAt class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTPOINTAT_H
+#define LL_LLHUDEFFECTPOINTAT_H
+
+#include "llhudeffect.h"
+
+class LLViewerObject;
+class LLVOAvatar;
+
+typedef enum e_pointat_type
+{
+ POINTAT_TARGET_NONE,
+ POINTAT_TARGET_SELECT,
+ POINTAT_TARGET_GRAB,
+ POINTAT_TARGET_CLEAR,
+ POINTAT_NUM_TARGETS
+} EPointAtType;
+
+class LLHUDEffectPointAt : public LLHUDEffect
+{
+public:
+ friend class LLHUDObject;
+
+ /*virtual*/ void markDead();
+ /*virtual*/ void setSourceObject(LLViewerObject* objectp);
+
+ BOOL setPointAt(EPointAtType target_type, LLViewerObject *object, LLVector3 position);
+ void clearPointAtTarget();
+
+ EPointAtType getPointAtType() { return mTargetType; }
+ const LLVector3& getPointAtPosAgent() { return mTargetPos; }
+ const LLVector3d getPointAtPosGlobal();
+protected:
+ LLHUDEffectPointAt(const U8 type);
+ ~LLHUDEffectPointAt();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+
+ // lookat behavior has either target position or target object with offset
+ void setTargetObjectAndOffset(LLViewerObject *objp, LLVector3d offset);
+ void setTargetPosGlobal(const LLVector3d &target_pos_global);
+ void calcTargetPosition();
+ void update();
+public:
+ static BOOL sDebugPointAt;
+private:
+ EPointAtType mTargetType;
+ LLVector3d mTargetOffsetGlobal;
+ LLVector3 mLastSentOffsetGlobal;
+ F32 mKillTime;
+ LLFrameTimer mTimer;
+ LLVector3 mTargetPos;
+ F32 mLastSendTime;
+};
+
+#endif // LL_LLHUDEFFECTPOINTAT_H
diff --git a/indra/newview/llhudeffecttrail.cpp b/indra/newview/llhudeffecttrail.cpp
new file mode 100644
index 0000000000..d76250d10a
--- /dev/null
+++ b/indra/newview/llhudeffecttrail.cpp
@@ -0,0 +1,271 @@
+/**
+ * @file llhudeffecttrail.cpp
+ * @brief LLHUDEffectSpiral class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudeffecttrail.h"
+
+#include "llviewercontrol.h"
+#include "llimagegl.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "llhudrender.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsim.h"
+#include "llviewerpartsource.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+
+const F32 PARTICLE_SPACING = 0.01f;
+const F32 MAX_SIZE = 0.025f;
+const F32 START_POS_MAG = 1.f;
+const F32 END_POS_MAG = 1.2f;
+
+
+LLHUDEffectSpiral::LLHUDEffectSpiral(const U8 type) : LLHUDEffect(type), mbInit(FALSE)
+{
+ mKillTime = 10.f;
+ mVMag = 1.f;
+ mVOffset = 0.f;
+ mInitialRadius = 1.f;
+ mFinalRadius = 1.f;
+ mSpinRate = 10.f;
+ mFlickerRate = 50.f;
+ mScaleBase = 0.1f;
+ mScaleVar = 0.f;
+
+ mFadeInterp.setStartTime(0.f);
+ mFadeInterp.setEndTime(mKillTime);
+ mFadeInterp.setStartVal(1.f);
+ mFadeInterp.setEndVal(1.f);
+}
+
+LLHUDEffectSpiral::~LLHUDEffectSpiral()
+{
+}
+
+void LLHUDEffectSpiral::markDead()
+{
+ if (mPartSourcep)
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ LLHUDObject::markDead();
+}
+
+void LLHUDEffectSpiral::packData(LLMessageSystem *mesgsys)
+{
+ if (!mSourceObject)
+ {
+ //llwarns << "Missing object in trail pack!" << llendl;
+ }
+ LLHUDEffect::packData(mesgsys);
+
+ U8 packed_data[56];
+ memset(packed_data, 0, 56);
+
+ if (mSourceObject)
+ {
+ htonmemcpy(packed_data, mSourceObject->mID.mData, MVT_LLUUID, 16);
+ }
+ if (mTargetObject)
+ {
+ htonmemcpy(packed_data + 16, mTargetObject->mID.mData, MVT_LLUUID, 16);
+ }
+ if (!mPositionGlobal.isExactlyZero())
+ {
+ htonmemcpy(packed_data + 32, mPositionGlobal.mdV, MVT_LLVector3d, 24);
+ }
+ mesgsys->addBinaryDataFast(_PREHASH_TypeData, packed_data, 56);
+}
+
+void LLHUDEffectSpiral::unpackData(LLMessageSystem *mesgsys, S32 blocknum)
+{
+ U8 packed_data[56];
+
+ LLHUDEffect::unpackData(mesgsys, blocknum);
+ LLUUID object_id, target_object_id;
+ S32 size = mesgsys->getSizeFast(_PREHASH_Effect, blocknum, _PREHASH_TypeData);
+ if (size != 56)
+ {
+ llwarns << "Spiral effect with bad size " << size << llendl;
+ return;
+ }
+ mesgsys->getBinaryDataFast(_PREHASH_Effect, _PREHASH_TypeData, packed_data, 56, blocknum);
+
+ htonmemcpy(object_id.mData, packed_data, MVT_LLUUID, 16);
+ htonmemcpy(target_object_id.mData, packed_data + 16, MVT_LLUUID, 16);
+ htonmemcpy(mPositionGlobal.mdV, packed_data + 32, MVT_LLVector3d, 24);
+
+ LLViewerObject *objp = NULL;
+
+ if (object_id.isNull())
+ {
+ setSourceObject(NULL);
+ }
+ else
+ {
+ LLViewerObject *objp = gObjectList.findObject(object_id);
+ if (objp)
+ {
+ setSourceObject(objp);
+ }
+ else
+ {
+ // We don't have this object, kill this effect
+ markDead();
+ return;
+ }
+ }
+
+ if (target_object_id.isNull())
+ {
+ setTargetObject(NULL);
+ }
+ else
+ {
+ objp = gObjectList.findObject(target_object_id);
+ if (objp)
+ {
+ setTargetObject(objp);
+ }
+ else
+ {
+ // We don't have this object, kill this effect
+ markDead();
+ return;
+ }
+ }
+
+ triggerLocal();
+}
+
+void LLHUDEffectSpiral::triggerLocal()
+{
+ mKillTime = mTimer.getElapsedTimeF32() + mDuration;
+
+ BOOL show_beam = gSavedSettings.getBOOL("ShowSelectionBeam");
+
+ LLColor4 color;
+ color.setVec(mColor);
+
+ if (!mPartSourcep)
+ {
+ if (!mTargetObject.isNull() && !mSourceObject.isNull())
+ {
+ if (show_beam)
+ {
+ LLViewerPartSourceBeam *psb = new LLViewerPartSourceBeam;
+ psb->setColor(color);
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(mTargetObject);
+ psb->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(psb);
+ mPartSourcep = psb;
+ }
+ }
+ else
+ {
+ if (!mSourceObject.isNull() && !mPositionGlobal.isExactlyZero())
+ {
+ if (show_beam)
+ {
+ LLViewerPartSourceBeam *psb = new LLViewerPartSourceBeam;
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(NULL);
+ psb->setColor(color);
+ psb->mLKGTargetPosGlobal = mPositionGlobal;
+ psb->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(psb);
+ mPartSourcep = psb;
+ }
+ }
+ else
+ {
+ LLVector3 pos;
+ if (mSourceObject)
+ {
+ pos = mSourceObject->getPositionAgent();
+ }
+ else
+ {
+ pos = gAgent.getPosAgentFromGlobal(mPositionGlobal);
+ }
+ LLViewerPartSourceSpiral *pss = new LLViewerPartSourceSpiral(pos);
+ if (!mSourceObject.isNull())
+ {
+ pss->setSourceObject(mSourceObject);
+ }
+ pss->setColor(color);
+ pss->setOwnerUUID(gAgent.getID());
+ gWorldPointer->mPartSim.addPartSource(pss);
+ mPartSourcep = pss;
+ }
+ }
+ }
+ else
+ {
+ LLViewerPartSource *ps = mPartSourcep;
+ if (mPartSourcep->getType() == LLViewerPartSource::LL_PART_SOURCE_BEAM)
+ {
+ LLViewerPartSourceBeam *psb = (LLViewerPartSourceBeam *)ps;
+ psb->setSourceObject(mSourceObject);
+ psb->setTargetObject(mTargetObject);
+ psb->setColor(color);
+ if (mTargetObject.isNull())
+ {
+ psb->mLKGTargetPosGlobal = mPositionGlobal;
+ }
+ }
+ else
+ {
+ LLViewerPartSourceSpiral *pss = (LLViewerPartSourceSpiral *)ps;
+ pss->setSourceObject(mSourceObject);
+ }
+ }
+
+ mbInit = TRUE;
+}
+
+void LLHUDEffectSpiral::setTargetObject(LLViewerObject *objp)
+{
+ if (objp == mTargetObject)
+ {
+ return;
+ }
+
+ mTargetObject = objp;
+}
+
+void LLHUDEffectSpiral::render()
+{
+ if (!mSourceObject.isNull() && mSourceObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ if(!mTargetObject.isNull() && mTargetObject->isDead())
+ {
+ markDead();
+ return;
+ }
+
+ F32 time = mTimer.getElapsedTimeF32();
+ if (mKillTime < time)
+ {
+ markDead();
+ return;
+ }
+}
diff --git a/indra/newview/llhudeffecttrail.h b/indra/newview/llhudeffecttrail.h
new file mode 100644
index 0000000000..f930739847
--- /dev/null
+++ b/indra/newview/llhudeffecttrail.h
@@ -0,0 +1,76 @@
+/**
+ * @file llhudeffecttrail.h
+ * @brief LLHUDEffectSpiral class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDEFFECTTRAIL_H
+#define LL_LLHUDEFFECTTRAIL_H
+
+#include "llhudeffect.h"
+
+#include "llframetimer.h"
+
+#include "llinterp.h"
+#include "llviewerpartsim.h"
+
+class LLViewerObject;
+
+const U32 NUM_TRAIL_POINTS = 40;
+
+
+class LLHUDEffectSpiral : public LLHUDEffect
+{
+public:
+ /*virtual*/ void markDead();
+ /*virtual*/ void setTargetObject(LLViewerObject* objectp);
+ void setVMag(F32 vmag) { mVMag = vmag; }
+ void setVOffset(F32 offset) { mVOffset = offset; }
+ void setInitialRadius(F32 radius) { mInitialRadius = radius; }
+ void setFinalRadius(F32 radius) { mFinalRadius = radius; }
+ void setScaleBase(F32 scale) { mScaleBase = scale; }
+ void setScaleVar(F32 scale) { mScaleVar = scale; }
+ void setSpinRate(F32 rate) { mSpinRate = rate; }
+ void setFlickerRate(F32 rate) { mFlickerRate = rate; }
+
+ // Start the effect playing locally.
+ void triggerLocal();
+
+ friend class LLHUDObject;
+protected:
+ LLHUDEffectSpiral(const U8 type);
+ ~LLHUDEffectSpiral();
+
+ /*virtual*/ void render();
+ /*virtual*/ void packData(LLMessageSystem *mesgsys);
+ /*virtual*/ void unpackData(LLMessageSystem *mesgsys, S32 blocknum);
+private:
+ /*
+ void setupParticle(const S32 i, const F32 start_time);
+
+
+ LLInterpExp<F32> mRadius[NUM_TRAIL_POINTS];
+ LLInterpLinear<LLVector3d> mDistance[NUM_TRAIL_POINTS];
+ LLInterpLinear<F32> mScale[NUM_TRAIL_POINTS];
+ F32 mOffset[NUM_TRAIL_POINTS];
+ */
+
+ BOOL mbInit;
+ LLPointer<LLViewerPartSource> mPartSourcep;
+
+ F32 mKillTime;
+ F32 mVMag;
+ F32 mVOffset;
+ F32 mInitialRadius;
+ F32 mFinalRadius;
+ F32 mSpinRate;
+ F32 mFlickerRate;
+ F32 mScaleBase;
+ F32 mScaleVar;
+ LLFrameTimer mTimer;
+ LLInterpLinear<F32> mFadeInterp;
+};
+
+#endif // LL_LLHUDEFFECTGLOW_H
diff --git a/indra/newview/llhudicon.cpp b/indra/newview/llhudicon.cpp
new file mode 100644
index 0000000000..f2ab2dab11
--- /dev/null
+++ b/indra/newview/llhudicon.cpp
@@ -0,0 +1,253 @@
+/**
+ * @file llhudicon.cpp
+ * @brief LLHUDIcon class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudicon.h"
+
+#include "llgl.h"
+
+#include "llviewerobject.h"
+#include "lldrawable.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+
+//-----------------------------------------------------------------------------
+// Local consts
+//-----------------------------------------------------------------------------
+const F32 ANIM_TIME = 0.4f;
+const F32 DIST_START_FADE = 15.f;
+const F32 DIST_END_FADE = 30.f;
+const F32 MAX_VISIBLE_TIME = 15.f;
+const F32 FADE_OUT_TIME = 1.f;
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+static F32 calc_bouncy_animation(F32 x)
+{
+ return -(cosf(x * F_PI * 2.5f - F_PI_BY_TWO))*(0.4f + x * -0.1f) + x * 1.3f;
+}
+
+
+//-----------------------------------------------------------------------------
+// static declarations
+//-----------------------------------------------------------------------------
+LLHUDIcon::icon_instance_t LLHUDIcon::sIconInstances;
+
+LLHUDIcon::LLHUDIcon(const U8 type) :
+ LLHUDObject(type),
+ mImagep(NULL),
+ mPickID(0),
+ mScale(0.1f)
+{
+ sIconInstances.push_back(this);
+}
+
+LLHUDIcon::~LLHUDIcon()
+{
+ mImagep = NULL;
+}
+
+void LLHUDIcon::renderIcon(BOOL for_select)
+{
+ LLGLSUIDefault texture_state;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLState no_texture(GL_TEXTURE_2D, for_select ? FALSE : TRUE);
+
+ if (mSourceObject.isNull() || mImagep.isNull())
+ {
+ markDead();
+ return;
+ }
+
+ LLVector3 obj_position = mSourceObject->getRenderPosition();
+
+ // put icon above object, and in front
+ // RN: don't use drawable radius, it's fricking HUGE
+ LLVector3 icon_relative_pos = (gCamera->getUpAxis() * ~mSourceObject->getRenderRotation());
+ icon_relative_pos.abs();
+
+ F32 distance_scale = llmin(mSourceObject->getScale().mV[VX] / icon_relative_pos.mV[VX],
+ mSourceObject->getScale().mV[VY] / icon_relative_pos.mV[VY],
+ mSourceObject->getScale().mV[VZ] / icon_relative_pos.mV[VZ]);
+ F32 up_distance = 0.5f * distance_scale;
+ LLVector3 icon_position = obj_position + (up_distance * gCamera->getUpAxis()) * 1.2f;
+
+ LLVector3 icon_to_cam = gCamera->getOrigin() - icon_position;
+ icon_to_cam.normVec();
+
+ icon_position += icon_to_cam * mSourceObject->mDrawable->getRadius() * 1.1f;
+
+ mDistance = dist_vec(icon_position, gCamera->getOrigin());
+
+ F32 alpha_factor = for_select ? 1.f : clamp_rescale(mDistance, DIST_START_FADE, DIST_END_FADE, 1.f, 0.f);
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ gCamera->getPixelVectors(icon_position, y_pixel_vec, x_pixel_vec);
+
+ F32 scale_factor = 1.f;
+ if (mAnimTimer.getElapsedTimeF32() < ANIM_TIME)
+ {
+ scale_factor = llmax(0.f, calc_bouncy_animation(mAnimTimer.getElapsedTimeF32() / ANIM_TIME));
+ }
+
+ F32 time_elapsed = mLifeTimer.getElapsedTimeF32();
+ if (time_elapsed > MAX_VISIBLE_TIME)
+ {
+ markDead();
+ return;
+ }
+
+ if (time_elapsed > MAX_VISIBLE_TIME - FADE_OUT_TIME)
+ {
+ alpha_factor *= clamp_rescale(time_elapsed, MAX_VISIBLE_TIME - FADE_OUT_TIME, MAX_VISIBLE_TIME, 1.f, 0.f);
+ }
+
+ F32 image_aspect = (F32)mImagep->mFullWidth / (F32)mImagep->mFullHeight;
+ LLVector3 x_scale = image_aspect * (F32)gViewerWindow->getWindowHeight() * mScale * scale_factor * x_pixel_vec;
+ LLVector3 y_scale = (F32)gViewerWindow->getWindowHeight() * mScale * scale_factor * y_pixel_vec;
+
+ LLVector3 lower_left = icon_position - (x_scale * 0.5f);
+ LLVector3 lower_right = icon_position + (x_scale * 0.5f);
+ LLVector3 upper_left = icon_position - (x_scale * 0.5f) + y_scale;
+ LLVector3 upper_right = icon_position + (x_scale * 0.5f) + y_scale;
+
+ if (for_select)
+ {
+ // set color to unique color id for picking
+ LLColor4U coloru((U8)(mPickID >> 16), (U8)(mPickID >> 8), (U8)mPickID);
+ glColor4ubv(coloru.mV);
+ }
+ else
+ {
+ LLColor4 icon_color = LLColor4::white;
+ icon_color.mV[VALPHA] = alpha_factor;
+ glColor4fv(icon_color.mV);
+ LLViewerImage::bindTexture(mImagep);
+ }
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2f(0.f, 1.f);
+ glVertex3fv(upper_left.mV);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3fv(lower_left.mV);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3fv(lower_right.mV);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3fv(upper_right.mV);
+ }
+ glEnd();
+}
+
+void LLHUDIcon::setImage(LLViewerImage* imagep)
+{
+ mImagep = imagep;
+ mImagep->setClamp(TRUE, TRUE);
+}
+
+void LLHUDIcon::setScale(F32 fraction_of_fov)
+{
+ mScale = fraction_of_fov;
+}
+
+void LLHUDIcon::markDead()
+{
+ if (mSourceObject)
+ {
+ mSourceObject->clearIcon();
+ }
+ LLHUDObject::markDead();
+}
+
+void LLHUDIcon::render()
+{
+ renderIcon(FALSE);
+}
+
+void LLHUDIcon::renderForSelect()
+{
+ renderIcon(TRUE);
+}
+
+
+//static
+S32 LLHUDIcon::generatePickIDs(S32 start_id, S32 step_size)
+{
+ S32 cur_id = start_id;
+ icon_instance_t::iterator icon_it;
+
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ (*icon_it)->mPickID = cur_id;
+ cur_id += step_size;
+ }
+
+ return cur_id;
+}
+
+//static
+LLHUDIcon* LLHUDIcon::handlePick(S32 pick_id)
+{
+ icon_instance_t::iterator icon_it;
+
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ if (pick_id == (*icon_it)->mPickID)
+ {
+ return *icon_it;
+ }
+ }
+
+ return NULL;
+}
+
+ //static
+void LLHUDIcon::updateAll()
+{
+ cleanupDeadIcons();
+}
+
+//static
+BOOL LLHUDIcon::iconsNearby()
+{
+ return !sIconInstances.empty();
+}
+
+//static
+void LLHUDIcon::cleanupDeadIcons()
+{
+ icon_instance_t::iterator icon_it;
+
+ icon_instance_t icons_to_erase;
+ for(icon_it = sIconInstances.begin(); icon_it != sIconInstances.end(); ++icon_it)
+ {
+ if ((*icon_it)->mDead)
+ {
+ icons_to_erase.push_back(*icon_it);
+ }
+ }
+
+ for(icon_it = icons_to_erase.begin(); icon_it != icons_to_erase.end(); ++icon_it)
+ {
+ icon_instance_t::iterator found_it = std::find(sIconInstances.begin(), sIconInstances.end(), *icon_it);
+ if (found_it != sIconInstances.end())
+ {
+ sIconInstances.erase(found_it);
+ }
+ }
+}
+
+//static
+S32 LLHUDIcon::getNumInstances()
+{
+ return (S32)sIconInstances.size();
+}
diff --git a/indra/newview/llhudicon.h b/indra/newview/llhudicon.h
new file mode 100644
index 0000000000..9835ff455b
--- /dev/null
+++ b/indra/newview/llhudicon.h
@@ -0,0 +1,72 @@
+/**
+ * @file llhudicon.h
+ * @brief LLHUDIcon class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDICON_H
+#define LL_LLHUDICON_H
+
+#include "llmemory.h"
+#include "lldarrayptr.h"
+
+#include "llhudobject.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "llrect.h"
+#include "llframetimer.h"
+#include "llfontgl.h"
+#include <set>
+#include <vector>
+#include "lldarray.h"
+
+// Renders a 2D icon billboard floating at the location specified.
+class LLDrawable;
+class LLViewerObject;
+
+class LLHUDIcon : public LLHUDObject
+{
+friend class LLHUDObject;
+
+public:
+ /*virtual*/ void render();
+ /*virtual*/ void renderForSelect();
+ /*virtual*/ void markDead();
+ /*virtual*/ F32 getDistance() const { return mDistance; }
+
+ void setImage(LLViewerImage* imagep);
+ void setScale(F32 fraction_of_fov);
+
+ void restartLifeTimer() { mLifeTimer.reset(); }
+
+ static S32 generatePickIDs(S32 start_id, S32 step_size);
+ static LLHUDIcon* handlePick(S32 pick_id);
+
+ static void updateAll();
+ static void cleanupDeadIcons();
+ static S32 getNumInstances();
+
+ static BOOL iconsNearby();
+
+protected:
+ LLHUDIcon(const U8 type);
+ ~LLHUDIcon();
+
+ void renderIcon(BOOL for_select); // common render code
+
+private:
+ LLPointer<LLViewerImage> mImagep;
+ LLFrameTimer mAnimTimer;
+ LLFrameTimer mLifeTimer;
+ F32 mDistance;
+ S32 mPickID;
+ F32 mScale;
+
+ typedef std::vector<LLPointer<LLHUDIcon> > icon_instance_t;
+ static icon_instance_t sIconInstances;
+};
+
+#endif // LL_LLHUDICON_H
diff --git a/indra/newview/llhudmanager.cpp b/indra/newview/llhudmanager.cpp
new file mode 100644
index 0000000000..40e140253e
--- /dev/null
+++ b/indra/newview/llhudmanager.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llhudmanager.cpp
+ * @brief LLHUDManager class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudmanager.h"
+
+#include "message.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "llhudconnector.h"
+#include "llhudeffect.h"
+#include "pipeline.h"
+#include "llviewercontrol.h"
+#include "llviewerobjectlist.h"
+
+LLHUDManager *gHUDManager = NULL;
+
+extern BOOL gNoRender;
+
+// These are loaded from saved settings.
+LLColor4 LLHUDManager::sParentColor;
+LLColor4 LLHUDManager::sChildColor;
+
+LLHUDManager::LLHUDManager()
+{
+ mShowPhysical = FALSE;
+
+ LLHUDManager::sParentColor = gColors.getColor("FocusColor");
+ // rdw commented out since it's not used. Also removed from colors_base.xml
+ //LLHUDManager::sChildColor = gColors.getColor("FocusSecondaryColor");
+}
+
+LLHUDManager::~LLHUDManager()
+{
+ mHUDJoints.reset();
+ mHUDSelectedJoints.reset();
+ mHUDEffects.reset();
+}
+
+
+void LLHUDManager::toggleShowPhysical(const BOOL show_physical)
+{
+ if (show_physical == mShowPhysical)
+ {
+ return;
+ }
+
+ mShowPhysical = show_physical;
+ if (show_physical)
+ {
+ S32 i;
+ for (i = 0; i < gObjectList.getNumObjects(); i++)
+ {
+ LLViewerObject *vobjp = gObjectList.getObject(i);
+
+ if (vobjp && vobjp->isJointChild() && vobjp->getParent())
+ {
+ LLHUDConnector *connectorp = (LLHUDConnector *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_CONNECTOR);
+ connectorp->setTargets(vobjp, (LLViewerObject *)vobjp->getParent());
+ connectorp->setColors(LLColor4(1.f, 1.f, 1.f, 1.f), sChildColor, sParentColor);
+ EHavokJointType joint_type = vobjp->getJointType();
+ if (HJT_HINGE == joint_type)
+ {
+ connectorp->setLabel("Hinge");
+ }
+ else if (HJT_POINT == joint_type)
+ {
+ connectorp->setLabel("P2P");
+ }
+#if 0
+ else if (HJT_LPOINT == joint_type)
+ {
+ connectorp->setLabel("LP2P");
+ }
+#endif
+#if 0
+ else if (HJT_WHEEL == joint_type)
+ {
+ connectorp->setLabel("Wheel");
+ }
+#endif
+ mHUDJoints.put(connectorp);
+ }
+ }
+ }
+ else
+ {
+ mHUDJoints.reset();
+ }
+}
+
+void LLHUDManager::showJoints(LLDynamicArray < LLViewerObject* > *object_list)
+{
+ for (S32 i=0; i<object_list->count(); i++)
+ {
+ LLViewerObject *vobjp = object_list->get(i);
+ if (vobjp && vobjp->isJointChild() && vobjp->getParent())
+ {
+ LLHUDConnector *connectorp = (LLHUDConnector *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_CONNECTOR);
+ connectorp->setTargets(vobjp, (LLViewerObject *)vobjp->getParent());
+ connectorp->setColors(LLColor4(1.f, 1.f, 1.f, 1.f), sChildColor, sParentColor);
+
+ EHavokJointType joint_type = vobjp->getJointType();
+ if (HJT_HINGE == joint_type)
+ {
+ connectorp->setLabel("Hinge");
+ }
+ else if (HJT_POINT == joint_type)
+ {
+ connectorp->setLabel("P2P");
+ }
+#if 0
+ else if (HJT_LPOINT == joint_type)
+ {
+ connectorp->setLabel("LP2P");
+ }
+#endif
+#if 0
+ else if (HJT_WHEEL == joint_type)
+ {
+ connectorp->setLabel("Wheel");
+ }
+#endif
+ mHUDSelectedJoints.put(connectorp);
+ }
+ }
+}
+
+void LLHUDManager::clearJoints()
+{
+ mHUDSelectedJoints.reset();
+}
+
+BOOL LLHUDManager::getShowPhysical() const
+{
+ return mShowPhysical;
+}
+
+void LLHUDManager::updateEffects()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_HUD_EFFECTS);
+ S32 i;
+ for (i = 0; i < mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *hep = mHUDEffects[i];
+ if (hep->isDead())
+ {
+ continue;
+ }
+ hep->update();
+ }
+}
+
+void LLHUDManager::sendEffects()
+{
+ S32 i;
+ for (i = 0; i < mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *hep = mHUDEffects[i];
+ if (hep->isDead())
+ {
+ llwarns << "Trying to send dead effect!" << llendl;
+ continue;
+ }
+ if (hep->mType <= LLHUDObject::LL_HUD_CONNECTOR)
+ {
+ llwarns << "Trying to send effect of type " << hep->mType << " which isn't really an effect and shouldn't be in this list!" << llendl;
+ continue;
+ }
+ if (hep->getNeedsSendToSim() && hep->getOriginatedHere())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_ViewerEffect);
+ gMessageSystem->nextBlockFast(_PREHASH_Effect);
+ hep->packData(gMessageSystem);
+ hep->setNeedsSendToSim(FALSE);
+ gAgent.sendMessage();
+ }
+ }
+}
+
+void LLHUDManager::cleanupEffects()
+{
+ S32 i = 0;
+
+ while (i < mHUDEffects.count())
+ {
+ if (mHUDEffects[i]->isDead())
+ {
+ mHUDEffects.remove(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+}
+
+LLHUDEffect *LLHUDManager::createViewerEffect(const U8 type, BOOL send_to_sim, BOOL originated_here)
+{
+ // Should assert that this is actually an LLHUDEffect
+ LLHUDEffect *hep = (LLHUDEffect *)LLHUDObject::addHUDObject(type);
+ if (!hep)
+ {
+ return NULL;
+ }
+
+ LLUUID tmp;
+ tmp.generate();
+ hep->setID(tmp);
+ hep->setNeedsSendToSim(send_to_sim);
+ hep->setOriginatedHere(originated_here);
+
+ mHUDEffects.put(hep);
+ return hep;
+}
+
+
+//static
+void LLHUDManager::processViewerEffect(LLMessageSystem *mesgsys, void **user_data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (!gHUDManager)
+ {
+ llwarns << "No gHUDManager!" << llendl;
+ return;
+ }
+
+ LLHUDEffect *effectp = NULL;
+ LLUUID effect_id;
+ U8 effect_type = 0;
+ S32 number_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_Effect);
+ S32 k;
+
+ for (k = 0; k < number_blocks; k++)
+ {
+ effectp = NULL;
+ LLHUDEffect::getIDType(mesgsys, k, effect_id, effect_type);
+
+ S32 i;
+ for (i = 0; i < gHUDManager->mHUDEffects.count(); i++)
+ {
+ LLHUDEffect *cur_effectp = gHUDManager->mHUDEffects[i];
+ if (!cur_effectp)
+ {
+ llwarns << "Null effect in effect manager, skipping" << llendl;
+ gHUDManager->mHUDEffects.remove(i);
+ i--;
+ continue;
+ }
+ if (cur_effectp->isDead())
+ {
+ // llwarns << "Dead effect in effect manager, removing" << llendl;
+ gHUDManager->mHUDEffects.remove(i);
+ i--;
+ continue;
+ }
+ if (cur_effectp->getID() == effect_id)
+ {
+ if (cur_effectp->getType() != effect_type)
+ {
+ llwarns << "Viewer effect update doesn't match old type!" << llendl;
+ }
+ effectp = cur_effectp;
+ break;
+ }
+ }
+
+ if (effect_type)
+ {
+ if (!effectp)
+ {
+ effectp = gHUDManager->createViewerEffect(effect_type, FALSE, FALSE);
+ }
+
+ if (effectp)
+ {
+ effectp->unpackData(mesgsys, k);
+ }
+ }
+ else
+ {
+ llwarns << "Received viewer effect of type " << effect_type << " which isn't really an effect!" << llendl;
+ }
+ }
+
+ //llinfos << "Got viewer effect: " << effect_id << llendl;
+}
+
diff --git a/indra/newview/llhudmanager.h b/indra/newview/llhudmanager.h
new file mode 100644
index 0000000000..4cb22a2477
--- /dev/null
+++ b/indra/newview/llhudmanager.h
@@ -0,0 +1,60 @@
+/**
+ * @file llhudmanager.h
+ * @brief LLHUDManager class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDMANAGER_H
+#define LL_LLHUDMANAGER_H
+
+// Responsible for managing all HUD elements.
+
+#include "llhudobject.h"
+#include "lldarray.h"
+#include "llanimalcontrols.h"
+#include "lllocalanimationobject.h"
+#include "llcape.h"
+
+class LLViewerObject;
+class LLHUDEffect;
+//Ventrella 9/16/05
+class LLHUDAnimalControls;
+// End Ventrella
+class LLMessageSystem;
+
+class LLHUDManager
+{
+public:
+ LLHUDManager();
+ ~LLHUDManager();
+
+ void toggleShowPhysical(const BOOL show_physical);
+ void showJoints(LLDynamicArray < LLViewerObject* > *object_list);
+ void clearJoints();
+ BOOL getShowPhysical() const;
+
+ LLHUDEffect *createViewerEffect(const U8 type, BOOL send_to_sim = TRUE, BOOL originated_here = TRUE);
+
+ void updateEffects();
+ void sendEffects();
+ void cleanupEffects();
+
+ static void processViewerEffect(LLMessageSystem *mesgsys, void **user_data);
+
+ static LLColor4 sParentColor;
+ static LLColor4 sChildColor;
+
+protected:
+ LLDynamicArrayPtr<LLPointer<LLHUDObject> > mHUDJoints;
+ LLDynamicArrayPtr<LLPointer<LLHUDObject> > mHUDSelectedJoints;
+ LLDynamicArrayPtr<LLPointer<LLHUDEffect> > mHUDEffects;
+
+ // ALT held down this frame?
+ BOOL mShowPhysical;
+};
+
+extern LLHUDManager *gHUDManager;
+
+#endif // LL_LLHUDMANAGER_H
diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp
new file mode 100644
index 0000000000..403e6c6dff
--- /dev/null
+++ b/indra/newview/llhudobject.cpp
@@ -0,0 +1,277 @@
+/**
+ * @file llhudobject.cpp
+ * @brief LLHUDObject class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// llhudobject.cpp
+// Copyright 2002, Linden Research, Inc.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudobject.h"
+#include "llviewerobject.h"
+
+#include "llhudtext.h"
+#include "llhudicon.h"
+#include "llhudconnector.h"
+#include "llhudeffectbeam.h"
+#include "llhudeffecttrail.h"
+#include "llhudeffectlookat.h"
+
+//Ventrella
+#include "llanimalcontrols.h"
+#include "lllocalanimationobject.h"
+#include "llcape.h"
+// End Ventrella
+
+#include "llagent.h"
+
+// statics
+std::list<LLPointer<LLHUDObject> > LLHUDObject::sHUDObjects;
+
+struct hud_object_further_away
+{
+ bool operator()(const LLPointer<LLHUDObject>& lhs, const LLPointer<LLHUDObject>& rhs) const;
+};
+
+
+bool hud_object_further_away::operator()(const LLPointer<LLHUDObject>& lhs, const LLPointer<LLHUDObject>& rhs) const
+{
+ return (lhs->getDistance() > rhs->getDistance()) ? true : false;
+}
+
+
+LLHUDObject::LLHUDObject(const U8 type) :
+ mPositionGlobal(),
+ mSourceObject(NULL),
+ mTargetObject(NULL)
+{
+ mVisible = TRUE;
+ mType = type;
+ mDead = FALSE;
+ mOnHUDAttachment = FALSE;
+}
+
+LLHUDObject::~LLHUDObject()
+{
+}
+
+void LLHUDObject::markDead()
+{
+ mVisible = FALSE;
+ mDead = TRUE;
+ mSourceObject = NULL;
+ mTargetObject = NULL;
+}
+
+F32 LLHUDObject::getDistance() const
+{
+ return 0.f;
+}
+
+void LLHUDObject::setSourceObject(LLViewerObject* objectp)
+{
+ if (objectp == mSourceObject)
+ {
+ return;
+ }
+
+ mSourceObject = objectp;
+}
+
+void LLHUDObject::setTargetObject(LLViewerObject* objectp)
+{
+ if (objectp == mTargetObject)
+ {
+ return;
+ }
+
+ mTargetObject = objectp;
+}
+
+void LLHUDObject::setPositionGlobal(const LLVector3d &position_global)
+{
+ mPositionGlobal = position_global;
+}
+
+void LLHUDObject::setPositionAgent(const LLVector3 &position_agent)
+{
+ mPositionGlobal = gAgent.getPosGlobalFromAgent(position_agent);
+}
+
+// static
+void LLHUDObject::cleanupHUDObjects()
+{
+ LLHUDIcon::cleanupDeadIcons();
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); ++object_it)
+ {
+ (*object_it)->markDead();
+ if ((*object_it)->getNumRefs() > 1)
+ {
+ llinfos << "LLHUDObject " << (LLHUDObject *)(*object_it) << " has " << (*object_it)->getNumRefs() << " refs!" << llendl;
+ }
+ }
+ sHUDObjects.clear();
+}
+
+// static
+LLHUDObject *LLHUDObject::addHUDObject(const U8 type)
+{
+ LLHUDObject *hud_objectp = NULL;
+
+ switch (type)
+ {
+ case LL_HUD_TEXT:
+ hud_objectp = new LLHUDText(type);
+ break;
+ case LL_HUD_ICON:
+ hud_objectp = new LLHUDIcon(type);
+ break;
+ case LL_HUD_CONNECTOR:
+ hud_objectp = new LLHUDConnector(type);
+ break;
+ case LL_HUD_EFFECT_BEAM:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.7f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.2f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.05f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_GLOW:
+ llerrs << "Glow not implemented!" << llendl;
+ break;
+ case LL_HUD_EFFECT_POINT:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_SPHERE:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(20.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(0.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_SPIRAL:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(-2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(0.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(10.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(20.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.02f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_EDIT:
+ hud_objectp = new LLHUDEffectSpiral(type);
+ ((LLHUDEffectSpiral *)hud_objectp)->setDuration(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVMag(2.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setVOffset(-1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setInitialRadius(1.5f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFinalRadius(1.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setSpinRate(4.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setFlickerRate(200.f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleBase(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setScaleVar(0.1f);
+ ((LLHUDEffectSpiral *)hud_objectp)->setColor(LLColor4U(255, 255, 255, 255));
+ break;
+ case LL_HUD_EFFECT_LOOKAT:
+ hud_objectp = new LLHUDEffectLookAt(type);
+ break;
+ case LL_HUD_EFFECT_POINTAT:
+ hud_objectp = new LLHUDEffectPointAt(type);
+ break;
+ default:
+ llwarns << "Unknown type of hud object:" << (U32) type << llendl;
+ }
+
+ if (hud_objectp)
+ {
+ sHUDObjects.push_back(hud_objectp);
+ }
+ return hud_objectp;
+}
+
+// static
+void LLHUDObject::updateAll()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_HUD_UPDATE);
+ LLHUDText::updateAll();
+ LLHUDIcon::updateAll();
+ sortObjects();
+}
+
+// static
+void LLHUDObject::renderAll()
+{
+ LLHUDObject *hud_objp;
+
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); )
+ {
+ hud_object_list_t::iterator cur_it = object_it++;
+ hud_objp = (*cur_it);
+ if (hud_objp->getNumRefs() == 1)
+ {
+ sHUDObjects.erase(cur_it);
+ }
+ else if (hud_objp->isVisible())
+ {
+ hud_objp->render();
+ }
+ }
+}
+
+// static
+void LLHUDObject::renderAllForSelect()
+{
+ LLHUDObject *hud_objp;
+
+ hud_object_list_t::iterator object_it;
+ for (object_it = sHUDObjects.begin(); object_it != sHUDObjects.end(); )
+ {
+ hud_object_list_t::iterator cur_it = object_it++;
+ hud_objp = (*cur_it);
+ if (hud_objp->getNumRefs() == 1)
+ {
+ sHUDObjects.erase(cur_it);
+ }
+ else if (hud_objp->isVisible())
+ {
+ hud_objp->renderForSelect();
+ }
+ }
+}
+
+// static
+void LLHUDObject::sortObjects()
+{
+ sHUDObjects.sort(hud_object_further_away());
+}
diff --git a/indra/newview/llhudobject.h b/indra/newview/llhudobject.h
new file mode 100644
index 0000000000..1148dd6521
--- /dev/null
+++ b/indra/newview/llhudobject.h
@@ -0,0 +1,96 @@
+/**
+ * @file llhudobject.h
+ * @brief LLHUDObject class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDOBJECT_H
+#define LL_LLHUDOBJECT_H
+
+/**
+ * Base class and manager for in-world 2.5D non-interactive objects
+ */
+
+#include "llmemory.h"
+
+#include "v4color.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "linked_lists.h"
+#include "lldrawpool.h"
+#include <list>
+
+class LLViewerCamera;
+class LLFontGL;
+class LLFace;
+class LLViewerObject;
+
+class LLHUDObject : public LLRefCount
+{
+public:
+ virtual void markDead();
+ virtual F32 getDistance() const;
+ virtual void setSourceObject(LLViewerObject* objectp);
+ virtual void setTargetObject(LLViewerObject* objectp);
+ virtual LLViewerObject* getSourceObject() { return mSourceObject; }
+ virtual LLViewerObject* getTargetObject() { return mTargetObject; }
+
+ void setPositionGlobal(const LLVector3d &position_global);
+ void setPositionAgent(const LLVector3 &position_agent);
+
+ BOOL isVisible() const { return mVisible; }
+
+ U8 getType() const { return mType; }
+
+ static LLHUDObject *addHUDObject(const U8 type);
+ static void updateAll();
+ static void renderAll();
+ static void renderAllForSelect();
+
+ static void cleanupHUDObjects();
+
+ enum
+ {
+ LL_HUD_TEXT,
+ LL_HUD_ICON,
+ LL_HUD_CONNECTOR,
+ LL_HUD_FLEXIBLE_OBJECT, // Ventrella
+ LL_HUD_ANIMAL_CONTROLS, // Ventrella
+ LL_HUD_LOCAL_ANIMATION_OBJECT, // Ventrella
+ LL_HUD_CLOTH, // Ventrella
+ LL_HUD_EFFECT_BEAM,
+ LL_HUD_EFFECT_GLOW,
+ LL_HUD_EFFECT_POINT,
+ LL_HUD_EFFECT_TRAIL,
+ LL_HUD_EFFECT_SPHERE,
+ LL_HUD_EFFECT_SPIRAL,
+ LL_HUD_EFFECT_EDIT,
+ LL_HUD_EFFECT_LOOKAT,
+ LL_HUD_EFFECT_POINTAT
+ };
+protected:
+ static void sortObjects();
+
+ LLHUDObject(const U8 type);
+ virtual ~LLHUDObject();
+
+ virtual void render() = 0;
+ virtual void renderForSelect() {};
+
+protected:
+ U8 mType;
+ BOOL mDead;
+ BOOL mVisible;
+ LLVector3d mPositionGlobal;
+ BOOL mOnHUDAttachment;
+ LLPointer<LLViewerObject> mSourceObject;
+ LLPointer<LLViewerObject> mTargetObject;
+
+private:
+ typedef std::list<LLPointer<LLHUDObject> > hud_object_list_t;
+ static hud_object_list_t sHUDObjects;
+};
+
+#endif // LL_LLHUDOBJECT_H
diff --git a/indra/newview/llhudrender.cpp b/indra/newview/llhudrender.cpp
new file mode 100644
index 0000000000..9b74b3963b
--- /dev/null
+++ b/indra/newview/llhudrender.cpp
@@ -0,0 +1,113 @@
+/**
+ * @file llhudrender.cpp
+ * @brief LLHUDRender class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudrender.h"
+
+#include "llgl.h"
+#include "llviewercamera.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llfontgl.h"
+#include "llimagegl.h"
+#include "llglheaders.h"
+#include "llviewerwindow.h"
+
+void hud_render_utf8text(const std::string &str, const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset, const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic)
+{
+ LLWString wstr(utf8str_to_wstring(str));
+ hud_render_text(wstr, pos_agent, font, style, x_offset, y_offset, color, orthographic);
+}
+
+void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset, const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic)
+{
+ // Do cheap plane culling
+ LLVector3 dir_vec = pos_agent - gCamera->getOrigin();
+ dir_vec /= dir_vec.magVec();
+
+ if (wstr.empty() || (!orthographic && dir_vec * gCamera->getAtAxis() <= 0.f))
+ {
+ return;
+ }
+
+ LLVector3 right_axis;
+ LLVector3 up_axis;
+ if (orthographic)
+ {
+ right_axis.setVec(0.f, -1.f / gViewerWindow->getWindowHeight(), 0.f);
+ up_axis.setVec(0.f, 0.f, 1.f / gViewerWindow->getWindowHeight());
+ }
+ else
+ {
+ gCamera->getPixelVectors(pos_agent, up_axis, right_axis);
+ }
+ LLCoordFrame render_frame = *gCamera;
+ LLQuaternion rot;
+ if (!orthographic)
+ {
+ rot = render_frame.getQuaternion();
+ rot = rot * LLQuaternion(-F_PI_BY_TWO, gCamera->getYAxis());
+ rot = rot * LLQuaternion(F_PI_BY_TWO, gCamera->getXAxis());
+ }
+ else
+ {
+ rot = LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 0.f, 1.f));
+ rot = rot * LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 1.f, 0.f));
+ }
+ F32 angle;
+ LLVector3 axis;
+ rot.getAngleAxis(&angle, axis);
+
+ LLVector3 render_pos = pos_agent + (floorf(x_offset) * right_axis) + (floorf(y_offset) * up_axis);
+
+ //get the render_pos in screen space
+ F64 modelview[16];
+ F64 projection[16];
+ GLint viewport[4];
+ glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
+ glGetDoublev(GL_PROJECTION_MATRIX, projection);
+ glGetIntegerv(GL_VIEWPORT, viewport);
+
+ F64 winX, winY, winZ;
+ gluProject(render_pos.mV[0], render_pos.mV[1], render_pos.mV[2],
+ modelview, projection, viewport,
+ &winX, &winY, &winZ);
+
+ //fonts all render orthographically, set up projection
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLUI::pushMatrix();
+
+ gViewerWindow->setup2DRender();
+
+ LLUI::loadIdentity();
+ LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f));
+ //glRotatef(angle * RAD_TO_DEG, axis.mV[VX], axis.mV[VY], axis.mV[VZ]);
+ //glScalef(right_scale, up_scale, 1.f);
+ F32 right_x;
+
+ font.render(wstr, 0, 0, 0, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, wstr.length(), 1000, &right_x);
+ LLUI::popMatrix();
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
diff --git a/indra/newview/llhudrender.h b/indra/newview/llhudrender.h
new file mode 100644
index 0000000000..31ef3f8464
--- /dev/null
+++ b/indra/newview/llhudrender.h
@@ -0,0 +1,39 @@
+/**
+ * @file llhudrender.h
+ * @brief LLHUDRender class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDRENDER_H
+#define LL_LLHUDRENDER_H
+
+#include "llfontgl.h"
+
+class LLVector3;
+class LLFontGL;
+
+// Utility classes for rendering HUD elements
+void hud_render_text(const LLWString &wstr,
+ const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset,
+ const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic);
+
+// Legacy, slower
+void hud_render_utf8text(const std::string &str,
+ const LLVector3 &pos_agent,
+ const LLFontGL &font,
+ const U8 style,
+ const F32 x_offset,
+ const F32 y_offset,
+ const LLColor4& color,
+ const BOOL orthographic);
+
+
+#endif //LL_LLHUDRENDER_H
+
diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp
new file mode 100644
index 0000000000..ad2ba368c9
--- /dev/null
+++ b/indra/newview/llhudtext.cpp
@@ -0,0 +1,991 @@
+/**
+ * @file llhudtext.cpp
+ * @brief LLHUDText class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudtext.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llchatbar.h"
+#include "llcriticaldamp.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llimagegl.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llvovolume.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llstatusbar.h"
+#include "llmenugl.h"
+#include "pipeline.h"
+#include <boost/tokenizer.hpp>
+
+
+const F32 SPRING_STRENGTH = 0.7f;
+const F32 RESTORATION_SPRING_TIME_CONSTANT = 0.1f;
+const F32 HORIZONTAL_PADDING = 15.f;
+const F32 VERTICAL_PADDING = 12.f;
+const F32 BUFFER_SIZE = 2.f;
+const F32 MIN_EDGE_OVERLAP = 3.f;
+F32 HUD_TEXT_MAX_WIDTH = 190.f;
+const F32 HUD_TEXT_MAX_WIDTH_NO_BUBBLE = 1000.f;
+const F32 RESIZE_TIME = 0.f;
+const S32 NUM_OVERLAP_ITERATIONS = 10;
+const F32 NEIGHBOR_FORCE_FRACTION = 1.f;
+const F32 POSITION_DAMPING_TC = 0.2f;
+const F32 MAX_STABLE_CAMERA_VELOCITY = 0.1f;
+const F32 LOD_0_SCREEN_COVERAGE = 0.15f;
+const F32 LOD_1_SCREEN_COVERAGE = 0.30f;
+const F32 LOD_2_SCREEN_COVERAGE = 0.40f;
+
+std::set<LLPointer<LLHUDText> > LLHUDText::sTextObjects;
+std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleTextObjects;
+std::vector<LLPointer<LLHUDText> > LLHUDText::sVisibleHUDTextObjects;
+
+bool lltextobject_further_away::operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const
+{
+ return (lhs->getDistance() > rhs->getDistance()) ? true : false;
+}
+
+
+LLHUDText::LLHUDText(const U8 type) :
+ LLHUDObject(type),
+ mUseBubble(FALSE),
+ mUsePixelSize(TRUE),
+ mVisibleOffScreen(FALSE),
+ mWidth(0.f),
+ mHeight(0.f),
+ mFontp(LLFontGL::sSansSerifSmall),
+ mBoldFontp(LLFontGL::sSansSerifBold),
+ mMass(1.f),
+ mMaxLines(10),
+ mOffsetY(0),
+ mTextAlignment(ALIGN_TEXT_CENTER),
+ mVertAlignment(ALIGN_VERT_CENTER),
+ mLOD(0)
+{
+ mColor = LLColor4(1.f, 1.f, 1.f, 1.f);
+ mDoFade = TRUE;
+ mFadeDistance = 8.f;
+ mFadeRange = 4.f;
+ mZCompare = TRUE;
+ mDropShadow = TRUE;
+ mOffscreen = FALSE;
+ mRadius = 0.1f;
+ LLPointer<LLHUDText> ptr(this);
+ sTextObjects.insert(ptr);
+ //LLDebugVarMessageBox::show("max width", &HUD_TEXT_MAX_WIDTH, 500.f, 1.f);
+}
+
+LLHUDText::~LLHUDText()
+{
+}
+
+
+void LLHUDText::render()
+{
+ if (!mOnHUDAttachment)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderText(FALSE);
+ }
+}
+
+void LLHUDText::renderForSelect()
+{
+ if (!mOnHUDAttachment)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderText(TRUE);
+ }
+}
+
+void LLHUDText::renderText(BOOL for_select)
+{
+ if (!mVisible)
+ {
+ return;
+ }
+
+ // don't pick text that isn't bound to a viewerobject or isn't in a bubble
+ if (for_select &&
+ (!mSourceObject || mSourceObject->mDrawable.isNull() || !mUseBubble))
+ {
+ return;
+ }
+
+ LLGLState gls_tex(GL_TEXTURE_2D, for_select ? FALSE : TRUE);
+ LLGLState gls_blend(GL_BLEND, for_select ? FALSE : TRUE);
+ LLGLState gls_alpha(GL_ALPHA_TEST, for_select ? FALSE : TRUE);
+
+ LLColor4 shadow_color(0.f, 0.f, 0.f, 1.f);
+ F32 alpha_factor = 1.f;
+ LLColor4 text_color = mColor;
+ if (mDoFade)
+ {
+ if (mLastDistance > mFadeDistance)
+ {
+ alpha_factor = llmax(0.f, 1.f - (mLastDistance - mFadeDistance)/mFadeRange);
+ text_color.mV[3] = text_color.mV[3]*alpha_factor;
+ }
+ }
+ if (text_color.mV[3] < 0.01f)
+ {
+ return;
+ }
+ shadow_color.mV[3] = text_color.mV[3];
+
+ mOffsetY = lltrunc(mHeight * ((mVertAlignment == ALIGN_VERT_CENTER) ? 0.5f : 1.f));
+
+ //FIXME: cache this image
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ //FIXME: make this a per-text setting
+ LLColor4 bg_color = gSavedSettings.getColor4("BackgroundChatColor");
+ bg_color.setAlpha(gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
+
+ const S32 border_height = 16;
+ const S32 border_width = 16;
+
+ //FIXME move this into helper function
+ F32 border_scale = 1.f;
+
+ if (border_height * 2 > mHeight)
+ {
+ border_scale = (F32)mHeight / ((F32)border_height * 2.f);
+ }
+ if (border_width * 2 > mWidth)
+ {
+ border_scale = llmin(border_scale, (F32)mWidth / ((F32)border_width * 2.f));
+ }
+
+ // scale screen size of borders down
+ //RN: for now, text on hud objects is never occluded
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ if (mOnHUDAttachment)
+ {
+ x_pixel_vec = LLVector3::y_axis / (F32)gViewerWindow->getWindowWidth();
+ y_pixel_vec = LLVector3::z_axis / (F32)gViewerWindow->getWindowHeight();
+ }
+ else
+ {
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+ }
+
+ LLVector2 border_scale_vec((F32)border_width / (F32)imagep->getWidth(), (F32)border_height / (F32)imagep->getHeight());
+ LLVector3 width_vec = mWidth * x_pixel_vec;
+ LLVector3 height_vec = mHeight * y_pixel_vec;
+ LLVector3 scaled_border_width = (F32)llfloor(border_scale * (F32)border_width) * x_pixel_vec;
+ LLVector3 scaled_border_height = (F32)llfloor(border_scale * (F32)border_height) * y_pixel_vec;
+
+ mRadius = (width_vec + height_vec).magVec() * 0.5f;
+
+ LLCoordGL screen_pos;
+ gCamera->projectPosAgentToScreen(mPositionAgent, screen_pos, FALSE);
+
+ LLVector2 screen_offset;
+ if (!mUseBubble)
+ {
+ screen_offset = mPositionOffset;
+ }
+ else
+ {
+ screen_offset = updateScreenPos(mPositionOffset);
+ }
+
+ LLVector3 render_position = mPositionAgent
+ + (x_pixel_vec * screen_offset.mV[VX])
+ + (y_pixel_vec * screen_offset.mV[VY]);
+
+ //if (mOnHUD)
+ //{
+ // render_position.mV[VY] -= fmodf(render_position.mV[VY], 1.f / (F32)gViewerWindow->getWindowWidth());
+ // render_position.mV[VZ] -= fmodf(render_position.mV[VZ], 1.f / (F32)gViewerWindow->getWindowHeight());
+ //}
+ //else
+ //{
+ // render_position = gCamera->roundToPixel(render_position);
+ //}
+
+ if (mUseBubble)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ LLUI::pushMatrix();
+ {
+ LLVector3 bg_pos = render_position
+ + (F32)mOffsetY * y_pixel_vec
+ - (width_vec / 2.f)
+ - (height_vec);
+ LLUI::translate(bg_pos.mV[VX], bg_pos.mV[VY], bg_pos.mV[VZ]);
+
+ if (for_select)
+ {
+ LLGLSNoTexture no_texture_state;
+ S32 name = mSourceObject->mGLName;
+ LLColor4U coloru((U8)(name >> 16), (U8)(name >> 8), (U8)name);
+ glColor4ubv(coloru.mV);
+ gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
+ LLUI::popMatrix();
+ return;
+ }
+ else
+ {
+ LLViewerImage::bindTexture(imagep);
+
+ glColor4fv(bg_color.mV);
+ gl_segmented_rect_3d_tex(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, height_vec);
+
+ if ( mLabelSegments.size())
+ {
+ LLUI::pushMatrix();
+ {
+ glColor4f(text_color.mV[VX], text_color.mV[VY], text_color.mV[VZ], gSavedSettings.getF32("ChatBubbleOpacity") * alpha_factor);
+ LLVector3 label_height = (mFontp->getLineHeight() * mLabelSegments.size() + (VERTICAL_PADDING / 3.f)) * y_pixel_vec;
+ LLVector3 label_offset = height_vec - label_height;
+ LLUI::translate(label_offset.mV[VX], label_offset.mV[VY], label_offset.mV[VZ]);
+ gl_segmented_rect_3d_tex_top(border_scale_vec, scaled_border_width, scaled_border_height, width_vec, label_height);
+ }
+ LLUI::popMatrix();
+ }
+ }
+
+ BOOL outside_width = llabs(mPositionOffset.mV[VX]) > mWidth * 0.5f;
+ BOOL outside_height = llabs(mPositionOffset.mV[VY] + (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.5f : 0.f)) > mHeight * (mVertAlignment == ALIGN_VERT_TOP ? mHeight * 0.75f : 0.5f);
+
+ // draw line segments pointing to parent object
+ if (!mOffscreen && (outside_width || outside_height))
+ {
+ LLUI::pushMatrix();
+ {
+ glColor4fv(bg_color.mV);
+ LLVector3 target_pos = -1.f * (mPositionOffset.mV[VX] * x_pixel_vec + mPositionOffset.mV[VY] * y_pixel_vec);
+ target_pos += (width_vec / 2.f);
+ target_pos += mVertAlignment == ALIGN_VERT_CENTER ? (height_vec * 0.5f) : LLVector3::zero;
+ target_pos -= 3.f * x_pixel_vec;
+ target_pos -= 6.f * y_pixel_vec;
+ LLUI::translate(target_pos.mV[VX], target_pos.mV[VY], target_pos.mV[VZ]);
+ gl_segmented_rect_3d_tex(border_scale_vec, 3.f * x_pixel_vec, 3.f * y_pixel_vec, 6.f * x_pixel_vec, 6.f * y_pixel_vec);
+ }
+ LLUI::popMatrix();
+
+
+ LLGLDisable gls_texture_2d(GL_TEXTURE_2D);
+ LLGLDepthTest gls_depth(mZCompare ? GL_TRUE : GL_FALSE, GL_FALSE);
+
+ LLVector3 box_center_offset;
+ box_center_offset = (width_vec * 0.5f) + (height_vec * 0.5f);
+ LLUI::translate(box_center_offset.mV[VX], box_center_offset.mV[VY], box_center_offset.mV[VZ]);
+ glColor4fv(bg_color.mV);
+ LLUI::setLineWidth(2.0);
+ glBegin(GL_LINES);
+ {
+ if (outside_width)
+ {
+ LLVector3 vert;
+ // draw line in x then y
+ if (mPositionOffset.mV[VX] < 0.f)
+ {
+ // start at right edge
+ vert = width_vec * 0.5f;
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ // start at left edge
+ vert = width_vec * -0.5f;
+ glVertex3fv(vert.mV);
+ }
+ vert = -mPositionOffset.mV[VX] * x_pixel_vec;
+ glVertex3fv(vert.mV);
+ glVertex3fv(vert.mV);
+ vert -= mPositionOffset.mV[VY] * y_pixel_vec;
+ vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ LLVector3 vert;
+ // draw line in y then x
+ if (mPositionOffset.mV[VY] < 0.f)
+ {
+ // start at top edge
+ vert = (height_vec * 0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
+ glVertex3fv(vert.mV);
+ }
+ else
+ {
+ // start at bottom edge
+ vert = (height_vec * -0.5f) - (mPositionOffset.mV[VX] * x_pixel_vec);
+ glVertex3fv(vert.mV);
+ }
+ vert = -mPositionOffset.mV[VY] * y_pixel_vec - mPositionOffset.mV[VX] * x_pixel_vec;
+ vert -= ((mVertAlignment == ALIGN_VERT_TOP) ? (height_vec * 0.5f) : LLVector3::zero);
+ glVertex3fv(vert.mV);
+ }
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0);
+
+ }
+ }
+ LLUI::popMatrix();
+ }
+
+ F32 y_offset = (F32)mOffsetY;
+
+ // Render label
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ for(std::vector<LLHUDTextSegment>::iterator segment_iter = mLabelSegments.begin();
+ segment_iter != mLabelSegments.end(); ++segment_iter )
+ {
+ const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
+ y_offset -= fontp->getLineHeight();
+
+ F32 x_offset;
+ if (mTextAlignment == ALIGN_TEXT_CENTER)
+ {
+ x_offset = -0.5f*segment_iter->getWidth(fontp);
+ }
+ else // ALIGN_LEFT
+ {
+ x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
+ }
+
+ LLColor4 label_color(0.f, 0.f, 0.f, 1.f);
+ label_color.mV[VALPHA] = alpha_factor;
+ hud_render_text(segment_iter->getText(), render_position, *fontp, segment_iter->mStyle, x_offset, y_offset, label_color, mOnHUDAttachment);
+ }
+ }
+
+ // Render text
+ {
+ // -1 mMaxLines means unlimited lines.
+ S32 start_segment;
+ S32 max_lines = getMaxLines();
+
+ if (max_lines < 0)
+ {
+ start_segment = 0;
+ }
+ else
+ {
+ start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
+ }
+
+ for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin() + start_segment;
+ segment_iter != mTextSegments.end(); ++segment_iter )
+ {
+ const LLFontGL* fontp = (segment_iter->mStyle == LLFontGL::BOLD) ? mBoldFontp : mFontp;
+ y_offset -= fontp->getLineHeight();
+
+ U8 style = segment_iter->mStyle;
+ if (mDropShadow)
+ {
+ style |= LLFontGL::DROP_SHADOW;
+ }
+
+ F32 x_offset;
+ if (mTextAlignment== ALIGN_TEXT_CENTER)
+ {
+ x_offset = -0.5f*segment_iter->getWidth(fontp);
+ }
+ else // ALIGN_LEFT
+ {
+ x_offset = -0.5f * mWidth + (HORIZONTAL_PADDING / 2.f);
+ }
+
+ text_color = segment_iter->mColor;
+ text_color.mV[VALPHA] *= alpha_factor;
+
+ hud_render_text(segment_iter->getText(), render_position, *fontp, style, x_offset, y_offset, text_color, mOnHUDAttachment);
+ }
+ }
+}
+
+void LLHUDText::setStringUTF8(const std::string &wtext)
+{
+ setString(utf8str_to_wstring(wtext));
+}
+
+void LLHUDText::setString(const LLWString &wtext)
+{
+ mTextSegments.clear();
+ addLine(wtext, mColor);
+}
+
+void LLHUDText::clearString()
+{
+ mTextSegments.clear();
+}
+
+
+void LLHUDText::addLine(const std::string &str, const LLColor4& color, const LLFontGL::StyleFlags style)
+{
+ addLine(utf8str_to_wstring(str), color, style);
+}
+
+
+void LLHUDText::addLine(const LLWString &wstr, const LLColor4& color, const LLFontGL::StyleFlags style)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ if (!wstr.empty())
+ {
+ LLWString wline(wstr);
+ typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
+ LLWString seps(utf8str_to_wstring("\r\n"));
+ boost::char_separator<llwchar> sep(seps.c_str());
+
+ tokenizer tokens(wline, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ while (iter != tokens.end())
+ {
+ U32 line_length = 0;
+ do
+ {
+ S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wline.length(), TRUE);
+ mTextSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), style, color));
+ line_length += segment_length;
+ }
+ while (line_length != iter->size());
+ ++iter;
+ }
+ }
+}
+
+void LLHUDText::setLabel(const std::string &label)
+{
+ setLabel(utf8str_to_wstring(label));
+}
+
+void LLHUDText::setLabel(const LLWString &wlabel)
+{
+ mLabelSegments.clear();
+
+ if (!wlabel.empty())
+ {
+ LLWString wstr(wlabel);
+ LLWString seps(utf8str_to_wstring("\r\n"));
+ LLWString empty;
+
+ typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer;
+ boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens);
+
+ tokenizer tokens(wstr, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ while (iter != tokens.end())
+ {
+ U32 line_length = 0;
+ do
+ {
+ S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), mUseBubble ? HUD_TEXT_MAX_WIDTH : HUD_TEXT_MAX_WIDTH_NO_BUBBLE, wstr.length(), TRUE);
+ mLabelSegments.push_back(LLHUDTextSegment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor));
+ line_length += segment_length;
+ }
+ while (line_length != iter->size());
+ ++iter;
+ }
+ }
+}
+
+void LLHUDText::setDropShadow(const BOOL do_shadow)
+{
+ mDropShadow = do_shadow;
+}
+
+void LLHUDText::setZCompare(const BOOL zcompare)
+{
+ mZCompare = zcompare;
+}
+
+void LLHUDText::setFont(const LLFontGL* font)
+{
+ mFontp = font;
+}
+
+
+void LLHUDText::setColor(const LLColor4 &color)
+{
+ mColor = color;
+ for (std::vector<LLHUDTextSegment>::iterator segment_iter = mTextSegments.begin();
+ segment_iter != mTextSegments.end(); ++segment_iter )
+ {
+ segment_iter->mColor = color;
+ }
+}
+
+
+void LLHUDText::setUsePixelSize(const BOOL use_pixel_size)
+{
+ mUsePixelSize = use_pixel_size;
+}
+
+void LLHUDText::setDoFade(const BOOL do_fade)
+{
+ mDoFade = do_fade;
+}
+
+void LLHUDText::updateVisibility()
+{
+ if (mSourceObject)
+ {
+ mSourceObject->updateText();
+ }
+
+ mPositionAgent = gAgent.getPosAgentFromGlobal(mPositionGlobal);
+
+ if (!mSourceObject)
+ {
+ //llwarns << "LLHUDText::updateScreenPos -- mSourceObject is NULL!" << llendl;
+ mVisible = TRUE;
+ if (mOnHUDAttachment)
+ {
+ sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
+ }
+ else
+ {
+ sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
+ }
+ return;
+ }
+
+ // Not visible if parent object is dead
+ if (mSourceObject->isDead())
+ {
+ mVisible = FALSE;
+ return;
+ }
+
+ // for now, all text on hud objects is visible
+ if (mOnHUDAttachment)
+ {
+ mVisible = TRUE;
+ sVisibleHUDTextObjects.push_back(LLPointer<LLHUDText> (this));
+ mLastDistance = mPositionAgent.mV[VX];
+ return;
+ }
+
+ // push text towards camera by radius of object, but not past camera
+ LLVector3 vec_from_camera = mPositionAgent - gCamera->getOrigin();
+ LLVector3 dir_from_camera = vec_from_camera;
+ dir_from_camera.normVec();
+
+ if (dir_from_camera * gCamera->getAtAxis() <= 0.f)
+ {
+ mPositionAgent -= projected_vec(vec_from_camera, gCamera->getAtAxis()) * 1.f;
+ mPositionAgent += gCamera->getAtAxis() * (gCamera->getNear() + 0.1f);
+ }
+ else if (vec_from_camera * gCamera->getAtAxis() <= gCamera->getNear() + 0.1f + mSourceObject->getVObjRadius())
+ {
+ mPositionAgent = gCamera->getOrigin() + vec_from_camera * ((gCamera->getNear() + 0.1f) / (vec_from_camera * gCamera->getAtAxis()));
+ }
+ else
+ {
+ mPositionAgent -= dir_from_camera * mSourceObject->getVObjRadius();
+ }
+
+ mLastDistance = (mPositionAgent - gCamera->getOrigin()).magVec();
+
+ if (mLOD >= 3 || !mTextSegments.size() || (mDoFade && (mLastDistance > mFadeDistance + mFadeRange)))
+ {
+ mVisible = FALSE;
+ return;
+ }
+
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+
+ LLVector3 render_position = mPositionAgent +
+ (x_pixel_vec * mPositionOffset.mV[VX]) +
+ (y_pixel_vec * mPositionOffset.mV[VY]);
+
+ mOffscreen = FALSE;
+ if (!gCamera->sphereInFrustum(render_position, mRadius))
+ {
+ if (!mVisibleOffScreen)
+ {
+ mVisible = FALSE;
+ return;
+ }
+ else
+ {
+ mOffscreen = TRUE;
+ }
+ }
+
+ mVisible = TRUE;
+ sVisibleTextObjects.push_back(LLPointer<LLHUDText> (this));
+}
+
+LLVector2 LLHUDText::updateScreenPos(LLVector2 &offset)
+{
+ LLCoordGL screen_pos;
+ LLVector2 screen_pos_vec;
+ LLVector3 x_pixel_vec;
+ LLVector3 y_pixel_vec;
+ gCamera->getPixelVectors(mPositionAgent, y_pixel_vec, x_pixel_vec);
+ LLVector3 world_pos = mPositionAgent + (offset.mV[VX] * x_pixel_vec) + (offset.mV[VY] * y_pixel_vec);
+ if (!gCamera->projectPosAgentToScreen(world_pos, screen_pos, FALSE) && mVisibleOffScreen)
+ {
+ // bubble off-screen, so find a spot for it along screen edge
+ LLVector2 window_center(gViewerWindow->getWindowDisplayWidth() * 0.5f, gViewerWindow->getWindowDisplayHeight() * 0.5f);
+ LLVector2 delta_from_center(screen_pos.mX - window_center.mV[VX],
+ screen_pos.mY - window_center.mV[VY]);
+ delta_from_center.normVec();
+
+ F32 camera_aspect = gCamera->getAspect();
+ F32 delta_aspect = llabs(delta_from_center.mV[VX] / delta_from_center.mV[VY]);
+ if (camera_aspect / llmax(delta_aspect, 0.001f) > 1.f)
+ {
+ // camera has wider aspect ratio than offset vector, so clamp to height
+ delta_from_center *= llabs(window_center.mV[VY] / delta_from_center.mV[VY]);
+ }
+ else
+ {
+ // camera has narrower aspect ratio than offset vector, so clamp to width
+ delta_from_center *= llabs(window_center.mV[VX] / delta_from_center.mV[VX]);
+ }
+
+ screen_pos_vec = window_center + delta_from_center;
+ }
+ else
+ {
+ screen_pos_vec.setVec((F32)screen_pos.mX, (F32)screen_pos.mY);
+ }
+ S32 bottom = STATUS_BAR_HEIGHT;
+ if (gChatBar->getVisible())
+ {
+ bottom += CHAT_BAR_HEIGHT;
+ }
+
+ LLVector2 screen_center;
+ screen_center.mV[VX] = llclamp((F32)screen_pos_vec.mV[VX], mWidth * 0.5f, (F32)gViewerWindow->getWindowDisplayWidth() - mWidth * 0.5f);
+
+ if(mVertAlignment == ALIGN_VERT_TOP)
+ {
+ screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
+ (F32)bottom,
+ (F32)gViewerWindow->getWindowDisplayHeight() - mHeight - (F32)MENU_BAR_HEIGHT);
+ mSoftScreenRect.setLeftTopAndSize(screen_center.mV[VX] - (mWidth + BUFFER_SIZE) * 0.5f,
+ screen_center.mV[VY] + (mHeight + BUFFER_SIZE), mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
+ }
+ else
+ {
+ screen_center.mV[VY] = llclamp((F32)screen_pos_vec.mV[VY],
+ (F32)bottom + mHeight * 0.5f,
+ (F32)gViewerWindow->getWindowDisplayHeight() - mHeight * 0.5f - (F32)MENU_BAR_HEIGHT);
+ mSoftScreenRect.setCenterAndSize(screen_center.mV[VX], screen_center.mV[VY], mWidth + BUFFER_SIZE, mHeight + BUFFER_SIZE);
+ }
+
+ return offset + (screen_center - LLVector2((F32)screen_pos.mX, (F32)screen_pos.mY));
+}
+
+void LLHUDText::updateSize()
+{
+ F32 width = 0.f;
+
+ S32 max_lines = getMaxLines();
+ S32 lines = (max_lines < 0) ? (S32)mTextSegments.size() : llmin((S32)mTextSegments.size(), max_lines);
+
+ F32 height = (F32)mFontp->getLineHeight() * (lines + mLabelSegments.size());
+
+ S32 start_segment;
+ if (max_lines < 0) start_segment = 0;
+ else start_segment = llmax((S32)0, (S32)mTextSegments.size() - max_lines);
+
+ std::vector<LLHUDTextSegment>::iterator iter = mTextSegments.begin() + start_segment;
+ while (iter != mTextSegments.end())
+ {
+ width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
+ ++iter;
+ }
+
+ iter = mLabelSegments.begin();
+ while (iter != mLabelSegments.end())
+ {
+ width = llmax(width, llmin(iter->getWidth(mFontp), HUD_TEXT_MAX_WIDTH));
+ ++iter;
+ }
+
+ if (width == 0.f)
+ {
+ return;
+ }
+
+ width += HORIZONTAL_PADDING;
+ height += VERTICAL_PADDING;
+
+ if (!mResizeTimer.getStarted() && (width != mWidth || height != mHeight))
+ {
+ mResizeTimer.start();
+ }
+
+ // *NOTE: removed logic which did a divide by zero.
+ F32 u = 1.f;//llclamp(mResizeTimer.getElapsedTimeF32() / RESIZE_TIME, 0.f, 1.f);
+ if (u == 1.f)
+ {
+ mResizeTimer.stop();
+ }
+
+ mWidth = llmax(width, lerp(mWidth, (F32)width, u));
+ mHeight = llmax(height, lerp(mHeight, (F32)height, u));
+}
+
+void LLHUDText::updateAll()
+{
+ // iterate over all text objects, calculate their restoration forces,
+ // and add them to the visible set if they are on screen and close enough
+ sVisibleTextObjects.clear();
+ sVisibleHUDTextObjects.clear();
+
+ TextObjectIterator text_it;
+ for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
+ {
+ LLHUDText* textp = (*text_it);
+ textp->mTargetPositionOffset.clearVec();
+ textp->updateSize();
+ textp->updateVisibility();
+ }
+
+ // sort back to front for rendering purposes
+ std::sort(sVisibleTextObjects.begin(), sVisibleTextObjects.end(), lltextobject_further_away());
+ std::sort(sVisibleHUDTextObjects.begin(), sVisibleHUDTextObjects.end(), lltextobject_further_away());
+
+ // iterate from front to back, and set LOD based on current screen coverage
+ F32 screen_area = (F32)(gViewerWindow->getWindowWidth() * gViewerWindow->getWindowHeight());
+ F32 current_screen_area = 0.f;
+ std::vector<LLPointer<LLHUDText> >::reverse_iterator r_it;
+ for (r_it = sVisibleTextObjects.rbegin(); r_it != sVisibleTextObjects.rend(); ++r_it)
+ {
+ LLHUDText* textp = (*r_it);
+ if (textp->mUseBubble)
+ {
+ if (current_screen_area / screen_area > LOD_2_SCREEN_COVERAGE)
+ {
+ textp->setLOD(3);
+ }
+ else if (current_screen_area / screen_area > LOD_1_SCREEN_COVERAGE)
+ {
+ textp->setLOD(2);
+ }
+ else if (current_screen_area / screen_area > LOD_0_SCREEN_COVERAGE)
+ {
+ textp->setLOD(1);
+ }
+ else
+ {
+ textp->setLOD(0);
+ }
+ textp->updateSize();
+ // find on-screen position and initialize collision rectangle
+ textp->mTargetPositionOffset = textp->updateScreenPos(LLVector2::zero);
+ current_screen_area += (F32)(textp->mSoftScreenRect.getWidth() * textp->mSoftScreenRect.getHeight());
+ }
+ }
+
+ LLStat* camera_vel_stat = gCamera->getVelocityStat();
+ F32 camera_vel = camera_vel_stat->getCurrent();
+ if (camera_vel > MAX_STABLE_CAMERA_VELOCITY)
+ {
+ return;
+ }
+
+ VisibleTextObjectIterator src_it;
+
+ for (S32 i = 0; i < NUM_OVERLAP_ITERATIONS; i++)
+ {
+ for (src_it = sVisibleTextObjects.begin(); src_it != sVisibleTextObjects.end(); ++src_it)
+ {
+ LLHUDText* src_textp = (*src_it);
+
+ if (!src_textp->mUseBubble)
+ {
+ continue;
+ }
+ VisibleTextObjectIterator dst_it = src_it;
+ ++dst_it;
+ for (; dst_it != sVisibleTextObjects.end(); ++dst_it)
+ {
+ LLHUDText* dst_textp = (*dst_it);
+
+ if (!dst_textp->mUseBubble)
+ {
+ continue;
+ }
+ if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect))
+ {
+ LLRectf intersect_rect = src_textp->mSoftScreenRect & dst_textp->mSoftScreenRect;
+ intersect_rect.stretch(-BUFFER_SIZE * 0.5f);
+
+ F32 src_center_x = src_textp->mSoftScreenRect.getCenterX();
+ F32 src_center_y = src_textp->mSoftScreenRect.getCenterY();
+ F32 dst_center_x = dst_textp->mSoftScreenRect.getCenterX();
+ F32 dst_center_y = dst_textp->mSoftScreenRect.getCenterY();
+ F32 intersect_center_x = intersect_rect.getCenterX();
+ F32 intersect_center_y = intersect_rect.getCenterY();
+ LLVector2 force = lerp(LLVector2(dst_center_x - intersect_center_x, dst_center_y - intersect_center_y),
+ LLVector2(intersect_center_x - src_center_x, intersect_center_y - src_center_y),
+ 0.5f);
+ force.setVec(dst_center_x - src_center_x, dst_center_y - src_center_y);
+ force.normVec();
+
+ LLVector2 src_force = -1.f * force;
+ LLVector2 dst_force = force;
+
+ LLVector2 force_strength;
+ F32 src_mult = dst_textp->mMass / (dst_textp->mMass + src_textp->mMass);
+ F32 dst_mult = 1.f - src_mult;
+ F32 src_aspect_ratio = src_textp->mSoftScreenRect.getWidth() / src_textp->mSoftScreenRect.getHeight();
+ F32 dst_aspect_ratio = dst_textp->mSoftScreenRect.getWidth() / dst_textp->mSoftScreenRect.getHeight();
+ src_force.mV[VY] *= src_aspect_ratio;
+ src_force.normVec();
+ dst_force.mV[VY] *= dst_aspect_ratio;
+ dst_force.normVec();
+
+ src_force.mV[VX] *= llmin(intersect_rect.getWidth() * src_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
+ src_force.mV[VY] *= llmin(intersect_rect.getHeight() * src_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
+ dst_force.mV[VX] *= llmin(intersect_rect.getWidth() * dst_mult, intersect_rect.getHeight() * SPRING_STRENGTH);
+ dst_force.mV[VY] *= llmin(intersect_rect.getHeight() * dst_mult, intersect_rect.getWidth() * SPRING_STRENGTH);
+
+ src_textp->mTargetPositionOffset += src_force;
+ dst_textp->mTargetPositionOffset += dst_force;
+ src_textp->mTargetPositionOffset = src_textp->updateScreenPos(src_textp->mTargetPositionOffset);
+ dst_textp->mTargetPositionOffset = dst_textp->updateScreenPos(dst_textp->mTargetPositionOffset);
+ }
+ }
+ }
+ }
+
+ VisibleTextObjectIterator this_object_it;
+ for (this_object_it = sVisibleTextObjects.begin(); this_object_it != sVisibleTextObjects.end(); ++this_object_it)
+ {
+ if (!(*this_object_it)->mUseBubble)
+ {
+ continue;
+ }
+ (*this_object_it)->mPositionOffset = lerp((*this_object_it)->mPositionOffset, (*this_object_it)->mTargetPositionOffset, LLCriticalDamp::getInterpolant(POSITION_DAMPING_TC));
+ }
+}
+
+void LLHUDText::setLOD(S32 lod)
+{
+ mLOD = lod;
+ //RN: uncomment this to visualize LOD levels
+ //char label[255];
+ //sprintf(label, "%d", lod);
+ //setLabel(label);
+}
+
+S32 LLHUDText::getMaxLines()
+{
+ switch(mLOD)
+ {
+ case 0:
+ return mMaxLines;
+ case 1:
+ return mMaxLines > 0 ? mMaxLines / 2 : 5;
+ case 2:
+ return mMaxLines > 0 ? mMaxLines / 3 : 2;
+ default:
+ // label only
+ return 0;
+ }
+}
+
+void LLHUDText::markDead()
+{
+ sTextObjects.erase(LLPointer<LLHUDText>(this));
+ LLHUDObject::markDead();
+}
+
+void LLHUDText::renderAllHUD()
+{
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ LLGLDepthTest depth(GL_FALSE, GL_FALSE);
+
+ VisibleTextObjectIterator text_it;
+
+ for (text_it = sVisibleHUDTextObjects.begin(); text_it != sVisibleHUDTextObjects.end(); ++text_it)
+ {
+ (*text_it)->renderText(FALSE);
+ }
+}
+
+//static
+void LLHUDText::addPickable(std::set<LLViewerObject*> &pick_list)
+{
+ //this might put an object on the pick list a second time, overriding it's mGLName, which is ok
+ //FIXME: we should probably cull against pick frustum
+ VisibleTextObjectIterator text_it;
+ for (text_it = sVisibleTextObjects.begin(); text_it != sVisibleTextObjects.end(); ++text_it)
+ {
+ if (!(*text_it)->mUseBubble)
+ {
+ continue;
+ }
+ pick_list.insert((*text_it)->mSourceObject);
+ }
+}
+
+//static
+// called when UI scale changes, to flush font width caches
+void LLHUDText::reshape()
+{
+ TextObjectIterator text_it;
+ for (text_it = sTextObjects.begin(); text_it != sTextObjects.end(); ++text_it)
+ {
+ LLHUDText* textp = (*text_it);
+ std::vector<LLHUDTextSegment>::iterator segment_iter;
+ for (segment_iter = textp->mTextSegments.begin();
+ segment_iter != textp->mTextSegments.end(); ++segment_iter )
+ {
+ segment_iter->clearFontWidthMap();
+ }
+ for(segment_iter = textp->mLabelSegments.begin();
+ segment_iter != textp->mLabelSegments.end(); ++segment_iter )
+ {
+ segment_iter->clearFontWidthMap();
+ }
+ }
+}
+
+//============================================================================
+
+F32 LLHUDText::LLHUDTextSegment::getWidth(const LLFontGL* font)
+{
+ std::map<const LLFontGL*, F32>::iterator iter = mFontWidthMap.find(font);
+ if (iter != mFontWidthMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ F32 width = font->getWidthF32(mText.c_str());
+ mFontWidthMap[font] = width;
+ return width;
+ }
+}
diff --git a/indra/newview/llhudtext.h b/indra/newview/llhudtext.h
new file mode 100644
index 0000000000..a2e328acd1
--- /dev/null
+++ b/indra/newview/llhudtext.h
@@ -0,0 +1,155 @@
+/**
+ * @file llhudtext.h
+ * @brief LLHUDText class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDTEXT_H
+#define LL_LLHUDTEXT_H
+
+#include "llmemory.h"
+#include "lldarrayptr.h"
+
+#include "llhudobject.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v2math.h"
+#include "llrect.h"
+#include "llframetimer.h"
+#include "llfontgl.h"
+#include <set>
+#include <vector>
+#include "lldarray.h"
+
+// Renders a 2D text billboard floating at the location specified.
+class LLDrawable;
+class LLHUDText;
+
+struct lltextobject_further_away
+{
+ bool operator()(const LLPointer<LLHUDText>& lhs, const LLPointer<LLHUDText>& rhs) const;
+};
+
+class LLHUDText : public LLHUDObject
+{
+protected:
+ class LLHUDTextSegment
+ {
+ public:
+ LLHUDTextSegment(const LLWString& text, const LLFontGL::StyleFlags style, const LLColor4& color)
+ : mColor(color), mStyle(style), mText(text) {}
+ F32 getWidth(const LLFontGL* font);
+ const LLWString& getText() const { return mText; };
+ void clearFontWidthMap() { mFontWidthMap.clear(); }
+
+ LLColor4 mColor;
+ LLFontGL::StyleFlags mStyle;
+ private:
+ LLWString mText;
+ std::map<const LLFontGL*, F32> mFontWidthMap;
+ };
+
+public:
+ typedef enum e_text_alignment
+ {
+ ALIGN_TEXT_LEFT,
+ ALIGN_TEXT_CENTER
+ } ETextAlignment;
+
+ typedef enum e_vert_alignment
+ {
+ ALIGN_VERT_TOP,
+ ALIGN_VERT_CENTER
+ } EVertAlignment;
+
+public:
+ void setStringUTF8(const std::string &utf8string);
+ void setString(const LLWString &wstring);
+ void clearString();
+ void addLine(const std::string &text, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL);
+ void addLine(const LLWString &wtext, const LLColor4& color, const LLFontGL::StyleFlags style = LLFontGL::NORMAL);
+ void setLabel(const std::string &label);
+ void setLabel(const LLWString &label);
+ void setDropShadow(const BOOL do_shadow);
+ void setFont(const LLFontGL* font);
+ void setColor(const LLColor4 &color);
+ void setUsePixelSize(const BOOL use_pixel_size);
+ void setZCompare(const BOOL zcompare);
+ void setDoFade(const BOOL do_fade);
+ void setVisibleOffScreen(BOOL visible) { mVisibleOffScreen = visible; }
+
+ // mMaxLines of -1 means unlimited lines.
+ void setMaxLines(S32 max_lines) { mMaxLines = max_lines; }
+ void setFadeDistance(F32 fade_distance, F32 fade_range) { mFadeDistance = fade_distance; mFadeRange = fade_range; }
+ void updateVisibility();
+ LLVector2 updateScreenPos(LLVector2 &offset_target);
+ void updateSize();
+ void setMass(F32 mass) { mMass = llmax(0.1f, mass); }
+ void setTextAlignment(ETextAlignment alignment) { mTextAlignment = alignment; }
+ void setVertAlignment(EVertAlignment alignment) { mVertAlignment = alignment; }
+ /*virtual*/ void markDead();
+ friend class LLHUDObject;
+ /*virtual*/ F32 getDistance() const { return mLastDistance; }
+ void setUseBubble(BOOL use_bubble) { mUseBubble = use_bubble; }
+ S32 getLOD() { return mLOD; }
+ BOOL getVisible() { return mVisible; }
+ void setOnHUDAttachment(BOOL on_hud) { mOnHUDAttachment = on_hud; }
+
+ static void renderAllHUD();
+ static void addPickable(std::set<LLViewerObject*> &pick_list);
+ static void reshape();
+protected:
+ LLHUDText(const U8 type);
+
+ /*virtual*/ void render();
+ /*virtual*/ void renderForSelect();
+ void renderText(BOOL for_select);
+ static void updateAll();
+ void setLOD(S32 lod);
+ S32 getMaxLines();
+
+private:
+ ~LLHUDText();
+ BOOL mOnHUD;
+ BOOL mUseBubble;
+ BOOL mDropShadow;
+ BOOL mDoFade;
+ F32 mFadeRange;
+ F32 mFadeDistance;
+ F32 mLastDistance;
+ BOOL mUsePixelSize;
+ BOOL mZCompare;
+ BOOL mVisibleOffScreen;
+ BOOL mOffscreen;
+ LLColor4 mColor;
+ LLVector3 mScale;
+ F32 mWidth;
+ F32 mHeight;
+ LLColor4U mPickColor;
+ const LLFontGL* mFontp;
+ const LLFontGL* mBoldFontp;
+ LLRectf mSoftScreenRect;
+ LLVector3 mPositionAgent;
+ LLVector2 mPositionOffset;
+ LLVector2 mTargetPositionOffset;
+ F32 mMass;
+ S32 mMaxLines;
+ S32 mOffsetY;
+ F32 mRadius;
+ std::vector<LLHUDTextSegment> mTextSegments;
+ std::vector<LLHUDTextSegment> mLabelSegments;
+ LLFrameTimer mResizeTimer;
+ ETextAlignment mTextAlignment;
+ EVertAlignment mVertAlignment;
+ S32 mLOD;
+
+ static std::set<LLPointer<LLHUDText> > sTextObjects;
+ static std::vector<LLPointer<LLHUDText> > sVisibleTextObjects;
+ static std::vector<LLPointer<LLHUDText> > sVisibleHUDTextObjects;
+ typedef std::set<LLPointer<LLHUDText> >::iterator TextObjectIterator;
+ typedef std::vector<LLPointer<LLHUDText> >::iterator VisibleTextObjectIterator;
+};
+
+#endif // LL_LLHUDTEXT_H
diff --git a/indra/newview/llhudview.cpp b/indra/newview/llhudview.cpp
new file mode 100644
index 0000000000..91cb5695ed
--- /dev/null
+++ b/indra/newview/llhudview.cpp
@@ -0,0 +1,78 @@
+/**
+ * @file llhudview.cpp
+ * @brief 2D HUD overlay
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llhudview.h"
+
+// library includes
+#include "v4color.h"
+#include "llcoord.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "llworldmapview.h"
+#include "lltracker.h"
+#include "llviewercamera.h"
+#include "llui.h"
+
+LLHUDView *gHUDView = NULL;
+
+const S32 HUD_ARROW_SIZE = 32;
+
+LLHUDView::LLHUDView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, FALSE)
+{ }
+
+LLHUDView::~LLHUDView()
+{ }
+
+EWidgetType LLHUDView::getWidgetType() const
+{
+ return WIDGET_TYPE_HUD_VIEW;
+}
+
+LLString LLHUDView::getWidgetTag() const
+{
+ return LL_HUD_VIEW_TAG;
+}
+
+// virtual
+void LLHUDView::draw()
+{
+ LLTracker::drawHUDArrow();
+}
+
+
+// public
+const LLColor4& LLHUDView::colorFromType(S32 type)
+{
+ switch (type)
+ {
+ case 0:
+ return LLColor4::green;
+ default:
+ return LLColor4::black;
+ }
+}
+
+
+/*virtual*/
+BOOL LLHUDView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (LLTracker::handleMouseDown(x, y))
+ {
+ return TRUE;
+ }
+ return LLView::handleMouseDown(x, y, mask);
+}
+
diff --git a/indra/newview/llhudview.h b/indra/newview/llhudview.h
new file mode 100644
index 0000000000..85267979c6
--- /dev/null
+++ b/indra/newview/llhudview.h
@@ -0,0 +1,37 @@
+/**
+ * @file llhudview.h
+ * @brief 2D HUD overlay
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLHUDVIEW_H
+#define LL_LLHUDVIEW_H
+
+#include "llview.h"
+#include "v4color.h"
+
+class LLVector3d;
+
+class LLHUDView
+: public LLView
+{
+public:
+ LLHUDView(const std::string& name, const LLRect& rect);
+ virtual ~LLHUDView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void draw();
+
+ const LLColor4& colorFromType(S32 type);
+
+protected:
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+};
+
+extern LLHUDView *gHUDView;
+
+#endif
diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp
new file mode 100644
index 0000000000..718ea894aa
--- /dev/null
+++ b/indra/newview/llimpanel.cpp
@@ -0,0 +1,793 @@
+/**
+ * @file llimpanel.cpp
+ * @brief LLIMPanel class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llimpanel.h"
+
+#include "indra_constants.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+#include "lltextbox.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llconsole.h"
+#include "llfloater.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llfloateravatarinfo.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "lltabcontainer.h"
+#include "llimview.h"
+#include "llviewertexteditor.h"
+#include "llviewermessage.h"
+#include "llviewerstats.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+#include "lllogchat.h"
+#include "llfloaterhtml.h"
+#include "llweb.h"
+
+//
+// Constants
+//
+const S32 LINE_HEIGHT = 16;
+const S32 MIN_WIDTH = 200;
+const S32 MIN_HEIGHT = 130;
+
+//
+// Statics
+//
+//
+static LLString sTitleString = "Instant Message with [NAME]";
+static LLString sTypingStartString = "[NAME]: ...";
+
+// Member Functions
+//
+
+LLFloaterIMPanel::LLFloaterIMPanel(const std::string& name, const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ EInstantMessage dialog) :
+ LLFloater(name, rect, session_label),
+ mInputEditor(NULL),
+ mHistoryEditor(NULL),
+ mSessionLabel(session_label),
+ mSessionUUID(session_id),
+ mOtherParticipantUUID(other_participant_id),
+ mLureID(),
+ mDialog(dialog),
+ mTyping(FALSE),
+ mOtherTyping(FALSE),
+ mTypingLineStartIndex(0),
+ mSentTypingState(TRUE),
+ mFirstKeystrokeTimer(),
+ mLastKeystrokeTimer()
+{
+ init();
+ setLabel(session_label);
+ setTitle(session_label);
+ mInputEditor->setMaxTextLength(1023);
+}
+
+
+void LLFloaterIMPanel::init()
+{
+ gUICtrlFactory->buildFloater(this, "floater_instant_message.xml", NULL, FALSE);
+}
+
+
+BOOL LLFloaterIMPanel::postBuild()
+{
+ requires("chat_editor", WIDGET_TYPE_LINE_EDITOR);
+ requires("profile_btn", WIDGET_TYPE_BUTTON);
+ requires("close_btn", WIDGET_TYPE_BUTTON);
+ requires("im_history", WIDGET_TYPE_TEXT_EDITOR);
+ requires("live_help_dialog", WIDGET_TYPE_TEXT_BOX);
+ requires("title_string", WIDGET_TYPE_TEXT_BOX);
+ requires("typing_start_string", WIDGET_TYPE_TEXT_BOX);
+
+ if (checkRequirements())
+ {
+ mInputEditor = LLUICtrlFactory::getLineEditorByName(this, "chat_editor");
+ mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived );
+ mInputEditor->setFocusLostCallback( onInputEditorFocusLost );
+ mInputEditor->setKeystrokeCallback( onInputEditorKeystroke );
+ mInputEditor->setCallbackUserData(this);
+ mInputEditor->setCommitOnFocusLost( FALSE );
+
+ LLButton* profile_btn = LLUICtrlFactory::getButtonByName(this, "profile_btn");
+ profile_btn->setClickedCallback(&LLFloaterIMPanel::onClickProfile, this);
+
+ LLButton* close_btn = LLUICtrlFactory::getButtonByName(this, "close_btn");
+ close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this);
+
+ mHistoryEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "im_history");
+ mHistoryEditor->setParseHTML(TRUE);
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") )
+ {
+ LLLogChat::loadHistory(mSessionLabel, &chatFromLogFile, (void *)this);
+ }
+
+ if (IM_SESSION_GROUP_START == mDialog
+ || IM_SESSION_911_START == mDialog)
+ {
+ profile_btn->setEnabled(FALSE);
+ if(IM_SESSION_911_START == mDialog)
+ {
+ LLTextBox* live_help_text = LLUICtrlFactory::getTextBoxByName(this, "live_help_dialog");
+ addHistoryLine(live_help_text->getText());
+ }
+ }
+
+ LLTextBox* title = LLUICtrlFactory::getTextBoxByName(this, "title_string");
+ sTitleString = title->getText();
+
+ LLTextBox* typing_start = LLUICtrlFactory::getTextBoxByName(this, "typing_start_string");
+
+ sTypingStartString = typing_start->getText();
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// virtual
+void LLFloaterIMPanel::draw()
+{
+ if (mTyping)
+ {
+ // Time out if user hasn't typed for a while.
+ if (mLastKeystrokeTimer.getElapsedTimeF32() > LLAgent::TYPING_TIMEOUT_SECS)
+ {
+ setTyping(FALSE);
+ }
+
+ // If we are typing, and it's been a little while, send the
+ // typing indicator
+ if (!mSentTypingState
+ && mFirstKeystrokeTimer.getElapsedTimeF32() > 1.f)
+ {
+ sendTypingState(TRUE);
+ mSentTypingState = TRUE;
+ }
+ }
+
+ LLFloater::draw();
+}
+
+
+BOOL LLFloaterIMPanel::addParticipants(const LLDynamicArray<LLUUID>& ids)
+{
+ if(isAddAllowed())
+ {
+ llinfos << "LLFloaterIMPanel::addParticipants() - adding participants" << llendl;
+ const S32 MAX_AGENTS = 50;
+ S32 count = ids.count();
+ if(count > MAX_AGENTS) return FALSE;
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, mOtherParticipantUUID);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addU8Fast(_PREHASH_Dialog, mDialog);
+ msg->addUUIDFast(_PREHASH_ID, mSessionUUID);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+ std::string name;
+ gAgent.buildFullname(name);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, LLString::null);
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+ if (IM_SESSION_GROUP_START == mDialog)
+ {
+ // *HACK: binary bucket contains session label - the server
+ // will actually add agents.
+ llinfos << "Group IM session name '" << mSessionLabel
+ << "'" << llendl;
+ msg->addStringFast(_PREHASH_BinaryBucket, mSessionLabel);
+ gAgent.sendReliableMessage();
+ }
+ else if (IM_SESSION_911_START == mDialog)
+ {
+ // HACK -- we modify the name of the session going out to
+ // the helpers to help them easily identify "Help"
+ // sessions in their collection of IM panels.
+ LLString name;
+ gAgent.getName(name);
+ LLString buffer = LLString("HELP ") + name;
+ llinfos << "LiveHelp IM session '" << buffer << "'." << llendl;
+ msg->addStringFast(_PREHASH_BinaryBucket, buffer.c_str());
+
+ // automaticaly open a wormhole when this reliable message gets through
+ msg->sendReliable(
+ gAgent.getRegionHost(),
+ 3, // retries
+ TRUE, // ping-based
+ 5.0f, // timeout
+ send_lure_911,
+ (void**)&mSessionUUID);
+ }
+ else
+ {
+ if (mDialog != IM_SESSION_ADD
+ && mDialog != IM_SESSION_OFFLINE_ADD)
+ {
+ llwarns << "LLFloaterIMPanel::addParticipants() - dialog type " << mDialog
+ << " is not an ADD" << llendl;
+ }
+ // *FIX: this could suffer from endian issues
+ S32 bucket_size = UUID_BYTES * count;
+ U8* bucket = new U8[bucket_size];
+ U8* pos = bucket;
+ for(S32 i = 0; i < count; ++i)
+ {
+ memcpy(pos, &(ids.get(i)), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, bucket, bucket_size);
+ delete[] bucket;
+ gAgent.sendReliableMessage();
+ }
+
+ }
+ else
+ {
+ llinfos << "LLFloaterIMPanel::addParticipants() - no need to add agents for "
+ << mDialog << llendl;
+ // successful add, because everyone that needed to get added
+ // was added.
+ }
+ return TRUE;
+}
+
+void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4& color, bool log_to_file)
+{
+ LLMultiFloater* hostp = getHost();
+ if( !getVisible() && hostp && log_to_file)
+ {
+ // Only flash for logged ("real") messages
+ LLTabContainer* parent = (LLTabContainer*) getParent();
+ parent->setTabPanelFlashing( this, TRUE );
+ }
+
+ // Now we're adding the actual line of text, so erase the
+ // "Foo is typing..." text segment, and the optional timestamp
+ // if it was present. JC
+ removeTypingIndicator();
+
+ // Actually add the line
+ LLString timestring;
+ bool prepend_newline = true;
+ if (gSavedSettings.getBOOL("IMShowTimestamps"))
+ {
+ timestring = mHistoryEditor->appendTime(prepend_newline);
+ prepend_newline = false;
+ }
+ mHistoryEditor->appendColoredText(utf8msg, false, prepend_newline, color);
+
+ if (log_to_file
+ && gSavedPerAccountSettings.getBOOL("LogInstantMessages") )
+ {
+ LLString histstr = timestring + utf8msg;
+
+ LLLogChat::saveHistory(mSessionLabel,histstr);
+ }
+}
+
+
+void LLFloaterIMPanel::setVisible(BOOL b)
+{
+ LLPanel::setVisible(b);
+
+ LLMultiFloater* hostp = getHost();
+ if( b && hostp )
+ {
+ LLTabContainer* parent = (LLTabContainer*) getParent();
+
+ // When this tab is displayed, you can stop flashing.
+ parent->setTabPanelFlashing( this, FALSE );
+
+ /* Don't change containing floater title - leave it "Instant Message" JC
+ LLUIString title = sTitleString;
+ title.setArg("[NAME]", mSessionLabel);
+ hostp->setTitle( title );
+ */
+ }
+}
+
+
+void LLFloaterIMPanel::setInputFocus( BOOL b )
+{
+ mInputEditor->setFocus( b );
+}
+
+
+void LLFloaterIMPanel::selectAll()
+{
+ mInputEditor->selectAll();
+}
+
+
+void LLFloaterIMPanel::selectNone()
+{
+ mInputEditor->deselect();
+}
+
+
+BOOL LLFloaterIMPanel::handleKeyHere( KEY key, MASK mask, BOOL called_from_parent )
+{
+ BOOL handled = FALSE;
+ if( getVisible() && mEnabled && !called_from_parent && gFocusMgr.childHasKeyboardFocus(this))
+ {
+ if( KEY_RETURN == key && mask == MASK_NONE)
+ {
+ sendMsg();
+ handled = TRUE;
+
+ // Close talk panels on hitting return
+ // but not shift-return or control-return
+ if ( !gSavedSettings.getBOOL("PinTalkViewOpen") && !(mask & MASK_CONTROL) && !(mask & MASK_SHIFT) )
+ {
+ gIMView->toggle(NULL);
+ }
+ }
+ else if ( KEY_ESCAPE == key )
+ {
+ handled = TRUE;
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+ // Close talk panel with escape
+ if( !gSavedSettings.getBOOL("PinTalkViewOpen") )
+ {
+ gIMView->toggle(NULL);
+ }
+ }
+ }
+
+ // May need to call base class LLPanel::handleKeyHere if not handled
+ // in order to tab between buttons. JNC 1.2.2002
+ return handled;
+}
+
+BOOL LLFloaterIMPanel::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL accepted = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_CALLINGCARD:
+ accepted = dropCallingCard((LLInventoryItem*)cargo_data, drop);
+ break;
+ case DAD_CATEGORY:
+ accepted = dropCategory((LLInventoryCategory*)cargo_data, drop);
+ break;
+ default:
+ break;
+ }
+ if (accepted)
+ {
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ return TRUE;
+}
+
+BOOL LLFloaterIMPanel::dropCallingCard(LLInventoryItem* item, BOOL drop)
+{
+ BOOL rv = isAddAllowed();
+ if(rv && item && item->getCreatorUUID().notNull())
+ {
+ if(drop)
+ {
+ LLDynamicArray<LLUUID> ids;
+ ids.put(item->getCreatorUUID());
+ addParticipants(ids);
+ }
+ }
+ else
+ {
+ // set to false if creator uuid is null.
+ rv = FALSE;
+ }
+ return rv;
+}
+
+BOOL LLFloaterIMPanel::dropCategory(LLInventoryCategory* category, BOOL drop)
+{
+ BOOL rv = isAddAllowed();
+ if(rv && category)
+ {
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ LLUniqueBuddyCollector buddies;
+ gInventory.collectDescendentsIf(category->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ buddies);
+ S32 count = items.count();
+ if(count == 0)
+ {
+ rv = FALSE;
+ }
+ else if(drop)
+ {
+ LLDynamicArray<LLUUID> ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.put(items.get(i)->getCreatorUUID());
+ }
+ addParticipants(ids);
+ }
+ }
+ return rv;
+}
+
+BOOL LLFloaterIMPanel::isAddAllowed() const
+{
+
+ return ((IM_SESSION_ADD == mDialog)
+ || (IM_SESSION_OFFLINE_ADD == mDialog)
+ || (IM_SESSION_GROUP_START == mDialog)
+ || (IM_SESSION_911_START == mDialog)
+ || (IM_SESSION_CARDLESS_START == mDialog));
+}
+
+
+// static
+void LLFloaterIMPanel::onTabClick(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ self->setInputFocus(TRUE);
+}
+
+
+// static
+void LLFloaterIMPanel::onClickProfile( void* userdata )
+{
+ // Bring up the Profile window
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+
+ if (self->mOtherParticipantUUID.notNull())
+ {
+ LLFloaterAvatarInfo::showFromDirectory(self->getOtherParticipantID());
+ }
+}
+
+// static
+void LLFloaterIMPanel::onClickClose( void* userdata )
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ if(self)
+ {
+ self->close();
+ }
+}
+
+void LLFloaterIMPanel::addTeleportButton(const LLUUID& lure_id)
+{
+ LLButton* btn = LLViewerUICtrlFactory::getButtonByName(this, "Teleport Btn");
+ if (!btn)
+ {
+ S32 BTN_VPAD = 2;
+ S32 BTN_HPAD = 2;
+
+ const char* teleport_label = "Teleport";
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ S32 p_btn_width = 75;
+ S32 c_btn_width = 60;
+ S32 t_btn_width = 75;
+
+ // adjust the size of the editor to make room for the new button
+ LLRect rect = mInputEditor->getRect();
+ S32 editor_right = rect.mRight - t_btn_width;
+ rect.mRight = editor_right;
+ mInputEditor->reshape(rect.getWidth(), rect.getHeight(), FALSE);
+ mInputEditor->setRect(rect);
+
+ const S32 IMPANEL_PAD = 1 + LLPANEL_BORDER_WIDTH;
+ const S32 IMPANEL_INPUT_HEIGHT = 20;
+
+ rect.setLeftTopAndSize(
+ mRect.getWidth() - IMPANEL_PAD - p_btn_width - c_btn_width - t_btn_width - BTN_HPAD - RESIZE_HANDLE_WIDTH,
+ IMPANEL_INPUT_HEIGHT + IMPANEL_PAD - BTN_VPAD,
+ t_btn_width,
+ BTN_HEIGHT);
+
+ btn = new LLButton(
+ "Teleport Btn", rect,
+ "","", "",
+ &LLFloaterIMPanel::onTeleport, this,
+ font, teleport_label, teleport_label );
+
+ btn->setFollowsBottom();
+ btn->setFollowsRight();
+ addChild( btn );
+ }
+ btn->setEnabled(TRUE);
+ mLureID = lure_id;
+}
+
+void LLFloaterIMPanel::removeTeleportButton()
+{
+ // TODO -- purge the button
+ LLButton* btn = LLViewerUICtrlFactory::getButtonByName(this, "Teleport Btn");
+ if (btn)
+ {
+ btn->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLFloaterIMPanel::onTeleport(void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ if(self)
+ {
+ send_simple_im(self->mLureID,
+ "",
+ IM_LURE_911,
+ LLUUID::null);
+ self->removeTeleportButton();
+ }
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorFocusReceived( LLUICtrl* caller, void* userdata )
+{
+ LLFloaterIMPanel* self= (LLFloaterIMPanel*) userdata;
+ self->mHistoryEditor->setCursorAndScrollToEnd();
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorFocusLost(LLLineEditor* caller, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*) userdata;
+ self->setTyping(FALSE);
+}
+
+// static
+void LLFloaterIMPanel::onInputEditorKeystroke(LLLineEditor* caller, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+ LLString text = self->mInputEditor->getText();
+ if (!text.empty())
+ {
+ self->setTyping(TRUE);
+ }
+ else
+ {
+ // Deleting all text counts as stopping typing.
+ self->setTyping(FALSE);
+ }
+}
+
+void LLFloaterIMPanel::close(bool app_quitting)
+{
+ setTyping(FALSE);
+
+ if(mSessionUUID.notNull())
+ {
+ std::string name;
+ gAgent.buildFullname(name);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ "",
+ IM_ONLINE,
+ IM_SESSION_DROP,
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+ }
+ gIMView->removeSession(mSessionUUID);
+
+ destroy();
+}
+
+
+void LLFloaterIMPanel::sendMsg()
+{
+ LLWString text = mInputEditor->getWText();
+ LLWString::trim(text);
+ if (!gAgent.isGodlike()
+ && (mDialog == IM_NOTHING_SPECIAL)
+ && mOtherParticipantUUID.isNull())
+ {
+ llinfos << "Cannot send IM to everyone unless you're a god." << llendl;
+ return;
+ }
+ if(text.length() > 0)
+ {
+ // Truncate and convert to UTF8 for transport
+ std::string utf8_text = wstring_to_utf8str(text);
+ utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1);
+ std::string name;
+ gAgent.buildFullname(name);
+
+ const LLRelationship* info = NULL;
+ info = LLAvatarTracker::instance().getBuddyInfo(mOtherParticipantUUID);
+ U8 offline = (!info || info->isOnline()) ? IM_ONLINE : IM_OFFLINE;
+
+ // default to IM_SESSION_SEND unless it's nothing special - in
+ // which case it's probably an IM to everyone.
+ U8 dialog = (mDialog == IM_NOTHING_SPECIAL)
+ ? (U8)IM_NOTHING_SPECIAL : (U8)IM_SESSION_SEND;
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ utf8_text.c_str(),
+ offline,
+ (EInstantMessage)dialog,
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+
+ // local echo
+ if((mDialog == IM_NOTHING_SPECIAL) && (mOtherParticipantUUID.notNull()))
+ {
+ std::string history_echo;
+ gAgent.buildFullname(history_echo);
+
+ // Look for IRC-style emotes here.
+ char tmpstr[5];
+ strcpy(tmpstr,utf8_text.substr(0,4).c_str());
+ if (!strncmp(tmpstr, "/me ", 4) || !strncmp(tmpstr, "/me'", 4))
+ {
+ utf8_text.replace(0,3,"");
+ }
+ else
+ {
+ history_echo += ": ";
+ }
+ history_echo += utf8_text;
+ addHistoryLine(history_echo);
+ }
+
+ gViewerStats->incStat(LLViewerStats::ST_IM_COUNT);
+ }
+ mInputEditor->setText("");
+
+ // Don't need to actually send the typing stop message, the other
+ // client will infer it from receiving the message.
+ mTyping = FALSE;
+ mSentTypingState = TRUE;
+}
+
+
+void LLFloaterIMPanel::setTyping(BOOL typing)
+{
+ if (typing)
+ {
+ // Every time you type something, reset this timer
+ mLastKeystrokeTimer.reset();
+
+ if (!mTyping)
+ {
+ // You just started typing.
+ mFirstKeystrokeTimer.reset();
+
+ // Will send typing state after a short delay.
+ mSentTypingState = FALSE;
+ }
+ }
+ else
+ {
+ if (mTyping)
+ {
+ // you just stopped typing, send state immediately
+ sendTypingState(FALSE);
+ mSentTypingState = TRUE;
+ }
+ }
+
+ mTyping = typing;
+}
+
+void LLFloaterIMPanel::sendTypingState(BOOL typing)
+{
+ // Don't want to send typing indicators to multiple people, potentially too
+ // much network traffic. Only send in person-to-person IMs.
+ if (mDialog != IM_NOTHING_SPECIAL) return;
+
+ std::string name;
+ gAgent.buildFullname(name);
+
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ mOtherParticipantUUID,
+ name.c_str(),
+ "typing",
+ IM_ONLINE,
+ (typing ? IM_TYPING_START : IM_TYPING_STOP),
+ mSessionUUID);
+ gAgent.sendReliableMessage();
+}
+
+
+void LLFloaterIMPanel::processIMTyping(const LLIMInfo* im_info, BOOL typing)
+{
+ if (typing)
+ {
+ // other user started typing
+ addTypingIndicator(im_info);
+ }
+ else
+ {
+ // other user stopped typing
+ removeTypingIndicator();
+ }
+}
+
+
+void LLFloaterIMPanel::addTypingIndicator(const LLIMInfo* im_info)
+{
+ mTypingLineStartIndex = mHistoryEditor->getText().length();
+
+ LLUIString typing_start = sTypingStartString;
+ typing_start.setArg("[NAME]", im_info->mName);
+ bool log_to_file = false;
+ addHistoryLine(typing_start, LLColor4::grey, log_to_file);
+ mOtherTyping = TRUE;
+}
+
+
+void LLFloaterIMPanel::removeTypingIndicator()
+{
+ if (mOtherTyping)
+ {
+ // Must do this first, otherwise addHistoryLine calls us again.
+ mOtherTyping = FALSE;
+
+ S32 chars_to_remove = mHistoryEditor->getText().length() - mTypingLineStartIndex;
+ mHistoryEditor->removeTextFromEnd(chars_to_remove);
+ }
+}
+
+//static
+void LLFloaterIMPanel::chatFromLogFile(LLString line, void* userdata)
+{
+ LLFloaterIMPanel* self = (LLFloaterIMPanel*)userdata;
+
+ //self->addHistoryLine(line, LLColor4::grey, FALSE);
+ self->mHistoryEditor->appendColoredText(line, false, true, LLColor4::grey);
+
+}
diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h
new file mode 100644
index 0000000000..caed94ebc1
--- /dev/null
+++ b/indra/newview/llimpanel.h
@@ -0,0 +1,155 @@
+/**
+ * @file llimpanel.h
+ * @brief LLIMPanel class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_IMPANEL_H
+#define LL_IMPANEL_H
+
+#include "llfloater.h"
+#include "lluuid.h"
+#include "lldarray.h"
+#include "llinstantmessage.h"
+
+class LLLineEditor;
+class LLViewerTextEditor;
+class LLInventoryItem;
+class LLInventoryCategory;
+
+class LLFloaterIMPanel : public LLFloater
+{
+public:
+ // The session id is the id of the session this is for. The target
+ // refers to the user (or group) that where this session serves as
+ // the default. For example, if you open a session though a
+ // calling card, a new session id will be generated, but the
+ // target_id will be the agent referenced by the calling card.
+ LLFloaterIMPanel(const std::string& name, const LLRect& rect,
+ const std::string& session_label,
+ const LLUUID& session_id, const LLUUID& target_id,
+ EInstantMessage dialog);
+
+ /*virtual*/ BOOL postBuild();
+
+ // Check typing timeout timer.
+ /*virtual*/ void draw();
+
+ /*virtual*/ void close(bool app_quitting = FALSE);
+
+ // add target ids to the session.
+ // Return TRUE if successful, otherwise FALSE.
+ BOOL addParticipants(const LLDynamicArray<LLUUID>& agent_ids);
+
+ void addHistoryLine(const std::string &utf8msg,
+ const LLColor4& color = LLColor4::white,
+ bool log_to_file = true);
+ void setInputFocus( BOOL b );
+
+ void selectAll();
+ void selectNone();
+ void setVisible(BOOL b);
+
+ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept,
+ LLString& tooltip_msg);
+
+ static void onInputEditorFocusReceived( LLUICtrl* caller, void* userdata );
+ static void onInputEditorFocusLost(LLLineEditor* caller, void* userdata);
+ static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata);
+ static void onTabClick( void* userdata );
+
+ static void onClickProfile( void* userdata ); // Profile button pressed
+ static void onClickClose( void* userdata );
+
+ //const LLUUID& getItemUUID() const { return mItemUUID; }
+ const LLUUID& getSessionID() const { return mSessionUUID; }
+ const LLUUID& getOtherParticipantID() const { return mOtherParticipantUUID; }
+
+ // HACK -- for enabling a teleport button for helpers
+ static void onTeleport(void* userdata);
+ void addTeleportButton(const LLUUID& lure_id);
+ void removeTeleportButton();
+
+ // Handle other participant in the session typing.
+ void processIMTyping(const LLIMInfo* im_info, BOOL typing);
+ static void chatFromLogFile(LLString line, void* userdata);
+
+private:
+ // called by constructors
+ void init();
+
+ // Called by UI methods.
+ void sendMsg();
+
+ // for adding agents via the UI. Return TRUE if possible, do it if
+ BOOL dropCallingCard(LLInventoryItem* item, BOOL drop);
+ BOOL dropCategory(LLInventoryCategory* category, BOOL drop);
+
+ // test if local agent can add agents.
+ BOOL isAddAllowed() const;
+
+ // Called whenever the user starts or stops typing.
+ // Sends the typing state to the other user if necessary.
+ void setTyping(BOOL typing);
+
+ // Add the "User is typing..." indicator.
+ void addTypingIndicator(const LLIMInfo* im_info);
+
+ // Remove the "User is typing..." indicator.
+ void removeTypingIndicator();
+
+ void sendTypingState(BOOL typing);
+
+ static LLFloaterIMPanel* sInstance;
+
+private:
+ LLLineEditor* mInputEditor;
+ LLViewerTextEditor* mHistoryEditor;
+ std::string mSessionLabel;
+
+ // The value of the mSessionUUID depends on how the IM session was started:
+ // one-on-one ==> random id
+ // group ==> group_id
+ // inventory folder ==> folder item_id
+ // 911 ==> Gaurdian_Angel_Group_ID ^ gAgent.getID()
+ LLUUID mSessionUUID;
+
+ // The value mOtherParticipantUUID depends on how the IM session was started:
+ // one-on-one = recipient's id
+ // group ==> group_id
+ // inventory folder ==> first target id in list
+ // 911 ==> sender
+ LLUUID mOtherParticipantUUID;
+
+ // the lure ID for help IM sessions
+ LLUUID mLureID;
+
+ EInstantMessage mDialog;
+
+ // Are you currently typing?
+ BOOL mTyping;
+
+ // Is other user currently typing?
+ BOOL mOtherTyping;
+
+ // Where does the "User is typing..." line start?
+ S32 mTypingLineStartIndex;
+
+ BOOL mSentTypingState;
+
+ // Optimization: Don't send "User is typing..." until the
+ // user has actually been typing for a little while. Prevents
+ // extra IMs for brief "lol" type utterences.
+ LLFrameTimer mFirstKeystrokeTimer;
+
+ // Timer to detect when user has stopped typing.
+ LLFrameTimer mLastKeystrokeTimer;
+};
+
+
+#endif // LL_IMPANEL_H
diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp
new file mode 100644
index 0000000000..fb8438dc93
--- /dev/null
+++ b/indra/newview/llimview.cpp
@@ -0,0 +1,717 @@
+/**
+ * @file llimview.cpp
+ * @brief Container for Instant Messaging
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llimview.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llbutton.h"
+#include "llstring.h"
+#include "linked_lists.h"
+#include "llvieweruictrlfactory.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llviewerwindow.h"
+#include "llresmgr.h"
+#include "llfloaternewim.h"
+#include "llimpanel.h"
+#include "llresizebar.h"
+#include "lltabcontainer.h"
+#include "viewer.h"
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llkeyboard.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llcallingcard.h"
+#include "lltoolbar.h"
+
+const EInstantMessage EVERYONE_DIALOG = IM_NOTHING_SPECIAL;
+const EInstantMessage GROUP_DIALOG = IM_SESSION_GROUP_START;
+const EInstantMessage DEFAULT_DIALOG = IM_NOTHING_SPECIAL;
+
+//
+// Globals
+//
+LLIMView* gIMView = NULL;
+
+//
+// Statics
+//
+static LLString sOnlyUserMessage;
+static LLString sOfflineMessage;
+
+//
+// Helper Functions
+//
+
+// returns true if a should appear before b
+BOOL group_dictionary_sort( LLGroupData* a, LLGroupData* b )
+{
+ return (LLString::compareDict( a->mName, b->mName ) < 0);
+}
+
+
+// the other_participant_id is either an agent_id, a group_id, or an inventory
+// folder item_id (collection of calling cards)
+LLUUID compute_session_id(EInstantMessage dialog, const LLUUID& other_participant_id)
+{
+ LLUUID session_id;
+ if (IM_SESSION_GROUP_START == dialog)
+ {
+ // slam group session_id to the group_id (other_participant_id)
+ session_id = other_participant_id;
+ }
+ else
+ {
+ LLUUID agent_id = gAgent.getID();
+ if (other_participant_id == agent_id)
+ {
+ // if we try to send an IM to ourselves then the XOR would be null
+ // so we just make the session_id the same as the agent_id
+ session_id = agent_id;
+ }
+ else
+ {
+ // peer-to-peer or peer-to-asset session_id is the XOR
+ session_id = other_participant_id ^ agent_id;
+ }
+ }
+ return session_id;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// LLFloaterIM
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+LLFloaterIM::LLFloaterIM()
+{
+}
+
+BOOL LLFloaterIM::postBuild()
+{
+ requires("only_user_message", WIDGET_TYPE_TEXT_BOX);
+ requires("offline_message", WIDGET_TYPE_TEXT_BOX);
+
+ if (checkRequirements())
+ {
+ sOnlyUserMessage = childGetText("only_user_message");
+ sOfflineMessage = childGetText("offline_message");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+//// virtual
+//BOOL LLFloaterIM::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+//{
+// BOOL handled = FALSE;
+// if (getEnabled()
+// && mask == (MASK_CONTROL|MASK_SHIFT))
+// {
+// if (key == 'W')
+// {
+// LLFloater* floater = getActiveFloater();
+// if (floater)
+// {
+// if (mTabContainer->getTabCount() == 1)
+// {
+// // trying to close last tab, close
+// // entire window.
+// close();
+// handled = TRUE;
+// }
+// }
+// }
+// }
+// return handled || LLMultiFloater::handleKeyHere(key, mask, called_from_parent);
+//}
+
+void LLFloaterIM::onClose(bool app_quitting)
+{
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowIM", FALSE);
+ }
+ setVisible(FALSE);
+}
+
+//virtual
+void LLFloaterIM::addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point)
+{
+ // this code is needed to fix the bug where new IMs received will resize the IM floater.
+ // SL-29075, SL-24556, and others
+ LLRect parent_rect = getRect();
+ S32 dheight = LLFLOATER_HEADER_SIZE + TABCNTR_HEADER_HEIGHT;
+ LLRect rect(0, parent_rect.getHeight()-dheight, parent_rect.getWidth(), 0);
+ floaterp->reshape(rect.getWidth(), rect.getHeight(), TRUE);
+ LLMultiFloater::addFloater(floaterp, select_added_floater, insertion_point);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIMViewFriendObserver
+//
+// Bridge to suport knowing when the inventory has changed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIMViewFriendObserver : public LLFriendObserver
+{
+public:
+ LLIMViewFriendObserver(LLIMView* tv) : mTV(tv) {}
+ virtual ~LLIMViewFriendObserver() {}
+ virtual void changed(U32 mask)
+ {
+ if(mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE))
+ {
+ mTV->refresh();
+ }
+ }
+protected:
+ LLIMView* mTV;
+};
+
+
+//
+// Public Static Member Functions
+//
+
+// This is a helper function to determine what kind of im session
+// should be used for the given agent.
+// static
+EInstantMessage LLIMView::defaultIMTypeForAgent(const LLUUID& agent_id)
+{
+ EInstantMessage type = IM_SESSION_CARDLESS_START;
+ if(is_agent_friend(agent_id))
+ {
+ if(LLAvatarTracker::instance().isBuddyOnline(agent_id))
+ {
+ type = IM_SESSION_ADD;
+ }
+ else
+ {
+ type = IM_SESSION_OFFLINE_ADD;
+ }
+ }
+ return type;
+}
+
+// static
+//void LLIMView::onPinButton(void*)
+//{
+// BOOL state = gSavedSettings.getBOOL( "PinTalkViewOpen" );
+// gSavedSettings.setBOOL( "PinTalkViewOpen", !state );
+//}
+
+// static
+void LLIMView::toggle(void*)
+{
+ static BOOL return_to_mouselook = FALSE;
+
+ // Hide the button and show the floater or vice versa.
+ llassert( gIMView );
+ BOOL old_state = gIMView->getFloaterOpen();
+
+ // If we're in mouselook and we triggered the Talk View, we want to talk.
+ if( gAgent.cameraMouselook() && old_state )
+ {
+ return_to_mouselook = TRUE;
+ gAgent.changeCameraToDefault();
+ return;
+ }
+
+ BOOL new_state = !old_state;
+
+ if (new_state)
+ {
+ // ...making visible
+ if ( gAgent.cameraMouselook() )
+ {
+ return_to_mouselook = TRUE;
+ gAgent.changeCameraToDefault();
+ }
+ }
+ else
+ {
+ // ...hiding
+ if ( gAgent.cameraThirdPerson() && return_to_mouselook )
+ {
+ gAgent.changeCameraToMouselook();
+ }
+ return_to_mouselook = FALSE;
+ }
+
+ gIMView->setFloaterOpen( new_state );
+}
+
+//
+// Member Functions
+//
+
+LLIMView::LLIMView(const std::string& name, const LLRect& rect) :
+ LLView(name, rect, FALSE),
+ mFriendObserver(NULL),
+ mIMReceived(FALSE)
+{
+ gIMView = this;
+ mFriendObserver = new LLIMViewFriendObserver(this);
+ LLAvatarTracker::instance().addObserver(mFriendObserver);
+
+ mTalkFloater = new LLFloaterIM();
+ gUICtrlFactory->buildFloater(mTalkFloater, "floater_im.xml");
+
+ // New IM Panel
+ mNewIMFloater = new LLFloaterNewIM();
+ mTalkFloater->addFloater(mNewIMFloater, TRUE);
+
+ // Tabs sometimes overlap resize handle
+ mTalkFloater->moveResizeHandleToFront();
+}
+
+LLIMView::~LLIMView()
+{
+ LLAvatarTracker::instance().removeObserver(mFriendObserver);
+ delete mFriendObserver;
+ // Children all cleaned up by default view destructor.
+}
+
+EWidgetType LLIMView::getWidgetType() const
+{
+ return WIDGET_TYPE_TALK_VIEW;
+}
+
+LLString LLIMView::getWidgetTag() const
+{
+ return LL_TALK_VIEW_TAG;
+}
+
+// Add a message to a session.
+void LLIMView::addMessage(
+ const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const char* from,
+ const char* msg,
+ const char* session_name,
+ EInstantMessage dialog,
+ U32 parent_estate_id,
+ const LLUUID& region_id,
+ const LLVector3& position)
+{
+ LLFloaterIMPanel* floater;
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ new_session_id = compute_session_id(dialog, other_participant_id);
+ }
+ floater = findFloaterBySession(new_session_id);
+ if (!floater)
+ {
+ floater = findFloaterBySession(other_participant_id);
+ if (floater)
+ {
+ llinfos << "found the IM session " << session_id
+ << " by participant " << other_participant_id << llendl;
+ }
+ }
+ if(floater)
+ {
+ floater->addHistoryLine(msg);
+ }
+ else
+ {
+ const char* name = from;
+ if(session_name && (strlen(session_name)>1))
+ {
+ name = session_name;
+ }
+ floater = createFloater(new_session_id, other_participant_id, name, dialog, FALSE);
+
+ // When we get a new IM, and if you are a god, display a bit
+ // of information about the source. This is to help liaisons
+ // when answering questions.
+ if(gAgent.isGodlike())
+ {
+ // XUI:translate
+ std::ostringstream bonus_info;
+ bonus_info << "*** parent estate: "
+ << parent_estate_id
+ << ((parent_estate_id == 1) ? ", mainland" : "")
+ << ((parent_estate_id == 5) ? ", teen" : "");
+
+ // once we have web-services (or something) which returns
+ // information about a region id, we can print this out
+ // and even have it link to map-teleport or something.
+ //<< "*** region_id: " << region_id << std::endl
+ //<< "*** position: " << position << std::endl;
+
+ floater->addHistoryLine(bonus_info.str());
+ }
+
+ floater->addHistoryLine(msg);
+ make_ui_sound("UISndNewIncomingIMSession");
+ }
+
+ if( !mTalkFloater->getVisible() && !floater->getVisible())
+ {
+ //if the IM window is not open and the floater is not visible (i.e. not torn off)
+ LLFloater* previouslyActiveFloater = mTalkFloater->getActiveFloater();
+
+ // select the newly added floater (or the floater with the new line added to it).
+ // it should be there.
+ mTalkFloater->selectFloater(floater);
+
+ //there was a previously unseen IM, make that old tab flashing
+ //it is assumed that the most recently unseen IM tab is the one current selected/active
+ if ( previouslyActiveFloater && getIMReceived() )
+ {
+ mTalkFloater->setFloaterFlashing(previouslyActiveFloater, TRUE);
+ }
+
+ //notify of a new IM
+ notifyNewIM();
+ }
+}
+
+void LLIMView::notifyNewIM()
+{
+ if(!gIMView->getFloaterOpen())
+ {
+ mIMReceived = TRUE;
+ }
+}
+
+BOOL LLIMView::getIMReceived() const
+{
+ return mIMReceived;
+}
+
+// This method returns TRUE if the local viewer has a session
+// currently open keyed to the uuid.
+BOOL LLIMView::isIMSessionOpen(const LLUUID& uuid)
+{
+ LLFloaterIMPanel* floater = findFloaterBySession(uuid);
+ if(floater) return TRUE;
+ return FALSE;
+}
+
+// This adds a session to the talk view. The name is the local name of
+// the session, dialog specifies the type of session. If the session
+// exists, it is brought forward. Specifying id = NULL results in an
+// im session to everyone. Returns the uuid of the session.
+LLUUID LLIMView::addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id)
+{
+ LLUUID session_id = compute_session_id(dialog, other_participant_id);
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(!floater)
+ {
+ floater = createFloater(session_id, other_participant_id, name, dialog, TRUE);
+ LLDynamicArray<LLUUID> ids;
+ ids.put(other_participant_id);
+ noteOfflineUsers(floater, ids);
+ floater->addParticipants(ids);
+ mTalkFloater->showFloater(floater);
+ }
+ else
+ {
+ floater->open();
+ }
+ //mTabContainer->selectTabPanel(panel);
+ floater->setInputFocus(TRUE);
+ return floater->getSessionID();
+}
+
+// Adds a session using the given session_id. If the session already exists
+// the dialog type is assumed correct. Returns the uuid of the session.
+LLUUID LLIMView::addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& session_id,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ if (0 == ids.getLength())
+ {
+ return LLUUID::null;
+ }
+
+ LLUUID other_participant_id = ids[0];
+ LLUUID new_session_id = session_id;
+ if (new_session_id.isNull())
+ {
+ new_session_id = compute_session_id(dialog, other_participant_id);
+ }
+
+ LLFloaterIMPanel* floater = findFloaterBySession(new_session_id);
+ if(!floater)
+ {
+ // On creation, use the first element of ids as the "other_participant_id"
+ floater = createFloater(new_session_id, other_participant_id, name, dialog, TRUE);
+ noteOfflineUsers(floater, ids);
+ }
+ floater->addParticipants(ids);
+ mTalkFloater->showFloater(floater);
+ //mTabContainer->selectTabPanel(panel);
+ floater->setInputFocus(TRUE);
+ return floater->getSessionID();
+}
+
+// This removes the panel referenced by the uuid, and then restores
+// internal consistency. The internal pointer is not deleted.
+void LLIMView::removeSession(const LLUUID& session_id)
+{
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if(floater)
+ {
+ mFloaters.erase(floater->getHandle());
+ mTalkFloater->removeFloater(floater);
+ //mTabContainer->removeTabPanel(floater);
+ }
+}
+
+void LLIMView::refresh()
+{
+ S32 old_scroll_pos = mNewIMFloater->getScrollPos();
+ mNewIMFloater->clearAllTargets();
+
+ // build a list of groups.
+ LLLinkedList<LLGroupData> group_list( group_dictionary_sort );
+
+ LLGroupData* group;
+ S32 count = gAgent.mGroups.count();
+ S32 i;
+ // read/sort groups on the first pass.
+ for(i = 0; i < count; ++i)
+ {
+ group = &(gAgent.mGroups.get(i));
+ group_list.addDataSorted( group );
+ }
+
+ // add groups to the floater on the second pass.
+ for(group = group_list.getFirstData();
+ group;
+ group = group_list.getNextData())
+ {
+ mNewIMFloater->addTarget(group->mID, group->mName,
+ (void*)(&GROUP_DIALOG), TRUE, FALSE);
+ }
+
+ // build a set of buddies in the current buddy list.
+ LLCollectAllBuddies collector;
+ LLAvatarTracker::instance().applyFunctor(collector);
+ LLCollectAllBuddies::buddy_map_t::iterator it;
+ LLCollectAllBuddies::buddy_map_t::iterator end;
+ it = collector.mOnline.begin();
+ end = collector.mOnline.end();
+ for( ; it != end; ++it)
+ {
+ mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), TRUE);
+ }
+ it = collector.mOffline.begin();
+ end = collector.mOffline.end();
+ for( ; it != end; ++it)
+ {
+ mNewIMFloater->addAgent((*it).second, (void*)(&DEFAULT_DIALOG), FALSE);
+ }
+
+ if(gAgent.isGodlike())
+ {
+ // XUI:translate
+ mNewIMFloater->addTarget(LLUUID::null, "All Residents, All Grids",
+ (void*)(&EVERYONE_DIALOG), TRUE, FALSE);
+ }
+
+ mNewIMFloater->setScrollPos( old_scroll_pos );
+}
+
+// JC - This used to set console visibility. It doesn't any more.
+void LLIMView::setFloaterOpen(BOOL set_open)
+{
+ gSavedSettings.setBOOL("ShowIM", set_open);
+
+ //RN "visible" and "open" are considered synonomous for now
+ if (set_open)
+ {
+ mTalkFloater->open();
+ }
+ else
+ {
+ mTalkFloater->close();
+ }
+
+ if( set_open )
+ {
+ // notifyNewIM();
+
+ // We're showing the IM, so mark view as non-pending
+ mIMReceived = FALSE;
+ }
+}
+
+
+BOOL LLIMView::getFloaterOpen()
+{
+ return mTalkFloater->getVisible();
+}
+
+void LLIMView::pruneSessions()
+{
+ if(mNewIMFloater)
+ {
+ BOOL removed = TRUE;
+ LLFloaterIMPanel* floater = NULL;
+ while(removed)
+ {
+ removed = FALSE;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if(floater && !mNewIMFloater->isUUIDAvailable(floater->getOtherParticipantID()))
+ {
+ // remove this floater
+ removed = TRUE;
+ mFloaters.erase(handle_it++);
+ floater->close();
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+void LLIMView::disconnectAllSessions()
+{
+ if(mNewIMFloater)
+ {
+ mNewIMFloater->setEnabled(FALSE);
+ }
+ LLFloaterIMPanel* floater = NULL;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ floater = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if (floater)
+ {
+ floater->setEnabled(FALSE);
+ }
+ }
+}
+
+
+// This method returns the im panel corresponding to the uuid
+// provided. The uuid can either be a session id or an agent
+// id. Returns NULL if there is no matching panel.
+LLFloaterIMPanel* LLIMView::findFloaterBySession(const LLUUID& session_id)
+{
+ LLFloaterIMPanel* rv = NULL;
+ std::set<LLViewHandle>::iterator handle_it;
+ for(handle_it = mFloaters.begin();
+ handle_it != mFloaters.end();
+ ++handle_it)
+ {
+ rv = (LLFloaterIMPanel*)LLFloater::getFloaterByHandle(*handle_it);
+ if(rv && session_id == rv->getSessionID())
+ {
+ break;
+ }
+ rv = NULL;
+ }
+ return rv;
+}
+
+
+BOOL LLIMView::hasSession(const LLUUID& session_id)
+{
+ return (findFloaterBySession(session_id) != NULL);
+}
+
+
+// create a floater and update internal representation for
+// consistency. Returns the pointer, caller (the class instance since
+// it is a private method) is not responsible for deleting the
+// pointer. Add the floater to this but do not select it.
+LLFloaterIMPanel* LLIMView::createFloater(const LLUUID& session_id,
+ const LLUUID& other_participant_id,
+ const std::string& session_label,
+ EInstantMessage dialog,
+ BOOL user_initiated)
+{
+ if (session_id.isNull())
+ {
+ llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl;
+ }
+ llinfos << "LLIMView::createFloater: from " << other_participant_id
+ << " in session " << session_id << llendl;
+ LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label,
+ LLRect(),
+ session_label,
+ session_id,
+ other_participant_id,
+ dialog);
+ LLTabContainerCommon::eInsertionPoint i_pt = user_initiated ? LLTabContainerCommon::RIGHT_OF_CURRENT : LLTabContainerCommon::END;
+ mTalkFloater->addFloater(floater, FALSE, i_pt);
+ mFloaters.insert(floater->getHandle());
+ return floater;
+}
+
+void LLIMView::noteOfflineUsers(LLFloaterIMPanel* floater,
+ const LLDynamicArray<LLUUID>& ids)
+{
+ S32 count = ids.count();
+ if(count == 0)
+ {
+ floater->addHistoryLine(sOnlyUserMessage);
+ }
+ else
+ {
+ const LLRelationship* info = NULL;
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ for(S32 i = 0; i < count; ++i)
+ {
+ info = at.getBuddyInfo(ids.get(i));
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ if(info && !info->isOnline()
+ && gCacheName->getName(ids.get(i), first, last))
+ {
+ LLUIString offline = sOfflineMessage;
+ offline.setArg("[FIRST]", first);
+ offline.setArg("[LAST]", last);
+ floater->addHistoryLine(offline);
+ }
+ }
+ }
+}
+
+void LLIMView::processIMTypingStart(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, TRUE);
+}
+
+void LLIMView::processIMTypingStop(const LLIMInfo* im_info)
+{
+ processIMTypingCore(im_info, FALSE);
+}
+
+void LLIMView::processIMTypingCore(const LLIMInfo* im_info, BOOL typing)
+{
+ LLUUID session_id = compute_session_id(im_info->mIMType, im_info->mFromID);
+ LLFloaterIMPanel* floater = findFloaterBySession(session_id);
+ if (floater)
+ {
+ floater->processIMTyping(im_info, typing);
+ }
+}
diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h
new file mode 100644
index 0000000000..3be1dace1a
--- /dev/null
+++ b/indra/newview/llimview.h
@@ -0,0 +1,150 @@
+/**
+ * @file llimview.h
+ * @brief Container for Instant Messaging
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLIMVIEW_H
+#define LL_LLIMVIEW_H
+
+#include "llfloater.h"
+#include "llinstantmessage.h"
+#include "lluuid.h"
+
+class LLFloaterNewIM;
+class LLUUID;
+class LLFloaterIMPanel;
+class LLFriendObserver;
+class LLFloaterIM;
+
+class LLIMView : public LLView
+{
+public:
+ LLIMView(const std::string& name, const LLRect& rect);
+ ~LLIMView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // Add a message to a session. The session can keyed to sesion id
+ // or agent id.
+ void addMessage(const LLUUID& session_id, const LLUUID& target_id,
+ const char* from, const char* msg,
+ const char* session_name = NULL,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ U32 parent_estate_id = 0,
+ const LLUUID& region_id = LLUUID::null,
+ const LLVector3& position = LLVector3::zero);
+
+ // This method returns TRUE if the local viewer has a session
+ // currently open keyed to the uuid. The uuid can be keyed by
+ // either session id or agent id.
+ BOOL isIMSessionOpen(const LLUUID& uuid);
+
+ // This adds a session to the talk view. The name is the local
+ // name of the session, dialog specifies the type of
+ // session. Since sessions can be keyed off of first recipient or
+ // initiator, the session can be matched against the id
+ // provided. If the session exists, it is brought forward. This
+ // method accepts a group id or an agent id. Specifying id = NULL
+ // results in an im session to everyone. Returns the uuid of the
+ // session.
+ LLUUID addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& other_participant_id);
+
+ // Adds a session using the given session_id. If the session already exists
+ // the dialog type is assumed correct. Returns the uuid of the session.
+ LLUUID addSession(const std::string& name,
+ EInstantMessage dialog,
+ const LLUUID& session_id,
+ const LLDynamicArray<LLUUID>& ids);
+
+ // This removes the panel referenced by the uuid, and then
+ // restores internal consistency. The internal pointer is not
+ // deleted.
+ void removeSession(const LLUUID& session_id);
+
+ void processIMTypingStart(const LLIMInfo* im_info);
+ void processIMTypingStop(const LLIMInfo* im_info);
+
+ // Rebuild stuff
+ void refresh();
+
+ void notifyNewIM();
+
+ // IM received that you haven't seen yet
+ BOOL getIMReceived() const;
+
+ void setFloaterOpen(BOOL open);
+ BOOL getFloaterOpen();
+
+ LLFloaterIM * getFloater() { return mTalkFloater; }
+
+ // close any sessions which are not available in the newimpanel.
+ void pruneSessions();
+
+ // This method is used to go through all active sessions and
+ // disable all of them. This method is usally called when you are
+ // forced to log out or similar situations where you do not have a
+ // good connection.
+ void disconnectAllSessions();
+
+ static void toggle(void*);
+
+ // This is a helper function to determine what kind of im session
+ // should be used for the given agent.
+ static EInstantMessage defaultIMTypeForAgent(const LLUUID& agent_id);
+
+ BOOL hasSession(const LLUUID& session_id);
+
+ // This method returns the im panel corresponding to the uuid
+ // provided. The uuid must be a session id. Returns NULL if there
+ // is no matching panel.
+ LLFloaterIMPanel* findFloaterBySession(const LLUUID& session_id);
+
+private:
+ // create a panel and update internal representation for
+ // consistency. Returns the pointer, caller (the class instance
+ // since it is a private method) is not responsible for deleting
+ // the pointer.
+ LLFloaterIMPanel* createFloater(const LLUUID& session_id, const LLUUID& target_id,
+ const std::string& name, EInstantMessage dialog, BOOL user_initiated = FALSE);
+
+ // This simple method just iterates through all of the ids, and
+ // prints a simple message if they are not online. Used to help
+ // reduce 'hello' messages to the linden employees unlucky enough
+ // to have their calling card in the default inventory.
+ void noteOfflineUsers(LLFloaterIMPanel* panel, const LLDynamicArray<LLUUID>& ids);
+
+ void processIMTypingCore(const LLIMInfo* im_info, BOOL typing);
+
+public:
+ LLFloaterIM* mTalkFloater;
+ LLFloaterNewIM* mNewIMFloater;
+
+private:
+ std::set<LLViewHandle> mFloaters;
+ LLFriendObserver* mFriendObserver;
+
+ // An IM has been received that you haven't seen yet.
+ BOOL mIMReceived;
+};
+
+
+class LLFloaterIM : public LLMultiFloater
+{
+public:
+ LLFloaterIM();
+ ///*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ BOOL postBuild();
+ /*virtual*/ void onClose(bool app_quitting);
+ /*virtual*/ void addFloater(LLFloater* floaterp, BOOL select_added_floater, LLTabContainer::eInsertionPoint insertion_point = LLTabContainerCommon::END);
+};
+
+// Globals
+extern LLIMView *gIMView;
+
+#endif // LL_LLIMView_H
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
new file mode 100755
index 0000000000..ed9209935e
--- /dev/null
+++ b/indra/newview/llinventorybridge.cpp
@@ -0,0 +1,4405 @@
+/**
+ * @file llinventorybridge.cpp
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <utility> // for std::pair<>
+
+#include "llinventoryview.h"
+#include "llinventorybridge.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h" // for radio buttons
+#include "llradiogroup.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "llui.h"
+
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterproperties.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llfolderview.h"
+#include "llgesturemgr.h"
+#include "lliconctrl.h"
+#include "llinventorymodel.h"
+#include "llinventoryclipboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llpreviewanim.h"
+#include "llpreviewgesture.h"
+#include "llpreviewlandmark.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewscript.h"
+#include "llpreviewsound.h"
+#include "llpreviewtexture.h"
+#include "llresmgr.h"
+#include "llscrollcontainer.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "viewer.h"
+#include "llviewermessage.h"
+#include "llviewerregion.h"
+#include "lltabcontainer.h"
+#include "llvieweruictrlfactory.h"
+#include "llselectmgr.h"
+#include "llfloateropenobject.h"
+
+// Helpers
+// bug in busy count inc/dec right now, logic is complex... do we really need it?
+void inc_busy_count()
+{
+// gViewerWindow->getWindow()->incBusyCount();
+}
+void dec_busy_count()
+{
+// gViewerWindow->getWindow()->decBusyCount();
+}
+
+// Function declarations
+struct LLWearableHoldingPattern;
+void wear_inventory_category_on_avatar(LLInventoryCategory* category, BOOL append);
+void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata);
+void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void*);
+void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append);
+void remove_inventory_category_from_avatar(LLInventoryCategory* category);
+void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata);
+void move_task_inventory_callback(S32 option, void* user_data);
+void confirm_replace_attachment_rez(S32 option, void* user_data);
+
+// TomY XUI: translate
+const char* FIND_HINT = "Start typing to select an item by name";
+const char* NAME_SEARCH_DESC = "Find items whose name contains (leave blank for all):";
+const char* NEW_LSL_NAME = "New Script";
+const char* NEW_NOTECARD_NAME = "New Note";
+const char* NEW_GESTURE_NAME = "New Gesture";
+
+const char* ICON_NAME[ICON_NAME_COUNT] =
+{
+ "inv_item_texture.tga",
+ "inv_item_sound.tga",
+ "inv_item_callingcard_online.tga",
+ "inv_item_callingcard_offline.tga",
+ "inv_item_landmark.tga",
+ "inv_item_landmark_visited.tga",
+ "inv_item_script.tga",
+ "inv_item_clothing.tga",
+ "inv_item_object.tga",
+ "inv_item_notecard.tga",
+ "inv_item_bodypart.tga",
+ "inv_item_snapshot.tga",
+
+ "inv_item_shape.tga",
+ "inv_item_bodypart.tga",
+ "inv_item_hair.tga",
+ "inv_item_eyes.tga",
+ "inv_item_shirt.tga",
+ "inv_item_pants.tga",
+ "inv_item_shoes.tga",
+ "inv_item_socks.tga",
+ "inv_item_jacket.tga",
+ "inv_item_gloves.tga",
+ "inv_item_undershirt.tga",
+ "inv_item_underpants.tga",
+ "inv_item_skirt.tga",
+
+ "inv_item_animation.tga",
+ "inv_item_gesture.tga",
+};
+
+struct LLWearInfo
+{
+ LLUUID mCategoryID;
+ BOOL mAppend;
+};
+
+BOOL gAddToOutfit = FALSE;
+
+// +=================================================+
+// | LLInvFVBridge |
+// +=================================================+
+
+const LLString& LLInvFVBridge::getName() const
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return obj->getName();
+ }
+ return LLString::null;
+}
+
+const LLString& LLInvFVBridge::getDisplayName() const
+{
+ return getName();
+}
+
+// Folders have full perms
+PermissionMask LLInvFVBridge::getPermissionMask() const
+{
+
+ return PERM_ALL;
+}
+
+// Folders don't have creation dates.
+U32 LLInvFVBridge::getCreationDate() const
+{
+ return 0;
+}
+
+// Can be destoryed (or moved to trash)
+BOOL LLInvFVBridge::isItemRemovable()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ if(model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// Can be moved to another folder
+BOOL LLInvFVBridge::isItemMovable()
+{
+ return TRUE;
+}
+
+//FIXME: make sure this does the right thing
+void LLInvFVBridge::showProperties()
+{
+ LLShowProps::showProperties(mUUID);
+}
+
+void LLInvFVBridge::removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ removeBatchNoCheck(batch);
+}
+
+void LLInvFVBridge::removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch)
+{
+ // this method moves a bunch of items and folders to the trash. As
+ // per design guidelines for the inventory model, the message is
+ // built and the accounting is performed first. After all of that,
+ // we call LLInventoryModel::moveObject() to move everything
+ // around.
+ LLInvFVBridge* bridge;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLMessageSystem* msg = gMessageSystem;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ std::vector<LLUUID> move_ids;
+ LLInventoryModel::update_map_t update;
+ bool start_new_message = true;
+ S32 count = batch.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ item = (LLViewerInventoryItem*)model->getItem(bridge->getUUID());
+ if(item)
+ {
+ if(item->getParentUUID() == trash_id) continue;
+ move_ids.push_back(item->getUUID());
+ LLPreview::hide(item->getUUID());
+ --update[item->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, trash_id);
+ msg->addString("NewName", NULL);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ for(i = 0; i < count; ++i)
+ {
+ bridge = (LLInvFVBridge*)(batch.get(i));
+ if(!bridge || !bridge->isItemRemovable()) continue;
+ cat = (LLViewerInventoryCategory*)model->getCategory(bridge->getUUID());
+ if(cat)
+ {
+ if(cat->getParentUUID() == trash_id) continue;
+ move_ids.push_back(cat->getUUID());
+ --update[cat->getParentUUID()];
+ ++update[trash_id];
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOL("Stamp", TRUE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat->getUUID());
+ msg->addUUIDFast(_PREHASH_ParentID, trash_id);
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ update.clear();
+ }
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ gInventory.accountForUpdate(update);
+ }
+
+ // move everything.
+ std::vector<LLUUID>::iterator it = move_ids.begin();
+ std::vector<LLUUID>::iterator end = move_ids.end();
+ for(; it != end; ++it)
+ {
+ gInventory.moveObject((*it), trash_id);
+ }
+
+ // notify inventory observers.
+ model->notifyObservers();
+}
+
+BOOL LLInvFVBridge::isClipboardPasteable() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ BOOL is_agent_inventory = model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID());
+
+ if(LLInventoryClipboard::instance().hasContents() && is_agent_inventory)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void hideContextEntries(LLMenuGL& menu,
+ const std::vector<LLString> &entries_to_show,
+ const std::vector<LLString> &disabled_entries)
+{
+ const LLView::child_list_t *list = menu.getChildList();
+
+ LLView::child_list_t::const_iterator itor;
+ for (itor = list->begin(); itor != list->end(); ++itor)
+ {
+ LLString name = (*itor)->getName();
+ bool found = false;
+ std::vector<LLString>::const_iterator itor2;
+ for (itor2 = entries_to_show.begin(); itor2 != entries_to_show.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ found = true;
+ }
+ }
+ if (!found)
+ {
+ (*itor)->setVisible(FALSE);
+ }
+ else
+ {
+ for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2)
+ {
+ if (*itor2 == name)
+ {
+ (*itor)->setEnabled(FALSE);
+ }
+ }
+ }
+ }
+}
+
+// Helper for commonly-used entries
+void LLInvFVBridge::getClipboardEntries(bool show_asset_id, std::vector<LLString> &items,
+ std::vector<LLString> &disabled_items, U32 flags)
+{
+ items.push_back("Rename");
+ if (!isItemRenameable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Rename");
+ }
+
+ if (show_asset_id)
+ {
+ items.push_back("Copy Asset UUID");
+ if ( (! ( isItemPermissive() || gAgent.isGodlike() ) )
+ || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Copy Asset UUID");
+ }
+ }
+
+ items.push_back("Copy Separator");
+
+ items.push_back("Copy");
+ if (!isItemCopyable())
+ {
+ disabled_items.push_back("Copy");
+ }
+
+ items.push_back("Paste");
+ if (!isClipboardPasteable() || (flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Paste");
+ }
+
+ items.push_back("Paste Separator");
+
+ items.push_back("Delete");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Delete");
+ }
+}
+
+void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLInvFVBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+//FIXME: remove this
+BOOL LLInvFVBridge::startDrag(EDragAndDropType* type, LLUUID* id)
+{
+ BOOL rv = FALSE;
+
+ LLInventoryObject* obj = getInventoryObject();
+
+ if(obj)
+ {
+ *type = LLAssetType::lookupDragAndDropType(obj->getType());
+ if(*type == DAD_NONE)
+ {
+ return FALSE;
+ }
+
+ *id = obj->getUUID();
+ //object_ids.put(obj->getUUID());
+
+ if (*type == DAD_CATEGORY)
+ {
+ gInventory.startBackgroundFetch(obj->getUUID());
+ }
+
+ rv = TRUE;
+ }
+
+ return rv;
+}
+
+LLInventoryObject* LLInvFVBridge::getInventoryObject() const
+{
+ LLInventoryObject* obj = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ obj = (LLInventoryObject*)model->getObject(mUUID);
+ }
+ return obj;
+}
+
+BOOL LLInvFVBridge::isInTrash() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ return model->isObjectDescendentOf(mUUID, trash_id);
+}
+
+BOOL LLInvFVBridge::isAgentInventory() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ if(gAgent.getInventoryRootID() == mUUID) return TRUE;
+ return model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID());
+}
+
+BOOL LLInvFVBridge::isItemPermissive() const
+{
+ return FALSE;
+}
+
+// static
+void LLInvFVBridge::changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(item->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(new_parent);
+ new_item->updateParentOnServer(restamp);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+}
+
+// static
+void LLInvFVBridge::changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* cat,
+ const LLUUID& new_parent,
+ BOOL restamp)
+{
+ if(cat->getParentUUID() != new_parent)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(cat->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(new_parent, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->setParent(new_parent);
+ new_cat->updateParentOnServer(restamp);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+}
+
+
+const char* safe_inv_type_lookup(LLInventoryType::EType inv_type)
+{
+ const char* rv = LLInventoryType::lookup(inv_type);
+ if(!rv)
+ {
+ const char* INVALID_TYPE = "<invalid>";
+ rv = INVALID_TYPE;
+ }
+ return rv;
+}
+
+LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags)
+{
+ LLInvFVBridge* new_listener = NULL;
+ switch(asset_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(!(inv_type == LLInventoryType::IT_TEXTURE || inv_type == LLInventoryType::IT_SNAPSHOT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLTextureBridge(inventory, uuid, inv_type);
+ break;
+
+ case LLAssetType::AT_SOUND:
+ if(!(inv_type == LLInventoryType::IT_SOUND))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLSoundBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LANDMARK:
+ if(!(inv_type == LLInventoryType::IT_LANDMARK))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLandmarkBridge(inventory, uuid, flags);
+ break;
+
+ case LLAssetType::AT_CALLINGCARD:
+ if(!(inv_type == LLInventoryType::IT_CALLINGCARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLCallingCardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_SCRIPT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLScriptBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ if(!(inv_type == LLInventoryType::IT_OBJECT || inv_type == LLInventoryType::IT_ATTACHMENT))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLObjectBridge(inventory, uuid, inv_type, flags);
+ break;
+
+ case LLAssetType::AT_NOTECARD:
+ if(!(inv_type == LLInventoryType::IT_NOTECARD))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLNotecardBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_ANIMATION:
+ if(!(inv_type == LLInventoryType::IT_ANIMATION))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLAnimationBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_GESTURE:
+ if(!(inv_type == LLInventoryType::IT_GESTURE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLGestureBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_LSL_TEXT:
+ if(!(inv_type == LLInventoryType::IT_LSL))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLLSLTextBridge(inventory, uuid);
+ break;
+
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ if(!(inv_type == LLInventoryType::IT_WEARABLE))
+ {
+ llwarns << LLAssetType::lookup(asset_type) << " asset has inventory type " << safe_inv_type_lookup(inv_type) << " on uuid " << uuid << llendl;
+ }
+ new_listener = new LLWearableBridge(inventory, uuid, asset_type, inv_type, (EWearableType)flags);
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ new_listener = new LLFolderBridge(inventory, uuid);
+ break;
+
+ default:
+ llinfos << "Unhandled asset type (llassetstorage.h): "
+ << (S32)asset_type << llendl;
+ break;
+ }
+
+ new_listener->mInvType = inv_type;
+ return new_listener;
+}
+
+// +=================================================+
+// | LLItemBridge |
+// +=================================================+
+
+void LLItemBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ }
+ else if ("purge" == action)
+ {
+ LLInventoryCategory* cat = model->getCategory(mUUID);
+ if(cat)
+ {
+ model->purgeDescendentsOf(mUUID);
+ }
+ LLInventoryObject* obj = model->getObject(mUUID);
+ if(!obj) return;
+ obj->removeFromServer();
+ model->deleteObject(mUUID);
+ model->notifyObservers();
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ }
+ else if ("copy_uuid" == action)
+ {
+ // Single item only
+ LLInventoryItem* item = model->getItem(mUUID);
+ if(!item) return;
+ LLUUID asset_id = item->getAssetUUID();
+ char buffer[UUID_STR_LENGTH];
+ asset_id.toString(buffer);
+
+ gViewerWindow->mWindow->copyTextToClipboard(utf8str_to_wstring(buffer));
+ return;
+ }
+ else if ("copy" == action)
+ {
+ copyToClipboard();
+ return;
+ }
+ else if ("paste" == action)
+ {
+ // Single item only
+ LLInventoryItem* itemp = model->getItem(mUUID);
+ if (!itemp) return;
+
+ LLFolderViewItem* folder_view_itemp = folder->getItemByID(itemp->getParentUUID());
+ if (!folder_view_itemp) return;
+
+ folder_view_itemp->getListener()->pasteFromClipboard();
+ return;
+ }
+}
+
+void LLItemBridge::selectItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item && !item->isComplete())
+ {
+ item->fetchFromServer();
+ }
+}
+
+void LLItemBridge::restoreItem()
+{
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ if(item)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ LLUUID new_parent = model->findCategoryUUIDForType(item->getType());
+ // do not restamp on restore.
+ LLInvFVBridge::changeItemParent(model, item, new_parent, FALSE);
+ }
+}
+
+LLViewerImage* LLItemBridge::getIcon() const
+{
+ LLString uuid_string = gViewerArt.getString(ICON_NAME[OBJECT_ICON_NAME]);
+ return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE);
+}
+
+PermissionMask LLItemBridge::getPermissionMask() const
+{
+ LLViewerInventoryItem* item = getItem();
+ PermissionMask perm_mask = 0;
+ if(item)
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ if (copy) perm_mask |= PERM_COPY;
+ if (mod) perm_mask |= PERM_MODIFY;
+ if (xfer) perm_mask |= PERM_TRANSFER;
+
+ }
+ return perm_mask;
+}
+
+const LLString& LLItemBridge::getDisplayName() const
+{
+ if(mDisplayName.empty())
+ {
+ buildDisplayName(getItem(), mDisplayName);
+ }
+ return mDisplayName;
+}
+
+void LLItemBridge::buildDisplayName(LLInventoryItem* item, LLString& name)
+{
+ if(item)
+ {
+ name.assign(item->getName());
+ }
+ else
+ {
+ name.assign(LLString::null);
+ }
+}
+
+LLString LLItemBridge::getLabelSuffix() const
+{
+ LLString suffix;
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ // it's a bit confusing to put nocopy/nomod/etc on calling cards.
+ if(LLAssetType::AT_CALLINGCARD != item->getType()
+ && item->getPermissions().getOwner() == gAgent.getID())
+ {
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+ const char* EMPTY = "";
+ const char* NO_COPY = " (no copy)";
+ const char* NO_MOD = " (no modify)";
+ const char* NO_XFER = " (no transfer)";
+ const char* scopy;
+ if(copy) scopy = EMPTY;
+ else scopy = NO_COPY;
+ const char* smod;
+ if(mod) smod = EMPTY;
+ else smod = NO_MOD;
+ const char* sxfer;
+ if(xfer) sxfer = EMPTY;
+ else sxfer = NO_XFER;
+ char buffer[MAX_STRING];
+ snprintf(
+ buffer,
+ MAX_STRING,
+ "%s%s%s",
+ scopy,
+ smod,
+ sxfer);
+ suffix.assign(buffer);
+ }
+ }
+ return suffix;
+}
+
+U32 LLItemBridge::getCreationDate() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return item->getCreationDate();
+ }
+ return 0;
+}
+
+
+BOOL LLItemBridge::isItemRenameable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ return (item->getPermissions().allowModifyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+
+BOOL LLItemBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLPreview::rename(mUUID, getPrefix() + new_name);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+
+BOOL LLItemBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ // restamp on move to trash.
+ LLInvFVBridge::changeItemParent(model, item, trash_id, TRUE);
+ }
+
+ // return false anyway, so that if it's called from the folder
+ // view, it doesn't remove the view - it's just being moved to the
+ // trash.
+ return FALSE;
+}
+
+BOOL LLItemBridge::isItemCopyable() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ return (item->getPermissions().allowCopyBy(gAgent.getID()));
+ }
+ return FALSE;
+}
+BOOL LLItemBridge::copyToClipboard() const
+{
+ if(isItemCopyable())
+ {
+ LLInventoryClipboard::instance().add(mUUID);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+LLViewerInventoryItem* LLItemBridge::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ item = (LLViewerInventoryItem*)model->getItem(mUUID);
+ }
+ return item;
+}
+
+BOOL LLItemBridge::isItemPermissive() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ U32 mask = item->getPermissions().getMaskBase();
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// +=================================================+
+// | LLFolderBridge |
+// +=================================================+
+
+LLFolderBridge* LLFolderBridge::sSelf=NULL;
+
+// Can be moved to another folder
+BOOL LLFolderBridge::isItemMovable()
+{
+ LLInventoryObject* obj = getInventoryObject();
+ if(obj)
+ {
+ return (LLAssetType::AT_NONE == ((LLInventoryCategory*)obj)->getPreferredType());
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::selectItem()
+{
+}
+
+
+// Can be destroyed (or moved to trash)
+BOOL LLFolderBridge::isItemRemovable()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model)
+ {
+ return FALSE;
+ }
+
+ if(!model->isObjectDescendentOf(mUUID, gAgent.getInventoryRootID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ LLInventoryCategory* category = model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ return FALSE;
+ }
+
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ return FALSE;
+ }
+ }
+
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgent.isWearingItem( item->getUUID() ) )
+ {
+ return FALSE;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLFolderBridge::isUpToDate() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ if( !category )
+ {
+ return FALSE;
+ }
+
+ return category->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN;
+}
+
+BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
+ BOOL drop)
+{
+ // This should never happen, but if an inventory item is incorrectly parented,
+ // the UI will get confused and pass in a NULL.
+ if(!inv_cat) return FALSE;
+
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ // check to make sure source is agent inventory, and is represented there.
+ LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource();
+ BOOL is_agent_inventory = (model->getCategory(inv_cat->getUUID()) != NULL)
+ && (LLToolDragAndDrop::SOURCE_AGENT == source);
+
+ BOOL accept = FALSE;
+ S32 i;
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ if(is_agent_inventory)
+ {
+ const LLUUID& cat_id = inv_cat->getUUID();
+
+ // Is the destination the trash?
+ LLUUID trash_id;
+ trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id)
+ || model->isObjectDescendentOf(mUUID, trash_id);
+ BOOL is_movable = (LLAssetType::AT_NONE == inv_cat->getPreferredType());
+ if( is_movable )
+ {
+ gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
+
+ for( i = 0; i < descendent_categories.count(); i++ )
+ {
+ LLInventoryCategory* category = descendent_categories[i];
+ if( LLAssetType::AT_NONE != category->getPreferredType() )
+ {
+ // ...can't move "special folders" like Textures
+ is_movable = FALSE;
+ break;
+ }
+ }
+
+ if( is_movable )
+ {
+ if( move_is_into_trash )
+ {
+ for( i = 0; i < descendent_items.count(); i++ )
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if( (item->getType() == LLAssetType::AT_CLOTHING) ||
+ (item->getType() == LLAssetType::AT_BODYPART) )
+ {
+ if( gAgent.isWearingItem( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ else
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ if( avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ is_movable = FALSE; // It's generally movable, but not into the trash!
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ accept = is_movable
+ && (mUUID != cat_id) // Can't move a folder into itself
+ && (mUUID != inv_cat->getParentUUID()) // Avoid moves that would change nothing
+ && !(model->isObjectDescendentOf(mUUID, cat_id)); // Avoid circularity
+ if(accept && drop)
+ {
+ // Look for any gestures and deactivate them
+ if (move_is_into_trash)
+ {
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(item->getUUID());
+ }
+ }
+ }
+
+ // Reparent the folder and restamp children if it's moving
+ // into trash.
+ LLInvFVBridge::changeCategoryParent(
+ model,
+ (LLViewerInventoryCategory*)inv_cat,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // content category has same ID as object itself
+ LLUUID object_id = inv_cat->getUUID();
+ LLUUID category_id = mUUID;
+ accept = move_inv_category_world_to_agent(object_id, category_id, drop);
+ }
+ return accept;
+}
+
+void warn_move_inventory(LLViewerObject* object, LLMoveInv* move_inv)
+{
+ const char* dialog = NULL;
+ if (object->flagScripted())
+ {
+ dialog = "MoveInventoryFromScriptedObject";
+ }
+ else
+ {
+ dialog = "MoveInventoryFromObject";
+ }
+ gViewerWindow->alertXml(dialog, move_task_inventory_callback, move_inv);
+}
+
+// Move/copy all inventory items from the Contents folder of an in-world
+// object to the agent's inventory, inside a given category.
+BOOL move_inv_category_world_to_agent(const LLUUID& object_id,
+ const LLUUID& category_id,
+ BOOL drop,
+ void (*callback)(S32, void*),
+ void* user_data)
+{
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ // content category has same ID as object itself
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // this folder is coming from an object, as there is only one folder in an object, the root,
+ // we need to collect the entire contents and handle them as a group
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+
+ if (inventory_objects.empty())
+ {
+ llinfos << "Object contents not found for drop." << llendl;
+ return FALSE;
+ }
+
+ BOOL accept = TRUE;
+ BOOL is_move = FALSE;
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ InventoryObjectList::iterator it = inventory_objects.begin();
+ InventoryObjectList::iterator end = inventory_objects.end();
+ for ( ; it != end; ++it)
+ {
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(((LLInventoryItem*)((LLInventoryObject*)(*it)))->getPermissions());
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ else
+ {
+ accept = FALSE;
+ break;
+ }
+ }
+
+ if(drop && accept)
+ {
+ it = inventory_objects.begin();
+ InventoryObjectList::iterator first_it = inventory_objects.begin();
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = object_id;
+ move_inv->mCategoryID = category_id;
+ move_inv->mCallback = callback;
+ move_inv->mUserData = user_data;
+
+ for ( ; it != end; ++it)
+ {
+ two_uuids_t two(category_id, (*it)->getUUID());
+ move_inv->mMoveList.push_back(two);
+ }
+
+ if(is_move)
+ {
+ // Callback called from within here.
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ move_task_inventory_callback(0, (void*)(move_inv));
+ }
+ }
+ return accept;
+}
+
+class LLFindWearables : public LLInventoryCollectFunctor
+{
+public:
+ LLFindWearables() {}
+ virtual ~LLFindWearables() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+bool LLFindWearables::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CLOTHING)
+ || (item->getType() == LLAssetType::AT_BODYPART))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLRightClickInventoryFetchObserver() {};
+ LLRightClickInventoryFetchObserver(const LLUUID& cat_id, bool copy_items) :
+ mCatID(cat_id),
+ mCopyItems(copy_items)
+ { };
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFolderBridge::staticFolderOptionsMenu();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+
+};
+
+//Used by LLFolderBridge as callback for directory recursion.
+class LLRightClickInventoryFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLRightClickInventoryFetchDescendentsObserver(bool copy_items) : mCopyItems(copy_items) {}
+ ~LLRightClickInventoryFetchDescendentsObserver() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+};
+
+void LLRightClickInventoryFetchDescendentsObserver::done()
+{
+ // What we do here is get the complete information on the items in
+ // the library, and set up an observer that will wait for that to
+ // happen.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+#if 0
+ // This early causes a giant menu to get produced, and doesn't seem to be needed.
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+#endif
+
+ LLRightClickInventoryFetchObserver* outfit;
+ outfit = new LLRightClickInventoryFetchObserver(mCompleteFolders.front(), mCopyItems);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ outfit->done(); //Not interested in waiting and this will be right 99% of the time.
+//Uncomment the following code for laggy Inventory UI.
+/* if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }*/
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryWearObserver
+//
+// Observer for "copy and wear" operation to support knowing
+// when the all of the contents have been added to inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryCopyAndWearObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCopyAndWearObserver(const LLUUID& cat_id, int count) :mCatID(cat_id), mContentsCount(count), mFolderAdded(FALSE) {}
+ virtual ~LLInventoryCopyAndWearObserver() {}
+ virtual void changed(U32 mask);
+
+protected:
+ LLUUID mCatID;
+ int mContentsCount;
+ BOOL mFolderAdded;
+};
+
+
+
+void LLInventoryCopyAndWearObserver::changed(U32 mask)
+{
+ if((mask & (LLInventoryObserver::ADD)) != 0)
+ {
+ if (!mFolderAdded)
+ {
+ const std::set<LLUUID>& changed_items = gInventory.getChangedIDs();
+
+ std::set<LLUUID>::const_iterator id_it = changed_items.begin();
+ std::set<LLUUID>::const_iterator id_end = changed_items.end();
+ for (;id_it != id_end; ++id_it)
+ {
+ if ((*id_it) == mCatID)
+ {
+ mFolderAdded = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (mFolderAdded)
+ {
+ LLViewerInventoryCategory* category = gInventory.getCategory(mCatID);
+
+ if (category->getDescendentCount() == mContentsCount)
+ {
+ gInventory.removeObserver(this);
+ wear_inventory_category(category, FALSE, TRUE);
+ delete this;
+ }
+ }
+
+ }
+}
+
+
+
+void LLFolderBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("open" == action)
+ {
+ openItem();
+ }
+ else if ("paste" == action)
+ {
+ pasteFromClipboard();
+ }
+ else if ("properties" == action)
+ {
+ showProperties();
+ }
+ else if ("replaceoutfit" == action)
+ {
+ modifyOutfit(FALSE);
+ }
+ else if ("addtooutfit" == action)
+ {
+ modifyOutfit(TRUE);
+ }
+ else if ("removefromoutfit" == action)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ remove_inventory_category_from_avatar ( cat );
+ }
+ else if ("purge" == action)
+ {
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+
+ if(cat)
+ {
+ model->purgeDescendentsOf(mUUID);
+ }
+ LLInventoryObject* obj = model->getObject(mUUID);
+ if(!obj) return;
+ obj->removeFromServer();
+ model->deleteObject(mUUID);
+ model->notifyObservers();
+ }
+ else if ("restore" == action)
+ {
+ restoreItem();
+ }
+}
+
+void LLFolderBridge::openItem()
+{
+ lldebugs << "LLFolderBridge::openItem()" << llendl;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ model->fetchDescendentsOf(mUUID);
+}
+
+BOOL LLFolderBridge::isItemRenameable() const
+{
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE)
+ && (cat->getOwnerID() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::restoreItem()
+{
+ LLViewerInventoryCategory* cat;
+ cat = (LLViewerInventoryCategory*)getCategory();
+ if(cat)
+ {
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ LLUUID new_parent = model->findCategoryUUIDForType(cat->getType());
+ // do not restamp children on restore
+ LLInvFVBridge::changeCategoryParent(model, cat, new_parent, FALSE);
+ }
+}
+
+// Icons for folders are based on the preferred type
+LLViewerImage* LLFolderBridge::getIcon() const
+{
+ const char* control = NULL;
+ LLAssetType::EType preferred_type = LLAssetType::AT_NONE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ preferred_type = cat->getPreferredType();
+ }
+ switch(preferred_type)
+ {
+ case LLAssetType::AT_TEXTURE:
+ control = "inv_folder_texture.tga";
+ break;
+ case LLAssetType::AT_SOUND:
+ control = "inv_folder_sound.tga";
+ break;
+ case LLAssetType::AT_CALLINGCARD:
+ control = "inv_folder_callingcard.tga";
+ break;
+ case LLAssetType::AT_LANDMARK:
+ control = "inv_folder_landmark.tga";
+ break;
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_LSL_TEXT:
+ control = "inv_folder_script.tga";
+ break;
+ case LLAssetType::AT_OBJECT:
+ control = "inv_folder_object.tga";
+ break;
+ case LLAssetType::AT_NOTECARD:
+ control = "inv_folder_notecard.tga";
+ break;
+ case LLAssetType::AT_CATEGORY:
+ control = "inv_folder_plain_closed.tga";
+ break;
+ case LLAssetType::AT_CLOTHING:
+ control = "inv_folder_clothing.tga";
+ break;
+ case LLAssetType::AT_BODYPART:
+ control = "inv_folder_bodypart.tga";
+ break;
+ case LLAssetType::AT_TRASH:
+ control = "inv_folder_trash.tga";
+ break;
+ case LLAssetType::AT_SNAPSHOT_CATEGORY:
+ control = "inv_folder_snapshot.tga";
+ break;
+ case LLAssetType::AT_LOST_AND_FOUND:
+ control = "inv_folder_lostandfound.tga";
+ break;
+ case LLAssetType::AT_ANIMATION:
+ control = "inv_folder_animation.tga";
+ break;
+ case LLAssetType::AT_GESTURE:
+ control = "inv_folder_gesture.tga";
+ break;
+ default:
+ control = "inv_folder_plain_closed.tga";
+ break;
+ }
+ LLString uuid_string = gViewerArt.getString(control);
+ return gImageList.getImage(LLUUID(uuid_string), MIPMAP_FALSE, TRUE);
+}
+
+BOOL LLFolderBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat && (cat->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat);
+ new_cat->rename(new_name);
+ new_cat->updateServer(FALSE);
+ model->updateCategory(new_cat);
+ model->notifyObservers();
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+BOOL LLFolderBridge::removeItem()
+{
+ if(!isItemRemovable())
+ {
+ return FALSE;
+ }
+ // move it to the trash
+ LLPreview::hide(mUUID);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ LLUUID trash_id;
+ trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+
+ // Look for any gestures and deactivate them
+ LLInventoryModel::cat_array_t descendent_categories;
+ LLInventoryModel::item_array_t descendent_items;
+ gInventory.collectDescendents( mUUID, descendent_categories, descendent_items, FALSE );
+
+ S32 i;
+ for (i = 0; i < descendent_items.count(); i++)
+ {
+ LLInventoryItem* item = descendent_items[i];
+ if (item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(item->getUUID());
+ }
+ }
+
+ // go ahead and do the normal remove if no 'last calling
+ // cards' are being removed.
+ LLViewerInventoryCategory* cat = getCategory();
+ if(cat)
+ {
+ LLInvFVBridge::changeCategoryParent(model, cat, trash_id, TRUE);
+ }
+
+ // return false anyway, so that if it's called from the folder
+ // view, it doesn't remove the view - it's just being moved to the
+ // trash.
+ return FALSE;
+
+}
+
+BOOL LLFolderBridge::isClipboardPasteable() const
+{
+ if(LLInventoryClipboard::instance().hasContents() && isAgentInventory())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLFolderBridge::pasteFromClipboard()
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model && isClipboardPasteable())
+ {
+ LLInventoryItem* item = NULL;
+ LLDynamicArray<LLUUID> objects;
+ LLInventoryClipboard::instance().retrieve(objects);
+ S32 count = objects.count();
+ LLUUID parent_id(mUUID);
+ for(S32 i = 0; i < count; i++)
+ {
+ item = model->getItem(objects.get(i));
+ if (item)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ parent_id,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+}
+
+void LLFolderBridge::staticFolderOptionsMenu()
+{
+ if (!sSelf) return;
+ sSelf->folderOptionsMenu();
+}
+
+void LLFolderBridge::folderOptionsMenu()
+{
+ std::vector<LLString> disabled_items;
+
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+
+ // calling card related functionality for folders.
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (mCallingCards || checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mItems.push_back("Calling Card Separator");
+ mItems.push_back("IM Contacts In Folder");
+ mItems.push_back("IM All Contacts In Folder");
+ }
+
+ // wearables related functionality for folders.
+ //is_wearable
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (mWearables ||
+ checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mItems.push_back("Folder Wearables Separator");
+ mItems.push_back("Add To Outfit");
+ mItems.push_back("Replace Outfit");
+ mItems.push_back("Take Off Items");
+ }
+ hideContextEntries(*mMenu, mItems, disabled_items);
+}
+
+BOOL LLFolderBridge::checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& is_type)
+{
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_type);
+ return ((item_array.count() > 0) ? TRUE : FALSE );
+}
+
+// Flags unused
+void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLFolderBridge::buildContextMenu()" << llendl;
+// std::vector<LLString> disabled_items;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(trash_id == mUUID)
+ {
+ // This is the trash.
+ mItems.push_back("Empty Trash");
+ }
+ else if(model->isObjectDescendentOf(mUUID, trash_id))
+ {
+ // This is a folder in the trash.
+ mItems.clear(); // clear any items that used to exist
+ mItems.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ mDisabledItems.push_back("Purge Item");
+ }
+
+ mItems.push_back("Restore Item");
+ }
+ else if(isAgentInventory()) // do not allow creating in library
+ {
+ // only mature accounts can create undershirts/underwear
+ /*if (gAgent.mAccess >= SIM_ACCESS_MATURE)
+ {
+ sub_menu->append(new LLMenuItemCallGL("New Undershirt",
+ &createNewUndershirt,
+ NULL,
+ (void*)this));
+ sub_menu->append(new LLMenuItemCallGL("New Underpants",
+ &createNewUnderpants,
+ NULL,
+ (void*)this));
+ }*/
+
+/* BOOL contains_calling_cards = FALSE;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_callingcard);
+ if(item_array.count() > 0) contains_calling_cards = TRUE;
+*/
+ mItems.push_back("New Folder");
+ mItems.push_back("New Script");
+ mItems.push_back("New Note");
+ mItems.push_back("New Gesture");
+ mItems.push_back("New Clothes");
+ mItems.push_back("New Body Parts");
+
+ getClipboardEntries(false, mItems, mDisabledItems, flags);
+
+ //Added by spatters to force inventory pull on right-click to display folder options correctly. 07-17-06
+ mCallingCards = mWearables = FALSE;
+
+ LLIsType is_callingcard(LLAssetType::AT_CALLINGCARD);
+ if (checkFolderForContentsOfType(model, is_callingcard))
+ {
+ mCallingCards=TRUE;
+ }
+
+ LLFindWearables is_wearable;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+
+ if (checkFolderForContentsOfType(model, is_wearable) ||
+ checkFolderForContentsOfType(model, is_object) ||
+ checkFolderForContentsOfType(model, is_gesture) )
+ {
+ mWearables=TRUE;
+ }
+
+ mMenu = &menu;
+ sSelf = this;
+ LLRightClickInventoryFetchDescendentsObserver* fetch = new LLRightClickInventoryFetchDescendentsObserver(FALSE);
+
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLViewerInventoryCategory* category = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ folders.push_back(category->getUUID());
+ fetch->fetchDescendents(folders);
+ inc_busy_count();
+ if(fetch->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ fetch->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(fetch);
+ }
+ }
+ else
+ {
+ mItems.push_back("--no options--");
+ mDisabledItems.push_back("--no options--");
+ }
+ hideContextEntries(menu, mItems, mDisabledItems);
+}
+
+BOOL LLFolderBridge::hasChildren() const
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLInventoryModel::EHasChildren has_children;
+ has_children = gInventory.categoryHasChildren(mUUID);
+ return has_children != LLInventoryModel::CHILDREN_NO;
+}
+
+BOOL LLFolderBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ //llinfos << "LLFolderBridge::dragOrDrop()" << llendl;
+ BOOL accept = FALSE;
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_CALLINGCARD:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ accept = dragItemIntoFolder((LLInventoryItem*)cargo_data,
+ drop);
+ break;
+ case DAD_CATEGORY:
+ accept = dragCategoryIntoFolder((LLInventoryCategory*)cargo_data,
+ drop);
+ break;
+ default:
+ break;
+ }
+ return accept;
+}
+
+LLViewerInventoryCategory* LLFolderBridge::getCategory() const
+{
+ LLViewerInventoryCategory* cat = NULL;
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(model)
+ {
+ cat = (LLViewerInventoryCategory*)model->getCategory(mUUID);
+ }
+ return cat;
+}
+
+
+// static
+void LLFolderBridge::pasteClipboard(void* user_data)
+{
+ LLFolderBridge* self = (LLFolderBridge*)user_data;
+ if(self) self->pasteFromClipboard();
+}
+
+void LLFolderBridge::createNewCategory(void* user_data)
+{
+ LLFolderBridge* bridge = (LLFolderBridge*)user_data;
+ if(!bridge) return;
+ LLInventoryPanel* panel = bridge->mInventoryPanel;
+ LLInventoryModel* model = panel->getModel();
+ if(!model) return;
+ LLUUID id;
+ id = model->createNewCategory(bridge->getUUID(),
+ LLAssetType::AT_NONE,
+ NULL);
+ model->notifyObservers();
+
+ // At this point, the bridge has probably been deleted, but the
+ // view is still there.
+ panel->setSelection(id, TAKE_FOCUS_YES);
+}
+
+void LLFolderBridge::createNewShirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHIRT);
+}
+
+void LLFolderBridge::createNewPants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_PANTS);
+}
+
+void LLFolderBridge::createNewShoes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHOES);
+}
+
+void LLFolderBridge::createNewSocks(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SOCKS);
+}
+
+void LLFolderBridge::createNewJacket(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_JACKET);
+}
+
+void LLFolderBridge::createNewSkirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIRT);
+}
+
+void LLFolderBridge::createNewGloves(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_GLOVES);
+}
+
+void LLFolderBridge::createNewUndershirt(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERSHIRT);
+}
+
+void LLFolderBridge::createNewUnderpants(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_UNDERPANTS);
+}
+
+void LLFolderBridge::createNewShape(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SHAPE);
+}
+
+void LLFolderBridge::createNewSkin(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_SKIN);
+}
+
+void LLFolderBridge::createNewHair(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_HAIR);
+}
+
+void LLFolderBridge::createNewEyes(void* user_data)
+{
+ LLFolderBridge::createWearable((LLFolderBridge*)user_data, WT_EYES);
+}
+
+// static
+void LLFolderBridge::createWearable(LLFolderBridge* bridge, EWearableType type)
+{
+ if(!bridge) return;
+ LLUUID parent_id = bridge->getUUID();
+ createWearable(parent_id, type);
+}
+
+// Separate function so can be called by global menu as well as right-click
+// menu.
+// static
+void LLFolderBridge::createWearable(LLUUID parent_id, EWearableType type)
+{
+ LLWearable* wearable = gWearableList.createNewWearable(type);
+ LLAssetType::EType asset_type = wearable->getAssetType();
+ LLInventoryType::EType inv_type = LLInventoryType::IT_WEARABLE;
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ parent_id, wearable->getTransactionID(), wearable->getName(),
+ wearable->getDescription(), asset_type, inv_type, wearable->getType(),
+ wearable->getPermissions().getMaskNextOwner(),
+ LLPointer<LLInventoryCallback>(NULL));
+}
+
+void LLFolderBridge::beginIMSession(BOOL only_online)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+ LLUniqueBuddyCollector is_buddy;
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ model->collectDescendentsIf(mUUID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_buddy);
+ S32 count = item_array.count();
+ if(count > 0)
+ {
+ // create the session
+ gIMView->setFloaterOpen(TRUE);
+ LLDynamicArray<LLUUID> members;
+ //members.put(gAgent.getID());
+ S32 i;
+ EInstantMessage type = IM_SESSION_ADD;
+ if(only_online)
+ {
+ LLAvatarTracker& at = LLAvatarTracker::instance();
+ LLUUID id;
+ for(i = 0; i < count; ++i)
+ {
+ id = item_array.get(i)->getCreatorUUID();
+ if(at.isBuddyOnline(id))
+ {
+ members.put(id);
+ }
+ }
+ }
+ else
+ {
+ type = IM_SESSION_OFFLINE_ADD;
+ for(i = 0; i < count; ++i)
+ {
+ members.put(item_array.get(i)->getCreatorUUID());
+ }
+ }
+ // the session_id is always the item_id of the inventory folder
+ gIMView->addSession(cat->getName(),
+ type,
+ mUUID,
+ members);
+ }
+}
+
+void LLFolderBridge::modifyOutfit(BOOL append)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return;
+ LLViewerInventoryCategory* cat = getCategory();
+ if(!cat) return;
+
+ wear_inventory_category_on_avatar( cat, append );
+}
+
+// helper stuff
+void move_task_inventory_callback(S32 option, void* user_data)
+{
+ LLMoveInv* move_inv = (LLMoveInv*)user_data;
+ LLFloaterOpenObject::LLCatAndWear* cat_and_wear = (LLFloaterOpenObject::LLCatAndWear* )move_inv->mUserData;
+ LLViewerObject* object = gObjectList.findObject(move_inv->mObjectID);
+
+ if(option == 0 && object)
+ {
+ if (cat_and_wear && cat_and_wear->mWear)
+ {
+ InventoryObjectList inventory_objects;
+ object->getInventoryContents(inventory_objects);
+ int contents_count = inventory_objects.size()-1; //subtract one for containing folder
+
+ LLInventoryCopyAndWearObserver* inventoryObserver = new LLInventoryCopyAndWearObserver(cat_and_wear->mCatID, contents_count);
+ gInventory.addObserver(inventoryObserver);
+ }
+
+ two_uuids_list_t::iterator move_it;
+ for (move_it = move_inv->mMoveList.begin();
+ move_it != move_inv->mMoveList.end();
+ ++move_it)
+ {
+ object->moveInventory(move_it->first, move_it->second);
+ }
+
+ // update the UI.
+ dialog_refresh_all();
+ }
+
+ if (move_inv->mCallback)
+ {
+ move_inv->mCallback(option, move_inv->mUserData);
+ }
+
+ delete move_inv;
+}
+
+BOOL LLFolderBridge::dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop)
+{
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+
+ // cannot drag into library
+ if(!isAgentInventory())
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+
+ LLToolDragAndDrop::ESource source = gToolDragAndDrop->getSource();
+ BOOL accept = FALSE;
+ LLViewerObject* object = NULL;
+ if(LLToolDragAndDrop::SOURCE_AGENT == source)
+ {
+
+ BOOL is_movable = TRUE;
+ switch( inv_item->getType() )
+ {
+ case LLAssetType::AT_ROOT_CATEGORY:
+ is_movable = FALSE;
+ break;
+
+ case LLAssetType::AT_CATEGORY:
+ is_movable = ( LLAssetType::AT_NONE == ((LLInventoryCategory*)inv_item)->getPreferredType() );
+ break;
+ }
+
+ LLUUID trash_id = model->findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ BOOL move_is_into_trash = (mUUID == trash_id) || model->isObjectDescendentOf(mUUID, trash_id);
+ if(is_movable && move_is_into_trash)
+ {
+ switch(inv_item->getType())
+ {
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_BODYPART:
+ is_movable = !gAgent.isWearingItem(inv_item->getUUID());
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ is_movable = !avatar->isWearingAttachment(inv_item->getUUID());
+ break;
+ }
+ }
+
+ accept = is_movable && (mUUID != inv_item->getParentUUID());
+ if(accept && drop)
+ {
+ if (inv_item->getType() == LLAssetType::AT_GESTURE
+ && gGestureManager.isGestureActive(inv_item->getUUID()))
+ {
+ gGestureManager.deactivateGesture(inv_item->getUUID());
+ }
+ // If an item is being dragged between windows, unselect
+ // everything in the active window so that we don't follow
+ // the selection to its new location (which is very
+ // annoying).
+ LLInventoryPanel* active_panel = LLInventoryView::getActiveInventory()->getPanel();
+ if (mInventoryPanel != active_panel)
+ {
+ active_panel->unSelectAll();
+ }
+
+ // restamp if the move is into the trash.
+ LLInvFVBridge::changeItemParent(
+ model,
+ (LLViewerInventoryItem*)inv_item,
+ mUUID,
+ move_is_into_trash);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_WORLD == source)
+ {
+ // Make sure the object exists. If we allowed dragging from
+ // anonymous objects, it would be possible to bypass
+ // permissions.
+ object = gObjectList.findObject(inv_item->getParentUUID());
+ if(!object)
+ {
+ llinfos << "Object not found for drop." << llendl;
+ return FALSE;
+ }
+
+ // coming from a task. Need to figure out if the person can
+ // move/copy this item.
+ LLPermissions perm(inv_item->getPermissions());
+ BOOL is_move = FALSE;
+ if((perm.allowCopyBy(gAgent.getID(), gAgent.getGroupID())
+ && perm.allowTransferTo(gAgent.getID())))
+// || gAgent.isGodlike())
+
+ {
+ accept = TRUE;
+ }
+ else if(object->permYouOwner())
+ {
+ // If the object cannot be copied, but the object the
+ // inventory is owned by the agent, then the item can be
+ // moved from the task to agent inventory.
+ is_move = TRUE;
+ accept = TRUE;
+ }
+ if(drop && accept)
+ {
+ LLMoveInv* move_inv = new LLMoveInv;
+ move_inv->mObjectID = inv_item->getParentUUID();
+ two_uuids_t item_pair(mUUID, inv_item->getUUID());
+ move_inv->mMoveList.push_back(item_pair);
+ move_inv->mCallback = NULL;
+ move_inv->mUserData = NULL;
+ if(is_move)
+ {
+ warn_move_inventory(object, move_inv);
+ }
+ else
+ {
+ move_task_inventory_callback(0, (void*)(move_inv));
+ }
+ }
+
+ }
+ else if(LLToolDragAndDrop::SOURCE_NOTECARD == source)
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_from_notecard(gToolDragAndDrop->getObjectID(),
+ gToolDragAndDrop->getSourceID(), inv_item);
+ }
+ }
+ else if(LLToolDragAndDrop::SOURCE_LIBRARY == source)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_item;
+ if(item && item->isComplete())
+ {
+ accept = TRUE;
+ if(drop)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ inv_item->getPermissions().getOwner(),
+ inv_item->getUUID(),
+ mUUID,
+ std::string(),
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ }
+ }
+ else
+ {
+ llwarns << "unhandled drag source" << llendl;
+ }
+ return accept;
+}
+
+// +=================================================+
+// | LLScriptBridge (DEPRECTED) |
+// +=================================================+
+
+LLViewerImage* LLScriptBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0);
+}
+
+// +=================================================+
+// | LLTextureBridge |
+// +=================================================+
+
+LLString LLTextureBridge::sPrefix("Texture: ");
+
+
+LLViewerImage* LLTextureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_TEXTURE, mInvType, 0);
+}
+
+void open_texture(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an exiting preview to the front
+ if( !LLPreview::show( item_id, take_focus ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewTexture* preview;
+ preview = new LLPreviewTexture("preview texture",
+ rect,
+ title,
+ item_id,
+ LLUUID::null,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLTextureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ open_texture(mUUID, getPrefix() + item->getName(), FALSE);
+ }
+}
+
+// +=================================================+
+// | LLSoundBridge |
+// +=================================================+
+
+LLString LLSoundBridge::sPrefix("Sound: ");
+
+
+LLViewerImage* LLSoundBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SOUND, LLInventoryType::IT_SOUND, 0);
+}
+
+void LLSoundBridge::openItem()
+{
+// Changed this back to the way it USED to work:
+// only open the preview dialog through the contextual right-click menu
+// double-click just plays the sound
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ openSoundPreview((void*)this);
+ //send_uuid_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+
+// if(!LLPreview::show(mUUID))
+// {
+// S32 left, top;
+// gFloaterView->getNewFloaterPosition(&left, &top);
+// LLRect rect = gSavedSettings.getRect("PreviewSoundRect");
+// rect.translate(left - rect.mLeft, top - rect.mTop);
+// new LLPreviewSound("preview sound",
+// rect,
+// getPrefix() + getName(),
+// mUUID));
+// }
+}
+
+void LLSoundBridge::previewItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ send_sound_trigger(item->getAssetUUID(), 1.0);
+ }
+}
+
+void LLSoundBridge::openSoundPreview(void* which)
+{
+ LLSoundBridge *me = (LLSoundBridge *)which;
+ if(!LLPreview::show(me->mUUID))
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewSoundRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLPreviewSound* preview = new LLPreviewSound("preview sound",
+ rect,
+ me->getPrefix() + me->getName(),
+ me->mUUID);
+ preview->setFocus(TRUE);
+ // Keep entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLSoundBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLTextureBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Sound Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Sound Separator");
+ items.push_back("Sound Play");
+
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLLandmarkBridge |
+// +=================================================+
+
+LLString LLLandmarkBridge::sPrefix("Landmark: ");
+
+LLViewerImage* LLLandmarkBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_LANDMARK, LLInventoryType::IT_LANDMARK, mVisited);
+}
+
+void LLLandmarkBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ lldebugs << "LLLandmarkBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Landmark Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Landmark Separator");
+ items.push_back("Teleport To Landmark");
+
+ hideContextEntries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLLandmarkBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("teleport" == action)
+ {
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ gAgent.teleportViaLandmark(item->getAssetUUID());
+
+ // we now automatically track the landmark you're teleporting to
+ // because you'll probably arrive at a telehub instead
+ if( gFloaterWorldMap )
+ {
+ gFloaterWorldMap->trackLandmark( item->getAssetUUID() );
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void open_landmark(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an exiting preview to the front
+ if( !LLPreview::show( item_id, take_focus ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewLandmark* preview = new LLPreviewLandmark("preview landmark",
+ rect,
+ title,
+ item_id,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+void LLLandmarkBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ open_landmark(mUUID, LLString(" ") + getPrefix() + item->getName(), FALSE);
+ }
+}
+
+
+// +=================================================+
+// | LLCallingCardObserver |
+// +=================================================+
+void LLCallingCardObserver::changed(U32 mask)
+{
+ mBridgep->refreshFolderViewItem();
+}
+
+// +=================================================+
+// | LLCallingCardBridge |
+// +=================================================+
+
+LLString LLCallingCardBridge::sPrefix("Calling Card: ");
+
+LLCallingCardBridge::LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid)
+{
+ mObserver = new LLCallingCardObserver(this);
+ LLAvatarTracker::instance().addObserver(mObserver);
+}
+
+LLCallingCardBridge::~LLCallingCardBridge()
+{
+ LLAvatarTracker::instance().removeObserver(mObserver);
+ delete mObserver;
+}
+
+void LLCallingCardBridge::refreshFolderViewItem()
+{
+ LLFolderViewItem* itemp = mInventoryPanel->getRootFolder()->getItemByID(mUUID);
+ if (itemp)
+ {
+ itemp->refresh();
+ }
+}
+
+// virtual
+void LLCallingCardBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("begin_im" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ gIMView->setFloaterOpen(TRUE);
+ gIMView->addSession(item->getName(), IM_NOTHING_SPECIAL, item->getCreatorUUID());
+ }
+ }
+ else if ("lure" == action)
+ {
+ LLViewerInventoryItem *item = getItem();
+ if (item && (item->getCreatorUUID() != gAgent.getID()) &&
+ (!item->getCreatorUUID().isNull()))
+ {
+ handle_lure(item->getCreatorUUID());
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+LLViewerImage* LLCallingCardBridge::getIcon() const
+{
+ BOOL online = FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ }
+ return get_item_icon(LLAssetType::AT_CALLINGCARD, LLInventoryType::IT_CALLINGCARD, online);
+}
+
+LLString LLCallingCardBridge::getLabelSuffix() const
+{
+ LLViewerInventoryItem* item = getItem();
+ if( item && LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID()) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (online)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void LLCallingCardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item && !item->getCreatorUUID().isNull())
+ {
+ BOOL online;
+ online = LLAvatarTracker::instance().isBuddyOnline(item->getCreatorUUID());
+ LLFloaterAvatarInfo::showFromFriend(item->getCreatorUUID(), online);
+ }
+}
+
+void LLCallingCardBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLCallingCardBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLInventoryItem* item = getItem();
+ BOOL good_card = (item
+ && (LLUUID::null != item->getCreatorUUID())
+ && (item->getCreatorUUID() != gAgent.getID()));
+
+ items.push_back("Send Instant Message");
+ items.push_back("Offer Teleport...");
+
+ if (!good_card)
+ {
+ disabled_items.push_back("Send Instant Message");
+ disabled_items.push_back("Offer Teleport...");
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+BOOL LLCallingCardBridge::dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data)
+{
+ LLViewerInventoryItem* item = getItem();
+ BOOL rv = FALSE;
+ if(item)
+ {
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem* inv_item = (LLInventoryItem*)cargo_data;
+ const LLPermissions& perm = inv_item->getPermissions();
+ if(gInventory.getItem(inv_item->getUUID())
+ && perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(item->getCreatorUUID(),
+ (LLInventoryItem*)cargo_data);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLInventoryCategory* inv_cat = (LLInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ rv = TRUE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(
+ item->getCreatorUUID(),
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably in
+ // an object's contents), so disallow dragging it here.
+ // You can't give something you don't yet have.
+ rv = FALSE;
+ }
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+// +=================================================+
+// | LLNotecardBridge |
+// +=================================================+
+
+LLString LLNotecardBridge::sPrefix("Note: ");
+
+
+LLViewerImage* LLNotecardBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_NOTECARD, LLInventoryType::IT_NOTECARD, 0);
+}
+
+void open_notecard(const LLUUID& item_id,
+ const LLString& title,
+ BOOL show_keep_discard,
+ const LLUUID& source_id,
+ BOOL take_focus)
+{
+ // See if we can bring an existing preview to the front
+ if(!LLPreview::show(item_id, take_focus))
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("NotecardEditorRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+ LLPreviewNotecard* preview;
+ preview = new LLPreviewNotecard("preview notecard",
+ rect,
+ title,
+ item_id,
+ LLUUID::null,
+ LLUUID::null,
+ show_keep_discard);
+ preview->setSourceID(source_id);
+ if(take_focus) preview->setFocus(TRUE);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+
+ //if (source_id.notNull())
+ //{
+ // // look for existing tabbed view for content from same source
+ // LLPreview* existing_preview = LLPreview::getPreviewForSource(source_id);
+ // if (existing_preview)
+ // {
+ // // found existing preview from this source
+ // // is it already hosted in a multi-preview window?
+ // LLMultiPreview* preview_hostp = (LLMultiPreview*)existing_preview->getHost();
+ // if (!preview_hostp)
+ // {
+ // // create new multipreview if it doesn't exist
+ // LLMultiPreview* preview_hostp = new LLMultiPreview(existing_preview->getRect());
+
+ // preview_hostp->addFloater(existing_preview);
+ // }
+ // // add this preview to existing host
+ // preview_hostp->addFloater(preview);
+ // }
+ //}
+
+ }
+}
+
+void LLNotecardBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ open_notecard(mUUID, getPrefix() + item->getName(), FALSE);
+ }
+}
+
+
+// +=================================================+
+// | LLGestureBridge |
+// +=================================================+
+
+LLString LLGestureBridge::sPrefix("Gesture: ");
+
+LLViewerImage* LLGestureBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_GESTURE, LLInventoryType::IT_GESTURE, 0);
+}
+
+LLFontGL::StyleFlags LLGestureBridge::getLabelStyle() const
+{
+ if( gGestureManager.isGestureActive(mUUID) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLGestureBridge::getLabelSuffix() const
+{
+ if( gGestureManager.isGestureActive(mUUID) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (active)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+// virtual
+void LLGestureBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("activate" == action)
+ {
+ gGestureManager.activateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else if ("deactivate" == action)
+ {
+ gGestureManager.deactivateGesture(mUUID);
+
+ LLViewerInventoryItem* item = gInventory.getItem(mUUID);
+ if (!item) return;
+
+ // Since we just changed the suffix to indicate (active)
+ // the server doesn't need to know, just the viewer.
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLGestureBridge::openItem()
+{
+ LLViewerInventoryItem* item = getItem();
+ if (!item) return;
+
+ // See if we can bring an existing preview to the front
+ if(!LLPreview::show(mUUID))
+ {
+ LLUUID item_id = mUUID;
+ LLString title = getPrefix() + item->getName();
+ LLUUID object_id = LLUUID::null;
+
+ // TODO: save the rectangle
+ LLPreviewGesture* preview = LLPreviewGesture::show(title, item_id, object_id);
+ preview->setFocus(TRUE);
+
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+}
+
+BOOL LLGestureBridge::removeItem()
+{
+ // Force close the preview window, if it exists
+ LLPreview::hide(mUUID);
+ gGestureManager.deactivateGesture(mUUID);
+ return LLItemBridge::removeItem();
+}
+
+void LLGestureBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLGestureBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back("Gesture Separator");
+ items.push_back("Activate");
+ items.push_back("Deactivate");
+
+ /*menu.append(new LLMenuItemCallGL("Activate",
+ handleActivateGesture,
+ enableActivateGesture,
+ (void*)this));
+ menu.append(new LLMenuItemCallGL("Deactivate",
+ handleDeactivateGesture,
+ enableDeactivateGesture,
+ (void*)this));*/
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// +=================================================+
+// | LLAnimationBridge |
+// +=================================================+
+
+LLString LLAnimationBridge::sPrefix("Animation: ");
+
+
+LLViewerImage* LLAnimationBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_ANIMATION, LLInventoryType::IT_ANIMATION, 0);
+}
+
+void LLAnimationBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+
+ lldebugs << "LLAnimationBridge::buildContextMenu()" << llendl;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Animation Open");
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+ }
+
+ items.push_back("Animation Separator");
+ items.push_back("Animation Play");
+ items.push_back("Animation Audition");
+
+ hideContextEntries(menu, items, disabled_items);
+
+}
+
+// virtual
+void LLAnimationBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ S32 activate = 0;
+
+ if ((action == "playworld") || (action == "playlocal"))
+ {
+
+ if ("playworld" == action) activate = 1;
+ if ("playlocal" == action) activate = 2;
+
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( mUUID ) )
+ {
+ // There isn't one, so make a new preview
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewAnimRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLPreviewAnim* preview = new LLPreviewAnim("preview anim",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID,
+ activate);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+ }
+ else
+ {
+ LLItemBridge::performAction(folder, model, action);
+ }
+}
+
+void LLAnimationBridge::openItem()
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( mUUID ) )
+ {
+ // There isn't one, so make a new preview
+ LLViewerInventoryItem* item = getItem();
+ if( item )
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewAnimRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLPreviewAnim* preview = new LLPreviewAnim("preview anim",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID,
+ 0);
+ preview->setFocus(TRUE);
+ // Force to be entirely onscreen.
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+}
+
+// +=================================================+
+// | LLObjectBridge |
+// +=================================================+
+
+// static
+LLString LLObjectBridge::sPrefix("Object: ");
+
+// static
+LLUUID LLObjectBridge::sContextMenuItemID;
+
+BOOL LLObjectBridge::isItemRemovable()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(!avatar) return FALSE;
+ if(avatar->isWearingAttachment(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLViewerImage* LLObjectBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_OBJECT, mInvType, mAttachPt);
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment);
+
+// virtual
+void LLObjectBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("attach" == action)
+ {
+ LLUUID object_id = mUUID;
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(object_id);
+ if(item && gInventory.isObjectDescendentOf(object_id, gAgent.getInventoryRootID()))
+ {
+ rez_attachment(item, NULL);
+ }
+ else if(item && item->isComplete())
+ {
+ // must be in library. copy it to our inventory and put it on.
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+ else if ("detach" == action)
+ {
+ LLInventoryItem* item = gInventory.getItem(mUUID);
+ if( item )
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, item->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+ }
+ // this object might have been selected, so let the selection manager know it's gone now
+ gSelectMgr->remove(gObjectList.findObject(item->getUUID()));
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLObjectBridge::openItem()
+{
+ /* Disabled -- this preview isn't useful. JC */
+ // CP: actually, this code is required - made changes to match LLAnimationBridge::openItem() idiom
+ // The properties preview is useful, converting to show object properties. - DaveP
+ LLShowProps::showProperties(mUUID);
+}
+
+LLFontGL::StyleFlags LLObjectBridge::getLabelStyle() const
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLObjectBridge::getLabelSuffix() const
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar && avatar->isWearingAttachment( mUUID ) )
+ {
+ LLString attachment_point_name = avatar->getAttachedPointName(mUUID);
+ LLString::toLower(attachment_point_name);
+ return LLItemBridge::getLabelSuffix() + LLString(" (worn on ") + attachment_point_name + LLString(")");
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+void rez_attachment(LLViewerInventoryItem* item, LLViewerJointAttachment* attachment)
+{
+ LLAttachmentRezAction* rez_action = new LLAttachmentRezAction;
+ rez_action->mItemID = item->getUUID();
+ rez_action->mAttachPt = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment);
+
+ if (attachment && attachment->getObject(0))
+ {
+ gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment_rez, (void*)rez_action);
+ }
+ else
+ {
+ confirm_replace_attachment_rez(0/*YES*/, (void*)rez_action);
+ }
+}
+
+void confirm_replace_attachment_rez(S32 option, void* user_data)
+{
+ LLAttachmentRezAction* rez_action = (LLAttachmentRezAction*)user_data;
+ if (option == 0/*YES*/)
+ {
+ if (rez_action)
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(rez_action->mItemID);
+
+ if (itemp)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezSingleAttachmentFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ItemID, itemp->getUUID());
+ msg->addUUIDFast(_PREHASH_OwnerID, itemp->getPermissions().getOwner());
+ msg->addU8Fast(_PREHASH_AttachmentPt, rez_action->mAttachPt);
+ pack_permissions_slam(msg, itemp->getFlags(), itemp->getPermissions());
+ msg->addStringFast(_PREHASH_Name, itemp->getName());
+ msg->addStringFast(_PREHASH_Description, itemp->getDescription());
+ msg->sendReliable(gAgent.getRegion()->getHost());
+ }
+ }
+ }
+ delete rez_action;
+}
+
+void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ LLObjectBridge::sContextMenuItemID = mUUID;
+
+ LLInventoryItem* item = getItem();
+ if(item)
+ {
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if( !avatarp )
+ {
+ return;
+ }
+
+ if( avatarp->isWearingAttachment( mUUID ) )
+ {
+ items.push_back("Detach From Yourself");
+ }
+ else
+ if( !isInTrash() )
+ {
+ items.push_back("Attach Separator");
+ items.push_back("Object Wear");
+ items.push_back("Attach To");
+ items.push_back("Attach To HUD");
+
+ LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE);
+ LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE);
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (attach_menu && (attach_menu->getChildCount() == 0) &&
+ attach_hud_menu && (attach_hud_menu->getChildCount() == 0) &&
+ avatarp)
+ {
+ for (LLViewerJointAttachment* attachment = avatarp->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = gAgent.getAvatarObject()->mAttachmentPoints.getNextData())
+ {
+ LLMenuItemCallGL *new_item;
+ if (attachment->getIsHUDAttachment())
+ {
+ attach_hud_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(),
+ NULL, //&LLObjectBridge::attachToAvatar,
+ NULL, &attach_label, (void*)attachment));
+ }
+ else
+ {
+ attach_menu->append(new_item = new LLMenuItemCallGL(attachment->getName(),
+ NULL, //&LLObjectBridge::attachToAvatar,
+ NULL, &attach_label, (void*)attachment));
+ }
+
+ LLSimpleListener* callback = mInventoryPanel->getListenerByName("Inventory.AttachObject");
+
+ if (callback)
+ {
+ new_item->addListener(callback, "on_click", LLSD(attachment->getName()));
+ }
+ }
+ }
+ }
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+BOOL LLObjectBridge::renameItem(const LLString& new_name)
+{
+ if(!isItemRenameable()) return FALSE;
+ LLPreview::rename(mUUID, getPrefix() + new_name);
+ LLInventoryModel* model = mInventoryPanel->getModel();
+ if(!model) return FALSE;
+ LLViewerInventoryItem* item = getItem();
+ if(item && (item->getName() != new_name))
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->rename(new_name);
+ buildDisplayName(new_item, mDisplayName);
+ new_item->updateServer(FALSE);
+ model->updateItem(new_item);
+ model->notifyObservers();
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ gSelectMgr->setObjectName( new_name );
+ gSelectMgr->deselectAll();
+ }
+ }
+ }
+ // return FALSE because we either notified observers (& therefore
+ // rebuilt) or we didn't update.
+ return FALSE;
+}
+
+// +=================================================+
+// | LLLSLTextBridge |
+// +=================================================+
+
+LLString LLLSLTextBridge::sPrefix("Script: ");
+
+LLViewerImage* LLLSLTextBridge::getIcon() const
+{
+ return get_item_icon(LLAssetType::AT_SCRIPT, LLInventoryType::IT_LSL, 0);
+}
+
+void LLLSLTextBridge::openItem()
+{
+ // See if we can bring an exiting preview to the front
+ if(!LLPreview::show(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewScriptRect");
+ rect.translate(left - rect.mLeft, top - rect.mTop);
+
+ LLPreviewLSL* preview = new LLPreviewLSL("preview lsl text",
+ rect,
+ getPrefix() + item->getName(),
+ mUUID);
+ preview->setFocus(TRUE);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(preview, FALSE);
+ }
+ }
+}
+
+// +=================================================+
+// | LLWearableBridge |
+// +=================================================+
+
+// HACK to get from avatar inventory to avatar
+void wear_inventory_item_on_avatar( LLInventoryItem* item )
+{
+ if(item)
+ {
+ lldebugs << "wear_inventory_item_on_avatar( " << item->getName()
+ << " )" << llendl;
+
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onWearOnAvatarArrived,
+ new LLUUID(item->getUUID()));
+ }
+}
+
+struct LLFoundData
+{
+ LLFoundData(const LLUUID& item_id,
+ const LLUUID& asset_id,
+ const LLString& name,
+ LLAssetType::EType asset_type) :
+ mItemID(item_id),
+ mAssetID(asset_id),
+ mAssetType(asset_type),
+ mName(name),
+ mWearable( NULL ) {}
+
+ LLUUID mItemID;
+ LLUUID mAssetID;
+ LLString mName;
+ LLAssetType::EType mAssetType;
+ LLWearable* mWearable;
+};
+
+struct LLWearableHoldingPattern
+{
+ LLWearableHoldingPattern() : mResolved(0) {}
+ ~LLWearableHoldingPattern() { mFoundList.deleteAllData(); }
+ LLDoubleLinkedList<LLFoundData> mFoundList;
+ S32 mResolved;
+};
+
+
+class LLOutfitObserver : public LLInventoryFetchObserver
+{
+public:
+ LLOutfitObserver(const LLUUID& cat_id, bool copy_items, bool append) :
+ mCatID(cat_id),
+ mCopyItems(copy_items),
+ mAppend(append)
+ {}
+ ~LLOutfitObserver() {}
+ virtual void done(); //public
+
+protected:
+ LLUUID mCatID;
+ bool mCopyItems;
+ bool mAppend;
+};
+
+class LLWearInventoryCategoryCallback : public LLInventoryCallback
+{
+public:
+ LLWearInventoryCategoryCallback(const LLUUID& cat_id, bool append)
+ {
+ mCatID = cat_id;
+ mAppend = append;
+ }
+ void fire(const LLUUID& item_id)
+ {
+ /*
+ * Do nothing. We only care about the destructor
+ */
+ }
+ ~LLWearInventoryCategoryCallback()
+ {
+ wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
+ }
+private:
+ LLUUID mCatID;
+ bool mAppend;
+};
+
+void LLOutfitObserver::done()
+{
+ // We now have an outfit ready to be copied to agent inventory. Do
+ // it, and wear that outfit normally.
+ if(mCopyItems)
+ {
+ LLInventoryCategory* cat = gInventory.getCategory(mCatID);
+ LLString name;
+ if(!cat)
+ {
+ // should never happen.
+ name = "New Outfit";
+ }
+ else
+ {
+ name = cat->getName();
+ }
+ LLViewerInventoryItem* item = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ LLUUID pid;
+ for(; it < end; ++it)
+ {
+ item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+ if(item)
+ {
+ if(LLInventoryType::IT_GESTURE == item->getInventoryType())
+ {
+ pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_GESTURE);
+ }
+ else
+ {
+ pid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CLOTHING);
+ }
+ break;
+ }
+ }
+ if(pid.isNull())
+ {
+ pid = gAgent.getInventoryRootID();
+ }
+
+ LLUUID cat_id = gInventory.createNewCategory(
+ pid,
+ LLAssetType::AT_NONE,
+ name);
+ mCatID = cat_id;
+ LLPointer<LLInventoryCallback> cb = new LLWearInventoryCategoryCallback(mCatID, mAppend);
+ it = mComplete.begin();
+ for(; it < end; ++it)
+ {
+ item = (LLViewerInventoryItem*)gInventory.getItem(*it);
+ if(item)
+ {
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ cat_id,
+ std::string(),
+ cb);
+ }
+ }
+ }
+ else
+ {
+ // Wear the inventory category.
+ wear_inventory_category_on_avatar(gInventory.getCategory(mCatID), mAppend);
+ }
+}
+
+class LLOutfitFetch : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLOutfitFetch(bool copy_items, bool append) : mCopyItems(copy_items), mAppend(append) {}
+ ~LLOutfitFetch() {}
+ virtual void done();
+protected:
+ bool mCopyItems;
+ bool mAppend;
+};
+
+void LLOutfitFetch::done()
+{
+ // What we do here is get the complete information on the items in
+ // the library, and set up an observer that will wait for that to
+ // happen.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ gInventory.collectDescendents(mCompleteFolders.front(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH);
+ S32 count = item_array.count();
+ if(!count)
+ {
+ llwarns << "Nothing fetched in category " << mCompleteFolders.front()
+ << llendl;
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+ return;
+ }
+
+ LLOutfitObserver* outfit;
+ outfit = new LLOutfitObserver(mCompleteFolders.front(), mCopyItems, mAppend);
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ ids.push_back(item_array.get(i)->getUUID());
+ }
+
+ // clean up, and remove this as an observer since the call to the
+ // outfit could notify observers and throw us into an infinite
+ // loop.
+ dec_busy_count();
+ gInventory.removeObserver(this);
+ delete this;
+
+ // increment busy count and either tell the inventory to check &
+ // call done, or add this object to the inventory for observation.
+ inc_busy_count();
+
+ // do the fetch
+ outfit->fetchItems(ids);
+ if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }
+}
+
+void wear_outfit_by_name(const char* name)
+{
+ llinfos << "Wearing category " << name << llendl;
+ inc_busy_count();
+
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLNameCategoryCollector has_name(name);
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ bool copy_items = false;
+ LLInventoryCategory* cat = NULL;
+ if (cat_array.count() > 0)
+ {
+ // Just wear the first one that matches
+ cat = cat_array.get(0);
+ }
+ else
+ {
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ if(cat_array.count() > 0)
+ {
+ cat = cat_array.get(0);
+ copy_items = true;
+ }
+ }
+
+ if(cat)
+ {
+ wear_inventory_category(cat, copy_items, false);
+ }
+ else
+ {
+ llwarns << "Couldn't find outfit " <<name<< " in wear_outfit_by_name()"
+ << llendl;
+ }
+
+ dec_busy_count();
+}
+
+void wear_inventory_category(LLInventoryCategory* category, bool copy, bool append)
+{
+ if(!category) return;
+
+ lldebugs << "wear_inventory_category( " << category->getName()
+ << " )" << llendl;
+ // What we do here is get the complete information on the items in
+ // the inventory, and set up an observer that will wait for that to
+ // happen.
+ LLOutfitFetch* outfit;
+ outfit = new LLOutfitFetch(copy, append);
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ folders.push_back(category->getUUID());
+ outfit->fetchDescendents(folders);
+ inc_busy_count();
+ if(outfit->isEverythingComplete())
+ {
+ // everything is already here - call done.
+ outfit->done();
+ }
+ else
+ {
+ // it's all on it's way - add an observer, and the inventory
+ // will call done for us when everything is here.
+ gInventory.addObserver(outfit);
+ }
+}
+
+// HACK to get from avatar inventory to avatar
+void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL append )
+{
+ // Avoid unintentionally overwriting old wearables. We have to do
+ // this up front to avoid having to deal with the case of multiple
+ // wearables being dirty.
+ if(!category) return;
+ lldebugs << "wear_inventory_category_on_avatar( " << category->getName()
+ << " )" << llendl;
+
+ LLWearInfo* userdata = new LLWearInfo;
+ userdata->mAppend = append;
+ userdata->mCategoryID = category->getUUID();
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty(
+ wear_inventory_category_on_avatar_step2,
+ userdata);
+ }
+ else
+ {
+ wear_inventory_category_on_avatar_step2(
+ TRUE,
+ userdata );
+ }
+}
+
+
+void wear_inventory_category_on_avatar_step2( BOOL proceed, void* userdata )
+{
+ LLWearInfo* wear_info = (LLWearInfo*)userdata;
+ if (!wear_info) return;
+
+ // Find all the wearables that are in the category's subtree.
+ lldebugs << "wear_inventory_category_on_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(wear_info->mCategoryID,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if( !wearable_count && !obj_count && !gest_count)
+ {
+ gViewerWindow->alertXml("CouldNotPutOnOutfit");
+ delete wear_info;
+ return;
+ }
+
+ // Processes that take time should show the busy cursor
+ if (wearable_count > 0 || obj_count > 0)
+ {
+ inc_busy_count();
+ }
+
+ // Activate all gestures in this folder
+ if (gest_count > 0)
+ {
+ llinfos << "Activating " << gest_count << " gestures" << llendl;
+
+ gGestureManager.activateGestures(gest_item_array);
+
+ // Update the inventory item labels to reflect the fact
+ // they are active.
+ LLViewerInventoryCategory* catp = gInventory.getCategory(wear_info->mCategoryID);
+ if (catp)
+ {
+ gInventory.updateCategory(catp);
+ gInventory.notifyObservers();
+ }
+ }
+
+ if(wearable_count > 0)
+ {
+ // Note: can't do normal iteration, because if all the
+ // wearables can be resolved immediately, then the
+ // callback will be called (and this object deleted)
+ // before the final getNextData().
+ LLWearableHoldingPattern* holder = new LLWearableHoldingPattern;
+ LLFoundData* found;
+ LLDynamicArray<LLFoundData*> found_container;
+ for(i = 0; i < wearable_count; ++i)
+ {
+ found = new LLFoundData(item_array.get(i)->getUUID(),
+ item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType());
+ holder->mFoundList.addData(found);
+ found_container.put(found);
+ }
+ for(i = 0; i < wearable_count; ++i)
+ {
+ gAddToOutfit = wear_info->mAppend;
+
+ found = found_container.get(i);
+ gWearableList.getAsset(found->mAssetID,
+ found->mName,
+ found->mAssetType,
+ wear_inventory_category_on_avatar_loop,
+ (void*)holder);
+ }
+ }
+
+
+ //If not appending and the folder doesn't contain only gestures, take off all attachments.
+ if (!wear_info->mAppend
+ && !(wearable_count == 0 && obj_count == 0 && gest_count > 0) )
+ {
+ LLAgent::userRemoveAllAttachments(NULL);
+ }
+
+ if( obj_count > 0 )
+ {
+ // We've found some attachements. Add these.
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ // Build a compound message to send all the objects that need to be rezzed.
+
+ // Limit number of packets to send
+ const S32 MAX_PACKETS_TO_SEND = 10;
+ const S32 OBJECTS_PER_PACKET = 4;
+ const S32 MAX_OBJECTS_TO_SEND = MAX_PACKETS_TO_SEND * OBJECTS_PER_PACKET;
+ if( obj_count > MAX_OBJECTS_TO_SEND )
+ {
+ obj_count = MAX_OBJECTS_TO_SEND;
+ }
+
+ // Create an id to keep the parts of the compound message together
+ LLUUID compound_msg_id;
+ compound_msg_id.generate();
+ LLMessageSystem* msg = gMessageSystem;
+
+ for(i = 0; i < obj_count; ++i)
+ {
+ if( 0 == (i % OBJECTS_PER_PACKET) )
+ {
+ // Start a new message chunk
+ msg->newMessageFast(_PREHASH_RezMultipleAttachmentsFromInv);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_HeaderData);
+ msg->addUUIDFast(_PREHASH_CompoundMsgID, compound_msg_id );
+ msg->addU8Fast(_PREHASH_TotalObjects, obj_count );
+ msg->addBOOLFast(_PREHASH_FirstDetachAll, !wear_info->mAppend );
+ }
+
+ LLInventoryItem* item = obj_item_array.get(i);
+ msg->nextBlockFast(_PREHASH_ObjectData );
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID() );
+ msg->addUUIDFast(_PREHASH_OwnerID, item->getPermissions().getOwner());
+ msg->addU8Fast(_PREHASH_AttachmentPt, 0 ); // Wear at the previous or default attachment point
+ pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+ msg->addStringFast(_PREHASH_Name, item->getName());
+ msg->addStringFast(_PREHASH_Description, item->getDescription());
+
+ if( (i+1 == obj_count) || ((OBJECTS_PER_PACKET-1) == (i % OBJECTS_PER_PACKET)) )
+ {
+ // End of message chunk
+ msg->sendReliable( gAgent.getRegion()->getHost() );
+ }
+ }
+ }
+ }
+ }
+ delete wear_info;
+ wear_info = NULL;
+}
+
+void wear_inventory_category_on_avatar_loop(LLWearable* wearable, void* data)
+{
+ LLWearableHoldingPattern* holder = (LLWearableHoldingPattern*)data;
+ BOOL append= gAddToOutfit;
+
+ if(wearable)
+ {
+ for(LLFoundData* data = holder->mFoundList.getFirstData();
+ data;
+ data = holder->mFoundList.getNextData() )
+ {
+ if(wearable->getID() == data->mAssetID)
+ {
+ data->mWearable = wearable;
+ break;
+ }
+ }
+ }
+ holder->mResolved += 1;
+ if(holder->mResolved >= holder->mFoundList.getLength())
+ {
+ wear_inventory_category_on_avatar_step3(holder, append);
+ }
+}
+
+void wear_inventory_category_on_avatar_step3(LLWearableHoldingPattern* holder, BOOL append)
+{
+ lldebugs << "wear_inventory_category_on_avatar_step3()" << llendl;
+ LLInventoryItem::item_array_t items;
+ LLDynamicArray< LLWearable* > wearables;
+
+ // For each wearable type, find the first instance in the category
+ // that we recursed through.
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ for(LLFoundData* data = holder->mFoundList.getFirstData();
+ data;
+ data = holder->mFoundList.getNextData())
+ {
+ LLWearable* wearable = data->mWearable;
+ if( wearable && ((S32)wearable->getType() == i) )
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(data->mItemID);
+ if( item && (item->getAssetUUID() == wearable->getID()) )
+ {
+ //RN: after discussing with Brashears, I disabled this code
+ //Metadata should reside in the item, not the asset
+ //And this code does not handle failed asset uploads properly
+// if(!wearable->isMatchedToInventoryItem(item ))
+// {
+// wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item );
+// // Now that we have an asset that matches the
+// // item, update the item to point to the new
+// // asset.
+// item->setAssetUUID(wearable->getID());
+// item->updateAssetOnServer();
+// }
+ items.put(item);
+ wearables.put(wearable);
+ }
+ break;
+ }
+ }
+ }
+
+ if(wearables.count() > 0)
+ {
+ gAgent.setWearableOutfit(items, wearables, !append);
+ gInventory.notifyObservers();
+ }
+
+ delete holder;
+
+ dec_busy_count();
+}
+
+void remove_inventory_category_from_avatar( LLInventoryCategory* category )
+{
+ if(!category) return;
+ lldebugs << "remove_inventory_category_from_avatar( " << category->getName()
+ << " )" << llendl;
+
+
+ LLUUID* uuid = new LLUUID(category->getUUID());
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->askToSaveAllIfDirty(
+ remove_inventory_category_from_avatar_step2,
+ uuid);
+ }
+ else
+ {
+ remove_inventory_category_from_avatar_step2(
+ TRUE,
+ uuid );
+ }
+}
+
+
+void remove_inventory_category_from_avatar_step2( BOOL proceed, void* userdata)
+{
+
+ // Find all the wearables that are in the category's subtree.
+ LLUUID* category_id = (LLUUID *)userdata;
+
+ lldebugs << "remove_inventory_category_from_avatar_step2()" << llendl;
+ if(proceed)
+ {
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLFindWearables is_wearable;
+ gInventory.collectDescendentsIf(*category_id,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_wearable);
+ S32 i;
+ S32 wearable_count = item_array.count();
+
+ LLInventoryModel::cat_array_t obj_cat_array;
+ LLInventoryModel::item_array_t obj_item_array;
+ LLIsType is_object( LLAssetType::AT_OBJECT );
+ gInventory.collectDescendentsIf(*category_id,
+ obj_cat_array,
+ obj_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_object);
+ S32 obj_count = obj_item_array.count();
+
+ // Find all gestures in this folder
+ LLInventoryModel::cat_array_t gest_cat_array;
+ LLInventoryModel::item_array_t gest_item_array;
+ LLIsType is_gesture( LLAssetType::AT_GESTURE );
+ gInventory.collectDescendentsIf(*category_id,
+ gest_cat_array,
+ gest_item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_gesture);
+ S32 gest_count = gest_item_array.count();
+
+ if (wearable_count > 0) //Loop through wearables. If worn, remove.
+ {
+ for(i = 0; i < wearable_count; ++i)
+ {
+ if( gAgent.isWearingItem (item_array.get(i)->getUUID()) )
+ {
+ gWearableList.getAsset(item_array.get(i)->getAssetUUID(),
+ item_array.get(i)->getName(),
+ item_array.get(i)->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new LLUUID(item_array.get(i)->getUUID()));
+
+ }
+ }
+ }
+
+
+ if (obj_count > 0)
+ {
+ for(i = 0; i < obj_count; ++i)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_DetachAttachmentIntoInv);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData );
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_ItemID, obj_item_array.get(i)->getUUID() );
+
+ gMessageSystem->sendReliable( gAgent.getRegion()->getHost() );
+
+ // this object might have been selected, so let the selection manager know it's gone now
+ gSelectMgr->remove(gObjectList.findObject( obj_item_array.get(i)->getUUID()) );
+ }
+ }
+
+ if (gest_count > 0)
+ {
+ for(i = 0; i < gest_count; ++i)
+ {
+ if ( gGestureManager.isGestureActive( gest_item_array.get(i)->getUUID()) )
+ {
+ gGestureManager.deactivateGesture( gest_item_array.get(i)->getUUID() );
+ gInventory.updateItem( gest_item_array.get(i) );
+ gInventory.notifyObservers();
+ }
+
+ }
+ }
+ }
+ delete category_id;
+ category_id = NULL;
+}
+
+BOOL LLWearableBridge::renameItem(const LLString& new_name)
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ gAgent.setWearableName( mUUID, new_name );
+ }
+ return LLItemBridge::renameItem(new_name);
+}
+
+BOOL LLWearableBridge::isItemRemovable()
+{
+ if(gAgent.isWearingItem(mUUID)) return FALSE;
+ return LLInvFVBridge::isItemRemovable();
+}
+
+LLFontGL::StyleFlags LLWearableBridge::getLabelStyle() const
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ // llinfos << "BOLD" << llendl;
+ return LLFontGL::BOLD;
+ }
+ else
+ {
+ return LLFontGL::NORMAL;
+ }
+}
+
+LLString LLWearableBridge::getLabelSuffix() const
+{
+ if( gAgent.isWearingItem( mUUID ) )
+ {
+ return LLItemBridge::getLabelSuffix() + " (worn)";
+ }
+ else
+ {
+ return LLItemBridge::getLabelSuffix();
+ }
+}
+
+LLViewerImage* LLWearableBridge::getIcon() const
+{
+ return get_item_icon(mAssetType, mInvType, mWearableType);
+}
+
+// virtual
+void LLWearableBridge::performAction(LLFolderView* folder, LLInventoryModel* model, LLString action)
+{
+ if ("wear" == action)
+ {
+ wearOnAvatar();
+ }
+ else if ("edit" == action)
+ {
+ editOnAvatar();
+ return;
+ }
+ else if ("take_off" == action)
+ {
+ if(gAgent.isWearingItem(mUUID))
+ {
+ LLViewerInventoryItem* item = getItem();
+ if (item)
+ {
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ LLWearableBridge::onRemoveFromAvatarArrived,
+ new LLUUID(mUUID));
+ }
+ }
+ }
+ else LLItemBridge::performAction(folder, model, action);
+}
+
+void LLWearableBridge::openItem()
+{
+ if( isInTrash() )
+ {
+ gViewerWindow->alertXml("CannotWearTrash");
+ }
+ else if(isAgentInventory())
+ {
+ if( !gAgent.isWearingItem( mUUID ) )
+ {
+ wearOnAvatar();
+ }
+ }
+ else
+ {
+ // must be in the inventory library. copy it to our inventory
+ // and put it on right away.
+ LLViewerInventoryItem* item = getItem();
+ if(item && item->isComplete())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else if(item)
+ {
+ // *FIX: Could in theory fetch and then do the operation above.
+ gViewerWindow->alertXml("CannotWearInfoNotComplete");
+ }
+ }
+}
+
+void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)
+{
+ lldebugs << "LLWearableBridge::buildContextMenu()" << llendl;
+ std::vector<LLString> items;
+ std::vector<LLString> disabled_items;
+ if(isInTrash())
+ {
+ items.push_back("Purge Item");
+ if (!isItemRemovable())
+ {
+ disabled_items.push_back("Purge Item");
+ }
+
+ items.push_back("Restore Item");
+ }
+ else
+ {
+ BOOL no_open = ((flags & SUPPRESS_OPEN_ITEM) == SUPPRESS_OPEN_ITEM);
+ if (!no_open)
+ {
+ items.push_back("Open");
+ }
+
+ items.push_back("Properties");
+
+ getClipboardEntries(true, items, disabled_items, flags);
+
+ items.push_back("Wearable Separator");
+ items.push_back("Wearable Wear");
+ items.push_back("Wearable Edit");
+ if ((flags & FIRST_SELECTED_ITEM) == 0)
+ {
+ disabled_items.push_back("Wearable Edit");
+ }
+ /*menu.appendSeparator();
+ menu.append(new LLMenuItemCallGL("Wear",
+ LLWearableBridge::onWearOnAvatar,
+ LLWearableBridge::canWearOnAvatar,
+ (void*)this));
+ menu.append(new LLMenuItemCallGL("Edit",
+ LLWearableBridge::onEditOnAvatar,
+ LLWearableBridge::canEditOnAvatar,
+ (void*)this));*/
+
+ LLViewerInventoryItem* item = getItem();
+ if( item && (item->getType() == LLAssetType::AT_CLOTHING) )
+ {
+ items.push_back("Take Off");
+ /*menu.append(new LLMenuItemCallGL("Take Off",
+ LLWearableBridge::onRemoveFromAvatar,
+ LLWearableBridge::canRemoveFromAvatar,
+ (void*)this));*/
+ }
+ }
+ hideContextEntries(menu, items, disabled_items);
+}
+
+// Called from menus
+// static
+BOOL LLWearableBridge::canWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+ if(!self->isAgentInventory())
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(!item || !item->isComplete()) return FALSE;
+ }
+ return (!gAgent.isWearingItem(self->mUUID));
+}
+
+// Called from menus
+// static
+void LLWearableBridge::onWearOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ self->wearOnAvatar();
+}
+
+void LLWearableBridge::wearOnAvatar()
+{
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return;
+ }
+
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ if(!isAgentInventory())
+ {
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onWearOnAvatarArrived( LLWearable* wearable, void* userdata )
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ LLViewerInventoryItem* item = NULL;
+ item = (LLViewerInventoryItem*)gInventory.getItem(*item_id);
+ if(item)
+ {
+ if(item->getAssetUUID() == wearable->getID())
+ {
+ //RN: after discussing with Brashears, I disabled this code
+ //Metadata should reside in the item, not the asset
+ //And this code does not handle failed asset uploads properly
+
+// if(!wearable->isMatchedToInventoryItem(item))
+// {
+// LLWearable* new_wearable = gWearableList.createWearableMatchedToInventoryItem( wearable, item );
+//
+// // Now that we have an asset that matches the
+// // item, update the item to point to the new
+// // asset.
+// item->setAssetUUID(new_wearable->getID());
+// item->updateAssetOnServer();
+// wearable = new_wearable;
+// }
+ gAgent.setWearable(item, wearable);
+ gInventory.notifyObservers();
+ //self->getFolderItem()->refreshFromRoot();
+ }
+ else
+ {
+ llinfos << "By the time wearable asset arrived, its inv item already pointed to a different asset." << llendl;
+ }
+ }
+ }
+ delete item_id;
+}
+
+// static
+BOOL LLWearableBridge::canEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return FALSE;
+
+ return (gAgent.isWearingItem(self->mUUID));
+}
+
+// static
+void LLWearableBridge::onEditOnAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(self)
+ {
+ self->editOnAvatar();
+ }
+}
+
+void LLWearableBridge::editOnAvatar()
+{
+ LLWearable* wearable = gAgent.getWearableFromWearableItem(mUUID);
+ if( wearable )
+ {
+ // Set the tab to the right wearable.
+ LLFloaterCustomize::setCurrentWearableType( wearable->getType() );
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() )
+ {
+ // Start Avatar Customization
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+}
+
+// static
+BOOL LLWearableBridge::canRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if( self && (LLAssetType::AT_BODYPART != self->mAssetType) )
+ {
+ return gAgent.isWearingItem( self->mUUID );
+ }
+ return FALSE;
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatar(void* user_data)
+{
+ LLWearableBridge* self = (LLWearableBridge*)user_data;
+ if(!self) return;
+ if(gAgent.isWearingItem(self->mUUID))
+ {
+ LLViewerInventoryItem* item = self->getItem();
+ if (item)
+ {
+ gWearableList.getAsset(item->getAssetUUID(),
+ item->getName(),
+ item->getType(),
+ onRemoveFromAvatarArrived,
+ new LLUUID(self->mUUID));
+ }
+ }
+}
+
+// static
+void LLWearableBridge::onRemoveFromAvatarArrived(LLWearable* wearable,
+ void* userdata)
+{
+ LLUUID* item_id = (LLUUID*) userdata;
+ if(wearable)
+ {
+ if( gAgent.isWearingItem( *item_id ) )
+ {
+ EWearableType type = wearable->getType();
+
+ if( !(type==WT_SHAPE || type==WT_SKIN || type==WT_HAIR ) ) //&&
+ //!((gAgent.mAccess >= SIM_ACCESS_MATURE) && ( type==WT_UNDERPANTS || type==WT_UNDERSHIRT )) )
+ {
+ gAgent.removeWearable( type );
+ }
+ }
+ }
+ delete item_id;
+}
diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h
new file mode 100755
index 0000000000..11573741a2
--- /dev/null
+++ b/indra/newview/llinventorybridge.h
@@ -0,0 +1,579 @@
+/**
+ * @file llinventorybridge.h
+ * @brief Implementation of the Inventory-Folder-View-Bridge classes.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llfloaterproperties.h"
+#include "llwearable.h"
+#include "llviewercontrol.h"
+#include "llcallingcard.h"
+
+enum EInventoryIcon
+{
+ TEXTURE_ICON_NAME,
+ SOUND_ICON_NAME,
+ CALLINGCARD_ONLINE_ICON_NAME,
+ CALLINGCARD_OFFLINE_ICON_NAME,
+ LANDMARK_ICON_NAME,
+ LANDMARK_VISITED_ICON_NAME,
+ SCRIPT_ICON_NAME,
+ CLOTHING_ICON_NAME,
+ OBJECT_ICON_NAME,
+ NOTECARD_ICON_NAME,
+ BODYPART_ICON_NAME,
+ SNAPSHOT_ICON_NAME,
+
+ BODYPART_SHAPE_ICON_NAME,
+ BODYPART_SKIN_ICON_NAME,
+ BODYPART_HAIR_ICON_NAME,
+ BODYPART_EYES_ICON_NAME,
+ CLOTHING_SHIRT_ICON_NAME,
+ CLOTHING_PANTS_ICON_NAME,
+ CLOTHING_SHOES_ICON_NAME,
+ CLOTHING_SOCKS_ICON_NAME,
+ CLOTHING_JACKET_ICON_NAME,
+ CLOTHING_GLOVES_ICON_NAME,
+ CLOTHING_UNDERSHIRT_ICON_NAME,
+ CLOTHING_UNDERPANTS_ICON_NAME,
+ CLOTHING_SKIRT_ICON_NAME,
+
+ ANIMATION_ICON_NAME,
+ GESTURE_ICON_NAME,
+
+ ICON_NAME_COUNT
+};
+
+extern const char* ICON_NAME[ICON_NAME_COUNT];
+
+typedef std::pair<LLUUID, LLUUID> two_uuids_t;
+typedef std::list<two_uuids_t> two_uuids_list_t;
+typedef std::pair<LLUUID, two_uuids_list_t> uuid_move_list_t;
+
+struct LLMoveInv
+{
+ LLUUID mObjectID;
+ LLUUID mCategoryID;
+ two_uuids_list_t mMoveList;
+ void (*mCallback)(S32, void*);
+ void* mUserData;
+};
+
+struct LLAttachmentRezAction
+{
+ LLUUID mItemID;
+ S32 mAttachPt;
+};
+
+
+//helper functions
+class LLShowProps
+{
+public:
+
+ static void showProperties(const LLUUID& uuid)
+ {
+ if(!LLFloaterProperties::show(uuid, LLUUID::null))
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PropertiesRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+ LLFloaterProperties* floater;
+ floater = new LLFloaterProperties("item properties",
+ rect,
+ "Inventory Item Properties",
+ uuid,
+ LLUUID::null);
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(floater, FALSE);
+ }
+ }
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryPanelObserver
+//
+// Bridge to support knowing when the inventory has changed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryPanelObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryPanelObserver(LLInventoryPanel* ip) : mIP(ip) {}
+ virtual ~LLInventoryPanelObserver() {}
+ virtual void changed(U32 mask) { mIP->modelChanged(mask); }
+protected:
+ LLInventoryPanel* mIP;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInvFVBridge (& it's derived classes)
+//
+// Short for Inventory-Folder-View-Bridge. This is an
+// implementation class to be able to view inventory items.
+//
+// You'll want to call LLInvItemFVELister::createBridge() to actually create
+// an instance of this class. This helps encapsulate the
+// funcationality a bit. (except for folders, you can create those
+// manually...)
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInvFVBridge : public LLFolderViewEventListener
+{
+public:
+ // This method is a convenience function which creates the correct
+ // type of bridge based on some basic information
+ static LLInvFVBridge* createBridge(LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type,
+ LLInventoryPanel* inventory,
+ const LLUUID& uuid,
+ U32 flags = 0x00);
+ virtual ~LLInvFVBridge() {}
+
+ virtual const LLUUID& getUUID() const { return mUUID; }
+
+ virtual const LLString& getPrefix() { return LLString::null; }
+ virtual void restoreItem() {}
+
+ // LLFolderViewEventListener functions
+ virtual const LLString& getName() const;
+ virtual const LLString& getDisplayName() const;
+ virtual PermissionMask getPermissionMask() const;
+ virtual U32 getCreationDate() const;
+ virtual LLFontGL::StyleFlags getLabelStyle() const
+ {
+ return LLFontGL::NORMAL;
+ }
+ virtual LLString getLabelSuffix() const { return LLString::null; }
+ virtual void openItem() {}
+ virtual void previewItem() {openItem();}
+ virtual void showProperties();
+ virtual BOOL isItemRenameable() const { return TRUE; }
+ //virtual BOOL renameItem(const LLString& new_name) {}
+ virtual BOOL isItemRemovable();
+ virtual BOOL isItemMovable();
+ //virtual BOOL removeItem() = 0;
+ virtual void removeBatch(LLDynamicArray<LLFolderViewEventListener*>& batch);
+ virtual void move(LLFolderViewEventListener* new_parent_bridge) {}
+ virtual BOOL isItemCopyable() const { return FALSE; }
+ virtual BOOL copyToClipboard() const { return FALSE; }
+ virtual void cutToClipboard() {}
+ virtual BOOL isClipboardPasteable() const;
+ virtual void pasteFromClipboard() {}
+ void getClipboardEntries(bool show_asset_id, std::vector<LLString> &items,
+ std::vector<LLString> &disabled_items, U32 flags);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL startDrag(EDragAndDropType* type, LLUUID* id);
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data) { return FALSE; }
+ virtual LLInventoryType::EType getInventoryType() const { return mInvType; }
+
+ // LLInvFVBridge functionality
+ virtual void clearDisplayName() {}
+
+protected:
+ LLInvFVBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ mInventoryPanel(inventory), mUUID(uuid) {}
+
+ LLInventoryObject* getInventoryObject() const;
+ BOOL isInTrash() const;
+ // return true if the item is in agent inventory. if false, it
+ // must be lost or in the inventory library.
+ BOOL isAgentInventory() const;
+ virtual BOOL isItemPermissive() const;
+ static void changeItemParent(LLInventoryModel* model,
+ LLViewerInventoryItem* item,
+ const LLUUID& new_parent,
+ BOOL restamp);
+ static void changeCategoryParent(LLInventoryModel* model,
+ LLViewerInventoryCategory* item,
+ const LLUUID& new_parent,
+ BOOL restamp);
+ void removeBatchNoCheck(LLDynamicArray<LLFolderViewEventListener*>& batch);
+
+protected:
+ LLInventoryPanel* mInventoryPanel;
+ LLUUID mUUID; // item id
+ LLInventoryType::EType mInvType;
+};
+
+
+class LLItemBridge : public LLInvFVBridge
+{
+public:
+ LLItemBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLInvFVBridge(inventory, uuid) {}
+
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+
+ virtual void selectItem();
+ virtual void restoreItem();
+
+ virtual LLViewerImage* getIcon() const;
+ virtual const LLString& getDisplayName() const;
+ virtual LLString getLabelSuffix() const;
+ virtual PermissionMask getPermissionMask() const;
+ virtual U32 getCreationDate() const;
+ virtual BOOL isItemRenameable() const;
+ virtual BOOL renameItem(const LLString& new_name);
+ virtual BOOL removeItem();
+ virtual BOOL isItemCopyable() const;
+ virtual BOOL copyToClipboard() const;
+ virtual BOOL hasChildren() const { return FALSE; }
+ virtual BOOL isUpToDate() const { return TRUE; }
+
+ // override for LLInvFVBridge
+ virtual void clearDisplayName() { mDisplayName.clear(); }
+
+ LLViewerInventoryItem* getItem() const;
+
+protected:
+ virtual BOOL isItemPermissive() const;
+ static void buildDisplayName(LLInventoryItem* item, LLString& name);
+ mutable LLString mDisplayName;
+};
+
+
+class LLFolderBridge : public LLInvFVBridge
+{
+ friend class LLInvFVBridge;
+public:
+ BOOL dragItemIntoFolder(LLInventoryItem* inv_item,
+ BOOL drop);
+ BOOL dragCategoryIntoFolder(LLInventoryCategory* inv_category,
+ BOOL drop);
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual BOOL isItemRenameable() const;
+ virtual void selectItem();
+ virtual void restoreItem();
+
+
+ virtual LLViewerImage* getIcon() const;
+ virtual BOOL renameItem(const LLString& new_name);
+ virtual BOOL removeItem();
+ virtual BOOL isClipboardPasteable() const;
+ virtual void pasteFromClipboard();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL hasChildren() const;
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data);
+
+ virtual BOOL isItemRemovable();
+ virtual BOOL isItemMovable();
+ virtual BOOL isUpToDate() const;
+
+ static void createWearable(LLFolderBridge* bridge, EWearableType type);
+ static void createWearable(LLUUID parent_folder_id, EWearableType type);
+
+ LLViewerInventoryCategory* getCategory() const;
+
+protected:
+ LLFolderBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLInvFVBridge(inventory, uuid) {}
+
+ // menu callbacks
+ static void pasteClipboard(void* user_data);
+ static void createNewCategory(void* user_data);
+
+ static void createNewShirt(void* user_data);
+ static void createNewPants(void* user_data);
+ static void createNewShoes(void* user_data);
+ static void createNewSocks(void* user_data);
+ static void createNewJacket(void* user_data);
+ static void createNewSkirt(void* user_data);
+ static void createNewGloves(void* user_data);
+ static void createNewUndershirt(void* user_data);
+ static void createNewUnderpants(void* user_data);
+ static void createNewShape(void* user_data);
+ static void createNewSkin(void* user_data);
+ static void createNewHair(void* user_data);
+ static void createNewEyes(void* user_data);
+
+ BOOL checkFolderForContentsOfType(LLInventoryModel* model, LLInventoryCollectFunctor& typeToCheck);
+
+ void beginIMSession(BOOL only_online);
+ void modifyOutfit(BOOL append);
+public:
+ static LLFolderBridge* sSelf;
+ static void staticFolderOptionsMenu();
+ void folderOptionsMenu();
+private:
+ BOOL mCallingCards;
+ BOOL mWearables;
+ LLMenuGL* mMenu;
+ std::vector<LLString> mItems;
+ std::vector<LLString> mDisabledItems;
+};
+
+// DEPRECATED
+class LLScriptBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ LLViewerImage* getIcon() const;
+
+protected:
+ LLScriptBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid) {}
+};
+
+
+class LLTextureBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLTextureBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type) :
+ LLItemBridge(inventory, uuid), mInvType(type) {}
+ static LLString sPrefix;
+ LLInventoryType::EType mInvType;
+};
+
+class LLSoundBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+ virtual void previewItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ static void openSoundPreview(void*);
+
+protected:
+ LLSoundBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+ static LLString sPrefix;
+};
+
+class LLLandmarkBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLLandmarkBridge(LLInventoryPanel* inventory, const LLUUID& uuid, U32 flags = 0x00) :
+ LLItemBridge(inventory, uuid)
+ {
+ mVisited = FALSE;
+ if (flags & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mVisited = TRUE;
+ }
+ }
+
+protected:
+ static LLString sPrefix;
+ BOOL mVisited;
+};
+
+class LLCallingCardBridge;
+
+class LLCallingCardObserver : public LLFriendObserver
+{
+public:
+ LLCallingCardObserver(LLCallingCardBridge* bridge) : mBridgep(bridge) {}
+ virtual ~LLCallingCardObserver() { mBridgep = NULL; }
+ virtual void changed(U32 mask);
+
+protected:
+ LLCallingCardBridge* mBridgep;
+};
+
+class LLCallingCardBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLString getLabelSuffix() const;
+ //virtual const LLString& getDisplayName() const;
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ //virtual void renameItem(const LLString& new_name);
+ //virtual BOOL removeItem();
+ virtual BOOL dragOrDrop(MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data);
+ void refreshFolderViewItem();
+
+protected:
+ LLCallingCardBridge( LLInventoryPanel* inventory, const LLUUID& uuid );
+ ~LLCallingCardBridge();
+
+protected:
+ static LLString sPrefix;
+ LLCallingCardObserver* mObserver;
+};
+
+
+class LLNotecardBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLNotecardBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+class LLGestureBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+
+ // Only suffix for gesture items, not task items, because only
+ // gestures in your inventory can be active.
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual BOOL removeItem();
+
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+protected:
+ LLGestureBridge(LLInventoryPanel* inventory, const LLUUID& uuid)
+ : LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLAnimationBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLAnimationBridge(LLInventoryPanel* inventory, const LLUUID& uuid) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLObjectBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual BOOL isItemRemovable();
+ virtual BOOL renameItem(const LLString& new_name);
+
+protected:
+ LLObjectBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLInventoryType::EType type, U32 flags) :
+ LLItemBridge(inventory, uuid), mInvType(type)
+ {
+ mAttachPt = (flags & 0xff); // low bye of inventory flags
+ }
+
+protected:
+ static LLString sPrefix;
+ static LLUUID sContextMenuItemID; // Only valid while the context menu is open.
+ LLInventoryType::EType mInvType;
+ U32 mAttachPt;
+
+};
+
+
+class LLLSLTextBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual const LLString& getPrefix() { return sPrefix; }
+
+ virtual LLViewerImage* getIcon() const;
+ virtual void openItem();
+
+protected:
+ LLLSLTextBridge( LLInventoryPanel* inventory, const LLUUID& uuid ) :
+ LLItemBridge(inventory, uuid) {}
+
+protected:
+ static LLString sPrefix;
+};
+
+
+class LLWearableBridge : public LLItemBridge
+{
+ friend class LLInvFVBridge;
+public:
+ virtual LLViewerImage* getIcon() const;
+ virtual void performAction(LLFolderView* folder, LLInventoryModel* model, LLString action);
+ virtual void openItem();
+ virtual void buildContextMenu(LLMenuGL& menu, U32 flags);
+ virtual LLFontGL::StyleFlags getLabelStyle() const;
+ virtual LLString getLabelSuffix() const;
+ virtual BOOL isItemRemovable();
+ virtual BOOL renameItem(const LLString& new_name);
+
+ static void onWearOnAvatar( void* userdata ); // Access to wearOnAvatar() from menu
+ static BOOL canWearOnAvatar( void* userdata );
+ static void onWearOnAvatarArrived( LLWearable* wearable, void* userdata );
+ void wearOnAvatar();
+
+ static BOOL canEditOnAvatar( void* userdata ); // Access to editOnAvatar() from menu
+ static void onEditOnAvatar( void* userdata );
+ void editOnAvatar();
+
+ static BOOL canRemoveFromAvatar( void* userdata );
+ static void onRemoveFromAvatar( void* userdata );
+ static void onRemoveFromAvatarArrived( LLWearable* wearable, void* userdata );
+
+protected:
+ LLWearableBridge(LLInventoryPanel* inventory, const LLUUID& uuid, LLAssetType::EType asset_type, LLInventoryType::EType inv_type, EWearableType wearable_type) :
+ LLItemBridge(inventory, uuid),
+ mAssetType( asset_type ),
+ mInvType(inv_type),
+ mWearableType(wearable_type)
+ {}
+
+protected:
+ LLAssetType::EType mAssetType;
+ LLInventoryType::EType mInvType;
+ EWearableType mWearableType;
+};
diff --git a/indra/newview/llinventoryclipboard.cpp b/indra/newview/llinventoryclipboard.cpp
new file mode 100644
index 0000000000..8008edd2ad
--- /dev/null
+++ b/indra/newview/llinventoryclipboard.cpp
@@ -0,0 +1,80 @@
+/**
+ * @file llinventoryclipboard.cpp
+ * @brief LLInventoryClipboard class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llinventoryclipboard.h"
+
+
+LLInventoryClipboard LLInventoryClipboard::sInstance;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryClipboard
+///----------------------------------------------------------------------------
+
+LLInventoryClipboard::LLInventoryClipboard()
+{
+}
+
+LLInventoryClipboard::~LLInventoryClipboard()
+{
+ reset();
+}
+
+void LLInventoryClipboard::add(const LLUUID& object)
+{
+ mObjects.put(object);
+}
+
+// this stores a single inventory object
+void LLInventoryClipboard::store(const LLUUID& object)
+{
+ reset();
+ mObjects.put(object);
+}
+
+void LLInventoryClipboard::store(const LLDynamicArray<LLUUID>& inv_objects)
+{
+ reset();
+ S32 count = inv_objects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ mObjects.put(inv_objects[i]);
+ }
+}
+
+void LLInventoryClipboard::retrieve(LLDynamicArray<LLUUID>& inv_objects) const
+{
+ inv_objects.reset();
+ S32 count = mObjects.count();
+ for(S32 i = 0; i < count; i++)
+ {
+ inv_objects.put(mObjects[i]);
+ }
+}
+
+void LLInventoryClipboard::reset()
+{
+ mObjects.reset();
+}
+
+// returns true if the clipboard has something pasteable in it.
+BOOL LLInventoryClipboard::hasContents() const
+{
+ return (mObjects.count() > 0);
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/llinventoryclipboard.h b/indra/newview/llinventoryclipboard.h
new file mode 100644
index 0000000000..4250822189
--- /dev/null
+++ b/indra/newview/llinventoryclipboard.h
@@ -0,0 +1,65 @@
+/**
+ * @file llinventoryclipboard.h
+ * @brief LLInventoryClipboard class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVENTORYCLIPBOARD_H
+#define LL_LLINVENTORYCLIPBOARD_H
+
+#include "lldarray.h"
+#include "lluuid.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryClipboard
+//
+// This class is used to cut/copy/paste inventory items around the
+// world. This class is accessed through a singleton (only one
+// inventory clipboard for now) which can be referenced using the
+// instance() method.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryClipboard
+{
+public:
+ // calling this before main() is undefined
+ static LLInventoryClipboard& instance() { return sInstance; }
+
+ // this method adds to the current list.
+ void add(const LLUUID& object);
+
+ // this stores a single inventory object
+ void store(const LLUUID& object);
+
+ // this method stores an array of objects
+ void store(const LLDynamicArray<LLUUID>& inventory_objects);
+
+ // this method gets the objects in the clipboard by copying them
+ // into the array provided.
+ void retrieve(LLDynamicArray<LLUUID>& inventory_objects) const;
+
+ // this method empties out the clipboard
+ void reset();
+
+ // returns true if the clipboard has something pasteable in it.
+ BOOL hasContents() const;
+
+protected:
+ static LLInventoryClipboard sInstance;
+
+ LLDynamicArray<LLUUID> mObjects;
+
+public:
+ // please don't actually call these
+ LLInventoryClipboard();
+ ~LLInventoryClipboard();
+private:
+ // please don't implement these
+ LLInventoryClipboard(const LLInventoryClipboard&);
+ LLInventoryClipboard& operator=(const LLInventoryClipboard&);
+};
+
+
+#endif // LL_LLINVENTORYCLIPBOARD_H
diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp
new file mode 100644
index 0000000000..509f040c34
--- /dev/null
+++ b/indra/newview/llinventorymodel.cpp
@@ -0,0 +1,3493 @@
+/**
+ * @file llinventorymodel.cpp
+ * @brief Implementation of the inventory model used to track agent inventory.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llinventorymodel.h"
+
+#include "llassetstorage.h"
+#include "llcrc.h"
+#include "lldir.h"
+#include "llsys.h"
+#include "llxfermanager.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llfloater.h"
+#include "llfocusmgr.h"
+#include "llinventoryview.h"
+#include "llviewerinventory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "lldbstrings.h"
+#include "llviewerstats.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llcallbacklist.h"
+#include <deque>
+
+//#define DIFF_INVENTORY_FILES
+#ifdef DIFF_INVENTORY_FILES
+#include "process.h"
+#endif
+
+BOOL LLInventoryModel::sBackgroundFetchActive = FALSE;
+BOOL LLInventoryModel::sAllFoldersFetched = FALSE;
+BOOL LLInventoryModel::sFullFetchStarted = FALSE;
+S32 LLInventoryModel::sNumFetchRetries = 0;
+F32 LLInventoryModel::sMinTimeBetweenFetches = 0.3f;
+F32 LLInventoryModel::sMaxTimeBetweenFetches = 10.f;
+BOOL LLInventoryModel::sTimelyFetchPending = FALSE;
+LLFrameTimer LLInventoryModel::sFetchTimer;
+
+// RN: for some reason, using std::queue in the header file confuses the compiler which things it's an xmlrpc_queue
+static std::deque<LLUUID> sFetchQueue;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+//BOOL decompress_file(const char* src_filename, const char* dst_filename);
+const F32 MAX_TIME_FOR_SINGLE_FETCH = 10.f;
+const S32 MAX_FETCH_RETRIES = 5;
+const char CACHE_FORMAT_STRING[] = "%s.inv";
+const char* NEW_CATEGORY_NAME = "New Folder";
+const char* NEW_CATEGORY_NAMES[LLAssetType::AT_COUNT] =
+{
+ "Textures", // AT_TEXTURE
+ "Sounds", // AT_SOUND
+ "Calling Cards", // AT_CALLINGCARD
+ "Landmarks", // AT_LANDMARK
+ "Scripts", // AT_SCRIPT (deprecated?)
+ "Clothing", // AT_CLOTHING
+ "Objects", // AT_OBJECT
+ "Notecards", // AT_NOTECARD
+ "New Folder", // AT_CATEGORY
+ "Inventory", // AT_ROOT_CATEGORY
+ "Scripts", // AT_LSL_TEXT
+ "Scripts", // AT_LSL_BYTECODE
+ "Uncompressed Images", // AT_TEXTURE_TGA
+ "Body Parts", // AT_BODYPART
+ "Trash", // AT_TRASH
+ "Photo Album", // AT_SNAPSHOT_CATEGORY
+ "Lost And Found", // AT_LOST_AND_FOUND
+ "Uncompressed Sounds", // AT_SOUND_WAV
+ "Uncompressed Images", // AT_IMAGE_TGA
+ "Uncompressed Images", // AT_IMAGE_JPEG
+ "Animations", // AT_ANIMATION
+ "Gestures", // AT_GESTURE
+};
+
+struct InventoryIDPtrLess
+{
+ bool operator()(const LLViewerInventoryCategory* i1, const LLViewerInventoryCategory* i2) const
+ {
+ return (i1->getUUID() < i2->getUUID());
+ }
+};
+
+class LLCanCache : public LLInventoryCollectFunctor
+{
+public:
+ LLCanCache(LLInventoryModel* model) : mModel(model) {}
+ virtual ~LLCanCache() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+protected:
+ LLInventoryModel* mModel;
+ std::set<LLUUID> mCachedCatIDs;
+};
+
+bool LLCanCache::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ bool rv = false;
+ if(item)
+ {
+ if(mCachedCatIDs.find(item->getParentUUID()) != mCachedCatIDs.end())
+ {
+ rv = true;
+ }
+ }
+ else if(cat)
+ {
+ // HACK: downcast
+ LLViewerInventoryCategory* c = (LLViewerInventoryCategory*)cat;
+ if(c->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ S32 descendents_server = c->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ mModel->getDirectDescendentsOf(
+ c->getUUID(),
+ cats,
+ items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ mCachedCatIDs.insert(c->getUUID());
+ rv = true;
+ }
+ }
+ }
+ return rv;
+}
+
+///----------------------------------------------------------------------------
+/// Class LLInventoryModel
+///----------------------------------------------------------------------------
+
+// global for the agent inventory.
+LLInventoryModel gInventory;
+
+// Default constructor
+LLInventoryModel::LLInventoryModel() :
+ mModifyMask(LLInventoryObserver::ALL),
+ mLastItem(NULL)
+{
+}
+
+// Destroys the object
+LLInventoryModel::~LLInventoryModel()
+{
+ empty();
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); ++iter)
+ {
+ delete *iter;
+ }
+}
+
+// This is a convenience function to check if one object has a parent
+// chain up to the category specified by UUID.
+BOOL LLInventoryModel::isObjectDescendentOf(const LLUUID& obj_id,
+ const LLUUID& cat_id)
+{
+ LLInventoryObject* obj = getObject(obj_id);
+ while(obj)
+ {
+ const LLUUID& parent_id = obj->getParentUUID();
+ if( parent_id.isNull() )
+ {
+ return FALSE;
+ }
+ if(parent_id == cat_id)
+ {
+ return TRUE;
+ }
+ // Since we're scanning up the parents, we only need to check
+ // in the category list.
+ obj = getCategory(parent_id);
+ }
+ return FALSE;
+}
+
+// Get the object by id. Returns NULL if not found.
+LLInventoryObject* LLInventoryModel::getObject(const LLUUID& id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(id);
+ if (cat)
+ {
+ return cat;
+ }
+ LLViewerInventoryItem* item = getItem(id);
+ if (item)
+ {
+ return item;
+ }
+ return NULL;
+}
+
+// Get the item by id. Returns NULL if not found.
+LLViewerInventoryItem* LLInventoryModel::getItem(const LLUUID& id) const
+{
+ LLViewerInventoryItem* item = NULL;
+ if(mLastItem.notNull() && mLastItem->getUUID() == id)
+ {
+ item = mLastItem;
+ }
+ else
+ {
+ item_map_t::const_iterator iter = mItemMap.find(id);
+ if (iter != mItemMap.end())
+ {
+ item = iter->second;
+ mLastItem = item;
+ }
+ }
+ return item;
+}
+
+// Get the category by id. Returns NULL if not found
+LLViewerInventoryCategory* LLInventoryModel::getCategory(const LLUUID& id) const
+{
+ LLViewerInventoryCategory* category = NULL;
+ cat_map_t::const_iterator iter = mCategoryMap.find(id);
+ if (iter != mCategoryMap.end())
+ {
+ category = iter->second;
+ }
+ return category;
+}
+
+S32 LLInventoryModel::getItemCount() const
+{
+ return mItemMap.size();
+}
+
+S32 LLInventoryModel::getCategoryCount() const
+{
+ return mCategoryMap.size();
+}
+
+// Return the direct descendents of the id provided. The array
+// provided points straight into the guts of this object, and
+// should only be used for read operations, since modifications
+// may invalidate the internal state of the inventory. Set passed
+// in values to NULL if the call fails.
+void LLInventoryModel::getDirectDescendentsOf(const LLUUID& cat_id,
+ cat_array_t*& categories,
+ item_array_t*& items) const
+{
+ categories = get_ptr_in_map(mParentChildCategoryTree, cat_id);
+ items = get_ptr_in_map(mParentChildItemTree, cat_id);
+}
+
+// findCategoryUUIDForType() returns the uuid of the category that
+// specifies 'type' as what it defaults to containing. The category is
+// not necessarily only for that type. *NOTE: This will create a new
+// inventory category on the fly if one does not exist.
+LLUUID LLInventoryModel::findCategoryUUIDForType(LLAssetType::EType t)
+{
+ LLUUID rv = findCatUUID(t);
+ if(rv.isNull())
+ {
+ rv = gAgent.getInventoryRootID();
+ if(rv.notNull())
+ {
+ rv = createNewCategory(rv, t, NULL);
+ }
+ }
+ return rv;
+}
+
+// Internal method which looks for a category with the specified
+// preferred type. Returns LLUUID::null if not found.
+LLUUID LLInventoryModel::findCatUUID(LLAssetType::EType preferred_type)
+{
+ LLUUID root_id = gAgent.getInventoryRootID();
+ if(LLAssetType::AT_CATEGORY == preferred_type)
+ {
+ return root_id;
+ }
+ if(root_id.notNull())
+ {
+ cat_array_t* cats = NULL;
+ cats = get_ptr_in_map(mParentChildCategoryTree, root_id);
+ if(cats)
+ {
+ S32 count = cats->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(cats->get(i)->getPreferredType() == preferred_type)
+ {
+ return cats->get(i)->getUUID();
+ }
+ }
+ }
+ }
+ return LLUUID::null;
+}
+
+// Convenience function to create a new category. You could call
+// updateCatgory() with a newly generated UUID category, but this
+// version will take care of details like what the name should be
+// based on preferred type. Returns the UUID of the new category.
+LLUUID LLInventoryModel::createNewCategory(const LLUUID& parent_id,
+ LLAssetType::EType preferred_type,
+ const LLString& pname)
+{
+ LLUUID id;
+ id.generate();
+ LLString name = pname;
+ if(!pname.empty())
+ {
+ name.assign(pname);
+ }
+ else if((preferred_type >= LLAssetType::AT_TEXTURE) &&
+ (preferred_type < LLAssetType::AT_COUNT))
+ {
+ name.assign(NEW_CATEGORY_NAMES[preferred_type]);
+ }
+ else
+ {
+ name.assign(NEW_CATEGORY_NAME);
+ }
+
+ // Add the category to the internal representation
+ LLPointer<LLViewerInventoryCategory> cat =
+ new LLViewerInventoryCategory(id, parent_id, preferred_type, name, gAgent.getID());
+ cat->setVersion(LLViewerInventoryCategory::VERSION_INITIAL);
+ cat->setDescendentCount(0);
+ LLCategoryUpdate update(cat->getParentUUID(), 1);
+ accountForUpdate(update);
+ updateCategory(cat);
+
+ // Create the category on the server. We do this to prevent people
+ // from munging their protected folders.
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("CreateInventoryFolder");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("FolderData");
+ cat->packMessage(msg);
+ gAgent.sendReliableMessage();
+
+ // return the folder id of the newly created folder
+ return id;
+}
+
+// Starting with the object specified, add it's descendents to the
+// array provided, but do not add the inventory object specified by
+// id. There is no guaranteed order. Neither array will be erased
+// before adding objects to it. Do not store a copy of the pointers
+// collected - use them, and collect them again later if you need to
+// reference the same objects.
+
+class LLAlwaysCollect : public LLInventoryCollectFunctor
+{
+public:
+ virtual ~LLAlwaysCollect() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ return TRUE;
+ }
+};
+
+void LLInventoryModel::collectDescendents(const LLUUID& id,
+ cat_array_t& cats,
+ item_array_t& items,
+ BOOL include_trash)
+{
+ LLAlwaysCollect always;
+ collectDescendentsIf(id, cats, items, include_trash, always);
+}
+
+void LLInventoryModel::collectDescendentsIf(const LLUUID& id,
+ cat_array_t& cats,
+ item_array_t& items,
+ BOOL include_trash,
+ LLInventoryCollectFunctor& add)
+{
+ // Start with categories
+ if(!include_trash)
+ {
+ LLUUID trash_id(findCatUUID(LLAssetType::AT_TRASH));
+ if(trash_id.notNull() && (trash_id == id))
+ return;
+ }
+ cat_array_t* cat_array = get_ptr_in_map(mParentChildCategoryTree, id);
+ if(cat_array)
+ {
+ S32 count = cat_array->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = cat_array->get(i);
+ if(add(cat,NULL))
+ {
+ cats.put(cat);
+ }
+ collectDescendentsIf(cat->getUUID(), cats, items, include_trash, add);
+ }
+ }
+
+ // Move onto items
+ LLViewerInventoryItem* item = NULL;
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, id);
+ if(item_array)
+ {
+ S32 count = item_array->count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ item = item_array->get(i);
+ if(add(NULL, item))
+ {
+ items.put(item);
+ }
+ }
+ }
+}
+
+// Generates a string containing the path to the item specified by
+// item_id.
+void LLInventoryModel::appendPath(const LLUUID& id, LLString& path)
+{
+ LLString temp;
+ LLInventoryObject* obj = getObject(id);
+ LLUUID parent_id;
+ if(obj) parent_id = obj->getParentUUID();
+ LLString forward_slash("/");
+ while(obj)
+ {
+ obj = getCategory(parent_id);
+ if(obj)
+ {
+ temp.assign(forward_slash + obj->getName() + temp);
+ parent_id = obj->getParentUUID();
+ }
+ }
+ path.append(temp);
+}
+
+// Calling this method with an inventory item will either change an
+// existing item with a matching item_id, or will add the item to the
+// current inventory.
+U32 LLInventoryModel::updateItem(const LLViewerInventoryItem* item)
+{
+ U32 mask = LLInventoryObserver::NONE;
+ if(item->getUUID().isNull())
+ {
+ return mask;
+ }
+ LLViewerInventoryItem* old_item = getItem(item->getUUID());
+ if(old_item)
+ {
+ // We already have an old item, modify it's values
+ LLUUID old_parent_id = old_item->getParentUUID();
+ LLUUID new_parent_id = item->getParentUUID();
+ if(old_parent_id != new_parent_id)
+ {
+ // need to update the parent-child tree
+ item_array_t* item_array;
+ item_array = get_ptr_in_map(mParentChildItemTree, old_parent_id);
+ if(item_array)
+ {
+ item_array->removeObj(old_item);
+ }
+ item_array = get_ptr_in_map(mParentChildItemTree, new_parent_id);
+ if(item_array)
+ {
+ item_array->put(old_item);
+ }
+ mask |= LLInventoryObserver::STRUCTURE;
+ }
+ if(old_item->getName() != item->getName())
+ {
+ mask |= LLInventoryObserver::LABEL;
+ }
+ old_item->copy(item);
+ mask |= LLInventoryObserver::INTERNAL;
+ }
+ else
+ {
+ // Simply add this item
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ addItem(new_item);
+
+ if(item->getParentUUID().isNull())
+ {
+ LLUUID category_id = findCategoryUUIDForType(new_item->getType());
+ new_item->setParent(category_id);
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, category_id);
+ if( item_array )
+ {
+ // *FIX: bit of a hack to call update server from here...
+ new_item->updateServer(TRUE);
+ item_array->put(new_item);
+ }
+ else
+ {
+ llwarns << "Couldn't find parent-child item tree for " << new_item->getName() << llendl;
+ }
+ }
+ else
+ {
+ // *NOTE: The general scheme is that if every byte of the
+ // uuid is 0, except for the last one or two,the use the
+ // last two bytes of the parent id, and match that up
+ // against the type. For now, we're only worried about
+ // lost & found.
+ LLUUID parent_id = item->getParentUUID();
+ if(parent_id == CATEGORIZE_LOST_AND_FOUND_ID)
+ {
+ parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ new_item->setParent(parent_id);
+ }
+ item_array_t* item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_array)
+ {
+ item_array->put(new_item);
+ }
+ else
+ {
+ // Whoops! No such parent, make one.
+ llinfos << "Lost item: " << new_item->getUUID() << " - "
+ << new_item->getName() << llendl;
+ parent_id = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ new_item->setParent(parent_id);
+ item_array = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_array)
+ {
+ // *FIX: bit of a hack to call update server from
+ // here...
+ new_item->updateServer(TRUE);
+ item_array->put(new_item);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ mask |= LLInventoryObserver::ADD;
+ }
+ if(item->getType() == LLAssetType::AT_CALLINGCARD)
+ {
+ mask |= LLInventoryObserver::CALLING_CARD;
+ }
+ addChangedMask(mask, item->getUUID());
+ return mask;
+}
+
+// Calling this method with an inventory category will either change
+// an existing item with the matching id, or it will add the category.
+void LLInventoryModel::updateCategory(const LLViewerInventoryCategory* cat)
+{
+ if(cat->getUUID().isNull())
+ {
+ return;
+ }
+ LLViewerInventoryCategory* old_cat = getCategory(cat->getUUID());
+ if(old_cat)
+ {
+ // We already have an old category, modify it's values
+ U32 mask = LLInventoryObserver::NONE;
+ LLUUID old_parent_id = old_cat->getParentUUID();
+ LLUUID new_parent_id = cat->getParentUUID();
+ if(old_parent_id != new_parent_id)
+ {
+ // need to update the parent-child tree
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, old_parent_id);
+ if(cat_array)
+ {
+ cat_array->removeObj(old_cat);
+ }
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, new_parent_id);
+ if(cat_array)
+ {
+ cat_array->put(old_cat);
+ }
+ mask |= LLInventoryObserver::STRUCTURE;
+ }
+ if(old_cat->getName() != cat->getName())
+ {
+ mask |= LLInventoryObserver::LABEL;
+ }
+ old_cat->copy(cat);
+ addChangedMask(mask, cat->getUUID());
+ }
+ else
+ {
+ // add this category
+ LLPointer<LLViewerInventoryCategory> new_cat = new LLViewerInventoryCategory(cat->getParentUUID());
+ new_cat->copy(cat);
+ addCategory(new_cat);
+
+ // make sure this category is correctly referenced by it's parent.
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(cat_array)
+ {
+ cat_array->put(new_cat);
+ }
+
+ // make space in the tree for this category's children.
+ cat_array_t* catsp = new cat_array_t;
+ item_array_t* itemsp = new item_array_t;
+ mParentChildCategoryTree[new_cat->getUUID()] = catsp;
+ mParentChildItemTree[new_cat->getUUID()] = itemsp;
+ addChangedMask(LLInventoryObserver::ADD, cat->getUUID());
+ }
+}
+
+void LLInventoryModel::moveObject(const LLUUID& object_id, const LLUUID& cat_id)
+{
+ lldebugs << "LLInventoryModel::moveObject()" << llendl;
+ if((object_id == cat_id) || !is_in_map(mCategoryMap, cat_id))
+ {
+ llwarns << "Could not move inventory object " << object_id << " to "
+ << cat_id << llendl;
+ return;
+ }
+ LLViewerInventoryCategory* cat = getCategory(object_id);
+ if(cat && (cat->getParentUUID() != cat_id))
+ {
+ cat_array_t* cat_array;
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(cat_array) cat_array->removeObj(cat);
+ cat_array = get_ptr_in_map(mParentChildCategoryTree, cat_id);
+ cat->setParent(cat_id);
+ if(cat_array) cat_array->put(cat);
+ addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
+ return;
+ }
+ LLViewerInventoryItem* item = getItem(object_id);
+ if(item && (item->getParentUUID() != cat_id))
+ {
+ item_array_t* item_array;
+ item_array = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(item_array) item_array->removeObj(item);
+ item_array = get_ptr_in_map(mParentChildItemTree, cat_id);
+ item->setParent(cat_id);
+ if(item_array) item_array->put(item);
+ addChangedMask(LLInventoryObserver::STRUCTURE, object_id);
+ return;
+ }
+}
+
+// Delete a particular inventory object by ID.
+void LLInventoryModel::deleteObject(const LLUUID& id)
+{
+ lldebugs << "LLInventoryModel::deleteObject()" << llendl;
+ LLPointer<LLInventoryObject> obj = getObject(id);
+ if(obj)
+ {
+ lldebugs << "Deleting inventory object " << id << llendl;
+ mLastItem = NULL;
+ LLUUID parent_id = obj->getParentUUID();
+ mCategoryMap.erase(id);
+ mItemMap.erase(id);
+ //mInventory.erase(id);
+ item_array_t* item_list = get_ptr_in_map(mParentChildItemTree, parent_id);
+ if(item_list)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)((LLInventoryObject*)obj);
+ item_list->removeObj(item);
+ }
+ cat_array_t* cat_list = get_ptr_in_map(mParentChildCategoryTree, parent_id);
+ if(cat_list)
+ {
+ LLViewerInventoryCategory* cat = (LLViewerInventoryCategory*)((LLInventoryObject*)obj);
+ cat_list->removeObj(cat);
+ }
+ item_list = get_ptr_in_map(mParentChildItemTree, id);
+ if(item_list)
+ {
+ delete item_list;
+ mParentChildItemTree.erase(id);
+ }
+ cat_list = get_ptr_in_map(mParentChildCategoryTree, id);
+ if(cat_list)
+ {
+ delete cat_list;
+ mParentChildCategoryTree.erase(id);
+ }
+ addChangedMask(LLInventoryObserver::REMOVE, id);
+ obj = NULL; // delete obj
+ }
+}
+
+// This is a method which collects the descendents of the id
+// provided. If the category is not found, no action is
+// taken. This method goes through the long winded process of
+// cancelling any calling cards, removing server representation of
+// folders, items, etc in a fairly efficient manner.
+void LLInventoryModel::purgeDescendentsOf(const LLUUID& id)
+{
+ EHasChildren children = categoryHasChildren(id);
+ if(children == CHILDREN_NO)
+ {
+ llinfos << "Not purging descendents of " << id << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> cat = getCategory(id);
+ if(cat.notNull())
+ {
+ // do the cache accounting
+ llinfos << "LLInventoryModel::purgeDescendentsOf " << cat->getName()
+ << llendl;
+ S32 descendents = cat->getDescendentCount();
+ if(descendents > 0)
+ {
+ LLCategoryUpdate up(id, -descendents);
+ accountForUpdate(up);
+ }
+
+ // we know that descendent count is 0, aide since the
+ // accounting may actually not do an update, we should force
+ // it here.
+ cat->setDescendentCount(0);
+
+ // send it upstream
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("PurgeInventoryDescendents");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("FolderID", id);
+ gAgent.sendReliableMessage();
+
+ // unceremoniously remove anything we have locally stored.
+ cat_array_t categories;
+ item_array_t items;
+ collectDescendents(id,
+ categories,
+ items,
+ INCLUDE_TRASH);
+ S32 count = items.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ deleteObject(items.get(i)->getUUID());
+ }
+ count = categories.count();
+ for(i = 0; i < count; ++i)
+ {
+ deleteObject(categories.get(i)->getUUID());
+ }
+ }
+}
+
+void LLInventoryModel::deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
+ LLDynamicArray<LLUUID>& item_ids)
+{
+ // Store off tre UUIDS of parents which are being deleted (thus no
+ // need to increment) and the parents which are being modified. We
+ // have to increment the version of the parent with each message
+ // sent upstream since the dataserver will increment each unique
+ // parent per update message.
+ std::set<LLUUID> ignore_parents;
+ update_map_t inc_parents;
+
+ S32 i;
+ S32 count = category_ids.count();
+ BOOL start_new_message = TRUE;
+ LLMessageSystem* msg = gMessageSystem;
+ LLPointer<LLViewerInventoryCategory> cat;
+ for(i = 0; i < count; i++)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ LLUUID cat_id = category_ids.get(i);
+
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, cat_id);
+ cat = getCategory(cat_id);
+ ignore_parents.insert(cat_id);
+ addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, cat_id);
+ if(cat.notNull() && (ignore_parents.find(cat->getParentUUID())==ignore_parents.end()))
+ {
+ --inc_parents[cat->getParentUUID()];
+ }
+ if(msg->isSendFullFast(_PREHASH_FolderData))
+ {
+ start_new_message = TRUE;
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ inc_parents.clear();
+ }
+ }
+
+ count = item_ids.count();
+ std::set<LLUUID>::iterator not_ignored = ignore_parents.end();
+ LLPointer<LLViewerInventoryItem> item;
+ if((0 == count) && (!start_new_message))
+ {
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, LLUUID::null);
+ }
+ for(i = 0; i < count; i++)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_RemoveInventoryObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, LLUUID::null);
+ }
+ LLUUID item_id = item_ids.get(i);
+ msg->nextBlockFast(_PREHASH_ItemData);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ item = getItem(item_id);
+ addChangedMask(LLInventoryObserver::REMOVE | LLInventoryObserver::STRUCTURE, item_id);
+ if(item.notNull() && (ignore_parents.find(item->getParentUUID()) == not_ignored))
+ {
+ --inc_parents[item->getParentUUID()];
+ }
+ if(msg->isSendFullFast(_PREHASH_ItemData))
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ inc_parents.clear();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ accountForUpdate(inc_parents);
+ }
+}
+
+// Add/remove an observer. If the observer is destroyed, be sure to
+// remove it.
+void LLInventoryModel::addObserver(LLInventoryObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLInventoryModel::removeObserver(LLInventoryObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+// Call this method when it's time to update everyone on a new state,
+// by default, the inventory model will not update observers
+// automatically.
+void LLInventoryModel::notifyObservers()
+{
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); )
+ {
+ LLInventoryObserver* observer = *iter;
+ observer->changed(mModifyMask);
+ // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
+ iter = mObservers.upper_bound(observer);
+ }
+
+ mModifyMask = LLInventoryObserver::NONE;
+ mChangedItemIDs.clear();
+}
+
+// store flag for change
+// and id of object change applies to
+void LLInventoryModel::addChangedMask(U32 mask, const LLUUID& referent)
+{
+ mModifyMask |= mask;
+ if (referent.notNull())
+ {
+ mChangedItemIDs.insert(referent);
+ }
+}
+
+// This method to prepares a set of mock inventory which provides
+// minimal functionality before the actual arrival of inventory.
+/*
+void LLInventoryModel::mock(const LLUUID& root_id)
+{
+ llinfos << "LLInventoryModel::mock() " << root_id << llendl;
+ if(root_id.isNull())
+ {
+ llwarns << "Not a valid root id" << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(
+ root_id,
+ LLUUID::null,
+ LLAssetType::AT_CATEGORY,
+ NEW_CATEGORY_NAMES[LLAssetType::AT_ROOT_CATEGORY],
+ gAgent.getID());
+ addCategory(cat);
+ gInventory.buildParentChildMap();
+}
+*/
+
+void LLInventoryModel::fetchDescendentsOf(const LLUUID& folder_id)
+{
+ LLViewerInventoryCategory* cat = getCategory(folder_id);
+ if(!cat)
+ {
+ llwarns << "Asked to fetch descendents of non-existent folder: "
+ << folder_id << llendl;
+ return;
+ }
+ //S32 known_descendents = 0;
+ ///cat_array_t* categories = get_ptr_in_map(mParentChildCategoryTree, folder_id);
+ //item_array_t* items = get_ptr_in_map(mParentChildItemTree, folder_id);
+ //if(categories)
+ //{
+ // known_descendents += categories->count();
+ //}
+ //if(items)
+ //{
+ // known_descendents += items->count();
+ //}
+ if(!cat->fetchDescendents())
+ {
+ //llinfos << "Not fetching descendents" << llendl;
+ }
+}
+
+// static
+bool LLInventoryModel::isEverythingFetched()
+{
+ return (sAllFoldersFetched ? true : false);
+}
+
+//static
+BOOL LLInventoryModel::backgroundFetchActive()
+{
+ return sBackgroundFetchActive;
+}
+
+//static
+void LLInventoryModel::startBackgroundFetch(const LLUUID& cat_id)
+{
+ if (!sAllFoldersFetched)
+ {
+ sBackgroundFetchActive = TRUE;
+ if (cat_id.isNull())
+ {
+ if (!sFullFetchStarted)
+ {
+ sFullFetchStarted = TRUE;
+ sFetchQueue.push_back(gInventoryLibraryRoot);
+ sFetchQueue.push_back(gAgent.getInventoryRootID());
+ gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+ }
+ else
+ {
+ // specific folder requests go to front of queue
+ if (sFetchQueue.empty() || sFetchQueue.front() != cat_id)
+ {
+ sFetchQueue.push_front(cat_id);
+ gIdleCallbacks.addFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+ }
+ }
+}
+
+//static
+void LLInventoryModel::stopBackgroundFetch()
+{
+ if (sBackgroundFetchActive)
+ {
+ sBackgroundFetchActive = FALSE;
+ gIdleCallbacks.deleteFunction(&LLInventoryModel::backgroundFetch, NULL);
+ }
+}
+
+//static
+void LLInventoryModel::backgroundFetch(void*)
+{
+ if (sBackgroundFetchActive)
+ {
+ // no more categories to fetch, stop fetch process
+ if (sFetchQueue.empty())
+ {
+ llinfos << "Inventory fetch completed" << llendl;
+ if (sFullFetchStarted)
+ {
+ sAllFoldersFetched = TRUE;
+ }
+ stopBackgroundFetch();
+ return;
+ }
+
+ F32 fast_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.1f);
+ F32 slow_fetch_time = lerp(sMinTimeBetweenFetches, sMaxTimeBetweenFetches, 0.5f);
+ if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() > slow_fetch_time)
+ {
+ // double timeouts on failure
+ sMinTimeBetweenFetches = llmin(sMinTimeBetweenFetches * 2.f, 10.f);
+ sMaxTimeBetweenFetches = llmin(sMaxTimeBetweenFetches * 2.f, 120.f);
+ llinfos << "Inventory fetch times grown to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
+ // fetch is no longer considered "timely" although we will wait for full time-out
+ sTimelyFetchPending = FALSE;
+ }
+
+ while(1)
+ {
+ if (sFetchQueue.empty())
+ {
+ break;
+ }
+
+ if(gDisconnected)
+ {
+ // just bail if we are disconnected.
+ break;
+ }
+
+ LLViewerInventoryCategory* cat = gInventory.getCategory(sFetchQueue.front());
+
+ // category has been deleted, remove from queue.
+ if (!cat)
+ {
+ sFetchQueue.pop_front();
+ continue;
+ }
+
+ if (sFetchTimer.getElapsedTimeF32() > sMinTimeBetweenFetches &&
+ LLViewerInventoryCategory::VERSION_UNKNOWN == cat->getVersion())
+ {
+ // category exists but has no children yet, fetch the descendants
+ // for now, just request every time and rely on retry timer to throttle
+ if (cat->fetchDescendents())
+ {
+ sFetchTimer.reset();
+ sTimelyFetchPending = TRUE;
+ }
+ else
+ {
+ // The catagory also tracks if it has expired and here it says it hasn't
+ // yet. Get out of here because nothing is going to happen until we
+ // update the timers.
+ break;
+ }
+ }
+ // do I have all my children?
+ else if (gInventory.isCategoryComplete(sFetchQueue.front()))
+ {
+ // finished with this category, remove from queue
+ sFetchQueue.pop_front();
+
+ // add all children to queue
+ parent_cat_map_t::iterator cat_it = gInventory.mParentChildCategoryTree.find(cat->getUUID());
+ if (cat_it != gInventory.mParentChildCategoryTree.end())
+ {
+ cat_array_t* child_categories = cat_it->second;
+
+ for (S32 child_num = 0; child_num < child_categories->count(); child_num++)
+ {
+ sFetchQueue.push_back(child_categories->get(child_num)->getUUID());
+ }
+ }
+
+ // we received a response in less than the fast time
+ if (sTimelyFetchPending && sFetchTimer.getElapsedTimeF32() < fast_fetch_time)
+ {
+ // shrink timeouts based on success
+ sMinTimeBetweenFetches = llmax(sMinTimeBetweenFetches * 0.8f, 0.3f);
+ sMaxTimeBetweenFetches = llmax(sMaxTimeBetweenFetches * 0.8f, 10.f);
+ //llinfos << "Inventory fetch times shrunk to (" << sMinTimeBetweenFetches << ", " << sMaxTimeBetweenFetches << ")" << llendl;
+ }
+
+ sTimelyFetchPending = FALSE;
+ continue;
+ }
+ else if (sFetchTimer.getElapsedTimeF32() > sMaxTimeBetweenFetches)
+ {
+ // received first packet, but our num descendants does not match db's num descendants
+ // so try again later
+ LLUUID fetch_id = sFetchQueue.front();
+ sFetchQueue.pop_front();
+
+ if (sNumFetchRetries++ < MAX_FETCH_RETRIES)
+ {
+ // push on back of queue
+ sFetchQueue.push_back(fetch_id);
+ }
+ sTimelyFetchPending = FALSE;
+ sFetchTimer.reset();
+ break;
+ }
+
+ // not enough time has elapsed to do a new fetch
+ break;
+ }
+ }
+}
+
+void LLInventoryModel::cache(
+ const LLUUID& parent_folder_id,
+ const LLUUID& agent_id)
+{
+ lldebugs << "Caching " << parent_folder_id << " for " << agent_id
+ << llendl;
+ LLViewerInventoryCategory* root_cat = getCategory(parent_folder_id);
+ if(!root_cat) return;
+ cat_array_t categories;
+ categories.put(root_cat);
+ item_array_t items;
+
+ LLCanCache can_cache(this);
+ can_cache(root_cat, NULL);
+ collectDescendentsIf(
+ parent_folder_id,
+ categories,
+ items,
+ INCLUDE_TRASH,
+ can_cache);
+ char agent_id_str[UUID_STR_LENGTH];
+ char inventory_filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_str);
+ std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, agent_id_str));
+ snprintf(
+ inventory_filename,
+ LL_MAX_PATH,
+ CACHE_FORMAT_STRING,
+ path.c_str());
+ saveToFile(inventory_filename, categories, items);
+ std::string gzip_filename(inventory_filename);
+ gzip_filename.append(".gz");
+ if(gzip_file(inventory_filename, gzip_filename.c_str()))
+ {
+ lldebugs << "Successfully compressed " << inventory_filename << llendl;
+ LLFile::remove(inventory_filename);
+ }
+ else
+ {
+ llwarns << "Unable to compress " << inventory_filename << llendl;
+ }
+}
+
+
+void LLInventoryModel::addCategory(LLViewerInventoryCategory* category)
+{
+ //llinfos << "LLInventoryModel::addCategory()" << llendl;
+ if(category)
+ {
+ // Insert category uniquely into the map
+ mCategoryMap[category->getUUID()] = category; // LLPointer will deref and delete the old one
+ //mInventory[category->getUUID()] = category;
+ }
+}
+
+void LLInventoryModel::addItem(LLViewerInventoryItem* item)
+{
+ //llinfos << "LLInventoryModel::addItem()" << llendl;
+ if(item)
+ {
+ mItemMap[item->getUUID()] = item;
+ //mInventory[item->getUUID()] = item;
+ }
+}
+
+// Empty the entire contents
+void LLInventoryModel::empty()
+{
+// llinfos << "LLInventoryModel::empty()" << llendl;
+ std::for_each(
+ mParentChildCategoryTree.begin(),
+ mParentChildCategoryTree.end(),
+ DeletePairedPointer());
+ mParentChildCategoryTree.clear();
+ std::for_each(
+ mParentChildItemTree.begin(),
+ mParentChildItemTree.end(),
+ DeletePairedPointer());
+ mParentChildItemTree.clear();
+ mCategoryMap.clear(); // remove all references (should delete entries)
+ mItemMap.clear(); // remove all references (should delete entries)
+ mLastItem = NULL;
+ //mInventory.clear();
+}
+
+void LLInventoryModel::accountForUpdate(const LLCategoryUpdate& update)
+{
+ LLViewerInventoryCategory* cat = getCategory(update.mCategoryID);
+ if(cat)
+ {
+ bool accounted = false;
+ S32 version = cat->getVersion();
+ if(version != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ S32 descendents_server = cat->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ getDirectDescendentsOf(update.mCategoryID, cats, items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ accounted = true;
+ descendents_actual += update.mDescendentDelta;
+ cat->setDescendentCount(descendents_actual);
+ cat->setVersion(++version);
+ llinfos << "accounted: '" << cat->getName() << "' "
+ << version << " with " << descendents_actual
+ << " descendents." << llendl;
+ }
+ }
+ if(!accounted)
+ {
+ lldebugs << "No accounting for: '" << cat->getName() << "' "
+ << version << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "No category found for update " << update.mCategoryID
+ << llendl;
+ }
+}
+
+void LLInventoryModel::accountForUpdate(
+ const LLInventoryModel::update_list_t& update)
+{
+ update_list_t::const_iterator it = update.begin();
+ update_list_t::const_iterator end = update.end();
+ for(; it != end; ++it)
+ {
+ accountForUpdate(*it);
+ }
+}
+
+void LLInventoryModel::accountForUpdate(
+ const LLInventoryModel::update_map_t& update)
+{
+ LLCategoryUpdate up;
+ update_map_t::const_iterator it = update.begin();
+ update_map_t::const_iterator end = update.end();
+ for(; it != end; ++it)
+ {
+ up.mCategoryID = (*it).first;
+ up.mDescendentDelta = (*it).second.mValue;
+ accountForUpdate(up);
+ }
+}
+
+
+/*
+void LLInventoryModel::incrementCategoryVersion(const LLUUID& category_id)
+{
+ LLViewerInventoryCategory* cat = getCategory(category_id);
+ if(cat)
+ {
+ S32 version = cat->getVersion();
+ if(LLViewerInventoryCategory::VERSION_UNKNOWN != version)
+ {
+ cat->setVersion(version + 1);
+ llinfos << "IncrementVersion: " << cat->getName() << " "
+ << cat->getVersion() << llendl;
+ }
+ else
+ {
+ llinfos << "Attempt to increment version when unknown: "
+ << category_id << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Attempt to increment category: " << category_id << llendl;
+ }
+}
+void LLInventoryModel::incrementCategorySetVersion(
+ const std::set<LLUUID>& categories)
+{
+ if(!categories.empty())
+ {
+ std::set<LLUUID>::const_iterator it = categories.begin();
+ std::set<LLUUID>::const_iterator end = categories.end();
+ for(; it != end; ++it)
+ {
+ incrementCategoryVersion(*it);
+ }
+ }
+}
+*/
+
+
+LLInventoryModel::EHasChildren LLInventoryModel::categoryHasChildren(
+ const LLUUID& cat_id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(cat_id);
+ if(!cat) return CHILDREN_NO;
+ if(cat->getDescendentCount() > 0)
+ {
+ return CHILDREN_YES;
+ }
+ if(cat->getDescendentCount() == 0)
+ {
+ return CHILDREN_NO;
+ }
+ if((cat->getDescendentCount() == LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+ || (cat->getVersion() == LLViewerInventoryCategory::VERSION_UNKNOWN))
+ {
+ return CHILDREN_MAYBE;
+ }
+
+ // Shouldn't have to run this, but who knows.
+ parent_cat_map_t::const_iterator cat_it = mParentChildCategoryTree.find(cat->getUUID());
+ if (cat_it != mParentChildCategoryTree.end() && cat_it->second->count() > 0)
+ {
+ return CHILDREN_YES;
+ }
+ parent_item_map_t::const_iterator item_it = mParentChildItemTree.find(cat->getUUID());
+ if (item_it != mParentChildItemTree.end() && item_it->second->count() > 0)
+ {
+ return CHILDREN_YES;
+ }
+
+ return CHILDREN_NO;
+}
+
+bool LLInventoryModel::isCategoryComplete(const LLUUID& cat_id) const
+{
+ LLViewerInventoryCategory* cat = getCategory(cat_id);
+ if(cat && (cat->getVersion()!=LLViewerInventoryCategory::VERSION_UNKNOWN))
+ {
+ S32 descendents_server = cat->getDescendentCount();
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ getDirectDescendentsOf(cat_id, cats, items);
+ S32 descendents_actual = 0;
+ if(cats && items)
+ {
+ descendents_actual = cats->count() + items->count();
+ }
+ if(descendents_server == descendents_actual)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLInventoryModel::loadSkeleton(
+ const LLInventoryModel::options_t& options,
+ const LLUUID& owner_id)
+{
+ lldebugs << "importing inventory skeleton for " << owner_id << llendl;
+
+ typedef std::set<LLPointer<LLViewerInventoryCategory>, InventoryIDPtrLess> cat_set_t;
+ cat_set_t temp_cats;
+
+ update_map_t child_counts;
+
+ LLUUID id;
+ LLAssetType::EType preferred_type;
+ bool rv = true;
+ for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
+ {
+ LLPointer<LLViewerInventoryCategory> cat = new LLViewerInventoryCategory(owner_id);
+ response_t::const_iterator no_response = (*it).end();
+ response_t::const_iterator skel;
+ skel = (*it).find("name");
+ if(skel == no_response) goto clean_cat;
+ cat->rename(LLString((*skel).second.c_str()));
+ skel = (*it).find("folder_id");
+ if(skel == no_response) goto clean_cat;
+ id.set((*skel).second.c_str());
+ // if an id is null, it locks the viewer.
+ if(id.isNull()) goto clean_cat;
+ cat->setUUID(id);
+ skel = (*it).find("parent_id");
+ if(skel == no_response) goto clean_cat;
+ id.set((*skel).second.c_str());
+ cat->setParent(id);
+ skel = (*it).find("type_default");
+ if(skel == no_response)
+ {
+ preferred_type = LLAssetType::AT_NONE;
+ }
+ else
+ {
+ S32 t = atoi((*skel).second.c_str());
+ preferred_type = (LLAssetType::EType)t;
+ }
+ cat->setPreferredType(preferred_type);
+ skel = (*it).find("version");
+ if(skel == no_response) goto clean_cat;
+ cat->setVersion(atoi((*skel).second.c_str()));
+ temp_cats.insert(cat);
+ continue;
+ clean_cat:
+ llwarns << "Unable to import near " << cat->getName() << llendl;
+ rv = false;
+ //delete cat; // automatic when cat is reasigned or destroyed
+ }
+
+ S32 cached_category_count = 0;
+ S32 cached_item_count = 0;
+ if(!temp_cats.empty())
+ {
+ cat_array_t categories;
+ item_array_t items;
+ char owner_id_str[UUID_STR_LENGTH];
+ owner_id.toString(owner_id_str);
+ std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str));
+ char inventory_filename[LL_MAX_PATH];
+ snprintf(
+ inventory_filename,
+ LL_MAX_PATH,
+ CACHE_FORMAT_STRING,
+ path.c_str());
+ const S32 NO_VERSION = LLViewerInventoryCategory::VERSION_UNKNOWN;
+ std::string gzip_filename(inventory_filename);
+ gzip_filename.append(".gz");
+ FILE* fp = LLFile::fopen(gzip_filename.c_str(), "rb");
+ bool remove_inventory_file = false;
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ if(gunzip_file(gzip_filename.c_str(), inventory_filename))
+ {
+ // we only want to remove the inventory file if it was
+ // gzipped before we loaded, and we successfully
+ // gunziped it.
+ remove_inventory_file = true;
+ }
+ else
+ {
+ llinfos << "Unable to gunzip " << gzip_filename << llendl;
+ }
+ }
+ if(loadFromFile(inventory_filename, categories, items))
+ {
+ // We were able to find a cache of files. So, use what we
+ // found to generate a set of categories we should add. We
+ // will go through each category loaded and if the version
+ // does not match, invalidate the version.
+ S32 count = categories.count();
+ cat_set_t::iterator not_cached = temp_cats.end();
+ std::set<LLUUID> cached_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = categories[i];
+ cat_set_t::iterator cit = temp_cats.find(cat);
+ LLViewerInventoryCategory* tcat = *cit;
+
+ // we can safely ignore anything loaded from file, but
+ // not sent down in the skeleton.
+ if(cit == not_cached) continue;
+ if(cat->getVersion() != tcat->getVersion())
+ {
+ // if the cached version does not match the server version,
+ // throw away the version we have so we can fetch the
+ // correct contents the next time the viewer opens the folder.
+ tcat->setVersion(NO_VERSION);
+ }
+ else
+ {
+ cached_ids.insert(tcat->getUUID());
+ }
+ }
+
+ // go ahead and add the cats returned during the download
+ std::set<LLUUID>::iterator not_cached_id = cached_ids.end();
+ cached_category_count = cached_ids.size();
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ if(cached_ids.find((*it)->getUUID()) == not_cached_id)
+ {
+ // this check is performed so that we do not
+ // mark new folders in the skeleton (and not in cache)
+ // as being cached.
+ LLViewerInventoryCategory *llvic = (*it);
+ llvic->setVersion(NO_VERSION);
+ }
+ addCategory(*it);
+ ++child_counts[(*it)->getParentUUID()];
+ }
+
+ // Add all the items loaded which are parented to a
+ // category with a correctly cached parent
+ count = items.count();
+ cat_map_t::iterator unparented = mCategoryMap.end();
+ for(int i = 0; i < count; ++i)
+ {
+ cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID());
+
+ if(cit != unparented)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ if(cat->getVersion() != NO_VERSION)
+ {
+ addItem(items[i]);
+ cached_item_count += 1;
+ ++child_counts[cat->getUUID()];
+ }
+ }
+ }
+ }
+ else
+ {
+ // go ahead and add everything after stripping the version
+ // information.
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ LLViewerInventoryCategory *llvic = (*it);
+ llvic->setVersion(NO_VERSION);
+ addCategory(*it);
+ }
+ }
+
+ // At this point, we need to set the known descendents for each
+ // category which successfully cached so that we do not
+ // needlessly fetch descendents for categories which we have.
+ update_map_t::iterator no_child_counts = child_counts.end();
+ update_map_t::iterator the_count;
+ for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = (*it);
+ if(cat->getVersion() != NO_VERSION)
+ {
+ the_count = child_counts.find(cat->getUUID());
+ if(the_count != no_child_counts)
+ {
+ cat->setDescendentCount((*the_count).second.mValue);
+ }
+ else
+ {
+ cat->setDescendentCount(0);
+ }
+ }
+ }
+
+ if(remove_inventory_file)
+ {
+ // clean up the gunzipped file.
+ LLFile::remove(inventory_filename);
+ }
+ categories.clear(); // will unref and delete entries
+ }
+
+ llinfos << "Successfully loaded " << cached_category_count
+ << " categories and " << cached_item_count << " items from cache."
+ << llendl;
+
+ return rv;
+}
+
+bool LLInventoryModel::loadMeat(
+ const LLInventoryModel::options_t& options, const LLUUID& owner_id)
+{
+ llinfos << "importing inventory for " << owner_id << llendl;
+ LLPermissions default_perm;
+ default_perm.init(LLUUID::null, owner_id, LLUUID::null, LLUUID::null);
+ LLPointer<LLViewerInventoryItem> item;
+ LLUUID id;
+ LLAssetType::EType type;
+ LLInventoryType::EType inv_type;
+ bool rv = true;
+ for(options_t::const_iterator it = options.begin(); it < options.end(); ++it)
+ {
+ item = new LLViewerInventoryItem;
+ response_t::const_iterator no_response = (*it).end();
+ response_t::const_iterator meat;
+ meat = (*it).find("name");
+ if(meat == no_response) goto clean_item;
+ item->rename(LLString((*meat).second.c_str()));
+ meat = (*it).find("item_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ item->setUUID(id);
+ meat = (*it).find("parent_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ item->setParent(id);
+ meat = (*it).find("type");
+ if(meat == no_response) goto clean_item;
+ type = (LLAssetType::EType)atoi((*meat).second.c_str());
+ item->setType(type);
+ meat = (*it).find("inv_type");
+ if(meat != no_response)
+ {
+ inv_type = (LLInventoryType::EType)atoi((*meat).second.c_str());
+ item->setInventoryType(inv_type);
+ }
+ meat = (*it).find("data_id");
+ if(meat == no_response) goto clean_item;
+ id.set((*meat).second.c_str());
+ if(LLAssetType::AT_CALLINGCARD == type)
+ {
+ LLPermissions perm;
+ perm.init(id, owner_id, LLUUID::null, LLUUID::null);
+ item->setPermissions(perm);
+ }
+ else
+ {
+ meat = (*it).find("perm_mask");
+ if(meat != no_response)
+ {
+ PermissionMask perm_mask = atoi((*meat).second.c_str());
+ default_perm.initMasks(
+ perm_mask, perm_mask, perm_mask, perm_mask, perm_mask);
+ }
+ else
+ {
+ default_perm.initMasks(
+ PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE, PERM_NONE);
+ }
+ item->setPermissions(default_perm);
+ item->setAssetUUID(id);
+ }
+ meat = (*it).find("flags");
+ if(meat != no_response)
+ {
+ item->setFlags(strtoul((*meat).second.c_str(), NULL, 0));
+ }
+ meat = (*it).find("time");
+ if(meat != no_response)
+ {
+ item->setCreationDate(atoi((*meat).second.c_str()));
+ }
+ addItem(item);
+ continue;
+ clean_item:
+ llwarns << "Unable to import near " << item->getName() << llendl;
+ rv = false;
+ //delete item; // automatic when item is reassigned or destroyed
+ }
+ return rv;
+}
+
+// This is a brute force method to rebuild the entire parent-child
+// relations. The overall operation has O(NlogN) performance, which
+// should be sufficient for our needs.
+void LLInventoryModel::buildParentChildMap()
+{
+ llinfos << "LLInventoryModel::buildParentChildMap()" << llendl;
+
+ // *NOTE: I am skipping the logic around folder version
+ // synchronization here because it seems if a folder is lost, we
+ // might actually want to invalidate it at that point - not
+ // attempt to cache. More time & thought is necessary.
+
+ // First the categories. We'll copy all of the categories into a
+ // temporary container to iterate over (oh for real iterators.)
+ // While we're at it, we'll allocate the arrays in the trees.
+ cat_array_t cats;
+ cat_array_t* catsp;
+ item_array_t* itemsp;
+
+ for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ cats.put(cat);
+ if (mParentChildCategoryTree.count(cat->getUUID()) == 0)
+ {
+ catsp = new cat_array_t;
+ mParentChildCategoryTree[cat->getUUID()] = catsp;
+ }
+ if (mParentChildItemTree.count(cat->getUUID()) == 0)
+ {
+ itemsp = new item_array_t;
+ mParentChildItemTree[cat->getUUID()] = itemsp;
+ }
+ }
+
+ // Insert a special parent for the root - so that lookups on
+ // LLUUID::null as the parent work correctly. This is kind of a
+ // blatent wastes of space since we allocate a block of memory for
+ // the array, but whatever - it's not that much space.
+ if (mParentChildCategoryTree.count(LLUUID::null) == 0)
+ {
+ catsp = new cat_array_t;
+ mParentChildCategoryTree[LLUUID::null] = catsp;
+ }
+
+ // Now we have a structure with all of the categories that we can
+ // iterate over and insert into the correct place in the child
+ // category tree.
+ S32 count = cats.count();
+ S32 i;
+ S32 lost = 0;
+ for(i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = cats.get(i);
+ catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(catsp)
+ {
+ catsp->put(cat);
+ }
+ else
+ {
+ // *NOTE: This process could be a lot more efficient if we
+ // used the new MoveInventoryFolder message, but we would
+ // have to continue to do the update & build here. So, to
+ // implement it, we would need a set or map of uuid pairs
+ // which would be (folder_id, new_parent_id) to be sent up
+ // to the server.
+ llinfos << "Lost categroy: " << cat->getUUID() << " - "
+ << cat->getName() << llendl;
+ ++lost;
+ // plop it into the lost & found.
+ LLAssetType::EType pref = cat->getPreferredType();
+ if(LLAssetType::AT_NONE == pref)
+ {
+ cat->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
+ }
+ else if(LLAssetType::AT_CATEGORY == pref)
+ {
+ // it's the root
+ cat->setParent(LLUUID::null);
+ }
+ else
+ {
+ // it's a protected folder.
+ cat->setParent(gAgent.getInventoryRootID());
+ }
+ cat->updateServer(TRUE);
+ catsp = get_ptr_in_map(mParentChildCategoryTree, cat->getParentUUID());
+ if(catsp)
+ {
+ catsp->put(cat);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ if(lost)
+ {
+ llwarns << "Found " << lost << " lost categories." << llendl;
+ }
+
+ // Now the items. We allocated in the last step, so now all we
+ // have to do is iterate over the items and put them in the right
+ // place.
+ item_array_t items;
+ if(!mItemMap.empty())
+ {
+ LLPointer<LLViewerInventoryItem> item;
+ for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+ {
+ item = (*iit).second;
+ items.put(item);
+ }
+ }
+ count = items.count();
+ lost = 0;
+ std::vector<LLUUID> lost_item_ids;
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> item;
+ item = items.get(i);
+ itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(itemsp)
+ {
+ itemsp->put(item);
+ }
+ else
+ {
+ llinfos << "Lost item: " << item->getUUID() << " - "
+ << item->getName() << llendl;
+ ++lost;
+ // plop it into the lost & found.
+ //
+ item->setParent(findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND));
+ // move it later using a special message to move items. If
+ // we update server here, the client might crash.
+ //item->updateServer();
+ lost_item_ids.push_back(item->getUUID());
+ itemsp = get_ptr_in_map(mParentChildItemTree, item->getParentUUID());
+ if(itemsp)
+ {
+ itemsp->put(item);
+ }
+ else
+ {
+ llwarns << "Lost and found Not there!!" << llendl;
+ }
+ }
+ }
+ if(lost)
+ {
+ llwarns << "Found " << lost << " lost items." << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ LLUUID lnf = findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ for(std::vector<LLUUID>::iterator it = lost_item_ids.begin() ; it < lost_item_ids.end(); ++it)
+ {
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, FALSE);
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, (*it));
+ msg->addUUIDFast(_PREHASH_FolderID, lnf);
+ msg->addString("NewName", NULL);
+ if(msg->mCurrentSendTotal >= MTUBYTES)
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+ }
+}
+
+struct LLUUIDAndName
+{
+ LLUUIDAndName() {}
+ LLUUIDAndName(const LLUUID& id, const LLString& name);
+ bool operator==(const LLUUIDAndName& rhs) const;
+ bool operator<(const LLUUIDAndName& rhs) const;
+ bool operator>(const LLUUIDAndName& rhs) const;
+
+ LLUUID mID;
+ LLString mName;
+};
+
+LLUUIDAndName::LLUUIDAndName(const LLUUID& id, const LLString& name) :
+ mID(id), mName(name)
+{
+}
+
+bool LLUUIDAndName::operator==(const LLUUIDAndName& rhs) const
+{
+ return ((mID == rhs.mID) && (mName == rhs.mName));
+}
+
+bool LLUUIDAndName::operator<(const LLUUIDAndName& rhs) const
+{
+ return (mID < rhs.mID);
+}
+
+bool LLUUIDAndName::operator>(const LLUUIDAndName& rhs) const
+{
+ return (mID > rhs.mID);
+}
+
+// Given the current state of the inventory items, figure out the
+// clone information. *FIX: This is sub-optimal, since we can insert
+// this information snurgically, but this makes sure the implementation
+// works before we worry about optimization.
+//void LLInventoryModel::recalculateCloneInformation()
+//{
+// //dumpInventory();
+//
+// // This implements a 'multi-map' like structure to keep track of
+// // how many clones we find.
+// typedef LLDynamicArray<LLViewerInventoryItem*> viewer_item_array_t;
+// typedef std::map<LLUUIDAndName, viewer_item_array_t*> clone_map_t;
+// clone_map_t clone_map;
+// LLUUIDAndName id_and_name;
+// viewer_item_array_t* clones = NULL;
+// LLViewerInventoryItem* item = NULL;
+// for(item = (LLViewerInventoryItem*)mItemMap.getFirstData();
+// item != NULL;
+// item = (LLViewerInventoryItem*)mItemMap.getNextData())
+// {
+// if(item->getType() == LLAssetType::AT_CALLINGCARD)
+// {
+// // if it's a calling card, we key off of the creator id, not
+// // the asset id.
+// id_and_name.mID = item->getCreatorUUID();
+// }
+// else
+// {
+// // if it's not a calling card, we key clones from the
+// // asset id.
+// id_and_name.mID = item->getAssetUUID();
+// }
+// if(id_and_name.mID == LLUUID::null)
+// {
+// continue;
+// }
+// id_and_name.mName = item->getName();
+// if(clone_map.checkData(id_and_name))
+// {
+// clones = clone_map.getData(id_and_name);
+// }
+// else
+// {
+// clones = new viewer_item_array_t;
+// clone_map.addData(id_and_name, clones);
+// }
+// clones->put(item);
+// }
+//
+// S32 count = 0;
+// for(clones = clone_map.getFirstData();
+// clones != NULL;
+// clones = clone_map.getNextData())
+// {
+// count = clones->count();
+// for(S32 i = 0; i < count; i++)
+// {
+// item = clones->get(i);
+// item->setCloneCount(count - 1);
+// //clones[i] = NULL;
+// }
+// delete clones;
+// }
+// clone_map.removeAllData();
+// //dumpInventory();
+//}
+
+// static
+bool LLInventoryModel::loadFromFile(
+ const char* filename,
+ LLInventoryModel::cat_array_t& categories,
+ LLInventoryModel::item_array_t& items)
+{
+ llinfos << "LLInventoryModel::loadFromFile(" << filename << ")" << llendl;
+ FILE* file = LLFile::fopen(filename, "rb");
+ if(!file)
+ {
+ llinfos << "unable to load inventory from: " << filename << llendl;
+ return false;
+ }
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ while(!feof(file) && fgets(buffer, MAX_STRING, file))
+ {
+ sscanf(buffer, " %s", keyword);
+ if(0 == strcmp("inv_category", keyword))
+ {
+ LLPointer<LLViewerInventoryCategory> inv_cat = new LLViewerInventoryCategory(LLUUID::null);
+ if(inv_cat->importFileLocal(file))
+ {
+ categories.put(inv_cat);
+ }
+ else
+ {
+ llwarns << "loadInventoryFromFile(). Ignoring invalid inventory category: " << inv_cat->getName() << llendl;
+ //delete inv_cat; // automatic when inv_cat is reassigned or destroyed
+ }
+ }
+ else if(0 == strcmp("inv_item", keyword))
+ {
+ LLPointer<LLViewerInventoryItem> inv_item = new LLViewerInventoryItem;
+ if( inv_item->importFileLocal(file) )
+ {
+ // *FIX: Need a better solution, this prevents the
+ // application from freezing, but breaks inventory
+ // caching.
+ if(inv_item->getUUID().isNull())
+ {
+ //delete inv_item; // automatic when inv_cat is reassigned or destroyed
+ llwarns << "Ignoring inventory with null item id: "
+ << inv_item->getName() << llendl;
+
+ }
+ else
+ {
+ items.put(inv_item);
+ }
+ }
+ else
+ {
+ llwarns << "loadInventoryFromFile(). Ignoring invalid inventory item: " << inv_item->getName() << llendl;
+ //delete inv_item; // automatic when inv_cat is reassigned or destroyed
+ }
+ }
+ else
+ {
+ llwarns << "Unknown token in inventory file '" << keyword << "'"
+ << llendl;
+ }
+ }
+ fclose(file);
+ return true;
+}
+
+// static
+bool LLInventoryModel::saveToFile(
+ const char* filename,
+ const cat_array_t& categories,
+ const item_array_t& items)
+{
+ llinfos << "LLInventoryModel::saveToFile(" << filename << ")" << llendl;
+ FILE* file = LLFile::fopen(filename, "wb");
+ if(!file)
+ {
+ llwarns << "unable to save inventory to: " << filename << llendl;
+ return false;
+ }
+
+ S32 count = categories.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ LLViewerInventoryCategory* cat = categories[i];
+ if(cat->getVersion() != LLViewerInventoryCategory::VERSION_UNKNOWN)
+ {
+ cat->exportFileLocal(file);
+ }
+ }
+
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ items[i]->exportFile(file);
+ }
+
+ fclose(file);
+ return true;
+}
+
+// message handling functionality
+// static
+void LLInventoryModel::registerCallbacks(LLMessageSystem* msg)
+{
+ //msg->setHandlerFuncFast(_PREHASH_InventoryUpdate,
+ // processInventoryUpdate,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_UseCachedInventory,
+ // processUseCachedInventory,
+ // NULL);
+ msg->setHandlerFuncFast(_PREHASH_UpdateCreateInventoryItem,
+ processUpdateCreateInventoryItem,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_RemoveInventoryItem,
+ processRemoveInventoryItem,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_UpdateInventoryFolder,
+ processUpdateInventoryFolder,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_RemoveInventoryFolder,
+ processRemoveInventoryFolder,
+ NULL);
+ //msg->setHandlerFuncFast(_PREHASH_ExchangeCallingCard,
+ // processExchangeCallingcard,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_AddCallingCard,
+ // processAddCallingcard,
+ // NULL);
+ //msg->setHandlerFuncFast(_PREHASH_DeclineCallingCard,
+ // processDeclineCallingcard,
+ // NULL);
+ msg->setHandlerFuncFast(_PREHASH_SaveAssetIntoInventory,
+ processSaveAssetIntoInventory,
+ NULL);
+ msg->setHandlerFuncFast(_PREHASH_BulkUpdateInventory,
+ processBulkUpdateInventory,
+ NULL);
+ msg->setHandlerFunc("InventoryDescendents", processInventoryDescendents);
+ msg->setHandlerFunc("MoveInventoryItem", processMoveInventoryItem);
+ msg->setHandlerFunc("FetchInventoryReply", processFetchInventoryReply);
+}
+
+/*
+//struct LLUpdateInventoryInfo
+{
+ LLString mFilenameAndPath;
+ BOOL mIsComplete;
+};
+
+// static
+void LLInventoryModel::processInventoryUpdate(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processInventoryUpdate()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an inventory update for the wrong agent." << llendl;
+ return;
+ }
+
+ BOOL is_complete;
+ msg->getBOOLFast(_PREHASH_InventoryData, _PREHASH_IsComplete, is_complete);
+ char filename[MAX_STRING];
+ msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, filename);
+
+ LLUpdateInventoryInfo* info = new LLUpdateInventoryInfo;
+ info->mFilenameAndPath = gDirUtilp->getExpandedFilename( LL_PATH_TEMP, filename );
+ info->mIsComplete = is_complete;
+
+ gXferManager->requestFile(info->mFilenameAndPath,
+ filename,
+ LL_PATH_CACHE, // The remote file is in the data directory
+ gUserServer,
+ TRUE, // Delete the remote file after the transfer
+ processInventoryFile,
+ (void*)info,
+ LLXferManager::HIGH_PRIORITY);
+}
+
+// static
+void LLInventoryModel::processInventoryFile(void** user_data, S32 error_code)
+{
+ llinfos << "LLInventoryModel::processInventoryFile()" << llendl;
+ //gInventory.dumpInventory();
+ LLUpdateInventoryInfo* info = (LLUpdateInventoryInfo*)user_data;
+ if(info && (0 == error_code))
+ {
+ if(info->mIsComplete)
+ {
+ if(gInventory.isLoaded())
+ {
+ // it's a complete update, and we've already loaded
+ // everything. Therefore, we need to blow away
+ // everything.
+ gInventory.empty();
+ }
+ else
+ {
+ // We want to merge with anything that's happened
+ // before the load. Therefore, we only want to get rid
+ // of the root category.
+ gInventory.deleteObject(gAgent.getInventoryRootID());
+ }
+ }
+
+ // decompress if necessary
+ const char* filename = info->mFilenameAndPath.c_str();
+ const char* ext = filename + strlen(filename) - 6;
+ char dst_filename[LL_MAX_PATH];
+ if(0 == strnicmp(ext, "gz", 2))
+ {
+ // it's a gz file. decmpress it.
+ dst_filename[0] = '\0';
+ strncat(dst_filename, filename, (ext - filename));
+ strcat(dst_filename, "tmp");
+ BOOL success = gunzip_file(filename, dst_filename);
+ LLFile::remove(filename);
+ if(!success)
+ {
+ llwarns << "Error loading inventory file. Return code: " << error_code << llendl;
+ LLNotifyBox::showXml("InventoryNetworkCorruption");
+ goto exit;
+ }
+ filename = dst_filename;
+ }
+
+#ifdef DIFF_INVENTORY_FILES
+ char agent_id_string[UUID_STR_LENGTH];
+ char inventory_filename[LL_MAX_PATH];
+ gAgent.getID().toString(agent_id_string);
+ sprintf(inventory_filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ char buffer[MAX_STRING];
+ sprintf(buffer,
+ "c:/cygwin/bin/diff %s %s",
+ inventory_filename,
+ filename);
+ llinfos << buffer << llendl;
+ system(buffer);
+
+#endif
+
+ // load it all up.
+ gInventory.loadFromFile(filename);
+ gInventory.buildParentChildMap();
+ //gInventory.recalculateCloneInformation();
+ gInventory.addChangedMask(LLInventoryObserver::ALL);
+ if(info->mIsComplete)
+ {
+ // Set loaded to true if it's a complete set because it's
+ // poosible for someone to accept an inventory category
+ // before they have their complete inventory. If we marked
+ // it loaded at that point, when the real inventory
+ // arrived, it would erase (on the client anyway) the
+ // existence of that inventory.
+ gInventory.mIsLoaded = TRUE;
+
+ //retrieveIM(gMessageSystem);
+ llinfos << "complete inventory update" << llendl;
+
+ // If we promised you a message on load, here it is
+ if (gViewerStats->getStat(LLViewerStats::ST_INVENTORY_TOO_LONG) > 0.0)
+ {
+ LLNotifyBox::showXml("InventoryLoaded");
+ }
+ }
+ else
+ {
+ llinfos << "incomplete inventory update" << llendl;
+ }
+ gInventory.notifyObservers();
+ LLFile::remove(filename);
+ }
+ else
+ {
+ llwarns << "Eror loading inventory file. Return code: " << error_code
+ << llendl;
+ if(error_code == LL_ERR_TCP_TIMEOUT)
+ {
+ //llwarns << "Re-requesting inventory" << llendl;
+ //gInventory.requestFromServer(gAgent.getID());
+ }
+ }
+
+exit:
+ delete info;
+ //gInventory.dumpInventory();
+}
+
+// static
+void LLInventoryModel::processUseCachedInventory(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLInventoryModel::processUseCachedInventory()" << llendl;
+ char agent_id_string[UUID_STR_LENGTH];
+ gAgent.getID().toString(agent_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, CACHE_FORMAT_STRING, gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ // We want to merge with anything that's happened before the
+ // load. Therefore, we only want to get rid of the root category.
+ gInventory.deleteObject(gAgent.getInventoryRootID());
+ gInventory.loadFromFile(filename);
+ gInventory.buildParentChildMap();
+ //gInventory.recalculateCloneInformation();
+ gInventory.addChangedMask(LLInventoryObserver::ALL);
+ gInventory.mIsLoaded = TRUE;
+ gInventory.notifyObservers();
+ //retrieveIM();
+}
+*/
+
+// static
+void LLInventoryModel::processUpdateCreateInventoryItem(LLMessageSystem* msg, void**)
+{
+ // do accounting and highlight new items if they arrive
+ if (gInventory.messageUpdateCore(msg, true, true))
+ {
+ U32 callback_id;
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
+ msg->getU32Fast(_PREHASH_InventoryData, _PREHASH_CallbackID, callback_id);
+
+ gInventoryCallbacks.fire(callback_id, item_id);
+ }
+
+}
+
+// static
+void LLInventoryModel::processFetchInventoryReply(LLMessageSystem* msg, void**)
+{
+ // no accounting
+ gInventory.messageUpdateCore(msg, false, false);
+}
+
+
+bool LLInventoryModel::messageUpdateCore(LLMessageSystem* msg, bool account, bool highlight_new)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a inventory update for the wrong agent: " << agent_id
+ << llendl;
+ return false;
+ }
+ LLPointer<LLViewerInventoryItem> lastitem; // hack
+ item_array_t items;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ lastitem = titem;
+ titem->unpackMessage(msg, _PREHASH_InventoryData, i);
+ lldebugs << "LLInventoryModel::messageUpdateCore() item id:"
+ << titem->getUUID() << llendl;
+ items.push_back(titem);
+ // examine update for changes.
+ LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
+ if(itemp)
+ {
+ if(titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ }
+ }
+ if(account)
+ {
+ gInventory.accountForUpdate(update);
+ }
+
+ U32 changes = 0x0;
+ for (item_array_t::iterator it = items.begin(); it != items.end(); ++it)
+ {
+ changes |= gInventory.updateItem(*it);
+ }
+ gInventory.notifyObservers();
+ gViewerWindow->getWindow()->decBusyCount();
+
+ // *HACK: Do the 'show' logic for a new item in the inventory if
+ // it is a newly created item.
+ if (highlight_new
+ && (changes & LLInventoryObserver::ADD) == LLInventoryObserver::ADD)
+ {
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(!gInventory.isObjectDescendentOf(lastitem->getUUID(), trash_id))
+ {
+ bool show_keep_discard = lastitem->getPermissions().getCreator() != gAgent.getID();
+ switch(lastitem->getType())
+ {
+ case LLAssetType::AT_NOTECARD:
+ open_notecard(
+ lastitem->getUUID(),
+ LLString("Note: ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ case LLAssetType::AT_LANDMARK:
+ open_landmark(
+ lastitem->getUUID(),
+ LLString(" ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ case LLAssetType::AT_TEXTURE:
+ open_texture(
+ lastitem->getUUID(),
+ LLString("Texture: ") + lastitem->getName(),
+ show_keep_discard);
+ break;
+ default:
+ break;
+ }
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUUID lost_and_found_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LOST_AND_FOUND);
+ BOOL inventory_has_focus = gFocusMgr.childHasKeyboardFocus(view);
+ BOOL user_is_away = gAwayTimer.getStarted();
+
+ // don't select lost and found items if an active user is working in the inventory
+ if (!gInventory.isObjectDescendentOf(lastitem->getUUID(), lost_and_found_id) ||
+ !inventory_has_focus ||
+ user_is_away)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+ view->getPanel()->setSelection(lastitem->getUUID(), TAKE_FOCUS_NO);
+ // HACK to open inventory offers that are
+ // accepted. This information really needs to
+ // flow through the instant messages and
+ // inventory restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryItem(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processRemoveInventoryItem()" << llendl;
+ LLUUID agent_id, item_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a RemoveInventoryItem for the wrong agent."
+ << llendl;
+ return;
+ }
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ std::vector<LLUUID> item_ids;
+ update_map_t update;
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+ LLViewerInventoryItem* itemp = gInventory.getItem(item_id);
+ if(itemp)
+ {
+ // we only bother with the delete and account if we found
+ // the item - this is usually a back-up for permissions,
+ // so frequently the item will already be gone.
+ --update[itemp->getParentUUID()];
+ item_ids.push_back(item_id);
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for(std::vector<LLUUID>::iterator it = item_ids.begin(); it != item_ids.end(); ++it)
+ {
+ gInventory.deleteObject(*it);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processUpdateInventoryFolder(LLMessageSystem* msg,
+ void**)
+{
+ lldebugs << "LLInventoryModel::processUpdateInventoryFolder()" << llendl;
+ LLUUID agent_id, folder_id, parent_id;
+ //char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an UpdateInventoryFolder for the wrong agent."
+ << llendl;
+ return;
+ }
+ LLPointer<LLViewerInventoryCategory> lastfolder; // hack
+ cat_array_t folders;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
+ lastfolder = tfolder;
+ tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
+ // make sure it's not a protected folder
+ tfolder->setPreferredType(LLAssetType::AT_NONE);
+ folders.push_back(tfolder);
+ // examine update for changes.
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
+ if(folderp)
+ {
+ if(tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for (cat_array_t::iterator it = folders.begin(); it != folders.end(); ++it)
+ {
+ gInventory.updateCategory(*it);
+ }
+ gInventory.notifyObservers();
+
+ // *HACK: Do the 'show' logic for a new item in the inventory.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(lastfolder->getUUID(), TAKE_FOCUS_NO);
+ }
+}
+
+// static
+void LLInventoryModel::processRemoveInventoryFolder(LLMessageSystem* msg,
+ void**)
+{
+ lldebugs << "LLInventoryModel::processRemoveInventoryFolder()" << llendl;
+ LLUUID agent_id, folder_id;
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a RemoveInventoryFolder for the wrong agent."
+ << llendl;
+ return;
+ }
+ std::vector<LLUUID> folder_ids;
+ update_map_t update;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, folder_id, i);
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(folder_id);
+ if(folderp)
+ {
+ --update[folderp->getParentUUID()];
+ folder_ids.push_back(folder_id);
+ }
+ }
+ gInventory.accountForUpdate(update);
+ for(std::vector<LLUUID>::iterator it = folder_ids.begin(); it != folder_ids.end(); ++it)
+ {
+ gInventory.deleteObject(*it);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processSaveAssetIntoInventory(LLMessageSystem* msg,
+ void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a SaveAssetIntoInventory message for the wrong agent."
+ << llendl;
+ return;
+ }
+
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id);
+ LLUUID new_asset_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_NewAssetID, new_asset_id);
+
+ lldebugs << "LLInventoryModel::processSaveAssetIntoInventory itemID=" << item_id << llendl;
+
+ LLViewerInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ item->setAssetUUID(new_asset_id);
+ LLCategoryUpdate up(item->getParentUUID(), 0);
+ gInventory.accountForUpdate(up);
+ gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id );
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llinfos << "LLInventoryModel::processSaveAssetIntoInventory item not found: " << item_id << llendl;
+ }
+ if(gViewerWindow)
+ {
+ gViewerWindow->getWindow()->decBusyCount();
+ }
+}
+
+struct InventoryCallbackInfo
+{
+ InventoryCallbackInfo(U32 callback, const LLUUID& inv_id) :
+ mCallback(callback), mInvID(inv_id) {}
+ U32 mCallback;
+ LLUUID mInvID;
+};
+
+// static
+void LLInventoryModel::processBulkUpdateInventory(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a BulkUpdateInventory for the wrong agent." << llendl;
+ return;
+ }
+ LLUUID tid;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, tid);
+ llinfos << "Bulk inventory: " << tid << llendl;
+
+ update_map_t update;
+ cat_array_t folders;
+ S32 count;
+ S32 i;
+ count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryCategory> tfolder = new LLViewerInventoryCategory(gAgent.getID());
+ tfolder->unpackMessage(msg, _PREHASH_FolderData, i);
+ //llinfos << "unpaked folder '" << tfolder->getName() << "' ("
+ // << tfolder->getUUID() << ") in " << tfolder->getParentUUID()
+ // << llendl;
+ if(tfolder->getUUID().notNull())
+ {
+ folders.push_back(tfolder);
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(tfolder->getUUID());
+ if(folderp)
+ {
+ if(tfolder->getParentUUID() == folderp->getParentUUID())
+ {
+ update[tfolder->getParentUUID()];
+ }
+ else
+ {
+ ++update[tfolder->getParentUUID()];
+ --update[folderp->getParentUUID()];
+ }
+ }
+ else
+ {
+ // we could not find the folder, so it is probably
+ // new. However, we only want to attempt accounting
+ // for the parent if we can find the parent.
+ folderp = gInventory.getCategory(tfolder->getParentUUID());
+ if(folderp)
+ {
+ ++update[tfolder->getParentUUID()];
+ }
+ }
+ }
+ }
+
+
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ std::vector<LLUUID> wearable_ids;
+ item_array_t items;
+ std::list<InventoryCallbackInfo> cblist;
+ for(i = 0; i < count; ++i)
+ {
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ titem->unpackMessage(msg, _PREHASH_ItemData, i);
+ //llinfos << "unpaked item '" << titem->getName() << "' in "
+ // << titem->getParentUUID() << llendl;
+ U32 callback_id;
+ msg->getU32Fast(_PREHASH_ItemData, _PREHASH_CallbackID, callback_id);
+ if(titem->getUUID().notNull())
+ {
+ items.push_back(titem);
+ cblist.push_back(InventoryCallbackInfo(callback_id, titem->getUUID()));
+ if (titem->getInventoryType() == LLInventoryType::IT_WEARABLE)
+ {
+ wearable_ids.push_back(titem->getUUID());
+ }
+ // examine update for changes.
+ LLViewerInventoryItem* itemp = gInventory.getItem(titem->getUUID());
+ if(itemp)
+ {
+ if(titem->getParentUUID() == itemp->getParentUUID())
+ {
+ update[titem->getParentUUID()];
+ }
+ else
+ {
+ ++update[titem->getParentUUID()];
+ --update[itemp->getParentUUID()];
+ }
+ }
+ else
+ {
+ LLViewerInventoryCategory* folderp = gInventory.getCategory(titem->getParentUUID());
+ if(folderp)
+ {
+ ++update[titem->getParentUUID()];
+ }
+ }
+ }
+ else
+ {
+ cblist.push_back(InventoryCallbackInfo(callback_id, LLUUID::null));
+ }
+ }
+ gInventory.accountForUpdate(update);
+
+ for (cat_array_t::iterator cit = folders.begin(); cit != folders.end(); ++cit)
+ {
+ gInventory.updateCategory(*cit);
+ }
+ for (item_array_t::iterator iit = items.begin(); iit != items.end(); ++iit)
+ {
+ gInventory.updateItem(*iit);
+ }
+ gInventory.notifyObservers();
+
+ // The incoming inventory could span more than one BulkInventoryUpdate packet,
+ // so record the transaction ID for this purchase, then wear all clothing
+ // that comes in as part of that transaction ID. JC
+ if (LLInventoryView::sWearNewClothing)
+ {
+ LLInventoryView::sWearNewClothingTransactionID = tid;
+ LLInventoryView::sWearNewClothing = FALSE;
+ }
+
+ if (tid == LLInventoryView::sWearNewClothingTransactionID)
+ {
+ count = wearable_ids.size();
+ for (i = 0; i < count; ++i)
+ {
+ LLViewerInventoryItem* wearable_item;
+ wearable_item = gInventory.getItem(wearable_ids[i]);
+ wear_inventory_item_on_avatar(wearable_item);
+ }
+ }
+
+ std::list<InventoryCallbackInfo>::iterator inv_it;
+ for (inv_it = cblist.begin(); inv_it != cblist.end(); ++inv_it)
+ {
+ InventoryCallbackInfo cbinfo = (*inv_it);
+ gInventoryCallbacks.fire(cbinfo.mCallback, cbinfo.mInvID);
+ }
+ // Don't show the inventory. We used to call showAgentInventory here.
+ //LLInventoryView* view = LLInventoryView::getActiveInventory();
+ //if(view)
+ //{
+ // const BOOL take_keyboard_focus = FALSE;
+ // view->setSelection(category.getUUID(), take_keyboard_focus );
+ // LLView* focus_view = gFocusMgr.getKeyboardFocus();
+ // LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+ // // HACK to open inventory offers that are accepted. This information
+ // // really needs to flow through the instant messages and inventory
+ // // transfer/update messages.
+ // if (LLInventoryView::sOpenNextNewItem)
+ // {
+ // view->openSelected();
+ // LLInventoryView::sOpenNextNewItem = FALSE;
+ // }
+ //
+ // // restore keyboard focus
+ // gFocusMgr.setKeyboardFocus(focus_view, callback);
+ //}
+}
+
+// static
+void LLInventoryModel::processInventoryDescendents(LLMessageSystem* msg,void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a UpdateInventoryItem for the wrong agent."
+ << llendl;
+ return;
+ }
+ LLUUID parent_id;
+ msg->getUUID("AgentData", "FolderID", parent_id);
+ LLUUID owner_id;
+ msg->getUUID("AgentData", "OwnerID", owner_id);
+ S32 version;
+ msg->getS32("AgentData", "Version", version);
+ S32 descendents;
+ msg->getS32("AgentData", "Descendents", descendents);
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ LLPointer<LLViewerInventoryCategory> tcategory = new LLViewerInventoryCategory(owner_id);
+ for(i = 0; i < count; ++i)
+ {
+ tcategory->unpackMessage(msg, _PREHASH_FolderData, i);
+ gInventory.updateCategory(tcategory);
+ }
+
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ LLPointer<LLViewerInventoryItem> titem = new LLViewerInventoryItem;
+ for(i = 0; i < count; ++i)
+ {
+ titem->unpackMessage(msg, _PREHASH_ItemData, i);
+ gInventory.updateItem(titem);
+ }
+
+ // set version and descendentcount according to message.
+ LLViewerInventoryCategory* cat = gInventory.getCategory(parent_id);
+ if(cat)
+ {
+ cat->setVersion(version);
+ cat->setDescendentCount(descendents);
+ }
+ gInventory.notifyObservers();
+}
+
+// static
+void LLInventoryModel::processMoveInventoryItem(LLMessageSystem* msg, void**)
+{
+ lldebugs << "LLInventoryModel::processMoveInventoryItem()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got a MoveInventoryItem message for the wrong agent."
+ << llendl;
+ return;
+ }
+
+ LLUUID item_id;
+ LLUUID folder_id;
+ char new_name[MAX_STRING];
+ bool anything_changed = false;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_InventoryData);
+ for(S32 i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+ LLViewerInventoryItem* item = gInventory.getItem(item_id);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_FolderID, folder_id, i);
+ msg->getString("InventoryData", "NewName", MAX_STRING, new_name, i);
+
+ lldebugs << "moving item " << item_id << " to folder "
+ << folder_id << llendl;
+ update_list_t update;
+ LLCategoryUpdate old_folder(item->getParentUUID(), -1);
+ update.push_back(old_folder);
+ LLCategoryUpdate new_folder(folder_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ new_item->setParent(folder_id);
+ if(strlen(new_name) > 0)
+ {
+ new_item->rename(new_name);
+ }
+ gInventory.updateItem(new_item);
+ anything_changed = true;
+ }
+ else
+ {
+ llinfos << "LLInventoryModel::processMoveInventoryItem item not found: " << item_id << llendl;
+ }
+ }
+ if(anything_changed)
+ {
+ gInventory.notifyObservers();
+ }
+}
+
+// *NOTE: DEBUG functionality
+void LLInventoryModel::dumpInventory()
+{
+ llinfos << "\nBegin Inventory Dump\n**********************:" << llendl;
+ llinfos << "mCategroy[] contains " << mCategoryMap.size() << " items." << llendl;
+ for(cat_map_t::iterator cit = mCategoryMap.begin(); cit != mCategoryMap.end(); ++cit)
+ {
+ LLViewerInventoryCategory* cat = cit->second;
+ if(cat)
+ {
+ llinfos << " " << cat->getUUID() << " '" << cat->getName() << "' "
+ << cat->getVersion() << " " << cat->getDescendentCount()
+ << llendl;
+ }
+ else
+ {
+ llinfos << " NULL!" << llendl;
+ }
+ }
+ llinfos << "mItemMap[] contains " << mItemMap.size() << " items." << llendl;
+ for(item_map_t::iterator iit = mItemMap.begin(); iit != mItemMap.end(); ++iit)
+ {
+ LLViewerInventoryItem* item = iit->second;
+ if(item)
+ {
+ llinfos << " " << item->getUUID() << " "
+ << item->getName() << llendl;
+ }
+ else
+ {
+ llinfos << " NULL!" << llendl;
+ }
+ }
+ llinfos << "\n**********************\nEnd Inventory Dump" << llendl;
+}
+
+
+///----------------------------------------------------------------------------
+/// LLInventoryCollectFunctor implementations
+///----------------------------------------------------------------------------
+
+bool LLIsType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat) return TRUE;
+ }
+ if(item)
+ {
+ if(item->getType() == mType) return TRUE;
+ }
+ return FALSE;
+}
+
+bool LLIsNotType::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat) return FALSE;
+ }
+ if(item)
+ {
+ if(item->getType() == mType) return FALSE;
+ else return TRUE;
+ }
+ return TRUE;
+}
+
+bool LLIsTypeWithPermissions::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(mType == LLAssetType::AT_CATEGORY)
+ {
+ if(cat)
+ {
+ return TRUE;
+ }
+ }
+ if(item)
+ {
+ if(item->getType() == mType)
+ {
+ LLPermissions perm = item->getPermissions();
+ if ((perm.getMaskBase() & mPerm) == mPerm)
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+//bool LLIsClone::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+//{
+// if(cat) return FALSE;
+// if(item)
+// {
+// if(mItemMap->getType() == LLAssetType::AT_CALLINGCARD)
+// {
+// if((item->getType() == LLAssetType::AT_CALLINGCARD)
+// && !(item->getCreatorUUID().isNull())
+// && (item->getCreatorUUID() == mItemMap->getCreatorUUID()))
+// {
+// return TRUE;
+// }
+// }
+// else
+// {
+// if((item->getType() == mItemMap->getType())
+// && !(item->getAssetUUID().isNull())
+// && (item->getAssetUUID() == mItemMap->getAssetUUID())
+// && (item->getName() == mItemMap->getName()))
+// {
+// return TRUE;
+// }
+// }
+// }
+// return FALSE;
+//}
+
+bool LLBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (!item->getCreatorUUID().isNull())
+ && (item->getCreatorUUID() != gAgent.getID()))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool LLUniqueBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (item->getCreatorUUID().notNull())
+ && (item->getCreatorUUID() != gAgent.getID()))
+ {
+ mSeen.insert(item->getCreatorUUID());
+ return true;
+ }
+ }
+ return false;
+}
+
+
+bool LLParticularBuddyCollector::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((LLAssetType::AT_CALLINGCARD == item->getType())
+ && (item->getCreatorUUID() == mBuddyID))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+bool LLNameCategoryCollector::operator()(
+ LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ if(cat)
+ {
+ if (!LLString::compareInsensitive(mName.c_str(), cat->getName().c_str()))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+///----------------------------------------------------------------------------
+/// Observers
+///----------------------------------------------------------------------------
+
+void LLInventoryCompletionObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mIncomplete.empty())
+ {
+ for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mComplete.push_back(*it);
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncomplete.empty())
+ {
+ done();
+ }
+ }
+}
+
+void LLInventoryCompletionObserver::watchItem(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ mIncomplete.push_back(id);
+ }
+}
+
+
+void LLInventoryFetchObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mIncomplete.empty())
+ {
+ for(item_ref_t::iterator it = mIncomplete.begin(); it < mIncomplete.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ // BUG: This can cause done() to get called prematurely below.
+ // This happens with the LLGestureInventoryFetchObserver that
+ // loads gestures at startup. JC
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mComplete.push_back(*it);
+ it = mIncomplete.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncomplete.empty())
+ {
+ done();
+ }
+ }
+ //llinfos << "LLInventoryFetchObserver::changed() mComplete size " << mComplete.size() << llendl;
+ //llinfos << "LLInventoryFetchObserver::changed() mIncomplete size " << mIncomplete.size() << llendl;
+}
+
+bool LLInventoryFetchObserver::isEverythingComplete() const
+{
+ return mIncomplete.empty();
+}
+
+void LLInventoryFetchObserver::fetchItems(
+ const LLInventoryFetchObserver::item_ref_t& ids)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ LLUUID owner_id;
+ for(item_ref_t::const_iterator it = ids.begin(); it < ids.end(); ++it)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(item)
+ {
+ if(item->isComplete())
+ {
+ // It's complete, so put it on the complete container.
+ mComplete.push_back(*it);
+ continue;
+ }
+ else
+ {
+ owner_id = item->getPermissions().getOwner();
+ }
+ }
+ else
+ {
+ // assume it's agent inventory.
+ owner_id = gAgent.getID();
+ }
+
+ // It's incomplete, so put it on the incomplete container, and
+ // pack this on the message.
+ mIncomplete.push_back(*it);
+ if(start_new_message)
+ {
+ start_new_message = FALSE;
+ msg->newMessageFast(_PREHASH_FetchInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
+ msg->addUUIDFast(_PREHASH_ItemID, (*it));
+ if(msg->getCurrentSendTotal() >= MTUBYTES)
+ {
+ start_new_message = TRUE;
+ gAgent.sendReliableMessage();
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+// virtual
+void LLInventoryFetchDescendentsObserver::changed(U32 mask)
+{
+ for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat)
+ {
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ if(isComplete(cat))
+ {
+ mCompleteFolders.push_back(*it);
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ if(mIncompleteFolders.empty())
+ {
+ done();
+ }
+}
+
+void LLInventoryFetchDescendentsObserver::fetchDescendents(
+ const folder_ref_t& ids)
+{
+ for(folder_ref_t::const_iterator it = ids.begin(); it != ids.end(); ++it)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat) continue;
+ if(!isComplete(cat))
+ {
+ cat->fetchDescendents();
+ mIncompleteFolders.push_back(*it);
+ }
+ else
+ {
+ mCompleteFolders.push_back(*it);
+ }
+ }
+}
+
+bool LLInventoryFetchDescendentsObserver::isEverythingComplete() const
+{
+ return mIncompleteFolders.empty();
+}
+
+bool LLInventoryFetchDescendentsObserver::isComplete(LLViewerInventoryCategory* cat)
+{
+ S32 version = cat->getVersion();
+ S32 descendents = cat->getDescendentCount();
+ if((LLViewerInventoryCategory::VERSION_UNKNOWN == version)
+ || (LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN == descendents))
+ {
+ return false;
+ }
+ // it might be complete - check known descendents against
+ // currently available.
+ LLInventoryModel::cat_array_t* cats;
+ LLInventoryModel::item_array_t* items;
+ gInventory.getDirectDescendentsOf(cat->getUUID(), cats, items);
+ if(!cats || !items)
+ {
+ // bit of a hack - pretend we're done if they are gone or
+ // incomplete. should never know, but it would suck if this
+ // kept tight looping because of a corrupt memory state.
+ return true;
+ }
+ S32 known = cats->count() + items->count();
+ if(descendents == known)
+ {
+ // hey - we're done.
+ return true;
+ }
+ return false;
+}
+
+void LLInventoryFetchComboObserver::changed(U32 mask)
+{
+ if(!mIncompleteItems.empty())
+ {
+ for(item_ref_t::iterator it = mIncompleteItems.begin(); it < mIncompleteItems.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ it = mIncompleteItems.erase(it);
+ continue;
+ }
+ if(item->isComplete())
+ {
+ mCompleteItems.push_back(*it);
+ it = mIncompleteItems.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ }
+ if(!mIncompleteFolders.empty())
+ {
+ for(folder_ref_t::iterator it = mIncompleteFolders.begin(); it < mIncompleteFolders.end();)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*it);
+ if(!cat)
+ {
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ if(gInventory.isCategoryComplete(*it))
+ {
+ mCompleteFolders.push_back(*it);
+ it = mIncompleteFolders.erase(it);
+ continue;
+ }
+ ++it;
+ }
+ }
+ if(!mDone && mIncompleteItems.empty() && mIncompleteFolders.empty())
+ {
+ mDone = true;
+ done();
+ }
+}
+
+void LLInventoryFetchComboObserver::fetch(
+ const folder_ref_t& folder_ids,
+ const item_ref_t& item_ids)
+{
+ lldebugs << "LLInventoryFetchComboObserver::fetch()" << llendl;
+ for(folder_ref_t::const_iterator fit = folder_ids.begin(); fit != folder_ids.end(); ++fit)
+ {
+ LLViewerInventoryCategory* cat = gInventory.getCategory(*fit);
+ if(!cat) continue;
+ if(!gInventory.isCategoryComplete(*fit))
+ {
+ cat->fetchDescendents();
+ lldebugs << "fetching folder " << *fit <<llendl;
+ mIncompleteFolders.push_back(*fit);
+ }
+ else
+ {
+ mCompleteFolders.push_back(*fit);
+ lldebugs << "completing folder " << *fit <<llendl;
+ }
+ }
+
+ // Now for the items - we fetch everything which is not a direct
+ // descendent of an incomplete folder because the item will show
+ // up in an inventory descendents message soon enough so we do not
+ // have to fetch it individually.
+ LLUUID owner_id;
+ LLMessageSystem* msg = gMessageSystem;
+ bool start_new_message = true;
+ for(item_ref_t::const_iterator iit = item_ids.begin(); iit != item_ids.end(); ++iit)
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*iit);
+ if(!item)
+ {
+ lldebugs << "uanble to find item " << *iit << llendl;
+ continue;
+ }
+ if(item->isComplete())
+ {
+ // It's complete, so put it on the complete container.
+ mCompleteItems.push_back(*iit);
+ lldebugs << "completing item " << *iit << llendl;
+ continue;
+ }
+ else
+ {
+ mIncompleteItems.push_back(*iit);
+ owner_id = item->getPermissions().getOwner();
+ }
+ if(std::find(mIncompleteFolders.begin(), mIncompleteFolders.end(), item->getParentUUID()) == mIncompleteFolders.end())
+ {
+ lldebugs << "fetching item " << *iit << llendl;
+ if(start_new_message)
+ {
+ start_new_message = false;
+ msg->newMessageFast(_PREHASH_FetchInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ }
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_OwnerID, owner_id);
+ msg->addUUIDFast(_PREHASH_ItemID, (*iit));
+ if(msg->isSendFullFast(_PREHASH_InventoryData))
+ {
+ start_new_message = true;
+ gAgent.sendReliableMessage();
+ }
+ }
+ else
+ {
+ lldebugs << "not worrying about " << *iit << llendl;
+ }
+ }
+ if(!start_new_message)
+ {
+ gAgent.sendReliableMessage();
+ }
+}
+
+void LLInventoryExistenceObserver::watchItem(const LLUUID& id)
+{
+ if(id.notNull())
+ {
+ mMIA.push_back(id);
+ }
+}
+
+void LLInventoryExistenceObserver::changed(U32 mask)
+{
+ // scan through the incomplete items and move or erase them as
+ // appropriate.
+ if(!mMIA.empty())
+ {
+ for(item_ref_t::iterator it = mMIA.begin(); it < mMIA.end(); )
+ {
+ LLViewerInventoryItem* item = gInventory.getItem(*it);
+ if(!item)
+ {
+ ++it;
+ continue;
+ }
+ mExist.push_back(*it);
+ it = mMIA.erase(it);
+ }
+ if(mMIA.empty())
+ {
+ done();
+ }
+ }
+}
+
+LLInventoryTransactionObserver::LLInventoryTransactionObserver(
+ const LLTransactionID& transaction_id) :
+ mTransactionID(transaction_id)
+{
+}
+
+void LLInventoryTransactionObserver::changed(U32 mask)
+{
+ if(mask & LLInventoryObserver::ADD)
+ {
+ // This could be it - see if we are processing a bulk update
+ LLMessageSystem* msg = gMessageSystem;
+ if(msg->getMessageName()
+ && (0 == strcmp(msg->getMessageName(), "BulkUpdateInventory")))
+ {
+ // we have a match for the message - now check the
+ // transaction id.
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TransactionID, id);
+ if(id == mTransactionID)
+ {
+ // woo hoo, we found it
+ folder_ref_t folders;
+ item_ref_t items;
+ S32 count;
+ count = msg->getNumberOfBlocksFast(_PREHASH_FolderData);
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_FolderData, _PREHASH_FolderID, id, i);
+ if(id.notNull())
+ {
+ folders.push_back(id);
+ }
+ }
+ count = msg->getNumberOfBlocksFast(_PREHASH_ItemData);
+ for(i = 0; i < count; ++i)
+ {
+ msg->getUUIDFast(_PREHASH_ItemData, _PREHASH_ItemID, id, i);
+ if(id.notNull())
+ {
+ items.push_back(id);
+ }
+ }
+
+ // call the derived class the implements this method.
+ done(folders, items);
+ }
+ }
+ }
+}
+
+
+///----------------------------------------------------------------------------
+/// LLAssetIDMatches
+///----------------------------------------------------------------------------
+bool LLAssetIDMatches ::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ return (item && item->getAssetUUID() == mAssetID);
+}
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+
+/*
+BOOL decompress_file(const char* src_filename, const char* dst_filename)
+{
+ BOOL rv = FALSE;
+ gzFile src = NULL;
+ U8* buffer = NULL;
+ FILE* dst = NULL;
+ S32 bytes = 0;
+ const S32 DECOMPRESS_BUFFER_SIZE = 32000;
+
+ // open the files
+ src = gzopen(src_filename, "rb");
+ if(!src) goto err_decompress;
+ dst = LLFile::fopen(dst_filename, "wb");
+ if(!dst) goto err_decompress;
+
+ // decompress.
+ buffer = new U8[DECOMPRESS_BUFFER_SIZE + 1];
+
+ do
+ {
+ bytes = gzread(src, buffer, DECOMPRESS_BUFFER_SIZE);
+ if (bytes < 0)
+ {
+ goto err_decompress;
+ }
+
+ fwrite(buffer, bytes, 1, dst);
+ } while(gzeof(src) == 0);
+
+ // success
+ rv = TRUE;
+
+ err_decompress:
+ if(src != NULL) gzclose(src);
+ if(buffer != NULL) delete[] buffer;
+ if(dst != NULL) fclose(dst);
+ return rv;
+}
+*/
diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h
new file mode 100644
index 0000000000..25f08322be
--- /dev/null
+++ b/indra/newview/llinventorymodel.h
@@ -0,0 +1,757 @@
+/**
+ * @file llinventorymodel.h
+ * @brief LLInventoryModel class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLINVENTORYMODEL_H
+#define LL_LLINVENTORYMODEL_H
+
+#include "llassetstorage.h"
+#include "lldarray.h"
+//#include "llskiplist.h"
+//#include "llptrskipmap.h"
+#include "lluuid.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryObserver
+//
+// This class is designed to be a simple abstract base class which can
+// relay messages when the inventory changes.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryObserver
+{
+public:
+ // This enumeration is a way to refer to what changed in a more
+ // human readable format. You can mask the value provided by
+ // chaged() to see if the observer is interested in the change.
+ enum
+ {
+ NONE = 0,
+ LABEL = 1, // name changed
+ INTERNAL = 2, // internal change, eg, asset uuid different
+ ADD = 4, // something added
+ REMOVE = 8, // something deleted
+ STRUCTURE = 16, // structural change, eg, item or folder moved
+ CALLING_CARD = 32, // online, grant status, cancel, etc change
+ ALL = 0xffffffff
+ };
+ virtual ~LLInventoryObserver() {};
+ virtual void changed(U32 mask) = 0;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryModel
+//
+// This class represents a collection of inventory, and provides
+// efficient ways to access that information. This class could in
+// theory be used for any place where you need inventory, though it
+// optimizes for time efficiency - not space efficiency, probably
+// making it inappropriate for use on tasks.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class LLInventoryObject;
+class LLInventoryItem;
+class LLInventoryCategory;
+class LLViewerInventoryItem;
+class LLViewerInventoryCategory;
+class LLViewerInventoryItem;
+class LLViewerInventoryCategory;
+class LLMessageSystem;
+class LLInventoryCollectFunctor;
+
+class LLInventoryModel
+{
+public:
+ typedef enum e_has_children
+ {
+ CHILDREN_NO,
+ CHILDREN_YES,
+ CHILDREN_MAYBE
+ }EHasChildren;
+
+ // These are used a lot...
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t;
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t;
+
+ // construction & destruction
+ LLInventoryModel();
+ ~LLInventoryModel();
+
+ //
+ // Accessors
+ //
+
+ // This is a convenience function to check if one object has a
+ // parent chain up to the category specified by UUID.
+ BOOL isObjectDescendentOf(const LLUUID& obj_id, const LLUUID& cat_id);
+
+ // Get the object by id. Returns NULL if not found.
+ // * WARNING: use the pointer returned for read operations - do
+ // not modify the object values in place or you will break stuff.
+ LLInventoryObject* getObject(const LLUUID& id) const;
+
+ // Get the item by id. Returns NULL if not found.
+ // * WARNING: use the pointer for read operations - use the
+ // updateItem() method to actually modify values.
+ LLViewerInventoryItem* getItem(const LLUUID& id) const;
+
+ // Get the category by id. Returns NULL if not found.
+ // * WARNING: use the pointer for read operations - use the
+ // updateCategory() method to actually modify values.
+ LLViewerInventoryCategory* getCategory(const LLUUID& id) const;
+
+ // Return the number of items or categories
+ S32 getItemCount() const;
+ S32 getCategoryCount() const;
+
+ // Return the direct descendents of the id provided.Set passed
+ // in values to NULL if the call fails.
+ // *WARNING: The array provided points straight into the guts of
+ // this object, and should only be used for read operations, since
+ // modifications may invalidate the internal state of the
+ // inventory.
+ void getDirectDescendentsOf(const LLUUID& cat_id,
+ cat_array_t*& categories,
+ item_array_t*& items) const;
+
+ // Starting with the object specified, add it's descendents to the
+ // array provided, but do not add the inventory object specified
+ // by id. There is no guaranteed order. Neither array will be
+ // erased before adding objects to it. Do not store a copy of the
+ // pointers collected - use them, and collect them again later if
+ // you need to reference the same objects.
+ enum { EXCLUDE_TRASH = FALSE, INCLUDE_TRASH = TRUE };
+ void collectDescendents(const LLUUID& id,
+ cat_array_t& categories,
+ item_array_t& items,
+ BOOL include_trash);
+
+ void collectDescendentsIf(const LLUUID& id,
+ cat_array_t& categories,
+ item_array_t& items,
+ BOOL include_trash,
+ LLInventoryCollectFunctor& add);
+
+ //
+ // Mutators
+ //
+
+ // Calling this method with an inventory item will either change
+ // an existing item with a matching item_id, or will add the item
+ // to the current inventory. Returns the change mask generated by
+ // the update. No notifcation will be sent to observers. This
+ // method will only generate network traffic if the item had to be
+ // reparented.
+ // *NOTE: In usage, you will want to perform cache accounting
+ // operations in LLInventoryModel::accountForUpdate() or
+ // LLViewerInventoryItem::updateServer() before calling this
+ // method.
+ U32 updateItem(const LLViewerInventoryItem* item);
+
+ // Calling this method with an inventory category will either
+ // change an existing item with the matching id, or it will add
+ // the category. No notifcation will be sent to observers. This
+ // method will only generate network traffic if the item had to be
+ // reparented.
+ // *NOTE: In usage, you will want to perform cache accounting
+ // operations in LLInventoryModel::accountForUpdate() or
+ // LLViewerInventoryCategory::updateServer() before calling this
+ // method.
+ void updateCategory(const LLViewerInventoryCategory* cat);
+
+ // This method will move the specified object id to the specified
+ // category, update the internal structures. No cache accounting,
+ // observer notification, or server update is performed.
+ void moveObject(const LLUUID& object_id, const LLUUID& cat_id);
+
+ // delete a particular inventory object by ID. This will purge one
+ // object from the internal data structures maintaining a
+ // cosistent internal state. No cache accounting, observer
+ // notification, or server update is performed.
+ void deleteObject(const LLUUID& id);
+
+ // This is a method which collects the descendents of the id
+ // provided. If the category is not found, no action is
+ // taken. This method goes through the long winded process of
+ // removing server representation of folders and items while doing
+ // cache accounting in a fairly efficient manner. This method does
+ // not notify observers (though maybe it shouldd...)
+ void purgeDescendentsOf(const LLUUID& id);
+
+ // This method optimally removes the referenced categories and
+ // items from the current agent's inventory in the database. It
+ // performs all of the during deletion. The local representation
+ // is not removed.
+ void deleteFromServer(LLDynamicArray<LLUUID>& category_ids,
+ LLDynamicArray<LLUUID>& item_ids);
+
+ // Add/remove an observer. If the observer is destroyed, be sure
+ // to remove it.
+ void addObserver(LLInventoryObserver* observer);
+ void removeObserver(LLInventoryObserver* observer);
+
+ //
+ // Misc Methods
+ //
+
+ // findCategoryUUIDForType() returns the uuid of the category that
+ // specifies 'type' as what it defaults to containing. The
+ // category is not necessarily only for that type. *NOTE: This
+ // will create a new inventory category on the fly if one does not
+ // exist.
+ LLUUID findCategoryUUIDForType(LLAssetType::EType preferred_type);
+
+ // Call this method when it's time to update everyone on a new
+ // state, by default, the inventory model will not update
+ // observers automatically.
+ void notifyObservers();
+
+ // This allows outsiders to tell the inventory if something has
+ // been changed 'under the hood', but outside the control of the
+ // inventory. For example, if we grant someone modify permissions,
+ // then that changes the data structures for LLAvatarTracker, but
+ // potentially affects inventory observers. This API makes sure
+ // that the next notify will include that notification.
+ void addChangedMask(U32 mask, const LLUUID& referent);
+
+ const std::set<LLUUID>& getChangedIDs() { return mChangedItemIDs; }
+
+ // This method to prepares a set of mock inventory which provides
+ // minimal functionality before the actual arrival of inventory.
+ //void mock(const LLUUID& root_id);
+
+ // make sure we have the descendents in the structure.
+ void fetchDescendentsOf(const LLUUID& folder_id);
+
+ // call this method to request the inventory.
+ //void requestFromServer(const LLUUID& agent_id);
+
+ // call this method on logout to save a terse representation
+ void cache(const LLUUID& parent_folder_id, const LLUUID& agent_id);
+
+ // Generates a string containing the path to the item specified by
+ // item_id.
+ void appendPath(const LLUUID& id, LLString& path);
+
+ // message handling functionality
+ static void registerCallbacks(LLMessageSystem* msg);
+
+ // Convenience function to create a new category. You could call
+ // updateCatgory() with a newly generated UUID category, but this
+ // version will take care of details like what the name should be
+ // based on preferred type. Returns the UUID of the new
+ // category. If you want to use the default name based on type,
+ // pass in a NULL to the 'name parameter.
+ LLUUID createNewCategory(const LLUUID& parent_id,
+ LLAssetType::EType preferred_type,
+ const LLString& name);
+
+ // methods to load up inventory skeleton & meat. These are used
+ // during authentication. return true if everything parsed.
+ typedef std::map<std::string, std::string> response_t;
+ typedef std::vector<response_t> options_t;
+ bool loadSkeleton(const options_t& options, const LLUUID& owner_id);
+ bool loadMeat(const options_t& options, const LLUUID& owner_id);
+
+ // This is a brute force method to rebuild the entire parent-child
+ // relations.
+ void buildParentChildMap();
+
+ //
+ // Category accounting.
+ //
+
+ // This structure represents the number of items added or removed
+ // from a category.
+ struct LLCategoryUpdate
+ {
+ LLCategoryUpdate() : mDescendentDelta(0) {}
+ LLCategoryUpdate(const LLUUID& category_id, S32 delta) :
+ mCategoryID(category_id),
+ mDescendentDelta(delta) {}
+ LLUUID mCategoryID;
+ S32 mDescendentDelta;
+ };
+ typedef std::vector<LLCategoryUpdate> update_list_t;
+
+ // This structure eixts to make it easier to account for deltas in
+ // a map.
+ struct LLInitializedS32
+ {
+ LLInitializedS32() : mValue(0) {}
+ S32 mValue;
+ LLInitializedS32& operator++() { ++mValue; return *this; }
+ LLInitializedS32& operator--() { --mValue; return *this; }
+ };
+ typedef std::map<LLUUID, LLInitializedS32> update_map_t;
+
+ // Call these methods when there are category updates, but call
+ // them *before* the actual update so the method can do descendent
+ // accounting correctly.
+ void accountForUpdate(const LLCategoryUpdate& update);
+ void accountForUpdate(const update_list_t& updates);
+ void accountForUpdate(const update_map_t& updates);
+
+ // Return child status of category children. yes/no/maybe
+ EHasChildren categoryHasChildren(const LLUUID& cat_id) const;
+
+ // returns true iff category version is known and theoretical
+ // descendents == actual descendents.
+ bool isCategoryComplete(const LLUUID& cat_id) const;
+
+ // start and stop background breadth-first fetching of inventory contents
+ // this gets triggered when performing a filter-search
+ static void startBackgroundFetch(const LLUUID& cat_id = LLUUID::null); // start fetch process
+ static void stopBackgroundFetch(); // stop fetch process
+ static BOOL backgroundFetchActive();
+ static bool isEverythingFetched();
+ static void backgroundFetch(void*); // background fetch idle function
+
+protected:
+
+ // Internal methods which add inventory and make sure that all of
+ // the internal data structures are consistent. These methods
+ // should be passed pointers of newly created objects, and the
+ // instance will take over the memory management from there.
+ void addCategory(LLViewerInventoryCategory* category);
+ void addItem(LLViewerInventoryItem* item);
+
+ // Internal method which looks for a category with the specified
+ // preferred type. Returns LLUUID::null if not found
+ LLUUID findCatUUID(LLAssetType::EType preferred_type);
+
+ // Empty the entire contents
+ void empty();
+
+ // Given the current state of the inventory items, figure out the
+ // clone information. *FIX: This is sub-optimal, since we can
+ // insert this information snurgically, but this makes sure the
+ // implementation works before we worry about optimization.
+ //void recalculateCloneInformation();
+
+ // file import/export.
+ static bool loadFromFile(
+ const char* filename,
+ cat_array_t& categories,
+ item_array_t& items);
+ static bool saveToFile(
+ const char* filename,
+ const cat_array_t& categories,
+ const item_array_t& items);
+
+ // message handling functionality
+ //static void processUseCachedInventory(LLMessageSystem* msg, void**);
+ static void processUpdateCreateInventoryItem(LLMessageSystem* msg, void**);
+ static void processRemoveInventoryItem(LLMessageSystem* msg, void**);
+ static void processUpdateInventoryFolder(LLMessageSystem* msg, void**);
+ static void processRemoveInventoryFolder(LLMessageSystem* msg, void**);
+ //static void processExchangeCallingcard(LLMessageSystem* msg, void**);
+ //static void processAddCallingcard(LLMessageSystem* msg, void**);
+ //static void processDeclineCallingcard(LLMessageSystem* msg, void**);
+ static void processSaveAssetIntoInventory(LLMessageSystem* msg, void**);
+ static void processBulkUpdateInventory(LLMessageSystem* msg, void**);
+ static void processInventoryDescendents(LLMessageSystem* msg, void**);
+ static void processMoveInventoryItem(LLMessageSystem* msg, void**);
+ static void processFetchInventoryReply(LLMessageSystem* msg, void**);
+
+ bool messageUpdateCore(LLMessageSystem* msg, bool do_accounting, bool highlight_new);
+
+protected:
+ // Varaibles used to track what has changed since the last notify.
+ U32 mModifyMask;
+ typedef std::set<LLUUID> changed_items_t;
+ changed_items_t mChangedItemIDs;
+
+ // Information for tracking the actual inventory. We index this
+ // information in a lot of different ways so we can access
+ // the inventory using several different identifiers.
+ // mInventory member data is the 'master' list of inventory, and
+ // mCategoryMap and mItemMap store uuid->object mappings.
+ typedef std::map<LLUUID, LLPointer<LLViewerInventoryCategory> > cat_map_t;
+ typedef std::map<LLUUID, LLPointer<LLViewerInventoryItem> > item_map_t;
+ //inv_map_t mInventory;
+ cat_map_t mCategoryMap;
+ item_map_t mItemMap;
+
+ // cache recent lookups
+ mutable LLPointer<LLViewerInventoryItem> mLastItem;
+
+ // This last set of indices is used to map parents to children.
+ //LLPtrSkipMap<const LLUUID, cat_array_t*> mParentChildCategoryTree;
+ //LLPtrSkipMap<const LLUUID, item_array_t*> mParentChildItemTree;
+ typedef std::map<LLUUID, cat_array_t*> parent_cat_map_t;
+ typedef std::map<LLUUID, item_array_t*> parent_item_map_t;
+ parent_cat_map_t mParentChildCategoryTree;
+ parent_item_map_t mParentChildItemTree;
+
+ typedef std::set<LLInventoryObserver*> observer_list_t;
+ observer_list_t mObservers;
+
+ // completing the fetch once per session should be sufficient
+ static BOOL sBackgroundFetchActive;
+ static BOOL sTimelyFetchPending;
+ static BOOL sAllFoldersFetched;
+ static BOOL sFullFetchStarted;
+ static S32 sNumFetchRetries;
+ static LLFrameTimer sFetchTimer;
+ static F32 sMinTimeBetweenFetches;
+ static F32 sMaxTimeBetweenFetches;
+
+public:
+ // *NOTE: DEBUG functionality
+ void dumpInventory();
+};
+
+// a special inventory model for the agent
+extern LLInventoryModel gInventory;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryCollectFunctor
+//
+// Base class for LLInventoryModel::collectDescendentsIf() method
+// which accepts an instance of one of these objects to use as the
+// function to determine if it should be added. Derive from this class
+// and override the () operator to return TRUE if you want to collect
+// the category or item passed in.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryCollectFunctor
+{
+public:
+ virtual ~LLInventoryCollectFunctor(){};
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) = 0;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLAssetIDMatches
+//
+// This functor finds inventory items pointing to the specified asset
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLViewerInventoryItem;
+
+class LLAssetIDMatches : public LLInventoryCollectFunctor
+{
+public:
+ LLAssetIDMatches(const LLUUID& asset_id) : mAssetID(asset_id) {}
+ virtual ~LLAssetIDMatches() {}
+ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+protected:
+ LLUUID mAssetID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsType
+//
+// Implementation of a LLInventoryCollectFunctor which returns TRUE if
+// the type is the type passed in during construction.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIsType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsType(LLAssetType::EType type) : mType(type) {}
+ virtual ~LLIsType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsNotType
+//
+// Implementation of a LLInventoryCollectFunctor which returns FALSE if the
+// type is the type passed in during construction, otherwise false.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLIsNotType : public LLInventoryCollectFunctor
+{
+public:
+ LLIsNotType(LLAssetType::EType type) : mType(type) {}
+ virtual ~LLIsNotType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+};
+
+class LLIsTypeWithPermissions : public LLInventoryCollectFunctor
+{
+public:
+ LLIsTypeWithPermissions(LLAssetType::EType type, const PermissionBit perms, const LLUUID &agent_id, const LLUUID &group_id)
+ : mType(type), mPerm(perms), mAgentID(agent_id), mGroupID(group_id) {}
+ virtual ~LLIsTypeWithPermissions() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLAssetType::EType mType;
+ PermissionBit mPerm;
+ LLUUID mAgentID;
+ LLUUID mGroupID;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLIsClone
+//
+// Implementation of a LLInventoryCollectFunctor which returns TRUE if
+// the object is a clone of the item passed in during
+// construction.
+//
+// *NOTE: Since clone information is determined based off of asset id
+// (or creator with calling cards), if the id is NULL, it has no
+// clones - even itself.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+//class LLIsClone : public LLInventoryCollectFunctor
+//{
+//public:
+// LLIsClone(LLViewerInventoryItem* item) : mItem(item) {}
+// virtual ~LLIsClone() {}
+// virtual bool operator()(LLViewerInventoryCategory* cat,
+// LLViewerInventoryItem* item);
+//protected:
+// LLPointer<LLViewerInventoryItem> mItem;
+//};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLBuddyCollector
+//
+// Simple class that collects calling cards that are not null, and not
+// the agent. Duplicates are possible.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLBuddyCollector() {}
+ virtual ~LLBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLUniqueBuddyCollector
+//
+// Simple class that collects calling cards that are not null, and not
+// the agent. Duplicates are discarded.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLUniqueBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLUniqueBuddyCollector() {}
+ virtual ~LLUniqueBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+
+protected:
+ std::set<LLUUID> mSeen;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLParticularBuddyCollector
+//
+// Simple class that collects calling cards that match a particular uuid
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLParticularBuddyCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLParticularBuddyCollector(const LLUUID& id) : mBuddyID(id) {}
+ virtual ~LLParticularBuddyCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ LLUUID mBuddyID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLNameCategoryCollector
+//
+// Collects categories based on case-insensitive match of prefix
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLNameCategoryCollector : public LLInventoryCollectFunctor
+{
+public:
+ LLNameCategoryCollector(const char* name) : mName(name) {}
+ virtual ~LLNameCategoryCollector() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+protected:
+ std::string mName;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryCompletionObserver
+//
+// Class which can be used as a base class for doing something when
+// when all observed items are locally complete. This class implements
+// the changed() method of LLInventoryObserver and declares a new
+// method named done() which is called when all watched items have
+// complete information in the inventory model.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryCompletionObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryCompletionObserver() {}
+ virtual void changed(U32 mask);
+
+ void watchItem(const LLUUID& id);
+
+protected:
+ virtual void done() = 0;
+
+ typedef std::vector<LLUUID> item_ref_t;
+ item_ref_t mComplete;
+ item_ref_t mIncomplete;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchObserver
+//
+// This class is much like the LLInventoryCompletionObserver, except
+// that it handles all the the fetching necessary. Override the done()
+// method to do the thing you want.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryFetchObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchObserver() {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> item_ref_t;
+
+ bool isEverythingComplete() const;
+ void fetchItems(const item_ref_t& ids);
+ virtual void done() = 0;
+
+protected:
+ item_ref_t mComplete;
+ item_ref_t mIncomplete;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchDescendentsObserver
+//
+// This class is much like the LLInventoryCompletionObserver, except
+// that it handles fetching based on category. Override the done()
+// method to do the thing you want.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryFetchDescendentsObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchDescendentsObserver() {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> folder_ref_t;
+ void fetchDescendents(const folder_ref_t& ids);
+ bool isEverythingComplete() const;
+ virtual void done() = 0;
+
+protected:
+ bool isComplete(LLViewerInventoryCategory* cat);
+ folder_ref_t mIncompleteFolders;
+ folder_ref_t mCompleteFolders;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryFetchComboObserver
+//
+// This class does an appropriate combination of fetch descendents and
+// item fetches based on completion of categories and items. Much like
+// the fetch and fetch descendents, this will call done() when everything
+// has arrived.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLInventoryFetchComboObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryFetchComboObserver() : mDone(false) {}
+ virtual void changed(U32 mask);
+
+ typedef std::vector<LLUUID> folder_ref_t;
+ typedef std::vector<LLUUID> item_ref_t;
+ void fetch(const folder_ref_t& folder_ids, const item_ref_t& item_ids);
+
+ virtual void done() = 0;
+
+protected:
+ bool mDone;
+ folder_ref_t mCompleteFolders;
+ folder_ref_t mIncompleteFolders;
+ item_ref_t mCompleteItems;
+ item_ref_t mIncompleteItems;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryExistenceObserver
+//
+// This class is used as a base class for doing somethign when all the
+// observed item ids exist in the inventory somewhere. You can derive
+// a class from this class and implement the done() method to do
+// something useful.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryExistenceObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryExistenceObserver() {}
+ virtual void changed(U32 mask);
+
+ void watchItem(const LLUUID& id);
+
+protected:
+ virtual void done() = 0;
+
+ typedef std::vector<LLUUID> item_ref_t;
+ item_ref_t mExist;
+ item_ref_t mMIA;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLInventoryTransactionObserver
+//
+// Class which can be used as a base class for doing something when an
+// inventory transaction completes.
+//
+// *NOTE: This class is not quite complete. Avoid using unless you fix up it's
+// functionality gaps.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLInventoryTransactionObserver : public LLInventoryObserver
+{
+public:
+ LLInventoryTransactionObserver(const LLTransactionID& transaction_id);
+ virtual void changed(U32 mask);
+
+protected:
+ typedef std::vector<LLUUID> folder_ref_t;
+ typedef std::vector<LLUUID> item_ref_t;
+ virtual void done(const folder_ref_t& folders, const item_ref_t& items) = 0;
+
+ LLTransactionID mTransactionID;
+};
+
+
+#endif // LL_LLINVENTORYMODEL_H
diff --git a/indra/newview/lljoystickbutton.cpp b/indra/newview/lljoystickbutton.cpp
new file mode 100644
index 0000000000..a9b5e6e124
--- /dev/null
+++ b/indra/newview/lljoystickbutton.cpp
@@ -0,0 +1,847 @@
+/**
+ * @file lljoystickbutton.cpp
+ * @brief LLJoystick class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lljoystickbutton.h"
+
+// Library includes
+#include "llcoord.h"
+#include "indra_constants.h"
+
+// Project includes
+#include "llui.h"
+#include "llagent.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llmoveview.h"
+
+#include "llglheaders.h"
+
+const F32 NUDGE_TIME = 0.25f; // in seconds
+const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
+
+//
+// Public Methods
+//
+LLJoystick::LLJoystick(
+ const LLString& name,
+ LLRect rect,
+ const LLString &default_image,
+ const LLString &selected_image,
+ EJoystickQuadrant initial_quadrant )
+ :
+ LLButton(name, rect, default_image, selected_image, NULL, NULL),
+ mInitialQuadrant(initial_quadrant),
+ mInitialOffset(0, 0),
+ mLastMouse(0, 0),
+ mFirstMouse(0, 0),
+ mVertSlopNear(0),
+ mVertSlopFar(0),
+ mHorizSlopNear(0),
+ mHorizSlopFar(0),
+ mHeldDown(FALSE),
+ mHeldDownTimer()
+{
+ mHeldDownCallback = &LLJoystick::onHeldDown;
+ mCallbackUserData = this;
+}
+
+
+void LLJoystick::updateSlop()
+{
+ mVertSlopNear = mRect.getHeight();
+ mVertSlopFar = mRect.getHeight() * 2;
+
+ mHorizSlopNear = mRect.getWidth();
+ mHorizSlopFar = mRect.getWidth() * 2;
+
+ // Compute initial mouse offset based on initial quadrant.
+ // Place the mouse evenly between the near and far zones.
+ switch (mInitialQuadrant)
+ {
+ case JQ_ORIGIN:
+ mInitialOffset.set(0, 0);
+ break;
+
+ case JQ_UP:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_DOWN:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_LEFT:
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ case JQ_RIGHT:
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ default:
+ llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
+ break;
+ }
+
+ return;
+}
+
+
+BOOL LLJoystick::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ //llinfos << "joystick mouse down " << x << ", " << y << llendl;
+
+ mLastMouse.set(x, y);
+ mFirstMouse.set(x, y);
+
+ mMouseDownTimer.reset();
+ return LLButton::handleMouseDown(x, y, mask);
+}
+
+
+BOOL LLJoystick::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // llinfos << "joystick mouse up " << x << ", " << y << llendl;
+
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ mLastMouse.set(x, y);
+ mHeldDown = FALSE;
+ onMouseUp();
+ }
+
+ return LLButton::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLJoystick::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gViewerWindow->hasMouseCapture( this ) )
+ {
+ mLastMouse.set(x, y);
+ }
+
+ return LLButton::handleHover(x, y, mask);
+}
+
+F32 LLJoystick::getElapsedHeldDownTime()
+{
+ if( mHeldDown )
+ {
+ return mMouseDownTimer.getElapsedTimeF32();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+// static
+void LLJoystick::onHeldDown(void *userdata)
+{
+ LLJoystick *self = (LLJoystick *)userdata;
+
+ llassert( gViewerWindow->hasMouseCapture( self ) );
+
+ self->mHeldDown = TRUE;
+ self->onHeldDown();
+}
+
+EJoystickQuadrant LLJoystick::selectQuadrant(LLXMLNodePtr node)
+{
+
+ EJoystickQuadrant quadrant = JQ_RIGHT;
+
+ if (node->hasAttribute("quadrant"))
+ {
+ LLString quadrant_name;
+ node->getAttributeString("quadrant", quadrant_name);
+
+ quadrant = quadrantFromName(quadrant_name.c_str());
+ }
+ return quadrant;
+}
+
+
+LLString LLJoystick::nameFromQuadrant(EJoystickQuadrant quadrant)
+{
+ if (quadrant == JQ_ORIGIN) return LLString("origin");
+ else if (quadrant == JQ_UP) return LLString("up");
+ else if (quadrant == JQ_DOWN) return LLString("down");
+ else if (quadrant == JQ_LEFT) return LLString("left");
+ else if (quadrant == JQ_RIGHT) return LLString("right");
+ else return LLString();
+}
+
+
+EJoystickQuadrant LLJoystick::quadrantFromName(const LLString& sQuadrant)
+{
+ EJoystickQuadrant quadrant = JQ_RIGHT;
+
+ if (sQuadrant == "origin")
+ {
+ quadrant = JQ_ORIGIN;
+ }
+ else if (sQuadrant == "up")
+ {
+ quadrant = JQ_UP;
+ }
+ else if (sQuadrant == "down")
+ {
+ quadrant = JQ_DOWN;
+ }
+ else if (sQuadrant == "left")
+ {
+ quadrant = JQ_LEFT;
+ }
+ else if (sQuadrant == "right")
+ {
+ quadrant = JQ_RIGHT;
+ }
+
+ return quadrant;
+}
+
+
+LLXMLNodePtr LLJoystick::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("halign", TRUE)->setStringValue(LLFontGL::nameFromHAlign(mHAlign));
+ node->createChild("quadrant", TRUE)->setStringValue(nameFromQuadrant(mInitialQuadrant));
+
+ addImageAttributeToXML(node,mImageUnselectedName,mImageUnselectedID,"image_unselected");
+ addImageAttributeToXML(node,mImageSelectedName,mImageSelectedID,"image_selected");
+
+ node->createChild("scale_image", TRUE)->setBoolValue(mScaleImage);
+
+ return node;
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickAgentTurn
+//-------------------------------------------------------------------------------
+
+void LLJoystickAgentTurn::onHeldDown()
+{
+ F32 time = getElapsedHeldDownTime();
+ updateSlop();
+
+ //llinfos << "move forward/backward (and/or turn)" << llendl;
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ float m = (float) (dx)/abs(dy);
+
+ if (m > 1) {
+ m = 1;
+ }
+ else if (m < -1) {
+ m = -1;
+ }
+ gAgent.moveYaw(-LLFloaterMove::getYawRate(time)*m);
+
+
+ // handle forward/back movement
+ if (dy > mVertSlopFar)
+ {
+ // ...if mouse is forward of run region run forward
+ gAgent.moveAt(1);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ if( time < NUDGE_TIME )
+ {
+ gAgent.moveAtNudge(1);
+ }
+ else
+ {
+ // ...else if mouse is forward of walk region walk forward
+ // JC 9/5/2002 - Always run / move quickly.
+ gAgent.moveAt(1);
+ }
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // ...else if mouse is behind run region run backward
+ gAgent.moveAt(-1);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ if( time < NUDGE_TIME )
+ {
+ gAgent.moveAtNudge(-1);
+ }
+ else
+ {
+ // ...else if mouse is behind walk region walk backward
+ // JC 9/5/2002 - Always run / move quickly.
+ gAgent.moveAt(-1);
+ }
+ }
+}
+
+LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("button");
+ node->getAttributeString("name", name);
+
+ LLString image_unselected;
+ if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
+
+ LLString image_selected;
+ if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
+
+ EJoystickQuadrant quad = JQ_ORIGIN;
+ if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
+
+ LLJoystickAgentTurn *button = new LLJoystickAgentTurn(name,
+ LLRect(),
+ image_unselected,
+ image_selected,
+ quad);
+
+ if (node->hasAttribute("halign"))
+ {
+ LLFontGL::HAlign halign = selectFontHAlign(node);
+ button->setHAlign(halign);
+ }
+
+ if (node->hasAttribute("scale_image"))
+ {
+ BOOL needsScale = FALSE;
+ node->getAttributeBOOL("scale_image",needsScale);
+ button->setScaleImage( needsScale );
+ }
+
+ button->initFromXML(node, parent);
+
+ return button;
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickAgentSlide
+//-------------------------------------------------------------------------------
+
+void LLJoystickAgentSlide::onMouseUp()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ switch (mInitialQuadrant)
+ {
+ case JQ_LEFT:
+ gAgent.moveLeftNudge(1);
+ break;
+
+ case JQ_RIGHT:
+ gAgent.moveLeftNudge(-1);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void LLJoystickAgentSlide::onHeldDown()
+{
+ //llinfos << "slide left/right (and/or move forward/backward)" << llendl;
+
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ // handle left-right sliding
+ if (dx > mHorizSlopNear)
+ {
+ gAgent.moveLeft(-1);
+ }
+ else if (dx < -mHorizSlopNear)
+ {
+ gAgent.moveLeft(1);
+ }
+
+ // handle forward/back movement
+ if (dy > mVertSlopFar)
+ {
+ // ...if mouse is forward of run region run forward
+ gAgent.moveAt(1);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ // ...else if mouse is forward of walk region walk forward
+ gAgent.moveAtNudge(1);
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // ...else if mouse is behind run region run backward
+ gAgent.moveAt(-1);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ // ...else if mouse is behind walk region walk backward
+ gAgent.moveAtNudge(-1);
+ }
+}
+
+
+// static
+LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("button");
+ node->getAttributeString("name", name);
+
+ LLString image_unselected;
+ if (node->hasAttribute("image_unselected")) node->getAttributeString("image_unselected",image_unselected);
+
+ LLString image_selected;
+ if (node->hasAttribute("image_selected")) node->getAttributeString("image_selected",image_selected);
+
+
+ EJoystickQuadrant quad = JQ_ORIGIN;
+ if (node->hasAttribute("quadrant")) quad = selectQuadrant(node);
+
+ LLJoystickAgentSlide *button = new LLJoystickAgentSlide(name,
+ LLRect(),
+ image_unselected,
+ image_selected,
+ quad);
+
+ if (node->hasAttribute("halign"))
+ {
+ LLFontGL::HAlign halign = selectFontHAlign(node);
+ button->setHAlign(halign);
+ }
+
+ if (node->hasAttribute("scale_image"))
+ {
+ BOOL needsScale = FALSE;
+ node->getAttributeBOOL("scale_image",needsScale);
+ button->setScaleImage( needsScale );
+ }
+
+ button->initFromXML(node, parent);
+
+ return button;
+}
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraRotate
+//-------------------------------------------------------------------------------
+
+LLJoystickCameraRotate::LLJoystickCameraRotate(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img)
+ :
+ LLJoystick(name, rect, out_img, in_img, JQ_ORIGIN),
+ mInLeft( FALSE ),
+ mInTop( FALSE ),
+ mInRight( FALSE ),
+ mInBottom( FALSE )
+{ }
+
+
+void LLJoystickCameraRotate::updateSlop()
+{
+ // do the initial offset calculation based on mousedown location
+
+ // small fixed slop region
+ mVertSlopNear = 16;
+ mVertSlopFar = 32;
+
+ mHorizSlopNear = 16;
+ mHorizSlopFar = 32;
+
+ return;
+}
+
+
+BOOL LLJoystickCameraRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ updateSlop();
+
+ // Set initial offset based on initial click location
+ S32 horiz_center = mRect.getWidth() / 2;
+ S32 vert_center = mRect.getHeight() / 2;
+
+ S32 dx = x - horiz_center;
+ S32 dy = y - vert_center;
+
+ if (dy > dx && dy > -dx)
+ {
+ // top
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ mInitialQuadrant = JQ_UP;
+ }
+ else if (dy > dx && dy <= -dx)
+ {
+ // left
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ mInitialQuadrant = JQ_LEFT;
+ }
+ else if (dy <= dx && dy <= -dx)
+ {
+ // bottom
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ mInitialQuadrant = JQ_DOWN;
+ }
+ else
+ {
+ // right
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ mInitialQuadrant = JQ_RIGHT;
+ }
+
+ return LLJoystick::handleMouseDown(x, y, mask);
+}
+
+
+void LLJoystickCameraRotate::onHeldDown()
+{
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ // left-right rotation
+ if (dx > mHorizSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitLeftKey(getOrbitRate());
+ }
+ else if (dx < -mHorizSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitRightKey(getOrbitRate());
+ }
+
+ // over/under rotation
+ if (dy > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitUpKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setOrbitDownKey(getOrbitRate());
+ }
+}
+
+F32 LLJoystickCameraRotate::getOrbitRate()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+ //llinfos << rate << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+
+// Only used for drawing
+void LLJoystickCameraRotate::setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom )
+{
+ mInLeft = left;
+ mInTop = top;
+ mInRight = right;
+ mInBottom = bottom;
+}
+
+void LLJoystickCameraRotate::draw()
+{
+
+ if( getVisible() )
+ {
+ LLGLSUIDefault gls_ui;
+
+ gl_draw_image( 0, 0, mImageUnselected );
+
+ if( mInTop )
+ {
+ drawRotatedImage( mImageSelected, 0 );
+ }
+
+ if( mInRight )
+ {
+ drawRotatedImage( mImageSelected, 1 );
+ }
+
+ if( mInBottom )
+ {
+ drawRotatedImage( mImageSelected, 2 );
+ }
+
+ if( mInLeft )
+ {
+ drawRotatedImage( mImageSelected, 3 );
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+// Draws image rotated by multiples of 90 degrees
+void LLJoystickCameraRotate::drawRotatedImage( LLImageGL* image, S32 rotations )
+{
+ S32 width = image->getWidth();
+ S32 height = image->getHeight();
+
+ F32 uv[][2] =
+ {
+ { 1.f, 1.f },
+ { 0.f, 1.f },
+ { 0.f, 0.f },
+ { 1.f, 0.f }
+ };
+
+ image->bind();
+
+ glColor4fv(UI_VERTEX_COLOR.mV);
+
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2fv( uv[ (rotations + 0) % 4]);
+ glVertex2i(width, height );
+
+ glTexCoord2fv( uv[ (rotations + 1) % 4]);
+ glVertex2i(0, height );
+
+ glTexCoord2fv( uv[ (rotations + 2) % 4]);
+ glVertex2i(0, 0);
+
+ glTexCoord2fv( uv[ (rotations + 3) % 4]);
+ glVertex2i(width, 0);
+ }
+ glEnd();
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraTrack
+//-------------------------------------------------------------------------------
+
+
+void LLJoystickCameraTrack::onHeldDown()
+{
+ updateSlop();
+
+ S32 dx = mLastMouse.mX - mFirstMouse.mX + mInitialOffset.mX;
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ if (dx > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanRightKey(getOrbitRate());
+ }
+ else if (dx < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanLeftKey(getOrbitRate());
+ }
+
+ // over/under rotation
+ if (dy > mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanUpKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ gAgent.unlockView();
+ gAgent.setPanDownKey(getOrbitRate());
+ }
+}
+
+
+
+//-------------------------------------------------------------------------------
+// LLJoystickCameraZoom
+//-------------------------------------------------------------------------------
+
+LLJoystickCameraZoom::LLJoystickCameraZoom(const LLString& name, LLRect rect, const LLString &out_img, const LLString &plus_in_img, const LLString &minus_in_img)
+ :
+ LLJoystick(name, rect, out_img, "", JQ_ORIGIN),
+ mInTop( FALSE ),
+ mInBottom( FALSE )
+{
+ mPlusInImage = gImageList.getImage(LLUI::findAssetUUIDByName(plus_in_img), MIPMAP_FALSE, TRUE);
+ mMinusInImage = gImageList.getImage(LLUI::findAssetUUIDByName(minus_in_img), MIPMAP_FALSE, TRUE);
+}
+
+
+BOOL LLJoystickCameraZoom::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLJoystick::handleMouseDown(x, y, mask);
+
+ if( handled )
+ {
+ if (mFirstMouse.mY > mRect.getHeight() / 2)
+ {
+ mInitialQuadrant = JQ_UP;
+ }
+ else
+ {
+ mInitialQuadrant = JQ_DOWN;
+ }
+ }
+ return handled;
+}
+
+
+void LLJoystickCameraZoom::onHeldDown()
+{
+ updateSlop();
+
+ const F32 FAST_RATE = 2.5f; // two and a half times the normal rate
+
+ S32 dy = mLastMouse.mY - mFirstMouse.mY + mInitialOffset.mY;
+
+ if (dy > mVertSlopFar)
+ {
+ // Zoom in fast
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(FAST_RATE);
+ }
+ else if (dy > mVertSlopNear)
+ {
+ // Zoom in slow
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(getOrbitRate());
+ }
+ else if (dy < -mVertSlopFar)
+ {
+ // Zoom out fast
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(FAST_RATE);
+ }
+ else if (dy < -mVertSlopNear)
+ {
+ // Zoom out slow
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(getOrbitRate());
+ }
+}
+
+// Only used for drawing
+void LLJoystickCameraZoom::setToggleState( BOOL top, BOOL bottom )
+{
+ mInTop = top;
+ mInBottom = bottom;
+}
+
+void LLJoystickCameraZoom::draw()
+{
+ if( getVisible() )
+ {
+ if( mInTop )
+ {
+ gl_draw_image( 0, 0, mPlusInImage );
+ }
+ else
+ if( mInBottom )
+ {
+ gl_draw_image( 0, 0, mMinusInImage );
+ }
+ else
+ {
+ gl_draw_image( 0, 0, mImageUnselected );
+ }
+
+ if (sDebugRects)
+ {
+ drawDebugRect();
+ }
+ }
+}
+
+void LLJoystickCameraZoom::updateSlop()
+{
+ mVertSlopNear = mRect.getHeight() / 4;
+ mVertSlopFar = mRect.getHeight() / 2;
+
+ mHorizSlopNear = mRect.getWidth() / 4;
+ mHorizSlopFar = mRect.getWidth() / 2;
+
+ // Compute initial mouse offset based on initial quadrant.
+ // Place the mouse evenly between the near and far zones.
+ switch (mInitialQuadrant)
+ {
+ case JQ_ORIGIN:
+ mInitialOffset.set(0, 0);
+ break;
+
+ case JQ_UP:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_DOWN:
+ mInitialOffset.mX = 0;
+ mInitialOffset.mY = - (mVertSlopNear + mVertSlopFar) / 2;
+ break;
+
+ case JQ_LEFT:
+ mInitialOffset.mX = - (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ case JQ_RIGHT:
+ mInitialOffset.mX = (mHorizSlopNear + mHorizSlopFar) / 2;
+ mInitialOffset.mY = 0;
+ break;
+
+ default:
+ llerrs << "LLJoystick::LLJoystick() - bad switch case" << llendl;
+ break;
+ }
+
+ return;
+}
+
+
+F32 LLJoystickCameraZoom::getOrbitRate()
+{
+ F32 time = getElapsedHeldDownTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+// llinfos << "rate " << rate << " time " << time << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
diff --git a/indra/newview/lljoystickbutton.h b/indra/newview/lljoystickbutton.h
new file mode 100644
index 0000000000..a9a8acdfaf
--- /dev/null
+++ b/indra/newview/lljoystickbutton.h
@@ -0,0 +1,168 @@
+/**
+ * @file lljoystickbutton.h
+ * @brief LLJoystick class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLJOYSTICKBUTTON_H
+#define LL_LLJOYSTICKBUTTON_H
+
+#include "llbutton.h"
+#include "llcoord.h"
+
+typedef enum e_joystick_quadrant
+{
+ JQ_ORIGIN,
+ JQ_UP,
+ JQ_DOWN,
+ JQ_LEFT,
+ JQ_RIGHT
+} EJoystickQuadrant;
+
+class LLJoystick
+: public LLButton
+{
+public:
+ LLJoystick(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void onMouseUp() {}
+ virtual void onHeldDown() = 0;
+ F32 getElapsedHeldDownTime();
+
+ static void onHeldDown(void *userdata); // called by llbutton callback handler
+ void setInitialQuadrant(EJoystickQuadrant initial) { mInitialQuadrant = initial; };
+
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLString nameFromQuadrant(const EJoystickQuadrant quadrant);
+ static EJoystickQuadrant quadrantFromName(const LLString& name);
+ static EJoystickQuadrant selectQuadrant(LLXMLNodePtr node);
+
+
+protected:
+ virtual void updateSlop(); // recompute slop margins
+
+protected:
+ EJoystickQuadrant mInitialQuadrant; // mousedown = click in this quadrant
+ LLCoordGL mInitialOffset; // pretend mouse started here
+ LLCoordGL mLastMouse; // where was mouse on last hover event
+ LLCoordGL mFirstMouse; // when mouse clicked, where was it
+ S32 mVertSlopNear; // where the slop regions end
+ S32 mVertSlopFar; // where the slop regions end
+ S32 mHorizSlopNear; // where the slop regions end
+ S32 mHorizSlopFar; // where the slop regions end
+ BOOL mHeldDown;
+ LLFrameTimer mHeldDownTimer;
+};
+
+
+// Turn agent left and right, move forward and back
+class LLJoystickAgentTurn
+: public LLJoystick
+{
+public:
+ LLJoystickAgentTurn(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial)
+ : LLJoystick(name, rect, default_image, selected_image, initial)
+ { }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_JOYSTICK_TURN; }
+ virtual LLString getWidgetTag() const { return LL_JOYSTICK_TURN; }
+
+ virtual void onHeldDown();
+
+ static LLView* LLJoystickAgentTurn::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+};
+
+
+// Slide left and right, move forward and back
+class LLJoystickAgentSlide
+: public LLJoystick
+{
+public:
+ LLJoystickAgentSlide(const LLString& name, LLRect rect, const LLString &default_image, const LLString &selected_image, EJoystickQuadrant initial)
+ : LLJoystick(name, rect, default_image, selected_image, initial)
+ { }
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_JOYSTICK_SLIDE; }
+ virtual LLString getWidgetTag() const { return LL_JOYSTICK_SLIDE; }
+
+
+ virtual void onHeldDown();
+ virtual void onMouseUp();
+
+ static LLView* LLJoystickAgentSlide::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+};
+
+
+// Rotate camera around the focus point
+class LLJoystickCameraRotate
+: public LLJoystick
+{
+public:
+ LLJoystickCameraRotate(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img);
+
+ virtual void setToggleState( BOOL left, BOOL top, BOOL right, BOOL bottom );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual void onHeldDown();
+ virtual void draw();
+
+protected:
+ F32 getOrbitRate();
+ virtual void updateSlop();
+ void drawRotatedImage( LLImageGL* image, S32 rotations );
+
+protected:
+ BOOL mInLeft;
+ BOOL mInTop;
+ BOOL mInRight;
+ BOOL mInBottom;
+};
+
+
+// Track the camera focus point forward/backward and side to side
+class LLJoystickCameraTrack
+: public LLJoystickCameraRotate
+{
+public:
+ LLJoystickCameraTrack(const LLString& name, LLRect rect, const LLString &out_img, const LLString &in_img)
+ : LLJoystickCameraRotate(name, rect, out_img, in_img)
+ { }
+
+ virtual void onHeldDown();
+};
+
+
+// Zoom the camera in and out
+class LLJoystickCameraZoom
+: public LLJoystick
+{
+public:
+ LLJoystickCameraZoom(const LLString& name, LLRect rect, const LLString &out_img, const LLString &plus_in_img, const LLString &minus_in_img);
+
+ virtual void setToggleState( BOOL top, BOOL bottom );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual void onHeldDown();
+ virtual void draw();
+
+protected:
+ virtual void updateSlop();
+ F32 getOrbitRate();
+
+protected:
+ BOOL mInTop;
+ BOOL mInBottom;
+ LLPointer<LLViewerImage> mPlusInImage;
+ LLPointer<LLViewerImage> mMinusInImage;
+};
+
+
+
+#endif // LL_LLJOYSTICKBUTTON_H
diff --git a/indra/newview/lllandmarklist.cpp b/indra/newview/lllandmarklist.cpp
new file mode 100644
index 0000000000..0fb32c2dc3
--- /dev/null
+++ b/indra/newview/lllandmarklist.cpp
@@ -0,0 +1,116 @@
+/**
+ * @file lllandmarklist.cpp
+ * @brief Landmark asset list class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllandmarklist.h"
+
+#include "message.h"
+#include "llassetstorage.h"
+
+#include "llagent.h"
+#include "llnotify.h"
+#include "llvfile.h"
+#include "llviewerstats.h"
+
+// Globals
+LLLandmarkList gLandmarkList;
+
+
+////////////////////////////////////////////////////////////////////////////
+// LLLandmarkList
+
+LLLandmarkList::~LLLandmarkList()
+{
+ std::for_each(mList.begin(), mList.end(), DeletePairedPointer());
+}
+
+LLLandmark* LLLandmarkList::getAsset( const LLUUID& asset_uuid )
+{
+ LLLandmark* landmark = get_ptr_in_map(mList, asset_uuid);
+ if(landmark)
+ {
+ return landmark;
+ }
+ else
+ {
+ if ( gLandmarkList.mBadList.find(asset_uuid) == gLandmarkList.mBadList.end() )
+ {
+ gAssetStorage->getAssetData(
+ asset_uuid,
+ LLAssetType::AT_LANDMARK,
+ LLLandmarkList::processGetAssetReply,
+ NULL);
+ }
+ }
+ return NULL;
+}
+
+// static
+void LLLandmarkList::processGetAssetReply(
+ LLVFS *vfs,
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status)
+{
+ if( status == 0 )
+ {
+ LLVFile file(vfs, uuid, type);
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[ file_length + 1 ];
+ file.read( (U8*)buffer, file_length);
+ buffer[ file_length ] = 0;
+
+ LLLandmark* landmark = LLLandmark::constructFromString(buffer);
+ if (landmark)
+ {
+ LLVector3d pos;
+ if(!landmark->getGlobalPos(pos))
+ {
+ LLUUID region_id;
+ if(landmark->getRegionID(region_id))
+ {
+ LLLandmark::requestRegionHandle(
+ gMessageSystem,
+ gAgent.getRegionHost(),
+ region_id,
+ NULL);
+ }
+ }
+ gLandmarkList.mList[ uuid ] = landmark;
+ }
+
+ delete[] buffer;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status )
+ {
+ LLNotifyBox::showXml("LandmarkMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadLandmark");
+ }
+
+ gLandmarkList.mBadList.insert(uuid);
+ }
+
+}
+
+BOOL LLLandmarkList::assetExists(const LLUUID& asset_uuid)
+{
+ return mList.count(asset_uuid) != 0 || mBadList.count(asset_uuid) != 0;
+}
diff --git a/indra/newview/lllandmarklist.h b/indra/newview/lllandmarklist.h
new file mode 100644
index 0000000000..15e900b601
--- /dev/null
+++ b/indra/newview/lllandmarklist.h
@@ -0,0 +1,51 @@
+/**
+ * @file lllandmarklist.h
+ * @brief Landmark asset list class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLANDMARKLIST_H
+#define LL_LLLANDMARKLIST_H
+
+#include <map>
+#include "lllandmark.h"
+#include "lluuid.h"
+#include "llassetstorage.h"
+
+class LLMessageSystem;
+class LLLineEditor;
+class LLInventoryItem;
+
+class LLLandmarkList
+{
+public:
+ LLLandmarkList() {}
+ ~LLLandmarkList();
+
+ //S32 getLength() { return mList.getLength(); }
+ //const LLLandmark* getFirst() { return mList.getFirstData(); }
+ //const LLLandmark* getNext() { return mList.getNextData(); }
+
+ BOOL assetExists(const LLUUID& asset_uuid);
+ LLLandmark* getAsset(const LLUUID& asset_uuid);
+ static void processGetAssetReply(
+ LLVFS *vfs,
+ const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data,
+ S32 status);
+
+protected:
+ typedef std::map<LLUUID, LLLandmark*> landmark_list_t;
+ landmark_list_t mList;
+
+ typedef std::set<LLUUID> landmark_bad_list_t;
+ landmark_bad_list_t mBadList;
+};
+
+
+extern LLLandmarkList gLandmarkList;
+
+#endif // LL_LLLANDMARKLIST_H
diff --git a/indra/newview/lllightconstants.h b/indra/newview/lllightconstants.h
new file mode 100644
index 0000000000..01dbc7d1cc
--- /dev/null
+++ b/indra/newview/lllightconstants.h
@@ -0,0 +1,15 @@
+/**
+ * @file lllightconstants.h
+ * @brief Constants for light functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLLIGHTCONSTANTS_H
+#define LL_LLLIGHTCONSTANTS_H
+
+//const F32 LIGHT_MAX_RADIUS = 20.f; // Maximum range of light
+const F32 LIGHT_FADE_TIME = 0.2f;
+
+#endif // LL_LLLIGHTCONSTANTS_H
diff --git a/indra/newview/lllogchat.cpp b/indra/newview/lllogchat.cpp
new file mode 100644
index 0000000000..32f0032b0b
--- /dev/null
+++ b/indra/newview/lllogchat.cpp
@@ -0,0 +1,82 @@
+/**
+ * @file lllogchat.cpp
+ * @brief LLLogChat class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lllogchat.h"
+
+const S32 LOG_RECALL_SIZE = 2048;
+
+//static
+LLString LLLogChat::makeLogFileName(LLString filename)
+{
+
+ filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_ACCOUNT_CHAT_LOGS,filename.c_str());
+ filename += ".txt";
+ return filename;
+}
+
+//static
+void LLLogChat::saveHistory(LLString filename, LLString line)
+{
+ FILE *fp = LLFile::fopen(LLLogChat::makeLogFileName(filename).c_str(), "a");
+ if (!fp)
+ {
+ llinfos << "Couldn't open chat history log!" << llendl;
+ }
+ else
+ {
+ fprintf(fp, "%s\n", line.c_str());
+
+ fclose (fp);
+ }
+}
+
+void LLLogChat::loadHistory(LLString filename , void (*callback)(LLString,void*), void* userdata)
+{
+ FILE *fptr = LLFile::fopen(makeLogFileName(filename).c_str(), "r");
+ if (!fptr)
+ {
+ return; //No previous conversation with this name.
+ }
+ else
+ {
+ char buffer[LOG_RECALL_SIZE];
+ char *bptr;
+ S32 len;
+ bool firstline=TRUE;
+
+ if ( fseek(fptr, (LOG_RECALL_SIZE - 1) * -1 , SEEK_END) )
+ { //File is smaller than recall size. Get it all.
+ firstline = FALSE;
+ if ( fseek(fptr, 0, SEEK_SET) )
+ {
+ fclose(fptr);
+ return;
+ }
+ }
+
+ while ( fgets(buffer, LOG_RECALL_SIZE, fptr) && !feof(fptr) )
+ {
+ len = strlen(buffer) - 1;
+ for ( bptr = (buffer + len); (*bptr == '\n' || *bptr == '\r') && bptr>buffer; bptr--) *bptr='\0';
+
+ if (!firstline)
+ {
+ callback(buffer,userdata);
+ }
+ else
+ {
+ firstline = FALSE;
+ }
+ }
+ callback("-- End of Log ---",userdata);
+
+ fclose(fptr);
+ }
+}
diff --git a/indra/newview/lllogchat.h b/indra/newview/lllogchat.h
new file mode 100644
index 0000000000..ac2d710b64
--- /dev/null
+++ b/indra/newview/lllogchat.h
@@ -0,0 +1,21 @@
+/**
+ * @file lllogchat.h
+ * @brief LLFloaterChat class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLLOGCHAT_H
+#define LL_LLLOGCHAT_H
+
+class LLLogChat
+{
+public:
+ static LLString makeLogFileName(LLString(filename));
+ static void saveHistory(LLString filename, LLString line);
+ static void loadHistory(LLString filename, void (*callback)(LLString,void*),void* userdata);
+};
+
+#endif
diff --git a/indra/newview/llmanip.cpp b/indra/newview/llmanip.cpp
new file mode 100644
index 0000000000..74c7ae6c18
--- /dev/null
+++ b/indra/newview/llmanip.cpp
@@ -0,0 +1,581 @@
+/**
+ * @file llmanip.cpp
+ * @brief LLManip class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmanip.h"
+
+#include "llmath.h"
+#include "v3math.h"
+//#include "llquaternion.h"
+#include "llgl.h"
+#include "llprimitive.h"
+#include "llview.h"
+#include "llviewerimagelist.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llfontgl.h"
+#include "llhudrender.h"
+#include "llselectmgr.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerjoint.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h" // for gWorldPointer
+#include "llresmgr.h"
+#include "viewer.h" // for gFPS
+#include "pipeline.h"
+#include "llglheaders.h"
+
+// Local constants...
+const S32 VERTICAL_OFFSET = 50;
+
+F32 LLManip::sHelpTextVisibleTime = 2.f;
+F32 LLManip::sHelpTextFadeTime = 2.f;
+S32 LLManip::sNumTimesHelpTextShown = 0;
+S32 LLManip::sMaxTimesShowHelpText = 5;
+F32 LLManip::sGridMaxSubdivisionLevel = 32.f;
+F32 LLManip::sGridMinSubdivisionLevel = 1.f;
+LLVector2 LLManip::sTickLabelSpacing(60.f, 25.f);
+
+
+//static
+void LLManip::rebuild(LLViewerObject* vobj)
+{
+ LLDrawable* drawablep = vobj->mDrawable;
+ if (drawablep && drawablep->getVOVolume())
+ {
+
+ gPipeline.markRebuild(drawablep,LLDrawable::REBUILD_VOLUME, TRUE);
+ //gPipeline.markMoved(drawablep, FALSE);
+ //gPipeline.updateMoveNormalAsync(vobj->mDrawable);
+
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED
+ drawablep->updateMove();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// LLManip
+
+
+LLManip::LLManip( const LLString& name, LLToolComposite* composite )
+ :
+ LLTool( name, composite ),
+ mInSnapRegime(FALSE)
+{}
+
+
+void LLManip::getManipNormal(LLViewerObject* object, EManipPart manip, LLVector3 &normal)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (manip >= LL_X_ARROW && manip <= LL_Z_ARROW)
+ {
+ LLVector3 arrow_axis;
+ getManipAxis(object, manip, arrow_axis);
+
+ LLVector3 cross = arrow_axis % gCamera->getAtAxis();
+ normal = cross % arrow_axis;
+ normal.normVec();
+ }
+ else if (manip >= LL_YZ_PLANE && manip <= LL_XY_PLANE)
+ {
+ switch (manip)
+ {
+ case LL_YZ_PLANE:
+ normal = LLVector3::x_axis;
+ break;
+ case LL_XZ_PLANE:
+ normal = LLVector3::y_axis;
+ break;
+ case LL_XY_PLANE:
+ normal = LLVector3::z_axis;
+ break;
+ default:
+ break;
+ }
+ normal.rotVec(grid_rotation);
+ }
+ else
+ {
+ normal.clearVec();
+ }
+}
+
+
+BOOL LLManip::getManipAxis(LLViewerObject* object, EManipPart manip, LLVector3 &axis)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (manip == LL_X_ARROW)
+ {
+ axis = LLVector3::x_axis;
+ }
+ else if (manip == LL_Y_ARROW)
+ {
+ axis = LLVector3::y_axis;
+ }
+ else if (manip == LL_Z_ARROW)
+ {
+ axis = LLVector3::z_axis;
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ axis.rotVec( grid_rotation );
+ return TRUE;
+}
+
+F32 LLManip::getSubdivisionLevel(const LLVector3 &reference_point, const LLVector3 &translate_axis, F32 grid_scale, S32 min_pixel_spacing)
+{
+ //update current snap subdivision level
+ LLVector3 cam_to_reference;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_reference = LLVector3(1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_reference = reference_point - gCamera->getOrigin();
+ }
+ F32 current_range = cam_to_reference.normVec();
+
+ F32 projected_translation_axis_length = (translate_axis % cam_to_reference).magVec();
+ F32 subdivisions = llmax(projected_translation_axis_length * grid_scale / (current_range / gCamera->getPixelMeterRatio() * min_pixel_spacing), 0.f);
+ subdivisions = llclamp((F32)pow(2.f, llfloor(log(subdivisions) / log(2.f))), 1.f / 32.f, 32.f);
+
+ return subdivisions;
+}
+
+BOOL LLManip::handleHover(S32 x, S32 y, MASK mask)
+{
+ // We only handle the event if mousedown started with us
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ // Release the mouse
+ setMouseCapture( FALSE );
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManip (active)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManip (inactive)" << llendl;
+ }
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+
+BOOL LLManip::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ if( hasMouseCapture() )
+ {
+ handled = TRUE;
+ setMouseCapture( FALSE );
+ }
+ return handled;
+}
+
+void LLManip::updateGridSettings()
+{
+ sGridMaxSubdivisionLevel = gSavedSettings.getBOOL("GridSubUnit") ? (F32)gSavedSettings.getS32("GridSubdivision") : 1.f;
+}
+
+BOOL LLManip::getMousePointOnPlaneAgent(LLVector3& point, S32 x, S32 y, LLVector3 origin, LLVector3 normal)
+{
+ LLVector3d origin_double = gAgent.getPosGlobalFromAgent(origin);
+ LLVector3d global_point;
+ BOOL result = getMousePointOnPlaneGlobal(global_point, x, y, origin_double, normal);
+ point = gAgent.getPosAgentFromGlobal(global_point);
+ return result;
+}
+
+BOOL LLManip::getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVector3d origin, LLVector3 normal)
+{
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ BOOL result = FALSE;
+ F32 mouse_x = ((F32)x / gViewerWindow->getWindowWidth() - 0.5f) * gCamera->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = ((F32)y / gViewerWindow->getWindowHeight() - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ LLVector3 origin_agent = gAgent.getPosAgentFromGlobal(origin);
+ LLVector3 mouse_pos = LLVector3(0.f, -mouse_x, mouse_y);
+ if (llabs(normal.mV[VX]) < 0.001f)
+ {
+ // use largish value that should be outside HUD manipulation range
+ mouse_pos.mV[VX] = 10.f;
+ }
+ else
+ {
+ mouse_pos.mV[VX] = (normal * (origin_agent - mouse_pos))
+ / (normal.mV[VX]);
+ result = TRUE;
+ }
+
+ point = gAgent.getPosGlobalFromAgent(mouse_pos);
+ return result;
+ }
+ else
+ {
+ return gViewerWindow->mousePointOnPlaneGlobal(
+ point, x, y, origin, normal );
+ }
+
+ //return FALSE;
+}
+
+// Given the line defined by mouse cursor (a1 + a_param*(a2-a1)) and the line defined by b1 + b_param*(b2-b1),
+// returns a_param and b_param for the points where lines are closest to each other.
+// Returns false if the two lines are parallel.
+BOOL LLManip::nearestPointOnLineFromMouse( S32 x, S32 y, const LLVector3& b1, const LLVector3& b2, F32 &a_param, F32 &b_param ) const
+{
+ LLVector3 a1;
+ LLVector3 a2;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) * gCamera->getAspect() / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = (((F32)y / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+ a1 = LLVector3(llmin(b1.mV[VX] - 0.1f, b2.mV[VX] - 0.1f, 0.f), -mouse_x, mouse_y);
+ a2 = a1 + LLVector3(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ a1 = gAgent.getCameraPositionAgent();
+ a2 = gAgent.getCameraPositionAgent() + LLVector3(gViewerWindow->mouseDirectionGlobal(x, y));
+ }
+
+ BOOL parallel = TRUE;
+ LLVector3 a = a2 - a1;
+ LLVector3 b = b2 - b1;
+
+ LLVector3 normal;
+ F32 dist, denom;
+ normal = (b % a) % b; // normal to plane (P) through b and (shortest line between a and b)
+ normal.normVec();
+ dist = b1 * normal; // distance from origin to P
+
+ denom = normal * a;
+ if( (denom < -F_APPROXIMATELY_ZERO) || (F_APPROXIMATELY_ZERO < denom) )
+ {
+ a_param = (dist - normal * a1) / denom;
+ parallel = FALSE;
+ }
+
+ normal = (a % b) % a; // normal to plane (P) through a and (shortest line between a and b)
+ normal.normVec();
+ dist = a1 * normal; // distance from origin to P
+ denom = normal * b;
+ if( (denom < -F_APPROXIMATELY_ZERO) || (F_APPROXIMATELY_ZERO < denom) )
+ {
+ b_param = (dist - normal * b1) / denom;
+ parallel = FALSE;
+ }
+
+ return parallel;
+}
+
+LLVector3 LLManip::getSavedPivotPoint() const
+{
+ return gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+}
+
+LLVector3 LLManip::getPivotPoint() const
+{
+ if (gSelectMgr->getFirstObject() && gSelectMgr->getObjectCount() == 1 && gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ return gSelectMgr->getFirstObject()->getPivotPositionAgent();
+ }
+ return gSelectMgr->getBBoxOfSelection().getCenterAgent();
+}
+
+
+void LLManip::renderGuidelines(BOOL draw_x, BOOL draw_y, BOOL draw_z)
+{
+ LLVector3 grid_origin;
+ LLQuaternion grid_rot;
+ LLVector3 grid_scale;
+ gSelectMgr->getGrid(grid_origin, grid_rot, grid_scale);
+
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if (!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ if (!object)
+ {
+ return;
+ }
+ }
+
+ //LLVector3 center_agent = gSelectMgr->getBBoxOfSelection().getCenterAgent();
+ LLVector3 center_agent = getPivotPoint();
+
+ glPushMatrix();
+ {
+ glTranslatef(center_agent.mV[VX], center_agent.mV[VY], center_agent.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+
+ grid_rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ F32 region_size = gWorldPointer->getRegionWidthInMeters();
+
+ const F32 LINE_ALPHA = 0.33f;
+
+ LLGLSNoTexture gls_no_texture;
+ LLUI::setLineWidth(1.5f);
+
+ if (draw_x)
+ {
+ glColor4f(1.f, 0.f, 0.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( -region_size, 0.f, 0.f );
+ glVertex3f( region_size, 0.f, 0.f );
+ glEnd();
+ }
+
+ if (draw_y)
+ {
+ glColor4f(0.f, 1.f, 0.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( 0.f, -region_size, 0.f );
+ glVertex3f( 0.f, region_size, 0.f );
+ glEnd();
+ }
+
+ if (draw_z)
+ {
+ glColor4f(0.f, 0.f, 1.f, LINE_ALPHA);
+ glBegin(GL_LINES);
+ glVertex3f( 0.f, 0.f, -region_size );
+ glVertex3f( 0.f, 0.f, region_size );
+ glEnd();
+ }
+ LLUI::setLineWidth(1.0f);
+ }
+ glPopMatrix();
+}
+
+void LLManip::renderXYZ(const LLVector3 &vec)
+{
+ const S32 PAD = 10;
+ char feedback_string[128];
+ LLVector3 camera_pos = gCamera->getOrigin() + gCamera->getAtAxis();
+ S32 vertical_offset = gViewerWindow->getWindowHeight() / 2 - VERTICAL_OFFSET;
+ S32 window_center_x = gViewerWindow->getWindowWidth() / 2;
+ S32 window_center_y = gViewerWindow->getWindowHeight() / 2;
+
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ glPushMatrix();
+ {
+ gViewerWindow->setup2DRender();
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+ glColor4f(0.f, 0.f, 0.f, 0.7f);
+
+ gl_draw_scaled_image_with_border(window_center_x - 115,
+ window_center_y + vertical_offset - PAD,
+ 16,
+ 16,
+ 235,
+ PAD * 2 + 10,
+ imagep,
+ LLColor4(0.f, 0.f, 0.f, 0.7f) );
+ }
+ glPopMatrix();
+
+ gViewerWindow->setup3DRender();
+
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ LLGLEnable tex(GL_TEXTURE_2D);
+ // render drop shadowed text
+ sprintf(feedback_string, "X: %.3f", vec.mV[VX]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -102.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ sprintf(feedback_string, "Y: %.3f", vec.mV[VY]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -27.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ sprintf(feedback_string, "Z: %.3f", vec.mV[VZ]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, 48.f + 1.f, (F32)vertical_offset - 1.f, LLColor4::black, FALSE);
+
+ // render text on top
+ sprintf(feedback_string, "X: %.3f", vec.mV[VX]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -102.f, (F32)vertical_offset, LLColor4(1.f, 0.5f, 0.5f, 1.f), FALSE);
+
+ glColor3f(0.5f, 1.f, 0.5f);
+ sprintf(feedback_string, "Y: %.3f", vec.mV[VY]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, -27.f, (F32)vertical_offset, LLColor4(0.5f, 1.f, 0.5f, 1.f), FALSE);
+
+ glColor3f(0.5f, 0.5f, 1.f);
+ sprintf(feedback_string, "Z: %.3f", vec.mV[VZ]);
+ hud_render_text(utf8str_to_wstring(feedback_string), camera_pos, *gResMgr->getRes( LLFONT_SANSSERIF ), LLFontGL::NORMAL, 48.f, (F32)vertical_offset, LLColor4(0.5f, 0.5f, 1.f, 1.f), FALSE);
+ }
+}
+
+void LLManip::renderTickText(const LLVector3& pos, const char* text, const LLColor4 &color)
+{
+ const LLFontGL* big_fontp = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ BOOL hud_selection = gSelectMgr->getSelectType() == SELECT_TYPE_HUD;
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ LLVector3 render_pos = pos;
+ if (hud_selection)
+ {
+ F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 inv_zoom_amt = 1.f / zoom_amt;
+ // scale text back up to counter-act zoom level
+ render_pos = pos * zoom_amt;
+ glScalef(inv_zoom_amt, inv_zoom_amt, inv_zoom_amt);
+ }
+
+ // render shadow first
+ LLColor4 shadow_color = LLColor4::black;
+ LLGLEnable tex(GL_TEXTURE_2D);
+ shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f;
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(text, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(text), 3.f, shadow_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(text, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(text), 3.f, color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+
+ glPopMatrix();
+}
+
+void LLManip::renderTickValue(const LLVector3& pos, F32 value, const char* suffix, const LLColor4 &color)
+{
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ const LLFontGL* big_fontp = gResMgr->getRes( LLFONT_SANSSERIF );
+ const LLFontGL* small_fontp = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+ char val_string[128];
+ char fraction_string[128];
+ F32 val_to_print = llround(value, 0.001f);
+ S32 fractional_portion = llround(fmodf(llabs(val_to_print), 1.f) * 100.f);
+ if (val_to_print < 0.f)
+ {
+ if (fractional_portion == 0)
+ {
+ sprintf(val_string, "-%d%s", lltrunc(llabs(val_to_print)), suffix);
+ }
+ else
+ {
+ sprintf(val_string, "-%d", lltrunc(llabs(val_to_print)));
+ }
+ }
+ else
+ {
+ if (fractional_portion == 0)
+ {
+ sprintf(val_string, "%d%s", lltrunc(llabs(val_to_print)), suffix);
+ }
+ else
+ {
+ sprintf(val_string, "%d", lltrunc(val_to_print));
+ }
+ }
+
+ BOOL hud_selection = gSelectMgr->getSelectType() == SELECT_TYPE_HUD;
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ LLVector3 render_pos = pos;
+ if (hud_selection)
+ {
+ F32 zoom_amt = gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 inv_zoom_amt = 1.f / zoom_amt;
+ // scale text back up to counter-act zoom level
+ render_pos = pos * zoom_amt;
+ glScalef(inv_zoom_amt, inv_zoom_amt, inv_zoom_amt);
+ }
+
+ LLColor4 shadow_color = LLColor4::black;
+ shadow_color.mV[VALPHA] = color.mV[VALPHA] * 0.5f;
+
+ LLGLEnable tex(GL_TEXTURE_2D);
+ if (fractional_portion != 0)
+ {
+ sprintf(fraction_string, "%c%d%s", gResMgr->getDecimalPoint(), fractional_portion, suffix);
+
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -1.f * big_fontp->getWidthF32(val_string), 3.f, shadow_color, hud_selection);
+ hud_render_utf8text(fraction_string, render_pos, *small_fontp, LLFontGL::NORMAL, 1.f, 3.f, shadow_color, hud_selection);
+
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -1.f * big_fontp->getWidthF32(val_string), 3.f, color, hud_selection);
+ hud_render_utf8text(fraction_string, render_pos, *small_fontp, LLFontGL::NORMAL, 1.f, 3.f, color, hud_selection);
+ }
+ else
+ {
+ gViewerWindow->setupViewport(1, -1);
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(val_string), 3.f, shadow_color, hud_selection);
+ gViewerWindow->setupViewport();
+ hud_render_utf8text(val_string, render_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(val_string), 3.f, color, hud_selection);
+ }
+ glPopMatrix();
+}
+
+LLColor4 LLManip::setupSnapGuideRenderPass(S32 pass)
+{
+ static LLColor4 grid_color_fg = gColors.getColor("GridlineColor");
+ static LLColor4 grid_color_bg = gColors.getColor("GridlineBGColor");
+ static LLColor4 grid_color_shadow = gColors.getColor("GridlineShadowColor");
+
+ LLColor4 line_color;
+ F32 line_alpha = gSavedSettings.getF32("GridOpacity");
+
+ switch(pass)
+ {
+ case 0:
+ // shadow
+ gViewerWindow->setupViewport(1, -1);
+ line_color = grid_color_shadow;
+ line_color.mV[VALPHA] *= line_alpha;
+ LLUI::setLineWidth(2.f);
+ break;
+ case 1:
+ // hidden lines
+ gViewerWindow->setupViewport();
+ line_color = grid_color_bg;
+ line_color.mV[VALPHA] *= line_alpha;
+ LLUI::setLineWidth(1.f);
+ break;
+ case 2:
+ // visible lines
+ line_color = grid_color_fg;
+ line_color.mV[VALPHA] *= line_alpha;
+ break;
+ }
+
+ return line_color;
+}
diff --git a/indra/newview/llmanip.h b/indra/newview/llmanip.h
new file mode 100644
index 0000000000..b1c2474166
--- /dev/null
+++ b/indra/newview/llmanip.h
@@ -0,0 +1,137 @@
+/**
+ * @file llmanip.h
+ * @brief LLManip class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MANIP_H
+#define LL_MANIP_H
+
+#include "lltool.h"
+//#include "v3math.h"
+
+class LLView;
+class LLTextBox;
+class LLViewerObject;
+class LLToolComposite;
+class LLVector3;
+
+const S32 MIN_DIVISION_PIXEL_WIDTH = 9;
+
+class LLManip : public LLTool
+{
+public:
+ typedef enum e_manip_part
+ {
+ LL_NO_PART = 0,
+
+ // Translation
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_Z_ARROW,
+
+ LL_YZ_PLANE,
+ LL_XZ_PLANE,
+ LL_XY_PLANE,
+
+ // Scale
+ LL_CORNER_NNN,
+ LL_CORNER_NNP,
+ LL_CORNER_NPN,
+ LL_CORNER_NPP,
+ LL_CORNER_PNN,
+ LL_CORNER_PNP,
+ LL_CORNER_PPN,
+ LL_CORNER_PPP,
+
+ // Faces
+ LL_FACE_POSZ,
+ LL_FACE_POSX,
+ LL_FACE_POSY,
+ LL_FACE_NEGX,
+ LL_FACE_NEGY,
+ LL_FACE_NEGZ,
+
+ // Edges
+ LL_EDGE_NEGX_NEGY,
+ LL_EDGE_NEGX_POSY,
+ LL_EDGE_POSX_NEGY,
+ LL_EDGE_POSX_POSY,
+
+ LL_EDGE_NEGY_NEGZ,
+ LL_EDGE_NEGY_POSZ,
+ LL_EDGE_POSY_NEGZ,
+ LL_EDGE_POSY_POSZ,
+
+ LL_EDGE_NEGZ_NEGX,
+ LL_EDGE_NEGZ_POSX,
+ LL_EDGE_POSZ_NEGX,
+ LL_EDGE_POSZ_POSX,
+
+ // Rotation Manip
+ LL_ROT_GENERAL,
+ LL_ROT_X,
+ LL_ROT_Y,
+ LL_ROT_Z,
+ LL_ROT_ROLL
+ } EManipPart;
+
+ // For use in loops and range checking.
+ typedef enum e_select_part_ranges
+ {
+ LL_ARROW_MIN = LL_X_ARROW,
+ LL_ARROW_MAX = LL_Z_ARROW,
+
+ LL_CORNER_MIN = LL_CORNER_NNN,
+ LL_CORNER_MAX = LL_CORNER_PPP,
+
+ LL_FACE_MIN = LL_FACE_POSZ,
+ LL_FACE_MAX = LL_FACE_NEGZ,
+
+ LL_EDGE_MIN = LL_EDGE_NEGX_NEGY,
+ LL_EDGE_MAX = LL_EDGE_POSZ_POSX
+ } EManipPartRanges;
+public:
+ static void rebuild(LLViewerObject* vobj);
+
+ LLManip( const LLString& name, LLToolComposite* composite );
+
+ virtual BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask) = 0;
+ void renderGuidelines(BOOL draw_x = TRUE, BOOL draw_y = TRUE, BOOL draw_z = TRUE);
+ static void renderXYZ(const LLVector3 &vec);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0;
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual EManipPart getHighlightedPart() { return LL_NO_PART; }
+ virtual void highlightManipulators(S32 x, S32 y) {};
+protected:
+ LLVector3 getSavedPivotPoint() const;
+ LLVector3 getPivotPoint() const;
+ void getManipNormal(LLViewerObject* object, EManipPart manip, LLVector3 &normal);
+ BOOL getManipAxis(LLViewerObject* object, EManipPart manip, LLVector3 &axis);
+ F32 getSubdivisionLevel(const LLVector3 &reference_point, const LLVector3 &translate_axis, F32 grid_scale, S32 min_pixel_spacing = MIN_DIVISION_PIXEL_WIDTH);
+ void renderTickValue(const LLVector3& pos, F32 value, const char* suffix, const LLColor4 &color);
+ void renderTickText(const LLVector3& pos, const char* suffix, const LLColor4 &color);
+ void updateGridSettings();
+ BOOL getMousePointOnPlaneGlobal(LLVector3d& point, S32 x, S32 y, LLVector3d origin, LLVector3 normal);
+ BOOL getMousePointOnPlaneAgent(LLVector3& point, S32 x, S32 y, LLVector3 origin, LLVector3 normal);
+ BOOL nearestPointOnLineFromMouse( S32 x, S32 y, const LLVector3& b1, const LLVector3& b2, F32 &a_param, F32 &b_param ) const;
+ LLColor4 setupSnapGuideRenderPass(S32 pass);
+protected:
+ LLFrameTimer mHelpTextTimer;
+ BOOL mInSnapRegime;
+
+ static F32 sHelpTextVisibleTime;
+ static F32 sHelpTextFadeTime;
+ static S32 sNumTimesHelpTextShown;
+ static S32 sMaxTimesShowHelpText;
+ static F32 sGridMaxSubdivisionLevel;
+ static F32 sGridMinSubdivisionLevel;
+ static LLVector2 sTickLabelSpacing;
+};
+
+
+#endif
diff --git a/indra/newview/llmaniprotate.cpp b/indra/newview/llmaniprotate.cpp
new file mode 100644
index 0000000000..a9508753c5
--- /dev/null
+++ b/indra/newview/llmaniprotate.cpp
@@ -0,0 +1,1876 @@
+/**
+ * @file llmaniprotate.cpp
+ * @brief LLManipRotate class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmaniprotate.h"
+
+// library includes
+#include "llmath.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "llprimitive.h"
+#include "llview.h"
+#include "llfontgl.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbox.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llhoverview.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llvoavatar.h"
+#include "llviewborder.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "lldrawable.h"
+#include "llglheaders.h"
+
+const F32 RADIUS_PIXELS = 100.f; // size in screen space
+const F32 SQ_RADIUS = RADIUS_PIXELS * RADIUS_PIXELS;
+const F32 WIDTH_PIXELS = 8;
+const S32 CIRCLE_STEPS = 100;
+const F32 DELTA = F_TWO_PI / CIRCLE_STEPS;
+const F32 SIN_DELTA = sin( DELTA );
+const F32 COS_DELTA = cos( DELTA );
+const F32 MAX_MANIP_SELECT_DISTANCE = 100.f;
+const F32 SNAP_ANGLE_INCREMENT = 5.625f;
+const F32 SNAP_ANGLE_DETENTE = SNAP_ANGLE_INCREMENT;
+const F32 SNAP_GUIDE_RADIUS_1 = 2.8f;
+const F32 SNAP_GUIDE_RADIUS_2 = 2.4f;
+const F32 SNAP_GUIDE_RADIUS_3 = 2.2f;
+const F32 SNAP_GUIDE_RADIUS_4 = 2.1f;
+const F32 SNAP_GUIDE_RADIUS_5 = 2.05f;
+const F32 SNAP_GUIDE_INNER_RADIUS = 2.f;
+const F32 AXIS_ONTO_CAM_TOLERANCE = cos( 80.f * DEG_TO_RAD );
+const F32 SELECTED_MANIPULATOR_SCALE = 1.05f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+
+extern void handle_reset_rotation(void*); // in LLViewerWindow
+extern void handle_first_tool(void*);
+
+LLManipRotate::LLManipRotate( LLToolComposite* composite )
+: LLManip( "Rotate", composite ),
+ mRotationCenter(),
+ mCenterScreen(),
+ mRotation(),
+ mMouseDown(),
+ mMouseCur(),
+ mRadiusMeters(0.f),
+ mCenterToCam(),
+ mCenterToCamNorm(),
+ mCenterToCamMag(0.f),
+ mCenterToProfilePlane(),
+ mCenterToProfilePlaneMag(0.f),
+ mManipPart( LL_NO_PART ),
+ mSendUpdateOnMouseUp( FALSE ),
+ mSmoothRotate( FALSE ),
+ mCamEdgeOn(FALSE),
+ mManipulatorScales(1.f, 1.f, 1.f, 1.f)
+{ }
+
+void LLManipRotate::handleSelect()
+{
+ //FIXME: put this in mouseDown?
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Drag colored bands to rotate object");
+}
+
+void LLManipRotate::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+
+ gFloaterTools->setStatusText("");
+}
+
+void LLManipRotate::render()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLEnable gl_blend(GL_BLEND);
+ LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
+
+ // You can rotate if you can move
+ LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if( !first_object )
+ {
+ return;
+ }
+
+ if( !updateVisiblity() )
+ {
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ LLColor4 highlight_outside( 1.f, 1.f, 0.f, 1.f );
+ LLColor4 highlight_inside( 0.7f, 0.7f, 0.f, 0.5f );
+ F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
+
+ glPushMatrix();
+ {
+ // are we in the middle of a constrained drag?
+ if (mManipPart >= LL_ROT_X && mManipPart <= LL_ROT_Z)
+ {
+ renderSnapGuides();
+ }
+ else
+ {
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ glPushMatrix();
+ {
+ // Draw "sphere" (intersection of sphere with tangent cone that has apex at camera)
+ glTranslatef( mCenterToProfilePlane.mV[VX], mCenterToProfilePlane.mV[VY], mCenterToProfilePlane.mV[VZ] );
+ glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
+
+ // Inverse change of basis vectors
+ LLVector3 forward = mCenterToCamNorm;
+ LLVector3 left = gAgent.getUpAxis() % forward;
+ left.normVec();
+ LLVector3 up = forward % left;
+
+ LLVector4 a(-forward);
+ a.mV[3] = 0;
+ LLVector4 b(up);
+ b.mV[3] = 0;
+ LLVector4 c(left);
+ c.mV[3] = 0;
+ LLMatrix4 mat;
+ mat.initRows(a, b, c, LLVector4(0.f, 0.f, 0.f, 1.f));
+
+ glMultMatrixf( &mat.mMatrix[0][0] );
+
+ glRotatef( -90, 0.f, 1.f, 0.f);
+ LLColor4 color;
+ if (mManipPart == LL_ROT_ROLL || mHighlightedPart == LL_ROT_ROLL)
+ {
+ color.setVec(0.8f, 0.8f, 0.8f, 0.8f);
+ glScalef(mManipulatorScales.mV[VW], mManipulatorScales.mV[VW], mManipulatorScales.mV[VW]);
+ }
+ else
+ {
+ color.setVec( 0.7f, 0.7f, 0.7f, 0.6f );
+ }
+ gl_washer_2d(mRadiusMeters + width_meters, mRadiusMeters, CIRCLE_STEPS, color, color);
+
+
+ if (mManipPart == LL_NO_PART)
+ {
+ glColor4f( 0.7f, 0.7f, 0.7f, 0.3f );
+ gl_circle_2d( 0, 0, mRadiusMeters, CIRCLE_STEPS, TRUE );
+ }
+
+ GLdouble plane_eqn[] = { 0, 0, 1, 0 };
+ glClipPlane( GL_CLIP_PLANE0, plane_eqn );
+ }
+ glPopMatrix();
+ }
+
+ glTranslatef( center.mV[VX], center.mV[VY], center.mV[VZ] );
+
+ LLQuaternion rot;
+ F32 angle_radians, x, y, z;
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+
+ if (mManipPart == LL_ROT_Z)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ // selected part
+ glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f) , LLColor4( 0.f, 0.f, 1.f, 0.3f ));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_Y)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 1.f, 0.f, 0.f );
+ glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f), LLColor4( 0.f, 1.f, 0.f, 0.3f));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_X)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 0.f, 1.f, 0.f );
+ glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
+ renderActiveRing( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f), LLColor4( 1.f, 0.f, 0.f, 0.3f));
+ }
+ glPopMatrix();
+ }
+ else if (mManipPart == LL_ROT_ROLL)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ else if (mManipPart == LL_NO_PART)
+ {
+ if (mHighlightedPart == LL_NO_PART)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLEnable clip_plane0(GL_CLIP_PLANE0);
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ // First pass: centers. Second pass: sides.
+ for( S32 i=0; i<2; i++ )
+ {
+ glPushMatrix();
+ {
+ if (mHighlightedPart == LL_ROT_Z)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, SELECTED_MANIPULATOR_SCALE, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ], mManipulatorScales.mV[VZ]);
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 1.f, 1.f ), LLColor4( 0.f, 0.f, 1.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.f, 0.8f, 0.8f ), LLColor4( 0.f, 0.f, 0.8f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 1.f, 0.f, 0.f );
+ if (mHighlightedPart == LL_ROT_Y)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, SELECTED_MANIPULATOR_SCALE, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VY], mManipulatorScales.mV[VY], mManipulatorScales.mV[VY]);
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 1.f, 0.f, 1.f ), LLColor4( 0.f, 1.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.f, 0.8f, 0.f, 0.8f ), LLColor4( 0.f, 0.8f, 0.f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ glPushMatrix();
+ {
+ glRotatef( 90.f, 0.f, 1.f, 0.f );
+ if (mHighlightedPart == LL_ROT_X)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(SELECTED_MANIPULATOR_SCALE, 1.f, 1.f, 1.f), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ glScalef(mManipulatorScales.mV[VX], mManipulatorScales.mV[VX], mManipulatorScales.mV[VX]);
+
+ // hovering over part
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 1.f, 0.f, 0.f, 1.f ), LLColor4( 1.f, 0.f, 0.f, 0.5f ), CIRCLE_STEPS, i);
+ }
+ else
+ {
+ // default
+ gl_ring( mRadiusMeters, width_meters, LLColor4( 0.8f, 0.f, 0.f, 0.8f ), LLColor4( 0.8f, 0.f, 0.f, 0.4f ), CIRCLE_STEPS, i);
+ }
+ }
+ glPopMatrix();
+
+ if (mHighlightedPart == LL_ROT_ROLL)
+ {
+ mManipulatorScales = lerp(mManipulatorScales, LLVector4(1.f, 1.f, 1.f, SELECTED_MANIPULATOR_SCALE), LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ }
+ }
+ }
+ glPopMatrix();
+ glPopMatrix();
+
+ LLVector3 euler_angles;
+ 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);
+
+ renderXYZ(euler_angles);
+}
+
+BOOL LLManipRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLViewerObject* first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if( first_object )
+ {
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj && mHighlightedPart != LL_NO_PART )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+ }
+
+ return handled;
+}
+
+// Assumes that one of the parts of the manipulator was hit.
+BOOL LLManipRotate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_rotate = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_rotate = can_rotate && objectp->permMove() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ if (!can_rotate)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+ // we just started a drag, so save initial object positions
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_ROTATE);
+
+ // save selection center
+ mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() ); //gSelectMgr->getSelectionCenterGlobal();
+
+ mManipPart = (EManipPart)hit_part;
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ if( mManipPart == LL_ROT_GENERAL)
+ {
+ mMouseDown = intersectMouseWithSphere( x, y, center, mRadiusMeters);
+ }
+ else
+ {
+ // Project onto the plane of the ring
+ LLVector3 axis = getConstraintAxis();
+
+ F32 axis_onto_cam = llabs( axis * mCenterToCamNorm );
+ const F32 AXIS_ONTO_CAM_TOL = cos( 85.f * DEG_TO_RAD );
+ if( axis_onto_cam < AXIS_ONTO_CAM_TOL )
+ {
+ LLVector3 up_from_axis = mCenterToCamNorm % axis;
+ up_from_axis.normVec();
+ LLVector3 cur_intersection;
+ getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
+ cur_intersection -= center;
+ mMouseDown = projected_vec(cur_intersection, up_from_axis);
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ F32 mouse_dist_sqrd = mMouseDown.magVecSquared();
+ if (mouse_dist_sqrd > 0.0001f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ mouse_dist_sqrd);
+ }
+ LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, axis);
+ mMouseDown += mouse_depth * projected_center_to_cam;
+
+ }
+ else
+ {
+ mMouseDown = findNearestPointOnRing( x, y, center, axis ) - center;
+ mMouseDown.normVec();
+ }
+ }
+
+ mMouseCur = mMouseDown;
+
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+ gSelectMgr->enableSilhouette(FALSE);
+ return TRUE;
+}
+
+
+LLVector3 LLManipRotate::findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis )
+{
+ // Project the delta onto the ring and rescale it by the radius so that it's _on_ the ring.
+ LLVector3 proj_onto_ring;
+ getMousePointOnPlaneAgent(proj_onto_ring, x, y, center, axis);
+ proj_onto_ring -= center;
+ proj_onto_ring.normVec();
+
+ return center + proj_onto_ring * mRadiusMeters;
+}
+
+BOOL LLManipRotate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ mManipPart = LL_NO_PART;
+
+ // Might have missed last update due to timing.
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( UPD_ROTATION | UPD_POSITION );
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ gSelectMgr->enableSilhouette(TRUE);
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ gSelectMgr->updateSelectionCenter();
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLManipRotate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ setMouseCapture( FALSE );
+ }
+ else
+ {
+ drag(x, y);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (active)" << llendl;
+ }
+ else
+ {
+ highlightManipulators(x, y);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipRotate (inactive)" << llendl;
+ }
+
+ gViewerWindow->setCursor(UI_CURSOR_TOOLROTATE);
+ return TRUE;
+}
+
+
+LLVector3 LLManipRotate::projectToSphere( F32 x, F32 y, BOOL* on_sphere )
+{
+ F32 z = 0.f;
+ F32 dist_squared = x*x + y*y;
+
+ *on_sphere = dist_squared <= SQ_RADIUS;
+ if( *on_sphere )
+ {
+ z = sqrt(SQ_RADIUS - dist_squared);
+ }
+ return LLVector3( x, y, z );
+}
+
+extern U32 gFrameCount;
+
+// Freeform rotation
+void LLManipRotate::drag( S32 x, S32 y )
+{
+ static LLTimer update_timer;
+ F32 elapsed_time = update_timer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+ BOOL send_rotation_update = FALSE;
+ BOOL send_position_update = FALSE;
+
+ if( !updateVisiblity() )
+ {
+ return;
+ }
+
+ if( mManipPart == LL_ROT_GENERAL )
+ {
+ mRotation = dragUnconstrained(x, y);
+ }
+ else
+ {
+ mRotation = dragConstrained(x, y);
+ }
+
+ BOOL damped = mSmoothRotate;
+ mSmoothRotate = FALSE;
+
+ LLViewerObject* object;
+ LLSelectNode* selectNode;
+ BOOL using_linked_selection = gSavedSettings.getBOOL("SelectLinkedSet");
+
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // have permission to move and object is root of selection or individually selected
+ if (object->permMove() && (object->isRootEdit() || selectNode->mIndividualSelection))
+ {
+ if (!object->isRootEdit())
+ {
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
+ {
+ // we will be moved properly by our parent, so skip
+ continue;
+ }
+ }
+
+ LLQuaternion new_rot = selectNode->mSavedRotation * mRotation;
+ std::vector<LLVector3> child_positions;
+ std::vector<LLQuaternion> child_rotations;
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ child_positions.push_back(childp->getPositionEdit());
+ child_rotations.push_back(childp->getRotationEdit());
+ }
+ }
+
+ if (object->getParent() && object->mDrawable.notNull())
+ {
+ LLQuaternion invParentRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ invParentRotation.transQuat();
+
+ object->setRotation(new_rot * invParentRotation, damped);
+ rebuild(object);
+ }
+ else
+ {
+ object->setRotation(new_rot, damped);
+ rebuild(object);
+ }
+
+ // don't send updates all the time for sub-objects
+ if (using_linked_selection && object->getRenderRotation() != new_rot)
+ {
+ send_rotation_update = TRUE;
+ }
+
+ // for individually selected roots, we need to counterrotate all the children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ //RN: must do non-damped updates on these objects so relative rotation appears constant
+ // instead of having two competing slerps making the child objects appear to "wobble"
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ LLVector3 child_offset = ((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit()) - childp->getPosition();
+ if (!childp->isSelected() && childp->mDrawable.notNull())
+ {
+ childp->setRotation(child_rotations[i] * ~object->getRotationEdit());
+ childp->setPosition((child_positions[i] - object->getPositionEdit()) * ~object->getRotationEdit());
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+
+ // update positions
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode != NULL; selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // to avoid cumulative position changes we calculate the objects new position using its saved position
+ if (object && object->permMove())
+ {
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ LLVector3 old_position;
+ LLVector3 new_position;
+
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // need to work in drawable space to handle selected items from multiple attachments
+ // (which have no shared frame of reference other than their render positions)
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ new_position = (selectNode->mSavedPositionLocal * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ old_position = (object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();//object->getRenderPosition();
+ }
+ else
+ {
+ new_position = gAgent.getPosAgentFromGlobal( selectNode->mSavedPositionGlobal );
+ old_position = object->getPositionAgent();
+ }
+
+ new_position = (new_position - center) * mRotation; // new relative rotated position
+ new_position += center;
+
+ if (object->isRootEdit() && !object->isAttachment())
+ {
+ LLVector3d new_pos_global = gAgent.getPosGlobalFromAgent(new_position);
+ new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
+ new_position = gAgent.getPosAgentFromGlobal(new_pos_global);
+ }
+
+ // for individually selected child objects
+ if (!object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ LLViewerObject* parentp = (LLViewerObject*)object->getParent();
+ if (!parentp->isSelected())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // find position relative to render position of parent
+ object->setPosition((new_position - parentp->getRenderPosition()) * ~parentp->getRenderRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionParent((new_position - parentp->getPositionAgent()) * ~parentp->getRotationRegion());
+ rebuild(object);
+ }
+ }
+ }
+ else if (object->isRootEdit())
+ {
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ object->setPosition((new_position - parent_xform->getWorldPosition()) * ~parent_xform->getWorldRotation());
+ rebuild(object);
+ }
+ else
+ {
+ object->setPositionAgent(new_position);
+ rebuild(object);
+ }
+ }
+
+ if (using_linked_selection && object->getPositionAgent() != new_position)
+ {
+ send_position_update = TRUE;
+ }
+
+ // for individually selected roots, we need to counter-translate all unselected children
+ if (object->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ // only offset by parent's translation as we've already countered parent's rotation
+ LLVector3 child_offset;
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ LLXform* attachment_point_xform = object->mDrawable->getXform()->getParent();
+ LLQuaternion parent_rotation = object->getRotation() * attachment_point_xform->getWorldRotation();
+ child_offset = LLVector3(old_position - new_position) * ~parent_rotation;
+ }
+ else
+ {
+ child_offset = LLVector3(old_position - new_position) * ~object->getRenderRotation();
+ }
+
+ rebuild(object);
+ for (U32 i = 0; i < object->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = object->mChildList[i];
+ if (!childp->isSelected() && childp->mDrawable.notNull())
+ {
+ childp->setPosition(childp->getPosition() + child_offset);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+
+ if ((send_position_update || send_rotation_update) && (elapsed_time > UPDATE_DELAY))
+ {
+ U32 flag = UPD_NONE;
+ if (send_rotation_update)
+ {
+ flag |= UPD_ROTATION;
+ }
+ if (send_position_update)
+ {
+ flag |= UPD_POSITION;
+ }
+
+ gSelectMgr->sendMultipleUpdate( flag );
+ update_timer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+
+ // RN: just clear focus so camera doesn't follow spurious object updates
+ gAgent.clearFocusObject();
+ dialog_refresh_all();
+}
+
+void LLManipRotate::renderActiveRing( F32 radius, F32 width, const LLColor4& front_color, const LLColor4& back_color)
+{
+ LLGLEnable cull_face(GL_CULL_FACE);
+ {
+ gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, FALSE);
+ gl_ring(radius, width, back_color, back_color * 0.5f, CIRCLE_STEPS, TRUE);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+ gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, FALSE);
+ gl_ring(radius, width, front_color, front_color * 0.5f, CIRCLE_STEPS, TRUE);
+ }
+}
+
+void LLManipRotate::renderSnapGuides()
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ LLVector3 constraint_axis = getConstraintAxis();
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+ LLVector3 cam_at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_at_axis = center - gAgent.getCameraPositionAgent();
+ cam_at_axis.normVec();
+ }
+
+ LLVector3 world_snap_axis;
+ LLVector3 test_axis = constraint_axis;
+
+ BOOL constrain_to_ref_object = FALSE;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ test_axis = test_axis * ~grid_rotation;
+ constrain_to_ref_object = TRUE;
+ }
+
+ test_axis.abs();
+
+ // find closest global/reference axis to local constraint axis;
+ if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
+ {
+ world_snap_axis = LLVector3::y_axis;
+ }
+ else if (test_axis.mV[VY] > test_axis.mV[VZ])
+ {
+ world_snap_axis = LLVector3::z_axis;
+ }
+ else
+ {
+ world_snap_axis = LLVector3::x_axis;
+ }
+
+ LLVector3 projected_snap_axis = world_snap_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ projected_snap_axis = projected_snap_axis * grid_rotation;
+ }
+ else if (constrain_to_ref_object)
+ {
+ projected_snap_axis = projected_snap_axis * grid_rotation;
+ }
+
+ // project world snap axis onto constraint plane
+ projected_snap_axis -= projected_vec(projected_snap_axis, constraint_axis);
+ projected_snap_axis.normVec();
+
+ S32 num_rings = mCamEdgeOn ? 2 : 1;
+ for (S32 ring_num = 0; ring_num < num_rings; ring_num++)
+ {
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ if (mCamEdgeOn)
+ {
+ // draw two opposing rings
+ if (ring_num == 0)
+ {
+ center += constraint_axis * mRadiusMeters * 0.5f;
+ }
+ else
+ {
+ center -= constraint_axis * mRadiusMeters * 0.5f;
+ }
+ }
+
+ LLGLDepthTest gls_depth(GL_FALSE);
+ for (S32 pass = 0; pass < 3; pass++)
+ {
+ // render snap guide ring
+ glPushMatrix();
+
+ LLQuaternion snap_guide_rot;
+ F32 angle_radians, x, y, z;
+ snap_guide_rot.shortestArc(LLVector3::z_axis, getConstraintAxis());
+ snap_guide_rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glTranslatef(center.mV[VX], center.mV[VY], center.mV[VZ]);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLColor4 line_color = setupSnapGuideRenderPass(pass);
+
+ glColor4fv(line_color.mV);
+
+ if (mCamEdgeOn)
+ {
+ // render an arc
+ LLVector3 edge_normal = cam_at_axis % constraint_axis;
+ edge_normal.normVec();
+ LLVector3 x_axis_snap = LLVector3::x_axis * snap_guide_rot;
+ LLVector3 y_axis_snap = LLVector3::y_axis * snap_guide_rot;
+
+ F32 end_angle = atan2(y_axis_snap * edge_normal, x_axis_snap * edge_normal);
+ //F32 start_angle = angle_between((-1.f * LLVector3::x_axis) * snap_guide_rot, edge_normal);
+ F32 start_angle = end_angle - F_PI;
+ gl_arc_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE, start_angle, end_angle);
+ }
+ else
+ {
+ gl_circle_2d(0.f, 0.f, mRadiusMeters * SNAP_GUIDE_INNER_RADIUS, CIRCLE_STEPS, FALSE);
+ }
+ glPopMatrix();
+
+ for (S32 i = 0; i < 64; i++)
+ {
+ BOOL render_text = TRUE;
+ F32 deg = 5.625f * (F32)i;
+ LLVector3 inner_point;
+ LLVector3 outer_point;
+ LLVector3 text_point;
+ LLQuaternion rot(deg * DEG_TO_RAD, constraint_axis);
+ glBegin(GL_LINES);
+ {
+ inner_point = (projected_snap_axis * mRadiusMeters * SNAP_GUIDE_INNER_RADIUS * rot) + center;
+ F32 tick_length = 0.f;
+ if (i % 16 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_1 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 8 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_2 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 4 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_3 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else if (i % 2 == 0)
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_4 - SNAP_GUIDE_INNER_RADIUS);
+ }
+ else
+ {
+ tick_length = mRadiusMeters * (SNAP_GUIDE_RADIUS_5 - SNAP_GUIDE_INNER_RADIUS);
+ }
+
+ if (mCamEdgeOn)
+ {
+ // don't draw ticks that are on back side of circle
+ F32 dot = cam_at_axis * (projected_snap_axis * rot);
+ if (dot > 0.f)
+ {
+ outer_point = inner_point;
+ render_text = FALSE;
+ }
+ else
+ {
+ if (ring_num == 0)
+ {
+ outer_point = inner_point + (constraint_axis * tick_length) * rot;
+ }
+ else
+ {
+ outer_point = inner_point - (constraint_axis * tick_length) * rot;
+ }
+ }
+ }
+ else
+ {
+ outer_point = inner_point + (projected_snap_axis * tick_length) * rot;
+ }
+
+ text_point = outer_point + (projected_snap_axis * mRadiusMeters * 0.1f) * rot;
+
+ glVertex3fv(inner_point.mV);
+ glVertex3fv(outer_point.mV);
+ }
+ glEnd();
+
+ //RN: text rendering does own shadow pass, so only render once
+ if (pass == 1 && render_text && i % 16 == 0)
+ {
+ if (world_snap_axis.mV[VX])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VZ] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VZ] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ }
+ }
+ else if (world_snap_axis.mV[VY])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Left" : "North", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VX] > 0.f)
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Right" : "South", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VX] > 0.f)
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ }
+ }
+ else if (world_snap_axis.mV[VZ])
+ {
+ if (i == 0)
+ {
+ renderTickText(text_point, "Up", LLColor4::white);
+ }
+ else if (i == 16)
+ {
+ if (constraint_axis.mV[VY] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ }
+ else if (i == 32)
+ {
+ renderTickText(text_point, "Down", LLColor4::white);
+ }
+ else
+ {
+ if (constraint_axis.mV[VY] > 0.f)
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Back" : "West", LLColor4::white);
+ }
+ else
+ {
+ renderTickText(text_point, gSelectMgr->selectionIsAttachment() ? "Forward" : "East", LLColor4::white);
+ }
+ }
+ }
+ }
+ glColor4fv(line_color.mV);
+ }
+
+ // now render projected object axis
+ if (mInSnapRegime)
+ {
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+
+ // project onto constraint plane
+ LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ object_axis = object_axis * first_node->getObject()->getRenderRotation();
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+ object_axis = object_axis * SNAP_GUIDE_INNER_RADIUS * mRadiusMeters + center;
+ LLVector3 line_start = center;
+
+ glBegin(GL_LINES);
+ {
+ glVertex3fv(line_start.mV);
+ glVertex3fv(object_axis.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
+ arrow_span.normVec();
+
+ arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
+ arrow_dir.normVec();
+ if (ring_num == 1)
+ {
+ arrow_dir *= -1.f;
+ }
+ glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
+ }
+ glEnd();
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ glBegin(GL_LINES);
+ {
+ glVertex3fv(line_start.mV);
+ glVertex3fv(object_axis.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = (object_axis - line_start) % getConstraintAxis();
+ arrow_span.normVec();
+
+ arrow_dir = mCamEdgeOn ? getConstraintAxis() : object_axis - line_start;
+ arrow_dir.normVec();
+ if (ring_num == 1)
+ {
+ arrow_dir *= -1.f;
+ }
+
+ glVertex3fv((object_axis + arrow_dir * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis + arrow_span * mRadiusMeters * 0.1f).mV);
+ glVertex3fv((object_axis - arrow_span * mRadiusMeters * 0.1f).mV);
+ }
+ glEnd();
+ }
+ }
+ }
+ }
+}
+
+// Returns TRUE if center of sphere is visible. Also sets a bunch of member variables that are used later (e.g. mCenterToCam)
+BOOL LLManipRotate::updateVisiblity()
+{
+ // Don't want to recalculate the center of the selection during a drag.
+ // Due to packet delays, sometimes half the objects in the selection have their
+ // new position and half have their old one. This creates subtle errors in the
+ // computed center position for that frame. Unfortunately, these errors
+ // accumulate. The result is objects seem to "fly apart" during rotations.
+ // JC - 03.26.2002
+ if (!hasMouseCapture())
+ {
+ mRotationCenter = gAgent.getPosGlobalFromAgent( getPivotPoint() );//gSelectMgr->getSelectionCenterGlobal();
+ }
+
+ BOOL visible = FALSE;
+
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mCenterToCam = LLVector3(-1.f / gAgent.getAvatarObject()->mHUDCurZoom, 0.f, 0.f);
+ mCenterToCamNorm = mCenterToCam;
+ mCenterToCamMag = mCenterToCamNorm.normVec();
+
+ mRadiusMeters = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
+ mRadiusMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+
+ mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
+ mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
+
+ mCenterScreen.set((S32)((0.5f - mRotationCenter.mdV[VY]) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowWidth()),
+ (S32)((mRotationCenter.mdV[VZ] + 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom * gViewerWindow->getWindowHeight()));
+ visible = TRUE;
+ }
+ else
+ {
+ visible = gCamera->projectPosAgentToScreen(center, mCenterScreen );
+ if( visible )
+ {
+ mCenterToCam = gAgent.getCameraPositionAgent() - center;
+ mCenterToCamNorm = mCenterToCam;
+ mCenterToCamMag = mCenterToCamNorm.normVec();
+ LLVector3 cameraAtAxis = gCamera->getAtAxis();
+ cameraAtAxis.normVec();
+
+ F32 z_dist = -1.f * (mCenterToCam * cameraAtAxis);
+
+ // Don't drag manip if object too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
+ if (dist_vec(gAgent.getPositionAgent(), center) > max_select_distance)
+ {
+ visible = FALSE;
+ }
+ }
+
+ if (mCenterToCamMag > 0.001f)
+ {
+ F32 fraction_of_fov = RADIUS_PIXELS / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mRadiusMeters = z_dist * tan(apparent_angle);
+
+ mCenterToProfilePlaneMag = mRadiusMeters * mRadiusMeters / mCenterToCamMag;
+ mCenterToProfilePlane = -mCenterToProfilePlaneMag * mCenterToCamNorm;
+ }
+ else
+ {
+ visible = FALSE;
+ }
+ }
+ }
+
+ mCamEdgeOn = FALSE;
+ F32 axis_onto_cam = mManipPart >= LL_ROT_X ? llabs( getConstraintAxis() * mCenterToCamNorm ) : 0.f;
+ if( axis_onto_cam < AXIS_ONTO_CAM_TOLERANCE )
+ {
+ mCamEdgeOn = TRUE;
+ }
+
+ return visible;
+}
+
+LLQuaternion LLManipRotate::dragUnconstrained( S32 x, S32 y )
+{
+ LLVector3 cam = gAgent.getCameraPositionAgent();
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ mMouseCur = intersectMouseWithSphere( x, y, center, mRadiusMeters);
+
+ F32 delta_x = (F32)(mCenterScreen.mX - x);
+ F32 delta_y = (F32)(mCenterScreen.mY - y);
+
+ F32 dist_from_sphere_center = sqrt(delta_x * delta_x + delta_y * delta_y);
+
+ LLVector3 axis = mMouseDown % mMouseCur;
+ axis.normVec();
+ F32 angle = acos(mMouseDown * mMouseCur);
+ LLQuaternion sphere_rot( angle, axis );
+
+ if (is_approx_zero(1.f - mMouseDown * mMouseCur))
+ {
+ return LLQuaternion::DEFAULT;
+ }
+ else if (dist_from_sphere_center < RADIUS_PIXELS)
+ {
+ return sphere_rot;
+ }
+ else
+ {
+ LLVector3 intersection;
+ getMousePointOnPlaneAgent( intersection, x, y, center + mCenterToProfilePlane, mCenterToCamNorm );
+
+ // amount dragging in sphere from center to periphery would rotate object
+ F32 in_sphere_angle = F_PI_BY_TWO;
+ F32 dist_to_tangent_point = mRadiusMeters;
+ if( !is_approx_zero( mCenterToProfilePlaneMag ) )
+ {
+ dist_to_tangent_point = sqrt( mRadiusMeters * mRadiusMeters - mCenterToProfilePlaneMag * mCenterToProfilePlaneMag );
+ in_sphere_angle = atan2( dist_to_tangent_point, mCenterToProfilePlaneMag );
+ }
+
+ LLVector3 profile_center_to_intersection = intersection - (center + mCenterToProfilePlane);
+ F32 dist_to_intersection = profile_center_to_intersection.normVec();
+ F32 angle = (-1.f + dist_to_intersection / dist_to_tangent_point) * in_sphere_angle;
+
+ LLVector3 axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ axis = LLVector3(-1.f, 0.f, 0.f) % profile_center_to_intersection;
+ }
+ else
+ {
+ axis = (cam - center) % profile_center_to_intersection;
+ axis.normVec();
+ }
+ return sphere_rot * LLQuaternion( angle, axis );
+ }
+}
+
+LLVector3 LLManipRotate::getConstraintAxis()
+{
+ LLVector3 axis;
+ if( LL_ROT_ROLL == mManipPart )
+ {
+ axis = mCenterToCamNorm;
+ }
+ else
+ {
+ S32 axis_dir = mManipPart - LL_ROT_X;
+ if ((axis_dir >= 0) && (axis_dir < 3))
+ {
+ axis.mV[axis_dir] = 1.f;
+ }
+ else
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llerrs << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
+#else
+ llwarns << "Got bogus hit part in LLManipRotate::getConstraintAxis():" << mManipPart << llendl
+#endif
+ axis.mV[0] = 1.f;
+ }
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLSelectNode* first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (first_node)
+ {
+ //FIXME: get agent local attachment grid working
+ // Put rotation into frame of first selected root object
+ axis = axis * grid_rotation;
+ }
+ }
+
+ return axis;
+}
+
+LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
+{
+ LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ LLVector3 constraint_axis = getConstraintAxis();
+ LLVector3 center = gAgent.getPosAgentFromGlobal( mRotationCenter );
+
+ F32 angle = 0.f;
+
+ // build snap axes
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 axis1;
+ LLVector3 axis2;
+
+ LLVector3 test_axis = constraint_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ test_axis = test_axis * ~grid_rotation;
+ }
+ test_axis.abs();
+
+ // find closest global axis to constraint axis;
+ if (test_axis.mV[VX] > test_axis.mV[VY] && test_axis.mV[VX] > test_axis.mV[VZ])
+ {
+ axis1 = LLVector3::y_axis;
+ }
+ else if (test_axis.mV[VY] > test_axis.mV[VZ])
+ {
+ axis1 = LLVector3::z_axis;
+ }
+ else
+ {
+ axis1 = LLVector3::x_axis;
+ }
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ axis1 = axis1 * grid_rotation;
+ }
+ else if (gSelectMgr->getGridMode() == GRID_MODE_REF_OBJECT)
+ {
+ axis1 = axis1 * grid_rotation;
+ }
+
+ //project axis onto constraint plane
+ axis1 -= (axis1 * constraint_axis) * constraint_axis;
+ axis1.normVec();
+
+ // calculate third and final axis
+ axis2 = constraint_axis % axis1;
+
+ //F32 axis_onto_cam = llabs( constraint_axis * mCenterToCamNorm );
+ if( mCamEdgeOn )
+ {
+ // We're looking at the ring edge-on.
+ LLVector3 snap_plane_center = (center + (constraint_axis * mRadiusMeters * 0.5f));
+ LLVector3 cam_to_snap_plane;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_to_snap_plane.normVec();
+ }
+
+ LLVector3 projected_mouse;
+ BOOL hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
+ projected_mouse -= snap_plane_center;
+
+ S32 snap_plane = 0;
+
+ F32 dot = cam_to_snap_plane * constraint_axis;
+ if (llabs(dot) < 0.01f)
+ {
+ // looking at ring edge on, project onto view plane and check if mouse is past ring
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
+ projected_mouse -= snap_plane_center;
+ dot = projected_mouse * constraint_axis;
+ if (projected_mouse * constraint_axis > 0)
+ {
+ snap_plane = 1;
+ }
+ projected_mouse -= dot * constraint_axis;
+ }
+ else if (dot > 0.f)
+ {
+ // look for mouse position outside and in front of snap circle
+ if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
+ {
+ snap_plane = 1;
+ }
+ }
+ else
+ {
+ // look for mouse position inside or in back of snap circle
+ if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
+ {
+ snap_plane = 1;
+ }
+ }
+
+ if (snap_plane == 0)
+ {
+ // try other plane
+ snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_to_snap_plane = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_to_snap_plane.normVec();
+ }
+
+ hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
+ projected_mouse -= snap_plane_center;
+
+ dot = cam_to_snap_plane * constraint_axis;
+ if (llabs(dot) < 0.01f)
+ {
+ // looking at ring edge on, project onto view plane and check if mouse is past ring
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
+ projected_mouse -= snap_plane_center;
+ dot = projected_mouse * constraint_axis;
+ if (projected_mouse * constraint_axis < 0)
+ {
+ snap_plane = 2;
+ }
+ projected_mouse -= dot * constraint_axis;
+ }
+ else if (dot < 0.f)
+ {
+ // look for mouse position outside and in front of snap circle
+ if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
+ {
+ snap_plane = 2;
+ }
+ }
+ else
+ {
+ // look for mouse position inside or in back of snap circle
+ if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
+ {
+ snap_plane = 2;
+ }
+ }
+ }
+
+ if (snap_plane > 0)
+ {
+ LLVector3 cam_at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ cam_at_axis = snap_plane_center - gAgent.getCameraPositionAgent();
+ cam_at_axis.normVec();
+ }
+
+ // first, project mouse onto screen plane at point tangent to rotation radius.
+ getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
+ // project that point onto rotation plane
+ projected_mouse -= snap_plane_center;
+ projected_mouse -= projected_vec(projected_mouse, constraint_axis);
+
+ F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ if (llabs(mouse_lateral_dist) > 0.01f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ (mouse_lateral_dist * mouse_lateral_dist));
+ }
+ LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
+ projected_mouse -= mouse_depth * projected_camera_at;
+
+ if (!mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = TRUE;
+ // 0 to 360 deg
+ 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);
+
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+ object_axis = object_axis * first_object_node->mSavedRotation;
+
+ // project onto constraint plane
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+
+ if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
+ {
+ F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
+ angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ else
+ {
+ angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ return LLQuaternion( -angle, constraint_axis );
+ }
+ else
+ {
+ if (mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = FALSE;
+
+ LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
+ up_from_axis.normVec();
+ LLVector3 cur_intersection;
+ getMousePointOnPlaneAgent(cur_intersection, x, y, center, mCenterToCam);
+ cur_intersection -= center;
+ mMouseCur = projected_vec(cur_intersection, up_from_axis);
+ F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
+ F32 mouse_dist_sqrd = mMouseCur.magVecSquared();
+ if (mouse_dist_sqrd > 0.0001f)
+ {
+ mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
+ mouse_dist_sqrd);
+ }
+ LLVector3 projected_center_to_cam = mCenterToCamNorm - projected_vec(mCenterToCamNorm, constraint_axis);
+ mMouseCur += mouse_depth * projected_center_to_cam;
+
+ F32 dist = (cur_intersection * up_from_axis) - (mMouseDown * up_from_axis);
+ angle = dist / (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * -F_PI_BY_TWO;
+ }
+ }
+ else
+ {
+ LLVector3 projected_mouse;
+ getMousePointOnPlaneAgent(projected_mouse, x, y, center, constraint_axis);
+ projected_mouse -= center;
+ mMouseCur = projected_mouse;
+ mMouseCur.normVec();
+
+ if (!first_object_node)
+ {
+ return LLQuaternion::DEFAULT;
+ }
+
+ if (gSavedSettings.getBOOL("SnapEnabled") && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters)
+ {
+ if (!mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = TRUE;
+ // 0 to 360 deg
+ 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);
+
+ LLVector3 object_axis;
+ getObjectAxisClosestToMouse(object_axis);
+ object_axis = object_axis * first_object_node->mSavedRotation;
+
+ // project onto constraint plane
+ object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
+ object_axis.normVec();
+
+ if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
+ {
+ F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
+ angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ else
+ {
+ angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
+ }
+ return LLQuaternion( -angle, constraint_axis );
+ }
+ else
+ {
+ if (mInSnapRegime)
+ {
+ mSmoothRotate = TRUE;
+ }
+ mInSnapRegime = FALSE;
+ }
+
+ angle = acos(mMouseCur * mMouseDown);
+
+ F32 dir = (mMouseDown % mMouseCur) * constraint_axis; // cross product
+ if( dir < 0.f )
+ {
+ angle *= -1.f;
+ }
+ }
+
+ F32 rot_step = gSavedSettings.getF32("RotationStep");
+ F32 step_size = DEG_TO_RAD * rot_step;
+ angle -= fmod(angle, step_size);
+
+ return LLQuaternion( angle, constraint_axis );
+}
+
+
+
+LLVector3 LLManipRotate::intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius)
+{
+ LLVector3 ray_pt;
+ LLVector3 ray_dir;
+ mouseToRay( x, y, &ray_pt, &ray_dir);
+ return intersectRayWithSphere( ray_pt, ray_dir, sphere_center, sphere_radius );
+}
+
+LLVector3 LLManipRotate::intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius)
+{
+ LLVector3 ray_pt_to_center = sphere_center - ray_pt;
+ F32 center_distance = ray_pt_to_center.normVec();
+
+ F32 dot = ray_dir * ray_pt_to_center;
+
+ if (dot == 0.f)
+ {
+ return LLVector3::zero;
+ }
+
+ // point which ray hits plane centered on sphere origin, facing ray origin
+ LLVector3 intersection_sphere_plane = ray_pt + (ray_dir * center_distance / dot);
+ // vector from sphere origin to the point, normalized to sphere radius
+ LLVector3 sphere_center_to_intersection = (intersection_sphere_plane - sphere_center) / sphere_radius;
+
+ F32 dist_squared = sphere_center_to_intersection.magVecSquared();
+ LLVector3 result;
+
+ if (dist_squared > 1.f)
+ {
+ result = sphere_center_to_intersection;
+ result.normVec();
+ }
+ else
+ {
+ result = sphere_center_to_intersection - ray_dir * sqrt(1.f - dist_squared);
+ }
+
+ return result;
+}
+
+// Utility function. Should probably be moved to another class.
+void LLManipRotate::mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir )
+{
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 mouse_x = (((F32)x / gViewerWindow->getWindowWidth()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+ F32 mouse_y = ((((F32)y) / gViewerWindow->getWindowHeight()) - 0.5f) / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ *ray_pt = LLVector3(-1.f, -mouse_x, mouse_y);
+ *ray_dir = LLVector3(1.f, 0.f, 0.f);
+ }
+ else
+ {
+ *ray_pt = gAgent.getCameraPositionAgent();
+ gCamera->projectScreenToPosAgent(x, y, ray_dir);
+ *ray_dir -= *ray_pt;
+ ray_dir->normVec();
+ }
+}
+
+void LLManipRotate::highlightManipulators( S32 x, S32 y )
+{
+ mHighlightedPart = LL_NO_PART;
+
+ //LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLViewerObject *first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+
+ if (!first_object)
+ {
+ return;
+ }
+
+ LLQuaternion object_rot = first_object->getRenderRotation();
+ LLVector3 rotation_center = gAgent.getPosAgentFromGlobal(mRotationCenter);
+ LLVector3 mouse_dir_x;
+ LLVector3 mouse_dir_y;
+ LLVector3 mouse_dir_z;
+ LLVector3 intersection_roll;
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 rot_x_axis = LLVector3::x_axis * grid_rotation;
+ LLVector3 rot_y_axis = LLVector3::y_axis * grid_rotation;
+ LLVector3 rot_z_axis = LLVector3::z_axis * grid_rotation;
+
+ F32 proj_rot_x_axis = llabs(rot_x_axis * mCenterToCamNorm);
+ F32 proj_rot_y_axis = llabs(rot_y_axis * mCenterToCamNorm);
+ F32 proj_rot_z_axis = llabs(rot_z_axis * mCenterToCamNorm);
+
+ F32 min_select_distance = 0.f;
+ F32 cur_select_distance = 0.f;
+
+ // test x
+ getMousePointOnPlaneAgent(mouse_dir_x, x, y, rotation_center, rot_x_axis);
+ mouse_dir_x -= rotation_center;
+ // push intersection point out when working at obtuse angle to make ring easier to hit
+ mouse_dir_x *= 1.f + (1.f - llabs(rot_x_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test y
+ getMousePointOnPlaneAgent(mouse_dir_y, x, y, rotation_center, rot_y_axis);
+ mouse_dir_y -= rotation_center;
+ mouse_dir_y *= 1.f + (1.f - llabs(rot_y_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test z
+ getMousePointOnPlaneAgent(mouse_dir_z, x, y, rotation_center, rot_z_axis);
+ mouse_dir_z -= rotation_center;
+ mouse_dir_z *= 1.f + (1.f - llabs(rot_z_axis * mCenterToCamNorm)) * 0.1f;
+
+ // test roll
+ getMousePointOnPlaneAgent(intersection_roll, x, y, rotation_center, mCenterToCamNorm);
+ intersection_roll -= rotation_center;
+
+ F32 dist_x = mouse_dir_x.normVec();
+ F32 dist_y = mouse_dir_y.normVec();
+ F32 dist_z = mouse_dir_z.normVec();
+
+ F32 distance_threshold = (MAX_MANIP_SELECT_DISTANCE * mRadiusMeters) / gViewerWindow->getWindowHeight();
+
+ if (llabs(dist_x - mRadiusMeters) * llmax(0.05f, proj_rot_x_axis) < distance_threshold)
+ {
+ // selected x
+ cur_select_distance = dist_x * mouse_dir_x * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_X;
+ }
+ }
+ if (llabs(dist_y - mRadiusMeters) * llmax(0.05f, proj_rot_y_axis) < distance_threshold)
+ {
+ // selected y
+ cur_select_distance = dist_y * mouse_dir_y * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_Y;
+ }
+ }
+ if (llabs(dist_z - mRadiusMeters) * llmax(0.05f, proj_rot_z_axis) < distance_threshold)
+ {
+ // selected z
+ cur_select_distance = dist_z * mouse_dir_z * mCenterToCamNorm;
+ if (cur_select_distance >= -0.05f && (min_select_distance == 0.f || cur_select_distance > min_select_distance))
+ {
+ min_select_distance = cur_select_distance;
+ mHighlightedPart = LL_ROT_Z;
+ }
+ }
+
+ // test for edge-on intersections
+ if (proj_rot_x_axis < 0.05f)
+ {
+ if ((proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_x_axis) < distance_threshold) && dist_y < mRadiusMeters) ||
+ (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_x_axis) < distance_threshold) && dist_z < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_X;
+ }
+ }
+
+ if (proj_rot_y_axis < 0.05f)
+ {
+ if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_y_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
+ (proj_rot_z_axis > 0.05f && (dist_z * llabs(mouse_dir_z * rot_y_axis) < distance_threshold) && dist_z < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_Y;
+ }
+ }
+
+ if (proj_rot_z_axis < 0.05f)
+ {
+ if ((proj_rot_x_axis > 0.05f && (dist_x * llabs(mouse_dir_x * rot_z_axis) < distance_threshold) && dist_x < mRadiusMeters) ||
+ (proj_rot_y_axis > 0.05f && (dist_y * llabs(mouse_dir_y * rot_z_axis) < distance_threshold) && dist_y < mRadiusMeters))
+ {
+ mHighlightedPart = LL_ROT_Z;
+ }
+ }
+
+ // test for roll
+ if (mHighlightedPart == LL_NO_PART)
+ {
+ F32 roll_distance = intersection_roll.magVec();
+ F32 width_meters = WIDTH_PIXELS * mRadiusMeters / RADIUS_PIXELS;
+
+ // use larger distance threshold for roll as it is checked only if something else wasn't highlighted
+ if (llabs(roll_distance - (mRadiusMeters + (width_meters * 2.f))) < distance_threshold * 2.f)
+ {
+ mHighlightedPart = LL_ROT_ROLL;
+ }
+ else if (roll_distance < mRadiusMeters)
+ {
+ mHighlightedPart = LL_ROT_GENERAL;
+ }
+ }
+}
+
+S32 LLManipRotate::getObjectAxisClosestToMouse(LLVector3& object_axis)
+{
+ LLSelectNode* first_object_node = gSelectMgr->getFirstMoveableNode(TRUE);
+
+ if (!first_object_node)
+ {
+ object_axis.clearVec();
+ return -1;
+ }
+
+ LLQuaternion obj_rotation = first_object_node->mSavedRotation;
+ LLVector3 mouse_down_object = mMouseDown * ~obj_rotation;
+ LLVector3 mouse_down_abs = mouse_down_object;
+ mouse_down_abs.abs();
+
+ S32 axis_index = 0;
+ if (mouse_down_abs.mV[VX] > mouse_down_abs.mV[VY] && mouse_down_abs.mV[VX] > mouse_down_abs.mV[VZ])
+ {
+ if (mouse_down_object.mV[VX] > 0.f)
+ {
+ object_axis = LLVector3::x_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::x_axis_neg;
+ }
+ axis_index = VX;
+ }
+ else if (mouse_down_abs.mV[VY] > mouse_down_abs.mV[VZ])
+ {
+ if (mouse_down_object.mV[VY] > 0.f)
+ {
+ object_axis = LLVector3::y_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::y_axis_neg;
+ }
+ axis_index = VY;
+ }
+ else
+ {
+ if (mouse_down_object.mV[VZ] > 0.f)
+ {
+ object_axis = LLVector3::z_axis;
+ }
+ else
+ {
+ object_axis = LLVector3::z_axis_neg;
+ }
+ axis_index = VZ;
+ }
+
+ return axis_index;
+}
diff --git a/indra/newview/llmaniprotate.h b/indra/newview/llmaniprotate.h
new file mode 100644
index 0000000000..5af6b6be92
--- /dev/null
+++ b/indra/newview/llmaniprotate.h
@@ -0,0 +1,100 @@
+/**
+ * @file llmaniprotate.h
+ * @brief LLManipRotate class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMANIPROTATE_H
+#define LL_LLMANIPROTATE_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "llregionposition.h"
+#include "llmanip.h"
+#include "llviewerobject.h"
+
+class LLToolComposite;
+class LLColor4;
+
+class LLManipRotate : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mAxisU;
+ LLVector3 mAxisV;
+ U32 mManipID;
+
+ ManipulatorHandle(LLVector3 axis_u, LLVector3 axis_v, U32 id) : mAxisU(axis_u), mAxisV(axis_v), mManipID(id){}
+ };
+
+ LLManipRotate( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void render();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+ virtual void highlightManipulators(S32 x, S32 y);
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+private:
+ void updateHoverView();
+
+ void drag( S32 x, S32 y );
+ LLVector3 projectToSphere( F32 x, F32 y, BOOL* on_sphere );
+
+ void renderSnapGuides();
+ void renderActiveRing(F32 radius, F32 width, const LLColor4& center_color, const LLColor4& side_color);
+
+ BOOL updateVisiblity();
+ LLVector3 findNearestPointOnRing( S32 x, S32 y, const LLVector3& center, const LLVector3& axis );
+
+ LLQuaternion dragUnconstrained( S32 x, S32 y );
+ LLQuaternion dragConstrained( S32 x, S32 y );
+ LLVector3 getConstraintAxis();
+ S32 getObjectAxisClosestToMouse(LLVector3& axis);
+
+ // Utility functions
+ static void mouseToRay( S32 x, S32 y, LLVector3* ray_pt, LLVector3* ray_dir );
+ static LLVector3 intersectMouseWithSphere( S32 x, S32 y, const LLVector3& sphere_center, F32 sphere_radius );
+ static LLVector3 intersectRayWithSphere( const LLVector3& ray_pt, const LLVector3& ray_dir, const LLVector3& sphere_center, F32 sphere_radius);
+
+private:
+ LLVector3d mRotationCenter;
+ LLCoordGL mCenterScreen;
+// S32 mLastHoverMouseX; // used to suppress hover if mouse doesn't move
+// S32 mLastHoverMouseY;
+ LLQuaternion mRotation;
+
+ LLVector3 mMouseDown;
+ LLVector3 mMouseCur;
+ F32 mRadiusMeters;
+
+ LLVector3 mCenterToCam;
+ LLVector3 mCenterToCamNorm;
+ F32 mCenterToCamMag;
+ LLVector3 mCenterToProfilePlane;
+ F32 mCenterToProfilePlaneMag;
+
+ EManipPart mManipPart;
+
+ BOOL mSendUpdateOnMouseUp;
+ EManipPart mHighlightedPart;
+
+ BOOL mSmoothRotate;
+ BOOL mCamEdgeOn;
+
+ LLVector4 mManipulatorVertices[6];
+ LLVector4 mManipulatorScales;
+};
+
+#endif // LL_LLMANIPROTATE_H
diff --git a/indra/newview/llmanipscale.cpp b/indra/newview/llmanipscale.cpp
new file mode 100644
index 0000000000..aa3749b539
--- /dev/null
+++ b/indra/newview/llmanipscale.cpp
@@ -0,0 +1,2024 @@
+/**
+ * @file llmanipscale.cpp
+ * @brief LLManipScale class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmanipscale.h"
+
+// library includes
+#include "llmath.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "llprimitive.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llglheaders.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llhudrender.h"
+#include "llworld.h"
+#include "v2math.h"
+#include "llvoavatar.h"
+
+
+const F32 MAX_MANIP_SELECT_DISTANCE_SQUARED = 11.f * 11.f;
+const F32 SNAP_GUIDE_SCREEN_OFFSET = 0.05f;
+const F32 SNAP_GUIDE_SCREEN_LENGTH = 0.7f;
+const F32 SELECTED_MANIPULATOR_SCALE = 1.2f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+const S32 NUM_MANIPULATORS = 14;
+
+const LLManip::EManipPart MANIPULATOR_IDS[NUM_MANIPULATORS] =
+{
+ LLManip::LL_CORNER_NNN,
+ LLManip::LL_CORNER_NNP,
+ LLManip::LL_CORNER_NPN,
+ LLManip::LL_CORNER_NPP,
+ LLManip::LL_CORNER_PNN,
+ LLManip::LL_CORNER_PNP,
+ LLManip::LL_CORNER_PPN,
+ LLManip::LL_CORNER_PPP,
+ LLManip::LL_FACE_POSZ,
+ LLManip::LL_FACE_POSX,
+ LLManip::LL_FACE_POSY,
+ LLManip::LL_FACE_NEGX,
+ LLManip::LL_FACE_NEGY,
+ LLManip::LL_FACE_NEGZ
+};
+
+
+
+// static
+void LLManipScale::setUniform(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleUniform", b);
+}
+
+// static
+void LLManipScale::setShowAxes(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleShowAxes", b);
+}
+
+// static
+void LLManipScale::setStretchTextures(BOOL b)
+{
+ gSavedSettings.setBOOL("ScaleStretchTextures", b);
+}
+
+// static
+BOOL LLManipScale::getUniform()
+{
+ return gSavedSettings.getBOOL("ScaleUniform");
+}
+
+// static
+BOOL LLManipScale::getShowAxes()
+{
+ return gSavedSettings.getBOOL("ScaleShowAxes");
+}
+
+// static
+BOOL LLManipScale::getStretchTextures()
+{
+ return gSavedSettings.getBOOL("ScaleStretchTextures");
+}
+
+inline void LLManipScale::conditionalHighlight( U32 part, const LLColor4* highlight, const LLColor4* normal )
+{
+ LLColor4 default_highlight( 1.f, 1.f, 1.f, 1.f );
+ LLColor4 default_normal( 0.7f, 0.7f, 0.7f, 0.6f );
+ LLColor4 invisible(0.f, 0.f, 0.f, 0.f);
+ F32 manipulator_scale = 1.f;
+
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ if((U32)MANIPULATOR_IDS[i] == part)
+ {
+ manipulator_scale = mManipulatorScales[i];
+ break;
+ }
+ }
+
+ mScaledBoxHandleSize = mBoxHandleSize * manipulator_scale;
+ if (mManipPart != (S32)LL_NO_PART && mManipPart != (S32)part)
+ {
+ glColor4fv( invisible.mV );
+ }
+ else if( mHighlightedPart == (S32)part )
+ {
+ glColor4fv( highlight ? highlight->mV : default_highlight.mV );
+ }
+ else
+ {
+ glColor4fv( normal ? normal->mV : default_normal.mV );
+ }
+}
+
+void LLManipScale::handleSelect()
+{
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ updateSnapGuides(bbox);
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Click and drag to stretch selected side");
+}
+
+void LLManipScale::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+ gFloaterTools->setStatusText("");
+}
+
+BOOL sort_manip_by_z(LLManipScale::ManipulatorHandle *new_manip, LLManipScale::ManipulatorHandle *test_manip)
+{
+ return ((new_manip->mType < test_manip->mType) || (new_manip->mPosition.mV[VZ] < test_manip->mPosition.mV[VZ]));
+}
+
+LLManipScale::LLManipScale( LLToolComposite* composite )
+ :
+ LLManip( "Scale", composite ),
+ mBoxHandleSize( 1.f ),
+ mScaledBoxHandleSize( 1.f ),
+ mManipPart( LL_NO_PART ),
+ mHighlightedPart( LL_NO_PART ),
+ mLastMouseX( -1 ),
+ mLastMouseY( -1 ),
+ mSendUpdateOnMouseUp( FALSE ),
+ mLastUpdateFlags( 0 ),
+ mScaleSnapUnit1(1.f),
+ mScaleSnapUnit2(1.f),
+ mSnapRegimeOffset(0.f),
+ mSnapGuideLength(0.f),
+ mScaleSnapValue(0.f)
+{
+ mProjectedManipulators.setInsertBefore(sort_manip_by_z);
+ mManipulatorScales = new F32[NUM_MANIPULATORS];
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ mManipulatorScales[i] = 1.f;
+ }
+}
+
+LLManipScale::~LLManipScale()
+{
+ delete []mManipulatorScales;
+}
+
+void LLManipScale::render()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLEnable gl_blend(GL_BLEND);
+ LLGLEnable gls_alpha_test(GL_ALPHA_TEST);
+
+ if( isSelectionScalable() )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Calculate size of drag handles
+
+ const F32 BOX_HANDLE_BASE_SIZE = 50.0f; // box size in pixels = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR
+ const F32 BOX_HANDLE_BASE_FACTOR = 0.2f;
+
+ LLVector3 center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
+
+ F32 range;
+ F32 range_from_agent;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mBoxHandleSize = BOX_HANDLE_BASE_SIZE * BOX_HANDLE_BASE_FACTOR / (F32) gCamera->getViewHeightInPixels();
+ mBoxHandleSize /= gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ range = dist_vec(gAgent.getCameraPositionAgent(), center_agent);
+ range_from_agent = dist_vec(gAgent.getPositionAgent(), center_agent);
+
+ // Don't draw manip if object too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ F32 max_select_distance = gSavedSettings.getF32("MaxSelectDistance");
+ if (range_from_agent > max_select_distance)
+ {
+ return;
+ }
+ }
+
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = BOX_HANDLE_BASE_SIZE / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mBoxHandleSize = range * tan(apparent_angle) * BOX_HANDLE_BASE_FACTOR;
+ }
+ else
+ {
+ // range == zero
+ mBoxHandleSize = BOX_HANDLE_BASE_FACTOR;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////
+ // Draw bounding box
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLVector3 pos_agent = bbox.getPositionAgent();
+ LLQuaternion rot = bbox.getRotation();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ {
+ glTranslatef(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+ rot.getAngleAxis(&angle_radians, &x, &y, &z);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+
+ {
+ LLGLEnable poly_offset(GL_POLYGON_OFFSET_FILL);
+ glPolygonOffset( -2.f, -2.f);
+
+ // JC - Band-aid until edge stretch working similar to side stretch
+ // in non-uniform.
+ // renderEdges( bbox );
+
+ renderCorners( bbox );
+ renderFaces( bbox );
+
+ if (mManipPart != LL_NO_PART)
+ {
+ renderGuidelinesPart( bbox );
+ }
+
+ glPolygonOffset( 0.f, 0.f);
+ }
+ }
+ glPopMatrix();
+
+ if (mManipPart != LL_NO_PART)
+ {
+ renderSnapGuides(bbox);
+ }
+ glPopMatrix();
+
+ renderXYZ(bbox.getExtentLocal());
+ }
+}
+
+BOOL LLManipScale::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj ||
+ (mHighlightedPart != LL_NO_PART) )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+
+ return handled;
+}
+
+// Assumes that one of the arrows on an object was hit.
+BOOL LLManipScale::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_scale = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_scale = can_scale && objectp->permModify() && objectp->permMove() && !objectp->isSeat();
+ }
+
+ if (!can_scale)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+
+ gSelectMgr->enableSilhouette(FALSE);
+ mManipPart = (EManipPart)hit_part;
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLVector3 box_center_agent = bbox.getCenterAgent();
+ LLVector3 box_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ) );
+
+ updateSnapGuides(bbox);
+
+ mDragStartPointGlobal = gAgent.getPosGlobalFromAgent(box_corner_agent);
+ mDragStartCenterGlobal = gAgent.getPosGlobalFromAgent(box_center_agent);
+ LLVector3 far_corner_agent = bbox.localToAgent( unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ) );
+ mDragFarHitGlobal = gAgent.getPosGlobalFromAgent(far_corner_agent);
+ mDragPointGlobal = mDragStartPointGlobal;
+
+ // we just started a drag, so save initial object positions, orientations, and scales
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+
+ mHelpTextTimer.reset();
+ sNumTimesHelpTextShown++;
+ return TRUE;
+}
+
+
+BOOL LLManipScale::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ gSelectMgr->enableSilhouette(TRUE);
+ mManipPart = LL_NO_PART;
+
+ // Might have missed last update due to UPDATE_DELAY timing
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( mLastUpdateFlags );
+ }
+
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLManipScale::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if( gSelectMgr->isEmpty() )
+ {
+ // Somehow the object got deselected while we were dragging it.
+ setMouseCapture( FALSE );
+ }
+ else
+ {
+ drag( x, y );
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (active)" << llendl;
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ // not dragging...
+ highlightManipulators(x, y);
+ }
+
+ // Patch up textures, if possible.
+ gSelectMgr->adjustTexturesByScale(TRUE, getStretchTextures());
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLSCALE);
+ return TRUE;
+}
+
+void LLManipScale::highlightManipulators(S32 x, S32 y)
+{
+ mHighlightedPart = LL_NO_PART;
+
+ // If we have something selected, try to hit its manipulator handles.
+ // Don't do this with nothing selected, as it kills the framerate.
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ if( isSelectionScalable() )
+ {
+ LLMatrix4 transform;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ LLVector4 translation(bbox.getPositionAgent());
+ transform.initRotTrans(bbox.getRotation(), translation);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ transform *= cfr;
+ LLMatrix4 window_scale;
+ F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+ window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
+ LLQuaternion::DEFAULT,
+ LLVector3::zero);
+ transform *= window_scale;
+ }
+ else
+ {
+ LLMatrix4 projMatrix = gCamera->getProjection();
+ LLMatrix4 modelView = gCamera->getModelview();
+ transform.initAll(LLVector3(1.f, 1.f, 1.f), bbox.getRotation(), bbox.getPositionAgent());
+
+ transform *= modelView;
+ transform *= projMatrix;
+ }
+
+ LLVector3 min = bbox.getMinLocal();
+ LLVector3 max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ mProjectedManipulators.deleteAllData();
+
+ S32 numManips = 0;
+ // corners
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], min.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], min.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], max.mV[VY], max.mV[VZ], 1.f);
+
+ // 1-D highlights are applicable iff one object is selected
+ if( gSelectMgr->getObjectCount() == 1 )
+ {
+ // face centers
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], max.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(max.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], max.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(min.mV[VX], ctr.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], min.mV[VY], ctr.mV[VZ], 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(ctr.mV[VX], ctr.mV[VY], min.mV[VZ], 1.f);
+ }
+
+ for (S32 i = 0; i < numManips; i++)
+ {
+ LLVector4 projectedVertex = mManipulatorVertices[i] * transform;
+ projectedVertex = projectedVertex * (1.f / projectedVertex.mV[VW]);
+
+ ManipulatorHandle* projManipulator = new ManipulatorHandle(LLVector3(projectedVertex.mV[VX], projectedVertex.mV[VY],
+ projectedVertex.mV[VZ]), MANIPULATOR_IDS[i], (i < 7) ? SCALE_MANIP_CORNER : SCALE_MANIP_FACE);
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ F32 half_width = (F32)gViewerWindow->getWindowWidth() / 2.f;
+ F32 half_height = (F32)gViewerWindow->getWindowHeight() / 2.f;
+ LLVector2 manip2d;
+ LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
+ LLVector2 delta;
+
+ mHighlightedPart = LL_NO_PART;
+
+ for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
+ manipulator;
+ manipulator = mProjectedManipulators.getNextData())
+ {
+ manip2d.setVec(manipulator->mPosition.mV[VX] * half_width, manipulator->mPosition.mV[VY] * half_height);
+
+ delta = manip2d - mousePos;
+ if (delta.magVecSquared() < MAX_MANIP_SELECT_DISTANCE_SQUARED)
+ {
+ mHighlightedPart = manipulator->mManipID;
+
+ //llinfos << "Tried: " << mHighlightedPart << llendl;
+ break;
+ }
+ }
+ }
+
+ for (S32 i = 0; i < NUM_MANIPULATORS; i++)
+ {
+ if (mHighlightedPart == MANIPULATOR_IDS[i])
+ {
+ mManipulatorScales[i] = lerp(mManipulatorScales[i], SELECTED_MANIPULATOR_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ else
+ {
+ mManipulatorScales[i] = lerp(mManipulatorScales[i], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE));
+ }
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipScale (inactive)" << llendl;
+}
+
+
+void LLManipScale::renderFaces( const LLBBox& bbox )
+{
+ // Don't bother to render the drag handles for 1-D scaling if
+ // more than one object is selected or if it is an attachment
+ if ( gSelectMgr->getObjectCount() > 1 )
+ {
+ return;
+ }
+
+ // This is a flattened representation of the box as render here
+ // .
+ // (+++) (++-) /|\t
+ // +------------+ | (texture coordinates)
+ // | | |
+ // | 1 | (*) --->s
+ // | +X |
+ // | |
+ // (+++) (+-+)| |(+--) (++-) (+++)
+ // +------------+------------+------------+------------+
+ // |0 3|3 7|7 4|4 0|
+ // | 0 | 4 | 5 | 2 |
+ // | +Z | -Y | -Z | +Y |
+ // | | | | |
+ // |1 2|2 6|6 5|5 1|
+ // +------------+------------+------------+------------+
+ // (-++) (--+)| |(---) (-+-) (-++)
+ // | 3 |
+ // | -X |
+ // | |
+ // | |
+ // +------------+
+ // (-++) (-+-)
+
+ LLColor4 highlight_color( 1.f, 1.f, 1.f, 0.5f);
+ LLColor4 normal_color( 1.f, 1.f, 1.f, 0.3f);
+
+ LLColor4 x_highlight_color( 1.f, 0.2f, 0.2f, 1.0f);
+ LLColor4 x_normal_color( 0.6f, 0.f, 0.f, 0.4f);
+
+ LLColor4 y_highlight_color( 0.2f, 1.f, 0.2f, 1.0f);
+ LLColor4 y_normal_color( 0.f, 0.6f, 0.f, 0.4f);
+
+ LLColor4 z_highlight_color( 0.2f, 0.2f, 1.f, 1.0f);
+ LLColor4 z_normal_color( 0.f, 0.f, 0.6f, 0.4f);
+
+ LLColor4 default_normal_color( 0.7f, 0.7f, 0.7f, 0.15f );
+
+ const LLVector3& min = bbox.getMinLocal();
+ const LLVector3& max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ if (mManipPart == LL_NO_PART)
+ {
+ glColor4fv( default_normal_color.mV );
+ glBegin(GL_QUADS);
+ {
+ // Face 0
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+
+ // Face 1
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+
+ // Face 2
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+
+ // Face 3
+ glVertex3f(min.mV[VX], max.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+
+ // Face 4
+ glVertex3f(min.mV[VX], min.mV[VY], max.mV[VZ]);
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], max.mV[VZ]);
+
+ // Face 5
+ glVertex3f(min.mV[VX], min.mV[VY], min.mV[VZ]);
+ glVertex3f(min.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], max.mV[VY], min.mV[VZ]);
+ glVertex3f(max.mV[VX], min.mV[VY], min.mV[VZ]);
+ }
+ glEnd();
+ }
+
+ // Find nearest vertex
+ LLVector3 orientWRTHead = bbox.agentToLocalBasis( bbox.getCenterAgent() - gAgent.getCameraPositionAgent() );
+ U32 nearest =
+ (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
+ (orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
+ (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
+
+ // opposite faces on Linden cubes:
+ // 0 & 5
+ // 1 & 3
+ // 2 & 4
+
+ // Table of order to draw faces, based on nearest vertex
+ static U32 face_list[8][6] = {
+ { 2,0,1, 4,5,3 }, // v6 F201 F453
+ { 2,0,3, 4,5,1 }, // v7 F203 F451
+ { 4,0,1, 2,5,3 }, // v5 F401 F253
+ { 4,0,3, 2,5,1 }, // v4 F403 F251
+ { 2,5,1, 4,0,3 }, // v2 F251 F403
+ { 2,5,3, 4,0,1 }, // v3 F253 F401
+ { 4,5,1, 2,0,3 }, // v1 F451 F203
+ { 4,5,3, 2,0,1 } // v0 F453 F201
+ };
+
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ for (S32 i = 0; i < 6; i++)
+ {
+ U32 face = face_list[nearest][i];
+ switch( face )
+ {
+ case 0:
+ conditionalHighlight( LL_FACE_POSZ, &z_highlight_color, &z_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], max.mV[VZ] ) );
+ break;
+
+ case 1:
+ conditionalHighlight( LL_FACE_POSX, &x_highlight_color, &x_normal_color );
+ renderAxisHandle( ctr, LLVector3( max.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 2:
+ conditionalHighlight( LL_FACE_POSY, &y_highlight_color, &y_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], max.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 3:
+ conditionalHighlight( LL_FACE_NEGX, &x_highlight_color, &x_normal_color );
+ renderAxisHandle( ctr, LLVector3( min.mV[VX], ctr.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 4:
+ conditionalHighlight( LL_FACE_NEGY, &y_highlight_color, &y_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], min.mV[VY], ctr.mV[VZ] ) );
+ break;
+
+ case 5:
+ conditionalHighlight( LL_FACE_NEGZ, &z_highlight_color, &z_normal_color );
+ renderAxisHandle( ctr, LLVector3( ctr.mV[VX], ctr.mV[VY], min.mV[VZ] ) );
+ break;
+ }
+ }
+ }
+}
+
+void LLManipScale::renderEdges( const LLBBox& bbox )
+{
+ LLVector3 extent = bbox.getExtentLocal();
+ F32 edge_width = mBoxHandleSize * .6f;
+
+ for( U32 part = LL_EDGE_MIN; part <= LL_EDGE_MAX; part++ )
+ {
+ LLVector3 direction = edgeToUnitVector( part );
+ LLVector3 center_to_edge = unitVectorToLocalBBoxExtent( direction, bbox );
+
+ glPushMatrix();
+ {
+ glTranslatef( center_to_edge.mV[0], center_to_edge.mV[1], center_to_edge.mV[2] );
+ conditionalHighlight( part );
+ glScalef(
+ direction.mV[0] ? edge_width : extent.mV[VX],
+ direction.mV[1] ? edge_width : extent.mV[VY],
+ direction.mV[2] ? edge_width : extent.mV[VZ] );
+ gBox.render();
+ }
+ glPopMatrix();
+ }
+}
+
+
+void LLManipScale::renderCorners( const LLBBox& bbox )
+{
+ U32 part = LL_CORNER_NNN;
+
+ F32 x_offset = bbox.getMinLocal().mV[VX];
+ for( S32 i=0; i < 2; i++ )
+ {
+ F32 y_offset = bbox.getMinLocal().mV[VY];
+ for( S32 j=0; j < 2; j++ )
+ {
+ F32 z_offset = bbox.getMinLocal().mV[VZ];
+ for( S32 k=0; k < 2; k++ )
+ {
+ conditionalHighlight( part );
+ renderBoxHandle( x_offset, y_offset, z_offset );
+ part++;
+
+ z_offset = bbox.getMaxLocal().mV[VZ];
+
+ }
+ y_offset = bbox.getMaxLocal().mV[VY];
+ }
+ x_offset = bbox.getMaxLocal().mV[VX];
+ }
+}
+
+
+void LLManipScale::renderBoxHandle( F32 x, F32 y, F32 z )
+{
+ LLGLDisable gls_tex(GL_TEXTURE_2D);
+
+ glPushMatrix();
+ {
+ glTranslatef( x, y, z );
+ glScalef( mScaledBoxHandleSize, mScaledBoxHandleSize, mScaledBoxHandleSize );
+ gBox.render();
+ }
+ glPopMatrix();
+}
+
+
+void LLManipScale::renderAxisHandle( const LLVector3& start, const LLVector3& end )
+{
+ if( getShowAxes() )
+ {
+ // Draws a single "jacks" style handle: a long, retangular box from start to end.
+ LLVector3 offset_start = end - start;
+ offset_start.normVec();
+ offset_start = start + mBoxHandleSize * offset_start;
+
+ LLVector3 delta = end - offset_start;
+ LLVector3 pos = offset_start + 0.5f * delta;
+
+ glPushMatrix();
+ {
+ glTranslatef( pos.mV[VX], pos.mV[VY], pos.mV[VZ] );
+ glScalef(
+ mBoxHandleSize + llabs(delta.mV[VX]),
+ mBoxHandleSize + llabs(delta.mV[VY]),
+ mBoxHandleSize + llabs(delta.mV[VZ]));
+ gBox.render();
+ }
+ glPopMatrix();
+ }
+ else
+ {
+ renderBoxHandle( end.mV[VX], end.mV[VY], end.mV[VZ] );
+ }
+}
+
+
+void LLManipScale::drag( S32 x, S32 y )
+{
+ if( (LL_FACE_MIN <= (S32)mManipPart)
+ && ((S32)mManipPart <= LL_FACE_MAX) )
+ {
+ dragFace( x, y );
+ }
+ else
+ if( (LL_CORNER_MIN <= (S32)mManipPart)
+ && ((S32)mManipPart <= LL_CORNER_MAX) )
+ {
+ dragCorner( x, y );
+ }
+
+ //gAgent.setObjectTracking(FALSE);
+ gAgent.clearFocusObject();
+}
+
+// Scale around the
+void LLManipScale::dragCorner( S32 x, S32 y )
+{
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ // Suppress scale if mouse hasn't moved.
+ if (x == mLastMouseX && y == mLastMouseY)
+ {
+ if (mSendUpdateOnMouseUp)
+ {
+ sendUpdates(TRUE,TRUE,TRUE);
+ }
+ return;
+ }
+
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ LLVector3d drag_start_point_global = mDragStartPointGlobal;
+ LLVector3d drag_start_center_global = mDragStartCenterGlobal;
+ LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
+
+ LLVector3d drag_start_dir_d;
+ drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
+ LLVector3 drag_start_dir_f;
+ drag_start_dir_f.setVec(drag_start_dir_d);
+
+ F32 s = 0;
+ F32 t = 0;
+
+ nearestPointOnLineFromMouse(x, y,
+ drag_start_center_agent,
+ drag_start_point_agent,
+ s, t );
+
+ F32 drag_start_dist = dist_vec(drag_start_point_agent, drag_start_center_agent);
+
+ if( s <= 0 ) // we only care about intersections in front of the camera
+ {
+ return;
+ }
+
+ LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
+
+ F32 scale_factor = t;
+
+ BOOL uniform = LLManipScale::getUniform();
+
+ if( !uniform )
+ {
+ scale_factor = 0.5f + (scale_factor * 0.5f);
+ }
+
+ // check for snapping
+ LLVector3 drag_center_agent = gAgent.getPosAgentFromGlobal(drag_point_global);
+ LLVector3 mouse_on_plane1;
+ getMousePointOnPlaneAgent(mouse_on_plane1, x, y, drag_center_agent, mScalePlaneNormal1);
+ LLVector3 mouse_on_plane2;
+ getMousePointOnPlaneAgent(mouse_on_plane2, x, y, drag_center_agent, mScalePlaneNormal2);
+ LLVector3 mouse_dir_1 = mouse_on_plane1 - mScaleCenter;
+ LLVector3 mouse_dir_2 = mouse_on_plane2 - mScaleCenter;
+ LLVector3 mouse_to_scale_line_1 = mouse_dir_1 - projected_vec(mouse_dir_1, mScaleDir);
+ LLVector3 mouse_to_scale_line_2 = mouse_dir_2 - projected_vec(mouse_dir_2, mScaleDir);
+ LLVector3 mouse_to_scale_line_dir_1 = mouse_to_scale_line_1;
+ mouse_to_scale_line_dir_1.normVec();
+ if (mouse_to_scale_line_dir_1 * mSnapGuideDir1 < 0.f)
+ {
+ // need to keep sign of mouse offset wrt to snap guide direction
+ mouse_to_scale_line_dir_1 *= -1.f;
+ }
+ LLVector3 mouse_to_scale_line_dir_2 = mouse_to_scale_line_2;
+ mouse_to_scale_line_dir_2.normVec();
+ if (mouse_to_scale_line_dir_2 * mSnapGuideDir2 < 0.f)
+ {
+ // need to keep sign of mouse offset wrt to snap guide direction
+ mouse_to_scale_line_dir_2 *= -1.f;
+ }
+
+ F32 snap_dir_dot_mouse_offset1 = mSnapGuideDir1 * mouse_to_scale_line_dir_1;
+ F32 snap_dir_dot_mouse_offset2 = mSnapGuideDir2 * mouse_to_scale_line_dir_2;
+
+ F32 dist_from_scale_line_1 = mouse_to_scale_line_1 * mouse_to_scale_line_dir_1;
+ F32 dist_from_scale_line_2 = mouse_to_scale_line_2 * mouse_to_scale_line_dir_2;
+
+ F32 max_scale = partToMaxScale(mManipPart, bbox);
+ F32 min_scale = partToMinScale(mManipPart, bbox);
+
+ BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
+ if (snap_enabled && dist_from_scale_line_1 > mSnapRegimeOffset * snap_dir_dot_mouse_offset1)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 projected_drag_pos = mouse_on_plane1 - (dist_from_scale_line_1 / snap_dir_dot_mouse_offset1) * mSnapGuideDir1;
+ F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
+
+ mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
+
+ scale_factor = mScaleSnapValue / drag_start_dist;
+ if( !uniform )
+ {
+ scale_factor *= 0.5f;
+ }
+ }
+ else if (snap_enabled && dist_from_scale_line_2 > mSnapRegimeOffset * snap_dir_dot_mouse_offset2)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 projected_drag_pos = mouse_on_plane2 - (dist_from_scale_line_2 / snap_dir_dot_mouse_offset2) * mSnapGuideDir2;
+ F32 drag_dist = (projected_drag_pos - mScaleCenter) * mScaleDir;
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(projected_drag_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit2 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit2 / cur_subdivisions);
+
+ mScaleSnapValue = llclamp((drag_dist - (relative_snap_dist - snap_dist)), min_scale, max_scale);
+
+ scale_factor = mScaleSnapValue / drag_start_dist;
+ if( !uniform )
+ {
+ scale_factor *= 0.5f;
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ F32 max_scale_factor = MAX_OBJECT_SCALE / MIN_OBJECT_SCALE;
+ F32 min_scale_factor = MIN_OBJECT_SCALE / MAX_OBJECT_SCALE;
+
+ // find max and min scale factors that will make biggest object hit max absolute scale and smallest object hit min absolute scale
+ LLSelectNode* selectNode;
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject* cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+
+ F32 cur_max_scale_factor = llmin( MAX_OBJECT_SCALE / scale.mV[VX], MAX_OBJECT_SCALE / scale.mV[VY], MAX_OBJECT_SCALE / scale.mV[VZ] );
+ max_scale_factor = llmin( max_scale_factor, cur_max_scale_factor );
+
+ F32 cur_min_scale_factor = llmax( MIN_OBJECT_SCALE / scale.mV[VX], MIN_OBJECT_SCALE / scale.mV[VY], MIN_OBJECT_SCALE / scale.mV[VZ] );
+ min_scale_factor = llmax( min_scale_factor, cur_min_scale_factor );
+ }
+ }
+
+ scale_factor = llclamp( scale_factor, min_scale_factor, max_scale_factor );
+
+ LLVector3d drag_global = uniform ? mDragStartCenterGlobal : mDragFarHitGlobal;
+
+ // do the root objects i.e. (TRUE == cur->isRootEdit())
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject* cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() && cur->isRootEdit() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+ cur->setScale( scale_factor * scale );
+
+ LLVector3 delta_pos;
+ LLVector3 original_pos = cur->getPositionEdit();
+ LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
+ if (!cur->isAttachment())
+ {
+ new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, new_pos_global);
+ }
+ cur->setPositionAbsoluteGlobal( new_pos_global );
+ rebuild(cur);
+
+ delta_pos = cur->getPositionEdit() - original_pos;
+
+ if (selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = cur->mChildList[child_num];
+
+ if (cur->isAttachment())
+ {
+ LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
+ childp->setPosition(child_pos);
+ }
+ else
+ {
+ LLVector3d child_pos_delta(delta_pos);
+ // RN: this updates drawable position instantly
+ childp->setPositionAbsoluteGlobal(childp->getPositionGlobal() - child_pos_delta);
+ }
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ // do the child objects i.e. (FALSE == cur->isRootEdit())
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject*cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() && !cur->isRootEdit() )
+ {
+ const LLVector3& scale = selectNode->mSavedScale;
+ cur->setScale( scale_factor * scale, FALSE );
+
+ if (!selectNode->mIndividualSelection)
+ {
+ cur->setPosition(selectNode->mSavedPositionLocal * scale_factor);
+ continue;
+ }
+
+ LLVector3d new_pos_global = drag_global + (selectNode->mSavedPositionGlobal - drag_global) * scale_factor;
+ cur->setPositionAbsoluteGlobal( new_pos_global );
+ rebuild(cur);
+ }
+ }
+
+
+
+ mDragPointGlobal = drag_point_global;
+
+ sendUpdates( TRUE, TRUE, TRUE );
+}
+
+
+void LLManipScale::dragFace( S32 x, S32 y )
+{
+ // Suppress scale if mouse hasn't moved.
+ if (x == mLastMouseX && y == mLastMouseY)
+ {
+ if (mSendUpdateOnMouseUp)
+ {
+ sendUpdates(TRUE,TRUE,FALSE);
+ }
+ return;
+ }
+
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ LLVector3d drag_start_point_global = mDragStartPointGlobal;
+ LLVector3d drag_start_center_global = mDragStartCenterGlobal;
+ LLVector3 drag_start_point_agent = gAgent.getPosAgentFromGlobal(drag_start_point_global);
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(drag_start_center_global);
+
+ LLVector3d drag_start_dir_d;
+ drag_start_dir_d.setVec(drag_start_point_global - drag_start_center_global);
+ LLVector3 drag_start_dir_f;
+ drag_start_dir_f.setVec(drag_start_dir_d);
+
+ LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+
+ F32 s = 0;
+ F32 t = 0;
+
+ nearestPointOnLineFromMouse(x,
+ y,
+ drag_start_center_agent,
+ drag_start_point_agent,
+ s, t );
+
+ if( s <= 0 ) // we only care about intersections in front of the camera
+ {
+ return;
+ }
+
+ LLVector3d drag_point_global = drag_start_center_global + t * drag_start_dir_d;
+ LLVector3 part_dir_local = partToUnitVector( mManipPart );
+
+ // check for snapping
+ LLVector3 mouse_on_plane;
+ getMousePointOnPlaneAgent(mouse_on_plane, x, y, mScaleCenter, mScalePlaneNormal1 );
+
+ LLVector3 mouse_on_scale_line = mScaleCenter + projected_vec(mouse_on_plane - mScaleCenter, mScaleDir);
+ LLVector3 drag_delta(mouse_on_scale_line - drag_start_point_agent);
+ F32 max_drag_dist = partToMaxScale(mManipPart, bbox);
+ F32 min_drag_dist = partToMinScale(mManipPart, bbox);
+
+ BOOL uniform = LLManipScale::getUniform();
+ if( uniform )
+ {
+ drag_delta *= 2.f;
+ }
+
+ LLVector3 scale_center_to_mouse = mouse_on_plane - mScaleCenter;
+ F32 dist_from_scale_line = dist_vec(scale_center_to_mouse, (mouse_on_scale_line - mScaleCenter));
+ F32 dist_along_scale_line = scale_center_to_mouse * mScaleDir;
+
+ BOOL snap_enabled = gSavedSettings.getBOOL("SnapEnabled");
+
+ if (snap_enabled && dist_from_scale_line > mSnapRegimeOffset)
+ {
+ mInSnapRegime = TRUE;
+
+ if (dist_along_scale_line > max_drag_dist)
+ {
+ mScaleSnapValue = max_drag_dist;
+
+ LLVector3 clamp_point = mScaleCenter + max_drag_dist * mScaleDir;
+ drag_delta.setVec(clamp_point - drag_start_point_agent);
+ }
+ else if (dist_along_scale_line < min_drag_dist)
+ {
+ mScaleSnapValue = min_drag_dist;
+
+ LLVector3 clamp_point = mScaleCenter + min_drag_dist * mScaleDir;
+ drag_delta.setVec(clamp_point - drag_start_point_agent);
+ }
+ else
+ {
+ F32 drag_dist = scale_center_to_mouse * mScaleDir;
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(mScaleCenter + mScaleDir * drag_dist, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+ F32 snap_dist = mScaleSnapUnit1 / (2.f * cur_subdivisions);
+ F32 relative_snap_dist = fmodf(drag_dist + snap_dist, mScaleSnapUnit1 / cur_subdivisions);
+ relative_snap_dist -= snap_dist;
+
+ //make sure that values that the scale is "snapped to"
+ //do not exceed/go under the applicable max/mins
+ //this causes the box to shift displacements ever so slightly
+ //although the "snap value" should go down to 0
+ //see Jira 1027
+ relative_snap_dist = llclamp(relative_snap_dist,
+ drag_dist - max_drag_dist,
+ drag_dist - min_drag_dist);
+
+ mScaleSnapValue = drag_dist - relative_snap_dist;
+
+ if (llabs(relative_snap_dist) < snap_dist)
+ {
+ LLVector3 drag_correction = relative_snap_dist * mScaleDir;
+ if (uniform)
+ {
+ drag_correction *= 2.f;
+ }
+
+ drag_delta -= drag_correction;
+ }
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ BOOL send_scale_update = FALSE;
+ BOOL send_position_update = FALSE;
+
+ LLVector3 dir_agent;
+ if( part_dir_local.mV[VX] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::x_axis );
+ }
+ else if( part_dir_local.mV[VY] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::y_axis );
+ }
+ else if( part_dir_local.mV[VZ] )
+ {
+ dir_agent = bbox.localToAgentBasis( LLVector3::z_axis );
+ }
+ stretchFace(
+ projected_vec(drag_start_dir_f, dir_agent) + drag_start_center_agent,
+ projected_vec(drag_delta, dir_agent));
+ send_position_update = TRUE;
+ send_scale_update = TRUE;
+
+ mDragPointGlobal = drag_point_global;
+
+ sendUpdates( send_position_update, send_scale_update );
+}
+
+void LLManipScale::sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner )
+{
+ // Throttle updates to 10 per second.
+ static LLTimer update_timer;
+ F32 elapsed_time = update_timer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+
+ if( send_scale_update || send_position_update )
+ {
+ U32 update_flags = UPD_NONE;
+ if (send_position_update) update_flags |= UPD_POSITION;
+ if (send_scale_update) update_flags |= UPD_SCALE;
+
+// BOOL send_type = SEND_INDIVIDUALS;
+ if (corner)
+ {
+ update_flags |= UPD_UNIFORM;
+ }
+ // keep this up to date for sendonmouseup
+ mLastUpdateFlags = update_flags;
+
+ // enforce minimum update delay and don't stream updates on sub-object selections
+ if( elapsed_time > UPDATE_DELAY && gSavedSettings.getBOOL("SelectLinkedSet") )
+ {
+ gSelectMgr->sendMultipleUpdate( update_flags );
+ update_timer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ dialog_refresh_all();
+ }
+}
+
+// Rescales in a single dimension. Either uniform (standard) or one-sided (scale plus translation)
+// depending on mUniform. Handles multiple selection and objects that are not aligned to the bounding box.
+void LLManipScale::stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent )
+{
+ LLVector3 drag_start_center_agent = gAgent.getPosAgentFromGlobal(mDragStartCenterGlobal);
+
+ LLSelectNode *selectNode;
+ for( selectNode = gSelectMgr->getFirstNode(); selectNode; selectNode = gSelectMgr->getNextNode() )
+ {
+ LLViewerObject*cur = selectNode->getObject();
+ if( cur->permModify() && cur->permMove() && !cur->isAvatar() )
+ {
+ LLBBox cur_bbox = cur->getBoundingBoxAgent();
+ LLVector3 start_local = cur_bbox.agentToLocal( drag_start_agent );
+ LLVector3 end_local = cur_bbox.agentToLocal( drag_start_agent + drag_delta_agent);
+ LLVector3 start_center_local = cur_bbox.agentToLocal( drag_start_center_agent );
+ LLVector3 axis = nearestAxis( start_local - start_center_local );
+ S32 axis_index = axis.mV[0] ? 0 : (axis.mV[1] ? 1 : 2 );
+
+ LLVector3 delta_local = end_local - start_local;
+ F32 delta_local_mag = delta_local.magVec();
+ LLVector3 dir_local;
+ if (delta_local_mag == 0.f)
+ {
+ dir_local = axis;
+ }
+ else
+ {
+ dir_local = delta_local / delta_local_mag; // normalized delta_local
+ }
+
+ F32 denom = axis * dir_local;
+ F32 desired_delta_size = is_approx_zero(denom) ? 0.f : (delta_local_mag / denom); // in meters
+ F32 desired_scale = llclamp(selectNode->mSavedScale.mV[axis_index] + desired_delta_size, MIN_OBJECT_SCALE, MAX_OBJECT_SCALE);
+ // propagate scale constraint back to position offset
+ desired_delta_size = desired_scale - selectNode->mSavedScale.mV[axis_index]; // propagate constraint back to position
+
+ LLVector3 scale = cur->getScale();
+ scale.mV[axis_index] = desired_scale;
+ cur->setScale(scale, FALSE);
+ rebuild(cur);
+ LLVector3 delta_pos;
+ if( !getUniform() )
+ {
+ LLVector3 delta_pos_local = axis * (0.5f * desired_delta_size);
+ LLVector3d delta_pos_global;
+ delta_pos_global.setVec(cur_bbox.localToAgent( delta_pos_local ) - cur_bbox.getCenterAgent());
+ LLVector3 cur_pos = cur->getPositionEdit();
+
+ if (cur->isRootEdit() && !cur->isAttachment())
+ {
+ LLVector3d new_pos_global = gWorldp->clipToVisibleRegions(selectNode->mSavedPositionGlobal, selectNode->mSavedPositionGlobal + delta_pos_global);
+ cur->setPositionGlobal( new_pos_global );
+ }
+ else
+ {
+ LLXform* parent_xform = cur->mDrawable->getXform()->getParent();
+ LLVector3 new_pos_local;
+ // this works in attachment point space using world space delta
+ if (parent_xform)
+ {
+ new_pos_local = selectNode->mSavedPositionLocal + (LLVector3(delta_pos_global) * ~parent_xform->getWorldRotation());
+ }
+ else
+ {
+ new_pos_local = selectNode->mSavedPositionLocal + LLVector3(delta_pos_global);
+ }
+ cur->setPosition(new_pos_local);
+ }
+ delta_pos = cur->getPositionEdit() - cur_pos;
+ }
+ if (cur->isRootEdit() && selectNode->mIndividualSelection)
+ {
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < cur->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = cur->mChildList[child_num];
+ if (!getUniform())
+ {
+ LLVector3 child_pos = childp->getPosition() - (delta_pos * ~cur->getRotationEdit());
+ childp->setPosition(child_pos);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void LLManipScale::renderGuidelinesPart( const LLBBox& bbox )
+{
+ LLVector3 guideline_start = bbox.getCenterLocal();
+
+ LLVector3 guideline_end = unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox );
+
+ if (!getUniform())
+ {
+ guideline_start = unitVectorToLocalBBoxExtent( -partToUnitVector( mManipPart ), bbox );
+ }
+
+ guideline_end -= guideline_start;
+ guideline_end.normVec();
+ guideline_end *= gWorldPointer->getRegionWidthInMeters();
+ guideline_end += guideline_start;
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.5f) );
+ }
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+ gl_stippled_line_3d( guideline_start, guideline_end, LLColor4(1.f, 1.f, 1.f, 0.25f) );
+ }
+}
+
+void LLManipScale::updateSnapGuides(const LLBBox& bbox)
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 box_corner_agent = bbox.localToAgent(unitVectorToLocalBBoxExtent( partToUnitVector( mManipPart ), bbox ));
+ mScaleCenter = getUniform() ? bbox.getCenterAgent() : bbox.localToAgent(unitVectorToLocalBBoxExtent( -1.f * partToUnitVector( mManipPart ), bbox ));
+ mScaleDir = box_corner_agent - mScaleCenter;
+ mScaleDir.normVec();
+
+ if(gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mSnapRegimeOffset = SNAP_GUIDE_SCREEN_OFFSET / gAgent.getAvatarObject()->mHUDCurZoom;
+
+ }
+ else
+ {
+ F32 object_distance = dist_vec(mScaleCenter, gCamera->getOrigin());
+ mSnapRegimeOffset = (SNAP_GUIDE_SCREEN_OFFSET * gViewerWindow->getWindowWidth() * object_distance) / gCamera->getPixelMeterRatio();
+ }
+ LLVector3 cam_at_axis;
+ F32 snap_guide_length;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ cam_at_axis.setVec(1.f, 0.f, 0.f);
+ snap_guide_length = SNAP_GUIDE_SCREEN_LENGTH / gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ cam_at_axis = gCamera->getAtAxis();
+ F32 manipulator_distance = dist_vec(box_corner_agent, gCamera->getOrigin());
+ snap_guide_length = (SNAP_GUIDE_SCREEN_LENGTH * gViewerWindow->getWindowWidth() * manipulator_distance) / gCamera->getPixelMeterRatio();
+ }
+
+ mSnapGuideLength = snap_guide_length / llmax(0.1f, (llmin(mSnapGuideDir1 * cam_at_axis, mSnapGuideDir2 * cam_at_axis)));
+
+ LLVector3 off_axis_dir = mScaleDir % cam_at_axis;
+ off_axis_dir.normVec();
+
+ if( (LL_FACE_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_FACE_MAX) )
+ {
+ LLVector3 object_scale = bbox.getMaxLocal();
+ object_scale.scaleVec(off_axis_dir * ~bbox.getRotation());
+ object_scale.abs();
+ if (object_scale.mV[VX] > object_scale.mV[VY] && object_scale.mV[VX] > object_scale.mV[VZ])
+ {
+ mSnapGuideDir1 = LLVector3::x_axis * bbox.getRotation();
+ }
+ else if (object_scale.mV[VY] > object_scale.mV[VZ])
+ {
+ mSnapGuideDir1 = LLVector3::y_axis * bbox.getRotation();
+ }
+ else
+ {
+ mSnapGuideDir1 = LLVector3::z_axis * bbox.getRotation();
+ }
+
+ LLVector3 scale_snap = grid_scale;
+ mScaleSnapUnit1 = scale_snap.scaleVec(partToUnitVector( mManipPart )).magVec();
+ mScaleSnapUnit2 = mScaleSnapUnit1;
+ mSnapGuideDir1 *= mSnapGuideDir1 * gCamera->getUpAxis() > 0.f ? 1.f : -1.f;
+ mSnapGuideDir2 = mSnapGuideDir1 * -1.f;
+ mSnapDir1 = mScaleDir;
+ mSnapDir2 = mScaleDir;
+ }
+ else if( (LL_CORNER_MIN <= (S32)mManipPart) && ((S32)mManipPart <= LL_CORNER_MAX) )
+ {
+ LLVector3 local_scale_dir = partToUnitVector( mManipPart );
+ LLVector3 local_camera_dir;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ local_camera_dir = LLVector3(-1.f, 0.f, 0.f) * ~bbox.getRotation();
+ }
+ else
+ {
+ local_camera_dir = (gCamera->getOrigin() - bbox.getCenterAgent()) * ~bbox.getRotation();
+ local_camera_dir.normVec();
+ }
+ local_scale_dir -= projected_vec(local_scale_dir, local_camera_dir);
+ local_scale_dir.normVec();
+ LLVector3 x_axis_proj_camera = LLVector3::x_axis - projected_vec(LLVector3::x_axis, local_camera_dir);
+ x_axis_proj_camera.normVec();
+ LLVector3 y_axis_proj_camera = LLVector3::y_axis - projected_vec(LLVector3::y_axis, local_camera_dir);
+ y_axis_proj_camera.normVec();
+ LLVector3 z_axis_proj_camera = LLVector3::z_axis - projected_vec(LLVector3::z_axis, local_camera_dir);
+ z_axis_proj_camera.normVec();
+ F32 x_axis_proj = llabs(local_scale_dir * x_axis_proj_camera);
+ F32 y_axis_proj = llabs(local_scale_dir * y_axis_proj_camera);
+ F32 z_axis_proj = llabs(local_scale_dir * z_axis_proj_camera);
+
+ if (x_axis_proj > y_axis_proj && x_axis_proj > z_axis_proj)
+ {
+ mSnapGuideDir1 = LLVector3::y_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VY];
+ mSnapGuideDir2 = LLVector3::z_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VZ];
+ }
+ else if (y_axis_proj > z_axis_proj)
+ {
+ mSnapGuideDir1 = LLVector3::x_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VX];
+ mSnapGuideDir2 = LLVector3::z_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VZ];
+ }
+ else
+ {
+ mSnapGuideDir1 = LLVector3::x_axis;
+ mScaleSnapUnit2 = grid_scale.mV[VX];
+ mSnapGuideDir2 = LLVector3::y_axis;
+ mScaleSnapUnit1 = grid_scale.mV[VY];
+ }
+
+ LLVector3 snap_guide_flip(1.f, 1.f, 1.f);
+ switch (mManipPart)
+ {
+ case LL_CORNER_NNN:
+ break;
+ case LL_CORNER_NNP:
+ snap_guide_flip.setVec(1.f, 1.f, -1.f);
+ break;
+ case LL_CORNER_NPN:
+ snap_guide_flip.setVec(1.f, -1.f, 1.f);
+ break;
+ case LL_CORNER_NPP:
+ snap_guide_flip.setVec(1.f, -1.f, -1.f);
+ break;
+ case LL_CORNER_PNN:
+ snap_guide_flip.setVec(-1.f, 1.f, 1.f);
+ break;
+ case LL_CORNER_PNP:
+ snap_guide_flip.setVec(-1.f, 1.f, -1.f);
+ break;
+ case LL_CORNER_PPN:
+ snap_guide_flip.setVec(-1.f, -1.f, 1.f);
+ break;
+ case LL_CORNER_PPP:
+ snap_guide_flip.setVec(-1.f, -1.f, -1.f);
+ break;
+ default:
+ break;
+ }
+ mSnapGuideDir1.scaleVec(snap_guide_flip);
+ mSnapGuideDir2.scaleVec(snap_guide_flip);
+ mSnapGuideDir1.rotVec(bbox.getRotation());
+ mSnapGuideDir2.rotVec(bbox.getRotation());
+ mSnapDir1 = -1.f * mSnapGuideDir2;
+ mSnapDir2 = -1.f * mSnapGuideDir1;
+ }
+
+ mScalePlaneNormal1 = mSnapGuideDir1 % mScaleDir;
+ mScalePlaneNormal1.normVec();
+
+ mScalePlaneNormal2 = mSnapGuideDir2 % mScaleDir;
+ mScalePlaneNormal2.normVec();
+
+ mScaleSnapUnit1 = mScaleSnapUnit1 / (mSnapDir1 * mScaleDir);
+ mScaleSnapUnit2 = mScaleSnapUnit2 / (mSnapDir2 * mScaleDir);
+}
+
+void LLManipScale::renderSnapGuides(const LLBBox& bbox)
+{
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ F32 max_subdivisions = sGridMaxSubdivisionLevel;
+ F32 grid_alpha = gSavedSettings.getF32("GridOpacity");
+
+ F32 max_point_on_scale_line = partToMaxScale(mManipPart, bbox);
+ LLVector3 drag_point = gAgent.getPosAgentFromGlobal(mDragPointGlobal);
+
+ updateGridSettings();
+
+ S32 pass;
+ for (pass = 0; pass < 3; pass++)
+ {
+ LLColor4 tick_color = setupSnapGuideRenderPass(pass);
+
+ glBegin(GL_LINES);
+ LLVector3 line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
+ LLVector3 line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_start.mV);
+ glColor4fv(tick_color.mV);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_end.mV);
+
+ line_mid = mScaleCenter + (mScaleSnapValue * mScaleDir) + (mSnapGuideDir2 * mSnapRegimeOffset);
+ line_start = line_mid - (mScaleDir * (llmin(mScaleSnapValue, mSnapGuideLength * 0.5f)));
+ line_end = line_mid + (mScaleDir * llmin(max_point_on_scale_line - mScaleSnapValue, mSnapGuideLength * 0.5f));
+ glVertex3fv(line_start.mV);
+ glColor4fv(tick_color.mV);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * 0.1f);
+ glVertex3fv(line_end.mV);
+ glEnd();
+ }
+
+ {
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ F32 dist_grid_axis = (drag_point - mScaleCenter) * mScaleDir;
+ // find distance to nearest smallest grid unit
+ F32 grid_offset1 = fmodf(dist_grid_axis, mScaleSnapUnit1 / max_subdivisions);
+ F32 grid_offset2 = fmodf(dist_grid_axis, mScaleSnapUnit2 / max_subdivisions);
+
+ // 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) / (mScaleSnapUnit1 / max_subdivisions));
+ S32 sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 / sGridMinSubdivisionLevel) / (mScaleSnapUnit2 / max_subdivisions));
+
+ S32 num_ticks_per_side1 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit1 / max_subdivisions)));
+ S32 num_ticks_per_side2 = llmax(1, lltrunc(0.5f * mSnapGuideLength / (mScaleSnapUnit2 / max_subdivisions)));
+ F32 dist_scale_units_1 = dist_grid_axis / (mScaleSnapUnit1 / max_subdivisions);
+ F32 dist_scale_units_2 = dist_grid_axis / (mScaleSnapUnit2 / max_subdivisions);
+ S32 ticks_from_scale_center_1 = lltrunc(dist_scale_units_1);
+ S32 ticks_from_scale_center_2 = lltrunc(dist_scale_units_2);
+ S32 max_ticks1 = llceil(max_point_on_scale_line / (mScaleSnapUnit1 / max_subdivisions) - dist_scale_units_1);
+ S32 max_ticks2 = llceil(max_point_on_scale_line / (mScaleSnapUnit2 / max_subdivisions) - dist_scale_units_2);
+ S32 start_tick = 0;
+ S32 stop_tick = 0;
+
+ if (mInSnapRegime)
+ {
+ // draw snap guide line
+ glBegin(GL_LINES);
+ LLVector3 snap_line_center = mScaleCenter + (mScaleSnapValue * mScaleDir);
+
+ LLVector3 snap_line_start = snap_line_center + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 snap_line_end = snap_line_center + (mSnapGuideDir2 * mSnapRegimeOffset);
+
+ glColor4f(1.f, 1.f, 1.f, grid_alpha);
+ glVertex3fv(snap_line_start.mV);
+ glVertex3fv(snap_line_center.mV);
+ glVertex3fv(snap_line_center.mV);
+ glVertex3fv(snap_line_end.mV);
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ //gGLSNoCullFaces.set();
+ glColor4f(1.f, 1.f, 1.f, grid_alpha);
+
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = mScaleDir;
+
+ arrow_dir = snap_line_start - snap_line_center;
+ arrow_dir.normVec();
+ glVertex3fv((snap_line_start + arrow_dir * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_start + arrow_span * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_start - arrow_span * mBoxHandleSize).mV);
+
+ arrow_dir = snap_line_end - snap_line_center;
+ arrow_dir.normVec();
+ glVertex3fv((snap_line_end + arrow_dir * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_end + arrow_span * mBoxHandleSize).mV);
+ glVertex3fv((snap_line_end - arrow_span * mBoxHandleSize).mV);
+ }
+ glEnd();
+ }
+
+ LLVector2 screen_translate_axis(llabs(mScaleDir * gCamera->getLeftAxis()), llabs(mScaleDir * gCamera->getUpAxis()));
+ screen_translate_axis.normVec();
+
+ S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
+
+ for (pass = 0; pass < 3; pass++)
+ {
+ LLColor4 tick_color = setupSnapGuideRenderPass(pass);
+
+ start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
+ stop_tick = llmin(max_ticks1, num_ticks_per_side1);
+
+ glBegin(GL_LINES);
+ // draw first row of ticks
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset_1), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset_1), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
+ LLVector3 tick_start = tick_pos + (mSnapGuideDir1 * mSnapRegimeOffset);
+ LLVector3 tick_end = tick_start + (mSnapGuideDir1 * mSnapRegimeOffset * tick_scale);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+
+ // draw opposite row of ticks
+ start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
+ stop_tick = llmin(max_ticks2, num_ticks_per_side2);
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 alpha = (1.f - (1.f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset_2), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset_2), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ glColor4f(tick_color.mV[VRED], tick_color.mV[VGREEN], tick_color.mV[VBLUE], tick_color.mV[VALPHA] * alpha);
+ LLVector3 tick_start = tick_pos + (mSnapGuideDir2 * mSnapRegimeOffset);
+ LLVector3 tick_end = tick_start + (mSnapGuideDir2 * mSnapRegimeOffset * tick_scale);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+ glEnd();
+ }
+
+ // render tick labels
+ start_tick = -(llmin(ticks_from_scale_center_1, num_ticks_per_side1));
+ stop_tick = llmin(max_ticks1, num_ticks_per_side1);
+
+ F32 grid_resolution = gSelectMgr->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) / (mScaleSnapUnit1 / max_subdivisions));
+ S32 label_sub_div_offset_2 = llround(fmod(dist_grid_axis - grid_offset2, mScaleSnapUnit2 * 32.f) / (mScaleSnapUnit2 / max_subdivisions));
+
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 tick_scale = 1.f;
+ F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side1)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit1 / max_subdivisions * (F32)i - grid_offset1));
+
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + label_sub_div_offset_1), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + label_sub_div_offset_1), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit1, tick_label_spacing)))) == 0.f)
+ {
+ LLVector3 text_origin = tick_pos +
+ (mSnapGuideDir1 * mSnapRegimeOffset * (1.f + tick_scale));
+
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 tick_val;
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 / grid_resolution);
+ }
+ else
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit1 * 2.f);
+ }
+
+ if (getUniform())
+ {
+ tick_val *= 2.f;
+ }
+
+ F32 text_highlight = 0.8f;
+
+ if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+
+ // label ticks on opposite side
+ if (mScaleSnapUnit2 != mScaleSnapUnit1)
+ {
+ start_tick = -(llmin(ticks_from_scale_center_2, num_ticks_per_side2));
+ stop_tick = llmin(max_ticks2, num_ticks_per_side2);
+ for (S32 i = start_tick; i <= stop_tick; i++)
+ {
+ F32 tick_scale = 1.f;
+ F32 alpha = grid_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side2)));
+ LLVector3 tick_pos = drag_point + (mScaleDir * (mScaleSnapUnit2 / max_subdivisions * (F32)i - grid_offset2));
+
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + label_sub_div_offset_2), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + label_sub_div_offset_2), (max_subdivisions / llmin(max_subdivisions, getSubdivisionLevel(tick_pos, mScaleDir, mScaleSnapUnit2, tick_label_spacing)))) == 0.f)
+ {
+ LLVector3 text_origin = tick_pos +
+ (mSnapGuideDir2 * mSnapRegimeOffset * (1.f + tick_scale));
+
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 tick_val;
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 / grid_resolution);
+ }
+ else
+ {
+ tick_val = (tick_pos - mScaleCenter) * mScaleDir / (mScaleSnapUnit2 * 2.f);
+ }
+
+ if (getUniform())
+ {
+ tick_val *= 2.f;
+ }
+
+ F32 text_highlight = 0.8f;
+
+ if (is_approx_equal(tick_val, mScaleSnapValue) && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ renderTickValue(text_origin, tick_val, grid_mode == GRID_MODE_WORLD ? "m" : "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+ }
+
+
+ // render help text
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
+ {
+ LLVector3 selection_center_start = gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+
+ LLVector3 offset_dir;
+ if (mSnapGuideDir1 * gCamera->getAtAxis() > mSnapGuideDir2 * gCamera->getAtAxis())
+ {
+ offset_dir = mSnapGuideDir2;
+ }
+ else
+ {
+ offset_dir = mSnapGuideDir1;
+ }
+
+ LLVector3 help_text_pos = selection_center_start + (mSnapRegimeOffset * 5.f * offset_dir);
+ const LLFontGL* big_fontp = LLFontGL::sSansSerif;
+
+ std::string help_text = "Move mouse cursor over ruler";
+ LLColor4 help_text_color = LLColor4::white;
+ help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, grid_alpha, 0.f);
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ help_text = "to snap to grid";
+ help_text_pos -= gCamera->getUpAxis() * mSnapRegimeOffset * 0.4f;
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ }
+ }
+ }
+}
+
+// Returns unit vector in direction of part of an origin-centered cube
+LLVector3 LLManipScale::partToUnitVector( S32 part ) const
+{
+ if( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) )
+ {
+ return faceToUnitVector( part );
+ }
+ else
+ if( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) )
+ {
+ return cornerToUnitVector( part );
+ }
+ else
+ if( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX ) )
+ {
+ return edgeToUnitVector( part );
+ }
+ return LLVector3();
+}
+
+
+// Returns unit vector in direction of face of an origin-centered cube
+LLVector3 LLManipScale::faceToUnitVector( S32 part ) const
+{
+ llassert( (LL_FACE_MIN <= part) && (part <= LL_FACE_MAX) );
+ switch( part )
+ {
+ case LL_FACE_POSX:
+ return LLVector3( 1.f, 0.f, 0.f );
+
+ case LL_FACE_NEGX:
+ return LLVector3( -1.f, 0.f, 0.f );
+
+ case LL_FACE_POSY:
+ return LLVector3( 0.f, 1.f, 0.f );
+
+ case LL_FACE_NEGY:
+ return LLVector3( 0.f, -1.f, 0.f );
+
+ case LL_FACE_POSZ:
+ return LLVector3( 0.f, 0.f, 1.f );
+
+ case LL_FACE_NEGZ:
+ return LLVector3( 0.f, 0.f, -1.f );
+ }
+ return LLVector3();
+}
+
+
+// Returns unit vector in direction of corner of an origin-centered cube
+LLVector3 LLManipScale::cornerToUnitVector( S32 part ) const
+{
+ llassert( (LL_CORNER_MIN <= part) && (part <= LL_CORNER_MAX) );
+ LLVector3 vec;
+ switch(part)
+ {
+ case LL_CORNER_NNN:
+ vec.setVec(-F_SQRT3, -F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_NNP:
+ vec.setVec(-F_SQRT3, -F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_NPN:
+ vec.setVec(-F_SQRT3, F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_NPP:
+ vec.setVec(-F_SQRT3, F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_PNN:
+ vec.setVec(F_SQRT3, -F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_PNP:
+ vec.setVec(F_SQRT3, -F_SQRT3, F_SQRT3);
+ break;
+ case LL_CORNER_PPN:
+ vec.setVec(F_SQRT3, F_SQRT3, -F_SQRT3);
+ break;
+ case LL_CORNER_PPP:
+ vec.setVec(F_SQRT3, F_SQRT3, F_SQRT3);
+ break;
+ default:
+ vec.clearVec();
+ }
+
+ return vec;
+}
+
+// Returns unit vector in direction of edge of an origin-centered cube
+LLVector3 LLManipScale::edgeToUnitVector( S32 part ) const
+{
+ llassert( (LL_EDGE_MIN <= part) && (part <= LL_EDGE_MAX) );
+ //FIXME
+ part -= LL_EDGE_MIN;
+ S32 rotation = part >> 2; // Edge between which faces: 0 => XY, 1 => YZ, 2 => ZX
+ LLVector3 v;
+ v.mV[rotation] = (part & 1) ? F_SQRT2 : -F_SQRT2;
+ v.mV[(rotation+1) % 3] = (part & 2) ? F_SQRT2 : -F_SQRT2;
+ // v.mV[(rotation+2) % 3] defaults to 0.
+ return v;
+}
+
+// Non-linear scale of origin-centered unit cube to non-origin-centered, non-symetrical bounding box
+LLVector3 LLManipScale::unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const
+{
+ const LLVector3& min = bbox.getMinLocal();
+ const LLVector3& max = bbox.getMaxLocal();
+ LLVector3 ctr = bbox.getCenterLocal();
+
+ return LLVector3(
+ v.mV[0] ? (v.mV[0]>0 ? max.mV[0] : min.mV[0] ) : ctr.mV[0],
+ v.mV[1] ? (v.mV[1]>0 ? max.mV[1] : min.mV[1] ) : ctr.mV[1],
+ v.mV[2] ? (v.mV[2]>0 ? max.mV[2] : min.mV[2] ) : ctr.mV[2] );
+}
+
+// returns max allowable scale along a given stretch axis
+F32 LLManipScale::partToMaxScale( S32 part, const LLBBox &bbox ) const
+{
+ F32 max_scale_factor = 0.f;
+ LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
+ bbox_extents.abs();
+ F32 max_extent = 0.f;
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (bbox_extents.mV[i] > max_extent)
+ {
+ max_extent = bbox_extents.mV[i];
+ }
+ }
+ max_scale_factor = bbox_extents.magVec() * MAX_OBJECT_SCALE / max_extent;
+
+ if (getUniform())
+ {
+ max_scale_factor *= 0.5f;
+ }
+
+ return max_scale_factor;
+}
+
+// returns min allowable scale along a given stretch axis
+F32 LLManipScale::partToMinScale( S32 part, const LLBBox &bbox ) const
+{
+ LLVector3 bbox_extents = unitVectorToLocalBBoxExtent( partToUnitVector( part ), bbox );
+ bbox_extents.abs();
+ F32 min_extent = MAX_OBJECT_SCALE;
+ for (U32 i = VX; i <= VZ; i++)
+ {
+ if (bbox_extents.mV[i] > 0.f && bbox_extents.mV[i] < min_extent)
+ {
+ min_extent = bbox_extents.mV[i];
+ }
+ }
+ F32 min_scale_factor = bbox_extents.magVec() * MIN_OBJECT_SCALE / min_extent;
+
+ if (getUniform())
+ {
+ min_scale_factor *= 0.5f;
+ }
+
+ return min_scale_factor;
+}
+
+// Returns the axis aligned unit vector closest to v.
+LLVector3 LLManipScale::nearestAxis( const LLVector3& v ) const
+{
+ // Note: yes, this is a slow but easy implementation
+ // assumes v is normalized
+
+ F32 coords[][3] =
+ {
+ { 1.f, 0.f, 0.f },
+ { 0.f, 1.f, 0.f },
+ { 0.f, 0.f, 1.f },
+ {-1.f, 0.f, 0.f },
+ { 0.f,-1.f, 0.f },
+ { 0.f, 0.f,-1.f }
+ };
+
+ F32 cosine[6];
+ cosine[0] = v * LLVector3( coords[0] );
+ cosine[1] = v * LLVector3( coords[1] );
+ cosine[2] = v * LLVector3( coords[2] );
+ cosine[3] = -cosine[0];
+ cosine[4] = -cosine[1];
+ cosine[5] = -cosine[2];
+
+ F32 greatest_cos = cosine[0];
+ S32 greatest_index = 0;
+ for( S32 i=1; i<6; i++ )
+ {
+ if( greatest_cos < cosine[i] )
+ {
+ greatest_cos = cosine[i];
+ greatest_index = i;
+ }
+ }
+
+ return LLVector3( coords[greatest_index] );
+}
+
+BOOL LLManipScale::isSelectionScalable() const
+{
+ // An selection is scalable if you are allowed to both edit and move
+ // everything in it, and it does not have any sitting agents
+ BOOL scalable = gSelectMgr->getFirstObject() ? TRUE : FALSE;
+ for(LLViewerObject* cur = gSelectMgr->getFirstObject();
+ cur;
+ cur = gSelectMgr->getNextObject() )
+ {
+ if( !(cur->permModify() && cur->permMove())
+ || cur->isSeat())
+ {
+ scalable = FALSE;
+ break;
+ }
+ }
+ return scalable;
+}
diff --git a/indra/newview/llmanipscale.h b/indra/newview/llmanipscale.h
new file mode 100644
index 0000000000..0ba141cc01
--- /dev/null
+++ b/indra/newview/llmanipscale.h
@@ -0,0 +1,137 @@
+/**
+ * @file llmanipscale.h
+ * @brief LLManipScale class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MANIPSCALE_H
+#define LL_MANIPSCALE_H
+
+// llmanipscale.h
+//
+// copyright 2001-2002, linden research inc
+
+
+#include "lltool.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "llmanip.h"
+#include "llviewerobject.h"
+#include "llbbox.h"
+
+class LLToolComposite;
+class LLColor4;
+
+typedef enum e_scale_manipulator_type
+{
+ SCALE_MANIP_CORNER,
+ SCALE_MANIP_FACE
+} EScaleManipulatorType;
+
+
+class LLManipScale : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mPosition;
+ EManipPart mManipID;
+ EScaleManipulatorType mType;
+
+ ManipulatorHandle(LLVector3 pos, EManipPart id, EScaleManipulatorType type):mPosition(pos), mManipID(id), mType(type){}
+ };
+
+
+ LLManipScale( LLToolComposite* composite );
+ ~LLManipScale();
+
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void render();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+ virtual void highlightManipulators(S32 x, S32 y); // decided which manipulator, if any, should be highlighted by mouse hover
+
+ static void setUniform( BOOL b );
+ static BOOL getUniform();
+ static void setStretchTextures( BOOL b );
+ static BOOL getStretchTextures();
+ static void setShowAxes( BOOL b );
+ static BOOL getShowAxes();
+
+private:
+ void renderCorners( const LLBBox& local_bbox );
+ void renderFaces( const LLBBox& local_bbox );
+ void renderEdges( const LLBBox& local_bbox );
+ void renderBoxHandle( F32 x, F32 y, F32 z );
+ void renderAxisHandle( const LLVector3& start, const LLVector3& end );
+ void renderGuidelinesPart( const LLBBox& local_bbox );
+ void renderSnapGuides( const LLBBox& local_bbox );
+
+ void revert();
+
+ inline void conditionalHighlight( U32 part, const LLColor4* highlight = NULL, const LLColor4* normal = NULL );
+
+ void drag( S32 x, S32 y );
+ void dragFace( S32 x, S32 y );
+ void dragCorner( S32 x, S32 y );
+
+ void sendUpdates( BOOL send_position_update, BOOL send_scale_update, BOOL corner = FALSE);
+
+ LLVector3 faceToUnitVector( S32 part ) const;
+ LLVector3 cornerToUnitVector( S32 part ) const;
+ LLVector3 edgeToUnitVector( S32 part ) const;
+ LLVector3 partToUnitVector( S32 part ) const;
+ LLVector3 unitVectorToLocalBBoxExtent( const LLVector3& v, const LLBBox& bbox ) const;
+ F32 partToMaxScale( S32 part, const LLBBox& bbox ) const;
+ F32 partToMinScale( S32 part, const LLBBox& bbox ) const;
+ LLVector3 nearestAxis( const LLVector3& v ) const;
+
+ BOOL isSelectionScalable() const;
+
+ void stretchFace( const LLVector3& drag_start_agent, const LLVector3& drag_delta_agent);
+
+ void adjustTextureRepeats(); // Adjusts texture coords based on mSavedScale and current scale, only works for boxes
+
+ void updateSnapGuides(const LLBBox& bbox);
+private:
+
+ F32 mBoxHandleSize; // The size of the handles at the corners of the bounding box
+ F32 mScaledBoxHandleSize; // handle size after scaling for selection feedback
+ EManipPart mManipPart;
+ LLVector3d mDragStartPointGlobal;
+ LLVector3d mDragStartCenterGlobal; // The center of the bounding box of all selected objects at time of drag start
+ LLVector3d mDragPointGlobal;
+ LLVector3d mDragFarHitGlobal;
+ EManipPart mHighlightedPart;
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ BOOL mSendUpdateOnMouseUp;
+ U32 mLastUpdateFlags;
+ LLLinkedList<ManipulatorHandle> mProjectedManipulators;
+ LLVector4 mManipulatorVertices[14];
+ F32 mScaleSnapUnit1; // size of snap multiples for axis 1
+ F32 mScaleSnapUnit2; // size of snap multiples for axis 2
+ LLVector3 mScalePlaneNormal1; // normal of plane in which scale occurs that most faces camera
+ LLVector3 mScalePlaneNormal2; // normal of plane in which scale occurs that most faces camera
+ LLVector3 mSnapGuideDir1;
+ LLVector3 mSnapGuideDir2;
+ LLVector3 mSnapDir1;
+ LLVector3 mSnapDir2;
+ F32 mSnapRegimeOffset;
+ F32 mSnapGuideLength;
+ LLVector3 mScaleCenter;
+ LLVector3 mScaleDir;
+ F32 mScaleSnapValue;
+ BOOL mInSnapRegime;
+ F32* mManipulatorScales;
+};
+
+#endif // LL_MANIPSCALE_H
diff --git a/indra/newview/llmaniptranslate.cpp b/indra/newview/llmaniptranslate.cpp
new file mode 100644
index 0000000000..3db23c06ca
--- /dev/null
+++ b/indra/newview/llmaniptranslate.cpp
@@ -0,0 +1,2126 @@
+/**
+ * @file llmaniptranslate.cpp
+ * @brief LLManipTranslate class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * Positioning tool
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmaniptranslate.h"
+
+#include "llgl.h"
+
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llfontgl.h"
+#include "llglheaders.h"
+#include "llhudrender.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "llviewercamera.h"
+#include "llviewerjoint.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llui.h"
+
+const S32 NUM_AXES = 3;
+const S32 MOUSE_DRAG_SLOP = 2; // pixels
+const F32 HANDLE_HIDE_ANGLE = 0.15f; // radians
+const F32 SELECTED_ARROW_SCALE = 1.3f;
+const F32 MANIPULATOR_HOTSPOT_START = 0.2f;
+const F32 MANIPULATOR_HOTSPOT_END = 1.2f;
+const F32 SNAP_GUIDE_SCREEN_SIZE = 0.7f;
+const F32 MIN_PLANE_MANIP_DOT_PRODUCT = 0.25f;
+const F32 PLANE_TICK_SIZE = 0.4f;
+const F32 MANIPULATOR_SCALE_HALF_LIFE = 0.07f;
+const F32 SNAP_ARROW_SCALE = 0.7f;
+
+static GLuint sGridTex = 0;
+
+const LLManip::EManipPart MANIPULATOR_IDS[9] =
+{
+ LLManip::LL_X_ARROW,
+ LLManip::LL_Y_ARROW,
+ LLManip::LL_Z_ARROW,
+ LLManip::LL_X_ARROW,
+ LLManip::LL_Y_ARROW,
+ LLManip::LL_Z_ARROW,
+ LLManip::LL_YZ_PLANE,
+ LLManip::LL_XZ_PLANE,
+ LLManip::LL_XY_PLANE
+};
+
+const U32 ARROW_TO_AXIS[4] =
+{
+ VX,
+ VX,
+ VY,
+ VZ
+};
+
+BOOL sort_manip_by_end_z(LLManipTranslate::ManipulatorHandle *new_manip, LLManipTranslate::ManipulatorHandle *test_manip)
+{
+ return (new_manip->mEndPosition.mV[VZ] < test_manip->mEndPosition.mV[VZ]);
+}
+
+LLManipTranslate::LLManipTranslate( LLToolComposite* composite )
+: LLManip( "Move", composite ),
+ mLastHoverMouseX(-1),
+ mLastHoverMouseY(-1),
+ mSendUpdateOnMouseUp(FALSE),
+ mMouseOutsideSlop(FALSE),
+ mCopyMadeThisDrag(FALSE),
+ mMouseDownX(-1),
+ mMouseDownY(-1),
+ mAxisArrowLength(50),
+ mConeSize(0),
+ mArrowLengthMeters(0.f),
+ mPlaneManipOffsetMeters(0.f),
+ mManipPart(LL_NO_PART),
+ mUpdateTimer(),
+ mSnapOffsetMeters(0.f),
+ mArrowScales(1.f, 1.f, 1.f),
+ mPlaneScales(1.f, 1.f, 1.f),
+ mPlaneManipPositions(1.f, 1.f, 1.f, 1.f)
+{
+ mProjectedManipulators.setInsertBefore(sort_manip_by_end_z);
+
+ if (sGridTex == 0)
+ {
+ restoreGL();
+ }
+}
+
+//static
+void LLManipTranslate::restoreGL()
+{
+ //generate grid texture
+ U32 rez = 512;
+ U32 mip = 0;
+
+ GLuint* d = new GLuint[rez*rez];
+ LLGLEnable tex2d(GL_TEXTURE_2D);
+ glGenTextures(1, &sGridTex);
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ while (rez >= 1)
+ {
+ for (U32 i = 0; i < rez*rez; i++)
+ {
+ d[i] = 0x00FFFFFF;
+ }
+
+ U32 subcol = 0xFFFFFFFF;
+ if (rez >= 4)
+ { //large grain grid
+ for (U32 i = 0; i < rez; i++)
+ {
+ if (rez <= 16)
+ {
+ if (rez == 16)
+ {
+ subcol = 0xA0FFFFFF;
+ }
+ else if (rez == 8)
+ {
+ subcol = 0x80FFFFFF;
+ }
+ else
+ {
+ subcol = 0x40FFFFFF;
+ }
+ }
+ else
+ {
+ subcol = 0xFFFFFFFF;
+ }
+ d[i *rez+ 0 ] = subcol;
+ d[0 *rez+ i ] = subcol;
+ if (rez >= 32)
+ {
+ d[i *rez+ (rez-1)] = subcol;
+ d[(rez-1) *rez+ i ] = subcol;
+ }
+
+ if (rez >= 64)
+ {
+ subcol = 0xFFFFFFFF;
+
+ if (i > 0 && i < (rez-1))
+ {
+ d[i *rez+ 1 ] = subcol;
+ d[i *rez+ (rez-2)] = subcol;
+ d[1 *rez+ i ] = subcol;
+ d[(rez-2) *rez+ i ] = subcol;
+ }
+ }
+ }
+ }
+
+ subcol = 0x50A0A0A0;
+ if (rez >= 128)
+ { //small grain grid
+ for (U32 i = 8; i < rez; i+=8)
+ {
+ for (U32 j = 2; j < rez-2; j++)
+ {
+ d[i *rez+ j] = subcol;
+ d[j *rez+ i] = subcol;
+ }
+ }
+ }
+ if (rez >= 64)
+ { //medium grain grid
+ if (rez == 64)
+ {
+ subcol = 0x50A0A0A0;
+ }
+ else
+ {
+ subcol = 0xA0D0D0D0;
+ }
+
+ for (U32 i = 32; i < rez; i+=32)
+ {
+ U32 pi = i-1;
+ for (U32 j = 2; j < rez-2; j++)
+ {
+ d[i *rez+ j] = subcol;
+ d[j *rez+ i] = subcol;
+
+ if (rez > 128)
+ {
+ d[pi *rez+ j] = subcol;
+ d[j *rez+ pi] = subcol;
+ }
+ }
+ }
+ }
+#ifdef LL_WINDOWS
+ glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, 0, GL_RGBA, GL_UNSIGNED_BYTE, d);
+#else
+ glTexImage2D(GL_TEXTURE_2D, mip, GL_RGBA, rez, rez, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, d);
+#endif
+ rez = rez >> 1;
+ mip++;
+ }
+ delete [] d;
+}
+
+
+LLManipTranslate::~LLManipTranslate()
+{
+ mProjectedManipulators.deleteAllData();
+}
+
+
+void LLManipTranslate::handleSelect()
+{
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ gFloaterTools->setStatusText("Drag to move, shift-drag to copy");
+}
+
+void LLManipTranslate::handleDeselect()
+{
+ mHighlightedPart = LL_NO_PART;
+ mManipPart = LL_NO_PART;
+ gFloaterTools->setStatusText("");
+}
+
+BOOL LLManipTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if( hit_obj &&
+ (mHighlightedPart == LL_X_ARROW ||
+ mHighlightedPart == LL_Y_ARROW ||
+ mHighlightedPart == LL_Z_ARROW ||
+ mHighlightedPart == LL_YZ_PLANE ||
+ mHighlightedPart == LL_XZ_PLANE ||
+ mHighlightedPart == LL_XY_PLANE ) )
+ {
+ handled = handleMouseDownOnPart( x, y, mask );
+ }
+
+ return handled;
+}
+
+// Assumes that one of the arrows on an object was hit.
+BOOL LLManipTranslate::handleMouseDownOnPart( S32 x, S32 y, MASK mask )
+{
+ BOOL can_move = gSelectMgr->getObjectCount() != 0;
+ for (LLViewerObject* objectp = gSelectMgr->getFirstObject();
+ objectp;
+ objectp = gSelectMgr->getNextObject())
+ {
+ can_move = can_move && objectp->permMove() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ }
+
+ if (!can_move)
+ {
+ return FALSE;
+ }
+
+ highlightManipulators(x, y);
+ S32 hit_part = mHighlightedPart;
+
+ if( (hit_part != LL_X_ARROW) &&
+ (hit_part != LL_Y_ARROW) &&
+ (hit_part != LL_Z_ARROW) &&
+ (hit_part != LL_YZ_PLANE) &&
+ (hit_part != LL_XZ_PLANE) &&
+ (hit_part != LL_XY_PLANE) )
+ {
+ return TRUE;
+ }
+
+ mHelpTextTimer.reset();
+ sNumTimesHelpTextShown++;
+
+ gSelectMgr->getGrid(mGridOrigin, mGridRotation, mGridScale);
+
+ gSelectMgr->enableSilhouette(FALSE);
+
+ // we just started a drag, so save initial object positions
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_MOVE);
+
+ mManipPart = (EManipPart)hit_part;
+ mMouseDownX = x;
+ mMouseDownY = y;
+ mMouseOutsideSlop = FALSE;
+
+ LLVector3 axis;
+
+ LLSelectNode *selectNode = gSelectMgr->getFirstMoveableNode(TRUE);
+
+ if (!selectNode)
+ {
+ // didn't find the object in our selection...oh well
+ llwarns << "Trying to translate an unselected object" << llendl;
+ return TRUE;
+ }
+
+ LLViewerObject *selected_object = selectNode->getObject();
+ if (!selected_object)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ // Compute unit vectors for arrow hit and a plane through that vector
+ BOOL axis_exists = getManipAxis(selected_object, mManipPart, axis);
+ getManipNormal(selected_object, mManipPart, mManipNormal);
+
+ //LLVector3 select_center_agent = gAgent.getPosAgentFromGlobal(gSelectMgr->getSelectionCenterGlobal());
+ // TomY: The above should (?) be identical to the below
+ LLVector3 select_center_agent = getPivotPoint();
+ mSubdivisions = llclamp(getSubdivisionLevel(select_center_agent, axis_exists ? axis : LLVector3::z_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ // if we clicked on a planar manipulator, recenter mouse cursor
+ if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
+ {
+ LLCoordGL mouse_pos;
+ gCamera->projectPosAgentToScreen(select_center_agent, mouse_pos);
+
+ if (gSavedSettings.getBOOL("SnapToMouseCursor"))
+ {
+ LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY);
+ x = mouse_pos.mX;
+ y = mouse_pos.mY;
+ }
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ LLVector3d object_start_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
+ getMousePointOnPlaneGlobal(mDragCursorStartGlobal, x, y, object_start_global, mManipNormal);
+ mDragSelectionStartGlobal = object_start_global;
+ mCopyMadeThisDrag = FALSE;
+
+ // Route future Mouse messages here preemptively. (Release on mouse up.)
+ setMouseCapture( TRUE );
+
+ return TRUE;
+}
+
+BOOL LLManipTranslate::handleHover(S32 x, S32 y, MASK mask)
+{
+ // Translation tool only works if mouse button is down.
+ // Bail out if mouse not down.
+ if( !hasMouseCapture() )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (inactive)" << llendl;
+ // Always show cursor
+ // gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+
+ highlightManipulators(x, y);
+ return TRUE;
+ }
+
+ // Handle auto-rotation if necessary.
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ BOOL rotated = FALSE;
+
+ // ...build mode moves camera about focus point
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ if (x < ROTATE_H_MARGIN)
+ {
+ gAgent.cameraOrbitAround(rotate_angle);
+ rotated = TRUE;
+ }
+ else if (x > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ gAgent.cameraOrbitAround(-rotate_angle);
+ rotated = TRUE;
+ }
+ }
+
+ // Suppress processing if mouse hasn't actually moved.
+ // This may cause problems if the camera moves outside of the
+ // rotation above.
+ if( x == mLastHoverMouseX && y == mLastHoverMouseY && !rotated)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse unmoved)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+ mLastHoverMouseX = x;
+ mLastHoverMouseY = y;
+
+ // Suppress if mouse hasn't moved past the initial slop region
+ // Reset once we start moving
+ if( !mMouseOutsideSlop )
+ {
+ if (abs(mMouseDownX - x) < MOUSE_DRAG_SLOP && abs(mMouseDownY - y) < MOUSE_DRAG_SLOP )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (mouse inside slop)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+ else
+ {
+ // ...just went outside the slop region
+ mMouseOutsideSlop = TRUE;
+ // If holding down shift, leave behind a copy.
+ if (mask == MASK_COPY)
+ {
+ // ...we're trying to make a copy
+ gSelectMgr->selectDuplicate(LLVector3::zero, FALSE);
+ mCopyMadeThisDrag = TRUE;
+
+ // When we make the copy, we don't want to do any other processing.
+ // If so, the object will also be moved, and the copy will be offset.
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (made copy)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ }
+ }
+ }
+
+ // Throttle updates to 10 per second.
+ BOOL send_update = FALSE;
+
+ LLVector3 axis_f;
+ LLVector3d axis_d;
+ LLViewerObject *object;
+
+ // pick the first object to constrain to grid w/ common origin
+ // this is so we don't screw up groups
+ LLSelectNode* selectNode = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (!selectNode)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ object = selectNode->getObject();
+ if (!object)
+ {
+ // somehow we lost the object!
+ llwarns << "Translate manip lost the object" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+ }
+
+ // Compute unit vectors for arrow hit and a plane through that vector
+ BOOL axis_exists = getManipAxis(object, mManipPart, axis_f); // TODO: move this
+
+ axis_d.setVec(axis_f);
+
+ gSelectMgr->updateSelectionCenter();
+ LLVector3d current_pos_global = gAgent.getPosGlobalFromAgent(getPivotPoint());
+
+ mSubdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), axis_f, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ // Project the cursor onto that plane
+ LLVector3d relative_move;
+ getMousePointOnPlaneGlobal(relative_move, x, y, current_pos_global, mManipNormal);\
+ relative_move -= mDragCursorStartGlobal;
+
+ // You can't move more than some distance from your original mousedown point.
+ if (gSavedSettings.getBOOL("LimitDragDistance"))
+ {
+ F32 max_drag_distance = gSavedSettings.getF32("MaxDragDistance");
+
+ if (relative_move.magVecSquared() > max_drag_distance * max_drag_distance)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (too far)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_NOLOCKED);
+ return TRUE;
+ }
+ }
+
+ F64 axis_magnitude = relative_move * axis_d; // dot product
+ LLVector3d cursor_point_snap_line;
+
+ F64 off_axis_magnitude;
+
+ getMousePointOnPlaneGlobal(cursor_point_snap_line, x, y, current_pos_global, mSnapOffsetAxis % axis_f);
+ off_axis_magnitude = axis_exists ? llabs((cursor_point_snap_line - current_pos_global) * LLVector3d(mSnapOffsetAxis)) : 0.f;
+
+ if (gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ if (off_axis_magnitude > mSnapOffsetMeters)
+ {
+ mInSnapRegime = TRUE;
+ LLVector3 mouse_down_offset(mDragCursorStartGlobal - mDragSelectionStartGlobal);
+ LLVector3 cursor_snap_agent = gAgent.getPosAgentFromGlobal(cursor_point_snap_line);
+ if (!gSavedSettings.getBOOL("SnapToMouseCursor"))
+ {
+ cursor_snap_agent -= mouse_down_offset;
+ }
+
+ F32 cursor_grid_dist = (cursor_snap_agent - mGridOrigin) * axis_f;
+
+ F32 snap_dist = getMinGridScale() / (2.f * mSubdivisions);
+ F32 relative_snap_dist = fmodf(llabs(cursor_grid_dist) + snap_dist, getMinGridScale() / mSubdivisions);
+ if (relative_snap_dist < snap_dist * 2.f)
+ {
+ if (cursor_grid_dist > 0.f)
+ {
+ cursor_grid_dist -= relative_snap_dist - snap_dist;
+ }
+ else
+ {
+ cursor_grid_dist += relative_snap_dist - snap_dist;
+ }
+ }
+
+ F32 object_start_on_axis = (gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal) - mGridOrigin) * axis_f;
+ axis_magnitude = cursor_grid_dist - object_start_on_axis;
+ }
+ else if (mManipPart >= LL_YZ_PLANE && mManipPart <= LL_XY_PLANE)
+ {
+ // subtract offset from object center
+ LLVector3d cursor_point_global;
+ getMousePointOnPlaneGlobal( cursor_point_global, x, y, current_pos_global, mManipNormal );
+ cursor_point_global -= (mDragCursorStartGlobal - mDragSelectionStartGlobal);
+
+ // snap to planar grid
+ LLVector3 cursor_point_agent = gAgent.getPosAgentFromGlobal(cursor_point_global);
+ LLVector3 camera_plane_projection = gCamera->getAtAxis();
+ camera_plane_projection -= projected_vec(camera_plane_projection, mManipNormal);
+ camera_plane_projection.normVec();
+ LLVector3 camera_projected_dir = camera_plane_projection;
+ camera_plane_projection.rotVec(~mGridRotation);
+ camera_plane_projection.scaleVec(mGridScale);
+ camera_plane_projection.abs();
+ F32 max_grid_scale;
+ if (camera_plane_projection.mV[VX] > camera_plane_projection.mV[VY] &&
+ camera_plane_projection.mV[VX] > camera_plane_projection.mV[VZ])
+ {
+ max_grid_scale = mGridScale.mV[VX];
+ }
+ else if (camera_plane_projection.mV[VY] > camera_plane_projection.mV[VZ])
+ {
+ max_grid_scale = mGridScale.mV[VY];
+ }
+ else
+ {
+ max_grid_scale = mGridScale.mV[VZ];
+ }
+
+ F32 num_subdivisions = llclamp(getSubdivisionLevel(getPivotPoint(), camera_projected_dir, max_grid_scale), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ F32 grid_scale_a;
+ F32 grid_scale_b;
+ LLVector3 cursor_point_grid = (cursor_point_agent - mGridOrigin) * ~mGridRotation;
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ grid_scale_a = mGridScale.mV[VY] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
+ cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ case LL_XZ_PLANE:
+ grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VZ] / num_subdivisions;
+ cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VZ] -= fmod(cursor_point_grid.mV[VZ] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ case LL_XY_PLANE:
+ grid_scale_a = mGridScale.mV[VX] / num_subdivisions;
+ grid_scale_b = mGridScale.mV[VY] / num_subdivisions;
+ cursor_point_grid.mV[VX] -= fmod(cursor_point_grid.mV[VX] + grid_scale_a * 0.5f, grid_scale_a) - grid_scale_a * 0.5f;
+ cursor_point_grid.mV[VY] -= fmod(cursor_point_grid.mV[VY] + grid_scale_b * 0.5f, grid_scale_b) - grid_scale_b * 0.5f;
+ break;
+ default:
+ break;
+ }
+ cursor_point_agent = (cursor_point_grid * mGridRotation) + mGridOrigin;
+ relative_move.setVec(cursor_point_agent - gAgent.getPosAgentFromGlobal(mDragSelectionStartGlobal));
+ mInSnapRegime = TRUE;
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+ }
+ else
+ {
+ mInSnapRegime = FALSE;
+ }
+
+ // Clamp to arrow direction
+ //FIXME: does this apply anymore?
+ if (!axis_exists)
+ {
+ axis_magnitude = relative_move.normVec();
+ axis_d.setVec(relative_move);
+ axis_d.normVec();
+ axis_f.setVec(axis_d);
+ }
+
+ LLVector3d clamped_relative_move = axis_magnitude * axis_d; // scalar multiply
+ LLVector3 clamped_relative_move_f = (F32)axis_magnitude * axis_f; // scalar multiply
+
+ for(selectNode = gSelectMgr->getFirstNode();
+ selectNode;
+ selectNode = gSelectMgr->getNextNode() )
+ {
+ object = selectNode->getObject();
+
+ // Only apply motion to root objects and objects selected
+ // as "individual".
+ if (!object->isRootEdit() && !selectNode->mIndividualSelection)
+ {
+ continue;
+ }
+
+ if (!object->isRootEdit())
+ {
+ // child objects should not update if parent is selected
+ LLViewerObject* editable_root = (LLViewerObject*)object->getParent();
+ if (editable_root->isSelected())
+ {
+ // we will be moved properly by our parent, so skip
+ continue;
+ }
+ }
+
+ if (object->permMove())
+ {
+ // handle attachments in local space
+ if (object->isAttachment() && object->mDrawable.notNull())
+ {
+ // calculate local version of relative move
+ LLQuaternion objWorldRotation = object->mDrawable->mXform.getParent()->getWorldRotation();
+ objWorldRotation.transQuat();
+
+ LLVector3 old_position_local = object->getPosition();
+ LLVector3 new_position_local = selectNode->mSavedPositionLocal + (clamped_relative_move_f * objWorldRotation);
+
+ // move and clamp root object first, before adjusting children
+ if (new_position_local != old_position_local)
+ {
+ send_update = TRUE;
+ }
+ //RN: I forget, but we need to do this because of snapping which doesn't often result
+ // in position changes even when the mouse moves
+ object->setPosition(new_position_local);
+ rebuild(object);
+ gAgent.getAvatarObject()->clampAttachmentPositions();
+ new_position_local = object->getPosition();
+
+ if (selectNode->mIndividualSelection)
+ {
+ send_update = FALSE;
+ LLVector3 child_offset = (old_position_local - new_position_local) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+
+ if (!childp->isSelected())
+ {
+ childp->setPosition(childp->getPosition() + child_offset);
+ rebuild(childp);
+ }
+ }
+ }
+ }
+ else
+ {
+ // compute new position to send to simulators, but don't set it yet.
+ // We need the old position to know which simulator to send the move message to.
+ LLVector3d new_position_global = selectNode->mSavedPositionGlobal + clamped_relative_move;
+
+ // Don't let object centers go too far underground
+ F64 min_height = gWorldp->getMinAllowedZ(object);
+ if (new_position_global.mdV[VZ] < min_height)
+ {
+ new_position_global.mdV[VZ] = min_height;
+ }
+
+ // For safety, cap heights where objects can be dragged
+ if (new_position_global.mdV[VZ] > MAX_OBJECT_Z)
+ {
+ new_position_global.mdV[VZ] = MAX_OBJECT_Z;
+ }
+
+ // Grass is always drawn on the ground, so clamp its position to the ground
+ if (object->getPCode() == LL_PCODE_LEGACY_GRASS)
+ {
+ new_position_global.mdV[VZ] = gWorldp->resolveLandHeightGlobal(new_position_global) + 1.f;
+ }
+
+ if (object->isRootEdit())
+ {
+ new_position_global = gWorldp->clipToVisibleRegions(object->getPositionGlobal(), new_position_global);
+ }
+
+ // PR: Only update if changed
+ LLVector3d old_position_global = object->getPositionGlobal();
+ LLVector3 old_position_agent = object->getPositionAgent();
+ LLVector3 new_position_agent = gAgent.getPosAgentFromGlobal(new_position_global);
+ if (object->isRootEdit())
+ {
+ // finally, move parent object after children have calculated new offsets
+ object->setPositionAgent(new_position_agent);
+ rebuild(object);
+ }
+ else
+ {
+ LLViewerObject* root_object = object->getRootEdit();
+ new_position_agent -= root_object->getPositionAgent();
+ new_position_agent = new_position_agent * ~root_object->getRotation();
+ object->setPositionParent(new_position_agent, FALSE);
+ rebuild(object);
+ }
+
+ if (selectNode->mIndividualSelection)
+ {
+ LLVector3 parent_offset = (new_position_agent - old_position_agent) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ if (!childp->isSelected())
+ {
+ childp->setPosition(childp->getPosition() - parent_offset);
+ rebuild(childp);
+ }
+ }
+ send_update = FALSE;
+ }
+ else if (old_position_global != new_position_global)
+ {
+ send_update = TRUE;
+ }
+ }
+ }
+ }
+
+ // Handle throttling to 10 updates per second.
+ F32 elapsed_time = mUpdateTimer.getElapsedTimeF32();
+ const F32 UPDATE_DELAY = 0.1f; // min time between transmitted updates
+ if (send_update && (elapsed_time > UPDATE_DELAY))
+ {
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ mUpdateTimer.reset();
+ mSendUpdateOnMouseUp = FALSE;
+ }
+ else
+ {
+ // ...suppressed update
+ mSendUpdateOnMouseUp = TRUE;
+ }
+
+ gSelectMgr->updateSelectionCenter();
+ gAgent.clearFocusObject();
+ //gAgent.setObjectTracking(FALSE);
+ dialog_refresh_all(); // ??? is this necessary?
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLManipTranslate (active)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLTRANSLATE);
+ return TRUE;
+}
+
+void LLManipTranslate::highlightManipulators(S32 x, S32 y)
+{
+ mHighlightedPart = LL_NO_PART;
+
+ if (!gSelectMgr->getObjectCount())
+ {
+ return;
+ }
+
+ //LLBBox bbox = gSelectMgr->getBBoxOfSelection();
+ LLMatrix4 projMatrix = gCamera->getProjection();
+ LLMatrix4 modelView = gCamera->getModelview();
+
+ LLVector3 object_position = getPivotPoint();
+
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+
+ LLVector3 relative_camera_dir;
+
+ LLMatrix4 transform;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ relative_camera_dir = LLVector3(1.f, 0.f, 0.f) * ~grid_rotation;
+ LLVector4 translation(object_position);
+ transform.initRotTrans(grid_rotation, translation);
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ transform *= cfr;
+ LLMatrix4 window_scale;
+ F32 zoom_level = 2.f * gAgent.getAvatarObject()->mHUDCurZoom;
+ window_scale.initAll(LLVector3(zoom_level / gCamera->getAspect(), zoom_level, 0.f),
+ LLQuaternion::DEFAULT,
+ LLVector3::zero);
+ transform *= window_scale;
+ }
+ else
+ {
+ relative_camera_dir = (object_position - gCamera->getOrigin()) * ~grid_rotation;
+ relative_camera_dir.normVec();
+
+ transform.initRotTrans(grid_rotation, LLVector4(object_position));
+ transform *= modelView;
+ transform *= projMatrix;
+ }
+
+ mProjectedManipulators.deleteAllData();
+
+ S32 numManips = 0;
+
+ // edges
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_START, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * MANIPULATOR_HOTSPOT_END, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 0.f, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 0.f, 1.f);
+
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_START, 1.f);
+ mManipulatorVertices[numManips++] = LLVector4(0.f, 0.f, mArrowLengthMeters * -MANIPULATOR_HOTSPOT_END, 1.f);
+
+ S32 num_arrow_manips = numManips;
+
+ // planar manipulators
+ BOOL planar_manip_yz_visible = FALSE;
+ BOOL planar_manip_xz_visible = FALSE;
+ BOOL planar_manip_xy_visible = FALSE;
+
+ mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_yz_visible = TRUE;
+ }
+
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_xz_visible = TRUE;
+ }
+
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f - PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ mManipulatorVertices[numManips] = LLVector4(mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), mPlaneManipOffsetMeters * (1.f + PLANE_TICK_SIZE * 0.5f), 0.f, 1.f);
+ mManipulatorVertices[numManips++].scaleVec(mPlaneManipPositions);
+ if (llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ planar_manip_xy_visible = TRUE;
+ }
+
+ for (S32 i = 0; i < num_arrow_manips; i+= 2)
+ {
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 10.f); // 10 pixel hotspot for arrows
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_yz_visible)
+ {
+ S32 i = num_arrow_manips;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_xz_visible)
+ {
+ S32 i = num_arrow_manips + 2;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ if (planar_manip_xy_visible)
+ {
+ S32 i = num_arrow_manips + 4;
+ LLVector4 projected_start = mManipulatorVertices[i] * transform;
+ projected_start = projected_start * (1.f / projected_start.mV[VW]);
+
+ LLVector4 projected_end = mManipulatorVertices[i + 1] * transform;
+ projected_end = projected_end * (1.f / projected_end.mV[VW]);
+
+ ManipulatorHandle* projManipulator =
+ new ManipulatorHandle(LLVector3(projected_start.mV[VX], projected_start.mV[VY], projected_start.mV[VZ]),
+ LLVector3(projected_end.mV[VX], projected_end.mV[VY], projected_end.mV[VZ]),
+ MANIPULATOR_IDS[i / 2],
+ 20.f); // 20 pixels for planar manipulators
+ mProjectedManipulators.addDataSorted(projManipulator);
+ }
+
+ LLVector2 manip_start_2d;
+ LLVector2 manip_end_2d;
+ LLVector2 manip_dir;
+ F32 half_width = gViewerWindow->getWindowWidth() / 2.f;
+ F32 half_height = gViewerWindow->getWindowHeight() / 2.f;
+ LLVector2 mousePos((F32)x - half_width, (F32)y - half_height);
+ LLVector2 mouse_delta;
+
+ for (ManipulatorHandle* manipulator = mProjectedManipulators.getFirstData();
+ manipulator;
+ manipulator = mProjectedManipulators.getNextData())
+ {
+ manip_start_2d.setVec(manipulator->mStartPosition.mV[VX] * half_width, manipulator->mStartPosition.mV[VY] * half_height);
+ manip_end_2d.setVec(manipulator->mEndPosition.mV[VX] * half_width, manipulator->mEndPosition.mV[VY] * half_height);
+ manip_dir = manip_end_2d - manip_start_2d;
+
+ mouse_delta = mousePos - manip_start_2d;
+
+ F32 manip_length = manip_dir.normVec();
+
+ F32 mouse_pos_manip = mouse_delta * manip_dir;
+ F32 mouse_dist_manip_squared = mouse_delta.magVecSquared() - (mouse_pos_manip * mouse_pos_manip);
+
+ if (mouse_pos_manip > 0.f &&
+ mouse_pos_manip < manip_length &&
+ mouse_dist_manip_squared < manipulator->mHotSpotRadius * manipulator->mHotSpotRadius)
+ {
+ mHighlightedPart = manipulator->mManipID;
+ break;
+ }
+ }
+}
+
+F32 LLManipTranslate::getMinGridScale()
+{
+ F32 scale;
+ switch (mManipPart)
+ {
+ case LL_NO_PART:
+ default:
+ scale = 1.f;
+ break;
+ case LL_X_ARROW:
+ scale = mGridScale.mV[VX];
+ break;
+ case LL_Y_ARROW:
+ scale = mGridScale.mV[VY];
+ break;
+ case LL_Z_ARROW:
+ scale = mGridScale.mV[VZ];
+ break;
+ case LL_YZ_PLANE:
+ scale = llmin(mGridScale.mV[VY], mGridScale.mV[VZ]);
+ break;
+ case LL_XZ_PLANE:
+ scale = llmin(mGridScale.mV[VX], mGridScale.mV[VZ]);
+ break;
+ case LL_XY_PLANE:
+ scale = llmin(mGridScale.mV[VX], mGridScale.mV[VY]);
+ break;
+ }
+
+ return scale;
+}
+
+
+BOOL LLManipTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // first, perform normal processing in case this was a quick-click
+ handleHover(x, y, mask);
+
+ // make sure arrow colors go back to normal
+ mManipPart = LL_NO_PART;
+ gSelectMgr->enableSilhouette(TRUE);
+
+ // Might have missed last update due to UPDATE_DELAY timing.
+ if (mSendUpdateOnMouseUp)
+ {
+ gSelectMgr->sendMultipleUpdate( UPD_POSITION );
+ mSendUpdateOnMouseUp = FALSE;
+ }
+
+// if (mCopyMadeThisDrag)
+// {
+// gSelectMgr->clearGridObjects();
+// }
+
+ mInSnapRegime = FALSE;
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ return LLManip::handleMouseUp(x, y, mask);
+}
+
+
+void LLManipTranslate::render()
+{
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderGuidelines();
+ }
+ {
+ renderTranslationHandles();
+ renderSnapGuides();
+ }
+ glPopMatrix();
+
+ renderText();
+}
+
+void LLManipTranslate::renderSnapGuides()
+{
+ if (!gSavedSettings.getBOOL("SnapEnabled"))
+ {
+ return;
+ }
+
+ F32 max_subdivisions = sGridMaxSubdivisionLevel;//(F32)gSavedSettings.getS32("GridSubdivision");
+ F32 line_alpha = gSavedSettings.getF32("GridOpacity");
+
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLGLDisable gls_cull(GL_CULL_FACE);
+ LLVector3 translate_axis;
+
+ if (mManipPart == LL_NO_PART)
+ {
+ return;
+ }
+
+ LLSelectNode *first_node = gSelectMgr->getFirstMoveableNode(TRUE);
+ if (!first_node)
+ {
+ return;
+ }
+
+ updateGridSettings();
+
+ F32 smallest_grid_unit_scale = getMinGridScale() / max_subdivisions;
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+ LLVector3 saved_selection_center = getSavedPivotPoint(); //gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+ LLVector3 selection_center = getPivotPoint();
+
+ LLViewerObject *first_object = first_node->getObject();
+
+ //pick appropriate projection plane for snap rulers according to relative camera position
+ if (mManipPart >= LL_X_ARROW && mManipPart <= LL_Z_ARROW)
+ {
+ getManipAxis(first_object, mManipPart, translate_axis);
+
+ LLVector3 at_axis_abs;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ at_axis_abs = LLVector3::x_axis * ~grid_rotation;
+ }
+ else
+ {
+ at_axis_abs = saved_selection_center - gCamera->getOrigin();
+ at_axis_abs.normVec();
+
+ at_axis_abs = at_axis_abs * ~grid_rotation;
+ }
+ at_axis_abs.abs();
+
+ if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY] && at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
+ {
+ if (mManipPart == LL_Y_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else if (mManipPart == LL_Z_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ }
+ else if (at_axis_abs.mV[VY] > at_axis_abs.mV[VZ])
+ {
+ if (mManipPart == LL_X_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else if (mManipPart == LL_Z_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VZ])
+ {
+ mSnapOffsetAxis = LLVector3::z_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ }
+ else
+ {
+ if (mManipPart == LL_X_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else if (mManipPart == LL_Y_ARROW)
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ else if (at_axis_abs.mV[VX] > at_axis_abs.mV[VY])
+ {
+ mSnapOffsetAxis = LLVector3::y_axis;
+ }
+ else
+ {
+ mSnapOffsetAxis = LLVector3::x_axis;
+ }
+ }
+
+ mSnapOffsetAxis = mSnapOffsetAxis * grid_rotation;
+
+ F32 guide_size_meters;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ guide_size_meters = 1.f / gAgent.getAvatarObject()->mHUDCurZoom;
+ mSnapOffsetMeters = mArrowLengthMeters * 1.5f;
+ }
+ else
+ {
+ LLVector3 cam_to_selection = getPivotPoint() - gCamera->getOrigin();
+ F32 current_range = cam_to_selection.normVec();
+ guide_size_meters = SNAP_GUIDE_SCREEN_SIZE * gViewerWindow->getWindowHeight() * current_range / gCamera->getPixelMeterRatio();
+
+ F32 fraction_of_fov = mAxisArrowLength / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ F32 offset_at_camera = tan(apparent_angle) * 1.5f;
+ F32 range = dist_vec(gAgent.getPosAgentFromGlobal(first_node->mSavedPositionGlobal), gCamera->getOrigin());
+ mSnapOffsetMeters = range * offset_at_camera;
+ }
+
+ LLVector3 tick_start;
+ LLVector3 tick_end;
+
+ // how far away from grid origin is the selection along the axis of translation?
+ F32 dist_grid_axis = (selection_center - mGridOrigin) * translate_axis;
+ // 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(fmod(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);
+
+ for (S32 pass = 0; pass < 3; pass++)
+ {
+ LLColor4 line_color = setupSnapGuideRenderPass(pass);
+
+ glBegin(GL_LINES);
+ {
+ LLVector3 line_start = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) + (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
+ LLVector3 line_end = selection_center + (mSnapOffsetMeters * mSnapOffsetAxis) - (translate_axis * (guide_size_meters * 0.5f + offset_nearest_grid_unit));
+ LLVector3 line_mid = (line_start + line_end) * 0.5f;
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_start.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_end.mV);
+
+ line_start.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) + (translate_axis * guide_size_meters * 0.5f));
+ line_end.setVec(selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) - (translate_axis * guide_size_meters * 0.5f));
+ line_mid = (line_start + line_end) * 0.5f;
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_start.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(line_mid.mV);
+ glVertex3fv(line_mid.mV);
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW] * 0.2f);
+ glVertex3fv(line_end.mV);
+
+ for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
+ {
+ tick_start = selection_center + (translate_axis * (smallest_grid_unit_scale * (F32)i - offset_nearest_grid_unit));
+
+ F32 cur_subdivisions = llclamp(getSubdivisionLevel(tick_start, translate_axis, getMinGridScale()), sGridMinSubdivisionLevel, sGridMaxSubdivisionLevel);
+
+ if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / cur_subdivisions)) != 0.f)
+ {
+ continue;
+ }
+
+ // add in off-axis offset
+ tick_start += (mSnapOffsetAxis * mSnapOffsetMeters);
+
+ BOOL is_sub_tick = FALSE;
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ is_sub_tick = TRUE;
+ }
+
+// S32 num_ticks_to_fade = is_sub_tick ? num_ticks_per_side / 2 : num_ticks_per_side;
+// F32 alpha = line_alpha * (1.f - (0.8f * ((F32)llabs(i) / (F32)num_ticks_to_fade)));
+
+ tick_end = tick_start + (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
+
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+
+ tick_start = selection_center + (mSnapOffsetAxis * -mSnapOffsetMeters) +
+ (translate_axis * (getMinGridScale() / (F32)(max_subdivisions) * (F32)i - offset_nearest_grid_unit));
+ tick_end = tick_start - (mSnapOffsetAxis * mSnapOffsetMeters * tick_scale);
+
+ glVertex3fv(tick_start.mV);
+ glVertex3fv(tick_end.mV);
+ }
+ }
+ glEnd();
+
+ if (mInSnapRegime)
+ {
+ LLVector3 line_start = selection_center - mSnapOffsetAxis * mSnapOffsetMeters;
+ LLVector3 line_end = selection_center + mSnapOffsetAxis * mSnapOffsetMeters;
+
+ glBegin(GL_LINES);
+ {
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+
+ glVertex3fv(line_start.mV);
+ glVertex3fv(line_end.mV);
+ }
+ glEnd();
+
+ // draw snap guide arrow
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4f(line_color.mV[VX], line_color.mV[VY], line_color.mV[VZ], line_color.mV[VW]);
+
+ LLVector3 arrow_dir;
+ LLVector3 arrow_span = translate_axis;
+
+ arrow_dir = -mSnapOffsetAxis;
+ glVertex3fv((line_start + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_start + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_start - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+
+ arrow_dir = mSnapOffsetAxis;
+ glVertex3fv((line_end + arrow_dir * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_end + arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ glVertex3fv((line_end - arrow_span * mConeSize * SNAP_ARROW_SCALE).mV);
+ }
+ glEnd();
+ }
+ }
+
+ sub_div_offset = llround(fmod(dist_grid_axis - offset_nearest_grid_unit, getMinGridScale() * 32.f) / smallest_grid_unit_scale);
+
+ LLVector2 screen_translate_axis(llabs(translate_axis * gCamera->getLeftAxis()), llabs(translate_axis * gCamera->getUpAxis()));
+ screen_translate_axis.normVec();
+
+ S32 tick_label_spacing = llround(screen_translate_axis * sTickLabelSpacing);
+
+ // render tickmark values
+ for (S32 i = -num_ticks_per_side; i <= num_ticks_per_side; i++)
+ {
+ LLVector3 tick_pos = selection_center + (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit));
+ F32 alpha = line_alpha * (1.f - (0.5f * ((F32)llabs(i) / (F32)num_ticks_per_side)));
+
+ F32 tick_scale = 1.f;
+ for (F32 division_level = max_subdivisions; division_level >= sGridMinSubdivisionLevel; division_level /= 2.f)
+ {
+ if (fmodf((F32)(i + sub_div_offset), division_level) == 0.f)
+ {
+ break;
+ }
+ tick_scale *= 0.7f;
+ }
+
+ if (fmodf((F32)(i + sub_div_offset), (max_subdivisions / llmin(sGridMaxSubdivisionLevel, getSubdivisionLevel(tick_pos, translate_axis, getMinGridScale(), tick_label_spacing)))) == 0.f)
+ {
+ F32 snap_offset_meters;
+
+ if (mSnapOffsetAxis * gCamera->getUpAxis() > 0.f)
+ {
+ snap_offset_meters = mSnapOffsetMeters;
+ }
+ else
+ {
+ snap_offset_meters = -mSnapOffsetMeters;
+ }
+ LLVector3 text_origin = selection_center +
+ (translate_axis * ((smallest_grid_unit_scale * (F32)i) - offset_nearest_grid_unit)) +
+ (mSnapOffsetAxis * snap_offset_meters * (1.f + tick_scale));
+
+ LLVector3 tick_offset = (tick_pos - mGridOrigin) * ~mGridRotation;
+ F32 offset_val = 0.5f * tick_offset.mV[ARROW_TO_AXIS[mManipPart]] / getMinGridScale();
+ EGridMode grid_mode = gSelectMgr->getGridMode();
+ F32 text_highlight = 0.8f;
+ if(i - llround(offset_nearest_grid_unit / smallest_grid_unit_scale) == 0 && mInSnapRegime)
+ {
+ text_highlight = 1.f;
+ }
+
+ if (grid_mode == GRID_MODE_WORLD)
+ {
+ // rescale units to meters from multiple of grid scale
+ offset_val *= 2.f * grid_scale[ARROW_TO_AXIS[mManipPart]];
+ renderTickValue(text_origin, offset_val, "m", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ else
+ {
+ renderTickValue(text_origin, offset_val, "x", LLColor4(text_highlight, text_highlight, text_highlight, alpha));
+ }
+ }
+ }
+ if (gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ // render helpful text
+ if (mHelpTextTimer.getElapsedTimeF32() < sHelpTextVisibleTime + sHelpTextFadeTime && sNumTimesHelpTextShown < sMaxTimesShowHelpText)
+ {
+ F32 snap_offset_meters_up;
+ if (mSnapOffsetAxis * gCamera->getUpAxis() > 0.f)
+ {
+ snap_offset_meters_up = mSnapOffsetMeters;
+ }
+ else
+ {
+ snap_offset_meters_up = -mSnapOffsetMeters;
+ }
+
+ LLVector3 selection_center_start = getSavedPivotPoint();//gSelectMgr->getSavedBBoxOfSelection().getCenterAgent();
+
+ LLVector3 help_text_pos = selection_center_start + (snap_offset_meters_up * 3.f * mSnapOffsetAxis);
+ const LLFontGL* big_fontp = LLFontGL::sSansSerif;
+
+ LLGLSTexture gls_texture;
+ std::string help_text = "Move mouse cursor over ruler to snap";
+ LLColor4 help_text_color = LLColor4::white;
+ help_text_color.mV[VALPHA] = clamp_rescale(mHelpTextTimer.getElapsedTimeF32(), sHelpTextVisibleTime, sHelpTextVisibleTime + sHelpTextFadeTime, line_alpha, 0.f);
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ help_text = "to snap to grid";
+ help_text_pos -= gCamera->getUpAxis() * mSnapOffsetMeters * 0.2f;
+ hud_render_utf8text(help_text, help_text_pos, *big_fontp, LLFontGL::NORMAL, -0.5f * big_fontp->getWidthF32(help_text), 3.f, help_text_color, gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ }
+ }
+ }
+ else
+ {
+ // render gridlines for planar snapping
+
+ F32 u = 0, v = 0;
+ glPushMatrix();
+
+ F32 x,y,z,angle_radians;
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+ glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLVector3 grid_center = selection_center - grid_origin;
+ grid_center *= ~grid_rotation;
+
+ F32 usc = 1;
+ F32 vsc = 1;
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ u = grid_center.mV[VY];
+ v = grid_center.mV[VZ];
+ usc = grid_scale.mV[VY];
+ vsc = grid_scale.mV[VZ];
+ break;
+ case LL_XZ_PLANE:
+ u = grid_center.mV[VX];
+ v = grid_center.mV[VZ];
+ usc = grid_scale.mV[VX];
+ vsc = grid_scale.mV[VZ];
+ break;
+ case LL_XY_PLANE:
+ u = grid_center.mV[VX];
+ v = grid_center.mV[VY];
+ usc = grid_scale.mV[VX];
+ vsc = grid_scale.mV[VY];
+ break;
+ default:
+ break;
+ }
+
+ F32 sz = mGridSizeMeters;
+ F32 tiles = sz;
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ usc = 1.0f/usc;
+ vsc = 1.0f/vsc;
+
+ while (usc > vsc*4.0f)
+ {
+ usc *= 0.5f;
+ }
+ while (vsc > usc * 4.0f)
+ {
+ vsc *= 0.5f;
+ }
+
+ glScalef(usc, vsc, 1.0f);
+ glTranslatef(u, v, 0);
+
+ float a = line_alpha;
+
+ LLColor4 col = gColors.getColor("SilhouetteChildColor");
+ {
+ //draw grid behind objects
+ LLGLSTexture tex2d;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);
+ renderGrid(u,v,tiles,0.9f, 0.9f, 0.9f,a*0.15f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ {
+ LLGLDisable alpha_test(GL_ALPHA_TEST);
+ //draw black overlay
+ glBindTexture(GL_TEXTURE_2D, 0);
+ renderGrid(u,v,tiles,0.0f, 0.0f, 0.0f,a*0.16f);
+
+ //draw grid top
+ glBindTexture(GL_TEXTURE_2D, sGridTex);
+ renderGrid(u,v,tiles,1,1,1,a);
+
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ renderGuidelines();
+ }
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GREATER);
+ LLGLEnable stipple(GL_LINE_STIPPLE);
+ glLineStipple(1, 0x3333);
+
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ renderGuidelines(FALSE, TRUE, TRUE);
+ break;
+ case LL_XZ_PLANE:
+ renderGuidelines(TRUE, FALSE, TRUE);
+ break;
+ case LL_XY_PLANE:
+ renderGuidelines(TRUE, TRUE, FALSE);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void LLManipTranslate::renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a)
+{
+ F32 d = size*0.5f;
+
+ for (F32 xx = -size-d; xx < size+d; xx += d)
+ {
+ glBegin(GL_TRIANGLE_STRIP);
+ for (F32 yy = -size-d; yy < size+d; yy += d)
+ {
+ float dx, dy, da;
+
+ dx = xx; dy = yy;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx+d; dy = yy;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx; dy = yy+d;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+
+ dx = xx+d; dy = yy+d;
+ da = sqrtf(llmax(0.0f, 1.0f-sqrtf(dx*dx+dy*dy)/size))*a;
+ glTexCoord2f(dx, dy);
+ renderGridVert(dx,dy,r,g,b,da);
+ }
+ glEnd();
+ }
+
+
+}
+
+
+void LLManipTranslate::renderText()
+{
+ if (gSelectMgr->getRootObjectCount() && !gSelectMgr->selectionIsAttachment())
+ {
+ LLVector3 pos = getPivotPoint();
+ renderXYZ(pos);
+ }
+ else
+ {
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ }
+
+ if (objectp)
+ {
+ renderXYZ(objectp->getPositionEdit());
+ }
+ }
+}
+
+void LLManipTranslate::renderTranslationHandles()
+{
+ LLVector3 grid_origin;
+ LLVector3 grid_scale;
+ LLQuaternion grid_rotation;
+ LLGLDepthTest gls_depth(GL_FALSE);
+
+ gSelectMgr->getGrid(grid_origin, grid_rotation, grid_scale);
+ LLVector3 at_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ at_axis = LLVector3::x_axis * ~grid_rotation;
+ }
+ else
+ {
+ at_axis = gCamera->getAtAxis() * ~grid_rotation;
+ }
+
+ if (at_axis.mV[VX] > 0.f)
+ {
+ mPlaneManipPositions.mV[VX] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VX] = -1.f;
+ }
+
+ if (at_axis.mV[VY] > 0.f)
+ {
+ mPlaneManipPositions.mV[VY] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VY] = -1.f;
+ }
+
+ if (at_axis.mV[VZ] > 0.f)
+ {
+ mPlaneManipPositions.mV[VZ] = 1.f;
+ }
+ else
+ {
+ mPlaneManipPositions.mV[VZ] = -1.f;
+ }
+
+ LLViewerObject *first_object = gSelectMgr->getFirstMoveableObject(TRUE);
+ if (!first_object) return;
+
+ LLVector3 selection_center = getPivotPoint();
+
+ // Drag handles
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ mArrowLengthMeters = mAxisArrowLength / gViewerWindow->getWindowHeight();
+ mArrowLengthMeters /= gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ LLVector3 camera_pos_agent = gAgent.getCameraPositionAgent();
+ F32 range = dist_vec(camera_pos_agent, selection_center);
+ F32 range_from_agent = dist_vec(gAgent.getPositionAgent(), selection_center);
+
+ // Don't draw handles if you're too far away
+ if (gSavedSettings.getBOOL("LimitSelectDistance"))
+ {
+ if (range_from_agent > gSavedSettings.getF32("MaxSelectDistance"))
+ {
+ return;
+ }
+ }
+
+ if (range > 0.001f)
+ {
+ // range != zero
+ F32 fraction_of_fov = mAxisArrowLength / (F32) gCamera->getViewHeightInPixels();
+ F32 apparent_angle = fraction_of_fov * gCamera->getView(); // radians
+ mArrowLengthMeters = range * tan(apparent_angle);
+ }
+ else
+ {
+ // range == zero
+ mArrowLengthMeters = 1.0f;
+ }
+ }
+
+ mPlaneManipOffsetMeters = mArrowLengthMeters * 1.8f;
+ mGridSizeMeters = gSavedSettings.getF32("GridDrawSize");
+ mConeSize = mArrowLengthMeters / 4.f;
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ {
+ glTranslatef(selection_center.mV[VX], selection_center.mV[VY], selection_center.mV[VZ]);
+
+ F32 angle_radians, x, y, z;
+ grid_rotation.getAngleAxis(&angle_radians, &x, &y, &z);
+
+ glRotatef(angle_radians * RAD_TO_DEG, x, y, z);
+
+ LLQuaternion invRotation = grid_rotation;
+ invRotation.conjQuat();
+
+ LLVector3 relative_camera_dir;
+
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ relative_camera_dir = LLVector3::x_axis * invRotation;
+ }
+ else
+ {
+ relative_camera_dir = (selection_center - gCamera->getOrigin()) * invRotation;
+ }
+ relative_camera_dir.normVec();
+
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ LLColor4 color1;
+ LLColor4 color2;
+
+ // update manipulator sizes
+ for (S32 index = 0; index < 3; index++)
+ {
+ if (index == mManipPart - LL_X_ARROW || index == mHighlightedPart - LL_X_ARROW)
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ else if (index == mManipPart - LL_YZ_PLANE || index == mHighlightedPart - LL_YZ_PLANE)
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], SELECTED_ARROW_SCALE, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ else
+ {
+ mArrowScales.mV[index] = lerp(mArrowScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ mPlaneScales.mV[index] = lerp(mPlaneScales.mV[index], 1.f, LLCriticalDamp::getInterpolant(MANIPULATOR_SCALE_HALF_LIFE ));
+ }
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_YZ_PLANE) && llabs(relative_camera_dir.mV[VX]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render YZ plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+ glTranslatef(0.f, mPlaneManipOffsetMeters, mPlaneManipOffsetMeters);
+ glScalef(mPlaneScales.mV[VX], mPlaneScales.mV[VX], mPlaneScales.mV[VX]);
+ if (mHighlightedPart == LL_YZ_PLANE)
+ {
+ color1.setVec(0.f, 1.f, 0.f, 1.f);
+ color2.setVec(0.f, 0.f, 1.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.f, 1.f, 0.f, 0.6f);
+ color2.setVec(0.f, 0.f, 1.f, 0.6f);
+ }
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+
+ glColor4fv(color2.mV);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
+
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ glPopMatrix();
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_XZ_PLANE) && llabs(relative_camera_dir.mV[VY]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render XZ plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+ glTranslatef(mPlaneManipOffsetMeters, 0.f, mPlaneManipOffsetMeters);
+ glScalef(mPlaneScales.mV[VY], mPlaneScales.mV[VY], mPlaneScales.mV[VY]);
+ if (mHighlightedPart == LL_XZ_PLANE)
+ {
+ color1.setVec(0.f, 0.f, 1.f, 1.f);
+ color2.setVec(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.f, 0.f, 1.f, 0.6f);
+ color2.setVec(1.f, 0.f, 0.f, 0.6f);
+ }
+
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+
+ glColor4fv(color2.mV);
+ glVertex3f(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f));
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f));
+ glVertex3f(mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f), 0.f, mPlaneManipOffsetMeters * (PLANE_TICK_SIZE * 0.25f));
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f);
+
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.1f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.25f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.25f);
+ glVertex3f(mPlaneManipOffsetMeters * -PLANE_TICK_SIZE * 0.4f, 0.f, mPlaneManipOffsetMeters * PLANE_TICK_SIZE * 0.1f);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+
+ glPopMatrix();
+ }
+
+ if ((mManipPart == LL_NO_PART || mManipPart == LL_XY_PLANE) && llabs(relative_camera_dir.mV[VZ]) > MIN_PLANE_MANIP_DOT_PRODUCT)
+ {
+ // render XY plane manipulator
+ glPushMatrix();
+ glScalef(mPlaneManipPositions.mV[VX], mPlaneManipPositions.mV[VY], mPlaneManipPositions.mV[VZ]);
+
+/* Y
+ ^
+ v1
+ | \
+ |<- v0
+ | /| \
+ v2__v__v3 > X
+*/
+ LLVector3 v0,v1,v2,v3;
+#if 0
+ // This should theoretically work but looks off; could be tuned later -SJB
+ glTranslatef(-mPlaneManipOffsetMeters, -mPlaneManipOffsetMeters, 0.f);
+ v0 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+ v1 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), 0.f);
+ v2 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+ v3 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+#else
+ glTranslatef(mPlaneManipOffsetMeters, mPlaneManipOffsetMeters, 0.f);
+ v0 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.25f), 0.f);
+ v1 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), 0.f);
+ v2 = LLVector3(mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+ v3 = LLVector3(mPlaneManipOffsetMeters * (-PLANE_TICK_SIZE * 0.75f), mPlaneManipOffsetMeters * ( PLANE_TICK_SIZE * 0.25f), 0.f);
+#endif
+ glScalef(mPlaneScales.mV[VZ], mPlaneScales.mV[VZ], mPlaneScales.mV[VZ]);
+ if (mHighlightedPart == LL_XY_PLANE)
+ {
+ color1.setVec(1.f, 0.f, 0.f, 1.f);
+ color2.setVec(0.f, 1.f, 0.f, 1.f);
+ }
+ else
+ {
+ color1.setVec(0.8f, 0.f, 0.f, 0.6f);
+ color2.setVec(0.f, 0.8f, 0.f, 0.6f);
+ }
+
+ glBegin(GL_TRIANGLES);
+ {
+ glColor4fv(color1.mV);
+ glVertex3fv(v0.mV);
+ glVertex3fv(v1.mV);
+ glVertex3fv(v2.mV);
+
+ glColor4fv(color2.mV);
+ glVertex3fv(v2.mV);
+ glVertex3fv(v3.mV);
+ glVertex3fv(v0.mV);
+ }
+ glEnd();
+
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINES);
+ {
+ glColor4f(0.f, 0.f, 0.f, 0.3f);
+ LLVector3 v12 = (v1 + v2) * .5f;
+ glVertex3fv(v0.mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv((v12 + (v0-v12)*.3f + (v2-v12)*.3f).mV);
+ glVertex3fv(v12.mV);
+ glVertex3fv((v12 + (v0-v12)*.3f + (v1-v12)*.3f).mV);
+
+ LLVector3 v23 = (v2 + v3) * .5f;
+ glVertex3fv(v0.mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv((v23 + (v0-v23)*.3f + (v3-v23)*.3f).mV);
+ glVertex3fv(v23.mV);
+ glVertex3fv((v23 + (v0-v23)*.3f + (v2-v23)*.3f).mV);
+ }
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+
+ glPopMatrix();
+ }
+ }
+ {
+ LLGLSNoTexture gls_ui_no_texture;
+
+ // Since we draw handles with depth testing off, we need to draw them in the
+ // proper depth order.
+
+ // Copied from LLDrawable::updateGeometry
+ LLVector3 pos_agent = first_object->getPositionAgent();
+ LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector3 headPos = pos_agent - camera_agent;
+
+ LLVector3 orientWRTHead = headPos * invRotation;
+
+ // Find nearest vertex
+ U32 nearest = (orientWRTHead.mV[0] < 0.0f ? 1 : 0) +
+ (orientWRTHead.mV[1] < 0.0f ? 2 : 0) +
+ (orientWRTHead.mV[2] < 0.0f ? 4 : 0);
+
+ // opposite faces on Linden cubes:
+ // 0 & 5
+ // 1 & 3
+ // 2 & 4
+
+ // Table of order to draw faces, based on nearest vertex
+ static U32 face_list[8][NUM_AXES*2] = {
+ { 2,0,1, 4,5,3 }, // v6 F201 F453
+ { 2,0,3, 4,5,1 }, // v7 F203 F451
+ { 4,0,1, 2,5,3 }, // v5 F401 F253
+ { 4,0,3, 2,5,1 }, // v4 F403 F251
+ { 2,5,1, 4,0,3 }, // v2 F251 F403
+ { 2,5,3, 4,0,1 }, // v3 F253 F401
+ { 4,5,1, 2,0,3 }, // v1 F451 F203
+ { 4,5,3, 2,0,1 }, // v0 F453 F201
+ };
+ static const EManipPart which_arrow[6] = {
+ LL_Z_ARROW,
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_X_ARROW,
+ LL_Y_ARROW,
+ LL_Z_ARROW};
+
+ // draw arrows for deeper faces first, closer faces last
+ LLVector3 camera_axis;
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ camera_axis = LLVector3::x_axis;
+ }
+ else
+ {
+ camera_axis.setVec(gAgent.getCameraPositionAgent() - first_object->getPositionAgent());
+ }
+
+ for (U32 i = 0; i < NUM_AXES*2; i++)
+ {
+ U32 face = face_list[nearest][i];
+
+ LLVector3 arrow_axis;
+ getManipAxis(first_object, which_arrow[face], arrow_axis);
+
+ if (fabs(angle_between(camera_axis, arrow_axis) - F_PI_BY_TWO) < F_PI_BY_TWO - HANDLE_HIDE_ANGLE)
+ {
+ renderArrow(which_arrow[face],
+ mManipPart,
+ (face >= 3) ? -mConeSize : mConeSize,
+ (face >= 3) ? -mArrowLengthMeters : mArrowLengthMeters,
+ mConeSize,
+ FALSE);
+ }
+ }
+ }
+ }
+ glPopMatrix();
+}
+
+
+void LLManipTranslate::renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, BOOL reverse_direction)
+{
+ LLGLSNoTexture gls_ui_no_texture;
+ LLGLEnable gls_blend(GL_BLEND);
+ LLGLEnable gls_color_material(GL_COLOR_MATERIAL);
+
+ for (S32 pass = 1; pass <= 2; pass++)
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, pass == 1 ? GL_LEQUAL : GL_GREATER);
+ glPushMatrix();
+
+ S32 index = 0;
+
+ index = ARROW_TO_AXIS[which_arrow];
+
+ // assign a color for this arrow
+ LLColor4 color; // black
+ if (which_arrow == selected_arrow || which_arrow == mHighlightedPart)
+ {
+ color.mV[index] = (pass == 1) ? 1.f : 0.5f;
+ }
+ else if (selected_arrow != LL_NO_PART)
+ {
+ color.mV[VALPHA] = 0.f;
+ }
+ else
+ {
+ color.mV[index] = pass == 1 ? .8f : .35f ; // red, green, or blue
+ color.mV[VALPHA] = 0.6f;
+ }
+ glColor4fv( color.mV );
+
+ LLVector3 vec;
+
+ {
+// Stipple looks OK, but not great - SJB
+// LLGLEnable stipple(GL_LINE_STIPPLE);
+// glLineStipple(1, 0x3333);
+
+ LLUI::setLineWidth(2.0f);
+ glBegin(GL_LINES);
+ vec.mV[index] = box_size;
+ glVertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
+
+ vec.mV[index] = arrow_size;
+ glVertex3f(vec.mV[0], vec.mV[1], vec.mV[2]);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+
+ glTranslatef(vec.mV[0], vec.mV[1], vec.mV[2]);
+ glScalef(handle_size, handle_size, handle_size);
+
+ F32 rot = 0.0f;
+ LLVector3 axis;
+
+ switch(which_arrow)
+ {
+ case LL_X_ARROW:
+ rot = reverse_direction ? -90.0f : 90.0f;
+ axis.mV[1] = 1.0f;
+ break;
+ case LL_Y_ARROW:
+ rot = reverse_direction ? 90.0f : -90.0f;
+ axis.mV[0] = 1.0f;
+ break;
+ case LL_Z_ARROW:
+ rot = reverse_direction ? 180.0f : 0.0f;
+ axis.mV[0] = 1.0f;
+ break;
+ default:
+ llerrs << "renderArrow called with bad arrow " << which_arrow << llendl;
+ break;
+ }
+
+ glRotatef(rot, axis.mV[0], axis.mV[1], axis.mV[2]);
+ glScalef(mArrowScales.mV[index], mArrowScales.mV[index], mArrowScales.mV[index] * 1.5f);
+
+ gCone.render(CONE_LOD_HIGHEST);
+
+ glPopMatrix();
+ }
+}
+
+void LLManipTranslate::renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha)
+{
+ glColor4f(r, g, b, alpha);
+ switch (mManipPart)
+ {
+ case LL_YZ_PLANE:
+ glVertex3f(0, x_trans, y_trans);
+ break;
+ case LL_XZ_PLANE:
+ glVertex3f(x_trans, 0, y_trans);
+ break;
+ case LL_XY_PLANE:
+ glVertex3f(x_trans, y_trans, 0);
+ break;
+ default:
+ glVertex3f(0,0,0);
+ break;
+ }
+
+}
diff --git a/indra/newview/llmaniptranslate.h b/indra/newview/llmaniptranslate.h
new file mode 100644
index 0000000000..5a93c0a24f
--- /dev/null
+++ b/indra/newview/llmaniptranslate.h
@@ -0,0 +1,97 @@
+/**
+ * @file llmaniptranslate.h
+ * @brief LLManipTranslate class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMANIPTRANSLATE_H
+#define LL_LLMANIPTRANSLATE_H
+
+#include "llmanip.h"
+#include "lltimer.h"
+#include "v4math.h"
+#include "linked_lists.h"
+#include "llquaternion.h"
+
+class LLManipTranslate : public LLManip
+{
+public:
+ class ManipulatorHandle
+ {
+ public:
+ LLVector3 mStartPosition;
+ LLVector3 mEndPosition;
+ EManipPart mManipID;
+ F32 mHotSpotRadius;
+
+ ManipulatorHandle(LLVector3 start_pos, LLVector3 end_pos, EManipPart id, F32 radius):mStartPosition(start_pos), mEndPosition(end_pos), mManipID(id), mHotSpotRadius(radius){}
+ };
+
+
+ LLManipTranslate( LLToolComposite* composite );
+ virtual ~LLManipTranslate();
+
+ static void restoreGL();
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void render();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ EManipPart getHighlightedPart() { return mHighlightedPart; }
+ virtual void highlightManipulators(S32 x, S32 y);
+ /*virtual*/ BOOL handleMouseDownOnPart(S32 x, S32 y, MASK mask);
+
+protected:
+ enum EHandleType {
+ HANDLE_CONE,
+ HANDLE_BOX,
+ HANDLE_SPHERE
+ };
+
+ void renderArrow(S32 which_arrow, S32 selected_arrow, F32 box_size, F32 arrow_size, F32 handle_size, BOOL reverse_direction);
+ void renderTranslationHandles();
+ void renderText();
+ void renderSnapGuides();
+ void renderGrid(F32 x, F32 y, F32 size, F32 r, F32 g, F32 b, F32 a);
+ void renderGridVert(F32 x_trans, F32 y_trans, F32 r, F32 g, F32 b, F32 alpha);
+ F32 getMinGridScale();
+
+private:
+ S32 mLastHoverMouseX;
+ S32 mLastHoverMouseY;
+ BOOL mSendUpdateOnMouseUp;
+ BOOL mMouseOutsideSlop; // true after mouse goes outside slop region
+ BOOL mCopyMadeThisDrag;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ F32 mAxisArrowLength; // pixels
+ F32 mConeSize; // meters, world space
+ F32 mArrowLengthMeters; // meters
+ F32 mGridSizeMeters;
+ F32 mPlaneManipOffsetMeters;
+ EManipPart mManipPart;
+ LLVector3 mManipNormal;
+ LLVector3d mDragCursorStartGlobal;
+ LLVector3d mDragSelectionStartGlobal;
+ LLTimer mUpdateTimer;
+ LLLinkedList<ManipulatorHandle> mProjectedManipulators;
+ LLVector4 mManipulatorVertices[18];
+ EManipPart mHighlightedPart;
+ F32 mSnapOffsetMeters;
+ LLVector3 mSnapOffsetAxis;
+ LLQuaternion mGridRotation;
+ LLVector3 mGridOrigin;
+ LLVector3 mGridScale;
+ F32 mSubdivisions;
+ BOOL mInSnapRegime;
+ BOOL mSnapped;
+ LLVector3 mArrowScales;
+ LLVector3 mPlaneScales;
+ LLVector4 mPlaneManipPositions;
+};
+
+#endif
diff --git a/indra/newview/llmemoryview.cpp b/indra/newview/llmemoryview.cpp
new file mode 100644
index 0000000000..5edb373cbb
--- /dev/null
+++ b/indra/newview/llmemoryview.cpp
@@ -0,0 +1,223 @@
+/**
+ * @file llmemoryview.cpp
+ * @brief LLMemoryView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "indra_constants.h"
+#include "llmemoryview.h"
+
+#include "llrect.h"
+#include "llerror.h"
+#include "llgl.h"
+#include "llmath.h"
+#include "llfontgl.h"
+
+#include "viewer.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llstat.h"
+
+#include "llfasttimer.h"
+
+LLMemoryView::LLMemoryView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, TRUE)
+{
+ setVisible(FALSE);
+}
+
+LLMemoryView::~LLMemoryView()
+{
+}
+
+EWidgetType LLMemoryView::getWidgetType() const
+{
+ return WIDGET_TYPE_MEMORY_VIEW;
+}
+
+LLString LLMemoryView::getWidgetTag() const
+{
+ return LL_MEMORY_VIEW_TAG;
+}
+
+BOOL LLMemoryView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mask & MASK_SHIFT)
+ {
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ }
+ else
+ {
+ }
+ return TRUE;
+}
+
+BOOL LLMemoryView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return TRUE;
+}
+
+
+BOOL LLMemoryView::handleHover(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+struct mtv_display_info {
+ S32 memtype;
+ const char *desc;
+ LLColor4 *color;
+};
+
+static LLColor4 red0(0.5f, 0.0f, 0.0f, 1.0f);
+
+static struct mtv_display_info mtv_display_table[] =
+{
+ { LLMemType::MTYPE_INIT, "Init", &LLColor4::white },
+ { LLMemType::MTYPE_STARTUP, "Startup", &LLColor4::cyan1 },
+ { LLMemType::MTYPE_MAIN, "Main", &LLColor4::cyan2 },
+ { LLMemType::MTYPE_IMAGEBASE, "ImageBase", &LLColor4::yellow1 },
+ { LLMemType::MTYPE_IMAGERAW, "ImageRaw", &LLColor4::yellow2 },
+ { LLMemType::MTYPE_IMAGEFORMATTED, "ImageFmtd", &LLColor4::yellow3 },
+ { LLMemType::MTYPE_APPFMTIMAGE, "ViewerImageFmt", &LLColor4::orange1 },
+ { LLMemType::MTYPE_APPRAWIMAGE, "ViewerImageRaw", &LLColor4::orange2 },
+ { LLMemType::MTYPE_APPAUXRAWIMAGE, "ViewerImageAux", &LLColor4::orange3 },
+ { LLMemType::MTYPE_DRAWABLE, "Drawable", &LLColor4::green1 },
+ { LLMemType::MTYPE_OBJECT, "ViewerObject", &LLColor4::green2 },
+ { LLMemType::MTYPE_PIPELINE, "Pipeline", &LLColor4::green3 },
+ { LLMemType::MTYPE_PARTICLES, "Particles", &LLColor4::green4 },
+ { LLMemType::MTYPE_SPACE_PARTITION, "Space Partition", &LLColor4::blue2 },
+ { LLMemType::MTYPE_AVATAR, "Avatar", &LLColor4::purple1 },
+ { LLMemType::MTYPE_REGIONS, "Regions", &LLColor4::blue1 },
+ { LLMemType::MTYPE_TEMP1, "Temp1", &LLColor4::red1 },
+ { LLMemType::MTYPE_TEMP2, "Temp2", &LLColor4::magenta1 },
+ { LLMemType::MTYPE_TEMP3, "Temp3", &LLColor4::red2 },
+ { LLMemType::MTYPE_TEMP4, "Temp4", &LLColor4::magenta2 },
+ { LLMemType::MTYPE_TEMP5, "Temp5", &LLColor4::red3 },
+ { LLMemType::MTYPE_TEMP6, "Temp6", &LLColor4::magenta3 },
+ { LLMemType::MTYPE_TEMP7, "Temp7", &LLColor4::red4 },
+ { LLMemType::MTYPE_TEMP8, "Temp8", &LLColor4::magenta4 },
+
+ { LLMemType::MTYPE_OTHER, "Other", &red0 },
+};
+static const int MTV_DISPLAY_NUM = (sizeof(mtv_display_table)/sizeof(mtv_display_table[0]));
+
+void LLMemoryView::draw()
+{
+ std::string tdesc;
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_tex;
+ gl_rect_2d(0, height, width, 0, LLColor4(0.f, 0.f, 0.f, 0.25f));
+
+#if MEM_TRACK_TYPE
+
+ S32 left, top, right, bottom;
+ S32 x, y;
+
+ S32 margin = 10;
+ S32 texth = (S32)LLFontGL::sMonospace->getLineHeight();
+
+ S32 xleft = margin;
+ S32 ytop = height - margin;
+ S32 labelwidth = 0;
+ S32 maxmaxbytes = 1;
+
+ // Make sure all timers are accounted for
+ // Set 'MT_OTHER' to unaccounted ticks last frame
+ {
+ S32 display_memtypes[LLMemType::MTYPE_NUM_TYPES];
+ for (S32 i=0; i < LLMemType::MTYPE_NUM_TYPES; i++)
+ {
+ display_memtypes[i] = 0;
+ }
+ for (S32 i=0; i < MTV_DISPLAY_NUM; i++)
+ {
+ S32 tidx = mtv_display_table[i].memtype;
+ display_memtypes[tidx]++;
+ }
+ LLMemType::sMemCount[LLMemType::MTYPE_OTHER] = 0;
+ LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] = 0;
+ for (S32 tidx = 0; tidx < LLMemType::MTYPE_NUM_TYPES; tidx++)
+ {
+ if (display_memtypes[tidx] == 0)
+ {
+ LLMemType::sMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMemCount[tidx];
+ LLMemType::sMaxMemCount[LLMemType::MTYPE_OTHER] += LLMemType::sMaxMemCount[tidx];
+ }
+ }
+ }
+
+ // Labels
+ {
+ LLGLSTexture gls_texture;
+ y = ytop;
+ S32 peak = 0;
+ for (S32 i=0; i<MTV_DISPLAY_NUM; i++)
+ {
+ x = xleft;
+
+ int tidx = mtv_display_table[i].memtype;
+ S32 bytes = LLMemType::sMemCount[tidx];
+ S32 maxbytes = LLMemType::sMaxMemCount[tidx];
+ maxmaxbytes = llmax(maxbytes, maxmaxbytes);
+ peak += maxbytes;
+ S32 mbytes = bytes >> 20;
+
+ tdesc = llformat("%s [%4d MB]",mtv_display_table[i].desc,mbytes);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+
+ y -= (texth + 2);
+
+ S32 textw = LLFontGL::sMonospace->getWidth(tdesc);
+ if (textw > labelwidth)
+ labelwidth = textw;
+ }
+ x = xleft;
+ tdesc = llformat("Total Bytes: %d MB Overhead: %d KB",
+ LLMemType::sTotalMem >> 20, LLMemType::sOverheadMem >> 10);
+ LLFontGL::sMonospace->renderUTF8(tdesc, 0, x, y, LLColor4::white, LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ // Bars
+ y = ytop;
+ labelwidth += 8;
+ S32 barw = width - labelwidth - xleft - margin;
+ for (S32 i=0; i<MTV_DISPLAY_NUM; i++)
+ {
+ x = xleft + labelwidth;
+
+ int tidx = mtv_display_table[i].memtype;
+ S32 bytes = LLMemType::sMemCount[tidx];
+ F32 frac = (F32)bytes / (F32)maxmaxbytes;
+ S32 w = (S32)(frac * (F32)barw);
+ left = x; right = x + w;
+ top = y; bottom = y - texth;
+ gl_rect_2d(left, top, right, bottom, *mtv_display_table[i].color);
+
+ S32 maxbytes = LLMemType::sMaxMemCount[tidx];
+ F32 frac2 = (F32)maxbytes / (F32)maxmaxbytes;
+ S32 w2 = (S32)(frac2 * (F32)barw);
+ left = x + w + 1; right = x + w2;
+ top = y; bottom = y - texth;
+ LLColor4 tcolor = *mtv_display_table[i].color;
+ tcolor.setAlpha(.5f);
+ gl_rect_2d(left, top, right, bottom, tcolor);
+
+ y -= (texth + 2);
+ }
+
+#endif
+
+ LLView::draw();
+}
diff --git a/indra/newview/llmemoryview.h b/indra/newview/llmemoryview.h
new file mode 100644
index 0000000000..f32242d06c
--- /dev/null
+++ b/indra/newview/llmemoryview.h
@@ -0,0 +1,31 @@
+/**
+ * @file llmemoryview.h
+ * @brief LLMemoryView class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMEMORYVIEW_H
+#define LL_LLMEMORYVIEW_H
+
+#include "llview.h"
+
+class LLMemoryView : public LLView
+{
+public:
+ LLMemoryView(const std::string& name, const LLRect& rect);
+ virtual ~LLMemoryView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void draw();
+
+private:
+};
+
+#endif
diff --git a/indra/newview/llmenucommands.cpp b/indra/newview/llmenucommands.cpp
new file mode 100644
index 0000000000..a3e0ab93b0
--- /dev/null
+++ b/indra/newview/llmenucommands.cpp
@@ -0,0 +1,135 @@
+/**
+ * @file llmenucommands.cpp
+ * @brief Implementations of menu commands.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmenucommands.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llcallingcard.h"
+#include "llchatbar.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloaterchat.h"
+#include "llfloaterclothing.h"
+#include "llfloaterdirectory.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llgivemoney.h"
+#include "llinventoryview.h"
+#include "llnotify.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewergesture.h" // for triggering gestures
+#include "llviewermessage.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "viewer.h"
+#include "llfocusmgr.h"
+
+void handle_track_avatar(const LLUUID& agent_id, const std::string& name)
+{
+ LLAvatarTracker::instance().track(agent_id, name);
+
+ LLFloaterDirectory::hide(NULL);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+void handle_pay_by_id(const LLUUID& agent_id)
+{
+ const BOOL is_group = FALSE;
+ LLFloaterPay::payDirectly(&give_money, agent_id, is_group);
+}
+
+void handle_mouselook(void*)
+{
+ gAgent.changeCameraToMouselook();
+}
+
+
+void handle_map(void*)
+{
+ LLFloaterWorldMap::toggle(NULL);
+}
+
+void handle_mini_map(void*)
+{
+ LLFloaterMap::toggle(NULL);
+}
+
+
+void handle_find(void*)
+{
+ LLFloaterDirectory::toggleFind(NULL);
+}
+
+
+void handle_events(void*)
+{
+ LLFloaterDirectory::toggleEvents(NULL);
+}
+
+
+void handle_inventory(void*)
+{
+ // We're using the inventory, possibly for the
+ // first time.
+ LLFirstUse::useInventory();
+
+ LLInventoryView::toggleVisibility(NULL);
+}
+
+
+void handle_clothing(void*)
+{
+ LLFloaterClothing::toggleVisibility();
+}
+
+
+void handle_chat(void*)
+{
+ // give focus to chatbar if it's open but not focused
+ if (gSavedSettings.getBOOL("ChatVisible") && gFocusMgr.childHasKeyboardFocus(gChatBar))
+ {
+ LLChatBar::stopChat();
+ }
+ else
+ {
+ LLChatBar::startChat(NULL);
+ }
+}
+
+void handle_slash_key(void*)
+{
+ // LLChatBar::startChat("/");
+ //
+ // Don't do this, it results in a double-slash in the input field.
+ // Another "/" will be automatically typed for us, because the WM_KEYDOWN event
+ // that generated the menu accelerator call (and hence puts focus in
+ // the chat edtior) will be followed by a "/" WM_CHAR character message,
+ // which will type the slash. Yes, it's weird. It only matters for
+ // menu accelerators that put input focus into a field. And Mac works
+ // the same way. JC
+
+ LLChatBar::startChat(NULL);
+}
diff --git a/indra/newview/llmenucommands.h b/indra/newview/llmenucommands.h
new file mode 100644
index 0000000000..9cda66c52a
--- /dev/null
+++ b/indra/newview/llmenucommands.h
@@ -0,0 +1,27 @@
+/**
+ * @file llmenucommands.h
+ * @brief Implementations of menu commands.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMENUCOMMANDS_H
+#define LL_LLMENUCOMMANDS_H
+
+class LLUUID;
+
+void handle_track_avatar(const LLUUID& agent_id, const std::string& name);
+void handle_pay_by_id(const LLUUID& agent_id);
+void handle_mouselook(void*);
+void handle_map(void*);
+void handle_mini_map(void*);
+void handle_find(void*);
+void handle_events(void*);
+void handle_inventory(void*);
+void handle_clothing(void*);
+void handle_chat(void*);
+void handle_return_key(void*);
+void handle_slash_key(void*);
+
+#endif
diff --git a/indra/newview/llmorphview.cpp b/indra/newview/llmorphview.cpp
new file mode 100644
index 0000000000..de4c6dcbd6
--- /dev/null
+++ b/indra/newview/llmorphview.cpp
@@ -0,0 +1,193 @@
+/**
+ * @file llmorphview.cpp
+ * @brief Container for Morph functionality
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmorphview.h"
+
+#include "lljoint.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloatercustomize.h"
+#include "llfloatertools.h"
+#include "llresmgr.h"
+#include "lltoolmgr.h"
+#include "lltoolmorph.h"
+#include "llviewercamera.h"
+#include "llvoavatar.h"
+#include "llviewerwindow.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+LLMorphView *gMorphView = NULL;
+
+
+const F32 EDIT_AVATAR_ORBIT_SPEED = 0.1f;
+const F32 EDIT_AVATAR_MAX_CAMERA_PITCH = 0.5f;
+
+const F32 CAMERA_MOVE_TIME = 0.5f;
+const F32 MORPH_NEAR_CLIP = 0.1f;
+
+const F32 CAMERA_DIST_MIN = 0.4f;
+const F32 CAMERA_DIST_MAX = 4.0f;
+const F32 CAMERA_DIST_STEP = 1.5f;
+
+//-----------------------------------------------------------------------------
+// LLMorphView()
+//-----------------------------------------------------------------------------
+LLMorphView::LLMorphView(const std::string& name, const LLRect& rect)
+ :
+ LLView(name, rect, FALSE, FOLLOWS_ALL),
+ mCameraTargetJoint( NULL ),
+ mCameraOffset(-0.5f, 0.05f, 0.07f ),
+ mCameraTargetOffset(0.f, 0.f, 0.05f ),
+ mOldCameraNearClip( 0.f ),
+ mCameraPitch( 0.f ),
+ mCameraYaw( 0.f ),
+ mCameraDist( -1.f ),
+ mCameraDrivenByKeys( FALSE )
+{
+}
+
+EWidgetType LLMorphView::getWidgetType() const
+{
+ return WIDGET_TYPE_MORPH_VIEW;
+}
+
+LLString LLMorphView::getWidgetTag() const
+{
+ return LL_MORPH_VIEW_TAG;
+}
+
+//-----------------------------------------------------------------------------
+// initialize()
+//-----------------------------------------------------------------------------
+void LLMorphView::initialize()
+{
+ mCameraPitch = 0.f;
+ mCameraYaw = 0.f;
+ mCameraDist = -1.f;
+
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if (!avatarp || avatarp->isDead())
+ {
+ gAgent.changeCameraToDefault();
+ return;
+ }
+
+ avatarp->stopMotion( ANIM_AGENT_BODY_NOISE );
+ avatarp->mSpecialRenderMode = 3;
+
+ // set up camera for close look at avatar
+ mOldCameraNearClip = gCamera->getNear();
+ gCamera->setNear(MORPH_NEAR_CLIP);
+}
+
+//-----------------------------------------------------------------------------
+// shutdown()
+//-----------------------------------------------------------------------------
+void LLMorphView::shutdown()
+{
+ LLVOAvatar::onCustomizeEnd();
+
+ LLVOAvatar *avatarp = gAgent.getAvatarObject();
+ if(avatarp && !avatarp->isDead())
+ {
+ avatarp->startMotion( ANIM_AGENT_BODY_NOISE );
+ avatarp->mSpecialRenderMode = 0;
+ // reset camera
+ gCamera->setNear(mOldCameraNearClip);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setVisible()
+//-----------------------------------------------------------------------------
+void LLMorphView::setVisible(BOOL visible)
+{
+ if( visible != getVisible() )
+ {
+ LLView::setVisible(visible);
+
+ if (visible)
+ {
+ llassert( !gFloaterCustomize );
+ gFloaterCustomize = new LLFloaterCustomize();
+ gFloaterCustomize->fetchInventory();
+ gFloaterCustomize->open();
+
+ // Must do this _after_ gFloaterView is initialized.
+ gFloaterCustomize->switchToDefaultSubpart();
+
+ initialize();
+
+ // First run dialog
+ LLFirstUse::useAppearance();
+ }
+ else
+ {
+ if( gFloaterCustomize )
+ {
+ gFloaterView->removeChild( gFloaterCustomize );
+ delete gFloaterCustomize;
+ gFloaterCustomize = NULL;
+ }
+
+ shutdown();
+ }
+ }
+}
+
+void LLMorphView::updateCamera()
+{
+ if (!mCameraTargetJoint)
+ {
+ setCameraTargetJoint(gAgent.getAvatarObject()->getJoint("mHead"));
+ }
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ return;
+ }
+ LLJoint* root_joint = avatar->getRootJoint();
+ if( !root_joint )
+ {
+ return;
+ }
+
+ const LLQuaternion& avatar_rot = root_joint->getWorldRotation();
+
+ LLVector3d joint_pos = gAgent.getPosGlobalFromAgent(mCameraTargetJoint->getWorldPosition());
+ LLVector3d target_pos = joint_pos + mCameraTargetOffset * avatar_rot;
+
+ LLQuaternion camera_rot_yaw(mCameraYaw, LLVector3::z_axis);
+ LLQuaternion camera_rot_pitch(mCameraPitch, LLVector3::y_axis);
+
+ LLVector3d camera_pos = joint_pos + mCameraOffset * camera_rot_pitch * camera_rot_yaw * avatar_rot;
+
+ gAgent.setCameraPosAndFocusGlobal( camera_pos, target_pos, gAgent.getID() );
+}
+
+void LLMorphView::setCameraDrivenByKeys(BOOL b)
+{
+ if( mCameraDrivenByKeys != b )
+ {
+ if( b )
+ {
+ // Reset to the default camera position specified by mCameraPitch, mCameraYaw, etc.
+ updateCamera();
+ }
+ mCameraDrivenByKeys = b;
+ }
+}
diff --git a/indra/newview/llmorphview.h b/indra/newview/llmorphview.h
new file mode 100644
index 0000000000..676564ac49
--- /dev/null
+++ b/indra/newview/llmorphview.h
@@ -0,0 +1,68 @@
+/**
+ * @file llmorphview.h
+ * @brief Container for character morph controls
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMORPHVIEW_H
+#define LL_LLMORPHVIEW_H
+
+#include "llview.h"
+#include "v3dmath.h"
+#include "llframetimer.h"
+
+class LLJoint;
+class LLFloaterCustomize;
+
+class LLMorphView : public LLView
+{
+public:
+ LLMorphView(const std::string& name, const LLRect& rect);
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void initialize();
+ void shutdown();
+
+ // inherited methods
+ /*virtual*/ void setVisible(BOOL visible);
+
+ void setCameraTargetJoint(LLJoint *joint) {mCameraTargetJoint = joint;}
+ LLJoint* getCameraTargetJoint() {return mCameraTargetJoint;}
+
+ void setCameraOffset(const LLVector3d& camera_offset) {mCameraOffset = camera_offset;}
+ void setCameraTargetOffset(const LLVector3d& camera_target_offset) {mCameraTargetOffset = camera_target_offset;}
+ void setCameraDistToDefault() { mCameraDist = -1.f; }
+
+ void updateCamera();
+ void setCameraDrivenByKeys( BOOL b );
+
+protected:
+ LLJoint* mCameraTargetJoint;
+ LLVector3d mCameraOffset;
+ LLVector3d mCameraTargetOffset;
+ LLVector3d mOldCameraPos;
+ LLVector3d mOldTargetPos;
+ F32 mOldCameraNearClip;
+ LLFrameTimer mCameraMoveTimer;
+
+ // camera rotation
+ F32 mCameraPitch;
+ F32 mCameraYaw;
+
+ // camera zoom
+ F32 mCameraDist;
+
+ BOOL mCameraDrivenByKeys;
+};
+
+//
+// Globals
+//
+
+extern LLMorphView *gMorphView;
+
+#endif
diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp
new file mode 100644
index 0000000000..8485a8b177
--- /dev/null
+++ b/indra/newview/llmoveview.cpp
@@ -0,0 +1,198 @@
+/**
+ * @file llmoveview.cpp
+ * @brief Container for movement buttons like forward, left, fly
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmoveview.h"
+
+// Library includes
+#include "indra_constants.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "viewer.h"
+#include "llfontgl.h"
+#include "llbutton.h"
+#include "llviewerwindow.h"
+#include "lljoystickbutton.h"
+#include "llresmgr.h"
+#include "llvieweruictrlfactory.h"
+
+//
+// Constants
+//
+
+const F32 MOVE_BUTTON_DELAY = 0.0f;
+const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed
+const F32 NUDGE_TIME = 0.25f; // in seconds
+
+const char *MOVE_TITLE = "";
+
+//
+// Global statics
+//
+
+LLFloaterMove* LLFloaterMove::sInstance = NULL;
+
+
+//
+// Member functions
+//
+
+// protected
+LLFloaterMove::LLFloaterMove()
+: LLFloater("move floater", "FloaterMoveRect", MOVE_TITLE, FALSE, 100, 100, DRAG_ON_TOP,
+ MINIMIZE_NO)
+{
+ setIsChrome(TRUE);
+ gUICtrlFactory->buildFloater(this,"floater_moveview.xml");
+
+ mForwardButton = LLViewerUICtrlFactory::getJoystickAgentTurnByName(this, "forward btn");
+ mForwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mBackwardButton = LLViewerUICtrlFactory::getJoystickAgentTurnByName(this, "backward btn");
+ mBackwardButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mSlideLeftButton = LLViewerUICtrlFactory::getJoystickAgentSlideByName(this, "slide left btn");
+ mSlideLeftButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mSlideRightButton = LLViewerUICtrlFactory::getJoystickAgentSlideByName(this, "slide right btn");
+ mSlideRightButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+
+ mTurnLeftButton = LLUICtrlFactory::getButtonByName(this, "turn left btn");
+ mTurnLeftButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mTurnLeftButton->setHeldDownCallback( turnLeft );
+
+ mTurnRightButton = LLUICtrlFactory::getButtonByName(this, "turn right btn");
+ mTurnRightButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mTurnRightButton->setHeldDownCallback( turnRight );
+
+ mMoveUpButton = LLUICtrlFactory::getButtonByName(this, "move up btn");
+ childSetAction("move up btn",moveUp,NULL);
+ mMoveUpButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mMoveUpButton->setHeldDownCallback( moveUp );
+
+ mMoveDownButton = LLUICtrlFactory::getButtonByName(this, "move down btn");
+ childSetAction("move down btn",moveDown,NULL);
+ mMoveDownButton->setHeldDownDelay(MOVE_BUTTON_DELAY);
+ mMoveDownButton->setHeldDownCallback( moveDown );
+
+ mFlyButton = LLUICtrlFactory::getButtonByName(this, "fly btn");
+ childSetAction("fly btn",onFlyButtonClicked,NULL);
+
+ sInstance = this;
+}
+
+// protected
+LLFloaterMove::~LLFloaterMove()
+{
+ // children all deleted by LLView destructor
+ sInstance = NULL;
+}
+
+// virtual
+void LLFloaterMove::onClose(bool app_quitting)
+{
+ LLFloater::onClose(app_quitting);
+
+ if (!app_quitting)
+ {
+ gSavedSettings.setBOOL("ShowMovementControls", FALSE);
+ }
+}
+
+//
+// Static member functions
+//
+
+// static
+void LLFloaterMove::show(void*)
+{
+ if (sInstance)
+ {
+ sInstance->open();
+ }
+ else
+ {
+ LLFloaterMove* f = new LLFloaterMove();
+ f->open();
+ }
+
+ gSavedSettings.setBOOL("ShowMovementControls", TRUE);
+}
+
+// static
+void LLFloaterMove::toggle(void*)
+{
+ if (sInstance)
+ {
+ sInstance->close();
+ }
+ else
+ {
+ show(NULL);
+ }
+}
+
+// static
+BOOL LLFloaterMove::visible(void*)
+{
+ return (sInstance != NULL);
+}
+
+// protected static
+void LLFloaterMove::onFlyButtonClicked(void *)
+{
+ gAgent.toggleFlying();
+}
+
+
+// protected static
+F32 LLFloaterMove::getYawRate( F32 time )
+{
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = YAW_NUDGE_RATE + time * (1 - YAW_NUDGE_RATE)/ NUDGE_TIME;
+ return rate;
+ }
+ else
+ {
+ return 1.f;
+ }
+}
+
+// protected static
+void LLFloaterMove::turnLeft(void *)
+{
+ F32 time = sInstance->mTurnLeftButton->getHeldDownTime();
+ gAgent.moveYaw( getYawRate( time ) );
+}
+
+// protected static
+void LLFloaterMove::turnRight(void *)
+{
+ F32 time = sInstance->mTurnRightButton->getHeldDownTime();
+ gAgent.moveYaw( -getYawRate( time ) );
+}
+
+// protected static
+void LLFloaterMove::moveUp(void *)
+{
+ // Jumps or flys up, depending on fly state
+ gAgent.moveUp(1);
+}
+
+// protected static
+void LLFloaterMove::moveDown(void *)
+{
+ // Crouches or flys down, depending on fly state
+ gAgent.moveUp(-1);
+}
+
+// EOF
diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h
new file mode 100644
index 0000000000..219f069de8
--- /dev/null
+++ b/indra/newview/llmoveview.h
@@ -0,0 +1,68 @@
+/**
+ * @file llmoveview.h
+ * @brief Container for buttons for walking, turning, flying
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLMOVEVIEW_H
+#define LL_LLMOVEVIEW_H
+
+// Library includes
+#include "llfloater.h"
+
+class LLButton;
+class LLJoystickAgentTurn;
+class LLJoystickAgentSlide;
+
+//
+// Classes
+//
+class LLFloaterMove
+: public LLFloater
+{
+protected:
+ LLFloaterMove();
+ ~LLFloaterMove();
+
+public:
+ /*virtual*/ void onClose(bool app_quitting);
+ static void onFlyButtonClicked(void* userdata);
+ static F32 getYawRate(F32 time);
+
+ static void show(void*);
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ // HACK for agent-driven button highlighting
+ static LLFloaterMove* getInstance() { return sInstance; }
+
+protected:
+ static void turnLeftNudge(void* userdata);
+ static void turnLeft(void* userdata);
+
+ static void turnRightNudge(void* userdata);
+ static void turnRight(void* userdata);
+
+ static void moveUp(void* userdata);
+ static void moveDown(void* userdata);
+
+public:
+ LLButton* mFlyButton;
+
+ LLJoystickAgentTurn* mForwardButton;
+ LLJoystickAgentTurn* mBackwardButton;
+ LLJoystickAgentSlide* mSlideLeftButton;
+ LLJoystickAgentSlide* mSlideRightButton;
+ LLButton* mTurnLeftButton;
+ LLButton* mTurnRightButton;
+ LLButton* mMoveUpButton;
+ LLButton* mMoveDownButton;
+
+protected:
+ static LLFloaterMove* sInstance;
+};
+
+
+#endif
diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp
new file mode 100644
index 0000000000..fc8ac0a372
--- /dev/null
+++ b/indra/newview/llmutelist.cpp
@@ -0,0 +1,553 @@
+/**
+ * @file llmutelist.cpp
+ * @author Richard Nelson, James Cook
+ * @brief Management of list of muted players
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ * How should muting work?
+ * Mute an avatar
+ * Mute a specific object (accidentally spamming)
+ *
+ * right-click avatar, mute
+ * see list of recent chatters, mute
+ * type a name to mute?
+ *
+ * show in list whether chatter is avatar or object
+ *
+ * need fast lookup by id
+ * need lookup by name, doesn't have to be fast
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llmutelist.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llcrc.h"
+#include "lldispatcher.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llfloatermute.h"
+#include "llviewermessage.h" // for gGenericDispatcher
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llworld.h" //for particle system banning
+
+LLMuteList* gMuteListp = NULL;
+
+// "emptymutelist"
+class LLDispatchEmptyMuteList : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ gMuteListp->setLoaded();
+ return true;
+ }
+};
+
+static LLDispatchEmptyMuteList sDispatchEmptyMuteList;
+
+//-----------------------------------------------------------------------------
+// LLMute()
+//-----------------------------------------------------------------------------
+const char BY_NAME_SUFFIX[] = " (by name)";
+const char AGENT_SUFFIX[] = " (resident)";
+const char OBJECT_SUFFIX[] = " (object)";
+const char GROUP_SUFFIX[] = " (group)";
+
+LLString LLMute::getDisplayName() const
+{
+ LLString name_with_suffix = mName;
+ switch (mType)
+ {
+ case BY_NAME:
+ default:
+ name_with_suffix += BY_NAME_SUFFIX;
+ break;
+ case AGENT:
+ name_with_suffix += AGENT_SUFFIX;
+ break;
+ case OBJECT:
+ name_with_suffix += OBJECT_SUFFIX;
+ break;
+ case GROUP:
+ name_with_suffix += GROUP_SUFFIX;
+ break;
+ }
+ return name_with_suffix;
+}
+
+void LLMute::setFromDisplayName(const LLString& display_name)
+{
+ size_t pos = 0;
+ mName = display_name;
+
+ pos = mName.rfind(GROUP_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = GROUP;
+ return;
+ }
+
+ pos = mName.rfind(OBJECT_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = OBJECT;
+ return;
+ }
+
+ pos = mName.rfind(AGENT_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = AGENT;
+ return;
+ }
+
+ pos = mName.rfind(BY_NAME_SUFFIX);
+ if (pos != std::string::npos)
+ {
+ mName.erase(pos);
+ mType = BY_NAME;
+ return;
+ }
+
+ llwarns << "Unable to set mute from display name " << display_name << llendl;
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// LLMuteList()
+//-----------------------------------------------------------------------------
+LLMuteList::LLMuteList() :
+ mIsLoaded(FALSE)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ // Register our various callbacks
+ msg->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate);
+ msg->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList);
+
+ gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLMuteList()
+//-----------------------------------------------------------------------------
+LLMuteList::~LLMuteList()
+{
+}
+
+BOOL LLMuteList::isLinden(const LLString& name) const
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(name, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ if (token_iter == tokens.end()) return FALSE;
+ token_iter++;
+ if (token_iter == tokens.end()) return FALSE;
+
+ LLString last_name = *token_iter;
+
+ if (last_name == "Linden")
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLMuteList::add(const LLMute& mute)
+{
+ // Can't mute Lindens
+ if ((mute.mType == LLMute::AGENT || mute.mType == LLMute::BY_NAME)
+ && isLinden(mute.mName))
+ {
+ gViewerWindow->alertXml("MuteLinden");
+ return FALSE;
+ }
+
+ // Can't mute self.
+ if (mute.mType == LLMute::AGENT
+ && mute.mID == gAgent.getID())
+ {
+ return FALSE;
+ }
+
+ if (mute.mType == LLMute::BY_NAME)
+ {
+ // Can't mute empty string by name
+ if (mute.mName.empty())
+ {
+ llwarns << "Trying to mute empty string by-name" << llendl;
+ return FALSE;
+ }
+
+ // Null mutes must have uuid null
+ if (mute.mID.notNull())
+ {
+ llwarns << "Trying to add by-name mute with non-null id" << llendl;
+ return FALSE;
+ }
+
+ std::pair<string_set_t::iterator, bool> result = mLegacyMutes.insert(mute.mName);
+ if (result.second)
+ {
+ llinfos << "Muting by name " << mute.mName << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ return TRUE;
+ }
+ else
+ {
+ // was duplicate
+ return FALSE;
+ }
+ }
+ else
+ {
+ std::pair<mute_set_t::iterator, bool> result = mMutes.insert(mute);
+ if (result.second)
+ {
+ llinfos << "Muting " << mute.mName << " id " << mute.mID << llendl;
+ updateAdd(mute);
+ notifyObservers();
+ //Kill all particle systems owned by muted task
+ if(mute.mType == LLMute::AGENT || mute.mType == LLMute::OBJECT)
+ {
+ gWorldPointer->mPartSim.cleanMutedParticles(mute.mID);
+ }
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+}
+
+void LLMuteList::updateAdd(const LLMute& mute)
+{
+ // Update the database
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateMuteListEntry);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
+ msg->addStringFast(_PREHASH_MuteName, mute.mName);
+ msg->addS32("MuteType", mute.mType);
+ msg->addU32("MuteFlags", 0x0); // future
+ gAgent.sendReliableMessage();
+
+ mIsLoaded = TRUE;
+}
+
+
+BOOL LLMuteList::remove(const LLMute& mute)
+{
+ BOOL found = FALSE;
+
+ // First, remove from main list.
+ mute_set_t::iterator it = mMutes.find(mute);
+ if (it != mMutes.end())
+ {
+ updateRemove(*it);
+ mMutes.erase(it);
+ // Must be after erase.
+ notifyObservers();
+ found = TRUE;
+ }
+
+ // Clean up any legacy mutes
+ string_set_t::iterator legacy_it = mLegacyMutes.find(mute.mName);
+ if (legacy_it != mLegacyMutes.end())
+ {
+ // Database representation of legacy mute is UUID null.
+ LLMute mute(LLUUID::null, *legacy_it, LLMute::BY_NAME);
+ updateRemove(mute);
+ mLegacyMutes.erase(legacy_it);
+ // Must be after erase.
+ notifyObservers();
+ found = TRUE;
+ }
+
+ return found;
+}
+
+
+void LLMuteList::updateRemove(const LLMute& mute)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveMuteListEntry);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addUUIDFast(_PREHASH_MuteID, mute.mID);
+ msg->addString("MuteName", mute.mName);
+ gAgent.sendReliableMessage();
+}
+
+
+std::vector<LLMute> LLMuteList::getMutes() const
+{
+ std::vector<LLMute> mutes;
+
+ for (mute_set_t::const_iterator it = mMutes.begin();
+ it != mMutes.end();
+ ++it)
+ {
+ mutes.push_back(*it);
+ }
+
+ for (string_set_t::const_iterator it = mLegacyMutes.begin();
+ it != mLegacyMutes.end();
+ ++it)
+ {
+ LLMute legacy(LLUUID::null, *it);
+ mutes.push_back(legacy);
+ }
+
+ std::sort(mutes.begin(), mutes.end(), compare_by_name());
+ return mutes;
+}
+
+//-----------------------------------------------------------------------------
+// loadFromFile()
+//-----------------------------------------------------------------------------
+BOOL LLMuteList::loadFromFile(const LLString& filename)
+{
+ FILE* fp = LLFile::fopen(filename.c_str(), "rb");
+ if (!fp)
+ {
+ llwarns << "Couldn't open mute list " << filename << llendl;
+ return FALSE;
+ }
+
+ char id_buffer[MAX_STRING];
+ char name_buffer[MAX_STRING];
+ char buffer[MAX_STRING];
+ while (!feof(fp)
+ && fgets(buffer, MAX_STRING, fp))
+ {
+ id_buffer[0] = '\0';
+ name_buffer[0] = '\0';
+ S32 type = 0;
+ sscanf(buffer, " %d %s %[^|]", &type, id_buffer, name_buffer);
+ LLUUID id = LLUUID(id_buffer);
+ LLMute mute(id, name_buffer, (LLMute::EType)type);
+ if (mute.mID.isNull()
+ || mute.mType == LLMute::BY_NAME)
+ {
+ mLegacyMutes.insert(mute.mName);
+ }
+ else
+ {
+ mMutes.insert(mute);
+ }
+ }
+ fclose(fp);
+ mIsLoaded = TRUE;
+ notifyObservers();
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// saveToFile()
+//-----------------------------------------------------------------------------
+BOOL LLMuteList::saveToFile(const LLString& filename)
+{
+ FILE* fp = LLFile::fopen(filename.c_str(), "wb");
+ if (!fp)
+ {
+ llwarns << "Couldn't open mute list " << filename << llendl;
+ return FALSE;
+ }
+ // legacy mutes have null uuid
+ char id_string[UUID_STR_LENGTH];
+ LLUUID::null.toString(id_string);
+ for (string_set_t::iterator it = mLegacyMutes.begin();
+ it != mLegacyMutes.end();
+ ++it)
+ {
+ fprintf(fp, "%d %s %s|\n", (S32)LLMute::BY_NAME, id_string, it->c_str());
+ }
+ for (mute_set_t::iterator it = mMutes.begin();
+ it != mMutes.end();
+ ++it)
+ {
+ it->mID.toString(id_string);
+ const LLString& name = it->mName;
+ fprintf(fp, "%d %s %s|\n", (S32)it->mType, id_string, name.c_str());
+ }
+ fclose(fp);
+ return TRUE;
+}
+
+
+BOOL LLMuteList::isMuted(const LLUUID& id, const LLString& name) const
+{
+ // don't need name or type for lookup
+ LLMute mute(id);
+ mute_set_t::const_iterator mute_it = mMutes.find(mute);
+ if (mute_it != mMutes.end()) return TRUE;
+
+ // empty names can't be legacy-muted
+ if (name.empty()) return FALSE;
+
+ // Look in legacy pile
+ string_set_t::const_iterator legacy_it = mLegacyMutes.find(name);
+ return legacy_it != mLegacyMutes.end();
+}
+
+//-----------------------------------------------------------------------------
+// requestFromServer()
+//-----------------------------------------------------------------------------
+void LLMuteList::requestFromServer(const LLUUID& agent_id)
+{
+ char agent_id_string[UUID_STR_LENGTH];
+ char filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_string);
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ LLCRC crc;
+ crc.update(filename);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MuteListRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MuteData);
+ msg->addU32Fast(_PREHASH_MuteCRC, crc.getCRC());
+ gAgent.sendReliableMessage();
+}
+
+//-----------------------------------------------------------------------------
+// cache()
+//-----------------------------------------------------------------------------
+
+void LLMuteList::cache(const LLUUID& agent_id)
+{
+ // Write to disk even if empty.
+ if(mIsLoaded)
+ {
+ char agent_id_string[UUID_STR_LENGTH];
+ char filename[LL_MAX_PATH];
+ agent_id.toString(agent_id_string);
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ saveToFile(filename);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Static message handlers
+//-----------------------------------------------------------------------------
+
+void LLMuteList::processMuteListUpdate(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLMuteList::processMuteListUpdate()" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_MuteData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "Got an mute list update for the wrong agent." << llendl;
+ return;
+ }
+ char filename[MAX_STRING];
+ filename[0] = '\0';
+ msg->getStringFast(_PREHASH_MuteData, _PREHASH_Filename, MAX_STRING, filename);
+
+ std::string *local_filename_and_path = new std::string(gDirUtilp->getExpandedFilename( LL_PATH_CACHE, filename ));
+ gXferManager->requestFile(local_filename_and_path->c_str(),
+ filename,
+ LL_PATH_CACHE,
+ msg->getSender(),
+ TRUE, // make the remote file temporary.
+ onFileMuteList,
+ (void**)local_filename_and_path,
+ LLXferManager::HIGH_PRIORITY);
+}
+
+void LLMuteList::processUseCachedMuteList(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLMuteList::processUseCachedMuteList()" << llendl;
+ if (!gMuteListp) return;
+
+ char agent_id_string[UUID_STR_LENGTH];
+ gAgent.getID().toString(agent_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.cached_mute", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,agent_id_string).c_str());
+ gMuteListp->loadFromFile(filename);
+}
+
+void LLMuteList::onFileMuteList(void** user_data, S32 error_code)
+{
+ llinfos << "LLMuteList::processMuteListFile()" << llendl;
+ if (!gMuteListp) return;
+
+ std::string *local_filename_and_path = (std::string*)user_data;
+ if(local_filename_and_path && !local_filename_and_path->empty() && (error_code == 0))
+ {
+ gMuteListp->loadFromFile(local_filename_and_path->c_str());
+ LLFile::remove(local_filename_and_path->c_str());
+ }
+ delete local_filename_and_path;
+}
+
+void LLMuteList::addObserver(LLMuteListObserver* observer)
+{
+ mObservers.insert(observer);
+}
+
+void LLMuteList::removeObserver(LLMuteListObserver* observer)
+{
+ mObservers.erase(observer);
+}
+
+void LLMuteList::setLoaded()
+{
+ mIsLoaded = TRUE;
+ notifyObservers();
+}
+
+void LLMuteList::notifyObservers()
+{
+ // HACK: LLFloaterMute is constructed before LLMuteList,
+ // so it can't easily observe it. Changing this requires
+ // much reshuffling of the startup process. JC
+ if (gFloaterMute)
+ {
+ gFloaterMute->refreshMuteList();
+ }
+
+ for (observer_set_t::iterator it = mObservers.begin();
+ it != mObservers.end();
+ )
+ {
+ LLMuteListObserver* observer = *it;
+ observer->onChange();
+ // In case onChange() deleted an entry.
+ it = mObservers.upper_bound(observer);
+ }
+}
diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h
new file mode 100644
index 0000000000..7f9b1b3bf6
--- /dev/null
+++ b/indra/newview/llmutelist.h
@@ -0,0 +1,128 @@
+/**
+ * @file llmutelist.h
+ * @brief Management of list of muted players
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_MUTELIST_H
+#define LL_MUTELIST_H
+
+#include "llstring.h"
+#include "lluuid.h"
+
+class LLViewerObject;
+class LLMessageSystem;
+class LLMuteListObserver;
+
+// An entry in the mute list.
+class LLMute
+{
+public:
+ // Legacy mutes are BY_NAME and have null UUID.
+ enum EType { BY_NAME = 0, AGENT = 1, OBJECT = 2, GROUP = 3, COUNT = 4 };
+
+ LLMute(const LLUUID& id, const LLString& name = "", EType type = BY_NAME)
+ : mID(id), mName(name), mType(type) { }
+
+ // Returns name + suffix based on type
+ // For example: "James Tester (resident)"
+ LLString getDisplayName() const;
+
+ // Converts a UI name into just the agent or object name
+ // For example: "James Tester (resident)" sets the name to "James Tester"
+ // and the type to AGENT.
+ void setFromDisplayName(const LLString& display_name);
+
+public:
+ LLUUID mID; // agent or object id
+ LLString mName; // agent or object name
+ EType mType; // needed for UI display of existing mutes
+};
+
+class LLMuteList
+{
+public:
+ LLMuteList();
+ ~LLMuteList();
+
+ void addObserver(LLMuteListObserver* observer);
+ void removeObserver(LLMuteListObserver* observer);
+
+ // Add either a normal or a BY_NAME mute.
+ BOOL add(const LLMute& mute);
+
+ // Remove both normal and legacy mutes.
+ BOOL remove(const LLMute& mute);
+
+ // Name is required to test against legacy text-only mutes.
+ BOOL isMuted(const LLUUID& id, const LLString& name = LLString::null) const;
+
+ BOOL isLinden(const LLString& name) const;
+
+ BOOL isLoaded() const { return mIsLoaded; }
+
+ std::vector<LLMute> getMutes() const;
+
+ // request the mute list
+ void requestFromServer(const LLUUID& agent_id);
+
+ // call this method on logout to save everything.
+ void cache(const LLUUID& agent_id);
+
+private:
+ BOOL loadFromFile(const LLString& filename);
+ BOOL saveToFile(const LLString& filename);
+
+ void setLoaded();
+ void notifyObservers();
+
+ void updateAdd(const LLMute& mute);
+ void updateRemove(const LLMute& mute);
+
+ // TODO: NULL out mute_id in database
+ static void processMuteListUpdate(LLMessageSystem* msg, void**);
+ static void processUseCachedMuteList(LLMessageSystem* msg, void**);
+
+ static void onFileMuteList(void** user_data, S32 code);
+
+private:
+ struct compare_by_name
+ {
+ bool operator()(const LLMute& a, const LLMute& b) const
+ {
+ return a.mName < b.mName;
+ }
+ };
+ struct compare_by_id
+ {
+ bool operator()(const LLMute& a, const LLMute& b) const
+ {
+ return a.mID < b.mID;
+ }
+ };
+ typedef std::set<LLMute, compare_by_id> mute_set_t;
+ mute_set_t mMutes;
+
+ typedef std::set<LLString> string_set_t;
+ string_set_t mLegacyMutes;
+
+ typedef std::set<LLMuteListObserver*> observer_set_t;
+ observer_set_t mObservers;
+
+ BOOL mIsLoaded;
+
+ friend class LLDispatchEmptyMuteList;
+};
+
+class LLMuteListObserver
+{
+public:
+ virtual ~LLMuteListObserver() { }
+ virtual void onChange() = 0;
+};
+
+extern LLMuteList *gMuteListp;
+
+#endif //LL_MUTELIST_H
diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp
new file mode 100644
index 0000000000..8782f529f1
--- /dev/null
+++ b/indra/newview/llnamebox.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file llnamebox.cpp
+ * @brief A text display widget
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnamebox.h"
+
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "lluuid.h"
+
+#include "llcachename.h"
+#include "llagent.h"
+
+// statics
+std::set<LLNameBox*> LLNameBox::sInstances;
+
+
+LLNameBox::LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id, BOOL is_group, const LLFontGL* font, BOOL mouse_opaque)
+: LLTextBox(name, rect, "(retrieving)", font, mouse_opaque),
+ mNameID(name_id)
+{
+ LLNameBox::sInstances.insert(this);
+ if(!name_id.isNull())
+ {
+ setNameID(name_id, is_group);
+ }
+ else
+ {
+ setText("");
+ }
+}
+
+LLNameBox::~LLNameBox()
+{
+ LLNameBox::sInstances.erase(this);
+}
+
+void LLNameBox::setNameID(const LLUUID& name_id, BOOL is_group)
+{
+ mNameID = name_id;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLString name;
+
+ if (!is_group)
+ {
+ gCacheName->getName(name_id, first, last);
+
+ name.assign(first);
+ name.append(1, ' ');
+ name.append(last);
+ }
+ else
+ {
+ gCacheName->getGroupName(name_id, group_name);
+
+ name.assign(group_name);
+ }
+
+ setText(name);
+}
+
+void LLNameBox::refresh(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ if (id == mNameID)
+ {
+ LLString name;
+
+ name.assign(firstname);
+ if (!is_group)
+ {
+ name.append(1, ' ');
+ name.append(lastname);
+ }
+
+ setText(name);
+ }
+}
+
+void LLNameBox::refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ std::set<LLNameBox*>::iterator it;
+ for (it = LLNameBox::sInstances.begin();
+ it != LLNameBox::sInstances.end();
+ ++it)
+ {
+ LLNameBox* box = *it;
+ box->refresh(id, firstname, lastname, is_group);
+ }
+}
diff --git a/indra/newview/llnamebox.h b/indra/newview/llnamebox.h
new file mode 100644
index 0000000000..7af4b82f2d
--- /dev/null
+++ b/indra/newview/llnamebox.h
@@ -0,0 +1,46 @@
+/**
+ * @file llnamebox.h
+ * @brief display and refresh a name from the name cache
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEBOX_H
+#define LL_LLNAMEBOX_H
+
+#include <set>
+
+#include "llview.h"
+#include "llstring.h"
+#include "llfontgl.h"
+#include "linked_lists.h"
+#include "lltextbox.h"
+
+class LLNameBox
+: public LLTextBox
+{
+public:
+ LLNameBox(const std::string& name, const LLRect& rect, const LLUUID& name_id = LLUUID::null, BOOL is_group = FALSE, const LLFontGL* font = NULL, BOOL mouse_opaque = TRUE );
+ // By default, follows top and left and is mouse-opaque.
+ // If no text, text = name.
+ // If no font, uses default system font.
+ virtual ~LLNameBox();
+
+ void setNameID(const LLUUID& name_id, BOOL is_group);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+private:
+ static std::set<LLNameBox*> sInstances;
+
+private:
+ LLUUID mNameID;
+
+};
+
+#endif
diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp
new file mode 100644
index 0000000000..bc6a105a51
--- /dev/null
+++ b/indra/newview/llnameeditor.cpp
@@ -0,0 +1,184 @@
+/**
+ * @file llnameeditor.cpp
+ * @brief Name Editor to refresh a name.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnameeditor.h"
+#include "llcachename.h"
+#include "llagent.h"
+
+#include "llfontgl.h"
+
+#include "lluuid.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llui.h"
+
+
+// statics
+std::set<LLNameEditor*> LLNameEditor::sInstances;
+
+LLNameEditor::LLNameEditor(const std::string& name, const LLRect& rect,
+ const LLUUID& name_id,
+ BOOL is_group,
+ const LLFontGL* glfont,
+ S32 max_text_length,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data),
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data),
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data),
+ void* userdata,
+ LLLinePrevalidateFunc prevalidate_func,
+ LLViewBorder::EBevel border_bevel,
+ LLViewBorder::EStyle border_style,
+ S32 border_thickness)
+: LLLineEditor(name, rect,
+ "(retrieving)",
+ glfont,
+ max_text_length,
+ commit_callback,
+ keystroke_callback,
+ focus_lost_callback,
+ userdata,
+ prevalidate_func,
+ border_bevel,
+ border_style,
+ border_thickness),
+ mNameID(name_id)
+{
+ LLNameEditor::sInstances.insert(this);
+ if(!name_id.isNull())
+ {
+ setNameID(name_id, is_group);
+ }
+}
+
+
+LLNameEditor::~LLNameEditor()
+{
+ LLNameEditor::sInstances.erase(this);
+}
+
+void LLNameEditor::setNameID(const LLUUID& name_id, BOOL is_group)
+{
+ mNameID = name_id;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLString name;
+
+ if (!is_group)
+ {
+ gCacheName->getName(name_id, first, last);
+
+ name.assign(first);
+ name.append(1, ' ');
+ name.append(last);
+ }
+ else
+ {
+ gCacheName->getGroupName(name_id, group_name);
+ name.assign(group_name);
+ }
+
+ setText(name);
+}
+
+void LLNameEditor::refresh(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ if (id == mNameID)
+ {
+ LLString name;
+
+ name.assign(firstname);
+ if (!is_group)
+ {
+ name.append(1, ' ');
+ name.append(lastname);
+ }
+
+ setText(name);
+ }
+}
+
+void LLNameEditor::refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group)
+{
+ std::set<LLNameEditor*>::iterator it;
+ for (it = LLNameEditor::sInstances.begin();
+ it != LLNameEditor::sInstances.end();
+ ++it)
+ {
+ LLNameEditor* box = *it;
+ box->refresh(id, firstname, lastname, is_group);
+ }
+}
+
+void LLNameEditor::setValue( LLSD value )
+{
+ setNameID(value.asUUID(), FALSE);
+}
+
+LLSD LLNameEditor::getValue() const
+{
+ return LLSD(mNameID);
+}
+
+LLView* LLNameEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("name_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ S32 max_text_length = 128;
+ node->getAttributeS32("max_length", max_text_length);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE;
+ LLString border_string;
+ node->getAttributeString("border_style", border_string);
+ LLString::toLower(border_string);
+
+ if (border_string == "texture")
+ {
+ border_style = LLViewBorder::STYLE_TEXTURE;
+ }
+
+ S32 border_thickness = 1;
+ node->getAttributeS32("border_thickness", border_thickness);
+
+ LLUICtrlCallback commit_callback = NULL;
+
+ LLNameEditor* line_editor = new LLNameEditor(name,
+ rect,
+ LLUUID::null, FALSE,
+ font,
+ max_text_length,
+ commit_callback,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ LLViewBorder::BEVEL_IN,
+ border_style,
+ border_thickness);
+
+ LLString label;
+ if(node->getAttributeString("label", label))
+ {
+ line_editor->setLabel(label);
+ }
+ line_editor->setColorParameters(node);
+ line_editor->initFromXML(node, parent);
+
+ return line_editor;
+}
diff --git a/indra/newview/llnameeditor.h b/indra/newview/llnameeditor.h
new file mode 100644
index 0000000000..b5e811fba4
--- /dev/null
+++ b/indra/newview/llnameeditor.h
@@ -0,0 +1,70 @@
+/**
+ * @file llnameeditor.h
+ * @brief display and refresh a name from the name cache
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMEEDITOR_H
+#define LL_LLNAMEEDITOR_H
+
+#include <set>
+
+#include "llview.h"
+#include "v4color.h"
+#include "llstring.h"
+#include "llfontgl.h"
+#include "linked_lists.h"
+#include "lllineeditor.h"
+
+
+class LLNameEditor
+: public LLLineEditor
+{
+public:
+ LLNameEditor(const std::string& name, const LLRect& rect,
+ const LLUUID& name_id = LLUUID::null,
+ BOOL is_group = FALSE,
+ const LLFontGL* glfont = NULL,
+ S32 max_text_length = 254,
+ void (*commit_callback)(LLUICtrl* caller, void* user_data) = NULL,
+ void (*keystroke_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void (*focus_lost_callback)(LLLineEditor* caller, void* user_data) = NULL,
+ void* userdata = NULL,
+ LLLinePrevalidateFunc prevalidate_func = NULL,
+ LLViewBorder::EBevel border_bevel = LLViewBorder::BEVEL_IN,
+ LLViewBorder::EStyle border_style = LLViewBorder::STYLE_LINE,
+ S32 border_thickness = 1);
+ // By default, follows top and left and is mouse-opaque.
+ // If no text, text = name.
+ // If no font, uses default system font.
+
+ virtual ~LLNameEditor();
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ void setNameID(const LLUUID& name_id, BOOL is_group);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_NAME_EDITOR; }
+ virtual LLString getWidgetTag() const { return LL_NAME_EDITOR_TAG; }
+
+ // Take/return agent UUIDs
+ virtual void setValue( LLSD value );
+ virtual LLSD getValue() const;
+
+private:
+ static std::set<LLNameEditor*> sInstances;
+
+private:
+ LLUUID mNameID;
+
+};
+
+#endif
diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp
new file mode 100644
index 0000000000..520dd06af1
--- /dev/null
+++ b/indra/newview/llnamelistctrl.cpp
@@ -0,0 +1,409 @@
+/**
+ * @file llnamelistctrl.cpp
+ * @brief A list of names, automatically refreshed from name cache.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <boost/tokenizer.hpp>
+
+#include "llnamelistctrl.h"
+
+#include "llcachename.h"
+#include "llagent.h"
+#include "llinventory.h"
+
+// statics
+std::set<LLNameListCtrl*> LLNameListCtrl::sInstances;
+
+LLNameListCtrl::LLNameListCtrl(const LLString& name,
+ const LLRect& rect,
+ LLUICtrlCallback cb,
+ void* userdata,
+ BOOL allow_multiple_selection,
+ BOOL draw_border,
+ S32 name_column_index,
+ const LLString& tooltip)
+: LLScrollListCtrl(name, rect, cb, userdata, allow_multiple_selection,
+ draw_border),
+ mNameColumnIndex(name_column_index),
+ mAllowCallingCardDrop(FALSE)
+{
+ setToolTip(tooltip);
+ LLNameListCtrl::sInstances.insert(this);
+}
+
+
+// virtual
+LLNameListCtrl::~LLNameListCtrl()
+{
+ LLNameListCtrl::sInstances.erase(this);
+}
+
+
+// public
+BOOL LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos,
+ BOOL enabled, LLString& suffix)
+{
+ //llinfos << "LLNameListCtrl::addNameItem " << agent_id << llendl;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ BOOL result = gCacheName->getName(agent_id, first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+ fullname.append(suffix);
+
+ addStringUUIDItem(fullname, agent_id, pos, enabled);
+
+ return result;
+}
+
+// virtual, public
+BOOL LLNameListCtrl::handleDragAndDrop(
+ S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ if (!mAllowCallingCardDrop)
+ {
+ return FALSE;
+ }
+
+ BOOL handled = FALSE;
+
+ if (cargo_type == DAD_CALLINGCARD)
+ {
+ if (drop)
+ {
+ LLInventoryItem* item = (LLInventoryItem *)cargo_data;
+ addNameItem(item->getCreatorUUID());
+ }
+
+ *accept = ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ if (!getToolTip().empty())
+ {
+ tooltip_msg = getToolTip();
+ }
+ else
+ {
+ // backwards compatable English tooltip (should be overridden in xml)
+ tooltip_msg.assign("Drag a calling card here\nto add a resident.");
+ }
+ }
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLNameListCtrl " << getName() << llendl;
+
+ return handled;
+}
+
+// public
+void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos,
+ BOOL enabled)
+{
+ //llinfos << "LLNameListCtrl::addGroupNameItem " << group_id << llendl;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(group_id, group_name);
+ addStringUUIDItem(group_name, group_id, pos, enabled);
+}
+
+// public
+void LLNameListCtrl::addGroupNameItem(LLScrollListItem* item, EAddPosition pos)
+
+{
+ //llinfos << "LLNameListCtrl::addGroupNameItem " << item->getUUID() << llendl;
+
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ gCacheName->getGroupName(item->getUUID(), group_name);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( group_name );
+
+ addItem(item, pos);
+}
+
+BOOL LLNameListCtrl::addNameItem(LLScrollListItem* item, EAddPosition pos)
+{
+ //llinfos << "LLNameListCtrl::addNameItem " << item->getUUID() << llendl;
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ BOOL result = gCacheName->getName(item->getUUID(), first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( fullname );
+
+ addItem(item, pos);
+
+ return result;
+}
+
+LLScrollListItem* LLNameListCtrl::addElement(const LLSD& value, EAddPosition pos, void* userdata)
+{
+ LLScrollListItem* item = LLScrollListCtrl::addElement(value, pos, userdata);
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+
+ gCacheName->getName(item->getUUID(), first, last);
+
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+ ((LLScrollListText*)cell)->setText( fullname );
+
+ return item;
+}
+
+// public
+void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
+{
+ BOOL item_exists = selectByID( agent_id );
+ if(item_exists)
+ {
+ S32 index = getItemIndex(getFirstSelected());
+ if(index >= 0)
+ {
+ deleteSingleItem(index);
+ }
+ }
+}
+
+// public
+void LLNameListCtrl::refresh(const LLUUID& id, const char* first,
+ const char* last,
+ BOOL is_group)
+{
+ //llinfos << "LLNameListCtrl::refresh " << id << " '" << first << " "
+ // << last << "'" << llendl;
+
+ LLString fullname;
+ fullname.assign(first);
+ if (last[0] && !is_group)
+ {
+ fullname.append(1, ' ');
+ fullname.append(last);
+ }
+
+ // TODO: scan items for that ID, fix if necessary
+ item_list::iterator iter;
+ for (iter = mItemList.begin(); iter != mItemList.end(); iter++)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getUUID() == id)
+ {
+ LLScrollListCell* cell = (LLScrollListCell*)item->getColumn(0);
+ cell = (LLScrollListCell*)item->getColumn(mNameColumnIndex);
+
+ ((LLScrollListText*)cell)->setText( fullname );
+ }
+ }
+}
+
+
+// static
+void LLNameListCtrl::refreshAll(const LLUUID& id, const char* first,
+ const char* last,
+ BOOL is_group)
+{
+ std::set<LLNameListCtrl*>::iterator it;
+ for (it = LLNameListCtrl::sInstances.begin();
+ it != LLNameListCtrl::sInstances.end();
+ ++it)
+ {
+ LLNameListCtrl* ctrl = *it;
+ ctrl->refresh(id, first, last, is_group);
+ }
+}
+
+// virtual
+LLXMLNodePtr LLNameListCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLScrollListCtrl::getXML();
+
+ node->createChild("allow_calling_card_drop", TRUE)->setBoolValue(mAllowCallingCardDrop);
+
+ if (mNameColumnIndex != 0)
+ {
+ node->createChild("name_column_index", TRUE)->setIntValue(mNameColumnIndex);
+ }
+
+ // Don't save contents, probably filled by code
+
+ return node;
+}
+
+LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("name_list");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ BOOL multi_select = FALSE;
+ node->getAttributeBOOL("multi_select", multi_select);
+
+ BOOL draw_border = TRUE;
+ node->getAttributeBOOL("draw_border", draw_border);
+
+ BOOL draw_heading = FALSE;
+ node->getAttributeBOOL("draw_heading", draw_heading);
+
+ BOOL collapse_empty_columns = FALSE;
+ node->getAttributeBOOL("collapse_empty_columns", collapse_empty_columns);
+
+ S32 name_column_index = 0;
+ node->getAttributeS32("name_column_index", name_column_index);
+
+ LLUICtrlCallback callback = NULL;
+
+ LLNameListCtrl* name_list = new LLNameListCtrl(name,
+ rect,
+ callback,
+ NULL,
+ multi_select,
+ draw_border,
+ name_column_index);
+
+ name_list->setDisplayHeading(draw_heading);
+ if (node->hasAttribute("heading_height"))
+ {
+ S32 heading_height;
+ node->getAttributeS32("heading_height", heading_height);
+ name_list->setHeadingHeight(heading_height);
+ }
+ if (node->hasAttribute("heading_font"))
+ {
+ LLString heading_font("");
+ node->getAttributeString("heading_font", heading_font);
+ LLFontGL* gl_font = LLFontGL::fontFromName(heading_font.c_str());
+ name_list->setHeadingFont(gl_font);
+ }
+ name_list->setCollapseEmptyColumns(collapse_empty_columns);
+
+ BOOL allow_calling_card_drop = FALSE;
+ if (node->getAttributeBOOL("allow_calling_card_drop", allow_calling_card_drop))
+ {
+ name_list->setAllowCallingCardDrop(allow_calling_card_drop);
+ }
+
+ name_list->setScrollListParameters(node);
+
+ name_list->initFromXML(node, parent);
+
+ LLSD columns;
+ S32 index = 0;
+ LLXMLNodePtr child;
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("column"))
+ {
+ LLString labelname("");
+ child->getAttributeString("label", labelname);
+
+ LLString columnname(labelname);
+ child->getAttributeString("name", columnname);
+
+ if (child->hasAttribute("relwidth"))
+ {
+ F32 columnrelwidth = 0.f;
+ child->getAttributeF32("relwidth", columnrelwidth);
+ columns[index]["relwidth"] = columnrelwidth;
+ }
+ else
+ {
+ S32 columnwidth = -1;
+ child->getAttributeS32("width", columnwidth);
+ columns[index]["width"] = columnwidth;
+ }
+
+ columns[index]["name"] = columnname;
+ columns[index]["label"] = labelname;
+ index++;
+ }
+ }
+ name_list->setColumnHeadings(columns);
+
+ for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
+ {
+ if (child->hasName("row"))
+ {
+ LLUUID id;
+ child->getAttributeUUID("id", id);
+
+ LLSD row;
+
+ row["id"] = id;
+
+ S32 column_idx = 0;
+ LLXMLNodePtr row_child;
+ for (row_child = node->getFirstChild(); row_child.notNull(); row_child = row_child->getNextSibling())
+ {
+ if (row_child->hasName("column"))
+ {
+ LLString value = row_child->getTextContents();
+
+ LLString columnname("");
+ row_child->getAttributeString("name", columnname);
+
+ LLString font("");
+ row_child->getAttributeString("font", font);
+
+ LLString font_style("");
+ row_child->getAttributeString("font-style", font_style);
+
+ row["columns"][column_idx]["column"] = columnname;
+ row["columns"][column_idx]["value"] = value;
+ row["columns"][column_idx]["font"] = font;
+ row["columns"][column_idx]["font-style"] = font_style;
+ column_idx++;
+ }
+ }
+ name_list->addElement(row);
+ }
+ }
+
+ LLString contents = node->getTextContents();
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("\t\n");
+ tokenizer tokens(contents, sep);
+ tokenizer::iterator token_iter = tokens.begin();
+
+ while(token_iter != tokens.end())
+ {
+ const char* line = token_iter->c_str();
+ name_list->addSimpleItem(line);
+ ++token_iter;
+ }
+
+ return name_list;
+}
+
diff --git a/indra/newview/llnamelistctrl.h b/indra/newview/llnamelistctrl.h
new file mode 100644
index 0000000000..4e9069e2a3
--- /dev/null
+++ b/indra/newview/llnamelistctrl.h
@@ -0,0 +1,71 @@
+/**
+ * @file llnamelistctrl.h
+ * @brief A list of names, automatically refreshing from the name cache.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNAMELISTCTRL_H
+#define LL_LLNAMELISTCTRL_H
+
+#include <set>
+
+#include "llscrolllistctrl.h"
+
+
+class LLNameListCtrl
+: public LLScrollListCtrl
+{
+public:
+ LLNameListCtrl(const LLString& name,
+ const LLRect& rect,
+ LLUICtrlCallback callback,
+ void* userdata,
+ BOOL allow_multiple_selection,
+ BOOL draw_border = TRUE,
+ S32 name_column_index = 0,
+ const LLString& tooltip = LLString::null);
+ virtual ~LLNameListCtrl();
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_NAME_LIST; }
+ virtual LLString getWidgetTag() const { return LL_NAME_LIST_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ // Add a user to the list by name. It will be added, the name
+ // requested from the cache, and updated as necessary.
+ BOOL addNameItem(const LLUUID& agent_id, EAddPosition pos = ADD_BOTTOM,
+ BOOL enabled = TRUE, LLString& suffix = LLString::null);
+ BOOL addNameItem(LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM);
+
+ virtual LLScrollListItem* addElement(const LLSD& value, EAddPosition pos = ADD_BOTTOM, void* userdata = NULL);
+
+ // Add a user to the list by name. It will be added, the name
+ // requested from the cache, and updated as necessary.
+ void addGroupNameItem(const LLUUID& group_id, EAddPosition pos = ADD_BOTTOM,
+ BOOL enabled = TRUE);
+ void addGroupNameItem(LLScrollListItem* item, EAddPosition pos = ADD_BOTTOM);
+
+
+ void removeNameItem(const LLUUID& agent_id);
+
+ void refresh(const LLUUID& id, const char* first, const char* last,
+ BOOL is_group);
+
+ static void refreshAll(const LLUUID& id, const char* firstname,
+ const char* lastname, BOOL is_group);
+
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+
+ void setAllowCallingCardDrop(BOOL b) { mAllowCallingCardDrop = b; }
+
+private:
+ static std::set<LLNameListCtrl*> sInstances;
+ S32 mNameColumnIndex;
+ BOOL mAllowCallingCardDrop;
+};
+
+#endif
diff --git a/indra/newview/llnetmap.cpp b/indra/newview/llnetmap.cpp
new file mode 100644
index 0000000000..178d707bb0
--- /dev/null
+++ b/indra/newview/llnetmap.cpp
@@ -0,0 +1,806 @@
+/**
+ * @file llnetmap.cpp
+ * @author James Cook
+ * @brief Display of surrounding regions, objects, and agents. View contained by LLFloaterMap.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llnetmap.h"
+
+#include "indra_constants.h"
+#include "llui.h"
+#include "linked_lists.h"
+#include "llmath.h" // clampf()
+#include "llfocusmgr.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermap.h"
+#include "llframetimer.h"
+#include "lltracker.h"
+#include "llmenugl.h"
+#include "llstatgraph.h"
+#include "llsurface.h"
+#include "lltextbox.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "llworldmapview.h" // shared draw code
+#include "viewer.h" // Only for constants!
+
+#include "llglheaders.h"
+
+const F32 MAP_SCALE_MIN = 64;
+const F32 MAP_SCALE_MID = 172;
+const F32 MAP_SCALE_MAX = 512;
+const F32 MAP_SCALE_INCREMENT = 16;
+
+const S32 TRACKING_RADIUS = 3;
+
+//static
+BOOL LLNetMap::sRotateMap = FALSE;
+LLNetMap* LLNetMap::sInstance = NULL;
+
+LLNetMap::LLNetMap(
+ const std::string& name,
+ const LLRect& rect,
+ const LLColor4& bg_color )
+ :
+ LLUICtrl(name, rect, FALSE, NULL, NULL), mBackgroundColor( bg_color ),
+ mObjectMapTPM(1.f),
+ mObjectMapPixels(255.f),
+ mTargetPanX( 0.f ),
+ mTargetPanY( 0.f ),
+ mCurPanX( 0.f ),
+ mCurPanY( 0.f ),
+ mUpdateNow( FALSE )
+{
+ mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS;
+
+ LLNetMap::sRotateMap = gSavedSettings.getBOOL( "MiniMapRotate" );
+
+ // Surface texture is dynamically generated/updated.
+// createObjectImage();
+
+ mObjectImageCenterGlobal = gAgent.getCameraPositionGlobal();
+
+ const S32 DIR_WIDTH = 10;
+ const S32 DIR_HEIGHT = 10;
+ LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 );
+
+ mTextBoxNorth = new LLTextBox( "N", major_dir_rect );
+ mTextBoxNorth->setDropshadowVisible( TRUE );
+ addChild( mTextBoxNorth );
+
+ LLColor4 minor_color( 1.f, 1.f, 1.f, .7f );
+
+ mTextBoxEast = new LLTextBox( "E", major_dir_rect );
+ mTextBoxEast->setColor( minor_color );
+ addChild( mTextBoxEast );
+
+ mTextBoxWest = new LLTextBox( "W", major_dir_rect );
+ mTextBoxWest->setColor( minor_color );
+ addChild( mTextBoxWest );
+
+ mTextBoxSouth = new LLTextBox( "S", major_dir_rect );
+ mTextBoxSouth->setColor( minor_color );
+ addChild( mTextBoxSouth );
+
+ LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 );
+
+ mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect );
+ mTextBoxSouthEast->setColor( minor_color );
+ addChild( mTextBoxSouthEast );
+
+ mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect );
+ mTextBoxNorthEast->setColor( minor_color );
+ addChild( mTextBoxNorthEast );
+
+ mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect );
+ mTextBoxSouthWest->setColor( minor_color );
+ addChild( mTextBoxSouthWest );
+
+ mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect );
+ mTextBoxNorthWest->setColor( minor_color );
+ addChild( mTextBoxNorthWest );
+
+ // Right-click menu
+ LLMenuGL* menu;
+ menu = new LLMenuGL("popup");
+ menu->setCanTearOff(FALSE);
+ menu->append(new LLMenuItemCallGL("Zoom Close", handleZoomLevel,
+ NULL, (void*)2) );
+ menu->append(new LLMenuItemCallGL("Zoom Medium", handleZoomLevel,
+ NULL, (void*)1) );
+ menu->append(new LLMenuItemCallGL("Zoom Far", handleZoomLevel,
+ NULL, (void*)0) );
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCallGL("Stop Tracking", &LLTracker::stopTracking,
+ &LLTracker::isTracking, NULL) );
+ menu->setVisible(FALSE);
+ addChild(menu);
+ mPopupMenuHandle = menu->mViewHandle;
+
+ sInstance = this;
+
+ gSavedSettings.getControl("MiniMapRotate")->addListener(&mNetMapListener);
+}
+
+LLNetMap::~LLNetMap()
+{
+ sInstance = NULL;
+}
+
+EWidgetType LLNetMap::getWidgetType() const
+{
+ return WIDGET_TYPE_NET_MAP;
+}
+
+LLString LLNetMap::getWidgetTag() const
+{
+ return LL_NET_MAP_TAG;
+}
+
+
+void LLNetMap::setScale( F32 scale )
+{
+ gMiniMapScale = scale;
+ if (gMiniMapScale == 0.f)
+ {
+ gMiniMapScale = 0.1f;
+ }
+
+ if (mObjectImagep.notNull())
+ {
+ F32 half_width = (F32)(mRect.getWidth() / 2);
+ F32 half_height = (F32)(mRect.getHeight() / 2);
+ F32 radius = sqrt( half_width * half_width + half_height * half_height );
+
+ F32 region_widths = (2.f*radius)/gMiniMapScale;
+
+ F32 meters;
+ if (!gWorldPointer)
+ {
+ // Hack! Sometimes world hasn't been initialized at this point.
+ meters = 256.f*region_widths;
+ }
+ else
+ {
+ meters = region_widths * gWorldPointer->getRegionWidthInMeters();
+ }
+
+ F32 num_pixels = (F32)mObjectImagep->getWidth();
+ mObjectMapTPM = num_pixels/meters;
+ mObjectMapPixels = 2.f*radius;
+ }
+
+ mPixelsPerMeter = gMiniMapScale / REGION_WIDTH_METERS;
+
+ mUpdateNow = TRUE;
+}
+
+void LLNetMap::translatePan( F32 delta_x, F32 delta_y )
+{
+ mTargetPanX += delta_x;
+ mTargetPanY += delta_y;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+void LLNetMap::draw()
+{
+ static LLFrameTimer map_timer;
+
+ if (!getVisible() || !gWorldPointer)
+ {
+ return;
+ }
+ if (mObjectImagep.isNull())
+ {
+ createObjectImage();
+ }
+
+ mCurPanX = lerp(mCurPanX, mTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
+ mCurPanY = lerp(mCurPanY, mTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
+
+ // Prepare a scissor region
+ // GLint params[4];
+ // glGetIntegerv( GL_SCISSOR_BOX, params );
+ F32 rotation = 0;
+
+ {
+ LLGLEnable scissor(GL_SCISSOR_TEST);
+
+ {
+ LLGLSNoTexture no_texture;
+ LLUI::setScissorRegionLocal(LLRect(0, mRect.getHeight(), mRect.getWidth(), 0));
+
+ glMatrixMode(GL_MODELVIEW);
+
+ // Draw background rectangle
+ glColor4fv( mBackgroundColor.mV );
+ gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0);
+ }
+
+ // region 0,0 is in the middle
+ S32 center_sw_left = mRect.getWidth() / 2 + llfloor(mCurPanX);
+ S32 center_sw_bottom = mRect.getHeight() / 2 + llfloor(mCurPanY);
+
+ glPushMatrix();
+
+ glTranslatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f);
+
+ if( LLNetMap::sRotateMap )
+ {
+ // rotate subsequent draws to agent rotation
+ rotation = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+ glRotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f);
+ }
+
+ // figure out where agent is
+ S32 region_width = llround(gWorldPointer->getRegionWidthInMeters());
+
+ LLViewerRegion *regionp;
+
+ for (regionp = gWorldPointer->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldPointer->mActiveRegionList.getNextData())
+ {
+ // Find x and y position relative to camera's center.
+ LLVector3 origin_agent = regionp->getOriginAgent();
+ LLVector3 rel_region_pos = origin_agent - gAgent.getCameraPositionAgent();
+ F32 relative_x = (rel_region_pos.mV[0] / region_width) * gMiniMapScale;
+ F32 relative_y = (rel_region_pos.mV[1] / region_width) * gMiniMapScale;
+
+ // background region rectangle
+ F32 bottom = relative_y;
+ F32 left = relative_x;
+ F32 top = bottom + gMiniMapScale ;
+ F32 right = left + gMiniMapScale ;
+
+ if (regionp == gAgent.getRegion())
+ {
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.8f, 0.8f, 0.8f, 1.f);
+ }
+
+ if (!regionp->mAlive)
+ {
+ glColor4f(1.f, 0.5f, 0.5f, 1.f);
+ }
+
+
+ // Draw using texture.
+ LLViewerImage::bindTexture(regionp->getLand().getSTexture());
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(right, top);
+ glEnd();
+
+ // Draw water
+ glAlphaFunc(GL_GREATER, ABOVE_WATERLINE_ALPHA / 255.f );
+ {
+ if (regionp->getLand().getWaterTexture())
+ {
+ LLViewerImage::bindTexture(regionp->getLand().getWaterTexture());
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(right, top);
+ glEnd();
+ }
+ }
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+
+ LLVector3d old_center = mObjectImageCenterGlobal;
+ LLVector3d new_center = gAgent.getCameraPositionGlobal();
+
+ new_center.mdV[0] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[0]);
+ new_center.mdV[1] = (5.f/mObjectMapTPM)*floor(0.2f*mObjectMapTPM*new_center.mdV[1]);
+ new_center.mdV[2] = 0.f;
+
+ if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
+ {
+ mUpdateNow = FALSE;
+ mObjectImageCenterGlobal = new_center;
+
+ // Center moved enough.
+ // Create the base texture.
+ U8 *default_texture = mObjectRawImagep->getData();
+ memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );
+
+ // Draw buildings
+ gObjectList.renderObjectsForMap(*this);
+
+ mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
+
+ map_timer.reset();
+ }
+
+ LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
+ map_center_agent -= gAgent.getCameraPositionAgent();
+ map_center_agent.mV[VX] *= gMiniMapScale/region_width;
+ map_center_agent.mV[VY] *= gMiniMapScale/region_width;
+
+ LLViewerImage::bindTexture(mObjectImagep);
+ F32 image_half_width = 0.5f*mObjectMapPixels;
+ F32 image_half_height = 0.5f*mObjectMapPixels;
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]);
+ glTexCoord2f(0.f, 0.f);
+ glVertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height);
+ glTexCoord2f(1.f, 0.f);
+ glVertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height);
+ glTexCoord2f(1.f, 1.f);
+ glVertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]);
+ glEnd();
+
+ glPopMatrix();
+
+ LLVector3d pos_global;
+ LLVector3 pos_map;
+
+ // Draw avatars
+ for (regionp = gWorldPointer->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldPointer->mActiveRegionList.getNextData())
+ {
+ const LLVector3d& origin_global = regionp->getOriginGlobal();
+
+ S32 count = regionp->mMapAvatars.count();
+ S32 i;
+ LLVector3 pos_local;
+ U32 compact_local;
+ U8 bits;
+ for (i = 0; i < count; i++)
+ {
+ compact_local = regionp->mMapAvatars.get(i);
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VZ] = F32(bits) * 4.f;
+ compact_local >>= 8;
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VY] = (F32)bits;
+ compact_local >>= 8;
+
+ bits = compact_local & 0xFF;
+ pos_local.mV[VX] = (F32)bits;
+
+ pos_global.setVec( pos_local );
+ pos_global += origin_global;
+
+ pos_map = globalPosToView(pos_global);
+ LLWorldMapView::drawAvatar(pos_map.mV[VX], pos_map.mV[VY],
+ gAvatarMapColor,
+ pos_map.mV[VZ]);
+ }
+ }
+
+ // Draw dot for autopilot target
+ if (gAgent.getAutoPilot())
+ {
+ drawTracking( gAgent.getAutoPilotTargetGlobal(), gTrackColor );
+ }
+ else
+ {
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_AVATAR == tracking_status )
+ {
+ drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor );
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status
+ || LLTracker::TRACKING_LOCATION == tracking_status )
+ {
+ drawTracking( LLTracker::getTrackedPositionGlobal(), gTrackColor );
+ }
+ }
+
+ // Draw dot for self avatar position
+ //drawTracking( gAgent.getPosGlobalFromAgent(gAgent.getFrameAgent().getCenter()), gSelfMapColor );
+ pos_global = gAgent.getPositionGlobal();
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY]) - 4,
+ LLWorldMapView::sAvatarYouSmallImage,
+ LLColor4::white);
+
+ // Draw frustum
+ F32 meters_to_pixels = gMiniMapScale/ gWorldPointer->getRegionWidthInMeters();
+
+ F32 horiz_fov = gCamera->getView() * gCamera->getAspect();
+ F32 far_clip_meters = gCamera->getFar();
+ F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
+
+ F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
+ F32 half_width_pixels = half_width_meters * meters_to_pixels;
+
+ F32 ctr_x = (F32)center_sw_left;
+ F32 ctr_y = (F32)center_sw_bottom;
+
+
+ LLGLSNoTexture no_texture;
+
+ if( LLNetMap::sRotateMap )
+ {
+ glColor4fv(gFrustumMapColor.mV);
+
+ glBegin( GL_TRIANGLES );
+ glVertex2f( ctr_x, ctr_y );
+ glVertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels );
+ glVertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels );
+ glEnd();
+ }
+ else
+ {
+ glColor4fv(gRotatingFrustumMapColor.mV);
+
+ // If we don't rotate the map, we have to rotate the frustum.
+ glPushMatrix();
+ glTranslatef( ctr_x, ctr_y, 0 );
+ glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
+ glBegin( GL_TRIANGLES );
+ glVertex2f( 0, 0 );
+ glVertex2f( -half_width_pixels, far_clip_pixels );
+ glVertex2f( half_width_pixels, far_clip_pixels );
+ glEnd();
+ glPopMatrix();
+ }
+ }
+
+ // Rotation of 0 means that North is up
+ setDirectionPos( mTextBoxEast, rotation );
+ setDirectionPos( mTextBoxNorth, rotation + F_PI_BY_TWO );
+ setDirectionPos( mTextBoxWest, rotation + F_PI );
+ setDirectionPos( mTextBoxSouth, rotation + F_PI + F_PI_BY_TWO );
+
+ setDirectionPos( mTextBoxNorthEast, rotation + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxNorthWest, rotation + F_PI_BY_TWO + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxSouthWest, rotation + F_PI + F_PI_BY_TWO / 2);
+ setDirectionPos( mTextBoxSouthEast, rotation + F_PI + F_PI_BY_TWO + F_PI_BY_TWO / 2);
+
+ LLUICtrl::draw();
+}
+
+LLVector3 LLNetMap::globalPosToView( const LLVector3d& global_pos )
+{
+ LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
+ LLVector3 pos_local;
+ pos_local.setVec(relative_pos_global); // convert to floats from doubles
+
+ pos_local.mV[VX] *= mPixelsPerMeter;
+ pos_local.mV[VY] *= mPixelsPerMeter;
+ // leave Z component in meters
+
+ if( LLNetMap::sRotateMap )
+ {
+ F32 radians = atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+ LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
+ pos_local.rotVec( rot );
+ }
+
+ pos_local.mV[VX] += mRect.getWidth() / 2 + mCurPanX;
+ pos_local.mV[VY] += mRect.getHeight() / 2 + mCurPanY;
+
+ return pos_local;
+}
+
+void LLNetMap::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
+ BOOL draw_arrow )
+{
+ LLVector3 pos_local = globalPosToView( pos_global );
+ if( (pos_local.mV[VX] < 0) ||
+ (pos_local.mV[VY] < 0) ||
+ (pos_local.mV[VX] >= mRect.getWidth()) ||
+ (pos_local.mV[VY] >= mRect.getHeight()) )
+ {
+ if (draw_arrow)
+ {
+ S32 x = llround( pos_local.mV[VX] );
+ S32 y = llround( pos_local.mV[VY] );
+ LLWorldMapView::drawTrackingCircle( mRect, x, y, color, 1, 10 );
+ LLWorldMapView::drawTrackingArrow( mRect, x, y, color );
+ }
+ }
+ else
+ {
+ LLWorldMapView::drawTrackingDot(pos_local.mV[VX],
+ pos_local.mV[VY],
+ color,
+ pos_local.mV[VZ]);
+ }
+}
+
+LLVector3d LLNetMap::viewPosToGlobal( S32 x, S32 y )
+{
+ x -= llround(mRect.getWidth() / 2 + mCurPanX);
+ y -= llround(mRect.getHeight() / 2 + mCurPanY);
+
+ LLVector3 pos_local( (F32)x, (F32)y, 0 );
+
+ F32 radians = - atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] );
+
+ if( LLNetMap::sRotateMap )
+ {
+ LLQuaternion rot(radians, LLVector3(0.f, 0.f, 1.f));
+ pos_local.rotVec( rot );
+ }
+
+ pos_local *= ( gWorldPointer->getRegionWidthInMeters() / gMiniMapScale );
+
+ LLVector3d pos_global;
+ pos_global.setVec( pos_local );
+ pos_global += gAgent.getCameraPositionGlobal();
+
+ return pos_global;
+}
+
+BOOL LLNetMap::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // note that clicks are reversed from what you'd think
+ setScale(llclamp(gMiniMapScale - clicks*MAP_SCALE_INCREMENT, MAP_SCALE_MIN, MAP_SCALE_MAX));
+ return TRUE;
+}
+
+BOOL LLNetMap::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen )
+{
+ BOOL handled = FALSE;
+ if (gDisconnected)
+ {
+ return FALSE;
+ }
+ if( getVisible() && pointInView( x, y ) )
+ {
+ LLViewerRegion* region = gWorldPointer->getRegionFromPosGlobal( viewPosToGlobal( x, y ) );
+ if( region )
+ {
+ msg.assign( region->getName() );
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ char buffer[MAX_STRING];
+ msg.append("\n");
+ region->getHost().getHostName(buffer, MAX_STRING);
+ msg.append(buffer);
+ msg.append("\n");
+ region->getHost().getString(buffer, MAX_STRING);
+ msg.append(buffer);
+#endif
+ // FIXME: put in XML so it can be translated
+ msg.append("\n(Double-click to open Map)");
+
+ S32 SLOP = 4;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ handled = TRUE;
+ }
+ return handled;
+}
+
+
+void LLNetMap::setDirectionPos( LLTextBox* text_box, F32 rotation )
+{
+ // Rotation is in radians.
+ // Rotation of 0 means x = 1, y = 0 on the unit circle.
+
+
+ F32 map_half_height = (F32)(mRect.getHeight() / 2);
+ F32 map_half_width = (F32)(mRect.getWidth() / 2);
+ F32 text_half_height = (F32)(text_box->getRect().getHeight() / 2);
+ F32 text_half_width = (F32)(text_box->getRect().getWidth() / 2);
+ F32 radius = llmin( map_half_height - text_half_height, map_half_width - text_half_width );
+
+ // Inset by a little to account for position display.
+ radius -= 8.f;
+
+ text_box->setOrigin(
+ llround(map_half_width - text_half_width + radius * cos( rotation )),
+ llround(map_half_height - text_half_height + radius * sin( rotation )) );
+}
+
+void LLNetMap::renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius_meters )
+{
+ LLVector3 local_pos;
+ local_pos.setVec( pos - mObjectImageCenterGlobal );
+
+ S32 diameter_pixels = llround(2 * radius_meters * mObjectMapTPM);
+ renderPoint( local_pos, color, diameter_pixels );
+}
+
+
+void LLNetMap::renderPoint(const LLVector3 &pos_local, const LLColor4U &color,
+ S32 diameter, S32 relative_height)
+{
+ if (diameter <= 0)
+ {
+ return;
+ }
+
+ 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);
+
+ if ((x_offset < 0) || (x_offset >= image_width))
+ {
+ return;
+ }
+ if ((y_offset < 0) || (y_offset >= image_height))
+ {
+ return;
+ }
+
+ U8 *datap = mObjectRawImagep->getData();
+
+ S32 neg_radius = diameter / 2;
+ S32 pos_radius = diameter - neg_radius;
+ S32 x, y;
+
+ if (relative_height > 0)
+ {
+ // ...point above agent
+ S32 px, py;
+
+ // vertical line
+ px = x_offset;
+ for (y = -neg_radius; y < pos_radius; y++)
+ {
+ py = y_offset + y;
+ if ((py < 0) || (py >= image_height))
+ {
+ continue;
+ }
+ S32 offset = px + py * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+
+ // top line
+ py = y_offset + pos_radius - 1;
+ for (x = -neg_radius; x < pos_radius; x++)
+ {
+ px = x_offset + x;
+ if ((px < 0) || (px >= image_width))
+ {
+ continue;
+ }
+ S32 offset = px + py * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+ }
+ else
+ {
+ // ...point level with agent
+ for (x = -neg_radius; x < pos_radius; x++)
+ {
+ S32 p_x = x_offset + x;
+ if ((p_x < 0) || (p_x >= image_width))
+ {
+ continue;
+ }
+
+ for (y = -neg_radius; y < pos_radius; y++)
+ {
+ S32 p_y = y_offset + y;
+ if ((p_y < 0) || (p_y >= image_height))
+ {
+ continue;
+ }
+ S32 offset = p_x + p_y * image_width;
+ ((U32*)datap)[offset] = color.mAll;
+ }
+ }
+ }
+}
+
+void LLNetMap::createObjectImage()
+{
+ // Find the size of the side of a square that surrounds the circle that surrounds mRect.
+ F32 half_width = (F32)(mRect.getWidth() / 2);
+ F32 half_height = (F32)(mRect.getHeight() / 2);
+ F32 radius = sqrt( half_width * half_width + half_height * half_height );
+ S32 square_size = S32( 2 * radius );
+
+ // Find the least power of two >= the minimum size.
+ const S32 MIN_SIZE = 32;
+ const S32 MAX_SIZE = 256;
+ S32 img_size = MIN_SIZE;
+ while( (img_size*2 < square_size ) && (img_size < MAX_SIZE) )
+ {
+ img_size <<= 1;
+ }
+
+ if( mObjectImagep.isNull() ||
+ (mObjectImagep->getWidth() != img_size) ||
+ (mObjectImagep->getHeight() != img_size) )
+ {
+ mObjectRawImagep = new LLImageRaw(img_size, img_size, 4);
+ U8* data = mObjectRawImagep->getData();
+ memset( data, 0, img_size * img_size * 4 );
+ mObjectImagep = new LLImageGL( mObjectRawImagep, FALSE);
+ setScale(gMiniMapScale);
+ }
+ mUpdateNow = TRUE;
+}
+
+BOOL LLNetMap::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ LLFloaterWorldMap::show(NULL, FALSE);
+ return TRUE;
+}
+
+BOOL LLNetMap::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLMenuGL* menu = (LLMenuGL*)LLView::getViewByHandle(mPopupMenuHandle);
+ if (menu)
+ {
+ menu->buildDrawLabels();
+ menu->updateParent(gMenuHolder);
+ LLMenuGL::showPopup(this, menu, x, y);
+ }
+ return TRUE;
+}
+
+
+// static
+void LLNetMap::handleZoomLevel(void* which)
+{
+ intptr_t level = (intptr_t)which;
+
+ switch(level)
+ {
+ case 0:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MIN);
+ break;
+ case 1:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MID);
+ break;
+ case 2:
+ LLNetMap::sInstance->setScale(MAP_SCALE_MAX);
+ break;
+ default:
+ break;
+ }
+}
+
+bool LLRotateNetMapListener::handleEvent(LLPointer<LLEvent> event, const LLSD& user_data)
+{
+ LLNetMap::setRotateMap(event->getValue().asBoolean());
+ return true;
+}
diff --git a/indra/newview/llnetmap.h b/indra/newview/llnetmap.h
new file mode 100644
index 0000000000..bd5c47008b
--- /dev/null
+++ b/indra/newview/llnetmap.h
@@ -0,0 +1,104 @@
+/**
+ * @file llnetmap.h
+ * @brief A little map of the world with network information
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLNETMAP_H
+#define LL_LLNETMAP_H
+
+#include "llmath.h"
+#include "lluictrl.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+#include "llimage.h"
+#include "llimagegl.h"
+
+class LLColor4U;
+class LLCoordGL;
+class LLTextBox;
+class LLMenuGL;
+
+class LLRotateNetMapListener : public LLSimpleListener
+{
+public:
+ bool handleEvent(LLPointer<LLEvent>, const LLSD& user_data);
+};
+
+class LLNetMap : public LLUICtrl
+{
+public:
+ LLNetMap(const std::string& name, const LLRect& rect, const LLColor4& bg_color );
+ virtual ~LLNetMap();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void draw();
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleRightMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
+
+ void setScale( F32 scale );
+ void translatePan( F32 delta_x, F32 delta_y );
+ void setPan( F32 x, F32 y ) { mTargetPanX = x; mTargetPanY = y; }
+
+ const LLVector3d& getObjectImageCenterGlobal() { return mObjectImageCenterGlobal; }
+ void renderPoint(const LLVector3 &pos, const LLColor4U &color,
+ S32 diameter, S32 relative_height = 0);
+ void renderScaledPointGlobal( const LLVector3d& pos, const LLColor4U &color, F32 radius );
+
+ LLVector3 globalPosToView(const LLVector3d& global_pos);
+ LLVector3d viewPosToGlobal(S32 x,S32 y);
+
+ static void setRotateMap( BOOL b ) { LLNetMap::sRotateMap = b; }
+ static void handleZoomLevel(void* which);
+
+ void drawTracking( const LLVector3d& pos_global,
+ const LLColor4& color,
+ BOOL draw_arrow = TRUE);
+
+protected:
+ void setDirectionPos( LLTextBox* text_box, F32 rotation );
+ void createObjectImage();
+ static void teleport( const LLVector3d& destination );
+ static void fly( const LLVector3d& destination );
+
+public:
+ LLViewHandle mPopupMenuHandle;
+ LLColor4 mBackgroundColor;
+
+ F32 mScale; // Size of a region in pixels
+ F32 mPixelsPerMeter; // world meters to map pixels
+ F32 mObjectMapTPM; // texels per meter on map
+ F32 mObjectMapPixels; // Width of object map in pixels;
+ F32 mTargetPanX;
+ F32 mTargetPanY;
+ F32 mCurPanX;
+ F32 mCurPanY;
+ BOOL mUpdateNow;
+ LLVector3d mObjectImageCenterGlobal;
+ LLPointer<LLImageRaw> mObjectRawImagep;
+ LLPointer<LLImageGL> mObjectImagep;
+ LLTextBox* mTextBoxEast;
+ LLTextBox* mTextBoxNorth;
+ LLTextBox* mTextBoxWest;
+ LLTextBox* mTextBoxSouth;
+
+ LLTextBox* mTextBoxSouthEast;
+ LLTextBox* mTextBoxNorthEast;
+ LLTextBox* mTextBoxNorthWest;
+ LLTextBox* mTextBoxSouthWest;
+
+ LLRotateNetMapListener mNetMapListener;
+
+ static BOOL sRotateMap;
+ static LLNetMap* sInstance;
+};
+
+
+#endif
diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp
new file mode 100644
index 0000000000..ed99cbe855
--- /dev/null
+++ b/indra/newview/lloverlaybar.cpp
@@ -0,0 +1,573 @@
+/**
+ * @file lloverlaybar.cpp
+ * @brief LLOverlayBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Temporary buttons that appear at the bottom of the screen when you
+// are in a mode.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lloverlaybar.h"
+
+#include "audioengine.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llvoavatar.h"
+#include "llmediaengine.h"
+#include "viewer.h"
+#include "llui.h"
+#include "llviewermenu.h" // handle_reset_view()
+#include "llviewerparcelmgr.h"
+#include "llwebbrowserctrl.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+
+//
+// Globals
+//
+
+LLOverlayBar *gOverlayBar = NULL;
+
+extern S32 MENU_BAR_HEIGHT;
+
+//
+// Functions
+//
+
+
+//static
+void* LLOverlayBar::createMediaRemote(void* userdata)
+{
+
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+
+
+ self->mMediaRemote = new LLMediaRemoteCtrl ( "media_remote",
+ "media",
+ LLRect(),
+ "panel_media_remote.xml");
+ return self->mMediaRemote;
+}
+
+
+
+void* LLOverlayBar::createMusicRemote(void* userdata)
+{
+
+ LLOverlayBar *self = (LLOverlayBar*)userdata;
+
+ self->mMusicRemote = new LLMediaRemoteCtrl ( "music_remote",
+ "music",
+ LLRect(),
+ "panel_music_remote.xml" );
+ return self->mMusicRemote;
+}
+
+
+
+
+LLOverlayBar::LLOverlayBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, rect, FALSE) // not bordered
+{
+ setMouseOpaque(FALSE);
+ setIsChrome(TRUE);
+
+ isBuilt = FALSE;
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["media_remote"] = LLCallbackMap(LLOverlayBar::createMediaRemote, this);
+ factory_map["music_remote"] = LLCallbackMap(LLOverlayBar::createMusicRemote, this);
+
+ gUICtrlFactory->buildPanel(this, "panel_overlaybar.xml", &factory_map);
+
+ childSetAction("IM Received",onClickIMReceived,this);
+ childSetAction("Set Not Busy",onClickSetNotBusy,this);
+ childSetAction("Release Keys",onClickReleaseKeys,this);
+ childSetAction("Mouselook",onClickMouselook,this);
+ childSetAction("Stand Up",onClickStandUp,this);
+
+ mMusicRemote->addObserver ( this );
+
+ if ( gAudiop )
+ {
+ mMusicRemote->setVolume ( gSavedSettings.getF32 ( "AudioLevelMusic" ) );
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ };
+
+ mIsFocusRoot = TRUE;
+
+ mMediaRemote->addObserver ( this );
+ mMediaRemote->setVolume ( gSavedSettings.getF32 ( "MediaAudioVolume" ) );
+
+ isBuilt = true;
+
+ layoutButtons();
+}
+
+LLOverlayBar::~LLOverlayBar()
+{
+ // LLView destructor cleans up children
+
+ mMusicRemote->remObserver ( this );
+ mMediaRemote->remObserver ( this );
+}
+
+EWidgetType LLOverlayBar::getWidgetType() const
+{
+ return WIDGET_TYPE_OVERLAY_BAR;
+}
+
+LLString LLOverlayBar::getWidgetTag() const
+{
+ return LL_OVERLAY_BAR_TAG;
+}
+
+// virtual
+void LLOverlayBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLView::reshape(width, height, called_from_parent);
+
+ if (isBuilt)
+ {
+ layoutButtons();
+ }
+}
+
+
+void LLOverlayBar::layoutButtons()
+{
+ S32 width = mRect.getWidth();
+ if (width > 800) width = 800;
+
+ S32 count = getChildCount();
+ const S32 PAD = gSavedSettings.getS32("StatusBarPad");
+
+ F32 segment_width = (F32)(width) / (F32)count;
+
+ S32 btn_width = lltrunc(segment_width - PAD);
+
+ S32 remote_width = mMusicRemote->getRect().getWidth();
+
+ // Evenly space all views
+ LLRect r;
+ S32 i = 0;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ r = view->getRect();
+ r.mLeft = (width) - llround((i+1)*segment_width);
+ r.mRight = r.mLeft + btn_width;
+ view->setRect(r);
+ i++;
+ }
+
+ // Fix up remotes to have constant width because they can't shrink
+ r = mMusicRemote->getRect();
+ r.mRight = r.mLeft + remote_width;
+ mMusicRemote->setRect(r);
+
+ r = mMediaRemote->getRect();
+ r.mLeft = mMusicRemote->getRect().mRight + PAD;
+ r.mRight = r.mLeft + remote_width;
+ mMediaRemote->setRect(r);
+
+ updateRect();
+}
+
+void LLOverlayBar::draw()
+{
+ // retrieve rounded rect image
+ LLUUID image_id;
+ image_id.set(gViewerArt.getString("rounded_square.tga"));
+ LLViewerImage* imagep = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ if (imagep)
+ {
+ LLGLSTexture texture_enabled;
+ LLViewerImage::bindTexture(imagep);
+
+ const S32 PAD = gSavedSettings.getS32("StatusBarPad");
+
+ // draw rounded rect tabs behind all children
+ LLRect r;
+ // focus highlights
+ LLColor4 color = gColors.getColor("FloaterFocusBorderColor");
+ glColor4fv(color.mV);
+ if(gFocusMgr.childHasKeyboardFocus(gBottomPanel))
+ {
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getEnabled() && view->getVisible())
+ {
+ r = view->getRect();
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 - 1,
+ r.mTop + 3,
+ r.mRight + PAD/3 + 1,
+ r.mBottom,
+ imagep->getWidth(),
+ imagep->getHeight(),
+ 16,
+ ROUNDED_RECT_TOP);
+ }
+ }
+ }
+
+ // main tabs
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getEnabled() && view->getVisible())
+ {
+ r = view->getRect();
+ // draw a nice little pseudo-3D outline
+ color = gColors.getColor("DefaultShadowDark");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 2, r.mRight + PAD/3, r.mBottom,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ color = gColors.getColor("DefaultHighlightLight");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3, r.mTop + 2, r.mRight + PAD/3 - 3, r.mBottom,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ // here's the main background. Note that it overhangs on the bottom so as to hide the
+ // focus highlight on the bottom panel, thus producing the illusion that the focus highlight
+ // continues around the tabs
+ color = gColors.getColor("FocusBackgroundColor");
+ glColor4fv(color.mV);
+ gl_segmented_rect_2d_tex(r.mLeft - PAD/3 + 1, r.mTop + 1, r.mRight + PAD/3 - 1, r.mBottom - 1,
+ imagep->getWidth(), imagep->getHeight(), 16, ROUNDED_RECT_TOP);
+ }
+ }
+ }
+
+ // draw children on top
+ LLPanel::draw();
+}
+
+
+// Per-frame updates of visibility
+void LLOverlayBar::refresh()
+{
+ BOOL im_received = gIMView->getIMReceived();
+ childSetVisible("IM Received", im_received);
+ childSetEnabled("IM Received", im_received);
+
+ BOOL busy = gAgent.getBusy();
+ childSetVisible("Set Not Busy", busy);
+ childSetEnabled("Set Not Busy", busy);
+
+ BOOL controls_grabbed = gAgent.anyControlGrabbed();
+
+ childSetVisible("Release Keys", controls_grabbed);
+ childSetEnabled("Release Keys", controls_grabbed);
+
+
+ BOOL mouselook_grabbed;
+ mouselook_grabbed = gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_DOWN_INDEX)
+ || gAgent.isControlGrabbed(CONTROL_ML_LBUTTON_UP_INDEX);
+
+
+ childSetVisible("Mouselook", mouselook_grabbed);
+ childSetEnabled("Mouselook", mouselook_grabbed);
+
+ BOOL sitting = FALSE;
+ if (gAgent.getAvatarObject())
+ {
+ sitting = gAgent.getAvatarObject()->mIsSitting;
+ childSetVisible("Stand Up", sitting);
+ childSetEnabled("Stand Up", sitting);
+
+ }
+
+ if ( gAudiop )
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (!parcel
+ || !parcel->getMusicURL()
+ || !parcel->getMusicURL()[0]
+ || !gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ mMusicRemote->setVisible(FALSE);
+ mMusicRemote->setEnabled(FALSE);
+ }
+ else
+ {
+ mMusicRemote->setVisible(TRUE);
+ mMusicRemote->setEnabled(TRUE);
+
+ S32 musicPlaying = gAudiop->isInternetStreamPlaying();
+
+ if ( musicPlaying == 0 ) // stopped
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( musicPlaying == 1 ) // playing
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ if (gAudiop)
+ {
+ gAudiop->setInternetStreamGain ( gSavedSettings.getF32 ( "AudioLevelMusic" ) );
+ }
+ }
+ else
+ if ( musicPlaying == 2 ) // paused
+ {
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ }
+ }
+
+ // if there is a url and a texture and media is enabled and available and media streaming is on... (phew!)
+ if ( LLMediaEngine::getInstance () &&
+ LLMediaEngine::getInstance ()->getUrl ().length () &&
+ LLMediaEngine::getInstance ()->getImageUUID ().notNull () &&
+ LLMediaEngine::getInstance ()->isEnabled () &&
+ LLMediaEngine::getInstance ()->isAvailable () &&
+ gSavedSettings.getBOOL ( "AudioStreamingVideo" ) )
+ {
+ // display remote control
+ mMediaRemote->setVisible ( TRUE );
+ mMediaRemote->setEnabled ( TRUE );
+
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer () )
+ {
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPlaying () ||
+ LLMediaEngine::getInstance ()->getMediaRenderer ()->isLooping () )
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
+ }
+ else
+ if ( LLMediaEngine::getInstance ()->getMediaRenderer ()->isPaused () )
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ }
+ else
+ {
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+ };
+ }
+ else
+ {
+ mMediaRemote->setVisible ( FALSE );
+ mMediaRemote->setEnabled ( FALSE );
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+
+ BOOL any_button = (childIsVisible("IM Received")
+ || childIsVisible("Set Not Busy")
+ || childIsVisible("Release Keys")
+ || childIsVisible("Mouselook")
+ || childIsVisible("Stand Up")
+ || mMusicRemote->getVisible()
+ || mMediaRemote->getVisible() );
+
+
+ // turn off the whole bar in mouselook
+ if (gAgent.cameraMouselook())
+ {
+ setVisible(FALSE);
+ }
+ else
+ {
+ setVisible(any_button);
+ };
+}
+
+//-----------------------------------------------------------------------
+// Static functions
+//-----------------------------------------------------------------------
+
+// static
+void LLOverlayBar::onClickIMReceived(void*)
+{
+ gIMView->setFloaterOpen(TRUE);
+}
+
+
+// static
+void LLOverlayBar::onClickSetNotBusy(void*)
+{
+ gAgent.clearBusy();
+}
+
+
+// static
+void LLOverlayBar::onClickReleaseKeys(void*)
+{
+ gAgent.forceReleaseControls();
+}
+
+// static
+void LLOverlayBar::onClickResetView(void* data)
+{
+ handle_reset_view();
+}
+
+//static
+void LLOverlayBar::onClickMouselook(void*)
+{
+ gAgent.changeCameraToMouselook();
+}
+
+//static
+void LLOverlayBar::onClickStandUp(void*)
+{
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void
+LLOverlayBar::
+onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+ F32 value = eventIn.getValue ();
+
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ gAudiop->setInternetStreamGain ( value );
+ };
+ gSavedSettings.setF32 ( "AudioLevelMusic", value );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->setVolume ( value );
+ gSavedSettings.setF32 ( "MediaAudioVolume", value );
+
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void
+LLOverlayBar::
+onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ if ( control == mMusicRemote )
+ {
+ if ( gAudiop )
+ {
+ gAudiop->stopInternetStream ();
+ };
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->stop ();
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Stop, TRUE );
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void LLOverlayBar::onPlayButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ if ( parcel )
+ {
+ // this doesn't work properly when crossing parcel boundaries - even when the
+ // stream is stopped, it doesn't return the right thing - commenting out for now.
+ //if ( gAudiop->isInternetStreamPlaying() == 0 )
+ //{
+ const char* music_url = parcel->getMusicURL();
+
+ gAudiop->startInternetStream(music_url);
+
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ //}
+ }
+ };
+
+ // CP: this is the old way of doing things (click play each time on a parcel to start stream)
+ //if (gAudiop)
+ //{
+ // if (gAudiop->isInternetStreamPlaying() > 0)
+ // {
+ // gAudiop->pauseInternetStream ( 0 );
+ // }
+ // else
+ // {
+ // if (parcel)
+ // {
+ // const char* music_url = parcel->getMusicURL();
+ // gAudiop->startInternetStream(music_url);
+ // }
+ // }
+ //};
+ //mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Stop, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ bool web_url = (parcel->getParcelFlag(PF_URL_WEB_PAGE) || parcel->getParcelFlag(PF_URL_RAW_HTML));
+ LLString path( "" );
+ #if LL_MOZILLA_ENABLED
+ LLString mozilla_subdir;
+ if (web_url)
+ {
+ path = get_mozilla_path();
+ }
+ #endif
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl( true, web_url, path );
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Play, TRUE );
+ }
+ };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+//
+void LLOverlayBar::onPauseButtonPressed( const LLMediaRemoteCtrlObserver::EventType& eventIn )
+{
+ LLUICtrl* control = eventIn.getControl ();
+
+ if ( control == mMusicRemote )
+ {
+ if (gAudiop)
+ {
+ gAudiop->pauseInternetStream ( 1 );
+ };
+ mMusicRemote->setTransportState ( LLMediaRemoteCtrl::Play, FALSE );
+ }
+ else
+ if ( control == mMediaRemote )
+ {
+ LLMediaEngine::getInstance ()->pause ();
+ mMediaRemote->setTransportState ( LLMediaRemoteCtrl::Pause, TRUE );
+ };
+}
diff --git a/indra/newview/lloverlaybar.h b/indra/newview/lloverlaybar.h
new file mode 100644
index 0000000000..78f544df57
--- /dev/null
+++ b/indra/newview/lloverlaybar.h
@@ -0,0 +1,77 @@
+/**
+ * @file lloverlaybar.h
+ * @brief LLOverlayBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLOVERLAYBAR_H
+#define LL_LLOVERLAYBAR_H
+
+#include "llpanel.h"
+#include "llmediaremotectrl.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 STATUS_BAR_HEIGHT;
+
+class LLButton;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+class LLSlider;
+class LLVolumeSliderCtrl;
+
+class LLOverlayBar
+: public LLPanel,
+ public LLMediaRemoteCtrlObserver
+{
+public:
+ LLOverlayBar(const std::string& name, const LLRect& rect );
+ ~LLOverlayBar();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ void refresh();
+
+ void layoutButtons();
+
+ /*virtual*/ void draw();
+
+ static void onClickIMReceived(void* data);
+ static void onClickSetNotBusy(void* data);
+ static void onClickReleaseKeys(void* data);
+ static void onClickMouselook(void* data);
+ static void onClickStandUp(void* data);
+ static void onClickResetView(void* data);
+
+ // observer overrides
+ void onVolumeChange ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onStopButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onPlayButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+ void onPauseButtonPressed ( const LLMediaRemoteCtrlObserver::EventType& eventIn );
+
+ LLMediaRemoteCtrl* getMusicRemoteControl () { return mMusicRemote; };
+
+protected:
+
+ static void* createMusicRemote(void* userdata);
+ static void* createMediaRemote(void* userdata);
+
+protected:
+ LLMediaRemoteCtrl* mMusicRemote;
+ LLMediaRemoteCtrl* mMediaRemote;
+ BOOL isBuilt;
+};
+
+extern LLOverlayBar* gOverlayBar;
+
+#endif
diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp
new file mode 100644
index 0000000000..185073b6e7
--- /dev/null
+++ b/indra/newview/llpanelavatar.cpp
@@ -0,0 +1,2347 @@
+/**
+ * @file llpanelavatar.cpp
+ * @brief LLPanelAvatar and related class implementations
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelavatar.h"
+
+#include "llfontgl.h"
+#include "llcachename.h"
+
+#include "llavatarconstants.h"
+#include "lluiconstants.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llcallingcard.h"
+#include "llcheckboxctrl.h"
+#include "llfloater.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermute.h"
+#include "llfloaterrate.h"
+#include "llfloateravatarinfo.h"
+#include "lliconctrl.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "llnameeditor.h"
+#include "llmutelist.h"
+#include "llpanelclassified.h"
+#include "llpanelpick.h"
+#include "llscrolllistctrl.h"
+#include "llstatusbar.h"
+#include "lltabcontainer.h"
+#include "lltabcontainervertical.h"
+#include "llimview.h"
+#include "lltooldraganddrop.h"
+#include "lluiconstants.h"
+#include "llvoavatar.h"
+#include "llviewermenu.h" // FIXME: for is_agent_friend()
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewborder.h"
+#include "llweb.h"
+#include "llinventorymodel.h"
+#include "viewer.h" // for gUserServer
+#include "roles_constants.h"
+#include "llwebbrowserctrl.h"
+
+#define kArraySize( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) )
+
+#include "llvieweruictrlfactory.h"
+
+// Statics
+LLLinkedList<LLPanelAvatar> LLPanelAvatar::sAllPanels;
+BOOL LLPanelAvatar::sAllowFirstLife = FALSE;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+
+// RN: move these to lldbstrings.h
+static const S32 DB_USER_FAVORITES_STR_LEN = 254;
+
+static const char IM_DISABLED_TOOLTIP[] = "Instant Message (IM).\nDisabled because you do not have their card.";
+static const char IM_ENABLED_TOOLTIP[] = "Instant Message (IM)";
+static const S32 LEFT = HPAD;
+
+static const S32 RULER0 = 65;
+static const S32 RULER1 = RULER0 + 5;
+static const S32 RULER2 = RULER1 + 90;
+static const S32 RULER3 = RULER2 + 90;
+static const S32 RULER4 = RULER3 + 10;
+
+static const S32 PICT_WIDTH = 180;
+static const S32 PICT_HEIGHT = 135;
+
+static const S32 RULER5 = RULER4 + 140;
+static const S32 WIDTH = RULER5 + 16;
+
+static const S32 MAX_CHARS = 254;
+
+static const LLColor4 WHITE(1,1,1,1);
+static const LLColor4 BLACK(0,0,0,1);
+static const LLColor4 CLEAR(0,0,0,0);
+
+extern void handle_lure(const LLUUID& invitee);
+extern void handle_pay_by_id(const LLUUID& payee);
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDropTarget
+//
+// This handy class is a simple way to drop something on another
+// view. It handles drop events, always setting itself to the size of
+// its parent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLDropTarget : public LLView
+{
+public:
+ LLDropTarget(const std::string& name, const LLRect& rect, const LLUUID& agent_id);
+ ~LLDropTarget();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ void doDrop(EDragAndDropType cargo_type, void* cargo_data);
+
+ //
+ // LLView functionality
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+ void setAgentID(const LLUUID &agent_id) { mAgentID = agent_id; }
+protected:
+ LLUUID mAgentID;
+};
+
+
+LLDropTarget::LLDropTarget(const std::string& name, const LLRect& rect,
+ const LLUUID& agent_id) :
+ LLView(name, rect, NOT_MOUSE_OPAQUE, FOLLOWS_ALL),
+ mAgentID(agent_id)
+{
+}
+
+LLDropTarget::~LLDropTarget()
+{
+}
+
+EWidgetType LLDropTarget::getWidgetType() const
+{
+ return WIDGET_TYPE_DROP_TARGET;
+}
+
+LLString LLDropTarget::getWidgetTag() const
+{
+ return LL_DROP_TARGET_TAG;
+}
+
+void LLDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
+{
+ llinfos << "LLDropTarget::doDrop()" << llendl;
+}
+
+BOOL LLDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+ if(getParent())
+ {
+ // check if inside
+ //LLRect parent_rect = mParentView->getRect();
+ //mRect.set(0, parent_rect.getHeight(), parent_rect.getWidth(), 0);
+ handled = TRUE;
+
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
+ if(gInventory.getItem(inv_item->getUUID())
+ && LLToolDragAndDrop::isInventoryGiveAcceptable(inv_item))
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventory(mAgentID, inv_item);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ {
+ LLViewerInventoryCategory* inv_cat = (LLViewerInventoryCategory*)cargo_data;
+ if( gInventory.getCategory( inv_cat->getUUID() ) )
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ LLToolDragAndDrop::giveInventoryCategory(mAgentID,
+ inv_cat);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CALLINGCARD:
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ return handled;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarSecondLife()
+//-----------------------------------------------------------------------------
+LLPanelAvatarSecondLife::LLPanelAvatarSecondLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar )
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mPartnerID()
+{
+}
+
+LLPanelAvatarSecondLife::~LLPanelAvatarSecondLife()
+{
+}
+
+void LLPanelAvatarSecondLife::draw()
+{
+ if (getVisible())
+ {
+ updatePartnerName();
+ }
+
+ LLPanel::draw();
+}
+
+void LLPanelAvatarSecondLife::updatePartnerName()
+{
+ if (mPartnerID.notNull())
+ {
+ char first[128];
+ char last[128];
+ BOOL found = gCacheName->getName(mPartnerID, first, last);
+ if (found)
+ {
+ childSetTextArg("partner_edit", "[FIRST]", first);
+ childSetTextArg("partner_edit", "[LAST]", last);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// clearControls()
+// Empty the data out of the controls, since we have to wait for new
+// data off the network.
+//-----------------------------------------------------------------------------
+void LLPanelAvatarSecondLife::clearControls()
+{
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(this,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(LLUUID::null);
+ }
+ childSetValue("about", "");
+ childSetValue("born", "");
+ childSetValue("acct", "");
+
+ childSetTextArg("partner_edit", "[FIRST]", "");
+ childSetTextArg("partner_edit", "[LAST]", "");
+
+ mPartnerID = LLUUID::null;
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(this,"groups");
+ if(group_list)
+ {
+ group_list->deleteAllItems();
+ }
+ LLScrollListCtrl* ratings_list = LLUICtrlFactory::getScrollListByName(this,"ratings");
+ if(ratings_list)
+ {
+ ratings_list->deleteAllItems();
+ }
+
+}
+
+
+//-----------------------------------------------------------------------------
+// enableControls()
+//-----------------------------------------------------------------------------
+void LLPanelAvatarSecondLife::enableControls(BOOL self)
+{
+ childSetEnabled("img", self);
+ childSetEnabled("about", self);
+ childSetVisible("allow_publish", self);
+ childSetEnabled("allow_publish", self);
+ childSetVisible("?", self);
+ childSetEnabled("?", self);
+
+ if (!self)
+ {
+ // This is because the LLTextEditor
+ // appears to reset the read only background color when
+ // setEnable is called, for some reason
+ LLTextEditor* about = LLUICtrlFactory::getTextEditorByName(this,"about");
+ if (about) about->setReadOnlyBgColor(CLEAR);
+ }
+}
+
+
+// static
+void LLPanelAvatarSecondLife::onClickImage(void *)
+{ }
+
+// static
+void LLPanelAvatarSecondLife::onDoubleClickGroup(void* data)
+{
+ LLPanelAvatarSecondLife* self = (LLPanelAvatarSecondLife*)data;
+
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(self,"groups");
+ if(group_list)
+ {
+ LLScrollListItem* item = group_list->getFirstSelected();
+ if(item && item->getUUID().notNull())
+ {
+ llinfos << "Show group info " << item->getUUID() << llendl;
+
+ LLFloaterGroupInfo::showFromUUID(item->getUUID());
+ }
+ }
+}
+
+// static
+void LLPanelAvatarSecondLife::onClickPublishHelp(void *)
+{
+ gViewerWindow->alertXml("ClickPublishHelpAvatar");
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarFirstLife()
+//-----------------------------------------------------------------------------
+LLPanelAvatarFirstLife::LLPanelAvatarFirstLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar )
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarFirstLife::~LLPanelAvatarFirstLife()
+{
+}
+
+
+void LLPanelAvatarFirstLife::enableControls(BOOL self)
+{
+ childSetEnabled("img", self);
+ childSetEnabled("about", self);
+}
+
+//-----------------------------------------------------------------------------
+// postBuild
+//-----------------------------------------------------------------------------
+
+BOOL LLPanelAvatarSecondLife::postBuild(void)
+{
+ childSetEnabled("born", FALSE);
+ childSetEnabled("partner_edit", FALSE);
+
+ childSetAction("?",onClickPublishHelp,this);
+ BOOL own_avatar = (mPanelAvatar->getAvatarID() == gAgent.getID() );
+ enableControls(own_avatar);
+
+ childSetVisible("About:",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("(500 chars)",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("about",LLPanelAvatar::sAllowFirstLife);
+
+ childSetVisible("allow_publish",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("?",LLPanelAvatar::sAllowFirstLife);
+
+ childSetVisible("online_unknown",TRUE);
+ childSetVisible("online_yes",FALSE);
+ childSetVisible("online_no",FALSE);
+
+ childSetAction("Show on Map", LLPanelAvatar::onClickTrack, mPanelAvatar);
+ childSetAction("Instant Message...", LLPanelAvatar::onClickIM, mPanelAvatar);
+ childSetAction("Rate...", LLPanelAvatar::onClickRate, mPanelAvatar);
+ childSetAction("Pay...", LLPanelAvatar::onClickPay, mPanelAvatar);
+ childSetAction("Mute", LLPanelAvatar::onClickMute, mPanelAvatar );
+
+ childSetAction("Offer Teleport...", LLPanelAvatar::onClickOfferTeleport, mPanelAvatar);
+
+ childSetDoubleClickCallback("groups", onDoubleClickGroup, this );
+
+ return TRUE;
+}
+BOOL LLPanelAvatarFirstLife::postBuild(void)
+{
+ BOOL own_avatar = (mPanelAvatar->getAvatarID() == gAgent.getID() );
+ enableControls(own_avatar);
+ return TRUE;
+}
+BOOL LLPanelAvatarNotes::postBuild(void)
+{ childSetCommitCallback("notes edit",onCommitNotes,this);
+
+ LLTextEditor* te = LLUICtrlFactory::getTextEditorByName(this,"notes edit");
+ if(te) te->setCommitOnFocusLost(TRUE);
+ return TRUE;
+}
+
+BOOL LLPanelAvatarWeb::postBuild(void)
+{
+ childSetAction("load",onClickLoad,this);
+ childSetAction("open",onClickOpen,this);
+ childSetAction("home",onClickLoad,this);
+ childSetAction("web_profile_help",onClickWebProfileHelp,this);
+
+ childSetCommitCallback("url_edit",onCommitURL,this);
+
+ childSetControlName("auto_load","AutoLoadWebProfiles");
+
+#if LL_LIBXUL_ENABLED
+ mWebBrowser = (LLWebBrowserCtrl*)getChildByName("profile_html");
+
+ // links open in internally
+ mWebBrowser->setOpenInExternalBrowser( false );
+
+ // observe browser events
+ LLMozLib::getInstance()->addObserver( mWebBrowser->getEmbeddedBrowserWindowId(), this );
+#endif // LL_LIBXUL_ENABLED
+
+ return TRUE;
+}
+
+BOOL LLPanelAvatarClassified::postBuild(void)
+{
+ childSetAction("New...",onClickNew,NULL);
+ childSetAction("Delete...",onClickDelete,NULL);
+ return TRUE;
+}
+BOOL LLPanelAvatarPicks::postBuild(void)
+{
+ childSetAction("New...",onClickNew,NULL);
+ childSetAction("Delete...",onClickDelete,NULL);
+ return TRUE;
+}
+
+BOOL LLPanelAvatarAdvanced::postBuild()
+{
+ for( S32 i = 0; i < kArraySize(mWantToCheck); i++ )
+ mWantToCheck[i] = NULL;
+ for( S32 i = 0; i < kArraySize(mSkillsCheck); i++ )
+ mSkillsCheck[i] = NULL;
+ mWantToCount = (8>kArraySize(mWantToCheck))?kArraySize(mWantToCheck):8;
+ for(int t=0;t < mWantToCount ;t++)
+ {
+ LLString ctlname = llformat("chk%d",t);
+ mWantToCheck[t] = LLUICtrlFactory::getCheckBoxByName(this,ctlname);
+ }
+ mSkillsCount = (6>kArraySize(mSkillsCheck))?kArraySize(mSkillsCheck):6;
+
+ for(int t=0;t<mSkillsCount;t++)
+ {
+ //Find the Skills checkboxes and save off thier controls
+ LLString ctlname = llformat("schk%d",t);
+ mSkillsCheck[t] = LLUICtrlFactory::getCheckBoxByName(this,ctlname);
+ }
+
+ mWantToEdit = LLUICtrlFactory::getLineEditorByName(this,"want_to_edit");
+ mSkillsEdit = LLUICtrlFactory::getLineEditorByName(this,"skills_edit");
+ childSetVisible("skills_edit",LLPanelAvatar::sAllowFirstLife);
+ childSetVisible("want_to_edit",LLPanelAvatar::sAllowFirstLife);
+
+ return TRUE;
+}
+
+LLPanelAvatarWeb::LLPanelAvatarWeb(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mWebBrowser(NULL)
+{
+}
+
+LLPanelAvatarWeb::~LLPanelAvatarWeb()
+{
+#if LL_LIBXUL_ENABLED
+ // stop observing browser events
+ LLMozLib::getInstance()->remObserver( mWebBrowser->getEmbeddedBrowserWindowId(), this );
+#endif
+}
+
+void LLPanelAvatarWeb::enableControls(BOOL self)
+{
+ childSetEnabled("url_edit",self);
+ childSetVisible("status_text",!self);
+}
+
+void LLPanelAvatarWeb::setWebURL(std::string url)
+{
+ bool changed_url = (mURL != url);
+
+ mURL = url;
+ bool have_url = !mURL.empty();
+
+ childSetText("url_edit",mURL);
+
+ childSetEnabled("load",have_url);
+ childSetEnabled("open",have_url);
+
+ childSetVisible("home",false);
+ childSetVisible("load",true);
+
+ if (have_url
+ && gSavedSettings.getBOOL("AutoLoadWebProfiles"))
+ {
+ if (changed_url)
+ {
+ load();
+ }
+ }
+ else
+ {
+ childSetVisible("profile_html",false);
+ }
+
+#if !LL_LIBXUL_ENABLED
+ childSetVisible("load",false);
+ childSetVisible("profile_html",false);
+ childSetVisible("status_text",false);
+#endif
+
+}
+
+// static
+void LLPanelAvatarWeb::onCommitURL(LLUICtrl* ctrl, void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ self->load( self->childGetText("url_edit") );
+}
+
+// static
+void LLPanelAvatarWeb::onClickWebProfileHelp(void *)
+{
+#if LL_LIBXUL_ENABLED
+ gViewerWindow->alertXml("ClickWebProfileHelpAvatar");
+#else
+ gViewerWindow->alertXml("ClickWebProfileNoWebHelpAvatar");
+#endif
+}
+
+void LLPanelAvatarWeb::load(std::string url)
+{
+ bool have_url = (!url.empty());
+
+#if LL_LIBXUL_ENABLED
+ if (have_url)
+ {
+ llinfos << "Loading " << url << llendl;
+ mWebBrowser->navigateTo( url );
+ }
+
+ // If we have_url then we loaded so use the home button
+ // Or if the url in address bar is not the home url show the home button.
+ bool use_home = (have_url
+ || url != mURL);
+
+ childSetVisible("profile_html",have_url);
+ childSetVisible("load",!use_home);
+ childSetVisible("home",use_home);
+
+ childSetEnabled("load",!use_home);
+ childSetEnabled("home",use_home);
+ childSetEnabled("open",have_url);
+
+#else
+ childSetEnabled("open",have_url);
+#endif
+}
+
+void LLPanelAvatarWeb::load()
+{
+ load(mURL);
+}
+
+// static
+void LLPanelAvatarWeb::onClickLoad(void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ self->load();
+}
+
+// static
+void LLPanelAvatarWeb::onClickOpen(void* data)
+{
+ LLPanelAvatarWeb* self = (LLPanelAvatarWeb*)data;
+
+ if (!self) return;
+
+ std::string url = self->childGetText("url_edit");
+ if (!url.empty())
+ {
+ LLWeb::loadURLExternal(url);
+ }
+}
+
+#if LL_LIBXUL_ENABLED
+void LLPanelAvatarWeb::onStatusTextChange( const EventType& eventIn )
+{
+ childSetText("status_text", eventIn.getStringValue() );
+}
+
+void LLPanelAvatarWeb::onLocationChange( const EventType& eventIn )
+{
+ childSetText("url_edit", eventIn.getStringValue() );
+}
+#endif
+
+LLPanelAvatarAdvanced::LLPanelAvatarAdvanced(
+ const std::string& name,
+ const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+ :
+ LLPanel(name, rect),
+ mPanelAvatar(panel_avatar),
+ mWantToCount(0),
+ mSkillsCount(0),
+ mWantToEdit( NULL ),
+ mSkillsEdit( NULL )
+{
+}
+
+LLPanelAvatarAdvanced::~LLPanelAvatarAdvanced()
+{
+}
+
+
+void LLPanelAvatarAdvanced::enableControls(BOOL self)
+{ int t;
+ for(t=0;t<mWantToCount;t++)
+ {
+ if(mWantToCheck[t])mWantToCheck[t]->setEnabled(self);
+ }
+ for(t=0;t<mSkillsCount;t++)
+ {
+ if(mSkillsCheck[t])mSkillsCheck[t]->setEnabled(self);
+ }
+
+ if (mWantToEdit) mWantToEdit->setEnabled(self);
+ if (mSkillsEdit) mSkillsEdit->setEnabled(self);
+ childSetEnabled("languages_edit",self);
+
+ if (!self)
+ {
+ // This is because the LLTextEditor
+ // appears to reset the read only background color when
+ // setEnable is called, for some reason
+ if (mWantToEdit) mWantToEdit->setReadOnlyBgColor(CLEAR);
+ if (mSkillsEdit) mSkillsEdit->setReadOnlyBgColor(CLEAR);
+ LLLineEditor* languages_edit = (LLLineEditor*)getChildByName("languages_edit");
+ languages_edit->setReadOnlyBgColor(CLEAR);
+ }
+}
+
+void LLPanelAvatarAdvanced::setWantSkills(U32 want_to_mask, const std::string& want_to_text,
+ U32 skills_mask, const std::string& skills_text,
+ const std::string& languages_text)
+{
+ for(int id =0;id<mWantToCount;id++)
+ {
+ mWantToCheck[id]->set( want_to_mask & 1<<id );
+ }
+ for(int id =0;id<mSkillsCount;id++)
+ {
+ mSkillsCheck[id]->set( skills_mask & 1<<id );
+ }
+ if (mWantToEdit && mSkillsEdit)
+ {
+ mWantToEdit->setText( want_to_text );
+ mSkillsEdit->setText( skills_text );
+ }
+
+ childSetText("languages_edit",languages_text);
+}
+
+void LLPanelAvatarAdvanced::getWantSkills(U32* want_to_mask, std::string& want_to_text,
+ U32* skills_mask, std::string& skills_text,
+ std::string& languages_text)
+{
+ if (want_to_mask)
+ {
+ *want_to_mask = 0;
+ for(int t=0;t<mWantToCount;t++)
+ {
+ if(mWantToCheck[t]->get())
+ *want_to_mask |= 1<<t;
+ }
+ }
+ if (skills_mask)
+ {
+ *skills_mask = 0;
+ for(int t=0;t<mSkillsCount;t++)
+ {
+ if(mSkillsCheck[t]->get())
+ *skills_mask |= 1<<t;
+ }
+ }
+ if (mWantToEdit)
+ {
+ want_to_text = mWantToEdit->getText();
+ }
+
+ if (mSkillsEdit)
+ {
+ skills_text = mSkillsEdit->getText();
+ }
+
+ languages_text = childGetText("languages_edit");
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarNotes()
+//-----------------------------------------------------------------------------
+LLPanelAvatarNotes::LLPanelAvatarNotes(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+LLPanelAvatarNotes::~LLPanelAvatarNotes()
+{
+}
+
+
+void LLPanelAvatarNotes::enableControls(BOOL self)
+{ }
+
+// static
+void LLPanelAvatarNotes::onCommitNotes(LLUICtrl*, void* userdata)
+{
+ LLPanelAvatarNotes* self = (LLPanelAvatarNotes*)userdata;
+
+ self->mPanelAvatar->sendAvatarNotesUpdate();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarClassified()
+//-----------------------------------------------------------------------------
+LLPanelAvatarClassified::LLPanelAvatarClassified(const LLString& name, const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarClassified::~LLPanelAvatarClassified()
+{
+ // children deleted by view destructor
+}
+
+
+void LLPanelAvatarClassified::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelAvatarClassified::refresh()
+{
+ BOOL self = (gAgent.getID() == mPanelAvatar->getAvatarID());
+
+
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"classified tab");
+
+ S32 tab_count = tabs ? tabs->getTabCount() : 0;
+
+ BOOL allow_new = TRUE; //tab_count < MAX_CLASSIFIEDS;
+ BOOL allow_delete = (tab_count > 0);
+ BOOL show_help = (tab_count == 0);
+
+ childSetEnabled("New...",self && allow_new);
+ childSetEnabled("Delete...",self && allow_delete);
+ childSetVisible("help_text",self && show_help);
+ childSetVisible("classified tab",!show_help);
+}
+
+
+void LLPanelAvatarClassified::enableControls(BOOL self)
+{
+}
+
+
+void LLPanelAvatarClassified::apply()
+{
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this, "classified tab");
+ for (S32 i = 0; i < tabs->getTabCount(); i++)
+ {
+ LLPanelClassified* panel = (LLPanelClassified*)tabs->getPanelByIndex(i);
+ panel->apply();
+ }
+}
+
+
+void LLPanelAvatarClassified::deleteClassifiedPanels()
+{
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"classified tab");
+ if (tabs)
+ {
+ tabs->deleteAllTabs();
+ }
+}
+
+
+void LLPanelAvatarClassified::processAvatarClassifiedReply(LLMessageSystem* msg, void**)
+{
+ S32 block = 0;
+ S32 block_count = 0;
+ LLUUID classified_id;
+ char classified_name[DB_PICK_NAME_SIZE];
+ LLPanelClassified* panel_classified = NULL;
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"classified tab");
+
+ // Clear out all the old panels.
+ // We'll replace them with the correct number of new panels.
+ deleteClassifiedPanels();
+
+ block_count = msg->getNumberOfBlocksFast(_PREHASH_Data);
+ for (block = 0; block < block_count; block++)
+ {
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ClassifiedID, classified_id, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, DB_PICK_NAME_SIZE, classified_name, block);
+
+ panel_classified = new LLPanelClassified(FALSE);
+
+ panel_classified->setClassifiedID(classified_id);
+
+ // This will request data from the server when the pick is first drawn.
+ panel_classified->markForServerRequest();
+
+ // The button should automatically truncate long names for us
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_classified, classified_name);
+ }
+ }
+
+ // Make sure somebody is highlighted. This works even if there
+ // are no tabs in the container.
+ if(tabs)
+ {
+ tabs->selectFirstTab();
+ }
+}
+
+
+// Create a new classified panel. It will automatically handle generating
+// its own id when it's time to save.
+// static
+void LLPanelAvatarClassified::onClickNew(void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ gViewerWindow->alertXml("AddClassified",callbackNew,self);
+
+}
+
+// static
+void LLPanelAvatarClassified::callbackNew(S32 option, void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ if (0 == option)
+ {
+ LLPanelClassified* panel_classified = new LLPanelClassified(FALSE);
+ panel_classified->initNewClassified();
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_classified, panel_classified->getClassifiedName());
+ tabs->selectLastTab();
+ }
+ }
+}
+
+
+// static
+void LLPanelAvatarClassified::onClickDelete(void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ LLPanelClassified* panel_classified = NULL;
+ if(tabs)
+ {
+ panel_classified = (LLPanelClassified*)tabs->getCurrentPanel();
+ }
+ if (!panel_classified) return;
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = panel_classified->getClassifiedName();
+ gViewerWindow->alertXml("DeleteClassified", args, callbackDelete, self);
+
+}
+
+
+// static
+void LLPanelAvatarClassified::callbackDelete(S32 option, void* data)
+{
+ LLPanelAvatarClassified* self = (LLPanelAvatarClassified*)data;
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(self,"classified tab");
+ LLPanelClassified* panel_classified=NULL;
+ if(tabs)
+ {
+ panel_classified = (LLPanelClassified*)tabs->getCurrentPanel();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ if (!panel_classified) return;
+
+ if (0 == option)
+ {
+ msg->newMessageFast(_PREHASH_ClassifiedDelete);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, panel_classified->getClassifiedID());
+ gAgent.sendReliableMessage();
+
+ if(tabs)
+ {
+ tabs->removeTabPanel(panel_classified);
+ }
+ delete panel_classified;
+ panel_classified = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatarPicks()
+//-----------------------------------------------------------------------------
+LLPanelAvatarPicks::LLPanelAvatarPicks(const std::string& name, const LLRect& rect,
+ LLPanelAvatar* panel_avatar)
+: LLPanel(name, rect),
+ mPanelAvatar(panel_avatar)
+{
+}
+
+
+LLPanelAvatarPicks::~LLPanelAvatarPicks()
+{
+ // children deleted by view destructor
+}
+
+
+void LLPanelAvatarPicks::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelAvatarPicks::refresh()
+{
+ BOOL self = (gAgent.getID() == mPanelAvatar->getAvatarID());
+
+ LLTabContainerCommon* tabs = LLViewerUICtrlFactory::getTabContainerByName(this,"picks tab");
+ S32 tab_count = tabs ? tabs->getTabCount() : 0;
+ BOOL allow_new = (tab_count < MAX_AVATAR_PICKS);
+ BOOL allow_delete = (tab_count > 0);
+
+ childSetEnabled("New...",self && allow_new);
+ childSetEnabled("Delete...",self && allow_delete);
+}
+
+
+// You are only allowed 10 picks.
+void LLPanelAvatarPicks::enableControls(BOOL self)
+{
+}
+
+
+void LLPanelAvatarPicks::deletePickPanels()
+{
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"picks tab");
+ if(tabs)
+ {
+ tabs->deleteAllTabs();
+ }
+}
+
+
+void LLPanelAvatarPicks::processAvatarPicksReply(LLMessageSystem* msg, void**)
+{
+ S32 block = 0;
+ S32 block_count = 0;
+ LLUUID pick_id;
+ char pick_name[DB_PICK_NAME_SIZE];
+ LLPanelPick* panel_pick = NULL;
+
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(this,"picks tab");
+
+ // Clear out all the old panels. We'll replace them with the correct
+ // number of new panels.
+ deletePickPanels();
+
+ block_count = msg->getNumberOfBlocks("Data");
+ for (block = 0; block < block_count; block++)
+ {
+ msg->getUUID("Data", "PickID", pick_id, block);
+ msg->getString("Data", "PickName", DB_PICK_NAME_SIZE, pick_name, block);
+
+ panel_pick = new LLPanelPick(FALSE);
+
+ panel_pick->setPickID(pick_id);
+
+ // This will request data from the server when the pick is first
+ // drawn.
+ panel_pick->markForServerRequest();
+
+ // The button should automatically truncate long names for us
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_pick, pick_name);
+ }
+ }
+
+ // Make sure somebody is highlighted. This works even if there
+ // are no tabs in the container.
+ if(tabs)
+ {
+ tabs->selectFirstTab();
+ }
+}
+
+
+// Create a new pick panel. It will automatically handle generating
+// its own id when it's time to save.
+// static
+void LLPanelAvatarPicks::onClickNew(void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLPanelPick* panel_pick = new LLPanelPick(FALSE);
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+
+ panel_pick->initNewPick();
+ if(tabs)
+ {
+ tabs->addTabPanel(panel_pick, panel_pick->getPickName());
+ tabs->selectLastTab();
+ }
+}
+
+
+// static
+void LLPanelAvatarPicks::onClickDelete(void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+ LLPanelPick* panel_pick = tabs?(LLPanelPick*)tabs->getCurrentPanel():NULL;
+
+ if (!panel_pick) return;
+
+ LLString::format_map_t args;
+ args["[PICK]"] = panel_pick->getPickName();
+
+ gViewerWindow->alertXml("DeleteAvatarPick", args,
+ callbackDelete,
+ self);
+}
+
+
+// static
+void LLPanelAvatarPicks::callbackDelete(S32 option, void* data)
+{
+ LLPanelAvatarPicks* self = (LLPanelAvatarPicks*)data;
+ LLTabContainerCommon* tabs = LLUICtrlFactory::getTabContainerByName(self,"picks tab");
+ LLPanelPick* panel_pick = tabs?(LLPanelPick*)tabs->getCurrentPanel():NULL;
+ LLMessageSystem* msg = gMessageSystem;
+
+ if (!panel_pick) return;
+
+ if (0 == option)
+ {
+ // If the viewer has a hacked god-mode, then this call will
+ // fail.
+ if(gAgent.isGodlike())
+ {
+ msg->newMessage("PickGodDelete");
+ }
+ else
+ {
+ msg->newMessage("PickDelete");
+ }
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", panel_pick->getPickID());
+
+ //God delete receiving end expects a query ID but we dont need it, so send a null.
+ //This is to resolve SL-24170 God Picks Delete results in crash.
+ if(gAgent.isGodlike())
+ msg->addUUID( "QueryID", LLUUID::null );
+
+
+ gAgent.sendReliableMessage();
+
+ if(tabs)
+ {
+ tabs->removeTabPanel(panel_pick);
+ }
+ delete panel_pick;
+ panel_pick = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPanelAvatar
+//-----------------------------------------------------------------------------
+LLPanelAvatar::LLPanelAvatar(
+ const std::string& name,
+ const LLRect &rect,
+ BOOL allow_edit)
+ :
+ LLPanel(name, rect, FALSE),
+ mPanelSecondLife(NULL),
+ mPanelAdvanced(NULL),
+ mPanelClassified(NULL),
+ mPanelPicks(NULL),
+ mPanelNotes(NULL),
+ mPanelFirstLife(NULL),
+ mPanelWeb(NULL),
+ mDropTarget(NULL),
+ mAvatarID( LLUUID::null ), // mAvatarID is set with 'setAvatar' or 'setAvatarID'
+ mHaveProperties(FALSE),
+ mHaveStatistics(FALSE),
+ mAllowEdit(allow_edit),
+ mDisableRate(FALSE)
+{
+
+ sAllPanels.addData(this);
+
+ LLCallbackMap::map_t factory_map;
+
+ factory_map["2nd Life"] = LLCallbackMap(createPanelAvatarSecondLife, this);
+ factory_map["WebProfile"] = LLCallbackMap(createPanelAvatarWeb, this);
+ factory_map["Interests"] = LLCallbackMap(createPanelAvatarInterests, this);
+ factory_map["Picks"] = LLCallbackMap(createPanelAvatarPicks, this);
+ factory_map["Classified"] = LLCallbackMap(createPanelAvatarClassified, this);
+ factory_map["1st Life"] = LLCallbackMap(createPanelAvatarFirstLife, this);
+ factory_map["My Notes"] = LLCallbackMap(createPanelAvatarNotes, this);
+
+ gUICtrlFactory->buildPanel(this, "panel_avatar.xml", &factory_map);
+
+ selectTab(0);
+
+
+}
+
+BOOL LLPanelAvatar::postBuild(void)
+{
+ mTab = LLUICtrlFactory::getTabContainerByName(this,"tab");
+ childSetAction("Kick",onClickKick,this);
+ childSetAction("Freeze",onClickFreeze, this);
+ childSetAction("Unfreeze", onClickUnfreeze, this);
+ childSetAction("csr_btn", onClickCSR, this);
+ childSetAction("OK", onClickOK, this);
+ childSetAction("Cancel", onClickCancel, this);
+
+ if(mTab && !sAllowFirstLife)
+ {
+ LLPanel* panel = mTab->getPanelByName("1st Life");
+ if (panel) mTab->removeTabPanel(panel);
+
+ panel = mTab->getPanelByName("WebProfile");
+ if (panel) mTab->removeTabPanel(panel);
+ }
+ childSetVisible("Kick",FALSE);
+ childSetEnabled("Kick",FALSE);
+ childSetVisible("Freeze",FALSE);
+ childSetEnabled("Freeze",FALSE);
+ childSetVisible("Unfreeze",FALSE);
+ childSetEnabled("Unfreeze",FALSE);
+ childSetVisible("csr_btn", FALSE);
+ childSetEnabled("csr_btn", FALSE);
+
+ return TRUE;
+}
+
+
+LLPanelAvatar::~LLPanelAvatar()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelAvatar::setAvatar(LLViewerObject *avatarp)
+{
+ // find the avatar and grab the name
+ LLNameValue *firstname = avatarp->getNVPair("FirstName");
+ LLNameValue *lastname = avatarp->getNVPair("LastName");
+
+ LLString name;
+ if (firstname && lastname)
+ {
+ name.assign( firstname->getString() );
+ name.append(" ");
+ name.append( lastname->getString() );
+ }
+ else
+ {
+ name.assign("");
+ }
+
+ // If we have an avatar pointer, they must be online.
+ setAvatarID(avatarp->getID(), name, ONLINE_STATUS_YES);
+}
+
+
+void LLPanelAvatar::setAvatarID(const LLUUID &avatar_id, const LLString &name,
+ EOnlineStatus online_status)
+{
+ if (avatar_id.isNull()) return;
+
+ BOOL avatar_changed = FALSE;
+ if (avatar_id != mAvatarID)
+ {
+ avatar_changed = TRUE;
+ }
+ mAvatarID = avatar_id;
+
+ // Determine if we have their calling card.
+ mIsFriend = is_agent_friend(mAvatarID);
+
+ if (ONLINE_STATUS_UNKNOWN == online_status)
+ {
+ // Determine if we know that they are online. If we can see them,
+ // we know they're online. Likewise, if we have a calling card,
+ // we know. Otherwise we don't.
+ LLViewerObject* object = gObjectList.findObject( mAvatarID );
+ if (object && !object->isDead())
+ {
+ online_status = ONLINE_STATUS_YES;
+ }
+ else if (mIsFriend)
+ {
+ if (LLAvatarTracker::instance().isBuddyOnline( mAvatarID ))
+ {
+ online_status = ONLINE_STATUS_YES;
+ }
+ else
+ {
+ online_status = ONLINE_STATUS_NO;
+ }
+ }
+ else
+ {
+ // Don't actually know if they are online.
+ }
+ }
+
+ mPanelSecondLife->childSetVisible("online_unknown",FALSE);
+ mPanelSecondLife->childSetVisible("online_yes",FALSE);
+ mPanelSecondLife->childSetVisible("online_no",FALSE);
+
+ switch(online_status)
+ {
+ case ONLINE_STATUS_YES:
+ mPanelSecondLife->childSetVisible("online_yes",TRUE);
+ break;
+ case ONLINE_STATUS_NO:
+ mPanelSecondLife->childSetVisible("online_no",TRUE);
+ break;
+ case ONLINE_STATUS_UNKNOWN:
+ default:
+ mPanelSecondLife->childSetVisible("online_unknown",TRUE);
+ break;
+ }
+
+ BOOL own_avatar = (mAvatarID == gAgent.getID() );
+
+ mPanelSecondLife->enableControls(own_avatar && mAllowEdit);
+ mPanelWeb->enableControls(own_avatar && mAllowEdit);
+ mPanelAdvanced->enableControls(own_avatar && mAllowEdit);
+ mPanelPicks->enableControls(own_avatar && mAllowEdit);
+ mPanelClassified->enableControls(own_avatar && mAllowEdit);
+ // Teens don't have this.
+ if (mPanelFirstLife) mPanelFirstLife->enableControls(own_avatar && mAllowEdit);
+ mPanelNotes->enableControls(own_avatar && mAllowEdit);
+
+
+ LLView *target_view = getChildByName("drop_target_rect", TRUE);
+ if(target_view)
+ {
+ if (mDropTarget)
+ {
+ delete mDropTarget;
+ }
+ mDropTarget = new LLDropTarget("drop target", target_view->getRect(), mAvatarID);
+ addChild(mDropTarget);
+ mDropTarget->setAgentID(mAvatarID);
+ }
+
+ LLNameEditor* NameEdit = LLViewerUICtrlFactory::getNameEditorByName(this, "name");
+ if(NameEdit)
+ {
+ if (name.empty())
+ {
+ NameEdit->setNameID(avatar_id, FALSE);
+ }
+ else
+ {
+ NameEdit->setText(name);
+ }
+ }
+// if (avatar_changed)
+ {
+ // While we're waiting for data off the network, clear out the
+ // old data.
+ mPanelSecondLife->clearControls();
+ mPanelPicks->deletePickPanels();
+ mPanelClassified->deleteClassifiedPanels();
+
+ sendAvatarPropertiesRequest();
+
+ if (own_avatar)
+ {
+ if (mAllowEdit)
+ {
+ // OK button disabled until properties data arrives
+ childSetVisible("OK",TRUE);
+ childSetEnabled("OK",TRUE);
+ childSetVisible("Cancel",TRUE);
+ childSetEnabled("Cancel",TRUE);
+ }
+ else
+ {
+ childSetVisible("OK",FALSE);
+ childSetEnabled("OK",FALSE);
+ }
+ childSetVisible("Instant Message...",FALSE);
+ childSetEnabled("Instant Message...",FALSE);
+ childSetVisible("Mute",FALSE);
+ childSetEnabled("Mute",FALSE);
+ childSetVisible("Offer Teleport...",FALSE);
+ childSetEnabled("Offer Teleport...",FALSE);
+ childSetVisible("drop target",FALSE);
+ childSetEnabled("drop target",FALSE);
+ childSetVisible("Show on Map",FALSE);
+ childSetEnabled("Show on Map",FALSE);
+ childSetVisible("Rate...",FALSE);
+ childSetEnabled("Rate...",FALSE);
+ childSetVisible("Pay...",FALSE);
+ childSetEnabled("Pay...",FALSE);
+ }
+ else
+ {
+ childSetVisible("OK",FALSE);
+ childSetEnabled("OK",FALSE);
+
+ childSetVisible("Cancel",FALSE);
+ childSetEnabled("Cancel",FALSE);
+
+ childSetVisible("Instant Message...",TRUE);
+ childSetEnabled("Instant Message...",FALSE);
+ childSetToolTip("Instant Message...",IM_ENABLED_TOOLTIP);
+ childSetVisible("Mute",TRUE);
+ childSetEnabled("Mute",FALSE);
+
+ childSetVisible("Offer Teleport...",TRUE);
+ BOOL in_prelude = gAgent.inPrelude();
+ if(gAgent.isGodlike())
+ {
+ childSetEnabled("Offer Teleport...", TRUE);
+ childSetToolTip("Offer Teleport...", childGetValue("TeleportGod").asString());
+ }
+ else if (in_prelude)
+ {
+ childSetEnabled("Offer Teleport...",FALSE);
+ childSetToolTip("Offer Teleport...",childGetValue("TeleportPrelude").asString());
+ }
+ else
+ {
+ // Even if user might be offline, allow a teleport offer.
+ BOOL maybe_online = (online_status != ONLINE_STATUS_NO);
+ childSetEnabled("Offer Teleport...", maybe_online);
+ childSetToolTip("Offer Teleport...", childGetValue("TeleportNormal").asString());
+ }
+ childSetVisible("drop target",TRUE);
+ childSetEnabled("drop target",FALSE);
+
+ childSetVisible("Show on Map",TRUE);
+ // Note: we don't always know online status, so always allow gods to try to track
+ BOOL enable_track = gAgent.isGodlike() || is_agent_mappable(mAvatarID);
+ childSetEnabled("Show on Map",enable_track);
+ if (!mIsFriend)
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapNonFriend").asString());
+ }
+ else if (ONLINE_STATUS_YES != online_status)
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOffline").asString());
+ }
+ else
+ {
+ childSetToolTip("Show on Map",childGetValue("ShowOnMapFriendOnline").asString());
+ }
+ childSetVisible("Rate...",TRUE);
+ childSetEnabled("Rate...",FALSE);
+ childSetVisible("Pay...",TRUE);
+ childSetEnabled("Pay...",FALSE);
+ }
+ }
+
+ BOOL is_god = FALSE;
+ if (gAgent.isGodlike()) is_god = TRUE;
+
+ childSetVisible("Kick", is_god);
+ childSetEnabled("Kick", is_god);
+ childSetVisible("Freeze", is_god);
+ childSetEnabled("Freeze", is_god);
+ childSetVisible("Unfreeze", is_god);
+ childSetEnabled("Unfreeze", is_god);
+ childSetVisible("csr_btn", is_god);
+ childSetEnabled("csr_btn", is_god);
+}
+
+
+void LLPanelAvatar::resetGroupList()
+{
+ // only get these updates asynchronously via the group floater, which works on the agent only
+ if (mAvatarID != gAgent.getID())
+ {
+ return;
+ }
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(mPanelSecondLife,"groups");
+
+ if (mPanelSecondLife && group_list)
+ {
+ group_list->deleteAllItems();
+ LLScrollListItem* item;
+
+ S32 count = gAgent.mGroups.count();
+ LLUUID id;
+
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLGroupData group_data = gAgent.mGroups.get(i);
+ id = group_data.mID;
+ std::string group_string;
+ /* Show group title? DUMMY_POWER for Don Grep
+ if(group_data.mOfficer)
+ {
+ group_string = "Officer of ";
+ }
+ else
+ {
+ group_string = "Member of ";
+ }
+ */
+
+ group_string += group_data.mName;
+ item = new LLScrollListItem(TRUE, NULL, id);
+ item->addColumn(group_string, LLFontGL::sSansSerifSmall, 0, LLFontGL::NORMAL);
+ group_list->addItem(item);
+ }
+ group_list->sortByColumn(0, TRUE);
+ }
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onClickIM()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickIM(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+ gIMView->setFloaterOpen(TRUE);
+
+ std::string name;
+ LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+ if (nameedit) name = nameedit->getText();
+ gIMView->addSession(name, IM_NOTHING_SPECIAL, self->mAvatarID);
+}
+
+
+// static
+//-----------------------------------------------------------------------------
+// onClickTrack()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickTrack(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if( gFloaterWorldMap )
+ {
+ std::string name;
+ LLNameEditor* nameedit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+ if (nameedit) name = nameedit->getText();
+ gFloaterWorldMap->trackAvatar(self->mAvatarID, name);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+}
+
+// static
+//-----------------------------------------------------------------------------
+// onClickRate()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickRate(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ LLFloaterRate::show(self->mAvatarID);
+}
+
+//-----------------------------------------------------------------------------
+// onClickMute()
+//-----------------------------------------------------------------------------
+void LLPanelAvatar::onClickMute(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ LLUUID agent_id = self->getAvatarID();
+ LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self->mPanelSecondLife, "name");
+
+ if (name_edit)
+ {
+ std::string agent_name = name_edit->getText();
+ gFloaterMute->show();
+
+ if (gMuteListp->isMuted(agent_id))
+ {
+ gFloaterMute->selectMute(agent_id);
+ }
+ else
+ {
+ LLMute mute(agent_id, agent_name, LLMute::AGENT);
+ gMuteListp->add(mute);
+ }
+ }
+}
+
+
+void LLPanelAvatar::disableRate()
+{
+ // Force off the rate button, but enable IM.
+ // Note that these buttons may not exist if it is your own profile.
+ childSetEnabled("Rate...",FALSE);
+ mDisableRate = TRUE;
+}
+
+
+// static
+void LLPanelAvatar::onClickOfferTeleport(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ handle_lure(self->mAvatarID);
+}
+
+
+// static
+void LLPanelAvatar::onClickPay(void *userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+ handle_pay_by_id(self->mAvatarID);
+}
+
+
+// static
+void LLPanelAvatar::onClickOK(void *userdata)
+{
+ LLPanelAvatar *self = (LLPanelAvatar *)userdata;
+
+ // JC: Only save the data if we actually got the original
+ // properties. Otherwise we might save blanks into
+ // the database.
+ if (self
+ && self->mHaveProperties)
+ {
+ self->sendAvatarPropertiesUpdate();
+
+ self->mPanelClassified->apply();
+
+ LLFloaterAvatarInfo *infop = LLFloaterAvatarInfo::getInstance(self->mAvatarID);
+ if (infop)
+ {
+ infop->close();
+ }
+ }
+}
+
+// static
+void LLPanelAvatar::onClickCancel(void *userdata)
+{
+ LLPanelAvatar *self = (LLPanelAvatar *)userdata;
+
+ if (self)
+ {
+ self->sendAvatarPropertiesRequest();
+ LLFloaterAvatarInfo *infop;
+ if ((infop = LLFloaterAvatarInfo::getInstance(self->mAvatarID)))
+ {
+ infop->close();
+ }
+ }
+}
+
+
+void LLPanelAvatar::sendAvatarPropertiesRequest()
+{
+ lldebugs << "LLPanelAvatar::sendAvatarPropertiesRequest()" << llendl;
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AvatarPropertiesRequest);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast( _PREHASH_AvatarID, mAvatarID);
+ gAgent.sendReliableMessage();
+}
+
+void LLPanelAvatar::sendAvatarNotesUpdate()
+{
+ std::string notes = mPanelNotes->childGetValue("notes edit").asString();
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessage("AvatarNotesUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", mAvatarID);
+ msg->addString("Notes", notes);
+
+ gAgent.sendReliableMessage();
+}
+
+
+// static
+void LLPanelAvatar::processAvatarPropertiesReply(LLMessageSystem *msg, void**)
+{
+ LLPanelAvatar* self = NULL;
+
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+ LLUUID image_id;
+ LLUUID fl_image_id;
+ LLUUID partner_id;
+ char about_text[DB_USER_ABOUT_BUF_SIZE];
+ char fl_about_text[DB_USER_FL_ABOUT_BUF_SIZE];
+ char born_on[DB_BORN_BUF_SIZE];
+ S32 charter_member_size = 0;
+ BOOL allow_publish = FALSE;
+ //BOOL mature = FALSE;
+ BOOL identified = FALSE;
+ BOOL transacted = FALSE;
+ char profile_url[DB_USER_PROFILE_URL_BUF_SIZE];
+
+ //llinfos << "properties packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+ self->childSetEnabled("Instant Message...",TRUE);
+ self->childSetEnabled("Pay...",TRUE);
+ self->childSetEnabled("Mute",TRUE);
+
+ if (!self->mDisableRate)
+ {
+ self->childSetEnabled("Rate...",TRUE);
+ }
+ lldebugs << "!!!!!!!!!!!!!!!!!!!!!!Enabling drop target" << llendl;
+ self->childSetEnabled("drop target",TRUE);
+
+ self->mHaveProperties = TRUE;
+ self->enableOKIfReady();
+
+ msg->getUUIDFast( _PREHASH_PropertiesData, _PREHASH_ImageID, image_id );
+ msg->getUUIDFast( _PREHASH_PropertiesData, _PREHASH_FLImageID, fl_image_id );
+ msg->getUUIDFast(_PREHASH_PropertiesData, _PREHASH_PartnerID, partner_id);
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_AboutText, DB_USER_ABOUT_BUF_SIZE, about_text );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_FLAboutText, DB_USER_FL_ABOUT_BUF_SIZE, fl_about_text );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_BornOn, DB_BORN_BUF_SIZE, born_on);
+ msg->getBOOLFast(_PREHASH_PropertiesData, _PREHASH_Identified, identified);
+ msg->getBOOLFast(_PREHASH_PropertiesData, _PREHASH_Transacted, transacted);
+ msg->getString("PropertiesData","ProfileURL", DB_USER_PROFILE_URL_BUF_SIZE, profile_url);
+
+ self->mPanelWeb->setWebURL(std::string(profile_url));
+ U8 caption_index = 0;
+ LLString caption_text;
+ charter_member_size = msg->getSize("PropertiesData", "CharterMember");
+ if(1 == charter_member_size)
+ {
+ msg->getBinaryData("PropertiesData", "CharterMember", &caption_index, 1);
+ }
+ else if(1 < charter_member_size)
+ {
+ char caption[MAX_STRING];
+ msg->getString("PropertiesData", "CharterMember", MAX_STRING, caption);
+ caption_text = caption;
+ }
+
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(self->mPanelSecondLife,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(image_id);
+ }
+ self->childSetValue("about", about_text);
+
+ if(caption_text.empty())
+ {
+ LLString::format_map_t args;
+ caption_text = self->mPanelSecondLife->childGetValue("CaptionTextAcctInfo").asString();
+
+ const char* ACCT_TYPE[] = {
+ "AcctTypeResident",
+ "AcctTypeTrial",
+ "AcctTypeCharterMember",
+ "AcctTypeEmployee"
+ };
+ caption_index = llclamp(caption_index, (U8)0, (U8)(sizeof(ACCT_TYPE)/sizeof(ACCT_TYPE[0])-1));
+ args["[ACCTTYPE]"] = self->mPanelSecondLife->childGetValue(ACCT_TYPE[caption_index]).asString();
+
+ LLString payment_text = " ";
+ const S32 DEFAULT_CAPTION_LINDEN_INDEX = 3;
+ if(caption_index != DEFAULT_CAPTION_LINDEN_INDEX)
+ {
+ if(transacted)
+ {
+ payment_text = "PaymentInfoUsed";
+ }
+ else if (identified)
+ {
+ payment_text = "PaymentInfoOnFile";
+ }
+ else
+ {
+ payment_text = "NoPaymentInfoOnFile";
+ }
+ args["[PAYMENTINFO]"] = self->mPanelSecondLife->childGetValue(payment_text).asString();
+ }
+ else
+ {
+ args["[PAYMENTINFO]"] = " ";
+ }
+ LLString::format(caption_text, args);
+ }
+
+ self->mPanelSecondLife->childSetValue("acct", caption_text);
+ self->mPanelSecondLife->childSetValue("born", born_on);
+
+ self->mPanelSecondLife->setPartnerID(partner_id);
+ self->mPanelSecondLife->updatePartnerName();
+
+ if (self->mPanelFirstLife)
+ {
+ // Teens don't get these
+ self->mPanelFirstLife->childSetValue("about", fl_about_text);
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(self->mPanelFirstLife,"img");
+ if(image_ctrl)
+ {
+ image_ctrl->setImageAssetID(fl_image_id);
+ }
+ msg->getBOOL("PropertiesData", "AllowPublish", allow_publish);
+
+ self->mPanelSecondLife->childSetValue("allow_publish", allow_publish);
+
+ }
+ }
+}
+
+// static
+void LLPanelAvatar::processAvatarInterestsReply(LLMessageSystem *msg, void**)
+{
+ LLPanelAvatar* self = NULL;
+
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+
+ U32 want_to_mask;
+ char want_to_text[DB_USER_WANT_TO_BUF_SIZE];
+ U32 skills_mask;
+ char skills_text[DB_USER_SKILLS_BUF_SIZE];
+ char languages_text[DB_USER_SKILLS_BUF_SIZE];
+
+ //llinfos << "properties packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_WantToMask, want_to_mask );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_WantToText, DB_USER_WANT_TO_BUF_SIZE, want_to_text );
+ msg->getU32Fast( _PREHASH_PropertiesData, _PREHASH_SkillsMask, skills_mask );
+ msg->getStringFast(_PREHASH_PropertiesData, _PREHASH_SkillsText, DB_USER_SKILLS_BUF_SIZE, skills_text );
+ msg->getString(_PREHASH_PropertiesData, "LanguagesText", DB_USER_SKILLS_BUF_SIZE, languages_text );
+
+ self->mPanelAdvanced->setWantSkills(want_to_mask, want_to_text, skills_mask, skills_text, languages_text);
+ }
+}
+
+// Separate function because the groups list can be very long, almost
+// filling a packet. JC
+// static
+void LLPanelAvatar::processAvatarGroupsReply(LLMessageSystem *msg, void**)
+{
+ LLUUID agent_id; // your id
+ LLUUID avatar_id; // target of this panel
+ U64 group_powers;
+ char group_title[DB_GROUP_TITLE_BUF_SIZE];
+ LLUUID group_id;
+ char group_name[DB_GROUP_NAME_BUF_SIZE];
+ LLUUID group_insignia_id;
+ const LLFontGL* FONT = LLFontGL::sSansSerifSmall;
+
+ llinfos << "groups packet size " << msg->getReceiveSize() << llendl;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AvatarID, avatar_id );
+
+ for (LLPanelAvatar* self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ LLScrollListCtrl* group_list = LLUICtrlFactory::getScrollListByName(self->mPanelSecondLife,"groups");
+// if(group_list)
+// {
+// group_list->deleteAllItems();
+// }
+
+ S32 group_count = msg->getNumberOfBlocksFast(_PREHASH_GroupData);
+ if (0 == group_count)
+ {
+ if(group_list) group_list->addSimpleItem("None");
+ }
+ else
+ {
+ for(S32 i = 0; i < group_count; ++i)
+ {
+ msg->getU64( _PREHASH_GroupData, "GroupPowers", group_powers, i );
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupTitle, DB_GROUP_TITLE_BUF_SIZE, group_title, i );
+ msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupID, group_id, i);
+ msg->getStringFast(_PREHASH_GroupData, _PREHASH_GroupName, DB_GROUP_NAME_BUF_SIZE, group_name, i );
+ msg->getUUIDFast( _PREHASH_GroupData, _PREHASH_GroupInsigniaID, group_insignia_id, i );
+
+ LLString group_string;
+ if (group_id.notNull())
+ {
+ group_string.assign("Member of ");
+ group_string.append(group_name);
+ }
+ else
+ {
+ group_string.assign("");
+ }
+
+ // Is this really necessary? Remove existing entry if it exists.
+ // TODO: clear the whole list when a request for data is made
+ if (group_list)
+ {
+ S32 index = group_list->getItemIndex(group_id);
+ if ( index >= 0 )
+ {
+ group_list->deleteSingleItem(index);
+ }
+ }
+ LLScrollListItem *group_item = new LLScrollListItem(TRUE, NULL, group_id);
+ group_item->addColumn(group_string, FONT);
+ if(group_list) group_list->addItem(group_item);
+ }
+ }
+ if(group_list) group_list->sortByColumn(0, TRUE);
+ }
+}
+
+// Don't enable the OK button until you actually have the data.
+// Otherwise you will write blanks back into the database.
+void LLPanelAvatar::enableOKIfReady()
+{
+ if(mHaveProperties && mHaveStatistics && childIsVisible("OK"))
+ {
+ childSetEnabled("OK", TRUE);
+ }
+ else
+ {
+ childSetEnabled("OK", FALSE);
+ }
+}
+
+void LLPanelAvatar::sendAvatarPropertiesUpdate()
+{
+ llinfos << "Sending avatarinfo update" << llendl;
+ BOOL allow_publish = FALSE;
+ BOOL mature = FALSE;
+ if (LLPanelAvatar::sAllowFirstLife)
+ {
+ allow_publish = childGetValue("allow_publish");
+ //A profile should never be mature.
+ mature = FALSE;
+ }
+ U32 want_to_mask = 0x0;
+ U32 skills_mask = 0x0;
+ std::string want_to_text;
+ std::string skills_text;
+ std::string languages_text;
+ mPanelAdvanced->getWantSkills(&want_to_mask, want_to_text, &skills_mask, skills_text, languages_text);
+
+ LLUUID first_life_image_id;
+ LLString first_life_about_text;
+ if (mPanelFirstLife)
+ {
+ first_life_about_text = mPanelFirstLife->childGetValue("about").asString();
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(mPanelFirstLife,"img");
+ if(image_ctrl)
+ {
+ first_life_image_id = image_ctrl->getImageAssetID();
+ }
+ }
+
+ LLString about_text = mPanelSecondLife->childGetValue("about").asString();
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AvatarPropertiesUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_PropertiesData);
+
+ LLTextureCtrl* image_ctrl = LLUICtrlFactory::getTexturePickerByName(mPanelSecondLife,"img");
+ if(image_ctrl)
+ {
+ msg->addUUIDFast( _PREHASH_ImageID, image_ctrl->getImageAssetID());
+ }
+ else
+ {
+ msg->addUUIDFast( _PREHASH_ImageID, LLUUID::null);
+ }
+// msg->addUUIDFast( _PREHASH_ImageID, mPanelSecondLife->mimage_ctrl->getImageAssetID() );
+ msg->addUUIDFast( _PREHASH_FLImageID, first_life_image_id);
+ msg->addStringFast( _PREHASH_AboutText, about_text);
+ msg->addStringFast( _PREHASH_FLAboutText, first_life_about_text);
+
+ msg->addBOOL("AllowPublish", allow_publish);
+ msg->addBOOL("MaturePublish", mature);
+ msg->addString("ProfileURL", mPanelWeb->childGetText("url_edit"));
+ gAgent.sendReliableMessage();
+
+ msg->newMessage("AvatarInterestsUpdate");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast( _PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_PropertiesData);
+ msg->addU32Fast( _PREHASH_WantToMask, want_to_mask);
+ msg->addStringFast( _PREHASH_WantToText, want_to_text);
+ msg->addU32Fast( _PREHASH_SkillsMask, skills_mask);
+ msg->addStringFast( _PREHASH_SkillsText, skills_text);
+ msg->addString( "LanguagesText", languages_text);
+ gAgent.sendReliableMessage();
+}
+
+void LLPanelAvatar::selectTab(S32 tabnum)
+{
+ if(mTab)
+ {
+ mTab->selectTab(tabnum);
+ }
+}
+
+void LLPanelAvatar::selectTabByName(std::string tab_name)
+{
+ if (mTab)
+ {
+ if (tab_name.empty())
+ {
+ mTab->selectFirstTab();
+ }
+ else
+ {
+ mTab->selectTabByName(tab_name);
+ }
+ }
+}
+
+
+// static
+void LLPanelAvatar::processAvatarStatisticsReply(LLMessageSystem *msg, void**)
+{
+ // extract the agent id
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+
+ LLUUID avatar_id;
+ msg->getUUIDFast(_PREHASH_AvatarData, _PREHASH_AvatarID, avatar_id);
+
+ // look up all panels which have this avatar
+ LLPanelAvatar *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != avatar_id)
+ {
+ continue;
+ }
+
+ self->mHaveStatistics = TRUE;
+ self->enableOKIfReady();
+
+ // clear out list
+ LLScrollListCtrl* ratings_list = LLUICtrlFactory::getScrollListByName(self->mPanelSecondLife,"ratings");
+ if (ratings_list)
+ {
+ ratings_list->deleteAllItems();
+ }
+ // build the item list
+ LLFontGL *font = LLFontGL::sSansSerifSmall;
+
+ S32 items = msg->getNumberOfBlocksFast(_PREHASH_StatisticsData);
+ for (S32 i = 0; i < items; i++)
+ {
+ char name[MAX_STRING];
+ S32 positive;
+ S32 negative;
+ char value_string[MAX_STRING];
+
+ msg->getStringFast( _PREHASH_StatisticsData,
+ _PREHASH_Name, MAX_STRING, name, i);
+ msg->getS32( "StatisticsData", "Positive", positive, i);
+ msg->getS32( "StatisticsData", "Negative", negative, i);
+
+ LLScrollListItem *item = NULL;
+
+ const S32 TEXT_WIDTH = 75;
+
+ item = new LLScrollListItem();
+ item->addColumn( name, font, TEXT_WIDTH );
+
+ sprintf( value_string, "+%d", positive);
+ item->addColumn( value_string, font, 50 );
+
+ item->addColumn("", font); // extra column to force striped appearance
+ if(ratings_list) ratings_list->addItem( item );
+ }
+ }
+}
+
+
+void LLPanelAvatar::processAvatarNotesReply(LLMessageSystem *msg, void**)
+{
+ // extract the agent id
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+
+ LLUUID target_id;
+ msg->getUUID("Data", "TargetID", target_id);
+
+ // look up all panels which have this avatar
+ LLPanelAvatar *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ char text[DB_USER_NOTE_SIZE];
+ msg->getString("Data", "Notes", DB_USER_NOTE_SIZE, text);
+ self->childSetValue("notes edit", text);
+ }
+}
+
+
+void LLPanelAvatar::processAvatarClassifiedReply(LLMessageSystem *msg, void** userdata)
+{
+ LLPanelAvatar *self = NULL;
+ LLUUID agent_id;
+ LLUUID target_id;
+
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_TargetID, target_id);
+
+ // look up all panels which have this avatar target
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ self->mPanelClassified->processAvatarClassifiedReply(msg, userdata);
+ }
+}
+
+void LLPanelAvatar::processAvatarPicksReply(LLMessageSystem *msg, void** userdata)
+{
+ LLPanelAvatar *self = NULL;
+ LLUUID agent_id;
+ LLUUID target_id;
+
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ msg->getUUID("AgentData", "TargetID", target_id);
+
+ // look up all panels which have this avatar target
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mAvatarID != target_id)
+ {
+ continue;
+ }
+
+ self->mPanelPicks->processAvatarPicksReply(msg, userdata);
+ }
+}
+
+// static
+void LLPanelAvatar::onClickKick(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect(left, top, left+400, top-300);
+
+ gViewerWindow->alertXmlEditText("KickUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishKick, self);
+}
+
+// static
+void LLPanelAvatar::finishKick(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_DEFAULT );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickFreeze(void* userdata)
+{
+ gViewerWindow->alertXmlEditText("FreezeUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishFreeze, userdata);
+}
+
+// static
+void LLPanelAvatar::finishFreeze(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_FREEZE );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickUnfreeze(void* userdata)
+{
+ gViewerWindow->alertXmlEditText("UnFreezeUser", LLString::format_map_t(),
+ NULL, NULL,
+ LLPanelAvatar::finishUnfreeze, userdata);
+}
+
+// static
+void LLPanelAvatar::finishUnfreeze(S32 option, const LLString& text, void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*) userdata;
+
+ if (option == 0)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_GodKickUser);
+ msg->nextBlockFast(_PREHASH_UserInfo);
+ msg->addUUIDFast(_PREHASH_GodID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_GodSessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_AgentID, self->mAvatarID );
+ msg->addU32("KickFlags", KICK_FLAGS_UNFREEZE );
+ msg->addStringFast(_PREHASH_Reason, text );
+ gAgent.sendReliableMessage();
+ }
+}
+
+// static
+void LLPanelAvatar::onClickCSR(void* userdata)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)userdata;
+ if (!self) return;
+
+ LLNameEditor* name_edit = LLViewerUICtrlFactory::getNameEditorByName(self, "name");
+ if (!name_edit) return;
+
+ LLString name = name_edit->getText();
+ if (name.empty()) return;
+
+ LLString url = "http://csr.lindenlab.com/agent/";
+
+ // slow and stupid, but it's late
+ S32 len = name.length();
+ for (S32 i = 0; i < len; i++)
+ {
+ if (name[i] == ' ')
+ {
+ url += "%20";
+ }
+ else
+ {
+ url += name[i];
+ }
+ }
+
+ LLWeb::loadURL(url);
+}
+
+
+void* LLPanelAvatar::createPanelAvatarSecondLife(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelSecondLife = new LLPanelAvatarSecondLife("2nd Life",LLRect(),self);
+ return self->mPanelSecondLife;
+}
+
+void* LLPanelAvatar::createPanelAvatarWeb(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelWeb = new LLPanelAvatarWeb("Web",LLRect(),self);
+ return self->mPanelWeb;
+}
+
+void* LLPanelAvatar::createPanelAvatarInterests(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelAdvanced = new LLPanelAvatarAdvanced("Interests",LLRect(),self);
+ return self->mPanelAdvanced;
+}
+
+
+void* LLPanelAvatar::createPanelAvatarPicks(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelPicks = new LLPanelAvatarPicks("Picks",LLRect(),self);
+ return self->mPanelPicks;
+}
+
+void* LLPanelAvatar::createPanelAvatarClassified(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelClassified = new LLPanelAvatarClassified("Classified",LLRect(),self);
+ return self->mPanelClassified;
+}
+
+void* LLPanelAvatar::createPanelAvatarFirstLife(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelFirstLife = new LLPanelAvatarFirstLife("1st Life", LLRect(), self);
+ return self->mPanelFirstLife;
+}
+
+void* LLPanelAvatar::createPanelAvatarNotes(void* data)
+{
+ LLPanelAvatar* self = (LLPanelAvatar*)data;
+ self->mPanelNotes = new LLPanelAvatarNotes("My Notes",LLRect(),self);
+ return self->mPanelNotes;
+}
diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h
new file mode 100644
index 0000000000..d6b0d235b0
--- /dev/null
+++ b/indra/newview/llpanelavatar.h
@@ -0,0 +1,335 @@
+/**
+ * @file llpanelavatar.h
+ * @brief LLPanelAvatar and related class definitions
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELAVATAR_H
+#define LL_LLPANELAVATAR_H
+
+#include "llpanel.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "linked_lists.h"
+#include "llmozlib.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLDropTarget;
+class LLInventoryItem;
+class LLLineEditor;
+class LLNameEditor;
+class LLPanelAvatar;
+class LLScrollListCtrl;
+class LLTabContainerCommon;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLViewerImage;
+class LLViewerObject;
+class LLMessageSystem;
+class LLIconCtrl;
+class LLWebBrowserCtrl;
+
+enum EOnlineStatus
+{
+ ONLINE_STATUS_UNKNOWN = -1,
+ ONLINE_STATUS_NO = 0,
+ ONLINE_STATUS_YES = 1
+};
+
+class LLPanelAvatarFirstLife
+: public LLPanel
+{
+public:
+ LLPanelAvatarFirstLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarFirstLife();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatarSecondLife
+: public LLPanel
+{
+public:
+ LLPanelAvatarSecondLife(const std::string& name, const LLRect &rect, LLPanelAvatar* panel_avatar );
+ /*virtual*/ ~LLPanelAvatarSecondLife();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ static void onClickImage( void *userdata);
+ static void onClickFriends( void *userdata);
+ static void onDoubleClickGroup(void* userdata);
+ static void onClickPublishHelp(void *userdata);
+
+ // Clear out the controls anticipating new network data.
+ void clearControls();
+ void enableControls(BOOL own_avatar);
+ void updateOnlineText(BOOL online, BOOL have_calling_card);
+ void updatePartnerName();
+
+ void setPartnerID(LLUUID id) { mPartnerID = id; }
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+
+ LLUUID mPartnerID;
+};
+
+// WARNING! The order of the inheritance here matters!! Do not change. - KLW
+class LLPanelAvatarWeb :
+ public LLPanel
+#if LL_LIBXUL_ENABLED
+ , public LLEmbeddedBrowserWindowObserver
+#endif
+{
+public:
+ LLPanelAvatarWeb(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarWeb();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+ void setWebURL(std::string url);
+
+ void load();
+ void load(std::string url);
+ static void onClickLoad(void* data);
+ static void onClickOpen(void* data);
+ static void onCommitURL(LLUICtrl* ctrl, void* data);
+ static void onClickWebProfileHelp(void *);
+
+#if LL_LIBXUL_ENABLED
+ // browser observer impls
+ virtual void onStatusTextChange( const EventType& eventIn );
+ virtual void onLocationChange( const EventType& eventIn );
+#endif
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+ std::string mURL;
+ LLWebBrowserCtrl* mWebBrowser;
+};
+
+class LLPanelAvatarAdvanced : public LLPanel
+{
+public:
+ LLPanelAvatarAdvanced(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarAdvanced();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+ void setWantSkills(U32 want_to_mask, const std::string& want_to_text,
+ U32 skills_mask, const std::string& skills_text,
+ const std::string& languages_text);
+ void getWantSkills(U32* want_to_mask, std::string& want_to_text,
+ U32* skills_mask, std::string& skills_text,
+ std::string& languages_text);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+ S32 mWantToCount;
+ S32 mSkillsCount;
+ LLCheckBoxCtrl *mWantToCheck[8];
+ LLLineEditor *mWantToEdit;
+ LLCheckBoxCtrl *mSkillsCheck[8];
+ LLLineEditor *mSkillsEdit;
+};
+
+
+class LLPanelAvatarNotes : public LLPanel
+{
+public:
+ LLPanelAvatarNotes(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarNotes();
+ /*virtual*/ BOOL postBuild(void);
+
+ void enableControls(BOOL own_avatar);
+
+ static void onCommitNotes(LLUICtrl* field, void* userdata);
+
+protected:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatarClassified : public LLPanel
+{
+public:
+ LLPanelAvatarClassified(const LLString& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarClassified();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void apply();
+ void enableControls(BOOL own_avatar);
+
+ // Delete all the classified sub-panels from the tab container
+ void deleteClassifiedPanels();
+
+ // Unpack the outline of classified for this avatar (count, names, but not
+ // actual data).
+ void processAvatarClassifiedReply(LLMessageSystem* msg, void**);
+
+private:
+ static void onClickNew(void* data);
+ static void onClickDelete(void* data);
+
+ static void callbackDelete(S32 option, void* data);
+ static void callbackNew(S32 option, void* data);
+
+private:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+class LLPanelAvatarPicks : public LLPanel
+{
+public:
+ LLPanelAvatarPicks(const std::string& name, const LLRect& rect, LLPanelAvatar* panel_avatar);
+ /*virtual*/ ~LLPanelAvatarPicks();
+
+ /*virtual*/ BOOL postBuild(void);
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void enableControls(BOOL own_avatar);
+
+ // Delete all the pick sub-panels from the tab container
+ void deletePickPanels();
+
+ // Unpack the outline of picks for this avatar (count, names, but not
+ // actual data).
+ void processAvatarPicksReply(LLMessageSystem* msg, void**);
+ void processAvatarClassifiedReply(LLMessageSystem* msg, void**);
+
+private:
+ static void onClickNew(void* data);
+ static void onClickDelete(void* data);
+
+ static void callbackDelete(S32 option, void* data);
+
+private:
+ LLPanelAvatar* mPanelAvatar;
+};
+
+
+class LLPanelAvatar : public LLPanel
+{
+public:
+ LLPanelAvatar(const std::string& name, const LLRect &rect, BOOL allow_edit);
+ /*virtual*/ ~LLPanelAvatar();
+
+ /*virtual*/ BOOL postBuild(void);
+
+ void setAvatar(LLViewerObject *avatarp);
+
+ // Fill in the avatar ID and handle some field fill-in, as well as
+ // button enablement.
+ // Pass one of the ONLINE_STATUS_foo constants above.
+ void setAvatarID(const LLUUID &avatar_id, const LLString &name, EOnlineStatus online_status);
+
+ const LLUUID& getAvatarID() const { return mAvatarID; }
+
+ void disableRate();
+
+ void resetGroupList();
+
+ void sendAvatarStatisticsRequest();
+
+ void sendAvatarPropertiesRequest();
+ void sendAvatarPropertiesUpdate();
+
+ void sendAvatarNotesRequest();
+ void sendAvatarNotesUpdate();
+
+ void sendAvatarPicksRequest();
+
+ void selectTab(S32 tabnum);
+ void selectTabByName(std::string tab_name);
+
+ BOOL haveData() { return mHaveProperties && mHaveStatistics; }
+
+ static void processAvatarPropertiesReply(LLMessageSystem *msg, void **);
+ static void processAvatarInterestsReply(LLMessageSystem *msg, void **);
+ static void processAvatarGroupsReply(LLMessageSystem* msg, void**);
+ static void processAvatarStatisticsReply(LLMessageSystem *msg, void **);
+ static void processAvatarNotesReply(LLMessageSystem *msg, void **);
+ static void processAvatarPicksReply(LLMessageSystem *msg, void **);
+ static void processAvatarClassifiedReply(LLMessageSystem *msg, void **);
+
+ static void onClickTrack( void *userdata);
+ static void onClickIM( void *userdata);
+ static void onClickOfferTeleport( void *userdata);
+ static void onClickPay( void *userdata);
+ static void onClickRate( void *userdata);
+ static void onClickOK( void *userdata);
+ static void onClickCancel( void *userdata);
+ static void onClickKick( void *userdata);
+ static void onClickFreeze( void *userdata);
+ static void onClickUnfreeze(void *userdata);
+ static void onClickCSR( void *userdata);
+ static void onClickMute( void *userdata);
+ static void onClickAddFriend(void* data);
+
+ static void finishKick(S32 option, const LLString& text, void* userdata);
+ static void finishFreeze(S32 option, const LLString& text, void* userdata);
+ static void finishUnfreeze(S32 option, const LLString& text, void* userdata);
+
+ static void showProfileCallback(S32 option, void *userdata);
+
+ // Teen users are not allowed to see or enter data into the first life page,
+ // or their own about/interests text entry fields.
+ static BOOL sAllowFirstLife;
+
+ static void* createPanelAvatar(void* data);
+ static void* createFloaterAvatarInfo(void* data);
+ static void* createPanelAvatarSecondLife(void* data);
+ static void* createPanelAvatarWeb(void* data);
+ static void* createPanelAvatarInterests(void* data);
+ static void* createPanelAvatarPicks(void* data);
+ static void* createPanelAvatarClassified(void* data);
+ static void* createPanelAvatarFirstLife(void* data);
+ static void* createPanelAvatarNotes(void* data);
+
+public:
+ LLPanelAvatarSecondLife* mPanelSecondLife;
+ LLPanelAvatarAdvanced* mPanelAdvanced;
+ LLPanelAvatarClassified* mPanelClassified;
+ LLPanelAvatarPicks* mPanelPicks;
+ LLPanelAvatarNotes* mPanelNotes;
+ LLPanelAvatarFirstLife* mPanelFirstLife;
+ LLPanelAvatarWeb* mPanelWeb;
+
+ LLDropTarget* mDropTarget;
+
+protected:
+ void enableOKIfReady();
+ LLUUID mAvatarID; // for which avatar is this window?
+ BOOL mIsFriend; // Are we friends?
+ BOOL mHaveProperties;
+ BOOL mHaveStatistics;
+ LLTabContainerCommon* mTab;
+ BOOL mAllowEdit;
+ BOOL mDisableRate;
+
+ static LLLinkedList<LLPanelAvatar> sAllPanels;
+};
+
+// helper funcs
+void add_left_label(LLPanel *panel, const LLString& name, S32 y);
+
+
+#endif // LL_LLPANELAVATAR_H
diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp
new file mode 100644
index 0000000000..7c84d0f6a5
--- /dev/null
+++ b/indra/newview/llpanelclassified.cpp
@@ -0,0 +1,852 @@
+/**
+ * @file llpanelclassified.cpp
+ * @brief LLPanelClassified class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a classified used both for the global view in the
+// Find directory, and also for each individual user's classified in their
+// profile.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelclassified.h"
+
+#include "lldir.h"
+#include "lldispatcher.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llclassifiedflags.h"
+#include "llviewercontrol.h"
+#include "lllineeditor.h"
+#include "llfloateravatarinfo.h"
+#include "lltabcontainervertical.h"
+#include "lltextbox.h"
+#include "llcombobox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llworldmap.h"
+#include "llfloaterworldmap.h"
+#include "llviewermessage.h" // send_generic_message
+#include "llviewerwindow.h" // for window width, height
+
+const S32 MINIMUM_PRICE_FOR_LISTING = 50; // L$
+
+// "classifiedclickthrough"
+// strings[0] = classified_id
+// strings[1] = teleport_clicks
+// strings[2] = map_clicks
+// strings[3] = profile_clicks
+class LLDispatchClassifiedClickThrough : public LLDispatchHandler
+{
+public:
+ virtual bool operator()(
+ const LLDispatcher* dispatcher,
+ const std::string& key,
+ const LLUUID& invoice,
+ const sparam_t& strings)
+ {
+ if (strings.size() != 4) return false;
+ LLUUID classified_id(strings[0]);
+ S32 teleport_clicks = atoi(strings[1].c_str());
+ S32 map_clicks = atoi(strings[2].c_str());
+ S32 profile_clicks = atoi(strings[3].c_str());
+ LLPanelClassified::setClickThrough(classified_id, teleport_clicks,
+ map_clicks,
+ profile_clicks);
+ return true;
+ }
+};
+
+static LLDispatchClassifiedClickThrough sClassifiedClickThrough;
+
+//static
+LLLinkedList<LLPanelClassified> LLPanelClassified::sAllPanels;
+
+LLPanelClassified::LLPanelClassified(BOOL in_finder)
+: LLPanel("Classified Panel"),
+ mInFinder(in_finder),
+ mClassifiedID(),
+ mCreatorID(),
+ mPriceForListing(0),
+ mDataRequested(FALSE),
+ mEnableCommit(FALSE),
+ mPaidFor(FALSE),
+ mParcelID(),
+ mPosGlobal(),
+ mSnapshotCtrl(NULL),
+ mNameEditor(NULL),
+ mLocationEditor(NULL),
+ mDescEditor(NULL),
+ mTeleportBtn(NULL),
+ mMapBtn(NULL),
+ //mLandmarkBtn(NULL),
+ //mEnabledCheck(NULL),
+ mMatureCheck(NULL),
+ mAutoRenewCheck(NULL),
+ mSetBtn(NULL),
+ mClickThroughText(NULL)
+{
+ sAllPanels.addData(this);
+
+ std::string classified_def_file;
+ if (mInFinder)
+ {
+ gUICtrlFactory->buildPanel(this, "panel_classified.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildPanel(this, "panel_avatar_classified.xml");
+ }
+
+ // Register dispatcher
+ gGenericDispatcher.addHandler("classifiedclickthrough",
+ &sClassifiedClickThrough);
+}
+
+
+LLPanelClassified::~LLPanelClassified()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelClassified::reset()
+{
+ mClassifiedID.setNull();
+ mCreatorID.setNull();
+ mParcelID.setNull();
+
+ // Don't request data, this isn't valid
+ mDataRequested = TRUE;
+
+ mEnableCommit = FALSE;
+ mPaidFor = FALSE;
+
+ mPosGlobal.clearVec();
+
+ clearCtrls();
+}
+
+
+BOOL LLPanelClassified::postBuild()
+{
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback(onCommitAny);
+ mSnapshotCtrl->setCallbackUserData(this);
+ mSnapshotSize = mSnapshotCtrl->getRect();
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "given_name_editor");
+ mNameEditor->setMaxTextLength(DB_PARCEL_NAME_LEN);
+ mNameEditor->setCommitOnFocusLost(TRUE);
+ mNameEditor->setFocusReceivedCallback(onFocusReceived);
+ mNameEditor->setCommitCallback(onCommitAny);
+ mNameEditor->setCallbackUserData(this);
+ mNameEditor->setPrevalidate( LLLineEditor::prevalidateASCII );
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+ mDescEditor->setCommitOnFocusLost(TRUE);
+ mDescEditor->setFocusReceivedCallback(onFocusReceived);
+ mDescEditor->setCommitCallback(onCommitAny);
+ mDescEditor->setCallbackUserData(this);
+ mDescEditor->setTabToNextField(TRUE);
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mSetBtn = LLViewerUICtrlFactory::getButtonByName(this, "set_location_btn");
+ mSetBtn->setClickedCallback(onClickSet);
+ mSetBtn->setCallbackUserData(this);
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ if(mInFinder)
+ {
+ mProfileBtn = LLViewerUICtrlFactory::getButtonByName(this, "classified_profile_btn");
+ mProfileBtn->setClickedCallback(onClickProfile);
+ mProfileBtn->setCallbackUserData(this);
+ }
+
+ mCategoryCombo = LLViewerUICtrlFactory::getComboBoxByName(this, "classified_category_combo");
+ LLClassifiedInfo::cat_map::iterator iter;
+ for (iter = LLClassifiedInfo::sCategories.begin();
+ iter != LLClassifiedInfo::sCategories.end();
+ iter++)
+ {
+ mCategoryCombo->add(iter->second, (void *)((intptr_t)iter->first), ADD_BOTTOM);
+ }
+ mCategoryCombo->setCurrentByIndex(0);
+ mCategoryCombo->setCommitCallback(onCommitAny);
+ mCategoryCombo->setCallbackUserData(this);
+
+ mMatureCheck = LLViewerUICtrlFactory::getCheckBoxByName(this, "classified_mature_check");
+ mMatureCheck->setCommitCallback(onCommitAny);
+ mMatureCheck->setCallbackUserData(this);
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ // Teens don't get to set mature flag. JC
+ mMatureCheck->setVisible(FALSE);
+ }
+
+ if (!mInFinder)
+ {
+ mAutoRenewCheck = LLUICtrlFactory::getCheckBoxByName(this, "auto_renew_check");
+ mAutoRenewCheck->setCommitCallback(onCommitAny);
+ mAutoRenewCheck->setCallbackUserData(this);
+ }
+
+ mUpdateBtn = LLUICtrlFactory::getButtonByName(this, "classified_update_btn");
+ mUpdateBtn->setClickedCallback(onClickUpdate);
+ mUpdateBtn->setCallbackUserData(this);
+ mEnableCommit = TRUE;
+
+ if (!mInFinder)
+ {
+ mClickThroughText = LLUICtrlFactory::getTextBoxByName(this, "click_through_text");
+ }
+
+ return TRUE;
+}
+
+
+void LLPanelClassified::apply()
+{
+ // Apply is used for automatically saving results, so only
+ // do that if there is a difference, and this is a save not create.
+ if (mEnableCommit && mPaidFor)
+ {
+ sendClassifiedInfoUpdate();
+ }
+}
+
+// Fill in some reasonable defaults for a new classified.
+void LLPanelClassified::initNewClassified()
+{
+ // TODO: Don't generate this on the client.
+ mClassifiedID.generate();
+
+ mCreatorID = gAgent.getID();
+
+ mPosGlobal = gAgent.getPositionGlobal();
+
+ mPaidFor = FALSE;
+
+ // Try to fill in the current parcel
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ mNameEditor->setText(parcel->getName());
+ //mDescEditor->setText(parcel->getDesc());
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ //mPriceEditor->setText("0");
+ mCategoryCombo->setCurrentByIndex(0);
+ }
+
+ // default new classifieds to publish
+ //mEnabledCheck->set(TRUE);
+
+ // delay commit until user hits save
+ // sendClassifiedInfoUpdate();
+
+ mUpdateBtn->setLabelSelected("Publish...");
+ mUpdateBtn->setLabelUnselected("Publish...");
+}
+
+
+void LLPanelClassified::setClassifiedID(const LLUUID& id)
+{
+ mClassifiedID = id;
+}
+
+
+//static
+void LLPanelClassified::setClickThrough(const LLUUID& classified_id,
+ S32 teleport,
+ S32 map,
+ S32 profile)
+{
+ LLPanelClassified *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mClassifiedID != classified_id)
+ {
+ continue;
+ }
+
+ if (self->mClickThroughText)
+ {
+ std::string msg = llformat("Clicks: %d teleport, %d map, %d profile",
+ teleport,
+ map,
+ profile);
+ self->mClickThroughText->setText(msg);
+ }
+ }
+}
+
+// Schedules the panel to request data
+// from the server next time it is drawn.
+void LLPanelClassified::markForServerRequest()
+{
+ mDataRequested = FALSE;
+}
+
+
+std::string LLPanelClassified::getClassifiedName()
+{
+ return mNameEditor->getText();
+}
+
+
+void LLPanelClassified::sendClassifiedInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ if (mClassifiedID != mRequestedID)
+ {
+ msg->newMessageFast(_PREHASH_ClassifiedInfoRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
+ gAgent.sendReliableMessage();
+
+ mDataRequested = TRUE;
+ mRequestedID = mClassifiedID;
+ }
+}
+
+
+void LLPanelClassified::sendClassifiedInfoUpdate()
+{
+ // If we don't have a classified id yet, we'll need to generate one,
+ // otherwise we'll keep overwriting classified_id 00000 in the database.
+ if (mClassifiedID.isNull())
+ {
+ // TODO: Don't do this on the client.
+ mClassifiedID.generate();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ClassifiedInfoUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_ClassifiedID, mClassifiedID);
+ // TODO: fix this
+ U32 category = mCategoryCombo->getCurrentIndex() + 1;
+ msg->addU32Fast(_PREHASH_Category, category);
+ msg->addStringFast(_PREHASH_Name, mNameEditor->getText());
+ msg->addStringFast(_PREHASH_Desc, mDescEditor->getText());
+
+ // fills in on simulator if null
+ msg->addUUIDFast(_PREHASH_ParcelID, mParcelID);
+ // fills in on simulator if null
+ msg->addU32Fast(_PREHASH_ParentEstate, 0);
+ msg->addUUIDFast(_PREHASH_SnapshotID, mSnapshotCtrl->getImageAssetID());
+ msg->addVector3dFast(_PREHASH_PosGlobal, mPosGlobal);
+ BOOL mature = mMatureCheck->get();
+ // Classifieds are always enabled/published 11/2005 JC
+ //BOOL enabled = mEnabledCheck->get();
+ BOOL auto_renew = FALSE;
+ if (mAutoRenewCheck)
+ {
+ auto_renew = mAutoRenewCheck->get();
+ }
+ U8 flags = pack_classified_flags(mature, auto_renew);
+ msg->addU8Fast(_PREHASH_ClassifiedFlags, flags);
+ msg->addS32("PriceForListing", mPriceForListing);
+ gAgent.sendReliableMessage();
+}
+
+
+//static
+void LLPanelClassified::processClassifiedInfoReply(LLMessageSystem *msg, void **)
+{
+ lldebugs << "processClassifiedInfoReply()" << llendl;
+ // Extract the agent id and verify the message is for this
+ // client.
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Agent ID mismatch in processClassifiedInfoReply"
+ << llendl;
+ return;
+ }
+
+ LLUUID classified_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ClassifiedID, classified_id);
+
+ LLUUID creator_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_CreatorID, creator_id);
+
+ LLUUID parcel_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ParcelID, parcel_id);
+
+ char name[DB_PARCEL_NAME_SIZE];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, DB_PARCEL_NAME_SIZE, name);
+
+ char desc[DB_PICK_DESC_SIZE];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Desc, DB_PICK_DESC_SIZE, desc);
+
+ LLUUID snapshot_id;
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_SnapshotID, snapshot_id);
+
+ // "Location text" is actually the original
+ // name that owner gave the parcel, and the location.
+ char buffer[256];
+ LLString location_text;
+
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ParcelName, 256, buffer);
+ if (buffer[0] != '\0')
+ {
+ location_text.assign(buffer);
+ location_text.append(", ");
+ }
+ else
+ {
+ location_text.assign("");
+ }
+
+ char sim_name[256];
+ msg->getStringFast(_PREHASH_Data, _PREHASH_SimName, 256, sim_name);
+
+ LLVector3d pos_global;
+ msg->getVector3dFast(_PREHASH_Data, _PREHASH_PosGlobal, pos_global);
+
+ S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)pos_global.mdV[VZ]);
+
+ sprintf(buffer, "%s (%d, %d, %d)", sim_name, region_x, region_y, region_z);
+ location_text.append(buffer);
+
+ U8 flags;
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_ClassifiedFlags, flags);
+ //BOOL enabled = is_cf_enabled(flags);
+ bool mature = is_cf_mature(flags);
+ bool auto_renew = is_cf_auto_renew(flags);
+
+ U32 date = 0;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_CreationDate, date);
+ time_t tim = date;
+ tm *now=localtime(&tim);
+
+ // future use
+ U32 expiration_date = 0;
+ msg->getU32("Data", "ExpirationDate", expiration_date);
+
+ U32 category = 0;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_Category, category);
+
+ S32 price_for_listing = 0;
+ msg->getS32("Data", "PriceForListing", price_for_listing);
+
+ // Look up the panel to fill in
+ LLPanelClassified *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mClassifiedID != classified_id)
+ {
+ continue;
+ }
+
+ // Found the panel, now fill in the information
+ self->mClassifiedID = classified_id;
+ self->mCreatorID = creator_id;
+ self->mParcelID = parcel_id;
+ self->mPriceForListing = price_for_listing;
+ self->mSimName.assign(sim_name);
+ self->mPosGlobal = pos_global;
+
+ // Update UI controls
+ self->mNameEditor->setText(name);
+ self->mDescEditor->setText(desc);
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+ self->mLocationEditor->setText(location_text);
+
+ self->mCategoryCombo->setCurrentByIndex(category - 1);
+ self->mMatureCheck->set(mature);
+ if (self->mAutoRenewCheck)
+ {
+ self->mAutoRenewCheck->set(auto_renew);
+ }
+
+ LLString datestr = llformat("%02d/%02d/%d", now->tm_mon+1, now->tm_mday, now->tm_year+1900);
+ self->childSetTextArg("ad_placed_paid", "[DATE]", datestr);
+ self->childSetTextArg("ad_placed_paid", "[AMT]", llformat("%d", price_for_listing));
+ self->childSetText("classified_info_text", self->childGetValue("ad_placed_paid").asString());
+
+ // If we got data from the database, we know the listing is paid for.
+ self->mPaidFor = TRUE;
+
+ self->mUpdateBtn->setLabelSelected("Update");
+ self->mUpdateBtn->setLabelUnselected("Update");
+ }
+}
+
+void LLPanelClassified::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelClassified::refresh()
+{
+ if (!mDataRequested)
+ {
+ sendClassifiedInfoRequest();
+ }
+
+ // Check for god mode
+ BOOL godlike = gAgent.isGodlike();
+ BOOL is_self = (gAgent.getID() == mCreatorID);
+
+ // Set button visibility/enablement appropriately
+ if (mInFinder)
+ {
+
+ // End user doesn't ned to see price twice, or date posted.
+
+ mSnapshotCtrl->setEnabled(godlike);
+ if(godlike)
+ {
+ //make it smaller, so text is more legible
+ mSnapshotCtrl->setRect(LLRect(20, 375, 320, 175));
+
+ }
+ else
+ {
+ mSnapshotCtrl->setRect(mSnapshotSize);
+ //normal
+ }
+ mNameEditor->setEnabled(godlike);
+ mDescEditor->setEnabled(godlike);
+ mCategoryCombo->setEnabled(godlike);
+ mCategoryCombo->setVisible(godlike);
+
+ //mEnabledCheck->setVisible(godlike);
+ //mEnabledCheck->setEnabled(godlike);
+
+ mMatureCheck->setEnabled(godlike);
+ mMatureCheck->setVisible(godlike);
+
+ // Jesse (who is the only one who uses this, as far as we can tell
+ // Says that he does not want a set location button - he has used it
+ // accidently in the past.
+ mSetBtn->setVisible(FALSE);
+ mSetBtn->setEnabled(FALSE);
+
+ mUpdateBtn->setEnabled(godlike);
+ mUpdateBtn->setVisible(godlike);
+ }
+ else
+ {
+ mSnapshotCtrl->setEnabled(is_self);
+ mNameEditor->setEnabled(is_self);
+ mDescEditor->setEnabled(is_self);
+ //mPriceEditor->setEnabled(is_self);
+ mCategoryCombo->setEnabled(is_self);
+
+ //mEnabledCheck->setVisible(FALSE);
+ //mEnabledCheck->setEnabled(is_self);
+ mMatureCheck->setEnabled(is_self);
+
+ mAutoRenewCheck->setEnabled(is_self);
+ mAutoRenewCheck->setVisible(is_self);
+
+ mClickThroughText->setEnabled(is_self);
+ mClickThroughText->setVisible(is_self);
+
+ mSetBtn->setVisible(is_self);
+ mSetBtn->setEnabled(is_self);
+
+ mUpdateBtn->setEnabled(is_self && mEnableCommit);
+ mUpdateBtn->setVisible(is_self);
+ }
+}
+
+// static
+void LLPanelClassified::onClickUpdate(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ if(self == NULL) return;
+
+ // Disallow leading spaces, punctuation, etc. that screw up
+ // sort order.
+ const LLString& name = self->mNameEditor->getText();
+ if (name.empty())
+ {
+ gViewerWindow->alertXml("BlankClassifiedName");
+ return;
+ }
+ if (!isalnum(name[0]))
+ {
+ gViewerWindow->alertXml("ClassifiedMustBeAlphanumeric");
+ return;
+ }
+
+ // if already paid for, just do the update
+ if (self->mPaidFor)
+ {
+ callbackConfirmPublish(0, self);
+ }
+ else
+ {
+ // Ask the user how much they want to pay
+ LLFloaterPriceForListing::show( callbackGotPriceForListing, self );
+ }
+}
+
+// static
+void LLPanelClassified::callbackGotPriceForListing(S32 option, LLString text, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Only do something if user hits publish
+ if (option != 0) return;
+
+ S32 price_for_listing = strtol(text.c_str(), NULL, 10);
+ if (price_for_listing < MINIMUM_PRICE_FOR_LISTING)
+ {
+ LLString::format_map_t args;
+ LLString price_text = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
+ args["[MIN_PRICE]"] = price_text;
+
+ gViewerWindow->alertXml("MinClassifiedPrice", args);
+ return;
+ }
+
+ // price is acceptable, put it in the dialog for later read by
+ // update send
+ self->mPriceForListing = price_for_listing;
+
+ LLString::format_map_t args;
+ args["[AMOUNT]"] = llformat("%d", price_for_listing);
+ gViewerWindow->alertXml("PublishClassified", args, &callbackConfirmPublish, self);
+
+}
+
+// static
+void LLPanelClassified::callbackConfirmPublish(S32 option, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Option 0 = publish
+ if (option != 0) return;
+
+ self->sendClassifiedInfoUpdate();
+
+ // Big hack - assume that top picks are always in a browser,
+ // and non-finder-classifieds are always in a tab container.
+ if (self->mInFinder)
+ {
+ // TODO: enable this
+ //LLPanelDirClassifieds* panel = (LLPanelDirClassifieds*)self->getParent();
+ //panel->renameClassified(self->mClassifiedID, self->mNameEditor->getText().c_str());
+ }
+ else
+ {
+ LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+ tab->setCurrentTabName(self->mNameEditor->getText());
+ }
+ self->mEnableCommit = FALSE;
+}
+
+// static
+void LLPanelClassified::onClickTeleport(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+
+ self->sendClassifiedClickMessage("teleport");
+ }
+}
+
+
+// static
+void LLPanelClassified::onClickMap(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+
+ self->sendClassifiedClickMessage("map");
+}
+
+// static
+void LLPanelClassified::onClickProfile(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ LLFloaterAvatarInfo::showFromDirectory(self->mCreatorID);
+ self->sendClassifiedClickMessage("profile");
+}
+
+// static
+/*
+void LLPanelClassified::onClickLandmark(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ create_landmark(self->mNameEditor->getText().c_str(), "", self->mPosGlobal);
+}
+*/
+
+// static
+void LLPanelClassified::onClickSet(void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+
+ // Save location for later.
+ self->mPosGlobal = gAgent.getPositionGlobal();
+
+ LLString location_text;
+ location_text.assign("(will update after save)");
+ location_text.append(", ");
+
+ S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]);
+
+ location_text.append(self->mSimName);
+ location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
+
+ self->mLocationEditor->setText(location_text);
+
+ // Set this to null so it updates on the next save.
+ self->mParcelID.setNull();
+
+ onCommitAny(NULL, data);
+}
+
+
+// static
+void LLPanelClassified::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelClassified* self = (LLPanelClassified*)data;
+ if (self)
+ {
+ self->mEnableCommit = TRUE;
+ }
+}
+
+// static
+void LLPanelClassified::onFocusReceived(LLUICtrl* ctrl, void* data)
+{
+ // first, allow the data to be saved
+ onCommitAny(ctrl, data);
+}
+
+
+void LLPanelClassified::sendClassifiedClickMessage(const char* type)
+{
+ // You're allowed to click on your own ads to reassure yourself
+ // that the system is working.
+ std::vector<std::string> strings;
+ strings.push_back(mClassifiedID.getString());
+ strings.push_back(type);
+ LLUUID no_invoice;
+ send_generic_message("classifiedclick", strings, no_invoice);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+
+LLFloaterPriceForListing::LLFloaterPriceForListing()
+: LLFloater("PriceForListing"),
+ mCallback(NULL),
+ mUserData(NULL)
+{ }
+
+//virtual
+LLFloaterPriceForListing::~LLFloaterPriceForListing()
+{ }
+
+//virtual
+BOOL LLFloaterPriceForListing::postBuild()
+{
+ LLLineEditor* edit = LLUICtrlFactory::getLineEditorByName(this, "price_edit");
+ if (edit)
+ {
+ edit->setPrevalidate(LLLineEditor::prevalidateNonNegativeS32);
+ LLString min_price = llformat("%d", MINIMUM_PRICE_FOR_LISTING);
+ edit->setText(min_price);
+ edit->selectAll();
+ edit->setFocus(TRUE);
+ }
+
+ childSetAction("set_price_btn", onClickSetPrice, this);
+
+ childSetAction("cancel_btn", onClickCancel, this);
+
+ setDefaultBtn("set_price_btn");
+ return TRUE;
+}
+
+//static
+void LLFloaterPriceForListing::show( void (*callback)(S32, LLString, void*), void* userdata)
+{
+ LLFloaterPriceForListing *self = new LLFloaterPriceForListing();
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_price_for_listing.xml");
+ self->center();
+
+ self->mCallback = callback;
+ self->mUserData = userdata;
+}
+
+//static
+void LLFloaterPriceForListing::onClickSetPrice(void* data)
+{
+ buttonCore(0, data);
+}
+
+//static
+void LLFloaterPriceForListing::onClickCancel(void* data)
+{
+ buttonCore(1, data);
+}
+
+//static
+void LLFloaterPriceForListing::buttonCore(S32 button, void* data)
+{
+ LLFloaterPriceForListing* self = (LLFloaterPriceForListing*)data;
+
+ if (self->mCallback)
+ {
+ LLString text = self->childGetText("price_edit");
+ self->mCallback(button, text, self->mUserData);
+ self->close();
+ }
+}
diff --git a/indra/newview/llpanelclassified.h b/indra/newview/llpanelclassified.h
new file mode 100644
index 0000000000..e3e7a299bf
--- /dev/null
+++ b/indra/newview/llpanelclassified.h
@@ -0,0 +1,148 @@
+/**
+ * @file llpanelclassified.h
+ * @brief LLPanelClassified class definition
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a classified used both for the global view in the
+// Find directory, and also for each individual user's classified in their
+// profile.
+
+#ifndef LL_LLPANELCLASSIFIED_H
+#define LL_LLPANELCLASSIFIED_H
+
+#include "llpanel.h"
+#include "llclassifiedinfo.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+#include "llfloater.h"
+//#include "llrect.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLIconCtrl;
+class LLLineEditor;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLMessageSystem;
+
+class LLPanelClassified : public LLPanel
+{
+public:
+ LLPanelClassified(BOOL in_finder);
+ /*virtual*/ ~LLPanelClassified();
+
+ void reset();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ void apply();
+
+ // Setup a new classified, including creating an id, giving a sane
+ // initial position, etc.
+ void initNewClassified();
+
+ void setClassifiedID(const LLUUID& id);
+ static void setClickThrough(const LLUUID& classified_id,
+ S32 teleport, S32 map, S32 profile);
+
+ // Schedules the panel to request data
+ // from the server next time it is drawn.
+ void markForServerRequest();
+
+ std::string getClassifiedName();
+ const LLUUID& getClassifiedID() const { return mClassifiedID; }
+
+ void sendClassifiedInfoRequest();
+ void sendClassifiedInfoUpdate();
+
+ static void processClassifiedInfoReply(LLMessageSystem* msg, void**);
+
+ static void callbackGotPriceForListing(S32 option, LLString text, void* data);
+ static void callbackConfirmPublish(S32 option, void* data);
+
+protected:
+ static void onClickUpdate(void* data);
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ static void onClickProfile(void* data);
+ static void onClickSet(void* data);
+
+ static void onFocusReceived(LLUICtrl* ctrl, void* data);
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+
+ void sendClassifiedClickMessage(const char* type);
+
+protected:
+ BOOL mInFinder;
+ LLUUID mClassifiedID;
+ LLUUID mRequestedID;
+ LLUUID mCreatorID;
+ LLUUID mParcelID;
+ S32 mPriceForListing;
+
+ // Data will be requested on first draw
+ BOOL mDataRequested;
+ BOOL mEnableCommit;
+
+ // For avatar panel classifieds only, has the user been charged
+ // yet for this classified? That is, have they saved once?
+ BOOL mPaidFor;
+
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+
+ LLTextureCtrl* mSnapshotCtrl;
+ LLLineEditor* mNameEditor;
+ LLLineEditor* mDateEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mLocationEditor;
+ LLComboBox* mCategoryCombo;
+
+ LLButton* mUpdateBtn;
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+ LLButton* mProfileBtn;
+
+ LLTextBox* mInfoText;
+ LLCheckBoxCtrl* mMatureCheck;
+ LLCheckBoxCtrl* mAutoRenewCheck;
+ LLButton* mSetBtn;
+ LLTextBox* mClickThroughText;
+
+ LLRect mSnapshotSize;
+ static LLLinkedList<LLPanelClassified> sAllPanels;
+};
+
+
+class LLFloaterPriceForListing
+: public LLFloater
+{
+public:
+ LLFloaterPriceForListing();
+ virtual ~LLFloaterPriceForListing();
+ virtual BOOL postBuild();
+
+ static void show( void (*callback)(S32 option, LLString value, void* userdata), void* userdata );
+
+private:
+ static void onClickSetPrice(void*);
+ static void onClickCancel(void*);
+ static void buttonCore(S32 button, void* data);
+
+private:
+ void (*mCallback)(S32 option, LLString, void*);
+ void* mUserData;
+};
+
+
+#endif // LL_LLPANELCLASSIFIED_H
diff --git a/indra/newview/llpanelcontents.cpp b/indra/newview/llpanelcontents.cpp
new file mode 100644
index 0000000000..ad3c3d42b0
--- /dev/null
+++ b/indra/newview/llpanelcontents.cpp
@@ -0,0 +1,189 @@
+/**
+ * @file llpanelcontents.cpp
+ * @brief Object contents panel in the tools floater.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelcontents.h"
+
+// linden library includes
+#include "llerror.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llmaterialtable.h"
+#include "llfontgl.h"
+#include "m3math.h"
+#include "llpermissionsflags.h"
+#include "lleconomy.h"
+#include "material_codes.h"
+
+// project includes
+#include "llui.h"
+#include "llspinctrl.h"
+#include "llcheckboxctrl.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llresmgr.h"
+#include "lltexturetable.h"
+#include "llselectmgr.h"
+#include "llpreviewscript.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "lltoolcomp.h"
+#include "llpanelinventory.h"
+#include "viewer.h"
+
+//
+// Imported globals
+//
+
+
+//
+// Globals
+//
+
+BOOL LLPanelContents::postBuild()
+{
+ LLRect rect = this->getRect();
+
+ setMouseOpaque(FALSE);
+
+ childSetAction("button new script",&LLPanelContents::onClickNewScript, this);
+
+ return TRUE;
+}
+
+LLPanelContents::LLPanelContents(const std::string& name)
+ : LLPanel(name),
+ mPanelInventory(NULL)
+{
+}
+
+
+LLPanelContents::~LLPanelContents()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+void LLPanelContents::getState(LLViewerObject *objectp )
+{
+ if( !objectp )
+ {
+ childSetEnabled("button new script",FALSE);
+ //mBtnNewScript->setEnabled( FALSE );
+ return;
+ }
+
+ // BUG? Check for all objects being editable?
+ BOOL editable = gAgent.isGodlike()
+ || (objectp->permModify() && objectp->permYouOwner());
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+
+ // Edit script button - ok if object is editable and there's an
+ // unambiguous destination for the object.
+ if( editable &&
+ all_volume &&
+ ((gSelectMgr->getRootObjectCount() == 1)
+ || (gSelectMgr->getObjectCount() == 1)))
+ {
+ //mBtnNewScript->setEnabled(TRUE);
+ childSetEnabled("button new script",TRUE);
+ }
+ else
+ {
+ //mBtnNewScript->setEnabled(FALSE);
+ childSetEnabled("button new script",FALSE);
+ }
+}
+
+
+void LLPanelContents::refresh()
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+
+ getState(object);
+ if (mPanelInventory)
+ {
+ mPanelInventory->refresh();
+ }
+}
+
+
+
+//
+// Static functions
+//
+
+// static
+void LLPanelContents::onClickNewScript(void *userdata)
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object)
+ {
+ object = gSelectMgr->getFirstObject();
+ }
+ if(object)
+ {
+ // *HACK: In order to resolve SL-22177, we need to create the
+ // script first, and then you have to click it in inventory to
+ // edit it. Bring this back when the functionality is secure.
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ perm.initMasks(
+ PERM_ALL,
+ PERM_ALL,
+ PERM_NONE,
+ PERM_NONE,
+ PERM_MOVE | PERM_TRANSFER);
+ LLString desc;
+ LLAssetType::generateDescriptionFor(LLAssetType::AT_LSL_TEXT, desc);
+ LLPointer<LLViewerInventoryItem> new_item =
+ new LLViewerInventoryItem(
+ LLUUID::null,
+ LLUUID::null,
+ perm,
+ LLUUID::null,
+ LLAssetType::AT_LSL_TEXT,
+ LLInventoryType::IT_LSL,
+ LLString("New Script"),
+ desc,
+ LLSaleInfo::DEFAULT,
+ LLViewerInventoryItem::II_FLAGS_NONE,
+ time_corrected());
+ object->saveScript(new_item, TRUE, true);
+#if 0
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewScriptRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLLiveLSLEditor* editor;
+ editor = new LLLiveLSLEditor("script ed",
+ rect,
+ "Script: New Script",
+ object->mID,
+ LLUUID::null);
+ editor->open();
+
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(editor, FALSE);
+#endif
+ }
+}
diff --git a/indra/newview/llpanelcontents.h b/indra/newview/llpanelcontents.h
new file mode 100644
index 0000000000..273ab20bc7
--- /dev/null
+++ b/indra/newview/llpanelcontents.h
@@ -0,0 +1,39 @@
+/**
+ * @file llpanelcontents.h
+ * @brief Object contents panel in the tools floater.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELCONTENTS_H
+#define LL_LLPANELCONTENTS_H
+
+#include "v3math.h"
+#include "llpanel.h"
+
+class LLButton;
+class LLPanelInventory;
+class LLViewerObject;
+class LLCheckBoxCtrl;
+class LLSpinCtrl;
+
+class LLPanelContents : public LLPanel
+{
+public:
+ virtual BOOL LLPanelContents::postBuild();
+ LLPanelContents(const std::string& name);
+ virtual ~LLPanelContents();
+
+ void refresh();
+
+ static void onClickNewScript( void* userdata);
+
+protected:
+ void getState(LLViewerObject *object);
+
+public:
+ LLPanelInventory* mPanelInventory;
+};
+
+#endif
diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp
new file mode 100644
index 0000000000..d023be66f2
--- /dev/null
+++ b/indra/newview/llpanelface.cpp
@@ -0,0 +1,956 @@
+/**
+ * @file llpanelface.cpp
+ * @brief Panel in the tools floater for editing face textures, colors, etc.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelface.h"
+
+// library includes
+#include "llerror.h"
+#include "llfocusmgr.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "llfontgl.h"
+
+// project includes
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "lldrawpoolbump.h"
+#include "lllineeditor.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltexturectrl.h"
+#include "lltextureentry.h"
+#include "lltexturetable.h"
+#include "lltooldraganddrop.h"
+#include "llui.h"
+#include "llviewercontrol.h"
+#include "llviewerobject.h"
+#include "llviewerstats.h"
+#include "llmediaengine.h"
+
+#include "llvieweruictrlfactory.h"
+
+//
+// Methods
+//
+
+BOOL LLPanelFace::postBuild()
+{
+ LLRect rect = this->getRect();
+ LLTextureCtrl* mTextureCtrl;
+ LLColorSwatchCtrl* mColorSwatch;
+
+ #if LL_MOZILLA_ENABLED
+ LLTextBox* mLabelMediaType;
+ LLComboBox* mComboMediaType;
+ LLTextBox* mLabelMediaURL;
+ LLLineEditor* mLineMediaURL;
+ #endif
+
+ LLTextBox* mLabelTexGen;
+ LLComboBox* mComboTexGen;
+
+ LLCheckBoxCtrl *mCheckFullbright;
+
+ LLTextBox* mLabelColorTransp;
+ LLSpinCtrl* mCtrlColorTransp; // transparency = 1 - alpha
+
+ setMouseOpaque(FALSE);
+ mTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if(mTextureCtrl)
+ {
+ mTextureCtrl->setDefaultImageAssetID(LLUUID( gSavedSettings.getString( "DefaultObjectTexture" )));
+ mTextureCtrl->setCommitCallback( LLPanelFace::onCommitTexture );
+ mTextureCtrl->setOnCancelCallback( LLPanelFace::onCancelTexture );
+ mTextureCtrl->setOnSelectCallback( LLPanelFace::onSelectTexture );
+ mTextureCtrl->setDragCallback(LLPanelFace::onDragTexture);
+ mTextureCtrl->setCallbackUserData( this );
+ mTextureCtrl->setFollowsTop();
+ mTextureCtrl->setFollowsLeft();
+ // Don't allow (no copy) or (no transfer) textures to be selected during immediate mode
+ mTextureCtrl->setImmediateFilterPermMask(PERM_COPY | PERM_TRANSFER);
+ // Allow any texture to be used during non-immediate mode.
+ mTextureCtrl->setNonImmediateFilterPermMask(PERM_NONE);
+ LLAggregatePermissions texture_perms;
+ if (gSelectMgr->selectGetAggregateTexturePermissions(texture_perms))
+ {
+ BOOL can_copy =
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_ALL;
+ BOOL can_transfer =
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_ALL;
+ mTextureCtrl->setCanApplyImmediately(can_copy && can_transfer);
+ }
+ else
+ {
+ mTextureCtrl->setCanApplyImmediately(FALSE);
+ }
+ }
+
+ mColorSwatch = LLUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(mColorSwatch)
+ {
+ mColorSwatch->setCommitCallback(LLPanelFace::onCommitColor);
+ mColorSwatch->setOnCancelCallback(LLPanelFace::onCancelColor);
+ mColorSwatch->setOnSelectCallback(LLPanelFace::onSelectColor);
+ mColorSwatch->setCallbackUserData( this );
+ mColorSwatch->setFollowsTop();
+ mColorSwatch->setFollowsLeft();
+ mColorSwatch->setCanApplyImmediately(TRUE);
+ }
+
+#if LL_MOZILLA_ENABLED
+ mLabelMediaType = LLUICtrlFactory::getTextBoxByName(this,"web_label");
+ if(mLabelMediaType)
+ mLabelMediaType->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+
+ mComboMediaType = LLUICtrlFactory::getComboBoxByName(this,"web_type_combo");
+ if(mComboMediaType)
+ {
+ mComboMediaType->setCommitCallback(onCommitMediaInfo);
+ mComboMediaType->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ mComboMediaType->setCallbackUserData( this );
+ mComboMediaType->add("None");
+ mComboMediaType->add("Web page");
+ }
+
+ mLabelMediaURL = LLUICtrlFactory::getTextBoxByName(this,"url_label");
+ if(mLabelMediaURL)
+ {
+ mLabelMediaURL->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ }
+
+ mLineMediaURL = LLUICtrlFactory::getLineEditorByName(this,"url_line");
+ if(mLineMediaURL)
+ {
+ mLineMediaURL->setCommitCallback(onCommitMediaInfo);
+ mLineMediaURL->setFollows(FOLLOWS_LEFT|FOLLOWS_TOP);
+ mLineMediaURL->setCommitOnFocusLost(TRUE);
+ mLineMediaURL->setCallbackUserData( this );
+ }
+#endif
+ mLabelColorTransp = LLUICtrlFactory::getTextBoxByName(this,"color trans");
+ if(mLabelColorTransp)
+ {
+ mLabelColorTransp->setFollowsTop();
+ mLabelColorTransp->setFollowsLeft();
+ }
+
+ mCtrlColorTransp = LLUICtrlFactory::getSpinnerByName(this,"ColorTrans");
+ if(mCtrlColorTransp)
+ {
+ mCtrlColorTransp->setCommitCallback(LLPanelFace::onCommitAlpha);
+ mCtrlColorTransp->setCallbackUserData(this);
+ mCtrlColorTransp->setPrecision(0);
+ mCtrlColorTransp->setFollowsTop();
+ mCtrlColorTransp->setFollowsLeft();
+ }
+
+ mCheckFullbright = LLUICtrlFactory::getCheckBoxByName(this,"checkbox fullbright");
+ if (mCheckFullbright)
+ {
+ mCheckFullbright->setCommitCallback(LLPanelFace::onCommitFullbright);
+ mCheckFullbright->setCallbackUserData( this );
+ }
+ mLabelTexGen = LLUICtrlFactory::getTextBoxByName(this,"tex gen");
+ mComboTexGen = LLUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if(mComboTexGen)
+ {
+ mComboTexGen->setCommitCallback(LLPanelFace::onCommitTexGen);
+ mComboTexGen->setFollows(FOLLOWS_LEFT | FOLLOWS_TOP);
+ mComboTexGen->setCallbackUserData( this );
+ }
+ childSetCommitCallback("combobox shininess",&LLPanelFace::onCommitShiny,this);
+ childSetCommitCallback("combobox bumpiness",&LLPanelFace::onCommitBump,this);
+ childSetCommitCallback("TexScaleU",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("checkbox flip s",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexScaleV",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("checkbox flip t",&LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexRot",&LLPanelFace::onCommitTextureInfo, this);
+ childSetAction("button apply",&onClickApply,this);
+ childSetCommitCallback("TexOffsetU",LLPanelFace::onCommitTextureInfo, this);
+ childSetCommitCallback("TexOffsetV",LLPanelFace::onCommitTextureInfo, this);
+ childSetAction("button align",onClickAutoFix,this);
+
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelFace::LLPanelFace(const std::string& name)
+: LLPanel(name)
+{
+}
+
+
+LLPanelFace::~LLPanelFace()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+
+void LLPanelFace::sendTexture()
+{
+ LLTextureCtrl* mTextureCtrl = gUICtrlFactory->getTexturePickerByName(this,"texture control");
+ if(!mTextureCtrl) return;
+ if( !mTextureCtrl->getTentative() )
+ {
+ // we grab the item id first, because we want to do a
+ // permissions check in the selection manager. ARGH!
+ LLUUID id = mTextureCtrl->getImageItemID();
+ if(id.isNull())
+ {
+ id = mTextureCtrl->getImageAssetID();
+ }
+ gSelectMgr->selectionSetImage(id);
+ }
+}
+
+void LLPanelFace::sendBump()
+{
+ LLComboBox* mComboBumpiness = gUICtrlFactory->getComboBoxByName(this,"combobox bumpiness");
+ if(!mComboBumpiness)return;
+ U8 bump = (U8) mComboBumpiness->getCurrentIndex() & TEM_BUMP_MASK;
+ gSelectMgr->selectionSetBumpmap( bump );
+}
+
+void LLPanelFace::sendTexGen()
+{
+ LLComboBox* mComboTexGen = gUICtrlFactory->getComboBoxByName(this,"combobox texgen");
+ if(!mComboTexGen)return;
+ U8 tex_gen = (U8) mComboTexGen->getCurrentIndex() << TEM_TEX_GEN_SHIFT;
+ gSelectMgr->selectionSetTexGen( tex_gen );
+}
+
+void LLPanelFace::sendShiny()
+{
+ LLComboBox* mComboShininess = gUICtrlFactory->getComboBoxByName(this,"combobox shininess");
+ if(!mComboShininess)return;
+ U8 shiny = (U8) mComboShininess->getCurrentIndex() & TEM_SHINY_MASK;
+ gSelectMgr->selectionSetShiny( shiny );
+}
+
+void LLPanelFace::sendFullbright()
+{
+ LLCheckBoxCtrl* mCheckFullbright = gUICtrlFactory->getCheckBoxByName(this,"checkbox fullbright");
+ if(!mCheckFullbright)return;
+ U8 fullbright = mCheckFullbright->get() ? TEM_FULLBRIGHT_MASK : 0;
+ gSelectMgr->selectionSetFullbright( fullbright );
+}
+
+#if LL_MOZILLA_ENABLED
+void LLPanelFace::sendMediaInfo()
+{
+ if (mComboMediaType)
+ {
+ U8 media_type = (U8) mComboMediaType->getCurrentIndex();
+ std::string media_url = mLineMediaURL->getText();
+
+ gSelectMgr->selectionSetMediaTypeAndURL( media_type, media_url );
+ }
+}
+#endif
+
+void LLPanelFace::sendColor()
+{
+
+ LLColorSwatchCtrl* mColorSwatch = LLViewerUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(!mColorSwatch)return;
+ LLColor4 color = mColorSwatch->get();
+
+ gSelectMgr->selectionSetColorOnly( color );
+}
+
+void LLPanelFace::sendAlpha()
+{
+ LLSpinCtrl* mCtrlColorTransp = LLViewerUICtrlFactory::getSpinnerByName(this,"ColorTrans");
+ if(!mCtrlColorTransp)return;
+ F32 alpha = (100.f - mCtrlColorTransp->get()) / 100.f;
+
+ gSelectMgr->selectionSetAlphaOnly( alpha );
+}
+
+
+
+void LLPanelFace::sendTextureInfo()
+{
+ S32 te;
+ LLViewerObject* object;
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ BOOL valid;
+ F32 value;
+ LLSpinCtrl* mCtrlTexScaleS = LLViewerUICtrlFactory::getSpinnerByName(this,"TexScaleU");
+ LLSpinCtrl* mCtrlTexScaleT = LLViewerUICtrlFactory::getSpinnerByName(this,"TexScaleV");
+ LLSpinCtrl* mCtrlTexOffsetS = LLViewerUICtrlFactory::getSpinnerByName(this,"TexOffsetU");
+ LLSpinCtrl* mCtrlTexOffsetT = LLViewerUICtrlFactory::getSpinnerByName(this,"TexOffsetV");
+ LLSpinCtrl* mCtrlTexRotation = LLViewerUICtrlFactory::getSpinnerByName(this,"TexRot");
+ LLCheckBoxCtrl* mCheckFlipScaleS = LLViewerUICtrlFactory::getCheckBoxByName(this,"checkbox flip s");
+ LLCheckBoxCtrl* mCheckFlipScaleT = LLViewerUICtrlFactory::getCheckBoxByName(this,"checkbox flip t");
+ LLComboBox* mComboTexGen = LLViewerUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if (mCtrlTexScaleS)
+ {
+ valid = !mCtrlTexScaleS->getTentative() || !mCheckFlipScaleS->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexScaleS->get();
+ if( mCheckFlipScaleS->get() )
+ {
+ value = -value;
+ }
+ if (mComboTexGen->getCurrentIndex() == 1)
+ {
+ value *= 0.5f;
+ }
+ object->setTEScaleS( te, value );
+ }
+ }
+
+ if (mCtrlTexScaleT)
+ {
+ valid = !mCtrlTexScaleT->getTentative() || !mCheckFlipScaleT->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexScaleT->get();
+ if( mCheckFlipScaleT->get() )
+ {
+ value = -value;
+ }
+ if (mComboTexGen->getCurrentIndex() == 1)
+ {
+ value *= 0.5f;
+ }
+ object->setTEScaleT( te, value );
+ }
+ }
+
+ if (mCtrlTexOffsetS)
+ {
+ valid = !mCtrlTexOffsetS->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexOffsetS->get();
+ object->setTEOffsetS( te, value );
+ }
+ }
+
+ if (mCtrlTexOffsetT)
+ {
+ valid = !mCtrlTexOffsetT->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexOffsetT->get();
+ object->setTEOffsetT( te, value );
+ }
+ }
+
+ if (mCtrlTexRotation)
+ {
+ valid = !mCtrlTexRotation->getTentative();
+ if (valid)
+ {
+ value = mCtrlTexRotation->get() * DEG_TO_RAD;
+ object->setTERotation( te, value );
+ }
+ }
+ }
+
+ for ( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->sendTEUpdate();
+ }
+}
+
+void LLPanelFace::getState()
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstObject();
+
+ if( objectp
+ && objectp->getPCode() == LL_PCODE_VOLUME)
+ {
+ BOOL editable = objectp->permModify();
+
+ // only turn on auto-adjust button if there is a media renderer and the media is loaded
+ childSetEnabled("textbox autofix",FALSE);
+ //mLabelTexAutoFix->setEnabled ( FALSE );
+ childSetEnabled("button align",FALSE);
+ //mBtnAutoFix->setEnabled ( FALSE );
+
+ if ( LLMediaEngine::getInstance()->getMediaRenderer () )
+ if ( LLMediaEngine::getInstance()->getMediaRenderer ()->isLoaded () )
+ {
+ childSetEnabled("textbox autofix",editable);
+ //mLabelTexAutoFix->setEnabled ( editable );
+ childSetEnabled("button align",editable);
+ //mBtnAutoFix->setEnabled ( editable );
+ }
+ childSetEnabled("button apply",editable);
+ //mBtnApply->setEnabled( editable );
+
+ #if LL_MOZILLA_ENABLED
+ if (gSavedSettings.getBOOL("UseWebPagesOnPrims"))
+ {
+ // Web page selection
+ mLabelMediaType->setEnabled(editable);
+ mLabelMediaType->setToolTip("Experimental");
+
+ // JAMESDEBUG - use viewerobject mMedia->mMediaType when transmission is wired in
+ U8 media_type = LLViewerObject::MEDIA_TYPE_NONE;
+ bool same = gSelectMgr->selectionGetMediaType( &media_type );
+ mComboMediaType->setTentative( !same );
+ mComboMediaType->setEnabled( editable );
+ mComboMediaType->setCurrentByIndex( (S32)media_type );
+ mComboMediaType->setToolTip("Experimental");
+
+ mLabelMediaURL->setEnabled(editable);
+ mLabelMediaURL->setToolTip("Experimental");
+
+ const std::string& media_url = objectp->getMediaURL();
+ mLineMediaURL->setEnabled( editable );
+ mLineMediaURL->setText( media_url );
+ mLineMediaURL->setToolTip("Experimental");
+ }
+ else
+ {
+ mLabelMediaType->setEnabled(FALSE);
+ mLabelMediaType->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mComboMediaType->setEnabled( FALSE );
+ mComboMediaType->setCurrentByIndex( LLViewerObject::MEDIA_TYPE_NONE );
+ mComboMediaType->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mLabelMediaURL->setEnabled(FALSE);
+ mLabelMediaURL->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+
+ mLineMediaURL->setEnabled( FALSE );
+ mLineMediaURL->setText( "" );
+ mLineMediaURL->setToolTip("Disabled because 'Show web pages on objects' preference is disabled");
+ }
+ #endif
+
+ // Texture
+ LLUUID id;
+ BOOL identical = gSelectMgr->selectionGetTexUUID(id);
+ LLTextureCtrl* mTextureCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if (identical)
+ {
+ // All selected have the same texture
+ if(mTextureCtrl){
+ mTextureCtrl->setTentative( FALSE );
+ mTextureCtrl->setEnabled( editable );
+ mTextureCtrl->setImageAssetID( id );
+ }
+ }
+ else
+ {
+ if(mTextureCtrl){
+ if( id.isNull() )
+ {
+ // None selected
+ mTextureCtrl->setTentative( FALSE );
+ mTextureCtrl->setEnabled( FALSE );
+ mTextureCtrl->setImageAssetID( LLUUID::null );
+ }
+ else
+ {
+ // Tentative: multiple selected with different textures
+ mTextureCtrl->setTentative( TRUE );
+ mTextureCtrl->setEnabled( editable );
+ mTextureCtrl->setImageAssetID( id );
+ }
+ }
+ }
+
+ LLAggregatePermissions texture_perms;
+ if(mTextureCtrl)
+ {
+// mTextureCtrl->setValid( editable );
+
+ if (gSelectMgr->selectGetAggregateTexturePermissions(texture_perms))
+ {
+ BOOL can_copy =
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_COPY) == LLAggregatePermissions::AP_ALL;
+ BOOL can_transfer =
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_EMPTY ||
+ texture_perms.getValue(PERM_TRANSFER) == LLAggregatePermissions::AP_ALL;
+ mTextureCtrl->setCanApplyImmediately(can_copy && can_transfer);
+ }
+ else
+ {
+ mTextureCtrl->setCanApplyImmediately(FALSE);
+ }
+ }
+
+ // Texture scale
+ {
+ childSetEnabled("tex scale",editable);
+ //mLabelTexScale->setEnabled( editable );
+ F32 scale_s = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueScaleS, &scale_s );
+ childSetValue("TexScaleU",editable ? llabs(scale_s) : 0);
+ childSetTentative("TexScaleU",LLSD((BOOL)(!identical)));
+ childSetEnabled("TexScaleU",editable);
+ childSetValue("checkbox flip s",LLSD((BOOL)(scale_s < 0 ? TRUE : FALSE )));
+ childSetTentative("checkbox flip s",LLSD((BOOL)((!identical) ? TRUE : FALSE )));
+ childSetEnabled("checkbox flip s",editable);
+ }
+
+ {
+ F32 scale_t = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueScaleT, &scale_t );
+
+ childSetValue("TexScaleV",llabs(editable ? llabs(scale_t) : 0));
+ childSetTentative("TexScaleV",LLSD((BOOL)(!identical)));
+ childSetEnabled("TexScaleV",editable);
+ childSetValue("checkbox flip t",LLSD((BOOL)(scale_t< 0 ? TRUE : FALSE )));
+ childSetTentative("checkbox flip t",LLSD((BOOL)((!identical) ? TRUE : FALSE )));
+ childSetEnabled("checkbox flip t",editable);
+ }
+
+ // Texture offset
+ {
+ childSetEnabled("tex offset",editable);
+ F32 offset_s = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueOffsetS, &offset_s );
+ childSetValue("TexOffsetU", editable ? offset_s : 0);
+ childSetTentative("TexOffsetU",!identical);
+ childSetEnabled("TexOffsetU",editable);
+ }
+
+ {
+ F32 offset_t = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueOffsetT, &offset_t );
+ childSetValue("TexOffsetV", editable ? offset_t : 0);
+ childSetTentative("TexOffsetV",!identical);
+ childSetEnabled("TexOffsetV",editable);
+ }
+
+ // Texture rotation
+ {
+ childSetEnabled("tex rotate",editable);
+ F32 rotation = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueTexRotation, &rotation );
+ childSetValue("TexRot", editable ? rotation * RAD_TO_DEG : 0);
+ childSetTentative("TexRot",!identical);
+ childSetEnabled("TexRot",editable);
+ }
+
+ // Color swatch
+ LLColorSwatchCtrl* mColorSwatch = LLViewerUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ LLColor4 color = LLColor4::white;
+ if(mColorSwatch)
+ {
+ identical = gSelectMgr->selectionGetColor(color);
+ mColorSwatch->setOriginal(color);
+ mColorSwatch->set(color, TRUE);
+
+ mColorSwatch->setValid(editable);
+ mColorSwatch->setEnabled( editable );
+ mColorSwatch->setCanApplyImmediately( editable );
+ }
+ // Color transparency
+ {
+ childSetEnabled("color trans",editable);
+ }
+
+ F32 transparency = (1.f - color.mV[VALPHA]) * 100.f;
+ {
+ childSetValue("ColorTrans", editable ? transparency : 0);
+ childSetEnabled("ColorTrans",editable);
+ }
+
+ // Bump
+ {
+ F32 shinyf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueShiny, &shinyf );
+ childGetSelectionInterface("combobox shininess")->selectNthItem((S32)shinyf);
+ childSetEnabled("combobox shininess",editable);
+ childSetTentative("combobox shininess",!identical);
+ childSetEnabled("label shininess",editable);
+ }
+
+ {
+ F32 bumpf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueBump, &bumpf );
+ childGetSelectionInterface("combobox bumpiness")->selectNthItem((S32)bumpf);
+ childSetEnabled("combobox bumpiness",editable);
+ childSetTentative("combobox bumpiness",!identical);
+ childSetEnabled("label bumpiness",editable);
+ }
+
+ {
+ F32 genf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueTexGen, &genf);
+ S32 selected_texgen = ((S32) genf) >> TEM_TEX_GEN_SHIFT;
+ childGetSelectionInterface("combobox texgen")->selectNthItem(selected_texgen);
+ childSetEnabled("combobox texgen",editable);
+ childSetTentative("combobox texgen",!identical);
+ childSetEnabled("tex gen",editable);
+
+ if (selected_texgen == 1)
+ {
+ childSetText("tex scale",childGetText("string repeats per meter"));
+ childSetValue("TexScaleU", 2.0f * childGetValue("TexScaleU").asReal() );
+ childSetValue("TexScaleV", 2.0f * childGetValue("TexScaleV").asReal() );
+ }
+ else
+ {
+ childSetText("tex scale",childGetText("string repeats per face"));
+ }
+
+ }
+
+ {
+ F32 fullbrightf = 0.f;
+ identical = allFacesSameValue( &LLPanelFace::valueFullbright, &fullbrightf );
+
+ childSetValue("checkbox fullbright",(S32)fullbrightf);
+ childSetEnabled("checkbox fullbright",editable);
+ childSetTentative("checkbox fullbright",!identical);
+ }
+
+ // Repeats per meter label
+ {
+ childSetEnabled("rpt",editable);
+ }
+
+ // Repeats per meter
+ F32 repeats = 1.f;
+ identical = allFacesSameValue( &LLPanelFace::valueRepeatsPerMeter, &repeats );
+ {
+ childSetValue("rptctrl", editable ? repeats : 0);
+ childSetTentative("rptctrl",!identical);
+ LLComboBox* mComboTexGen = LLViewerUICtrlFactory::getComboBoxByName(this,"combobox texgen");
+ if (mComboTexGen)
+ {
+ BOOL enabled = editable && (!mComboTexGen || mComboTexGen->getCurrentIndex() != 1);
+ childSetEnabled("rptctrl",enabled);
+ childSetEnabled("button apply",enabled);
+ }
+ }
+ }
+ else
+ {
+ // Disable all UICtrls
+ clearCtrls();
+
+ // Disable non-UICtrls
+ LLTextureCtrl* mTextureCtrl = LLUICtrlFactory::getTexturePickerByName(this,"texture control");
+ if(mTextureCtrl)
+ {
+ mTextureCtrl->setImageAssetID( LLUUID::null );
+ mTextureCtrl->setEnabled( FALSE ); // this is a LLUICtrl, but we don't want it to have keyboard focus so we add it as a child, not a ctrl.
+// mTextureCtrl->setValid(FALSE);
+ }
+ LLColorSwatchCtrl* mColorSwatch = LLUICtrlFactory::getColorSwatchByName(this,"colorswatch");
+ if(mColorSwatch)
+ {
+ mColorSwatch->setEnabled( FALSE );
+ mColorSwatch->setValid(FALSE);
+ }
+ childSetEnabled("color trans",FALSE);
+ childSetEnabled("rpt",FALSE);
+ childSetEnabled("tex scale",FALSE);
+ childSetEnabled("tex offset",FALSE);
+ childSetEnabled("tex rotate",FALSE);
+ childSetEnabled("tex gen",FALSE);
+ childSetEnabled("label shininess",FALSE);
+ childSetEnabled("label bumpiness",FALSE);
+
+ childSetEnabled("textbox autofix",FALSE);
+
+ childSetEnabled("button align",FALSE);
+ childSetEnabled("button apply",FALSE);
+ }
+}
+
+
+void LLPanelFace::refresh()
+{
+ getState();
+}
+
+
+BOOL LLPanelFace::allFacesSameValue( F32 (get_face_value(LLViewerObject*, S32)), F32 *value)
+{
+ LLViewerObject* object;
+ S32 te;
+
+ // Get the value from the primary selected TE
+ F32 first_value = *value;
+ BOOL got_first = FALSE;
+ gSelectMgr->getPrimaryTE(&object, &te);
+ if (object)
+ {
+ first_value = get_face_value(object, te);
+ got_first = true;
+ }
+
+ // Now iterate through all TEs to test for sameness
+ BOOL identical = TRUE;
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ if (!got_first)
+ {
+ first_value = get_face_value(object, te);
+ got_first = true;
+ }
+ if ( get_face_value(object, te) != first_value )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *value = first_value;
+ return identical;
+}
+
+
+//
+// Static functions
+//
+
+// static
+F32 LLPanelFace::valueRepeatsPerMeter(LLViewerObject* object, S32 face)
+{
+ U32 s_axis = VX;
+ U32 t_axis = VY;
+
+ // BUG: Only repeats along S axis
+ // BUG: Only works for boxes.
+ gSelectMgr->getTESTAxes(object, face, &s_axis, &t_axis);
+ return object->getTE(face)->mScaleS / object->getScale().mV[s_axis];
+}
+
+// static
+F32 LLPanelFace::valueScaleS(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mScaleS;
+}
+
+
+// static
+F32 LLPanelFace::valueScaleT(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mScaleT;
+}
+
+// static
+F32 LLPanelFace::valueOffsetS(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mOffsetS;
+}
+
+// static
+F32 LLPanelFace::valueOffsetT(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mOffsetT;
+}
+
+// static
+F32 LLPanelFace::valueTexRotation(LLViewerObject* object, S32 face)
+{
+ return object->getTE(face)->mRotation;
+}
+
+// static
+F32 LLPanelFace::valueBump(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getBumpmap());
+}
+
+// static
+F32 LLPanelFace::valueTexGen(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getTexGen());
+}
+
+// static
+F32 LLPanelFace::valueShiny(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getShiny());
+}
+
+// static
+F32 LLPanelFace::valueFullbright(LLViewerObject* object, S32 face)
+{
+ return (F32)(object->getTE(face)->getFullbright());
+}
+
+
+// static
+void LLPanelFace::onCommitColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendColor();
+}
+
+// static
+void LLPanelFace::onCommitAlpha(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendAlpha();
+}
+
+// static
+void LLPanelFace::onCancelColor(LLUICtrl* ctrl, void* userdata)
+{
+ gSelectMgr->selectionRevertColors();
+}
+
+// static
+void LLPanelFace::onSelectColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ gSelectMgr->saveSelectedObjectColors();
+ self->sendColor();
+}
+
+// static
+void LLPanelFace::onCommitBump(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendBump();
+}
+
+// static
+void LLPanelFace::onCommitTexGen(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendTexGen();
+}
+
+// static
+void LLPanelFace::onCommitShiny(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendShiny();
+}
+
+// static
+void LLPanelFace::onCommitFullbright(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendFullbright();
+}
+
+// static
+BOOL LLPanelFace::onDragTexture(LLUICtrl*, LLInventoryItem* item, void*)
+{
+ BOOL accept = TRUE;
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ while(accept && obj)
+ {
+ if(!LLToolDragAndDrop::isInventoryDropAcceptable(obj, item))
+ accept = FALSE;
+ else
+ obj = gSelectMgr->getNextRootObject();
+ }
+ return accept;
+}
+
+// static
+void LLPanelFace::onCommitTexture( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+
+ self->sendTexture();
+}
+
+// static
+void LLPanelFace::onCancelTexture(LLUICtrl* ctrl, void* userdata)
+{
+ gSelectMgr->selectionRevertTextures();
+}
+
+// static
+void LLPanelFace::onSelectTexture(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ gSelectMgr->saveSelectedObjectTextures();
+ self->sendTexture();
+}
+
+
+// static
+void LLPanelFace::onCommitTextureInfo( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+ self->sendTextureInfo();
+}
+
+#if LL_MOZILLA_ENABLED
+// static
+void LLPanelFace::onCommitMediaInfo(LLUICtrl* ctrl, void* data)
+{
+ LLPanelFace* self = (LLPanelFace*) data;
+ self->sendMediaInfo();
+}
+#endif
+
+// Commit the number of repeats per meter
+// static
+void LLPanelFace::onClickApply(void* userdata)
+{
+ LLPanelFace* self = (LLPanelFace*) userdata;
+
+ gFocusMgr.setKeyboardFocus( NULL, NULL );
+
+ //F32 repeats_per_meter = self->mCtrlRepeatsPerMeter->get();
+ F32 repeats_per_meter = (F32)self->childGetValue( "rptctrl" ).asReal();//self->mCtrlRepeatsPerMeter->get();
+ gSelectMgr->selectionTexScaleAutofit( repeats_per_meter );
+}
+
+// commit the fit media texture to prim button
+void LLPanelFace::onClickAutoFix(void* userdata)
+{
+ S32 te;
+ LLViewerObject* object;
+
+ // for all selected objects
+ for ( gSelectMgr->getFirstTE(&object, &te); object; gSelectMgr->getNextTE(&object, &te) )
+ {
+ // only do this if it's a media texture
+ if ( object->getTE ( te )->getID() == LLMediaEngine::getInstance()->getImageUUID () )
+ {
+ // make sure we're valid
+ if ( LLMediaEngine::getInstance()->getMediaRenderer() )
+ {
+ // calculate correct scaling based on media dimensions and next-power-of-2 texture dimensions
+ F32 scaleS = (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getMediaWidth() /
+ (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getTextureWidth();
+
+ F32 scaleT = (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getMediaHeight() /
+ (F32)LLMediaEngine::getInstance()->getMediaRenderer()->getTextureHeight();
+
+ // set scale and adjust offset
+ object->setTEScaleS( te, scaleS );
+ object->setTEScaleT( te, scaleT ); // don't need to flip Y anymore since QT does this for us now.
+ object->setTEOffsetS( te, -( 1.0f - scaleS ) / 2.0f );
+ object->setTEOffsetT( te, -( 1.0f - scaleT ) / 2.0f );
+ };
+ };
+ };
+
+ // not clear why this is in a separate loop but i followed the patter from further up this file just in case.
+ for ( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->sendTEUpdate();
+ };
+}
diff --git a/indra/newview/llpanelface.h b/indra/newview/llpanelface.h
new file mode 100644
index 0000000000..03f361d23c
--- /dev/null
+++ b/indra/newview/llpanelface.h
@@ -0,0 +1,136 @@
+/**
+ * @file llpanelface.h
+ * @brief Panel in the tools floater for editing face textures, colors, etc.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELFACE_H
+#define LL_LLPANELFACE_H
+
+#include "v4color.h"
+#include "llpanel.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLColorSwatchCtrl;
+class LLComboBox;
+class LLInventoryItem;
+class LLLineEditor;
+class LLSpinCtrl;
+class LLTextBox;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLViewerObject;
+
+class LLPanelFace : public LLPanel
+{
+public:
+ virtual BOOL postBuild();
+ LLPanelFace(const std::string& name);
+ virtual ~LLPanelFace();
+
+ void refresh();
+
+protected:
+ // Given a callback function that returns an F32, figures out
+ // if that F32 is the same for all selected faces. "value"
+ // contains the identical value, or the first object's value.
+ BOOL allFacesSameValue( F32 (get_face_value(LLViewerObject*, S32)), F32 *value);
+
+ void getState();
+
+ void sendTexture(); // applies and sends texture
+ void sendTextureInfo(); // applies and sends texture scale, offset, etc.
+ void sendColor(); // applies and sends color
+ void sendAlpha(); // applies and sends transparency
+ void sendBump(); // applies and sends bump map
+ void sendTexGen(); // applies and sends bump map
+ void sendShiny(); // applies and sends shininess
+ void sendFullbright(); // applies and sends full bright
+ #if LL_MOZILLA_ENABLED
+ void sendMediaInfo(); // web page settings and URL
+ #endif
+
+ // this function is to return TRUE if the dra should succeed.
+ static BOOL onDragTexture(LLUICtrl* ctrl, LLInventoryItem* item, void* ud);
+
+ static void onCommitTexture( LLUICtrl* ctrl, void* userdata);
+ static void onCancelTexture( LLUICtrl* ctrl, void* userdata);
+ static void onSelectTexture( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTextureInfo( LLUICtrl* ctrl, void* userdata);
+ static void onCommitColor( LLUICtrl* ctrl, void* userdata);
+ static void onCommitAlpha( LLUICtrl* ctrl, void* userdata);
+ static void onCancelColor( LLUICtrl* ctrl, void* userdata);
+ static void onSelectColor( LLUICtrl* ctrl, void* userdata);
+ static void onCommitBump( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTexGen( LLUICtrl* ctrl, void* userdata);
+ static void onCommitShiny( LLUICtrl* ctrl, void* userdata);
+ static void onCommitFullbright( LLUICtrl* ctrl, void* userdata);
+ #if LL_MOZILLA_ENABLED
+ static void onCommitMediaInfo( LLUICtrl* ctrl, void* data);
+ #endif
+
+ static void onClickApply(void*);
+ static void onClickAutoFix(void*);
+
+ static F32 valueScaleS(LLViewerObject* object, S32 face);
+ static F32 valueScaleT(LLViewerObject* object, S32 face);
+ static F32 valueOffsetS(LLViewerObject* object, S32 face);
+ static F32 valueOffsetT(LLViewerObject* object, S32 face);
+ static F32 valueTexRotation(LLViewerObject* object, S32 face);
+ static F32 valueRepeatsPerMeter(LLViewerObject* object, S32 face);
+ static F32 valueBump(LLViewerObject* object, S32 face);
+ static F32 valueTexGen(LLViewerObject* object, S32 face);
+ static F32 valueShiny(LLViewerObject* object, S32 face);
+ static F32 valueFullbright(LLViewerObject* object, S32 face);
+
+protected:
+ //LLTextureCtrl* mTextureCtrl;
+ //LLColorSwatchCtrl* mColorSwatch;
+
+ //#if LL_MOZILLA_ENABLED
+ //LLTextBox* mLabelMediaType;
+ //LLComboBox* mComboMediaType;
+ //LLTextBox* mLabelMediaURL;
+ //LLLineEditor* mLineMediaURL;
+ //#endif
+
+ //LLTextBox *mLabelTexScale;
+ //LLSpinCtrl *mCtrlTexScaleS;
+ //LLSpinCtrl *mCtrlTexScaleT;
+
+ //LLCheckBoxCtrl *mCheckFlipScaleS;
+ //LLCheckBoxCtrl *mCheckFlipScaleT;
+
+ //LLTextBox *mLabelTexOffset;
+ //LLSpinCtrl *mCtrlTexOffsetS;
+ //LLSpinCtrl *mCtrlTexOffsetT;
+
+ //LLTextBox *mLabelTexRotation;
+ //LLSpinCtrl *mCtrlTexRotation;
+
+ //LLTextBox* mLabelTexGen;
+ //LLComboBox* mComboTexGen;
+
+ //LLTextBox* mLabelShininess;
+ //LLComboBox* mComboShininess;
+
+ //LLTextBox* mLabelBumpiness;
+ //LLComboBox* mComboBumpiness;
+
+ //LLCheckBoxCtrl *mCheckFullbright;
+ //
+ //LLTextBox* mLabelColorTransp;
+ //LLSpinCtrl* mCtrlColorTransp; // transparency = 1 - alpha
+
+ //LLTextBox* mLabelRepeatsPerMeter;
+ //LLSpinCtrl* mCtrlRepeatsPerMeter;
+ //LLButton* mBtnApply;
+
+ //LLTextBox* mLabelTexAutoFix;
+ //LLButton* mBtnAutoFix;
+};
+
+#endif
diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp
new file mode 100644
index 0000000000..9ad5855b8e
--- /dev/null
+++ b/indra/newview/llpanelgroup.cpp
@@ -0,0 +1,651 @@
+/**
+ * @file llpanelgroup.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroup.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llpanelgroupgeneral.h"
+#include "llpanelgrouproles.h"
+#include "llpanelgroupvoting.h"
+#include "llpanelgrouplandmoney.h"
+#include "llpanelgroupnotices.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "llviewermessage.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+// static
+void* LLPanelGroupTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupTab("panel group tab", *group_id);
+}
+
+LLPanelGroupTab::~LLPanelGroupTab()
+{
+ mObservers.clear();
+}
+
+BOOL LLPanelGroupTab::isVisibleByAgent(LLAgent* agentp)
+{
+ //default to being visible
+ return TRUE;
+}
+
+BOOL LLPanelGroupTab::postBuild()
+{
+ // Hook up the help button callback.
+ LLButton* button = (LLButton*) getChildByName("help_button");
+ if (button)
+ {
+ button->setClickedCallback(onClickHelp);
+ button->setCallbackUserData(this);
+ }
+
+ // Read help text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*) getChildByName("help_text");
+ if (txt)
+ {
+ mHelpText = txt->getText();
+ removeChild(txt);
+ }
+ return TRUE;
+}
+
+void LLPanelGroupTab::addObserver(LLPanelGroupTabObserver *obs)
+{
+ mObservers.insert(obs);
+}
+
+void LLPanelGroupTab::removeObserver(LLPanelGroupTabObserver *obs)
+{
+ mObservers.erase(obs);
+}
+
+void LLPanelGroupTab::notifyObservers()
+{
+
+ for (observer_list_t::iterator iter = mObservers.begin();
+ iter != mObservers.end(); )
+ {
+ LLPanelGroupTabObserver* observer = *iter;
+ observer->tabChanged();
+
+ // safe way to incrament since changed may delete entries! (@!##%@!@&*!)
+ iter = mObservers.upper_bound(observer);
+ }
+}
+
+// static
+void LLPanelGroupTab::onClickHelp(void* user_data)
+{
+ LLPanelGroupTab* self = static_cast<LLPanelGroupTab*>(user_data);
+ self->handleClickHelp();
+}
+
+void LLPanelGroupTab::handleClickHelp()
+{
+ // Display the help text.
+ LLString help_text( getHelpText() );
+ if ( !help_text.empty() )
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = help_text;
+ LLAlertDialog* dialogp = gViewerWindow->alertXml("GenericAlert", args);
+ if (dialogp)
+ {
+ LLView* viewp = this;
+ LLFloater* root_floater = NULL;
+ while(viewp)
+ {
+ if(viewp->getWidgetType() == WIDGET_TYPE_FLOATER)
+ {
+ root_floater = (LLFloater*)viewp;
+ }
+ viewp = viewp->getParent();
+ }
+ if (root_floater)
+ {
+ root_floater->addDependentFloater(dialogp);
+ }
+ }
+ }
+}
+
+LLPanelGroup::LLPanelGroup(const std::string& filename,
+ const std::string& name,
+ const LLUUID& group_id,
+ const std::string& initial_tab_selected)
+: LLPanel(name, LLRect(), FALSE),
+ LLGroupMgrObserver( group_id ),
+ mCurrentTab( NULL ),
+ mRequestedTab( NULL ),
+ mTabContainer( NULL ),
+ mForceClose( FALSE ),
+ mIgnoreTransition( FALSE ),
+ mInitialTab(initial_tab_selected),
+ mAllowEdit( TRUE ),
+ mShowingNotifyDialog( FALSE )
+{
+ // Set up the factory callbacks.
+ mFactoryMap["general_tab"] = LLCallbackMap(LLPanelGroupGeneral::createTab,
+ &mID);
+ mFactoryMap["roles_tab"] = LLCallbackMap(LLPanelGroupRoles::createTab,
+ &mID);
+ mFactoryMap["notices_tab"] = LLCallbackMap(LLPanelGroupNotices::createTab,
+ &mID);
+ mFactoryMap["voting_tab"] = LLCallbackMap(LLPanelGroupVoting::createTab,
+ &mID);
+ mFactoryMap["land_money_tab"]= LLCallbackMap(LLPanelGroupLandMoney::createTab,
+ &mID);
+ // Roles sub tabs
+ mFactoryMap["members_sub_tab"] = LLCallbackMap(LLPanelGroupMembersSubTab::createTab, &mID);
+ mFactoryMap["roles_sub_tab"] = LLCallbackMap(LLPanelGroupRolesSubTab::createTab, &mID);
+ mFactoryMap["actions_sub_tab"] = LLCallbackMap(LLPanelGroupActionsSubTab::createTab, &mID);
+
+ // Pass on construction of this panel to the control factory.
+ gUICtrlFactory->buildPanel(this, filename, &getFactoryMap());
+ mFilename = filename;
+
+ gGroupMgr->addObserver(this);
+}
+
+LLPanelGroup::~LLPanelGroup()
+{
+ gGroupMgr->removeObserver(this);
+
+ int i;
+ int tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanelGroupTab* panelp =
+ (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i);
+
+ if ( panelp ) panelp->removeObserver(this);
+ }
+}
+
+void LLPanelGroup::updateTabVisibility()
+{
+ S32 i;
+ S32 tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanelGroupTab* panelp =
+ (LLPanelGroupTab*) mTabContainer->getPanelByIndex(i);
+
+ BOOL visible = panelp->isVisibleByAgent(&gAgent);
+ mTabContainer->enableTabButton(i, visible);
+
+ if ( !visible && mCurrentTab == panelp )
+ {
+ //we are disabling the currently selected tab
+ //select the previous one
+ mTabContainer->selectPrevTab();
+ mCurrentTab =
+ (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ }
+ }
+}
+
+
+
+BOOL LLPanelGroup::postBuild()
+{
+ mTabContainer = (LLTabContainerCommon*) getChildByName("group_tab_container");
+
+ if (mTabContainer)
+ {
+ // Select the initial tab specified via constructor
+ const BOOL recurse = TRUE;
+ LLPanelGroupTab* tabp =
+ (LLPanelGroupTab*) getChildByName(mInitialTab, recurse);
+
+ if (!tabp)
+ {
+ //our initial tab selection was invalid, just select the
+ //first tab then or default to selecting the initial
+ //selected tab specified in the layout file
+ tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ //no tab was initially selected through constructor
+ //or the XML, select the first tab
+ if (!tabp)
+ {
+ mTabContainer->selectFirstTab();
+ tabp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ }
+ }
+ else
+ {
+ mTabContainer->selectTabPanel(tabp);
+ }
+
+ mCurrentTab = tabp;
+
+ // Add click callbacks.
+ S32 i;
+ S32 tab_count = mTabContainer->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ LLPanel* tab_panel = mTabContainer->getPanelByIndex(i);
+ LLPanelGroupTab* panelp =(LLPanelGroupTab*)tab_panel; // bit of a hack
+
+ // Pass on whether or not to allow edit to tabs.
+ panelp->setAllowEdit(mAllowEdit);
+ panelp->addObserver(this);
+
+ mTabContainer->setTabChangeCallback(panelp, onClickTab);
+ mTabContainer->setTabUserData(panelp, this);
+ }
+ updateTabVisibility();
+
+ // Act as though this tab was just activated.
+ mCurrentTab->activate();
+ }
+
+ // Read apply text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("default_needs_apply_text");
+ if (txt)
+ {
+ mDefaultNeedsApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+ txt = (LLTextBox*)getChildByName("want_apply_text");
+ if (txt)
+ {
+ mWantApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+
+ LLButton* button = (LLButton*) getChildByName("btn_ok");
+ if (button)
+ {
+ button->setClickedCallback(onBtnOK);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ button = (LLButton*) getChildByName("btn_cancel");
+ if (button)
+ {
+ button->setClickedCallback(onBtnCancel);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ button = (LLButton*) getChildByName("btn_apply");
+ if (button)
+ {
+ button->setClickedCallback(onBtnApply);
+ button->setVisible(mAllowEdit);
+ button->setEnabled(FALSE);
+
+ mApplyBtn = button;
+ }
+
+ button = (LLButton*) getChildByName("btn_refresh");
+ if (button)
+ {
+ button->setClickedCallback(onBtnRefresh);
+ button->setCallbackUserData(this);
+ button->setVisible(mAllowEdit);
+ }
+
+ return TRUE;
+}
+
+void LLPanelGroup::changed(LLGroupChange gc)
+{
+ updateTabVisibility();
+ // Notify the currently active panel that group manager information has changed.
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ if (panelp)
+ {
+ panelp->update(gc);
+ }
+}
+
+// PanelGroupTab observer trigger
+void LLPanelGroup::tabChanged()
+{
+ //some tab information has changed,....enable/disable the apply button
+ //based on if they need an apply
+ if ( mApplyBtn )
+ {
+ LLString mesg;
+ mApplyBtn->setEnabled(mAllowEdit && mCurrentTab->needsApply(mesg));
+ }
+}
+
+// static
+void LLPanelGroup::onClickTab(void* user_data, bool from_click)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->handleClickTab();
+}
+
+void LLPanelGroup::handleClickTab()
+{
+ // If we are already handling a transition,
+ // ignore this.
+ if (mIgnoreTransition)
+ {
+ return;
+ }
+
+ mRequestedTab = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+
+ // Make sure they aren't just clicking the same tab...
+ if (mRequestedTab == mCurrentTab)
+ {
+ return;
+ }
+
+ // Try to switch from the current panel to the panel the user selected.
+ attemptTransition();
+}
+
+void LLPanelGroup::setGroupID(const LLUUID& group_id)
+{
+ LLRect rect(getRect());
+
+ gGroupMgr->removeObserver(this);
+ mID = group_id;
+ gGroupMgr->addObserver(this);
+ //TODO: this is really bad, we should add a method
+ // where the panels can just update themselves
+ // on a group id change. Similar to update() but with a group
+ // id change.
+
+ // For now, rebuild panel
+ //delete children and rebuild panel
+ deleteAllChildren();
+ gUICtrlFactory->buildPanel(this, mFilename, &getFactoryMap());
+}
+
+void LLPanelGroup::selectTab(std::string tab_name)
+{
+ const BOOL recurse = TRUE;
+
+ LLPanelGroupTab* tabp =
+ (LLPanelGroupTab*) getChildByName(tab_name, recurse);
+
+ if ( tabp && mTabContainer )
+ {
+ mTabContainer->selectTabPanel(tabp);
+ onClickTab(this, false);
+ }
+}
+
+BOOL LLPanelGroup::canClose()
+{
+ if (mShowingNotifyDialog) return FALSE;
+ if (mCurrentTab && mCurrentTab->hasModal()) return FALSE;
+ if (mForceClose || !mAllowEdit) return TRUE;
+
+ // Try to switch from the current panel to nothing, indicating a close action.
+ mRequestedTab = NULL;
+ return attemptTransition();
+}
+
+BOOL LLPanelGroup::attemptTransition()
+{
+ // Check if the current tab needs to be applied.
+ LLString mesg;
+ if (mCurrentTab && mCurrentTab->needsApply(mesg))
+ {
+ // If no message was provided, give a generic one.
+ if (mesg.empty())
+ {
+ mesg = mDefaultNeedsApplyMesg;
+ }
+ // Create a notify box, telling the user about the unapplied tab.
+ LLString::format_map_t args;
+ args["[NEEDS_APPLY_MESSAGE]"] = mesg;
+ args["[WANT_APPLY_MESSAGE]"] = mWantApplyMesg;
+ gViewerWindow->alertXml("PanelGroupApply", args,
+ onNotifyCallback, (void*) this);
+ mShowingNotifyDialog = TRUE;
+
+ // We need to reselect the current tab, since it isn't finished.
+ if (mTabContainer)
+ {
+ // selectTabPanel is going to trigger another
+ // click event. We want to ignore it so that
+ // mRequestedTab is not updated.
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mCurrentTab );
+ mIgnoreTransition = FALSE;
+ }
+ // Returning FALSE will block a close action from finishing until
+ // we get a response back from the user.
+ return FALSE;
+ }
+ else
+ {
+ // The current panel didn't have anything it needed to apply.
+ if ( mRequestedTab )
+ {
+ transitionToTab();
+ }
+ // Returning TRUE will allow any close action to proceed.
+ return TRUE;
+ }
+}
+
+void LLPanelGroup::transitionToTab()
+{
+ // Tell the current panel that it is being deactivated.
+ if (mCurrentTab)
+ {
+ mCurrentTab->deactivate();
+ }
+
+ // If the requested panel exists, activate it.
+ if (mRequestedTab)
+ {
+ // This is now the current tab;
+ mCurrentTab = mRequestedTab;
+ mCurrentTab->activate();
+ }
+ else // NULL requested indicates a close action.
+ {
+ close();
+ }
+}
+
+// static
+void LLPanelGroup::onNotifyCallback(S32 option, void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ if (self)
+ {
+ self->handleNotifyCallback(option);
+ }
+}
+
+void LLPanelGroup::handleNotifyCallback(S32 option)
+{
+ mShowingNotifyDialog = FALSE;
+ switch (option)
+ {
+ case 0: // "Apply Changes"
+ // Try to apply changes, and switch to the requested tab.
+ if ( !apply() )
+ {
+ // There was a problem doing the apply.
+ // Skip switching tabs.
+ break;
+ }
+
+ // This panel's info successfully applied.
+ // Switch to the next panel.
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 1: // "Ignore Changes"
+ // Switch to the requested panel without applying changes
+ // (Changes may already have been applied in the previous block)
+ mCurrentTab->cancel();
+ mIgnoreTransition = TRUE;
+ mTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 2: // "Cancel"
+ default:
+ // Do nothing. The user is canceling the action.
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+// static
+void LLPanelGroup::onBtnOK(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ // If we are able to apply changes, then close.
+ if(self->apply())
+ {
+ self->close();
+ }
+}
+
+// static
+void LLPanelGroup::onBtnCancel(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->close();
+}
+
+// static
+void LLPanelGroup::onBtnApply(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->apply();
+}
+
+bool LLPanelGroup::apply()
+{
+ // Pass this along to the currently visible tab.
+ if (!mTabContainer) return false;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ LLString mesg;
+ if ( !panelp->needsApply(mesg) )
+ {
+ // We don't need to apply anything.
+ // We're done.
+ return true;
+ }
+
+ // Ignore the needs apply message.
+ // Try to do the actual apply.
+ LLString apply_mesg;
+ if ( panelp->apply( apply_mesg ) )
+ {
+ // Everything worked. We're done.
+ return true;
+ }
+
+ // There was a problem doing the actual apply.
+ // Inform the user.
+ if ( !apply_mesg.empty() )
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = apply_mesg;
+ gViewerWindow->alertXml("GenericAlert", args);
+ }
+
+ return false;
+}
+
+// static
+void LLPanelGroup::onBtnRefresh(void* user_data)
+{
+ LLPanelGroup* self = static_cast<LLPanelGroup*>(user_data);
+ self->refreshData();
+}
+
+// virtual
+void LLPanelGroup::draw()
+{
+ LLPanel::draw();
+
+ if (mRefreshTimer.hasExpired())
+ {
+ mRefreshTimer.stop();
+ childEnable("btn_refresh");
+ }
+}
+
+void LLPanelGroup::refreshData()
+{
+ gGroupMgr->clearGroupData(getID());
+ mCurrentTab->activate();
+
+ // 5 second timeout
+ childDisable("btn_refresh");
+ mRefreshTimer.start();
+ mRefreshTimer.setTimerExpirySec(5);
+}
+
+void LLPanelGroup::close()
+{
+ // Pass this to the parent, if it is a floater.
+ LLView* viewp = getParent();
+ if (viewp
+ && WIDGET_TYPE_FLOATER == viewp->getWidgetType())
+ {
+ // First, set the force close flag, since the floater
+ // will be asking us whether it can close.
+ mForceClose = TRUE;
+ // Tell the parent floater to close.
+ LLFloater* floaterp = (LLFloater*) viewp;
+ floaterp->close();
+ }
+}
+
+void LLPanelGroup::showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer)
+{
+ if (mCurrentTab->getName() != "notices_tab")
+ {
+ // We need to clean up that inventory offer.
+ if (inventory_offer)
+ {
+ inventory_offer_callback( 1 , inventory_offer);
+ }
+ return;
+ }
+
+ LLPanelGroupNotices* notices = static_cast<LLPanelGroupNotices*>(mCurrentTab);
+
+ notices->showNotice(subject,message,has_inventory,inventory_name,inventory_offer);
+}
diff --git a/indra/newview/llpanelgroup.h b/indra/newview/llpanelgroup.h
new file mode 100644
index 0000000000..c8ac001056
--- /dev/null
+++ b/indra/newview/llpanelgroup.h
@@ -0,0 +1,173 @@
+/**
+ * @file llpanelgroup.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUP_H
+#define LL_LLPANELGROUP_H
+
+#include "llgroupmgr.h"
+#include "llpanel.h"
+#include "lltimer.h"
+
+struct LLOfferInfo;
+
+const S32 UPDATE_MEMBERS_PER_FRAME = 500;
+
+// Forward declares
+class LLPanelGroupTab;
+class LLTabContainerCommon;
+class LLAgent;
+
+class LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroupTabObserver() {};
+ virtual ~LLPanelGroupTabObserver(){};
+ virtual void tabChanged() = 0;
+};
+
+class LLPanelGroup : public LLPanel,
+ public LLGroupMgrObserver,
+ public LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroup(const std::string& filename,
+ const std::string& name,
+ const LLUUID& group_id,
+ const std::string& initial_tab_selected = "");
+ virtual ~LLPanelGroup();
+
+ virtual BOOL postBuild();
+
+ static void onBtnOK(void*);
+ static void onBtnCancel(void*);
+ static void onBtnApply(void*);
+ static void onBtnRefresh(void*);
+ static void onClickTab(void*,bool);
+ void handleClickTab();
+
+ void setGroupID(const LLUUID& group_id);
+ void selectTab(std::string tab_name);
+
+ // Called when embedded in a floater during a close attempt.
+ BOOL canClose();
+
+ // Checks if the current tab needs to be applied, and tries to switch to the requested tab.
+ BOOL attemptTransition();
+
+ // Switches to the requested tab (will close() if requested is NULL)
+ void transitionToTab();
+
+ void updateTabVisibility();
+
+ // Used by attemptTransition to query the user's response to a tab that needs to apply.
+ static void onNotifyCallback(S32 option, void* user_data);
+ void handleNotifyCallback(S32 option);
+
+ bool apply();
+ void refreshData();
+ void close();
+ void draw();
+
+ // Group manager observer trigger.
+ virtual void changed(LLGroupChange gc);
+
+ // PanelGroupTab observer trigger
+ virtual void tabChanged();
+
+ void setAllowEdit(BOOL v) { mAllowEdit = v; }
+
+ void showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer);
+protected:
+ LLPanelGroupTab* mCurrentTab;
+ LLPanelGroupTab* mRequestedTab;
+ LLTabContainerCommon* mTabContainer;
+ BOOL mIgnoreTransition;
+
+ LLButton* mApplyBtn;
+
+ LLTimer mRefreshTimer;
+
+ BOOL mForceClose;
+
+ std::string mInitialTab;
+ std::string mFilename;
+
+ LLString mDefaultNeedsApplyMesg;
+ LLString mWantApplyMesg;
+
+ BOOL mAllowEdit;
+ BOOL mShowingNotifyDialog;
+};
+
+class LLPanelGroupTab : public LLPanel
+{
+public:
+ LLPanelGroupTab(const std::string& name, const LLUUID& group_id)
+ : LLPanel(name), mGroupID(group_id), mAllowEdit(TRUE), mHasModal(FALSE) { }
+ virtual ~LLPanelGroupTab();
+
+ // Factory that returns a new LLPanelGroupFoo tab.
+ static void* createTab(void* data);
+
+ // Triggered when the tab becomes active.
+ virtual void activate() { }
+
+ // Triggered when the tab becomes inactive.
+ virtual void deactivate() { }
+
+ // Asks if something needs to be applied.
+ // If returning true, this function should modify the message to the user.
+ virtual bool needsApply(LLString& mesg) { return false; }
+
+ // Asks if there is currently a modal dialog being shown.
+ virtual BOOL hasModal() { return mHasModal; }
+
+ // Request to apply current data.
+ // If returning fail, this function should modify the message to the user.
+ virtual bool apply(LLString& mesg) { return true; }
+
+ // Request a cancel of changes
+ virtual void cancel() { }
+
+ // Triggered when group information changes in the group manager.
+ virtual void update(LLGroupChange gc) { }
+
+ // This is the text to be displayed when a help button is pressed.
+ virtual LLString getHelpText() const { return mHelpText; }
+
+ // Display anything returned by getHelpText
+ static void onClickHelp(void* data);
+ void handleClickHelp();
+
+ // This just connects the help button callback.
+ virtual BOOL postBuild();
+
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ void setAllowEdit(BOOL v) { mAllowEdit = v; }
+
+ void addObserver(LLPanelGroupTabObserver *obs);
+ void removeObserver(LLPanelGroupTabObserver *obs);
+ void notifyObservers();
+
+protected:
+ LLUUID mGroupID;
+ LLTabContainerCommon* mTabContainer;
+ LLString mHelpText;
+
+ BOOL mAllowEdit;
+ BOOL mHasModal;
+
+ typedef std::set<LLPanelGroupTabObserver*> observer_list_t;
+ observer_list_t mObservers;
+};
+
+#endif // LL_LLPANELGROUP_H
diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp
new file mode 100644
index 0000000000..c996354044
--- /dev/null
+++ b/indra/newview/llpanelgroupgeneral.cpp
@@ -0,0 +1,771 @@
+/**
+ * @file llpanelgroupgeneral.cpp
+ * @brief General information about a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupgeneral.h"
+
+#include "llvieweruictrlfactory.h"
+#include "llagent.h"
+#include "roles_constants.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinfo.h"
+
+// UI elements
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldbstrings.h"
+#include "lllineeditor.h"
+#include "llnamebox.h"
+#include "llnamelistctrl.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "lltexturectrl.h"
+#include "llviewermessage.h"
+#include "llviewerwindow.h"
+
+// static
+void* LLPanelGroupGeneral::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupGeneral("panel group general", *group_id);
+}
+
+
+LLPanelGroupGeneral::LLPanelGroupGeneral(const std::string& name,
+ const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mPendingMemberUpdate(FALSE),
+ mChanged(FALSE),
+ mFirstUse(TRUE),
+ mGroupNameEditor(NULL),
+ mGroupName(NULL),
+ mFounderName(NULL),
+ mInsignia(NULL),
+ mEditCharter(NULL),
+ mEditName(NULL),
+ mBtnJoinGroup(NULL),
+ mListVisibleMembers(NULL),
+ mCtrlShowInGroupList(NULL),
+ mCtrlPublishOnWeb(NULL),
+ mCtrlMature(NULL),
+ mCtrlOpenEnrollment(NULL),
+ mCtrlEnrollmentFee(NULL),
+ mSpinEnrollmentFee(NULL),
+ mCtrlReceiveNotices(NULL),
+ mActiveTitleLabel(NULL),
+ mComboActiveTitle(NULL)
+{
+
+}
+
+LLPanelGroupGeneral::~LLPanelGroupGeneral()
+{
+}
+
+BOOL LLPanelGroupGeneral::postBuild()
+{
+ llinfos << "LLPanelGroupGeneral::postBuild()" << llendl;
+
+ bool recurse = true;
+
+ // General info
+ mGroupNameEditor = (LLLineEditor*) getChildByName("group_name_editor", recurse);
+ mGroupName = (LLTextBox*) getChildByName("group_name", recurse);
+
+ mInsignia = (LLTextureCtrl*) getChildByName("insignia", recurse);
+ if (mInsignia)
+ {
+ mInsignia->setCommitCallback(onCommitAny);
+ mInsignia->setCallbackUserData(this);
+ mDefaultIconID = mInsignia->getImageAssetID();
+ }
+
+ mEditCharter = (LLTextEditor*) getChildByName("charter", recurse);
+ if(mEditCharter)
+ {
+ mEditCharter->setCommitCallback(onCommitAny);
+ mEditCharter->setFocusReceivedCallback(onCommitAny);
+ mEditCharter->setFocusChangedCallback(onCommitAny);
+ mEditCharter->setCallbackUserData(this);
+ }
+
+ mBtnJoinGroup = (LLButton*) getChildByName("join_button", recurse);
+ if ( mBtnJoinGroup )
+ {
+ mBtnJoinGroup->setClickedCallback(onClickJoin);
+ mBtnJoinGroup->setCallbackUserData(this);
+ }
+
+ mBtnInfo = (LLButton*) getChildByName("info_button", recurse);
+ if ( mBtnInfo )
+ {
+ mBtnInfo->setClickedCallback(onClickInfo);
+ mBtnInfo->setCallbackUserData(this);
+ }
+
+ LLTextBox* founder = (LLTextBox*) getChildByName("founder_name");
+ if (founder)
+ {
+ mFounderName = new LLNameBox(founder->getName(),founder->getRect(),LLUUID::null,FALSE,founder->getFont(),founder->getMouseOpaque());
+ removeChild(founder);
+ addChild(mFounderName);
+ }
+
+ mListVisibleMembers = (LLNameListCtrl*) getChildByName("visible_members", recurse);
+ if (mListVisibleMembers)
+ {
+ mListVisibleMembers->setDoubleClickCallback(openProfile);
+ mListVisibleMembers->setCallbackUserData(this);
+ }
+
+ // Options
+ mCtrlShowInGroupList = (LLCheckBoxCtrl*) getChildByName("show_in_group_list", recurse);
+ if (mCtrlShowInGroupList)
+ {
+ mCtrlShowInGroupList->setCommitCallback(onCommitAny);
+ mCtrlShowInGroupList->setCallbackUserData(this);
+ }
+
+ mCtrlPublishOnWeb = (LLCheckBoxCtrl*) getChildByName("publish_on_web", recurse);
+ if (mCtrlPublishOnWeb)
+ {
+ mCtrlPublishOnWeb->setCommitCallback(onCommitAny);
+ mCtrlPublishOnWeb->setCallbackUserData(this);
+ }
+
+ mCtrlMature = (LLCheckBoxCtrl*) getChildByName("mature", recurse);
+ if (mCtrlMature)
+ {
+ mCtrlMature->setCommitCallback(onCommitAny);
+ mCtrlMature->setCallbackUserData(this);
+ }
+
+ mCtrlOpenEnrollment = (LLCheckBoxCtrl*) getChildByName("open_enrollement", recurse);
+ if (mCtrlOpenEnrollment)
+ {
+ mCtrlOpenEnrollment->setCommitCallback(onCommitAny);
+ mCtrlOpenEnrollment->setCallbackUserData(this);
+ }
+
+ mCtrlEnrollmentFee = (LLCheckBoxCtrl*) getChildByName("check_enrollment_fee", recurse);
+ if (mCtrlEnrollmentFee)
+ {
+ mCtrlEnrollmentFee->setCommitCallback(onCommitEnrollment);
+ mCtrlEnrollmentFee->setCallbackUserData(this);
+ }
+
+ mSpinEnrollmentFee = (LLSpinCtrl*) getChildByName("spin_enrollment_fee", recurse);
+ if (mSpinEnrollmentFee)
+ {
+ mSpinEnrollmentFee->setCommitCallback(onCommitAny);
+ mSpinEnrollmentFee->setCallbackUserData(this);
+ }
+
+ BOOL accept_notices = FALSE;
+ LLGroupData data;
+ if(gAgent.getGroupData(mGroupID,data))
+ {
+ accept_notices = data.mAcceptNotices;
+ }
+ mCtrlReceiveNotices = (LLCheckBoxCtrl*) getChildByName("receive_notices", recurse);
+ if (mCtrlReceiveNotices)
+ {
+ mCtrlReceiveNotices->setCommitCallback(onReceiveNotices);
+ mCtrlReceiveNotices->setCallbackUserData(this);
+ mCtrlReceiveNotices->set(accept_notices);
+ mCtrlReceiveNotices->setEnabled(data.mID.notNull());
+ }
+
+ mActiveTitleLabel = (LLTextBox*) getChildByName("active_title_label", recurse);
+
+ mComboActiveTitle = (LLComboBox*) getChildByName("active_title", recurse);
+ if (mComboActiveTitle)
+ {
+ mComboActiveTitle->setCommitCallback(onCommitTitle);
+ mComboActiveTitle->setCallbackUserData(this);
+ }
+
+ // Extra data
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("incomplete_member_data_str");
+ if (txt)
+ {
+ mIncompleteMemberDataStr = txt->getText();
+ removeChild(txt);
+ }
+
+ txt = (LLTextBox*)getChildByName("confirm_group_create_str");
+ if (txt)
+ {
+ mConfirmGroupCreateStr = txt->getText();
+ removeChild(txt);
+ }
+
+ // If the group_id is null, then we are creating a new group
+ if (mGroupID.isNull())
+ {
+ mGroupNameEditor->setEnabled(TRUE);
+ mEditCharter->setEnabled(TRUE);
+
+ mCtrlShowInGroupList->setEnabled(TRUE);
+ mCtrlPublishOnWeb->setEnabled(TRUE);
+ mCtrlMature->setEnabled(TRUE);
+ mCtrlOpenEnrollment->setEnabled(TRUE);
+ mCtrlEnrollmentFee->setEnabled(TRUE);
+ mSpinEnrollmentFee->setEnabled(TRUE);
+
+ mBtnJoinGroup->setVisible(FALSE);
+ mBtnInfo->setVisible(FALSE);
+ mGroupName->setVisible(FALSE);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+// static
+void LLPanelGroupGeneral::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ self->mChanged = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupGeneral::onCommitEnrollment(LLUICtrl* ctrl, void* data)
+{
+ onCommitAny(ctrl, data);
+
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ // Make sure both enrollment related widgets are there.
+ if (!self->mCtrlEnrollmentFee || !self->mSpinEnrollmentFee)
+ {
+ return;
+ }
+
+ // Make sure the agent can change enrollment info.
+ if (!gAgent.hasPowerInGroup(self->mGroupID,GP_MEMBER_OPTIONS)
+ || !self->mAllowEdit)
+ {
+ return;
+ }
+
+ if (self->mCtrlEnrollmentFee->get())
+ {
+ self->mSpinEnrollmentFee->setEnabled(TRUE);
+ }
+ else
+ {
+ self->mSpinEnrollmentFee->setEnabled(FALSE);
+ self->mSpinEnrollmentFee->set(0);
+ }
+}
+
+// static
+void LLPanelGroupGeneral::onCommitTitle(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ if (self->mGroupID.isNull() || !self->mAllowEdit) return;
+ gGroupMgr->sendGroupTitleUpdate(self->mGroupID,self->mComboActiveTitle->getCurrentID());
+ self->update(GC_TITLES);
+}
+
+// static
+void LLPanelGroupGeneral::onClickInfo(void *userdata)
+{
+ LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata;
+
+ if ( !self ) return;
+
+ lldebugs << "open group info: " << self->mGroupID << llendl;
+
+ LLFloaterGroupInfo::showFromUUID(self->mGroupID);
+}
+
+// static
+void LLPanelGroupGeneral::onClickJoin(void *userdata)
+{
+ LLPanelGroupGeneral *self = (LLPanelGroupGeneral *)userdata;
+
+ if ( !self ) return;
+
+ lldebugs << "joining group: " << self->mGroupID << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(self->mGroupID);
+
+ S32 cost = gdatap->mMembershipFee;
+ LLString::format_map_t args;
+ args["[COST]"] = llformat("%d", cost);
+
+ if (can_afford_transaction(cost))
+ {
+ gViewerWindow->alertXml("JoinGroupCanAfford", args,
+ LLPanelGroupGeneral::joinDlgCB,
+ self);
+ }
+ else
+ {
+ gViewerWindow->alertXml("JoinGroupCannotAfford", args);
+ }
+}
+
+// static
+void LLPanelGroupGeneral::joinDlgCB(S32 which, void *userdata)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*) userdata;
+
+ if (which == 1 || !self)
+ {
+ // user clicked cancel
+ return;
+ }
+
+ gGroupMgr->sendGroupMemberJoin(self->mGroupID);
+}
+
+// static
+void LLPanelGroupGeneral::onReceiveNotices(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+ LLCheckBoxCtrl* check = (LLCheckBoxCtrl*)ctrl;
+
+ if(!self) return;
+ gAgent.setGroupAcceptNotices(self->mGroupID, check->get());
+}
+
+// static
+void LLPanelGroupGeneral::openProfile(void* data)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)data;
+
+ if (self && self->mListVisibleMembers)
+ {
+ LLScrollListItem* selected = self->mListVisibleMembers->getFirstSelected();
+ if (selected)
+ {
+ LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() );
+ }
+ }
+}
+
+bool LLPanelGroupGeneral::needsApply(LLString& mesg)
+{
+ llinfos << "LLPanelGroupGeneral::needsApply(LLString& mesg) " << mChanged << llendl;
+
+ mesg = "General group information has changed.";
+ return mChanged || mGroupID.isNull();
+}
+
+void LLPanelGroupGeneral::activate()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (mGroupID.notNull()
+ && (!gdatap || mFirstUse))
+ {
+ gGroupMgr->sendGroupTitlesRequest(mGroupID);
+ gGroupMgr->sendGroupPropertiesRequest(mGroupID);
+
+
+ if (!gdatap || !gdatap->isMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ mFirstUse = FALSE;
+ }
+ mChanged = FALSE;
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupGeneral::draw()
+{
+ LLPanelGroupTab::draw();
+
+ if (mPendingMemberUpdate)
+ {
+ updateMembers();
+ }
+}
+
+bool LLPanelGroupGeneral::apply(LLString& mesg)
+{
+ if (!mAllowEdit)
+ {
+ llwarns << "LLPanelGroupGeneral::apply() called with false mAllowEdit"
+ << llendl;
+ return true;
+ }
+
+ llinfos << "LLPanelGroupGeneral::apply" << llendl;
+ if (mGroupID.isNull())
+ {
+ // Validate the group name length.
+ S32 group_name_len = mGroupNameEditor->getText().size();
+ if ( group_name_len < DB_GROUP_NAME_MIN_LEN
+ || group_name_len > DB_GROUP_NAME_STR_LEN)
+ {
+ std::ostringstream temp_error;
+ temp_error << "A group name must be between " << DB_GROUP_NAME_MIN_LEN
+ << " and " << DB_GROUP_NAME_STR_LEN << " characters.";
+ mesg = temp_error.str();
+ return false;
+ }
+
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = mConfirmGroupCreateStr;
+ gViewerWindow->alertXml("GenericAlertYesCancel", args,
+ createGroupCallback,this);
+
+ return false;
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap)
+ {
+ mesg = "No group data found for group ";
+ mesg.append(mGroupID.getString());
+ return false;
+ }
+
+ bool can_change_ident = false;
+ bool can_change_member_opts = false;
+ can_change_ident = gAgent.hasPowerInGroup(mGroupID,GP_GROUP_CHANGE_IDENTITY);
+ can_change_member_opts = gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS);
+
+ if (can_change_ident)
+ {
+ if (mCtrlPublishOnWeb) gdatap->mAllowPublish = mCtrlPublishOnWeb->get();
+ if (mEditCharter) gdatap->mCharter = mEditCharter->getText();
+ if (mInsignia) gdatap->mInsigniaID = mInsignia->getImageAssetID();
+ if (mCtrlMature) gdatap->mMaturePublish = mCtrlMature->get();
+ if (mCtrlShowInGroupList) gdatap->mShowInList = mCtrlShowInGroupList->get();
+ }
+
+ if (can_change_member_opts)
+ {
+ if (mCtrlOpenEnrollment) gdatap->mOpenEnrollment = mCtrlOpenEnrollment->get();
+ if (mCtrlEnrollmentFee && mSpinEnrollmentFee)
+ {
+ gdatap->mMembershipFee = (mCtrlEnrollmentFee->get()) ?
+ (S32) mSpinEnrollmentFee->get() : 0;
+ }
+ }
+
+ if (can_change_ident || can_change_member_opts)
+ {
+ gGroupMgr->sendUpdateGroupInfo(mGroupID);
+ }
+
+ if (mCtrlReceiveNotices) gAgent.setGroupAcceptNotices(mGroupID, mCtrlReceiveNotices->get());
+
+ mChanged = FALSE;
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupGeneral::cancel()
+{
+ mChanged = FALSE;
+
+ //cancel out all of the click changes to, although since we are
+ //shifting tabs or closing the floater, this need not be done...yet
+ notifyObservers();
+}
+
+// static
+void LLPanelGroupGeneral::createGroupCallback(S32 option, void* userdata)
+{
+ LLPanelGroupGeneral* self = (LLPanelGroupGeneral*)userdata;
+ if (!self) return;
+
+ switch(option)
+ {
+ case 0:
+ {
+ // Yay! We are making a new group!
+ U32 enrollment_fee = (self->mCtrlEnrollmentFee->get() ?
+ (U32) self->mSpinEnrollmentFee->get() : 0);
+
+ gGroupMgr->sendCreateGroupRequest(self->mGroupNameEditor->getText(),
+ self->mEditCharter->getText(),
+ self->mCtrlShowInGroupList->get(),
+ self->mInsignia->getImageAssetID(),
+ enrollment_fee,
+ self->mCtrlOpenEnrollment->get(),
+ self->mCtrlPublishOnWeb->get(),
+ self->mCtrlMature->get());
+
+ }
+ break;
+ case 1:
+ default:
+ break;
+ }
+}
+
+static F32 sSDTime = 0.0f;
+static F32 sElementTime = 0.0f;
+static F32 sAllTime = 0.0f;
+
+// virtual
+void LLPanelGroupGeneral::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLGroupData agent_gdatap;
+ bool is_member = false;
+ if (gAgent.getGroupData(mGroupID,agent_gdatap)) is_member = true;
+
+ if (mComboActiveTitle)
+ {
+ mComboActiveTitle->setVisible(is_member);
+ mComboActiveTitle->setEnabled(mAllowEdit);
+
+ if ( mActiveTitleLabel) mActiveTitleLabel->setVisible(is_member);
+
+ if (is_member)
+ {
+ LLUUID current_title_role;
+
+ mComboActiveTitle->clear();
+ mComboActiveTitle->removeall();
+ bool has_selected_title = false;
+
+ if (1 == gdatap->mTitles.size())
+ {
+ // Only the everyone title. Don't bother letting them try changing this.
+ mComboActiveTitle->setEnabled(FALSE);
+ }
+ else
+ {
+ mComboActiveTitle->setEnabled(TRUE);
+ }
+
+ std::vector<LLGroupTitle>::const_iterator citer = gdatap->mTitles.begin();
+ std::vector<LLGroupTitle>::const_iterator end = gdatap->mTitles.end();
+
+ for ( ; citer != end; ++citer)
+ {
+ mComboActiveTitle->add(citer->mTitle,citer->mRoleID, (citer->mSelected ? ADD_TOP : ADD_BOTTOM));
+ if (citer->mSelected)
+ {
+ mComboActiveTitle->setCurrentByID(citer->mRoleID);
+ has_selected_title = true;
+ }
+ }
+
+ if (!has_selected_title)
+ {
+ mComboActiveTitle->setCurrentByID(LLUUID::null);
+ }
+ }
+ }
+
+ // If this was just a titles update, we are done.
+ if (gc == GC_TITLES) return;
+
+ bool can_change_ident = false;
+ bool can_change_member_opts = false;
+ can_change_ident = gAgent.hasPowerInGroup(mGroupID,GP_GROUP_CHANGE_IDENTITY);
+ can_change_member_opts = gAgent.hasPowerInGroup(mGroupID,GP_MEMBER_OPTIONS);
+
+ if (mCtrlShowInGroupList)
+ {
+ mCtrlShowInGroupList->set(gdatap->mShowInList);
+ mCtrlShowInGroupList->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlPublishOnWeb)
+ {
+ mCtrlPublishOnWeb->set(gdatap->mAllowPublish);
+ mCtrlPublishOnWeb->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlMature)
+ {
+ mCtrlMature->set(gdatap->mMaturePublish);
+ mCtrlMature->setEnabled(mAllowEdit && can_change_ident);
+ }
+ if (mCtrlOpenEnrollment)
+ {
+ mCtrlOpenEnrollment->set(gdatap->mOpenEnrollment);
+ mCtrlOpenEnrollment->setEnabled(mAllowEdit && can_change_member_opts);
+ }
+ if (mCtrlEnrollmentFee)
+ {
+ mCtrlEnrollmentFee->set(gdatap->mMembershipFee > 0);
+ mCtrlEnrollmentFee->setEnabled(mAllowEdit && can_change_member_opts);
+ }
+
+ if (mSpinEnrollmentFee)
+ {
+ S32 fee = gdatap->mMembershipFee;
+ mSpinEnrollmentFee->set((F32)fee);
+ mSpinEnrollmentFee->setEnabled( mAllowEdit
+ && (fee > 0) && can_change_member_opts);
+ }
+ if ( mBtnJoinGroup )
+ {
+ char fee_buff[20];
+ bool visible;
+
+ visible = !is_member && gdatap->mOpenEnrollment;
+ mBtnJoinGroup->setVisible(visible);
+
+ if ( visible )
+ {
+ sprintf(fee_buff, "Join (L$%d)", gdatap->mMembershipFee);
+ mBtnJoinGroup->setLabelSelected(std::string(fee_buff));
+ mBtnJoinGroup->setLabelUnselected(std::string(fee_buff));
+ }
+ }
+ if ( mBtnInfo )
+ {
+ mBtnInfo->setVisible(is_member && !mAllowEdit);
+ }
+
+ if (mCtrlReceiveNotices)
+ {
+ mCtrlReceiveNotices->setVisible(is_member);
+ if (is_member)
+ {
+ mCtrlReceiveNotices->set(agent_gdatap.mAcceptNotices);
+ mCtrlReceiveNotices->setEnabled(mAllowEdit);
+ }
+ }
+
+
+ if (mInsignia) mInsignia->setEnabled(mAllowEdit && can_change_ident);
+ if (mEditCharter) mEditCharter->setEnabled(mAllowEdit && can_change_ident);
+
+ if (mGroupName) mGroupName->setText(gdatap->mName);
+ if (mGroupNameEditor) mGroupNameEditor->setVisible(FALSE);
+ if (mFounderName) mFounderName->setNameID(gdatap->mFounderID,FALSE);
+ if (mInsignia)
+ {
+ if (gdatap->mInsigniaID.notNull())
+ {
+ mInsignia->setImageAssetID(gdatap->mInsigniaID);
+ }
+ else
+ {
+
+ mInsignia->setImageAssetID(mDefaultIconID);
+ }
+ }
+ if (mEditCharter) mEditCharter->setText(gdatap->mCharter);
+
+ if (mListVisibleMembers)
+ {
+ mListVisibleMembers->deleteAllItems();
+
+ if (gdatap->isMemberDataComplete())
+ {
+ mMemberProgress = gdatap->mMembers.begin();
+ mPendingMemberUpdate = TRUE;
+
+ sSDTime = 0.0f;
+ sElementTime = 0.0f;
+ sAllTime = 0.0f;
+ }
+ else
+ {
+ LLScrollListItem* row = new LLScrollListItem( TRUE, NULL, LLUUID::null );
+ std::stringstream pending;
+ pending << "Retrieving member list (" << gdatap->mMembers.size() << "\\" << gdatap->mMemberCount << ")";
+ row->addColumn(pending.str(), LLFontGL::sSansSerif);
+ mListVisibleMembers->setEnabled(FALSE);
+ mListVisibleMembers->addItem(row);
+ }
+ }
+}
+
+void LLPanelGroupGeneral::updateMembers()
+{
+ mPendingMemberUpdate = FALSE;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!mListVisibleMembers || !gdatap
+ || !gdatap->isMemberDataComplete())
+ {
+ return;
+ }
+
+ static LLTimer all_timer;
+ static LLTimer sd_timer;
+ static LLTimer element_timer;
+
+ all_timer.reset();
+ S32 i = 0;
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+
+ for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME;
+ ++mMemberProgress, ++i)
+ {
+ //llinfos << "Adding " << iter->first << ", " << iter->second->getTitle() << llendl;
+ LLGroupMemberData* member = mMemberProgress->second;
+ if (!member)
+ {
+ continue;
+ }
+ // Owners show up in bold.
+ LLString style = "NORMAL";
+ if ( member->isOwner() )
+ {
+ style = "BOLD";
+ }
+
+ sd_timer.reset();
+ LLSD row;
+ row["id"] = member->getID();
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["font-style"] = style;
+ // value is filled in by name list control
+
+ row["columns"][1]["column"] = "title";
+ row["columns"][1]["value"] = member->getTitle();
+ row["columns"][1]["font-style"] = style;
+
+ row["columns"][2]["column"] = "online";
+ row["columns"][2]["value"] = member->getOnlineStatus();
+ row["columns"][2]["font-style"] = style;
+
+ sSDTime += sd_timer.getElapsedTimeF32();
+
+ element_timer.reset();
+ mListVisibleMembers->addElement(row);//, ADD_SORTED);
+ sElementTime += element_timer.getElapsedTimeF32();
+ }
+ sAllTime += all_timer.getElapsedTimeF32();
+
+ llinfos << "Updated " << i << " of " << UPDATE_MEMBERS_PER_FRAME << "members in the list." << llendl;
+ if (mMemberProgress == end)
+ {
+ llinfos << " member list completed." << llendl;
+ mListVisibleMembers->setEnabled(TRUE);
+
+ llinfos << "All Time: " << sAllTime << llendl;
+ llinfos << "SD Time: " << sSDTime << llendl;
+ llinfos << "Element Time: " << sElementTime << llendl;
+ }
+ else
+ {
+ mPendingMemberUpdate = TRUE;
+ mListVisibleMembers->setEnabled(FALSE);
+ }
+}
diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h
new file mode 100644
index 0000000000..d62cab0133
--- /dev/null
+++ b/indra/newview/llpanelgroupgeneral.h
@@ -0,0 +1,91 @@
+/**
+ * @file llpanelgroupgeneral.h
+ * @brief General information about a group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPGENERAL_H
+#define LL_LLPANELGROUPGENERAL_H
+
+#include "llpanelgroup.h"
+
+class LLLineEditor;
+class LLTextBox;
+class LLTextureCtrl;
+class LLTextEditor;
+class LLButton;
+class LLNameListCtrl;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLNameBox;
+class LLSpinCtrl;
+
+class LLPanelGroupGeneral : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupGeneral(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupGeneral();
+
+ // LLPanelGroupTab
+ static void* createTab(void* data);
+ virtual void activate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ static void createGroupCallback(S32 option, void* user_data);
+
+ virtual void update(LLGroupChange gc);
+
+ virtual BOOL postBuild();
+
+ virtual void draw();
+
+private:
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+ static void onCommitTitle(LLUICtrl* ctrl, void* data);
+ static void onCommitEnrollment(LLUICtrl* ctrl, void* data);
+ static void onClickJoin(void* userdata);
+ static void onClickInfo(void* userdata);
+ static void onReceiveNotices(LLUICtrl* ctrl, void* data);
+ static void openProfile(void* data);
+
+ static void joinDlgCB(S32 which, void *userdata);
+
+ void updateMembers();
+
+ BOOL mPendingMemberUpdate;
+ BOOL mChanged;
+ BOOL mFirstUse;
+ std::string mIncompleteMemberDataStr;
+ std::string mConfirmGroupCreateStr;
+ LLUUID mDefaultIconID;
+
+ // Group information
+ LLLineEditor *mGroupNameEditor;
+ LLTextBox *mGroupName;
+ LLNameBox *mFounderName;
+ LLTextureCtrl *mInsignia;
+ LLTextEditor *mEditCharter;
+ LLLineEditor *mEditName;
+ LLButton *mBtnJoinGroup;
+ LLButton *mBtnInfo;
+
+ LLNameListCtrl *mListVisibleMembers;
+
+ // Options
+ LLCheckBoxCtrl *mCtrlShowInGroupList;
+ LLCheckBoxCtrl *mCtrlPublishOnWeb;
+ LLCheckBoxCtrl *mCtrlMature;
+ LLCheckBoxCtrl *mCtrlOpenEnrollment;
+ LLCheckBoxCtrl *mCtrlEnrollmentFee;
+ LLSpinCtrl *mSpinEnrollmentFee;
+ LLCheckBoxCtrl *mCtrlReceiveNotices;
+ LLTextBox *mActiveTitleLabel;
+ LLComboBox *mComboActiveTitle;
+
+ LLGroupMgrGroupData::member_iter mMemberProgress;
+};
+
+#endif
diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp
new file mode 100644
index 0000000000..231551a756
--- /dev/null
+++ b/indra/newview/llpanelgroupinvite.cpp
@@ -0,0 +1,392 @@
+/**
+ * @file llpanelgroupinvite.cpp
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupinvite.h"
+
+#include "llagent.h"
+#include "llfloateravatarpicker.h"
+#include "llbutton.h"
+#include "llcombobox.h"
+#include "llgroupmgr.h"
+#include "llnamelistctrl.h"
+#include "llspinctrl.h"
+#include "llvieweruictrlfactory.h"
+
+class LLPanelGroupInvite::impl
+{
+public:
+ impl(const LLUUID& group_id);
+ ~impl();
+
+ void addUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids);
+ void submitInvitations();
+ void addRoleNames(LLGroupMgrGroupData* gdatap);
+ void handleRemove();
+ void handleSelection();
+
+ static void callbackClickCancel(void* userdata);
+ static void callbackClickOK(void* userdata);
+ static void callbackClickAdd(void* userdata);
+ static void callbackClickRemove(void* userdata);
+ static void callbackSelect(LLUICtrl* ctrl, void* userdata);
+ static void callbackAddUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids,
+ void* user_data);
+
+public:
+ LLUUID mGroupID;
+
+ LLNameListCtrl *mInvitees;
+ LLComboBox *mRoleNames;
+ LLButton *mRemoveButton;
+
+ void (*mCloseCallback)(void* data);
+
+ void* mCloseCallbackUserData;
+};
+
+
+LLPanelGroupInvite::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+
+ mInvitees = NULL;
+ mRoleNames = NULL;
+ mRemoveButton = NULL;
+
+ mCloseCallback = NULL;
+ mCloseCallbackUserData = NULL;
+}
+
+LLPanelGroupInvite::impl::~impl()
+{
+}
+
+void LLPanelGroupInvite::impl::addUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& agent_ids)
+{
+ std::string name;
+ LLUUID id;
+
+ for (S32 i = 0; i < (S32)names.size(); i++)
+ {
+ name = names[i];
+ id = agent_ids[i];
+
+ // Make sure this agent isn't already in the list.
+ bool already_in_list = false;
+ std::vector<LLScrollListItem*> items = mInvitees->getAllData();
+ for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ if (item->getUUID() == id)
+ {
+ already_in_list = true;
+ break;
+ }
+ }
+ if (already_in_list)
+ {
+ continue;
+ }
+
+ //add the name to the names list
+ const BOOL enabled = TRUE;
+ LLScrollListItem* row = new LLScrollListItem(
+ enabled, NULL, id);
+ row->addColumn(name.c_str(), LLFontGL::sSansSerif);
+ mInvitees->addItem(row);
+ }
+}
+
+void LLPanelGroupInvite::impl::submitInvitations()
+{
+ std::map<LLUUID, LLUUID> role_member_pairs;
+
+ // Default to everyone role.
+ LLUUID role_id = LLUUID::null;
+
+ if (mRoleNames)
+ {
+ role_id = mRoleNames->getCurrentID();
+ }
+
+ //loop over the users
+ std::vector<LLScrollListItem*> items = mInvitees->getAllData();
+ for (std::vector<LLScrollListItem*>::iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLScrollListItem* item = *iter;
+ role_member_pairs[item->getUUID()] = role_id;
+ }
+
+ gGroupMgr->sendGroupMemberInvites(mGroupID, role_member_pairs);
+
+ //then close
+ (*mCloseCallback)(mCloseCallbackUserData);
+}
+
+void LLPanelGroupInvite::impl::addRoleNames(LLGroupMgrGroupData* gdatap)
+{
+ LLGroupMgrGroupData::member_iter agent_iter =
+ gdatap->mMembers.find(gAgent.getID());
+
+ //get the member data for the agent if it exists
+ if ( agent_iter != gdatap->mMembers.end() )
+ {
+ LLGroupMemberData* member_data = (*agent_iter).second;
+
+ //loop over the agent's roles in the group
+ //then add those roles to the list of roles that the agent
+ //can invite people to be
+ if ( member_data && mRoleNames)
+ {
+ //if the user is the owner then we add
+ //all of the roles in the group
+ //else if they have the add to roles power
+ //we add every role but owner,
+ //else if they have the limited add to roles power
+ //we add every role the user is in
+ //else we just add to everyone
+ bool is_owner = member_data->isInRole(gdatap->mOwnerRole);
+ bool can_assign_any = gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_ASSIGN_MEMBER);
+ bool can_assign_limited = gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_ASSIGN_MEMBER_LIMITED);
+
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ //populate the role list
+ for ( ; rit != end; ++rit)
+ {
+ LLUUID role_id = (*rit).first;
+ LLRoleData rd;
+ if ( gdatap->getRoleData(role_id,rd) )
+ {
+ // Owners can add any role.
+ if ( is_owner
+ // Even 'can_assign_any' can't add owner role.
+ || (can_assign_any && role_id != gdatap->mOwnerRole)
+ // Add all roles user is in
+ || (can_assign_limited && member_data->isInRole(role_id))
+ // Everyone role.
+ || role_id == LLUUID::null )
+ {
+ mRoleNames->add(rd.mRoleName,
+ role_id,
+ ADD_BOTTOM);
+ }
+ }
+ }
+ }//end if member data is not null
+ }//end if agent is in the group
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackClickAdd(void* userdata)
+{
+ LLPanelGroupInvite* panelp = (LLPanelGroupInvite*) userdata;
+
+ if ( panelp )
+ {
+ //Right now this is hard coded with some knowledge that it is part
+ //of a floater since the avatar picker needs to be added as a dependent
+ //floater to the parent floater.
+ //Soon the avatar picker will be embedded into this panel
+ //instead of being it's own separate floater. But that is next week.
+ //This will do for now. -jwolk May 10, 2006
+ LLFloater* parentp;
+
+ parentp = gFloaterView->getParentFloater(panelp);
+ parentp->addDependentFloater(LLFloaterAvatarPicker::show(callbackAddUsers,
+ panelp->mImplementation,
+ TRUE));
+ }
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackClickRemove(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp ) selfp->handleRemove();
+}
+
+void LLPanelGroupInvite::impl::handleRemove()
+{
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection =
+ mInvitees->getAllSelected();
+ if (selection.empty()) return;
+
+ // Remove all selected invitees.
+ mInvitees->deleteSelectedItems();
+ mRemoveButton->setEnabled(FALSE);
+}
+
+// static
+void LLPanelGroupInvite::impl::callbackSelect(
+ LLUICtrl* ctrl, void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+ if ( selfp ) selfp->handleSelection();
+}
+
+void LLPanelGroupInvite::impl::handleSelection()
+{
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection =
+ mInvitees->getAllSelected();
+ if (selection.empty())
+ {
+ mRemoveButton->setEnabled(FALSE);
+ }
+ else
+ {
+ mRemoveButton->setEnabled(TRUE);
+ }
+}
+
+void LLPanelGroupInvite::impl::callbackClickCancel(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp )
+ {
+ (*(selfp->mCloseCallback))(selfp->mCloseCallbackUserData);
+ }
+}
+
+void LLPanelGroupInvite::impl::callbackClickOK(void* userdata)
+{
+ impl* selfp = (impl*) userdata;
+
+ if ( selfp ) selfp->submitInvitations();
+}
+
+//static
+void LLPanelGroupInvite::impl::callbackAddUsers(const std::vector<std::string>& names,
+ const std::vector<LLUUID>& ids,
+ void* user_data)
+{
+ impl* selfp = (impl*) user_data;
+
+ if ( selfp) selfp->addUsers(names, ids);
+}
+
+LLPanelGroupInvite::LLPanelGroupInvite(const std::string& name,
+ const LLUUID& group_id)
+ : LLPanel(name)
+{
+ mImplementation = new impl(group_id);
+
+ std::string panel_def_file;
+
+ // Pass on construction of this panel to the control factory.
+ gUICtrlFactory->buildPanel(this, "panel_group_invite.xml", &getFactoryMap());
+}
+
+LLPanelGroupInvite::~LLPanelGroupInvite()
+{
+ delete mImplementation;
+}
+
+void LLPanelGroupInvite::setCloseCallback(void (*close_callback)(void*),
+ void* data)
+{
+ mImplementation->mCloseCallback = close_callback;
+ mImplementation->mCloseCallbackUserData = data;
+}
+
+void LLPanelGroupInvite::clear()
+{
+ mImplementation->mInvitees->deleteAllItems();
+ mImplementation->mRoleNames->clear();
+ mImplementation->mRoleNames->removeall();
+}
+
+void LLPanelGroupInvite::update()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mImplementation->mGroupID);
+
+ if (!gdatap || !gdatap->isRoleDataComplete())
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mImplementation->mGroupID);
+ }
+ else
+ {
+ if ( mImplementation->mRoleNames )
+ {
+ mImplementation->mRoleNames->clear();
+ mImplementation->mRoleNames->removeall();
+
+ //add the role names and select the everybody role by default
+ mImplementation->addRoleNames(gdatap);
+ mImplementation->mRoleNames->setCurrentByID(LLUUID::null);
+ }
+ }
+}
+
+BOOL LLPanelGroupInvite::postBuild()
+{
+ BOOL recurse = TRUE;
+
+ mImplementation->mRoleNames = (LLComboBox*) getChildByName("role_name",
+ recurse);
+ mImplementation->mInvitees =
+ (LLNameListCtrl*) getChildByName("invitee_list", recurse);
+ if ( mImplementation->mInvitees )
+ {
+ mImplementation->mInvitees->setCallbackUserData(mImplementation);
+ mImplementation->mInvitees->setCommitOnSelectionChange(TRUE);
+ mImplementation->mInvitees->setCommitCallback(impl::callbackSelect);
+ }
+
+ LLButton* button = (LLButton*) getChildByName("add_button", recurse);
+ if ( button )
+ {
+ // default to opening avatarpicker automatically
+ // (*impl::callbackClickAdd)((void*)this);
+ button->setClickedCallback(impl::callbackClickAdd);
+ button->setCallbackUserData(this);
+ }
+
+ mImplementation->mRemoveButton =
+ (LLButton*) getChildByName("remove_button", recurse);
+ if ( mImplementation->mRemoveButton )
+ {
+ mImplementation->mRemoveButton->
+ setClickedCallback(impl::callbackClickRemove);
+ mImplementation->mRemoveButton->setCallbackUserData(mImplementation);
+ mImplementation->mRemoveButton->setEnabled(FALSE);
+ }
+
+ button = (LLButton*) getChildByName("ok_button", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(impl::callbackClickOK);
+ button->setCallbackUserData(mImplementation);
+ }
+
+ button = (LLButton*) getChildByName("cancel_button", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(impl::callbackClickCancel);
+ button->setCallbackUserData(mImplementation);
+ }
+
+ update();
+
+ return (mImplementation->mRoleNames &&
+ mImplementation->mInvitees &&
+ mImplementation->mRemoveButton);
+}
diff --git a/indra/newview/llpanelgroupinvite.h b/indra/newview/llpanelgroupinvite.h
new file mode 100644
index 0000000000..bab39a1a66
--- /dev/null
+++ b/indra/newview/llpanelgroupinvite.h
@@ -0,0 +1,32 @@
+/**
+ * @file llpanelgroupinvite.h
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPINVITE_H
+#define LL_LLPANELGROUPINVITE_H
+
+#include "llpanel.h"
+#include "lluuid.h"
+
+class LLPanelGroupInvite
+: public LLPanel
+{
+public:
+ LLPanelGroupInvite(const std::string& name, const LLUUID& group_id);
+ ~LLPanelGroupInvite();
+
+ void clear();
+ void update();
+
+ void setCloseCallback(void (*close_callback)(void*), void* data);
+
+ virtual BOOL postBuild();
+protected:
+ class impl;
+ impl* mImplementation;
+};
+
+#endif
diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp
new file mode 100644
index 0000000000..1cff3a6ea4
--- /dev/null
+++ b/indra/newview/llpanelgrouplandmoney.cpp
@@ -0,0 +1,1387 @@
+/**
+ * @file llpanelgrouplandmoney.cpp
+ * @brief Panel for group land and money.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgrouplandmoney.h"
+
+#include "lluiconstants.h"
+#include "roles_constants.h"
+
+#include "llparcel.h"
+#include "llqueryflags.h"
+
+#include "llagent.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "lltabcontainer.h"
+#include "lltransactiontypes.h"
+#include "llvieweruictrlfactory.h"
+
+#include "llstatusbar.h"
+#include "llfloaterworldmap.h"
+#include "llviewermessage.h"
+
+const char LOADING_STRING[] = "Computing...";
+
+class LLPanelGroupLandMoney::impl
+{
+public:
+ impl(const LLUUID& group_id); //constructor
+ virtual ~impl();
+
+ void requestGroupLandInfo();
+
+ int getStoredContribution();
+ void setYourContributionTextField(int contrib);
+ void setYourMaxContributionTextBox(int max);
+
+ virtual void onMapButton();
+ virtual bool applyContribution();
+ virtual void processGroupLand(LLMessageSystem* msg);
+
+ static void mapCallback(void* data);
+ static void contributionCommitCallback(LLUICtrl* ctrl, void* userdata);
+ static void contributionKeystrokeCallback(LLLineEditor* caller, void* userdata);
+
+//member variables
+public:
+ LLTextBox *mTotalContributedLandp;
+ LLTextBox *mTotalLandInUsep;
+ LLTextBox *mLandAvailablep;
+ LLTextBox* mGroupOverLimitTextp;
+ LLIconCtrl* mGroupOverLimitIconp;
+ LLTextBox* mYourContributionMaxTextp;
+
+ LLLineEditor* mYourContributionEditorp;
+
+ LLButton* mMapButtonp;
+
+ LLGroupMoneyTabEventHandler* mMoneyDetailsTabEHp;
+ LLGroupMoneyTabEventHandler* mMoneyPlanningTabEHp;
+ LLGroupMoneyTabEventHandler* mMoneySalesTabEHp;
+
+ LLScrollListCtrl* mGroupParcelsp;
+
+ LLUUID mGroupID;
+ LLUUID mTransID;
+
+ bool mBeenActivated;
+ bool mNeedsSendGroupLandRequest;
+ bool mNeedsApply;
+
+ std::string mCantViewParcelsText;
+ std::string mCantViewAccountsText;
+};
+
+//*******************************************
+//** LLPanelGroupLandMoney::impl Functions **
+//*******************************************
+LLPanelGroupLandMoney::impl::impl(const LLUUID& group_id)
+{
+ mGroupID = group_id;
+ mTransID = LLUUID::null;
+
+ mBeenActivated = false;
+ mNeedsSendGroupLandRequest = true;
+ mNeedsApply = false;
+
+ mTotalLandInUsep = NULL;
+ mTotalContributedLandp = NULL;
+ mLandAvailablep = NULL;
+ mYourContributionEditorp = NULL;
+ mMapButtonp = NULL;
+ mGroupParcelsp = NULL;
+ mYourContributionMaxTextp = NULL;
+ mGroupOverLimitTextp = NULL;
+ mGroupOverLimitIconp = NULL;
+
+ mMoneySalesTabEHp = NULL;
+ mMoneyPlanningTabEHp = NULL;
+ mMoneyDetailsTabEHp = NULL;
+}
+
+LLPanelGroupLandMoney::impl::~impl()
+{
+ if ( mMoneySalesTabEHp ) delete mMoneySalesTabEHp;
+ if ( mMoneyDetailsTabEHp ) delete mMoneyDetailsTabEHp;
+ if ( mMoneyPlanningTabEHp ) delete mMoneyPlanningTabEHp;
+}
+
+void LLPanelGroupLandMoney::impl::requestGroupLandInfo()
+{
+ U32 query_flags = DFQ_GROUP_OWNED;
+
+ mTransID.generate();
+ mGroupParcelsp->deleteAllItems();
+
+ send_places_query(mGroupID, mTransID, "", query_flags, LLParcel::C_ANY, "");
+}
+
+void LLPanelGroupLandMoney::impl::onMapButton()
+{
+ LLScrollListItem* itemp;
+ itemp = mGroupParcelsp->getFirstSelected();
+ if (!itemp) return;
+
+ const LLScrollListCell* cellp;
+ // name
+ // location
+ // area
+ cellp = itemp->getColumn(3); // hidden
+
+ F32 global_x = 0.f;
+ F32 global_y = 0.f;
+ sscanf(cellp->getText().c_str(), "%f %f", &global_x, &global_y);
+
+ // Hack: Use the agent's z-height
+ F64 global_z = gAgent.getPositionGlobal().mdV[VZ];
+
+ LLVector3d pos_global(global_x, global_y, global_z);
+ gFloaterWorldMap->trackLocation(pos_global);
+
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+bool LLPanelGroupLandMoney::impl::applyContribution()
+{
+ // calculate max donation, which is sum of available and current.
+ S32 your_contribution = 0;
+ S32 sqm_avail;
+
+ your_contribution = getStoredContribution();
+ sqm_avail = your_contribution;
+
+ if(gStatusBar)
+ {
+ sqm_avail += gStatusBar->getSquareMetersLeft();
+ }
+
+ // get new contribution and compare to available
+ S32 new_contribution = atoi(mYourContributionEditorp->getText().c_str());
+
+ if( new_contribution != your_contribution &&
+ new_contribution >= 0 &&
+ new_contribution <= sqm_avail )
+ {
+ // update group info and server
+ if(!gAgent.setGroupContribution(mGroupID, new_contribution))
+ {
+ // should never happen...
+ llwarns << "Unable to set contribution." << llendl;
+ return false;
+ }
+ }
+ else
+ {
+ //TODO: throw up some error message here and return? right now we just
+ //fail silently and force the previous value -jwolk
+ new_contribution = your_contribution;
+ }
+
+ //set your contribution
+ setYourContributionTextField(new_contribution);
+
+ return true;
+}
+
+// Retrieves the land contribution for this agent that is currently
+// stored in the database, NOT what is currently entered in the text field
+int LLPanelGroupLandMoney::impl::getStoredContribution()
+{
+ LLGroupData group_data;
+
+ group_data.mContribution = 0;
+ gAgent.getGroupData(mGroupID, group_data);
+
+ return group_data.mContribution;
+}
+
+// Fills in the text field with the contribution, contrib
+void LLPanelGroupLandMoney::impl::setYourContributionTextField(int contrib)
+{
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ sprintf(buffer, "%d", contrib);
+
+ if ( mYourContributionEditorp )
+ {
+ mYourContributionEditorp->setText(buffer);
+ mYourContributionEditorp->draw();
+ }
+}
+
+void LLPanelGroupLandMoney::impl::setYourMaxContributionTextBox(int max)
+{
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+
+ sprintf(buffer, "(%d max)", max);
+ if ( mYourContributionMaxTextp )
+ {
+ mYourContributionMaxTextp->setText(buffer);
+ }
+}
+
+//static
+void LLPanelGroupLandMoney::impl::mapCallback(void* data)
+{
+ LLPanelGroupLandMoney::impl* selfp = (LLPanelGroupLandMoney::impl*) data;
+
+ if ( selfp ) selfp->onMapButton();
+}
+
+void LLPanelGroupLandMoney::impl::contributionCommitCallback(LLUICtrl* ctrl,
+ void* userdata)
+{
+ LLPanelGroupLandMoney* tabp = (LLPanelGroupLandMoney*) userdata;
+ LLLineEditor* editorp = (LLLineEditor*) ctrl;
+
+ if ( tabp && editorp )
+ {
+ impl* self = tabp->mImplementationp;
+ int your_contribution = 0;
+ int new_contribution = 0;
+
+ new_contribution= atoi(editorp->getText().c_str());
+ your_contribution = self->getStoredContribution();
+
+ //reset their junk data to be "good" data to us
+ self->setYourContributionTextField(new_contribution);
+
+ //check to see if they're contribution text has changed
+ self->mNeedsApply = new_contribution != your_contribution;
+ tabp->notifyObservers();
+ }
+}
+
+void LLPanelGroupLandMoney::impl::contributionKeystrokeCallback(LLLineEditor* caller,
+ void* userdata)
+{
+ impl::contributionCommitCallback(caller, userdata);
+}
+
+//static
+void LLPanelGroupLandMoney::impl::processGroupLand(LLMessageSystem* msg)
+{
+ S32 count = msg->getNumberOfBlocks("QueryData");
+ if(count > 0)
+ {
+ S32 first_block = 0;
+
+ LLUUID owner_id;
+ LLUUID trans_id;
+
+ msg->getUUID("QueryData", "OwnerID", owner_id, 0);
+ msg->getUUID("TransactionData", "TransactionID", trans_id);
+
+ if(owner_id.isNull())
+ {
+ // special block which has total contribution
+ ++first_block;
+ S32 total_contribution;
+ msg->getS32("QueryData", "ActualArea", total_contribution, 0);
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d sq. meters", total_contribution);
+ mTotalContributedLandp->setText(buffer);
+ S32 committed;
+ msg->getS32("QueryData", "BillableArea", committed, 0);
+ sprintf(buffer, "%d sq. meters", committed);
+ mTotalLandInUsep->setText(buffer);
+ S32 available = total_contribution - committed;
+ sprintf(buffer, "%d sq. meters", available);
+ mLandAvailablep->setText(buffer);
+ buffer[0] = '\0';
+ if ( mGroupOverLimitTextp && mGroupOverLimitIconp )
+ {
+ mGroupOverLimitIconp->setVisible(available < 0);
+ mGroupOverLimitTextp->setVisible(available < 0);
+ }
+ }
+
+ if ( trans_id != mTransID ) return;
+ // This power was removed to make group roles simpler
+ //if ( !gAgent.hasPowerInGroup(mGroupID, GP_LAND_VIEW_OWNED) ) return;
+ if (!gAgent.isInGroup(mGroupID)) return;
+
+ //we updated more than just the available area special block
+ if ( count > 1)
+ {
+ mMapButtonp->setEnabled(TRUE);
+ }
+
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ char sim_name[MAX_STRING];
+ for(S32 i = first_block; i < count; ++i)
+ {
+ msg->getUUID("QueryData", "OwnerID", owner_id, i);
+ msg->getString("QueryData", "Name", MAX_STRING, name, i);
+ msg->getString("QueryData", "Desc", MAX_STRING, desc, i);
+ msg->getS32("QueryData", "ActualArea", actual_area, i);
+ msg->getS32("QueryData", "BillableArea", billable_area, i);
+ msg->getU8("QueryData", "Flags", flags, i);
+ msg->getF32("QueryData", "GlobalX", global_x, i);
+ msg->getF32("QueryData", "GlobalY", global_y, i);
+ msg->getString("QueryData", "SimName", MAX_STRING, sim_name, i);
+
+ S32 region_x = llround(global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = llround(global_y) % REGION_WIDTH_UNITS;
+ char location[MAX_STRING];
+ sprintf(location, "%s (%d, %d)", sim_name, region_x, region_y);
+ char area[MAX_STRING];
+ if(billable_area == actual_area)
+ {
+ sprintf(area, "%d", billable_area);
+ }
+ else
+ {
+ sprintf(area, "%d / %d", billable_area, actual_area);
+ }
+ char hidden[MAX_STRING];
+ sprintf(hidden, "%f %f", global_x, global_y);
+
+ LLSD row;
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = name;
+ row["columns"][0]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][1]["column"] = "location";
+ row["columns"][1]["value"] = location;
+ row["columns"][1]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][2]["column"] = "area";
+ row["columns"][2]["value"] = area;
+ row["columns"][2]["font"] = "SANSSERIFSMALL";
+
+ row["columns"][3]["column"] = "hidden";
+ row["columns"][3]["value"] = hidden;
+
+ mGroupParcelsp->addElement(row, ADD_SORTED);
+ }
+ }
+}
+
+//*************************************
+//** LLPanelGroupLandMoney Functions **
+//*************************************
+
+//static
+void* LLPanelGroupLandMoney::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupLandMoney("panel group land money", *group_id);
+}
+
+//static
+LLMap<LLUUID, LLPanelGroupLandMoney*> LLPanelGroupLandMoney::sGroupIDs;
+
+LLPanelGroupLandMoney::LLPanelGroupLandMoney(const std::string& name,
+ const LLUUID& group_id) :
+ LLPanelGroupTab(name, group_id)
+{
+ mImplementationp = new impl(group_id);
+
+ //problem what if someone has both the group floater open and the finder
+ //open to the same group? Some maps that map group ids to panels
+ //will then only be working for the last panel for a given group id :(
+ LLPanelGroupLandMoney::sGroupIDs.addData(group_id, this);
+}
+
+LLPanelGroupLandMoney::~LLPanelGroupLandMoney()
+{
+ delete mImplementationp;
+ LLPanelGroupLandMoney::sGroupIDs.removeData(mGroupID);
+}
+
+void LLPanelGroupLandMoney::activate()
+{
+ if ( !mImplementationp->mBeenActivated )
+ {
+ //select the first tab
+ LLTabContainerCommon* tabp = (LLTabContainerCommon*) getChildByName("group_money_tab_container");
+
+ if ( tabp )
+ {
+ tabp->selectFirstTab();
+ mImplementationp->mBeenActivated = true;
+ }
+
+ //fill in the max contribution
+
+ //This calculation is unfortunately based on
+ //the status bar's concept of how much land the user has
+ //which can change dynamically if the user buys new land, gives
+ //more land to a group, etc.
+ //A race condition can occur if we want to update the UI's
+ //concept of the user's max contribution before the status
+ //bar has been updated from a change in the user's group contribution.
+
+ //Since the max contribution should not change solely on changing
+ //a user's group contribution, (it would only change through
+ //purchasing of new land) this code is placed here
+ //and only updated once to prevent the race condition
+ //at the price of having stale data.
+ //We need to have the status bar have observers
+ //or find better way of distributing up to date land data. - jwolk
+ S32 max_avail = mImplementationp->getStoredContribution();
+ if(gStatusBar)
+ {
+ max_avail += gStatusBar->getSquareMetersLeft();
+ }
+ mImplementationp->setYourMaxContributionTextBox(max_avail);
+ }
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupLandMoney::update(LLGroupChange gc)
+{
+ if (gc != GC_ALL) return; //Don't update if it's the wrong panel!
+
+ LLTabContainerCommon* tabp = (LLTabContainerCommon*) getChildByName("group_money_tab_container");
+
+ if ( tabp )
+ {
+ LLPanel* panelp;
+ LLGroupMoneyTabEventHandler* eh;
+
+ panelp = tabp->getCurrentPanel();
+
+ //now pull the event handler associated with that money tab
+ if ( panelp )
+ {
+ eh = get_if_there(LLGroupMoneyTabEventHandler::sTabsToHandlers,
+ panelp,
+ (LLGroupMoneyTabEventHandler*)NULL);
+ if ( eh ) eh->onClickTab();
+ }
+ }
+
+ mImplementationp->requestGroupLandInfo();
+ mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution());
+}
+
+bool LLPanelGroupLandMoney::needsApply(LLString& mesg)
+{
+ return mImplementationp->mNeedsApply;
+}
+
+bool LLPanelGroupLandMoney::apply(LLString& mesg)
+{
+ if (!mImplementationp->applyContribution() )
+ {
+ mesg.assign("Unable to set your land contribution.");
+ return false;
+ }
+
+ mImplementationp->mNeedsApply = false;
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupLandMoney::cancel()
+{
+ //set the contribution back to the "stored value"
+ mImplementationp->setYourContributionTextField(mImplementationp->getStoredContribution());
+
+ mImplementationp->mNeedsApply = false;
+ notifyObservers();
+}
+
+
+BOOL LLPanelGroupLandMoney::postBuild()
+{
+ /* This power was removed to make group roles simpler
+ bool has_parcel_view = gAgent.hasPowerInGroup(mGroupID,
+ GP_LAND_VIEW_OWNED);
+ bool has_accounting_view = gAgent.hasPowerInGroup(mGroupID,
+ GP_ACCOUNTING_VIEW);
+ */
+
+ bool can_view = gAgent.isInGroup(mGroupID);
+
+ mImplementationp->mTotalLandInUsep =
+ (LLTextBox*) getChildByName("total_land_in_use_value");
+ mImplementationp->mTotalContributedLandp =
+ (LLTextBox*) getChildByName("total_contributed_land_value");
+ mImplementationp->mLandAvailablep =
+ (LLTextBox*) getChildByName("land_available_value");
+ mImplementationp->mGroupOverLimitIconp =
+ (LLIconCtrl*) getChildByName("group_over_limit_icon");
+ mImplementationp->mGroupOverLimitTextp =
+ (LLTextBox*) getChildByName("group_over_limit_text");
+ mImplementationp->mYourContributionMaxTextp =
+ (LLTextBox*) getChildByName("your_contribution_max_value");
+
+ mImplementationp->mYourContributionEditorp
+ = (LLLineEditor*) getChildByName("your_contribution_line_editor");
+ if ( mImplementationp->mYourContributionEditorp )
+ {
+ LLLineEditor* editor = mImplementationp->mYourContributionEditorp;
+
+ editor->setCommitCallback(mImplementationp->contributionCommitCallback);
+ editor->setKeystrokeCallback(mImplementationp->contributionKeystrokeCallback);
+ editor->setCallbackUserData(this);
+ }
+
+ mImplementationp->mMapButtonp = (LLButton*) getChildByName("map_button");
+
+ mImplementationp->mGroupParcelsp =
+ (LLScrollListCtrl*) getChildByName("group_parcel_list");
+
+ LLTextBox *no_permsp =
+ (LLTextBox*) getChildByName("cant_view_group_land_text");
+ if ( no_permsp )
+ {
+ mImplementationp->mCantViewParcelsText = no_permsp->getText();
+ removeChild(no_permsp);
+ }
+
+ no_permsp = (LLTextBox*) getChildByName("cant_view_group_accounting_text");
+ if ( no_permsp )
+ {
+ mImplementationp->mCantViewAccountsText = no_permsp->getText();
+ removeChild(no_permsp);
+ }
+
+
+ if ( mImplementationp->mMapButtonp )
+ {
+ mImplementationp->mMapButtonp->setClickedCallback(LLPanelGroupLandMoney::impl::mapCallback, mImplementationp);
+ }
+
+ if ( mImplementationp->mGroupOverLimitTextp )
+ {
+ mImplementationp->mGroupOverLimitTextp->setVisible(FALSE);
+ }
+
+ if ( mImplementationp->mGroupOverLimitIconp )
+ {
+ mImplementationp->mGroupOverLimitIconp->setVisible(FALSE);
+ }
+
+ if ( mImplementationp->mMapButtonp )
+ {
+ mImplementationp->mMapButtonp->setEnabled(FALSE);
+ }
+
+ if ( !can_view )
+ {
+ if ( mImplementationp->mGroupParcelsp )
+ {
+ mImplementationp->mGroupParcelsp->addSimpleItem(
+ mImplementationp->mCantViewParcelsText);
+ mImplementationp->mGroupParcelsp->setEnabled(FALSE);
+ }
+ }
+
+
+
+ LLButton* earlierp, *laterp;
+ LLTextEditor* textp;
+ LLPanel* panelp;
+
+ LLTabContainerCommon* tabcp = (LLTabContainerCommon*)
+ getChildByName("group_money_tab_container", true);
+
+ if ( !can_view )
+ {
+ if ( tabcp )
+ {
+ S32 i;
+ S32 tab_count = tabcp->getTabCount();
+
+ for (i = tab_count - 1; i >=0; --i)
+ {
+ tabcp->enableTabButton(i, false);
+ }
+ }
+ }
+
+
+ //pull out the widgets for the money details tab
+ earlierp = (LLButton*) getChildByName("earlier_details_button", true);
+ laterp = (LLButton*) getChildByName("later_details_button", true);
+ textp = (LLTextEditor*) getChildByName("group_money_details_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_details_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneyDetailsTabEHp =
+ new LLGroupMoneyDetailsTabEventHandler(earlierp,
+ laterp,
+ textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ textp = (LLTextEditor*) getChildByName("group_money_planning_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_planning_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneyPlanningTabEHp =
+ new LLGroupMoneyPlanningTabEventHandler(textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ //pull out the widgets for the money sales tab
+ earlierp = (LLButton*) getChildByName("earlier_sales_button", true);
+ laterp = (LLButton*) getChildByName("later_sales_button", true);
+ textp = (LLTextEditor*) getChildByName("group_money_sales_text", true);
+ panelp = (LLPanel*) getChildByName("group_money_sales_tab", true);
+
+ if ( !can_view )
+ {
+ textp->setText(mImplementationp->mCantViewAccountsText);
+ }
+ else
+ {
+ mImplementationp->mMoneySalesTabEHp =
+ new LLGroupMoneySalesTabEventHandler(earlierp,
+ laterp,
+ textp,
+ tabcp,
+ panelp,
+ mGroupID);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+BOOL LLPanelGroupLandMoney::isVisibleByAgent(LLAgent* agentp)
+{
+ return mAllowEdit && agentp->isInGroup(mGroupID);
+}
+
+void LLPanelGroupLandMoney::processPlacesReply(LLMessageSystem* msg, void**)
+{
+ LLUUID group_id;
+ msg->getUUID("AgentData", "QueryID", group_id);
+
+ LLPanelGroupLandMoney* selfp = sGroupIDs.getIfThere(group_id);
+ if(!selfp)
+ {
+ llinfos << "Group Panel Land Money " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ selfp->mImplementationp->processGroupLand(msg);
+}
+
+//*************************************************
+//** LLGroupMoneyTabEventHandler::impl Functions **
+//*************************************************
+
+class LLGroupMoneyTabEventHandler::impl
+{
+public:
+ impl(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days);
+ ~impl();
+
+ bool getCanClickLater();
+ bool getCanClickEarlier();
+
+ void updateButtons();
+
+//member variables
+public:
+ LLUUID mGroupID;
+ LLUUID mPanelID;
+
+ LLPanel* mPanelp;
+
+ int mIntervalLength;
+ int mMaxInterval;
+ int mCurrentInterval;
+
+ LLTextEditor* mTextEditorp;
+ LLButton* mEarlierButtonp;
+ LLButton* mLaterButtonp;
+};
+
+LLGroupMoneyTabEventHandler::impl::impl(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days)
+{
+ mGroupID = group_id;
+ mPanelID.generate();
+
+ mIntervalLength = interval_length_days;
+ mMaxInterval = max_interval_days;
+ mCurrentInterval = 0;
+
+ mTextEditorp = text_editorp;
+ mEarlierButtonp = earlier_buttonp;
+ mLaterButtonp = later_buttonp;
+ mPanelp = panelp;
+}
+
+LLGroupMoneyTabEventHandler::impl::~impl()
+{
+}
+
+bool LLGroupMoneyTabEventHandler::impl::getCanClickEarlier()
+{
+ return (mCurrentInterval < mMaxInterval);
+}
+
+bool LLGroupMoneyTabEventHandler::impl::getCanClickLater()
+{
+ return ( mCurrentInterval > 0 );
+}
+
+void LLGroupMoneyTabEventHandler::impl::updateButtons()
+{
+ if ( mEarlierButtonp )
+ {
+ mEarlierButtonp->setEnabled(getCanClickEarlier());
+ }
+ if ( mLaterButtonp )
+ {
+ mLaterButtonp->setEnabled(getCanClickLater());
+ }
+}
+
+//*******************************************
+//** LLGroupMoneyTabEventHandler Functions **
+//*******************************************
+LLMap<LLUUID, LLGroupMoneyTabEventHandler*> LLGroupMoneyTabEventHandler::sInstanceIDs;
+std::map<LLPanel*, LLGroupMoneyTabEventHandler*> LLGroupMoneyTabEventHandler::sTabsToHandlers;
+
+LLGroupMoneyTabEventHandler::LLGroupMoneyTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days)
+{
+ mImplementationp = new impl(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ panelp,
+ group_id,
+ interval_length_days,
+ max_interval_days);
+
+ if ( earlier_buttonp )
+ {
+ earlier_buttonp->setClickedCallback(LLGroupMoneyTabEventHandler::clickEarlierCallback,
+ this);
+ }
+
+ if ( later_buttonp )
+ {
+ later_buttonp->setClickedCallback(LLGroupMoneyTabEventHandler::clickLaterCallback,
+ this);
+ }
+
+ mImplementationp->updateButtons();
+
+ if ( tab_containerp && panelp )
+ {
+ tab_containerp->setTabChangeCallback(panelp,
+ LLGroupMoneyTabEventHandler::clickTabCallback);
+ tab_containerp->setTabUserData(panelp, this);
+ }
+
+ sInstanceIDs.addData(mImplementationp->mPanelID, this);
+ sTabsToHandlers[panelp] = this;
+}
+
+LLGroupMoneyTabEventHandler::~LLGroupMoneyTabEventHandler()
+{
+ sInstanceIDs.removeData(mImplementationp->mPanelID);
+ sTabsToHandlers.erase(mImplementationp->mPanelp);
+
+ delete mImplementationp;
+}
+
+
+void LLGroupMoneyTabEventHandler::onClickTab()
+{
+ requestData(gMessageSystem);
+}
+
+void LLGroupMoneyTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ //do nothing
+}
+
+void LLGroupMoneyTabEventHandler::processReply(LLMessageSystem* msg, void** data)
+{
+ //do nothing
+}
+
+void LLGroupMoneyTabEventHandler::onClickEarlier()
+{
+ if ( mImplementationp->mTextEditorp)
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+ mImplementationp->mCurrentInterval++;
+
+ mImplementationp->updateButtons();
+
+ requestData(gMessageSystem);
+}
+
+void LLGroupMoneyTabEventHandler::onClickLater()
+{
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+ mImplementationp->mCurrentInterval--;
+
+ mImplementationp->updateButtons();
+
+ requestData(gMessageSystem);
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickEarlierCallback(void* data)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+
+ if ( selfp ) selfp->onClickEarlier();
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickLaterCallback(void* data)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+ if ( selfp ) selfp->onClickLater();
+}
+
+//static
+void LLGroupMoneyTabEventHandler::clickTabCallback(void* data, bool from_click)
+{
+ LLGroupMoneyTabEventHandler* selfp = (LLGroupMoneyTabEventHandler*) data;
+ if ( selfp ) selfp->onClickTab();
+}
+
+//**************************************************
+//** LLGroupMoneyDetailsTabEventHandler Functions **
+//**************************************************
+
+LLGroupMoneyDetailsTabEventHandler::LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneyDetailsTabEventHandler::~LLGroupMoneyDetailsTabEventHandler()
+{
+}
+
+void LLGroupMoneyDetailsTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountDetailsRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength );
+ msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval);
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneyDetailsTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account details not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ LLString text;
+
+ char start_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+
+ if ( interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval )
+ {
+ llinfos << "Out of date details packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ sprintf(line, "%s\n\n", start_date);
+ text.append(line);
+
+ S32 total_amount = 0;
+ S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData);
+ for(S32 i = 0; i < transactions; i++)
+ {
+ S32 amount = 0;
+ char desc[MAX_STRING];
+
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Description, MAX_STRING, desc, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i);
+
+ if (amount != 0)
+ {
+ sprintf(line, "%-24s %6d\n", desc, amount );
+ text.append(line);
+ }
+ else
+ {
+ // skip it
+ }
+
+ total_amount += amount;
+ }
+
+ text.append(1, '\n');
+
+ sprintf(line, "%-24s %6d\n", "Total", total_amount );
+ text.append(line);
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneyDetailsTabEventHandler::processGroupAccountDetailsReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+ LLGroupMoneyTabEventHandler* selfp = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!selfp)
+ {
+ llwarns << "GroupAccountDetails recieved for non-existent group panel." << llendl;
+ return;
+ }
+
+ selfp->processReply(msg, data);
+}
+
+//************************************************
+//** LLGroupMoneySalesTabEventHandler Functions **
+//************************************************
+
+LLGroupMoneySalesTabEventHandler::LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(earlier_buttonp,
+ later_buttonp,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneySalesTabEventHandler::~LLGroupMoneySalesTabEventHandler()
+{
+}
+
+void LLGroupMoneySalesTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountTransactionsRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength );
+ msg->addS32Fast(_PREHASH_CurrentInterval, mImplementationp->mCurrentInterval);
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneySalesTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account Transactions not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ std::string text = mImplementationp->mTextEditorp->getText();
+
+ char start_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+
+ if (interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval)
+ {
+ llinfos << "Out of date details packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ // If this is the first packet, clear the text, don't append.
+ // Start with the date.
+ if (text == LOADING_STRING)
+ {
+ text.clear();
+
+ sprintf(line, "%s\n\n", start_date);
+ text.append(line);
+ }
+
+ S32 transactions = msg->getNumberOfBlocksFast(_PREHASH_HistoryData);
+ if (transactions == 0)
+ {
+ text.append("(none)");
+ }
+ else
+ {
+ for(S32 i = 0; i < transactions; i++)
+ {
+ const S32 SHORT_STRING = 64;
+ char time[SHORT_STRING];
+ S32 type = 0;
+ S32 amount = 0;
+ char user[SHORT_STRING];
+ char item[SHORT_STRING];
+
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Time, SHORT_STRING, time, i);
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_User, SHORT_STRING, user, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Type, type, i);
+ msg->getStringFast(_PREHASH_HistoryData, _PREHASH_Item, SHORT_STRING, item, i );
+ msg->getS32Fast(_PREHASH_HistoryData, _PREHASH_Amount, amount, i);
+
+ if (amount != 0)
+ {
+ char* verb;
+
+ switch(type)
+ {
+ case TRANS_OBJECT_SALE:
+ verb = "bought";
+ break;
+ case TRANS_GIFT:
+ verb = "paid you";
+ break;
+ case TRANS_PAY_OBJECT:
+ verb = "paid into";
+ break;
+ case TRANS_LAND_PASS_SALE:
+ verb = "bought pass to";
+ break;
+ case TRANS_EVENT_FEE:
+ verb = "paid fee for event";
+ break;
+ case TRANS_EVENT_PRIZE:
+ verb = "paid prize for event";
+ break;
+ default:
+ verb = "";
+ break;
+ }
+
+ sprintf(line, "%s %6d - %s %s %s\n", time, amount, user, verb, item);
+ text.append(line);
+ }
+ }
+ }
+
+ if ( mImplementationp->mTextEditorp)
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneySalesTabEventHandler::processGroupAccountTransactionsReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+
+ LLGroupMoneyTabEventHandler* self;
+
+ self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!self)
+ {
+ llwarns << "GroupAccountTransactions recieved for non-existent group panel." << llendl;
+ return;
+ }
+
+ self->processReply(msg, data);
+}
+
+//***************************************************
+//** LLGroupMoneyPlanningTabEventHandler Functions **
+//***************************************************
+
+LLGroupMoneyPlanningTabEventHandler::LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id)
+ : LLGroupMoneyTabEventHandler(NULL,
+ NULL,
+ text_editorp,
+ tab_containerp,
+ panelp,
+ group_id,
+ SUMMARY_INTERVAL,
+ SUMMARY_MAX)
+{
+}
+
+LLGroupMoneyPlanningTabEventHandler::~LLGroupMoneyPlanningTabEventHandler()
+{
+}
+
+void LLGroupMoneyPlanningTabEventHandler::requestData(LLMessageSystem* msg)
+{
+ msg->newMessageFast(_PREHASH_GroupAccountSummaryRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, mImplementationp->mGroupID );
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_RequestID, mImplementationp->mPanelID );
+ msg->addS32Fast(_PREHASH_IntervalDays, mImplementationp->mIntervalLength);
+ msg->addS32Fast(_PREHASH_CurrentInterval, 0); //planning has 0 interval
+
+ gAgent.sendReliableMessage();
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(LOADING_STRING);
+ }
+
+ LLGroupMoneyTabEventHandler::requestData(msg);
+}
+
+void LLGroupMoneyPlanningTabEventHandler::processReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID group_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_GroupID, group_id );
+ if (mImplementationp->mGroupID != group_id)
+ {
+ llwarns << "Group Account Summary received not for this group!" << llendl;
+ return;
+ }
+
+ char line[MAX_STRING];
+ LLString text;
+
+ char start_date[MAX_STRING];
+ char last_stipend_date[MAX_STRING];
+ char next_stipend_date[MAX_STRING];
+ S32 interval_days;
+ S32 current_interval;
+ S32 balance;
+ S32 total_credits;
+ S32 total_debits;
+ S32 cur_object_tax;
+ S32 cur_light_tax;
+ S32 cur_land_tax;
+ S32 cur_group_tax;
+ S32 cur_parcel_dir_fee;
+ S32 cur_total_tax;
+ S32 proj_object_tax;
+ S32 proj_light_tax;
+ S32 proj_land_tax;
+ S32 proj_group_tax;
+ S32 proj_parcel_dir_fee;
+ S32 proj_total_tax;
+ S32 non_exempt_members;
+
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_IntervalDays, interval_days );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_CurrentInterval, current_interval );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_Balance, balance );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalCredits, total_credits );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_TotalDebits, total_debits );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxCurrent, cur_object_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxCurrent, cur_light_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxCurrent, cur_land_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxCurrent, cur_group_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeCurrent, cur_parcel_dir_fee );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ObjectTaxEstimate, proj_object_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LightTaxEstimate, proj_light_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_LandTaxEstimate, proj_land_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_GroupTaxEstimate, proj_group_tax );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_ParcelDirFeeEstimate, proj_parcel_dir_fee );
+ msg->getS32Fast(_PREHASH_MoneyData, _PREHASH_NonExemptMembers, non_exempt_members );
+
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_StartDate, MAX_STRING, start_date);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_LastTaxDate, MAX_STRING, last_stipend_date);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_TaxDate, MAX_STRING, next_stipend_date);
+
+ cur_total_tax = cur_object_tax + cur_light_tax + cur_land_tax + cur_group_tax + cur_parcel_dir_fee;
+ proj_total_tax = proj_object_tax + proj_light_tax + proj_land_tax + proj_group_tax + proj_parcel_dir_fee;
+
+ if (interval_days != mImplementationp->mIntervalLength ||
+ current_interval != mImplementationp->mCurrentInterval)
+ {
+ llinfos << "Out of date summary packet " << interval_days << " "
+ << current_interval << llendl;
+ return;
+ }
+
+ sprintf(line, "Summary for this week, beginning on %s\n", start_date);
+ text.append(line);
+
+ if (current_interval == 0)
+ {
+ sprintf(line, "The next stipend day is %s\n\n", next_stipend_date);
+ text.append(line);
+ sprintf(line, "%-24sL$%6d\n", "Balance", balance );
+ text.append(line);
+
+ text.append(1, '\n');
+ }
+
+ sprintf(line, " Group Individual Share\n");
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Credits", total_credits, (S32)floor((F32)total_credits/(F32)non_exempt_members));
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Debits", total_debits, (S32)floor((F32)total_debits/(F32)non_exempt_members));
+ text.append(line);
+ sprintf(line, "%-24s %6d %6d \n", "Total", total_credits + total_debits, (S32)floor((F32)(total_credits + total_debits)/(F32)non_exempt_members));
+ text.append(line);
+
+ if ( mImplementationp->mTextEditorp )
+ {
+ mImplementationp->mTextEditorp->setText(text);
+ }
+}
+
+//static
+void LLGroupMoneyPlanningTabEventHandler::processGroupAccountSummaryReply(LLMessageSystem* msg,
+ void** data)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id );
+ if (gAgent.getID() != agent_id)
+ {
+ llwarns << "Got group money history reply for another agent!"
+ << " Probably a userserver bug!" << llendl;
+ return;
+ }
+
+ LLUUID request_id;
+ msg->getUUIDFast(_PREHASH_MoneyData, _PREHASH_RequestID, request_id );
+
+ LLGroupMoneyTabEventHandler* self;
+
+ self = LLGroupMoneyTabEventHandler::sInstanceIDs.getIfThere(request_id);
+ if (!self)
+ {
+ llwarns << "GroupAccountSummary recieved for non-existent group money planning tab." << llendl;
+ return;
+ }
+
+ self->processReply(msg, data);
+}
diff --git a/indra/newview/llpanelgrouplandmoney.h b/indra/newview/llpanelgrouplandmoney.h
new file mode 100644
index 0000000000..b1cf44fe46
--- /dev/null
+++ b/indra/newview/llpanelgrouplandmoney.h
@@ -0,0 +1,127 @@
+/**
+ * @file llpanelgrouplandmoney.h
+ * @brief Panel for group land and money.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PANEL_GROUP_LAND_MONEY_H
+#define LL_PANEL_GROUP_LAND_MONEY_H
+
+#include "llpanelgroup.h"
+#include "llmap.h"
+#include "lluuid.h"
+
+#include "llbutton.h"
+#include "lltexteditor.h"
+#include "llpanel.h"
+
+class LLPanelGroupLandMoney : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupLandMoney(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupLandMoney();
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ virtual void update(LLGroupChange gc);
+
+ static void processPlacesReply(LLMessageSystem* msg, void**);
+
+ static LLMap<LLUUID, LLPanelGroupLandMoney*> sGroupIDs;
+protected:
+ class impl;
+ impl* mImplementationp;
+};
+
+class LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyTabEventHandler(LLButton* earlier_button,
+ LLButton* later_button,
+ LLTextEditor* text_editor,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id,
+ S32 interval_length_days,
+ S32 max_interval_days);
+ virtual ~LLGroupMoneyTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ virtual void onClickEarlier();
+ virtual void onClickLater();
+ virtual void onClickTab();
+
+ static void clickEarlierCallback(void* data);
+ static void clickLaterCallback(void* data);
+ static void clickTabCallback(void* user_data, bool from_click);
+
+ static LLMap<LLUUID, LLGroupMoneyTabEventHandler*> sInstanceIDs;
+ static std::map<LLPanel*, LLGroupMoneyTabEventHandler*> sTabsToHandlers;
+protected:
+ class impl;
+ impl* mImplementationp;
+};
+
+class LLGroupMoneyDetailsTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyDetailsTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneyDetailsTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountDetailsReply(LLMessageSystem* msg,
+ void** data);
+};
+
+class LLGroupMoneySalesTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneySalesTabEventHandler(LLButton* earlier_buttonp,
+ LLButton* later_buttonp,
+ LLTextEditor* text_editorp,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneySalesTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountTransactionsReply(LLMessageSystem* msg,
+ void** data);
+};
+
+class LLGroupMoneyPlanningTabEventHandler : public LLGroupMoneyTabEventHandler
+{
+public:
+ LLGroupMoneyPlanningTabEventHandler(LLTextEditor* text_editor,
+ LLTabContainerCommon* tab_containerp,
+ LLPanel* panelp,
+ const LLUUID& group_id);
+ virtual ~LLGroupMoneyPlanningTabEventHandler();
+
+ virtual void requestData(LLMessageSystem* msg);
+ virtual void processReply(LLMessageSystem* msg, void** data);
+
+ static void processGroupAccountSummaryReply(LLMessageSystem* msg,
+ void** data);
+};
+
+#endif // LL_PANEL_GROUP_LAND_MONEY_H
diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp
new file mode 100644
index 0000000000..9f774db2e9
--- /dev/null
+++ b/indra/newview/llpanelgroupnotices.cpp
@@ -0,0 +1,565 @@
+/**
+ * @file llpanelgroupnotices.cpp
+ * @brief A panel to display group notices.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelgroupnotices.h"
+
+#include "llview.h"
+
+#include "llinventory.h"
+#include "llviewerinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llagent.h"
+#include "lltooldraganddrop.h"
+
+#include "lllineeditor.h"
+#include "lltexteditor.h"
+#include "llbutton.h"
+#include "lliconctrl.h"
+#include "llcheckboxctrl.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+
+#include "roles_constants.h"
+#include "llviewerwindow.h"
+#include "llviewermessage.h"
+
+/////////////////////////
+// LLPanelGroupNotices //
+/////////////////////////
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLDropTarget
+//
+// This handy class is a simple way to drop something on another
+// view. It handles drop events, always setting itself to the size of
+// its parent.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class LLGroupDropTarget : public LLView
+{
+public:
+ LLGroupDropTarget(const std::string& name, const LLRect& rect, LLPanelGroupNotices* panel, const LLUUID& group_id);
+ ~LLGroupDropTarget() {};
+
+ void doDrop(EDragAndDropType cargo_type, void* cargo_data);
+
+ //
+ // LLView functionality
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+protected:
+ LLPanelGroupNotices* mGroupNoticesPanel;
+ LLUUID mGroupID;
+};
+
+LLGroupDropTarget::LLGroupDropTarget(const std::string& name, const LLRect& rect,
+ LLPanelGroupNotices* panel, const LLUUID& group_id) :
+ LLView(name, rect, NOT_MOUSE_OPAQUE, FOLLOWS_ALL),
+ mGroupNoticesPanel(panel),
+ mGroupID(group_id)
+{
+}
+
+EWidgetType LLGroupDropTarget::getWidgetType() const
+{
+ return WIDGET_TYPE_DONTCARE;
+}
+
+LLString LLGroupDropTarget::getWidgetTag() const
+{
+ return LL_GROUP_DROP_TARGET_TAG;
+}
+
+void LLGroupDropTarget::doDrop(EDragAndDropType cargo_type, void* cargo_data)
+{
+ llinfos << "LLGroupDropTarget::doDrop()" << llendl;
+}
+
+BOOL LLGroupDropTarget::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (!gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_SEND))
+ {
+ *accept = ACCEPT_NO;
+ return TRUE;
+ }
+
+ if(getParent())
+ {
+ // check if inside
+ //LLRect parent_rect = mParentView->getRect();
+ //mRect.set(0, parent_rect.getHeight(), parent_rect.getWidth(), 0);
+ handled = TRUE;
+
+ // check the type
+ switch(cargo_type)
+ {
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_CLOTHING:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLViewerInventoryItem* inv_item = (LLViewerInventoryItem*)cargo_data;
+ if(gInventory.getItem(inv_item->getUUID())
+ && LLToolDragAndDrop::isInventoryGroupGiveAcceptable(inv_item))
+ {
+ //FIXME: get multiple object transfers working
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ if(drop)
+ {
+ mGroupNoticesPanel->setItem(inv_item);
+ }
+ }
+ else
+ {
+ // It's not in the user's inventory (it's probably
+ // in an object's contents), so disallow dragging
+ // it here. You can't give something you don't
+ // yet have.
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ case DAD_CATEGORY:
+ case DAD_CALLINGCARD:
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ return handled;
+}
+
+//-----------------------------------------------------------------------------
+// LLPanelGroupNotices
+//-----------------------------------------------------------------------------
+char* build_notice_date(const time_t& the_time, char* buffer)
+{
+ time_t t = the_time;
+ if (!t) time(&t);
+ tm* lt = localtime(&t);
+ //for some reason, the month is off by 1. See other uses of
+ //"local" time in the code...
+ sprintf(buffer,"%i/%i/%i", lt->tm_mon + 1, lt->tm_mday, lt->tm_year + 1900);
+
+ return buffer;
+}
+
+LLPanelGroupNotices::LLPanelGroupNotices(const std::string& name,
+ const LLUUID& group_id) :
+ LLPanelGroupTab(name,group_id),
+ mInventoryItem(NULL),
+ mInventoryOffer(NULL)
+{
+ sInstances[group_id] = this;
+}
+
+LLPanelGroupNotices::~LLPanelGroupNotices()
+{
+ sInstances.erase(mGroupID);
+
+ if (mInventoryOffer)
+ {
+ // Cancel the inventory offer.
+ inventory_offer_callback( 1 , mInventoryOffer);
+ mInventoryOffer = NULL;
+ }
+}
+
+// static
+void* LLPanelGroupNotices::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupNotices("panel group notices", *group_id);
+}
+
+BOOL LLPanelGroupNotices::isVisibleByAgent(LLAgent* agentp)
+{
+ return mAllowEdit &&
+ agentp->hasPowerInGroup(mGroupID, GP_NOTICES_SEND | GP_NOTICES_RECEIVE);
+}
+
+BOOL LLPanelGroupNotices::postBuild()
+{
+ bool recurse = true;
+
+ mNoticesList = (LLScrollListCtrl*)getChildByName("notice_list",recurse);
+ mNoticesList->setCommitOnSelectionChange(TRUE);
+ mNoticesList->setCommitCallback(onSelectNotice);
+ mNoticesList->setCallbackUserData(this);
+
+ mBtnNewMessage = (LLButton*)getChildByName("create_new_notice",recurse);
+ mBtnNewMessage->setClickedCallback(onClickNewMessage);
+ mBtnNewMessage->setCallbackUserData(this);
+ mBtnNewMessage->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_NOTICES_SEND));
+
+ mBtnGetPastNotices = (LLButton*)getChildByName("refresh_notices",recurse);
+ mBtnGetPastNotices->setClickedCallback(onClickRefreshNotices);
+ mBtnGetPastNotices->setCallbackUserData(this);
+
+ // Create
+ mCreateSubject = (LLLineEditor*)getChildByName("create_subject",recurse);
+ mCreateMessage = (LLTextEditor*)getChildByName("create_message",recurse);
+
+ mCreateInventoryName = (LLLineEditor*)getChildByName("create_inventory_name",recurse);
+ mCreateInventoryName->setTabStop(FALSE);
+ mCreateInventoryName->setEnabled(FALSE);
+
+ mCreateInventoryIcon = (LLIconCtrl*)getChildByName("create_inv_icon",recurse);
+ mCreateInventoryIcon->setVisible(FALSE);
+
+ mBtnSendMessage = (LLButton*)getChildByName("send_notice",recurse);
+ mBtnSendMessage->setClickedCallback(onClickSendMessage);
+ mBtnSendMessage->setCallbackUserData(this);
+
+ mBtnRemoveAttachment = (LLButton*)getChildByName("remove_attachment",recurse);
+ mBtnRemoveAttachment->setClickedCallback(onClickRemoveAttachment);
+ mBtnRemoveAttachment->setCallbackUserData(this);
+ mBtnRemoveAttachment->setEnabled(FALSE);
+
+ // View
+ mViewSubject = (LLLineEditor*)getChildByName("view_subject",recurse);
+ mViewMessage = (LLTextEditor*)getChildByName("view_message",recurse);
+
+ mViewInventoryName = (LLLineEditor*)getChildByName("view_inventory_name",recurse);
+ mViewInventoryName->setTabStop(FALSE);
+ mViewInventoryName->setEnabled(FALSE);
+
+ mViewInventoryIcon = (LLIconCtrl*)getChildByName("view_inv_icon",recurse);
+ mViewInventoryIcon->setVisible(FALSE);
+
+ mBtnOpenAttachment = (LLButton*)getChildByName("open_attachment",recurse);
+ mBtnOpenAttachment->setClickedCallback(onClickOpenAttachment);
+ mBtnOpenAttachment->setCallbackUserData(this);
+
+ LLTextBox *txt = (LLTextBox*) getChildByName("no_notices_text",false);
+ if (txt)
+ {
+ mNoNoticesStr = txt->getText();
+ removeChild(txt);
+ }
+
+ mPanelCreateNotice = (LLPanel*) getChildByName("panel_create_new_notice",recurse);
+ mPanelViewNotice = (LLPanel*) getChildByName("panel_view_past_notice",recurse);
+
+ // Must be in front of all other UI elements.
+ LLPanel* dtv = (LLPanel*)getChildByName("drop_target",recurse);
+ LLGroupDropTarget* target = new LLGroupDropTarget("drop_target",
+ dtv->getRect(),
+ this, mGroupID);
+ target->setEnabled(TRUE);
+ target->setToolTip(dtv->getToolTip());
+
+ mPanelCreateNotice->addChild(target);
+ mPanelCreateNotice->removeChild(dtv);
+
+ arrangeNoticeView(VIEW_PAST_NOTICE);
+
+ return LLPanelGroupTab::postBuild();
+}
+
+void LLPanelGroupNotices::activate()
+{
+ BOOL can_send = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_SEND);
+ BOOL can_receive = gAgent.hasPowerInGroup(mGroupID,GP_NOTICES_RECEIVE);
+
+ mPanelViewNotice->setEnabled(can_receive);
+ mPanelCreateNotice->setEnabled(can_send);
+
+ // Always disabled to stop direct editing of attachment names
+ mCreateInventoryName->setEnabled(FALSE);
+ mViewInventoryName->setEnabled(FALSE);
+
+ // If we can receive notices, grab them right away.
+ if (can_receive)
+ {
+ onClickRefreshNotices(this);
+ }
+}
+
+void LLPanelGroupNotices::setItem(LLPointer<LLInventoryItem> inv_item)
+{
+ mInventoryItem = inv_item;
+
+ LLViewerImage* item_icon = get_item_icon(inv_item->getType(),
+ inv_item->getInventoryType(),
+ inv_item->getFlags());
+
+ mCreateInventoryIcon->setImage(item_icon->getID());
+ mCreateInventoryIcon->setVisible(TRUE);
+
+ std::stringstream ss;
+ ss << " " << mInventoryItem->getName();
+
+ mCreateInventoryName->setText(ss.str());
+ mBtnRemoveAttachment->setEnabled(TRUE);
+}
+
+void LLPanelGroupNotices::onClickRemoveAttachment(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+ self->mInventoryItem = NULL;
+ self->mCreateInventoryName->clear();
+ self->mCreateInventoryIcon->setVisible(FALSE);
+ self->mBtnRemoveAttachment->setEnabled(FALSE);
+}
+
+//static
+void LLPanelGroupNotices::onClickOpenAttachment(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ inventory_offer_callback( 0 , self->mInventoryOffer);
+ self->mInventoryOffer = NULL;
+ self->mBtnOpenAttachment->setEnabled(FALSE);
+}
+
+void LLPanelGroupNotices::onClickSendMessage(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ if (self->mCreateSubject->getText().empty())
+ {
+ // Must supply a subject
+ gViewerWindow->alertXml("MustSpecifyGroupNoticeSubject");
+ return;
+ }
+ send_group_notice(
+ self->mGroupID,
+ self->mCreateSubject->getText().c_str(),
+ self->mCreateMessage->getText().c_str(),
+ self->mInventoryItem);
+
+ self->mCreateMessage->clear();
+ self->mCreateSubject->clear();
+ onClickRemoveAttachment(data);
+
+ self->arrangeNoticeView(VIEW_PAST_NOTICE);
+ onClickRefreshNotices(self);
+
+}
+
+//static
+void LLPanelGroupNotices::onClickNewMessage(void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ self->arrangeNoticeView(CREATE_NEW_NOTICE);
+
+ if (self->mInventoryOffer)
+ {
+ inventory_offer_callback( 1 , self->mInventoryOffer);
+ self->mInventoryOffer = NULL;
+ }
+
+ self->mCreateSubject->clear();
+ self->mCreateMessage->clear();
+ if (self->mInventoryItem) onClickRemoveAttachment(self);
+ self->mNoticesList->deselectAllItems(TRUE); // TRUE == don't commit on chnage
+}
+
+void LLPanelGroupNotices::onClickRefreshNotices(void* data)
+{
+ lldebugs << "LLPanelGroupNotices::onClickGetPastNotices" << llendl;
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ self->mNoticesList->deleteAllItems();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupNoticesListRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID",self->mGroupID);
+ gAgent.sendReliableMessage();
+}
+
+//static
+std::map<LLUUID,LLPanelGroupNotices*> LLPanelGroupNotices::sInstances;
+
+// static
+void LLPanelGroupNotices::processGroupNoticesListReply(LLMessageSystem* msg, void** data)
+{
+ LLUUID group_id;
+ msg->getUUID("AgentData", "GroupID", group_id);
+
+ std::map<LLUUID,LLPanelGroupNotices*>::iterator it = sInstances.find(group_id);
+ if (it == sInstances.end())
+ {
+ llinfos << "Group Panel Notices " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ LLPanelGroupNotices* selfp = it->second;
+ if(!selfp)
+ {
+ llinfos << "Group Panel Notices " << group_id << " no longer in existence."
+ << llendl;
+ return;
+ }
+
+ selfp->processNotices(msg);
+}
+
+void LLPanelGroupNotices::processNotices(LLMessageSystem* msg)
+{
+ LLUUID id;
+ char subj[MAX_STRING];
+ char name[MAX_STRING];
+ U32 timestamp;
+ BOOL has_attachment;
+ U8 asset_type;
+
+ S32 i=0;
+ S32 count = msg->getNumberOfBlocks("Data");
+ for (;i<count;++i)
+ {
+ msg->getUUID("Data","NoticeID",id,i);
+ if (1 == count && id.isNull())
+ {
+ // Only one entry, the dummy entry.
+ mNoticesList->addSimpleItem(mNoNoticesStr,ADD_BOTTOM,FALSE);
+ mNoticesList->setEnabled(FALSE);
+ return;
+ }
+
+ msg->getString("Data","Subject",MAX_STRING,subj,i);
+ msg->getString("Data","FromName",MAX_STRING,name,i);
+ msg->getBOOL("Data","HasAttachment",has_attachment,i);
+ msg->getU8("Data","AssetType",asset_type,i);
+ msg->getU32("Data","Timestamp",timestamp,i);
+ time_t t = timestamp;
+
+ LLSD row;
+ row["id"] = id;
+
+ row["columns"][0]["column"] = "icon";
+ if (has_attachment)
+ {
+ LLUUID icon_id = get_item_icon_uuid(
+ (LLAssetType::EType)asset_type,
+ LLInventoryType::IT_NONE,FALSE);
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = icon_id;
+ }
+
+ row["columns"][1]["column"] = "subject";
+ row["columns"][1]["value"] = subj;
+
+ row["columns"][2]["column"] = "from";
+ row["columns"][2]["value"] = name;
+
+ char buffer[30];
+ build_notice_date(t, buffer);
+ row["columns"][3]["column"] = "date";
+ row["columns"][3]["value"] = buffer;
+
+ snprintf(buffer, 30, "%u", timestamp);
+ row["columns"][4]["column"] = "sort";
+ row["columns"][4]["value"] = buffer;
+
+ mNoticesList->addElement(row, ADD_SORTED);
+ }
+}
+
+void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data)
+{
+ LLPanelGroupNotices* self = (LLPanelGroupNotices*)data;
+
+ if(!self) return;
+ LLScrollListItem* item = self->mNoticesList->getFirstSelected();
+ if (!item) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GroupNoticeRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID",gAgent.getID());
+ msg->addUUID("SessionID",gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("GroupNoticeID",item->getUUID());
+ gAgent.sendReliableMessage();
+
+ lldebugs << "Item " << item->getUUID().getString().c_str() << " selected." << llendl;
+}
+
+void LLPanelGroupNotices::showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer)
+{
+ arrangeNoticeView(VIEW_PAST_NOTICE);
+
+ if(mViewSubject) mViewSubject->setText(subject);
+ if(mViewMessage) mViewMessage->setText(message);
+
+ if (mInventoryOffer)
+ {
+ // Cancel the inventory offer for the previously viewed notice
+ inventory_offer_callback( 1 , mInventoryOffer);
+ mInventoryOffer = NULL;
+ }
+
+ if (inventory_offer)
+ {
+ mInventoryOffer = inventory_offer;
+
+ LLViewerImage* item_icon = get_item_icon(mInventoryOffer->mType,
+ LLInventoryType::IT_TEXTURE,
+ 0);
+
+ mViewInventoryIcon->setImage(item_icon->getID());
+ mViewInventoryIcon->setVisible(TRUE);
+
+ std::stringstream ss;
+ ss << " " << inventory_name;
+
+ mViewInventoryName->setText(ss.str());
+ mBtnOpenAttachment->setEnabled(TRUE);
+ }
+ else
+ {
+ mViewInventoryName->clear();
+ mViewInventoryIcon->setVisible(FALSE);
+ mBtnOpenAttachment->setEnabled(FALSE);
+ }
+}
+
+void LLPanelGroupNotices::arrangeNoticeView(ENoticeView view_type)
+{
+ if (CREATE_NEW_NOTICE == view_type)
+ {
+ mPanelCreateNotice->setVisible(TRUE);
+ mPanelViewNotice->setVisible(FALSE);
+ }
+ else
+ {
+ mPanelCreateNotice->setVisible(FALSE);
+ mPanelViewNotice->setVisible(TRUE);
+ mBtnOpenAttachment->setEnabled(FALSE);
+ }
+}
diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h
new file mode 100644
index 0000000000..62c8f62162
--- /dev/null
+++ b/indra/newview/llpanelgroupnotices.h
@@ -0,0 +1,98 @@
+/**
+ * @file llpanelgroupnotices.h
+ * @brief A panel to display group notices.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPNOTICES_H
+#define LL_LLPANELGROUPNOTICES_H
+
+#include "llpanelgroup.h"
+#include "llmemory.h"
+#include "llinventory.h"
+
+class LLLineEditor;
+class LLTextEditor;
+class LLButton;
+class LLIconCtrl;
+class LLCheckBoxCtrl;
+class LLScrollListCtrl;
+
+class LLPanelGroupNotices : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupNotices(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupNotices();
+
+ // LLPanelGroupTab
+ static void* createTab(void* data);
+ virtual void activate();
+ //virtual bool needsApply(LLString& mesg);
+ //virtual bool apply(LLString& mesg);
+ //virtual void update();
+
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ void setItem(LLPointer<LLInventoryItem> inv_item);
+
+ static void processGroupNoticesListReply(LLMessageSystem* msg, void** data);
+
+ void showNotice(const char* subject,
+ const char* message,
+ const bool& has_inventory,
+ const char* inventory_name,
+ LLOfferInfo* inventory_offer);
+
+private:
+ static void onClickRemoveAttachment(void* data);
+ static void onClickOpenAttachment(void* data);
+ static void onClickSendMessage(void* data);
+ static void onClickNewMessage(void* data);
+ static void onClickRefreshNotices(void* data);
+
+ void processNotices(LLMessageSystem* msg);
+ static void onSelectNotice(LLUICtrl* ctrl, void* data);
+
+ enum ENoticeView
+ {
+ VIEW_PAST_NOTICE,
+ CREATE_NEW_NOTICE
+ };
+
+ void arrangeNoticeView(ENoticeView view_type);
+
+ LLPointer<LLInventoryItem> mInventoryItem;
+
+ LLLineEditor *mCreateSubject;
+ LLLineEditor *mCreateInventoryName;
+ LLTextEditor *mCreateMessage;
+
+ LLLineEditor *mViewSubject;
+ LLLineEditor *mViewInventoryName;
+ LLTextEditor *mViewMessage;
+
+ LLButton *mBtnSendMessage;
+ LLButton *mBtnNewMessage;
+ LLButton *mBtnRemoveAttachment;
+ LLButton *mBtnOpenAttachment;
+ LLButton *mBtnGetPastNotices;
+
+ LLPanel *mPanelCreateNotice;
+ LLPanel *mPanelViewNotice;
+
+ LLIconCtrl *mCreateInventoryIcon;
+ LLIconCtrl *mViewInventoryIcon;
+
+ LLScrollListCtrl *mNoticesList;
+
+ std::string mNoNoticesStr;
+
+ LLOfferInfo* mInventoryOffer;
+
+ static std::map<LLUUID,LLPanelGroupNotices*> sInstances;
+};
+
+#endif
diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp
new file mode 100644
index 0000000000..ccec55efce
--- /dev/null
+++ b/indra/newview/llpanelgrouproles.cpp
@@ -0,0 +1,2642 @@
+/**
+ * @file llpanelgrouproles.cpp
+ * @brief Panel for roles information about a particular group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llcheckboxctrl.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloateravatarinfo.h"
+#include "llfloatergroupinvite.h"
+#include "lliconctrl.h"
+#include "lllineeditor.h"
+#include "llnamelistctrl.h"
+#include "llnotify.h"
+#include "llpanelgrouproles.h"
+#include "llscrolllistctrl.h"
+#include "lltabcontainer.h"
+#include "lltextbox.h"
+#include "lltexteditor.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llfocusmgr.h"
+
+#include "roles_constants.h"
+
+bool agentCanRemoveFromRole(const LLUUID& group_id,
+ const LLUUID& role_id)
+{
+ return gAgent.hasPowerInGroup(group_id, GP_ROLE_REMOVE_MEMBER);
+}
+
+bool agentCanAddToRole(const LLUUID& group_id,
+ const LLUUID& role_id)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(group_id);
+ if (!gdatap)
+ {
+ llwarns << "agentCanAddToRole "
+ << "-- No group data!" << llendl;
+ return false;
+ }
+
+ //make sure the agent is in the group
+ LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
+ if (mi == gdatap->mMembers.end())
+ {
+ return false;
+ }
+
+ LLGroupMemberData* member_data = (*mi).second;
+
+ // Owners can add to any role.
+ if ( member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ return true;
+ }
+
+ // 'Limited assign members' can add to roles the user is in.
+ if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER_LIMITED) &&
+ member_data->isInRole(role_id) )
+ {
+ return true;
+ }
+
+ // 'assign members' can add to non-owner roles.
+ if ( gAgent.hasPowerInGroup(group_id, GP_ROLE_ASSIGN_MEMBER) &&
+ role_id != gdatap->mOwnerRole )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+// static
+void* LLPanelGroupRoles::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupRoles("panel group roles", *group_id);
+}
+
+LLPanelGroupRoles::LLPanelGroupRoles(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mCurrentTab(NULL),
+ mRequestedTab( NULL ),
+ mSubTabContainer( NULL ),
+ mFirstUse( TRUE ),
+ mIgnoreTransition( FALSE )
+{
+}
+
+LLPanelGroupRoles::~LLPanelGroupRoles()
+{
+ int i;
+ for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
+ {
+ LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);
+
+ subtabp->removeObserver(this);
+ }
+}
+
+BOOL LLPanelGroupRoles::postBuild()
+{
+ lldebugs << "LLPanelGroupRoles::postBuild()" << llendl;
+
+ mSubTabContainer = (LLTabContainerCommon*) getChildByName("roles_tab_container");
+
+ if (!mSubTabContainer) return FALSE;
+
+ // Hook up each sub-tabs callback and widgets.
+ S32 i;
+ for (i = 0; i < mSubTabContainer->getTabCount(); ++i)
+ {
+ LLPanelGroupSubTab* subtabp = (LLPanelGroupSubTab*) mSubTabContainer->getPanelByIndex(i);
+
+ // Add click callbacks to all the tabs.
+ mSubTabContainer->setTabChangeCallback(subtabp, onClickSubTab);
+ mSubTabContainer->setTabUserData(subtabp, this);
+
+ // Hand the subtab a pointer to this LLPanelGroupRoles, so that it can
+ // look around for the widgets it is interested in.
+ if (!subtabp->postBuildSubTab(this)) return FALSE;
+
+ subtabp->addObserver(this);
+ }
+
+ // Set the current tab to whatever is currently being shown.
+ mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!mCurrentTab)
+ {
+ // Need to select a tab.
+ mSubTabContainer->selectFirstTab();
+ mCurrentTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ }
+
+ if (!mCurrentTab) return FALSE;
+
+ // Act as though this tab was just activated.
+ mCurrentTab->activate();
+
+ // Read apply text from the xml file.
+ LLTextBox* txt;
+ // Don't recurse for this, since we don't currently have a recursive removeChild()
+ txt = (LLTextBox*)getChildByName("default_needs_apply_text");
+ if (txt)
+ {
+ mDefaultNeedsApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+ txt = (LLTextBox*)getChildByName("want_apply_text");
+ if (txt)
+ {
+ mWantApplyMesg = txt->getText();
+ removeChild(txt);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+BOOL LLPanelGroupRoles::isVisibleByAgent(LLAgent* agentp)
+{
+ /* This power was removed to make group roles simpler
+ return agentp->hasPowerInGroup(mGroupID,
+ GP_ROLE_CREATE |
+ GP_ROLE_DELETE |
+ GP_ROLE_PROPERTIES |
+ GP_ROLE_VIEW |
+ GP_ROLE_ASSIGN_MEMBER |
+ GP_ROLE_REMOVE_MEMBER |
+ GP_ROLE_CHANGE_ACTIONS |
+ GP_MEMBER_INVITE |
+ GP_MEMBER_EJECT |
+ GP_MEMBER_OPTIONS );
+ */
+ return mAllowEdit && agentp->isInGroup(mGroupID);
+
+}
+
+// static
+void LLPanelGroupRoles::onClickSubTab(void* user_data, bool from_click)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ self->handleClickSubTab();
+}
+
+void LLPanelGroupRoles::handleClickSubTab()
+{
+ // If we are already handling a transition,
+ // ignore this.
+ if (mIgnoreTransition)
+ {
+ return;
+ }
+
+ mRequestedTab = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+
+ // Make sure they aren't just clicking the same tab...
+ if (mRequestedTab == mCurrentTab)
+ {
+ return;
+ }
+
+ // Try to switch from the current panel to the panel the user selected.
+ attemptTransition();
+}
+
+BOOL LLPanelGroupRoles::attemptTransition()
+{
+ // Check if the current tab needs to be applied.
+ LLString mesg;
+ if (mCurrentTab && mCurrentTab->needsApply(mesg))
+ {
+ // If no message was provided, give a generic one.
+ if (mesg.empty())
+ {
+ mesg = mDefaultNeedsApplyMesg;
+ }
+ // Create a notify box, telling the user about the unapplied tab.
+ LLString::format_map_t args;
+ args["[NEEDS_APPLY_MESSAGE]"] = mesg;
+ args["[WANT_APPLY_MESSAGE]"] = mWantApplyMesg;
+ gViewerWindow->alertXml("PanelGroupApply", args,
+ onNotifyCallback, (void*) this);
+ mHasModal = TRUE;
+ // We need to reselect the current tab, since it isn't finished.
+ if (mSubTabContainer)
+ {
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mCurrentTab );
+ mIgnoreTransition = FALSE;
+ }
+ // Returning FALSE will block a close action from finishing until
+ // we get a response back from the user.
+ return FALSE;
+ }
+ else
+ {
+ // The current panel didn't have anything it needed to apply.
+ if (mRequestedTab)
+ {
+ transitionToTab();
+ }
+ return TRUE;
+ }
+}
+
+void LLPanelGroupRoles::transitionToTab()
+{
+ // Tell the current panel that it is being deactivated.
+ if (mCurrentTab)
+ {
+ mCurrentTab->deactivate();
+ }
+
+ // Tell the new panel that it is being activated.
+ if (mRequestedTab)
+ {
+ // This is now the current tab;
+ mCurrentTab = mRequestedTab;
+ mCurrentTab->activate();
+ }
+}
+
+// static
+void LLPanelGroupRoles::onNotifyCallback(S32 option, void* user_data)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ if (self)
+ {
+ self->handleNotifyCallback(option);
+ }
+}
+
+void LLPanelGroupRoles::handleNotifyCallback(S32 option)
+{
+ mHasModal = FALSE;
+ switch (option)
+ {
+ case 0: // "Apply Changes"
+ {
+ // Try to apply changes, and switch to the requested tab.
+ LLString apply_mesg;
+ if ( !apply( apply_mesg ) )
+ {
+ // There was a problem doing the apply.
+ if ( !apply_mesg.empty() )
+ {
+ mHasModal = TRUE;
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = apply_mesg;
+ gViewerWindow->alertXml("GenericAlert", args, onModalClose, (void*) this);
+ }
+ // Skip switching tabs.
+ break;
+ }
+
+ // This panel's info successfully applied.
+ // Switch to the next panel.
+ // No break! Continue into 'Ignore Changes' which just switches tabs.
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ }
+ case 1: // "Ignore Changes"
+ // Switch to the requested panel without applying changes
+ cancel();
+ mIgnoreTransition = TRUE;
+ mSubTabContainer->selectTabPanel( mRequestedTab );
+ mIgnoreTransition = FALSE;
+ transitionToTab();
+ break;
+ case 2: // "Cancel"
+ default:
+ // Do nothing. The user is canceling the action.
+ break;
+ }
+}
+
+// static
+void LLPanelGroupRoles::onModalClose(S32 option, void* user_data)
+{
+ LLPanelGroupRoles* self = static_cast<LLPanelGroupRoles*>(user_data);
+ if (self)
+ {
+ self->mHasModal = FALSE;
+ }
+}
+
+
+bool LLPanelGroupRoles::apply(LLString& mesg)
+{
+ // Pass this along to the currently visible sub tab.
+ if (!mSubTabContainer) return false;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ // Ignore the needs apply message.
+ LLString ignore_mesg;
+ if ( !panelp->needsApply(ignore_mesg) )
+ {
+ // We don't need to apply anything.
+ // We're done.
+ return true;
+ }
+
+ // Try to do the actual apply.
+ return panelp->apply(mesg);
+}
+
+void LLPanelGroupRoles::cancel()
+{
+ // Pass this along to the currently visible sub tab.
+ if (!mSubTabContainer) return;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return;
+
+ panelp->cancel();
+}
+
+// Pass all of these messages to the currently visible sub tab.
+LLString LLPanelGroupRoles::getHelpText() const
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp)
+ {
+ return panelp->getHelpText();
+ }
+ else
+ {
+ return mHelpText;
+ }
+}
+
+void LLPanelGroupRoles::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp)
+ {
+ panelp->update(gc);
+ }
+ else
+ {
+ llwarns << "LLPanelGroupRoles::update() -- No subtab to update!" << llendl;
+ }
+}
+
+void LLPanelGroupRoles::activate()
+{
+ // Start requesting member and role data if needed.
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ //if (!gdatap || mFirstUse)
+ {
+ // Check member data.
+
+ if (!gdatap || !gdatap->isMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ // Check role data.
+ if (!gdatap || !gdatap->isRoleDataComplete() )
+ {
+ // Mildly hackish - clear all pending changes
+ cancel();
+
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+
+ // Check role-member mapping data.
+ if (!gdatap || !gdatap->isRoleMemberDataComplete() )
+ {
+ gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
+ }
+
+ // Need this to get base group member powers
+ if (!gdatap || !gdatap->isGroupPropertiesDataComplete() )
+ {
+ gGroupMgr->sendGroupPropertiesRequest(mGroupID);
+ }
+
+ mFirstUse = FALSE;
+ }
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp) panelp->activate();
+}
+
+void LLPanelGroupRoles::deactivate()
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (panelp) panelp->deactivate();
+}
+
+bool LLPanelGroupRoles::needsApply(LLString& mesg)
+{
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return false;
+
+ return panelp->needsApply(mesg);
+}
+
+BOOL LLPanelGroupRoles::hasModal()
+{
+ if (mHasModal) return TRUE;
+
+ LLPanelGroupTab* panelp = (LLPanelGroupTab*) mSubTabContainer->getCurrentPanel();
+ if (!panelp) return FALSE;
+
+ return panelp->hasModal();
+}
+
+// PanelGroupTab observer trigger
+void LLPanelGroupRoles::tabChanged()
+{
+ notifyObservers();
+}
+
+////////////////////////////
+// LLPanelGroupSubTab
+////////////////////////////
+LLPanelGroupSubTab::LLPanelGroupSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupTab(name, group_id),
+ mHeader(NULL),
+ mFooter(NULL),
+ mSearchLineEditor(NULL),
+ mSearchButton(NULL),
+ mShowAllButton(NULL)
+{
+}
+
+LLPanelGroupSubTab::~LLPanelGroupSubTab()
+{
+}
+
+BOOL LLPanelGroupSubTab::postBuild()
+{
+ // Hook up the search widgets.
+ bool recurse = true;
+ mSearchLineEditor = (LLLineEditor*) getChildByName("search_text", recurse);
+
+ if (!mSearchLineEditor) return FALSE;
+ mSearchLineEditor->setKeystrokeCallback(onSearchKeystroke);
+ mSearchLineEditor->setCallbackUserData(this);
+
+ mSearchButton = (LLButton*) getChildByName("search_button", recurse);
+
+ if (!mSearchButton) return FALSE;
+ mSearchButton->setClickedCallback(onClickSearch);
+ mSearchButton->setCallbackUserData(this);
+ mSearchButton->setEnabled(FALSE);
+
+ mShowAllButton = (LLButton*) getChildByName("show_all_button", recurse);
+
+ if (!mShowAllButton) return FALSE;
+ mShowAllButton->setClickedCallback(onClickShowAll);
+ mShowAllButton->setCallbackUserData(this);
+ mShowAllButton->setEnabled(FALSE);
+
+ // Get icons for later use.
+ mActionIcons.clear();
+
+ bool no_recurse = false;
+
+ LLIconCtrl* icon = (LLIconCtrl*) getChildByName("power_folder_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["folder"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ icon = (LLIconCtrl*) getChildByName("power_all_have_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["full"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ icon = (LLIconCtrl*) getChildByName("power_partial_icon",no_recurse);
+ if (icon && icon->getImage().notNull())
+ {
+ mActionIcons["partial"] = icon->getImage();
+ removeChild(icon);
+ }
+
+ return LLPanelGroupTab::postBuild();
+}
+
+// static
+void LLPanelGroupSubTab::onSearchKeystroke(LLLineEditor* caller, void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleSearchKeystroke(caller);
+}
+
+void LLPanelGroupSubTab::handleSearchKeystroke(LLLineEditor* caller)
+{
+ if (caller->getText().size())
+ {
+ setDefaultBtn( mSearchButton );
+ mSearchButton->setEnabled(TRUE);
+ }
+ else
+ {
+ setDefaultBtn( NULL );
+ mSearchButton->setEnabled(FALSE);
+ }
+}
+
+// static
+void LLPanelGroupSubTab::onClickSearch(void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleClickSearch();
+}
+
+void LLPanelGroupSubTab::handleClickSearch()
+{
+ lldebugs << "LLPanelGroupSubTab::handleClickSearch()" << llendl;
+
+ if (0 == mSearchLineEditor->getText().size())
+ {
+ // No search text. (This shouldn't happen... the search button should have been disabled).
+ llwarns << "handleClickSearch with no search text!" << llendl;
+ mSearchButton->setEnabled(FALSE);
+ return;
+ }
+
+ setSearchFilter( mSearchLineEditor->getText() );
+ mShowAllButton->setEnabled(TRUE);
+}
+
+// static
+void LLPanelGroupSubTab::onClickShowAll(void* user_data)
+{
+ LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data);
+ self->handleClickShowAll();
+}
+
+void LLPanelGroupSubTab::handleClickShowAll()
+{
+ lldebugs << "LLPanelGroupSubTab::handleClickShowAll()" << llendl;
+ setSearchFilter( LLString::null );
+ mShowAllButton->setEnabled(FALSE);
+}
+
+void LLPanelGroupSubTab::setSearchFilter(const LLString& filter)
+{
+ lldebugs << "LLPanelGroupSubTab::setSearchFilter() ==> '" << filter << "'" << llendl;
+ mSearchFilter = filter;
+ LLString::toLower(mSearchFilter);
+ update(GC_ALL);
+}
+
+void LLPanelGroupSubTab::activate()
+{
+ lldebugs << "LLPanelGroupSubTab::activate()" << llendl;
+ setOthersVisible(TRUE);
+}
+
+void LLPanelGroupSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupSubTab::deactivate()" << llendl;
+ setOthersVisible(FALSE);
+}
+
+void LLPanelGroupSubTab::setOthersVisible(BOOL b)
+{
+ if (mHeader)
+ {
+ mHeader->setVisible( b );
+ }
+ else
+ {
+ llwarns << "LLPanelGroupSubTab missing header!" << llendl;
+ }
+
+ if (mFooter)
+ {
+ mFooter->setVisible( b );
+ }
+ else
+ {
+ llwarns << "LLPanelGroupSubTab missing footer!" << llendl;
+ }
+}
+
+bool LLPanelGroupSubTab::matchesActionSearchFilter(std::string action)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ LLString::toLower(action);
+ std::string::size_type match = action.find(mSearchFilter);
+
+ if (std::string::npos == match)
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LLPanelGroupSubTab::buildActionsList(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role)
+{
+ if (gGroupMgr->mRoleActionSets.empty())
+ {
+ llwarns << "Can't build action list - no actions found." << llendl;
+ return;
+ }
+
+ std::vector<LLRoleActionSet*>::iterator ras_it = gGroupMgr->mRoleActionSets.begin();
+ std::vector<LLRoleActionSet*>::iterator ras_end = gGroupMgr->mRoleActionSets.end();
+
+ for ( ; ras_it != ras_end; ++ras_it)
+ {
+ buildActionCategory(ctrl,
+ allowed_by_some,
+ allowed_by_all,
+ (*ras_it),
+ icons,
+ commit_callback,
+ show_all,
+ filter,
+ is_owner_role);
+ }
+}
+
+void LLPanelGroupSubTab::buildActionCategory(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ LLRoleActionSet* action_set,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role)
+{
+ lldebugs << "Building role list for: " << action_set->mActionSetData->mName << llendl;
+ // See if the allow mask matches anything in this category.
+ if (show_all || (allowed_by_some & action_set->mActionSetData->mPowerBit))
+ {
+ // List all the actions in this category that at least some members have.
+ LLSD row;
+
+ row["columns"][0]["column"] = "icon";
+ icon_map_t::iterator iter = icons.find("folder");
+ if (iter != icons.end())
+ {
+ row["columns"][0]["type"] = "icon";
+ row["columns"][0]["value"] = (*iter).second;
+ }
+
+ row["columns"][1]["column"] = "action";
+ row["columns"][1]["value"] = action_set->mActionSetData->mName;
+ row["columns"][1]["font-style"] = "BOLD";
+
+ LLScrollListItem* title_row = ctrl->addElement(row, ADD_BOTTOM, action_set->mActionSetData);
+
+ bool category_matches_filter = (filter) ? matchesActionSearchFilter(action_set->mActionSetData->mName) : true;
+
+ std::vector<LLRoleAction*>::iterator ra_it = action_set->mActions.begin();
+ std::vector<LLRoleAction*>::iterator ra_end = action_set->mActions.end();
+
+ bool items_match_filter = false;
+ BOOL can_change_actions = (!is_owner_role && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CHANGE_ACTIONS));
+
+ for ( ; ra_it != ra_end; ++ra_it)
+ {
+ // See if anyone has these action.
+ if (!show_all && !(allowed_by_some & (*ra_it)->mPowerBit))
+ {
+ continue;
+ }
+
+ // See if we are filtering out these actions
+ // If we aren't using filters, category_matches_filter will be true.
+ if (!category_matches_filter
+ && !matchesActionSearchFilter((*ra_it)->mDescription))
+ {
+ continue;
+ }
+
+ items_match_filter = true;
+
+ // See if everyone has these actions.
+ bool show_full_strength = false;
+ if ( (allowed_by_some & (*ra_it)->mPowerBit) == (allowed_by_all & (*ra_it)->mPowerBit) )
+ {
+ show_full_strength = true;
+ }
+
+ LLSD row;
+
+ S32 column_index = 0;
+ row["columns"][column_index]["column"] = "icon";
+ ++column_index;
+
+
+ S32 check_box_index = -1;
+ if (commit_callback)
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "checkbox";
+ row["columns"][column_index]["value"] = (*ra_it)->mName;
+ check_box_index = column_index;
+ ++column_index;
+ }
+ else
+ {
+ if (show_full_strength)
+ {
+ icon_map_t::iterator iter = icons.find("full");
+ if (iter != icons.end())
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "icon";
+ row["columns"][column_index]["value"] = (*iter).second;
+ ++column_index;
+ }
+ }
+ else
+ {
+ icon_map_t::iterator iter = icons.find("partial");
+ if (iter != icons.end())
+ {
+ row["columns"][column_index]["column"] = "checkbox";
+ row["columns"][column_index]["type"] = "icon";
+ row["columns"][column_index]["value"] = (*iter).second;
+ ++column_index;
+ }
+ row["enabled"] = false;
+ }
+ }
+
+ row["columns"][column_index]["column"] = "action";
+ row["columns"][column_index]["value"] = (*ra_it)->mDescription;
+ row["columns"][column_index]["font"] = "SANSSERIFSMALL";
+
+ LLScrollListItem* item = ctrl->addElement(row, ADD_BOTTOM, (*ra_it));
+
+ if (-1 != check_box_index)
+ {
+ // Extract the checkbox that was created.
+ LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(check_box_index);
+ LLCheckBoxCtrl* check = check_cell->getCheckBox();
+ check->setEnabled(can_change_actions);
+ check->setCommitCallback(commit_callback);
+ check->setCallbackUserData(ctrl->getCallbackUserData());
+ check->setToolTip( check->getLabel() );
+
+ if (show_all)
+ {
+ check->setTentative(FALSE);
+ if (allowed_by_some & (*ra_it)->mPowerBit)
+ {
+ check->set(TRUE);
+ }
+ else
+ {
+ check->set(FALSE);
+ }
+ }
+ else
+ {
+ check->set(TRUE);
+ if (show_full_strength)
+ {
+ check->setTentative(FALSE);
+ }
+ else
+ {
+ check->setTentative(TRUE);
+ }
+ }
+ }
+ }
+
+ if (!items_match_filter)
+ {
+ S32 title_index = ctrl->getItemIndex(title_row);
+ ctrl->deleteSingleItem(title_index);
+ }
+ }
+}
+
+void LLPanelGroupSubTab::setFooterEnabled(BOOL enable)
+{
+ if (mFooter)
+ {
+ mFooter->setAllChildrenEnabled(enable);
+ }
+}
+
+////////////////////////////
+// LLPanelGroupMembersSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupMembersSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupMembersSubTab("panel group members sub tab", *group_id);
+}
+
+LLPanelGroupMembersSubTab::LLPanelGroupMembersSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id),
+ mMembersList(NULL),
+ mAssignedRolesList(NULL),
+ mAllowedActionsList(NULL),
+ mChanged(FALSE),
+ mPendingMemberUpdate(FALSE),
+ mHasMatch(FALSE),
+ mNumOwnerAdditions(0)
+{
+}
+
+LLPanelGroupMembersSubTab::~LLPanelGroupMembersSubTab()
+{
+}
+
+BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("members_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("members_footer", recurse);
+
+ mMembersList = (LLNameListCtrl*) parent->getChildByName("member_list", recurse);
+ mAssignedRolesList = (LLScrollListCtrl*) parent->getChildByName("member_assigned_roles", recurse);
+ mAllowedActionsList = (LLScrollListCtrl*) parent->getChildByName("member_allowed_actions", recurse);
+
+ if (!mMembersList || !mAssignedRolesList || !mAllowedActionsList) return FALSE;
+
+ // We want to be notified whenever a member is selected.
+ mMembersList->setCallbackUserData(this);
+ mMembersList->setCommitOnSelectionChange(TRUE);
+ mMembersList->setCommitCallback(onMemberSelect);
+ // Show the member's profile on double click.
+ mMembersList->setDoubleClickCallback(onMemberDoubleClick);
+
+ LLButton* button = (LLButton*) parent->getChildByName("member_invite", recurse);
+ if ( button )
+ {
+ button->setClickedCallback(onInviteMember);
+ button->setCallbackUserData(this);
+ button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE));
+ }
+
+ mEjectBtn = (LLButton*) parent->getChildByName("member_eject", recurse);
+ if ( mEjectBtn )
+ {
+ mEjectBtn->setClickedCallback(onEjectMembers);
+ mEjectBtn->setCallbackUserData(this);
+ mEjectBtn->setEnabled(FALSE);
+ }
+
+ return TRUE;
+}
+
+
+// static
+void LLPanelGroupMembersSubTab::onMemberSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ self->handleMemberSelect();
+}
+
+void LLPanelGroupMembersSubTab::handleMemberSelect()
+{
+ lldebugs << "LLPanelGroupMembersSubTab::handleMemberSelect" << llendl;
+
+ mAssignedRolesList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::handleMemberSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ // Check if there is anything selected.
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ // Build a vector of all selected members, and gather allowed actions.
+ std::vector<LLUUID> selected_members;
+ U64 allowed_by_all = 0xffffffffffffLL;
+ U64 allowed_by_some = 0;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin();
+ itor != selection.end(); ++itor)
+ {
+ selected_members.push_back( (*itor)->getUUID() );
+ // Get this member's power mask including any unsaved changes
+
+ U64 powers = getAgentPowersBasedOnRoleChanges((*itor)->getUUID());
+
+ allowed_by_all &= powers;
+ allowed_by_some |= powers;
+ }
+ std::sort(selected_members.begin(), selected_members.end());
+
+ //////////////////////////////////
+ // Build the allowed actions list.
+ //////////////////////////////////
+ buildActionsList(mAllowedActionsList,
+ allowed_by_some,
+ allowed_by_all,
+ mActionIcons,
+ NULL,
+ FALSE,
+ FALSE,
+ FALSE);
+
+ //////////////////////////////////
+ // Build the assigned roles list.
+ //////////////////////////////////
+ // Add each role to the assigned roles list.
+ LLGroupMgrGroupData::role_iter iter = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ BOOL can_eject_members = gAgent.hasPowerInGroup(mGroupID,
+ GP_MEMBER_EJECT);
+ BOOL member_is_owner = FALSE;
+
+ for( ; iter != end; ++iter)
+ {
+ // Count how many selected users are in this role.
+ const LLUUID& role_id = iter->first;
+ LLGroupRoleData* group_role_data = iter->second;
+
+ if (group_role_data)
+ {
+ const BOOL needs_sort = FALSE;
+ S32 count = group_role_data->getMembersInRole(
+ selected_members, needs_sort);
+ //check if the user has permissions to assign/remove
+ //members to/from the role (but the ability to add/remove
+ //should only be based on the "saved" changes to the role
+ //not in the temp/meta data. -jwolk
+ BOOL cb_enable = ( (count > 0) ?
+ agentCanRemoveFromRole(mGroupID, role_id) :
+ agentCanAddToRole(mGroupID, role_id) );
+
+
+ // Owner role has special enabling permissions for removal.
+ if (cb_enable && (count > 0) && role_id == gdatap->mOwnerRole)
+ {
+ // Check if any owners besides this agent are selected.
+ std::vector<LLUUID>::const_iterator member_iter;
+ std::vector<LLUUID>::const_iterator member_end =
+ selected_members.end();
+ for (member_iter = selected_members.begin();
+ member_iter != member_end;
+ ++member_iter)
+ {
+ // Don't count the agent.
+ if ((*member_iter) == gAgent.getID()) continue;
+
+ // Look up the member data.
+ LLGroupMgrGroupData::member_iter mi =
+ gdatap->mMembers.find((*member_iter));
+ if (mi == gdatap->mMembers.end()) continue;
+ LLGroupMemberData* member_data = (*mi).second;
+ // Is the member an owner?
+ if ( member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ // Can't remove other owners.
+ cb_enable = FALSE;
+ break;
+ }
+ }
+ }
+
+ //now see if there are any role changes for the selected
+ //members and remember to include them
+ std::vector<LLUUID>::iterator sel_mem_iter = selected_members.begin();
+ for (; sel_mem_iter != selected_members.end(); sel_mem_iter++)
+ {
+ LLRoleMemberChangeType type;
+ if ( getRoleChangeType(*sel_mem_iter, role_id, type) )
+ {
+ if ( type == RMC_ADD ) count++;
+ else if ( type == RMC_REMOVE ) count--;
+ }
+ }
+
+ // If anyone selected is in any role besides 'Everyone' then they can't be ejected.
+ if (role_id.notNull() && (count > 0))
+ {
+ can_eject_members = FALSE;
+ if (role_id == gdatap->mOwnerRole)
+ {
+ member_is_owner = TRUE;
+ }
+ }
+
+ LLRoleData rd;
+ if (gdatap->getRoleData(role_id,rd))
+ {
+ std::ostringstream label;
+ label << rd.mRoleName;
+ // Don't bother showing a count, if there is only 0 or 1.
+ if (count > 1)
+ {
+ label << ": " << count ;
+ }
+
+ LLSD row;
+ row["id"] = role_id;
+
+ row["columns"][0]["column"] = "checkbox";
+ row["columns"][0]["type"] = "checkbox";
+
+ row["columns"][1]["column"] = "role";
+ row["columns"][1]["value"] = label.str();
+
+ if (row["id"].asUUID().isNull())
+ {
+ // This is the everyone role, you can't take people out of the everyone role!
+ row["enabled"] = false;
+ }
+
+ LLScrollListItem* item = mAssignedRolesList->addElement(row);
+
+ // Extract the checkbox that was created.
+ LLScrollListCheck* check_cell = (LLScrollListCheck*) item->getColumn(0);
+ LLCheckBoxCtrl* check = check_cell->getCheckBox();
+ check->setCommitCallback(onRoleCheck);
+ check->setCallbackUserData(this);
+ check->set( count > 0 );
+ check->setTentative(0 != count && selected_members.size() != count);
+
+ //NOTE: as of right now a user can break the group
+ //by removing himself from a role if he is the
+ //last owner. We should check for this special case
+ // -jwolk
+ check->setEnabled(cb_enable);
+ }
+ }
+ else
+ {
+ // This could happen if changes are not synced right on sub-panel change.
+ llwarns << "No group role data for " << iter->second << llendl;
+ }
+ }
+ mAssignedRolesList->setEnabled(TRUE);
+
+ if (!can_eject_members && !member_is_owner)
+ {
+ // Maybe we can eject them because we are an owner...
+ LLGroupMgrGroupData::member_iter mi = gdatap->mMembers.find(gAgent.getID());
+ if (mi != gdatap->mMembers.end())
+ {
+ LLGroupMemberData* member_data = (*mi).second;
+
+ if ( member_data && member_data->isInRole(gdatap->mOwnerRole) )
+ {
+ can_eject_members = TRUE;
+ }
+ }
+ }
+
+ mEjectBtn->setEnabled(can_eject_members);
+}
+
+// static
+void LLPanelGroupMembersSubTab::onMemberDoubleClick(void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ self->handleMemberDoubleClick();
+}
+
+//static
+void LLPanelGroupMembersSubTab::onInviteMember(void *userdata)
+{
+ LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;
+
+ if ( selfp )
+ {
+ selfp->handleInviteMember();
+ }
+}
+
+void LLPanelGroupMembersSubTab::handleInviteMember()
+{
+ LLFloaterGroupInvite::showForGroup(mGroupID);
+}
+
+void LLPanelGroupMembersSubTab::onEjectMembers(void *userdata)
+{
+ LLPanelGroupMembersSubTab* selfp = (LLPanelGroupMembersSubTab*) userdata;
+
+ if ( selfp )
+ {
+ selfp->handleEjectMembers();
+ }
+}
+
+void LLPanelGroupMembersSubTab::handleEjectMembers()
+{
+ //send down an eject message
+ std::vector<LLUUID> selected_members;
+
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ selected_members.push_back((*itor)->getUUID());
+ }
+
+ mMembersList->deleteSelectedItems();
+
+ gGroupMgr->sendGroupMemberEjects(mGroupID,
+ selected_members);
+}
+
+void LLPanelGroupMembersSubTab::handleRoleCheck(const LLUUID& role_id,
+ LLRoleMemberChangeType type)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap) return;
+
+ //add that the user is requesting to change the roles for selected
+ //members
+ U64 powers_all_have = 0xffffffffffffLL;
+ U64 powers_some_have = 0;
+
+ BOOL is_owner_role = ( gdatap->mOwnerRole == role_id );
+ LLUUID member_id;
+
+
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member;
+ role_change_data_map_t *role_change_datap;
+ role_change_data_map_t::iterator role_end;
+ role_change_data_map_t::iterator role;
+
+ std::vector<LLScrollListItem*> selection = mMembersList->getAllSelected();
+ if (selection.empty()) return;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ member_id = (*itor)->getUUID();
+
+ //see if we requested a change for this member before
+ member = mMemberRoleChangeData.find(member_id);
+ if ( member != member_end )
+ {
+ //this member had previously had their role data changed
+ //so grab it
+ role_change_datap = (*member).second;
+ }
+ else
+ {
+ role_change_datap = new role_change_data_map_t;
+ mMemberRoleChangeData[member_id] = role_change_datap;
+ }
+
+ //now check to see if the selected group member
+ //had changed his association with the selected role before
+ role_end = role_change_datap->end();
+ role = role_change_datap->find(role_id);
+
+ if ( role != role_end )
+ {
+ //see if the new change type cancels out the previous change
+ if (role->second != type)
+ {
+ role_change_datap->erase(role_id);
+ if ( is_owner_role ) mNumOwnerAdditions--;
+ }
+ //else do nothing
+
+ if ( role_change_datap->empty() )
+ {
+ //the current member now has no role changes
+ //so erase the role change and erase the member's entry
+ delete role_change_datap;
+ role_change_datap = NULL;
+
+ mMemberRoleChangeData.erase(member_id);
+ }
+ }
+ else
+ {
+ //a previously unchanged role is being changed
+ (*role_change_datap)[role_id] = type;
+ if ( is_owner_role && type == RMC_ADD ) mNumOwnerAdditions++;
+ }
+
+ //we need to calculate what powers the selected members
+ //have (including the role changes we're making)
+ //so that we can rebuild the action list
+ U64 new_powers = getAgentPowersBasedOnRoleChanges(member_id);
+
+ powers_all_have &= new_powers;
+ powers_some_have |= new_powers;
+ }
+
+
+ mChanged = !mMemberRoleChangeData.empty();
+ notifyObservers();
+
+ //alrighty now we need to update the actions list
+ //to reflect the changes
+ mAllowedActionsList->deleteAllItems();
+ buildActionsList(mAllowedActionsList,
+ powers_some_have,
+ powers_all_have,
+ mActionIcons,
+ NULL,
+ FALSE,
+ FALSE,
+ FALSE);
+}
+
+
+// static
+void LLPanelGroupMembersSubTab::onRoleCheck(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupMembersSubTab* self = static_cast<LLPanelGroupMembersSubTab*>(user_data);
+ LLCheckBoxCtrl* check_box = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check_box || !self) return;
+
+
+ LLUUID role_id = self->mAssignedRolesList->getFirstSelected()->getUUID();
+ LLRoleMemberChangeType change_type = (check_box->get() ?
+ RMC_ADD :
+ RMC_REMOVE);
+
+ self->handleRoleCheck(role_id, change_type);
+}
+
+void LLPanelGroupMembersSubTab::handleMemberDoubleClick()
+{
+ LLScrollListItem* selected = mMembersList->getFirstSelected();
+ if (selected)
+ {
+ LLFloaterAvatarInfo::showFromDirectory( selected->getUUID() );
+ }
+}
+
+void LLPanelGroupMembersSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+
+ update(GC_ALL);
+}
+
+void LLPanelGroupMembersSubTab::deactivate()
+{
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupMembersSubTab::needsApply(LLString& mesg)
+{
+ return mChanged;
+}
+
+void LLPanelGroupMembersSubTab::cancel()
+{
+ if ( mChanged )
+ {
+ std::for_each(mMemberRoleChangeData.begin(),
+ mMemberRoleChangeData.end(),
+ DeletePairedPointer());
+ mMemberRoleChangeData.clear();
+
+ mChanged = FALSE;
+ notifyObservers();
+ }
+}
+
+bool LLPanelGroupMembersSubTab::apply(LLString& mesg)
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "Unable to get group data for group " << mGroupID << llendl;
+
+ mesg.assign("Unable to save member data. Try again later.");
+ return false;
+ }
+
+ if (mChanged)
+ {
+ //figure out if we are somehow adding an owner or not and alert
+ //the user...possibly make it ignorable
+ if ( mNumOwnerAdditions > 0 )
+ {
+ LLRoleData rd;
+ LLStringBase<char>::format_map_t args;
+
+ if ( gdatap->getRoleData(gdatap->mOwnerRole, rd) )
+ {
+ mHasModal = TRUE;
+ args["[ROLE_NAME]"] = rd.mRoleName;
+ gViewerWindow->alertXml("AddGroupOwnerWarning",
+ args,
+ addOwnerCB,
+ this);
+ }
+ else
+ {
+ llwarns << "Unable to get role information for the owner role in group " << mGroupID << llendl;
+
+ mesg.assign("Unable to retried specific group information. Try again later");
+ return false;
+ }
+
+ }
+ else
+ {
+ applyMemberChanges();
+ }
+ }
+
+ return true;
+}
+
+//static
+void LLPanelGroupMembersSubTab::addOwnerCB(S32 option, void* data)
+{
+ LLPanelGroupMembersSubTab* self = (LLPanelGroupMembersSubTab*) data;
+
+ if (!self) return;
+
+ self->mHasModal = FALSE;
+
+ if (0 == option)
+ {
+ // User clicked "Yes"
+ self->applyMemberChanges();
+ }
+}
+
+void LLPanelGroupMembersSubTab::applyMemberChanges()
+{
+ //sucks to do a find again here, but it is in constant time, so, could
+ //be worse
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "Unable to get group data for group " << mGroupID << llendl;
+ return;
+ }
+
+ //we need to add all of the changed roles data
+ //for each member whose role changed
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member = mMemberRoleChangeData.begin();
+
+ for (; member != member_end; member++)
+ {
+ role_change_data_map_t::iterator role_end = member->second->end();
+ role_change_data_map_t::iterator role = member->second->begin();
+
+ for (; role != role_end; role++)
+ {
+ gdatap->changeRoleMember(role->first, //role_id
+ member->first, //member_id
+ role->second); //add/remove
+ }
+
+ member->second->clear();
+ delete member->second;
+ }
+ mMemberRoleChangeData.clear();
+
+ gGroupMgr->sendGroupRoleMemberChanges(mGroupID);
+ //force a UI update
+ handleMemberSelect();
+
+ mChanged = FALSE;
+ mNumOwnerAdditions = 0;
+ notifyObservers();
+}
+
+bool LLPanelGroupMembersSubTab::matchesSearchFilter(char* first, char* last)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ // Create a full name, and compare it to the search filter.
+ LLString fullname;
+ fullname.assign(first);
+ fullname.append(1, ' ');
+ fullname.append(last);
+ LLString::toLower(fullname);
+
+ std::string::size_type match = fullname.find(mSearchFilter);
+
+ if (std::string::npos == match)
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+U64 LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges(const LLUUID& agent_id)
+{
+ //we loop over all of the changes
+ //if we are adding a role, then we simply add the role's powers
+ //if we are removing a role, we store that role id away
+ //and then we have to build the powers up bases on the roles the agent
+ //is in
+ member_role_change_iter member_end = mMemberRoleChangeData.end();
+ member_role_change_iter member;
+ role_change_data_map_t *role_change_datap = NULL;
+ role_change_data_map_t::iterator role_end;
+ role_change_data_map_t::iterator role;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No group data!" << llendl;
+ return GP_NO_POWERS;
+ }
+
+ LLGroupMemberData* member_data = gdatap->mMembers[agent_id];
+ if ( !member_data )
+ {
+ llwarns << "LLPanelGroupMembersSubTab::getAgentPowersBasedOnRoleChanges() -- No member data for member with UUID " << agent_id << llendl;
+ return GP_NO_POWERS;
+ }
+
+ //see if there are unsaved role changes for this agent
+ member = mMemberRoleChangeData.find(agent_id);
+ if ( member != member_end )
+ {
+ //this member has unsaved role changes
+ //so grab them
+ role_change_datap = (*member).second;
+ }
+
+ U64 new_powers = GP_NO_POWERS;
+
+ if ( role_change_datap )
+ {
+ std::vector<LLUUID> roles_to_be_removed;
+
+ role_end = role_change_datap->end();
+ role = role_change_datap->begin();
+
+ for (; role != role_end; role++)
+ {
+ if ( role->second == RMC_ADD )
+ new_powers |= gdatap->getRolePowers(role->first);
+ else
+ {
+ roles_to_be_removed.push_back(role->first);
+ }
+ }
+
+ //loop over the member's current roles, summing up
+ //the powers (not including the role we are removing)
+ std::map<LLUUID,LLGroupRoleData*>::iterator current_role =
+ member_data->roleBegin();
+ std::map<LLUUID,LLGroupRoleData*>::iterator end_role =
+ member_data->roleEnd();
+
+ for (; current_role != end_role; current_role++)
+ {
+ bool role_in_remove_list =
+ (std::find(roles_to_be_removed.begin(),
+ roles_to_be_removed.end(),
+ current_role->second->getID()) !=
+ roles_to_be_removed.end());
+
+ if ( !role_in_remove_list )
+ {
+ new_powers |=
+ current_role->second->getRoleData().mRolePowers;
+ }
+ }
+ }
+ else
+ {
+ //there are no changes for this member
+ //the member's powers are just the ones stored in the group
+ //manager
+ new_powers = member_data->getAgentPowers();
+ }
+
+ return new_powers;
+}
+
+//If there is no change, returns false be sure to verify
+//that there is a role change before attempting to get it or else
+//the data will make no sense. Stores the role change type
+bool LLPanelGroupMembersSubTab::getRoleChangeType(const LLUUID& member_id,
+ const LLUUID& role_id,
+ LLRoleMemberChangeType& type)
+{
+ member_role_change_iter member_changes_iter;
+ role_change_data_map_t::iterator role_changes_iter;
+
+ member_changes_iter = mMemberRoleChangeData.find(member_id);
+ if ( member_changes_iter != mMemberRoleChangeData.end() )
+ {
+ role_changes_iter = member_changes_iter->second->find(role_id);
+ if ( role_changes_iter != member_changes_iter->second->end() )
+ {
+ type = role_changes_iter->second;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLPanelGroupMembersSubTab::draw()
+{
+ LLPanelGroupSubTab::draw();
+
+ if (mPendingMemberUpdate)
+ {
+ updateMembers();
+ }
+}
+
+void LLPanelGroupMembersSubTab::update(LLGroupChange gc)
+{
+ if (mGroupID.isNull()) return;
+
+ if ( GC_TITLES == gc || GC_PROPERTIES == gc )
+ {
+ // Don't care about title or general group properties updates.
+ return;
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::update() -- No group data!" << llendl;
+ return;
+ }
+
+ // Rebuild the members list.
+ mMembersList->deleteAllItems();
+
+ // Wait for both all data to be retrieved before displaying anything.
+ if ( gdatap->isMemberDataComplete()
+ && gdatap->isRoleDataComplete()
+ && gdatap->isRoleMemberDataComplete())
+ {
+ mMemberProgress = gdatap->mMembers.begin();
+ mPendingMemberUpdate = TRUE;
+ mHasMatch = FALSE;
+ }
+ else
+ {
+ // Build a string with info on retrieval progress.
+ std::ostringstream retrieved;
+ if ( !gdatap->isMemberDataComplete() )
+ {
+ // Still busy retreiving member list.
+ retrieved << "Retrieving member list (" << gdatap->mMembers.size()
+ << " / " << gdatap->mMemberCount << ")...";
+ }
+ else if( !gdatap->isRoleDataComplete() )
+ {
+ // Still busy retreiving role list.
+ retrieved << "Retrieving role list (" << gdatap->mRoles.size()
+ << " / " << gdatap->mRoleCount << ")...";
+ }
+ else // (!gdatap->isRoleMemberDataComplete())
+ {
+ // Still busy retreiving role/member mappings.
+ retrieved << "Retrieving role member mappings...";
+ }
+ mMembersList->setEnabled(FALSE);
+ mMembersList->addSimpleItem(retrieved.str());
+ }
+}
+
+void LLPanelGroupMembersSubTab::updateMembers()
+{
+ mPendingMemberUpdate = FALSE;
+
+ lldebugs << "LLPanelGroupMembersSubTab::updateMembers()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupMembersSubTab::updateMembers() -- No group data!" << llendl;
+ return;
+ }
+
+ // Make sure all data is still complete. Incomplete data
+ // may occur if we refresh.
+ if ( !gdatap->isMemberDataComplete()
+ || !gdatap->isRoleDataComplete()
+ || !gdatap->isRoleMemberDataComplete())
+ {
+ return;
+ }
+
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+
+ char first[DB_FIRST_NAME_BUF_SIZE];
+ char last[DB_LAST_NAME_BUF_SIZE];
+ S32 i = 0;
+ for( ; mMemberProgress != end && i<UPDATE_MEMBERS_PER_FRAME;
+ ++mMemberProgress, ++i)
+ {
+ if (!mMemberProgress->second)
+ continue;
+ // Do filtering on name if it is already in the cache.
+ bool add_member = true;
+
+ if (gCacheName->getName(mMemberProgress->first, first, last))
+ {
+ if ( !matchesSearchFilter(first, last) )
+ {
+ add_member = false;
+ }
+ }
+
+ if (add_member)
+ {
+ // Build the donated tier string.
+ std::ostringstream donated;
+ donated << mMemberProgress->second->getContribution() << " sq. m.";
+
+ LLSD row;
+ row["id"] = (*mMemberProgress).first;
+
+ row["columns"][0]["column"] = "name";
+ // value is filled in by name list control
+
+ row["columns"][1]["column"] = "donated";
+ row["columns"][1]["value"] = donated.str();
+
+ row["columns"][2]["column"] = "online";
+ row["columns"][2]["value"] = mMemberProgress->second->getOnlineStatus();
+ row["columns"][2]["font"] = "SANSSERIFSMALL";
+
+ mMembersList->addElement(row);//, ADD_SORTED);
+ mHasMatch = TRUE;
+ }
+ }
+
+ if (mMemberProgress == end)
+ {
+ if (mHasMatch)
+ {
+ mMembersList->setEnabled(TRUE);
+ }
+ else
+ {
+ mMembersList->setEnabled(FALSE);
+ mMembersList->addSimpleItem("No match.");
+ }
+ }
+ else
+ {
+ mPendingMemberUpdate = TRUE;
+ }
+
+ // This should clear the other two lists, since nothing is selected.
+ handleMemberSelect();
+}
+
+
+
+////////////////////////////
+// LLPanelGroupRolesSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupRolesSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupRolesSubTab("panel group roles sub tab", *group_id);
+}
+
+LLPanelGroupRolesSubTab::LLPanelGroupRolesSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id), mHasRoleChange(FALSE)
+{
+}
+
+LLPanelGroupRolesSubTab::~LLPanelGroupRolesSubTab()
+{
+}
+
+BOOL LLPanelGroupRolesSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("roles_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("roles_footer", recurse);
+
+
+ mRolesList = (LLScrollListCtrl*) parent->getChildByName("role_list", recurse);
+ mAssignedMembersList = (LLNameListCtrl*) parent->getChildByName("role_assigned_members", recurse);
+ mAllowedActionsList = (LLScrollListCtrl*) parent->getChildByName("role_allowed_actions", recurse);
+
+ mRoleName = (LLLineEditor*) parent->getChildByName("role_name", recurse);
+ mRoleTitle = (LLLineEditor*) parent->getChildByName("role_title", recurse);
+ mRoleDescription = (LLTextEditor*) parent->getChildByName("role_description", recurse);
+
+ mMemberVisibleCheck = (LLCheckBoxCtrl*) parent->getChildByName("role_visible_in_list", recurse);
+
+ if (!mRolesList || !mAssignedMembersList || !mAllowedActionsList
+ || !mRoleName || !mRoleTitle || !mRoleDescription || !mMemberVisibleCheck)
+ {
+ llwarns << "ARG! element not found." << llendl;
+ return FALSE;
+ }
+
+ LLTextBox* txt = (LLTextBox*) parent->getChildByName("cant_delete_role", FALSE);
+ if (txt)
+ {
+ mRemoveEveryoneTxt = txt->getText();
+ parent->removeChild(txt);
+ }
+
+ mCreateRoleButton =
+ (LLButton*) parent->getChildByName("role_create", recurse);
+ if ( mCreateRoleButton )
+ {
+ mCreateRoleButton->setCallbackUserData(this);
+ mCreateRoleButton->setClickedCallback(onCreateRole);
+ mCreateRoleButton->setEnabled(FALSE);
+ }
+
+ mDeleteRoleButton =
+ (LLButton*) parent->getChildByName("role_delete", recurse);
+ if ( mDeleteRoleButton )
+ {
+ mDeleteRoleButton->setCallbackUserData(this);
+ mDeleteRoleButton->setClickedCallback(onDeleteRole);
+ mDeleteRoleButton->setEnabled(FALSE);
+ }
+
+ mRolesList->setCommitOnSelectionChange(TRUE);
+ mRolesList->setCallbackUserData(this);
+ mRolesList->setCommitCallback(onRoleSelect);
+
+ mMemberVisibleCheck->setCallbackUserData(this);
+ mMemberVisibleCheck->setCommitCallback(onMemberVisibilityChange);
+
+ mAllowedActionsList->setCommitOnSelectionChange(TRUE);
+ mAllowedActionsList->setCallbackUserData(this);
+
+ mRoleName->setCommitOnFocusLost(TRUE);
+ mRoleName->setCallbackUserData(this);
+ mRoleName->setKeystrokeCallback(onPropertiesKey);
+
+ mRoleTitle->setCommitOnFocusLost(TRUE);
+ mRoleTitle->setCallbackUserData(this);
+ mRoleTitle->setKeystrokeCallback(onPropertiesKey);
+
+ mRoleDescription->setCommitOnFocusLost(TRUE);
+ mRoleDescription->setCallbackUserData(this);
+ mRoleDescription->setCommitCallback(onDescriptionCommit);
+ mRoleDescription->setFocusReceivedCallback(onDescriptionCommit);
+
+ setFooterEnabled(FALSE);
+
+ return TRUE;
+}
+
+void LLPanelGroupRolesSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+
+ mRolesList->deselectAllItems();
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+
+ setFooterEnabled(FALSE);
+
+ mHasRoleChange = FALSE;
+ update(GC_ALL);
+}
+
+void LLPanelGroupRolesSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupRolesSubTab::deactivate()" << llendl;
+
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupRolesSubTab::needsApply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::needsApply()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ return (mHasRoleChange // Text changed in current role
+ || (gdatap && gdatap->pendingRoleChanges())); // Pending role changes in the group
+}
+
+bool LLPanelGroupRolesSubTab::apply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::apply()" << llendl;
+
+ saveRoleChanges();
+ gGroupMgr->sendGroupRoleChanges(mGroupID);
+
+ notifyObservers();
+
+ return true;
+}
+
+void LLPanelGroupRolesSubTab::cancel()
+{
+ mHasRoleChange = FALSE;
+ gGroupMgr->cancelGroupRoleChanges(mGroupID);
+
+ notifyObservers();
+}
+
+LLSD LLPanelGroupRolesSubTab::createRoleItem(const LLUUID& role_id,
+ std::string name,
+ std::string title,
+ S32 members)
+{
+ LLSD row;
+ row["id"] = role_id;
+
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = name;
+
+ row["columns"][1]["column"] = "title";
+ row["columns"][1]["value"] = title;
+
+ row["columns"][2]["column"] = "members";
+ row["columns"][2]["value"] = members;
+
+ return row;
+}
+
+bool LLPanelGroupRolesSubTab::matchesSearchFilter(std::string rolename, std::string roletitle)
+{
+ // If the search filter is empty, everything passes.
+ if (mSearchFilter.empty()) return true;
+
+ LLString::toLower(rolename);
+ LLString::toLower(roletitle);
+ std::string::size_type match_name = rolename.find(mSearchFilter);
+ std::string::size_type match_title = roletitle.find(mSearchFilter);
+
+ if ( (std::string::npos == match_name)
+ && (std::string::npos == match_title))
+ {
+ // not found
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+void LLPanelGroupRolesSubTab::update(LLGroupChange gc)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::update()" << llendl;
+
+ if (mGroupID.isNull()) return;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap || !gdatap->isRoleDataComplete())
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+ else
+ {
+ bool had_selection = false;
+ LLUUID last_selected;
+ if (mRolesList->getFirstSelected())
+ {
+ last_selected = mRolesList->getFirstSelected()->getUUID();
+ had_selection = true;
+ }
+ mRolesList->deleteAllItems();
+
+ LLScrollListItem* item = NULL;
+
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+
+ for ( ; rit != end; ++rit)
+ {
+ LLRoleData rd;
+ if (gdatap->getRoleData((*rit).first,rd))
+ {
+ if (matchesSearchFilter(rd.mRoleName, rd.mRoleTitle))
+ {
+ // If this is the everyone role, then EVERYONE is in it.
+ S32 members_in_role = (*rit).first.isNull() ? gdatap->mMembers.size() : (*rit).second->getTotalMembersInRole();
+ LLSD row = createRoleItem((*rit).first,rd.mRoleName, rd.mRoleTitle, members_in_role);
+ item = mRolesList->addElement(row, ((*rit).first.isNull()) ? ADD_TOP : ADD_BOTTOM, this);
+ if (had_selection && ((*rit).first == last_selected))
+ {
+ item->setSelected(TRUE);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "LLPanelGroupRolesSubTab::update() No role data for role " << (*rit).first << llendl;
+ }
+ }
+
+ mRolesList->sortByColumn("name", TRUE);
+
+ if ( (gdatap->mRoles.size() < MAX_ROLES)
+ && gAgent.hasPowerInGroup(mGroupID, GP_ROLE_CREATE) )
+ {
+ mCreateRoleButton->setEnabled(TRUE);
+ }
+ else
+ {
+ mCreateRoleButton->setEnabled(FALSE);
+ }
+
+ if (had_selection)
+ {
+ handleRoleSelect();
+ }
+ else
+ {
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+ setFooterEnabled(FALSE);
+ mDeleteRoleButton->setEnabled(FALSE);
+ }
+ }
+
+ if (!gdatap || !gdatap->isMemberDataComplete())
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ if (!gdatap || !gdatap->isRoleMemberDataComplete())
+ {
+ gGroupMgr->sendGroupRoleMembersRequest(mGroupID);
+ }
+
+ if ((GC_ROLE_MEMBER_DATA == gc || GC_MEMBER_DATA == gc)
+ && gdatap->isMemberDataComplete()
+ && gdatap->isRoleMemberDataComplete())
+ {
+ buildMembersList();
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onRoleSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ self->handleRoleSelect();
+}
+
+void LLPanelGroupRolesSubTab::handleRoleSelect()
+{
+ BOOL can_delete = TRUE;
+ lldebugs << "LLPanelGroupRolesSubTab::handleRoleSelect()" << llendl;
+
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ saveRoleChanges();
+
+ // Check if there is anything selected.
+ LLScrollListItem* item = mRolesList->getFirstSelected();
+ if (!item)
+ {
+ setFooterEnabled(FALSE);
+ return;
+ }
+
+ setFooterEnabled(TRUE);
+
+ LLRoleData rd;
+ if (gdatap->getRoleData(item->getUUID(),rd))
+ {
+ BOOL is_owner_role = ( gdatap->mOwnerRole == item->getUUID() );
+ mRoleName->setText(rd.mRoleName);
+ mRoleTitle->setText(rd.mRoleTitle);
+ mRoleDescription->setText(rd.mRoleDescription);
+
+ mAllowedActionsList->setEnabled(gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_CHANGE_ACTIONS));
+ buildActionsList(mAllowedActionsList,
+ rd.mRolePowers,
+ 0LL,
+ mActionIcons,
+ onActionCheck,
+ TRUE,
+ FALSE,
+ is_owner_role);
+
+
+ mMemberVisibleCheck->set((rd.mRolePowers & GP_MEMBER_VISIBLE_IN_DIR) == GP_MEMBER_VISIBLE_IN_DIR);
+ mRoleName->setEnabled(!is_owner_role &&
+ gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mRoleTitle->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mRoleDescription->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+ mMemberVisibleCheck->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_ROLE_PROPERTIES));
+
+ if (item->getUUID().isNull())
+ {
+ // Everyone role, can't edit description or name or delete
+ mRoleDescription->setEnabled(FALSE);
+ mRoleName->setEnabled(FALSE);
+ can_delete = FALSE;
+ }
+ //you can't delete the owner role
+ if ( is_owner_role ) can_delete = FALSE;
+ }
+ else
+ {
+ mRolesList->deselectAllItems();
+ mAssignedMembersList->deleteAllItems();
+ mAllowedActionsList->deleteAllItems();
+ mRoleName->clear();
+ mRoleDescription->clear();
+ mRoleTitle->clear();
+ setFooterEnabled(FALSE);
+
+ can_delete = FALSE;
+ }
+ mSelectedRole = item->getUUID();
+ buildMembersList();
+
+ can_delete = can_delete && gAgent.hasPowerInGroup(mGroupID,
+ GP_ROLE_DELETE);
+ mDeleteRoleButton->setEnabled(can_delete);
+}
+
+void LLPanelGroupRolesSubTab::buildMembersList()
+{
+ mAssignedMembersList->deleteAllItems();
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ // Check if there is anything selected.
+ LLScrollListItem* item = mRolesList->getFirstSelected();
+ if (!item) return;
+
+ if (item->getUUID().isNull())
+ {
+ // Special cased 'Everyone' role
+ LLGroupMgrGroupData::member_iter mit = gdatap->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+ for ( ; mit != end; ++mit)
+ {
+ mAssignedMembersList->addNameItem((*mit).first);
+ }
+ }
+ else
+ {
+ LLGroupMgrGroupData::role_iter rit = gdatap->mRoles.find(item->getUUID());
+ if (rit != gdatap->mRoles.end())
+ {
+ LLGroupRoleData* rdatap = (*rit).second;
+ if (rdatap)
+ {
+ std::vector<LLUUID>::const_iterator mit = rdatap->getMembersBegin();
+ std::vector<LLUUID>::const_iterator end = rdatap->getMembersEnd();
+ for ( ; mit != end; ++mit)
+ {
+ mAssignedMembersList->addNameItem((*mit));
+ }
+ }
+ }
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onActionCheck(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check || !self) return;
+
+ self->handleActionCheck(check);
+}
+
+struct ActionCBData
+{
+ LLPanelGroupRolesSubTab* mSelf;
+ LLCheckBoxCtrl* mCheck;
+};
+
+void LLPanelGroupRolesSubTab::handleActionCheck(LLCheckBoxCtrl* check, bool force)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::handleActionSelect()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ LLScrollListItem* action_item = mAllowedActionsList->getFirstSelected();
+ if (!action_item)
+ {
+ return;
+ }
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+ LLUUID role_id = role_item->getUUID();
+
+ LLRoleAction* rap = (LLRoleAction*)action_item->getUserdata();
+ U64 power = rap->mPowerBit;
+
+ if (check->get())
+ {
+ if (!force && ( (GP_ROLE_ASSIGN_MEMBER == power)
+ || (GP_ROLE_CHANGE_ACTIONS == power) ))
+ {
+ // Uncheck the item, for now. It will be
+ // checked if they click 'Yes', below.
+ check->set(FALSE);
+
+ LLRoleData rd;
+ LLStringBase<char>::format_map_t args;
+
+ if ( gdatap->getRoleData(role_id, rd) )
+ {
+ args["[ACTION_NAME]"] = rap->mDescription;
+ args["[ROLE_NAME]"] = rd.mRoleName;
+ struct ActionCBData* cb_data = new ActionCBData;
+ cb_data->mSelf = this;
+ cb_data->mCheck = check;
+ mHasModal = TRUE;
+ LLString warning = "AssignDangerousActionWarning";
+ if (GP_ROLE_CHANGE_ACTIONS == power)
+ {
+ warning = "AssignDangerousAbilityWarning";
+ }
+ gViewerWindow->alertXml(warning, args, addActionCB, cb_data);
+ }
+ else
+ {
+ llwarns << "Unable to look up role information for role id: "
+ << role_id << llendl;
+ }
+ }
+ else
+ {
+ gdatap->addRolePower(role_id,power);
+ }
+ }
+ else
+ {
+ gdatap->removeRolePower(role_id,power);
+ }
+
+ mHasRoleChange = TRUE;
+ notifyObservers();
+}
+
+//static
+void LLPanelGroupRolesSubTab::addActionCB(S32 option, void* data)
+{
+ struct ActionCBData* cb_data = (struct ActionCBData*) data;
+
+ if (!cb_data || !cb_data->mSelf || !cb_data->mCheck) return;
+
+ cb_data->mSelf->mHasModal = FALSE;
+
+ if (0 == option)
+ {
+ // User clicked "Yes"
+ cb_data->mCheck->set(TRUE);
+ const bool force_add = true;
+ cb_data->mSelf->handleActionCheck(cb_data->mCheck, force_add);
+ }
+}
+
+
+// static
+void LLPanelGroupRolesSubTab::onPropertiesKey(LLLineEditor* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->mHasRoleChange = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onDescriptionCommit(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->mHasRoleChange = TRUE;
+ self->notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onMemberVisibilityChange(LLUICtrl* ctrl, void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ LLCheckBoxCtrl* check = static_cast<LLCheckBoxCtrl*>(ctrl);
+ if (!check || !self) return;
+
+ self->handleMemberVisibilityChange(check->get());
+}
+
+void LLPanelGroupRolesSubTab::handleMemberVisibilityChange(bool value)
+{
+ lldebugs << "LLPanelGroupRolesSubTab::handleMemberVisibilityChange()" << llendl;
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+ if (!gdatap)
+ {
+ llwarns << "LLPanelGroupRolesSubTab::handleRoleSelect() "
+ << "-- No group data!" << llendl;
+ return;
+ }
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+
+ if (value)
+ {
+ gdatap->addRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
+ }
+ else
+ {
+ gdatap->removeRolePower(role_item->getUUID(),GP_MEMBER_VISIBLE_IN_DIR);
+ }
+}
+
+// static
+void LLPanelGroupRolesSubTab::onCreateRole(void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->handleCreateRole();
+}
+
+void LLPanelGroupRolesSubTab::handleCreateRole()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLUUID new_role_id;
+ new_role_id.generate();
+
+ LLRoleData rd;
+ rd.mRoleName = "New Role";
+ gdatap->createRole(new_role_id,rd);
+
+ mRolesList->deselectAllItems(TRUE);
+ LLSD row;
+ row["id"] = new_role_id;
+ row["columns"][0]["column"] = "name";
+ row["columns"][0]["value"] = rd.mRoleName;
+ mRolesList->addElement(row, ADD_BOTTOM, this);
+ mRolesList->selectByID(new_role_id);
+
+ // put focus on name field and select its contents
+ if(mRoleName)
+ {
+ mRoleName->setFocus(TRUE);
+ mRoleName->onTabInto();
+ gFocusMgr.triggerFocusFlash();
+ }
+
+ notifyObservers();
+}
+
+// static
+void LLPanelGroupRolesSubTab::onDeleteRole(void* user_data)
+{
+ LLPanelGroupRolesSubTab* self = static_cast<LLPanelGroupRolesSubTab*>(user_data);
+ if (!self) return;
+
+ self->handleDeleteRole();
+}
+
+void LLPanelGroupRolesSubTab::handleDeleteRole()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ LLScrollListItem* role_item = mRolesList->getFirstSelected();
+ if (!role_item)
+ {
+ return;
+ }
+
+ if (role_item->getUUID().isNull() || role_item->getUUID() == gdatap->mOwnerRole)
+ {
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = mRemoveEveryoneTxt;
+ LLNotifyBox::showXml("GenericNotify", args);
+ return;
+ }
+
+ gdatap->deleteRole(role_item->getUUID());
+ mRolesList->deleteSingleItem(mRolesList->getFirstSelectedIndex());
+ mRolesList->selectFirstItem();
+
+ notifyObservers();
+}
+
+void LLPanelGroupRolesSubTab::saveRoleChanges()
+{
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ if (mHasRoleChange)
+ {
+ LLRoleData rd;
+ if (!gdatap->getRoleData(mSelectedRole,rd)) return;
+
+ rd.mRoleName = mRoleName->getText();
+ rd.mRoleDescription = mRoleDescription->getText();
+ rd.mRoleTitle = mRoleTitle->getText();
+
+ gdatap->setRoleData(mSelectedRole,rd);
+
+ mRolesList->deleteSingleItem(mRolesList->getItemIndex(mSelectedRole));
+
+ LLSD row = createRoleItem(mSelectedRole,rd.mRoleName,rd.mRoleTitle,0);
+ LLScrollListItem* item = mRolesList->addElement(row, ADD_BOTTOM, this);
+ item->setSelected(TRUE);
+
+ mHasRoleChange = FALSE;
+ }
+}
+////////////////////////////
+// LLPanelGroupActionsSubTab
+////////////////////////////
+
+// static
+void* LLPanelGroupActionsSubTab::createTab(void* data)
+{
+ LLUUID* group_id = static_cast<LLUUID*>(data);
+ return new LLPanelGroupActionsSubTab("panel group actions sub tab", *group_id);
+}
+
+LLPanelGroupActionsSubTab::LLPanelGroupActionsSubTab(const std::string& name, const LLUUID& group_id)
+: LLPanelGroupSubTab(name, group_id)
+{
+}
+
+LLPanelGroupActionsSubTab::~LLPanelGroupActionsSubTab()
+{
+}
+
+BOOL LLPanelGroupActionsSubTab::postBuildSubTab(LLView* root)
+{
+ // Upcast parent so we can ask it for sibling controls.
+ LLPanelGroupRoles* parent = (LLPanelGroupRoles*) root;
+
+ // Look recursively from the parent to find all our widgets.
+ bool recurse = true;
+ mHeader = (LLPanel*) parent->getChildByName("actions_header", recurse);
+ mFooter = (LLPanel*) parent->getChildByName("actions_footer", recurse);
+
+ mActionDescription = (LLTextEditor*) parent->getChildByName("action_description", recurse);
+
+ mActionList = (LLScrollListCtrl*) parent->getChildByName("action_list",recurse);
+ mActionRoles = (LLScrollListCtrl*) parent->getChildByName("action_roles",recurse);
+ mActionMembers = (LLNameListCtrl*) parent->getChildByName("action_members",recurse);
+
+ if (!mActionList || !mActionDescription || !mActionRoles || !mActionMembers) return FALSE;
+
+ mActionList->setCallbackUserData(this);
+ mActionList->setCommitOnSelectionChange(TRUE);
+ mActionList->setCommitCallback(onActionSelect);
+
+ mActionMembers->setCallbackUserData(this);
+ mActionRoles->setCallbackUserData(this);
+
+ update(GC_ALL);
+
+ return TRUE;
+}
+
+void LLPanelGroupActionsSubTab::activate()
+{
+ LLPanelGroupSubTab::activate();
+ lldebugs << "LLPanelGroupActionsSubTab::activate()" << llendl;
+
+ mActionList->deselectAllItems();
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+ mActionDescription->clear();
+}
+
+void LLPanelGroupActionsSubTab::deactivate()
+{
+ lldebugs << "LLPanelGroupActionsSubTab::deactivate()" << llendl;
+
+ LLPanelGroupSubTab::deactivate();
+}
+
+bool LLPanelGroupActionsSubTab::needsApply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::needsApply()" << llendl;
+
+ return false;
+}
+
+bool LLPanelGroupActionsSubTab::apply(LLString& mesg)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::apply()" << llendl;
+ return true;
+}
+
+void LLPanelGroupActionsSubTab::update(LLGroupChange gc)
+{
+ lldebugs << "LLPanelGroupActionsSubTab::update()" << llendl;
+
+ if (mGroupID.isNull()) return;
+
+ mActionList->deselectAllItems();
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+ mActionDescription->clear();
+
+ mActionList->deleteAllItems();
+ buildActionsList(mActionList,
+ GP_ALL_POWERS,
+ GP_ALL_POWERS,
+ mActionIcons,
+ NULL,
+ FALSE,
+ TRUE,
+ FALSE);
+}
+
+// static
+void LLPanelGroupActionsSubTab::onActionSelect(LLUICtrl* scroll, void* data)
+{
+ LLPanelGroupActionsSubTab* self = static_cast<LLPanelGroupActionsSubTab*>(data);
+ self->handleActionSelect();
+}
+
+void LLPanelGroupActionsSubTab::handleActionSelect()
+{
+ mActionMembers->deleteAllItems();
+ mActionRoles->deleteAllItems();
+
+ U64 power_mask = GP_NO_POWERS;
+ std::vector<LLScrollListItem*> selection =
+ mActionList->getAllSelected();
+ if (selection.empty()) return;
+
+ LLRoleAction* rap;
+
+ std::vector<LLScrollListItem*>::iterator itor;
+ for (itor = selection.begin() ;
+ itor != selection.end(); ++itor)
+ {
+ rap = (LLRoleAction*)( (*itor)->getUserdata() );
+ power_mask |= rap->mPowerBit;
+ }
+
+ if (selection.size() == 1)
+ {
+ LLScrollListItem* item = selection[0];
+ rap = (LLRoleAction*)(item->getUserdata());
+
+ if (rap->mLongDescription.empty())
+ {
+ mActionDescription->setText(rap->mDescription);
+ }
+ else
+ {
+ mActionDescription->setText(rap->mLongDescription);
+ }
+ }
+ else
+ {
+ mActionDescription->clear();
+ }
+
+ LLGroupMgrGroupData* gdatap = gGroupMgr->getGroupData(mGroupID);
+
+ if (!gdatap) return;
+
+ if (gdatap->isMemberDataComplete())
+ {
+ LLGroupMgrGroupData::member_iter it = gdatap->mMembers.begin();
+ LLGroupMgrGroupData::member_iter end = gdatap->mMembers.end();
+ LLGroupMemberData* gmd;
+
+ for ( ; it != end; ++it)
+ {
+ gmd = (*it).second;
+ if (!gmd) continue;
+ if ((gmd->getAgentPowers() & power_mask) == power_mask)
+ {
+ mActionMembers->addNameItem(gmd->getID());
+ }
+ }
+ }
+ else
+ {
+ gGroupMgr->sendGroupMembersRequest(mGroupID);
+ }
+
+ if (gdatap->isRoleDataComplete())
+ {
+ LLGroupMgrGroupData::role_iter it = gdatap->mRoles.begin();
+ LLGroupMgrGroupData::role_iter end = gdatap->mRoles.end();
+ LLGroupRoleData* rmd;
+
+ for ( ; it != end; ++it)
+ {
+ rmd = (*it).second;
+ if (!rmd) continue;
+ if ((rmd->getRoleData().mRolePowers & power_mask) == power_mask)
+ {
+ mActionRoles->addSimpleItem(rmd->getRoleData().mRoleName);
+ }
+ }
+ }
+ else
+ {
+ gGroupMgr->sendGroupRoleDataRequest(mGroupID);
+ }
+}
diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h
new file mode 100644
index 0000000000..58a81b1daa
--- /dev/null
+++ b/indra/newview/llpanelgrouproles.h
@@ -0,0 +1,297 @@
+/**
+ * @file llpanelgrouproles.h
+ * @brief Panel for roles information about a particular group.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELGROUPROLES_H
+#define LL_LLPANELGROUPROLES_H
+
+#include "llpanelgroup.h"
+
+class LLNameListCtrl;
+class LLPanelGroupSubTab;
+class LLPanelGroupMembersSubTab;
+class LLPanelGroupRolesSubTab;
+class LLPanelGroupActionsSubTab;
+class LLScrollListCtrl;
+class LLScrollListItem;
+
+// Forward declare for friend usage.
+//virtual BOOL LLPanelGroupSubTab::postBuildSubTab(LLView*);
+
+typedef std::map<std::string,LLUUID> icon_map_t;
+
+class LLPanelGroupRoles : public LLPanelGroupTab,
+ public LLPanelGroupTabObserver
+{
+public:
+ LLPanelGroupRoles(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupRoles();
+
+ // Allow sub tabs to ask for sibling controls.
+ friend class LLPanelGroupMembersSubTab;
+ friend class LLPanelGroupRolesSubTab;
+ friend class LLPanelGroupActionsSubTab;
+
+ virtual BOOL postBuild();
+ virtual BOOL isVisibleByAgent(LLAgent* agentp);
+
+ static void* createTab(void* data);
+ static void onClickSubTab(void*,bool);
+ void handleClickSubTab();
+
+ // Checks if the current tab needs to be applied, and tries to switch to the requested tab.
+ BOOL attemptTransition();
+
+ // Switches to the requested tab (will close() if requested is NULL)
+ void transitionToTab();
+
+ // Used by attemptTransition to query the user's response to a tab that needs to apply.
+ static void onNotifyCallback(S32 option, void* user_data);
+ void handleNotifyCallback(S32 option);
+ static void onModalClose(S32 option, void* user_data);
+
+ // Most of these messages are just passed on to the current sub-tab.
+ virtual LLString getHelpText() const;
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual BOOL hasModal();
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ virtual void update(LLGroupChange gc);
+
+ // PanelGroupTab observer trigger
+ virtual void tabChanged();
+
+protected:
+ LLPanelGroupTab* mCurrentTab;
+ LLPanelGroupTab* mRequestedTab;
+ LLTabContainerCommon* mSubTabContainer;
+ BOOL mFirstUse;
+ BOOL mIgnoreTransition;
+
+ LLString mDefaultNeedsApplyMesg;
+ LLString mWantApplyMesg;
+};
+
+class LLPanelGroupSubTab : public LLPanelGroupTab
+{
+public:
+ LLPanelGroupSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupSubTab();
+
+ virtual BOOL postBuild();
+
+ // This allows sub-tabs to collect child widgets from a higher level in the view hierarchy.
+ virtual BOOL postBuildSubTab(LLView* root) { return TRUE; }
+
+ static void onSearchKeystroke(LLLineEditor* caller, void* user_data);
+ void handleSearchKeystroke(LLLineEditor* caller);
+
+ static void onClickSearch(void*);
+ void handleClickSearch();
+ static void onClickShowAll(void*);
+ void handleClickShowAll();
+
+ virtual void setSearchFilter( const LLString& filter );
+
+ virtual void activate();
+ virtual void deactivate();
+
+ // Helper functions
+ bool matchesActionSearchFilter(std::string action);
+ void buildActionsList(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role);
+ void buildActionCategory(LLScrollListCtrl* ctrl,
+ U64 allowed_by_some,
+ U64 allowed_by_all,
+ LLRoleActionSet* action_set,
+ icon_map_t& icons,
+ void (*commit_callback)(LLUICtrl*,void*),
+ BOOL show_all,
+ BOOL filter,
+ BOOL is_owner_role);
+
+ void setFooterEnabled(BOOL enable);
+protected:
+ LLPanel* mHeader;
+ LLPanel* mFooter;
+
+ LLLineEditor* mSearchLineEditor;
+ LLButton* mSearchButton;
+ LLButton* mShowAllButton;
+
+ LLString mSearchFilter;
+
+ icon_map_t mActionIcons;
+
+ void setOthersVisible(BOOL b);
+};
+
+class LLPanelGroupMembersSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupMembersSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupMembersSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ static void onMemberSelect(LLUICtrl*, void*);
+ void handleMemberSelect();
+
+ static void onMemberDoubleClick(void*);
+ void handleMemberDoubleClick();
+
+ static void onInviteMember(void*);
+ void handleInviteMember();
+
+ static void onEjectMembers(void*);
+ void handleEjectMembers();
+
+ static void onRoleCheck(LLUICtrl* check, void* user_data);
+ void handleRoleCheck(const LLUUID& role_id,
+ LLRoleMemberChangeType type);
+
+ void applyMemberChanges();
+ static void addOwnerCB(S32 option, void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual void cancel();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void update(LLGroupChange gc);
+ void updateMembers();
+
+ virtual void draw();
+
+protected:
+ typedef std::map<LLUUID, LLRoleMemberChangeType> role_change_data_map_t;
+ typedef std::map<LLUUID, role_change_data_map_t*>::iterator member_role_change_iter;
+ typedef std::map<LLUUID, role_change_data_map_t*> member_role_changes_map_t;
+
+ bool matchesSearchFilter(char* first, char* last);
+
+ U64 getAgentPowersBasedOnRoleChanges(const LLUUID& agent_id);
+ bool getRoleChangeType(const LLUUID& member_id,
+ const LLUUID& role_id,
+ LLRoleMemberChangeType& type);
+
+ LLNameListCtrl* mMembersList;
+ LLScrollListCtrl* mAssignedRolesList;
+ LLScrollListCtrl* mAllowedActionsList;
+ LLButton* mEjectBtn;
+
+ BOOL mChanged;
+ BOOL mPendingMemberUpdate;
+ BOOL mHasMatch;
+
+ member_role_changes_map_t mMemberRoleChangeData;
+ U32 mNumOwnerAdditions;
+
+ LLGroupMgrGroupData::member_iter mMemberProgress;
+};
+
+class LLPanelGroupRolesSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupRolesSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupRolesSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void cancel();
+ bool matchesSearchFilter(std::string rolename, std::string roletitle);
+ virtual void update(LLGroupChange gc);
+
+ static void onRoleSelect(LLUICtrl*, void*);
+ void handleRoleSelect();
+ void buildMembersList();
+
+ static void onActionCheck(LLUICtrl*, void*);
+ void handleActionCheck(LLCheckBoxCtrl*, bool force=false);
+ static void addActionCB(S32 option, void* data);
+
+ static void onPropertiesKey(LLLineEditor*, void*);
+
+ static void onDescriptionCommit(LLUICtrl*, void*);
+
+ static void onMemberVisibilityChange(LLUICtrl*, void*);
+ void handleMemberVisibilityChange(bool value);
+
+ static void onCreateRole(void*);
+ void handleCreateRole();
+
+ static void onDeleteRole(void*);
+ void handleDeleteRole();
+
+ void saveRoleChanges();
+protected:
+ LLSD createRoleItem(const LLUUID& role_id,
+ std::string name,
+ std::string title,
+ S32 members);
+
+ LLScrollListCtrl* mRolesList;
+ LLNameListCtrl* mAssignedMembersList;
+ LLScrollListCtrl* mAllowedActionsList;
+
+ LLLineEditor* mRoleName;
+ LLLineEditor* mRoleTitle;
+ LLTextEditor* mRoleDescription;
+
+ LLCheckBoxCtrl* mMemberVisibleCheck;
+ LLButton* mDeleteRoleButton;
+ LLButton* mCreateRoleButton;
+
+ LLUUID mSelectedRole;
+ BOOL mHasRoleChange;
+ std::string mRemoveEveryoneTxt;
+};
+
+class LLPanelGroupActionsSubTab : public LLPanelGroupSubTab
+{
+public:
+ LLPanelGroupActionsSubTab(const std::string& name, const LLUUID& group_id);
+ virtual ~LLPanelGroupActionsSubTab();
+
+ virtual BOOL postBuildSubTab(LLView* root);
+
+ static void* createTab(void* data);
+
+ virtual void activate();
+ virtual void deactivate();
+ virtual bool needsApply(LLString& mesg);
+ virtual bool apply(LLString& mesg);
+ virtual void update(LLGroupChange gc);
+
+ static void onActionSelect(LLUICtrl*, void*);
+ void handleActionSelect();
+protected:
+ LLScrollListCtrl* mActionList;
+ LLScrollListCtrl* mActionRoles;
+ LLNameListCtrl* mActionMembers;
+
+ LLTextEditor* mActionDescription;
+};
+
+
+#endif // LL_LLPANELGROUPROLES_H
diff --git a/indra/newview/llpanelland.cpp b/indra/newview/llpanelland.cpp
new file mode 100644
index 0000000000..1a8885d05b
--- /dev/null
+++ b/indra/newview/llpanelland.cpp
@@ -0,0 +1,236 @@
+/**
+ * @file llpanelland.cpp
+ * @brief Land information in the tool floater, NOT the "About Land" floater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <time.h>
+
+#include "llpanelland.h"
+
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfloaterland.h"
+#include "lltextbox.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "roles_constants.h"
+
+#include "llvieweruictrlfactory.h"
+
+LLPanelLandSelectObserver* LLPanelLandInfo::sObserver = NULL;
+LLPanelLandInfo* LLPanelLandInfo::sInstance = NULL;
+
+class LLPanelLandSelectObserver : public LLParcelObserver
+{
+public:
+ LLPanelLandSelectObserver() {}
+ virtual ~LLPanelLandSelectObserver() {}
+ virtual void changed() { LLPanelLandInfo::refreshAll(); }
+};
+
+
+BOOL LLPanelLandInfo::postBuild()
+{
+
+ childSetAction("button buy land",onClickClaim,this);
+ childSetAction("button abandon land",onClickRelease,this);
+ childSetAction("button subdivide land",onClickDivide,this);
+ childSetAction("button join land",onClickJoin,this);
+ childSetAction("button about land",onClickAbout,this);
+
+ return TRUE;
+}
+//
+// Methods
+//
+LLPanelLandInfo::LLPanelLandInfo(const std::string& name)
+: LLPanel(name)
+{
+ if (!sInstance)
+ {
+ sInstance = this;
+ }
+ if (!sObserver)
+ {
+ sObserver = new LLPanelLandSelectObserver();
+ gParcelMgr->addObserver( sObserver );
+ }
+
+}
+
+
+// virtual
+LLPanelLandInfo::~LLPanelLandInfo()
+{
+ gParcelMgr->removeObserver( sObserver );
+ delete sObserver;
+ sObserver = NULL;
+
+ sInstance = NULL;
+}
+
+
+// static
+void LLPanelLandInfo::refreshAll()
+{
+ if (sInstance)
+ {
+ sInstance->refresh();
+ }
+}
+
+
+// public
+void LLPanelLandInfo::refresh()
+{
+ LLParcel *parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion *regionp = gParcelMgr->getSelectionRegion();
+
+ if (!parcel || !regionp)
+ {
+ // nothing selected, disable panel
+ childSetVisible("label_area_price",false);
+ childSetVisible("label_area",false);
+
+ //mTextPrice->setText("");
+ childSetText("textbox price","");
+
+ childSetEnabled("button buy land",FALSE);
+ childSetEnabled("button abandon land",FALSE);
+ childSetEnabled("button subdivide land",FALSE);
+ childSetEnabled("button join land",FALSE);
+ childSetEnabled("button about land",FALSE);
+ }
+ else
+ {
+ // something selected, hooray!
+ const LLUUID& owner_id = parcel->getOwnerID();
+ const LLUUID& auth_buyer_id = parcel->getAuthorizedBuyerID();
+
+ BOOL is_public = parcel->isPublic();
+ BOOL is_for_sale = parcel->getForSale()
+ && ((parcel->getSalePrice() > 0) || (auth_buyer_id.notNull()));
+ BOOL can_buy = (is_for_sale
+ && (owner_id != gAgent.getID())
+ && ((gAgent.getID() == auth_buyer_id)
+ || (auth_buyer_id.isNull())));
+
+ if (is_public)
+ {
+ childSetEnabled("button buy land",TRUE);
+ }
+ else
+ {
+ childSetEnabled("button buy land",can_buy);
+ }
+
+ BOOL owner_release = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_RELEASE);
+ BOOL owner_divide = LLViewerParcelMgr::isParcelOwnedByAgent(parcel, GP_LAND_DIVIDE_JOIN);
+
+ BOOL manager_releaseable = ( gAgent.canManageEstate()
+ && (parcel->getOwnerID() == regionp->getOwner()) );
+
+ BOOL manager_divideable = ( gAgent.canManageEstate()
+ && ((parcel->getOwnerID() == regionp->getOwner()) || owner_divide) );
+
+ childSetEnabled("button abandon land",owner_release || manager_releaseable || gAgent.isGodlike());
+
+ // only mainland sims are subdividable by owner
+ if (regionp->getRegionFlags() && REGION_FLAGS_ALLOW_PARCEL_CHANGES)
+ {
+ childSetEnabled("button subdivide land",owner_divide || manager_divideable || gAgent.isGodlike());
+ }
+ else
+ {
+ childSetEnabled("button subdivide land",manager_divideable || gAgent.isGodlike());
+ }
+
+ // To join land, must have something selected,
+ // not just a single unit of land,
+ // you must own part of it,
+ // and it must not be a whole parcel.
+ if (gParcelMgr->getSelectedArea() > PARCEL_UNIT_AREA
+ //&& gParcelMgr->getSelfCount() > 1
+ && !gParcelMgr->getWholeParcelSelected())
+ {
+ childSetEnabled("button join land",TRUE);
+ }
+ else
+ {
+ lldebugs << "Invalid selection for joining land" << llendl;
+ childSetEnabled("button join land",FALSE);
+ }
+
+ childSetEnabled("button about land",TRUE);
+
+ // show pricing information
+ S32 area;
+ S32 claim_price;
+ S32 rent_price;
+ BOOL for_sale;
+ F32 dwell;
+ gParcelMgr->getDisplayInfo(&area,
+ &claim_price,
+ &rent_price,
+ &for_sale,
+ &dwell);
+ if(is_public || (is_for_sale && gParcelMgr->getWholeParcelSelected()))
+ {
+ childSetTextArg("label_area_price","[PRICE]", llformat("%d",claim_price));
+ childSetTextArg("label_area_price","[AREA]", llformat("%d",area));
+ childSetVisible("label_area_price",true);
+ childSetVisible("label_area",false);
+ }
+ else
+ {
+ childSetVisible("label_area_price",false);
+ childSetTextArg("label_area","[AREA]", llformat("%d",area));
+ childSetVisible("label_area",true);
+ }
+ }
+}
+
+
+//static
+void LLPanelLandInfo::onClickClaim(void*)
+{
+ gParcelMgr->startBuyLand();
+}
+
+
+//static
+void LLPanelLandInfo::onClickRelease(void*)
+{
+ gParcelMgr->startReleaseLand();
+}
+
+// static
+void LLPanelLandInfo::onClickDivide(void*)
+{
+ gParcelMgr->startDivideLand();
+}
+
+// static
+void LLPanelLandInfo::onClickJoin(void*)
+{
+ gParcelMgr->startJoinLand();
+}
+
+//static
+void LLPanelLandInfo::onClickAbout(void*)
+{
+ // Promote the rectangle selection to a parcel selection
+ if (!gParcelMgr->getWholeParcelSelected())
+ {
+ gParcelMgr->selectParcelInRectangle();
+ }
+
+ LLFloaterLand::show();
+}
diff --git a/indra/newview/llpanelland.h b/indra/newview/llpanelland.h
new file mode 100644
index 0000000000..aee3e65329
--- /dev/null
+++ b/indra/newview/llpanelland.h
@@ -0,0 +1,54 @@
+/**
+ * @file llpanelland.h
+ * @brief Land information in the tool floater, NOT the "About Land" floater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELLAND_H
+#define LL_LLPANELLAND_H
+
+#include "llpanel.h"
+
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLButton;
+class LLSpinCtrl;
+class LLLineEditor;
+class LLPanelLandSelectObserver;
+
+class LLPanelLandInfo
+: public LLPanel
+{
+public:
+ LLPanelLandInfo(const std::string& name);
+ virtual ~LLPanelLandInfo();
+
+ void refresh();
+ static void refreshAll();
+
+protected:
+ static void onClickClaim(void*);
+ static void onClickRelease(void*);
+ static void onClickDivide(void*);
+ static void onClickJoin(void*);
+ static void onClickAbout(void*);
+
+protected:
+ //LLTextBox* mTextPriceLabel;
+ //LLTextBox* mTextPrice;
+
+ //LLButton* mBtnClaimLand;
+ //LLButton* mBtnReleaseLand;
+ //LLButton* mBtnDivideLand;
+ //LLButton* mBtnJoinLand;
+ //LLButton* mBtnAbout;
+
+ virtual BOOL postBuild();
+
+ static LLPanelLandSelectObserver* sObserver;
+ static LLPanelLandInfo* sInstance;
+};
+
+#endif
diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp
new file mode 100644
index 0000000000..e1ed7a0df7
--- /dev/null
+++ b/indra/newview/llpanellogin.cpp
@@ -0,0 +1,748 @@
+/**
+ * @file llpanellogin.cpp
+ * @brief Login dialog and logo display
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanellogin.h"
+
+#include "indra_constants.h" // for key and mask constants
+#include "llfontgl.h"
+#include "llmd5.h"
+#include "llsecondlifeurls.h"
+#include "llwindow.h" // shell_open()
+#include "llversion.h"
+#include "v4color.h"
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llviewercontrol.h"
+#include "llfloaterabout.h"
+#include "llfloaterpreference.h"
+#include "llfocusmgr.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "lluiconstants.h"
+#include "llviewerbuild.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h" // for handle_preferences()
+#include "llviewernetwork.h"
+#include "llviewerwindow.h" // to link into child list
+#include "llmozlib.h"
+#include "llnotify.h"
+#include "viewer.h" // for gHideLinks
+#include "llvieweruictrlfactory.h"
+#include "llhttpclient.h"
+#include "llweb.h"
+#include "llwebbrowserctrl.h"
+
+#include "llfloaterhtmlhelp.h"
+#include "llfloatertos.h"
+
+#include "llglheaders.h"
+
+const S32 BLACK_BORDER_HEIGHT = 160;
+const S32 MAX_PASSWORD = 16;
+
+LLPanelLogin *LLPanelLogin::sInstance = NULL;
+BOOL LLPanelLogin::sCapslockDidNotification = FALSE;
+
+// helper class that trys to download a URL from a web site and calls a method
+// on parent class indicating if the web server is working or not
+class LLIamHereLogin : public LLHTTPClient::Responder
+{
+ private:
+ LLIamHereLogin( LLPanelLogin* parent ) :
+ mParent( parent )
+ {}
+
+ LLPanelLogin* mParent;
+
+ public:
+ static boost::intrusive_ptr< LLIamHereLogin > build( LLPanelLogin* parent )
+ {
+ return boost::intrusive_ptr< LLIamHereLogin >( new LLIamHereLogin( parent ) );
+ };
+
+ virtual void setParent( LLPanelLogin* parentIn )
+ {
+ mParent = parentIn;
+ };
+
+ virtual void result( const LLSD& content )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( true );
+ };
+
+ virtual void error( U32 status, const std::string& reason )
+ {
+ if ( mParent )
+ mParent->setSiteIsAlive( false );
+ };
+};
+
+// this is global and not a class member to keep crud out of the header file
+namespace {
+ boost::intrusive_ptr< LLIamHereLogin > gResponsePtr = 0;
+};
+
+//---------------------------------------------------------------------------
+// Public methods
+//---------------------------------------------------------------------------
+LLPanelLogin::LLPanelLogin(const LLRect &rect,
+ BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void *cb_data)
+: LLPanel("panel_login", LLRect(0,600,800,0), FALSE), // not bordered
+ mLogoImage(),
+ mCallback(callback),
+ mCallbackData(cb_data),
+ mHtmlAvailable( TRUE )
+{
+ mIsFocusRoot = TRUE;
+ mMungedPassword[0] = '\0';
+ mIncomingPassword[0] = '\0';
+
+ setBackgroundVisible(FALSE);
+ setBackgroundOpaque(TRUE);
+
+ // instance management
+ if (LLPanelLogin::sInstance)
+ {
+ llwarns << "Duplicate instance of login view deleted" << llendl;
+ delete LLPanelLogin::sInstance;
+ }
+
+ LLPanelLogin::sInstance = this;
+
+ // add to front so we are the bottom-most child
+ gViewerWindow->getRootView()->addChildAtEnd(this);
+
+ // Logo
+ mLogoImage = gImageList.getImage("startup_logo.tga", LLUUID::null, MIPMAP_FALSE, TRUE);
+
+ gUICtrlFactory->buildPanel(this, "panel_login.xml");
+ setRect(rect);
+ reshape(rect.getWidth(), rect.getHeight());
+
+ childSetPrevalidate("first_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+ childSetPrevalidate("last_name_edit", LLLineEditor::prevalidatePrintableNoSpace);
+
+ childSetCommitCallback("password_edit", mungePassword);
+ childSetKeystrokeCallback("password_edit", onPassKey, this);
+ childSetUserData("password_edit", this);
+
+ LLLineEditor* edit = LLUICtrlFactory::getLineEditorByName(this, "password_edit");
+ if (edit) edit->setDrawAsterixes(TRUE);
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(this, "start_location_combo");
+ if (combo)
+ {
+ combo->setAllowTextEntry(TRUE, 128, FALSE);
+
+ // The XML file loads the combo with the following labels:
+ // 0 - "My Home"
+ // 1 - "My Last Location"
+ // 2 - "<Type region name>"
+
+ BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation");
+ LLString sim_string = LLURLSimString::sInstance.mSimString;
+ if (!sim_string.empty())
+ {
+ // Replace "<Type region name>" with this region name
+ combo->remove(2);
+ combo->add( sim_string );
+ combo->setTextEntry(sim_string);
+ combo->setCurrentByIndex( 2 );
+ }
+ else if (login_last)
+ {
+ combo->setCurrentByIndex( 1 );
+ }
+ else
+ {
+ combo->setCurrentByIndex( 0 );
+ }
+ }
+
+ // Specific servers added later.
+ childSetVisible("server_combo", show_server);
+
+ childSetAction("new_account_btn", onClickNewAccount, this);
+ childSetVisible("new_account_btn", !gHideLinks);
+
+ childSetAction("connect_btn", onClickConnect, this);
+
+ setDefaultBtn("connect_btn");
+
+ childSetAction("preferences_btn", LLFloaterPreference::show, this);
+
+ childSetAction("quit_btn", onClickQuit, this);
+
+ LLTextBox* text = LLUICtrlFactory::getTextBoxByName(this, "version_text");
+ if (text)
+ {
+ LLString version = llformat("%d.%d.%d (%d)",
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ LL_VIEWER_BUILD );
+ text->setText(version);
+ text->setClickedCallback(onClickVersion);
+ text->setCallbackUserData(this);
+
+ // HACK
+ S32 right = getRect().mRight;
+ LLRect r = text->getRect();
+ const S32 PAD = 2;
+ r.setOriginAndSize( right - r.getWidth() - PAD, PAD,
+ r.getWidth(), r.getHeight() );
+ text->setRect(r);
+ }
+
+ // get the web browser control
+ #if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "login_html");
+ if ( web_browser )
+ {
+ // don't make it a tab stop until SL-27594 is fixed
+ web_browser->setTabStop(FALSE);
+
+ // painfully build the path to the loading screen
+ std::string loading_path( gDirUtilp->getExpandedFilename( LL_PATH_SKINS, "" ) );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "html" );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "loading" );
+ loading_path.append( gDirUtilp->getDirDelimiter() );
+ loading_path.append( "loading.html" );
+ web_browser->navigateTo( loading_path.c_str() );
+
+ // make links open in external browser
+ web_browser->setOpenInExternalBrowser( true );
+
+ // force the size to be correct (XML doesn't seem to be sufficient to do this)
+ LLRect htmlRect = mRect;
+ htmlRect.setCenterAndSize( mRect.getCenterX(), mRect.getCenterY() + 40, mRect.getWidth(), mRect.getHeight() - 80 );
+ web_browser->setRect( htmlRect );
+ web_browser->reshape( htmlRect.getWidth(), htmlRect.getHeight(), TRUE );
+ reshape( mRect.getWidth(), mRect.getHeight(), 1 );
+
+ // kick off a request to grab the url manually
+ gResponsePtr = LLIamHereLogin::build( this );
+ LLHTTPClient::get( childGetValue( "real_url" ).asString(), gResponsePtr );
+ };
+ #else
+ mHtmlAvailable = FALSE;
+ #endif
+
+ // Initialize visibility (and don't force visibility - use prefs)
+ refreshLocation( false );
+}
+
+void LLPanelLogin::setSiteIsAlive( bool alive )
+{
+#if LL_LIBXUL_ENABLED
+ LLWebBrowserCtrl* web_browser = LLUICtrlFactory::getWebBrowserCtrlByName(this, "login_html");
+ // if the contents of the site was retrieved
+ if ( alive )
+ {
+ if ( web_browser )
+ {
+ // navigate to the "real" page
+ web_browser->navigateTo( childGetValue( "real_url" ).asString() );
+
+ // mark as available
+ mHtmlAvailable = TRUE;
+ };
+ }
+ else
+ // the site is not available (missing page, server down, other badness)
+ {
+ if ( web_browser )
+ {
+ // hide browser control (revealing default one)
+ web_browser->setVisible( FALSE );
+
+ // mark as unavailable
+ mHtmlAvailable = FALSE;
+ };
+ };
+#else
+ mHtmlAvailable = FALSE;
+#endif
+}
+
+void LLPanelLogin::mungePassword(LLUICtrl* caller, void* user_data)
+{
+ LLPanelLogin* self = (LLPanelLogin*)user_data;
+ LLLineEditor* editor = (LLLineEditor*)caller;
+ std::string password = editor->getText();
+
+ // Re-md5 if we've changed at all
+ if (password != self->mIncomingPassword)
+ {
+ LLMD5 pass((unsigned char *)password.c_str());
+ pass.hex_digest(self->mMungedPassword);
+ }
+}
+
+LLPanelLogin::~LLPanelLogin()
+{
+ LLPanelLogin::sInstance = NULL;
+
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ // We know we're done with the image, so be rid of it.
+ gImageList.deleteImage( mLogoImage );
+}
+
+// virtual
+void LLPanelLogin::draw()
+{
+ if (!getVisible()) return;
+
+ BOOL target_fullscreen;
+ S32 target_width;
+ S32 target_height;
+ gViewerWindow->getTargetWindow(target_fullscreen, target_width, target_height);
+
+ childSetVisible("full_screen_text", target_fullscreen);
+
+ glPushMatrix();
+ {
+ F32 image_aspect = 1.333333f;
+ F32 view_aspect = (F32)mRect.getWidth() / (F32)mRect.getHeight();
+ // stretch image to maintain aspect ratio
+ if (image_aspect > view_aspect)
+ {
+ glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * mRect.getWidth(), 0.f, 0.f);
+ glScalef(image_aspect / view_aspect, 1.f, 1.f);
+ }
+ // Don't maintain aspect ratio if screen wider than image. This results in the
+ // hand being partially cut off. JC
+ //else
+ //{
+ // glTranslatef(0.f, -0.5f * (view_aspect / image_aspect - 1.f) * (F32)BLACK_BORDER_HEIGHT, 0.f);
+ // glScalef(1.f, view_aspect / image_aspect, 1.f);
+ //}
+
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ if ( mHtmlAvailable )
+ {
+ // draw a background box in black
+ gl_rect_2d( 0, height - 264, width, 264, LLColor4( 0.0f, 0.0f, 0.0f, 1.f ) );
+
+ // draw the bottom part of the background image - just the blue background to the native client UI
+ gl_draw_scaled_image(0, -264, width + 8, mLogoImage->getHeight(), mLogoImage);
+ }
+ else
+ {
+ // the HTML login page is not available so default to the original screen
+ S32 offscreen_part = height / 3;
+ gl_draw_scaled_image(0, -offscreen_part, width, height+offscreen_part, mLogoImage);
+ };
+ }
+ glPopMatrix();
+
+ LLPanel::draw();
+}
+
+// virtual
+BOOL LLPanelLogin::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (getVisible() && getEnabled())
+ {
+ if (( KEY_RETURN == key ) && (MASK_ALT == mask))
+ {
+ gViewerWindow->toggleFullscreen(FALSE);
+ return TRUE;
+ }
+
+ if (('P' == key) && (MASK_CONTROL == mask))
+ {
+ LLFloaterPreference::show(NULL);
+ return TRUE;
+ }
+
+ #if LL_LIBXUL_ENABLED
+ if ( KEY_F1 == key )
+ {
+ llinfos << "Spawning HTML help window" << llendl;
+ LLHtmlHelp::show( );
+ return TRUE;
+ };
+ #if ! LL_RELEASE_FOR_DOWNLOAD
+ if ( KEY_F2 == key )
+ {
+ llinfos << "Spawning floater TOS window" << llendl;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,"");
+ tos_dialog->startModal();
+ return TRUE;
+ };
+ #endif
+ #endif
+
+ if (!called_from_parent)
+ {
+ if (KEY_RETURN == key && MASK_NONE == mask)
+ {
+ // let the panel handle UICtrl processing: calls onClickConnect()
+ return LLPanel::handleKeyHere(key, mask, called_from_parent);
+ }
+ }
+ }
+
+ return LLPanel::handleKeyHere(key, mask, called_from_parent);
+}
+
+// virtual
+void LLPanelLogin::setFocus(BOOL b)
+{
+ if(b != hasFocus())
+ {
+ if(b)
+ {
+ LLPanelLogin::giveFocus();
+ }
+ else
+ {
+ LLPanel::setFocus(b);
+ }
+ }
+}
+
+// static
+void LLPanelLogin::giveFocus()
+{
+ if( sInstance )
+ {
+ // Grab focus and move cursor to first blank input field
+ std::string first = sInstance->childGetText("first_name_edit");
+ std::string pass = sInstance->childGetText("password_edit");
+
+ BOOL have_first = !first.empty();
+ BOOL have_pass = !pass.empty();
+
+ LLLineEditor* edit = NULL;
+ if (have_first && !have_pass)
+ {
+ // User saved his name but not his password. Move
+ // focus to password field.
+ edit = LLUICtrlFactory::getLineEditorByName(sInstance, "password_edit");
+ }
+ else
+ {
+ // User doesn't have a name, so start there.
+ edit = LLUICtrlFactory::getLineEditorByName(sInstance, "first_name_edit");
+ }
+
+ if (edit)
+ {
+ edit->setFocus(TRUE);
+ edit->selectAll();
+ }
+ }
+}
+
+
+// static
+void LLPanelLogin::show(const LLRect &rect,
+ BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void* callback_data)
+{
+ new LLPanelLogin(rect, show_server, callback, callback_data);
+
+ if( !gFocusMgr.getKeyboardFocus() )
+ {
+ // Grab focus and move cursor to first enabled control
+ sInstance->setFocus(TRUE);
+ // make sure that focus always goes here
+ gFocusMgr.setDefaultKeyboardFocus(sInstance);
+ }
+}
+
+// static
+void LLPanelLogin::setFields(const std::string& firstname, const std::string& lastname, const std::string& password,
+ BOOL remember)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted fillFields with no login view shown" << llendl;
+ return;
+ }
+
+ sInstance->childSetText("first_name_edit", firstname);
+ sInstance->childSetText("last_name_edit", lastname);
+
+ // Max "actual" password length is 16 characters.
+ // Hex digests are always 32 characters.
+ if (password.length() == 32)
+ {
+ // This is a MD5 hex digest of a password.
+ // We don't actually use the password input field,
+ // fill it with MAX_PASSWORD characters so we get a
+ // nice row of asterixes.
+ const char* filler = "123456789!123456";
+ sInstance->childSetText("password_edit", filler);
+ strcpy(sInstance->mIncomingPassword, filler);
+ strcpy(sInstance->mMungedPassword, password.c_str());
+ }
+ else
+ {
+ // this is a normal text password
+ sInstance->childSetText("password_edit", password);
+ strcpy(sInstance->mIncomingPassword, password.c_str());
+ LLMD5 pass((unsigned char *)password.c_str());
+ pass.hex_digest(sInstance->mMungedPassword);
+ }
+
+ sInstance->childSetValue("remember_check", remember);
+}
+
+
+// static
+void LLPanelLogin::addServer(const char *server, S32 domain_name)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted addServer with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ combo->add(server, LLSD(domain_name) );
+ combo->setCurrentByIndex(0);
+ }
+}
+
+// static
+void LLPanelLogin::getFields(LLString &firstname, LLString &lastname, LLString &password,
+ BOOL &remember)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getFields with no login view shown" << llendl;
+ return;
+ }
+
+ firstname = sInstance->childGetText("first_name_edit");
+ LLString::trim(firstname);
+
+ lastname = sInstance->childGetText("last_name_edit");
+ LLString::trim(lastname);
+
+ password.assign( sInstance->mMungedPassword );
+ remember = sInstance->childGetValue("remember_check");
+}
+
+
+// static
+void LLPanelLogin::getServer(LLString &server, S32 &domain_name)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getServer with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ LLSD combo_val = combo->getValue();
+ if (LLSD::TypeInteger == combo_val.type())
+ {
+ domain_name = combo->getValue().asInteger();
+
+ if ((S32)USERSERVER_OTHER == domain_name)
+ {
+ server = gUserServerName;
+ }
+ }
+ else
+ {
+ // no valid selection, return other
+ domain_name = (S32)USERSERVER_OTHER;
+ server = combo_val.asString();
+ }
+ }
+}
+
+// static
+void LLPanelLogin::getLocation(LLString &location)
+{
+ if (!sInstance)
+ {
+ llwarns << "Attempted getLocation with no login view shown" << llendl;
+ return;
+ }
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "start_location_combo");
+ if (combo)
+ {
+ location = combo->getValue().asString();
+ }
+}
+
+// static
+void LLPanelLogin::refreshLocation( bool force_visible )
+{
+ if (!sInstance) return;
+
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "start_location_combo");
+ if (!combo) return;
+
+ LLString sim_string = LLURLSimString::sInstance.mSimString;
+ if (!sim_string.empty())
+ {
+ combo->setCurrentByIndex( 3 ); // BUG? Maybe 2?
+ combo->setTextEntry(sim_string);
+ }
+ else
+ {
+ BOOL login_last = gSavedSettings.getBOOL("LoginLastLocation");
+ combo->setCurrentByIndex( login_last ? 1 : 0 );
+ }
+
+ BOOL show_start = TRUE;
+
+ if ( ! force_visible )
+ show_start = gSavedSettings.getBOOL("ShowStartLocation");
+
+ sInstance->childSetVisible("start_location_combo", show_start);
+ sInstance->childSetVisible("start_location_text", show_start);
+}
+
+// static
+void LLPanelLogin::close()
+{
+ if (sInstance)
+ {
+ gViewerWindow->getRootView()->removeChild( LLPanelLogin::sInstance );
+
+ gFocusMgr.setDefaultKeyboardFocus(NULL);
+
+ delete sInstance;
+ sInstance = NULL;
+ }
+}
+
+
+//---------------------------------------------------------------------------
+// Protected methods
+//---------------------------------------------------------------------------
+
+// static
+void LLPanelLogin::onClickConnect(void *)
+{
+ if (sInstance && sInstance->mCallback)
+ {
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ // JC - Make sure the fields all get committed.
+ sInstance->setFocus(FALSE);
+
+ LLString first = sInstance->childGetText("first_name_edit");
+ LLString last = sInstance->childGetText("last_name_edit");
+ if (!first.empty() && !last.empty())
+ {
+ // has both first and last name typed
+
+ // store off custom server entry, if currently selected
+ LLComboBox* combo = LLUICtrlFactory::getComboBoxByName(sInstance, "server_combo");
+ if (combo)
+ {
+ S32 selected_server = combo->getValue();
+ if (selected_server == USERSERVER_NONE)
+ {
+ LLString custom_server = combo->getValue().asString();
+ gSavedSettings.setString("CustomServer", custom_server);
+ }
+ }
+ sInstance->mCallback(0, sInstance->mCallbackData);
+ }
+ else
+ {
+ // empty first or last name
+ // same as clicking new account
+ onClickNewAccount(NULL);
+ }
+ }
+}
+
+
+// static
+void LLPanelLogin::newAccountAlertCallback(S32 option, void*)
+{
+ if (0 == option)
+ {
+ llinfos << "Going to account creation URL" << llendl;
+ LLWeb::loadURL( CREATE_ACCOUNT_URL );
+ }
+ else
+ {
+ sInstance->setFocus(TRUE);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickNewAccount(void*)
+{
+ if (gHideLinks)
+ {
+ gViewerWindow->alertXml("MustHaveAccountToLogInNoLinks");
+ }
+ else
+ {
+ gViewerWindow->alertXml("MustHaveAccountToLogIn",
+ LLPanelLogin::newAccountAlertCallback);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickQuit(void*)
+{
+ if (sInstance && sInstance->mCallback)
+ {
+ // tell the responder we're not here anymore
+ if ( gResponsePtr )
+ gResponsePtr->setParent( 0 );
+
+ sInstance->mCallback(1, sInstance->mCallbackData);
+ }
+}
+
+
+// static
+void LLPanelLogin::onClickVersion(void*)
+{
+ LLFloaterAbout::show(NULL);
+}
+
+// static
+void LLPanelLogin::onPassKey(LLLineEditor* caller, void* user_data)
+{
+ if (gKeyboard->getKeyDown(KEY_CAPSLOCK) && sCapslockDidNotification == FALSE)
+ {
+ LLNotifyBox::showXml("CapsKeyOn");
+ sCapslockDidNotification = TRUE;
+ }
+}
diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h
new file mode 100644
index 0000000000..060f2f6732
--- /dev/null
+++ b/indra/newview/llpanellogin.h
@@ -0,0 +1,83 @@
+/**
+ * @file llpanellogin.h
+ * @brief Login username entry fields.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELLOGIN_H
+#define LL_LLPANELLOGIN_H
+
+#include "llpanel.h"
+
+#include "lldbstrings.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+#include "llstring.h"
+
+class LLTextBox;
+class LLLineEditor;
+class LLCheckBoxCtrl;
+class LLButton;
+class LLComboBox;
+
+class LLPanelLogin
+: public LLPanel
+{
+public:
+ LLPanelLogin(const LLRect &rect, BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void *callback_data);
+ ~LLPanelLogin();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void draw();
+ virtual void setFocus( BOOL b );
+
+ static void show(const LLRect &rect, BOOL show_server,
+ void (*callback)(S32 option, void* user_data),
+ void* callback_data);
+
+ static void setFields(const std::string& firstname, const std::string& lastname,
+ const std::string& password, BOOL remember);
+
+ static void addServer(const char *server, S32 domain_name);
+ static void refreshLocation( bool force_visible );
+
+ static void getFields(LLString &firstname, LLString &lastname,
+ LLString &password, BOOL &remember);
+
+ static void getServer(LLString &server, S32& domain_name);
+ static void getLocation(LLString &location);
+
+ static void close();
+
+ void setSiteIsAlive( bool alive );
+
+ static void giveFocus();
+ static void mungePassword(LLUICtrl* caller, void* user_data);
+
+private:
+ static void onClickConnect(void*);
+ static void onClickNewAccount(void*);
+ static void newAccountAlertCallback(S32 option, void*);
+ static void onClickQuit(void*);
+ static void onClickVersion(void*);
+ static void onPassKey(LLLineEditor* caller, void* user_data);
+
+private:
+ LLPointer<LLViewerImage> mLogoImage;
+
+ void (*mCallback)(S32 option, void *userdata);
+ void* mCallbackData;
+
+ char mIncomingPassword[DB_USER_PASSWORD_BUF_SIZE];
+ char mMungedPassword[MD5HEX_STR_SIZE];
+
+ static LLPanelLogin* sInstance;
+ static BOOL sCapslockDidNotification;
+ BOOL mHtmlAvailable;
+};
+
+#endif
diff --git a/indra/newview/llpanelobject.cpp b/indra/newview/llpanelobject.cpp
new file mode 100644
index 0000000000..71eeef97ff
--- /dev/null
+++ b/indra/newview/llpanelobject.cpp
@@ -0,0 +1,1679 @@
+/**
+ * @file llpanelobject.cpp
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelobject.h"
+
+// linden library includes
+#include "lleconomy.h"
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+#include "llvolume.h"
+#include "m3math.h"
+
+// project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "llfocusmgr.h"
+#include "llmanipscale.h"
+#include "llpanelinventory.h"
+#include "llpreviewscript.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltool.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lldrawpool.h"
+
+//
+// Constants
+//
+enum {
+ MI_BOX,
+ MI_CYLINDER,
+ MI_PRISM,
+ MI_SPHERE,
+ MI_TORUS,
+ MI_TUBE,
+ MI_RING,
+ MI_NONE,
+ MI_VOLUME_COUNT
+};
+
+enum {
+ MI_HOLE_SAME,
+ MI_HOLE_CIRCLE,
+ MI_HOLE_SQUARE,
+ MI_HOLE_TRIANGLE,
+ MI_HOLE_COUNT
+};
+
+//XUI:translate (depricated, so very low priority)
+static const LLString LEGACY_FULLBRIGHT_DESC("Fullbright (Legacy)");
+
+BOOL LLPanelObject::postBuild()
+{
+ setMouseOpaque(FALSE);
+
+ //--------------------------------------------------------
+ // Top
+ //--------------------------------------------------------
+
+ // Lock checkbox
+ mCheckLock = gUICtrlFactory->getCheckBoxByName(this,"checkbox locked");
+ childSetCommitCallback("checkbox locked",onCommitLock,this);
+
+ // Physical checkbox
+ mCheckPhysics = gUICtrlFactory->getCheckBoxByName(this,"Physical Checkbox Ctrl");
+ childSetCommitCallback("Physical Checkbox Ctrl",onCommitPhysics,this);
+
+ // Temporary checkbox
+ mCheckTemporary = gUICtrlFactory->getCheckBoxByName(this,"Temporary Checkbox Ctrl");
+ childSetCommitCallback("Temporary Checkbox Ctrl",onCommitTemporary,this);
+
+ // Phantom checkbox
+ mCheckPhantom = gUICtrlFactory->getCheckBoxByName(this,"Phantom Checkbox Ctrl");
+ childSetCommitCallback("Phantom Checkbox Ctrl",onCommitPhantom,this);
+
+ // Position
+ mLabelPosition = gUICtrlFactory->getTextBoxByName(this,"label position");
+ mCtrlPosX = gUICtrlFactory->getSpinnerByName(this,"Pos X");
+ childSetCommitCallback("Pos X",onCommitPosition,this);
+ mCtrlPosY = gUICtrlFactory->getSpinnerByName(this,"Pos Y");
+ childSetCommitCallback("Pos Y",onCommitPosition,this);
+ mCtrlPosZ = gUICtrlFactory->getSpinnerByName(this,"Pos Z");
+ childSetCommitCallback("Pos Z",onCommitPosition,this);
+
+ // Scale
+ mLabelSize = gUICtrlFactory->getTextBoxByName(this,"label size");
+ mCtrlScaleX = gUICtrlFactory->getSpinnerByName(this,"Scale X");
+ childSetCommitCallback("Scale X",onCommitScale,this);
+
+ // Scale Y
+ mCtrlScaleY = gUICtrlFactory->getSpinnerByName(this,"Scale Y");
+ childSetCommitCallback("Scale Y",onCommitScale,this);
+
+ // Scale Z
+ mCtrlScaleZ = gUICtrlFactory->getSpinnerByName(this,"Scale Z");
+ childSetCommitCallback("Scale Z",onCommitScale,this);
+
+ // Rotation
+ mLabelRotation = gUICtrlFactory->getTextBoxByName(this,"label rotation");
+ mCtrlRotX = gUICtrlFactory->getSpinnerByName(this,"Rot X");
+ childSetCommitCallback("Rot X",onCommitRotation,this);
+ mCtrlRotY = gUICtrlFactory->getSpinnerByName(this,"Rot Y");
+ childSetCommitCallback("Rot Y",onCommitRotation,this);
+ mCtrlRotZ = gUICtrlFactory->getSpinnerByName(this,"Rot Z");
+ childSetCommitCallback("Rot Z",onCommitRotation,this);
+
+ //--------------------------------------------------------
+
+ // material type popup
+ mLabelMaterial = gUICtrlFactory->getTextBoxByName(this,"label material");
+ mComboMaterial = gUICtrlFactory->getComboBoxByName(this,"material");
+ childSetCommitCallback("material",onCommitMaterial,this);
+ mComboMaterial->removeall();
+ // XUI:translate
+ LLMaterialInfo *minfop;
+ for (minfop = LLMaterialTable::basic.mMaterialInfoList.getFirstData();
+ minfop != NULL;
+ minfop = LLMaterialTable::basic.mMaterialInfoList.getNextData())
+ {
+ if (minfop->mMCode != LL_MCODE_LIGHT)
+ {
+ mComboMaterial->add(minfop->mName);
+ }
+ }
+ mComboMaterialItemCount = mComboMaterial->getItemCount();
+
+ // Base Type
+ mLabelBaseType = gUICtrlFactory->getTextBoxByName(this,"label basetype");
+ mComboBaseType = gUICtrlFactory->getComboBoxByName(this,"comboBaseType");
+ childSetCommitCallback("comboBaseType",onCommitParametric,this);
+
+ // Cut
+ mLabelCut = gUICtrlFactory->getTextBoxByName(this,"text cut");
+ mSpinCutBegin = gUICtrlFactory->getSpinnerByName(this,"cut begin");
+ childSetCommitCallback("cut begin",onCommitParametric,this);
+ mSpinCutBegin->setValidateBeforeCommit( precommitValidate );
+ mSpinCutEnd = gUICtrlFactory->getSpinnerByName(this,"cut end");
+ childSetCommitCallback("cut end",onCommitParametric,this);
+ mSpinCutEnd->setValidateBeforeCommit( &precommitValidate );
+
+ // Hollow / Skew
+ mLabelHollow = gUICtrlFactory->getTextBoxByName(this,"text hollow");
+ mLabelSkew = gUICtrlFactory->getTextBoxByName(this,"text skew");
+ mSpinHollow = gUICtrlFactory->getSpinnerByName(this,"Scale 1");
+ childSetCommitCallback("Scale 1",onCommitParametric,this);
+ mSpinHollow->setValidateBeforeCommit( &precommitValidate );
+ mSpinSkew = gUICtrlFactory->getSpinnerByName(this,"Skew");
+ childSetCommitCallback("Skew",onCommitParametric,this);
+ mSpinSkew->setValidateBeforeCommit( &precommitValidate );
+ mLabelHoleType = gUICtrlFactory->getTextBoxByName(this,"Hollow Shape");
+
+ // Hole Type
+ mComboHoleType = gUICtrlFactory->getComboBoxByName(this,"hole");
+ childSetCommitCallback("hole",onCommitParametric,this);
+
+ // Twist
+ mLabelTwist = gUICtrlFactory->getTextBoxByName(this,"text twist");
+ mSpinTwistBegin = gUICtrlFactory->getSpinnerByName(this,"Twist Begin");
+ childSetCommitCallback("Twist Begin",onCommitParametric,this);
+ mSpinTwistBegin->setValidateBeforeCommit( precommitValidate );
+ mSpinTwist = gUICtrlFactory->getSpinnerByName(this,"Twist End");
+ childSetCommitCallback("Twist End",onCommitParametric,this);
+ mSpinTwist->setValidateBeforeCommit( &precommitValidate );
+
+ // Scale
+ mSpinScaleX = gUICtrlFactory->getSpinnerByName(this,"Taper Scale X");
+ childSetCommitCallback("Taper Scale X",onCommitParametric,this);
+ mSpinScaleX->setValidateBeforeCommit( &precommitValidate );
+ mSpinScaleY = gUICtrlFactory->getSpinnerByName(this,"Taper Scale Y");
+ childSetCommitCallback("Taper Scale Y",onCommitParametric,this);
+ mSpinScaleY->setValidateBeforeCommit( &precommitValidate );
+
+ // Shear
+ mLabelShear = gUICtrlFactory->getTextBoxByName(this,"text topshear");
+ mSpinShearX = gUICtrlFactory->getSpinnerByName(this,"Shear X");
+ childSetCommitCallback("Shear X",onCommitParametric,this);
+ mSpinShearX->setValidateBeforeCommit( &precommitValidate );
+ mSpinShearY = gUICtrlFactory->getSpinnerByName(this,"Shear Y");
+ childSetCommitCallback("Shear Y",onCommitParametric,this);
+ mSpinShearY->setValidateBeforeCommit( &precommitValidate );
+
+ // Path / Profile
+ mCtrlPathBegin = gUICtrlFactory->getSpinnerByName(this,"Path Limit Begin");
+ childSetCommitCallback("Path Limit Begin",onCommitParametric,this);
+ mCtrlPathBegin->setValidateBeforeCommit( &precommitValidate );
+ mCtrlPathEnd = gUICtrlFactory->getSpinnerByName(this,"Path Limit End");
+ childSetCommitCallback("Path Limit End",onCommitParametric,this);
+ mCtrlPathEnd->setValidateBeforeCommit( &precommitValidate );
+
+ // Taper
+ mLabelTaper = gUICtrlFactory->getTextBoxByName(this,"text taper2");
+ mSpinTaperX = gUICtrlFactory->getSpinnerByName(this,"Taper X");
+ childSetCommitCallback("Taper X",onCommitParametric,this);
+ mSpinTaperX->setValidateBeforeCommit( precommitValidate );
+ mSpinTaperY = gUICtrlFactory->getSpinnerByName(this,"Taper Y");
+ childSetCommitCallback("Taper Y",onCommitParametric,this);
+ mSpinTaperY->setValidateBeforeCommit( precommitValidate );
+
+ // Radius Offset / Revolutions
+ mLabelRadiusOffset = gUICtrlFactory->getTextBoxByName(this,"text radius delta");
+ mLabelRevolutions = gUICtrlFactory->getTextBoxByName(this,"text revolutions");
+ mSpinRadiusOffset = gUICtrlFactory->getSpinnerByName(this,"Radius Offset");
+ childSetCommitCallback("Radius Offset",onCommitParametric,this);
+ mSpinRadiusOffset->setValidateBeforeCommit( &precommitValidate );
+ mSpinRevolutions = gUICtrlFactory->getSpinnerByName(this,"Revolutions");
+ childSetCommitCallback("Revolutions",onCommitParametric,this);
+ mSpinRevolutions->setValidateBeforeCommit( &precommitValidate );
+
+ // Start with everyone disabled
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelObject::LLPanelObject(const std::string& name)
+: LLPanel(name),
+ mIsPhysical(FALSE),
+ mIsTemporary(FALSE),
+ mIsPhantom(FALSE),
+ mCastShadows(TRUE),
+ mSelectedType(MI_BOX)
+{
+}
+
+
+LLPanelObject::~LLPanelObject()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLPanelObject::getState( )
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ LLViewerObject* root_objectp = objectp;
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ //FIXME: shouldn't we just keep the child?
+ if (objectp)
+ {
+ LLViewerObject* parentp = objectp->getSubParent();
+
+ if (parentp)
+ {
+ root_objectp = parentp;
+ }
+ else
+ {
+ root_objectp = objectp;
+ }
+ }
+ }
+
+ LLVOVolume *volobjp = NULL;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ volobjp = (LLVOVolume *)objectp;
+ }
+
+ if( !objectp )
+ {
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ // Disable all text input fields
+ clearCtrls();
+ return;
+ }
+
+ // can move or rotate only linked group with move permissions, or sub-object with move and modify perms
+ BOOL enable_move = objectp->permMove() && !objectp->isAttachment() && (objectp->permModify() || gSavedSettings.getBOOL("SelectLinkedSet"));
+ BOOL enable_scale = objectp->permMove() && objectp->permModify();
+ BOOL enable_rotate = objectp->permMove() && ( (objectp->permModify() && !objectp->isAttachment()) || gSavedSettings.getBOOL("SelectLinkedSet"));
+
+ LLVector3 vec;
+ if (enable_move)
+ {
+ vec = objectp->getPositionEdit();
+ mCtrlPosX->set( vec.mV[VX] );
+ mCtrlPosY->set( vec.mV[VY] );
+ mCtrlPosZ->set( vec.mV[VZ] );
+ }
+ else
+ {
+ mCtrlPosX->clear();
+ mCtrlPosY->clear();
+ mCtrlPosZ->clear();
+ }
+
+
+ mLabelPosition->setEnabled( enable_move );
+ mCtrlPosX->setEnabled(enable_move);
+ mCtrlPosY->setEnabled(enable_move);
+ mCtrlPosZ->setEnabled(enable_move);
+
+ if (enable_scale)
+ {
+ vec = objectp->getScale();
+ mCtrlScaleX->set( vec.mV[VX] );
+ mCtrlScaleY->set( vec.mV[VY] );
+ mCtrlScaleZ->set( vec.mV[VZ] );
+ }
+ else
+ {
+ mCtrlScaleX->clear();
+ mCtrlScaleY->clear();
+ mCtrlScaleZ->clear();
+ }
+
+ mLabelSize->setEnabled( enable_scale );
+ mCtrlScaleX->setEnabled( enable_scale );
+ mCtrlScaleY->setEnabled( enable_scale );
+ mCtrlScaleZ->setEnabled( enable_scale );
+
+ 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);
+
+ if (enable_rotate)
+ {
+ mCtrlRotX->set( mCurEulerDegrees.mV[VX] );
+ mCtrlRotY->set( mCurEulerDegrees.mV[VY] );
+ mCtrlRotZ->set( mCurEulerDegrees.mV[VZ] );
+ }
+ else
+ {
+ mCtrlRotX->clear();
+ mCtrlRotY->clear();
+ mCtrlRotZ->clear();
+ }
+
+ mLabelRotation->setEnabled( enable_rotate );
+ mCtrlRotX->setEnabled( enable_rotate );
+ mCtrlRotY->setEnabled( enable_rotate );
+ mCtrlRotZ->setEnabled( enable_rotate );
+
+ BOOL owners_identical;
+ LLUUID owner_id;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ // BUG? Check for all objects being editable?
+ S32 roots_selected = gSelectMgr->getRootObjectCount();
+ BOOL editable = root_objectp->permModify();
+ S32 selected_count = gSelectMgr->getObjectCount();
+ BOOL single_volume = (gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME ))
+ && (selected_count == 1);
+
+ // Select Single Message
+ childSetVisible("select_single", FALSE);
+ childSetVisible("edit_object", FALSE);
+ if (!editable || single_volume || selected_count <= 1)
+ {
+ childSetVisible("edit_object", TRUE);
+ childSetEnabled("edit_object", TRUE);
+ }
+ else
+ {
+ childSetVisible("select_single", TRUE);
+ childSetEnabled("select_single", TRUE);
+ }
+ // Lock checkbox - only modifiable if you own the object.
+ BOOL self_owned = (gAgent.getID() == owner_id);
+ mCheckLock->setEnabled( roots_selected > 0 && self_owned );
+
+ // More lock and debit checkbox - get the values
+ BOOL valid;
+ U32 owner_mask_on;
+ U32 owner_mask_off;
+ valid = gSelectMgr->selectGetPerm(PERM_OWNER, &owner_mask_on, &owner_mask_off);
+
+ if(valid)
+ {
+ if(owner_mask_on & PERM_MOVE)
+ {
+ // owner can move, so not locked
+ mCheckLock->set(FALSE);
+ mCheckLock->setTentative(FALSE);
+ }
+ else if(owner_mask_off & PERM_MOVE)
+ {
+ // owner can't move, so locked
+ mCheckLock->set(TRUE);
+ mCheckLock->setTentative(FALSE);
+ }
+ else
+ {
+ // some locked, some not locked
+ mCheckLock->set(FALSE);
+ mCheckLock->setTentative(TRUE);
+ }
+ }
+
+ BOOL is_flexible = volobjp && volobjp->isFlexible();
+
+ // Physics checkbox
+ mIsPhysical = root_objectp->usePhysics();
+ mCheckPhysics->set( mIsPhysical );
+ mCheckPhysics->setEnabled( roots_selected==1
+ && (editable || gAgent.isGodlike())
+ && !is_flexible);
+
+ mIsTemporary = root_objectp->flagTemporaryOnRez();
+ mCheckTemporary->set( mIsTemporary );
+ mCheckTemporary->setEnabled( roots_selected==1 && editable );
+
+ mIsPhantom = root_objectp->flagPhantom();
+ mCheckPhantom->set( mIsPhantom );
+ mCheckPhantom->setEnabled( roots_selected==1 && editable && !is_flexible );
+
+#if 0 // 1.9.2
+ mCastShadows = root_objectp->flagCastShadows();
+ mCheckCastShadows->set( mCastShadows );
+ mCheckCastShadows->setEnabled( roots_selected==1 && editable );
+#endif
+
+ // Update material part
+ U8 material_code;
+ BOOL material_same = gSelectMgr->selectionGetMaterial(&material_code);
+ if (editable && single_volume && material_same)
+ {
+ mComboMaterial->setEnabled( TRUE );
+ mLabelMaterial->setEnabled( TRUE );
+ if (material_code == LL_MCODE_LIGHT)
+ {
+ if (mComboMaterial->getItemCount() == mComboMaterialItemCount)
+ {
+ mComboMaterial->add(LEGACY_FULLBRIGHT_DESC);
+ }
+ mComboMaterial->setSimple(LEGACY_FULLBRIGHT_DESC);
+ }
+ else
+ {
+ if (mComboMaterial->getItemCount() != mComboMaterialItemCount)
+ {
+ mComboMaterial->remove(LEGACY_FULLBRIGHT_DESC);
+ }
+ mComboMaterial->setSimple(LLMaterialTable::basic.getName(material_code));
+ }
+ }
+ else
+ {
+ mComboMaterial->setEnabled( FALSE );
+ mLabelMaterial->setEnabled( FALSE );
+ }
+ //----------------------------------------------------------------------------
+
+ S32 selected_item = MI_BOX;
+ S32 selected_hole = MI_HOLE_SAME;
+ BOOL enabled = FALSE;
+ BOOL hole_enabled = FALSE;
+ F32 scale_x=1.f, scale_y=1.f;
+
+ if( !objectp || !objectp->getVolume() || !editable || !single_volume)
+ {
+ // Clear out all geometry fields.
+ mComboBaseType->clear();
+ mSpinHollow->clear();
+ mSpinCutBegin->clear();
+ mSpinCutEnd->clear();
+ mCtrlPathBegin->clear();
+ mCtrlPathEnd->clear();
+ mSpinScaleX->clear();
+ mSpinScaleY->clear();
+ mSpinTwist->clear();
+ mSpinTwistBegin->clear();
+ mComboHoleType->clear();
+ mSpinShearX->clear();
+ mSpinShearY->clear();
+ mSpinTaperX->clear();
+ mSpinTaperY->clear();
+ mSpinRadiusOffset->clear();
+ mSpinRevolutions->clear();
+ mSpinSkew->clear();
+
+ mSelectedType = MI_NONE;
+ }
+ else
+ {
+ // Only allowed to change these parameters for objects
+ // that you have permissions on AND are not attachments.
+ enabled = root_objectp->permModify();
+
+ const LLVolumeParams &volume_params = objectp->getVolume()->getParams();
+
+ // Volume type
+ U8 path = volume_params.getPathParams().getCurveType();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ U8 profile = profile_and_hole & LL_PCODE_PROFILE_MASK;
+ U8 hole = profile_and_hole & LL_PCODE_HOLE_MASK;
+
+ // Scale goes first so we can differentiate between a sphere and a torus,
+ // which have the same profile and path types.
+
+ // Scale
+ scale_x = volume_params.getRatioX();
+ scale_y = volume_params.getRatioY();
+
+ BOOL linear_path = (path == LL_PCODE_PATH_LINE) || (path == LL_PCODE_PATH_FLEXIBLE);
+ if ( linear_path && profile == LL_PCODE_PROFILE_CIRCLE )
+ {
+ selected_item = MI_CYLINDER;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_SQUARE )
+ {
+ selected_item = MI_BOX;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_ISOTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_EQUALTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if ( linear_path && profile == LL_PCODE_PROFILE_RIGHTTRI )
+ {
+ selected_item = MI_PRISM;
+ }
+ else if (path == LL_PCODE_PATH_FLEXIBLE) // shouldn't happen
+ {
+ selected_item = MI_CYLINDER; // reasonable default
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y > 0.75f)
+ {
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE && scale_y <= 0.75f)
+ {
+ selected_item = MI_TORUS;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_CIRCLE_HALF)
+ {
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE2 && profile == LL_PCODE_PROFILE_CIRCLE )
+ {
+ // Spirals aren't supported. Make it into a sphere. JC
+ selected_item = MI_SPHERE;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_EQUALTRI )
+ {
+ selected_item = MI_RING;
+ }
+ else if ( path == LL_PCODE_PATH_CIRCLE && profile == LL_PCODE_PROFILE_SQUARE && scale_y <= 0.75f)
+ {
+ selected_item = MI_TUBE;
+ }
+ else
+ {
+ llinfos << "Unknown path " << (S32) path << " profile " << (S32) profile << " in getState" << llendl;
+ selected_item = MI_BOX;
+ }
+ mComboBaseType ->setCurrentByIndex( selected_item );
+ mSelectedType = selected_item;
+
+ // Grab S path
+ F32 begin_s = volume_params.getBeginS();
+ F32 end_s = volume_params.getEndS();
+
+ // Compute cut and advanced cut from S and T
+ F32 begin_t = volume_params.getBeginT();
+ F32 end_t = volume_params.getEndT();
+
+ // Hollowness
+ F32 hollow = volume_params.getHollow();
+ mSpinHollow->set( 100.f * hollow );
+
+ // All hollow objects allow a shape to be selected.
+ if (hollow > 0.f)
+ {
+ switch (hole)
+ {
+ case LL_PCODE_HOLE_CIRCLE:
+ selected_hole = MI_HOLE_CIRCLE;
+ break;
+ case LL_PCODE_HOLE_SQUARE:
+ selected_hole = MI_HOLE_SQUARE;
+ break;
+ case LL_PCODE_HOLE_TRIANGLE:
+ selected_hole = MI_HOLE_TRIANGLE;
+ break;
+ case LL_PCODE_HOLE_SAME:
+ default:
+ selected_hole = MI_HOLE_SAME;
+ break;
+ }
+ mComboHoleType->setCurrentByIndex( selected_hole );
+ hole_enabled = enabled;
+ }
+ else
+ {
+ mComboHoleType->setCurrentByIndex( MI_HOLE_SAME );
+ hole_enabled = FALSE;
+ }
+
+ // Cut interpretation varies based on base object type
+ F32 cut_begin, cut_end, adv_cut_begin, adv_cut_end;
+
+ if ( selected_item == MI_SPHERE || selected_item == MI_TORUS ||
+ selected_item == MI_TUBE || selected_item == MI_RING )
+ {
+ cut_begin = begin_t;
+ cut_end = end_t;
+ adv_cut_begin = begin_s;
+ adv_cut_end = end_s;
+ }
+ else
+ {
+ cut_begin = begin_s;
+ cut_end = end_s;
+ adv_cut_begin = begin_t;
+ adv_cut_end = end_t;
+ }
+
+ mSpinCutBegin ->set( cut_begin );
+ mSpinCutEnd ->set( cut_end );
+ mCtrlPathBegin ->set( adv_cut_begin );
+ mCtrlPathEnd ->set( adv_cut_end );
+
+ // Twist
+ F32 twist = volume_params.getTwist();
+ F32 twist_begin = volume_params.getTwistBegin();
+ // Check the path type for conversion.
+ if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE)
+ {
+ twist *= OBJECT_TWIST_LINEAR_MAX;
+ twist_begin *= OBJECT_TWIST_LINEAR_MAX;
+ }
+ else
+ {
+ twist *= OBJECT_TWIST_MAX;
+ twist_begin *= OBJECT_TWIST_MAX;
+ }
+
+ mSpinTwist ->set( twist );
+ mSpinTwistBegin ->set( twist_begin );
+
+ // Shear
+ F32 shear_x = volume_params.getShearX();
+ F32 shear_y = volume_params.getShearY();
+ mSpinShearX->set( shear_x );
+ mSpinShearY->set( shear_y );
+
+ // Taper
+ F32 taper_x = volume_params.getTaperX();
+ F32 taper_y = volume_params.getTaperY();
+ mSpinTaperX->set( taper_x );
+ mSpinTaperY->set( taper_y );
+
+ // Radius offset.
+ F32 radius_offset = volume_params.getRadiusOffset();
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(scale_y);
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+ // Enforce the maximum magnitude.
+ if (radius_mag > max_radius_mag)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ }
+ mSpinRadiusOffset->set( radius_offset);
+
+ // Revolutions
+ F32 revolutions = volume_params.getRevolutions();
+ mSpinRevolutions->set( revolutions );
+
+ // Skew
+ F32 skew = volume_params.getSkew();
+ // Limit skew, based on revolutions hole size x.
+ F32 skew_mag= fabs(skew);
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ if (skew_mag < min_skew_mag)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ }
+ mSpinSkew->set( skew );
+ }
+
+ // Compute control visibility, label names, and twist range.
+ // Start with defaults.
+ BOOL top_size_x_visible = TRUE;
+ BOOL top_size_y_visible = TRUE;
+ BOOL top_shear_x_visible = TRUE;
+ BOOL top_shear_y_visible = TRUE;
+ BOOL twist_visible = TRUE;
+ BOOL advanced_cut_visible = FALSE;
+ BOOL taper_visible = FALSE;
+ BOOL skew_visible = FALSE;
+ BOOL radius_offset_visible = FALSE;
+ BOOL revolutions_visible = FALSE;
+ F32 twist_min = OBJECT_TWIST_LINEAR_MIN;
+ F32 twist_max = OBJECT_TWIST_LINEAR_MAX;
+ F32 twist_inc = OBJECT_TWIST_LINEAR_INC;
+
+ BOOL advanced_is_dimple = FALSE;
+ BOOL size_is_hole = FALSE;
+
+ // Tune based on overall volume type
+ switch (selected_item)
+ {
+ case MI_SPHERE:
+ top_size_x_visible = FALSE;
+ top_size_y_visible = FALSE;
+ top_shear_x_visible = FALSE;
+ top_shear_y_visible = FALSE;
+ //twist_visible = FALSE;
+ advanced_cut_visible = TRUE;
+ advanced_is_dimple = TRUE;
+ twist_min = OBJECT_TWIST_MIN;
+ twist_max = OBJECT_TWIST_MAX;
+ twist_inc = OBJECT_TWIST_INC;
+ break;
+
+ case MI_TORUS:
+ case MI_TUBE:
+ case MI_RING:
+ //top_size_x_visible = FALSE;
+ //top_size_y_visible = FALSE;
+ size_is_hole = TRUE;
+ skew_visible = TRUE;
+ advanced_cut_visible = TRUE;
+ taper_visible = TRUE;
+ radius_offset_visible = TRUE;
+ revolutions_visible = TRUE;
+ twist_min = OBJECT_TWIST_MIN;
+ twist_max = OBJECT_TWIST_MAX;
+ twist_inc = OBJECT_TWIST_INC;
+
+ break;
+
+ case MI_BOX:
+ case MI_CYLINDER:
+ case MI_PRISM:
+ default:
+ break;
+ }
+
+ // Check if we need to change top size/hole size params.
+ switch (selected_item)
+ {
+ case MI_SPHERE:
+ case MI_TORUS:
+ case MI_TUBE:
+ case MI_RING:
+ mSpinScaleX->set( scale_x );
+ mSpinScaleY->set( scale_y );
+ mSpinScaleX->setMinValue(OBJECT_MIN_HOLE_SIZE);
+ mSpinScaleX->setMaxValue(OBJECT_MAX_HOLE_SIZE_X);
+ mSpinScaleY->setMinValue(OBJECT_MIN_HOLE_SIZE);
+ mSpinScaleY->setMaxValue(OBJECT_MAX_HOLE_SIZE_Y);
+ break;
+ default:
+ mSpinScaleX->set( 1.f - scale_x );
+ mSpinScaleY->set( 1.f - scale_y );
+ mSpinScaleX->setMinValue(-1.f);
+ mSpinScaleX->setMaxValue(1.f);
+ mSpinScaleY->setMinValue(-1.f);
+ mSpinScaleY->setMaxValue(1.f);
+ break;
+ }
+
+ // Check if we need to limit the hollow based on the hole type.
+ if ( selected_hole == MI_HOLE_SQUARE &&
+ ( selected_item == MI_CYLINDER || selected_item == MI_TORUS ||
+ selected_item == MI_PRISM || selected_item == MI_RING ||
+ selected_item == MI_SPHERE ) )
+ {
+ mSpinHollow->setMinValue(0.f);
+ mSpinHollow->setMaxValue(70.f);
+ }
+ else
+ {
+ mSpinHollow->setMinValue(0.f);
+ mSpinHollow->setMaxValue(95.f);
+ }
+
+ // Update field enablement
+ mLabelBaseType ->setEnabled( enabled );
+ mComboBaseType ->setEnabled( enabled );
+
+ mLabelCut ->setEnabled( enabled );
+ mSpinCutBegin ->setEnabled( enabled );
+ mSpinCutEnd ->setEnabled( enabled );
+
+ mLabelHollow ->setEnabled( enabled );
+ mSpinHollow ->setEnabled( enabled );
+ mLabelHoleType ->setEnabled( hole_enabled );
+ mComboHoleType ->setEnabled( hole_enabled );
+
+ mLabelTwist ->setEnabled( enabled );
+ mSpinTwist ->setEnabled( enabled );
+ mSpinTwistBegin ->setEnabled( enabled );
+
+ mLabelSkew ->setEnabled( enabled );
+ mSpinSkew ->setEnabled( enabled );
+
+ childSetVisible("scale_hole", FALSE);
+ childSetVisible("scale_taper", FALSE);
+ if (top_size_x_visible || top_size_y_visible)
+ {
+ if (size_is_hole)
+ {
+ childSetVisible("scale_hole", TRUE);
+ childSetEnabled("scale_hole", enabled);
+ }
+ else
+ {
+ childSetVisible("scale_taper", TRUE);
+ childSetEnabled("scale_taper", enabled);
+ }
+ }
+
+ mSpinScaleX ->setEnabled( enabled );
+ mSpinScaleY ->setEnabled( enabled );
+
+ mLabelShear ->setEnabled( enabled );
+ mSpinShearX ->setEnabled( enabled );
+ mSpinShearY ->setEnabled( enabled );
+
+ childSetVisible("advanced_cut", FALSE);
+ childSetVisible("advanced_dimple", FALSE);
+ if (advanced_cut_visible)
+ {
+ if (advanced_is_dimple)
+ {
+ childSetVisible("advanced_dimple", TRUE);
+ childSetEnabled("advanced_dimple", enabled);
+ }
+ else
+ {
+ childSetVisible("advanced_cut", TRUE);
+ childSetEnabled("advanced_cut", enabled);
+ }
+ }
+
+ mCtrlPathBegin ->setEnabled( enabled );
+ mCtrlPathEnd ->setEnabled( enabled );
+
+ mLabelTaper ->setEnabled( enabled );
+ mSpinTaperX ->setEnabled( enabled );
+ mSpinTaperY ->setEnabled( enabled );
+
+ mLabelRadiusOffset->setEnabled( enabled );
+ mSpinRadiusOffset ->setEnabled( enabled );
+
+ mLabelRevolutions->setEnabled( enabled );
+ mSpinRevolutions ->setEnabled( enabled );
+
+ // Update field visibility
+ mLabelTwist ->setVisible( twist_visible );
+ mSpinTwist ->setVisible( twist_visible );
+ mSpinTwistBegin ->setVisible( twist_visible );
+ mSpinTwist ->setMinValue( twist_min );
+ mSpinTwist ->setMaxValue( twist_max );
+ mSpinTwist ->setIncrement( twist_inc );
+ mSpinTwistBegin ->setMinValue( twist_min );
+ mSpinTwistBegin ->setMaxValue( twist_max );
+ mSpinTwistBegin ->setIncrement( twist_inc );
+
+ mSpinScaleX ->setVisible( top_size_x_visible );
+ mSpinScaleY ->setVisible( top_size_y_visible );
+
+ mLabelSkew ->setVisible( skew_visible );
+ mSpinSkew ->setVisible( skew_visible );
+
+ mLabelShear ->setVisible( top_shear_x_visible || top_shear_y_visible );
+ mSpinShearX ->setVisible( top_shear_x_visible );
+ mSpinShearY ->setVisible( top_shear_y_visible );
+
+ mCtrlPathBegin ->setVisible( advanced_cut_visible );
+ mCtrlPathEnd ->setVisible( advanced_cut_visible );
+
+ mLabelTaper ->setVisible( taper_visible );
+ mSpinTaperX ->setVisible( taper_visible );
+ mSpinTaperY ->setVisible( taper_visible );
+
+ mLabelRadiusOffset->setVisible( radius_offset_visible );
+ mSpinRadiusOffset ->setVisible( radius_offset_visible );
+
+ mLabelRevolutions->setVisible( revolutions_visible );
+ mSpinRevolutions ->setVisible( revolutions_visible );
+
+ //----------------------------------------------------------------------------
+
+ mObject = objectp;
+ mRootObject = root_objectp;
+}
+
+// static
+BOOL LLPanelObject::precommitValidate( LLUICtrl* ctrl, void* userdata )
+{
+ // TODO: Richard will fill this in later.
+ return TRUE; // FALSE means that validation failed and new value should not be commited.
+}
+
+void LLPanelObject::sendIsPhysical()
+{
+ BOOL value = mCheckPhysics->get();
+ if( mIsPhysical != value )
+ {
+ gSelectMgr->selectionUpdatePhysics(value);
+ mIsPhysical = value;
+
+ llinfos << "update physics sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update physics not changed" << llendl;
+ }
+}
+
+void LLPanelObject::sendIsTemporary()
+{
+ BOOL value = mCheckTemporary->get();
+ if( mIsTemporary != value )
+ {
+ gSelectMgr->selectionUpdateTemporary(value);
+ mIsTemporary = value;
+
+ llinfos << "update temporary sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update temporary not changed" << llendl;
+ }
+}
+
+
+void LLPanelObject::sendIsPhantom()
+{
+ BOOL value = mCheckPhantom->get();
+ if( mIsPhantom != value )
+ {
+ gSelectMgr->selectionUpdatePhantom(value);
+ mIsPhantom = value;
+
+ llinfos << "update phantom sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update phantom not changed" << llendl;
+ }
+}
+
+void LLPanelObject::sendCastShadows()
+{
+ BOOL value = mCheckCastShadows->get();
+ if( mCastShadows != value )
+ {
+ gSelectMgr->selectionUpdateCastShadows(value);
+ mCastShadows = value;
+
+ llinfos << "update cast shadows sent" << llendl;
+ }
+ else
+ {
+ llinfos << "update cast shadows not changed" << llendl;
+ }
+}
+
+// static
+void LLPanelObject::onCommitMaterial( LLUICtrl* ctrl, void* userdata )
+{
+ //LLPanelObject* self = (LLPanelObject*) userdata;
+ LLComboBox* box = (LLComboBox*) ctrl;
+
+ if (box)
+ {
+ // apply the currently selected material to the object
+ const LLString& material_name = box->getSimple();
+ if (material_name != LEGACY_FULLBRIGHT_DESC)
+ {
+ U8 material_code = LLMaterialTable::basic.getMCode(material_name.c_str());
+ gSelectMgr->selectionSetMaterial(material_code);
+ }
+ }
+}
+
+// static
+void LLPanelObject::onCommitParametric( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+
+ if (self->mObject.isNull())
+ {
+ return;
+ }
+
+ if (self->mObject->getPCode() != LL_PCODE_VOLUME)
+ {
+ // Don't allow modification of non-volume objects.
+ return;
+ }
+
+ LLVolume *volume = self->mObject->getVolume();
+ if (!volume)
+ {
+ return;
+ }
+
+ LLVolumeParams volume_params;
+ self->getVolumeParams(volume_params);
+
+ // Update the volume, if necessary.
+ self->mObject->updateVolume(volume_params);
+
+ // This was added to make sure thate when changes are made, the UI
+ // adjusts to present valid options.
+ // *FIX: only some changes, ie, hollow or primitive type changes,
+ // require a refresh.
+ self->refresh();
+
+}
+
+void LLPanelObject::getVolumeParams(LLVolumeParams& volume_params)
+{
+ // Figure out what type of volume to make
+ S32 was_selected_type = mSelectedType;
+ S32 selected_type = mComboBaseType->getCurrentIndex();
+ U8 profile;
+ U8 path;
+ switch ( selected_type )
+ {
+ case MI_CYLINDER:
+ profile = LL_PCODE_PROFILE_CIRCLE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_BOX:
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_PRISM:
+ profile = LL_PCODE_PROFILE_EQUALTRI;
+ path = LL_PCODE_PATH_LINE;
+ break;
+
+ case MI_SPHERE:
+ profile = LL_PCODE_PROFILE_CIRCLE_HALF;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_TORUS:
+ profile = LL_PCODE_PROFILE_CIRCLE;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_TUBE:
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ case MI_RING:
+ profile = LL_PCODE_PROFILE_EQUALTRI;
+ path = LL_PCODE_PATH_CIRCLE;
+ break;
+
+ default:
+ llwarns << "Unknown base type " << selected_type
+ << " in getVolumeParams()" << llendl;
+ // assume a box
+ selected_type = MI_BOX;
+ profile = LL_PCODE_PROFILE_SQUARE;
+ path = LL_PCODE_PATH_LINE;
+ break;
+ }
+
+ if (path == LL_PCODE_PATH_LINE)
+ {
+ LLVOVolume *volobjp = (LLVOVolume *)(LLViewerObject*)(mObject);
+ if (volobjp->isFlexible())
+ {
+ path = LL_PCODE_PATH_FLEXIBLE;
+ }
+ }
+
+ S32 selected_hole = mComboHoleType->getCurrentIndex();
+ U8 hole;
+ switch (selected_hole)
+ {
+ case MI_HOLE_CIRCLE:
+ hole = LL_PCODE_HOLE_CIRCLE;
+ break;
+ case MI_HOLE_SQUARE:
+ hole = LL_PCODE_HOLE_SQUARE;
+ break;
+ case MI_HOLE_TRIANGLE:
+ hole = LL_PCODE_HOLE_TRIANGLE;
+ break;
+ case MI_HOLE_SAME:
+ default:
+ hole = LL_PCODE_HOLE_SAME;
+ break;
+ }
+
+ volume_params.setType(profile | hole, path);
+ mSelectedType = selected_type;
+
+ // Compute cut start/end
+ F32 cut_begin = mSpinCutBegin->get();
+ F32 cut_end = mSpinCutEnd->get();
+
+ // Make sure at least OBJECT_CUT_INC of the object survives
+ if (cut_begin > cut_end - OBJECT_MIN_CUT_INC)
+ {
+ cut_begin = cut_end - OBJECT_MIN_CUT_INC;
+ mSpinCutBegin->set(cut_begin);
+ }
+
+ F32 adv_cut_begin = mCtrlPathBegin->get();
+ F32 adv_cut_end = mCtrlPathEnd->get();
+
+ // Make sure at least OBJECT_CUT_INC of the object survives
+ if (adv_cut_begin > adv_cut_end - OBJECT_MIN_CUT_INC)
+ {
+ adv_cut_begin = adv_cut_end - OBJECT_MIN_CUT_INC;
+ mCtrlPathBegin->set(adv_cut_begin);
+ }
+
+ F32 begin_s, end_s;
+ F32 begin_t, end_t;
+
+ if (selected_type == MI_SPHERE || selected_type == MI_TORUS ||
+ selected_type == MI_TUBE || selected_type == MI_RING)
+ {
+ begin_s = adv_cut_begin;
+ end_s = adv_cut_end;
+
+ begin_t = cut_begin;
+ end_t = cut_end;
+ }
+ else
+ {
+ begin_s = cut_begin;
+ end_s = cut_end;
+
+ begin_t = adv_cut_begin;
+ end_t = adv_cut_end;
+ }
+
+ volume_params.setBeginAndEndS(begin_s, end_s);
+ volume_params.setBeginAndEndT(begin_t, end_t);
+
+ // Hollowness
+ F32 hollow = mSpinHollow->get() / 100.f;
+
+ if ( selected_hole == MI_HOLE_SQUARE &&
+ ( selected_type == MI_CYLINDER || selected_type == MI_TORUS ||
+ selected_type == MI_PRISM || selected_type == MI_RING ||
+ selected_type == MI_SPHERE ) )
+ {
+ if (hollow > 0.7f) hollow = 0.7f;
+ }
+
+ volume_params.setHollow( hollow );
+
+ // Twist Begin,End
+ F32 twist_begin = mSpinTwistBegin->get();
+ F32 twist = mSpinTwist->get();
+ // Check the path type for twist conversion.
+ if (path == LL_PCODE_PATH_LINE || path == LL_PCODE_PATH_FLEXIBLE)
+ {
+ twist_begin /= OBJECT_TWIST_LINEAR_MAX;
+ twist /= OBJECT_TWIST_LINEAR_MAX;
+ }
+ else
+ {
+ twist_begin /= OBJECT_TWIST_MAX;
+ twist /= OBJECT_TWIST_MAX;
+ }
+
+ volume_params.setTwistBegin(twist_begin);
+ volume_params.setTwist(twist);
+
+ // Scale X,Y
+ F32 scale_x = mSpinScaleX->get();
+ F32 scale_y = mSpinScaleY->get();
+ if ( was_selected_type == MI_BOX || was_selected_type == MI_CYLINDER || was_selected_type == MI_PRISM)
+ {
+ scale_x = 1.f - scale_x;
+ scale_y = 1.f - scale_y;
+ }
+
+ // Skew
+ F32 skew = mSpinSkew->get();
+
+ // Taper X,Y
+ F32 taper_x = mSpinTaperX->get();
+ F32 taper_y = mSpinTaperY->get();
+
+ // Radius offset
+ F32 radius_offset = mSpinRadiusOffset->get();
+
+ // Revolutions
+ F32 revolutions = mSpinRevolutions->get();
+
+ if ( selected_type == MI_SPHERE )
+ {
+ // Snap values to valid sphere parameters.
+ scale_x = 1.0f;
+ scale_y = 1.0f;
+ skew = 0.0f;
+ taper_x = 0.0f;
+ taper_y = 0.0f;
+ radius_offset = 0.0f;
+ revolutions = 1.0f;
+ }
+ else if ( selected_type == MI_TORUS || selected_type == MI_TUBE ||
+ selected_type == MI_RING )
+ {
+ scale_x = llclamp(
+ scale_x,
+ OBJECT_MIN_HOLE_SIZE,
+ OBJECT_MAX_HOLE_SIZE_X);
+ scale_y = llclamp(
+ scale_y,
+ OBJECT_MIN_HOLE_SIZE,
+ OBJECT_MAX_HOLE_SIZE_Y);
+
+ // Limit radius offset, based on taper and hole size y.
+ F32 radius_mag = fabs(radius_offset);
+ F32 hole_y_mag = fabs(scale_y);
+ F32 taper_y_mag = fabs(taper_y);
+ // Check to see if the taper effects us.
+ if ( (radius_offset > 0.f && taper_y < 0.f) ||
+ (radius_offset < 0.f && taper_y > 0.f) )
+ {
+ // The taper does not help increase the radius offset range.
+ taper_y_mag = 0.f;
+ }
+ F32 max_radius_mag = 1.f - hole_y_mag * (1.f - taper_y_mag) / (1.f - hole_y_mag);
+ // Enforce the maximum magnitude.
+ if (radius_mag > max_radius_mag)
+ {
+ // Check radius offset sign.
+ if (radius_offset < 0.f)
+ {
+ radius_offset = -max_radius_mag;
+ }
+ else
+ {
+ radius_offset = max_radius_mag;
+ }
+ }
+
+ // Check the skew value against the revolutions.
+ F32 skew_mag= fabs(skew);
+ F32 min_skew_mag = 1.0f - 1.0f / (revolutions * scale_x + 1.0f);
+ // Discontinuity; A revolution of 1 allows skews below 0.5.
+ if ( fabs(revolutions - 1.0f) < 0.001)
+ min_skew_mag = 0.0f;
+
+ // Clip skew.
+ if (skew_mag < min_skew_mag)
+ {
+ // Check skew sign.
+ if (skew < 0.0f)
+ {
+ skew = -min_skew_mag;
+ }
+ else
+ {
+ skew = min_skew_mag;
+ }
+ }
+ }
+
+ volume_params.setRatio( scale_x, scale_y );
+ volume_params.setSkew(skew);
+ volume_params.setTaper( taper_x, taper_y );
+ volume_params.setRadiusOffset(radius_offset);
+ volume_params.setRevolutions(revolutions);
+
+ // Shear X,Y
+ F32 shear_x = mSpinShearX->get();
+ F32 shear_y = mSpinShearY->get();
+ volume_params.setShear( shear_x, shear_y );
+}
+
+// BUG: Make work with multiple objects
+void LLPanelObject::sendRotation()
+{
+ 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);
+
+ // Note: must compare before conversion to radians
+ LLVector3 delta = new_rot - mCurEulerDegrees;
+
+ if (delta.magVec() >= 0.0005f)
+ {
+ mCurEulerDegrees = new_rot;
+ new_rot *= DEG_TO_RAD;
+
+ LLQuaternion rotation;
+ rotation.setQuat(new_rot.mV[VX], new_rot.mV[VY], new_rot.mV[VZ]);
+
+ if (mRootObject != mObject)
+ {
+ rotation = rotation * ~mRootObject->getRotationRegion();
+ }
+
+ mObject->setRotation(rotation, TRUE );
+
+ gSelectMgr->sendMultipleUpdate(UPD_ROTATION);
+ }
+}
+
+
+// BUG: Make work with multiple objects
+void LLPanelObject::sendScale()
+{
+ if (mObject.isNull()) return;
+
+ LLVector3 newscale(mCtrlScaleX->get(), mCtrlScaleY->get(), mCtrlScaleZ->get());
+
+ LLVector3 delta = newscale - mObject->getScale();
+ if (delta.magVec() >= 0.0005f)
+ {
+ // scale changed by more than 1/2 millimeter
+
+ // check to see if we aren't scaling the textures
+ // (in which case the tex coord's need to be recomputed)
+ BOOL dont_stretch_textures = !LLManipScale::getStretchTextures();
+ if (dont_stretch_textures)
+ {
+ gSelectMgr->saveSelectedObjectTransform(SELECT_ACTION_TYPE_SCALE);
+ }
+
+ mObject->setScale(newscale, TRUE);
+ gSelectMgr->sendMultipleUpdate(UPD_SCALE | UPD_POSITION);
+
+ gSelectMgr->adjustTexturesByScale(TRUE, !dont_stretch_textures);
+// llinfos << "scale sent" << llendl;
+ }
+ else
+ {
+// llinfos << "scale not changed" << llendl;
+ }
+}
+
+
+void LLPanelObject::sendPosition()
+{
+ if (mObject.isNull()) return;
+
+ LLVector3 newpos(mCtrlPosX->get(), mCtrlPosY->get(), mCtrlPosZ->get());
+ LLViewerRegion* regionp = mObject->getRegion();
+
+ // Clamp the Z height
+ const F32 height = newpos.mV[VZ];
+ const F32 min_height = gWorldp->getMinAllowedZ(mObject);
+ const F32 max_height = gWorldPointer->getRegionMaxHeight();
+
+ if (!mObject->isAttachment())
+ {
+ if ( height < min_height)
+ {
+ newpos.mV[VZ] = min_height;
+ mCtrlPosZ->set( min_height );
+ }
+ else if ( height > max_height )
+ {
+ newpos.mV[VZ] = max_height;
+ mCtrlPosZ->set( max_height );
+ }
+
+ // Grass is always drawn on the ground, so clamp its position to the ground
+ if (mObject->getPCode() == LL_PCODE_LEGACY_GRASS)
+ {
+ mCtrlPosZ->set(gWorldp->resolveLandHeightAgent(newpos) + 1.f);
+ }
+ }
+
+ // Make sure new position is in a valid region, so the object
+ // won't get dumped by the simulator.
+ LLVector3d new_pos_global = regionp->getPosGlobalFromRegion(newpos);
+
+ if ( gWorldPointer->positionRegionValidGlobal(new_pos_global) )
+ {
+ // send only if the position is changed, that is, the delta vector is not zero
+ LLVector3d old_pos_global = mObject->getPositionGlobal();
+ LLVector3d delta = new_pos_global - old_pos_global;
+ // moved more than 1/2 millimeter
+ if (delta.magVec() >= 0.0005f)
+ {
+ if (mRootObject != mObject)
+ {
+ newpos = newpos - mRootObject->getPositionRegion();
+ newpos = newpos * ~mRootObject->getRotationRegion();
+ mObject->setPositionParent(newpos);
+ }
+ else
+ {
+ mObject->setPositionEdit(newpos);
+ }
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ //mRootObject->sendPositionUpdate();
+
+ gSelectMgr->updateSelectionCenter();
+
+// llinfos << "position sent" << llendl;
+ }
+ else
+ {
+// llinfos << "position not changed" << llendl;
+ }
+ }
+ else
+ {
+ // move failed, so we update the UI with the correct values
+ LLVector3 vec = mRootObject->getPositionRegion();
+ mCtrlPosX->set(vec.mV[VX]);
+ mCtrlPosY->set(vec.mV[VY]);
+ mCtrlPosZ->set(vec.mV[VZ]);
+ }
+}
+
+
+void LLPanelObject::refresh()
+{
+ getState();
+ if (mObject.notNull() && mObject->isDead())
+ {
+ mObject = NULL;
+ }
+
+ if (mRootObject.notNull() && mRootObject->isDead())
+ {
+ mRootObject = NULL;
+ }
+}
+
+
+void LLPanelObject::draw()
+{
+ const LLColor4 white( 1.0f, 1.0f, 1.0f, 1);
+ const LLColor4 red( 1.0f, 0.25f, 0.f, 1);
+ const LLColor4 green( 0.f, 1.0f, 0.f, 1);
+ const LLColor4 blue( 0.f, 0.5f, 1.0f, 1);
+
+ // Tune the colors of the labels
+ LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ if (tool == gToolTranslate)
+ {
+ mCtrlPosX ->setLabelColor(red);
+ mCtrlPosY ->setLabelColor(green);
+ mCtrlPosZ ->setLabelColor(blue);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+ else if ( tool == gToolStretch )
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(red);
+ mCtrlScaleY ->setLabelColor(green);
+ mCtrlScaleZ ->setLabelColor(blue);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+ else if ( tool == gToolRotate )
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(red);
+ mCtrlRotY ->setLabelColor(green);
+ mCtrlRotZ ->setLabelColor(blue);
+ }
+ else
+ {
+ mCtrlPosX ->setLabelColor(white);
+ mCtrlPosY ->setLabelColor(white);
+ mCtrlPosZ ->setLabelColor(white);
+
+ mCtrlScaleX ->setLabelColor(white);
+ mCtrlScaleY ->setLabelColor(white);
+ mCtrlScaleZ ->setLabelColor(white);
+
+ mCtrlRotX ->setLabelColor(white);
+ mCtrlRotY ->setLabelColor(white);
+ mCtrlRotZ ->setLabelColor(white);
+ }
+
+ LLPanel::draw();
+}
+
+// virtual
+void LLPanelObject::clearCtrls()
+{
+ LLPanel::clearCtrls();
+
+ mCheckLock ->set(FALSE);
+ mCheckLock ->setEnabled( FALSE );
+ mCheckPhysics ->set(FALSE);
+ mCheckPhysics ->setEnabled( FALSE );
+ mCheckTemporary ->set(FALSE);
+ mCheckTemporary ->setEnabled( FALSE );
+ mCheckPhantom ->set(FALSE);
+ mCheckPhantom ->setEnabled( FALSE );
+#if 0 // 1.9.2
+ mCheckCastShadows->set(FALSE);
+ mCheckCastShadows->setEnabled( FALSE );
+#endif
+ mComboMaterial ->setEnabled( FALSE );
+ mLabelMaterial ->setEnabled( FALSE );
+ // Disable text labels
+ mLabelPosition ->setEnabled( FALSE );
+ mLabelSize ->setEnabled( FALSE );
+ mLabelRotation ->setEnabled( FALSE );
+ mLabelBaseType ->setEnabled( FALSE );
+ mLabelCut ->setEnabled( FALSE );
+ mLabelHollow ->setEnabled( FALSE );
+ mLabelHoleType ->setEnabled( FALSE );
+ mLabelTwist ->setEnabled( FALSE );
+ mLabelSkew ->setEnabled( FALSE );
+ mLabelShear ->setEnabled( FALSE );
+ mLabelTaper ->setEnabled( FALSE );
+ mLabelRadiusOffset->setEnabled( FALSE );
+ mLabelRevolutions->setEnabled( FALSE );
+
+ childSetVisible("select_single", TRUE);
+ childSetVisible("edit_object", TRUE);
+ childSetEnabled("edit_object", FALSE);
+
+ childSetEnabled("scale_hole", FALSE);
+ childSetEnabled("scale_taper", FALSE);
+ childSetEnabled( "advanced_cut", FALSE );
+ childSetEnabled( "advanced_dimple", FALSE );
+}
+
+//
+// Static functions
+//
+
+// static
+void LLPanelObject::onCommitLock(LLUICtrl *ctrl, void *data)
+{
+ // Checkbox will have toggled itself
+ LLPanelObject *self = (LLPanelObject *)data;
+
+ if(self->mRootObject.isNull()) return;
+
+ BOOL new_state = self->mCheckLock->get();
+
+ gSelectMgr->setObjectPermissions(PERM_OWNER, !new_state, PERM_MOVE | PERM_MODIFY);
+}
+
+// static
+void LLPanelObject::onCommitPosition( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendPosition();
+}
+
+// static
+void LLPanelObject::onCommitScale( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendScale();
+}
+
+// static
+void LLPanelObject::onCommitRotation( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendRotation();
+}
+
+// static
+void LLPanelObject::onCommitPhysics( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsPhysical();
+}
+
+// static
+void LLPanelObject::onCommitTemporary( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsTemporary();
+}
+
+// static
+void LLPanelObject::onCommitPhantom( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendIsPhantom();
+}
+
+// static
+void LLPanelObject::onCommitCastShadows( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelObject* self = (LLPanelObject*) userdata;
+ self->sendCastShadows();
+}
diff --git a/indra/newview/llpanelobject.h b/indra/newview/llpanelobject.h
new file mode 100644
index 0000000000..07b65b6372
--- /dev/null
+++ b/indra/newview/llpanelobject.h
@@ -0,0 +1,147 @@
+/**
+ * @file llpanelobject.h
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELOBJECT_H
+#define LL_LLPANELOBJECT_H
+
+#include "v3math.h"
+#include "llpanel.h"
+#include "llmemory.h"
+#include "llvolume.h"
+
+class LLSpinCtrl;
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLUICtrl;
+class LLButton;
+class LLViewerObject;
+class LLComboBox;
+class LLPanelInventory;
+class LLColorSwatchCtrl;
+
+class LLPanelObject : public LLPanel
+{
+public:
+ LLPanelObject(const std::string& name);
+ virtual ~LLPanelObject();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+ virtual void clearCtrls();
+
+ void refresh();
+
+ static BOOL precommitValidate(LLUICtrl* ctrl,void* userdata);
+
+ static void onCommitLock(LLUICtrl *ctrl, void *data);
+ static void onCommitPosition( LLUICtrl* ctrl, void* userdata);
+ static void onCommitScale( LLUICtrl* ctrl, void* userdata);
+ static void onCommitRotation( LLUICtrl* ctrl, void* userdata);
+ static void onCommitPhysics( LLUICtrl* ctrl, void* userdata);
+ static void onCommitTemporary( LLUICtrl* ctrl, void* userdata);
+ static void onCommitPhantom( LLUICtrl* ctrl, void* userdata);
+ static void onCommitCastShadows( LLUICtrl* ctrl, void* userdata);
+
+ static void onCommitParametric(LLUICtrl* ctrl, void* userdata);
+
+ static void onCommitMaterial( LLUICtrl* ctrl, void* userdata);
+
+protected:
+ void getState();
+
+ void sendRotation();
+ void sendScale();
+ void sendPosition();
+ void sendIsPhysical();
+ void sendIsTemporary();
+ void sendIsPhantom();
+ void sendCastShadows();
+
+ void getVolumeParams(LLVolumeParams& volume_params);
+
+protected:
+ S32 mComboMaterialItemCount;
+
+ LLTextBox* mLabelMaterial;
+ LLComboBox* mComboMaterial;
+
+ // Per-object options
+ LLTextBox* mLabelBaseType;
+ LLComboBox* mComboBaseType;
+
+ LLTextBox* mLabelCut;
+ LLSpinCtrl* mSpinCutBegin;
+ LLSpinCtrl* mSpinCutEnd;
+
+ LLTextBox* mLabelHollow;
+ LLSpinCtrl* mSpinHollow;
+
+ LLTextBox* mLabelHoleType;
+ LLComboBox* mComboHoleType;
+
+ LLTextBox* mLabelTwist;
+ LLSpinCtrl* mSpinTwist;
+ LLSpinCtrl* mSpinTwistBegin;
+
+ LLSpinCtrl* mSpinScaleX;
+ LLSpinCtrl* mSpinScaleY;
+
+ LLTextBox* mLabelSkew;
+ LLSpinCtrl* mSpinSkew;
+
+ LLTextBox* mLabelShear;
+ LLSpinCtrl* mSpinShearX;
+ LLSpinCtrl* mSpinShearY;
+
+ // Advanced Path
+ LLSpinCtrl* mCtrlPathBegin;
+ LLSpinCtrl* mCtrlPathEnd;
+
+ LLTextBox* mLabelTaper;
+ LLSpinCtrl* mSpinTaperX;
+ LLSpinCtrl* mSpinTaperY;
+
+ LLTextBox* mLabelRadiusOffset;
+ LLSpinCtrl* mSpinRadiusOffset;
+
+ LLTextBox* mLabelRevolutions;
+ LLSpinCtrl* mSpinRevolutions;
+
+ LLTextBox* mLabelPosition;
+ LLSpinCtrl* mCtrlPosX;
+ LLSpinCtrl* mCtrlPosY;
+ LLSpinCtrl* mCtrlPosZ;
+
+ LLTextBox* mLabelSize;
+ LLSpinCtrl* mCtrlScaleX;
+ LLSpinCtrl* mCtrlScaleY;
+ LLSpinCtrl* mCtrlScaleZ;
+
+ LLTextBox* mLabelRotation;
+ LLSpinCtrl* mCtrlRotX;
+ LLSpinCtrl* mCtrlRotY;
+ LLSpinCtrl* mCtrlRotZ;
+
+ LLCheckBoxCtrl *mCheckLock;
+ LLCheckBoxCtrl *mCheckPhysics;
+ LLCheckBoxCtrl *mCheckTemporary;
+ LLCheckBoxCtrl *mCheckPhantom;
+ LLCheckBoxCtrl *mCheckCastShadows;
+
+ LLVector3 mCurEulerDegrees; // to avoid sending rotation when not changed
+ BOOL mIsPhysical; // to avoid sending "physical" when not changed
+ BOOL mIsTemporary; // to avoid sending "temporary" when not changed
+ BOOL mIsPhantom; // to avoid sending "phantom" when not changed
+ BOOL mCastShadows; // to avoid sending "cast shadows" when not changed
+ S32 mSelectedType; // So we know what selected type we last were
+
+ LLPointer<LLViewerObject> mObject;
+ LLPointer<LLViewerObject> mRootObject;
+};
+
+#endif
diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp
new file mode 100644
index 0000000000..9e23b977d9
--- /dev/null
+++ b/indra/newview/llpanelpermissions.cpp
@@ -0,0 +1,1028 @@
+/**
+ * @file llpanelpermissions.cpp
+ * @brief LLPanelPermissions class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// This class represents the panel in the build view for
+// viewing/editing object names, owners, permissions, etc.
+//
+//*****************************************************************************
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelpermissions.h"
+
+#include "lluuid.h"
+#include "llpermissions.h"
+#include "llcategory.h"
+#include "llclickaction.h"
+#include "llfocusmgr.h"
+#include "llstring.h"
+
+#include "llviewerwindow.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llviewerobject.h"
+#include "llselectmgr.h"
+#include "llagent.h"
+#include "llstatusbar.h" // for getBalance()
+#include "lllineeditor.h"
+#include "llradiogroup.h"
+#include "llcombobox.h"
+#include "llfloateravatarinfo.h"
+#include "lluiconstants.h"
+#include "lldbstrings.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llnamebox.h"
+#include "llviewercontrol.h"
+#include "llvieweruictrlfactory.h"
+#include "roles_constants.h"
+
+///----------------------------------------------------------------------------
+/// Class llpanelpermissions
+///----------------------------------------------------------------------------
+
+// Default constructor
+
+BOOL LLPanelPermissions::postBuild()
+{
+ this->childSetCommitCallback("Object Name",LLPanelPermissions::onCommitName,this);
+ this->childSetPrevalidate("Object Name",LLLineEditor::prevalidatePrintableNotPipe);
+ this->childSetCommitCallback("Object Description",LLPanelPermissions::onCommitDesc,this);
+ this->childSetPrevalidate("Object Description",LLLineEditor::prevalidatePrintableNotPipe);
+
+
+ this->childSetAction("button owner profile",LLPanelPermissions::onClickOwner,this);
+ this->childSetAction("button creator profile",LLPanelPermissions::onClickCreator,this);
+
+ this->childSetAction("button set group",LLPanelPermissions::onClickGroup,this);
+
+ this->childSetCommitCallback("checkbox share with group",LLPanelPermissions::onCommitGroupShare,this);
+
+ this->childSetAction("button deed",LLPanelPermissions::onClickDeedToGroup,this);
+
+ this->childSetCommitCallback("checkbox allow everyone move",LLPanelPermissions::onCommitEveryoneMove,this);
+
+ this->childSetCommitCallback("checkbox allow everyone copy",LLPanelPermissions::onCommitEveryoneCopy,this);
+
+ this->childSetCommitCallback("checkbox for sale",LLPanelPermissions::onCommitSaleInfo,this);
+
+ this->childSetCommitCallback("EdCost",LLPanelPermissions::onCommitSaleInfo,this);
+ this->childSetPrevalidate("EdCost",LLLineEditor::prevalidateNonNegativeS32);
+
+ this->childSetCommitCallback("sale type",LLPanelPermissions::onCommitSaleType,this);
+
+ this->childSetCommitCallback("checkbox next owner can modify",LLPanelPermissions::onCommitNextOwnerModify,this);
+ this->childSetCommitCallback("checkbox next owner can copy",LLPanelPermissions::onCommitNextOwnerCopy,this);
+ this->childSetCommitCallback("checkbox next owner can transfer",LLPanelPermissions::onCommitNextOwnerTransfer,this);
+ this->childSetCommitCallback("clickaction",LLPanelPermissions::onCommitClickAction,this);
+
+ LLTextBox* LabelGroupNameRectProxy = gUICtrlFactory->getTextBoxByName(this,"Group Name Proxy");
+ if(LabelGroupNameRectProxy )
+ {
+ mLabelGroupName = new LLNameBox("Group Name",LabelGroupNameRectProxy->getRect());
+ addChild(mLabelGroupName);
+ }else
+ mLabelGroupName = NULL;
+
+ return TRUE;
+}
+
+LLPanelPermissions::LLPanelPermissions(const std::string& title) :
+ LLPanel(title)
+{
+ setMouseOpaque(FALSE);
+}
+
+// Destroys the object
+LLPanelPermissions::~LLPanelPermissions()
+{
+ // base class will take care of everything
+}
+
+
+void LLPanelPermissions::refresh()
+{
+
+ LLButton* BtnDeedToGroup = gUICtrlFactory->getButtonByName(this,"button deed");
+ if(BtnDeedToGroup)
+ {
+ LLString deedText;
+ if (gSavedSettings.getWarning("DeedObject"))
+ {
+ deedText = this->childGetText("text deed continued");
+ }
+ else
+ {
+ deedText = this->childGetText("text deed");
+ }
+ BtnDeedToGroup->setLabelSelected(deedText);
+ BtnDeedToGroup->setLabelUnselected(deedText);
+ }
+ BOOL root_selected = TRUE;
+ LLSelectNode* nodep = gSelectMgr->getFirstRootNode();
+ S32 object_count = gSelectMgr->getRootObjectCount();
+ if(!nodep || 0 == object_count)
+ {
+ nodep = gSelectMgr->getFirstNode();
+ object_count = gSelectMgr->getObjectCount();
+ root_selected = FALSE;
+ }
+
+ BOOL attachment_selected = gSelectMgr->selectionIsAttachment();
+
+ LLViewerObject* objectp = NULL;
+ if(nodep) objectp = nodep->getObject();
+ if(!nodep || !objectp || attachment_selected)
+ {
+ // ...nothing selected
+ childSetEnabled("perm_modify",false);
+ childSetText("perm_modify","");
+
+ childSetEnabled("Creator:",false);
+ childSetText("Creator Name","");
+ childSetEnabled("Creator Name",false);
+ childSetEnabled("button creator profile",false);
+
+ childSetEnabled("Owner:",false);
+ childSetText("Owner Name","");
+ childSetEnabled("Owner Name",false);
+ childSetEnabled("button owner profile",false);
+
+ childSetEnabled("Group:",false);
+ childSetText("Group Name","");
+ childSetEnabled("Group Name",false);
+ childSetEnabled("button set group",false);
+
+ childSetText("Object Name","");
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Name:",false);
+ childSetText("Group Name","");
+ childSetEnabled("Group Name",false);
+ childSetEnabled("Description:",false);
+ childSetText("Object Description","");
+ childSetEnabled("Object Description",false);
+
+ childSetText("prim info","");
+ childSetEnabled("prim info",false);
+
+ childSetEnabled("Permissions:",false);
+
+ childSetValue("checkbox share with group",FALSE);
+ childSetEnabled("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetEnabled("checkbox allow everyone move",false);
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetEnabled("checkbox allow everyone copy",false);
+
+ //Next owner can:
+ childSetEnabled("Next owner can:",false);
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can modify",false);
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can copy",false);
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetEnabled("checkbox next owner can transfer",false);
+
+ //checkbox for sale
+ childSetValue("checkbox for sale",FALSE);
+ childSetEnabled("checkbox for sale",false);
+
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType)
+ {
+ RadioSaleType->setSelectedIndex(-1);
+ RadioSaleType->setEnabled(FALSE);
+ }
+
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost",false);
+ childSetEnabled("EdCost",false);
+
+ childSetEnabled("label click action",false);
+ LLComboBox* ComboClickAction = gUICtrlFactory->getComboBoxByName(this,"clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setEnabled(FALSE);
+ ComboClickAction->clear();
+ }
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+
+ return;
+ }
+
+ // figure out a few variables
+ BOOL is_one_object = (object_count == 1);
+
+ // BUG: fails if a root and non-root are both single-selected.
+ BOOL is_perm_modify = (gSelectMgr->getFirstRootNode()
+ && gSelectMgr->selectGetRootsModify())
+ || gSelectMgr->selectGetModify();
+ const LLView* keyboard_focus_view = gFocusMgr.getKeyboardFocus();
+ S32 string_index = 0;
+ LLString MODIFY_INFO_STRINGS[] =
+ {
+ childGetText("text modify info 1"),
+ childGetText("text modify info 2"),
+ childGetText("text modify info 3"),
+ childGetText("text modify info 4")
+ };
+ if(!is_perm_modify)
+ {
+ string_index += 2;
+ }
+ if(!is_one_object)
+ {
+ ++string_index;
+ }
+ childSetEnabled("perm_modify",true);
+ childSetText("perm_modify",MODIFY_INFO_STRINGS[string_index]);
+
+ childSetEnabled("Permissions:",true);
+
+ // Update creator text field
+ childSetEnabled("Creator:",true);
+ BOOL creators_identical;
+ LLString creator_name;
+ creators_identical = gSelectMgr->selectGetCreator(mCreatorID,
+ creator_name);
+
+ childSetText("Creator Name",creator_name);
+ childSetEnabled("Creator Name",TRUE);
+ childSetEnabled("button creator profile", creators_identical && mCreatorID.notNull() );
+
+ // Update owner text field
+ childSetEnabled("Owner:",true);
+
+ BOOL owners_identical;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(mOwnerID, owner_name);
+
+// llinfos << "owners_identical " << (owners_identical ? "TRUE": "FALSE") << llendl;
+
+ if (mOwnerID.isNull())
+ {
+ if(gSelectMgr->selectIsGroupOwned())
+ {
+ // Group owned already displayed by selectGetOwner
+ }
+ else
+ {
+ // Display last owner if public
+ LLString last_owner_name;
+ gSelectMgr->selectGetLastOwner(mLastOwnerID, last_owner_name);
+
+ // It should never happen that the last owner is null and the owner
+ // is null, but it seems to be a bug in the simulator right now. JC
+ if (!mLastOwnerID.isNull() && !last_owner_name.empty())
+ {
+ owner_name.append(", last ");
+ owner_name.append( last_owner_name );
+ }
+ }
+ }
+
+ childSetText("Owner Name",owner_name);
+ childSetEnabled("Owner Name",TRUE);
+ childSetEnabled("button owner profile",owners_identical && (mOwnerID.notNull() || gSelectMgr->selectIsGroupOwned()));
+
+ // update group text field
+ childSetEnabled("Group:",true);
+ LLUUID group_id;
+ BOOL groups_identical = gSelectMgr->selectGetGroup(group_id);
+ if (groups_identical)
+ {
+ if(mLabelGroupName)
+ {
+ mLabelGroupName->setNameID(group_id, TRUE);
+ mLabelGroupName->setEnabled(TRUE);
+ }
+ }
+ childSetEnabled("button set group",owners_identical && (mOwnerID == gAgent.getID()));
+
+ // figure out the contents of the name, description, & category
+ BOOL edit_name_desc = FALSE;
+ if(is_one_object && objectp->permModify())
+ {
+ edit_name_desc = TRUE;
+ }
+ if(is_one_object)
+ {
+ childSetEnabled("Name:",true);
+ LLLineEditor* LineEditorObjectName = gUICtrlFactory->getLineEditorByName(this,"Object Name");
+ if(keyboard_focus_view != LineEditorObjectName)
+ {
+ childSetText("Object Name",nodep->mName);
+ }
+
+ childSetEnabled("Description:",true);
+ LLLineEditor* LineEditorObjectDesc = gUICtrlFactory->getLineEditorByName(this,"Object Description");
+ if(LineEditorObjectDesc)
+ {
+ if(keyboard_focus_view != LineEditorObjectDesc)
+ {
+ LineEditorObjectDesc->setText(nodep->mDescription);
+ }
+ }
+ }
+
+ if(edit_name_desc)
+ {
+ childSetEnabled("Object Name",true);
+ childSetEnabled("Object Description",true);
+ }
+ else
+ {
+ childSetEnabled("Object Name",false);
+ childSetEnabled("Object Description",false);
+ }
+
+
+ // Pre-compute object info string
+ S32 prim_count = gSelectMgr->getObjectCount();
+ S32 obj_count = gSelectMgr->getRootObjectCount();
+
+ LLString object_info_string;
+ if (1 == obj_count)
+ {
+ object_info_string.assign("1 Object, ");
+ }
+ else
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d Objects, ", obj_count);
+ object_info_string.assign(buffer);
+ }
+ if (1 == prim_count)
+ {
+ object_info_string.append("1 Primitive");
+ }
+ else
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d Primitives", prim_count);
+ object_info_string.append(buffer);
+ }
+ childSetText("prim info",object_info_string);
+ childSetEnabled("prim info",true);
+
+ S32 price;
+ BOOL is_for_sale = gSelectMgr->selectIsForSale(price);
+ if (!is_for_sale)
+ {
+ price = DEFAULT_PRICE;
+ }
+
+ BOOL self_owned = (gAgent.getID() == mOwnerID);
+ BOOL group_owned = gSelectMgr->selectIsGroupOwned() ;
+ BOOL public_owned = (mOwnerID.isNull() && !gSelectMgr->selectIsGroupOwned());
+ BOOL can_transfer = gSelectMgr->selectGetRootsTransfer();
+ BOOL can_copy = gSelectMgr->selectGetRootsCopy();
+
+ if(!owners_identical)
+ {
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost","");
+ childSetEnabled("EdCost",false);
+ }
+ else if(self_owned || (group_owned && gAgent.hasPowerInGroup(group_id,GP_OBJECT_SET_SALE)))
+ {
+ LLLineEditor* EditPrice = gUICtrlFactory->getLineEditorByName(this,"EdCost");
+ if(keyboard_focus_view != EditPrice)
+ {
+ childSetText("EdCost",llformat("%d",price));
+ }
+ if(is_for_sale && is_one_object && can_transfer)
+ {
+ childSetEnabled("Price: L$",true);
+ childSetEnabled("EdCost",true);
+ }
+ else
+ {
+ childSetEnabled("Price: L$",false);
+ childSetEnabled("EdCost",false);
+ }
+ }
+ else if(!public_owned)
+ {
+ // ...someone, not you, owns it
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost",llformat("%d",price));
+ childSetEnabled("EdCost",false);
+ }
+ else
+ {
+ // ...public object
+ childSetEnabled("Price: L$",false);
+ childSetText("EdCost","");
+ childSetEnabled("EdCost",false);
+ }
+
+ // Enable and disable the permissions checkboxes
+ // based on who owns the object.
+ // TODO: Creator permissions
+
+ BOOL valid_base_perms = FALSE;
+ BOOL valid_owner_perms = FALSE;
+ BOOL valid_group_perms = FALSE;
+ BOOL valid_everyone_perms = FALSE;
+ BOOL valid_next_perms = FALSE;
+
+ U32 base_mask_on;
+ U32 base_mask_off;
+ U32 owner_mask_on;
+ U32 owner_mask_off;
+ U32 group_mask_on;
+ U32 group_mask_off;
+ U32 everyone_mask_on;
+ U32 everyone_mask_off;
+ U32 next_owner_mask_on = 0;
+ U32 next_owner_mask_off = 0;
+
+ valid_base_perms = gSelectMgr->selectGetPerm(PERM_BASE,
+ &base_mask_on,
+ &base_mask_off);
+
+ valid_owner_perms = gSelectMgr->selectGetPerm(PERM_OWNER,
+ &owner_mask_on,
+ &owner_mask_off);
+
+ valid_group_perms = gSelectMgr->selectGetPerm(PERM_GROUP,
+ &group_mask_on,
+ &group_mask_off);
+
+ valid_everyone_perms = gSelectMgr->selectGetPerm(PERM_EVERYONE,
+ &everyone_mask_on,
+ &everyone_mask_off);
+
+ valid_next_perms = gSelectMgr->selectGetPerm(PERM_NEXT_OWNER,
+ &next_owner_mask_on,
+ &next_owner_mask_off);
+
+
+ if( gSavedSettings.getBOOL("DebugPermissions") )
+ {
+ char perm_string[10];
+ if (valid_base_perms)
+ {
+
+ strcpy(perm_string, "B: ");
+ mask_to_string(base_mask_on, perm_string+3);
+ childSetText("B:",perm_string);
+ childSetVisible("B:",true);
+
+ strcpy(perm_string, "O: ");
+ mask_to_string(owner_mask_on, perm_string+3);
+ childSetText("O:",perm_string);
+ childSetVisible("O:",true);
+
+ strcpy(perm_string, "G: ");
+ mask_to_string(group_mask_on, perm_string+3);
+ childSetText("G:",perm_string);
+ childSetVisible("G:",true);
+
+ strcpy(perm_string, "E: ");
+ mask_to_string(everyone_mask_on, perm_string+3);
+ childSetText("E:",perm_string);
+ childSetVisible("E:",true);
+
+ strcpy(perm_string, "N: ");
+ mask_to_string(next_owner_mask_on, perm_string+3);
+ childSetText("N:",perm_string);
+ childSetVisible("N:",true);
+ }
+ strcpy(perm_string, "F: ");
+ U32 flag_mask = 0x0;
+ if (objectp->permMove())
+ flag_mask |= PERM_MOVE;
+ if (objectp->permModify())
+ flag_mask |= PERM_MODIFY;
+ if (objectp->permCopy())
+ flag_mask |= PERM_COPY;
+ if (objectp->permTransfer())
+ flag_mask |= PERM_TRANSFER;
+ mask_to_string(flag_mask, perm_string+3);
+ childSetText("F:",perm_string);
+ childSetVisible("F:",true);
+ }
+ else
+ {
+ childSetVisible("B:",false);
+ childSetVisible("O:",false);
+ childSetVisible("G:",false);
+ childSetVisible("E:",false);
+ childSetVisible("N:",false);
+ childSetVisible("F:",false);
+ }
+
+ bool has_change_perm_ability = false;
+ bool has_change_sale_ability = false;
+
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_MANIPULATE))))
+ {
+ has_change_perm_ability = true;
+ }
+ if(valid_base_perms
+ && (self_owned
+ || (group_owned && gAgent.hasPowerInGroup(group_id, GP_OBJECT_SET_SALE))))
+ {
+ has_change_sale_ability = true;
+ }
+
+ if (!has_change_perm_ability && !has_change_sale_ability && !root_selected)
+ {
+ // XUI:translate
+ // ...must select root to choose permissions
+ childSetValue("perm_modify", "Must select entire object to set permissions.");
+ }
+
+ if (has_change_perm_ability)
+ {
+ childSetEnabled("checkbox share with group",true);
+ childSetEnabled("checkbox allow everyone move",owner_mask_on & PERM_MOVE);
+ childSetEnabled("checkbox allow everyone copy",owner_mask_on & PERM_COPY && owner_mask_on & PERM_TRANSFER);
+ }
+ else
+ {
+ childSetEnabled("checkbox share with group", FALSE);
+ childSetEnabled("checkbox allow everyone move", FALSE);
+ childSetEnabled("checkbox allow everyone copy", FALSE);
+ }
+
+ if (has_change_sale_ability && (owner_mask_on & PERM_TRANSFER))
+ {
+ childSetEnabled("checkbox for sale", can_transfer || (!can_transfer && is_for_sale));
+ childSetEnabled("sale type",is_for_sale && can_transfer);
+
+ childSetEnabled("Next owner can:", TRUE);
+ childSetEnabled("checkbox next owner can modify",base_mask_on & PERM_MODIFY);
+ childSetEnabled("checkbox next owner can copy",base_mask_on & PERM_COPY);
+ childSetEnabled("checkbox next owner can transfer",next_owner_mask_on & PERM_COPY);
+ }
+ else
+ {
+ childSetEnabled("checkbox for sale",FALSE);
+ childSetEnabled("sale type",FALSE);
+
+ childSetEnabled("Next owner can:",FALSE);
+ childSetEnabled("checkbox next owner can modify",FALSE);
+ childSetEnabled("checkbox next owner can copy",FALSE);
+ childSetEnabled("checkbox next owner can transfer",FALSE);
+ }
+
+ if(valid_group_perms)
+ {
+ if((group_mask_on & PERM_COPY) && (group_mask_on & PERM_MODIFY) && (group_mask_on & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",FALSE);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (owner_mask_on & PERM_TRANSFER) && !group_owned);
+ }
+ else if((group_mask_off & PERM_COPY) && (group_mask_off & PERM_MODIFY) && (group_mask_off & PERM_MOVE))
+ {
+ childSetValue("checkbox share with group",FALSE);
+ childSetTentative("checkbox share with group",false);
+ childSetEnabled("button deed",false);
+ }
+ else
+ {
+ childSetValue("checkbox share with group",TRUE);
+ childSetTentative("checkbox share with group",true);
+ childSetEnabled("button deed",gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) && (group_mask_on & PERM_MOVE) && (owner_mask_on & PERM_TRANSFER) && !group_owned);
+ }
+ }
+
+ if(valid_everyone_perms)
+ {
+ // Move
+ if(everyone_mask_on & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else if(everyone_mask_off & PERM_MOVE)
+ {
+ childSetValue("checkbox allow everyone move",FALSE);
+ childSetTentative("checkbox allow everyone move",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone move",TRUE);
+ childSetTentative("checkbox allow everyone move",true);
+ }
+
+ // Copy == everyone can't copy
+ if(everyone_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",!can_copy || !can_transfer);
+ }
+ else if(everyone_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox allow everyone copy",FALSE);
+ childSetTentative("checkbox allow everyone copy",false);
+ }
+ else
+ {
+ childSetValue("checkbox allow everyone copy",TRUE);
+ childSetTentative("checkbox allow everyone copy",true);
+ }
+ }
+
+ if(valid_next_perms)
+ {
+ // Modify == next owner canot modify
+ if(next_owner_mask_on & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else if(next_owner_mask_off & PERM_MODIFY)
+ {
+ childSetValue("checkbox next owner can modify",FALSE);
+ childSetTentative("checkbox next owner can modify",false);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can modify",TRUE);
+ childSetTentative("checkbox next owner can modify",true);
+ }
+
+ // Copy == next owner cannot copy
+ if(next_owner_mask_on & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",!can_copy);
+ }
+ else if(next_owner_mask_off & PERM_COPY)
+ {
+ childSetValue("checkbox next owner can copy",FALSE);
+ childSetTentative("checkbox next owner can copy",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can copy",TRUE);
+ childSetTentative("checkbox next owner can copy",TRUE);
+ }
+
+ // Transfer == next owner cannot transfer
+ if(next_owner_mask_on & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",!can_transfer);
+ }
+ else if(next_owner_mask_off & PERM_TRANSFER)
+ {
+ childSetValue("checkbox next owner can transfer",FALSE);
+ childSetTentative("checkbox next owner can transfer",FALSE);
+ }
+ else
+ {
+ childSetValue("checkbox next owner can transfer",TRUE);
+ childSetTentative("checkbox next owner can transfer",TRUE);
+ }
+ }
+
+ // reflect sale information
+ LLSaleInfo sale_info;
+ BOOL valid_sale_info = gSelectMgr->selectGetSaleInfo(sale_info);
+ LLSaleInfo::EForSale sale_type = sale_info.getSaleType();
+
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType && valid_sale_info)
+ {
+ RadioSaleType->setSelectedIndex((S32)sale_type - 1);
+ }
+
+ if (is_for_sale)
+ {
+ childSetValue("checkbox for sale",TRUE);
+ childSetTentative("checkbox for sale",!can_transfer || (!can_copy && sale_type == LLSaleInfo::FS_COPY));
+ }
+ else
+ {
+ childSetValue("checkbox for sale",FALSE);
+ childSetTentative("checkbox for sale",false);
+ }
+
+ // Click action (touch, sit, buy)
+ BOOL all_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME );
+ U8 click_action = 0;
+ if (gSelectMgr->selectionGetClickAction(&click_action))
+ {
+ LLComboBox* ComboClickAction = gUICtrlFactory->getComboBoxByName(this,"clickaction");
+ if(ComboClickAction)
+ {
+ ComboClickAction->setCurrentByIndex((S32)click_action);
+ }
+ }
+ childSetEnabled("label click action",is_perm_modify && all_volume);
+ childSetEnabled("clickaction",is_perm_modify && all_volume);
+}
+
+
+// static
+void LLPanelPermissions::onClickClaim(void*)
+{
+ // try to claim ownership
+ gSelectMgr->sendOwner(gAgent.getID(), gAgent.getGroupID());
+}
+
+// static
+void LLPanelPermissions::onClickRelease(void*)
+{
+ // try to release ownership
+ gSelectMgr->sendOwner(LLUUID::null, LLUUID::null);
+}
+
+// static
+void LLPanelPermissions::onClickCreator(void *data)
+{
+ LLPanelPermissions *self = (LLPanelPermissions *)data;
+
+ LLFloaterAvatarInfo::showFromObject(self->mCreatorID);
+}
+
+// static
+void LLPanelPermissions::onClickOwner(void *data)
+{
+ LLPanelPermissions *self = (LLPanelPermissions *)data;
+
+ if (gSelectMgr->selectIsGroupOwned())
+ {
+ LLUUID group_id;
+ gSelectMgr->selectGetGroup(group_id);
+ LLFloaterGroupInfo::showFromUUID(group_id);
+ }
+ else
+ {
+ LLFloaterAvatarInfo::showFromObject(self->mOwnerID);
+ }
+}
+
+void LLPanelPermissions::onClickGroup(void* data)
+{
+ LLUUID owner_id;
+ LLString name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, name);
+ if(owners_identical && (owner_id == gAgent.getID()))
+ {
+ LLFloaterGroups* fg;
+ fg = LLFloaterGroups::show(gAgent.getID(), LLFloaterGroups::CHOOSE_ONE);
+ fg->setOkCallback( cbGroupID, data );
+ }
+}
+
+// static
+void LLPanelPermissions::cbGroupID(LLUUID group_id, void* userdata)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)userdata;
+ if(self->mLabelGroupName)
+ {
+ self->mLabelGroupName->setNameID(group_id, TRUE);
+ }
+ gSelectMgr->sendGroup(group_id);
+}
+
+void callback_deed_to_group(S32 option, void*)
+{
+ if (0 == option)
+ {
+ LLUUID group_id;
+ BOOL groups_identical = gSelectMgr->selectGetGroup(group_id);
+ if(groups_identical && (gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED)))
+ {
+ gSelectMgr->sendOwner(LLUUID::null, group_id, FALSE);
+// gViewerStats->incStat(LLViewerStats::ST_RELEASE_COUNT);
+ }
+ }
+}
+
+void LLPanelPermissions::onClickDeedToGroup(void* data)
+{
+ gViewerWindow->alertXml( "DeedObjectToGroup",
+ callback_deed_to_group, NULL);
+}
+
+///----------------------------------------------------------------------------
+/// Permissions checkboxes
+///----------------------------------------------------------------------------
+
+// static
+void LLPanelPermissions::onCommitPerm(LLUICtrl *ctrl, void *data, U8 field, U32 perm)
+{
+ LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ if(!object) return;
+
+ // Checkbox will have toggled itself
+ // LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLCheckBoxCtrl *check = (LLCheckBoxCtrl *)ctrl;
+ BOOL new_state = check->get();
+
+ gSelectMgr->setObjectPermissions(field, new_state, perm);
+}
+
+// static
+void LLPanelPermissions::onCommitGroupShare(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_GROUP, PERM_MODIFY | PERM_MOVE | PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitEveryoneMove(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_EVERYONE, PERM_MOVE);
+}
+
+
+// static
+void LLPanelPermissions::onCommitEveryoneCopy(LLUICtrl *ctrl, void *data)
+{
+ onCommitPerm(ctrl, data, PERM_EVERYONE, PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerModify(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerModify" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_MODIFY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerCopy(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerCopy" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_COPY);
+}
+
+// static
+void LLPanelPermissions::onCommitNextOwnerTransfer(LLUICtrl* ctrl, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitNextOwnerTransfer" << llendl;
+ onCommitPerm(ctrl, data, PERM_NEXT_OWNER, PERM_TRANSFER);
+}
+
+// static
+void LLPanelPermissions::onCommitName(LLUICtrl*, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitName()" << llendl;
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLLineEditor* tb = gUICtrlFactory->getLineEditorByName(self,"Object Name");
+ if(tb)
+ {
+ gSelectMgr->setObjectName(tb->getText());
+// gSelectMgr->setObjectName(self->mLabelObjectName->getText());
+ }
+}
+
+
+// static
+void LLPanelPermissions::onCommitDesc(LLUICtrl*, void* data)
+{
+ //llinfos << "LLPanelPermissions::onCommitDesc()" << llendl;
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ LLLineEditor* le = gUICtrlFactory->getLineEditorByName(self,"Object Description");
+ if(le)
+ {
+ gSelectMgr->setObjectDescription(le->getText());
+ }
+}
+
+// static
+void LLPanelPermissions::onCommitSaleInfo(LLUICtrl*, void* data)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ self->setAllSaleInfo();
+}
+
+// static
+void LLPanelPermissions::onCommitSaleType(LLUICtrl*, void* data)
+{
+ LLPanelPermissions* self = (LLPanelPermissions*)data;
+ self->setAllSaleInfo();
+}
+
+void LLPanelPermissions::setAllSaleInfo()
+{
+ llinfos << "LLPanelPermissions::setAllSaleInfo()" << llendl;
+ LLSaleInfo::EForSale sale_type = LLSaleInfo::FS_NOT;
+
+ LLCheckBoxCtrl* mCheckPurchase = gUICtrlFactory->getCheckBoxByName(this,"checkbox for sale");
+
+ if(mCheckPurchase && mCheckPurchase->get())
+ {
+ LLRadioGroup* RadioSaleType = gUICtrlFactory->getRadioGroupByName(this,"sale type");
+ if(RadioSaleType)
+ {
+ switch(RadioSaleType->getSelectedIndex())
+ {
+ case 0:
+ sale_type = LLSaleInfo::FS_ORIGINAL;
+ break;
+ case 1:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ case 2:
+ sale_type = LLSaleInfo::FS_CONTENTS;
+ break;
+ default:
+ sale_type = LLSaleInfo::FS_COPY;
+ break;
+ }
+ }
+ }
+ LLLineEditor* mEditPrice = gUICtrlFactory->getLineEditorByName(this,"EdCost");
+
+ S32 price = -1;
+ if(mEditPrice)
+ {
+ price = atoi(mEditPrice->getText().c_str());
+ }
+ // Invalid data - turn off the sale
+ if (price < 0)
+ {
+ sale_type = LLSaleInfo::FS_NOT;
+ price = 0;
+ }
+
+ LLSaleInfo sale_info(sale_type, price);
+ gSelectMgr->setObjectSaleInfo(sale_info);
+
+ // If turned off for-sale, make sure click-action buy is turned
+ // off as well
+ if (sale_type == LLSaleInfo::FS_NOT)
+ {
+ U8 click_action = 0;
+ gSelectMgr->selectionGetClickAction(&click_action);
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ gSelectMgr->selectionSetClickAction(CLICK_ACTION_TOUCH);
+ }
+ }
+}
+
+class LLSelectionPayable : public LLSelectedObjectFunctor
+{
+public:
+ virtual bool apply(LLViewerObject* obj)
+ {
+ // can pay if you or your parent has money() event in script
+ LLViewerObject* parent = (LLViewerObject*)obj->getParent();
+ return (obj->flagTakesMoney()
+ || (parent && parent->flagTakesMoney()));
+ }
+};
+
+// static
+void LLPanelPermissions::onCommitClickAction(LLUICtrl* ctrl, void*)
+{
+ LLComboBox* box = (LLComboBox*)ctrl;
+ if (!box) return;
+
+ U8 click_action = (U8)box->getCurrentIndex();
+ if (click_action == CLICK_ACTION_BUY)
+ {
+ LLSaleInfo sale_info;
+ gSelectMgr->selectGetSaleInfo(sale_info);
+ if (!sale_info.isForSale())
+ {
+ gViewerWindow->alertXml("CantSetBuyObject");
+
+ // Set click action back to its old value
+ U8 click_action = 0;
+ gSelectMgr->selectionGetClickAction(&click_action);
+ box->setCurrentByIndex((S32)click_action);
+
+ return;
+ }
+ }
+ else if (click_action == CLICK_ACTION_PAY)
+ {
+ // Verify object has script with money() handler
+ LLSelectionPayable payable;
+ bool can_pay = gSelectMgr->applyToObjects(&payable);
+ if (!can_pay)
+ {
+ // Warn, but do it anyway.
+ gViewerWindow->alertXml("ClickActionNotPayable");
+ }
+ }
+ gSelectMgr->selectionSetClickAction(click_action);
+}
diff --git a/indra/newview/llpanelpermissions.h b/indra/newview/llpanelpermissions.h
new file mode 100644
index 0000000000..286a66f397
--- /dev/null
+++ b/indra/newview/llpanelpermissions.h
@@ -0,0 +1,92 @@
+/**
+ * @file llpanelpermissions.h
+ * @brief LLPanelPermissions class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELPERMISSIONS_H
+#define LL_LLPANELPERMISSIONS_H
+
+#ifndef LL_LLPANEL_H
+#include "llpanel.h"
+#endif
+
+#include "lluuid.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class llpanelpermissions
+//
+// Panel for permissions of an object.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLButton;
+class LLLineEditor;
+class LLRadioGroup;
+class LLComboBox;
+class LLNameBox;
+
+class LLPanelPermissions : public LLPanel
+{
+public:
+ LLPanelPermissions(const std::string& title);
+ virtual ~LLPanelPermissions();
+
+ virtual BOOL postBuild();
+
+ // MANIPULATORS
+ void refresh(); // refresh all labels as needed
+// void setPermCheckboxes(U32 mask_on, U32 mask_off,
+// LLCheckBoxCtrl* move, LLCheckboxCtrl* edit,
+// LLCheckBoxCtrl* copy);
+protected:
+ // statics
+ static void onClickClaim(void*);
+ static void onClickRelease(void*);
+ static void onClickCreator(void*);
+ static void onClickOwner(void*);
+ static void onClickGroup(void*);
+ static void cbGroupID(LLUUID group_id, void* userdata);
+ static void onClickDeedToGroup(void*);
+
+ static void onCommitPerm(LLUICtrl *ctrl, void *data, U8 field, U32 perm);
+
+// static void onCommitGroupMove(LLUICtrl *ctrl, void *data);
+// static void onCommitGroupCopy(LLUICtrl *ctrl, void *data);
+// static void onCommitGroupModify(LLUICtrl *ctrl, void *data);
+ static void onCommitGroupShare(LLUICtrl *ctrl, void *data);
+
+ static void onCommitEveryoneMove(LLUICtrl *ctrl, void *data);
+ static void onCommitEveryoneCopy(LLUICtrl *ctrl, void *data);
+ //static void onCommitEveryoneModify(LLUICtrl *ctrl, void *data);
+
+ static void onCommitNextOwnerModify(LLUICtrl* ctrl, void* data);
+ static void onCommitNextOwnerCopy(LLUICtrl* ctrl, void* data);
+ static void onCommitNextOwnerTransfer(LLUICtrl* ctrl, void* data);
+
+ static void onCommitName(LLUICtrl* ctrl, void* data);
+ static void onCommitDesc(LLUICtrl* ctrl, void* data);
+
+ static void onCommitSaleInfo(LLUICtrl* ctrl, void* data);
+ static void onCommitSaleType(LLUICtrl* ctrl, void* data);
+ void setAllSaleInfo();
+
+ static void onCommitClickAction(LLUICtrl* ctrl, void*);
+
+protected:
+ LLNameBox* mLabelGroupName; // group name
+
+ //LLTextBox* mBuyerLabel;
+ //LLCheckBoxCtrl* mCheckBuyerModify;
+ //LLCheckBoxCtrl* mCheckBuyerCopy;
+
+ LLUUID mCreatorID;
+ LLUUID mOwnerID;
+ LLUUID mLastOwnerID;
+};
+
+
+#endif // LL_LLPANELPERMISSIONS_H
diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp
new file mode 100644
index 0000000000..9ad039d01b
--- /dev/null
+++ b/indra/newview/llpanelpick.cpp
@@ -0,0 +1,494 @@
+/**
+ * @file llpanelpick.cpp
+ * @brief LLPanelPick class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a "Top Pick" used both for the global top picks in the
+// Find directory, and also for each individual user's picks in their
+// profile.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelpick.h"
+
+#include "lldir.h"
+#include "llparcel.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llviewercontrol.h"
+#include "lllineeditor.h"
+#include "lltabcontainervertical.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "lluiconstants.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerparcelmgr.h"
+#include "llworldmap.h"
+#include "llfloaterworldmap.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+
+//static
+LLLinkedList<LLPanelPick> LLPanelPick::sAllPanels;
+
+LLPanelPick::LLPanelPick(BOOL top_pick)
+: LLPanel("Top Picks Panel"),
+ mTopPick(top_pick),
+ mPickID(),
+ mCreatorID(),
+ mParcelID(),
+ mDataRequested(FALSE),
+ mDataReceived(FALSE),
+ mPosGlobal(),
+ mSnapshotCtrl(NULL),
+ mNameEditor(NULL),
+ mDescEditor(NULL),
+ mLocationEditor(NULL),
+ mTeleportBtn(NULL),
+ mMapBtn(NULL),
+ //mLandmarkBtn(NULL),
+ mSortOrderText(NULL),
+ mSortOrderEditor(NULL),
+ mEnabledCheck(NULL),
+ mSetBtn(NULL)
+{
+ sAllPanels.addData(this);
+
+ std::string pick_def_file;
+ if (top_pick)
+ {
+ gUICtrlFactory->buildPanel(this, "panel_top_pick.xml");
+ }
+ else
+ {
+ gUICtrlFactory->buildPanel(this, "panel_avatar_pick.xml");
+ }
+}
+
+
+LLPanelPick::~LLPanelPick()
+{
+ sAllPanels.removeData(this);
+}
+
+
+void LLPanelPick::reset()
+{
+ mPickID.setNull();
+ mCreatorID.setNull();
+ mParcelID.setNull();
+
+ // Don't request data, this isn't valid
+ mDataRequested = TRUE;
+ mDataReceived = FALSE;
+
+ mPosGlobal.clearVec();
+
+ clearCtrls();
+}
+
+
+BOOL LLPanelPick::postBuild()
+{
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setCommitCallback(onCommitAny);
+ mSnapshotCtrl->setCallbackUserData(this);
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "given_name_editor");
+ mNameEditor->setCommitOnFocusLost(TRUE);
+ mNameEditor->setCommitCallback(onCommitAny);
+ mNameEditor->setCallbackUserData(this);
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+ mDescEditor->setCommitOnFocusLost(TRUE);
+ mDescEditor->setCommitCallback(onCommitAny);
+ mDescEditor->setCallbackUserData(this);
+ mDescEditor->setTabToNextField(TRUE);
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mSetBtn = LLViewerUICtrlFactory::getButtonByName(this, "set_location_btn");
+ mSetBtn->setClickedCallback(onClickSet);
+ mSetBtn->setCallbackUserData(this);
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "pick_teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "pick_map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ mSortOrderText = LLViewerUICtrlFactory::getTextBoxByName(this, "sort_order_text");
+
+ mSortOrderEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "sort_order_editor");
+ mSortOrderEditor->setPrevalidate(LLLineEditor::prevalidateInt);
+ mSortOrderEditor->setCommitOnFocusLost(TRUE);
+ mSortOrderEditor->setCommitCallback(onCommitAny);
+ mSortOrderEditor->setCallbackUserData(this);
+
+ mEnabledCheck = LLViewerUICtrlFactory::getCheckBoxByName(this, "enabled_check");
+ mEnabledCheck->setCommitCallback(onCommitAny);
+ mEnabledCheck->setCallbackUserData(this);
+
+ return TRUE;
+}
+
+
+// Fill in some reasonable defaults for a new pick.
+void LLPanelPick::initNewPick()
+{
+ mPickID.generate();
+
+ mCreatorID = gAgent.getID();
+
+ mPosGlobal = gAgent.getPositionGlobal();
+
+ // Try to fill in the current parcel
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ if (parcel)
+ {
+ mNameEditor->setText(parcel->getName());
+ mDescEditor->setText(parcel->getDesc());
+ mSnapshotCtrl->setImageAssetID(parcel->getSnapshotID());
+ }
+
+ // Commit to the database, since we've got "new" values.
+ sendPickInfoUpdate();
+}
+
+
+void LLPanelPick::setPickID(const LLUUID& id)
+{
+ mPickID = id;
+}
+
+
+// Schedules the panel to request data
+// from the server next time it is drawn.
+void LLPanelPick::markForServerRequest()
+{
+ mDataRequested = FALSE;
+ mDataReceived = FALSE;
+}
+
+
+std::string LLPanelPick::getPickName()
+{
+ return mNameEditor->getText();
+}
+
+
+void LLPanelPick::sendPickInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessage("PickInfoRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", mPickID);
+ gAgent.sendReliableMessage();
+
+ mDataRequested = TRUE;
+}
+
+
+void LLPanelPick::sendPickInfoUpdate()
+{
+ // If we don't have a pick id yet, we'll need to generate one,
+ // otherwise we'll keep overwriting pick_id 00000 in the database.
+ if (mPickID.isNull())
+ {
+ mPickID.generate();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("PickInfoUpdate");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("PickID", mPickID);
+ msg->addUUID("CreatorID", mCreatorID);
+ msg->addBOOL("TopPick", mTopPick);
+ // fills in on simulator if null
+ msg->addUUID("ParcelID", mParcelID);
+ msg->addString("Name", mNameEditor->getText());
+ msg->addString("Desc", mDescEditor->getText());
+ msg->addUUID("SnapshotID", mSnapshotCtrl->getImageAssetID());
+ msg->addVector3d("PosGlobal", mPosGlobal);
+
+ // Only top picks have a sort order
+ S32 sort_order;
+ if (mTopPick)
+ {
+ sort_order = atoi(mSortOrderEditor->getText().c_str());
+ }
+ else
+ {
+ sort_order = 0;
+ }
+ msg->addS32("SortOrder", sort_order);
+ msg->addBOOL("Enabled", mEnabledCheck->get());
+ gAgent.sendReliableMessage();
+}
+
+
+//static
+void LLPanelPick::processPickInfoReply(LLMessageSystem *msg, void **)
+{
+ // Extract the agent id and verify the message is for this
+ // client.
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id );
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Agent ID mismatch in processPickInfoReply"
+ << llendl;
+ return;
+ }
+
+ LLUUID pick_id;
+ msg->getUUID("Data", "PickID", pick_id);
+
+ LLUUID creator_id;
+ msg->getUUID("Data", "CreatorID", creator_id);
+
+ BOOL top_pick;
+ msg->getBOOL("Data", "TopPick", top_pick);
+
+ LLUUID parcel_id;
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ char name[DB_PARCEL_NAME_SIZE];
+ msg->getString("Data", "Name", DB_PARCEL_NAME_SIZE, name);
+
+ char desc[DB_PICK_DESC_SIZE];
+ msg->getString("Data", "Desc", DB_PICK_DESC_SIZE, desc);
+
+ LLUUID snapshot_id;
+ msg->getUUID("Data", "SnapshotID", snapshot_id);
+
+ // "Location text" is actually the owner name, the original
+ // name that owner gave the parcel, and the location.
+ char buffer[256];
+ LLString location_text;
+
+ msg->getString("Data", "User", 256, buffer);
+ location_text.assign(buffer);
+ location_text.append(", ");
+
+ msg->getString("Data", "OriginalName", 256, buffer);
+ if (buffer[0] != '\0')
+ {
+ location_text.append(buffer);
+ location_text.append(", ");
+ }
+
+ char sim_name[256];
+ msg->getString("Data", "SimName", 256, sim_name);
+
+ LLVector3d pos_global;
+ msg->getVector3d("Data", "PosGlobal", pos_global);
+
+ S32 region_x = llround((F32)pos_global.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)pos_global.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)pos_global.mdV[VZ]);
+
+ sprintf(buffer, "%s (%d, %d, %d)", sim_name, region_x, region_y, region_z);
+ location_text.append(buffer);
+
+ S32 sort_order;
+ msg->getS32("Data", "SortOrder", sort_order);
+
+ BOOL enabled;
+ msg->getBOOL("Data", "Enabled", enabled);
+
+ // Look up the panel to fill in
+ LLPanelPick *self = NULL;
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ // For top picks, must match pick id
+ if (self->mPickID != pick_id)
+ {
+ continue;
+ }
+
+ self->mDataReceived = TRUE;
+
+ // Found the panel, now fill in the information
+ self->mPickID = pick_id;
+ self->mCreatorID = creator_id;
+ self->mParcelID = parcel_id;
+ self->mSimName.assign(sim_name);
+ self->mPosGlobal = pos_global;
+
+ // Update UI controls
+ self->mNameEditor->setText(name);
+ self->mDescEditor->setText(desc);
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+ self->mLocationEditor->setText(location_text);
+ self->mEnabledCheck->set(enabled);
+
+ sprintf(buffer, "%d", sort_order);
+ self->mSortOrderEditor->setText(buffer);
+ }
+}
+
+void LLPanelPick::draw()
+{
+ if (getVisible())
+ {
+ refresh();
+
+ LLPanel::draw();
+ }
+}
+
+
+void LLPanelPick::refresh()
+{
+ if (!mDataRequested)
+ {
+ sendPickInfoRequest();
+ }
+
+ // Check for god mode
+ BOOL godlike = gAgent.isGodlike();
+ BOOL is_self = (gAgent.getID() == mCreatorID);
+
+ // Set button visibility/enablement appropriately
+ if (mTopPick)
+ {
+ mSnapshotCtrl->setEnabled(godlike);
+ mNameEditor->setEnabled(godlike);
+ mDescEditor->setEnabled(godlike);
+
+ mSortOrderText->setVisible(godlike);
+
+ mSortOrderEditor->setVisible(godlike);
+ mSortOrderEditor->setEnabled(godlike);
+
+ mEnabledCheck->setVisible(godlike);
+ mEnabledCheck->setEnabled(godlike);
+
+ mSetBtn->setVisible(godlike);
+ mSetBtn->setEnabled(godlike);
+ }
+ else
+ {
+ mSnapshotCtrl->setEnabled(is_self);
+ mNameEditor->setEnabled(is_self);
+ mDescEditor->setEnabled(is_self);
+
+ mSortOrderText->setVisible(FALSE);
+
+ mSortOrderEditor->setVisible(FALSE);
+ mSortOrderEditor->setEnabled(FALSE);
+
+ mEnabledCheck->setVisible(FALSE);
+ mEnabledCheck->setEnabled(FALSE);
+
+ mSetBtn->setVisible(is_self);
+ mSetBtn->setEnabled(is_self);
+ }
+}
+
+
+// static
+void LLPanelPick::onClickTeleport(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ }
+}
+
+
+// static
+void LLPanelPick::onClickMap(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+// static
+/*
+void LLPanelPick::onClickLandmark(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+ create_landmark(self->mNameEditor->getText().c_str(), "", self->mPosGlobal);
+}
+*/
+
+// static
+void LLPanelPick::onClickSet(void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ // Save location for later.
+ self->mPosGlobal = gAgent.getPositionGlobal();
+
+ LLString location_text;
+ location_text.assign("(will update after save)");
+ location_text.append(", ");
+
+ S32 region_x = llround((F32)self->mPosGlobal.mdV[VX]) % REGION_WIDTH_UNITS;
+ S32 region_y = llround((F32)self->mPosGlobal.mdV[VY]) % REGION_WIDTH_UNITS;
+ S32 region_z = llround((F32)self->mPosGlobal.mdV[VZ]);
+
+ location_text.append(self->mSimName);
+ location_text.append(llformat(" (%d, %d, %d)", region_x, region_y, region_z));
+
+ // if sim name in pick is different from current sim name
+ // make sure it's clear that all that's being changed
+ // is the location and nothing else
+ if ( gAgent.getRegion ()->getName () != self->mSimName )
+ {
+ gViewerWindow->alertXml("SetPickLocation");
+ };
+
+ self->mLocationEditor->setText(location_text);
+
+ onCommitAny(NULL, data);
+}
+
+
+// static
+void LLPanelPick::onCommitAny(LLUICtrl* ctrl, void* data)
+{
+ LLPanelPick* self = (LLPanelPick*)data;
+
+ // have we received up to date data for this pick?
+ if (self->mDataReceived)
+ {
+ self->sendPickInfoUpdate();
+
+ // Big hack - assume that top picks are always in a browser,
+ // and non-top-picks are always in a tab container.
+ /*if (self->mTopPick)
+ {
+ LLPanelDirPicks* panel = (LLPanelDirPicks*)self->getParent();
+ panel->renamePick(self->mPickID, self->mNameEditor->getText().c_str());
+ }
+ else
+ {*/
+ LLTabContainerVertical* tab = (LLTabContainerVertical*)self->getParent();
+ tab->setCurrentTabName(self->mNameEditor->getText());
+ //}
+ }
+}
diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h
new file mode 100644
index 0000000000..bdc7ef1f3e
--- /dev/null
+++ b/indra/newview/llpanelpick.h
@@ -0,0 +1,99 @@
+/**
+ * @file llpanelpick.h
+ * @brief LLPanelPick class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Display of a "Top Pick" used both for the global top picks in the
+// Find directory, and also for each individual user's picks in their
+// profile.
+
+#ifndef LL_LLPANELPICK_H
+#define LL_LLPANELPICK_H
+
+#include "llpanel.h"
+#include "v3dmath.h"
+#include "lluuid.h"
+
+class LLButton;
+class LLCheckBoxCtrl;
+class LLIconCtrl;
+class LLLineEditor;
+class LLTextBox;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLUICtrl;
+class LLMessageSystem;
+
+class LLPanelPick : public LLPanel
+{
+public:
+ LLPanelPick(BOOL top_pick);
+ /*virtual*/ ~LLPanelPick();
+
+ void reset();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ void draw();
+
+ void refresh();
+
+ // Setup a new pick, including creating an id, giving a sane
+ // initial position, etc.
+ void initNewPick();
+
+ void setPickID(const LLUUID& id);
+
+ // Schedules the panel to request data
+ // from the server next time it is drawn.
+ void markForServerRequest();
+
+ std::string getPickName();
+ const LLUUID& getPickID() const { return mPickID; }
+
+ void sendPickInfoRequest();
+ void sendPickInfoUpdate();
+
+ static void processPickInfoReply(LLMessageSystem* msg, void**);
+
+protected:
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ //static void onClickLandmark(void* data);
+ static void onClickSet(void* data);
+
+ static void onCommitAny(LLUICtrl* ctrl, void* data);
+
+protected:
+ BOOL mTopPick;
+ LLUUID mPickID;
+ LLUUID mCreatorID;
+ LLUUID mParcelID;
+
+ // Data will be requested on first draw
+ BOOL mDataRequested;
+ BOOL mDataReceived;
+
+ LLString mSimName;
+ LLVector3d mPosGlobal;
+
+ LLTextureCtrl* mSnapshotCtrl;
+ LLLineEditor* mNameEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mLocationEditor;
+
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+
+ LLTextBox* mSortOrderText;
+ LLLineEditor* mSortOrderEditor;
+ LLCheckBoxCtrl* mEnabledCheck;
+ LLButton* mSetBtn;
+
+ static LLLinkedList<LLPanelPick> sAllPanels;
+};
+
+#endif // LL_LLPANELPICK_H
diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp
new file mode 100644
index 0000000000..45ca1b0871
--- /dev/null
+++ b/indra/newview/llpanelplace.cpp
@@ -0,0 +1,271 @@
+/**
+ * @file llpanelplace.cpp
+ * @brief Display of a place in the Find directory.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpanelplace.h"
+
+#include "llviewercontrol.h"
+#include "llqueryflags.h"
+#include "message.h"
+#include "llui.h"
+#include "llsecondlifeurls.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llfloaterworldmap.h"
+#include "lllineeditor.h"
+#include "lluiconstants.h"
+#include "lltextbox.h"
+#include "llviewertexteditor.h"
+#include "lltexturectrl.h"
+#include "llworldmap.h"
+#include "llviewerregion.h"
+#include "llvieweruictrlfactory.h"
+//#include "llviewermenu.h" // create_landmark()
+#include "llweb.h"
+
+//static
+LLLinkedList<LLPanelPlace> LLPanelPlace::sAllPanels;
+
+LLPanelPlace::LLPanelPlace()
+: LLPanel("Places Panel"),
+ mParcelID(),
+ mPosGlobal(),
+ mAuctionID(0)
+{
+ sAllPanels.addData(this);
+}
+
+
+LLPanelPlace::~LLPanelPlace()
+{
+ sAllPanels.removeData(this);
+}
+
+
+BOOL LLPanelPlace::postBuild()
+{
+ // Since this is only used in the directory browser, always
+ // disable the snapshot control. Otherwise clicking on it will
+ // open a texture picker.
+ mSnapshotCtrl = LLViewerUICtrlFactory::getTexturePickerByName(this, "snapshot_ctrl");
+ mSnapshotCtrl->setEnabled(FALSE);
+
+ mNameEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "name_editor");
+
+ mDescEditor = LLUICtrlFactory::getTextEditorByName(this, "desc_editor");
+
+ mInfoEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "info_editor");
+
+ mLocationEditor = LLViewerUICtrlFactory::getLineEditorByName(this, "location_editor");
+
+ mTeleportBtn = LLViewerUICtrlFactory::getButtonByName(this, "teleport_btn");
+ mTeleportBtn->setClickedCallback(onClickTeleport);
+ mTeleportBtn->setCallbackUserData(this);
+
+ mMapBtn = LLViewerUICtrlFactory::getButtonByName(this, "map_btn");
+ mMapBtn->setClickedCallback(onClickMap);
+ mMapBtn->setCallbackUserData(this);
+
+ //mLandmarkBtn = LLViewerUICtrlFactory::getButtonByName(this, "landmark_btn");
+ //mLandmarkBtn->setClickedCallback(onClickLandmark);
+ //mLandmarkBtn->setCallbackUserData(this);
+
+ mAuctionBtn = LLViewerUICtrlFactory::getButtonByName(this, "auction_btn");
+ mAuctionBtn->setClickedCallback(onClickAuction);
+ mAuctionBtn->setCallbackUserData(this);
+
+ // Default to no auction button. We'll show it if we get an auction id
+ mAuctionBtn->setVisible(FALSE);
+
+ return TRUE;
+}
+
+
+
+void LLPanelPlace::setParcelID(const LLUUID& parcel_id)
+{
+ mParcelID = parcel_id;
+}
+
+
+void LLPanelPlace::sendParcelInfoRequest()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ if (mParcelID != mRequestedID)
+ {
+ msg->newMessage("ParcelInfoRequest");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("ParcelID", mParcelID);
+ gAgent.sendReliableMessage();
+ mRequestedID = mParcelID;
+ }
+}
+
+
+//static
+void LLPanelPlace::processParcelInfoReply(LLMessageSystem *msg, void **)
+{
+ LLUUID agent_id;
+ LLUUID parcel_id;
+ LLUUID owner_id;
+ char name[MAX_STRING];
+ char desc[MAX_STRING];
+ S32 actual_area;
+ S32 billable_area;
+ U8 flags;
+ F32 global_x;
+ F32 global_y;
+ F32 global_z;
+ char sim_name[MAX_STRING];
+ LLUUID snapshot_id;
+ F32 dwell;
+ S32 sale_price;
+ S32 auction_id;
+ char buffer[256];
+
+ msg->getUUID("AgentData", "AgentID", agent_id );
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ // look up all panels which have this avatar
+ LLPanelPlace *self = NULL;
+
+ for (self = sAllPanels.getFirstData(); self; self = sAllPanels.getNextData())
+ {
+ if (self->mParcelID != parcel_id)
+ {
+ continue;
+ }
+
+ msg->getUUID ("Data", "OwnerID", owner_id);
+ msg->getString ("Data", "Name", MAX_STRING, name);
+ msg->getString ("Data", "Desc", MAX_STRING, desc);
+ msg->getS32 ("Data", "ActualArea", actual_area);
+ msg->getS32 ("Data", "BillableArea", billable_area);
+ msg->getU8 ("Data", "Flags", flags);
+ msg->getF32 ("Data", "GlobalX", global_x);
+ msg->getF32 ("Data", "GlobalY", global_y);
+ msg->getF32 ("Data", "GlobalZ", global_z);
+ msg->getString ("Data", "SimName", MAX_STRING, sim_name);
+ msg->getUUID ("Data", "SnapshotID", snapshot_id);
+ msg->getF32 ("Data", "Dwell", dwell);
+ msg->getS32 ("Data", "SalePrice", sale_price);
+ msg->getS32 ("Data", "AuctionID", auction_id);
+
+ self->mPosGlobal.setVec(global_x, global_y, global_z);
+
+ self->mAuctionID = auction_id;
+
+ self->mSnapshotCtrl->setImageAssetID(snapshot_id);
+
+ self->mNameEditor->setText(name);
+
+ self->mDescEditor->setText(desc);
+
+ LLString info;
+ sprintf(buffer, "Traffic: %.0f, Area: %d sq. m.", dwell, actual_area);
+ info.append(buffer);
+ if (flags & DFQ_FOR_SALE)
+ {
+ sprintf(buffer, ", For Sale for L$%d", sale_price);
+ info.append(buffer);
+ }
+ if (auction_id != 0)
+ {
+ sprintf(buffer, ", Auction ID %010d", auction_id);
+ info.append(buffer);
+ }
+ self->mInfoEditor->setText(info);
+
+ S32 region_x = llround(global_x) % REGION_WIDTH_UNITS;
+ S32 region_y = llround(global_y) % REGION_WIDTH_UNITS;
+ S32 region_z = llround(global_z);
+
+ // HACK: Flag 0x1 == mature region, otherwise assume PG
+ const char* rating = LLViewerRegion::accessToString(SIM_ACCESS_PG);
+ if (flags & 0x1)
+ {
+ rating = LLViewerRegion::accessToString(SIM_ACCESS_MATURE);
+ }
+
+ sprintf(buffer, "%s %d, %d, %d (%s)",
+ sim_name, region_x, region_y, region_z, rating);
+ self->mLocationEditor->setText(buffer);
+
+ BOOL show_auction = (auction_id > 0);
+ self->mAuctionBtn->setVisible(show_auction);
+ }
+}
+
+
+// static
+void LLPanelPlace::onClickTeleport(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gAgent.teleportViaLocation(self->mPosGlobal);
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ }
+}
+
+// static
+void LLPanelPlace::onClickMap(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (!self->mPosGlobal.isExactlyZero())
+ {
+ gFloaterWorldMap->trackLocation(self->mPosGlobal);
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+}
+
+// static
+/*
+void LLPanelPlace::onClickLandmark(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ create_landmark(self->mNameEditor->getText(), "", self->mPosGlobal);
+}
+*/
+
+
+// static
+void LLPanelPlace::onClickAuction(void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ gViewerWindow->alertXml("GoToAuctionPage",
+ callbackAuctionWebPage,
+ self);
+}
+
+// static
+void LLPanelPlace::callbackAuctionWebPage(S32 option, void* data)
+{
+ LLPanelPlace* self = (LLPanelPlace*)data;
+
+ if (0 == option)
+ {
+ char url[256];
+ sprintf(url, "%s%010d", AUCTION_URL, self->mAuctionID);
+
+ llinfos << "Loading auction page " << url << llendl;
+
+ LLWeb::loadURL(url);
+ }
+}
diff --git a/indra/newview/llpanelplace.h b/indra/newview/llpanelplace.h
new file mode 100644
index 0000000000..b45aef3e87
--- /dev/null
+++ b/indra/newview/llpanelplace.h
@@ -0,0 +1,70 @@
+/**
+ * @file llpanelplace.h
+ * @brief Display of a place in the Find directory.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELPLACE_H
+#define LL_LLPANELPLACE_H
+
+#include "llpanel.h"
+
+#include "v3dmath.h"
+#include "lluuid.h"
+
+class LLButton;
+class LLTextBox;
+class LLLineEditor;
+class LLTextEditor;
+class LLTextureCtrl;
+class LLMessageSystem;
+
+class LLPanelPlace : public LLPanel
+{
+public:
+ LLPanelPlace();
+ /*virtual*/ ~LLPanelPlace();
+
+ /*virtual*/ BOOL postBuild();
+
+
+ void setParcelID(const LLUUID& parcel_id);
+
+ void sendParcelInfoRequest();
+
+ static void processParcelInfoReply(LLMessageSystem* msg, void**);
+
+protected:
+ static void onClickTeleport(void* data);
+ static void onClickMap(void* data);
+ //static void onClickLandmark(void* data);
+ static void onClickAuction(void* data);
+
+ // Go to auction web page if user clicked OK
+ static void callbackAuctionWebPage(S32 option, void* data);
+
+protected:
+ LLUUID mParcelID;
+ LLUUID mRequestedID;
+ LLVector3d mPosGlobal;
+ // Zero if this is not an auction
+ S32 mAuctionID;
+
+ LLTextureCtrl* mSnapshotCtrl;
+
+ LLLineEditor* mNameEditor;
+ LLTextEditor* mDescEditor;
+ LLLineEditor* mInfoEditor;
+ LLLineEditor* mLocationEditor;
+
+ LLButton* mTeleportBtn;
+ LLButton* mMapBtn;
+ //LLButton* mLandmarkBtn;
+ LLButton* mAuctionBtn;
+
+ static LLLinkedList<LLPanelPlace> sAllPanels;
+};
+
+#endif // LL_LLPANELPLACE_H
diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp
new file mode 100644
index 0000000000..cd942db7e1
--- /dev/null
+++ b/indra/newview/llpanelvolume.cpp
@@ -0,0 +1,499 @@
+/**
+ * @file llpanelvolume.cpp
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llpanelvolume.h"
+
+// linden library includes
+#include "llclickaction.h"
+#include "lleconomy.h"
+#include "llerror.h"
+#include "llfontgl.h"
+#include "llflexibleobject.h"
+#include "llmaterialtable.h"
+#include "llpermissionsflags.h"
+#include "llstring.h"
+#include "llvolume.h"
+#include "m3math.h"
+#include "material_codes.h"
+
+// project includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcolorswatch.h"
+#include "llcombobox.h"
+#include "llfirstuse.h"
+#include "llfocusmgr.h"
+#include "llmanipscale.h"
+#include "llpanelinventory.h"
+#include "llpreviewscript.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llspinctrl.h"
+#include "lltextbox.h"
+#include "lltool.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+#include "lldrawpool.h"
+#include "llvieweruictrlfactory.h"
+
+// "Features" Tab
+
+BOOL LLPanelVolume::postBuild()
+{
+ // Flexible Objects Parameters
+ {
+ childSetCommitCallback("Flexible1D Checkbox Ctrl",onCommitIsFlexible,this);
+ childSetCommitCallback("FlexNumSections",onCommitFlexible,this);
+ childSetValidate("FlexNumSections",precommitValidate);
+ childSetCommitCallback("FlexGravity",onCommitFlexible,this);
+ childSetValidate("FlexGravity",precommitValidate);
+ childSetCommitCallback("FlexFriction",onCommitFlexible,this);
+ childSetValidate("FlexFriction",precommitValidate);
+ childSetCommitCallback("FlexWind",onCommitFlexible,this);
+ childSetValidate("FlexWind",precommitValidate);
+ childSetCommitCallback("FlexTension",onCommitFlexible,this);
+ childSetValidate("FlexTension",precommitValidate);
+ childSetCommitCallback("FlexForceX",onCommitFlexible,this);
+ childSetValidate("FlexForceX",precommitValidate);
+ childSetCommitCallback("FlexForceY",onCommitFlexible,this);
+ childSetValidate("FlexForceY",precommitValidate);
+ childSetCommitCallback("FlexForceZ",onCommitFlexible,this);
+ childSetValidate("FlexForceZ",precommitValidate);
+ }
+
+ // LIGHT Parameters
+ {
+ childSetCommitCallback("Light Checkbox Ctrl",onCommitIsLight,this);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch){
+ LightColorSwatch->setOnCancelCallback(onLightCancelColor);
+ LightColorSwatch->setOnSelectCallback(onLightSelectColor);
+ childSetCommitCallback("colorswatch",onCommitLight,this);
+ }
+ childSetCommitCallback("Light Intensity",onCommitLight,this);
+ childSetValidate("Light Intensity",precommitValidate);
+ childSetCommitCallback("Light Radius",onCommitLight,this);
+ childSetValidate("Light Radius",precommitValidate);
+ childSetCommitCallback("Light Falloff",onCommitLight,this);
+ childSetValidate("Light Falloff",precommitValidate);
+ }
+
+ // Start with everyone disabled
+ clearCtrls();
+
+ return TRUE;
+}
+
+LLPanelVolume::LLPanelVolume(const std::string& name)
+ : LLPanel(name)
+{
+ setMouseOpaque(FALSE);
+
+}
+
+
+LLPanelVolume::~LLPanelVolume()
+{
+ // Children all cleaned up by default view destructor.
+}
+
+void LLPanelVolume::getState( )
+{
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ LLViewerObject* root_objectp = objectp;
+ if(!objectp)
+ {
+ objectp = gSelectMgr->getFirstObject();
+ //FIXME: shouldn't we just keep the child?
+ if (objectp)
+ {
+ LLViewerObject* parentp = objectp->getSubParent();
+
+ if (parentp)
+ {
+ root_objectp = parentp;
+ }
+ else
+ {
+ root_objectp = objectp;
+ }
+ }
+ }
+
+ LLVOVolume *volobjp = NULL;
+ if ( objectp && (objectp->getPCode() == LL_PCODE_VOLUME))
+ {
+ volobjp = (LLVOVolume *)objectp;
+ }
+
+ if( !objectp )
+ {
+ //forfeit focus
+ if (gFocusMgr.childHasKeyboardFocus(this))
+ {
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ }
+
+ // Disable all text input fields
+ clearCtrls();
+
+ return;
+ }
+
+ BOOL owners_identical;
+ LLUUID owner_id;
+ LLString owner_name;
+ owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+
+ // BUG? Check for all objects being editable?
+ BOOL editable = root_objectp->permModify();
+ BOOL single_volume = gSelectMgr->selectionAllPCode( LL_PCODE_VOLUME )
+ && gSelectMgr->getObjectCount() == 1;
+
+ // Select Single Message
+ if (single_volume)
+ {
+ childSetVisible("edit_object",true);
+ childSetEnabled("edit_object",true);
+ childSetVisible("select_single",false);
+ }
+ else
+ {
+ childSetVisible("edit_object",false);
+ childSetVisible("select_single",true);
+ childSetEnabled("select_single",true);
+ }
+
+ // Light properties
+ BOOL is_light = volobjp && volobjp->getIsLight();
+ childSetValue("Light Checkbox Ctrl",is_light);
+ childSetEnabled("Light Checkbox Ctrl",editable && single_volume && volobjp);
+
+ if (is_light && editable && single_volume)
+ {
+ childSetEnabled("label color",true);
+ //mLabelColor ->setEnabled( TRUE );
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( TRUE );
+ LightColorSwatch->setValid( TRUE );
+ LightColorSwatch->set(volobjp->getLightBaseColor());
+ }
+ childSetEnabled("Light Intensity",true);
+ childSetEnabled("Light Radius",true);
+ childSetEnabled("Light Falloff",true);
+
+ childSetValue("Light Intensity",volobjp->getLightIntensity());
+ childSetValue("Light Radius",volobjp->getLightRadius());
+ childSetValue("Light Falloff",volobjp->getLightFalloff());
+
+ mLightSavedColor = volobjp->getLightColor();
+ }
+ else
+ {
+ childSetEnabled("label color",false);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( FALSE );
+ LightColorSwatch->setValid( FALSE );
+ }
+ childSetEnabled("Light Intensity",false);
+ childSetEnabled("Light Radius",false);
+ childSetEnabled("Light Falloff",false);
+ }
+
+ // Flexible properties
+ BOOL is_flexible = volobjp && volobjp->isFlexible();
+ childSetValue("Flexible1D Checkbox Ctrl",is_flexible);
+ if (is_flexible || (volobjp && volobjp->canBeFlexible()))
+ {
+ childSetEnabled("Flexible1D Checkbox Ctrl", editable && single_volume && volobjp);
+ }
+ else
+ {
+ childSetEnabled("Flexible1D Checkbox Ctrl", false);
+ }
+ if (is_flexible && editable && single_volume)
+ {
+ childSetVisible("FlexNumSections",true);
+ childSetVisible("FlexGravity",true);
+ childSetVisible("FlexTension",true);
+ childSetVisible("FlexFriction",true);
+ childSetVisible("FlexWind",true);
+ childSetVisible("FlexForceX",true);
+ childSetVisible("FlexForceY",true);
+ childSetVisible("FlexForceZ",true);
+
+ childSetEnabled("FlexNumSections",true);
+ childSetEnabled("FlexGravity",true);
+ childSetEnabled("FlexTension",true);
+ childSetEnabled("FlexFriction",true);
+ childSetEnabled("FlexWind",true);
+ childSetEnabled("FlexForceX",true);
+ childSetEnabled("FlexForceY",true);
+ childSetEnabled("FlexForceZ",true);
+
+ LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+
+ childSetValue("FlexNumSections",(F32)attributes->getSimulateLOD());
+ childSetValue("FlexGravity",attributes->getGravity());
+ childSetValue("FlexTension",attributes->getTension());
+ childSetValue("FlexFriction",attributes->getAirFriction());
+ childSetValue("FlexWind",attributes->getWindSensitivity());
+ childSetValue("FlexForceX",attributes->getUserForce().mV[VX]);
+ childSetValue("FlexForceY",attributes->getUserForce().mV[VY]);
+ childSetValue("FlexForceZ",attributes->getUserForce().mV[VZ]);
+ }
+ else
+ {
+ childSetEnabled("FlexNumSections",false);
+ childSetEnabled("FlexGravity",false);
+ childSetEnabled("FlexTension",false);
+ childSetEnabled("FlexFriction",false);
+ childSetEnabled("FlexWind",false);
+ childSetEnabled("FlexForceX",false);
+ childSetEnabled("FlexForceY",false);
+ childSetEnabled("FlexForceZ",false);
+ }
+
+ mObject = objectp;
+ mRootObject = root_objectp;
+}
+
+// static
+BOOL LLPanelVolume::precommitValidate( LLUICtrl* ctrl, void* userdata )
+{
+ // TODO: Richard will fill this in later.
+ return TRUE; // FALSE means that validation failed and new value should not be commited.
+}
+
+
+void LLPanelVolume::refresh()
+{
+ getState();
+ if (mObject.notNull() && mObject->isDead())
+ {
+ mObject = NULL;
+ }
+
+ if (mRootObject.notNull() && mRootObject->isDead())
+ {
+ mRootObject = NULL;
+ }
+}
+
+
+void LLPanelVolume::draw()
+{
+ LLPanel::draw();
+}
+
+// virtual
+void LLPanelVolume::clearCtrls()
+{
+ LLPanel::clearCtrls();
+
+ childSetEnabled("select_single",false);
+ childSetVisible("select_single",true);
+ childSetEnabled("edit_object",false);
+ childSetVisible("edit_object",false);
+ childSetEnabled("Light Checkbox Ctrl",false);
+ childSetEnabled("label color",false);
+ childSetEnabled("label color",false);
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(this,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setEnabled( FALSE );
+ LightColorSwatch->setValid( FALSE );
+ }
+ childSetEnabled("Light Intensity",false);
+ childSetEnabled("Light Radius",false);
+ childSetEnabled("Light Falloff",false);
+
+ childSetEnabled("Flexible1D Checkbox Ctrl",false);
+ childSetEnabled("FlexNumSections",false);
+ childSetEnabled("FlexGravity",false);
+ childSetEnabled("FlexTension",false);
+ childSetEnabled("FlexFriction",false);
+ childSetEnabled("FlexWind",false);
+ childSetEnabled("FlexForceX",false);
+ childSetEnabled("FlexForceY",false);
+ childSetEnabled("FlexForceZ",false);
+}
+
+//
+// Static functions
+//
+
+void LLPanelVolume::sendIsLight()
+{
+ LLViewerObject* objectp = mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+ BOOL value = childGetValue("Light Checkbox Ctrl");
+ volobjp->setIsLight(value);
+ llinfos << "update light sent" << llendl;
+}
+
+void LLPanelVolume::sendIsFlexible()
+{
+ LLViewerObject* objectp = mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+ BOOL is_flexible = childGetValue("Flexible1D Checkbox Ctrl");
+ //BOOL is_flexible = mCheckFlexible1D->get();
+
+ if (is_flexible)
+ {
+ LLFirstUse::useFlexible();
+
+ if (objectp->getClickAction() == CLICK_ACTION_SIT)
+ {
+ gSelectMgr->selectionSetClickAction(CLICK_ACTION_NONE);
+ }
+
+ }
+
+ if (volobjp->setIsFlexible(is_flexible))
+ {
+ mObject->sendShapeUpdate();
+ gSelectMgr->selectionUpdatePhantom(volobjp->flagPhantom());
+ }
+
+ llinfos << "update flexible sent" << llendl;
+}
+
+void LLPanelVolume::onLightCancelColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LightColorSwatch->setColor(self->mLightSavedColor);
+ }
+ onLightSelectColor(NULL, userdata);
+}
+
+void LLPanelVolume::onLightSelectColor(LLUICtrl* ctrl, void* userdata)
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LLColor4 clr = LightColorSwatch->get();
+ LLColor3 clr3( clr );
+ volobjp->setLightColor(clr3);
+ self->mLightSavedColor = clr;
+ }
+}
+
+// static
+void LLPanelVolume::onCommitLight( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+ LLVOVolume *volobjp = (LLVOVolume *)objectp;
+
+
+ volobjp->setLightIntensity((F32)self->childGetValue("Light Intensity").asReal());
+ volobjp->setLightRadius((F32)self->childGetValue("Light Radius").asReal());
+ volobjp->setLightFalloff((F32)self->childGetValue("Light Falloff").asReal());
+ LLColorSwatchCtrl* LightColorSwatch = gUICtrlFactory->getColorSwatchByName(self,"colorswatch");
+ if(LightColorSwatch)
+ {
+ LLColor4 clr = LightColorSwatch->get();
+ volobjp->setLightColor(LLColor3(clr));
+ }
+}
+
+// static
+void LLPanelVolume::onCommitIsLight( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ self->sendIsLight();
+}
+
+//----------------------------------------------------------------------------
+
+// static
+void LLPanelVolume::onCommitFlexible( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ LLViewerObject* objectp = self->mObject;
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+
+ LLFlexibleObjectData *attributes = (LLFlexibleObjectData *)objectp->getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+ if (attributes)
+ {
+ LLFlexibleObjectData new_attributes;
+ new_attributes = *attributes;
+
+
+ new_attributes.setSimulateLOD(self->childGetValue("FlexNumSections").asInteger());//(S32)self->mSpinSections->get());
+ new_attributes.setGravity((F32)self->childGetValue("FlexGravity").asReal());
+ new_attributes.setTension((F32)self->childGetValue("FlexTension").asReal());
+ new_attributes.setAirFriction((F32)self->childGetValue("FlexFriction").asReal());
+ new_attributes.setWindSensitivity((F32)self->childGetValue("FlexWind").asReal());
+ F32 fx = (F32)self->childGetValue("FlexForceX").asReal();
+ F32 fy = (F32)self->childGetValue("FlexForceY").asReal();
+ F32 fz = (F32)self->childGetValue("FlexForceZ").asReal();
+ LLVector3 force(fx,fy,fz);
+
+ new_attributes.setUserForce(force);
+ objectp->setParameterEntry(LLNetworkData::PARAMS_FLEXIBLE, new_attributes, true);
+ }
+
+ // Values may fail validation
+ self->refresh();
+}
+
+// static
+void LLPanelVolume::onCommitIsFlexible( LLUICtrl* ctrl, void* userdata )
+{
+ LLPanelVolume* self = (LLPanelVolume*) userdata;
+ self->sendIsFlexible();
+}
+
diff --git a/indra/newview/llpanelvolume.h b/indra/newview/llpanelvolume.h
new file mode 100644
index 0000000000..c8f4486521
--- /dev/null
+++ b/indra/newview/llpanelvolume.h
@@ -0,0 +1,82 @@
+/**
+ * @file llpanelvolume.h
+ * @brief Object editing (position, scale, etc.) in the tools floater
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPANELVOLUME_H
+#define LL_LLPANELVOLUME_H
+
+#include "v3math.h"
+#include "llpanel.h"
+#include "llmemory.h"
+#include "llvolume.h"
+
+class LLSpinCtrl;
+class LLCheckBoxCtrl;
+class LLTextBox;
+class LLUICtrl;
+class LLButton;
+class LLViewerObject;
+class LLComboBox;
+class LLPanelInventory;
+class LLColorSwatchCtrl;
+
+class LLPanelVolume : public LLPanel
+{
+public:
+ LLPanelVolume(const std::string& name);
+ virtual ~LLPanelVolume();
+
+ virtual void draw();
+ virtual void clearCtrls();
+
+ virtual BOOL postBuild();
+
+ void refresh();
+
+ void sendIsLight();
+ void sendIsFlexible();
+
+ static BOOL precommitValidate(LLUICtrl* ctrl,void* userdata);
+
+ static void onCommitIsLight( LLUICtrl* ctrl, void* userdata);
+ static void onCommitLight( LLUICtrl* ctrl, void* userdata);
+ static void onCommitIsFlexible( LLUICtrl* ctrl, void* userdata);
+ static void onCommitFlexible( LLUICtrl* ctrl, void* userdata);
+
+ static void onLightCancelColor(LLUICtrl* ctrl, void* userdata);
+ static void onLightSelectColor(LLUICtrl* ctrl, void* userdata);
+
+protected:
+ void getState();
+
+protected:
+/*
+ LLTextBox* mLabelSelectSingleMessage;
+ // Light
+ LLCheckBoxCtrl* mCheckLight;
+ LLCheckBoxCtrl* mCheckFlexible1D;
+ LLTextBox* mLabelColor;
+ LLColorSwatchCtrl* mLightColorSwatch;
+ LLSpinCtrl* mLightIntensity;
+ LLSpinCtrl* mLightRadius;
+ LLSpinCtrl* mLightFalloff;
+ LLSpinCtrl* mLightCutoff;
+ // Flexibile
+ LLSpinCtrl* mSpinSections;
+ LLSpinCtrl* mSpinGravity;
+ LLSpinCtrl* mSpinTension;
+ LLSpinCtrl* mSpinFriction;
+ LLSpinCtrl* mSpinWind;
+ LLSpinCtrl* mSpinForce[3];
+*/
+
+ LLColor4 mLightSavedColor;
+ LLPointer<LLViewerObject> mObject;
+ LLPointer<LLViewerObject> mRootObject;
+};
+
+#endif
diff --git a/indra/newview/llpatchvertexarray.cpp b/indra/newview/llpatchvertexarray.cpp
new file mode 100644
index 0000000000..62f8ac87bb
--- /dev/null
+++ b/indra/newview/llpatchvertexarray.cpp
@@ -0,0 +1,231 @@
+/**
+ * @file llpatchvertexarray.cpp
+ * @brief Implementation of the LLSurfaceVertexArray class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpatchvertexarray.h"
+#include "llsurfacepatch.h"
+
+// constructors
+
+LLPatchVertexArray::LLPatchVertexArray() :
+ mSurfaceWidth(0),
+ mPatchWidth(0),
+ mPatchOrder(0),
+ mRenderLevelp(NULL),
+ mRenderStridep(NULL)
+{
+}
+
+LLPatchVertexArray::LLPatchVertexArray(U32 surface_width, U32 patch_width, F32 meters_per_grid) :
+ mRenderLevelp(NULL),
+ mRenderStridep(NULL)
+{
+ create(surface_width, patch_width, meters_per_grid);
+}
+
+
+LLPatchVertexArray::~LLPatchVertexArray()
+{
+ destroy();
+}
+
+
+void LLPatchVertexArray::create(U32 surface_width, U32 patch_width, F32 meters_per_grid)
+{
+ // PART 1 -- Make sure the arguments are good...
+ // Make sure patch_width is not greater than surface_width
+ if (patch_width > surface_width)
+ {
+ U32 temp = patch_width;
+ patch_width = surface_width;
+ surface_width = temp;
+ }
+
+ // Make sure (surface_width-1) is equal to a power_of_two.
+ // (The -1 is there because an LLSurface has a buffer of 1 on
+ // its East and North edges).
+ U32 power_of_two = 1;
+ U32 surface_order = 0;
+ while (power_of_two < (surface_width-1))
+ {
+ power_of_two *= 2;
+ surface_order += 1;
+ }
+
+ if (power_of_two == (surface_width-1))
+ {
+ mSurfaceWidth = surface_width;
+
+ // Make sure patch_width is a factor of (surface_width - 1)
+ U32 ratio = (surface_width - 1) / patch_width;
+ F32 fratio = ((float)(surface_width - 1)) / ((float)(patch_width));
+ if ( fratio == (float)(ratio))
+ {
+ // Make sure patch_width is a power of two
+ power_of_two = 1;
+ U32 patch_order = 0;
+ while (power_of_two < patch_width)
+ {
+ power_of_two *= 2;
+ patch_order += 1;
+ }
+ if (power_of_two == patch_width)
+ {
+ mPatchWidth = patch_width;
+ mPatchOrder = patch_order;
+ }
+ else // patch_width is not a power of two...
+ {
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+ }
+ else // patch_width is not a factor of (surface_width - 1)...
+ {
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+ }
+ else // surface_width is not a power of two...
+ {
+ mSurfaceWidth = 0;
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+ }
+
+ // PART 2 -- Allocate memory for the render level table
+ if (mPatchWidth > 0)
+ {
+ mRenderLevelp = new U32 [2*mPatchWidth + 1];
+ mRenderStridep = new U32 [mPatchOrder + 1];
+ }
+
+
+ // Now that we've allocated memory, we can initialize the arrays...
+ init();
+}
+
+
+void LLPatchVertexArray::destroy()
+{
+ if (mPatchWidth == 0)
+ {
+ return;
+ }
+
+ delete [] mRenderLevelp;
+ delete [] mRenderStridep;
+ mSurfaceWidth = 0;
+ mPatchWidth = 0;
+ mPatchOrder = 0;
+}
+
+
+void LLPatchVertexArray::init()
+// Initializes the triangle strip arrays.
+{
+ U32 j;
+ U32 level, stride;
+ U32 k;
+
+ // We need to build two look-up tables...
+
+ // render_level -> render_stride
+ // A 16x16 patch has 5 render levels : 2^0 to 2^4
+ // render_level render_stride
+ // 4 1
+ // 3 2
+ // 2 4
+ // 1 8
+ // 0 16
+ stride = mPatchWidth;
+ for (level=0; level<mPatchOrder + 1; level++)
+ {
+ mRenderStridep[level] = stride;
+ stride /= 2;
+ }
+
+ // render_level <- render_stride.
+/*
+ // For a 16x16 patch we'll clamp the render_strides to 0 through 16
+ // and enter the nearest render_level in the table. Of course, only
+ // power-of-two render strides are actually used.
+ //
+ // render_stride render_level
+ // 0 4
+ // 1 4 *
+ // 2 3 *
+ // 3 3
+ // 4 2 *
+ // 5 2
+ // 6 2
+ // 7 1
+ // 8 1 *
+ // 9 1
+ // 10 1
+ // 11 1
+ // 12 1
+ // 13 0
+ // 14 0
+ // 15 0
+ // 16 Always 0
+
+ level = mPatchOrder;
+ for (stride=0; stride<mPatchWidth; stride++)
+ {
+ if ((F32) stride > 2.1f * mRenderStridep[level])
+ {
+ level--;
+ };
+ mRenderLevelp[stride] = level;
+ }
+ */
+
+ // This method is more agressive about putting triangles onscreen
+ level = mPatchOrder;
+ k = 2;
+ mRenderLevelp[0] = mPatchOrder;
+ mRenderLevelp[1] = mPatchOrder;
+ stride = 2;
+ while(stride < 2*mPatchWidth)
+ {
+ for (j=0; j<k && stride<2*mPatchWidth; j++)
+ {
+ mRenderLevelp[stride++] = level;
+ }
+ k *= 2;
+ level--;
+ }
+ mRenderLevelp[2*mPatchWidth] = 0;
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLPatchVertexArray &va)
+{
+ U32 i;
+ s << "{ \n";
+ s << " mSurfaceWidth = " << va.mSurfaceWidth << "\n";
+ s << " mPatchWidth = " << va.mPatchWidth << "\n";
+ s << " mPatchOrder = " << va.mPatchOrder << "\n";
+ s << " mRenderStridep = \n";
+ for (i=0; i<va.mPatchOrder+1; i++)
+ {
+ s << " " << i << " " << va.mRenderStridep[i] << "\n";
+ }
+ s << " mRenderLevelp = \n";
+ for (i=0; i < 2*va.mPatchWidth + 1; i++)
+ {
+ s << " " << i << " " << va.mRenderLevelp[i] << "\n";
+ }
+ s << "}";
+ return s;
+}
+
+
+// EOF
diff --git a/indra/newview/llpatchvertexarray.h b/indra/newview/llpatchvertexarray.h
new file mode 100644
index 0000000000..5addfa57de
--- /dev/null
+++ b/indra/newview/llpatchvertexarray.h
@@ -0,0 +1,51 @@
+/**
+ * @file llpatchvertexarray.h
+ * @brief description of Surface class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPATCHVERTEXARRAY_H
+#define LL_LLPATCHVERTEXARRAY_H
+
+// A LLPatchVertexArray is really just a structure of vertex arrays for
+// rendering a "patch" of a certain size.
+//
+// A "patch" is currently a sub-square of a larger square array of data
+// we call a "surface".
+class LLPatchVertexArray
+{
+public:
+ LLPatchVertexArray();
+ LLPatchVertexArray(U32 surface_width, U32 patch_width, F32 meters_per_grid);
+ virtual ~LLPatchVertexArray();
+
+ void create(U32 surface_width, U32 patch_width, F32 meters_per_grid);
+ void destroy();
+ void init();
+
+public:
+ U32 mSurfaceWidth; // grid points on one side of a LLSurface
+ U32 mPatchWidth; // grid points on one side of a LLPatch
+ U32 mPatchOrder; // 2^mPatchOrder >= mPatchWidth
+
+ U32 *mRenderLevelp; // Look-up table : render_stride -> render_level
+ U32 *mRenderStridep; // Look-up table : render_level -> render_stride
+
+ // We want to be able to render a patch from multiple resolutions.
+ // The lowest resolution has two triangles, and the highest has
+ // 2*mPatchWidth*mPatchWidth triangles.
+ //
+ // mPatchWidth is not hard-coded, so we don't know how much memory
+ // to allocate to the vertex arrays until it is set. Once it is
+ // set, we will calculate how much total memory to allocate for the
+ // vertex arrays, and then keep track of their lengths and locations
+ // in the memory bank.
+ //
+ // A Patch has three regions that need vertex arrays: middle, north,
+ // and east. For each region there are three items that must be
+ // kept track of: data, offset, and length.
+};
+
+#endif
diff --git a/indra/newview/llpolymesh.cpp b/indra/newview/llpolymesh.cpp
new file mode 100644
index 0000000000..593a502b37
--- /dev/null
+++ b/indra/newview/llpolymesh.cpp
@@ -0,0 +1,1117 @@
+/**
+ * @file llpolymesh.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llpolymesh.h"
+
+#include "llviewercontrol.h"
+#include "llxmltree.h"
+#include "llvoavatar.h"
+#include "lldir.h"
+#include "llvolume.h"
+#include "llendianswizzle.h"
+
+#include "llfasttimer.h"
+
+#define HEADER_ASCII "Linden Mesh 1.0"
+#define HEADER_BINARY "Linden Binary Mesh 1.0"
+
+extern LLControlGroup gSavedSettings; // read only
+
+//-----------------------------------------------------------------------------
+// Global table of loaded LLPolyMeshes
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMeshSharedDataTable LLPolyMesh::sGlobalSharedMeshList;
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::LLPolyMeshSharedData()
+{
+ mNumVertices = 0;
+ mBaseCoords = NULL;
+ mBaseNormals = NULL;
+ mBaseBinormals = NULL;
+ mTexCoords = NULL;
+ mDetailTexCoords = NULL;
+ mWeights = NULL;
+ mHasWeights = FALSE;
+ mHasDetailTexCoords = FALSE;
+
+ mNumFaces = 0;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ mJointNames = NULL;
+
+ mTriangleIndices = NULL;
+ mNumTriangleIndices = 0;
+
+ mReferenceData = NULL;
+
+ mLastIndexOffset = -1;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMeshSharedData()
+//-----------------------------------------------------------------------------
+LLPolyMeshSharedData::~LLPolyMeshSharedData()
+{
+ freeMeshData();
+ mMorphData.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// setupLOD()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::setupLOD(LLPolyMeshSharedData* reference_data)
+{
+ mReferenceData = reference_data;
+
+ if (reference_data)
+ {
+ mBaseCoords = reference_data->mBaseCoords;
+ mBaseNormals = reference_data->mBaseNormals;
+ mBaseBinormals = reference_data->mBaseBinormals;
+ mTexCoords = reference_data->mTexCoords;
+ mDetailTexCoords = reference_data->mDetailTexCoords;
+ mWeights = reference_data->mWeights;
+ mHasWeights = reference_data->mHasWeights;
+ mHasDetailTexCoords = reference_data->mHasDetailTexCoords;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::freeMeshData()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::freeMeshData()
+{
+ if (!mReferenceData)
+ {
+ mNumVertices = 0;
+
+ delete [] mBaseCoords;
+ mBaseCoords = NULL;
+
+ delete [] mBaseNormals;
+ mBaseNormals = NULL;
+
+ delete [] mBaseBinormals;
+ mBaseBinormals = NULL;
+
+ delete [] mTexCoords;
+ mTexCoords = NULL;
+
+ delete [] mDetailTexCoords;
+ mDetailTexCoords = NULL;
+
+ delete [] mWeights;
+ mWeights = NULL;
+ }
+
+ mNumFaces = 0;
+ delete [] mFaces;
+ mFaces = NULL;
+
+ mNumJointNames = 0;
+ delete [] mJointNames;
+ mJointNames = NULL;
+
+ delete [] mTriangleIndices;
+ mTriangleIndices = NULL;
+
+// mVertFaceMap.deleteAllData();
+}
+
+// compate_int is used by the qsort function to sort the index array
+int compare_int(const void *a, const void *b);
+
+//-----------------------------------------------------------------------------
+// genIndices()
+//-----------------------------------------------------------------------------
+void LLPolyMeshSharedData::genIndices(S32 index_offset)
+{
+ if (index_offset == mLastIndexOffset)
+ {
+ return;
+ }
+
+ delete []mTriangleIndices;
+ mTriangleIndices = new U32[mNumTriangleIndices];
+
+ S32 cur_index = 0;
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ mTriangleIndices[cur_index] = mFaces[i][0] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][1] + index_offset;
+ cur_index++;
+ mTriangleIndices[cur_index] = mFaces[i][2] + index_offset;
+ cur_index++;
+ }
+
+ mLastIndexOffset = index_offset;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::getNumKB()
+//--------------------------------------------------------------------
+U32 LLPolyMeshSharedData::getNumKB()
+{
+ U32 num_kb = sizeof(LLPolyMesh);
+
+ if (!isLOD())
+ {
+ num_kb += mNumVertices *
+ ( sizeof(LLVector3) + // coords
+ sizeof(LLVector3) + // normals
+ sizeof(LLVector2) ); // texCoords
+ }
+
+ if (mHasDetailTexCoords && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(LLVector2); // detailTexCoords
+ }
+
+ if (mHasWeights && !isLOD())
+ {
+ num_kb += mNumVertices * sizeof(float); // weights
+ }
+
+ num_kb += mNumFaces * sizeof(LLPolyFace); // faces
+
+ num_kb /= 1024;
+ return num_kb;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateVertexData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateVertexData( U32 numVertices )
+{
+ U32 i;
+ mBaseCoords = new LLVector3[ numVertices ];
+ mBaseNormals = new LLVector3[ numVertices ];
+ mBaseBinormals = new LLVector3[ numVertices ];
+ mTexCoords = new LLVector2[ numVertices ];
+ mDetailTexCoords = new LLVector2[ numVertices ];
+ mWeights = new F32[ numVertices ];
+ for (i = 0; i < numVertices; i++)
+ {
+ mWeights[i] = 0.f;
+ }
+ mNumVertices = numVertices;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateFaceData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateFaceData( U32 numFaces )
+{
+ mFaces = new LLPolyFace[ numFaces ];
+ mNumFaces = numFaces;
+ mNumTriangleIndices = mNumFaces * 3;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMeshSharedData::allocateJointNames()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::allocateJointNames( U32 numJointNames )
+{
+ mJointNames = new std::string[ numJointNames ];
+ mNumJointNames = numJointNames;
+ return TRUE;
+}
+
+//--------------------------------------------------------------------
+// LLPolyMeshSharedData::loadMesh()
+//--------------------------------------------------------------------
+BOOL LLPolyMeshSharedData::loadMesh( const char *fileName )
+{
+ //-------------------------------------------------------------------------
+ // Open the file
+ //-------------------------------------------------------------------------
+ FILE *fp = LLFile::fopen(fileName, "rb");
+ if (!fp)
+ {
+ llerrs << "can't open: " << fileName << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // Read a chunk
+ //-------------------------------------------------------------------------
+ char header[128];
+ fread(header, sizeof(char), 128, fp);
+
+ //-------------------------------------------------------------------------
+ // Check for proper binary header
+ //-------------------------------------------------------------------------
+ BOOL status = FALSE;
+ if ( strncmp(header, HEADER_BINARY, strlen(HEADER_BINARY)) == 0 )
+ {
+ lldebugs << "Loading " << fileName << llendl;
+
+ //----------------------------------------------------------------
+ // File Header (seek past it)
+ //----------------------------------------------------------------
+ fseek(fp, 24, SEEK_SET);
+
+ //----------------------------------------------------------------
+ // HasWeights
+ //----------------------------------------------------------------
+ U8 hasWeights;
+ size_t numRead = fread(&hasWeights, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasWeights flag from " << fileName << llendl;
+ }
+ if (!isLOD())
+ {
+ mHasWeights = (hasWeights==0) ? FALSE : TRUE;
+ }
+
+ //----------------------------------------------------------------
+ // HasDetailTexCoords
+ //----------------------------------------------------------------
+ U8 hasDetailTexCoords;
+ numRead = fread(&hasDetailTexCoords, sizeof(U8), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read HasDetailTexCoords flag from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Position
+ //----------------------------------------------------------------
+ LLVector3 position;
+ numRead = fread(position.mV, sizeof(float), 3, fp);
+ llendianswizzle(position.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Position from " << fileName << llendl;
+ }
+ setPosition( position );
+
+ //----------------------------------------------------------------
+ // Rotation
+ //----------------------------------------------------------------
+ LLVector3 rotationAngles;
+ numRead = fread(rotationAngles.mV, sizeof(float), 3, fp);
+ llendianswizzle(rotationAngles.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read RotationAngles from " << fileName << llendl;
+ }
+
+ U8 rotationOrder;
+ numRead = fread(&rotationOrder, 1, 1, fp);
+
+ if (numRead != 1)
+ {
+ llerrs << "can't read RotationOrder from " << fileName << llendl;
+ }
+
+ rotationOrder = 0;
+
+ setRotation( mayaQ( rotationAngles.mV[0],
+ rotationAngles.mV[1],
+ rotationAngles.mV[2],
+ (LLQuaternion::Order)rotationOrder ) );
+
+ //----------------------------------------------------------------
+ // Scale
+ //----------------------------------------------------------------
+ LLVector3 scale;
+ numRead = fread(scale.mV, sizeof(float), 3, fp);
+ llendianswizzle(scale.mV, sizeof(float), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Scale from " << fileName << llendl;
+ }
+ setScale( scale );
+
+ //-------------------------------------------------------------------------
+ // Release any existing mesh geometry
+ //-------------------------------------------------------------------------
+ freeMeshData();
+
+ U16 numVertices = 0;
+
+ //----------------------------------------------------------------
+ // NumVertices
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ numRead = fread(&numVertices, sizeof(U16), 1, fp);
+ llendianswizzle(&numVertices, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumVertices from " << fileName << llendl;
+ }
+
+ allocateVertexData( numVertices );
+
+ //----------------------------------------------------------------
+ // Coords
+ //----------------------------------------------------------------
+ numRead = fread(mBaseCoords, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseCoords, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read Coordinates from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Normals
+ //----------------------------------------------------------------
+ numRead = fread(mBaseNormals, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseNormals, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << " can't read Normals from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // Binormals
+ //----------------------------------------------------------------
+ numRead = fread(mBaseBinormals, 3*sizeof(float), numVertices, fp);
+ llendianswizzle(mBaseBinormals, sizeof(float), 3*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << " can't read Binormals from " << fileName << llendl;
+ }
+
+
+ //----------------------------------------------------------------
+ // TexCoords
+ //----------------------------------------------------------------
+ numRead = fread(mTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read TexCoords from " << fileName << llendl;
+ }
+
+ //----------------------------------------------------------------
+ // DetailTexCoords
+ //----------------------------------------------------------------
+ if (mHasDetailTexCoords)
+ {
+ numRead = fread(mDetailTexCoords, 2*sizeof(float), numVertices, fp);
+ llendianswizzle(mDetailTexCoords, sizeof(float), 2*numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read DetailTexCoords from " << fileName << llendl;
+ }
+ }
+
+ //----------------------------------------------------------------
+ // Weights
+ //----------------------------------------------------------------
+ if (mHasWeights)
+ {
+ numRead = fread(mWeights, sizeof(float), numVertices, fp);
+ llendianswizzle(mWeights, sizeof(float), numVertices);
+ if (numRead != numVertices)
+ {
+ llerrs << "can't read Weights from " << fileName << llendl;
+ }
+ }
+ }
+
+ //----------------------------------------------------------------
+ // NumFaces
+ //----------------------------------------------------------------
+ U16 numFaces;
+ numRead = fread(&numFaces, sizeof(U16), 1, fp);
+ llendianswizzle(&numFaces, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumFaces from " << fileName << llendl;
+ }
+ allocateFaceData( numFaces );
+
+
+ //----------------------------------------------------------------
+ // Faces
+ //----------------------------------------------------------------
+ U32 i;
+ U32 numTris = 0;
+ for (i = 0; i < numFaces; i++)
+ {
+ S16 face[3];
+ numRead = fread(face, sizeof(U16), 3, fp);
+ llendianswizzle(face, sizeof(U16), 3);
+ if (numRead != 3)
+ {
+ llerrs << "can't read Face[" << i << "] from " << fileName << llendl;
+ }
+ if (mReferenceData)
+ {
+ llassert(face[0] < mReferenceData->mNumVertices);
+ llassert(face[1] < mReferenceData->mNumVertices);
+ llassert(face[2] < mReferenceData->mNumVertices);
+ }
+
+ if (isLOD())
+ {
+ // store largest index in case of LODs
+ for (S32 j = 0; j < 3; j++)
+ {
+ if (face[j] > mNumVertices - 1)
+ {
+ mNumVertices = face[j] + 1;
+ }
+ }
+ }
+ mFaces[i][0] = face[0];
+ mFaces[i][1] = face[1];
+ mFaces[i][2] = face[2];
+
+// S32 j;
+// for(j = 0; j < 3; j++)
+// {
+// LLDynamicArray<S32> *face_list = mVertFaceMap.getIfThere(face[j]);
+// if (!face_list)
+// {
+// face_list = new LLDynamicArray<S32>;
+// mVertFaceMap.addData(face[j], face_list);
+// }
+// face_list->put(i);
+// }
+
+ numTris++;
+ }
+
+ lldebugs << "verts: " << numVertices
+ << ", faces: " << numFaces
+ << ", tris: " << numTris
+ << llendl;
+
+ //----------------------------------------------------------------
+ // NumSkinJoints
+ //----------------------------------------------------------------
+ if (!isLOD())
+ {
+ U16 numSkinJoints = 0;
+ if ( mHasWeights )
+ {
+ numRead = fread(&numSkinJoints, sizeof(U16), 1, fp);
+ llendianswizzle(&numSkinJoints, sizeof(U16), 1);
+ if (numRead != 1)
+ {
+ llerrs << "can't read NumSkinJoints from " << fileName << llendl;
+ }
+ allocateJointNames( numSkinJoints );
+ }
+
+ //----------------------------------------------------------------
+ // SkinJoints
+ //----------------------------------------------------------------
+ for (i=0; i < numSkinJoints; i++)
+ {
+ char jointName[64];
+ numRead = fread(jointName, sizeof(jointName), 1, fp);
+ if (numRead != 1)
+ {
+ llerrs << "can't read Skin[" << i << "].Name from " << fileName << llendl;
+ }
+
+ std::string *jn = &mJointNames[i];
+ *jn = jointName;
+ }
+
+ //-------------------------------------------------------------------------
+ // look for morph section
+ //-------------------------------------------------------------------------
+ char morphName[64];
+ while(fread(&morphName, sizeof(char), 64, fp) == 64)
+ {
+ if (!strcmp(morphName, "End Morphs"))
+ {
+ // we reached the end of the morphs
+ break;
+ }
+ LLPolyMorphData* morph_data = new LLPolyMorphData(morphName);
+
+ BOOL result = morph_data->loadBinary(fp, this);
+
+ if (!result)
+ {
+ delete morph_data;
+ continue;
+ }
+
+ mMorphData.addData(morph_data);
+ }
+
+ S32 numRemaps;
+ if (fread(&numRemaps, sizeof(S32), 1, fp) == 1)
+ {
+ llendianswizzle(&numRemaps, sizeof(S32), 1);
+ for (S32 i = 0; i < numRemaps; i++)
+ {
+ S32 remapSrc;
+ S32 remapDst;
+ if (fread(&remapSrc, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read source vertex in vertex remap data" << llendl;
+ break;
+ }
+ if (fread(&remapDst, sizeof(S32), 1, fp) != 1)
+ {
+ llerrs << "can't read destination vertex in vertex remap data" << llendl;
+ break;
+ }
+ llendianswizzle(&remapSrc, sizeof(S32), 1);
+ llendianswizzle(&remapDst, sizeof(S32), 1);
+
+ mSharedVerts[remapSrc] = remapDst;
+ }
+ }
+ }
+
+ status = TRUE;
+ }
+ else
+ {
+ llerrs << "invalid mesh file header: " << fileName << llendl;
+ status = FALSE;
+ }
+
+ if (0 == mNumJointNames)
+ {
+ allocateJointNames(1);
+ }
+
+ fclose( fp );
+
+ return status;
+}
+
+//-----------------------------------------------------------------------------
+// getSharedVert()
+//-----------------------------------------------------------------------------
+const S32 *LLPolyMeshSharedData::getSharedVert(S32 vert)
+{
+ if (mSharedVerts.count(vert) > 0)
+ {
+ return &mSharedVerts[vert];
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getUV()
+//-----------------------------------------------------------------------------
+const LLVector2 &LLPolyMeshSharedData::getUVs(U32 index)
+{
+ // TODO: convert all index variables to S32
+ llassert((S32)index < mNumVertices);
+
+ return mTexCoords[index];
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh)
+{
+ llassert(shared_data);
+
+ mSharedData = shared_data;
+ mReferenceMesh = reference_mesh;
+ mAvatarp = NULL;
+ mVertexData = NULL;
+
+ if (shared_data->isLOD() && reference_mesh)
+ {
+ mCoords = reference_mesh->mCoords;
+ mNormals = reference_mesh->mNormals;
+ mScaledNormals = reference_mesh->mScaledNormals;
+ mBinormals = reference_mesh->mBinormals;
+ mScaledBinormals = reference_mesh->mScaledBinormals;
+ mTexCoords = reference_mesh->mTexCoords;
+ mClothingWeights = reference_mesh->mClothingWeights;
+ }
+ else
+ {
+#if 1 // Allocate memory without initializing every vector
+ // NOTE: This makes asusmptions about the size of LLVector[234]
+ int nverts = mSharedData->mNumVertices;
+ int nfloats = nverts * (3*5 + 2 + 4);
+ mVertexData = new F32[nfloats];
+ int offset = 0;
+ mCoords = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mNormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mScaledNormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mBinormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mScaledBinormals = (LLVector3*)(mVertexData + offset); offset += 3*nverts;
+ mTexCoords = (LLVector2*)(mVertexData + offset); offset += 2*nverts;
+ mClothingWeights = (LLVector4*)(mVertexData + offset); offset += 4*nverts;
+#else
+ mCoords = new LLVector3[mSharedData->mNumVertices];
+ mNormals = new LLVector3[mSharedData->mNumVertices];
+ mScaledNormals = new LLVector3[mSharedData->mNumVertices];
+ mBinormals = new LLVector3[mSharedData->mNumVertices];
+ mScaledBinormals = new LLVector3[mSharedData->mNumVertices];
+ mTexCoords = new LLVector2[mSharedData->mNumVertices];
+ mClothingWeights = new LLVector4[mSharedData->mNumVertices];
+ memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
+#endif
+ initializeForMorph();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh::~LLPolyMesh()
+{
+ S32 i;
+ for (i = 0; i < mJointRenderData.count(); i++)
+ {
+ delete mJointRenderData[i];
+ mJointRenderData[i] = NULL;
+ }
+#if 0 // These are now allocated as one big uninitialized chunk
+ delete [] mCoords;
+ delete [] mNormals;
+ delete [] mScaledNormals;
+ delete [] mBinormals;
+ delete [] mScaledBinormals;
+ delete [] mClothingWeights;
+ delete [] mTexCoords;
+#else
+ delete [] mVertexData;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::getMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh *LLPolyMesh::getMesh(const LLString &name, LLPolyMesh* reference_mesh)
+{
+ //-------------------------------------------------------------------------
+ // search for an existing mesh by this name
+ //-------------------------------------------------------------------------
+ LLPolyMeshSharedData **meshSharedData = sGlobalSharedMeshList.getValue(name);
+ if (meshSharedData)
+ {
+// llinfos << "Polymesh " << name << " found in global mesh table." << llendl;
+ LLPolyMesh *poly_mesh = new LLPolyMesh(*meshSharedData, reference_mesh);
+ return poly_mesh;
+ }
+
+ //-------------------------------------------------------------------------
+ // if not found, create a new one, add it to the list
+ //-------------------------------------------------------------------------
+ char full_path[LL_MAX_PATH];
+ sprintf(full_path, "%s", (gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,name.c_str())).c_str());
+
+ LLPolyMeshSharedData *mesh_data = new LLPolyMeshSharedData();
+ if (reference_mesh)
+ {
+ mesh_data->setupLOD(reference_mesh->getSharedData());
+ }
+ if ( ! mesh_data->loadMesh( full_path ) )
+ {
+ delete mesh_data;
+ return NULL;
+ }
+
+ LLPolyMesh *poly_mesh = new LLPolyMesh(mesh_data, reference_mesh);
+
+// llinfos << "Polymesh " << name << " added to global mesh table." << llendl;
+ sGlobalSharedMeshList.addToTail(name, poly_mesh->mSharedData);
+
+ return poly_mesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh::freeAllMeshes()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::freeAllMeshes()
+{
+ U32 i;
+
+ // delete each item in the global lists
+ for (i=0; i<sGlobalSharedMeshList.length(); i++)
+ {
+ // returns a pointer to the value, which is the pointer
+ // to the mesh
+ LLPolyMeshSharedData **shared_mesh_pp = sGlobalSharedMeshList.getValueAt(i);
+
+ // delete the mesh
+ delete *shared_mesh_pp;
+ }
+
+ // empty the lists
+ sGlobalSharedMeshList.removeAll();
+}
+
+LLPolyMeshSharedData *LLPolyMesh::getSharedData() const
+{
+ return mSharedData;
+}
+
+
+//--------------------------------------------------------------------
+// LLPolyMesh::dumpDiagInfo()
+//--------------------------------------------------------------------
+void LLPolyMesh::dumpDiagInfo()
+{
+ // keep track of totals
+ U32 total_verts = 0;
+ U32 total_faces = 0;
+ U32 total_kb = 0;
+
+ char buf[1024];
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ llinfos << " Global PolyMesh Table (DEBUG only)" << llendl;
+ llinfos << " Verts Faces Mem(KB) Name" << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+
+ // print each loaded mesh, and it's memory usage
+ for (U32 i=0; i<sGlobalSharedMeshList.length(); i++)
+ {
+ std::string *mesh_name_p = sGlobalSharedMeshList.getIndexAt(i);
+
+ LLPolyMeshSharedData **mesh_pp = sGlobalSharedMeshList.getValueAt(i);
+ LLPolyMeshSharedData &mesh = **mesh_pp;
+
+ S32 num_verts = mesh.mNumVertices;
+ S32 num_faces = mesh.mNumFaces;
+ U32 num_kb = mesh.getNumKB();
+
+ sprintf(buf, "%8d %8d %8d %s", num_verts, num_faces, num_kb, mesh_name_p->c_str());
+ llinfos << buf << llendl;
+
+ total_verts += num_verts;
+ total_faces += num_faces;
+ total_kb += num_kb;
+ }
+
+ llinfos << "-----------------------------------------------------" << llendl;
+ sprintf(buf, "%8d %8d %8d TOTAL", total_verts, total_faces, total_kb );
+ llinfos << buf << llendl;
+ llinfos << "-----------------------------------------------------" << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// getCoords()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMesh::getCoords() const
+{
+ return mCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableCoords()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableCoords()
+{
+ return mCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableNormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableNormals()
+{
+ return mNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableBinormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getWritableBinormals()
+{
+ return mBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// getWritableClothingWeights()
+//-----------------------------------------------------------------------------
+LLVector4 *LLPolyMesh::getWritableClothingWeights()
+{
+ return mClothingWeights;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableTexCoords()
+//-----------------------------------------------------------------------------
+LLVector2 *LLPolyMesh::getWritableTexCoords()
+{
+ return mTexCoords;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledNormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getScaledNormals()
+{
+ return mScaledNormals;
+}
+
+//-----------------------------------------------------------------------------
+// getScaledBinormals()
+//-----------------------------------------------------------------------------
+LLVector3 *LLPolyMesh::getScaledBinormals()
+{
+ return mScaledBinormals;
+}
+
+
+//-----------------------------------------------------------------------------
+// initializeForMorph()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::initializeForMorph()
+{
+ if (!mSharedData)
+ return;
+
+ memcpy(mCoords, mSharedData->mBaseCoords, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mScaledNormals, mSharedData->mBaseNormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mScaledBinormals, mSharedData->mBaseBinormals, sizeof(LLVector3) * mSharedData->mNumVertices);
+ memcpy(mTexCoords, mSharedData->mTexCoords, sizeof(LLVector2) * mSharedData->mNumVertices);
+ memset(mClothingWeights, 0, sizeof(LLVector4) * mSharedData->mNumVertices);
+}
+
+//-----------------------------------------------------------------------------
+// getMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData* LLPolyMesh::getMorphData(const char *morph_name)
+{
+ if (!mSharedData) return NULL;
+ for (LLPolyMorphData *morph_data = mSharedData->mMorphData.getFirstData();
+ morph_data;
+ morph_data = mSharedData->mMorphData.getNextData())
+ {
+ if (!strcmp(morph_data->getName(), morph_name))
+ {
+ return morph_data;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// removeMorphData()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::removeMorphData(LLPolyMorphData *morph_target)
+{
+ if (!mSharedData) return;
+
+ mSharedData->mMorphData.removeData(morph_target);
+}
+
+//-----------------------------------------------------------------------------
+// deleteAllMorphData()
+//-----------------------------------------------------------------------------
+void LLPolyMesh::deleteAllMorphData()
+{
+ if (!mSharedData) return;
+
+ mSharedData->mMorphData.deleteAllData();
+}
+
+//-----------------------------------------------------------------------------
+// getWeights()
+//-----------------------------------------------------------------------------
+const F32* LLPolyMesh::getWeights() const
+{
+ return mSharedData->mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// getWritableWeights()
+//-----------------------------------------------------------------------------
+F32* LLPolyMesh::getWritableWeights() const
+{
+ return mSharedData->mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortionInfo()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortionInfo::LLPolySkeletalDistortionInfo()
+{
+}
+
+BOOL LLPolySkeletalDistortionInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_skeleton" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* skeletalParam = node->getChildByName("param_skeleton");
+
+ for( LLXmlTreeNode* bone = skeletalParam->getFirstChild(); bone; bone = skeletalParam->getNextChild() )
+ {
+ if (bone->hasName("bone"))
+ {
+ LLString name;
+ LLVector3 scale;
+ LLVector3 pos;
+ BOOL haspos = FALSE;
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!bone->getFastAttributeString(name_string, name))
+ {
+ llwarns << "No bone name specified for skeletal param." << llendl;
+ continue;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!bone->getFastAttributeVector3(scale_string, scale))
+ {
+ llwarns << "No scale specified for bone " << name << "." << llendl;
+ continue;
+ }
+
+ // optional offset deformation (translation)
+ static LLStdStringHandle offset_string = LLXmlTree::addAttributeString("offset");
+ if (bone->getFastAttributeVector3(offset_string, pos))
+ {
+ haspos = TRUE;
+ }
+ mBoneInfoList.push_back(LLPolySkeletalBoneInfo(name, scale, pos, haspos));
+ }
+ else
+ {
+ llwarns << "Unrecognized element " << bone->getName() << " in skeletal distortion" << llendl;
+ continue;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::LLPolySkeletalDistortion(LLVOAvatar *avatarp)
+{
+ mAvatar = avatarp;
+ mDefaultVec.setVec(0.001f, 0.001f, 0.001f);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolySkeletalDistortion()
+//-----------------------------------------------------------------------------
+LLPolySkeletalDistortion::~LLPolySkeletalDistortion()
+{
+}
+
+BOOL LLPolySkeletalDistortion::setInfo(LLPolySkeletalDistortionInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLPolySkeletalDistortionInfo::bone_info_list_t::iterator iter;
+ for (iter = getInfo()->mBoneInfoList.begin(); iter != getInfo()->mBoneInfoList.end(); iter++)
+ {
+ LLPolySkeletalBoneInfo *bone_info = &(*iter);
+ LLJoint* joint = mAvatar->getJoint(bone_info->mBoneName);
+ if (!joint)
+ {
+ llwarns << "Joint " << bone_info->mBoneName << " not found." << llendl;
+ continue;
+ }
+
+ if (mJointScales.find(joint) != mJointScales.end())
+ {
+ llwarns << "Scale deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+
+ // store it
+ mJointScales[joint] = bone_info->mScaleDeformation;
+
+ // apply to children that need to inherit it
+ for ( LLViewerJoint *child_joint = (LLViewerJoint *)joint->mChildren.getFirstData();
+ child_joint != NULL;
+ child_joint = (LLViewerJoint *)joint->mChildren.getNextData() )
+ {
+ if (child_joint->inheritScale())
+ {
+ LLVector3 childDeformation = LLVector3(child_joint->getScale());
+ childDeformation.scaleVec(bone_info->mScaleDeformation);
+ mJointScales[child_joint] = childDeformation;
+ }
+ }
+
+ if (bone_info->mHasPositionDeformation)
+ {
+ if (mJointOffsets.find(joint) != mJointOffsets.end())
+ {
+ llwarns << "Offset deformation already supplied for joint " << joint->getName() << "." << llendl;
+ }
+ mJointOffsets[joint] = bone_info->mPositionDeformation;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+void LLPolySkeletalDistortion::apply( ESex avatar_sex )
+{
+ F32 effective_weight = ( getSex() & avatar_sex ) ? mCurWeight : getDefaultWeight();
+
+ LLJoint* joint;
+ joint_vec_map_t::iterator iter;
+
+ for (iter = mJointScales.begin();
+ iter != mJointScales.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newScale = joint->getScale();
+ LLVector3 scaleDelta = iter->second;
+ newScale = newScale + (effective_weight * scaleDelta) - (mLastWeight * scaleDelta);
+ joint->setScale(newScale);
+ }
+
+ for (iter = mJointOffsets.begin();
+ iter != mJointOffsets.end();
+ iter++)
+ {
+ joint = iter->first;
+ LLVector3 newPosition = joint->getPosition();
+ LLVector3 positionDelta = iter->second;
+ newPosition = newPosition + (effective_weight * positionDelta) - (mLastWeight * positionDelta);
+ joint->setPosition(newPosition);
+ }
+
+ if (mLastWeight != mCurWeight && !mIsAnimating)
+ {
+ mAvatar->setSkeletonSerialNum(mAvatar->getSkeletonSerialNum() + 1);
+ }
+ mLastWeight = mCurWeight;
+}
+
+// End
diff --git a/indra/newview/llpolymesh.h b/indra/newview/llpolymesh.h
new file mode 100644
index 0000000000..cefda92288
--- /dev/null
+++ b/indra/newview/llpolymesh.h
@@ -0,0 +1,412 @@
+/**
+ * @file llpolymesh.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOLYMESH_H
+#define LL_LLPOLYMESH_H
+
+#include <string>
+#include <map>
+#include "llstl.h"
+
+#include "v3math.h"
+#include "v2math.h"
+#include "llquaternion.h"
+#include "llskipmap.h"
+#include "llassoclist.h"
+#include "llpolymorph.h"
+#include "llptrskipmap.h"
+#include "lljoint.h"
+//#include "lldarray.h"
+
+class LLSkinJoint;
+class LLVOAvatar;
+
+//#define USE_STRIPS // Use tri-strips for rendering.
+
+//-----------------------------------------------------------------------------
+// LLPolyFace
+// A set of 4 vertex indices.
+// An LLPolyFace can represent either a triangle or quad.
+// If the last index is -1, it's a triangle.
+//-----------------------------------------------------------------------------
+typedef S32 LLPolyFace[3];
+
+//struct PrimitiveGroup;
+
+//-----------------------------------------------------------------------------
+// LLPolyMesh
+// A polyhedra consisting of any number of triangles and quads.
+// All instances contain a set of faces, and optionally may include
+// faces grouped into named face sets.
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget;
+
+class LLPolyMeshSharedData
+{
+ friend class LLPolyMesh;
+private:
+ // transform data
+ LLVector3 mPosition;
+ LLQuaternion mRotation;
+ LLVector3 mScale;
+
+ // vertex data
+ S32 mNumVertices;
+ LLVector3 *mBaseCoords;
+ LLVector3 *mBaseNormals;
+ LLVector3 *mBaseBinormals;
+ LLVector2 *mTexCoords;
+ LLVector2 *mDetailTexCoords;
+ F32 *mWeights;
+
+ BOOL mHasWeights;
+ BOOL mHasDetailTexCoords;
+
+ // face data
+ S32 mNumFaces;
+ LLPolyFace *mFaces;
+
+ // face set data
+ U32 mNumJointNames;
+ std::string* mJointNames;
+
+ // morph targets
+ typedef LLLinkedList<LLPolyMorphData> LLPolyMorphDataList;
+ LLPolyMorphDataList mMorphData;
+
+ std::map<S32, S32> mSharedVerts;
+
+ LLPolyMeshSharedData* mReferenceData;
+ S32 mLastIndexOffset;
+
+public:
+ // Temporarily...
+ // Triangle indices
+ U32 mNumTriangleIndices;
+ U32 *mTriangleIndices;
+
+private:
+ LLPolyMeshSharedData();
+
+ ~LLPolyMeshSharedData();
+
+ void setupLOD(LLPolyMeshSharedData* reference_data);
+
+ // Frees all mesh memory resources
+ void freeMeshData();
+
+ void setPosition( const LLVector3 &pos ) { mPosition = pos; }
+ void setRotation( const LLQuaternion &rot ) { mRotation = rot; }
+ void setScale( const LLVector3 &scale ) { mScale = scale; }
+
+ BOOL allocateVertexData( U32 numVertices );
+
+ BOOL allocateFaceData( U32 numFaces );
+
+ BOOL allocateJointNames( U32 numJointNames );
+
+ // Retrieve the number of KB of memory used by this instance
+ U32 getNumKB();
+
+ // Load mesh data from file
+ BOOL loadMesh( const char *fileName );
+
+public:
+ void genIndices(S32 offset);
+
+ const LLVector2 &getUVs(U32 index);
+
+ const S32 *getSharedVert(S32 vert);
+
+ BOOL isLOD() { return (mReferenceData != NULL); }
+};
+
+
+class LLJointRenderData
+{
+public:
+ LLJointRenderData(const LLMatrix4* world_matrix, LLSkinJoint* skin_joint) : mWorldMatrix(world_matrix), mSkinJoint(skin_joint){}
+ ~LLJointRenderData(){}
+
+ const LLMatrix4* mWorldMatrix;
+ LLSkinJoint* mSkinJoint;
+};
+
+
+class LLPolyMesh
+{
+public:
+
+ // Constructor
+ LLPolyMesh(LLPolyMeshSharedData *shared_data, LLPolyMesh *reference_mesh);
+
+ // Destructor
+ ~LLPolyMesh();
+
+ // Requests a mesh by name.
+ // If the mesh already exists in the global mesh table, it is returned,
+ // otherwise it is loaded from file, added to the table, and returned.
+ static LLPolyMesh *getMesh( const LLString &name, LLPolyMesh* reference_mesh = NULL);
+
+ // Frees all loaded meshes.
+ // This should only be called once you know there are no outstanding
+ // references to these objects. Generally, upon exit of the application.
+ static void freeAllMeshes();
+
+ //--------------------------------------------------------------------
+ // Transform Data Access
+ //--------------------------------------------------------------------
+ // Get position
+ const LLVector3 &getPosition() {
+ llassert (mSharedData);
+ return mSharedData->mPosition;
+ }
+
+ // Get rotation
+ const LLQuaternion &getRotation() {
+ llassert (mSharedData);
+ return mSharedData->mRotation;
+ }
+
+ // Get scale
+ const LLVector3 &getScale() {
+ llassert (mSharedData);
+ return mSharedData->mScale;
+ }
+
+ //--------------------------------------------------------------------
+ // Vertex Data Access
+ //--------------------------------------------------------------------
+ // Get number of vertices
+ U32 getNumVertices() {
+ llassert (mSharedData);
+ return mSharedData->mNumVertices;
+ }
+
+ // Returns whether or not the mesh has detail texture coords
+ BOOL hasDetailTexCoords() {
+ llassert (mSharedData);
+ return mSharedData->mHasDetailTexCoords;
+ }
+
+ // Returns whether or not the mesh has vertex weights
+ BOOL hasWeights() const{
+ llassert (mSharedData);
+ return mSharedData->mHasWeights;
+ }
+
+ // Get coords
+ const LLVector3 *getCoords() const;
+
+ // non const version
+ LLVector3 *getWritableCoords();
+
+ // Get normals
+ const LLVector3 *getNormals() const{
+ return mNormals;
+ }
+
+ // Get normals
+ const LLVector3 *getBinormals() const{
+ return mBinormals;
+ }
+
+ // Get base mesh normals
+ const LLVector3 *getBaseNormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseNormals;
+ }
+
+ // Get base mesh normals
+ const LLVector3 *getBaseBinormals() const{
+ llassert(mSharedData);
+ return mSharedData->mBaseBinormals;
+ }
+
+ // intermediate morphed normals and output normals
+ LLVector3 *getWritableNormals();
+ LLVector3 *getScaledNormals();
+
+ LLVector3 *getWritableBinormals();
+ LLVector3 *getScaledBinormals();
+
+ // Get texCoords
+ const LLVector2 *getTexCoords() const {
+ return mTexCoords;
+ }
+
+ // non const version
+ LLVector2 *getWritableTexCoords();
+
+ // Get detailTexCoords
+ const LLVector2 *getDetailTexCoords() const {
+ llassert (mSharedData);
+ return mSharedData->mDetailTexCoords;
+ }
+
+ // Get weights
+ const F32 *getWeights() const;
+
+ F32 *getWritableWeights() const;
+
+ LLVector4 *getWritableClothingWeights();
+
+ const LLVector4 *getClothingWeights()
+ {
+ return mClothingWeights;
+ }
+
+ //--------------------------------------------------------------------
+ // Face Data Access
+ //--------------------------------------------------------------------
+ // Get number of faces
+ S32 getNumFaces() {
+ llassert (mSharedData);
+ return mSharedData->mNumFaces;
+ }
+
+ // Get faces
+ LLPolyFace *getFaces() {
+ llassert (mSharedData);
+ return mSharedData->mFaces;
+ }
+
+ U32 getNumJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mNumJointNames;
+ }
+
+ std::string *getJointNames() {
+ llassert (mSharedData);
+ return mSharedData->mJointNames;
+ }
+
+ LLPolyMorphData* getMorphData(const char *morph_name);
+ void removeMorphData(LLPolyMorphData *morph_target);
+ void deleteAllMorphData();
+
+ LLPolyMeshSharedData *getSharedData() const;
+ LLPolyMesh *getReferenceMesh() { return mReferenceMesh ? mReferenceMesh : this; }
+
+ // Get indices
+ U32* getIndices() { return mSharedData ? mSharedData->mTriangleIndices : NULL; }
+
+ BOOL isLOD() { return mSharedData && mSharedData->isLOD(); }
+
+ void setAvatar(LLVOAvatar* avatarp) { mAvatarp = avatarp; }
+ LLVOAvatar* getAvatar() { return mAvatarp; }
+
+ LLDynamicArray<LLJointRenderData*> mJointRenderData;
+
+ U32 mFaceVertexOffset;
+ U32 mFaceVertexCount;
+ U32 mFaceIndexCount;
+ U32 mCurVertexCount;
+private:
+ void initializeForMorph();
+
+ // Dumps diagnostic information about the global mesh table
+ static void dumpDiagInfo();
+
+protected:
+ // mesh data shared across all instances of a given mesh
+ LLPolyMeshSharedData *mSharedData;
+ // Single array of floats for allocation / deletion
+ F32 *mVertexData;
+ // deformed vertices (resulting from application of morph targets)
+ LLVector3 *mCoords;
+ // deformed normals (resulting from application of morph targets)
+ LLVector3 *mScaledNormals;
+ // output normals (after normalization)
+ LLVector3 *mNormals;
+ // deformed binormals (resulting from application of morph targets)
+ LLVector3 *mScaledBinormals;
+ // output binormals (after normalization)
+ LLVector3 *mBinormals;
+ // weight values that mark verts as clothing/skin
+ LLVector4 *mClothingWeights;
+ // output texture coordinates
+ LLVector2 *mTexCoords;
+
+ LLPolyMesh *mReferenceMesh;
+
+ // global mesh list
+ typedef LLAssocList<std::string, LLPolyMeshSharedData*> LLPolyMeshSharedDataTable;
+ static LLPolyMeshSharedDataTable sGlobalSharedMeshList;
+
+ LLVOAvatar* mAvatarp;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformationInfo
+// Shared information for LLPolySkeletalDeformations
+//-----------------------------------------------------------------------------
+struct LLPolySkeletalBoneInfo
+{
+ LLPolySkeletalBoneInfo(LLString &name, LLVector3 &scale, LLVector3 &pos, BOOL haspos)
+ : mBoneName(name),
+ mScaleDeformation(scale),
+ mPositionDeformation(pos),
+ mHasPositionDeformation(haspos) {}
+ LLString mBoneName;
+ LLVector3 mScaleDeformation;
+ LLVector3 mPositionDeformation;
+ BOOL mHasPositionDeformation;
+};
+
+class LLPolySkeletalDistortionInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolySkeletalDistortion;
+public:
+ LLPolySkeletalDistortionInfo();
+ /*virtual*/ ~LLPolySkeletalDistortionInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::vector<LLPolySkeletalBoneInfo> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolySkeletalDeformation
+// A set of joint scale data for deforming the avatar mesh
+//-----------------------------------------------------------------------------
+class LLPolySkeletalDistortion : public LLViewerVisualParam
+{
+public:
+ LLPolySkeletalDistortion(LLVOAvatar *avatarp);
+ ~LLPolySkeletalDistortion();
+
+ // Special: These functions are overridden by child classes
+ LLPolySkeletalDistortionInfo* getInfo() const { return (LLPolySkeletalDistortionInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolySkeletalDistortionInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 0.1f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mDefaultVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 0.1f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh){return LLVector3(0.001f, 0.001f, 0.001f);}
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return &mDefaultVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh){index = 0; poly_mesh = NULL; return NULL;};
+
+protected:
+ typedef std::map<LLJoint*, LLVector3> joint_vec_map_t;
+ joint_vec_map_t mJointScales;
+ joint_vec_map_t mJointOffsets;
+ LLVector3 mDefaultVec;
+ LLVOAvatar* mAvatar;
+};
+
+#endif // LL_LLPOLYMESH_H
+
diff --git a/indra/newview/llpolymorph.cpp b/indra/newview/llpolymorph.cpp
new file mode 100644
index 0000000000..317550a383
--- /dev/null
+++ b/indra/newview/llpolymorph.cpp
@@ -0,0 +1,639 @@
+/**
+ * @file llpolymorph.cpp
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llpolymorph.h"
+#include "linked_lists.h"
+#include "llvoavatar.h"
+#include "llxmltree.h"
+#include "llendianswizzle.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+const F32 NORMAL_SOFTEN_FACTOR = 0.65f;
+
+LLLinkedList<LLPolyMorphData> gLoadedMorphs;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::LLPolyMorphData(char *morph_name)
+{
+ llassert (morph_name);
+
+ mName = new char[strlen(morph_name) + 1];
+ strcpy(mName, morph_name);
+
+ mNumIndices = 0;
+ mCurrentIndex = 0;
+ mTotalDistortion = 0.f;
+ mAvgDistortion.zeroVec();
+ mMaxDistortion = 0.f;
+ mVertexIndices = NULL;
+ mCoords = NULL;
+ mNormals = NULL;
+ mBinormals = NULL;
+ mTexCoords = NULL;
+
+ mMesh = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphData()
+//-----------------------------------------------------------------------------
+LLPolyMorphData::~LLPolyMorphData()
+{
+ delete [] mName;
+ delete [] mVertexIndices;
+ delete [] mCoords;
+ delete [] mNormals;
+ delete [] mBinormals;
+ delete [] mTexCoords;
+}
+
+//-----------------------------------------------------------------------------
+// loadBinary()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphData::loadBinary(FILE *fp, LLPolyMeshSharedData *mesh)
+{
+ S32 numVertices;
+ S32 numRead;
+
+ numRead = fread(&numVertices, sizeof(S32), 1, fp);
+ llendianswizzle(&numVertices, sizeof(S32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read number of morph target vertices" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate vertices
+ //-------------------------------------------------------------------------
+ mCoords = new LLVector3[numVertices];
+ mNormals = new LLVector3[numVertices];
+ mBinormals = new LLVector3[numVertices];
+ mTexCoords = new LLVector2[numVertices];
+ // Actually, we are allocating more space than we need for the skiplist
+ mVertexIndices = new U32[numVertices];
+ mNumIndices = 0;
+ mTotalDistortion = 0.f;
+ mMaxDistortion = 0.f;
+ mAvgDistortion.zeroVec();
+ mMesh = mesh;
+
+ //-------------------------------------------------------------------------
+ // read vertices
+ //-------------------------------------------------------------------------
+ for(S32 v = 0; v < numVertices; v++)
+ {
+ numRead = fread(&mVertexIndices[v], sizeof(U32), 1, fp);
+ llendianswizzle(&mVertexIndices[v], sizeof(U32), 1);
+ if (numRead != 1)
+ {
+ llwarns << "Can't read morph target vertex number" << llendl;
+ return FALSE;
+ }
+
+ if (mVertexIndices[v] > 10000)
+ {
+ llerrs << "Bad morph index: " << mVertexIndices[v] << llendl;
+ }
+
+
+ numRead = fread(&mCoords[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mCoords[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target vertex coordinates" << llendl;
+ return FALSE;
+ }
+
+ F32 magnitude = mCoords[v].magVec();
+
+ mTotalDistortion += magnitude;
+ mAvgDistortion.mV[VX] += fabs(mCoords[v].mV[VX]);
+ mAvgDistortion.mV[VY] += fabs(mCoords[v].mV[VY]);
+ mAvgDistortion.mV[VZ] += fabs(mCoords[v].mV[VZ]);
+
+ if (magnitude > mMaxDistortion)
+ {
+ mMaxDistortion = magnitude;
+ }
+
+ numRead = fread(&mNormals[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mNormals[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target normal" << llendl;
+ return FALSE;
+ }
+
+ numRead = fread(&mBinormals[v].mV, sizeof(F32), 3, fp);
+ llendianswizzle(&mBinormals[v].mV, sizeof(F32), 3);
+ if (numRead != 3)
+ {
+ llwarns << "Can't read morph target binormal" << llendl;
+ return FALSE;
+ }
+
+
+ numRead = fread(&mTexCoords[v].mV, sizeof(F32), 2, fp);
+ llendianswizzle(&mTexCoords[v].mV, sizeof(F32), 2);
+ if (numRead != 2)
+ {
+ llwarns << "Can't read morph target uv" << llendl;
+ return FALSE;
+ }
+
+ mNumIndices++;
+ }
+
+ mAvgDistortion = mAvgDistortion * (1.f/(F32)mNumIndices);
+ mAvgDistortion.normVec();
+
+ gLoadedMorphs.addData(this);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo()
+//-----------------------------------------------------------------------------
+LLPolyMorphTargetInfo::LLPolyMorphTargetInfo()
+ : mIsClothingMorph(FALSE)
+{
+}
+
+BOOL LLPolyMorphTargetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_morph" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // Get mixed-case name
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mMorphName ) )
+ {
+ llwarns << "Avatar file: <param> is missing name attribute" << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+
+ static LLStdStringHandle clothing_morph_string = LLXmlTree::addAttributeString("clothing_morph");
+ node->getFastAttributeBOOL(clothing_morph_string, mIsClothingMorph);
+
+ LLXmlTreeNode *paramNode = node->getChildByName("param_morph");
+
+ for (LLXmlTreeNode* child_node = paramNode->getFirstChild();
+ child_node;
+ child_node = paramNode->getNextChild())
+ {
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (child_node->hasName("volume_morph"))
+ {
+ LLString volume_name;
+ if (child_node->getFastAttributeString(name_string, volume_name))
+ {
+ LLVector3 scale;
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ child_node->getFastAttributeVector3(scale_string, scale);
+
+ LLVector3 pos;
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ child_node->getFastAttributeVector3(pos_string, pos);
+
+ mVolumeInfoList.push_back(LLPolyVolumeMorphInfo(volume_name,scale,pos));
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::LLPolyMorphTarget(LLPolyMesh *poly_mesh)
+ : mMorphData(NULL), mMesh(poly_mesh),
+ mVertMask(NULL),
+ mLastSex(SEX_FEMALE),
+ mNumMorphMasksPending(0)
+{
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyMorphTarget()
+//-----------------------------------------------------------------------------
+LLPolyMorphTarget::~LLPolyMorphTarget()
+{
+ delete mVertMask;
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::setInfo(LLPolyMorphTargetInfo* info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+
+ LLVOAvatar* avatarp = mMesh->getAvatar();
+ LLPolyMorphTargetInfo::volume_info_list_t::iterator iter;
+ for (iter = getInfo()->mVolumeInfoList.begin(); iter != getInfo()->mVolumeInfoList.end(); iter++)
+ {
+ LLPolyVolumeMorphInfo *volume_info = &(*iter);
+ std::string vol_string(volume_info->mName);
+ for (S32 i = 0; i < avatarp->mNumCollisionVolumes; i++)
+ {
+ if (avatarp->mCollisionVolumes[i].getName() == vol_string)
+ {
+ mVolumeMorphs.push_back(LLPolyVolumeMorph(&avatarp->mCollisionVolumes[i],
+ volume_info->mScale,
+ volume_info->mPos));
+ break;
+ }
+ }
+ }
+
+ mMorphData = mMesh->getMorphData(getInfo()->mMorphName.c_str());
+ if (!mMorphData)
+ {
+ llwarns << "No morph target named " << getInfo()->mMorphName << " found in mesh." << llendl;
+ return FALSE; // Continue, ignoring this tag
+ }
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLPolyMorphTarget::parseData(LLXmlTreeNode* node)
+{
+ LLPolyMorphTargetInfo* info = new LLPolyMorphTargetInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// getVertexDistortion()
+//-----------------------------------------------------------------------------
+LLVector3 LLPolyMorphTarget::getVertexDistortion(S32 requested_index, LLPolyMesh *mesh)
+{
+ if (!mMorphData || mMesh != mesh) return LLVector3::zero;
+
+ for(U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ if (mMorphData->mVertexIndices[index] == (U32)requested_index)
+ {
+ return mMorphData->mCoords[index];
+ }
+ }
+
+ return LLVector3::zero;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMorphTarget::getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector3::zero;
+
+ LLVector3* resultVec;
+ mMorphData->mCurrentIndex = 0;
+ if (mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getNextDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3 *LLPolyMorphTarget::getNextDistortion(U32 *index, LLPolyMesh **poly_mesh)
+{
+ if (!mMorphData) return &LLVector3::zero;
+
+ LLVector3* resultVec;
+ mMorphData->mCurrentIndex++;
+ if (mMorphData->mCurrentIndex < mMorphData->mNumIndices)
+ {
+ resultVec = &mMorphData->mCoords[mMorphData->mCurrentIndex];
+ if (index != NULL)
+ {
+ *index = mMorphData->mVertexIndices[mMorphData->mCurrentIndex];
+ }
+ if (poly_mesh != NULL)
+ {
+ *poly_mesh = mMesh;
+ }
+ return resultVec;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getTotalDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getTotalDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mTotalDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getAvgDistortion()
+//-----------------------------------------------------------------------------
+const LLVector3& LLPolyMorphTarget::getAvgDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mAvgDistortion;
+ }
+ else
+ {
+ return LLVector3::zero;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getMaxDistortion()
+//-----------------------------------------------------------------------------
+F32 LLPolyMorphTarget::getMaxDistortion()
+{
+ if (mMorphData)
+ {
+ return mMorphData->mMaxDistortion;
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// apply()
+//-----------------------------------------------------------------------------
+void LLPolyMorphTarget::apply( ESex avatar_sex )
+{
+ if (!mMorphData || mNumMorphMasksPending > 0)
+ {
+ return;
+ }
+
+ mLastSex = avatar_sex;
+
+ // perform differential update of morph
+ F32 delta_weight = ( getSex() & avatar_sex ) ? (mCurWeight - mLastWeight) : (getDefaultWeight() - mLastWeight);
+ // store last weight
+ mLastWeight += delta_weight;
+
+ if (delta_weight != 0.f)
+ {
+ llassert(!mMesh->isLOD());
+ LLVector3 *coords = mMesh->getWritableCoords();
+
+ LLVector3 *scaled_normals = mMesh->getScaledNormals();
+ LLVector3 *normals = mMesh->getWritableNormals();
+
+ LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector3 *binormals = mMesh->getWritableBinormals();
+
+ LLVector4 *clothing_weights = mMesh->getWritableClothingWeights();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ F32 *maskWeightArray = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ for(U32 vert_index_morph = 0; vert_index_morph < mMorphData->mNumIndices; vert_index_morph++)
+ {
+ S32 vert_index_mesh = mMorphData->mVertexIndices[vert_index_morph];
+
+ F32 maskWeight = 1.f;
+ if (maskWeightArray)
+ {
+ maskWeight = maskWeightArray[vert_index_morph];
+ }
+
+ coords[vert_index_mesh] += mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
+ if (getInfo()->mIsClothingMorph && clothing_weights)
+ {
+ LLVector3 clothing_offset = mMorphData->mCoords[vert_index_morph] * delta_weight * maskWeight;
+ LLVector4* clothing_weight = &clothing_weights[vert_index_mesh];
+ clothing_weight->mV[VX] += clothing_offset.mV[VX];
+ clothing_weight->mV[VY] += clothing_offset.mV[VY];
+ clothing_weight->mV[VZ] += clothing_offset.mV[VZ];
+ clothing_weight->mV[VW] = maskWeight;
+ }
+
+ // calculate new normals based on half angles
+ scaled_normals[vert_index_mesh] += mMorphData->mNormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
+ LLVector3 normalized_normal = scaled_normals[vert_index_mesh];
+ normalized_normal.normVec();
+ normals[vert_index_mesh] = normalized_normal;
+
+ // calculate new binormals
+ scaled_binormals[vert_index_mesh] += mMorphData->mBinormals[vert_index_morph] * delta_weight * maskWeight * NORMAL_SOFTEN_FACTOR;
+ LLVector3 tangent = scaled_binormals[vert_index_mesh] % normalized_normal;
+ LLVector3 normalized_binormal = normalized_normal % tangent;
+ normalized_binormal.normVec();
+ binormals[vert_index_mesh] = normalized_binormal;
+
+ tex_coords[vert_index_mesh] += mMorphData->mTexCoords[vert_index_morph] * delta_weight * maskWeight;
+ }
+
+ // now apply volume changes
+ for( volume_list_t::iterator iter = mVolumeMorphs.begin(); iter != mVolumeMorphs.end(); iter++ )
+ {
+ LLPolyVolumeMorph* volume_morph = &(*iter);
+ LLVector3 scale_delta = volume_morph->mScale * delta_weight;
+ LLVector3 pos_delta = volume_morph->mPos * delta_weight;
+
+ volume_morph->mVolume->setScale(volume_morph->mVolume->getScale() + scale_delta);
+ volume_morph->mVolume->setPosition(volume_morph->mVolume->getPosition() + pos_delta);
+ }
+ }
+
+ if (mNext)
+ {
+ mNext->apply(avatar_sex);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// applyMask()
+//-----------------------------------------------------------------------------
+void LLPolyMorphTarget::applyMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert)
+{
+ LLVector4 *clothing_weights = getInfo()->mIsClothingMorph ? mMesh->getWritableClothingWeights() : NULL;
+
+ if (!mVertMask)
+ {
+ mVertMask = new LLPolyVertexMask(mMorphData);
+ mNumMorphMasksPending--;
+ }
+ else
+ {
+ // remove effect of previous mask
+ F32 *maskWeights = (mVertMask) ? mVertMask->getMorphMaskWeights() : NULL;
+
+ if (maskWeights)
+ {
+ LLVector3 *coords = mMesh->getWritableCoords();
+ LLVector3 *scaled_normals = mMesh->getScaledNormals();
+ LLVector3 *scaled_binormals = mMesh->getScaledBinormals();
+ LLVector2 *tex_coords = mMesh->getWritableTexCoords();
+
+ for(U32 vert = 0; vert < mMorphData->mNumIndices; vert++)
+ {
+ F32 lastMaskWeight = mLastWeight * maskWeights[vert];
+ S32 out_vert = mMorphData->mVertexIndices[vert];
+
+ // remove effect of existing masked morph
+ coords[out_vert] -= mMorphData->mCoords[vert] * lastMaskWeight;
+ scaled_normals[out_vert] -= mMorphData->mNormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
+ scaled_binormals[out_vert] -= mMorphData->mBinormals[vert] * lastMaskWeight * NORMAL_SOFTEN_FACTOR;
+ tex_coords[out_vert] -= mMorphData->mTexCoords[vert] * lastMaskWeight;
+
+ if (clothing_weights)
+ {
+ LLVector3 clothing_offset = mMorphData->mCoords[vert] * lastMaskWeight;
+ LLVector4* clothing_weight = &clothing_weights[out_vert];
+ clothing_weight->mV[VX] -= clothing_offset.mV[VX];
+ clothing_weight->mV[VY] -= clothing_offset.mV[VY];
+ clothing_weight->mV[VZ] -= clothing_offset.mV[VZ];
+ }
+ }
+ }
+ }
+
+ // set last weight to 0, since we've removed the effect of this morph
+ mLastWeight = 0.f;
+
+ mVertMask->generateMask(maskTextureData, width, height, num_components, invert, clothing_weights);
+
+ apply(mLastSex);
+}
+
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::LLPolyVertexMask(LLPolyMorphData* morph_data)
+{
+ mWeights = new F32[morph_data->mNumIndices];
+ mMorphData = morph_data;
+ mWeightsGenerated = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+LLPolyVertexMask::~LLPolyVertexMask()
+{
+ delete[] mWeights;
+}
+
+//-----------------------------------------------------------------------------
+// generateMask()
+//-----------------------------------------------------------------------------
+void LLPolyVertexMask::generateMask(U8 *maskTextureData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4 *clothing_weights)
+{
+// RN debug output that uses Image Debugger (http://www.cs.unc.edu/~baxter/projects/imdebug/)
+// BOOL debugImg = FALSE;
+// if (debugImg)
+// {
+// if (invert)
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d *-1 %p", width, height, maskTextureData);
+// }
+// else
+// {
+// imdebug("lum rbga=rgba b=8 w=%d h=%d %p", width, height, maskTextureData);
+// }
+// }
+ for (U32 index = 0; index < mMorphData->mNumIndices; index++)
+ {
+ S32 vertIndex = mMorphData->mVertexIndices[index];
+ const S32 *sharedVertIndex = mMorphData->mMesh->getSharedVert(vertIndex);
+ LLVector2 uvCoords;
+
+ if (sharedVertIndex)
+ {
+ uvCoords = mMorphData->mMesh->getUVs(*sharedVertIndex);
+ }
+ else
+ {
+ uvCoords = mMorphData->mMesh->getUVs(vertIndex);
+ }
+ U32 s = llclamp((U32)(uvCoords.mV[VX] * (F32)(width - 1)), (U32)0, (U32)width - 1);
+ U32 t = llclamp((U32)(uvCoords.mV[VY] * (F32)(height - 1)), (U32)0, (U32)height - 1);
+
+ mWeights[index] = ((F32) maskTextureData[((t * width + s) * num_components) + (num_components - 1)]) / 255.f;
+
+ if (invert)
+ {
+ mWeights[index] = 1.f - mWeights[index];
+ }
+
+ // now apply step function
+ // mWeights[index] = mWeights[index] > 0.95f ? 1.f : 0.f;
+
+ if (clothing_weights)
+ {
+ clothing_weights[vertIndex].mV[VW] = mWeights[index];
+ }
+ }
+ mWeightsGenerated = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getMaskForMorphIndex()
+//-----------------------------------------------------------------------------
+F32* LLPolyVertexMask::getMorphMaskWeights()
+{
+ if (!mWeightsGenerated)
+ {
+ return NULL;
+ }
+
+ return mWeights;
+}
diff --git a/indra/newview/llpolymorph.h b/indra/newview/llpolymorph.h
new file mode 100644
index 0000000000..e5e16eb0ee
--- /dev/null
+++ b/indra/newview/llpolymorph.h
@@ -0,0 +1,163 @@
+/**
+ * @file llpolymorph.h
+ * @brief Implementation of LLPolyMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPOLYMORPH_H
+#define LL_LLPOLYMORPH_H
+
+#include <string>
+#include <vector>
+
+#include "llviewervisualparam.h"
+#include "llskiplist.h"
+#include "linked_lists.h"
+
+class LLPolyMeshSharedData;
+class LLVOAvatar;
+class LLVector2;
+class LLViewerJointCollisionVolume;
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphData()
+//-----------------------------------------------------------------------------
+class LLPolyMorphData
+{
+public:
+ LLPolyMorphData(char *morph_name);
+ ~LLPolyMorphData();
+
+ BOOL loadBinary(FILE* fp, LLPolyMeshSharedData *mesh);
+ char* loadASCII(char* text, LLPolyMeshSharedData *mesh);
+ char* getName() { return mName; }
+
+public:
+ char* mName;
+
+ // morphology
+ U32 mNumIndices;
+ U32* mVertexIndices;
+ U32 mCurrentIndex;
+ LLVector3* mCoords;
+ LLVector3* mNormals;
+ LLVector3* mBinormals;
+ LLVector2* mTexCoords;
+
+ F32 mTotalDistortion; // vertex distortion summed over entire morph
+ F32 mMaxDistortion; // maximum single vertex distortion in a given morph
+ LLVector3 mAvgDistortion; // average vertex distortion, to infer directionality of the morph
+ LLPolyMeshSharedData* mMesh;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyVertexMask()
+//-----------------------------------------------------------------------------
+class LLPolyVertexMask
+{
+public:
+ LLPolyVertexMask(LLPolyMorphData* morph_data);
+ ~LLPolyVertexMask();
+
+ void generateMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert, LLVector4 *clothing_weights);
+ F32* getMorphMaskWeights();
+
+
+protected:
+ F32* mWeights;
+ LLPolyMorphData *mMorphData;
+ BOOL mWeightsGenerated;
+
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget Data structs
+//-----------------------------------------------------------------------------
+struct LLPolyVolumeMorphInfo
+{
+ LLPolyVolumeMorphInfo(LLString &name, LLVector3 &scale, LLVector3 &pos)
+ : mName(name), mScale(scale), mPos(pos) {};
+
+ LLString mName;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+struct LLPolyVolumeMorph
+{
+ LLPolyVolumeMorph(LLViewerJointCollisionVolume* volume, LLVector3 scale, LLVector3 pos)
+ : mVolume(volume), mScale(scale), mPos(pos) {};
+
+ LLViewerJointCollisionVolume* mVolume;
+ LLVector3 mScale;
+ LLVector3 mPos;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTargetInfo
+// Shared information for LLPolyMorphTargets
+//-----------------------------------------------------------------------------
+class LLPolyMorphTargetInfo : public LLViewerVisualParamInfo
+{
+ friend class LLPolyMorphTarget;
+public:
+ LLPolyMorphTargetInfo();
+ /*virtual*/ ~LLPolyMorphTargetInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mMorphName;
+ BOOL mIsClothingMorph;
+ typedef std::vector<LLPolyVolumeMorphInfo> volume_info_list_t;
+ volume_info_list_t mVolumeInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLPolyMorphTarget
+// A set of vertex data associated with morph target.
+// These morph targets must be topologically consistent with a given Polymesh
+// (share face sets)
+//-----------------------------------------------------------------------------
+class LLPolyMorphTarget : public LLViewerVisualParam
+{
+public:
+ LLPolyMorphTarget(LLPolyMesh *poly_mesh);
+ ~LLPolyMorphTarget();
+
+ // Special: These functions are overridden by child classes
+ LLPolyMorphTargetInfo* getInfo() const { return (LLPolyMorphTargetInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLPolyMorphTargetInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex sex );
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion();
+ /*virtual*/ const LLVector3& getAvgDistortion();
+ /*virtual*/ F32 getMaxDistortion();
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh);
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh);
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh);
+
+ void applyMask(U8 *maskData, S32 width, S32 height, S32 num_components, BOOL invert);
+ void addPendingMorphMask() { mNumMorphMasksPending++; }
+
+protected:
+ LLPolyMorphData* mMorphData;
+ LLPolyMesh* mMesh;
+ LLPolyVertexMask * mVertMask;
+ ESex mLastSex;
+ // number of morph masks that haven't been generated, must be 0 before this morph is applied
+ BOOL mNumMorphMasksPending;
+
+ typedef std::vector<LLPolyVolumeMorph> volume_list_t;
+ volume_list_t mVolumeMorphs;
+
+};
+
+#endif // LL_LLPOLYMORPH_H
diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp
new file mode 100644
index 0000000000..5cc2d2e39f
--- /dev/null
+++ b/indra/newview/llpreview.cpp
@@ -0,0 +1,489 @@
+/**
+ * @file llpreview.cpp
+ * @brief LLPreview class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "stdenums.h"
+
+#include "llpreview.h"
+#include "lllineeditor.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llfocusmgr.h"
+#include "lltooldraganddrop.h"
+#include "llradiogroup.h"
+#include "llassetstorage.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "lldbstrings.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "llselectmgr.h"
+#include "llinventoryview.h"
+#include "llviewerinventory.h"
+
+// Constants
+
+// Globals and statics
+LLPreview::preview_multimap_t LLPreview::sPreviewsBySource;
+LLPreview::preview_map_t LLPreview::sInstances;
+
+// Functions
+LLPreview::LLPreview(const std::string& name) :
+ LLFloater(name),
+ mCopyToInvBtn(NULL),
+ mForceClose(FALSE),
+ mCloseAfterSave(FALSE),
+ mAssetStatus(PREVIEW_ASSET_UNLOADED)
+{
+ // don't add to instance list, since ItemID is null
+ mAuxItem = new LLInventoryItem; // (LLPointer is auto-deleted)
+ // don't necessarily steal focus on creation -- sometimes these guys pop up without user action
+ mAutoFocus = FALSE;
+}
+
+LLPreview::LLPreview(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid, BOOL allow_resize, S32 min_width, S32 min_height )
+: LLFloater(name, rect, title, allow_resize, min_width, min_height ),
+ mItemUUID(item_uuid),
+ mSourceID(LLUUID::null),
+ mObjectUUID(object_uuid),
+ mCopyToInvBtn( NULL ),
+ mForceClose( FALSE ),
+ mCloseAfterSave(FALSE),
+ mAssetStatus(PREVIEW_ASSET_UNLOADED)
+{
+ mAuxItem = new LLInventoryItem;
+ // don't necessarily steal focus on creation -- sometimes these guys pop up without user action
+ mAutoFocus = FALSE;
+
+ if (mItemUUID.notNull())
+ {
+ sInstances[mItemUUID] = this;
+ }
+
+}
+
+LLPreview::~LLPreview()
+{
+ gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit()
+
+ if (mItemUUID.notNull())
+ {
+ sInstances.erase( mItemUUID );
+ }
+
+ if (mSourceID.notNull())
+ {
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(mSourceID);
+ for (; found_it != sPreviewsBySource.end(); ++found_it)
+ {
+ if (found_it->second == mViewHandle)
+ {
+ sPreviewsBySource.erase(found_it);
+ break;
+ }
+ }
+ }
+}
+
+void LLPreview::setItemID(const LLUUID& item_id)
+{
+ if (mItemUUID.notNull())
+ {
+ sInstances.erase(mItemUUID);
+ }
+
+ mItemUUID = item_id;
+
+ if (mItemUUID.notNull())
+ {
+ sInstances[mItemUUID] = this;
+ }
+}
+
+void LLPreview::setObjectID(const LLUUID& object_id)
+{
+ mObjectUUID = object_id;
+}
+
+void LLPreview::setSourceID(const LLUUID& source_id)
+{
+ if (mSourceID.notNull())
+ {
+ // erase old one
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(mSourceID);
+ for (; found_it != sPreviewsBySource.end(); ++found_it)
+ {
+ if (found_it->second == mViewHandle)
+ {
+ sPreviewsBySource.erase(found_it);
+ break;
+ }
+ }
+ }
+ mSourceID = source_id;
+ sPreviewsBySource.insert(preview_multimap_t::value_type(mSourceID, mViewHandle));
+}
+
+LLViewerInventoryItem* LLPreview::getItem() const
+{
+ LLViewerInventoryItem* item = NULL;
+ if(mObjectUUID.isNull())
+ {
+ // it's an inventory item, so get the item.
+ item = gInventory.getItem(mItemUUID);
+ }
+ else
+ {
+ // it's an object's inventory item.
+ LLViewerObject* object = gObjectList.findObject(mObjectUUID);
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(mItemUUID);
+ }
+ }
+ return item;
+}
+
+// Sub-classes should override this function if they allow editing
+void LLPreview::onCommit()
+{
+ LLViewerInventoryItem* item = getItem();
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ BOOL has_sale_info = FALSE;
+ LLSaleInfo sale_info;
+ new_item->setDescription(childGetText("desc"));
+ if(mObjectUUID.notNull())
+ {
+ // must be in an object
+ LLViewerObject* object = gObjectList.findObject(mObjectUUID);
+ if(object)
+ {
+ object->updateInventory(
+ new_item,
+ TASK_INVENTORY_ITEM_KEY,
+ false);
+ }
+ }
+ else if(item->getPermissions().getOwner() == gAgent.getID())
+ {
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+
+ // If the item is an attachment that is currently being worn,
+ // update the object itself.
+ if( item->getType() == LLAssetType::AT_OBJECT )
+ {
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ LLViewerObject* obj = avatar->getWornAttachment( item->getUUID() );
+ if( obj )
+ {
+ gSelectMgr->deselectAll();
+ gSelectMgr->addAsIndividual( obj, SELECT_ALL_TES, FALSE );
+ gSelectMgr->setObjectDescription( childGetText("desc") );
+
+ if( has_sale_info )
+ {
+ gSelectMgr->setObjectSaleInfo( sale_info );
+ }
+
+ gSelectMgr->deselectAll();
+ }
+ }
+ }
+ }
+ }
+}
+
+// static
+void LLPreview::onText(LLUICtrl*, void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ self->onCommit();
+}
+
+// static
+void LLPreview::onRadio(LLUICtrl*, void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ self->onCommit();
+}
+
+// static
+LLPreview* LLPreview::find(const LLUUID& item_uuid)
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return instance;
+}
+
+// static
+LLPreview* LLPreview::show( const LLUUID& item_uuid, BOOL take_focus )
+{
+ LLPreview* instance = LLPreview::find(item_uuid);
+ if(instance)
+ {
+ if (LLFloater::getFloaterHost() && LLFloater::getFloaterHost() != instance->getHost())
+ {
+ // this preview window is being opened in a new context
+ // needs to be rehosted
+ LLFloater::getFloaterHost()->addFloater(instance, TRUE);
+ }
+ instance->open();
+ if (take_focus)
+ {
+ instance->setFocus(TRUE);
+ }
+ }
+
+ return instance;
+}
+
+// static
+bool LLPreview::save( const LLUUID& item_uuid, LLPointer<LLInventoryItem>* itemptr )
+{
+ bool res = false;
+ LLPreview* instance = LLPreview::find(item_uuid);
+ if(instance)
+ {
+ res = instance->saveItem(itemptr);
+ }
+ if (!res)
+ {
+ delete itemptr;
+ }
+ return res;
+}
+
+// static
+void LLPreview::hide(const LLUUID& item_uuid)
+{
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ LLPreview* instance = found_it->second;
+ if( instance->getParent() )
+ {
+ instance->getParent()->removeChild( instance );
+ }
+
+ delete instance;
+ }
+}
+
+// static
+void LLPreview::rename(const LLUUID& item_uuid, const std::string& new_name)
+{
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ LLPreview* instance = found_it->second;
+ instance->setTitle( new_name );
+ }
+}
+
+BOOL LLPreview::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if(mClientRect.pointInRect(x, y))
+ {
+ // No handler needed for focus lost since this class has no
+ // state that depends on it.
+ bringToFront(x, y);
+ gFocusMgr.setMouseCapture(this, NULL);
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart(screen_x, screen_y);
+ return TRUE;
+ }
+ return LLFloater::handleMouseDown(x, y, mask);
+}
+
+BOOL LLPreview::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if(gFocusMgr.getMouseCapture() == this)
+ {
+ gFocusMgr.setMouseCapture(NULL, NULL);
+ return TRUE;
+ }
+ return LLFloater::handleMouseUp(x, y, mask);
+}
+
+BOOL LLPreview::handleHover(S32 x, S32 y, MASK mask)
+{
+ if(gFocusMgr.getMouseCapture() == this)
+ {
+ S32 screen_x;
+ S32 screen_y;
+ LLViewerInventoryItem *item = getItem();
+
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ if(item
+ && item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ && gToolDragAndDrop->isOverThreshold(screen_x, screen_y))
+ {
+ EDragAndDropType type;
+ type = LLAssetType::lookupDragAndDropType(item->getType());
+ LLToolDragAndDrop::ESource src = LLToolDragAndDrop::SOURCE_LIBRARY;
+ if(!mObjectUUID.isNull())
+ {
+ src = LLToolDragAndDrop::SOURCE_WORLD;
+ }
+ else if(item->getPermissions().getOwner() == gAgent.getID())
+ {
+ src = LLToolDragAndDrop::SOURCE_AGENT;
+ }
+ gToolDragAndDrop->beginDrag(type,
+ item->getUUID(),
+ src,
+ mObjectUUID);
+ return gToolDragAndDrop->handleHover(x, y, mask );
+ }
+ }
+ return LLFloater::handleHover(x,y,mask);
+}
+
+void LLPreview::open()
+{
+ LLMultiFloater* hostp = getHost();
+ if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
+ {
+ loadAsset();
+ }
+ LLFloater::open();
+}
+
+// virtual
+bool LLPreview::saveItem(LLPointer<LLInventoryItem>* itemptr)
+{
+ return false;
+}
+
+
+// static
+void LLPreview::onBtnCopyToInv(void* userdata)
+{
+ LLPreview* self = (LLPreview*) userdata;
+ LLInventoryItem *item = self->mAuxItem;
+
+ if(item && item->getUUID().notNull())
+ {
+ // Copy to inventory
+ if (self->mNotecardInventoryID.notNull())
+ {
+ copy_inventory_from_notecard(self->mObjectID,
+ self->mNotecardInventoryID, item);
+ }
+ else
+ {
+ LLPointer<LLInventoryCallback> cb = NULL;
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ }
+}
+
+// static
+void LLPreview::onKeepBtn(void* data)
+{
+ LLPreview* self = (LLPreview*)data;
+ self->close();
+}
+
+// static
+void LLPreview::onDiscardBtn(void* data)
+{
+ LLPreview* self = (LLPreview*)data;
+
+ LLViewerInventoryItem* item = self->getItem();
+ if (!item) return;
+
+ self->mForceClose = TRUE;
+ self->close();
+
+ // Delete the item entirely
+ /*
+ item->removeFromServer();
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ */
+
+ // Move the item to the trash
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if (item->getParentUUID() != trash_id)
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(item->getParentUUID(),-1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setParent(trash_id);
+ // no need to restamp it though it's a move into trash because
+ // it's a brand new item already.
+ new_item->updateParentOnServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+}
+
+//static
+LLPreview* LLPreview::getFirstPreviewForSource(const LLUUID& source_id)
+{
+ preview_multimap_t::iterator found_it = sPreviewsBySource.find(source_id);
+ if (found_it != sPreviewsBySource.end())
+ {
+ // just return first one
+ return (LLPreview*)LLFloater::getFloaterByHandle(found_it->second);
+ }
+ return NULL;
+}
+
+//
+// LLMultiPreview
+//
+
+LLMultiPreview::LLMultiPreview(const LLRect& rect) : LLMultiFloater("Preview", rect)
+{
+}
+
+void LLMultiPreview::open()
+{
+ LLMultiFloater::open();
+ LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel();
+ if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
+ {
+ frontmost_preview->loadAsset();
+ }
+}
+
+void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click)
+{
+ LLPreview* opened_preview = (LLPreview*)opened_floater;
+ if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED)
+ {
+ opened_preview->loadAsset();
+ }
+}
diff --git a/indra/newview/llpreview.h b/indra/newview/llpreview.h
new file mode 100644
index 0000000000..eb82965cd6
--- /dev/null
+++ b/indra/newview/llpreview.h
@@ -0,0 +1,139 @@
+/**
+ * @file llpreview.h
+ * @brief LLPreview class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEW_H
+#define LL_LLPREVIEW_H
+
+#include "llfloater.h"
+#include "llresizehandle.h"
+#include "llmap.h"
+#include "lluuid.h"
+#include "llviewerinventory.h"
+#include "lltabcontainer.h"
+#include "lllineeditor.h"
+
+class LLLineEditor;
+class LLRadioGroup;
+class LLPreview;
+
+class LLMultiPreview : public LLMultiFloater
+{
+public:
+ LLMultiPreview(const LLRect& rect);
+
+ /*virtual*/void open();
+ /*virtual*/void tabOpen(LLFloater* opened_floater, bool from_click);
+};
+
+class LLPreview : public LLFloater
+{
+public:
+ typedef enum e_asset_status
+ {
+ PREVIEW_ASSET_ERROR,
+ PREVIEW_ASSET_UNLOADED,
+ PREVIEW_ASSET_LOADING,
+ PREVIEW_ASSET_LOADED
+ } EAssetStatus;
+public:
+ // Used for XML-based construction.
+ LLPreview(const std::string& name);
+ LLPreview(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid, BOOL allow_resize = FALSE, S32 min_width = 0, S32 min_height = 0 );
+ virtual ~LLPreview();
+
+ void setItemID(const LLUUID& item_id);
+ void setObjectID(const LLUUID& object_id);
+ void setSourceID(const LLUUID& source_id);
+ LLViewerInventoryItem* getItem() const;
+
+ static LLPreview* find(const LLUUID& item_uuid);
+ static LLPreview* show(const LLUUID& item_uuid, BOOL take_focus = TRUE );
+ static void hide(const LLUUID& item_uuid);
+ static void rename(const LLUUID& item_uuid, const std::string& new_name);
+ static bool save(const LLUUID& item_uuid, LLPointer<LLInventoryItem>* itemptr);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void open();
+ virtual bool saveItem(LLPointer<LLInventoryItem>* itemptr);
+
+ void setAuxItem( const LLInventoryItem* item )
+ {
+ if ( mAuxItem )
+ mAuxItem->copy(item);
+ }
+
+ static void onBtnCopyToInv(void* userdata);
+
+ void addKeepDiscardButtons();
+ static void onKeepBtn(void* data);
+ static void onDiscardBtn(void* data);
+
+ virtual void loadAsset() { mAssetStatus = PREVIEW_ASSET_LOADED; }
+ virtual EAssetStatus getAssetStatus() { return mAssetStatus;}
+
+ static LLPreview* getFirstPreviewForSource(const LLUUID& source_id);
+ void setNotecardInfo(const LLUUID& notecard_inv_id, const LLUUID& object_id)
+ { mNotecardInventoryID = notecard_inv_id; mObjectID = object_id; }
+
+protected:
+ virtual void onCommit();
+
+ void addDescriptionUI();
+
+ static void onText(LLUICtrl*, void* userdata);
+ static void onRadio(LLUICtrl*, void* userdata);
+
+
+protected:
+ LLUUID mItemUUID;
+ LLUUID mSourceID;
+
+ // mObjectID will have a value if it is associated with a task in
+ // the world, and will be == LLUUID::null if it's in the agent
+ // inventory.
+ LLUUID mObjectUUID;
+
+ LLRect mClientRect;
+
+ LLPointer<LLInventoryItem> mAuxItem; // HACK!
+ LLButton* mCopyToInvBtn;
+
+ // Close without saving changes
+ BOOL mForceClose;
+
+ // When closing springs a "Want to save?" dialog, we want
+ // to keep the preview open until the save completes.
+ BOOL mCloseAfterSave;
+
+ EAssetStatus mAssetStatus;
+
+ typedef std::map<LLUUID, LLPreview*> preview_map_t;
+ typedef std::multimap<LLUUID, LLViewHandle> preview_multimap_t;
+
+ static preview_multimap_t sPreviewsBySource;
+ static preview_map_t sInstances;
+ LLUUID mNotecardInventoryID;
+ LLUUID mObjectID;
+};
+
+
+const S32 PREVIEW_BORDER = 4;
+const S32 PREVIEW_PAD = 5;
+const S32 PREVIEW_BUTTON_WIDTH = 100;
+
+const S32 PREVIEW_LINE_HEIGHT = 19;
+const S32 PREVIEW_CLOSE_BOX_SIZE = 16;
+const S32 PREVIEW_BORDER_WIDTH = 2;
+const S32 PREVIEW_RESIZE_HANDLE_SIZE = S32(RESIZE_HANDLE_WIDTH * OO_SQRT2) + PREVIEW_BORDER_WIDTH;
+const S32 PREVIEW_VPAD = 2;
+const S32 PREVIEW_HPAD = PREVIEW_RESIZE_HANDLE_SIZE;
+const S32 PREVIEW_HEADER_SIZE = 2*PREVIEW_LINE_HEIGHT + 2 * PREVIEW_VPAD;
+
+#endif // LL_LLPREVIEW_H
diff --git a/indra/newview/llpreviewanim.cpp b/indra/newview/llpreviewanim.cpp
new file mode 100644
index 0000000000..25273d035d
--- /dev/null
+++ b/indra/newview/llpreviewanim.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llpreviewanim.cpp
+ * @brief LLPreviewAnim class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewanim.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llinventory.h"
+#include "llinventoryview.h"
+#include "llvoavatar.h"
+#include "llagent.h" // gAgent
+#include "llkeyframemotion.h"
+#include "llfilepicker.h"
+#include "lllineeditor.h"
+#include "lluictrlfactory.h"
+#include "llvieweruictrlfactory.h"
+
+extern LLAgent gAgent;
+
+LLPreviewAnim::LLPreviewAnim(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const S32& activate, const LLUUID& object_uuid ) :
+ LLPreview( name, rect, title, item_uuid, object_uuid)
+{
+ gUICtrlFactory->buildFloater(this,"floater_preview_animation.xml");
+
+ childSetAction("Anim play btn",playAnim,this);
+ childSetAction("Anim audition btn",auditionAnim,this);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ // preload the animation
+ if(item)
+ {
+ gAgent.getAvatarObject()->createMotion(item->getAssetUUID());
+ }
+
+ switch ( activate )
+ {
+ case 1:
+ {
+ playAnim( (void *) this );
+ break;
+ }
+ case 2:
+ {
+ auditionAnim( (void *) this );
+ break;
+ }
+ default:
+ {
+ //do nothing
+ }
+ }
+}
+
+// static
+void LLPreviewAnim::endAnimCallback( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+
+ self->childSetValue("Anim play btn", FALSE);
+ self->childSetValue("Anim audition btn", FALSE);
+
+}
+
+// static
+void LLPreviewAnim::playAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLUUID itemID=item->getAssetUUID();
+
+ LLButton* btn = LLUICtrlFactory::getButtonByName(self, "Anim play btn");
+ if (btn)
+ {
+ btn->toggleState();
+ }
+
+ if (self->childGetValue("Anim play btn").asBoolean() )
+ {
+ self->mPauseRequest = NULL;
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_START);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(itemID);
+
+ if (motion)
+ motion->setDeactivateCallback(&endAnimCallback, (void *)self);
+ }
+ else
+ {
+ gAgent.getAvatarObject()->stopMotion(itemID);
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_STOP);
+ }
+ }
+}
+
+// static
+void LLPreviewAnim::auditionAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLUUID itemID=item->getAssetUUID();
+
+ LLButton* btn = LLUICtrlFactory::getButtonByName(self, "Anim audition btn");
+ if (btn)
+ {
+ btn->toggleState();
+ }
+
+ if (self->childGetValue("Anim audition btn").asBoolean() )
+ {
+ self->mPauseRequest = NULL;
+ gAgent.getAvatarObject()->startMotion(item->getAssetUUID());
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(itemID);
+
+ if (motion)
+ motion->setDeactivateCallback(&endAnimCallback, (void *)self);
+ }
+ else
+ {
+ gAgent.getAvatarObject()->stopMotion(itemID);
+ gAgent.sendAnimationRequest(itemID, ANIM_REQUEST_STOP);
+ }
+ }
+}
+
+void LLPreviewAnim::saveAnim( void *userdata )
+{
+ LLPreviewAnim* self = (LLPreviewAnim*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item)
+ {
+ LLKeyframeMotion* motionp = (LLKeyframeMotion*)gAgent.getAvatarObject()->createMotion( item->getAssetUUID() );
+ if (motionp && motionp->isLoaded())
+ {
+ LLFilePicker& picker = LLFilePicker::instance();
+ LLString proposed_name = item->getName() + LLString(".xaf");
+ if (picker.getSaveFile(LLFilePicker::FFSAVE_ANIM, proposed_name.c_str()))
+ {
+ apr_file_t* fp = ll_apr_file_open(picker.getFirstFile(), LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to open file " << picker.getFirstFile() << llendl;
+ return;
+ }
+ motionp->writeCAL3D(fp);
+ apr_file_close(fp);
+ }
+ }
+ }
+}
+
+void LLPreviewAnim::onClose(bool app_quitting)
+{
+ LLInventoryItem *item = getItem();
+
+ if(item)
+ {
+ gAgent.getAvatarObject()->stopMotion(item->getAssetUUID());
+ gAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLMotion* motion = avatar->findMotion(item->getAssetUUID());
+
+ if (motion)
+ motion->setDeactivateCallback(NULL, (void *)NULL);
+
+ }
+ destroy();
+}
diff --git a/indra/newview/llpreviewanim.h b/indra/newview/llpreviewanim.h
new file mode 100644
index 0000000000..8a85579bba
--- /dev/null
+++ b/indra/newview/llpreviewanim.h
@@ -0,0 +1,39 @@
+/**
+ * @file llpreviewanim.h
+ * @brief LLPreviewAnim class definition
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWANIM_H
+#define LL_LLPREVIEWANIM_H
+
+#include "llpreview.h"
+#include "llcharacter.h"
+
+class LLPreviewAnim : public LLPreview
+{
+public:
+ LLPreviewAnim(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid,
+ const S32& activate,
+ const LLUUID& object_uuid = LLUUID::null);
+
+ static void playAnim( void* userdata );
+ static void auditionAnim( void* userdata );
+ static void saveAnim( void* userdata );
+ static void endAnimCallback( void *userdata );
+
+protected:
+ virtual void onClose(bool app_quitting);
+
+ LLAnimPauseRequest mPauseRequest;
+ LLUUID mItemID;
+ LLString mTitle;
+ LLUUID mObjectID;
+ LLButton* mPlayBtn;
+ LLButton* mAuditionBtn;
+};
+
+#endif // LL_LLPREVIEWSOUND_H
diff --git a/indra/newview/llpreviewgesture.cpp b/indra/newview/llpreviewgesture.cpp
new file mode 100644
index 0000000000..9496b5df5f
--- /dev/null
+++ b/indra/newview/llpreviewgesture.cpp
@@ -0,0 +1,1698 @@
+/**
+ * @file llpreviewgesture.cpp
+ * @brief Editing UI for inventory-based gestures.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <algorithm>
+
+#include "llpreviewgesture.h"
+
+// libraries
+#include "lldatapacker.h"
+#include "lldarray.h"
+#include "llstring.h"
+#include "lldir.h"
+#include "llmultigesture.h"
+#include "llvfile.h"
+
+// newview
+#include "llagent.h" // todo: remove
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llfloatergesture.h" // for some label constants
+#include "llgesturemgr.h"
+#include "llinventorymodel.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llradiogroup.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h" // busycount
+#include "viewer.h" // gVFS
+
+#include "llresmgr.h"
+
+const char NONE_LABEL[] = "---";
+const char SHIFT_LABEL[] = "Shift";
+const char CTRL_LABEL[] = "Ctrl";
+
+void dialog_refresh_all();
+
+// used for getting
+
+class LLInventoryGestureAvailable : public LLInventoryCompletionObserver
+{
+public:
+ LLInventoryGestureAvailable() {}
+
+protected:
+ virtual void done();
+};
+
+void LLInventoryGestureAvailable::done()
+{
+ LLPreview* preview = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ for(; it < end; ++it)
+ {
+ preview = LLPreview::find((*it));
+ if(preview)
+ {
+ preview->refresh();
+ }
+ }
+ gInventory.removeObserver(this);
+ delete this;
+}
+
+// Used for sorting
+struct SortItemPtrsByName
+{
+ bool operator()(const LLInventoryItem* i1, const LLInventoryItem* i2)
+ {
+ return (LLString::compareDict(i1->getName(), i2->getName()) < 0);
+ }
+};
+
+// static
+LLPreviewGesture* LLPreviewGesture::show(const std::string& title, const LLUUID& item_id, const LLUUID& object_id, BOOL take_focus)
+{
+ LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(item_id);
+ if (previewp)
+ {
+ previewp->open();
+ if (take_focus)
+ {
+ previewp->setFocus(TRUE);
+ }
+ return previewp;
+ }
+
+ LLPreviewGesture* self = new LLPreviewGesture();
+
+ // Finish internal construction
+ self->init(item_id, object_id);
+
+ // Builds and adds to gFloaterView
+ gUICtrlFactory->buildFloater(self, "floater_preview_gesture.xml");
+ self->setTitle(title);
+
+ // Move window to top-left of screen
+ LLMultiFloater* hostp = self->getHost();
+ if (hostp == NULL)
+ {
+ LLRect r = self->getRect();
+ LLRect screen = gFloaterView->getRect();
+ r.setLeftTopAndSize(0, screen.getHeight(), r.getWidth(), r.getHeight());
+ self->setRect(r);
+ }
+ else
+ {
+ // re-add to host to update title
+ hostp->addFloater(self, TRUE);
+ }
+
+ // this will call refresh when we have everything.
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)self->getItem();
+ if(item && !item->isComplete())
+ {
+ LLInventoryGestureAvailable* observer;
+ observer = new LLInventoryGestureAvailable();
+ observer->watchItem(item_id);
+ gInventory.addObserver(observer);
+ item->fetchFromServer();
+ }
+ else
+ {
+ // not sure this is necessary.
+ self->refresh();
+ }
+
+ if (take_focus)
+ {
+ self->setFocus(TRUE);
+ }
+
+ return self;
+}
+
+
+// virtual
+BOOL LLPreviewGesture::handleKeyHere(KEY key, MASK mask,
+ BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ saveIfNeeded();
+ return TRUE;
+ }
+ }
+ return LLPreview::handleKeyHere(key, mask, called_from_parent);
+}
+
+
+// virtual
+BOOL LLPreviewGesture::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = TRUE;
+ switch(cargo_type)
+ {
+ case DAD_ANIMATION:
+ case DAD_SOUND:
+ {
+ // TODO: Don't allow this if you can't transfer the sound/animation
+
+ // make a script step
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+ if (item
+ && gInventory.getItem(item->getUUID()))
+ {
+ LLPermissions perm = item->getPermissions();
+ if (!((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED))
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only animations and sounds\n"
+ "with unrestricted permissions\n"
+ "can be added to a gesture.");
+ }
+ break;
+ }
+ else if (drop)
+ {
+ LLScrollListItem* line = NULL;
+ if (cargo_type == DAD_ANIMATION)
+ {
+ line = addStep("Animation");
+ LLGestureStepAnimation* anim = (LLGestureStepAnimation*)line->getUserdata();
+ anim->mAnimAssetID = item->getAssetUUID();
+ anim->mAnimName = item->getName();
+ }
+ else if (cargo_type == DAD_SOUND)
+ {
+ line = addStep("Sound");
+ LLGestureStepSound* sound = (LLGestureStepSound*)line->getUserdata();
+ sound->mSoundAssetID = item->getAssetUUID();
+ sound->mSoundName = item->getName();
+ }
+ updateLabel(line);
+ mDirty = TRUE;
+ refresh();
+ }
+ *accept = ACCEPT_YES_COPY_MULTI;
+ }
+ else
+ {
+ // Not in user's inventory means it was in object inventory
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+ default:
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only animations and sounds\n"
+ "can be added to a gesture.");
+ }
+ break;
+ }
+ return handled;
+}
+
+
+// virtual
+BOOL LLPreviewGesture::canClose()
+{
+ if(!mDirty)
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges",
+ handleSaveChangesDialog,
+ this );
+ return FALSE;
+ }
+}
+
+// virtual
+void LLPreviewGesture::onClose(bool app_quitting)
+{
+ gGestureManager.stopGesture(mPreviewGesture);
+ LLPreview::onClose(app_quitting);
+}
+
+// virtual
+void LLPreviewGesture::setMinimized(BOOL minimize)
+{
+ if (minimize != isMinimized())
+ {
+ LLFloater::setMinimized(minimize);
+
+ // We're being restored
+ if (!minimize)
+ {
+ refresh();
+ }
+ }
+}
+
+
+// static
+void LLPreviewGesture::handleSaveChangesDialog(S32 option, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ switch(option)
+ {
+ case 0: // "Yes"
+ gGestureManager.stopGesture(self->mPreviewGesture);
+ self->mCloseAfterSave = TRUE;
+ onClickSave(data);
+ break;
+
+ case 1: // "No"
+ gGestureManager.stopGesture(self->mPreviewGesture);
+ self->mDirty = FALSE; // Force the dirty flag because user has clicked NO on confirm save dialog...
+ self->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+
+LLPreviewGesture::LLPreviewGesture()
+: LLPreview("Gesture Preview"),
+ mTriggerEditor(NULL),
+ mModifierCombo(NULL),
+ mKeyCombo(NULL),
+ mLibraryList(NULL),
+ mAddBtn(NULL),
+ mUpBtn(NULL),
+ mDownBtn(NULL),
+ mDeleteBtn(NULL),
+ mStepList(NULL),
+ mOptionsText(NULL),
+ mAnimationRadio(NULL),
+ mAnimationCombo(NULL),
+ mSoundCombo(NULL),
+ mChatEditor(NULL),
+ mSaveBtn(NULL),
+ mPreviewBtn(NULL),
+ mPreviewGesture(NULL),
+ mDirty(FALSE)
+{
+}
+
+
+LLPreviewGesture::~LLPreviewGesture()
+{
+ // Userdata for all steps is a LLGestureStep we need to clean up
+ std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
+ std::vector<LLScrollListItem*>::iterator data_itor;
+ for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
+ {
+ LLScrollListItem* item = *data_itor;
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+ delete step;
+ step = NULL;
+ }
+}
+
+
+BOOL LLPreviewGesture::postBuild()
+{
+ LLLineEditor* edit;
+ LLComboBox* combo;
+ LLButton* btn;
+ LLScrollListCtrl* list;
+ LLTextBox* text;
+ LLCheckBoxCtrl* check;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "trigger_editor");
+ edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitCallback(onCommitSetDirty);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mTriggerEditor = edit;
+
+ text = LLViewerUICtrlFactory::getTextBoxByName(this, "replace_text");
+ text->setEnabled(FALSE);
+ mReplaceText = text;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "replace_editor");
+ edit->setEnabled(FALSE);
+ edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitCallback(onCommitSetDirty);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mReplaceEditor = edit;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "modifier_combo");
+ combo->setCommitCallback(onCommitSetDirty);
+ combo->setCallbackUserData(this);
+ mModifierCombo = combo;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "key_combo");
+ combo->setCommitCallback(onCommitSetDirty);
+ combo->setCallbackUserData(this);
+ mKeyCombo = combo;
+
+ list = LLViewerUICtrlFactory::getScrollListByName(this, "library_list");
+ list->setCommitCallback(onCommitLibrary);
+ list->setDoubleClickCallback(onClickAdd);
+ list->setCallbackUserData(this);
+ mLibraryList = list;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "add_btn");
+ btn->setClickedCallback(onClickAdd);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mAddBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "up_btn");
+ btn->setClickedCallback(onClickUp);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mUpBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "down_btn");
+ btn->setClickedCallback(onClickDown);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mDownBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "delete_btn");
+ btn->setClickedCallback(onClickDelete);
+ btn->setCallbackUserData(this);
+ btn->setEnabled(FALSE);
+ mDeleteBtn = btn;
+
+ list = LLViewerUICtrlFactory::getScrollListByName(this, "step_list");
+ list->setCommitCallback(onCommitStep);
+ list->setCallbackUserData(this);
+ mStepList = list;
+
+ // Options
+ text = LLViewerUICtrlFactory::getTextBoxByName(this, "options_text");
+ text->setBorderVisible(TRUE);
+ mOptionsText = text;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "animation_list");
+ combo->setVisible(FALSE);
+ combo->setCommitCallback(onCommitAnimation);
+ combo->setCallbackUserData(this);
+ mAnimationCombo = combo;
+
+ LLRadioGroup* group;
+ group = LLViewerUICtrlFactory::getRadioGroupByName(this, "animation_trigger_type");
+ group->setVisible(FALSE);
+ group->setCommitCallback(onCommitAnimationTrigger);
+ group->setCallbackUserData(this);
+ mAnimationRadio = group;
+
+ combo = LLViewerUICtrlFactory::getComboBoxByName(this, "sound_list");
+ combo->setVisible(FALSE);
+ combo->setCommitCallback(onCommitSound);
+ combo->setCallbackUserData(this);
+ mSoundCombo = combo;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "chat_editor");
+ edit->setVisible(FALSE);
+ edit->setCommitCallback(onCommitChat);
+ //edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mChatEditor = edit;
+
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "wait_anim_check");
+ check->setVisible(FALSE);
+ check->setCommitCallback(onCommitWait);
+ check->setCallbackUserData(this);
+ mWaitAnimCheck = check;
+
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "wait_time_check");
+ check->setVisible(FALSE);
+ check->setCommitCallback(onCommitWait);
+ check->setCallbackUserData(this);
+ mWaitTimeCheck = check;
+
+ edit = LLViewerUICtrlFactory::getLineEditorByName(this, "wait_time_editor");
+ edit->setEnabled(FALSE);
+ edit->setVisible(FALSE);
+ edit->setPrevalidate(LLLineEditor::prevalidateFloat);
+// edit->setKeystrokeCallback(onKeystrokeCommit);
+ edit->setCommitOnFocusLost(TRUE);
+ edit->setCommitCallback(onCommitWaitTime);
+ edit->setCallbackUserData(this);
+ edit->setIgnoreTab(TRUE);
+ mWaitTimeEditor = edit;
+
+ // Buttons at the bottom
+ check = LLViewerUICtrlFactory::getCheckBoxByName(this, "active_check");
+ check->setCommitCallback(onCommitActive);
+ check->setCallbackUserData(this);
+ mActiveCheck = check;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "save_btn");
+ btn->setClickedCallback(onClickSave);
+ btn->setCallbackUserData(this);
+ mSaveBtn = btn;
+
+ btn = LLViewerUICtrlFactory::getButtonByName(this, "preview_btn");
+ btn->setClickedCallback(onClickPreview);
+ btn->setCallbackUserData(this);
+ mPreviewBtn = btn;
+
+
+ // Populate the comboboxes
+ addModifiers();
+ addKeys();
+ addAnimations();
+ addSounds();
+
+
+ LLInventoryItem* item = getItem();
+
+ if (item)
+ {
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+ }
+
+ return TRUE;
+}
+
+
+void LLPreviewGesture::addModifiers()
+{
+ LLComboBox* combo = mModifierCombo;
+
+ combo->add( NONE_LABEL, ADD_BOTTOM );
+ combo->add( SHIFT_LABEL, ADD_BOTTOM );
+ combo->add( CTRL_LABEL, ADD_BOTTOM );
+ combo->setCurrentByIndex(0);
+}
+
+void LLPreviewGesture::addKeys()
+{
+ LLComboBox* combo = mKeyCombo;
+
+ combo->add( NONE_LABEL );
+ for (KEY key = KEY_F2; key <= KEY_F12; key++)
+ {
+ combo->add( LLKeyboard::stringFromKey(key), ADD_BOTTOM );
+ }
+ combo->setCurrentByIndex(0);
+}
+
+
+// TODO: Sort the legacy and non-legacy together?
+void LLPreviewGesture::addAnimations()
+{
+ LLComboBox* combo = mAnimationCombo;
+
+ combo->removeall();
+
+ combo->add("-- None --", LLUUID::null);
+
+ // Add all the default (legacy) animations
+ S32 i;
+ for (i = 0; i < gUserAnimStatesCount; ++i)
+ {
+ // Use the user-readable name
+ const char* label = gUserAnimStates[i].mLabel;
+ const LLUUID& id = gUserAnimStates[i].mID;
+ combo->add(label, id);
+ }
+
+ // Get all inventory items that are animations
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLIsTypeWithPermissions is_copyable_animation(LLAssetType::AT_ANIMATION,
+ PERM_ITEM_UNRESTRICTED,
+ gAgent.getID(),
+ gAgent.getGroupID());
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_copyable_animation);
+
+ // Copy into something we can sort
+ std::vector<LLInventoryItem*> animations;
+
+ S32 count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ animations.push_back( items.get(i) );
+ }
+
+ // Do the sort
+ std::sort(animations.begin(), animations.end(), SortItemPtrsByName());
+
+ // And load up the combobox
+ std::vector<LLInventoryItem*>::iterator it;
+ for (it = animations.begin(); it != animations.end(); ++it)
+ {
+ LLInventoryItem* item = *it;
+
+ combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
+ }
+}
+
+
+void LLPreviewGesture::addSounds()
+{
+ // Get all inventory items that are sounds
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLIsTypeWithPermissions is_copyable_sound(LLAssetType::AT_SOUND,
+ PERM_ITEM_UNRESTRICTED,
+ gAgent.getID(),
+ gAgent.getGroupID());
+ gInventory.collectDescendentsIf(gAgent.getInventoryRootID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_copyable_sound);
+
+ // Copy sounds into something we can sort
+ std::vector<LLInventoryItem*> sounds;
+
+ S32 i;
+ S32 count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ sounds.push_back( items.get(i) );
+ }
+
+ // Do the sort
+ std::sort(sounds.begin(), sounds.end(), SortItemPtrsByName());
+
+ // And load up the combobox
+ LLComboBox* combo = mSoundCombo;
+ combo->removeall();
+ std::vector<LLInventoryItem*>::iterator it;
+ for (it = sounds.begin(); it != sounds.end(); ++it)
+ {
+ LLInventoryItem* item = *it;
+
+ combo->add(item->getName(), item->getAssetUUID(), ADD_BOTTOM);
+ }
+}
+
+
+void LLPreviewGesture::init(const LLUUID& item_id, const LLUUID& object_id)
+{
+ // Sets ID and adds to instance list
+ setItemID(item_id);
+ setObjectID(object_id);
+}
+
+
+void LLPreviewGesture::refresh()
+{
+ // If previewing or item is incomplete, all controls are disabled
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)getItem();
+ bool is_complete = (item && item->isComplete()) ? true : false;
+ if (mPreviewGesture || !is_complete)
+ {
+
+ childSetEnabled("desc", FALSE);
+ //mDescEditor->setEnabled(FALSE);
+ mTriggerEditor->setEnabled(FALSE);
+ mReplaceText->setEnabled(FALSE);
+ mReplaceEditor->setEnabled(FALSE);
+ mModifierCombo->setEnabled(FALSE);
+ mKeyCombo->setEnabled(FALSE);
+ mLibraryList->setEnabled(FALSE);
+ mAddBtn->setEnabled(FALSE);
+ mUpBtn->setEnabled(FALSE);
+ mDownBtn->setEnabled(FALSE);
+ mDeleteBtn->setEnabled(FALSE);
+ mStepList->setEnabled(FALSE);
+ mOptionsText->setEnabled(FALSE);
+ mAnimationCombo->setEnabled(FALSE);
+ mAnimationRadio->setEnabled(FALSE);
+ mSoundCombo->setEnabled(FALSE);
+ mChatEditor->setEnabled(FALSE);
+ mWaitAnimCheck->setEnabled(FALSE);
+ mWaitTimeCheck->setEnabled(FALSE);
+ mWaitTimeEditor->setEnabled(FALSE);
+ mActiveCheck->setEnabled(FALSE);
+ mSaveBtn->setEnabled(FALSE);
+
+ // Make sure preview button is enabled, so we can stop it
+ mPreviewBtn->setEnabled(TRUE);
+ return;
+ }
+
+ BOOL modifiable = item->getPermissions().allowModifyBy(gAgent.getID());
+
+ childSetEnabled("desc", modifiable);
+ mTriggerEditor->setEnabled(TRUE);
+ mLibraryList->setEnabled(modifiable);
+ mStepList->setEnabled(modifiable);
+ mOptionsText->setEnabled(modifiable);
+ mAnimationCombo->setEnabled(modifiable);
+ mAnimationRadio->setEnabled(modifiable);
+ mSoundCombo->setEnabled(modifiable);
+ mChatEditor->setEnabled(modifiable);
+ mWaitAnimCheck->setEnabled(modifiable);
+ mWaitTimeCheck->setEnabled(modifiable);
+ mWaitTimeEditor->setEnabled(modifiable);
+ mActiveCheck->setEnabled(TRUE);
+
+ const std::string& trigger = mTriggerEditor->getText();
+ BOOL have_trigger = !trigger.empty();
+
+ const std::string& replace = mReplaceEditor->getText();
+ BOOL have_replace = !replace.empty();
+
+ LLScrollListItem* library_item = mLibraryList->getFirstSelected();
+ BOOL have_library = (library_item != NULL);
+
+ LLScrollListItem* step_item = mStepList->getFirstSelected();
+ S32 step_index = mStepList->getFirstSelectedIndex();
+ S32 step_count = mStepList->getItemCount();
+ BOOL have_step = (step_item != NULL);
+
+ mReplaceText->setEnabled(have_trigger || have_replace);
+ mReplaceEditor->setEnabled(have_trigger || have_replace);
+
+ mModifierCombo->setEnabled(TRUE);
+ mKeyCombo->setEnabled(TRUE);
+
+ mAddBtn->setEnabled(modifiable && have_library);
+ mUpBtn->setEnabled(modifiable && have_step && step_index > 0);
+ mDownBtn->setEnabled(modifiable && have_step && step_index < step_count-1);
+ mDeleteBtn->setEnabled(modifiable && have_step);
+
+ // Assume all not visible
+ mAnimationCombo->setVisible(FALSE);
+ mAnimationRadio->setVisible(FALSE);
+ mSoundCombo->setVisible(FALSE);
+ mChatEditor->setVisible(FALSE);
+ mWaitAnimCheck->setVisible(FALSE);
+ mWaitTimeCheck->setVisible(FALSE);
+ mWaitTimeEditor->setVisible(FALSE);
+
+ if (have_step)
+ {
+ // figure out the type, show proper options, update text
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ EStepType type = step->getType();
+ switch(type)
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ mOptionsText->setText("Animation to play:");
+ mAnimationCombo->setVisible(TRUE);
+ mAnimationRadio->setVisible(TRUE);
+ mAnimationRadio->setSelectedIndex((anim_step->mFlags & ANIM_FLAG_STOP) ? 1 : 0);
+ mAnimationCombo->setCurrentByID(anim_step->mAnimAssetID);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ mOptionsText->setText("Sound to play:");
+ mSoundCombo->setVisible(TRUE);
+ mSoundCombo->setCurrentByID(sound_step->mSoundAssetID);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ mOptionsText->setText("Chat to say:");
+ mChatEditor->setVisible(TRUE);
+ mChatEditor->setText(chat_step->mChatText);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ mOptionsText->setText("Wait:");
+ mWaitAnimCheck->setVisible(TRUE);
+ mWaitAnimCheck->set(wait_step->mFlags & WAIT_FLAG_ALL_ANIM);
+ mWaitTimeCheck->setVisible(TRUE);
+ mWaitTimeCheck->set(wait_step->mFlags & WAIT_FLAG_TIME);
+ mWaitTimeEditor->setVisible(TRUE);
+ char buffer[16];
+ sprintf(buffer, "%.1f", (double)wait_step->mWaitSeconds);
+ mWaitTimeEditor->setText(buffer);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // no gesture
+ mOptionsText->setText("");
+ }
+
+ BOOL active = gGestureManager.isGestureActive(mItemUUID);
+ mActiveCheck->set(active);
+
+ // Can only preview if there are steps
+ mPreviewBtn->setEnabled(step_count > 0);
+
+ // And can only save if changes have been made
+ mSaveBtn->setEnabled(mDirty);
+ addAnimations();
+ addSounds();
+}
+
+
+void LLPreviewGesture::initDefaultGesture()
+{
+ LLScrollListItem* item;
+ item = addStep("Animation");
+ LLGestureStepAnimation* anim = (LLGestureStepAnimation*)item->getUserdata();
+ anim->mAnimAssetID = ANIM_AGENT_HELLO;
+ anim->mAnimName = "Wave";
+ updateLabel(item);
+
+ item = addStep("Wait");
+ LLGestureStepWait* wait = (LLGestureStepWait*)item->getUserdata();
+ wait->mFlags = WAIT_FLAG_ALL_ANIM;
+ updateLabel(item);
+
+ item = addStep("Chat");
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)item->getUserdata();
+ chat_step->mChatText = "Hello, avatar!";
+ updateLabel(item);
+
+ // Start with item list selected
+ mStepList->selectFirstItem();
+
+ // this is *new* content, so we are dirty
+ mDirty = TRUE;
+}
+
+
+void LLPreviewGesture::loadAsset()
+{
+ LLInventoryItem* item = getItem();
+ if (!item) return;
+
+ LLUUID asset_id = item->getAssetUUID();
+ if (asset_id.isNull())
+ {
+ // Freshly created gesture, don't need to load asset.
+ // Blank gesture will be fine.
+ initDefaultGesture();
+ refresh();
+ return;
+ }
+
+ // TODO: Based on item->getPermissions().allow*
+ // could enable/disable UI.
+
+ // Copy the UUID, because the user might close the preview
+ // window if the download gets stalled.
+ LLUUID* item_idp = new LLUUID(mItemUUID);
+
+ const BOOL high_priority = TRUE;
+ gAssetStorage->getAssetData(asset_id,
+ LLAssetType::AT_GESTURE,
+ onLoadComplete,
+ (void**)item_idp,
+ high_priority);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+}
+
+
+// static
+void LLPreviewGesture::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* item_idp = (LLUUID*)user_data;
+ LLPreview* preview = LLPreview::find(*item_idp);
+ if (preview)
+ {
+ LLPreviewGesture* self = (LLPreviewGesture*)preview;
+
+ if (0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+ S32 size = file.getSize();
+
+ char* buffer = new char[size+1];
+ file.read((U8*)buffer, size);
+ buffer[size] = '\0';
+
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ LLDataPackerAsciiBuffer dp(buffer, size+1);
+ BOOL ok = gesture->deserialize(dp);
+
+ if (ok)
+ {
+ // Everything has been successful. Load up the UI.
+ self->loadUIFromGesture(gesture);
+
+ self->mStepList->selectFirstItem();
+
+ self->mDirty = FALSE;
+ self->refresh();
+ }
+ else
+ {
+ llwarns << "Unable to load gesture" << llendl;
+ }
+
+ delete gesture;
+ gesture = NULL;
+
+ delete [] buffer;
+ buffer = NULL;
+
+ self->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("GestureMissing");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadGesture");
+ }
+
+ llwarns << "Problem loading gesture: " << status << llendl;
+ self->mAssetStatus = PREVIEW_ASSET_ERROR;
+ }
+ }
+ delete item_idp;
+ item_idp = NULL;
+}
+
+
+void LLPreviewGesture::loadUIFromGesture(LLMultiGesture* gesture)
+{
+ /*LLInventoryItem* item = getItem();
+
+
+
+ if (item)
+ {
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+ descEditor->setText(item->getDescription());
+ }*/
+
+ mTriggerEditor->setText(gesture->mTrigger);
+
+ mReplaceEditor->setText(gesture->mReplaceText);
+
+ switch (gesture->mMask)
+ {
+ default:
+ case MASK_NONE:
+ mModifierCombo->setSimple( NONE_LABEL );
+ break;
+ case MASK_SHIFT:
+ mModifierCombo->setSimple( SHIFT_LABEL );
+ break;
+ case MASK_CONTROL:
+ mModifierCombo->setSimple( CTRL_LABEL );
+ break;
+ }
+
+ mKeyCombo->setCurrentByIndex(0);
+ if (gesture->mKey != KEY_NONE)
+ {
+ mKeyCombo->setSimple(LLKeyboard::stringFromKey(gesture->mKey));
+ }
+
+ // Make UI steps for each gesture step
+ S32 i;
+ S32 count = gesture->mSteps.size();
+ for (i = 0; i < count; ++i)
+ {
+ LLGestureStep* step = gesture->mSteps[i];
+
+ LLGestureStep* new_step = NULL;
+
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ LLGestureStepAnimation* new_anim_step =
+ new LLGestureStepAnimation(*anim_step);
+ new_step = new_anim_step;
+ break;
+ }
+ case STEP_SOUND:
+ {
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ LLGestureStepSound* new_sound_step =
+ new LLGestureStepSound(*sound_step);
+ new_step = new_sound_step;
+ break;
+ }
+ case STEP_CHAT:
+ {
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ LLGestureStepChat* new_chat_step =
+ new LLGestureStepChat(*chat_step);
+ new_step = new_chat_step;
+ break;
+ }
+ case STEP_WAIT:
+ {
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ LLGestureStepWait* new_wait_step =
+ new LLGestureStepWait(*wait_step);
+ new_step = new_wait_step;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if (!new_step) continue;
+
+ // Create an enabled item with this step
+ LLScrollListItem* item = new LLScrollListItem(TRUE, new_step);
+ item->addColumn(new_step->getLabel(), LLFontGL::sSansSerifSmall);
+
+ // Add item to bottom of list
+ mStepList->addItem(item, ADD_BOTTOM);
+ }
+}
+
+// Helpful structure so we can look up the inventory item
+// after the save finishes.
+struct LLSaveInfo
+{
+ LLSaveInfo(const LLUUID& item_id, const LLUUID& object_id, const LLString& desc,
+ const LLTransactionID tid)
+ : mItemUUID(item_id), mObjectUUID(object_id), mDesc(desc), mTransactionID(tid)
+ {
+ }
+
+ LLUUID mItemUUID;
+ LLUUID mObjectUUID;
+ LLString mDesc;
+ LLTransactionID mTransactionID;
+};
+
+
+void LLPreviewGesture::saveIfNeeded()
+{
+ if (!gAssetStorage)
+ {
+ llwarns << "Can't save gesture, no asset storage system." << llendl;
+ return;
+ }
+
+ if (!mDirty)
+ {
+ return;
+ }
+
+ // Copy the UI into a gesture
+ LLMultiGesture* gesture = createGesture();
+
+ // Serialize the gesture
+ S32 max_size = gesture->getMaxSerialSize();
+ char* buffer = new char[max_size];
+
+ LLDataPackerAsciiBuffer dp(buffer, max_size);
+
+ BOOL ok = gesture->serialize(dp);
+
+ if (dp.getCurrentSize() > 1000)
+ {
+ gViewerWindow->alertXml("GestureSaveFailedTooManySteps");
+
+ delete gesture;
+ gesture = NULL;
+ }
+ else if (!ok)
+ {
+ gViewerWindow->alertXml("GestureSaveFailedTryAgain");
+ delete gesture;
+ gesture = NULL;
+ }
+ else
+ {
+ // Every save gets a new UUID. Yup.
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_GESTURE, LLVFile::APPEND);
+
+ S32 size = dp.getCurrentSize();
+ file.setMaxSize(size);
+ file.write((U8*)buffer, size);
+
+ // Upload that asset to the database
+ LLInventoryItem* item = getItem();
+ if (item)
+ {
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+ LLSaveInfo* info = new LLSaveInfo(mItemUUID, mObjectUUID, descEditor->getText(), tid);
+
+ const BOOL temp_file = FALSE;
+
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_GESTURE, onSaveComplete, info, temp_file);
+
+ }
+
+ // If this gesture is active, then we need to update the in-memory
+ // active map with the new pointer.
+ if (gGestureManager.isGestureActive(mItemUUID))
+ {
+ // gesture manager now owns the pointer
+ gGestureManager.replaceGesture(mItemUUID, gesture, asset_id);
+
+ // replaceGesture may deactivate other gestures so let the
+ // inventory know.
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ // we're done with this gesture
+ delete gesture;
+ gesture = NULL;
+ }
+
+ mDirty = FALSE;
+ refresh();
+ }
+
+ delete [] buffer;
+ buffer = NULL;
+}
+
+
+// TODO: This is very similar to LLPreviewNotecard::onSaveComplete.
+// Could merge code.
+// static
+void LLPreviewGesture::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLSaveInfo* info = (LLSaveInfo*)user_data;
+ if (info && (status == 0))
+ {
+ if(info->mObjectUUID.isNull())
+ {
+ // Saving into user inventory
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setDescription(info->mDesc);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for gesture " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl
+ }
+ }
+ else
+ {
+ // Saving into in-world object inventory
+ LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
+ LLViewerInventoryItem* item = NULL;
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(info->mItemUUID);
+ }
+ if(object && item)
+ {
+ item->setDescription(info->mDesc);
+ item->setAssetUUID(asset_uuid);
+ item->setTransactionID(info->mTransactionID);
+ object->updateInventory(item, TASK_INVENTORY_ITEM_KEY, false);
+ dialog_refresh_all();
+ }
+ else
+ {
+ gViewerWindow->alertXml("GestureSaveFailedObjectNotFound");
+ }
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewGesture* previewp = (LLPreviewGesture*)LLPreview::find(info->mItemUUID);
+ if (previewp && previewp->mCloseAfterSave)
+ {
+ previewp->close();
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving gesture: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("GestureSaveFailedReason",args);
+ }
+ delete info;
+ info = NULL;
+}
+
+
+LLMultiGesture* LLPreviewGesture::createGesture()
+{
+ LLMultiGesture* gesture = new LLMultiGesture();
+
+ gesture->mTrigger = mTriggerEditor->getText();
+ gesture->mReplaceText = mReplaceEditor->getText();
+
+ const LLString& modifier = mModifierCombo->getSimple();
+ if (modifier == CTRL_LABEL)
+ {
+ gesture->mMask = MASK_CONTROL;
+ }
+ else if (modifier == SHIFT_LABEL)
+ {
+ gesture->mMask = MASK_SHIFT;
+ }
+ else
+ {
+ gesture->mMask = MASK_NONE;
+ }
+
+ if (mKeyCombo->getCurrentIndex() == 0)
+ {
+ gesture->mKey = KEY_NONE;
+ }
+ else
+ {
+ const LLString& key_string = mKeyCombo->getSimple();
+ LLKeyboard::keyFromString(key_string.c_str(), &(gesture->mKey));
+ }
+
+ std::vector<LLScrollListItem*> data_list = mStepList->getAllData();
+ std::vector<LLScrollListItem*>::iterator data_itor;
+ for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
+ {
+ LLScrollListItem* item = *data_itor;
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+
+ switch(step->getType())
+ {
+ case STEP_ANIMATION:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ LLGestureStepAnimation* new_anim_step =
+ new LLGestureStepAnimation(*anim_step);
+ gesture->mSteps.push_back(new_anim_step);
+ break;
+ }
+ case STEP_SOUND:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ LLGestureStepSound* new_sound_step =
+ new LLGestureStepSound(*sound_step);
+ gesture->mSteps.push_back(new_sound_step);
+ break;
+ }
+ case STEP_CHAT:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ LLGestureStepChat* new_chat_step =
+ new LLGestureStepChat(*chat_step);
+ gesture->mSteps.push_back(new_chat_step);
+ break;
+ }
+ case STEP_WAIT:
+ {
+ // Copy UI-generated step into actual gesture step
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ LLGestureStepWait* new_wait_step =
+ new LLGestureStepWait(*wait_step);
+ gesture->mSteps.push_back(new_wait_step);
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ return gesture;
+}
+
+
+// static
+void LLPreviewGesture::updateLabel(LLScrollListItem* item)
+{
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+
+ LLScrollListCell* cell = item->getColumn(0);
+ LLScrollListText* text_cell = (LLScrollListText*)cell;
+ std::string label = step->getLabel();
+ text_cell->setText(label);
+}
+
+// static
+void LLPreviewGesture::onCommitSetDirty(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitLibrary(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
+ if (library_item)
+ {
+ self->mStepList->deselectAllItems();
+ self->refresh();
+ }
+}
+
+
+// static
+void LLPreviewGesture::onCommitStep(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ self->mLibraryList->deselectAllItems();
+ self->refresh();
+}
+
+
+// static
+void LLPreviewGesture::onCommitAnimation(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_ANIMATION)
+ {
+ // Assign the animation name
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (self->mAnimationCombo->getCurrentIndex() == 0)
+ {
+ anim_step->mAnimName.clear();
+ anim_step->mAnimAssetID.setNull();
+ }
+ else
+ {
+ anim_step->mAnimName = self->mAnimationCombo->getSimple();
+ anim_step->mAnimAssetID = self->mAnimationCombo->getCurrentID();
+ }
+ //anim_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitAnimationTrigger(LLUICtrl* ctrl, void *data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_ANIMATION)
+ {
+ LLGestureStepAnimation* anim_step = (LLGestureStepAnimation*)step;
+ if (self->mAnimationRadio->getSelectedIndex() == 0)
+ {
+ // start
+ anim_step->mFlags &= ~ANIM_FLAG_STOP;
+ }
+ else
+ {
+ // stop
+ anim_step->mFlags |= ANIM_FLAG_STOP;
+ }
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitSound(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (step_item)
+ {
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() == STEP_SOUND)
+ {
+ // Assign the sound name
+ LLGestureStepSound* sound_step = (LLGestureStepSound*)step;
+ sound_step->mSoundName = self->mSoundCombo->getSimple();
+ sound_step->mSoundAssetID = self->mSoundCombo->getCurrentID();
+ sound_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitChat(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_CHAT) return;
+
+ LLGestureStepChat* chat_step = (LLGestureStepChat*)step;
+ chat_step->mChatText = self->mChatEditor->getText();
+ chat_step->mFlags = 0x0;
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitWait(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_WAIT) return;
+
+ LLGestureStepWait* wait_step = (LLGestureStepWait*)step;
+ U32 flags = 0x0;
+ if (self->mWaitAnimCheck->get()) flags |= WAIT_FLAG_ALL_ANIM;
+ if (self->mWaitTimeCheck->get()) flags |= WAIT_FLAG_TIME;
+ wait_step->mFlags = flags;
+
+ {
+ LLLocale locale(LLLocale::USER_LOCALE);
+
+ F32 wait_seconds = (F32)atof(self->mWaitTimeEditor->getText().c_str());
+ if (wait_seconds < 0.f) wait_seconds = 0.f;
+ if (wait_seconds > 3600.f) wait_seconds = 3600.f;
+ wait_step->mWaitSeconds = wait_seconds;
+ }
+
+ // Enable the input area if necessary
+ self->mWaitTimeEditor->setEnabled(self->mWaitTimeCheck->get());
+
+ // Update the UI label in the list
+ updateLabel(step_item);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onCommitWaitTime(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* step_item = self->mStepList->getFirstSelected();
+ if (!step_item) return;
+
+ LLGestureStep* step = (LLGestureStep*)step_item->getUserdata();
+ if (step->getType() != STEP_WAIT) return;
+
+ self->mWaitTimeCheck->set(TRUE);
+ onCommitWait(ctrl, data);
+}
+
+
+// static
+void LLPreviewGesture::onKeystrokeCommit(LLLineEditor* caller,
+ void* data)
+{
+ // Just commit every keystroke
+ onCommitSetDirty(caller, data);
+}
+
+// static
+void LLPreviewGesture::onClickAdd(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* library_item = self->mLibraryList->getFirstSelected();
+ if (!library_item) return;
+
+ const LLScrollListCell* library_cell = library_item->getColumn(0);
+ const std::string& library_text = library_cell->getText();
+
+ self->addStep(library_text);
+
+ self->mDirty = TRUE;
+ self->refresh();
+}
+
+LLScrollListItem* LLPreviewGesture::addStep(const std::string& library_text)
+{
+ LLGestureStep* step = NULL;
+ if (!LLString::compareInsensitive(library_text.c_str(), "Animation"))
+ {
+ step = new LLGestureStepAnimation();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Sound"))
+ {
+ step = new LLGestureStepSound();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Chat"))
+ {
+ step = new LLGestureStepChat();
+ }
+ else if (!LLString::compareInsensitive(library_text.c_str(), "Wait"))
+ {
+ step = new LLGestureStepWait();
+ }
+
+ // Create an enabled item with this step
+ LLScrollListItem* step_item = new LLScrollListItem(TRUE, step);
+ std::string label = step->getLabel();
+ step_item->addColumn(label, LLFontGL::sSansSerifSmall);
+
+ // Add item to bottom of list
+ mStepList->addItem(step_item, ADD_BOTTOM);
+
+ // And move selection to the list on the right
+ mLibraryList->deselectAllItems();
+ mStepList->deselectAllItems();
+
+ step_item->setSelected(TRUE);
+
+ return step_item;
+}
+
+// static
+void LLPreviewGesture::onClickUp(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index > 0)
+ {
+ self->mStepList->swapWithPrevious(selected_index);
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onClickDown(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index < 0) return;
+
+ S32 count = self->mStepList->getItemCount();
+ if (selected_index < count-1)
+ {
+ self->mStepList->swapWithNext(selected_index);
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onClickDelete(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ LLScrollListItem* item = self->mStepList->getFirstSelected();
+ S32 selected_index = self->mStepList->getFirstSelectedIndex();
+ if (selected_index >= 0)
+ {
+ LLGestureStep* step = (LLGestureStep*)item->getUserdata();
+ delete step;
+ step = NULL;
+
+ self->mStepList->deleteSingleItem(selected_index);
+
+ self->mDirty = TRUE;
+ self->refresh();
+ }
+}
+
+// static
+void LLPreviewGesture::onCommitActive(LLUICtrl* ctrl, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ if (!gGestureManager.isGestureActive(self->mItemUUID))
+ {
+ gGestureManager.activateGesture(self->mItemUUID);
+ }
+ else
+ {
+ gGestureManager.deactivateGesture(self->mItemUUID);
+ }
+
+ // Make sure the (active) label in the inventory gets updated.
+ LLViewerInventoryItem* item = gInventory.getItem(self->mItemUUID);
+ if (item)
+ {
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+
+ self->refresh();
+}
+
+// static
+void LLPreviewGesture::onClickSave(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+ self->saveIfNeeded();
+}
+
+// static
+void LLPreviewGesture::onClickPreview(void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ if (!self->mPreviewGesture)
+ {
+ // make temporary gesture
+ self->mPreviewGesture = self->createGesture();
+
+ // add a callback
+ self->mPreviewGesture->mDoneCallback = onDonePreview;
+ self->mPreviewGesture->mCallbackData = self;
+
+ // set the button title
+ self->mPreviewBtn->setLabelSelected("Stop");
+ self->mPreviewBtn->setLabelUnselected("Stop");
+
+ // play it, and delete when done
+ gGestureManager.playGesture(self->mPreviewGesture);
+
+ self->refresh();
+ }
+ else
+ {
+ // Will call onDonePreview() below
+ gGestureManager.stopGesture(self->mPreviewGesture);
+
+ self->refresh();
+ }
+}
+
+
+// static
+void LLPreviewGesture::onDonePreview(LLMultiGesture* gesture, void* data)
+{
+ LLPreviewGesture* self = (LLPreviewGesture*)data;
+
+ self->mPreviewBtn->setLabelSelected("Preview");
+ self->mPreviewBtn->setLabelUnselected("Preview");
+
+ delete self->mPreviewGesture;
+ self->mPreviewGesture = NULL;
+
+ self->refresh();
+}
diff --git a/indra/newview/llpreviewgesture.h b/indra/newview/llpreviewgesture.h
new file mode 100644
index 0000000000..77d7477d91
--- /dev/null
+++ b/indra/newview/llpreviewgesture.h
@@ -0,0 +1,149 @@
+/**
+ * @file llpreviewgesture.h
+ * @brief Editing UI for inventory-based gestures.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWGESTURE_H
+#define LL_LLPREVIEWGESTURE_H
+
+#include "llpreview.h"
+
+class LLMultiGesture;
+class LLLineEditor;
+class LLTextBox;
+class LLCheckBoxCtrl;
+class LLComboBox;
+class LLScrollListCtrl;
+class LLScrollListItem;
+class LLButton;
+class LLGestureStep;
+class LLRadioGroup;
+
+class LLPreviewGesture : public LLPreview
+{
+public:
+ // Pass an object_id if this gesture is inside an object in the world,
+ // otherwise use LLUUID::null.
+ static LLPreviewGesture* show(const std::string& title, const LLUUID& item_id, const LLUUID& object_id, BOOL take_focus = TRUE);
+
+ // LLView
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ // LLPanel
+ virtual BOOL postBuild();
+
+ // LLFloater
+ virtual BOOL canClose();
+ virtual void setMinimized(BOOL minimize);
+ virtual void onClose(bool app_quitting);
+
+
+protected:
+ LLPreviewGesture();
+ virtual ~LLPreviewGesture();
+
+ void init(const LLUUID& item_id, const LLUUID& object_id);
+
+ // Populate various comboboxes
+ void addModifiers();
+ void addKeys();
+ void addAnimations();
+ void addSounds();
+
+ void refresh();
+
+ void initDefaultGesture();
+
+ void loadAsset();
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ void loadUIFromGesture(LLMultiGesture* gesture);
+
+ void saveIfNeeded();
+
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ void* user_data,
+ S32 status);
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+
+ // Write UI back into gesture
+ LLMultiGesture* createGesture();
+
+ // Add a step. Pass the name of the step, like "Animation",
+ // "Sound", "Chat", or "Wait"
+ LLScrollListItem* addStep(const std::string& step_name);
+
+ static void updateLabel(LLScrollListItem* item);
+
+ static void onCommitSetDirty(LLUICtrl* ctrl, void* data);
+ static void onCommitLibrary(LLUICtrl* ctrl, void* data);
+ static void onCommitStep(LLUICtrl* ctrl, void* data);
+ static void onCommitAnimation(LLUICtrl* ctrl, void* data);
+ static void onCommitSound(LLUICtrl* ctrl, void* data);
+ static void onCommitChat(LLUICtrl* ctrl, void* data);
+ static void onCommitWait(LLUICtrl* ctrl, void* data);
+ static void onCommitWaitTime(LLUICtrl* ctrl, void* data);
+
+ static void onCommitAnimationTrigger(LLUICtrl* ctrl, void *data);
+
+ // Handy function to commit each keystroke
+ static void onKeystrokeCommit(LLLineEditor* caller, void* data);
+
+ static void onClickAdd(void* data);
+ static void onClickUp(void* data);
+ static void onClickDown(void* data);
+ static void onClickDelete(void* data);
+
+ static void onCommitActive(LLUICtrl* ctrl, void* data);
+ static void onClickSave(void* data);
+ static void onClickPreview(void* data);
+
+ static void onDonePreview(LLMultiGesture* gesture, void* data);
+
+protected:
+ // LLPreview contains mDescEditor
+ LLLineEditor* mTriggerEditor;
+ LLTextBox* mReplaceText;
+ LLLineEditor* mReplaceEditor;
+ LLComboBox* mModifierCombo;
+ LLComboBox* mKeyCombo;
+
+ LLScrollListCtrl* mLibraryList;
+ LLButton* mAddBtn;
+ LLButton* mUpBtn;
+ LLButton* mDownBtn;
+ LLButton* mDeleteBtn;
+ LLScrollListCtrl* mStepList;
+
+ // Options panels for items in gesture list
+ LLTextBox* mOptionsText;
+ LLRadioGroup* mAnimationRadio;
+ LLComboBox* mAnimationCombo;
+ LLComboBox* mSoundCombo;
+ LLLineEditor* mChatEditor;
+ LLCheckBoxCtrl* mWaitAnimCheck;
+ LLCheckBoxCtrl* mWaitTimeCheck;
+ LLLineEditor* mWaitTimeEditor;
+
+ LLCheckBoxCtrl* mActiveCheck;
+ LLButton* mSaveBtn;
+ LLButton* mPreviewBtn;
+
+ LLMultiGesture* mPreviewGesture;
+ BOOL mDirty;
+};
+
+#endif
diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp
new file mode 100644
index 0000000000..e88c702453
--- /dev/null
+++ b/indra/newview/llpreviewnotecard.cpp
@@ -0,0 +1,579 @@
+/**
+ * @file llpreviewnotecard.cpp
+ * @brief Implementation of the notecard editor
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewnotecard.h"
+
+#include "llinventory.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llbutton.h"
+#include "llinventorymodel.h"
+#include "lllineeditor.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "roles_constants.h"
+#include "llscrollbar.h"
+#include "llselectmgr.h"
+#include "llviewertexteditor.h"
+#include "llvfile.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "lldir.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "viewer.h" // app_abort_quit()
+#include "lllineeditor.h"
+#include "llvieweruictrlfactory.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+const S32 PREVIEW_MIN_WIDTH =
+ 2 * PREVIEW_BORDER +
+ 2 * PREVIEW_BUTTON_WIDTH +
+ PREVIEW_PAD + RESIZE_HANDLE_WIDTH +
+ PREVIEW_PAD;
+const S32 PREVIEW_MIN_HEIGHT =
+ 2 * PREVIEW_BORDER +
+ 3*(20 + PREVIEW_PAD) +
+ 2 * SCROLLBAR_SIZE + 128;
+
+///----------------------------------------------------------------------------
+/// Class LLPreviewNotecard
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLPreviewNotecard::LLPreviewNotecard(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_id,
+ const LLUUID& object_id,
+ const LLUUID& asset_id,
+ BOOL show_keep_discard) :
+ LLPreview(name, rect, title, item_id, object_id, TRUE,
+ PREVIEW_MIN_WIDTH,
+ PREVIEW_MIN_HEIGHT),
+ mAssetID( asset_id ),
+ mNotecardItemID(item_id),
+ mObjectID(object_id)
+{
+ LLRect curRect = rect;
+
+ if (show_keep_discard)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_notecard_keep_discard.xml");
+ childSetAction("Keep",onKeepBtn,this);
+ childSetAction("Discard",onDiscardBtn,this);
+ }
+ else
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_notecard.xml");
+ childSetAction("Save",onClickSave,this);
+
+ if( mAssetID.isNull() )
+ {
+ LLInventoryItem* item = getItem();
+ if( item )
+ {
+ mAssetID = item->getAssetUUID();
+ }
+ }
+ }
+
+ reshape(curRect.getWidth(), curRect.getHeight(), TRUE);
+ setRect(curRect);
+
+ childSetVisible("lock", FALSE);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ if (item)
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ setTitle(title);
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if (editor)
+ {
+ editor->setWordWrap(TRUE);
+ editor->setSourceID(item_id);
+ editor->setHandleEditKeysDirectly(TRUE);
+ }
+
+ gAgent.changeCameraToDefault();
+}
+
+BOOL LLPreviewNotecard::postBuild()
+{
+ LLViewerTextEditor *ed = (LLViewerTextEditor *)gUICtrlFactory->getTextEditorByName(this, "Notecard Editor");
+ if (ed)
+ {
+ ed->setNotecardInfo(mNotecardItemID, mObjectID);
+ }
+ return TRUE;
+}
+
+bool LLPreviewNotecard::saveItem(LLPointer<LLInventoryItem>* itemptr)
+{
+ LLInventoryItem* item = NULL;
+ if (itemptr && itemptr->notNull())
+ {
+ item = (LLInventoryItem*)(*itemptr);
+ }
+ bool res = saveIfNeeded(item);
+ if (res)
+ {
+ delete itemptr;
+ }
+ return res;
+}
+
+void LLPreviewNotecard::setEnabled( BOOL enabled )
+{
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ childSetEnabled("Notecard Editor", enabled);
+ childSetVisible("lock", !enabled);
+ childSetEnabled("desc", enabled);
+ childSetEnabled("Save", enabled && editor && (!editor->isPristine()));
+
+}
+
+
+void LLPreviewNotecard::draw()
+{
+
+
+ //childSetFocus("Save", FALSE);
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+ BOOL script_changed = !editor->isPristine();
+
+ childSetEnabled("Save", script_changed && getEnabled());
+
+ LLPreview::draw();
+}
+
+// virtual
+BOOL LLPreviewNotecard::handleKeyHere(KEY key, MASK mask,
+ BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ saveIfNeeded();
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+// virtual
+BOOL LLPreviewNotecard::canClose()
+{
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(mForceClose || editor->isPristine())
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges",
+ &LLPreviewNotecard::handleSaveChangesDialog,
+ this);
+
+ return FALSE;
+ }
+}
+
+const LLInventoryItem* LLPreviewNotecard::getDragItem()
+{
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(editor)
+ {
+ return editor->getDragItem();
+ }
+ return NULL;
+}
+
+void LLPreviewNotecard::loadAsset()
+{
+ // request the asset.
+ LLInventoryItem* item = getItem();
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if (!editor)
+ return;
+
+
+ if(item)
+ {
+ if (gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || gAgent.isGodlike())
+ {
+ mAssetID = item->getAssetUUID();
+ if(mAssetID.isNull())
+ {
+ editor->setText("");
+ editor->makePristine();
+ editor->setEnabled(TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ LLUUID* new_uuid = new LLUUID(mItemUUID);
+ LLHost source_sim = LLHost::invalid;
+ if (mObjectUUID.notNull())
+ {
+ LLViewerObject *objectp = gObjectList.findObject(mObjectUUID);
+ if (objectp && objectp->getRegion())
+ {
+ source_sim = objectp->getRegion()->getHost();
+ }
+ else
+ {
+ // The object that we're trying to look at disappeared, bail.
+ llwarns << "Can't find object " << mObjectUUID << " associated with notecard." << llendl;
+ mAssetID.setNull();
+ editor->setText("Unable to find object containing this note.");
+ editor->makePristine();
+ editor->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ return;
+ }
+ }
+ gAssetStorage->getInvItemAsset(source_sim,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ mObjectUUID,
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &onLoadComplete,
+ (void*)new_uuid,
+ TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+ }
+ }
+ else
+ {
+ mAssetID.setNull();
+ editor->setText("You are not allowed to view this note.");
+ editor->makePristine();
+ editor->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ if(!gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ editor->setEnabled(FALSE);
+ childSetVisible("lock", TRUE);
+ }
+ }
+ else
+ {
+ editor->setText("");
+ editor->makePristine();
+ editor->setEnabled(TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+}
+
+// static
+void LLPreviewNotecard::onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "LLPreviewNotecard::onLoadComplete()" << llendl;
+ LLUUID* item_id = (LLUUID*)user_data;
+ LLPreviewNotecard* preview = LLPreviewNotecard::getInstance(*item_id);
+ if( preview )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+
+ LLViewerTextEditor* previewEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(preview, "Notecard Editor");
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ if( !previewEditor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing notecard" << llendl;
+ }
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ previewEditor->setText(buffer);
+ }
+
+ previewEditor->makePristine();
+
+ LLInventoryItem* item = preview->getItem();
+ BOOL modifiable = item && gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ previewEditor->setEnabled(modifiable);
+ delete[] buffer;
+ preview->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("NotecardMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("NotecardNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadNotecard");
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ preview->mAssetStatus = PREVIEW_ASSET_ERROR;
+ }
+ }
+ delete item_id;
+}
+
+// static
+LLPreviewNotecard* LLPreviewNotecard::getInstance(const LLUUID& item_id)
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_id);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return (LLPreviewNotecard*)instance;
+}
+
+// static
+void LLPreviewNotecard::onClickSave(void* user_data)
+{
+ //llinfos << "LLPreviewNotecard::onBtnSave()" << llendl;
+ LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data;
+ if(preview)
+ {
+ preview->saveIfNeeded();
+ }
+}
+
+struct LLSaveNotecardInfo
+{
+ LLPreviewNotecard* mSelf;
+ LLUUID mItemUUID;
+ LLUUID mObjectUUID;
+ LLTransactionID mTransactionID;
+ LLPointer<LLInventoryItem> mCopyItem;
+ LLSaveNotecardInfo(LLPreviewNotecard* self, const LLUUID& item_id, const LLUUID& object_id,
+ const LLTransactionID& transaction_id, LLInventoryItem* copyitem) :
+ mSelf(self), mItemUUID(item_id), mObjectUUID(object_id), mTransactionID(transaction_id), mCopyItem(copyitem)
+ {
+ }
+};
+
+bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
+{
+ if(!gAssetStorage)
+ {
+ llwarns << "Not connected to an asset storage system." << llendl;
+ return false;
+ }
+
+
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Notecard Editor");
+
+ if(!editor->isPristine())
+ {
+ // We need to update the asset information
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_NOTECARD, LLVFile::APPEND);
+
+ LLString buffer;
+ if (!editor->exportBuffer(buffer))
+ {
+ return false;
+ }
+
+ editor->makePristine();
+
+ S32 size = buffer.length() + 1;
+ file.setMaxSize(size);
+ file.write((U8*)buffer.c_str(), size);
+
+ LLInventoryItem* item = getItem();
+ // save it out to database
+ if (item)
+ {
+
+ LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID, mObjectUUID,
+ tid, copyitem);
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_NOTECARD,
+ &onSaveComplete,
+ (void*)info,
+ FALSE);
+ }
+ }
+ return true;
+}
+
+// static
+void LLPreviewNotecard::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLSaveNotecardInfo* info = (LLSaveNotecardInfo*)user_data;
+ if(info && (0 == status))
+ {
+ if(info->mObjectUUID.isNull())
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for script " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl;
+ }
+ }
+ else
+ {
+ LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
+ LLViewerInventoryItem* item = NULL;
+ if(object)
+ {
+ item = (LLViewerInventoryItem*)object->getInventoryObject(info->mItemUUID);
+ }
+ if(object && item)
+ {
+ item->setAssetUUID(asset_uuid);
+ item->setTransactionID(info->mTransactionID);
+ object->updateInventory(item, TASK_INVENTORY_ITEM_KEY, false);
+ dialog_refresh_all();
+ }
+ else
+ {
+ gViewerWindow->alertXml("SaveNotecardFailObjectNotFound");
+ }
+ }
+ // Perform item copy to inventory
+ if (info->mCopyItem.notNull())
+ {
+ LLViewerTextEditor* editor = LLViewerUICtrlFactory::getViewerTextEditorByName(info->mSelf, "Notecard Editor");
+ if (editor)
+ {
+ editor->copyInventory(info->mCopyItem);
+ }
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewNotecard* previewp = (LLPreviewNotecard*)LLPreview::find(info->mItemUUID);
+ if (previewp && previewp->mCloseAfterSave)
+ {
+ previewp->close();
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving notecard: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveNotecardFailReason",args);
+ }
+
+ char uuid_string[UUID_STR_LENGTH];
+ asset_uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.tmp", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(filename);
+ delete info;
+}
+
+// static
+void LLPreviewNotecard::handleSaveChangesDialog(S32 option, void* userdata)
+{
+ LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
+ switch(option)
+ {
+ case 0: // "Yes"
+ self->mCloseAfterSave = TRUE;
+ LLPreviewNotecard::onClickSave((void*)self);
+ break;
+
+ case 1: // "No"
+ self->mForceClose = TRUE;
+ self->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+void LLPreviewNotecard::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPreview::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("NotecardEditorRect", mRect);
+ }
+}
+
+// EOF
diff --git a/indra/newview/llpreviewnotecard.h b/indra/newview/llpreviewnotecard.h
new file mode 100644
index 0000000000..3bb3da5f54
--- /dev/null
+++ b/indra/newview/llpreviewnotecard.h
@@ -0,0 +1,84 @@
+/**
+ * @file llpreviewnotecard.h
+ * @brief LLPreviewNotecard class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWNOTECARD_H
+#define LL_LLPREVIEWNOTECARD_H
+
+#include "llpreview.h"
+#include "llassetstorage.h"
+#include "lliconctrl.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLPreviewNotecard
+//
+// This class allows us to edit notecards
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerTextEditor;
+class LLButton;
+
+class LLPreviewNotecard : public LLPreview
+{
+public:
+ LLPreviewNotecard(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_id,
+ const LLUUID& object_id = LLUUID::null,
+ const LLUUID& asset_id = LLUUID::null,
+ BOOL show_keep_discard = FALSE);
+
+ // llpreview
+ virtual bool saveItem(LLPointer<LLInventoryItem>* itemptr);
+
+ // llview
+ virtual void draw();
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ virtual void setEnabled( BOOL enabled );
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ // llfloater
+ virtual BOOL canClose();
+
+ // llpanel
+ virtual BOOL postBuild();
+
+ // reach into the text editor, and grab the drag item
+ const LLInventoryItem* getDragItem();
+
+
+protected:
+
+ virtual void loadAsset();
+ bool saveIfNeeded(LLInventoryItem* copyitem = NULL);
+
+ static LLPreviewNotecard* getInstance(const LLUUID& uuid);
+
+ static void onLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+
+ static void onClickSave(void* data);
+
+ static void onSaveComplete(const LLUUID& asset_uuid,
+ void* user_data,
+ S32 status);
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+
+protected:
+ LLViewerTextEditor* mEditor;
+ LLButton* mSaveBtn;
+
+ LLUUID mAssetID;
+
+ LLUUID mNotecardItemID;
+ LLUUID mObjectID;
+};
+
+
+#endif // LL_LLPREVIEWNOTECARD_H
diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp
new file mode 100644
index 0000000000..175824d866
--- /dev/null
+++ b/indra/newview/llpreviewscript.cpp
@@ -0,0 +1,1984 @@
+/**
+ * @file llpreviewscript.cpp
+ * @brief LLPreviewScript class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewscript.h"
+
+#include "llassetstorage.h"
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "lldir.h"
+#include "llinventorymodel.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+
+#include "llresmgr.h"
+#include "llscrollbar.h"
+#include "llscrollcontainer.h"
+#include "llscrolllistctrl.h"
+#include "llslider.h"
+#include "lscript_rt_interface.h"
+#include "lscript_export.h"
+#include "lltextbox.h"
+#include "lltooldraganddrop.h"
+#include "llvfile.h"
+
+#include "llagent.h"
+#include "llnotify.h"
+#include "llmenugl.h"
+#include "roles_constants.h"
+#include "llselectmgr.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llkeyboard.h"
+#include "llscrollcontainer.h"
+#include "llcheckboxctrl.h"
+#include "llselectmgr.h"
+#include "lltooldraganddrop.h"
+#include "llscrolllistctrl.h"
+#include "lltextbox.h"
+#include "llslider.h"
+#include "viewer.h"
+#include "lldir.h"
+#include "llcombobox.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerwindow.h"
+#include "llvieweruictrlfactory.h"
+#include "lluictrlfactory.h"
+
+#include "viewer.h"
+
+#include "llpanelinventory.h"
+
+
+const char HELLO_LSL[] =
+ "default\n"
+ "{\n"
+ " state_entry()\n"
+ " {\n"
+ " llSay(0, \"Hello, Avatar!\");\n"
+ " }\n"
+ "\n"
+ " touch_start(integer total_number)\n"
+ " {\n"
+ " llSay(0, \"Touched.\");\n"
+ " }\n"
+ "}\n";
+const char HELP_LSL[] = "lsl_guide.html";
+
+const char DEFAULT_SCRIPT_NAME[] = "New Script";
+const char DEFAULT_SCRIPT_DESC[] = "(No Description)";
+
+const char ENABLED_RUNNING_CHECKBOX_LABEL[] = "Running";
+const char DISABLED_RUNNING_CHECKBOX_LABEL[] = "Public Objects cannot run scripts";
+
+// Description and header information
+
+const S32 SCRIPT_BORDER = 4;
+const S32 SCRIPT_PAD = 5;
+const S32 SCRIPT_BUTTON_WIDTH = 128;
+const S32 SCRIPT_BUTTON_HEIGHT = 24; // HACK: Use BTN_HEIGHT where possible.
+const S32 LINE_COLUMN_HEIGHT = 14;
+const S32 BTN_PAD = 8;
+
+const S32 SCRIPT_EDITOR_MIN_HEIGHT = 2 * SCROLLBAR_SIZE + 2 * LLPANEL_BORDER_WIDTH + 128;
+
+const S32 SCRIPT_MIN_WIDTH =
+ 2 * SCRIPT_BORDER +
+ 2 * SCRIPT_BUTTON_WIDTH +
+ SCRIPT_PAD + RESIZE_HANDLE_WIDTH +
+ SCRIPT_PAD;
+
+const S32 SCRIPT_MIN_HEIGHT =
+ 2 * SCRIPT_BORDER +
+ 3*(SCRIPT_BUTTON_HEIGHT + SCRIPT_PAD) +
+ LINE_COLUMN_HEIGHT +
+ SCRIPT_EDITOR_MIN_HEIGHT;
+
+const S32 MAX_EXPORT_SIZE = 1000;
+
+const S32 SCRIPT_SEARCH_WIDTH = 300;
+const S32 SCRIPT_SEARCH_HEIGHT = 120;
+const S32 SCRIPT_SEARCH_LABEL_WIDTH = 50;
+const S32 SCRIPT_SEARCH_BUTTON_WIDTH = 80;
+const S32 TEXT_EDIT_COLUMN_HEIGHT = 16;
+/// ---------------------------------------------------------------------------
+/// LLFloaterScriptSearch
+/// ---------------------------------------------------------------------------
+class LLFloaterScriptSearch : public LLFloater
+{
+public:
+ LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core);
+ ~LLFloaterScriptSearch();
+
+ static void show(LLScriptEdCore* editor_core);
+ static void onBtnSearch(void* userdata);
+ void handleBtnSearch();
+
+ static void onBtnReplace(void* userdata);
+ void handleBtnReplace();
+
+ static void onBtnReplaceAll(void* userdata);
+ void handleBtnReplaceAll();
+
+ LLScriptEdCore* getEditorCore() { return mEditorCore; }
+ static LLFloaterScriptSearch* getInstance() { return sInstance; }
+
+ void open();
+
+private:
+
+ LLScriptEdCore* mEditorCore;
+
+ static LLFloaterScriptSearch* sInstance;
+};
+
+LLFloaterScriptSearch* LLFloaterScriptSearch::sInstance = NULL;
+
+LLFloaterScriptSearch::LLFloaterScriptSearch(std::string title, LLRect rect, LLScriptEdCore* editor_core)
+ : LLFloater("script search",rect,title), mEditorCore(editor_core)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_script_search.xml");
+
+ childSetAction("search_btn", onBtnSearch,this);
+ childSetAction("replace_btn", onBtnReplace,this);
+ childSetAction("replace_all_btn", onBtnReplaceAll,this);
+
+ setDefaultBtn("search_btn");
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+ sInstance = this;
+
+ childSetFocus("search_text", TRUE);
+}
+
+//static
+void LLFloaterScriptSearch::show(LLScriptEdCore* editor_core)
+{
+ if (sInstance && sInstance->mEditorCore && sInstance->mEditorCore != editor_core)
+ {
+ sInstance->close();
+ delete sInstance;
+ }
+
+ if (!sInstance)
+ {
+ S32 left = 0;
+ S32 top = 0;
+ gFloaterView->getNewFloaterPosition(&left,&top);
+
+ // sInstance will be assigned in the constructor.
+ new LLFloaterScriptSearch("Script Search",LLRect(left,top,left + SCRIPT_SEARCH_WIDTH,top - SCRIPT_SEARCH_HEIGHT),editor_core);
+ }
+
+ sInstance->open();
+}
+
+LLFloaterScriptSearch::~LLFloaterScriptSearch()
+{
+ sInstance = NULL;
+}
+
+// static
+void LLFloaterScriptSearch::onBtnSearch(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnSearch();
+}
+
+void LLFloaterScriptSearch::handleBtnSearch()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->selectNext(childGetText("search_text"), caseChk->get());
+}
+
+// static
+void LLFloaterScriptSearch::onBtnReplace(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnReplace();
+}
+
+void LLFloaterScriptSearch::handleBtnReplace()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->replaceText(childGetText("search_text"), childGetText("replace_text"), caseChk->get());
+}
+
+// static
+void LLFloaterScriptSearch::onBtnReplaceAll(void *userdata)
+{
+ LLFloaterScriptSearch* self = (LLFloaterScriptSearch*)userdata;
+ self->handleBtnReplaceAll();
+}
+
+void LLFloaterScriptSearch::handleBtnReplaceAll()
+{
+ LLCheckBoxCtrl* caseChk = LLUICtrlFactory::getCheckBoxByName(this,"case_text");
+ mEditorCore->mEditor->replaceTextAll(childGetText("search_text"), childGetText("replace_text"), caseChk->get());
+}
+
+void LLFloaterScriptSearch::open()
+{
+ LLFloater::open();
+ childSetFocus("search_text", TRUE);
+}
+/// ---------------------------------------------------------------------------
+/// LLScriptEdCore
+/// ---------------------------------------------------------------------------
+
+LLScriptEdCore::LLScriptEdCore(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& sample,
+ const std::string& help,
+ const LLViewHandle& floater_handle,
+ void (*load_callback)(void*),
+ void (*save_callback)(void*, BOOL),
+ void* userdata,
+ S32 bottom_pad)
+ :
+ LLPanel( "name", rect ),
+ mSampleText(sample),
+ mHelpFile ( help ),
+ mEditor( NULL ),
+ mLoadCallback( load_callback ),
+ mSaveCallback( save_callback ),
+ mUserdata( userdata ),
+ mForceClose( FALSE )
+{
+ setFollowsAll();
+ setBorderVisible(FALSE);
+
+
+ gUICtrlFactory->buildPanel(this, "floater_script_ed_panel.xml");
+
+ mErrorList = LLUICtrlFactory::getScrollListByName(this, "lsl errors");
+
+ mFunctions = LLUICtrlFactory::getComboBoxByName(this, "Insert...");
+
+ childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this);
+ mFunctions->setLabel("Insert...");
+
+ mEditor = LLViewerUICtrlFactory::getViewerTextEditorByName(this, "Script Editor");
+ mEditor->setReadOnlyBgColor(gColors.getColor( "ScriptBgReadOnlyColor" ) );
+ mEditor->setFollowsAll();
+ mEditor->setHandleEditKeysDirectly(TRUE);
+ mEditor->setEnabled(TRUE);
+ mEditor->setWordWrap(TRUE);
+
+ LLDynamicArray<const char*> funcs;
+ LLDynamicArray<const char*> tooltips;
+ for (S32 i = 0; i < gScriptLibrary.mNextNumber; i++)
+ {
+ // Make sure this isn't a god only function, or the agent is a god.
+ if (!gScriptLibrary.mFunctions[i]->mGodOnly || gAgent.isGodlike())
+ {
+ funcs.put(gScriptLibrary.mFunctions[i]->mName);
+ tooltips.put(gScriptLibrary.mFunctions[i]->mDesc);
+ }
+ }
+ LLColor3 color(0.5f, 0.0f, 0.15f);
+
+ mEditor->loadKeywords(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"keywords.ini"), funcs, tooltips, color);
+
+
+ LLKeywordToken *token;
+ LLKeywords::word_token_map_t::iterator token_it;
+ for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it)
+ {
+ token = token_it->second;
+ if (token->mColor == color)
+ mFunctions->add(wstring_to_utf8str(token->mToken));
+ }
+
+ for (token_it = mEditor->mKeywords.mWordTokenMap.begin(); token_it != mEditor->mKeywords.mWordTokenMap.end(); ++token_it)
+ {
+ token = token_it->second;
+ if (token->mColor != color)
+ mFunctions->add(wstring_to_utf8str(token->mToken));
+ }
+
+
+ childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this);
+ childSetAction("Save_btn", onBtnSave,this);
+
+ initMenu();
+
+
+ // Do the work that addTabPanel() normally does.
+ //LLRect tab_panel_rect( 0, mRect.getHeight(), mRect.getWidth(), 0 );
+ //tab_panel_rect.stretch( -LLPANEL_BORDER_WIDTH );
+ //mCodePanel->setFollowsAll();
+ //mCodePanel->translate( tab_panel_rect.mLeft - mCodePanel->getRect().mLeft, tab_panel_rect.mBottom - mCodePanel->getRect().mBottom);
+ //mCodePanel->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
+
+}
+
+LLScriptEdCore::~LLScriptEdCore()
+{
+ deleteBridges();
+
+ // If the search window is up for this editor, close it.
+ LLFloaterScriptSearch* script_search = LLFloaterScriptSearch::getInstance();
+ if (script_search && script_search->getEditorCore() == this)
+ {
+ script_search->close();
+ delete script_search;
+ }
+}
+
+void LLScriptEdCore::initMenu()
+{
+
+ LLMenuItemCallGL* menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Save");
+ menuItem->setMenuCallback(onBtnSave, this);
+ menuItem->setEnabledCallback(hasChanged);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Revert All Changes");
+ menuItem->setMenuCallback(onBtnUndoChanges, this);
+ menuItem->setEnabledCallback(hasChanged);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Undo");
+ menuItem->setMenuCallback(onUndoMenu, this);
+ menuItem->setEnabledCallback(enableUndoMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Redo");
+ menuItem->setMenuCallback(onRedoMenu, this);
+ menuItem->setEnabledCallback(enableRedoMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Cut");
+ menuItem->setMenuCallback(onCutMenu, this);
+ menuItem->setEnabledCallback(enableCutMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Copy");
+ menuItem->setMenuCallback(onCopyMenu, this);
+ menuItem->setEnabledCallback(enableCopyMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Paste");
+ menuItem->setMenuCallback(onPasteMenu, this);
+ menuItem->setEnabledCallback(enablePasteMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Select All");
+ menuItem->setMenuCallback(onSelectAllMenu, this);
+ menuItem->setEnabledCallback(enableSelectAllMenu);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Search / Replace...");
+ menuItem->setMenuCallback(onSearchMenu, this);
+ menuItem->setEnabledCallback(NULL);
+
+ menuItem = LLUICtrlFactory::getMenuItemCallByName(this, "Help...");
+ menuItem->setMenuCallback(onBtnHelp, this);
+ menuItem->setEnabledCallback(NULL);
+
+}
+
+BOOL LLScriptEdCore::hasChanged(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+
+ return !self->mEditor->isPristine();
+}
+
+void LLScriptEdCore::draw()
+{
+ BOOL script_changed = !mEditor->isPristine();
+ childSetEnabled("Save_btn", script_changed);
+
+ if( mEditor->hasFocus() )
+ {
+ S32 line = 0;
+ S32 column = 0;
+ mEditor->getCurrentLineAndColumn( &line, &column, FALSE ); // don't include wordwrap
+ char cursor_pos[STD_STRING_BUF_SIZE];
+ sprintf( cursor_pos, "Line %d, Column %d", line, column );
+ childSetText("line_col", cursor_pos);
+ }
+ else
+ {
+ childSetText("line_col", "");
+ }
+
+ LLPanel::draw();
+}
+
+BOOL LLScriptEdCore::canClose()
+{
+ if(mForceClose || mEditor->isPristine())
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Bring up view-modal dialog: Save changes? Yes, No, Cancel
+ gViewerWindow->alertXml("SaveChanges", LLScriptEdCore::handleSaveChangesDialog, this);
+ return FALSE;
+ }
+}
+
+// static
+void LLScriptEdCore::handleSaveChangesDialog( S32 option, void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ switch( option )
+ {
+ case 0: // "Yes"
+ // close after saving
+ LLScriptEdCore::doSave( self, TRUE );
+ break;
+
+ case 1: // "No"
+ self->mForceClose = TRUE;
+ // This will close immediately because mForceClose is true, so we won't
+ // infinite loop with these dialogs. JC
+ ((LLFloater*) self->getParent())->close();
+ break;
+
+ case 2: // "Cancel"
+ default:
+ // If we were quitting, we didn't really mean it.
+ app_abort_quit();
+ break;
+ }
+}
+
+// static
+void LLScriptEdCore::onHelpWebDialog(S32 option, void* userdata)
+{
+ LLScriptEdCore* corep = (LLScriptEdCore*)userdata;
+
+ switch(option)
+ {
+ case 0:
+ load_url_local_file(corep->mHelpFile.c_str());
+ break;
+ default:
+ break;
+ }
+}
+
+// static
+void LLScriptEdCore::onBtnHelp(void* userdata)
+{
+ gViewerWindow->alertXml("WebLaunchLSLGuide",
+ onHelpWebDialog,
+ userdata);
+}
+
+// static
+void LLScriptEdCore::onBtnInsertSample(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ // Insert sample code
+ self->mEditor->selectAll();
+ self->mEditor->cut();
+ self->mEditor->insertText(self->mSampleText);
+}
+
+// static
+void LLScriptEdCore::onBtnInsertFunction(LLUICtrl *ui, void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ // Insert sample code
+ if(self->mEditor->getEnabled())
+ {
+ self->mEditor->insertText(self->mFunctions->getSimple());
+ }
+ self->mEditor->setFocus(TRUE);
+}
+
+// static
+void LLScriptEdCore::doSave( void* userdata, BOOL close_after_save )
+{
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_LSL_SAVE_COUNT );
+ }
+
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+
+ if( self->mSaveCallback )
+ {
+ self->mSaveCallback( self->mUserdata, close_after_save );
+ }
+}
+
+// static
+void LLScriptEdCore::onBtnSave(void* data)
+{
+ // do the save, but don't close afterwards
+ doSave(data, FALSE);
+}
+
+// static
+void LLScriptEdCore::onBtnUndoChanges( void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ if( !self->mEditor->tryToRevertToPristineState() )
+ {
+ gViewerWindow->alertXml("ScriptCannotUndo",
+ LLScriptEdCore::handleReloadFromServerDialog, self);
+ }
+}
+
+void LLScriptEdCore::onSearchMenu(void* userdata)
+{
+ LLScriptEdCore* sec = (LLScriptEdCore*)userdata;
+ LLFloaterScriptSearch::show(sec);
+}
+
+// static
+void LLScriptEdCore::onUndoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->undo();
+}
+
+// static
+void LLScriptEdCore::onRedoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->redo();
+}
+
+// static
+void LLScriptEdCore::onCutMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->cut();
+}
+
+// static
+void LLScriptEdCore::onCopyMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->copy();
+}
+
+// static
+void LLScriptEdCore::onPasteMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->paste();
+}
+
+// static
+void LLScriptEdCore::onSelectAllMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->selectAll();
+}
+
+// static
+void LLScriptEdCore::onDeselectMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return;
+ self->mEditor->deselect();
+}
+
+// static
+BOOL LLScriptEdCore::enableUndoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canUndo();
+}
+
+// static
+BOOL LLScriptEdCore::enableRedoMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canRedo();
+}
+
+// static
+BOOL LLScriptEdCore::enableCutMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canCut();
+}
+
+// static
+BOOL LLScriptEdCore::enableCopyMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canCopy();
+}
+
+// static
+BOOL LLScriptEdCore::enablePasteMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canPaste();
+}
+
+// static
+BOOL LLScriptEdCore::enableSelectAllMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canSelectAll();
+}
+
+// static
+BOOL LLScriptEdCore::enableDeselectMenu(void* userdata)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)userdata;
+ if (!self || !self->mEditor) return FALSE;
+ return self->mEditor->canDeselect();
+}
+
+// static
+void LLScriptEdCore::onErrorList(LLUICtrl*, void* user_data)
+{
+ LLScriptEdCore* self = (LLScriptEdCore*)user_data;
+ LLScrollListItem* item = self->mErrorList->getFirstSelected();
+ if(item)
+ {
+ // *FIX: This fucked up little hack is here because we don't
+ // have a grep library. This is very brittle code.
+ S32 row = 0;
+ S32 column = 0;
+ const LLScrollListCell* cell = item->getColumn(0);
+ LLString line(cell->getText());
+ line.erase(0, 1);
+ LLString::replaceChar(line, ',',' ');
+ LLString::replaceChar(line, ')',' ');
+ sscanf(line.c_str(), "%d %d", &row, &column);
+ //llinfos << "LLScriptEdCore::onErrorList() - " << row << ", "
+ //<< column << llendl;
+ self->mEditor->setCursor(row, column);
+ self->mEditor->setFocus(TRUE);
+ }
+}
+
+// static
+void LLScriptEdCore::handleReloadFromServerDialog( S32 option, void* userdata )
+{
+ LLScriptEdCore* self = (LLScriptEdCore*) userdata;
+ switch( option )
+ {
+ case 0: // "Yes"
+ if( self->mLoadCallback )
+ {
+ self->mEditor->setText( "Loading..." );
+ self->mLoadCallback( self->mUserdata );
+ }
+ break;
+
+ case 1: // "No"
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+}
+
+void LLScriptEdCore::selectFirstError()
+{
+ // Select the first item;
+ mErrorList->selectFirstItem();
+ onErrorList(mErrorList, this);
+}
+
+
+struct LLEntryAndEdCore
+{
+ LLScriptEdCore* mCore;
+ LLEntryAndEdCore(LLScriptEdCore* core) :
+ mCore(core)
+ {}
+};
+
+void LLScriptEdCore::deleteBridges()
+{
+ S32 count = mBridges.count();
+ LLEntryAndEdCore* eandc;
+ for(S32 i = 0; i < count; i++)
+ {
+ eandc = mBridges.get(i);
+ delete eandc;
+ mBridges[i] = NULL;
+ }
+ mBridges.reset();
+}
+
+// virtual
+BOOL LLScriptEdCore::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if(getVisible() && getEnabled())
+ {
+ if(('S' == key) && (MASK_CONTROL == (mask & MASK_CONTROL)))
+ {
+ if(mSaveCallback)
+ {
+ // don't close after saving
+ mSaveCallback(mUserdata, FALSE);
+ }
+
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/// ---------------------------------------------------------------------------
+/// LLPreviewLSL
+/// ---------------------------------------------------------------------------
+
+struct LLScriptSaveInfo
+{
+ LLUUID mItemUUID;
+ LLString mDescription;
+ LLTransactionID mTransactionID;
+
+ LLScriptSaveInfo(const LLUUID& uuid, const LLString& desc, LLTransactionID tid) :
+ mItemUUID(uuid), mDescription(desc), mTransactionID(tid) {}
+};
+
+
+
+//static
+void* LLPreviewLSL::createScriptEdPanel(void* userdata)
+{
+
+ LLPreviewLSL *self = (LLPreviewLSL*)userdata;
+
+ self->mScriptEd = new LLScriptEdCore("script panel",
+ LLRect(),
+ HELLO_LSL,
+ HELP_LSL,
+ self->mViewHandle,
+ LLPreviewLSL::onLoad,
+ LLPreviewLSL::onSave,
+ self,
+ 0);
+
+ return self->mScriptEd;
+}
+
+
+LLPreviewLSL::LLPreviewLSL(const std::string& name, const LLRect& rect,
+ const std::string& title, const LLUUID& item_id )
+: LLPreview( name, rect, title, item_id, LLUUID::null, TRUE,
+ SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT ),
+ mPendingUploads(0)
+{
+
+ LLRect curRect = rect;
+
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["script panel"] = LLCallbackMap(LLPreviewLSL::createScriptEdPanel, this);
+
+
+ gUICtrlFactory->buildFloater(this,"floater_script_preview.xml", &factory_map);
+
+ moveResizeHandleToFront();
+
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ LLMultiFloater* hostp = getHost();
+
+ if (!sHostp && !hostp && getAssetStatus() == PREVIEW_ASSET_UNLOADED)
+ {
+ loadAsset();
+ }
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ reshape(curRect.getWidth(), curRect.getHeight(), TRUE);
+ setRect(curRect);
+ }
+}
+
+
+void LLPreviewLSL::loadAsset()
+{
+ // *HACK: we poke into inventory to see if it's there, and if so,
+ // then it might be part of the inventory library. If it's in the
+ // library, then you can see the script, but not modify it.
+ LLInventoryItem* item = gInventory.getItem(mItemUUID);
+ BOOL is_library = item
+ && !gInventory.isObjectDescendentOf(mItemUUID,
+ gAgent.getInventoryRootID());
+ if(!item)
+ {
+ // do the more generic search.
+ getItem();
+ }
+ if(item && !(item->getAssetUUID().isNull()))
+ {
+ BOOL is_copyable = gAgent.allowOperation(PERM_COPY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ BOOL is_modifiable = gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE);
+ if (gAgent.isGodlike() || (is_copyable && (is_modifiable || is_library)))
+ {
+ LLUUID* new_uuid = new LLUUID(mItemUUID);
+ gAssetStorage->getInvItemAsset(LLHost::invalid,
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ LLUUID::null,
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &LLPreviewLSL::onLoadComplete,
+ (void*)new_uuid,
+ TRUE);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+ }
+ else
+ {
+ mScriptEd->mEditor->setText("You are not allowed to view this script.");
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mEditor->setEnabled(FALSE);
+ mScriptEd->mFunctions->setEnabled(FALSE);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ childSetVisible("lock", !is_modifiable);
+ mScriptEd->childSetEnabled("Insert...", is_modifiable);
+ }
+ else
+ {
+ mScriptEd->mEditor->setText(HELLO_LSL);
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+}
+
+
+BOOL LLPreviewLSL::canClose()
+{
+ return mScriptEd->canClose();
+}
+
+//override the llpreview open which attempts to load asset, load after xml ui made
+void LLPreviewLSL::open()
+{
+ LLFloater::open();
+}
+
+// static
+void LLPreviewLSL::onLoad(void* userdata)
+{
+ LLPreviewLSL* self = (LLPreviewLSL*)userdata;
+ self->loadAsset();
+}
+
+// static
+void LLPreviewLSL::onSave(void* userdata, BOOL close_after_save)
+{
+ LLPreviewLSL* self = (LLPreviewLSL*)userdata;
+ self->mCloseAfterSave = close_after_save;
+ self->saveIfNeeded();
+}
+
+
+// Save needs to compile the text in the buffer. If the compile
+// succeeds, then save both assets out to the database. If the compile
+// fails, go ahead and save the text anyway so that the user doesn't
+// get too fucked.
+void LLPreviewLSL::saveIfNeeded()
+{
+ // llinfos << "LLPreviewLSL::save()" << llendl;
+ if(!mScriptEd->mEditor->isPristine())
+ {
+ mPendingUploads = 0;
+ mScriptEd->mErrorList->deleteAllItems();
+ mScriptEd->mEditor->makePristine();
+
+ // We need to update the asset information
+ LLTransactionID tid;
+ LLAssetID uuid;
+ tid.generate();
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ char uuid_string[UUID_STR_LENGTH];
+ uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ if(!fp)
+ {
+ llwarns << "Unable to write to " << filename << llendl;
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall);
+ mScriptEd->mErrorList->addItem(item);
+ return;
+ }
+
+ LLString utf8text = mScriptEd->mEditor->getText();
+ //fprintf(fp, "%s|%s\n", LLAssetType::lookup(LLAssetType::AT_LSL_TEXT),
+ //uuid_string);
+ //fprintf(fp,"{\n%s}\n", text.c_str());
+ fputs(utf8text.c_str(), fp);
+ fclose(fp);
+ fp = NULL;
+
+ // also write it out to the vfs for upload
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND);
+ S32 size = utf8text.length() + 1;
+
+ file.setMaxSize(size);
+ file.write((U8*)utf8text.c_str(), size);
+
+ LLInventoryItem *inv_item = getItem();
+
+ // save it out to database
+ if(gAssetStorage && inv_item)
+ {
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLScriptSaveInfo* info = NULL;
+
+ LLLineEditor* descEditor = LLUICtrlFactory::getLineEditorByName(this, "desc");
+
+ info = new LLScriptSaveInfo(mItemUUID,
+ descEditor->getText(),
+ tid);
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &LLPreviewLSL::onSaveComplete, info);
+ }
+
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLScrollListItem* item = NULL;
+ const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ llinfos << "Compile failed!" << llendl;
+ //char command[256];
+ //sprintf(command, "type %s\n", err_filename);
+ //system(command);
+
+ // load the error file into the error scrolllist
+ if(NULL != (fp = LLFile::fopen(err_filename, "r")))
+ {
+ char buffer[MAX_STRING];
+ LLString line;
+ while(!feof(fp))
+ {
+
+ fgets(buffer, MAX_STRING, fp);
+ if(feof(fp))
+ {
+ break;
+ }
+ else if(!buffer)
+ {
+ continue;
+ }
+ else
+ {
+ line.assign(buffer);
+ LLString::stripNonprintable(line);
+ item = new LLScrollListItem();
+ item->addColumn(line, err_font);
+ mScriptEd->mErrorList->addItem(item);
+ }
+ }
+ fclose(fp);
+ mScriptEd->selectFirstError();
+ }
+ }
+ else
+ {
+ llinfos << "Compile worked!" << llendl;
+ if(gAssetStorage)
+ {
+ // move the compiled file into the vfs for transport
+ FILE* fp = LLFile::fopen(dst_filename, "rb");
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+ fp = NULL;
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLUUID* this_uuid = new LLUUID(mItemUUID);
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &LLPreviewLSL::onSaveBytecodeComplete,
+ (void**)this_uuid);
+ }
+ }
+
+ // get rid of any temp files left lying around
+ LLFile::remove(filename);
+ LLFile::remove(err_filename);
+ LLFile::remove(dst_filename);
+ }
+}
+
+
+// static
+void LLPreviewLSL::onSaveComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLScriptSaveInfo* info = reinterpret_cast<LLScriptSaveInfo*>(user_data);
+ if(0 == status)
+ {
+ if (info)
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
+ if(item)
+ {
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ new_item->setAssetUUID(asset_uuid);
+ new_item->setTransactionID(info->mTransactionID);
+ new_item->updateServer(FALSE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llwarns << "Inventory item for script " << info->mItemUUID
+ << " is no longer in agent inventory." << llendl
+ }
+
+ // Find our window and close it if requested.
+ LLPreviewLSL* self = (LLPreviewLSL*)LLPreview::find(info->mItemUUID);
+ if (self)
+ {
+ getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving script: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveScriptFailReason", args);
+ }
+ delete info;
+}
+
+// static
+void LLPreviewLSL::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLUUID* instance_uuid = (LLUUID*)user_data;
+ LLPreviewLSL* self = NULL;
+ if(instance_uuid)
+ {
+ self = LLPreviewLSL::getInstance(*instance_uuid);
+ }
+ if (0 == status)
+ {
+ if (self)
+ {
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Compile successful!", LLFontGL::sSansSerifSmall);
+ self->mScriptEd->mErrorList->addItem(item);
+
+ // Find our window and close it if requested.
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Problem saving LSL Bytecode (Preview)" << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("SaveBytecodeFailReason", args);
+ }
+ delete instance_uuid;
+}
+
+// static
+void LLPreviewLSL::onLoadComplete( LLVFS *vfs, const LLUUID& asset_uuid, LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLUUID* item_uuid = (LLUUID*)user_data;
+ LLPreviewLSL* preview = LLPreviewLSL::getInstance(*item_uuid);
+ if( preview )
+ {
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type);
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+ preview->mScriptEd->mEditor->setText(buffer);
+ preview->mScriptEd->mEditor->makePristine();
+ delete [] buffer;
+ LLInventoryItem* item = gInventory.getItem(*item_uuid);
+ BOOL is_modifiable = FALSE;
+ if(item
+ && gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ is_modifiable = TRUE;
+ }
+ preview->mScriptEd->mEditor->setEnabled(is_modifiable);
+ preview->mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("ScriptMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("ScriptNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadScript");
+ }
+
+ preview->mAssetStatus = PREVIEW_ASSET_ERROR;
+ llwarns << "Problem loading script: " << status << llendl;
+ }
+ }
+ delete item_uuid;
+}
+
+// static
+LLPreviewLSL* LLPreviewLSL::getInstance( const LLUUID& item_uuid )
+{
+ LLPreview* instance = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ instance = found_it->second;
+ }
+ return (LLPreviewLSL*)instance;
+}
+
+void LLPreviewLSL::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPreview::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("PreviewScriptRect", mRect);
+ }
+}
+
+/// ---------------------------------------------------------------------------
+/// LLLiveLSLEditor
+/// ---------------------------------------------------------------------------
+
+LLMap<LLUUID, LLLiveLSLEditor*> LLLiveLSLEditor::sInstances;
+
+
+
+//static
+void* LLLiveLSLEditor::createScriptEdPanel(void* userdata)
+{
+
+ LLLiveLSLEditor *self = (LLLiveLSLEditor*)userdata;
+
+ self->mScriptEd = new LLScriptEdCore("script ed panel",
+ LLRect(),
+ HELLO_LSL,
+ HELP_LSL,
+ self->mViewHandle,
+ &LLLiveLSLEditor::onLoad,
+ &LLLiveLSLEditor::onSave,
+ self,
+ 0);
+
+ return self->mScriptEd;
+}
+
+
+LLLiveLSLEditor::LLLiveLSLEditor(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& object_id,
+ const LLUUID& item_id) :
+ LLFloater(name, rect, title, TRUE, SCRIPT_MIN_WIDTH, SCRIPT_MIN_HEIGHT),
+ mObjectID(object_id),
+ mItemID(item_id),
+ mScriptEd(NULL),
+ mAskedForRunningInfo(FALSE),
+ mHaveRunningInfo(FALSE),
+ mCloseAfterSave(FALSE),
+ mPendingUploads(0)
+{
+
+
+ BOOL is_new = FALSE;
+ if(mItemID.isNull())
+ {
+ mItemID.generate();
+ is_new = TRUE;
+ }
+
+
+ LLLiveLSLEditor::sInstances.addData(mItemID ^ mObjectID, this);
+
+
+
+ LLCallbackMap::map_t factory_map;
+ factory_map["script ed panel"] = LLCallbackMap(LLLiveLSLEditor::createScriptEdPanel, this);
+
+ moveResizeHandleToFront();
+
+ gUICtrlFactory->buildFloater(this,"floater_live_lsleditor.xml", &factory_map);
+
+
+ childSetCommitCallback("running", LLLiveLSLEditor::onRunningCheckboxClicked, this);
+ childSetEnabled("running", FALSE);
+
+ childSetAction("Reset",&LLLiveLSLEditor::onReset,this);
+ childSetEnabled("Reset", TRUE);
+
+
+ mScriptEd->mEditor->makePristine();
+ loadAsset(is_new);
+ mScriptEd->mEditor->setFocus(TRUE);
+
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+
+ setTitle(title);
+
+}
+
+LLLiveLSLEditor::~LLLiveLSLEditor()
+{
+ LLLiveLSLEditor::sInstances.removeData(mItemID ^ mObjectID);
+}
+
+
+void LLLiveLSLEditor::loadAsset(BOOL is_new)
+{
+ //llinfos << "LLLiveLSLEditor::loadAsset()" << llendl;
+ if(!is_new)
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(object)
+ {
+ // HACK! we "know" that mItemID refers to a LLViewerInventoryItem...
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)object->getInventoryObject(mItemID);
+ if(item
+ && (gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || gAgent.isGodlike()))
+ {
+ mItem = new LLViewerInventoryItem(item);
+ //llinfos << "asset id " << mItem->getAssetUUID() << llendl;
+ }
+
+ if(!gAgent.isGodlike()
+ && (item
+ && (!gAgent.allowOperation(PERM_COPY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE)
+ || !gAgent.allowOperation(PERM_MODIFY,
+ item->getPermissions(), GP_OBJECT_MANIPULATE))))
+
+ {
+ mScriptEd->mEditor->setText("You are not allowed to view this script.");
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mEditor->setEnabled(FALSE);
+ }
+ else if(mItem.notNull() && mItem->getAssetUUID().notNull())
+ {
+ // request the text from the object
+ LLUUID* user_data = new LLUUID(mItemID ^ mObjectID);
+ gAssetStorage->getInvItemAsset(object->getRegion()->getHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ item->getPermissions().getOwner(),
+ object->getID(),
+ item->getUUID(),
+ item->getAssetUUID(),
+ item->getType(),
+ &LLLiveLSLEditor::onLoadComplete,
+ (void*)user_data,
+ TRUE);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_GetScriptRunning);
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, mItemID);
+ msg->sendReliable(object->getRegion()->getHost());
+ mAskedForRunningInfo = TRUE;
+ }
+ else
+ {
+ mScriptEd->mEditor->setText("");
+ mScriptEd->mEditor->makePristine();
+ }
+
+ if(item
+ && !gAgent.allowOperation(PERM_MODIFY, item->getPermissions(),
+ GP_OBJECT_MANIPULATE))
+ {
+ mScriptEd->mEditor->setEnabled(FALSE);
+ }
+ // This is commented out, because we don't completely
+ // handle script exports yet.
+ /*
+ // request the exports from the object
+ gMessageSystem->newMessage("GetScriptExports");
+ gMessageSystem->nextBlock("ScriptBlock");
+ gMessageSystem->addUUID("AgentID", gAgent.getID());
+ U32 local_id = object->getLocalID();
+ gMessageSystem->addData("LocalID", &local_id);
+ gMessageSystem->addUUID("ItemID", mItemID);
+ LLHost host(object->getRegion()->getIP(),
+ object->getRegion()->getPort());
+ gMessageSystem->sendReliable(host);
+ */
+ }
+ }
+ else
+ {
+ mScriptEd->mEditor->setText(HELLO_LSL);
+ //mScriptEd->mEditor->setText("");
+ //mScriptEd->mEditor->makePristine();
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, gAgent.getGroupID());
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ mItem = new LLViewerInventoryItem(mItemID,
+ mObjectID,
+ perm,
+ LLUUID::null,
+ LLAssetType::AT_LSL_TEXT,
+ LLInventoryType::IT_LSL,
+ DEFAULT_SCRIPT_NAME,
+ DEFAULT_SCRIPT_DESC,
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ time_corrected());
+ }
+}
+
+// static
+void LLLiveLSLEditor::onLoadComplete(LLVFS *vfs, const LLUUID& asset_id,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ LLLiveLSLEditor* instance = NULL;
+ LLUUID* xored_id = (LLUUID*)user_data;
+
+ if( LLLiveLSLEditor::sInstances.checkData(*xored_id) )
+ {
+ if( LL_ERR_NOERR == status )
+ {
+ instance = LLLiveLSLEditor::sInstances[*xored_id];
+ instance->loadScriptText(vfs, asset_id, type);
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ LLNotifyBox::showXml("ScriptMissing");
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ LLNotifyBox::showXml("ScriptNoPermissions");
+ }
+ else
+ {
+ LLNotifyBox::showXml("UnableToLoadScript");
+ }
+ }
+ }
+
+ delete xored_id;
+}
+
+void LLLiveLSLEditor::loadScriptText(const char* filename)
+{
+ FILE* file = LLFile::fopen(filename, "rb");
+ if(file)
+ {
+ // read in the whole file
+ fseek(file, 0L, SEEK_END);
+ S32 file_length = ftell(file);
+ fseek(file, 0L, SEEK_SET);
+ char* buffer = new char[file_length+1];
+ fread(buffer, file_length, 1, file);
+ fclose(file);
+ buffer[file_length] = 0;
+ mScriptEd->mEditor->setText(buffer);
+ mScriptEd->mEditor->makePristine();
+ delete[] buffer;
+ }
+ else
+ {
+ llwarns << "Error opening " << filename << llendl;
+ }
+}
+
+void LLLiveLSLEditor::loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type)
+{
+ LLVFile file(vfs, uuid, type);
+ S32 file_length = file.getSize();
+ char *buffer = new char[file_length + 1];
+ file.read((U8*)buffer, file_length);
+
+ if (file.getLastBytesRead() != file_length ||
+ file_length <= 0)
+ {
+ llwarns << "Error reading " << uuid << ":" << type << llendl;
+ }
+
+ buffer[file_length] = '\0';
+
+ mScriptEd->mEditor->setText(buffer);
+ mScriptEd->mEditor->makePristine();
+ delete[] buffer;
+
+}
+
+
+void LLLiveLSLEditor::onRunningCheckboxClicked( LLUICtrl*, void* userdata )
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
+ LLViewerObject* object = gObjectList.findObject( self->mObjectID );
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(self, "running");
+ BOOL running = runningCheckbox->get();
+ //self->mRunningCheckbox->get();
+ if( object )
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SetScriptRunning);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
+ msg->addBOOLFast(_PREHASH_Running, running);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ else
+ {
+ runningCheckbox->set(!running);
+ gViewerWindow->alertXml("CouldNotStartStopScript");
+ }
+}
+
+void LLLiveLSLEditor::onReset(void *userdata)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*) userdata;
+
+ LLViewerObject* object = gObjectList.findObject( self->mObjectID );
+ if(object)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ScriptReset);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Script);
+ msg->addUUIDFast(_PREHASH_ObjectID, self->mObjectID);
+ msg->addUUIDFast(_PREHASH_ItemID, self->mItemID);
+ msg->sendReliable(object->getRegion()->getHost());
+ }
+ else
+ {
+ gViewerWindow->alertXml("CouldNotStartStopScript");
+ }
+}
+
+void LLLiveLSLEditor::draw()
+{
+ if(getVisible())
+ {
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running");
+ if(object && mAskedForRunningInfo && mHaveRunningInfo)
+ {
+ if(object->permAnyOwner())
+ {
+ runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(TRUE);
+ }
+ else
+ {
+ runningCheckbox->setLabel(DISABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(FALSE);
+ // *FIX: Set it to false so that the ui is correct for
+ // a box that is released to public. It could be
+ // incorrect after a release/claim cycle, but will be
+ // correct after clicking on it.
+ runningCheckbox->set(FALSE);
+ }
+ }
+ else if(!object)
+ {
+ // HACK: Display this information in the title bar.
+ // Really ought to put in main window.
+ setTitle("Script (object out of range)");
+ runningCheckbox->setEnabled(FALSE);
+ // object may have fallen out of range.
+ mHaveRunningInfo = FALSE;
+ }
+ LLFloater::draw();
+ }
+}
+
+struct LLLiveLSLSaveData
+{
+ LLLiveLSLSaveData(const LLUUID& id, const LLViewerInventoryItem* item, BOOL active);
+ LLUUID mObjectID;
+ LLPointer<LLViewerInventoryItem> mItem;
+ BOOL mActive;
+};
+
+LLLiveLSLSaveData::LLLiveLSLSaveData(const LLUUID& id,
+ const LLViewerInventoryItem* item,
+ BOOL active) :
+ mObjectID(id),
+ mActive(active)
+{
+ llassert(item);
+ mItem = new LLViewerInventoryItem(item);
+}
+
+void LLLiveLSLEditor::saveIfNeeded()
+{
+ llinfos << "LLLiveLSLEditor::saveIfNeeded()" << llendl;
+ LLViewerObject* object = gObjectList.findObject(mObjectID);
+ if(!object)
+ {
+ gViewerWindow->alertXml("SaveScriptFailObjectNotFound");
+ return;
+ }
+
+ // get the latest info about it. We used to be losing the script
+ // name on save, because the viewer object version of the item,
+ // and the editor version would get out of synch. Here's a good
+ // place to synch them back up.
+ // HACK! we "know" that mItemID refers to a LLInventoryItem...
+ LLInventoryItem* inv_item = (LLInventoryItem*)object->getInventoryObject(mItemID);
+ if(inv_item)
+ {
+ mItem->copy(inv_item);
+ }
+
+ // Don't need to save if we're pristine
+ if(mScriptEd->mEditor->isPristine())
+ {
+ return;
+ }
+
+ mPendingUploads = 0;
+
+ // save the script
+ mScriptEd->mEditor->makePristine();
+ mScriptEd->mErrorList->deleteAllItems();
+
+ // set up the save on the local machine.
+ mScriptEd->mEditor->makePristine();
+ LLTransactionID tid;
+ LLAssetID uuid;
+ tid.generate();
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ mItem->setAssetUUID(uuid);
+ mItem->setTransactionID(tid);
+
+ // write out the data, and store it in the asset database
+ char uuid_string[UUID_STR_LENGTH];
+ uuid.toString(uuid_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ if(!fp)
+ {
+ llwarns << "Unable to write to " << filename << llendl;
+ LLScrollListItem* item = new LLScrollListItem();
+ item->addColumn("Error writing to local file. Is your hard drive full?", LLFontGL::sSansSerifSmall);
+ mScriptEd->mErrorList->addItem(item);
+ return;
+ }
+ LLString utf8text = mScriptEd->mEditor->getText();
+ fputs(utf8text.c_str(), fp);
+ fclose(fp);
+
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(this, "running");
+
+ // save it out to database
+ if(gAssetStorage)
+ {
+ // write it out to the vfs for upload
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_TEXT, LLVFile::APPEND);
+ S32 size = utf8text.length() + 1;
+
+ file.setMaxSize(size);
+ file.write((U8*)utf8text.c_str(), size);
+
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLLiveLSLSaveData* data = new LLLiveLSLSaveData(mObjectID,
+ mItem,
+ runningCheckbox->get());
+ gAssetStorage->storeAssetData(tid, LLAssetType::AT_LSL_TEXT, &onSaveTextComplete, (void*)data, FALSE);
+ }
+
+#if LL_WINDOWS
+ // This major hack was inserted because sometimes compilation
+ // would fail because it couldn't open this file... I decided
+ // to make a loop until open was successful. This seems to be
+ // a problem specific to ntfs.
+ fp = NULL;
+ const U32 MAX_TRIES = 20;
+ U32 tries = MAX_TRIES;
+ while((!fp) && --tries)
+ {
+ ms_sleep(17);
+ fp = LLFile::fopen(filename, "r");
+ if(!fp)
+ {
+ llwarns << "Trying to open the source file " << filename
+ << " again" << llendl;
+ }
+ else
+ {
+ fclose(fp);
+ }
+ }
+ fp = NULL;
+#endif
+
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ char err_filename[LL_MAX_PATH];
+ sprintf(err_filename, "%s.out", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLScrollListItem* item = NULL;
+ const LLFontGL* err_font = gResMgr->getRes(LLFONT_OCRA);
+ if(!lscript_compile(filename, dst_filename, err_filename, gAgent.isGodlike()))
+ {
+ // load the error file into the error scrolllist
+ llinfos << "Compile failed!" << llendl;
+ if(NULL != (fp = LLFile::fopen(err_filename, "r")))
+ {
+ char buffer[MAX_STRING];
+ LLString line;
+ while(!feof(fp))
+ {
+
+ fgets(buffer, MAX_STRING, fp);
+ if(feof(fp))
+ {
+ break;
+ }
+ else if(!buffer)
+ {
+ continue;
+ }
+ else
+ {
+ line.assign(buffer);
+ LLString::stripNonprintable(line);
+ item = new LLScrollListItem();
+ item->addColumn(line, err_font);
+ mScriptEd->mErrorList->addItem(item);
+ }
+ }
+ fclose(fp);
+ mScriptEd->selectFirstError();
+ // don't set the asset id, because we want to save the
+ // script, even though the compile failed.
+ //mItem->setAssetUUID(LLUUID::null);
+ object->saveScript(mItem, FALSE, false);
+ dialog_refresh_all();
+ }
+ }
+ else
+ {
+ llinfos << "Compile worked!" << llendl;
+ mScriptEd->mErrorList->addSimpleItem("Compile successful, saving...");
+ if(gAssetStorage)
+ {
+ llinfos << "LLLiveLSLEditor::saveAsset "
+ << mItem->getAssetUUID() << llendl;
+
+ // move the compiled file into the vfs for transport
+ FILE* fp = LLFile::fopen(dst_filename, "rb");
+ LLVFile file(gVFS, uuid, LLAssetType::AT_LSL_BYTECODE, LLVFile::APPEND);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+ fp = NULL;
+
+ getWindow()->incBusyCount();
+ mPendingUploads++;
+ LLLiveLSLSaveData* data = NULL;
+ data = new LLLiveLSLSaveData(mObjectID,
+ mItem,
+ runningCheckbox->get());
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_LSL_BYTECODE,
+ &LLLiveLSLEditor::onSaveBytecodeComplete,
+ (void*)data);
+ dialog_refresh_all();
+ }
+ }
+
+ // get rid of any temp files left lying around
+ LLFile::remove(filename);
+ LLFile::remove(err_filename);
+ LLFile::remove(dst_filename);
+
+ // If we successfully saved it, then we should be able to check/uncheck the running box!
+ runningCheckbox->setLabel(ENABLED_RUNNING_CHECKBOX_LABEL);
+ runningCheckbox->setEnabled(TRUE);
+}
+
+void LLLiveLSLEditor::onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
+
+ if (status)
+ {
+ llwarns << "Unable to save text for a script." << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveText", args);
+ }
+ else
+ {
+ LLLiveLSLEditor* self = sInstances.getIfThere(data->mItem->getUUID() ^ data->mObjectID);
+ if (self)
+ {
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ }
+ delete data;
+ data = NULL;
+}
+
+
+void LLLiveLSLEditor::onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status) // StoreAssetData callback (fixed)
+{
+ LLLiveLSLSaveData* data = (LLLiveLSLSaveData*)user_data;
+ if(!data)
+ return;
+ if(0 ==status)
+ {
+ llinfos << "LSL Bytecode saved" << llendl;
+ LLUUID xor_id = data->mItem->getUUID() ^ data->mObjectID;
+ LLLiveLSLEditor* self = sInstances.getIfThere(xor_id);
+ if(self)
+ {
+ // Tell the user that the compile worked.
+ self->mScriptEd->mErrorList->addSimpleItem("Save complete.");
+ // close the window if this completes both uploads
+ self->getWindow()->decBusyCount();
+ self->mPendingUploads--;
+ if (self->mPendingUploads <= 0
+ && self->mCloseAfterSave)
+ {
+ self->close();
+ }
+ }
+ LLViewerObject* object = gObjectList.findObject(data->mObjectID);
+ if(object)
+ {
+ object->saveScript(data->mItem, data->mActive, false);
+ dialog_refresh_all();
+ //LLToolDragAndDrop::dropScript(object, ids->first,
+ // LLAssetType::AT_LSL_TEXT, FALSE);
+ }
+ }
+ else
+ {
+ llinfos << "Problem saving LSL Bytecode (Live Editor)" << llendl;
+ llwarns << "Unable to save a compiled script." << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = std::string(LLAssetStorage::getErrorString(status));
+ gViewerWindow->alertXml("CompileQueueSaveBytecode", args);
+ }
+ char uuid_string[UUID_STR_LENGTH];
+ data->mItem->getAssetUUID().toString(uuid_string);
+ char dst_filename[LL_MAX_PATH];
+ sprintf(dst_filename, "%s.lso", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(dst_filename);
+ sprintf(dst_filename, "%s.lsl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_string).c_str());
+ LLFile::remove(dst_filename);
+ delete data;
+}
+
+BOOL LLLiveLSLEditor::canClose()
+{
+ return (mScriptEd->canClose());
+}
+
+// static
+void LLLiveLSLEditor::onLoad(void* userdata)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+ self->loadAsset();
+}
+
+// static
+void LLLiveLSLEditor::onSave(void* userdata, BOOL close_after_save)
+{
+ LLLiveLSLEditor* self = (LLLiveLSLEditor*)userdata;
+ self->mCloseAfterSave = close_after_save;
+ self->saveIfNeeded();
+}
+
+// static
+LLLiveLSLEditor* LLLiveLSLEditor::show(const LLUUID& script_id, const LLUUID& object_id)
+{
+ LLLiveLSLEditor* instance = NULL;
+ LLUUID xored_id = script_id ^ object_id;
+ if(LLLiveLSLEditor::sInstances.checkData(xored_id))
+ {
+ // Move the existing view to the front
+ instance = LLLiveLSLEditor::sInstances[xored_id];
+ instance->open();
+ }
+ return instance;
+}
+
+// static
+void LLLiveLSLEditor::hide(const LLUUID& script_id, const LLUUID& object_id)
+{
+ LLUUID xored_id = script_id ^ object_id;
+ if( LLLiveLSLEditor::sInstances.checkData( xored_id ) )
+ {
+ LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id];
+ if(instance->getParent())
+ {
+ instance->getParent()->removeChild(instance);
+ }
+ delete instance;
+ }
+}
+
+// static
+void LLLiveLSLEditor::processScriptRunningReply(LLMessageSystem* msg, void**)
+{
+ LLUUID item_id;
+ LLUUID object_id;
+ msg->getUUIDFast(_PREHASH_Script, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_Script, _PREHASH_ItemID, item_id);
+ LLUUID xored_id = item_id ^ object_id;
+ if(LLLiveLSLEditor::sInstances.checkData(xored_id))
+ {
+ LLLiveLSLEditor* instance = LLLiveLSLEditor::sInstances[xored_id];
+ instance->mHaveRunningInfo = TRUE;
+ BOOL running;
+ msg->getBOOLFast(_PREHASH_Script, _PREHASH_Running, running);
+ LLCheckBoxCtrl* runningCheckbox = LLUICtrlFactory::getCheckBoxByName(instance, "running");
+ runningCheckbox->set(running);
+ }
+}
+
+void LLLiveLSLEditor::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLFloater::reshape( width, height, called_from_parent );
+
+ if( !isMinimized() )
+ {
+ // So that next time you open a script it will have the same height and width
+ // (although not the same position).
+ gSavedSettings.setRect("PreviewScriptRect", mRect);
+ }
+}
diff --git a/indra/newview/llpreviewscript.h b/indra/newview/llpreviewscript.h
new file mode 100644
index 0000000000..228ca8c637
--- /dev/null
+++ b/indra/newview/llpreviewscript.h
@@ -0,0 +1,212 @@
+/**
+ * @file llpreviewscript.h
+ * @brief LLPreviewScript class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWSCRIPT_H
+#define LL_LLPREVIEWSCRIPT_H
+
+#include "lldarray.h"
+#include "llpreview.h"
+#include "lltabcontainer.h"
+#include "llinventory.h"
+#include "llcombobox.h"
+#include "lliconctrl.h"
+
+
+class LLMessageSystem;
+class LLTextEditor;
+class LLButton;
+class LLCheckBoxCtrl;
+class LLScrollListCtrl;
+class LLViewerObject;
+struct LLEntryAndEdCore;
+class LLMenuBarGL;
+class LLFloaterScriptSearch;
+
+// Inner, implementation class. LLPreviewScript and LLLiveScriptEditor each own one of these.
+class LLScriptEdCore : public LLPanel
+{
+ friend class LLPreviewScript;
+ friend class LLPreviewLSL;
+ friend class LLLiveScriptEditor;
+ friend class LLLiveLSLEditor;
+ friend class LLFloaterScriptSearch;
+
+public:
+ LLScriptEdCore(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& sample,
+ const std::string& help,
+ const LLViewHandle& floater_handle,
+ void (*load_callback)(void* userdata),
+ void (*save_callback)(void* userdata, BOOL close_after_save),
+ void* userdata,
+ S32 bottom_pad = 0); // pad below bottom row of buttons
+ ~LLScriptEdCore();
+
+ void initMenu();
+
+ virtual void draw();
+
+ BOOL canClose();
+
+ static void handleSaveChangesDialog(S32 option, void* userdata);
+ static void handleReloadFromServerDialog(S32 option, void* userdata);
+
+ static void onHelpWebDialog(S32 option, void* userdata);
+ static void onBtnHelp(void* userdata);
+ static void onBtnInsertSample(void*);
+ static void onBtnInsertFunction(LLUICtrl*, void*);
+ static void doSave( void* userdata, BOOL close_after_save );
+ static void onBtnSave(void*);
+ static void onBtnUndoChanges(void*);
+ static void onSearchMenu(void* userdata);
+
+ static void onUndoMenu(void* userdata);
+ static void onRedoMenu(void* userdata);
+ static void onCutMenu(void* userdata);
+ static void onCopyMenu(void* userdata);
+ static void onPasteMenu(void* userdata);
+ static void onSelectAllMenu(void* userdata);
+ static void onDeselectMenu(void* userdata);
+
+ static BOOL enableUndoMenu(void* userdata);
+ static BOOL enableRedoMenu(void* userdata);
+ static BOOL enableCutMenu(void* userdata);
+ static BOOL enableCopyMenu(void* userdata);
+ static BOOL enablePasteMenu(void* userdata);
+ static BOOL enableSelectAllMenu(void* userdata);
+ static BOOL enableDeselectMenu(void* userdata);
+
+ static BOOL hasChanged(void* userdata);
+
+ void selectFirstError();
+
+ virtual BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+
+protected:
+ void deleteBridges();
+
+ static void onErrorList(LLUICtrl*, void* user_data);
+
+private:
+ LLString mSampleText;
+ std::string mHelpFile;
+ LLTextEditor* mEditor;
+ void (*mLoadCallback)(void* userdata);
+ void (*mSaveCallback)(void* userdata, BOOL close_after_save);
+ void* mUserdata;
+ LLComboBox *mFunctions;
+ BOOL mForceClose;
+ //LLPanel* mGuiPanel;
+ LLPanel* mCodePanel;
+ LLScrollListCtrl* mErrorList;
+ LLDynamicArray<LLEntryAndEdCore*> mBridges;
+};
+
+
+// Used to view and edit a LSL from your inventory.
+class LLPreviewLSL : public LLPreview
+{
+public:
+ LLPreviewLSL(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid );
+
+ /*virtual*/ void open();
+
+protected:
+ virtual BOOL canClose();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ virtual void loadAsset();
+ void saveIfNeeded();
+
+ static void onLoad(void* userdata);
+ static void onSave(void* userdata, BOOL close_after_save);
+
+ static void onLoadComplete(LLVFS *vfs, const LLUUID& uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+ static void onSaveComplete(const LLUUID& uuid, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static LLPreviewLSL* getInstance(const LLUUID& uuid);
+
+ static void* createScriptEdPanel(void* userdata);
+
+
+protected:
+ LLScriptEdCore* mScriptEd;
+ // Can safely close only after both text and bytecode are uploaded
+ S32 mPendingUploads;
+};
+
+
+// Used to view and edit an LSL that is attached to an object.
+class LLLiveLSLEditor : public LLFloater
+{
+public:
+ LLLiveLSLEditor(const std::string& name, const LLRect& rect,
+ const std::string& title,
+ const LLUUID& object_id, const LLUUID& item_id);
+ ~LLLiveLSLEditor();
+
+
+ static LLLiveLSLEditor* show(const LLUUID& item_id, const LLUUID& object_id);
+ static void hide(const LLUUID& item_id, const LLUUID& object_id);
+
+ static void processScriptRunningReply(LLMessageSystem* msg, void**);
+
+protected:
+ virtual BOOL canClose();
+ virtual void draw();
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE);
+
+ void loadAsset(BOOL is_new = FALSE);
+ void saveIfNeeded();
+
+ static void onLoad(void* userdata);
+ static void onSave(void* userdata, BOOL close_after_save);
+
+ static void onLoadComplete(LLVFS *vfs, const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+ static void onSaveTextComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static void onSaveBytecodeComplete(const LLUUID& asset_uuid, void* user_data, S32 status);
+ static void onRunningCheckboxClicked(LLUICtrl*, void* userdata);
+ static void onReset(void* userdata);
+
+ void loadScriptText(const char* filename);
+ void loadScriptText(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type);
+
+ static void onErrorList(LLUICtrl*, void* user_data);
+
+ static void* createScriptEdPanel(void* userdata);
+
+
+protected:
+ LLUUID mObjectID;
+ LLUUID mItemID; // The inventory item this script is associated with
+ BOOL mIsNew;
+ LLScriptEdCore* mScriptEd;
+ //LLUUID mTransmitID;
+ LLCheckBoxCtrl *mRunningCheckbox;
+ BOOL mAskedForRunningInfo;
+ BOOL mHaveRunningInfo;
+ LLButton *mResetButton;
+ LLPointer<LLViewerInventoryItem> mItem;
+ BOOL mCloseAfterSave;
+ // need to save both text and script, so need to decide when done
+ S32 mPendingUploads;
+
+ static LLMap<LLUUID, LLLiveLSLEditor*> sInstances;
+};
+
+// name of help file for lsl
+extern const char HELP_LSL[];
+
+#endif // LL_LLPREVIEWSCRIPT_H
diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp
new file mode 100644
index 0000000000..8b0f55aff9
--- /dev/null
+++ b/indra/newview/llpreviewsound.cpp
@@ -0,0 +1,87 @@
+/**
+ * @file llpreviewsound.cpp
+ * @brief LLPreviewSound class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewsound.h"
+#include "llbutton.h"
+#include "llresmgr.h"
+#include "llinventory.h"
+#include "llinventoryview.h"
+#include "audioengine.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
+#include "llagent.h" // gAgent
+#include "llvieweruictrlfactory.h"
+
+extern LLAudioEngine* gAudiop;
+extern LLAgent gAgent;
+
+const F32 SOUND_GAIN = 1.0f;
+
+LLPreviewSound::LLPreviewSound(const std::string& name, const LLRect& rect, const std::string& title, const LLUUID& item_uuid, const LLUUID& object_uuid) :
+ LLPreview( name, rect, title, item_uuid, object_uuid)
+{
+
+ gUICtrlFactory->buildFloater(this,"floater_preview_sound.xml");
+
+ childSetAction("Sound play btn",&LLPreviewSound::playSound,this);
+ childSetAction("Sound audition btn",&LLPreviewSound::auditionSound,this);
+
+ LLButton* button = LLUICtrlFactory::getButtonByName(this, "Sound play btn");
+ button->setSoundFlags(LLView::SILENT);
+
+ button = LLUICtrlFactory::getButtonByName(this, "Sound audition btn");
+ button->setSoundFlags(LLView::SILENT);
+
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+
+ // preload the sound
+ if(item && gAudiop)
+ {
+ gAudiop->preloadSound(item->getAssetUUID());
+ }
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+
+}
+
+// static
+void LLPreviewSound::playSound( void *userdata )
+{
+ LLPreviewSound* self = (LLPreviewSound*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item && gAudiop)
+ {
+ send_sound_trigger(item->getAssetUUID(), SOUND_GAIN);
+ }
+}
+
+// static
+void LLPreviewSound::auditionSound( void *userdata )
+{
+ LLPreviewSound* self = (LLPreviewSound*) userdata;
+ LLInventoryItem *item = self->getItem();
+
+ if(item && gAudiop)
+ {
+ LLVector3d lpos_global = gAgent.getPositionGlobal();
+
+ gAudiop->triggerSound(item->getAssetUUID(), gAgent.getID(), SOUND_GAIN, lpos_global);
+ }
+}
diff --git a/indra/newview/llpreviewsound.h b/indra/newview/llpreviewsound.h
new file mode 100644
index 0000000000..dc19c37bb1
--- /dev/null
+++ b/indra/newview/llpreviewsound.h
@@ -0,0 +1,26 @@
+/**
+ * @file llpreviewsound.h
+ * @brief LLPreviewSound class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWSOUND_H
+#define LL_LLPREVIEWSOUND_H
+
+#include "llpreview.h"
+
+class LLPreviewSound : public LLPreview
+{
+public:
+ LLPreviewSound(const std::string& name, const LLRect& rect, const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_uuid = LLUUID::null);
+
+ static void playSound( void* userdata );
+ static void auditionSound( void* userdata );
+
+};
+
+#endif // LL_LLPREVIEWSOUND_H
diff --git a/indra/newview/llpreviewtexture.cpp b/indra/newview/llpreviewtexture.cpp
new file mode 100644
index 0000000000..a50ded3b53
--- /dev/null
+++ b/indra/newview/llpreviewtexture.cpp
@@ -0,0 +1,365 @@
+/**
+ * @file llpreviewtexture.cpp
+ * @brief LLPreviewTexture class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llpreviewtexture.h"
+
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llresmgr.h"
+#include "llagent.h"
+#include "llbutton.h"
+#include "llui.h"
+#include "llinventoryview.h"
+#include "llinventory.h"
+#include "llviewerwindow.h"
+#include "lltextbox.h"
+#include "llimagetga.h"
+#include "llfilepicker.h"
+#include "llvieweruictrlfactory.h"
+
+const S32 PREVIEW_TEXTURE_MIN_WIDTH = 300;
+const S32 PREVIEW_TEXTURE_MIN_HEIGHT = 120;
+
+const S32 CLIENT_RECT_VPAD = 4;
+
+const F32 SECONDS_TO_SHOW_FILE_SAVED_MSG = 8.f;
+
+LLPreviewTexture::LLPreviewTexture(const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_id,
+ BOOL show_keep_discard)
+: LLPreview(name, rect, title, item_uuid, object_id, TRUE, PREVIEW_TEXTURE_MIN_WIDTH, PREVIEW_TEXTURE_MIN_HEIGHT ),
+ mLoadingFullImage( FALSE ),
+ mShowKeepDiscard(show_keep_discard),
+ mCopyToInv(FALSE),
+ mIsCopyable(FALSE),
+ mLastHeight(0),
+ mLastWidth(0)
+{
+ LLInventoryItem *item = getItem();
+ if(item)
+ {
+ mImageID = item->getAssetUUID();
+ const LLPermissions& perm = item->getPermissions();
+ U32 mask = PERM_NONE;
+ if(perm.getOwner() == gAgent.getID())
+ {
+ mask = perm.getMaskBase();
+ }
+ else if(gAgent.isInGroup(perm.getGroup()))
+ {
+ mask = perm.getMaskGroup();
+ }
+ else
+ {
+ mask = perm.getMaskEveryone();
+ }
+ if((mask & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ mIsCopyable = TRUE;
+ }
+ }
+
+ init();
+
+ setTitle(title);
+
+ if (!getHost())
+ {
+ LLRect curRect = getRect();
+ translate(rect.mLeft - curRect.mLeft, rect.mTop - curRect.mTop);
+ }
+}
+
+
+// Note: uses asset_id as a dummy item id.
+LLPreviewTexture::LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& asset_id,
+ BOOL copy_to_inv)
+ :
+ LLPreview(
+ name,
+ rect,
+ title,
+ asset_id,
+ LLUUID::null,
+ TRUE,
+ PREVIEW_TEXTURE_MIN_WIDTH,
+ PREVIEW_TEXTURE_MIN_HEIGHT ),
+ mImageID(asset_id),
+ mLoadingFullImage( FALSE ),
+ mShowKeepDiscard(FALSE),
+ mCopyToInv(copy_to_inv),
+ mIsCopyable(TRUE),
+ mLastHeight(0),
+ mLastWidth(0)
+{
+
+ init();
+
+ setTitle(title);
+
+ LLRect curRect = getRect();
+ translate(curRect.mLeft - rect.mLeft, curRect.mTop - rect.mTop);
+
+}
+
+
+LLPreviewTexture::~LLPreviewTexture()
+{
+ if( mLoadingFullImage )
+ {
+ getWindow()->decBusyCount();
+ }
+
+ mImage = NULL;
+}
+
+
+void LLPreviewTexture::init()
+{
+
+
+ if (mCopyToInv)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_embedded_texture.xml");
+
+ childSetAction("Copy To Inventory",LLPreview::onBtnCopyToInv,this);
+ }
+
+ else if (mShowKeepDiscard)
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_texture_keep_discard.xml");
+
+ childSetAction("Keep",onKeepBtn,this);
+ childSetAction("Discard",onDiscardBtn,this);
+ }
+
+ else
+ {
+ gUICtrlFactory->buildFloater(this,"floater_preview_texture.xml");
+ }
+
+
+ if (!mCopyToInv)
+ {
+ LLInventoryItem* item = getItem();
+
+ childSetCommitCallback("desc", LLPreview::onText, this);
+ childSetText("desc", item->getDescription());
+ childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe);
+ }
+}
+
+
+// virtual
+BOOL LLPreviewTexture::canSaveAs()
+{
+ return mIsCopyable && !mLoadingFullImage && mImage.notNull() && !mImage->isMissingAsset();
+}
+
+
+// virtual
+void LLPreviewTexture::saveAs()
+{
+ if( !mLoadingFullImage )
+ {
+ LLFilePicker& file_picker = LLFilePicker::instance();
+ if( !file_picker.getSaveFile( LLFilePicker::FFSAVE_TGA ) )
+ {
+ // User canceled save.
+ return;
+ }
+ mSaveFileName = file_picker.getFirstFile();
+ mLoadingFullImage = TRUE;
+ getWindow()->incBusyCount();
+ mImage->setLoadedCallback( LLPreviewTexture::onFileLoadedForSave,
+ 0,
+ TRUE,
+ new LLUUID( mItemUUID ) );
+ }
+}
+
+
+// static
+void LLPreviewTexture::onFileLoadedForSave(BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata)
+{
+ LLUUID* item_uuid = (LLUUID*) userdata;
+ LLPreviewTexture* self = NULL;
+ preview_map_t::iterator found_it = LLPreview::sInstances.find(*item_uuid);
+ if(found_it != LLPreview::sInstances.end())
+ {
+ self = (LLPreviewTexture*) found_it->second;
+ }
+
+ if( final || !success )
+ {
+ delete item_uuid;
+
+ if( self )
+ {
+ self->getWindow()->decBusyCount();
+ self->mLoadingFullImage = FALSE;
+ }
+ }
+
+ if( self && final && success )
+ {
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA;
+ if( !image_tga->encode( src ) )
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = self->mSaveFileName;
+ gViewerWindow->alertXml("CannotEncodeFile", args);
+ }
+ else if( !image_tga->save( self->mSaveFileName ) )
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = self->mSaveFileName;
+ gViewerWindow->alertXml("CannotWriteFile", args);
+ }
+ else
+ {
+ self->mSavedFileTimer.reset();
+ self->mSavedFileTimer.setTimerExpirySec( SECONDS_TO_SHOW_FILE_SAVED_MSG );
+ }
+
+ self->mSaveFileName.clear();
+ }
+
+ if( self && !success )
+ {
+ gViewerWindow->alertXml("CannotDownloadFile");
+ }
+}
+
+
+// It takes a while until we get height and width information.
+// When we receive it, reshape the window accordingly.
+void LLPreviewTexture::updateAspectRatio()
+{
+ if (!mImage) return;
+
+ S32 image_height = llmax(1, mImage->getHeight(0));
+ S32 image_width = llmax(1, mImage->getWidth(0));
+ // Attempt to make the image 1:1 on screen.
+ // If that fails, cut width by half.
+ S32 client_width = image_width;
+ S32 horiz_pad = 2 * (LLPANEL_BORDER_WIDTH + PREVIEW_PAD) + PREVIEW_RESIZE_HANDLE_SIZE;
+ S32 screen_width = gViewerWindow->getWindowWidth();
+ S32 max_client_width = screen_width - horiz_pad;
+
+ while (client_width > max_client_width)
+ {
+ client_width /= 2;
+ }
+
+ // Demand width at least 128
+ if (client_width < 128)
+ {
+ client_width = 128;
+ }
+
+ S32 view_width = client_width + horiz_pad;
+
+ // Adjust the height based on the width computed above.
+ F32 inv_aspect_ratio = (F32) image_height / (F32) image_width;
+ S32 client_height = llround(client_width * inv_aspect_ratio);
+ S32 view_height =
+ PREVIEW_HEADER_SIZE + // header (includes top border)
+ client_height + 2 * CLIENT_RECT_VPAD + // texture plus uniform spacing (which leaves room for resize handle)
+ LLPANEL_BORDER_WIDTH; // bottom border
+
+ // set text on dimensions display (should be moved out of here and into a callback of some sort)
+ childSetTextArg("dimensions", "[WIDTH]", llformat("%d", mImage->mFullWidth));
+ childSetTextArg("dimensions", "[HEIGHT]", llformat("%d", mImage->mFullHeight));
+
+ // add space for dimensions
+ S32 info_height = 0;
+ LLRect dim_rect;
+ childGetRect("dimensions", dim_rect);
+ S32 dim_height = dim_rect.getHeight();
+ info_height += dim_height + CLIENT_RECT_VPAD;
+ view_height += info_height;
+
+ S32 button_height = 0;
+ if (mShowKeepDiscard || mCopyToInv) { //mCopyToInvBtn
+
+ // add space for buttons
+ view_height += BTN_HEIGHT + CLIENT_RECT_VPAD;
+ button_height = BTN_HEIGHT + PREVIEW_PAD;
+ }
+
+ LLRect window_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ window_rect.mTop -= (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD);
+ window_rect.mBottom += PREVIEW_BORDER + button_height + CLIENT_RECT_VPAD + info_height + CLIENT_RECT_VPAD;
+ LLMultiFloater* hostp = getHost();
+ if (hostp)
+ {
+ // try to keep aspect ratio when hosted, as hosting view can resize without user input
+ mClientRect.setLeftTopAndSize(window_rect.getCenterX() - (client_width / 2), window_rect.mTop, client_width, client_height);
+ }
+ else
+ {
+ mClientRect.setLeftTopAndSize(LLPANEL_BORDER_WIDTH + PREVIEW_PAD + 6,
+ mRect.getHeight() - (PREVIEW_HEADER_SIZE + CLIENT_RECT_VPAD),
+ mRect.getWidth() - horiz_pad,
+ mRect.getHeight() - (view_height - client_height) - 8);
+ }
+
+ if (mImage->mFullHeight > mLastHeight && mImage->mFullWidth > mLastWidth)
+ {
+ mLastWidth = image_width;
+ mLastHeight = image_height;
+
+ S32 old_top = mRect.mTop;
+ S32 old_left = mRect.mLeft;
+ if (hostp)
+ {
+ hostp->growToFit(this, view_width, view_height);
+ }
+ else
+ {
+ reshape( view_width, view_height );
+ S32 new_bottom = old_top - mRect.getHeight();
+ setOrigin( old_left, new_bottom );
+ }
+
+ // Try to keep whole view onscreen, don't allow partial offscreen.
+ gFloaterView->adjustToFitScreen(this, FALSE);
+ }
+}
+
+void LLPreviewTexture::loadAsset()
+{
+ mImage = gImageList.getImage(mImageID, MIPMAP_FALSE, FALSE);
+ mImage->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ mAssetStatus = PREVIEW_ASSET_LOADING;
+}
+
+LLPreview::EAssetStatus LLPreviewTexture::getAssetStatus()
+{
+ if (mImage.notNull() && (mImage->mFullWidth * mImage->mFullHeight > 0))
+ {
+ mAssetStatus = PREVIEW_ASSET_LOADED;
+ }
+ return mAssetStatus;
+}
diff --git a/indra/newview/llpreviewtexture.h b/indra/newview/llpreviewtexture.h
new file mode 100644
index 0000000000..765a40c639
--- /dev/null
+++ b/indra/newview/llpreviewtexture.h
@@ -0,0 +1,78 @@
+/**
+ * @file llpreviewtexture.h
+ * @brief LLPreviewTexture class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPREVIEWTEXTURE_H
+#define LL_LLPREVIEWTEXTURE_H
+
+#include "llpreview.h"
+#include "llbutton.h"
+#include "llframetimer.h"
+#include "llviewerimage.h"
+
+class LLImageRaw;
+
+class LLPreviewTexture : public LLPreview
+{
+public:
+ LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& item_uuid,
+ const LLUUID& object_id,
+ BOOL show_keep_discard = FALSE);
+ LLPreviewTexture(
+ const std::string& name,
+ const LLRect& rect,
+ const std::string& title,
+ const LLUUID& asset_id,
+ BOOL copy_to_inv = FALSE);
+ ~LLPreviewTexture();
+
+ virtual void draw();
+
+ virtual BOOL canSaveAs();
+ virtual void saveAs();
+
+ virtual void loadAsset();
+ virtual EAssetStatus getAssetStatus();
+
+ static void saveToFile(void* userdata);
+ static void onFileLoadedForSave(
+ BOOL success,
+ LLViewerImage *src_vi,
+ LLImageRaw* src,
+ LLImageRaw* aux_src,
+ S32 discard_level,
+ BOOL final,
+ void* userdata );
+
+
+protected:
+ void init();
+ void updateAspectRatio();
+
+protected:
+ LLUUID mImageID;
+ LLPointer<LLViewerImage> mImage;
+ BOOL mLoadingFullImage;
+ LLString mSaveFileName;
+ LLFrameTimer mSavedFileTimer;
+ BOOL mShowKeepDiscard;
+ BOOL mCopyToInv;
+
+ // This is stored off in a member variable, because the save-as
+ // button and drag and drop functionality need to know.
+ BOOL mIsCopyable;
+
+ S32 mLastHeight;
+ S32 mLastWidth;
+};
+
+
+#endif // LL_LLPREVIEWTEXTURE_H
diff --git a/indra/newview/llprogressview.cpp b/indra/newview/llprogressview.cpp
new file mode 100644
index 0000000000..2faa9ba351
--- /dev/null
+++ b/indra/newview/llprogressview.cpp
@@ -0,0 +1,326 @@
+/**
+ * @file llprogressview.cpp
+ * @brief LLProgressView class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llprogressview.h"
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llgl.h"
+#include "llui.h"
+#include "llfontgl.h"
+#include "llimagegl.h"
+#include "lltimer.h"
+#include "llglheaders.h"
+
+#include "llagent.h"
+#include "llfocusmgr.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+LLProgressView* LLProgressView::sInstance = NULL;
+
+LLPointer<LLImageGL> gStartImageGL = NULL;
+S32 gStartImageWidth = 1;
+S32 gStartImageHeight = 1;
+const F32 FADE_IN_TIME = 1.f;
+
+const LLString ANIMATION_FILENAME = "Login Sequence ";
+const LLString ANIMATION_SUFFIX = ".jpg";
+const F32 TOTAL_LOGIN_TIME = 10.f; // seconds, wild guess at time from GL context to actual world view
+S32 gLastStartAnimationFrame = 0; // human-style indexing, first image = 1
+const S32 ANIMATION_FRAMES = 1; //13;
+
+// XUI:translate
+LLProgressView::LLProgressView(const std::string& name, const LLRect &rect)
+: LLView(name, rect, TRUE)
+{
+ mPercentDone = 0.f;
+ mDrawBackground = TRUE;
+
+ const S32 CANCEL_BTN_WIDTH = 70;
+ const S32 CANCEL_BTN_OFFSET = 16;
+ LLRect r;
+ r.setOriginAndSize(
+ mRect.getWidth() - CANCEL_BTN_OFFSET - CANCEL_BTN_WIDTH, CANCEL_BTN_OFFSET,
+ CANCEL_BTN_WIDTH, BTN_HEIGHT );
+
+ mCancelBtn = new LLButton(
+ "Quit",
+ r,
+ "",
+ LLProgressView::onCancelButtonClicked,
+ NULL );
+ mCancelBtn->setFollows( FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCancelBtn );
+ mFadeTimer.stop();
+ setVisible(FALSE);
+
+ sInstance = this;
+}
+
+
+LLProgressView::~LLProgressView()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+ sInstance = NULL;
+}
+
+EWidgetType LLProgressView::getWidgetType() const
+{
+ return WIDGET_TYPE_PROGRESS_VIEW;
+}
+
+LLString LLProgressView::getWidgetTag() const
+{
+ return LL_PROGRESS_VIEW_TAG;
+}
+
+BOOL LLProgressView::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( childrenHandleHover( x, y, mask ) == NULL )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLProgressView" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_WAIT);
+ }
+ return TRUE;
+}
+
+
+BOOL LLProgressView::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if( getVisible() )
+ {
+ // Suck up all keystokes except CTRL-Q.
+ if( ('Q' == key) && (MASK_CONTROL == mask) )
+ {
+ app_request_quit();
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLProgressView::setVisible(BOOL visible)
+{
+ if (getVisible() && !visible)
+ {
+ mFadeTimer.start();
+ }
+ else if (!getVisible() && visible)
+ {
+ gFocusMgr.setTopView(this, NULL);
+ mFadeTimer.stop();
+ mProgressTimer.start();
+ LLView::setVisible(visible);
+ }
+}
+
+
+void LLProgressView::draw()
+{
+ static LLTimer timer;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ // Make sure the progress view always fills the entire window.
+ S32 width = gViewerWindow->getWindowWidth();
+ S32 height = gViewerWindow->getWindowHeight();
+ if( (width != mRect.getWidth()) || (height != mRect.getHeight()) )
+ {
+ reshape( width, height );
+ }
+
+ // Paint bitmap if we've got one
+ if (mDrawBackground)
+ {
+ glPushMatrix();
+ if (gStartImageGL)
+ {
+ LLGLSUIDefault gls_ui;
+ LLViewerImage::bindTexture(gStartImageGL);
+ glColor4f(1.f, 1.f, 1.f, mFadeTimer.getStarted() ? clamp_rescale(mFadeTimer.getElapsedTimeF32(), 0.f, FADE_IN_TIME, 1.f, 0.f) : 1.f);
+ F32 image_aspect = (F32)gStartImageWidth / (F32)gStartImageHeight;
+ F32 view_aspect = (F32)width / (F32)height;
+ // stretch image to maintain aspect ratio
+ if (image_aspect > view_aspect)
+ {
+ glTranslatef(-0.5f * (image_aspect / view_aspect - 1.f) * width, 0.f, 0.f);
+ glScalef(image_aspect / view_aspect, 1.f, 1.f);
+ }
+ else
+ {
+ glTranslatef(0.f, -0.5f * (view_aspect / image_aspect - 1.f) * height, 0.f);
+ glScalef(1.f, view_aspect / image_aspect, 1.f);
+ }
+ gl_rect_2d_simple_tex( mRect.getWidth(), mRect.getHeight() );
+ gStartImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4f(0.f, 0.f, 0.f, 1.f);
+ gl_rect_2d(mRect);
+ }
+ glPopMatrix();
+ }
+
+ if (mFadeTimer.getStarted())
+ {
+ LLView::draw();
+ if (mFadeTimer.getElapsedTimeF32() > FADE_IN_TIME)
+ {
+ gFocusMgr.removeTopViewWithoutCallback(this);
+ LLView::setVisible(FALSE);
+ gStartImageGL = NULL;
+ }
+ return;
+ }
+
+ S32 line_x = mRect.getWidth() / 2;
+ S32 line_one_y = mRect.getHeight() / 2 + 64;
+ const S32 LINE_SPACING = 25;
+ S32 line_two_y = line_one_y - LINE_SPACING;
+ const LLFontGL* font = LLFontGL::sSansSerif;
+
+ LLViewerImage* shadow_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square_soft.tga")), MIPMAP_FALSE, TRUE);
+ LLViewerImage* bar_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
+
+ //LLColor4 background_color = gColors.getColor("DefaultShadowLight");
+ LLColor4 background_color = LLColor4(0.3254f, 0.4f, 0.5058f, 1.0f);
+
+ F32 alpha = 0.5f + 0.5f*0.5f*(1.f + (F32)sin(3.f*timer.getElapsedTimeF32()));
+ // background_color.mV[3] = background_color.mV[3]*alpha;
+
+ LLString top_line = gSecondLife;
+
+ font->renderUTF8(top_line, 0,
+ line_x, line_one_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ font->renderUTF8(mText, 0,
+ line_x, line_two_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+
+ S32 bar_bottom = line_two_y - 30;
+ S32 bar_height = 18;
+ S32 bar_width = mRect.getWidth() * 2 / 3;
+ S32 bar_left = (mRect.getWidth() / 2) - (bar_width / 2);
+
+ gl_draw_scaled_image_with_border(
+ bar_left + 2,
+ bar_bottom - 2,
+ 16,
+ 16,
+ bar_width,
+ bar_height,
+ shadow_imagep,
+ gColors.getColor("ColorDropShadow"));
+
+ gl_draw_scaled_image_with_border(
+ bar_left,
+ bar_bottom,
+ 16,
+ 16,
+ bar_width,
+ bar_height,
+ bar_imagep,
+ LLColor4(0.7f, 0.7f, 0.8f, 1.0f));
+
+ gl_draw_scaled_image_with_border(bar_left + 2, bar_bottom + 2, 16, 16,
+ bar_width - 4, bar_height - 4,
+ bar_imagep,
+ background_color);
+
+ LLColor4 bar_color = LLColor4(0.5764f, 0.6627f, 0.8352f, 1.0f);
+ bar_color.mV[3] = alpha;
+ gl_draw_scaled_image_with_border(bar_left + 2, bar_bottom + 2, 16, 16,
+ llround((bar_width - 4) * (mPercentDone / 100.f)), bar_height - 4,
+ bar_imagep,
+ bar_color);
+
+ S32 line_three_y = line_two_y - LINE_SPACING * 3;
+
+ // draw the message if there is one
+ if(!mMessage.empty())
+ {
+ LLWString wmessage = utf8str_to_wstring(mMessage);
+ const F32 MAX_PIXELS = 640.0f;
+ S32 chars_left = wmessage.length();
+ S32 chars_this_time = 0;
+ S32 msgidx = 0;
+ while(chars_left > 0)
+ {
+ chars_this_time = font->maxDrawableChars(wmessage.substr(msgidx).c_str(),
+ MAX_PIXELS,
+ MAX_STRING - 1,
+ TRUE);
+ LLWString wbuffer = wmessage.substr(msgidx, chars_this_time);
+ font->render(wbuffer, 0,
+ (F32)line_x, (F32)line_three_y,
+ LLColor4::white,
+ LLFontGL::HCENTER, LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ msgidx += chars_this_time;
+ chars_left -= chars_this_time;
+ line_three_y -= LINE_SPACING;
+ }
+ }
+
+ // draw children
+ LLView::draw();
+}
+
+void LLProgressView::setText(const LLString& text)
+{
+ mText = text;
+}
+
+void LLProgressView::setPercent(const F32 percent)
+{
+ mPercentDone = llclamp(percent, 0.f, 100.f);
+}
+
+void LLProgressView::setMessage(const LLString& msg)
+{
+ mMessage = msg;
+}
+
+void LLProgressView::setCancelButtonVisible(BOOL b, const LLString& label)
+{
+ mCancelBtn->setVisible( b );
+ mCancelBtn->setEnabled( b );
+ mCancelBtn->setLabelSelected(label);
+ mCancelBtn->setLabelUnselected(label);
+}
+
+// static
+void LLProgressView::onCancelButtonClicked(void*)
+{
+ if (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE)
+ {
+ app_request_quit();
+ }
+ else
+ {
+ gAgent.teleportCancel();
+ sInstance->mCancelBtn->setEnabled(FALSE);
+ sInstance->setVisible(FALSE);
+ }
+}
diff --git a/indra/newview/llprogressview.h b/indra/newview/llprogressview.h
new file mode 100644
index 0000000000..474a431b09
--- /dev/null
+++ b/indra/newview/llprogressview.h
@@ -0,0 +1,54 @@
+/**
+ * @file llprogressview.h
+ * @brief LLProgressView class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPROGRESSVIEW_H
+#define LL_LLPROGRESSVIEW_H
+
+#include "llview.h"
+#include "llframetimer.h"
+
+class LLImageRaw;
+class LLButton;
+
+class LLProgressView : public LLView
+{
+public:
+ LLProgressView(const std::string& name, const LLRect& rect);
+ virtual ~LLProgressView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask, BOOL called_from_parent);
+ /*virtual*/ void setVisible(BOOL visible);
+
+ void setText(const LLString& text);
+ void setPercent(const F32 percent);
+
+ // Set it to NULL when you want to eliminate the message.
+ void setMessage(const LLString& msg);
+
+ void setCancelButtonVisible(BOOL b, const LLString& label);
+
+ static void onCancelButtonClicked( void* );
+
+protected:
+ BOOL mDrawBackground;
+ F32 mPercentDone;
+ LLString mText;
+ LLString mMessage;
+ LLButton* mCancelBtn;
+ LLFrameTimer mFadeTimer;
+ LLFrameTimer mProgressTimer;
+
+ static LLProgressView* sInstance;
+};
+
+#endif // LL_LLPROGRESSVIEW_H
diff --git a/indra/newview/llregionposition.cpp b/indra/newview/llregionposition.cpp
new file mode 100644
index 0000000000..e2a2dc7140
--- /dev/null
+++ b/indra/newview/llregionposition.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llregionposition.cpp
+ * @brief Region position storing class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llregionposition.h"
+
+#include "llagent.h"
+#include "llworld.h"
+#include "llviewerregion.h"
+
+LLRegionPosition::LLRegionPosition()
+{
+ mRegionp = NULL;
+}
+
+LLRegionPosition::LLRegionPosition(LLViewerRegion *regionp, const LLVector3 &position)
+{
+ mRegionp = regionp;
+ mPositionRegion = position;
+}
+
+LLRegionPosition::LLRegionPosition(const LLVector3d &global_position)
+{
+ setPositionGlobal(global_position);
+}
+
+LLViewerRegion *LLRegionPosition::getRegion() const
+{
+ return mRegionp;
+}
+
+const LLVector3 &LLRegionPosition::getPositionRegion() const
+{
+ return mPositionRegion;
+}
+
+const LLVector3 LLRegionPosition::getPositionAgent() const
+{
+ return mRegionp->getPosAgentFromRegion( mPositionRegion );
+}
+
+LLVector3d LLRegionPosition::getPositionGlobal() const
+{
+ if (mRegionp)
+ {
+ return mRegionp->getPosGlobalFromRegion(mPositionRegion);
+ }
+ else
+ {
+ LLVector3d pos_global;
+ pos_global.setVec(mPositionRegion);
+ return pos_global;
+ }
+}
+
+
+void LLRegionPosition::setPositionGlobal(const LLVector3d& position_global )
+{
+ mRegionp = gWorldPointer->getRegionFromPosGlobal(position_global);
+ if (mRegionp)
+ {
+ mPositionRegion = mRegionp->getPosRegionFromGlobal(position_global);
+ }
+ else
+ {
+ mRegionp = gAgent.getRegion();
+ llassert(mRegionp);
+ mPositionRegion = mRegionp->getPosRegionFromGlobal(position_global);
+ }
+}
diff --git a/indra/newview/llregionposition.h b/indra/newview/llregionposition.h
new file mode 100644
index 0000000000..a505da2833
--- /dev/null
+++ b/indra/newview/llregionposition.h
@@ -0,0 +1,43 @@
+/**
+ * @file llregionposition.h
+ * @brief Region position storing class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLREGIONPOSITION_H
+#define LL_LLREGIONPOSITION_H
+
+/**
+ * This class maintains a region, offset pair to store position, so when our "global"
+ * coordinate frame shifts, all calculations are still correct.
+ */
+
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLViewerRegion;
+
+class LLRegionPosition
+{
+private:
+ LLViewerRegion *mRegionp;
+public:
+ LLVector3 mPositionRegion;
+ LLRegionPosition();
+ LLRegionPosition(LLViewerRegion *regionp, const LLVector3 &position_local);
+ LLRegionPosition(const LLVector3d &global_position); // From global coords ONLY!
+
+ LLViewerRegion* getRegion() const;
+ void setPositionGlobal(const LLVector3d& global_pos);
+ LLVector3d getPositionGlobal() const;
+ const LLVector3& getPositionRegion() const;
+ const LLVector3 getPositionAgent() const;
+
+
+ void clear() { mRegionp = NULL; mPositionRegion.clearVec(); }
+// LLRegionPosition operator+(const LLRegionPosition &pos) const;
+};
+
+#endif // LL_REGION_POSITION_H
diff --git a/indra/newview/llsavedsettingsglue.cpp b/indra/newview/llsavedsettingsglue.cpp
new file mode 100644
index 0000000000..e98940f170
--- /dev/null
+++ b/indra/newview/llsavedsettingsglue.cpp
@@ -0,0 +1,51 @@
+/**
+ * @file llsavedsettingsglue.cpp
+ * @author James Cook
+ * @brief LLSavedSettingsGlue class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsavedsettingsglue.h"
+
+#include "lluictrl.h"
+
+#include "llviewercontrol.h"
+
+void LLSavedSettingsGlue::setBOOL(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setBOOL(name, value.asBoolean());
+}
+
+void LLSavedSettingsGlue::setS32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setS32(name, value.asInteger());
+}
+
+void LLSavedSettingsGlue::setF32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setF32(name, (F32)value.asReal());
+}
+
+void LLSavedSettingsGlue::setU32(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setU32(name, (U32)value.asInteger());
+}
+
+void LLSavedSettingsGlue::setString(LLUICtrl* ctrl, void* data)
+{
+ const char* name = (const char*)data;
+ LLSD value = ctrl->getValue();
+ gSavedSettings.setString(name, value.asString());
+}
diff --git a/indra/newview/llsavedsettingsglue.h b/indra/newview/llsavedsettingsglue.h
new file mode 100644
index 0000000000..5f5d5ab787
--- /dev/null
+++ b/indra/newview/llsavedsettingsglue.h
@@ -0,0 +1,28 @@
+/**
+ * @file llsavedsettingsglue.h
+ * @author James Cook
+ * @brief LLSavedSettingsGlue class definition
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSAVEDSETTINGSGLUE_H
+#define LL_LLSAVEDSETTINGSGLUE_H
+
+class LLUICtrl;
+
+// Helper to change gSavedSettings from UI widget commit callbacks.
+// Set the widget callback to be one of the setFoo() calls below,
+// and assign the control name as a const char* to the userdata.
+class LLSavedSettingsGlue
+{
+public:
+ static void setBOOL(LLUICtrl* ctrl, void* name);
+ static void setS32(LLUICtrl* ctrl, void* name);
+ static void setF32(LLUICtrl* ctrl, void* name);
+ static void setU32(LLUICtrl* ctrl, void* name);
+ static void setString(LLUICtrl* ctrl, void* name);
+};
+
+#endif
diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp
new file mode 100644
index 0000000000..8eeddeaa18
--- /dev/null
+++ b/indra/newview/llselectmgr.cpp
@@ -0,0 +1,6732 @@
+/**
+ * @file llselectmgr.cpp
+ * @brief A manager for selected objects and faces.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// file include
+#include "llselectmgr.h"
+
+// library includes
+#include "llcachename.h"
+#include "lldbstrings.h"
+#include "lleconomy.h"
+#include "llgl.h"
+#include "llpermissions.h"
+#include "llpermissionsflags.h"
+#include "llptrskiplist.h"
+#include "llundo.h"
+#include "lluuid.h"
+#include "llvolume.h"
+#include "message.h"
+#include "object_flags.h"
+#include "llquaternion.h"
+
+// viewer includes
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+#include "llfloaterproperties.h"
+#include "llfloaterrate.h"
+#include "llfloaterreporter.h"
+#include "llfloatertools.h"
+#include "llframetimer.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llmenugl.h"
+#include "llstatusbar.h"
+#include "llsurface.h"
+#include "lltool.h"
+#include "lltooldraganddrop.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+#include "llglheaders.h"
+
+//
+// Consts
+//
+
+const S32 NUM_SELECTION_UNDO_ENTRIES = 200;
+const F32 SILHOUETTE_UPDATE_THRESHOLD_SQUARED = 0.02f;
+const S32 OWNERSHIP_COST_PER_OBJECT = 10; // Must be the same as economy_constants.price_object_claim in the database.
+const S32 MAX_ACTION_QUEUE_SIZE = 20;
+const S32 MAX_SILS_PER_FRAME = 50;
+const S32 MAX_OBJECTS_PER_PACKET = 254;
+
+extern LLGlobalEconomy *gGlobalEconomy;
+extern LLUUID gLastHitObjectID;
+extern LLVector3d gLastHitObjectOffset;
+
+//
+// Globals
+//
+LLSelectMgr* gSelectMgr = NULL;
+
+BOOL gDebugSelectMgr = FALSE;
+
+BOOL gHideSelectedObjects = FALSE;
+BOOL gAllowSelectAvatar = FALSE;
+
+BOOL LLSelectMgr::sRectSelectInclusive = TRUE;
+BOOL LLSelectMgr::sRenderHiddenSelections = TRUE;
+BOOL LLSelectMgr::sRenderLightRadius = FALSE;
+F32 LLSelectMgr::sHighlightThickness = 0.f;
+F32 LLSelectMgr::sHighlightUScale = 0.f;
+F32 LLSelectMgr::sHighlightVScale = 0.f;
+F32 LLSelectMgr::sHighlightAlpha = 0.f;
+F32 LLSelectMgr::sHighlightAlphaTest = 0.f;
+F32 LLSelectMgr::sHighlightUAnim = 0.f;
+F32 LLSelectMgr::sHighlightVAnim = 0.f;
+LLColor4 LLSelectMgr::sSilhouetteParentColor;
+LLColor4 LLSelectMgr::sSilhouetteChildColor;
+LLColor4 LLSelectMgr::sHighlightParentColor;
+LLColor4 LLSelectMgr::sHighlightChildColor;
+LLColor4 LLSelectMgr::sContextSilhouetteColor;
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// struct LLDeRezInfo
+//
+// Used to keep track of important derez info.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+struct LLDeRezInfo
+{
+ EDeRezDestination mDestination;
+ LLUUID mDestinationID;
+ LLDeRezInfo(EDeRezDestination dest, const LLUUID& dest_id) :
+ mDestination(dest), mDestinationID(dest_id) {}
+};
+
+//
+// Imports
+//
+
+
+//
+// Functions
+//
+
+//-----------------------------------------------------------------------------
+// LLSelectMgr()
+//-----------------------------------------------------------------------------
+LLSelectMgr::LLSelectMgr()
+{
+ mTEMode = FALSE;
+ mLastCameraPos.clearVec();
+
+ sHighlightThickness = gSavedSettings.getF32("SelectionHighlightThickness");
+ sHighlightUScale = gSavedSettings.getF32("SelectionHighlightUScale");
+ sHighlightVScale = gSavedSettings.getF32("SelectionHighlightVScale");
+ sHighlightAlpha = gSavedSettings.getF32("SelectionHighlightAlpha");
+ sHighlightAlphaTest = gSavedSettings.getF32("SelectionHighlightAlphaTest");
+ sHighlightUAnim = gSavedSettings.getF32("SelectionHighlightUAnim");
+ sHighlightVAnim = gSavedSettings.getF32("SelectionHighlightVAnim");
+
+ sSilhouetteParentColor = gColors.getColor("SilhouetteParentColor");
+ sSilhouetteChildColor = gColors.getColor("SilhouetteChildColor");
+ sHighlightParentColor = gColors.getColor("HighlightParentColor");
+ sHighlightChildColor = gColors.getColor("HighlightChildColor");
+ sContextSilhouetteColor = gColors.getColor("ContextSilhouetteColor")*0.5f;
+
+ sRenderLightRadius = gSavedSettings.getBOOL("RenderLightRadius");
+
+ mRenderSilhouettes = TRUE;
+
+ mGridMode = GRID_MODE_WORLD;
+ gSavedSettings.setS32("GridMode", (S32)GRID_MODE_WORLD);
+ mGridValid = FALSE;
+
+ mSelectType = SELECT_TYPE_WORLD;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLSelectMgr()
+//-----------------------------------------------------------------------------
+LLSelectMgr::~LLSelectMgr()
+{
+ mHoverObjects.deleteAllNodes();
+ mSelectedObjects.deleteAllNodes();
+ mHighlightedObjects.deleteAllNodes();
+ mRectSelectedObjects.clear();
+ mGridObjects.deleteAllNodes();
+ mUndoQueue.clear();
+ mRedoQueue.clear();
+}
+
+bool LLSelectMgr::applyToObjects(LLSelectedObjectFunctor* func)
+{
+ bool result = true;
+ LLViewerObject* object;
+ for (object = getFirstObject(); object != NULL; object = getNextObject())
+ {
+ result = result && func->apply(object);
+ }
+ return result;
+}
+
+bool LLSelectMgr::applyToRootObjects(LLSelectedObjectFunctor* func)
+{
+ bool result = true;
+ LLViewerObject* object;
+ for (object = getFirstRootObject();
+ object != NULL;
+ object = getNextRootObject())
+ {
+ result = result && func->apply(object);
+ }
+ return result;
+}
+
+bool LLSelectMgr::applyToNodes(LLSelectedNodeFunctor *func)
+{
+ bool result = true;
+ LLSelectNode* node;
+ for (node = getFirstNode(); node != NULL; node = getNextNode())
+ {
+ result = result && func->apply(node);
+ }
+ return result;
+}
+
+void LLSelectMgr::updateEffects()
+{
+ if (mEffectsTimer.getElapsedTimeF32() > 1.f)
+ {
+ mSelectedObjects.updateEffects();
+ mEffectsTimer.reset();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select just the object, not any other group members.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectOnly(LLViewerObject* object, S32 face)
+{
+ llassert( object );
+
+ // Don't add an object that is already in the list
+ if (object->isSelected() ) {
+ // make sure point at position is updated
+ updatePointAt();
+ gEditMenuHandler = this;
+ return;
+ }
+
+ if (!canSelectObject(object))
+ {
+ //make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ // llinfos << "Adding object to selected object list" << llendl;
+
+ // Place it in the list and tag it.
+ // This will refresh dialogs.
+ addAsIndividual(object, face);
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ //FIXME: shouldn't zero out these either
+ object->setVelocity(LLVector3::zero);
+ object->setAcceleration(LLVector3::zero);
+ //object->setAngularVelocity(LLVector3::zero);
+ object->resetRot();
+
+ // Always send to simulator, so you get a copy of the
+ // permissions structure back.
+ gMessageSystem->newMessageFast(_PREHASH_ObjectSelect);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ LLViewerRegion* regionp = object->getRegion();
+ gMessageSystem->sendReliable( regionp->getHost());
+
+ updatePointAt();
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select the object, parents and children.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectAndFamily(LLViewerObject* obj, BOOL add_to_end)
+{
+ llassert( obj );
+
+ // This may be incorrect if things weren't family selected before... - djs 07/08/02
+ // Don't add an object that is already in the list
+ if (obj->isSelected() )
+ {
+ // make sure pointat position is updated
+ updatePointAt();
+ gEditMenuHandler = this;
+ return;
+ }
+
+ if (!canSelectObject(obj))
+ {
+ //make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ // Since we're selecting a family, start at the root, but
+ // don't include an avatar.
+ LLViewerObject* root = obj;
+
+ while(!root->isAvatar() && root->getParent() && !root->isJointChild())
+ {
+ LLViewerObject* parent = (LLViewerObject*)root->getParent();
+ if (parent->isAvatar())
+ {
+ break;
+ }
+ root = parent;
+ }
+
+ // Collect all of the objects
+ LLDynamicArray<LLViewerObject*> objects;
+
+ root->addThisAndNonJointChildren(objects);
+ addAsFamily(objects, add_to_end);
+
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+
+ dialog_refresh_all();
+
+ // Always send to simulator, so you get a copy of the permissions
+ // structure back.
+ sendSelect();
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ root->setVelocity(LLVector3::zero);
+ root->setAcceleration(LLVector3::zero);
+ //root->setAngularVelocity(LLVector3::zero);
+ root->resetRot();
+
+ // leave component mode
+ if (!gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+ promoteSelectionToRoot();
+ }
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Select the object, parents and children.
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectObjectAndFamily(const LLDynamicArray<LLViewerObject*>& object_list,
+ BOOL send_to_sim)
+{
+ // Collect all of the objects, children included
+ LLDynamicArray<LLViewerObject*> objects;
+ LLViewerObject *object;
+ S32 i;
+
+ if (object_list.count() < 1) return;
+
+ // NOTE -- we add the objects in REVERSE ORDER
+ // to preserve the order in the mSelectedObjects list
+ for (i = object_list.count() - 1; i >= 0; i--)
+ {
+ object = object_list.get(i);
+
+ llassert( object );
+
+ if (!canSelectObject(object)) continue;
+
+ object->addThisAndNonJointChildren(objects);
+ addAsFamily(objects);
+
+ // Stop the object from moving (this anticipates changes on the
+ // simulator in LLTask::userSelect)
+ object->setVelocity(LLVector3::zero);
+ object->setAcceleration(LLVector3::zero);
+ //object->setAngularVelocity(LLVector3::zero);
+ object->resetRot();
+ }
+
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+ dialog_refresh_all();
+
+ // Almost always send to simulator, so you get a copy of the permissions
+ // structure back.
+ // JC: The one case where you don't want to do this is if you're selecting
+ // all the objects on a sim.
+ if (send_to_sim)
+ {
+ sendSelect();
+ }
+
+ // leave component mode
+ if (!gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSavedSettings.setBOOL("SelectLinkedSet", TRUE);
+ promoteSelectionToRoot();
+ }
+
+ // have selection manager handle edit menu immediately after
+ // user selects an object
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+// Use for when the simulator kills an object. This version also
+// handles informing the current tool of the object's deletion.
+//
+// Caller needs to call dialog_refresh_all if necessary.
+BOOL LLSelectMgr::selectionRemoveObject(const LLUUID &id)
+{
+
+
+ BOOL object_found = FALSE;
+ LLTool *tool = NULL;
+ if (!gNoRender)
+ {
+ tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ // It's possible that the tool is editing an object that is not selected
+ LLViewerObject* tool_editing_object = tool->getEditingObject();
+ if( tool_editing_object && tool_editing_object->mID == id)
+ {
+ tool->stopEditing();
+ object_found = TRUE;
+ }
+ }
+
+ // Iterate through selected objects list and kill the object
+ if( !object_found )
+ {
+ LLViewerObject* prevobjp = NULL;
+ for( LLViewerObject* tobjp = getFirstObject(); tobjp != NULL; tobjp = getNextObject() )
+ {
+ if (tobjp == prevobjp)
+ {
+ // Somehow we got stuck in an infinite loop... (DaveP)
+ // this logic is kind of twisted, not sure how this is happening, so...
+ llwarns << "Detected infinite loop #1 in LLSelectMgr::selectionRemoveObject:|" << llendl;
+ //MikeS. adding warning and comment...
+ //These infinite loops happen because the LLSelectMgr iteration routines are non-reentrant.
+ //deselectObjectAndFamily uses getFirstObject and getNextObject to mess with the array,
+ //resetting the arrays internal iterator state. This needs fixing BAD.
+ continue;
+ }
+ // It's possible the item being removed has an avatar sitting on it
+ // So remove the avatar that is sitting on the object.
+ if (tobjp->mID == id || tobjp->isAvatar())
+ {
+ if (!gNoRender)
+ {
+ tool->stopEditing();
+ }
+
+ // lose the selection, don't tell simulator, it knows
+ deselectObjectAndFamily(tobjp, FALSE);
+
+ if (tobjp->mID == id)
+ {
+ if(object_found == TRUE){
+ //MikeS. adding warning... This happens when removing a linked attachment while sitting on an object..
+ //I think the selection manager needs to be rewritten. BAD.
+ llwarns << "Detected infinite loop #2 in LLSelectMgr::selectionRemoveObject:|" << llendl;
+ break;
+ }
+ object_found = TRUE;
+ }
+ }
+ prevobjp = tobjp;
+ }
+ }
+
+ return object_found;
+}
+
+void LLSelectMgr::deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim)
+{
+ // bail if nothing selected or if object wasn't selected in the first place
+ if(!object) return;
+ if(!object->isSelected()) return;
+
+ // Collect all of the objects, and remove them
+ LLDynamicArray<LLViewerObject*> objects;
+ object = (LLViewerObject*)object->getRoot();
+ object->addThisAndAllChildren(objects);
+ remove(objects);
+
+ if (!send_to_sim) return;
+
+ //-----------------------------------------------------------
+ // Inform simulator of deselection
+ //-----------------------------------------------------------
+ LLViewerRegion* regionp = object->getRegion();
+
+ BOOL start_new_message = TRUE;
+ S32 select_count = 0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ for (S32 i = 0; i < objects.count(); i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_ObjectDeselect);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ select_count++;
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, (objects[i])->getLocalID());
+ select_count++;
+
+ if(msg->mCurrentSendTotal >= MTUBYTES || select_count >= MAX_OBJECTS_PER_PACKET)
+ {
+ msg->sendReliable(regionp->getHost() );
+ select_count = 0;
+ start_new_message = TRUE;
+ }
+ }
+
+ if (!start_new_message)
+ {
+ msg->sendReliable(regionp->getHost() );
+ }
+
+ updatePointAt();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim)
+{
+ // bail if nothing selected or if object wasn't selected in the first place
+ if (!object) return;
+ if (!object->isSelected() ) return;
+
+ if (send_to_sim)
+ {
+ LLViewerRegion* region = object->getRegion();
+ gMessageSystem->newMessageFast(_PREHASH_ObjectDeselect);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ gMessageSystem->sendReliable(region->getHost());
+ }
+
+ // This will refresh dialogs.
+ remove( object );
+
+ updatePointAt();
+ updateSelectionCenter();
+}
+
+
+//-----------------------------------------------------------------------------
+// addAsFamily
+//-----------------------------------------------------------------------------
+
+void LLSelectMgr::addAsFamily(LLDynamicArray<LLViewerObject*>& objects, BOOL add_to_end)
+{
+ S32 count = objects.count();
+ LLViewerObject *objectp = NULL;
+
+ LLSelectNode *nodep = NULL;
+ for (S32 i = 0; i < count; i++)
+ {
+ objectp = objects.get(i);
+
+ // Can't select yourself
+ if (objectp->mID == gAgentID
+ && !gAllowSelectAvatar)
+ {
+ continue;
+ }
+
+ if (!objectp->isSelected())
+ {
+ nodep = new LLSelectNode(objectp, TRUE);
+ if (add_to_end)
+ {
+ mSelectedObjects.addNodeAtEnd(nodep);
+ }
+ else
+ {
+ mSelectedObjects.addNode(nodep);
+ }
+ objectp->setSelected(TRUE);
+
+ if (objectp->getNumTEs() > 0)
+ {
+ nodep->selectAllTEs(TRUE);
+ }
+ else
+ {
+ // object has no faces, so don't mess with faces
+ }
+ }
+ else
+ {
+ // we want this object to be selected for real
+ // so clear transient flag
+ LLSelectNode* select_node = findSelectNode(objectp);
+ if (select_node)
+ {
+ select_node->setTransient(FALSE);
+ }
+ }
+ }
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+}
+
+//-----------------------------------------------------------------------------
+// addAsIndividual() - a single object, face, etc
+//-----------------------------------------------------------------------------
+void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoable)
+{
+ // check to see if object is already in list
+ LLSelectNode *nodep = findSelectNode(objectp);
+
+ // if not in list, add it
+ if (!nodep)
+ {
+ nodep = new LLSelectNode(objectp, TRUE);
+ mSelectedObjects.addNode(nodep);
+ }
+ else
+ {
+ // make this a full-fledged selection
+ nodep->setTransient(FALSE);
+ // Move it to the front of the list
+ mSelectedObjects.removeNode(nodep);
+ mSelectedObjects.addNode(nodep);
+ }
+
+ // Make sure the object is tagged as selected
+ objectp->setSelected( TRUE );
+
+ // And make sure we don't consider it as part of a family
+ nodep->mIndividualSelection = TRUE;
+
+ // Handle face selection
+ if (objectp->getNumTEs() <= 0)
+ {
+ // object has no faces, so don't do anything
+ }
+ else if (face == SELECT_ALL_TES)
+ {
+ nodep->selectAllTEs(TRUE);
+ }
+ else if (0 <= face && face < SELECT_MAX_TES)
+ {
+ nodep->selectTE(face, TRUE);
+ }
+ else
+ {
+ llerrs << "LLSelectMgr::add face " << face << " out-of-range" << llendl;
+ return;
+ }
+
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+void LLSelectMgr::setHoverObject(LLViewerObject *objectp)
+{
+ // Always blitz hover list when setting
+ mHoverObjects.deleteAllNodes();
+
+ if (!objectp)
+ {
+ return;
+ }
+
+ // Can't select yourself
+ if (objectp->mID == gAgentID)
+ {
+ return;
+ }
+
+ // Can't select land
+ if (objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ return;
+ }
+
+ // Collect all of the objects
+ LLDynamicArray<LLViewerObject*> objects;
+ objectp = objectp->getRootEdit();
+ objectp->addThisAndNonJointChildren(objects);
+
+
+ S32 count = objects.count();
+ LLViewerObject* cur_objectp = NULL;
+ LLSelectNode* nodep = NULL;
+ for(S32 i = 0; i < count; i++)
+ {
+ cur_objectp = objects[i];
+ nodep = new LLSelectNode(cur_objectp, FALSE);
+ mHoverObjects.addNodeAtEnd(nodep);
+ }
+
+ requestObjectPropertiesFamily(objectp);
+}
+
+LLSelectNode *LLSelectMgr::getHoverNode()
+{
+ return getHoverObjects().getFirstRootNode();
+}
+
+void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ if (objectp->getPCode() != LL_PCODE_VOLUME)
+ {
+ return;
+ }
+
+ if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) ||
+ (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove()))
+ {
+ // only select my own objects
+ return;
+ }
+
+ mRectSelectedObjects.insert(objectp);
+}
+
+void LLSelectMgr::highlightObjectAndFamily(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot();
+
+ highlightObjectOnly(root_obj);
+
+ for(U32 i = 0; i < root_obj->mChildList.size(); i++)
+ {
+ highlightObjectOnly(root_obj->mChildList[i]);
+ }
+}
+
+// Note that this ignores the "select owned only" flag
+// It's also more efficient than calling the single-object version over and
+// over.
+void LLSelectMgr::highlightObjectAndFamily(const LLDynamicArray<LLViewerObject*>& list)
+{
+ S32 i;
+ S32 count = list.count();
+
+ for (i = 0; i < count; i++)
+ {
+ LLViewerObject* object = list.get(i);
+
+ if (!object) continue;
+ if (object->getPCode() != LL_PCODE_VOLUME) continue;
+
+ LLViewerObject* root = (LLViewerObject*)object->getRoot();
+ mRectSelectedObjects.insert(root);
+
+ S32 j;
+ S32 child_count = root->mChildList.size();
+ for (j = 0; j < child_count; j++)
+ {
+ LLViewerObject* child = root->mChildList[j];
+ mRectSelectedObjects.insert(child);
+ }
+ }
+}
+
+void LLSelectMgr::unhighlightObjectOnly(LLViewerObject* objectp)
+{
+ if (!objectp || (objectp->getPCode() != LL_PCODE_VOLUME))
+ {
+ return;
+ }
+
+ mRectSelectedObjects.erase(objectp);
+}
+
+void LLSelectMgr::unhighlightObjectAndFamily(LLViewerObject* objectp)
+{
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLViewerObject* root_obj = (LLViewerObject*)objectp->getRoot();
+
+ unhighlightObjectOnly(root_obj);
+
+ for(U32 i = 0; i < root_obj->mChildList.size(); i++)
+ {
+ unhighlightObjectOnly(root_obj->mChildList[i]);
+ }
+}
+
+
+void LLSelectMgr::unhighlightAll()
+{
+ mRectSelectedObjects.clear();
+ mHighlightedObjects.deleteAllNodes();
+}
+
+void LLSelectMgr::selectHighlightedObjects()
+{
+ if (!mHighlightedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ LLSelectNode *nodep;
+ for (nodep = mHighlightedObjects.getFirstNode();
+ nodep;
+ nodep = mHighlightedObjects.getNextNode())
+ {
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (!canSelectObject(objectp))
+ {
+ continue;
+ }
+
+ // already selected
+ if (objectp->isSelected())
+ {
+ continue;
+ }
+
+ LLSelectNode* new_nodep = new LLSelectNode(*nodep);
+ mSelectedObjects.addNode(new_nodep);
+
+ // flag this object as selected
+ objectp->setSelected(TRUE);
+
+ mSelectType = getSelectTypeForObject(objectp);
+
+ // request properties on root objects
+ if (objectp->isRootEdit())
+ {
+ requestObjectPropertiesFamily(objectp);
+ }
+ }
+
+ // pack up messages to let sim know these objects are selected
+ sendSelect();
+ unhighlightAll();
+ updateSelectionCenter();
+ saveSelectedObjectTransform(SELECT_ACTION_TYPE_PICK);
+ updatePointAt();
+
+ if (getObjectCount())
+ {
+ gEditMenuHandler = this;
+ }
+}
+
+void LLSelectMgr::deselectHighlightedObjects()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mRectSelectedObjects.begin();
+ iter != mRectSelectedObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (!select_linked_set)
+ {
+ deselectObjectOnly(objectp);
+ }
+ else
+ {
+ LLViewerObject* root_object = (LLViewerObject*)objectp->getRoot();
+ if (root_object->isSelected())
+ {
+ deselectObjectAndFamily(root_object);
+ }
+ }
+ }
+
+ unhighlightAll();
+}
+
+void LLSelectMgr::addGridObject(LLViewerObject* objectp)
+{
+ LLSelectNode* nodep = new LLSelectNode(objectp, FALSE);
+ mGridObjects.addNodeAtEnd(nodep);
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ nodep = new LLSelectNode(objectp->mChildList[i], FALSE);
+ mGridObjects.addNodeAtEnd(nodep);
+ }
+}
+
+void LLSelectMgr::clearGridObjects()
+{
+ mGridObjects.deleteAllNodes();
+}
+
+void LLSelectMgr::setGridMode(EGridMode mode)
+{
+ mGridMode = mode;
+ gSavedSettings.setS32("GridMode", mode);
+ updateSelectionCenter();
+ mGridValid = FALSE;
+}
+
+void LLSelectMgr::getGrid(LLVector3& origin, LLQuaternion &rotation, LLVector3 &scale)
+{
+ LLSelectNode* grid_node = mGridObjects.getFirstNode();
+ LLViewerObject* grid_object = mGridObjects.getFirstObject();
+ //FIXME: get to work with multiple grid objects
+ if (grid_node && grid_node->getObject()->isDead())
+ {
+ mGridObjects.removeNode(grid_node);
+ grid_object = NULL;
+ }
+
+ if (mGridMode == GRID_MODE_LOCAL && gSelectMgr->getObjectCount())
+ {
+ LLBBox bbox = mSavedSelectionBBox;
+ mGridOrigin = mSavedSelectionBBox.getCenterAgent();
+ mGridRotation = mSavedSelectionBBox.getRotation();
+ mGridScale = mSavedSelectionBBox.getExtentLocal() * 0.5f;
+ }
+ else if (mGridMode == GRID_MODE_REF_OBJECT && grid_object && grid_object->mDrawable.notNull())
+ {
+ mGridRotation = grid_object->getRenderRotation();
+ LLVector3 first_grid_obj_pos = grid_object->getRenderPosition();
+
+ LLVector3 min_extents(F32_MAX, F32_MAX, F32_MAX);
+ LLVector3 max_extents(F32_MIN, F32_MIN, F32_MIN);
+ BOOL grid_changed = FALSE;
+ LLSelectNode* grid_nodep;
+ for (grid_nodep = mGridObjects.getFirstNode();
+ grid_nodep;
+ grid_nodep = mGridObjects.getNextNode())
+ {
+ grid_object = grid_nodep->getObject();
+
+ LLVector3 local_min_extents(F32_MAX, F32_MAX, F32_MAX);
+ LLVector3 local_max_extents(F32_MIN, F32_MIN, F32_MIN);
+
+ //FIXME: silhouette flag is insufficient as it gets cleared by view update
+ if (!mGridValid ||
+ grid_object->isChanged(LLXform::SILHOUETTE)
+ || (grid_object->getParent() && grid_object->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ getSilhouetteExtents(grid_nodep, mGridRotation, local_min_extents, local_max_extents);
+ grid_changed = TRUE;
+ LLVector3 object_offset = (grid_object->getRenderPosition() - first_grid_obj_pos) * ~mGridRotation;
+ local_min_extents += object_offset;
+ local_max_extents += object_offset;
+ }
+ min_extents.mV[VX] = llmin(min_extents.mV[VX], local_min_extents.mV[VX]);
+ min_extents.mV[VY] = llmin(min_extents.mV[VY], local_min_extents.mV[VY]);
+ min_extents.mV[VZ] = llmin(min_extents.mV[VZ], local_min_extents.mV[VZ]);
+ max_extents.mV[VX] = llmax(max_extents.mV[VX], local_max_extents.mV[VX]);
+ max_extents.mV[VY] = llmax(max_extents.mV[VY], local_max_extents.mV[VY]);
+ max_extents.mV[VZ] = llmax(max_extents.mV[VZ], local_max_extents.mV[VZ]);
+ }
+ if (grid_changed)
+ {
+ mGridOrigin = lerp(min_extents, max_extents, 0.5f);
+ mGridOrigin = mGridOrigin * ~mGridRotation;
+ mGridOrigin += first_grid_obj_pos;
+ mGridScale = (max_extents - min_extents) * 0.5f;
+ }
+ }
+ else // GRID_MODE_WORLD or just plain default
+ {
+ LLViewerObject* first_object = gSelectMgr->getFirstRootObject();
+ if (!first_object)
+ {
+ first_object = gSelectMgr->getFirstObject();
+ }
+
+ mGridOrigin.clearVec();
+ mGridRotation.loadIdentity();
+
+ mSelectType = getSelectTypeForObject( first_object );
+
+ switch (mSelectType)
+ {
+ case SELECT_TYPE_ATTACHMENT:
+ if (first_object)
+ {
+ // this means this object *has* to be an attachment
+ LLXform* attachment_point_xform = first_object->getRootEdit()->mDrawable->mXform.getParent();
+ mGridOrigin = attachment_point_xform->getWorldPosition();
+ mGridRotation = attachment_point_xform->getWorldRotation();
+ mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution");
+ }
+ break;
+ case SELECT_TYPE_HUD:
+ // use HUD-scaled grid
+ mGridScale = LLVector3(0.25f, 0.25f, 0.25f);
+ break;
+ case SELECT_TYPE_WORLD:
+ mGridScale = LLVector3(1.f, 1.f, 1.f) * gSavedSettings.getF32("GridResolution");
+ break;
+ }
+ }
+ llassert(mGridOrigin.isFinite());
+
+ origin = mGridOrigin;
+ rotation = mGridRotation;
+ scale = mGridScale;
+ mGridValid = TRUE;
+}
+
+
+
+LLSelectNode* LLSelectMgr::findSelectNode(LLViewerObject *object)
+{
+ return mSelectedObjects.findNode(object);
+}
+
+//-----------------------------------------------------------------------------
+// contains()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::contains(LLViewerObject* object)
+{
+ return mSelectedObjects.findNode(object) != NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// contains()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::contains(LLViewerObject* object, S32 te)
+{
+ LLSelectNode *nodep;
+ if (te == SELECT_ALL_TES)
+ {
+ // ...all faces
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject() == object)
+ {
+ BOOL all_selected = TRUE;
+ for (S32 i = 0; i < SELECT_MAX_TES; i++)
+ {
+ all_selected = all_selected && nodep->isTESelected(i);
+ }
+ return all_selected;
+ }
+ }
+ return FALSE;
+ }
+ else
+ {
+ // ...one face
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject() == object && nodep->isTESelected(te))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// remove() - an array of objects
+//-----------------------------------------------------------------------------
+
+void LLSelectMgr::remove(LLDynamicArray<LLViewerObject*>& objects)
+{
+ S32 count = objects.count();
+ LLViewerObject *objectp = NULL;
+ LLSelectNode *nodep = NULL;
+ for(S32 i = 0; i < count; i++)
+ {
+ objectp = objects.get(i);
+ for(nodep = mSelectedObjects.getFirstNode();
+ nodep != NULL;
+ nodep = mSelectedObjects.getNextNode())
+ {
+ if(nodep->getObject() == objectp)
+ {
+ objectp->setSelected(FALSE);
+ mSelectedObjects.removeNode(nodep);
+ break;
+ }
+ }
+ }
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+//-----------------------------------------------------------------------------
+// remove() - a single object
+//-----------------------------------------------------------------------------
+void LLSelectMgr::remove(LLViewerObject *objectp, S32 te, BOOL undoable)
+{
+ // check if object already in list
+ // FIXME: can we just check isSelected()?
+ LLSelectNode *nodep = findSelectNode(objectp);
+
+ if (!nodep)
+ {
+ return;
+ }
+
+
+ // if face = all, remove object from list
+ if (objectp->getNumTEs() <= 0)
+ {
+ // object doesn't have faces, so blow it away
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+ }
+ else if (te == SELECT_ALL_TES)
+ {
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+ }
+ else if (0 <= te && te < SELECT_MAX_TES)
+ {
+ // ...valid face, check to see if it was on
+ if (nodep->isTESelected(te))
+ {
+ nodep->selectTE(te, FALSE);
+ }
+ else
+ {
+ llerrs << "LLSelectMgr::remove - tried to remove TE " << te << " that wasn't selected" << llendl;
+ return;
+ }
+
+ // ...check to see if this operation turned off all faces
+ BOOL found = FALSE;
+ for (S32 i = 0; i < nodep->getObject()->getNumTEs(); i++)
+ {
+ found = found || nodep->isTESelected(i);
+ }
+
+ // ...all faces now turned off, so remove
+ if (!found)
+ {
+ mSelectedObjects.removeNode(nodep);
+ objectp->setSelected( FALSE );
+
+ // BUG: Doesn't update simulator that object is gone.
+ }
+ }
+ else
+ {
+ // ...out of range face
+ llerrs << "LLSelectMgr::remove - TE " << te << " out of range" << llendl;
+ }
+
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+
+//-----------------------------------------------------------------------------
+// removeAll()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::removeAll()
+{
+ LLViewerObject *objectp;
+ for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject() )
+ {
+ objectp->setSelected( FALSE );
+ }
+
+ mSelectedObjects.deleteAllNodes();
+
+ updateSelectionCenter();
+ dialog_refresh_all();
+}
+
+//-----------------------------------------------------------------------------
+// promoteSelectionToRoot()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::promoteSelectionToRoot()
+{
+ std::set<LLViewerObject*> selection_set;
+
+ BOOL selection_changed = FALSE;
+
+ LLSelectNode* nodep;
+ LLViewerObject *objectp;
+ for (nodep = mSelectedObjects.getFirstNode();
+ nodep;
+ nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->mIndividualSelection)
+ {
+ selection_changed = TRUE;
+ }
+
+ objectp = nodep->getObject();
+ LLViewerObject* parentp = objectp;
+ while(parentp->getParent() && !(parentp->isRootEdit() || parentp->isJointChild()))
+ {
+ parentp = (LLViewerObject*)parentp->getParent();
+ }
+
+ selection_set.insert(parentp);
+ }
+
+ if (selection_changed)
+ {
+ deselectAll();
+
+ std::set<LLViewerObject*>::iterator set_iter;
+ for (set_iter = selection_set.begin(); set_iter != selection_set.end(); ++set_iter)
+ {
+ selectObjectAndFamily(*set_iter);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// demoteSelectionToIndividuals()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::demoteSelectionToIndividuals()
+{
+ LLDynamicArray<LLViewerObject*> objects;
+
+ for (LLViewerObject* root_objectp = mSelectedObjects.getFirstRootObject();
+ root_objectp;
+ root_objectp = mSelectedObjects.getNextRootObject())
+ {
+ root_objectp->addThisAndNonJointChildren(objects);
+ }
+
+ if (objects.getLength())
+ {
+ deselectAll();
+ for(S32 i = 0; i < objects.count(); i++)
+ {
+ selectObjectOnly(objects[i]);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getObjectCount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getObjectCount()
+{
+ return mSelectedObjects.getNumNodes();
+}
+
+
+//-----------------------------------------------------------------------------
+// getTECount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getTECount()
+{
+ S32 count = 0;
+
+ LLSelectNode* nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if (nodep->getObject())
+ {
+ S32 num_tes = nodep->getObject()->getNumTEs();
+ for (S32 te = 0; te < num_tes; te++)
+ {
+ if (nodep->isTESelected(te))
+ {
+ count++;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// getRootObjectCount()
+//-----------------------------------------------------------------------------
+S32 LLSelectMgr::getRootObjectCount()
+{
+ LLSelectNode *nodep;
+
+ S32 count = 0;
+ for(nodep = mSelectedObjects.getFirstRootNode(); nodep; nodep = mSelectedObjects.getNextRootNode())
+ {
+ ++count;
+ }
+ return count;
+}
+
+
+//-----------------------------------------------------------------------------
+// dump()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::dump()
+{
+ llinfos << "Selection Manager: " << mSelectedObjects.getNumNodes() << " items" << llendl;
+
+ llinfos << "TE mode " << mTEMode << llendl;
+
+ S32 i = 0;
+
+ LLViewerObject *objectp;
+ for (objectp = mSelectedObjects.getFirstObject();
+ objectp;
+ objectp = mSelectedObjects.getNextObject())
+ {
+ llinfos << "Object " << i << " type " << LLPrimitive::pCodeToString(objectp->getPCode()) << llendl;
+ llinfos << " hasLSL " << objectp->flagScripted() << llendl;
+ llinfos << " hasTouch " << objectp->flagHandleTouch() << llendl;
+ llinfos << " hasMoney " << objectp->flagTakesMoney() << llendl;
+ llinfos << " getposition " << objectp->getPosition() << llendl;
+ llinfos << " getpositionAgent " << objectp->getPositionAgent() << llendl;
+ llinfos << " getpositionRegion " << objectp->getPositionRegion() << llendl;
+ llinfos << " getpositionGlobal " << objectp->getPositionGlobal() << llendl;
+ LLDrawable* drawablep = objectp->mDrawable;
+ llinfos << " " << (drawablep&& drawablep->isVisible() ? "visible" : "invisible") << llendl;
+ llinfos << " " << (drawablep&& drawablep->isState(LLDrawable::FORCE_INVISIBLE) ? "force_invisible" : "") << llendl;
+ i++;
+ }
+
+ // Face iterator
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&objectp, &te);
+ objectp;
+ mSelectedObjects.getNextTE(&objectp, &te))
+ {
+ llinfos << "Object " << objectp << " te " << te << llendl;
+ }
+
+ llinfos << mHighlightedObjects.getNumNodes() << " objects currently highlighted." << llendl;
+
+ llinfos << "Center global " << mSelectionCenterGlobal << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// cleanup()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::cleanup()
+{
+ mSilhouetteImagep = NULL;
+}
+
+
+//---------------------------------------------------------------------------
+// Manipulate properties of selected objects
+//---------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// selectionSetImage()
+//-----------------------------------------------------------------------------
+//FIXME: re-arch texture applying out of lltooldraganddrop
+void LLSelectMgr::selectionSetImage(const LLUUID& imageid)
+{
+ // First for (no copy) textures and multiple object selection
+ LLViewerInventoryItem* item = gInventory.getItem(imageid);
+
+ if(item
+ && !item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID())
+ && (mSelectedObjects.getNumNodes() > 1) )
+ {
+ llwarns << "Attempted to apply no-copy texture to multiple objects"
+ << llendl;
+ return;
+ }
+
+ LLViewerObject* objectp;
+ S32 te;
+
+ // Apply the texture to each side
+ for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te))
+ {
+
+ if (item)
+ {
+ LLToolDragAndDrop::dropTextureOneFace(objectp,te,item,LLToolDragAndDrop::SOURCE_AGENT,LLUUID::null);
+
+ // HACK! HACK! ARG!
+ // FIXME: Replace mSelectedObjects with a REAL container class!
+ LLViewerObject* tmp_object;
+ S32 tmp_te;
+ mSelectedObjects.getCurrentTE(&tmp_object,&tmp_te);
+ if ((tmp_object != objectp) || (tmp_te != te) )
+ {
+ //AAARG someone has moved our list around!
+ mSelectedObjects.getFirstTE(&tmp_object, &tmp_te);
+ while ((tmp_object != objectp) || (tmp_te != te))
+ {
+ mSelectedObjects.getNextTE(&tmp_object, &tmp_te);
+ }
+ }
+ }
+ else
+ {
+ // Texture picker defaults aren't inventory items
+ // * Don't need to worry about permissions for them
+ // * Can just apply the texture and be done with it.
+ objectp->setTEImage(te, gImageList.getImage(imageid));
+ objectp->sendTEUpdate();
+ }
+ }
+
+ // 1 particle effect per object
+ if (mSelectType != SELECT_TYPE_HUD)
+ {
+ for (objectp = mSelectedObjects.getFirstObject(); objectp; objectp = mSelectedObjects.getNextObject())
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(objectp);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetColor()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetColor(const LLColor4 &color)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetColorOnly()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetColorOnly(const LLColor4 &color)
+{
+ LLViewerObject* object;
+ LLColor4 new_color = color;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLColor4 prev_color = object->getTE(te)->getColor();
+ new_color.mV[VALPHA] = prev_color.mV[VALPHA];
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, new_color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// selectionSetAlphaOnly()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetAlphaOnly(const F32 alpha)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLColor4 prev_color = object->getTE(te)->getColor();
+ prev_color.mV[VALPHA] = alpha;
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, prev_color);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionRevertColors()
+{
+ LLViewerObject* object;
+ S32 te;
+
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLSelectNode* nodep = mSelectedObjects.findNode(object);
+ if (nodep && te < (S32)nodep->mSavedColors.size())
+ {
+ LLColor4 color = nodep->mSavedColors[te];
+ // update viewer side color in anticipation of update from simulator
+ object->setTEColor(te, color);
+ }
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+BOOL LLSelectMgr::selectionRevertTextures()
+{
+ LLViewerObject* object;
+ S32 te;
+
+ BOOL revert_successful = TRUE;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ LLSelectNode* nodep = mSelectedObjects.findNode(object);
+ if (nodep && te < (S32)nodep->mSavedTextures.size())
+ {
+ LLUUID id = nodep->mSavedTextures[te];
+ // update textures on viewer side
+ if (id.isNull())
+ {
+ // this was probably a no-copy texture, leave image as-is
+ revert_successful = FALSE;
+ }
+ else
+ {
+ object->setTEImage(te, gImageList.getImage(id));
+ }
+ }
+ }
+ }
+
+ // propagate texture changes to server
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+
+ return revert_successful;
+}
+
+void LLSelectMgr::selectionSetBumpmap(U8 bumpmap)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEBumpmap(te, bumpmap);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetTexGen(U8 texgen)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTETexGen(te, texgen);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+void LLSelectMgr::selectionSetShiny(U8 shiny)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEShiny(te, shiny);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetFullbright(U8 fullbright)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEFullbright(te, fullbright);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ if (fullbright)
+ {
+ U8 material = object->getMaterial();
+ U8 mcode = material & LL_MCODE_MASK;
+ if (mcode == LL_MCODE_LIGHT)
+ {
+ mcode = LL_MCODE_GLASS;
+ material = (material & ~LL_MCODE_MASK) | mcode;
+ object->setMaterial(material);
+ object->sendMaterialUpdate();
+ }
+ }
+ }
+ }
+}
+
+void LLSelectMgr::selectionSetMediaTypeAndURL(U8 media_type, const std::string& media_url)
+{
+ LLViewerObject* object;
+ S32 te;
+ U8 media_flags = LLTextureEntry::MF_NONE;
+ if (media_type == LLViewerObject::MEDIA_TYPE_WEB_PAGE)
+ {
+ media_flags = LLTextureEntry::MF_WEB_PAGE;
+ }
+
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te) )
+ {
+ if (object->permModify())
+ {
+ // update viewer side color in anticipation of update from simulator
+ object->setTEMediaFlags(te, media_flags);
+ }
+ }
+
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ // JAMESDEBUG TODO set object media type
+ object->setMediaType(media_type);
+ object->setMediaURL(media_url);
+
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// findObjectPermissions()
+//-----------------------------------------------------------------------------
+LLPermissions* LLSelectMgr::findObjectPermissions(const LLViewerObject* object)
+{
+ LLSelectNode* nodep;
+
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode() )
+ {
+ if((nodep->getObject() == object) && nodep->mValid)
+ {
+ return nodep->mPermissions;
+ }
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionGetTexUUID()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetTexUUID(LLUUID& id)
+{
+ LLViewerObject* first_objectp;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_objectp, &first_te);
+
+ // nothing selected
+ if (!first_objectp)
+ {
+ return FALSE;
+ }
+
+ LLViewerImage* first_imagep = first_objectp->getTEImage(first_te);
+
+ if (!first_imagep)
+ {
+ return FALSE;
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject *objectp;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&objectp, &te); objectp; mSelectedObjects.getNextTE(&objectp, &te) )
+ {
+ if (objectp->getTEImage(te) != first_imagep)
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ id = first_imagep->getID();
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetColor()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetColor(LLColor4 &color)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ LLColor4 first_color;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_color = first_object->getTE(first_te)->getColor();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getColor() != first_color))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ color = first_color;
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionGetBumpmap()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetBumpmap(U8 *bumpmap)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getBumpmap();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getBumpmap() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *bumpmap = first_value;
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetShiny()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetShiny(U8 *shiny)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getShiny();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getShiny() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *shiny = first_value;
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetFullbright()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetFullbright(U8 *fullbright)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return FALSE;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return FALSE;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getFullbright();
+ }
+
+ BOOL identical = TRUE;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getFullbright() != first_value))
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *fullbright = first_value;
+ return identical;
+}
+
+// JAMESDEBUG TODO make this return mediatype off viewer object
+bool LLSelectMgr::selectionGetMediaType(U8 *media_type)
+{
+ LLViewerObject* first_object;
+ S32 first_te;
+ mSelectedObjects.getPrimaryTE(&first_object, &first_te);
+
+ // nothing selected
+ if (!first_object)
+ {
+ return false;
+ }
+
+ U8 first_value;
+ if (!first_object->getTE(first_te))
+ {
+ return false;
+ }
+ else
+ {
+ first_value = first_object->getTE(first_te)->getMediaFlags();
+ }
+
+ bool identical = true;
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->getTE(te) || (object->getTE(te)->getMediaFlags() != first_value))
+ {
+ identical = false;
+ break;
+ }
+ }
+
+ *media_type = first_value;
+ return identical;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// selectionSetMaterial()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionSetMaterial(U8 material)
+{
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstObject(); object != NULL; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->permModify())
+ {
+ U8 cur_material = object->getMaterial();
+ material |= (cur_material & ~LL_MCODE_MASK);
+ object->setMaterial(material);
+ object->sendMaterialUpdate();
+ }
+ }
+}
+
+// True if all selected objects have this PCode
+BOOL LLSelectMgr::selectionAllPCode(LLPCode code)
+{
+ LLViewerObject *object;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if (object->getPCode() != code)
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectionGetMaterial()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectionGetMaterial(U8 *out_material)
+{
+ LLViewerObject *object = mSelectedObjects.getFirstObject();
+ if (!object) return FALSE;
+
+ U8 material = object->getMaterial();
+
+ BOOL identical = TRUE;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if ( material != object->getMaterial())
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *out_material = material;
+ return identical;
+}
+
+BOOL LLSelectMgr::selectionGetClickAction(U8 *out_action)
+{
+ LLViewerObject *object = mSelectedObjects.getFirstObject();
+ if (!object) return FALSE;
+
+ U8 action = object->getClickAction();
+
+ BOOL identical = TRUE;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ if ( action != object->getClickAction())
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ *out_action = action;
+ return identical;
+}
+
+void LLSelectMgr::selectionSetClickAction(U8 action)
+{
+ LLViewerObject* object = NULL;
+ for ( object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ object->setClickAction(action);
+ }
+ sendListToRegions(
+ "ObjectClickAction",
+ packAgentAndSessionID,
+ packObjectClickAction,
+ &action,
+ SEND_INDIVIDUALS);
+}
+
+
+//-----------------------------------------------------------------------------
+// godlike requests
+//-----------------------------------------------------------------------------
+
+typedef std::pair<const LLString, const LLString> godlike_request_t;
+void LLSelectMgr::sendGodlikeRequest(const LLString& request, const LLString& param)
+{
+ // If the agent is neither godlike nor an estate owner, the server
+ // will reject the request.
+ LLString message_type;
+ if (gAgent.isGodlike())
+ {
+ message_type = "GodlikeMessage";
+ }
+ else
+ {
+ message_type = "EstateOwnerMessage";
+ }
+
+ godlike_request_t data(request, param);
+ if(!getRootObjectCount())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage(message_type.c_str());
+ LLSelectMgr::packGodlikeHead(&data);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ sendListToRegions(message_type, packGodlikeHead, packObjectIDAsParam, &data, SEND_ONLY_ROOTS);
+ }
+}
+
+void LLSelectMgr::packGodlikeHead(void* user_data)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ godlike_request_t* data = (godlike_request_t*)user_data;
+ msg->nextBlock("MethodData");
+ msg->addString("Method", data->first.c_str());
+ msg->addUUID("Invoice", LLUUID::null);
+
+ // The parameters used to be restricted to either string or
+ // integer. This mimics that behavior under the new 'string-only'
+ // parameter list by not packing a string if there wasn't one
+ // specified. The object ids will be packed in the
+ // packObjectIDAsParam() method.
+ if(data->second.size() > 0)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", data->second);
+ }
+}
+
+// static
+void LLSelectMgr::packObjectIDAsParam(LLSelectNode* node, void *)
+{
+ char buf [MAX_STRING];
+ sprintf(buf, "%u", node->getObject()->getLocalID());
+ gMessageSystem->nextBlock("ParamList");
+ gMessageSystem->addString("Parameter", buf);
+}
+
+//-----------------------------------------------------------------------------
+// Rotation options
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionResetRotation()
+{
+ LLQuaternion identity(0.f, 0.f, 0.f, 1.f);
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() )
+ {
+ object->setRotation(identity);
+ if (object->mDrawable.notNull())
+ {
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ }
+ object->sendRotationUpdate();
+ }
+}
+
+void LLSelectMgr::selectionRotateAroundZ(F32 degrees)
+{
+ LLQuaternion rot( degrees * DEG_TO_RAD, LLVector3(0,0,1) );
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstRootObject(); object; object = mSelectedObjects.getNextRootObject() )
+ {
+ object->setRotation( object->getRotationEdit() * rot );
+ if (object->mDrawable.notNull())
+ {
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ }
+ object->sendRotationUpdate();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionTexScaleAutofit()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionTexScaleAutofit(F32 repeats_per_meter)
+{
+ LLViewerObject* object;
+ S32 te;
+ for (mSelectedObjects.getFirstTE(&object, &te); object; mSelectedObjects.getNextTE(&object, &te))
+ {
+ if (!object->permModify())
+ {
+ continue;
+ }
+
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ // Compute S,T to axis mapping
+ U32 s_axis, t_axis;
+ if (!getTESTAxes(object, te, &s_axis, &t_axis))
+ {
+ continue;
+ }
+
+ F32 new_s = object->getScale().mV[s_axis] * repeats_per_meter;
+ F32 new_t = object->getScale().mV[t_axis] * repeats_per_meter;
+
+ object->setTEScale(te, new_s, new_t);
+ }
+
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject())
+ {
+ if (object->permModify())
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+// BUG: Only works for boxes.
+// Face numbering for flex boxes as of 1.14.2002
+//-----------------------------------------------------------------------------
+// getFaceSTAxes()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::getTESTAxes(const LLViewerObject* object, const U8 face, U32* s_axis, U32* t_axis)
+{
+ if (face == 0)
+ {
+ *s_axis = VX; *t_axis = VY;
+ return TRUE;
+ }
+ else if (face == 1)
+ {
+ *s_axis = VX; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 2)
+ {
+ *s_axis = VY; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 3)
+ {
+ *s_axis = VX; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 4)
+ {
+ *s_axis = VY; *t_axis = VZ;
+ return TRUE;
+ }
+ else if (face == 5)
+ {
+ *s_axis = VX; *t_axis = VY;
+ return TRUE;
+ }
+ else
+ {
+ // unknown face
+ return FALSE;
+ }
+}
+
+// Called at the end of a scale operation, this adjusts the textures to attempt to
+// maintain a constant repeats per meter.
+// BUG: Only works for flex boxes.
+//-----------------------------------------------------------------------------
+// adjustTexturesByScale()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::adjustTexturesByScale(BOOL send_to_sim, BOOL stretch)
+{
+ LLViewerObject* object;
+ LLSelectNode* selectNode;
+
+ BOOL send = FALSE;
+
+ for (selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ object = selectNode->getObject();
+ if (!object->permModify())
+ {
+ continue;
+ }
+
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ for (U8 te_num = 0; te_num < object->getNumTEs(); te_num++)
+ {
+ const LLTextureEntry* tep = object->getTE(te_num);
+
+ BOOL planar = tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR;
+ if (planar == stretch)
+ {
+ // Figure out how S,T changed with scale operation
+ U32 s_axis, t_axis;
+ if (!getTESTAxes(object, te_num, &s_axis, &t_axis)) continue;
+
+ LLVector3 scale_ratio = selectNode->mTextureScaleRatios[te_num];
+ LLVector3 object_scale = object->getScale();
+
+ // Apply new scale to face
+ if (planar)
+ {
+ object->setTEScale(te_num, 1.f/object_scale.mV[s_axis]*scale_ratio.mV[s_axis],
+ 1.f/object_scale.mV[t_axis]*scale_ratio.mV[t_axis]);
+ }
+ else
+ {
+ object->setTEScale(te_num, scale_ratio.mV[s_axis]*object_scale.mV[s_axis],
+ scale_ratio.mV[t_axis]*object_scale.mV[t_axis]);
+ }
+ send = send_to_sim;
+ }
+ }
+
+ if (send)
+ {
+ object->sendTEUpdate();
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// selectionResetTexInfo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::selectionResetTexInfo(S32 selected_face)
+{
+ S32 start_face, end_face;
+
+ LLViewerObject* object;
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject())
+ {
+ if (!object->permModify())
+ {
+ continue;
+ }
+ if (object->getNumTEs() == 0)
+ {
+ continue;
+ }
+
+ if (selected_face == -1)
+ {
+ start_face = 0;
+ end_face = object->getNumTEs() - 1;
+ }
+ else
+ {
+ start_face = selected_face;
+ end_face = selected_face;
+ }
+
+ for (S32 face = start_face; face <= end_face; face++)
+ {
+ // Actually, each object should reset to its appropriate value.
+ object->setTEScale(face, 1.f, 1.f);
+ object->setTEOffset(face, 0.f, 0.f);
+ object->setTERotation(face, 0.f);
+ }
+
+ object->sendTEUpdate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getFirstEditableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstEditableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permModify() )
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object)
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstMoveableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstMoveableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permMove() )
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object && !object->isJointChild())
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstEditableNode()
+//-----------------------------------------------------------------------------
+LLSelectNode* LLSelectMgr::getFirstEditableNode(BOOL get_root)
+{
+ LLSelectNode* selectNode = NULL;
+
+ if (get_root)
+ {
+ for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode())
+ {
+ if( selectNode->getObject()->permModify() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+ }
+ for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ if( selectNode->getObject()->permModify() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstMoveableNode()
+//-----------------------------------------------------------------------------
+LLSelectNode* LLSelectMgr::getFirstMoveableNode(BOOL get_root)
+{
+ LLSelectNode* selectNode = NULL;
+
+ if (get_root)
+ {
+ for(selectNode = mSelectedObjects.getFirstRootNode(); selectNode; selectNode = mSelectedObjects.getNextRootNode())
+ {
+ if( selectNode->getObject()->permMove() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+ }
+ for(selectNode = mSelectedObjects.getFirstNode(); selectNode; selectNode = mSelectedObjects.getNextNode())
+ {
+ if( selectNode->getObject()->permMove() )
+ {
+ return selectNode;
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstDeleteableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstDeleteableObject(BOOL get_root)
+{
+ //RN: don't currently support deletion of child objects, as that requires separating them first
+ // then derezzing to trash
+ get_root = TRUE;
+
+ LLViewerObject* object = NULL;
+ if (get_root)
+ {
+ for(LLViewerObject* current = getFirstRootObject();
+ current != NULL;
+ current = getNextRootObject())
+ {
+ // you can delete an object if permissions allow it, you are
+ // the owner, you are an officer in the group that owns the
+ // object, or you are not the owner but it is on land you own
+ // or land owned by your group. (whew!)
+ if( (current->permModify())
+ || (current->permYouOwner())
+ || (!current->permAnyOwner()) // public
+ || (current->isOverAgentOwnedLand())
+ || (current->isOverGroupOwnedLand())
+ )
+ {
+
+ if( !current->isAttachment() )
+ {
+ object = current;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ for(LLViewerObject* current = getFirstObject();
+ current != NULL;
+ current = getNextObject())
+ {
+ // you can delete an object if permissions allow it, you are
+ // the owner, you are an officer in the group that owns the
+ // object, or you are not the owner but it is on land you own
+ // or land owned by your group. (whew!)
+ if( (current->permModify())
+ || (current->permYouOwner())
+ || (!current->permAnyOwner()) // public
+ || (current->isOverAgentOwnedLand())
+ || (current->isOverGroupOwnedLand())
+ )
+ {
+ if( !current->isAttachment() )
+ {
+ object = current;
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstCopyableObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectMgr::getFirstCopyableObject(BOOL get_root)
+{
+ LLViewerObject* object = NULL;
+ for(LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject())
+ {
+ if( cur->permCopy() && !cur->isAttachment())
+ {
+ object = cur;
+ break;
+ }
+ }
+
+ if (get_root && object)
+ {
+ LLViewerObject *parent;
+ while ((parent = (LLViewerObject*)object->getParent()))
+ {
+ if (parent->isSelected())
+ {
+ object = parent;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return object;
+}
+
+//-----------------------------------------------------------------------------
+// areMultpleEditableObjectsSelected()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::areMultpleEditableObjectsSelected()
+{
+ S32 count = 0;
+ for( LLViewerObject* cur = mSelectedObjects.getFirstObject(); cur; cur = mSelectedObjects.getNextObject() )
+ {
+ if( cur->permModify() )
+ {
+ count++;
+ if( count > 1 )
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetAllRootsValid()
+// Returns true if the viewer has information on all selected objects
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetAllRootsValid()
+{
+ for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+
+ if( !node->getObject() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetAllValid()
+// Returns true if the viewer has information on all selected objects
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetAllValid()
+{
+ for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() )
+ {
+
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+
+ if( !node->getObject() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetModify() - return true if current agent can modify all
+// selected objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetModify()
+{
+ for( LLSelectNode* node = getFirstNode(); node; node = getNextNode() )
+ {
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if( !object || !object->permModify() )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetRootsModify() - return true if current agent can modify all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsModify()
+{
+ for( LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if( !node->mValid )
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if( !object || !object->permModify() )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetRootsTransfer() - return true if current agent can transfer all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsTransfer()
+{
+ for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if(!object || !object->permTransfer())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetRootsCopy() - return true if current agent can copy all
+// selected root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetRootsCopy()
+{
+ for(LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ return FALSE;
+ }
+ LLViewerObject* object = node->getObject();
+ if(!object || !object->permCopy())
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetCreator()
+// Creator information only applies to root objects.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetCreator(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getCreator();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getCreator() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ if (identical)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign( "(multiple)" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetOwner()
+// Owner information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetOwner(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ BOOL group_owner = FALSE;
+ id.setNull();
+ node->mPermissions->getOwnership(id, group_owner);
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ LLUUID owner_id;
+ BOOL is_group_owned = FALSE;
+ if (!(node->mPermissions->getOwnership(owner_id, is_group_owned))
+ || owner_id != id )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ BOOL public_owner = (id.isNull() && !group_owner);
+
+ if (identical)
+ {
+ if (group_owner)
+ {
+ name.assign( "(Group Owned)");
+ }
+ else if(!public_owner)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign("Public");
+ }
+ }
+ else
+ {
+ name.assign( "(multiple)" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetLastOwner()
+// Owner information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetLastOwner(LLUUID& id, LLString& name)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getLastOwner();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getLastOwner() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ BOOL public_owner = (id.isNull());
+
+ if (identical)
+ {
+ if(!public_owner)
+ {
+ char firstname[DB_FIRST_NAME_BUF_SIZE];
+ char lastname[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(id, firstname, lastname);
+ name.assign( firstname );
+ name.append( " " );
+ name.append( lastname );
+ }
+ else
+ {
+ name.assign("Public or Group");
+ }
+ }
+ else
+ {
+ name.assign( "" );
+ }
+
+ return identical;
+}
+
+
+//-----------------------------------------------------------------------------
+// selectGetGroup()
+// Group information only applies to roots.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetGroup(LLUUID& id)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ id = node->mPermissions->getGroup();
+
+ BOOL identical = TRUE;
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ identical = FALSE;
+ break;
+ }
+
+ if ( !(id == node->mPermissions->getGroup() ) )
+ {
+ identical = FALSE;
+ break;
+ }
+ }
+
+ return identical;
+}
+
+//-----------------------------------------------------------------------------
+// selectIsGroupOwned()
+// Only operates on root nodes.
+// Returns TRUE if all have valid data and they are all group owned.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectIsGroupOwned()
+{
+ LLSelectNode* node = getFirstRootNode();
+ if(!node) node = getFirstNode();
+ if(!node) return FALSE;
+ if(!node->mValid) return FALSE;
+ LLViewerObject* obj = node->getObject();
+ if(!obj) return FALSE;
+ if(!(obj->isRoot() || obj->isJointChild())) return FALSE;
+
+ BOOL is_group_owned = node->mPermissions->isGroupOwned();
+
+ if(is_group_owned)
+ {
+ for ( node = getNextRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ is_group_owned = FALSE;
+ break;
+ }
+
+ if ( !( node->mPermissions->isGroupOwned() ) )
+ {
+ is_group_owned = FALSE;
+ break;
+ }
+ }
+ }
+ return is_group_owned;
+}
+
+//-----------------------------------------------------------------------------
+// selectGetPerm()
+// Only operates on root nodes.
+// Returns TRUE if all have valid data.
+// mask_on has bits set to true where all permissions are true
+// mask_off has bits set to true where all permissions are false
+// if a bit is off both in mask_on and mask_off, the values differ within
+// the selection.
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::selectGetPerm(U8 which_perm, U32* mask_on, U32* mask_off)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+
+ U32 mask;
+ U32 mask_and = 0xffffffff;
+ U32 mask_or = 0x00000000;
+ BOOL all_valid = TRUE;
+
+ for ( node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if (!node->mValid)
+ {
+ all_valid = FALSE;
+ break;
+ }
+
+ switch( which_perm )
+ {
+ case PERM_BASE:
+ mask = node->mPermissions->getMaskBase();
+ break;
+ case PERM_OWNER:
+ mask = node->mPermissions->getMaskOwner();
+ break;
+ case PERM_GROUP:
+ mask = node->mPermissions->getMaskGroup();
+ break;
+ case PERM_EVERYONE:
+ mask = node->mPermissions->getMaskEveryone();
+ break;
+ case PERM_NEXT_OWNER:
+ mask = node->mPermissions->getMaskNextOwner();
+ break;
+ default:
+ mask = 0x0;
+ break;
+ }
+ mask_and &= mask;
+ mask_or |= mask;
+ }
+
+ if (all_valid)
+ {
+ // ...true through all ANDs means all true
+ *mask_on = mask_and;
+
+ // ...false through all ORs means all false
+ *mask_off = ~mask_or;
+ return TRUE;
+ }
+ else
+ {
+ *mask_on = 0;
+ *mask_off = 0;
+ return FALSE;
+ }
+}
+
+
+
+BOOL LLSelectMgr::selectGetOwnershipCost(S32* out_cost)
+{
+ return mSelectedObjects.getOwnershipCost(*out_cost);
+}
+
+BOOL LLSelectMgr::selectGetPermissions(LLPermissions& perm)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ perm = *(node->mPermissions);
+ for(node = getNextRootNode(); node != NULL; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ perm.accumulate(*(node->mPermissions));
+ }
+ return valid;
+}
+
+
+void LLSelectMgr::selectDelete()
+{
+ S32 deleteable_count = 0;
+
+ BOOL locked_but_deleteable_object = FALSE;
+ BOOL no_copy_but_deleteable_object = FALSE;
+ BOOL all_owned_by_you = TRUE;
+ for(LLViewerObject* obj = getFirstObject();
+ obj != NULL;
+ obj = getNextObject())
+ {
+ if( obj->isAttachment() )
+ {
+ continue;
+ }
+
+ deleteable_count++;
+
+ // Check to see if you can delete objects which are locked.
+ if(!obj->permMove())
+ {
+ locked_but_deleteable_object = TRUE;
+ }
+ if(!obj->permCopy())
+ {
+ no_copy_but_deleteable_object = TRUE;
+ }
+ if(!obj->permYouOwner())
+ {
+ all_owned_by_you = FALSE;
+ }
+ }
+
+ if( 0 == deleteable_count )
+ {
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ if(locked_but_deleteable_object ||
+ no_copy_but_deleteable_object ||
+ !all_owned_by_you)
+ {
+ // convert any transient pie-menu selections to full selection so this operation
+ // has some context
+ // NOTE: if user cancels delete operation, this will potentially leave objects selected outside of build mode
+ // but this is ok, if not ideal
+ convertTransient();
+
+ //This is messy, but needed to get all english our of the UI.
+ if(locked_but_deleteable_object && !no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //Locked only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLock",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //No Copy only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopy",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //not owned only
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(locked_but_deleteable_object && no_copy_but_deleteable_object && all_owned_by_you)
+ {
+ //locked and no copy
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopy",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(locked_but_deleteable_object && !no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //locked and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else if(!locked_but_deleteable_object && no_copy_but_deleteable_object && !all_owned_by_you)
+ {
+ //no copy and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteNoCopyNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+ else
+ {
+ //locked, no copy and not owned
+ gViewerWindow->alertXml( "ConfirmObjectDeleteLockNoCopyNoOwn",
+ &LLSelectMgr::confirmDelete,
+ this);
+ }
+
+
+
+ }
+ else
+ {
+ confirmDelete(0, (void*)this);
+ }
+}
+
+// static
+void LLSelectMgr::confirmDelete(S32 option, void* data)
+{
+ LLSelectMgr* self = (LLSelectMgr*)data;
+ if(!self) return;
+ switch(option)
+ {
+ case 0:
+ {
+ // TODO: Make sure you have delete permissions on all of them.
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ // attempt to derez into the trash.
+ LLDeRezInfo* info = new LLDeRezInfo(DRD_TRASH, trash_id);
+ self->sendListToRegions("DeRezObject",
+ packDeRezHeader,
+ packObjectLocalID,
+ (void*)info,
+ SEND_ONLY_ROOTS);
+ // VEFFECT: Delete Object - one effect for all deletes
+ if (self->mSelectType != SELECT_TYPE_HUD)
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal( self->getSelectionCenterGlobal() );
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ F32 duration = 0.5f;
+ duration += self->getObjectCount() / 64.f;
+ effectp->setDuration(duration);
+ }
+
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+
+ // Keep track of how many objects have been deleted.
+ F64 obj_delete_count = gViewerStats->getStat(LLViewerStats::ST_OBJECT_DELETE_COUNT);
+ obj_delete_count += self->getObjectCount();
+ gViewerStats->setStat(LLViewerStats::ST_OBJECT_DELETE_COUNT, obj_delete_count );
+ }
+ break;
+ case 1:
+ default:
+ break;
+ }
+}
+
+
+void LLSelectMgr::selectForceDelete()
+{
+ sendListToRegions(
+ "ObjectDelete",
+ packDeleteHeader,
+ packObjectLocalID,
+ (void*)TRUE,
+ SEND_ONLY_ROOTS);
+}
+
+
+// returns TRUE if anything is for sale. calculates the total price
+// and stores that value in price.
+BOOL LLSelectMgr::selectIsForSale(S32& price)
+{
+ BOOL any_for_sale = FALSE;
+ price = 0;
+
+ LLSelectNode *node;
+ for (node = getFirstRootNode(); node; node = getNextRootNode() )
+ {
+ if (node->mSaleInfo.isForSale())
+ {
+ price += node->mSaleInfo.getSalePrice();
+ any_for_sale = TRUE;
+ }
+ }
+
+ return any_for_sale;
+
+}
+
+// returns TRUE if all nodes are valid. method also stores an
+// accumulated sale info.
+BOOL LLSelectMgr::selectGetSaleInfo(LLSaleInfo& sale_info)
+{
+ LLSelectNode* node = getFirstRootNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ sale_info = node->mSaleInfo;
+ for(node = getNextRootNode(); node != NULL; node = getNextRootNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ sale_info.accumulate(node->mSaleInfo);
+ }
+ return valid;
+}
+
+BOOL LLSelectMgr::selectGetAggregatePermissions(LLAggregatePermissions& ag_perm)
+{
+ LLSelectNode* node = getFirstNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ ag_perm = node->mAggregatePerm;
+ for(node = getNextNode(); node != NULL; node = getNextNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ ag_perm.aggregate(node->mAggregatePerm);
+ }
+ return valid;
+}
+
+BOOL LLSelectMgr::selectGetAggregateTexturePermissions(LLAggregatePermissions& ag_perm)
+{
+ LLSelectNode* node = getFirstNode();
+ if (!node) return FALSE;
+ if (!node->mValid) return FALSE;
+ BOOL valid = TRUE;
+ ag_perm = node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm;
+ for(node = getNextNode(); node != NULL; node = getNextNode())
+ {
+ if(!node->mValid)
+ {
+ valid = FALSE;
+ break;
+ }
+ ag_perm.aggregate(node->getObject()->permYouOwner() ? node->mAggregateTexturePermOwner : node->mAggregateTexturePerm);
+ }
+ return valid;
+}
+
+
+// returns TRUE is any node is currenly worn as an attachment
+BOOL LLSelectMgr::selectionIsAttachment()
+{
+ return (mSelectType == SELECT_TYPE_ATTACHMENT || mSelectType == SELECT_TYPE_HUD);
+}
+
+//--------------------------------------------------------------------
+// Duplicate objects
+//--------------------------------------------------------------------
+
+// JC - If this doesn't work right, duplicate the selection list
+// before doing anything, do a deselect, then send the duplicate
+// messages.
+struct LLDuplicateData
+{
+ LLVector3 offset;
+ U32 flags;
+};
+
+void LLSelectMgr::selectDuplicate(const LLVector3& offset, BOOL select_copy)
+{
+ if (selectionIsAttachment())
+ {
+ //RN: do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+ LLDuplicateData data;
+
+ data.offset = offset;
+ data.flags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
+
+ sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+
+ if (select_copy)
+ {
+ // the new copy will be coming in selected
+ deselectAll();
+ }
+ else
+ {
+ for (LLSelectNode* node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ node->mDuplicated = TRUE;
+ node->mDuplicatePos = node->getObject()->getPositionGlobal();
+ node->mDuplicateRot = node->getObject()->getRotation();
+ }
+ }
+}
+
+void LLSelectMgr::repeatDuplicate()
+{
+ if (selectionIsAttachment())
+ {
+ //RN: do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ LLSelectNode* node;
+ LLDynamicArray<LLViewerObject*> non_duplicated_objects;
+
+ for (node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if (!node->mDuplicated)
+ {
+ non_duplicated_objects.put(node->getObject());
+ }
+ }
+
+ // make sure only previously duplicated objects are selected
+ for (S32 i = 0; i < non_duplicated_objects.count(); i++)
+ {
+ deselectObjectAndFamily(non_duplicated_objects[i]);
+ }
+
+ // duplicate objects in place
+ LLDuplicateData data;
+
+ data.offset = LLVector3::zero;
+ data.flags = 0x0;
+
+ sendListToRegions("ObjectDuplicate", packDuplicateHeader, packDuplicate, &data, SEND_ONLY_ROOTS);
+
+ // move current selection based on delta from duplication position and update duplication position
+ for (node = getFirstRootNode(); node; node = getNextRootNode())
+ {
+ if (node->mDuplicated)
+ {
+ LLQuaternion cur_rot = node->getObject()->getRotation();
+ LLQuaternion rot_delta = (~node->mDuplicateRot * cur_rot);
+ LLQuaternion new_rot = cur_rot * rot_delta;
+ LLVector3d cur_pos = node->getObject()->getPositionGlobal();
+ LLVector3d new_pos = cur_pos + ((cur_pos - node->mDuplicatePos) * rot_delta);
+
+ node->mDuplicatePos = node->getObject()->getPositionGlobal();
+ node->mDuplicateRot = node->getObject()->getRotation();
+ node->getObject()->setPositionGlobal(new_pos);
+ node->getObject()->setRotation(new_rot);
+ }
+ }
+
+ sendMultipleUpdate(UPD_ROTATION | UPD_POSITION);
+}
+
+// static
+void LLSelectMgr::packDuplicate( LLSelectNode* node, void *duplicate_data )
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+}
+
+
+//--------------------------------------------------------------------
+// Duplicate On Ray
+//--------------------------------------------------------------------
+
+// Duplicates the selected objects, but places the copy along a cast
+// ray.
+struct LLDuplicateOnRayData
+{
+ LLVector3 mRayStartRegion;
+ LLVector3 mRayEndRegion;
+ BOOL mBypassRaycast;
+ BOOL mRayEndIsIntersection;
+ LLUUID mRayTargetID;
+ BOOL mCopyCenters;
+ BOOL mCopyRotates;
+ U32 mFlags;
+};
+
+void LLSelectMgr::selectDuplicateOnRay(const LLVector3 &ray_start_region,
+ const LLVector3 &ray_end_region,
+ BOOL bypass_raycast,
+ BOOL ray_end_is_intersection,
+ const LLUUID &ray_target_id,
+ BOOL copy_centers,
+ BOOL copy_rotates,
+ BOOL select_copy)
+{
+ if (selectionIsAttachment())
+ {
+ // do not duplicate attachments
+ make_ui_sound("UISndInvalidOp");
+ return;
+ }
+
+ LLDuplicateOnRayData data;
+
+ data.mRayStartRegion = ray_start_region;
+ data.mRayEndRegion = ray_end_region;
+ data.mBypassRaycast = bypass_raycast;
+ data.mRayEndIsIntersection = ray_end_is_intersection;
+ data.mRayTargetID = ray_target_id;
+ data.mCopyCenters = copy_centers;
+ data.mCopyRotates = copy_rotates;
+ data.mFlags = (select_copy ? FLAGS_CREATE_SELECTED : 0x0);
+
+ sendListToRegions("ObjectDuplicateOnRay",
+ packDuplicateOnRayHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+
+ if (select_copy)
+ {
+ // the new copy will be coming in selected
+ deselectAll();
+ }
+}
+
+// static
+void LLSelectMgr::packDuplicateOnRayHead(void *user_data)
+{
+ LLMessageSystem *msg = gMessageSystem;
+ LLDuplicateOnRayData *data = (LLDuplicateOnRayData *)user_data;
+
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID() );
+ msg->addVector3Fast(_PREHASH_RayStart, data->mRayStartRegion );
+ msg->addVector3Fast(_PREHASH_RayEnd, data->mRayEndRegion );
+ msg->addBOOLFast(_PREHASH_BypassRaycast, data->mBypassRaycast );
+ msg->addBOOLFast(_PREHASH_RayEndIsIntersection, data->mRayEndIsIntersection );
+ msg->addBOOLFast(_PREHASH_CopyCenters, data->mCopyCenters );
+ msg->addBOOLFast(_PREHASH_CopyRotates, data->mCopyRotates );
+ msg->addUUIDFast(_PREHASH_RayTargetID, data->mRayTargetID );
+ msg->addU32Fast(_PREHASH_DuplicateFlags, data->mFlags );
+}
+
+
+
+//------------------------------------------------------------------------
+// Object position, scale, rotation update, all-in-one
+//------------------------------------------------------------------------
+
+void LLSelectMgr::sendMultipleUpdate(U32 type)
+{
+ if (type == UPD_NONE) return;
+ // send individual updates when selecting textures or individual objects
+ ESendType send_type = (gSavedSettings.getBOOL("SelectLinkedSet") && !getTEMode()) ? SEND_ONLY_ROOTS : SEND_ROOTS_FIRST;
+ if (send_type == SEND_ONLY_ROOTS)
+ {
+ // tell simulator to apply to whole linked sets
+ type |= UPD_LINKED_SETS;
+ }
+
+ sendListToRegions(
+ "MultipleObjectUpdate",
+ packAgentAndSessionID,
+ packMultipleUpdate,
+ &type,
+ send_type);
+}
+
+// static
+void LLSelectMgr::packMultipleUpdate(LLSelectNode* node, void *user_data)
+{
+ LLViewerObject* object = node->getObject();
+ U32 *type32 = (U32 *)user_data;
+ U8 type = (U8)*type32;
+ U8 data[256];
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID() );
+ gMessageSystem->addU8Fast(_PREHASH_Type, type );
+
+ S32 offset = 0;
+
+ // JC: You MUST pack the data in this order. The receiving
+ // routine process_multiple_update_message on simulator will
+ // extract them in this order.
+
+ if (type & UPD_POSITION)
+ {
+ htonmemcpy(&data[offset], &(object->getPosition().mV), MVT_LLVector3, 12);
+ offset += 12;
+ }
+ if (type & UPD_ROTATION)
+ {
+ LLQuaternion quat = object->getRotation();
+ LLVector3 vec = quat.packToVector3();
+ htonmemcpy(&data[offset], &(vec.mV), MVT_LLQuaternion, 12);
+ offset += 12;
+ }
+ if (type & UPD_SCALE)
+ {
+ //llinfos << "Sending object scale " << object->getScale() << llendl;
+ htonmemcpy(&data[offset], &(object->getScale().mV), MVT_LLVector3, 12);
+ offset += 12;
+ }
+ gMessageSystem->addBinaryDataFast(_PREHASH_Data, data, offset);
+}
+
+//------------------------------------------------------------------------
+// Ownership
+//------------------------------------------------------------------------
+struct LLOwnerData
+{
+ LLUUID owner_id;
+ LLUUID group_id;
+ BOOL override;
+};
+
+void LLSelectMgr::sendOwner(const LLUUID& owner_id,
+ const LLUUID& group_id,
+ BOOL override)
+{
+ LLOwnerData data;
+
+ data.owner_id = owner_id;
+ data.group_id = group_id;
+ data.override = override;
+
+ sendListToRegions("ObjectOwner", packOwnerHead, packObjectLocalID, &data, SEND_ONLY_ROOTS);
+}
+
+// static
+void LLSelectMgr::packOwnerHead(void *user_data)
+{
+ LLOwnerData *data = (LLOwnerData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ gMessageSystem->nextBlockFast(_PREHASH_HeaderData);
+ gMessageSystem->addBOOLFast(_PREHASH_Override, data->override);
+ gMessageSystem->addUUIDFast(_PREHASH_OwnerID, data->owner_id);
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id);
+}
+
+//------------------------------------------------------------------------
+// Group
+//------------------------------------------------------------------------
+
+void LLSelectMgr::sendGroup(const LLUUID& group_id)
+{
+ LLUUID local_group_id(group_id);
+ sendListToRegions("ObjectGroup", packAgentAndSessionAndGroupID, packObjectLocalID, &local_group_id, SEND_ONLY_ROOTS);
+}
+
+
+//------------------------------------------------------------------------
+// Buy
+//------------------------------------------------------------------------
+
+struct LLBuyData
+{
+ LLDynamicArray<LLViewerObject*> mObjectsSent;
+ LLUUID mCategoryID;
+ LLSaleInfo mSaleInfo;
+};
+
+// FIXME: doesn't work for multiple object buy, which UI does not currently support
+// sale info is used for verification only, if it doesn't match region info then sale is canceled
+// Need to get sale info -as displayed in the UI- for every item.
+void LLSelectMgr::sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info)
+{
+ LLBuyData buy;
+ buy.mCategoryID = category_id;
+ buy.mSaleInfo = sale_info;
+ sendListToRegions("ObjectBuy", packAgentGroupAndCatID, packBuyObjectIDs, &buy, SEND_ONLY_ROOTS);
+}
+
+// static
+void LLSelectMgr::packBuyObjectIDs(LLSelectNode* node, void* data)
+{
+ LLBuyData* buy = (LLBuyData*)data;
+
+ LLViewerObject* object = node->getObject();
+ if(buy->mObjectsSent.find(object) == LLDynamicArray<LLViewerObject*>::FAIL)
+ {
+ buy->mObjectsSent.put(object);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addU8Fast(_PREHASH_SaleType, buy->mSaleInfo.getSaleType());
+ gMessageSystem->addS32Fast(_PREHASH_SalePrice, buy->mSaleInfo.getSalePrice());
+ }
+}
+
+//------------------------------------------------------------------------
+// Permissions
+//------------------------------------------------------------------------
+
+struct LLPermData
+{
+ U8 mField;
+ BOOL mSet;
+ U32 mMask;
+ BOOL mOverride;
+};
+
+// TODO: Make this able to fail elegantly.
+void LLSelectMgr::setObjectPermissions(U8 field,
+ BOOL set,
+ U32 mask,
+ BOOL override)
+{
+ LLPermData data;
+
+ data.mField = field;
+ data.mSet = set;
+ data.mMask = mask;
+ data.mOverride = override;
+
+ sendListToRegions("ObjectPermissions", packPermissionsHead, packPermissions, &data, SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::packPermissionsHead(void* user_data)
+{
+ LLPermData* data = (LLPermData*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_HeaderData);
+ gMessageSystem->addBOOLFast(_PREHASH_Override, data->mOverride);
+}
+
+
+// Now that you've added a bunch of objects, send a select message
+// on the entire list for efficiency.
+/*
+void LLSelectMgr::sendSelect()
+{
+ llerrs << "Not implemented" << llendl;
+}
+*/
+
+void LLSelectMgr::deselectAll()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDeselect",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+
+ removeAll();
+
+ mLastSentSelectionCenterGlobal.clearVec();
+
+ updatePointAt();
+ gHUDManager->clearJoints();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::deselectTransient()
+{
+ std::set<LLViewerObject*> objects_to_deselect;
+ LLSelectNode *nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode())
+ {
+ if (nodep->isTransient())
+ {
+ objects_to_deselect.insert(nodep->getObject());
+ }
+ }
+
+ std::set<LLViewerObject*>::iterator iter;
+ for (iter = objects_to_deselect.begin();
+ iter != objects_to_deselect.end();
+ ++iter)
+ {
+ deselectObjectOnly(*iter);
+ }
+
+ gHUDManager->clearJoints();
+ updateSelectionCenter();
+}
+
+void LLSelectMgr::convertTransient()
+{
+ LLSelectNode *nodep;
+ for (nodep = mSelectedObjects.getFirstNode(); nodep; nodep = mSelectedObjects.getNextNode())
+ {
+ nodep->setTransient(FALSE);
+ }
+}
+
+void LLSelectMgr::deselectAllIfTooFar()
+{
+ if (isEmpty() || mSelectType == SELECT_TYPE_HUD)
+ {
+ return;
+ }
+
+ // HACK: Don't deselect when we're navigating to rate an object's
+ // owner or creator. JC
+ if (gPieObject->getVisible() || gPieRate->getVisible() )
+ {
+ return;
+ }
+
+ LLVector3d selectionCenter = getSelectionCenterGlobal();
+ if (gSavedSettings.getBOOL("LimitSelectDistance")
+ && !selectionCenter.isExactlyZero())
+ {
+ F32 deselect_dist = gSavedSettings.getF32("MaxSelectDistance");
+ F32 deselect_dist_sq = deselect_dist * deselect_dist;
+
+ LLVector3d select_delta = gAgent.getPositionGlobal() - selectionCenter;
+ F32 select_dist_sq = (F32) select_delta.magVecSquared();
+
+ if (select_dist_sq > deselect_dist_sq)
+ {
+ if (gDebugSelectMgr)
+ {
+ llinfos << "Selection manager: auto-deselecting, select_dist = " << fsqrtf(select_dist_sq) << llendl;
+ llinfos << "agent pos global = " << gAgent.getPositionGlobal() << llendl;
+ llinfos << "selection pos global = " << selectionCenter << llendl;
+ }
+
+ deselectAll();
+ }
+ }
+}
+
+
+void LLSelectMgr::setObjectName(const LLString& name)
+{
+ // we only work correctly if 1 object is selected.
+ if(getRootObjectCount() == 1)
+ {
+ sendListToRegions("ObjectName",
+ packAgentAndSessionID,
+ packObjectName,
+ (void*)name.c_str(),
+ SEND_ONLY_ROOTS);
+ }
+ else if(getObjectCount() == 1)
+ {
+ sendListToRegions("ObjectName",
+ packAgentAndSessionID,
+ packObjectName,
+ (void*)name.c_str(),
+ SEND_INDIVIDUALS);
+ }
+}
+
+void LLSelectMgr::setObjectDescription(const LLString& desc)
+{
+ // we only work correctly if 1 object is selected.
+ if(getRootObjectCount() == 1)
+ {
+ sendListToRegions("ObjectDescription",
+ packAgentAndSessionID,
+ packObjectDescription,
+ (void*)desc.c_str(),
+ SEND_ONLY_ROOTS);
+ }
+ else if(getObjectCount() == 1)
+ {
+ sendListToRegions("ObjectDescription",
+ packAgentAndSessionID,
+ packObjectDescription,
+ (void*)desc.c_str(),
+ SEND_INDIVIDUALS);
+ }
+}
+
+void LLSelectMgr::setObjectCategory(const LLCategory& category)
+{
+ // for now, we only want to be able to set one root category at
+ // a time.
+ if(getRootObjectCount() != 1) return;
+ sendListToRegions("ObjectCategory",
+ packAgentAndSessionID,
+ packObjectCategory,
+ (void*)(&category),
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::setObjectSaleInfo(const LLSaleInfo& sale_info)
+{
+ // Only one sale info at a time for now
+ if(getRootObjectCount() != 1) return;
+ sendListToRegions("ObjectSaleInfo",
+ packAgentAndSessionID,
+ packObjectSaleInfo,
+ (void*)(&sale_info),
+ SEND_ONLY_ROOTS);
+}
+
+//----------------------------------------------------------------------
+// Attachments
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendAttach(U8 attachment_point)
+{
+ LLViewerObject* attach_object = mSelectedObjects.getFirstRootObject();
+
+ if (!attach_object || !gAgent.getAvatarObject() || mSelectType != SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ // Special case: Attach to default location for this object.
+ if (0 == attachment_point)
+ {
+ sendListToRegions(
+ "ObjectAttach",
+ packAgentIDAndSessionAndAttachment,
+ packObjectIDAndRotation,
+ &attachment_point,
+ SEND_ONLY_ROOTS );
+ }
+ else
+ {
+ LLViewerJointAttachment* attachment = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachment_point);
+ if (attachment)
+ {
+ LLQuaternion object_world_rot = attach_object->getRenderRotation();
+ LLQuaternion attachment_pt__world_rot = attachment->getWorldRotation();
+ LLQuaternion local_rot = object_world_rot * ~attachment_pt__world_rot;
+
+ F32 x,y,z;
+ local_rot.getEulerAngles(&x, &y, &z);
+ // snap to nearest 90 degree rotation
+ // make sure all euler angles are positive
+ if (x < F_PI_BY_TWO) x += F_TWO_PI;
+ if (y < F_PI_BY_TWO) y += F_TWO_PI;
+ if (z < F_PI_BY_TWO) z += F_TWO_PI;
+
+ // add 45 degrees so that rounding down becomes rounding off
+ x += F_PI_BY_TWO / 2.f;
+ y += F_PI_BY_TWO / 2.f;
+ z += F_PI_BY_TWO / 2.f;
+ // round down to nearest multiple of 90 degrees
+ x -= fmodf(x, F_PI_BY_TWO);
+ y -= fmodf(y, F_PI_BY_TWO);
+ z -= fmodf(z, F_PI_BY_TWO);
+
+ // pass the requested rotation on to the simulator
+ local_rot.setQuat(x, y, z);
+ attach_object->setRotation(local_rot);
+
+ sendListToRegions(
+ "ObjectAttach",
+ packAgentIDAndSessionAndAttachment,
+ packObjectIDAndRotation,
+ &attachment_point,
+ SEND_ONLY_ROOTS );
+ }
+ }
+}
+
+void LLSelectMgr::sendDetach()
+{
+ if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDetach",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS );
+}
+
+
+void LLSelectMgr::sendDropAttachment()
+{
+ if (!mSelectedObjects.getNumNodes() || mSelectType == SELECT_TYPE_WORLD)
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDrop",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+//----------------------------------------------------------------------
+// Links
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendLink()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectLink",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::sendDelink()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ // Delink needs to send individuals so you can unlink a single object from
+ // a linked set.
+ sendListToRegions(
+ "ObjectDelink",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+}
+
+
+//----------------------------------------------------------------------
+// Hinges
+//----------------------------------------------------------------------
+
+void LLSelectMgr::sendHinge(U8 type)
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectHinge",
+ packHingeHead,
+ packObjectLocalID,
+ &type,
+ SEND_ONLY_ROOTS);
+}
+
+
+void LLSelectMgr::sendDehinge()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectDehinge",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_ONLY_ROOTS);
+}
+
+void LLSelectMgr::sendSelect()
+{
+ if (!mSelectedObjects.getNumNodes())
+ {
+ return;
+ }
+
+ sendListToRegions(
+ "ObjectSelect",
+ packAgentAndSessionID,
+ packObjectLocalID,
+ NULL,
+ SEND_INDIVIDUALS);
+}
+
+// static
+void LLSelectMgr::packHingeHead(void *user_data)
+{
+ U8 *type = (U8 *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ gMessageSystem->nextBlockFast(_PREHASH_JointType);
+ gMessageSystem->addU8Fast(_PREHASH_Type, *type );
+}
+
+
+void LLSelectMgr::selectionDump()
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ object->dump();
+ }
+}
+
+void LLSelectMgr::saveSelectedObjectColors()
+{
+ LLSelectNode* selectNode;
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ selectNode->saveColors();
+ }
+}
+
+void LLSelectMgr::saveSelectedObjectTextures()
+{
+ LLSelectNode* selectNode;
+
+ // invalidate current selection so we update saved textures
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ selectNode->mValid = FALSE;
+ }
+
+ // request object properties message to get updated permissions data
+ sendSelect();
+}
+
+
+// This routine should be called whenever a drag is initiated.
+// also need to know to which simulator to send update message
+void LLSelectMgr::saveSelectedObjectTransform(EActionType action_type)
+{
+ LLSelectNode* selectNode;
+
+ if (isEmpty())
+ {
+ // nothing selected, so nothing to save
+ return;
+ }
+
+ for (selectNode = getFirstNode(); selectNode; selectNode = getNextNode() )
+ {
+ LLViewerObject* object;
+ object = selectNode->getObject();
+ selectNode->mSavedPositionLocal = object->getPosition();
+ if (object->isAttachment())
+ {
+ if (object->isRootEdit())
+ {
+ LLXform* parent_xform = object->mDrawable->getXform()->getParent();
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition());
+ }
+ else
+ {
+ LLViewerObject* attachment_root = (LLViewerObject*)object->getParent();
+ LLXform* parent_xform = attachment_root->mDrawable->getXform()->getParent();
+ LLVector3 root_pos = (attachment_root->getPosition() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ LLQuaternion root_rot = (attachment_root->getRotation() * parent_xform->getWorldRotation());
+ selectNode->mSavedPositionGlobal = gAgent.getPosGlobalFromAgent((object->getPosition() * root_rot) + root_pos);
+ }
+ selectNode->mSavedRotation = object->getRenderRotation();
+ }
+ else
+ {
+ selectNode->mSavedPositionGlobal = object->getPositionGlobal();
+ selectNode->mSavedRotation = object->getRotationRegion();
+ }
+
+ selectNode->mSavedScale = object->getScale();
+ selectNode->saveTextureScaleRatios();
+
+ if (object->isAttachment() &&
+ action_type != SELECT_ACTION_TYPE_PICK)
+ {
+ LLSelectAction* selectAction = new LLSelectAction();
+ selectAction->mActionType = action_type;
+ selectAction->mPosition = object->getPosition();
+ selectAction->mRotation = object->getRotation();
+ selectAction->mScale = object->getScale();
+ selectAction->mObjectID = object->getID();
+ selectAction->mIndividualSelection = selectNode->mIndividualSelection;
+
+ mUndoQueue.push_back(selectAction);
+
+ while ((mUndoQueue.size() > (U32)MAX_ACTION_QUEUE_SIZE))
+ {
+ LLSelectAction* action = mUndoQueue.front();
+ mUndoQueue.pop_front();
+ delete action;
+ }
+
+ // remove this object from the redo queue
+ std::deque<LLSelectAction*>::iterator it;
+ for (it = mRedoQueue.begin(); it != mRedoQueue.end();)
+ {
+ if ((*it)->mObjectID == object->getID())
+ {
+ LLSelectAction* actionp = *it;
+ it = mRedoQueue.erase(it);
+ delete actionp;
+ }
+ else
+ {
+ ++it;
+ }
+ }
+ }
+ }
+ mSavedSelectionBBox = getBBoxOfSelection();
+}
+
+void LLSelectMgr::selectionUpdatePhysics(BOOL physics)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_USE_PHYSICS, physics);
+ }
+}
+
+void LLSelectMgr::selectionUpdateTemporary(BOOL is_temporary)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_TEMPORARY_ON_REZ, is_temporary);
+ }
+}
+
+void LLSelectMgr::selectionUpdatePhantom(BOOL is_phantom)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || !(object->isRoot() // don't send for child objects
+ || object->isJointChild()))
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_PHANTOM, is_phantom);
+ }
+}
+
+void LLSelectMgr::selectionUpdateCastShadows(BOOL cast_shadows)
+{
+ LLViewerObject *object;
+
+ for (object = getFirstObject(); object; object = getNextObject() )
+ {
+ if ( !object->permModify() // preemptive permissions check
+ || object->isJointChild())
+ {
+ continue;
+ }
+ object->setFlags( FLAGS_CAST_SHADOWS, cast_shadows);
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Helpful packing functions for sendObjectMessage()
+//----------------------------------------------------------------------
+
+// static
+void LLSelectMgr::packAgentIDAndSessionAndAttachment( void *user_data)
+{
+ U8 *attachment_point = (U8*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addU8Fast(_PREHASH_AttachmentPoint, *attachment_point);
+}
+
+// static
+void LLSelectMgr::packAgentID( void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+}
+
+// static
+void LLSelectMgr::packAgentAndSessionID(void* user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+}
+
+// static
+void LLSelectMgr::packAgentAndGroupID(void* user_data)
+{
+ LLOwnerData *data = (LLOwnerData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, data->owner_id );
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, data->group_id );
+}
+
+// static
+void LLSelectMgr::packAgentAndSessionAndGroupID(void* user_data)
+{
+ LLUUID* group_idp = (LLUUID*) user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, *group_idp);
+}
+
+// static
+void LLSelectMgr::packDuplicateHeader(void* data)
+{
+ LLUUID group_id(gAgent.getGroupID());
+ packAgentAndSessionAndGroupID(&group_id);
+
+ LLDuplicateData* dup_data = (LLDuplicateData*) data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_SharedData);
+ gMessageSystem->addVector3Fast(_PREHASH_Offset, dup_data->offset);
+ gMessageSystem->addU32Fast(_PREHASH_DuplicateFlags, dup_data->flags);
+}
+
+// static
+void LLSelectMgr::packDeleteHeader(void* userdata)
+{
+ BOOL force = (BOOL)(intptr_t)userdata;
+
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addBOOLFast(_PREHASH_Force, force);
+}
+
+// static
+void LLSelectMgr::packAgentGroupAndCatID(void* user_data)
+{
+ LLBuyData* buy = (LLBuyData*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ gMessageSystem->addUUIDFast(_PREHASH_CategoryID, buy->mCategoryID);
+}
+
+//static
+void LLSelectMgr::packDeRezHeader(void* user_data)
+{
+ LLDeRezInfo* info = (LLDeRezInfo*)user_data;
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ gMessageSystem->addU8Fast(_PREHASH_Destination, (U8)info->mDestination);
+ gMessageSystem->addUUIDFast(_PREHASH_DestinationID, info->mDestinationID);
+ LLUUID tid;
+ tid.generate();
+ gMessageSystem->addUUIDFast(_PREHASH_TransactionID, tid);
+ const U8 PACKET = 1;
+ gMessageSystem->addU8Fast(_PREHASH_PacketCount, PACKET);
+ gMessageSystem->addU8Fast(_PREHASH_PacketNumber, PACKET);
+}
+
+// static
+void LLSelectMgr::packObjectID(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addUUIDFast(_PREHASH_ObjectID, node->getObject()->mID );
+}
+
+void LLSelectMgr::packObjectIDAndRotation(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, node->getObject()->getRotation());
+}
+
+void LLSelectMgr::packObjectClickAction(LLSelectNode* node, void *user_data)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID() );
+ gMessageSystem->addU8("ClickAction", node->getObject()->getClickAction());
+}
+
+// static
+void LLSelectMgr::packObjectLocalID(LLSelectNode* node, void *)
+{
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+}
+
+// static
+void LLSelectMgr::packObjectName(LLSelectNode* node, void* user_data)
+{
+ char* name = (char*)user_data;
+ if(!name) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ gMessageSystem->addStringFast(_PREHASH_Name, name);
+}
+
+// static
+void LLSelectMgr::packObjectDescription(LLSelectNode* node,
+ void* user_data)
+{
+ char* desc = (char*)user_data;
+ if(!desc) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ gMessageSystem->addStringFast(_PREHASH_Description, desc);
+}
+
+// static
+void LLSelectMgr::packObjectCategory(LLSelectNode* node, void* user_data)
+{
+ LLCategory* category = (LLCategory*)user_data;
+ if(!category) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ category->packMessage(gMessageSystem);
+}
+
+// static
+void LLSelectMgr::packObjectSaleInfo(LLSelectNode* node, void* user_data)
+{
+ LLSaleInfo* sale_info = (LLSaleInfo*)user_data;
+ if(!sale_info) return;
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_LocalID, node->getObject()->getLocalID());
+ sale_info->packMessage(gMessageSystem);
+}
+
+// static
+void LLSelectMgr::packPhysics(LLSelectNode* node, void *user_data)
+{
+}
+
+// static
+void LLSelectMgr::packShape(LLSelectNode* node, void *user_data)
+{
+}
+
+// static
+void LLSelectMgr::packPermissions(LLSelectNode* node, void *user_data)
+{
+ LLPermData *data = (LLPermData *)user_data;
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, node->getObject()->getLocalID());
+
+ gMessageSystem->addU8Fast(_PREHASH_Field, data->mField);
+ gMessageSystem->addBOOLFast(_PREHASH_Set, data->mSet);
+ gMessageSystem->addU32Fast(_PREHASH_Mask, data->mMask);
+}
+
+// Utility function to send some information to every region containing
+// an object on the selection list. We want to do this to reduce the total
+// number of packets sent by the viewer.
+void LLSelectMgr::sendListToRegions(const LLString& message_name,
+ void (*pack_header)(void *user_data),
+ void (*pack_body)(LLSelectNode* node, void *user_data),
+ void *user_data,
+ ESendType send_type)
+{
+ LLSelectNode* node;
+ LLViewerRegion* last_region;
+ LLViewerRegion* current_region;
+
+ S32 objects_sent = 0;
+ S32 packets_sent = 0;
+ S32 objects_in_this_packet = 0;
+
+ std::queue<LLSelectNode*> nodes_to_send;
+
+ switch(send_type)
+ {
+ case SEND_ONLY_ROOTS:
+ node = mSelectedObjects.getFirstRootNode();
+ while(node)
+ {
+ nodes_to_send.push(node);
+ node = mSelectedObjects.getNextRootNode();
+ }
+ break;
+ case SEND_INDIVIDUALS:
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ nodes_to_send.push(node);
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+ case SEND_ROOTS_FIRST:
+ // first roots...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+
+ // then children...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (!node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+ case SEND_CHILDREN_FIRST:
+ // first children...
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (!node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+
+ // ...then roots
+ node = mSelectedObjects.getFirstNode();
+ while(node)
+ {
+ if (node->getObject()->isRootEdit())
+ {
+ nodes_to_send.push(node);
+ }
+ node = mSelectedObjects.getNextNode();
+ }
+ break;
+
+ default:
+ llerrs << "Bad send type " << send_type << " passed to SendListToRegions()" << llendl;
+ }
+
+ // bail if nothing selected
+ if (nodes_to_send.empty()) return;
+
+ node = nodes_to_send.front();
+ nodes_to_send.pop();
+
+ // cache last region information
+ current_region = node->getObject()->getRegion();
+
+ // Start duplicate message
+ // CRO: this isn't
+ gMessageSystem->newMessage(message_name.c_str());
+ (*pack_header)(user_data);
+
+ // For each object
+ while (node != NULL)
+ {
+ // remember the last region, look up the current one
+ last_region = current_region;
+ current_region = node->getObject()->getRegion();
+
+ // if to same simulator and message not too big
+ if ((current_region == last_region)
+ && (gMessageSystem->mCurrentSendTotal < MTUBYTES)
+ && (objects_in_this_packet < MAX_OBJECTS_PER_PACKET))
+ {
+ // add another instance of the body of the data
+ (*pack_body)(node, user_data);
+ ++objects_sent;
+ ++objects_in_this_packet;
+
+ // and on to the next object
+ if(nodes_to_send.empty())
+ {
+ node = NULL;
+ }
+ else
+ {
+ node = nodes_to_send.front();
+ nodes_to_send.pop();
+ }
+ }
+ else
+ {
+ // otherwise send current message and start new one
+ gMessageSystem->sendReliable( last_region->getHost());
+ packets_sent++;
+ objects_in_this_packet = 0;
+
+ gMessageSystem->newMessage(message_name.c_str());
+ (*pack_header)(user_data);
+
+ // don't move to the next object, we still need to add the
+ // body data.
+ }
+ }
+
+ // flush messages
+ if (gMessageSystem->mCurrentSendTotal > 0)
+ {
+ gMessageSystem->sendReliable( current_region->getHost());
+ packets_sent++;
+ }
+ else
+ {
+ gMessageSystem->clearMessage();
+ }
+
+ // llinfos << "sendListToRegions " << message_name << " obj " << objects_sent << " pkt " << packets_sent << llendl;
+}
+
+
+//
+// Network communications
+//
+
+void LLSelectMgr::requestObjectPropertiesFamily(LLViewerObject* object)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_RequestObjectPropertiesFamily);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_RequestFlags, 0x0 );
+ msg->addUUIDFast(_PREHASH_ObjectID, object->mID );
+
+ LLViewerRegion* regionp = object->getRegion();
+ msg->sendReliable( regionp->getHost() );
+}
+
+
+// static
+void LLSelectMgr::processObjectProperties(LLMessageSystem* msg, void** user_data)
+{
+ S32 i;
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_ObjectData);
+ for (i = 0; i < count; i++)
+ {
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id, i);
+
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLUUID group_id;
+ LLUUID last_owner_id;
+ LLUUID extra_id;
+ U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask;
+ LLSaleInfo sale_info;
+ LLCategory category;
+ LLAggregatePermissions ag_perms;
+ LLAggregatePermissions ag_texture_perms;
+ LLAggregatePermissions ag_texture_perms_owner;
+
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_CreatorID, creator_id, i);
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, i);
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_GroupMask, group_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask, i);
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask, i);
+ sale_info.unpackMultiMessage(msg, _PREHASH_ObjectData, i);
+
+ ag_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePerms, i);
+ ag_texture_perms.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTextures, i);
+ ag_texture_perms_owner.unpackMessage(msg, _PREHASH_ObjectData, _PREHASH_AggregatePermTexturesOwner, i);
+ category.unpackMultiMessage(msg, _PREHASH_ObjectData, i);
+
+ S16 inv_serial = 0;
+ msg->getS16Fast(_PREHASH_ObjectData, _PREHASH_InventorySerial, inv_serial, i);
+
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ItemID, item_id, i);
+ LLUUID folder_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FolderID, folder_id, i);
+ LLUUID from_task_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FromTaskID, from_task_id, i);
+
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id, i);
+
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name, i);
+ char desc[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc, i);
+
+ char touch_name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_TouchName, DB_INV_ITEM_NAME_BUF_SIZE, touch_name, i);
+ char sit_name[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_SitName, DB_INV_ITEM_DESC_BUF_SIZE, sit_name, i);
+
+ //unpack TE IDs
+ std::vector<LLUUID> texture_ids;
+ S32 size = msg->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_TextureID);
+ if (size > 0)
+ {
+ S8 packed_buffer[SELECT_MAX_TES * UUID_BYTES];
+ msg->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureID, packed_buffer, 0, i, SELECT_MAX_TES * UUID_BYTES);
+
+ for (S32 buf_offset = 0; buf_offset < size; buf_offset += UUID_BYTES)
+ {
+ LLUUID id;
+ memcpy(id.mData, packed_buffer + buf_offset, UUID_BYTES);
+ texture_ids.push_back(id);
+ }
+ }
+
+
+ // Iterate through nodes at end, since it can be on both the regular AND hover list
+ BOOL found = FALSE;
+ LLSelectNode* node;
+ for (node = gSelectMgr->mSelectedObjects.getFirstNode();
+ node;
+ node = gSelectMgr->mSelectedObjects.getNextNode())
+ {
+ if (node->getObject()->mID == id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+
+ if (node)
+ {
+ if (node->mInventorySerial != inv_serial)
+ {
+ node->getObject()->dirtyInventory();
+ }
+
+ // save texture data as soon as we get texture perms first time
+ if (!node->mValid)
+ {
+ BOOL can_copy = FALSE;
+ BOOL can_transfer = FALSE;
+
+ LLAggregatePermissions::EValue value = LLAggregatePermissions::AP_NONE;
+ if(node->getObject()->permYouOwner())
+ {
+ value = ag_texture_perms_owner.getValue(PERM_COPY);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_copy = TRUE;
+ }
+ value = ag_texture_perms_owner.getValue(PERM_TRANSFER);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_transfer = TRUE;
+ }
+ }
+ else
+ {
+ value = ag_texture_perms.getValue(PERM_COPY);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_copy = TRUE;
+ }
+ value = ag_texture_perms.getValue(PERM_TRANSFER);
+ if (value == LLAggregatePermissions::AP_EMPTY || value == LLAggregatePermissions::AP_ALL)
+ {
+ can_transfer = TRUE;
+ }
+ }
+
+ if (can_copy && can_transfer)
+ {
+ // this should be the only place that saved textures is called
+ node->saveTextures(texture_ids);
+ }
+ }
+
+ node->mValid = TRUE;
+ node->mPermissions->init(creator_id, owner_id,
+ last_owner_id, group_id);
+ node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask);
+ node->mItemID = item_id;
+ node->mFolderID = folder_id;
+ node->mFromTaskID = from_task_id;
+ node->mName.assign(name);
+ node->mDescription.assign(desc);
+ node->mSaleInfo = sale_info;
+ node->mAggregatePerm = ag_perms;
+ node->mAggregateTexturePerm = ag_texture_perms;
+ node->mAggregateTexturePermOwner = ag_texture_perms_owner;
+ node->mCategory = category;
+ node->mInventorySerial = inv_serial;
+ node->mSitName.assign(sit_name);
+ node->mTouchName.assign(touch_name);
+ }
+ }
+
+ dialog_refresh_all();
+
+ // silly hack to allow 'save into inventory'
+ if(gPopupMenuView->getVisible())
+ {
+ gPopupMenuView->setItemEnabled(SAVE_INTO_INVENTORY,
+ enable_save_into_inventory(NULL));
+ }
+
+ // hack for left-click buy object
+ LLToolPie::selectionPropertiesReceived();
+}
+
+// static
+void LLSelectMgr::processObjectPropertiesFamily(LLMessageSystem* msg, void** user_data)
+{
+ LLUUID id;
+
+ U32 request_flags;
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLUUID group_id;
+ LLUUID extra_id;
+ U32 base_mask, owner_mask, group_mask, everyone_mask, next_owner_mask;
+ LLSaleInfo sale_info;
+ LLCategory category;
+
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_RequestFlags, request_flags );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, id );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id );
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_GroupID, group_id );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_BaseMask, base_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_OwnerMask, owner_mask );
+ msg->getU32Fast(_PREHASH_ObjectData,_PREHASH_GroupMask, group_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_EveryoneMask, everyone_mask );
+ msg->getU32Fast(_PREHASH_ObjectData, _PREHASH_NextOwnerMask, next_owner_mask);
+ sale_info.unpackMessage(msg, _PREHASH_ObjectData);
+ category.unpackMessage(msg, _PREHASH_ObjectData);
+
+ LLUUID last_owner_id;
+ msg->getUUIDFast(_PREHASH_ObjectData, _PREHASH_LastOwnerID, last_owner_id );
+
+ // unpack name & desc
+ char name[DB_INV_ITEM_NAME_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Name, DB_INV_ITEM_NAME_BUF_SIZE, name);
+
+ char desc[DB_INV_ITEM_DESC_BUF_SIZE];
+ msg->getStringFast(_PREHASH_ObjectData, _PREHASH_Description, DB_INV_ITEM_DESC_BUF_SIZE, desc);
+
+ // the reporter widget askes the server for info about picked objects
+ if (request_flags & (COMPLAINT_REPORT_REQUEST | BUG_REPORT_REQUEST))
+ {
+ EReportType report_type = (COMPLAINT_REPORT_REQUEST & request_flags) ? COMPLAINT_REPORT : BUG_REPORT;
+ LLFloaterReporter *reporterp = LLFloaterReporter::getReporter(report_type);
+ if (reporterp)
+ {
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_LAST_NAME_BUF_SIZE];
+ gCacheName->getName(owner_id, first_name, last_name);
+ LLString fullname(first_name);
+ fullname.append(" ");
+ fullname.append(last_name);
+ reporterp->setPickedObjectProperties(name, fullname.c_str());
+ }
+ }
+
+ // Now look through all of the hovered nodes
+ BOOL found = FALSE;
+ LLSelectNode* node;
+ for (node = gSelectMgr->mHoverObjects.getFirstNode();
+ node;
+ node = gSelectMgr->mHoverObjects.getNextNode())
+ {
+ if (node->getObject()->mID == id)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (node)
+ {
+ node->mValid = TRUE;
+ node->mPermissions->init(LLUUID::null, owner_id,
+ last_owner_id, group_id);
+ node->mPermissions->initMasks(base_mask, owner_mask, everyone_mask, group_mask, next_owner_mask);
+ node->mSaleInfo = sale_info;
+ node->mCategory = category;
+ node->mName.assign(name);
+ node->mDescription.assign(desc);
+ }
+
+ dialog_refresh_all();
+}
+
+
+// static
+void LLSelectMgr::processForceObjectSelect(LLMessageSystem* msg, void**)
+{
+ BOOL reset_list;
+ msg->getBOOL("Header", "ResetList", reset_list);
+
+ if (reset_list)
+ {
+ gSelectMgr->deselectAll();
+ }
+
+ LLUUID full_id;
+ S32 local_id;
+ LLViewerObject* object;
+ LLDynamicArray<LLViewerObject*> objects;
+ S32 i;
+ S32 block_count = msg->getNumberOfBlocks("Data");
+
+ for (i = 0; i < block_count; i++)
+ {
+ msg->getS32("Data", "LocalID", local_id, i);
+
+ gObjectList.getUUIDFromLocal(full_id,
+ local_id,
+ msg->getSenderIP(),
+ msg->getSenderPort());
+ object = gObjectList.findObject(full_id);
+ if (object)
+ {
+ objects.put(object);
+ }
+ }
+
+ // Don't select, just highlight
+ gSelectMgr->highlightObjectAndFamily(objects);
+}
+
+
+extern LLGLdouble gGLModelView[16];
+
+void LLSelectMgr::updateSilhouettes()
+{
+ LLSelectNode *node;
+ S32 num_sils_genned = 0;
+
+ LLVector3d cameraPos = gAgent.getCameraPositionGlobal();
+ F32 currentCameraZoom = gAgent.getCurrentCameraBuildOffset();
+
+ if (!mSilhouetteImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("silhouette.tga") );
+ mSilhouetteImagep = gImageList.getImage(id, TRUE, TRUE);
+ }
+
+
+ if((cameraPos - mLastCameraPos).magVecSquared() > SILHOUETTE_UPDATE_THRESHOLD_SQUARED * currentCameraZoom * currentCameraZoom)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ if (node->getObject())
+ {
+ node->getObject()->setChanged(LLXform::SILHOUETTE);
+ }
+ }
+
+ mLastCameraPos = gAgent.getCameraPositionGlobal();
+ }
+
+ LLDynamicArray<LLViewerObject*> changed_objects;
+
+ if (mSelectedObjects.getNumNodes())
+ {
+ //gGLSPipelineSelection.set();
+
+ //mSilhouetteImagep->bindTexture();
+ //glAlphaFunc(GL_GREATER, sHighlightAlphaTest);
+
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+
+ // do roots first, then children so that root flags are cleared ASAP
+ BOOL roots_only = (pass == 0);
+ BOOL is_root = (objectp->isRootEdit());
+ if (roots_only != is_root || objectp->mDrawable.isNull())
+ {
+ continue;
+ }
+
+ if (!node->mSilhouetteExists
+ || objectp->isChanged(LLXform::SILHOUETTE)
+ || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ if (num_sils_genned++ < MAX_SILS_PER_FRAME && objectp->mDrawable->isVisible())
+ {
+ generateSilhouette(node, gCamera->getOrigin());
+ changed_objects.put(objectp);
+ }
+ else if (objectp->isAttachment())
+ {
+ //RN: hack for orthogonal projection of HUD attachments
+ LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent();
+ if (attachment_pt && attachment_pt->getIsHUDAttachment())
+ {
+ LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f);
+ generateSilhouette(node, camera_pos);
+ }
+ }
+ }
+
+ //if (!gFloaterTools || !gFloaterTools->getVisible())
+ //{
+ // node->renderOneSilhouette(sContextSilhouetteColor);
+ //}
+ //else if (objectp->isRootEdit())
+ //{
+ // node->renderOneSilhouette(sSilhouetteParentColor);
+ //}
+ //else
+ //{
+ // node->renderOneSilhouette(sSilhouetteChildColor);
+ //}
+ }
+ }
+ //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ if (mRectSelectedObjects.size() > 0)
+ {
+ //gGLSPipelineSelection.set();
+
+ //mSilhouetteImagep->bindTexture();
+ //glAlphaFunc(GL_GREATER, sHighlightAlphaTest);
+
+ std::set<LLViewerObject*> roots;
+
+ // sync mHighlightedObjects with mRectSelectedObjects since the latter is rebuilt every frame and former
+ // persists from frame to frame to avoid regenerating object silhouettes
+ // mHighlightedObjects includes all siblings of rect selected objects
+
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+
+ // generate list of roots from current object selection
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mRectSelectedObjects.begin();
+ iter != mRectSelectedObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (select_linked_set)
+ {
+ LLViewerObject *rootp = (LLViewerObject*)objectp->getRoot();
+ roots.insert(rootp);
+ }
+ else
+ {
+ roots.insert(objectp);
+ }
+ }
+
+ // remove highlight nodes not in roots list
+ LLDynamicArray<LLSelectNode*> remove_these_nodes;
+ LLDynamicArray<LLViewerObject*> remove_these_roots;
+ for (LLSelectNode* nodep = mHighlightedObjects.getFirstNode(); nodep; nodep = mHighlightedObjects.getNextNode())
+ {
+ LLViewerObject* objectp = nodep->getObject();
+ if (objectp->isRoot() || !select_linked_set)
+ {
+ if (roots.count(objectp) == 0)
+ {
+ remove_these_nodes.put(nodep);
+ }
+ else
+ {
+ remove_these_roots.put(objectp);
+ }
+ }
+ else
+ {
+ LLViewerObject* rootp = (LLViewerObject*)objectp->getRoot();
+
+ if (roots.count(rootp) == 0)
+ {
+ remove_these_nodes.put(nodep);
+ }
+ }
+ }
+
+ // remove all highlight nodes no longer in rectangle selection
+ S32 i;
+ for (i = 0; i < remove_these_nodes.count(); i++)
+ {
+ mHighlightedObjects.removeNode(remove_these_nodes[i]);
+ }
+
+ // remove all root objects already being highlighted
+ for (i = 0; i < remove_these_roots.count(); i++)
+ {
+ roots.erase(remove_these_roots[i]);
+ }
+
+ // add all new objects in rectangle selection
+ for (std::set<LLViewerObject*>::iterator iter = roots.begin();
+ iter != roots.end(); iter++)
+ {
+ LLViewerObject* objectp = *iter;
+ LLSelectNode* rect_select_node = new LLSelectNode(objectp, TRUE);
+ rect_select_node->selectAllTEs(TRUE);
+
+ if (!canSelectObject(objectp))
+ {
+ continue;
+ }
+
+ mHighlightedObjects.addNode(rect_select_node);
+
+ if (!select_linked_set)
+ {
+ rect_select_node->mIndividualSelection = TRUE;
+ }
+ else
+ {
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = objectp->mChildList[i];
+
+ if (!canSelectObject(child_objectp))
+ {
+ continue;
+ }
+
+ rect_select_node = new LLSelectNode(objectp->mChildList[i], TRUE);
+ rect_select_node->selectAllTEs(TRUE);
+ mHighlightedObjects.addNode(rect_select_node);
+ }
+ }
+ }
+
+ num_sils_genned = 0;
+
+ // render silhouettes for highlighted objects
+ //BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+
+ // do roots first, then children so that root flags are cleared ASAP
+ BOOL roots_only = (pass == 0);
+ BOOL is_root = objectp->isRootEdit();
+ if (roots_only != is_root)
+ {
+ continue;
+ }
+
+ if (!node->mSilhouetteExists
+ || objectp->isChanged(LLXform::SILHOUETTE)
+ || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE)))
+ {
+ if (num_sils_genned++ < MAX_SILS_PER_FRAME)
+ {
+ generateSilhouette(node, gCamera->getOrigin());
+ changed_objects.put(objectp);
+ }
+ else if (objectp->isAttachment() && objectp->getRootEdit()->mDrawable.notNull())
+ {
+ //RN: hack for orthogonal projection of HUD attachments
+ LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent();
+ if (attachment_pt && attachment_pt->getIsHUDAttachment())
+ {
+ LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f);
+ generateSilhouette(node, camera_pos);
+ }
+ }
+ }
+ //LLColor4 highlight_color;
+ //
+ //if (subtracting_from_selection)
+ //{
+ // node->renderOneSilhouette(LLColor4::red);
+ //}
+ //else if (!objectp->isSelected())
+ //{
+ // highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor;
+ // node->renderOneSilhouette(highlight_color);
+ //}
+ }
+ }
+ //mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ mHighlightedObjects.deleteAllNodes();
+ }
+
+ for (S32 i = 0; i < changed_objects.count(); i++)
+ {
+ // clear flags after traversing node list (as child objects need to refer to parent flags, etc)
+ changed_objects[i]->clearChanged(LLXform::MOVED | LLXform::SILHOUETTE);
+ }
+
+ //glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+void LLSelectMgr::renderSilhouettes(BOOL for_hud)
+{
+ if (!mRenderSilhouettes)
+ {
+ return;
+ }
+
+ LLSelectNode *node;
+ LLViewerImage::bindTexture(gSelectMgr->mSilhouetteImagep);
+ LLGLSPipelineSelection gls_select;
+ glAlphaFunc(GL_GREATER, 0.0f);
+ LLGLEnable blend(GL_BLEND);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (for_hud && avatar)
+ {
+ LLBBox hud_bbox = avatar->getHUDBBox();
+
+ F32 cur_zoom = avatar->mHUDCurZoom;
+
+ // set up transform to encompass bounding box of HUD
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+ glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+ glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
+ glScalef(cur_zoom, cur_zoom, cur_zoom);
+ }
+ if (mSelectedObjects.getNumNodes())
+ {
+ glPushAttrib(GL_FOG_BIT);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mSelectedObjects.getFirstNode(); node; node = mSelectedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+ if (objectp->isHUDAttachment() != for_hud)
+ {
+ continue;
+ }
+ if (node->isTransient())
+ {
+ BOOL oldHidden = LLSelectMgr::sRenderHiddenSelections;
+ LLSelectMgr::sRenderHiddenSelections = FALSE;
+ node->renderOneSilhouette(sContextSilhouetteColor);
+ LLSelectMgr::sRenderHiddenSelections = oldHidden;
+ }
+ else if (objectp->isRootEdit())
+ {
+ node->renderOneSilhouette(sSilhouetteParentColor);
+ }
+ else
+ {
+ node->renderOneSilhouette(sSilhouetteChildColor);
+ }
+ }
+ }
+ glPopAttrib();
+ }
+
+ if (mHighlightedObjects.getNumNodes())
+ {
+ // render silhouettes for highlighted objects
+ BOOL subtracting_from_selection = (gKeyboard->currentMask(TRUE) == MASK_CONTROL);
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (node = mHighlightedObjects.getFirstNode(); node; node = mHighlightedObjects.getNextNode() )
+ {
+ LLViewerObject* objectp = node->getObject();
+ if (objectp->isHUDAttachment() != for_hud)
+ {
+ continue;
+ }
+
+ if (subtracting_from_selection)
+ {
+ node->renderOneSilhouette(LLColor4::red);
+ }
+ else if (!objectp->isSelected())
+ {
+ LLColor4 highlight_color = objectp->isRoot() ? sHighlightParentColor : sHighlightChildColor;
+ node->renderOneSilhouette(highlight_color);
+ }
+ }
+ }
+ }
+
+ if (for_hud && avatar)
+ {
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ stop_glerror();
+ }
+
+ gSelectMgr->mSilhouetteImagep->unbindTexture(0, GL_TEXTURE_2D);
+ glAlphaFunc(GL_GREATER, 0.01f);
+}
+
+void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
+{
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (objectp && objectp->getPCode() == LL_PCODE_VOLUME)
+ {
+ ((LLVOVolume*)objectp)->generateSilhouette(nodep, view_point);
+ }
+}
+
+void LLSelectMgr::getSilhouetteExtents(LLSelectNode* nodep, const LLQuaternion& orientation, LLVector3& min_extents, LLVector3& max_extents)
+{
+ LLViewerObject* objectp = nodep->getObject();
+
+ if (objectp->mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLQuaternion test_rot = orientation * ~objectp->getRenderRotation();
+ LLVector3 x_axis_rot = LLVector3::x_axis * test_rot;
+ LLVector3 y_axis_rot = LLVector3::y_axis * test_rot;
+ LLVector3 z_axis_rot = LLVector3::z_axis * test_rot;
+
+ x_axis_rot.scaleVec(objectp->mDrawable->getScale());
+ y_axis_rot.scaleVec(objectp->mDrawable->getScale());
+ z_axis_rot.scaleVec(objectp->mDrawable->getScale());
+
+ generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + x_axis_rot * 100.f);
+
+ S32 num_vertices = nodep->mSilhouetteVertices.size();
+ if (num_vertices)
+ {
+ min_extents.mV[VY] = llmin(min_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot);
+ max_extents.mV[VY] = llmax(max_extents.mV[VY], nodep->mSilhouetteVertices[0] * y_axis_rot);
+
+ min_extents.mV[VZ] = llmin(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot);
+ max_extents.mV[VZ] = llmax(min_extents.mV[VZ], nodep->mSilhouetteVertices[0] * z_axis_rot);
+
+ for (S32 vert = 1; vert < num_vertices; vert++)
+ {
+ F32 y_pos = nodep->mSilhouetteVertices[vert] * y_axis_rot;
+ F32 z_pos = nodep->mSilhouetteVertices[vert] * z_axis_rot;
+ min_extents.mV[VY] = llmin(y_pos, min_extents.mV[VY]);
+ max_extents.mV[VY] = llmax(y_pos, max_extents.mV[VY]);
+ min_extents.mV[VZ] = llmin(z_pos, min_extents.mV[VZ]);
+ max_extents.mV[VZ] = llmax(z_pos, max_extents.mV[VZ]);
+ }
+ }
+
+ generateSilhouette(nodep, objectp->mDrawable->getPositionAgent() + y_axis_rot * 100.f);
+
+ num_vertices = nodep->mSilhouetteVertices.size();
+ if (num_vertices)
+ {
+ min_extents.mV[VX] = llmin(min_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot);
+ max_extents.mV[VX] = llmax(max_extents.mV[VX], nodep->mSilhouetteVertices[0] * x_axis_rot);
+
+ for (S32 vert = 1; vert < num_vertices; vert++)
+ {
+ F32 x_pos = nodep->mSilhouetteVertices[vert] * x_axis_rot;
+ min_extents.mV[VX] = llmin(x_pos, min_extents.mV[VX]);
+ max_extents.mV[VX] = llmax(x_pos, max_extents.mV[VX]);
+ }
+ }
+
+ generateSilhouette(nodep, gCamera->getOrigin());
+}
+
+
+//
+// Utility classes
+//
+LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow)
+{
+ mObject = object;
+ selectAllTEs(FALSE);
+ mIndividualSelection = FALSE;
+ mTransient = FALSE;
+ mValid = FALSE;
+ mPermissions = new LLPermissions();
+ mInventorySerial = 0;
+ mName = LLString::null;
+ mDescription = LLString::null;
+ mTouchName = LLString::null;
+ mSitName = LLString::null;
+ mSilhouetteExists = FALSE;
+ mDuplicated = FALSE;
+
+ saveColors();
+}
+
+LLSelectNode::LLSelectNode(const LLSelectNode& nodep)
+{
+ S32 i;
+ for (i = 0; i < SELECT_MAX_TES; i++)
+ {
+ mTESelected[i] = nodep.mTESelected[i];
+ }
+ mLastTESelected = nodep.mLastTESelected;
+
+ mIndividualSelection = nodep.mIndividualSelection;
+
+ mValid = nodep.mValid;
+ mTransient = nodep.mTransient;
+ mPermissions = new LLPermissions(*nodep.mPermissions);
+ mSaleInfo = nodep.mSaleInfo;;
+ mAggregatePerm = nodep.mAggregatePerm;
+ mAggregateTexturePerm = nodep.mAggregateTexturePerm;
+ mAggregateTexturePermOwner = nodep.mAggregateTexturePermOwner;
+ mName = nodep.mName;
+ mDescription = nodep.mDescription;
+ mCategory = nodep.mCategory;
+ mSavedPositionLocal = nodep.mSavedPositionLocal;
+ mSavedPositionGlobal = nodep.mSavedPositionGlobal;
+ mSavedScale = nodep.mSavedScale;
+ mSavedRotation = nodep.mSavedRotation;
+ mDuplicated = nodep.mDuplicated;
+ mDuplicatePos = nodep.mDuplicatePos;
+ mDuplicateRot = nodep.mDuplicateRot;
+ mItemID = nodep.mItemID;
+ mFolderID = nodep.mFolderID;
+ mFromTaskID = nodep.mFromTaskID;
+ mTouchName = nodep.mTouchName;
+ mSitName = nodep.mSitName;
+
+ mSilhouetteVertices = nodep.mSilhouetteVertices;
+ mSilhouetteNormals = nodep.mSilhouetteNormals;
+ mSilhouetteSegments = nodep.mSilhouetteSegments;
+ mSilhouetteExists = nodep.mSilhouetteExists;
+ mObject = nodep.mObject;
+
+ std::vector<LLColor4>::const_iterator color_iter;
+ mSavedColors.clear();
+ for (color_iter = nodep.mSavedColors.begin(); color_iter != nodep.mSavedColors.end(); ++color_iter)
+ {
+ mSavedColors.push_back(*color_iter);
+ }
+
+ saveTextures(nodep.mSavedTextures);
+}
+
+LLSelectNode::~LLSelectNode()
+{
+ delete mPermissions;
+ mPermissions = NULL;
+}
+
+void LLSelectNode::selectAllTEs(BOOL b)
+{
+ for (S32 i = 0; i < SELECT_MAX_TES; i++)
+ {
+ mTESelected[i] = b;
+ }
+ mLastTESelected = 0;
+}
+
+void LLSelectNode::selectTE(S32 te_index, BOOL selected)
+{
+ if (te_index < 0 || te_index >= SELECT_MAX_TES)
+ {
+ return;
+ }
+ mTESelected[te_index] = selected;
+ mLastTESelected = te_index;
+}
+
+BOOL LLSelectNode::isTESelected(S32 te_index)
+{
+ if (te_index < 0 || te_index >= mObject->getNumTEs())
+ {
+ return FALSE;
+ }
+ return mTESelected[te_index];
+}
+
+S32 LLSelectNode::getLastSelectedTE()
+{
+ if (!isTESelected(mLastTESelected))
+ {
+ return -1;
+ }
+ return mLastTESelected;
+}
+
+LLViewerObject *LLSelectNode::getObject()
+{
+ if (!mObject)
+ {
+ return NULL;
+ }
+ else if (mObject->isDead())
+ {
+ mObject = NULL;
+ }
+ return mObject;
+}
+
+void LLSelectNode::saveColors()
+{
+ if (mObject.notNull())
+ {
+ mSavedColors.clear();
+ for (S32 i = 0; i < mObject->getNumTEs(); i++)
+ {
+ const LLTextureEntry* tep = mObject->getTE(i);
+ mSavedColors.push_back(tep->getColor());
+ }
+ }
+}
+
+void LLSelectNode::saveTextures(const std::vector<LLUUID>& textures)
+{
+ if (mObject.notNull())
+ {
+ mSavedTextures.clear();
+
+ std::vector<LLUUID>::const_iterator texture_it;
+ for (texture_it = textures.begin(); texture_it != textures.end(); ++texture_it)
+ {
+ mSavedTextures.push_back(*texture_it);
+ }
+ }
+}
+
+void LLSelectNode::saveTextureScaleRatios()
+{
+ mTextureScaleRatios.clear();
+ if (mObject.notNull())
+ {
+ for (U8 i = 0; i < mObject->getNumTEs(); i++)
+ {
+ F32 s,t;
+ const LLTextureEntry* tep = mObject->getTE(i);
+ tep->getScale(&s,&t);
+ U32 s_axis, t_axis;
+
+ gSelectMgr->getTESTAxes(mObject, i, &s_axis, &t_axis);
+
+ LLVector3 v;
+ LLVector3 scale = mObject->getScale();
+
+ if (tep->getTexGen() == LLTextureEntry::TEX_GEN_PLANAR)
+ {
+ v.mV[s_axis] = s*scale.mV[s_axis];
+ v.mV[t_axis] = t*scale.mV[t_axis];
+ }
+ else
+ {
+ v.mV[s_axis] = s/scale.mV[s_axis];
+ v.mV[t_axis] = t/scale.mV[t_axis];
+ }
+
+ mTextureScaleRatios.push_back(v);
+ }
+ }
+}
+
+
+// This implementation should be similar to LLTask::allowOperationOnTask
+BOOL LLSelectNode::allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const
+{
+ // Extract ownership.
+ BOOL object_is_group_owned = FALSE;
+ LLUUID object_owner_id;
+ mPermissions->getOwnership(object_owner_id, object_is_group_owned);
+
+ // Operations on invalid or public objects is not allowed.
+ if (!mObject || (mObject->isDead()) || !mPermissions->isOwned())
+ {
+ return FALSE;
+ }
+
+ // The transfer permissions can never be given through proxy.
+ if (PERM_TRANSFER == op)
+ {
+ // The owner of an agent-owned object can transfer to themselves.
+ if ( !object_is_group_owned
+ && (gAgent.getID() == object_owner_id) )
+ {
+ return TRUE;
+ }
+ else
+ {
+ // Otherwise check aggregate permissions.
+ return mObject->permTransfer();
+ }
+ }
+
+ if (PERM_MOVE == op
+ || PERM_MODIFY == op)
+ {
+ // only owners can move or modify their attachments
+ // no proxy allowed.
+ if (mObject->isAttachment() && object_owner_id != gAgent.getID())
+ {
+ return FALSE;
+ }
+ }
+
+ // Calculate proxy_agent_id and group_id to use for permissions checks.
+ // proxy_agent_id may be set to the object owner through group powers.
+ // group_id can only be set to the object's group, if the agent is in that group.
+ LLUUID group_id = LLUUID::null;
+ LLUUID proxy_agent_id = gAgent.getID();
+
+ // Gods can always operate.
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+
+ // Check if the agent is in the same group as the object.
+ LLUUID object_group_id = mPermissions->getGroup();
+ if (object_group_id.notNull() &&
+ gAgent.isInGroup(object_group_id))
+ {
+ // Assume the object's group during this operation.
+ group_id = object_group_id;
+ }
+
+ // Only allow proxy powers for PERM_COPY if the actual agent can
+ // receive the item (ie has PERM_TRANSFER permissions).
+ // NOTE: op == PERM_TRANSFER has already been handled, but if
+ // that ever changes we need to BLOCK proxy powers for PERM_TRANSFER. DK 03/28/06
+ if (PERM_COPY != op || mPermissions->allowTransferTo(gAgent.getID()))
+ {
+ // Check if the agent can assume ownership through group proxy or agent-granted proxy.
+ if ( ( object_is_group_owned
+ && gAgent.hasPowerInGroup(object_owner_id, group_proxy_power))
+ // Only allow proxy for move, modify, and copy.
+ || ( (PERM_MOVE == op || PERM_MODIFY == op || PERM_COPY == op)
+ && (!object_is_group_owned
+ && gAgent.isGrantedProxy(*mPermissions))))
+ {
+ // This agent is able to assume the ownership role for this operation.
+ proxy_agent_id = object_owner_id;
+ }
+ }
+
+ // We now have max ownership information.
+ if (PERM_OWNER == op)
+ {
+ // This this was just a check for ownership, we can now return the answer.
+ return (proxy_agent_id == object_owner_id ? TRUE : FALSE);
+ }
+
+ // check permissions to see if the agent can operate
+ return (mPermissions->allowOperationBy(op, proxy_agent_id, group_id));
+}
+
+//-----------------------------------------------------------------------------
+// renderOneSilhouette()
+//-----------------------------------------------------------------------------
+void LLSelectNode::renderOneSilhouette(const LLColor4 &color)
+{
+ LLViewerObject* objectp = getObject();
+ if (!objectp)
+ {
+ return;
+ }
+
+ LLDrawable* drawable = objectp->mDrawable;
+ if(!drawable)
+ {
+ return;
+ }
+
+ if (!mSilhouetteExists)
+ {
+ return;
+ }
+
+ BOOL is_hud_object = objectp->isHUDAttachment();
+
+ if (!drawable->isVisible() && !is_hud_object)
+ {
+ return;
+ }
+
+ if (mSilhouetteVertices.size() == 0 || mSilhouetteNormals.size() != mSilhouetteVertices.size())
+ {
+ return;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (!is_hud_object)
+ {
+ glLoadIdentity();
+ glMultMatrixd(gGLModelView);
+ }
+
+
+ if (drawable->isActive())
+ {
+ glMultMatrixf((F32*) objectp->getRenderMatrix().mMatrix);
+ }
+
+ LLVolume *volume = objectp->getVolume();
+ if (volume)
+ {
+ F32 silhouette_thickness;
+ if (is_hud_object && gAgent.getAvatarObject())
+ {
+ silhouette_thickness = LLSelectMgr::sHighlightThickness / gAgent.getAvatarObject()->mHUDCurZoom;
+ }
+ else
+ {
+ LLVector3 view_vector = gCamera->getOrigin() - objectp->getRenderPosition();
+ silhouette_thickness = drawable->mDistanceWRTCamera * LLSelectMgr::sHighlightThickness * (gCamera->getView() / gCamera->getDefaultFOV());
+ }
+ F32 animationTime = (F32)LLFrameTimer::getElapsedSeconds();
+
+ F32 u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f);
+ F32 v_coord = 1.f - fmod(animationTime * LLSelectMgr::sHighlightVAnim, 1.f);
+ F32 u_divisor = 1.f / ((F32)(mSilhouetteVertices.size() - 1));
+
+ if (LLSelectMgr::sRenderHiddenSelections) // && gFloaterTools && gFloaterTools->getVisible())
+ {
+ glBlendFunc(GL_SRC_COLOR, GL_ONE);
+ LLGLEnable fog(GL_FOG);
+ glFogi(GL_FOG_MODE, GL_LINEAR);
+ float d = (gCamera->getPointOfInterest()-gCamera->getOrigin()).magVec();
+ LLColor4 fogCol = color * (F32)llclamp((gSelectMgr->getSelectionCenterGlobal()-gAgent.getCameraPositionGlobal()).magVec()/(gSelectMgr->getBBoxOfSelection().getExtentLocal().magVec()*4), 0.0, 1.0);
+ glFogf(GL_FOG_START, d);
+ glFogf(GL_FOG_END, d*(1 + (gCamera->getView() / gCamera->getDefaultFOV())));
+ glFogfv(GL_FOG_COLOR, fogCol.mV);
+
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_GEQUAL);
+ glAlphaFunc(GL_GREATER, 0.01f);
+ glBegin(GL_LINES);
+ {
+ S32 i = 0;
+ for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+ {
+// S32 first_i = i;
+ for(; i < mSilhouetteSegments[seg_num]; i++)
+ {
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+ }
+
+ /*u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.4f);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[first_i].mV );*/
+ }
+ }
+ glEnd();
+ u_coord = fmod(animationTime * LLSelectMgr::sHighlightUAnim, 1.f);
+ }
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ //glAlphaFunc(GL_GREATER, LLSelectMgr::sHighlightAlphaTest);
+ glBegin(GL_TRIANGLES);
+ {
+ S32 i = 0;
+ for (S32 seg_num = 0; seg_num < (S32)mSilhouetteSegments.size(); seg_num++)
+ {
+ S32 first_i = i;
+ LLVector3 v;
+ LLVector2 t;
+
+ for(; i < mSilhouetteSegments[seg_num]; i++)
+ {
+
+ if (i == first_i) {
+ LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
+ vert += mSilhouetteVertices[i];
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+ glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
+ glVertex3fv( vert.mV );
+
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+
+ glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+
+ v = mSilhouetteVertices[i];
+ t = LLVector2(u_coord, v_coord);
+ }
+ else {
+ LLVector3 vert = (mSilhouetteNormals[i]) * silhouette_thickness;
+ vert += mSilhouetteVertices[i];
+
+ glColor4f(color.mV[VRED], color.mV[VGREEN], color.mV[VBLUE], 0.0f); //LLSelectMgr::sHighlightAlpha);
+ glTexCoord2f( u_coord, v_coord + LLSelectMgr::sHighlightVScale );
+ glVertex3fv( vert.mV );
+ glVertex3fv( vert.mV );
+
+ glTexCoord2fv(t.mV);
+ u_coord += u_divisor * LLSelectMgr::sHighlightUScale;
+ glColor4f(color.mV[VRED]*2, color.mV[VGREEN]*2, color.mV[VBLUE]*2, LLSelectMgr::sHighlightAlpha*2);
+ glVertex3fv(v.mV);
+ glTexCoord2f( u_coord, v_coord );
+ glVertex3fv( mSilhouetteVertices[i].mV );
+
+ }
+ }
+ }
+ }
+ glEnd();
+
+ }
+ glPopMatrix();
+}
+
+//
+// Utility Functions
+//
+
+// Update everyone who cares about the selection list
+void dialog_refresh_all()
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ //could refresh selected object info in toolbar here
+
+ gFloaterTools->dirty();
+
+ if( gPieObject->getVisible() )
+ {
+ gPieObject->arrange();
+ }
+
+ LLFloaterProperties::dirtyAll();
+}
+
+S32 get_family_count(LLViewerObject *parent)
+{
+ if (!parent)
+ {
+ llwarns << "Trying to get_family_count on null parent!" << llendl;
+ }
+ S32 count = 1; // for this object
+ for (U32 i = 0; i < parent->mChildList.size(); i++)
+ {
+ LLViewerObject* child = parent->mChildList[i];
+
+ if (!child)
+ {
+ llwarns << "Family object has NULL child! Show Doug." << llendl;
+ }
+ else if (child->isDead())
+ {
+ llwarns << "Family object has dead child object. Show Doug." << llendl;
+ }
+ else
+ {
+ if (gSelectMgr->canSelectObject(child))
+ {
+ count += get_family_count( child );
+ }
+ }
+ }
+ return count;
+}
+
+//-----------------------------------------------------------------------------
+// updateSelectionCenter
+//-----------------------------------------------------------------------------
+void LLSelectMgr::updateSelectionCenter()
+{
+ const F32 MOVE_SELECTION_THRESHOLD = 1.f; // Movement threshold in meters for updating selection
+ // center (tractor beam)
+
+ LLViewerObject* object = mSelectedObjects.getFirstObject();
+ if (!object)
+ {
+ // nothing selected, probably grabbing
+ // Ignore by setting to avatar origin.
+ mSelectionCenterGlobal.clearVec();
+ mShowSelection = FALSE;
+ mSelectionBBox = LLBBox();
+ mPauseRequest = NULL;
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
+ gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
+ }
+ }
+ else
+ {
+ mSelectType = getSelectTypeForObject(object);
+
+ if (mSelectType == SELECT_TYPE_ATTACHMENT && gAgent.getAvatarObject())
+ {
+ mPauseRequest = gAgent.getAvatarObject()->requestPause();
+ }
+ else
+ {
+ mPauseRequest = NULL;
+ }
+
+ if (mSelectType != SELECT_TYPE_HUD && gAgent.getAvatarObject())
+ {
+ // reset hud ZOOM
+ gAgent.getAvatarObject()->mHUDTargetZoom = 1.f;
+ gAgent.getAvatarObject()->mHUDCurZoom = 1.f;
+ }
+
+ mShowSelection = FALSE;
+ LLBBox bbox;
+
+ // have stuff selected
+ LLVector3d select_center;
+ // keep a list of jointed objects for showing the joint HUDEffects
+ gHUDManager->clearJoints();
+ LLDynamicArray < LLViewerObject *> jointed_objects;
+
+ for (object = mSelectedObjects.getFirstObject(); object; object = mSelectedObjects.getNextObject() )
+ {
+ LLViewerObject *myAvatar = gAgent.getAvatarObject();
+ LLViewerObject *root = object->getRootEdit();
+ if (mSelectType == SELECT_TYPE_WORLD && // not an attachment
+ !root->isChild(myAvatar) && // not the object you're sitting on
+ !object->isAvatar()) // not another avatar
+ {
+ mShowSelection = TRUE;
+ }
+
+ bbox.addBBoxAgent( object->getBoundingBoxAgent() );
+
+ if (object->isJointChild())
+ {
+ jointed_objects.put(object);
+ }
+ } // end for
+
+ LLVector3 bbox_center_agent = bbox.getCenterAgent();
+ mSelectionCenterGlobal = gAgent.getPosGlobalFromAgent(bbox_center_agent);
+ mSelectionBBox = bbox;
+
+ if (jointed_objects.count())
+ {
+ gHUDManager->showJoints(&jointed_objects);
+ }
+ }
+
+ if ( !(gAgentID == LLUUID::null) )
+ {
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (mShowSelection)
+ {
+ LLVector3d select_center_global;
+
+ if( tool->isEditing() )
+ {
+ select_center_global = tool->getEditingPointGlobal();
+ }
+ else
+ {
+ select_center_global = mSelectionCenterGlobal;
+ }
+
+ // Send selection center if moved beyond threshold (used to animate tractor beam)
+ LLVector3d diff;
+ diff = select_center_global - mLastSentSelectionCenterGlobal;
+
+ if ( diff.magVecSquared() > MOVE_SELECTION_THRESHOLD*MOVE_SELECTION_THRESHOLD )
+ {
+ // Transmit updated selection center
+ mLastSentSelectionCenterGlobal = select_center_global;
+ }
+ }
+ }
+
+ // give up edit menu if no objects selected
+ if (gEditMenuHandler == this && getObjectCount() == 0)
+ {
+ gEditMenuHandler = NULL;
+ }
+}
+
+void LLSelectMgr::updatePointAt()
+{
+ if (mShowSelection)
+ {
+ if (getObjectCount())
+ {
+ LLVector3 select_offset;
+ LLViewerObject *click_object = gObjectList.findObject(gLastHitObjectID);
+ if (click_object && click_object->isSelected())
+ {
+ // clicked on another object in our selection group, use that as target
+ select_offset.setVec(gLastHitObjectOffset);
+ select_offset.rotVec(~click_object->getRenderRotation());
+
+ gAgent.setPointAt(POINTAT_TARGET_SELECT, click_object, select_offset);
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, click_object, select_offset);
+ }
+ else
+ {
+ // didn't click on an object this time, revert to pointing at center of first object
+ gAgent.setPointAt(POINTAT_TARGET_SELECT, getFirstObject());
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, getFirstObject());
+ }
+ }
+ else
+ {
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+ }
+ else
+ {
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getBBoxOfSelection()
+//-----------------------------------------------------------------------------
+LLBBox LLSelectMgr::getBBoxOfSelection() const
+{
+ return mSelectionBBox;
+}
+
+
+//-----------------------------------------------------------------------------
+// canUndo()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canUndo()
+{
+ return getFirstEditableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// undo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::undo()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ LLUUID group_id(gAgent.getGroupID());
+ sendListToRegions("Undo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+}
+
+//-----------------------------------------------------------------------------
+// canRedo()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canRedo()
+{
+ return getFirstEditableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// redo()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::redo()
+{
+ BOOL select_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ LLUUID group_id(gAgent.getGroupID());
+ sendListToRegions("Redo", packAgentAndSessionAndGroupID, packObjectID, &group_id, select_linked_set ? SEND_ONLY_ROOTS : SEND_CHILDREN_FIRST);
+}
+
+//-----------------------------------------------------------------------------
+// canDoDelete()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDoDelete()
+{
+ return getFirstDeleteableObject() != NULL;
+}
+
+//-----------------------------------------------------------------------------
+// doDelete()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::doDelete()
+{
+ selectDelete();
+}
+
+//-----------------------------------------------------------------------------
+// canDeselect()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDeselect()
+{
+ return !isEmpty();
+}
+
+//-----------------------------------------------------------------------------
+// deselect()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::deselect()
+{
+ deselectAll();
+}
+//-----------------------------------------------------------------------------
+// canDuplicate()
+//-----------------------------------------------------------------------------
+BOOL LLSelectMgr::canDuplicate()
+{
+ return getFirstCopyableObject() != NULL;
+}
+//-----------------------------------------------------------------------------
+// duplicate()
+//-----------------------------------------------------------------------------
+void LLSelectMgr::duplicate()
+{
+ LLVector3 offset(0.5f, 0.5f, 0.f);
+ selectDuplicate(offset, TRUE);
+}
+//-----------------------------------------------------------------------------
+// undoRedo()
+//-----------------------------------------------------------------------------
+U32 LLSelectMgr::undoRedo(std::deque<LLSelectAction*> &queue_src, std::deque<LLSelectAction*> &queue_dst, const LLUUID &object_id)
+{
+ if (queue_src.size() == 0)
+ {
+ return 0;
+ }
+
+ U32 update_type = 0;
+ std::deque<LLSelectAction*> temp_queue;
+ LLSelectAction* src_actionp = queue_src.back();
+
+ while (src_actionp->mObjectID != object_id)
+ {
+ temp_queue.push_back(src_actionp);
+ queue_src.pop_back();
+ if (!queue_src.size())
+ {
+ // put everything back
+ LLSelectAction* actionp;
+ while (temp_queue.size())
+ {
+ actionp = temp_queue.back();
+ temp_queue.pop_back();
+ queue_src.push_back(actionp);
+ }
+ return 0;
+ }
+ src_actionp = queue_src.back();
+ }
+
+ if(src_actionp)
+ {
+ LLSelectAction* dst_actionp = new LLSelectAction();
+ dst_actionp->mActionType = src_actionp->mActionType;
+ dst_actionp->mObjectID = src_actionp->mObjectID;
+ dst_actionp->mIndividualSelection = src_actionp->mIndividualSelection;
+
+ LLViewerObject* object = gObjectList.findObject(src_actionp->mObjectID);
+ if (object && object->mDrawable.notNull())
+ {
+ LLVector3 old_position_local = object->getPosition();
+
+ switch(src_actionp->mActionType)
+ {
+ case SELECT_ACTION_TYPE_MOVE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ object->setPosition(src_actionp->mPosition, TRUE);
+ if (object->isRootEdit() && src_actionp->mIndividualSelection)
+ {
+ // counter-translate children
+ LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation();
+
+ // counter-translate child objects if we are moving the root as an individual
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ childp->setPosition(childp->getPosition() - parent_offset);
+ }
+ }
+ update_type |= UPD_POSITION;
+ break;
+ case SELECT_ACTION_TYPE_ROTATE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ dst_actionp->mRotation = object->mDrawable->getRotation();
+ object->setRotation(src_actionp->mRotation, TRUE);
+ object->setPosition(src_actionp->mPosition, TRUE);
+ if (object->isRootEdit() && src_actionp->mIndividualSelection)
+ {
+ // counter-translate and rotate children
+ LLVector3 parent_offset = (src_actionp->mPosition - old_position_local) * ~object->getRotation();
+
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++)
+ {
+ LLViewerObject* childp = object->mChildList[child_num];
+ LLQuaternion delta_rot_inv = dst_actionp->mRotation * ~src_actionp->mRotation;
+ childp->setPosition((childp->getPosition() * delta_rot_inv) - parent_offset);
+ childp->setRotation(childp->getRotation() * delta_rot_inv );
+ }
+ }
+ update_type |= UPD_ROTATION | UPD_POSITION;
+ break;
+ case SELECT_ACTION_TYPE_SCALE:
+ dst_actionp->mPosition = object->mDrawable->getPosition();
+ dst_actionp->mScale = object->getScale();
+ object->setScale(src_actionp->mScale, TRUE);
+ object->setPosition(src_actionp->mPosition, TRUE);
+ update_type |= UPD_SCALE | UPD_POSITION;
+ break;
+ default:
+ // do nothing
+ break;
+ }
+ }
+ queue_src.pop_back();
+ delete src_actionp;
+ queue_dst.push_back(dst_actionp);
+ while (queue_dst.size() > (U32)MAX_ACTION_QUEUE_SIZE)
+ {
+ LLSelectAction* action = queue_dst.front();
+ queue_dst.pop_front();
+ delete action;
+ }
+
+ }
+
+ // put everything back
+ LLSelectAction* actionp;
+ while (temp_queue.size())
+ {
+ actionp = temp_queue.back();
+ temp_queue.pop_back();
+ queue_src.push_back(actionp);
+ }
+
+ return update_type;
+}
+
+ESelectType LLSelectMgr::getSelectTypeForObject(LLViewerObject* object)
+{
+ if (!object)
+ {
+ return SELECT_TYPE_WORLD;
+ }
+ if (object->isHUDAttachment())
+ {
+ return SELECT_TYPE_HUD;
+ }
+ else if (object->isAttachment())
+ {
+ return SELECT_TYPE_ATTACHMENT;
+ }
+ else
+ {
+ return SELECT_TYPE_WORLD;
+ }
+}
+
+void LLSelectMgr::validateSelection()
+{
+ LLViewerObject* objectp;
+ for (objectp = getFirstObject(); objectp; objectp = getNextObject())
+ {
+ if (!canSelectObject(objectp))
+ {
+ deselectObjectOnly(objectp);
+ }
+ }
+}
+
+BOOL LLSelectMgr::canSelectObject(LLViewerObject* object)
+{
+ if (mForceSelection)
+ {
+ return TRUE;
+ }
+
+ if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !object->permYouOwner()) ||
+ (gSavedSettings.getBOOL("SelectMovableOnly") && !object->permMove()))
+ {
+ // only select my own objects
+ return FALSE;
+ }
+
+ // Can't select dead objects
+ if (object->isDead()) return FALSE;
+
+ // Can't select orphans
+ if (object->isOrphaned()) return FALSE;
+
+ // Can't select avatars
+ if (object->isAvatar()) return FALSE;
+
+ // Can't select land
+ if (object->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH) return FALSE;
+
+ ESelectType selection_type = getSelectTypeForObject(object);
+ if (getObjectCount() > 0 && mSelectType != selection_type) return FALSE;
+
+ return TRUE;
+}
+
+LLSelectNodeList::LLSelectNodeList() : std::list<LLSelectNode*>()
+{
+ mCurrentTE = -1;
+ mCurrentNode = end();
+}
+
+LLSelectNodeList::~LLSelectNodeList()
+{
+ std::for_each(begin(), end(), DeletePointer());
+}
+
+void LLSelectNodeList::updateEffects()
+{
+}
+
+S32 LLSelectNodeList::getNumNodes()
+{
+ return size();
+}
+
+void LLSelectNodeList::addNode(LLSelectNode *nodep)
+{
+ push_front(nodep);
+ mSelectNodeMap[nodep->getObject()] = nodep;
+}
+
+void LLSelectNodeList::addNodeAtEnd(LLSelectNode *nodep)
+{
+ push_back(nodep);
+ mSelectNodeMap[nodep->getObject()] = nodep;
+}
+
+void LLSelectNodeList::removeNode(LLSelectNode *nodep)
+{
+ std::list<LLSelectNode*>::iterator iter;
+ for (iter = begin(); iter != end(); ++iter)
+ {
+ if ((*iter) == nodep)
+ {
+ mSelectNodeMap.erase(nodep->getObject());
+ erase(iter++);
+ }
+ }
+}
+
+void LLSelectNodeList::deleteAllNodes()
+{
+ std::for_each(begin(), end(), DeletePointer());
+ clear();
+ mSelectNodeMap.clear();
+}
+
+LLSelectNode* LLSelectNodeList::findNode(LLViewerObject* objectp)
+{
+ std::map<LLViewerObject*, LLSelectNode*>::iterator found_it = mSelectNodeMap.find(objectp);
+ if (found_it != mSelectNodeMap.end())
+ {
+ return found_it->second;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getFirstNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getFirstNode()
+{
+ mCurrentNode = begin();//getFirstData();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getCurrentNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getCurrentNode()
+{
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// getNextNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getNextNode()
+{
+ ++mCurrentNode;
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return *mCurrentNode;
+ }
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// getFirstObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectNodeList::getFirstObject()
+{
+ mCurrentNode = begin();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return (*mCurrentNode)->getObject();
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextObject()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLSelectNodeList::getNextObject()
+{
+ ++mCurrentNode;// = getNextData();
+
+ while (mCurrentNode != end() && !(*mCurrentNode)->getObject())
+ {
+ // The object on this was killed at some point, delete it.
+ erase(mCurrentNode++);
+ }
+
+ if (mCurrentNode != end())
+ {
+ return (*mCurrentNode)->getObject();
+ }
+
+ return NULL;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// getPrimaryTE()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getPrimaryTE(LLViewerObject* *object, S32 *te)
+{
+ // initialize object and te
+ *te = 0;
+ *object = NULL;
+
+ BOOL searching_roots = TRUE;
+
+ // try for root node first, then first child node
+ LLSelectNode *primary_node = getFirstNode(); //getFirstRootNode();
+ if (!primary_node)
+ {
+ primary_node = getFirstNode();
+ searching_roots = FALSE;
+ }
+
+ while (primary_node)
+ {
+ S32 last_selected_te = primary_node->getLastSelectedTE();
+ if (last_selected_te >= 0)
+ {
+ *object = primary_node->getObject();
+ *te = last_selected_te;
+ return;
+ }
+ for(S32 cur_te = 0; cur_te < primary_node->getObject()->getNumTEs(); cur_te++)
+ {
+ // if face selected
+ if (primary_node->isTESelected(cur_te))
+ {
+ // return this object and face
+ *object = primary_node->getObject();
+ *te = cur_te;
+ return;
+ }
+ }
+ if (searching_roots)
+ {
+ primary_node = getNextRootNode();
+ if (!primary_node)
+ {
+ primary_node = getFirstNode();
+ searching_roots = FALSE;
+ }
+ }
+ else
+ {
+ primary_node = getNextNode();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getFirstTE()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getFirstTE(LLViewerObject* *object, S32 *te)
+{
+ // start with first face
+ mCurrentTE = 0;
+
+ LLSelectNode *cur_node = getFirstNode();
+
+ // repeat over all selection nodes
+ while (cur_node)
+ {
+ // skip objects with no faces
+ if (cur_node->getObject()->getNumTEs() == 0)
+ {
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ continue;
+ }
+
+ // repeat over all faces for this object
+ while (mCurrentTE < cur_node->getObject()->getNumTEs())
+ {
+ // if face selected
+ if (cur_node->isTESelected(mCurrentTE))
+ {
+ // return this object and face
+ *object = cur_node->getObject();
+ *te = mCurrentTE;
+ return;
+ }
+
+ mCurrentTE++;
+ }
+
+ // Couldn't find a selected face.
+ // This can happen if an object's volume parameters are changed in such a way
+ // that texture entries are eliminated.
+ //
+ // TODO: Consider selecting all faces in this case? Subscribe the selection
+ // list to the volume changing code?
+
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ }
+
+ // The list doesn't contain any nodes. Return NULL.
+ *object = NULL;
+ *te = -1;
+ return;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextFace()
+//-----------------------------------------------------------------------------
+void LLSelectNodeList::getNextTE(LLViewerObject* *object, S32 *te)
+{
+ // try next face
+ mCurrentTE++;
+
+ LLSelectNode *cur_node = getCurrentNode();
+ // repeat over remaining selection nodes
+ while ( cur_node )
+ {
+ // skip objects with no faces
+ if (cur_node->getObject()->getNumTEs() == 0)
+ {
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ continue;
+ }
+
+ // repeat over all faces for this object
+ // CRO: getNumTEs() no longer equals mFaces.count(), so use mFaces.count() instead
+ while ( mCurrentTE < cur_node->getObject()->getNumTEs() )
+ {
+ // if face selected
+ if (cur_node->isTESelected(mCurrentTE))
+ {
+ // return this object and face
+ *object = cur_node->getObject();
+ *te = mCurrentTE;
+ return;
+ }
+
+ mCurrentTE++;
+ }
+
+ mCurrentTE = 0;
+ cur_node = getNextNode();
+ }
+
+ // The list doesn't contain any nodes. Return NULL.
+ *object = NULL;
+ *te = -1;
+ return;
+}
+
+void LLSelectNodeList::getCurrentTE(LLViewerObject* *object, S32 *te)
+{
+ if (mCurrentNode != end())
+ {
+ *object = (*mCurrentNode)->getObject();
+ *te = mCurrentTE;
+ }
+ else
+ {
+ *object = NULL;
+ *te = -1;
+ }
+}
+//-----------------------------------------------------------------------------
+// getFirstRootNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getFirstRootNode()
+{
+ LLSelectNode *cur_node = getFirstNode();
+
+ // scan through child objects and roots set to ignore
+ while (cur_node &&
+ (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) ||
+ cur_node->mIndividualSelection))
+ {
+ cur_node = getNextNode();
+ }
+
+ return cur_node;
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextRootNode()
+//-----------------------------------------------------------------------------
+LLSelectNode *LLSelectNodeList::getNextRootNode()
+{
+ LLSelectNode *cur_node = getNextNode();
+
+ while (cur_node &&
+ (!(cur_node->getObject()->isRootEdit() || cur_node->getObject()->isJointChild()) ||
+ cur_node->mIndividualSelection))
+ {
+ cur_node = getNextNode();
+ }
+
+ return cur_node;
+}
+
+
+//-----------------------------------------------------------------------------
+// getFirstRootObject()
+//-----------------------------------------------------------------------------
+LLViewerObject *LLSelectNodeList::getFirstRootObject()
+{
+ LLSelectNode *node = getFirstRootNode();
+
+ if (node)
+ {
+ return node->getObject();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getNextRootObject()
+//-----------------------------------------------------------------------------
+LLViewerObject *LLSelectNodeList::getNextRootObject()
+{
+ LLSelectNode *node = getNextRootNode();
+
+ if (node)
+ {
+ return node->getObject();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// isEmpty()
+//-----------------------------------------------------------------------------
+BOOL LLSelectNodeList::isEmpty()
+{
+ return (size() == 0);
+}
+
+//-----------------------------------------------------------------------------
+// getOwnershipCost()
+//-----------------------------------------------------------------------------
+BOOL LLSelectNodeList::getOwnershipCost(S32 &cost)
+{
+ S32 count = 0;
+ for( LLSelectNode* nodep = getFirstNode(); nodep; nodep = getNextNode() )
+ {
+ count++;
+ }
+
+ cost = count * OWNERSHIP_COST_PER_OBJECT;
+
+ return (count > 0);
+}
diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h
new file mode 100644
index 0000000000..df07e5518f
--- /dev/null
+++ b/indra/newview/llselectmgr.h
@@ -0,0 +1,625 @@
+/**
+ * @file llselectmgr.h
+ * @brief A manager for selected objects and TEs.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSELECTMGR_H
+#define LL_LLSELECTMGR_H
+
+#include "llcharacter.h"
+#include "lldarray.h"
+#include "lleditmenuhandler.h"
+#include "llstring.h"
+#include "llundo.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llsaleinfo.h"
+#include "llcategory.h"
+#include "v3dmath.h"
+#include "llquaternion.h"
+#include "llcoord.h"
+#include "llframetimer.h"
+#include "llbbox.h"
+#include "llpermissions.h"
+
+#include <deque>
+
+class LLMessageSystem;
+class LLViewerImage;
+class LLViewerObject;
+class LLColor4;
+class LLVector3;
+class LLSelectNode;
+
+const S32 SELECT_ALL_TES = -1;
+const S32 SELECT_MAX_TES = 32;
+
+// Do something to all objects in the selection manager.
+// The bool return value can be used to indicate if all
+// objects are identical (gathering information) or if
+// the operation was successful.
+class LLSelectedObjectFunctor
+{
+public:
+ virtual ~LLSelectedObjectFunctor() {};
+ virtual bool apply(LLViewerObject* object) = 0;
+};
+
+// Do something to all select nodes in the selection manager.
+// The bool return value can be used to indicate if all
+// objects are identical (gathering information) or if
+// the operation was successful.
+class LLSelectedNodeFunctor
+{
+public:
+ virtual ~LLSelectedNodeFunctor() {};
+ virtual bool apply(LLSelectNode* node) = 0;
+};
+
+typedef enum e_send_type
+{
+ SEND_ONLY_ROOTS,
+ SEND_INDIVIDUALS,
+ SEND_ROOTS_FIRST, // useful for serial undos on linked sets
+ SEND_CHILDREN_FIRST // useful for serial transforms of linked sets
+} ESendType;
+
+typedef enum e_grid_mode
+{
+ GRID_MODE_WORLD,
+ GRID_MODE_LOCAL,
+ GRID_MODE_REF_OBJECT
+} EGridMode;
+
+typedef enum e_action_type
+{
+ SELECT_ACTION_TYPE_BEGIN,
+ SELECT_ACTION_TYPE_PICK,
+ SELECT_ACTION_TYPE_MOVE,
+ SELECT_ACTION_TYPE_ROTATE,
+ SELECT_ACTION_TYPE_SCALE,
+ NUM_ACTION_TYPES
+}EActionType;
+
+typedef enum e_selection_type
+{
+ SELECT_TYPE_WORLD,
+ SELECT_TYPE_ATTACHMENT,
+ SELECT_TYPE_HUD
+}ESelectType;
+
+class LLSelectNodeList : public std::list<LLSelectNode*>
+{
+public:
+ LLSelectNodeList();
+ virtual ~LLSelectNodeList();
+
+ void updateEffects();
+
+ BOOL isEmpty();
+
+ S32 getOwnershipCost(S32 &cost);
+
+ LLSelectNode* getFirstNode();
+ LLSelectNode* getCurrentNode(); // Warning! This is NOT the same as the linked_list getCurrentNode
+ LLSelectNode* getNextNode();
+
+ LLSelectNode *getFirstRootNode();
+ LLSelectNode *getNextRootNode();
+
+ // iterate through objects
+ LLViewerObject* getFirstObject();
+ LLViewerObject* getNextObject();
+
+ // iterate through root objects
+ LLViewerObject *getFirstRootObject();
+ LLViewerObject *getNextRootObject();
+
+ // iterate through texture entries
+ void getPrimaryTE(LLViewerObject* *object, S32 *te);
+ void getFirstTE(LLViewerObject* *object, S32 *te);
+ void getNextTE(LLViewerObject* *object, S32 *te);
+ void getCurrentTE(LLViewerObject* *object, S32 *te);
+
+ void addNode(LLSelectNode *nodep);
+ void addNodeAtEnd(LLSelectNode *nodep);
+ void removeNode(LLSelectNode *nodep);
+ void deleteAllNodes(); // Delete all nodes
+ S32 getNumNodes();
+ LLSelectNode* findNode(LLViewerObject* objectp);
+private:
+ const LLSelectNodeList &operator=(const LLSelectNodeList &);
+
+ std::list<LLSelectNode*>::iterator mCurrentNode;
+ S32 mCurrentTE;
+ std::map<LLViewerObject*, LLSelectNode*> mSelectNodeMap;
+};
+
+struct LLSelectAction
+{
+public:
+ EActionType mActionType;
+ LLVector3 mPosition;
+ LLVector3 mScale;
+ LLQuaternion mRotation;
+ LLUUID mObjectID;
+ BOOL mIndividualSelection;
+};
+
+class LLSelectMgr : public LLEditMenuHandler
+{
+public:
+ static BOOL sRectSelectInclusive; // do we need to surround an object to pick it?
+ static BOOL sRenderHiddenSelections; // do we show selection silhouettes that are occluded?
+ static BOOL sRenderLightRadius; // do we show the radius of selected lights?
+ static F32 sHighlightThickness;
+ static F32 sHighlightUScale;
+ static F32 sHighlightVScale;
+ static F32 sHighlightAlpha;
+ static F32 sHighlightAlphaTest;
+ static F32 sHighlightUAnim;
+ static F32 sHighlightVAnim;
+ static LLColor4 sSilhouetteParentColor;
+ static LLColor4 sSilhouetteChildColor;
+ static LLColor4 sHighlightParentColor;
+ static LLColor4 sHighlightChildColor;
+ static LLColor4 sContextSilhouetteColor;
+public:
+ LLSelectMgr();
+ ~LLSelectMgr();
+
+ // LLEditMenuHandler interface
+ virtual BOOL canUndo();
+ virtual void undo();
+
+ virtual BOOL canRedo();
+ virtual void redo();
+
+ virtual BOOL canDoDelete();
+ virtual void doDelete();
+
+ virtual void deselect();
+ virtual BOOL canDeselect();
+
+ virtual void duplicate();
+ virtual BOOL canDuplicate();
+
+ // Apply functors to various subsets of the selected objects
+ // Returns the AND of all apply() calls.
+ bool applyToRootObjects(LLSelectedObjectFunctor* func);
+ bool applyToObjects(LLSelectedObjectFunctor* func);
+ bool applyToNodes(LLSelectedNodeFunctor* func);
+
+ void updateEffects(); // Update HUD effects
+
+ void setForceSelection(BOOL force) { mForceSelection = force; }
+
+ // For when you want just a child object.
+ void selectObjectOnly(LLViewerObject* object, S32 face = SELECT_ALL_TES);
+
+ // This method is meant to select an object, and then select all
+ // of the ancestors and descendents. This should be the normal behavior.
+ void selectObjectAndFamily(LLViewerObject* object, BOOL add_to_end = FALSE);
+
+ // Same as above, but takes a list of objects. Used by rectangle select.
+ void selectObjectAndFamily(const LLDynamicArray<LLViewerObject*>& object_list, BOOL send_to_sim = TRUE);
+
+ void deselectObjectOnly(LLViewerObject* object, BOOL send_to_sim = TRUE);
+
+ void deselectObjectAndFamily(LLViewerObject* object, BOOL send_to_sim = TRUE);
+
+ void deselectTransient(); // deselect "temporarily" selected objects (via pie menu)
+ void convertTransient(); // converts temporarily selected objects to full-fledged selections
+
+ // Send deselect messages to simulator, then clear the list
+ void deselectAll();
+
+ // Deselect if the selection center is too far away from the agent.
+ void deselectAllIfTooFar();
+
+ BOOL selectionRemoveObject(const LLUUID &id);
+
+ BOOL contains(LLViewerObject* object);
+ BOOL contains(LLViewerObject* object, S32 te);
+
+ ESelectType getSelectType() { return mSelectType; }
+
+ // count members
+ S32 getObjectCount();
+ S32 getTECount();
+ S32 getRootObjectCount();
+
+ BOOL isEmpty() { return !mSelectedObjects.getNumNodes(); }
+
+ BOOL shouldShowSelection() { return mShowSelection; }
+
+ LLSelectNodeList &getHoverObjects() { return mHoverObjects; }
+ LLSelectNodeList &getSelectedObjects() { return mSelectedObjects; }
+
+ // iterate through objects
+ LLViewerObject* getFirstObject() { return mSelectedObjects.getFirstObject(); }
+ LLViewerObject* getNextObject() { return mSelectedObjects.getNextObject(); }
+
+ // iterate through root objects
+ LLViewerObject *getFirstRootObject() { return mSelectedObjects.getFirstRootObject(); }
+ LLViewerObject *getNextRootObject() { return mSelectedObjects.getNextRootObject(); }
+
+ LLViewerObject* getFirstHighlightedObject() { return mHighlightedObjects.getFirstObject(); }
+ LLViewerObject* getNextHighlightedObject() { return mHighlightedObjects.getNextObject(); }
+
+ // iterate through tes
+ void getPrimaryTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getPrimaryTE(object, te); }
+ void getFirstTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getFirstTE(object, te); }
+ void getNextTE(LLViewerObject* *object, S32 *te) { mSelectedObjects.getNextTE(object, te); };
+
+ void setHoverObject(LLViewerObject *objectp);
+
+ void addGridObject(LLViewerObject* objectp);
+ void clearGridObjects();
+ void setGridMode(EGridMode mode);
+ EGridMode getGridMode() { return mGridMode; }
+ void getGrid(LLVector3& origin, LLQuaternion& rotation, LLVector3 &scale);
+
+ void highlightObjectOnly(LLViewerObject *objectp);
+ void highlightObjectAndFamily(LLViewerObject *objectp);
+ void highlightObjectAndFamily(const LLDynamicArray<LLViewerObject*>& list);
+ void unhighlightObjectOnly(LLViewerObject *objectp);
+ void unhighlightObjectAndFamily(LLViewerObject *objectp);
+ void unhighlightAll();
+ void selectHighlightedObjects();
+ void deselectHighlightedObjects();
+
+ LLSelectNode *findSelectNode(LLViewerObject *objectp);
+ LLSelectNode *getFirstRootNode() { return mSelectedObjects.getFirstRootNode(); }
+ LLSelectNode *getNextRootNode() { return mSelectedObjects.getNextRootNode(); }
+ LLSelectNode* getFirstNode() { return mSelectedObjects.getFirstNode(); }
+ LLSelectNode* getNextNode() { return mSelectedObjects.getNextNode(); }
+
+ LLSelectNode *getHoverNode();
+
+ BOOL getTEMode() { return mTEMode; }
+ void setTEMode(BOOL b) { mTEMode = b; }
+
+ LLViewerObject* getFirstCopyableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstEditableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstMoveableObject(BOOL get_root = FALSE);
+ LLViewerObject* getFirstDeleteableObject(BOOL get_root = FALSE);
+
+ LLSelectNode* getFirstEditableNode(BOOL get_root = FALSE);
+ LLSelectNode* getFirstMoveableNode(BOOL get_root = FALSE);
+
+ LLBBox getBBoxOfSelection() const;
+ LLBBox getSavedBBoxOfSelection() const { return mSavedSelectionBBox; }
+
+ BOOL areMultpleEditableObjectsSelected();
+
+ void dump();
+ void cleanup();
+
+ void updateSilhouettes();
+ void renderSilhouettes(BOOL for_hud);
+ void enableSilhouette(BOOL enable) { mRenderSilhouettes = enable; }
+
+ // Utility functions to operate on the list
+
+ void saveSelectedObjectTransform(EActionType action_type);
+ void saveSelectedObjectColors();
+ void saveSelectedObjectTextures();
+
+ void selectionUpdatePhysics(BOOL use_physics);
+ void selectionUpdateTemporary(BOOL is_temporary);
+ void selectionUpdatePhantom(BOOL is_ghost);
+ void selectionUpdateCastShadows(BOOL cast_shadows);
+ void selectionDump();
+
+ BOOL selectionAllPCode(LLPCode code); // all objects have this PCode
+ BOOL selectionGetMaterial(U8 *material); // all objects have same material
+ BOOL selectionGetTexUUID(LLUUID& id); // true if all selected tes have same texture
+ BOOL selectionGetColor(LLColor4 &color); // all tes have same color
+ BOOL selectionGetTexScale(F32 *u, F32 *v); // true if all selected tes have same scale
+ BOOL selectionGetTexOffset(F32 *u, F32 *v); // true if all selected tes have same offset
+ BOOL selectionGetTexRotation(F32 *rad); // true if all selected tes have same rotation
+ BOOL selectionGetBumpmap(U8 *bumpmap); // true if all selected tes have same
+ BOOL selectionGetShiny(U8 *shiny); // true if all selected tes have same
+ BOOL selectionGetFullbright(U8 *fullbright);// true if all selected tes have same
+ bool selectionGetMediaType(U8 *media_type); // true if all selected tes have same
+ BOOL selectionGetClickAction(U8* action);
+
+ void selectionSetMaterial(U8 material);
+ void selectionSetImage(const LLUUID& imageid); // could be item or asset id
+ void selectionSetColor(const LLColor4 &color);
+ void selectionSetColorOnly(const LLColor4 &color); // Set only the RGB channels
+ void selectionSetAlphaOnly(const F32 alpha); // Set only the alpha channel
+ void selectionRevertColors();
+ BOOL selectionRevertTextures();
+ void selectionSetBumpmap( U8 bumpmap );
+ void selectionSetTexGen( U8 texgen );
+ void selectionSetShiny( U8 shiny );
+ void selectionSetFullbright( U8 fullbright );
+ void selectionSetMediaTypeAndURL( U8 media_type, const std::string& media_url );
+ void selectionSetClickAction(U8 action);
+
+ void setObjectPermissions(U8 perm_field, BOOL set, U32 perm_mask, BOOL override = FALSE);
+ void setObjectName(const LLString& name);
+ void setObjectDescription(const LLString& desc);
+ void setObjectCategory(const LLCategory& category);
+ void setObjectSaleInfo(const LLSaleInfo& sale_info);
+
+ void selectionTexScaleAutofit(F32 repeats_per_meter);
+ void selectionResetTexInfo(S32 te); // sets S,T to 1
+ void adjustTexturesByScale(BOOL send_to_sim, BOOL stretch);
+ BOOL getTESTAxes(const LLViewerObject* object, const U8 te, U32* s_axis, U32* t_axis); // Only for flex boxes
+
+ void selectionResetRotation(); // sets rotation quat to identity
+ void selectionRotateAroundZ(F32 degrees);
+ void sendGodlikeRequest(const LLString& request, const LLString& parameter);
+
+
+ // will make sure all selected object meet current criteria, or deselect them otherwise
+ void validateSelection();
+
+ // returns TRUE if it is possible to select this object
+ BOOL canSelectObject(LLViewerObject* object);
+
+ // Returns true if the viewer has information on all selected objects
+ BOOL selectGetAllRootsValid();
+ BOOL selectGetAllValid();
+
+ // returns TRUE if you can modify all selected objects.
+ BOOL selectGetRootsModify();
+ BOOL selectGetModify();
+
+ // returns TRUE if selected objects can be transferred.
+ BOOL selectGetRootsTransfer();
+
+ // returns TRUE if selected objects can be copied.
+ BOOL selectGetRootsCopy();
+
+ BOOL selectGetCreator(LLUUID& id, LLString& name); // true if all have same creator, returns id
+ BOOL selectGetOwner(LLUUID& id, LLString& name); // true if all objects have same owner, returns id
+ BOOL selectGetLastOwner(LLUUID& id, LLString& name); // true if all objects have same owner, returns id
+
+ // returns TRUE if all are the same. id is stuffed with
+ // the value found if available.
+ BOOL selectGetGroup(LLUUID& id);
+ BOOL selectGetPerm( U8 which_perm, U32* mask_on, U32* mask_off); // true if all have data, returns two masks, each indicating which bits are all on and all off
+ BOOL selectGetOwnershipCost(S32* cost); // sum of all ownership costs
+
+ BOOL selectIsGroupOwned(); // true if all root objects have valid data and are group owned.
+
+ // returns TRUE if all the nodes are valid. Accumulates
+ // permissions in the parameter.
+ BOOL selectGetPermissions(LLPermissions& perm);
+
+ // returns TRUE if anything is for sale. calculates the total
+ // price and stores that value in price.
+ BOOL selectIsForSale(S32& price);
+
+ // returns TRUE if all nodes are valid.
+ BOOL selectGetCategory(LLCategory& category);
+
+ // returns TRUE if all nodes are valid. method also stores an
+ // accumulated sale info.
+ BOOL selectGetSaleInfo(LLSaleInfo& sale_info);
+
+ // returns TRUE if all nodes are valid. fills passed in object
+ // with the aggregate permissions of the selection.
+ BOOL selectGetAggregatePermissions(LLAggregatePermissions& ag_perm);
+
+ // returns TRUE if all nodes are valid. fills passed in object
+ // with the aggregate permissions for texture inventory items of the selection.
+ BOOL selectGetAggregateTexturePermissions(LLAggregatePermissions& ag_perm);
+
+ // returns TRUE is any node is currenly worn as an attachment
+ BOOL selectionIsAttachment();
+
+ LLPermissions* findObjectPermissions(const LLViewerObject* object);
+
+ void selectDelete(); // Delete on simulator
+ void selectForceDelete(); // just delete, no into trash
+ void selectDuplicate(const LLVector3& offset, BOOL select_copy); // Duplicate on simulator
+ void repeatDuplicate();
+ void selectDuplicateOnRay(const LLVector3 &ray_start_region,
+ const LLVector3 &ray_end_region,
+ BOOL bypass_raycast,
+ BOOL ray_end_is_intersection,
+ const LLUUID &ray_target_id,
+ BOOL copy_centers,
+ BOOL copy_rotates,
+ BOOL select_copy);
+
+ void sendMultipleUpdate(U32 type); // Position, rotation, scale all in one
+ void sendOwner(const LLUUID& owner_id, const LLUUID& group_id, BOOL override = FALSE);
+ void sendGroup(const LLUUID& group_id);
+
+ // Category ID is the UUID of the folder you want to contain the purchase
+ // FIXME: sale_info check doesn't work for multiple object buy, which UI does not currently support
+ // sale info is used for verification only, if it doesn't match region info then sale is canceled
+ void sendBuy(const LLUUID& buyer_id, const LLUUID& category_id, const LLSaleInfo sale_info);
+ void sendAttach(U8 attachment_point);
+ void sendDetach();
+ void sendDropAttachment();
+ void sendLink();
+ void sendDelink();
+ void sendHinge(U8 type);
+ void sendDehinge();
+ void sendSelect();
+
+ void requestObjectPropertiesFamily(LLViewerObject* object); // asks sim for creator, permissions, resources, etc.
+ static void processObjectProperties(LLMessageSystem *mesgsys, void **user_data);
+ static void processObjectPropertiesFamily(LLMessageSystem *mesgsys, void **user_data);
+ static void processForceObjectSelect(LLMessageSystem* msg, void**);
+
+ void requestGodInfo();
+
+ LLVector3d getSelectionCenterGlobal() const { return mSelectionCenterGlobal; }
+ void updateSelectionCenter();
+ void updatePointAt();
+
+ // Internal list maintenance functions. TODO: Make these private!
+ void remove(LLDynamicArray<LLViewerObject*>& objects);
+ void remove(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
+ void removeAll();
+ void addAsIndividual(LLViewerObject* object, S32 te = SELECT_ALL_TES, BOOL undoable = TRUE);
+ void promoteSelectionToRoot();
+ void demoteSelectionToIndividuals();
+
+private:
+
+ ESelectType getSelectTypeForObject(LLViewerObject* object);
+ void addAsFamily(LLDynamicArray<LLViewerObject*>& objects, BOOL add_to_end = FALSE);
+ void generateSilhouette(LLSelectNode *nodep, const LLVector3& view_point);
+ void getSilhouetteExtents(LLSelectNode* nodep, const LLQuaternion& orientation, LLVector3& min_extents, LLVector3& max_extents);
+ // Send one message to each region containing an object on selection list.
+ void sendListToRegions( const LLString& message_name,
+ void (*pack_header)(void *user_data),
+ void (*pack_body)(LLSelectNode* node, void *user_data),
+ void *user_data,
+ ESendType send_type);
+ U32 undoRedo(std::deque<LLSelectAction*> &queue_src, std::deque<LLSelectAction*> &queue_dst, const LLUUID &object_id);
+
+ static void packAgentID( void *);
+ static void packAgentAndSessionID(void* user_data);
+ static void packAgentAndGroupID(void* user_data);
+ static void packAgentAndSessionAndGroupID(void* user_data);
+ static void packAgentIDAndSessionAndAttachment(void*);
+ static void packAgentGroupAndCatID(void*);
+ static void packDeleteHeader(void* userdata);
+ static void packDeRezHeader(void* user_data);
+ static void packObjectID( LLSelectNode* node, void *);
+ static void packObjectIDAsParam(LLSelectNode* node, void *);
+ static void packObjectIDAndRotation( LLSelectNode* node, void *);
+ static void packObjectLocalID(LLSelectNode* node, void *);
+ static void packObjectClickAction(LLSelectNode* node, void* data);
+ static void packObjectName(LLSelectNode* node, void* user_data);
+ static void packObjectDescription(LLSelectNode* node, void* user_data);
+ static void packObjectCategory(LLSelectNode* node, void* user_data);
+ static void packObjectSaleInfo(LLSelectNode* node, void* user_data);
+ static void packBuyObjectIDs(LLSelectNode* node, void* user_data);
+ static void packDuplicate( LLSelectNode* node, void *duplicate_data);
+ static void packDuplicateHeader(void*);
+ static void packDuplicateOnRayHead(void *user_data);
+ static void packPermissions(LLSelectNode* node, void *user_data);
+ static void packDeselect( LLSelectNode* node, void *user_data);
+ static void packMultipleUpdate(LLSelectNode* node, void *user_data);
+ static void packPhysics(LLSelectNode* node, void *user_data);
+ static void packShape(LLSelectNode* node, void *user_data);
+ static void packOwnerHead(void *user_data);
+ static void packHingeHead(void *user_data);
+ static void packPermissionsHead(void* user_data);
+ static void packGodlikeHead(void* user_data);
+ static void confirmDelete(S32 option, void* data);
+
+private:
+ LLPointer<LLViewerImage> mSilhouetteImagep;
+
+ LLSelectNodeList mSelectedObjects;
+
+ LLSelectNodeList mHoverObjects;
+
+ std::set<LLPointer<LLViewerObject> > mRectSelectedObjects;
+
+ LLSelectNodeList mGridObjects;
+ LLQuaternion mGridRotation;
+ LLVector3 mGridOrigin;
+ LLVector3 mGridScale;
+ EGridMode mGridMode;
+ BOOL mGridValid;
+
+ LLSelectNodeList mHighlightedObjects;
+
+ BOOL mTEMode; // render te
+ LLVector3d mSelectionCenterGlobal;
+ LLBBox mSelectionBBox;
+
+ LLVector3d mLastSentSelectionCenterGlobal;
+ BOOL mShowSelection; // do we send the selection center name value and do we animate this selection?
+ LLVector3d mLastCameraPos; // camera position from last generation of selection silhouette
+ BOOL mRenderSilhouettes; // do we render the silhouette
+ LLBBox mSavedSelectionBBox;
+
+ ESelectType mSelectType;
+
+ LLFrameTimer mEffectsTimer;
+ BOOL mForceSelection;
+
+ std::deque<LLSelectAction*> mUndoQueue;
+ std::deque<LLSelectAction*> mRedoQueue;
+
+ LLAnimPauseRequest mPauseRequest;
+};
+
+
+// Contains information about a selected object, particularly which
+// tes are selected.
+class LLSelectNode
+{
+public:
+ LLSelectNode(LLViewerObject* object, BOOL do_glow);
+ LLSelectNode(const LLSelectNode& nodep);
+ ~LLSelectNode();
+
+ void selectAllTEs(BOOL b);
+ void selectTE(S32 te_index, BOOL selected);
+ BOOL isTESelected(S32 te_index);
+ S32 getLastSelectedTE();
+ void renderOneSilhouette(const LLColor4 &color);
+ void setTransient(BOOL transient) { mTransient = transient; }
+ BOOL isTransient() { return mTransient; }
+ LLViewerObject *getObject();
+ //FIXME: invalidate stored textures and colors when # faces change
+ void saveColors();
+ void saveTextures(const std::vector<LLUUID>& textures);
+ void saveTextureScaleRatios();
+
+ BOOL allowOperationOnNode(PermissionBit op, U64 group_proxy_power) const;
+
+public:
+ BOOL mIndividualSelection; // For root objects and objects individually selected
+
+ BOOL mTransient;
+ BOOL mValid; // is extra information valid?
+ LLPermissions* mPermissions;
+ LLSaleInfo mSaleInfo;
+ LLAggregatePermissions mAggregatePerm;
+ LLAggregatePermissions mAggregateTexturePerm;
+ LLAggregatePermissions mAggregateTexturePermOwner;
+ LLString mName;
+ LLString mDescription;
+ LLCategory mCategory;
+ S16 mInventorySerial;
+ LLVector3 mSavedPositionLocal; // for interactively modifying object position
+ LLVector3d mSavedPositionGlobal; // for interactively modifying object position
+ LLVector3 mSavedScale; // for interactively modifying object scale
+ LLQuaternion mSavedRotation; // for interactively modifying object rotation
+ BOOL mDuplicated;
+ LLVector3d mDuplicatePos;
+ LLQuaternion mDuplicateRot;
+ LLUUID mItemID;
+ LLUUID mFolderID;
+ LLUUID mFromTaskID;
+ LLString mTouchName;
+ LLString mSitName;
+ std::vector<LLColor4> mSavedColors;
+ std::vector<LLUUID> mSavedTextures;
+ std::vector<LLVector3> mTextureScaleRatios;
+ std::vector<LLVector3> mSilhouetteVertices; // array of vertices to render silhouette of object
+ std::vector<LLVector3> mSilhouetteNormals; // array of normals to render silhouette of object
+ std::vector<S32> mSilhouetteSegments; // array of normals to render silhouette of object
+ BOOL mSilhouetteExists; // need to generate silhouette?
+
+protected:
+ LLPointer<LLViewerObject> mObject;
+ BOOL mTESelected[SELECT_MAX_TES];
+ S32 mLastTESelected;
+};
+
+extern LLSelectMgr* gSelectMgr;
+
+// Utilities
+void dialog_refresh_all(); // Update subscribers to the selection list
+
+#endif
diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp
new file mode 100644
index 0000000000..022f02c57e
--- /dev/null
+++ b/indra/newview/llsky.cpp
@@ -0,0 +1,427 @@
+/**
+ * @file llsky.cpp
+ * @brief IndraWorld sky class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Ideas:
+// -haze should be controlled by global query from sims
+// -need secondary optical effects on sun (flare)
+// -stars should be brought down from sims
+// -star intensity should be driven by global ambient level from sims,
+// so that eclipses, etc can be easily done.
+//
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsky.h"
+
+// linden library includes
+#include "llerror.h"
+#include "llmath.h"
+#include "math.h"
+#include "v4color.h"
+
+#include "llviewerobjectlist.h"
+#include "llviewerobject.h"
+#include "llviewercamera.h"
+#include "pipeline.h"
+#include "llagent.h"
+#include "lldrawpool.h"
+
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llcubemap.h"
+#include "llviewercontrol.h"
+
+extern LLPipeline gPipeline;
+
+F32 azimuth_from_vector(const LLVector3 &v);
+F32 elevation_from_vector(const LLVector3 &v);
+
+// ---------------- LLSky ----------------
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+LLSky::LLSky()
+{
+ // Set initial clear color to black
+ // Set fog color
+ mFogColor.mV[VRED] = mFogColor.mV[VGREEN] = mFogColor.mV[VBLUE] = 0.5f;
+ mFogColor.mV[VALPHA] = 0.0f;
+
+ mLightingGeneration = 0;
+ mUpdatedThisFrame = TRUE;
+ mOverrideSimSunPosition = FALSE;
+ mSunPhase = 0.f;
+}
+
+
+LLSky::~LLSky()
+{
+}
+
+void LLSky::cleanup()
+{
+ mVOSkyp = NULL;
+ mVOStarsp = NULL;
+ mVOGroundp = NULL;
+}
+
+void LLSky::destroyGL()
+{
+ if (!mVOSkyp.isNull() && mVOSkyp->getCubeMap())
+ {
+ mVOSkyp->cleanupGL();
+ }
+}
+
+void LLSky::restoreGL()
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->restoreGL();
+ }
+}
+
+void LLSky::setOverrideSun(BOOL override)
+{
+ if (!mOverrideSimSunPosition && override)
+ {
+ mLastSunDirection = getSunDirection();
+ }
+ else if (mOverrideSimSunPosition && !override)
+ {
+ setSunDirection(mLastSunDirection, LLVector3::zero);
+ }
+ mOverrideSimSunPosition = override;
+}
+
+void LLSky::setSunDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity)
+{
+ mVOSkyp->setSunDirection(sun_direction, sun_ang_velocity);
+}
+
+
+void LLSky::setSunTargetDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity)
+{
+ mSunTargDir = sun_direction;
+}
+
+
+LLVector3 LLSky::getSunDirection() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getToSun();
+ }
+ else
+ {
+ return LLVector3::z_axis;
+ }
+}
+
+
+LLVector3 LLSky::getMoonDirection() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getToMoon();
+ }
+ else
+ {
+ return LLVector3::z_axis;
+ }
+}
+
+
+LLColor4 LLSky::getSunDiffuseColor() const
+{
+ if (mVOSkyp)
+ {
+ return LLColor4(mVOSkyp->getSunDiffuseColor());
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4 LLSky::getMoonDiffuseColor() const
+{
+ if (mVOSkyp)
+ {
+ return LLColor4(mVOSkyp->getMoonDiffuseColor());
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4 LLSky::getTotalAmbientColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getTotalAmbientColor();
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+BOOL LLSky::sunUp() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->isSunUp();
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+LLColor4 LLSky::calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->calcInScatter(transp, point, exag);
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+LLColor4U LLSky::getFadeColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFadeColor();
+ }
+ else
+ {
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Public Methods
+//////////////////////////////////////////////////////////////////////
+
+
+void LLSky::init(const LLVector3 &sun_direction)
+{
+ mVOSkyp = (LLVOSky *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_SKY, gAgent.getRegion());
+ mVOSkyp->initSunDirection(sun_direction, LLVector3());
+ gPipeline.addObject((LLViewerObject *)mVOSkyp);
+
+ mVOStarsp = (LLVOStars *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_STARS, gAgent.getRegion());
+ gPipeline.addObject((LLViewerObject *)mVOStarsp);
+
+ mVOGroundp = (LLVOGround*)gObjectList.createObjectViewer(LLViewerObject::LL_VO_GROUND, gAgent.getRegion());
+ LLVOGround *groundp = mVOGroundp;
+ gPipeline.addObject((LLViewerObject *)groundp);
+
+ gSky.setFogRatio(gSavedSettings.getF32("RenderFogRatio"));
+
+ ////////////////////////////
+ //
+ // Legacy code, ignore
+ //
+ //
+
+ // Get the parameters.
+ mSunDefaultPosition = gSavedSettings.getVector3("SkySunDefaultPosition");
+
+
+ if (gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || mOverrideSimSunPosition)
+ {
+ setSunDirection(mSunDefaultPosition, LLVector3(0.f, 0.f, 0.f));
+ }
+ else
+ {
+ setSunDirection(sun_direction, LLVector3(0.f, 0.f, 0.f));
+ }
+
+ mUpdatedThisFrame = TRUE;
+}
+
+
+void LLSky::setCloudDensityAtAgent(F32 cloud_density)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setCloudDensity(cloud_density);
+ }
+}
+
+
+void LLSky::setWind(const LLVector3& average_wind)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setWind(average_wind);
+ }
+}
+
+
+void LLSky::propagateHeavenlyBodies(F32 dt)
+{
+ if (!mOverrideSimSunPosition)
+ {
+ LLVector3 curr_dir = getSunDirection();
+ LLVector3 diff = mSunTargDir - curr_dir;
+ const F32 dist = diff.normVec();
+ if (dist > 0)
+ {
+ const F32 step = llmin (dist, 0.00005f);
+ //const F32 step = min (dist, 0.0001);
+ diff *= step;
+ curr_dir += diff;
+ curr_dir.normVec();
+ if (mVOSkyp)
+ {
+ mVOSkyp->setSunDirection(curr_dir, LLVector3());
+ }
+ }
+ }
+}
+
+F32 LLSky::getSunPhase() const
+{
+ return mSunPhase;
+}
+
+void LLSky::setSunPhase(const F32 phase)
+{
+ mSunPhase = phase;
+}
+
+//////////////////////////////////////////////////////////////////////
+// Private Methods
+//////////////////////////////////////////////////////////////////////
+
+
+LLColor4 LLSky::getFogColor() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFogColor();
+ }
+
+ return LLColor4(1.f, 1.f, 1.f, 1.f);
+}
+
+
+void LLSky::updateFog(const F32 distance)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->updateFog(distance);
+ }
+}
+
+void LLSky::updateCull()
+{
+ /*if (mVOSkyp.notNull() && mVOSkyp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOSkyp->mDrawable);
+ }
+ else
+ {
+ llinfos << "No sky drawable!" << llendl;
+ }*/
+
+ if (mVOStarsp.notNull() && mVOStarsp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOStarsp->mDrawable);
+ }
+ else
+ {
+ llinfos << "No stars drawable!" << llendl;
+ }
+
+ /*if (mVOGroundp.notNull() && mVOGroundp->mDrawable.notNull())
+ {
+ gPipeline.markVisible(mVOGroundp->mDrawable);
+ }*/
+}
+
+void LLSky::updateSky()
+{
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY))
+ {
+ return;
+ }
+ if (mVOSkyp)
+ {
+ mVOSkyp->updateSky();
+ }
+ if (mVOStarsp)
+ {
+ if (mVOStarsp->mDrawable)
+ {
+ gPipeline.markRebuild(mVOStarsp->mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ }
+}
+
+
+void LLSky::setFogRatio(const F32 fog_ratio)
+{
+ if (mVOSkyp)
+ {
+ mVOSkyp->setFogRatio(fog_ratio);
+ }
+}
+
+
+F32 LLSky::getFogRatio() const
+{
+ if (mVOSkyp)
+ {
+ return mVOSkyp->getFogRatio();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+
+// Returns angle (DEGREES) between the horizontal plane and "v",
+// where the angle is negative when v.mV[VZ] < 0.0f
+F32 elevation_from_vector(const LLVector3 &v)
+{
+ F32 elevation = 0.0f;
+ F32 xy_component = (F32) sqrt(v.mV[VX] * v.mV[VX] + v.mV[VY] * v.mV[VY]);
+ if (xy_component != 0.0f)
+ {
+ elevation = RAD_TO_DEG * (F32) atan(v.mV[VZ]/xy_component);
+ }
+ else
+ {
+ if (v.mV[VZ] > 0.f)
+ {
+ elevation = 90.f;
+ }
+ else
+ {
+ elevation = -90.f;
+ }
+ }
+ return elevation;
+}
diff --git a/indra/newview/llsky.h b/indra/newview/llsky.h
new file mode 100644
index 0000000000..2413615085
--- /dev/null
+++ b/indra/newview/llsky.h
@@ -0,0 +1,101 @@
+/**
+ * @file llsky.h
+ * @brief It's, uh, the sky!
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSKY_H
+#define LL_LLSKY_H
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llvoground.h"
+
+const F32 NIGHTTIME_ELEVATION = -8.0f; // degrees
+const F32 NIGHTTIME_ELEVATION_COS = (F32)sin(NIGHTTIME_ELEVATION*DEG_TO_RAD);
+
+class LLViewerCamera;
+
+
+class LLSky
+{
+public:
+ LLSky();
+ ~LLSky();
+
+ void init(const LLVector3 &sun_direction);
+ void free();
+
+ void cleanup();
+
+ void setOverrideSun(BOOL override);
+ BOOL getOverrideSun() { return mOverrideSimSunPosition; }
+ void setSunDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity);
+ void setSunTargetDirection(const LLVector3 &sun_direction, const LLVector3 &sun_ang_velocity);
+
+
+ LLColor4 getFogColor() const;
+
+
+ void setCloudDensityAtAgent(F32 cloud_density);
+ void setWind(const LLVector3& wind);
+
+ void updateFog(const F32 distance);
+ void updateCull();
+ void updateSky();
+
+ void propagateHeavenlyBodies(F32 dt); // dt = seconds
+
+ S32 mLightingGeneration;
+ BOOL mUpdatedThisFrame;
+
+ void setFogRatio(const F32 fog_ratio); // Fog distance as fraction of cull distance.
+ F32 getFogRatio() const;
+ LLColor4U getFadeColor() const;
+
+ LLVector3 getSunDirection() const;
+ LLVector3 getMoonDirection() const;
+ LLColor4 getSunDiffuseColor() const;
+ LLColor4 getMoonDiffuseColor() const;
+ LLColor4 getTotalAmbientColor() const;
+ BOOL sunUp() const;
+ LLColor4 calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const;
+
+ F32 getSunPhase() const;
+ void setSunPhase(const F32 phase);
+
+ void destroyGL();
+ void restoreGL();
+
+public:
+ LLPointer<LLVOSky> mVOSkyp; // Pointer to the LLVOSky object (only one, ever!)
+ LLPointer<LLVOStars> mVOStarsp; // Pointer to the LLVOStars object (only one, ever!)
+ LLPointer<LLVOGround> mVOGroundp;
+
+ LLVector3 mSunTargDir;
+
+ // Legacy stuff
+ LLVector3 mSunDefaultPosition;
+
+protected:
+ BOOL mOverrideSimSunPosition;
+
+ F32 mSunPhase;
+ LLColor4 mFogColor; // Color to use for fog and haze
+
+ F32 mHaze; // a multiplier to scale the lighting
+ F32 mDomeRadius; // sky dome size
+
+ LLVector3 mLastSunDirection;
+};
+
+extern LLSky gSky;
+#endif
diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp
new file mode 100644
index 0000000000..8b5db02c85
--- /dev/null
+++ b/indra/newview/llspatialpartition.cpp
@@ -0,0 +1,2255 @@
+/**
+ * @file llspatialpartition.cpp
+ * @brief LLSpatialGroup class implementation and supporting functions
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llspatialpartition.h"
+
+#include "llglheaders.h"
+
+#include "llviewerobjectlist.h"
+#include "llvovolume.h"
+#include "llviewercamera.h"
+#include "llface.h"
+#include "viewer.h"
+
+#include "llcamera.h"
+#include "pipeline.h"
+
+static BOOL sIgnoreOcclusion = TRUE;
+static GLuint sBoxList = 0;
+
+const S32 SG_LOD_SWITCH_STAGGER = 4;
+const F32 SG_MAX_OBJ_RAD = 1.f;
+const F32 SG_OCCLUSION_FUDGE = 1.1f;
+const S32 SG_MOVE_PERIOD = 32;
+const S32 SG_LOD_PERIOD = 16;
+
+#define SG_DISCARD_TOLERANCE 0.25f
+
+#if LL_OCTREE_PARANOIA_CHECK
+#define assert_octree_valid(x) x->validate()
+#else
+#define assert_octree_valid(x)
+#endif
+
+static F32 sLastMaxTexPriority = 1.f;
+static F32 sCurMaxTexPriority = 1.f;
+
+//static counter for frame to switch LOD on
+S32 LLSpatialGroup::sLODSeed = 0;
+
+void sg_assert(BOOL expr)
+{
+#if LL_OCTREE_PARANOIA_CHECK
+ if (!expr)
+ {
+ llerrs << "Octree invalid!" << llendl;
+ }
+#endif
+}
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+void validate_drawable(LLDrawable* drawablep)
+{
+ F64 rad = drawablep->getBinRadius();
+ const LLVector3* ext = drawablep->getSpatialExtents();
+
+ if (rad < 0 || rad > 4096 ||
+ (ext[1]-ext[0]).magVec() > 4096)
+ {
+ llwarns << "Invalid drawable found in octree." << llendl;
+ }
+}
+#else
+#define validate_drawable(x)
+#endif
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group);
+
+BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size)
+{
+ float fAWdU[3];
+ LLVector3 dir;
+ LLVector3 diff;
+
+ for (U32 i = 0; i < 3; i++)
+ {
+ dir.mV[i] = 0.5f * (end.mV[i] - start.mV[i]);
+ diff.mV[i] = (0.5f * (end.mV[i] + start.mV[i])) - center.mV[i];
+ fAWdU[i] = fabsf(dir.mV[i]);
+ if(fabsf(diff.mV[i])>size.mV[i] + fAWdU[i]) return false;
+ }
+
+ float f;
+ f = dir.mV[1] * diff.mV[2] - dir.mV[2] * diff.mV[1]; if(fabsf(f)>size.mV[1]*fAWdU[2] + size.mV[2]*fAWdU[1]) return false;
+ f = dir.mV[2] * diff.mV[0] - dir.mV[0] * diff.mV[2]; if(fabsf(f)>size.mV[0]*fAWdU[2] + size.mV[2]*fAWdU[0]) return false;
+ f = dir.mV[0] * diff.mV[1] - dir.mV[1] * diff.mV[0]; if(fabsf(f)>size.mV[0]*fAWdU[1] + size.mV[1]*fAWdU[0]) return false;
+
+ return true;
+}
+
+//returns:
+// 0 if sphere and AABB are not intersecting
+// 1 if they are
+// 2 if AABB is entirely inside sphere
+
+S32 LLSphereAABB(const LLVector3& center, const LLVector3& size, const LLVector3& pos, const F32 &rad)
+{
+ S32 ret = 2;
+
+ LLVector3 min = center - size;
+ LLVector3 max = center + size;
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (min.mV[i] > pos.mV[i] + rad ||
+ max.mV[i] < pos.mV[i] - rad)
+ { //totally outside
+ return 0;
+ }
+
+ if (min.mV[i] < pos.mV[i] - rad ||
+ max.mV[i] > pos.mV[i] + rad)
+ { //intersecting
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+LLSpatialGroup::~LLSpatialGroup()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!safeToDelete())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
+#else
+ llerrs << "Spatial Group deleted while being tracked " << ((void*) mState) << llendl;
+#endif
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < mSpatialPartition->mOccludedList.size(); i++)
+ {
+ if (mSpatialPartition->mOccludedList[i] == this)
+ {
+ llerrs << "Spatial Group deleted while being tracked STATE ERROR " << ((void*) mState) << llendl;
+ }
+ }
+#endif
+}
+
+BOOL LLSpatialGroup::safeToDelete()
+{
+ return gQuit || !isState(IN_QUEUE | ACTIVE_OCCLUSION | RESHADOW_QUEUE);
+}
+
+class LLRelightPainter : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLVector3 mOrigin, mDir;
+ F32 mRadius;
+
+ LLRelightPainter(LLVector3 origin, LLVector3 dir, F32 radius)
+ : mOrigin(origin), mDir(dir), mRadius(radius)
+ { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup::OctreeNode* node = (LLSpatialGroup::OctreeNode*) n;
+
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+ group->setState(LLSpatialGroup::RESHADOW);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ const LLSpatialGroup::OctreeNode* child = node->getChild(i);
+ LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
+
+ LLVector3 res;
+
+ LLVector3 center, size;
+
+ center = group->mBounds[0];
+ size = group->mBounds[1];
+
+ if (child->isInside(LLVector3d(mOrigin)) || LLRayAABB(center, size, mOrigin, mDir, res, mRadius))
+ {
+ traverse(child);
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+
+};
+
+BOOL LLSpatialGroup::isVisible()
+{
+ if (sIgnoreOcclusion)
+ {
+ return !isState(CULLED);
+ }
+ else
+ {
+ return !isState(CULLED | OCCLUDED);
+ }
+}
+
+void LLSpatialGroup::validate()
+{
+#if LL_OCTREE_PARANOIA_CHECK
+
+ sg_assert(!isState(DIRTY));
+
+ LLVector3 myMin = mBounds[0] - mBounds[1];
+ LLVector3 myMax = mBounds[0] + mBounds[1];
+
+ for (U32 i = 0; i < mOctreeNode->getChildCount(); ++i)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+
+ group->validate();
+
+ //ensure all children are enclosed in this node
+ LLVector3 center = group->mBounds[0];
+ LLVector3 size = group->mBounds[1];
+
+ LLVector3 min = center - size;
+ LLVector3 max = center + size;
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ sg_assert(min.mV[j] >= myMin.mV[j]-0.02f);
+ sg_assert(max.mV[j] <= myMax.mV[j]+0.02f);
+ }
+ }
+
+#endif
+}
+
+BOOL LLSpatialGroup::updateInGroup(LLDrawable *drawablep, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->updateSpatialExtents();
+ validate_drawable(drawablep);
+
+ if (mOctreeNode->isInside(drawablep) && mOctreeNode->contains(drawablep))
+ {
+ unbound();
+ setState(OBJECT_DIRTY);
+ validate_drawable(drawablep);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLSpatialGroup::addObject(LLDrawable *drawablep, BOOL add_all, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (!from_octree)
+ {
+ mOctreeNode->insert(drawablep);
+ }
+ else
+ {
+ drawablep->setSpatialGroup(this, 0);
+ validate_drawable(drawablep);
+ }
+
+ return TRUE;
+}
+
+BOOL LLSpatialGroup::boundObjects(BOOL empty, LLVector3& minOut, LLVector3& maxOut)
+{
+ const OctreeState* node = mOctreeNode->getOctState();
+
+ if (node->getData().empty())
+ { //don't do anything if there are no objects
+ if (empty && mOctreeNode->getParent())
+ { //only root is allowed to be empty
+ OCT_ERRS << "Empty leaf found in octree." << llendl;
+ }
+ return FALSE;
+ }
+
+ LLVector3& newMin = mObjectExtents[0];
+ LLVector3& newMax = mObjectExtents[1];
+
+ if (isState(OBJECT_DIRTY))
+ { //calculate new bounding box
+ clearState(OBJECT_DIRTY);
+
+ //initialize bounding box to first element
+ OctreeState::const_element_iter i = node->getData().begin();
+ LLDrawable* drawablep = *i;
+ const LLVector3* minMax = drawablep->getSpatialExtents();
+
+ newMin.setVec(minMax[0]);
+ newMax.setVec(minMax[1]);
+
+ for (++i; i != node->getData().end(); ++i)
+ {
+ drawablep = *i;
+ minMax = drawablep->getSpatialExtents();
+
+ //bin up the object
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (minMax[0].mV[i] < newMin.mV[i])
+ {
+ newMin.mV[i] = minMax[0].mV[i];
+ }
+ if (minMax[1].mV[i] > newMax.mV[i])
+ {
+ newMax.mV[i] = minMax[1].mV[i];
+ }
+ }
+ }
+
+ mObjectBounds[0] = (newMin + newMax) * 0.5f;
+ mObjectBounds[1] = (newMax - newMin) * 0.5f;
+ }
+
+ if (empty)
+ {
+ minOut = newMin;
+ maxOut = newMax;
+ }
+ else
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (newMin.mV[i] < minOut.mV[i])
+ {
+ minOut.mV[i] = newMin.mV[i];
+ }
+ if (newMax.mV[i] > maxOut.mV[i])
+ {
+ maxOut.mV[i] = newMax.mV[i];
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSpatialGroup::unbound()
+{
+ if (isState(DIRTY))
+ {
+ return;
+ }
+
+ setState(DIRTY);
+
+ //all the parent nodes need to rebound this child
+ if (mOctreeNode)
+ {
+ OctreeNode* parent = (OctreeNode*) mOctreeNode->getParent();
+ while (parent != NULL)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) parent->getListener(0);
+ if (group->isState(DIRTY))
+ {
+ return;
+ }
+
+ group->setState(DIRTY);
+ parent = (OctreeNode*) parent->getParent();
+ }
+ }
+}
+
+BOOL LLSpatialGroup::removeObject(LLDrawable *drawablep, BOOL from_octree)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ unbound();
+ if (!from_octree)
+ {
+ if (!mOctreeNode->remove(drawablep))
+ {
+ OCT_ERRS << "Could not remove drawable from spatial group" << llendl;
+ }
+ }
+ else
+ {
+ drawablep->setSpatialGroup(NULL, -1);
+ }
+ return TRUE;
+}
+
+void LLSpatialGroup::shift(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLVector3d offsetd(offset);
+ mOctreeNode->setCenter(mOctreeNode->getCenter()+offsetd);
+ mOctreeNode->updateMinMax();
+ mBounds[0] += offset;
+ mExtents[0] += offset;
+ mExtents[1] += offset;
+ mObjectBounds[0] += offset;
+ mObjectExtents[0] += offset;
+ mObjectExtents[1] += offset;
+}
+
+class LLSpatialSetState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialSetState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { ((LLSpatialGroup*) branch->getListener(0))->setState(mState); }
+};
+
+class LLSpatialSetStateDiff : public LLSpatialSetState
+{
+public:
+ LLSpatialSetStateDiff(U32 state) : LLSpatialSetState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+void LLSpatialGroup::setState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialSetStateDiff setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialSetState setter(state);
+ setter.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState |= state;
+ }
+}
+
+class LLSpatialClearState : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ U32 mState;
+ LLSpatialClearState(U32 state) : mState(state) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { ((LLSpatialGroup*) branch->getListener(0))->clearState(mState); }
+};
+
+class LLSpatialClearStateDiff : public LLSpatialClearState
+{
+public:
+ LLSpatialClearStateDiff(U32 state) : LLSpatialClearState(state) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (!group->isState(mState))
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ }
+};
+
+
+void LLSpatialGroup::clearState(U32 state, S32 mode)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (mode > STATE_MODE_SINGLE)
+ {
+ if (mode == STATE_MODE_DIFF)
+ {
+ LLSpatialClearStateDiff clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ else
+ {
+ LLSpatialClearState clearer(state);
+ clearer.traverse(mOctreeNode);
+ }
+ }
+ else
+ {
+ mState &= ~state;
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ if (state & LLSpatialGroup::ACTIVE_OCCLUSION)
+ {
+ LLSpatialPartition* part = mSpatialPartition;
+ for (U32 i = 0; i < part->mOccludedList.size(); i++)
+ {
+ if (part->mOccludedList[i] == this)
+ {
+ llerrs << "LLSpatialGroup state error: " << mState << llendl;
+ }
+ }
+ }
+#endif
+}
+
+//======================================
+// Octree Listener Implementation
+//======================================
+
+LLSpatialGroup::LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part)
+: mState(0), mOctreeNode(node), mSpatialPartition(part)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ sg_assert(mOctreeNode->getListenerCount() == 0);
+ mOctreeNode->addListener(this);
+ setState(DIRTY);
+
+ mBounds[0] = LLVector3(node->getCenter());
+ mBounds[1] = LLVector3(node->getSize());
+
+ sLODSeed = (sLODSeed+1)%SG_LOD_PERIOD;
+ mLODHash = sLODSeed;
+}
+
+BOOL LLSpatialGroup::changeLOD()
+{
+ return LLDrawable::getCurrentFrame()%SG_LOD_PERIOD == mLODHash;
+}
+
+void LLSpatialGroup::handleInsertion(const TreeNode* node, LLDrawable* drawable)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ addObject(drawable, FALSE, TRUE);
+ unbound();
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleRemoval(const TreeNode* node, LLDrawable* drawable)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ removeObject(drawable, TRUE);
+ setState(OBJECT_DIRTY);
+}
+
+void LLSpatialGroup::handleDestruction(const TreeNode* node)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ if (mOctreeNode)
+ {
+ OctreeState* state = mOctreeNode->getOctState();
+ for (OctreeState::element_iter i = state->getData().begin(); i != state->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (!drawable->isDead())
+ {
+ drawable->setSpatialGroup(NULL, -1);
+ }
+ }
+ }
+
+ if (safeToDelete())
+ {
+ delete this;
+ }
+ else
+ {
+ setState(DEAD);
+ mOctreeNode = NULL;
+ }
+}
+
+void LLSpatialGroup::handleStateChange(const TreeNode* node)
+{
+ //drop bounding box upon state change
+ if (mOctreeNode != node)
+ {
+ mOctreeNode = (OctreeNode*) node;
+ }
+ unbound();
+}
+
+void LLSpatialGroup::handleChildAddition(const OctreeNode* parent, OctreeNode* child)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ if (child->getListenerCount() == 0)
+ {
+ (new LLSpatialGroup(child, mSpatialPartition))->setState(mState & SG_STATE_INHERIT_MASK);
+ }
+ else
+ {
+ OCT_ERRS << "LLSpatialGroup redundancy detected." << llendl;
+ }
+
+ unbound();
+}
+
+void LLSpatialGroup::handleChildRemoval(const OctreeNode* parent, const OctreeNode* child)
+{
+ unbound();
+}
+
+BOOL LLSpatialGroup::rebound()
+{
+ if (!isState(DIRTY))
+ { //return TRUE if we're not empty
+ return TRUE;
+ }
+
+ LLVector3 oldBounds[2];
+
+ if (isState(QUERY_OUT))
+ { //a query has been issued, if our bounding box changes significantly
+ //we need to discard the issued query
+ oldBounds[0] = mBounds[0];
+ oldBounds[1] = mBounds[1];
+ }
+
+ if (mOctreeNode->getChildCount() == 1 && mOctreeNode->getElementCount() == 0)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->rebound();
+
+ //copy single child's bounding box
+ mBounds[0] = group->mBounds[0];
+ mBounds[1] = group->mBounds[1];
+ mExtents[0] = group->mExtents[0];
+ mExtents[1] = group->mExtents[1];
+
+ group->setState(SKIP_FRUSTUM_CHECK);
+ }
+ else if (mOctreeNode->hasLeafState())
+ { //copy object bounding box if this is a leaf
+ boundObjects(TRUE, mExtents[0], mExtents[1]);
+ mBounds[0] = mObjectBounds[0];
+ mBounds[1] = mObjectBounds[1];
+ }
+ else
+ {
+ LLVector3& newMin = mExtents[0];
+ LLVector3& newMax = mExtents[1];
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctreeNode->getChild(0)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ //initialize to first child
+ newMin = group->mExtents[0];
+ newMax = group->mExtents[1];
+
+ //first, rebound children
+ for (U32 i = 1; i < mOctreeNode->getChildCount(); i++)
+ {
+ group = (LLSpatialGroup*) mOctreeNode->getChild(i)->getListener(0);
+ group->clearState(SKIP_FRUSTUM_CHECK);
+ group->rebound();
+ const LLVector3& max = group->mExtents[1];
+ const LLVector3& min = group->mExtents[0];
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (max.mV[j] > newMax.mV[j])
+ {
+ newMax.mV[j] = max.mV[j];
+ }
+ if (min.mV[j] < newMin.mV[j])
+ {
+ newMin.mV[j] = min.mV[j];
+ }
+ }
+ }
+
+ boundObjects(FALSE, newMin, newMax);
+
+ mBounds[0] = (newMin + newMax)*0.5f;
+ mBounds[1] = (newMax - newMin)*0.5f;
+ }
+
+ if (isState(QUERY_OUT))
+ {
+ for (U32 i = 0; i < 3 && !isState(DISCARD_QUERY); i++)
+ {
+ if (fabsf(mBounds[0].mV[i]-oldBounds[0].mV[i]) > SG_DISCARD_TOLERANCE ||
+ fabsf(mBounds[1].mV[i]-oldBounds[1].mV[i]) > SG_DISCARD_TOLERANCE)
+ { //bounding box changed significantly, discard last issued
+ //occlusion query
+ setState(DISCARD_QUERY);
+ }
+ }
+ }
+
+ clearState(DIRTY);
+
+ return TRUE;
+}
+
+//==============================================
+
+LLSpatialPartition::LLSpatialPartition()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ mOctree = new LLSpatialGroup::OctreeNode(LLVector3d(0,0,0),
+ LLVector3d(1,1,1),
+ new LLSpatialGroup::OctreeRoot(), NULL);
+ new LLSpatialGroup(mOctree, this);
+}
+
+
+LLSpatialPartition::~LLSpatialPartition()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ delete mOctree;
+ mOctree = NULL;
+}
+
+
+LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+
+ if (drawablep->getPositionGroup().magVecSquared() > MAX_MAG)
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "LLSpatialPartition::put Object out of range!" << llendl;
+ llinfos << drawablep->getPositionGroup() << llendl;
+
+ if (drawablep->getVObj())
+ {
+ llwarns << "Dumping debugging info: " << llendl;
+ drawablep->getVObj()->dump();
+ }
+#endif
+ return NULL;
+ }
+
+ drawablep->updateSpatialExtents();
+ validate_drawable(drawablep);
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+
+ assert_octree_valid(mOctree);
+ mOctree->insert(drawablep);
+ assert_octree_valid(mOctree);
+
+ LLSpatialGroup::OctreeNode* node = mOctree->getNodeAt(drawablep);
+
+ return (LLSpatialGroup*) node->getListener(0);
+}
+
+BOOL LLSpatialPartition::remove(LLDrawable *drawablep, LLSpatialGroup *curp)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ drawablep->setSpatialGroup(NULL, -1);
+
+ if (!curp->removeObject(drawablep))
+ {
+ OCT_ERRS << "Failed to remove drawable from octree!" << llendl;
+ }
+
+ assert_octree_valid(mOctree);
+
+ return TRUE;
+}
+
+void LLSpatialPartition::move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
+
+ if (curp && curp->mSpatialPartition != this)
+ {
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp->mSpatialPartition->remove(drawablep, curp))
+ {
+ put(drawablep);
+ return;
+ }
+ else
+ {
+ OCT_ERRS << "Drawable lost between spatial partitions on outbound transition." << llendl;
+ }
+ }
+
+ if (curp && curp->updateInGroup(drawablep, immediate))
+ {
+ // Already updated, don't need to do anything
+ assert_octree_valid(mOctree);
+ return;
+ }
+
+ //keep drawable from being garbage collected
+ LLPointer<LLDrawable> ptr = drawablep;
+ if (curp && !remove(drawablep, curp))
+ {
+ OCT_ERRS << "Move couldn't find existing spatial group!" << llendl;
+ }
+
+ put(drawablep);
+}
+
+class LLSpatialShift : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLSpatialShift(LLVector3 offset) : mOffset(offset) { }
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ ((LLSpatialGroup*) branch->getListener(0))->shift(mOffset);
+ }
+
+ LLVector3 mOffset;
+};
+
+void LLSpatialPartition::shift(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ llinfos << "Shifting octree: " << offset << llendl;
+ LLSpatialShift shifter(offset);
+ shifter.traverse(mOctree);
+}
+
+BOOL LLSpatialPartition::checkOcclusion(LLSpatialGroup* group, LLCamera* camera)
+{
+ if (sIgnoreOcclusion)
+ {
+ return FALSE;
+ }
+
+ if (!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION | LLSpatialGroup::OCCLUDED) &&
+ !earlyFail(camera, group))
+ {
+ group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ mQueryQueue.push(group);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+class LLOctreeCull : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeCull(LLCamera* camera)
+ : mCamera(camera), mRes(0) { }
+
+ virtual bool earlyFail(const LLSpatialGroup* group)
+ {
+ if (mRes && //never occlusion cull the root node
+ !sIgnoreOcclusion && //never occlusion cull selection
+ group->isState(LLSpatialGroup::OCCLUDED))
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (earlyFail(group))
+ {
+ return;
+ }
+
+ if (mRes == 2 ||
+ (mRes && group->isState(LLSpatialGroup::SKIP_FRUSTUM_CHECK)))
+ { //fully in, just add everything
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ mRes = mCamera->AABBInFrustum(group->mBounds[0], group->mBounds[1]);
+
+ if (mRes)
+ { //at least partially in, run on down
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ lateFail(group);
+ }
+ mRes = 0;
+ }
+ }
+
+ virtual void lateFail(LLSpatialGroup* group)
+ {
+ if (!group->isState(LLSpatialGroup::CULLED))
+ { //totally culled, so are all its children
+ group->setState(LLSpatialGroup::CULLED, LLSpatialGroup::STATE_MODE_DIFF);
+ }
+ }
+
+ virtual bool checkObjects(const LLSpatialGroup::OctreeState* branch, const LLSpatialGroup* group)
+ {
+
+ if (branch->getElementCount() == 0) //no elements
+ {
+ return false;
+ }
+ else if (branch->getChildCount() == 0) //leaf state, already checked tightest bounding box
+ {
+ return true;
+ }
+ else if (mRes == 1 && !mCamera->AABBInFrustum(group->mObjectBounds[0], group->mObjectBounds[1])) //no objects in frustum
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ virtual void preprocess(LLSpatialGroup* group)
+ {
+ if (group->isState(LLSpatialGroup::CULLED))
+ { //this is the first frame this node is visible
+ group->clearState(LLSpatialGroup::CULLED);
+ if (group->mOctreeNode->hasLeafState())
+ { //if it's a leaf, force it onto the active occlusion list to prevent
+ //massive frame stutters
+ group->mSpatialPartition->checkOcclusion(group, mCamera);
+ }
+ }
+ }
+
+ virtual void processDrawable(LLDrawable* drawable)
+ {
+ if (!drawable->isDead())
+ {
+ const LLVector3* extents = drawable->getSpatialExtents();
+
+ F32 objRad = drawable->getRadius();
+ objRad *= objRad;
+ F32 distSqr = ((extents[0]+extents[1])*0.5f - mCamera->getOrigin()).magVecSquared();
+ if (objRad/distSqr > SG_MIN_DIST_RATIO)
+ {
+ gPipeline.markNotCulled(drawable, *mCamera);
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ preprocess(group);
+
+ if (checkObjects(branch, group))
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ processDrawable(*i);
+ }
+ }
+ }
+
+ LLCamera *mCamera;
+ S32 mRes;
+};
+
+
+class LLOctreeSelect : public LLOctreeCull
+{
+public:
+ LLOctreeSelect(LLCamera* camera, std::vector<LLDrawable*>* results)
+ : LLOctreeCull(camera), mResults(results) { }
+
+ virtual bool earlyFail(const LLSpatialGroup* group) { return false; }
+ virtual void lateFail(LLSpatialGroup* group) { }
+ virtual void preprocess(LLSpatialGroup* group) { }
+
+ virtual void processDrawable(LLDrawable* drawable)
+ {
+ if (!drawable->isDead())
+ {
+ if (drawable->isSpatialBridge())
+ {
+ drawable->setVisible(*mCamera, mResults, TRUE);
+ }
+ else
+ {
+ mResults->push_back(drawable);
+ }
+ }
+ }
+
+ std::vector<LLDrawable*>* mResults;
+};
+
+
+void drawBox(const LLVector3& c, const LLVector3& r)
+{
+ glPushMatrix();
+ glTranslatef(c.mV[0], c.mV[1], c.mV[2]);
+ glScalef(r.mV[0], r.mV[1], r.mV[2]);
+ glCallList(sBoxList);
+ glPopMatrix();
+}
+
+void genBoxList()
+{
+ if (sBoxList != 0)
+ {
+ return;
+ }
+
+ sBoxList = glGenLists(1);
+ glNewList(sBoxList, GL_COMPILE);
+
+ LLVector3 c,r;
+ c = LLVector3(0,0,0);
+ r = LLVector3(1,1,1);
+
+ glBegin(GL_TRIANGLE_STRIP);
+ //left front
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ //right front
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ //right back
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ //left back
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ //left front
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ glEnd();
+
+ //bottom
+ glBegin(GL_TRIANGLE_STRIP);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,-1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,-1))).mV);
+ glEnd();
+
+ //top
+ glBegin(GL_TRIANGLE_STRIP);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(1,-1,1))).mV);
+ glVertex3fv((c+r.scaledVec(LLVector3(-1,-1,1))).mV);
+ glEnd();
+
+ glEndList();
+}
+
+void LLSpatialPartition::restoreGL()
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ mOcclusionQueries.clear();
+ sBoxList = 0;
+
+ //generate query ids
+ while (mOcclusionQueries.size() < mOccludedList.size())
+ {
+ GLuint id;
+ glGenQueriesARB(1, &id);
+ mOcclusionQueries.push_back(id);
+ }
+
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ { //previously issued queries are now invalid
+ mOccludedList[i]->setState(LLSpatialGroup::DISCARD_QUERY);
+ }
+
+ genBoxList();
+}
+
+S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* results, BOOL for_select)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0);
+ group->rebound();
+ }
+
+ if (for_select)
+ {
+ LLOctreeSelect selecter(&camera, results);
+ selecter.traverse(mOctree);
+ }
+ else
+ {
+ LLOctreeCull culler(&camera);
+ culler.traverse(mOctree);
+ }
+
+ sIgnoreOcclusion = !(gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery);
+ return 0;
+}
+
+class LLOctreeClearOccludedNotActive : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeClearOccludedNotActive() { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+ if ((!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION)) //|| group->isState(LLSpatialGroup::QUERY_PENDING)
+ || group->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION))
+ { //the children are all occluded or culled as well
+ group->clearState(LLSpatialGroup::OCCLUDED);
+ for (U32 i = 0; i < group->mOctreeNode->getChildCount(); i++)
+ {
+ traverse(group->mOctreeNode->getChild(i));
+ }
+ }
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+};
+
+class LLQueueNonCulled : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ std::queue<LLSpatialGroup*>* mQueue;
+ LLQueueNonCulled(std::queue<LLSpatialGroup*> *queue) : mQueue(queue) { }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+ if (group->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED))
+ { //the children are all occluded or culled as well
+ return;
+ }
+
+ if (!group->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mQueue->push(group);
+ }
+
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch) { }
+};
+
+BOOL earlyFail(LLCamera* camera, LLSpatialGroup* group)
+{
+ LLVector3 c = group->mBounds[0];
+ LLVector3 r = group->mBounds[1] * (SG_OCCLUSION_FUDGE*2.f) + LLVector3(0.01f,0.01f,0.01f);
+
+ if (group->isState(LLSpatialGroup::CULLED) || !camera->AABBInFrustum(c, r))
+ {
+ return TRUE;
+ }
+
+ LLVector3 e = camera->getOrigin();
+
+ LLVector3 min = c - r;
+ LLVector3 max = c + r;
+
+ for (U32 j = 0; j < 3; j++)
+ {
+ if (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j])
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLSpatialPartition::processOcclusion(LLCamera* camera)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+ LLSpatialGroup* rootGroup = (LLSpatialGroup*) mOctree->getListener(0);
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND);
+ rootGroup->rebound();
+ }
+
+ //update potentials
+ if (!rootGroup->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ rootGroup->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(rootGroup);
+ }
+
+ const U32 MAX_PULLED = 32;
+ const U32 MAX_PUSHED = mOcclusionQueue.size();
+ U32 count = 0;
+ U32 pcount = 0;
+
+ while (pcount < MAX_PUSHED && count < MAX_PULLED && !mOcclusionQueue.empty())
+ {
+ LLFastTimer t(LLFastTimer::FTM_OCCLUSION);
+
+ LLSpatialGroup* group = mOcclusionQueue.front();
+ if (!group->isState(LLSpatialGroup::IN_QUEUE))
+ {
+ OCT_ERRS << "Spatial Group State Error. Group in queue not tagged as such." << llendl;
+ }
+
+ mOcclusionQueue.pop();
+ group->clearState(LLSpatialGroup::IN_QUEUE);
+
+ if (group->isDead())
+ {
+ if (group->safeToDelete())
+ {
+ delete group;
+ }
+ continue;
+ }
+
+ if (group->isState(LLSpatialGroup::CULLED | LLSpatialGroup::OCCLUDED))
+ { //already culled, skip it
+ continue;
+ }
+
+ //before we process, enqueue this group's children
+ for (U32 i = 0; i < group->mOctreeNode->getChildCount(); i++)
+ {
+ LLSpatialGroup* child = (LLSpatialGroup*) group->mOctreeNode->getChild(i)->getListener(0);
+
+ if (!child->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED)
+ && !child->isState(LLSpatialGroup::IN_QUEUE | LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ child->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(child);
+ }
+ }
+
+ /*if (group->isState(LLSpatialGroup::QUERY_PENDING))
+ { //already on the pending group, put it back
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(group);
+ pcount++;
+ continue;
+ }*/
+
+ if (earlyFail(camera, group))
+ {
+ sg_assert(!group->isState(LLSpatialGroup::OCCLUDED));
+ group->setState(LLSpatialGroup::IN_QUEUE);
+ mOcclusionQueue.push(group);
+ pcount++;
+ continue;
+ }
+
+ //add to pending queue
+ if (!group->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 i = 0; i < mOccludedList.size(); ++i)
+ {
+ sg_assert(mOccludedList[i] != group);
+ }
+#endif
+ //group->setState(LLSpatialGroup::QUERY_PENDING);
+ group->setState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ mQueryQueue.push(group);
+ count++;
+ }
+ }
+
+ //read back results from last frame
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+ LLFastTimer t(LLFastTimer::FTM_OCCLUSION_READBACK);
+
+ if (mOccludedList[i]->isDead() || !mOccludedList[i]->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ continue;
+ }
+ GLuint res = 0;
+
+ if (mOccludedList[i]->isState(LLSpatialGroup::EARLY_FAIL | LLSpatialGroup::DISCARD_QUERY) ||
+ !mOccludedList[i]->isState(LLSpatialGroup::QUERY_OUT))
+ {
+ mOccludedList[i]->clearState(LLSpatialGroup::EARLY_FAIL);
+ mOccludedList[i]->clearState(LLSpatialGroup::DISCARD_QUERY);
+ res = 1;
+ }
+ else
+ {
+ glGetQueryObjectuivARB(mOcclusionQueries[i], GL_QUERY_RESULT_ARB, &res);
+ stop_glerror();
+ }
+
+ if (res) //NOT OCCLUDED
+ {
+ if (mOccludedList[i]->isState(LLSpatialGroup::OCCLUDED))
+ { //this node was occluded last frame
+ LLSpatialGroup::OctreeNode* node = mOccludedList[i]->mOctreeNode;
+ //add any immediate children to the queue that are not already there
+ for (U32 j = 0; j < node->getChildCount(); j++)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getChild(j)->getListener(0);
+ checkOcclusion(group, camera);
+ }
+ }
+
+ //clear occlusion status for everything not on the active list
+ LLOctreeClearOccludedNotActive clear_occluded;
+ mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+ mOccludedList[i]->clearState(LLSpatialGroup::OCCLUDED);
+ mOccludedList[i]->clearState(LLSpatialGroup::OCCLUDING);
+ clear_occluded.traverse(mOccludedList[i]->mOctreeNode);
+ }
+ else
+ { //OCCLUDED
+ if (mOccludedList[i]->isState(LLSpatialGroup::OCCLUDING))
+ {
+ if (!mOccludedList[i]->isState(LLSpatialGroup::OCCLUDED))
+ {
+ LLSpatialGroup::OctreeNode* oct_parent = (LLSpatialGroup::OctreeNode*) mOccludedList[i]->mOctreeNode->getParent();
+ if (oct_parent)
+ {
+ LLSpatialGroup* parent = (LLSpatialGroup*) oct_parent->getListener(0);
+
+ if (checkOcclusion(parent, camera))
+ { //force a guess on the parent and siblings
+
+ for (U32 i = 0; i < parent->mOctreeNode->getChildCount(); i++)
+ {
+ LLSpatialGroup* child = (LLSpatialGroup*) parent->mOctreeNode->getChild(i)->getListener(0);
+ checkOcclusion(child, camera);
+ }
+ }
+ }
+ }
+ mOccludedList[i]->setState(LLSpatialGroup::OCCLUDED, LLSpatialGroup::STATE_MODE_DIFF);
+ }
+ else
+ {
+ //take children off the active list
+ mOccludedList[i]->setState(LLSpatialGroup::DEACTIVATE_OCCLUSION, LLSpatialGroup::STATE_MODE_DIFF);
+
+ //keep this node on the active list
+ mOccludedList[i]->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+
+ //this node is a top level occluder
+ mOccludedList[i]->setState(LLSpatialGroup::OCCLUDING);
+ }
+ }
+
+ mOccludedList[i]->clearState(LLSpatialGroup::QUERY_OUT);
+ }
+
+ //remove non-occluded groups from occluded list
+ for (U32 i = 0; i < mOccludedList.size(); )
+ {
+ if (mOccludedList[i]->isDead() || //needs to be deleted
+ !mOccludedList[i]->isState(LLSpatialGroup::OCCLUDING) || //is not occluding
+ mOccludedList[i]->isState(LLSpatialGroup::DEACTIVATE_OCCLUSION)) //parent is occluded
+ {
+ LLSpatialGroup* groupp = mOccludedList[i];
+ mOccludedList.erase(mOccludedList.begin()+i);
+ groupp->clearState(LLSpatialGroup::ACTIVE_OCCLUSION);
+ groupp->clearState(LLSpatialGroup::DEACTIVATE_OCCLUSION);
+ groupp->clearState(LLSpatialGroup::OCCLUDING);
+
+ if (groupp->isDead() && groupp->safeToDelete())
+ {
+ delete groupp;
+ }
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ //pump some non-culled items onto the occlusion list
+ //count = MAX_PULLED;
+ while (!mQueryQueue.empty())
+ {
+ LLSpatialGroup* group = mQueryQueue.front();
+ mQueryQueue.pop();
+ //group->clearState(LLSpatialGroup::QUERY_PENDING);
+ mOccludedList.push_back(group);
+ }
+
+ //generate query ids
+ while (mOcclusionQueries.size() < mOccludedList.size())
+ {
+ GLuint id;
+ glGenQueriesARB(1, &id);
+ mOcclusionQueries.push_back(id);
+ }
+}
+
+void LLSpatialPartition::doOcclusion(LLCamera* camera)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ sIgnoreOcclusion = gUseWireframe;
+ LLFastTimer t(LLFastTimer::FTM_RENDER_OCCLUSION);
+
+#if LL_OCTREE_PARANOIA_CHECK
+ LLSpatialGroup* check = (LLSpatialGroup*) mOctree->getListener(0);
+ check->validate();
+#endif
+
+ stop_glerror();
+
+ //actually perform the occlusion queries
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ glDisable(GL_TEXTURE_2D);
+ gPipeline.disableLights();
+ LLGLEnable cull_face(GL_CULL_FACE);
+ LLGLDisable blend(GL_BLEND);
+ LLGLDisable alpha_test(GL_ALPHA_TEST);
+ LLGLDisable fog(GL_FOG);
+ glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
+ glColor4f(1,1,1,1);
+
+ //sort occlusion queries front to back
+ /*for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+ LLSpatialGroup* group = mOccludedList[i];
+
+ LLVector3 v = group->mOctreeNode->getCenter()-camera->getOrigin();
+ group->mDistance = v*v;
+ }
+
+ std::sort(mOccludedList.begin(), mOccludedList.end(), dist_greater());
+
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+ LLGLEnable stencil(GL_STENCIL_TEST);
+ glStencilFunc(GL_GREATER, 1, 0xFFFFFFFF);
+ glStencilOp(GL_KEEP, GL_SET, GL_KEEP);*/
+
+ genBoxList();
+
+ for (U32 i = 0; i < mOccludedList.size(); i++)
+ {
+#if LL_OCTREE_PARANOIA_CHECK
+ for (U32 j = i+1; j < mOccludedList.size(); j++)
+ {
+ sg_assert(mOccludedList[i] != mOccludedList[j]);
+ }
+#endif
+ LLSpatialGroup* group = mOccludedList[i];
+ if (group->isDead())
+ {
+ continue;
+ }
+
+ if (earlyFail(camera, group))
+ {
+ group->setState(LLSpatialGroup::EARLY_FAIL);
+ }
+ else
+ { //early rejection criteria passed, send some geometry to the query
+ LLVector3 c;
+ LLVector3 r;
+
+ sg_assert(!group->isState(LLSpatialGroup::DIRTY));
+
+ c = group->mBounds[0];
+ r = group->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);
+ for (U32 k = 0; k < 3; k++)
+ {
+ r.mV[k] = llmin(group->mBounds[1].mV[k]+0.25f, r.mV[k]);
+ }
+
+#if LL_OCTREE_PARANOIA_CHECK
+ LLVector3 e = camera->getOrigin();
+ LLVector3 min = c - r;
+ LLVector3 max = c + r;
+ BOOL outside = FALSE;
+ for (U32 j = 0; j < 3; j++)
+ {
+ outside = outside || (e.mV[j] < min.mV[j] || e.mV[j] > max.mV[j]);
+ }
+ sg_assert(outside);
+#endif
+
+ glBeginQueryARB(GL_SAMPLES_PASSED_ARB, mOcclusionQueries[i]);
+ drawBox(c,r);
+ glEndQueryARB(GL_SAMPLES_PASSED_ARB);
+
+ group->setState(LLSpatialGroup::QUERY_OUT);
+ group->clearState(LLSpatialGroup::DISCARD_QUERY);
+ }
+ }
+ stop_glerror();
+
+ glFlush();
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ glEnable(GL_TEXTURE_2D);
+}
+
+class LLOctreeGet : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLOctreeGet(LLVector3 pos, F32 rad, LLDrawable::drawable_set_t* results, BOOL lights)
+ : mPosition(pos), mRad(rad), mResults(results), mLights(lights), mRes(0)
+ {
+
+ }
+
+ virtual void traverse(const LLSpatialGroup::TreeNode* n)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) n->getListener(0);
+
+ if (mRes == 2)
+ { //fully in, just add everything
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ else
+ {
+ LLVector3 center, size;
+
+ center = group->mBounds[0];
+ size = group->mBounds[1];
+
+ mRes = LLSphereAABB(center, size, mPosition, mRad);
+ if (mRes > 0)
+ {
+ LLSpatialGroup::OctreeTraveler::traverse(n);
+ }
+ mRes = 0;
+ }
+ }
+
+ static BOOL skip(LLDrawable* drawable, BOOL get_lights)
+ {
+ if (get_lights != drawable->isLight())
+ {
+ return TRUE;
+ }
+ if (get_lights && drawable->getVObj()->isHUDAttachment())
+ {
+ return TRUE; // no lighting from HUD objects
+ }
+ if (get_lights && drawable->isState(LLDrawable::ACTIVE))
+ {
+ return TRUE; // ignore active lights
+ }
+ return FALSE;
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+ if (!skip(drawable, mLights))
+ {
+ if (mRes == 2)
+ {
+ mResults->insert(drawable);
+ }
+ else
+ {
+ LLVector3 v = LLVector3(drawable->getPositionGroup())-mPosition;
+ float dsq = v.magVecSquared();
+ float maxd = mRad + drawable->getVisibilityRadius();
+ if (dsq <= maxd*maxd)
+ {
+ mResults->insert(drawable);
+ }
+ }
+ }
+ }
+ }
+
+ LLVector3 mPosition;
+ F32 mRad;
+ LLDrawable::drawable_set_t* mResults;
+ BOOL mLights;
+ U32 mRes;
+};
+
+S32 LLSpatialPartition::getDrawables(const LLVector3& pos, F32 rad,
+ LLDrawable::drawable_set_t &results,
+ BOOL get_lights)
+{
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ LLOctreeGet getter(pos, rad, &results, get_lights);
+ getter.traverse(mOctree);
+
+ return results.size();
+}
+
+S32 LLSpatialPartition::getObjects(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results)
+{
+ return getDrawables(pos, rad, results, FALSE);
+}
+
+S32 LLSpatialPartition::getLights(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results)
+{
+ return getDrawables(pos, rad, results, TRUE);
+}
+
+class LLOctreeRenderNonOccluded : public LLOctreeTraveler<LLDrawable>
+{
+public:
+ LLOctreeRenderNonOccluded() {}
+
+ virtual void traverse(const LLSpatialGroup::OctreeNode* node)
+ {
+ const LLSpatialGroup::OctreeState* state = node->getOctState();
+ LLSpatialGroup* group = (LLSpatialGroup*) node->getListener(0);
+
+ if (!group->isState(LLSpatialGroup::OCCLUDED | LLSpatialGroup::CULLED))
+ {
+ state->accept(this);
+
+ for (U32 i = 0; i < state->getChildCount(); i++)
+ {
+ traverse(state->getChild(i));
+ }
+
+ /*if (state->getElementCount() == 0)
+ {
+ return;
+ }*/
+
+ //draw tight fit bounding box for spatial group
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+ {
+ if (node->getElementCount() == 0)
+ {
+ return;
+ }
+
+ if (node->hasLeafState())
+ {
+ glColor4f(1,1,1,1);
+ }
+ else
+ {
+ glColor4f(0,1,1,1);
+ }
+
+
+ LLVector3 pos;
+ LLVector3 size;
+
+ pos = group->mObjectBounds[0];
+ size = group->mObjectBounds[1];
+
+ LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos+v4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((pos-v1).mV);
+ glVertex3fv((pos-v2).mV);
+ glVertex3fv((pos-v3).mV);
+ glVertex3fv((pos-v4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos-v3).mV);
+
+ glVertex3fv((pos+v4).mV);
+ glVertex3fv((pos-v2).mV);
+
+ //left
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos-v4).mV);
+
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos-v1).mV);
+
+ glEnd();
+
+ LLVector3 nc = LLVector3(node->getCenter());
+ LLVector3 ns = LLVector3(node->getSize());
+
+ LLVector3 nv1 = ns.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 nv2 = ns.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 nv3 = ns.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 nv4 = ns.scaledVec(LLVector3( 1,-1,1));
+
+
+
+ /*if (node->getElementCount() > 0)
+ {
+ //spokes
+ glColor4f(1,1,0,1);
+ glVertex3fv(pos.mV);
+ glColor4f(1,1,0,0);
+ glVertex3fv(nc.mV);
+
+ glColor4f(1,1,0,1); glVertex3fv((pos+v1).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v1).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v2).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v2).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v3).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v3).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos+v4).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ glColor4f(1,1,0,1); glVertex3fv((pos-v4).mV); glColor4f(1,1,0,0); glVertex3fv(pos.mV);
+ }*/
+
+
+
+ /*glColor4f(0,1,0,1);
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((nc+nv1).mV);
+ glVertex3fv((nc+nv2).mV);
+ glVertex3fv((nc+nv3).mV);
+ glVertex3fv((nc+nv4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((nc-nv1).mV);
+ glVertex3fv((nc-nv2).mV);
+ glVertex3fv((nc-nv3).mV);
+ glVertex3fv((nc-nv4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((nc+nv1).mV);
+ glVertex3fv((nc-nv3).mV);
+
+ glVertex3fv((nc+nv4).mV);
+ glVertex3fv((nc-nv2).mV);
+
+ //left
+ glVertex3fv((nc+nv2).mV);
+ glVertex3fv((nc-nv4).mV);
+
+ glVertex3fv((nc+nv3).mV);
+ glVertex3fv((nc-nv1).mV);
+ glEnd();*/
+
+ glLineWidth(1);
+
+ glDepthMask(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glColor4f(0.1f,0.1f,1,0.1f);
+ drawBox(group->mObjectBounds[0], group->mObjectBounds[1]*1.01f+LLVector3(0.001f, 0.001f, 0.001f));
+ glDepthMask(GL_TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ /*else
+ {
+ //occlusion paranoia check
+ const LLSpatialGroup::OctreeNode* parent = node;
+ while (parent != NULL)
+ {
+ LLSpatialGroup* grp = (LLSpatialGroup*) parent->getListener(0);
+ if (grp->isState(LLSpatialGroup::ACTIVE_OCCLUSION))
+ {
+ return;
+ }
+ parent = (const LLSpatialGroup::OctreeNode*) parent->getParent();
+ }
+
+ glColor4f(1,0,1,1);
+ drawBox(group->mBounds[0], group->mBounds[1]);
+ }*/
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ LLSpatialGroup* group = (LLSpatialGroup*) branch->getListener(0);
+
+ if (group->isState(LLSpatialGroup::CULLED | LLSpatialGroup::OCCLUDED))
+ {
+ return;
+ }
+
+ LLVector3 nodeCenter = group->mBounds[0];
+ LLVector3 octCenter = LLVector3(group->mOctreeNode->getCenter());
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE))
+ {
+ glBegin(GL_LINES);
+ glColor4f(1,0.5f,0,1);
+ glVertex3fv(nodeCenter.mV);
+ glColor4f(0,1,1,0);
+ glVertex3fv(octCenter.mV);
+ glEnd();
+ }
+
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ LLDrawable* drawable = *i;
+
+ if (drawable->isSpatialBridge())
+ {
+ LLSpatialBridge* bridge = (LLSpatialBridge*) drawable;
+ glPushMatrix();
+ glMultMatrixf((F32*)bridge->mDrawable->getWorldMatrix().mMatrix);
+ traverse(bridge->mOctree);
+ glPopMatrix();
+ }
+
+ if (!drawable->isVisible())
+ {
+ continue;
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_BBOXES))
+ {
+ if (drawable->isSpatialBridge())
+ {
+ glColor4f(1,0.5f,0,1);
+ }
+ else if (drawable->getVOVolume())
+ {
+ if (drawable->isRoot())
+ {
+ glColor4f(1,1,0,1);
+ }
+ else
+ {
+ glColor4f(0,1,0,1);
+ }
+ }
+ else if (drawable->getVObj())
+ {
+ switch (drawable->getVObj()->getPCode())
+ {
+ case LLViewerObject::LL_VO_SURFACE_PATCH:
+ glColor4f(0,1,1,1);
+ break;
+ case LLViewerObject::LL_VO_CLOUDS:
+ glColor4f(0.5f,0.5f,0.5f,1.0f);
+ break;
+ case LLViewerObject::LL_VO_PART_GROUP:
+ glColor4f(0,0,1,1);
+ break;
+ case LLViewerObject::LL_VO_WATER:
+ glColor4f(0,0.5f,1,1);
+ break;
+ case LL_PCODE_LEGACY_TREE:
+ glColor4f(0,0.5f,0,1);
+ default:
+ glColor4f(1,0,1,1);
+ break;
+ }
+ }
+ else
+ {
+ glColor4f(1,0,0,1);
+ }
+
+
+ const LLVector3* ext = drawable->getSpatialExtents();
+
+ LLVector3 pos = (ext[0] + ext[1]) * 0.5f;
+ LLVector3 size = (ext[1] - ext[0]) * 0.5f;
+
+ LLVector3 v1 = size.scaledVec(LLVector3( 1, 1,1));
+ LLVector3 v2 = size.scaledVec(LLVector3(-1, 1,1));
+ LLVector3 v3 = size.scaledVec(LLVector3(-1,-1,1));
+ LLVector3 v4 = size.scaledVec(LLVector3( 1,-1,1));
+
+ glBegin(GL_LINE_LOOP); //top
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos+v4).mV);
+ glEnd();
+
+ glBegin(GL_LINE_LOOP); //bottom
+ glVertex3fv((pos-v1).mV);
+ glVertex3fv((pos-v2).mV);
+ glVertex3fv((pos-v3).mV);
+ glVertex3fv((pos-v4).mV);
+ glEnd();
+
+
+ glBegin(GL_LINES);
+
+ //right
+ glVertex3fv((pos+v1).mV);
+ glVertex3fv((pos-v3).mV);
+
+ glVertex3fv((pos+v4).mV);
+ glVertex3fv((pos-v2).mV);
+
+ //left
+ glVertex3fv((pos+v2).mV);
+ glVertex3fv((pos-v4).mV);
+
+ glVertex3fv((pos+v3).mV);
+ glVertex3fv((pos-v1).mV);
+
+ glEnd();
+
+ //render space partition trace
+ glBegin(GL_LINE_STRIP);
+ glColor4f(1,0,0,1);
+ glVertex3fv(pos.mV);
+ glColor4f(0,1,0,1);
+ glVertex3dv(drawable->getPositionGroup().mdV);
+ glColor4f(0,0,1,1);
+ glVertex3fv(nodeCenter.mV);
+ glColor4f(1,1,0,1);
+ glVertex3fv(octCenter.mV);
+ glEnd();
+ }
+
+ if (drawable->getVOVolume() && gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS | LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ glLineWidth(3);
+
+ for (int face=0; face<drawable->getNumFaces(); ++face)
+ {
+ LLFace *facep = drawable->getFace(face);
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_FACE_CHAINS))
+ {
+ LLGLDepthTest depth(GL_FALSE);
+ if (facep->mNextFace)
+ {
+ glBegin(GL_LINE_STRIP);
+
+ if (facep->isState(LLFace::GLOBAL))
+ {
+ glColor4f(0,1,0,1);
+ }
+ else
+ {
+ glColor4f(1,0.5f,0.25f,1);
+ }
+
+ if (drawable->isActive())
+ {
+ glVertex3fv(facep->mCenterLocal.mV);
+ glVertex3fv(facep->mNextFace->mCenterLocal.mV);
+ }
+ else
+ {
+ glVertex3fv(facep->mCenterAgent.mV);
+ glVertex3fv(facep->mNextFace->mCenterAgent.mV);
+ }
+
+ glEnd();
+ }
+ else
+ {
+ glPointSize(5);
+ glBegin(GL_POINTS);
+
+ if (!facep->isState(LLFace::GLOBAL))
+ {
+ glColor4f(1,0.75f,0,1);
+ glVertex3fv(facep->mCenterLocal.mV);
+ }
+ else
+ {
+ glColor4f(0,0.75f,1,1);
+ glVertex3fv(facep->mCenterAgent.mV);
+ }
+
+ glEnd();
+ glPointSize(1);
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ LLVector4 cold(0,0,0.25f);
+ LLVector4 hot(1,0.25f,0.25f);
+
+ LLVector4 boost_cold(0,0,0,0);
+ LLVector4 boost_hot(0,1,0,1);
+
+ LLGLDisable blend(GL_BLEND);
+
+ LLViewerImage* imagep = facep->getTexture();
+ if (imagep)
+ {
+
+ //F32 vsize = LLVOVolume::getTextureVirtualSize(facep);
+ F32 vsize = imagep->mMaxVirtualSize;
+
+ if (vsize > sCurMaxTexPriority)
+ {
+ sCurMaxTexPriority = vsize;
+ }
+
+ F32 t = vsize/sLastMaxTexPriority;
+
+ LLVector4 col = lerp(cold, hot, t);
+ glColor4fv(col.mV);
+ }
+ else
+ {
+ glColor4f(1,0,1,1);
+ }
+
+ LLVector3 center = (facep->mExtents[1]+facep->mExtents[0])*0.5f;
+ LLVector3 size = (facep->mExtents[1]-facep->mExtents[0])*0.5f + LLVector3(0.01f, 0.01f, 0.01f);
+ drawBox(center, size);
+
+ S32 boost = imagep->getBoostLevel();
+ if (boost)
+ {
+ F32 t = (F32) boost / (F32) (LLViewerImage::BOOST_MAX_LEVEL-1);
+ LLVector4 col = lerp(boost_cold, boost_hot, t);
+ LLGLEnable blend_on(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+ glColor4fv(col.mV);
+ drawBox(center, size);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ }
+ }
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_POINTS))
+ {
+ glPointSize(4);
+ glColor4f(1,1,1,1);
+ glBegin(GL_POINTS);
+ S32 num_faces = drawable->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ LLStrider<LLVector3> vertices;
+ drawable->getFace(i)->getVertices(vertices);
+
+ LLFace* face = drawable->getFace(i);
+
+ for (S32 v = 0; v < (S32)drawable->getFace(i)->getGeomCount(); v++)
+ {
+ if (!face->getDrawable()->isActive())
+ {
+ //glVertex3fv(vertices[v].mV);
+ }
+ else
+ {
+ glVertex3fv((vertices[v]*face->getRenderMatrix()).mV);
+ }
+ }
+ }
+ glEnd();
+ glPointSize(1);
+ }
+
+ glLineWidth(1);
+ }
+ }
+};
+
+void LLSpatialPartition::renderDebug()
+{
+ if (!gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCTREE |
+ LLPipeline::RENDER_DEBUG_OCCLUSION |
+ LLPipeline::RENDER_DEBUG_BBOXES |
+ LLPipeline::RENDER_DEBUG_POINTS |
+ LLPipeline::RENDER_DEBUG_FACE_CHAINS |
+ LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ return;
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY))
+ {
+ //sLastMaxTexPriority = lerp(sLastMaxTexPriority, sCurMaxTexPriority, gFrameIntervalSeconds);
+ sLastMaxTexPriority = sCurMaxTexPriority;
+ sCurMaxTexPriority = 0.f;
+ }
+
+ LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION);
+
+ LLGLDisable cullface(GL_CULL_FACE);
+ LLGLEnable blend(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ LLGLDisable tex(GL_TEXTURE_2D);
+ gPipeline.disableLights();
+
+ LLOctreeRenderNonOccluded render_debug;
+ render_debug.traverse(mOctree);
+
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ //draw frustum
+ glColor4f(0,0,1,0.5f);
+ glBegin(GL_QUADS);
+ //glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[3].mV);
+
+ //glVertex3fv(gCamera->mAgentFrustum[4].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[5].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[6].mV);
+ //glVertex3fv(gCamera->mAgentFrustum[7].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ glVertex3fv(gCamera->mAgentFrustum[5].mV);
+ glVertex3fv(gCamera->mAgentFrustum[4].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[1].mV);
+ glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ glVertex3fv(gCamera->mAgentFrustum[6].mV);
+ glVertex3fv(gCamera->mAgentFrustum[5].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[2].mV);
+ glVertex3fv(gCamera->mAgentFrustum[3].mV);
+ glVertex3fv(gCamera->mAgentFrustum[7].mV);
+ glVertex3fv(gCamera->mAgentFrustum[6].mV);
+
+ glVertex3fv(gCamera->mAgentFrustum[3].mV);
+ glVertex3fv(gCamera->mAgentFrustum[0].mV);
+ glVertex3fv(gCamera->mAgentFrustum[4].mV);
+ glVertex3fv(gCamera->mAgentFrustum[7].mV);
+
+ glEnd();
+ }
+
+ if (gPipeline.hasRenderDebugMask(LLPipeline::RENDER_DEBUG_OCCLUSION))
+ {
+ LLGLDisable fog(GL_FOG);
+ LLGLDepthTest gls_depth(GL_FALSE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE);
+
+ for (std::vector<LLSpatialGroup*>::iterator i = mOccludedList.begin(); i != mOccludedList.end(); ++i)
+ { //draw occluded nodes
+ LLSpatialGroup* node = *i;
+ if (node->isDead())
+ {
+ continue;
+ }
+ if (!node->isState(LLSpatialGroup::OCCLUDED))
+ {
+ continue;
+ }
+ else
+ {
+ glColor4f(0.25f,0.125f,0.1f,0.125f);
+ }
+ LLVector3 c;
+ LLVector3 r;
+
+ c = node->mBounds[0];
+ r = node->mBounds[1]*SG_OCCLUSION_FUDGE + LLVector3(0.01f,0.01f,0.01f);;
+
+ drawBox(c,r);
+ }
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ }
+}
+
+
+BOOL LLSpatialPartition::isVisible(const LLVector3& v)
+{
+ if (!gCamera->sphereInFrustum(v, 4.0f))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+class LLOctreePick : public LLSpatialGroup::OctreeTraveler
+{
+public:
+ LLVector3 mStart;
+ LLVector3 mEnd;
+ LLDrawable* mRet;
+
+ LLOctreePick(LLVector3 start, LLVector3 end)
+ : mStart(start), mEnd(end)
+ {
+ mRet = NULL;
+ }
+
+ virtual LLDrawable* check(const LLSpatialGroup::OctreeNode* node)
+ {
+ const LLSpatialGroup::OctreeState* state = node->getOctState();
+ state->accept(this);
+
+ for (U32 i = 0; i < node->getChildCount(); i++)
+ {
+ const LLSpatialGroup::OctreeNode* child = node->getChild(i);
+ LLVector3 res;
+
+ LLSpatialGroup* group = (LLSpatialGroup*) child->getListener(0);
+
+ LLVector3 size;
+ LLVector3 center;
+
+ size = group->mBounds[1];
+ center = group->mBounds[0];
+
+ if (LLLineSegmentAABB(mStart, mEnd, center, size))
+ {
+ check(child);
+ }
+ }
+
+ return mRet;
+ }
+
+ virtual void visit(const LLSpatialGroup::OctreeState* branch)
+ {
+ for (LLSpatialGroup::OctreeState::const_element_iter i = branch->getData().begin(); i != branch->getData().end(); ++i)
+ {
+ check(*i);
+ }
+ }
+
+ virtual bool check(LLDrawable* drawable)
+ {
+ LLViewerObject* vobj = drawable->getVObj();
+ if (vobj->lineSegmentIntersect(mStart, mEnd))
+ {
+ mRet = vobj->mDrawable;
+ }
+
+ return false;
+ }
+};
+
+LLDrawable* LLSpatialPartition::pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision)
+{
+ LLOctreePick pick(start, end);
+ LLDrawable* ret = pick.check(mOctree);
+ collision.setVec(pick.mEnd);
+ return ret;
+}
diff --git a/indra/newview/llspatialpartition.h b/indra/newview/llspatialpartition.h
new file mode 100644
index 0000000000..012e3a9e82
--- /dev/null
+++ b/indra/newview/llspatialpartition.h
@@ -0,0 +1,209 @@
+/**
+ * @file llspatialpartition.h
+ * @brief LLSpatialGroup header file including definitions for supporting functions
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSPATIALPARTITION_H
+#define LL_LLSPATIALPARTITION_H
+
+#define SG_MIN_DIST_RATIO 0.00001f
+
+#include "llmemory.h"
+#include "lldrawable.h"
+#include "lloctree.h"
+#include "llgltypes.h"
+
+#include <queue>
+
+class LLCullInfo
+{
+public:
+ LLVector3 mPos;
+ F32 mRadius;
+ LLPointer<LLDrawable> mDrawablep;
+};
+
+#define SG_STATE_INHERIT_MASK (CULLED | OCCLUDED)
+class LLSpatialPartition;
+
+class LLSpatialGroup : public LLOctreeListener<LLDrawable>
+{
+ friend class LLSpatialPartition;
+public:
+
+ typedef LLOctreeListener<LLDrawable> BaseType;
+ typedef LLOctreeListener<LLDrawable> OctreeListener;
+ typedef LLTreeNode<LLDrawable> TreeNode;
+ typedef LLOctreeNode<LLDrawable> OctreeNode;
+ typedef LLOctreeRoot<LLDrawable> OctreeRoot;
+ typedef LLOctreeState<LLDrawable> OctreeState;
+ typedef LLOctreeTraveler<LLDrawable> OctreeTraveler;
+
+ typedef enum
+ {
+ IN_QUEUE = 0x00000001,
+ QUERY_PENDING = 0x00000002,
+ CULLED = 0x00000004,
+ OCCLUDED = 0x00000008,
+ DEAD = 0x00000010,
+ ACTIVE_OCCLUSION = 0x00000020,
+ EARLY_FAIL = 0x00000040,
+ DEACTIVATE_OCCLUSION = 0x00000080,
+ RESHADOW = 0x00000100,
+ RESHADOW_QUEUE = 0x00000200,
+ DIRTY = 0x00000400,
+ OBJECT_DIRTY = 0x00000800,
+ DISCARD_QUERY = 0x00001000,
+ QUERY_OUT = 0x00002000,
+ OCCLUDING = 0x00004000,
+ SKIP_FRUSTUM_CHECK = 0x00008000,
+ } eSpatialState;
+
+ typedef enum
+ {
+ STATE_MODE_SINGLE = 0, //set one node
+ STATE_MODE_BRANCH, //set entire branch
+ STATE_MODE_DIFF //set entire branch as long as current state is different
+ } eSetStateMode;
+
+ LLSpatialGroup(OctreeNode* node, LLSpatialPartition* part);
+ BOOL safeToDelete();
+ virtual ~LLSpatialGroup();
+
+ S32 getCount() const { return mObjects.size(); }
+ BOOL isDead() { return isState(DEAD); }
+ BOOL isState(U32 state) const { return mState & state ? TRUE : FALSE; }
+ U32 getState() { return mState; }
+ void setState(U32 state) { mState |= state; }
+ void clearState(U32 state) { mState &= ~state; }
+
+ void validate();
+
+ void setState(U32 state, S32 mode);
+
+
+ void clearState(U32 state, S32 mode);
+ BOOL addObject(LLDrawable *drawablep, BOOL add_all = FALSE, BOOL from_octree = FALSE);
+ BOOL removeObject(LLDrawable *drawablep, BOOL from_octree = FALSE);
+ BOOL updateInGroup(LLDrawable *drawablep, BOOL immediate = FALSE); // Update position if it's in the group
+ BOOL isVisible();
+ void shift(const LLVector3 &offset);
+ BOOL boundObjects(BOOL empty, LLVector3& newMin, LLVector3& newMax);
+ void unbound();
+ BOOL rebound();
+ BOOL changeLOD();
+
+ //LISTENER FUNCTIONS
+ virtual void handleInsertion(const TreeNode* node, LLDrawable* face);
+ virtual void handleRemoval(const TreeNode* node, LLDrawable* face);
+ virtual void handleDestruction(const TreeNode* node);
+ virtual void handleStateChange(const TreeNode* node);
+ virtual void handleChildAddition(const OctreeNode* parent, OctreeNode* child);
+ virtual void handleChildRemoval(const OctreeNode* parent, const OctreeNode* child);
+
+protected:
+ std::vector<LLCullInfo> mObjects;
+ U32 mState;
+ S32 mLODHash;
+ static S32 sLODSeed;
+
+public:
+ OctreeNode* mOctreeNode;
+ LLSpatialPartition* mSpatialPartition;
+ LLVector3 mBounds[2];
+ LLVector3 mExtents[2];
+ LLVector3 mObjectExtents[2];
+ LLVector3 mObjectBounds[2];
+
+};
+
+class LLSpatialPartition
+{
+public:
+ LLSpatialPartition();
+ virtual ~LLSpatialPartition();
+
+ LLSpatialGroup *put(LLDrawable *drawablep);
+ BOOL remove(LLDrawable *drawablep, LLSpatialGroup *curp);
+
+ LLDrawable* pickDrawable(const LLVector3& start, const LLVector3& end, LLVector3& collision);
+
+ // If the drawable moves, move it here.
+ virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
+ void shift(const LLVector3 &offset);
+
+ S32 cull(LLCamera &camera, std::vector<LLDrawable *>* results = NULL, BOOL for_select = FALSE); // Cull on arbitrary frustum
+ BOOL checkOcclusion(LLSpatialGroup* group, LLCamera* camera);
+ void processOcclusion(LLCamera* camera);
+ void doOcclusion(LLCamera* camera);
+ BOOL isVisible(const LLVector3& v);
+
+ S32 getObjects(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results );
+ S32 getLights(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results );
+
+ void renderDebug();
+ void restoreGL();
+
+protected:
+ S32 getDrawables(const LLVector3& pos, F32 rad, LLDrawable::drawable_set_t &results, BOOL get_lights );
+
+ LLSpatialGroup *mLastAddedGroupp;
+
+ typedef std::set<LLSpatialGroup*> spatial_group_set_t;
+ spatial_group_set_t mSpatialGroups;
+
+ //things that might be occluded
+ std::queue<LLSpatialGroup*> mOcclusionQueue;
+
+ //things awaiting query
+ std::queue<LLSpatialGroup*> mQueryQueue;
+
+ std::vector<LLGLuint> mOcclusionQueries;
+
+public:
+ LLSpatialGroup::OctreeNode* mOctree;
+
+ //things that are occluded
+ std::vector<LLSpatialGroup*> mOccludedList;
+
+ std::queue<LLSpatialGroup*> mReshadowQueue;
+
+};
+
+// class for creating bridges between spatial partitions
+class LLSpatialBridge : public LLDrawable, public LLSpatialPartition
+{
+public:
+ LLSpatialBridge(LLDrawable* root);
+ virtual ~LLSpatialBridge();
+
+ virtual BOOL isSpatialBridge() const { return TRUE; }
+
+ virtual void updateSpatialExtents();
+ virtual void updateBinRadius();
+ virtual void setVisible(LLCamera& camera_in, std::vector<LLDrawable*>* results = NULL, BOOL for_select = FALSE);
+ virtual void updateDistance(LLCamera& camera_in);
+ virtual void makeActive();
+ virtual void makeStatic();
+ virtual void move(LLDrawable *drawablep, LLSpatialGroup *curp, BOOL immediate = FALSE);
+ virtual BOOL updateMove();
+ virtual void shiftPos(const LLVector3& vec);
+ virtual void cleanupReferences();
+ virtual LLSpatialPartition* asPartition() { return this; }
+ LLCamera transformCamera(LLCamera& camera);
+
+ LLDrawable* mDrawable;
+};
+
+extern const F32 SG_BOX_SIDE;
+extern const F32 SG_BOX_OFFSET;
+extern const F32 SG_BOX_RAD;
+
+extern const F32 SG_OBJ_SIDE;
+extern const F32 SG_MAX_OBJ_RAD;
+
+#endif //LL_LLSPATIALPARTITION_H
+
diff --git a/indra/newview/llsprite.cpp b/indra/newview/llsprite.cpp
new file mode 100644
index 0000000000..e614134080
--- /dev/null
+++ b/indra/newview/llsprite.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llsprite.cpp
+ * @brief LLSprite class implementation
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/* -*- c++ -*-
+ * Notes:
+ * PR - Should add a creator that can take a pointer rather than handle for streaming
+ * object textures.
+ * PR - Need to add support for lit/non-lit conditions, set normals?
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <llglheaders.h>
+
+#include "llsprite.h"
+
+#include "math.h"
+
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "viewer.h"
+
+LLVector3 LLSprite::sCameraUp(0.0f,0.0f,1.0f);
+LLVector3 LLSprite::sCameraRight(1.0f,0.0f,0.0f);
+LLVector3 LLSprite::sCameraPosition(0.f, 0.f, 0.f);
+LLVector3 LLSprite::sNormal(0.0f,0.0f,0.0f);
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+// A simple initialization
+LLSprite::LLSprite(const LLUUID &image_uuid)
+{
+ mImageID = image_uuid;
+ mImagep = NULL;
+
+ setSize(1.0f, 1.0f);
+ setPosition(LLVector3(0.0f, 0.0f, 0.0f));
+ mTexMode = GL_REPLACE;
+ mColor.setVec(0.5f, 0.5f, 0.5f, 1.0f);
+ mFollow = TRUE;
+ mUseCameraUp = TRUE;
+}
+
+LLSprite::LLSprite(const LLUUID &image_uuid, const F32 width, const F32 height, const BOOL b_usemipmap)
+{
+ mImageID = image_uuid;
+ mImagep = NULL;
+
+ setSize(width,height);
+ setPosition(LLVector3(0.0f, 0.0f, 0.0f));
+ mTexMode = GL_REPLACE;
+ mColor.setVec(0.5f, 0.5f, 0.5f, 1.0f);
+ mFollow = TRUE;
+ mUseCameraUp = TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////
+LLSprite::~LLSprite()
+{
+}
+
+void LLSprite::updateFace(LLFace &face)
+{
+ LLViewerCamera &camera = *gCamera;
+
+ // First, figure out how many vertices/indices we need.
+ U32 num_vertices, num_indices;
+ U32 vertex_count = 0;
+
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> tex_coordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ // Get the total number of vertices and indices
+ if (mFollow)
+ {
+ num_vertices = 4;
+ num_indices = 6;
+ }
+ else
+ {
+ num_vertices = 4;
+ num_indices = 12;
+ }
+
+ // Setup face
+ face.setPrimType(LLTriangles);
+ face.setSize(num_vertices, num_indices);
+ index_offset = face.getGeometry(verticesp,normalsp,tex_coordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ if (mFollow)
+ {
+ sCameraUp = camera.getUpAxis();
+ sCameraRight = -camera.getLeftAxis();
+ sCameraPosition = camera.getOrigin();
+ sNormal = -camera.getAtAxis();
+ if (mUseCameraUp)
+ {
+ // these need to live here because the height/width may change between render calls
+ mScaledUp = sCameraUp;
+ mScaledRight = sCameraRight;
+
+ mScaledUp *= mHeightDiv2;
+ mScaledRight *= mWidthDiv2;
+
+ mA = mPosition + mScaledRight + mScaledUp;
+ mB = mPosition - mScaledRight + mScaledUp;
+ mC = mPosition - mScaledRight - mScaledUp;
+ mD = mPosition + mScaledRight - mScaledUp;
+ }
+ else
+ {
+ // The up vector is perpendicular to the camera vector...
+ LLVector3 camera_vec = mPosition - sCameraPosition;
+ mScaledRight = camera_vec % LLVector3(0.f, 0.f, 1.f);
+ mScaledUp = -(camera_vec % mScaledRight);
+ mScaledUp.normVec();
+ mScaledRight.normVec();
+ mScaledUp *= mHeightDiv2;
+ mScaledRight *= mWidthDiv2;
+
+ mA = mPosition + mScaledRight + mScaledUp;
+ mB = mPosition - mScaledRight + mScaledUp;
+ mC = mPosition - mScaledRight - mScaledUp;
+ mD = mPosition + mScaledRight - mScaledUp;
+ }
+ }
+ else
+ {
+ // this is equivalent to how it was done before. . .
+ // we need to establish a way to
+ // identify the orientation of a particular sprite rather than
+ // just banging it in on the x,z plane if it's not following the camera.
+
+ LLVector3 x_axis;
+ LLVector3 y_axis;
+
+ F32 dot = sNormal * LLVector3(0.f, 1.f, 0.f);
+ if (dot == 1.f || dot == -1.f)
+ {
+ x_axis.setVec(1.f, 0.f, 0.f);
+ y_axis.setVec(0.f, 1.f, 0.f);
+ }
+ else
+ {
+ x_axis = sNormal % LLVector3(0.f, -1.f, 0.f);
+ x_axis.normVec();
+
+ y_axis = sNormal % x_axis;
+ }
+
+ LLQuaternion yaw_rot(mYaw, sNormal);
+
+ // rotate axes by specified yaw
+ x_axis = x_axis * yaw_rot;
+ y_axis = y_axis * yaw_rot;
+
+ // rescale axes by width and height of sprite
+ x_axis = x_axis * mWidthDiv2;
+ y_axis = y_axis * mHeightDiv2;
+
+ mA = -x_axis + y_axis;
+ mB = x_axis + y_axis;
+ mC = x_axis - y_axis;
+ mD = -x_axis - y_axis;
+
+ mA += mPosition;
+ mB += mPosition;
+ mC += mPosition;
+ mD += mPosition;
+ }
+
+ face.setFaceColor(mColor);
+
+ *tex_coordsp = LLVector2(0.f, 0.f);
+ *verticesp = mC;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(0.f, 1.f);
+ *verticesp = mB;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(1.f, 1.f);
+ *verticesp = mA;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ *tex_coordsp = LLVector2(1.f, 0.0f);
+ *verticesp = mD;
+ tex_coordsp++;
+ verticesp++;
+ vertex_count++;
+
+ // Generate indices, since they're easy.
+ // Just a series of quads.
+ *indicesp++ = index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 1 + index_offset;
+
+ *indicesp++ = index_offset;
+ *indicesp++ = 3 + index_offset;
+ *indicesp++ = 2 + index_offset;
+
+ if (!mFollow)
+ {
+ *indicesp++ = 0 + index_offset;
+ *indicesp++ = 1 + index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 0 + index_offset;
+ *indicesp++ = 2 + index_offset;
+ *indicesp++ = 3 + index_offset;
+ }
+
+ face.mCenterAgent = mPosition;
+}
+
+void LLSprite::setPosition(const LLVector3 &position)
+{
+ mPosition = position;
+}
+
+
+void LLSprite::setPitch(const F32 pitch)
+{
+ mPitch = pitch;
+}
+
+
+void LLSprite::setSize(const F32 width, const F32 height)
+{
+ mWidth = width;
+ mHeight = height;
+ mWidthDiv2 = width/2.0f;
+ mHeightDiv2 = height/2.0f;
+}
+
+void LLSprite::setYaw(F32 yaw)
+{
+ mYaw = yaw;
+}
+
+void LLSprite::setFollow(const BOOL follow)
+{
+ mFollow = follow;
+}
+
+void LLSprite::setUseCameraUp(const BOOL use_up)
+{
+ mUseCameraUp = use_up;
+}
+
+void LLSprite::setTexMode(const LLGLenum mode)
+{
+ mTexMode = mode;
+}
+
+void LLSprite::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+void LLSprite::setColor(const F32 r, const F32 g, const F32 b, const F32 a)
+{
+ mColor.setVec(r, g, b, a);
+}
+
+
+
+
+
+
+
+
+
+
diff --git a/indra/newview/llsprite.h b/indra/newview/llsprite.h
new file mode 100644
index 0000000000..6aa24fc8db
--- /dev/null
+++ b/indra/newview/llsprite.h
@@ -0,0 +1,89 @@
+/**
+ * @file llsprite.h
+ * @brief LLSprite class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSPRITE_H
+#define LL_LLSPRITE_H
+
+////#include "vmath.h"
+//#include "llmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "v4color.h"
+#include "lluuid.h"
+#include "llgl.h"
+#include "llviewerimage.h"
+
+class LLViewerCamera;
+
+class LLFace;
+
+class LLSprite
+{
+public:
+ LLSprite(const LLUUID &image_uuid);
+ LLSprite(const LLUUID &image_uuid, const F32 width, const F32 height, const BOOL b_usemipmap = TRUE);
+ ~LLSprite();
+
+ void render(LLViewerCamera * camerap);
+
+ F32 getWidth() const { return mWidth; }
+ F32 getHeight() const { return mHeight; }
+ F32 getYaw() const { return mYaw; }
+ F32 getPitch() const { return mPitch; }
+ F32 getAlpha() const { return mColor.mV[VALPHA]; }
+
+ LLVector3 getPosition() const { return mPosition; }
+ LLColor4 getColor() const { return mColor; }
+
+ void setPosition(const LLVector3 &position);
+ void setPitch(const F32 pitch);
+ void setSize(const F32 width, const F32 height);
+ void setYaw(const F32 yaw);
+ void setFollow(const BOOL follow);
+ void setUseCameraUp(const BOOL use_up);
+
+ void setTexMode(LLGLenum mode);
+ void setColor(const LLColor4 &color);
+ void setColor(const F32 r, const F32 g, const F32 b, const F32 a);
+ void setAlpha(const F32 alpha) { mColor.mV[VALPHA] = alpha; }
+ void setNormal(const LLVector3 &normal) { sNormal = normal; sNormal.normVec();}
+
+ F32 getAlpha();
+
+ void updateFace(LLFace &face);
+
+public:
+ LLUUID mImageID;
+ LLPointer<LLViewerImage> mImagep;
+private:
+ F32 mWidth;
+ F32 mHeight;
+ F32 mWidthDiv2;
+ F32 mHeightDiv2;
+ F32 mPitch;
+ F32 mYaw;
+ LLVector3 mPosition;
+ BOOL mFollow;
+ BOOL mUseCameraUp;
+
+ LLColor4 mColor;
+ LLGLenum mTexMode;
+
+ // put
+ LLVector3 mScaledUp;
+ LLVector3 mScaledRight;
+ static LLVector3 sCameraUp;
+ static LLVector3 sCameraRight;
+ static LLVector3 sCameraPosition;
+ static LLVector3 sNormal;
+ LLVector3 mA,mB,mC,mD; // the four corners of a quad
+
+};
+
+#endif
+
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
new file mode 100644
index 0000000000..643b86e104
--- /dev/null
+++ b/indra/newview/llstartup.cpp
@@ -0,0 +1,3959 @@
+/**
+ * @file llstartup.cpp
+ * @brief startup routines.
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstartup.h"
+
+#if LL_WINDOWS
+#include <process.h> // _spawnl()
+#else
+#include <sys/stat.h> // mkdir()
+#endif
+
+#include "audioengine.h"
+
+#if LL_FMOD
+#include "audioengine_fmod.h"
+#endif
+
+#include "audiosettings.h"
+#include "llcachename.h"
+#include "llviewercontrol.h"
+#include "llcrypto.h"
+#include "lldir.h"
+#include "lleconomy.h"
+#include "llfiltersd2xmlrpc.h"
+#include "llfocusmgr.h"
+#include "imageids.h"
+#include "lllandmark.h"
+#include "llloginflags.h"
+#include "llmd5.h"
+#include "llmemorystream.h"
+#include "llregionhandle.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include "llsecondlifeurls.h"
+#include "llstring.h"
+#include "lluserrelations.h"
+#include "llversion.h"
+#include "llvfs.h"
+#include "llwindow.h" // for shell_open
+#include "message.h"
+#include "v3math.h"
+
+#include "llagent.h"
+#include "llagentpilot.h"
+#include "llasynchostbyname.h"
+#include "llfloateravatarpicker.h"
+#include "llcallbacklist.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llconsole.h"
+#include "llcontainerview.h"
+#include "lldebugview.h"
+#include "lldrawable.h"
+#include "lleventnotifier.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfloateraccounthistory.h"
+#include "llfloaterchat.h"
+#include "llfloatergesture.h"
+#include "llfloaterland.h"
+#include "llfloatertopobjects.h"
+#include "llfloaterrate.h"
+#include "llfloatertos.h"
+#include "llfloaterworldmap.h"
+#include "llframestats.h"
+#include "llframestatview.h"
+#include "llgesturemgr.h"
+#include "llgroupmgr.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llhttpclient.h"
+#include "llimagebmp.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "llpanellogin.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llpanelavatar.h"
+#include "llpaneldirbrowser.h"
+#include "llpaneldirland.h"
+#include "llpanelevent.h"
+#include "llpanelclassified.h"
+#include "llpanelpick.h"
+#include "llpanelplace.h"
+#include "llpanelgrouplandmoney.h"
+#include "llpanelgroupnotices.h"
+#include "llpreview.h"
+#include "llpreviewscript.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstatview.h"
+#include "llsurface.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llurlwhitelist.h"
+#include "lluserauth.h"
+#include "llviewerassetstorage.h"
+#include "llviewercamera.h"
+#include "llviewerdisplay.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewermedialist.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerthrottle.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoclouds.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "llxfermanager.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llmediaengine.h"
+#include "llfasttimerview.h"
+#include "llmozlib.h"
+#include "llweb.h"
+#include "llfloaterhtml.h"
+
+#if LL_WINDOWS
+#include "llwindebug.h"
+#include "lldxhardware.h"
+#endif
+
+#if LL_QUICKTIME_ENABLED
+#if LL_DARWIN
+#include <QuickTime/QuickTime.h>
+#else
+// quicktime specific includes
+#include "MacTypes.h"
+#include "QTML.h"
+#include "Movies.h"
+#include "FixMath.h"
+#endif
+#endif
+
+//
+// exported globals
+//
+
+// HACK: Allow server to change sun and moon IDs.
+// I can't figure out how to pass the appropriate
+// information into the LLVOSky constructor. JC
+LLUUID gSunTextureID = IMG_SUN;
+LLUUID gMoonTextureID = IMG_MOON;
+LLUUID gCloudTextureID = IMG_CLOUD_POOF;
+
+const char* SCREEN_HOME_FILENAME = "screen_home.bmp";
+const char* SCREEN_LAST_FILENAME = "screen_last.bmp";
+
+//
+// Imported globals
+//
+extern LLPointer<LLImageGL> gStartImageGL;
+extern S32 gStartImageWidth;
+extern S32 gStartImageHeight;
+extern std::string gSerialNumber;
+
+//
+// local globals
+//
+
+static LLHost gAgentSimHost;
+static BOOL gSkipOptionalUpdate = FALSE;
+
+bool gQuickTimeInitialized = false;
+static bool gGotUseCircuitCodeAck = false;
+LLString gInitialOutfit;
+LLString gInitialOutfitGender; // "male" or "female"
+
+
+//
+// local function declaration
+//
+
+void login_show();
+void login_callback(S32 option, void* userdata);
+LLString load_password_from_disk();
+void save_password_to_disk(const char* hashed_password);
+BOOL is_hex_string(U8* str, S32 len);
+void show_first_run_dialog();
+void first_run_dialog_callback(S32 option, void* userdata);
+void set_startup_status(const F32 frac, const char* string, const char* msg);
+void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata );
+void login_alert_status(S32 option, void* user_data);
+void update_app(BOOL mandatory, const std::string& message);
+void update_dialog_callback(S32 option, void *userdata);
+void login_packet_failed(void**, S32 result);
+void use_circuit_callback(void**, S32 result);
+void register_viewer_callbacks(LLMessageSystem* msg);
+void init_stat_view();
+void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32);
+void dialog_choose_gender_first_start();
+void callback_choose_gender(S32 option, void* userdata);
+void init_start_screen(S32 location_id);
+void release_start_screen();
+void process_connect_to_userserver(LLMessageSystem* msg, void**);
+
+
+//
+// exported functionality
+//
+
+//
+// local classes
+//
+class LLGestureInventoryFetchObserver : public LLInventoryFetchObserver
+{
+public:
+ LLGestureInventoryFetchObserver() {}
+ virtual void done()
+ {
+ // we've downloaded all the items, so repaint the dialog
+ LLFloaterGesture::refreshAll();
+
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+
+
+// Returns FALSE to skip other idle processing. Should only return
+// TRUE when all initialization done.
+BOOL idle_startup()
+{
+ LLMemType mt1(LLMemType::MTYPE_STARTUP);
+
+ const F32 PRECACHING_DELAY = gSavedSettings.getF32("PrecachingDelay");
+ const F32 TIMEOUT_SECONDS = 5.f;
+ const S32 MAX_TIMEOUT_COUNT = 3;
+ static LLTimer timeout;
+ static S32 timeout_count = 0;
+
+ static LLTimer login_time;
+
+ // until this is encapsulated, this little hack for the
+ // auth/transform loop will do.
+ static F32 progress = 0.10f;
+
+ static std::string auth_uri;
+ static std::string auth_method;
+ static std::string auth_desc;
+ static std::string auth_message;
+ static LLString firstname;
+ static LLString lastname;
+ static LLString password;
+ static std::vector<const char*> requested_options;
+
+ static U32 region_size = 256;
+ static F32 region_scale = 1.f;
+ static U64 first_sim_handle = 0;
+ static LLHost first_sim;
+ static std::string first_sim_seed_cap;
+
+ static LLVector3 initial_sun_direction(1.f, 0.f, 0.f);
+ static LLVector3 agent_start_position_region(10.f, 10.f, 10.f); // default for when no space server
+ static LLVector3 agent_start_look_at(1.0f, 0.f, 0.f);
+ static std::string agent_start_location = "safe";
+
+ // last location by default
+ static S32 agent_location_id = START_LOCATION_ID_LAST;
+ static S32 location_which = START_LOCATION_ID_LAST;
+
+ static BOOL show_connect_box = TRUE;
+ static BOOL remember_password = TRUE;
+
+ static BOOL stipend_since_login = FALSE;
+
+ static BOOL samename = FALSE;
+
+ static BOOL did_precache = FALSE;
+
+ BOOL do_normal_idle = FALSE;
+
+ // HACK: These are things from the main loop that usually aren't done
+ // until initialization is complete, but need to be done here for things
+ // to work.
+ gIdleCallbacks.callFunctions();
+ gViewerWindow->handlePerFrameHover();
+ LLMortician::updateClass();
+
+ if (gNoRender)
+ {
+ // HACK, skip optional updates if you're running drones
+ gSkipOptionalUpdate = TRUE;
+ }
+ else
+ {
+ // Update images?
+ gImageList.updateImages(0.01f);
+ }
+
+ if (STATE_FIRST == gStartupState)
+ {
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
+
+ /////////////////////////////////////////////////
+ //
+ // Initialize stuff that doesn't need data from userserver/simulators
+ //
+
+ if (gFeatureManagerp->isSafe())
+ {
+ gViewerWindow->alertXml("DisplaySetToSafe");
+ }
+ else if (gSavedSettings.getS32("LastFeatureVersion") < gFeatureManagerp->getVersion())
+ {
+ if (gSavedSettings.getS32("LastFeatureVersion") != 0)
+ {
+ gViewerWindow->alertXml("DisplaySetToRecommended");
+ }
+ }
+ gSavedSettings.setS32("LastFeatureVersion", gFeatureManagerp->getVersion());
+
+ LLString xml_file = LLUI::locateSkin("xui_version.xml");
+ LLXMLNodePtr root;
+ bool xml_ok = false;
+ if (LLXMLNode::parseFile(xml_file, root, NULL))
+ {
+ if( (root->hasName("xui_version") ) )
+ {
+ LLString value = root->getValue();
+ F32 version = 0.0f;
+ LLString::convertToF32(value, version);
+ if (version >= 1.0f)
+ {
+ xml_ok = true;
+ }
+ }
+ }
+ if (!xml_ok)
+ {
+ // XUI:translate (maybe - very unlikely error message)
+ // Note: alerts.xml may be invalid - if this gets translated it will need to be in the code
+ LLString bad_xui_msg = "An error occured while updating Second Life. Please download the latest version from www.secondlife.com.";
+ app_early_exit(bad_xui_msg);
+ }
+ //
+ // Statistics stuff
+ //
+
+ // Load autopilot and stats stuff
+ gAgentPilot.load(gSavedSettings.getString("StatsPilotFile").c_str());
+ gFrameStats.setFilename(gSavedSettings.getString("StatsFile"));
+ gFrameStats.setSummaryFilename(gSavedSettings.getString("StatsSummaryFile"));
+
+ //gErrorStream.setTime(gSavedSettings.getBOOL("LogTimestamps"));
+
+ // Load the throttle settings
+ gViewerThrottle.load();
+
+ //
+ // Initialize messaging system
+ //
+ llinfos << "Initializing messaging system..." << llendl;
+
+ std::string message_template_path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"message_template.msg");
+
+ FILE* found_template = NULL;
+ found_template = LLFile::fopen(message_template_path.c_str(), "r");
+ if (found_template)
+ {
+ fclose(found_template);
+
+ if(!start_messaging_system(
+ message_template_path,
+ gAgent.mViewerPort,
+ LL_VERSION_MAJOR,
+ LL_VERSION_MINOR,
+ LL_VERSION_PATCH,
+ FALSE,
+ std::string()))
+ {
+ std::string msg = llformat("Unable to start networking, error %d", gMessageSystem->getErrorCode());
+ app_early_exit(msg);
+ }
+ }
+ else
+ {
+ app_early_exit("Unable to initialize communications.");
+ }
+
+ if(gMessageSystem && gMessageSystem->isOK())
+ {
+ // Initialize all of the callbacks in case of bad message
+ // system data
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setExceptionFunc(MX_UNREGISTERED_MESSAGE,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_PACKET_TOO_SHORT,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_RAN_OFF_END_OF_PACKET,
+ invalid_message_callback,
+ NULL);
+ msg->setExceptionFunc(MX_WROTE_PAST_BUFFER_SIZE,
+ invalid_message_callback,
+ NULL);
+
+ gErrorStream.setUTCTimestamp(gLogUTC);
+ if (gSavedSettings.getBOOL("LogMessages") || gLogMessages)
+ {
+ llinfos << "Message logging activated!" << llendl;
+ msg->startLogging();
+ }
+
+ // start the xfer system. by default, choke the downloads
+ // a lot...
+ const S32 VIEWER_MAX_XFER = 3;
+ start_xfer_manager(gVFS);
+ gXferManager->setMaxIncomingXfers(VIEWER_MAX_XFER);
+ F32 xfer_throttle_bps = gSavedSettings.getF32("XferThrottle");
+ if (xfer_throttle_bps > 1.f)
+ {
+ gXferManager->setUseAckThrottling(TRUE);
+ gXferManager->setAckThrottleBPS(xfer_throttle_bps);
+ }
+ gAssetStorage = new LLViewerAssetStorage(msg, gXferManager, gVFS, gUserServer);
+
+ msg->mPacketRing.setDropPercentage(gPacketDropPercentage);
+ if (gInBandwidth != 0.f)
+ {
+ llinfos << "Setting packetring incoming bandwidth to " << gInBandwidth << llendl;
+ msg->mPacketRing.setUseInThrottle(TRUE);
+ msg->mPacketRing.setInBandwidth(gInBandwidth);
+ }
+ if (gOutBandwidth != 0.f)
+ {
+ llinfos << "Setting packetring outgoing bandwidth to " << gOutBandwidth << llendl;
+ msg->mPacketRing.setUseOutThrottle(TRUE);
+ msg->mPacketRing.setOutBandwidth(gOutBandwidth);
+ }
+ }
+
+ // initialize the economy
+ gGlobalEconomy = new LLGlobalEconomy();
+
+ //---------------------------------------------------------------------
+ // LibXUL (Mozilla) initialization
+ //---------------------------------------------------------------------
+ #if LL_LIBXUL_ENABLED
+ set_startup_status(0.48f, "Initializing embedded web browser...", gAgent.mMOTD.c_str());
+ display_startup();
+
+ #if LL_DARWIN
+ // For Mac OS, we store both the shared libraries and the runtime files (chrome/, plugins/, etc) in
+ // Second Life.app/Contents/MacOS/. This matches the way Firefox is distributed on the Mac.
+ std::string profileBaseDir(gDirUtilp->getExecutableDir());
+ #else
+ std::string profileBaseDir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) );
+ profileBaseDir += gDirUtilp->getDirDelimiter();
+ #ifdef LL_DEBUG
+ profileBaseDir += "mozilla_debug";
+ #else
+ profileBaseDir += "mozilla";
+ #endif
+ #endif
+ LLMozLib::getInstance()->init( profileBaseDir, gDirUtilp->getExpandedFilename( LL_PATH_MOZILLA_PROFILE, "" ) );
+
+ std::ostringstream codec;
+ codec << "[Second Life " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD << "]";
+ LLMozLib::getInstance()->setBrowserAgentId( codec.str() );
+ #endif
+
+ //-------------------------------------------------
+ // Init audio, which may be needed for prefs dialog
+ // or audio cues in connection UI.
+ //-------------------------------------------------
+
+ if (gUseAudio)
+ {
+#if LL_FMOD
+ gAudiop = (LLAudioEngine *) new LLAudioEngine_FMOD();
+#else
+ gAudiop = NULL;
+#endif
+
+ if (gAudiop)
+ {
+#if LL_WINDOWS
+ // FMOD on Windows needs the window handle to stop playing audio
+ // when window is minimized. JC
+ void* window_handle = (void*)llwindow_get_hwnd(gViewerWindow->getWindow());
+#else
+ void* window_handle = NULL;
+#endif
+ BOOL init = gAudiop->init(kAUDIO_NUM_SOURCES, window_handle);
+ if(!init)
+ {
+ llwarns << "Unable to initialize audio engine" << llendl;
+ }
+ gAudiop->setMuted(TRUE);
+ }
+ }
+
+ if (LLTimer::knownBadTimer())
+ {
+ llwarns << "Unreliable timers detected (may be bad PCI chipset)!!" << llendl;
+ }
+
+ // Get ready to show the login dialog
+ if (!gConnectToSomething)
+ {
+ // Don't use a session token, and generate a random user id
+ gAgentID.generate();
+ gAgentSessionID = LLUUID::null;
+
+ gStartupState = STATE_WORLD_INIT;
+ return do_normal_idle;
+ }
+ else if (!gRunLocal)
+ {
+ //
+ // Log on to userserver
+ //
+ if( !gCmdLineFirstName.empty()
+ && !gCmdLineLastName.empty()
+ && !gCmdLinePassword.empty())
+ {
+ firstname = gCmdLineFirstName;
+ lastname = gCmdLineLastName;
+
+ LLMD5 pass((unsigned char*)gCmdLinePassword.c_str());
+ char md5pass[33];
+ pass.hex_digest(md5pass);
+ password = md5pass;
+
+ remember_password = gSavedSettings.getBOOL("RememberPassword");
+ show_connect_box = FALSE;
+ }
+ else if (gAutoLogin || gSavedSettings.getBOOL("AutoLogin"))
+ {
+ firstname = gSavedSettings.getString("FirstName");
+ lastname = gSavedSettings.getString("LastName");
+ password = load_password_from_disk();
+ remember_password = TRUE;
+ show_connect_box = FALSE;
+ }
+ else
+ {
+ // if not automatically logging in, display login dialog
+ // until a valid userserver is selected
+ firstname = gSavedSettings.getString("FirstName");
+ lastname = gSavedSettings.getString("LastName");
+ password = load_password_from_disk();
+ remember_password = gSavedSettings.getBOOL("RememberPassword");
+ show_connect_box = TRUE;
+ }
+
+ // Go to the next startup state
+ gStartupState++;
+ return do_normal_idle;
+ }
+ else
+ {
+ gStartupState++;
+ return do_normal_idle;
+ }
+ }
+
+ if (STATE_LOGIN_SHOW == gStartupState)
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ // Push our window frontmost
+ gViewerWindow->getWindow()->show();
+
+ timeout_count = 0;
+
+ if (gConnectToSomething && !gRunLocal)
+ {
+ if (show_connect_box)
+ {
+ if (gNoRender)
+ {
+ llerrs << "Need to autologin or use command line with norender!" << llendl;
+ }
+ // Make sure the process dialog doesn't hide things
+ gViewerWindow->setShowProgress(FALSE);
+
+ // Show the login dialog
+ login_show();
+
+ // connect dialog is already shown, so fill in the names
+ LLPanelLogin::setFields( firstname, lastname, password, remember_password );
+ LLPanelLogin::giveFocus();
+
+ gSavedSettings.setBOOL("FirstRunThisInstall", FALSE);
+
+ gStartupState++;
+ }
+ else
+ {
+ // skip directly to message template verification
+ gStartupState = STATE_LOGIN_CLEANUP;
+ }
+ }
+ else
+ {
+ gMessageSystem->setCircuitProtection(FALSE);
+ gStartupState = STATE_LOGIN_CLEANUP;
+ }
+
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ if (STATE_LOGIN_WAIT == gStartupState)
+ {
+ // Don't do anything. Wait for the login view to call the login_callback,
+ // which will push us to the next state.
+
+ // Sleep so we don't spin the CPU
+ ms_sleep(1);
+ return do_normal_idle;
+ }
+
+ if (STATE_LOGIN_CLEANUP == gStartupState)
+ {
+ if (show_connect_box)
+ {
+ // Load all the name information out of the login view
+ LLPanelLogin::getFields(firstname, lastname, password, remember_password);
+
+ // HACK: Try to make not jump on login
+ gKeyboard->resetKeys();
+ }
+
+ if (!firstname.empty() && !lastname.empty())
+ {
+ gSavedSettings.setString("FirstName", firstname);
+ gSavedSettings.setString("LastName", lastname);
+
+ llinfos << "Attempting login as: " << firstname << " " << lastname << llendl;
+ write_debug("Attempting login as: ");
+ write_debug(firstname);
+ write_debug(" ");
+ write_debug(lastname);
+ write_debug("\n");
+ }
+
+ // create necessary directories
+ // FIXME: these mkdir's should error check
+ gDirUtilp->setLindenUserDir(firstname.c_str(), lastname.c_str());
+
+
+ LLFile::mkdir(gDirUtilp->getLindenUserDir().c_str());
+
+ // the mute list is loaded in the llmutelist class.
+
+ gSavedSettings.loadFromFile(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml"));
+
+ // handle the per account settings setup
+ strcpy(gPerAccountSettingsFileName, gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, DEFAULT_SETTINGS_FILE).c_str());
+
+ // per account settings. Set defaults here if not found. If we get a bunch of these, eventually move to a function.
+ gSavedPerAccountSettings.loadFromFile(gPerAccountSettingsFileName);
+
+ // Need to set the LastLogoff time here if we don't have one. LastLogoff is used for "Recent Items" calculation
+ // and startup time is close enough if we don't have a real value.
+ if (gSavedPerAccountSettings.getU32("LastLogoff") == 0)
+ {
+ gSavedPerAccountSettings.setU32("LastLogoff", time_corrected());
+ }
+
+ //Default the path if one isn't set.
+ if (gSavedPerAccountSettings.getString("InstantMessageLogPath").empty())
+ {
+ gDirUtilp->setChatLogsDir(gDirUtilp->getOSUserAppDir());
+ gSavedPerAccountSettings.setString("InstantMessageLogPath",gDirUtilp->getChatLogsDir());
+ }
+ else
+ {
+ gDirUtilp->setChatLogsDir(gSavedPerAccountSettings.getString("InstantMessageLogPath"));
+ }
+
+ gDirUtilp->setPerAccountChatLogsDir(firstname.c_str(), lastname.c_str());
+
+ LLFile::mkdir(gDirUtilp->getChatLogsDir().c_str());
+ LLFile::mkdir(gDirUtilp->getPerAccountChatLogsDir().c_str());
+
+
+#if LL_WINDOWS
+ if (gSavedSettings.getBOOL("UseDebugLogin") && show_connect_box)
+#else
+ if (show_connect_box)
+#endif
+ {
+ LLString server_label;
+ S32 domain_name_index;
+ LLPanelLogin::getServer( server_label, domain_name_index );
+ gUserServerChoice = (EUserServerDomain) domain_name_index;
+ gSavedSettings.setS32("ServerChoice", gUserServerChoice);
+ if (gUserServerChoice == USERSERVER_OTHER)
+ {
+ gUserServer.setHostByName( server_label.c_str() );
+ snprintf(gUserServerName, MAX_STRING, "%s", server_label.c_str());
+ }
+ }
+
+ if (show_connect_box)
+ {
+ LLString location;
+ LLPanelLogin::getLocation( location );
+ LLURLSimString::setString( location );
+ LLPanelLogin::close();
+ }
+
+ //For HTML parsing in text boxes.
+ LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") );
+ LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &process_secondlife_url );
+
+ //-------------------------------------------------
+ // Handle startup progress screen
+ //-------------------------------------------------
+
+ // on startup the user can request to go to their home,
+ // their last location, or some URL "-url //sim/x/y[/z]"
+ // All accounts have both a home and a last location, and we don't support
+ // more locations than that. Choose the appropriate one. JC
+ if (LLURLSimString::parse())
+ {
+ // a startup URL was specified
+ agent_location_id = START_LOCATION_ID_TELEHUB;
+
+ // doesn't really matter what location_which is, since
+ // agent_start_look_at will be overwritten when the
+ // UserLoginLocationReply arrives
+ location_which = START_LOCATION_ID_LAST;
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ agent_location_id = START_LOCATION_ID_LAST; // last location
+ location_which = START_LOCATION_ID_LAST;
+ }
+ else
+ {
+ agent_location_id = START_LOCATION_ID_HOME; // home
+ location_which = START_LOCATION_ID_HOME;
+ }
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_WAIT);
+
+ if (!gNoRender)
+ {
+ init_start_screen(agent_location_id);
+ }
+
+ // Display the startup progress bar.
+ gViewerWindow->setShowProgress(TRUE);
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Quit");
+
+ // Poke the VFS, which could potentially block for a while if
+ // Windows XP is acting up
+ set_startup_status(0.05f, "Verifying cache files (can take 60-90 seconds)...", NULL);
+ display_startup();
+
+ gVFS->pokeFiles();
+
+ // color init must be after saved settings loaded
+ init_colors();
+
+ // Request userserver domain name
+ set_startup_status(0.05f, "Finding Server Domain Name...", NULL);
+
+ // We're prematurely switching out of this state because the
+ // userserver name resolver can potentiallly occur before reaching the end of the
+ // switch statement. Also, if it's done at the bottom, sometimes we will
+ // skip the userserver resolved step (in the local cases) - djs 09/24/03
+ gStartupState++;
+ timeout.reset();
+
+ switch( gUserServerChoice )
+ {
+ case USERSERVER_AGNI:
+ gInProductionGrid = TRUE;
+ case USERSERVER_DMZ:
+ case USERSERVER_ADITI:
+ case USERSERVER_SIVA:
+ case USERSERVER_SHAKTI:
+ case USERSERVER_DURGA:
+ case USERSERVER_SOMA:
+ case USERSERVER_GANGA:
+ {
+ const char* host_name = gUserServerDomainName[gUserServerChoice].mName;
+ sprintf(gUserServerName,"%s", host_name);
+ llinfos << "Resolving " <<
+ gUserServerDomainName[gUserServerChoice].mLabel <<
+ " userserver domain name " << host_name << llendl;
+
+ BOOL requested_domain_name = gAsyncHostByName.startRequest( host_name, on_userserver_name_resolved, NULL );
+ if( !requested_domain_name )
+ //BOOL resolved_domain_name = gUserServer.setHostByName( host_name );
+ //if( !resolved_domain_name )
+ {
+ llwarns << "setHostByName failed" << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[HOST_NAME]"] = host_name;
+
+ gViewerWindow->alertXml("UnableToConnect", args, login_alert_done );
+ gStartupState = STATE_LOGIN_SHOW;
+ return FALSE;
+ }
+ break;
+ }
+
+ case USERSERVER_LOCAL:
+ llinfos << "Using local userserver" << llendl;
+ gUserServer.setAddress( LOOPBACK_ADDRESS_STRING );
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ break;
+
+ case USERSERVER_OTHER:
+ llinfos << "Userserver set explicitly" << llendl;
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ break;
+
+ case USERSERVER_NONE:
+ default:
+ llerrs << "No userserver IP address specified" << llendl;
+ break;
+ }
+ return do_normal_idle;
+ }
+
+ if (STATE_RESOLVING_USERSERVER == gStartupState)
+ {
+ // Don't do anything. Wait for LL_WM_HOST_RESOLVED which is handled by LLAsyncHostByName,
+ // which calls on_userserver_name_resolved, which will push us to the next state.
+ if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS*3.f)
+ {
+ // Cancel the pending asynchostbyname request
+
+ gViewerWindow->alertXml("CanNotFindServer",
+ login_alert_status, NULL);
+
+ // Back up to login screen
+ gStartupState = STATE_LOGIN_SHOW;
+ gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
+ }
+ ms_sleep(1);
+ return do_normal_idle;
+ }
+
+ if (STATE_USERSERVER_RESOLVED == gStartupState)
+ {
+ if (!gUserServer.isOk())
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[IP_ADDRESS]"] = u32_to_ip_string( gUserServer.getAddress() );
+
+ gViewerWindow->alertXml("PleaseSelectServer", args, login_alert_done );
+
+ gStartupState = STATE_LOGIN_SHOW;
+ return FALSE;
+ }
+
+ write_debug("Userserver: ");
+ char tmp_str[256];
+ gUserServer.getIPString(tmp_str, 256);
+ write_debug(tmp_str);
+ write_debug("\n");
+
+ gStartupState++;
+ }
+
+ if (STATE_MESSAGE_TEMPLATE_SEND == gStartupState)
+ {
+ set_startup_status(0.10f, "Verifying protocol version...", NULL);
+
+ LLHost mt_host;
+ if (!gRunLocal)
+ {
+ // open up user server circuit (trusted)
+ gMessageSystem->enableCircuit(gUserServer, TRUE);
+ mt_host = gUserServer;
+ }
+ else
+ {
+ mt_host = gAgentSimHost;
+ }
+
+ llinfos << "Verifying message template..." << llendl;
+
+ // register with the message system so it knows we're
+ // expecting this message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, null_message_callback, NULL);
+ msg->newMessageFast(_PREHASH_SecuredTemplateChecksumRequest);
+ msg->nextBlockFast(_PREHASH_TokenBlock);
+ lldebugs << "random token: " << gTemplateToken << llendl;
+ msg->addUUIDFast(_PREHASH_Token, gTemplateToken);
+ msg->sendReliable(mt_host);
+
+ timeout.reset();
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ if (STATE_MESSAGE_TEMPLATE_WAIT == gStartupState)
+ {
+ U32 remote_template_checksum = 0;
+
+ U8 major_version = 0;
+ U8 minor_version = 0;
+ U8 patch_version = 0;
+ U8 server_version = 0;
+ U32 flags = 0x0;
+
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ if (msg->isMessageFast(_PREHASH_TemplateChecksumReply))
+ {
+ LLUUID token;
+ msg->getUUID("TokenBlock", "Token", token);
+ if(token != gTemplateToken)
+ {
+ llwarns << "Incorrect token in template checksum reply: "
+ << token << llendl;
+ return do_normal_idle;
+ }
+ msg->getU32("DataBlock", "Checksum", remote_template_checksum);
+ msg->getU8 ("DataBlock", "MajorVersion", major_version);
+ msg->getU8 ("DataBlock", "MinorVersion", minor_version);
+ msg->getU8 ("DataBlock", "PatchVersion", patch_version);
+ msg->getU8 ("DataBlock", "ServerVersion", server_version);
+ msg->getU32("DataBlock", "Flags", flags);
+
+ BOOL update_available = FALSE;
+ BOOL mandatory = FALSE;
+
+ if (remote_template_checksum != msg->mMessageFileChecksum)
+ {
+ llinfos << "Message template out of sync" << llendl;
+ // Mandatory update -- message template checksum doesn't match
+ update_available = TRUE;
+ mandatory = TRUE;
+ }
+
+ BOOL quit = FALSE;
+ if (update_available)
+ {
+ if (show_connect_box)
+ {
+ update_app(mandatory, "");
+ gStartupState = STATE_UPDATE_CHECK;
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ // Bail out and clean up circuit
+ if (quit)
+ {
+ msg->newMessageFast(_PREHASH_CloseCircuit);
+ msg->sendMessage( msg->getSender() );
+ app_force_quit(NULL);
+ return FALSE;
+ }
+
+ // If we get here, we've got a compatible message template
+
+ if (!mandatory)
+ {
+ llinfos << "Message template is current!" << llendl;
+ }
+ gStartupState = STATE_LOGIN_AUTH_INIT;
+ timeout.reset();
+ // unregister with the message system so it knows we're no longer expecting this message
+ msg->setHandlerFuncFast(_PREHASH_TemplateChecksumReply, NULL, NULL);
+
+ msg->newMessageFast(_PREHASH_CloseCircuit);
+ msg->sendMessage(gUserServer);
+ msg->disableCircuit(gUserServer);
+ if (gRunLocal)
+ {
+ msg->enableCircuit(gAgentSimHost, TRUE);
+
+ // Don't use a session token, and generate a random user id
+ gAgentID.generate();
+ gAgentSessionID = LLUUID::null;
+
+ // Skip userserver queries.
+ gStartupState = STATE_WORLD_INIT;
+ }
+ }
+ }
+ gMessageSystem->processAcks();
+
+ if (timeout.getElapsedTimeF32() > TIMEOUT_SECONDS)
+ {
+ if (timeout_count > MAX_TIMEOUT_COUNT)
+ {
+ gViewerWindow->alertXml("SystemMayBeDown",
+ login_alert_status,
+ NULL);
+
+ // Back up to login screen
+ gStartupState = STATE_LOGIN_SHOW;
+ gViewerStats->incStat(LLViewerStats::ST_LOGIN_TIMEOUT_COUNT);
+ }
+ else
+ {
+ llinfos << "Resending on timeout" << llendl;
+ gStartupState--;
+ timeout_count++;
+ }
+ }
+
+ return do_normal_idle;
+ }
+
+ if (STATE_UPDATE_CHECK == gStartupState)
+ {
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_AUTH_INIT == gStartupState)
+ {
+//#define LL_MINIMIAL_REQUESTED_OPTIONS
+ lldebugs << "STATE_LOGIN_AUTH_INIT" << llendl;
+ if (!gUserAuthp)
+ {
+ gUserAuthp = new LLUserAuth();
+ }
+ requested_options.clear();
+ requested_options.push_back("inventory-root");
+ requested_options.push_back("inventory-skeleton");
+ //requested_options.push_back("inventory-meat");
+ //requested_options.push_back("inventory-skel-targets");
+#if (!defined LL_MINIMIAL_REQUESTED_OPTIONS)
+ if(gRequestInventoryLibrary)
+ {
+ requested_options.push_back("inventory-lib-root");
+ requested_options.push_back("inventory-lib-owner");
+ requested_options.push_back("inventory-skel-lib");
+ // requested_options.push_back("inventory-meat-lib");
+ }
+
+ requested_options.push_back("initial-outfit");
+ requested_options.push_back("gestures");
+ requested_options.push_back("event_categories");
+ requested_options.push_back("event_notifications");
+ requested_options.push_back("classified_categories");
+ //requested_options.push_back("inventory-targets");
+ requested_options.push_back("buddy-list");
+ requested_options.push_back("ui-config");
+#endif
+ requested_options.push_back("login-flags");
+ requested_options.push_back("global-textures");
+ if(gGodConnect)
+ {
+ gSavedSettings.setBOOL("UseDebugMenus", TRUE);
+ requested_options.push_back("god-connect");
+ }
+ auth_uri = getLoginURI();
+ auth_method = "login_to_simulator";
+ auth_desc = "Logging in. ";
+ auth_desc += gSecondLife;
+ auth_desc += " may appear frozen. Please wait.";
+ ++gStartupState;
+ }
+
+ if (STATE_LOGIN_AUTHENTICATE == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_AUTHENTICATE" << llendl;
+ set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
+ progress += 0.02f;
+ display_startup();
+
+ std::stringstream start;
+ if (LLURLSimString::parse())
+ {
+ // a startup URL was specified
+ std::stringstream unescaped_start;
+ unescaped_start << "uri:"
+ << LLURLSimString::sInstance.mSimName << "&"
+ << LLURLSimString::sInstance.mX << "&"
+ << LLURLSimString::sInstance.mY << "&"
+ << LLURLSimString::sInstance.mZ;
+ start << xml_escape_string(unescaped_start.str().c_str());
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ start << "last";
+ }
+ else
+ {
+ start << "home";
+ }
+
+ char hashed_mac_string[MD5HEX_STR_SIZE];
+ LLMD5 hashed_mac;
+ hashed_mac.update( gMACAddress, MAC_ADDRESS_BYTES );
+ hashed_mac.finalize();
+ hashed_mac.hex_digest(hashed_mac_string);
+
+ gUserAuthp->authenticate(
+ auth_uri.c_str(),
+ auth_method.c_str(),
+ firstname.c_str(),
+ lastname.c_str(),
+ password.c_str(),
+ start.str().c_str(),
+ gSkipOptionalUpdate,
+ gAcceptTOS,
+ gAcceptCriticalMessage,
+ gViewerDigest,
+ gLastExecFroze,
+ requested_options,
+ hashed_mac_string,
+ gSerialNumber);
+ // reset globals
+ gAcceptTOS = FALSE;
+ gAcceptCriticalMessage = FALSE;
+ ++gStartupState;
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_NO_DATA_YET == gStartupState)
+ {
+ //lldebugs << "STATE_LOGIN_NO_DATA_YET" << llendl;
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_LOGIN_NO_DATA_YET!" << llendl;
+ }
+ // Process messages to keep from dropping circuit.
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ if(LLUserAuth::E_NO_RESPONSE_YET == error)
+ {
+ //llinfos << "waiting..." << llendl;
+ return do_normal_idle;
+ }
+ ++gStartupState;
+ progress += 0.01f;
+ set_startup_status(progress, auth_desc.c_str(), auth_message.c_str());
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_DOWNLOADING == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_DOWNLOADING" << llendl;
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_LOGIN_DOWNLOADING!" << llendl;
+ }
+ // Process messages to keep from dropping circuit.
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ if(LLUserAuth::E_DOWNLOADING == error)
+ {
+ //llinfos << "downloading..." << llendl;
+ return do_normal_idle;
+ }
+ ++gStartupState;
+ progress += 0.01f;
+ set_startup_status(progress, "Processing Response...", auth_message.c_str());
+ return do_normal_idle;
+ }
+
+ if(STATE_LOGIN_PROCESS_RESPONSE == gStartupState)
+ {
+ lldebugs << "STATE_LOGIN_PROCESS_RESPONSE" << llendl;
+ std::ostringstream emsg;
+ BOOL quit = FALSE;
+ const char* login_response = NULL;
+ const char* reason_response = NULL;
+ const char* message_response = NULL;
+ BOOL successful_login = FALSE;
+ LLUserAuth::UserAuthcode error = gUserAuthp->authResponse();
+ // reset globals
+ gAcceptTOS = FALSE;
+ gAcceptCriticalMessage = FALSE;
+ switch(error)
+ {
+ case LLUserAuth::E_OK:
+ login_response = gUserAuthp->getResponse("login");
+ if(login_response && (0 == strcmp(login_response, "true")))
+ {
+ // Yay, login!
+ successful_login = TRUE;
+ }
+ else if(login_response && (0 == strcmp(login_response, "indeterminate")))
+ {
+ llinfos << "Indeterminate login..." << llendl;
+ auth_uri = gUserAuthp->getResponse("next_url");
+ auth_method = gUserAuthp->getResponse("next_method");
+ auth_message = gUserAuthp->getResponse("message");
+ if(auth_method.substr(0, 5) == "login")
+ {
+ auth_desc.assign("Authenticating...");
+ }
+ else
+ {
+ auth_desc.assign("Performing account maintenance...");
+ }
+ // ignoring the duration & options array for now.
+ // Go back to authenticate.
+ gStartupState = STATE_LOGIN_AUTHENTICATE;
+ return do_normal_idle;
+ }
+ else
+ {
+ emsg << "Login failed.\n";
+ reason_response = gUserAuthp->getResponse("reason");
+ message_response = gUserAuthp->getResponse("message");
+
+ if (gHideLinks && reason_response && (0 == strcmp(reason_response, "disabled")))
+ {
+ emsg << gDisabledMessage;
+ }
+ else if (message_response)
+ {
+ emsg << message_response;
+ }
+
+ if(reason_response && (0 == strcmp(reason_response, "tos")))
+ {
+ if (show_connect_box)
+ {
+ llinfos << "Need tos agreement" << llendl;
+ gStartupState = STATE_UPDATE_CHECK;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_TOS,
+ message_response);
+ tos_dialog->startModal();
+ // LLFloaterTOS deletes itself.
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "critical")))
+ {
+ if (show_connect_box)
+ {
+ llinfos << "Need critical message" << llendl;
+ gStartupState = STATE_UPDATE_CHECK;
+ LLFloaterTOS* tos_dialog = LLFloaterTOS::show(LLFloaterTOS::TOS_CRITICAL_MESSAGE,
+ message_response);
+ tos_dialog->startModal();
+ // LLFloaterTOS deletes itself.
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "key")))
+ {
+ // Couldn't login because user/password is wrong
+ // Clear the password
+ password = "";
+ }
+ if(reason_response && (0 == strcmp(reason_response, "update")))
+ {
+ auth_message = gUserAuthp->getResponse("message");
+ if (show_connect_box)
+ {
+ update_app(TRUE, auth_message);
+ gStartupState = STATE_UPDATE_CHECK;
+ return FALSE;
+ }
+ else
+ {
+ quit = TRUE;
+ }
+ }
+ if(reason_response && (0 == strcmp(reason_response, "optional")))
+ {
+ llinfos << "Login got optional update" << llendl;
+ auth_message = gUserAuthp->getResponse("message");
+ if (show_connect_box)
+ {
+ update_app(FALSE, auth_message);
+ gStartupState = STATE_UPDATE_CHECK;
+ gSkipOptionalUpdate = TRUE;
+ return FALSE;
+ }
+ }
+ }
+ break;
+ case LLUserAuth::E_COULDNT_RESOLVE_HOST:
+ case LLUserAuth::E_SSL_PEER_CERTIFICATE:
+ case LLUserAuth::E_UNHANDLED_ERROR:
+ default:
+ emsg << "Unable to connect to " << gSecondLife << ".\n";
+ emsg << gUserAuthp->errorMessage();
+ break;
+ case LLUserAuth::E_SSL_CACERT:
+ case LLUserAuth::E_SSL_CONNECT_ERROR:
+ emsg << "Unable to establish a secure connection to the login server.\n";
+ emsg << gUserAuthp->errorMessage();
+ break;
+ }
+
+ // Version update and we're not showing the dialog
+ if(quit)
+ {
+ delete gUserAuthp;
+ gUserAuthp = NULL;
+ app_force_quit(NULL);
+ return FALSE;
+ }
+
+ if(successful_login)
+ {
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth on successful login!" << llendl;
+ }
+
+ // unpack login data needed by the application
+ const char* text;
+ text = gUserAuthp->getResponse("agent_id");
+ if(text) gAgentID.set(text);
+ write_debug("AgentID: ");
+ write_debug(text);
+ write_debug("\n");
+
+ text = gUserAuthp->getResponse("session_id");
+ if(text) gAgentSessionID.set(text);
+ write_debug("SessionID: ");
+ write_debug(text);
+ write_debug("\n");
+
+ text = gUserAuthp->getResponse("secure_session_id");
+ if(text) gAgent.mSecureSessionID.set(text);
+
+ text = gUserAuthp->getResponse("first_name");
+ if(text)
+ {
+ // Remove quotes from string. Login.cgi sends these to force
+ // names that look like numbers into strings.
+ firstname.assign(text);
+ LLString::replaceChar(firstname, '"', ' ');
+ LLString::trim(firstname);
+ }
+ text = gUserAuthp->getResponse("last_name");
+ if(text) lastname.assign(text);
+ gSavedSettings.setString("FirstName", firstname);
+ gSavedSettings.setString("LastName", lastname);
+ if (remember_password)
+ {
+ save_password_to_disk(password.c_str());
+ }
+ else
+ {
+ save_password_to_disk(NULL);
+ }
+ gSavedSettings.setBOOL("RememberPassword", remember_password);
+ gSavedSettings.setBOOL("LoginLastLocation", gSavedSettings.getBOOL("LoginLastLocation"));
+ gSavedSettings.setBOOL("LoggedIn", TRUE);
+
+ text = gUserAuthp->getResponse("agent_access");
+ if(text && (text[0] == 'M'))
+ {
+ gAgent.mAccess = SIM_ACCESS_MATURE;
+ }
+ else
+ {
+ gAgent.mAccess = SIM_ACCESS_PG;
+ }
+
+ text = gUserAuthp->getResponse("start_location");
+ if(text) agent_start_location.assign(text);
+ text = gUserAuthp->getResponse("circuit_code");
+ if(text)
+ {
+ gMessageSystem->mOurCircuitCode = strtoul(text, NULL, 10);
+ }
+ const char* sim_ip_str = gUserAuthp->getResponse("sim_ip");
+ const char* sim_port_str = gUserAuthp->getResponse("sim_port");
+ if(sim_ip_str && sim_port_str)
+ {
+ U32 sim_port = strtoul(sim_port_str, NULL, 10);
+ first_sim.set(sim_ip_str, sim_port);
+ if (first_sim.isOk())
+ {
+ gMessageSystem->enableCircuit(first_sim, TRUE);
+ }
+ }
+ const char* region_x_str = gUserAuthp->getResponse("region_x");
+ const char* region_y_str = gUserAuthp->getResponse("region_y");
+ if(region_x_str && region_y_str)
+ {
+ U32 region_x = strtoul(region_x_str, NULL, 10);
+ U32 region_y = strtoul(region_y_str, NULL, 10);
+ first_sim_handle = to_region_handle(region_x, region_y);
+ }
+
+ const char* look_at_str = gUserAuthp->getResponse("look_at");
+ if (look_at_str)
+ {
+ LLMemoryStream mstr((U8*)look_at_str, strlen(look_at_str));
+ LLSD sd = LLSDNotationParser::parse(mstr);
+ agent_start_look_at = ll_vector3_from_sd(sd);
+ }
+
+ text = gUserAuthp->getResponse("seed_capability");
+ if (text) first_sim_seed_cap = text;
+
+ text = gUserAuthp->getResponse("seconds_since_epoch");
+ if(text)
+ {
+ U32 server_utc_time = strtoul(text, NULL, 10);
+ if(server_utc_time)
+ {
+ time_t now = time(NULL);
+ gUTCOffset = (server_utc_time - now);
+ }
+ }
+
+ const char* home_location = gUserAuthp->getResponse("home");
+ if(home_location)
+ {
+ LLMemoryStream mstr((U8*)home_location, strlen(home_location));
+ LLSD sd = LLSDNotationParser::parse(mstr);
+ S32 region_x = sd["region_handle"][0].asInteger();
+ S32 region_y = sd["region_handle"][1].asInteger();
+ U64 region_handle = to_region_handle(region_x, region_y);
+ LLVector3 position = ll_vector3_from_sd(sd["position"]);
+ gAgent.setHomePosRegion(region_handle, position);
+ }
+
+ gAgent.mMOTD.assign(gUserAuthp->getResponse("message"));
+ LLUserAuth::options_t options;
+ if(gUserAuthp->getOptions("inventory-root", options))
+ {
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("folder_id");
+ if(it != options[0].end())
+ {
+ gAgent.mInventoryRootID.set((*it).second.c_str());
+ //gInventory.mock(gAgent.getInventoryRootID());
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("login-flags", options))
+ {
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator no_flag = options[0].end();
+ it = options[0].find("ever_logged_in");
+ if(it != no_flag)
+ {
+ if((*it).second == "N") gAgent.setFirstLogin(TRUE);
+ else gAgent.setFirstLogin(FALSE);
+ }
+ it = options[0].find("stipend_since_login");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") stipend_since_login = TRUE;
+ }
+ it = options[0].find("gendered");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") gAgent.setGenderChosen(TRUE);
+ }
+ it = options[0].find("daylight_savings");
+ if(it != no_flag)
+ {
+ if((*it).second == "Y") gPacificDaylightTime = TRUE;
+ else gPacificDaylightTime = FALSE;
+ }
+ }
+ options.clear();
+ if (gUserAuthp->getOptions("initial-outfit", options)
+ && !options.empty())
+ {
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator it_end = options[0].end();
+ it = options[0].find("folder_name");
+ if(it != it_end)
+ {
+ gInitialOutfit = (*it).second;
+ }
+ it = options[0].find("gender");
+ if (it != it_end)
+ {
+ gInitialOutfitGender = (*it).second;
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("global-textures", options))
+ {
+ // Extract sun and moon texture IDs. These are used
+ // in the LLVOSky constructor, but I can't figure out
+ // how to pass them in. JC
+ LLUserAuth::response_t::iterator it;
+ LLUserAuth::response_t::iterator no_texture = options[0].end();
+ it = options[0].find("sun_texture_id");
+ if(it != no_texture)
+ {
+ gSunTextureID.set((*it).second.c_str());
+ }
+ it = options[0].find("moon_texture_id");
+ if(it != no_texture)
+ {
+ gMoonTextureID.set((*it).second.c_str());
+ }
+ it = options[0].find("cloud_texture_id");
+ if(it != no_texture)
+ {
+ gCloudTextureID.set((*it).second.c_str());
+ }
+ }
+
+
+ // JC: gesture loading done below, when we have an asset system
+ // in place. Don't delete/clear user_credentials until then.
+
+ if(gAgentID.notNull()
+ && gAgentSessionID.notNull()
+ && gMessageSystem->mOurCircuitCode
+ && first_sim.isOk()
+ && gAgent.mInventoryRootID.notNull())
+ {
+ ++gStartupState;
+ }
+ else
+ {
+ if (gNoRender)
+ {
+ llinfos << "Bad login - missing return values" << llendl;
+ llinfos << emsg << llendl;
+ exit(0);
+ }
+ // Bounce back to the login screen.
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = emsg.str();
+ gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ }
+ else
+ {
+ if (gNoRender)
+ {
+ llinfos << "Failed to login!" << llendl;
+ llinfos << emsg << llendl;
+ exit(0);
+ }
+ // Bounce back to the login screen.
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = emsg.str();
+ gViewerWindow->alertXml("ErrorMessage", args, login_alert_done);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // World Init
+ //---------------------------------------------------------------------
+ if (STATE_WORLD_INIT == gStartupState)
+ {
+ set_startup_status(0.40f, "Initializing World...", gAgent.mMOTD.c_str());
+ display_startup();
+ // We should have an agent id by this point.
+ llassert(!(gAgentID == LLUUID::null));
+
+ // Finish agent initialization. (Requires gSavedSettings, builds camera)
+ gAgent.init();
+
+ // Since we connected, save off the settings so the user doesn't have to
+ // type the name/password again if we crash.
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ // Create selection manager
+ // Must be done before menus created, because many enabled callbacks
+ // require its existance.
+ gSelectMgr = new LLSelectMgr();
+ gParcelMgr = new LLViewerParcelMgr();
+ gHUDManager = new LLHUDManager();
+ gMuteListp = new LLMuteList();
+
+ //
+ // Initialize classes w/graphics stuff.
+ //
+ LLSurface::initClasses();
+
+ LLFace::initClass();
+
+ LLDrawable::initClass();
+
+ // RN: don't initialize VO classes in drone mode, they are too closely tied to rendering
+ LLViewerObject::initVOClasses();
+
+ display_startup();
+
+ // World initialization must be done after above window init
+ gWorldp = new LLWorld(region_size, region_scale);
+
+ // User might have overridden far clip
+ gWorldp->setLandFarClip( gAgent.mDrawDistance );
+
+ if (!gRunLocal)
+ {
+ // Before we create the first region, we need to set the agent's mOriginGlobal
+ // This is necessary because creating objects before this is set will result in a
+ // bad mPositionAgent cache.
+
+ gAgent.initOriginGlobal(from_region_handle(first_sim_handle));
+
+ gWorldp->addRegion(first_sim_handle, first_sim);
+
+ LLViewerRegion *regionp = gWorldp->getRegionFromHandle(first_sim_handle);
+ llinfos << "Adding initial simulator " << regionp->getOriginGlobal() << llendl;
+
+ regionp->setSeedCapability(first_sim_seed_cap);
+
+ // Set agent's initial region to be the one we just created.
+ gAgent.setRegion(regionp);
+
+ // Set agent's initial position, which will be read by LLVOAvatar when the avatar
+ // object is created. I think this must be done after setting the region. JC
+ gAgent.setPositionAgent(agent_start_position_region);
+ }
+ else
+ {
+ // With one simulator, assume region is at 0,0, hence has regionHandle 0
+ // VEFFECT: Login
+ gWorldp->addRegion(0, gAgentSimHost);
+ gAgent.setRegion(gWorldp->getRegionFromHandle(0));
+ }
+
+ display_startup();
+
+ // Initialize UI
+ if (!gNoRender)
+ {
+ // Initialize all our tools. Must be done after saved settings loaded.
+ gToolMgr = new LLToolMgr();
+ gToolMgr->initTools();
+ // Quickly get something onscreen to look at.
+ gViewerWindow->initWorldUI();
+
+ // Move the progress view in front of the UI
+ gViewerWindow->moveProgressViewToFront();
+
+ gErrorStream.setFixedBuffer(gDebugView->mDebugConsolep);
+ // set initial visibility of debug console
+ gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));
+ gDebugView->mStatViewp->setVisible(gSavedSettings.getBOOL("ShowDebugStats"));
+ }
+
+ //
+ // Set message handlers
+ //
+ llinfos << "Initializing communications..." << llendl;
+
+ // register callbacks for messages. . . do this after initial handshake to make sure that we don't catch any unwanted
+ register_viewer_callbacks(gMessageSystem);
+
+ // Debugging info parameters
+ gMessageSystem->setMaxMessageTime( 0.5f ); // Spam if decoding all msgs takes more than 500 ms
+
+ #ifndef LL_RELEASE_FOR_DOWNLOAD
+ gMessageSystem->setTimeDecodes( TRUE ); // Time the decode of each msg
+ gMessageSystem->setTimeDecodesSpamThreshold( 0.05f ); // Spam if a single msg takes over 50ms to decode
+ #endif
+
+ gXferManager->registerCallbacks(gMessageSystem);
+
+ gCacheName = new LLCacheName(gMessageSystem);
+ gCacheName->addObserver(callback_cache_name);
+
+ // Load stored cache if possible
+ load_name_cache();
+
+ // Data storage for map of world.
+ gWorldMap = new LLWorldMap();
+
+ // register null callbacks for audio until the audio system is initialized
+ gMessageSystem->setHandlerFuncFast(_PREHASH_SoundTrigger, null_message_callback, NULL);
+ gMessageSystem->setHandlerFuncFast(_PREHASH_AttachedSound, null_message_callback, NULL);
+
+ //reset statistics
+ gViewerStats->resetStats();
+
+ if (!gNoRender)
+ {
+ //
+ // Set up all of our statistics UI stuff.
+ //
+ init_stat_view();
+ }
+
+ display_startup();
+ //
+ // Set up region and surface defaults
+ //
+
+
+ // Sets up the parameters for the first simulator
+
+ llinfos << "Initializing camera..." << llendl;
+ gFrameTime = totalTime();
+ F32 last_time = gFrameTimeSeconds;
+ gFrameTimeSeconds = (S64)(gFrameTime - gStartTime)/SEC_TO_MICROSEC;
+
+ gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
+ if (gFrameIntervalSeconds < 0.f)
+ {
+ gFrameIntervalSeconds = 0.f;
+ }
+
+ // Make sure agent knows correct aspect ratio
+ gCamera->setViewHeightInPixels(gViewerWindow->getWindowDisplayHeight());
+ if (gViewerWindow->mWindow->getFullscreen())
+ {
+ gCamera->setAspect(gViewerWindow->getDisplayAspectRatio());
+ }
+ else
+ {
+ gCamera->setAspect( (F32) gViewerWindow->getWindowWidth() / (F32) gViewerWindow->getWindowHeight());
+ }
+
+ // Move agent to starting location. The position handed to us by
+ // the space server is in global coordinates, but the agent frame
+ // is in region local coordinates. Therefore, we need to adjust
+ // the coordinates handed to us to fit in the local region.
+
+ gAgent.setPositionAgent(agent_start_position_region);
+ gAgent.resetAxes(agent_start_look_at);
+ gAgent.stopCameraAnimation();
+ gAgent.resetCamera();
+
+ // Initialize global class data needed for surfaces (i.e. textures)
+ if (!gNoRender)
+ {
+ llinfos << "Initializing sky..." << llendl;
+ // Initialize all of the viewer object classes for the first time (doing things like texture fetches.
+ gSky.init(initial_sun_direction);
+ }
+
+ set_startup_status(0.45f, "Decoding UI images...", gAgent.mMOTD.c_str());
+ display_startup();
+ llinfos << "Decoding images..." << llendl;
+ // For all images pre-loaded into viewer cache, decode them.
+ // Need to do this AFTER we init the sky
+ gImageList.decodeAllImages();
+ gStartupState++;
+
+ // JC - Do this as late as possible to increase likelihood Purify
+ // will run.
+ if (!gRunLocal)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ if (!msg->mOurCircuitCode)
+ {
+ llwarns << "Attempting to connect to simulator with a zero circuit code!" << llendl;
+ }
+ msg->enableCircuit(first_sim, TRUE);
+ // now, use the circuit info to tell simulator about us!
+ llinfos << "viewer: UserLoginLocationReply() Enabling " << first_sim << " with code " << msg->mOurCircuitCode << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->mOurCircuitCode);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(
+ first_sim,
+ MAX_TIMEOUT_COUNT,
+ FALSE,
+ TIMEOUT_SECONDS,
+ use_circuit_callback,
+ NULL);
+ }
+
+ timeout.reset();
+ if (!gConnectToSomething)
+ {
+ gStartupState = STATE_MISC;
+ }
+
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // LLMediaEngine Init
+ //---------------------------------------------------------------------
+ if (STATE_QUICKTIME_INIT == gStartupState)
+ {
+ if (gViewerWindow)
+ {
+ if (gSavedSettings.getBOOL("MuteAudio"))
+ {
+ LLMediaEngine::updateClass( 0.0f );
+ }
+ else
+ {
+ LLMediaEngine::updateClass( gSavedSettings.getF32( "MediaAudioVolume" ) );
+ }
+ }
+
+ #if LL_QUICKTIME_ENABLED // windows only right now but will be ported to mac
+ if (!gQuickTimeInitialized)
+ {
+ // initialize quicktime libraries (fails gracefully if quicktime not installed ($QUICKTIME)
+ llinfos << "Initializing QuickTime...." << llendl;
+ set_startup_status(0.47f, "Initializing QuickTime...", gAgent.mMOTD.c_str());
+ display_startup();
+ #if LL_WINDOWS
+ // Only necessary/available on Windows.
+ if ( InitializeQTML ( 0L ) != noErr )
+ {
+ // quicktime init failed - turn off media engine support
+ LLMediaEngine::getInstance ()->setAvailable ( FALSE );
+ llinfos << "...not found - unable to initialize." << llendl;
+ set_startup_status(0.47f, "QuickTime not found - unable to initialize.", gAgent.mMOTD.c_str());
+ }
+ else
+ {
+ llinfos << ".. initialized successfully." << llendl;
+ set_startup_status(0.47f, "QuickTime initialized successfully.", gAgent.mMOTD.c_str());
+ };
+ #endif
+ EnterMovies ();
+ gQuickTimeInitialized = true;
+ }
+ #endif
+
+ // Get list of URLs approved for usage
+ // CP: removed since they're not useful without Mozilla enabled
+ #if LL_MOZILLA_ENABLED
+ LLUrlWhiteList::getInstance()->load();
+ #endif
+
+ // initialize mozilla if we're using web page on a prim or not using an external browser for floater
+ BOOL use_web_pages_on_prims = gSavedSettings.getBOOL("UseWebPagesOnPrims");
+ BOOL use_external_browser = gSavedSettings.getBOOL("UseExternalBrowser");
+
+use_external_browser = false;
+
+ if (use_web_pages_on_prims || !use_external_browser)
+ {
+ //llinfos << "Initializing web browser...." << llendl;
+ //set_startup_status(0.48f, "Initializing web browser...", gAgent.mMOTD.c_str());
+ //display_startup();
+ // initialize mozilla
+ LLString mozilla_path = gDirUtilp->getExecutableDir();
+ mozilla_path.append( gDirUtilp->getDirDelimiter() );
+#if LL_DEBUG
+ mozilla_path.append( "mozilla_debug" );
+#else
+ mozilla_path.append( "mozilla" );
+#endif
+
+#if LL_MOZILLA_ENABLED
+ if (!gMozillaInitialized)
+ {
+ void* platform_window = gViewerWindow->getPlatformWindow();
+ mozilla_init_embedding(platform_window, mozilla_path);
+ }
+#endif
+
+ if (use_web_pages_on_prims)
+ {
+ gMediaList = new LLViewerMediaList(2);
+ }
+ }
+
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Send
+ //---------------------------------------------------------------------
+ if(STATE_WORLD_WAIT == gStartupState)
+ {
+ //llinfos << "Waiting for simulator ack...." << llendl;
+ set_startup_status(0.49f, "Waiting for region handshake...", gAgent.mMOTD.c_str());
+ if(gGotUseCircuitCodeAck)
+ {
+ ++gStartupState;
+ }
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ }
+ msg->processAcks();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Send
+ //---------------------------------------------------------------------
+ if (STATE_AGENT_SEND == gStartupState)
+ {
+ llinfos << "Connecting to region..." << llendl;
+ set_startup_status(0.50f, "Connecting to region...", gAgent.mMOTD.c_str());
+ // register with the message system so it knows we're
+ // expecting this message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(
+ _PREHASH_AgentMovementComplete,
+ process_agent_movement_complete,
+ NULL);
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if(!gRunLocal && regionp)
+ {
+ send_complete_agent_movement(regionp->getHost());
+ gAssetStorage->setUpstream(regionp->getHost());
+ gCacheName->setUpstream(regionp->getHost());
+ msg->newMessageFast(_PREHASH_EconomyDataRequest);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ gStartupState++;
+ }
+
+ // Create login effect
+ // But not on first login, because you can't see your avatar then
+ if (!gAgent.isFirstLogin())
+ {
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+ }
+
+ gStartupState++;
+
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Agent Wait
+ //---------------------------------------------------------------------
+ if (STATE_AGENT_WAIT == gStartupState)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ while (msg->checkMessages(gFrameCount))
+ {
+ if (msg->isMessageFast(_PREHASH_AgentMovementComplete))
+ {
+ gStartupState++;
+ // Sometimes we have more than one message in the
+ // queue. break out of this loop and continue
+ // processing. If we don't, then this could skip one
+ // or more login steps.
+ break;
+ }
+ else
+ {
+ //llinfos << "Awaiting AvatarInitComplete, got "
+ //<< msg->getMessageName() << llendl;
+ }
+ }
+ msg->processAcks();
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Inventory Send
+ //---------------------------------------------------------------------
+ if (STATE_INVENTORY_SEND == gStartupState)
+ {
+ if (!gUserAuthp)
+ {
+ llerrs << "No userauth in STATE_INVENTORY_SEND!" << llendl;
+ }
+
+ if (gConnectToSomething && !gRunLocal)
+ {
+ // unpack thin inventory
+ LLUserAuth::options_t options;
+ options.clear();
+ //bool dump_buffer = false;
+
+ if(gUserAuthp->getOptions("inventory-lib-root", options)
+ && !options.empty())
+ {
+ // should only be one
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("folder_id");
+ if(it != options[0].end())
+ {
+ gInventoryLibraryRoot.set((*it).second.c_str());
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-lib-owner", options)
+ && !options.empty())
+ {
+ // should only be one
+ LLUserAuth::response_t::iterator it;
+ it = options[0].find("agent_id");
+ if(it != options[0].end())
+ {
+ gInventoryLibraryOwner.set((*it).second.c_str());
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-skel-lib", options)
+ && gInventoryLibraryOwner.notNull())
+ {
+ if(!gInventory.loadSkeleton(options, gInventoryLibraryOwner))
+ {
+ llwarns << "Problem loading inventory-skel-lib" << llendl;
+ }
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("inventory-skeleton", options))
+ {
+ if(!gInventory.loadSkeleton(options, gAgent.getID()))
+ {
+ llwarns << "Problem loading inventory-skel-targets"
+ << llendl;
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("buddy-list", options))
+ {
+ LLUserAuth::options_t::iterator it = options.begin();
+ LLUserAuth::options_t::iterator end = options.end();
+ LLAvatarTracker::buddy_map_t list;
+ LLUUID agent_id;
+ S32 has_rights = 0, given_rights = 0;
+ for (; it != end; ++it)
+ {
+ LLUserAuth::response_t::const_iterator option_it;
+ option_it = (*it).find("buddy_id");
+ if(option_it != (*it).end())
+ {
+ agent_id.set((*option_it).second.c_str());
+ }
+ option_it = (*it).find("buddy_rights_has");
+ if(option_it != (*it).end())
+ {
+ has_rights = atoi((*option_it).second.c_str());
+ }
+ option_it = (*it).find("buddy_rights_given");
+ if(option_it != (*it).end())
+ {
+ given_rights = atoi((*option_it).second.c_str());
+ }
+ list[agent_id] = new LLRelationship(given_rights, has_rights, false);
+ }
+ LLAvatarTracker::instance().addBuddyList(list);
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("ui-config", options))
+ {
+ LLUserAuth::options_t::iterator it = options.begin();
+ LLUserAuth::options_t::iterator end = options.end();
+ for (; it != end; ++it)
+ {
+ LLUserAuth::response_t::const_iterator option_it;
+ option_it = (*it).find("allow_first_life");
+ if(option_it != (*it).end())
+ {
+ if (option_it->second == "Y")
+ {
+ LLPanelAvatar::sAllowFirstLife = TRUE;
+ }
+ }
+ }
+ }
+
+ options.clear();
+ if(gUserAuthp->getOptions("event_categories", options))
+ {
+ LLEventInfo::loadCategories(options);
+ }
+ if(gUserAuthp->getOptions("event_notifications", options))
+ {
+ gEventNotifier.load(options);
+ }
+ options.clear();
+ if(gUserAuthp->getOptions("classified_categories", options))
+ {
+ LLClassifiedInfo::loadCategories(options);
+ }
+ gInventory.buildParentChildMap();
+ gInventory.addChangedMask(LLInventoryObserver::ALL, LLUUID::null);
+ gInventory.notifyObservers();
+
+ // set up callbacks
+ LLMessageSystem* msg = gMessageSystem;
+ LLInventoryModel::registerCallbacks(msg);
+ LLAvatarTracker::instance().registerCallbacks(msg);
+ LLLandmark::registerCallbacks(msg);
+
+ // request mute list
+ gMuteListp->requestFromServer(gAgent.getID());
+
+ // Get money and ownership credit information
+ msg->newMessageFast(_PREHASH_MoneyBalanceRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_TransactionID, LLUUID::null );
+ gAgent.sendReliableMessage();
+
+ // request all group information
+ // *FIX: This will not do the right thing if the message
+ // gets there before the requestuserserverconnection
+ // circuit is completed.
+ gAgent.sendAgentDataUpdateRequest();
+
+
+ // NOTE: removed as part of user-privacy
+ // enhancements. this information should be available from
+ // login. 2006-10-16 Phoenix.
+ // get the users that have been granted modify powers
+ //msg->newMessageFast(_PREHASH_RequestGrantedProxies);
+ //msg->nextBlockFast(_PREHASH_AgentData);
+ //msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ //msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ //gAgent.sendReliableMessage();
+
+ BOOL shown_at_exit = gSavedSettings.getBOOL("ShowInventory");
+
+ // Create the inventory views
+ LLInventoryView::showAgentInventory();
+
+ // Hide the inventory if it wasn't shown at exit
+ if(!shown_at_exit)
+ {
+ LLInventoryView::toggleVisibility(NULL);
+ }
+ }
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Assert agent to userserver
+ //---------------------------------------------------------------------
+ if (STATE_CONNECT_USERSERVER == gStartupState)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->enableCircuit(gUserServer, TRUE);
+ msg->newMessage("ConnectAgentToUserserver");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->sendReliable(gUserServer);
+ gStartupState++;
+ return do_normal_idle;
+ }
+
+ //---------------------------------------------------------------------
+ // Misc
+ //---------------------------------------------------------------------
+ if (STATE_MISC == gStartupState)
+ {
+ // Create a few objects if we don't actually have a world
+ if (!gConnectToSomething)
+ {
+ // could add them here
+ }
+
+ // We have a region, and just did a big inventory download.
+ // We can estimate the user's connection speed, and set their
+ // max bandwidth accordingly. JC
+ if (gSavedSettings.getBOOL("FirstLoginThisInstall")
+ && gUserAuthp)
+ {
+ // This is actually a pessimistic computation, because TCP may not have enough
+ // time to ramp up on the (small) default inventory file to truly measure max
+ // bandwidth. JC
+ F64 rate_bps = gUserAuthp->getLastTransferRateBPS();
+ const F32 FAST_RATE_BPS = 600.f * 1024.f;
+ const F32 FASTER_RATE_BPS = 750.f * 1024.f;
+ F32 max_bandwidth = gViewerThrottle.getMaxBandwidth();
+ if (rate_bps > FASTER_RATE_BPS
+ && rate_bps > max_bandwidth)
+ {
+ llinfos << "Fast network connection, increasing max bandwidth to "
+ << FASTER_RATE_BPS/1024.f
+ << " Kbps" << llendl;
+ gViewerThrottle.setMaxBandwidth(FASTER_RATE_BPS / 1024.f);
+ }
+ else if (rate_bps > FAST_RATE_BPS
+ && rate_bps > max_bandwidth)
+ {
+ llinfos << "Fast network connection, increasing max bandwidth to "
+ << FAST_RATE_BPS/1024.f
+ << " Kbps" << llendl;
+ gViewerThrottle.setMaxBandwidth(FAST_RATE_BPS / 1024.f);
+ }
+ }
+
+ // We're successfully logged in.
+ gSavedSettings.setBOOL("FirstLoginThisInstall", FALSE);
+
+
+ // based on the comments, we've successfully logged in so we can delete the 'forced'
+ // URL that the updater set in settings.ini (in a mostly paranoid fashion)
+ LLString nextLoginLocation = gSavedSettings.getString( "NextLoginLocation" );
+ if ( nextLoginLocation.length() )
+ {
+ // clear it
+ gSavedSettings.setString( "NextLoginLocation", "" );
+
+ // and make sure it's saved
+ gSavedSettings.saveToFile( gSettingsFileName, TRUE );
+ };
+
+ if (!gNoRender)
+ {
+ // JC: Initializing audio requests many sounds for download.
+ init_audio();
+
+ // JC: Initialize "active" gestures. This may also trigger
+ // many gesture downloads, if this is the user's first
+ // time on this machine or -purge has been run.
+ LLUserAuth::options_t gesture_options;
+ if (gUserAuthp->getOptions("gestures", gesture_options))
+ {
+ llinfos << "Gesture Manager loading " << gesture_options.size()
+ << llendl;
+ std::vector<LLUUID> item_ids;
+ LLUserAuth::options_t::iterator resp_it;
+ for (resp_it = gesture_options.begin();
+ resp_it != gesture_options.end();
+ ++resp_it)
+ {
+ const LLUserAuth::response_t& response = *resp_it;
+ LLUUID item_id;
+ LLUUID asset_id;
+ LLUserAuth::response_t::const_iterator option_it;
+
+ option_it = response.find("item_id");
+ if (option_it != response.end())
+ {
+ const std::string& uuid_string = (*option_it).second;
+ item_id.set(uuid_string.c_str());
+ }
+ option_it = response.find("asset_id");
+ if (option_it != response.end())
+ {
+ const std::string& uuid_string = (*option_it).second;
+ asset_id.set(uuid_string.c_str());
+ }
+
+ if (item_id.notNull() && asset_id.notNull())
+ {
+ // Could schedule and delay these for later.
+ const BOOL no_inform_server = FALSE;
+ const BOOL no_deactivate_similar = FALSE;
+ gGestureManager.activateGestureWithAsset(item_id, asset_id,
+ no_inform_server,
+ no_deactivate_similar);
+ // We need to fetch the inventory items for these gestures
+ // so we have the names to populate the UI.
+ item_ids.push_back(item_id);
+ }
+ }
+
+ LLGestureInventoryFetchObserver* fetch = new LLGestureInventoryFetchObserver();
+ fetch->fetchItems(item_ids);
+ // deletes itself when done
+ gInventory.addObserver(fetch);
+ }
+ }
+ gDisplaySwapBuffers = TRUE;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->setHandlerFuncFast(_PREHASH_SoundTrigger, process_sound_trigger);
+ msg->setHandlerFuncFast(_PREHASH_PreloadSound, process_preload_sound);
+ msg->setHandlerFuncFast(_PREHASH_AttachedSound, process_attached_sound);
+ msg->setHandlerFuncFast(_PREHASH_AttachedSoundGainChange, process_attached_sound_gain_change);
+ //msg->setHandlerFuncFast(_PREHASH_AttachedSoundCutoffRadius, process_attached_sound_cutoff_radius);
+ msg->setHandlerFunc(
+ "ConnectToUserserver",
+ process_connect_to_userserver);
+
+ llinfos << "Initialization complete" << llendl;
+ gInitializationComplete = TRUE;
+
+ gRenderStartTime.reset();
+
+ // HACK: Inform simulator of window size.
+ // Do this here so it's less likely to race with RegisterNewAgent.
+ // TODO: Put this into RegisterNewAgent
+ // JC - 7/20/2002
+ gViewerWindow->sendShapeToSim();
+
+ // if needed, show the money history window
+ if (stipend_since_login && !gNoRender)
+ {
+ LLFloaterAccountHistory::show(NULL);
+ }
+
+ if (!gAgent.isFirstLogin())
+ {
+ bool url_ok = LLURLSimString::sInstance.parse();
+ if (!((agent_start_location == "url" && url_ok) ||
+ (!url_ok && ((agent_start_location == "last" && gSavedSettings.getBOOL("LoginLastLocation")) ||
+ (agent_start_location == "home" && !gSavedSettings.getBOOL("LoginLastLocation"))))))
+ {
+ // The reason we show the alert is because we want to
+ // reduce confusion for when you log in and your provided
+ // location is not your expected location. So, if this is
+ // your first login, then you do not have an expectation,
+ // thus, do not show this alert.
+ LLString::format_map_t args;
+ if (url_ok)
+ {
+ args["[TYPE]"] = "desired";
+ args["[HELP]"] = " ";
+ }
+ else if (gSavedSettings.getBOOL("LoginLastLocation"))
+ {
+ args["[TYPE]"] = "last";
+ args["[HELP]"] = " \n ";
+ }
+ else
+ {
+ args["[TYPE]"] = "home";
+ args["[HELP]"] = " \nYou may want to set a new home location.\n ";
+ }
+ gViewerWindow->alertXml("AvatarMoved", args);
+ }
+ else
+ {
+ if (samename)
+ {
+ // restore old camera pos
+ gAgent.setFocusOnAvatar(FALSE, FALSE);
+ gAgent.setCameraPosAndFocusGlobal(gSavedSettings.getVector3d("CameraPosOnLogout"), gSavedSettings.getVector3d("FocusPosOnLogout"), LLUUID::null);
+ BOOL limit_hit = FALSE;
+ gAgent.calcCameraPositionTargetGlobal(&limit_hit);
+ if (limit_hit)
+ {
+ gAgent.setFocusOnAvatar(TRUE, FALSE);
+ }
+ gAgent.stopCameraAnimation();
+ }
+ }
+ }
+
+ gStartupState++;
+ timeout.reset();
+ return do_normal_idle;
+ }
+
+ if (STATE_PRECACHE == gStartupState)
+ {
+ do_normal_idle = TRUE;
+ if (!did_precache)
+ {
+ did_precache = TRUE;
+ // Don't preload map information! The amount of data for all the
+ // map items (icons for classifieds, avatar locations, etc.) is
+ // huge, and not throttled. This overflows the downstream
+ // pipe during startup, when lots of information is being sent.
+ // The problem manifests itself as invisible avatars on login. JC
+ //gWorldMap->setCurrentLayer(0); // pre-load layer 0 of the world map
+
+ gImageList.doPreloadImages(); // pre-load some images from static VFS
+ }
+
+ F32 timeout_frac = timeout.getElapsedTimeF32()/PRECACHING_DELAY;
+ // wait precache-delay and for agent's avatar or a lot longer.
+ if(((timeout_frac > 1.f) && gAgent.getAvatarObject())
+ || (timeout_frac > 3.f))
+ {
+ gStartupState++;
+ }
+ else
+ {
+ set_startup_status(0.50f + 0.50f * timeout_frac, "Precaching...",
+ gAgent.mMOTD.c_str());
+ }
+
+ return do_normal_idle;
+ }
+
+ if (STATE_WEARABLES_WAIT == gStartupState)
+ {
+ do_normal_idle = TRUE;
+
+ static LLFrameTimer wearables_timer;
+
+ const F32 wearables_time = wearables_timer.getElapsedTimeF32();
+ const F32 MAX_WEARABLES_TIME = 10.f;
+
+ if(gAgent.getWearablesLoaded() || !gAgent.isGenderChosen())
+ {
+ gStartupState++;
+ }
+ else if (wearables_time > MAX_WEARABLES_TIME)
+ {
+ gViewerWindow->alertXml("ClothingLoading");
+ gViewerStats->incStat(LLViewerStats::ST_WEARABLES_TOO_LONG);
+ gStartupState++;
+ }
+ else
+ {
+ set_startup_status(0.f + 0.25f * wearables_time / MAX_WEARABLES_TIME,
+ "Downloading clothing...",
+ gAgent.mMOTD.c_str());
+ }
+ return do_normal_idle;
+ }
+
+ if (STATE_CLEANUP == gStartupState)
+ {
+ set_startup_status(1.0, "", NULL);
+
+ do_normal_idle = TRUE;
+
+ // Let the map know about the inventory.
+ if(gFloaterWorldMap)
+ {
+ gFloaterWorldMap->observeInventory(&gInventory);
+ gFloaterWorldMap->observeFriends();
+ }
+
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->resetBusyCount();
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ //llinfos << "Done releasing bitmap" << llendl;
+ gViewerWindow->setShowProgress(FALSE);
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+
+ // We're not away from keyboard, even though login might have taken
+ // a while. JC
+ gAgent.clearAFK();
+
+ // Have the agent start watching the friends list so we can update proxies
+ gAgent.observeFriends();
+ if (gSavedSettings.getBOOL("LoginAsGod"))
+ {
+ gAgent.requestEnterGodMode();
+ }
+
+ // On first start, ask user for gender
+ dialog_choose_gender_first_start();
+
+ // Start automatic replay if the flag is set.
+ if (gSavedSettings.getBOOL("StatsAutoRun"))
+ {
+ LLUUID id;
+ llinfos << "Starting automatic playback" << llendl;
+ gAgentPilot.startPlayback();
+ }
+
+ // ok, if we've gotten this far and have a startup URL
+ if (LLURLSimString::sInstance.parse())
+ {
+ // kick off request for landmark to startup URL
+ if(gFloaterWorldMap)
+ {
+ LLVector3 pos = gAgent.getPositionAgent();
+ if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
+ || LLURLSimString::sInstance.mX != llfloor(pos[VX])
+ || LLURLSimString::sInstance.mY != llfloor(pos[VY]) )
+ {
+ LLFloaterWorldMap::show(NULL, TRUE);
+ gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
+ LLURLSimString::sInstance.mX,
+ LLURLSimString::sInstance.mY,
+ LLURLSimString::sInstance.mZ);
+ }
+ }
+ }
+
+ // Clean up the userauth stuff.
+ if (gUserAuthp)
+ {
+ delete gUserAuthp;
+ gUserAuthp = NULL;
+ }
+
+ gStartupState++;
+ //RN: unmute audio now that we are entering world
+ //JC: But only if the user wants audio working.
+ if (gAudiop)
+ {
+ BOOL mute = gSavedSettings.getBOOL("MuteAudio");
+ gAudiop->setMuted(mute);
+ }
+
+ // reset keyboard focus to sane state of pointing at world
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+
+#if 0 // sjb: enable for auto-enabling timer display
+ gDebugView->mFastTimerView->setVisible(TRUE);
+#endif
+ return do_normal_idle;
+ }
+
+ llwarns << "Reached end of idle_startup for state " << gStartupState << llendl;
+ return do_normal_idle;
+}
+
+//
+// local function definition
+//
+
+void unsupported_graphics_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "User cancelled after driver check" << llendl;
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
+ "unsupported_card.html");
+ app_force_quit( help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void check_driver_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "User cancelled after driver check" << llendl;
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP,
+ "graphics_driver_update.html");
+ app_force_quit( help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void login_show()
+{
+ LLPanelLogin::show( gViewerWindow->getVirtualWindowRect(),
+ gSavedSettings.getBOOL("UseDebugLogin"),
+ login_callback, NULL );
+
+ // Make sure all the UI textures are present and decoded.
+ gImageList.decodeAllImages();
+
+ if( USERSERVER_OTHER == gUserServerChoice )
+ {
+ LLPanelLogin::addServer( gUserServerName, USERSERVER_OTHER );
+ }
+ else
+ {
+ LLPanelLogin::addServer( gUserServerDomainName[gUserServerChoice].mLabel, gUserServerChoice );
+ }
+
+ // Arg! We hate loops!
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DMZ].mLabel, USERSERVER_DMZ );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_LOCAL].mLabel, USERSERVER_LOCAL );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_AGNI].mLabel, USERSERVER_AGNI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_ADITI].mLabel, USERSERVER_ADITI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SIVA].mLabel, USERSERVER_SIVA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_DURGA].mLabel, USERSERVER_DURGA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SHAKTI].mLabel, USERSERVER_SHAKTI );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_GANGA].mLabel, USERSERVER_GANGA );
+ LLPanelLogin::addServer( gUserServerDomainName[USERSERVER_SOMA].mLabel, USERSERVER_SOMA );
+}
+
+// Callback for when login screen is closed. Option 0 = connect, option 1 = quit.
+void login_callback(S32 option, void *userdata)
+{
+ const S32 CONNECT_OPTION = 0;
+ const S32 QUIT_OPTION = 1;
+
+ if (CONNECT_OPTION == option)
+ {
+ gStartupState++;
+ return;
+ }
+ else if (QUIT_OPTION == option)
+ {
+ // Make sure we don't save the password if the user is trying to clear it.
+ LLString first, last, password;
+ BOOL remember = TRUE;
+ LLPanelLogin::getFields(first, last, password, remember);
+ if (!remember)
+ {
+ // turn off the setting and write out to disk
+ gSavedSettings.setBOOL("RememberPassword", FALSE);
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ // stomp the saved password on disk
+ save_password_to_disk(NULL);
+ }
+
+ LLPanelLogin::close();
+
+ // Next iteration through main loop should shut down the app cleanly.
+ gQuit = TRUE;
+
+ return;
+ }
+ else
+ {
+ llwarns << "Unknown login button clicked" << llendl;
+ }
+}
+
+LLString load_password_from_disk()
+{
+ LLString hashed_password("");
+
+ // Look for legacy "marker" password from settings.ini
+ hashed_password = gSavedSettings.getString("Marker");
+ if (!hashed_password.empty())
+ {
+ // Stomp the Marker entry.
+ gSavedSettings.setString("Marker", "");
+
+ // Return that password.
+ return hashed_password;
+ }
+
+ std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
+ "password.dat");
+ FILE* fp = LLFile::fopen(filepath.c_str(), "rb");
+ if (!fp)
+ {
+ return hashed_password;
+ }
+
+ // UUID is 16 bytes, written into ASCII is 32 characters
+ // without trailing \0
+ const S32 HASHED_LENGTH = 32;
+ U8 buffer[HASHED_LENGTH+1];
+
+ if (1 != fread(buffer, HASHED_LENGTH, 1, fp))
+ {
+ return hashed_password;
+ }
+
+ fclose(fp);
+
+ // Decipher with MAC address
+ LLXORCipher cipher(gMACAddress, 6);
+ cipher.decrypt(buffer, HASHED_LENGTH);
+
+ buffer[HASHED_LENGTH] = '\0';
+
+ // Check to see if the mac address generated a bad hashed
+ // password. It should be a hex-string or else the mac adress has
+ // changed. This is a security feature to make sure that if you
+ // get someone's password.dat file, you cannot hack their account.
+ if(is_hex_string(buffer, HASHED_LENGTH))
+ {
+ hashed_password.assign((char*)buffer);
+ }
+
+ return hashed_password;
+}
+
+void save_password_to_disk(const char* hashed_password)
+{
+ std::string filepath = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,
+ "password.dat");
+ if (!hashed_password)
+ {
+ // No password, remove the file.
+ LLFile::remove(filepath.c_str());
+ }
+ else
+ {
+ FILE* fp = LLFile::fopen(filepath.c_str(), "wb");
+ if (!fp)
+ {
+ return;
+ }
+
+ // Encipher with MAC address
+ const S32 HASHED_LENGTH = 32;
+ U8 buffer[HASHED_LENGTH+1];
+
+ LLString::copy((char*)buffer, hashed_password, HASHED_LENGTH+1);
+
+ LLXORCipher cipher(gMACAddress, 6);
+ cipher.encrypt(buffer, HASHED_LENGTH);
+
+ fwrite(buffer, HASHED_LENGTH, 1, fp);
+
+ fclose(fp);
+ }
+}
+
+BOOL is_hex_string(U8* str, S32 len)
+{
+ BOOL rv = TRUE;
+ U8* c = str;
+ while(rv && len--)
+ {
+ switch(*c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ ++c;
+ break;
+ default:
+ rv = FALSE;
+ break;
+ }
+ }
+ return rv;
+}
+
+void show_first_run_dialog()
+{
+ gViewerWindow->alertXml("FirstRun", first_run_dialog_callback, NULL);
+}
+
+void first_run_dialog_callback(S32 option, void* userdata)
+{
+ if (0 == option)
+ {
+ llinfos << "First run dialog cancelling" << llendl;
+ LLWeb::loadURL( CREATE_ACCOUNT_URL );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+
+
+void set_startup_status(const F32 frac, const char *string, const char* msg)
+{
+ gViewerWindow->setProgressPercent(frac*100);
+ gViewerWindow->setProgressString(string);
+
+ gViewerWindow->setProgressMessage(msg);
+}
+
+void on_userserver_name_resolved( BOOL success, const LLString& host_name, U32 ip, void* userdata )
+{
+ if( STATE_RESOLVING_USERSERVER != gStartupState )
+ {
+ llwarns << "Userserver name callback returned during invalid state!" << llendl;
+ return;
+ }
+
+ if( success )
+ {
+ gUserServer.setAddress( ip );
+ llinfos << "...Userserver resolved to " << gUserServer << llendl;
+ gStartupState = STATE_USERSERVER_RESOLVED;
+ }
+ else
+ {
+ llwarns << "setHostByName failed" << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[HOST_NAME]"] = host_name;
+ gViewerWindow->alertXml("SetByHostFail", args, login_alert_done );
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+}
+
+void login_alert_status(S32 option, void* user_data)
+{
+ if (0 == option)
+ {
+ // OK button
+ }
+ else if (1 == option)
+ {
+ // Help button
+ std::string help_path;
+ help_path = gDirUtilp->getExpandedFilename(LL_PATH_HELP, "unable_to_connect.html");
+ load_url_local_file(help_path.c_str() );
+ }
+
+ LLPanelLogin::giveFocus();
+}
+
+void update_app(BOOL mandatory, const std::string& auth_msg)
+{
+ // store off config state, as we might quit soon
+ gSavedSettings.saveToFile(gSettingsFileName, TRUE);
+
+ std::ostringstream message;
+
+ //XUI:translate
+ std::string msg;
+ if (!auth_msg.empty())
+ {
+ msg = "(" + auth_msg + ") \n";
+ }
+ LLStringBase<char>::format_map_t args;
+ args["[MESSAGE]"] = msg;
+
+ BOOL *mandatoryp = new BOOL(mandatory);
+
+#if LL_WINDOWS
+ if (mandatory)
+ {
+ gViewerWindow->alertXml("DownloadWindowsMandatory", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+ }
+ else
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ gViewerWindow->alertXml("DownloadWindowsReleaseForDownload", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#else
+ gViewerWindow->alertXml("DownloadWindows", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#endif
+ }
+#else
+ if (mandatory)
+ {
+ gViewerWindow->alertXml("DownloadMacMandatory", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+ }
+ else
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ gViewerWindow->alertXml("DownloadMacReleaseForDownload", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#else
+ gViewerWindow->alertXml("DownloadMac", args,
+ update_dialog_callback,
+ (void *)mandatoryp);
+#endif
+ }
+#endif
+
+}
+
+
+void update_dialog_callback(S32 option, void *userdata)
+{
+ std::string update_exe_path;
+ BOOL mandatory = *(BOOL *)userdata;
+
+#if !LL_RELEASE_FOR_DOWNLOAD
+ if (option == 2)
+ {
+ gStartupState++;
+ return;
+ }
+#endif
+
+ if (option == 1)
+ {
+ // ...user doesn't want to do it
+ if (mandatory)
+ {
+ app_force_quit();
+ // Bump them back to the login screen.
+ //gStartupState = STATE_LOGIN_SHOW;
+ }
+ else
+ {
+ gStartupState++;
+ }
+ return;
+ }
+
+#if LL_WINDOWS
+ char ip[MAX_STRING];
+
+ update_exe_path = gDirUtilp->getTempFilename();
+ if (update_exe_path.empty())
+ {
+ // We're hosed, bail
+ llwarns << "LLDir::getTempFilename() failed" << llendl;
+ app_force_quit(NULL);
+ return;
+ }
+
+ update_exe_path += ".exe";
+
+ std::string updater_source = gDirUtilp->getAppRODataDir();
+ updater_source += gDirUtilp->getDirDelimiter();
+ updater_source += "updater.exe";
+
+ llinfos << "Calling CopyFile source: " << updater_source.c_str()
+ << " dest: " << update_exe_path
+ << llendl;
+
+
+ if (!CopyFileA(updater_source.c_str(), update_exe_path.c_str(), FALSE))
+ {
+ llinfos << "Unable to copy the updater!" << llendl;
+ app_force_quit(NULL);
+ return;
+ }
+ u32_to_ip_string(gUserServer.getAddress(), ip);
+
+ std::ostringstream params;
+ params << "-userserver " << gUserServerName;
+
+ // if a sim name was passed in via command line parameter (typically through a SLURL)
+ if ( LLURLSimString::sInstance.mSimString.length() )
+ {
+ // record the location to start at next time
+ gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
+ };
+
+ if (gHideLinks)
+ {
+ // Figure out the program name.
+ const char* data_dir = gDirUtilp->getAppRODataDir().c_str();
+ // Roll back from the end, stopping at the first '\'
+ const char* program_name = data_dir + strlen(data_dir);
+ while ( (data_dir != --program_name) &&
+ *(program_name) != '\\');
+
+ if ( *(program_name) == '\\')
+ {
+ // We found a '\'.
+ program_name++;
+ }
+ else
+ {
+ // Oops.
+ program_name = "SecondLife";
+ }
+
+ params << " -silent -name \"" << gSecondLife << "\" -program \"" << program_name << "\"";
+ }
+
+ llinfos << "Calling updater: " << update_exe_path << " " << params.str() << llendl;
+
+ remove_marker_file(); // In case updater fails
+
+ // Use spawn() to run asynchronously
+ int retval = _spawnl(_P_NOWAIT, update_exe_path.c_str(), update_exe_path.c_str(), params.str().c_str(), NULL);
+ llinfos << "Spawn returned " << retval << llendl;
+
+#elif LL_DARWIN
+ // if a sim name was passed in via command line parameter (typically through a SLURL)
+ if ( LLURLSimString::sInstance.mSimString.length() )
+ {
+ // record the location to start at next time
+ gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
+ };
+
+ update_exe_path = "'";
+ update_exe_path += gDirUtilp->getAppRODataDir();
+ update_exe_path += "/AutoUpdater.app/Contents/MacOS/AutoUpdater' -userserver ";
+ update_exe_path += gUserServerName;
+ update_exe_path += " -name \"";
+ update_exe_path += gSecondLife;
+ update_exe_path += "\" &";
+
+ llinfos << "Calling updater: " << update_exe_path << llendl;
+
+ remove_marker_file(); // In case updater fails
+
+ // Run the auto-updater.
+ system(update_exe_path.c_str());
+
+#elif LL_LINUX
+ OSMessageBox("Automatic updating is not yet implemented for Linux.\n"
+ "Please download the latest version from www.secondlife.com.",
+ NULL, OSMB_OK);
+ remove_marker_file();
+
+#endif
+ app_force_quit(NULL);
+}
+
+void use_circuit_callback(void**, S32 result)
+{
+ // bail if we're quitting.
+ if(gQuit) return;
+ static bool called = false;
+ if(!called)
+ {
+ called = true;
+ if (result)
+ {
+ // Make sure user knows something bad happened. JC
+ llinfos << "Backing up to login screen!" << llendl;
+ gViewerWindow->alertXml("LoginPacketNeverReceived",
+ login_alert_status, NULL);
+ gStartupState = STATE_LOGIN_SHOW;
+ }
+ else
+ {
+ gGotUseCircuitCodeAck = true;
+ }
+ }
+}
+
+void register_viewer_callbacks(LLMessageSystem* msg)
+{
+ msg->setHandlerFuncFast(_PREHASH_LayerData, process_layer_data );
+ msg->setHandlerFuncFast(_PREHASH_ImageData, LLViewerImage::receiveImage );
+ msg->setHandlerFuncFast(_PREHASH_ImagePacket, LLViewerImage::receiveImagePacket );
+ msg->setHandlerFuncFast(_PREHASH_ObjectUpdate, process_object_update );
+ msg->setHandlerFunc("ObjectUpdateCompressed", process_compressed_object_update );
+ msg->setHandlerFunc("ObjectUpdateCached", process_cached_object_update );
+ msg->setHandlerFuncFast(_PREHASH_ImprovedTerseObjectUpdate, process_terse_object_update_improved );
+ msg->setHandlerFunc("SimStats", process_sim_stats);
+ msg->setHandlerFuncFast(_PREHASH_HealthMessage, process_health_message );
+ msg->setHandlerFuncFast(_PREHASH_EconomyData, process_economy_data);
+ msg->setHandlerFunc("RegionInfo", LLViewerRegion::processRegionInfo);
+
+ msg->setHandlerFuncFast(_PREHASH_ChatFromSimulator, process_chat_from_simulator);
+ msg->setHandlerFuncFast(_PREHASH_KillObject, process_kill_object, NULL);
+ msg->setHandlerFuncFast(_PREHASH_SimulatorViewerTimeMessage, process_time_synch, NULL);
+ msg->setHandlerFuncFast(_PREHASH_EnableSimulator, process_enable_simulator);
+ msg->setHandlerFuncFast(_PREHASH_DisableSimulator, process_disable_simulator);
+ msg->setHandlerFuncFast(_PREHASH_KickUser, process_kick_user, NULL);
+
+ msg->setHandlerFunc("CrossedRegion", process_crossed_region);
+ msg->setHandlerFuncFast(_PREHASH_TeleportFinish, process_teleport_finish);
+
+ msg->setHandlerFuncFast(_PREHASH_AlertMessage, process_alert_message);
+ msg->setHandlerFunc("AgentAlertMessage", process_agent_alert_message);
+ msg->setHandlerFuncFast(_PREHASH_MeanCollisionAlert, process_mean_collision_alert_message, NULL);
+ msg->setHandlerFunc("ViewerFrozenMessage", process_frozen_message);
+
+ msg->setHandlerFuncFast(_PREHASH_RequestAvatarInfo, process_avatar_info_request);
+ msg->setHandlerFuncFast(_PREHASH_NameValuePair, process_name_value);
+ msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair, process_remove_name_value);
+ msg->setHandlerFuncFast(_PREHASH_AvatarAnimation, process_avatar_animation);
+ msg->setHandlerFuncFast(_PREHASH_AvatarAppearance, process_avatar_appearance);
+ msg->setHandlerFunc("AgentCachedTextureResponse", LLAgent::processAgentCachedTextureResponse);
+ msg->setHandlerFunc("RebakeAvatarTextures", LLVOAvatar::processRebakeAvatarTextures);
+ msg->setHandlerFuncFast(_PREHASH_CameraConstraint, process_camera_constraint);
+ msg->setHandlerFuncFast(_PREHASH_AvatarSitResponse, process_avatar_sit_response);
+ msg->setHandlerFunc("SetFollowCamProperties", process_set_follow_cam_properties);
+ msg->setHandlerFunc("ClearFollowCamProperties", process_clear_follow_cam_properties);
+
+ msg->setHandlerFuncFast(_PREHASH_ImprovedInstantMessage, process_improved_im);
+ msg->setHandlerFuncFast(_PREHASH_ScriptQuestion, process_script_question);
+ msg->setHandlerFuncFast(_PREHASH_ObjectProperties, LLSelectMgr::processObjectProperties, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ObjectPropertiesFamily, LLSelectMgr::processObjectPropertiesFamily, NULL);
+ msg->setHandlerFunc("ForceObjectSelect", LLSelectMgr::processForceObjectSelect);
+
+ msg->setHandlerFuncFast(_PREHASH_MoneyBalanceReply, process_money_balance_reply, NULL);
+ msg->setHandlerFuncFast(_PREHASH_CoarseLocationUpdate, LLWorld::processCoarseUpdate, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ReplyTaskInventory, LLViewerObject::processTaskInv, NULL);
+ msg->setHandlerFuncFast(_PREHASH_DerezContainer, process_derez_container, NULL);
+ msg->setHandlerFuncFast(_PREHASH_ScriptRunningReply,
+ &LLLiveLSLEditor::processScriptRunningReply);
+
+ msg->setHandlerFuncFast(_PREHASH_DeRezAck, process_derez_ack);
+
+ msg->setHandlerFunc("LogoutReply", process_logout_reply);
+
+ //msg->setHandlerFuncFast(_PREHASH_AddModifyAbility,
+ // &LLAgent::processAddModifyAbility);
+ //msg->setHandlerFuncFast(_PREHASH_RemoveModifyAbility,
+ // &LLAgent::processRemoveModifyAbility);
+ msg->setHandlerFuncFast(_PREHASH_AgentDataUpdate,
+ &LLAgent::processAgentDataUpdate);
+ msg->setHandlerFuncFast(_PREHASH_AgentGroupDataUpdate,
+ &LLAgent::processAgentGroupDataUpdate);
+ msg->setHandlerFunc("AgentDropGroup",
+ &LLAgent::processAgentDropGroup);
+ // land ownership messages
+ msg->setHandlerFuncFast(_PREHASH_ParcelOverlay,
+ LLViewerParcelMgr::processParcelOverlay);
+ msg->setHandlerFuncFast(_PREHASH_ParcelProperties,
+ LLViewerParcelMgr::processParcelProperties);
+ msg->setHandlerFunc("ParcelAccessListReply",
+ LLViewerParcelMgr::processParcelAccessListReply);
+ msg->setHandlerFunc("ParcelDwellReply",
+ LLViewerParcelMgr::processParcelDwellReply);
+
+ msg->setHandlerFunc("AvatarPropertiesReply",
+ LLPanelAvatar::processAvatarPropertiesReply);
+ msg->setHandlerFunc("AvatarInterestsReply",
+ LLPanelAvatar::processAvatarInterestsReply);
+ msg->setHandlerFunc("AvatarGroupsReply",
+ LLPanelAvatar::processAvatarGroupsReply);
+ msg->setHandlerFuncFast(_PREHASH_AvatarStatisticsReply,
+ LLPanelAvatar::processAvatarStatisticsReply);
+ msg->setHandlerFunc("AvatarNotesReply",
+ LLPanelAvatar::processAvatarNotesReply);
+ msg->setHandlerFunc("AvatarPicksReply",
+ LLPanelAvatar::processAvatarPicksReply);
+ msg->setHandlerFunc("AvatarClassifiedReply",
+ LLPanelAvatar::processAvatarClassifiedReply);
+
+ msg->setHandlerFuncFast(_PREHASH_CreateGroupReply,
+ LLGroupMgr::processCreateGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_JoinGroupReply,
+ LLGroupMgr::processJoinGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_EjectGroupMemberReply,
+ LLGroupMgr::processEjectGroupMemberReply);
+ msg->setHandlerFuncFast(_PREHASH_LeaveGroupReply,
+ LLGroupMgr::processLeaveGroupReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupProfileReply,
+ LLGroupMgr::processGroupPropertiesReply);
+
+ msg->setHandlerFuncFast(_PREHASH_ReputationIndividualReply,
+ LLFloaterRate::processReputationIndividualReply);
+
+ msg->setHandlerFuncFast(_PREHASH_AgentWearablesUpdate,
+ LLAgent::processAgentInitialWearablesUpdate );
+
+ msg->setHandlerFunc("ScriptControlChange",
+ LLAgent::processScriptControlChange );
+
+ msg->setHandlerFuncFast(_PREHASH_GestureUpdate,
+ LLViewerGestureList::processGestureUpdate);
+
+ msg->setHandlerFuncFast(_PREHASH_ViewerEffect, LLHUDManager::processViewerEffect);
+
+ msg->setHandlerFuncFast(_PREHASH_GrantGodlikePowers, process_grant_godlike_powers);
+
+ msg->setHandlerFuncFast(_PREHASH_MoneySummaryReply,
+ LLFloaterAccountHistory::processMoneySummaryReply);
+ msg->setHandlerFuncFast(_PREHASH_MoneyDetailsReply,
+ LLFloaterAccountHistory::processMoneyDetailsReply);
+ msg->setHandlerFuncFast(_PREHASH_MoneyTransactionsReply,
+ LLFloaterAccountHistory::processMoneyTransactionsReply);
+
+ // ASDF
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountSummaryReply,
+ LLGroupMoneyPlanningTabEventHandler::processGroupAccountSummaryReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountDetailsReply,
+ LLGroupMoneyDetailsTabEventHandler::processGroupAccountDetailsReply);
+ msg->setHandlerFuncFast(_PREHASH_GroupAccountTransactionsReply,
+ LLGroupMoneySalesTabEventHandler::processGroupAccountTransactionsReply);
+
+ msg->setHandlerFuncFast(_PREHASH_UserInfoReply,
+ process_user_info_reply);
+
+ msg->setHandlerFunc("RegionHandshake", process_region_handshake, NULL);
+
+ msg->setHandlerFunc("TeleportStart", process_teleport_start );
+ msg->setHandlerFunc("TeleportProgress", process_teleport_progress);
+ msg->setHandlerFunc("TeleportFailed", process_teleport_failed, NULL);
+ msg->setHandlerFunc("TeleportLocal", process_teleport_local, NULL);
+
+ msg->setHandlerFunc("ImageNotInDatabase", LLViewerImageList::processImageNotInDatabase, NULL);
+
+ msg->setHandlerFuncFast(_PREHASH_GroupMembersReply,
+ LLGroupMgr::processGroupMembersReply);
+ msg->setHandlerFunc("GroupRoleDataReply",
+ LLGroupMgr::processGroupRoleDataReply);
+ msg->setHandlerFunc("GroupRoleMembersReply",
+ LLGroupMgr::processGroupRoleMembersReply);
+ msg->setHandlerFunc("GroupTitlesReply",
+ LLGroupMgr::processGroupTitlesReply);
+ // Special handler as this message is sometimes used for group land.
+ msg->setHandlerFunc("PlacesReply", process_places_reply);
+ msg->setHandlerFunc("GroupNoticesListReply", LLPanelGroupNotices::processGroupNoticesListReply);
+
+ msg->setHandlerFunc("DirPlacesReply", LLPanelDirBrowser::processDirPlacesReply);
+ msg->setHandlerFunc("DirPeopleReply", LLPanelDirBrowser::processDirPeopleReply);
+ msg->setHandlerFunc("DirEventsReply", LLPanelDirBrowser::processDirEventsReply);
+ msg->setHandlerFunc("DirGroupsReply", LLPanelDirBrowser::processDirGroupsReply);
+ //msg->setHandlerFunc("DirPicksReply", LLPanelDirBrowser::processDirPicksReply);
+ msg->setHandlerFunc("DirClassifiedReply", LLPanelDirBrowser::processDirClassifiedReply);
+ msg->setHandlerFunc("DirLandReply", LLPanelDirBrowser::processDirLandReply);
+ msg->setHandlerFunc("DirPopularReply",LLPanelDirBrowser::processDirPopularReply);
+
+ msg->setHandlerFunc("AvatarPickerReply", LLFloaterAvatarPicker::processAvatarPickerReply);
+
+ msg->setHandlerFunc("MapLayerReply", LLWorldMap::processMapLayerReply);
+ msg->setHandlerFunc("MapBlockReply", LLWorldMap::processMapBlockReply);
+ msg->setHandlerFunc("MapItemReply", LLWorldMap::processMapItemReply);
+
+ msg->setHandlerFunc("EventInfoReply", LLPanelEvent::processEventInfoReply);
+ msg->setHandlerFunc("PickInfoReply", LLPanelPick::processPickInfoReply);
+ msg->setHandlerFunc("ClassifiedInfoReply", LLPanelClassified::processClassifiedInfoReply);
+ msg->setHandlerFunc("ParcelInfoReply", LLPanelPlace::processParcelInfoReply);
+ msg->setHandlerFunc("ScriptDialog", process_script_dialog);
+ msg->setHandlerFunc("LoadURL", process_load_url);
+ msg->setHandlerFunc("ScriptTeleportRequest", process_script_teleport_request);
+ msg->setHandlerFunc("EstateCovenantReply", process_covenant_reply);
+
+ // calling cards
+ msg->setHandlerFunc("OfferCallingCard", process_offer_callingcard);
+ msg->setHandlerFunc("AcceptCallingCard", process_accept_callingcard);
+ msg->setHandlerFunc("DeclineCallingCard", process_decline_callingcard);
+
+ msg->setHandlerFunc("ParcelObjectOwnersReply", LLPanelLandObjects::processParcelObjectOwnersReply);
+
+ // Reponse to the "Refresh" button on land objects floater.
+ if (gSavedSettings.getBOOL("AudioStreamingVideo"))
+ {
+ msg->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media);
+ msg->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update );
+ }
+ else
+ {
+ msg->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback );
+ }
+
+ msg->setHandlerFunc("InitiateDownload", process_initiate_download);
+ msg->setHandlerFunc("LandStatReply", LLFloaterTopObjects::handle_land_reply);
+ msg->setHandlerFunc("GenericMessage", process_generic_message);
+
+ msg->setHandlerFuncFast(_PREHASH_FeatureDisabled, process_feature_disabled_message);
+}
+
+
+void init_stat_view()
+{
+ LLFrameStatView *frameviewp = gDebugView->mFrameStatView;
+ frameviewp->setup(gFrameStats);
+ frameviewp->mShowPercent = FALSE;
+
+ LLRect rect;
+ LLStatBar *stat_barp;
+ rect = gDebugView->mStatViewp->getRect();
+
+ //
+ // Viewer advanced stats
+ //
+ LLStatView *stat_viewp = NULL;
+
+ //
+ // Viewer Basic
+ //
+ stat_viewp = new LLStatView("basic stat view", "Basic", "OpenDebugStatBasic", rect);
+ gDebugView->mStatViewp->addChildAtEnd(stat_viewp);
+
+ stat_barp = stat_viewp->addStat("FPS", &(gViewerStats->mFPSStat));
+ stat_barp->setUnitLabel(" fps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 45.f;
+ stat_barp->mTickSpacing = 7.5f;
+ stat_barp->mLabelSpacing = 15.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = TRUE;
+
+ stat_barp = stat_viewp->addStat("Bandwidth", &(gViewerStats->mKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 900.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 300.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = stat_viewp->addStat("Packet Loss", &(gViewerStats->mPacketsLostPercentStat));
+ stat_barp->setUnitLabel(" %%");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 5.f;
+ stat_barp->mTickSpacing = 1.f;
+ stat_barp->mLabelSpacing = 1.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = TRUE;
+ stat_barp->mPrecision = 1;
+
+ stat_barp = stat_viewp->addStat("Ping Sim", &(gViewerStats->mSimPingStat));
+ stat_barp->setUnitLabel(" msec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = stat_viewp->addStat("Ping User", &(gViewerStats->mUserserverPingStat));
+ stat_barp->setUnitLabel(" msec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+
+ stat_viewp = new LLStatView("advanced stat view", "Advanced", "OpenDebugStatAdvanced", rect);
+ gDebugView->mStatViewp->addChildAtEnd(stat_viewp);
+
+
+ LLStatView *render_statviewp;
+ render_statviewp = new LLStatView("render stat view", "Render", "OpenDebugStatRender", rect);
+ stat_viewp->addChildAtEnd(render_statviewp);
+
+ stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 500.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = render_statviewp->addStat("KTris Drawn", &(gPipeline.mTrianglesDrawnStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 3000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPrecision = 1;
+
+ stat_barp = render_statviewp->addStat("Total Objs", &(gObjectList.mNumObjectsStat));
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 10000.f;
+ stat_barp->mTickSpacing = 2500.f;
+ stat_barp->mLabelSpacing = 5000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = render_statviewp->addStat("New Objs", &(gObjectList.mNumNewObjectsStat));
+ stat_barp->setLabel("New Objs");
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = TRUE;
+ stat_barp->mDisplayBar = FALSE;
+
+
+ // Pipeline statistics
+ LLStatView *pipeline_statviewp;
+ pipeline_statviewp = new LLStatView("pipeline stat view", "Pipeline", "", rect);
+ render_statviewp->addChildAtEnd(pipeline_statviewp);
+
+ stat_barp = pipeline_statviewp->addStat("Visible Drawables", &(gPipeline.mNumVisibleDrawablesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 10000.f;
+ stat_barp->mTickSpacing = 1000.f;
+ stat_barp->mLabelSpacing = 5000.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Visible Faces", &(gPipeline.mNumVisibleFacesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40000.f;
+ stat_barp->mTickSpacing = 5000.f;
+ stat_barp->mLabelSpacing = 10000.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("DirtyGeom", &(gPipeline.mGeometryChangesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("DirtyLight", &(gPipeline.mLightingChangesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("MoveList", &(gPipeline.mMoveChangesStat));
+ stat_barp->setUnitLabel("dr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Compiles", &(gPipeline.mCompilesStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1000.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 500.f;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = pipeline_statviewp->addStat("Verts Relit", &(gPipeline.mVerticesRelitStat));
+ stat_barp->setUnitLabel("/fr");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40000.f;
+ stat_barp->mTickSpacing = 10000.f;
+ stat_barp->mLabelSpacing = 20000.f;
+ stat_barp->mPerSec = FALSE;
+
+ // Texture statistics
+ LLStatView *texture_statviewp;
+ texture_statviewp = new LLStatView("texture stat view", "Texture", "", rect);
+ render_statviewp->addChildAtEnd(texture_statviewp);
+
+ stat_barp = texture_statviewp->addStat("Count", &(gImageList.sNumImagesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 8000.f;
+ stat_barp->mTickSpacing = 2000.f;
+ stat_barp->mLabelSpacing = 4000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Raw Count", &(gImageList.sNumRawImagesStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 8000.f;
+ stat_barp->mTickSpacing = 2000.f;
+ stat_barp->mLabelSpacing = 4000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = texture_statviewp->addStat("GL Mem", &(gImageList.sGLTexMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Formatted Mem", &(gImageList.sFormattedMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Raw Mem", &(gImageList.sRawMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+ stat_barp = texture_statviewp->addStat("Bound Mem", &(gImageList.sGLBoundMemStat));
+ stat_barp->setUnitLabel("");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 400.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPrecision = 1;
+ stat_barp->mPerSec = FALSE;
+
+
+ // Network statistics
+ LLStatView *net_statviewp;
+ net_statviewp = new LLStatView("network stat view", "Network", "OpenDebugStatNet", rect);
+ stat_viewp->addChildAtEnd(net_statviewp);
+
+ stat_barp = net_statviewp->addStat("Packets In", &(gViewerStats->mPacketsInStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Packets Out", &(gViewerStats->mPacketsOutStat));
+ stat_barp->setUnitLabel("/sec");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Objects", &(gViewerStats->mObjectKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Texture", &(gViewerStats->mTextureKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Asset", &(gViewerStats->mAssetKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Layers", &(gViewerStats->mLayersKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mDisplayBar = FALSE;
+
+ stat_barp = net_statviewp->addStat("Actual In", &(gViewerStats->mActualInKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1024.f;
+ stat_barp->mTickSpacing = 128.f;
+ stat_barp->mLabelSpacing = 256.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = net_statviewp->addStat("Actual Out", &(gViewerStats->mActualOutKBitStat));
+ stat_barp->setUnitLabel(" kbps");
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 512.f;
+ stat_barp->mTickSpacing = 128.f;
+ stat_barp->mLabelSpacing = 256.f;
+ stat_barp->mDisplayBar = TRUE;
+ stat_barp->mDisplayHistory = FALSE;
+
+ stat_barp = net_statviewp->addStat("VFS Pending Ops", &(gViewerStats->mVFSPendingOperations));
+ stat_barp->setUnitLabel(" ");
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+
+
+ // Simulator stats
+ LLStatView *sim_statviewp = new LLStatView("sim stat view", "Simulator", "OpenDebugStatSim", rect);
+ gDebugView->mStatViewp->addChildAtEnd(sim_statviewp);
+
+ stat_barp = sim_statviewp->addStat("Time Dilation", &(gViewerStats->mSimTimeDilation));
+ stat_barp->mPrecision = 2;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 1.f;
+ stat_barp->mTickSpacing = 0.25f;
+ stat_barp->mLabelSpacing = 0.5f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Sim FPS", &(gViewerStats->mSimFPS));
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 200.f;
+ stat_barp->mTickSpacing = 20.f;
+ stat_barp->mLabelSpacing = 100.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Physics FPS", &(gViewerStats->mSimPhysicsFPS));
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 66.f;
+ stat_barp->mTickSpacing = 33.f;
+ stat_barp->mLabelSpacing = 33.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Agent Updates/Sec", &(gViewerStats->mSimAgentUPS));
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100.f;
+ stat_barp->mTickSpacing = 25.f;
+ stat_barp->mLabelSpacing = 50.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Main Agents", &(gViewerStats->mSimMainAgents));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 80.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 40.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Child Agents", &(gViewerStats->mSimChildAgents));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 5.f;
+ stat_barp->mLabelSpacing = 10.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Objects", &(gViewerStats->mSimObjects));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 30000.f;
+ stat_barp->mTickSpacing = 5000.f;
+ stat_barp->mLabelSpacing = 10000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Active Objects", &(gViewerStats->mSimActiveObjects));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Active Scripts", &(gViewerStats->mSimActiveScripts));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Script Perf", &(gViewerStats->mSimLSLIPS));
+ stat_barp->setUnitLabel(" ips");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100000.f;
+ stat_barp->mTickSpacing = 25000.f;
+ stat_barp->mLabelSpacing = 50000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Packets In", &(gViewerStats->mSimInPPS));
+ stat_barp->setUnitLabel(" pps");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 2000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Packets Out", &(gViewerStats->mSimOutPPS));
+ stat_barp->setUnitLabel(" pps");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 2000.f;
+ stat_barp->mTickSpacing = 250.f;
+ stat_barp->mLabelSpacing = 1000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Pending Downloads", &(gViewerStats->mSimPendingDownloads));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 800.f;
+ stat_barp->mTickSpacing = 100.f;
+ stat_barp->mLabelSpacing = 200.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Pending Uploads", &(gViewerStats->mSimPendingUploads));
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100.f;
+ stat_barp->mTickSpacing = 25.f;
+ stat_barp->mLabelSpacing = 50.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_statviewp->addStat("Total Unacked Bytes", &(gViewerStats->mSimTotalUnackedBytes));
+ stat_barp->setUnitLabel(" kb");
+ stat_barp->mPrecision = 0;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 100000.f;
+ stat_barp->mTickSpacing = 25000.f;
+ stat_barp->mLabelSpacing = 50000.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ LLStatView *sim_time_viewp;
+ sim_time_viewp = new LLStatView("sim perf view", "Time (ms)", "", rect);
+ sim_statviewp->addChildAtEnd(sim_time_viewp);
+
+ stat_barp = sim_time_viewp->addStat("Total Frame Time", &(gViewerStats->mSimFrameMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Net Time", &(gViewerStats->mSimNetMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Sim Time (Physics)", &(gViewerStats->mSimSimPhysicsMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Sim Time (Other)", &(gViewerStats->mSimSimOtherMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Agent Time", &(gViewerStats->mSimAgentMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Images Time", &(gViewerStats->mSimImagesMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ stat_barp = sim_time_viewp->addStat("Script Time", &(gViewerStats->mSimScriptMsec));
+ stat_barp->setUnitLabel("ms");
+ stat_barp->mPrecision = 1;
+ stat_barp->mMinBar = 0.f;
+ stat_barp->mMaxBar = 40.f;
+ stat_barp->mTickSpacing = 10.f;
+ stat_barp->mLabelSpacing = 20.f;
+ stat_barp->mPerSec = FALSE;
+ stat_barp->mDisplayBar = FALSE;
+ stat_barp->mDisplayMean = FALSE;
+
+ LLRect r = gDebugView->mStatViewp->getRect();
+
+ // Reshape based on the parameters we set.
+ gDebugView->mStatViewp->reshape(r.getWidth(), r.getHeight());
+}
+
+void asset_callback_nothing(LLVFS*, const LLUUID&, LLAssetType::EType, void*, S32)
+{
+ // nothing
+}
+
+// *HACK: Must match name in Library or agent inventory
+const char* COMMON_GESTURES_FOLDER = "Common Gestures";
+const char* MALE_GESTURES_FOLDER = "Male Gestures";
+const char* FEMALE_GESTURES_FOLDER = "Female Gestures";
+const char* MALE_OUTFIT_FOLDER = "Male Shape & Outfit";
+const char* FEMALE_OUTFIT_FOLDER = "Female Shape & Outfit";
+const S32 OPT_USE_INITIAL_OUTFIT = -2;
+const S32 OPT_CLOSED_WINDOW = -1;
+const S32 OPT_MALE = 0;
+const S32 OPT_FEMALE = 1;
+
+void callback_choose_gender(S32 option, void* userdata)
+{
+ S32 gender = OPT_FEMALE;
+ const char* outfit = FEMALE_OUTFIT_FOLDER;
+ const char* gestures = FEMALE_GESTURES_FOLDER;
+ const char* common_gestures = COMMON_GESTURES_FOLDER;
+ if (!gInitialOutfit.empty())
+ {
+ outfit = gInitialOutfit.c_str();
+ if (gInitialOutfitGender == "male")
+ {
+ gender = OPT_MALE;
+ gestures = MALE_GESTURES_FOLDER;
+ }
+ else
+ {
+ gender = OPT_FEMALE;
+ gestures = FEMALE_GESTURES_FOLDER;
+ }
+ }
+ else
+ {
+ switch(option)
+ {
+ case OPT_MALE:
+ gender = OPT_MALE;
+ outfit = MALE_OUTFIT_FOLDER;
+ gestures = MALE_GESTURES_FOLDER;
+ break;
+
+ case OPT_FEMALE:
+ case OPT_CLOSED_WINDOW:
+ default:
+ gender = OPT_FEMALE;
+ outfit = FEMALE_OUTFIT_FOLDER;
+ gestures = FEMALE_GESTURES_FOLDER;
+ break;
+ }
+ }
+
+ // try to find the outfit - if not there, create some default
+ // wearables.
+ LLInventoryModel::cat_array_t cat_array;
+ LLInventoryModel::item_array_t item_array;
+ LLNameCategoryCollector has_name(outfit);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cat_array,
+ item_array,
+ LLInventoryModel::EXCLUDE_TRASH,
+ has_name);
+ if (0 == cat_array.count())
+ {
+ gAgent.createStandardWearables(gender);
+ }
+ else
+ {
+ wear_outfit_by_name(outfit);
+ }
+ wear_outfit_by_name(gestures);
+ wear_outfit_by_name(common_gestures);
+
+ typedef std::map<LLUUID, LLMultiGesture*> item_map_t;
+ item_map_t::iterator gestureIterator;
+
+ // Must be here so they aren't invisible if they close the window.
+ gAgent.setGenderChosen(TRUE);
+}
+
+// XUI:translate
+void dialog_choose_gender_first_start()
+{
+ if (!gNoRender
+ && (!gAgent.isGenderChosen()))
+ {
+ if (!gInitialOutfit.empty())
+ {
+ gViewerWindow->alertXml("WelcomeNoClothes",
+ callback_choose_gender, NULL);
+ }
+ else
+ {
+ gViewerWindow->alertXml("WelcomeChooseSex",
+ callback_choose_gender, NULL);
+
+ }
+ }
+}
+
+// Loads a bitmap to display during load
+// location_id = 0 => last position
+// location_id = 1 => home position
+void init_start_screen(S32 location_id)
+{
+ if (gStartImageGL)
+ {
+ gStartImageGL = NULL;
+ llinfos << "re-initializing start screen" << llendl;
+ }
+
+ llinfos << "Loading startup bitmap..." << llendl;
+
+ LLString temp_str = gDirUtilp->getLindenUserDir() + gDirUtilp->getDirDelimiter();
+
+ if ((S32)START_LOCATION_ID_LAST == location_id)
+ {
+ temp_str += SCREEN_LAST_FILENAME;
+ }
+ else
+ {
+ temp_str += SCREEN_HOME_FILENAME;
+ }
+
+ LLPointer<LLImageBMP> start_image_bmp = new LLImageBMP;
+ if( !start_image_bmp->load(temp_str) )
+ {
+ llinfos << "Bitmap load failed" << llendl;
+ return;
+ }
+
+ gStartImageGL = new LLImageGL(FALSE);
+ gStartImageWidth = start_image_bmp->getWidth();
+ gStartImageHeight = start_image_bmp->getHeight();
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if (!start_image_bmp->decode(raw))
+ {
+ llinfos << "Bitmap decode failed" << llendl;
+ gStartImageGL = NULL;
+ return;
+ }
+
+ raw->expandToPowerOfTwo();
+ gStartImageGL->createGLTexture(0, raw);
+}
+
+
+// frees the bitmap
+void release_start_screen()
+{
+ //llinfos << "Releasing bitmap..." << llendl;
+ gStartImageGL = NULL;
+}
+
+void process_connect_to_userserver(LLMessageSystem* msg, void**)
+{
+ // Sent unreliably since if we've become disconnected, the
+ // userserver will get back to us eventually. By sending reliable,
+ // we also may accidently induce two separate validations under
+ // conditions where the userserver is already lagged.
+ msg->newMessage("ConnectAgentToUserserver");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->sendMessage(gUserServer);
+}
+
+bool LLStartUp::canGoFullscreen()
+{
+ return gStartupState >= STATE_WORLD_INIT;
+}
+
+
diff --git a/indra/newview/llstartup.h b/indra/newview/llstartup.h
new file mode 100644
index 0000000000..c7b3bc13d5
--- /dev/null
+++ b/indra/newview/llstartup.h
@@ -0,0 +1,72 @@
+/**
+ * @file llstartup.h
+ * @brief startup routines and logic declaration
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTARTUP_H
+#define LL_LLSTARTUP_H
+
+// functions
+BOOL idle_startup();
+void cleanup_app();
+LLString load_password_from_disk();
+
+// constants, variables, & enumerations
+extern const char* SCREEN_HOME_FILENAME;
+extern const char* SCREEN_LAST_FILENAME;
+
+enum EStartupState{
+ STATE_FIRST,
+ STATE_LOGIN_SHOW,
+ STATE_LOGIN_WAIT,
+ STATE_LOGIN_CLEANUP,
+ STATE_RESOLVING_USERSERVER,
+ STATE_USERSERVER_RESOLVED,
+ STATE_MESSAGE_TEMPLATE_SEND,
+ STATE_MESSAGE_TEMPLATE_WAIT,
+ STATE_UPDATE_CHECK,
+ STATE_LOGIN_AUTH_INIT,
+ STATE_LOGIN_AUTHENTICATE,
+ STATE_LOGIN_NO_DATA_YET,
+ STATE_LOGIN_DOWNLOADING,
+ STATE_LOGIN_PROCESS_RESPONSE,
+ //STATE_USERSERVER_SEND,
+ //STATE_USERSERVER_WAIT,
+ //STATE_LOCATION_SEND,
+ //STATE_LOCATION_WAIT,
+ STATE_WORLD_INIT,
+ STATE_QUICKTIME_INIT,
+ STATE_WORLD_WAIT,
+ STATE_AGENT_SEND,
+ STATE_AGENT_WAIT,
+ STATE_INVENTORY_SEND,
+ STATE_CONNECT_USERSERVER,
+ STATE_MISC,
+ STATE_PRECACHE,
+ STATE_WEARABLES_WAIT,
+ //STATE_INVENTORY_WAIT,
+ //STATE_TOS_AGREEMENT_START,
+ //STATE_TOS_AGREEMENT_WAIT,
+ //STATE_CRITICAL_MESSAGE_START,
+ //STATE_CRITICAL_MESSAGE_WAIT,
+ STATE_CLEANUP,
+ STATE_STARTED
+};
+
+// exorted symbol
+extern S32 gStartupState;
+extern bool gQuickTimeInitialized;
+
+class LLStartUp
+{
+public:
+ static bool canGoFullscreen();
+ // returns true if we are far enough along in startup to allow
+ // going full screen
+};
+
+
+#endif // LL_LLSTARTUP_H
diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp
new file mode 100644
index 0000000000..f4d87e5c6f
--- /dev/null
+++ b/indra/newview/llstatusbar.cpp
@@ -0,0 +1,612 @@
+/**
+ * @file llstatusbar.cpp
+ * @brief LLStatusBar class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstatusbar.h"
+
+#include <iomanip>
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "llparcel.h"
+#include "llstring.h"
+#include "message.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterchat.h"
+#include "llfloaterland.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterscriptdebug.h"
+#include "llhudicon.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llnotify.h"
+#include "llimview.h"
+#include "lltextbox.h"
+#include "llui.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llframetimer.h"
+#include "llvoavatar.h"
+#include "llresmgr.h"
+#include "llworld.h"
+#include "llstatgraph.h"
+#include "llviewermenu.h" // for gMenuBarView
+#include "llviewerparcelmgr.h"
+#include "llviewerthrottle.h"
+#include "llvieweruictrlfactory.h"
+
+#include "lltoolmgr.h"
+#include "llfocusmgr.h"
+#include "viewer.h"
+
+//
+// Globals
+//
+LLStatusBar *gStatusBar = NULL;
+S32 STATUS_BAR_HEIGHT = 0;
+extern S32 MENU_BAR_HEIGHT;
+
+// TODO: these values ought to be in the XML too
+const S32 MENU_PARCEL_SPACING = 1; // Distance from right of menu item to parcel information
+const S32 SIM_STAT_WIDTH = 8;
+const F32 SIM_WARN_FRACTION = 0.75f;
+const F32 SIM_FULL_FRACTION = 0.98f;
+const LLColor4 SIM_OK_COLOR(0.f, 1.f, 0.f, 1.f);
+const LLColor4 SIM_WARN_COLOR(1.f, 1.f, 0.f, 1.f);
+const LLColor4 SIM_FULL_COLOR(1.f, 0.f, 0.f, 1.f);
+const F32 ICON_TIMER_EXPIRY = 3.f; // How long the balance and health icons should flash after a change.
+const F32 ICON_FLASH_FREQUENCY = 2.f;
+const S32 GRAPHIC_FUDGE = 4;
+const S32 TEXT_HEIGHT = 18;
+
+LLStatusBar::LLStatusBar(const std::string& name, const LLRect& rect)
+: LLPanel(name, LLRect(), FALSE), // not mouse opaque
+ mBalance(0),
+ mHealth(100),
+ mSquareMetersCredit(0),
+ mSquareMetersCommitted(0)
+{
+ // status bar can possible overlay menus?
+ mMouseOpaque = FALSE;
+ setIsChrome(TRUE);
+
+ mBalanceTimer = new LLFrameTimer();
+ mHealthTimer = new LLFrameTimer();
+
+ gUICtrlFactory->buildPanel(this,"panel_status_bar.xml");
+
+ // status bar can never get a tab
+ setFocusRoot(FALSE);
+
+ mBtnScriptOut = LLUICtrlFactory::getButtonByName( this, "scriptout" );
+ mBtnHealth = LLUICtrlFactory::getButtonByName( this, "health" );
+ mBtnFly = LLUICtrlFactory::getButtonByName( this, "fly" );
+ mBtnBuild = LLUICtrlFactory::getButtonByName( this, "build" );
+ mBtnScripts = LLUICtrlFactory::getButtonByName( this, "scripts" );
+ mBtnPush = LLUICtrlFactory::getButtonByName( this, "restrictpush" );
+ mBtnBuyLand = LLUICtrlFactory::getButtonByName( this, "buyland" );
+ mBtnBuyCurrency = LLUICtrlFactory::getButtonByName( this, "buycurrency" );
+
+ mTextParcelName = LLUICtrlFactory::getTextBoxByName( this, "ParcelNameText" );
+ mTextBalance = LLUICtrlFactory::getTextBoxByName( this, "BalanceText" );
+
+ mTextHealth = LLUICtrlFactory::getTextBoxByName( this, "HealthText" );
+ mTextTime = LLUICtrlFactory::getTextBoxByName( this, "TimeText" );
+
+ S32 x = mRect.getWidth() - 2;
+ S32 y = 0;
+ LLRect r;
+ r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1);
+ mSGBandwidth = new LLStatGraph("BandwidthGraph", r);
+ mSGBandwidth->setFollows(FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
+ mSGBandwidth->setStat(&gViewerStats->mKBitStat);
+ LLString text = childGetText("bandwidth_tooltip") + " ";
+ LLUIString bandwidth_tooltip = text; // get the text from XML until this widget is XML driven
+ mSGBandwidth->setLabel(bandwidth_tooltip.getString().c_str());
+ mSGBandwidth->setUnits("Kbps");
+ mSGBandwidth->setPrecision(0);
+ addChild(mSGBandwidth);
+ x -= SIM_STAT_WIDTH + 2;
+
+ r.set( x-SIM_STAT_WIDTH, y+MENU_BAR_HEIGHT-1, x, y+1);
+ mSGPacketLoss = new LLStatGraph("PacketLossPercent", r);
+ mSGPacketLoss->setFollows(FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
+ mSGPacketLoss->setStat(&gViewerStats->mPacketsLostPercentStat);
+ text = childGetText("packet_loss_tooltip") + " ";
+ LLUIString packet_loss_tooltip = text; // get the text from XML until this widget is XML driven
+ mSGPacketLoss->setLabel(packet_loss_tooltip.getString().c_str());
+ mSGPacketLoss->setUnits("%");
+ mSGPacketLoss->setMin(0.f);
+ mSGPacketLoss->setMax(5.f);
+ mSGPacketLoss->setThreshold(0, 0.5f);
+ mSGPacketLoss->setThreshold(1, 1.f);
+ mSGPacketLoss->setThreshold(2, 3.f);
+ mSGPacketLoss->setPrecision(1);
+ mSGPacketLoss->mPerSec = FALSE;
+ addChild(mSGPacketLoss);
+
+}
+
+LLStatusBar::~LLStatusBar()
+{
+ delete mBalanceTimer;
+ mBalanceTimer = NULL;
+
+ delete mHealthTimer;
+ mHealthTimer = NULL;
+
+ // LLView destructor cleans up children
+}
+
+BOOL LLStatusBar::postBuild()
+{
+ childSetAction("scriptout", onClickScriptDebug, this);
+ childSetAction("health", onClickHealth, this);
+ childSetAction("fly", onClickFly, this);
+ childSetAction("buyland", onClickBuyLand, this );
+ childSetAction("buycurrency", onClickBuyCurrency, this );
+ childSetAction("build", onClickBuild, this );
+ childSetAction("scripts", onClickScripts, this );
+ childSetAction("restrictpush", onClickPush, this );
+
+ childSetActionTextbox("ParcelNameText", onClickParcelInfo );
+ childSetActionTextbox("BalanceText", onClickBalance );
+
+ return TRUE;
+}
+
+EWidgetType LLStatusBar::getWidgetType() const
+{
+ return WIDGET_TYPE_STATUS_BAR;
+}
+
+LLString LLStatusBar::getWidgetTag() const
+{
+ return LL_STATUS_BAR_TAG;
+}
+
+//-----------------------------------------------------------------------
+// Overrides
+//-----------------------------------------------------------------------
+
+// virtual
+void LLStatusBar::draw()
+{
+ refresh();
+
+ LLView::draw();
+}
+
+
+// Per-frame updates of visibility
+void LLStatusBar::refresh()
+{
+ F32 bwtotal = gViewerThrottle.getMaxBandwidth() / 1000.f;
+ mSGBandwidth->setMin(0.f);
+ mSGBandwidth->setMax(bwtotal*1.25f);
+ mSGBandwidth->setThreshold(0, bwtotal*0.75f);
+ mSGBandwidth->setThreshold(1, bwtotal);
+ mSGBandwidth->setThreshold(2, bwtotal);
+
+ // Get current UTC time, adjusted for the user's clock
+ // being off.
+ U32 utc_time;
+ utc_time = time_corrected();
+
+ // There's only one internal tm buffer.
+ struct tm* internal_time;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ internal_time = utc_to_pacific_time(utc_time, gPacificDaylightTime);
+
+ S32 hour = internal_time->tm_hour;
+ S32 min = internal_time->tm_min;
+
+ std::string am_pm = "AM";
+ if (hour > 11)
+ {
+ hour -= 12;
+ am_pm = "PM";
+ }
+
+ std::string tz = "PST";
+ if (gPacificDaylightTime)
+ {
+ tz = "PDT";
+ }
+ // Zero hour is 12 AM
+ if (hour == 0) hour = 12;
+ std::ostringstream t;
+ t << std::setfill(' ') << std::setw(2) << hour << ":"
+ << std::setfill('0') << std::setw(2) << min
+ << " " << am_pm << " " << tz;
+ mTextTime->setText(t.str().c_str());
+
+ LLRect r;
+ const S32 MENU_RIGHT = gMenuBarView->getRightmostMenuEdge();
+ S32 x = MENU_RIGHT + MENU_PARCEL_SPACING;
+ S32 y = 0;
+
+ LLViewerRegion *region = gAgent.getRegion();
+ LLParcel *parcel = gParcelMgr->getAgentParcel();
+
+ LLRect buttonRect;
+
+ if (LLHUDIcon::iconsNearby())
+ {
+ childGetRect( "scriptout", buttonRect );
+ r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnScriptOut->setRect(r);
+ mBtnScriptOut->setVisible(TRUE);
+ x += buttonRect.getWidth();
+ }
+ else
+ {
+ mBtnScriptOut->setVisible(FALSE);
+ }
+
+ if ((region && region->getAllowDamage()) ||
+ (parcel && parcel->getAllowDamage()) )
+ {
+ // set visibility based on flashing
+ if( mHealthTimer->hasExpired() )
+ {
+ mBtnHealth->setVisible( TRUE );
+ }
+ else
+ {
+ BOOL flash = S32(mHealthTimer->getElapsedSeconds() * ICON_FLASH_FREQUENCY) & 1;
+ mBtnHealth->setVisible( flash );
+ }
+ mTextHealth->setVisible(TRUE);
+
+ // Health
+ childGetRect( "health", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnHealth->setRect(r);
+ x += buttonRect.getWidth();
+
+ const S32 health_width = S32( LLFontGL::sSansSerifSmall->getWidth("100%") );
+ r.set(x, y+TEXT_HEIGHT - 2, x+health_width, y);
+ mTextHealth->setRect(r);
+ x += health_width;
+ }
+ else
+ {
+ // invisible if region doesn't allow damage
+ mBtnHealth->setVisible(FALSE);
+ mTextHealth->setVisible(FALSE);
+ }
+
+ if ((region && region->getBlockFly()) ||
+ (parcel && !parcel->getAllowFly()) )
+ {
+ // No Fly Zone
+ childGetRect( "fly", buttonRect );
+ mBtnFly->setVisible(TRUE);
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnFly->setRect(r);
+ x += buttonRect.getWidth();
+ }
+ else
+ {
+ mBtnFly->setVisible(FALSE);
+ }
+
+ BOOL no_build = parcel && !parcel->getAllowModify();
+ mBtnBuild->setVisible( no_build );
+ if (no_build)
+ {
+ childGetRect( "build", buttonRect );
+ // No Build Zone
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnBuild->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL no_scripts = FALSE;
+ if((region
+ && ((region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
+ || (region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)))
+ || (parcel && !parcel->getAllowOtherScripts()))
+ {
+ no_scripts = TRUE;
+ }
+ mBtnScripts->setVisible( no_scripts );
+ if (no_scripts)
+ {
+ // No scripts
+ childGetRect( "scripts", buttonRect );
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnScripts->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL no_region_push = (region && region->getRestrictPushObject());
+ BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject());
+ mBtnPush->setVisible( no_push );
+ if (no_push)
+ {
+ childGetRect( "restrictpush", buttonRect );
+ // No Push Zone
+ r.setOriginAndSize( x, y-GRAPHIC_FUDGE, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnPush->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ BOOL canBuyLand = parcel
+ && !parcel->isPublic()
+ && gParcelMgr->canAgentBuyParcel(parcel, false);
+ mBtnBuyLand->setVisible(canBuyLand);
+ if (canBuyLand)
+ {
+ childGetRect( "buyland", buttonRect );
+ r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight());
+ mBtnBuyLand->setRect(r);
+ x += buttonRect.getWidth();
+ }
+
+ LLString location_name;
+ if (region)
+ {
+ const LLVector3& agent_pos_region = gAgent.getPositionAgent();
+ S32 pos_x = lltrunc( agent_pos_region.mV[VX] );
+ S32 pos_y = lltrunc( agent_pos_region.mV[VY] );
+ S32 pos_z = lltrunc( agent_pos_region.mV[VZ] );
+
+ // Round the numbers based on the velocity
+ LLVector3 agent_velocity = gAgent.getVelocity();
+ F32 velocity_mag_sq = agent_velocity.magVecSquared();
+
+ const F32 FLY_CUTOFF = 6.f; // meters/sec
+ const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF;
+ const F32 WALK_CUTOFF = 1.5f; // meters/sec
+ const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF;
+
+ if (velocity_mag_sq > FLY_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 4;
+ pos_y -= pos_y % 4;
+ }
+ else if (velocity_mag_sq > WALK_CUTOFF_SQ)
+ {
+ pos_x -= pos_x % 2;
+ pos_y -= pos_y % 2;
+ }
+
+ if (parcel && parcel->getName())
+ {
+ location_name = region->getName()
+ + llformat(" %d, %d, %d (%s) - %s",
+ pos_x, pos_y, pos_z,
+ region->getSimAccessString(),
+ parcel->getName());
+ }
+ else
+ {
+ location_name = region->getName()
+ + llformat(" %d, %d, %d (%s)",
+ pos_x, pos_y, pos_z,
+ region->getSimAccessString());
+ }
+ }
+ else
+ {
+ // no region
+ location_name = "(Unknown)";
+ }
+ mTextParcelName->setText(location_name);
+
+ // Adjust region name and parcel name
+ x += 4;
+
+ const S32 PARCEL_RIGHT = llmin(mTextTime->getRect().mLeft, mTextParcelName->getTextPixelWidth() + x + 5);
+ r.set(x+4, mRect.getHeight() - 2, PARCEL_RIGHT, 0);
+ mTextParcelName->setRect(r);
+}
+
+void LLStatusBar::setVisibleForMouselook(bool visible)
+{
+ mTextBalance->setVisible(visible);
+ mTextTime->setVisible(visible);
+ mSGBandwidth->setVisible(visible);
+ mSGPacketLoss->setVisible(visible);
+ mBtnBuyCurrency->setVisible(visible);
+}
+
+void LLStatusBar::debitBalance(S32 debit)
+{
+ setBalance(getBalance() - debit);
+}
+
+void LLStatusBar::creditBalance(S32 credit)
+{
+ setBalance(getBalance() + credit);
+}
+
+void LLStatusBar::setBalance(S32 balance)
+{
+ LLString balance_str;
+ gResMgr->getMonetaryString( balance_str, balance );
+ mTextBalance->setText( balance_str );
+
+ if (mBalance && (fabs((F32)(mBalance - balance)) > gSavedSettings.getF32("UISndMoneyChangeThreshold")))
+ {
+ if (mBalance > balance)
+ make_ui_sound("UISndMoneyChangeDown");
+ else
+ make_ui_sound("UISndMoneyChangeUp");
+ }
+
+ if( balance != mBalance )
+ {
+ mBalanceTimer->reset();
+ mBalanceTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
+ mBalance = balance;
+ }
+}
+
+void LLStatusBar::setHealth(S32 health)
+{
+ char buffer[MAX_STRING];
+ sprintf(buffer, "%d%%", health);
+ //llinfos << "Setting health to: " << buffer << llendl;
+ mTextHealth->setText(buffer);
+
+ if( mHealth > health )
+ {
+ if (mHealth > (health + gSavedSettings.getF32("UISndHealthReductionThreshold")))
+ {
+ LLVOAvatar *me;
+
+ if ((me = gAgent.getAvatarObject()))
+ {
+ if (me->getSex() == SEX_FEMALE)
+ {
+ make_ui_sound("UISndHealthReductionF");
+ }
+ else
+ {
+ make_ui_sound("UISndHealthReductionM");
+ }
+ }
+ }
+
+ mHealthTimer->reset();
+ mHealthTimer->setTimerExpirySec( ICON_TIMER_EXPIRY );
+ }
+
+ mHealth = health;
+}
+
+S32 LLStatusBar::getBalance() const
+{
+ return mBalance;
+}
+
+
+S32 LLStatusBar::getHealth() const
+{
+ return mHealth;
+}
+
+void LLStatusBar::setLandCredit(S32 credit)
+{
+ mSquareMetersCredit = credit;
+}
+void LLStatusBar::setLandCommitted(S32 committed)
+{
+ mSquareMetersCommitted = committed;
+}
+
+BOOL LLStatusBar::isUserTiered() const
+{
+ return (mSquareMetersCredit > 0);
+}
+
+S32 LLStatusBar::getSquareMetersCredit() const
+{
+ return mSquareMetersCredit;
+}
+
+S32 LLStatusBar::getSquareMetersCommitted() const
+{
+ return mSquareMetersCommitted;
+}
+
+S32 LLStatusBar::getSquareMetersLeft() const
+{
+ return mSquareMetersCredit - mSquareMetersCommitted;
+}
+
+// static
+void LLStatusBar::onClickParcelInfo(void* data)
+{
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+
+ LLFloaterLand::show();
+}
+
+// static
+void LLStatusBar::onClickBalance(void* data)
+{
+ LLFloaterBuyCurrency::buyCurrency();
+}
+
+// static
+void LLStatusBar::onClickBuyCurrency(void* data)
+{
+ LLFloaterBuyCurrency::buyCurrency();
+}
+
+// static
+void LLStatusBar::onClickHealth(void* )
+{
+ LLNotifyBox::showXml("NotSafe");
+}
+
+// static
+void LLStatusBar::onClickScriptDebug(void*)
+{
+ LLFloaterScriptDebug::show(LLUUID::null);
+}
+
+// static
+void LLStatusBar::onClickFly(void* )
+{
+ LLNotifyBox::showXml("NoFly");
+}
+
+// static
+void LLStatusBar::onClickPush(void* )
+{
+ LLNotifyBox::showXml("PushRestricted");
+}
+
+// static
+void LLStatusBar::onClickBuild(void*)
+{
+ LLNotifyBox::showXml("NoBuild");
+}
+
+// static
+void LLStatusBar::onClickScripts(void*)
+{
+ LLViewerRegion* region = gAgent.getRegion();
+ if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
+ {
+ LLNotifyBox::showXml("ScriptsStopped");
+ }
+ else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
+ {
+ LLNotifyBox::showXml("ScriptsNotRunning");
+ }
+ else
+ {
+ LLNotifyBox::showXml("NoOutsideScripts");
+ }
+}
+
+// static
+void LLStatusBar::onClickBuyLand(void*)
+{
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ gParcelMgr->startBuyLand();
+}
diff --git a/indra/newview/llstatusbar.h b/indra/newview/llstatusbar.h
new file mode 100644
index 0000000000..c3f520315c
--- /dev/null
+++ b/indra/newview/llstatusbar.h
@@ -0,0 +1,108 @@
+/**
+ * @file llstatusbar.h
+ * @brief LLStatusBar class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTATUSBAR_H
+#define LL_LLSTATUSBAR_H
+
+#include "llpanel.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 STATUS_BAR_HEIGHT;
+
+class LLButton;
+class LLLineEditor;
+class LLMessageSystem;
+class LLTextBox;
+class LLTextEditor;
+class LLUICtrl;
+class LLUUID;
+class LLFrameTimer;
+class LLStatGraph;
+
+class LLStatusBar
+: public LLPanel
+{
+public:
+ LLStatusBar(const std::string& name, const LLRect& rect );
+ ~LLStatusBar();
+ virtual BOOL postBuild();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ // OVERRIDES
+ virtual void draw();
+
+ // MANIPULATORS
+ void setBalance(S32 balance);
+ void debitBalance(S32 debit);
+ void creditBalance(S32 credit);
+
+ void setHealth(S32 percent);
+
+ void setLandCredit(S32 credit);
+ void setLandCommitted(S32 committed);
+
+ void refresh();
+ void setVisibleForMouselook(bool visible);
+ // some elements should hide in mouselook
+
+ // ACCESSORS
+ S32 getBalance() const;
+ S32 getHealth() const;
+
+ BOOL isUserTiered() const;
+ S32 getSquareMetersCredit() const;
+ S32 getSquareMetersCommitted() const;
+ S32 getSquareMetersLeft() const;
+
+protected:
+ static void onClickParcelInfo(void*);
+ static void onClickBalance(void*);
+ static void onClickBuyCurrency(void*);
+ static void onClickRegionInfo(void*);
+ static void onClickHealth(void*);
+ static void onClickFly(void*);
+ static void onClickPush(void*);
+ static void onClickBuild(void*);
+ static void onClickScripts(void*);
+ static void onClickBuyLand(void*);
+ static void onClickScriptDebug(void*);
+
+protected:
+ LLTextBox *mTextBalance;
+ LLTextBox *mTextHealth;
+ LLTextBox *mTextTime;
+
+ LLButton *mBtnScriptOut;
+ LLButton *mBtnHealth;
+ LLButton *mBtnFly;
+ LLButton *mBtnBuild;
+ LLButton *mBtnScripts;
+ LLButton *mBtnPush;
+ LLButton *mBtnBuyLand;
+
+
+ LLTextBox* mTextParcelName;
+
+ LLStatGraph *mSGBandwidth;
+ LLStatGraph *mSGPacketLoss;
+
+ LLButton *mBtnBuyCurrency;
+
+ S32 mBalance;
+ S32 mHealth;
+ S32 mSquareMetersCredit;
+ S32 mSquareMetersCommitted;
+ LLFrameTimer* mBalanceTimer;
+ LLFrameTimer* mHealthTimer;
+};
+
+extern LLStatusBar *gStatusBar;
+
+#endif
diff --git a/indra/newview/llsurface.cpp b/indra/newview/llsurface.cpp
new file mode 100644
index 0000000000..fc0c46bbe1
--- /dev/null
+++ b/indra/newview/llsurface.cpp
@@ -0,0 +1,1337 @@
+/**
+ * @file llsurface.cpp
+ * @brief Implementation of LLSurface class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsurface.h"
+
+#include "llviewerimagelist.h"
+#include "llpatchvertexarray.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+#include "bitpack.h"
+#include "llviewerobjectlist.h"
+#include "llregionhandle.h"
+#include "llagent.h"
+#include "viewer.h"
+#include "llworld.h"
+#include "llviewercontrol.h"
+#include "llviewerimage.h"
+#include "llsurfacepatch.h"
+#include "llvosurfacepatch.h"
+#include "llvowater.h"
+#include "pipeline.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "noise.h"
+#include "llviewercamera.h"
+#include "llglheaders.h"
+#include "lldrawpool.h"
+#include "lldrawable.h"
+
+extern LLPipeline gPipeline;
+
+LLColor4U MAX_WATER_COLOR(0, 48, 96, 240);
+
+
+S32 LLSurface::sTextureSize = 256;
+S32 LLSurface::sTexelsUpdated = 0;
+F32 LLSurface::sTextureUpdateTime = 0.f;
+LLStat LLSurface::sTexelsUpdatedPerSecStat;
+
+extern void bad_network_handler();
+
+// ---------------- LLSurface:: Public Members ---------------
+
+LLSurface::LLSurface(U32 type, LLViewerRegion *regionp) :
+ mGridsPerEdge(0),
+ mOOGridsPerEdge(0.f),
+ mPatchesPerEdge(0),
+ mType(type),
+ mOriginGlobal(0.0, 0.0, 0.0),
+ mSTexturep(NULL),
+ mWaterTexturep(NULL),
+ mGridsPerPatchEdge(0),
+ mMetersPerGrid(1.0f),
+ mMetersPerEdge(1.0f),
+ mRegionp(regionp)
+{
+ // Surface data
+ mSurfaceZ = NULL;
+ mNorm = NULL;
+
+ // Patch data
+ mPatchList = NULL;
+
+ // One of each for each camera
+ mVisiblePatchCount = 0;
+
+ mHasZData = FALSE;
+ // "uninitialized" min/max z
+ mMinZ = 10000.f;
+ mMaxZ = -10000.f;
+
+ mWaterObjp = NULL;
+
+ // In here temporarily.
+ mSurfacePatchUpdateCount = 0;
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ mNeighbors[i] = NULL;
+ }
+}
+
+
+LLSurface::~LLSurface()
+{
+ delete [] mSurfaceZ;
+ mSurfaceZ = NULL;
+
+ delete [] mNorm;
+
+ mGridsPerEdge = 0;
+ mGridsPerPatchEdge = 0;
+ mPatchesPerEdge = 0;
+ mNumberOfPatches = 0;
+ destroyPatchData();
+
+ LLDrawPool *poolp = gPipeline.findPool(LLDrawPool::POOL_TERRAIN, mSTexturep);
+ if (!poolp)
+ {
+ llwarns << "No pool for terrain on destruction!" << llendl;
+ }
+ else if (poolp->mReferences.empty())
+ {
+ gPipeline.removePool(poolp);
+ // Don't enable this until we blitz the draw pool for it as well. -- djs
+ if (mSTexturep)
+ {
+ gImageList.deleteImage(mSTexturep);
+ mSTexturep = NULL;
+ }
+ if (mWaterTexturep)
+ {
+ gImageList.deleteImage(mWaterTexturep);
+ mWaterTexturep = NULL;
+ }
+ }
+ else
+ {
+ llerrs << "Terrain pool not empty!" << llendl;
+ }
+}
+
+void LLSurface::initClasses()
+{
+}
+
+void LLSurface::setRegion(LLViewerRegion *regionp)
+{
+ mRegionp = regionp;
+}
+
+// Assumes that arguments are powers of 2, and that
+// grids_per_edge / grids_per_patch_edge = power of 2
+void LLSurface::create(const S32 grids_per_edge,
+ const S32 grids_per_patch_edge,
+ const LLVector3d &origin_global,
+ const F32 width)
+{
+ // Initialize various constants for the surface
+ mGridsPerEdge = grids_per_edge + 1; // Add 1 for the east and north buffer
+ mOOGridsPerEdge = 1.f / mGridsPerEdge;
+ mGridsPerPatchEdge = grids_per_patch_edge;
+ mPatchesPerEdge = (mGridsPerEdge - 1) / mGridsPerPatchEdge;
+ mNumberOfPatches = mPatchesPerEdge * mPatchesPerEdge;
+ mMetersPerGrid = width / ((F32)(mGridsPerEdge - 1));
+ mMetersPerEdge = mMetersPerGrid * (mGridsPerEdge - 1);
+
+ mOriginGlobal.setVec(origin_global);
+
+ mPVArray.create(mGridsPerEdge, mGridsPerPatchEdge, gWorldPointer->getRegionScale());
+
+ S32 number_of_grids = mGridsPerEdge * mGridsPerEdge;
+
+ /////////////////////////////////////
+ //
+ // Initialize data arrays for surface
+ ///
+ mSurfaceZ = new F32[number_of_grids];
+ mNorm = new LLVector3[number_of_grids];
+
+ // Reset the surface to be a flat square grid
+ for(S32 i=0; i < number_of_grids; i++)
+ {
+ // Surface is flat and zero
+ // Normals all point up
+ mSurfaceZ[i] = 0.0f;
+ mNorm[i].setVec(0.f, 0.f, 1.f);
+ }
+
+
+ mVisiblePatchCount = 0;
+
+
+ ///////////////////////
+ //
+ // Initialize textures
+ //
+
+ initTextures();
+
+ // Has to be done after texture initialization
+ createPatchData();
+}
+
+LLViewerImage* LLSurface::getSTexture()
+{
+ if (mSTexturep.notNull() && !mSTexturep->getHasGLTexture())
+ {
+ createSTexture();
+ }
+ return mSTexturep;
+}
+
+LLViewerImage* LLSurface::getWaterTexture()
+{
+ if (mWaterTexturep.notNull() && !mWaterTexturep->getHasGLTexture())
+ {
+ createWaterTexture();
+ }
+ return mWaterTexturep;
+}
+
+void LLSurface::createSTexture()
+{
+ if (!mSTexturep)
+ {
+ // Fill with dummy gray data.
+ LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize, sTextureSize, 3);
+ U8 *default_texture = raw->getData();
+ for (S32 i = 0; i < sTextureSize; i++)
+ {
+ for (S32 j = 0; j < sTextureSize; j++)
+ {
+ *(default_texture + (i*sTextureSize + j)*3) = 128;
+ *(default_texture + (i*sTextureSize + j)*3 + 1) = 128;
+ *(default_texture + (i*sTextureSize + j)*3 + 2) = 128;
+ }
+ }
+
+ mSTexturep = new LLViewerImage(raw, FALSE);
+ mSTexturep->dontDiscard();
+ mSTexturep->bind();
+ mSTexturep->setClamp(TRUE, TRUE);
+ gImageList.addImage(mSTexturep);
+ }
+}
+
+void LLSurface::createWaterTexture()
+{
+ if (!mWaterTexturep)
+ {
+ // Create the water texture
+ LLPointer<LLImageRaw> raw = new LLImageRaw(sTextureSize/2, sTextureSize/2, 4);
+ U8 *default_texture = raw->getData();
+ for (S32 i = 0; i < sTextureSize/2; i++)
+ {
+ for (S32 j = 0; j < sTextureSize/2; j++)
+ {
+ *(default_texture + (i*sTextureSize/2 + j)*4) = MAX_WATER_COLOR.mV[0];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 1) = MAX_WATER_COLOR.mV[1];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 2) = MAX_WATER_COLOR.mV[2];
+ *(default_texture + (i*sTextureSize/2 + j)*4 + 3) = MAX_WATER_COLOR.mV[3];
+ }
+ }
+ mWaterTexturep = new LLViewerImage(raw, FALSE);
+ mWaterTexturep->dontDiscard();
+ mWaterTexturep->bind();
+ mWaterTexturep->setClamp(TRUE, TRUE);
+ gImageList.addImage(mWaterTexturep);
+ }
+}
+
+void LLSurface::initTextures()
+{
+ ///////////////////////
+ //
+ // Main surface texture
+ //
+ createSTexture();
+
+ ///////////////////////
+ //
+ // Water texture
+ //
+ if (gSavedSettings.getBOOL("RenderWater") )
+ {
+ createWaterTexture();
+ mWaterObjp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, mRegionp);
+ gPipeline.addObject(mWaterObjp);
+ LLVector3d water_pos_global = from_region_handle(mRegionp->getHandle());
+ water_pos_global += LLVector3d(128.0, 128.0, DEFAULT_WATER_HEIGHT);
+ mWaterObjp->setPositionGlobal(water_pos_global);
+ }
+}
+
+
+void LLSurface::setOriginGlobal(const LLVector3d &origin_global)
+{
+ LLVector3d new_origin_global;
+ mOriginGlobal = origin_global;
+ LLSurfacePatch *patchp;
+ S32 i, j;
+ // Need to update the southwest corners of the patches
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+
+ new_origin_global = patchp->getOriginGlobal();
+
+ new_origin_global.mdV[0] = mOriginGlobal.mdV[0] + i * mMetersPerGrid * mGridsPerPatchEdge;
+ new_origin_global.mdV[1] = mOriginGlobal.mdV[1] + j * mMetersPerGrid * mGridsPerPatchEdge;
+ patchp->setOriginGlobal(new_origin_global);
+ }
+ }
+
+ // Hack!
+ if (mWaterObjp.notNull() && mWaterObjp->mDrawable.notNull())
+ {
+ const F64 x = origin_global.mdV[VX] + 128.0;
+ const F64 y = origin_global.mdV[VY] + 128.0;
+ const F64 z = mWaterObjp->getPositionGlobal().mdV[VZ];
+
+ LLVector3d water_origin_global(x, y, z);
+
+ mWaterObjp->setPositionGlobal(water_origin_global);
+ gPipeline.markMoved(mWaterObjp->mDrawable);
+ }
+}
+
+
+void LLSurface::connectNeighbor(LLSurface *neighborp, U32 direction)
+{
+ S32 i;
+ LLSurfacePatch *patchp, *neighbor_patchp;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ mNeighbors[direction] = neighborp;
+ neighborp->mNeighbors[gDirOpposite[direction]] = this;
+
+ // Connect patches
+ if (NORTHEAST == direction)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(0, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateNorthEdge(); // Only update one of north or east.
+ patchp->dirtyZ();
+ }
+ else if (NORTHWEST == direction)
+ {
+ patchp = getPatch(0, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+ }
+ else if (SOUTHWEST == direction)
+ {
+ patchp = getPatch(0, 0);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateNorthEdge(); // Only update one of north or east.
+ neighbor_patchp->dirtyZ();
+ }
+ else if (SOUTHEAST == direction)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, 0);
+ neighbor_patchp = neighborp->getPatch(0, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+ }
+ else if (EAST == direction)
+ {
+ // Do east/west connections, first
+ for (i = 0; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateEastEdge();
+ patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 0; i < (S32)mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i+1);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHWEST);
+ }
+ // Now do southeast/northwest connections
+ for (i = 1; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(mPatchesPerEdge - 1, i);
+ neighbor_patchp = neighborp->getPatch(0, i-1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHWEST);
+ }
+ }
+ else if (NORTH == direction)
+ {
+ // Do north/south connections, first
+ for (i = 0; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ patchp->updateNorthEdge();
+ patchp->dirtyZ();
+ }
+
+ // Do northeast/southwest connections
+ for (i = 0; i < (S32)mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i+1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHWEST);
+ }
+ // Do southeast/northwest connections
+ for (i = 1; i < (S32)mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, mPatchesPerEdge - 1);
+ neighbor_patchp = neighborp->getPatch(i-1, 0);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHEAST);
+ }
+ }
+ else if (WEST == direction)
+ {
+ // Do east/west connections, first
+ for (i = 0; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateEastEdge();
+ neighbor_patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 1; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHEAST);
+ }
+
+ // Now do northwest/southeast connections
+ for (i = 0; i < mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(0, i);
+ neighbor_patchp = neighborp->getPatch(mPatchesPerEdge - 1, i + 1);
+
+ patchp->connectNeighbor(neighbor_patchp, NORTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, SOUTHEAST);
+ }
+ }
+ else if (SOUTH == direction)
+ {
+ // Do north/south connections, first
+ for (i = 0; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, direction);
+ neighbor_patchp->connectNeighbor(patchp, gDirOpposite[direction]);
+
+ neighbor_patchp->updateNorthEdge();
+ neighbor_patchp->dirtyZ();
+ }
+
+ // Now do northeast/southwest connections
+ for (i = 1; i < mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i - 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHWEST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHEAST);
+ }
+ // Now do northeast/southwest connections
+ for (i = 0; i < mPatchesPerEdge - 1; i++)
+ {
+ patchp = getPatch(i, 0);
+ neighbor_patchp = neighborp->getPatch(i + 1, mPatchesPerEdge - 1);
+
+ patchp->connectNeighbor(neighbor_patchp, SOUTHEAST);
+ neighbor_patchp->connectNeighbor(patchp, NORTHWEST);
+ }
+ }
+}
+
+void LLSurface::disconnectNeighbor(LLSurface *surfacep)
+{
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (surfacep == mNeighbors[i])
+ {
+ mNeighbors[i] = NULL;
+ }
+ }
+
+ // Iterate through surface patches, removing any connectivity to removed surface.
+ for (i = 0; i < mNumberOfPatches; i++)
+ {
+ (mPatchList + i)->disconnectNeighbor(surfacep);
+ }
+}
+
+
+void LLSurface::disconnectAllNeighbors()
+{
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (mNeighbors[i])
+ {
+ mNeighbors[i]->disconnectNeighbor(this);
+ mNeighbors[i] = NULL;
+ }
+ }
+}
+
+
+
+const LLVector3d &LLSurface::getOriginGlobal() const
+{
+ return mOriginGlobal;
+}
+
+LLVector3 LLSurface::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+F32 LLSurface::getMetersPerGrid() const
+{
+ return mMetersPerGrid;
+}
+
+S32 LLSurface::getGridsPerEdge() const
+{
+ return mGridsPerEdge;
+}
+
+S32 LLSurface::getPatchesPerEdge() const
+{
+ return mPatchesPerEdge;
+}
+
+S32 LLSurface::getGridsPerPatchEdge() const
+{
+ return mGridsPerPatchEdge;
+}
+
+void LLSurface::moveZ(const S32 x, const S32 y, const F32 delta)
+{
+ llassert(x >= 0);
+ llassert(y >= 0);
+ llassert(x < mGridsPerEdge);
+ llassert(y < mGridsPerEdge);
+ mSurfaceZ[x + y*mGridsPerEdge] += delta;
+}
+
+
+void LLSurface::updatePatchVisibilities(LLAgent &agent)
+{
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(gAgent.getCameraPositionGlobal());
+
+ LLSurfacePatch *patchp;
+
+ mVisiblePatchCount = 0;
+ for (S32 i=0; i<mNumberOfPatches; i++)
+ {
+ patchp = mPatchList + i;
+
+ patchp->updateVisibility();
+ if (patchp->getVisible())
+ {
+ mVisiblePatchCount++;
+ patchp->updateCameraDistanceRegion(pos_region);
+ }
+ }
+}
+
+
+
+BOOL LLSurface::idleUpdate()
+{
+ if (!gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TERRAIN))
+ {
+ return TRUE;
+ }
+
+ // Perform idle time update of non-critical stuff.
+ // In this case, texture and normal updates.
+ LLTimer update_timer;
+ LLSurfacePatch *patchp = NULL;
+
+ // If the Z height data has changed, we need to rebuild our
+ // property line vertex arrays.
+ if (mDirtyPatchList.count() > 0)
+ {
+ getRegion()->dirtyHeights();
+ }
+
+ S32 i = 0;
+ while (i < mDirtyPatchList.count())
+ {
+ patchp = mDirtyPatchList[i];
+ patchp->updateNormals();
+ patchp->updateVerticalStats();
+
+ if ((update_timer.getElapsedTimeF32() < 0.05f) && patchp->updateTexture())
+ {
+ patchp->clearDirty();
+ mDirtyPatchList.remove(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ return TRUE;
+}
+
+// TODO -- move this to LLViewerRegion class
+void LLSurface::renderSurfaceBounds()
+{
+ // Shows the edge of the surface, so that visibility across regions can be seen
+ LLVector3 origin_agent = getOriginAgent();
+
+ glPushMatrix();
+ LLGLSNoTexture no_texture;
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+ glTranslatef(origin_agent.mV[VX] + (region_width_meters * 0.005f),
+ origin_agent.mV[VY] + (region_width_meters * 0.005f), 0.f);
+
+ glColor4ub(0, 128, 0, 64);
+
+ F32 length = region_width_meters * 0.995f;
+ F32 height = length/8.0f;
+
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+ glBegin(GL_QUADS);
+ glVertex3f(length, 0, 0);
+ glVertex3f(0,0, 0);
+ glVertex3f(0,0, height);
+ glVertex3f(length,0, height);
+
+ glVertex3f(length,0, height);
+ glVertex3f(0,0, height);
+ glVertex3f(0,0, 0);
+ glVertex3f(length, 0, 0);
+ glEnd();
+ glTranslatef(length, 0, 0);
+ glRotated(90, 0, 0, 1);
+
+ glPopMatrix();
+}
+
+
+void LLSurface::decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch)
+{
+
+ LLPatchHeader ph;
+ S32 j, i;
+ S32 patch[LARGE_PATCH_SIZE*LARGE_PATCH_SIZE];
+ LLSurfacePatch *patchp;
+
+ init_patch_decompressor(gopp->patch_size);
+ gopp->stride = mGridsPerEdge;
+ set_group_of_patch_header(gopp);
+
+ while (1)
+ {
+ decode_patch_header(bitpack, &ph);
+ if (ph.quant_wbits == END_OF_PATCHES)
+ {
+ break;
+ }
+
+ i = ph.patchids >> 5;
+ j = ph.patchids & 0x1F;
+
+ if ((i >= mPatchesPerEdge) || (j >= mPatchesPerEdge))
+ {
+ llwarns << "Received invalid terrain packet - patch header patch ID incorrect!"
+ << " patches per edge " << mPatchesPerEdge
+ << " i " << i
+ << " j " << j
+ << " dc_offset " << ph.dc_offset
+ << " range " << (S32)ph.range
+ << " quant_wbits " << (S32)ph.quant_wbits
+ << " patchids " << (S32)ph.patchids
+ << llendl;
+ bad_network_handler();
+ return;
+ }
+
+ patchp = &mPatchList[j*mPatchesPerEdge + i];
+
+
+ decode_patch(bitpack, patch);
+ decompress_patch(patchp->getDataZ(), patch, &ph);
+
+ // Update edges for neighbors. Need to guarantee that this gets done before we generate vertical stats.
+ patchp->updateNorthEdge();
+ patchp->updateEastEdge();
+ if (patchp->getNeighborPatch(WEST))
+ {
+ patchp->getNeighborPatch(WEST)->updateEastEdge();
+ }
+ if (patchp->getNeighborPatch(SOUTHWEST))
+ {
+ patchp->getNeighborPatch(SOUTHWEST)->updateEastEdge();
+ patchp->getNeighborPatch(SOUTHWEST)->updateNorthEdge();
+ }
+ if (patchp->getNeighborPatch(SOUTH))
+ {
+ patchp->getNeighborPatch(SOUTH)->updateNorthEdge();
+ }
+
+ // Dirty patch statistics, and flag that the patch has data.
+ patchp->dirtyZ();
+ patchp->setHasReceivedData();
+ }
+}
+
+
+// Retrurns TRUE if "position" is within the bounds of surface.
+// "position" is region-local
+BOOL LLSurface::containsPosition(const LLVector3 &position)
+{
+ if (position.mV[VX] < 0.0f || position.mV[VX] > mMetersPerEdge ||
+ position.mV[VY] < 0.0f || position.mV[VY] > mMetersPerEdge)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+F32 LLSurface::resolveHeightRegion(const F32 x, const F32 y) const
+{
+ F32 height = 0.0f;
+ F32 oometerspergrid = 1.f/mMetersPerGrid;
+
+ // Check to see if v is actually above surface
+ // We use (mGridsPerEdge-1) below rather than (mGridsPerEdge)
+ // becuase of the east and north buffers
+
+ if (x >= 0.f &&
+ x <= mMetersPerEdge &&
+ y >= 0.f &&
+ y <= mMetersPerEdge)
+ {
+ const S32 left = llfloor(x * oometerspergrid);
+ const S32 bottom = llfloor(y * oometerspergrid);
+
+ // Don't walk off the edge of the array!
+ const S32 right = ( left+1 < (S32)mGridsPerEdge-1 ? left+1 : left );
+ const S32 top = ( bottom+1 < (S32)mGridsPerEdge-1 ? bottom+1 : bottom );
+
+ // Figure out if v is in first or second triangle of the square
+ // and calculate the slopes accordingly
+ // | |
+ // -(i,j+1)---(i+1,j+1)--
+ // | 1 / | ^
+ // | / 2 | |
+ // | / | j
+ // --(i,j)----(i+1,j)--
+ // | |
+ //
+ // i ->
+ // where N = mGridsPerEdge
+
+ const F32 left_bottom = getZ( left, bottom );
+ const F32 right_bottom = getZ( right, bottom );
+ const F32 left_top = getZ( left, top );
+ const F32 right_top = getZ( right, top );
+
+ // dx and dy are incremental steps from (mSurface + k)
+ F32 dx = x - left * mMetersPerGrid;
+ F32 dy = y - bottom * mMetersPerGrid;
+
+ if (dy > dx)
+ {
+ // triangle 1
+ dy *= left_top - left_bottom;
+ dx *= right_top - left_top;
+ }
+ else
+ {
+ // triangle 2
+ dx *= right_bottom - left_bottom;
+ dy *= right_top - right_bottom;
+ }
+ height = left_bottom + (dx + dy) * oometerspergrid;
+ }
+ return height;
+}
+
+
+F32 LLSurface::resolveHeightGlobal(const LLVector3d& v) const
+{
+ if (!mRegionp)
+ {
+ return 0.f;
+ }
+
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(v);
+
+ return resolveHeightRegion(pos_region);
+}
+
+
+LLVector3 LLSurface::resolveNormalGlobal(const LLVector3d& pos_global) const
+{
+ if (!mSurfaceZ)
+ {
+ // Hmm. Uninitialized surface!
+ return LLVector3::z_axis;
+ }
+ //
+ // Returns the vector normal to a surface at location specified by vector v
+ //
+ F32 oometerspergrid = 1.f/mMetersPerGrid;
+ LLVector3 normal;
+ F32 dzx, dzy;
+
+ if (pos_global.mdV[VX] >= mOriginGlobal.mdV[VX] &&
+ pos_global.mdV[VX] < mOriginGlobal.mdV[VX] + mMetersPerEdge &&
+ pos_global.mdV[VY] >= mOriginGlobal.mdV[VY] &&
+ pos_global.mdV[VY] < mOriginGlobal.mdV[VY] + mMetersPerEdge)
+ {
+ U32 i, j, k;
+ F32 dx, dy;
+ i = (U32) ((pos_global.mdV[VX] - mOriginGlobal.mdV[VX]) * oometerspergrid);
+ j = (U32) ((pos_global.mdV[VY] - mOriginGlobal.mdV[VY]) * oometerspergrid );
+ k = i + j*mGridsPerEdge;
+
+ // Figure out if v is in first or second triangle of the square
+ // and calculate the slopes accordingly
+ // | |
+ // -(k+N)---(k+1+N)--
+ // | 1 / | ^
+ // | / 2 | |
+ // | / | j
+ // --(k)----(k+1)--
+ // | |
+ //
+ // i ->
+ // where N = mGridsPerEdge
+
+ // dx and dy are incremental steps from (mSurface + k)
+ dx = (F32)(pos_global.mdV[VX] - i*mMetersPerGrid - mOriginGlobal.mdV[VX]);
+ dy = (F32)(pos_global.mdV[VY] - j*mMetersPerGrid - mOriginGlobal.mdV[VY]);
+ if (dy > dx)
+ { // triangle 1
+ dzx = *(mSurfaceZ + k + 1 + mGridsPerEdge) - *(mSurfaceZ + k + mGridsPerEdge);
+ dzy = *(mSurfaceZ + k) - *(mSurfaceZ + k + mGridsPerEdge);
+ normal.setVec(-dzx,dzy,1);
+ }
+ else
+ { // triangle 2
+ dzx = *(mSurfaceZ + k) - *(mSurfaceZ + k + 1);
+ dzy = *(mSurfaceZ + k + 1 + mGridsPerEdge) - *(mSurfaceZ + k + 1);
+ normal.setVec(dzx,-dzy,1);
+ }
+ }
+ normal.normVec();
+ return normal;
+
+
+}
+
+LLSurfacePatch *LLSurface::resolvePatchRegion(const F32 x, const F32 y) const
+{
+// x and y should be region-local coordinates.
+// If x and y are outside of the surface, then the returned
+// index will be for the nearest boundary patch.
+//
+// 12 | 13| 14| 15
+// | | |
+// +---+---+---+---+
+// | 12| 13| 14| 15|
+// ----+---+---+---+---+-----
+// 8 | 8 | 9 | 10| 11| 11
+// ----+---+---+---+---+-----
+// 4 | 4 | 5 | 6 | 7 | 7
+// ----+---+---+---+---+-----
+// | 0 | 1 | 2 | 3 |
+// +---+---+---+---+
+// | | |
+// 0 | 1 | 2 | 3
+//
+
+// When x and y are not region-local do the following first
+
+ S32 i, j;
+ if (x < 0.0f)
+ {
+ i = 0;
+ }
+ else if (x >= mMetersPerEdge)
+ {
+ i = mPatchesPerEdge - 1;
+ }
+ else
+ {
+ i = (U32) (x / (mMetersPerGrid * mGridsPerPatchEdge));
+ }
+
+ if (y < 0.0f)
+ {
+ j = 0;
+ }
+ else if (y >= mMetersPerEdge)
+ {
+ j = mPatchesPerEdge - 1;
+ }
+ else
+ {
+ j = (U32) (y / (mMetersPerGrid * mGridsPerPatchEdge));
+ }
+
+ // *NOTE: Super paranoia code follows.
+ S32 index = i + j * mPatchesPerEdge;
+ if((index < 0) || (index >= mNumberOfPatches))
+ {
+ if(0 == mNumberOfPatches)
+ {
+ llwarns << "No patches for current region!" << llendl;
+ return NULL;
+ }
+ S32 old_index = index;
+ index = llclamp(old_index, 0, (mNumberOfPatches - 1));
+ llwarns << "Clamping out of range patch index " << old_index
+ << " to " << index << llendl;
+ }
+ return &(mPatchList[index]);
+}
+
+
+LLSurfacePatch *LLSurface::resolvePatchRegion(const LLVector3 &pos_region) const
+{
+ return resolvePatchRegion(pos_region.mV[VX], pos_region.mV[VY]);
+}
+
+
+LLSurfacePatch *LLSurface::resolvePatchGlobal(const LLVector3d &pos_global) const
+{
+ LLVector3 pos_region = mRegionp->getPosRegionFromGlobal(pos_global);
+ return resolvePatchRegion(pos_region);
+}
+
+
+std::ostream& operator<<(std::ostream &s, const LLSurface &S)
+{
+ s << "{ \n";
+ s << " mGridsPerEdge = " << S.mGridsPerEdge - 1 << " + 1\n";
+ s << " mGridsPerPatchEdge = " << S.mGridsPerPatchEdge << "\n";
+ s << " mPatchesPerEdge = " << S.mPatchesPerEdge << "\n";
+ s << " mOriginGlobal = " << S.mOriginGlobal << "\n";
+ s << " mMetersPerGrid = " << S.mMetersPerGrid << "\n";
+ s << " mVisiblePatchCount = " << S.mVisiblePatchCount << "\n";
+ s << "}";
+ return s;
+}
+
+
+// ---------------- LLSurface:: Protected ----------------
+
+void LLSurface::createPatchData()
+{
+ // Assumes mGridsPerEdge, mGridsPerPatchEdge, and mPatchesPerEdge have been properly set
+ // TODO -- check for create() called when surface is not empty
+ S32 i, j;
+ LLSurfacePatch *patchp;
+
+ // Allocate memory
+ mPatchList = new LLSurfacePatch[mNumberOfPatches];
+
+ // One of each for each camera
+ mVisiblePatchCount = mNumberOfPatches;
+
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+ patchp->setSurface(this);
+ }
+ }
+
+ for (j=0; j<mPatchesPerEdge; j++)
+ {
+ for (i=0; i<mPatchesPerEdge; i++)
+ {
+ patchp = getPatch(i, j);
+ patchp->mHasReceivedData = FALSE;
+ patchp->mSTexUpdate = TRUE;
+
+ S32 data_offset = i * mGridsPerPatchEdge + j * mGridsPerPatchEdge * mGridsPerEdge;
+
+ patchp->setDataZ(mSurfaceZ + data_offset);
+ patchp->setDataNorm(mNorm + data_offset);
+
+
+ // We make each patch point to its neighbors so we can do resolution checking
+ // when butting up different resolutions. Patches that don't have neighbors
+ // somewhere will point to NULL on that side.
+ if (i < mPatchesPerEdge-1)
+ {
+ patchp->setNeighborPatch(EAST,getPatch(i+1, j));
+ }
+ else
+ {
+ patchp->setNeighborPatch(EAST, NULL);
+ }
+
+ if (j < mPatchesPerEdge-1)
+ {
+ patchp->setNeighborPatch(NORTH, getPatch(i, j+1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTH, NULL);
+ }
+
+ if (i > 0)
+ {
+ patchp->setNeighborPatch(WEST, getPatch(i - 1, j));
+ }
+ else
+ {
+ patchp->setNeighborPatch(WEST, NULL);
+ }
+
+ if (j > 0)
+ {
+ patchp->setNeighborPatch(SOUTH, getPatch(i, j-1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTH, NULL);
+ }
+
+ if (i < (mPatchesPerEdge-1) && j < (mPatchesPerEdge-1))
+ {
+ patchp->setNeighborPatch(NORTHEAST, getPatch(i + 1, j + 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTHEAST, NULL);
+ }
+
+ if (i > 0 && j < (mPatchesPerEdge-1))
+ {
+ patchp->setNeighborPatch(NORTHWEST, getPatch(i - 1, j + 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(NORTHWEST, NULL);
+ }
+
+ if (i > 0 && j > 0)
+ {
+ patchp->setNeighborPatch(SOUTHWEST, getPatch(i - 1, j - 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTHWEST, NULL);
+ }
+
+ if (i < (mPatchesPerEdge-1) && j > 0)
+ {
+ patchp->setNeighborPatch(SOUTHEAST, getPatch(i + 1, j - 1));
+ }
+ else
+ {
+ patchp->setNeighborPatch(SOUTHEAST, NULL);
+ }
+
+ LLVector3d origin_global;
+ origin_global.mdV[0] = mOriginGlobal.mdV[0] + i * mMetersPerGrid * mGridsPerPatchEdge;
+ origin_global.mdV[1] = mOriginGlobal.mdV[0] + j * mMetersPerGrid * mGridsPerPatchEdge;
+ origin_global.mdV[2] = 0.f;
+ patchp->setOriginGlobal(origin_global);
+ }
+ }
+}
+
+
+void LLSurface::destroyPatchData()
+{
+ // Delete all of the cached patch data for these patches.
+
+ delete [] mPatchList;
+ mPatchList = NULL;
+ mVisiblePatchCount = 0;
+}
+
+
+void LLSurface::setTextureSize(const S32 texture_size)
+{
+ sTextureSize = texture_size;
+}
+
+
+U32 LLSurface::getRenderLevel(const U32 render_stride) const
+{
+ return mPVArray.mRenderLevelp[render_stride];
+}
+
+
+U32 LLSurface::getRenderStride(const U32 render_level) const
+{
+ return mPVArray.mRenderStridep[render_level];
+}
+
+
+LLSurfacePatch *LLSurface::getPatch(const S32 x, const S32 y) const
+{
+ if ((x < 0) || (x >= mPatchesPerEdge))
+ {
+ llerrs << "Asking for patch out of bounds" << llendl;
+ return NULL;
+ }
+ if ((y < 0) || (y >= mPatchesPerEdge))
+ {
+ llerrs << "Asking for patch out of bounds" << llendl;
+ return NULL;
+ }
+
+ return mPatchList + x + y*mPatchesPerEdge;
+}
+
+
+void LLSurface::dirtyAllPatches()
+{
+ S32 i;
+ for (i = 0; i < mNumberOfPatches; i++)
+ {
+ mPatchList[i].dirtyZ();
+ }
+}
+
+void LLSurface::dirtySurfacePatch(LLSurfacePatch *patchp)
+{
+ // Put surface patch on dirty surface patch list
+ if (-1 == mDirtyPatchList.find(patchp))
+ {
+ mDirtyPatchList.put(patchp);
+ }
+}
+
+
+void LLSurface::setWaterHeight(F32 height)
+{
+ if (!mWaterObjp.isNull())
+ {
+ LLVector3 water_pos_region = mWaterObjp->getPositionRegion();
+ water_pos_region.mV[VZ] = height;
+ mWaterObjp->setPositionRegion(water_pos_region);
+ }
+ else
+ {
+ llwarns << "LLSurface::setWaterHeight with no water object!" << llendl;
+ }
+}
+
+F32 LLSurface::getWaterHeight() const
+{
+ if (!mWaterObjp.isNull())
+ {
+ // we have a water object, the usual case
+ return mWaterObjp->getPositionRegion().mV[VZ];
+ }
+ else
+ {
+ return DEFAULT_WATER_HEIGHT;
+ }
+}
+
+
+BOOL LLSurface::generateWaterTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ if (!getWaterTexture())
+ {
+ return FALSE;
+ }
+
+ S32 tex_width = mWaterTexturep->getWidth();
+ S32 tex_height = mWaterTexturep->getHeight();
+ S32 tex_comps = mWaterTexturep->getComponents();
+ S32 tex_stride = tex_width * tex_comps;
+ LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps);
+ U8 *rawp = raw->getData();
+
+ F32 scale = 256.f * getMetersPerGrid() / (F32)tex_width;
+ F32 scale_inv = 1.f / scale;
+
+ 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);
+
+ if (x_end > tex_width)
+ {
+ x_end = tex_width;
+ }
+ if (y_end > tex_width)
+ {
+ y_end = tex_width;
+ }
+
+ LLVector3d origin_global = from_region_handle(getRegion()->getHandle());
+
+ // OK, for now, just have the composition value equal the height at the point.
+ LLVector3 location;
+ LLColor4U coloru;
+
+ const F32 WATER_HEIGHT = getWaterHeight();
+
+ S32 i, j, offset;
+ for (j = y_begin; j < y_end; j++)
+ {
+ for (i = x_begin; i < x_end; i++)
+ {
+ //F32 nv[2];
+ //nv[0] = i/256.f;
+ //nv[1] = j/256.f;
+ // const S32 modulation = noise2(nv)*40;
+ offset = j*tex_stride + i*tex_comps;
+ location.mV[VX] = i*scale;
+ location.mV[VY] = j*scale;
+
+ // Sample multiple points
+ const F32 height = resolveHeightRegion(location);
+
+ if (height > WATER_HEIGHT)
+ {
+ // Above water...
+ coloru = MAX_WATER_COLOR;
+ coloru.mV[3] = ABOVE_WATERLINE_ALPHA;
+ *(rawp + offset++) = coloru.mV[0];
+ *(rawp + offset++) = coloru.mV[1];
+ *(rawp + offset++) = coloru.mV[2];
+ *(rawp + offset++) = coloru.mV[3];
+ }
+ else
+ {
+ // 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);
+
+ alpha = llmin(llround((F32)MAX_WATER_COLOR.mV[3]), alpha);
+ alpha = llmax(64, alpha);
+
+ coloru.mV[3] = alpha;
+ *(rawp + offset++) = coloru.mV[0];
+ *(rawp + offset++) = coloru.mV[1];
+ *(rawp + offset++) = coloru.mV[2];
+ *(rawp + offset++) = coloru.mV[3];
+ }
+ }
+ }
+
+ mWaterTexturep->setSubImage(raw, x_begin, y_begin, x_end - x_begin, y_end - y_begin);
+ return TRUE;
+}
diff --git a/indra/newview/llsurface.h b/indra/newview/llsurface.h
new file mode 100644
index 0000000000..7698c272ab
--- /dev/null
+++ b/indra/newview/llsurface.h
@@ -0,0 +1,249 @@
+/**
+ * @file llsurface.h
+ * @brief Description of LLSurface class
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSURFACE_H
+#define LL_LLSURFACE_H
+
+//#include "vmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4math.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+
+#include "v4coloru.h"
+#include "v4color.h"
+
+#include "llvowater.h"
+#include "llpatchvertexarray.h"
+#include "lldarray.h"
+#include "llviewerimage.h"
+
+class LLTimer;
+class LLUUID;
+class LLAgent;
+class LLStat;
+
+static const U8 NO_EDGE = 0x00;
+static const U8 EAST_EDGE = 0x01;
+static const U8 NORTH_EDGE = 0x02;
+static const U8 WEST_EDGE = 0x04;
+static const U8 SOUTH_EDGE = 0x08;
+
+static const S32 ONE_MORE_THAN_NEIGHBOR = 1;
+static const S32 EQUAL_TO_NEIGHBOR = 0;
+static const S32 ONE_LESS_THAN_NEIGHBOR = -1;
+
+const S32 ABOVE_WATERLINE_ALPHA = 32; // The alpha of water when the land elevation is above the waterline.
+
+class LLViewerRegion;
+class LLSurfacePatch;
+class LLBitPack;
+class LLGroupHeader;
+
+class LLSurface
+{
+public:
+ LLSurface(U32 type, LLViewerRegion *regionp = NULL);
+ virtual ~LLSurface();
+
+ static void initClasses(); // Do class initialization for LLSurface and its child classes.
+
+ void create(const S32 surface_grid_width,
+ const S32 surface_patch_width,
+ const LLVector3d &origin_global,
+ const F32 width); // Allocates and initializes surface
+
+ void setRegion(LLViewerRegion *regionp);
+
+ void setOriginGlobal(const LLVector3d &origin_global);
+
+ void connectNeighbor(LLSurface *neighborp, U32 direction);
+ void disconnectNeighbor(LLSurface *neighborp);
+ void disconnectAllNeighbors();
+
+ virtual void decompressDCTPatch(LLBitPack &bitpack, LLGroupHeader *gopp, BOOL b_large_patch);
+ virtual void updatePatchVisibilities(LLAgent &agent);
+
+ inline F32 getZ(const U32 k) const { return mSurfaceZ[k]; }
+ inline F32 getZ(const S32 i, const S32 j) const { return mSurfaceZ[i + j*mGridsPerEdge]; }
+
+ LLVector3 getOriginAgent() const;
+ const LLVector3d &getOriginGlobal() const;
+ F32 getMetersPerGrid() const;
+ S32 getGridsPerEdge() const;
+ S32 getPatchesPerEdge() const;
+ S32 getGridsPerPatchEdge() const;
+ U32 getRenderStride(const U32 render_level) const;
+ U32 getRenderLevel(const U32 render_stride) const;
+
+ // Returns the height of the surface immediately above (or below) location,
+ // or if location is not above surface returns zero.
+ F32 resolveHeightRegion(const F32 x, const F32 y) const;
+ F32 resolveHeightRegion(const LLVector3 &location) const
+ { return resolveHeightRegion( location.mV[VX], location.mV[VY] ); }
+ F32 resolveHeightGlobal(const LLVector3d &position_global) const;
+ LLVector3 resolveNormalGlobal(const LLVector3d& v) const; // Returns normal to surface
+
+ LLSurfacePatch *resolvePatchRegion(const F32 x, const F32 y) const;
+ LLSurfacePatch *resolvePatchRegion(const LLVector3 &position_region) const;
+ LLSurfacePatch *resolvePatchGlobal(const LLVector3d &position_global) const;
+
+ // Update methods (called during idle, normally)
+ BOOL idleUpdate();
+
+ void renderSurfaceBounds();
+
+ BOOL containsPosition(const LLVector3 &position);
+
+ void moveZ(const S32 x, const S32 y, const F32 delta);
+
+ LLViewerRegion *getRegion() const { return mRegionp; }
+
+ F32 getMinZ() const { return mMinZ; }
+ F32 getMaxZ() const { return mMaxZ; }
+
+ void setWaterHeight(F32 height);
+ F32 getWaterHeight() const;
+
+ LLViewerImage *getSTexture();
+ LLViewerImage *getWaterTexture();
+ BOOL hasZData() const { return mHasZData; }
+
+ void dirtyAllPatches(); // Use this to dirty all patches when changing terrain parameters
+
+ void dirtySurfacePatch(LLSurfacePatch *patchp);
+ LLVOWater *getWaterObj() { return mWaterObjp; }
+
+ static void setTextureSize(const S32 texture_size);
+
+ friend class LLSurfacePatch;
+ friend std::ostream& operator<<(std::ostream &s, const LLSurface &S);
+public:
+ // Number of grid points on one side of a region, including +1 buffer for
+ // north and east edge.
+ S32 mGridsPerEdge;
+
+ F32 mOOGridsPerEdge; // Inverse of grids per edge
+
+ S32 mPatchesPerEdge; // Number of patches on one side of a region
+ S32 mNumberOfPatches; // Total number of patches
+
+
+ // Each surface points at 8 neighbors (or NULL)
+ // +---+---+---+
+ // |NW | N | NE|
+ // +---+---+---+
+ // | W | 0 | E |
+ // +---+---+---+
+ // |SW | S | SE|
+ // +---+---+---+
+ LLSurface *mNeighbors[8]; // Adjacent patches
+
+ U32 mType; // Useful for identifying derived classes
+
+ F32 mDetailTextureScale; // Number of times to repeat detail texture across this surface
+
+ static F32 sTextureUpdateTime;
+ static S32 sTexelsUpdated;
+ static LLStat sTexelsUpdatedPerSecStat;
+
+protected:
+ void createSTexture();
+ void createWaterTexture();
+ void initTextures();
+ void initWater();
+
+
+ void createPatchData(); // Allocates memory for patches.
+ void destroyPatchData(); // Deallocates memory for patches.
+
+ BOOL generateWaterTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height); // Generate texture from composition values.
+
+ //F32 updateTexture(LLSurfacePatch *ppatch);
+
+ LLSurfacePatch *getPatch(const S32 x, const S32 y) const;
+
+protected:
+ LLVector3d mOriginGlobal; // In absolute frame
+ LLSurfacePatch *mPatchList; // Array of all patches
+
+ // Array of grid data, mGridsPerEdge * mGridsPerEdge
+ F32 *mSurfaceZ;
+
+ // Array of grid normals, mGridsPerEdge * mGridsPerEdge
+ LLVector3 *mNorm;
+
+ LLDynamicArray<LLSurfacePatch *> mDirtyPatchList;
+
+
+ // The textures should never be directly initialized - use the setter methods!
+ LLPointer<LLViewerImage> mSTexturep; // Texture for surface
+ LLPointer<LLViewerImage> mWaterTexturep; // Water texture
+
+ LLPointer<LLVOWater> mWaterObjp;
+
+ // When we want multiple cameras we'll need one of each these for each camera
+ S32 mVisiblePatchCount;
+
+ U32 mGridsPerPatchEdge; // Number of grid points on a side of a patch
+ F32 mMetersPerGrid; // Converts (i,j) indecies to distance
+ F32 mMetersPerEdge; // = mMetersPerGrid * (mGridsPerEdge-1)
+
+ F32 mSurfaceTexScale; // Scale factors for automatic tex coord generation
+ F32 mDetailTexScale;
+
+ LLPatchVertexArray mPVArray;
+
+ BOOL mHasZData; // We've received any patch data for this surface.
+ F32 mMinZ; // min z for this region (during the session)
+ F32 mMaxZ; // max z for this region (during the session)
+
+ S32 mSurfacePatchUpdateCount; // Number of frames since last update.
+
+private:
+ LLViewerRegion *mRegionp; // Patch whose coordinate system this surface is using.
+ static S32 sTextureSize; // Size of the surface texture
+};
+
+
+
+// . __.
+// Z /|\ /| Y North
+// | /
+// | / |<----------------- mGridsPerSurfaceEdge --------------->|
+// | / __________________________________________________________
+// |/______\ X /_______________________________________________________ /
+// / / / / / / / /M*M-2 /M*M-1 / /
+// /______/______/______/______/______/______/______/______/ /
+// / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// West / / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ / East
+// /... / / / / / / / / /
+// /______/______/______/______/______/______/______/______/ /
+// _. / 2M / / / / / / / / /
+// /| /______/______/______/______/______/______/______/______/ /
+// / / M / M+1 / M+2 / ... / / / / 2M-1 / /
+// j /______/______/______/______/______/______/______/______/ /
+// / 0 / 1 / 2 / ... / / / / M-1 / /
+// /______/______/______/______/______/______/______/______/_/
+// South |<-L->|
+// i -->
+//
+// where M = mSurfPatchWidth
+// and L = mPatchGridWidth
+//
+// Notice that mGridsPerSurfaceEdge = a power of two + 1
+// This provides a buffer on the east and north edges that will allow us to
+// fill the cracks between adjacent surfaces when rendering.
+#endif
diff --git a/indra/newview/llsurfacepatch.cpp b/indra/newview/llsurfacepatch.cpp
new file mode 100644
index 0000000000..ac0add8ae3
--- /dev/null
+++ b/indra/newview/llsurfacepatch.cpp
@@ -0,0 +1,974 @@
+/**
+ * @file llsurfacepatch.cpp
+ * @brief LLSurfacePatch class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsurfacepatch.h"
+#include "llpatchvertexarray.h"
+#include "llviewerobjectlist.h"
+#include "llvosurfacepatch.h"
+#include "llsurface.h"
+#include "pipeline.h"
+#include "llagent.h"
+#include "timing.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+
+// For getting composition values
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "lldrawpool.h"
+#include "noise.h"
+
+extern U64 gFrameTime;
+extern LLPipeline gPipeline;
+
+LLSurfacePatch::LLSurfacePatch() :
+ mDataZ(NULL),
+ mVObjp(NULL),
+ mLastUpdateTime(0),
+ mSurfacep(NULL)
+{
+ // This flag is used to communicate between adjacent surfaces and is set
+ // to non-zero values by higher classes.
+ mConnectedEdge = NO_EDGE;
+ mCenterRegion = LLVector3(0.f, 0.f, 0.f);
+ mOriginRegion = LLVector3(0.f, 0.f, 0.f);
+ mHasReceivedData = FALSE;
+ mMinZ = 0.0f;
+ mMaxZ = 0.0f;
+ mMeanZ = 0.0f;
+ mMinComposition = 0.f;
+ mMeanComposition = 0.f;
+ mMaxComposition = 0.f;
+ mRadius = 0.f;
+ mDirty = FALSE;
+ mDirtyZStats = TRUE;
+ mHeightsGenerated = FALSE;
+
+ S32 i;
+ for (i = 0; i < 8; i++)
+ {
+ setNeighborPatch(i, NULL);
+ }
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = TRUE;
+ }
+}
+
+
+LLSurfacePatch::~LLSurfacePatch()
+{
+ mVObjp = NULL;
+}
+
+
+void LLSurfacePatch::dirty()
+{
+ // These are outside of the loop in case we're still waiting for a dirty from the
+ // texture being updated...
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ }
+ else
+ {
+ llwarns << "No viewer object for this surface patch!" << llendl;
+ }
+
+ mDirtyZStats = TRUE;
+ mHeightsGenerated = FALSE;
+
+ if (!mDirty)
+ {
+ mDirty = TRUE;
+ mSurfacep->dirtySurfacePatch(this);
+ }
+}
+
+
+void LLSurfacePatch::setSurface(LLSurface *surfacep)
+{
+ mSurfacep = surfacep;
+ if (mVObjp == (LLVOSurfacePatch *)NULL)
+ {
+ llassert(mSurfacep->mType == 'l');
+
+ mVObjp = (LLVOSurfacePatch *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_SURFACE_PATCH, mSurfacep->getRegion());
+ mVObjp->setPatch(this);
+ mVObjp->setPositionRegion(mCenterRegion);
+ gPipeline.addObject(mVObjp);
+ }
+}
+
+void LLSurfacePatch::disconnectNeighbor(LLSurface *surfacep)
+{
+ U32 i;
+ for (i = 0; i < 8; i++)
+ {
+ if (getNeighborPatch(i))
+ {
+ if (getNeighborPatch(i)->mSurfacep == surfacep)
+ {
+ setNeighborPatch(i, NULL);
+ mNormalsInvalid[i] = TRUE;
+ }
+ }
+ }
+
+ // Clean up connected edges
+ if (getNeighborPatch(EAST))
+ {
+ if (getNeighborPatch(EAST)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~EAST_EDGE;
+ }
+ }
+ if (getNeighborPatch(NORTH))
+ {
+ if (getNeighborPatch(NORTH)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~NORTH_EDGE;
+ }
+ }
+ if (getNeighborPatch(WEST))
+ {
+ if (getNeighborPatch(WEST)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~WEST_EDGE;
+ }
+ }
+ if (getNeighborPatch(SOUTH))
+ {
+ if (getNeighborPatch(SOUTH)->mSurfacep == surfacep)
+ {
+ mConnectedEdge &= ~SOUTH_EDGE;
+ }
+ }
+}
+
+LLVector3 LLSurfacePatch::getPointAgent(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+ LLVector3 pos;
+ pos = getOriginAgent();
+ pos.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos.mV[VZ] = *(mDataZ + point_offset);
+ return pos;
+}
+
+LLVector2 LLSurfacePatch::getTexCoords(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+ LLVector3 pos, rel_pos;
+ pos = getOriginAgent();
+ pos.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos.mV[VZ] = *(mDataZ + point_offset);
+ rel_pos = pos - mSurfacep->getOriginAgent();
+ rel_pos *= 1.f/surface_stride;
+ return LLVector2(rel_pos.mV[VX], rel_pos.mV[VY]);
+}
+
+
+void LLSurfacePatch::eval(const U32 x, const U32 y, const U32 stride, LLVector3 *vertex, LLVector3 *normal,
+ LLVector2 *tex0, LLVector2 *tex1)
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ U32 point_offset = x + y*surface_stride;
+
+ *normal = getNormal(x, y);
+
+ LLVector3 pos_agent = getOriginAgent();
+ pos_agent.mV[VX] += x * mSurfacep->getMetersPerGrid();
+ pos_agent.mV[VY] += y * mSurfacep->getMetersPerGrid();
+ pos_agent.mV[VZ] = *(mDataZ + point_offset);
+ *vertex = pos_agent;
+
+ LLVector3 rel_pos = pos_agent - mSurfacep->getOriginAgent();
+ LLVector3 tex_pos = rel_pos * (1.f/surface_stride);
+ tex0->mV[0] = tex_pos.mV[0];
+ tex0->mV[1] = tex_pos.mV[1];
+ tex1->mV[0] = mSurfacep->getRegion()->getCompositionXY(llfloor(mOriginRegion.mV[0])+x, llfloor(mOriginRegion.mV[1])+y);
+
+ const F32 xyScale = 4.9215f*7.f; //0.93284f;
+ const F32 xyScaleInv = (1.f / xyScale)*(0.2222222222f);
+
+ F32 vec[3] = {
+ fmod((F32)(mOriginGlobal.mdV[0] + x)*xyScaleInv, 256.f),
+ fmod((F32)(mOriginGlobal.mdV[1] + y)*xyScaleInv, 256.f),
+ 0.f
+ };
+ F32 rand_val = llclamp(noise2(vec)* 0.75f + 0.5f, 0.f, 1.f);
+ tex1->mV[1] = rand_val;
+
+
+}
+
+
+void LLSurfacePatch::calcNormal(const U32 x, const U32 y, const U32 stride)
+{
+ U32 patch_width = mSurfacep->mPVArray.mPatchWidth;
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+
+ const F32 mpg = mSurfacep->getMetersPerGrid() * stride;
+
+ S32 poffsets[2][2][2];
+ poffsets[0][0][0] = x - stride;
+ poffsets[0][0][1] = y - stride;
+
+ poffsets[0][1][0] = x - stride;
+ poffsets[0][1][1] = y + stride;
+
+ poffsets[1][0][0] = x + stride;
+ poffsets[1][0][1] = y - stride;
+
+ poffsets[1][1][0] = x + stride;
+ poffsets[1][1][1] = y + stride;
+
+ const LLSurfacePatch *ppatches[2][2];
+
+ // LLVector3 p1, p2, p3, p4;
+
+ ppatches[0][0] = this;
+ ppatches[0][1] = this;
+ ppatches[1][0] = this;
+ ppatches[1][1] = this;
+
+ U32 i, j;
+ for (i = 0; i < 2; i++)
+ {
+ for (j = 0; j < 2; j++)
+ {
+ if (poffsets[i][j][0] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(WEST))
+ {
+ poffsets[i][j][0] = 0;
+ }
+ else
+ {
+ poffsets[i][j][0] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(WEST);
+ }
+ }
+ if (poffsets[i][j][1] < 0)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(SOUTH))
+ {
+ poffsets[i][j][1] = 0;
+ }
+ else
+ {
+ poffsets[i][j][1] += patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(SOUTH);
+ }
+ }
+ if (poffsets[i][j][0] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(EAST))
+ {
+ poffsets[i][j][0] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][0] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(EAST);
+ }
+ }
+ if (poffsets[i][j][1] >= (S32)patch_width)
+ {
+ if (!ppatches[i][j]->getNeighborPatch(NORTH))
+ {
+ poffsets[i][j][1] = patch_width - 1;
+ }
+ else
+ {
+ poffsets[i][j][1] -= patch_width;
+ ppatches[i][j] = ppatches[i][j]->getNeighborPatch(NORTH);
+ }
+ }
+ }
+ }
+
+ LLVector3 p00(-mpg,-mpg,
+ *(ppatches[0][0]->mDataZ
+ + poffsets[0][0][0]
+ + poffsets[0][0][1]*surface_stride));
+ LLVector3 p01(-mpg,+mpg,
+ *(ppatches[0][1]->mDataZ
+ + poffsets[0][1][0]
+ + poffsets[0][1][1]*surface_stride));
+ LLVector3 p10(+mpg,-mpg,
+ *(ppatches[1][0]->mDataZ
+ + poffsets[1][0][0]
+ + poffsets[1][0][1]*surface_stride));
+ LLVector3 p11(+mpg,+mpg,
+ *(ppatches[1][1]->mDataZ
+ + poffsets[1][1][0]
+ + poffsets[1][1][1]*surface_stride));
+
+ LLVector3 c1 = p11 - p00;
+ LLVector3 c2 = p01 - p10;
+
+ LLVector3 normal = c1;
+ normal %= c2;
+ normal.normVec();
+
+ *(mDataNorm + surface_stride * y + x) = normal;
+}
+
+const LLVector3 &LLSurfacePatch::getNormal(const U32 x, const U32 y) const
+{
+ U32 surface_stride = mSurfacep->getGridsPerEdge();
+ return *(mDataNorm + surface_stride * y + x);
+}
+
+
+void LLSurfacePatch::updateCameraDistanceRegion(const LLVector3 &pos_region)
+{
+ LLVector3 dv = pos_region;
+ dv -= mCenterRegion;
+ mVisInfo.mDistance = llmax(0.f, (F32)(dv.magVec() - mRadius));
+}
+
+F32 LLSurfacePatch::getDistance() const
+{
+ return mVisInfo.mDistance;
+}
+
+
+// Called when a patch has changed its height field
+// data.
+void LLSurfacePatch::updateVerticalStats()
+{
+ if (!mDirtyZStats)
+ {
+ return;
+ }
+
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+ F32 meters_per_grid = mSurfacep->getMetersPerGrid();
+
+ U32 i, j, k;
+ F32 z, total;
+
+ z = *(mDataZ);
+
+ mMinZ = z;
+ mMaxZ = z;
+
+ k = 0;
+ total = 0.0f;
+
+ // Iterate to +1 because we need to do the edges correctly.
+ for (j=0; j<(grids_per_patch_edge+1); j++)
+ {
+ for (i=0; i<(grids_per_patch_edge+1); i++)
+ {
+ z = *(mDataZ + i + j*grids_per_edge);
+
+ if (z < mMinZ)
+ {
+ mMinZ = z;
+ }
+ if (z > mMaxZ)
+ {
+ mMaxZ = z;
+ }
+ total += z;
+ k++;
+ }
+ }
+ mMeanZ = total / (F32) k;
+ mCenterRegion.mV[VZ] = 0.5f * (mMinZ + mMaxZ);
+
+ LLVector3 diam_vec(meters_per_grid*grids_per_patch_edge,
+ meters_per_grid*grids_per_patch_edge,
+ mMaxZ - mMinZ);
+ mRadius = diam_vec.magVec() * 0.5f;
+
+ mSurfacep->mMaxZ = llmax(mMaxZ, mSurfacep->mMaxZ);
+ mSurfacep->mMinZ = llmin(mMinZ, mSurfacep->mMinZ);
+ mSurfacep->mHasZData = TRUE;
+ mSurfacep->getRegion()->calculateCenterGlobal();
+
+ if (mVObjp)
+ {
+ mVObjp->dirtyPatch();
+ }
+ mDirtyZStats = FALSE;
+}
+
+
+void LLSurfacePatch::updateNormals()
+{
+ if (mSurfacep->mType == 'w')
+ {
+ return;
+ }
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ BOOL dirty_patch = FALSE;
+
+ U32 i, j;
+ // update the east edge
+ if (mNormalsInvalid[EAST] || mNormalsInvalid[NORTHEAST] || mNormalsInvalid[SOUTHEAST])
+ {
+ for (j = 0; j <= grids_per_patch_edge; j++)
+ {
+ calcNormal(grids_per_patch_edge, j, 2);
+ calcNormal(grids_per_patch_edge - 1, j, 2);
+ calcNormal(grids_per_patch_edge - 2, j, 2);
+ }
+
+ dirty_patch = TRUE;
+ }
+
+ // update the north edge
+ if (mNormalsInvalid[NORTHEAST] || mNormalsInvalid[NORTH] || mNormalsInvalid[NORTHWEST])
+ {
+ for (i = 0; i <= grids_per_patch_edge; i++)
+ {
+ calcNormal(i, grids_per_patch_edge, 2);
+ calcNormal(i, grids_per_patch_edge - 1, 2);
+ calcNormal(i, grids_per_patch_edge - 2, 2);
+ }
+
+ dirty_patch = TRUE;
+ }
+
+ // update the west edge
+ if (mNormalsInvalid[NORTHWEST] || mNormalsInvalid[WEST] || mNormalsInvalid[SOUTHWEST])
+ {
+ for (j = 0; j < grids_per_patch_edge; j++)
+ {
+ calcNormal(0, j, 2);
+ calcNormal(1, j, 2);
+ }
+ dirty_patch = TRUE;
+ }
+
+ // update the south edge
+ if (mNormalsInvalid[SOUTHWEST] || mNormalsInvalid[SOUTH] || mNormalsInvalid[SOUTHEAST])
+ {
+ for (i = 0; i < grids_per_patch_edge; i++)
+ {
+ calcNormal(i, 0, 2);
+ calcNormal(i, 1, 2);
+ }
+ dirty_patch = TRUE;
+ }
+
+ // Invalidating the northeast corner is different, because depending on what the adjacent neighbors are,
+ // we'll want to do different things.
+ if (mNormalsInvalid[NORTHEAST])
+ {
+ if (!getNeighborPatch(NORTHEAST))
+ {
+ if (!getNeighborPatch(NORTH))
+ {
+ if (!getNeighborPatch(EAST))
+ {
+ // No north or east neighbors. Pull from the diagonal in your own patch.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ if (getNeighborPatch(EAST)->getHasReceivedData())
+ {
+ // East, but not north. Pull from your east neighbor's northwest point.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(EAST)->mDataZ + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ }
+ }
+ else
+ {
+ // We have a north.
+ if (getNeighborPatch(EAST))
+ {
+ // North and east neighbors, but not northeast.
+ // Pull from diagonal in your own patch.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ else
+ {
+ if (getNeighborPatch(NORTH)->getHasReceivedData())
+ {
+ // North, but not east. Pull from your north neighbor's southeast corner.
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(NORTH)->mDataZ + (grids_per_patch_edge - 1));
+ }
+ else
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(mDataZ + grids_per_patch_edge - 1 + (grids_per_patch_edge - 1)*grids_per_edge);
+ }
+ }
+ }
+ }
+ else if (getNeighborPatch(NORTHEAST)->mSurfacep != mSurfacep)
+ {
+ if (
+ (!getNeighborPatch(NORTH) || (getNeighborPatch(NORTH)->mSurfacep != mSurfacep))
+ &&
+ (!getNeighborPatch(EAST) || (getNeighborPatch(EAST)->mSurfacep != mSurfacep)))
+ {
+ *(mDataZ + grids_per_patch_edge + grids_per_patch_edge*grids_per_edge) =
+ *(getNeighborPatch(NORTHEAST)->mDataZ);
+ }
+ }
+ else
+ {
+ // We've got a northeast patch in the same surface.
+ // The z and normals will be handled by that patch.
+ }
+ calcNormal(grids_per_patch_edge, grids_per_patch_edge, 2);
+ calcNormal(grids_per_patch_edge, grids_per_patch_edge - 1, 2);
+ calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge, 2);
+ calcNormal(grids_per_patch_edge - 1, grids_per_patch_edge - 1, 2);
+ dirty_patch = TRUE;
+ }
+
+ // update the middle normals
+ if (mNormalsInvalid[MIDDLE])
+ {
+ for (j=2; j < grids_per_patch_edge - 2; j++)
+ {
+ for (i=2; i < grids_per_patch_edge - 2; i++)
+ {
+ calcNormal(i, j, 2);
+ }
+ }
+ dirty_patch = TRUE;
+ }
+
+ if (dirty_patch)
+ {
+ mSurfacep->dirtySurfacePatch(this);
+ }
+
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = FALSE;
+ }
+}
+
+void LLSurfacePatch::updateEastEdge()
+{
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ U32 j, k;
+ F32 *west_surface, *east_surface;
+
+ if (!getNeighborPatch(EAST))
+ {
+ west_surface = mDataZ + grids_per_patch_edge;
+ east_surface = mDataZ + grids_per_patch_edge - 1;
+ }
+ else if (mConnectedEdge & EAST_EDGE)
+ {
+ west_surface = mDataZ + grids_per_patch_edge;
+ east_surface = getNeighborPatch(EAST)->mDataZ;
+ }
+ else
+ {
+ return;
+ }
+
+ // If patchp is on the east edge of its surface, then we update the east
+ // side buffer
+ for (j=0; j < grids_per_patch_edge; j++)
+ {
+ k = j * grids_per_edge;
+ *(west_surface + k) = *(east_surface + k); // update buffer Z
+ }
+}
+
+
+void LLSurfacePatch::updateNorthEdge()
+{
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+ U32 grids_per_edge = mSurfacep->getGridsPerEdge();
+
+ U32 i;
+ F32 *south_surface, *north_surface;
+
+ if (!getNeighborPatch(NORTH))
+ {
+ south_surface = mDataZ + grids_per_patch_edge*grids_per_edge;
+ north_surface = mDataZ + (grids_per_patch_edge - 1) * grids_per_edge;
+ }
+ else if (mConnectedEdge & NORTH_EDGE)
+ {
+ south_surface = mDataZ + grids_per_patch_edge*grids_per_edge;
+ north_surface = getNeighborPatch(NORTH)->mDataZ;
+ }
+ else
+ {
+ return;
+ }
+
+ // Update patchp's north edge ...
+ for (i=0; i<grids_per_patch_edge; i++)
+ {
+ *(south_surface + i) = *(north_surface + i); // update buffer Z
+ }
+}
+
+
+BOOL LLSurfacePatch::updateTexture()
+{
+ if (mSTexUpdate) // Update texture as needed
+ {
+ F32 meters_per_grid = getSurface()->getMetersPerGrid();
+ F32 grids_per_patch_edge = (F32)getSurface()->getGridsPerPatchEdge();
+
+ if ((!getNeighborPatch(EAST) || getNeighborPatch(EAST)->getHasReceivedData())
+ && (!getNeighborPatch(WEST) || getNeighborPatch(WEST)->getHasReceivedData())
+ && (!getNeighborPatch(SOUTH) || getNeighborPatch(SOUTH)->getHasReceivedData())
+ && (!getNeighborPatch(NORTH) || getNeighborPatch(NORTH)->getHasReceivedData()))
+ {
+ LLViewerRegion *regionp = getSurface()->getRegion();
+ LLVector3d origin_region = getOriginGlobal() - getSurface()->getOriginGlobal();
+
+ // Have to figure out a better way to deal with these edge conditions...
+ LLVLComposition* comp = regionp->getComposition();
+ if (!mHeightsGenerated)
+ {
+ F32 patch_size = meters_per_grid*(grids_per_patch_edge+1);
+ if (comp->generateHeights((F32)origin_region[VX], (F32)origin_region[VY],
+ patch_size, patch_size))
+ {
+ mHeightsGenerated = TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+
+ if (comp->generateComposition())
+ {
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ }
+ updateCompositionStats();
+ F32 tex_patch_size = meters_per_grid*grids_per_patch_edge;
+ if (comp->generateTexture((F32)origin_region[VX], (F32)origin_region[VY],
+ tex_patch_size, tex_patch_size))
+ {
+ mSTexUpdate = FALSE;
+
+ // Also generate the water texture
+ mSurfacep->generateWaterTexture((F32)origin_region.mdV[VX], (F32)origin_region.mdV[VY],
+ tex_patch_size, tex_patch_size);
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+
+void LLSurfacePatch::dirtyZ()
+{
+ mSTexUpdate = TRUE;
+
+ // Invalidate all normals in this patch
+ U32 i;
+ for (i = 0; i < 9; i++)
+ {
+ mNormalsInvalid[i] = TRUE;
+ }
+
+ // Invalidate normals in this and neighboring patches
+ for (i = 0; i < 8; i++)
+ {
+ if (getNeighborPatch(i))
+ {
+ getNeighborPatch(i)->mNormalsInvalid[gDirOpposite[i]] = TRUE;
+ getNeighborPatch(i)->dirty();
+ if (i < 4)
+ {
+ getNeighborPatch(i)->mNormalsInvalid[gDirAdjacent[gDirOpposite[i]][0]] = TRUE;
+ getNeighborPatch(i)->mNormalsInvalid[gDirAdjacent[gDirOpposite[i]][1]] = TRUE;
+ }
+ }
+ }
+
+ dirty();
+ mLastUpdateTime = gFrameTime;
+}
+
+
+const U64 &LLSurfacePatch::getLastUpdateTime() const
+{
+ return mLastUpdateTime;
+}
+
+F32 LLSurfacePatch::getMaxZ() const
+{
+ return mMaxZ;
+}
+
+F32 LLSurfacePatch::getMinZ() const
+{
+ return mMinZ;
+}
+
+void LLSurfacePatch::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+
+ LLVector3 origin_region;
+ origin_region.setVec(mOriginGlobal - mSurfacep->getOriginGlobal());
+
+ mOriginRegion = origin_region;
+ mCenterRegion.mV[VX] = origin_region.mV[VX] + 0.5f*mSurfacep->getGridsPerPatchEdge()*mSurfacep->getMetersPerGrid();
+ mCenterRegion.mV[VY] = origin_region.mV[VY] + 0.5f*mSurfacep->getGridsPerPatchEdge()*mSurfacep->getMetersPerGrid();
+
+ mVisInfo.mbIsVisible = FALSE;
+ mVisInfo.mDistance = 512.0f;
+ mVisInfo.mRenderLevel = 0;
+ mVisInfo.mRenderStride = mSurfacep->getGridsPerPatchEdge();
+
+}
+
+void LLSurfacePatch::connectNeighbor(LLSurfacePatch *neighbor_patchp, const U32 direction)
+{
+ llassert(neighbor_patchp);
+ mNormalsInvalid[direction] = TRUE;
+ neighbor_patchp->mNormalsInvalid[gDirOpposite[direction]] = TRUE;
+
+ setNeighborPatch(direction, neighbor_patchp);
+ neighbor_patchp->setNeighborPatch(gDirOpposite[direction], this);
+
+ if (EAST == direction)
+ {
+ mConnectedEdge |= EAST_EDGE;
+ neighbor_patchp->mConnectedEdge |= WEST_EDGE;
+ }
+ else if (NORTH == direction)
+ {
+ mConnectedEdge |= NORTH_EDGE;
+ neighbor_patchp->mConnectedEdge |= SOUTH_EDGE;
+ }
+ else if (WEST == direction)
+ {
+ mConnectedEdge |= WEST_EDGE;
+ neighbor_patchp->mConnectedEdge |= EAST_EDGE;
+ }
+ else if (SOUTH == direction)
+ {
+ mConnectedEdge |= SOUTH_EDGE;
+ neighbor_patchp->mConnectedEdge |= NORTH_EDGE;
+ }
+}
+
+void LLSurfacePatch::updateVisibility()
+{
+ if (mVObjp == (LLVOSurfacePatch*)NULL)
+ {
+ return;
+ }
+
+ const F32 DEFAULT_DELTA_ANGLE = (0.15f);
+ U32 old_render_stride, max_render_stride;
+ U32 new_render_level;
+ F32 stride_per_distance = DEFAULT_DELTA_ANGLE / mSurfacep->getMetersPerGrid();
+ U32 grids_per_patch_edge = mSurfacep->getGridsPerPatchEdge();
+
+ // sphere in frustum on global coordinates
+ if (gCamera->sphereInFrustum(mCenterRegion + mSurfacep->getOriginAgent(), mRadius) )
+ {
+ // We now need to calculate the render stride based on patchp's distance
+ // from LLCamera render_stride is governed by a relation something like this...
+ //
+ // delta_angle * patch.distance
+ // render_stride <= ----------------------------------------
+ // mMetersPerGrid
+ //
+ // where 'delta_angle' is the desired solid angle of the average polgon on a patch.
+ //
+ // Any render_stride smaller than the RHS would be 'satisfactory'. Smaller
+ // strides give more resolution, but efficiency suggests that we use the largest
+ // of the render_strides that obey the relation. Flexibility is achieved by
+ // modulating 'delta_angle' until we have an acceptable number of triangles.
+
+ old_render_stride = mVisInfo.mRenderStride;
+
+ // Calculate the render_stride using information in agent
+ max_render_stride = lltrunc(mVisInfo.mDistance * stride_per_distance);
+ max_render_stride = llmin(max_render_stride , 2*grids_per_patch_edge);
+
+ // We only use render_strides that are powers of two, so we use look-up tables to figure out
+ // the render_level and corresponding render_stride
+ new_render_level = mVisInfo.mRenderLevel = mSurfacep->getRenderLevel(max_render_stride);
+ mVisInfo.mRenderStride = mSurfacep->getRenderStride(new_render_level);
+
+ if ((mVisInfo.mRenderStride != old_render_stride))
+ // The reason we check !mbIsVisible is because non-visible patches normals
+ // are not updated when their data is changed. When this changes we can get
+ // rid of mbIsVisible altogether.
+ {
+ if (mVObjp)
+ {
+ mVObjp->dirtyGeom();
+ if (getNeighborPatch(WEST))
+ {
+ getNeighborPatch(WEST)->mVObjp->dirtyGeom();
+ }
+ if (getNeighborPatch(SOUTH))
+ {
+ getNeighborPatch(SOUTH)->mVObjp->dirtyGeom();
+ }
+ }
+ }
+ mVisInfo.mbIsVisible = TRUE;
+ }
+ else
+ {
+ mVisInfo.mbIsVisible = FALSE;
+ }
+}
+
+
+const LLVector3d &LLSurfacePatch::getOriginGlobal() const
+{
+ return mOriginGlobal;
+}
+
+LLVector3 LLSurfacePatch::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+BOOL LLSurfacePatch::getVisible() const
+{
+ return mVisInfo.mbIsVisible;
+}
+
+U32 LLSurfacePatch::getRenderStride() const
+{
+ return mVisInfo.mRenderStride;
+}
+
+S32 LLSurfacePatch::getRenderLevel() const
+{
+ return mVisInfo.mRenderLevel;
+}
+
+void LLSurfacePatch::setHasReceivedData()
+{
+ mHasReceivedData = TRUE;
+}
+
+BOOL LLSurfacePatch::getHasReceivedData() const
+{
+ return mHasReceivedData;
+}
+
+const LLVector3 &LLSurfacePatch::getCenterRegion() const
+{
+ return mCenterRegion;
+}
+
+
+void LLSurfacePatch::updateCompositionStats()
+{
+ LLViewerLayer *vlp = mSurfacep->getRegion()->getComposition();
+
+ F32 x, y, width, height, mpg, min, mean, max;
+
+ LLVector3 origin = getOriginAgent() - mSurfacep->getOriginAgent();
+ mpg = mSurfacep->getMetersPerGrid();
+ x = origin.mV[VX];
+ y = origin.mV[VY];
+ width = mpg*(mSurfacep->getGridsPerPatchEdge()+1);
+ height = mpg*(mSurfacep->getGridsPerPatchEdge()+1);
+
+ mean = 0.f;
+ min = vlp->getValueScaled(x, y);
+ max= min;
+ U32 count = 0;
+ F32 i, j;
+ for (j = 0; j < height; j += mpg)
+ {
+ for (i = 0; i < width; i += mpg)
+ {
+ F32 comp = vlp->getValueScaled(x + i, y + j);
+ mean += comp;
+ min = llmin(min, comp);
+ max = llmax(max, comp);
+ count++;
+ }
+ }
+ mean /= count;
+
+ mMinComposition = min;
+ mMeanComposition = mean;
+ mMaxComposition = max;
+}
+
+F32 LLSurfacePatch::getMeanComposition() const
+{
+ return mMeanComposition;
+}
+
+F32 LLSurfacePatch::getMinComposition() const
+{
+ return mMinComposition;
+}
+
+F32 LLSurfacePatch::getMaxComposition() const
+{
+ return mMaxComposition;
+}
+
+void LLSurfacePatch::setNeighborPatch(const U32 direction, LLSurfacePatch *neighborp)
+{
+ mNeighborPatches[direction] = neighborp;
+ mNormalsInvalid[direction] = TRUE;
+ if (direction < 4)
+ {
+ mNormalsInvalid[gDirAdjacent[direction][0]] = TRUE;
+ mNormalsInvalid[gDirAdjacent[direction][1]] = TRUE;
+ }
+}
+
+LLSurfacePatch *LLSurfacePatch::getNeighborPatch(const U32 direction) const
+{
+ return mNeighborPatches[direction];
+}
+
+void LLSurfacePatch::clearVObj()
+{
+ mVObjp = NULL;
+}
diff --git a/indra/newview/llsurfacepatch.h b/indra/newview/llsurfacepatch.h
new file mode 100644
index 0000000000..7835b3f598
--- /dev/null
+++ b/indra/newview/llsurfacepatch.h
@@ -0,0 +1,158 @@
+/**
+ * @file llsurfacepatch.h
+ * @brief LLSurfacePatch class definition
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSURFACEPATCH_H
+#define LL_LLSURFACEPATCH_H
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llmemory.h"
+
+class LLSurface;
+class LLVOSurfacePatch;
+class LLVector2;
+class LLColor4U;
+class LLAgent;
+
+// A patch shouldn't know about its visibility since that really depends on the
+// camera that is looking (or not looking) at it. So, anything about a patch
+// that is specific to a camera should be in the struct below.
+struct LLPatchVisibilityInfo
+{
+ BOOL mbIsVisible;
+ F32 mDistance; // Distance from camera
+ S32 mRenderLevel;
+ U32 mRenderStride;
+};
+
+
+
+class LLSurfacePatch
+{
+public:
+ LLSurfacePatch();
+ ~LLSurfacePatch();
+
+ void reset(const U32 id);
+ void connectNeighbor(LLSurfacePatch *neighborp, const U32 direction);
+ void disconnectNeighbor(LLSurface *surfacep);
+
+ void setNeighborPatch(const U32 direction, LLSurfacePatch *neighborp);
+ LLSurfacePatch *getNeighborPatch(const U32 direction) const;
+
+ void colorPatch(const U8 r, const U8 g, const U8 b);
+
+ BOOL updateTexture();
+
+ void updateVerticalStats();
+ void updateCompositionStats();
+ void updateNormals();
+
+ void updateEastEdge();
+ void updateNorthEdge();
+
+ void updateCameraDistanceRegion( const LLVector3 &pos_region);
+ void updateVisibility();
+
+ void dirtyZ(); // Dirty the z values of this patch
+ void setHasReceivedData();
+ BOOL getHasReceivedData() const;
+
+ F32 getDistance() const;
+ F32 getMaxZ() const;
+ F32 getMinZ() const;
+ F32 getMeanComposition() const;
+ F32 getMinComposition() const;
+ F32 getMaxComposition() const;
+ const LLVector3 &getCenterRegion() const;
+ const U64 &getLastUpdateTime() const;
+ LLSurface *getSurface() const { return mSurfacep; }
+ LLVector3 getPointAgent(const U32 x, const U32 y) const; // get the point at the offset.
+ LLVector2 getTexCoords(const U32 x, const U32 y) const;
+
+ void calcNormal(const U32 x, const U32 y, const U32 stride);
+ const LLVector3 &getNormal(const U32 x, const U32 y) const;
+
+ void eval(const U32 x, const U32 y, const U32 stride,
+ LLVector3 *vertex, LLVector3 *normal, LLVector2 *tex0, LLVector2 *tex1);
+
+
+
+ LLVector3 getOriginAgent() const;
+ const LLVector3d &getOriginGlobal() const;
+ void setOriginGlobal(const LLVector3d &origin_global);
+
+ // connectivity -- each LLPatch points at 5 neighbors (or NULL)
+ // +---+---+---+
+ // | | 2 | 5 |
+ // +---+---+---+
+ // | 3 | 0 | 1 |
+ // +---+---+---+
+ // | 6 | 4 | |
+ // +---+---+---+
+
+
+ BOOL getVisible() const;
+ U32 getRenderStride() const;
+ S32 getRenderLevel() const;
+
+ void setSurface(LLSurface *surfacep);
+ void setDataZ(F32 *data_z) { mDataZ = data_z; }
+ void setDataNorm(LLVector3 *data_norm) { mDataNorm = data_norm; }
+ F32 *getDataZ() const { return mDataZ; }
+
+ void dirty(); // Mark this surface patch as dirty...
+ void clearDirty() { mDirty = FALSE; }
+
+ void clearVObj();
+
+public:
+ BOOL mHasReceivedData; // has the patch EVER received height data?
+ BOOL mSTexUpdate; // Does the surface texture need to be updated?
+
+protected:
+ LLSurfacePatch *mNeighborPatches[8]; // Adjacent patches
+ BOOL mNormalsInvalid[9]; // Which normals are invalid
+
+ BOOL mDirty;
+ BOOL mDirtyZStats;
+ BOOL mHeightsGenerated;
+
+ U32 mDataOffset;
+ F32 *mDataZ;
+ LLVector3 *mDataNorm;
+
+ // Pointer to the LLVOSurfacePatch object which is used in the new renderer.
+ LLPointer<LLVOSurfacePatch> mVObjp;
+
+ // All of the camera-dependent stuff should be in its own structure...
+ LLPatchVisibilityInfo mVisInfo;
+
+ // pointers to beginnings of patch data fields
+ LLVector3d mOriginGlobal;
+ LLVector3 mOriginRegion;
+
+
+ // height field stats
+ LLVector3 mCenterRegion; // Center in region-local coords
+ F32 mMinZ, mMaxZ, mMeanZ;
+ F32 mRadius;
+
+ F32 mMinComposition;
+ F32 mMaxComposition;
+ F32 mMeanComposition;
+
+ U8 mConnectedEdge; // This flag is non-zero iff patch is on at least one edge
+ // of LLSurface that is "connected" to another LLSurface
+ U64 mLastUpdateTime; // Time patch was last updated
+
+ LLSurface *mSurfacep; // Pointer to "parent" surface
+};
+
+
+#endif // LL_LLSURFACEPATCH_H
diff --git a/indra/newview/lltable.h b/indra/newview/lltable.h
new file mode 100644
index 0000000000..9f3b536483
--- /dev/null
+++ b/indra/newview/lltable.h
@@ -0,0 +1,48 @@
+/**
+ * @file lltable.h
+ * @brief Description of LLTable template class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTABLE_H
+#define LL_LLTABLE_H
+
+template<class T> class LLTable
+{
+private:
+ T *_tab;
+ U32 _w;
+ U32 _h;
+ U32 _size;
+public:
+ LLTable(U32 w, U32 h = 0) : _tab(0), _w(w), _h(h)
+ {
+ if (_w < 0) _w = 0;
+ if (_h < 0) _h = 0;
+ if (0 == h)
+ _h = _w;
+ _size = _w * _h;
+ if ((_w > 0) && (_h > 0))
+ _tab = new T[_size];
+ }
+
+ ~LLTable()
+ {
+ delete[] _tab;
+ _tab = NULL;
+ }
+
+ void init(const T& t)
+ {
+ for (U32 i = 0; i < _size; ++i)
+ _tab[i] = t;
+ }
+ const T& at(U32 w, U32 h) const { return _tab[h * _w + w]; }
+ T& at(U32 w, U32 h) { return _tab[h * _w + w]; }
+ U32 size() const { return _size; }
+ U32 w() const { return _w; }
+ U32 h() const { return _h; }
+};
+#endif // LL_LLTABLE_H
diff --git a/indra/newview/lltexlayer.cpp b/indra/newview/lltexlayer.cpp
new file mode 100644
index 0000000000..da468d4ab1
--- /dev/null
+++ b/indra/newview/lltexlayer.cpp
@@ -0,0 +1,2839 @@
+/**
+ * @file lltexlayer.cpp
+ * @brief A texture layer. Used for avatars.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "imageids.h"
+#include "llagent.h"
+#include "llcrc.h"
+#include "lldir.h"
+#include "llglheaders.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llpolymorph.h"
+#include "llquantize.h"
+#include "lltexlayer.h"
+#include "llui.h"
+#include "llvfile.h"
+#include "llviewerimagelist.h"
+#include "llviewerimagelist.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llxmltree.h"
+#include "pipeline.h"
+#include "v4coloru.h"
+#include "viewer.h"
+
+//#include "../tools/imdebug/imdebug.h"
+
+
+// SJB: We really always want to use the GL cache;
+// let GL page textures in and out of video RAM instead of trying to do so by hand.
+// const U32 USE_AVATAR_GL_CACHE_THRESHOLD = 1024 * 1024 * 35; // 35 MB
+BOOL gUseAvatarGLCache = TRUE; //FALSE;
+
+LLGradientPaletteList gGradientPaletteList;
+
+// static
+S32 LLTexLayerSetBuffer::sGLByteCount = 0;
+S32 LLTexLayerSetBuffer::sGLBumpByteCount = 0;
+
+//-----------------------------------------------------------------------------
+// LLBakedUploadData()
+//-----------------------------------------------------------------------------
+LLBakedUploadData::LLBakedUploadData( LLVOAvatar* avatar, LLTexLayerSetBuffer* layerset_buffer ) :
+ mAvatar( avatar ),
+ mLayerSetBuffer( layerset_buffer )
+{
+ mID.generate();
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ LLWearable* wearable = gAgent.getWearable( (EWearableType)i);
+ if( wearable )
+ {
+ mWearableAssets[i] = wearable->getID();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one.
+//-----------------------------------------------------------------------------
+LLTexLayerSetBuffer::LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump )
+ :
+ // ORDER_LAST => must render these after the hints are created.
+ LLDynamicTexture( width, height, 4, LLDynamicTexture::ORDER_LAST, TRUE ),
+ mNeedsUpdate( TRUE ),
+ mNeedsUpload( FALSE ),
+ mUploadPending( FALSE ), // Not used for any logic here, just to sync sending of updates
+ mTexLayerSet( owner ),
+ mInitialized( FALSE ),
+ mBumpTexName(0)
+{
+ LLTexLayerSetBuffer::sGLByteCount += getSize();
+
+ if( has_bump )
+ {
+ LLGLSUIDefault gls_ui;
+ glGenTextures(1, (GLuint*) &mBumpTexName);
+
+ LLImageGL::bindExternalTexture(mBumpTexName, 0, GL_TEXTURE_2D);
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ stop_glerror();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ LLImageGL::sGlobalTextureMemory += mWidth * mHeight * 4;
+ LLTexLayerSetBuffer::sGLBumpByteCount += mWidth * mHeight * 4;
+ }
+}
+
+LLTexLayerSetBuffer::~LLTexLayerSetBuffer()
+{
+ LLTexLayerSetBuffer::sGLByteCount -= getSize();
+
+ if( mBumpTexName )
+ {
+ glDeleteTextures(1, (GLuint*) &mBumpTexName);
+ stop_glerror();
+ mBumpTexName = 0;
+
+ LLImageGL::sGlobalTextureMemory -= mWidth * mHeight * 4;
+ LLTexLayerSetBuffer::sGLBumpByteCount -= mWidth * mHeight * 4;
+ }
+}
+
+// static
+void LLTexLayerSetBuffer::dumpTotalByteCount()
+{
+ llinfos << "Composite System GL Buffers: " << (LLTexLayerSetBuffer::sGLByteCount/1024) << "KB" << llendl;
+ llinfos << "Composite System GL Bump Buffers: " << (LLTexLayerSetBuffer::sGLBumpByteCount/1024) << "KB" << llendl;
+}
+
+void LLTexLayerSetBuffer::requestUpdate()
+{
+ mNeedsUpdate = TRUE;
+
+ // If we're in the middle of uploading a baked texture, we don't care about it any more.
+ // When it's downloaded, ignore it.
+ mUploadID.setNull();
+}
+
+void LLTexLayerSetBuffer::requestUpload()
+{
+ if (!mNeedsUpload)
+ {
+ mNeedsUpload = TRUE;
+ mUploadPending = TRUE;
+ }
+}
+
+void LLTexLayerSetBuffer::cancelUpload()
+{
+ if (mNeedsUpload)
+ {
+ mNeedsUpload = FALSE;
+ }
+ mUploadPending = FALSE;
+}
+
+void LLTexLayerSetBuffer::pushProjection()
+{
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+}
+
+void LLTexLayerSetBuffer::popProjection()
+{
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+BOOL LLTexLayerSetBuffer::needsRender()
+{
+ LLVOAvatar* avatar = mTexLayerSet->getAvatar();
+ BOOL upload_now = mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal();
+ BOOL needs_update = gAgent.mNumPendingQueries == 0 && (mNeedsUpdate || upload_now) && !avatar->mAppearanceAnimating;
+ if (needs_update)
+ {
+ BOOL invalid_skirt = avatar->getBakedTE(mTexLayerSet) == LLVOAvatar::TEX_SKIRT_BAKED && !avatar->isWearingWearableType(WT_SKIRT);
+ if (invalid_skirt)
+ {
+ // we were trying to create a skirt texture
+ // but we're no longer wearing a skirt...
+ needs_update = FALSE;
+ cancelUpload();
+ }
+ else
+ {
+ needs_update &= (avatar->isSelf() || (avatar->isVisible() && !avatar->isCulled()));
+ needs_update &= mTexLayerSet->isLocalTextureDataAvailable();
+ }
+ }
+ return needs_update;
+}
+
+void LLTexLayerSetBuffer::preRender(BOOL clear_depth)
+{
+ // Set up an ortho projection
+ pushProjection();
+
+ // keep depth buffer, we don't need to clear it
+ LLDynamicTexture::preRender(FALSE);
+}
+
+void LLTexLayerSetBuffer::postRender(BOOL success)
+{
+ popProjection();
+
+ LLDynamicTexture::postRender(success);
+}
+
+BOOL LLTexLayerSetBuffer::render()
+{
+ U8* baked_bump_data = NULL;
+
+// gUseAvatarGLCache = ( gImageList.getMaxResidentTexMem() > USE_AVATAR_GL_CACHE_THRESHOLD );
+
+ // do we need to upload, and do we have sufficient data to create an uploadable composite?
+ // When do we upload the texture if gAgent.mNumPendingQueries is non-zero?
+ BOOL upload_now = (gAgent.mNumPendingQueries == 0 && mNeedsUpload && mTexLayerSet->isLocalTextureDataFinal());
+ BOOL success = TRUE;
+
+ // Composite bump
+ if( mBumpTexName )
+ {
+ // Composite the bump data
+ success &= mTexLayerSet->renderBump( mOrigin.mX, mOrigin.mY, mWidth, mHeight );
+ stop_glerror();
+
+ if (success)
+ {
+ LLGLSUIDefault gls_ui;
+
+ // read back into texture (this is done externally for the color data)
+ LLImageGL::bindExternalTexture( mBumpTexName, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, mOrigin.mX, mOrigin.mY, mWidth, mHeight);
+ stop_glerror();
+
+ // if we need to upload the data, read it back into a buffer
+ if( upload_now )
+ {
+ baked_bump_data = new U8[ mWidth * mHeight * 4 ];
+ glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_bump_data );
+ stop_glerror();
+ }
+ }
+ }
+
+ // Composite the color data
+ LLGLSUIDefault gls_ui;
+ success &= mTexLayerSet->render( mOrigin.mX, mOrigin.mY, mWidth, mHeight );
+
+ if( upload_now )
+ {
+ if (!success)
+ {
+ delete baked_bump_data;
+ llinfos << "Failed attempt to bake " << mTexLayerSet->getBodyRegion() << llendl;
+ mUploadPending = FALSE;
+ }
+ else
+ {
+ readBackAndUpload(baked_bump_data);
+ }
+ }
+
+ // reset GL state
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+
+ // we have valid texture data now
+ mInitialized = TRUE;
+ mNeedsUpdate = FALSE;
+
+ return success;
+}
+
+BOOL LLTexLayerSetBuffer::updateImmediate()
+{
+ mNeedsUpdate = TRUE;
+ BOOL result = FALSE;
+
+ if (needsRender())
+ {
+ preRender(FALSE);
+ result = render();
+ postRender(result);
+ }
+
+ return result;
+}
+
+void LLTexLayerSetBuffer::readBackAndUpload(U8* baked_bump_data)
+{
+ // pointers for storing data to upload
+ U8* baked_color_data = new U8[ mWidth * mHeight * 4 ];
+
+ glReadPixels(mOrigin.mX, mOrigin.mY, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, baked_color_data );
+ stop_glerror();
+
+ llinfos << "Baked " << mTexLayerSet->getBodyRegion() << llendl;
+ gViewerStats->incStat(LLViewerStats::ST_TEX_BAKES);
+
+ llassert( gAgent.getAvatarObject() == mTexLayerSet->getAvatar() );
+
+ // We won't need our caches since we're baked now. (Techically, we won't
+ // really be baked until this image is sent to the server and the Avatar
+ // Appearance message is received.)
+ mTexLayerSet->deleteCaches();
+
+ LLGLSUIDefault gls_ui;
+
+ LLPointer<LLImageRaw> baked_mask_image = new LLImageRaw(mWidth, mHeight, 1 );
+ U8* baked_mask_data = baked_mask_image->getData();
+
+ mTexLayerSet->gatherAlphaMasks(baked_mask_data, mWidth, mHeight);
+// imdebug("lum b=8 w=%d h=%d %p", mWidth, mHeight, baked_mask_data);
+
+
+ // writes into baked_color_data
+ const char* comment_text = NULL;
+
+ S32 baked_image_components = mBumpTexName ? 5 : 4; // red green blue [bump] clothing
+ LLPointer<LLImageRaw> baked_image = new LLImageRaw( mWidth, mHeight, baked_image_components );
+ U8* baked_image_data = baked_image->getData();
+
+ if( mBumpTexName )
+ {
+ comment_text = LINDEN_J2C_COMMENT_PREFIX "RGBHM"; // 5 channels: rgb, heightfield/alpha, mask
+
+ // Hide the alpha for the eyelashes in a corner of the bump map
+ if (mTexLayerSet->getBodyRegion() == "head")
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[5*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[5*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[5*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[5*i + 3] = baked_color_data[4*i + 3] < 255 ? baked_color_data[4*i + 3] : baked_bump_data[4*i];
+ baked_image_data[5*i + 4] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ else
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[5*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[5*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[5*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[5*i + 3] = baked_bump_data[4*i];
+ baked_image_data[5*i + 4] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (mTexLayerSet->getBodyRegion() == "skirt")
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[4*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[4*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[4*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[4*i + 3] = baked_color_data[4*i + 3]; // Use alpha, not bump
+ i++;
+ }
+ }
+ }
+ else
+ {
+ S32 i = 0;
+ for( S32 u = 0; u < mWidth; u++ )
+ {
+ for( S32 v = 0; v < mHeight; v++ )
+ {
+ baked_image_data[4*i + 0] = baked_color_data[4*i + 0];
+ baked_image_data[4*i + 1] = baked_color_data[4*i + 1];
+ baked_image_data[4*i + 2] = baked_color_data[4*i + 2];
+ baked_image_data[4*i + 3] = baked_mask_data[i];
+ i++;
+ }
+ }
+ }
+ }
+
+ LLPointer<LLImageJ2C> compressedImage = new LLImageJ2C;
+ compressedImage->setRate(0.f);
+ LLTransactionID tid;
+ LLAssetID asset_id;
+ tid.generate();
+ asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ BOOL res = false;
+ if( compressedImage->encode(baked_image, comment_text))
+ {
+ res = LLVFile::writeFile(compressedImage->getData(), compressedImage->getDataSize(),
+ gVFS, asset_id, LLAssetType::AT_TEXTURE);
+ if (res)
+ {
+ LLPointer<LLImageJ2C> integrity_test = new LLImageJ2C;
+ BOOL valid = FALSE;
+ S32 file_size;
+ U8* data = LLVFile::readFile(gVFS, asset_id, LLAssetType::AT_TEXTURE, &file_size);
+ if (data)
+ {
+ valid = integrity_test->validate(data, file_size); // integrity_test will delete 'data'
+ }
+ else
+ {
+ integrity_test->setLastError("Unable to read entire file");
+ }
+
+ if( valid )
+ {
+ LLBakedUploadData* baked_upload_data = new LLBakedUploadData( gAgent.getAvatarObject(), this );
+ mUploadID = baked_upload_data->mID;
+
+ gAssetStorage->storeAssetData(tid,
+ LLAssetType::AT_TEXTURE,
+ LLTexLayerSetBuffer::onTextureUploadComplete,
+ baked_upload_data,
+ TRUE, // temp_file
+ FALSE, // is_priority
+ TRUE); // store_local
+
+ mNeedsUpload = FALSE;
+ }
+ else
+ {
+ mUploadPending = FALSE;
+ llinfos << "unable to create baked upload file: corrupted" << llendl;
+ LLVFile file(gVFS, asset_id, LLAssetType::AT_TEXTURE, LLVFile::WRITE);
+ file.remove();
+ }
+ }
+ }
+ if (!res)
+ {
+ mUploadPending = FALSE;
+ llinfos << "unable to create baked upload file" << llendl;
+ }
+
+ delete [] baked_color_data;
+ delete [] baked_bump_data;
+}
+
+
+// static
+void LLTexLayerSetBuffer::onTextureUploadComplete(const LLUUID& uuid, void* userdata, S32 result) // StoreAssetData callback (not fixed)
+{
+ LLBakedUploadData* baked_upload_data = (LLBakedUploadData*)(U32)userdata;
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+
+ if (0 == result && avatar && !avatar->isDead())
+ {
+ // Sanity check: only the user's avatar should be uploading textures.
+ if( baked_upload_data->mAvatar == avatar )
+ {
+ // Because the avatar is still valid, it's layerset buffers should be valid also.
+ LLTexLayerSetBuffer* layerset_buffer = baked_upload_data->mLayerSetBuffer;
+ layerset_buffer->mUploadPending = FALSE;
+
+ if (layerset_buffer->mUploadID.isNull())
+ {
+ // The upload got canceled, we should be in the process of baking a new texture
+ // so request an upload with the new data
+ layerset_buffer->requestUpload();
+ }
+ else if( baked_upload_data->mID == layerset_buffer->mUploadID )
+ {
+ // This is the upload we're currently waiting for.
+ layerset_buffer->mUploadID.setNull();
+
+ if( result >= 0 )
+ {
+ LLVOAvatar::ETextureIndex baked_te = avatar->getBakedTE( layerset_buffer->mTexLayerSet );
+ if( !gAgent.cameraCustomizeAvatar() )
+ {
+ avatar->setNewBakedTexture( baked_te, uuid );
+ }
+ else
+ {
+ llinfos << "LLTexLayerSetBuffer::onTextureUploadComplete() when in Customize Avatar" << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Baked upload failed. Reason: " << result << llendl;
+ //FIXME: retry upload after n seconds, asset server could be busy
+ }
+ }
+ else
+ {
+ llinfos << "Received baked texture out of date, ignored." << llendl;
+ }
+
+ avatar->dirtyMesh();
+ }
+ }
+ else
+ {
+ // Baked texture failed to upload, but since we didn't set the new baked texture, it means that they'll
+ // try and rebake it at some point in the future (after login?)
+ llwarns << "Baked upload failed" << llendl;
+ }
+
+ delete baked_upload_data;
+}
+
+
+void LLTexLayerSetBuffer::bindTexture()
+{
+ if( mInitialized )
+ {
+ LLDynamicTexture::bindTexture();
+ }
+ else
+ {
+ gImageList.getImage(IMG_DEFAULT)->bind();
+ }
+}
+
+void LLTexLayerSetBuffer::bindBumpTexture( U32 stage )
+{
+ if( mBumpTexName )
+ {
+ LLImageGL::bindExternalTexture(mBumpTexName, stage, GL_TEXTURE_2D);
+
+ if( mLastBindTime != LLImageGL::sLastFrameTime )
+ {
+ mLastBindTime = LLImageGL::sLastFrameTime;
+ LLImageGL::updateBoundTexMem(mWidth * mHeight * 4);
+ }
+ }
+ else
+ {
+ LLImageGL::unbindTexture(stage, GL_TEXTURE_2D);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+LLTexLayerSetInfo::LLTexLayerSetInfo( )
+ :
+ mBodyRegion( "" ),
+ mWidth( 512 ),
+ mHeight( 512 ),
+ mClearAlpha( TRUE )
+{
+}
+
+LLTexLayerSetInfo::~LLTexLayerSetInfo( )
+{
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerSetInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer_set" ) );
+ if( !node->hasName( "layer_set" ) )
+ {
+ return FALSE;
+ }
+
+ // body_region
+ static LLStdStringHandle body_region_string = LLXmlTree::addAttributeString("body_region");
+ if( !node->getFastAttributeString( body_region_string, mBodyRegion ) )
+ {
+ llwarns << "<layer_set> is missing body_region attribute" << llendl;
+ return FALSE;
+ }
+
+ // width, height
+ static LLStdStringHandle width_string = LLXmlTree::addAttributeString("width");
+ if( !node->getFastAttributeS32( width_string, mWidth ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle height_string = LLXmlTree::addAttributeString("height");
+ if( !node->getFastAttributeS32( height_string, mHeight ) )
+ {
+ return FALSE;
+ }
+
+ // Optional alpha component to apply after all compositing is complete.
+ static LLStdStringHandle alpha_tga_file_string = LLXmlTree::addAttributeString("alpha_tga_file");
+ node->getFastAttributeString( alpha_tga_file_string, mStaticAlphaFileName );
+
+ static LLStdStringHandle clear_alpha_string = LLXmlTree::addAttributeString("clear_alpha");
+ node->getFastAttributeBOOL( clear_alpha_string, mClearAlpha );
+
+ // <layer>
+ for (LLXmlTreeNode* child = node->getChildByName( "layer" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLTexLayerInfo* info = new LLTexLayerInfo();
+ if( !info->parseXml( child ))
+ {
+ delete info;
+ return FALSE;
+ }
+ mLayerInfoList.push_back( info );
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::sHasCaches = FALSE;
+
+LLTexLayerSet::LLTexLayerSet( LLVOAvatar* avatar )
+ :
+ mComposite( NULL ),
+ mAvatar( avatar ),
+ mUpdatesEnabled( FALSE ),
+ mHasBump( FALSE ),
+ mInfo( NULL )
+{
+}
+
+LLTexLayerSet::~LLTexLayerSet()
+{
+ std::for_each(mLayerList.begin(), mLayerList.end(), DeletePointer());
+ delete mComposite;
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::setInfo(LLTexLayerSetInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ LLTexLayerSetInfo::layer_info_list_t::iterator iter;
+ mLayerList.reserve(info->mLayerInfoList.size());
+ for (iter = info->mLayerInfoList.begin(); iter != info->mLayerInfoList.end(); iter++)
+ {
+ LLTexLayer* layer = new LLTexLayer( this );
+ if (!layer->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mLayerList.push_back( layer );
+ }
+
+ requestUpdate();
+
+ stop_glerror();
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayerSet::parseData(LLXmlTreeNode* node)
+{
+ LLTexLayerSetInfo *info = new LLTexLayerSetInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return FALSE;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+void LLTexLayerSet::deleteCaches()
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ layer->deleteCaches();
+ }
+}
+
+// Returns TRUE if at least one packet of data has been received for each of the textures that this layerset depends on.
+BOOL LLTexLayerSet::isLocalTextureDataAvailable()
+{
+ return mAvatar->isLocalTextureDataAvailable( this );
+}
+
+
+// Returns TRUE if all of the data for the textures that this layerset depends on have arrived.
+BOOL LLTexLayerSet::isLocalTextureDataFinal()
+{
+ return mAvatar->isLocalTextureDataFinal( this );
+}
+
+
+BOOL LLTexLayerSet::render( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ // composite color layers
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ if( layer->getRenderPass() == RP_COLOR )
+ {
+ success &= layer->render( x, y, width, height );
+ }
+ }
+
+ // (Optionally) replace alpha with a single component image from a tga file.
+ if( !getInfo()->mStaticAlphaFileName.empty() )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+ glBlendFunc( GL_ONE, GL_ZERO );
+
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticAlphaFileName, TRUE );
+ if( image_gl )
+ {
+ LLGLSUIDefault gls_ui;
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLImageRaw* image_raw = gTexStaticImageList.getImageRaw( getInfo()->mStaticAlphaFileName );
+ if( image_raw )
+ {
+ GLenum format = GL_ALPHA;
+ if( mAvatar->bindScratchTexture(format) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, image_raw->getWidth(), image_raw->getHeight(), format, GL_UNSIGNED_BYTE, image_raw->getData() );
+ stop_glerror();
+
+ gl_rect_2d_simple_tex( width, height );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ }
+ else
+ if( getInfo()->mClearAlpha )
+ {
+ // Set the alpha channel to one (clean up after previous blending)
+ LLGLSNoTextureNoAlphaTest gls_no_alpha;
+ glColor4f( 0.f, 0.f, 0.f, 1.f );
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ gl_rect_2d_simple( width, height );
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ }
+ stop_glerror();
+
+ return success;
+}
+
+BOOL LLTexLayerSet::renderBump( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ LLGLSUIDefault gls_ui;
+ LLGLDepthTest gls_depth(GL_FALSE, GL_FALSE);
+
+ //static S32 bump_layer_count = 1;
+
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ if( layer->getRenderPass() == RP_BUMP )
+ {
+ success &= layer->render( x, y, width, height );
+ }
+ }
+
+ // Set the alpha channel to one (clean up after previous blending)
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha;
+ glColor4f( 0.f, 0.f, 0.f, 1.f );
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ gl_rect_2d_simple( width, height );
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+ stop_glerror();
+
+ return success;
+}
+
+void LLTexLayerSet::requestUpdate()
+{
+ if( mUpdatesEnabled )
+ {
+ createComposite();
+ mComposite->requestUpdate();
+ }
+}
+
+void LLTexLayerSet::requestUpload()
+{
+ createComposite();
+ mComposite->requestUpload();
+}
+
+void LLTexLayerSet::cancelUpload()
+{
+ if(mComposite)
+ {
+ mComposite->cancelUpload();
+ }
+}
+
+void LLTexLayerSet::createComposite()
+{
+ if( !mComposite )
+ {
+ S32 width = mInfo->mWidth;
+ S32 height = mInfo->mHeight;
+ // Composite other avatars at reduced resolution
+ if( !mAvatar->mIsSelf )
+ {
+ width /= 2;
+ height /= 2;
+ }
+ mComposite = new LLTexLayerSetBuffer( this, width, height, mHasBump );
+ }
+}
+
+void LLTexLayerSet::destroyComposite()
+{
+ if( mComposite )
+ {
+ delete mComposite;
+ mComposite = NULL;
+ }
+}
+
+void LLTexLayerSet::setUpdatesEnabled( BOOL b )
+{
+ mUpdatesEnabled = b;
+}
+
+
+void LLTexLayerSet::updateComposite()
+{
+ createComposite();
+ mComposite->updateImmediate();
+}
+
+LLTexLayerSetBuffer* LLTexLayerSet::getComposite()
+{
+ createComposite();
+ return mComposite;
+}
+
+void LLTexLayerSet::gatherAlphaMasks(U8 *data, S32 width, S32 height)
+{
+ S32 size = width * height;
+
+ memset(data, 255, width * height);
+
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ U8* alphaData = layer->getAlphaData();
+ if (!alphaData && layer->hasAlphaParams())
+ {
+ LLColor4 net_color;
+ layer->findNetColor( &net_color );
+ layer->invalidateMorphMasks();
+ layer->renderAlphaMasks(mComposite->getOriginX(), mComposite->getOriginY(), width, height, &net_color);
+ alphaData = layer->getAlphaData();
+ }
+ if (alphaData)
+ {
+ for( S32 i = 0; i < size; i++ )
+ {
+ U8 curAlpha = data[i];
+ U16 resultAlpha = curAlpha;
+ resultAlpha *= (alphaData[i] + 1);
+ resultAlpha = resultAlpha >> 8;
+ data[i] = (U8)resultAlpha;
+ }
+ }
+ }
+}
+
+void LLTexLayerSet::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ for( layer_list_t::iterator iter = mLayerList.begin(); iter != mLayerList.end(); iter++ )
+ {
+ LLTexLayer* layer = *iter;
+ layer->applyMorphMask(tex_data, width, height, num_components);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+LLTexLayerInfo::LLTexLayerInfo( )
+ :
+ mWriteAllChannels( FALSE ),
+ mRenderPass( RP_COLOR ),
+ mFixedColor( 0.f, 0.f, 0.f, 0.f ),
+ mLocalTexture( -1 ),
+ mStaticImageIsMask( FALSE ),
+ mUseLocalTextureAlphaOnly( FALSE )
+{
+}
+
+LLTexLayerInfo::~LLTexLayerInfo( )
+{
+ std::for_each(mColorInfoList.begin(), mColorInfoList.end(), DeletePointer());
+ std::for_each(mAlphaInfoList.begin(), mAlphaInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexLayerInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "layer" ) );
+
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle write_all_channels_string = LLXmlTree::addAttributeString("write_all_channels");
+ node->getFastAttributeBOOL( write_all_channels_string, mWriteAllChannels );
+
+ LLString render_pass_name;
+ static LLStdStringHandle render_pass_string = LLXmlTree::addAttributeString("render_pass");
+ if( node->getFastAttributeString( render_pass_string, render_pass_name ) )
+ {
+ if( render_pass_name == "bump" )
+ {
+ mRenderPass = RP_BUMP;
+ }
+ }
+
+ // Note: layers can have either a "global_color" attrib, a "fixed_color" attrib, or a <param_color> child.
+ // global color attribute (optional)
+ static LLStdStringHandle global_color_string = LLXmlTree::addAttributeString("global_color");
+ node->getFastAttributeString( global_color_string, mGlobalColor );
+
+ // color attribute (optional)
+ LLColor4U color4u;
+ static LLStdStringHandle fixed_color_string = LLXmlTree::addAttributeString("fixed_color");
+ if( node->getFastAttributeColor4U( fixed_color_string, color4u ) )
+ {
+ mFixedColor.setVec( color4u );
+ }
+
+ // <texture> optional sub-element
+ for (LLXmlTreeNode* texture_node = node->getChildByName( "texture" );
+ texture_node;
+ texture_node = node->getNextNamedChild())
+ {
+ LLString local_texture;
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ static LLStdStringHandle local_texture_string = LLXmlTree::addAttributeString("local_texture");
+ static LLStdStringHandle file_is_mask_string = LLXmlTree::addAttributeString("file_is_mask");
+ static LLStdStringHandle local_texture_alpha_only_string = LLXmlTree::addAttributeString("local_texture_alpha_only");
+ if( texture_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ texture_node->getFastAttributeBOOL( file_is_mask_string, mStaticImageIsMask );
+ }
+ else if( texture_node->getFastAttributeString( local_texture_string, local_texture ) )
+ {
+ texture_node->getFastAttributeBOOL( local_texture_alpha_only_string, mUseLocalTextureAlphaOnly );
+
+ if( "upper_shirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_SHIRT;
+ }
+ else if( "upper_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_BODYPAINT;
+ }
+ else if( "lower_pants" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_PANTS;
+ }
+ else if( "lower_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_BODYPAINT;
+ }
+ else if( "lower_shoes" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_SHOES;
+ }
+ else if( "head_bodypaint" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_HEAD_BODYPAINT;
+ }
+ else if( "lower_socks" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_SOCKS;
+ }
+ else if( "upper_jacket" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_JACKET;
+ }
+ else if( "lower_jacket" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_JACKET;
+ }
+ else if( "upper_gloves" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_GLOVES;
+ }
+ else if( "upper_undershirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_UPPER_UNDERSHIRT;
+ }
+ else if( "lower_underpants" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_LOWER_UNDERPANTS;
+ }
+ else if( "eyes_iris" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_EYES_IRIS;
+ }
+ else if( "skirt" == local_texture )
+ {
+ mLocalTexture = LLVOAvatar::LOCTEX_SKIRT;
+ }
+ else
+ {
+ llwarns << "<texture> element has invalid local_texure attribute: " << mName << " " << local_texture << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<texture> element is missing a required attribute. " << mName << llendl;
+ return FALSE;
+ }
+ }
+
+ for (LLXmlTreeNode* maskNode = node->getChildByName( "morph_mask" );
+ maskNode;
+ maskNode = node->getNextNamedChild())
+ {
+ LLString morph_name;
+ static LLStdStringHandle morph_name_string = LLXmlTree::addAttributeString("morph_name");
+ if (maskNode->getFastAttributeString(morph_name_string, morph_name))
+ {
+ BOOL invert = FALSE;
+ static LLStdStringHandle invert_string = LLXmlTree::addAttributeString("invert");
+ maskNode->getFastAttributeBOOL(invert_string, invert);
+ mMorphNameList.push_back(std::pair<LLString,BOOL>(morph_name,invert));
+ }
+ }
+
+ // <param> optional sub-element (color or alpha params)
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexParamColorInfo* info = new LLTexParamColorInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mColorInfoList.push_back( info );
+ }
+ else if( child->getChildByName( "param_alpha" ) )
+ {
+ // <param><param_alpha/></param>
+ LLTexLayerParamAlphaInfo* info = new LLTexLayerParamAlphaInfo( );
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mAlphaInfoList.push_back( info );
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayer
+// A single texture layer, consisting of:
+// * color, consisting of either
+// * one or more color parameters (weighted colors)
+// * a reference to a global color
+// * a fixed color with non-zero alpha
+// * opaque white (the default)
+// * (optional) a texture defined by either
+// * a GUID
+// * a texture entry index (TE)
+// * (optional) one or more alpha parameters (weighted alpha textures)
+//-----------------------------------------------------------------------------
+LLTexLayer::LLTexLayer( LLTexLayerSet* layer_set )
+ :
+ mTexLayerSet( layer_set ),
+ mMorphMasksValid( FALSE ),
+ mStaticImageInvalid( FALSE ),
+ mInfo( NULL )
+{
+}
+
+LLTexLayer::~LLTexLayer()
+{
+ // mParamAlphaList and mParamColorList are LLViewerVisualParam's and get
+ // deleted with ~LLCharacter()
+ //std::for_each(mParamAlphaList.begin(), mParamAlphaList.end(), DeletePointer());
+ //std::for_each(mParamColorList.begin(), mParamColorList.end(), DeletePointer());
+
+ for( alpha_cache_t::iterator iter = mAlphaCache.begin();
+ iter != mAlphaCache.end(); iter++ )
+ {
+ U8* alpha_data = iter->second;
+ delete [] alpha_data;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setInfo
+//-----------------------------------------------------------------------------
+
+BOOL LLTexLayer::setInfo(LLTexLayerInfo* info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ if (info->mRenderPass == RP_BUMP)
+ mTexLayerSet->setBump(TRUE);
+
+ {
+ LLTexLayerInfo::morph_name_list_t::iterator iter;
+ for (iter = mInfo->mMorphNameList.begin(); iter != mInfo->mMorphNameList.end(); iter++)
+ {
+ // FIXME: we assume that the referenced visual param is a morph target,
+ // need a better way of actually looking this up
+ LLPolyMorphTarget *morph_param;
+ LLString *name = &(iter->first);
+ morph_param = (LLPolyMorphTarget *)(getTexLayerSet()->getAvatar()->getVisualParam(name->c_str()));
+ if (morph_param)
+ {
+ BOOL invert = iter->second;
+ addMaskedMorph(morph_param, invert);
+ }
+ }
+ }
+
+ {
+ LLTexLayerInfo::color_info_list_t::iterator iter;
+ mParamColorList.reserve(mInfo->mColorInfoList.size());
+ for (iter = mInfo->mColorInfoList.begin(); iter != mInfo->mColorInfoList.end(); iter++)
+ {
+ LLTexParamColor* param_color = new LLTexParamColor( this );
+ if (!param_color->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamColorList.push_back( param_color );
+ }
+ }
+ {
+ LLTexLayerInfo::alpha_info_list_t::iterator iter;
+ mParamAlphaList.reserve(mInfo->mAlphaInfoList.size());
+ for (iter = mInfo->mAlphaInfoList.begin(); iter != mInfo->mAlphaInfoList.end(); iter++)
+ {
+ LLTexLayerParamAlpha* param_alpha = new LLTexLayerParamAlpha( this );
+ if (!param_alpha->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamAlphaList.push_back( param_alpha );
+ }
+ }
+
+ return TRUE;
+}
+
+#if 0 // obsolete
+//-----------------------------------------------------------------------------
+// parseData
+//-----------------------------------------------------------------------------
+BOOL LLTexLayer::parseData( LLXmlTreeNode* node )
+{
+ LLTexLayerInfo *info = new LLTexLayerInfo;
+
+ if (!info->parseXml(node))
+ {
+ delete info;
+ return FALSE;
+ }
+ if (!setInfo(info))
+ {
+ delete info;
+ return FALSE;
+ }
+ return TRUE;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+
+
+BOOL LLTexLayer::loadStaticImageRaw()
+{
+ if( mStaticImageRaw.isNull() && !mStaticImageInvalid)
+ {
+ mStaticImageRaw = gTexStaticImageList.getImageRaw( getInfo()->mStaticImageFileName );
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mStaticImageRaw.notNull() ? TRUE : FALSE;
+ if( mStaticImageRaw.isNull() )
+ {
+ llwarns << "Unable to load static file: " << getInfo()->mStaticImageFileName << llendl;
+ mStaticImageInvalid = TRUE; // don't try again.
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void LLTexLayer::deleteCaches()
+{
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin();
+ iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ param->deleteCaches();
+ }
+ mStaticImageRaw = NULL;
+}
+
+BOOL LLTexLayer::render( S32 x, S32 y, S32 width, S32 height )
+{
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ gPipeline.disableLights();
+
+ BOOL success = TRUE;
+
+ BOOL color_specified = FALSE;
+ BOOL alpha_mask_specified = FALSE;
+
+ LLColor4 net_color;
+ color_specified = findNetColor( &net_color );
+
+ // If you can't see the layer, don't render it.
+ if( is_approx_zero( net_color.mV[VW] ) )
+ {
+ return success;
+ }
+
+ alpha_list_t::iterator iter = mParamAlphaList.begin();
+ if( iter != mParamAlphaList.end() )
+ {
+ // If we have alpha masks, but we're skipping all of them, skip the whole layer.
+ // However, we can't do this optimization if we have morph masks that need updating.
+ if( mMaskedMorphs.empty() )
+ {
+ BOOL skip_layer = TRUE;
+
+ while( iter != mParamAlphaList.end() )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+
+ if( !param->getSkip() )
+ {
+ skip_layer = FALSE;
+ break;
+ }
+
+ iter++;
+ }
+
+ if( skip_layer )
+ {
+ return success;
+ }
+ }
+
+ renderAlphaMasks( x, y, width, height, &net_color );
+ alpha_mask_specified = TRUE;
+ glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA );
+ }
+
+ glColor4fv( net_color.mV);
+
+ if( getInfo()->mWriteAllChannels )
+ {
+ glBlendFunc( GL_ONE, GL_ZERO );
+ }
+
+ if( (getInfo()->mLocalTexture != -1) && !getInfo()->mUseLocalTextureAlphaOnly )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = NULL;
+ if( mTexLayerSet->getAvatar()->getLocalTextureGL( getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( image_gl )
+ {
+ LLGLDisable alpha_test(getInfo()->mWriteAllChannels ? GL_ALPHA_TEST : 0);
+
+ BOOL old_clamps = image_gl->getClampS();
+ BOOL old_clampt = image_gl->getClampT();
+
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ image_gl->setClamp(old_clamps, old_clampt);
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( mTexLayerSet->getAvatar()->getLocalTextureRaw( getInfo()->mLocalTexture, image_raw ) )
+ {
+ success &= renderImageRaw( image_raw->getData(),
+ image_raw->getWidth(),
+ image_raw->getHeight(),
+ image_raw->getComponents(),
+ width,
+ height,
+ FALSE );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( image_gl )
+ {
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ if (!loadStaticImageRaw())
+ {
+ success = FALSE;
+ }
+ if( mStaticImageRaw.notNull() )
+ {
+ success &= renderImageRaw(
+ mStaticImageRaw->getData(),
+ mStaticImageRaw->getWidth(),
+ mStaticImageRaw->getHeight(),
+ mStaticImageRaw->getComponents(), width, height, getInfo()->mStaticImageIsMask );
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( ((-1 == getInfo()->mLocalTexture) ||
+ getInfo()->mUseLocalTextureAlphaOnly) &&
+ getInfo()->mStaticImageFileName.empty() &&
+ color_specified )
+ {
+ LLGLSNoTextureNoAlphaTest gls;
+ glColor4fv( net_color.mV);
+ gl_rect_2d_simple( width, height );
+ }
+
+ if( alpha_mask_specified || getInfo()->mWriteAllChannels )
+ {
+ // Restore standard blend func value
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+ stop_glerror();
+ }
+
+ if( !success )
+ {
+ llinfos << "LLTexLayer::render() partial: " << getInfo()->mName << llendl;
+ }
+ return success;
+}
+
+U8* LLTexLayer::getAlphaData()
+{
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID(getInfo()->mLocalTexture);
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)&param_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+
+ alpha_cache_t::iterator iter2 = mAlphaCache.find(cache_index);
+ return (iter2 == mAlphaCache.end()) ? 0 : iter2->second;
+}
+
+BOOL LLTexLayer::findNetColor( LLColor4* net_color )
+{
+ // Color is either:
+ // * one or more color parameters (weighted colors) (which may make use of a global color or fixed color)
+ // * a reference to a global color
+ // * a fixed color with non-zero alpha
+ // * opaque white (the default)
+
+ if( !mParamColorList.empty() )
+ {
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatar()->getGlobalColor( getInfo()->mGlobalColor ) );
+ }
+ else
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ }
+ else
+ {
+ net_color->setVec( 0.f, 0.f, 0.f, 0.f );
+ }
+
+ for( color_list_t::iterator iter = mParamColorList.begin();
+ iter != mParamColorList.end(); iter++ )
+ {
+ LLTexParamColor* param = *iter;
+ LLColor4 param_net = param->getNetColor();
+ switch( param->getOperation() )
+ {
+ case OP_ADD:
+ *net_color += param_net;
+ break;
+ case OP_MULTIPLY:
+ net_color->mV[VX] *= param_net.mV[VX];
+ net_color->mV[VY] *= param_net.mV[VY];
+ net_color->mV[VZ] *= param_net.mV[VZ];
+ net_color->mV[VW] *= param_net.mV[VW];
+ break;
+ case OP_BLEND:
+ net_color->setVec( lerp(*net_color, param_net, param->getWeight()) );
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ return TRUE;
+ }
+
+ if( !getGlobalColor().empty() )
+ {
+ net_color->setVec( mTexLayerSet->getAvatar()->getGlobalColor( getGlobalColor() ) );
+ return TRUE;
+ }
+
+ if( getInfo()->mFixedColor.mV[VW] )
+ {
+ net_color->setVec( getInfo()->mFixedColor );
+ return TRUE;
+ }
+
+ net_color->setToWhite();
+
+ return FALSE; // No need to draw a separate colored polygon
+}
+
+
+BOOL LLTexLayer::renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4* colorp )
+{
+ BOOL success = TRUE;
+
+ llassert( !mParamAlphaList.empty() );
+
+ glColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE );
+
+ alpha_list_t::iterator iter = mParamAlphaList.begin();
+ LLTexLayerParamAlpha* first_param = *iter;
+
+ // Note: if the first param is a mulitply, multiply against the current buffer's alpha
+ if( !first_param || !first_param->getMultiplyBlend() )
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+
+ // Clear the alpha
+ glBlendFunc( GL_ONE, GL_ZERO );
+
+ glColor4f( 0.f, 0.f, 0.f, 0.f );
+ gl_rect_2d_simple( width, height );
+ }
+
+ // Accumulate alphas
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ glColor4f( 1.f, 1.f, 1.f, 1.f );
+
+ for( iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ success &= param->render( x, y, width, height );
+ }
+
+ // Approximates a min() function
+ glBlendFunc( GL_DST_ALPHA, GL_ZERO );
+
+ // Accumulate the alpha component of the texture
+ if( getInfo()->mLocalTexture != -1 )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = NULL;
+ if( mTexLayerSet->getAvatar()->getLocalTextureGL( getInfo()->mLocalTexture, &image_gl ) )
+ {
+ if( image_gl && (image_gl->getComponents() == 4) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ BOOL old_clamps = image_gl->getClampS();
+ BOOL old_clampt = image_gl->getClampT();
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ image_gl->setClamp(old_clamps, old_clampt);
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( mTexLayerSet->getAvatar()->getLocalTextureRaw( getInfo()->mLocalTexture, image_raw ) )
+ {
+ if(image_raw->getComponents() == 4)
+ {
+ success &= renderImageRaw(
+ image_raw->getData(),
+ image_raw->getWidth(),
+ image_raw->getHeight(),
+ image_raw->getComponents(), width, height, FALSE );
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() )
+ {
+ if( gUseAvatarGLCache )
+ {
+ LLImageGL* image_gl = gTexStaticImageList.getImageGL( getInfo()->mStaticImageFileName, getInfo()->mStaticImageIsMask );
+ if( image_gl )
+ {
+ if( (image_gl->getComponents() == 4) ||
+ ( (image_gl->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ image_gl->bind();
+ gl_rect_2d_simple_tex( width, height );
+ image_gl->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ if (!loadStaticImageRaw())
+ {
+ success = FALSE;
+ }
+
+ if( mStaticImageRaw.notNull() )
+ {
+ if( (mStaticImageRaw->getComponents() == 4) ||
+ ( (mStaticImageRaw->getComponents() == 1) && getInfo()->mStaticImageIsMask ) )
+ {
+ success &= renderImageRaw(
+ mStaticImageRaw->getData(),
+ mStaticImageRaw->getWidth(),
+ mStaticImageRaw->getHeight(),
+ mStaticImageRaw->getComponents(), width, height, getInfo()->mStaticImageIsMask );
+ }
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+
+ // Draw a rectangle with the layer color to multiply the alpha by that color's alpha.
+ // Note: we're still using glBlendFunc( GL_DST_ALPHA, GL_ZERO );
+ if( colorp->mV[VW] != 1.f )
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+ glColor4fv( colorp->mV );
+ gl_rect_2d_simple( width, height );
+ }
+
+
+ LLGLSUIDefault gls_ui;
+
+ glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
+
+ if (!mMorphMasksValid && !mMaskedMorphs.empty())
+ {
+ LLCRC alpha_mask_crc;
+ const LLUUID& uuid = mTexLayerSet->getAvatar()->getLocalTextureID(getInfo()->mLocalTexture);
+ alpha_mask_crc.update((U8*)(&uuid.mData), UUID_BYTES);
+
+ for( alpha_list_t::iterator iter = mParamAlphaList.begin(); iter != mParamAlphaList.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* param = *iter;
+ F32 param_weight = param->getWeight();
+ alpha_mask_crc.update((U8*)&param_weight, sizeof(F32));
+ }
+
+ U32 cache_index = alpha_mask_crc.getCRC();
+
+ alpha_cache_t::iterator iter2 = mAlphaCache.find(cache_index);
+ U8* alpha_data;
+ if (iter2 != mAlphaCache.end())
+ {
+ alpha_data = iter2->second;
+ }
+ else
+ {
+ // clear out a slot if we have filled our cache
+ S32 max_cache_entries = getTexLayerSet()->getAvatar()->mIsSelf ? 4 : 1;
+ while ((S32)mAlphaCache.size() >= max_cache_entries)
+ {
+ iter2 = mAlphaCache.begin(); // arbitrarily grab the first entry
+ alpha_data = iter2->second;
+ delete [] alpha_data;
+ mAlphaCache.erase(iter2);
+ }
+ alpha_data = new U8[width * height];
+ mAlphaCache[cache_index] = alpha_data;
+ glReadPixels(x, y, width, height, GL_ALPHA, GL_UNSIGNED_BYTE, alpha_data);
+ }
+
+ getTexLayerSet()->getAvatar()->dirtyMesh();
+
+ mMorphMasksValid = TRUE;
+
+ for( morph_list_t::iterator iter3 = mMaskedMorphs.begin();
+ iter3 != mMaskedMorphs.end(); iter3++ )
+ {
+ LLMaskedMorph* maskedMorph = &(*iter3);
+ maskedMorph->mMorphTarget->applyMask(alpha_data, width, height, 1, maskedMorph->mInvert);
+ }
+ }
+
+ return success;
+}
+
+void LLTexLayer::applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components)
+{
+ for( morph_list_t::iterator iter = mMaskedMorphs.begin();
+ iter != mMaskedMorphs.end(); iter++ )
+ {
+ LLMaskedMorph* maskedMorph = &(*iter);
+ maskedMorph->mMorphTarget->applyMask(tex_data, width, height, num_components, maskedMorph->mInvert);
+ }
+}
+
+// Returns TRUE on success.
+BOOL LLTexLayer::renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask )
+{
+ if (!in_data)
+ {
+ return FALSE;
+ }
+ GLenum format_options[4] = { GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA };
+ GLenum format = format_options[in_components-1];
+ if( is_mask )
+ {
+ llassert( 1 == in_components );
+ format = GL_ALPHA;
+ }
+
+ if( (in_width != VOAVATAR_SCRATCH_TEX_WIDTH) || (in_height != VOAVATAR_SCRATCH_TEX_HEIGHT) )
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ GLenum internal_format_options[4] = { GL_LUMINANCE8, GL_LUMINANCE8_ALPHA8, GL_RGB8, GL_RGBA8 };
+ GLenum internal_format = internal_format_options[in_components-1];
+ if( is_mask )
+ {
+ llassert( 1 == in_components );
+ internal_format = GL_ALPHA8;
+ }
+
+ GLuint name = 0;
+ glGenTextures(1, &name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ in_width, in_height,
+ 0, format, GL_UNSIGNED_BYTE, in_data );
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ glDeleteTextures(1, &name );
+ stop_glerror();
+ }
+ else
+ {
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ if( !mTexLayerSet->getAvatar()->bindScratchTexture(format) )
+ {
+ return FALSE;
+ }
+
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, in_width, in_height, format, GL_UNSIGNED_BYTE, in_data );
+ stop_glerror();
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ return TRUE;
+}
+
+void LLTexLayer::requestUpdate()
+{
+ mTexLayerSet->requestUpdate();
+}
+
+void LLTexLayer::addMaskedMorph(LLPolyMorphTarget* morph_target, BOOL invert)
+{
+ mMaskedMorphs.push_front(LLMaskedMorph(morph_target, invert));
+}
+
+void LLTexLayer::invalidateMorphMasks()
+{
+ mMorphMasksValid = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlphaInfo
+//-----------------------------------------------------------------------------
+LLTexLayerParamAlphaInfo::LLTexLayerParamAlphaInfo( )
+ :
+ mMultiplyBlend( FALSE ),
+ mSkipIfZeroWeight( FALSE ),
+ mDomain( 0.f )
+{
+}
+
+BOOL LLTexLayerParamAlphaInfo::parseXml(LLXmlTreeNode* node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_alpha" ) );
+
+ if( !LLViewerVisualParamInfo::parseXml(node) )
+ return FALSE;
+
+ LLXmlTreeNode* param_alpha_node = node->getChildByName( "param_alpha" );
+ if( !param_alpha_node )
+ {
+ return FALSE;
+ }
+
+ static LLStdStringHandle tga_file_string = LLXmlTree::addAttributeString("tga_file");
+ if( param_alpha_node->getFastAttributeString( tga_file_string, mStaticImageFileName ) )
+ {
+ // Don't load the image file until it's actually needed.
+ }
+// else
+// {
+// llwarns << "<param_alpha> element is missing tga_file attribute." << llendl;
+// }
+
+ static LLStdStringHandle multiply_blend_string = LLXmlTree::addAttributeString("multiply_blend");
+ param_alpha_node->getFastAttributeBOOL( multiply_blend_string, mMultiplyBlend );
+
+ static LLStdStringHandle skip_if_zero_string = LLXmlTree::addAttributeString("skip_if_zero");
+ param_alpha_node->getFastAttributeBOOL( skip_if_zero_string, mSkipIfZeroWeight );
+
+ static LLStdStringHandle domain_string = LLXmlTree::addAttributeString("domain");
+ param_alpha_node->getFastAttributeF32( domain_string, mDomain );
+
+ gGradientPaletteList.initPalette(mDomain);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+
+// static
+LLTexLayerParamAlpha::param_alpha_ptr_list_t LLTexLayerParamAlpha::sInstances;
+
+// static
+void LLTexLayerParamAlpha::dumpCacheByteCount()
+{
+ S32 gl_bytes = 0;
+ getCacheByteCount( &gl_bytes );
+ llinfos << "Processed Alpha Texture Cache GL:" << (gl_bytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLTexLayerParamAlpha::getCacheByteCount( S32* gl_bytes )
+{
+ *gl_bytes = 0;
+
+ for( param_alpha_ptr_list_t::iterator iter = sInstances.begin();
+ iter != sInstances.end(); iter++ )
+ {
+ LLTexLayerParamAlpha* instance = *iter;
+ LLImageGL* image_gl = instance->mCachedProcessedImageGL;
+ if( image_gl )
+ {
+ S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+
+ if( image_gl->getHasGLTexture() )
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+LLTexLayerParamAlpha::LLTexLayerParamAlpha( LLTexLayer* layer )
+ :
+ mCachedProcessedImageGL( NULL ),
+ mTexLayer( layer ),
+ mNeedsCreateTexture( FALSE ),
+ mStaticImageInvalid( FALSE ),
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mCachedEffectiveWeight(0.f)
+{
+ sInstances.push_front( this );
+}
+
+LLTexLayerParamAlpha::~LLTexLayerParamAlpha()
+{
+ deleteCaches();
+ sInstances.remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+BOOL LLTexLayerParamAlpha::setInfo(LLTexLayerParamAlphaInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+
+ mTexLayer->getTexLayerSet()->getAvatar()->addVisualParam( this );
+ setWeight(getDefaultWeight(), FALSE );
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+
+void LLTexLayerParamAlpha::deleteCaches()
+{
+ mStaticImageTGA = NULL; // deletes image
+ mCachedProcessedImageGL = NULL;
+ mStaticImageRaw = NULL;
+ mNeedsCreateTexture = FALSE;
+}
+
+void LLTexLayerParamAlpha::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ return;
+ }
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8( mCurWeight, min_weight, max_weight );
+ U8 new_u8 = F32_to_U8( new_weight, min_weight, max_weight );
+ if( cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ LLVOAvatar* avatar = mTexLayer->getTexLayerSet()->getAvatar();
+ if( avatar->getSex() & getSex() )
+ {
+ avatar->invalidateComposite( mTexLayer->getTexLayerSet(), set_by_user );
+ mTexLayer->invalidateMorphMasks();
+ }
+ }
+}
+
+void LLTexLayerParamAlpha::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ mTargetWeight = target_value;
+ setWeight(target_value, set_by_user);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+void LLTexLayerParamAlpha::animate(F32 delta, BOOL set_by_user)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, set_by_user);
+ }
+}
+
+BOOL LLTexLayerParamAlpha::getSkip()
+{
+ LLVOAvatar *avatar = mTexLayer->getTexLayerSet()->getAvatar();
+
+ if( getInfo()->mSkipIfZeroWeight )
+ {
+ F32 effective_weight = ( avatar->getSex() & getSex() ) ? mCurWeight : getDefaultWeight();
+ if (is_approx_zero( effective_weight ))
+ {
+ return TRUE;
+ }
+ }
+
+ EWearableType type = (EWearableType)getWearableType();
+ if( (type != WT_INVALID) && !avatar->isWearingWearableType( type ) )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+BOOL LLTexLayerParamAlpha::render( S32 x, S32 y, S32 width, S32 height )
+{
+ BOOL success = TRUE;
+
+ F32 effective_weight = ( mTexLayer->getTexLayerSet()->getAvatar()->getSex() & getSex() ) ? mCurWeight : getDefaultWeight();
+ BOOL weight_changed = effective_weight != mCachedEffectiveWeight;
+ if( getSkip() )
+ {
+ return success;
+ }
+
+ if( getInfo()->mMultiplyBlend )
+ {
+ glBlendFunc( GL_DST_ALPHA, GL_ZERO ); // Multiplication: approximates a min() function
+ }
+ else
+ {
+ glBlendFunc( GL_ONE, GL_ONE ); // Addition: approximates a max() function
+ }
+
+ if( !getInfo()->mStaticImageFileName.empty() && !mStaticImageInvalid)
+ {
+ if( mStaticImageTGA.isNull() )
+ {
+ // Don't load the image file until we actually need it the first time. Like now.
+ mStaticImageTGA = gTexStaticImageList.getImageTGA( getInfo()->mStaticImageFileName );
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mStaticImageTGA.notNull() ? TRUE : FALSE;
+
+ if( mStaticImageTGA.isNull() )
+ {
+ llwarns << "Unable to load static file: " << getInfo()->mStaticImageFileName << llendl;
+ mStaticImageInvalid = TRUE; // don't try again.
+ return FALSE;
+ }
+ }
+
+ const S32 image_tga_width = mStaticImageTGA->getWidth();
+ const S32 image_tga_height = mStaticImageTGA->getHeight();
+ if( !mCachedProcessedImageGL ||
+ (mCachedProcessedImageGL->getWidth() != image_tga_width) ||
+ (mCachedProcessedImageGL->getHeight() != image_tga_height) ||
+ (weight_changed && !(gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))) ||
+ (!gUseAvatarGLCache) )
+ {
+// llinfos << "Building Cached Alpha: " << mName << ": (" << mStaticImageRaw->getWidth() << ", " << mStaticImageRaw->getHeight() << ") " << effective_weight << llendl;
+ mCachedEffectiveWeight = effective_weight;
+
+ if( !mCachedProcessedImageGL )
+ {
+ mCachedProcessedImageGL = new LLImageGL( image_tga_width, image_tga_height, 1, FALSE);
+
+ // We now have something in one of our caches
+ LLTexLayerSet::sHasCaches |= mCachedProcessedImageGL ? TRUE : FALSE;
+
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ // interpret luminance values as color index table
+ mCachedProcessedImageGL->setExplicitFormat( GL_COLOR_INDEX8_EXT, GL_COLOR_INDEX );
+ }
+ else
+ {
+ mCachedProcessedImageGL->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
+ }
+ }
+
+ // Applies domain and effective weight to data as it is decoded. Also resizes the raw image if needed.
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ mStaticImageRaw = NULL;
+ mStaticImageRaw = new LLImageRaw;
+ mStaticImageTGA->decode(mStaticImageRaw);
+ mNeedsCreateTexture = TRUE;
+ }
+ else
+ {
+ mStaticImageRaw = NULL;
+ mStaticImageRaw = new LLImageRaw;
+ mStaticImageTGA->decodeAndProcess( mStaticImageRaw, getInfo()->mDomain, effective_weight );
+ mNeedsCreateTexture = TRUE;
+ }
+ }
+
+ if( mCachedProcessedImageGL )
+ {
+ if( gUseAvatarGLCache ) // 64 MB
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ if( mNeedsCreateTexture )
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+ mNeedsCreateTexture = FALSE;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+ }
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ mCachedProcessedImageGL->bind();
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+ mCachedProcessedImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ // Create the GL texture, and then hang onto it for future use.
+ if( mNeedsCreateTexture )
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+ mNeedsCreateTexture = FALSE;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+ }
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ mCachedProcessedImageGL->bind();
+ gl_rect_2d_simple_tex( width, height );
+ mCachedProcessedImageGL->unbindTexture(0, GL_TEXTURE_2D);
+ }
+ stop_glerror();
+ }
+ else
+ {
+ if( (mCachedProcessedImageGL->getWidth() != VOAVATAR_SCRATCH_TEX_WIDTH) ||
+ (mCachedProcessedImageGL->getHeight() != VOAVATAR_SCRATCH_TEX_HEIGHT) )
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ mCachedProcessedImageGL->destroyGLTexture();
+ }
+ else
+ {
+ // Create the GL texture, bind it and draw a rect, and then immediately destroy it.
+ mCachedProcessedImageGL->createGLTexture(0, mStaticImageRaw);
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+
+ mCachedProcessedImageGL->bind();
+ mCachedProcessedImageGL->setClamp(TRUE, TRUE);
+
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+
+ mCachedProcessedImageGL->destroyGLTexture();
+ }
+ stop_glerror();
+ }
+ else
+ {
+ if (gGLManager.mHasPalettedTextures && gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_PALETTE))
+ {
+ // Write into a pre-existing GL Image, and then bind and render that.
+ // Faster than creating a new GL Image and then destroying it.
+ if( mTexLayer->getTexLayerSet()->getAvatar()->bindScratchTexture( GL_COLOR_INDEX ) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
+ mCachedProcessedImageGL->getWidth(),
+ mCachedProcessedImageGL->getHeight(),
+ GL_COLOR_INDEX, GL_UNSIGNED_BYTE,
+ mStaticImageRaw->getData() );
+ stop_glerror();
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gGradientPaletteList.setHardwarePalette( getInfo()->mDomain, effective_weight );
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ else
+ {
+ // Write into a pre-existing GL Image, and then bind and render that.
+ // Faster than creating a new GL Image and then destroying it.
+ if( mTexLayer->getTexLayerSet()->getAvatar()->bindScratchTexture( GL_ALPHA ) )
+ {
+ glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
+ mCachedProcessedImageGL->getWidth(),
+ mCachedProcessedImageGL->getHeight(),
+ GL_ALPHA, GL_UNSIGNED_BYTE,
+ mStaticImageRaw->getData() );
+ stop_glerror();
+
+ LLGLSNoAlphaTest gls_no_alpha_test;
+ gl_rect_2d_simple_tex( width, height );
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ else
+ {
+ success = FALSE;
+ }
+ }
+ }
+ }
+ }
+
+ // Don't keep the cache for other people's avatars
+ // (It's not really a "cache" in that case, but the logic is the same)
+ if( !mTexLayer->getTexLayerSet()->getAvatar()->mIsSelf )
+ {
+ mCachedProcessedImageGL = NULL;
+ }
+ }
+ else
+ {
+ LLGLSNoTextureNoAlphaTest gls_no_texture_no_alpha_test;
+ glColor4f( 0.f, 0.f, 0.f, effective_weight );
+ gl_rect_2d_simple( width, height );
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColorInfo
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColorInfo::LLTexGlobalColorInfo()
+{
+}
+
+
+LLTexGlobalColorInfo::~LLTexGlobalColorInfo()
+{
+ for_each(mColorInfoList.begin(), mColorInfoList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColorInfo::parseXml(LLXmlTreeNode* node)
+{
+ // name attribute
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if( !node->getFastAttributeString( name_string, mName ) )
+ {
+ llwarns << "<global_color> element is missing name attribute." << llendl;
+ return FALSE;
+ }
+ // <param> sub-element
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if( child->getChildByName( "param_color" ) )
+ {
+ // <param><param_color/></param>
+ LLTexParamColorInfo* info = new LLTexParamColorInfo();
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+ mColorInfoList.push_back( info );
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColor
+//-----------------------------------------------------------------------------
+
+LLTexGlobalColor::LLTexGlobalColor( LLVOAvatar* avatar )
+ :
+ mAvatar( avatar ),
+ mInfo( NULL )
+{
+}
+
+
+LLTexGlobalColor::~LLTexGlobalColor()
+{
+ // mParamList are LLViewerVisualParam's and get deleted with ~LLCharacter()
+ //std::for_each(mParamList.begin(), mParamList.end(), DeletePointer());
+}
+
+BOOL LLTexGlobalColor::setInfo(LLTexGlobalColorInfo *info)
+{
+ llassert(mInfo == NULL);
+ mInfo = info;
+ //mID = info->mID; // No ID
+
+ LLTexGlobalColorInfo::color_info_list_t::iterator iter;
+ mParamList.reserve(mInfo->mColorInfoList.size());
+ for (iter = mInfo->mColorInfoList.begin(); iter != mInfo->mColorInfoList.end(); iter++)
+ {
+ LLTexParamColor* param_color = new LLTexParamColor( this );
+ if (!param_color->setInfo(*iter))
+ {
+ mInfo = NULL;
+ return FALSE;
+ }
+ mParamList.push_back( param_color );
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+
+LLColor4 LLTexGlobalColor::getColor()
+{
+ // Sum of color params
+ if( !mParamList.empty() )
+ {
+ LLColor4 net_color( 0.f, 0.f, 0.f, 0.f );
+
+ for( param_list_t::iterator iter = mParamList.begin();
+ iter != mParamList.end(); iter++ )
+ {
+ LLTexParamColor* param = *iter;
+ LLColor4 param_net = param->getNetColor();
+ switch( param->getOperation() )
+ {
+ case OP_ADD:
+ net_color += param_net;
+ break;
+ case OP_MULTIPLY:
+ net_color.mV[VX] *= param_net.mV[VX];
+ net_color.mV[VY] *= param_net.mV[VY];
+ net_color.mV[VZ] *= param_net.mV[VZ];
+ net_color.mV[VW] *= param_net.mV[VW];
+ break;
+ case OP_BLEND:
+ net_color = lerp(net_color, param_net, param->getWeight());
+ break;
+ default:
+ llassert(0);
+ break;
+ }
+ }
+
+ net_color.mV[VX] = llclampf( net_color.mV[VX] );
+ net_color.mV[VY] = llclampf( net_color.mV[VY] );
+ net_color.mV[VZ] = llclampf( net_color.mV[VZ] );
+ net_color.mV[VW] = llclampf( net_color.mV[VW] );
+
+ return net_color;
+ }
+ return LLColor4( 1.f, 1.f, 1.f, 1.f );
+}
+
+//-----------------------------------------------------------------------------
+// LLTexParamColorInfo
+//-----------------------------------------------------------------------------
+LLTexParamColorInfo::LLTexParamColorInfo()
+ :
+ mOperation( OP_ADD ),
+ mNumColors( 0 )
+{
+}
+
+BOOL LLTexParamColorInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert( node->hasName( "param" ) && node->getChildByName( "param_color" ) );
+
+ if (!LLViewerVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ LLXmlTreeNode* param_color_node = node->getChildByName( "param_color" );
+ if( !param_color_node )
+ {
+ return FALSE;
+ }
+
+ LLString op_string;
+ static LLStdStringHandle operation_string = LLXmlTree::addAttributeString("operation");
+ if( param_color_node->getFastAttributeString( operation_string, op_string ) )
+ {
+ LLString::toLower(op_string);
+ if ( op_string == "add" ) mOperation = OP_ADD;
+ else if ( op_string == "multiply" ) mOperation = OP_MULTIPLY;
+ else if ( op_string == "blend" ) mOperation = OP_BLEND;
+ }
+
+ mNumColors = 0;
+
+ LLColor4U color4u;
+ for (LLXmlTreeNode* child = param_color_node->getChildByName( "value" );
+ child;
+ child = param_color_node->getNextNamedChild())
+ {
+ if( (mNumColors < MAX_COLOR_VALUES) )
+ {
+ static LLStdStringHandle color_string = LLXmlTree::addAttributeString("color");
+ if( child->getFastAttributeColor4U( color_string, color4u ) )
+ {
+ mColors[ mNumColors ].setVec(color4u);
+ mNumColors++;
+ }
+ }
+ }
+ if( !mNumColors )
+ {
+ llwarns << "<param_color> is missing <value> sub-elements" << llendl;
+ return FALSE;
+ }
+
+ if( (mOperation == OP_BLEND) && (mNumColors != 1) )
+ {
+ llwarns << "<param_color> with operation\"blend\" must have exactly one <value>" << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLTexParamColor
+//-----------------------------------------------------------------------------
+LLTexParamColor::LLTexParamColor( LLTexGlobalColor* tex_global_color )
+ :
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mTexGlobalColor( tex_global_color ),
+ mTexLayer( NULL ),
+ mAvatar( tex_global_color->getAvatar() )
+{
+}
+
+LLTexParamColor::LLTexParamColor( LLTexLayer* layer )
+ :
+ mAvgDistortionVec(1.f, 1.f, 1.f),
+ mTexGlobalColor( NULL ),
+ mTexLayer( layer ),
+ mAvatar( layer->getTexLayerSet()->getAvatar() )
+{
+}
+
+
+LLTexParamColor::~LLTexParamColor()
+{
+}
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLTexParamColor::setInfo(LLTexParamColorInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mID = info->mID;
+ mInfo = info;
+
+ mAvatar->addVisualParam( this );
+ setWeight( getDefaultWeight(), FALSE );
+
+ return TRUE;
+}
+
+LLColor4 LLTexParamColor::getNetColor()
+{
+ llassert( getInfo()->mNumColors >= 1 );
+
+ F32 effective_weight = ( mAvatar && (mAvatar->getSex() & getSex()) ) ? mCurWeight : getDefaultWeight();
+
+ S32 index_last = getInfo()->mNumColors - 1;
+ F32 scaled_weight = effective_weight * index_last;
+ S32 index_start = (S32) scaled_weight;
+ S32 index_end = index_start + 1;
+ if( index_start == index_last )
+ {
+ return getInfo()->mColors[index_last];
+ }
+ else
+ {
+ F32 weight = scaled_weight - index_start;
+ const LLColor4 *start = &getInfo()->mColors[ index_start ];
+ const LLColor4 *end = &getInfo()->mColors[ index_end ];
+ return LLColor4(
+ (1.f - weight) * start->mV[VX] + weight * end->mV[VX],
+ (1.f - weight) * start->mV[VY] + weight * end->mV[VY],
+ (1.f - weight) * start->mV[VZ] + weight * end->mV[VZ],
+ (1.f - weight) * start->mV[VW] + weight * end->mV[VW] );
+ }
+}
+
+void LLTexParamColor::setWeight(F32 weight, BOOL set_by_user)
+{
+ if (mIsAnimating)
+ {
+ return;
+ }
+ F32 min_weight = getMinWeight();
+ F32 max_weight = getMaxWeight();
+ F32 new_weight = llclamp(weight, min_weight, max_weight);
+ U8 cur_u8 = F32_to_U8( mCurWeight, min_weight, max_weight );
+ U8 new_u8 = F32_to_U8( new_weight, min_weight, max_weight );
+ if( cur_u8 != new_u8)
+ {
+ mCurWeight = new_weight;
+
+ if( getInfo()->mNumColors <= 0 )
+ {
+ // This will happen when we set the default weight the first time.
+ return;
+ }
+
+ if( mAvatar->getSex() & getSex() )
+ {
+ if( mTexGlobalColor )
+ {
+ mAvatar->onGlobalColorChanged( mTexGlobalColor, set_by_user );
+ }
+ else
+ if( mTexLayer )
+ {
+ mAvatar->invalidateComposite( mTexLayer->getTexLayerSet(), set_by_user );
+ }
+ }
+// llinfos << "param " << mName << " = " << new_weight << llendl;
+ }
+}
+
+void LLTexParamColor::setAnimationTarget(F32 target_value, BOOL set_by_user)
+{
+ // set value first then set interpolating flag to ignore further updates
+ mTargetWeight = target_value;
+ setWeight(target_value, set_by_user);
+ mIsAnimating = TRUE;
+ if (mNext)
+ {
+ mNext->setAnimationTarget(target_value, set_by_user);
+ }
+}
+
+void LLTexParamColor::animate(F32 delta, BOOL set_by_user)
+{
+ if (mNext)
+ {
+ mNext->animate(delta, set_by_user);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLTexStaticImageList
+//-----------------------------------------------------------------------------
+
+// static
+LLTexStaticImageList gTexStaticImageList;
+LLStringTable LLTexStaticImageList::sImageNames(16384);
+
+LLTexStaticImageList::LLTexStaticImageList()
+ :
+ mRawBytes( 0 ),
+ mGLBytes( 0 ),
+ mTGABytes( 0 )
+{}
+
+LLTexStaticImageList::~LLTexStaticImageList()
+{
+ deleteCachedImages();
+}
+
+void LLTexStaticImageList::dumpByteCount()
+{
+ llinfos << "Avatar Static Textures " <<
+ " Raw:" << (mRawBytes / 1024) <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+}
+
+void LLTexStaticImageList::deleteCachedImages()
+{
+ if( mRawBytes || mGLBytes || mTGABytes )
+ {
+ llinfos << "Clearing Static Textures " <<
+ " Raw:" << (mRawBytes / 1024) <<
+ "KB GL:" << (mGLBytes / 1024) <<
+ "KB TGA:" << (mTGABytes / 1024) << "KB" << llendl;
+
+ //mStaticImageLists uses LLPointers, clear() will cause deletion
+
+ mStaticImageListRaw.clear();
+ mStaticImageListTGA.clear();
+ mStaticImageListGL.clear();
+
+ mRawBytes = 0;
+ mGLBytes = 0;
+ mTGABytes = 0;
+ }
+}
+
+// Note: in general, for a given image image we'll call either getImageTga(), getImageRaw() or getImageGL().
+// We call getImageTga() if the image is used as an alpha gradient.
+// Otherwise, we call getImageRaw() if we have 32 MB or less of video RAM or less and getImageGL() if we have
+// more video RAM than that.
+
+// Returns an LLImageTGA that contains the encoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageTGA* LLTexStaticImageList::getImageTGA(const LLString& file_name)
+{
+ const char *namekey = sImageNames.addString(file_name);
+ image_tga_map_t::iterator iter = mStaticImageListTGA.find(namekey);
+ if( iter != mStaticImageListTGA.end() )
+ {
+ return iter->second;
+ }
+ else
+ {
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name.c_str());
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ mStaticImageListTGA[ namekey ] = image_tga;
+ mTGABytes += image_tga->getDataSize();
+ return image_tga;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
+
+
+
+// Returns an LLImageRaw that contains the decoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageRaw* LLTexStaticImageList::getImageRaw(const LLString& file_name)
+{
+ LLPointer<LLImageRaw> image_raw;
+ const char *namekey = sImageNames.addString(file_name);
+ image_raw_map_t::iterator iter = mStaticImageListRaw.find(namekey);
+ if( iter != mStaticImageListRaw.end() )
+ {
+ image_raw = iter->second;
+ }
+ else
+ {
+ image_raw = new LLImageRaw();
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ mStaticImageListRaw[ namekey ] = image_raw;
+ mRawBytes += image_raw->getDataSize();
+ }
+ else
+ {
+ image_raw = NULL;
+ }
+ }
+
+ return image_raw;
+}
+
+// Returns a GL Image (without a backing ImageRaw) that contains the decoded data from a tga file named file_name.
+// Caches the result to speed identical subsequent requests.
+LLImageGL* LLTexStaticImageList::getImageGL(const LLString& file_name, BOOL is_mask )
+{
+ LLPointer<LLImageGL> image_gl;
+ const char *namekey = sImageNames.addString(file_name);
+
+ image_gl_map_t::iterator iter = mStaticImageListGL.find(namekey);
+ if( iter != mStaticImageListGL.end() )
+ {
+ image_gl = iter->second;
+ }
+ else
+ {
+ image_gl = new LLImageGL( FALSE );
+ LLPointer<LLImageRaw> image_raw = new LLImageRaw;
+ if( loadImageRaw( file_name, image_raw ) )
+ {
+ if( (image_raw->getComponents() == 1) && is_mask )
+ {
+ // Note: these are static, unchanging images so it's ok to assume
+ // that once an image is a mask it's always a mask.
+ image_gl->setExplicitFormat( GL_ALPHA8, GL_ALPHA );
+ }
+ image_gl->createGLTexture(0, image_raw);
+
+ image_gl->bind();
+ image_gl->setClamp(TRUE, TRUE);
+
+ mStaticImageListGL [ namekey ] = image_gl;
+ mGLBytes += (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+ }
+ else
+ {
+ image_gl = NULL;
+ }
+ }
+
+ return image_gl;
+}
+
+// Reads a .tga file, decodes it, and puts the decoded data in image_raw.
+// Returns TRUE if successful.
+BOOL LLTexStaticImageList::loadImageRaw( const LLString& file_name, LLImageRaw* image_raw )
+{
+ BOOL success = FALSE;
+ std::string path;
+ path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,file_name.c_str());
+ LLPointer<LLImageTGA> image_tga = new LLImageTGA( path );
+ if( image_tga->getDataSize() > 0 )
+ {
+ // Copy data from tga to raw.
+ success = image_tga->decode( image_raw );
+ }
+
+ return success;
+}
+
+//-----------------------------------------------------------------------------
+// LLMaskedMorph()
+//-----------------------------------------------------------------------------
+LLMaskedMorph::LLMaskedMorph( LLPolyMorphTarget *morph_target, BOOL invert ) : mMorphTarget(morph_target), mInvert(invert)
+{
+ morph_target->addPendingMorphMask();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLGradientPaletteList
+//-----------------------------------------------------------------------------
+
+LLGradientPaletteList::~LLGradientPaletteList()
+{
+ // Note: can't just call deleteAllData() because the data values are arrays.
+ for( palette_map_t::iterator iter = mPaletteMap.begin();
+ iter != mPaletteMap.end(); iter++ )
+ {
+ U8* data = iter->second;
+ delete []data;
+ }
+}
+
+void LLGradientPaletteList::initPalette(F32 domain)
+{
+ palette_map_t::iterator iter = mPaletteMap.find( domain );
+ if( iter == mPaletteMap.end() )
+ {
+ U8 *palette = new U8[512 * 4];
+ mPaletteMap[domain] = palette;
+ S32 ramp_start = 255 - llfloor(domain * 255.f);
+ S32 ramp_end = 255;
+ F32 ramp_factor = (ramp_end == ramp_start) ? 0.f : (255.f / ((F32)ramp_end - (F32)ramp_start));
+
+ //FIXME: move conditionals outside of loop, since this really is just a sequential process
+ for (S32 i = 0; i < 512; i++)
+ {
+ palette[(i * 4) + 1] = 0;
+ palette[(i * 4) + 2] = 0;
+ if (i <= ramp_start)
+ {
+ palette[(i * 4)] = 0;
+ palette[(i * 4) + 3] = 0;
+ }
+ else if (i < ramp_end)
+ {
+ palette[(i * 4)] = llfloor(((F32)i - (F32)ramp_start) * ramp_factor);
+ palette[(i * 4) + 3] = llfloor(((F32)i - (F32)ramp_start) * ramp_factor);
+ }
+ else
+ {
+ palette[(i * 4)] = 255;
+ palette[(i * 4) + 3] = 255;
+ }
+ }
+ }
+}
+
+void LLGradientPaletteList::setHardwarePalette( F32 domain, F32 effective_weight )
+{
+ palette_map_t::iterator iter = mPaletteMap.find( domain );
+ if( iter != mPaletteMap.end() )
+ {
+ U8* palette = iter->second;
+ set_palette( palette + llfloor(effective_weight * (255.f * (1.f - domain))) * 4);
+ }
+ else
+ {
+ llwarns << "LLGradientPaletteList::setHardwarePalette() missing domain " << domain << llendl;
+ }
+}
diff --git a/indra/newview/lltexlayer.h b/indra/newview/lltexlayer.h
new file mode 100644
index 0000000000..5c1a11cdf1
--- /dev/null
+++ b/indra/newview/lltexlayer.h
@@ -0,0 +1,566 @@
+/**
+ * @file lltexlayer.h
+ * @brief A texture layer. Used for avatars.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXLAYER_H
+#define LL_LLTEXLAYER_H
+
+#include <deque>
+#include "llassetstorage.h"
+#include "lldynamictexture.h"
+#include "llrect.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "llviewerimage.h"
+#include "llviewervisualparam.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+#include "v4color.h"
+#include "llfloater.h"
+
+class LLTexLayerSetInfo;
+class LLTexLayerSet;
+class LLTexLayerInfo;
+class LLTexLayer;
+class LLImageGL;
+class LLImageTGA;
+class LLTexGlobalColorInfo;
+class LLTexLayerParamAlphaInfo;
+class LLTexLayerParamAlpha;
+class LLTexParamColorInfo;
+class LLTexParamColor;
+class LLPolyMesh;
+class LLXmlTreeNode;
+class LLImageRaw;
+class LLPolyMorphTarget;
+
+class LLTextureCtrl;
+class LLVOAvatar;
+
+
+enum EColorOperation
+{
+ OP_ADD = 0,
+ OP_MULTIPLY = 1,
+ OP_BLEND = 2,
+ OP_COUNT = 3 // Number of operations
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlphaInfo
+//-----------------------------------------------------------------------------
+class LLTexLayerParamAlphaInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexLayerParamAlpha;
+public:
+ LLTexLayerParamAlphaInfo();
+ /*virtual*/ ~LLTexLayerParamAlphaInfo() {};
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mStaticImageFileName;
+ BOOL mMultiplyBlend;
+ BOOL mSkipIfZeroWeight;
+ F32 mDomain;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexParamColorInfo
+//-----------------------------------------------------------------------------
+class LLTexParamColorInfo : public LLViewerVisualParamInfo
+{
+ friend class LLTexParamColor;
+
+public:
+ LLTexParamColorInfo();
+ virtual ~LLTexParamColorInfo() {};
+ BOOL parseXml( LLXmlTreeNode* node );
+
+protected:
+ enum { MAX_COLOR_VALUES = 20 };
+ EColorOperation mOperation;
+ LLColor4 mColors[MAX_COLOR_VALUES];
+ S32 mNumColors;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColorInfo
+//-----------------------------------------------------------------------------
+class LLTexGlobalColorInfo
+{
+ friend class LLTexGlobalColor;
+public:
+ LLTexGlobalColorInfo();
+ ~LLTexGlobalColorInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ typedef std::vector<LLTexParamColorInfo *> color_info_list_t;
+ color_info_list_t mColorInfoList;
+ LLString mName;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetInfo
+// Containes shared layer set data
+//-----------------------------------------------------------------------------
+class LLTexLayerSetInfo
+{
+ friend class LLTexLayerSet;
+public:
+ LLTexLayerSetInfo();
+ ~LLTexLayerSetInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mBodyRegion;
+ S32 mWidth;
+ S32 mHeight;
+ LLString mStaticAlphaFileName;
+ BOOL mClearAlpha; // Set alpha to 1 for this layerset (if there is no mStaticAlphaFileName)
+
+ typedef std::vector<LLTexLayerInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerInfo
+//-----------------------------------------------------------------------------
+enum ERenderPass
+{
+ RP_COLOR,
+ RP_BUMP,
+ RP_SHINE
+};
+
+class LLTexLayerInfo
+{
+ friend class LLTexLayer;
+public:
+ LLTexLayerInfo();
+ ~LLTexLayerInfo();
+
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mName;
+
+ BOOL mWriteAllChannels; // Don't use masking. Just write RGBA into buffer,
+ ERenderPass mRenderPass;
+
+ LLString mGlobalColor;
+ LLColor4 mFixedColor;
+
+ S32 mLocalTexture;
+ LLString mStaticImageFileName;
+ BOOL mStaticImageIsMask;
+ BOOL mUseLocalTextureAlphaOnly; // Ignore RGB channels from the input texture. Use alpha as a mask
+
+ typedef std::vector<std::pair<LLString,BOOL> > morph_name_list_t;
+ morph_name_list_t mMorphNameList;
+
+ typedef std::vector<LLTexParamColorInfo*> color_info_list_t;
+ color_info_list_t mColorInfoList;
+
+ typedef std::vector<LLTexLayerParamAlphaInfo*> alpha_info_list_t;
+ alpha_info_list_t mAlphaInfoList;
+
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSetBuffer
+// The composite image that a LLTexLayerSet writes to. Each LLTexLayerSet has one.
+//-----------------------------------------------------------------------------
+class LLTexLayerSetBuffer : public LLDynamicTexture
+{
+public:
+ LLTexLayerSetBuffer( LLTexLayerSet* owner, S32 width, S32 height, BOOL has_bump );
+ virtual ~LLTexLayerSetBuffer();
+
+ virtual void preRender(BOOL clear_depth);
+ virtual void postRender(BOOL success);
+ virtual BOOL render();
+ BOOL updateImmediate();
+ virtual void bindTexture();
+ void bindBumpTexture( U32 stage );
+ BOOL isInitialized() { return mInitialized; } // Initialized here means that we've done at least one render
+ BOOL needsRender();
+ void requestUpdate();
+ void requestUpload();
+ void cancelUpload();
+ BOOL uploadPending() { return mUploadPending; }
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ void readBackAndUpload(U8* baked_bump_data);
+ static void onTextureUploadComplete( const LLUUID& uuid,
+ void* userdata,
+ S32 result);
+ static void dumpTotalByteCount();
+
+private:
+ void pushProjection();
+ void popProjection();
+
+private:
+ BOOL mNeedsUpdate;
+ BOOL mNeedsUpload;
+ BOOL mUploadPending;
+ LLUUID mUploadID; // Identifys the current upload process (null if none). Used to avoid overlaps (eg, when the user rapidly makes two changes outside of Face Edit)
+ LLTexLayerSet* mTexLayerSet;
+ BOOL mInitialized;
+ LLGLuint mBumpTexName; // zero if none
+
+ static S32 sGLByteCount;
+ static S32 sGLBumpByteCount;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerSet
+// An ordered set of texture layers that get composited into a single texture.
+//-----------------------------------------------------------------------------
+class LLTexLayerSet
+{
+public:
+ LLTexLayerSet( LLVOAvatar* avatar );
+ ~LLTexLayerSet();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexLayerSetInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerSetInfo *info);
+
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ BOOL renderBump( S32 x, S32 y, S32 width,S32 height );
+ BOOL isBodyRegion( const char* region ) { return mInfo->mBodyRegion == region; }
+ LLTexLayerSetBuffer* getComposite();
+ void requestUpdate();
+ void requestUpload();
+ void cancelUpload();
+ LLVOAvatar* getAvatar() { return mAvatar; }
+ void updateComposite();
+ BOOL isLocalTextureDataAvailable();
+ BOOL isLocalTextureDataFinal();
+ void createComposite();
+ void destroyComposite();
+ void setUpdatesEnabled( BOOL b );
+ BOOL getUpdatesEnabled() { return mUpdatesEnabled; }
+ void deleteCaches();
+ void gatherAlphaMasks(U8 *data, S32 width, S32 height);
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+ const LLString& getBodyRegion() { return mInfo->mBodyRegion; }
+ BOOL hasComposite() { return (mComposite != NULL); }
+ void setBump( BOOL b ) { mHasBump = b; }
+ BOOL hasBump() { return mHasBump; }
+
+public:
+ static BOOL sHasCaches;
+
+protected:
+ typedef std::vector<LLTexLayer *> layer_list_t;
+ layer_list_t mLayerList;
+ LLTexLayerSetBuffer* mComposite;
+ LLVOAvatar* mAvatar;
+ BOOL mUpdatesEnabled;
+ BOOL mHasBump;
+
+ LLTexLayerSetInfo *mInfo;
+};
+
+//-----------------------------------------------------------------------------
+// LLMaskedMorph
+//-----------------------------------------------------------------------------
+
+class LLMaskedMorph
+{
+public:
+ LLMaskedMorph( LLPolyMorphTarget *morph_target, BOOL invert );
+
+public:
+ LLPolyMorphTarget *mMorphTarget;
+ BOOL mInvert;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayer
+// A single texture layer
+//-----------------------------------------------------------------------------
+class LLTexLayer
+{
+public:
+ LLTexLayer( LLTexLayerSet* layer_set );
+ ~LLTexLayer();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexLayerInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerInfo *info);
+
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ void requestUpdate();
+ LLTexLayerSet* getTexLayerSet() { return mTexLayerSet; }
+
+ const std::string& getName() { return mInfo->mName; }
+
+ void addMaskedMorph(LLPolyMorphTarget* morph_target, BOOL invert);
+ void deleteCaches();
+ U8* getAlphaData();
+ void applyMorphMask(U8* tex_data, S32 width, S32 height, S32 num_components);
+
+ void invalidateMorphMasks();
+ ERenderPass getRenderPass() { return mInfo->mRenderPass; }
+ const LLString& getGlobalColor() { return mInfo->mGlobalColor; }
+ BOOL findNetColor( LLColor4* color );
+ BOOL renderImageRaw( U8* in_data, S32 in_width, S32 in_height, S32 in_components, S32 width, S32 height, BOOL is_mask );
+ BOOL renderAlphaMasks( S32 x, S32 y, S32 width, S32 height, LLColor4* colorp );
+ BOOL hasAlphaParams() { return (!mParamAlphaList.empty());}
+
+protected:
+ BOOL loadStaticImageRaw();
+
+protected:
+ LLTexLayerSet* mTexLayerSet;
+ LLPointer<LLImageRaw> mStaticImageRaw;
+
+ // Layers can have either mParamColorList, mGlobalColor, or mFixedColor. They are looked for in that order.
+ typedef std::vector<LLTexParamColor *> color_list_t;
+ color_list_t mParamColorList;
+ // mGlobalColor name stored in mInfo
+ // mFixedColor value stored in mInfo
+
+ typedef std::vector<LLTexLayerParamAlpha *> alpha_list_t;
+ alpha_list_t mParamAlphaList;
+
+
+ typedef std::deque<LLMaskedMorph> morph_list_t;
+ morph_list_t mMaskedMorphs;
+ typedef std::map<U32, U8*> alpha_cache_t;
+ alpha_cache_t mAlphaCache;
+ BOOL mMorphMasksValid;
+ BOOL mStaticImageInvalid;
+
+ LLTexLayerInfo *mInfo;
+};
+
+//-----------------------------------------------------------------------------
+// LLTexLayerParamAlpha
+//-----------------------------------------------------------------------------
+class LLTexLayerParamAlpha : public LLViewerVisualParam
+{
+public:
+ LLTexLayerParamAlpha( LLTexLayer* layer );
+ /*virtual*/ ~LLTexLayerParamAlpha();
+
+ // Special: These functions are overridden by child classes
+ LLTexLayerParamAlphaInfo* getInfo() const { return (LLTexLayerParamAlphaInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexLayerParamAlphaInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL set_by_user);
+ /*virtual*/ void animate(F32 delta, BOOL set_by_user);
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector3(1.f, 1.f, 1.f);}
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ BOOL render( S32 x, S32 y, S32 width, S32 height );
+ BOOL getSkip();
+ void deleteCaches();
+ LLTexLayer* getTexLayer() { return mTexLayer; }
+ BOOL getMultiplyBlend() { return getInfo()->mMultiplyBlend; }
+
+protected:
+ LLPointer<LLImageGL> mCachedProcessedImageGL;
+ LLTexLayer* mTexLayer;
+ LLPointer<LLImageTGA> mStaticImageTGA;
+ LLPointer<LLImageRaw> mStaticImageRaw;
+ BOOL mNeedsCreateTexture;
+ BOOL mStaticImageInvalid;
+ LLVector3 mAvgDistortionVec;
+ F32 mCachedEffectiveWeight;
+
+public:
+ // Global list of instances for gathering statistics
+ static void dumpCacheByteCount();
+ static void getCacheByteCount( S32* gl_bytes );
+
+ typedef std::list< LLTexLayerParamAlpha* > param_alpha_ptr_list_t;
+ static param_alpha_ptr_list_t sInstances;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexGlobalColor
+//-----------------------------------------------------------------------------
+class LLTexGlobalColor
+{
+public:
+ LLTexGlobalColor( LLVOAvatar* avatar );
+ ~LLTexGlobalColor();
+
+ //BOOL parseData(LLXmlTreeNode* node);
+ LLTexGlobalColorInfo* getInfo() const { return mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexGlobalColorInfo *info);
+
+ void requstUpdate();
+ LLVOAvatar* getAvatar() { return mAvatar; }
+ LLColor4 getColor();
+ const std::string& getName() { return mInfo->mName; }
+
+protected:
+ typedef std::vector<LLTexParamColor *> param_list_t;
+ param_list_t mParamList;
+ LLVOAvatar* mAvatar;
+
+ LLTexGlobalColorInfo *mInfo;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLTexParamColor
+//-----------------------------------------------------------------------------
+class LLTexParamColor : public LLViewerVisualParam
+{
+public:
+ LLTexParamColor( LLTexGlobalColor* tex_color );
+ LLTexParamColor( LLTexLayer* layer );
+ /* virtual */ ~LLTexParamColor();
+
+ // Special: These functions are overridden by child classes
+ LLTexParamColorInfo* getInfo() const { return (LLTexParamColorInfo*)mInfo; }
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLTexParamColorInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+ /*virtual*/ void apply( ESex avatar_sex ) {}
+ /*virtual*/ void setWeight(F32 weight, BOOL set_by_user);
+ /*virtual*/ void setAnimationTarget(F32 target_value, BOOL set_by_user);
+ /*virtual*/ void animate(F32 delta, BOOL set_by_user);
+
+
+ // LLViewerVisualParam Virtual functions
+ /*virtual*/ F32 getTotalDistortion() { return 1.f; }
+ /*virtual*/ const LLVector3& getAvgDistortion() { return mAvgDistortionVec; }
+ /*virtual*/ F32 getMaxDistortion() { return 3.f; }
+ /*virtual*/ LLVector3 getVertexDistortion(S32 index, LLPolyMesh *poly_mesh) { return LLVector3(1.f, 1.f, 1.f); }
+ /*virtual*/ const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return &mAvgDistortionVec;};
+ /*virtual*/ const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **poly_mesh) { index = 0; poly_mesh = NULL; return NULL;};
+
+ // New functions
+ LLColor4 getNetColor();
+ EColorOperation getOperation() const { return getInfo()->mOperation; }
+
+
+protected:
+ LLVector3 mAvgDistortionVec;
+ LLTexGlobalColor* mTexGlobalColor; // either has mTexGlobalColor or mTexLayer as its parent
+ LLTexLayer* mTexLayer;
+ LLVOAvatar* mAvatar; // redundant, but simplifies the code
+};
+
+//-----------------------------------------------------------------------------
+// LLTexStaticImageList
+//-----------------------------------------------------------------------------
+
+class LLTexStaticImageList
+{
+public:
+ LLTexStaticImageList();
+ ~LLTexStaticImageList();
+
+ LLImageRaw* getImageRaw( const LLString& file_name );
+ LLImageGL* getImageGL( const LLString& file_name, BOOL is_mask );
+ LLImageTGA* getImageTGA( const LLString& file_name );
+
+ void deleteCachedImages();
+ void dumpByteCount();
+
+private:
+ BOOL loadImageRaw( const LLString& file_name, LLImageRaw* image_raw );
+
+private:
+ static LLStringTable sImageNames;
+
+ typedef std::map< const char *, LLPointer<LLImageRaw> > image_raw_map_t;
+ typedef std::map< const char *, LLPointer<LLImageGL> > image_gl_map_t;
+ typedef std::map< const char *, LLPointer<LLImageTGA> > image_tga_map_t;
+ image_raw_map_t mStaticImageListRaw;
+ image_gl_map_t mStaticImageListGL;
+ image_tga_map_t mStaticImageListTGA;
+
+public:
+ S32 mRawBytes;
+ S32 mGLBytes;
+ S32 mTGABytes;
+};
+
+
+//-----------------------------------------------------------------------------
+// LLGradientPaletteList
+// A static set of ramp grayscale palettes. The "effective_weight" is used
+// to determine the x position of the ramp (offset)
+//
+// "Domain" isn't really the right word. It refers to the width of the
+// ramp portion of the function that relates input and output pixel values.
+// A domain of 0 gives a step function.
+//
+// | /----------------
+// O| / |
+// u| / |
+// t| / |
+// p|------------------/ |
+// u| | |
+// t|<---------------->|<-->|
+// | "offset" "domain"
+// |
+// --+---Input--------------------------------
+// |
+//-----------------------------------------------------------------------------
+class LLGradientPaletteList
+{
+public:
+ LLGradientPaletteList() {}
+ ~LLGradientPaletteList();
+
+ void initPalette(F32 domain);
+ void setHardwarePalette(F32 domain, F32 effective_weight);
+
+private:
+ typedef std::map<F32, U8*> palette_map_t;
+ palette_map_t mPaletteMap;
+};
+
+// Used by LLTexLayerSetBuffer for a callback.
+class LLBakedUploadData
+{
+public:
+ LLBakedUploadData( LLVOAvatar* avatar, LLTexLayerSetBuffer* layerset_buffer );
+ ~LLBakedUploadData() {}
+
+ LLUUID mID;
+ LLVOAvatar* mAvatar;
+ LLTexLayerSetBuffer* mLayerSetBuffer;
+ LLUUID mWearableAssets[WT_COUNT];
+};
+
+extern LLTexStaticImageList gTexStaticImageList;
+
+
+#endif // LL_LLTEXLAYER_H
diff --git a/indra/newview/lltexturectrl.cpp b/indra/newview/lltexturectrl.cpp
new file mode 100644
index 0000000000..8a098f4482
--- /dev/null
+++ b/indra/newview/lltexturectrl.cpp
@@ -0,0 +1,1368 @@
+/**
+ * @file lltexturectrl.cpp
+ * @author Richard Nelson, James Cook
+ * @brief LLTextureCtrl class implementation including related functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltexturectrl.h"
+
+#include "llagent.h"
+#include "llviewerimagelist.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llbutton.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llviewerimage.h"
+#include "llfolderview.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "lllineeditor.h"
+#include "llui.h"
+#include "llviewerinventory.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llassetstorage.h"
+#include "lltextbox.h"
+#include "llresizehandle.h"
+#include "llscrollcontainer.h"
+#include "lltoolmgr.h"
+#include "lltoolpipette.h"
+
+#include "lltool.h"
+#include "llviewerwindow.h"
+#include "llviewerobject.h"
+#include "llviewercontrol.h"
+#include "llglheaders.h"
+#include "llvieweruictrlfactory.h"
+
+
+static const S32 CLOSE_BTN_WIDTH = 100;
+const S32 PIPETTE_BTN_WIDTH = 32;
+static const S32 HPAD = 4;
+static const S32 VPAD = 4;
+static const S32 LINE = 16;
+static const S32 SMALL_BTN_WIDTH = 64;
+static const S32 TEX_PICKER_MIN_WIDTH =
+ (HPAD +
+ CLOSE_BTN_WIDTH +
+ HPAD +
+ CLOSE_BTN_WIDTH +
+ HPAD +
+ SMALL_BTN_WIDTH +
+ HPAD +
+ SMALL_BTN_WIDTH +
+ HPAD +
+ 30 +
+ RESIZE_HANDLE_WIDTH * 2);
+static const S32 CLEAR_BTN_WIDTH = 50;
+static const S32 TEX_PICKER_MIN_HEIGHT = 290;
+static const S32 FOOTER_HEIGHT = 100;
+static const S32 BORDER_PAD = HPAD;
+static const S32 TEXTURE_INVENTORY_PADDING = 30;
+static const F32 CONTEXT_CONE_IN_ALPHA = 0.0f;
+static const F32 CONTEXT_CONE_OUT_ALPHA = 0.35f;
+
+
+//static const char CURRENT_IMAGE_NAME[] = "Current Texture";
+//static const char WHITE_IMAGE_NAME[] = "Blank Texture";
+//static const char NO_IMAGE_NAME[] = "None";
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLFloaterTexturePicker
+
+class LLFloaterTexturePicker : public LLFloater
+{
+public:
+ LLFloaterTexturePicker(
+ LLTextureCtrl* owner,
+ const LLRect& rect,
+ const std::string& label,
+ PermissionMask immediate_filter_perm_mask,
+ PermissionMask non_immediate_filter_perm_mask,
+ BOOL can_apply_immediately);
+ virtual ~LLFloaterTexturePicker();
+
+ // LLView overrides
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+ virtual void draw();
+
+ // LLFloater overrides
+ virtual void onClose(bool app_quitting);
+
+ // New functions
+ void setImageID( const LLUUID& image_asset_id);
+ void updateImageStats();
+ const LLUUID& getAssetID() { return mImageAssetID; }
+ const LLUUID& findItemID(const LLUUID& asset_id, BOOL copyable_only);
+ void setCanApplyImmediately(BOOL b);
+
+ void setDirty( BOOL b ) { mIsDirty = b; }
+ BOOL isDirty() { return mIsDirty; }
+ void setActive( BOOL active );
+
+ LLTextureCtrl* getOwner() const { return mOwner; }
+ void setOwner(LLTextureCtrl* owner) { mOwner = owner; }
+
+ void stopUsingPipette();
+ PermissionMask getFilterPermMask();
+ void updateFilterPermMask();
+ void commitIfImmediateSet();
+
+ static void onBtnSetToDefault( void* userdata );
+ static void onBtnSelect( void* userdata );
+ static void onBtnCancel( void* userdata );
+ static void onBtnPipette( void* userdata );
+ //static void onBtnRevert( void* userdata );
+ static void onBtnWhite( void* userdata );
+ static void onBtnNone( void* userdata );
+ static void onBtnClear( void* userdata );
+ static void onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data);
+ static void onShowFolders(LLUICtrl* ctrl, void* userdata);
+ static void onApplyImmediateCheck(LLUICtrl* ctrl, void* userdata);
+ static void onSearchEdit(const LLString& search_string, void* user_data );
+ static void onTextureSelect( const LLTextureEntry& te, void *data );
+
+protected:
+ LLPointer<LLViewerImage> mTexturep;
+ LLTextureCtrl* mOwner;
+
+ LLUUID mImageAssetID; // Currently selected texture
+
+ LLUUID mWhiteImageAssetID;
+ LLUUID mSpecialCurrentImageAssetID; // Used when the asset id has no corresponding texture in the user's inventory.
+ LLUUID mOriginalImageAssetID;
+
+ LLTextBox* mTentativeLabel;
+ LLTextBox* mResolutionLabel;
+
+ LLString mPendingName;
+ BOOL mIsDirty;
+ BOOL mActive;
+
+ LLSearchEditor* mSearchEdit;
+ LLInventoryPanel* mInventoryPanel;
+ PermissionMask mImmediateFilterPermMask;
+ PermissionMask mNonImmediateFilterPermMask;
+ BOOL mCanApplyImmediately;
+ BOOL mNoCopyTextureSelected;
+ F32 mContextConeOpacity;
+};
+
+LLFloaterTexturePicker::LLFloaterTexturePicker(
+ LLTextureCtrl* owner,
+ const LLRect& rect,
+ const std::string& label,
+ PermissionMask immediate_filter_perm_mask,
+ PermissionMask non_immediate_filter_perm_mask,
+ BOOL can_apply_immediately)
+ :
+ LLFloater( "texture picker",
+ rect,
+ LLString( "Pick: " ) + label,
+ TRUE,
+ TEX_PICKER_MIN_WIDTH, TEX_PICKER_MIN_HEIGHT ),
+ mOwner( owner ),
+ mImageAssetID( owner->getImageAssetID() ),
+ mWhiteImageAssetID( gSavedSettings.getString( "UIImgWhiteUUID" ) ),
+ mOriginalImageAssetID(owner->getImageAssetID()),
+ mTentativeLabel(NULL),
+ mResolutionLabel(NULL),
+ mIsDirty( FALSE ),
+ mActive( TRUE ),
+ mSearchEdit(NULL),
+ mImmediateFilterPermMask(immediate_filter_perm_mask),
+ mNonImmediateFilterPermMask(non_immediate_filter_perm_mask),
+ mContextConeOpacity(0.f)
+{
+ gUICtrlFactory->buildFloater(this,"floater_texture_ctrl.xml");
+
+ mTentativeLabel = LLUICtrlFactory::getTextBoxByName(this,"Multiple");
+
+ mResolutionLabel = LLUICtrlFactory::getTextBoxByName(this,"unknown");
+
+
+ childSetAction("Default",LLFloaterTexturePicker::onBtnSetToDefault,this);
+ childSetAction("None", LLFloaterTexturePicker::onBtnNone,this);
+ childSetAction("Blank", LLFloaterTexturePicker::onBtnWhite,this);
+
+
+ childSetCommitCallback("show_folders_check", onShowFolders, this);
+ childSetVisible("show_folders_check", FALSE);
+
+ mSearchEdit = (LLSearchEditor*)getCtrlByNameAndType("inventory search editor", WIDGET_TYPE_SEARCH_EDITOR);
+ mSearchEdit->setSearchCallback(onSearchEdit, this);
+
+ mInventoryPanel = (LLInventoryPanel*)this->getCtrlByNameAndType("inventory panel", WIDGET_TYPE_INVENTORY_PANEL);
+
+ if(mInventoryPanel)
+ {
+ U32 filter_types = 0x0;
+ filter_types |= 0x1 << LLInventoryType::IT_TEXTURE;
+ filter_types |= 0x1 << LLInventoryType::IT_SNAPSHOT;
+
+ mInventoryPanel->setAutoSelectOverride(true);
+ mInventoryPanel->setFilterTypes(filter_types);
+ mInventoryPanel->setFilterPermMask(getFilterPermMask());
+ mInventoryPanel->setSelectCallback(onSelectionChange, this);
+ mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ mInventoryPanel->setAllowMultiSelect(FALSE);
+ // Commented out to stop opening all folders with textures
+ // mInventoryPanel->openDefaultFolderForType(LLAssetType::AT_TEXTURE);
+
+ // don't put keyboard focus on selected item, because the selection callback
+ // will assume that this was user input
+ mInventoryPanel->setSelection(findItemID(mImageAssetID, FALSE), TAKE_FOCUS_NO);
+ }
+
+ mCanApplyImmediately = can_apply_immediately;
+ mNoCopyTextureSelected = FALSE;
+
+ childSetValue("apply_immediate_check", gSavedSettings.getBOOL("ApplyTextureImmediately"));
+ childSetCommitCallback("apply_immediate_check", onApplyImmediateCheck, this);
+
+ if (!can_apply_immediately)
+ {
+ childSetEnabled("show_folders_check", FALSE);
+ }
+
+ childSetAction("Pipette", LLFloaterTexturePicker::onBtnPipette,this);
+ childSetAction("Cancel", LLFloaterTexturePicker::onBtnCancel,this);
+ childSetAction("Select", LLFloaterTexturePicker::onBtnSelect,this);
+
+ // update permission filter once UI is fully initialized
+ updateFilterPermMask();
+
+ setCanMinimize(FALSE);
+}
+
+LLFloaterTexturePicker::~LLFloaterTexturePicker()
+{
+}
+
+void LLFloaterTexturePicker::setImageID(const LLUUID& image_id)
+{
+ if( mImageAssetID != image_id && mActive)
+ {
+ mNoCopyTextureSelected = FALSE;
+ mIsDirty = TRUE;
+ mImageAssetID = image_id;
+ LLUUID item_id = findItemID(mImageAssetID, FALSE);
+ if (item_id.isNull())
+ {
+ mInventoryPanel->clearSelection();
+ }
+ else
+ {
+ LLInventoryItem* itemp = gInventory.getItem(image_id);
+ if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // no copy texture
+ childSetValue("apply_immediate_check", FALSE);
+ mNoCopyTextureSelected = TRUE;
+ }
+ mInventoryPanel->setSelection(item_id, TAKE_FOCUS_NO);
+ }
+ }
+}
+
+void LLFloaterTexturePicker::setActive( BOOL active )
+{
+ if (!active && childGetValue("Pipette").asBoolean())
+ {
+ stopUsingPipette();
+ }
+ mActive = active;
+}
+
+void LLFloaterTexturePicker::setCanApplyImmediately(BOOL b)
+{
+ mCanApplyImmediately = b;
+ if (!mCanApplyImmediately)
+ {
+ childSetValue("apply_immediate_check", FALSE);
+ }
+ updateFilterPermMask();
+}
+
+void LLFloaterTexturePicker::stopUsingPipette()
+{
+ if (gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette)
+ {
+ gToolMgr->clearTransientTool();
+ }
+}
+
+void LLFloaterTexturePicker::updateImageStats()
+{
+ if (mTexturep.notNull())
+ {
+ //RN: have we received header data for this image?
+ if (mTexturep->getWidth(0) > 0 && mTexturep->getHeight(0) > 0)
+ {
+ std::ostringstream formatted_dims;
+ formatted_dims << llformat("Dimensions: %d x %d", mTexturep->getWidth(0),mTexturep->getHeight(0));
+ mResolutionLabel->setText(formatted_dims.str());
+ }
+ else
+ {
+ mResolutionLabel->setText("Dimensions: unknown");
+ }
+ if (gAgent.isGodlike())
+ {
+ LLString tstring = "Pick: " + mTexturep->getID().getString();
+ setTitle(tstring);
+ }
+ }
+}
+
+// virtual
+BOOL LLFloaterTexturePicker::handleDragAndDrop(
+ S32 x, S32 y, MASK mask,
+ BOOL drop,
+ EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (cargo_type == DAD_TEXTURE)
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ PermissionMask item_perm_mask = 0;
+ if (copy) item_perm_mask |= PERM_COPY;
+ if (mod) item_perm_mask |= PERM_MODIFY;
+ if (xfer) item_perm_mask |= PERM_TRANSFER;
+
+ PermissionMask filter_perm_mask = getFilterPermMask();
+ if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
+ {
+ if (drop)
+ {
+ setImageID( item->getAssetUUID() );
+ commitIfImmediateSet();
+ }
+
+ *accept = ACCEPT_YES_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLFloaterTexturePicker " << getName() << llendl;
+
+ return handled;
+}
+
+// virtual
+void LLFloaterTexturePicker::onClose(bool app_quitting)
+{
+ if (mOwner)
+ {
+ mOwner->onFloaterClose();
+ }
+ stopUsingPipette();
+ destroy();
+}
+
+// virtual
+void LLFloaterTexturePicker::draw()
+{
+ if (mOwner)
+ {
+ // draw cone of context pointing back to texture swatch
+ LLRect owner_rect;
+ mOwner->localRectToOtherView(mOwner->getLocalRect(), &owner_rect, this);
+ LLRect local_rect = getLocalRect();
+ if (gFocusMgr.childHasKeyboardFocus(this) && mOwner->isInVisibleChain() && mContextConeOpacity > 0.001f)
+ {
+ LLGLSNoTexture no_texture;
+ LLGLEnable(GL_CULL_FACE);
+ glBegin(GL_QUADS);
+ {
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mLeft, owner_rect.mTop);
+ glVertex2i(owner_rect.mRight, owner_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mTop);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ glVertex2i(owner_rect.mLeft, owner_rect.mTop);
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mTop);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mRight, owner_rect.mTop);
+ glVertex2i(owner_rect.mRight, owner_rect.mBottom);
+
+
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_OUT_ALPHA * mContextConeOpacity);
+ glVertex2i(local_rect.mLeft, local_rect.mBottom);
+ glVertex2i(local_rect.mRight, local_rect.mBottom);
+ glColor4f(0.f, 0.f, 0.f, CONTEXT_CONE_IN_ALPHA * mContextConeOpacity);
+ glVertex2i(owner_rect.mRight, owner_rect.mBottom);
+ glVertex2i(owner_rect.mLeft, owner_rect.mBottom);
+ }
+ glEnd();
+ }
+ }
+
+ if (gFocusMgr.childHasMouseCapture(mDragHandle))
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 1.f, LLCriticalDamp::getInterpolant(0.1f));
+ }
+ else
+ {
+ mContextConeOpacity = lerp(mContextConeOpacity, 0.f, LLCriticalDamp::getInterpolant(0.2f));
+ }
+
+ updateImageStats();
+
+ // if we're inactive, gray out "apply immediate" checkbox
+ childSetEnabled("show_folders_check", mActive && mCanApplyImmediately && !mNoCopyTextureSelected);
+ childSetEnabled("Select", mActive);
+ childSetEnabled("Pipette", gToolMgr != NULL && mActive);
+ childSetValue("Pipette", gToolMgr && gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolPipette);
+
+ //RN: reset search bar to reflect actual search query (all caps, for example)
+ mSearchEdit->setText(mInventoryPanel->getFilterSubString());
+
+ //BOOL allow_copy = FALSE;
+ if( getVisible() && mOwner)
+ {
+ mTexturep = NULL;
+ if(mImageAssetID.notNull())
+ {
+ mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ }
+
+ if (mTentativeLabel)
+ {
+ mTentativeLabel->setVisible( FALSE );
+ }
+
+ childSetEnabled("Default", mImageAssetID != mOwner->getDefaultImageAssetID());
+ childSetEnabled("Blank", mImageAssetID != mWhiteImageAssetID );
+ childSetEnabled("None", mOwner->getAllowNoTexture() && !mImageAssetID.isNull() );
+
+ LLFloater::draw();
+
+ if( isMinimized() )
+ {
+ return;
+ }
+
+ // Border
+ LLRect border( BORDER_PAD,
+ mRect.getHeight() - LLFLOATER_HEADER_SIZE - BORDER_PAD,
+ ((TEX_PICKER_MIN_WIDTH / 2) - TEXTURE_INVENTORY_PADDING - HPAD) - BORDER_PAD,
+ BORDER_PAD + FOOTER_HEIGHT + (mRect.getHeight() - TEX_PICKER_MIN_HEIGHT));
+ gl_rect_2d( border, LLColor4::black, FALSE );
+
+
+ // Interior
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ if( mTexturep )
+ {
+ if( mTexturep->getComponents() == 4 )
+ {
+ gl_rect_2d_checkerboard( interior );
+ }
+
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep );
+
+ // Pump the priority
+ mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+
+ // Draw Tentative Label over the image
+ if( mOwner->getTentative() && !mIsDirty )
+ {
+ mTentativeLabel->setVisible( TRUE );
+ drawChild(mTentativeLabel);
+ }
+ }
+ else
+ {
+ gl_rect_2d( interior, LLColor4::grey, TRUE );
+
+ // Draw X
+ gl_draw_x(interior, LLColor4::black );
+ }
+ }
+}
+
+// static
+/*
+void LLFloaterTexturePicker::onSaveAnotherCopyDialog( S32 option, void* userdata )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if( 0 == option )
+ {
+ self->copyToInventoryFinal();
+ }
+}
+*/
+
+const LLUUID& LLFloaterTexturePicker::findItemID(const LLUUID& asset_id, BOOL copyable_only)
+{
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLAssetIDMatches asset_id_matches(asset_id);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cats,
+ items,
+ LLInventoryModel::INCLUDE_TRASH,
+ asset_id_matches);
+
+ if (items.count())
+ {
+ // search for copyable version first
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* itemp = items[i];
+ LLPermissions item_permissions = itemp->getPermissions();
+ if (item_permissions.allowCopyBy(gAgent.getID(), gAgent.getGroupID()))
+ {
+ return itemp->getUUID();
+ }
+ }
+ // otherwise just return first instance, unless copyable requested
+ if (copyable_only)
+ {
+ return LLUUID::null;
+ }
+ else
+ {
+ return items[0]->getUUID();
+ }
+ }
+
+ return LLUUID::null;
+}
+
+PermissionMask LLFloaterTexturePicker::getFilterPermMask()
+{
+ bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
+ return apply_immediate ? mImmediateFilterPermMask : mNonImmediateFilterPermMask;
+}
+
+void LLFloaterTexturePicker::commitIfImmediateSet()
+{
+ bool apply_immediate = childGetValue("apply_immediate_check").asBoolean();
+ if (!mNoCopyTextureSelected && apply_immediate && mOwner)
+ {
+ mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CHANGE);
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onBtnSetToDefault(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if (self->mOwner)
+ {
+ self->setImageID( self->mOwner->getDefaultImageAssetID() );
+ }
+ self->commitIfImmediateSet();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnWhite(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mWhiteImageAssetID );
+ self->commitIfImmediateSet();
+}
+
+
+// static
+void LLFloaterTexturePicker::onBtnNone(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( LLUUID::null );
+ self->commitIfImmediateSet();
+}
+
+/*
+// static
+void LLFloaterTexturePicker::onBtnRevert(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mOriginalImageAssetID );
+ // TODO: Change this to tell the owner to cancel. It needs to be
+ // smart enough to restore multi-texture selections.
+ self->mOwner->onFloaterCommit();
+ self->mIsDirty = FALSE;
+}*/
+
+// static
+void LLFloaterTexturePicker::onBtnCancel(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ self->setImageID( self->mOriginalImageAssetID );
+ if (self->mOwner)
+ {
+ self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_CANCEL);
+ }
+ self->mIsDirty = FALSE;
+ self->close();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnSelect(void* userdata)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+ if (self->mOwner)
+ {
+ self->mOwner->onFloaterCommit(LLTextureCtrl::TEXTURE_SELECT);
+ }
+ self->close();
+}
+
+// static
+void LLFloaterTexturePicker::onBtnPipette( void* userdata )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*) userdata;
+
+ if ( self && gToolMgr)
+ {
+ BOOL pipette_active = self->childGetValue("Pipette").asBoolean();
+ pipette_active = !pipette_active;
+ if (pipette_active)
+ {
+ gToolPipette->setSelectCallback(onTextureSelect, self);
+ gToolMgr->setTransientTool(gToolPipette);
+ }
+ else
+ {
+ gToolMgr->clearTransientTool();
+ }
+ }
+
+}
+
+// static
+void LLFloaterTexturePicker::onSelectionChange(const std::deque<LLFolderViewItem*> &items, BOOL user_action, void* data)
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;
+ if (items.size())
+ {
+ LLFolderViewItem* first_item = items.front();
+ LLInventoryItem* itemp = gInventory.getItem(first_item->getListener()->getUUID());
+ self->mNoCopyTextureSelected = FALSE;
+ if (itemp)
+ {
+ if (!itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ self->mNoCopyTextureSelected = TRUE;
+ }
+ self->mImageAssetID = itemp->getAssetUUID();
+ self->mIsDirty = TRUE;
+ if (user_action)
+ {
+ // only commit intentional selections, not implicit ones
+ self->commitIfImmediateSet();
+ }
+ }
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onShowFolders(LLUICtrl* ctrl, void *user_data)
+{
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ if (check_box->get())
+ {
+ picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
+ }
+ else
+ {
+ picker->mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NO_FOLDERS);
+ }
+}
+
+// static
+void LLFloaterTexturePicker::onApplyImmediateCheck(LLUICtrl* ctrl, void *user_data)
+{
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ LLCheckBoxCtrl* check_box = (LLCheckBoxCtrl*)ctrl;
+ gSavedSettings.setBOOL("ApplyTextureImmediately", check_box->get());
+
+ picker->updateFilterPermMask();
+ picker->commitIfImmediateSet();
+}
+
+void LLFloaterTexturePicker::updateFilterPermMask()
+{
+ mInventoryPanel->setFilterPermMask( getFilterPermMask() );
+}
+
+void LLFloaterTexturePicker::onSearchEdit(const LLString& search_string, void* user_data )
+{
+ LLFloaterTexturePicker* picker = (LLFloaterTexturePicker*)user_data;
+
+ std::string filter_text = search_string;
+
+ if (filter_text.empty()&& picker->mInventoryPanel->getFilterSubString().empty())
+ {
+ // current filter and new filter empty, do nothing
+ return;
+ }
+ std::string upper_case_search_string = filter_text;
+ LLString::toUpper(upper_case_search_string);
+
+ picker->mInventoryPanel->setFilterSubString(upper_case_search_string);
+
+ LLFolderView* root_folder = picker->mInventoryPanel->getRootFolder();
+
+ //if (search_string.size())
+ //{
+ // LLSelectFirstFilteredItem filter;
+ // root_folder->applyFunctorRecursively(filter);
+ // //...and scroll to show it
+ // root_folder->scrollToShowSelection();
+ //}
+
+ KEY key = gKeyboard->currentKey();
+
+ if ((key == KEY_RETURN || key == KEY_DOWN) && gKeyboard->currentMask(FALSE) == MASK_NONE)
+ {
+ if (search_string.size())
+ {
+ LLSelectFirstFilteredItem filter;
+ root_folder->applyFunctorRecursively(filter);
+ //...and scroll to show it
+ root_folder->scrollToShowSelection();
+ }
+
+ if (!root_folder->getCurSelectedItem())
+ {
+ LLFolderViewItem* itemp = root_folder->getItemByID(gAgent.getInventoryRootID());
+ if (itemp)
+ {
+ root_folder->setSelection(itemp, FALSE, FALSE);
+ }
+ }
+
+ // move focus to inventory proper
+ root_folder->setFocus(TRUE);
+ root_folder->scrollToShowSelection();
+ }
+}
+
+//static
+void LLFloaterTexturePicker::onTextureSelect( const LLTextureEntry& te, void *data )
+{
+ LLFloaterTexturePicker* self = (LLFloaterTexturePicker*)data;
+
+ LLUUID inventory_item_id = self->findItemID(te.getID(), TRUE);
+ if (self && inventory_item_id.notNull())
+ {
+ gToolPipette->setResult(TRUE, "");
+ self->setImageID(te.getID());
+
+ self->mNoCopyTextureSelected = FALSE;
+ LLInventoryItem* itemp = gInventory.getItem(inventory_item_id);
+
+ if (itemp && !itemp->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // no copy texture
+ self->mNoCopyTextureSelected = TRUE;
+ }
+
+ self->commitIfImmediateSet();
+ }
+ else
+ {
+ gToolPipette->setResult(FALSE, "You do not have a copy this \nof texture in your inventory");
+ }
+}
+
+///////////////////////////////////////////////////////////////////////
+// LLTextureCtrl
+
+LLTextureCtrl::LLTextureCtrl(
+ const std::string& name,
+ const LLRect &rect,
+ const std::string& label,
+ const LLUUID &image_id,
+ const LLUUID &default_image_id,
+ const std::string& default_image_name )
+ :
+ LLUICtrl(name, rect, TRUE, NULL, NULL, FOLLOWS_LEFT | FOLLOWS_TOP),
+ mDragCallback(NULL),
+ mDropCallback(NULL),
+ mOnCancelCallback(NULL),
+ mOnSelectCallback(NULL),
+ mBorderColor( gColors.getColor("DefaultHighlightLight") ),
+ mImageAssetID( image_id ),
+ mDefaultImageAssetID( default_image_id ),
+ mDefaultImageName( default_image_name ),
+ mLabel( label ),
+ mAllowNoTexture( FALSE ),
+ mImmediateFilterPermMask( PERM_NONE ),
+ mNonImmediateFilterPermMask( PERM_NONE ),
+ mCanApplyImmediately( FALSE ),
+ mNeedsRawImageData( FALSE ),
+ mValid( TRUE )
+{
+ mCaption = new LLTextBox( label,
+ LLRect( 0, BTN_HEIGHT_SMALL, mRect.getWidth(), 0 ),
+ NULL,
+ LLFontGL::sSansSerifSmall );
+ mCaption->setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+ addChild( mCaption );
+
+ S32 image_top = mRect.getHeight();
+ S32 image_bottom = BTN_HEIGHT_SMALL;
+ S32 image_middle = (image_top + image_bottom) / 2;
+ S32 line_height = llround(LLFontGL::sSansSerifSmall->getLineHeight());
+
+ mTentativeLabel = new LLTextBox( "Multiple",
+ LLRect(
+ 0, image_middle + line_height / 2,
+ mRect.getWidth(), image_middle - line_height / 2 ),
+ NULL,
+ LLFontGL::sSansSerifSmall );
+ mTentativeLabel->setHAlign( LLFontGL::HCENTER );
+ mTentativeLabel->setFollowsAll();
+ addChild( mTentativeLabel );
+
+ LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
+ border_rect.mBottom += BTN_HEIGHT_SMALL;
+ mBorder = new LLViewBorder("border", border_rect, LLViewBorder::BEVEL_IN);
+ addChild(mBorder);
+
+ setEnabled(TRUE); // for the tooltip
+}
+
+
+LLTextureCtrl::~LLTextureCtrl()
+{
+ closeFloater();
+}
+
+// virtual
+LLXMLNodePtr LLTextureCtrl::getXML(bool save_children) const
+{
+ LLXMLNodePtr node = LLUICtrl::getXML();
+
+ node->createChild("label", TRUE)->setStringValue(getLabel());
+
+ node->createChild("default_image_name", TRUE)->setStringValue(getDefaultImageName());
+
+ node->createChild("allow_no_texture", TRUE)->setBoolValue(mAllowNoTexture);
+
+ node->createChild("can_apply_immediately", TRUE)->setBoolValue(mCanApplyImmediately );
+
+ return node;
+}
+
+LLView* LLTextureCtrl::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("texture_picker");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent);
+
+ LLString label;
+ node->getAttributeString("label", label);
+
+ LLString image_id("");
+ node->getAttributeString("image", image_id);
+
+ LLString default_image_id("");
+ node->getAttributeString("default_image", default_image_id);
+
+ LLString default_image_name("Default");
+ node->getAttributeString("default_image_name", default_image_name);
+
+ BOOL allow_no_texture = FALSE;
+ node->getAttributeBOOL("allow_no_texture", allow_no_texture);
+
+ BOOL can_apply_immediately = FALSE;
+ node->getAttributeBOOL("can_apply_immediately", can_apply_immediately);
+
+ if (label.empty())
+ {
+ label.assign(node->getValue());
+ }
+
+ LLTextureCtrl* texture_picker = new LLTextureCtrl(
+ name,
+ rect,
+ label,
+ LLUUID(image_id),
+ LLUUID(default_image_id),
+ default_image_name );
+ texture_picker->setAllowNoTexture(allow_no_texture);
+ texture_picker->setCanApplyImmediately(can_apply_immediately);
+
+ texture_picker->initFromXML(node, parent);
+
+ return texture_picker;
+}
+
+void LLTextureCtrl::setCaption(const LLString& caption)
+{
+ mCaption->setText( caption );
+}
+
+void LLTextureCtrl::setCanApplyImmediately(BOOL b)
+{
+ mCanApplyImmediately = b;
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setCanApplyImmediately(b);
+ }
+}
+
+void LLTextureCtrl::setVisible( BOOL visible )
+{
+ if( !visible )
+ {
+ closeFloater();
+ }
+ LLUICtrl::setVisible( visible );
+}
+
+void LLTextureCtrl::setEnabled( BOOL enabled )
+{
+ if( enabled )
+ {
+ setToolTip( "Click to choose a picture" );
+ }
+ else
+ {
+ setToolTip( "" );
+ //FIXME: would be better to keep floater open
+ // and show disabled state
+ closeFloater();
+ }
+
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setActive(enabled);
+ }
+
+ mCaption->setEnabled( enabled );
+
+ LLUICtrl::setEnabled( enabled );
+}
+
+void LLTextureCtrl::setValid(BOOL valid )
+{
+ mValid = valid;
+ if (!valid)
+ {
+ LLFloaterTexturePicker* pickerp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if (pickerp)
+ {
+ pickerp->setActive(FALSE);
+ }
+ }
+}
+
+
+// virtual
+void LLTextureCtrl::clear()
+{
+ setImageAssetID(LLUUID::null);
+}
+
+void LLTextureCtrl::setLabel(const LLString& label)
+{
+ mLabel = label;
+ mCaption->setText(label);
+}
+
+void LLTextureCtrl::showPicker(BOOL take_focus)
+{
+ LLFloater* floaterp = LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ // Show the dialog
+ if( floaterp )
+ {
+ floaterp->open( );
+ }
+ else
+ {
+ if( !mLastFloaterLeftTop.mX && !mLastFloaterLeftTop.mY )
+ {
+ gFloaterView->getNewFloaterPosition(&mLastFloaterLeftTop.mX, &mLastFloaterLeftTop.mY);
+ }
+ LLRect rect = gSavedSettings.getRect("TexturePickerRect");
+ rect.translate( mLastFloaterLeftTop.mX - rect.mLeft, mLastFloaterLeftTop.mY - rect.mTop );
+
+ floaterp = new LLFloaterTexturePicker(
+ this,
+ rect,
+ mLabel,
+ mImmediateFilterPermMask,
+ mNonImmediateFilterPermMask,
+ mCanApplyImmediately);
+ mFloaterHandle = floaterp->getHandle();
+
+ gFloaterView->getParentFloater(this)->addDependentFloater(floaterp);
+ floaterp->open();
+ }
+
+ if (take_focus)
+ {
+ floaterp->setFocus(TRUE);
+ }
+}
+
+
+void LLTextureCtrl::closeFloater()
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp )
+ {
+ floaterp->setOwner(NULL);
+ floaterp->close();
+ }
+}
+
+// Allow us to download textures quickly when floater is shown
+class LLTextureFetchDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ virtual void done()
+ {
+ // We need to find textures in all folders, so get the main
+ // background download going.
+ gInventory.startBackgroundFetch();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+BOOL LLTextureCtrl::handleHover(S32 x, S32 y, MASK mask)
+{
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ return TRUE;
+}
+
+
+BOOL LLTextureCtrl::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = LLUICtrl::handleMouseDown( x, y , mask );
+ if( handled )
+ {
+ showPicker(FALSE);
+
+ //grab textures first...
+ gInventory.startBackgroundFetch(gInventory.findCategoryUUIDForType(LLAssetType::AT_TEXTURE));
+ //...then start full inventory fetch.
+ gInventory.startBackgroundFetch();
+ }
+ return handled;
+}
+
+void LLTextureCtrl::onFloaterClose()
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ if (floaterp)
+ {
+ floaterp->setOwner(NULL);
+ mLastFloaterLeftTop.set( floaterp->getRect().mLeft, floaterp->getRect().mTop );
+ }
+
+ mFloaterHandle.markDead();
+}
+
+void LLTextureCtrl::onFloaterCommit(ETexturePickOp op)
+{
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+
+ if( floaterp && mEnabled)
+ {
+ if( floaterp->isDirty() )
+ {
+ setTentative( FALSE );
+ mImageItemID = floaterp->findItemID(floaterp->getAssetID(), FALSE);
+ lldebugs << "mImageItemID: " << mImageItemID << llendl;
+ mImageAssetID = floaterp->getAssetID();
+ lldebugs << "mImageAssetID: " << mImageAssetID << llendl;
+ if (op == TEXTURE_SELECT && mOnSelectCallback)
+ {
+ mOnSelectCallback(this, mCallbackUserData);
+ }
+ else if (op == TEXTURE_CANCEL && mOnCancelCallback)
+ {
+ mOnCancelCallback(this, mCallbackUserData);
+ }
+ else
+ {
+ onCommit();
+ }
+ }
+ }
+}
+
+void LLTextureCtrl::setImageAssetID( const LLUUID& asset_id )
+{
+ if( mImageAssetID != asset_id )
+ {
+ mImageItemID.setNull();
+ mImageAssetID = asset_id;
+ LLFloaterTexturePicker* floaterp = (LLFloaterTexturePicker*)LLFloater::getFloaterByHandle(mFloaterHandle);
+ if( floaterp && getEnabled() )
+ {
+ floaterp->setImageID( asset_id );
+ floaterp->setDirty( FALSE );
+ }
+ }
+}
+
+BOOL LLTextureCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ // this downcast may be invalid - but if the second test below
+ // returns true, then the cast was valid, and we can perform
+ // the third test without problems.
+ LLInventoryItem* item = (LLInventoryItem*)cargo_data;
+ if (mEnabled && (cargo_type == DAD_TEXTURE) && allowDrop(item))
+ {
+ if (drop)
+ {
+ if(doDrop(item))
+ {
+ // This removes the 'Multiple' overlay, since
+ // there is now only one texture selected.
+ setTentative( FALSE );
+ onCommit();
+ }
+ }
+
+ *accept = ACCEPT_YES_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLTextureCtrl " << getName() << llendl;
+
+ return handled;
+}
+
+void LLTextureCtrl::draw()
+{
+ if( getVisible() )
+ {
+ mBorder->setKeyboardFocusHighlight(hasFocus());
+
+ if (mImageAssetID.isNull() || !mValid)
+ {
+ mTexturep = NULL;
+ }
+ else
+ {
+ mTexturep = gImageList.getImage(mImageAssetID, MIPMAP_YES, IMMEDIATE_NO);
+ mTexturep->setBoostLevel(LLViewerImage::BOOST_PREVIEW);
+ }
+
+ // Border
+ LLRect border( 0, mRect.getHeight(), mRect.getWidth(), BTN_HEIGHT_SMALL );
+ gl_rect_2d( border, mBorderColor, FALSE );
+
+ // Interior
+ LLRect interior = border;
+ interior.stretch( -1 );
+
+ if( mTexturep )
+ {
+ if( mTexturep->getComponents() == 4 )
+ {
+ gl_rect_2d_checkerboard( interior );
+ }
+
+ gl_draw_scaled_image( interior.mLeft, interior.mBottom, interior.getWidth(), interior.getHeight(), mTexturep);
+ mTexturep->addTextureStats( (F32)(interior.getWidth() * interior.getHeight()) );
+ }
+ else
+ {
+ gl_rect_2d( interior, LLColor4::grey, TRUE );
+
+ // Draw X
+ gl_draw_x( interior, LLColor4::black );
+ }
+
+ mTentativeLabel->setVisible( !mTexturep.isNull() && mTentative );
+
+ LLUICtrl::draw();
+ }
+}
+
+BOOL LLTextureCtrl::allowDrop(LLInventoryItem* item)
+{
+ BOOL copy = item->getPermissions().allowCopyBy(gAgent.getID());
+ BOOL mod = item->getPermissions().allowModifyBy(gAgent.getID());
+ BOOL xfer = item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID());
+
+ PermissionMask item_perm_mask = 0;
+ if (copy) item_perm_mask |= PERM_COPY;
+ if (mod) item_perm_mask |= PERM_MODIFY;
+ if (xfer) item_perm_mask |= PERM_TRANSFER;
+
+ PermissionMask filter_perm_mask = mCanApplyImmediately ?
+ mImmediateFilterPermMask : mNonImmediateFilterPermMask;
+ if ( (item_perm_mask & filter_perm_mask) == filter_perm_mask )
+ {
+ if(mDragCallback)
+ {
+ return mDragCallback(this, item, mCallbackUserData);
+ }
+ else
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLTextureCtrl::doDrop(LLInventoryItem* item)
+{
+ // call the callback if it exists.
+ if(mDropCallback)
+ {
+ // if it returns TRUE, we return TRUE, and therefore the
+ // commit is called above.
+ return mDropCallback(this, item, mCallbackUserData);
+ }
+
+ // no callback installed, so just set the image ids and carry on.
+ setImageAssetID( item->getAssetUUID() );
+ mImageItemID = item->getUUID();
+ return TRUE;
+}
+
+BOOL LLTextureCtrl::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
+{
+ if( getVisible() && mEnabled && !called_from_parent && ' ' == uni_char )
+ {
+ showPicker(TRUE);
+ return TRUE;
+ }
+ return LLUICtrl::handleUnicodeCharHere(uni_char, called_from_parent);
+}
+
+void LLTextureCtrl::setValue( LLSD value )
+{
+ setImageAssetID(value.asUUID());
+}
+
+LLSD LLTextureCtrl::getValue() const
+{
+ return LLSD(getImageAssetID());
+}
+
+
+/////////////////////////////////////////////////////////////////////////////////
+// LLToolTexEyedropper
+
+class LLToolTexEyedropper : public LLTool
+{
+public:
+ LLToolTexEyedropper( void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ), void* userdata );
+ virtual ~LLToolTexEyedropper();
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+protected:
+ void (*mCallback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata );
+ void* mCallbackUserData;
+};
+
+
+LLToolTexEyedropper::LLToolTexEyedropper(
+ void (*callback)(const LLUUID& obj_id, const LLUUID& image_id, void* userdata ),
+ void* userdata )
+ : LLTool("texeyedropper"),
+ mCallback( callback ),
+ mCallbackUserData( userdata )
+{
+}
+
+LLToolTexEyedropper::~LLToolTexEyedropper()
+{
+}
+
+
+BOOL LLToolTexEyedropper::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj &&
+ !hit_obj->isAvatar())
+ {
+ if( (0 <= gLastHitObjectFace) && (gLastHitObjectFace < hit_obj->getNumTEs()) )
+ {
+ LLViewerImage* image = hit_obj->getTEImage( gLastHitObjectFace );
+ if( image )
+ {
+ if( mCallback )
+ {
+ mCallback( hit_obj->getID(), image->getID(), mCallbackUserData );
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+BOOL LLToolTexEyedropper::handleHover(S32 x, S32 y, MASK mask)
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolTexEyedropper" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_CROSS); // TODO: better cursor
+ return TRUE;
+}
+
diff --git a/indra/newview/lltexturectrl.h b/indra/newview/lltexturectrl.h
new file mode 100644
index 0000000000..fa4e71d266
--- /dev/null
+++ b/indra/newview/lltexturectrl.h
@@ -0,0 +1,158 @@
+/**
+ * @file lltexturectrl.h
+ * @author Richard Nelson, James Cook
+ * @brief LLTextureCtrl class header file including related functions
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTURECTRL_H
+#define LL_LLTEXTURECTRL_H
+
+#include "llcoord.h"
+#include "llfloater.h"
+#include "llstring.h"
+#include "lluictrl.h"
+#include "llpermissionsflags.h"
+
+class LLButton;
+class LLFloaterTexturePicker;
+class LLInventoryItem;
+class LLTextBox;
+class LLViewBorder;
+
+// used for setting drag & drop callbacks.
+typedef BOOL (*drag_n_drop_callback)(LLUICtrl*, LLInventoryItem*, void*);
+
+//////////////////////////////////////////////////////////////////////////////////////////
+// LLTextureCtrl
+
+
+class LLTextureCtrl
+: public LLUICtrl
+{
+public:
+ typedef enum e_texture_pick_op
+ {
+ TEXTURE_CHANGE,
+ TEXTURE_SELECT,
+ TEXTURE_CANCEL
+ } ETexturePickOp;
+
+public:
+ LLTextureCtrl(
+ const std::string& name, const LLRect& rect,
+ const std::string& label,
+ const LLUUID& image_id,
+ const LLUUID& default_image_id,
+ const std::string& default_image_name );
+ virtual ~LLTextureCtrl();
+
+ // LLView interface
+ virtual EWidgetType getWidgetType() const { return WIDGET_TYPE_TEXTURE_PICKER; }
+ virtual LLString getWidgetTag() const { return LL_TEXTURE_CTRL_TAG; }
+ virtual LLXMLNodePtr getXML(bool save_children = true) const;
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent);
+
+ virtual void draw();
+ virtual void setVisible( BOOL visible );
+ virtual void setEnabled( BOOL enabled );
+
+ void setValid(BOOL valid);
+
+ // LLUICtrl interface
+ virtual void clear();
+
+ // Takes a UUID, wraps get/setImageAssetID
+ virtual void setValue( LLSD value );
+ virtual LLSD getValue() const;
+
+ // LLTextureCtrl interface
+ void showPicker(BOOL take_focus);
+ void setLabel(const LLString& label);
+ const LLString& getLabel() const { return mLabel; }
+
+ void setAllowNoTexture( BOOL b ) { mAllowNoTexture = b; }
+ bool getAllowNoTexture() const { return mAllowNoTexture; }
+
+ const LLUUID& getImageItemID() { return mImageItemID; }
+
+ void setImageAssetID(const LLUUID &image_asset_id);
+ const LLUUID& getImageAssetID() const { return mImageAssetID; }
+
+ void setDefaultImageAssetID( const LLUUID& id ) { mDefaultImageAssetID = id; }
+
+ const LLString& getDefaultImageName() const { return mDefaultImageName; }
+ const LLUUID& getDefaultImageAssetID() const { return mDefaultImageAssetID; }
+
+ void setCaption(const LLString& caption);
+ void setCanApplyImmediately(BOOL b);
+
+ void setImmediateFilterPermMask(PermissionMask mask)
+ { mImmediateFilterPermMask = mask; }
+ void setNonImmediateFilterPermMask(PermissionMask mask)
+ { mNonImmediateFilterPermMask = mask; }
+ PermissionMask getImmediateFilterPermMask() { return mImmediateFilterPermMask; }
+ PermissionMask getNonImmediateFilterPermMask() { return mNonImmediateFilterPermMask; }
+
+ void closeFloater();
+
+ void onFloaterClose();
+ void onFloaterCommit(ETexturePickOp op);
+
+ // This call is returned when a drag is detected. Your callback
+ // should return TRUE if the drag is acceptable.
+ void setDragCallback(drag_n_drop_callback cb) { mDragCallback = cb; }
+
+ // This callback is called when the drop happens. Return TRUE if
+ // the drop happened - resulting in an on commit callback, but not
+ // necessariliy any other change.
+ void setDropCallback(drag_n_drop_callback cb) { mDropCallback = cb; }
+
+ void setOnCancelCallback(LLUICtrlCallback cb) { mOnCancelCallback = cb; }
+
+ void setOnSelectCallback(LLUICtrlCallback cb) { mOnSelectCallback = cb; }
+
+private:
+ BOOL allowDrop(LLInventoryItem* item);
+ BOOL doDrop(LLInventoryItem* item);
+
+private:
+ drag_n_drop_callback mDragCallback;
+ drag_n_drop_callback mDropCallback;
+ LLUICtrlCallback mOnCancelCallback;
+ LLUICtrlCallback mOnSelectCallback;
+ LLPointer<LLViewerImage> mTexturep;
+ LLColor4 mBorderColor;
+ LLUUID mImageItemID;
+ LLUUID mImageAssetID;
+ LLUUID mDefaultImageAssetID;
+ LLString mDefaultImageName;
+ LLViewHandle mFloaterHandle;
+ LLTextBox* mTentativeLabel;
+ LLTextBox* mCaption;
+ LLString mLabel;
+ BOOL mAllowNoTexture; // If true, the user can select "none" as an option
+ LLCoordGL mLastFloaterLeftTop;
+ PermissionMask mImmediateFilterPermMask;
+ PermissionMask mNonImmediateFilterPermMask;
+ BOOL mCanApplyImmediately;
+ BOOL mNeedsRawImageData;
+ LLViewBorder* mBorder;
+ BOOL mValid;
+};
+
+// XUI HACK: When floaters converted, switch this file to lltexturepicker.h/cpp
+// and class to LLTexturePicker
+#define LLTexturePicker LLTextureCtrl
+
+#endif // LL_LLTEXTURECTRL_H
diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp
new file mode 100644
index 0000000000..cc27fd4b9e
--- /dev/null
+++ b/indra/newview/lltexturefetch.cpp
@@ -0,0 +1,744 @@
+/**
+ * @file llviewerimage.cpp
+ * @brief Object which handles a received image (and associated texture(s))
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llstl.h"
+
+#include "lltexturefetch.h"
+
+#include "llworkerthread.h"
+#include "llimage.h"
+#include "llvfs.h"
+
+#include "llviewerimage.h"
+#include "viewer.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+class LLTextureFetchWorker : public LLWorkerClass
+{
+ friend class LLTextureFetchImpl;
+
+public:
+ // From LLWorkerClass
+ static void initClass(bool threaded, bool runalways);
+ static void updateClass();
+ static void cleanupClass();
+
+ // New methods
+ static LLTextureFetchWorker* getWorker(const LLUUID& id, const LLHost& host,
+ F32 mPriority, S32 discard,
+ BOOL needs_aux = FALSE);
+ static LLTextureFetchWorker* getActiveWorker(const LLUUID& id);
+
+private:
+ static void sendRequestListToSimulators();
+
+public:
+ virtual bool doWork(S32 param); // Called from LLWorkerThread::processRequest()
+
+ ~LLTextureFetchWorker();
+ void relese() { --mActiveCount; }
+
+
+protected:
+ LLTextureFetchWorker(const LLUUID& id, const LLHost& host, F32 mPriority, S32 discard);
+
+private:
+ virtual void startWork(S32 param); // called from addWork() (MAIN THREAD)
+ virtual void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD)
+
+ void setImagePriority(F32 priority);
+ void setDesiredDiscard(S32 discard);
+ void insertPacket(S32 index, U8* data, S32 size);
+ void clearPackets();
+ U32 calcWorkPriority();
+ bool startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type);
+ bool loadFromVFS();
+ bool processSimulatorPackets();
+ void startDecode();
+ bool decodeImage();
+
+ void lockWorkData() { mWorkMutex.lock(); }
+ void unlockWorkData() { mWorkMutex.unlock(); }
+
+ static void lockQueue() { sDataMutex->lock(); }
+ static void unlockQueue() { sDataMutex->unlock(); }
+
+private:
+ enum e_state
+ {
+ INIT = 1,
+ LOAD_FROM_CACHE,
+ LOAD_FROM_SIMULATOR,
+ WRITE_TO_VFS,
+ DECODE_IMAGE,
+ DECODE_IMAGE_UPDATE,
+ DONE
+ };
+ e_state mState;
+ LLPointer<LLImageFormatted> mFormattedImage;
+ LLPointer<LLImageRaw> mRawImage;
+ LLPointer<LLImageRaw> mAuxImage;
+ LLUUID mID;
+ LLHost mHost;
+ F32 mPriority;
+ S32 mDesiredDiscard;
+ S32 mRequestedDiscard;
+ S32 mDecodedDiscard;
+ LLFrameTimer mRequestedTimer;
+ LLFrameTimer mIdleTimer;
+ LLVFSThread::handle_t mFileHandle;
+ U8* mBuffer;
+ S32 mBufferSize;
+ S32 mRequestedSize;
+ BOOL mLoaded;
+ BOOL mRequested;
+ BOOL mDecoded;
+ BOOL mNeedsAux;
+ S32 mActiveCount;
+
+ // Work Data
+ LLMutex mWorkMutex;
+ struct PacketData
+ {
+ PacketData(U8* data, S32 size) { mData = data; mSize = size; }
+ ~PacketData() { clearData(); }
+ void clearData() { delete mData; mData = NULL; }
+ U8* mData;
+ U32 mSize;
+ };
+ std::vector<PacketData*> mPackets;
+ S32 mLastPacket;
+ S32 mTotalPackets;
+ S32 mTotalBytes;
+
+ // Class variables (statics)
+
+ static LLWorkerThread* sWorkerThread;
+ static LLMutex* sDataMutex;
+
+ // Map of all requests by UUID
+ typedef std::map<LLUUID,LLTextureFetchWorker*> map_t;
+ static map_t sRequests;
+
+ // Set of requests that require network data
+ typedef std::set<LLTextureFetchWorker*> queue_t ;
+ static queue_t sNetworkQueue;
+
+ static LLFrameTimer sNetworkTimer;
+};
+
+//statics
+LLTextureFetchWorker::map_t LLTextureFetchWorker::sRequests;
+LLTextureFetchWorker::queue_t LLTextureFetchWorker::sNetworkQueue;
+LLFrameTimer LLTextureFetchWorker::sNetworkTimer;
+LLWorkerThread* LLTextureFetchWorker::sWorkerThread = NULL;
+LLMutex* LLTextureFetchWorker::sDataMutex = NULL;
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::initClass(bool threaded, bool runalways)
+{
+ sWorkerThread = new LLWorkerThread(threaded, runalways);
+ sDataMutex = new LLMutex(sWorkerThread->getAPRPool());
+}
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::updateClass()
+{
+ const F32 REQUEST_TIME = 1.f;
+ const F32 MIN_IDLE_TIME = 1.f * 60.f; // 1 minute
+ const F32 MAX_IDLE_TIME = 5.f * 60.f; // 5 minutes
+ const S32 MIN_IDLE_COUNT = 16; // always keep last 16 idle requests
+ const F32 MAX_IDLE_COUNT = 1024; // max number of idle requests
+
+ // Periodically, gather the list of textures that need data from the network
+ // And send the requests out to the simulators
+ if (sNetworkTimer.getElapsedTimeF32() >= REQUEST_TIME)
+ {
+ sNetworkTimer.reset();
+ sendRequestListToSimulators();
+ }
+ // Remove any old requests (releasing their raw data)
+ typedef std::pair<F32, LLTextureFetchWorker*> idle_pair;
+ typedef std::set<idle_pair, compare_pair_greater<F32,LLTextureFetchWorker*> > idle_set;
+ idle_set remove_set;
+ for (map_t::iterator iter = sRequests.begin(); iter != sRequests.end(); ++iter)
+ {
+ LLTextureFetchWorker* worker = iter->second;
+ if (worker->mActiveCount > 0)
+ continue;
+ if (worker->haveWork())
+ continue;
+ F32 idletime = worker->mIdleTimer.getElapsedTimeF32();
+ if (idletime < MIN_IDLE_TIME)
+ continue;
+ remove_set.insert(std::make_pair(idletime, worker));
+ }
+ S32 num_left = remove_set.size();
+ for (idle_set::iterator iter = remove_set.begin(); iter != remove_set.end(); ++iter)
+ {
+ if (num_left <= MIN_IDLE_COUNT)
+ break;
+ if (iter->first < MAX_IDLE_TIME &&
+ num_left < MAX_IDLE_COUNT)
+ break;
+ num_left--;
+ }
+}
+
+// called from MAIN THREAD
+//static
+void LLTextureFetchWorker::sendRequestListToSimulators()
+{
+ const F32 LAZY_FLUSH_TIMEOUT = 60.f;
+ const S32 IMAGES_PER_REQUEST = 50;
+ // Send requests
+ typedef std::map< LLHost, std::vector<LLTextureFetchWorker*> > work_request_map_t;
+ work_request_map_t requests;
+ for (queue_t::iterator iter = sNetworkQueue.begin(); iter != sNetworkQueue.end(); ++iter)
+ {
+ LLTextureFetchWorker* req = *iter;
+ if (req->haveWork())
+ {
+ continue; // busy
+ }
+ if ((req->mRequestedDiscard == req->mDesiredDiscard) &&
+ (req->mRequestedTimer.getElapsedTimeF32() < LAZY_FLUSH_TIMEOUT))
+ {
+ continue;
+ }
+ req->mRequestedDiscard = req->mDesiredDiscard;
+ req->mRequestedTimer.reset();
+ requests[req->mHost].push_back(req);
+ }
+ for (work_request_map_t::iterator iter1 = requests.begin();
+ iter1 != requests.end(); ++iter1)
+ {
+ LLHost host = iter1->first;
+ // invalid host = load from static VFS
+ if (host != LLHost::invalid)
+ {
+ S32 request_count = 0;
+ for (std::vector<LLTextureFetchWorker*>::iterator iter2 = iter1->second.begin();
+ iter2 != iter1->second.end(); ++iter2)
+ {
+ LLTextureFetchWorker* req = *iter2;
+ if (0 == request_count)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RequestImage);
+ }
+ S32 packet = req->mLastPacket + 1;
+ gMessageSystem->nextBlockFast(_PREHASH_RequestImage);
+ gMessageSystem->addUUIDFast(_PREHASH_Image, req->mID);
+ gMessageSystem->addS32Fast(_PREHASH_DiscardLevel, req->mDesiredDiscard);
+ gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, req->mPriority);
+ gMessageSystem->addU32Fast(_PREHASH_Packet, packet);
+
+ request_count++;
+ if (request_count >= IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ request_count = 0;
+ }
+ }
+ if (request_count >= IMAGES_PER_REQUEST)
+ {
+ gMessageSystem->sendSemiReliable(host, NULL, NULL);
+ }
+ }
+ }
+}
+
+//static
+LLTextureFetchWorker* LLTextureFetchWorker::getWorker(const LLUUID& id,
+ const LLHost& host,
+ F32 priority,
+ S32 discard,
+ BOOL needs_aux)
+{
+ LLTextureFetchWorker* res;
+ lockQueue();
+ map_t::iterator iter = sRequests.find(id);
+ if (iter != sRequests.end())
+ {
+ res = iter->second;
+ if (res->mHost != host)
+ {
+ llerrs << "LLTextureFetchWorker::getWorker called with multiple hosts" << llendl;
+ }
+ res->setImagePriority(priority);
+ res->setDesiredDiscard(discard);
+
+ }
+ else
+ {
+ res = new LLTextureFetchWorker(id, host, priority, discard);
+ }
+ unlockQueue();
+ res->mActiveCount++;
+ res->mNeedsAux = needs_aux;
+ return res;
+}
+
+LLTextureFetchWorker* LLTextureFetchWorker::getActiveWorker(const LLUUID& id)
+{
+ LLTextureFetchWorker* res = NULL;
+ lockQueue();
+ map_t::iterator iter = sRequests.find(id);
+ if (iter != sRequests.end())
+ {
+ res = iter->second;
+ }
+ unlockQueue();
+ return res;
+}
+
+LLTextureFetchWorker::LLTextureFetchWorker(const LLUUID& id, // Image UUID
+ const LLHost& host, // Simulator host
+ F32 priority, // Priority
+ S32 discard) // Desired discard level
+ : LLWorkerClass(sWorkerThread, "TextureFetch"),
+ mState(INIT),
+ mID(id),
+ mHost(host),
+ mPriority(priority),
+ mDesiredDiscard(discard),
+ mRequestedDiscard(-1),
+ mDecodedDiscard(-1),
+ mFileHandle(LLVFSThread::nullHandle()),
+ mBuffer(NULL),
+ mBufferSize(0),
+ mRequestedSize(0),
+ mLoaded(FALSE),
+ mRequested(FALSE),
+ mDecoded(FALSE),
+ mActiveCount(0),
+ mWorkMutex(sWorkerThread->getAPRPool()),
+ mLastPacket(-1),
+ mTotalPackets(0),
+ mTotalBytes(0)
+{
+ lockQueue();
+ sRequests[mID] = this;
+ unlockQueue();
+ addWork(0, calcWorkPriority());
+}
+
+LLTextureFetchWorker::~LLTextureFetchWorker()
+{
+ lockQueue();
+ mFormattedImage = NULL;
+ map_t::iterator iter = sRequests.find(mID);
+ if (iter != sRequests.end() && iter->second == this)
+ {
+ sRequests.erase(iter);
+ }
+ sNetworkQueue.erase(this);
+ unlockQueue();
+ clearPackets();
+}
+
+void LLTextureFetchWorker::clearPackets()
+{
+ lockWorkData();
+ for_each(mPackets.begin(), mPackets.end(), DeletePointer());
+ mPackets.clear();
+ unlockWorkData();
+}
+
+U32 LLTextureFetchWorker::calcWorkPriority()
+{
+ F32 priority_scale = (F32)LLWorkerThread::PRIORITY_LOWBITS / LLViewerImage::maxDecodePriority();
+ U32 priority = (U32)(mPriority * priority_scale);
+ return LLWorkerThread::PRIORITY_NORMAL | priority;
+}
+
+void LLTextureFetchWorker::setDesiredDiscard(S32 discard)
+{
+ if (mDesiredDiscard != discard)
+ {
+ mDesiredDiscard = discard;
+ if (!haveWork())
+ {
+ addWork(0, calcWorkPriority());
+ }
+ }
+}
+
+void LLTextureFetchWorker::setImagePriority(F32 priority)
+{
+ if (priority != mPriority)
+ {
+ mPriority = priority;
+ setPriority(calcWorkPriority());
+ }
+}
+
+void LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size)
+{
+ PacketData* packet = new PacketData(data, size);
+
+ lockWorkData();
+ if (index >= (S32)mPackets.size())
+ {
+ mPackets.resize(index+1, (PacketData*)NULL); // initializes v to NULL pointers
+ }
+ if (mPackets[index] != NULL)
+ {
+ llwarns << "LLTextureFetchWorker::insertPacket called for duplicate packet: " << index << llendl;
+ }
+ mPackets[index] = packet;
+ while (mLastPacket+1 < (S32)mPackets.size() && mPackets[mLastPacket+1] != NULL)
+ {
+ ++mLastPacket;
+ }
+ unlockWorkData();
+}
+
+// Called from LLWorkerThread::processRequest()
+bool LLTextureFetchWorker::doWork(S32 param)
+{
+ switch(mState)
+ {
+ case INIT:
+ {
+ // fall through
+ }
+ case LOAD_FROM_CACHE:
+ {
+ // Load any existing data from the cache
+ if (mFileHandle == LLVFSThread::nullHandle())
+ {
+ bool res = startVFSLoad(gVFS, LLAssetType::AT_TEXTURE);
+ if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE_TGA);
+ if (!res) res = startVFSLoad(gStaticVFS, LLAssetType::AT_TEXTURE);
+ if (!res)
+ {
+ // Didn't load from VFS
+ mFormattedImage = new LLImageJ2C;
+ mState = LOAD_FROM_SIMULATOR;
+ }
+ }
+ if (mFileHandle != LLVFSThread::nullHandle())
+ {
+ if (!loadFromVFS())
+ {
+ return false; // not done
+ }
+ if (!mLoaded)
+ {
+ llwarns << "Load from VFS failed on: " << mID << llendl;
+ return true;
+ }
+ bool res = mFormattedImage->setData(mBuffer, mBufferSize);
+ if (!res)
+ {
+ llwarns << "loadLocalImage() - setData() failed" << llendl;
+ mFormattedImage->deleteData();
+ return true;
+ }
+ // Successfully loaded
+ if (mFormattedImage->getDiscardLevel() <= mRequestedDiscard)
+ {
+ // we have enough data, decode it
+ mState = DECODE_IMAGE;
+ mRequestedSize = mBufferSize;
+ }
+ else
+ {
+ // need more data
+ mState = LOAD_FROM_SIMULATOR;
+ mRequestedSize = mFormattedImage->calcDataSize(mRequestedDiscard);
+ }
+ }
+ return false;
+ }
+ case LOAD_FROM_SIMULATOR:
+ {
+ if (!mRequested)
+ {
+ lockQueue();
+ sNetworkQueue.insert(this);
+ unlockQueue();
+ mRequested = TRUE;
+ }
+ if (processSimulatorPackets())
+ {
+ mState = WRITE_TO_VFS;
+ }
+ return false;
+ }
+ case WRITE_TO_VFS:
+ {
+ mState = DECODE_IMAGE;
+ // fall through
+ }
+ case DECODE_IMAGE:
+ {
+ startDecode();
+ mState = DECODE_IMAGE_UPDATE;
+ // fall through
+ }
+ case DECODE_IMAGE_UPDATE:
+ {
+ if (decodeImage())
+ {
+ mState = DONE;
+ }
+ return false;
+ }
+ case DONE:
+ {
+ // Do any cleanup here
+ // Destroy the formatted image, we don't need it any more (raw image is still valid)
+ mFormattedImage = NULL;
+ mIdleTimer.reset();
+ return true;
+ }
+ default:
+ {
+ llerrs << "LLTextureFetchWorker::doWork() has illegal state" << llendl;
+ return true;
+ }
+ }
+}
+
+// Called from MAIN thread
+void LLTextureFetchWorker::startWork(S32 param)
+{
+}
+
+void LLTextureFetchWorker::endWork(S32 param, bool aborted)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::startVFSLoad(LLVFS* vfs, LLAssetType::EType asset_type)
+{
+ // Start load from VFS if it's there
+ if (vfs->getExists(mID, asset_type))
+ {
+ mBufferSize = vfs->getSize(mID, asset_type);
+ mBuffer = new U8[mBufferSize];
+ mFileHandle = LLVFSThread::sLocal->read(vfs, mID, asset_type, mBuffer, 0, mBufferSize);
+ if (mFileHandle == LLVFSThread::nullHandle())
+ {
+ llwarns << "loadLocalImage() - vfs read failed in static VFS: " << mID << llendl;
+ delete mBuffer;
+ mBuffer = NULL;
+ return false;
+ }
+ if (asset_type == LLAssetType::AT_TEXTURE_TGA)
+ {
+ mFormattedImage = new LLImageTGA;
+ }
+ else if (asset_type == LLAssetType::AT_TEXTURE)
+ {
+ mFormattedImage = new LLImageJ2C;
+ }
+ else
+ {
+ llerrs << "LLTextureFetchWorker::startVFSLoad called with bad asset type: " << asset_type << llendl;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool LLTextureFetchWorker::loadFromVFS()
+{
+ LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
+
+ llassert(mLoaded == FALSE);
+
+ // Check loading status
+ LLVFSThread::status_t status = LLVFSThread::sLocal->getRequestStatus(mFileHandle);
+ if (status == LLVFSThread::STATUS_QUEUED || status == LLVFSThread::STATUS_INPROGRESS)
+ {
+ return false;
+ }
+ else if (status == LLVFSThread::STATUS_COMPLETE)
+ {
+ mLoaded = TRUE;
+ return true;
+ }
+ else
+ {
+ llwarns << "loadLocalImage() - vfs read failed" << llendl;
+ LLVFSThread::Request* req = (LLVFSThread::Request*)LLVFSThread::sLocal->getRequest(mFileHandle);
+ if (req && mFormattedImage.notNull())
+ {
+ LLVFS* vfs = req->getVFS();
+ LLAssetType::EType asset_type = mFormattedImage->getCodec() == IMG_CODEC_TGA ? LLAssetType::AT_TEXTURE_TGA : LLAssetType::AT_TEXTURE;
+ vfs->removeFile(mID, asset_type);
+ }
+ return true;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool LLTextureFetchWorker::processSimulatorPackets()
+{
+ bool res = false;
+ lockWorkData();
+ if (mLastPacket >= 0)
+ {
+ S32 data_size = 0;
+ for (S32 i = 0; i<=mLastPacket; i++)
+ {
+ data_size += mPackets[i]->mSize;
+ }
+ if (data_size >= mRequestedSize || mLastPacket == mTotalPackets)
+ {
+ /// We have enough (or all) data, copy it into mBuffer
+ if (mBufferSize < data_size)
+ {
+ delete mBuffer;
+ mBuffer = new U8[data_size];
+ mBufferSize = data_size;
+ }
+ S32 offset = 0;
+ for (S32 i = 0; i<=mLastPacket; i++)
+ {
+ memcpy(mBuffer + offset, mPackets[i]->mData, mPackets[i]->mSize);
+ offset += mPackets[i]->mSize;
+ }
+ res = true;
+ }
+ }
+ unlockWorkData();
+ return res;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void LLTextureFetchWorker::startDecode()
+{
+ mRawImage = NULL;
+ mAuxImage = NULL;
+}
+
+bool LLTextureFetchWorker::decodeImage()
+{
+ const F32 MAX_DECODE_TIME = .001f; // 1 ms
+ if (mRawImage->getDataSize() == 0)
+ {
+ if (!mFormattedImage->requestDecodedData(mRawImage, -1, MAX_DECODE_TIME))
+ {
+ return false;
+ }
+ mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
+ }
+ if (mNeedsAux && mAuxImage->getDataSize() == 0)
+ {
+ if (!mFormattedImage->requestDecodedAuxData(mAuxImage, 4, -1, MAX_DECODE_TIME ))
+ {
+ return false;
+ }
+ mFormattedImage->releaseDecodedData(); // so that we have the only ref to the raw image
+ }
+ mDecodedDiscard = mFormattedImage->getDiscardLevel();
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+#if 0
+// static
+void LLTextureFetchWorker::receiveImageHeader(LLMessageSystem *msg, void **user_data)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+
+ // Receive image header, copy into image object and decompresses
+ // if this is a one-packet image.
+
+ gImageList.sTextureBits += msg->getReceiveBytes();
+ gImageList.sTexturePackets++;
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
+// LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
+
+ LLTextureFetchWorker* worker = getActiveWorker(id);
+ if (!worker)
+ {
+ llwarns << "receiveImageHeader for non active worker: " << id << llendl;
+ return;
+ }
+ worker->mRequestedTimer.reset();
+
+ // check to see if we've gotten this packet before
+ if (worker->mLastPacket != -1)
+ {
+ llwarns << "Img: " << id << ":" << " Duplicate Image Header" << llendl;
+ return;
+ }
+
+ // Copy header data into image object
+ worker->lockWorkData();
+ msg->getU8Fast(_PREHASH_ImageID, _PREHASH_Codec, image->mDataCodec);
+ msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packets, image->mTotalPackets);
+ msg->getU32Fast(_PREHASH_ImageID, _PREHASH_Size, image->mTotalBytes);
+ if (0 == image->mTotalPackets)
+ {
+ llwarns << "Img: " << id << ":" << " Number of packets is 0" << llendl;
+ }
+ worker->unlockWorkData();
+
+ U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data);
+ if (data_size)
+ {
+ U8 *data = new U8[data_size];
+ msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
+ worker->insertPacket(0, data, data_size)
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// static
+void LLTextureFetchWorker::receiveImagePacket(LLMessageSystem *msg, void **user_data)
+{
+ LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE);
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES);
+
+ gImageList.sTextureBits += msg->getReceiveBytes();
+ gImageList.sTexturePackets++;
+
+ LLUUID id;
+ msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, id);
+// LLString ip_string(u32_to_ip_string(msg->getSenderIP()));
+
+ U16 packet_num;
+ msg->getU16Fast(_PREHASH_ImageID, _PREHASH_Packet, packet_num);
+
+ LLTextureFetchWorker* worker = getActiveWorker(id);
+ if (!worker)
+ {
+ llwarns << "receiveImageHeader for non active worker: " << id << llendl;
+ return;
+ }
+ worker->mRequestedTimer.reset();
+
+ U16 data_size = msg->getSizeFast(_PREHASH_ImageData, _PREHASH_Data);
+ if (data_size)
+ {
+ U8 *data = new U8[data_size];
+ msg->getBinaryDataFast(_PREHASH_ImageData, _PREHASH_Data, data, data_size);
+ worker->insertPacket(0, data, data_size)
+ }
+}
+#endif
+
+ //////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h
new file mode 100644
index 0000000000..9d841ae23a
--- /dev/null
+++ b/indra/newview/lltexturefetch.h
@@ -0,0 +1,28 @@
+/**
+ * @file lltexturefetch.h
+ * @brief Object for managing texture fetches.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREFETCH_H
+#define LL_LLTEXTUREFETCH_H
+
+#include "llworkerthread.h"
+
+class LLViewerImage;
+
+// Interface class
+class LLTextureFetch
+{
+public:
+ static void initClass();
+ static void updateClass();
+ static void cleanupClass();
+
+ static LLWorkerClass::handle_t addRequest(LLImageFormatted* image, S32 discard);
+ static bool getRequestFinished(LLWorkerClass::handle_t handle);
+};
+
+#endif LL_LLTEXTUREFETCH_H
diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp
new file mode 100644
index 0000000000..01bc0bd5fd
--- /dev/null
+++ b/indra/newview/lltextureview.cpp
@@ -0,0 +1,304 @@
+/**
+ * @file lltextureview.cpp
+ * @brief LLTextureView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <set>
+
+#include "lltextureview.h"
+
+#include "llrect.h"
+#include "llerror.h"
+
+#include "viewer.h"
+#include "llui.h"
+
+#include "llviewerimagelist.h"
+#include "llselectmgr.h"
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "llhoverview.h"
+
+LLTextureView *gTextureView = NULL;
+
+//static
+std::set<LLViewerImage*> LLTextureView::sDebugImages;
+
+// Used for sorting
+struct SortTextureBars
+{
+ bool operator()(const LLView* i1, const LLView* i2)
+ {
+ LLTextureBar* bar1p = (LLTextureBar*)i1;
+ LLTextureBar* bar2p = (LLTextureBar*)i2;
+ LLViewerImage *i1p = bar1p->mImagep;
+ LLViewerImage *i2p = bar2p->mImagep;
+ F32 pri1 = i1p->getDecodePriority(); // i1p->mRequestedDownloadPriority
+ F32 pri2 = i2p->getDecodePriority(); // i2p->mRequestedDownloadPriority
+ if (pri1 > pri2)
+ return true;
+ else if (pri2 > pri1)
+ return false;
+ else
+ return i1p->getID() < i2p->getID();
+ }
+};
+
+LLTextureView::LLTextureView(const std::string& name, const LLRect& rect)
+: LLContainerView(name, rect)
+{
+ setVisible(FALSE);
+ mFreezeView = FALSE;
+
+ mNumTextureBars = 0;
+ setDisplayChildren(TRUE);
+ mGLTexMemBar = 0;
+}
+
+LLTextureView::~LLTextureView()
+{
+ // Children all cleaned up by default view destructor.
+ delete mGLTexMemBar;
+ mGLTexMemBar = 0;
+}
+
+EWidgetType LLTextureView::getWidgetType() const
+{
+ return WIDGET_TYPE_TEXTURE_VIEW;
+}
+
+LLString LLTextureView::getWidgetTag() const
+{
+ return LL_TEXTURE_VIEW_TAG;
+}
+
+
+typedef std::pair<F32,LLViewerImage*> decode_pair_t;
+struct compare_decode_pair
+{
+ bool operator()(const decode_pair_t& a, const decode_pair_t& b)
+ {
+ return a.first > b.first;
+ }
+};
+
+void LLTextureView::draw()
+{
+ if (!mFreezeView)
+ {
+// LLViewerObject *objectp;
+// S32 te;
+
+ for_each(mTextureBars.begin(), mTextureBars.end(), DeletePointer());
+ mTextureBars.clear();
+
+ delete mGLTexMemBar;
+ mGLTexMemBar = 0;
+
+ typedef std::multiset<decode_pair_t, compare_decode_pair > display_list_t;
+ display_list_t display_image_list;
+
+ for (LLViewerImageList::image_list_t::iterator iter = gImageList.mImageList.begin();
+ iter != gImageList.mImageList.end(); )
+ {
+ LLPointer<LLViewerImage> imagep = *iter++;
+#if 1
+ if (imagep->getDontDiscard())
+ {
+ continue;
+ }
+#endif
+ if (imagep->isMissingAsset())
+ {
+ continue;
+ }
+
+#define HIGH_PRIORITY 100000000.f
+ F32 pri = imagep->getDecodePriority();
+
+ if (sDebugImages.find(imagep) != sDebugImages.end())
+ {
+ pri += 3*HIGH_PRIORITY;
+ }
+
+#if 1
+ if (pri < HIGH_PRIORITY && gSelectMgr)
+ {
+ S32 te;
+ LLViewerObject *objectp;
+ for (gSelectMgr->getFirstTE(&objectp, &te); objectp; gSelectMgr->getNextTE(&objectp, &te))
+ {
+ if (imagep == objectp->getTEImage(te))
+ {
+ pri += 2*HIGH_PRIORITY;
+ break;
+ }
+ }
+ }
+#endif
+#if 1
+ if (pri < HIGH_PRIORITY)
+ {
+ LLViewerObject *objectp = gHoverView->getLastHoverObject();
+ if (objectp)
+ {
+ S32 tex_count = objectp->getNumTEs();
+ for (S32 i = 0; i < tex_count; i++)
+ {
+ if (imagep == objectp->getTEImage(i))
+ {
+ pri += 2*HIGH_PRIORITY;
+ break;
+ }
+ }
+ }
+ }
+#endif
+#if 0
+ if (pri < HIGH_PRIORITY)
+ {
+ if (imagep->mBoostPriority)
+ {
+ pri += 4*HIGH_PRIORITY;
+ }
+ }
+#endif
+#if 1
+ if (pri > 0.f && pri < HIGH_PRIORITY)
+ {
+ if (imagep->mLastPacketTimer.getElapsedTimeF32() < 1.f ||
+ imagep->mLastDecodeTime.getElapsedTimeF32() < 1.f)
+ {
+ pri += 1*HIGH_PRIORITY;
+ }
+ }
+#endif
+// if (pri > 0.0f)
+ {
+ display_image_list.insert(std::make_pair(pri, imagep));
+ }
+ }
+
+ static S32 max_count = 50;
+ S32 count = 0;
+ for (display_list_t::iterator iter = display_image_list.begin();
+ iter != display_image_list.end(); iter++)
+ {
+ LLViewerImage* imagep = iter->second;
+ S32 hilite = 0;
+ F32 pri = iter->first;
+ if (pri >= 1 * HIGH_PRIORITY)
+ {
+ hilite = (S32)((pri+1) / HIGH_PRIORITY) - 1;
+ }
+ if ((hilite || count < max_count-10) && (count < max_count))
+ {
+ if (addBar(imagep, hilite))
+ {
+ count++;
+ }
+ }
+ }
+
+ sortChildren(SortTextureBars());
+
+ mGLTexMemBar = new LLGLTexMemBar("gl texmem bar");
+ addChild(mGLTexMemBar);
+
+ reshape(mRect.getWidth(), mRect.getHeight(), TRUE);
+
+ /*
+ count = gImageList.getNumImages();
+ char info_string[512];
+ sprintf(info_string, "Global Info:\nTexture Count: %d", count);
+ mInfoTextp->setText(info_string);
+ */
+
+
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ if (viewp->getRect().mBottom < 0)
+ {
+ viewp->setVisible(FALSE);
+ }
+ }
+ }
+
+ LLContainerView::draw();
+
+}
+
+BOOL LLTextureView::addBar(LLViewerImage *imagep, S32 hilite)
+{
+ if (!imagep)
+ {
+ return FALSE;
+ }
+
+ LLTextureBar *barp;
+ LLRect r;
+
+ mNumTextureBars++;
+
+ for (std::vector<LLTextureBar*>::iterator iter = mTextureBars.begin();
+ iter != mTextureBars.end(); iter++)
+ {
+ LLTextureBar* barp = *iter;
+ if (barp->mImagep == imagep)
+ {
+ barp->mHilite = hilite;
+ return FALSE;
+ }
+ }
+
+ barp = new LLTextureBar("texture bar", r);
+ barp->mImagep = imagep;
+ barp->mHilite = hilite;
+
+ addChild(barp);
+ mTextureBars.push_back(barp);
+
+ // Rearrange all child bars.
+ reshape(mRect.getWidth(), mRect.getHeight());
+ return TRUE;
+}
+
+BOOL LLTextureView::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (mask & MASK_SHIFT)
+ {
+ mFreezeView = !mFreezeView;
+ return TRUE;
+ }
+ else if (mask & MASK_CONTROL)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLTextureView::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ return FALSE;
+}
+
+BOOL LLTextureView::handleKey(KEY key, MASK mask, BOOL called_from_parent)
+{
+ if (key == ' ')
+ {
+ mFreezeView = !mFreezeView;
+ return TRUE;
+ }
+ return FALSE;
+}
+
diff --git a/indra/newview/lltextureview.h b/indra/newview/lltextureview.h
new file mode 100644
index 0000000000..e832a55211
--- /dev/null
+++ b/indra/newview/lltextureview.h
@@ -0,0 +1,55 @@
+/**
+ * @file lltextureview.h
+ * @brief LLTextureView class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTEXTUREVIEW_H
+#define LL_LLTEXTUREVIEW_H
+
+#include "lltexturebar.h"
+#include "llcontainerview.h"
+#include "linked_lists.h"
+
+class LLViewerImage;
+
+class LLTextureView : public LLContainerView
+{
+public:
+ LLTextureView(const std::string& name, const LLRect& rect);
+ ~LLTextureView();
+
+ virtual EWidgetType getWidgetType() const;
+ virtual LLString getWidgetTag() const;
+
+ /*virtual*/ void draw();
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+
+ static void addDebugImage(LLViewerImage* image) { sDebugImages.insert(image); }
+ static void removeDebugImage(LLViewerImage* image) { sDebugImages.insert(image); }
+ static void clearDebugImages() { sDebugImages.clear(); }
+
+private:
+ BOOL addBar(LLViewerImage *image, BOOL hilight = FALSE);
+ void removeAllBars();
+
+private:
+ LLTextBox *mInfoTextp;
+
+ std::vector<LLTextureBar*> mTextureBars;
+ U32 mNumTextureBars;
+
+ LLGLTexMemBar* mGLTexMemBar;
+
+ BOOL mFreezeView;
+
+public:
+ static std::set<LLViewerImage*> sDebugImages;
+};
+
+extern LLTextureView *gTextureView;
+#endif // LL_TEXTURE_VIEW_H
diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp
new file mode 100644
index 0000000000..f9ff070289
--- /dev/null
+++ b/indra/newview/lltool.cpp
@@ -0,0 +1,151 @@
+/**
+ * @file lltool.cpp
+ * @brief LLTool class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltool.h"
+
+#include "indra_constants.h"
+#include "llerror.h"
+#include "llview.h"
+
+#include "llviewerwindow.h"
+#include "lltoolcomp.h"
+#include "llfocusmgr.h"
+#include "llagent.h"
+#include "llviewborder.h"
+
+extern BOOL gDebugClicks;
+
+//static
+const LLString LLTool::sNameNull("null");
+
+LLTool::LLTool( const LLString& name, LLToolComposite* composite ) :
+ mComposite( composite ),
+ mName(name)
+{
+}
+
+LLTool::~LLTool()
+{
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ llwarns << "Tool deleted holding mouse capture. Mouse capture removed." << llendl;
+ gFocusMgr.removeMouseCaptureWithoutCallback( this );
+ }
+}
+
+
+BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLTool left mouse down" << llendl;
+ }
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleMouseDown" << llendl;
+ gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN);
+ return TRUE;
+}
+
+BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLTool left mouse up" << llendl;
+ }
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleMouseUp" << llendl;
+ gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP);
+ return TRUE;
+}
+
+BOOL LLTool::handleHover(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by a tool" << llendl;
+ // by default, do nothing, say we handled it
+ return TRUE;
+}
+
+BOOL LLTool::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleScrollWheel" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleDoubleClick(S32 x,S32 y,MASK mask)
+{
+ // llinfos << "LLTool::handleDoubleClick" << llendl;
+ // by default, pretend it's a left click
+ return FALSE;
+}
+
+BOOL LLTool::handleRightMouseDown(S32 x,S32 y,MASK mask)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleRightMouseDown" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleRightMouseDown" << llendl;
+ return FALSE;
+}
+
+BOOL LLTool::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ // by default, didn't handle it
+ // llinfos << "LLTool::handleToolTip" << llendl;
+ return FALSE;
+}
+
+void LLTool::setMouseCapture( BOOL b )
+{
+ if( b )
+ {
+ gViewerWindow->setMouseCapture(mComposite ? mComposite : this, &LLTool::onMouseCaptureLost );
+ }
+ else
+ if( hasMouseCapture() )
+ {
+ gViewerWindow->setMouseCapture( NULL, NULL );
+ }
+}
+
+// virtual
+void LLTool::draw()
+{ }
+
+BOOL LLTool::hasMouseCapture()
+{
+ return gViewerWindow->hasMouseCapture(mComposite ? mComposite : this);
+}
+
+BOOL LLTool::handleKey(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+
+// static
+void LLTool::onMouseCaptureLost( LLMouseHandler* old_captor )
+{
+ LLTool* self = (LLTool*) old_captor;
+ if( self->mComposite )
+ {
+ self->mComposite->onMouseCaptureLost();
+ }
+ else
+ {
+ self->onMouseCaptureLost();
+ }
+}
diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h
new file mode 100644
index 0000000000..f0e1b170bd
--- /dev/null
+++ b/indra/newview/lltool.h
@@ -0,0 +1,84 @@
+/**
+ * @file lltool.h
+ * @brief LLTool class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOL_H
+#define LL_LLTOOL_H
+
+#include "llkeyboard.h"
+#include "llmousehandler.h"
+#include "llcoord.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLViewerObject;
+class LLToolComposite;
+class LLView;
+class LLPanel;
+
+class LLTool
+: public LLMouseHandler
+{
+public:
+ LLTool( const LLString& name, LLToolComposite* composite = NULL );
+ virtual ~LLTool();
+
+ // Hack to support LLFocusMgr
+ virtual BOOL isView() { return FALSE; }
+
+ // Virtual functions inherited from LLMouseHandler
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen);
+ // Return FALSE to allow context menu to be shown.
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+ { *local_x = screen_x; *local_y = screen_y; }
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+ { *screen_x = local_x; *screen_y = local_y; }
+
+ virtual const LLString& getName() const { return mName; }
+
+ // New virtual functions
+ virtual LLViewerObject* getEditingObject() { return NULL; }
+ virtual LLVector3d getEditingPointGlobal() { return LLVector3d(); }
+ virtual BOOL isEditing() { return (getEditingObject() != NULL); }
+ virtual void stopEditing() {}
+
+ virtual BOOL clipMouseWhenDown() { return TRUE; }
+
+ virtual void handleSelect() { } // do stuff when your tool is selected
+ virtual void handleDeselect() { } // clean up when your tool is deselected
+
+ // isAlwaysRendered() - return true if this is a tool that should
+ // always be rendered regardless of selection.
+ virtual BOOL isAlwaysRendered() { return FALSE; }
+
+ virtual void render() {} // draw tool specific 3D content in world
+ virtual void draw(); // draw tool specific 2D overlay
+
+ virtual BOOL handleKey(KEY key, MASK mask);
+
+ // Note: NOT virtual. Subclasses should call this version.
+ void setMouseCapture(BOOL b);
+ BOOL hasMouseCapture();
+ static void onMouseCaptureLost(LLMouseHandler* old_captor);
+ virtual void onMouseCaptureLost() {} // override this one as needed.
+
+protected:
+ LLToolComposite* mComposite; // Composite will handle mouse captures.
+ LLString mName;
+
+public:
+ static const LLString sNameNull;
+};
+
+#endif
diff --git a/indra/newview/lltoolbar.cpp b/indra/newview/lltoolbar.cpp
new file mode 100644
index 0000000000..e6cfefa62d
--- /dev/null
+++ b/indra/newview/lltoolbar.cpp
@@ -0,0 +1,436 @@
+/**
+ * @file lltoolbar.cpp
+ * @author James Cook, Richard Nelson
+ * @brief Large friendly buttons at bottom of screen.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolbar.h"
+
+#include "imageids.h"
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+#include "llviewercontrol.h"
+#include "llmenucommands.h"
+#include "llimview.h"
+#include "lluiconstants.h"
+#include "llvoavatar.h"
+#include "lltooldraganddrop.h"
+#include "llinventoryview.h"
+#include "llfloaterfriends.h"
+#include "llfloatersnapshot.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewermenu.h"
+#include "llfirstuse.h"
+#include "llviewerparcelmgr.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "lltoolgrab.h"
+
+#if LL_DARWIN
+
+ #include "llresizehandle.h"
+
+ // This class draws like an LLResizeHandle but has no interactivity.
+ // It's just there to provide a cue to the user that the lower right corner of the window functions as a resize handle.
+ class LLFakeResizeHandle : public LLResizeHandle
+ {
+ public:
+ LLFakeResizeHandle(const LLString& name, const LLRect& rect, S32 min_width, S32 min_height, ECorner corner = RIGHT_BOTTOM )
+ : LLResizeHandle(name, rect, min_width, min_height, corner )
+ {
+
+ }
+
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) { return FALSE; };
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; };
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask) { return FALSE; };
+
+ };
+
+#endif // LL_DARWIN
+
+//
+// Globals
+//
+
+LLToolBar *gToolBar = NULL;
+S32 TOOL_BAR_HEIGHT = 20;
+
+//
+// Statics
+//
+F32 LLToolBar::sInventoryAutoOpenTime = 1.f;
+
+//
+// Functions
+//
+
+LLToolBar::LLToolBar(const std::string& name, const LLRect& r)
+: LLPanel(name, r, BORDER_NO)
+#if LL_DARWIN
+ , mResizeHandle(NULL)
+#endif // LL_DARWIN
+{
+ setIsChrome(TRUE);
+ setFollows( FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM );
+
+ gUICtrlFactory->buildPanel(this, "panel_toolbar.xml");
+ mIsFocusRoot = TRUE;
+
+}
+
+
+BOOL LLToolBar::postBuild()
+{
+ childSetAction("im_btn", onClickIM, this);
+ childSetControlName("im_btn", "ShowIM");
+
+ childSetAction("chat_btn", onClickChat, this);
+ childSetControlName("chat_btn", "ChatVisible");
+
+ childSetAction("friends_btn", onClickFriends, this);
+ childSetControlName("friends_btn", "ShowFriends");
+
+ childSetAction("appearance_btn", onClickAppearance, this);
+ childSetControlName("appearance_btn", "");
+
+ childSetAction("clothing_btn", onClickClothing, this);
+ childSetControlName("clothing_btn", "ClothingBtnState");
+
+ childSetAction("fly_btn", onClickFly, this);
+ childSetControlName("fly_btn", "FlyBtnState");
+
+ childSetAction("sit_btn", onClickSit, this);
+ childSetControlName("sit_btn", "SitBtnState");
+
+ childSetAction("snapshot_btn", onClickSnapshot, this);
+ childSetControlName("snapshot_btn", "");
+
+ childSetAction("directory_btn", onClickDirectory, this);
+ childSetControlName("directory_btn", "ShowDirectory");
+
+ childSetAction("build_btn", onClickBuild, this);
+ childSetControlName("build_btn", "BuildBtnState");
+
+ childSetAction("radar_btn", onClickRadar, this);
+ childSetControlName("radar_btn", "ShowMiniMap");
+
+ childSetAction("map_btn", onClickMap, this);
+ childSetControlName("map_btn", "ShowWorldMap");
+
+ childSetAction("inventory_btn", onClickInventory, this);
+ childSetControlName("inventory_btn", "ShowInventory");
+
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *view = *child_iter;
+ if(view->getWidgetType() == WIDGET_TYPE_BUTTON)
+ {
+ LLButton* btn = (LLButton*)view;
+ btn->setSoundFlags(LLView::SILENT);
+ }
+ }
+
+#if LL_DARWIN
+ if(mResizeHandle == NULL)
+ {
+ LLRect rect(0, 0, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT);
+ mResizeHandle = new LLFakeResizeHandle(LLString(""), rect, RESIZE_HANDLE_WIDTH, RESIZE_HANDLE_HEIGHT);
+ this->addChildAtEnd(mResizeHandle);
+ }
+#endif // LL_DARWIN
+
+ layoutButtons();
+
+ return TRUE;
+}
+
+LLToolBar::~LLToolBar()
+{
+ // LLView destructor cleans up children
+}
+
+
+BOOL LLToolBar::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg)
+{
+ LLButton* inventory_btn = LLUICtrlFactory::getButtonByName(this, "inventory_btn");
+ if (!inventory_btn) return FALSE;
+
+ LLInventoryView* active_inventory = LLInventoryView::getActiveInventory();
+
+ if(active_inventory && active_inventory->getVisible())
+ {
+ mInventoryAutoOpen = FALSE;
+ }
+ else if (inventory_btn->getRect().pointInRect(x, y))
+ {
+ if (mInventoryAutoOpen)
+ {
+ if (!(active_inventory && active_inventory->getVisible()) &&
+ mInventoryAutoOpenTimer.getElapsedTimeF32() > sInventoryAutoOpenTime)
+ {
+ LLInventoryView::showAgentInventory();
+ }
+ }
+ else
+ {
+ mInventoryAutoOpen = TRUE;
+ mInventoryAutoOpenTimer.reset();
+ }
+ }
+
+ return LLPanel::handleDragAndDrop(x, y, mask, drop, cargo_type, cargo_data, accept, tooltip_msg);
+}
+
+// static
+void LLToolBar::toggle(void*)
+{
+ BOOL show = gSavedSettings.getBOOL("ShowToolBar");
+ gSavedSettings.setBOOL("ShowToolBar", !show);
+ gToolBar->setVisible(!show);
+}
+
+
+// static
+BOOL LLToolBar::visible(void*)
+{
+ return gToolBar->getVisible();
+}
+
+
+void LLToolBar::layoutButtons()
+{
+ // Always spans whole window. JC
+ const S32 FUDGE_WIDTH_OF_SCREEN = 4;
+ S32 width = gViewerWindow->getWindowWidth() + FUDGE_WIDTH_OF_SCREEN;
+ S32 count = getChildCount();
+ S32 pad = 2;
+
+#if LL_DARWIN
+ // this function may be called before postBuild(), in which case mResizeHandle won't have been set up yet.
+ if(mResizeHandle != NULL)
+ {
+ // a resize handle has been added as a child, increasing the count by one.
+ count--;
+
+ if(!gViewerWindow->getWindow()->getFullscreen())
+ {
+ // Only when running in windowed mode on the Mac, leave room for a resize widget on the right edge of the bar.
+ width -= RESIZE_HANDLE_WIDTH;
+
+ LLRect r;
+ r.mLeft = width - pad;
+ r.mBottom = 0;
+ r.mRight = r.mLeft + RESIZE_HANDLE_WIDTH;
+ r.mTop = r.mBottom + RESIZE_HANDLE_HEIGHT;
+ mResizeHandle->setRect(r);
+ mResizeHandle->setVisible(TRUE);
+ }
+ else
+ {
+ mResizeHandle->setVisible(FALSE);
+ }
+ }
+#endif // LL_DARWIN
+
+ // We actually want to extend "pad" pixels off the right edge of the
+ // screen, such that the rightmost button is aligned.
+ F32 segment_width = (F32)(width + pad) / (F32)count;
+ S32 btn_width = lltrunc(segment_width - pad);
+
+ // Evenly space all views
+ S32 height = -1;
+ S32 i = count - 1;
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *btn_view = *child_iter;
+ if(btn_view->getWidgetType() == WIDGET_TYPE_BUTTON)
+ {
+ if (height < 0)
+ {
+ height = btn_view->getRect().getHeight();
+ }
+ S32 x = llround(i*segment_width);
+ S32 y = 0;
+ LLRect r;
+ r.setOriginAndSize(x, y, btn_width, height);
+ btn_view->setRect(r);
+ i--;
+ }
+ }
+}
+
+
+// virtual
+void LLToolBar::reshape(S32 width, S32 height, BOOL called_from_parent)
+{
+ LLPanel::reshape(width, height, called_from_parent);
+
+ layoutButtons();
+}
+
+
+// Per-frame updates of visibility
+void LLToolBar::refresh()
+{
+ BOOL show = gSavedSettings.getBOOL("ShowToolBar");
+ BOOL mouselook = gAgent.cameraMouselook();
+ setVisible(show && !mouselook);
+
+ // Clothing button updated inside LLFloaterClothing
+
+ childSetEnabled("fly_btn", gAgent.canFly() || gAgent.getFlying() );
+
+ childSetEnabled("build_btn", gParcelMgr->agentCanBuild() );
+
+ // Check to see if we're in build mode
+ BOOL build_mode = gToolMgr->inEdit();
+ // And not just clicking on a scripted object
+ if (gToolGrab->getHideBuildHighlight())
+ {
+ build_mode = FALSE;
+ }
+ gSavedSettings.setBOOL("BuildBtnState", build_mode);
+}
+
+
+// static
+void LLToolBar::onClickIM(void* user_data)
+{
+ if(gIMView->getFloaterOpen())
+ {
+ // this is if we want Ctrl-T to be simply a toggle
+ // gIMView->setFloaterOpen( FALSE );
+ // three-state behavior follows
+ if(gFocusMgr.childHasKeyboardFocus(gIMView->getFloater()))
+ {
+ gIMView->setFloaterOpen( FALSE );
+ } else {
+ gIMView->getFloater()->setFocus( TRUE );
+ }
+ }
+ else
+ {
+ gIMView->setFloaterOpen( TRUE );
+ }
+}
+
+
+// static
+void LLToolBar::onClickChat(void* user_data)
+{
+ handle_chat(NULL);
+}
+
+
+// static
+void LLToolBar::onClickFriends(void*)
+{
+ LLFloaterFriends::toggle();
+}
+
+
+// static
+void LLToolBar::onClickAppearance(void*)
+{
+ if (gAgent.getWearablesLoaded())
+ {
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+}
+
+
+// static
+void LLToolBar::onClickClothing(void*)
+{
+ handle_clothing(NULL);
+}
+
+
+// static
+void LLToolBar::onClickFly(void*)
+{
+ gAgent.toggleFlying();
+}
+
+
+// static
+void LLToolBar::onClickSit(void*)
+{
+ if (!(gAgent.getControlFlags() & AGENT_CONTROL_SIT_ON_GROUND))
+ {
+ // sit down
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+
+ // Might be first sit
+ LLFirstUse::useSit();
+ }
+ else
+ {
+ // stand up
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ }
+}
+
+
+// static
+void LLToolBar::onClickSnapshot(void*)
+{
+ LLFloaterSnapshot::show (0);
+}
+
+
+// static
+void LLToolBar::onClickDirectory(void*)
+{
+ handle_find(NULL);
+}
+
+
+// static
+void LLToolBar::onClickBuild(void*)
+{
+ toggle_build_mode();
+}
+
+
+// static
+void LLToolBar::onClickRadar(void*)
+{
+ handle_mini_map(NULL);
+}
+
+
+// static
+void LLToolBar::onClickMap(void*)
+{
+ handle_map(NULL);
+}
+
+
+// static
+void LLToolBar::onClickInventory(void*)
+{
+ handle_inventory(NULL);
+}
+
diff --git a/indra/newview/lltoolbar.h b/indra/newview/lltoolbar.h
new file mode 100644
index 0000000000..f9eee1d4fb
--- /dev/null
+++ b/indra/newview/lltoolbar.h
@@ -0,0 +1,76 @@
+/**
+ * @file lltoolbar.h
+ * @brief Large friendly buttons at bottom of screen.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLBAR_H
+#define LL_LLTOOLBAR_H
+
+#include "llpanel.h"
+
+#include "llframetimer.h"
+
+// "Constants" loaded from settings.xml at start time
+extern S32 TOOL_BAR_HEIGHT;
+
+#if LL_DARWIN
+ class LLFakeResizeHandle;
+#endif // LL_DARWIN
+
+class LLToolBar
+: public LLPanel
+{
+public:
+ LLToolBar(const std::string& name, const LLRect& rect );
+ ~LLToolBar();
+
+ /*virtual*/ BOOL postBuild();
+
+ /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EDragAndDropType cargo_type,
+ void* cargo_data,
+ EAcceptance* accept,
+ LLString& tooltip_msg);
+
+ /*virtual*/ void reshape(S32 width, S32 height, BOOL called_from_parent);
+
+ static void toggle(void*);
+ static BOOL visible(void*);
+
+ // Move buttons to appropriate locations based on rect.
+ void layoutButtons();
+
+ // Per-frame refresh call
+ void refresh();
+
+ // callbacks
+ static void onClickIM(void*);
+ static void onClickChat(void* data);
+ static void onClickFriends(void* data);
+ static void onClickAppearance(void* data);
+ static void onClickClothing(void* data);
+ static void onClickFly(void*);
+ static void onClickSit(void*);
+ static void onClickSnapshot(void* data);
+ static void onClickDirectory(void* data);
+ static void onClickBuild(void* data);
+ static void onClickRadar(void* data);
+ static void onClickMap(void* data);
+ static void onClickInventory(void* data);
+
+ static F32 sInventoryAutoOpenTime;
+
+private:
+ BOOL mInventoryAutoOpen;
+ LLFrameTimer mInventoryAutoOpenTimer;
+#if LL_DARWIN
+ LLFakeResizeHandle *mResizeHandle;
+#endif // LL_DARWIN
+};
+
+extern LLToolBar *gToolBar;
+
+#endif
diff --git a/indra/newview/lltoolbrush.cpp b/indra/newview/lltoolbrush.cpp
new file mode 100644
index 0000000000..8255008aa0
--- /dev/null
+++ b/indra/newview/lltoolbrush.cpp
@@ -0,0 +1,585 @@
+/**
+ * @file lltoolbrush.cpp
+ * @brief Implementation of the toolbrushes
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolbrush.h"
+#include "lltoolselectland.h"
+
+#include "llgl.h"
+
+#include "message.h"
+
+#include "llagent.h"
+#include "llcallbacklist.h"
+#include "llviewercontrol.h"
+#include "llfloatertools.h"
+#include "llregionposition.h"
+#include "llstatusbar.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llparcel.h"
+
+#include "llglheaders.h"
+
+const std::string REGION_BLOCKS_TERRAFORM_MSG = "This region does not allow terraforming.\n"
+ "You will need to buy land in another part of the world to terraform it.";
+
+// Globals
+LLToolBrushLand *gToolLand = NULL;
+
+///============================================================================
+/// Local function declarations, constants, enums, and typedefs
+///============================================================================
+
+const S32 LAND_BRUSH_SIZE_COUNT = 3;
+const F32 LAND_BRUSH_SIZE[LAND_BRUSH_SIZE_COUNT] = {1.0f, 2.0f, 4.0f};
+const S32 LAND_STEPS = 3;
+const F32 LAND_METERS_PER_SECOND = 1.0f;
+enum
+{
+ E_LAND_LEVEL = 0,
+ E_LAND_RAISE = 1,
+ E_LAND_LOWER = 2,
+ E_LAND_SMOOTH = 3,
+ E_LAND_NOISE = 4,
+ E_LAND_REVERT = 5,
+ E_LAND_INVALID = 6,
+};
+const LLColor4 OVERLAY_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
+
+///============================================================================
+/// Class LLToolBrushLand
+///============================================================================
+
+// constructor
+LLToolBrushLand::LLToolBrushLand()
+: LLTool("Land"),
+ mStartingZ( 0.0f ),
+ mMouseX( 0 ),
+ mMouseY(0),
+ mGotHover(FALSE),
+ mLastShowParcelOwners(FALSE),
+ mBrushSelected(FALSE)
+{
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+}
+
+void LLToolBrushLand::modifyLandAtPointGlobal(const LLVector3d &pos_global,
+ MASK mask)
+{
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ determineAffectedRegions(mLastAffectedRegions, pos_global);
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ break;
+ default:
+ action = E_LAND_INVALID;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(pos_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ F32 seconds = 1.0f / gFPSClamped;
+ F32 x_pos = (F32)pos_region.mV[VX];
+ F32 y_pos = (F32)pos_region.mV[VY];
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, x_pos );
+ msg->addF32Fast(_PREHASH_South, y_pos );
+ msg->addF32Fast(_PREHASH_East, x_pos );
+ msg->addF32Fast(_PREHASH_North, y_pos );
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::modifyLandInSelectionGlobal()
+{
+ if (gParcelMgr->selectionEmpty())
+ {
+ return;
+ }
+
+ if (gToolMgr->getCurrentTool(gKeyboard->currentMask(TRUE)) == gToolParcel)
+ {
+ // selecting land, don't do anything
+ return;
+ }
+
+ LLVector3d min;
+ LLVector3d max;
+
+ gParcelMgr->getSelection(min, max);
+
+ S32 radioAction = gSavedSettings.getS32("RadioLandBrushAction");
+
+ mLastAffectedRegions.removeAllNodes();
+
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(min.mdV[VX], max.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], min.mdV[VY], 0));
+ determineAffectedRegions(mLastAffectedRegions, LLVector3d(max.mdV[VX], max.mdV[VY], 0));
+
+ LLRegionPosition mid_point_region((min + max) * 0.5);
+ LLViewerRegion* center_region = mid_point_region.getRegion();
+ if (center_region)
+ {
+ LLVector3 pos_region = mid_point_region.getPositionRegion();
+ U32 grids = center_region->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = center_region->getLand().getZ(i+j*grids);
+ }
+ else
+ {
+ mStartingZ = 0.f;
+ }
+
+ // Stop if our selection include a no-terraform region
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return;
+ }
+ }
+
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ //BOOL is_changed = FALSE;
+ LLVector3 min_region = regionp->getPosRegionFromGlobal(min);
+ LLVector3 max_region = regionp->getPosRegionFromGlobal(max);
+
+ min_region.clamp(0.f, regionp->getWidth());
+ max_region.clamp(0.f, regionp->getWidth());
+ F32 seconds = 1.0f;
+
+ LLSurface &land = regionp->getLand();
+ char action = E_LAND_LEVEL;
+ switch (radioAction)
+ {
+ case 0:
+ // // average toward mStartingZ
+ action = E_LAND_LEVEL;
+ seconds = 1.f;
+ break;
+ case 1:
+ action = E_LAND_RAISE;
+ break;
+ case 2:
+ action = E_LAND_LOWER;
+ break;
+ case 3:
+ action = E_LAND_SMOOTH;
+ seconds = 10.f;
+ break;
+ case 4:
+ action = E_LAND_NOISE;
+ seconds = 0.5f;
+ break;
+ case 5:
+ action = E_LAND_REVERT;
+ seconds = 0.5f;
+ break;
+ default:
+ //action = E_LAND_INVALID;
+ //seconds = 0.0f;
+ return;
+ break;
+ }
+
+ // Don't send a message to the region if nothing changed.
+ //if(!is_changed) continue;
+
+ // Now to update the patch information so it will redraw correctly.
+ LLSurfacePatch *patchp= land.resolvePatchRegion(min_region);
+ if (patchp)
+ {
+ patchp->dirtyZ();
+ }
+
+ // Also force the property lines to update, normals to recompute, etc.
+ regionp->forceUpdate();
+
+ // tell the simulator what we've done
+ U8 brush_size = (U8)mBrushIndex;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ModifyLand);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ModifyBlock);
+ msg->addU8Fast(_PREHASH_Action, (U8)action);
+ msg->addU8Fast(_PREHASH_BrushSize, brush_size);
+ msg->addF32Fast(_PREHASH_Seconds, seconds);
+ msg->addF32Fast(_PREHASH_Height, mStartingZ);
+
+ BOOL parcel_selected = gParcelMgr->getWholeParcelSelected();
+ LLParcel* selected_parcel = gParcelMgr->getSelectedParcel();
+
+ if (parcel_selected && selected_parcel)
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, selected_parcel->getLocalID());
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, -1);
+ msg->addF32Fast(_PREHASH_West, min_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, min_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, max_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, max_region.mV[VY] );
+ }
+
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+void LLToolBrushLand::brush( void )
+{
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( mMouseX, mMouseY, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ modifyLandAtPointGlobal(spot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+BOOL LLToolBrushLand::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Find the z value of the initial click.
+ LLVector3d spot;
+ if( gViewerWindow->mousePointOnLandGlobal( x, y, &spot ) )
+ {
+ // Round to nearest X,Y grid
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ LLRegionPosition region_position( spot );
+ LLViewerRegion* regionp = region_position.getRegion();
+
+ if (!canTerraform(regionp))
+ {
+ alertNoTerraform(regionp);
+ return TRUE;
+ }
+
+ LLVector3 pos_region = region_position.getPositionRegion();
+ U32 grids = regionp->getLand().mGridsPerEdge;
+ S32 i = llclamp( (S32)pos_region.mV[VX], 0, (S32)grids );
+ S32 j = llclamp( (S32)pos_region.mV[VY], 0, (S32)grids );
+ mStartingZ = regionp->getLand().getZ(i+j*grids);
+ mMouseX = x;
+ mMouseY = y;
+ gIdleCallbacks.addFunction( &LLToolBrushLand::onIdle, (void*)this );
+ setMouseCapture( TRUE );
+
+ gParcelMgr->setSelectionVisible(FALSE);
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+BOOL LLToolBrushLand::handleHover( S32 x, S32 y, MASK mask )
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolBrushLand ("
+ << (hasMouseCapture() ? "active":"inactive")
+ << ")" << llendl;
+ mMouseX = x;
+ mMouseY = y;
+ mGotHover = TRUE;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLLAND);
+ return TRUE;
+}
+
+BOOL LLToolBrushLand::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ mLastAffectedRegions.removeAllNodes();
+ if( hasMouseCapture() )
+ {
+ // Release the mouse
+ setMouseCapture( FALSE );
+
+ gParcelMgr->setSelectionVisible(TRUE);
+
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, (void*)this );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+void LLToolBrushLand::handleSelect()
+{
+ gEditMenuHandler = this;
+
+ gFloaterTools->setStatusText("Click and hold to modify land");
+// if (!mBrushSelected)
+ {
+ mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
+ gSavedSettings.setBOOL("ShowParcelOwners", TRUE);
+ mBrushSelected = TRUE;
+ }
+}
+
+
+void LLToolBrushLand::handleDeselect()
+{
+ if( gEditMenuHandler == this )
+ {
+ gEditMenuHandler = NULL;
+ }
+ gFloaterTools->setStatusText("");
+ gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
+ gParcelMgr->setSelectionVisible(TRUE);
+ mBrushSelected = FALSE;
+}
+
+// Draw the area that will be affected.
+void LLToolBrushLand::render()
+{
+ if(mGotHover)
+ {
+ //llinfos << "LLToolBrushLand::render()" << llendl;
+ LLVector3d spot;
+ if(gViewerWindow->mousePointOnLandGlobal(mMouseX, mMouseY, &spot))
+ {
+ spot.mdV[VX] = floor( spot.mdV[VX] + 0.5 );
+ spot.mdV[VY] = floor( spot.mdV[VY] + 0.5 );
+
+ mBrushIndex = gSavedSettings.getS32("RadioLandBrushSize");
+ LLLinkedList<LLViewerRegion> regions;
+ determineAffectedRegions(regions, spot);
+
+ // Now, for each region, render the overlay
+ LLVector3 pos_world = gAgent.getRegion()->getPosRegionFromGlobal(spot);
+ for(LLViewerRegion* region = regions.getFirstData();
+ region != NULL;
+ region = regions.getNextData())
+ {
+ renderOverlay(region->getLand(),
+ region->getPosRegionFromGlobal(spot),
+ pos_world);
+ }
+ }
+ mGotHover = FALSE;
+ }
+}
+
+void LLToolBrushLand::renderOverlay(LLSurface& land, const LLVector3& pos_region,
+ const LLVector3& pos_world)
+{
+ glMatrixMode(GL_MODELVIEW);
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest mDepthTest(GL_TRUE);
+ glPushMatrix();
+ glColor4fv(OVERLAY_COLOR.mV);
+ glTranslatef(0.0f, 0.0f, 1.0f);
+ //glPushMatrix();
+ //glTranslatef(spot.mV[VX], spot.mV[VY], 100.0f);
+ //gl_rect_2d(0, 10, 10, 0);
+ //glPopMatrix();
+ S32 i = (S32) pos_region.mV[VX];
+ S32 j = (S32) pos_region.mV[VY];
+ S32 half_edge = llfloor(LAND_BRUSH_SIZE[mBrushIndex]);
+ //F32 dz = 0.0f;
+ //S32 dist = 0;
+ glBegin(GL_POINTS);
+ for(S32 di = -half_edge; di <= half_edge; di++)
+ {
+ if((i+di) < 0 || (i+di) >= (S32)land.mGridsPerEdge) continue;
+ for(S32 dj = -half_edge; dj <= half_edge; dj++)
+ {
+ if( (j+dj) < 0 || (j+dj) >= (S32)land.mGridsPerEdge ) continue;
+ glVertex3f(pos_world.mV[VX] + di, pos_world.mV[VY] + dj,
+ land.getZ((i+di)+(j+dj)*land.mGridsPerEdge));
+ }
+ }
+ glEnd();
+ glPopMatrix();
+}
+
+void LLToolBrushLand::determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
+ const LLVector3d& spot ) const
+{
+ LLVector3d corner(spot);
+ corner.mdV[VX] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ corner.mdV[VY] -= (LAND_BRUSH_SIZE[mBrushIndex] / 2);
+ LLViewerRegion* region = NULL;
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VX] += LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+ corner.mdV[VY] -= LAND_BRUSH_SIZE[mBrushIndex];
+ region = gWorldPointer->getRegionFromPosGlobal(corner);
+ if(region && !regions.checkData(region))
+ {
+ regions.addData(region);
+ }
+}
+
+// static
+void LLToolBrushLand::onIdle( void* brush_tool )
+{
+ LLToolBrushLand* self = reinterpret_cast<LLToolBrushLand*>(brush_tool);
+
+ if( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) == self )
+ {
+ self->brush();
+ }
+ else
+ {
+ gIdleCallbacks.deleteFunction( &LLToolBrushLand::onIdle, self );
+ }
+}
+
+void LLToolBrushLand::onMouseCaptureLost()
+{
+ gIdleCallbacks.deleteFunction(&LLToolBrushLand::onIdle, this);
+}
+
+// static
+void LLToolBrushLand::undo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_UndoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+void LLToolBrushLand::redo()
+{
+ for(LLViewerRegion* regionp = mLastAffectedRegions.getFirstData();
+ regionp != NULL;
+ regionp = mLastAffectedRegions.getNextData())
+ {
+ gMessageSystem->newMessageFast(_PREHASH_RedoLand);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->sendMessage(regionp->getHost());
+ }
+}
+
+// static
+bool LLToolBrushLand::canTerraform(LLViewerRegion* regionp) const
+{
+ if (!regionp) return false;
+ if (regionp->canManageEstate()) return true;
+ return !(regionp->getRegionFlags() & REGION_FLAGS_BLOCK_TERRAFORM);
+}
+
+// static
+void LLToolBrushLand::alertNoTerraform(LLViewerRegion* regionp)
+{
+ if (!regionp) return;
+
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = regionp->getName();
+ gViewerWindow->alertXml("RegionNoTerraforming", args);
+
+}
+
+///============================================================================
+/// Local function definitions
+///============================================================================
diff --git a/indra/newview/lltoolbrush.h b/indra/newview/lltoolbrush.h
new file mode 100644
index 0000000000..3cae7559a3
--- /dev/null
+++ b/indra/newview/lltoolbrush.h
@@ -0,0 +1,88 @@
+/**
+ * @file lltoolbrush.h
+ * @brief toolbrush class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLBRUSH_H
+#define LL_LLTOOLBRUSH_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lleditmenuhandler.h"
+
+class LLSurface;
+class LLVector3d;
+class LLViewerRegion;
+
+template<class DATA_TYPE> class LLLinkedList;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLToolBrushLand
+//
+// A toolbrush that modifies the land.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLToolBrushLand : public LLTool, public LLEditMenuHandler
+{
+public:
+ LLToolBrushLand();
+
+ // x,y in window coords, 0,0 = left,bot
+ virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask );
+ virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ // isAlwaysRendered() - return true if this is a tool that should
+ // always be rendered regardless of selection.
+ virtual BOOL isAlwaysRendered() { return TRUE; }
+
+ // Draw the area that will be affected.
+ virtual void render();
+
+ // on Idle is where the land modification actually occurs
+ static void onIdle(void* brush_tool);
+
+ void onMouseCaptureLost();
+
+ void modifyLandInSelectionGlobal();
+ virtual void undo();
+ virtual BOOL canUndo() { return TRUE; }
+
+ virtual void redo();
+ virtual BOOL canRedo() { return FALSE; }
+
+
+protected:
+ void brush( void );
+ void modifyLandAtPointGlobal( const LLVector3d &spot, MASK mask );
+
+ void determineAffectedRegions(LLLinkedList<LLViewerRegion>& regions,
+ const LLVector3d& spot) const;
+ void renderOverlay(LLSurface& land, const LLVector3& pos_region,
+ const LLVector3& pos_world);
+
+ // Does region allow terraform, or are we a god?
+ bool canTerraform(LLViewerRegion* regionp) const;
+
+ // Modal dialog that you can't terraform the region
+ void alertNoTerraform(LLViewerRegion* regionp);
+
+protected:
+ F32 mStartingZ;
+ S32 mMouseX;
+ S32 mMouseY;
+ S32 mBrushIndex;
+ BOOL mGotHover;
+ BOOL mLastShowParcelOwners;
+ BOOL mBrushSelected;
+ LLLinkedList<LLViewerRegion> mLastAffectedRegions;
+};
+
+extern LLToolBrushLand *gToolLand;
+
+#endif // LL_LLTOOLBRUSH_H
diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp
new file mode 100644
index 0000000000..2b8d4ed73c
--- /dev/null
+++ b/indra/newview/lltoolcomp.cpp
@@ -0,0 +1,673 @@
+/**
+ * @file lltoolcomp.cpp
+ * @brief Composite tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolcomp.h"
+
+#include "llgl.h"
+#include "indra_constants.h"
+
+#include "llmanip.h"
+#include "llmaniprotate.h"
+#include "llmanipscale.h"
+#include "llmaniptranslate.h"
+#include "llmenugl.h" // for right-click menu hack
+#include "llselectmgr.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolgun.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "lltoolselectrect.h"
+#include "lltoolplacer.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llagent.h"
+#include "llfloatertools.h"
+#include "llviewercontrol.h"
+
+const S32 BUTTON_HEIGHT = 16;
+const S32 BUTTON_WIDTH_SMALL = 32;
+const S32 BUTTON_WIDTH_BIG = 48;
+const S32 HPAD = 4;
+
+// Globals
+LLToolCompTranslate *gToolTranslate = NULL;
+LLToolCompScale *gToolStretch = NULL;
+LLToolCompRotate *gToolRotate = NULL;
+LLToolCompCreate *gToolCreate = NULL;
+LLToolCompGun *gToolGun = NULL;
+
+extern LLControlGroup gSavedSettings;
+
+//-----------------------------------------------------------------------
+// LLToolComposite
+
+//static
+void LLToolComposite::setCurrentTool( LLTool* new_tool )
+{
+ if( mCur != new_tool )
+ {
+ if( mSelected )
+ {
+ mCur->handleDeselect();
+ mCur = new_tool;
+ mCur->handleSelect();
+ }
+ else
+ {
+ mCur = new_tool;
+ }
+ }
+}
+
+LLToolComposite::LLToolComposite(const LLString& name)
+ : LLTool(name),
+ mCur(NULL), mDefault(NULL), mSelected(FALSE),
+ mMouseDown(FALSE), mManip(NULL), mSelectRect(NULL)
+{
+}
+
+// Returns to the default tool
+BOOL LLToolComposite::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = mCur->handleMouseUp( x, y, mask );
+ if( handled )
+ {
+ setCurrentTool( mDefault );
+ }
+ return handled;
+}
+
+void LLToolComposite::onMouseCaptureLost()
+{
+ mCur->onMouseCaptureLost();
+ setCurrentTool( mDefault );
+}
+
+BOOL LLToolComposite::isSelecting()
+{
+ return mCur == mSelectRect;
+}
+
+void LLToolComposite::handleSelect()
+{
+ if (gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ gSelectMgr->promoteSelectionToRoot();
+ }
+ mCur = mDefault;
+ mCur->handleSelect();
+ mSelected = TRUE;
+}
+
+//----------------------------------------------------------------------------
+// LLToolCompTranslate
+//----------------------------------------------------------------------------
+
+LLToolCompTranslate::LLToolCompTranslate()
+ : LLToolComposite("Move")
+{
+ mManip = new LLManipTranslate(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+LLToolCompTranslate::~LLToolCompTranslate()
+{
+ delete mManip;
+ mManip = NULL;
+
+ delete mSelectRect;
+ mSelectRect = NULL;
+}
+
+BOOL LLToolCompTranslate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool( mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompTranslate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompTranslate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolTranslate->mManip->highlightManipulators(x, y);
+ if (!gToolTranslate->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolTranslate->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+ return;
+ }
+
+ if( hit_obj || gToolTranslate->mManip->getHighlightedPart() != LLManip::LL_NO_PART )
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolTranslate->mManip->getHighlightedPart() )
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mManip );
+ gToolTranslate->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mSelectRect );
+ gToolTranslate->mSelectRect->handleMouseDown( x, y, mask );
+
+ //FIXME: add toggle to trigger old click-drag functionality
+ // gToolTranslate->mManip->handleMouseDownOnPart( XY_part, x, y, mask);
+ }
+ }
+ else
+ {
+ gToolTranslate->setCurrentTool( gToolTranslate->mSelectRect );
+ gToolTranslate->mSelectRect->handleMouseDown( x, y, mask);
+ }
+}
+
+BOOL LLToolCompTranslate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolCompTranslate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+
+void LLToolCompTranslate::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// LLToolCompScale
+
+LLToolCompScale::LLToolCompScale()
+ : LLToolComposite("Stretch")
+{
+ mManip = new LLManipScale(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+LLToolCompScale::~LLToolCompScale()
+{
+ delete mManip;
+ delete mSelectRect;
+}
+
+BOOL LLToolCompScale::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool(mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompScale::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompScale::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolStretch->mManip->highlightManipulators(x, y);
+ if (!gToolStretch->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolStretch->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+
+ return;
+ }
+
+ if( hit_obj || gToolStretch->mManip->getHighlightedPart() != LLManip::LL_NO_PART)
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolStretch->mManip->getHighlightedPart() )
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mManip );
+ gToolStretch->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mSelectRect );
+ gToolStretch->mSelectRect->handleMouseDown( x, y, mask );
+ }
+ }
+ else
+ {
+ gToolStretch->setCurrentTool( gToolStretch->mSelectRect );
+ gToolStretch->mCur->handleMouseDown( x, y, mask );
+ }
+}
+
+BOOL LLToolCompScale::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolCompScale::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ //gBuildView->setPropertiesPanelOpen(TRUE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return handleMouseDown(x, y, mask);
+ }
+}
+
+
+void LLToolCompScale::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+//-----------------------------------------------------------------------
+// LLToolCompCreate
+
+LLToolCompCreate::LLToolCompCreate()
+ : LLToolComposite("Create")
+{
+ mPlacer = new LLToolPlacer();
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mPlacer;
+ mDefault = mPlacer;
+ mObjectPlacedOnMouseDown = FALSE;
+}
+
+
+LLToolCompCreate::~LLToolCompCreate()
+{
+ delete mPlacer;
+ delete mSelectRect;
+}
+
+
+BOOL LLToolCompCreate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+ mMouseDown = TRUE;
+
+ if ( !(mask == MASK_SHIFT) && !(mask == MASK_CONTROL) )
+ {
+ setCurrentTool( mPlacer );
+ handled = mPlacer->placeObject( x, y, mask );
+ }
+ else
+ {
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ handled = TRUE;
+ }
+
+ mObjectPlacedOnMouseDown = TRUE;
+
+ return TRUE;
+}
+
+void LLToolCompCreate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ // HACK: Mask off shift and control, so you can't multi-select
+ // multiple objects with the create tool.
+ mask = (mask & ~MASK_SHIFT);
+ mask = (mask & ~MASK_CONTROL);
+
+ gToolCreate->setCurrentTool( gToolCreate->mSelectRect );
+ gToolCreate->mSelectRect->handleMouseDown( x, y, mask);
+}
+
+BOOL LLToolCompCreate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ return handleMouseDown(x, y, mask);
+}
+
+BOOL LLToolCompCreate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if ( mMouseDown && !mObjectPlacedOnMouseDown && !(mask == MASK_SHIFT) && !(mask == MASK_CONTROL) )
+ {
+ setCurrentTool( mPlacer );
+ handled = mPlacer->placeObject( x, y, mask );
+ }
+
+ mObjectPlacedOnMouseDown = FALSE;
+ mMouseDown = FALSE;
+
+ return handled;
+}
+
+//-----------------------------------------------------------------------
+// LLToolCompRotate
+
+LLToolCompRotate::LLToolCompRotate()
+ : LLToolComposite("Rotate")
+{
+ mManip = new LLManipRotate(this);
+ mSelectRect = new LLToolSelectRect(this);
+
+ mCur = mManip;
+ mDefault = mManip;
+}
+
+
+LLToolCompRotate::~LLToolCompRotate()
+{
+ delete mManip;
+ delete mSelectRect;
+}
+
+BOOL LLToolCompRotate::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( !mCur->hasMouseCapture() )
+ {
+ setCurrentTool( mManip );
+ }
+ return mCur->handleHover( x, y, mask );
+}
+
+
+BOOL LLToolCompRotate::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolCompRotate::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ gToolRotate->mManip->highlightManipulators(x, y);
+ if (!gToolRotate->mMouseDown)
+ {
+ // fast click on object, but mouse is already up...just do select
+ gToolRotate->mSelectRect->handleObjectSelection(hit_obj, mask, !gSavedSettings.getBOOL("SelectLinkedSet"), FALSE);
+ return;
+ }
+
+ if( hit_obj || gToolRotate->mManip->getHighlightedPart() != LLManip::LL_NO_PART)
+ {
+ if (gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+ if( LLManip::LL_NO_PART != gToolRotate->mManip->getHighlightedPart() )
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mManip );
+ gToolRotate->mManip->handleMouseDownOnPart( x, y, mask );
+ }
+ else
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mSelectRect );
+ gToolRotate->mSelectRect->handleMouseDown( x, y, mask );
+ }
+ }
+ else
+ {
+ gToolRotate->setCurrentTool( gToolRotate->mSelectRect );
+ gToolRotate->mCur->handleMouseDown( x, y, mask );
+ }
+}
+
+BOOL LLToolCompRotate::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mMouseDown = FALSE;
+ return LLToolComposite::handleMouseUp(x, y, mask);
+}
+
+
+BOOL LLToolCompRotate::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty() && mManip->getHighlightedPart() == LLManip::LL_NO_PART)
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ //gBuildView->setPropertiesPanelOpen(TRUE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return handleMouseDown(x, y, mask);
+ }
+}
+
+
+void LLToolCompRotate::render()
+{
+ mCur->render();
+
+ if( mCur != mManip )
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ mManip->renderGuidelines();
+ }
+}
+
+
+//-----------------------------------------------------------------------
+// LLToolCompGun
+
+LLToolCompGun::LLToolCompGun()
+ : LLToolComposite("Mouselook")
+{
+ mGun = new LLToolGun(this);
+ mGrab = new LLToolGrab(this);
+ mNull = new LLTool("null", this);
+
+ setCurrentTool(mGun);
+ mDefault = mGun;
+}
+
+
+LLToolCompGun::~LLToolCompGun()
+{
+ delete mGun;
+ mGun = NULL;
+
+ delete mGrab;
+ mGrab = NULL;
+
+ delete mNull;
+ mNull = NULL;
+}
+
+BOOL LLToolCompGun::handleHover(S32 x, S32 y, MASK mask)
+{
+ // HACK to make mouselook kick in again after item selected
+ // from context menu
+ if ( mCur == mNull && !gPopupMenuView->getVisible() )
+ {
+ gSelectMgr->deselectAll();
+ setCurrentTool( (LLTool*) mGrab );
+ }
+
+ // Note: if the tool changed, we can't delegate the current mouse event
+ // after the change because tools can modify the mouse during selection and deselection.
+ // Instead we let the current tool handle the event and then make the change.
+ // The new tool will take effect on the next frame.
+
+ mCur->handleHover( x, y, mask );
+
+ // If mouse button not down...
+ if( !gViewerWindow->getLeftMouseDown())
+ {
+ // let ALT switch from gun to grab
+ if ( mCur == mGun && (mask & MASK_ALT) )
+ {
+ setCurrentTool( (LLTool*) mGrab );
+ }
+ else if ( mCur == mGrab && !(mask & MASK_ALT) )
+ {
+ setCurrentTool( (LLTool*) mGun );
+ setMouseCapture(TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolCompGun::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // if the left button is grabbed, don't put up the pie menu
+ if (gAgent.leftButtonGrabbed())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN);
+ return FALSE;
+ }
+
+ // On mousedown, start grabbing
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( (LLTool*) mGrab );
+
+ return gToolGrab->handleMouseDown(x, y, mask);
+}
+
+
+BOOL LLToolCompGun::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ // if the left button is grabbed, don't put up the pie menu
+ if (gAgent.leftButtonGrabbed())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_DOWN);
+ return FALSE;
+ }
+
+ // On mousedown, start grabbing
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( (LLTool*) mGrab );
+
+ return gToolGrab->handleDoubleClick(x, y, mask);
+}
+
+
+BOOL LLToolCompGun::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ /* JC - suppress context menu 8/29/2002
+
+ // On right mouse, go through some convoluted steps to
+ // make the build menu appear.
+ setCurrentTool( (LLTool*) mNull );
+
+ // This should return FALSE, meaning the context menu will
+ // be shown.
+ return FALSE;
+ */
+
+ // Returning true will suppress the context menu
+ return TRUE;
+}
+
+
+BOOL LLToolCompGun::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ gAgent.setControlFlags(AGENT_CONTROL_ML_LBUTTON_UP);
+ setCurrentTool( (LLTool*) mGun );
+ return TRUE;
+}
+
+void LLToolCompGun::onMouseCaptureLost()
+{
+ mCur->onMouseCaptureLost();
+
+ // JC - I don't know if this is necessary. Maybe we could lose capture
+ // if someone ALT-Tab's out when in mouselook.
+ setCurrentTool( (LLTool*) mGun );
+}
+
+void LLToolCompGun::handleSelect()
+{
+ LLToolComposite::handleSelect();
+ setMouseCapture(TRUE);
+}
+
+void LLToolCompGun::handleDeselect()
+{
+ LLToolComposite::handleDeselect();
+ setMouseCapture(FALSE);
+}
+
+
+BOOL LLToolCompGun::handleScrollWheel(S32 x, S32 y, S32 clicks)
+{
+ if (clicks > 0)
+ {
+ gAgent.changeCameraToDefault();
+
+ }
+ return TRUE;
+}
diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h
new file mode 100644
index 0000000000..cb1ec33f1b
--- /dev/null
+++ b/indra/newview/lltoolcomp.h
@@ -0,0 +1,200 @@
+/**
+ * @file lltoolcomp.h
+ * @brief Composite tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLCOMP_H
+#define LL_TOOLCOMP_H
+
+#include "lltool.h"
+
+class LLManip;
+class LLToolSelectRect;
+class LLToolPlacer;
+
+class LLView;
+class LLTextBox;
+
+//-----------------------------------------------------------------------
+// LLToolComposite
+
+class LLToolComposite : public LLTool
+{
+public:
+ LLToolComposite(const LLString& name);
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask) = 0; // Sets the current tool
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask) = 0;
+
+ // Map virtual functions to the currently active internal tool
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask) { return mCur->handleHover( x, y, mask ); }
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) { return mCur->handleScrollWheel( x, y, clicks ); }
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) { return mCur->handleRightMouseDown( x, y, mask ); }
+
+ virtual LLViewerObject* getEditingObject() { return mCur->getEditingObject(); }
+ virtual LLVector3d getEditingPointGlobal() { return mCur->getEditingPointGlobal(); }
+ virtual BOOL isEditing() { return mCur->isEditing(); }
+ virtual void stopEditing() { mCur->stopEditing(); mCur = mDefault; }
+
+ virtual BOOL clipMouseWhenDown() { return mCur->clipMouseWhenDown(); }
+
+ virtual void handleSelect();
+ virtual void handleDeselect() { mCur->handleDeselect(); mCur = mDefault; mSelected = FALSE; }
+
+ virtual void render() { mCur->render(); }
+ virtual void draw() { mCur->draw(); }
+
+ virtual BOOL handleKey(KEY key, MASK mask) { return mCur->handleKey( key, mask ); }
+
+ virtual void onMouseCaptureLost();
+
+ virtual void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const
+ { mCur->screenPointToLocal(screen_x, screen_y, local_x, local_y); }
+
+ virtual void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const
+ { mCur->localPointToScreen(local_x, local_y, screen_x, screen_y); }
+
+ BOOL isSelecting();
+protected:
+ void setCurrentTool( LLTool* new_tool );
+ LLTool* getCurrentTool() { return mCur; }
+ // In hover handler, call this to auto-switch tools
+ void setToolFromMask( MASK mask, LLTool *normal );
+
+protected:
+ LLTool* mCur; // The tool to which we're delegating.
+ LLTool* mDefault;
+ BOOL mSelected;
+ BOOL mMouseDown;
+ LLManip* mManip;
+ LLToolSelectRect* mSelectRect;
+
+public:
+ static const LLString sNameComp;
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompTranslate
+
+class LLToolCompTranslate : public LLToolComposite
+{
+public:
+ LLToolCompTranslate();
+ virtual ~LLToolCompTranslate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompScale
+
+class LLToolCompScale : public LLToolComposite
+{
+public:
+ LLToolCompScale();
+ virtual ~LLToolCompScale();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); // Returns to the default tool
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompRotate
+
+class LLToolCompRotate : public LLToolComposite
+{
+public:
+ LLToolCompRotate();
+ virtual ~LLToolCompRotate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+};
+
+//-----------------------------------------------------------------------
+// LLToolCompCreate
+
+class LLToolCompCreate : public LLToolComposite
+{
+public:
+ LLToolCompCreate();
+ virtual ~LLToolCompCreate();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+protected:
+ LLToolPlacer* mPlacer;
+ BOOL mObjectPlacedOnMouseDown;
+};
+
+
+//-----------------------------------------------------------------------
+// LLToolCompGun
+
+class LLToolGun;
+class LLToolGrab;
+class LLToolSelect;
+
+class LLToolCompGun : public LLToolComposite
+{
+public:
+ LLToolCompGun();
+ virtual ~LLToolCompGun();
+
+ // Overridden from LLToolComposite
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks);
+ virtual void onMouseCaptureLost();
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+protected:
+ LLToolGun* mGun;
+ LLToolGrab* mGrab;
+ LLTool* mNull;
+};
+
+extern LLToolCompTranslate *gToolTranslate;
+extern LLToolCompScale *gToolStretch;
+extern LLToolCompRotate *gToolRotate;
+extern LLToolCompCreate *gToolCreate;
+extern LLToolCompGun *gToolGun;
+
+#endif // LL_TOOLCOMP_H
diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp
new file mode 100644
index 0000000000..dcfcd03a68
--- /dev/null
+++ b/indra/newview/lltooldraganddrop.cpp
@@ -0,0 +1,2969 @@
+/**
+ * @file lltooldraganddrop.cpp
+ * @brief LLToolDragAndDrop class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "message.h"
+#include "lltooldraganddrop.h"
+
+#include "llinstantmessage.h"
+#include "lldir.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloatertools.h"
+#include "llgesturemgr.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llnotify.h"
+#include "llpreviewnotecard.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "llworld.h"
+#include "object_flags.h"
+#include "viewer.h"
+
+LLToolDragAndDrop *gToolDragAndDrop = NULL;
+
+// MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES
+// or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a
+// bit from there to give some pad.
+const S32 MAX_ITEMS = 42;
+const char* FOLDER_INCLUDES_ATTACHMENTS_BEING_WORN =
+ "Cannot give folders that contain objects that are attached to you.\n"
+ "Detach the object(s) and then try again.";
+
+
+// syntactic sugar
+#define callMemberFunction(object,ptrToMember) ((object).*(ptrToMember))
+
+/*
+const LLUUID MULTI_CONTAINER_TEXTURE("b2181ea2-1937-2ee1-78b8-bf05c43536b7");
+LLUUID CONTAINER_TEXTURES[LLAssetType::AT_COUNT];
+
+const char* CONTAINER_TEXTURE_NAMES[LLAssetType::AT_COUNT] =
+{
+ "container_texture.tga",
+ "container_sound.tga",
+ "container_many_things.tga",
+ "container_landmark.tga",
+ "container_script.tga",
+ "container_clothing.tga",
+ "container_object.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_script.tga",
+ "container_script.tga",
+ "container_texture.tga",
+ "container_bodypart.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_many_things.tga",
+ "container_sound.tga",
+ "container_texture.tga",
+ "container_texture.tga",
+ "container_animation.tga",
+ "container_gesture.tga"
+};
+*/
+
+class LLNoPreferredType : public LLInventoryCollectFunctor
+{
+public:
+ LLNoPreferredType() {}
+ virtual ~LLNoPreferredType() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE))
+ {
+ return true;
+ }
+ return false;
+ }
+};
+
+class LLNoPreferredTypeOrItem : public LLInventoryCollectFunctor
+{
+public:
+ LLNoPreferredTypeOrItem() {}
+ virtual ~LLNoPreferredTypeOrItem() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+ {
+ if(item) return true;
+ if(cat && (cat->getPreferredType() == LLAssetType::AT_NONE))
+ {
+ return true;
+ }
+ return false;
+ }
+};
+
+class LLDroppableItem : public LLInventoryCollectFunctor
+{
+public:
+ LLDroppableItem(BOOL is_transfer) :
+ mCountLosing(0), mIsTransfer(is_transfer) {}
+ virtual ~LLDroppableItem() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ S32 countNoCopy() const { return mCountLosing; }
+
+protected:
+ S32 mCountLosing;
+ BOOL mIsTransfer;
+};
+
+bool LLDroppableItem::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ bool allowed = false;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = true;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = true;
+ }
+ break;
+
+ default:
+ allowed = true;
+ break;
+ }
+ if(mIsTransfer
+ && !item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ allowed = false;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ ++mCountLosing;
+ }
+ }
+ return allowed;
+}
+
+class LLUncopyableItems : public LLInventoryCollectFunctor
+{
+public:
+ LLUncopyableItems() {}
+ virtual ~LLUncopyableItems() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+};
+
+bool LLUncopyableItems::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ BOOL uncopyable = FALSE;
+ if(item)
+ {
+ BOOL allowed = FALSE;
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ uncopyable = TRUE;
+ }
+ }
+ return (uncopyable ? true : false);
+}
+
+class LLDropCopyableItems : public LLInventoryCollectFunctor
+{
+public:
+ LLDropCopyableItems() {}
+ virtual ~LLDropCopyableItems() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+};
+
+
+bool LLDropCopyableItems::operator()(
+ LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ BOOL allowed = FALSE;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // whoops, can't copy it - don't allow it.
+ allowed = FALSE;
+ }
+ }
+ return (allowed ? true : false);
+}
+
+class LLGiveable : public LLInventoryCollectFunctor
+{
+public:
+ LLGiveable() : mCountLosing(0) {}
+ virtual ~LLGiveable() {}
+ virtual bool operator()(LLInventoryCategory* cat, LLInventoryItem* item);
+
+ S32 countNoCopy() const { return mCountLosing; }
+protected:
+ S32 mCountLosing;
+};
+
+bool LLGiveable::operator()(LLInventoryCategory* cat, LLInventoryItem* item)
+{
+ // All categories can be given.
+ if(cat) return TRUE;
+ BOOL allowed = FALSE;
+ if(item)
+ {
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ // not allowed
+ break;
+
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && !my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!gAgent.isWearingItem(item->getUUID()))
+ {
+ allowed = TRUE;
+ }
+ break;
+
+ default:
+ allowed = TRUE;
+ break;
+ }
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ allowed = FALSE;
+ }
+ if(allowed && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ ++mCountLosing;
+ }
+ }
+ return (allowed ? true : false);
+}
+
+class LLCategoryFireAndForget : public LLInventoryFetchComboObserver
+{
+public:
+ LLCategoryFireAndForget() {}
+ ~LLCategoryFireAndForget() {}
+ virtual void done()
+ {
+ /* no-op: it's fire n forget right? */
+ lldebugs << "LLCategoryFireAndForget::done()" << llendl;
+ }
+};
+
+class LLCategoryDropObserver : public LLInventoryFetchObserver
+{
+public:
+ LLCategoryDropObserver(
+ const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
+ mObjectID(obj_id),
+ mSource(src)
+ {}
+ ~LLCategoryDropObserver() {}
+ virtual void done();
+
+protected:
+ LLUUID mObjectID;
+ LLToolDragAndDrop::ESource mSource;
+};
+
+void LLCategoryDropObserver::done()
+{
+ gInventory.removeObserver(this);
+ LLViewerObject* dst_obj = gObjectList.findObject(mObjectID);
+ if(dst_obj)
+ {
+ // *FIX: coalesce these...
+ LLInventoryItem* item = NULL;
+ item_ref_t::iterator it = mComplete.begin();
+ item_ref_t::iterator end = mComplete.end();
+ for(; it < end; ++it)
+ {
+ item = gInventory.getItem(*it);
+ if(item)
+ {
+ LLToolDragAndDrop::dropInventory(
+ dst_obj,
+ item,
+ mSource,
+ LLUUID::null);
+ }
+ }
+ }
+ delete this;
+}
+
+class LLCategoryDropDescendentsObserver : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLCategoryDropDescendentsObserver(
+ const LLUUID& obj_id, LLToolDragAndDrop::ESource src) :
+ mObjectID(obj_id),
+ mSource(src)
+ {}
+ ~LLCategoryDropDescendentsObserver() {}
+ virtual void done();
+
+protected:
+ LLUUID mObjectID;
+ LLToolDragAndDrop::ESource mSource;
+};
+
+void LLCategoryDropDescendentsObserver::done()
+{
+
+ gInventory.removeObserver(this);
+ folder_ref_t::iterator it = mCompleteFolders.begin();
+ folder_ref_t::iterator end = mCompleteFolders.end();
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ for(; it != end; ++it)
+ {
+ gInventory.collectDescendents(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH);
+ }
+
+ S32 count = items.count();
+ if(count)
+ {
+ std::set<LLUUID> unique_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ unique_ids.insert(items.get(i)->getUUID());
+ }
+ LLInventoryFetchObserver::item_ref_t ids;
+ std::back_insert_iterator<LLInventoryFetchObserver::item_ref_t> copier(ids);
+ std::copy(unique_ids.begin(), unique_ids.end(), copier);
+ LLCategoryDropObserver* dropper;
+ dropper = new LLCategoryDropObserver(mObjectID, mSource);
+ dropper->fetchItems(ids);
+ if(dropper->isEverythingComplete())
+ {
+ dropper->done();
+ }
+ else
+ {
+ gInventory.addObserver(dropper);
+ }
+ }
+ delete this;
+}
+
+// This array is used to more easily control what happens when a 3d
+// drag and drop event occurs. Since there's an array of drop target
+// and cargo type, it's implemented as an array of pointers to member
+// functions which correctly carry out the actual drop.
+LLToolDragAndDrop::dragOrDrop3dImpl LLToolDragAndDrop::sDragAndDrop3d[DAD_COUNT][LLToolDragAndDrop::DT_COUNT] =
+{
+ // Source: DAD_NONE
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_TEXTURE
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dTextureObject, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_SOUND
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CALLINGCARD
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_LANDMARK
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_SCRIPT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dRezScript, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CLOTHING
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearItem, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_OBJECT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dRezAttachmentFromInv, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventoryObject, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dRezObjectOnObject, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dRezObjectOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_NOTECARD
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_CATEGORY
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearCategory, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventoryCategory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventoryCategory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dCategoryOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_ROOT
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_LAND
+ },
+ // Source: DAD_BODYPART
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dWearItem, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_ANIMATION
+ // TODO: animation on self could play it? edit it?
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+ // Source: DAD_GESTURE
+ // TODO: gesture on self could play it? edit it?
+ {
+ &LLToolDragAndDrop::dad3dNULL, // Dest: DT_NONE
+ &LLToolDragAndDrop::dad3dActivateGesture, // Dest: DT_SELF
+ &LLToolDragAndDrop::dad3dGiveInventory, // Dest: DT_AVATAR
+ &LLToolDragAndDrop::dad3dUpdateInventory, // Dest: DT_OBJECT
+ &LLToolDragAndDrop::dad3dNULL,//dad3dAssetOnLand, // Dest: DT_LAND
+ },
+};
+
+LLToolDragAndDrop::LLToolDragAndDrop()
+ :
+ LLTool("draganddrop", NULL),
+ mDragStartX(0),
+ mDragStartY(0),
+ mCursor(UI_CURSOR_NO),
+ mLastAccept(ACCEPT_NO),
+ mDrop(FALSE),
+ mCurItemIndex(0)
+{
+ // setup container texture ids
+ //for (S32 i = 0; i < LLAssetType::AT_COUNT; i++)
+ //{
+ // CONTAINER_TEXTURES[i].set(gViewerArt.getString(CONTAINER_TEXTURE_NAMES[i]));
+ //}
+}
+
+void LLToolDragAndDrop::setDragStart(S32 x, S32 y)
+{
+ mDragStartX = x;
+ mDragStartY = y;
+}
+
+BOOL LLToolDragAndDrop::isOverThreshold(S32 x,S32 y)
+{
+ const S32 MIN_MANHATTAN_DIST = 3;
+ S32 manhattan_dist = llabs( x - mDragStartX ) + llabs( y - mDragStartY );
+ return manhattan_dist >= MIN_MANHATTAN_DIST;
+}
+
+void LLToolDragAndDrop::beginDrag(EDragAndDropType type,
+ const LLUUID& cargo_id,
+ ESource source,
+ const LLUUID& source_id,
+ const LLUUID& object_id)
+{
+ if(type == DAD_NONE)
+ {
+ llwarns << "Attempted to start drag without a cargo type" << llendl;
+ return;
+ }
+ mCargoTypes.clear();
+ mCargoTypes.push_back(type);
+ mCargoIDs.clear();
+ mCargoIDs.push_back(cargo_id);
+ mSource = source;
+ mSourceID = source_id;
+ mObjectID = object_id;
+
+ setMouseCapture( TRUE );
+ gToolMgr->setTransientTool( this );
+ mCursor = UI_CURSOR_NO;
+ if((mCargoTypes[0] == DAD_CATEGORY)
+ && ((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY)))
+ {
+ LLInventoryCategory* cat = gInventory.getCategory(cargo_id);
+ // go ahead and fire & forget the descendents if we are not
+ // dragging a protected folder.
+ if(cat)
+ {
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLNoPreferredTypeOrItem is_not_preferred;
+ LLInventoryFetchComboObserver::folder_ref_t folder_ids;
+ LLInventoryFetchComboObserver::item_ref_t item_ids;
+ if(is_not_preferred(cat, NULL))
+ {
+ folder_ids.push_back(cargo_id);
+ }
+ gInventory.collectDescendentsIf(
+ cargo_id,
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_not_preferred);
+ S32 count = cats.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ folder_ids.push_back(cats.get(i)->getUUID());
+ }
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ item_ids.push_back(items.get(i)->getUUID());
+ }
+ if(!folder_ids.empty() || !item_ids.empty())
+ {
+ LLCategoryFireAndForget fetcher;
+ fetcher.fetch(folder_ids, item_ids);
+ }
+ }
+ }
+}
+
+void LLToolDragAndDrop::beginMultiDrag(
+ const std::vector<EDragAndDropType> types,
+ const std::vector<LLUUID>& cargo_ids,
+ ESource source,
+ const LLUUID& source_id)
+{
+ // assert on public api is evil
+ //llassert( type != DAD_NONE );
+
+ std::vector<EDragAndDropType>::const_iterator types_it;
+ for (types_it = types.begin(); types_it != types.end(); ++types_it)
+ {
+ if(DAD_NONE == *types_it)
+ {
+ llwarns << "Attempted to start drag without a cargo type" << llendl;
+ return;
+ }
+ }
+ mCargoTypes = types;
+ mCargoIDs = cargo_ids;
+ mSource = source;
+ mSourceID = source_id;
+
+ setMouseCapture( TRUE );
+ gToolMgr->setTransientTool( this );
+ mCursor = UI_CURSOR_NO;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // find cats in the cargo.
+ LLInventoryCategory* cat = NULL;
+ S32 count = llmin(cargo_ids.size(), types.size());
+ std::set<LLUUID> cat_ids;
+ for(S32 i = 0; i < count; ++i)
+ {
+ cat = gInventory.getCategory(cargo_ids[i]);
+ if(cat)
+ {
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLNoPreferredType is_not_preferred;
+ if(is_not_preferred(cat, NULL))
+ {
+ cat_ids.insert(cat->getUUID());
+ }
+ gInventory.collectDescendentsIf(
+ cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_not_preferred);
+ S32 cat_count = cats.count();
+ for(S32 i = 0; i < cat_count; ++i)
+ {
+ cat_ids.insert(cat->getUUID());
+ }
+ }
+ }
+ if(!cat_ids.empty())
+ {
+ LLInventoryFetchComboObserver::folder_ref_t folder_ids;
+ LLInventoryFetchComboObserver::item_ref_t item_ids;
+ std::back_insert_iterator<LLInventoryFetchDescendentsObserver::folder_ref_t> copier(folder_ids);
+ std::copy(cat_ids.begin(), cat_ids.end(), copier);
+ LLCategoryFireAndForget fetcher;
+ fetcher.fetch(folder_ids, item_ids);
+ }
+ }
+}
+
+void LLToolDragAndDrop::endDrag()
+{
+ gSelectMgr->unhighlightAll();
+ setMouseCapture(FALSE);
+}
+
+void LLToolDragAndDrop::onMouseCaptureLost()
+{
+ // Called whenever the drag ends or if mouse captue is simply lost
+ gToolMgr->clearTransientTool();
+ mCargoTypes.clear();
+ mCargoIDs.clear();
+ mSource = SOURCE_AGENT;
+ mSourceID.setNull();
+ mObjectID.setNull();
+}
+
+BOOL LLToolDragAndDrop::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if( hasMouseCapture() )
+ {
+ EAcceptance acceptance = ACCEPT_NO;
+ dragOrDrop( x, y, mask, TRUE, &acceptance );
+ endDrag();
+ }
+ return TRUE;
+}
+
+BOOL LLToolDragAndDrop::handleHover( S32 x, S32 y, MASK mask )
+{
+ EAcceptance acceptance = ACCEPT_NO;
+ dragOrDrop( x, y, mask, FALSE, &acceptance );
+
+ switch( acceptance )
+ {
+ case ACCEPT_YES_MULTI:
+ if (mCargoIDs.size() > 1)
+ {
+ mCursor = UI_CURSOR_ARROWDRAGMULTI;
+ }
+ else
+ {
+ mCursor = UI_CURSOR_ARROWDRAG;
+ }
+ break;
+ case ACCEPT_YES_SINGLE:
+ mCursor = UI_CURSOR_ARROWDRAG;
+ break;
+
+ case ACCEPT_NO_LOCKED:
+ mCursor = UI_CURSOR_NOLOCKED;
+ break;
+
+ case ACCEPT_NO:
+ mCursor = UI_CURSOR_NO;
+ break;
+
+ case ACCEPT_YES_COPY_MULTI:
+ if (mCargoIDs.size() > 1)
+ {
+ mCursor = UI_CURSOR_ARROWCOPYMULTI;
+ }
+ else
+ {
+ mCursor = UI_CURSOR_ARROWCOPY;
+ }
+ break;
+ case ACCEPT_YES_COPY_SINGLE:
+ mCursor = UI_CURSOR_ARROWCOPY;
+ break;
+ case ACCEPT_POSTPONED:
+ break;
+ default:
+ llassert( FALSE );
+ }
+
+ gViewerWindow->getWindow()->setCursor( mCursor );
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolDragAndDrop" << llendl;
+ return TRUE;
+}
+
+BOOL LLToolDragAndDrop::handleKey(KEY key, MASK mask)
+{
+ if (key == KEY_ESCAPE)
+ {
+ // cancel drag and drop operation
+ endDrag();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+BOOL LLToolDragAndDrop::handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen)
+{
+ if (!mToolTipMsg.empty())
+ {
+ msg = mToolTipMsg;
+ //*stick_rect_screen = gViewerWindow->getWindowRect();
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLToolDragAndDrop::handleDeselect()
+{
+ mToolTipMsg.clear();
+}
+
+// protected
+void LLToolDragAndDrop::dragOrDrop( S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance)
+{
+ *acceptance = ACCEPT_YES_MULTI;
+
+ BOOL handled = FALSE;
+
+ LLView* top_view = gViewerWindow->getTopView();
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+
+ mToolTipMsg.assign("");
+
+ if(top_view)
+ {
+ handled = TRUE;
+
+ for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ EAcceptance item_acceptance = ACCEPT_NO;
+ handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, FALSE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ if (handled)
+ {
+ // use sort order to determine priority of acceptance
+ *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ // all objects passed, go ahead and perform drop if necessary
+ if (handled && drop && (U32)*acceptance >= ACCEPT_YES_COPY_SINGLE)
+ {
+ // drop all items
+ if ((U32)*acceptance >= ACCEPT_YES_COPY_MULTI)
+ {
+ mCurItemIndex = 0;
+ }
+ // drop just last item
+ else
+ {
+ mCurItemIndex = mCargoIDs.size() - 1;
+ }
+ for (; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ S32 local_x, local_y;
+
+ EAcceptance item_acceptance;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = handled && top_view->handleDragAndDrop(local_x, local_y, mask, TRUE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ }
+ }
+ }
+ if (handled)
+ {
+ mLastAccept = (EAcceptance)*acceptance;
+ }
+ }
+
+ if(!handled)
+ {
+ handled = TRUE;
+
+ LLView* root_view = gViewerWindow->getRootView();
+
+ for (mCurItemIndex = 0; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ EAcceptance item_acceptance = ACCEPT_NO;
+ handled = handled && root_view->handleDragAndDrop(x, y, mask, FALSE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ if (handled)
+ {
+ // use sort order to determine priority of acceptance
+ *acceptance = (EAcceptance)llmin((U32)item_acceptance, (U32)*acceptance);
+ }
+ }
+ // all objects passed, go ahead and perform drop if necessary
+ if (handled && drop && (U32)*acceptance > ACCEPT_NO_LOCKED)
+ {
+ // drop all items
+ if ((U32)*acceptance >= ACCEPT_YES_COPY_MULTI)
+ {
+ mCurItemIndex = 0;
+ }
+ // drop just last item
+ else
+ {
+ mCurItemIndex = mCargoIDs.size() - 1;
+ }
+ for (; mCurItemIndex < (S32)mCargoIDs.size(); mCurItemIndex++)
+ {
+ LLInventoryObject* cargo = locateInventory(item, cat);
+
+ if (cargo)
+ {
+ //S32 local_x, local_y;
+
+ EAcceptance item_acceptance;
+ handled = handled && root_view->handleDragAndDrop(x, y, mask, TRUE,
+ mCargoTypes[mCurItemIndex],
+ (void*)cargo,
+ &item_acceptance,
+ mToolTipMsg);
+ }
+ }
+ }
+
+ if (handled)
+ {
+ mLastAccept = (EAcceptance)*acceptance;
+ }
+ }
+
+ if ( !handled )
+ {
+ dragOrDrop3D( x, y, mask, drop, acceptance );
+ }
+}
+
+void LLToolDragAndDrop::dragOrDrop3D( S32 x, S32 y, MASK mask, BOOL drop, EAcceptance* acceptance )
+{
+ mDrop = drop;
+ if (mDrop)
+ {
+ gPickFaces = TRUE;
+ // don't allow drag and drop onto transparent objects
+ gViewerWindow->hitObjectOrLandGlobalImmediate(x, y, pickCallback, FALSE);
+ }
+ else
+ {
+ // Don't pick faces during hover. Nothing currently requires per-face
+ // data.
+ // don't allow drag and drop onto transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, FALSE);
+ }
+
+ *acceptance = mLastAccept;
+}
+
+void LLToolDragAndDrop::pickCallback(S32 x, S32 y, MASK mask)
+{
+ EDropTarget target = DT_NONE;
+ S32 hit_face = -1;
+
+ LLViewerObject* hit_obj = gViewerWindow->lastNonFloraObjectHit();
+ gSelectMgr->unhighlightAll();
+
+ // Treat attachments as part of the avatar they are attached to.
+ if (hit_obj)
+ {
+ if(hit_obj->isAttachment() && !hit_obj->isHUDAttachment())
+ {
+ LLVOAvatar* avatar = LLVOAvatar::findAvatarFromAttachment( hit_obj );
+ if( !avatar )
+ {
+ gToolDragAndDrop->mLastAccept = ACCEPT_NO;
+ gToolDragAndDrop->mCursor = UI_CURSOR_NO;
+ gViewerWindow->getWindow()->setCursor( gToolDragAndDrop->mCursor );
+ return;
+ }
+
+ hit_obj = avatar;
+ }
+
+ if(hit_obj->isAvatar())
+ {
+ if(((LLVOAvatar*) hit_obj)->mIsSelf)
+ {
+ target = DT_SELF;
+ hit_face = -1;
+ }
+ else
+ {
+ target = DT_AVATAR;
+ hit_face = -1;
+ }
+ }
+ else
+ {
+ target = DT_OBJECT;
+ hit_face = gLastHitNonFloraObjectFace;
+ // if any item being dragged will be applied to the object under our cursor
+ // highlight that object
+ for (S32 i = 0; i < (S32)gToolDragAndDrop->mCargoIDs.size(); i++)
+ {
+ if (gToolDragAndDrop->mCargoTypes[i] != DAD_OBJECT || (mask & MASK_CONTROL))
+ {
+ gSelectMgr->highlightObjectAndFamily(hit_obj);
+ break;
+ }
+ }
+ }
+ }
+ else if(gLastHitLand)
+ {
+ target = DT_LAND;
+ hit_face = -1;
+ }
+
+ gToolDragAndDrop->mLastAccept = ACCEPT_YES_MULTI;
+
+ for (gToolDragAndDrop->mCurItemIndex = 0; gToolDragAndDrop->mCurItemIndex < (S32)gToolDragAndDrop->mCargoIDs.size();
+ gToolDragAndDrop->mCurItemIndex++)
+ {
+ // Call the right implementation function
+ gToolDragAndDrop->mLastAccept = (EAcceptance)llmin(
+ (U32)gToolDragAndDrop->mLastAccept,
+ (U32)callMemberFunction((*gToolDragAndDrop),
+ gToolDragAndDrop->sDragAndDrop3d[gToolDragAndDrop->mCargoTypes[gToolDragAndDrop->mCurItemIndex]][target])
+ (hit_obj, hit_face, mask, FALSE));
+ }
+
+ if (gToolDragAndDrop->mDrop && (U32)gToolDragAndDrop->mLastAccept >= ACCEPT_YES_COPY_SINGLE)
+ {
+ // if target allows multi-drop, go ahead and start iteration at beginning of cargo list
+ if (gToolDragAndDrop->mLastAccept >= ACCEPT_YES_COPY_MULTI)
+ {
+ gToolDragAndDrop->mCurItemIndex = 0;
+ }
+ // otherwise start at end, to follow selection rules (last selected item is most current)
+ else
+ {
+ gToolDragAndDrop->mCurItemIndex = gToolDragAndDrop->mCargoIDs.size() - 1;
+ }
+
+ for (; gToolDragAndDrop->mCurItemIndex < (S32)gToolDragAndDrop->mCargoIDs.size();
+ gToolDragAndDrop->mCurItemIndex++)
+ {
+ // Call the right implementation function
+ (U32)callMemberFunction((*gToolDragAndDrop),
+ gToolDragAndDrop->sDragAndDrop3d[gToolDragAndDrop->mCargoTypes[gToolDragAndDrop->mCurItemIndex]][target])
+ (hit_obj, hit_face, mask, TRUE);
+ }
+ }
+
+ switch( gToolDragAndDrop->mLastAccept )
+ {
+ case ACCEPT_YES_MULTI:
+ if (gToolDragAndDrop->mCargoIDs.size() > 1)
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAGMULTI;
+ }
+ else
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAG;
+ }
+ break;
+ case ACCEPT_YES_SINGLE:
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWDRAG;
+ break;
+
+ case ACCEPT_NO_LOCKED:
+ gToolDragAndDrop->mCursor = UI_CURSOR_NOLOCKED;
+ break;
+
+ case ACCEPT_NO:
+ gToolDragAndDrop->mCursor = UI_CURSOR_NO;
+ break;
+
+ case ACCEPT_YES_COPY_MULTI:
+ if (gToolDragAndDrop->mCargoIDs.size() > 1)
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPYMULTI;
+ }
+ else
+ {
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPY;
+ }
+ break;
+ case ACCEPT_YES_COPY_SINGLE:
+ gToolDragAndDrop->mCursor = UI_CURSOR_ARROWCOPY;
+ break;
+ case ACCEPT_POSTPONED:
+ break;
+ default:
+ llassert( FALSE );
+ }
+
+ gToolDragAndDrop->mLastHitPos = gLastHitPosGlobal + gLastHitObjectOffset;
+ gToolDragAndDrop->mLastCameraPos = gAgent.getCameraPositionGlobal();
+
+ gViewerWindow->getWindow()->setCursor( gToolDragAndDrop->mCursor );
+}
+
+// static
+BOOL LLToolDragAndDrop::handleDropTextureProtections(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ // Always succeed if....
+ // texture is from the library
+ // or already in the contents of the object
+ if(SOURCE_LIBRARY == source)
+ {
+ // dropping a texture from the library always just works.
+ return TRUE;
+ }
+
+ if (hit_obj->getInventoryItemByAsset(item->getAssetUUID()))
+ {
+ // if the asset is already in the object's inventory
+ // then it can always be added to a side.
+ // This saves some work if the task's inventory is already loaded
+ return TRUE;
+ }
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ if(!item->getPermissions().allowOperationBy(PERM_COPY, gAgent.getID()))
+ {
+ // Check that we can add the texture as inventory to the object
+ if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
+ {
+ return FALSE;
+ }
+ // make sure the object has the texture in it's inventory.
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the texture from local inventory. The server
+ // will actually remove the item from agent inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and the
+ // source region has crashed, you can bypass these
+ // permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return FALSE;
+ }
+ }
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
+ }
+ else if(!item->getPermissions().allowOperationBy(PERM_TRANSFER,
+ gAgent.getID()))
+ {
+ // Check that we can add the testure as inventory to the object
+ if (willObjectAcceptInventory(hit_obj,item) < ACCEPT_YES_COPY_SINGLE )
+ {
+ return FALSE;
+ }
+ // *FIX: may want to make sure agent can paint hit_obj.
+
+ // make sure the object has the texture in it's inventory.
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ASSET_KEY, true);
+ }
+ return TRUE;
+}
+
+void LLToolDragAndDrop::dropTextureAllFaces(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ LLUUID asset_id = item->getAssetUUID();
+ BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
+ if(!success)
+ {
+ return;
+ }
+ LLViewerImage* image = gImageList.getImage(asset_id);
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+ S32 num_faces = hit_obj->getNumTEs();
+ for( S32 face = 0; face < num_faces; face++ )
+ {
+
+ // update viewer side image in anticipation of update from simulator
+ hit_obj->setTEImage(face, image);
+ dialog_refresh_all();
+
+ // send the update to the simulator
+ hit_obj->sendTEUpdate();
+ }
+
+}
+
+/*
+void LLToolDragAndDrop::dropTextureOneFaceAvatar(LLVOAvatar* avatar, S32 hit_face, LLInventoryItem* item)
+{
+ if (hit_face == -1) return;
+ LLViewerImage* image = gImageList.getImage(item->getAssetUUID());
+
+ avatar->userSetOptionalTE( hit_face, image);
+}
+*/
+
+void LLToolDragAndDrop::dropTextureOneFace(LLViewerObject* hit_obj,
+ S32 hit_face,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ if (hit_face == -1) return;
+ if (!item)
+ {
+ llwarns << "LLToolDragAndDrop::dropTextureOneFace no texture item." << llendl;
+ return;
+ }
+ LLUUID asset_id = item->getAssetUUID();
+ BOOL success = handleDropTextureProtections(hit_obj, item, source, src_id);
+ if(!success)
+ {
+ return;
+ }
+ // update viewer side image in anticipation of update from simulator
+ LLViewerImage* image = gImageList.getImage(asset_id);
+ gViewerStats->incStat(LLViewerStats::ST_EDIT_TEXTURE_COUNT );
+ hit_obj->setTEImage(hit_face, image);
+ dialog_refresh_all();
+
+ // send the update to the simulator
+ hit_obj->sendTEUpdate();
+}
+
+
+void LLToolDragAndDrop::dropScript(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ BOOL active,
+ ESource source,
+ const LLUUID& src_id)
+{
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == gToolDragAndDrop->mSource)
+ || (SOURCE_NOTECARD == gToolDragAndDrop->mSource))
+ {
+ llwarns << "Call to LLToolDragAndDrop::dropScript() from world"
+ << " or notecard." << llendl;
+ return;
+ }
+ if(hit_obj && item)
+ {
+ LLPointer<LLViewerInventoryItem> new_script = new LLViewerInventoryItem(item);
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the script from local inventory. The server
+ // will actually remove the item from agent inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and
+ // the source region has crashed, you can bypass
+ // these permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return;
+ }
+ }
+ }
+ hit_obj->saveScript(new_script, active, true);
+ gFloaterTools->dirty();
+
+ // VEFFECT: SetScript
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(hit_obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+}
+
+void LLToolDragAndDrop::dropObject(LLViewerObject* raycast_target,
+ BOOL bypass_sim_raycast,
+ BOOL from_task_inventory,
+ BOOL remove_from_inventory)
+{
+ LLViewerRegion* regionp = gWorldp->getRegionFromPosGlobal(mLastHitPos);
+ if (!regionp)
+ {
+ llwarns << "Couldn't find region to rez object" << llendl;
+ return;
+ }
+
+ //llinfos << "Rezzing object" << llendl;
+ make_ui_sound("UISndObjectRezIn");
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return;
+
+ if (regionp
+ && (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
+ {
+ LLFirstUse::useSandbox();
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ if (mSource == SOURCE_NOTECARD)
+ {
+ msg->newMessageFast(_PREHASH_RezObjectFromNotecard);
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_RezObject);
+ }
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+
+ msg->nextBlock("RezData");
+ // if it's being rezzed from task inventory, we need to enable
+ // saving it back into the task inventory.
+ // *FIX: We can probably compress this to a single byte, since I
+ // think folderid == mSourceID. This will be a later
+ // optimization.
+ if(from_task_inventory)
+ {
+ msg->addUUIDFast(_PREHASH_FromTaskID, mSourceID);
+ }
+ else
+ {
+ msg->addUUIDFast(_PREHASH_FromTaskID, LLUUID::null);
+ }
+ msg->addU8Fast(_PREHASH_BypassRaycast, (U8) bypass_sim_raycast);
+ msg->addVector3Fast(_PREHASH_RayStart, regionp->getPosRegionFromGlobal(mLastCameraPos));
+ msg->addVector3Fast(_PREHASH_RayEnd, regionp->getPosRegionFromGlobal(mLastHitPos));
+ // Limit raycast to a single object.
+ // Speeds up server raycast + avoid problems with server ray
+ // hitting objects that were clipped by the near plane or culled
+ // on the viewer.
+ LLUUID ray_target_id;
+ if( raycast_target )
+ {
+ ray_target_id = raycast_target->getID();
+ }
+ else
+ {
+ ray_target_id.setNull();
+ }
+ msg->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
+ msg->addBOOLFast(_PREHASH_RayEndIsIntersection, FALSE);
+ // Select the object only if we're editing.
+ BOOL rez_selected = gToolMgr->inEdit();
+ msg->addBOOLFast(_PREHASH_RezSelected, rez_selected);
+
+ // check if it cannot be copied, and mark as remove if it is -
+ // this will remove the object from inventory after rez. Only
+ // bother with this check if we would not normally remove from
+ // inventory.
+ if(!remove_from_inventory
+ && !item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ remove_from_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ bool is_in_trash = false;
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ is_in_trash = true;
+ remove_from_inventory = TRUE;
+ }
+ msg->addBOOLFast(_PREHASH_RemoveItem, remove_from_inventory);
+
+ // deal with permissions slam logic
+ pack_permissions_slam(msg, item->getFlags(), item->getPermissions());
+
+ LLUUID folder_id = item->getParentUUID();
+ if((SOURCE_LIBRARY == mSource) || (is_in_trash))
+ {
+ // since it's coming from the library or trash, we want to not
+ // 'take' it back to the same place.
+ item->setParent(LLUUID::null);
+ }
+ if (mSource == SOURCE_NOTECARD)
+ {
+ msg->nextBlockFast(_PREHASH_NotecardData);
+ msg->addUUIDFast(_PREHASH_NotecardItemID, mSourceID);
+ msg->addUUIDFast(_PREHASH_ObjectID, mObjectID);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item->getUUID());
+ }
+ else
+ {
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ item->packMessage(msg);
+ }
+ msg->sendReliable(regionp->getHost());
+ // back out the change. no actual internal changes take place.
+ item->setParent(folder_id);
+
+ // If we're going to select it, get ready for the incoming
+ // selected object.
+ if (rez_selected)
+ {
+ gSelectMgr->deselectAll();
+ gViewerWindow->getWindow()->incBusyCount();
+ }
+
+ if(remove_from_inventory)
+ {
+ // Delete it from inventory immediately so that users cannot
+ // easily bypass copy protection in laggy situations. If the
+ // rez fails, we will put it back on the server.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+
+ // VEFFECT: DropObject
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setPositionGlobal(mLastHitPos);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+
+ gViewerStats->incStat(LLViewerStats::ST_REZ_COUNT);
+}
+
+void LLToolDragAndDrop::dropInventory(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id)
+{
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == gToolDragAndDrop->mSource)
+ || (SOURCE_NOTECARD == gToolDragAndDrop->mSource))
+ {
+ llwarns << "Call to LLToolDragAndDrop::dropInventory() from world"
+ << " or notecard." << llendl;
+ return;
+ }
+
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem(item);
+ S32 creation_date = time_corrected();
+ new_item->setCreationDate(creation_date);
+
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ if(SOURCE_AGENT == source)
+ {
+ // Remove the inventory item from local inventory. The
+ // server will actually remove the item from agent
+ // inventory.
+ gInventory.deleteObject(item->getUUID());
+ gInventory.notifyObservers();
+ }
+ else if(SOURCE_WORLD == source)
+ {
+ // *FIX: if the objects are in different regions, and the
+ // source region has crashed, you can bypass these
+ // permissions.
+ LLViewerObject* src_obj = gObjectList.findObject(src_id);
+ if(src_obj)
+ {
+ src_obj->removeInventory(item->getUUID());
+ }
+ else
+ {
+ llwarns << "Unable to find source object." << llendl;
+ return;
+ }
+ }
+ }
+ hit_obj->updateInventory(new_item, TASK_INVENTORY_ITEM_KEY, true);
+ if (gFloaterTools->getVisible())
+ {
+ //FIXME: only show this if panel not expanded?
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ }
+
+ // VEFFECT: AddToInventory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(hit_obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+}
+
+struct LLGiveInventoryInfo
+{
+ LLUUID mToAgentID;
+ LLUUID mInventoryObjectID;
+ LLGiveInventoryInfo(const LLUUID& to_agent, const LLUUID& obj_id) :
+ mToAgentID(to_agent), mInventoryObjectID(obj_id) {}
+};
+
+void LLToolDragAndDrop::giveInventory(const LLUUID& to_agent,
+ LLInventoryItem* item)
+{
+ llinfos << "LLToolDragAndDrop::giveInventory()" << llendl;
+ if(!isInventoryGiveAcceptable(item))
+ {
+ return;
+ }
+ if(item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ // just give it away.
+ LLToolDragAndDrop::commitGiveInventoryItem(to_agent, item);
+ }
+ else
+ {
+ // ask if the agent is sure.
+ LLGiveInventoryInfo* info = new LLGiveInventoryInfo(to_agent,
+ item->getUUID());
+
+ gViewerWindow->alertXml("CannotCopyWarning",
+ &LLToolDragAndDrop::handleCopyProtectedItem,
+ (void*)info);
+ }
+}
+
+// static
+void LLToolDragAndDrop::handleCopyProtectedItem(S32 option, void* data)
+{
+ LLGiveInventoryInfo* info = (LLGiveInventoryInfo*)data;
+ LLInventoryItem* item = NULL;
+ switch(option)
+ {
+ case 0: // "Yes"
+ item = gInventory.getItem(info->mInventoryObjectID);
+ if(item)
+ {
+ LLToolDragAndDrop::commitGiveInventoryItem(info->mToAgentID,
+ item);
+ // delete it for now - it will be deleted on the server
+ // quickly enough.
+ gInventory.deleteObject(info->mInventoryObjectID);
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ gViewerWindow->alertXml("CannotGiveItem");
+ }
+ break;
+
+ default: // no, cancel, whatever, who cares, not yes.
+ gViewerWindow->alertXml("TransactionCancelled");
+ break;
+ }
+}
+
+// static
+void LLToolDragAndDrop::commitGiveInventoryItem(const LLUUID& to_agent,
+ LLInventoryItem* item)
+{
+ if(!item) return;
+ std::string name;
+ gAgent.buildFullname(name);
+ LLUUID transaction_id;
+ transaction_id.generate();
+ const S32 BUCKET_SIZE = sizeof(U8) + UUID_BYTES;
+ U8 bucket[BUCKET_SIZE];
+ bucket[0] = (U8)item->getType();
+ memcpy(&bucket[1], &(item->getUUID().mData), UUID_BYTES);
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_agent,
+ name.c_str(),
+ item->getName().c_str(),
+ IM_ONLINE,
+ IM_INVENTORY_OFFERED,
+ transaction_id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ NO_TIMESTAMP,
+ bucket,
+ BUCKET_SIZE);
+ gAgent.sendReliableMessage();
+
+ // VEFFECT: giveInventory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(gObjectList.findObject(to_agent));
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+}
+
+void LLToolDragAndDrop::giveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat)
+{
+ if(!cat) return;
+ llinfos << "LLToolDragAndDrop::giveInventoryCategory() - "
+ << cat->getUUID() << llendl;
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar )
+ {
+ return;
+ }
+
+ // Test out how many items are being given.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLGiveable giveable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ giveable);
+ S32 count = cats.count();
+ bool complete = true;
+ for(S32 i = 0; i < count; ++i)
+ {
+ if(!gInventory.isCategoryComplete(cats.get(i)->getUUID()))
+ {
+ complete = false;
+ break;
+ }
+ }
+ if(!complete)
+ {
+ LLNotifyBox::showXml("IncompleteInventory");
+ return;
+ }
+ count = items.count() + cats.count();
+ if(count > MAX_ITEMS)
+ {
+ gViewerWindow->alertXml("TooManyItems");
+ return;
+ }
+ else if(count == 0)
+ {
+ gViewerWindow->alertXml("NoItems");
+ return;
+ }
+ else
+ {
+ if(0 == giveable.countNoCopy())
+ {
+ LLToolDragAndDrop::commitGiveInventoryCategory(to_agent, cat);
+ }
+ else
+ {
+ LLGiveInventoryInfo* info = NULL;
+ info = new LLGiveInventoryInfo(to_agent, cat->getUUID());
+ LLStringBase<char>::format_map_t args;
+ args["[COUNT]"] = llformat("%d",giveable.countNoCopy());
+ gViewerWindow->alertXml("CannotCopyCountItems", args,
+ &LLToolDragAndDrop::handleCopyProtectedCategory,
+ (void*)info);
+
+ }
+ }
+}
+
+
+// static
+void LLToolDragAndDrop::handleCopyProtectedCategory(S32 option, void* data)
+{
+ LLGiveInventoryInfo* info = (LLGiveInventoryInfo*)data;
+ LLInventoryCategory* cat = NULL;
+ switch(option)
+ {
+ case 0: // "Yes"
+ cat = gInventory.getCategory(info->mInventoryObjectID);
+ if(cat)
+ {
+ LLToolDragAndDrop::commitGiveInventoryCategory(info->mToAgentID,
+ cat);
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLUncopyableItems remove;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ remove);
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ gInventory.deleteObject(items.get(i)->getUUID());
+ }
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ gViewerWindow->alertXml("CannotGiveCategory");
+ }
+ break;
+
+ default: // no, cancel, whatever, who cares, not yes.
+ gViewerWindow->alertXml("TransactionCancelled");
+ break;
+ }
+}
+
+// static
+void LLToolDragAndDrop::commitGiveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat)
+{
+ if(!cat) return;
+ llinfos << "LLToolDragAndDrop::commitGiveInventoryCategory() - "
+ << cat->getUUID() << llendl;
+
+ // Test out how many items are being given.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLGiveable giveable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ giveable);
+
+ // MAX ITEMS is based on (sizeof(uuid)+2) * count must be <
+ // MTUBYTES or 18 * count < 1200 => count < 1200/18 =>
+ // 66. I've cut it down a bit from there to give some pad.
+ S32 count = items.count() + cats.count();
+ if(count > MAX_ITEMS)
+ {
+ gViewerWindow->alertXml("TooManyItems");
+ return;
+ }
+ else if(count == 0)
+ {
+ gViewerWindow->alertXml("NoItems");
+ return;
+ }
+ else
+ {
+ std::string name;
+ gAgent.buildFullname(name);
+ LLUUID transaction_id;
+ transaction_id.generate();
+ S32 bucket_size = (sizeof(U8) + UUID_BYTES) * (count + 1);
+ U8* bucket = new U8[bucket_size];
+ U8* pos = bucket;
+ U8 type = (U8)cat->getType();
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(cat->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ S32 i;
+ count = cats.count();
+ for(i = 0; i < count; ++i)
+ {
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(cats.get(i)->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ type = (U8)items.get(i)->getType();
+ memcpy(pos, &type, sizeof(U8));
+ pos += sizeof(U8);
+ memcpy(pos, &(items.get(i)->getUUID()), UUID_BYTES);
+ pos += UUID_BYTES;
+ }
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_agent,
+ name.c_str(),
+ cat->getName().c_str(),
+ IM_ONLINE,
+ IM_INVENTORY_OFFERED,
+ transaction_id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ NO_TIMESTAMP,
+ bucket,
+ bucket_size);
+ gAgent.sendReliableMessage();
+ delete[] bucket;
+
+ // VEFFECT: giveInventoryCategory
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(gObjectList.findObject(to_agent));
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gFloaterTools->dirty();
+ }
+}
+
+// static
+BOOL LLToolDragAndDrop::isInventoryGiveAcceptable(LLInventoryItem* item)
+{
+ if(!item)
+ {
+ return FALSE;
+ }
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ return FALSE;
+ }
+ BOOL copyable = FALSE;
+ if(item->getPermissions().allowCopyBy(gAgent.getID())) copyable = TRUE;
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if(!my_avatar)
+ {
+ return FALSE;
+ }
+ BOOL acceptable = TRUE;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ acceptable = FALSE;
+ break;
+ case LLAssetType::AT_OBJECT:
+ if(my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(!copyable && gAgent.isWearingItem(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ return acceptable;
+}
+
+// Static
+BOOL LLToolDragAndDrop::isInventoryGroupGiveAcceptable(LLInventoryItem* item)
+{
+ if(!item)
+ {
+ return FALSE;
+ }
+
+ // These permissions are double checked in the simulator in
+ // LLGroupNoticeInventoryItemFetch::result().
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ return FALSE;
+ }
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ return FALSE;
+ }
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if(!my_avatar)
+ {
+ return FALSE;
+ }
+
+ BOOL acceptable = TRUE;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_CALLINGCARD:
+ acceptable = FALSE;
+ break;
+ case LLAssetType::AT_OBJECT:
+ if(my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ acceptable = FALSE;
+ }
+ break;
+ default:
+ break;
+ }
+ return acceptable;
+}
+
+// accessor that looks at permissions, copyability, and names of
+// inventory items to determine if a drop would be ok.
+EAcceptance LLToolDragAndDrop::willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item)
+{
+ // check the basics
+ if(!item || !obj) return ACCEPT_NO;
+ // HACK: downcast
+ LLViewerInventoryItem* vitem = (LLViewerInventoryItem*)item;
+ if(!vitem->isComplete()) return ACCEPT_NO;
+
+ // deny attempts to drop from an object onto itself. This is to
+ // help make sure that drops that are from an object to an object
+ // don't have to worry about order of evaluation. Think of this
+ // like check for self in assignment.
+ if(obj->getID() == item->getParentUUID())
+ {
+ return ACCEPT_NO;
+ }
+
+ //BOOL copy = (perm.allowCopyBy(gAgent.getID(),
+ // gAgent.getGroupID())
+ // && (obj->mPermModify || obj->mFlagAllowInventoryAdd));
+ BOOL worn = FALSE;
+ LLVOAvatar* my_avatar = NULL;
+ switch(item->getType())
+ {
+ case LLAssetType::AT_OBJECT:
+ my_avatar = gAgent.getAvatarObject();
+ if(my_avatar && my_avatar->isWearingAttachment(item->getUUID()))
+ {
+ worn = TRUE;
+ }
+ break;
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_CLOTHING:
+ if(gAgent.isWearingItem(item->getUUID()))
+ {
+ worn = TRUE;
+ }
+ break;
+ default:
+ break;
+ }
+ const LLPermissions& perm = item->getPermissions();
+ BOOL modify = (obj->permModify() || obj->flagAllowInventoryAdd());
+ BOOL transfer = FALSE;
+ if((obj->permYouOwner() && (perm.getOwner() == gAgent.getID()))
+ || perm.allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ transfer = TRUE;
+ }
+ BOOL volume = (LL_PCODE_VOLUME == obj->getPCode());
+ BOOL attached = obj->isAttachment();
+ BOOL unrestricted = ((perm.getMaskBase() & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) ? TRUE : FALSE;
+ if(attached && !unrestricted)
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ else if(modify && transfer && volume && !worn)
+ {
+ return ACCEPT_YES_MULTI;
+ }
+ else if(!modify)
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ return ACCEPT_NO;
+}
+
+///
+/// Methods called in the drag & drop array
+///
+
+EAcceptance LLToolDragAndDrop::dad3dNULL(
+ LLViewerObject*, S32, MASK, BOOL)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dNULL()" << llendl;
+ return ACCEPT_NO;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezAttachmentFromInv(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezAttachmentFromInv()" << llendl;
+ // must be in the user's inventory
+ if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ // must not be in the trash
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ // must not be already wearing it
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar || avatar->isWearingAttachment(item->getUUID()) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ if(mSource == SOURCE_LIBRARY)
+ {
+ LLPointer<LLInventoryCallback> cb = new RezAttachmentCallback(0);
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ rez_attachment(item, 0);
+ }
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dRezObjectOnLand(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ if (mSource == SOURCE_WORLD)
+ {
+ return dad3dRezFromObjectOnLand(obj, face, mask, drop);
+ }
+
+ lldebugs << "LLToolDragAndDrop::dad3dRezObjectOnLand()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ EAcceptance accept;
+ BOOL remove_inventory;
+
+ // Get initial settings based on shift key
+ if (mask & MASK_SHIFT)
+ {
+ // For now, always make copy
+ //accept = ACCEPT_YES_SINGLE;
+ //remove_inventory = TRUE;
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+ else
+ {
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+
+ // check if the item can be copied. If not, send that to the sim
+ // which will remove the inventory item.
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ if(drop)
+ {
+ dropObject(obj, TRUE, FALSE, remove_inventory);
+ }
+
+ return accept;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezObjectOnObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ // handle objects coming from object inventory
+ if (mSource == SOURCE_WORLD)
+ {
+ return dad3dRezFromObjectOnObject(obj, face, mask, drop);
+ }
+
+ lldebugs << "LLToolDragAndDrop::dad3dRezObjectOnObject()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ LLVOAvatar* my_avatar = gAgent.getAvatarObject();
+ if( !my_avatar || my_avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if((mask & MASK_CONTROL))
+ {
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if(mSource == SOURCE_NOTECARD)
+ {
+ return ACCEPT_NO;
+ }
+
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ dropInventory(obj, item, mSource, mSourceID);
+ }
+ return rv;
+ }
+
+ EAcceptance accept;
+ BOOL remove_inventory;
+
+ if (mask & MASK_SHIFT)
+ {
+ // For now, always make copy
+ //accept = ACCEPT_YES_SINGLE;
+ //remove_inventory = TRUE;
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+ else
+ {
+ accept = ACCEPT_YES_COPY_SINGLE;
+ remove_inventory = FALSE;
+ }
+
+ // check if the item can be copied. If not, send that to the sim
+ // which will remove the inventory item.
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ // Check if it's in the trash.
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(gInventory.isObjectDescendentOf(item->getUUID(), trash_id))
+ {
+ accept = ACCEPT_YES_SINGLE;
+ remove_inventory = TRUE;
+ }
+
+ if(drop)
+ {
+ dropObject(obj, FALSE, FALSE, remove_inventory);
+ }
+
+ return accept;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezScript(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezScript()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ // rez in the script active by default, rez in inactive if the
+ // control key is being held down.
+ BOOL active = ((mask & MASK_CONTROL) == 0);
+
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ dropScript(root_object, item, active, mSource, mSourceID);
+ }
+ return rv;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dTextureObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dTextureObject()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ EAcceptance rv = willObjectAcceptInventory(obj, item);
+ if((mask & MASK_CONTROL))
+ {
+ if((ACCEPT_YES_SINGLE <= rv) && drop)
+ {
+ dropInventory(obj, item, mSource, mSourceID);
+ }
+ return rv;
+ }
+ if(!obj->permModify())
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop && (ACCEPT_YES_SINGLE <= rv))
+ {
+ if((mask & MASK_SHIFT))
+ {
+ dropTextureAllFaces(obj, item, mSource, mSourceID);
+ }
+ else
+ {
+ dropTextureOneFace(obj, face, item, mSource, mSourceID);
+ }
+
+ // VEFFECT: SetTexture
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM, TRUE);
+ effectp->setSourceObject(gAgent.getAvatarObject());
+ effectp->setTargetObject(obj);
+ effectp->setDuration(LL_HUD_DUR_SHORT);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+
+ // enable multi-drop, although last texture will win
+ return ACCEPT_YES_MULTI;
+}
+/*
+EAcceptance LLToolDragAndDrop::dad3dTextureSelf(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dTextureAvatar()" << llendl;
+ if(drop)
+ {
+ if( !(mask & MASK_SHIFT) )
+ {
+ dropTextureOneFaceAvatar( (LLVOAvatar*)obj, face, (LLInventoryItem*)mCargoData);
+ }
+ }
+ return (mask & MASK_SHIFT) ? ACCEPT_NO : ACCEPT_YES_SINGLE;
+}
+*/
+
+EAcceptance LLToolDragAndDrop::dad3dWearItem(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dWearItem()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+ {
+ // it's in the agent inventory
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return ACCEPT_NO;
+ }
+
+ if(mSource == SOURCE_LIBRARY)
+ {
+ // create item based on that one, and put it on if that
+ // was a success.
+ LLPointer<LLInventoryCallback> cb = new WearOnAvatarCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ wear_inventory_item_on_avatar( item );
+ }
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ // TODO: copy/move item to avatar's inventory and then wear it.
+ return ACCEPT_NO;
+ }
+}
+
+EAcceptance LLToolDragAndDrop::dad3dActivateGesture(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dActivateGesture()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+
+ if(mSource == SOURCE_AGENT || mSource == SOURCE_LIBRARY)
+ {
+ // it's in the agent inventory
+ LLUUID trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if( gInventory.isObjectDescendentOf( item->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if( drop )
+ {
+ LLUUID item_id;
+ if(mSource == SOURCE_LIBRARY)
+ {
+ // create item based on that one, and put it on if that
+ // was a success.
+ LLPointer<LLInventoryCallback> cb = new ActivateGestureCallback();
+ copy_inventory_item(
+ gAgent.getID(),
+ item->getPermissions().getOwner(),
+ item->getUUID(),
+ LLUUID::null,
+ std::string(),
+ cb);
+ }
+ else
+ {
+ gGestureManager.activateGesture(item->getUUID());
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+ }
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ return ACCEPT_NO;
+ }
+}
+
+EAcceptance LLToolDragAndDrop::dad3dWearCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dWearCategory()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* category;
+ locateInventory(item, category);
+ if(!category) return ACCEPT_NO;
+
+ if (drop)
+ {
+ // Don't wear anything until initial wearables are loaded, can
+ // destroy clothing items.
+ if (!gAgent.areWearablesLoaded())
+ {
+ gViewerWindow->alertXml("CanNotChangeAppearanceUntilLoaded");
+ return ACCEPT_NO;
+ }
+ }
+
+ if(mSource == SOURCE_AGENT)
+ {
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ if( gInventory.isObjectDescendentOf( category->getUUID(), trash_id ) )
+ {
+ return ACCEPT_NO;
+ }
+
+ if(drop)
+ {
+ BOOL append = ( (mask & MASK_SHIFT) ? TRUE : FALSE );
+ wear_inventory_category(category, false, append);
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else if(mSource == SOURCE_LIBRARY)
+ {
+ if(drop)
+ {
+ wear_inventory_category(category, true, false);
+ }
+ return ACCEPT_YES_MULTI;
+ }
+ else
+ {
+ // TODO: copy/move category to avatar's inventory and then wear it.
+ return ACCEPT_NO;
+ }
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dUpdateInventory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dadUpdateInventory()" << llendl;
+
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ if((SOURCE_WORLD == mSource) || (SOURCE_NOTECARD == mSource))
+ {
+ return ACCEPT_NO;
+ }
+
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ EAcceptance rv = willObjectAcceptInventory(root_object, item);
+ if(root_object && drop && (ACCEPT_YES_COPY_SINGLE <= rv))
+ {
+ dropInventory(root_object, item, mSource, mSourceID);
+ }
+ return rv;
+}
+
+BOOL LLToolDragAndDrop::dadUpdateInventory(LLViewerObject* obj, BOOL drop)
+{
+ EAcceptance rv = dad3dUpdateInventory(obj, -1, MASK_NONE, drop);
+ return (rv >= ACCEPT_YES_COPY_SINGLE);
+}
+
+EAcceptance LLToolDragAndDrop::dad3dUpdateInventoryCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dUpdateInventoryCategory()" << llendl;
+ if(mSource != SOURCE_AGENT && mSource != SOURCE_LIBRARY)
+ {
+ return ACCEPT_NO;
+ }
+ if(obj->isAttachment()) return ACCEPT_NO_LOCKED;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+
+ // find all the items in the category
+ LLDroppableItem droppable(!obj->permYouOwner());
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ droppable);
+ cats.put(cat);
+ if(droppable.countNoCopy() > 0)
+ {
+ llwarns << "*** Need to confirm this step" << llendl;
+ }
+ LLViewerObject* root_object = obj;
+ if (obj && obj->getParent())
+ {
+ LLViewerObject* parent_obj = (LLViewerObject*)obj->getParent();
+ if (!parent_obj->isAvatar())
+ {
+ root_object = parent_obj;
+ }
+ }
+
+ // Check for accept
+ S32 i;
+ S32 count = cats.count();
+ for(i = 0; i < count; ++i)
+ {
+ rv = gInventory.isCategoryComplete(cats.get(i)->getUUID()) ? ACCEPT_YES_MULTI : ACCEPT_NO;
+ if(rv < ACCEPT_YES_SINGLE)
+ {
+ lldebugs << "Category " << cats.get(i)->getUUID()
+ << "is not complete." << llendl;
+ break;
+ }
+ }
+ if(ACCEPT_YES_COPY_SINGLE <= rv)
+ {
+ count = items.count();
+ for(i = 0; i < count; ++i)
+ {
+ rv = willObjectAcceptInventory(root_object, items.get(i));
+ if(rv < ACCEPT_YES_COPY_SINGLE)
+ {
+ lldebugs << "Object will not accept "
+ << items.get(i)->getUUID() << llendl;
+ break;
+ }
+ }
+ }
+
+ // if every item is accepted, go ahead and send it on.
+ if(drop && (ACCEPT_YES_COPY_SINGLE <= rv))
+ {
+ S32 count = items.count();
+ LLInventoryFetchObserver::item_ref_t ids;
+ for(i = 0; i < count; ++i)
+ {
+ //dropInventory(root_object, items.get(i), mSource, mSourceID);
+ ids.push_back(items.get(i)->getUUID());
+ }
+ LLCategoryDropObserver* dropper;
+ dropper = new LLCategoryDropObserver(obj->getID(), mSource);
+ dropper->fetchItems(ids);
+ if(dropper->isEverythingComplete())
+ {
+ dropper->done();
+ }
+ else
+ {
+ gInventory.addObserver(dropper);
+ }
+ }
+ return rv;
+}
+
+BOOL LLToolDragAndDrop::dadUpdateInventoryCategory(LLViewerObject* obj,
+ BOOL drop)
+{
+ EAcceptance rv = dad3dUpdateInventoryCategory(obj, -1, MASK_NONE, drop);
+ return (rv >= ACCEPT_YES_COPY_SINGLE);
+}
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventoryObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryObject()" << llendl;
+
+ // item has to be in agent inventory.
+ if(mSource != SOURCE_AGENT) return ACCEPT_NO;
+
+ // find the item now.
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!item->getPermissions().allowOperationBy(PERM_TRANSFER, gAgent.getID()))
+ {
+ // cannot give away no-transfer objects
+ return ACCEPT_NO;
+ }
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if(avatar && avatar->isWearingAttachment( item->getUUID() ) )
+ {
+ // You can't give objects that are attached to you
+ return ACCEPT_NO;
+ }
+ if( obj && avatar )
+ {
+ if(drop)
+ {
+ giveInventory(obj->getID(), item );
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+ }
+ return ACCEPT_NO;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventory()" << llendl;
+ // item has to be in agent inventory.
+ if(mSource != SOURCE_AGENT) return ACCEPT_NO;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!isInventoryGiveAcceptable(item))
+ {
+ return ACCEPT_NO;
+ }
+ if(drop && obj)
+ {
+ giveInventory(obj->getID(), item);
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dGiveInventoryCategory(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dGiveInventoryCategory()" << llendl;
+ if(drop && obj)
+ {
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ giveInventoryCategory(obj->getID(), cat);
+ }
+ //FIXME: deal with all the issues surrounding multi-object inventory transfers
+ return ACCEPT_YES_SINGLE;
+}
+
+
+EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnLand(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezFromObjectOnLand()" << llendl;
+ LLViewerInventoryItem* item = NULL;
+ LLViewerInventoryCategory* cat = NULL;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if(!item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ || !item->getPermissions().allowTransferTo(LLUUID::null))
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop)
+ {
+ dropObject(obj, TRUE, TRUE, FALSE);
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dRezFromObjectOnObject(
+ LLViewerObject* obj, S32 face, MASK mask, BOOL drop)
+{
+ lldebugs << "LLToolDragAndDrop::dad3dRezFromObjectOnObject()" << llendl;
+ LLViewerInventoryItem* item;
+ LLViewerInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!item || !item->isComplete()) return ACCEPT_NO;
+ if((mask & MASK_CONTROL))
+ {
+ // *HACK: In order to resolve SL-22177, we need to block drags
+ // from notecards and objects onto other objects.
+ return ACCEPT_NO;
+
+ // *HACK: uncomment this when appropriate
+ //EAcceptance rv = willObjectAcceptInventory(obj, item);
+ //if(drop && (ACCEPT_YES_SINGLE <= rv))
+ //{
+ // dropInventory(obj, item, mSource, mSourceID);
+ //}
+ //return rv;
+ }
+ if(!item->getPermissions().allowCopyBy(gAgent.getID(),
+ gAgent.getGroupID())
+ || !item->getPermissions().allowTransferTo(LLUUID::null))
+ {
+ return ACCEPT_NO_LOCKED;
+ }
+ if(drop)
+ {
+ dropObject(obj, FALSE, TRUE, FALSE);
+ }
+ return ACCEPT_YES_SINGLE;
+}
+
+EAcceptance LLToolDragAndDrop::dad3dCategoryOnLand(
+ LLViewerObject *obj, S32 face, MASK mask, BOOL drop)
+{
+ return ACCEPT_NO;
+ /*
+ lldebugs << "LLToolDragAndDrop::dad3dCategoryOnLand()" << llendl;
+ LLInventoryItem* item;
+ LLInventoryCategory* cat;
+ locateInventory(item, cat);
+ if(!cat) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+
+ // find all the items in the category
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLDropCopyableItems droppable;
+ gInventory.collectDescendentsIf(cat->getUUID(),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ droppable);
+ if(items.count() > 0)
+ {
+ rv = ACCEPT_YES_SINGLE;
+ }
+ if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+ {
+ createContainer(items, cat->getName());
+ return ACCEPT_NO;
+ }
+ return rv;
+ */
+}
+
+
+// This is based on ALOT of copied, special-cased code
+// This shortcuts alot of steps to make a basic object
+// w/ an inventory and a special permissions set
+EAcceptance LLToolDragAndDrop::dad3dAssetOnLand(
+ LLViewerObject *obj, S32 face, MASK mask, BOOL drop)
+{
+ return ACCEPT_NO;
+ /*
+ lldebugs << "LLToolDragAndDrop::dad3dAssetOnLand()" << llendl;
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLViewerInventoryItem::item_array_t copyable_items;
+ locateMultipleInventory(items, cats);
+ if(!items.count()) return ACCEPT_NO;
+ EAcceptance rv = ACCEPT_NO;
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* item = items[i];
+ if(item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ copyable_items.put(item);
+ rv = ACCEPT_YES_SINGLE;
+ }
+ }
+
+ if((rv >= ACCEPT_YES_COPY_SINGLE) && drop)
+ {
+ createContainer(copyable_items, NULL);
+ }
+
+ return rv;
+ */
+}
+
+LLInventoryObject* LLToolDragAndDrop::locateInventory(
+ LLViewerInventoryItem*& item,
+ LLViewerInventoryCategory*& cat)
+{
+ item = NULL;
+ cat = NULL;
+ if(mCargoIDs.empty()) return NULL;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // The object should be in user inventory.
+ item = (LLViewerInventoryItem*)gInventory.getItem(mCargoIDs[mCurItemIndex]);
+ cat = (LLViewerInventoryCategory*)gInventory.getCategory(mCargoIDs[mCurItemIndex]);
+ }
+ else if(mSource == SOURCE_WORLD)
+ {
+ // This object is in some task inventory somewhere.
+ LLViewerObject* obj = gObjectList.findObject(mSourceID);
+ if(obj)
+ {
+ if((mCargoTypes[mCurItemIndex] == DAD_CATEGORY)
+ || (mCargoTypes[mCurItemIndex] == DAD_ROOT_CATEGORY))
+ {
+ cat = (LLViewerInventoryCategory*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
+ }
+ else
+ {
+ item = (LLViewerInventoryItem*)obj->getInventoryObject(mCargoIDs[mCurItemIndex]);
+ }
+ }
+ }
+ else if(mSource == SOURCE_NOTECARD)
+ {
+ LLPreviewNotecard* card;
+ card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
+ if(card)
+ {
+ item = (LLViewerInventoryItem*)card->getDragItem();
+ }
+ }
+ if(item) return item;
+ if(cat) return cat;
+ return NULL;
+}
+
+/*
+LLInventoryObject* LLToolDragAndDrop::locateMultipleInventory(LLViewerInventoryCategory::cat_array_t& cats,
+ LLViewerInventoryItem::item_array_t& items)
+{
+ if(mCargoIDs.count() == 0) return NULL;
+ if((mSource == SOURCE_AGENT) || (mSource == SOURCE_LIBRARY))
+ {
+ // The object should be in user inventory.
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryItem* item = gInventory.getItem(mCargoIDs[i]);
+ if (item)
+ {
+ items.put(item);
+ }
+ LLInventoryCategory* category = gInventory.getCategory(mCargoIDs[i]);
+ if (category)
+ {
+ cats.put(category);
+ }
+ }
+ }
+ else if(mSource == SOURCE_WORLD)
+ {
+ // This object is in some task inventory somewhere.
+ LLViewerObject* obj = gObjectList.findObject(mSourceID);
+ if(obj)
+ {
+ if((mCargoType == DAD_CATEGORY)
+ || (mCargoType == DAD_ROOT_CATEGORY))
+ {
+ // The object should be in user inventory.
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryCategory* category = (LLInventoryCategory*)obj->getInventoryObject(mCargoIDs[i]);
+ if (category)
+ {
+ cats.put(category);
+ }
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < mCargoIDs.count(); i++)
+ {
+ LLInventoryItem* item = (LLInventoryItem*)obj->getInventoryObject(mCargoIDs[i]);
+ if (item)
+ {
+ items.put(item);
+ }
+ }
+ }
+ }
+ }
+ else if(mSource == SOURCE_NOTECARD)
+ {
+ LLPreviewNotecard* card;
+ card = (LLPreviewNotecard*)LLPreview::find(mSourceID);
+ if(card)
+ {
+ items.put((LLInventoryItem*)card->getDragItem());
+ }
+ }
+ if(items.count()) return items[0];
+ if(cats.count()) return cats[0];
+ return NULL;
+}
+*/
+
+void LLToolDragAndDrop::createContainer(LLViewerInventoryItem::item_array_t &items, const char* preferred_name )
+{
+ llwarns << "LLToolDragAndDrop::createContainer()" << llendl;
+ return;
+}
+
+
+// utility functions
+
+void pack_permissions_slam(LLMessageSystem* msg, U32 flags, const LLPermissions& perms)
+{
+ U32 group_mask = perms.getMaskGroup();
+ U32 everyone_mask = perms.getMaskEveryone();
+ U32 next_owner_mask = perms.getMaskNextOwner();
+
+ msg->addU32Fast(_PREHASH_ItemFlags, flags);
+ msg->addU32Fast(_PREHASH_GroupMask, group_mask);
+ msg->addU32Fast(_PREHASH_EveryoneMask, everyone_mask);
+ msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_mask);
+}
diff --git a/indra/newview/lltooldraganddrop.h b/indra/newview/lltooldraganddrop.h
new file mode 100644
index 0000000000..8f1c7c8ddb
--- /dev/null
+++ b/indra/newview/lltooldraganddrop.h
@@ -0,0 +1,239 @@
+/**
+ * @file lltooldraganddrop.h
+ * @brief LLToolDragAndDrop class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLDRAGANDDROP_H
+#define LL_TOOLDRAGANDDROP_H
+
+#include "lltool.h"
+#include "llview.h"
+#include "lluuid.h"
+#include "stdenums.h"
+#include "llassetstorage.h"
+#include "lldarray.h"
+#include "llpermissions.h"
+#include "llwindow.h"
+#include "llviewerinventory.h"
+
+class LLToolDragAndDrop;
+class LLViewerRegion;
+class LLVOAvatar;
+
+class LLToolDragAndDrop : public LLTool
+{
+public:
+ LLToolDragAndDrop();
+
+ // overridden from LLTool
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleKey(KEY key, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen);
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+ void setDragStart( S32 x, S32 y ); // In screen space
+ BOOL isOverThreshold( S32 x, S32 y ); // In screen space
+
+ enum ESource
+ {
+ SOURCE_AGENT,
+ SOURCE_WORLD,
+ SOURCE_NOTECARD,
+ SOURCE_LIBRARY
+ };
+
+ void beginDrag(EDragAndDropType type,
+ const LLUUID& cargo_id,
+ ESource source,
+ const LLUUID& source_id = LLUUID::null,
+ const LLUUID& object_id = LLUUID::null);
+ void beginMultiDrag(const std::vector<EDragAndDropType> types,
+ const std::vector<LLUUID>& cargo_ids,
+ ESource source,
+ const LLUUID& source_id = LLUUID::null);
+ void endDrag();
+ ESource getSource() const { return mSource; }
+ const LLUUID& getSourceID() const { return mSourceID; }
+ const LLUUID& getObjectID() const { return mObjectID; }
+ EAcceptance getLastAccept() { return mLastAccept; }
+
+protected:
+ enum EDropTarget
+ {
+ DT_NONE = 0,
+ DT_SELF = 1,
+ DT_AVATAR = 2,
+ DT_OBJECT = 3,
+ DT_LAND = 4,
+ DT_COUNT = 5
+ };
+
+ // dragOrDrop3dImpl points to a member of LLToolDragAndDrop that
+ // takes parameters (LLViewerObject* obj, S32 face, MASK, BOOL
+ // drop) and returns a BOOL if drop is ok
+ typedef EAcceptance (LLToolDragAndDrop::*dragOrDrop3dImpl)
+ (LLViewerObject*, S32, MASK, BOOL);
+
+ void dragOrDrop(S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance);
+ void dragOrDrop3D(S32 x, S32 y, MASK mask, BOOL drop,
+ EAcceptance* acceptance);
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+
+ S32 mDragStartX;
+ S32 mDragStartY;
+
+ std::vector<EDragAndDropType> mCargoTypes;
+ //void* mCargoData;
+ std::vector<LLUUID> mCargoIDs;
+ ESource mSource;
+ LLUUID mSourceID;
+ LLUUID mObjectID;
+
+ LLVector3d mLastCameraPos;
+ LLVector3d mLastHitPos;
+
+ ECursorType mCursor;
+ EAcceptance mLastAccept;
+ BOOL mDrop;
+ S32 mCurItemIndex;
+ LLString mToolTipMsg;
+
+ // array of pointers to functions that implement the logic to
+ // dragging and dropping into the simulator.
+ static dragOrDrop3dImpl sDragAndDrop3d[DAD_COUNT][DT_COUNT];
+
+protected:
+ // 3d drop functions. these call down into the static functions
+ // named drop<ThingToDrop> if drop is TRUE and permissions allow
+ // that behavior.
+ EAcceptance dad3dNULL(LLViewerObject*, S32, MASK, BOOL);
+ EAcceptance dad3dRezObjectOnLand(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezObjectOnObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezScript(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dTextureObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+// EAcceptance dad3dTextureSelf(LLViewerObject* obj, S32 face,
+// MASK mask, BOOL drop);
+ EAcceptance dad3dWearItem(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dWearCategory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dUpdateInventory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dUpdateInventoryCategory(LLViewerObject* obj,
+ S32 face,
+ MASK mask,
+ BOOL drop);
+ EAcceptance dad3dGiveInventoryObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dGiveInventory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dGiveInventoryCategory(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezFromObjectOnLand(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezFromObjectOnObject(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dRezAttachmentFromInv(LLViewerObject* obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dCategoryOnLand(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dAssetOnLand(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+ EAcceptance dad3dActivateGesture(LLViewerObject *obj, S32 face,
+ MASK mask, BOOL drop);
+
+ // This method converts mCargoID to an inventory item or
+ // folder. If no item or category is found, both pointers will be
+ // returned NULL.
+ LLInventoryObject* locateInventory(LLViewerInventoryItem*& item,
+ LLViewerInventoryCategory*& cat);
+
+ //LLInventoryObject* locateMultipleInventory(
+ // LLViewerInventoryCategory::cat_array_t& cats,
+ // LLViewerInventoryItem::item_array_t& items);
+
+ void createContainer(LLViewerInventoryItem::item_array_t &items, const char* preferred_name);
+ void dropObject(LLViewerObject* raycast_target,
+ BOOL bypass_sim_raycast,
+ BOOL from_task_inventory,
+ BOOL remove_from_inventory);
+
+ // accessor that looks at permissions, copyability, and names of
+ // inventory items to determine if a drop would be ok.
+ static EAcceptance willObjectAcceptInventory(LLViewerObject* obj, LLInventoryItem* item);
+
+ // deal with permissions of object, etc. returns TRUE if drop can
+ // proceed, otherwise FALSE.
+ static BOOL handleDropTextureProtections(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ LLToolDragAndDrop::ESource source,
+ const LLUUID& src_id);
+
+
+ // give inventory item functionality
+ static void handleCopyProtectedItem(S32 option, void* data);
+ static void commitGiveInventoryItem(const LLUUID& to_agent,
+ LLInventoryItem* item);
+
+ // give inventory category functionality
+ static void handleCopyProtectedCategory(S32 option, void* data);
+ static void commitGiveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* cat);
+public:
+ // helper functions
+ static BOOL isInventoryDropAcceptable(LLViewerObject* obj, LLInventoryItem* item) { return (ACCEPT_YES_COPY_SINGLE <= willObjectAcceptInventory(obj, item)); }
+
+ // This simple helper function assumes you are attempting to
+ // transfer item. returns true if you can give, otherwise false.
+ static BOOL isInventoryGiveAcceptable(LLInventoryItem* item);
+ static BOOL isInventoryGroupGiveAcceptable(LLInventoryItem* item);
+
+ BOOL dadUpdateInventory(LLViewerObject* obj, BOOL drop);
+ BOOL dadUpdateInventoryCategory(LLViewerObject* obj, BOOL drop);
+
+ // methods that act on the simulator state.
+ static void dropScript(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ BOOL active,
+ ESource source,
+ const LLUUID& src_id);
+ static void dropTextureOneFace(LLViewerObject* hit_obj, S32 hit_face,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+ static void dropTextureAllFaces(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+ //static void dropTextureOneFaceAvatar(LLVOAvatar* avatar,S32 hit_face,
+ // LLInventoryItem* item)
+
+ static void dropInventory(LLViewerObject* hit_obj,
+ LLInventoryItem* item,
+ ESource source,
+ const LLUUID& src_id);
+
+ static void giveInventory(const LLUUID& to_agent, LLInventoryItem* item);
+ static void giveInventoryCategory(const LLUUID& to_agent,
+ LLInventoryCategory* item);
+};
+
+// Singleton
+extern LLToolDragAndDrop *gToolDragAndDrop;
+
+// utility functions
+void pack_permissions_slam(LLMessageSystem* msg, U32 flags, const LLPermissions& perms);
+
+#endif // LL_TOOLDRAGANDDROP_H
diff --git a/indra/newview/lltoolface.cpp b/indra/newview/lltoolface.cpp
new file mode 100644
index 0000000000..c35fcb434c
--- /dev/null
+++ b/indra/newview/lltoolface.cpp
@@ -0,0 +1,141 @@
+/**
+ * @file lltoolface.cpp
+ * @brief A tool to manipulate faces
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolface.h"
+
+// Library includes
+#include "v3math.h"
+
+// Viewer includes
+#include "llagent.h"
+//#include "llbuildview.h"
+#include "llviewercontrol.h"
+#include "llselectmgr.h"
+#include "lltoolview.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llfloatertools.h"
+
+// Globals
+LLToolFace *gToolFace = NULL;
+
+//
+// Member functions
+//
+
+LLToolFace::LLToolFace()
+: LLTool("Texture")
+{ }
+
+
+LLToolFace::~LLToolFace()
+{ }
+
+
+BOOL LLToolFace::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (!gSelectMgr->isEmpty())
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its properties
+ //gBuildView->showFacePanel();
+ gFloaterTools->showPanel( LLFloaterTools::PANEL_FACE );
+ //gBuildView->showMore(LLBuildView::PANEL_FACE);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+
+BOOL LLToolFace::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gPickFaces = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolFace::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj)
+ {
+ S32 hit_face = gLastHitObjectFace;
+
+ if (hit_obj->isAvatar())
+ {
+ // ...clicked on an avatar, so don't do anything
+ return;
+ }
+
+ // ...clicked on a world object, try to pick the appropriate face
+
+ if (mask & MASK_SHIFT)
+ {
+ // If object not selected, need to inform sim
+ if ( !hit_obj->isSelected() )
+ {
+ // object wasn't selected so add the object and face
+ gSelectMgr->selectObjectOnly(hit_obj, hit_face);
+ }
+ else if (!gSelectMgr->contains(hit_obj, hit_face) )
+ {
+ // object is selected, but not this face, so add it.
+ gSelectMgr->addAsIndividual(hit_obj, hit_face);
+ }
+ else
+ {
+ // object is selected, as is this face, so remove the face.
+ gSelectMgr->remove(hit_obj, hit_face);
+
+ // BUG: If you remove the last face, the simulator won't know about it.
+ }
+ }
+ else
+ {
+ // clicked without modifiers, select only
+ // this face
+ gSelectMgr->deselectAll();
+ gSelectMgr->selectObjectOnly(hit_obj, hit_face);
+ }
+ }
+ else
+ {
+ if (!(mask == MASK_SHIFT))
+ {
+ gSelectMgr->deselectAll();
+ }
+ }
+}
+
+
+void LLToolFace::handleSelect()
+{
+ // From now on, draw faces
+ gSelectMgr->setTEMode(TRUE);
+}
+
+
+void LLToolFace::handleDeselect()
+{
+ // Stop drawing faces
+ gSelectMgr->setTEMode(FALSE);
+}
+
+
+void LLToolFace::render()
+{
+ // for now, do nothing
+}
diff --git a/indra/newview/lltoolface.h b/indra/newview/lltoolface.h
new file mode 100644
index 0000000000..d2b4be5deb
--- /dev/null
+++ b/indra/newview/lltoolface.h
@@ -0,0 +1,34 @@
+/**
+ * @file lltoolface.h
+ * @brief A tool to select object faces
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLFACE_H
+#define LL_LLTOOLFACE_H
+
+#include "lltool.h"
+
+class LLViewerObject;
+
+class LLToolFace
+: public LLTool
+{
+public:
+ LLToolFace();
+ virtual ~LLToolFace();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual void handleSelect();
+ virtual void handleDeselect();
+ virtual void render(); // draw face highlights
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+};
+
+extern LLToolFace *gToolFace;
+
+#endif
diff --git a/indra/newview/lltoolfocus.cpp b/indra/newview/lltoolfocus.cpp
new file mode 100644
index 0000000000..627a49a6cc
--- /dev/null
+++ b/indra/newview/lltoolfocus.cpp
@@ -0,0 +1,442 @@
+/**
+ * @file lltoolfocus.cpp
+ * @brief A tool to set the build focus point.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolfocus.h"
+
+// Library includes
+#include "v3math.h"
+#include "llfontgl.h"
+#include "llui.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llhoverview.h"
+#include "llhudmanager.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "llviewborder.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llvoavatar.h"
+#include "llmorphview.h"
+
+// Globals
+LLToolCamera *gToolCamera = NULL;
+BOOL gCameraBtnOrbit = FALSE;
+BOOL gCameraBtnPan = FALSE;
+const S32 SLOP_RANGE = 4;
+const F32 FOCUS_OFFSET_FACTOR = 1.f;
+
+extern void handle_first_tool(void*);
+
+
+//
+// Camera - shared functionality
+//
+
+LLToolCamera::LLToolCamera()
+: LLTool("Camera"),
+ mAccumX(0),
+ mAccumY(0),
+ mMouseDownX(0),
+ mMouseDownY(0),
+ mOutsideSlopX(FALSE),
+ mOutsideSlopY(FALSE),
+ mValidClickPoint(FALSE),
+ mMouseSteering(FALSE),
+ mMouseUpX(0),
+ mMouseUpY(0),
+ mMouseUpMask(MASK_NONE)
+{ }
+
+
+LLToolCamera::~LLToolCamera()
+{ }
+
+// virtual
+void LLToolCamera::handleSelect()
+{
+ gFloaterTools->setStatusText("Click and drag to change view");
+}
+
+// virtual
+void LLToolCamera::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+// gAgent.setLookingAtAvatar(FALSE);
+}
+
+BOOL LLToolCamera::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Ensure a mouseup
+ setMouseCapture(TRUE);
+
+ // call the base class to propogate info to sim
+ LLTool::handleMouseDown(x, y, mask);
+
+ mAccumX = 0;
+ mAccumY = 0;
+
+ mOutsideSlopX = FALSE;
+ mOutsideSlopY = FALSE;
+
+ mValidClickPoint = FALSE;
+
+ // If mouse capture gets ripped away, claim we moused up
+ // at the point we moused down. JC
+ mMouseUpX = x;
+ mMouseUpY = y;
+ mMouseUpMask = mask;
+
+ gViewerWindow->hideCursor();
+
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ // don't steal focus from UI
+ return FALSE;
+}
+
+void LLToolCamera::pickCallback(S32 x, S32 y, MASK mask)
+{
+ if (!gToolCamera->hasMouseCapture())
+ {
+ return;
+ }
+
+ gToolCamera->mMouseDownX = x;
+ gToolCamera->mMouseDownY = y;
+
+ gViewerWindow->moveCursorToCenter();
+
+ // Potentially recenter if click outside rectangle
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+
+ // Check for hit the sky, or some other invalid point
+ if (!hit_obj && gLastHitPosGlobal.isExactlyZero())
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+
+ // check for hud attachments
+ if (hit_obj && hit_obj->isHUDAttachment())
+ {
+ if (!gSelectMgr->getObjectCount() || gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+ }
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ BOOL good_customize_avatar_hit = FALSE;
+ if( hit_obj )
+ {
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( hit_obj == avatar)
+ {
+ // It's you
+ good_customize_avatar_hit = TRUE;
+ }
+ else
+ if( hit_obj->isAttachment() && hit_obj->permYouOwner() )
+ {
+ // It's an attachment that you're wearing
+ good_customize_avatar_hit = TRUE;
+ }
+ }
+
+ if( !good_customize_avatar_hit )
+ {
+ gToolCamera->mValidClickPoint = FALSE;
+ return;
+ }
+
+ if( gMorphView )
+ {
+ gMorphView->setCameraDrivenByKeys( FALSE );
+ }
+ }
+ //RN: check to see if this is mouse-driving as opposed to ALT-zoom or Focus tool
+ else if (mask & MASK_ALT ||
+ (gToolMgr->getCurrentTool(mask)->getName() == "Camera"))
+ {
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ if (hit_obj)
+ {
+ // ...clicked on a world object, so focus at its position
+ // Use "gLastHitPosGlobal" because it's correct for avatar heads,
+ // not pelvis.
+ if (!hit_obj->isHUDAttachment())
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal( gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+ }
+ }
+ else if (!gLastHitPosGlobal.isExactlyZero())
+ {
+ // Hit the ground
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal( gLastHitPosGlobal, gLastHitObjectID);
+ }
+
+ if (!(mask & MASK_ALT) &&
+ gAgent.cameraThirdPerson() &&
+ gViewerWindow->getLeftMouseDown() &&
+ !gSavedSettings.getBOOL("FreezeTime") &&
+ (hit_obj == gAgent.getAvatarObject() ||
+ (hit_obj && hit_obj->isAttachment() && LLVOAvatar::findAvatarFromAttachment(hit_obj)->mIsSelf)))
+ {
+ gToolCamera->mMouseSteering = TRUE;
+ }
+
+ }
+
+ gToolCamera->mValidClickPoint = TRUE;
+
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ gAgent.setFocusOnAvatar(FALSE, FALSE);
+
+ LLVector3d cam_pos = gAgent.getCameraPositionGlobal();
+ cam_pos -= LLVector3d(gCamera->getLeftAxis() * gAgent.calcCustomizeAvatarUIOffset( cam_pos ));
+
+ gAgent.setCameraPosAndFocusGlobal( cam_pos, gLastHitObjectOffset + gLastHitPosGlobal, gLastHitObjectID);
+ }
+}
+
+
+// "Let go" of the mouse, for example on mouse up or when
+// we lose mouse capture. This ensures that cursor becomes visible
+// if a modal dialog pops up during Alt-Zoom. JC
+void LLToolCamera::releaseMouse()
+{
+ // Need to tell the sim that the mouse button is up, since this
+ // tool is no longer working and cursor is visible (despite actual
+ // mouse button status).
+ LLTool::handleMouseUp(mMouseUpX, mMouseUpY, mMouseUpMask);
+
+ gViewerWindow->showCursor();
+
+ gToolMgr->clearTransientTool();
+
+ mMouseSteering = FALSE;
+ mValidClickPoint = FALSE;
+ mOutsideSlopX = FALSE;
+ mOutsideSlopY = FALSE;
+}
+
+
+BOOL LLToolCamera::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // Claim that we're mousing up somewhere
+ mMouseUpX = x;
+ mMouseUpY = y;
+ mMouseUpMask = mask;
+
+ if (hasMouseCapture())
+ {
+ if (mValidClickPoint)
+ {
+ if( CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode() )
+ {
+ LLCoordGL mouse_pos;
+ LLVector3 focus_pos = gAgent.getPosAgentFromGlobal(gAgent.getFocusGlobal());
+ gCamera->projectPosAgentToScreen(focus_pos, mouse_pos);
+
+ LLUI::setCursorPositionScreen(mouse_pos.mX, mouse_pos.mY);
+ }
+ else if (mMouseSteering)
+ {
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+ else
+ {
+ gViewerWindow->moveCursorToCenter();
+ }
+ }
+ else
+ {
+ // not a valid zoomable object
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+
+ // calls releaseMouse() internally
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ releaseMouse();
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolCamera::handleHover(S32 x, S32 y, MASK mask)
+{
+ S32 dx = gViewerWindow->getCurrentMouseDX();
+ S32 dy = gViewerWindow->getCurrentMouseDY();
+
+ BOOL moved_outside_slop = FALSE;
+
+ if (hasMouseCapture() && mValidClickPoint)
+ {
+ mAccumX += llabs(dx);
+ mAccumY += llabs(dy);
+
+ if (mAccumX >= SLOP_RANGE)
+ {
+ if (!mOutsideSlopX)
+ {
+ moved_outside_slop = TRUE;
+ }
+ mOutsideSlopX = TRUE;
+ }
+
+ if (mAccumY >= SLOP_RANGE)
+ {
+ if (!mOutsideSlopY)
+ {
+ moved_outside_slop = TRUE;
+ }
+ mOutsideSlopY = TRUE;
+ }
+ }
+
+ if (mOutsideSlopX || mOutsideSlopY)
+ {
+ if (!mValidClickPoint)
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [invalid point]" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_NO);
+ gViewerWindow->showCursor();
+ return TRUE;
+ }
+
+ if (gCameraBtnOrbit ||
+ mask == MASK_ORBIT ||
+ mask == (MASK_ALT | MASK_ORBIT))
+ {
+ // Orbit tool
+ if (hasMouseCapture())
+ {
+ const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL );
+ }
+
+ if (dy != 0)
+ {
+ gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL );
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolFocus [active]" << llendl;
+ }
+ else if ( gCameraBtnPan ||
+ mask == MASK_PAN ||
+ mask == (MASK_PAN | MASK_ALT) )
+ {
+ // Pan tool
+ if (hasMouseCapture())
+ {
+ LLVector3d camera_to_focus = gAgent.getCameraPositionGlobal();
+ camera_to_focus -= gAgent.getFocusGlobal();
+ F32 dist = (F32) camera_to_focus.normVec();
+
+ // Fudge factor for pan
+ F32 meters_per_pixel = 3.f * dist / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraPanLeft( dx * meters_per_pixel );
+ }
+
+ if (dy != 0)
+ {
+ gAgent.cameraPanUp( -dy * meters_per_pixel );
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPan" << llendl;
+ }
+ else
+ {
+ // Zoom tool
+ if (hasMouseCapture())
+ {
+
+ const F32 RADIANS_PER_PIXEL = 360.f * DEG_TO_RAD / gViewerWindow->getWindowWidth();
+
+ if (dx != 0)
+ {
+ gAgent.cameraOrbitAround( -dx * RADIANS_PER_PIXEL );
+ }
+
+ const F32 IN_FACTOR = 0.99f;
+
+ if (dy != 0 && mOutsideSlopY )
+ {
+ if (mMouseSteering)
+ {
+ gAgent.cameraOrbitOver( -dy * RADIANS_PER_PIXEL );
+ }
+ else
+ {
+ gAgent.cameraZoomIn( pow( IN_FACTOR, dy ) );
+ }
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolZoom" << llendl;
+ }
+ }
+
+ if (gCameraBtnOrbit ||
+ mask == MASK_ORBIT ||
+ mask == (MASK_ALT | MASK_ORBIT))
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLCAMERA);
+ }
+ else if ( gCameraBtnPan ||
+ mask == MASK_PAN ||
+ mask == (MASK_PAN | MASK_ALT) )
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLPAN);
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLZOOMIN);
+ }
+
+ return TRUE;
+}
+
+
+void LLToolCamera::onMouseCaptureLost()
+{
+ releaseMouse();
+}
diff --git a/indra/newview/lltoolfocus.h b/indra/newview/lltoolfocus.h
new file mode 100644
index 0000000000..1d3d4a728b
--- /dev/null
+++ b/indra/newview/lltoolfocus.h
@@ -0,0 +1,58 @@
+/**
+ * @file lltoolfocus.h
+ * @brief A tool to set the build focus point.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLFOCUS_H
+#define LL_LLTOOLFOCUS_H
+
+#include "lltool.h"
+
+class LLToolCamera
+: public LLTool
+{
+public:
+ LLToolCamera();
+ virtual ~LLToolCamera();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void onMouseCaptureLost();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+ BOOL mouseSteerMode() { return mMouseSteering; }
+
+protected:
+ // called from handleMouseUp and onMouseCaptureLost to "let go"
+ // of the mouse and make it visible JC
+ void releaseMouse();
+
+protected:
+ S32 mAccumX;
+ S32 mAccumY;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ BOOL mOutsideSlopX;
+ BOOL mOutsideSlopY;
+ BOOL mValidClickPoint;
+ BOOL mMouseSteering;
+ S32 mMouseUpX; // needed for releaseMouse()
+ S32 mMouseUpY;
+ MASK mMouseUpMask;
+};
+
+
+extern LLToolCamera *gToolCamera;
+
+extern BOOL gCameraBtnOrbit;
+extern BOOL gCameraBtnPan;
+
+#endif
diff --git a/indra/newview/lltoolgrab.cpp b/indra/newview/lltoolgrab.cpp
new file mode 100644
index 0000000000..898322d5ca
--- /dev/null
+++ b/indra/newview/lltoolgrab.cpp
@@ -0,0 +1,875 @@
+/**
+ * @file lltoolgrab.cpp
+ * @brief LLToolGrab class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolgrab.h"
+
+// library headers
+#include "indra_constants.h" // for agent control flags
+#include "llviewercontrol.h"
+#include "llquaternion.h"
+#include "llbox.h"
+#include "message.h"
+#include "llview.h"
+#include "llfontgl.h"
+#include "llui.h"
+
+// newview headers
+#include "llagent.h"
+//#include "llfloateravatarinfo.h"
+#include "lldrawable.h"
+#include "llfloatertools.h"
+#include "llhudeffect.h"
+#include "llhudmanager.h"
+#include "llregionposition.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llviewercamera.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h"
+
+const S32 SLOP_DIST_SQ = 4;
+
+// Override modifier key behavior with these buttons
+BOOL gGrabBtnVertical = FALSE;
+BOOL gGrabBtnSpin = FALSE;
+LLTool* gGrabTransientTool = NULL;
+LLToolGrab *gToolGrab = NULL;
+extern BOOL gDebugClicks;
+
+//
+// Methods
+//
+LLToolGrab::LLToolGrab( LLToolComposite* composite )
+: LLTool( "Grab", composite ),
+ mMode( GRAB_INACTIVE ),
+ mVerticalDragging( FALSE ),
+ mHitLand(FALSE),
+ mHitObjectID(),
+ mGrabObject( NULL ),
+ mMouseDownX( -1 ),
+ mMouseDownY( -1 ),
+ mHasMoved( FALSE ),
+ mSpinGrabbing( FALSE ),
+ mSpinRotation(),
+ mHideBuildHighlight(FALSE)
+{ }
+
+LLToolGrab::~LLToolGrab()
+{ }
+
+
+// virtual
+void LLToolGrab::handleSelect()
+{
+ gFloaterTools->setStatusText("Drag to move objects, Ctrl to lift, Ctrl-Shift to spin");
+ gGrabBtnVertical = FALSE;
+ gGrabBtnSpin = FALSE;
+}
+
+void LLToolGrab::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+
+ gFloaterTools->setStatusText("");
+}
+
+BOOL LLToolGrab::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleDoubleClick (becoming mouseDown)" << llendl;
+ }
+
+ return FALSE;
+}
+
+BOOL LLToolGrab::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleMouseDown" << llendl;
+ }
+
+ mHitLand = FALSE;
+
+ // call the base class to propogate info to sim
+ LLTool::handleMouseDown(x, y, mask);
+
+ if (!gAgent.leftButtonGrabbed())
+ {
+ // can grab transparent objects (how touch event propagates, scripters rely on this)
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback, TRUE);
+ }
+ return TRUE;
+}
+
+void LLToolGrab::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject *objectp = gObjectList.findObject( gLastHitObjectID );
+
+ BOOL extend_select = (mask & MASK_SHIFT);
+
+ if (!extend_select && !gSelectMgr->isEmpty())
+ {
+ gSelectMgr->deselectAll();
+ gToolGrab->mDeselectedThisClick = TRUE;
+ }
+ else
+ {
+ gToolGrab->mDeselectedThisClick = FALSE;
+ }
+
+ // if not over object, do nothing
+ if (!objectp)
+ {
+ gToolGrab->setMouseCapture(TRUE);
+ gToolGrab->mMode = GRAB_NOOBJECT;
+ gToolGrab->mHitObjectID.setNull();
+ }
+ else
+ {
+ gToolGrab->handleObjectHit(objectp, x, y, mask);
+ }
+}
+
+BOOL LLToolGrab::handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask)
+{
+ mMouseDownX = x;
+ mMouseDownY = y;
+ mMouseMask = mask;
+
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolGrab handleObjectHit " << mMouseDownX << "," << mMouseDownY << llendl;
+ }
+
+ if (objectp->isAvatar())
+ {
+ if (gGrabTransientTool)
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+ return TRUE;
+ }
+
+ setMouseCapture( TRUE );
+
+ mHitObjectID = objectp->getID();
+
+ // Grabs always start from the root
+ // objectp = (LLViewerObject *)objectp->getRoot();
+
+ LLViewerObject* parent = objectp->getRootEdit();
+ BOOL script_touch = (objectp && objectp->flagHandleTouch()) || (parent && parent->flagHandleTouch());
+
+ // Clicks on scripted or physical objects are temporary grabs, so
+ // not "Build mode"
+ mHideBuildHighlight = script_touch || objectp->usePhysics();
+
+ if (!objectp->usePhysics())
+ {
+ // In mouselook, we shouldn't be able to grab non-physical,
+ // non-touchable objects. If it has a touch handler, we
+ // do grab it (so llDetectedGrab works), but movement is
+ // blocked on the server side. JC
+ if (gAgent.cameraMouselook() && !script_touch)
+ {
+ mMode = GRAB_LOCKED;
+ }
+ else
+ {
+ mMode = GRAB_NONPHYSICAL;
+ }
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ // Don't bail out here, go on and grab so buttons can get
+ // their "touched" event.
+ }
+ else if( !objectp->permMove() )
+ {
+ // if mouse is over a physical object without move permission, show feedback if user tries to move it.
+ mMode = GRAB_LOCKED;
+
+ // Don't bail out here, go on and grab so buttons can get
+ // their "touched" event.
+ }
+ else
+ {
+ // if mouse is over a physical object with move permission,
+ // select it and enter "grab" mode (hiding cursor, etc.)
+
+ mMode = GRAB_ACTIVE_CENTER;
+
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ // Always send "touched" message
+
+ mAccumDeltaX = 0;
+ mAccumDeltaY = 0;
+ mHasMoved = FALSE;
+ mOutsideSlop = FALSE;
+
+ mGrabObject = objectp;
+
+ mGrabOffset.clearVec();
+
+ mVerticalDragging = (mask == MASK_VERTICAL) || gGrabBtnVertical;
+
+ startGrab(x, y);
+
+ if ((mask == MASK_SPIN) || gGrabBtnSpin)
+ {
+ startSpin();
+ }
+
+ gSelectMgr->updateSelectionCenter(); // update selection beam
+
+ // update point at
+ LLViewerObject *edit_object = gObjectList.findObject(mHitObjectID);
+ if (edit_object)
+ {
+ LLVector3 local_edit_point = gAgent.getPosAgentFromGlobal(gLastHitNonFloraPosGlobal);
+ local_edit_point -= edit_object->getPositionAgent();
+ local_edit_point = local_edit_point * ~edit_object->getRenderRotation();
+ gAgent.setPointAt(POINTAT_TARGET_GRAB, edit_object, local_edit_point );
+ gAgent.setLookAt(LOOKAT_TARGET_SELECT, edit_object, local_edit_point );
+ }
+
+ // on transient grabs (clicks on world objects), kill the grab immediately
+ if (!gViewerWindow->getLeftMouseDown()
+ && gGrabTransientTool
+ && (mMode == GRAB_NONPHYSICAL || mMode == GRAB_LOCKED))
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+
+ return TRUE;
+}
+
+
+void LLToolGrab::startSpin()
+{
+ mSpinGrabbing = TRUE;
+
+ // Was saveSelectedObjectTransform()
+ LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+ mSpinRotation = root->getRotation();
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectSpinStart);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+}
+
+
+void LLToolGrab::stopSpin()
+{
+ mSpinGrabbing = FALSE;
+
+ LLMessageSystem *msg = gMessageSystem;
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ msg->newMessageFast(_PREHASH_ObjectSpinStop);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ // do nothing
+ break;
+ }
+}
+
+
+void LLToolGrab::startGrab(S32 x, S32 y)
+{
+ // Compute grab_offset in the OBJECT's root's coordinate frame
+ // (sometimes root == object)
+ LLViewerObject *root = (LLViewerObject *)mGrabObject->getRoot();
+
+ // drag from center
+ LLVector3d grab_start_global = root->getPositionGlobal();
+
+ // Where the grab starts, relative to the center of the root object of the set.
+ // JC - This code looks wonky, but I believe it does the right thing.
+ // Otherwise, when you grab a linked object set, it "pops" on the start
+ // of the drag.
+ LLVector3d grab_offsetd = root->getPositionGlobal() - mGrabObject->getPositionGlobal();
+
+ LLVector3 grab_offset;
+ grab_offset.setVec(grab_offsetd);
+
+ LLQuaternion rotation = root->getRotation();
+ rotation.conjQuat();
+ grab_offset = grab_offset * rotation;
+
+ // This planar drag starts at the grab point
+ mDragStartPointGlobal = grab_start_global;
+ mDragStartFromCamera = grab_start_global - gAgent.getCameraPositionGlobal();
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
+ msg->addVector3Fast(_PREHASH_GrabOffset, grab_offset );
+ msg->sendMessage( mGrabObject->getRegion()->getHost());
+
+ mGrabOffsetFromCenterInitial = grab_offset;
+ mGrabHiddenOffsetFromCamera = mDragStartFromCamera;
+
+ mGrabTimer.reset();
+}
+
+
+BOOL LLToolGrab::handleHover(S32 x, S32 y, MASK mask)
+{
+ mLastMouseX = x;
+ mLastMouseY = y;
+
+ if (!gViewerWindow->getLeftMouseDown())
+ {
+ gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
+ setMouseCapture(FALSE);
+ return TRUE;
+ }
+
+ // Do the right hover based on mode
+ switch( mMode )
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ handleHoverActive( x, y, mask ); // cursor hidden
+ break;
+
+ case GRAB_INACTIVE:
+ handleHoverInactive( x, y, mask ); // cursor set here
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_LOCKED:
+ handleHoverFailed( x, y, mask );
+ break;
+
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Dragging.
+void LLToolGrab::handleHoverActive(S32 x, S32 y, MASK mask)
+{
+ llassert( hasMouseCapture() );
+ llassert( mGrabObject );
+ if (mGrabObject->isDead())
+ {
+ // Bail out of drag because object has been killed
+ setMouseCapture(FALSE);
+ return;
+ }
+
+ //--------------------------------------------------
+ // Toggle spinning
+ //--------------------------------------------------
+ if (mSpinGrabbing && !(mask == MASK_SPIN) && !gGrabBtnSpin)
+ {
+ // user released ALT key, stop spinning
+ stopSpin();
+ }
+ else if (!mSpinGrabbing && (mask == MASK_SPIN) )
+ {
+ // user pressed ALT key, start spinning
+ startSpin();
+ }
+
+ //--------------------------------------------------
+ // Toggle vertical dragging
+ //--------------------------------------------------
+ if (mVerticalDragging && !(mask == MASK_VERTICAL) && !gGrabBtnVertical)
+ {
+ // ...switch to horizontal dragging
+ mVerticalDragging = FALSE;
+
+ mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+ mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
+ }
+ else if (!mVerticalDragging && (mask == MASK_VERTICAL) )
+ {
+ // ...switch to vertical dragging
+ mVerticalDragging = TRUE;
+
+ mDragStartPointGlobal = gViewerWindow->clickPointInWorldGlobal(x, y, mGrabObject);
+ mDragStartFromCamera = mDragStartPointGlobal - gAgent.getCameraPositionGlobal();
+ }
+
+ const F32 RADIANS_PER_PIXEL_X = 0.01f;
+ const F32 RADIANS_PER_PIXEL_Y = 0.01f;
+
+ const F32 SENSITIVITY_X = 0.0075f;
+ const F32 SENSITIVITY_Y = 0.0075f;
+
+ S32 dx = x - (gViewerWindow->getWindowWidth() / 2);
+ S32 dy = y - (gViewerWindow->getWindowHeight() / 2);
+
+ if (dx != 0 || dy != 0)
+ {
+ mAccumDeltaX += dx;
+ mAccumDeltaY += dy;
+ S32 dist_sq = mAccumDeltaX * mAccumDeltaX + mAccumDeltaY * mAccumDeltaY;
+ if (dist_sq > SLOP_DIST_SQ)
+ {
+ mOutsideSlop = TRUE;
+ }
+
+ // mouse has moved outside center
+ mHasMoved = TRUE;
+
+ if (mSpinGrabbing)
+ {
+ //------------------------------------------------------
+ // Handle spinning
+ //------------------------------------------------------
+
+ // x motion maps to rotation around vertical axis
+ LLVector3 up(0.f, 0.f, 1.f);
+ LLQuaternion rotation_around_vertical( dx*RADIANS_PER_PIXEL_X, up );
+
+ // y motion maps to rotation around left axis
+ const LLVector3 &agent_left = gCamera->getLeftAxis();
+ LLQuaternion rotation_around_left( dy*RADIANS_PER_PIXEL_Y, agent_left );
+
+ // compose with current rotation
+ mSpinRotation = mSpinRotation * rotation_around_vertical;
+ mSpinRotation = mSpinRotation * rotation_around_left;
+
+ // TODO: Throttle these
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectSpinUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->addQuatFast(_PREHASH_Rotation, mSpinRotation );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ }
+ else
+ {
+ //------------------------------------------------------
+ // Handle grabbing
+ //------------------------------------------------------
+
+ LLVector3d x_part;
+ x_part.setVec(gCamera->getLeftAxis());
+ x_part.mdV[VZ] = 0.0;
+ x_part.normVec();
+
+ LLVector3d y_part;
+ if( mVerticalDragging )
+ {
+ y_part.setVec(gCamera->getUpAxis());
+ // y_part.setVec(0.f, 0.f, 1.f);
+ }
+ else
+ {
+ // drag toward camera
+ y_part = x_part % LLVector3d::z_axis;
+ y_part.mdV[VZ] = 0.0;
+ y_part.normVec();
+ }
+
+ mGrabHiddenOffsetFromCamera = mGrabHiddenOffsetFromCamera
+ + (x_part * (-dx * SENSITIVITY_X))
+ + (y_part * ( dy * SENSITIVITY_Y));
+
+
+ // Send the message to the viewer.
+ F32 dt = mGrabTimer.getElapsedTimeAndResetF32();
+ U32 dt_milliseconds = (U32) (1000.f * dt);
+
+ // need to return offset from mGrabStartPoint
+ LLVector3d grab_point_global;
+
+ grab_point_global = gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
+
+ /* Snap to grid disabled for grab tool - very confusing
+ // Handle snapping to grid, but only when the tool is formally selected.
+ BOOL snap_on = gSavedSettings.getBOOL("SnapEnabled");
+ if (snap_on && !gGrabTransientTool)
+ {
+ F64 snap_size = gSavedSettings.getF32("GridResolution");
+ U8 snap_dimensions = (mVerticalDragging ? 3 : 2);
+
+ for (U8 i = 0; i < snap_dimensions; i++)
+ {
+ grab_point_global.mdV[i] += snap_size / 2;
+ grab_point_global.mdV[i] -= fmod(grab_point_global.mdV[i], snap_size);
+ }
+ }
+ */
+
+ // Don't let object centers go underground.
+ F32 land_height = gWorldPointer->resolveLandHeightGlobal(grab_point_global);
+
+ if (grab_point_global.mdV[VZ] < land_height)
+ {
+ grab_point_global.mdV[VZ] = land_height;
+ }
+
+ // For safety, cap heights where objects can be dragged
+ if (grab_point_global.mdV[VZ] > MAX_OBJECT_Z)
+ {
+ grab_point_global.mdV[VZ] = MAX_OBJECT_Z;
+ }
+
+ grab_point_global = gWorldp->clipToVisibleRegions(mDragStartPointGlobal, grab_point_global);
+ // propagate constrained grab point back to grab offset
+ mGrabHiddenOffsetFromCamera = grab_point_global - gAgent.getCameraPositionGlobal();
+
+ // Handle auto-rotation at screen edge.
+ LLVector3 grab_pos_agent = gAgent.getPosAgentFromGlobal( grab_point_global );
+
+ LLCoordGL grab_center_gl( gViewerWindow->getWindowWidth() / 2, gViewerWindow->getWindowHeight() / 2);
+ gCamera->projectPosAgentToScreen(grab_pos_agent, grab_center_gl);
+
+ const S32 ROTATE_H_MARGIN = gViewerWindow->getWindowWidth() / 20;
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ // ...build mode moves camera about focus point
+ if (grab_center_gl.mX < ROTATE_H_MARGIN)
+ {
+ if (gAgent.getFocusOnAvatar())
+ {
+ gAgent.yaw(rotate_angle);
+ }
+ else
+ {
+ gAgent.cameraOrbitAround(rotate_angle);
+ }
+ }
+ else if (grab_center_gl.mX > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ if (gAgent.getFocusOnAvatar())
+ {
+ gAgent.yaw(-rotate_angle);
+ }
+ else
+ {
+ gAgent.cameraOrbitAround(-rotate_angle);
+ }
+ }
+
+ // Don't move above top of screen or below bottom
+ if ((grab_center_gl.mY < gViewerWindow->getWindowHeight() - 6)
+ && (grab_center_gl.mY > 24))
+ {
+ // Transmit update to simulator
+ LLVector3 grab_pos_region = mGrabObject->getRegion()->getPosRegionFromGlobal( grab_point_global );
+
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectGrabUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, mGrabObject->getID() );
+ msg->addVector3Fast(_PREHASH_GrabOffsetInitial, mGrabOffsetFromCenterInitial );
+ msg->addVector3Fast(_PREHASH_GrabPosition, grab_pos_region );
+ msg->addU32Fast(_PREHASH_TimeSinceLast, dt_milliseconds );
+ msg->sendMessage( mGrabObject->getRegion()->getHost() );
+ }
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ gSelectMgr->updateSelectionCenter();
+
+ }
+
+ // once we've initiated a drag, lock the camera down
+ if (mHasMoved)
+ {
+ if (!gAgent.cameraMouselook() &&
+ !mGrabObject->isHUDAttachment() &&
+ mGrabObject->getRoot() == gAgent.getAvatarObject()->getRoot())
+ {
+ // force focus to point in space where we were looking previously
+ gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null);
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ }
+ else
+ {
+ gAgent.clearFocusObject();
+ }
+ }
+
+ // HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover. This is actually a no-op since the cursor is hidden.
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (active) [cursor hidden]" << llendl;
+}
+
+
+// Not dragging. Just showing affordances
+void LLToolGrab::handleHoverInactive(S32 x, S32 y, MASK mask)
+{
+ const F32 ROTATE_ANGLE_PER_SECOND = 40.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+
+ // Look for cursor against the edge of the screen
+ // Only works in fullscreen
+ if (gSavedSettings.getBOOL("FullScreen"))
+ {
+ if (gAgent.cameraThirdPerson() )
+ {
+ if (x == 0)
+ {
+ gAgent.yaw(rotate_angle);
+ //gAgent.setControlFlags(AGENT_CONTROL_YAW_POS);
+ }
+ else if (x == (gViewerWindow->getWindowWidth() - 1) )
+ {
+ gAgent.yaw(-rotate_angle);
+ //gAgent.setControlFlags(AGENT_CONTROL_YAW_NEG);
+ }
+ }
+ }
+
+ // JC - TODO - change cursor based on gGrabBtnVertical, gGrabBtnSpin
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (inactive-not over editable object)" << llendl;
+ gViewerWindow->setCursor(UI_CURSOR_TOOLGRAB);
+}
+
+// User is trying to do something that's not allowed.
+void LLToolGrab::handleHoverFailed(S32 x, S32 y, MASK mask)
+{
+ if( GRAB_NOOBJECT == mMode )
+ {
+ gViewerWindow->setCursor(UI_CURSOR_NO);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (not on object)" << llendl;
+ }
+ else
+ {
+ S32 dist_sq = (x-mMouseDownX) * (x-mMouseDownX) + (y-mMouseDownY) * (y-mMouseDownY);
+ if( mOutsideSlop || dist_sq > SLOP_DIST_SQ )
+ {
+ mOutsideSlop = TRUE;
+
+ switch( mMode )
+ {
+ case GRAB_LOCKED:
+ gViewerWindow->setCursor(UI_CURSOR_GRABLOCKED);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, no move permission)" << llendl;
+ break;
+
+// Non physical now handled by handleHoverActive - CRO
+// case GRAB_NONPHYSICAL:
+// gViewerWindow->setCursor(UI_CURSOR_ARROW);
+// lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed, nonphysical)" << llendl;
+// break;
+ default:
+ llassert(0);
+ }
+ }
+ else
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGrab (grab failed but within slop)" << llendl;
+ }
+ }
+}
+
+
+
+
+BOOL LLToolGrab::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ // call the base class to propogate info to sim
+ LLTool::handleMouseUp(x, y, mask);
+
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+ mMode = GRAB_INACTIVE;
+
+ // HACK: Make some grabs temporary
+ if (gGrabTransientTool)
+ {
+ gBasicToolset->selectTool( gGrabTransientTool );
+ gGrabTransientTool = NULL;
+ }
+
+ //gAgent.setObjectTracking(gSavedSettings.getBOOL("TrackFocusObject"));
+
+ return TRUE;
+}
+
+void LLToolGrab::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+ }
+}
+
+void LLToolGrab::onMouseCaptureLost()
+{
+ // First, fix cursor placement
+ if( !gAgent.cameraMouselook()
+ && (GRAB_ACTIVE_CENTER == mMode || GRAB_NONPHYSICAL == mMode))
+ {
+ llassert( mGrabObject );
+
+ if (mGrabObject->isHUDAttachment())
+ {
+ // ...move cursor "naturally", as if it had moved when hidden
+ S32 x = mMouseDownX + mAccumDeltaX;
+ S32 y = mMouseDownY + mAccumDeltaY;
+ LLUI::setCursorPositionScreen(x, y);
+ }
+ else if (mHasMoved)
+ {
+ // ...move cursor back to the center of the object
+ LLVector3 grab_point_agent = mGrabObject->getRenderPosition();
+
+ LLCoordGL gl_point;
+ gCamera->projectPosAgentToScreen(grab_point_agent, gl_point);
+
+ LLUI::setCursorPositionScreen(gl_point.mX, gl_point.mY);
+ }
+ else
+ {
+ // ...move cursor back to click position
+ LLUI::setCursorPositionScreen(mMouseDownX, mMouseDownY);
+ }
+
+ gViewerWindow->showCursor();
+ }
+
+ stopGrab();
+ stopSpin();
+ mMode = GRAB_INACTIVE;
+
+ mHideBuildHighlight = FALSE;
+
+ mGrabObject = NULL;
+
+ gSelectMgr->updateSelectionCenter();
+ gAgent.setPointAt(POINTAT_TARGET_CLEAR);
+ gAgent.setLookAt(LOOKAT_TARGET_CLEAR);
+
+ dialog_refresh_all();
+}
+
+
+void LLToolGrab::stopGrab()
+{
+ // Next, send messages to simulator
+ LLMessageSystem *msg = gMessageSystem;
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ msg->newMessageFast(_PREHASH_ObjectDeGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, mGrabObject->mLocalID);
+ msg->sendMessage(mGrabObject->getRegion()->getHost());
+
+ mVerticalDragging = FALSE;
+ mGrabOffset.clearVec();
+ break;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ // do nothing
+ break;
+ }
+
+ mHideBuildHighlight = FALSE;
+}
+
+
+void LLToolGrab::draw()
+{ }
+
+void LLToolGrab::render()
+{ }
+
+BOOL LLToolGrab::isEditing()
+{
+ // Can't just compare to null directly due to "smart" pointer.
+ LLViewerObject *obj = mGrabObject;
+ return (obj != NULL);
+}
+
+LLViewerObject* LLToolGrab::getEditingObject()
+{
+ return mGrabObject;
+}
+
+
+LLVector3d LLToolGrab::getEditingPointGlobal()
+{
+ return getGrabPointGlobal();
+}
+
+LLVector3d LLToolGrab::getGrabPointGlobal()
+{
+ switch(mMode)
+ {
+ case GRAB_ACTIVE_CENTER:
+ case GRAB_NONPHYSICAL:
+ case GRAB_LOCKED:
+ return gAgent.getCameraPositionGlobal() + mGrabHiddenOffsetFromCamera;
+
+ case GRAB_NOOBJECT:
+ case GRAB_INACTIVE:
+ default:
+ return gAgent.getPositionGlobal();
+ }
+}
diff --git a/indra/newview/lltoolgrab.h b/indra/newview/lltoolgrab.h
new file mode 100644
index 0000000000..5073d0e211
--- /dev/null
+++ b/indra/newview/lltoolgrab.h
@@ -0,0 +1,112 @@
+/**
+ * @file lltoolgrab.h
+ * @brief LLToolGrab class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLGRAB_H
+#define LL_TOOLGRAB_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "llquaternion.h"
+#include "llmemory.h"
+#include "lluuid.h"
+
+class LLView;
+class LLTextBox;
+class LLViewerObject;
+
+class LLToolGrab : public LLTool
+{
+public:
+ LLToolGrab( LLToolComposite* composite = NULL );
+ ~LLToolGrab();
+
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ void render(); // 3D elements
+ /*virtual*/ void draw(); // 2D elements
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual LLViewerObject* getEditingObject();
+ virtual LLVector3d getEditingPointGlobal();
+ virtual BOOL isEditing();
+ virtual void stopEditing();
+
+ virtual void onMouseCaptureLost();
+
+ BOOL hasGrabOffset() { return TRUE; } // HACK
+ LLVector3 getGrabOffset(S32 x, S32 y); // HACK
+
+ // Capture the mouse and start grabbing.
+ BOOL handleObjectHit(LLViewerObject *objectp, S32 x, S32 y, MASK mask);
+
+ // Certain grabs should not highlight the "Build" toolbar button
+ BOOL getHideBuildHighlight() { return mHideBuildHighlight; }
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+private:
+ LLVector3d getGrabPointGlobal();
+ void startGrab(S32 x, S32 y);
+ void stopGrab();
+
+ void startSpin();
+ void stopSpin();
+
+ void handleHoverSpin(S32 x, S32 y, MASK mask);
+ void handleHoverActive(S32 x, S32 y, MASK mask);
+ void handleHoverInactive(S32 x, S32 y, MASK mask);
+ void handleHoverFailed(S32 x, S32 y, MASK mask);
+
+private:
+ enum EGrabMode { GRAB_INACTIVE, GRAB_ACTIVE_CENTER, GRAB_NONPHYSICAL, GRAB_LOCKED, GRAB_NOOBJECT };
+
+ EGrabMode mMode;
+
+ BOOL mVerticalDragging;
+
+ BOOL mHitLand;
+ LLUUID mHitObjectID; // if hit something, its ID
+
+ LLPointer<LLViewerObject> mGrabObject; // the object currently being grabbed
+ LLTimer mGrabTimer; // send simulator time between hover movements
+
+ LLVector3 mGrabOffsetFromCenterInitial; // meters from CG of object
+ LLVector3 mGrabOffset; // how far cursor currently is from grab start point, meters
+ LLVector3d mGrabHiddenOffsetFromCamera; // in cursor hidden drag, how far is grab offset from camera
+
+ LLVector3d mDragStartPointGlobal; // projected into world
+ LLVector3d mDragStartFromCamera; // drag start relative to camera
+
+ S32 mLastMouseX;
+ S32 mLastMouseY;
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+ MASK mMouseMask;
+ S32 mAccumDeltaX; // since cursor hidden, how far have you moved?
+ S32 mAccumDeltaY;
+ BOOL mHasMoved; // has mouse moved off center at all?
+ BOOL mOutsideSlop; // has mouse moved outside center 5 pixels?
+ BOOL mDeselectedThisClick;
+
+ BOOL mSpinGrabbing;
+ LLQuaternion mSpinRotation;
+
+ BOOL mHideBuildHighlight;
+};
+
+extern LLToolGrab *gToolGrab;
+
+extern BOOL gGrabBtnVertical;
+extern BOOL gGrabBtnSpin;
+extern LLTool* gGrabTransientTool;
+
+#endif // LL_TOOLGRAB_H
+
diff --git a/indra/newview/lltoolgun.cpp b/indra/newview/lltoolgun.cpp
new file mode 100644
index 0000000000..4c01b86153
--- /dev/null
+++ b/indra/newview/lltoolgun.cpp
@@ -0,0 +1,122 @@
+/**
+ * @file lltoolgun.cpp
+ * @brief LLToolGun class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolgun.h"
+
+#include "llviewerwindow.h"
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llsky.h"
+#include "viewer.h"
+#include "llresmgr.h"
+#include "llfontgl.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewercamera.h"
+#include "llhudmanager.h"
+#include "lltoolmgr.h"
+#include "lltoolgrab.h"
+
+LLToolGun::LLToolGun( LLToolComposite* composite )
+ :
+ LLTool( "gun", composite )
+{
+ mCrosshairImg = gImageList.getImage( LLUUID( gSavedSettings.getString("UIImgCrosshairsUUID") ), MIPMAP_FALSE, TRUE );
+}
+
+void LLToolGun::handleSelect()
+{
+ gViewerWindow->hideCursor();
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->mWindow->setMouseClipping(TRUE);
+}
+
+void LLToolGun::handleDeselect()
+{
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->showCursor();
+ gViewerWindow->mWindow->setMouseClipping(FALSE);
+}
+
+BOOL LLToolGun::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( gToolGrab );
+
+ return gToolGrab->handleMouseDown(x, y, mask);
+}
+
+BOOL LLToolGun::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ #if 1 //LL_WINDOWS || LL_DARWIN
+ const F32 NOMINAL_MOUSE_SENSITIVITY = 0.0025f;
+ #else
+ const F32 NOMINAL_MOUSE_SENSITIVITY = 0.025f;
+ #endif
+
+
+ F32 mouse_sensitivity = clamp_rescale(gMouseSensitivity, 0.f, 15.f, 0.5f, 2.75f) * NOMINAL_MOUSE_SENSITIVITY;
+
+ // ...move the view with the mouse
+
+ // get mouse movement delta
+ S32 dx = -gViewerWindow->getCurrentMouseDX();
+ S32 dy = -gViewerWindow->getCurrentMouseDY();
+
+ if (dx != 0 || dy != 0)
+ {
+ // ...actually moved off center
+ if (gInvertMouse)
+ {
+ gAgent.pitch(mouse_sensitivity * -dy);
+ }
+ else
+ {
+ gAgent.pitch(mouse_sensitivity * dy);
+ }
+ LLVector3 skyward = gAgent.getReferenceUpVector();
+ gAgent.rotate(mouse_sensitivity * dx, skyward.mV[VX], skyward.mV[VY], skyward.mV[VZ]);
+
+ if (gSavedSettings.getBOOL("MouseSun"))
+ {
+ gSky.setSunDirection(gCamera->getAtAxis(), LLVector3(0.f, 0.f, 0.f));
+ gSky.setOverrideSun(TRUE);
+ gSavedSettings.setVector3("SkySunDefaultPosition", gCamera->getAtAxis());
+ }
+
+ gViewerWindow->moveCursorToCenter();
+ gViewerWindow->hideCursor();
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGun (mouselook)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolGun (not mouselook)" << llendl;
+ }
+
+ // HACK to avoid assert: error checking system makes sure that the cursor is set during every handleHover. This is actually a no-op since the cursor is hidden.
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+
+ return TRUE;
+}
+
+void LLToolGun::draw()
+{
+ if( gSavedSettings.getBOOL("ShowCrosshairs") )
+ {
+ gl_draw_image(
+ ( gViewerWindow->getWindowWidth() - mCrosshairImg->getWidth() ) / 2,
+ ( gViewerWindow->getWindowHeight() - mCrosshairImg->getHeight() ) / 2,
+ mCrosshairImg );
+ }
+}
diff --git a/indra/newview/lltoolgun.h b/indra/newview/lltoolgun.h
new file mode 100644
index 0000000000..d567db30ca
--- /dev/null
+++ b/indra/newview/lltoolgun.h
@@ -0,0 +1,35 @@
+/**
+ * @file lltoolgun.h
+ * @brief LLToolGun class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLGUN_H
+#define LL_TOOLGUN_H
+
+#include "lltool.h"
+#include "llviewerimage.h"
+
+
+class LLToolGun : public LLTool
+{
+public:
+ LLToolGun( LLToolComposite* composite=NULL );
+
+ virtual void draw();
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual BOOL clipMouseWhenDown() { return FALSE; }
+
+private:
+ LLPointer<LLViewerImage> mCrosshairImg;
+};
+
+#endif
diff --git a/indra/newview/lltoolindividual.cpp b/indra/newview/lltoolindividual.cpp
new file mode 100644
index 0000000000..a5afd379ed
--- /dev/null
+++ b/indra/newview/lltoolindividual.cpp
@@ -0,0 +1,102 @@
+/**
+ * @file lltoolindividual.cpp
+ * @brief LLToolIndividual class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//*****************************************************************************
+//
+// This is a tool for selecting individual object from the
+// toolbox. Handy for when you want to deal with child object
+// inventory...
+//
+//*****************************************************************************
+
+#include "llviewerprecompiledheaders.h"
+#include "lltoolindividual.h"
+
+#include "llselectmgr.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llfloatertools.h"
+
+///----------------------------------------------------------------------------
+/// Globals
+///----------------------------------------------------------------------------
+
+LLToolIndividual* gToolIndividual = NULL;
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+
+///----------------------------------------------------------------------------
+/// Class LLToolIndividual
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLToolIndividual::LLToolIndividual()
+ : LLTool("Individual")
+{
+}
+
+// Destroys the object
+LLToolIndividual::~LLToolIndividual()
+{
+}
+
+BOOL LLToolIndividual::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+void LLToolIndividual::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* obj = gViewerWindow->lastObjectHit();
+ gSelectMgr->deselectAll();
+ if(obj)
+ {
+ gSelectMgr->selectObjectOnly(obj);
+ }
+}
+
+BOOL LLToolIndividual::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if(!gSelectMgr->isEmpty())
+ {
+ // You should already have an object selected from the mousedown.
+ // If so, show its inventory.
+ //gBuildView->showInventoryPanel();
+ //gBuildView->showPanel(LLBuildView::PANEL_CONTENTS);
+ gFloaterTools->showPanel(LLFloaterTools::PANEL_CONTENTS);
+ return TRUE;
+ }
+ else
+ {
+ // Nothing selected means the first mouse click was probably
+ // bad, so try again.
+ return FALSE;
+ }
+}
+
+void LLToolIndividual::handleSelect()
+{
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(!obj)
+ {
+ obj = gSelectMgr->getFirstObject();
+ }
+ gSelectMgr->deselectAll();
+ if(obj)
+ {
+ gSelectMgr->selectObjectOnly(obj);
+ }
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/newview/lltoolindividual.h b/indra/newview/lltoolindividual.h
new file mode 100644
index 0000000000..b5fe789d50
--- /dev/null
+++ b/indra/newview/lltoolindividual.h
@@ -0,0 +1,41 @@
+/**
+ * @file lltoolindividual.h
+ * @brief LLToolIndividual class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLINDIVIDUAL_H
+#define LL_LLTOOLINDIVIDUAL_H
+
+#include "lltool.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class lltoolindividual
+//
+// A tool to select individual objects rather than linked sets.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLToolIndividual : public LLTool
+{
+public:
+ LLToolIndividual();
+ virtual ~LLToolIndividual();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ virtual void handleSelect();
+ //virtual void handleDeselect();
+ //virtual void render();
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+
+};
+
+extern LLToolIndividual* gToolIndividual;
+
+
+#endif // LL_LLTOOLINDIVIDUAL_H
diff --git a/indra/newview/lltoolmgr.cpp b/indra/newview/lltoolmgr.cpp
new file mode 100644
index 0000000000..99271e18c5
--- /dev/null
+++ b/indra/newview/lltoolmgr.cpp
@@ -0,0 +1,532 @@
+/**
+ * @file lltoolmgr.cpp
+ * @brief LLToolMgr class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolmgr.h"
+
+#include "lltool.h"
+// tools and manipulators
+#include "llmanipscale.h"
+#include "lltoolbrush.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolindividual.h"
+#include "lltoolmorph.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "lltoolobjpicker.h"
+#include "lltoolpipette.h"
+
+// Globals (created and destroyed by LLAgent)
+LLToolMgr* gToolMgr = NULL;
+
+// Used when app not active to avoid processing hover.
+LLTool* gToolNull = NULL;
+
+LLToolset* gCurrentToolset = NULL;
+LLToolset* gBasicToolset = NULL;
+LLToolset* gCameraToolset = NULL;
+//LLToolset* gLandToolset = NULL;
+LLToolset* gMouselookToolset = NULL;
+LLToolset* gFaceEditToolset = NULL;
+
+/////////////////////////////////////////////////////
+// LLToolMgr
+
+LLToolMgr::LLToolMgr()
+ :
+ mCurrentTool(NULL),
+ mSavedTool(NULL),
+ mTransientTool( NULL ),
+ mOverrideTool( NULL )
+{
+ gToolNull = new LLTool(NULL); // Does nothing
+ setCurrentTool(gToolNull);
+
+ gBasicToolset = new LLToolset();
+ gCameraToolset = new LLToolset();
+// gLandToolset = new LLToolset();
+ gMouselookToolset = new LLToolset();
+ gFaceEditToolset = new LLToolset();
+
+ gCurrentToolset = gBasicToolset;
+}
+
+void LLToolMgr::initTools()
+{
+ // Initialize all the tools
+ // Variables that are reused for each tool
+ LLTool* tool = NULL;
+
+ //
+ // Pie tool (not visible in UI, implicit)
+ //
+ gToolPie = new LLToolPie();
+
+ gBasicToolset->addTool( gToolPie );
+// gCameraToolset->addTool( gToolPie );
+// gLandToolset->addTool( gToolPie );
+
+ // Camera tool
+ gToolCamera = new LLToolCamera();
+ gBasicToolset ->addTool( gToolCamera );
+ gCameraToolset->addTool( gToolCamera );
+
+ //
+ // Grab tool
+ //
+ gToolGrab = new LLToolGrab();
+ tool = gToolGrab;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Translation tool
+ //
+ gToolTranslate = new LLToolCompTranslate();
+ tool = gToolTranslate;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Scale ("Stretch") tool
+ //
+ gToolStretch = new LLToolCompScale();
+ tool = gToolStretch;
+
+
+ //
+ // Rotation tool
+ //
+ gToolRotate = new LLToolCompRotate();
+ tool = gToolRotate;
+
+
+ //
+ // Face tool
+ //
+ gToolFace = new LLToolFace();
+ tool = gToolFace;
+
+ //
+ // Pipette tool
+ //
+ gToolPipette = new LLToolPipette();
+
+ //
+ // Individual object selector
+ //
+ gToolIndividual = new LLToolIndividual();
+
+
+ //
+ // Create object tool
+ //
+ gToolCreate = new LLToolCompCreate();
+ tool = gToolCreate;
+
+ gBasicToolset->addTool( tool );
+
+ //
+ // Land brush tool
+ //
+ gToolLand = new LLToolBrushLand();
+ tool = gToolLand;
+
+ gBasicToolset->addTool( tool );
+
+
+ //
+ // Land select tool
+ //
+ gToolParcel = new LLToolSelectLand();
+ tool = gToolParcel;
+
+ //
+ // Gun tool
+ //
+ gToolGun = new LLToolCompGun();
+ gMouselookToolset->addTool( gToolGun );
+
+ //
+ // Face edit tool
+ //
+// gToolMorph = new LLToolMorph();
+// gFaceEditToolset->addTool( gToolMorph );
+ gFaceEditToolset->addTool( gToolCamera );
+
+ // Drag and drop tool
+ gToolDragAndDrop = new LLToolDragAndDrop();
+
+ gToolObjPicker = new LLToolObjPicker();
+
+ // On startup, use "select" tool
+ gBasicToolset->selectTool( gToolPie );
+ useSelectedTool( gBasicToolset );
+}
+
+LLToolMgr::~LLToolMgr()
+{
+ delete gToolPie;
+ gToolPie = NULL;
+
+ delete gToolGun;
+ gToolGun = NULL;
+
+ delete gToolCamera;
+ gToolCamera = NULL;
+
+// delete gToolMorph;
+// gToolMorph = NULL;
+
+ delete gToolDragAndDrop;
+ gToolDragAndDrop = NULL;
+
+ delete gBasicToolset;
+ gBasicToolset = NULL;
+
+ delete gToolGrab;
+ gToolGrab = NULL;
+
+ delete gToolRotate;
+ gToolRotate = NULL;
+
+ delete gToolTranslate;
+ gToolTranslate = NULL;
+
+ delete gToolStretch;
+ gToolStretch = NULL;
+
+ delete gToolIndividual;
+ gToolIndividual = NULL;
+
+ delete gToolPipette;
+ gToolPipette = NULL;
+
+ delete gToolCreate;
+ gToolCreate = NULL;
+
+ delete gToolFace;
+ gToolFace = NULL;
+
+ delete gToolLand;
+ gToolLand = NULL;
+
+ delete gToolParcel;
+ gToolParcel = NULL;
+
+ delete gToolObjPicker;
+ gToolObjPicker = NULL;
+
+ delete gMouselookToolset;
+ gMouselookToolset = NULL;
+
+ delete gFaceEditToolset;
+ gFaceEditToolset = NULL;
+
+ delete gCameraToolset;
+ gCameraToolset = NULL;
+
+ delete gToolNull;
+ gToolNull = NULL;
+}
+
+
+void LLToolMgr::useSelectedTool( LLToolset* vp )
+{
+ setCurrentTool( vp->getSelectedTool() );
+}
+
+BOOL LLToolMgr::usingTransientTool()
+{
+ return mTransientTool ? TRUE : FALSE;
+}
+
+void LLToolMgr::setCurrentTool( LLTool* tool )
+{
+ if (tool == mCurrentTool)
+ {
+ // didn't change tool, so don't mess with
+ // handleSelect or handleDeselect
+ return;
+ }
+
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ }
+ else if( mCurrentTool )
+ {
+ mCurrentTool->handleDeselect();
+ }
+
+ mCurrentTool = tool;
+ if (mCurrentTool)
+ {
+ mCurrentTool->handleSelect();
+ }
+}
+
+LLTool* LLToolMgr::getCurrentTool(MASK override_mask)
+{
+ // In mid-drag, always keep the current tool
+ if (gToolTranslate->hasMouseCapture()
+ || gToolRotate->hasMouseCapture()
+ || gToolStretch->hasMouseCapture())
+ {
+ // might have gotten here by overriding another tool
+ if (mOverrideTool)
+ {
+ return mOverrideTool;
+ }
+ else
+ {
+ return mCurrentTool;
+ }
+ }
+
+ if (mTransientTool)
+ {
+ mOverrideTool = NULL;
+ return mTransientTool;
+ }
+
+ if (mCurrentTool == gToolGun)
+ {
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+
+ // ALT always gets you the camera tool
+ if (override_mask & MASK_ALT)
+ {
+ mOverrideTool = gToolCamera;
+ return mOverrideTool;
+ }
+
+ if (mCurrentTool == gToolCamera)
+ {
+ // ...can't switch out of camera
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ else if (mCurrentTool == gToolGrab)
+ {
+ // ...can't switch out of grab
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ else
+ {
+ // ...can switch between editing tools
+ if (override_mask == MASK_CONTROL)
+ {
+ // Control lifts when in the pie tool, otherwise switches to rotate
+ if (mCurrentTool == gToolPie)
+ {
+ mOverrideTool = gToolGrab;
+ }
+ else
+ {
+ mOverrideTool = gToolRotate;
+ }
+ return mOverrideTool;
+ }
+ else if (override_mask == (MASK_CONTROL | MASK_SHIFT))
+ {
+ // Shift-Control spins when in the pie tool, otherwise switches to scale
+ if (mCurrentTool == gToolPie)
+ {
+ mOverrideTool = gToolGrab;
+ }
+ else
+ {
+ mOverrideTool = gToolStretch;
+ }
+ return mOverrideTool;
+ }
+ else
+ {
+ mOverrideTool = NULL;
+ return mCurrentTool;
+ }
+ }
+}
+
+BOOL LLToolMgr::inEdit()
+{
+ return mCurrentTool != gToolPie && mCurrentTool != gToolNull;
+}
+
+void LLToolMgr::setTransientTool(LLTool* tool)
+{
+ if (!tool)
+ {
+ clearTransientTool();
+ }
+ else
+ {
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ }
+ else if (mCurrentTool)
+ {
+ mCurrentTool->handleDeselect();
+ }
+
+ mTransientTool = tool;
+ mTransientTool->handleSelect();
+ }
+}
+
+void LLToolMgr::clearTransientTool()
+{
+ if (mTransientTool)
+ {
+ mTransientTool->handleDeselect();
+ mTransientTool = NULL;
+ if (mCurrentTool)
+ {
+ mCurrentTool->handleSelect();
+ }
+ else
+ {
+ llwarns << "mCurrentTool is NULL" << llendl;
+ }
+ }
+}
+
+
+// The "gun tool", used for handling mouselook, captures the mouse and
+// locks it within the window. When the app loses focus we need to
+// release this locking.
+void LLToolMgr::onAppFocusLost()
+{
+ if (mCurrentTool
+ && mCurrentTool == gToolGun)
+ {
+ mCurrentTool->handleDeselect();
+ }
+ mSavedTool = mCurrentTool;
+ mCurrentTool = gToolNull;
+}
+
+void LLToolMgr::onAppFocusGained()
+{
+ if (mSavedTool)
+ {
+ if (mSavedTool == gToolGun)
+ {
+ mCurrentTool->handleSelect();
+ }
+ mCurrentTool = mSavedTool;
+ mSavedTool = NULL;
+ }
+}
+
+/////////////////////////////////////////////////////
+// LLToolset
+
+void LLToolset::addTool(LLTool* tool)
+{
+ llassert( !mToolList.checkData( tool ) ); // check for duplicates
+
+ mToolList.addDataAtEnd( tool );
+ if( !mSelectedTool )
+ {
+ mSelectedTool = tool;
+ }
+}
+
+
+void LLToolset::selectTool(LLTool* tool)
+{
+ mSelectedTool = tool;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+
+void LLToolset::selectToolByIndex( S32 index )
+{
+ LLTool *tool = mToolList.getNthData( index );
+ if (tool)
+ {
+ mSelectedTool = tool;
+ gToolMgr->setCurrentTool( tool );
+ }
+}
+
+BOOL LLToolset::isToolSelected( S32 index )
+{
+ return (mToolList.getNthData( index ) == mSelectedTool);
+}
+
+
+void LLToolset::selectFirstTool()
+{
+ mSelectedTool = mToolList.getFirstData();
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+
+void LLToolset::selectNextTool()
+{
+ LLTool* next = NULL;
+ for( LLTool* cur = mToolList.getFirstData(); cur; cur = mToolList.getNextData() )
+ {
+ if( cur == mSelectedTool )
+ {
+ next = mToolList.getNextData();
+ break;
+ }
+ }
+
+ if( !next )
+ {
+ next = mToolList.getFirstData();
+ }
+
+ mSelectedTool = next;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+void LLToolset::selectPrevTool()
+{
+ LLTool* prev = NULL;
+ for( LLTool* cur = mToolList.getLastData(); cur; cur = mToolList.getPreviousData() )
+ {
+ if( cur == mSelectedTool )
+ {
+ prev = mToolList.getPreviousData();
+ break;
+ }
+ }
+
+ if( !prev )
+ {
+ prev = mToolList.getLastData();
+ }
+
+ mSelectedTool = prev;
+ gToolMgr->setCurrentTool( mSelectedTool );
+}
+
+void select_tool( void *tool_pointer )
+{
+ LLTool *tool = (LLTool *)tool_pointer;
+ gCurrentToolset->selectTool( tool );
+}
diff --git a/indra/newview/lltoolmgr.h b/indra/newview/lltoolmgr.h
new file mode 100644
index 0000000000..b54a893b0e
--- /dev/null
+++ b/indra/newview/lltoolmgr.h
@@ -0,0 +1,99 @@
+/**
+ * @file lltoolmgr.h
+ * @brief LLToolMgr class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLMGR_H
+#define LL_TOOLMGR_H
+
+#include "doublelinkedlist.h"
+#include "llkeyboard.h"
+
+class LLTool;
+class LLToolset;
+
+// Key bindings for common operations
+const MASK MASK_VERTICAL = MASK_CONTROL;
+const MASK MASK_SPIN = MASK_CONTROL | MASK_SHIFT;
+const MASK MASK_ZOOM = MASK_NONE;
+const MASK MASK_ORBIT = MASK_CONTROL;
+const MASK MASK_PAN = MASK_CONTROL | MASK_SHIFT;
+const MASK MASK_COPY = MASK_SHIFT;
+
+class LLToolMgr
+{
+public:
+ LLToolMgr();
+ ~LLToolMgr();
+
+ // Must be called after gSavedSettings set up.
+ void initTools();
+
+ LLTool* getCurrentTool(MASK override_mask);
+
+ BOOL inEdit();
+ void useSelectedTool( LLToolset* vp );
+
+ void setTransientTool(LLTool* tool);
+ void clearTransientTool();
+ BOOL usingTransientTool();
+
+ void onAppFocusGained();
+ void onAppFocusLost();
+
+protected:
+ friend class LLToolset; // to allow access to setCurrentTool();
+ void setCurrentTool(LLTool* tool);
+
+protected:
+ LLTool* mCurrentTool;
+ LLTool* mSavedTool; // The current tool at the time application focus was lost.
+ LLTool* mTransientTool;
+ LLTool* mOverrideTool; // Tool triggered by keyboard override
+};
+
+// Sets of tools for various modes
+class LLToolset
+{
+public:
+ LLToolset() : mSelectedTool(NULL) {}
+
+ LLTool* getSelectedTool() { return mSelectedTool; }
+
+ void addTool(LLTool* tool);
+
+ void selectTool( LLTool* tool );
+ void selectToolByIndex( S32 index );
+ void selectFirstTool();
+ void selectNextTool();
+ void selectPrevTool();
+
+ void handleScrollWheel(S32 clicks);
+
+ BOOL isToolSelected( S32 index );
+
+protected:
+ LLTool* mSelectedTool;
+ LLDoubleLinkedList<LLTool> mToolList;
+};
+
+// Handy callbacks for switching tools
+void select_tool(void *tool);
+
+
+// Globals (created and destroyed by LLViewerWindow)
+extern LLToolMgr* gToolMgr;
+
+extern LLToolset* gCurrentToolset;
+extern LLToolset* gBasicToolset;
+extern LLToolset *gCameraToolset;
+//extern LLToolset *gLandToolset;
+extern LLToolset* gMouselookToolset;
+extern LLToolset* gFaceEditToolset;
+
+extern LLTool* gToolNull;
+
+#endif
diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp
new file mode 100644
index 0000000000..87e6496588
--- /dev/null
+++ b/indra/newview/lltoolmorph.cpp
@@ -0,0 +1,279 @@
+/**
+ * @file lltoolmorph.cpp
+ * @brief A tool to manipulate faces..
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolmorph.h"
+
+// Library includes
+#include "audioengine.h"
+#include "llviewercontrol.h"
+#include "llfontgl.h"
+#include "sound_ids.h"
+#include "v3math.h"
+#include "v3color.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "llface.h"
+#include "llfloatercustomize.h"
+#include "llmorphview.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "lltexlayer.h"
+#include "lltoolmgr.h"
+#include "lltoolview.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+//LLToolMorph *gToolMorph = NULL;
+
+//static
+LLLinkedList<LLVisualParamHint> LLVisualParamHint::sInstances;
+BOOL LLVisualParamReset::sDirty = FALSE;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamHint()
+//-----------------------------------------------------------------------------
+
+// static
+LLVisualParamHint::LLVisualParamHint(
+ S32 pos_x, S32 pos_y,
+ S32 width, S32 height,
+ LLViewerJointMesh *mesh,
+ LLViewerVisualParam *param,
+ F32 param_weight)
+ :
+ LLDynamicTexture(width, height, 3, LLDynamicTexture::ORDER_MIDDLE, TRUE ),
+ mNeedsUpdate( TRUE ),
+ mIsVisible( FALSE ),
+ mJointMesh( mesh ),
+ mVisualParam( param ),
+ mVisualParamWeight( param_weight ),
+ mAllowsUpdates( TRUE ),
+ mDelayFrames( 0 ),
+ mRect( pos_x, pos_y + height, pos_x + width, pos_y ),
+ mLastParamWeight(0.f)
+{
+ LLVisualParamHint::sInstances.addData( this );
+ LLUUID id;
+ id.set( gViewerArt.getString("avatar_thumb_bkgrnd.tga") );
+ mBackgroundp = gImageList.getImage(id, FALSE, TRUE);
+
+
+ llassert(width != 0);
+ llassert(height != 0);
+}
+
+//-----------------------------------------------------------------------------
+// ~LLVisualParamHint()
+//-----------------------------------------------------------------------------
+LLVisualParamHint::~LLVisualParamHint()
+{
+ LLVisualParamHint::sInstances.removeData( this );
+}
+
+//-----------------------------------------------------------------------------
+// static
+// requestHintUpdates()
+// Requests updates for all instances (excluding two possible exceptions) Grungy but efficient.
+//-----------------------------------------------------------------------------
+void LLVisualParamHint::requestHintUpdates( LLVisualParamHint* exception1, LLVisualParamHint* exception2 )
+{
+ S32 delay_frames = 0;
+ for(LLVisualParamHint* instance = sInstances.getFirstData();
+ instance;
+ instance = sInstances.getNextData())
+ {
+ if( (instance != exception1) && (instance != exception2) )
+ {
+ if( instance->mAllowsUpdates )
+ {
+ instance->mNeedsUpdate = TRUE;
+ instance->mDelayFrames = delay_frames;
+ delay_frames++;
+ }
+ else
+ {
+ instance->mNeedsUpdate = TRUE;
+ instance->mDelayFrames = 0;
+ }
+ }
+ }
+}
+
+BOOL LLVisualParamHint::needsRender()
+{
+ return mNeedsUpdate && mDelayFrames-- <= 0 && !gAgent.getAvatarObject()->mAppearanceAnimating && mAllowsUpdates;
+}
+
+void LLVisualParamHint::preRender(BOOL clear_depth)
+{
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ mLastParamWeight = avatarp->getVisualParamWeight(mVisualParam);
+ avatarp->setVisualParamWeight(mVisualParam, mVisualParamWeight);
+ avatarp->setVisualParamWeight("Blink_Left", 0.f);
+ avatarp->setVisualParamWeight("Blink_Right", 0.f);
+ avatarp->updateComposites();
+ avatarp->updateVisualParams();
+ avatarp->updateGeometry(avatarp->mDrawable);
+ LLDynamicTexture::preRender(clear_depth);
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamHint::render()
+{
+ LLVisualParamReset::sDirty = TRUE;
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glOrtho(0.0f, mWidth, 0.0f, mHeight, -1.0f, 1.0f);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSUIDefault gls_ui;
+ //LLGLState::verify(TRUE);
+ LLViewerImage::bindTexture(mBackgroundp);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ gl_rect_2d_simple_tex( mWidth, mHeight );
+ mBackgroundp->unbindTexture(0, GL_TEXTURE_2D);
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ mNeedsUpdate = FALSE;
+ mIsVisible = TRUE;
+
+ LLViewerJointMesh* cam_target_joint = NULL;
+ const std::string& cam_target_mesh_name = mVisualParam->getCameraTargetName();
+ if( !cam_target_mesh_name.empty() )
+ {
+ cam_target_joint = (LLViewerJointMesh*)avatarp->getJoint( cam_target_mesh_name );
+ }
+ if( !cam_target_joint )
+ {
+ cam_target_joint = (LLViewerJointMesh*)gMorphView->getCameraTargetJoint();
+ }
+ if( !cam_target_joint )
+ {
+ cam_target_joint = (LLViewerJointMesh*)avatarp->getJoint("mHead");
+ }
+
+ LLQuaternion avatar_rotation;
+ LLJoint* root_joint = avatarp->getRootJoint();
+ if( root_joint )
+ {
+ avatar_rotation = root_joint->getWorldRotation();
+ }
+
+ LLVector3 target_joint_pos = cam_target_joint->getWorldPosition();
+
+ LLVector3 target_offset( 0, 0, mVisualParam->getCameraElevation() );
+ LLVector3 target_pos = target_joint_pos + (target_offset * avatar_rotation);
+
+ F32 cam_angle_radians = mVisualParam->getCameraAngle() * DEG_TO_RAD;
+ LLVector3 camera_snapshot_offset(
+ mVisualParam->getCameraDistance() * cosf( cam_angle_radians ),
+ mVisualParam->getCameraDistance() * sinf( cam_angle_radians ),
+ mVisualParam->getCameraElevation() );
+ LLVector3 camera_pos = target_joint_pos + (camera_snapshot_offset * avatar_rotation);
+
+ gCamera->setAspect((F32)mWidth / (F32)mHeight);
+ gCamera->setOriginAndLookAt(
+ camera_pos, // camera
+ LLVector3(0.f, 0.f, 1.f), // up
+ target_pos ); // point of interest
+
+ gCamera->setPerspective(FALSE, mOrigin.mX, mOrigin.mY, mWidth, mHeight, FALSE);
+
+ if (avatarp->mDrawable.notNull())
+ {
+ LLDrawPoolAvatar *avatarPoolp = (LLDrawPoolAvatar *)avatarp->mDrawable->getFace(0)->getPool();
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ gPipeline.unbindAGP();
+ avatarPoolp->syncAGP();
+ gPipeline.bindAGP();
+ avatarPoolp->renderAvatars(avatarp, TRUE); // renders only one avatar (no shaders)
+ }
+ avatarp->setVisualParamWeight(mVisualParam, mLastParamWeight);
+
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// draw()
+//-----------------------------------------------------------------------------
+void LLVisualParamHint::draw()
+{
+ if (!mIsVisible) return;
+
+ bindTexture();
+
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ LLGLSUIDefault gls_ui;
+ glBegin(GL_QUADS);
+ {
+ glTexCoord2i(0, 1);
+ glVertex2i(0, mHeight);
+ glTexCoord2i(0, 0);
+ glVertex2i(0, 0);
+ glTexCoord2i(1, 0);
+ glVertex2i(mWidth, 0);
+ glTexCoord2i(1, 1);
+ glVertex2i(mWidth, mHeight);
+ }
+ glEnd();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+}
+
+//-----------------------------------------------------------------------------
+// LLVisualParamReset()
+//-----------------------------------------------------------------------------
+LLVisualParamReset::LLVisualParamReset() : LLDynamicTexture(1, 1, 1, ORDER_RESET, FALSE)
+{
+}
+
+//-----------------------------------------------------------------------------
+// render()
+//-----------------------------------------------------------------------------
+BOOL LLVisualParamReset::render()
+{
+ if (sDirty)
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ avatarp->updateComposites();
+ avatarp->updateVisualParams();
+ avatarp->updateGeometry(avatarp->mDrawable);
+ sDirty = FALSE;
+ }
+
+ return FALSE;
+}
diff --git a/indra/newview/lltoolmorph.h b/indra/newview/lltoolmorph.h
new file mode 100644
index 0000000000..0cdb0cb188
--- /dev/null
+++ b/indra/newview/lltoolmorph.h
@@ -0,0 +1,88 @@
+/**
+ * @file lltoolmorph.h
+ * @brief A tool to select object faces.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLMORPH_H
+#define LL_LLTOOLMORPH_H
+
+#include "lltool.h"
+#include "m4math.h"
+#include "v2math.h"
+#include "linked_lists.h"
+#include "lldynamictexture.h"
+#include "llundo.h"
+#include "lltextbox.h"
+#include "llstrider.h"
+#include "llviewervisualparam.h"
+#include "llframetimer.h"
+#include "llviewerimage.h"
+
+class LLViewerJointMesh;
+class LLPolyMesh;
+class LLViewerObject;
+
+//-----------------------------------------------------------------------------
+// LLVisualParamHint
+//-----------------------------------------------------------------------------
+class LLVisualParamHint
+: public LLDynamicTexture
+{
+public:
+ LLVisualParamHint(
+ S32 pos_x, S32 pos_y,
+ S32 width, S32 height,
+ LLViewerJointMesh *mesh,
+ LLViewerVisualParam *param,
+ F32 param_weight);
+ virtual ~LLVisualParamHint();
+
+ BOOL needsRender();
+ void preRender(BOOL clear_depth);
+ BOOL render();
+ void requestUpdate( S32 delay_frames ) {mNeedsUpdate = TRUE; mDelayFrames = delay_frames; }
+ void setUpdateDelayFrames( S32 delay_frames ) { mDelayFrames = delay_frames; }
+ void draw();
+
+ LLViewerVisualParam* getVisualParam() { return mVisualParam; }
+ F32 getVisualParamWeight() { return mVisualParamWeight; }
+ BOOL getVisible() { return mIsVisible; }
+
+ void setAllowsUpdates( BOOL b ) { mAllowsUpdates = b; }
+
+ const LLRect& getRect() { return mRect; }
+
+ // Requests updates for all instances (excluding two possible exceptions) Grungy but efficient.
+ static void requestHintUpdates( LLVisualParamHint* exception1 = NULL, LLVisualParamHint* exception2 = NULL );
+
+protected:
+ BOOL mNeedsUpdate; // does this texture need to be re-rendered?
+ BOOL mIsVisible; // is this distortion hint visible?
+ LLViewerJointMesh* mJointMesh; // mesh that this distortion applies to
+ LLViewerVisualParam* mVisualParam; // visual param applied by this hint
+ F32 mVisualParamWeight; // weight for this visual parameter
+ BOOL mAllowsUpdates; // updates are blocked unless this is true
+ S32 mDelayFrames; // updates are blocked for this many frames
+ LLRect mRect;
+ F32 mLastParamWeight;
+
+ LLPointer<LLViewerImage> mBackgroundp;
+
+ static LLLinkedList<LLVisualParamHint> sInstances;
+};
+
+// this class resets avatar data at the end of an update cycle
+class LLVisualParamReset : public LLDynamicTexture
+{
+public:
+ LLVisualParamReset();
+ /*virtual */ BOOL render();
+
+ static BOOL sDirty;
+};
+
+#endif
+
diff --git a/indra/newview/lltoolobjpicker.cpp b/indra/newview/lltoolobjpicker.cpp
new file mode 100644
index 0000000000..ca74a64bf8
--- /dev/null
+++ b/indra/newview/lltoolobjpicker.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file lltoolobjpicker.cpp
+ * @brief LLToolObjPicker class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// LLToolObjPicker is a transient tool, useful for a single object pick.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolobjpicker.h"
+
+#include "llagent.h"
+#include "llselectmgr.h"
+#include "llworld.h"
+#include "viewer.h" // for gFPSClamped, pie menus
+#include "llviewercontrol.h"
+#include "llmenugl.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewermenu.h"
+#include "llviewercamera.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+
+LLToolObjPicker* gToolObjPicker = NULL;
+
+LLToolObjPicker::LLToolObjPicker()
+: LLTool( "ObjPicker", NULL ),
+ mPicked( FALSE ),
+ mHitObjectID( LLUUID::null ),
+ mExitCallback( NULL ),
+ mExitCallbackData( NULL )
+{ }
+
+
+// returns TRUE if an object was selected
+BOOL LLToolObjPicker::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ LLView* viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleMouseDown(x, y, mask);
+
+ mHitObjectID.setNull();
+
+ if (! handled)
+ {
+ // didn't click in any UI object, so must have clicked in the world
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ handled = TRUE;
+ }
+ else
+ {
+ if (hasMouseCapture())
+ {
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ llwarns << "PickerTool doesn't have mouse capture on mouseDown" << llendl;
+ }
+ }
+
+ // Pass mousedown to base class
+ LLTool::handleMouseDown(x, y, mask);
+
+ return handled;
+}
+
+void LLToolObjPicker::pickCallback(S32 x, S32 y, MASK mask)
+{
+ // You must hit the body for this tool to think you hit the object.
+ LLViewerObject* objectp = NULL;
+ objectp = gObjectList.findObject( gLastHitObjectID );
+ if (objectp)
+ {
+ gToolObjPicker->mHitObjectID = objectp->mID;
+ gToolObjPicker->mPicked = TRUE;
+ }
+}
+
+
+BOOL LLToolObjPicker::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLView* viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleHover(x, y, mask);
+ if (handled)
+ {
+ // let UI handle this
+ }
+
+ LLTool::handleMouseUp(x, y, mask);
+ if (hasMouseCapture())
+ {
+ setMouseCapture(FALSE);
+ }
+ else
+ {
+ llwarns << "PickerTool doesn't have mouse capture on mouseUp" << llendl;
+ }
+ return handled;
+}
+
+
+BOOL LLToolObjPicker::handleHover(S32 x, S32 y, MASK mask)
+{
+ LLView *viewp = gViewerWindow->getRootView();
+ BOOL handled = viewp->handleHover(x, y, mask);
+ if (!handled)
+ {
+ // Used to do pick on hover. Now we just always display the cursor.
+ ECursorType cursor = UI_CURSOR_ARROWLOCKED;
+
+ cursor = UI_CURSOR_TOOLPICKOBJECT3;
+
+ gViewerWindow->getWindow()->setCursor(cursor);
+ }
+ return handled;
+}
+
+
+void LLToolObjPicker::onMouseCaptureLost()
+{
+ if (mExitCallback)
+ {
+ mExitCallback(mExitCallbackData);
+
+ mExitCallback = NULL;
+ mExitCallbackData = NULL;
+ }
+
+ mPicked = FALSE;
+ mHitObjectID.setNull();
+}
+
+// virtual
+void LLToolObjPicker::setExitCallback(void (*callback)(void *), void *callback_data)
+{
+ mExitCallback = callback;
+ mExitCallbackData = callback_data;
+}
+
+// virtual
+void LLToolObjPicker::handleSelect()
+{
+ LLTool::handleSelect();
+ setMouseCapture(TRUE);
+}
+
+// virtual
+void LLToolObjPicker::handleDeselect()
+{
+ if (hasMouseCapture())
+ {
+ LLTool::handleDeselect();
+ setMouseCapture(FALSE);
+ }
+}
+
+
diff --git a/indra/newview/lltoolobjpicker.h b/indra/newview/lltoolobjpicker.h
new file mode 100644
index 0000000000..cf6e81a5c7
--- /dev/null
+++ b/indra/newview/lltoolobjpicker.h
@@ -0,0 +1,45 @@
+/**
+ * @file lltoolobjpicker.h
+ * @brief LLToolObjPicker class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLOBJPICKER_H
+#define LL_TOOLOBJPICKER_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+class LLToolObjPicker : public LLTool
+{
+public:
+ LLToolObjPicker();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+
+ virtual void handleSelect();
+ virtual void handleDeselect();
+
+ virtual void onMouseCaptureLost();
+
+ virtual void setExitCallback(void (*callback)(void *), void *callback_data);
+
+ LLUUID getObjectID() const { return mHitObjectID; }
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+ BOOL mPicked;
+ LLUUID mHitObjectID;
+ void (*mExitCallback)(void *callback_data);
+ void *mExitCallbackData;
+};
+
+extern LLToolObjPicker* gToolObjPicker;
+
+#endif
diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp
new file mode 100644
index 0000000000..89df9c5249
--- /dev/null
+++ b/indra/newview/lltoolpie.cpp
@@ -0,0 +1,635 @@
+/**
+ * @file lltoolpie.cpp
+ * @brief LLToolPie class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolpie.h"
+
+#include "indra_constants.h"
+#include "llclickaction.h"
+#include "llparcel.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterland.h"
+#include "llfloaterscriptdebug.h"
+#include "llhoverview.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llselectmgr.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolselect.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "llui.h"
+
+LLToolPie *gToolPie = NULL;
+
+LLViewerObject* LLToolPie::sClickActionObject = NULL;
+
+extern void handle_buy(void*);
+
+extern BOOL gDebugClicks;
+
+LLToolPie::LLToolPie()
+: LLTool("Select"),
+ mPieMouseButtonDown( FALSE ),
+ mGrabMouseButtonDown( FALSE ),
+ mHitLand( FALSE ),
+ mHitObjectID(),
+ mMouseOutsideSlop( FALSE )
+{ }
+
+
+BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // if buttons swapped, don't pick transparent so users can't "pay"
+ // transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, leftMouseCallback,
+ TRUE, TRUE);
+ mGrabMouseButtonDown = TRUE;
+ return TRUE;
+}
+
+// static
+void LLToolPie::leftMouseCallback(S32 x, S32 y, MASK mask)
+{
+ gToolPie->pickAndShowMenu(x, y, mask, FALSE);
+}
+
+BOOL LLToolPie::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ // Pick faces in case they select "Copy Texture" and need that info.
+ gPickFaces = TRUE;
+ // don't pick transparent so users can't "pay" transparent objects
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, rightMouseCallback,
+ FALSE, TRUE);
+ mPieMouseButtonDown = TRUE;
+ // don't steal focus from UI
+ return FALSE;
+}
+
+// static
+void LLToolPie::rightMouseCallback(S32 x, S32 y, MASK mask)
+{
+ gToolPie->pickAndShowMenu(x, y, mask, TRUE);
+}
+
+// True if you selected an object.
+BOOL LLToolPie::pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL always_show)
+{
+ if (!always_show && gLastHitParcelWall)
+ {
+ LLParcel* parcel = gParcelMgr->getCollisionParcel();
+ if (parcel)
+ {
+ gParcelMgr->selectCollisionParcel();
+ if (parcel->getParcelFlag(PF_USE_PASS_LIST)
+ && !gParcelMgr->isCollisionBanned())
+ {
+ // if selling passes, just buy one
+ void* deselect_when_done = (void*)TRUE;
+ LLPanelLandGeneral::onClickBuyPass(deselect_when_done);
+ }
+ else
+ {
+ // not selling passes, get info
+ LLFloaterLand::show();
+ }
+ }
+
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ LLViewerObject *parent = NULL;
+
+ mHitLand = !object && !gLastHitPosGlobal.isExactlyZero();
+ if (!mHitLand)
+ {
+ gParcelMgr->deselectLand();
+ }
+
+ if (object)
+ {
+ mHitObjectID = object->mID;
+
+ parent = object->getRootEdit();
+ }
+ else
+ {
+ mHitObjectID.setNull();
+ }
+
+ BOOL touchable = (object && object->flagHandleTouch())
+ || (parent && parent->flagHandleTouch());
+
+ // If it's a left-click, and we have a special action, do it.
+ if (useClickAction(always_show, mask, object, parent))
+ {
+ U8 click_action = 0;
+ if (object && object->getClickAction())
+ {
+ click_action = object->getClickAction();
+ }
+ else if (parent && parent->getClickAction())
+ {
+ click_action = parent->getClickAction();
+ }
+
+ switch(click_action)
+ {
+ case CLICK_ACTION_TOUCH:
+ default:
+ // nothing
+ break;
+ case CLICK_ACTION_SIT:
+ handle_sit_or_stand();
+ return TRUE;
+ case CLICK_ACTION_PAY:
+ if (object && object->flagTakesMoney()
+ || parent && parent->flagTakesMoney())
+ {
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ return TRUE;
+ }
+ break;
+ case CLICK_ACTION_BUY:
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ return TRUE;
+ case CLICK_ACTION_OPEN:
+ if (parent && parent->allowOpen())
+ {
+ sClickActionObject = parent;
+ LLToolSelect::handleObjectSelection(parent, MASK_NONE, FALSE, TRUE);
+ }
+ return TRUE;
+ }
+ }
+
+ // Switch to grab tool if physical or triggerable
+ if (object &&
+ !object->isAvatar() &&
+ ((object->usePhysics() || (parent && !parent->isAvatar() && parent->usePhysics())) || touchable) &&
+ !always_show)
+ {
+ gGrabTransientTool = this;
+ gCurrentToolset->selectTool( gToolGrab );
+ return gToolGrab->handleObjectHit( object, x, y, mask);
+ }
+
+ if (!object && gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+ {
+ LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+ }
+
+ // If left-click never selects or spawns a menu
+ // Eat the event.
+ if (!gSavedSettings.getBOOL("LeftClickShowMenu")
+ && !always_show)
+ {
+ // mouse already released
+ if (!mGrabMouseButtonDown)
+ {
+ return TRUE;
+ }
+
+ while( object && object->isAttachment() && !object->flagHandleTouch())
+ {
+ // don't pick avatar through hud attachment
+ if (object->isHUDAttachment())
+ {
+ break;
+ }
+ object = (LLViewerObject*)object->getParent();
+ }
+ if (object && object == gAgent.getAvatarObject())
+ {
+ // we left clicked on avatar, switch to focus mode
+ gToolMgr->setTransientTool(gToolCamera);
+ gViewerWindow->hideCursor();
+ gToolCamera->setMouseCapture(TRUE);
+ gToolCamera->pickCallback(gViewerWindow->getCurrentMouseX(), gViewerWindow->getCurrentMouseY(), mask);
+ gAgent.setFocusOnAvatar(TRUE, TRUE);
+
+ return TRUE;
+ }
+ // Could be first left-click on nothing
+ LLFirstUse::useLeftClickNoHit();
+
+ // Eat the event
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ if (!always_show && gAgent.leftButtonGrabbed())
+ {
+ // if the left button is grabbed, don't put up the pie menu
+ return LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // Can't ignore children here.
+ LLToolSelect::handleObjectSelection(object, mask, FALSE, TRUE);
+
+ // Spawn pie menu
+ if (mHitLand)
+ {
+ gParcelMgr->selectParcelAt( gLastHitPosGlobal );
+
+ gPieLand->show(x, y, mPieMouseButtonDown);
+
+ // VEFFECT: ShowPie
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
+ effectp->setPositionGlobal(gLastHitPosGlobal);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ effectp->setDuration(0.25f);
+ }
+ else if (mHitObjectID == gAgent.getID() )
+ {
+ gPieSelf->show(x, y, mPieMouseButtonDown);
+ }
+ else if (object)
+ {
+ if (object->isAvatar()
+ || (object->isAttachment() && !object->isHUDAttachment() && !object->permYouOwner()))
+ {
+ // Find the attachment's avatar
+ while( object && object->isAttachment())
+ {
+ object = (LLViewerObject*)object->getParent();
+ }
+
+ // Object is an avatar, so check for mute by id.
+ LLVOAvatar* avatar = (LLVOAvatar*)object;
+ LLString name = avatar->getFullname();
+ if (gMuteListp->isMuted(avatar->getID(), name))
+ {
+ gMenuHolder->childSetText("Avatar Mute", "Unmute");
+ //gMutePieMenu->setLabel("Unmute");
+ }
+ else
+ {
+ gMenuHolder->childSetText("Avatar Mute", "Mute");
+ //gMutePieMenu->setLabel("Mute");
+ }
+
+ gPieAvatar->show(x, y, mPieMouseButtonDown);
+ }
+ else if (object->isAttachment())
+ {
+ gPieAttachment->show(x, y, mPieMouseButtonDown);
+ }
+ else
+ {
+ // BUG: What about chatting child objects?
+ LLString name;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ name = node->mName;
+ }
+ if (gMuteListp->isMuted(object->getID(), name))
+ {
+ gMenuHolder->childSetText("Object Mute", "Unmute");
+ //gMuteObjectPieMenu->setLabel("Unmute");
+ }
+ else
+ {
+ gMenuHolder->childSetText("Object Mute", "Mute");
+ //gMuteObjectPieMenu->setLabel("Mute");
+ }
+
+ gPieObject->show(x, y, mPieMouseButtonDown);
+
+ // VEFFECT: ShowPie object
+ // Don't show when you click on someone else, it freaks them
+ // out.
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_SPHERE, TRUE);
+ effectp->setPositionGlobal(gLastHitPosGlobal);
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ effectp->setDuration(0.25f);
+ }
+ }
+
+ if (always_show)
+ {
+ // ignore return value
+ LLTool::handleRightMouseDown(x, y, mask);
+ }
+ else
+ {
+ // ignore return value
+ LLTool::handleMouseDown(x, y, mask);
+ }
+
+ // We handled the event.
+ return TRUE;
+}
+
+BOOL LLToolPie::useClickAction(BOOL always_show,
+ MASK mask,
+ LLViewerObject* object,
+ LLViewerObject* parent)
+{
+ return !always_show
+ && mask == MASK_NONE
+ && object
+ && !object->isAttachment()
+ && LLPrimitive::isPrimitive(object->getPCode())
+ && (object->getClickAction()
+ || parent->getClickAction());
+
+}
+
+U8 final_click_action(LLViewerObject* obj)
+{
+ if (!obj) return CLICK_ACTION_NONE;
+ if (obj->isAttachment()) return CLICK_ACTION_NONE;
+
+ U8 click_action = CLICK_ACTION_TOUCH;
+ LLViewerObject* parent = (obj ? obj->getRootEdit() : NULL);
+ if ((obj && obj->getClickAction())
+ || (parent && parent->getClickAction()))
+ {
+ if (obj && obj->getClickAction())
+ {
+ click_action = obj->getClickAction();
+ }
+ else if (parent && parent->getClickAction())
+ {
+ click_action = parent->getClickAction();
+ }
+ }
+ return click_action;
+}
+
+// When we get object properties after left-clicking on an object
+// with left-click = buy, if it's the same object, do the buy.
+// static
+void LLToolPie::selectionPropertiesReceived()
+{
+ // Make sure all data has been received.
+ // This function will be called repeatedly as the data comes in.
+ if (!gSelectMgr->selectGetAllValid())
+ {
+ return;
+ }
+
+ if (sClickActionObject
+ && !sClickActionObject->isDead())
+ {
+ LLViewerObject* root = gSelectMgr->getFirstRootObject();
+ if (root == sClickActionObject)
+ {
+ U8 action = root->getClickAction();
+ switch (action)
+ {
+ case CLICK_ACTION_BUY:
+ handle_buy(NULL);
+ break;
+ case CLICK_ACTION_PAY:
+ handle_give_money_dialog();
+ break;
+ case CLICK_ACTION_OPEN:
+ handle_object_open();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ sClickActionObject = NULL;
+}
+
+BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask)
+{
+ /*
+ // If auto-rotate occurs, tag mouse-outside-slop to make sure the drag
+ // gets started.
+ const S32 ROTATE_H_MARGIN = (S32) (0.1f * gViewerWindow->getWindowWidth() );
+ const F32 ROTATE_ANGLE_PER_SECOND = 30.f * DEG_TO_RAD;
+ const F32 rotate_angle = ROTATE_ANGLE_PER_SECOND / gFPSClamped;
+ // ...normal modes can only yaw
+ if (x < ROTATE_H_MARGIN)
+ {
+ gAgent.yaw(rotate_angle);
+ mMouseOutsideSlop = TRUE;
+ }
+ else if (x > gViewerWindow->getWindowWidth() - ROTATE_H_MARGIN)
+ {
+ gAgent.yaw(-rotate_angle);
+ mMouseOutsideSlop = TRUE;
+ }
+ */
+
+ LLViewerObject *object = NULL;
+ LLViewerObject *parent = NULL;
+ if (gHoverView)
+ {
+ object = gHoverView->getLastHoverObject();
+ }
+
+ if (object)
+ {
+ parent = object->getRootEdit();
+ }
+
+ if (object && useClickAction(FALSE, mask, object, parent))
+ {
+ U8 click_action = final_click_action(object);
+ ECursorType cursor = UI_CURSOR_ARROW;
+ switch(click_action)
+ {
+ default: break;
+ case CLICK_ACTION_SIT: cursor = UI_CURSOR_TOOLSIT; break;
+ case CLICK_ACTION_BUY: cursor = UI_CURSOR_TOOLBUY; break;
+ case CLICK_ACTION_OPEN:
+ // Open always opens the parent.
+ if (parent && parent->allowOpen())
+ {
+ cursor = UI_CURSOR_TOOLOPEN;
+ }
+ break;
+ case CLICK_ACTION_PAY:
+ if ((object && object->flagTakesMoney())
+ || (parent && parent->flagTakesMoney()))
+ {
+ cursor = UI_CURSOR_TOOLPAY;
+ }
+ break;
+ }
+ gViewerWindow->getWindow()->setCursor(cursor);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else if ((object && !object->isAvatar() && object->usePhysics())
+ || (parent && !parent->isAvatar() && parent->usePhysics()))
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLGRAB);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else if ( (object && object->flagHandleTouch())
+ || (parent && parent->flagHandleTouch()))
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_HAND);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+ else
+ {
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl;
+ }
+
+ return TRUE;
+}
+
+BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* obj = gViewerWindow->lastObjectHit();
+ U8 click_action = final_click_action(obj);
+ if (click_action != CLICK_ACTION_NONE)
+ {
+ switch(click_action)
+ {
+ case CLICK_ACTION_BUY:
+ case CLICK_ACTION_PAY:
+ case CLICK_ACTION_OPEN:
+ // Because these actions open UI dialogs, we won't change
+ // the cursor again until the next hover and GL pick over
+ // the world. Keep the cursor an arrow, assuming that
+ // after the user moves off the UI, they won't be on the
+ // same object anymore.
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ // Make sure the hover-picked object is ignored.
+ gHoverView->resetLastHoverObject();
+ break;
+ default:
+ break;
+ }
+ }
+
+ mGrabMouseButtonDown = FALSE;
+ gToolMgr->clearTransientTool();
+ return LLTool::handleMouseUp(x, y, mask);
+}
+
+BOOL LLToolPie::handleRightMouseUp(S32 x, S32 y, MASK mask)
+{
+ mPieMouseButtonDown = FALSE;
+ gToolMgr->clearTransientTool();
+ return LLTool::handleRightMouseUp(x, y, mask);
+}
+
+
+BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ if (gDebugClicks)
+ {
+ llinfos << "LLToolPie handleDoubleClick (becoming mouseDown)" << llendl;
+ }
+
+ if (gSavedSettings.getBOOL("DoubleClickAutoPilot"))
+ {
+ if (gLastHitLand
+ && !gLastHitPosGlobal.isExactlyZero())
+ {
+ handle_go_to();
+ return TRUE;
+ }
+ else if (gLastHitObjectID.notNull()
+ && !gLastHitPosGlobal.isExactlyZero())
+ {
+ // Hit an object
+ // HACK: Call the last hit position the point we hit on the object
+ gLastHitPosGlobal += gLastHitObjectOffset;
+ handle_go_to();
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+ /* JC - don't do go-there, because then double-clicking on physical
+ objects gets you into trouble.
+
+ // If double-click on object or land, go there.
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (object)
+ {
+ if (object->isAvatar())
+ {
+ LLFloaterAvatarInfo::showFromAvatar(object);
+ }
+ else
+ {
+ handle_go_to(NULL);
+ }
+ }
+ else if (!gLastHitPosGlobal.isExactlyZero())
+ {
+ handle_go_to(NULL);
+ }
+
+ return TRUE;
+ */
+}
+
+
+void LLToolPie::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+ // remove temporary selection for pie menu
+ gSelectMgr->validateSelection();
+}
+
+
+void LLToolPie::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+void LLToolPie::onMouseCaptureLost()
+{
+ mMouseOutsideSlop = FALSE;
+}
+
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolPie::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
+
+
+void LLToolPie::render()
+{
+ return;
+}
+
diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h
new file mode 100644
index 0000000000..d440a443e2
--- /dev/null
+++ b/indra/newview/lltoolpie.h
@@ -0,0 +1,59 @@
+/**
+ * @file lltoolpie.h
+ * @brief LLToolPie class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLPIE_H
+#define LL_TOOLPIE_H
+
+#include "lltool.h"
+#include "lluuid.h"
+
+class LLViewerObject;
+
+class LLToolPie
+: public LLTool
+{
+public:
+ LLToolPie( );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ virtual void render();
+
+ virtual void stopEditing();
+
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+ static void leftMouseCallback(S32 x, S32 y, MASK mask);
+ static void rightMouseCallback(S32 x, S32 y, MASK mask);
+
+ static void selectionPropertiesReceived();
+
+protected:
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+ BOOL pickAndShowMenu(S32 x, S32 y, MASK mask, BOOL edit_menu);
+ BOOL useClickAction(BOOL always_show, MASK mask, LLViewerObject* object,
+ LLViewerObject* parent);
+
+protected:
+ BOOL mPieMouseButtonDown;
+ BOOL mGrabMouseButtonDown;
+ BOOL mHitLand;
+ LLUUID mHitObjectID;
+ BOOL mMouseOutsideSlop; // for this drag, has mouse moved outside slop region
+ static LLViewerObject* sClickActionObject;
+};
+
+extern LLToolPie *gToolPie;
+
+#endif
diff --git a/indra/newview/lltoolpipette.cpp b/indra/newview/lltoolpipette.cpp
new file mode 100755
index 0000000000..8d4d58f40c
--- /dev/null
+++ b/indra/newview/lltoolpipette.cpp
@@ -0,0 +1,120 @@
+/**
+ * @file lltoolpipette.cpp
+ * @brief LLToolPipette class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ * A tool to pick texture entry infro from objects in world (color/texture)
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolpipette.h"
+
+// Library includes
+
+// Viewer includes
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+
+// Globals
+LLToolPipette *gToolPipette = NULL;
+
+//
+// Member functions
+//
+
+LLToolPipette::LLToolPipette()
+: LLTool("Pipette"),
+mSuccess(TRUE)
+{
+ mSelectCallback = NULL;
+ mUserData = NULL;
+}
+
+
+LLToolPipette::~LLToolPipette()
+{ }
+
+
+BOOL LLToolPipette::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ mSuccess = TRUE;
+ mTooltipMsg.clear();
+ gPickFaces = TRUE;
+ setMouseCapture(TRUE);
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+}
+
+BOOL LLToolPipette::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mSuccess = TRUE;
+ gSelectMgr->unhighlightAll();
+ //FIXME: this assumes the pipette tool is a transient tool
+ gToolMgr->clearTransientTool();
+ setMouseCapture(FALSE);
+ return TRUE;
+}
+
+BOOL LLToolPipette::handleHover(S32 x, S32 y, MASK mask)
+{
+ gViewerWindow->setCursor(mSuccess ? UI_CURSOR_PIPETTE : UI_CURSOR_NO);
+ if (hasMouseCapture()) // mouse button is down
+ {
+ gPickFaces = TRUE;
+ gViewerWindow->hitObjectOrLandGlobalAsync(x, y, mask, pickCallback);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLToolPipette::handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen)
+{
+ if (mTooltipMsg.empty())
+ {
+ return FALSE;
+ }
+ // keep tooltip message up when mouse in this part of screen
+ sticky_rect_screen->setCenterAndSize(x, y, 20, 20);
+ msg = mTooltipMsg;
+ return TRUE;
+}
+
+void LLToolPipette::pickCallback(S32 x, S32 y, MASK mask)
+{
+ LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
+ gSelectMgr->unhighlightAll();
+
+ // if we clicked on a face of a valid prim, save off texture entry data
+ if (hit_obj &&
+ hit_obj->getPCode() == LL_PCODE_VOLUME &&
+ gLastHitObjectFace != -1)
+ {
+ //TODO: this should highlight the selected face only
+ gSelectMgr->highlightObjectOnly(hit_obj);
+ gToolPipette->mTextureEntry = *hit_obj->getTE(gLastHitObjectFace);
+ if (gToolPipette->mSelectCallback)
+ {
+ gToolPipette->mSelectCallback(gToolPipette->mTextureEntry, gToolPipette->mUserData);
+ }
+ }
+}
+
+void LLToolPipette::setSelectCallback(select_callback callback, void* user_data)
+{
+ mSelectCallback = callback;
+ mUserData = user_data;
+}
+
+void LLToolPipette::setResult(BOOL success, const LLString& msg)
+{
+ mTooltipMsg = msg;
+ mSuccess = success;
+}
diff --git a/indra/newview/lltoolpipette.h b/indra/newview/lltoolpipette.h
new file mode 100755
index 0000000000..532f75c3e7
--- /dev/null
+++ b/indra/newview/lltoolpipette.h
@@ -0,0 +1,47 @@
+/**
+ * @file lltoolpipette.h
+ * @brief LLToolPipette class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A tool to pick texture entry infro from objects in world (color/texture)
+
+#ifndef LL_LLTOOLPIPETTE_H
+#define LL_LLTOOLPIPETTE_H
+
+#include "lltool.h"
+#include "lltextureentry.h"
+
+class LLViewerObject;
+
+class LLToolPipette
+: public LLTool
+{
+public:
+ LLToolPipette();
+ virtual ~LLToolPipette();
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect *sticky_rect_screen);
+
+ typedef void (*select_callback)(const LLTextureEntry& te, void *data);
+ void setSelectCallback(select_callback callback, void* user_data);
+ void setResult(BOOL success, const LLString& msg);
+
+ static void pickCallback(S32 x, S32 y, MASK mask);
+
+protected:
+ LLTextureEntry mTextureEntry;
+ select_callback mSelectCallback;
+ BOOL mSuccess;
+ LLString mTooltipMsg;
+ void* mUserData;
+};
+
+extern LLToolPipette *gToolPipette;
+
+#endif //LL_LLTOOLPIPETTE_H
diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp
new file mode 100644
index 0000000000..facfda2c85
--- /dev/null
+++ b/indra/newview/lltoolplacer.cpp
@@ -0,0 +1,225 @@
+/**
+ * @file lltoolplacer.cpp
+ * @brief Tool for placing new objects into the world
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// self header
+#include "lltoolplacer.h"
+
+// linden library headers
+#include "llprimitive.h"
+
+// viewer headers
+#include "llbutton.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolcomp.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llui.h"
+
+//static
+LLPCode LLToolPlacer::sObjectType = LL_PCODE_CUBE;
+
+LLToolPlacer::LLToolPlacer()
+: LLTool( "Create" )
+{
+}
+
+// Used by the placer tool to add copies of the current selection.
+// Inspired by add_object(). JC
+BOOL add_duplicate(S32 x, S32 y)
+{
+ LLVector3 ray_start_region;
+ LLVector3 ray_end_region;
+ LLViewerRegion* regionp = NULL;
+ BOOL b_hit_land = FALSE;
+ S32 hit_face = -1;
+ LLViewerObject* hit_obj = NULL;
+ BOOL success = raycast_for_new_obj_pos( x, y, &hit_obj, &hit_face, &b_hit_land, &ray_start_region, &ray_end_region, &regionp );
+ if( !success )
+ {
+ make_ui_sound("UISndInvalidOp");
+ return FALSE;
+ }
+ if( hit_obj && (hit_obj->isAvatar() || hit_obj->isAttachment()) )
+ {
+ // Can't create objects on avatars or attachments
+ make_ui_sound("UISndInvalidOp");
+ return FALSE;
+ }
+
+
+ // Limit raycast to a single object.
+ // Speeds up server raycast + avoid problems with server ray hitting objects
+ // that were clipped by the near plane or culled on the viewer.
+ LLUUID ray_target_id;
+ if( hit_obj )
+ {
+ ray_target_id = hit_obj->getID();
+ }
+ else
+ {
+ ray_target_id.setNull();
+ }
+
+ gSelectMgr->selectDuplicateOnRay(ray_start_region,
+ ray_end_region,
+ b_hit_land, // suppress raycast
+ FALSE, // intersection
+ ray_target_id,
+ gSavedSettings.getBOOL("CreateToolCopyCenters"),
+ gSavedSettings.getBOOL("CreateToolCopyRotates"),
+ FALSE); // select copy
+
+ if (regionp
+ && (regionp->getRegionFlags() & REGION_FLAGS_SANDBOX))
+ {
+ LLFirstUse::useSandbox();
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLToolPlacer::placeObject(S32 x, S32 y, MASK mask)
+{
+ BOOL added = TRUE;
+
+ if (gSavedSettings.getBOOL("CreateToolCopySelection"))
+ {
+ added = add_duplicate(x, y);
+ }
+ else
+ {
+ added = add_object( sObjectType, x, y, NO_PHYSICS );
+ }
+
+ // ...and go back to the default tool
+ if (added && !gSavedSettings.getBOOL("CreateToolKeepSelected"))
+ {
+ gCurrentToolset->selectTool( gToolTranslate );
+ }
+
+ return added;
+}
+
+BOOL LLToolPlacer::handleHover(S32 x, S32 y, MASK mask)
+{
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPlacer" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_TOOLCREATE);
+ return TRUE;
+}
+
+void LLToolPlacer::handleSelect()
+{
+ gFloaterTools->setStatusText("Click in the world to create, shift-click to select");
+}
+
+void LLToolPlacer::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+}
+
+//////////////////////////////////////////////////////
+// LLToolPlacerPanel
+
+// static
+LLPCode LLToolPlacerPanel::sCube = LL_PCODE_CUBE;
+LLPCode LLToolPlacerPanel::sPrism = LL_PCODE_PRISM;
+LLPCode LLToolPlacerPanel::sPyramid = LL_PCODE_PYRAMID;
+LLPCode LLToolPlacerPanel::sTetrahedron = LL_PCODE_TETRAHEDRON;
+LLPCode LLToolPlacerPanel::sCylinder = LL_PCODE_CYLINDER;
+LLPCode LLToolPlacerPanel::sCylinderHemi= LL_PCODE_CYLINDER_HEMI;
+LLPCode LLToolPlacerPanel::sCone = LL_PCODE_CONE;
+LLPCode LLToolPlacerPanel::sConeHemi = LL_PCODE_CONE_HEMI;
+LLPCode LLToolPlacerPanel::sTorus = LL_PCODE_TORUS;
+LLPCode LLToolPlacerPanel::sSquareTorus = LLViewerObject::LL_VO_SQUARE_TORUS;
+LLPCode LLToolPlacerPanel::sTriangleTorus = LLViewerObject::LL_VO_TRIANGLE_TORUS;
+LLPCode LLToolPlacerPanel::sSphere = LL_PCODE_SPHERE;
+LLPCode LLToolPlacerPanel::sSphereHemi = LL_PCODE_SPHERE_HEMI;
+LLPCode LLToolPlacerPanel::sTree = LL_PCODE_LEGACY_TREE;
+LLPCode LLToolPlacerPanel::sGrass = LL_PCODE_LEGACY_GRASS;
+LLPCode LLToolPlacerPanel::sTreeNew = LL_PCODE_TREE_NEW;
+
+S32 LLToolPlacerPanel::sButtonsAdded = 0;
+LLButton* LLToolPlacerPanel::sButtons[ TOOL_PLACER_NUM_BUTTONS ];
+
+LLToolPlacerPanel::LLToolPlacerPanel(const std::string& name, const LLRect& rect)
+ :
+ LLPanel( name, rect )
+{
+ /* DEPRECATED - JC
+ addButton( "UIImgCubeUUID", "UIImgCubeSelectedUUID", &LLToolPlacerPanel::sCube );
+ addButton( "UIImgPrismUUID", "UIImgPrismSelectedUUID", &LLToolPlacerPanel::sPrism );
+ addButton( "UIImgPyramidUUID", "UIImgPyramidSelectedUUID", &LLToolPlacerPanel::sPyramid );
+ addButton( "UIImgTetrahedronUUID", "UIImgTetrahedronSelectedUUID", &LLToolPlacerPanel::sTetrahedron );
+ addButton( "UIImgCylinderUUID", "UIImgCylinderSelectedUUID", &LLToolPlacerPanel::sCylinder );
+ addButton( "UIImgHalfCylinderUUID", "UIImgHalfCylinderSelectedUUID",&LLToolPlacerPanel::sCylinderHemi );
+ addButton( "UIImgConeUUID", "UIImgConeSelectedUUID", &LLToolPlacerPanel::sCone );
+ addButton( "UIImgHalfConeUUID", "UIImgHalfConeSelectedUUID", &LLToolPlacerPanel::sConeHemi );
+ addButton( "UIImgSphereUUID", "UIImgSphereSelectedUUID", &LLToolPlacerPanel::sSphere );
+ addButton( "UIImgHalfSphereUUID", "UIImgHalfSphereSelectedUUID", &LLToolPlacerPanel::sSphereHemi );
+ addButton( "UIImgTreeUUID", "UIImgTreeSelectedUUID", &LLToolPlacerPanel::sTree );
+ addButton( "UIImgGrassUUID", "UIImgGrassSelectedUUID", &LLToolPlacerPanel::sGrass );
+ addButton( "ObjectTorusImageID", "ObjectTorusActiveImageID", &LLToolPlacerPanel::sTorus );
+ addButton( "ObjectTubeImageID", "ObjectTubeActiveImageID", &LLToolPlacerPanel::sSquareTorus );
+ */
+}
+
+void LLToolPlacerPanel::addButton( const LLString& up_state, const LLString& down_state, LLPCode* pcode )
+{
+ const S32 TOOL_SIZE = 32;
+ const S32 HORIZ_SPACING = TOOL_SIZE + 5;
+ const S32 VERT_SPACING = TOOL_SIZE + 5;
+ const S32 VPAD = 10;
+ const S32 HPAD = 7;
+
+ S32 row = sButtonsAdded / 4;
+ S32 column = sButtonsAdded % 4;
+
+ LLRect help_rect = gSavedSettings.getRect("ToolHelpRect");
+
+ // Build the rectangle, recalling the origin is at lower left
+ // and we want the icons to build down from the top.
+ LLRect rect;
+ rect.setLeftTopAndSize(
+ HPAD + (column * HORIZ_SPACING),
+ help_rect.mBottom - VPAD - (row * VERT_SPACING),
+ TOOL_SIZE,
+ TOOL_SIZE );
+
+ LLButton* btn = new LLButton(
+ "ToolPlacerOptBtn",
+ rect,
+ up_state,
+ down_state,
+ "", &LLToolPlacerPanel::setObjectType,
+ pcode,
+ LLFontGL::sSansSerif);
+ btn->setFollowsBottom();
+ btn->setFollowsLeft();
+ addChild(btn);
+
+ sButtons[sButtonsAdded] = btn;
+ sButtonsAdded++;
+}
+
+// static
+void LLToolPlacerPanel::setObjectType( void* data )
+{
+ LLPCode pcode = *(LLPCode*) data;
+ LLToolPlacer::setObjectType( pcode );
+}
diff --git a/indra/newview/lltoolplacer.h b/indra/newview/lltoolplacer.h
new file mode 100644
index 0000000000..650fc03b01
--- /dev/null
+++ b/indra/newview/lltoolplacer.h
@@ -0,0 +1,79 @@
+/**
+ * @file lltoolplacer.h
+ * @brief Tool for placing new objects into the world
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLPLACER_H
+#define LL_TOOLPLACER_H
+
+#include "llprimitive.h"
+#include "llpanel.h"
+#include "lltool.h"
+
+class LLButton;
+
+////////////////////////////////////////////////////
+// LLToolPlacer
+
+class LLToolPlacer
+ : public LLTool
+{
+public:
+ LLToolPlacer();
+
+ virtual BOOL placeObject(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void handleSelect(); // do stuff when your tool is selected
+ virtual void handleDeselect(); // clean up when your tool is deselected
+
+ static void setObjectType( LLPCode type ) { sObjectType = type; }
+ static LLPCode getObjectType() { return sObjectType; }
+
+protected:
+ static LLPCode sObjectType;
+};
+
+////////////////////////////////////////////////////
+// LLToolPlacerPanel
+
+
+const S32 TOOL_PLACER_NUM_BUTTONS = 14;
+
+
+class LLToolPlacerPanel : public LLPanel
+{
+public:
+
+ LLToolPlacerPanel(const std::string& name, const LLRect& rect);
+
+ static void setObjectType( void* data );
+
+ static LLPCode sCube;
+ static LLPCode sPrism;
+ static LLPCode sPyramid;
+ static LLPCode sTetrahedron;
+ static LLPCode sCylinder;
+ static LLPCode sCylinderHemi;
+ static LLPCode sCone;
+ static LLPCode sConeHemi;
+ static LLPCode sTorus;
+ static LLPCode sSquareTorus;
+ static LLPCode sTriangleTorus;
+ static LLPCode sSphere;
+ static LLPCode sSphereHemi;
+ static LLPCode sTree;
+ static LLPCode sGrass;
+ static LLPCode sTreeNew;
+
+private:
+ void addButton( const LLString& up_state, const LLString& down_state, LLPCode* pcode );
+
+private:
+ static S32 sButtonsAdded;
+ static LLButton* sButtons[ TOOL_PLACER_NUM_BUTTONS ];
+};
+
+#endif
diff --git a/indra/newview/lltoolselect.cpp b/indra/newview/lltoolselect.cpp
new file mode 100644
index 0000000000..a303776ade
--- /dev/null
+++ b/indra/newview/lltoolselect.cpp
@@ -0,0 +1,244 @@
+/**
+ * @file lltoolselect.cpp
+ * @brief LLToolSelect class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolselect.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llmanip.h"
+#include "llmenugl.h"
+#include "llselectmgr.h"
+#include "lltoolmgr.h"
+#include "llfloaterscriptdebug.h"
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+#include "viewer.h" // for gFPSClamped, pie menus
+
+// Globals
+LLToolSelect *gToolSelect = NULL;
+extern BOOL gAllowSelectAvatar;
+
+const F32 SELECTION_ROTATION_TRESHOLD = 0.1f;
+
+LLToolSelect::LLToolSelect( LLToolComposite* composite )
+: LLTool( "Select", composite ),
+ mIgnoreGroup( FALSE )
+{
+ }
+
+// True if you selected an object.
+BOOL LLToolSelect::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // didn't click in any UI object, so must have clicked in the world
+ LLViewerObject* object = NULL;
+
+ // You must hit the body for this tool to think you hit the object.
+ object = gObjectList.findObject( gLastHitObjectID );
+
+ if (object)
+ {
+ mSelectObjectID = object->getID();
+ handled = TRUE;
+ }
+ else
+ {
+ mSelectObjectID.setNull();
+ }
+
+ // Pass mousedown to agent
+ LLTool::handleMouseDown(x, y, mask);
+
+ return handled;
+}
+
+BOOL LLToolSelect::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ //RN: double click to toggle individual/linked picking???
+ return LLTool::handleDoubleClick(x, y, mask);
+}
+
+// static
+void LLToolSelect::handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select)
+{
+ BOOL select_owned = gSavedSettings.getBOOL("SelectOwnedOnly");
+ BOOL select_movable = gSavedSettings.getBOOL("SelectMovableOnly");
+
+ // *NOTE: These settings must be cleaned up at bottom of function.
+ if (temp_select || gAllowSelectAvatar)
+ {
+ gSavedSettings.setBOOL("SelectOwnedOnly", FALSE);
+ gSavedSettings.setBOOL("SelectMovableOnly", FALSE);
+ gSelectMgr->setForceSelection(TRUE);
+ }
+
+ BOOL extend_select = (mask == MASK_SHIFT) || (mask == MASK_CONTROL);
+
+ // If no object, check for icon, then just deselect
+ if (!object)
+ {
+ if (gLastHitHUDIcon && gLastHitHUDIcon->getSourceObject())
+ {
+ LLFloaterScriptDebug::show(gLastHitHUDIcon->getSourceObject()->getID());
+ }
+ else if (!extend_select)
+ {
+ gSelectMgr->deselectAll();
+ }
+ }
+ else
+ {
+ BOOL already_selected = object->isSelected();
+
+ if ( extend_select )
+ {
+ if ( already_selected )
+ {
+ if ( ignore_group )
+ {
+ gSelectMgr->deselectObjectOnly(object);
+ }
+ else
+ {
+ gSelectMgr->deselectObjectAndFamily(object);
+ }
+ }
+ else
+ {
+ if ( ignore_group )
+ {
+ gSelectMgr->selectObjectOnly(object, SELECT_ALL_TES);
+ }
+ else
+ {
+ gSelectMgr->selectObjectAndFamily(object);
+ }
+ }
+ }
+ else
+ {
+ // JC - Change behavior to make it easier to select children
+ // of linked sets. 9/3/2002
+ if( !already_selected || ignore_group)
+ {
+ // ...lose current selection in favor of just this object
+ gSelectMgr->deselectAll();
+ }
+
+ if ( ignore_group )
+ {
+ gSelectMgr->selectObjectOnly(object, SELECT_ALL_TES);
+ }
+ else
+ {
+ gSelectMgr->selectObjectAndFamily(object);
+ }
+ }
+
+ if (!gAgent.getFocusOnAvatar() && // if camera not glued to avatar
+ LLVOAvatar::findAvatarFromAttachment(object) != gAgent.getAvatarObject() && // and it's not one of your attachments
+ object != gAgent.getAvatarObject()) // and it's not you
+ {
+ // have avatar turn to face the selected object(s)
+ LLVector3d selection_center = gSelectMgr->getSelectionCenterGlobal();
+ selection_center = selection_center - gAgent.getPositionGlobal();
+ LLVector3 selection_dir;
+ selection_dir.setVec(selection_center);
+ selection_dir.mV[VZ] = 0.f;
+ selection_dir.normVec();
+ if (!object->isAvatar() && gAgent.getAtAxis() * selection_dir < 0.6f)
+ {
+ LLQuaternion target_rot;
+ target_rot.shortestArc(LLVector3::x_axis, selection_dir);
+ gAgent.startAutoPilotGlobal(gAgent.getPositionGlobal(), "", &target_rot, NULL, NULL, 1.f, SELECTION_ROTATION_TRESHOLD);
+ }
+ }
+
+ if (temp_select)
+ {
+ if (!already_selected)
+ {
+ LLViewerObject* root_object = (LLViewerObject*)object->getRootEdit();
+
+ // this is just a temporary selection
+ LLSelectNode* select_node = gSelectMgr->findSelectNode(root_object);
+ if (select_node)
+ {
+ select_node->setTransient(TRUE);
+ }
+
+ for (S32 i = 0; i < (S32)root_object->mChildList.size(); i++)
+ {
+ select_node = gSelectMgr->findSelectNode(root_object->mChildList[i]);
+ if (select_node)
+ {
+ select_node->setTransient(TRUE);
+ }
+ }
+
+ }
+ } //if(temp_select)
+ } //if(!object)
+
+ // Cleanup temp select settings above.
+ if (temp_select || gAllowSelectAvatar)
+ {
+ gSavedSettings.setBOOL("SelectOwnedOnly", select_owned);
+ gSavedSettings.setBOOL("SelectMovableOnly", select_movable);
+ gSelectMgr->setForceSelection(FALSE);
+ }
+}
+
+BOOL LLToolSelect::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ mIgnoreGroup = !gSavedSettings.getBOOL("SelectLinkedSet");
+
+ LLViewerObject* object = gObjectList.findObject(mSelectObjectID);
+ LLToolSelect::handleObjectSelection(object, mask, mIgnoreGroup, FALSE);
+
+ return LLTool::handleMouseUp(x, y, mask);
+}
+
+void LLToolSelect::handleDeselect()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+
+void LLToolSelect::stopEditing()
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE ); // Calls onMouseCaptureLost() indirectly
+ }
+}
+
+void LLToolSelect::onMouseCaptureLost()
+{
+ // Finish drag
+
+ gSelectMgr->enableSilhouette(TRUE);
+
+ // Clean up drag-specific variables
+ mIgnoreGroup = FALSE;
+}
+
+
diff --git a/indra/newview/lltoolselect.h b/indra/newview/lltoolselect.h
new file mode 100644
index 0000000000..db31494834
--- /dev/null
+++ b/indra/newview/lltoolselect.h
@@ -0,0 +1,39 @@
+/**
+ * @file lltoolselect.h
+ * @brief LLToolSelect class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_TOOLSELECT_H
+#define LL_TOOLSELECT_H
+
+#include "lltool.h"
+#include "v3math.h"
+#include "lluuid.h"
+
+class LLToolSelect : public LLTool
+{
+public:
+ LLToolSelect( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+
+ virtual void stopEditing();
+
+ static void handleObjectSelection(LLViewerObject *object, MASK mask, BOOL ignore_group, BOOL temp_select);
+
+ virtual void onMouseCaptureLost();
+ virtual void handleDeselect();
+
+protected:
+ BOOL mIgnoreGroup;
+ LLUUID mSelectObjectID;
+};
+
+extern LLToolSelect *gToolSelect;
+
+#endif // LL_TOOLSELECTION_H
diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp
new file mode 100644
index 0000000000..7d51d4a81c
--- /dev/null
+++ b/indra/newview/lltoolselectland.cpp
@@ -0,0 +1,223 @@
+/**
+ * @file lltoolselectland.cpp
+ * @brief LLToolSelectLand class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolselectland.h"
+
+// indra includes
+#include "llparcel.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llfloatertools.h"
+#include "llselectmgr.h"
+#include "llstatusbar.h"
+#include "lltoolview.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+
+// Globals
+LLToolSelectLand *gToolParcel = NULL;
+
+//
+// Member functions
+//
+
+LLToolSelectLand::LLToolSelectLand( )
+: LLTool( "Parcel" ),
+ mDragStartGlobal(),
+ mDragEndGlobal(),
+ mDragEndValid(FALSE),
+ mDragStartX(0),
+ mDragStartY(0),
+ mDragEndX(0),
+ mDragEndY(0),
+ mMouseOutsideSlop(FALSE),
+ mWestSouthBottom(),
+ mEastNorthTop(),
+ mLastShowParcelOwners(FALSE)
+{ }
+
+
+BOOL LLToolSelectLand::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &mDragStartGlobal);
+ if (hit_land)
+ {
+ setMouseCapture( TRUE );
+
+ mDragStartX = x;
+ mDragStartY = y;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ mDragEndValid = TRUE;
+ mDragEndGlobal = mDragStartGlobal;
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ mMouseOutsideSlop = TRUE; //FALSE;
+
+ gParcelMgr->deselectLand();
+ }
+
+ return hit_land;
+}
+
+
+BOOL LLToolSelectLand::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ LLVector3d pos_global;
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &pos_global);
+ if (hit_land)
+ {
+ // Auto-select this parcel
+ gParcelMgr->selectParcelAt( pos_global );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLToolSelectLand::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ setMouseCapture( FALSE );
+
+ if (mMouseOutsideSlop && mDragEndValid)
+ {
+ // Take the drag start and end locations, then map the southwest
+ // point down to the next grid location, and the northeast point up
+ // to the next grid location.
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ // Don't auto-select entire parcel.
+ gParcelMgr->selectLand( mWestSouthBottom, mEastNorthTop, FALSE );
+ }
+
+ mMouseOutsideSlop = FALSE;
+ mDragEndValid = FALSE;
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLToolSelectLand::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if (mMouseOutsideSlop || outsideSlop(x, y, mDragStartX, mDragStartY))
+ {
+ mMouseOutsideSlop = TRUE;
+
+ // Must do this every frame, in case the camera moved or the land moved
+ // since last frame.
+
+ // If doesn't hit land, doesn't change old value
+ LLVector3d land_global;
+ BOOL hit_land = gViewerWindow->mousePointOnLandGlobal(x, y, &land_global);
+ if (hit_land)
+ {
+ mDragEndValid = TRUE;
+ mDragEndGlobal = land_global;
+
+ sanitize_corners(mDragStartGlobal, mDragEndGlobal, mWestSouthBottom, mEastNorthTop);
+
+ mWestSouthBottom -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+ mEastNorthTop += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
+
+ roundXY(mWestSouthBottom);
+ roundXY(mEastNorthTop);
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, land)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ else
+ {
+ mDragEndValid = FALSE;
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, no land)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_NO);
+ }
+
+ mDragEndX = x;
+ mDragEndY = y;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (active, in slop)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectLand (inactive)" << llendl;
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+
+ return TRUE;
+}
+
+
+void LLToolSelectLand::render()
+{
+ if( hasMouseCapture() && /*mMouseOutsideSlop &&*/ mDragEndValid)
+ {
+ gParcelMgr->renderRect( mWestSouthBottom, mEastNorthTop );
+ }
+}
+
+void LLToolSelectLand::handleSelect()
+{
+ gFloaterTools->setStatusText("Click and drag to select land");
+ mLastShowParcelOwners = gSavedSettings.getBOOL("ShowParcelOwners");
+ gSavedSettings.setBOOL("ShowParcelOwners", TRUE);
+}
+
+
+void LLToolSelectLand::handleDeselect()
+{
+ gFloaterTools->setStatusText("");
+ //gParcelMgr->deselectLand();
+ gSavedSettings.setBOOL("ShowParcelOwners", mLastShowParcelOwners);
+}
+
+
+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 );
+}
+
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolSelectLand::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
diff --git a/indra/newview/lltoolselectland.h b/indra/newview/lltoolselectland.h
new file mode 100644
index 0000000000..9ae2b071b5
--- /dev/null
+++ b/indra/newview/lltoolselectland.h
@@ -0,0 +1,56 @@
+/**
+ * @file lltoolselectland.h
+ * @brief LLToolSelectLand class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLSELECTLAND_H
+#define LL_LLTOOLSELECTLAND_H
+
+#include "lltool.h"
+#include "v3dmath.h"
+
+class LLToolSelectLand
+: public LLTool
+{
+public:
+ LLToolSelectLand( );
+
+ /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask);
+ /*virtual*/ void render(); // draw the select rectangle
+ /*virtual*/ BOOL isAlwaysRendered() { return TRUE; }
+
+ /*virtual*/ void handleSelect();
+ /*virtual*/ void handleDeselect();
+
+protected:
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+ void roundXY(LLVector3d& vec);
+
+protected:
+ LLVector3d mDragStartGlobal; // global coords
+ LLVector3d mDragEndGlobal; // global coords
+ BOOL mDragEndValid; // is drag end a valid point in the world?
+
+ S32 mDragStartX; // screen coords, from left
+ S32 mDragStartY; // screen coords, from bottom
+
+ S32 mDragEndX;
+ S32 mDragEndY;
+
+ BOOL mMouseOutsideSlop; // has mouse ever gone outside slop region?
+
+ LLVector3d mWestSouthBottom; // global coords, from drag
+ LLVector3d mEastNorthTop; // global coords, from drag
+
+ BOOL mLastShowParcelOwners; // store last Show Parcel Owners setting
+};
+
+extern LLToolSelectLand *gToolParcel;
+
+#endif
diff --git a/indra/newview/lltoolselectrect.cpp b/indra/newview/lltoolselectrect.cpp
new file mode 100644
index 0000000000..b9c43d4221
--- /dev/null
+++ b/indra/newview/lltoolselectrect.cpp
@@ -0,0 +1,182 @@
+/**
+ * @file lltoolselectrect.cpp
+ * @brief A tool to select multiple objects with a screen-space rectangle.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// File includes
+#include "lltoolselectrect.h"
+
+// Library includes
+#include "llgl.h"
+#include "lldarray.h"
+
+// Viewer includes
+#include "llviewercontrol.h"
+#include "llui.h"
+#include "llselectmgr.h"
+#include "lltoolview.h"
+#include "lltoolmgr.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerwindow.h"
+#include "llviewercamera.h"
+#include "viewer.h"
+
+#include "llglheaders.h"
+
+// Globals
+const S32 SLOP_RADIUS = 5;
+
+
+//
+// Member functions
+//
+
+LLToolSelectRect::LLToolSelectRect( LLToolComposite* composite )
+ :
+ LLToolSelect( composite ),
+ mDragStartX(0),
+ mDragStartY(0),
+ mDragEndX(0),
+ mDragEndY(0),
+ mDragLastWidth(0),
+ mDragLastHeight(0),
+ mMouseOutsideSlop(FALSE)
+
+{ }
+
+
+void dialog_refresh_all(void);
+
+BOOL LLToolSelectRect::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ // start dragging rectangle
+ setMouseCapture( TRUE );
+
+ mDragStartX = x;
+ mDragStartY = y;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ mMouseOutsideSlop = FALSE;
+
+ LLToolSelect::handleMouseDown(x, y, mask);
+ return TRUE;
+}
+
+
+BOOL LLToolSelectRect::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ setMouseCapture( FALSE );
+
+ if( mMouseOutsideSlop )
+ {
+ mDragLastWidth = 0;
+ mDragLastHeight = 0;
+
+ mMouseOutsideSlop = FALSE;
+
+ if (mask == MASK_CONTROL)
+ {
+ gSelectMgr->deselectHighlightedObjects();
+ }
+ else
+ {
+ gSelectMgr->selectHighlightedObjects();
+ }
+ return TRUE;
+ }
+ else
+ {
+ return LLToolSelect::handleMouseUp(x, y, mask);
+ }
+}
+
+
+BOOL LLToolSelectRect::handleHover(S32 x, S32 y, MASK mask)
+{
+ if( hasMouseCapture() )
+ {
+ if (mMouseOutsideSlop || outsideSlop(x, y, mDragStartX, mDragStartY))
+ {
+ if (!mMouseOutsideSlop && !(mask & MASK_SHIFT) && !(mask & MASK_CONTROL))
+ {
+ // just started rect select, and not adding to current selection
+ gSelectMgr->deselectAll();
+ }
+ mMouseOutsideSlop = TRUE;
+ mDragEndX = x;
+ mDragEndY = y;
+
+ handleRectangleSelection(x, y, mask);
+ }
+ else
+ {
+ return LLToolSelect::handleHover(x, y, mask);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectRect (active)" << llendl;
+ }
+ else
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolSelectRect (inactive)" << llendl;
+ }
+
+ gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW);
+ return TRUE;
+}
+
+
+void LLToolSelectRect::draw()
+{
+ if( hasMouseCapture() && mMouseOutsideSlop)
+ {
+ if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(1.f, 1.f, 0.f, 1.f);
+ }
+ LLGLSNoTexture gls_no_texture;
+ gl_rect_2d(
+ llmin(mDragStartX, mDragEndX),
+ llmax(mDragStartY, mDragEndY),
+ llmax(mDragStartX, mDragEndX),
+ llmin(mDragStartY, mDragEndY),
+ FALSE);
+ if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+ {
+ glColor4f(1.f, 0.f, 0.f, 0.1f);
+ }
+ else
+ {
+ glColor4f(1.f, 1.f, 0.f, 0.1f);
+ }
+ gl_rect_2d(
+ llmin(mDragStartX, mDragEndX),
+ llmax(mDragStartY, mDragEndY),
+ llmax(mDragStartX, mDragEndX),
+ llmin(mDragStartY, mDragEndY));
+ }
+}
+
+// true if x,y outside small box around start_x,start_y
+BOOL LLToolSelectRect::outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -SLOP_RADIUS || SLOP_RADIUS <= dx || dy <= -SLOP_RADIUS || SLOP_RADIUS <= dy);
+}
+
+
+//
+// Static functions
+//
diff --git a/indra/newview/lltoolselectrect.h b/indra/newview/lltoolselectrect.h
new file mode 100644
index 0000000000..fb6355460e
--- /dev/null
+++ b/indra/newview/lltoolselectrect.h
@@ -0,0 +1,44 @@
+/**
+ * @file lltoolselectrect.h
+ * @brief A tool to select multiple objects with a screen-space rectangle.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLSELECTRECT_H
+#define LL_LLTOOLSELECTRECT_H
+
+#include "lltool.h"
+#include "lltoolselect.h"
+
+class LLToolSelectRect
+: public LLToolSelect
+{
+public:
+ LLToolSelectRect( LLToolComposite* composite );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual void draw(); // draw the select rectangle
+
+protected:
+ void handleRectangleSelection(S32 x, S32 y, MASK mask); // true if you selected one
+ BOOL outsideSlop(S32 x, S32 y, S32 start_x, S32 start_y);
+
+protected:
+ S32 mDragStartX; // screen coords, from left
+ S32 mDragStartY; // screen coords, from bottom
+
+ S32 mDragEndX;
+ S32 mDragEndY;
+
+ S32 mDragLastWidth;
+ S32 mDragLastHeight;
+
+ BOOL mMouseOutsideSlop; // has mouse ever gone outside slop region?
+};
+
+
+#endif
diff --git a/indra/newview/lltoolview.cpp b/indra/newview/lltoolview.cpp
new file mode 100644
index 0000000000..49e5896ab7
--- /dev/null
+++ b/indra/newview/lltoolview.cpp
@@ -0,0 +1,175 @@
+/**
+ * @file lltoolview.cpp
+ * @brief A UI contains for tool palette tools
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lltoolview.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+
+#include "llagent.h"
+#include "llbutton.h"
+#include "llpanel.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "lltextbox.h"
+#include "llresmgr.h"
+
+
+LLToolContainer::LLToolContainer(LLToolView* parent)
+: mParent(parent),
+ mButton(NULL),
+ mPanel(NULL),
+ mTool(NULL)
+{ }
+
+
+LLToolContainer::~LLToolContainer()
+{
+ // mParent is a pointer to the tool view
+ // mButton is owned by the tool view
+ // mPanel is owned by the tool view
+ delete mTool;
+ mTool = NULL;
+}
+
+
+LLToolView::LLToolView(const std::string& name, const LLRect& rect)
+: LLView(name, rect, MOUSE_OPAQUE),
+ mButtonCount(0)
+{
+}
+
+
+LLToolView::~LLToolView()
+{
+ mContainList.deleteAllData();
+}
+
+//XUI: translate
+void LLToolView::addTool(const LLString& icon_off, const LLString& icon_on, LLPanel* panel, LLTool* tool, LLView* hoverView, const char* label)
+{
+ llassert(tool);
+
+ LLToolContainer* contain = new LLToolContainer(this);
+
+ LLRect btn_rect = getButtonRect(mButtonCount);
+
+ contain->mButton = new LLButton("ToolBtn",
+ btn_rect,
+ icon_off,
+ icon_on,
+ "",
+ &LLToolView::onClickToolButton,
+ contain,
+ LLFontGL::sSansSerif);
+
+ contain->mPanel = panel;
+ contain->mTool = tool;
+
+ addChild(contain->mButton);
+ mButtonCount++;
+
+ const S32 LABEL_TOP_SPACING = 0;
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+ S32 label_width = font->getWidth( label );
+ LLRect label_rect;
+ label_rect.setLeftTopAndSize(
+ btn_rect.mLeft + btn_rect.getWidth() / 2 - label_width / 2,
+ btn_rect.mBottom - LABEL_TOP_SPACING,
+ label_width,
+ llfloor(font->getLineHeight()));
+ addChild( new LLTextBox( "tool label", label_rect, label, font ) );
+
+ // Can optionally ignore panel
+ if (contain->mPanel)
+ {
+ contain->mPanel->setBackgroundVisible( FALSE );
+ contain->mPanel->setBorderVisible( FALSE );
+ addChild(contain->mPanel);
+ }
+
+ mContainList.addData(contain);
+}
+
+
+LLRect LLToolView::getButtonRect(S32 button_index)
+{
+ const S32 HPAD = 7;
+ const S32 VPAD = 7;
+ const S32 TOOL_SIZE = 32;
+ const S32 HORIZ_SPACING = TOOL_SIZE + 5;
+ const S32 VERT_SPACING = TOOL_SIZE + 14;
+
+ S32 tools_per_row = mRect.getWidth() / HORIZ_SPACING;
+
+ S32 row = button_index / tools_per_row;
+ S32 column = button_index % tools_per_row;
+
+ // Build the rectangle, recalling the origin is at lower left
+ // and we want the icons to build down from the top.
+ LLRect rect;
+ rect.setLeftTopAndSize( HPAD + (column * HORIZ_SPACING),
+ -VPAD + getRect().getHeight() - (row * VERT_SPACING),
+ TOOL_SIZE,
+ TOOL_SIZE
+ );
+
+ return rect;
+}
+
+
+void LLToolView::draw()
+{
+ // turn off highlighting for all containers
+ // and hide all option panels except for the selected one.
+ LLTool* selected = gCurrentToolset->getSelectedTool();
+ for( LLToolContainer* contain = mContainList.getFirstData();
+ contain != NULL;
+ contain = mContainList.getNextData()
+ )
+ {
+ BOOL state = (contain->mTool == selected);
+ contain->mButton->setToggleState( state );
+ if (contain->mPanel)
+ {
+ contain->mPanel->setVisible( state );
+ }
+ }
+
+ // Draw children normally
+ LLView::draw();
+}
+
+// protected
+LLToolContainer* LLToolView::findToolContainer( LLTool *tool )
+{
+ // Find the container for this tool
+ llassert( tool );
+ for( LLToolContainer* contain = mContainList.getFirstData(); contain; contain = mContainList.getNextData() )
+ {
+ if( contain->mTool == tool )
+ {
+ return contain;
+ }
+ }
+ llerrs << "LLToolView::findToolContainer - tool not found" << llendl;
+ return NULL;
+}
+
+// static
+void LLToolView::onClickToolButton(void* userdata)
+{
+ LLToolContainer* clicked = (LLToolContainer*) userdata;
+
+ // Switch to this one
+ gCurrentToolset->selectTool( clicked->mTool );
+ gToolMgr->useSelectedTool( gCurrentToolset );
+}
+
diff --git a/indra/newview/lltoolview.h b/indra/newview/lltoolview.h
new file mode 100644
index 0000000000..7c7cb581dd
--- /dev/null
+++ b/indra/newview/lltoolview.h
@@ -0,0 +1,69 @@
+/**
+ * @file lltoolview.h
+ * @brief UI container for tools.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLTOOLVIEW_H
+#define LL_LLTOOLVIEW_H
+
+// requires stdtypes.h
+#include "linked_lists.h"
+#include "llpanel.h"
+
+// forward declares
+class LLTool;
+class LLButton;
+
+class LLToolView;
+
+
+// Utility class, container for the package of information we need about
+// each tool. The package can either point directly to a tool, or indirectly
+// to another view of tools.
+class LLToolContainer
+{
+public:
+ LLToolContainer(LLToolView* parent);
+ ~LLToolContainer();
+
+public:
+ LLToolView* mParent; // toolview that owns this container
+ LLButton* mButton;
+ LLPanel* mPanel;
+ LLTool* mTool; // if not NULL, this is a tool ref
+};
+
+
+// A view containing automatically arranged button icons representing
+// tools. The icons sit on top of panels containing options for each
+// tool.
+class LLToolView
+: public LLView
+{
+public:
+ LLToolView(const std::string& name, const LLRect& rect);
+ ~LLToolView();
+
+ virtual void draw(); // handle juggling tool button highlights, panel visibility
+
+ static void onClickToolButton(void* container);
+
+ void addTool(const LLString& icon_off, const LLString& icon_on, LLPanel* panel, LLTool* tool, LLView* hoverView, const char* label);
+
+ LLView* getCurrentHoverView();
+
+private:
+ LLRect getButtonRect(S32 button_index); // return rect for button to add, zero-based index
+ LLToolContainer *findToolContainer(LLTool *tool);
+
+
+private:
+ LLLinkedList
+ <LLToolContainer> mContainList;
+ S32 mButtonCount; // used to compute rectangles
+};
+
+#endif
diff --git a/indra/newview/lltracker.cpp b/indra/newview/lltracker.cpp
new file mode 100644
index 0000000000..10641df4c3
--- /dev/null
+++ b/indra/newview/lltracker.cpp
@@ -0,0 +1,801 @@
+/**
+ * @file lltracker.cpp
+ * @brief Container for objects user is tracking.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// library includes
+#include "llcoord.h"
+#include "lldarray.h"
+#include "llfontgl.h"
+#include "llgl.h"
+#include "llinventory.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+
+// viewer includes
+#include "viewer.h"
+#include "lltracker.h"
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llcylinder.h"
+#include "llfloaterworldmap.h"
+#include "llhudtext.h"
+#include "llhudview.h"
+#include "llinventorymodel.h"
+#include "lllandmarklist.h"
+#include "llsky.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llworld.h"
+#include "llworldmapview.h"
+
+const F32 DESTINATION_REACHED_RADIUS = 3.0f;
+const F32 DESTINATION_VISITED_RADIUS = 6.0f;
+
+// this last one is useful for when the landmark is
+// very close to agent when tracking is turned on
+const F32 DESTINATION_UNVISITED_RADIUS = 12.0f;
+
+const S32 ARROW_OFF_RADIUS_SQRD = 100;
+
+const S32 HUD_ARROW_SIZE = 32;
+
+// static
+LLTracker *LLTracker::sTrackerp = NULL;
+BOOL LLTracker::sCheesyBeacon = FALSE;
+
+LLTracker::LLTracker()
+: mTrackingStatus(TRACKING_NOTHING),
+ mTrackingLocationType(LOCATION_NOTHING),
+ mHUDArrowCenterX(0),
+ mHUDArrowCenterY(0),
+ mToolTip( "" ),
+ mTrackedLandmarkName(""),
+ mHasReachedLandmark(FALSE),
+ mHasLandmarkPosition(FALSE),
+ mLandmarkHasBeenVisited(FALSE),
+ mTrackedLocationName( "" ),
+ mIsTrackingLocation(FALSE)
+{ }
+
+
+LLTracker::~LLTracker()
+{
+ purgeBeaconText();
+}
+
+
+// static
+void LLTracker::stopTracking(void* userdata)
+{
+ BOOL clear_ui = ((BOOL)(intptr_t)userdata);
+ instance()->stopTrackingAll(clear_ui);
+}
+
+
+// static virtual
+void LLTracker::drawHUDArrow()
+{
+ /* tracking autopilot destination has been disabled
+ -- 2004.01.09, Leviathan
+ // Draw dot for autopilot target
+ if (gAgent.getAutoPilot())
+ {
+ instance()->drawMarker( gAgent.getAutoPilotTargetGlobal(), gTrackColor );
+ return;
+ }
+ */
+ switch (getTrackingStatus())
+ {
+ case TRACKING_AVATAR:
+ // Tracked avatar
+ if(LLAvatarTracker::instance().haveTrackingInfo())
+ {
+ instance()->drawMarker( LLAvatarTracker::instance().getGlobalPos(), gTrackColor );
+ }
+ break;
+
+ case TRACKING_LANDMARK:
+ instance()->drawMarker( getTrackedPositionGlobal(), gTrackColor );
+ break;
+
+ case TRACKING_LOCATION:
+ if (!gWorldp)
+ {
+ break;
+ }
+ // HACK -- try to keep the location just above the terrain
+#if 0
+ // UNHACKED by CRO - keep location where the location is
+ instance()->mTrackedPositionGlobal.mdV[VZ] =
+ 0.9f * instance()->mTrackedPositionGlobal.mdV[VZ]
+ + 0.1f * (gWorldp->resolveLandHeightGlobal(getTrackedPositionGlobal()) + 1.5f);
+#endif
+ instance()->mTrackedPositionGlobal.mdV[VZ] = llclamp((F32)instance()->mTrackedPositionGlobal.mdV[VZ], gWorldp->resolveLandHeightGlobal(getTrackedPositionGlobal()) + 1.5f, (F32)instance()->getTrackedPositionGlobal().mdV[VZ]);
+ instance()->drawMarker( getTrackedPositionGlobal(), gTrackColor );
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+// static
+void LLTracker::render3D()
+{
+ if (!gFloaterWorldMap)
+ {
+ return;
+ }
+
+ // Arbitary location beacon
+ if( instance()->mIsTrackingLocation )
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ LLVector3d pos_global = instance()->mTrackedPositionGlobal;
+ // (z-attenuation < 1) means compute "shorter" distance in z-axis,
+ // so cancel tracking even if avatar is a little above or below.
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(pos_global, 0.5f);
+ if (dist < DESTINATION_REACHED_RADIUS)
+ {
+ instance()->stopTrackingLocation();
+ }
+ else
+ {
+ renderBeacon( instance()->mTrackedPositionGlobal, gTrackColor,
+ instance()->mBeaconText, instance()->mTrackedLocationName );
+ }
+ }
+
+ // Landmark beacon
+ else if( !instance()->mTrackedLandmarkAssetID.isNull() )
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ if (instance()->mHasLandmarkPosition)
+ {
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(instance()->mTrackedPositionGlobal, 1.0f);
+
+ if ( !instance()->mLandmarkHasBeenVisited
+ && dist < DESTINATION_VISITED_RADIUS )
+ {
+ // its close enough ==> flag as visited
+ instance()->setLandmarkVisited();
+ }
+
+ if ( !instance()->mHasReachedLandmark
+ && dist < DESTINATION_REACHED_RADIUS )
+ {
+ // its VERY CLOSE ==> automatically stop tracking
+ instance()->stopTrackingLandmark();
+ }
+ else
+ {
+ if ( instance()->mHasReachedLandmark
+ && dist > DESTINATION_UNVISITED_RADIUS )
+ {
+ // this is so that landmark beacons don't immediately
+ // disappear when they're created only a few meters
+ // away, yet disappear when the agent wanders away
+ // and back again
+ instance()->mHasReachedLandmark = FALSE;
+ }
+ renderBeacon( instance()->mTrackedPositionGlobal, gTrackColor,
+ instance()->mBeaconText, instance()->mTrackedLandmarkName );
+ }
+ }
+ else
+ {
+ // probably just finished downloading the asset
+ instance()->cacheLandmarkPosition();
+ }
+ }
+ else
+ {
+ // Avatar beacon
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if(av_tracker.haveTrackingInfo())
+ {
+ if (!instance()->mBeaconText)
+ {
+ instance()->mBeaconText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ instance()->mBeaconText->setDoFade(FALSE);
+ }
+
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(instance()->mTrackedPositionGlobal, 0.0f);
+ if (dist < DESTINATION_REACHED_RADIUS)
+ {
+ instance()->stopTrackingAvatar();
+ }
+ else
+ {
+ renderBeacon( av_tracker.getGlobalPos(), gTrackColor,
+ instance()->mBeaconText, av_tracker.getName() );
+ }
+ }
+ else
+ {
+ BOOL stop_tracking = FALSE;
+ const LLUUID& avatar_id = av_tracker.getAvatarID();
+ if(avatar_id.isNull())
+ {
+ stop_tracking = TRUE;
+ }
+ else
+ {
+ const LLRelationship* buddy = av_tracker.getBuddyInfo(avatar_id);
+ if(buddy && !buddy->isOnline())
+ {
+ stop_tracking = TRUE;
+ }
+ if(!buddy && !gAgent.isGodlike())
+ {
+ stop_tracking = TRUE;
+ }
+ }
+ if(stop_tracking)
+ {
+ instance()->stopTrackingAvatar();
+ }
+ }
+ }
+}
+
+
+// static
+void LLTracker::trackAvatar( const LLUUID& avatar_id, const LLString& name )
+{
+ instance()->stopTrackingLandmark();
+ instance()->stopTrackingLocation();
+
+ LLAvatarTracker::instance().track( avatar_id, name );
+ instance()->mTrackingStatus = TRACKING_AVATAR;
+ instance()->mLabel = name;
+}
+
+
+// static
+void LLTracker::trackLandmark( const LLUUID& asset_id, const LLUUID& item_id, const LLString& name)
+{
+ instance()->stopTrackingAvatar();
+ instance()->stopTrackingLocation();
+
+ instance()->mTrackedLandmarkAssetID = asset_id;
+ instance()->mTrackedLandmarkItemID = item_id;
+ instance()->mTrackedLandmarkName = name;
+ instance()->cacheLandmarkPosition();
+ instance()->mTrackingStatus = TRACKING_LANDMARK;
+ instance()->mLabel = name;
+}
+
+
+// static
+void LLTracker::trackLocation(const LLVector3d& pos_global, const LLString& full_name, const LLString& tooltip, ETrackingLocationType location_type)
+{
+ instance()->stopTrackingAvatar();
+ instance()->stopTrackingLandmark();
+
+ instance()->mTrackedPositionGlobal = pos_global;
+ instance()->mTrackedLocationName = full_name;
+ instance()->mIsTrackingLocation = TRUE;
+ instance()->mTrackingStatus = TRACKING_LOCATION;
+ instance()->mTrackingLocationType = location_type;
+ instance()->mLabel = full_name;
+ instance()->mToolTip = tooltip;
+}
+
+
+// static
+BOOL LLTracker::handleMouseDown(S32 x, S32 y)
+{
+ BOOL eat_mouse_click = FALSE;
+ // fortunately, we can always compute the tracking arrow center
+ S32 dist_sqrd = (x - instance()->mHUDArrowCenterX) * (x - instance()->mHUDArrowCenterX) +
+ (y - instance()->mHUDArrowCenterY) * (y - instance()->mHUDArrowCenterY);
+ if (dist_sqrd < ARROW_OFF_RADIUS_SQRD)
+ {
+ /* tracking autopilot destination has been disabled
+ -- 2004.01.09, Leviathan
+ // turn off tracking
+ if (gAgent.getAutoPilot())
+ {
+ gAgent.stopAutoPilot(TRUE); // TRUE because cancelled by user
+ eat_mouse_click = TRUE;
+ }
+ */
+ if (getTrackingStatus())
+ {
+ instance()->stopTrackingAll();
+ eat_mouse_click = TRUE;
+ }
+ }
+ return eat_mouse_click;
+}
+
+
+// static
+LLVector3d LLTracker::getTrackedPositionGlobal()
+{
+ LLVector3d pos_global;
+ switch (getTrackingStatus())
+ {
+ case TRACKING_AVATAR:
+ {
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if (av_tracker.haveTrackingInfo())
+ {
+ pos_global = av_tracker.getGlobalPos(); }
+ break;
+ }
+ case TRACKING_LANDMARK:
+ if( instance()->mHasLandmarkPosition )
+ {
+ pos_global = instance()->mTrackedPositionGlobal;
+ }
+ break;
+ case TRACKING_LOCATION:
+ pos_global = instance()->mTrackedPositionGlobal;
+ break;
+ default:
+ break;
+ }
+ return pos_global;
+}
+
+
+// static
+BOOL LLTracker::hasLandmarkPosition()
+{
+ if (!instance()->mHasLandmarkPosition)
+ {
+ // maybe we just received the landmark position info
+ instance()->cacheLandmarkPosition();
+ }
+ return instance()->mHasLandmarkPosition;
+}
+
+
+// static
+const LLString& LLTracker::getTrackedLocationName()
+{
+ return instance()->mTrackedLocationName;
+}
+
+F32 pulse_func(F32 t, F32 z)
+{
+ if (!LLTracker::sCheesyBeacon)
+ {
+ return 0.f;
+ }
+
+ t *= 3.14159f;
+ z -= t*64.f - 256.f;
+
+ F32 a = cosf(z*3.14159/512.f)*10.0f;
+ a = llmax(a, 9.9f);
+ a -= 9.9f;
+ a *= 10.f;
+ return a;
+}
+
+void draw_shockwave(F32 center_z, F32 t, S32 steps, LLColor4 color)
+{
+ if (!LLTracker::sCheesyBeacon)
+ {
+ return;
+ }
+
+ t *= 0.6284f/3.14159f;
+
+ t -= (F32) (S32) t;
+
+ t = llmax(t, 0.5f);
+ t -= 0.5f;
+ t *= 2.0f;
+
+ F32 radius = t*16536.f;
+
+ // Inexact, but reasonably fast.
+ F32 delta = F_TWO_PI / steps;
+ F32 sin_delta = sin( delta );
+ F32 cos_delta = cos( delta );
+ F32 x = radius;
+ F32 y = 0.f;
+
+ LLColor4 ccol = LLColor4(1,1,1,(1.f-t)*0.25f);
+ glBegin(GL_TRIANGLE_FAN);
+ glColor4fv(ccol.mV);
+ glVertex3f(0.f, 0.f, center_z);
+ // make sure circle is complete
+ steps += 1;
+
+ color.mV[3] = (1.f-t*t);
+
+ glColor4fv(color.mV);
+ while( steps-- )
+ {
+ // Successive rotations
+ glVertex3f( x, y, center_z );
+ F32 x_new = x * cos_delta - y * sin_delta;
+ y = x * sin_delta + y * cos_delta;
+ x = x_new;
+ }
+ glEnd();
+}
+
+
+// static
+void LLTracker::renderBeacon(LLVector3d pos_global,
+ const LLColor4& color,
+ LLHUDText* hud_textp,
+ const std::string& label )
+{
+ sCheesyBeacon = gSavedSettings.getBOOL("CheesyBeacon");
+ LLVector3d to_vec = pos_global - gAgent.getCameraPositionGlobal();
+
+ F32 dist = (F32)to_vec.magVec();
+ F32 color_frac = 1.f;
+ if (dist > 0.99f * gCamera->getFar())
+ {
+ color_frac = 0.4f;
+ // pos_global = gAgent.getCameraPositionGlobal() + 0.99f*(gCamera->getFar()/dist)*to_vec;
+ }
+ else
+ {
+ color_frac = 1.f - 0.6f*(dist/gCamera->getFar());
+ }
+
+ LLColor4 fogged_color = color_frac * color + (1 - color_frac)*gSky.getFogColor();
+
+ F32 FADE_DIST = 3.f;
+ fogged_color.mV[3] = llmax(0.2f, llmin(0.5f,(dist-FADE_DIST)/FADE_DIST));
+
+ LLVector3 pos_agent = gAgent.getPosAgentFromGlobal(pos_global);
+
+ LLGLSTracker gls_tracker; // default - TEXTURE + CULL_FACE + LIGHTING + GL_BLEND + GL_ALPHA_TEST
+ LLGLDisable cull_face(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef(pos_agent.mV[0], pos_agent.mV[1], pos_agent.mV[2]);
+
+ draw_shockwave(1024.f, gRenderStartTime.getElapsedTimeF32(), 32, fogged_color);
+
+ //glScalef(1.f, 1.f, 1000.f);
+ glColor4fv(fogged_color.mV);
+ const U32 BEACON_VERTS = 256;
+ const F32 step = 1024.0f/BEACON_VERTS;
+
+ LLVector3 x_axis = gCamera->getLeftAxis();
+ F32 t = gRenderStartTime.getElapsedTimeF32();
+ F32 dr = dist/gCamera->getFar();
+
+ for (U32 i = 0; i < BEACON_VERTS; i++)
+ {
+ F32 x = x_axis.mV[0];
+ F32 y = x_axis.mV[1];
+
+ F32 z = i * step;
+ F32 z_next = (i+1)*step;
+
+ F32 a = pulse_func(t, z);
+ F32 an = pulse_func(t, z_next);
+
+ LLColor4 c_col = fogged_color + LLColor4(a,a,a,a);
+ LLColor4 col_next = fogged_color + LLColor4(an,an,an,an);
+ LLColor4 col_edge = fogged_color * LLColor4(a,a,a,0.0f);
+ LLColor4 col_edge_next = fogged_color * LLColor4(an,an,an,0.0f);
+
+ a *= 2.f;
+ a += 1.0f+dr;
+
+ an *= 2.f;
+ an += 1.0f+dr;
+
+ glBegin(GL_TRIANGLE_STRIP);
+ glColor4fv(col_edge.mV);
+ glVertex3f(-x*a, -y*a, z);
+ glColor4fv(col_edge_next.mV);
+ glVertex3f(-x*an, -y*an, z_next);
+
+ glColor4fv(c_col.mV);
+ glVertex3f(0, 0, z);
+ glColor4fv(col_next.mV);
+ glVertex3f(0, 0, z_next);
+
+ glColor4fv(col_edge.mV);
+ glVertex3f(x*a,y*a,z);
+ glColor4fv(col_edge_next.mV);
+ glVertex3f(x*an,y*an,z_next);
+
+ glEnd();
+ }
+
+ //gCylinder.render(1000);
+ glPopMatrix();
+
+ char text[1024];
+ sprintf(text, "%.0f m", to_vec.magVec());
+
+ LLWString wstr;
+ wstr += utf8str_to_wstring(label);
+ wstr += '\n';
+ wstr += utf8str_to_wstring(text);
+
+ hud_textp->setFont(LLFontGL::sSansSerif);
+ hud_textp->setZCompare(FALSE);
+ hud_textp->setColor(LLColor4(1.f, 1.f, 1.f, llmax(0.2f, llmin(1.f,(dist-FADE_DIST)/FADE_DIST))));
+
+ hud_textp->setString(wstr);
+ hud_textp->setVertAlignment(LLHUDText::ALIGN_VERT_CENTER);
+ hud_textp->setPositionAgent(pos_agent);
+}
+
+
+void LLTracker::stopTrackingAll(BOOL clear_ui)
+{
+ switch (mTrackingStatus)
+ {
+ case TRACKING_AVATAR :
+ stopTrackingAvatar(clear_ui);
+ break;
+ case TRACKING_LANDMARK :
+ stopTrackingLandmark(clear_ui);
+ break;
+ case TRACKING_LOCATION :
+ stopTrackingLocation(clear_ui);
+ break;
+ default:
+ mTrackingStatus = TRACKING_NOTHING;
+ break;
+ }
+}
+
+
+void LLTracker::stopTrackingAvatar(BOOL clear_ui)
+{
+ LLAvatarTracker& av_tracker = LLAvatarTracker::instance();
+ if( !av_tracker.getAvatarID().isNull() )
+ {
+ av_tracker.untrack( av_tracker.getAvatarID() );
+ }
+
+ purgeBeaconText();
+ gFloaterWorldMap->clearAvatarSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+}
+
+
+void LLTracker::stopTrackingLandmark(BOOL clear_ui)
+{
+ purgeBeaconText();
+ mTrackedLandmarkAssetID.setNull();
+ mTrackedLandmarkItemID.setNull();
+ mTrackedLandmarkName.assign("");
+ mTrackedPositionGlobal.zeroVec();
+ mHasLandmarkPosition = FALSE;
+ mHasReachedLandmark = FALSE;
+ mLandmarkHasBeenVisited = TRUE;
+ gFloaterWorldMap->clearLandmarkSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+}
+
+
+void LLTracker::stopTrackingLocation(BOOL clear_ui)
+{
+ purgeBeaconText();
+ mTrackedLocationName.assign("");
+ mIsTrackingLocation = FALSE;
+ mTrackedPositionGlobal.zeroVec();
+ gFloaterWorldMap->clearLocationSelection(clear_ui);
+ mTrackingStatus = TRACKING_NOTHING;
+ mTrackingLocationType = LOCATION_NOTHING;
+}
+
+
+void LLTracker::drawMarker(const LLVector3d& pos_global, const LLColor4& color)
+{
+ if (!gCamera)
+ {
+ return;
+ }
+
+ // get position
+ LLVector3 pos_local = gAgent.getPosAgentFromGlobal(pos_global);
+
+ // check in frustum
+ LLCoordGL screen;
+ S32 x = 0;
+ S32 y = 0;
+ const BOOL CLAMP = TRUE;
+
+ if (gCamera->projectPosAgentToScreen(pos_local, screen, CLAMP)
+ || gCamera->projectPosAgentToScreenEdge(pos_local, screen) )
+ {
+ gHUDView->screenPointToLocal(screen.mX, screen.mY, &x, &y);
+
+ // the center of the rendered position of the arrow obeys
+ // the following rules:
+ // (1) it lies on an ellipse centered on the target position
+ // (2) it lies on the line between the target and the window center
+ // (3) right now the radii of the ellipse are fixed, but eventually
+ // they will be a function of the target text
+ //
+ // from those rules we can compute the position of the
+ // lower left corner of the image
+ LLRect rect = gHUDView->getRect();
+ S32 x_center = lltrunc(0.5f * (F32)rect.getWidth());
+ S32 y_center = lltrunc(0.5f * (F32)rect.getHeight());
+ x = x - x_center; // x and y relative to center
+ y = y - y_center;
+ F32 dist = sqrt((F32)(x*x + y*y));
+ S32 half_arrow_size = lltrunc(0.5f * HUD_ARROW_SIZE);
+ if (dist > 0.f)
+ {
+ const F32 ARROW_ELLIPSE_RADIUS_X = 2 * HUD_ARROW_SIZE;
+ const F32 ARROW_ELLIPSE_RADIUS_Y = HUD_ARROW_SIZE;
+
+ // compute where the arrow should be
+ F32 x_target = (F32)(x + x_center) - (ARROW_ELLIPSE_RADIUS_X * ((F32)x / dist) );
+ F32 y_target = (F32)(y + y_center) - (ARROW_ELLIPSE_RADIUS_Y * ((F32)y / dist) );
+
+ // keep the arrow within the window
+ F32 x_clamped = llclamp( x_target, (F32)half_arrow_size, (F32)(rect.getWidth() - half_arrow_size));
+ F32 y_clamped = llclamp( y_target, (F32)half_arrow_size, (F32)(rect.getHeight() - half_arrow_size));
+
+ F32 slope = (F32)(y) / (F32)(x);
+ F32 window_ratio = (F32)(rect.getHeight() - HUD_ARROW_SIZE) / (F32)(rect.getWidth() - HUD_ARROW_SIZE);
+
+ // if the arrow has been clamped on one axis
+ // then we need to compute the other axis
+ if (llabs(slope) > window_ratio)
+ {
+ if (y_clamped != (F32)y_target)
+ {
+ // clamp by y
+ x_clamped = (y_clamped - (F32)y_center) / slope + (F32)x_center;
+ }
+ }
+ else if (x_clamped != (F32)x_target)
+ {
+ // clamp by x
+ y_clamped = (x_clamped - (F32)x_center) * slope + (F32)y_center;
+ }
+ mHUDArrowCenterX = lltrunc(x_clamped);
+ mHUDArrowCenterY = lltrunc(y_clamped);
+ }
+ else
+ {
+ // recycle the old values
+ x = mHUDArrowCenterX - x_center;
+ y = mHUDArrowCenterY - y_center;
+ }
+
+ F32 angle = atan2( (F32)y, (F32)x );
+
+ gl_draw_scaled_rotated_image(mHUDArrowCenterX - half_arrow_size,
+ mHUDArrowCenterY - half_arrow_size,
+ HUD_ARROW_SIZE, HUD_ARROW_SIZE,
+ RAD_TO_DEG * angle,
+ LLWorldMapView::sTrackArrowImage,
+ color);
+ }
+}
+
+
+void LLTracker::setLandmarkVisited()
+{
+ // poke the inventory item
+ if (!mTrackedLandmarkItemID.isNull())
+ {
+ LLInventoryItem* i = gInventory.getItem( mTrackedLandmarkItemID );
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)i;
+ if ( item
+ && !(item->getFlags()&LLInventoryItem::II_FLAGS_LANDMARK_VISITED))
+ {
+ U32 flags = item->getFlags();
+ flags |= LLInventoryItem::II_FLAGS_LANDMARK_VISITED;
+ item->setFlags(flags);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ChangeInventoryItemFlags");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("ItemID", mTrackedLandmarkItemID);
+ msg->addU32("Flags", flags);
+ gAgent.sendReliableMessage();
+
+ LLInventoryModel::LLCategoryUpdate up(item->getParentUUID(), 0);
+ gInventory.accountForUpdate(up);
+
+ // need to communicate that the icon needs to change...
+ gInventory.addChangedMask(LLInventoryObserver::INTERNAL, item->getUUID());
+ gInventory.notifyObservers();
+ }
+ }
+}
+
+
+void LLTracker::cacheLandmarkPosition()
+{
+ // the landmark asset download may have finished, in which case
+ // we'll now be able to figure out where we're trying to go
+ BOOL found_landmark = FALSE;
+ if( mTrackedLandmarkAssetID == LLFloaterWorldMap::getHomeID())
+ {
+ LLVector3d pos_global;
+ if ( gAgent.getHomePosGlobal( &mTrackedPositionGlobal ))
+ {
+ found_landmark = TRUE;
+ }
+ else
+ {
+ llwarns << "LLTracker couldn't find home pos" << llendl;
+ mTrackedLandmarkAssetID.setNull();
+ mTrackedLandmarkItemID.setNull();
+ }
+ }
+ else
+ {
+ LLLandmark* landmark = gLandmarkList.getAsset(mTrackedLandmarkAssetID);
+ if(landmark && landmark->getGlobalPos(mTrackedPositionGlobal))
+ {
+ found_landmark = TRUE;
+
+ // cache the object's visitation status
+ mLandmarkHasBeenVisited = FALSE;
+ LLInventoryItem* item = gInventory.getItem(mTrackedLandmarkItemID);
+ if ( item
+ && item->getFlags()&LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ mLandmarkHasBeenVisited = TRUE;
+ }
+ }
+ }
+ if ( found_landmark && gFloaterWorldMap )
+ {
+ mHasReachedLandmark = FALSE;
+ F32 dist = gFloaterWorldMap->getDistanceToDestination(mTrackedPositionGlobal, 1.0f);
+ if ( dist < DESTINATION_UNVISITED_RADIUS )
+ {
+ mHasReachedLandmark = TRUE;
+ }
+ mHasLandmarkPosition = TRUE;
+ }
+ mHasLandmarkPosition = found_landmark;
+}
+
+
+void LLTracker::purgeBeaconText()
+{
+ if(!mBeaconText.isNull())
+ {
+ mBeaconText->markDead();
+ mBeaconText = NULL;
+ }
+}
+
diff --git a/indra/newview/lltracker.h b/indra/newview/lltracker.h
new file mode 100644
index 0000000000..ea32b48b8e
--- /dev/null
+++ b/indra/newview/lltracker.h
@@ -0,0 +1,135 @@
+/**
+ * @file lltracker.h
+ * @brief Container for objects user is tracking.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A singleton class for tracking stuff.
+//
+// TODO -- LLAvatarTracker functionality should probably be moved
+// to the LLTracker class.
+
+
+#ifndef LL_LLTRACKER_H
+#define LL_LLTRACKER_H
+
+#include "lldarray.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "lluuid.h"
+#include "v3dmath.h"
+
+class LLHUDText;
+
+
+class LLTracker
+{
+public:
+ enum ETrackingStatus
+ {
+ TRACKING_NOTHING = 0,
+ TRACKING_AVATAR = 1,
+ TRACKING_LANDMARK = 2,
+ TRACKING_LOCATION = 3,
+ };
+
+ enum ETrackingLocationType
+ {
+ LOCATION_NOTHING,
+ LOCATION_EVENT,
+ LOCATION_ITEM,
+ };
+
+ static LLTracker* instance()
+ {
+ if (!sTrackerp)
+ {
+ sTrackerp = new LLTracker();
+ }
+ return sTrackerp;
+ }
+
+ static void cleanupInstance() { delete sTrackerp; sTrackerp = NULL; }
+
+ //static void drawTrackerArrow();
+ // these are static so that they can be used a callbacks
+ static ETrackingStatus getTrackingStatus() { return instance()->mTrackingStatus; }
+ static ETrackingLocationType getTrackedLocationType() { return instance()->mTrackingLocationType; }
+ static BOOL isTracking(void*) { return (BOOL) instance()->mTrackingStatus; }
+ static void stopTracking(void*);
+
+ static const LLUUID& getTrackedLandmarkAssetID() { return instance()->mTrackedLandmarkAssetID; }
+ static const LLUUID& getTrackedLandmarkItemID() { return instance()->mTrackedLandmarkItemID; }
+
+ static void trackAvatar( const LLUUID& avatar_id, const LLString& name );
+ static void trackLandmark( const LLUUID& landmark_asset_id, const LLUUID& landmark_item_id , const LLString& name);
+ static void trackLocation(const LLVector3d& pos, const LLString& full_name, const LLString& tooltip, ETrackingLocationType location_type = LOCATION_NOTHING);
+
+ // returns global pos of tracked thing
+ static LLVector3d getTrackedPositionGlobal();
+
+ static BOOL hasLandmarkPosition();
+ static const LLString& getTrackedLocationName();
+
+ static void drawHUDArrow();
+
+ // Draw in-world 3D tracking stuff
+ static void render3D();
+
+ static BOOL handleMouseDown(S32 x, S32 y);
+
+ static LLTracker* sTrackerp;
+ static BOOL sCheesyBeacon;
+
+ static const LLString& getLabel() { return instance()->mLabel; }
+ static const LLString& getToolTip() { return instance()->mToolTip; }
+protected:
+ LLTracker();
+ ~LLTracker();
+
+ static void renderBeacon( LLVector3d pos_global,
+ const LLColor4& color,
+ LLHUDText* hud_textp,
+ const std::string& label );
+
+ void stopTrackingAll(BOOL clear_ui = FALSE);
+ void stopTrackingAvatar(BOOL clear_ui = FALSE);
+ void stopTrackingLocation(BOOL clear_ui = FALSE);
+ void stopTrackingLandmark(BOOL clear_ui = FALSE);
+
+ void drawMarker(const LLVector3d& pos_global, const LLColor4& color);
+ void setLandmarkVisited();
+ void cacheLandmarkPosition();
+ void purgeBeaconText();
+
+protected:
+ ETrackingStatus mTrackingStatus;
+ ETrackingLocationType mTrackingLocationType;
+ LLPointer<LLHUDText> mBeaconText;
+ S32 mHUDArrowCenterX;
+ S32 mHUDArrowCenterY;
+
+ LLVector3d mTrackedPositionGlobal;
+
+ LLString mLabel;
+ LLString mToolTip;
+
+ LLString mTrackedLandmarkName;
+ LLUUID mTrackedLandmarkAssetID;
+ LLUUID mTrackedLandmarkItemID;
+ LLDynamicArray<LLUUID> mLandmarkAssetIDList;
+ LLDynamicArray<LLUUID> mLandmarkItemIDList;
+ BOOL mHasReachedLandmark;
+ BOOL mHasLandmarkPosition;
+ BOOL mLandmarkHasBeenVisited;
+
+ LLString mTrackedLocationName;
+ BOOL mIsTrackingLocation;
+ BOOL mHasReachedLocation;
+};
+
+
+#endif
+
diff --git a/indra/newview/lluiconstants.h b/indra/newview/lluiconstants.h
new file mode 100644
index 0000000000..01663ed233
--- /dev/null
+++ b/indra/newview/lluiconstants.h
@@ -0,0 +1,32 @@
+/**
+ * @file lluiconstants.h
+ * @brief Compile-time configuration for UI
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLUICONSTANTS_H
+#define LL_LLUICONSTANTS_H
+
+// spacing for small font lines of text, like LLTextBoxes
+const S32 LINE = 16;
+
+// spacing for larger lines of text
+const S32 LINE_BIG = 24;
+
+// default vertical padding
+const S32 VPAD = 4;
+
+// default horizontal padding
+const S32 HPAD = 4;
+
+// Account History, how far to look into past
+const S32 SUMMARY_INTERVAL = 7; // one week
+const S32 SUMMARY_MAX = 8; //
+const S32 DETAILS_INTERVAL = 1; // one day
+const S32 DETAILS_MAX = 30; // one month
+const S32 TRANSACTIONS_INTERVAL = 1;// one day
+const S32 TRANSACTIONS_MAX = 30; // one month
+
+#endif
diff --git a/indra/newview/lluploaddialog.cpp b/indra/newview/lluploaddialog.cpp
new file mode 100644
index 0000000000..f91db06ef9
--- /dev/null
+++ b/indra/newview/lluploaddialog.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file lluploaddialog.cpp
+ * @brief LLUploadDialog class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "lluploaddialog.h"
+#include "llviewerwindow.h"
+#include "llfontgl.h"
+#include "llresmgr.h"
+#include "lltextbox.h"
+#include "llbutton.h"
+#include "llkeyboard.h"
+#include "llfocusmgr.h"
+#include "llviewercontrol.h"
+
+// static
+LLUploadDialog* LLUploadDialog::sDialog = NULL;
+
+// static
+LLUploadDialog* LLUploadDialog::modalUploadDialog(const std::string& msg)
+{
+ // Note: object adds, removes, and destroys itself.
+ return new LLUploadDialog(msg);
+}
+
+// static
+void LLUploadDialog::modalUploadFinished()
+{
+ // Note: object adds, removes, and destroys itself.
+ delete LLUploadDialog::sDialog;
+ LLUploadDialog::sDialog = NULL;
+}
+
+////////////////////////////////////////////////////////////
+// Private methods
+
+LLUploadDialog::LLUploadDialog( const std::string& msg)
+ :
+ LLPanel( "Uploading...", LLRect(0,100,100,0) ) // dummy rect. Will reshape below.
+{
+ setBackgroundVisible( TRUE );
+
+ if( LLUploadDialog::sDialog )
+ {
+ delete LLUploadDialog::sDialog;
+ }
+ LLUploadDialog::sDialog = this;
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ LLRect msg_rect;
+ for (int line_num=0; line_num<16; ++line_num)
+ {
+ mLabelBox[line_num] = new LLTextBox( "Filename", msg_rect, "", font );
+ addChild(mLabelBox[line_num]);
+ }
+
+ setMessage(msg);
+
+ // The dialog view is a root view
+ gViewerWindow->setTopView( this, NULL );
+}
+
+void LLUploadDialog::setMessage( const std::string& msg)
+{
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ const S32 VPAD = 16;
+ const S32 HPAD = 25;
+
+ // Make the text boxes a little wider than the text
+ const S32 TEXT_PAD = 8;
+
+ // Split message into lines, separated by '\n'
+ S32 max_msg_width = 0;
+ LLDoubleLinkedList<LLString> msg_lines;
+
+ S32 size = msg.size() + 1;// + strlen("Uploading...\n\n");
+ char* temp_msg = new char[size];
+
+ //strcpy(temp_msg,"Uploading...\n\n");
+ strcpy( temp_msg, msg.c_str());
+
+ char* token = strtok( temp_msg, "\n" );
+ while( token )
+ {
+ S32 cur_width = S32(font->getWidth(token) + 0.99f) + TEXT_PAD;
+ max_msg_width = llmax( max_msg_width, cur_width );
+ msg_lines.addDataAtEnd( new LLString( token ) );
+ token = strtok( NULL, "\n" );
+ }
+ delete[] temp_msg;
+
+
+ S32 line_height = S32( font->getLineHeight() + 0.99f );
+ S32 dialog_width = max_msg_width + 2 * HPAD;
+ S32 dialog_height = line_height * msg_lines.getLength() + 2 * VPAD;
+
+ reshape( dialog_width, dialog_height, FALSE );
+
+ // Message
+ S32 msg_x = (mRect.getWidth() - max_msg_width) / 2;
+ S32 msg_y = mRect.getHeight() - VPAD - line_height;
+ int line_num;
+ for (line_num=0; line_num<16; ++line_num)
+ {
+ mLabelBox[line_num]->setVisible(FALSE);
+ }
+ line_num = 0;
+ for( LLString* cur_line = msg_lines.getFirstData(); cur_line; cur_line = msg_lines.getNextData() )
+ {
+ LLRect msg_rect;
+ msg_rect.setOriginAndSize( msg_x, msg_y, max_msg_width, line_height );
+ mLabelBox[line_num]->setRect(msg_rect);
+ mLabelBox[line_num]->setText(*cur_line);
+ mLabelBox[line_num]->setColor( gColors.getColor( "LabelTextColor" ) );
+ mLabelBox[line_num]->setVisible(TRUE);
+ msg_y -= line_height;
+ ++line_num;
+ }
+ msg_lines.deleteAllData();
+
+ centerDialog();
+}
+
+LLUploadDialog::~LLUploadDialog()
+{
+ gFocusMgr.releaseFocusIfNeeded( this );
+
+// LLFilePicker::instance().reset();
+
+
+ LLUploadDialog::sDialog = NULL;
+}
+
+void LLUploadDialog::centerDialog()
+{
+ LLRect window_rect = gViewerWindow->getRootView()->getRect();
+
+ S32 dialog_left = window_rect.mLeft + (window_rect.getWidth() - mRect.getWidth()) / 2;
+ S32 dialog_bottom = window_rect.mBottom + (window_rect.getHeight() - mRect.getHeight()) / 2;
+
+ translate( dialog_left - mRect.mLeft, dialog_bottom - mRect.mBottom );
+}
+
diff --git a/indra/newview/lluploaddialog.h b/indra/newview/lluploaddialog.h
new file mode 100644
index 0000000000..4af3dce378
--- /dev/null
+++ b/indra/newview/lluploaddialog.h
@@ -0,0 +1,38 @@
+/**
+ * @file lluploaddialog.h
+ * @brief LLUploadDialog class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_UPLOADDIALOG_H
+#define LL_UPLOADDIALOG_H
+
+#include "llpanel.h"
+#include "lltextbox.h"
+
+class LLUploadDialog : public LLPanel
+{
+public:
+ // Use this function to open a modal dialog and display it until the user presses the "close" button.
+ static LLUploadDialog* modalUploadDialog(const std::string& msg); // Message to display
+ static void modalUploadFinished(); // Message to display
+
+ static bool modalUploadIsFinished() { return (sDialog == NULL); }
+
+ void setMessage( const std::string& msg );
+
+private:
+ LLUploadDialog( const std::string& msg);
+ virtual ~LLUploadDialog(); // No you can't kill it. It can only kill itself.
+
+ void centerDialog();
+
+ LLTextBox* mLabelBox[16];
+
+private:
+ static LLUploadDialog* sDialog; // Hidden singleton instance, created and destroyed as needed.
+};
+
+#endif // LL_UPLOADDIALOG_H
diff --git a/indra/newview/llurl.cpp b/indra/newview/llurl.cpp
new file mode 100644
index 0000000000..2cd05bdcbf
--- /dev/null
+++ b/indra/newview/llurl.cpp
@@ -0,0 +1,256 @@
+/**
+ * @file llurl.cpp
+ * @brief Text url class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include <string.h>
+#include "llurl.h"
+#include "llerror.h"
+
+LLURL::LLURL()
+{
+ init("");
+}
+
+LLURL::LLURL(const LLURL &url)
+{
+ if (this != &url)
+ {
+ init(url.getFQURL());
+ }
+ else
+ {
+ init("");
+ }
+}
+
+LLURL::LLURL(const char * url)
+{
+ init(url);
+}
+
+LLURL::~LLURL()
+{
+ free();
+}
+
+void LLURL::init(const char * url)
+{
+ mURI[0] = '\0';
+ mAuthority[0] = '\0';
+ mPath[0] = '\0';
+ mFilename[0] = '\0';
+ mExtension[0] = '\0';
+ mTag[0] = '\0';
+
+ char url_copy[MAX_STRING];
+
+ strcpy (url_copy,url);
+
+ char *parse;
+ char *leftover_url = url_copy;
+ S32 span = 0;
+
+ // copy and lop off tag
+ if ((parse = strchr(url_copy,'#')))
+ {
+ strcpy(mTag,parse+1);
+ *parse = '\0';
+ }
+
+ // copy out URI if it exists, update leftover_url
+ if ((parse = strchr(url_copy,':')))
+ {
+ *parse = '\0';
+ strcpy(mURI,leftover_url);
+ leftover_url = parse + 1;
+ }
+
+ // copy out authority if it exists, update leftover_url
+ if ((leftover_url[0] == '/') && (leftover_url[1] == '/'))
+ {
+ leftover_url += 2; // skip the "//"
+
+ span = strcspn(leftover_url, "/");
+ strncat(mAuthority,leftover_url,span);
+ leftover_url += span;
+ }
+
+ if ((parse = strrchr(leftover_url,'.')))
+ {
+ // copy and lop off extension
+ strcpy(mExtension,parse+1);
+ *parse = '\0';
+ }
+
+ if ((parse = strrchr(leftover_url,'/')))
+ {
+ parse++;
+ }
+ else
+ {
+ parse = leftover_url;
+ }
+
+ // copy and lop off filename
+ strcpy(mFilename,parse);
+ *parse = '\0';
+
+ // what's left should be the path
+ strcpy(mPath,leftover_url);
+
+// llinfos << url << " decomposed into: " << llendl;
+// llinfos << " URI : <" << mURI << ">" << llendl;
+// llinfos << " Auth: <" << mAuthority << ">" << llendl;
+// llinfos << " Path: <" << mPath << ">" << llendl;
+// llinfos << " File: <" << mFilename << ">" << llendl;
+// llinfos << " Ext : <" << mExtension << ">" << llendl;
+// llinfos << " Tag : <" << mTag << ">" << llendl;
+}
+
+void LLURL::free()
+{
+}
+
+// Copy assignment
+LLURL &LLURL::operator=(const LLURL &rhs)
+{
+ if (this != &rhs)
+ {
+ this->init(rhs.getFQURL());
+ }
+
+ return *this;
+}
+
+// Compare
+bool LLURL::operator==(const LLURL &rhs) const
+{
+ if ((strcmp(mURI, rhs.mURI))
+ || (strcmp(mAuthority, rhs.mAuthority))
+ || (strcmp(mPath, rhs.mPath))
+ || (strcmp(mFilename, rhs.mFilename))
+ || (strcmp(mExtension, rhs.mExtension))
+ || (strcmp(mTag, rhs.mTag))
+ )
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool LLURL::operator!=(const LLURL& rhs) const
+{
+ return !(*this == rhs);
+}
+
+const char * LLURL::getFQURL() const
+{
+ char fqurl[LL_MAX_PATH];
+
+ fqurl[0] = '\0';
+
+ if (mURI[0])
+ {
+ strcat(fqurl,mURI);
+ strcat(fqurl,":");
+ if (mAuthority[0])
+ {
+ strcat(fqurl,"//");
+ }
+ }
+
+ if (mAuthority[0])
+ {
+ strcat(fqurl,mAuthority);
+ }
+
+ strcat(fqurl,mPath);
+
+ strcat(fqurl,mFilename);
+
+ if (mExtension[0])
+ {
+ strcat(fqurl,".");
+ strcat(fqurl,mExtension);
+ }
+
+ if (mTag[0])
+ {
+ strcat(fqurl,"#");
+ strcat(fqurl,mTag);
+ }
+
+ strcpy(LLURL::sReturnString,fqurl);
+
+ return(LLURL::sReturnString);
+}
+
+
+const char* LLURL::updateRelativePath(const LLURL &url)
+{
+ char new_path[LL_MAX_PATH];
+ char tmp_path[LL_MAX_PATH];
+
+ char *parse;
+
+ if (mPath[0] != '/')
+ {
+ //start with existing path
+ strcpy (new_path,url.mPath);
+ strcpy (tmp_path,mPath);
+
+ parse = strtok(tmp_path,"/");
+ while (parse)
+ {
+ if (!strcmp(parse,"."))
+ {
+ // skip this, it's meaningless
+ }
+ else if (!strcmp(parse,".."))
+ {
+ if ((parse = strrchr(new_path, '/')))
+ {
+ *parse = '\0';
+ if ((parse = strrchr(new_path, '/')))
+ {
+ *(parse+1) = '\0';
+ }
+ else
+ {
+ new_path[0] = '\0';
+ }
+ }
+ else
+ {
+ strcat(new_path,"../");
+ }
+
+ }
+ else
+ {
+ strcat(new_path,parse);
+ strcat(new_path,"/");
+ }
+ parse = strtok(NULL,"/");
+ }
+ strcpy(mPath,new_path);
+ }
+ return mPath;
+}
+
+const char * LLURL::getFullPath()
+{
+ strcpy(LLURL::sReturnString,mPath);
+ strcat(LLURL::sReturnString,mFilename);
+ strcat(LLURL::sReturnString,".");
+ strcat(LLURL::sReturnString,mExtension);
+ return(sReturnString);
+}
+
+
+char LLURL::sReturnString[LL_MAX_PATH] = "";
diff --git a/indra/newview/llurl.h b/indra/newview/llurl.h
new file mode 100644
index 0000000000..1e13aa94b8
--- /dev/null
+++ b/indra/newview/llurl.h
@@ -0,0 +1,76 @@
+/**
+ * @file llurl.h
+ * @brief Text url class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURL_H
+#define LL_LLURL_H
+
+// splits a URL into its parts, which are:
+//
+// [URI][AUTHORITY][PATH][FILENAME][EXTENSION][TAG]
+//
+// e.g. http://www.lindenlab.com/early/bite_me.html#where
+//
+// URI= "http"
+// AUTHORITY= "www.lindenlab.com"
+// PATH= "/early/"
+// FILENAME= "bite_me"
+// EXTENSION= "html"
+// TAG= "where"
+//
+//
+// test cases:
+//
+// http://www.lindenlab.com/early/bite_me.html#where
+// http://www.lindenlab.com/
+// http://www.lindenlab.com
+// www.lindenlab.com ?
+// early/bite_me.html#where
+// mailto://test@lindenlab.com
+// mailto:test@lindenlab.com
+//
+//
+
+
+class LLURL
+{
+public:
+ LLURL();
+ LLURL(const LLURL &url);
+ LLURL(const char * url);
+
+ LLURL &operator=(const LLURL &rhs);
+
+ virtual ~LLURL();
+
+ virtual void init (const char * url);
+ virtual void free ();
+
+ bool operator==(const LLURL &rhs) const;
+ bool operator!=(const LLURL &rhs) const;
+
+ virtual const char *getFQURL() const;
+ virtual const char *getFullPath();
+
+ virtual const char *updateRelativePath(const LLURL &url);
+
+ virtual BOOL isExtension(const char *compare) {return (!strcmp(mExtension,compare));};
+
+public:
+
+ char mURI[LL_MAX_PATH];
+ char mAuthority[LL_MAX_PATH];
+ char mPath[LL_MAX_PATH];
+ char mFilename[LL_MAX_PATH];
+ char mExtension[LL_MAX_PATH];
+ char mTag[LL_MAX_PATH];
+
+ static char sReturnString[LL_MAX_PATH];
+};
+
+#endif // LL_LLURL_H
+
diff --git a/indra/newview/llurlwhitelist.cpp b/indra/newview/llurlwhitelist.cpp
new file mode 100644
index 0000000000..d582d2d19f
--- /dev/null
+++ b/indra/newview/llurlwhitelist.cpp
@@ -0,0 +1,221 @@
+/**
+ * @file llurlwhitelist.cpp
+ * @author Callum Prentice
+ * @brief maintains a "white list" of acceptable URLS that are stored on disk
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llurlwhitelist.h"
+
+#include <iostream>
+#include <fstream>
+
+LLUrlWhiteList* LLUrlWhiteList::sInstance = 0;
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLUrlWhiteList::LLUrlWhiteList () :
+ mLoaded ( false ),
+ mFilename ( "url_whitelist.ini" ),
+ mUrlList ( 0 ),
+ mUrlListIter ( 0 )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+LLUrlWhiteList::~LLUrlWhiteList ()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+//static
+void LLUrlWhiteList::initClass ()
+{
+ if ( ! sInstance )
+ {
+ sInstance = new LLUrlWhiteList ();
+ }
+}
+
+//static
+void LLUrlWhiteList::cleanupClass ()
+{
+ delete sInstance;
+ sInstance = NULL;
+}
+
+LLUrlWhiteList* LLUrlWhiteList::getInstance ()
+{
+ return sInstance;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::load ()
+{
+ // don't load if we're already loaded
+ if ( mLoaded )
+ return ( true );
+
+ // remove current entries before we load over them
+ clear ();
+
+ // build filename for each user
+ LLString resolvedFilename = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, mFilename.c_str () );
+
+ // open a file for reading
+ llifstream file ( resolvedFilename.c_str () );
+ if ( file.is_open () )
+ {
+ // add each line in the file to the list
+ std::string line;
+ while ( std::getline ( file, line ) )
+ {
+ addItem ( line, false );
+ };
+
+ file.close ();
+
+ // flag as loaded
+ mLoaded = true;
+
+ return true;
+ };
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::save ()
+{
+ // build filename for each user
+ LLString resolvedFilename = gDirUtilp->getExpandedFilename ( LL_PATH_PER_SL_ACCOUNT, mFilename.c_str () );
+
+ // open a file for writing
+ llofstream file ( resolvedFilename.c_str () );
+ if ( file.is_open () )
+ {
+ // for each entry we have
+ for ( LLStringListIter iter = mUrlList.begin (); iter != mUrlList.end (); ++iter )
+ {
+ file << ( *iter ) << std::endl;
+ };
+
+ file.close ();
+
+ return true;
+ };
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::clear ()
+{
+ mUrlList.clear ();
+
+ // invalidate iterator since we changed the contents
+ mUrlListIter = mUrlList.end ();
+
+ return true;
+}
+
+LLString url_cleanup(LLString pattern)
+{
+ LLString::trim(pattern);
+ S32 length = pattern.length();
+ S32 position = 0;
+ std::string::reverse_iterator it = pattern.rbegin();
+ ++it; // skip last char, might be '/'
+ ++position;
+ for (; it < pattern.rend(); ++it)
+ {
+ char c = *it;
+ if (c == '/')
+ {
+ // found second to last '/'
+ S32 desired_length = length - position;
+ LLString::truncate(pattern, desired_length);
+ break;
+ }
+ ++position;
+ }
+ return pattern;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::addItem ( const LLString& itemIn, bool saveAfterAdd )
+{
+ LLString item = url_cleanup(itemIn);
+
+ mUrlList.insert ( mUrlList.end (), item );
+
+ // use this when all you want to do is call addItem ( ... ) where necessary
+ if ( saveAfterAdd )
+ save ();
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::getFirst ( LLString& valueOut )
+{
+ if ( mUrlList.size () == 0 )
+ return false;
+
+ mUrlListIter = mUrlList.begin ();
+
+ valueOut = * ( mUrlListIter );
+
+ ++mUrlListIter;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::getNext ( LLString& valueOut )
+{
+ if ( mUrlListIter == mUrlList.end () )
+ return false;
+
+ valueOut = * ( mUrlListIter );
+
+ ++mUrlListIter;
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+bool LLUrlWhiteList::containsMatch ( const LLString& patternIn )
+{
+ return false;
+
+ // CP: removed since they're not useful without Mozilla enabled
+ #if LL_MOZILLA_ENABLED
+ LLString pattern = url_cleanup(patternIn);
+
+ if (pattern.empty()) return false;
+
+ LLStringListIter iter = std::find ( mUrlList.begin (), mUrlList.end (), pattern );
+
+ if ( iter != mUrlList.end () )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ };
+ #endif
+}
diff --git a/indra/newview/llurlwhitelist.h b/indra/newview/llurlwhitelist.h
new file mode 100644
index 0000000000..e0d1f64ddb
--- /dev/null
+++ b/indra/newview/llurlwhitelist.h
@@ -0,0 +1,48 @@
+/**
+ * @file llurlwhitelist.h
+ * @author Callum Prentice
+ * @brief maintains a "white list" of acceptable URLS that are stored on disk
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLURLWHITELIST_H
+#define LL_LLURLWHITELIST_H
+
+#include <list>
+
+class LLUrlWhiteList
+{
+ public:
+ virtual ~LLUrlWhiteList ();
+
+ static void initClass();
+ static void cleanupClass();
+ static LLUrlWhiteList* getInstance ();
+
+ bool load ();
+ bool save ();
+
+ bool clear ();
+ bool addItem ( const LLString& itemIn, bool saveAfterAdd );
+
+ bool containsMatch ( const LLString& patternIn );
+
+ bool getFirst ( LLString& valueOut );
+ bool getNext ( LLString& valueOut );
+
+ private:
+ LLUrlWhiteList ();
+ static LLUrlWhiteList* sInstance;
+
+ typedef std::list < LLString > LLStringList;
+ typedef std::list < LLString >::iterator LLStringListIter;
+
+ bool mLoaded;
+ const LLString mFilename;
+ LLStringList mUrlList;
+ LLStringListIter mUrlListIter;
+};
+
+#endif // LL_LLURLWHITELIST_H
diff --git a/indra/newview/llviewchildren.cpp b/indra/newview/llviewchildren.cpp
new file mode 100644
index 0000000000..3bf138d103
--- /dev/null
+++ b/indra/newview/llviewchildren.cpp
@@ -0,0 +1,99 @@
+/**
+ * @file llviewchildren.cpp
+ * @brief LLViewChildren class implementation
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewchildren.h"
+
+#include "lluictrlfactory.h"
+
+// viewer includes
+#include "llbutton.h"
+#include "lliconctrl.h"
+#include "lltextbox.h"
+#include "lluuid.h"
+#include "llpanel.h"
+#include "llviewercontrol.h"
+
+
+// *NOTE: Do not use mParent reference in the constructor, since it is
+// potentially not fully constructud.
+LLViewChildren::LLViewChildren(LLPanel& parent)
+ : mParent(parent)
+{
+}
+
+
+void LLViewChildren::show(const char* id, bool visible)
+{
+ mParent.childSetVisible(id, visible);
+}
+
+void LLViewChildren::enable(const char* id, bool enabled)
+{
+ mParent.childSetEnabled(id, enabled);
+}
+
+void LLViewChildren::setText(
+ const char* id, const std::string& text, bool visible)
+{
+ LLTextBox* child = LLUICtrlFactory::getTextBoxByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setText(text);
+ }
+}
+
+void LLViewChildren::setWrappedText(
+ const char* id, const std::string& text, bool visible)
+{
+ LLTextBox* child = LLUICtrlFactory::getTextBoxByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setWrappedText(text);
+ }
+}
+
+void LLViewChildren::setBadge(const char* id, Badge badge, bool visible)
+{
+ static LLUUID badgeOK(gViewerArt.getString("badge_ok.tga"));
+ static LLUUID badgeNote(gViewerArt.getString("badge_note.tga"));
+ static LLUUID badgeWarn(gViewerArt.getString("badge_warn.tga"));
+ static LLUUID badgeError(gViewerArt.getString("badge_error.tga"));
+
+ LLUUID badgeUUID;
+ switch (badge)
+ {
+ default:
+ case BADGE_OK: badgeUUID = badgeOK; break;
+ case BADGE_NOTE: badgeUUID = badgeNote; break;
+ case BADGE_WARN: badgeUUID = badgeWarn; break;
+ case BADGE_ERROR: badgeUUID = badgeError; break;
+ }
+
+ LLIconCtrl* child = LLUICtrlFactory::getIconByName(&mParent, id);
+ if (child)
+ {
+ child->setVisible(visible);
+ child->setImage(badgeUUID);
+ }
+}
+
+void LLViewChildren::setAction(const char* id,
+ void(*function)(void*), void* value)
+{
+ LLButton* button = LLUICtrlFactory::getButtonByName(&mParent, id);
+ if (button)
+ {
+ button->setClickedCallback(function, value);
+ }
+}
+
+
diff --git a/indra/newview/llviewchildren.h b/indra/newview/llviewchildren.h
new file mode 100644
index 0000000000..1a7238127f
--- /dev/null
+++ b/indra/newview/llviewchildren.h
@@ -0,0 +1,49 @@
+/**
+ * @file llviewchildren.h
+ * @brief LLViewChildren class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWCHILDREN_H
+#define LL_LLVIEWCHILDREN_H
+
+class LLPanel;
+
+class LLViewChildren
+ // makes it easy to manipulate children of a view by id safely
+ // encapsulates common operations into simple, one line calls
+{
+public:
+ LLViewChildren(LLPanel& parent);
+
+ // all views
+ void show(const char* id, bool visible = true);
+ void hide(const char* id) { show(id, false); }
+
+ void enable(const char* id, bool enabled = true);
+ void disable(const char* id) { enable(id, false); };
+
+ //
+ // LLTextBox
+ void setText(const char* id,
+ const std::string& text, bool visible = true);
+ void setWrappedText(const char* id,
+ const std::string& text, bool visible = true);
+
+ // LLIconCtrl
+ enum Badge { BADGE_OK, BADGE_NOTE, BADGE_WARN, BADGE_ERROR };
+
+ void setBadge(const char* id, Badge b, bool visible = true);
+
+
+ // LLButton
+ void setAction(const char* id, void(*function)(void*), void* value);
+
+
+private:
+ LLPanel& mParent;
+};
+
+#endif
diff --git a/indra/newview/llviewerassetstorage.cpp b/indra/newview/llviewerassetstorage.cpp
new file mode 100644
index 0000000000..b75f86d76f
--- /dev/null
+++ b/indra/newview/llviewerassetstorage.cpp
@@ -0,0 +1,196 @@
+/**
+ * @file llviewerassetstorage.cpp
+ * @brief Subclass capable of loading asset data to/from an external source.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#include "linden_common.h"
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llviewerassetstorage.h"
+#include "llviewerbuild.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host)
+ : LLAssetStorage(msg, xfer, vfs, upstream_host)
+{
+}
+
+
+LLViewerAssetStorage::LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs)
+ : LLAssetStorage(msg, xfer, vfs)
+{
+}
+
+// virtual
+void LLViewerAssetStorage::storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority,
+ bool store_local)
+{
+ LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+ llinfos << "LLViewerAssetStorage::storeAssetData (legacy) " << tid << ":" << LLAssetType::lookup(asset_type)
+ << " ASSET_ID: " << asset_id << llendl;
+
+ if (mUpstreamHost.isOk())
+ {
+ if (mVFS->getExists(asset_id, asset_type))
+ {
+ // Pack data into this packet if we can fit it.
+ U8 buffer[MTUBYTES];
+ buffer[0] = 0;
+
+ LLVFile vfile(mVFS, asset_id, asset_type, LLVFile::READ);
+ S32 asset_size = vfile.getSize();
+
+ LLAssetRequest *req = new LLAssetRequest(asset_id, asset_type);
+ req->mUpCallback = callback;
+ req->mUserData = user_data;
+
+ if (asset_size < 1)
+ {
+ // This can happen if there's a bug in our code or if the VFS has been corrupted.
+ llwarns << "LLViewerAssetStorage::storeAssetData() Data _should_ already be in the VFS, but it's not! " << asset_id << llendl;
+
+ delete req;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_FAILED);
+ }
+ return;
+ }
+ else if(is_priority)
+ {
+ mPendingUploads.push_front(req);
+ }
+ else
+ {
+ mPendingUploads.push_back(req);
+ }
+
+ // Read the data from the VFS if it'll fit in this packet.
+ if (asset_size + 100 < MTUBYTES)
+ {
+ BOOL res = vfile.read(buffer, asset_size);
+ S32 bytes_read = res ? vfile.getLastBytesRead() : 0;
+
+ if( bytes_read == asset_size )
+ {
+ req->mDataSentInFirstPacket = TRUE;
+ //llinfos << "LLViewerAssetStorage::createAsset sending data in first packet" << llendl;
+ }
+ else
+ {
+ llwarns << "Probable corruption in VFS file, aborting store asset data" << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE);
+ }
+ return;
+ }
+ }
+ else
+ {
+ // Too big, do an xfer
+ buffer[0] = 0;
+ asset_size = 0;
+ }
+ mMessageSys->newMessageFast(_PREHASH_AssetUploadRequest);
+ mMessageSys->nextBlockFast(_PREHASH_AssetBlock);
+ mMessageSys->addUUIDFast(_PREHASH_TransactionID, tid);
+ mMessageSys->addS8Fast(_PREHASH_Type, (S8)asset_type);
+ mMessageSys->addBOOLFast(_PREHASH_Tempfile, temp_file);
+ mMessageSys->addBOOLFast(_PREHASH_StoreLocal, store_local);
+ mMessageSys->addBinaryDataFast( _PREHASH_AssetData, buffer, asset_size );
+ mMessageSys->sendReliable(mUpstreamHost);
+ }
+ else
+ {
+ llwarns << "AssetStorage: attempt to upload non-existent vfile " << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE);
+ }
+ }
+ }
+ else
+ {
+ llwarns << "Attempt to move asset store request upstream w/o valid upstream provider" << llendl;
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_CIRCUIT_GONE);
+ }
+ }
+}
+
+void LLViewerAssetStorage::storeAssetData(
+ const char* filename,
+ const LLTransactionID& tid,
+ LLAssetType::EType asset_type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file,
+ bool is_priority)
+{
+ LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+ llinfos << "LLViewerAssetStorage::storeAssetData (legacy)" << asset_id << ":" << LLAssetType::lookup(asset_type) << llendl;
+
+ LLLegacyAssetRequest *legacy = new LLLegacyAssetRequest;
+
+ llinfos << "ASSET_ID: " << asset_id << llendl;
+
+ legacy->mUpCallback = callback;
+ legacy->mUserData = user_data;
+
+ FILE *fp = LLFile::fopen(filename, "rb");
+ if (fp)
+ {
+ LLVFile file(mVFS, asset_id, asset_type, LLVFile::WRITE);
+
+ fseek(fp, 0, SEEK_END);
+ S32 size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ file.setMaxSize(size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((size = (S32)fread(copy_buf, 1, buf_size, fp)))
+ {
+ file.write(copy_buf, size);
+ }
+ fclose(fp);
+
+ // if this upload fails, the caller needs to setup a new tempfile for us
+ if (temp_file)
+ {
+ LLFile::remove(filename);
+ }
+
+ LLViewerAssetStorage::storeAssetData(
+ tid,
+ asset_type,
+ legacyStoreDataCallback,
+ (void**)legacy,
+ temp_file,
+ is_priority);
+ }
+ else
+ {
+ if (callback)
+ {
+ callback(asset_id, user_data, LL_ERR_CANNOT_OPEN_FILE);
+ }
+ }
+}
diff --git a/indra/newview/llviewerassetstorage.h b/indra/newview/llviewerassetstorage.h
new file mode 100644
index 0000000000..15dc533f09
--- /dev/null
+++ b/indra/newview/llviewerassetstorage.h
@@ -0,0 +1,47 @@
+/**
+ * @file llviewerassetstorage.h
+ * @brief Class for loading asset data to/from an external source.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLVIEWERASSETSTORAGE_H
+#define LLVIEWERASSETSTORAGE_H
+
+#include "llassetstorage.h"
+//#include "curl/curl.h"
+
+class LLVFile;
+
+class LLViewerAssetStorage : public LLAssetStorage
+{
+public:
+ typedef void (*LLStoreAssetCallback)(const LLUUID &transaction_id, void *user_data, S32 status);
+
+ LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs, const LLHost &upstream_host);
+
+ LLViewerAssetStorage(LLMessageSystem *msg, LLXferManager *xfer,
+ LLVFS *vfs);
+
+ virtual void storeAssetData(
+ const LLTransactionID& tid,
+ LLAssetType::EType atype,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false,
+ bool store_local = false);
+
+ virtual void storeAssetData(
+ const char* filename,
+ const LLTransactionID& tid,
+ LLAssetType::EType type,
+ LLStoreAssetCallback callback,
+ void* user_data,
+ bool temp_file = false,
+ bool is_priority = false);
+};
+
+#endif
diff --git a/indra/newview/llviewercamera.cpp b/indra/newview/llviewercamera.cpp
new file mode 100644
index 0000000000..c4c25b33f4
--- /dev/null
+++ b/indra/newview/llviewercamera.cpp
@@ -0,0 +1,611 @@
+/**
+ * @file llviewercamera.cpp
+ * @brief LLViewerCamera class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <iomanip> // for setprecision
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercamera.h"
+
+#include "llquaternion.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llvovolume.h"
+#include "llworld.h"
+
+BOOL LLViewerCamera::sDisableCameraConstraints = FALSE;
+
+LLViewerCamera *gCamera = NULL;
+
+LLViewerCamera::LLViewerCamera() : LLCamera()
+{
+ calcProjection(getFar());
+ S32 i;
+ for (i = 0; i < 16; i++)
+ {
+ mGLProjectionMatrix[i] = 0.f;
+ }
+ mCameraFOVDefault = DEFAULT_FIELD_OF_VIEW;
+ mPixelMeterRatio = 0.f;
+ mScreenPixelArea = 0;
+ mZoomFactor = 1.f;
+ mZoomSubregion = 1;
+}
+
+void LLViewerCamera::updateCameraLocation(const LLVector3 &center,
+ const LLVector3 &up_direction,
+ const LLVector3 &point_of_interest)
+{
+ LLVector3 last_position;
+ LLVector3 last_axis;
+ last_position = getOrigin();
+ last_axis = getAtAxis();
+
+ mLastPointOfInterest = point_of_interest;
+
+ // constrain to max distance from avatar
+ LLVector3 camera_offset = center - gAgent.getPositionAgent();
+
+ setOriginAndLookAt(center, up_direction, point_of_interest);
+
+ F32 dpos = (center - last_position).magVec();
+ LLQuaternion rotation;
+ rotation.shortestArc(last_axis, getAtAxis());
+
+ F32 x, y, z;
+ F32 drot;
+ rotation.getAngleAxis(&drot, &x, &y, &z);
+ mVelocityStat.addValue(dpos);
+ mAngularVelocityStat.addValue(drot);
+ // update pixel meter ratio using default fov, not modified one
+ mPixelMeterRatio = mViewHeightInPixels / (2.f*tanf(mCameraFOVDefault*0.5));
+ // update screen pixel area
+ mScreenPixelArea =(S32)((F32)mViewHeightInPixels * ((F32)mViewHeightInPixels * mAspect));
+}
+
+// Handy copies of last good GL matrices
+F64 gGLModelView[16];
+S32 gGLViewport[4];
+
+const LLMatrix4 &LLViewerCamera::getProjection() const
+{
+ calcProjection(getFar());
+ return mProjectionMatrix;
+
+}
+
+const LLMatrix4 &LLViewerCamera::getModelview() const
+{
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ getMatrixToLocal(mModelviewMatrix);
+ mModelviewMatrix *= cfr;
+ return mModelviewMatrix;
+}
+
+void LLViewerCamera::calcProjection(const F32 far_distance) const
+{
+ F32 fov_y, z_far, z_near, aspect, f;
+ fov_y = getView();
+ z_far = far_distance;
+ z_near = getNear();
+ aspect = getAspect();
+
+ f = 1/tan(fov_y*0.5f);
+
+ mProjectionMatrix.zero();
+ mProjectionMatrix.mMatrix[0][0] = f/aspect;
+ mProjectionMatrix.mMatrix[1][1] = f;
+ mProjectionMatrix.mMatrix[2][2] = (z_far + z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[3][2] = (2*z_far*z_near)/(z_near - z_far);
+ mProjectionMatrix.mMatrix[2][3] = -1;
+}
+
+// Sets up opengl state for 3D drawing. If for selection, also
+// sets up a pick matrix. x and y are ignored if for_selection is false.
+// The picking region is centered on x,y and has the specified width and
+// height.
+
+LLMatrix4 gProjectionMat;
+
+void LLViewerCamera::updateFrustumPlanes()
+{
+ GLint viewport[4];
+ GLdouble model[16];
+ GLdouble proj[16];
+ GLdouble objX,objY,objZ;
+
+ LLVector3 frust[8];
+
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ glGetDoublev(GL_MODELVIEW_MATRIX, model);
+ glGetDoublev(GL_PROJECTION_MATRIX,proj);
+
+ gluUnProject(viewport[0],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[0].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[1].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[2].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],0,model,proj,viewport,&objX,&objY,&objZ);
+ frust[3].setVec((F32)objX,(F32)objY,(F32)objZ);
+ /*gluUnProject(viewport[0],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[4].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[5].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0]+viewport[2],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[6].setVec((F32)objX,(F32)objY,(F32)objZ);
+ gluUnProject(viewport[0],viewport[1]+viewport[3],1,model,proj,viewport,&objX,&objY,&objZ);
+ frust[7].setVec((F32)objX,(F32)objY,(F32)objZ);*/
+
+ for (U32 i = 0; i < 4; i++)
+ {
+ LLVector3 vec = frust[i] - getOrigin();
+ vec.normVec();
+ frust[i+4] = getOrigin() + vec*getFar()*2.0;
+ }
+
+ calcAgentFrustumPlanes(frust);
+}
+
+void LLViewerCamera::setPerspective(BOOL for_selection,
+ S32 x, S32 y_from_bot, S32 width, S32 height,
+ BOOL limit_select_distance,
+ F32 z_near, F32 z_far)
+{
+ F32 fov_y, aspect;
+ fov_y = RAD_TO_DEG * getView();
+ BOOL z_default_near, z_default_far = FALSE;
+ if (z_far <= 0)
+ {
+ z_default_far = TRUE;
+ z_far = getFar();
+ }
+ if (z_near <= 0)
+ {
+ z_default_near = TRUE;
+ z_near = getNear();
+ }
+ aspect = getAspect();
+
+ // Load camera view matrix
+ glMatrixMode( GL_PROJECTION );
+ glLoadIdentity();
+
+ if (for_selection)
+ {
+ // make a tiny little viewport
+ // anything drawn into this viewport will be "selected"
+ const U8 VIEWPORT_VECTOR_LEN = 4;
+ GLint viewport[VIEWPORT_VECTOR_LEN];
+ glGetIntegerv(GL_VIEWPORT, viewport);
+ gluPickMatrix(x + width / 2, y_from_bot + height / 2, width, height, viewport);
+
+ if (limit_select_distance)
+ {
+ // ...select distance from control
+ z_far = gSavedSettings.getF32("MaxSelectDistance");
+ }
+ else
+ {
+ z_far = gAgent.mDrawDistance;
+ }
+ }
+ else
+ {
+ // Only override the far clip if it's not passed in explicitly.
+ if (z_default_far)
+ {
+ z_far = MAX_FAR_CLIP;
+ }
+ glViewport(x, y_from_bot, width, height);
+ }
+
+ if (mZoomFactor > 1.f)
+ {
+ float offset = mZoomFactor - 1.f;
+ int pos_y = mZoomSubregion / llceil(mZoomFactor);
+ int pos_x = mZoomSubregion - (pos_y*llceil(mZoomFactor));
+ glTranslatef(offset - (F32)pos_x * 2.f, offset - (F32)pos_y * 2.f, 0.f);
+ glScalef(mZoomFactor, mZoomFactor, 1.f);
+ }
+
+ calcProjection(z_far); // Update the projection matrix cache
+
+ gluPerspective(fov_y,
+ aspect,
+ z_near,
+ z_far);
+ glGetFloatv(GL_PROJECTION_MATRIX, (float*)&gProjectionMat);
+
+ glMatrixMode( GL_MODELVIEW );
+
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+
+ GLfloat ogl_matrix[16];
+ getOpenGLTransform(ogl_matrix);
+ glMultMatrixf(ogl_matrix);
+
+ if (for_selection && (width > 1 || height > 1))
+ {
+ calculateFrustumPlanesFromWindow((F32)(x - width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot - height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f,
+ (F32)(x + width / 2) / (F32)gViewerWindow->getWindowWidth() - 0.5f,
+ (F32)(y_from_bot + height / 2) / (F32)gViewerWindow->getWindowHeight() - 0.5f);
+
+ }
+
+ // if not picking and not doing a snapshot, cache various GL matrices
+ if (!for_selection && mZoomFactor == 1.f)
+ {
+ // Save GL matrices for access elsewhere in code, especially project_world_to_screen
+ glGetDoublev(GL_PROJECTION_MATRIX, mGLProjectionMatrix);
+ glGetDoublev(GL_MODELVIEW_MATRIX, gGLModelView);
+ glGetIntegerv(GL_VIEWPORT, (GLint*)gGLViewport);
+ }
+
+ updateFrustumPlanes();
+
+ if (gSavedSettings.getBOOL("CameraOffset"))
+ {
+ glMatrixMode(GL_PROJECTION);
+ glTranslatef(0,0,-50);
+ glRotatef(20.0,1,0,0);
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// screen coordinates to the agent's region.
+void LLViewerCamera::projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent) const
+{
+
+ GLdouble x, y, z;
+ gluUnProject(
+ GLdouble(screen_x), GLdouble(screen_y), 0.0,
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x,
+ &y,
+ &z );
+ pos_agent->setVec( (F32)x, (F32)y, (F32)z );
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to screen coordinates. Returns TRUE if point in within
+// the current window.
+BOOL LLViewerCamera::projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp) const
+{
+ BOOL in_front = TRUE;
+ GLdouble x, y, z; // object's window coords, GL-style
+
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ if (clamp)
+ {
+ return FALSE;
+ }
+ else
+ {
+ in_front = FALSE;
+ }
+ }
+
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY], pos_agent.mV[VZ],
+ gGLModelView, mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ // convert screen coordinates to virtual UI coordinates
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ BOOL valid = TRUE;
+
+ if (clamp)
+ {
+ if (int_x < window_rect.mLeft)
+ {
+ out_point.mX = window_rect.mLeft;
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ out_point.mX = window_rect.mRight;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ }
+
+ if (int_y < window_rect.mBottom)
+ {
+ out_point.mY = window_rect.mBottom;
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ out_point.mY = window_rect.mTop;
+ valid = FALSE;
+ }
+ else
+ {
+ out_point.mY = int_y;
+ }
+ return valid;
+ }
+ else
+ {
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+
+ if (int_x < window_rect.mLeft)
+ {
+ valid = FALSE;
+ }
+ else if (int_x > window_rect.mRight)
+ {
+ valid = FALSE;
+ }
+ if (int_y < window_rect.mBottom)
+ {
+ valid = FALSE;
+ }
+ else if (int_y > window_rect.mTop)
+ {
+ valid = FALSE;
+ }
+
+ return in_front && valid;
+ }
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// Uses the last GL matrices set in set_perspective to project a point from
+// the agent's region space to the nearest edge in screen coordinates.
+// Returns TRUE if projection succeeds.
+BOOL LLViewerCamera::projectPosAgentToScreenEdge(const LLVector3 &pos_agent,
+ LLCoordGL &out_point) const
+{
+ LLVector3 dir_to_point = pos_agent - getOrigin();
+ dir_to_point /= dir_to_point.magVec();
+
+ BOOL in_front = TRUE;
+ if (dir_to_point * getAtAxis() < 0.f)
+ {
+ in_front = FALSE;
+ }
+
+ GLdouble x, y, z; // object's window coords, GL-style
+ if (GL_TRUE == gluProject(pos_agent.mV[VX], pos_agent.mV[VY],
+ pos_agent.mV[VZ], gGLModelView,
+ mGLProjectionMatrix, (GLint*)gGLViewport,
+ &x, &y, &z))
+ {
+ x /= gViewerWindow->getDisplayScale().mV[VX];
+ y /= gViewerWindow->getDisplayScale().mV[VY];
+ // should now have the x,y coords of grab_point in screen space
+ const LLRect& window_rect = gViewerWindow->getVirtualWindowRect();
+
+ // ...sanity check
+ S32 int_x = lltrunc(x);
+ S32 int_y = lltrunc(y);
+
+ // find the center
+ GLdouble center_x = (GLdouble)(0.5f * (window_rect.mLeft + window_rect.mRight));
+ GLdouble center_y = (GLdouble)(0.5f * (window_rect.mBottom + window_rect.mTop));
+
+ if (x == center_x && y == center_y)
+ {
+ // can't project to edge from exact center
+ return FALSE;
+ }
+
+ // find the line from center to local
+ GLdouble line_x = x - center_x;
+ GLdouble line_y = y - center_y;
+
+ int_x = lltrunc(center_x);
+ int_y = lltrunc(center_y);
+
+
+ if (0.f == line_x)
+ {
+ // the slope of the line is undefined
+ if (line_y > 0.f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ else if (0 == window_rect.getWidth())
+ {
+ // the diagonal slope of the view is undefined
+ if (y < window_rect.mBottom)
+ {
+ int_y = window_rect.mBottom;
+ }
+ else if ( y > window_rect.mTop)
+ {
+ int_y = window_rect.mTop;
+ }
+ }
+ else
+ {
+ F32 line_slope = (F32)(line_y / line_x);
+ F32 rect_slope = ((F32)window_rect.getHeight()) / ((F32)window_rect.getWidth());
+
+ if (fabs(line_slope) > rect_slope)
+ {
+ if (line_y < 0.f)
+ {
+ // bottom
+ int_y = window_rect.mBottom;
+ }
+ else
+ {
+ // top
+ int_y = window_rect.mTop;
+ }
+ int_x = lltrunc(((GLdouble)int_y - center_y) / line_slope + center_x);
+ }
+ else if (fabs(line_slope) < rect_slope)
+ {
+ if (line_x < 0.f)
+ {
+ // left
+ int_x = window_rect.mLeft;
+ }
+ else
+ {
+ // right
+ int_x = window_rect.mRight;
+ }
+ int_y = lltrunc(((GLdouble)int_x - center_x) * line_slope + center_y);
+ }
+ else
+ {
+ // exactly parallel ==> push to the corners
+ if (line_x > 0.f)
+ {
+ int_x = window_rect.mRight;
+ }
+ else
+ {
+ int_x = window_rect.mLeft;
+ }
+ if (line_y > 0.0f)
+ {
+ int_y = window_rect.mTop;
+ }
+ else
+ {
+ int_y = window_rect.mBottom;
+ }
+ }
+ }
+ if (!in_front)
+ {
+ int_x = window_rect.mLeft + window_rect.mRight - int_x;
+ int_y = window_rect.mBottom + window_rect.mTop - int_y;
+ }
+ out_point.mX = int_x;
+ out_point.mY = int_y;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void LLViewerCamera::getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right)
+{
+ LLVector3 to_vec = pos_agent - getOrigin();
+
+ F32 at_dist = to_vec * getAtAxis();
+
+ F32 height_meters = at_dist* (F32)tan(getView()/2.f);
+ F32 height_pixels = getViewHeightInPixels()/2.f;
+
+ F32 pixel_aspect = gViewerWindow->getWindow()->getPixelAspectRatio();
+
+ F32 meters_per_pixel = height_meters / height_pixels;
+ up = getUpAxis() * meters_per_pixel * gViewerWindow->getDisplayScale().mV[VY];
+ right = -1.f * pixel_aspect * meters_per_pixel * getLeftAxis() * gViewerWindow->getDisplayScale().mV[VX];
+}
+
+LLVector3 LLViewerCamera::roundToPixel(const LLVector3 &pos_agent)
+{
+ F32 dist = (pos_agent - getOrigin()).magVec();
+ // Convert to screen space and back, preserving the depth.
+ LLCoordGL screen_point;
+ if (!projectPosAgentToScreen(pos_agent, screen_point, FALSE))
+ {
+ // Off the screen, just return the original position.
+ return pos_agent;
+ }
+
+ LLVector3 ray_dir;
+
+ projectScreenToPosAgent(screen_point.mX, screen_point.mY, &ray_dir);
+ ray_dir -= getOrigin();
+ ray_dir.normVec();
+
+ LLVector3 pos_agent_rounded = getOrigin() + ray_dir*dist;
+
+ /*
+ LLVector3 pixel_x, pixel_y;
+ getPixelVectors(pos_agent_rounded, pixel_y, pixel_x);
+ pos_agent_rounded += 0.5f*pixel_x, 0.5f*pixel_y;
+ */
+ return pos_agent_rounded;
+}
+
+BOOL LLViewerCamera::cameraUnderWater() const
+{
+ return getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight();
+}
+
+BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts)
+{
+ S32 i, num_faces;
+ LLDrawable* drawablep = volumep->mDrawable;
+
+ if (!drawablep)
+ {
+ return FALSE;
+ }
+
+ num_faces = drawablep->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ LLStrider<LLVector3> vertices;
+ LLFace* face = drawablep->getFace(i);
+ face->getVertices(vertices);
+
+ for (S32 v = 0; v < (S32)drawablep->getFace(i)->getGeomCount(); v++)
+ {
+ LLVector3 vec = vertices[v];
+ if (!face->isState(LLFace::GLOBAL))
+ {
+ vec = vec*face->getRenderMatrix();
+ }
+
+ BOOL in_frustum = pointInFrustum(vec) > 0;
+
+ if ( !in_frustum && all_verts ||
+ in_frustum && !all_verts)
+ {
+ return !all_verts;
+ }
+ }
+ }
+ return all_verts;
+}
diff --git a/indra/newview/llviewercamera.h b/indra/newview/llviewercamera.h
new file mode 100644
index 0000000000..6797103997
--- /dev/null
+++ b/indra/newview/llviewercamera.h
@@ -0,0 +1,95 @@
+/**
+ * @file llviewercamera.h
+ * @brief LLViewerCamera class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERCAMERA_H
+#define LL_LLVIEWERCAMERA_H
+
+#include "llcamera.h"
+#include "lltimer.h"
+#include "llstat.h"
+#include "m4math.h"
+
+class LLCoordGL;
+class LLViewerObject;
+
+// This rotation matrix moves the default OpenGL reference frame
+// (-Z at, Y up) to Cory's favorite reference frame (X at, Z up)
+const F32 OGL_TO_CFR_ROTATION[16] = { 0.f, 0.f, -1.f, 0.f, // -Z becomes X
+ -1.f, 0.f, 0.f, 0.f, // -X becomes Y
+ 0.f, 1.f, 0.f, 0.f, // Y becomes Z
+ 0.f, 0.f, 0.f, 1.f };
+
+class LLViewerCamera : public LLCamera
+{
+public:
+ LLViewerCamera();
+
+// const LLVector3 &getPositionAgent() const;
+// const LLVector3d &getPositionGlobal() const;
+
+ void updateCameraLocation(const LLVector3 &center,
+ const LLVector3 &up_direction,
+ const LLVector3 &point_of_interest);
+
+ void updateFrustumPlanes();
+ void setPerspective(BOOL for_selection, S32 x, S32 y_from_bot, S32 width, S32 height, BOOL limit_select_distance, F32 z_near = 0, F32 z_far = 0);
+
+ const LLMatrix4 &getProjection() const;
+ const LLMatrix4 &getModelview() const;
+
+ // Warning! These assume the current global matrices are correct
+ void projectScreenToPosAgent(const S32 screen_x, const S32 screen_y, LLVector3* pos_agent ) const;
+ BOOL projectPosAgentToScreen(const LLVector3 &pos_agent, LLCoordGL &out_point, const BOOL clamp = TRUE) const;
+ BOOL projectPosAgentToScreenEdge(const LLVector3 &pos_agent, LLCoordGL &out_point) const;
+
+
+ LLStat *getVelocityStat() { return &mVelocityStat; }
+ LLStat *getAngularVelocityStat() { return &mAngularVelocityStat; }
+
+ void getPixelVectors(const LLVector3 &pos_agent, LLVector3 &up, LLVector3 &right);
+ LLVector3 roundToPixel(const LLVector3 &pos_agent);
+
+ void setDefaultFOV(F32 fov) { mCameraFOVDefault = fov; }
+ F32 getDefaultFOV() { return mCameraFOVDefault; }
+
+ BOOL cameraUnderWater() const;
+
+ const LLVector3 &getPointOfInterest() { return mLastPointOfInterest; }
+ BOOL areVertsVisible(LLViewerObject* volumep, BOOL all_verts);
+ F32 getPixelMeterRatio() const { return mPixelMeterRatio; }
+ S32 getScreenPixelArea() const { return mScreenPixelArea; }
+
+ void setZoomParameters(F32 factor, S16 subregion) { mZoomFactor = factor; mZoomSubregion = subregion; }
+ F32 getZoomFactor() { return mZoomFactor; }
+ S16 getZoomSubRegion() { return mZoomSubregion; }
+
+protected:
+ void calcProjection(const F32 far_distance) const;
+
+ LLStat mVelocityStat;
+ LLStat mAngularVelocityStat;
+ mutable LLMatrix4 mProjectionMatrix; // Cache of perspective matrix
+ mutable LLMatrix4 mModelviewMatrix;
+ F32 mCameraFOVDefault;
+ LLVector3 mLastPointOfInterest;
+ F32 mPixelMeterRatio; // Divide by distance from camera to get pixels per meter at that distance.
+ S32 mScreenPixelArea; // Pixel area of entire window
+ F32 mZoomFactor;
+ S16 mZoomSubregion;
+
+public:
+ static BOOL sDisableCameraConstraints;
+ F64 mGLProjectionMatrix[16];
+
+};
+
+extern LLViewerCamera *gCamera;
+extern F64 gGLModelView[16];
+extern S32 gGLViewport[4];
+
+#endif // LL_LLVIEWERCAMERA_H
diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp
new file mode 100644
index 0000000000..c0de0c872a
--- /dev/null
+++ b/indra/newview/llviewercontrol.cpp
@@ -0,0 +1,465 @@
+/**
+ * @file llviewercontrol.cpp
+ * @brief Viewer configuration
+ * @author Richard Nelson
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewercontrol.h"
+
+#include "indra_constants.h"
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llrect.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v3color.h"
+
+#include "llfloater.h"
+#include "llvieweruictrlfactory.h"
+#include "llfirstuse.h"
+#include "llcombobox.h"
+#include "llspinctrl.h"
+#include "llcolorswatch.h"
+
+LLFloaterSettingsDebug* LLFloaterSettingsDebug::sInstance = NULL;
+
+LLControlGroup gSavedSettings; // saved at end of session
+LLControlGroup gSavedPerAccountSettings; // saved at end of session
+LLControlGroup gViewerArt; // read-only
+LLControlGroup gColors; // read-only
+LLControlGroup gCrashSettings; // saved at end of session
+
+LLString gLastRunVersion;
+LLString gCurrentVersion;
+
+char gSettingsFileName[LL_MAX_PATH];
+char gPerAccountSettingsFileName[LL_MAX_PATH] = "";
+
+LLFloaterSettingsDebug::LLFloaterSettingsDebug() : LLFloater("Configuration Editor")
+{
+}
+
+LLFloaterSettingsDebug::~LLFloaterSettingsDebug()
+{
+ sInstance = NULL;
+}
+
+BOOL LLFloaterSettingsDebug::postBuild()
+{
+ LLComboBox* settings_combo = LLUICtrlFactory::getComboBoxByName(this, "settings_combo");
+
+ LLControlGroup::ctrl_name_table_t::iterator name_it;
+ for(name_it = gSavedSettings.mNameTable.begin(); name_it != gSavedSettings.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ for(name_it = gSavedPerAccountSettings.mNameTable.begin(); name_it != gSavedPerAccountSettings.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ for(name_it = gColors.mNameTable.begin(); name_it != gColors.mNameTable.end(); ++name_it)
+ {
+ settings_combo->add(name_it->first, (void*)name_it->second);
+ }
+ settings_combo->sortByName();
+ settings_combo->setCommitCallback(onSettingSelect);
+ settings_combo->setCallbackUserData(this);
+ settings_combo->updateSelection();
+
+ childSetCommitCallback("val_spinner_1", onCommitSettings);
+ childSetUserData("val_spinner_1", this);
+ childSetCommitCallback("val_spinner_2", onCommitSettings);
+ childSetUserData("val_spinner_2", this);
+ childSetCommitCallback("val_spinner_3", onCommitSettings);
+ childSetUserData("val_spinner_3", this);
+ childSetCommitCallback("val_spinner_4", onCommitSettings);
+ childSetUserData("val_spinner_4", this);
+ childSetCommitCallback("val_text", onCommitSettings);
+ childSetUserData("val_text", this);
+ childSetCommitCallback("boolean_combo", onCommitSettings);
+ childSetUserData("boolean_combo", this);
+ childSetCommitCallback("color_swatch", onCommitSettings);
+ childSetUserData("color_swatch", this);
+
+ mComment = (LLTextEditor*)getChildByName("comment_text");
+ return TRUE;
+}
+
+void LLFloaterSettingsDebug::draw()
+{
+ LLComboBox* settings_combo = (LLComboBox*)getChildByName("settings_combo");
+ LLControlBase* controlp = (LLControlBase*)settings_combo->getCurrentUserdata();
+ updateControl(controlp);
+
+ LLFloater::draw();
+}
+
+//static
+void LLFloaterSettingsDebug::show(void*)
+{
+ if (sInstance == NULL)
+ {
+ sInstance = new LLFloaterSettingsDebug();
+
+ gUICtrlFactory->buildFloater(sInstance, "floater_settings_debug.xml");
+ }
+
+ sInstance->open();
+}
+
+//static
+void LLFloaterSettingsDebug::onSettingSelect(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterSettingsDebug* floaterp = (LLFloaterSettingsDebug*)user_data;
+ LLComboBox* combo_box = (LLComboBox*)ctrl;
+ LLControlBase* controlp = (LLControlBase*)combo_box->getCurrentUserdata();
+
+ floaterp->updateControl(controlp);
+}
+
+//static
+void LLFloaterSettingsDebug::onCommitSettings(LLUICtrl* ctrl, void* user_data)
+{
+ LLFloaterSettingsDebug* floaterp = (LLFloaterSettingsDebug*)user_data;
+
+ LLComboBox* settings_combo = (LLComboBox*)floaterp->getChildByName("settings_combo");
+ LLControlBase* controlp = (LLControlBase*)settings_combo->getCurrentUserdata();
+
+ LLVector3 vector;
+ LLVector3d vectord;
+ LLRect rect;
+ LLColor4 col4;
+ LLColor3 col3;
+ LLColor4U col4U;
+ LLColor4 color_with_alpha;
+
+ switch(controlp->type())
+ {
+ case TYPE_U32:
+ controlp->set(floaterp->childGetValue("val_spinner_1"));
+ break;
+ case TYPE_S32:
+ controlp->set(floaterp->childGetValue("val_spinner_1"));
+ break;
+ case TYPE_F32:
+ controlp->set(LLSD(floaterp->childGetValue("val_spinner_1").asReal()));
+ break;
+ case TYPE_BOOLEAN:
+ controlp->set(floaterp->childGetValue("boolean_combo"));
+ break;
+ case TYPE_STRING:
+ controlp->set(LLSD(floaterp->childGetValue("val_text").asString()));
+ break;
+ case TYPE_VEC3:
+ vector.mV[VX] = (F32)floaterp->childGetValue("val_spinner_1").asReal();
+ vector.mV[VY] = (F32)floaterp->childGetValue("val_spinner_2").asReal();
+ vector.mV[VZ] = (F32)floaterp->childGetValue("val_spinner_3").asReal();
+ controlp->set(vector.getValue());
+ break;
+ case TYPE_VEC3D:
+ vectord.mdV[VX] = floaterp->childGetValue("val_spinner_1").asReal();
+ vectord.mdV[VY] = floaterp->childGetValue("val_spinner_2").asReal();
+ vectord.mdV[VZ] = floaterp->childGetValue("val_spinner_3").asReal();
+ controlp->set(vectord.getValue());
+ break;
+ case TYPE_RECT:
+ rect.mLeft = floaterp->childGetValue("val_spinner_1").asInteger();
+ rect.mRight = floaterp->childGetValue("val_spinner_2").asInteger();
+ rect.mBottom = floaterp->childGetValue("val_spinner_3").asInteger();
+ rect.mTop = floaterp->childGetValue("val_spinner_4").asInteger();
+ controlp->set(rect.getValue());
+ break;
+ case TYPE_COL4:
+ col3.setValue(floaterp->childGetValue("color_swatch"));
+ col4 = LLColor4(col3, (F32)floaterp->childGetValue("val_spinner_4").asReal());
+ controlp->set(col4.getValue());
+ break;
+ case TYPE_COL3:
+ controlp->set(floaterp->childGetValue("color_swatch"));
+ //col3.mV[VRED] = (F32)floaterp->childGetValue("val_spinner_1").asC();
+ //col3.mV[VGREEN] = (F32)floaterp->childGetValue("val_spinner_2").asReal();
+ //col3.mV[VBLUE] = (F32)floaterp->childGetValue("val_spinner_3").asReal();
+ //controlp->set(col3.getValue());
+ break;
+ case TYPE_COL4U:
+ col3.setValue(floaterp->childGetValue("color_swatch"));
+ col4U.setVecScaleClamp(col3);
+ col4U.mV[VALPHA] = floaterp->childGetValue("val_spinner_4").asInteger();
+ controlp->set(col4U.getValue());
+ break;
+ default:
+ break;
+ }
+}
+
+// we've switched controls, or doing per-frame update, so update spinners, etc.
+void LLFloaterSettingsDebug::updateControl(LLControlBase* controlp)
+{
+ LLSpinCtrl* spinner1 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_1");
+ LLSpinCtrl* spinner2 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_2");
+ LLSpinCtrl* spinner3 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_3");
+ LLSpinCtrl* spinner4 = LLUICtrlFactory::getSpinnerByName(this, "val_spinner_4");
+ LLColorSwatchCtrl* color_swatch = LLUICtrlFactory::getColorSwatchByName(this, "color_swatch");
+ spinner1->setVisible(FALSE);
+ spinner2->setVisible(FALSE);
+ spinner3->setVisible(FALSE);
+ spinner4->setVisible(FALSE);
+ color_swatch->setVisible(FALSE);
+ childSetVisible("val_text", FALSE);
+ childSetVisible("boolean_combo", FALSE);
+ mComment->setText("");
+
+ if (controlp)
+ {
+ eControlType type = controlp->type();
+ mComment->setText(controlp->getComment());
+ spinner1->setMaxValue(F32_MAX);
+ spinner2->setMaxValue(F32_MAX);
+ spinner3->setMaxValue(F32_MAX);
+ spinner4->setMaxValue(F32_MAX);
+ spinner1->setMinValue(-F32_MAX);
+ spinner2->setMinValue(-F32_MAX);
+ spinner3->setMinValue(-F32_MAX);
+ spinner4->setMinValue(-F32_MAX);
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setIncrement(0.1f);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setIncrement(0.1f);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setIncrement(0.1f);
+ }
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setIncrement(0.1f);
+ }
+
+ LLSD sd = controlp->get();
+ switch(type)
+ {
+ case TYPE_U32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setValue(sd);
+ spinner1->setMinValue((F32)U32_MIN);
+ spinner1->setMaxValue((F32)U32_MAX);
+ spinner1->setIncrement(1.f);
+ spinner1->setPrecision(0);
+ }
+ break;
+ case TYPE_S32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setValue(sd);
+ spinner1->setMinValue((F32)S32_MIN);
+ spinner1->setMaxValue((F32)S32_MAX);
+ spinner1->setIncrement(1.f);
+ spinner1->setPrecision(0);
+ }
+ break;
+ case TYPE_F32:
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("value");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(sd);
+ }
+ break;
+ case TYPE_BOOLEAN:
+ childSetVisible("boolean_combo", TRUE);
+
+ if (!childHasFocus("boolean_combo"))
+ {
+ if (sd.asBoolean())
+ {
+ childSetValue("boolean_combo", LLSD("true"));
+ }
+ else
+ {
+ childSetValue("boolean_combo", LLSD(""));
+ }
+ }
+ break;
+ case TYPE_STRING:
+ childSetVisible("val_text", TRUE);
+ if (!childHasFocus("val_text"))
+ {
+ childSetValue("val_text", sd);
+ }
+ break;
+ case TYPE_VEC3:
+ {
+ LLVector3 v;
+ v.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("X");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Y");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Z");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(v[VX]);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(3);
+ spinner2->setValue(v[VY]);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(3);
+ spinner3->setValue(v[VZ]);
+ }
+ break;
+ }
+ case TYPE_VEC3D:
+ {
+ LLVector3d v;
+ v.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("X");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Y");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Z");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(3);
+ spinner1->setValue(v[VX]);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(3);
+ spinner2->setValue(v[VY]);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(3);
+ spinner3->setValue(v[VZ]);
+ }
+ break;
+ }
+ case TYPE_RECT:
+ {
+ LLRect r;
+ r.setValue(sd);
+ spinner1->setVisible(TRUE);
+ spinner1->setLabel("Left");
+ spinner2->setVisible(TRUE);
+ spinner2->setLabel("Right");
+ spinner3->setVisible(TRUE);
+ spinner3->setLabel("Bottom");
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Top");
+ if (!spinner1->hasFocus())
+ {
+ spinner1->setPrecision(0);
+ spinner1->setValue(r.mLeft);
+ }
+ if (!spinner2->hasFocus())
+ {
+ spinner2->setPrecision(0);
+ spinner2->setValue(r.mRight);
+ }
+ if (!spinner3->hasFocus())
+ {
+ spinner3->setPrecision(0);
+ spinner3->setValue(r.mBottom);
+ }
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(0);
+ spinner4->setValue(r.mTop);
+ }
+
+ spinner1->setMinValue((F32)S32_MIN);
+ spinner1->setMaxValue((F32)S32_MAX);
+ spinner1->setIncrement(1.f);
+
+ spinner2->setMinValue((F32)S32_MIN);
+ spinner2->setMaxValue((F32)S32_MAX);
+ spinner2->setIncrement(1.f);
+
+ spinner3->setMinValue((F32)S32_MIN);
+ spinner3->setMaxValue((F32)S32_MAX);
+ spinner3->setIncrement(1.f);
+
+ spinner4->setMinValue((F32)S32_MIN);
+ spinner4->setMaxValue((F32)S32_MAX);
+ spinner4->setIncrement(1.f);
+ break;
+ }
+ case TYPE_COL4:
+ {
+ LLColor4 clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ // only set if changed so color picker doesn't update
+ if(clr != LLColor4(color_swatch->getValue()))
+ {
+ color_swatch->set(LLColor4(sd), TRUE, FALSE);
+ }
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Alpha");
+ if (!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(3);
+ spinner4->setValue(clr.mV[VALPHA]);
+ }
+ break;
+ }
+ case TYPE_COL3:
+ {
+ LLColor3 clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ color_swatch->setValue(sd);
+ break;
+ }
+ case TYPE_COL4U:
+ {
+ LLColor4U clr;
+ clr.setValue(sd);
+ color_swatch->setVisible(TRUE);
+ if(LLColor4(clr) != LLColor4(color_swatch->getValue()))
+ {
+ color_swatch->set(LLColor4(clr), TRUE, FALSE);
+ }
+ spinner4->setVisible(TRUE);
+ spinner4->setLabel("Alpha");
+ if(!spinner4->hasFocus())
+ {
+ spinner4->setPrecision(0);
+ spinner4->setValue(clr.mV[VALPHA]);
+ }
+
+ spinner4->setMinValue(0);
+ spinner4->setMaxValue(255);
+ spinner4->setIncrement(1.f);
+
+ break;
+ }
+ default:
+ mComment->setText("unknown");
+ break;
+ }
+ }
+
+}
diff --git a/indra/newview/llviewercontrol.h b/indra/newview/llviewercontrol.h
new file mode 100644
index 0000000000..a6692df7fb
--- /dev/null
+++ b/indra/newview/llviewercontrol.h
@@ -0,0 +1,60 @@
+/**
+ * @file llviewercontrol.h
+ * @brief references to viewer-specific control files
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERCONTROL_H
+#define LL_LLVIEWERCONTROL_H
+
+#include "llcontrol.h"
+#include "llfloater.h"
+#include "lltexteditor.h"
+
+class LLFloaterSettingsDebug : public LLFloater
+{
+public:
+ LLFloaterSettingsDebug();
+ virtual ~LLFloaterSettingsDebug();
+
+ virtual BOOL postBuild();
+ virtual void draw();
+
+ void updateControl(LLControlBase* control);
+
+ static void show(void*);
+ static void onSettingSelect(LLUICtrl* ctrl, void* user_data);
+ static void onCommitSettings(LLUICtrl* ctrl, void* user_data);
+
+protected:
+ static LLFloaterSettingsDebug* sInstance;
+ LLTextEditor* mComment;
+};
+
+//setting variables are declared in this function
+void declare_settings();
+
+// saved at end of session
+extern LLControlGroup gSavedSettings;
+extern LLControlGroup gSavedPerAccountSettings;
+
+// Read-only
+extern LLControlGroup gViewerArt;
+
+// Read-only
+extern LLControlGroup gColors;
+
+// Saved at end of session
+extern LLControlGroup gCrashSettings;
+
+// Set after settings loaded
+extern LLString gLastRunVersion;
+extern LLString gCurrentVersion;
+
+extern char gSettingsFileName[LL_MAX_PATH];
+extern char gPerAccountSettingsFileName[LL_MAX_PATH];
+extern const char *DEFAULT_SETTINGS_FILE;
+
+#endif // LL_LLVIEWERCONTROL_H
diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp
new file mode 100644
index 0000000000..d027f7dd54
--- /dev/null
+++ b/indra/newview/llviewerdisplay.cpp
@@ -0,0 +1,832 @@
+/**
+ * @file llviewerdisplay.cpp
+ * @brief LLViewerDisplay class implementation
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llcoord.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "lldynamictexture.h"
+#include "lldrawpoolalpha.h"
+#include "llframestats.h"
+#include "llgl.h"
+#include "llglheaders.h"
+#include "llhudmanager.h"
+#include "llimagebmp.h"
+#include "llimagegl.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstartup.h"
+#include "lltoolfocus.h"
+#include "lltoolmgr.h"
+#include "lltooldraganddrop.h"
+#include "lltoolpie.h"
+#include "lltracker.h"
+#include "llui.h"
+#include "llviewercamera.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvograss.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llstartup.h"
+#include "llfasttimer.h"
+#include "llfloatertools.h"
+#include "llviewerimagelist.h"
+#include "llfocusmgr.h"
+
+extern U32 gFrameCount;
+extern LLPointer<LLImageGL> gStartImageGL;
+extern LLPointer<LLImageGL> gDisconnectedImagep;
+extern BOOL gLogoutRequestSent;
+extern LLTimer gLogoutTimer;
+extern BOOL gHaveSavedSnapshot;
+extern BOOL gDisplaySwapBuffers;
+
+// used to toggle renderer back on after teleport
+const F32 TELEPORT_RENDER_DELAY = 20.f; // Max time a teleport is allowed to take before we raise the curtain
+const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
+BOOL gTeleportDisplay = FALSE;
+LLFrameTimer gTeleportDisplayTimer;
+LLFrameTimer gTeleportArrivalTimer;
+const F32 RESTORE_GL_TIME = 5.f; // Wait this long while reloading textures before we raise the curtain
+
+BOOL gForceRenderLandFence = FALSE;
+BOOL gDisplaySwapBuffers = FALSE;
+
+// Rendering stuff
+void pre_show_depth_buffer();
+void post_show_depth_buffer();
+void render_ui_and_swap();
+void render_ui_3d();
+void render_ui_2d();
+void render_disconnected_background();
+
+void process_keystrokes_async(); // in viewer.cpp
+
+void display_startup()
+{
+ if ( !gViewerWindow->getActive()
+ || !gViewerWindow->mWindow->getVisible()
+ || gViewerWindow->mWindow->getMinimized()
+ || gNoRender )
+ {
+ return;
+ }
+
+ LLDynamicTexture::updateAllInstances();
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ LLGLSDefault gls_default;
+ LLGLSUIDefault gls_ui;
+ gPipeline.disableLights();
+
+ gViewerWindow->setup2DRender();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ gViewerWindow->draw();
+ gViewerWindow->mWindow->swapBuffers();
+}
+
+
+void display_update_camera()
+{
+ // TODO: cut draw distance down if customizing avatar?
+ // TODO: cut draw distance on per-parcel basis?
+
+ // Cut draw distance in half when customizing avatar,
+ // but on the viewer only.
+ F32 final_far = gAgent.mDrawDistance;
+ if (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode())
+ {
+ final_far *= 0.5f;
+ }
+ gCamera->setFar(final_far);
+ gViewerWindow->setup3DRender();
+
+ // Update land visibility too
+ if (gWorldp)
+ {
+ gWorldp->setLandFarClip(final_far);
+ }
+}
+
+
+// Paint the display!
+void display(BOOL rebuild, F32 zoom_factor, int subfield)
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER);
+
+ LLGLSDefault gls_default;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE, GL_LEQUAL);
+
+ // No clue where this is getting unset, but safe enough to reset it here.
+ for (S32 j = 7; j >=0; j--)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+j);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB+j);
+ j == 0 ? glEnable(GL_TEXTURE_2D) : glDisable(GL_TEXTURE_2D);
+ }
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+
+ gPipeline.disableLights();
+
+ // Don't draw if the window is hidden or minimized.
+ // In fact, must explicitly check the minimized state before drawing.
+ // Attempting to draw into a minimized window causes a GL error. JC
+ if ( !gViewerWindow->getActive()
+ || !gViewerWindow->mWindow->getVisible()
+ || gViewerWindow->mWindow->getMinimized() )
+ {
+ // Clean up memory the pools may have allocated
+ if (rebuild)
+ {
+ if (!gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::REBUILD);
+ gPipeline.rebuildPools();
+ }
+ }
+ return;
+ }
+
+ gViewerWindow->checkSettings();
+ gViewerWindow->performPick();
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+
+ //////////////////////////////////////////////////////////
+ //
+ // Logic for forcing window updates if we're in drone mode.
+ //
+
+ if (gNoRender)
+ {
+#if LL_WINDOWS
+ static F32 last_update_time = 0.f;
+ if ((gFrameTimeSeconds - last_update_time) > 1.f)
+ {
+ InvalidateRect(llwindow_get_hwnd(gViewerWindow->getWindow()), NULL, FALSE);
+ last_update_time = gFrameTimeSeconds;
+ }
+#elif LL_DARWIN
+ // MBW -- Do something clever here.
+#endif
+ // Not actually rendering, don't bother.
+ return;
+ }
+
+
+ //
+ // Bail out if we're in the startup state and don't want to try to
+ // render the world.
+ //
+ if (gStartupState < STATE_STARTED)
+ {
+ display_startup();
+ return;
+ }
+
+ //LLGLState::verify(FALSE);
+
+ /////////////////////////////////////////////////
+ //
+ // Update GL Texture statistics (used for discard logic?)
+ //
+
+ gFrameStats.start(LLFrameStats::UPDATE_TEX_STATS);
+ stop_glerror();
+
+ LLImageGL::updateStats(gFrameTimeSeconds);
+
+ LLVOAvatar::sRenderName = gSavedSettings.getS32("RenderName");
+ gPipeline.mBackfaceCull = TRUE;
+ gFrameCount++;
+
+ //////////////////////////////////////////////////////////
+ //
+ // Display start screen if we're teleporting, and skip render
+ //
+
+ if (gTeleportDisplay)
+ {
+ const F32 TELEPORT_ARRIVAL_DELAY = 2.f; // Time to preload the world before raising the curtain after we've actually already arrived.
+
+ S32 attach_count = 0;
+ if (gAgent.getAvatarObject())
+ {
+ attach_count = gAgent.getAvatarObject()->getAttachmentCount();
+ }
+ F32 teleport_save_time = TELEPORT_EXPIRY + TELEPORT_EXPIRY_PER_ATTACHMENT * attach_count;
+ F32 teleport_elapsed = gTeleportDisplayTimer.getElapsedTimeF32();
+ F32 teleport_percent = teleport_elapsed * (100.f / teleport_save_time);
+ if( (gAgent.getTeleportState() != LLAgent::TELEPORT_START) && (teleport_percent > 100.f) )
+ {
+ // Give up. Don't keep the UI locked forever.
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ gAgent.setTeleportMessage("");
+ }
+
+ const LLString& message = gAgent.getTeleportMessage();
+ switch( gAgent.getTeleportState() )
+ {
+ case LLAgent::TELEPORT_START:
+ // Transition to REQUESTED. Viewer has sent some kind
+ // of TeleportRequest to the source simulator
+ gTeleportDisplayTimer.reset();
+ gViewerWindow->setShowProgress(TRUE);
+ gViewerWindow->setProgressPercent(0);
+ gAgent.setTeleportState( LLAgent::TELEPORT_REQUESTED );
+ gAgent.setTeleportMessage("Requesting Teleport...");
+ break;
+
+ case LLAgent::TELEPORT_REQUESTED:
+ // Waiting for source simulator to respond
+ gViewerWindow->setProgressPercent( llmin(teleport_percent, 37.5f) );
+ gViewerWindow->setProgressString(message);
+ break;
+
+ case LLAgent::TELEPORT_MOVING:
+ // Viewer has received destination location from source simulator
+ gViewerWindow->setProgressPercent( llmin(teleport_percent, 75.f) );
+ gViewerWindow->setProgressString(message);
+ break;
+
+ case LLAgent::TELEPORT_START_ARRIVAL:
+ // Transition to ARRIVING. Viewer has received avatar update, etc., from destination simulator
+ gTeleportArrivalTimer.reset();
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent(75.f);
+ gAgent.setTeleportState( LLAgent::TELEPORT_ARRIVING );
+ gAgent.setTeleportMessage("Arriving...");
+ gImageList.mForceResetTextureStats = TRUE;
+ break;
+
+ case LLAgent::TELEPORT_ARRIVING:
+ // Make the user wait while content "pre-caches"
+ {
+ F32 arrival_fraction = (gTeleportArrivalTimer.getElapsedTimeF32() / TELEPORT_ARRIVAL_DELAY);
+ if( arrival_fraction > 1.f )
+ {
+ arrival_fraction = 1.f;
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent( arrival_fraction * 25.f + 75.f);
+ gViewerWindow->setProgressString(message);
+ }
+ break;
+
+ case LLAgent::TELEPORT_CANCELLING:
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "Cancel");
+ gViewerWindow->setProgressPercent( 100.f );
+ gViewerWindow->setProgressString("Canceling...");
+ break;
+
+ case LLAgent::TELEPORT_NONE:
+ // No teleport in progress
+ gViewerWindow->setShowProgress(FALSE);
+ gTeleportDisplay = FALSE;
+ break;
+ }
+ }
+ else if(gLogoutRequestSent)
+ {
+ F32 percent_done = gLogoutTimer.getElapsedTimeF32() * 100.f / gLogoutMaxTime;
+ if (percent_done > 100.f)
+ {
+ percent_done = 100.f;
+ }
+
+ if( gQuit )
+ {
+ percent_done = 100.f;
+ }
+
+ gViewerWindow->setProgressPercent( percent_done );
+ }
+ else
+ if (gRestoreGL)
+ {
+ F32 percent_done = gRestoreGLTimer.getElapsedTimeF32() * 100.f / RESTORE_GL_TIME;
+ if( percent_done > 100.f )
+ {
+ gViewerWindow->setShowProgress(FALSE);
+ gRestoreGL = FALSE;
+ }
+ else
+ {
+
+ if( gQuit )
+ {
+ percent_done = 100.f;
+ }
+
+ gViewerWindow->setProgressPercent( percent_done );
+ }
+ }
+
+ //////////////////////////
+ //
+ // Prepare for the next frame
+ //
+
+ // Hmm... Should this be moved elsewhere? - djs 09/09/02
+ // do render-to-texture stuff here
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES))
+ {
+// LLFastTimer t(LLFastTimer::FTM_UPDATE_TEXTURES);
+ if (LLDynamicTexture::updateAllInstances())
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+ }
+
+ if (rebuild)
+ {
+ if (gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::STATE_SORT);
+ gFrameStats.start(LLFrameStats::REBUILD);
+ }
+ }
+
+ /////////////////////////////
+ //
+ // Update the camera
+ //
+ //
+
+ gCamera->setZoomParameters(zoom_factor, subfield);
+
+ //////////////////////////
+ //
+ // clear the next buffer
+ // (must follow dynamic texture writing since that uses the frame buffer)
+ //
+
+ if (gDisconnected)
+ {
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ render_disconnected_background();
+ }
+ else if (!gViewerWindow->isPickPending())
+ {
+ glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
+ }
+ gViewerWindow->setupViewport();
+
+
+ //////////////////////////
+ //
+ // Set rendering options
+ //
+ //
+ stop_glerror();
+ if (gSavedSettings.getBOOL("ShowDepthBuffer"))
+ {
+ pre_show_depth_buffer();
+ }
+
+ if(gUseWireframe)//gSavedSettings.getBOOL("UseWireframe"))
+ {
+ glClearColor(0.5f, 0.5f, 0.5f, 0.f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+ stop_glerror();
+
+ ///////////////////////////////////////
+ //
+ // Slam lighting parameters back to our defaults.
+ // Note that these are not the same as GL defaults...
+
+ stop_glerror();
+ F32 one[4] = {1.f, 1.f, 1.f, 1.f};
+ glLightModelfv (GL_LIGHT_MODEL_AMBIENT,one);
+ stop_glerror();
+
+ //LLGLState::verify();
+
+ /////////////////////////////////////
+ //
+ // Render
+ //
+ // Actually push all of our triangles to the screen.
+ //
+ if (!gDisconnected)
+ {
+ LLFastTimer t(LLFastTimer::FTM_WORLD_UPDATE);
+ stop_glerror();
+ display_update_camera();
+ stop_glerror();
+
+ //FIXME: merge these two methods
+ gHUDManager->updateEffects();
+ LLHUDObject::updateAll();
+ stop_glerror();
+
+ gFrameStats.start(LLFrameStats::UPDATE_GEOM);
+ const F32 max_geom_update_time = 0.005f; // 5 ms update time
+ gPipeline.updateGeom(max_geom_update_time);
+ stop_glerror();
+
+ gFrameStats.start(LLFrameStats::UPDATE_CULL);
+ gPipeline.updateCull();
+ stop_glerror();
+
+ if (rebuild && !gViewerWindow->renderingFastFrame())
+ {
+ LLFastTimer t(LLFastTimer::FTM_REBUILD);
+
+ ///////////////////////////////////
+ //
+ // StateSort
+ //
+ // Responsible for taking visible objects, and adding them to the appropriate draw orders.
+ // In the case of alpha objects, z-sorts them first.
+ // Also creates special lists for outlines and selected face rendering.
+ //
+ gFrameStats.start(LLFrameStats::STATE_SORT);
+ gPipeline.stateSort();
+ stop_glerror();
+
+ //////////////////////////////////////
+ //
+ // rebuildPools
+ //
+ //
+ gFrameStats.start(LLFrameStats::REBUILD);
+ gPipeline.rebuildPools();
+ stop_glerror();
+ }
+ }
+
+ //// render frontmost floater opaque for occlusion culling purposes
+ //LLFloater* frontmost_floaterp = gFloaterView->getFrontmost();
+ //// assumes frontmost floater with focus is opaque
+ //if (frontmost_floaterp && gFocusMgr.childHasKeyboardFocus(frontmost_floaterp))
+ //{
+ // glMatrixMode(GL_MODELVIEW);
+ // glPushMatrix();
+ // {
+ // LLGLSNoTexture gls_no_texture;
+
+ // glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
+ // glLoadIdentity();
+
+ // LLRect floater_rect = frontmost_floaterp->getScreenRect();
+ // // deflate by one pixel so rounding errors don't occlude outside of floater extents
+ // floater_rect.stretch(-1);
+ // LLRectf floater_3d_rect((F32)floater_rect.mLeft / (F32)gViewerWindow->getWindowWidth(),
+ // (F32)floater_rect.mTop / (F32)gViewerWindow->getWindowHeight(),
+ // (F32)floater_rect.mRight / (F32)gViewerWindow->getWindowWidth(),
+ // (F32)floater_rect.mBottom / (F32)gViewerWindow->getWindowHeight());
+ // floater_3d_rect.translate(-0.5f, -0.5f);
+ // glTranslatef(0.f, 0.f, -gCamera->getNear());
+ // glScalef(gCamera->getNear() * gCamera->getAspect() / sinf(gCamera->getView()), gCamera->getNear() / sinf(gCamera->getView()), 1.f);
+ // glColor4fv(LLColor4::white.mV);
+ // glBegin(GL_QUADS);
+ // {
+ // glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mBottom, 0.f);
+ // glVertex3f(floater_3d_rect.mLeft, floater_3d_rect.mTop, 0.f);
+ // glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mTop, 0.f);
+ // glVertex3f(floater_3d_rect.mRight, floater_3d_rect.mBottom, 0.f);
+ // }
+ // glEnd();
+ // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ // }
+ // glPopMatrix();
+ //}
+
+ if (gViewerWindow->renderingFastFrame())
+ {
+ gFrameStats.start(LLFrameStats::RENDER_SYNC);
+ gFrameStats.start(LLFrameStats::RENDER_GEOM);
+ }
+ else if (!(gLogoutRequestSent && gHaveSavedSnapshot)
+ && !gRestoreGL
+ && !gDisconnected)
+ {
+ gPipeline.renderGeom();
+ stop_glerror();
+ }
+
+ gFrameStats.start(LLFrameStats::RENDER_UI);
+
+ if (gHandleKeysAsync)
+ {
+ process_keystrokes_async();
+ stop_glerror();
+ }
+
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ render_ui_and_swap();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+
+ gFrameStats.start(LLFrameStats::MISC_END);
+ stop_glerror();
+
+}
+
+
+void render_ui_and_swap()
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+
+ LLGLSDefault gls_default;
+ {
+ LLGLSUIDefault gls_ui;
+ gPipeline.disableLights();
+
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
+ if (!gViewerWindow->renderingFastFrame() && !gDisconnected)
+ {
+ render_ui_3d();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ }
+
+ render_ui_2d();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+#endif
+ }
+
+ // now do the swap buffer
+ if (gDisplaySwapBuffers)
+ {
+ LLFastTimer t(LLFastTimer::FTM_SWAP);
+ gViewerWindow->mWindow->swapBuffers();
+ }
+ }
+
+ gViewerWindow->finishFirstFastFrame();
+}
+
+void render_ui_3d()
+{
+ LLGLSPipeline gls_pipeline;
+
+ //////////////////////////////////////
+ //
+ // Render 3D UI elements
+ // NOTE: zbuffer is cleared before we get here by LLDrawPoolHUD,
+ // so 3d elements requiring Z buffer are moved to LLDrawPoolHUD
+ //
+
+ // Render selections
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+
+ /////////////////////////////////////////////////////////////
+ //
+ // Render 2.5D elements (2D elements in the world)
+ // Stuff without z writes
+ //
+
+ // Debugging stuff goes before the UI.
+
+ if (gSavedSettings.getBOOL("ShowDepthBuffer"))
+ {
+ post_show_depth_buffer();
+ }
+
+ // Coordinate axes
+ if (gSavedSettings.getBOOL("ShowAxes"))
+ {
+ draw_axes();
+ }
+
+ stop_glerror();
+
+ gViewerWindow->renderSelections(FALSE, FALSE, TRUE); // Non HUD call in render_hud_elements
+ stop_glerror();
+}
+
+void render_ui_2d()
+{
+ LLGLSUIDefault gls_ui;
+
+ /////////////////////////////////////////////////////////////
+ //
+ // Render 2D UI elements that overlay the world (no z compare)
+
+ // Disable wireframe mode below here, as this is HUD/menus
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ // Menu overlays, HUD, etc
+ gViewerWindow->setup2DRender();
+
+ F32 zoom_factor = gCamera->getZoomFactor();
+ S16 sub_region = gCamera->getZoomSubRegion();
+
+ if (zoom_factor > 1.f)
+ {
+ //decompose subregion number to x and y values
+ 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->getWindowWidth() * (F32)pos_x / zoom_factor);
+ LLFontGL::sCurOrigin.mY -= llround((F32)gViewerWindow->getWindowHeight() * (F32)pos_y / zoom_factor);
+ }
+
+
+ stop_glerror();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // render outline for HUD
+ if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mHUDCurZoom < 0.98f)
+ {
+ glPushMatrix();
+ S32 half_width = (gViewerWindow->getWindowWidth() / 2);
+ S32 half_height = (gViewerWindow->getWindowHeight() / 2);
+ glTranslatef((F32)half_width, (F32)half_height, 0.f);
+ glScalef(gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom, gAgent.getAvatarObject()->mHUDCurZoom);
+ glColor4fv(LLColor4::white.mV);
+ gl_rect_2d(-half_width, half_height, half_width, -half_height, FALSE);
+ glPopMatrix();
+ stop_glerror();
+ }
+ gViewerWindow->draw();
+ if (gDebugSelect)
+ {
+ gViewerWindow->drawPickBuffer();
+ }
+
+ // reset current origin for font rendering, in case of tiling render
+ LLFontGL::sCurOrigin.set(0, 0);
+}
+
+void renderCoordinateAxes()
+{
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_LINES);
+ glColor3f(1.0f, 0.0f, 0.0f); // i direction = X-Axis = red
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(2.0f, 0.0f, 0.0f);
+ glVertex3f(3.0f, 0.0f, 0.0f);
+ glVertex3f(5.0f, 0.0f, 0.0f);
+ glVertex3f(6.0f, 0.0f, 0.0f);
+ glVertex3f(8.0f, 0.0f, 0.0f);
+ // Make an X
+ glVertex3f(11.0f, 1.0f, 1.0f);
+ glVertex3f(11.0f, -1.0f, -1.0f);
+ glVertex3f(11.0f, 1.0f, -1.0f);
+ glVertex3f(11.0f, -1.0f, 1.0f);
+
+ glColor3f(0.0f, 1.0f, 0.0f); // j direction = Y-Axis = green
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 2.0f, 0.0f);
+ glVertex3f(0.0f, 3.0f, 0.0f);
+ glVertex3f(0.0f, 5.0f, 0.0f);
+ glVertex3f(0.0f, 6.0f, 0.0f);
+ glVertex3f(0.0f, 8.0f, 0.0f);
+ // Make a Y
+ glVertex3f(1.0f, 11.0f, 1.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(-1.0f, 11.0f, 1.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(0.0f, 11.0f, 0.0f);
+ glVertex3f(0.0f, 11.0f, -1.0f);
+
+ glColor3f(0.0f, 0.0f, 1.0f); // Z-Axis = blue
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.0f, 2.0f);
+ glVertex3f(0.0f, 0.0f, 3.0f);
+ glVertex3f(0.0f, 0.0f, 5.0f);
+ glVertex3f(0.0f, 0.0f, 6.0f);
+ glVertex3f(0.0f, 0.0f, 8.0f);
+ // Make a Z
+ glVertex3f(-1.0f, 1.0f, 11.0f);
+ glVertex3f(1.0f, 1.0f, 11.0f);
+ glVertex3f(1.0f, 1.0f, 11.0f);
+ glVertex3f(-1.0f, -1.0f, 11.0f);
+ glVertex3f(-1.0f, -1.0f, 11.0f);
+ glVertex3f(1.0f, -1.0f, 11.0f);
+ glEnd();
+}
+
+void draw_axes()
+{
+ LLGLSUIDefault gls_ui;
+ LLGLSNoTexture gls_no_texture;
+ // A vertical white line at origin
+ LLVector3 v = gAgent.getPositionAgent();
+ glBegin(GL_LINES);
+ glColor3f(1.0f, 1.0f, 1.0f);
+ glVertex3f(0.0f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.0f, 40.0f);
+ glEnd();
+ // Some coordinate axes
+ glPushMatrix();
+ glTranslatef( v.mV[VX], v.mV[VY], v.mV[VZ] );
+ renderCoordinateAxes();
+ glPopMatrix();
+}
+
+
+void render_disconnected_background()
+{
+ if (!gDisconnectedImagep && gDisconnected)
+ {
+ llinfos << "Loading last bitmap..." << llendl;
+
+ char temp_str[MAX_PATH];
+ strcpy(temp_str, gDirUtilp->getLindenUserDir().c_str());
+ strcat(temp_str, gDirUtilp->getDirDelimiter().c_str());
+
+ strcat(temp_str, SCREEN_LAST_FILENAME);
+
+ LLPointer<LLImageBMP> image_bmp = new LLImageBMP;
+ if( !image_bmp->load(temp_str) )
+ {
+ //llinfos << "Bitmap load failed" << llendl;
+ return;
+ }
+
+ gDisconnectedImagep = new LLImageGL( FALSE );
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ if (!image_bmp->decode(raw))
+ {
+ llinfos << "Bitmap decode failed" << llendl;
+ gDisconnectedImagep = NULL;
+ return;
+ }
+
+ U8 *rawp = raw->getData();
+ S32 npixels = (S32)image_bmp->getWidth()*(S32)image_bmp->getHeight();
+ for (S32 i = 0; i < npixels; i++)
+ {
+ S32 sum = 0;
+ sum = *rawp + *(rawp+1) + *(rawp+2);
+ sum /= 3;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ *rawp = ((S32)sum*6 + *rawp)/7;
+ rawp++;
+ }
+
+
+ raw->expandToPowerOfTwo();
+ gDisconnectedImagep->createGLTexture(0, raw);
+ gStartImageGL = gDisconnectedImagep;
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+
+ // Make sure the progress view always fills the entire window.
+ S32 width = gViewerWindow->getWindowWidth();
+ S32 height = gViewerWindow->getWindowHeight();
+
+ if (gDisconnectedImagep)
+ {
+ LLGLSUIDefault gls_ui;
+ gViewerWindow->setup2DRender();
+ glPushMatrix();
+ {
+ // scale ui to reflect UIScaleFactor
+ // this can't be done in setup2DRender because it requires a
+ // pushMatrix/popMatrix pair
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+
+ LLViewerImage::bindTexture(gDisconnectedImagep);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ gl_rect_2d_simple_tex(width, height);
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ }
+ glPopMatrix();
+ }
+}
diff --git a/indra/newview/llviewerdisplay.h b/indra/newview/llviewerdisplay.h
new file mode 100644
index 0000000000..6dd3e51efd
--- /dev/null
+++ b/indra/newview/llviewerdisplay.h
@@ -0,0 +1,17 @@
+/**
+ * @file llviewerdisplay.h
+ * @brief LLViewerDisplay class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERDISPLAY_H
+#define LL_LLVIEWERDISPLAY_H
+
+void display_startup();
+
+extern BOOL gDisplaySwapBuffers;
+
+
+#endif // LL_LLVIEWERDISPLAY_H
diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp
new file mode 100644
index 0000000000..6710f8c00c
--- /dev/null
+++ b/indra/newview/llviewergesture.cpp
@@ -0,0 +1,260 @@
+/**
+ * @file llviewergesture.cpp
+ * @brief LLViewerGesture class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewergesture.h"
+
+#include "audioengine.h"
+#include "lldir.h"
+#include "llviewerinventory.h"
+#include "sound_ids.h" // for testing
+
+#include "llchatbar.h"
+#include "llkeyboard.h" // for key shortcuts for testing
+#include "llinventorymodel.h"
+#include "llvoavatar.h"
+#include "llxfermanager.h"
+#include "llviewermessage.h" // send_guid_sound_trigger
+#include "llviewernetwork.h"
+#include "llagent.h"
+
+// Globals
+LLViewerGestureList gGestureList;
+
+const F32 LLViewerGesture::SOUND_VOLUME = 1.f;
+
+LLViewerGesture::LLViewerGesture()
+: LLGesture()
+{ }
+
+LLViewerGesture::LLViewerGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id,
+ const std::string &animation,
+ const std::string &output_string)
+: LLGesture(key, mask, trigger, sound_item_id, animation, output_string)
+{
+}
+
+LLViewerGesture::LLViewerGesture(U8 **buffer, S32 max_size)
+: LLGesture(buffer, max_size)
+{
+}
+
+LLViewerGesture::LLViewerGesture(const LLViewerGesture &rhs)
+: LLGesture((LLGesture)rhs)
+{
+}
+
+BOOL LLViewerGesture::trigger(KEY key, MASK mask)
+{
+ if (mKey == key && mMask == mask)
+ {
+ doTrigger( TRUE );
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+BOOL LLViewerGesture::trigger(const std::string &trigger_string)
+{
+ // Assumes trigger_string is lowercase
+ if (mTriggerLower == trigger_string)
+ {
+ doTrigger( FALSE );
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+// private
+void LLViewerGesture::doTrigger( BOOL send_chat )
+{
+ if (mSoundItemID != LLUUID::null)
+ {
+ LLViewerInventoryItem *item;
+ item = gInventory.getItem(mSoundItemID);
+ if (item)
+ {
+ send_sound_trigger(item->getAssetUUID(), SOUND_VOLUME);
+ }
+ }
+
+ if (!mAnimation.empty())
+ {
+ // AFK animations trigger the special "away" state, which
+ // includes agent control settings. JC
+ if (mAnimation == "enter_away_from_keyboard_state" || mAnimation == "away")
+ {
+ gAgent.setAFK();
+ }
+ else
+ {
+ LLUUID anim_id = gAnimLibrary.stringToAnimState(mAnimation.c_str());
+ gAgent.sendAnimationRequest(anim_id, ANIM_REQUEST_START);
+ }
+ }
+
+ if ( send_chat && !mOutputString.empty())
+ {
+ // Don't play nodding animation, since that might not blend
+ // with the gesture animation.
+ gChatBar->sendChatFromViewer(mOutputString, CHAT_TYPE_NORMAL, FALSE);
+ }
+}
+
+
+LLViewerGestureList::LLViewerGestureList()
+: LLGestureList()
+{
+ mIsLoaded = FALSE;
+}
+
+void LLViewerGestureList::saveToServer()
+{
+ U8 *buffer = new U8[getMaxSerialSize()];
+
+ U8 *end = serialize(buffer);
+
+ if (end - buffer > getMaxSerialSize())
+ {
+ llerrs << "Wrote off end of buffer, serial size computation is wrong" << llendl;
+ }
+
+ //U64 xfer_id = gXferManager->registerXfer(buffer, end - buffer);
+ // write to a file because mem<->mem xfer isn't implemented
+ LLUUID random_uuid;
+ char filename[LL_MAX_PATH];
+ random_uuid.generate();
+ random_uuid.toString(filename);
+ strcat(filename,".tmp");
+
+ char filename_and_path[LL_MAX_PATH];
+ sprintf(filename_and_path, "%s%s%s",
+ gDirUtilp->getTempDir().c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ filename);
+
+ FILE *fp = LLFile::fopen(filename_and_path, "wb");
+
+ if (fp)
+ {
+ fwrite(buffer, end - buffer, 1, fp);
+ fclose(fp);
+
+ gMessageSystem->newMessageFast(_PREHASH_GestureUpdate);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addStringFast(_PREHASH_Filename, filename);
+ gMessageSystem->addBOOLFast(_PREHASH_ToViewer, FALSE);
+ gMessageSystem->sendReliable(gUserServer);
+ }
+
+ delete[] buffer;
+}
+
+/*
+void LLViewerGestureList::requestFromServer()
+{
+ gMessageSystem->newMessageFast(_PREHASH_GestureRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ gMessageSystem->addU8("Reset", 0);
+ gMessageSystem->sendReliable(gUserServer);
+}
+
+void LLViewerGestureList::requestResetFromServer( BOOL is_male )
+{
+ gMessageSystem->newMessageFast(_PREHASH_GestureRequest);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentBlock);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ gMessageSystem->addU8("Reset", is_male ? 1 : 2);
+ gMessageSystem->sendReliable(gUserServer);
+ mIsLoaded = FALSE;
+}
+*/
+
+// helper for deserialize that creates the right LLGesture subclass
+LLGesture *LLViewerGestureList::create_gesture(U8 **buffer, S32 max_size)
+{
+ return new LLViewerGesture(buffer, max_size);
+}
+
+
+// See if the prefix matches any gesture. If so, return TRUE
+// and place the full text of the gesture trigger into
+// output_str
+BOOL LLViewerGestureList::matchPrefix(const std::string& in_str, std::string* out_str)
+{
+ S32 in_len = in_str.length();
+
+ LLString in_str_lc = in_str;
+ LLString::toLower(in_str_lc);
+
+ for (S32 i = 0; i < count(); i++)
+ {
+ LLGesture* gesture = get(i);
+ const std::string &trigger = gesture->getTrigger();
+
+ if (in_len > (S32)trigger.length())
+ {
+ // too short, bail out
+ continue;
+ }
+
+ std::string trigger_trunc = utf8str_truncate(trigger, in_len);
+ LLString::toLower(trigger_trunc);
+ if (in_str_lc == trigger_trunc)
+ {
+ *out_str = trigger;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+// static
+void LLViewerGestureList::xferCallback(void *data, S32 size, void** /*user_data*/, S32 status)
+{
+ if (LL_ERR_NOERR == status)
+ {
+ U8 *buffer = (U8 *)data;
+ U8 *end = gGestureList.deserialize(buffer, size);
+
+ if (end - buffer > size)
+ {
+ llerrs << "Read off of end of array, error in serialization" << llendl;
+ }
+
+ gGestureList.mIsLoaded = TRUE;
+ }
+ else
+ {
+ llwarns << "Unable to load gesture list!" << llendl;
+ }
+}
+
+// static
+void LLViewerGestureList::processGestureUpdate(LLMessageSystem *msg, void** /*user_data*/)
+{
+ char remote_filename[MAX_STRING];
+ msg->getStringFast(_PREHASH_AgentBlock, _PREHASH_Filename, MAX_STRING, remote_filename);
+
+
+ gXferManager->requestFile(remote_filename, LL_PATH_CACHE, msg->getSender(), TRUE, xferCallback, NULL,
+ LLXferManager::HIGH_PRIORITY);
+}
diff --git a/indra/newview/llviewergesture.h b/indra/newview/llviewergesture.h
new file mode 100644
index 0000000000..ced12a7c34
--- /dev/null
+++ b/indra/newview/llviewergesture.h
@@ -0,0 +1,71 @@
+/**
+ * @file llviewergesture.h
+ * @brief LLViewerGesture class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERGESTURE_H
+#define LL_LLVIEWERGESTURE_H
+
+#include "llanimationstates.h"
+#include "lluuid.h"
+#include "llstring.h"
+#include "lldarray.h"
+#include "llgesture.h"
+
+class LLMessageSystem;
+
+class LLViewerGesture : public LLGesture
+{
+public:
+ LLViewerGesture();
+ LLViewerGesture(KEY key, MASK mask, const std::string &trigger,
+ const LLUUID &sound_item_id, const std::string &animation,
+ const std::string &output_string);
+
+ LLViewerGesture(U8 **buffer, S32 max_size); // deserializes, advances buffer
+ LLViewerGesture(const LLViewerGesture &gesture);
+
+ // Triggers if a key/mask matches it
+ virtual BOOL trigger(KEY key, MASK mask);
+
+ // Triggers if case-insensitive substring matches (assumes string is lowercase)
+ virtual BOOL trigger(const std::string &string);
+
+ void doTrigger( BOOL send_chat );
+
+protected:
+ static const F32 SOUND_VOLUME;
+};
+
+class LLViewerGestureList : public LLGestureList
+{
+public:
+ LLViewerGestureList();
+
+ void saveToServer();
+ //void requestFromServer();
+ BOOL getIsLoaded() { return mIsLoaded; }
+
+ //void requestResetFromServer( BOOL is_male );
+
+ // See if the prefix matches any gesture. If so, return TRUE
+ // and place the full text of the gesture trigger into
+ // output_str
+ BOOL matchPrefix(const std::string& in_str, std::string* out_str);
+
+ static void xferCallback(void *data, S32 size, void** /*user_data*/, S32 status);
+ static void processGestureUpdate(LLMessageSystem *msg, void** /*user_data*/);
+
+protected:
+ LLGesture *create_gesture(U8 **buffer, S32 max_size);
+
+protected:
+ BOOL mIsLoaded;
+};
+
+extern LLViewerGestureList gGestureList;
+
+#endif
diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp
new file mode 100644
index 0000000000..9c2638362e
--- /dev/null
+++ b/indra/newview/llviewerinventory.cpp
@@ -0,0 +1,705 @@
+/**
+ * @file llviewerinventory.cpp
+ * @brief Implementation of the viewer side inventory objects.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerinventory.h"
+
+#include "message.h"
+#include "indra_constants.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "llconsole.h"
+#include "llinventorymodel.h"
+#include "llnotify.h"
+#include "llimview.h"
+#include "viewer.h"
+#include "llgesturemgr.h"
+
+#include "llinventoryview.h"
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+///----------------------------------------------------------------------------
+/// Class LLViewerInventoryItem
+///----------------------------------------------------------------------------
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ const LLPermissions& perm,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc) :
+ LLInventoryItem(uuid, parent_uuid, perm, asset_uuid, type, inv_type,
+ name, desc, sale_info, flags, creation_date_utc),
+ mIsComplete(TRUE)
+{
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const char* name,
+ LLInventoryType::EType inv_type) :
+ LLInventoryItem(),
+ mIsComplete(FALSE)
+{
+ mUUID = item_id;
+ mParentUUID = parent_id;
+ mInventoryType = inv_type;
+ mName.assign(name);
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem() :
+ LLInventoryItem(),
+ mIsComplete(FALSE)
+{
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLViewerInventoryItem* other) :
+ LLInventoryItem()
+{
+ copy(other);
+ if (!mIsComplete)
+ {
+ llwarns << "LLViewerInventoryItem copy constructor for incomplete item"
+ << mUUID << llendl;
+ }
+}
+
+LLViewerInventoryItem::LLViewerInventoryItem(const LLInventoryItem *other) :
+ LLInventoryItem(other)
+{
+ LLInventoryItem::copy(other);
+ if (!mIsComplete)
+ {
+ llwarns << "LLViewerInventoryItem copy constructor for incomplete item"
+ << mUUID << llendl;
+ }
+}
+
+
+LLViewerInventoryItem::~LLViewerInventoryItem()
+{
+}
+
+// virtual
+void LLViewerInventoryItem::copy(const LLViewerInventoryItem* other)
+{
+ LLInventoryItem::copy(other);
+ mIsComplete = other->mIsComplete;
+ mTransactionID = other->mTransactionID;
+}
+
+void LLViewerInventoryItem::copy(const LLInventoryItem *other)
+{
+ LLInventoryItem::copy(other);
+ mIsComplete = true;
+ mTransactionID.setNull();
+}
+
+// virtual
+void LLViewerInventoryItem::clone(LLPointer<LLViewerInventoryItem>& newitem) const
+{
+ newitem = new LLViewerInventoryItem(this);
+ if(newitem.notNull())
+ {
+ LLUUID item_id;
+ item_id.generate();
+ newitem->setUUID(item_id);
+ }
+}
+
+void LLViewerInventoryItem::removeFromServer()
+{
+ llinfos << "Removing inventory item " << mUUID << " from server."
+ << llendl;
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryItem::updateServer(BOOL is_new) const
+{
+ if(!mIsComplete)
+ {
+ // *FIX: deal with this better.
+ // If we're crashing here then the UI is incorrectly enabled.
+ llerrs << "LLViewerInventoryItem::updateServer() - for incomplete item"
+ << llendl;
+ return;
+ }
+ if(gAgent.getID() != mPermissions.getOwner())
+ {
+ // *FIX: deal with this better.
+ llwarns << "LLViewerInventoryItem::updateServer() - for unowned item"
+ << llendl;
+ return;
+ }
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_CallbackID, 0);
+ packMessage(msg);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryItem::fetchFromServer(void) const
+{
+ if(!mIsComplete)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("FetchInventory");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("OwnerID", mPermissions.getOwner());
+ msg->addUUID("ItemID", mUUID);
+ gAgent.sendReliableMessage();
+ }
+ else
+ {
+ // *FIX: this can be removed after a bit.
+ llwarns << "request to fetch complete item" << llendl;
+ }
+}
+
+// virtual
+BOOL LLViewerInventoryItem::unpackMessage(
+ LLMessageSystem* msg, const char* block, S32 block_num)
+{
+ BOOL rv = LLInventoryItem::unpackMessage(msg, block, block_num);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+void LLViewerInventoryItem::setTransactionID(const LLTransactionID& transaction_id)
+{
+ mTransactionID = transaction_id;
+}
+// virtual
+void LLViewerInventoryItem::packMessage(LLMessageSystem* msg) const
+{
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
+ mPermissions.packMessage(msg);
+ msg->addUUIDFast(_PREHASH_TransactionID, mTransactionID);
+ S8 type = static_cast<S8>(mType);
+ msg->addS8Fast(_PREHASH_Type, type);
+ type = static_cast<S8>(mInventoryType);
+ msg->addS8Fast(_PREHASH_InvType, type);
+ msg->addU32Fast(_PREHASH_Flags, mFlags);
+ mSaleInfo.packMessage(msg);
+ msg->addStringFast(_PREHASH_Name, mName);
+ msg->addStringFast(_PREHASH_Description, mDescription);
+ msg->addS32Fast(_PREHASH_CreationDate, mCreationDate);
+ U32 crc = getCRC32();
+ msg->addU32Fast(_PREHASH_CRC, crc);
+}
+// virtual
+BOOL LLViewerInventoryItem::importFile(FILE* fp)
+{
+ BOOL rv = LLInventoryItem::importFile(fp);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+// virtual
+BOOL LLViewerInventoryItem::importLegacyStream(std::istream& input_stream)
+{
+ BOOL rv = LLInventoryItem::importLegacyStream(input_stream);
+ mIsComplete = TRUE;
+ return rv;
+}
+
+bool LLViewerInventoryItem::importFileLocal(FILE* fp)
+{
+ // TODO: convert all functions that return BOOL to return bool
+ bool rv = (LLInventoryItem::importFile(fp) ? true : false);
+ mIsComplete = false;
+ return rv;
+}
+
+bool LLViewerInventoryItem::exportFileLocal(FILE* fp) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_item\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\titem_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ mPermissions.exportFile(fp);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ const char* inv_type_str = LLInventoryType::lookup(mInventoryType);
+ if(inv_type_str) fprintf(fp, "\t\tinv_type\t%s\n", inv_type_str);
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ fprintf(fp, "\t\tcreation_date\t%d\n", mCreationDate);
+ fprintf(fp,"\t}\n");
+ return true;
+}
+
+void LLViewerInventoryItem::updateParentOnServer(BOOL restamp) const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_Stamp, restamp);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, mUUID);
+ msg->addUUIDFast(_PREHASH_FolderID, mParentUUID);
+ msg->addString("NewName", NULL);
+ gAgent.sendReliableMessage();
+}
+
+//void LLViewerInventoryItem::setCloneCount(S32 clones)
+//{
+// mClones = clones;
+//}
+
+//S32 LLViewerInventoryItem::getCloneCount() const
+//{
+// return mClones;
+//}
+
+///----------------------------------------------------------------------------
+/// Class LLViewerInventoryCategory
+///----------------------------------------------------------------------------
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& uuid,
+ const LLUUID& parent_uuid,
+ LLAssetType::EType pref,
+ const LLString& name,
+ const LLUUID& owner_id) :
+ LLInventoryCategory(uuid, parent_uuid, pref, name),
+ mOwnerID(owner_id),
+ mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+{
+ mDescendentsRequested.reset();
+}
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLUUID& owner_id) :
+ mOwnerID(owner_id),
+ mVersion(LLViewerInventoryCategory::VERSION_UNKNOWN),
+ mDescendentCount(LLViewerInventoryCategory::DESCENDENT_COUNT_UNKNOWN)
+{
+ mDescendentsRequested.reset();
+}
+
+LLViewerInventoryCategory::LLViewerInventoryCategory(const LLViewerInventoryCategory* other)
+{
+ copy(other);
+}
+
+LLViewerInventoryCategory::~LLViewerInventoryCategory()
+{
+}
+
+// virtual
+void LLViewerInventoryCategory::copy(const LLViewerInventoryCategory* other)
+{
+ LLInventoryCategory::copy(other);
+ mOwnerID = other->mOwnerID;
+ mVersion = other->mVersion;
+ mDescendentCount = other->mDescendentCount;
+ mDescendentsRequested = other->mDescendentsRequested;
+}
+
+
+void LLViewerInventoryCategory::updateParentOnServer(BOOL restamp) const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->addBOOL("Stamp", restamp);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_FolderID, mUUID);
+ msg->addUUIDFast(_PREHASH_ParentID, mParentUUID);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryCategory::updateServer(BOOL is_new) const
+{
+ // communicate that change with the server.
+ if(LLAssetType::AT_NONE != mPreferredType)
+ {
+ LLNotifyBox::showXml("CannotModifyProtectedCategories");
+ return;
+ }
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, is_new ? 1 : 0);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ packMessage(msg);
+ gAgent.sendReliableMessage();
+}
+
+void LLViewerInventoryCategory::removeFromServer( void )
+{
+ llinfos << "Removing inventory category " << mUUID << " from server."
+ << llendl;
+ // communicate that change with the server.
+ if(LLAssetType::AT_NONE != mPreferredType)
+ {
+ LLNotifyBox::showXml("CannotRemoveProtectedCategories");
+ return;
+ }
+
+ LLInventoryModel::LLCategoryUpdate up(mParentUUID, -1);
+ gInventory.accountForUpdate(up);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveInventoryFolder);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, mUUID);
+ gAgent.sendReliableMessage();
+}
+
+bool LLViewerInventoryCategory::fetchDescendents()
+{
+ if((VERSION_UNKNOWN == mVersion)
+ && mDescendentsRequested.hasExpired())
+ {
+ const F32 FETCH_TIMER_EXPIRY = 10.0f;
+ mDescendentsRequested.reset();
+ mDescendentsRequested.setTimerExpirySec(FETCH_TIMER_EXPIRY);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("FetchInventoryDescendents");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("InventoryData");
+ msg->addUUID("FolderID", mUUID);
+ msg->addUUID("OwnerID", mOwnerID);
+ // bitfield
+ // 1 = by date
+ // 2 = folders by date
+ // Need to mask off anything but the first bit.
+ // This comes from LLInventoryFilter from llfolderview.h
+ U32 sort_order = gSavedSettings.getU32("InventorySortOrder") & 0x1;
+ msg->addS32("SortOrder", sort_order);
+ msg->addBOOL("FetchFolders", FALSE);
+ msg->addBOOL("FetchItems", TRUE);
+ gAgent.sendReliableMessage();
+ return true;
+ }
+ return false;
+}
+
+bool LLViewerInventoryCategory::importFileLocal(FILE* fp)
+{
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ char valuestr[MAX_STRING];
+
+ keyword[0] = '\0';
+ valuestr[0] = '\0';
+ while(!feof(fp))
+ {
+ fgets(buffer, MAX_STRING, fp);
+ sscanf(buffer, " %s %s", keyword, valuestr);
+ if(!keyword)
+ {
+ continue;
+ }
+ if(0 == strcmp("{",keyword))
+ {
+ continue;
+ }
+ if(0 == strcmp("}", keyword))
+ {
+ break;
+ }
+ else if(0 == strcmp("cat_id", keyword))
+ {
+ mUUID.set(valuestr);
+ }
+ else if(0 == strcmp("parent_id", keyword))
+ {
+ mParentUUID.set(valuestr);
+ }
+ else if(0 == strcmp("type", keyword))
+ {
+ mType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("pref_type", keyword))
+ {
+ mPreferredType = LLAssetType::lookup(valuestr);
+ }
+ else if(0 == strcmp("name", keyword))
+ {
+ //strcpy(valuestr, buffer + strlen(keyword) + 3);
+ // *NOTE: Not ANSI C, but widely supported.
+ sscanf(buffer, " %s %[^|]", keyword, valuestr);
+ mName.assign(valuestr);
+ LLString::replaceNonstandardASCII(mName, ' ');
+ LLString::replaceChar(mName, '|', ' ');
+ }
+ else if(0 == strcmp("owner_id", keyword))
+ {
+ mOwnerID.set(valuestr);
+ }
+ else if(0 == strcmp("version", keyword))
+ {
+ sscanf(valuestr, "%d", &mVersion);
+ }
+ else
+ {
+ llwarns << "unknown keyword '" << keyword
+ << "' in inventory import category " << mUUID << llendl;
+ }
+ }
+ return true;
+}
+
+bool LLViewerInventoryCategory::exportFileLocal(FILE* fp) const
+{
+ char uuid_str[UUID_STR_LENGTH];
+ fprintf(fp, "\tinv_category\t0\n\t{\n");
+ mUUID.toString(uuid_str);
+ fprintf(fp, "\t\tcat_id\t%s\n", uuid_str);
+ mParentUUID.toString(uuid_str);
+ fprintf(fp, "\t\tparent_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\ttype\t%s\n", LLAssetType::lookup(mType));
+ fprintf(fp, "\t\tpref_type\t%s\n", LLAssetType::lookup(mPreferredType));
+ fprintf(fp, "\t\tname\t%s|\n", mName.c_str());
+ mOwnerID.toString(uuid_str);
+ fprintf(fp, "\t\towner_id\t%s\n", uuid_str);
+ fprintf(fp, "\t\tversion\t%d\n", mVersion);
+ fprintf(fp,"\t}\n");
+ return true;
+}
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
+
+/*
+void inventory_reliable_callback(void**, S32 status)
+{
+ if(0 != status)
+ {
+ char buffer[MAX_STRING];
+ sprintf(buffer, "Reliable packet failure: %d", status);
+ llwarns << buffer << llendl;
+ }
+}
+*/
+LLInventoryCallbackManager::LLInventoryCallbackManager() :
+ mLastCallback(0)
+{
+}
+
+LLInventoryCallbackManager::~LLInventoryCallbackManager()
+{
+}
+
+U32 LLInventoryCallbackManager::registerCB(LLPointer<LLInventoryCallback> cb)
+{
+ if (cb.isNull())
+ return 0;
+
+ mLastCallback++;
+ if (!mLastCallback)
+ mLastCallback++;
+
+ mMap[mLastCallback] = cb;
+ return mLastCallback;
+}
+
+void LLInventoryCallbackManager::fire(U32 callback_id, const LLUUID& item_id)
+{
+ if (!callback_id)
+ return;
+
+ std::map<U32, LLPointer<LLInventoryCallback> >::iterator i;
+
+ i = mMap.find(callback_id);
+ if (i != mMap.end())
+ {
+ (*i).second->fire(item_id);
+ mMap.erase(i);
+ }
+}
+
+void WearOnAvatarCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ LLViewerInventoryItem *item = gInventory.getItem(inv_item);
+ if (item)
+ {
+ wear_inventory_item_on_avatar(item);
+ }
+}
+
+RezAttachmentCallback::RezAttachmentCallback(LLViewerJointAttachment *attachmentp)
+{
+ mAttach = attachmentp;
+}
+RezAttachmentCallback::~RezAttachmentCallback()
+{
+}
+
+void RezAttachmentCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ LLViewerInventoryItem *item = gInventory.getItem(inv_item);
+ if (item)
+ {
+ rez_attachment(item, mAttach);
+ }
+}
+
+extern LLGestureManager gGestureManager;
+void ActivateGestureCallback::fire(const LLUUID& inv_item)
+{
+ if (inv_item.isNull())
+ return;
+
+ gGestureManager.activateGesture(inv_item);
+}
+
+LLInventoryCallbackManager gInventoryCallbacks;
+
+void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
+ const LLUUID& parent, const LLTransactionID& transaction_id,
+ const std::string& name,
+ const std::string& desc, LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type, EWearableType wtype,
+ U32 next_owner_perm,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CreateInventoryItem);
+ msg->nextBlock(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->nextBlock(_PREHASH_InventoryBlock);
+ msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
+ msg->addUUIDFast(_PREHASH_FolderID, parent);
+ msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
+ msg->addU32Fast(_PREHASH_NextOwnerMask, next_owner_perm);
+ msg->addS8Fast(_PREHASH_Type, (S8)asset_type);
+ msg->addS8Fast(_PREHASH_InvType, (S8)inv_type);
+ msg->addU8Fast(_PREHASH_WearableType, (U8)wtype);
+ msg->addStringFast(_PREHASH_Name, name);
+ msg->addStringFast(_PREHASH_Description, desc);
+
+ gAgent.sendReliableMessage();
+}
+
+void copy_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& current_owner,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CopyInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_CallbackID, gInventoryCallbacks.registerCB(cb));
+ msg->addUUIDFast(_PREHASH_OldAgentID, current_owner);
+ msg->addUUIDFast(_PREHASH_OldItemID, item_id);
+ msg->addUUIDFast(_PREHASH_NewFolderID, parent_id);
+ msg->addString("NewName", new_name);
+ gAgent.sendReliableMessage();
+}
+
+void move_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveInventoryItem);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_id);
+ msg->addUUIDFast(_PREHASH_SessionID, session_id);
+ msg->addBOOLFast(_PREHASH_Stamp, false);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->addUUIDFast(_PREHASH_FolderID, parent_id);
+ msg->addString("NewName", new_name);
+ gAgent.sendReliableMessage();
+}
+
+void _copy_inventory_from_notecard_hdr(const LLUUID& object_id, const LLUUID& notecard_inv_id)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CopyInventoryFromNotecard);
+ msg->nextBlock(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock(_PREHASH_NotecardData);
+ msg->addUUIDFast(_PREHASH_NotecardItemID, notecard_inv_id);
+ msg->addUUIDFast(_PREHASH_ObjectID, object_id);
+}
+
+void copy_inventory_from_notecard(const LLUUID& object_id, const LLUUID& notecard_inv_id, const LLInventoryItem *src)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ /*
+ * I was going to handle multiple inventory items here, but then I realized that
+ * we are only handling one at a time. Perhaps you can only select one at a
+ * time from the notecard?
+ */
+ _copy_inventory_from_notecard_hdr(object_id, notecard_inv_id);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addUUIDFast(_PREHASH_ItemID, src->getUUID());
+ msg->addUUIDFast(_PREHASH_FolderID, gInventory.findCategoryUUIDForType(src->getType()));
+ gAgent.sendReliableMessage();
+}
diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h
new file mode 100644
index 0000000000..2668e1121c
--- /dev/null
+++ b/indra/newview/llviewerinventory.h
@@ -0,0 +1,253 @@
+/**
+ * @file llviewerinventory.h
+ * @brief Declaration of the inventory bits that only used on the viewer.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERINVENTORY_H
+#define LL_LLVIEWERINVENTORY_H
+
+#include "llinventory.h"
+#include "llframetimer.h"
+#include "llwearable.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLViewerInventoryItem
+//
+// An inventory item represents something that the current user has in
+// their inventory.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerInventoryItem : public LLInventoryItem
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryItem> > item_array_t;
+
+protected:
+ ~LLViewerInventoryItem( void ); // ref counted
+
+public:
+ // construct a complete viewer inventory item
+ LLViewerInventoryItem(const LLUUID& uuid, const LLUUID& parent_uuid,
+ const LLPermissions& permissions,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ LLInventoryType::EType inv_type,
+ const LLString& name,
+ const LLString& desc,
+ const LLSaleInfo& sale_info,
+ U32 flags,
+ S32 creation_date_utc);
+
+ // construct a viewer inventory item which has the minimal amount
+ // of information to use in the UI.
+ LLViewerInventoryItem(
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const char* name,
+ LLInventoryType::EType inv_type);
+
+ // construct an invalid and incomplete viewer inventory item.
+ // usually useful for unpacking or importing or what have you.
+ // *NOTE: it is important to call setComplete() if you expect the
+ // operations to provide all necessary information.
+ LLViewerInventoryItem();
+ // Create a copy of an inventory item from a pointer to another item
+ // Note: Because InventoryItems are ref counted,
+ // reference copy (a = b) is prohibited
+ LLViewerInventoryItem(const LLViewerInventoryItem* other);
+ LLViewerInventoryItem(const LLInventoryItem* other);
+
+ virtual void copy(const LLViewerInventoryItem* other);
+ virtual void copy(const LLInventoryItem* other);
+
+ // construct a new clone of this item - it creates a new viewer
+ // inventory item using the copy constructor, and returns it.
+ // It is up to the caller to delete (unref) the item.
+ virtual void clone(LLPointer<LLViewerInventoryItem>& newitem) const;
+
+ // virtual methods
+ virtual void removeFromServer( void );
+ virtual void updateParentOnServer(BOOL restamp) const;
+ virtual void updateServer(BOOL is_new) const;
+ void fetchFromServer(void) const;
+
+ //virtual void packMessage(LLMessageSystem* msg) const;
+ virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+ virtual BOOL importFile(FILE* fp);
+ virtual BOOL importLegacyStream(std::istream& input_stream);
+
+ // file handling on the viewer. These are not meant for anything
+ // other than cacheing.
+ bool exportFileLocal(FILE* fp) const;
+ bool importFileLocal(FILE* fp);
+
+ // new methods
+ BOOL isComplete() const { return mIsComplete; }
+ void setComplete(BOOL complete) { mIsComplete = complete; }
+ //void updateAssetOnServer() const;
+
+ virtual void packMessage(LLMessageSystem* msg) const;
+ virtual void setTransactionID(const LLTransactionID& transaction_id);
+ struct comparePointers
+ {
+ bool operator()(const LLPointer<LLViewerInventoryItem>& a, const LLPointer<LLViewerInventoryItem>& b)
+ {
+ return a->getName().compare(b->getName()) < 0;
+ }
+ };
+ LLTransactionID getTransactionID() const { return mTransactionID; }
+
+protected:
+ BOOL mIsComplete;
+ LLTransactionID mTransactionID;
+};
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class LLViewerInventoryCategory
+//
+// An instance of this class represents a category of inventory
+// items. Users come with a set of default categories, and can create
+// new ones as needed.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LLViewerInventoryCategory : public LLInventoryCategory
+{
+public:
+ typedef LLDynamicArray<LLPointer<LLViewerInventoryCategory> > cat_array_t;
+
+protected:
+ ~LLViewerInventoryCategory();
+
+public:
+ LLViewerInventoryCategory(const LLUUID& uuid, const LLUUID& parent_uuid,
+ LLAssetType::EType preferred_type,
+ const LLString& name,
+ const LLUUID& owner_id);
+ LLViewerInventoryCategory(const LLUUID& owner_id);
+ // Create a copy of an inventory category from a pointer to another category
+ // Note: Because InventoryCategorys are ref counted, reference copy (a = b)
+ // is prohibited
+ LLViewerInventoryCategory(const LLViewerInventoryCategory* other);
+ virtual void copy(const LLViewerInventoryCategory* other);
+
+ virtual void removeFromServer();
+ virtual void updateParentOnServer(BOOL restamp_children) const;
+ virtual void updateServer(BOOL is_new) const;
+ //virtual BOOL unpackMessage(LLMessageSystem* msg, const char* block, S32 block_num = 0);
+
+ const LLUUID& getOwnerID() const { return mOwnerID; }
+
+ // Version handling
+ enum { VERSION_UNKNOWN = -1, VERSION_INITIAL = 1 };
+ S32 getVersion() const { return mVersion; }
+ void setVersion(S32 version) { mVersion = version; }
+
+ // Returns true if a fetch was issued.
+ bool fetchDescendents();
+
+ // used to help make cacheing more robust - for example, if
+ // someone is getting 4 packets but logs out after 3. the viewer
+ // may never know the cache is wrong.
+ enum { DESCENDENT_COUNT_UNKNOWN = -1 };
+ S32 getDescendentCount() const { return mDescendentCount; }
+ void setDescendentCount(S32 descendents) { mDescendentCount = descendents; }
+
+ // file handling on the viewer. These are not meant for anything
+ // other than cacheing.
+ bool exportFileLocal(FILE* fp) const;
+ bool importFileLocal(FILE* fp);
+
+protected:
+ LLUUID mOwnerID;
+ S32 mVersion;
+ S32 mDescendentCount;
+ LLFrameTimer mDescendentsRequested;
+};
+
+class LLInventoryCallback : public LLRefCount
+{
+public:
+ virtual ~LLInventoryCallback() {}
+ virtual void fire(const LLUUID& inv_item) = 0;
+};
+
+class WearOnAvatarCallback : public LLInventoryCallback
+{
+ void fire(const LLUUID& inv_item);
+};
+
+class LLViewerJointAttachment;
+
+class RezAttachmentCallback : public LLInventoryCallback
+{
+public:
+ RezAttachmentCallback(LLViewerJointAttachment *attachmentp);
+ ~RezAttachmentCallback();
+ void fire(const LLUUID& inv_item);
+private:
+ LLViewerJointAttachment* mAttach;
+};
+
+class ActivateGestureCallback : public LLInventoryCallback
+{
+public:
+ void fire(const LLUUID& inv_item);
+};
+
+// misc functions
+//void inventory_reliable_callback(void**, S32 status);
+
+class LLInventoryCallbackManager
+{
+public:
+ LLInventoryCallbackManager();
+ ~LLInventoryCallbackManager();
+
+ void fire(U32 callback_id, const LLUUID& item_id);
+ U32 registerCB(LLPointer<LLInventoryCallback> cb);
+private:
+ std::map<U32, LLPointer<LLInventoryCallback> > mMap;
+ U32 mLastCallback;
+};
+extern LLInventoryCallbackManager gInventoryCallbacks;
+
+
+#define NOT_WEARABLE (EWearableType)0
+
+void create_inventory_item(const LLUUID& agent_id, const LLUUID& session_id,
+ const LLUUID& parent, const LLTransactionID& transaction_id,
+ const std::string& name,
+ const std::string& desc, LLAssetType::EType asset_type,
+ LLInventoryType::EType inv_type, EWearableType wtype,
+ U32 next_owner_perm,
+ LLPointer<LLInventoryCallback> cb);
+
+/**
+ * @brief Securely create a new inventory item by copying from another.
+ */
+void copy_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& current_owner,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb);
+
+void move_inventory_item(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& item_id,
+ const LLUUID& parent_id,
+ const std::string& new_name,
+ LLPointer<LLInventoryCallback> cb);
+
+void copy_inventory_from_notecard(const LLUUID& object_id,
+ const LLUUID& notecard_inv_id,
+ const LLInventoryItem *src);
+
+
+#endif // LL_LLVIEWERINVENTORY_H
diff --git a/indra/newview/llviewerjoint.cpp b/indra/newview/llviewerjoint.cpp
new file mode 100644
index 0000000000..cfd23e7120
--- /dev/null
+++ b/indra/newview/llviewerjoint.cpp
@@ -0,0 +1,607 @@
+/**
+ * @file llviewerjoint.cpp
+ * @brief Implementation of LLViewerJoint class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerjoint.h"
+
+#include "llgl.h"
+#include "llmath.h"
+#include "llglheaders.h"
+#include "llsphere.h"
+#include "llvoavatar.h"
+#include "pipeline.h"
+
+#define DEFAULT_LOD 0.0f
+
+const S32 MIN_PIXEL_AREA_3PASS_HAIR = 64*64;
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+BOOL LLViewerJoint::sDisableLOD = FALSE;
+
+//-----------------------------------------------------------------------------
+// LLViewerJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::LLViewerJoint()
+{
+ mUpdateXform = TRUE;
+ mValid = FALSE;
+ mComponents = SC_JOINT | SC_BONE | SC_AXES;
+ mMinPixelArea = DEFAULT_LOD;
+ mPickName = PN_DEFAULT;
+ mVisible = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLViewerJoint()
+// Class Constructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::LLViewerJoint(const std::string &name, LLJoint *parent) :
+ LLJoint(name, parent)
+{
+ mValid = FALSE;
+ mComponents = SC_JOINT | SC_BONE | SC_AXES;
+ mMinPixelArea = DEFAULT_LOD;
+ mPickName = PN_DEFAULT;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJoint()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLViewerJoint::~LLViewerJoint()
+{
+}
+
+
+//--------------------------------------------------------------------
+// setValid()
+//--------------------------------------------------------------------
+void LLViewerJoint::setValid( BOOL valid, BOOL recursive )
+{
+ //----------------------------------------------------------------
+ // set visibility for this joint
+ //----------------------------------------------------------------
+ mValid = valid;
+
+ //----------------------------------------------------------------
+ // set visibility for children
+ //----------------------------------------------------------------
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->setValid(valid, TRUE);
+ }
+ }
+
+}
+
+//--------------------------------------------------------------------
+// renderSkeleton()
+//--------------------------------------------------------------------
+void LLViewerJoint::renderSkeleton(BOOL recursive)
+{
+ F32 nc = 0.57735f;
+
+ //----------------------------------------------------------------
+ // push matrix stack
+ //----------------------------------------------------------------
+ glPushMatrix();
+
+ //----------------------------------------------------------------
+ // render the bone to my parent
+ //----------------------------------------------------------------
+ if (mComponents & SC_BONE)
+ {
+ drawBone();
+ }
+
+ //----------------------------------------------------------------
+ // offset to joint position and
+ // rotate to our orientation
+ //----------------------------------------------------------------
+ glLoadIdentity();
+ glMultMatrixf( &getWorldMatrix().mMatrix[0][0] );
+
+ //----------------------------------------------------------------
+ // render joint axes
+ //----------------------------------------------------------------
+ if (mComponents & SC_AXES)
+ {
+ glBegin(GL_LINES);
+ glColor3f( 1.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.1f, 0.0f, 0.0f );
+
+ glColor3f( 0.0f, 1.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.1f, 0.0f );
+
+ glColor3f( 0.0f, 0.0f, 1.0f );
+ glVertex3f( 0.0f, 0.0f, 0.0f );
+ glVertex3f( 0.0f, 0.0f, 0.1f );
+ glEnd();
+ }
+
+ //----------------------------------------------------------------
+ // render the joint graphic
+ //----------------------------------------------------------------
+ if (mComponents & SC_JOINT)
+ {
+ glColor3f( 1.0f, 1.0f, 0.0f );
+
+ glBegin(GL_TRIANGLES);
+
+ // joint top half
+ glNormal3f(nc, nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+
+ glNormal3f(-nc, nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+
+ glNormal3f(-nc, -nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+
+ glNormal3f(nc, -nc, nc);
+ glVertex3f(0.0f, 0.0f, 0.05f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+
+ // joint bottom half
+ glNormal3f(nc, nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+
+ glNormal3f(-nc, nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, 0.05f, 0.0f);
+
+ glNormal3f(-nc, -nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+ glVertex3f(-0.05f, 0.0f, 0.0f);
+
+ glNormal3f(nc, -nc, -nc);
+ glVertex3f(0.0f, 0.0f, -0.05f);
+ glVertex3f(0.05f, 0.0f, 0.0f);
+ glVertex3f(0.0f, -0.05f, 0.0f);
+
+ glEnd();
+ }
+
+ //----------------------------------------------------------------
+ // render children
+ //----------------------------------------------------------------
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->renderSkeleton();
+ }
+ }
+
+ //----------------------------------------------------------------
+ // pop matrix stack
+ //----------------------------------------------------------------
+ glPopMatrix();
+}
+
+
+//--------------------------------------------------------------------
+// render()
+//--------------------------------------------------------------------
+U32 LLViewerJoint::render( F32 pixelArea )
+{
+ U32 triangle_count = 0;
+
+ //----------------------------------------------------------------
+ // ignore invisible objects
+ //----------------------------------------------------------------
+ if ( mValid )
+ {
+
+ //----------------------------------------------------------------
+ // if object is transparent, defer it, otherwise
+ // give the joint subclass a chance to draw itself
+ //----------------------------------------------------------------
+ if ( gRenderForSelect )
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ else if ( isTransparent() )
+ {
+ LLGLEnable blend(GL_BLEND);
+ // Hair and Skirt
+ if ((pixelArea > MIN_PIXEL_AREA_3PASS_HAIR))
+ {
+ // render all three passes
+ LLGLEnable alpha_test(GL_ALPHA_TEST);
+ LLGLDisable cull(GL_CULL_FACE);
+ // first pass renders without writing to the z buffer
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ // second pass writes to z buffer only
+ glColorMask(FALSE, FALSE, FALSE, FALSE);
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ // third past respects z buffer and writes color
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+ else
+ {
+ LLGLEnable alpha_test(GL_ALPHA_TEST);
+ // Render Inside (no Z buffer write)
+ glCullFace(GL_FRONT);
+ {
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ triangle_count += drawShape( pixelArea );
+ }
+ // Render Outside (write to the Z buffer)
+ glCullFace(GL_BACK);
+ {
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+ }
+ else
+ {
+ // set up render state
+ LLGLDisable blend(GL_BLEND);
+ LLGLSPipelineAvatar gls_pipeline_avatar;
+ triangle_count += drawShape( pixelArea );
+ }
+ }
+
+ //----------------------------------------------------------------
+ // render children
+ //----------------------------------------------------------------
+ LLViewerJoint *joint;
+ for ( joint = (LLViewerJoint *)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint *)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixelArea >= jointLOD || sDisableLOD)
+ {
+ triangle_count += joint->render( pixelArea );
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ return triangle_count;
+}
+
+
+//--------------------------------------------------------------------
+// drawBone()
+//--------------------------------------------------------------------
+void LLViewerJoint::drawBone()
+{
+ if ( mParent == NULL )
+ return;
+
+ F32 boneSize = 0.02f;
+
+ // rotate to point to child (bone direction)
+ glPushMatrix();
+
+ LLVector3 boneX = getPosition();
+ F32 length = boneX.normVec();
+
+ LLVector3 boneZ(1.0f, 0.0f, 1.0f);
+
+ LLVector3 boneY = boneZ % boneX;
+ boneY.normVec();
+
+ boneZ = boneX % boneY;
+
+ LLMatrix4 rotateMat;
+ rotateMat.setFwdRow( boneX );
+ rotateMat.setLeftRow( boneY );
+ rotateMat.setUpRow( boneZ );
+ glMultMatrixf( &rotateMat.mMatrix[0][0] );
+
+ // render the bone
+ glColor3f( 0.5f, 0.5f, 0.0f );
+
+ glBegin(GL_TRIANGLES);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, boneSize, 0.0f);
+ glVertex3f( 0.0f, 0.0f, boneSize);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, 0.0f, -boneSize);
+ glVertex3f( 0.0f, boneSize, 0.0f);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, -boneSize, 0.0f);
+ glVertex3f( 0.0f, 0.0f, -boneSize);
+
+ glVertex3f( length, 0.0f, 0.0f);
+ glVertex3f( 0.0f, 0.0f, boneSize);
+ glVertex3f( 0.0f, -boneSize, 0.0f);
+
+ glEnd();
+
+ // restore matrix
+ glPopMatrix();
+}
+
+//--------------------------------------------------------------------
+// isTransparent()
+//--------------------------------------------------------------------
+BOOL LLViewerJoint::isTransparent()
+{
+ return FALSE;
+}
+
+//--------------------------------------------------------------------
+// drawShape()
+//--------------------------------------------------------------------
+U32 LLViewerJoint::drawShape( F32 pixelArea )
+{
+ return 0;
+}
+
+//--------------------------------------------------------------------
+// setSkeletonComponents()
+//--------------------------------------------------------------------
+void LLViewerJoint::setSkeletonComponents( U32 comp, BOOL recursive )
+{
+ mComponents = comp;
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint *)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint *)mChildren.getNextData() )
+ {
+ joint->setSkeletonComponents(comp, recursive);
+ }
+ }
+}
+
+void LLViewerJoint::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ joint->updateFaceSizes(num_vertices, pixel_area);
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+}
+
+void LLViewerJoint::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ joint->updateFaceData(face, pixel_area, damp_wind);
+
+ if (jointLOD != DEFAULT_LOD)
+ {
+ break;
+ }
+ }
+ }
+}
+
+
+BOOL LLViewerJoint::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL lod_changed = FALSE;
+ BOOL found_lod = FALSE;
+
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ F32 jointLOD = joint->getLOD();
+
+ if (found_lod || jointLOD == DEFAULT_LOD)
+ {
+ // we've already found a joint to enable, so enable the rest as alternatives
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ }
+ else
+ {
+ if (pixel_area >= jointLOD || sDisableLOD)
+ {
+ lod_changed |= joint->updateLOD(pixel_area, TRUE);
+ found_lod = TRUE;
+ }
+ else
+ {
+ lod_changed |= joint->updateLOD(pixel_area, FALSE);
+ }
+ }
+ }
+ return lod_changed;
+}
+
+void LLViewerJoint::dump()
+{
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->dump();
+ }
+}
+
+void LLViewerJoint::setVisible(BOOL visible, BOOL recursive)
+{
+ mVisible = visible;
+
+ if (recursive)
+ {
+ for ( LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ joint->setVisible(visible, recursive);
+ }
+ }
+}
+
+void LLViewerJoint::writeCAL3D(apr_file_t* fp)
+{
+ LLVector3 bone_pos = mXform.getPosition();
+ if (mParent)
+ {
+ bone_pos.scaleVec(mParent->getScale());
+ bone_pos *= 100.f;
+ }
+ else
+ {
+ bone_pos.clearVec();
+ }
+
+ LLQuaternion bone_rot;
+
+ S32 num_children = 0;
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ num_children++;
+ }
+ }
+
+ LLJoint* cur_joint = this;
+ LLVector3 rootSkinOffset;
+ if (mParent)
+ {
+ while (cur_joint)
+ {
+ rootSkinOffset -= cur_joint->getSkinOffset();
+ cur_joint = (LLViewerJoint*)cur_joint->getParent();
+ }
+
+ rootSkinOffset *= 100.f;
+ }
+
+ apr_file_printf(fp, " <BONE ID=\"%d\" NAME=\"%s\" NUMCHILDS=\"%d\">\n", mJointNum + 1, mName.c_str(), num_children);
+ apr_file_printf(fp, " <TRANSLATION>%.6f %.6f %.6f</TRANSLATION>\n", bone_pos.mV[VX], bone_pos.mV[VY], bone_pos.mV[VZ]);
+ apr_file_printf(fp, " <ROTATION>%.6f %.6f %.6f %.6f</ROTATION>\n", bone_rot.mQ[VX], bone_rot.mQ[VY], bone_rot.mQ[VZ], bone_rot.mQ[VW]);
+ apr_file_printf(fp, " <LOCALTRANSLATION>%.6f %.6f %.6f</LOCALTRANSLATION>\n", rootSkinOffset.mV[VX], rootSkinOffset.mV[VY], rootSkinOffset.mV[VZ]);
+ apr_file_printf(fp, " <LOCALROTATION>0 0 0 1</LOCALROTATION>\n");
+ apr_file_printf(fp, " <PARENTID>%d</PARENTID>\n", mParent ? mParent->mJointNum + 1 : -1);
+
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ apr_file_printf(fp, " <CHILDID>%d</CHILDID>\n", joint->mJointNum + 1);
+ }
+ }
+ apr_file_printf(fp, " </BONE>\n");
+
+ // recurse
+ for (LLViewerJoint *joint = (LLViewerJoint*)mChildren.getFirstData();
+ joint != NULL;
+ joint = (LLViewerJoint*)mChildren.getNextData() )
+ {
+ if (joint->mJointNum != -1)
+ {
+ joint->writeCAL3D(fp);
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointCollisionVolume()
+//-----------------------------------------------------------------------------
+
+LLViewerJointCollisionVolume::LLViewerJointCollisionVolume()
+{
+ mUpdateXform = FALSE;
+}
+
+LLViewerJointCollisionVolume::LLViewerJointCollisionVolume(const std::string &name, LLJoint *parent) : LLViewerJoint(name, parent)
+{
+
+}
+
+void LLViewerJointCollisionVolume::render()
+{
+ updateWorldMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glMultMatrixf( &mXform.getWorldMatrix().mMatrix[0][0] );
+
+ glColor3f( 0.f, 0.f, 1.f );
+ gSphere.render();
+
+ glPopMatrix();
+}
+
+LLVector3 LLViewerJointCollisionVolume::getVolumePos(LLVector3 &offset)
+{
+ mUpdateXform = TRUE;
+
+ LLVector3 result = offset;
+ result.scaleVec(getScale());
+ result.rotVec(getWorldRotation());
+ result += getWorldPosition();
+
+ return result;
+}
+
+// End
diff --git a/indra/newview/llviewerjoint.h b/indra/newview/llviewerjoint.h
new file mode 100644
index 0000000000..e38c1c8e7d
--- /dev/null
+++ b/indra/newview/llviewerjoint.h
@@ -0,0 +1,137 @@
+/**
+ * @file llviewerjoint.h
+ * @brief Implementation of LLViewerJoint class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINT_H
+#define LL_LLVIEWERJOINT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "lljoint.h"
+#include "llapr.h"
+
+class LLFace;
+
+//-----------------------------------------------------------------------------
+// class LLViewerJoint
+//-----------------------------------------------------------------------------
+class LLViewerJoint :
+ public LLJoint
+{
+public:
+ LLViewerJoint();
+ LLViewerJoint(const std::string &name, LLJoint *parent = NULL);
+ virtual ~LLViewerJoint();
+
+ // Gets the validity of this joint
+ BOOL getValid() { return mValid; }
+
+ // Sets the validity of this joint
+ virtual void setValid( BOOL valid, BOOL recursive=FALSE );
+
+ // Primarily for debugging and character setup
+ // Derived classes may add text/graphic output.
+ // Draw skeleton graphic for debugging and character setup
+ virtual void renderSkeleton(BOOL recursive=TRUE);
+
+ // Render character hierarchy.
+ // Traverses the entire joint hierarchy, setting up
+ // transforms and calling the drawShape().
+ // Derived classes may add text/graphic output.
+ virtual U32 render( F32 pixelArea ); // Returns triangle count
+
+ // Draws a bone graphic to the parent joint.
+ // Derived classes may add text/graphic output.
+ // Called by renderSkeleton().
+ virtual void drawBone();
+
+ // Returns true if this object is transparent.
+ // This is used to determine in which order to draw objects.
+ virtual BOOL isTransparent();
+
+ // Returns true if this object should inherit scale modifiers from its immediate parent
+ virtual BOOL inheritScale() { return FALSE; }
+
+ // Draws the shape attached to a joint.
+ // Called by render().
+ virtual U32 drawShape( F32 pixelArea );
+ virtual void drawNormals() {}
+
+ enum Components
+ {
+ SC_BONE = 1,
+ SC_JOINT = 2,
+ SC_AXES = 4
+ };
+
+ // Selects which skeleton components to draw
+ void setSkeletonComponents( U32 comp, BOOL recursive = TRUE );
+
+ // Returns which skeleton components are enables for drawing
+ U32 getSkeletonComponents() { return mComponents; }
+
+ // Sets the level of detail for this node as a minimum
+ // pixel area threshold. If the current pixel area for this
+ // object is less than the specified threshold, the node is
+ // not traversed. In additin, if a value is specified (not
+ // default of 0.0), and the pixel area is larger than the
+ // specified miniumn, the node is rendered, but no other siblings
+ // of this node under the same parent will be.
+ F32 getLOD() { return mMinPixelArea; }
+ void setLOD( F32 pixelArea ) { mMinPixelArea = pixelArea; }
+
+ // Sets the OpenGL selection stack name that is pushed and popped
+ // with this joint state. The default value indicates that no name
+ // should be pushed/popped.
+ enum PickName
+ {
+ PN_DEFAULT = -1,
+ PN_0 = 0,
+ PN_1 = 1,
+ PN_2 = 2,
+ PN_3 = 3,
+ PN_4 = 4,
+ PN_5 = 5
+ };
+ void setPickName(PickName name) { mPickName = name; }
+ PickName getPickName() { return mPickName; }
+
+ virtual void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+ virtual void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
+ virtual BOOL updateLOD(F32 pixel_area, BOOL activate);
+ virtual void dump();
+
+ void setVisible( BOOL visible, BOOL recursive );
+ virtual void writeCAL3D(apr_file_t* fp);
+
+public:
+ static BOOL sDisableLOD;
+
+protected:
+ BOOL mValid;
+ U32 mComponents;
+ F32 mMinPixelArea;
+ PickName mPickName;
+ BOOL mVisible;
+};
+
+class LLViewerJointCollisionVolume : public LLViewerJoint
+{
+public:
+ LLViewerJointCollisionVolume();
+ LLViewerJointCollisionVolume(const std::string &name, LLJoint *parent = NULL);
+ virtual ~LLViewerJointCollisionVolume() {};
+
+ virtual BOOL inheritScale() { return TRUE; }
+
+ void render();
+ LLVector3 getVolumePos(LLVector3 &offset);
+};
+
+#endif // LL_LLVIEWERJOINT_H
+
diff --git a/indra/newview/llviewerjointattachment.cpp b/indra/newview/llviewerjointattachment.cpp
new file mode 100644
index 0000000000..a8fd7b9d90
--- /dev/null
+++ b/indra/newview/llviewerjointattachment.cpp
@@ -0,0 +1,381 @@
+/**
+ * @file llviewerjointattachment.cpp
+ * @brief Implementation of LLViewerJointAttachment class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerjointattachment.h"
+
+#include "llagentconstants.h"
+
+#include "llviewercontrol.h"
+#include "lldrawable.h"
+#include "llgl.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "pipeline.h"
+#include "llinventorymodel.h"
+#include "llviewerobjectlist.h"
+#include "llface.h"
+#include "llvoavatar.h"
+
+#include "llglheaders.h"
+
+extern LLPipeline gPipeline;
+
+//-----------------------------------------------------------------------------
+// LLViewerJointAttachment()
+//-----------------------------------------------------------------------------
+LLViewerJointAttachment::LLViewerJointAttachment()
+{
+ mJoint = NULL;
+ mAttachedObject = NULL;
+ mAttachmentDirty = FALSE;
+ mGroup = 0;
+ mUpdateXform = FALSE;
+ mIsHUDAttachment = FALSE;
+ mValid = FALSE;
+ mPieSlice = -1;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJointAttachment()
+//-----------------------------------------------------------------------------
+LLViewerJointAttachment::~LLViewerJointAttachment()
+{
+}
+
+//-----------------------------------------------------------------------------
+// isTransparent()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::isTransparent()
+{
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// drawShape()
+//-----------------------------------------------------------------------------
+U32 LLViewerJointAttachment::drawShape( F32 pixelArea )
+{
+ if (LLVOAvatar::sShowAttachmentPoints)
+ {
+ LLGLDisable cull_face(GL_CULL_FACE);
+
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ glBegin(GL_QUADS);
+ {
+ glVertex3f(-0.1f, 0.1f, 0.f);
+ glVertex3f(-0.1f, -0.1f, 0.f);
+ glVertex3f(0.1f, -0.1f, 0.f);
+ glVertex3f(0.1f, 0.1f, 0.f);
+ }glEnd();
+ }
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// lazyAttach()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::lazyAttach()
+{
+ if (!mAttachedObject)
+ {
+ return;
+ }
+ LLDrawable *drawablep = mAttachedObject->mDrawable;
+
+ if (mAttachmentDirty && drawablep)
+ {
+ setupDrawable(drawablep);
+ mAttachmentDirty = FALSE;
+ }
+}
+
+void LLViewerJointAttachment::setupDrawable(LLDrawable* drawablep)
+{
+ drawablep->mXform.setParent(&mXform); // LLViewerJointAttachment::lazyAttach
+ drawablep->makeActive();
+ LLVector3 current_pos = mAttachedObject->getRenderPosition();
+ LLQuaternion current_rot = mAttachedObject->getRenderRotation();
+ LLQuaternion attachment_pt_inv_rot = ~getWorldRotation();
+
+ current_pos -= getWorldPosition();
+ current_pos.rotVec(attachment_pt_inv_rot);
+
+ current_rot = current_rot * attachment_pt_inv_rot;
+
+ drawablep->mXform.setPosition(current_pos);
+ drawablep->mXform.setRotation(current_rot);
+ gPipeline.markMoved(drawablep);
+ gPipeline.markTextured(drawablep); // face may need to change draw pool to/from POOL_HUD
+ drawablep->setState(LLDrawable::USE_BACKLIGHT);
+
+ if(mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
+ {
+ drawablep->getFace(face_num)->setState(LLFace::HUD_RENDER);
+ }
+ }
+
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->setState(LLDrawable::USE_BACKLIGHT);
+ gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ if(mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < childp->mDrawable->getNumFaces(); face_num++)
+ {
+ childp->mDrawable->getFace(face_num)->setState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// addObject()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::addObject(LLViewerObject* object)
+{
+ if (mAttachedObject)
+ {
+ llwarns << "Attempted to attach object where an attachment already exists!" << llendl;
+ return FALSE;
+ }
+ mAttachedObject = object;
+
+ LLUUID item_id;
+
+ // Find the inventory item ID of the attached object
+ LLNameValue* item_id_nv = object->getNVPair("AttachItemID");
+ if( item_id_nv )
+ {
+ const char* s = item_id_nv->getString();
+ if( s )
+ {
+ item_id.set( s );
+ lldebugs << "getNVPair( AttachItemID ) = " << item_id << llendl;
+ }
+ }
+
+ mItemID = item_id;
+
+ LLDrawable* drawablep = object->mDrawable;
+
+ if (drawablep)
+ {
+ setupDrawable(drawablep);
+ }
+ else
+ {
+ // do lazy update once we have a drawable for this object
+ mAttachmentDirty = TRUE;
+ }
+
+ if (mIsHUDAttachment)
+ {
+ if (object->mText.notNull())
+ {
+ object->mText->setOnHUDAttachment(TRUE);
+ }
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mText.notNull())
+ {
+ childp->mText->setOnHUDAttachment(TRUE);
+ }
+ }
+ }
+ calcLOD();
+ mUpdateXform = TRUE;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// removeObject()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::removeObject(LLViewerObject *object)
+{
+ // force object visibile
+ setAttachmentVisibility(TRUE);
+
+ if (object->mDrawable.notNull())
+ {
+ LLVector3 cur_position = object->getRenderPosition();
+ LLQuaternion cur_rotation = object->getRenderRotation();
+
+ object->mDrawable->mXform.setPosition(cur_position);
+ object->mDrawable->mXform.setRotation(cur_rotation);
+ gPipeline.markMoved(object->mDrawable, TRUE);
+ gPipeline.markTextured(object->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ object->mDrawable->clearState(LLDrawable::USE_BACKLIGHT);
+
+ if (mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < object->mDrawable->getNumFaces(); face_num++)
+ {
+ object->mDrawable->getFace(face_num)->clearState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->clearState(LLDrawable::USE_BACKLIGHT);
+ gPipeline.markTextured(childp->mDrawable); // face may need to change draw pool to/from POOL_HUD
+ if (mIsHUDAttachment)
+ {
+ for (S32 face_num = 0; face_num < childp->mDrawable->getNumFaces(); face_num++)
+ {
+ childp->mDrawable->getFace(face_num)->clearState(LLFace::HUD_RENDER);
+ }
+ }
+ }
+ }
+
+ if (mIsHUDAttachment)
+ {
+ if (object->mText.notNull())
+ {
+ object->mText->setOnHUDAttachment(FALSE);
+ }
+ for (LLViewerObject::child_list_t::iterator iter = object->mChildList.begin();
+ iter != object->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp->mText.notNull())
+ {
+ childp->mText->setOnHUDAttachment(FALSE);
+ }
+ }
+ }
+
+ mAttachedObject = NULL;
+ mUpdateXform = FALSE;
+ mItemID.setNull();
+}
+
+//-----------------------------------------------------------------------------
+// setAttachmentVisibility()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::setAttachmentVisibility(BOOL visible)
+{
+ if (!mAttachedObject || mAttachedObject->mDrawable.isNull())
+ return;
+
+ if (visible)
+ {
+ // Hack to make attachments not visible by disabling their type mask!
+ // This will break if you can ever attach non-volumes! - djs 02/14/03
+ mAttachedObject->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ childp->mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ }
+ }
+ }
+ else
+ {
+ //RN: sometimes we call this on objects that are already invisible, so check for that case
+ if (!mAttachedObject->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) &&
+ !mAttachedObject->mDrawable->isRenderType(0))
+ {
+ llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
+ }
+ mAttachedObject->mDrawable->setRenderType(0);
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ if (childp && childp->mDrawable.notNull())
+ {
+ if (!childp->mDrawable->isRenderType(LLPipeline::RENDER_TYPE_VOLUME) &&
+ !mAttachedObject->mDrawable->isRenderType(0))
+ {
+ llerrs << "Tried to attach non-volume to a joint, visibility hack dangerous!" << llendl;
+ }
+ childp->mDrawable->setRenderType(0);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setOriginalPosition()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::setOriginalPosition(LLVector3& position)
+{
+ mOriginalPos = position;
+ setPosition(position);
+}
+
+//-----------------------------------------------------------------------------
+// clampObjectPosition()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::clampObjectPosition()
+{
+ if (mAttachedObject)
+ {
+ //FIXME: object can drift when hitting maximum radius
+ LLVector3 attachmentPos = mAttachedObject->getPosition();
+ F32 dist = attachmentPos.normVec();
+ dist = llmin(dist, MAX_ATTACHMENT_DIST);
+ attachmentPos *= dist;
+ mAttachedObject->setPosition(attachmentPos);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// calcLOD()
+//-----------------------------------------------------------------------------
+void LLViewerJointAttachment::calcLOD()
+{
+ F32 maxarea = mAttachedObject->getMaxScale() * mAttachedObject->getMidScale();
+ for (LLViewerObject::child_list_t::iterator iter = mAttachedObject->mChildList.begin();
+ iter != mAttachedObject->mChildList.end(); ++iter)
+ {
+ LLViewerObject* childp = *iter;
+ F32 area = childp->getMaxScale() * childp->getMidScale();
+ maxarea = llmax(maxarea, area);
+ }
+ maxarea = llclamp(maxarea, .01f*.01f, 1.f);
+ F32 avatar_area = (4.f * 4.f); // pixels for an avatar sized attachment
+ F32 min_pixel_area = avatar_area / maxarea;
+ setLOD(min_pixel_area);
+}
+
+//-----------------------------------------------------------------------------
+// updateLOD()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointAttachment::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL res = FALSE;
+ if (!mValid)
+ {
+ setValid(TRUE, TRUE);
+ res = TRUE;
+ }
+ return res;
+}
+
diff --git a/indra/newview/llviewerjointattachment.h b/indra/newview/llviewerjointattachment.h
new file mode 100644
index 0000000000..b3a6ccb89c
--- /dev/null
+++ b/indra/newview/llviewerjointattachment.h
@@ -0,0 +1,92 @@
+/**
+ * @file llviewerjointattachment.h
+ * @brief Implementation of LLViewerJointAttachment class
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINTATTACHMENT_H
+#define LL_LLVIEWERJOINTATTACHMENT_H
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerjoint.h"
+#include "llstring.h"
+#include "lluuid.h"
+
+class LLDrawable;
+class LLViewerObject;
+
+//-----------------------------------------------------------------------------
+// class LLViewerJointAttachment
+//-----------------------------------------------------------------------------
+class LLViewerJointAttachment :
+ public LLViewerJoint
+{
+public:
+ LLViewerJointAttachment();
+ virtual ~LLViewerJointAttachment();
+
+ //virtual U32 render( F32 pixelArea ); // Returns triangle count
+
+ // Returns true if this object is transparent.
+ // This is used to determine in which order to draw objects.
+ /*virtual*/ BOOL isTransparent();
+
+ // Draws the shape attached to a joint.
+ // Called by render().
+ /*virtual*/ U32 drawShape( F32 pixelArea );
+
+ /*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
+
+ //
+ // accessors
+ //
+
+ void setJoint (LLJoint* joint) { mJoint = joint; }
+ void setPieSlice(S32 pie_slice) { mPieSlice = pie_slice; }
+ void setVisibleInFirstPerson(BOOL visibility) { mVisibleInFirst = visibility; }
+ BOOL getVisibleInFirstPerson() { return mVisibleInFirst; }
+ void setGroup(S32 group) { mGroup = group; }
+ void setOriginalPosition(LLVector3 &position);
+ void setAttachmentVisibility(BOOL visible);
+ void setIsHUDAttachment(BOOL is_hud) { mIsHUDAttachment = is_hud; }
+ BOOL getIsHUDAttachment() { return mIsHUDAttachment; }
+
+ BOOL isAnimatable() { return FALSE; }
+
+ S32 getGroup() { return mGroup; }
+ S32 getPieSlice() { return mPieSlice; }
+ BOOL getAttachmentDirty() { return mAttachmentDirty && mAttachedObject; }
+ LLViewerObject *getObject(S32 i) { return mAttachedObject; }
+ S32 getNumObjects() { return (mAttachedObject ? 1 : 0); }
+ const LLUUID& getItemID() { return mItemID; }
+
+ //
+ // unique methods
+ //
+ BOOL addObject(LLViewerObject* object);
+ void removeObject(LLViewerObject *object);
+
+ void lazyAttach();
+ void setupDrawable(LLDrawable* drawable);
+ void clampObjectPosition();
+
+protected:
+ void calcLOD();
+
+protected:
+ LLJoint* mJoint;
+ LLViewerObject* mAttachedObject;
+ BOOL mAttachmentDirty; // does attachment drawable need to be fixed up?
+ BOOL mVisibleInFirst;
+ LLVector3 mOriginalPos;
+ S32 mGroup;
+ BOOL mIsHUDAttachment;
+ S32 mPieSlice;
+ LLUUID mItemID; // Inventory item id of the attached item (null if not in inventory)
+};
+
+#endif // LL_LLVIEWERJOINTATTACHMENT_H
diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp
new file mode 100644
index 0000000000..23abba1c9f
--- /dev/null
+++ b/indra/newview/llviewerjointmesh.cpp
@@ -0,0 +1,1601 @@
+/**
+ * @file llviewerjointmesh.cpp
+ * @brief Implementation of LLViewerJointMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#if LL_WINDOWS // For Intel vector classes
+ #include "fvec.h"
+#endif
+
+#include "imageids.h"
+#include "llfasttimer.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolbump.h"
+#include "lldynamictexture.h"
+#include "llface.h"
+#include "llgldbg.h"
+#include "llglheaders.h"
+#include "lltexlayer.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerjointmesh.h"
+#include "llvoavatar.h"
+#include "llsky.h"
+#include "pipeline.h"
+
+#if !LL_DARWIN && !LL_LINUX
+extern PFNGLWEIGHTPOINTERARBPROC glWeightPointerARB;
+extern PFNGLWEIGHTFVARBPROC glWeightfvARB;
+extern PFNGLVERTEXBLENDARBPROC glVertexBlendARB;
+#endif
+extern BOOL gRenderForSelect;
+
+LLMatrix4 gBlendMat;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::LLSkinJoint
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// ~LLSkinJoint
+//-----------------------------------------------------------------------------
+LLSkinJoint::~LLSkinJoint()
+{
+ mJoint = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLSkinJoint::setupSkinJoint()
+//-----------------------------------------------------------------------------
+BOOL LLSkinJoint::setupSkinJoint( LLViewerJoint *joint)
+{
+ // find the named joint
+ mJoint = joint;
+ if ( !mJoint )
+ {
+ llinfos << "Can't find joint" << llendl;
+ }
+
+ // compute the inverse root skin matrix
+ mRootToJointSkinOffset.clearVec();
+
+ LLVector3 rootSkinOffset;
+ while (joint)
+ {
+ rootSkinOffset += joint->getSkinOffset();
+ joint = (LLViewerJoint*)joint->getParent();
+ }
+
+ mRootToJointSkinOffset = -rootSkinOffset;
+ mRootToParentJointSkinOffset = mRootToJointSkinOffset;
+ mRootToParentJointSkinOffset += mJoint->getSkinOffset();
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+
+BOOL LLViewerJointMesh::sPipelineRender = FALSE;
+EAvatarRenderPass LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+U32 LLViewerJointMesh::sClothingMaskImageName = 0;
+LLColor4 LLViewerJointMesh::sClothingInnerColor;
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh()
+//-----------------------------------------------------------------------------
+LLViewerJointMesh::LLViewerJointMesh()
+ :
+ mTexture( NULL ),
+ mLayerSet( NULL ),
+ mTestImageName( 0 ),
+ mIsTransparent(FALSE)
+{
+
+ mColor[0] = 1.0f;
+ mColor[1] = 1.0f;
+ mColor[2] = 1.0f;
+ mColor[3] = 1.0f;
+ mShiny = 0.0f;
+ mCullBackFaces = TRUE;
+
+ mMesh = NULL;
+
+ mNumSkinJoints = 0;
+ mSkinJoints = NULL;
+
+ mFace = NULL;
+
+ mMeshID = 0;
+ mUpdateXform = FALSE;
+
+ mValid = FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// ~LLViewerJointMesh()
+// Class Destructor
+//-----------------------------------------------------------------------------
+LLViewerJointMesh::~LLViewerJointMesh()
+{
+ mMesh = NULL;
+ mTexture = NULL;
+ freeSkinData();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::allocateSkinData()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointMesh::allocateSkinData( U32 numSkinJoints )
+{
+ mSkinJoints = new LLSkinJoint[ numSkinJoints ];
+ mNumSkinJoints = numSkinJoints;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// getSkinJointByIndex()
+//-----------------------------------------------------------------------------
+S32 LLViewerJointMesh::getBoundJointsByIndex(S32 index, S32 &joint_a, S32& joint_b)
+{
+ S32 num_joints = 0;
+ if (mNumSkinJoints == 0)
+ {
+ return num_joints;
+ }
+
+ joint_a = -1;
+ joint_b = -1;
+
+ LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
+
+ if (index < reference_mesh->mJointRenderData.count())
+ {
+ LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index];
+ if (render_datap->mSkinJoint)
+ {
+ joint_a = render_datap->mSkinJoint->mJoint->mJointNum;
+ }
+ num_joints++;
+ }
+ if (index + 1 < reference_mesh->mJointRenderData.count())
+ {
+ LLJointRenderData* render_datap = reference_mesh->mJointRenderData[index + 1];
+ if (render_datap->mSkinJoint)
+ {
+ joint_b = render_datap->mSkinJoint->mJoint->mJointNum;
+ }
+
+ if (joint_a == -1)
+ {
+ joint_a = render_datap->mSkinJoint->mJoint->getParent()->mJointNum;
+ }
+ num_joints++;
+ }
+ return num_joints;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::freeSkinData()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::freeSkinData()
+{
+ mNumSkinJoints = 0;
+ delete [] mSkinJoints;
+ mSkinJoints = NULL;
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getColor()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha )
+{
+ *red = mColor[0];
+ *green = mColor[1];
+ *blue = mColor[2];
+ *alpha = mColor[3];
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setColor()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setColor( F32 red, F32 green, F32 blue, F32 alpha )
+{
+ mColor[0] = red;
+ mColor[1] = green;
+ mColor[2] = blue;
+ mColor[3] = alpha;
+}
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getTexture()
+//--------------------------------------------------------------------
+//LLViewerImage *LLViewerJointMesh::getTexture()
+//{
+// return mTexture;
+//}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setTexture()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setTexture( LLViewerImage *texture )
+{
+ mTexture = texture;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( texture )
+ {
+ mLayerSet = NULL;
+ //texture->bindTexture(0);
+ //texture->setClamp(TRUE, TRUE);
+ }
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::setLayerSet()
+// Sets the shape texture (takes precedence over normal texture)
+//--------------------------------------------------------------------
+void LLViewerJointMesh::setLayerSet( LLTexLayerSet* layer_set )
+{
+ mLayerSet = layer_set;
+
+ // texture and dynamic_texture are mutually exclusive
+ if( layer_set )
+ {
+ mTexture = NULL;
+ }
+}
+
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::getMesh()
+//--------------------------------------------------------------------
+LLPolyMesh *LLViewerJointMesh::getMesh()
+{
+ return mMesh;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerJointMesh::setMesh()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::setMesh( LLPolyMesh *mesh )
+{
+ // set the mesh pointer
+ mMesh = mesh;
+
+ // release any existing skin joints
+ freeSkinData();
+
+ if ( mMesh == NULL )
+ {
+ return;
+ }
+
+ // acquire the transform from the mesh object
+ setPosition( mMesh->getPosition() );
+ setRotation( mMesh->getRotation() );
+ setScale( mMesh->getScale() );
+
+ // create skin joints if necessary
+ if ( mMesh->hasWeights() && !mMesh->isLOD())
+ {
+ U32 numJointNames = mMesh->getNumJointNames();
+
+ allocateSkinData( numJointNames );
+ std::string *jointNames = mMesh->getJointNames();
+
+ U32 jn;
+ for (jn = 0; jn < numJointNames; jn++)
+ {
+ //llinfos << "Setting up joint " << jointNames[jn].c_str() << llendl;
+ LLViewerJoint* joint = (LLViewerJoint*)(getRoot()->findJoint(jointNames[jn]) );
+ mSkinJoints[jn].setupSkinJoint( joint );
+ }
+ }
+
+ // setup joint array
+ if (!mMesh->isLOD())
+ {
+ setupJoint((LLViewerJoint*)getRoot());
+ }
+
+// llinfos << "joint render entries: " << mMesh->mJointRenderData.count() << llendl;
+}
+
+//-----------------------------------------------------------------------------
+// setupJoint()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::setupJoint(LLViewerJoint* current_joint)
+{
+// llinfos << "Mesh: " << getName() << llendl;
+
+// S32 joint_count = 0;
+ U32 sj;
+ for (sj=0; sj<mNumSkinJoints; sj++)
+ {
+ LLSkinJoint &js = mSkinJoints[sj];
+
+ if (js.mJoint != current_joint)
+ {
+ continue;
+ }
+
+ // we've found a skinjoint for this joint..
+
+ // is the last joint in the array our parent?
+ if(mMesh->mJointRenderData.count() && mMesh->mJointRenderData[mMesh->mJointRenderData.count() - 1]->mWorldMatrix == &current_joint->getParent()->getWorldMatrix())
+ {
+ // ...then just add ourselves
+ LLViewerJoint* jointp = js.mJoint;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&jointp->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << js.mJoint->getName() << llendl;
+// joint_count++;
+ }
+ // otherwise add our parent and ourselves
+ else
+ {
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getParent()->getWorldMatrix(), NULL));
+// llinfos << "joint " << joint_count << current_joint->getParent()->getName() << llendl;
+// joint_count++;
+ mMesh->mJointRenderData.put(new LLJointRenderData(&current_joint->getWorldMatrix(), &js));
+// llinfos << "joint " << joint_count << current_joint->getName() << llendl;
+// joint_count++;
+ }
+ }
+
+ // depth-first traversal
+ for (LLJoint *child_joint = current_joint->mChildren.getFirstData();
+ child_joint;
+ child_joint = current_joint->mChildren.getNextData())
+ {
+ setupJoint((LLViewerJoint*)child_joint);
+ }
+}
+
+const S32 NUM_AXES = 3;
+
+// register layoud
+// rotation X 0-n
+// rotation Y 0-n
+// rotation Z 0-n
+// pivot parent 0-n -- child = n+1
+
+static LLMatrix4 gJointMat[32];
+static LLMatrix3 gJointRot[32];
+static LLVector4 gJointPivot[32];
+
+//-----------------------------------------------------------------------------
+// uploadJointMatrices()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::uploadJointMatrices()
+{
+ S32 joint_num;
+ LLPolyMesh *reference_mesh = mMesh->getReferenceMesh();
+ LLDrawPool *poolp = mFace ? mFace->getPool() : NULL;
+ BOOL hardware_skinning = (poolp && poolp->getVertexShaderLevel() > 0) ? TRUE : FALSE;
+
+ //calculate joint matrices
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ LLMatrix4 joint_mat = *reference_mesh->mJointRenderData[joint_num]->mWorldMatrix;
+
+ if (hardware_skinning)
+ {
+ joint_mat *= gCamera->getModelview();
+ }
+ gJointMat[joint_num] = joint_mat;
+ gJointRot[joint_num] = joint_mat.getMat3();
+ }
+
+ BOOL last_pivot_uploaded = FALSE;
+ S32 j = 0;
+
+ //upload joint pivots
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ LLSkinJoint *sj = reference_mesh->mJointRenderData[joint_num]->mSkinJoint;
+ if (sj)
+ {
+ if (!last_pivot_uploaded)
+ {
+ LLVector4 parent_pivot(sj->mRootToParentJointSkinOffset);
+ parent_pivot.mV[VW] = 0.f;
+ gJointPivot[j++] = parent_pivot;
+ }
+
+ LLVector4 child_pivot(sj->mRootToJointSkinOffset);
+ child_pivot.mV[VW] = 0.f;
+
+ gJointPivot[j++] = child_pivot;
+
+ last_pivot_uploaded = TRUE;
+ }
+ else
+ {
+ last_pivot_uploaded = FALSE;
+ }
+ }
+
+ //add pivot point into transform
+ for (S32 i = 0; i < j; i++)
+ {
+ LLVector3 pivot;
+ pivot = LLVector3(gJointPivot[i]);
+ pivot = pivot * gJointRot[i];
+ gJointMat[i].translate(pivot);
+ }
+
+ // upload matrices
+ if (hardware_skinning)
+ {
+ GLfloat mat[45*4];
+ memset(mat, 0, sizeof(GLfloat)*45*4);
+
+ for (joint_num = 0; joint_num < reference_mesh->mJointRenderData.count(); joint_num++)
+ {
+ gJointMat[joint_num].transpose();
+
+ for (S32 axis = 0; axis < NUM_AXES; axis++)
+ {
+ F32* vector = gJointMat[joint_num].mMatrix[axis];
+ //glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+5, (GLfloat*)vector);
+ U32 offset = LL_CHARACTER_MAX_JOINTS_PER_MESH*axis+joint_num;
+ memcpy(mat+offset*4, vector, sizeof(GLfloat)*4);
+ //glProgramLocalParameter4fvARB(GL_VERTEX_PROGRAM_ARB, LL_CHARACTER_MAX_JOINTS_PER_MESH * axis + joint_num+6, (GLfloat*)vector);
+ //cgGLSetParameterArray4f(gPipeline.mAvatarMatrix, offset, 1, vector);
+ }
+ }
+ glUniform4fvARB(gPipeline.mAvatarMatrixParam, 45, mat);
+ }
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::drawBone()
+//--------------------------------------------------------------------
+void LLViewerJointMesh::drawBone()
+{
+}
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::isTransparent()
+//--------------------------------------------------------------------
+BOOL LLViewerJointMesh::isTransparent()
+{
+ return mIsTransparent;
+}
+
+//--------------------------------------------------------------------
+// DrawElementsBLEND and utility code
+//--------------------------------------------------------------------
+
+// compate_int is used by the qsort function to sort the index array
+int compare_int(const void *a, const void *b)
+{
+ if (*(U32*)a < *(U32*)b)
+ {
+ return -1;
+ }
+ else if (*(U32*)a > *(U32*)b)
+ {
+ return 1;
+ }
+ else return 0;
+}
+
+#if LL_WINDOWS || (LL_DARWIN && __i386__) // SSE optimizations in avatar code
+
+#if LL_DARWIN
+#include <xmmintrin.h>
+
+ // On Windows, this class is defined in fvec.h. I've only reproduced the parts of it we use here for now.
+ #pragma pack(push,16) /* Must ensure class & union 16-B aligned */
+ class F32vec4
+ {
+ protected:
+ __m128 vec;
+ public:
+
+ /* Constructors: __m128, 4 floats, 1 float */
+ F32vec4() {}
+
+ /* initialize 4 SP FP with __m128 data type */
+ F32vec4(__m128 m) { vec = m;}
+
+ /* Explicitly initialize each of 4 SP FPs with same float */
+ explicit F32vec4(float f) { vec = _mm_set_ps1(f); }
+ };
+ #pragma pack(pop) /* 16-B aligned */
+
+
+#endif
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ F32 last_weight = F32_MAX;
+ LLMatrix4 *blend_mat = &gBlendMat;
+
+ for (S32 index = vert_offset; index < vert_offset + vert_count; index++)
+ {
+ F32 w = weights [index]; // register copy of weight
+ F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
+ F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
+
+ if (w == last_weight)
+ {
+ // load input and output vertices, and last blended matrix
+ __asm {
+ mov esi, vin
+ mov edi, vout
+
+ mov edx, blend_mat
+ movaps xmm4, [edx]
+ movaps xmm5, [edx+0x10]
+ movaps xmm6, [edx+0x20]
+ movaps xmm7, [edx+0x30]
+ }
+ }
+ else
+ {
+ last_weight = w;
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 *m0 = &(gJointMat[joint+1]);
+ LLMatrix4 *m1 = &(gJointMat[joint+0]);
+
+ // some initial code to load Matrix 0 into SSE registers
+ __asm {
+ mov esi, vin
+ mov edi, vout
+
+ //matrix2
+ mov edx, m0
+ movaps xmm4, [edx]
+ movaps xmm5, [edx+0x10]
+ movaps xmm6, [edx+0x20]
+ movaps xmm7, [edx+0x30]
+ };
+
+ // if w == 1.0f, we don't need to blend.
+ // but since we do the trick of blending the matrices, here, if w != 1.0,
+ // we load Matrix 1 into the other 4 SSE registers and blend both matrices
+ // based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
+
+ if (w != 1.0f)
+ {
+ F32vec4 weight(w);
+
+ __asm { // do blending of matrices instead of verts and normals -- faster
+ mov edx, m1
+ movaps xmm0, [edx]
+ movaps xmm1, [edx+0x10]
+ movaps xmm2, [edx+0x20]
+ movaps xmm3, [edx+0x30]
+
+ subps xmm4, xmm0 // do blend for each matrix column
+ subps xmm5, xmm1 // diff, then multiply weight and re-add
+ subps xmm6, xmm2
+ subps xmm7, xmm3
+
+ mulps xmm4, weight
+ mulps xmm5, weight
+ mulps xmm6, weight
+ mulps xmm7, weight
+
+ addps xmm4, xmm0
+ addps xmm5, xmm1
+ addps xmm6, xmm2
+ addps xmm7, xmm3
+ };
+ }
+
+ __asm {
+ // save off blended matrix
+ mov edx, blend_mat;
+ movaps [edx], xmm4;
+ movaps [edx+0x10], xmm5;
+ movaps [edx+0x20], xmm6;
+ movaps [edx+0x30], xmm7;
+ }
+ }
+
+ // now, we have either a blended matrix in xmm4-7 or the original Matrix 0
+ // we then multiply each vertex and normal by this one matrix.
+
+ // For SSE2, we would try to keep the original two matrices in other registers
+ // and avoid reloading them. However, they should ramain in L1 cache in the
+ // current case.
+
+ // One possible optimization would be to sort the vertices by weight instead
+ // of just index (we still want to uniqify). If we note when two or more vertices
+ // share the same weight, we can avoid doing the middle SSE code above and just
+ // re-use the blended matrix for those vertices
+
+
+ // now, we do the actual vertex blending
+ __asm {
+ // load Vertex into xmm0.
+ movaps xmm0, [esi] // change aps to ups when input is no longer 16-baligned
+ movaps xmm1, xmm0 // copy vector into xmm0 through xmm2 (x,y,z)
+ movaps xmm2, xmm0
+ shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // clone vertex (x) across vector
+ shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1); // clone vertex (y) across vector
+ shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2); // same for Z
+ mulps xmm0, xmm4 // do the actual matrix multipication for r0
+ mulps xmm1, xmm5 // for r1
+ mulps xmm2, xmm6 // for r2
+ addps xmm0, xmm1 // accumulate
+ addps xmm0, xmm2 // accumulate
+ addps xmm0, xmm7 // add in the row 4 which holds the x,y,z translation. assumes w=1 (vertex-w, not weight)
+
+ movaps [edi], xmm0 // store aligned in output array
+
+ // load Normal into xmm0.
+ movaps xmm0, [esi + 0x10] // change aps to ups when input no longer 16-byte aligned
+ movaps xmm1, xmm0 //
+ movaps xmm2, xmm0
+ shufps xmm0, xmm0, _MM_SHUFFLE(0,0,0,0); // since UV sits between vertex and normal, normal starts at element 1, not 0
+ shufps xmm1, xmm1, _MM_SHUFFLE(1,1,1,1);
+ shufps xmm2, xmm2, _MM_SHUFFLE(2,2,2,2);
+ mulps xmm0, xmm4 // multiply by matrix
+ mulps xmm1, xmm5 // multiply
+ mulps xmm2, xmm6 // multiply
+ addps xmm0, xmm1 // accumulate
+ addps xmm0, xmm2 // accumulate. note: do not add translation component to normals, save time too
+ movaps [edi + 0x10], xmm0 // store aligned
+ }
+
+ *(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
+ }
+}
+
+#elif LL_LINUX
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ assert(0);
+}
+
+#elif LL_DARWIN
+// AltiVec versions of the same...
+
+static inline vector float loadAlign(int offset, vector float *addr)
+{
+ vector float in0 = vec_ld(offset, addr);
+ vector float in1 = vec_ld(offset + 16, addr);
+ vector unsigned char perm = vec_lvsl(0, (unsigned char*)addr);
+
+ return(vec_perm(in0, in1, perm));
+}
+
+static inline void storeAlign(vector float v, int offset, vector float *addr)
+{
+ vector float in0 = vec_ld(offset, addr);
+ vector float in1 = vec_ld(offset + 16, addr);
+ vector unsigned char perm = vec_lvsr(0, (unsigned char *)addr);
+ vector float temp = vec_perm(v, v, perm);
+ vector unsigned char mask = (vector unsigned char)vec_cmpgt(perm, vec_splat_u8(15));
+
+ in0 = vec_sel(in0, temp, (vector unsigned int)mask);
+ in1 = vec_sel(temp, in1, (vector unsigned int)mask);
+
+ vec_st(in0, offset, addr);
+ vec_st(in1, offset + 16, addr);
+}
+
+void blend_SSE_32_32_batch(const int vert_offset, const int vert_count, float* output,
+ LLStrider<LLVector3>& vertices, LLStrider<LLVector2>& texcoords, LLStrider<LLVector3>& normals, LLStrider<F32>& weights)
+{
+ F32 last_weight = F32_MAX;
+// LLMatrix4 &blend_mat = gBlendMat;
+
+ vector float matrix0_0, matrix0_1, matrix0_2, matrix0_3;
+ vector unsigned char out0perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
+// vector unsigned char out1perm = (vector unsigned char) ( 0x00,0x01,0x02,0x03, 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B );
+ vector unsigned char out1perm = (vector unsigned char) ( 0x10,0x11,0x12,0x13, 0x14,0x15,0x16,0x17, 0x18,0x19,0x1A,0x1B, 0x0C,0x0D,0x0E,0x0F );
+
+ vector float zero = (vector float)vec_splat_u32(0);
+
+ for (U32 index = vert_offset; index < vert_offset + vert_count; index++)
+ {
+ F32 w = weights [index]; // register copy of weight
+ F32 *vin = &vertices[index].mV[0]; // pointer to input vertex data, assumed to be V3+T2+N3+whatever
+ F32 *vout = output + index * (AVATAR_VERTEX_BYTES/sizeof(F32)); // pointer to the output vertex data, assumed to be 16 byte aligned
+
+ // MBW -- XXX -- If this isn't the case, this code gets more complicated.
+ if(0x0000000F & (U32)vin)
+ {
+ llerrs << "blend_SSE_batch: input not 16-byte aligned!" << llendl;
+ }
+ if(0x0000000F & (U32)vout)
+ {
+ llerrs << "blend_SSE_batch: output not 16-byte aligned!" << llendl;
+ }
+// if(0x0000000F & (U32)&(blend_mat.mMatrix))
+// {
+// llerrs << "blend_SSE_batch: blend_mat not 16-byte aligned!" << llendl;
+// }
+
+ if (w == last_weight)
+ {
+ // load last blended matrix
+ // Still loaded from last time through the loop.
+// matrix0_0 = vec_ld(0x00, (vector float*)&(blend_mat.mMatrix));
+// matrix0_1 = vec_ld(0x10, (vector float*)&(blend_mat.mMatrix));
+// matrix0_2 = vec_ld(0x20, (vector float*)&(blend_mat.mMatrix));
+// matrix0_3 = vec_ld(0x30, (vector float*)&(blend_mat.mMatrix));
+ }
+ else
+ {
+ last_weight = w;
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 &m0 = gJointMat[joint+1];
+ LLMatrix4 &m1 = gJointMat[joint+0];
+
+ // load Matrix 0 into vector registers
+ matrix0_0 = vec_ld(0x00, (vector float*)&(m0.mMatrix));
+ matrix0_1 = vec_ld(0x10, (vector float*)&(m0.mMatrix));
+ matrix0_2 = vec_ld(0x20, (vector float*)&(m0.mMatrix));
+ matrix0_3 = vec_ld(0x30, (vector float*)&(m0.mMatrix));
+
+ // if w == 1.0f, we don't need to blend.
+ // but since we do the trick of blending the matrices, here, if w != 1.0,
+ // we load Matrix 1 into the other 4 SSE registers and blend both matrices
+ // based on the weight (which we load ingo a 16-byte aligned vector: w,w,w,w)
+
+ if (w != 1.0f)
+ {
+ vector float matrix1_0, matrix1_1, matrix1_2, matrix1_3;
+
+ // This loads the weight somewhere in the vector register
+ vector float weight = vec_lde(0, &(w));
+ // and this splats it to all elements.
+ weight = vec_splat(vec_perm(weight, weight, vec_lvsl(0, &(w))), 0);
+
+ // do blending of matrices instead of verts and normals -- faster
+ matrix1_0 = vec_ld(0x00, (vector float*)&(m1.mMatrix));
+ matrix1_1 = vec_ld(0x10, (vector float*)&(m1.mMatrix));
+ matrix1_2 = vec_ld(0x20, (vector float*)&(m1.mMatrix));
+ matrix1_3 = vec_ld(0x30, (vector float*)&(m1.mMatrix));
+
+ // m0[col] = ((m0[col] - m1[col]) * weight) + m1[col];
+ matrix0_0 = vec_madd(vec_sub(matrix0_0, matrix1_0), weight, matrix1_0);
+ matrix0_1 = vec_madd(vec_sub(matrix0_1, matrix1_1), weight, matrix1_1);
+ matrix0_2 = vec_madd(vec_sub(matrix0_2, matrix1_2), weight, matrix1_2);
+ matrix0_3 = vec_madd(vec_sub(matrix0_3, matrix1_3), weight, matrix1_3);
+ }
+
+ // save off blended matrix
+// vec_st(matrix0_0, 0x00, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_1, 0x10, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_2, 0x20, (vector float*)&(blend_mat.mMatrix));
+// vec_st(matrix0_3, 0x30, (vector float*)&(blend_mat.mMatrix));
+ }
+
+ // now, we have either a blended matrix in matrix0_0-3 or the original Matrix 0
+ // we then multiply each vertex and normal by this one matrix.
+
+ // For SSE2, we would try to keep the original two matrices in other registers
+ // and avoid reloading them. However, they should ramain in L1 cache in the
+ // current case.
+
+ // One possible optimization would be to sort the vertices by weight instead
+ // of just index (we still want to uniqify). If we note when two or more vertices
+ // share the same weight, we can avoid doing the middle SSE code above and just
+ // re-use the blended matrix for those vertices
+
+
+ // now, we do the actual vertex blending
+
+ vector float in0 = vec_ld(AVATAR_OFFSET_POS, (vector float*)vin);
+ vector float in1 = vec_ld(AVATAR_OFFSET_NORMAL, (vector float*)vin);
+
+ // Matrix multiply vertex
+ vector float out0 = vec_madd
+ (
+ vec_splat(in0, 0),
+ matrix0_0,
+ vec_madd
+ (
+ vec_splat(in0, 1),
+ matrix0_1,
+ vec_madd
+ (
+ vec_splat(in0, 2),
+ matrix0_2,
+ matrix0_3
+ )
+ )
+ );
+
+ // Matrix multiply normal
+ vector float out1 = vec_madd
+ (
+ vec_splat(in1, 0),
+ matrix0_0,
+ vec_madd
+ (
+ vec_splat(in1, 1),
+ matrix0_1,
+ vec_madd
+ (
+ vec_splat(in1, 2),
+ matrix0_2,
+ // no translation for normals
+ (vector float)vec_splat_u32(0)
+ )
+ )
+ );
+
+ // indexed store
+ vec_stl(vec_perm(in0, out0, out0perm), AVATAR_OFFSET_POS, (vector float*)vout); // Pos
+ vec_stl(vec_perm(in1, out1, out1perm), AVATAR_OFFSET_NORMAL, (vector float*)vout); // Norm
+ *(LLVector2*)(vout + (AVATAR_OFFSET_TEX0/sizeof(F32))) = texcoords[index]; // write texcoord into appropriate spot.
+ }
+}
+
+#endif
+
+
+void llDrawElementsBatchBlend(const U32 vert_offset, const U32 vert_count, LLFace *face, const S32 index_count, const U32 *indices)
+{
+ U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
+
+ if (gAGPVertices)
+ {
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tcoords0;
+ LLStrider<F32> weights;
+
+ LLStrider<LLVector3> o_vertices;
+ LLStrider<LLVector3> o_normals;
+ LLStrider<LLVector2> o_texcoords0;
+
+
+ LLStrider<LLVector3> binormals;
+ LLStrider<LLVector2> o_texcoords1;
+ // get the source vertices from the draw pool. We index these ourselves, as there was
+ // no guarantee the indices for a single jointmesh were contigious
+
+ LLDrawPool *pool = face->getPool();
+ pool->getVertexStrider (vertices, 0);
+ pool->getTexCoordStrider (tcoords0, 0, 0);
+ pool->getNormalStrider (normals, 0);
+ pool->getBinormalStrider (binormals, 0);
+ pool->getVertexWeightStrider(weights, 0);
+
+ // load the addresses of the output striders
+ o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
+ o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
+ o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
+ o_texcoords1= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX1); o_texcoords1.setStride(AVATAR_VERTEX_BYTES);
+
+#if !LL_LINUX // !!! FIXME
+ if (gGLManager.mSoftwareBlendSSE)
+ {
+ // do SSE blend without binormals or extra texcoords
+ blend_SSE_32_32_batch(vert_offset, vert_count, (float*)gAGPVertices,
+ vertices, tcoords0, normals, weights);
+ }
+ else // fully backwards compatible software blending, no SSE
+#endif
+ {
+ LLVector4 tpos0, tnorm0, tpos1, tnorm1, tbinorm0, tbinorm1;
+ F32 last_weight = F32_MAX;
+ LLMatrix3 gBlendRotMat;
+
+ {
+ for (U32 index=vert_offset; index < vert_offset + vert_count; index++)
+ {
+ // blend by first matrix
+ F32 w = weights [index];
+
+ if (w != last_weight)
+ {
+ last_weight = w;
+
+ S32 joint = llfloor(w);
+ w -= joint;
+
+ LLMatrix4 &m0 = gJointMat[joint+1];
+ LLMatrix4 &m1 = gJointMat[joint+0];
+ LLMatrix3 &n0 = gJointRot[joint+1];
+ LLMatrix3 &n1 = gJointRot[joint+0];
+
+ if (w == 1.0f)
+ {
+ gBlendMat = m0;
+ gBlendRotMat = n0;
+ }
+ else
+ {
+ gBlendMat.mMatrix[VX][VX] = lerp(m1.mMatrix[VX][VX], m0.mMatrix[VX][VX], w);
+ gBlendMat.mMatrix[VX][VY] = lerp(m1.mMatrix[VX][VY], m0.mMatrix[VX][VY], w);
+ gBlendMat.mMatrix[VX][VZ] = lerp(m1.mMatrix[VX][VZ], m0.mMatrix[VX][VZ], w);
+
+ gBlendMat.mMatrix[VY][VX] = lerp(m1.mMatrix[VY][VX], m0.mMatrix[VY][VX], w);
+ gBlendMat.mMatrix[VY][VY] = lerp(m1.mMatrix[VY][VY], m0.mMatrix[VY][VY], w);
+ gBlendMat.mMatrix[VY][VZ] = lerp(m1.mMatrix[VY][VZ], m0.mMatrix[VY][VZ], w);
+
+ gBlendMat.mMatrix[VZ][VX] = lerp(m1.mMatrix[VZ][VX], m0.mMatrix[VZ][VX], w);
+ gBlendMat.mMatrix[VZ][VY] = lerp(m1.mMatrix[VZ][VY], m0.mMatrix[VZ][VY], w);
+ gBlendMat.mMatrix[VZ][VZ] = lerp(m1.mMatrix[VZ][VZ], m0.mMatrix[VZ][VZ], w);
+
+ gBlendMat.mMatrix[VW][VX] = lerp(m1.mMatrix[VW][VX], m0.mMatrix[VW][VX], w);
+ gBlendMat.mMatrix[VW][VY] = lerp(m1.mMatrix[VW][VY], m0.mMatrix[VW][VY], w);
+ gBlendMat.mMatrix[VW][VZ] = lerp(m1.mMatrix[VW][VZ], m0.mMatrix[VW][VZ], w);
+
+ gBlendRotMat.mMatrix[VX][VX] = lerp(n1.mMatrix[VX][VX], n0.mMatrix[VX][VX], w);
+ gBlendRotMat.mMatrix[VX][VY] = lerp(n1.mMatrix[VX][VY], n0.mMatrix[VX][VY], w);
+ gBlendRotMat.mMatrix[VX][VZ] = lerp(n1.mMatrix[VX][VZ], n0.mMatrix[VX][VZ], w);
+
+ gBlendRotMat.mMatrix[VY][VX] = lerp(n1.mMatrix[VY][VX], n0.mMatrix[VY][VX], w);
+ gBlendRotMat.mMatrix[VY][VY] = lerp(n1.mMatrix[VY][VY], n0.mMatrix[VY][VY], w);
+ gBlendRotMat.mMatrix[VY][VZ] = lerp(n1.mMatrix[VY][VZ], n0.mMatrix[VY][VZ], w);
+
+ gBlendRotMat.mMatrix[VZ][VX] = lerp(n1.mMatrix[VZ][VX], n0.mMatrix[VZ][VX], w);
+ gBlendRotMat.mMatrix[VZ][VY] = lerp(n1.mMatrix[VZ][VY], n0.mMatrix[VZ][VY], w);
+ gBlendRotMat.mMatrix[VZ][VZ] = lerp(n1.mMatrix[VZ][VZ], n0.mMatrix[VZ][VZ], w);
+ }
+ }
+
+ // write result
+ o_vertices [index] = vertices[index] * gBlendMat;
+ o_normals [index] = normals [index] * gBlendRotMat;
+ o_texcoords0[index] = tcoords0[index];
+
+ /*
+ // Verification code. Leave this here. It's useful for keeping the SSE and non-SSE versions in sync.
+ LLVector3 temp;
+ temp = tpos0;
+ if( (o_vertices[index] - temp).magVecSquared() > 0.001f )
+ {
+ llerrs << "V SSE: " << o_vertices[index] << " v. " << temp << llendl;
+ }
+
+ temp = tnorm0;
+ if( (o_normals[index] - temp).magVecSquared() > 0.001f )
+ {
+ llerrs << "N SSE: " << o_normals[index] << " v. " << temp << llendl;
+ }
+
+ if( (o_texcoords0[index] - tcoords0[index]).magVecSquared() > 0.001f )
+ {
+ llerrs << "T0 SSE: " << o_texcoords0[index] << " v. " << tcoords0[index] << llendl;
+ }
+ */
+ }
+ }
+ }
+
+#if LL_DARWIN
+ // *HACK* *CHOKE* *PUKE*
+ // No way does this belong here.
+ glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * vert_count, gAGPVertices + (AVATAR_VERTEX_BYTES * vert_offset));
+#endif
+ glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices); // draw it!
+ }
+ else
+ {
+ glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, indices);
+ }
+}
+
+
+
+//--------------------------------------------------------------------
+// DrawElements
+
+// works just like glDrawElements, except it assumes GL_TRIANGLES and GL_UNSIGNED_INT indices
+
+// why? because the destination buffer may not be the AGP buffer and the eyes do not use blending
+// separate the eyes into their own drawpools and this code goes away.
+
+//--------------------------------------------------------------------
+
+void llDrawElements(const S32 count, const U32 *indices, LLFace *face)
+{
+ U8* gAGPVertices = gPipeline.bufferGetScratchMemory();
+
+ if (gAGPVertices)
+ {
+#if LL_DARWIN
+ U32 minIndex = indices[0];
+ U32 maxIndex = indices[0];
+#endif
+ {
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tcoords;
+ LLStrider<F32> weights;
+
+ LLStrider<LLVector3> o_vertices;
+ LLStrider<LLVector3> o_normals;
+ LLStrider<LLVector2> o_texcoords0;
+
+ LLDrawPool *pool = face->getPool();
+ pool->getVertexStrider (vertices,0);
+ pool->getNormalStrider (normals, 0);
+ pool->getTexCoordStrider (tcoords, 0);
+
+ o_vertices = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_POS); o_vertices.setStride( AVATAR_VERTEX_BYTES);
+ o_normals = (LLVector3*)(gAGPVertices + AVATAR_OFFSET_NORMAL); o_normals.setStride( AVATAR_VERTEX_BYTES);
+ o_texcoords0= (LLVector2*)(gAGPVertices + AVATAR_OFFSET_TEX0); o_texcoords0.setStride(AVATAR_VERTEX_BYTES);
+
+ for (S32 i=0; i < count; i++)
+ {
+ U32 index = indices[i];
+
+ o_vertices [index] = vertices[index];
+ o_normals [index] = normals [index];
+ o_texcoords0[index] = tcoords [index];
+
+#if LL_DARWIN
+ maxIndex = llmax(index, maxIndex);
+ minIndex = llmin(index, minIndex);
+#endif
+ }
+ }
+
+#if LL_DARWIN
+ // *HACK* *CHOKE* *PUKE*
+ // No way does this belong here.
+ glFlushVertexArrayRangeAPPLE(AVATAR_VERTEX_BYTES * (maxIndex + 1 - minIndex), gAGPVertices + (AVATAR_VERTEX_BYTES * minIndex));
+#endif
+
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
+ }
+ else
+ {
+ glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, indices);
+ }
+}
+
+
+//--------------------------------------------------------------------
+// LLViewerJointMesh::drawShape()
+//--------------------------------------------------------------------
+U32 LLViewerJointMesh::drawShape( F32 pixelArea )
+{
+ if (!mValid || !mVisible) return 0;
+
+ U32 triangle_count = 0;
+
+ //----------------------------------------------------------------
+ // if no mesh bail out now
+ //----------------------------------------------------------------
+ if ( !mMesh || !mFace)
+ {
+ return 0;
+ }
+
+ //----------------------------------------------------------------
+ // if we have no faces, bail out now
+ //----------------------------------------------------------------
+ if ( mMesh->getNumFaces() == 0 )
+ {
+ return 0;
+ }
+
+ stop_glerror();
+
+ //----------------------------------------------------------------
+ // setup current color
+ //----------------------------------------------------------------
+ if (gRenderForSelect)
+ {
+ S32 name = mFace->getDrawable() ? mFace->getDrawable()->getVObj()->mGLName : 0;
+ LLColor4U color((U8)(name >> 16), (U8)(name >> 8), (U8)name, 0xff);
+ LLColor4 color_float(color);
+
+ glColor4f(color_float.mV[0], color_float.mV[1], color_float.mV[2], 1.f);
+ }
+ else
+ {
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glColor4f(0,0,0,1);
+
+ if (gPipeline.mMaterialIndex > 0)
+ {
+ glVertexAttrib4fvARB(gPipeline.mMaterialIndex, mColor.mV);
+ }
+
+ if (mShiny && gPipeline.mSpecularIndex > 0)
+ {
+ glVertexAttrib4fARB(gPipeline.mSpecularIndex, 1,1,1,1);
+ }
+ }
+ else
+ {
+ glColor4fv(mColor.mV);
+ }
+ }
+
+ stop_glerror();
+
+// LLGLSSpecular specular(mSpecular, gRenderForSelect ? 0.0f : mShiny);
+ LLGLSSpecular specular(LLColor4(1.f,1.f,1.f,1.f), gRenderForSelect ? 0.0f : mShiny && !(mFace->getPool()->getVertexShaderLevel() > 0));
+
+ LLGLEnable texture_2d((gRenderForSelect && isTransparent()) ? GL_TEXTURE_2D : 0);
+
+ //----------------------------------------------------------------
+ // setup current texture
+ //----------------------------------------------------------------
+ llassert( !(mTexture.notNull() && mLayerSet) ); // mutually exclusive
+
+ //GLuint test_image_name = 0;
+
+ //
+ LLGLState force_alpha_test(GL_ALPHA_TEST, isTransparent());
+
+ if (mTestImageName)
+ {
+ LLImageGL::bindExternalTexture( mTestImageName, 0, GL_TEXTURE_2D );
+
+ if (mIsTransparent)
+ {
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.7f, 0.6f, 0.3f, 1.f);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_ONE_MINUS_SRC_ALPHA);
+ }
+ }
+ else if( mLayerSet )
+ {
+ if( mLayerSet->hasComposite() )
+ {
+ mLayerSet->getComposite()->bindTexture();
+ }
+ else
+ {
+ llwarns << "Layerset without composite" << llendl;
+ gImageList.getImage(IMG_DEFAULT)->bind();
+ }
+ }
+ else
+ if ( mTexture.notNull() )
+ {
+ mTexture->bind();
+ if (!mTexture->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ }
+ if (!mTexture->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ }
+ }
+ else
+ {
+ gImageList.getImage(IMG_DEFAULT_AVATAR)->bind();
+ }
+
+ if (gRenderForSelect)
+ {
+ if (isTransparent())
+ {
+ //gGLSObjectSelectDepthAlpha.set();
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE); // GL_TEXTURE_ENV_COLOR is set in renderPass1
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+ }
+ else
+ {
+ //gGLSObjectSelectDepth.set();
+ }
+ }
+ else
+ {
+ //----------------------------------------------------------------
+ // by default, backface culling is enabled
+ //----------------------------------------------------------------
+ if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+ {
+ //LLGLSPipelineAvatar gls_pipeline_avatar;
+ LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, sClothingInnerColor.mV);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_CONSTANT_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA);
+ }
+ else if (sRenderPass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
+ {
+ //gGLSPipelineAvatarAlphaPass1.set();
+ glAlphaFunc(GL_GREATER, 0.1f);
+ LLImageGL::bindExternalTexture( sClothingMaskImageName, 1, GL_TEXTURE_2D );
+
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR);
+
+ glClientActiveTextureARB(GL_TEXTURE1_ARB);
+ glEnable(GL_TEXTURE_2D); // Texture unit 1
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_MODULATE);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
+ glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
+ }
+ else if ( isTransparent())
+ {
+ //gGLSNoCullFaces.set();
+ }
+ else
+ {
+ //gGLSCullFaces.set();
+ }
+ }
+
+ if (mMesh->hasWeights())
+ {
+ uploadJointMatrices();
+
+
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
+
+ glPopMatrix();
+ }
+ else
+ {
+ if (mFace->getGeomIndex() < 0)
+ {
+ llerrs << "Invalid geometry index in LLViewerJointMesh::drawShape() " << mFace->getGeomIndex() << llendl;
+ }
+
+ if ((S32)(mMesh->mFaceVertexOffset + mMesh->mFaceVertexCount) > mFace->getGeomCount())
+ {
+ ((LLVOAvatar*)mFace->getDrawable()->getVObj())->mRoot.dump();
+ llerrs << "Rendering outside of vertex bounds with mesh " << mName << " at pixel area " << pixelArea << llendl;
+ }
+ llDrawElementsBatchBlend(mMesh->mFaceVertexOffset, mMesh->mFaceVertexCount,
+ mFace, mMesh->mFaceIndexCount, mMesh->getIndices());
+ }
+
+ }
+ else
+ {
+ glPushMatrix();
+ LLMatrix4 jointToWorld = getWorldMatrix();
+ jointToWorld *= gCamera->getModelview();
+ glLoadMatrixf((GLfloat*)jointToWorld.mMatrix);
+
+ if ((mFace->getPool()->getVertexShaderLevel() > 0))
+ {
+ glDrawElements(GL_TRIANGLES, mMesh->mFaceIndexCount, GL_UNSIGNED_INT, mMesh->getIndices());
+ }
+ else // this else clause handles non-weighted vertices. llDrawElements just copies and draws
+ {
+ llDrawElements(mMesh->mFaceIndexCount, mMesh->getIndices(), mFace);
+ }
+
+ glPopMatrix();
+ }
+
+ triangle_count += mMesh->mFaceIndexCount;
+
+ if (gRenderForSelect)
+ {
+ glColor4fv(mColor.mV);
+ }
+
+ if (mTestImageName)
+ {
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ }
+
+ if (sRenderPass != AVATAR_RENDER_PASS_SINGLE)
+ {
+ LLImageGL::unbindTexture(1, GL_TEXTURE_2D);
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+
+ // Return to the default texture.
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glClientActiveTextureARB(GL_TEXTURE0_ARB);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
+ glAlphaFunc(GL_GREATER, 0.01f);
+ }
+
+ if (mTexture.notNull()) {
+ if (!mTexture->getClampS()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ }
+ if (!mTexture->getClampT()) {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ }
+ }
+
+ return triangle_count;
+}
+
+//-----------------------------------------------------------------------------
+// updateFaceSizes()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::updateFaceSizes(U32 &num_vertices, F32 pixel_area)
+{
+ // Do a pre-alloc pass to determine sizes of data.
+ if (mMesh && mValid)
+ {
+ mMesh->mFaceVertexOffset = num_vertices;
+ mMesh->mFaceVertexCount = mMesh->getNumVertices();
+ mMesh->getReferenceMesh()->mCurVertexCount = mMesh->mFaceVertexCount;
+ num_vertices += mMesh->getNumVertices();
+
+ mMesh->mFaceIndexCount = mMesh->getSharedData()->mNumTriangleIndices;
+
+ mMesh->getSharedData()->genIndices(mMesh->mFaceVertexOffset);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateFaceData()
+//-----------------------------------------------------------------------------
+void LLViewerJointMesh::updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind)
+{
+ U32 i;
+
+ if (!mValid) return;
+
+ mFace = face;
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector3> binormalsp;
+ LLStrider<LLVector2> tex_coordsp;
+ LLStrider<F32> vertex_weightsp;
+ LLStrider<LLVector4> clothing_weightsp;
+
+ // Copy data into the faces from the polymesh data.
+ if (mMesh)
+ {
+ if (mMesh->getNumVertices())
+ {
+ S32 index = face->getGeometryAvatar(verticesp, normalsp, binormalsp, tex_coordsp, vertex_weightsp, clothing_weightsp);
+
+ if (-1 == index)
+ {
+ return;
+ }
+
+ for (i = 0; i < mMesh->getNumVertices(); i++)
+ {
+ verticesp[mMesh->mFaceVertexOffset + i] = *(mMesh->getCoords() + i);
+ tex_coordsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getTexCoords() + i);
+ normalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getNormals() + i);
+ binormalsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getBinormals() + i);
+ vertex_weightsp[mMesh->mFaceVertexOffset + i] = *(mMesh->getWeights() + i);
+ if (damp_wind)
+ {
+ clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(0,0,0,0);
+ }
+ else
+ {
+ clothing_weightsp[mMesh->mFaceVertexOffset + i].setVec(*(mMesh->getClothingWeights() + i));
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateLOD()
+//-----------------------------------------------------------------------------
+BOOL LLViewerJointMesh::updateLOD(F32 pixel_area, BOOL activate)
+{
+ BOOL valid = mValid;
+ setValid(activate, TRUE);
+ return (valid != activate);
+}
+
+void LLViewerJointMesh::dump()
+{
+ if (mValid)
+ {
+ llinfos << "Usable LOD " << mName << llendl;
+ }
+}
+
+void LLViewerJointMesh::writeCAL3D(apr_file_t* fp, S32 material_num, LLCharacter* characterp)
+{
+ apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n", mMesh->getNumVertices(), mMesh->getNumFaces(), material_num);
+
+ const LLVector3* mesh_coords = mMesh->getCoords();
+ const LLVector3* mesh_normals = mMesh->getNormals();
+ const LLVector2* mesh_uvs = mMesh->getTexCoords();
+ const F32* mesh_weights = mMesh->getWeights();
+ LLVector3 mesh_offset;
+ LLVector3 scale(1.f, 1.f, 1.f);
+ S32 joint_a = -1;
+ S32 joint_b = -1;
+ S32 num_bound_joints = 0;
+
+ if(!mMesh->hasWeights())
+ {
+ num_bound_joints = 1;
+ LLJoint* cur_joint = this;
+ while(cur_joint)
+ {
+ if (cur_joint->mJointNum != -1 && joint_a == -1)
+ {
+ joint_a = cur_joint->mJointNum;
+ }
+ mesh_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+ }
+
+ for (S32 i = 0; i < (S32)mMesh->getNumVertices(); i++)
+ {
+ LLVector3 coord = mesh_coords[i];
+
+ if (mMesh->hasWeights())
+ {
+ // calculate joint to which this skinned vertex is bound
+ num_bound_joints = getBoundJointsByIndex(llfloor(mesh_weights[i]), joint_a, joint_b);
+ LLJoint* first_joint = characterp->getCharacterJoint(joint_a);
+ LLJoint* second_joint = characterp->getCharacterJoint(joint_b);
+
+ LLVector3 first_joint_offset;
+ LLJoint* cur_joint = first_joint;
+ while(cur_joint)
+ {
+ first_joint_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+
+ LLVector3 second_joint_offset;
+ cur_joint = second_joint;
+ while(cur_joint)
+ {
+ second_joint_offset += cur_joint->getSkinOffset();
+ cur_joint = cur_joint->getParent();
+ }
+
+ LLVector3 first_coord = coord - first_joint_offset;
+ first_coord.scaleVec(first_joint->getScale());
+ LLVector3 second_coord = coord - second_joint_offset;
+ if (second_joint)
+ {
+ second_coord.scaleVec(second_joint->getScale());
+ }
+
+ coord = lerp(first_joint_offset + first_coord, second_joint_offset + second_coord, fmodf(mesh_weights[i], 1.f));
+ }
+
+ // add offset to move rigid mesh to target location
+ coord += mesh_offset;
+ coord *= 100.f;
+
+ apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"%d\">\n", i, num_bound_joints);
+ apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", coord.mV[VX], coord.mV[VY], coord.mV[VZ]);
+ apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", mesh_normals[i].mV[VX], mesh_normals[i].mV[VY], mesh_normals[i].mV[VZ]);
+ apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", mesh_uvs[i].mV[VX], 1.f - mesh_uvs[i].mV[VY]);
+ if (num_bound_joints >= 1)
+ {
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_a + 1, 1.f - fmod(mesh_weights[i], 1.f));
+ }
+ if (num_bound_joints == 2)
+ {
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">%.2f</INFLUENCE>\n", joint_b + 1, fmod(mesh_weights[i], 1.f));
+ }
+ apr_file_printf(fp, " </VERTEX>\n");
+ }
+
+ LLPolyFace* mesh_faces = mMesh->getFaces();
+ for (S32 i = 0; i < mMesh->getNumFaces(); i++)
+ {
+ apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", mesh_faces[i][0], mesh_faces[i][1], mesh_faces[i][2]);
+ }
+
+ apr_file_printf(fp, " </SUBMESH>\n");
+}
+
+// End
diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h
new file mode 100644
index 0000000000..963ad05595
--- /dev/null
+++ b/indra/newview/llviewerjointmesh.h
@@ -0,0 +1,138 @@
+/**
+ * @file llviewerjointmesh.h
+ * @brief Implementation of LLViewerJointMesh class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERJOINTMESH_H
+#define LL_LLVIEWERJOINTMESH_H
+
+#include "llviewerjoint.h"
+#include "llviewerimage.h"
+#include "llpolymesh.h"
+#include "v4color.h"
+#include "llapr.h"
+
+class LLDrawable;
+class LLFace;
+class LLCharacter;
+class LLTexLayerSet;
+
+typedef enum e_avatar_render_pass
+{
+ AVATAR_RENDER_PASS_SINGLE,
+ AVATAR_RENDER_PASS_CLOTHING_INNER,
+ AVATAR_RENDER_PASS_CLOTHING_OUTER
+} EAvatarRenderPass;
+
+class LLSkinJoint
+{
+public:
+ LLSkinJoint();
+ ~LLSkinJoint();
+ BOOL setupSkinJoint( LLViewerJoint *joint);
+
+ LLViewerJoint *mJoint;
+ LLVector3 mRootToJointSkinOffset;
+ LLVector3 mRootToParentJointSkinOffset;
+};
+
+//-----------------------------------------------------------------------------
+// class LLViewerJointMesh
+//-----------------------------------------------------------------------------
+class LLViewerJointMesh : public LLViewerJoint
+{
+protected:
+ LLColor4 mColor; // color value
+// LLColor4 mSpecular; // specular color (always white for now)
+ F32 mShiny; // shiny value
+ LLPointer<LLViewerImage> mTexture; // ptr to a global texture
+ LLTexLayerSet* mLayerSet; // ptr to a layer set owned by the avatar
+ U32 mTestImageName; // handle to a temporary texture for previewing uploads
+ LLPolyMesh* mMesh; // ptr to a global polymesh
+ BOOL mCullBackFaces; // true by default
+ LLFace* mFace; // ptr to a face w/ AGP copy of mesh
+
+ U32 mFaceIndexCount;
+ BOOL mIsTransparent;
+
+ U32 mNumSkinJoints;
+ LLSkinJoint* mSkinJoints;
+ S32 mMeshID;
+
+public:
+ static BOOL sPipelineRender;
+ //RN: this is here for testing purposes
+ static U32 sClothingMaskImageName;
+ static EAvatarRenderPass sRenderPass;
+ static LLColor4 sClothingInnerColor;
+
+public:
+ // Constructor
+ LLViewerJointMesh();
+
+ // Destructor
+ virtual ~LLViewerJointMesh();
+
+ // Gets the shape color
+ void getColor( F32 *red, F32 *green, F32 *blue, F32 *alpha );
+
+ // Sets the shape color
+ void setColor( F32 red, F32 green, F32 blue, F32 alpha );
+
+ // Sets the shininess
+ void setSpecular( const LLColor4& color, F32 shiny ) { /*mSpecular = color;*/ mShiny = shiny; };
+
+ // Sets the shape texture
+ void setTexture( LLViewerImage *texture );
+
+ void setTestTexture( U32 name ) { mTestImageName = name; }
+
+ // Sets layer set responsible for a dynamic shape texture (takes precedence over normal texture)
+ void setLayerSet( LLTexLayerSet* layer_set );
+
+ // Gets the poly mesh
+ LLPolyMesh *getMesh();
+
+ // Sets the poly mesh
+ void setMesh( LLPolyMesh *mesh );
+
+ // Sets up joint matrix data for rendering
+ void setupJoint(LLViewerJoint* current_joint);
+
+ // Render time method to upload batches of joint matrices
+ void uploadJointMatrices();
+
+ // Sets ID for picking
+ void setMeshID( S32 id ) {mMeshID = id;}
+
+ // Gets ID for picking
+ S32 getMeshID() { return mMeshID; }
+
+ // overloaded from base class
+ /*virtual*/ void drawBone();
+ /*virtual*/ BOOL isTransparent();
+ /*virtual*/ U32 drawShape( F32 pixelArea );
+
+ /*virtual*/ void updateFaceSizes(U32 &num_vertices, F32 pixel_area);
+ /*virtual*/ void updateFaceData(LLFace *face, F32 pixel_area, BOOL damp_wind = FALSE);
+ /*virtual*/ BOOL updateLOD(F32 pixel_area, BOOL activate);
+ /*virtual*/ void dump();
+
+ void setIsTransparent(BOOL is_transparent) { mIsTransparent = is_transparent; }
+
+ /*virtual*/ BOOL isAnimatable() { return FALSE; }
+ void writeCAL3D(apr_file_t* fp, S32 material_num, LLCharacter* characterp);
+private:
+ // Allocate skin data
+ BOOL allocateSkinData( U32 numSkinJoints );
+
+ S32 getBoundJointsByIndex(S32 index, S32 &joint_a, S32& joint_b);
+
+ // Free skin data
+ void freeSkinData();
+};
+
+#endif // LL_LLVIEWERJOINTMESH_H
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
new file mode 100644
index 0000000000..8f31f1192a
--- /dev/null
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -0,0 +1,833 @@
+/**
+ * @file llviewerkeyboard.cpp
+ * @brief LLViewerKeyboard class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerkeyboard.h"
+#include "llmath.h"
+#include "llagent.h"
+#include "llchatbar.h"
+#include "llviewercontrol.h"
+#include "llfocusmgr.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "lltoolfocus.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+
+//
+// Constants
+//
+
+const F32 FLY_TIME = 0.5f;
+const F32 FLY_FRAMES = 4;
+
+const F32 NUDGE_TIME = 0.25f; // in seconds
+const S32 NUDGE_FRAMES = 2;
+const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed
+const F32 YAW_NUDGE_RATE = 0.05f; // fraction of normal speed
+
+LLViewerKeyboard gViewerKeyboard;
+
+void agent_jump( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < FLY_TIME
+ || frame_count <= FLY_FRAMES
+ || gAgent.upGrabbed()
+ || !gSavedSettings.getBOOL("AutomaticFly"))
+ {
+ gAgent.moveUp(1);
+ }
+ else
+ {
+ gAgent.setFlying(TRUE);
+ gAgent.moveUp(1);
+ }
+}
+
+void agent_push_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.moveUp(-1);
+}
+
+void agent_push_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ gAgent.moveAtNudge(1);
+ }
+ else
+ {
+ gAgent.moveAt(1);
+ }
+}
+
+
+void agent_push_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ gAgent.moveAtNudge(-1);
+ }
+ else
+ {
+ gAgent.moveAt(-1);
+ }
+}
+
+void agent_slide_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ if (frame_count == 0)
+ {
+ // only send nudge on first frame
+ gAgent.moveLeftNudge(1);
+ }
+ }
+ else
+ {
+ gAgent.moveLeft(1);
+ }
+}
+
+
+void agent_slide_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ S32 frame_count = llround(gKeyboard->getCurKeyElapsedFrameCount());
+
+ if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES)
+ {
+ if (frame_count == 0)
+ {
+ // only send nudge on first frame
+ gAgent.moveLeftNudge(-1);
+ }
+ }
+ else
+ {
+ gAgent.moveLeft(-1);
+ }
+}
+
+void agent_turn_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if (gToolCamera->mouseSteerMode())
+ {
+ agent_slide_left(s);
+ }
+ else
+ {
+ gAgent.moveYaw( LLFloaterMove::getYawRate( time ) );
+ }
+}
+
+
+void agent_turn_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if (gToolCamera->mouseSteerMode())
+ {
+ agent_slide_right(s);
+ }
+ else
+ {
+ gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) );
+ }
+}
+
+void agent_look_up( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.movePitch(-1);
+ //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
+}
+
+
+void agent_look_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.movePitch(1);
+ //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() );
+}
+
+void agent_toggle_fly( EKeystate s )
+{
+ // Only catch the edge
+ if (KEYSTATE_DOWN == s )
+ {
+ gAgent.toggleFlying();
+ }
+}
+
+F32 get_orbit_rate()
+{
+ F32 time = gKeyboard->getCurKeyElapsedTime();
+ if( time < NUDGE_TIME )
+ {
+ F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME;
+ //llinfos << rate << llendl;
+ return rate;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+void camera_spin_around_ccw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+}
+
+
+void camera_spin_around_cw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+}
+
+void camera_spin_around_ccw_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.rotateGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_turn_right(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_around_cw_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.rotateGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_turn_left(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_over( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+}
+
+
+void camera_spin_under( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+}
+
+void camera_spin_over_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.upGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_jump(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_spin_under_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.downGrabbed() || gAgent.sitCameraEnabled())
+ {
+ //send keystrokes, but do not change camera
+ agent_push_down(s);
+ }
+ else
+ {
+ //change camera but do not send keystrokes
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+ }
+}
+
+void camera_move_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitInKey( get_orbit_rate() );
+}
+
+
+void camera_move_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+}
+
+void camera_move_forward_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ if (gAgent.forwardGrabbed() || gAgent.sitCameraEnabled())
+ {
+ agent_push_forward(s);
+ }
+ else
+ {
+ gAgent.setOrbitInKey( get_orbit_rate() );
+ }
+}
+
+
+void camera_move_backward_sitting( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+
+ if (gAgent.backwardGrabbed() || gAgent.sitCameraEnabled())
+ {
+ agent_push_backward(s);
+ }
+ else
+ {
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+ }
+}
+
+void camera_pan_up( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanUpKey( get_orbit_rate() );
+}
+
+void camera_pan_down( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanDownKey( get_orbit_rate() );
+}
+
+void camera_pan_left( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanLeftKey( get_orbit_rate() );
+}
+
+void camera_pan_right( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanRightKey( get_orbit_rate() );
+}
+
+void camera_pan_in( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanInKey( get_orbit_rate() );
+}
+
+void camera_pan_out( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setPanOutKey( get_orbit_rate() );
+}
+
+void camera_move_forward_fast( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitInKey(2.5f);
+}
+
+void camera_move_backward_fast( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gAgent.unlockView();
+ gAgent.setOrbitOutKey(2.5f);
+}
+
+
+void edit_avatar_spin_ccw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitLeftKey( get_orbit_rate() );
+ //gMorphView->orbitLeft( get_orbit_rate() );
+}
+
+
+void edit_avatar_spin_cw( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitRightKey( get_orbit_rate() );
+ //gMorphView->orbitRight( get_orbit_rate() );
+}
+
+void edit_avatar_spin_over( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitUpKey( get_orbit_rate() );
+ //gMorphView->orbitUp( get_orbit_rate() );
+}
+
+
+void edit_avatar_spin_under( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitDownKey( get_orbit_rate() );
+ //gMorphView->orbitDown( get_orbit_rate() );
+}
+
+void edit_avatar_move_forward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitInKey( get_orbit_rate() );
+ //gMorphView->orbitIn();
+}
+
+
+void edit_avatar_move_backward( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ gMorphView->setCameraDrivenByKeys( TRUE );
+ gAgent.setOrbitOutKey( get_orbit_rate() );
+ //gMorphView->orbitOut();
+}
+
+void stop_moving( EKeystate s )
+{
+ if( KEYSTATE_UP == s ) return;
+ // stop agent
+ gAgent.setControlFlags(AGENT_CONTROL_STOP);
+
+ // cancel autopilot
+ gAgent.stopAutoPilot();
+}
+
+void start_chat( EKeystate s )
+{
+ if (!gChatBar->inputEditorHasFocus())
+ {
+ // start chat
+ gChatBar->startChat(NULL);
+ }
+}
+
+void bind_keyboard_functions()
+{
+ gViewerKeyboard.bindNamedFunction("jump", agent_jump);
+ gViewerKeyboard.bindNamedFunction("push_down", agent_push_down);
+ gViewerKeyboard.bindNamedFunction("push_forward", agent_push_forward);
+ gViewerKeyboard.bindNamedFunction("push_backward", agent_push_backward);
+ gViewerKeyboard.bindNamedFunction("look_up", agent_look_up);
+ gViewerKeyboard.bindNamedFunction("look_down", agent_look_down);
+ gViewerKeyboard.bindNamedFunction("toggle_fly", agent_toggle_fly);
+ gViewerKeyboard.bindNamedFunction("turn_left", agent_turn_left);
+ gViewerKeyboard.bindNamedFunction("turn_right", agent_turn_right);
+ gViewerKeyboard.bindNamedFunction("slide_left", agent_slide_left);
+ gViewerKeyboard.bindNamedFunction("slide_right", agent_slide_right);
+ gViewerKeyboard.bindNamedFunction("spin_around_ccw", camera_spin_around_ccw);
+ gViewerKeyboard.bindNamedFunction("spin_around_cw", camera_spin_around_cw);
+ gViewerKeyboard.bindNamedFunction("spin_around_ccw_sitting", camera_spin_around_ccw_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_around_cw_sitting", camera_spin_around_cw_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_over", camera_spin_over);
+ gViewerKeyboard.bindNamedFunction("spin_under", camera_spin_under);
+ gViewerKeyboard.bindNamedFunction("spin_over_sitting", camera_spin_over_sitting);
+ gViewerKeyboard.bindNamedFunction("spin_under_sitting", camera_spin_under_sitting);
+ gViewerKeyboard.bindNamedFunction("move_forward", camera_move_forward);
+ gViewerKeyboard.bindNamedFunction("move_backward", camera_move_backward);
+ gViewerKeyboard.bindNamedFunction("move_forward_sitting", camera_move_forward_sitting);
+ gViewerKeyboard.bindNamedFunction("move_backward_sitting", camera_move_backward_sitting);
+ gViewerKeyboard.bindNamedFunction("pan_up", camera_pan_up);
+ gViewerKeyboard.bindNamedFunction("pan_down", camera_pan_down);
+ gViewerKeyboard.bindNamedFunction("pan_left", camera_pan_left);
+ gViewerKeyboard.bindNamedFunction("pan_right", camera_pan_right);
+ gViewerKeyboard.bindNamedFunction("pan_in", camera_pan_in);
+ gViewerKeyboard.bindNamedFunction("pan_out", camera_pan_out);
+ gViewerKeyboard.bindNamedFunction("move_forward_fast", camera_move_forward_fast);
+ gViewerKeyboard.bindNamedFunction("move_backward_fast", camera_move_backward_fast);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_ccw", edit_avatar_spin_ccw);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_cw", edit_avatar_spin_cw);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_over", edit_avatar_spin_over);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_spin_under", edit_avatar_spin_under);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_move_forward", edit_avatar_move_forward);
+ gViewerKeyboard.bindNamedFunction("edit_avatar_move_backward", edit_avatar_move_backward);
+ gViewerKeyboard.bindNamedFunction("stop_moving", stop_moving);
+ gViewerKeyboard.bindNamedFunction("start_chat", start_chat);
+}
+
+LLViewerKeyboard::LLViewerKeyboard()
+{
+ for (S32 i = 0; i < MODE_COUNT; i++)
+ {
+ mBindingCount[i] = 0;
+ }
+
+ for (S32 i = 0; i < KEY_COUNT; i++)
+ {
+ mKeyHandledByUI[i] = FALSE;
+ }
+ // we want the UI to never see these keys so that they can always control the avatar/camera
+ for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++)
+ {
+ mKeysSkippedByUI.insert(k);
+ }
+}
+
+
+void LLViewerKeyboard::bindNamedFunction(const char *name, LLKeyFunc func)
+{
+ S32 i = mNamedFunctionCount;
+ mNamedFunctions[i].mName = name;
+ mNamedFunctions[i].mFunction = func;
+ mNamedFunctionCount++;
+}
+
+
+BOOL LLViewerKeyboard::modeFromString(const char *string, S32 *mode)
+{
+ if (!strcmp(string, "FIRST_PERSON"))
+ {
+ *mode = MODE_FIRST_PERSON;
+ return TRUE;
+ }
+ else if (!strcmp(string, "THIRD_PERSON"))
+ {
+ *mode = MODE_THIRD_PERSON;
+ return TRUE;
+ }
+ else if (!strcmp(string, "EDIT"))
+ {
+ *mode = MODE_EDIT;
+ return TRUE;
+ }
+ else if (!strcmp(string, "EDIT_AVATAR"))
+ {
+ *mode = MODE_EDIT_AVATAR;
+ return TRUE;
+ }
+ else if (!strcmp(string, "SITTING"))
+ {
+ *mode = MODE_SITTING;
+ return TRUE;
+ }
+ else
+ {
+ *mode = MODE_THIRD_PERSON;
+ return FALSE;
+ }
+}
+
+BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated)
+{
+ // check for re-map
+ EKeyboardMode mode = gViewerKeyboard.getMode();
+ U32 keyidx = (translated_mask<<16) | translated_key;
+ key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx);
+ if (iter != mRemapKeys[mode].end())
+ {
+ translated_key = (iter->second) & 0xff;
+ translated_mask = (iter->second)>>16;
+ }
+
+ // No repeats of F-keys
+ BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12);
+ if (!repeatable_key && repeated)
+ {
+ return FALSE;
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "keydown -" << translated_key << "-" << llendl;
+ // skip skipped keys
+ if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end())
+ {
+ mKeyHandledByUI[translated_key] = FALSE;
+ }
+ else
+ {
+ // it is sufficient to set this value once per call to handlekey
+ // without clearing it, as it is only used in the subsequent call to scanKey
+ mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask);
+ }
+ return mKeyHandledByUI[translated_key];
+}
+
+
+
+BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const char *function_name)
+{
+ S32 i,index;
+ void (*function)(EKeystate keystate) = NULL;
+ const char *name = NULL;
+
+ // Allow remapping of F2-F12
+ if (function_name[0] == 'F')
+ {
+ int c1 = function_name[1] - '0';
+ int c2 = function_name[2] ? function_name[2] - '0' : -1;
+ if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9)
+ {
+ int idx = c1;
+ if (c2 >= 0)
+ idx = idx*10 + c2;
+ if (idx >=2 && idx <= 12)
+ {
+ U32 keyidx = ((mask<<16)|key);
+ (mRemapKeys[mode])[keyidx] = ((0<<16)|KEY_F1+(idx-1));
+ return TRUE;
+ }
+ }
+ }
+
+ // Not remapped, look for a function
+ for (i = 0; i < mNamedFunctionCount; i++)
+ {
+ if (!strcmp(function_name, mNamedFunctions[i].mName))
+ {
+ function = mNamedFunctions[i].mFunction;
+ name = mNamedFunctions[i].mName;
+ }
+ }
+
+ if (!function)
+ {
+ llerrs << "Can't bind key to function " << function_name << ", no function with this name found" << llendl;
+ return FALSE;
+ }
+
+ // check for duplicate first and overwrite
+ for (index = 0; index < mBindingCount[mode]; index++)
+ {
+ if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask)
+ break;
+ }
+
+ if (index >= MAX_KEY_BINDINGS)
+ {
+ llerrs << "LLKeyboard::bindKey() - too many keys for mode " << mode << llendl;
+ return FALSE;
+ }
+
+ if (mode >= MODE_COUNT)
+ {
+ llerror("LLKeyboard::bindKey() - unknown mode passed", mode);
+ return FALSE;
+ }
+
+ mBindings[mode][index].mKey = key;
+ mBindings[mode][index].mMask = mask;
+// mBindings[mode][index].mName = name;
+ mBindings[mode][index].mFunction = function;
+
+ if (index == mBindingCount[mode])
+ mBindingCount[mode]++;
+
+ // printf("Bound key %c to %s\n", key, name);
+
+ return TRUE;
+}
+
+
+S32 LLViewerKeyboard::loadBindings(const char *filename)
+{
+ FILE *fp;
+ const S32 BUFFER_SIZE = 2048;
+ char buffer[BUFFER_SIZE];
+ char mode_string[MAX_STRING];
+ char key_string[MAX_STRING];
+ char mask_string[MAX_STRING];
+ char function_string[MAX_STRING];
+ S32 mode = MODE_THIRD_PERSON;
+ KEY key = 0;
+ MASK mask = 0;
+ S32 tokens_read;
+ S32 binding_count = 0;
+ S32 line_count = 0;
+
+ fp = LLFile::fopen(filename, "r");
+
+ if (!fp)
+ {
+ return 0;
+ }
+
+
+ while (!feof(fp))
+ {
+ line_count++;
+ if (!fgets(buffer, BUFFER_SIZE, fp))
+ break;
+
+ // skip over comments, blank lines
+ if (buffer[0] == '#' || buffer[0] == '\n') continue;
+
+ // grab the binding strings
+ tokens_read = sscanf(buffer, "%s %s %s %s", mode_string, key_string, mask_string, function_string);
+
+ if (tokens_read == EOF)
+ {
+ llinfos << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << llendl;
+ fclose(fp);
+ return 0;
+ }
+ else if (tokens_read < 4)
+ {
+ llinfos << "Can't read line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // convert mode
+ if (!modeFromString(mode_string, &mode))
+ {
+ llinfos << "Unknown mode on line " << line_count << " of key binding file " << filename << llendl;
+ llinfos << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << llendl;
+ continue;
+ }
+
+ // convert key
+ if (!LLKeyboard::keyFromString(key_string, &key))
+ {
+ llinfos << "Can't interpret key on line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // convert mask
+ if (!LLKeyboard::maskFromString(mask_string, &mask))
+ {
+ llinfos << "Can't interpret mask on line " << line_count << " of key binding file " << filename << llendl;
+ continue;
+ }
+
+ // bind key
+ if (bindKey(mode, key, mask, function_string))
+ {
+ binding_count++;
+ }
+ }
+
+ fclose(fp);
+
+ return binding_count;
+}
+
+
+EKeyboardMode LLViewerKeyboard::getMode()
+{
+ if ( gAgent.cameraMouselook() )
+ {
+ return MODE_FIRST_PERSON;
+ }
+ else if ( gMorphView && gMorphView->getVisible())
+ {
+ return MODE_EDIT_AVATAR;
+ }
+ else if (gAgent.getAvatarObject() && gAgent.getAvatarObject()->mIsSitting)
+ {
+ return MODE_SITTING;
+ }
+ else
+ {
+ return MODE_THIRD_PERSON;
+ }
+}
+
+
+// Called from scanKeyboard.
+void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+ S32 mode = getMode();
+ // Consider keyboard scanning as NOT mouse event. JC
+ MASK mask = gKeyboard->currentMask(FALSE);
+
+ LLKeyBinding* binding = mBindings[mode];
+ S32 binding_count = mBindingCount[mode];
+
+
+ if (mKeyHandledByUI[key])
+ {
+ return;
+ }
+
+ // don't process key down on repeated keys
+ BOOL repeat = gKeyboard->getKeyRepeated(key);
+
+ for (S32 i = 0; i < binding_count; i++)
+ {
+ //for (S32 key = 0; key < KEY_COUNT; key++)
+ if (binding[i].mKey == key)
+ {
+ //if (binding[i].mKey == key && binding[i].mMask == mask)
+ if (binding[i].mMask == mask)
+ {
+ if (key_down && !repeat)
+ {
+ // ...key went down this frame, call function
+ (*binding[i].mFunction)( KEYSTATE_DOWN );
+ }
+ else if (key_up)
+ {
+ // ...key went down this frame, call function
+ (*binding[i].mFunction)( KEYSTATE_UP );
+ }
+ else if (key_level)
+ {
+ // ...key held down from previous frame
+ // Not windows, just call the function.
+ (*binding[i].mFunction)( KEYSTATE_LEVEL );
+ }//if
+ }//if
+ }//for
+ }//for
+}
diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h
new file mode 100644
index 0000000000..2272aa5d11
--- /dev/null
+++ b/indra/newview/llviewerkeyboard.h
@@ -0,0 +1,71 @@
+/**
+ * @file llviewerkeyboard.h
+ * @brief LLViewerKeyboard class header file
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERKEYBOARD_H
+#define LL_LLVIEWERKEYBOARD_H
+
+#include "llkeyboard.h" // For EKeystate
+
+const S32 MAX_NAMED_FUNCTIONS = 100;
+const S32 MAX_KEY_BINDINGS = 128; // was 60
+
+class LLNamedFunction
+{
+public:
+ const char *mName;
+ LLKeyFunc mFunction;
+};
+
+typedef enum e_keyboard_mode
+{
+ MODE_FIRST_PERSON,
+ MODE_THIRD_PERSON,
+ MODE_EDIT,
+ MODE_EDIT_AVATAR,
+ MODE_SITTING,
+ MODE_COUNT
+} EKeyboardMode;
+
+
+void agent_push_forward( EKeystate s );
+void agent_turn_right( EKeystate s );
+void bind_keyboard_functions();
+
+class LLViewerKeyboard
+{
+public:
+ LLViewerKeyboard();
+
+ BOOL handleKey(KEY key, MASK mask, BOOL repeated);
+
+ void bindNamedFunction(const char *name, LLKeyFunc func);
+
+ S32 loadBindings(const char *filename); // returns number bound, 0 on error
+ EKeyboardMode getMode();
+
+ BOOL modeFromString(const char *string, S32 *mode); // False on failure
+
+ void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+protected:
+ BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const char *function_name);
+ S32 mNamedFunctionCount;
+ LLNamedFunction mNamedFunctions[MAX_NAMED_FUNCTIONS];
+
+ // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here
+ S32 mBindingCount[MODE_COUNT];
+ LLKeyBinding mBindings[MODE_COUNT][MAX_KEY_BINDINGS];
+
+ typedef std::map<U32, U32> key_remap_t;
+ key_remap_t mRemapKeys[MODE_COUNT];
+ std::set<KEY> mKeysSkippedByUI;
+ BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI
+};
+
+extern LLViewerKeyboard gViewerKeyboard;
+
+#endif // LL_LLVIEWERKEYBOARD_H
diff --git a/indra/newview/llviewerlayer.cpp b/indra/newview/llviewerlayer.cpp
new file mode 100644
index 0000000000..a3ac9e0859
--- /dev/null
+++ b/indra/newview/llviewerlayer.cpp
@@ -0,0 +1,82 @@
+/**
+ * @file llviewerlayer.cpp
+ * @brief LLViewerLayer class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerlayer.h"
+#include "llerror.h"
+#include "llmath.h"
+
+LLViewerLayer::LLViewerLayer(const S32 width, const F32 scale)
+{
+ mWidth = width;
+ mScale = scale;
+ mScaleInv = 1.f/scale;
+ mDatap = new F32[width*width];
+
+ for (S32 i = 0; i < width*width; i++)
+ {
+ *(mDatap + i) = 0.f;
+ }
+}
+
+LLViewerLayer::~LLViewerLayer()
+{
+ delete[] mDatap;
+ mDatap = NULL;
+}
+
+F32 LLViewerLayer::getValue(const S32 x, const S32 y) const
+{
+// llassert(x >= 0);
+// llassert(x < mWidth);
+// llassert(y >= 0);
+// llassert(y < mWidth);
+
+ return *(mDatap + x + y*mWidth);
+}
+
+F32 LLViewerLayer::getValueScaled(const F32 x, const F32 y) const
+{
+ S32 x1, x2, y1, y2;
+ F32 x_frac, y_frac;
+
+ x_frac = x*mScaleInv;
+ x1 = llfloor(x_frac);
+ x2 = x1 + 1;
+ x_frac -= x1;
+
+ y_frac = y*mScaleInv;
+ y1 = llfloor(y_frac);
+ y2 = y1 + 1;
+ y_frac -= y1;
+
+ x1 = llmin((S32)mWidth-1, x1);
+ x1 = llmax(0, x1);
+ x2 = llmin((S32)mWidth-1, x2);
+ x2 = llmax(0, x2);
+ y1 = llmin((S32)mWidth-1, y1);
+ y1 = llmax(0, y1);
+ y2 = llmin((S32)mWidth-1, y2);
+ y2 = llmax(0, y2);
+
+ // Take weighted average of all four points (bilinear interpolation)
+ S32 row1 = y1 * mWidth;
+ S32 row2 = y2 * mWidth;
+
+ // Access in squential order in memory, and don't use immediately.
+ F32 row1_left = mDatap[ row1 + x1 ];
+ F32 row1_right = mDatap[ row1 + x2 ];
+ F32 row2_left = mDatap[ row2 + x1 ];
+ F32 row2_right = mDatap[ row2 + x2 ];
+
+ F32 row1_interp = row1_left - x_frac * (row1_left - row1_right);
+ F32 row2_interp = row2_left - x_frac * (row2_left - row2_right);
+
+ return row1_interp - y_frac * (row1_interp - row2_interp);
+}
diff --git a/indra/newview/llviewerlayer.h b/indra/newview/llviewerlayer.h
new file mode 100644
index 0000000000..47f4780009
--- /dev/null
+++ b/indra/newview/llviewerlayer.h
@@ -0,0 +1,30 @@
+/**
+ * @file llviewerlayer.h
+ * @brief LLViewerLayer class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERLAYER_H
+#define LL_LLVIEWERLAYER_H
+
+// Viewer-side representation of a layer...
+
+class LLViewerLayer
+{
+public:
+ LLViewerLayer(const S32 width, const F32 scale = 1.f);
+ virtual ~LLViewerLayer();
+
+ F32 getValueScaled(const F32 x, const F32 y) const;
+protected:
+ F32 getValue(const S32 x, const S32 y) const;
+protected:
+ S32 mWidth;
+ F32 mScale;
+ F32 mScaleInv;
+ F32 *mDatap;
+};
+
+#endif // LL_LLVIEWERLAYER_H
diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp
new file mode 100644
index 0000000000..1bf279bf2b
--- /dev/null
+++ b/indra/newview/llviewermenu.cpp
@@ -0,0 +1,8810 @@
+/**
+ * @file llviewermenu.cpp
+ * @brief Builds menus out of items.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermenu.h"
+
+// system library includes
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <boost/tokenizer.hpp>
+
+// linden library includes
+#include "audioengine.h"
+#include "indra_constants.h"
+#include "llassetuploadresponders.h"
+#include "llassetstorage.h"
+#include "llchat.h"
+#include "lleconomy.h"
+#include "llfocusmgr.h"
+#include "llfontgl.h"
+#include "llinstantmessage.h"
+#include "llpermissionsflags.h"
+#include "llrect.h"
+#include "llsecondlifeurls.h"
+#include "lltransactiontypes.h"
+#include "llui.h"
+#include "llview.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "raytrace.h"
+#include "llsdserialize.h"
+#include "lltimer.h"
+#include "vorbisencode.h"
+#include "llvfile.h"
+#include "llvolumemgr.h"
+#include "llwindow.h" // for shell_open()
+
+// newview includes
+#include "llagent.h"
+#include "llagentpilot.h"
+#include "llbox.h"
+#include "llcallingcard.h"
+#include "llcameraview.h"
+#include "llclipboard.h"
+#include "llcompilequeue.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "lldebugview.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolhud.h"
+#include "lldrawpooltree.h"
+#include "llface.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloaterabout.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloateranimpreview.h"
+#include "llfloateravatarinfo.h"
+#include "llfloateravatartextures.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterbump.h"
+#include "llfloaterbuy.h"
+#include "llfloaterbuycontents.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterbuyland.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatereditui.h"
+#include "llfloaterfriends.h"
+#include "llfloatergesture.h"
+#include "llfloatergodtools.h"
+#include "llfloatergroupinfo.h"
+#include "llfloatergroups.h"
+#include "llfloaterhtmlhelp.h"
+#include "llfloaterhtmlfind.h"
+#include "llfloaterimport.h"
+#include "llfloaterland.h"
+#include "llfloaterlandholdings.h"
+#include "llfloatermap.h"
+#include "llfloateraccounthistory.h"
+#include "llfloaterimagepreview.h"
+#include "llfloatermute.h"
+#include "llfloaternamedesc.h"
+#include "llfloateropenobject.h"
+#include "llfloaterpermissionsmgr.h"
+#include "llfloaterpreference.h"
+#include "llfloaterrate.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterreporter.h"
+#include "llfloaterscriptdebug.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertest.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llframestats.h"
+#include "llframestatview.h"
+#include "llfasttimerview.h"
+#include "llmemoryview.h"
+#include "llgivemoney.h"
+#include "llgroupmgr.h"
+#include "llhoverview.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagetga.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "llpanellogin.h"
+#include "llmenucommands.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llmutelist.h"
+#include "llnotify.h"
+#include "llpanelobject.h"
+#include "llparcel.h"
+#include "llpreviewscript.h"
+#include "llpreviewtexture.h"
+#include "llprimitive.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llstatusbar.h"
+#include "llstatview.h"
+#include "llstring.h"
+#include "llsurfacepatch.h"
+#include "llimview.h"
+#include "lltextureview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolcomp.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselectland.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "lluserauth.h"
+#include "lluuid.h"
+#include "llvelocitybar.h"
+#include "llviewercamera.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermessage.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvolume.h"
+#include "llweb.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "object_flags.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "roles_constants.h"
+
+#include "lltexlayer.h"
+
+void init_client_menu(LLMenuGL* menu);
+void init_server_menu(LLMenuGL* menu);
+
+void init_debug_world_menu(LLMenuGL* menu);
+void init_debug_rendering_menu(LLMenuGL* menu);
+void init_debug_ui_menu(LLMenuGL* menu);
+void init_debug_xui_menu(LLMenuGL* menu);
+void init_debug_avatar_menu(LLMenuGL* menu);
+void init_debug_baked_texture_menu(LLMenuGL* menu);
+
+BOOL enable_land_build(void*);
+BOOL enable_object_build(void*);
+
+LLVOAvatar* find_avatar_from_object( LLViewerObject* object );
+LLVOAvatar* find_avatar_from_object( const LLUUID& object_id );
+
+void handle_test_load_url(void*);
+
+extern void disconnect_viewer(void *);
+
+//
+// Evil hackish imported globals
+//
+extern BOOL gRenderLightGlows;
+extern BOOL gRenderAvatar;
+extern BOOL gHideSelectedObjects;
+extern BOOL gShowOverlayTitle;
+extern BOOL gRandomizeFramerate;
+extern BOOL gPeriodicSlowFrame;
+extern BOOL gOcclusionCull;
+extern BOOL gAllowSelectAvatar;
+
+//
+// Globals
+//
+
+LLMenuBarGL *gMenuBarView = NULL;
+LLViewerMenuHolderGL *gMenuHolder = NULL;
+LLMenuGL *gPopupMenuView = NULL;
+
+// Pie menus
+LLPieMenu *gPieSelf = NULL;
+LLPieMenu *gPieAvatar = NULL;
+LLPieMenu *gPieObject = NULL;
+LLPieMenu *gPieAttachment = NULL;
+LLPieMenu *gPieLand = NULL;
+
+// local constants
+const LLString CLIENT_MENU_NAME("Client");
+const LLString SERVER_MENU_NAME("Server");
+
+const LLString SAVE_INTO_INVENTORY("Save Object Back to My Inventory");
+const LLString SAVE_INTO_TASK_INVENTORY("Save Object Back to Object Contents");
+
+#if LL_WINDOWS
+static const char* SOUND_EXTENSIONS = ".wav";
+static const char* IMAGE_EXTENSIONS = ".tga .bmp .jpg .jpeg";
+static const char* ANIM_EXTENSIONS = ".bvh";
+#ifdef _CORY_TESTING
+static const char* GEOMETRY_EXTENSIONS = ".slg";
+#endif
+static const char* XML_EXTENSIONS = ".xml";
+static const char* SLOBJECT_EXTENSIONS = ".slobject";
+#endif
+static const char* ALL_FILE_EXTENSIONS = "*.*";
+
+LLMenuGL* gAttachSubMenu = NULL;
+LLMenuGL* gDetachSubMenu = NULL;
+LLMenuGL* gTakeOffClothes = NULL;
+LLPieMenu* gPieRate = NULL;
+LLPieMenu* gAttachScreenPieMenu = NULL;
+LLPieMenu* gAttachPieMenu = NULL;
+LLPieMenu* gAttachBodyPartPieMenus[8];
+LLPieMenu* gDetachPieMenu = NULL;
+LLPieMenu* gDetachScreenPieMenu = NULL;
+LLPieMenu* gDetachBodyPartPieMenus[8];
+
+LLMenuItemCallGL* gAFKMenu = NULL;
+LLMenuItemCallGL* gBusyMenu = NULL;
+
+typedef LLMemberListener<LLView> view_listener_t;
+
+//
+// Local prototypes
+//
+BOOL enable_attach(void*);
+void handle_leave_group(void *);
+
+// File Menu
+const char* upload_pick(void* data);
+void handle_upload(void* data);
+void handle_upload_object(void* data);
+void handle_compress_image(void*);
+BOOL enable_save_as(void *);
+
+// Edit menu
+void handle_dump_group_info(void *);
+void handle_dump_focus(void*);
+
+void handle_region_dump_settings(void*);
+void handle_region_dump_temp_asset_data(void*);
+void handle_region_clear_temp_asset_data(void*);
+
+// Object pie menu
+BOOL sitting_on_selection();
+
+void near_sit_object();
+void label_sit_or_stand(LLString& label, void*);
+// buy and take alias into the same UI positions, so these
+// declarations handle this mess.
+BOOL is_selection_buy_not_take();
+S32 selection_price();
+BOOL enable_take();
+void handle_take();
+void confirm_take(S32 option, void* data);
+BOOL enable_buy(void*);
+void handle_buy(void *);
+void handle_buy_object(LLSaleInfo sale_info);
+void handle_buy_contents(LLSaleInfo sale_info);
+void label_touch(LLString& label, void*);
+
+// Land pie menu
+void near_sit_down_point(BOOL success, void *);
+
+// Avatar pie menu
+void handle_follow(void *userdata);
+void handle_talk_to(void *userdata);
+
+// Debug menu
+void show_permissions_control(void*);
+void load_url_local_file(const char* file_name);
+void toggle_build_options(void* user_data);
+#if 0 // Unused
+void handle_audio_status_1(void*);
+void handle_audio_status_2(void*);
+void handle_audio_status_3(void*);
+void handle_audio_status_4(void*);
+#endif
+void reload_ui(void*);
+void handle_agent_stop_moving(void*);
+void print_packets_lost(void*);
+void drop_packet(void*);
+void velocity_interpolate( void* data );
+void update_fov(S32 increments);
+void toggle_wind_audio(void);
+void toggle_water_audio(void);
+void handle_rebake_textures(void*);
+BOOL check_admin_override(void*);
+void handle_admin_override_toggle(void*);
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+void handle_toggle_hacked_godmode(void*);
+BOOL check_toggle_hacked_godmode(void*);
+#endif
+
+void toggle_glow(void *);
+BOOL check_glow(void *);
+
+void toggle_vbo(void *);
+BOOL check_vbo(void *);
+
+void toggle_vertex_shaders(void *);
+BOOL check_vertex_shaders(void *);
+
+void toggle_cull_small(void *);
+
+void toggle_show_xui_names(void *);
+BOOL check_show_xui_names(void *);
+
+// Debug UI
+void handle_save_to_xml(void*);
+void handle_load_from_xml(void*);
+
+void handle_god_mode(void*);
+
+// God menu
+void handle_leave_god_mode(void*);
+
+BOOL is_inventory_visible( void* user_data );
+void handle_reset_view();
+
+void disabled_duplicate(void*);
+void handle_duplicate_in_place(void*);
+void handle_repeat_duplicate(void*);
+
+void handle_export(void*);
+void handle_deed_object_to_group(void*);
+BOOL enable_deed_object_to_group(void*);
+void handle_object_owner_self(void*);
+void handle_object_owner_permissive(void*);
+void handle_object_lock(void*);
+void handle_object_asset_ids(void*);
+void force_take_copy(void*);
+#ifdef _CORY_TESTING
+void force_export_copy(void*);
+void force_import_geometry(void*);
+#endif
+
+void handle_force_parcel_owner_to_me(void*);
+void handle_force_parcel_to_content(void*);
+void handle_claim_public_land(void*);
+
+void handle_god_expunge_user(void*);
+
+void handle_god_request_havok(void *);
+void handle_god_request_avatar_geometry(void *); // Hack for easy testing of new avatar geometry
+void reload_personal_settings_overrides(void *);
+void force_breakpoint(void *);
+void reload_vertex_shader(void *);
+void flush_animations(void *);
+void slow_mo_animations(void *);
+void handle_disconnect_viewer(void *);
+
+void handle_stopall(void*);
+void handle_hinge(void*);
+void handle_ptop(void*);
+void handle_lptop(void*);
+void handle_wheel(void*);
+void handle_dehinge(void*);
+BOOL enable_dehinge(void*);
+void handle_force_delete(void*);
+void print_object_info(void*);
+void show_debug_menus();
+void toggle_debug_menus(void*);
+void toggle_map( void* user_data );
+void export_info_callback(LLAssetInfo *info, void **user_data, S32 result);
+void export_data_callback(LLVFS *vfs, const LLUUID& uuid, LLAssetType::EType type, void **user_data, S32 result);
+void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result);
+BOOL menu_check_build_tool( void* user_data );
+void handle_reload_settings(void*);
+void focus_here(void*);
+void dump_select_mgr(void*);
+void dump_volume_mgr(void*);
+void dump_inventory(void*);
+void edit_ui(void*);
+void toggle_visibility(void*);
+BOOL get_visibility(void*);
+
+// Avatar Pie menu
+void request_friendship(const LLUUID& agent_id);
+
+// Tools menu
+void handle_first_tool(void*);
+void handle_next_tool(void*);
+void handle_previous_tool(void*);
+void handle_force_unlock(void*);
+void handle_selected_texture_info(void*);
+void handle_dump_image_list(void*);
+
+void handle_fullscreen_debug(void*);
+void handle_crash(void*);
+void handle_dump_followcam(void*);
+void handle_viewer_enable_circuit_log(void*);
+void handle_viewer_disable_circuit_log(void*);
+void handle_viewer_enable_message_log(void*);
+void handle_viewer_disable_message_log(void*);
+void handle_send_postcard(void*);
+void handle_gestures_old(void*);
+void handle_focus(void *);
+BOOL enable_buy_land(void*);
+void handle_move(void*);
+void handle_show_inventory(void*);
+void handle_activate(void*);
+BOOL enable_activate(void*);
+
+// Help menu
+void handle_buy_currency(void*);
+
+void handle_test_male(void *);
+void handle_test_female(void *);
+void handle_toggle_pg(void*);
+void handle_dump_attachments(void *);
+void handle_show_overlay_title(void*);
+void handle_dump_avatar_local_textures(void*);
+void handle_debug_avatar_textures(void*);
+void handle_grab_texture(void*);
+BOOL enable_grab_texture(void*);
+
+BOOL menu_ui_enabled(void *user_data);
+void check_toggle_control( LLUICtrl *, void* user_data );
+BOOL menu_check_control( void* user_data);
+void menu_toggle_variable( void* user_data );
+BOOL menu_check_variable( void* user_data);
+BOOL enable_land_selected( void* );
+BOOL enable_more_than_one_selected(void* );
+BOOL enable_selection_you_own_all(void*);
+BOOL enable_selection_you_own_one(void*);
+BOOL enable_save_into_inventory(void*);
+BOOL enable_save_into_task_inventory(void*);
+BOOL enable_not_thirdperson(void*);
+BOOL enable_export_selected(void *);
+BOOL enable_have_card(void*);
+BOOL enable_detach(void*);
+BOOL enable_region_owner(void*);
+
+
+class LLMenuParcelObserver : public LLParcelObserver
+{
+public:
+ LLMenuParcelObserver();
+ ~LLMenuParcelObserver();
+ virtual void changed();
+};
+
+static LLMenuParcelObserver* gMenuParcelObserver = NULL;
+
+LLMenuParcelObserver::LLMenuParcelObserver()
+{
+ gParcelMgr->addObserver(this);
+}
+
+LLMenuParcelObserver::~LLMenuParcelObserver()
+{
+ gParcelMgr->removeObserver(this);
+}
+
+void LLMenuParcelObserver::changed()
+{
+ gMenuHolder->childSetEnabled("Land Buy Pass", LLPanelLandGeneral::enableBuyPass(NULL));
+
+ BOOL buyable = enable_buy_land(NULL);
+ gMenuHolder->childSetEnabled("Land Buy", buyable);
+ gMenuHolder->childSetEnabled("Buy Land...", buyable);
+}
+
+
+//-----------------------------------------------------------------------------
+// Menu Construction
+//-----------------------------------------------------------------------------
+
+// code required to calculate anything about the menus
+void pre_init_menus()
+{
+ // static information
+ LLColor4 color;
+ color = gColors.getColor( "MenuDefaultBgColor" );
+ LLMenuGL::setDefaultBackgroundColor( color );
+ color = gColors.getColor( "MenuItemEnabledColor" );
+ LLMenuItemGL::setEnabledColor( color );
+ color = gColors.getColor( "MenuItemDisabledColor" );
+ LLMenuItemGL::setDisabledColor( color );
+ color = gColors.getColor( "MenuItemHighlightBgColor" );
+ LLMenuItemGL::setHighlightBGColor( color );
+ color = gColors.getColor( "MenuItemHighlightFgColor" );
+ LLMenuItemGL::setHighlightFGColor( color );
+}
+
+void initialize_menu_actions();
+
+//-----------------------------------------------------------------------------
+// Initialize main menus
+//
+// HOW TO NAME MENUS:
+//
+// First Letter Of Each Word Is Capitalized, Even At Or And
+//
+// Items that lead to dialog boxes end in "..."
+//
+// Break up groups of more than 6 items with separators
+//-----------------------------------------------------------------------------
+void init_menus()
+{
+ S32 top = gViewerWindow->getRootView()->getRect().getHeight();
+ S32 width = gViewerWindow->getRootView()->getRect().getWidth();
+
+ //
+ // Main menu bar
+ //
+ gMenuHolder = new LLViewerMenuHolderGL();
+ gMenuHolder->setRect(LLRect(0, top - MENU_BAR_HEIGHT, width, STATUS_BAR_HEIGHT));
+ gMenuHolder->setFollowsAll();
+
+ LLMenuGL::sDefaultMenuContainer = gMenuHolder;
+
+ // Initialize actions
+ initialize_menu_actions();
+
+ gMenuBarView = (LLMenuBarGL*)gUICtrlFactory->buildMenu("menu_viewer.xml", gMenuHolder);
+ gMenuBarView->setRect(LLRect(0, top, width, top - MENU_BAR_HEIGHT));
+ gViewerWindow->getRootView()->addChild(gMenuBarView);
+
+ // menu holder appears on top of menu bar so you can see the menu title
+ // flash when an item is triggered (the flash occurs in the holder)
+ gViewerWindow->getRootView()->addChild(gMenuHolder);
+
+ gMenuHolder->childSetLabelArg("Upload Image", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", "10");
+ gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", "10");
+
+ gAFKMenu = (LLMenuItemCallGL*)gMenuBarView->getChildByName("Set Away", TRUE);
+ gBusyMenu = (LLMenuItemCallGL*)gMenuBarView->getChildByName("Set Busy", TRUE);
+ gAttachSubMenu = gMenuBarView->getChildMenuByName("Attach Object", TRUE);
+ gDetachSubMenu = gMenuBarView->getChildMenuByName("Detach Object", TRUE);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gMenuBarView->getChildByName("Menu Underpants", TRUE)->setVisible(FALSE);
+ gMenuBarView->getChildByName("Menu Undershirt", TRUE)->setVisible(FALSE);
+ }
+
+ // TomY TODO convert these two
+ LLMenuGL*menu;
+ menu = new LLMenuGL(CLIENT_MENU_NAME);
+ init_client_menu(menu);
+ gMenuBarView->appendMenu( menu );
+ menu->updateParent(gMenuHolder);
+
+ menu = new LLMenuGL(SERVER_MENU_NAME);
+ init_server_menu(menu);
+ gMenuBarView->appendMenu( menu );
+ menu->updateParent(gMenuHolder);
+
+ gMenuBarView->createJumpKeys();
+
+ ///
+ /// Popup menu
+ ///
+ /// The popup menu is now populated by the show_context_menu()
+ /// method.
+
+ gPopupMenuView = new LLMenuGL( "Popup" );
+ gPopupMenuView->setVisible( FALSE );
+ gMenuHolder->addChild( gPopupMenuView );
+
+ ///
+ /// Pie menus
+ ///
+ gPieSelf = gUICtrlFactory->buildPieMenu("menu_pie_self.xml", gMenuHolder);
+
+ // TomY TODO: what shall we do about these?
+ gDetachScreenPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Detach HUD", true);
+ gDetachPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Detach", true);
+
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gMenuHolder->getChildByName("Self Underpants", TRUE)->setVisible(FALSE);
+ gMenuHolder->getChildByName("Self Undershirt", TRUE)->setVisible(FALSE);
+ }
+
+ gPieAvatar = gUICtrlFactory->buildPieMenu("menu_pie_avatar.xml", gMenuHolder);
+
+ gPieObject = gUICtrlFactory->buildPieMenu("menu_pie_object.xml", gMenuHolder);
+
+ gAttachScreenPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Attach HUD", true);
+ gAttachPieMenu = (LLPieMenu*)gMenuHolder->getChildByName("Object Attach", true);
+ gPieRate = (LLPieMenu*)gMenuHolder->getChildByName("Rate Menu", true);
+
+ gPieAttachment = gUICtrlFactory->buildPieMenu("menu_pie_attachment.xml", gMenuHolder);
+
+ gPieLand = gUICtrlFactory->buildPieMenu("menu_pie_land.xml", gMenuHolder);
+
+ ///
+ /// set up the colors
+ ///
+ LLColor4 color;
+
+ // If we are not in production, use a different color to make it apparent.
+ if (gInProductionGrid)
+ {
+ color = gColors.getColor( "MenuBarBgColor" );
+ }
+ else
+ {
+ color = gColors.getColor( "MenuNonProductionBgColor" );
+ }
+
+ gMenuBarView->setBackgroundColor( color );
+
+ LLColor4 pie_color = gColors.getColor("PieMenuBgColor");
+ gPieSelf->setBackgroundColor( pie_color );
+ gPieAvatar->setBackgroundColor( pie_color );
+ gPieObject->setBackgroundColor( pie_color );
+ gPieAttachment->setBackgroundColor( pie_color );
+ gPieLand->setBackgroundColor( pie_color );
+
+ color = gColors.getColor( "MenuPopupBgColor" );
+ gPopupMenuView->setBackgroundColor( color );
+
+ // Let land based option enable when parcel changes
+ gMenuParcelObserver = new LLMenuParcelObserver();
+
+ //
+ // Debug menu visiblity
+ //
+ show_debug_menus();
+}
+
+void init_client_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = NULL;
+
+ //menu->append(new LLMenuItemCallGL("Permissions Control", &show_permissions_control));
+
+// this is now in the view menu so we don't need it here!
+ {
+ LLMenuGL* sub = new LLMenuGL("Consoles");
+ menu->appendMenu(sub);
+ sub->append(new LLMenuItemCheckGL("Frame Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mFrameStatView,
+ '2', MASK_CONTROL|MASK_SHIFT ) );
+ sub->append(new LLMenuItemCheckGL("Texture Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gTextureView,
+ '3', MASK_CONTROL|MASK_SHIFT ) );
+ LLView* debugview = gDebugView->mDebugConsolep;
+ sub->append(new LLMenuItemCheckGL("Debug Console",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ debugview,
+ '4', MASK_CONTROL|MASK_SHIFT ) );
+#if 0 // Unused
+ {
+ LLMenuGL* sub = new LLMenuGL("Audio");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL("Global Pos",
+ &handle_audio_status_1, NULL, NULL ,'5', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Cone",
+ &handle_audio_status_2, NULL, NULL ,'6', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Local Pos",
+ &handle_audio_status_3, NULL, NULL ,'7', MASK_CONTROL|MASK_SHIFT) );
+ sub->append(new LLMenuItemCallGL("Duration",
+ &handle_audio_status_4, NULL, NULL ,'8', MASK_CONTROL|MASK_SHIFT) );
+ sub->createJumpKeys();
+ }
+#endif
+ sub->append(new LLMenuItemCheckGL("Fast Timers",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mFastTimerView,
+ '9', MASK_CONTROL|MASK_SHIFT ) );
+ sub->append(new LLMenuItemCheckGL("Memory",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gDebugView->mMemoryView,
+ '0', MASK_CONTROL|MASK_SHIFT ) );
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Region Info to Debug Console",
+ &handle_region_dump_settings, NULL));
+ sub->append(new LLMenuItemCallGL("Group Info to Debug Console",
+ &handle_dump_group_info, NULL, NULL));
+ sub->createJumpKeys();
+ }
+
+ // neither of these works particularly well at the moment
+ /*menu->append(new LLMenuItemCallGL( "Reload UI XML", &reload_ui,
+ NULL, NULL, 'R', MASK_ALT | MASK_CONTROL ) );*/
+ /*menu->append(new LLMenuItemCallGL("Reload settings/colors",
+ &handle_reload_settings, NULL, NULL));*/
+ menu->append(new LLMenuItemCallGL("Reload personal setting overrides",
+ &reload_personal_settings_overrides, NULL, NULL, KEY_F2, MASK_CONTROL|MASK_SHIFT));
+
+ sub_menu = new LLMenuGL("HUD Info");
+ {
+ sub_menu->append(new LLMenuItemCheckGL("Velocity",
+ &toggle_visibility,
+ NULL,
+ &get_visibility,
+ (void*)gVelocityBar));
+
+ sub_menu->append(new LLMenuItemToggleGL("Camera", &gDisplayCameraPos ) );
+ sub_menu->append(new LLMenuItemToggleGL("Wind", &gDisplayWindInfo) );
+ sub_menu->append(new LLMenuItemToggleGL("FOV", &gDisplayFOV ) );
+ sub_menu->createJumpKeys();
+ }
+ menu->appendMenu(sub_menu);
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCheckGL( "High-res Snapshot",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"HighResSnapshot"));
+
+ menu->append(new LLMenuItemToggleGL("Quiet Snapshots to Disk",
+ &gQuietSnapshot));
+
+ menu->append(new LLMenuItemCheckGL("Show Mouselook Crosshairs",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowCrosshairs"));
+
+ menu->append(new LLMenuItemCheckGL("Debug Permissions",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"DebugPermissions"));
+
+
+
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid)
+ {
+ menu->append(new LLMenuItemCheckGL("Hacked Godmode",
+ &handle_toggle_hacked_godmode,
+ NULL,
+ &check_toggle_hacked_godmode,
+ (void*)"HackedGodmode"));
+ }
+#endif
+
+ menu->append(new LLMenuItemCallGL("Clear Group Cache",
+ LLGroupMgr::debugClearAllGroups));
+ menu->appendSeparator();
+
+ sub_menu = new LLMenuGL("Rendering");
+ init_debug_rendering_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("World");
+ init_debug_world_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("UI");
+ init_debug_ui_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("XUI");
+ init_debug_xui_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("Character");
+ init_debug_avatar_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+{
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Network");
+
+ sub->append(new LLMenuItemCallGL("Enable Circuit Log",
+ &handle_viewer_enable_circuit_log, NULL));
+ sub->append(new LLMenuItemCallGL("Disable Circuit Log",
+ &handle_viewer_disable_circuit_log, NULL));
+ sub->append(new LLMenuItemCallGL("Enable Message Log",
+ &handle_viewer_enable_message_log, NULL));
+ sub->append(new LLMenuItemCallGL("Disable Message Log",
+ &handle_viewer_disable_message_log, NULL));
+
+ sub->appendSeparator();
+
+ sub->append(new LLMenuItemCheckGL("Velocity Interpolate Objects",
+ &velocity_interpolate,
+ NULL,
+ &menu_check_control,
+ (void*)"VelocityInterpolate"));
+ sub->append(new LLMenuItemCheckGL("Ping Interpolate Object Positions",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"PingInterpolate"));
+
+ sub->appendSeparator();
+
+ sub->append(new LLMenuItemCallGL("Drop a Packet",
+ &drop_packet, NULL, NULL,
+ 'L', MASK_ALT | MASK_CONTROL));
+
+ menu->appendMenu( sub );
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Recorder");
+
+ sub->append(new LLMenuItemCheckGL("Full Session Logging", &menu_toggle_control, NULL, &menu_check_control, (void*)"StatsSessionTrackFrameStats"));
+
+ sub->append(new LLMenuItemCallGL("Start Logging", &LLFrameStats::startLogging, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Logging", &LLFrameStats::stopLogging, NULL));
+ sub->append(new LLMenuItemCallGL("Log 10 Seconds", &LLFrameStats::timedLogging10, NULL));
+ sub->append(new LLMenuItemCallGL("Log 30 Seconds", &LLFrameStats::timedLogging30, NULL));
+ sub->append(new LLMenuItemCallGL("Log 60 Seconds", &LLFrameStats::timedLogging60, NULL));
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Start Playback", &LLAgentPilot::startPlayback, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Playback", &LLAgentPilot::stopPlayback, NULL));
+ sub->append(new LLMenuItemToggleGL("Loop Playback", &LLAgentPilot::sLoop) );
+ sub->append(new LLMenuItemCallGL("Start Record", &LLAgentPilot::startRecord, NULL));
+ sub->append(new LLMenuItemCallGL("Stop Record", &LLAgentPilot::saveRecord, NULL));
+
+ menu->appendMenu( sub );
+ sub->createJumpKeys();
+ }
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemToggleGL("Show Updates",
+ &gShowObjectUpdates,
+ 'U', MASK_ALT | MASK_SHIFT | MASK_CONTROL));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Compress Image...",
+ &handle_compress_image, NULL, NULL));
+
+ menu->append(new LLMenuItemCheckGL("Limit Select Distance",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"LimitSelectDistance"));
+
+ menu->append(new LLMenuItemToggleGL("Disable Camera Constraints",
+ &LLViewerCamera::sDisableCameraConstraints));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCheckGL( "Console Window",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowConsoleWindow"));
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ {
+ LLMenuGL* sub = NULL;
+ sub = new LLMenuGL("Debugging");
+ sub->append(new LLMenuItemCallGL("Force Breakpoint", &force_breakpoint, NULL, NULL, 'B', MASK_CONTROL | MASK_ALT));
+ sub->append(new LLMenuItemCallGL("LLError And Crash", &handle_crash));
+ sub->createJumpKeys();
+ menu->appendMenu(sub);
+ }
+#endif
+
+ // TomY Temporary menu item so we can test this floater
+ menu->append(new LLMenuItemCheckGL("Clothing...",
+ &handle_clothing,
+ NULL,
+ NULL,
+ NULL));
+
+ menu->append(new LLMenuItemCallGL("Debug Settings", LLFloaterSettingsDebug::show, NULL, NULL));
+ menu->append(new LLMenuItemCheckGL("View Admin Options", &handle_admin_override_toggle, NULL, &check_admin_override, NULL, 'V', MASK_CONTROL | MASK_ALT));
+ menu->createJumpKeys();
+}
+
+void handle_upload_data(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if(!picker.getOpenFile())
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ const char* filename = picker.getFirstFile();
+ S32 index = strlen(filename);
+ char delim = gDirUtilp->getDirDelimiter()[0];
+ while(index && filename[index--] != delim);
+ index += 2;
+ const char* basename = &filename[index];
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("InitiateUpload");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->nextBlock("FileData");
+ msg->addString("BaseFilename", basename);
+ msg->addString("SourceFilename", filename);
+ gAgent.sendReliableMessage();
+}
+
+void init_debug_world_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCheckGL("Mouse Moves Sun",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"MouseSun",
+ 'M', MASK_CONTROL|MASK_ALT));
+ menu->append(new LLMenuItemCheckGL("Sim Sun Override",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"SkyOverrideSimSunPosition"));
+ menu->append(new LLMenuItemCallGL("Dump Scripted Camera",
+ &handle_dump_followcam, NULL, NULL));
+ menu->append(new LLMenuItemCheckGL("Fixed Weather",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"FixedWeather"));
+ menu->createJumpKeys();
+}
+
+
+void handle_export_menus_to_xml(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if(!picker.getSaveFile(LLFilePicker::FFSAVE_XML))
+ {
+ llwarns << "No file" << llendl;
+ return;
+ }
+ const char* filename = picker.getFirstFile();
+
+ llofstream out(filename);
+ LLXMLNodePtr node = gMenuBarView->getXML();
+ node->writeToOstream(out);
+ out.close();
+}
+
+extern BOOL gDebugClicks;
+extern BOOL gDebugWindowProc;
+extern BOOL gDebugTextEditorTips;
+extern BOOL gDebugSelectMgr;
+
+void init_debug_ui_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Editable UI", &edit_ui));
+ menu->append(new LLMenuItemToggleGL("Async Keystrokes", &gHandleKeysAsync));
+ menu->append(new LLMenuItemCallGL( "Dump SelectMgr", &dump_select_mgr));
+ menu->append(new LLMenuItemCallGL( "Dump Inventory", &dump_inventory));
+ menu->append(new LLMenuItemCallGL( "Dump Focus Holder", &handle_dump_focus, NULL, NULL, 'F', MASK_ALT | MASK_CONTROL));
+ menu->append(new LLMenuItemCallGL( "Dump VolumeMgr", &dump_volume_mgr, NULL, NULL));
+ menu->append(new LLMenuItemCallGL( "Print Selected Object Info", &print_object_info, NULL, NULL, 'P', MASK_CONTROL|MASK_SHIFT ));
+ menu->append(new LLMenuItemCallGL( "Print Agent Info", &print_agent_nvpairs, NULL, NULL, 'P', MASK_SHIFT ));
+ menu->append(new LLMenuItemCallGL( "Print Texture Memory Stats", &output_statistics, NULL, NULL, 'M', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ menu->append(new LLMenuItemCheckGL("Double-Click Auto-Pilot",
+ menu_toggle_control, NULL, menu_check_control,
+ (void*)"DoubleClickAutoPilot"));
+ menu->appendSeparator();
+// menu->append(new LLMenuItemCallGL( "Print Packets Lost", &print_packets_lost, NULL, NULL, 'L', MASK_SHIFT ));
+ menu->append(new LLMenuItemToggleGL("Debug SelectMgr", &gDebugSelectMgr));
+ menu->append(new LLMenuItemToggleGL("Debug Clicks", &gDebugClicks));
+ menu->append(new LLMenuItemToggleGL("Debug Views", &LLView::sDebugRects));
+ menu->append(new LLMenuItemToggleGL("Debug Mouse Events", &LLView::sDebugMouseHandling));
+ menu->append(new LLMenuItemToggleGL("Debug Keys", &LLView::sDebugKeys));
+ menu->append(new LLMenuItemToggleGL("Debug WindowProc", &gDebugWindowProc));
+ menu->append(new LLMenuItemToggleGL("Debug Text Editor Tips", &gDebugTextEditorTips));
+ menu->createJumpKeys();
+}
+
+void init_debug_xui_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Floater Test...", LLFloaterTest::show));
+ menu->append(new LLMenuItemCallGL("Export Menus to XML...", handle_export_menus_to_xml));
+ menu->append(new LLMenuItemCallGL("Edit UI...", LLFloaterEditUI::show));
+ menu->append(new LLMenuItemCallGL("Load from XML...", handle_load_from_xml));
+ menu->append(new LLMenuItemCallGL("Save to XML...", handle_save_to_xml));
+ menu->append(new LLMenuItemCheckGL("Show XUI Names", toggle_show_xui_names, NULL, check_show_xui_names, NULL));
+
+ //menu->append(new LLMenuItemCallGL("Buy Currency...", handle_buy_currency));
+ menu->createJumpKeys();
+}
+
+void init_debug_rendering_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = NULL;
+
+ ///////////////////////////
+ //
+ // Debug menu for types/pools
+ //
+ sub_menu = new LLMenuGL("Types");
+ menu->appendMenu(sub_menu);
+
+ sub_menu->append(new LLMenuItemCheckGL("Simple",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_SIMPLE, '1', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Alpha",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_ALPHA, '2', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Tree",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_TREE, '3', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Character",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_AVATAR, '4', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("SurfacePatch",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_TERRAIN, '5', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Sky",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_SKY, '6', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Water",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_WATER, '7', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Ground",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_GROUND, '8', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Volume",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_VOLUME, '9', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Grass",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_GRASS, '0', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Clouds",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_CLOUDS, '-', MASK_CONTROL|MASK_ALT| MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Particles",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_PARTICLES, '=', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->append(new LLMenuItemCheckGL("Bump",
+ &LLPipeline::toggleRenderType, NULL,
+ &LLPipeline::toggleRenderTypeControl,
+ (void*)LLPipeline::RENDER_TYPE_BUMP, '\\', MASK_CONTROL|MASK_ALT|MASK_SHIFT));
+ sub_menu->createJumpKeys();
+ sub_menu = new LLMenuGL("Features");
+ menu->appendMenu(sub_menu);
+ sub_menu->append(new LLMenuItemCheckGL("UI",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_UI, '1', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Selected",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_SELECTED, '2', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Highlighted",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_HIGHLIGHTED, '3', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Dynamic Textures",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES, '4', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Fog",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FOG, '6', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Palletized Textures",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_PALETTE, '7', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL("Test FRInfo",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FR_INFO, '8', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL( "Flexible Objects",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_FLEXIBLE, '9', MASK_ALT|MASK_CONTROL));
+ sub_menu->append(new LLMenuItemCheckGL( "Chain Faces",
+ &LLPipeline::toggleRenderDebugFeature, NULL,
+ &LLPipeline::toggleRenderDebugFeatureControl,
+ (void*)LLPipeline::RENDER_DEBUG_FEATURE_CHAIN_FACES, '0', MASK_ALT|MASK_CONTROL));
+ sub_menu->createJumpKeys();
+
+ /////////////////////////////
+ //
+ // Debug menu for info displays
+ //
+ sub_menu = new LLMenuGL("Info Displays");
+ menu->appendMenu(sub_menu);
+
+ sub_menu->append(new LLMenuItemCheckGL("Verify", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_VERIFY));
+ sub_menu->append(new LLMenuItemCheckGL("AGP Map", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_AGP_MEM));
+ sub_menu->append(new LLMenuItemCheckGL("BBoxes", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_BBOXES));
+ sub_menu->append(new LLMenuItemCheckGL("Points", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_POINTS));
+ sub_menu->append(new LLMenuItemCheckGL("Octree", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_OCTREE));
+ sub_menu->append(new LLMenuItemCheckGL("Occlusion", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_OCCLUSION));
+ sub_menu->append(new LLMenuItemCheckGL("Face Chains", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_FACE_CHAINS));
+ sub_menu->append(new LLMenuItemCheckGL("Texture Priority", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_TEXTURE_PRIORITY));
+ sub_menu->append(new LLMenuItemCheckGL("Composition", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_COMPOSITION));
+ sub_menu->append(new LLMenuItemCheckGL("ShadowMap", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_SHADOW_MAP));
+ sub_menu->append(new LLMenuItemCheckGL("LightTrace",&LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_LIGHT_TRACE));
+ sub_menu->append(new LLMenuItemCheckGL("Pools", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_POOLS));
+ sub_menu->append(new LLMenuItemCheckGL("Queues", &LLPipeline::toggleRenderDebug, NULL,
+ &LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_QUEUES));
+ sub_menu->append(new LLMenuItemCheckGL("Map", &LLPipeline::toggleRenderDebug, NULL,
+ LLPipeline::toggleRenderDebugControl,
+ (void*)LLPipeline::RENDER_DEBUG_MAP));
+
+ sub_menu->append(new LLMenuItemCheckGL("Show Depth Buffer",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"ShowDepthBuffer"));
+ sub_menu->append(new LLMenuItemToggleGL("Show Select Buffer", &gDebugSelect));
+
+ sub_menu = new LLMenuGL("Render Tests");
+
+ sub_menu->append(new LLMenuItemCheckGL("Camera Offset",
+ &menu_toggle_control,
+ NULL,
+ &menu_check_control,
+ (void*)"CameraOffset"));
+
+ sub_menu->append(new LLMenuItemToggleGL("Randomize Framerate", &gRandomizeFramerate));
+
+ sub_menu->append(new LLMenuItemToggleGL("Periodic Slow Frame", &gPeriodicSlowFrame));
+ sub_menu->createJumpKeys();
+
+ menu->appendMenu( sub_menu );
+
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCheckGL("Axes", menu_toggle_control, NULL, menu_check_control, (void*)"ShowAxes"));
+ menu->append(new LLMenuItemCheckGL("Use VBO", toggle_vbo, NULL, check_vbo, NULL));
+ menu->append(new LLMenuItemCheckGL("Light Glows", toggle_glow, NULL, check_glow, NULL));
+// menu->append(new LLMenuItemCheckGL("Cull Small Objects", toggle_cull_small, NULL, menu_check_control, (void*)"RenderCullBySize"));
+
+ menu->appendSeparator();
+ menu->append(new LLMenuItemToggleGL("Hide Selected", &gHideSelectedObjects));
+ menu->appendSeparator();
+ menu->append(new LLMenuItemCheckGL("Tangent Basis", menu_toggle_control, NULL, menu_check_control, (void*)"ShowTangentBasis"));
+ menu->append(new LLMenuItemCallGL("Selected Texture Info", handle_selected_texture_info, NULL, NULL, 'T', MASK_CONTROL|MASK_SHIFT|MASK_ALT));
+ //menu->append(new LLMenuItemCallGL("Dump Image List", handle_dump_image_list, NULL, NULL, 'I', MASK_CONTROL|MASK_SHIFT));
+
+ menu->append(new LLMenuItemToggleGL("Wireframe", &gUseWireframe,
+ 'R', MASK_CONTROL|MASK_SHIFT));
+
+ LLMenuItemCheckGL* item;
+ item = new LLMenuItemCheckGL("Object-Object Occlusion", menu_toggle_control, NULL, menu_check_control, (void*)"UseOcclusion", 'O', MASK_CONTROL|MASK_SHIFT);
+ item->setEnabled(gGLManager.mHasOcclusionQuery);
+ menu->append(item);
+
+
+ item = new LLMenuItemCheckGL("Animate Textures", menu_toggle_control, NULL, menu_check_control, (void*)"AnimateTextures", 'A', MASK_CONTROL|MASK_ALT);
+ menu->append(item);
+
+ item = new LLMenuItemCheckGL("Disable Textures", menu_toggle_variable, NULL, menu_check_variable, (void*)&LLViewerImage::sDontLoadVolumeTextures);
+ menu->append(item);
+
+ item = new LLMenuItemCheckGL("Cheesy Beacon", menu_toggle_control, NULL, menu_check_control, (void*)"CheesyBeacon");
+ menu->append(item);
+
+#if 0 // 1.9.2
+ item = new LLMenuItemCheckGL("Vertex Shaders", toggle_vertex_shaders, NULL, check_vertex_shaders, (void*)"VertexShaderEnable", 'V', MASK_CONTROL|MASK_ALT);
+ item->setEnabled(gGLManager.mHasVertexShader);
+ menu->append(item);
+#endif
+ menu->createJumpKeys();
+}
+
+extern BOOL gDebugAvatarRotation;
+
+void init_debug_avatar_menu(LLMenuGL* menu)
+{
+ LLMenuGL* sub_menu = new LLMenuGL("Grab Baked Texture");
+ init_debug_baked_texture_menu(sub_menu);
+ menu->appendMenu(sub_menu);
+
+ sub_menu = new LLMenuGL("Character Tests");
+ sub_menu->append(new LLMenuItemToggleGL("Go Away/AFK When Idle",
+ &gAllowAFK));
+
+ sub_menu->append(new LLMenuItemCallGL("Appearance To XML",
+ &LLVOAvatar::dumpArchetypeXML));
+
+ // HACK for easy testing of avatar geometry
+ sub_menu->append(new LLMenuItemCallGL( "Toggle Character Geometry",
+ &handle_god_request_avatar_geometry, &enable_god_customer_service, NULL));
+
+ sub_menu->append(new LLMenuItemCallGL("Test Male",
+ handle_test_male));
+
+ sub_menu->append(new LLMenuItemCallGL("Test Female",
+ handle_test_female));
+
+ sub_menu->append(new LLMenuItemCallGL("Toggle PG", handle_toggle_pg));
+
+ sub_menu->append(new LLMenuItemToggleGL("Allow Select Avatar", &gAllowSelectAvatar));
+ sub_menu->createJumpKeys();
+
+ menu->appendMenu(sub_menu);
+
+ menu->append(new LLMenuItemCallGL("Force Params to Default", &LLAgent::clearVisualParams, NULL));
+ menu->append(new LLMenuItemCallGL("Reload Vertex Shader", &reload_vertex_shader, NULL));
+ menu->append(new LLMenuItemToggleGL("Animation Info", &LLVOAvatar::sShowAnimationDebug));
+ menu->append(new LLMenuItemCallGL("Flush Animations", &flush_animations, NULL));
+ menu->append(new LLMenuItemCallGL("Slow Motion Animations", &slow_mo_animations, NULL));
+ menu->append(new LLMenuItemToggleGL("Show Look At", &LLHUDEffectLookAt::sDebugLookAt));
+ menu->append(new LLMenuItemToggleGL("Show Point At", &LLHUDEffectPointAt::sDebugPointAt));
+ menu->append(new LLMenuItemToggleGL("Debug Joint Updates", &LLVOAvatar::sJointDebug));
+ menu->append(new LLMenuItemToggleGL("Disable LOD", &LLViewerJoint::sDisableLOD));
+ menu->append(new LLMenuItemToggleGL("Debug Character Vis", &LLVOAvatar::sDebugInvisible));
+ //menu->append(new LLMenuItemToggleGL("Show Attachment Points", &LLVOAvatar::sShowAttachmentPoints));
+ menu->append(new LLMenuItemToggleGL("Show Collision Plane", &LLVOAvatar::sShowFootPlane));
+ menu->append(new LLMenuItemToggleGL("Show Collision Skeleton", &LLVOAvatar::sShowCollisionVolumes));
+ menu->append(new LLMenuItemToggleGL("Software Blending SSE", &gGLManager.mSoftwareBlendSSE));
+#if 0 // Removed since this feature doesn't actually work as of 1.9.1 --TomY
+ menu->append(new LLMenuItemToggleGL("Character Load Test", &LLVOAvatar::sAvatarLoadTest));
+#endif
+ menu->append(new LLMenuItemToggleGL( "Display Agent Target", &LLAgent::sDebugDisplayTarget));
+ menu->append(new LLMenuItemToggleGL( "Debug Rotation", &gDebugAvatarRotation));
+ menu->append(new LLMenuItemCallGL("Dump Attachments", handle_dump_attachments));
+ menu->append(new LLMenuItemCallGL("Rebake Textures", handle_rebake_textures));
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ menu->append(new LLMenuItemCallGL("Debug Avatar Textures", handle_debug_avatar_textures, NULL, NULL, 'A', MASK_SHIFT|MASK_CONTROL|MASK_ALT));
+ menu->append(new LLMenuItemCallGL("Dump Local Textures", handle_dump_avatar_local_textures, NULL, NULL, 'M', MASK_SHIFT|MASK_ALT ));
+#endif
+ menu->createJumpKeys();
+}
+
+void init_debug_baked_texture_menu(LLMenuGL* menu)
+{
+ menu->append(new LLMenuItemCallGL("Iris", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_EYES_BAKED));
+ menu->append(new LLMenuItemCallGL("Head", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_HEAD_BAKED));
+ menu->append(new LLMenuItemCallGL("Upper Body", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_UPPER_BAKED));
+ menu->append(new LLMenuItemCallGL("Lower Body", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_LOWER_BAKED));
+ menu->append(new LLMenuItemCallGL("Skirt", handle_grab_texture, enable_grab_texture, (void*) LLVOAvatar::TEX_SKIRT_BAKED));
+ menu->createJumpKeys();
+}
+
+void init_server_menu(LLMenuGL* menu)
+{
+ /*
+ {
+ // These messages are now trusted. We can write scripts to do
+ // this, and the message is unchecked for source.
+ LLMenuGL* sub_menu = NULL;
+ sub_menu = new LLMenuGL("Sim Logging");
+
+ sub_menu->append(new LLMenuItemCallGL("Turn off llinfos Log",
+ &handle_reduce_llinfo_log, &enable_god_customer_service));
+
+ sub_menu->append(new LLMenuItemCallGL("Normal Logging",
+ &handle_normal_llinfo_log, &enable_god_customer_service));
+
+ sub_menu->appendSeparator();
+ sub_menu->append(new LLMenuItemCallGL("Enable Circuit Log",
+ &handle_sim_enable_circuit_log, &enable_god_customer_service));
+ sub_menu->append(new LLMenuItemCallGL("Disable Circuit Log",
+ &handle_sim_disable_circuit_log, &enable_god_customer_service));
+ sub_menu->appendSeparator();
+ sub_menu->append(new LLMenuItemCallGL("Enable Message Log",
+ &handle_sim_enable_message_log, &enable_god_customer_service));
+ sub_menu->append(new LLMenuItemCallGL("Disable Message Log",
+ &handle_sim_disable_message_log, &enable_god_customer_service));
+
+ sub_menu->appendSeparator();
+
+ sub_menu->append(new LLMenuItemCallGL("Fetch Message Log",
+ &handle_sim_fetch_message_log, &enable_god_customer_service));
+
+ sub_menu->append(new LLMenuItemCallGL("Fetch Log",
+ &handle_sim_fetch_log, &enable_god_customer_service));
+
+ menu->appendMenu( sub_menu );
+ }
+ */
+
+ {
+ LLMenuGL* sub = new LLMenuGL("Object");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL( "Take Copy",
+ &force_take_copy, &enable_god_customer_service, NULL));
+#ifdef _CORY_TESTING
+ sub->append(new LLMenuItemCallGL( "Export Copy",
+ &force_export_copy, NULL, NULL));
+ sub->append(new LLMenuItemCallGL( "Import Geometry",
+ &force_import_geometry, NULL, NULL));
+#endif
+ //sub->append(new LLMenuItemCallGL( "Force Public",
+ // &handle_object_owner_none, NULL, NULL));
+ //sub->append(new LLMenuItemCallGL( "Force Ownership/Permissive",
+ // &handle_object_owner_self_and_permissive, NULL, NULL, 'K', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Force Owner To Me",
+ &handle_object_owner_self, &enable_god_customer_service));
+ sub->append(new LLMenuItemCallGL( "Force Owner Permissive",
+ &handle_object_owner_permissive, &enable_god_customer_service));
+ //sub->append(new LLMenuItemCallGL( "Force Totally Permissive",
+ // &handle_object_permissive));
+ sub->append(new LLMenuItemCallGL( "Delete",
+ &handle_force_delete, &enable_god_customer_service, NULL, KEY_DELETE, MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Lock",
+ &handle_object_lock, &enable_god_customer_service, NULL, 'L', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->append(new LLMenuItemCallGL( "Get Asset IDs",
+ &handle_object_asset_ids, &enable_god_customer_service, NULL, 'I', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = new LLMenuGL("Parcel");
+ menu->appendMenu(sub);
+
+ sub->append(new LLMenuItemCallGL("Owner To Me",
+ &handle_force_parcel_owner_to_me,
+ &enable_god_customer_service, NULL));
+ sub->append(new LLMenuItemCallGL("Set to Linden Content",
+ &handle_force_parcel_to_content,
+ &enable_god_customer_service, NULL,
+ 'C', MASK_SHIFT | MASK_ALT | MASK_CONTROL));
+ //sub->append(new LLMenuItemCallGL("Toggle First Land bit",
+ // &handle_toggle_parcel_newbie));
+ sub->appendSeparator();
+ sub->append(new LLMenuItemCallGL("Claim Public Land",
+ &handle_claim_public_land, &enable_god_customer_service));
+
+ sub->createJumpKeys();
+ }
+ {
+ LLMenuGL* sub = new LLMenuGL("Region");
+ menu->appendMenu(sub);
+ sub->append(new LLMenuItemCallGL("Dump Temp Asset Data",
+ &handle_region_dump_temp_asset_data,
+ &enable_god_customer_service, NULL));
+ sub->createJumpKeys();
+ }
+ menu->append(new LLMenuItemCallGL( "God Tools...",
+ &LLFloaterGodTools::show, &enable_god_basic, NULL));
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Upload Data File...",
+ &handle_upload_data,
+ &enable_god_customer_service,
+ NULL));
+
+
+ menu->appendSeparator();
+
+ menu->append(new LLMenuItemCallGL("Save Region State",
+ &LLPanelRegionTools::onSaveState, &enable_god_customer_service, NULL));
+
+// menu->append(new LLMenuItemCallGL("Force Join Group", handle_force_join_group));
+
+
+
+ menu->appendSeparator();
+//
+// menu->append(new LLMenuItemCallGL( "OverlayTitle",
+// &handle_show_overlay_title, &enable_god_customer_service, NULL));
+
+ menu->append(new LLMenuItemCallGL("Request Admin Status",
+ &handle_god_mode, NULL, NULL, 'G', MASK_ALT | MASK_CONTROL));
+
+ menu->append(new LLMenuItemCallGL("Leave Admin Status",
+ &handle_leave_god_mode, NULL, NULL, 'G', MASK_ALT | MASK_SHIFT | MASK_CONTROL));
+ menu->createJumpKeys();
+}
+
+//-----------------------------------------------------------------------------
+// cleanup_menus()
+//-----------------------------------------------------------------------------
+void cleanup_menus()
+{
+ delete gMenuParcelObserver;
+ gMenuParcelObserver = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Object pie menu
+//-----------------------------------------------------------------------------
+
+class LLObjectRateCreator : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterRate::show(LLFloaterRate::RS_CREATOR);
+ return true;
+ }
+};
+
+class LLObjectRateOwner : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Don't allow rating of group owned objects.
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node) return true;
+ if (node->mPermissions->isGroupOwned())
+ {
+ gViewerWindow->alertXml("CantRateOwnedByGroup");
+ return true;
+ }
+
+ LLFloaterRate::show(LLFloaterRate::RS_OWNER);
+ return true;
+ }
+};
+
+class LLObjectReportAbuse : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterReporter::showFromObject(gLastHitObjectID);
+ return true;
+ }
+};
+
+// Enable only when you didn't create it, and the creator
+// is not the owner.
+class LLObjectEnableRateCreator : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID creator_id;
+ LLUUID owner_id;
+ LLString dummy;
+ BOOL identical_creator = gSelectMgr->selectGetCreator(creator_id, dummy);
+
+ BOOL new_value;
+ if (!identical_creator)
+ {
+ new_value = FALSE;
+ }
+ else
+ {
+ new_value = (creator_id != gAgent.getID());
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Enabled if object owner isn't the agent.
+class LLObjectEnableRateOwner : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID owner_id;
+ LLString dummy;
+ BOOL identical_owner = gSelectMgr->selectGetOwner(owner_id, dummy);
+
+ BOOL new_value;
+ if (!identical_owner)
+ {
+ new_value = FALSE;
+ }
+ else if (owner_id.isNull())
+ {
+ new_value = FALSE;
+ }
+ else
+ {
+ new_value = (owner_id != gAgent.getID());
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+// Enabled it you clicked an object
+class LLObjectEnableReportAbuse : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !gLastHitObjectID.isNull();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+BOOL enable_attach(void*)
+{
+ // All root objects must be owned by agent.
+ BOOL rv = FALSE;
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(obj)
+ {
+ rv = TRUE;
+ for(obj = gSelectMgr->getFirstRootObject() ; obj != NULL; obj = gSelectMgr->getNextRootObject())
+ {
+ for (U32 child_num = 0; child_num < obj->mChildList.size(); child_num++ )
+ {
+ LLViewerObject *child = obj->mChildList[child_num];
+ if (child->isAvatar())
+ {
+ return FALSE;
+ }
+ }
+ if(!obj->permMove())
+ {
+ rv = FALSE;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+
+class LLObjectTouch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gObjectList.findObject(gLastHitObjectID);
+ if (!object) return true;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ObjectGrab);
+ msg->nextBlockFast( _PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast( _PREHASH_ObjectData);
+ msg->addU32Fast( _PREHASH_LocalID, object->mLocalID);
+ msg->addVector3Fast(_PREHASH_GrabOffset, LLVector3::zero );
+ msg->sendMessage( object->getRegion()->getHost());
+
+ // HACK: Hope the packets arrive safely and in order.
+ msg->newMessageFast(_PREHASH_ObjectDeGrab);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_LocalID, object->mLocalID);
+ msg->sendMessage(object->getRegion()->getHost());
+
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+
+// One object must have touch sensor
+class LLObjectEnableTouch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ bool new_value = obj && obj->flagHandleTouch();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK Update label
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mTouchName.empty())
+ {
+ gMenuHolder->childSetText("Object Touch", node->mTouchName);
+ }
+ else
+ {
+ gMenuHolder->childSetText("Object Touch", userdata["data"].asString());
+ }
+
+ return true;
+ }
+};
+
+void label_touch(LLString& label, void*)
+{
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mTouchName.empty())
+ {
+ label.assign(node->mTouchName);
+ }
+ else
+ {
+ label.assign("Touch");
+ }
+}
+
+bool handle_object_open()
+{
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ if(!obj) return true;
+
+ // transient selection must be made permanent
+ gSelectMgr->convertTransient();
+ LLFloaterOpenObject::show();
+ return true;
+}
+
+class LLObjectOpen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_object_open();
+ }
+};
+
+class LLObjectEnableOpen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Look for contents in root object, which is all the LLFloaterOpenObject
+ // understands.
+ LLViewerObject* obj = gObjectList.findObject(gLastHitObjectID);
+ bool new_value = (obj != NULL);
+ if (new_value)
+ {
+ LLViewerObject* root = obj->getRootEdit();
+ if (!root) new_value = false;
+ else new_value = root->allowOpen();
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+class LLViewCheckBuildMode : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gToolMgr->inEdit();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+bool toggle_build_mode()
+{
+ if (gToolMgr->inEdit())
+ {
+ // just reset the view, will pull us out of edit mode
+ handle_reset_view();
+ }
+ else
+ {
+ if (gAgent.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gAgent.getPositionGlobal() + 2.0 * LLVector3d(gAgent.getAtAxis()));
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ }
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ }
+ return true;
+}
+
+class LLViewBuildMode : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return toggle_build_mode();
+ }
+};
+
+
+class LLObjectBuild : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+class LLObjectEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gParcelMgr->deselectLand();
+ // convert transient selections to permanent
+ gSelectMgr->convertTransient();
+
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit())
+ {
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD || !gSavedSettings.getBOOL("EditCameraMovement"))
+ {
+ // always freeze camera in space, even if camera doesn't move
+ // so, for example, follow cam scripts can't affect you when in build mode
+ gAgent.setFocusGlobal(gAgent.calcFocusPositionTargetGlobal(), LLUUID::null);
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ }
+ else
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ // zoom in on object center instead of where we clicked, as we need to see the manipulator handles
+ gAgent.setFocusGlobal(gLastHitPosGlobal /*+ gLastHitObjectOffset*/, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ }
+
+ gFloaterTools->open();
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolTranslate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+
+//---------------------------------------------------------------------------
+// Land pie menu
+//---------------------------------------------------------------------------
+class LLLandBuild : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gParcelMgr->deselectLand();
+
+ if (gAgent.getFocusOnAvatar() && !gToolMgr->inEdit() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ gAgent.cameraOrbitOver( 30.f * DEG_TO_RAD );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // otherwise just move focus
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolCreate );
+
+ // Could be first use
+ LLFirstUse::useBuild();
+ return true;
+ }
+};
+
+class LLLandBuyPass : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLPanelLandGeneral::onClickBuyPass((void *)FALSE);
+ return true;
+ }
+};
+
+class LLLandEnableBuyPass : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLPanelLandGeneral::enableBuyPass(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// BUG: Should really check if CLICK POINT is in a parcel where you can build.
+BOOL enable_land_build(void*)
+{
+ if (gAgent.isGodlike()) return TRUE;
+ if (gAgent.inPrelude()) return FALSE;
+
+ BOOL can_build = FALSE;
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (agent_parcel)
+ {
+ can_build = agent_parcel->getAllowModify();
+ }
+ return can_build;
+}
+
+// BUG: Should really check if OBJECT is in a parcel where you can build.
+BOOL enable_object_build(void*)
+{
+ if (gAgent.isGodlike()) return TRUE;
+ if (gAgent.inPrelude()) return FALSE;
+
+ BOOL can_build = FALSE;
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (agent_parcel)
+ {
+ can_build = agent_parcel->getAllowModify();
+ }
+ return can_build;
+}
+
+class LLEnableEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() || !gAgent.inPrelude();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLSelfRemoveAllAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLAgent::userRemoveAllAttachments(NULL);
+ return true;
+ }
+};
+
+class LLSelfEnableRemoveAllAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gAgent.getAvatarObject())
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ for (LLViewerJointAttachment* attachmentp = avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getObject(0))
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_has_attachments(void*)
+{
+
+ return FALSE;
+}
+
+//---------------------------------------------------------------------------
+// Avatar pie menu
+//---------------------------------------------------------------------------
+void handle_follow(void *userdata)
+{
+ // follow a given avatar, ID in gLastHitObjectID
+ gAgent.startFollowPilot(gLastHitObjectID);
+}
+
+class LLObjectEnableMute : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ bool new_value = (object != NULL);
+ if (new_value)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(object);
+ if (avatar)
+ {
+ // It's an avatar
+ LLNameValue *lastname = avatar->getNVPair("LastName");
+ BOOL is_linden = lastname && !LLString::compareStrings(lastname->getString(), "Linden");
+ BOOL is_self = avatar->isSelf();
+ new_value = !is_linden && !is_self;
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLObjectMute : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if (!object) return true;
+
+ LLUUID id;
+ LLString name;
+ LLMute::EType type;
+ LLVOAvatar* avatar = find_avatar_from_object(object);
+ if (avatar)
+ {
+ id = avatar->getID();
+
+ LLNameValue *firstname = avatar->getNVPair("FirstName");
+ LLNameValue *lastname = avatar->getNVPair("LastName");
+ if (firstname && lastname)
+ {
+ name = firstname->getString();
+ name += " ";
+ name += lastname->getString();
+ }
+
+ type = LLMute::AGENT;
+ }
+ else
+ {
+ // it's an object
+ id = object->getID();
+
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node)
+ {
+ name = node->mName;
+ }
+
+ type = LLMute::OBJECT;
+ }
+
+ LLMute mute(id, name, type);
+ if (gMuteListp->isMuted(mute.mID, mute.mName))
+ {
+ gMuteListp->remove(mute);
+ }
+ else
+ {
+ gMuteListp->add(mute);
+ gFloaterMute->show();
+ }
+
+ gSelectMgr->deselectAll();
+ return true;
+ }
+};
+
+bool handle_go_to()
+{
+ // JAMESDEBUG try simulator autopilot
+ std::vector<std::string> strings;
+ std::string val;
+ val = llformat("%g", gLastHitPosGlobal.mdV[VX]);
+ strings.push_back(val);
+ val = llformat("%g", gLastHitPosGlobal.mdV[VY]);
+ strings.push_back(val);
+ val = llformat("%g", gLastHitPosGlobal.mdV[VZ]);
+ strings.push_back(val);
+ send_generic_message("autopilot", strings);
+
+ // Don't select anything
+ gSelectMgr->deselectTransient();
+
+ gParcelMgr->deselectLand();
+
+ if (gAgent.getAvatarObject() && !gSavedSettings.getBOOL("AutoPilotLocksCamera"))
+ {
+ gAgent.setFocusGlobal(gAgent.getFocusTargetGlobal(), gAgent.getAvatarObject()->getID());
+ }
+ else
+ {
+ // Snap camera back to behind avatar
+ gAgent.setFocusOnAvatar(TRUE, ANIMATE);
+ }
+
+ // Could be first use
+ LLFirstUse::useGoTo();
+ return true;
+}
+
+class LLGoToObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_go_to();
+ }
+};
+
+//---------------------------------------------------------------------------
+// Parcel freeze, eject, etc.
+//---------------------------------------------------------------------------
+void callback_freeze(S32 option, void* data)
+{
+ LLUUID* avatar_id = (LLUUID*) data;
+
+ if (0 == option || 1 == option)
+ {
+ U32 flags = 0x0;
+ if (1 == option)
+ {
+ // unfreeze
+ flags |= 0x1;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ LLViewerObject* avatar = gObjectList.findObject(*avatar_id);
+
+ if (avatar)
+ {
+ msg->newMessage("FreezeUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", *avatar_id );
+ msg->addU32("Flags", flags );
+ msg->sendReliable( avatar->getRegion()->getHost() );
+ }
+ }
+
+ delete avatar_id;
+ avatar_id = NULL;
+}
+
+class LLAvatarFreeze : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLUUID* avatar_id = new LLUUID( avatar->getID() );
+
+ gViewerWindow->alertXml("FreezeAvatar",
+ callback_freeze, (void*)avatar_id);
+
+ }
+ return true;
+ }
+};
+
+class LLAvatarVisibleDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarEnableDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarDebug : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ avatar->dumpLocalTextures();
+ }
+ llinfos << "Dumping temporary asset data to simulator logs for avatar " << avatar->getID() << llendl;
+ std::vector<std::string> strings;
+ strings.push_back( avatar->getID().getString() );
+ LLUUID invoice;
+ send_generic_message("dumptempassetdata", strings, invoice);
+ LLFloaterAvatarTextures::show( avatar->getID() );
+ return true;
+ }
+};
+
+void callback_eject(S32 option, void* data)
+{
+ LLUUID* avatar_id = (LLUUID*) data;
+
+ if (0 == option || 1 == option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ LLViewerObject* avatar = gObjectList.findObject(*avatar_id);
+
+ if (avatar)
+ {
+ U32 flags = 0x0;
+ if (1 == option)
+ {
+ // eject and add to ban list
+ flags |= 0x1;
+ }
+
+ msg->newMessage("EjectUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addUUID("TargetID", *avatar_id );
+ msg->addU32("Flags", flags );
+ msg->sendReliable( avatar->getRegion()->getHost() );
+ }
+ }
+
+ delete avatar_id;
+ avatar_id = NULL;
+}
+
+class LLAvatarEject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLUUID* avatar_id = new LLUUID( avatar->getID() );
+ gViewerWindow->alertXml("EjectAvatar",
+ callback_eject, (void*)avatar_id);
+
+ }
+ return true;
+ }
+};
+
+class LLAvatarEnableFreezeEject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ bool new_value = (avatar != NULL);
+
+ if (new_value)
+ {
+ const LLVector3& pos = avatar->getPositionRegion();
+ LLViewerRegion* region = avatar->getRegion();
+ new_value = (region != NULL);
+
+ if (new_value)
+ {
+ new_value = (region->isOwnedSelf(pos) || region->isOwnedGroup(pos));
+ }
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLAvatarGiveCard : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ llinfos << "handle_give_card()" << llendl;
+ LLViewerObject* dest = gViewerWindow->lastObjectHit();
+ if(dest && dest->isAvatar())
+ {
+ bool found_name = false;
+ LLString::format_map_t args;
+ LLNameValue* nvfirst = dest->getNVPair("FirstName");
+ LLNameValue* nvlast = dest->getNVPair("LastName");
+ if(nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ found_name = true;
+ }
+ LLViewerRegion* region = dest->getRegion();
+ LLHost dest_host;
+ if(region)
+ {
+ dest_host = region->getHost();
+ }
+ if(found_name && dest_host.isOk())
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("OfferCallingCard");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_DestID, dest->getID());
+ LLUUID transaction_id;
+ transaction_id.generate();
+ msg->addUUIDFast(_PREHASH_TransactionID, transaction_id);
+ msg->sendReliable(dest_host);
+ LLNotifyBox::showXml("OfferedCard", args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("CantOfferCallingCard", args);
+ }
+ }
+ return true;
+ }
+};
+
+
+
+void login_done(S32 which, void *user)
+{
+ llinfos << "Login done " << which << llendl;
+
+ LLPanelLogin::close();
+}
+
+
+
+class LLAvatarRate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if( avatar )
+ {
+ LLFloaterRate::show( avatar->getID() );
+ }
+ return true;
+ }
+};
+
+void callback_leave_group(S32 option, void *userdata)
+{
+ if (option == 0)
+ {
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_LeaveGroupRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_GroupData);
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.mGroupID );
+ //msg->sendReliable( gUserServer );
+ gAgent.sendReliableMessage();
+ }
+}
+
+void handle_leave_group(void *)
+{
+ if (gAgent.getGroupID() != LLUUID::null)
+ {
+ LLString::format_map_t args;
+ args["[GROUP]"] = gAgent.mGroupName;
+ gViewerWindow->alertXml("GroupLeaveConfirmMember", args, callback_leave_group);
+ }
+}
+
+void append_aggregate(LLString& string, const LLAggregatePermissions& ag_perm, PermissionBit bit, const char* txt)
+{
+ LLAggregatePermissions::EValue val = ag_perm.getValue(bit);
+ char buffer[MAX_STRING];
+ buffer[0] = '\0';
+ switch(val)
+ {
+ case LLAggregatePermissions::AP_NONE:
+ sprintf(buffer, "* %s None\n", txt);
+ break;
+ case LLAggregatePermissions::AP_SOME:
+ sprintf(buffer, "* %s Some\n", txt);
+ break;
+ case LLAggregatePermissions::AP_ALL:
+ sprintf(buffer, "* %s All\n", txt);
+ break;
+ case LLAggregatePermissions::AP_EMPTY:
+ default:
+ break;
+ }
+ string.append(buffer);
+}
+
+const char* build_extensions_string(LLFilePicker::ELoadFilter filter)
+{
+ switch(filter)
+ {
+#if LL_WINDOWS
+ case LLFilePicker::FFLOAD_IMAGE:
+ return IMAGE_EXTENSIONS;
+ case LLFilePicker::FFLOAD_WAV:
+ return SOUND_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ANIM:
+ return ANIM_EXTENSIONS;
+ case LLFilePicker::FFLOAD_SLOBJECT:
+ return SLOBJECT_EXTENSIONS;
+#ifdef _CORY_TESTING
+ case LLFilePicker::FFLOAD_GEOMETRY:
+ return GEOMETRY_EXTENSIONS;
+#endif
+ case LLFilePicker::FFLOAD_XML:
+ return XML_EXTENSIONS;
+ case LLFilePicker::FFLOAD_ALL:
+ return ALL_FILE_EXTENSIONS;
+#endif
+ default:
+ return ALL_FILE_EXTENSIONS;
+ }
+}
+
+
+BOOL enable_buy(void*)
+{
+ // In order to buy, there must only be 1 purchaseable object in
+ // the selection manger.
+ if(gSelectMgr->getRootObjectCount() != 1) return FALSE;
+ LLViewerObject* obj = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node)
+ {
+ obj = node->getObject();
+ if(!obj) return FALSE;
+
+ if(node->mSaleInfo.isForSale() && node->mPermissions->getMaskOwner() & PERM_TRANSFER &&
+ (node->mPermissions->getMaskOwner() & PERM_COPY || node->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY))
+ {
+ if(obj->permAnyOwner()) return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+class LLObjectEnableBuy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_buy(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Note: This will only work if the selected object's data has been
+// received by the viewer and cached in the selection manager.
+void handle_buy_object(LLSaleInfo sale_info)
+{
+ if(!gSelectMgr->selectGetAllRootsValid())
+ {
+ LLNotifyBox::showXml("UnableToBuyWhileDownloading");
+ return;
+ }
+
+ LLUUID owner_id;
+ LLString owner_name;
+ BOOL owners_identical = gSelectMgr->selectGetOwner(owner_id, owner_name);
+ if (!owners_identical)
+ {
+ LLNotifyBox::showXml("CannotBuyObjectsFromDifferentOwners");
+ return;
+ }
+
+ LLPermissions perm;
+ BOOL valid = gSelectMgr->selectGetPermissions(perm);
+ LLAggregatePermissions ag_perm;
+ valid &= gSelectMgr->selectGetAggregatePermissions(ag_perm);
+ if(!valid || !sale_info.isForSale() || !perm.allowTransferTo(gAgent.getID()))
+ {
+ LLNotifyBox::showXml("ObjectNotForSale");
+ return;
+ }
+
+ if(sale_info.getSalePrice() > gStatusBar->getBalance())
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ "This object costs", sale_info.getSalePrice());
+ return;
+ }
+
+ LLFloaterBuy::show(sale_info);
+}
+
+
+void handle_buy_contents(LLSaleInfo sale_info)
+{
+ LLFloaterBuyContents::show(sale_info);
+}
+
+class LLFileEnableSaveAs : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gFloaterView->getFrontmost() && gFloaterView->getFrontmost()->canSaveAs();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void handle_region_dump_temp_asset_data(void*)
+{
+ llinfos << "Dumping temporary asset data to simulator logs" << llendl;
+ std::vector<std::string> strings;
+ LLUUID invoice;
+ send_generic_message("dumptempassetdata", strings, invoice);
+}
+
+void handle_region_clear_temp_asset_data(void*)
+{
+ llinfos << "Clearing temporary asset data" << llendl;
+ std::vector<std::string> strings;
+ LLUUID invoice;
+ send_generic_message("cleartempassetdata", strings, invoice);
+}
+
+void handle_region_dump_settings(void*)
+{
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ llinfos << "Damage: " << (regionp->getAllowDamage() ? "on" : "off") << llendl;
+ llinfos << "Landmark: " << (regionp->getAllowLandmark() ? "on" : "off") << llendl;
+ llinfos << "SetHome: " << (regionp->getAllowSetHome() ? "on" : "off") << llendl;
+ llinfos << "ResetHome: " << (regionp->getResetHomeOnTeleport() ? "on" : "off") << llendl;
+ llinfos << "SunFixed: " << (regionp->getSunFixed() ? "on" : "off") << llendl;
+ llinfos << "BlockFly: " << (regionp->getBlockFly() ? "on" : "off") << llendl;
+ llinfos << "AllowP2P: " << (regionp->getAllowDirectTeleport() ? "on" : "off") << llendl;
+ llinfos << "Water: " << (regionp->getWaterHeight()) << llendl;
+ }
+}
+
+void handle_dump_group_info(void *)
+{
+ llinfos << "group " << gAgent.mGroupName << llendl;
+ llinfos << "ID " << gAgent.mGroupID << llendl;
+ llinfos << "powers " << gAgent.mGroupPowers << llendl;
+ llinfos << "title " << gAgent.mGroupTitle << llendl;
+ //llinfos << "insig " << gAgent.mGroupInsigniaID << llendl;
+}
+
+
+void handle_dump_focus(void *)
+{
+ LLView *view = gFocusMgr.getKeyboardFocus();
+
+ llinfos << "Keyboard focus " << (view ? view->getName() : "(none)") << llendl;
+}
+
+class LLSelfStandUp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ return true;
+ }
+};
+
+class LLSelfEnableStandUp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getAvatarObject() && gAgent.getAvatarObject()->mIsSitting;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL check_admin_override(void*)
+{
+ return gAgent.getAdminOverride();
+}
+
+void handle_admin_override_toggle(void*)
+{
+ if(!gAgent.getAdminOverride())
+ {
+ gAgent.setAdminOverride(TRUE);
+ show_debug_menus();
+ }
+ else gAgent.setAdminOverride(FALSE);
+}
+
+void handle_god_mode(void*)
+{
+ gAgent.requestEnterGodMode();
+}
+
+void handle_leave_god_mode(void*)
+{
+ gAgent.requestLeaveGodMode();
+}
+
+void set_god_level(U8 god_level)
+{
+ U8 old_god_level = gAgent.getGodLevel();
+ gAgent.setGodLevel( god_level );
+ show_debug_menus();
+ gIMView->refresh();
+ gParcelMgr->notifyObservers();
+
+ // Some classifieds change visibility on god mode
+ LLFloaterDirectory::requestClassified();
+
+ // God mode changes sim visibility
+ gWorldMap->reset();
+ gWorldMap->setCurrentLayer(0);
+
+ // inventory in items may change in god mode
+ gObjectList.dirtyAllObjectInventory();
+
+ LLString::format_map_t args;
+ if(god_level > GOD_NOT)
+ {
+ args["[LEVEL]"] = llformat("%d",(S32)god_level);
+ if (gInProductionGrid)
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuBarGodBgColor" ) );
+ }
+ else
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuNonProductionGodBgColor" ) );
+ }
+ LLNotifyBox::showXml("EnteringGodMode", args);
+ }
+ else
+ {
+ args["[LEVEL]"] = llformat("%d",(S32)old_god_level);
+ if (gInProductionGrid)
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuBarBgColor" ) );
+ }
+ else
+ {
+ gMenuBarView->setBackgroundColor( gColors.getColor( "MenuNonProductionBgColor" ) );
+ }
+ LLNotifyBox::showXml("LeavingGodMode", args);
+ }
+}
+
+#ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+void handle_toggle_hacked_godmode(void*)
+{
+ gHackGodmode = !gHackGodmode;
+ set_god_level(gHackGodmode ? GOD_MAINTENANCE : GOD_NOT);
+}
+
+BOOL check_toggle_hacked_godmode(void*)
+{
+ return gHackGodmode;
+}
+#endif
+
+void process_grant_godlike_powers(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((agent_id == gAgent.getID()) && (session_id == gAgent.getSessionID()))
+ {
+ U8 god_level;
+ msg->getU8Fast(_PREHASH_GrantData, _PREHASH_GodLevel, god_level);
+ set_god_level(god_level);
+ }
+ else
+ {
+ llwarns << "Grant godlike for wrong agent " << agent_id << llendl;
+ }
+}
+
+void load_url_local_file(const char* file_name)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+#if LL_DARWIN || LL_LINUX
+ // MBW -- If the Mac client is in fullscreen mode, it needs to go windowed so the browser will be visible.
+ if(gViewerWindow->mWindow->getFullscreen())
+ {
+ gViewerWindow->toggleFullscreen(TRUE);
+ }
+#endif
+
+ // JC - system() blocks until IE has launched.
+ // spawn() runs asynchronously, but opens a command prompt.
+ // ShellExecute() just opens the damn file with the default
+ // web browser.
+ std::string full_path = "file:///";
+ full_path.append(gDirUtilp->getAppRODataDir());
+ full_path.append(gDirUtilp->getDirDelimiter());
+ full_path.append(file_name);
+
+ LLWeb::loadURL(full_path.c_str());
+}
+
+/*
+class LLHaveCallingcard : public LLInventoryCollectFunctor
+{
+public:
+ LLHaveCallingcard(const LLUUID& agent_id);
+ virtual ~LLHaveCallingcard() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ BOOL isThere() const { return mIsThere;}
+protected:
+ LLUUID mID;
+ BOOL mIsThere;
+};
+
+LLHaveCallingcard::LLHaveCallingcard(const LLUUID& agent_id) :
+ mID(agent_id),
+ mIsThere(FALSE)
+{
+}
+
+bool LLHaveCallingcard::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item)
+ {
+ if((item->getType() == LLAssetType::AT_CALLINGCARD)
+ && (item->getCreatorUUID() == mID))
+ {
+ mIsThere = TRUE;
+ }
+ }
+ return FALSE;
+}
+*/
+
+BOOL is_agent_friend(const LLUUID& agent_id)
+{
+ return (LLAvatarTracker::instance().getBuddyInfo(agent_id) != NULL);
+}
+
+BOOL is_agent_mappable(const LLUUID& agent_id)
+{
+ return (is_agent_friend(agent_id) &&
+ LLAvatarTracker::instance().getBuddyInfo(agent_id)->isOnline() &&
+ LLAvatarTracker::instance().getBuddyInfo(agent_id)->isRightGrantedFrom(LLRelationship::GRANT_MAP_LOCATION)
+ );
+}
+
+// Enable a menu item when you have someone's card.
+/*
+BOOL enable_have_card(void *userdata)
+{
+ LLUUID* avatar_id = (LLUUID *)userdata;
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+ else if(avatar_id)
+ {
+ return is_agent_friend(*avatar_id);
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+*/
+
+// Enable a menu item when you don't have someone's card.
+class LLAvatarEnableAddFriend : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+ bool new_value = avatar && !is_agent_friend(avatar->getID());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void request_friendship(const LLUUID& dest_id)
+{
+ LLViewerObject* dest = gObjectList.findObject(dest_id);
+ if(dest && dest->isAvatar())
+ {
+ LLString fullname;
+ LLString::format_map_t args;
+ LLNameValue* nvfirst = dest->getNVPair("FirstName");
+ LLNameValue* nvlast = dest->getNVPair("LastName");
+ if(nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ fullname = nvfirst->getString();
+ fullname += " ";
+ fullname += nvlast->getString();
+ }
+ if (!fullname.empty())
+ {
+ LLFloaterFriends::requestFriendship(dest_id, fullname);
+ LLNotifyBox::showXml("OfferedFriendship", args);
+ }
+ else
+ {
+ gViewerWindow->alertXml("CantOfferFriendship");
+ }
+ }
+}
+
+
+class LLEditEnableCustomizeAvatar : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getWearablesLoaded();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+bool handle_sit_or_stand()
+{
+ LLViewerObject *object = gObjectList.findObject(gLastHitNonFloraObjectID);
+ if (!object)
+ {
+ return true;
+ }
+
+ if (sitting_on_selection())
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+
+ // get object selection offset
+
+ if (object && object->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVector3d offset_double = gViewerWindow->lastNonFloraObjectHitOffset();
+ LLVector3 offset_single;
+ offset_single.setVec(offset_double);
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentRequestSit);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_TargetObject);
+ gMessageSystem->addUUIDFast(_PREHASH_TargetID, object->mID);
+ gMessageSystem->addVector3Fast(_PREHASH_Offset, offset_single);
+
+ object->getRegion()->sendReliableMessage();
+
+ gSelectMgr->deselectTransient();
+ }
+ return true;
+}
+
+class LLObjectSitOrStand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_sit_or_stand();
+ }
+};
+
+void near_sit_down_point(BOOL success, void *)
+{
+ gAgent.setFlying(FALSE);
+ gAgent.setControlFlags(AGENT_CONTROL_SIT_ON_GROUND);
+
+ // Might be first sit
+ LLFirstUse::useSit();
+}
+
+class LLLandSit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_STAND_UP);
+ gParcelMgr->deselectLand();
+
+ LLVector3d posGlobal = gLastHitPosGlobal;
+
+ LLQuaternion target_rot;
+ if (gAgent.getAvatarObject())
+ {
+ target_rot = gAgent.getAvatarObject()->getRotation();
+ }
+ else
+ {
+ target_rot = gAgent.getFrameAgent().getQuaternion();
+ }
+ gAgent.startAutoPilotGlobal(posGlobal, "Sit", &target_rot, near_sit_down_point, NULL, 0.7f);
+ return true;
+ }
+};
+
+void show_permissions_control(void*)
+{
+ LLFloaterPermissionsMgr* floaterp = LLFloaterPermissionsMgr::show();
+ floaterp->mPermissions->addPermissionsData("foo1", LLUUID::null, 0);
+ floaterp->mPermissions->addPermissionsData("foo2", LLUUID::null, 0);
+ floaterp->mPermissions->addPermissionsData("foo3", LLUUID::null, 0);
+}
+
+#if 0 // Unused (these just modify AudioInfoPage which is not used anywhere in the code
+void handle_audio_status_1(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (1 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 1;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_2(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (2 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 2;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_3(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (3 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 3;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+
+void handle_audio_status_4(void*)
+{
+ S32 page = gSavedSettings.getS32("AudioInfoPage");
+ if (4 == page)
+ {
+ page = 0;
+ }
+ else
+ {
+ page = 4;
+ }
+ gSavedSettings.setS32("AudioInfoPage", page);
+}
+#endif
+
+void reload_ui(void *)
+{
+ gUICtrlFactory->rebuild();
+}
+
+class LLWorldFly : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.toggleFlying();
+ return true;
+ }
+};
+
+void handle_agent_stop_moving(void*)
+{
+ // stop agent
+ gAgent.setControlFlags(AGENT_CONTROL_STOP);
+
+ // cancel autopilot
+ gAgent.stopAutoPilot();
+}
+
+void print_packets_lost(void*)
+{
+ gWorldPointer->printPacketsLost();
+}
+
+
+void drop_packet(void*)
+{
+ gMessageSystem->mPacketRing.dropPackets(1);
+}
+
+
+void velocity_interpolate( void* data )
+{
+ BOOL toggle = gSavedSettings.getBOOL("VelocityInterpolate");
+ LLMessageSystem* msg = gMessageSystem;
+ if ( !toggle )
+ {
+ msg->newMessageFast(_PREHASH_VelocityInterpolateOn);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ llinfos << "Velocity Interpolation On" << llendl;
+ }
+ else
+ {
+ msg->newMessageFast(_PREHASH_VelocityInterpolateOff);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+ llinfos << "Velocity Interpolation Off" << llendl;
+ }
+ // BUG this is a hack because of the change in menu behavior. The
+ // old menu system would automatically change a control's value,
+ // but the new LLMenuGL system doesn't know what a control
+ // is. However, it's easy to distinguish between the two callers
+ // because LLMenuGL passes in the name of the user data (the
+ // control name) to the callback function, and the user data goes
+ // unused in the old menu code. Thus, if data is not null, then we
+ // need to swap the value of the control.
+ if( data )
+ {
+ gSavedSettings.setBOOL( static_cast<char*>(data), !toggle );
+ }
+}
+
+
+void update_fov(S32 increments)
+{
+ F32 old_fov = gCamera->getDefaultFOV();
+ // for each increment, FoV is 20% bigger
+ F32 new_fov = old_fov * pow(1.2f, increments);
+
+ // cap the FoV
+ new_fov = llclamp(new_fov, MIN_FIELD_OF_VIEW, MAX_FIELD_OF_VIEW);
+
+ if (new_fov != old_fov)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentFOV);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+
+ msg->nextBlockFast(_PREHASH_FOVBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ msg->addF32Fast(_PREHASH_VerticalAngle, new_fov);
+
+ gAgent.sendReliableMessage();
+
+ // force agent to update dirty patches
+ gCamera->setDefaultFOV(new_fov);
+ gCamera->setView(new_fov);
+ }
+}
+
+class LLViewZoomOut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ update_fov(1);
+ return true;
+ }
+};
+
+class LLViewZoomIn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ update_fov(-1);
+ return true;
+ }
+};
+
+class LLViewZoomDefault : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ F32 old_fov = gCamera->getView();
+ // for each increment, FoV is 20% bigger
+ F32 new_fov = DEFAULT_FIELD_OF_VIEW;
+
+ if (new_fov != old_fov)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_AgentFOV);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->nextBlockFast(_PREHASH_FOVBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ msg->addF32Fast(_PREHASH_VerticalAngle, new_fov);
+
+ gAgent.sendReliableMessage();
+
+ // force agent to update dirty patches
+ gCamera->setDefaultFOV(new_fov);
+ gCamera->setView(new_fov);
+ }
+ return true;
+ }
+};
+
+
+
+void toggle_wind_audio(void)
+{
+ if (gAudiop)
+ {
+ gAudiop->enableWind(!(gAudiop->isWindEnabled()));
+ }
+}
+
+
+// Callback for enablement
+BOOL is_inventory_visible( void* user_data )
+{
+ LLInventoryView* iv = reinterpret_cast<LLInventoryView*>(user_data);
+ if( iv )
+ {
+ return iv->getVisible();
+ }
+ return FALSE;
+}
+
+void handle_show_newest_map(void*)
+{
+ LLFloaterWorldMap::show(NULL, FALSE);
+}
+
+//-------------------------------------------------------------------
+// Help menu functions
+//-------------------------------------------------------------------
+
+class LLHelpMOTD : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString::format_map_t args;
+ args["[MOTD]"] = gAgent.mMOTD;
+ gViewerWindow->alertXml("MOTD", args, NULL, NULL);
+ return true;
+ }
+};
+
+class LLHelpLiveHelp : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // the session_id of a 911 session will always be this agent's session id
+ static LLUUID session_id(LLUUID::null);
+ if (session_id.isNull())
+ {
+ session_id.generate();
+ }
+ gIMView->setFloaterOpen(TRUE);
+ LLDynamicArray<LLUUID> members;
+ members.put(gAgent.getID());
+ gIMView->addSession("Help Request", IM_SESSION_911_START, session_id, members); //xui: translate
+ return true;
+ }
+};
+
+//
+// Major mode switching
+//
+void reset_view_final( BOOL proceed, void* );
+
+void handle_reset_view()
+{
+ if( (CAMERA_MODE_CUSTOMIZE_AVATAR == gAgent.getCameraMode()) && gFloaterCustomize )
+ {
+ // Show dialog box if needed.
+ gFloaterCustomize->askToSaveAllIfDirty( reset_view_final, NULL );
+ }
+ else
+ {
+ reset_view_final( TRUE, NULL );
+ }
+}
+
+class LLViewResetView : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_reset_view();
+ return true;
+ }
+};
+
+// Note: extra parameters allow this function to be called from dialog.
+void reset_view_final( BOOL proceed, void* )
+{
+ if( !proceed )
+ {
+ return;
+ }
+
+ gAgent.changeCameraToDefault();
+
+ gAgent.resetView(!gFloaterTools->getVisible());
+
+ gViewerWindow->showCursor();
+
+ // Switch back to basic toolset
+ gCurrentToolset = gBasicToolset;
+ gBasicToolset->selectFirstTool();
+ gToolMgr->useSelectedTool( gBasicToolset );
+}
+
+class LLViewLookAtLastChatter : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.lookAtLastChat();
+ return true;
+ }
+};
+
+class LLViewMouselook : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (!gAgent.cameraMouselook())
+ {
+ gAgent.changeCameraToMouselook();
+ }
+ else
+ {
+ gAgent.changeCameraToDefault();
+ }
+ return true;
+ }
+};
+
+class LLViewFullscreen : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gViewerWindow->toggleFullscreen(TRUE);
+ return true;
+ }
+};
+
+class LLViewDefaultUISize : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSavedSettings.setF32("UIScaleFactor", 1.0f);
+ gSavedSettings.setBOOL("UIAutoScale", FALSE);
+ gViewerWindow->reshape(gViewerWindow->getWindowDisplayWidth(), gViewerWindow->getWindowDisplayHeight());
+ return true;
+ }
+};
+
+class LLEditDuplicate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(gEditMenuHandler)
+ {
+ gEditMenuHandler->duplicate();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableDuplicate : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDuplicate();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+void disabled_duplicate(void*)
+{
+ if (gSelectMgr->getFirstObject())
+ {
+ LLNotifyBox::showXml("CopyFailed");
+ }
+}
+
+void handle_duplicate_in_place(void*)
+{
+ llinfos << "handle_duplicate_in_place" << llendl;
+
+ LLVector3 offset(0.f, 0.f, 0.f);
+ gSelectMgr->selectDuplicate(offset, TRUE);
+}
+
+void handle_repeat_duplicate(void*)
+{
+ gSelectMgr->repeatDuplicate();
+}
+
+void handle_deed_object_to_group(void*)
+{
+ LLUUID group_id;
+
+ gSelectMgr->selectGetGroup(group_id);
+ gSelectMgr->sendOwner(LLUUID::null, group_id, FALSE);
+ gViewerStats->incStat(LLViewerStats::ST_RELEASE_COUNT);
+}
+
+BOOL enable_deed_object_to_group(void*)
+{
+ if(gSelectMgr->isEmpty()) return FALSE;
+ LLPermissions perm;
+ LLUUID group_id;
+
+ if (gSelectMgr->selectGetGroup(group_id) &&
+ gAgent.hasPowerInGroup(group_id, GP_OBJECT_DEED) &&
+ gSelectMgr->selectGetPermissions(perm) &&
+ perm.deedToGroup(gAgent.getID(), group_id))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ * No longer able to support viewer side manipulations in this way
+ *
+void god_force_inv_owner_permissive(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void*)
+{
+ typedef std::vector<LLPointer<LLViewerInventoryItem> > item_array_t;
+ item_array_t items;
+
+ InventoryObjectList::const_iterator inv_it = inventory->begin();
+ InventoryObjectList::const_iterator inv_end = inventory->end();
+ for ( ; inv_it != inv_end; ++inv_it)
+ {
+ if(((*inv_it)->getType() != LLAssetType::AT_CATEGORY)
+ && ((*inv_it)->getType() != LLAssetType::AT_ROOT_CATEGORY))
+ {
+ LLInventoryObject* obj = *inv_it;
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem((LLViewerInventoryItem*)obj);
+ LLPermissions perm(new_item->getPermissions());
+ perm.setMaskBase(PERM_ALL);
+ perm.setMaskOwner(PERM_ALL);
+ new_item->setPermissions(perm);
+ items.push_back(new_item);
+ }
+ }
+ item_array_t::iterator end = items.end();
+ item_array_t::iterator it;
+ for(it = items.begin(); it != end; ++it)
+ {
+ // since we have the inventory item in the callback, it should not
+ // invalidate iteration through the selection manager.
+ object->updateInventory((*it), TASK_INVENTORY_ITEM_KEY, false);
+ }
+}
+*/
+
+void handle_object_owner_permissive(void*)
+{
+ // only send this if they're a god.
+ if(gAgent.isGodlike())
+ {
+ // do the objects.
+ gSelectMgr->setObjectPermissions(PERM_BASE, TRUE, PERM_ALL, TRUE);
+ gSelectMgr->setObjectPermissions(PERM_OWNER, TRUE, PERM_ALL, TRUE);
+ }
+}
+
+void handle_object_owner_self(void*)
+{
+ // only send this if they're a god.
+ if(gAgent.isGodlike())
+ {
+ gSelectMgr->sendOwner(gAgent.getID(), gAgent.getGroupID(), TRUE);
+ }
+}
+
+// Shortcut to set owner permissions to not editable.
+void handle_object_lock(void*)
+{
+ gSelectMgr->setObjectPermissions(PERM_OWNER, FALSE, PERM_MODIFY);
+}
+
+void handle_object_asset_ids(void*)
+{
+ // only send this if they're a god.
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("objectinfo", "assetids");
+ }
+}
+
+void handle_force_parcel_owner_to_me(void*)
+{
+ gParcelMgr->sendParcelGodForceOwner( gAgent.getID() );
+}
+
+void handle_force_parcel_to_content(void*)
+{
+ gParcelMgr->sendParcelGodForceToContent();
+}
+
+void handle_claim_public_land(void*)
+{
+ if (gParcelMgr->getSelectionRegion() != gAgent.getRegion())
+ {
+ LLNotifyBox::showXml("ClaimPublicLand");
+ return;
+ }
+
+ LLVector3d west_south_global;
+ LLVector3d east_north_global;
+ gParcelMgr->getSelection(west_south_global, east_north_global);
+ LLVector3 west_south = gAgent.getPosAgentFromGlobal(west_south_global);
+ LLVector3 east_north = gAgent.getPosAgentFromGlobal(east_north_global);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GodlikeMessage");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", "claimpublicland");
+ msg->addUUID("Invoice", LLUUID::null);
+ char buffer[32];
+ sprintf(buffer, "%f", west_south.mV[VX]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", west_south.mV[VY]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", east_north.mV[VX]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ sprintf(buffer, "%f", east_north.mV[VY]);
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", buffer);
+ gAgent.sendReliableMessage();
+}
+
+//void handle_toggle_parcel_newbie(void*)
+//{
+// gParcelMgr->toggleParcelGodReserveForNewbie();
+//}
+
+void on_expunge_user(S32 option, const LLString& text, void*)
+{
+ if(option == -1) return;
+ llinfos << "on_expunge_user(" << option << "," << text << ")" << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ LLUUID user_id;
+ if(user_id.set(text))
+ {
+ msg->newMessage("GodExpungeUser");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ExpungeData");
+ msg->addUUID("AgentID", user_id);
+ msg->sendReliable(gUserServer);
+ }
+ else
+ {
+ gViewerWindow->alertXml("InvalidUUID");
+ }
+}
+
+void handle_god_expunge_user(void*)
+{
+ gViewerWindow->alertXmlEditText("ExpungeUser", LLString::format_map_t(),
+ NULL, NULL,
+ on_expunge_user, NULL);
+}
+
+void handle_god_request_havok(void *)
+{
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("havok", "infoverbose");
+ }
+}
+
+//void handle_god_request_foo(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest(GOD_WANTS_FOO);
+// }
+//}
+
+//void handle_god_request_terrain_save(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest("terrain", "save");
+// }
+//}
+
+//void handle_god_request_terrain_load(void *)
+//{
+// if (gAgent.isGodlike())
+// {
+// gSelectMgr->sendGodlikeRequest("terrain", "load");
+// }
+//}
+
+
+// HACK for easily testing new avatar geometry
+void handle_god_request_avatar_geometry(void *)
+{
+ if (gAgent.isGodlike())
+ {
+ gSelectMgr->sendGodlikeRequest("avatar toggle", NULL);
+ }
+}
+
+
+void handle_show_overlay_title(void*)
+{
+ gShowOverlayTitle = !gShowOverlayTitle;
+ gSavedSettings.setBOOL("ShowOverlayTitle", gShowOverlayTitle);
+}
+
+void derez_objects(EDeRezDestination dest, const LLUUID& dest_id)
+{
+ if(gAgent.cameraMouselook())
+ {
+ gAgent.changeCameraToDefault();
+ }
+ //gInventoryView->setPanelOpen(TRUE);
+
+ LLViewerObject* object = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(!node) return;
+ object = node->getObject();
+ if(!object) return;
+ LLViewerRegion* region = object->getRegion();
+ char* error = NULL;
+
+ // Check conditions that we can't deal with, building a list of
+ // everything that we'll actually be derezzing.
+ LLDynamicArray<LLViewerObject*> derez_objects;
+ BOOL can_derez_current;
+ for( ; node != NULL; node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid) continue;
+ if(object->getRegion() != region)
+ {
+ // *FIX: This doesn't work at all if the some of the
+ // objects are in regions besides the first object
+ // selected. We should really support this.
+
+ // ...crosses region boundaries
+ error = "AcquireErrorObjectSpan";
+ break;
+ }
+ if (object->isAvatar())
+ {
+ // ...don't acquire avatars
+ continue;
+ }
+
+ // If AssetContainers are being sent back, they will appear as
+ // boxes in the owner's inventory.
+ if (object->getNVPair("AssetContainer")
+ && dest != DRD_RETURN_TO_OWNER)
+ {
+ // this object is an asset container, derez its contents, not it
+ llwarns << "Attempt to derez deprecated AssetContainer object type not supported." << llendl;
+ /*
+ object->requestInventory(container_inventory_arrived,
+ (void *)(BOOL)(DRD_TAKE_INTO_AGENT_INVENTORY == dest));
+ */
+ continue;
+ }
+ can_derez_current = FALSE;
+ switch(dest)
+ {
+ case DRD_TAKE_INTO_AGENT_INVENTORY:
+ case DRD_TRASH:
+ if( (node->mPermissions->allowTransferTo(gAgent.getID()) && object->permModify())
+ || (node->allowOperationOnNode(PERM_OWNER, GP_OBJECT_MANIPULATE)) )
+ {
+ can_derez_current = TRUE;
+ }
+ break;
+
+ case DRD_RETURN_TO_OWNER:
+ can_derez_current = TRUE;
+ break;
+
+ default:
+ if((node->mPermissions->allowTransferTo(gAgent.getID())
+ && object->permCopy())
+ || gAgent.isGodlike())
+ {
+ can_derez_current = TRUE;
+ }
+ break;
+ }
+ if(can_derez_current)
+ {
+ derez_objects.put(object);
+ }
+ }
+
+ // This constant is based on (1200 - HEADER_SIZE) / 4 bytes per
+ // root. I lopped off a few (33) to provide a bit
+ // pad. HEADER_SIZE is currently 67 bytes, most of which is UUIDs.
+ // This gives us a maximum of 63500 root objects - which should
+ // satisfy anybody.
+ const S32 MAX_ROOTS_PER_PACKET = 250;
+ const S32 MAX_PACKET_COUNT = 254;
+ F32 packets = ceil((F32)derez_objects.count() / (F32)MAX_ROOTS_PER_PACKET);
+ if(packets > (F32)MAX_PACKET_COUNT)
+ {
+ error = "AcquireErrorTooManyObjects";
+ }
+
+ if(!error && derez_objects.count() > 0)
+ {
+ U8 d = (U8)dest;
+ LLUUID tid;
+ tid.generate();
+ U8 packet_count = (U8)packets;
+ S32 object_index = 0;
+ S32 objects_in_packet = 0;
+ LLMessageSystem* msg = gMessageSystem;
+ for(U8 packet_number = 0;
+ packet_number < packet_count;
+ ++packet_number)
+ {
+ msg->newMessageFast(_PREHASH_DeRezObject);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_AgentBlock);
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ msg->addU8Fast(_PREHASH_Destination, d);
+ msg->addUUIDFast(_PREHASH_DestinationID, dest_id);
+ msg->addUUIDFast(_PREHASH_TransactionID, tid);
+ msg->addU8Fast(_PREHASH_PacketCount, packet_count);
+ msg->addU8Fast(_PREHASH_PacketNumber, packet_number);
+ objects_in_packet = 0;
+ while((object_index < derez_objects.count())
+ && (objects_in_packet++ < MAX_ROOTS_PER_PACKET))
+
+ {
+ object = derez_objects.get(object_index++);
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID());
+ // VEFFECT: DerezObject
+ LLHUDEffectSpiral* effectp = (LLHUDEffectSpiral*)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(object->getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ }
+ msg->sendReliable(region->getHost());
+ }
+ make_ui_sound("UISndObjectRezOut");
+
+ // Busy count decremented by inventory update, so only increment
+ // if will be causing an update.
+ if (dest != DRD_RETURN_TO_OWNER)
+ {
+ gViewerWindow->getWindow()->incBusyCount();
+ }
+ }
+ else if(error)
+ {
+ gViewerWindow->alertXml(error);
+ }
+}
+
+class LLToolsTakeCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty()) return true;
+
+ const LLUUID& category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ derez_objects(DRD_ACQUIRE_TO_AGENT_INVENTORY, category_id);
+
+ // Only deselect if we're not building
+ if (!gToolMgr->inEdit())
+ {
+ gSelectMgr->deselectTransient();
+ }
+ return true;
+ }
+};
+
+
+void callback_return_to_owner(S32 option, void* data)
+{
+ if (0 == option)
+ {
+ // Ignore category ID for this derez destination.
+ derez_objects(DRD_RETURN_TO_OWNER, LLUUID::null);
+ }
+}
+
+// You can return an object to its owner if it is on your land.
+class LLObjectReturn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty()) return true;
+
+ gViewerWindow->alertXml("ReturnToOwner",
+ callback_return_to_owner,
+ NULL);
+ return true;
+ }
+};
+
+
+// Allow return to owner if one or more of the selected items is
+// over land you own.
+class LLObjectEnableReturn : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ bool new_value = true;
+#else
+ bool new_value = false;
+ if (gAgent.isGodlike())
+ {
+ new_value = true;
+ }
+ else
+ {
+ LLViewerRegion* region = gAgent.getRegion();
+ if (region)
+ {
+ // Estate owners and managers can always return objects.
+ if (region->canManageEstate())
+ {
+ new_value = true;
+ }
+ else
+ {
+ LLViewerObject* obj = NULL;
+ for(obj = gSelectMgr->getFirstRootObject();
+ obj;
+ obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->isOverAgentOwnedLand()
+ || obj->isOverGroupOwnedLand()
+ || obj->permModify())
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+void force_take_copy(void*)
+{
+ if (gSelectMgr->isEmpty()) return;
+ const LLUUID& category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ derez_objects(DRD_FORCE_TO_GOD_INVENTORY, category_id);
+
+ // Only deselect if we're not building
+ if (!gToolMgr->inEdit())
+ {
+ gSelectMgr->deselectTransient();
+ }
+}
+#ifdef _CORY_TESTING
+
+void force_export_copy(void*)
+{
+ LLViewerObject* object = NULL;
+ LLSelectNode* node = gSelectMgr->getFirstNode();
+ if(!node) return;
+ object = node->getObject();
+ if(!object) return;
+
+
+ LLString proposed_name;
+ proposed_name.append(node->mName);
+ proposed_name.append( ".slg" );
+
+ LLViewerRegion* region = object->getRegion();
+
+ // Check conditions that we can't deal with, building a list of
+ // everything that we'll actually be derezzing.
+
+ std::vector<LLViewerObject*> export_objects;
+ std::vector<std::string> export_names;
+ std::vector<std::string> export_descriptions;
+
+ S32 object_index = 0;
+
+ for( ; node != NULL; node = gSelectMgr->getNextNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid)
+ {
+ // Clicked cancel
+ return;
+ }
+ if(object->getRegion() != region)
+ {
+ // Clicked cancel
+ return;
+ }
+ if (object->isAvatar())
+ {
+ continue;
+ }
+
+ if (object->getNVPair("AssetContainer"))
+ {
+ continue;
+ }
+ export_objects.push_back(node->getObject());
+ export_names.push_back(node->mName);
+ export_descriptions.push_back(node->mDescription);
+ }
+
+ if (export_objects.empty())
+ {
+ return;
+ }
+
+ // pick a save file
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_GEOMETRY, proposed_name))
+ {
+ // Clicked cancel
+ return;
+ }
+
+ // Copy the directory + file name
+ char filepath[LL_MAX_PATH];
+ strcpy(filepath, picker.getFirstFile());
+
+ apr_file_t* fp = ll_apr_file_open(filepath, LL_APR_W);
+
+ if (!fp)
+ {
+ return;
+ }
+
+ object = export_objects[object_index];
+ LLVector3 baseoffset = object->getPositionRegion();
+
+ apr_file_printf(fp, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n");
+ apr_file_printf(fp, "<LindenGeometry>\n");
+
+ while(object_index < export_objects.size())
+ {
+ apr_file_printf(fp, "<Object\n");
+ apr_file_printf(fp, "\tShape='%s'\n", export_names[object_index].c_str());
+ apr_file_printf(fp, "\tDescription='%s'\n", export_descriptions[object_index].c_str());
+
+ apr_file_printf(fp, "\tPCode='%d'\n", (U32)object->getPCode());
+ apr_file_printf(fp, "\tMaterial='%d'\n", object->getMaterial());
+ apr_file_printf(fp, "\tScale='%5f %5f %5f'\n", object->getScale().mV[VX], object->getScale().mV[VY], object->getScale().mV[VZ]);
+ LLVector3 delta = object->getPositionRegion() - baseoffset;
+ LLQuaternion rot = object->getRotationRegion();
+ apr_file_printf(fp, "\tOffset='%5f %5f %5f'\n", delta.mV[VX], delta.mV[VY], delta.mV[VZ]);
+ apr_file_printf(fp, "\tOrientation='%5f %5f %5f %5f'\n", rot.mQ[VX], rot.mQ[VY], rot.mQ[VZ], rot.mQ[VS]);
+ const LLProfileParams pparams = object->getVolume()->getProfile().mParams;
+ apr_file_printf(fp, "\tShapeProfile='%d %f %f %f'\n", pparams.getCurveType(), pparams.getBegin(), pparams.getEnd(), pparams.getHollow());
+ const LLPathParams paparams = object->getVolume()->getPath().mParams;
+ apr_file_printf(fp, "\tShapePath='%d %f %f %f %f %f %f %f %f %f %f %f %f %f'\n",
+ paparams.getCurveType(), paparams.getBegin(), paparams.getEnd(), paparams.getTwist(), paparams.getTwistBegin(), paparams.getScaleX(), paparams.getScaleY(),
+ paparams.getShearX(), paparams.getShearY(), paparams.getRadiusOffset(), paparams.getTaperX(), paparams.getTaperY(),
+ paparams.getRevolutions(), paparams.getSkew());
+ S32 face, numfaces;
+ numfaces = object->getNumTEs();
+ apr_file_printf(fp, "\tNumberOfFaces='%d'>\n", numfaces);
+ for (face = 0; face < numfaces; face++)
+ {
+ const LLTextureEntry *te = object->getTE(face);
+ LLColor4 color = te->getColor();
+ apr_file_printf(fp, "\t<Face\n\t\tFaceColor='%d %5f %5f %5f %5f'\n", face, color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW]);
+
+ char texture[UUID_STR_LENGTH];
+ LLUUID texid = te->getID();
+ texid.toString(texture);
+ F32 sx, sy, ox, oy;
+ te->getScale(&sx, &sy);
+ te->getOffset(&ox, &oy);
+
+ apr_file_printf(fp, "\t\tFace='%d %5f %5f %5f %5f %5f %d %s'\n\t/>\n", face, sx, sy, ox, oy, te->getRotation(), te->getBumpShinyFullbright(), texture);
+ }
+ apr_file_printf(fp, "</Object>\n");
+ object = export_objects[++object_index];
+ }
+
+ apr_file_printf(fp, "</LindenGeometry>\n");
+
+ fclose(fp);
+}
+
+void undo_find_local_contact_point(LLVector3 &contact,
+ const LLVector3& surface_norm,
+ const LLQuaternion& rot,
+ const LLVector3& scale )
+{
+ LLVector3 local_norm = surface_norm;
+ local_norm.rotVec( ~rot );
+
+ LLVector3 v[6];
+ v[0].mV[VX] = -1.f;
+ v[1].mV[VX] = 1.f;
+
+ v[2].mV[VY] = -1.f;
+ v[3].mV[VY] = 1.f;
+
+ v[4].mV[VZ] = -1.f;
+ v[5].mV[VZ] = 1.f;
+
+ contact = v[0];
+ F32 cur_val = 0;
+
+ for( S32 i = 0; i < 6; i++ )
+ {
+ F32 val = v[i] * local_norm;
+ if( val < cur_val )
+ {
+ contact = v[i];
+ cur_val = val;
+ }
+ }
+
+ contact.mV[VX] *= 0.5f * scale.mV[VX];
+ contact.mV[VY] *= 0.5f * scale.mV[VY];
+ contact.mV[VZ] *= 0.5f * scale.mV[VZ];
+ contact.rotVec( rot );
+}
+
+
+
+void force_import_geometry(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(LLFilePicker::FFLOAD_GEOMETRY))
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ return;
+ }
+
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ llinfos << "Loading LSG file " << directory << llendl;
+ LLXmlTree *xmlparser = new LLXmlTree();
+ xmlparser->parseFile(directory, TRUE);
+ LLXmlTreeNode *root = xmlparser->getRoot();
+ if( !root )
+ {
+ return;
+ }
+ // header
+ if( !root->hasName( "LindenGeometry" ) )
+ {
+ llwarns << "Invalid LindenGeometry file header: " << directory << llendl;
+ return;
+ }
+ // objects
+ for (LLXmlTreeNode *child = root->getChildByName( "Object" );
+ child;
+ child = root->getNextNamedChild())
+ {
+ // get object data
+ char name[255]; // Shape
+ char description[255]; // Description
+ U32 material; // Material
+ F32 sx, sy, sz; // Scale
+ LLVector3 scale;
+ F32 ox, oy, oz; // Offset
+ LLVector3 offset;
+ F32 rx, ry, rz, rs; // Orientation
+ LLQuaternion rot;
+ U32 curve;
+ F32 begin;
+ F32 end;
+ F32 hollow;
+ F32 twist;
+ F32 scx, scy;
+ F32 shx, shy;
+ F32 twist_begin;
+ F32 radius_offset;
+ F32 tx, ty;
+ F32 revolutions;
+ F32 skew;
+ S32 faces;
+ U32 pcode;
+ U32 flags = FLAGS_CREATE_SELECTED;
+
+ LLString attribute;
+
+ S32 count = 0;
+
+ child->getAttributeString("PCode", &attribute);
+ pcode = atoi(attribute.c_str());
+ child->getAttributeString("Shape", &attribute);
+ sscanf(attribute.c_str(), "%s", name);
+ child->getAttributeString("Description", &attribute);
+ sscanf(attribute.c_str(), "%s", description);
+ child->getAttributeString("Material", &attribute);
+ material = atoi(attribute.c_str());
+ child->getAttributeString("Scale", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f", &sx, &sy, &sz);
+ scale.setVec(sx, sy, sz);
+ child->getAttributeString("Offset", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f", &ox, &oy, &oz);
+ offset.setVec(ox, oy, oz);
+ child->getAttributeString("Orientation", &attribute);
+ sscanf(attribute.c_str(), "%f %f %f %f", &rx, &ry, &rz, &rs);
+ rot.mQ[VX] = rx;
+ rot.mQ[VY] = ry;
+ rot.mQ[VZ] = rz;
+ rot.mQ[VS] = rs;
+
+ child->getAttributeString("ShapeProfile", &attribute);
+ sscanf(attribute.c_str(), "%d %f %f %f", &curve, &begin, &end, &hollow);
+ LLProfileParams pparams(curve, begin, end, hollow);
+ child->getAttributeString("ShapePath", &attribute);
+ sscanf(attribute.c_str(), "%d %f %f %f %f %f %f %f %f %f %f %f %f %f",
+ &curve, &begin, &end, &twist, &twist_begin, &scx, &scy, &shx, &shy, &radius_offset, &tx, &ty, &revolutions, &skew);
+ LLPathParams paparams(curve, begin, end, scx, scy, shx, shy, twist, twist_begin, radius_offset, tx, ty, revolutions, skew);
+ child->getAttributeString("NumberOfFaces", &attribute);
+ faces = atoi(attribute.c_str());
+
+
+
+ gMessageSystem->newMessageFast(_PREHASH_ObjectAdd);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU8Fast(_PREHASH_PCode, pcode);
+ gMessageSystem->addU8Fast(_PREHASH_Material, material);
+ gMessageSystem->addU32Fast(_PREHASH_AddFlags, flags );
+ pparams.packMessage(gMessageSystem);
+ paparams.packMessage(gMessageSystem);
+
+ LLVector3 forward;
+ forward.setVec(3.f, 0.f, 1.f);
+ forward = forward * gAgent.getQuat();
+
+ LLVector3 start = gAgent.getPositionAgent() + forward;
+
+ start += offset;
+
+ // offset position to make up for error introduced by placement code
+ LLVector3 normal(0.f, 0.f, 1.f);
+ LLVector3 delta;
+
+ undo_find_local_contact_point(delta, normal, rot, scale);
+
+ start += delta;
+
+ gMessageSystem->addVector3Fast(_PREHASH_Scale, scale );
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, rot );
+ gMessageSystem->addVector3Fast(_PREHASH_RayStart, start );
+ gMessageSystem->addVector3Fast(_PREHASH_RayEnd, start );
+ gMessageSystem->addBOOLFast(_PREHASH_BypassRaycast, TRUE );
+ gMessageSystem->addBOOLFast(_PREHASH_RayEndIsIntersection, FALSE );
+
+ U8 state = 0;
+ gMessageSystem->addU8Fast(_PREHASH_State, state);
+
+ LLUUID ray_target_id;
+ gMessageSystem->addUUIDFast(_PREHASH_RayTargetID, ray_target_id );
+
+ /* Setting TE info through ObjectAdd is no longer supported.
+ LLPrimitive temp_primitive;
+ temp_primitive.setNumTEs(faces);
+ for (LLXmlTreeNode *face = child->getChildByName( "Face" );
+ face;
+ face = child->getNextNamedChild())
+ {
+ // read the faces
+ U32 facenumber;
+ LLColor4 color;
+ char texture[UUID_STR_LENGTH];
+ LLUUID texid;
+ texid.toString(texture);
+ F32 sx, sy, ox, oy, rot;
+ U8 bump;
+ LLTextureEntry te;
+
+ face->getAttributeString("FaceColor", &attribute);
+ sscanf(attribute, "%d %f %f %f %f", &facenumber, &color.mV[VX], &color.mV[VY], &color.mV[VZ], &color.mV[VW]);
+ face->getAttributeString("Face", &attribute);
+ sscanf(attribute, "%d %f %f %f %f %f %d %s", &facenumber, &sx, &sy, &ox, &oy, &rot, &bump, texture);
+ texid.set(texture);
+ te.setColor(color);
+ te.setBumpShinyFullbright(bump);
+ te.setID(texid);
+ te.setRotation(rot);
+ te.setOffset(ox, oy);
+ te.setScale(sx, sy);
+
+ temp_primitive.setTE(facenumber, te);
+ }
+
+ temp_primitive.packTEMessage(gMessageSystem);
+ */
+ gMessageSystem->sendReliable(gAgent.getRegionHost());
+ }
+
+}
+#endif
+
+void handle_take()
+{
+ // we want to use the folder this was derezzed from if it's
+ // available. Otherwise, derez to the normal place.
+ if(gSelectMgr->isEmpty()) return;
+ LLSelectNode* node = NULL;
+ LLViewerObject* object = NULL;
+ BOOL you_own_everything = TRUE;
+
+ BOOL locked_but_takeable_object = FALSE;
+ LLUUID category_id;
+ for(node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(object)
+ {
+ if(!object->permYouOwner())
+ {
+ you_own_everything = FALSE;
+ }
+
+ if(!object->permMove())
+
+ {
+
+ locked_but_takeable_object = TRUE;
+
+ }
+ }
+ if(node->mFolderID.notNull())
+ {
+ if(category_id.isNull())
+ {
+ category_id = node->mFolderID;
+ }
+ else if(category_id != node->mFolderID)
+ {
+ // we have found two potential destinations. break out
+ // now and send to the default location.
+ category_id.setNull();
+ break;
+ }
+ }
+ }
+ if(category_id.notNull())
+ {
+ // there is an unambiguous destination. See if this agent has
+ // such a location and it is not in the trash.
+ if(!gInventory.getCategory(category_id))
+ {
+ // nope, set to NULL.
+ category_id.setNull();
+ }
+ if(category_id.notNull())
+ {
+ LLUUID trash;
+ trash = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ if(category_id == trash || gInventory.isObjectDescendentOf(category_id, trash))
+ {
+ category_id.setNull();
+ }
+ }
+ }
+ if(category_id.isNull())
+ {
+ category_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_OBJECT);
+ }
+ LLUUID* cat_id = new LLUUID(category_id);
+ if(locked_but_takeable_object ||
+
+ !you_own_everything)
+ {
+
+ if(locked_but_takeable_object && you_own_everything)
+ {
+
+ gViewerWindow->alertXml("ConfirmObjectTakeLock",
+ confirm_take,
+ (void*)cat_id);
+
+ }
+ else if(!locked_but_takeable_object && !you_own_everything)
+ {
+
+ gViewerWindow->alertXml("ConfirmObjectTakeNoOwn",
+ confirm_take,
+ (void*)cat_id);
+ }
+ else
+ {
+ gViewerWindow->alertXml("ConfirmObjectTakeLockNoOwn",
+ confirm_take,
+ (void*)cat_id);
+ }
+
+
+ }
+
+ else
+ {
+ confirm_take(0, (void*)cat_id);
+ }
+}
+
+void confirm_take(S32 option, void* data)
+{
+ LLUUID* cat_id = (LLUUID*)data;
+ if(!cat_id) return;
+ if(enable_take() && (option == 0))
+ {
+ derez_objects(DRD_TAKE_INTO_AGENT_INVENTORY, *cat_id);
+ }
+ delete cat_id;
+}
+
+// You can take an item when it is public and transferrable, or when
+// you own it. We err on the side of enabling the item when at least
+// one item selected can be copied to inventory.
+BOOL enable_take()
+{
+ if (sitting_on_selection())
+ {
+ return FALSE;
+ }
+
+ LLViewerObject* object = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ object = node->getObject();
+ if(!object || !node->mValid) continue;
+ if (object->isAvatar())
+ {
+ // ...don't acquire avatars
+ continue;
+ }
+
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+# endif
+ if((node->mPermissions->allowTransferTo(gAgent.getID())
+ && object->permModify())
+ || (node->mPermissions->getOwner() == gAgent.getID()))
+ {
+ return TRUE;
+ }
+#endif
+ }
+ return FALSE;
+}
+
+class LLToolsBuyOrTake : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr->isEmpty())
+ {
+ return true;
+ }
+
+ if (is_selection_buy_not_take())
+ {
+ S32 total_price = selection_price();
+
+ if (total_price <= gStatusBar->getBalance())
+ {
+ handle_buy(NULL);
+ }
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ "Buying this costs", total_price);
+ }
+ }
+ else
+ {
+ handle_take();
+ }
+ return true;
+ }
+};
+
+class LLToolsEnableBuyOrTake : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool is_buy = is_selection_buy_not_take();
+ bool new_value = is_buy ? enable_buy(NULL) : enable_take();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK: Update label
+ LLString label;
+ LLString buy_text;
+ LLString take_text;
+ LLString param = userdata["data"].asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ buy_text = param.substr(0, offset);
+ take_text = param.substr(offset+1);
+ }
+ if (is_buy)
+ {
+ label = buy_text;
+ }
+ else
+ {
+ label = take_text;
+ }
+ gMenuHolder->childSetText("Pie Object Take", label);
+ gMenuHolder->childSetText("Menu Object Take", label);
+
+ return true;
+ }
+};
+
+// This is a small helper function to determine if we have a buy or a
+// take in the selection. This method is to help with the aliasing
+// problems of putting buy and take in the same pie menu space. After
+// a fair amont of discussion, it was determined to prefer buy over
+// take. The reasoning follows from the fact that when users walk up
+// to buy something, they will click on one or more items. Thus, if
+// anything is for sale, it becomes a buy operation, and the server
+// will group all of the buy items, and copyable/modifiable items into
+// one package and give the end user as much as the permissions will
+// allow. If the user wanted to take something, they will select fewer
+// and fewer items until only 'takeable' items are left. The one
+// exception is if you own everything in the selection that is for
+// sale, in this case, you can't buy stuff from yourself, so you can
+// take it.
+// return value = TRUE if selection is a 'buy'.
+// FALSE if selection is a 'take'
+BOOL is_selection_buy_not_take()
+{
+ LLViewerObject* obj = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ obj = node->getObject();
+ if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale()))
+ {
+ // you do not own the object and it is for sale, thus,
+ // it's a buy
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+S32 selection_price()
+{
+ LLViewerObject* obj = NULL;
+ S32 total_price = 0;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ obj = node->getObject();
+ if(obj && !(obj->permYouOwner()) && (node->mSaleInfo.isForSale()))
+ {
+ // you do not own the object and it is for sale.
+ // Add its price.
+ total_price += node->mSaleInfo.getSalePrice();
+ }
+ }
+
+ return total_price;
+}
+
+void callback_show_buy_currency(S32 option, void*)
+{
+ if (0 == option)
+ {
+ llinfos << "Loading page " << BUY_CURRENCY_URL << llendl;
+ LLWeb::loadURL(BUY_CURRENCY_URL);
+ }
+}
+
+
+void show_buy_currency(const char* extra)
+{
+ // Don't show currency web page for branded clients.
+
+ std::ostringstream mesg;
+ if (extra != NULL)
+ {
+ mesg << extra << "\n \n";
+ }
+ mesg << "Go to " << BUY_CURRENCY_URL << "\nfor information on purchasing currency?";
+
+ LLString::format_map_t args;
+ args["[EXTRA]"] = extra;
+ args["[URL]"] = BUY_CURRENCY_URL;
+ gViewerWindow->alertXml("PromptGoToCurrencyPage", args,
+ callback_show_buy_currency);
+}
+
+void handle_buy_currency(void*)
+{
+// LLFloaterBuyCurrency::buyCurrency();
+}
+
+void handle_buy(void*)
+{
+ if (gSelectMgr->isEmpty()) return;
+
+ LLSaleInfo sale_info;
+ BOOL valid = gSelectMgr->selectGetSaleInfo(sale_info);
+ if (!valid) return;
+
+ if (sale_info.getSaleType() == LLSaleInfo::FS_CONTENTS)
+ {
+ handle_buy_contents(sale_info);
+ }
+ else
+ {
+ handle_buy_object(sale_info);
+ }
+}
+
+class LLObjectBuy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_buy(NULL);
+ return true;
+ }
+};
+
+BOOL sitting_on_selection()
+{
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (!node)
+ {
+ return FALSE;
+ }
+
+ if (!node->mValid)
+ {
+ return FALSE;
+ }
+
+ LLViewerObject* root_object = node->getObject();
+ if (!root_object)
+ {
+ return FALSE;
+ }
+
+ // Need to determine if avatar is sitting on this object
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar)
+ {
+ return FALSE;
+ }
+
+ return (avatar->mIsSitting && avatar->getRoot() == root_object);
+}
+
+class LLToolsSaveToInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(enable_save_into_inventory(NULL))
+ {
+ derez_objects(DRD_SAVE_INTO_AGENT_INVENTORY, LLUUID::null);
+ }
+ return true;
+ }
+};
+
+class LLToolsSaveToObjectInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(gSelectMgr)
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node && (node->mValid) && (!node->mFromTaskID.isNull()))
+ {
+ // *FIX: check to see if the fromtaskid object exists.
+ derez_objects(DRD_SAVE_INTO_TASK_INVENTORY, node->mFromTaskID);
+ }
+ }
+ return true;
+ }
+};
+
+// Round the position of all root objects to the grid
+class LLToolsSnapObjectXY : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ F64 snap_size = (F64)gSavedSettings.getF32("GridResolution");
+
+ LLViewerObject* obj;
+ for (obj = gSelectMgr->getFirstRootObject();
+ obj != NULL;
+ obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->permModify())
+ {
+ LLVector3d pos_global = obj->getPositionGlobal();
+ F64 round_x = fmod(pos_global.mdV[VX], snap_size);
+ if (round_x < snap_size * 0.5)
+ {
+ // closer to round down
+ pos_global.mdV[VX] -= round_x;
+ }
+ else
+ {
+ // closer to round up
+ pos_global.mdV[VX] -= round_x;
+ pos_global.mdV[VX] += snap_size;
+ }
+
+ F64 round_y = fmod(pos_global.mdV[VY], snap_size);
+ if (round_y < snap_size * 0.5)
+ {
+ pos_global.mdV[VY] -= round_y;
+ }
+ else
+ {
+ pos_global.mdV[VY] -= round_y;
+ pos_global.mdV[VY] += snap_size;
+ }
+
+ obj->setPositionGlobal(pos_global, FALSE);
+ }
+ }
+ gSelectMgr->sendMultipleUpdate(UPD_POSITION);
+ return true;
+ }
+};
+
+// in order to link, all objects must have the same owner, and the
+// agent must have the ability to modify all of the objects. However,
+// we're not answering that question with this method. The question
+// we're answering is: does the user have a reasonable expectation
+// that a link operation should work? If so, return true, false
+// otherwise. this allows the handle_link method to more finely check
+// the selection and give an error message when the uer has a
+// reasonable expectation for the link to work, but it will fail.
+class LLToolsEnableLink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ // check if there are at least 2 objects selected, and that the
+ // user can modify at least one of the selected objects.
+
+ // in component mode, can't link
+ if (gSavedSettings.getBOOL("SelectLinkedSet"))
+ {
+ if(gSelectMgr->selectGetAllRootsValid() && gSelectMgr->getRootObjectCount() >= 2)
+ {
+ for(LLViewerObject* object = gSelectMgr->getFirstRootObject();
+ object != NULL;
+ object = gSelectMgr->getNextRootObject())
+ {
+ if(object->permModify())
+ {
+ new_value = true;
+ break;
+ }
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsLink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if(!gSelectMgr->selectGetAllRootsValid())
+ {
+ LLNotifyBox::showXml("UnableToLinkWhileDownloading");
+ return true;
+ }
+
+ S32 object_count = gSelectMgr->getObjectCount();
+ if (object_count > MAX_CHILDREN_PER_TASK + 1)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[COUNT]"] = llformat("%d", object_count);
+ int max = MAX_CHILDREN_PER_TASK+1;
+ args["[MAX]"] = llformat("%d", max);
+ gViewerWindow->alertXml("UnableToLinkObjects", args);
+ return true;
+ }
+
+ if(gSelectMgr->getRootObjectCount() < 2)
+ {
+ gViewerWindow->alertXml("CannotLinkIncompleteSet");
+ return true;
+ }
+ if(!gSelectMgr->selectGetRootsModify())
+ {
+ gViewerWindow->alertXml("CannotLinkModify");
+ return true;
+ }
+ LLUUID owner_id;
+ LLString owner_name;
+ if(!gSelectMgr->selectGetOwner(owner_id, owner_name))
+ {
+ // we don't actually care if you're the owner, but novices are
+ // the most likely to be stumped by this one, so offer the
+ // easiest and most likely solution.
+ gViewerWindow->alertXml("CannotLinkDifferentOwners");
+ return true;
+ }
+ gSelectMgr->sendLink();
+ return true;
+ }
+};
+
+class LLToolsEnableUnlink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gSelectMgr->selectGetAllRootsValid() &&
+ gSelectMgr->getFirstEditableObject() &&
+ !gSelectMgr->getFirstEditableObject()->isAttachment();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsUnlink : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSelectMgr->sendDelink();
+ return true;
+ }
+};
+
+
+class LLToolsStopAllAnimations : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+
+ if (!avatarp) return true;
+
+ LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.begin();
+ for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it)
+ {
+ avatarp->stopMotion( anim_it->second, TRUE );
+ }
+
+ avatarp->processAnimationStateChanges();
+ return true;
+ }
+};
+
+void handle_hinge(void*)
+{
+ gSelectMgr->sendHinge(1);
+}
+
+void handle_ptop(void*)
+{
+ gSelectMgr->sendHinge(2);
+}
+
+void handle_lptop(void*)
+{
+ gSelectMgr->sendHinge(3);
+}
+
+void handle_wheel(void*)
+{
+ gSelectMgr->sendHinge(4);
+}
+
+void handle_dehinge(void*)
+{
+ gSelectMgr->sendDehinge();
+}
+
+BOOL enable_dehinge(void*)
+{
+ LLViewerObject* obj = gSelectMgr->getFirstEditableObject();
+ return obj && !obj->isAttachment();
+}
+
+
+class LLEditEnableCut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canCut();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditCut : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->cut();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canCopy();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->copy();
+ }
+ return true;
+ }
+};
+
+class LLEditEnablePaste : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canPaste();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditPaste : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->paste();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDoDelete();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // If a text field can do a deletion, it gets precedence over deleting
+ // an object in the world.
+ if( gEditMenuHandler && gEditMenuHandler->canDoDelete())
+ {
+ gEditMenuHandler->doDelete();
+ }
+
+ // and close any pie/context menus when done
+ gMenuHolder->hideMenus();
+
+ // When deleting an object we may not actually be done
+ // Keep selection so we know what to delete when confirmation is needed about the delete
+ gPieObject->hide(TRUE);
+ return true;
+ }
+};
+
+class LLObjectEnableDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value =
+#ifdef HACKED_GODLIKE_VIEWER
+ TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ (!gInProductionGrid && gAgent.isGodlike()) ||
+# endif
+ (gSelectMgr && gSelectMgr->canDoDelete());
+#endif
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditSearch : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterDirectory::toggleFind(NULL);
+ return true;
+ }
+};
+
+class LLObjectDelete : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gSelectMgr)
+ {
+ gSelectMgr->doDelete();
+ }
+
+ // and close any pie/context menus when done
+ gMenuHolder->hideMenus();
+
+ // When deleting an object we may not actually be done
+ // Keep selection so we know what to delete when confirmation is needed about the delete
+ gPieObject->hide(TRUE);
+ return true;
+ }
+};
+
+void handle_force_delete(void*)
+{
+ gSelectMgr->selectForceDelete();
+}
+
+class LLViewEnableLastChatter : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ //FIXME: add check that last chatter is in range
+ bool new_value = (gAgent.cameraThirdPerson() && gAgent.getLastChatter().notNull());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditEnableDeselect : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canDeselect();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditDeselect : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->deselect();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableSelectAll : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canSelectAll();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+class LLEditSelectAll : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler )
+ {
+ gEditMenuHandler->selectAll();
+ }
+ return true;
+ }
+};
+
+
+class LLEditEnableUndo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canUndo();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditUndo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler && gEditMenuHandler->canUndo() )
+ {
+ gEditMenuHandler->undo();
+ }
+ return true;
+ }
+};
+
+class LLEditEnableRedo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gEditMenuHandler && gEditMenuHandler->canRedo();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditRedo : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gEditMenuHandler && gEditMenuHandler->canRedo() )
+ {
+ gEditMenuHandler->redo();
+ }
+ return true;
+ }
+};
+
+
+
+void print_object_info(void*)
+{
+ gSelectMgr->selectionDump();
+}
+
+
+void show_debug_menus()
+{
+ // this can get called at login screen where there is no menu so only toggle it if one exists
+ if ( gMenuBarView )
+ {
+ BOOL debug = gSavedSettings.getBOOL("UseDebugMenus");
+ gMenuBarView->setItemVisible(CLIENT_MENU_NAME, debug);
+ gMenuBarView->setItemEnabled(CLIENT_MENU_NAME, debug);
+ gMenuBarView->setItemVisible(SERVER_MENU_NAME, debug);
+ gMenuBarView->setItemEnabled(SERVER_MENU_NAME, debug);
+ //gMenuBarView->setItemVisible(LLString("DebugOptions"), visible);
+ //gMenuBarView->setItemVisible(LLString(AVI_TOOLS), visible);
+ };
+}
+
+void toggle_debug_menus(void*)
+{
+ BOOL visible = ! gSavedSettings.getBOOL("UseDebugMenus");
+ gSavedSettings.setBOOL("UseDebugMenus", visible);
+ show_debug_menus();
+}
+
+
+void toggle_map( void* user_data )
+{
+ // Toggle the item
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+ if (checked)
+ {
+ gFloaterMap->close();
+ }
+ else
+ {
+ gFloaterMap->open();
+ }
+}
+
+/**
+ char* upload_pick(void* data)
+
+ If applicable, brings up a file chooser in which the user selects a file
+ to upload for a particular task. If the file is valid for the given action,
+ returns the string to the full path filename, else returns NULL.
+ Data is the load filter for the type of file as defined in LLFilePicker.
+**/
+const char* upload_pick(void* data)
+{
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ // This doesn't seem necessary. JC
+ // display();
+ }
+
+ LLFilePicker::ELoadFilter type;
+ if(data)
+ {
+ type = (LLFilePicker::ELoadFilter)((S32)data);
+ }
+ else
+ {
+ type = LLFilePicker::FFLOAD_ALL;
+ }
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getOpenFile(type))
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ return NULL;
+ }
+
+ const char* filename = picker.getFirstFile();
+ const char* ext = strrchr(filename, '.');
+
+ //strincmp doesn't like NULL pointers
+ if (ext == NULL)
+ {
+ const char* short_name = strrchr(filename,
+ *gDirUtilp->getDirDelimiter().c_str());
+
+ // No extension
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = LLString(short_name + 1);
+ gViewerWindow->alertXml("NoFileExtension", args);
+ return NULL;
+ }
+ else
+ {
+ //so there is an extension
+ //loop over the valid extensions and compare to see
+ //if the extension is valid
+
+ //now grab the set of valid file extensions
+ const char* valids = build_extensions_string(type);
+ std::string valid_extensions = std::string(valids);
+
+ BOOL ext_valid = FALSE;
+
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(" ");
+ tokenizer tokens(valid_extensions, sep);
+ tokenizer::iterator token_iter;
+
+ //now loop over all valid file extensions
+ //and compare them to the extension of the file
+ //to be uploaded
+ for( token_iter = tokens.begin();
+ token_iter != tokens.end() && ext_valid != TRUE;
+ ++token_iter)
+ {
+ const char* cur_token = token_iter->c_str();
+
+ if (0 == strnicmp(cur_token, ext, strlen(cur_token)) ||
+ 0 == strnicmp(cur_token, "*.*", strlen(cur_token)))
+ {
+ //valid extension
+ //or the acceptable extension is any
+ ext_valid = TRUE;
+ }
+ }//end for (loop over all tokens)
+
+ if (ext_valid == FALSE)
+ {
+ //should only get here if the extension exists
+ //but is invalid
+ LLStringBase<char>::format_map_t args;
+ args["[EXTENSION]"] = ext;
+ args["[VALIDS]"] = valids;
+ gViewerWindow->alertXml("InvalidFileExtension", args);
+ return NULL;
+ }
+ }//end else (non-null extension)
+
+ //valid file extension
+
+ //now we check to see
+ //if the file is actually a valid image/sound/etc.
+ if (type == LLFilePicker::FFLOAD_WAV)
+ {
+ // pre-qualify wavs to make sure the format is acceptable
+ char error_msg[MAX_STRING];
+ if (check_for_invalid_wav_formats(filename,error_msg))
+ {
+ llinfos << error_msg << ": " << filename << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = filename;
+ gViewerWindow->alertXml( error_msg, args );
+ return NULL;
+ }
+ }//end if a wave/sound file
+
+
+ return filename;
+}
+
+void handle_upload_object(void* data)
+{
+ const char* filename = upload_pick(data);
+ if (filename)
+ {
+ // start the import
+ LLFloaterImport* floaterp = new LLFloaterImport(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_import.xml");
+ }
+}
+
+class LLFileUploadImage : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void *)(S32)LLFilePicker::FFLOAD_IMAGE);
+ if (filename)
+ {
+ LLFloaterImagePreview* floaterp = new LLFloaterImagePreview(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_image_preview.xml");
+ }
+ return TRUE;
+ }
+};
+
+class LLFileUploadSound : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void*)((S32)LLFilePicker::FFLOAD_WAV));
+ if (filename)
+ {
+ LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_sound_preview.xml");
+ }
+ return true;
+ }
+};
+
+class LLFileUploadAnim : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const char* filename = upload_pick((void*)((S32)LLFilePicker::FFLOAD_ANIM));
+ if (filename)
+ {
+ LLFloaterAnimPreview* floaterp = new LLFloaterAnimPreview(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_animation_preview.xml");
+ }
+ return true;
+ }
+};
+
+class LLFileUploadBulk : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // TODO:
+ // Iterate over all files
+ // Check extensions for uploadability, cost
+ // Check user balance for entire cost
+ // Charge user entire cost
+ // Loop, uploading
+ // If an upload fails, refund the user for that one
+ //
+ // Also fix single upload to charge first, then refund
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getMultipleOpenFiles())
+ {
+ const char* filename = picker.getFirstFile();
+ const char* name = picker.getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(filename, asset_name, asset_name, 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE); // file
+ }
+ else
+ {
+ llinfos << "Couldn't import objects from file" << llendl;
+ }
+ return true;
+ }
+};
+
+void upload_error(const char* error_message, const char* label, const std::string filename, const LLStringBase<char>::format_map_t args)
+{
+ llwarns << error_message << llendl;
+ gViewerWindow->alertXml(label, args);
+ if(remove(filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+}
+
+class LLFileCloseWindow : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloater *top = gFloaterView->getFrontmost();
+ if (top && top->hasFocus())
+ {
+ LLFloater::closeByMenu( top );
+ }
+ return true;
+ }
+};
+
+class LLFileSaveTexture : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloater* top = gFloaterView->getFrontmost();
+ if (top)
+ {
+ top->saveAs();
+ }
+ return true;
+ }
+};
+
+class LLFileTakeSnapshot : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLFloaterSnapshot::show(NULL);
+ return true;
+ }
+};
+
+class LLFileTakeSnapshotToDisk : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+
+ S32 width = gViewerWindow->getWindowDisplayWidth();
+ S32 height = gViewerWindow->getWindowDisplayHeight();
+
+ if (gSavedSettings.getBOOL("HighResSnapshot"))
+ {
+ width *= 2;
+ height *= 2;
+ }
+
+ if (gViewerWindow->rawSnapshot(raw,
+ width,
+ height,
+ TRUE,
+ gSavedSettings.getBOOL("RenderUIInSnapshot"),
+ FALSE))
+ {
+ if (!gQuietSnapshot)
+ {
+ gViewerWindow->playSnapshotAnimAndSound();
+ }
+ LLImageBase::setSizeOverride(TRUE);
+ gViewerWindow->saveImageNumbered(raw);
+ LLImageBase::setSizeOverride(FALSE);
+ }
+ return true;
+ }
+};
+
+class LLFileSaveMovie : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerWindow::saveMovieNumbered(NULL);
+ return true;
+ }
+};
+
+class LLFileSetWindowSize : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString size = userdata.asString();
+ S32 width, height;
+ sscanf(size.c_str(), "%d,%d", &width, &height);
+ LLViewerWindow::movieSize(width, height);
+ return true;
+ }
+};
+
+class LLFileQuit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ app_request_quit();
+ return true;
+ }
+};
+
+void handle_upload(void* data)
+{
+ const char* filename = upload_pick(data);
+ if (filename)
+ {
+ LLFloaterNameDesc* floaterp = new LLFloaterNameDesc(filename);
+ gUICtrlFactory->buildFloater(floaterp, "floater_name_description.xml");
+ }
+}
+
+void handle_compress_image(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getOpenFile(LLFilePicker::FFLOAD_IMAGE))
+ {
+ std::string infile(picker.getFirstFile());
+ std::string outfile = infile + ".j2c";
+
+ llinfos << "Input: " << infile << llendl;
+ llinfos << "Output: " << outfile << llendl;
+
+ BOOL success;
+
+ success = LLViewerImageList::createUploadFile(infile, outfile, IMG_CODEC_TGA);
+
+ if (success)
+ {
+ llinfos << "Compression complete" << llendl;
+ }
+ else
+ {
+ llinfos << "Compression failed: " << LLImageBase::getLastError() << llendl;
+ }
+ }
+}
+
+void upload_new_resource(const LLString& src_filename, std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm,
+ const LLString& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void *userdata)
+{
+ // Generate the temporary UUID.
+ LLString filename = gDirUtilp->getTempFilename();
+ LLTransactionID tid;
+ LLAssetID uuid;
+
+ LLStringBase<char>::format_map_t args;
+
+ LLString ext = src_filename.substr(src_filename.find_last_of('.'));
+ LLAssetType::EType asset_type = LLAssetType::AT_NONE;
+ char error_message[MAX_STRING];
+ error_message[0] = '\0';
+ LLString temp_str;
+
+ BOOL error = FALSE;
+
+ if (ext.empty())
+ {
+ LLString::size_type offset = filename.find_last_of(gDirUtilp->getDirDelimiter());
+ if (offset != LLString::npos)
+ offset++;
+ LLString short_name = filename.substr(offset);
+
+ // No extension
+ sprintf(error_message,
+ "No file extension for the file: '%s'\nPlease make sure the file has a correct file extension",
+ short_name.c_str());
+ args["[FILE]"] = short_name;
+ upload_error(error_message, "NofileExtension", filename, args);
+ return;
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".bmp") == 0 )
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_BMP ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".tga") == 0 )
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_TGA ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if( LLString::compareInsensitive(ext.c_str(),".jpg") == 0 || LLString::compareInsensitive(ext.c_str(),".jpeg") == 0)
+ {
+ asset_type = LLAssetType::AT_TEXTURE;
+ if (!LLViewerImageList::createUploadFile(src_filename,
+ filename,
+ IMG_CODEC_JPEG ))
+ {
+ sprintf(error_message, "Problem with file %s:\n\n%s\n",
+ src_filename.c_str(), LLImageBase::getLastError().c_str());
+ args["[FILE]"] = src_filename;
+ args["[ERROR]"] = LLImageBase::getLastError();
+ upload_error(error_message, "ProblemWithFile", filename, args);
+ return;
+ }
+ }
+ else if(LLString::compareInsensitive(ext.c_str(),".wav") == 0)
+ {
+ asset_type = LLAssetType::AT_SOUND; // tag it as audio
+ S32 encode_result = 0;
+
+ S32 bitrate = 128;
+
+ if (compression_info)
+ {
+ bitrate = compression_info;
+ }
+ llinfos << "Attempting to encode wav as an ogg file at " << bitrate << "kbps" << llendl;
+
+ encode_result = encode_vorbis_file_at(src_filename.c_str(), filename.c_str(), bitrate*1000);
+
+ if (LLVORBISENC_NOERR != encode_result)
+ {
+ switch(encode_result)
+ {
+ case LLVORBISENC_DEST_OPEN_ERR:
+ sprintf(error_message, "Couldn't open temporary compressed sound file for writing: %s\n", filename.c_str());
+ args["[FILE]"] = filename;
+ upload_error(error_message, "CannotOpenTemporarySoundFile", filename, args);
+ break;
+
+ default:
+ sprintf(error_message, "Unknown vorbis encode failure on: %s\n", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "UnknownVorbisEncodeFailure", filename, args);
+ break;
+ }
+ return;
+ }
+ }
+ else if(LLString::compareInsensitive(ext.c_str(),".tmp") == 0)
+ {
+ // This is a generic .lin resource file
+ asset_type = LLAssetType::AT_OBJECT;
+ FILE *in = LLFile::fopen(src_filename.c_str(), "rb");
+ if (in)
+ {
+ // read in the file header
+ char buf[16384];
+ S32 read;
+ S32 version;
+ if (fscanf(in, "LindenResource\nversion %d\n", &version))
+ {
+ if (2 == version)
+ {
+ char label[MAX_STRING];
+ char value[MAX_STRING];
+ S32 tokens_read;
+ while (fgets(buf, 1024, in))
+ {
+ label[0] = '\0';
+ value[0] = '\0';
+ tokens_read = sscanf(buf, "%s %s\n", label, value);
+
+ llinfos << "got: " << label << " = " << value
+ << llendl;
+
+ if (EOF == tokens_read)
+ {
+ fclose(in);
+ sprintf(error_message, "corrupt resource file: %s", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "CorruptResourceFile", filename, args);
+ return;
+ }
+
+ if (2 == tokens_read)
+ {
+ if (! strcmp("type", label))
+ {
+ asset_type = (LLAssetType::EType)(atoi(value));
+ }
+ }
+ else
+ {
+ if (! strcmp("_DATA_", label))
+ {
+ // below is the data section
+ break;
+ }
+ }
+ // other values are currently discarded
+ }
+
+ }
+ else
+ {
+ fclose(in);
+ sprintf(error_message, "unknown linden resource file version in file: %s", src_filename.c_str());
+ args["[FILE]"] = src_filename;
+ upload_error(error_message, "UnknownResourceFileVersion", filename, args);
+ return;
+ }
+ }
+ else
+ {
+ // this is an original binary formatted .lin file
+ // start over at the beginning of the file
+ fseek(in, 0, SEEK_SET);
+
+ const S32 MAX_ASSET_DESCRIPTION_LENGTH = 256;
+ const S32 MAX_ASSET_NAME_LENGTH = 64;
+ S32 header_size = 34 + MAX_ASSET_DESCRIPTION_LENGTH + MAX_ASSET_NAME_LENGTH;
+ S16 type_num;
+
+ // read in and throw out most of the header except for the type
+ fread(buf, header_size, 1, in);
+ memcpy(&type_num, buf + 16, sizeof(S16));
+ asset_type = (LLAssetType::EType)type_num;
+ }
+
+ // copy the file's data segment into another file for uploading
+ FILE *out = LLFile::fopen(filename.c_str(), "wb");
+ if (out)
+ {
+ while((read = fread(buf, 1, 16384, in)))
+ {
+ fwrite(buf, 1, read, out);
+ }
+ fclose(out);
+ }
+ else
+ {
+ fclose(in);
+ sprintf(error_message, "Unable to create output file: %s", filename.c_str());
+ args["[FILE]"] = filename;
+ upload_error(error_message, "UnableToCreateOutputFile", filename, args);
+ return;
+ }
+
+ fclose(in);
+ }
+ else
+ {
+ llinfos << "Couldn't open .lin file " << src_filename << llendl;
+ }
+ }
+ else if (LLString::compareInsensitive(ext.c_str(),".bvh") == 0)
+ {
+ sprintf(error_message, "We do not currently support bulk upload of animation files\n");
+ upload_error(error_message, "DoNotSupportBulkAnimationUpload", filename, args);
+ return;
+ }
+ else
+ {
+ // Unknown extension
+ sprintf(error_message, "Unknown file extension %s\nExpected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh", ext.c_str());
+ error = TRUE;;
+ }
+
+ // gen a new transaction ID for this asset
+ tid.generate();
+
+ if (!error)
+ {
+ uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+ // copy this file into the vfs for upload
+ S32 file_size;
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_RB, &file_size);
+ if (fp)
+ {
+ LLVFile file(gVFS, uuid, asset_type, LLVFile::WRITE);
+
+ file.setMaxSize(file_size);
+
+ const S32 buf_size = 65536;
+ U8 copy_buf[buf_size];
+ while ((file_size = ll_apr_file_read(fp, copy_buf, buf_size)))
+ {
+ file.write(copy_buf, file_size);
+ }
+ apr_file_close(fp);
+ }
+ else
+ {
+ sprintf(error_message, "Unable to access output file: %s", filename.c_str());
+ error = TRUE;
+ }
+ }
+
+ if (!error)
+ {
+ LLString t_disp_name = display_name;
+ if (t_disp_name.empty())
+ {
+ t_disp_name = src_filename;
+ }
+ upload_new_resource(tid, asset_type, name, desc, compression_info, // tid
+ destination_folder_type, inv_type, next_owner_perm,
+ display_name, callback, userdata);
+ }
+ else
+ {
+ llwarns << error_message << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[ERROR_MESSAGE]"] = error_message;
+ gViewerWindow->alertXml("ErrorMessage", args);
+ if(LLFile::remove(filename.c_str()) == -1)
+ {
+ lldebugs << "unable to remove temp file" << llendl;
+ }
+ LLFilePicker::instance().reset();
+ }
+}
+
+void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType asset_type,
+ std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm,
+ const LLString& display_name,
+ LLAssetStorage::LLStoreAssetCallback callback,
+ void *userdata)
+{
+ LLAssetID uuid = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ if( LLAssetType::AT_SOUND == asset_type )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_SOUND_COUNT );
+ }
+ else
+ if( LLAssetType::AT_TEXTURE == asset_type )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_TEXTURE_COUNT );
+ }
+ else
+ if( LLAssetType::AT_ANIMATION == asset_type)
+ {
+ gViewerStats->incStat(LLViewerStats::ST_UPLOAD_ANIM_COUNT );
+ }
+
+ if(LLInventoryType::IT_NONE == inv_type)
+ {
+ inv_type = LLInventoryType::defaultForAssetType(asset_type);
+ }
+ LLString::stripNonprintable(name);
+ LLString::stripNonprintable(desc);
+ if(name.empty())
+ {
+ name = "(No Name)";
+ }
+ if(desc.empty())
+ {
+ desc = "(No Description)";
+ }
+
+ // At this point, we're ready for the upload.
+ LLString upload_message = "Uploading...\n\n";
+ upload_message.append(display_name);
+ LLUploadDialog::modalUploadDialog(upload_message);
+
+ llinfos << "*** Uploading: " << llendl;
+ llinfos << "Type: " << LLAssetType::lookup(asset_type) << llendl;
+ llinfos << "UUID: " << uuid << llendl;
+ llinfos << "Name: " << name << llendl;
+ llinfos << "Desc: " << desc << llendl;
+ lldebugs << "Folder: " << gInventory.findCategoryUUIDForType(destination_folder_type) << llendl;
+ lldebugs << "Asset Type: " << LLAssetType::lookup(asset_type) << llendl;
+ std::string url = gAgent.getRegion()->getCapability("NewAgentInventory");
+ if (!url.empty())
+ {
+ llinfos << "New Agent Inventory via capability" << llendl;
+ LLSD body;
+ body["folder_id"] = gInventory.findCategoryUUIDForType((destination_folder_type == LLAssetType::AT_NONE) ? asset_type : destination_folder_type);
+ body["asset_type"] = LLAssetType::lookup(asset_type);
+ body["inventory_type"] = LLInventoryType::lookup(inv_type);
+ body["name"] = name;
+ body["description"] = desc;
+
+ std::ostringstream llsdxml;
+ LLSDSerialize::toXML(body, llsdxml);
+ lldebugs << "posting body to capability: " << llsdxml.str() << llendl;
+ LLHTTPClient::post(url, body, new LLNewAgentInventoryResponder(uuid, body));
+ }
+ else
+ {
+ llinfos << "NewAgentInventory capability not found, new agent inventory via asset system." << llendl;
+ // check for adequate funds
+ // TODO: do this check on the sim
+ if (LLAssetType::AT_SOUND == asset_type ||
+ LLAssetType::AT_TEXTURE == asset_type ||
+ LLAssetType::AT_ANIMATION == asset_type)
+ {
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+ S32 balance = gStatusBar->getBalance();
+ if (balance < upload_cost)
+ {
+ // insufficient funds, bail on this upload
+ LLFloaterBuyCurrency::buyCurrency("Uploading costs", upload_cost);
+ return;
+ }
+ }
+
+ LLResourceData* data = new LLResourceData;
+ data->mAssetInfo.mTransactionID = tid;
+ data->mAssetInfo.mUuid = uuid;
+ data->mAssetInfo.mType = asset_type;
+ data->mAssetInfo.mCreatorID = gAgentID;
+ data->mInventoryType = inv_type;
+ data->mNextOwnerPerm = next_owner_perm;
+ data->mUserData = userdata;
+ data->mAssetInfo.setName(name);
+ data->mAssetInfo.setDescription(desc);
+ data->mPreferredLocation = destination_folder_type;
+
+ LLAssetStorage::LLStoreAssetCallback asset_callback = &upload_done_callback;
+ if (callback)
+ {
+ asset_callback = callback;
+ }
+ gAssetStorage->storeAssetData(data->mAssetInfo.mTransactionID, data->mAssetInfo.mType,
+ asset_callback,
+ (void*)data,
+ FALSE);
+ }
+}
+
+void upload_done_callback(const LLUUID& uuid, void* user_data, S32 result) // StoreAssetData callback (fixed)
+{
+ LLResourceData* data = (LLResourceData*)user_data;
+ //LLAssetType::EType pref_loc = data->mPreferredLocation;
+ BOOL is_balance_sufficient = TRUE;
+ if(result >= 0)
+ {
+ LLAssetType::EType dest_loc = (data->mPreferredLocation == LLAssetType::AT_NONE) ? data->mAssetInfo.mType : data->mPreferredLocation;
+
+ if (LLAssetType::AT_SOUND == data->mAssetInfo.mType ||
+ LLAssetType::AT_TEXTURE == data->mAssetInfo.mType ||
+ LLAssetType::AT_ANIMATION == data->mAssetInfo.mType)
+ {
+ // Charge the user for the upload.
+ LLViewerRegion* region = gAgent.getRegion();
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+
+ if(!(can_afford_transaction(upload_cost)))
+ {
+ LLFloaterBuyCurrency::buyCurrency(
+ llformat("Uploading %s costs",
+ data->mAssetInfo.getName().c_str()),
+ upload_cost);
+ is_balance_sufficient = FALSE;
+ }
+ else if(region)
+ {
+ // Charge user for upload
+ gStatusBar->debitBalance(upload_cost);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoneyTransferRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_DestID, LLUUID::null);
+ msg->addU8("Flags", 0);
+ msg->addS32Fast(_PREHASH_Amount, upload_cost);
+ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addS32Fast(_PREHASH_TransactionType, TRANS_UPLOAD_CHARGE);
+ msg->addStringFast(_PREHASH_Description, NULL);
+ msg->sendReliable(region->getHost());
+ }
+ }
+
+ if(is_balance_sufficient)
+ {
+ // Actually add the upload to inventory
+ llinfos << "Adding " << uuid << " to inventory." << llendl;
+ LLUUID folder_id(gInventory.findCategoryUUIDForType(dest_loc));
+ if(folder_id.notNull())
+ {
+ U32 next_owner_perm = data->mNextOwnerPerm;
+ if(PERM_NONE == next_owner_perm)
+ {
+ next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ }
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ folder_id, data->mAssetInfo.mTransactionID, data->mAssetInfo.getName(),
+ data->mAssetInfo.getDescription(), data->mAssetInfo.mType,
+ data->mInventoryType, NOT_WEARABLE, next_owner_perm,
+ LLPointer<LLInventoryCallback>(NULL));
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+ }
+ }
+ else // if(result >= 0)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[FILE]"] = LLInventoryType::lookupHumanReadable(data->mInventoryType);
+ args["[REASON]"] = LLString(LLAssetStorage::getErrorString(result));
+ gViewerWindow->alertXml("CannotUploadReason", args);
+ }
+
+ LLUploadDialog::modalUploadFinished();
+ delete data;
+
+ // *FIX: This is a pretty big hack. What this does is check the
+ // file picker if there are any more pending uploads. If so,
+ // upload that file.
+ const char* next_file = LLFilePicker::instance().getNextFile();
+ if(is_balance_sufficient && next_file)
+ {
+ const char* name = LLFilePicker::instance().getDirname();
+
+ LLString asset_name = name;
+ LLString::replaceNonstandardASCII( asset_name, '?' );
+ LLString::replaceChar(asset_name, '|', '?');
+ LLString::stripNonprintable(asset_name);
+ LLString::trim(asset_name);
+
+ char* asset_name_str = (char*)asset_name.c_str();
+ char* end_p = strrchr(asset_name_str, '.'); // strip extension if exists
+ if( !end_p )
+ {
+ end_p = asset_name_str + strlen( asset_name_str );
+ }
+
+ S32 len = llmin( (S32) (DB_INV_ITEM_NAME_STR_LEN), (S32) (end_p - asset_name_str) );
+
+ asset_name = asset_name.substr( 0, len );
+
+ upload_new_resource(next_file, asset_name, asset_name, // file
+ 0, LLAssetType::AT_NONE, LLInventoryType::IT_NONE);
+ }
+}
+
+LLUUID gExporterRequestID;
+LLString gExportDirectory;
+
+LLUploadDialog *gExportDialog = NULL;
+
+void handle_export_selected( void * )
+{
+ if (gSelectMgr->isEmpty())
+ {
+ return;
+ }
+ llinfos << "Exporting selected objects:" << llendl;
+ LLViewerObject *object = gSelectMgr->getFirstRootObject();
+
+ gExporterRequestID.generate();
+ gExportDirectory = "";
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectExportSelected);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_RequestID, gExporterRequestID);
+ msg->addS16Fast(_PREHASH_VolumeDetail, 4);
+
+ for (; object != NULL; object = gSelectMgr->getNextRootObject())
+ {
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addUUIDFast(_PREHASH_ObjectID, object->getID());
+ llinfos << "Object: " << object->getID() << llendl;
+ }
+ msg->sendReliable(gAgent.getRegion()->getHost());
+
+ gExportDialog = LLUploadDialog::modalUploadDialog("Exporting selected objects...");
+}
+
+BOOL menu_check_build_tool( void* user_data )
+{
+ S32 index = (S32) user_data;
+ return gCurrentToolset->isToolSelected( index );
+}
+
+void handle_reload_settings(void*)
+{
+ gSavedSettings.resetToDefaults();
+ gSavedSettings.loadFromFile(gSettingsFileName, TRUE);
+
+ llinfos << "Loading colors from colors.xml" << llendl;
+ std::string color_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"colors.xml");
+ gColors.resetToDefaults();
+ gColors.loadFromFile(color_file, FALSE, TYPE_COL4U);
+}
+
+class LLWorldSetHomeLocation : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // we just send the message and let the server check for failure cases
+ // server will echo back a "Home position set." alert if it succeeds
+ // and the home location screencapture happens when that alert is recieved
+ gAgent.setStartPosition(START_LOCATION_ID_HOME);
+ return true;
+ }
+};
+
+class LLWorldTeleportHome : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gAgent.teleportHome();
+ return true;
+ }
+};
+
+class LLWorldAlwaysRun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getAlwaysRun())
+ {
+ gAgent.clearAlwaysRun();
+ }
+ else
+ {
+ gAgent.setAlwaysRun();
+ }
+ LLMessageSystem *msg = gMessageSystem;
+
+
+ msg->newMessageFast(_PREHASH_SetAlwaysRun);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_AlwaysRun, gAgent.getAlwaysRun() );
+ gAgent.sendReliableMessage();
+ return true;
+ }
+};
+
+class LLWorldCheckAlwaysRun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.getAlwaysRun();
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldSetAway : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getAFK())
+ {
+ gAgent.clearAFK();
+ }
+ else
+ {
+ gAgent.setAFK();
+ }
+ return true;
+ }
+};
+
+class LLWorldSetBusy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getBusy())
+ {
+ gAgent.clearBusy();
+ }
+ else
+ {
+ gAgent.setBusy();
+ gViewerWindow->alertXml("BusyModeSet");
+ }
+ return true;
+ }
+};
+
+
+class LLWorldCreateLandmark : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerRegion* agent_region = gAgent.getRegion();
+ if(!agent_region)
+ {
+ llwarns << "No agent region" << llendl;
+ return true;
+ }
+ LLParcel* agent_parcel = gParcelMgr->getAgentParcel();
+ if (!agent_parcel)
+ {
+ llwarns << "No agent parcel" << llendl;
+ return true;
+ }
+ if (!agent_parcel->getAllowLandmark()
+ && !LLViewerParcelMgr::isParcelOwnedByAgent(agent_parcel, GP_LAND_ALLOW_LANDMARK))
+ {
+ gViewerWindow->alertXml("CannotCreateLandmarkNotOwner");
+ return true;
+ }
+
+ LLUUID folder_id;
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ std::string pos_string;
+ gAgent.buildLocationString(pos_string);
+
+ create_inventory_item(gAgent.getID(), gAgent.getSessionID(),
+ folder_id, LLTransactionID::tnull,
+ pos_string, pos_string, // name, desc
+ LLAssetType::AT_LANDMARK,
+ LLInventoryType::IT_LANDMARK,
+ NOT_WEARABLE, PERM_ALL,
+ NULL);
+ return true;
+ }
+};
+
+class LLToolsLookAtSelection : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ const F32 PADDING_FACTOR = 2.f;
+ BOOL zoom = (userdata.asString() == "zoom");
+ if (!gSelectMgr->isEmpty())
+ {
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+
+ LLBBox selection_bbox = gSelectMgr->getBBoxOfSelection();
+ F32 angle_of_view = llmax(0.1f, gCamera->getAspect() > 1.f ? gCamera->getView() * gCamera->getAspect() : gCamera->getView());
+ F32 distance = selection_bbox.getExtentLocal().magVec() * PADDING_FACTOR / atan(angle_of_view);
+
+ LLVector3 obj_to_cam = gCamera->getOrigin() - selection_bbox.getCenterAgent();
+ obj_to_cam.normVec();
+
+ if (zoom)
+ {
+ gAgent.setCameraPosAndFocusGlobal(gSelectMgr->getSelectionCenterGlobal() + LLVector3d(obj_to_cam * distance), gSelectMgr->getSelectionCenterGlobal(), gSelectMgr->getFirstObject()->mID );
+ }
+ else
+ {
+ gAgent.setFocusGlobal( gSelectMgr->getSelectionCenterGlobal(), gSelectMgr->getFirstObject()->mID );
+ }
+ }
+ return true;
+ }
+};
+
+/*
+void handle_reset_rotation(void*)
+{
+ gSelectMgr->selectionResetRotation();
+
+ dialog_refresh_all();
+}
+*/
+
+class LLAvatarAddFriend : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if(avatar && !is_agent_friend(avatar->getID()))
+ {
+ request_friendship(avatar->getID());
+ }
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+void complete_give_money(S32 option, void* user_data)
+{
+ if (option == 0)
+ {
+ gAgent.clearBusy();
+ }
+
+ LLUUID* object_id = (LLUUID*)user_data;
+
+ LLViewerObject* object = gObjectList.findObject(*object_id);
+ if (object)
+ {
+ if (object->isAvatar())
+ {
+ const BOOL is_group = FALSE;
+ LLFloaterPay::payDirectly(&give_money,
+ *object_id,
+ is_group);
+ }
+ else
+ {
+ LLFloaterPay::payViaObject(&give_money, *object_id);
+ }
+ }
+
+ delete object_id;
+}
+
+bool handle_give_money_dialog()
+{
+ LLViewerObject *objectp = gViewerWindow->lastObjectHit();
+ LLUUID* object_id = new LLUUID();
+
+ // Show avatar's name if paying attachment
+ if (objectp && objectp->isAttachment())
+ {
+ while (objectp && !objectp->isAvatar())
+ {
+ objectp = (LLViewerObject*)objectp->getParent();
+ }
+ }
+
+ if (objectp)
+ {
+ *object_id = objectp->getID();
+ }
+
+ if (gAgent.getBusy())
+ {
+ // warn users of being in busy mode during a transaction
+ gViewerWindow->alertXml("BusyModePay", complete_give_money, object_id);
+ }
+ else
+ {
+ complete_give_money(1, object_id);
+ }
+ return true;
+}
+
+class LLPayObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ return handle_give_money_dialog();
+ }
+};
+
+class LLEnablePayObject : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object(gViewerWindow->lastObjectHit());
+ bool new_value = (avatar != NULL);
+ if (!new_value)
+ {
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if( object )
+ {
+ LLViewerObject *parent = (LLViewerObject *)object->getParent();
+ if((object->flagTakesMoney()) || (parent && parent->flagTakesMoney()))
+ {
+ new_value = true;
+ }
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLObjectEnableSitOrStand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ LLViewerObject* dest_object = NULL;
+ if(dest_object = gObjectList.findObject(gLastHitObjectID))
+ {
+ if(dest_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ new_value = true;
+ }
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+
+ // HACK: Update label
+ LLString label;
+ LLString sit_text;
+ LLString stand_text;
+ LLString param = userdata["data"].asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ sit_text = param.substr(0, offset);
+ stand_text = param.substr(offset+1);
+ }
+ if (sitting_on_selection())
+ {
+ label = stand_text;
+ }
+ else
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if (node && node->mValid && !node->mSitName.empty())
+ {
+ label.assign(node->mSitName);
+ }
+ else
+ {
+ label = sit_text;
+ }
+ }
+ gMenuHolder->childSetText("Object Sit", label);
+
+ return true;
+ }
+};
+
+void edit_ui(void*)
+{
+ LLFloater::setEditModeEnabled(!LLFloater::getEditModeEnabled());
+}
+
+void dump_select_mgr(void*)
+{
+ gSelectMgr->dump();
+}
+
+void dump_volume_mgr(void*)
+{
+ gVolumeMgr->dump();
+}
+
+void dump_inventory(void*)
+{
+ gInventory.dumpInventory();
+}
+
+
+void handle_first_tool(void*)
+{
+ gCurrentToolset->selectFirstTool();
+}
+
+
+void handle_next_tool(void*)
+{
+ gCurrentToolset->selectNextTool();
+}
+
+
+void handle_previous_tool(void*)
+{
+ gCurrentToolset->selectPrevTool();
+}
+
+
+// forcibly unlock an object
+void handle_force_unlock(void*)
+{
+ // First, make it public.
+ gSelectMgr->sendOwner(LLUUID::null, LLUUID::null, TRUE);
+
+ // Second, lie to the viewer and mark it editable and unowned
+ LLViewerObject* object;
+ for (object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ object->mFlags |= FLAGS_OBJECT_MOVE;
+ object->mFlags |= FLAGS_OBJECT_MODIFY;
+ object->mFlags |= FLAGS_OBJECT_COPY;
+
+ object->mFlags &= ~FLAGS_OBJECT_ANY_OWNER;
+ object->mFlags &= ~FLAGS_OBJECT_YOU_OWNER;
+ }
+}
+
+// Fullscreen debug stuff
+void handle_fullscreen_debug(void*)
+{
+ llinfos << "Width " << gViewerWindow->getWindowWidth() << " Height " << gViewerWindow->getWindowHeight() << llendl;
+ llinfos << "mouse_x_from_center(100) " << mouse_x_from_center(100) << " y " << mouse_y_from_center(100) << llendl;
+}
+
+void handle_crash(void*)
+{
+ llerrs << "This is an llerror" << llendl;
+}
+
+class LLWorldForceSun : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString tod = userdata.asString();
+ LLVector3 sun_direction;
+ if (tod == "sunrise")
+ {
+ sun_direction.setVec(1.0f, 0.f, 0.2f);
+ }
+ else if (tod == "noon")
+ {
+ sun_direction.setVec(0.0f, 0.3f, 1.0f);
+ }
+ else if (tod == "sunset")
+ {
+ sun_direction.setVec(-1.0f, 0.f, 0.2f);
+ }
+ else if (tod == "midnight")
+ {
+ sun_direction.setVec(0.0f, 0.3f, -1.0f);
+ }
+ else
+ {
+ gSky.setOverrideSun(FALSE);
+ return true;
+ }
+ sun_direction.normVec();
+ gSky.setOverrideSun(TRUE);
+ gSky.setSunDirection( sun_direction, LLVector3(0.f, 0.f, 0.f));
+ return true;
+ }
+};
+
+void handle_dump_followcam(void*)
+{
+ LLFollowCamMgr::dump();
+}
+
+void handle_viewer_enable_circuit_log(void*)
+{
+ llinfos << "Showing circuit information every " << gMessageSystem->mCircuitPrintFreq << " seconds" << llendl;
+ gErrorStream.setLevel( LLErrorStream::DEBUG );
+ gErrorStream.setDebugFlag( LLERR_CIRCUIT_INFO );
+ // and dump stuff out immediately
+ gMessageSystem->dumpCircuitInfo();
+}
+
+void handle_viewer_disable_circuit_log(void*)
+{
+ llinfos << "Hiding circuit information" << llendl;
+#if !LL_DEBUG
+ gErrorStream.setLevel( LLErrorStream::INFO );
+#endif
+ gErrorStream.clearDebugFlag( LLERR_CIRCUIT_INFO );
+}
+
+void handle_viewer_enable_message_log(void*)
+{
+ gMessageSystem->startLogging();
+}
+
+void handle_viewer_disable_message_log(void*)
+{
+ gMessageSystem->stopLogging();
+}
+
+// TomY TODO: Move!
+class LLShowFloater : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString floater_name = userdata.asString();
+ if (floater_name == "gestures")
+ {
+ LLFloaterGesture::toggleVisibility();
+ }
+ else if (floater_name == "appearance")
+ {
+ if (gAgent.getWearablesLoaded())
+ {
+ gAgent.changeCameraToCustomizeAvatar();
+ }
+ }
+ else if (floater_name == "friends")
+ {
+ LLFloaterFriends::toggle(NULL);
+ }
+ else if (floater_name == "preferences")
+ {
+ LLFloaterPreference::show(NULL);
+ }
+ else if (floater_name == "toolbar")
+ {
+ LLToolBar::toggle(NULL);
+ }
+ else if (floater_name == "chat history")
+ {
+ LLFloaterChat::toggle(NULL);
+ }
+ else if (floater_name == "im")
+ {
+ LLToolBar::onClickIM(NULL);
+ }
+ else if (floater_name == "inventory")
+ {
+ LLInventoryView::toggleVisibility(NULL);
+ }
+ else if (floater_name == "mute list")
+ {
+ LLFloaterMute::toggle(NULL);
+ }
+ else if (floater_name == "camera controls")
+ {
+ LLFloaterCamera::toggle(NULL);
+ }
+ else if (floater_name == "movement controls")
+ {
+ LLFloaterMove::show(NULL);
+ }
+ else if (floater_name == "world map")
+ {
+ LLFloaterWorldMap::toggle(NULL);
+ }
+ else if (floater_name == "mini map")
+ {
+ LLFloaterMap::toggle(NULL);
+ }
+ else if (floater_name == "stat bar")
+ {
+ gDebugView->mStatViewp->setVisible(!gDebugView->mStatViewp->getVisible());
+ }
+ else if (floater_name == "account history")
+ {
+ LLFloaterAccountHistory::show(NULL);
+ }
+ else if (floater_name == "my land")
+ {
+ LLFloaterLandHoldings::show(NULL);
+ }
+ else if (floater_name == "about land")
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ }
+
+ LLFloaterLand::show();
+ }
+ else if (floater_name == "buy land")
+ {
+ if (gParcelMgr->selectionEmpty())
+ {
+ gParcelMgr->selectParcelAt(gAgent.getPositionGlobal());
+ }
+
+ gParcelMgr->startBuyLand();
+ }
+ else if (floater_name == "about region")
+ {
+ LLFloaterRegionInfo::show((void *)NULL);
+ }
+ else if (floater_name == "grid options")
+ {
+ LLFloaterBuildOptions::show(NULL);
+ }
+ else if (floater_name == "script errors")
+ {
+ LLFloaterScriptDebug::show(LLUUID::null);
+ }
+ else if (floater_name == "help")
+ {
+#if LL_LIBXUL_ENABLED
+ LLHtmlHelp::show(NULL);
+#endif
+ }
+ else if (floater_name == "complaint reporter")
+ {
+ // Prevent menu from appearing in screen shot.
+ gMenuHolder->hideMenus();
+ LLFloaterReporter::showFromMenu(COMPLAINT_REPORT);
+ }
+ else if (floater_name == "mean events")
+ {
+ if (!gNoRender)
+ {
+ LLFloaterBump::show(NULL);
+ }
+ }
+ else if (floater_name == "bug reporter")
+ {
+ // Prevent menu from appearing in screen shot.
+ gMenuHolder->hideMenus();
+ LLFloaterReporter::showFromMenu(BUG_REPORT);
+ }
+ else if (floater_name == "buy currency")
+ {
+ LLFloaterBuyCurrency::buyCurrency();
+ }
+ else if (floater_name == "about")
+ {
+ LLFloaterAbout::show(NULL);
+ }
+ return true;
+ }
+};
+
+class LLFloaterVisible : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata["control"].asString();
+ LLString floater_name = userdata["data"].asString();
+ bool new_value = false;
+ if (floater_name == "friends")
+ {
+ new_value = LLFloaterFriends::visible(NULL);
+ }
+ else if (floater_name == "toolbar")
+ {
+ new_value = LLToolBar::visible(NULL);
+ }
+ else if (floater_name == "chat history")
+ {
+ new_value = LLFloaterChat::visible(NULL);
+ }
+ else if (floater_name == "im")
+ {
+ new_value = gIMView && gIMView->mTalkFloater && gIMView->mTalkFloater->getVisible();
+ }
+ else if (floater_name == "mute list")
+ {
+ new_value = LLFloaterMute::visible(NULL);
+ }
+ else if (floater_name == "camera controls")
+ {
+ new_value = LLFloaterCamera::visible(NULL);
+ }
+ else if (floater_name == "movement controls")
+ {
+ new_value = LLFloaterMove::visible(NULL);
+ }
+ else if (floater_name == "stat bar")
+ {
+ new_value = gDebugView->mStatViewp->getVisible();
+ }
+ gMenuHolder->findControl(control_name)->setValue(new_value);
+ return true;
+ }
+};
+
+void callback_show_url(S32 option, void* url)
+{
+ const char* urlp = (const char*)url;
+ if (0 == option)
+ {
+ LLWeb::loadURL(urlp);
+ }
+ delete urlp;
+}
+
+class LLPromptShowURL : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString param = userdata.asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ LLString alert = param.substr(0, offset);
+ LLString url = param.substr(offset+1);
+ char *url_copy = new char[url.size()+1];
+ strcpy(url_copy, url.c_str());
+ gViewerWindow->alertXml(alert, callback_show_url, url_copy);
+ }
+ else
+ {
+ llinfos << "PromptShowURL invalid parameters! Expecting \"ALERT,URL\"." << llendl;
+ }
+ return true;
+ }
+};
+
+void callback_show_file(S32 option, void* filename)
+{
+ const char* filenamep = (const char*)filename;
+ if (0 == option)
+ {
+ load_url_local_file(filenamep);
+ }
+ delete filenamep;
+}
+
+class LLPromptShowFile : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString param = userdata.asString();
+ LLString::size_type offset = param.find(",");
+ if (offset != param.npos)
+ {
+ LLString alert = param.substr(0, offset);
+ LLString file = param.substr(offset+1);
+ char *file_copy = new char[file.size()+1];
+ strcpy(file_copy, file.c_str());
+ gViewerWindow->alertXml(alert, callback_show_file, file_copy);
+ }
+ else
+ {
+ llinfos << "PromptShowFile invalid parameters! Expecting \"ALERT,FILE\"." << llendl;
+ }
+ return true;
+ }
+};
+
+class LLShowAgentProfile : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID agent_id;
+ if (userdata.asString() == "agent")
+ {
+ agent_id = gAgent.getID();
+ }
+ else if (userdata.asString() == "hit object")
+ {
+ agent_id = gLastHitObjectID;
+ }
+ else
+ {
+ agent_id = userdata.asUUID();
+ }
+
+ LLVOAvatar* avatar = find_avatar_from_object(agent_id);
+ if (avatar)
+ {
+ LLFloaterAvatarInfo::showFromAvatar(avatar);
+ }
+ return true;
+ }
+};
+
+class LLShowAgentGroups : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLUUID agent_id;
+ if (userdata.asString() == "agent")
+ {
+ agent_id = gAgent.getID();
+ }
+ else
+ {
+ agent_id = userdata.asUUID();
+ }
+ if(agent_id.notNull())
+ {
+ LLFloaterGroups::show(agent_id, LLFloaterGroups::AGENT_GROUPS);
+ }
+ return true;
+ }
+};
+
+void handle_focus(void *)
+{
+ if (gDisconnected)
+ {
+ return;
+ }
+
+ if (gAgent.getFocusOnAvatar())
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gAgent.cameraZoomIn(0.666f);
+ }
+ else
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ // Switch to camera toolset
+// gCurrentToolset = gCameraToolset;
+ gCurrentToolset->selectTool( gToolCamera );
+}
+
+class LLLandEdit : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ if (gAgent.getFocusOnAvatar() && gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+
+ gAgent.cameraOrbitOver( F_PI * 0.25f );
+ gViewerWindow->moveCursorToCenter();
+ }
+ else if ( gSavedSettings.getBOOL("EditCameraMovement") )
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ gViewerWindow->moveCursorToCenter();
+ }
+
+
+ gParcelMgr->selectParcelAt( gLastHitPosGlobal );
+
+ gFloaterTools->showMore(TRUE);
+ gFloaterView->bringToFront( gFloaterTools );
+
+ // Switch to land edit toolset
+ gCurrentToolset->selectTool( gToolParcel );
+ return true;
+ }
+};
+
+class LLWorldEnableBuyLand : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gParcelMgr->canAgentBuyParcel(
+ gParcelMgr->selectionEmpty()
+ ? gParcelMgr->getAgentParcel()
+ : gParcelMgr->getSelectedParcel(),
+ false);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_buy_land(void*)
+{
+ return gParcelMgr->canAgentBuyParcel(
+ gParcelMgr->getSelectedParcel(), false);
+}
+
+
+void handle_move(void*)
+{
+ if (gAgent.getFocusOnAvatar())
+ {
+ // zoom in if we're looking at the avatar
+ gAgent.setFocusOnAvatar(FALSE, ANIMATE);
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+
+ gAgent.cameraZoomIn(0.666f);
+ }
+ else
+ {
+ gAgent.setFocusGlobal(gLastHitPosGlobal + gLastHitObjectOffset, gLastHitObjectID);
+ }
+
+ gViewerWindow->moveCursorToCenter();
+
+ gCurrentToolset = gBasicToolset;
+ gCurrentToolset->selectTool( gToolGrab );
+}
+
+
+void near_attach_object(BOOL success, void *user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ U8 attachment_id;
+ if (attachment)
+ {
+ attachment_id = gAgent.getAvatarObject()->mAttachmentPoints.reverseLookup(attachment);
+ }
+ else
+ {
+ // interpret 0 as "default location"
+ attachment_id = 0;
+ }
+
+ gSelectMgr->sendAttach(attachment_id);
+ gSelectMgr->deselectTransient();
+}
+
+class LLObjectAttachToAvatar : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ confirm_replace_attachment(0, NULL);
+ }
+ return true;
+ }
+};
+
+// move this somewhere global
+void handle_attach_to_avatar(void* user_data)
+{
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ if (attachment && attachment->getObject(0))
+ {
+ gViewerWindow->alertXml("ReplaceAttachment", confirm_replace_attachment, user_data);
+ }
+ else
+ {
+ confirm_replace_attachment(0, user_data);
+ }
+ }
+}
+void confirm_replace_attachment(S32 option, void* user_data)
+{
+ if (option == 0/*YES*/)
+ {
+ LLViewerObject* selectedObject = gSelectMgr->getFirstRootObject();
+ if (selectedObject)
+ {
+ const F32 MIN_STOP_DISTANCE = 1.f; // meters
+ const F32 ARM_LENGTH = 0.5f; // meters
+ const F32 SCALE_FUDGE = 1.5f;
+
+ F32 stop_distance = SCALE_FUDGE * selectedObject->getMaxScale() + ARM_LENGTH;
+ if (stop_distance < MIN_STOP_DISTANCE)
+ {
+ stop_distance = MIN_STOP_DISTANCE;
+ }
+
+ LLVector3 walkToSpot = selectedObject->getPositionAgent();
+
+ // make sure we stop in front of the object
+ LLVector3 delta = walkToSpot - gAgent.getPositionAgent();
+ delta.normVec();
+ delta = delta * 0.5f;
+ walkToSpot -= delta;
+
+ gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(walkToSpot), "Attach", NULL, near_attach_object, user_data, stop_distance);
+ gAgent.clearFocusObject();
+ }
+ }
+}
+
+class LLAttachmentDrop : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Called when the user clicked on an object attached to them
+ // and selected "Drop".
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (!object)
+ {
+ llwarns << "handle_drop_attachment() - no object to drop" << llendl;
+ return true;
+ }
+
+ LLViewerObject *parent = (LLViewerObject*)object->getParent();
+ while (parent)
+ {
+ if(parent->isAvatar())
+ {
+ break;
+ }
+ object = parent;
+ parent = (LLViewerObject*)parent->getParent();
+ }
+
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ if (object->isAvatar())
+ {
+ llwarns << "Trying to detach avatar from avatar." << llendl;
+ return true;
+ }
+
+ // The sendDropAttachment() method works on the list of selected
+ // objects. Thus we need to clear the list, make sure it only
+ // contains the object the user clicked, send the message,
+ // then clear the list.
+ // We use deselectAll to update the simulator's notion of what's
+ // selected, and removeAll just to change things locally.
+ //gSelectMgr->deselectAll();
+ //gSelectMgr->selectObjectAndFamily(object);
+ gSelectMgr->sendDropAttachment();
+ gSelectMgr->deselectTransient();
+ return true;
+ }
+};
+
+// called from avatar pie menu
+void handle_detach_from_avatar(void* user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ LLViewerObject* attached_object = attachment->getObject(0);
+
+ if (attached_object)
+ {
+ gMessageSystem->newMessage("ObjectDetach");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, attached_object->getLocalID());
+ gMessageSystem->sendReliable( gAgent.getRegionHost() );
+ }
+}
+
+void attach_label(LLString& label, void* user_data)
+{
+ LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data;
+ if (attachmentp)
+ {
+ label = attachmentp->getName();
+ if (attachmentp->getObject(0))
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
+ if (itemp)
+ {
+ label += LLString(" (") + itemp->getName() + LLString(")");
+ }
+ }
+ }
+}
+
+void detach_label(LLString& label, void* user_data)
+{
+ LLViewerJointAttachment* attachmentp = (LLViewerJointAttachment*)user_data;
+ if (attachmentp)
+ {
+ label = attachmentp->getName();
+ if (attachmentp->getObject(0))
+ {
+ LLViewerInventoryItem* itemp = gInventory.getItem(attachmentp->getItemID());
+ if (itemp)
+ {
+ label += LLString(" (") + itemp->getName() + LLString(")");
+ }
+ }
+ }
+}
+
+
+class LLAttachmentDetach : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // Called when the user clicked on an object attached to them
+ // and selected "Detach".
+ LLViewerObject *object = gViewerWindow->lastObjectHit();
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ LLViewerObject *parent = (LLViewerObject*)object->getParent();
+ while (parent)
+ {
+ if(parent->isAvatar())
+ {
+ break;
+ }
+ object = parent;
+ parent = (LLViewerObject*)parent->getParent();
+ }
+
+ if (!object)
+ {
+ llwarns << "handle_detach() - no object to detach" << llendl;
+ return true;
+ }
+
+ if (object->isAvatar())
+ {
+ llwarns << "Trying to detach avatar from avatar." << llendl;
+ return true;
+ }
+
+ // The sendDetach() method works on the list of selected
+ // objects. Thus we need to clear the list, make sure it only
+ // contains the object the user clicked, send the message,
+ // then clear the list.
+ // We use deselectAll to update the simulator's notion of what's
+ // selected, and removeAll just to change things locally.
+ //RN: I thought it was more useful to detach everything that was selected
+ if (gSelectMgr->selectionIsAttachment())
+ {
+ gSelectMgr->sendDetach();
+ gSelectMgr->deselectAll();
+ }
+ return true;
+ }
+};
+
+//Adding an observer for a Jira 2422 and needs to be a fetch observer
+//for Jira 3119
+class LLWornItemFetchedObserver : public LLInventoryFetchObserver
+{
+public:
+ LLWornItemFetchedObserver() {}
+ virtual ~LLWornItemFetchedObserver() {}
+
+protected:
+ virtual void done()
+ {
+ gPieAttachment->buildDrawLabels();
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// You can only drop items on parcels where you can build.
+class LLAttachmentEnableDrop : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLParcel* parcel = gParcelMgr->getAgentParcel();
+ BOOL can_build = gAgent.isGodlike() || (parcel && parcel->getAllowModify());
+
+ //Add an inventory observer to only allow dropping the newly attached item
+ //once it exists in your inventory. Look at Jira 2422.
+ //-jwolk
+
+ // A bug occurs when you wear/drop an item before it actively is added to your inventory
+ // if this is the case (you're on a slow sim, etc.) a copy of the object,
+ // well, a newly created object with the same properties, is placed
+ // in your inventory. Therefore, we disable the drop option until the
+ // item is in your inventory
+
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ LLViewerJointAttachment* attachment_pt = NULL;
+ LLInventoryItem* item = NULL;
+
+ if ( object )
+ {
+ S32 attachmentID = ATTACHMENT_ID_FROM_STATE(object->getState());
+ attachment_pt = gAgent.getAvatarObject()->mAttachmentPoints.getIfThere(attachmentID);
+
+ if ( attachment_pt )
+ {
+ // make sure item is in your inventory (it could be a delayed attach message being sent from the sim)
+ // so check to see if the item is in the inventory already
+ item = gInventory.getItem(attachment_pt->getItemID());
+
+ if ( !item )
+ {
+ // Item does not exist, make an observer to enable the pie menu
+ // when the item finishes fetching worst case scenario
+ // if a fetch is already out there (being sent from a slow sim)
+ // we refetch and there are 2 fetches
+ LLWornItemFetchedObserver* wornItemFetched = new LLWornItemFetchedObserver();
+ LLInventoryFetchObserver::item_ref_t items; //add item to the inventory item to be fetched
+
+ items.push_back(attachment_pt->getItemID());
+
+ wornItemFetched->fetchItems(items);
+ gInventory.addObserver(wornItemFetched);
+ }
+ }
+ }
+
+ //now check to make sure that the item is actually in the inventory before we enable dropping it
+ bool new_value = enable_detach(NULL) && can_build && item;
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_detach(void*)
+{
+ LLViewerObject* object = gViewerWindow->lastObjectHit();
+ if (!object) return FALSE;
+ if (!object->isAttachment()) return FALSE;
+
+ // Find the avatar who owns this attachment
+ LLViewerObject* avatar = object;
+ while (avatar)
+ {
+ // ...if it's you, good to detach
+ if (avatar->getID() == gAgent.getID())
+ {
+ return TRUE;
+ }
+
+ avatar = (LLViewerObject*)avatar->getParent();
+ }
+
+ return FALSE;
+}
+
+class LLAttachmentEnableDetach : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_detach(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// Used to tell if the selected object can be attached to your avatar.
+BOOL object_selected_and_point_valid(void *user_data)
+{
+ //LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ for (LLViewerObject *object = gSelectMgr->getFirstRootObject(); object; object = gSelectMgr->getNextRootObject())
+ {
+ for (U32 child_num = 0; child_num < object->mChildList.size(); child_num++ )
+ {
+ if (object->mChildList[child_num]->isAvatar())
+ {
+ return FALSE;
+ }
+ }
+ }
+
+ return ((gSelectMgr != NULL) &&
+ (gSelectMgr->getRootObjectCount() == 1) &&
+ (gSelectMgr->getFirstRootObject()->getPCode() == LL_PCODE_VOLUME) &&
+ gSelectMgr->getFirstRootObject()->permYouOwner() &&
+ !((LLViewerObject*)gSelectMgr->getFirstRootObject()->getRoot())->isAvatar() &&
+ (gSelectMgr->getFirstRootObject()->getNVPair("AssetContainer") == NULL));
+}
+
+// Also for seeing if object can be attached. See above.
+class LLObjectEnableWear : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gSelectMgr)
+ {
+ LLViewerObject* first_root = gSelectMgr->getFirstRootObject();
+ if (first_root)
+ {
+ new_value = gSelectMgr->getRootObjectCount() == 1
+ && first_root->getPCode() == LL_PCODE_VOLUME
+ && first_root->permYouOwner()
+ && !((LLViewerObject*)gSelectMgr->getFirstRootObject()->getRoot())->isAvatar()
+ && (first_root->getNVPair("AssetContainer") == NULL);
+ }
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+
+BOOL object_attached(void *user_data)
+{
+ LLViewerJointAttachment *attachment = (LLViewerJointAttachment *)user_data;
+
+ return attachment->getObject(0) != NULL;
+}
+
+class LLAvatarSendIM : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLVOAvatar* avatar = find_avatar_from_object( gViewerWindow->lastObjectHit() );
+ if(avatar)
+ {
+ LLString name("IM");
+ LLNameValue *first = avatar->getNVPair("FirstName");
+ LLNameValue *last = avatar->getNVPair("LastName");
+ if (first && last)
+ {
+ name.assign( first->getString() );
+ name.append(" ");
+ name.append( last->getString() );
+ }
+
+ gIMView->setFloaterOpen(TRUE);
+ //EInstantMessage type = have_agent_callingcard(gLastHitObjectID)
+ // ? IM_SESSION_ADD : IM_SESSION_CARDLESS_START;
+ gIMView->addSession(name,
+ IM_NOTHING_SPECIAL,
+ avatar->getID());
+ }
+ return true;
+ }
+};
+
+
+void handle_activate(void*)
+{
+}
+
+BOOL enable_activate(void*)
+{
+ return FALSE;
+}
+
+class LLToolsSelectedScriptAction : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString action = userdata.asString();
+ LLFloaterScriptQueue *queue = NULL;
+ if (action == "compile")
+ {
+ queue = LLFloaterCompileQueue::create();
+ }
+ else if (action == "reset")
+ {
+ queue = LLFloaterResetQueue::create();
+ }
+ else if (action == "start")
+ {
+ queue = LLFloaterRunQueue::create();
+ }
+ else if (action == "stop")
+ {
+ queue = LLFloaterNotRunQueue::create();
+ }
+ if (!queue) return true;
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotRecompileSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotRecompileSelectObjectsNoPermission");
+ }
+ }
+ return true;
+ }
+};
+
+void handle_reset_selection(void*)
+{
+ LLFloaterResetQueue* queue = LLFloaterResetQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotResetSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotResetSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_set_run_selection(void*)
+{
+ LLFloaterRunQueue* queue = LLFloaterRunQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotSetRunningSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotSerRunningSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_set_not_run_selection(void*)
+{
+ LLFloaterNotRunQueue* queue = LLFloaterNotRunQueue::create();
+
+ BOOL scripted = FALSE;
+ BOOL modifiable = FALSE;
+
+ for(LLViewerObject* obj = gSelectMgr->getFirstObject();
+ obj;
+ obj = gSelectMgr->getNextObject())
+ {
+ scripted = obj->flagScripted();
+ modifiable = obj->permModify();
+
+ if( scripted && modifiable )
+ queue->addObject(obj->getID());
+ else
+ break;
+ }
+
+ if(!queue->start())
+ {
+ if ( ! scripted )
+ {
+ gViewerWindow->alertXml("CannotSetRunningNotSelectObjectsNoScripts");
+ }
+ else if ( ! modifiable )
+ {
+ gViewerWindow->alertXml("CannotSerRunningNotSelectObjectsNoPermission");
+ }
+ }
+}
+
+void handle_selected_texture_info(void*)
+{
+ LLSelectNode* node = NULL;
+ for (node = gSelectMgr->getFirstNode(); node != NULL; node = gSelectMgr->getNextNode())
+ {
+ if (!node->mValid) continue;
+
+ std::string msg;
+ msg.assign("Texture info for: ");
+ msg.append(node->mName);
+ LLChat chat(msg);
+ LLFloaterChat::addChat(chat);
+
+ U8 te_count = node->getObject()->getNumTEs();
+ // map from texture ID to list of faces using it
+ typedef std::map< LLUUID, std::vector<U8> > map_t;
+ map_t faces_per_texture;
+ for (U8 i = 0; i < te_count; i++)
+ {
+ if (!node->isTESelected(i)) continue;
+
+ LLViewerImage* img = node->getObject()->getTEImage(i);
+ LLUUID image_id = img->getID();
+ faces_per_texture[image_id].push_back(i);
+ }
+ // Per-texture, dump which faces are using it.
+ map_t::iterator it;
+ for (it = faces_per_texture.begin(); it != faces_per_texture.end(); ++it)
+ {
+ LLUUID image_id = it->first;
+ U8 te = it->second[0];
+ LLViewerImage* img = node->getObject()->getTEImage(te);
+ S32 height = img->getHeight();
+ S32 width = img->getWidth();
+ S32 components = img->getComponents();
+ std::string image_id_string;
+ if (gAgent.isGodlike())
+ {
+ image_id_string = image_id.getString() + " ";
+ }
+ msg = llformat("%s%dx%d %s on face ",
+ image_id_string.c_str(),
+ width,
+ height,
+ (components == 4 ? "alpha" : "opaque"));
+ for (U8 i = 0; i < it->second.size(); ++i)
+ {
+ msg.append( llformat("%d ", (S32)(it->second[i])));
+ }
+ LLChat chat(msg);
+ LLFloaterChat::addChat(chat);
+ }
+ }
+}
+
+void handle_dump_image_list(void*)
+{
+ gImageList.dump();
+}
+
+void handle_test_male(void*)
+{
+ wear_outfit_by_name("Male Shape & Outfit");
+ //gGestureList.requestResetFromServer( TRUE );
+}
+
+void handle_test_female(void*)
+{
+ wear_outfit_by_name("Female Shape & Outfit");
+ //gGestureList.requestResetFromServer( FALSE );
+}
+
+void handle_toggle_pg(void*)
+{
+ if (gAgent.mAccess < SIM_ACCESS_MATURE)
+ {
+ gAgent.mAccess = SIM_ACCESS_MATURE;
+ }
+ else
+ {
+ gAgent.mAccess = SIM_ACCESS_PG;
+ }
+
+ LLFloaterWorldMap::reloadIcons(NULL);
+
+ llinfos << "Access set to " << (S32)gAgent.mAccess << llendl;
+}
+
+void handle_dump_attachments(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar )
+ {
+ llinfos << "NO AVATAR" << llendl;
+ return;
+ }
+
+ for( LLViewerJointAttachment* attachment = avatar->mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = avatar->mAttachmentPoints.getNextData() )
+ {
+ S32 key = avatar->mAttachmentPoints.getCurrentKeyWithoutIncrement();
+ BOOL visible = (attachment->getObject(0) != NULL &&
+ attachment->getObject(0)->mDrawable.notNull() &&
+ !attachment->getObject(0)->mDrawable->isRenderType(0));
+ LLVector3 pos;
+ if (visible) pos = attachment->getObject(0)->mDrawable->getPosition();
+ llinfos << "ATTACHMENT " << key << ": item_id=" << attachment->getItemID()
+ << (attachment->getObject(0) ? " present " : " absent ")
+ << (visible ? "visible " : "invisible ")
+ << " at " << pos
+ << " and " << (visible ? attachment->getObject(0)->getPosition() : LLVector3::zero)
+ << llendl;
+ }
+}
+
+//---------------------------------------------------------------------
+// Callbacks for enabling/disabling items
+//---------------------------------------------------------------------
+
+BOOL menu_ui_enabled(void *user_data)
+{
+ BOOL high_res = gSavedSettings.getBOOL( "HighResSnapshot" );
+ return !high_res;
+}
+
+// TomY TODO DEPRECATE & REMOVE
+void menu_toggle_control( void* user_data )
+{
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ if (LLString(static_cast<char*>(user_data)) == "HighResSnapshot" && !checked)
+ {
+ // High Res Snapshot active, must uncheck RenderUIInSnapshot
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", FALSE );
+ }
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+}
+
+
+// these are used in the gl menus to set control values.
+class LLToggleControl : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata.asString();
+ BOOL checked = gSavedSettings.getBOOL( control_name );
+ if (control_name == "HighResSnapshot" && !checked)
+ {
+ // High Res Snapshot active, must uncheck RenderUIInSnapshot
+ gSavedSettings.setBOOL( "RenderUIInSnapshot", FALSE );
+ }
+ gSavedSettings.setBOOL( control_name, !checked );
+ return true;
+ }
+};
+
+// As above, but can be a callback from a LLCheckboxCtrl
+void check_toggle_control( LLUICtrl *, void* user_data )
+{
+ BOOL checked = gSavedSettings.getBOOL( static_cast<char*>(user_data) );
+ gSavedSettings.setBOOL( static_cast<char*>(user_data), !checked );
+}
+
+BOOL menu_check_control( void* user_data)
+{
+ return gSavedSettings.getBOOL((char*)user_data);
+}
+
+//
+void menu_toggle_variable( void* user_data )
+{
+ BOOL checked = *(BOOL*)user_data;
+ *(BOOL*)user_data = !checked;
+}
+
+BOOL menu_check_variable( void* user_data)
+{
+ return *(BOOL*)user_data;
+}
+
+
+BOOL enable_land_selected( void* )
+{
+ return gParcelMgr && !(gParcelMgr->selectionEmpty());
+}
+
+class LLSomethingSelected : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !(gSelectMgr->isEmpty());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLSomethingSelectedNoHUD : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = !(gSelectMgr->isEmpty()) && !(gSelectMgr->getSelectType() == SELECT_TYPE_HUD);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_more_than_one_selected(void* )
+{
+ return (gSelectMgr->getObjectCount() > 1);
+}
+
+class LLEditableSelected : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = (gSelectMgr->getFirstEditableObject() != NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsEnableTakeCopy : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = false;
+ if (gSelectMgr)
+ {
+ new_value = true;
+#ifndef HACKED_GODLIKE_VIEWER
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (gInProductionGrid || !gAgent.isGodlike())
+# endif
+ {
+ LLViewerObject* obj = gSelectMgr->getFirstRootObject();
+ if(obj)
+ {
+ for( ; obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if(!(obj->permCopy()) || obj->isAttachment())
+ {
+ new_value = false;
+ }
+ }
+ }
+ }
+#endif // HACKED_GODLIKE_VIEWER
+ }
+
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_selection_you_own_all(void*)
+{
+ LLViewerObject *obj;
+ for (obj = gSelectMgr->getFirstRootObject(); obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if (!obj->permYouOwner())
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL enable_selection_you_own_one(void*)
+{
+ LLViewerObject *obj;
+ for (obj = gSelectMgr->getFirstRootObject(); obj; obj = gSelectMgr->getNextRootObject())
+ {
+ if (obj->permYouOwner())
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+class LLHasAsset : public LLInventoryCollectFunctor
+{
+public:
+ LLHasAsset(const LLUUID& id) : mAssetID(id), mHasAsset(FALSE) {}
+ virtual ~LLHasAsset() {}
+ virtual bool operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item);
+ BOOL hasAsset() const { return mHasAsset; }
+
+protected:
+ LLUUID mAssetID;
+ BOOL mHasAsset;
+};
+
+bool LLHasAsset::operator()(LLInventoryCategory* cat,
+ LLInventoryItem* item)
+{
+ if(item && item->getAssetUUID() == mAssetID)
+ {
+ mHasAsset = TRUE;
+ }
+ return FALSE;
+}
+
+BOOL enable_save_into_inventory(void*)
+{
+ if(gSelectMgr)
+ {
+ // find the last root
+ LLSelectNode* last_node = NULL;
+ for(LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ node != NULL;
+ node = gSelectMgr->getNextRootNode())
+ {
+ last_node = node;
+ }
+
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+# endif
+ // check all pre-req's for save into inventory.
+ if(last_node && last_node->mValid && !last_node->mItemID.isNull()
+ && (last_node->mPermissions->getOwner() == gAgent.getID())
+ && (gInventory.getItem(last_node->mItemID) != NULL))
+ {
+ LLViewerObject* obj = last_node->getObject();
+ if( obj && !obj->isAttachment() )
+ {
+ return TRUE;
+ }
+ }
+#endif
+ }
+ return FALSE;
+}
+
+class LLToolsEnableSaveToInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_save_into_inventory(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_save_into_task_inventory(void*)
+{
+ if(gSelectMgr)
+ {
+ LLSelectNode* node = gSelectMgr->getFirstRootNode();
+ if(node && (node->mValid) && (!node->mFromTaskID.isNull()))
+ {
+ // *FIX: check to see if the fromtaskid object exists.
+
+ LLViewerObject* obj = node->getObject();
+ if( obj && !obj->isAttachment() )
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+class LLToolsEnableSaveToObjectInventory : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = enable_save_into_task_inventory(NULL);
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_not_thirdperson(void*)
+{
+ return !gAgent.cameraThirdPerson();
+}
+
+class LLFileEnableUpload : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gStatusBar && gGlobalEconomy && (gStatusBar->getBalance() >= gGlobalEconomy->getPriceUpload());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_export_selected(void *)
+{
+ if (gSelectMgr->isEmpty())
+ {
+ return FALSE;
+ }
+ if (!gExporterRequestID.isNull())
+ {
+ return FALSE;
+ }
+ if (!LLUploadDialog::modalUploadIsFinished())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+class LLViewEnableMouselook : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // You can't go directly from customize avatar to mouselook.
+ // TODO: write code with appropriate dialogs to handle this transition.
+ bool new_value = (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode() && !gSavedSettings.getBOOL("FreezeTime"));
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLToolsEnableToolNotPie : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = ( gToolMgr->getCurrentTool(MASK_NONE) != gToolPie );
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldEnableCreateLandmark : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() ||
+ (gAgent.getRegion() && gAgent.getRegion()->getAllowLandmark());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLWorldEnableSetHomeLocation : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = gAgent.isGodlike() ||
+ (gAgent.getRegion() && gAgent.getRegion()->getAllowSetHome());
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+BOOL enable_region_owner(void*)
+{
+ if(gAgent.getRegion() && gAgent.getRegion()->getOwner() == gAgent.getID())
+ return TRUE;
+ return enable_god_customer_service(NULL);
+}
+
+BOOL enable_god_full(void*)
+{
+ return gAgent.getGodLevel() >= GOD_FULL;
+}
+
+BOOL enable_god_liaison(void*)
+{
+ return gAgent.getGodLevel() >= GOD_LIAISON;
+}
+
+BOOL enable_god_customer_service(void*)
+{
+ return gAgent.getGodLevel() >= GOD_CUSTOMER_SERVICE;
+}
+
+BOOL enable_god_basic(void*)
+{
+ return gAgent.getGodLevel() > GOD_NOT;
+}
+
+void toggle_vbo(void *)
+{
+ gPipeline.mUseVBO = !gPipeline.mUseVBO;
+
+ if (!gPipeline.usingAGP())
+ {
+ return;
+ }
+
+ gPipeline.setUseAGP(FALSE);
+ gPipeline.setUseAGP(TRUE);
+
+ gSavedSettings.setBOOL("RenderUseVBO", gPipeline.mUseVBO);
+}
+
+BOOL check_vbo(void *)
+{
+ return gPipeline.mUseVBO;
+}
+
+#if 0 // 1.9.2
+void toggle_vertex_shaders(void *)
+{
+ BOOL use_shaders = gPipeline.getUseVertexShaders();
+ gPipeline.setUseVertexShaders(use_shaders);
+}
+
+BOOL check_vertex_shaders(void *)
+{
+ return gPipeline.getUseVertexShaders();
+}
+#endif
+
+void toggle_glow(void *)
+{
+ gRenderLightGlows = !gRenderLightGlows;
+
+ gSavedSettings.setBOOL("RenderLightGlows", gRenderLightGlows);
+}
+
+BOOL check_glow(void *)
+{
+ return gRenderLightGlows;
+}
+
+
+void toggle_show_xui_names(void *)
+{
+ BOOL showXUINames = gSavedSettings.getBOOL("ShowXUINames");
+
+ showXUINames = !showXUINames;
+ gSavedSettings.setBOOL("ShowXUINames", showXUINames);
+}
+
+BOOL check_show_xui_names(void *)
+{
+ return gSavedSettings.getBOOL("ShowXUINames");
+}
+
+
+
+void toggle_cull_small(void *)
+{
+// gPipeline.mCullBySize = !gPipeline.mCullBySize;
+//
+// gSavedSettings.setBOOL("RenderCullBySize", gPipeline.mCullBySize);
+}
+
+class LLToolsSelectOnlyMyObjects : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ BOOL cur_val = gSavedSettings.getBOOL("SelectOwnedOnly");
+
+ gSavedSettings.setBOOL("SelectOwnedOnly", ! cur_val );
+
+ return true;
+ }
+};
+
+class LLToolsSelectOnlyMovableObjects : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ BOOL cur_val = gSavedSettings.getBOOL("SelectMovableOnly");
+
+ gSavedSettings.setBOOL("SelectMovableOnly", ! cur_val );
+
+ return true;
+ }
+};
+
+class LLToolsSelectBySurrounding : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLSelectMgr::sRectSelectInclusive = !LLSelectMgr::sRectSelectInclusive;
+
+ gSavedSettings.setBOOL("RectangleSelectInclusive", LLSelectMgr::sRectSelectInclusive);
+ return true;
+ }
+};
+
+class LLToolsShowHiddenSelection : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // TomY TODO Merge these
+ LLSelectMgr::sRenderHiddenSelections = !LLSelectMgr::sRenderHiddenSelections;
+
+ gSavedSettings.setBOOL("RenderHiddenSelections", LLSelectMgr::sRenderHiddenSelections);
+ return true;
+ }
+};
+
+class LLToolsShowSelectionLightRadius : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ // TomY TODO merge these
+ LLSelectMgr::sRenderLightRadius = !LLSelectMgr::sRenderLightRadius;
+
+ gSavedSettings.setBOOL("RenderLightRadius", LLSelectMgr::sRenderLightRadius);
+ return true;
+ }
+};
+
+void reload_personal_settings_overrides(void *)
+{
+ llinfos << "Loading overrides from " << gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml") << llendl;
+
+ gSavedSettings.loadFromFile(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT,"overrides.xml"));
+}
+
+void force_breakpoint(void *)
+{
+#if LL_WINDOWS // Forcing a breakpoint
+ DebugBreak();
+#endif
+}
+
+void reload_vertex_shader(void *)
+{
+ //THIS WOULD BE AN AWESOME PLACE TO RELOAD SHADERS... just a thought - DaveP
+}
+
+void flush_animations(void *)
+{
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->resetAnimations();
+ }
+}
+
+void slow_mo_animations(void*)
+{
+ static BOOL slow_mo = FALSE;
+ if (slow_mo)
+ {
+ gAgent.getAvatarObject()->setAnimTimeFactor(1.f);
+ slow_mo = FALSE;
+ }
+ else
+ {
+ gAgent.getAvatarObject()->setAnimTimeFactor(0.2f);
+ slow_mo = TRUE;
+ }
+}
+
+void handle_dump_avatar_local_textures(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ avatar->dumpLocalTextures();
+ }
+}
+
+void handle_debug_avatar_textures(void*)
+{
+ LLFloaterAvatarTextures::show(gLastHitObjectID);
+}
+
+void handle_grab_texture(void* data)
+{
+ LLVOAvatar::ETextureIndex index = (LLVOAvatar::ETextureIndex) ((U32) data);
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if ( avatar )
+ {
+ const LLUUID& asset_id = avatar->grabLocalTexture(index);
+ llinfos << "Adding baked texture " << asset_id << " to inventory." << llendl;
+ LLAssetType::EType asset_type = LLAssetType::AT_TEXTURE;
+ LLInventoryType::EType inv_type = LLInventoryType::IT_TEXTURE;
+ LLUUID folder_id(gInventory.findCategoryUUIDForType(asset_type));
+ if(folder_id.notNull())
+ {
+ LLString name = "Baked ";
+ switch (index)
+ {
+ case LLVOAvatar::TEX_EYES_BAKED:
+ name.append("Iris");
+ break;
+ case LLVOAvatar::TEX_HEAD_BAKED:
+ name.append("Head");
+ break;
+ case LLVOAvatar::TEX_UPPER_BAKED:
+ name.append("Upper Body");
+ break;
+ case LLVOAvatar::TEX_LOWER_BAKED:
+ name.append("Lower Body");
+ break;
+ case LLVOAvatar::TEX_SKIRT_BAKED:
+ name.append("Skirt");
+ break;
+ default:
+ name.append("Unknown");
+ break;
+ }
+ name.append(" Texture");
+
+ LLUUID item_id;
+ item_id.generate();
+ LLPermissions perm;
+ perm.init(gAgentID,
+ gAgentID,
+ LLUUID::null,
+ LLUUID::null);
+ U32 next_owner_perm = PERM_MOVE | PERM_TRANSFER;
+ perm.initMasks(PERM_ALL,
+ PERM_ALL,
+ PERM_NONE,
+ PERM_NONE,
+ next_owner_perm);
+ S32 creation_date_now = time_corrected();
+ LLPointer<LLViewerInventoryItem> item
+ = new LLViewerInventoryItem(item_id,
+ folder_id,
+ perm,
+ asset_id,
+ asset_type,
+ inv_type,
+ name,
+ "",
+ LLSaleInfo::DEFAULT,
+ LLInventoryItem::II_FLAGS_NONE,
+ creation_date_now);
+
+ item->updateServer(TRUE);
+ gInventory.updateItem(item);
+ gInventory.notifyObservers();
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+
+ // Show the preview panel for textures to let
+ // user know that the image is now in inventory.
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback = gFocusMgr.getFocusCallback();
+
+ view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO);
+ view->getPanel()->openSelected();
+ //LLInventoryView::dumpSelectionInformation((void*)view);
+ // restore keyboard focus
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ }
+ }
+ else
+ {
+ llwarns << "Can't find a folder to put it in" << llendl;
+ }
+ }
+}
+
+BOOL enable_grab_texture(void* data)
+{
+ LLVOAvatar::ETextureIndex index = (LLVOAvatar::ETextureIndex) ((U32) data);
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if ( avatar )
+ {
+ return avatar->canGrabLocalTexture(index);
+ }
+ return FALSE;
+}
+
+// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing.
+// Returns NULL on failure.
+LLVOAvatar* find_avatar_from_object( LLViewerObject* object )
+{
+ if (object)
+ {
+ if( object->isAttachment() )
+ {
+ do
+ {
+ object = (LLViewerObject*) object->getParent();
+ }
+ while( object && !object->isAvatar() );
+ }
+ else
+ if( !object->isAvatar() )
+ {
+ object = NULL;
+ }
+ }
+
+ return (LLVOAvatar*) object;
+}
+
+
+// Returns a pointer to the avatar give the UUID of the avatar OR of an attachment the avatar is wearing.
+// Returns NULL on failure.
+LLVOAvatar* find_avatar_from_object( const LLUUID& object_id )
+{
+ return find_avatar_from_object( gObjectList.findObject(object_id) );
+}
+
+
+void handle_disconnect_viewer(void *)
+{
+ char message[2048];
+ message[0] = '\0';
+
+ sprintf(message, "Testing viewer disconnect");
+
+ do_disconnect(message);
+}
+
+
+class LLToolsUseSelectionForGrid : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gSelectMgr->clearGridObjects();
+
+ for (LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ objectp;
+ objectp = gSelectMgr->getNextRootObject())
+ {
+ gSelectMgr->addGridObject(objectp);
+ }
+ gSelectMgr->setGridMode(GRID_MODE_REF_OBJECT);
+ if (gFloaterTools)
+ {
+ gFloaterTools->mComboGridMode->setCurrentByIndex((S32)GRID_MODE_REF_OBJECT);
+ }
+ return true;
+ }
+};
+
+void handle_test_load_url(void*)
+{
+ LLWeb::loadURL("");
+ LLWeb::loadURL("hacker://www.google.com/");
+ LLWeb::loadURL("http");
+ LLWeb::loadURL("http://www.google.com/");
+}
+
+//
+// LLViewerMenuHolderGL
+//
+
+BOOL LLViewerMenuHolderGL::hideMenus()
+{
+ BOOL handled = LLMenuHolderGL::hideMenus();
+ if (handled)
+ {
+ gSelectMgr->deselectTransient();
+ if (!gFloaterTools->getVisible() && !LLFloaterLand::floaterVisible())
+ {
+ gParcelMgr->deselectLand();
+ }
+ }
+ return handled;
+}
+
+void handle_save_to_xml(void*)
+{
+ LLFloater* frontmost = gFloaterView->getFrontmost();
+ if (!frontmost)
+ {
+ gViewerWindow->alertXml("NoFrontmostFloater");
+ return;
+ }
+
+ LLString default_name = "floater_";
+ default_name += frontmost->getTitle();
+ default_name += ".xml";
+
+ LLString::toLower(default_name);
+ LLString::replaceChar(default_name, ' ', '_');
+ LLString::replaceChar(default_name, '/', '_');
+ LLString::replaceChar(default_name, ':', '_');
+ LLString::replaceChar(default_name, '"', '_');
+
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getSaveFile(LLFilePicker::FFSAVE_XML, default_name.c_str()))
+ {
+ LLString filename = picker.getFirstFile();
+ gUICtrlFactory->saveToXML(frontmost, filename);
+ }
+}
+
+void handle_load_from_xml(void*)
+{
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (picker.getOpenFile(LLFilePicker::FFLOAD_XML))
+ {
+ LLString filename = picker.getFirstFile();
+ LLFloater* floater = new LLFloater("sample_floater");
+ gUICtrlFactory->buildFloater(floater, filename);
+ }
+}
+
+void handle_rebake_textures(void*)
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if (!avatar) return;
+
+ // Slam pending upload count to "unstick" things
+ bool slam_for_debug = true;
+ avatar->forceBakeAllTextures(slam_for_debug);
+}
+
+void toggle_visibility(void* user_data)
+{
+ LLView* viewp = (LLView*)user_data;
+ viewp->setVisible(!viewp->getVisible());
+}
+
+BOOL get_visibility(void* user_data)
+{
+ LLView* viewp = (LLView*)user_data;
+ return viewp->getVisible();
+}
+
+// TomY TODO: Get rid of these?
+class LLViewShowHoverTips : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLHoverView::sShowHoverTips = !LLHoverView::sShowHoverTips;
+ return true;
+ }
+};
+
+class LLViewCheckShowHoverTips : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLHoverView::sShowHoverTips;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// TomY TODO: Get rid of these?
+class LLViewHighlightTransparent : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLDrawPoolAlpha::sShowDebugAlpha = !LLDrawPoolAlpha::sShowDebugAlpha;
+ return true;
+ }
+};
+
+class LLViewCheckHighlightTransparent : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLDrawPoolAlpha::sShowDebugAlpha;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLViewToggleBeacon : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString beacon = userdata.asString();
+ if (beacon == "scripts")
+ {
+ LLPipeline::toggleRenderScriptedBeacons(NULL);
+ }
+ else if (beacon == "physical")
+ {
+ LLPipeline::toggleRenderPhysicalBeacons(NULL);
+ }
+ else if (beacon == "sounds")
+ {
+ LLPipeline::toggleRenderSoundBeacons(NULL);
+ }
+ else if (beacon == "particles")
+ {
+ LLPipeline::toggleRenderParticleBeacons(NULL);
+ }
+ return true;
+ }
+};
+
+class LLViewCheckBeaconEnabled : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString beacon = userdata["data"].asString();
+ bool new_value = false;
+ if (beacon == "scripts")
+ {
+ new_value = LLPipeline::getRenderScriptedBeacons(NULL);
+ }
+ else if (beacon == "physical")
+ {
+ new_value = LLPipeline::getRenderPhysicalBeacons(NULL);
+ }
+ else if (beacon == "sounds")
+ {
+ new_value = LLPipeline::getRenderSoundBeacons(NULL);
+ }
+ else if (beacon == "particles")
+ {
+ new_value = LLPipeline::getRenderParticleBeacons(NULL);
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLViewToggleRenderType : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString type = userdata.asString();
+ if (type == "particles")
+ {
+ LLPipeline::toggleRenderType((void *)(S32)LLPipeline::RENDER_TYPE_PARTICLES);
+ }
+ return true;
+ }
+};
+
+class LLViewCheckRenderType : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString type = userdata["data"].asString();
+ bool new_value = false;
+ if (type == "particles")
+ {
+ new_value = LLPipeline::toggleRenderTypeControlNegated((void *)(S32)LLPipeline::RENDER_TYPE_PARTICLES);
+ }
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+// TomY TODO: Get rid of these?
+class LLViewShowHUDAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLDrawPoolHUD::sShowHUDAttachments = !LLDrawPoolHUD::sShowHUDAttachments;
+ return true;
+ }
+};
+
+class LLViewCheckHUDAttachments : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ bool new_value = LLDrawPoolHUD::sShowHUDAttachments;
+ gMenuHolder->findControl(userdata["control"].asString())->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditEnableTakeOff : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString control_name = userdata["control"].asString();
+ LLString clothing = userdata["data"].asString();
+ bool new_value = false;
+ if (clothing == "shirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SHIRT);
+ }
+ if (clothing == "pants")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_PANTS);
+ }
+ if (clothing == "shoes")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SHOES);
+ }
+ if (clothing == "socks")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SOCKS);
+ }
+ if (clothing == "jacket")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_JACKET);
+ }
+ if (clothing == "gloves")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_GLOVES);
+ }
+ if (clothing == "undershirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_UNDERSHIRT);
+ }
+ if (clothing == "underpants")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_UNDERPANTS);
+ }
+ if (clothing == "skirt")
+ {
+ new_value = LLAgent::selfHasWearable((void *)WT_SKIRT);
+ }
+ gMenuHolder->findControl(control_name)->setValue(new_value);
+ return true;
+ }
+};
+
+class LLEditTakeOff : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString clothing = userdata.asString();
+ if (clothing == "shirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SHIRT);
+ }
+ else if (clothing == "pants")
+ {
+ LLAgent::userRemoveWearable((void*)WT_PANTS);
+ }
+ else if (clothing == "shoes")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SHOES);
+ }
+ else if (clothing == "socks")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SOCKS);
+ }
+ else if (clothing == "jacket")
+ {
+ LLAgent::userRemoveWearable((void*)WT_JACKET);
+ }
+ else if (clothing == "gloves")
+ {
+ LLAgent::userRemoveWearable((void*)WT_GLOVES);
+ }
+ else if (clothing == "undershirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_UNDERSHIRT);
+ }
+ else if (clothing == "underpants")
+ {
+ LLAgent::userRemoveWearable((void*)WT_UNDERPANTS);
+ }
+ else if (clothing == "skirt")
+ {
+ LLAgent::userRemoveWearable((void*)WT_SKIRT);
+ }
+ else if (clothing == "all")
+ {
+ LLAgent::userRemoveAllClothes(NULL);
+ }
+ return true;
+ }
+};
+
+class LLWorldChat : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_chat(NULL);
+ return true;
+ }
+};
+
+class LLWorldStartGesture : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ handle_slash_key(NULL);
+ return true;
+ }
+};
+
+class LLToolsSelectTool : public view_listener_t
+{
+ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ LLString tool_name = userdata.asString();
+ if (tool_name == "focus")
+ {
+ gCurrentToolset->selectToolByIndex(1);
+ }
+ else if (tool_name == "move")
+ {
+ gCurrentToolset->selectToolByIndex(2);
+ }
+ else if (tool_name == "edit")
+ {
+ gCurrentToolset->selectToolByIndex(3);
+ }
+ else if (tool_name == "create")
+ {
+ gCurrentToolset->selectToolByIndex(4);
+ }
+ else if (tool_name == "land")
+ {
+ gCurrentToolset->selectToolByIndex(5);
+ }
+ return true;
+ }
+};
+
+void initialize_menu_actions()
+{
+ // File menu
+ (new LLFileUploadImage())->registerListener(gMenuHolder, "File.UploadImage");
+ (new LLFileUploadSound())->registerListener(gMenuHolder, "File.UploadSound");
+ (new LLFileUploadAnim())->registerListener(gMenuHolder, "File.UploadAnim");
+ (new LLFileUploadBulk())->registerListener(gMenuHolder, "File.UploadBulk");
+ (new LLFileCloseWindow())->registerListener(gMenuHolder, "File.CloseWindow");
+ (new LLFileSaveTexture())->registerListener(gMenuHolder, "File.SaveTexture");
+ (new LLFileTakeSnapshot())->registerListener(gMenuHolder, "File.TakeSnapshot");
+ (new LLFileTakeSnapshotToDisk())->registerListener(gMenuHolder, "File.TakeSnapshotToDisk");
+ (new LLFileSaveMovie())->registerListener(gMenuHolder, "File.SaveMovie");
+ (new LLFileSetWindowSize())->registerListener(gMenuHolder, "File.SetWindowSize");
+ (new LLFileQuit())->registerListener(gMenuHolder, "File.Quit");
+
+ (new LLFileEnableUpload())->registerListener(gMenuHolder, "File.EnableUpload");
+ (new LLFileEnableSaveAs())->registerListener(gMenuHolder, "File.EnableSaveAs");
+
+ // Edit menu
+ (new LLEditUndo())->registerListener(gMenuHolder, "Edit.Undo");
+ (new LLEditRedo())->registerListener(gMenuHolder, "Edit.Redo");
+ (new LLEditCut())->registerListener(gMenuHolder, "Edit.Cut");
+ (new LLEditCopy())->registerListener(gMenuHolder, "Edit.Copy");
+ (new LLEditPaste())->registerListener(gMenuHolder, "Edit.Paste");
+ (new LLEditDelete())->registerListener(gMenuHolder, "Edit.Delete");
+ (new LLEditSearch())->registerListener(gMenuHolder, "Edit.Search");
+ (new LLEditSelectAll())->registerListener(gMenuHolder, "Edit.SelectAll");
+ (new LLEditDeselect())->registerListener(gMenuHolder, "Edit.Deselect");
+ (new LLEditDuplicate())->registerListener(gMenuHolder, "Edit.Duplicate");
+ (new LLEditTakeOff())->registerListener(gMenuHolder, "Edit.TakeOff");
+
+ (new LLEditEnableUndo())->registerListener(gMenuHolder, "Edit.EnableUndo");
+ (new LLEditEnableRedo())->registerListener(gMenuHolder, "Edit.EnableRedo");
+ (new LLEditEnableCut())->registerListener(gMenuHolder, "Edit.EnableCut");
+ (new LLEditEnableCopy())->registerListener(gMenuHolder, "Edit.EnableCopy");
+ (new LLEditEnablePaste())->registerListener(gMenuHolder, "Edit.EnablePaste");
+ (new LLEditEnableDelete())->registerListener(gMenuHolder, "Edit.EnableDelete");
+ (new LLEditEnableSelectAll())->registerListener(gMenuHolder, "Edit.EnableSelectAll");
+ (new LLEditEnableDeselect())->registerListener(gMenuHolder, "Edit.EnableDeselect");
+ (new LLEditEnableDuplicate())->registerListener(gMenuHolder, "Edit.EnableDuplicate");
+ (new LLEditEnableTakeOff())->registerListener(gMenuHolder, "Edit.EnableTakeOff");
+ (new LLEditEnableCustomizeAvatar())->registerListener(gMenuHolder, "Edit.EnableCustomizeAvatar");
+
+ // View menu
+ (new LLViewMouselook())->registerListener(gMenuHolder, "View.Mouselook");
+ (new LLViewBuildMode())->registerListener(gMenuHolder, "View.BuildMode");
+ (new LLViewResetView())->registerListener(gMenuHolder, "View.ResetView");
+ (new LLViewLookAtLastChatter())->registerListener(gMenuHolder, "View.LookAtLastChatter");
+ (new LLViewShowHoverTips())->registerListener(gMenuHolder, "View.ShowHoverTips");
+ (new LLViewHighlightTransparent())->registerListener(gMenuHolder, "View.HighlightTransparent");
+ (new LLViewToggleBeacon())->registerListener(gMenuHolder, "View.ToggleBeacon");
+ (new LLViewToggleRenderType())->registerListener(gMenuHolder, "View.ToggleRenderType");
+ (new LLViewShowHUDAttachments())->registerListener(gMenuHolder, "View.ShowHUDAttachments");
+ (new LLViewZoomOut())->registerListener(gMenuHolder, "View.ZoomOut");
+ (new LLViewZoomIn())->registerListener(gMenuHolder, "View.ZoomIn");
+ (new LLViewZoomDefault())->registerListener(gMenuHolder, "View.ZoomDefault");
+ (new LLViewFullscreen())->registerListener(gMenuHolder, "View.Fullscreen");
+ (new LLViewDefaultUISize())->registerListener(gMenuHolder, "View.DefaultUISize");
+
+ (new LLViewEnableMouselook())->registerListener(gMenuHolder, "View.EnableMouselook");
+ (new LLViewEnableLastChatter())->registerListener(gMenuHolder, "View.EnableLastChatter");
+
+ (new LLViewCheckBuildMode())->registerListener(gMenuHolder, "View.CheckBuildMode");
+ (new LLViewCheckShowHoverTips())->registerListener(gMenuHolder, "View.CheckShowHoverTips");
+ (new LLViewCheckHighlightTransparent())->registerListener(gMenuHolder, "View.CheckHighlightTransparent");
+ (new LLViewCheckBeaconEnabled())->registerListener(gMenuHolder, "View.CheckBeaconEnabled");
+ (new LLViewCheckRenderType())->registerListener(gMenuHolder, "View.CheckRenderType");
+ (new LLViewCheckHUDAttachments())->registerListener(gMenuHolder, "View.CheckHUDAttachments");
+
+ // World menu
+ (new LLWorldChat())->registerListener(gMenuHolder, "World.Chat");
+ (new LLWorldStartGesture())->registerListener(gMenuHolder, "World.StartGesture");
+ (new LLWorldAlwaysRun())->registerListener(gMenuHolder, "World.AlwaysRun");
+ (new LLWorldFly())->registerListener(gMenuHolder, "World.Fly");
+ (new LLWorldCreateLandmark())->registerListener(gMenuHolder, "World.CreateLandmark");
+ (new LLWorldSetHomeLocation())->registerListener(gMenuHolder, "World.SetHomeLocation");
+ (new LLWorldTeleportHome())->registerListener(gMenuHolder, "World.TeleportHome");
+ (new LLWorldSetAway())->registerListener(gMenuHolder, "World.SetAway");
+ (new LLWorldSetBusy())->registerListener(gMenuHolder, "World.SetBusy");
+
+ (new LLWorldEnableCreateLandmark())->registerListener(gMenuHolder, "World.EnableCreateLandmark");
+ (new LLWorldEnableSetHomeLocation())->registerListener(gMenuHolder, "World.EnableSetHomeLocation");
+ (new LLWorldEnableBuyLand())->registerListener(gMenuHolder, "World.EnableBuyLand");
+
+ (new LLWorldCheckAlwaysRun())->registerListener(gMenuHolder, "World.CheckAlwaysRun");
+
+ (new LLWorldForceSun())->registerListener(gMenuHolder, "World.ForceSun");
+
+ // Tools menu
+ (new LLToolsSelectTool())->registerListener(gMenuHolder, "Tools.SelectTool");
+ (new LLToolsSelectOnlyMyObjects())->registerListener(gMenuHolder, "Tools.SelectOnlyMyObjects");
+ (new LLToolsSelectOnlyMovableObjects())->registerListener(gMenuHolder, "Tools.SelectOnlyMovableObjects");
+ (new LLToolsSelectBySurrounding())->registerListener(gMenuHolder, "Tools.SelectBySurrounding");
+ (new LLToolsShowHiddenSelection())->registerListener(gMenuHolder, "Tools.ShowHiddenSelection");
+ (new LLToolsShowSelectionLightRadius())->registerListener(gMenuHolder, "Tools.ShowSelectionLightRadius");
+ (new LLToolsSnapObjectXY())->registerListener(gMenuHolder, "Tools.SnapObjectXY");
+ (new LLToolsUseSelectionForGrid())->registerListener(gMenuHolder, "Tools.UseSelectionForGrid");
+ (new LLToolsLink())->registerListener(gMenuHolder, "Tools.Link");
+ (new LLToolsUnlink())->registerListener(gMenuHolder, "Tools.Unlink");
+ (new LLToolsStopAllAnimations())->registerListener(gMenuHolder, "Tools.StopAllAnimations");
+ (new LLToolsLookAtSelection())->registerListener(gMenuHolder, "Tools.LookAtSelection");
+ (new LLToolsBuyOrTake())->registerListener(gMenuHolder, "Tools.BuyOrTake");
+ (new LLToolsTakeCopy())->registerListener(gMenuHolder, "Tools.TakeCopy");
+ (new LLToolsSaveToInventory())->registerListener(gMenuHolder, "Tools.SaveToInventory");
+ (new LLToolsSaveToObjectInventory())->registerListener(gMenuHolder, "Tools.SaveToObjectInventory");
+ (new LLToolsSelectedScriptAction())->registerListener(gMenuHolder, "Tools.SelectedScriptAction");
+
+ (new LLToolsEnableToolNotPie())->registerListener(gMenuHolder, "Tools.EnableToolNotPie");
+ (new LLToolsEnableLink())->registerListener(gMenuHolder, "Tools.EnableLink");
+ (new LLToolsEnableUnlink())->registerListener(gMenuHolder, "Tools.EnableUnlink");
+ (new LLToolsEnableBuyOrTake())->registerListener(gMenuHolder, "Tools.EnableBuyOrTake");
+ (new LLToolsEnableTakeCopy())->registerListener(gMenuHolder, "Tools.EnableTakeCopy");
+ (new LLToolsEnableSaveToInventory())->registerListener(gMenuHolder, "Tools.SaveToInventory");
+ (new LLToolsEnableSaveToObjectInventory())->registerListener(gMenuHolder, "Tools.SaveToObjectInventory");
+
+ /*(new LLToolsVisibleBuyObject())->registerListener(gMenuHolder, "Tools.VisibleBuyObject");
+ (new LLToolsVisibleTakeObject())->registerListener(gMenuHolder, "Tools.VisibleTakeObject");*/
+
+ // Help menu
+ (new LLHelpLiveHelp())->registerListener(gMenuHolder, "Help.LiveHelp");
+ (new LLHelpMOTD())->registerListener(gMenuHolder, "Help.MOTD");
+
+ // Self pie menu
+ (new LLSelfStandUp())->registerListener(gMenuHolder, "Self.StandUp");
+ (new LLSelfRemoveAllAttachments())->registerListener(gMenuHolder, "Self.RemoveAllAttachments");
+
+ (new LLSelfEnableStandUp())->registerListener(gMenuHolder, "Self.EnableStandUp");
+ (new LLSelfEnableRemoveAllAttachments())->registerListener(gMenuHolder, "Self.EnableRemoveAllAttachments");
+
+ // Avatar pie menu
+ (new LLObjectMute())->registerListener(gMenuHolder, "Avatar.Mute");
+ (new LLAvatarRate())->registerListener(gMenuHolder, "Avatar.Rate");
+ (new LLAvatarAddFriend())->registerListener(gMenuHolder, "Avatar.AddFriend");
+ (new LLAvatarFreeze())->registerListener(gMenuHolder, "Avatar.Freeze");
+ (new LLAvatarDebug())->registerListener(gMenuHolder, "Avatar.Debug");
+ (new LLAvatarVisibleDebug())->registerListener(gMenuHolder, "Avatar.VisibleDebug");
+ (new LLAvatarEnableDebug())->registerListener(gMenuHolder, "Avatar.EnableDebug");
+ (new LLAvatarGiveCard())->registerListener(gMenuHolder, "Avatar.GiveCard");
+ (new LLAvatarEject())->registerListener(gMenuHolder, "Avatar.Eject");
+ (new LLAvatarSendIM())->registerListener(gMenuHolder, "Avatar.SendIM");
+
+ (new LLObjectEnableMute())->registerListener(gMenuHolder, "Avatar.EnableMute");
+ (new LLAvatarEnableAddFriend())->registerListener(gMenuHolder, "Avatar.EnableAddFriend");
+ (new LLAvatarEnableFreezeEject())->registerListener(gMenuHolder, "Avatar.EnableFreezeEject");
+
+ // Object pie menu
+ (new LLObjectOpen())->registerListener(gMenuHolder, "Object.Open");
+ (new LLObjectBuild())->registerListener(gMenuHolder, "Object.Build");
+ (new LLObjectTouch())->registerListener(gMenuHolder, "Object.Touch");
+ (new LLObjectSitOrStand())->registerListener(gMenuHolder, "Object.SitOrStand");
+ (new LLObjectDelete())->registerListener(gMenuHolder, "Object.Delete");
+ (new LLObjectAttachToAvatar())->registerListener(gMenuHolder, "Object.AttachToAvatar");
+ (new LLObjectReturn())->registerListener(gMenuHolder, "Object.Return");
+ (new LLObjectRateOwner())->registerListener(gMenuHolder, "Object.RateOwner");
+ (new LLObjectReportAbuse())->registerListener(gMenuHolder, "Object.ReportAbuse");
+ (new LLObjectRateCreator())->registerListener(gMenuHolder, "Object.RateCreator");
+ (new LLObjectMute())->registerListener(gMenuHolder, "Object.Mute");
+ (new LLObjectBuy())->registerListener(gMenuHolder, "Object.Buy");
+ (new LLObjectEdit())->registerListener(gMenuHolder, "Object.Edit");
+
+ (new LLObjectEnableOpen())->registerListener(gMenuHolder, "Object.EnableOpen");
+ (new LLObjectEnableTouch())->registerListener(gMenuHolder, "Object.EnableTouch");
+ (new LLObjectEnableSitOrStand())->registerListener(gMenuHolder, "Object.EnableSitOrStand");
+ (new LLObjectEnableDelete())->registerListener(gMenuHolder, "Object.EnableDelete");
+ (new LLObjectEnableWear())->registerListener(gMenuHolder, "Object.EnableWear");
+ (new LLObjectEnableReturn())->registerListener(gMenuHolder, "Object.EnableReturn");
+ (new LLObjectEnableRateOwner())->registerListener(gMenuHolder, "Object.EnableRateOwner");
+ (new LLObjectEnableReportAbuse())->registerListener(gMenuHolder, "Object.EnableReportAbuse");
+ (new LLObjectEnableRateCreator())->registerListener(gMenuHolder, "Object.EnableRateCreator");
+ (new LLObjectEnableMute())->registerListener(gMenuHolder, "Object.EnableMute");
+ (new LLObjectEnableBuy())->registerListener(gMenuHolder, "Object.EnableBuy");
+
+ /*(new LLObjectVisibleTouch())->registerListener(gMenuHolder, "Object.VisibleTouch");
+ (new LLObjectVisibleCustomTouch())->registerListener(gMenuHolder, "Object.VisibleCustomTouch");
+ (new LLObjectVisibleStandUp())->registerListener(gMenuHolder, "Object.VisibleStandUp");
+ (new LLObjectVisibleSitHere())->registerListener(gMenuHolder, "Object.VisibleSitHere");
+ (new LLObjectVisibleCustomSit())->registerListener(gMenuHolder, "Object.VisibleCustomSit");*/
+
+ // Attachment pie menu
+ (new LLAttachmentDrop())->registerListener(gMenuHolder, "Attachment.Drop");
+ (new LLAttachmentDetach())->registerListener(gMenuHolder, "Attachment.Detach");
+
+ (new LLAttachmentEnableDrop())->registerListener(gMenuHolder, "Attachment.EnableDrop");
+ (new LLAttachmentEnableDetach())->registerListener(gMenuHolder, "Attachment.EnableDetach");
+
+ // Land pie menu
+ (new LLLandBuild())->registerListener(gMenuHolder, "Land.Build");
+ (new LLLandSit())->registerListener(gMenuHolder, "Land.Sit");
+ (new LLLandBuyPass())->registerListener(gMenuHolder, "Land.BuyPass");
+ (new LLLandEdit())->registerListener(gMenuHolder, "Land.Edit");
+
+ (new LLLandEnableBuyPass())->registerListener(gMenuHolder, "Land.EnableBuyPass");
+
+ // Generic actions
+ (new LLShowFloater())->registerListener(gMenuHolder, "ShowFloater");
+ (new LLPromptShowURL())->registerListener(gMenuHolder, "PromptShowURL");
+ (new LLPromptShowFile())->registerListener(gMenuHolder, "PromptShowFile");
+ (new LLShowAgentProfile())->registerListener(gMenuHolder, "ShowAgentProfile");
+ (new LLShowAgentGroups())->registerListener(gMenuHolder, "ShowAgentGroups");
+ (new LLToggleControl())->registerListener(gMenuHolder, "ToggleControl");
+
+ (new LLGoToObject())->registerListener(gMenuHolder, "GoToObject");
+ (new LLPayObject())->registerListener(gMenuHolder, "PayObject");
+
+ (new LLEnablePayObject())->registerListener(gMenuHolder, "EnablePayObject");
+ (new LLEnableEdit())->registerListener(gMenuHolder, "EnableEdit");
+
+ (new LLFloaterVisible())->registerListener(gMenuHolder, "FloaterVisible");
+ (new LLSomethingSelected())->registerListener(gMenuHolder, "SomethingSelected");
+ (new LLSomethingSelectedNoHUD())->registerListener(gMenuHolder, "SomethingSelectedNoHUD");
+ (new LLEditableSelected())->registerListener(gMenuHolder, "EditableSelected");
+}
diff --git a/indra/newview/llviewermenu.h b/indra/newview/llviewermenu.h
new file mode 100644
index 0000000000..37a7f0fa13
--- /dev/null
+++ b/indra/newview/llviewermenu.h
@@ -0,0 +1,174 @@
+/**
+ * @file llviewermenu.h
+ * @brief Builds menus out of objects
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERMENU_H
+#define LL_LLVIEWERMENU_H
+
+#include "llassetstorage.h"
+#include "llinventory.h"
+#include "llmenugl.h"
+
+//newview includes
+#include "llfilepicker.h"
+
+class LLUICtrl;
+class LLView;
+
+struct LLResourceData
+{
+ LLAssetInfo mAssetInfo;
+ LLAssetType::EType mPreferredLocation;
+ LLInventoryType::EType mInventoryType;
+ U32 mNextOwnerPerm;
+ void *mUserData;
+};
+
+void pre_init_menus();
+void init_menus();
+void cleanup_menus();
+
+void show_debug_menus(); // checks for if menus should be shown first.
+void show_context_menu( S32 x, S32 y, MASK mask );
+void show_build_mode_context_menu(S32 x, S32 y, MASK mask);
+void load_url_local_file(const char *file_name);
+BOOL enable_save_into_inventory(void*);
+void handle_reset_view();
+void handle_cut(void*);
+void handle_copy(void*);
+void handle_paste(void*);
+void handle_delete(void*);
+void handle_redo(void*);
+void handle_undo(void*);
+void handle_select_all(void*);
+void handle_deselect(void*);
+void handle_delete_object();
+void handle_duplicate(void*);
+void handle_duplicate_in_place(void*);
+BOOL enable_not_have_card(void *userdata);
+void process_grant_godlike_powers(LLMessageSystem* msg, void**);
+
+BOOL enable_cut(void*);
+BOOL enable_copy(void*);
+BOOL enable_paste(void*);
+BOOL enable_select_all(void*);
+BOOL enable_deselect(void*);
+BOOL enable_undo(void*);
+BOOL enable_redo(void*);
+
+// returns TRUE if we have a friend relationship with agent_id
+BOOL is_agent_friend(const LLUUID& agent_id);
+BOOL is_agent_mappable(const LLUUID& agent_id);
+
+void menu_toggle_control( void* user_data );
+void check_toggle_control( LLUICtrl *, void* user_data );
+void handle_attach_to_avatar(void* user_data);
+void confirm_replace_attachment(S32 option, void* user_data);
+void handle_detach_from_avatar(void* user_data);
+void attach_label(LLString& label, void* user_data);
+void detach_label(LLString& label, void* user_data);
+BOOL object_selected_and_point_valid(void* user_data);
+BOOL object_attached(void* user_data);
+void handle_detach(void*);
+BOOL enable_god_full(void* user_data);
+BOOL enable_god_liaison(void* user_data);
+BOOL enable_god_customer_service(void* user_data);
+BOOL enable_god_basic(void* user_data);
+void handle_show_newest_map(void*);
+
+void exchange_callingcard(const LLUUID& dest_id);
+
+void handle_gestures(void*);
+void handle_sit_down(void*);
+bool toggle_build_mode();
+void handle_object_build(void*);
+void handle_save_snapshot(void *);
+
+bool handle_sit_or_stand();
+bool handle_give_money_dialog();
+bool handle_object_open();
+bool handle_go_to();
+
+void upload_new_resource(const LLString& src_filename, std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm = PERM_NONE,
+ const LLString& display_name = LLString::null,
+ LLAssetStorage::LLStoreAssetCallback callback = NULL,
+ void *userdata = NULL);
+
+void upload_new_resource(const LLTransactionID &tid, LLAssetType::EType type,
+ std::string name,
+ std::string desc, S32 compression_info,
+ LLAssetType::EType destination_folder_type,
+ LLInventoryType::EType inv_type,
+ U32 next_owner_perm = PERM_NONE,
+ const LLString& display_name = LLString::null,
+ LLAssetStorage::LLStoreAssetCallback callback = NULL,
+ void *userdata = NULL);
+
+// Export to XML or Collada
+void handle_export_selected( void * );
+
+//Retrieve a list of valid extensions for a given file "type"
+const char* build_extensions_string(LLFilePicker::ELoadFilter filter);
+
+// Pass in an empty string and this function will build a string that
+// describes buyer permissions.
+class LLSaleInfo;
+class LLPermissions;
+
+class LLViewerMenuHolderGL : public LLMenuHolderGL
+{
+public:
+ LLViewerMenuHolderGL() : LLMenuHolderGL() {};
+
+ virtual BOOL hideMenus();
+ //virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent);
+};
+
+extern const LLString SAVE_INTO_INVENTORY;
+
+extern LLMenuBarGL* gMenuBarView;
+//extern LLView* gMenuBarHolder;
+extern LLMenuGL* gPopupMenuView;
+extern LLViewerMenuHolderGL* gMenuHolder;
+
+// Pie menus
+extern LLPieMenu *gPieSelf;
+extern LLPieMenu *gPieAvatar;
+extern LLPieMenu *gPieObject;
+extern LLPieMenu *gPieAttachment;
+extern LLPieMenu *gPieLand;
+extern LLPieMenu* gPieRate;
+
+// Pie menus
+extern LLPieMenu *gPieSelfSimple;
+extern LLPieMenu *gPieAvatarSimple;
+extern LLPieMenu *gPieObjectSimple;
+extern LLPieMenu *gPieAttachmentSimple;
+extern LLPieMenu *gPieLandSimple;
+
+// Needed to build menus when attachment site list available
+extern LLMenuGL* gAttachSubMenu;
+extern LLMenuGL* gDetachSubMenu;
+extern LLMenuGL* gTakeOffClothes;
+extern LLPieMenu* gAttachScreenPieMenu;
+extern LLPieMenu* gDetachScreenPieMenu;
+extern LLPieMenu* gAttachPieMenu;
+extern LLPieMenu* gDetachPieMenu;
+extern LLPieMenu* gAttachBodyPartPieMenus[8];
+extern LLPieMenu* gDetachBodyPartPieMenus[8];
+
+extern LLMenuItemCallGL* gAFKMenu;
+extern LLMenuItemCallGL* gBusyMenu;
+extern LLMenuItemCallGL* gMutePieMenu;
+extern LLMenuItemCallGL* gMuteObjectPieMenu;
+extern LLMenuItemCallGL* gBuyPassPieMenu;
+
+#endif
diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp
new file mode 100644
index 0000000000..c649229311
--- /dev/null
+++ b/indra/newview/llviewermessage.cpp
@@ -0,0 +1,5017 @@
+/**
+ * @file llviewermessage.cpp
+ * @brief Dumping ground for viewer-side message system callbacks.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewermessage.h"
+
+#include <deque>
+#include <stdio.h>
+#include <string.h>
+
+#include "audioengine.h"
+#include "audiosettings.h"
+#include "indra_constants.h"
+#include "lscript_byteformat.h"
+#include "mean_collision_data.h"
+#include "llfloaterbump.h"
+#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llchat.h"
+#include "lldbstrings.h"
+#include "lleconomy.h"
+#include "llfilepicker.h"
+#include "llfloaterimport.h"
+#include "llfocusmgr.h"
+#include "llfollowcamparams.h"
+#include "llinstantmessage.h"
+#include "llquantize.h"
+#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "llsdserialize.h"
+#include "llstring.h"
+#include "llteleportflags.h"
+#include "lltracker.h"
+#include "lltransactionflags.h"
+#include "llwindow.h" // shell_open()
+#include "llxfermanager.h"
+#include "message.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "lldrawpool.h"
+#include "llfirstuse.h"
+#include "llfloaterbuycurrency.h"
+#include "llfloaterbuyland.h"
+#include "llfloaterchat.h"
+#include "llfloatergroupinfo.h"
+#include "llfloaterimagepreview.h"
+#include "llfloaterland.h"
+#include "llfloaterregioninfo.h"
+#include "llfloaterlandholdings.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloaterpostcard.h"
+#include "llfloaterpreference.h"
+#include "llfollowcam.h"
+#include "llgroupnotify.h"
+#include "llhudeffect.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llimpanel.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llnetmap.h"
+#include "llnotify.h"
+#include "llpanelgrouplandmoney.h"
+#include "llselectmgr.h"
+#include "llstartup.h"
+#include "llsky.h"
+#include "llstatenums.h"
+#include "llstatusbar.h"
+#include "llimview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolmgr.h"
+#include "llui.h" // for make_ui_sound
+#include "lluploaddialog.h"
+#include "llviewercamera.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewertexteditor.h"
+#include "llviewerthrottle.h"
+#include "llviewerwindow.h"
+#include "llvlmanager.h"
+#include "llvoavatar.h"
+#include "llvotextbubble.h"
+#include "llweb.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "llfloaterworldmap.h"
+
+#include <boost/tokenizer.hpp>
+
+#if LL_WINDOWS // For Windows specific error handler
+#include "llwindebug.h" // For the invalid message handler
+#endif
+
+//
+// Constants
+//
+const F32 BIRD_AUDIBLE_RADIUS = 32.0f;
+const F32 SIT_DISTANCE_FROM_TARGET = 0.25f;
+static const F32 LOGOUT_REPLY_TIME = 3.f; // Wait this long after LogoutReply before quitting.
+extern BOOL gDebugClicks;
+
+extern void bad_network_handler();
+
+LLDispatcher gGenericDispatcher;
+
+// function prototypes
+void open_offer(const std::vector<LLUUID>& items);
+void friendship_offer_callback(S32 option, void* user_data);
+
+struct LLFriendshipOffer
+{
+ LLUUID mFromID;
+ LLUUID mTransactionID;
+ BOOL mOnline;
+ LLHost mHost;
+};
+
+//const char BUSY_AUTO_RESPONSE[] = "The Resident you messaged is in 'busy mode' which means they have "
+// "requested not to be disturbed. Your message will still be shown in their IM "
+// "panel for later viewing.";
+
+//
+// Functions
+//
+
+void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group,
+ S32 trx_type, const LLString& desc)
+{
+ if(0 == amount) return;
+ amount = abs(amount);
+ llinfos << "give_money(" << uuid << "," << amount << ")"<< llendl;
+ if(can_afford_transaction(amount))
+ {
+// gStatusBar->debitBalance(amount);
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoneyTransferRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, agent_get_id());
+ msg->addUUIDFast(_PREHASH_SessionID, agent_get_session_id());
+ msg->nextBlockFast(_PREHASH_MoneyData);
+ msg->addUUIDFast(_PREHASH_SourceID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_DestID, uuid);
+ msg->addU8Fast(_PREHASH_Flags, pack_transaction_flags(FALSE, is_group));
+ msg->addS32Fast(_PREHASH_Amount, amount);
+ msg->addU8Fast(_PREHASH_AggregatePermNextOwner, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addU8Fast(_PREHASH_AggregatePermInventory, (U8)LLAggregatePermissions::AP_EMPTY);
+ msg->addS32Fast(_PREHASH_TransactionType, trx_type );
+ msg->addStringFast(_PREHASH_Description, desc.c_str());
+ msg->sendReliable(region->getHost());
+ }
+ else
+ {
+ LLFloaterBuyCurrency::buyCurrency("Giving", amount);
+ }
+}
+
+BOOL can_afford_transaction(S32 cost)
+{
+ return((cost <= 0)||((gStatusBar) && (gStatusBar->getBalance() >=cost)));
+}
+
+void send_complete_agent_movement(const LLHost& sim_host)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_CompleteAgentMovement);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode);
+ msg->sendReliable(sim_host);
+}
+
+void process_logout_reply(LLMessageSystem* msg, void**)
+{
+ // The server has told us it's ok to quit.
+ llinfos << "process_logout_reply" << llendl;
+
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ LLUUID session_id;
+ msg->getUUID("AgentData", "SessionID", session_id);
+ if((agent_id != agent_get_id()) || (session_id != agent_get_session_id()))
+ {
+ llwarns << "Bogus Logout Reply" << llendl;
+ }
+
+ S32 count = msg->getNumberOfBlocksFast( _PREHASH_InventoryData );
+ for( S32 i = 0; i < count; i++ )
+ {
+ LLUUID item_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_ItemID, item_id, i);
+
+ if( (1 == count) && item_id.isNull() )
+ {
+ // Detect dummy item. Indicates an empty list.
+ break;
+ }
+
+
+ // Update our the asset ids of the inventory items we're currently wearing.
+ LLUUID new_asset_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_NewAssetID, new_asset_id, i);
+
+ llinfos << "process_logout_reply itemID=" << item_id << llendl;
+ LLInventoryItem* item = gInventory.getItem( item_id );
+ if( item )
+ {
+ item->setAssetUUID(new_asset_id);
+
+ gInventory.addChangedMask( LLInventoryObserver::INTERNAL, item_id );
+ gInventory.notifyObservers();
+ }
+ else
+ {
+ llinfos << "process_logout_reply item not found: " << item_id << llendl;
+ }
+ }
+
+ /*
+ gLogoutTimer.reset();
+ gLogoutMaxTime = LOGOUT_REPLY_TIME;
+ gViewerWindow->setProgressString("Logging out...");
+
+ if (gNoRender)
+ {
+ // Hack so drones can quit with new logout process
+ */
+
+ app_force_quit(NULL);
+}
+
+void process_layer_data(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLViewerRegion *regionp = gWorldp->getRegion(mesgsys->getSender());
+
+ if (!regionp || gNoRender)
+ {
+ return;
+ }
+
+
+ S32 size;
+ S8 type;
+
+ mesgsys->getS8Fast(_PREHASH_LayerID, _PREHASH_Type, type);
+ size = mesgsys->getSizeFast(_PREHASH_LayerData, _PREHASH_Data);
+ if(!size)
+ {
+ llwarns << "Layer data has zero size." << llendl;
+ return;
+ }
+ U8 *datap = new U8[size];
+ mesgsys->getBinaryDataFast(_PREHASH_LayerData, _PREHASH_Data, datap, size);
+ LLVLData *vl_datap = new LLVLData(regionp, type, datap, size);
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gVLManager.addLayerData(vl_datap, mesgsys->getReceiveCompressedSize());
+ }
+ else
+ {
+ gVLManager.addLayerData(vl_datap, mesgsys->getReceiveSize());
+ }
+}
+
+S32 exported_object_count = 0;
+S32 exported_image_count = 0;
+S32 current_object_count = 0;
+S32 current_image_count = 0;
+
+extern LLNotifyBox *gExporterNotify;
+extern LLUUID gExporterRequestID;
+extern LLString gExportDirectory;
+
+extern LLUploadDialog *gExportDialog;
+
+LLString gExportedFile;
+
+std::map<LLUUID, LLString> gImageChecksums;
+
+void export_complete()
+{
+ LLUploadDialog::modalUploadFinished();
+ gExporterRequestID.setNull();
+ gExportDirectory = "";
+
+ FILE *fXML = LLFile::fopen(gExportedFile.c_str(), "rb");
+ fseek(fXML, 0, SEEK_END);
+ U32 length = ftell(fXML);
+ fseek(fXML, 0, SEEK_SET);
+ U8 *buffer = new U8[length];
+ fread(buffer, 1, length, fXML);
+ fclose(fXML);
+
+ char *pos = (char *)buffer;
+ while ((pos = strstr(pos+1, "<sl:image ")) != 0)
+ {
+ char *pos_check = strstr(pos, "checksum=\"");
+ char *pos_uuid = strstr(pos_check, "\">");
+
+ if (pos_check && pos_uuid)
+ {
+ char image_uuid_str[UUID_STR_SIZE];
+ memcpy(image_uuid_str, pos_uuid+2, UUID_STR_SIZE-1);
+ image_uuid_str[UUID_STR_SIZE-1] = 0;
+
+ LLUUID image_uuid(image_uuid_str);
+
+ llinfos << "Found UUID: " << image_uuid << llendl;
+
+ std::map<LLUUID, LLString>::iterator itor = gImageChecksums.find(image_uuid);
+ if (itor != gImageChecksums.end())
+ {
+ llinfos << "Replacing with checksum: " << itor->second << llendl;
+ memcpy(&pos_check[10], itor->second.c_str(), 32);
+ }
+ }
+ }
+
+ FILE *fXMLOut = LLFile::fopen(gExportedFile.c_str(), "wb");
+ fwrite(buffer, 1, length, fXMLOut);
+ fclose(fXMLOut);
+
+ delete buffer;
+}
+
+
+void exported_item_complete(const LLTSCode status, void *user_data)
+{
+ //LLString *filename = (LLString *)user_data;
+
+ if (status < LLTS_OK)
+ {
+ llinfos << "Export failed!" << llendl;
+ }
+ else
+ {
+ ++current_object_count;
+ if (current_image_count == exported_image_count && current_object_count == exported_object_count)
+ {
+ llinfos << "*** Export complete ***" << llendl;
+
+ export_complete();
+ }
+ else
+ {
+ gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
+ }
+ }
+}
+
+struct exported_image_info
+{
+ LLUUID image_id;
+ LLString filename;
+ U32 image_num;
+};
+
+void exported_j2c_complete(const LLTSCode status, void *user_data)
+{
+ exported_image_info *info = (exported_image_info *)user_data;
+ LLUUID image_id = info->image_id;
+ U32 image_num = info->image_num;
+ LLString filename = info->filename;
+ delete info;
+
+ if (status < LLTS_OK)
+ {
+ llinfos << "Image download failed!" << llendl;
+ }
+ else
+ {
+ FILE *fIn = LLFile::fopen(filename.c_str(), "rb");
+ if (fIn)
+ {
+ LLPointer<LLImageJ2C> ImageUtility = new LLImageJ2C;
+ LLPointer<LLImageTGA> TargaUtility = new LLImageTGA;
+
+ fseek(fIn, 0, SEEK_END);
+ S32 length = ftell(fIn);
+ fseek(fIn, 0, SEEK_SET);
+ U8 *buffer = ImageUtility->allocateData(length);
+ fread(buffer, 1, length, fIn);
+ fclose(fIn);
+ LLFile::remove(filename.c_str());
+
+ // Convert to TGA
+ LLPointer<LLImageRaw> image = new LLImageRaw();
+
+ ImageUtility->updateData();
+ ImageUtility->decode(image, 100000.0f);
+
+ TargaUtility->encode(image);
+ U8 *data = TargaUtility->getData();
+ S32 data_size = TargaUtility->getDataSize();
+
+ char *file_path = new char[filename.size()+1];
+ strcpy(file_path, filename.c_str());
+ char *end = strrchr(file_path, gDirUtilp->getDirDelimiter()[0]);
+ end[0] = 0;
+ LLString output_file = llformat("%s/image-%03d.tga", file_path, image_num);//filename;
+ delete file_path;
+ //S32 name_len = output_file.length();
+ //strcpy(&output_file[name_len-3], "tga");
+ FILE *fOut = LLFile::fopen(output_file.c_str(), "wb");
+ char md5_hash_string[33];
+ strcpy(md5_hash_string, "00000000000000000000000000000000");
+ if (fOut)
+ {
+ fwrite(data, 1, data_size, fOut);
+ fseek(fOut, 0, SEEK_SET);
+ fclose(fOut);
+ fOut = LLFile::fopen(output_file.c_str(), "rb");
+ LLMD5 my_md5_hash(fOut);
+ my_md5_hash.hex_digest(md5_hash_string);
+ }
+
+ gImageChecksums.insert(std::pair<LLUUID, LLString>(image_id, md5_hash_string));
+ }
+ }
+
+ ++current_image_count;
+ if (current_image_count == exported_image_count && current_object_count == exported_object_count)
+ {
+ llinfos << "*** Export textures complete ***" << llendl;
+ export_complete();
+ }
+ else
+ {
+ gExportDialog->setMessage(llformat("Exported %d/%d object files, %d/%d textures.", current_object_count, exported_object_count, current_image_count, exported_image_count));
+ }
+}
+
+void process_derez_ack(LLMessageSystem*, void**)
+{
+ if(gViewerWindow) gViewerWindow->getWindow()->decBusyCount();
+}
+
+void process_places_reply(LLMessageSystem* msg, void** data)
+{
+ LLUUID query_id;
+
+ msg->getUUID("AgentData", "QueryID", query_id);
+ if (query_id.isNull())
+ {
+ LLFloaterLandHoldings::processPlacesReply(msg, data);
+ }
+ else if(gAgent.isInGroup(query_id))
+ {
+ LLPanelGroupLandMoney::processPlacesReply(msg, data);
+ }
+ else
+ {
+ llwarns << "Got invalid PlacesReply message" << llendl;
+ }
+}
+
+void send_sound_trigger(const LLUUID& sound_id, F32 gain)
+{
+ if (sound_id.isNull())
+ {
+ // zero guids don't get sent (no sound)
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_SoundTrigger);
+ msg->nextBlockFast(_PREHASH_SoundData);
+ msg->addUUIDFast(_PREHASH_SoundID, sound_id);
+ // Client untrusted, ids set on sim
+ msg->addUUIDFast(_PREHASH_OwnerID, LLUUID::null );
+ msg->addUUIDFast(_PREHASH_ObjectID, LLUUID::null );
+ msg->addUUIDFast(_PREHASH_ParentID, LLUUID::null );
+
+ msg->addU64Fast(_PREHASH_Handle, gAgent.getRegion()->getHandle());
+
+ LLVector3 position = gAgent.getPositionAgent();
+ msg->addVector3Fast(_PREHASH_Position, position);
+ msg->addF32Fast(_PREHASH_Gain, gain);
+
+ gAgent.sendMessage();
+}
+
+struct LLJoinGroupData
+{
+ LLUUID mGroupID;
+ LLUUID mTransactionID;
+ std::string mName;
+ std::string mMessage;
+ S32 mFee;
+};
+
+void join_group_callback(S32 option, void* user_data)
+{
+ LLJoinGroupData* data = (LLJoinGroupData*)user_data;
+ BOOL delete_context_data = TRUE;
+ bool accept_invite = false;
+ if(option == 0 && data && !data->mGroupID.isNull())
+ {
+ // check for promotion or demotion.
+ S32 max_groups = MAX_AGENT_GROUPS;
+ if(gAgent.isInGroup(data->mGroupID)) ++max_groups;
+
+ if(gAgent.mGroups.count() < max_groups)
+ {
+ accept_invite = true;
+ }
+ else
+ {
+ delete_context_data = FALSE;
+ LLString::format_map_t args;
+ args["[NAME]"] = data->mName;
+ args["[INVITE]"] = data->mMessage;
+ LLAlertDialog::showXml("JoinedTooManyGroupsMember", args, join_group_callback, (void*)data);
+ }
+ }
+
+ if (accept_invite)
+ {
+ // If there is a fee to join this group, make
+ // sure the user is sure they want to join.
+ if (data->mFee > 0)
+ {
+ delete_context_data = FALSE;
+ LLString::format_map_t args;
+ args["[COST]"] = llformat("%d", data->mFee);
+ // Set the fee to 0, so that we don't keep
+ // asking about a fee.
+ data->mFee = 0;
+ gViewerWindow->alertXml("JoinGroupCanAfford",
+ args,
+ join_group_callback,
+ (void*)data);
+ }
+ else
+ {
+ send_improved_im(data->mGroupID,
+ "name",
+ "message",
+ IM_ONLINE,
+ IM_GROUP_INVITATION_ACCEPT,
+ data->mTransactionID);
+ }
+ }
+ else if (data)
+ {
+ send_improved_im(data->mGroupID,
+ "name",
+ "message",
+ IM_ONLINE,
+ IM_GROUP_INVITATION_DECLINE,
+ data->mTransactionID);
+ }
+
+ if(delete_context_data)
+ {
+ delete data;
+ data = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Instant Message
+//-----------------------------------------------------------------------------
+class LLOpenAgentOffer : public LLInventoryFetchObserver
+{
+public:
+ LLOpenAgentOffer() {}
+ virtual ~LLOpenAgentOffer() {}
+
+ virtual void done()
+ {
+ open_offer(mComplete);
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+class LLOpenTaskOffer : public LLInventoryExistenceObserver
+{
+public:
+ LLOpenTaskOffer() {}
+ virtual ~LLOpenTaskOffer() {}
+
+protected:
+ virtual void done()
+ {
+ open_offer(mExist);
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+class LLDiscardAgentOffer : public LLInventoryFetchComboObserver
+{
+public:
+ LLDiscardAgentOffer(const LLUUID& folder_id, const LLUUID& object_id) :
+ mFolderID(folder_id),
+ mObjectID(object_id) {}
+ virtual ~LLDiscardAgentOffer() {}
+ virtual void done()
+ {
+ lldebugs << "LLDiscardAgentOffer::done()" << llendl;
+ LLUUID trash_id;
+ trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH);
+ bool notify = false;
+ if(trash_id.notNull() && mObjectID.notNull())
+ {
+ LLInventoryModel::update_list_t update;
+ LLInventoryModel::LLCategoryUpdate old_folder(mFolderID, -1);
+ update.push_back(old_folder);
+ LLInventoryModel::LLCategoryUpdate new_folder(trash_id, 1);
+ update.push_back(new_folder);
+ gInventory.accountForUpdate(update);
+ gInventory.moveObject(mObjectID, trash_id);
+ LLInventoryObject* obj = gInventory.getObject(mObjectID);
+ if(obj)
+ {
+ // no need to restamp since this is already a freshly
+ // stamped item.
+ obj->updateParentOnServer(FALSE);
+ notify = true;
+ }
+ }
+ else
+ {
+ llwarns << "DiscardAgentOffer unable to find: "
+ << (trash_id.isNull() ? "trash " : "")
+ << (mObjectID.isNull() ? "object" : "") << llendl;
+ }
+ gInventory.removeObserver(this);
+ if(notify)
+ {
+ gInventory.notifyObservers();
+ }
+ delete this;
+ }
+protected:
+ LLUUID mFolderID;
+ LLUUID mObjectID;
+};
+
+
+void open_offer(const std::vector<LLUUID>& items)
+{
+ std::vector<LLUUID>::const_iterator it = items.begin();
+ std::vector<LLUUID>::const_iterator end = items.end();
+ LLUUID trash_id(gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH));
+ LLInventoryItem* item;
+ for(; it != end; ++it)
+ {
+ item = gInventory.getItem(*it);
+ if(!item)
+ {
+ llwarns << "Unable to show inventory item: " << *it << llendl;
+ continue;
+ }
+ if(gInventory.isObjectDescendentOf(*it, trash_id))
+ {
+ continue;
+ }
+ switch(item->getType())
+ {
+ case LLAssetType::AT_NOTECARD:
+ open_notecard(*it, LLString("Note: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ case LLAssetType::AT_LANDMARK:
+ open_landmark(*it, LLString("Landmark: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ case LLAssetType::AT_TEXTURE:
+ open_texture(*it, LLString("Texture: ") + item->getName(), TRUE, LLUUID::null, FALSE);
+ break;
+ default:
+ {
+ // Don't auto-open the inventory - just select it if we
+ // already have an active inventory.
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus();
+ LLFocusMgr::FocusLostCallback callback;
+ callback = gFocusMgr.getFocusCallback();
+ view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO);
+ gFocusMgr.setKeyboardFocus(focus_ctrl, callback);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void inventory_offer_mute_callback(const LLUUID& blocked_id,
+ const char* first_name,
+ const char* last_name,
+ BOOL is_group,
+ void*)
+{
+ LLString from_name;
+ LLMute::EType type;
+
+ if (is_group)
+ {
+ type = LLMute::GROUP;
+ from_name = first_name;
+ }
+ else
+ {
+ type = LLMute::AGENT;
+ from_name += first_name;
+ from_name += " ";
+ from_name += last_name;
+ }
+
+ LLMute mute(blocked_id, from_name, type);
+ if (gMuteListp->add(mute))
+ {
+ gFloaterMute->show();
+ gFloaterMute->selectMute(blocked_id);
+ }
+}
+
+void inventory_offer_callback(S32 option, void* user_data)
+{
+ LLChat chat;
+ LLString log_message;
+
+ LLOfferInfo* info = (LLOfferInfo*)user_data;
+ if(!info) return;
+
+ // For muting, we need to add the mute, then decline the offer.
+ // This must be done here because:
+ // * callback may be called immediately,
+ // * adding the mute sends a message,
+ // * we can't build two messages at once. JC
+ if (option == 2)
+ {
+ gCacheName->get(info->mFromID, info->mFromGroup, inventory_offer_mute_callback, NULL);
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ImprovedInstantMessage);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_MessageBlock);
+ msg->addBOOLFast(_PREHASH_FromGroup, FALSE);
+ msg->addUUIDFast(_PREHASH_ToAgentID, info->mFromID);
+ msg->addU8Fast(_PREHASH_Offline, IM_ONLINE);
+ msg->addUUIDFast(_PREHASH_ID, info->mTransactionID);
+ msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary
+ std::string name;
+ gAgent.buildFullname(name);
+ msg->addStringFast(_PREHASH_FromAgentName, name);
+ msg->addStringFast(_PREHASH_Message, "");
+ msg->addU32Fast(_PREHASH_ParentEstateID, 0);
+ msg->addUUIDFast(_PREHASH_RegionID, LLUUID::null);
+ msg->addVector3Fast(_PREHASH_Position, gAgent.getPositionAgent());
+ LLInventoryObserver* opener = NULL;
+ LLViewerInventoryCategory* catp = NULL;
+ catp = (LLViewerInventoryCategory*)gInventory.getCategory(info->mObjectID);
+ LLViewerInventoryItem* itemp = NULL;
+ if(!catp)
+ {
+ itemp = (LLViewerInventoryItem*)gInventory.getItem(info->mObjectID);
+ }
+
+ // XUI:translate
+ LLString from_string;
+ if (info->mFromObject == TRUE)
+ {
+ if (info->mFromGroup)
+ {
+ char group_name[MAX_STRING];
+ if (gCacheName->getGroupName(info->mFromID, group_name))
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by the group '" + group_name + "'";
+ }
+ else
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by an unknown group";
+ }
+ }
+ else
+ {
+ char first_name[MAX_STRING];
+ char last_name[MAX_STRING];
+ if (gCacheName->getName(info->mFromID, first_name, last_name))
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by " + first_name + " " + last_name;
+ }
+ else
+ {
+ from_string = LLString("An object named ") + info->mFromName + " owned by an unknown user";
+ }
+ }
+ }
+ else
+ {
+ from_string = info->mFromName;
+ }
+
+ switch(option)
+ {
+ case 0:
+ // ACCEPT. The math for the dialog works, because the accept
+ // for inventory_offered, task_inventory_offer or
+ // group_notice_inventory is 1 greater than the offer integer value.
+ // Generates IM_INVENTORY_ACCEPTED, IM_TASK_INVENTORY_ACCEPTED,
+ // or IM_GROUP_NOTICE_INVENTORY_ACCEPTED
+ msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 1));
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, &(info->mFolderID.mData),
+ sizeof(info->mFolderID.mData));
+ // send the message
+ msg->sendReliable(info->mHost);
+ log_message = info->mFromName + " gave you " + info->mDesc + ".";
+ chat.mText = log_message;
+ LLFloaterChat::addChatHistory(chat);
+ // we will want to open this item when it comes back.
+ lldebugs << "Initializing an opener for tid: " << info->mTransactionID
+ << llendl;
+ switch (info->mIM)
+ {
+ case IM_INVENTORY_OFFERED:
+ {
+ // This is an offer from an agent. In this case, the back
+ // end has already copied the items into your inventory,
+ // so we can fetch it out of our inventory.
+ LLInventoryFetchObserver::item_ref_t items;
+ items.push_back(info->mObjectID);
+ LLOpenAgentOffer* open_agent_offer = new LLOpenAgentOffer;
+ open_agent_offer->fetchItems(items);
+ if(catp || (itemp && itemp->isComplete()))
+ {
+ open_agent_offer->done();
+ }
+ else
+ {
+ opener = open_agent_offer;
+ }
+ }
+ break;
+ case IM_TASK_INVENTORY_OFFERED:
+ case IM_GROUP_NOTICE:
+ case IM_GROUP_NOTICE_REQUESTED:
+ {
+ // This is an offer from a task or group.
+ // Because it would be easy
+ // to write a task which would overload your inventory, we
+ // force the offer to stay in an instant message until
+ // accepted. Thus, we have to respond, and then wait for
+ // the update to come back before we open the item.
+ LLOpenTaskOffer* open_task_offer = new LLOpenTaskOffer;
+ open_task_offer->watchItem(info->mObjectID);
+ if(itemp && itemp->isComplete())
+ {
+ opener->changed(0x0);
+ }
+ else
+ {
+ opener = open_task_offer;
+ }
+ }
+ break;
+ default:
+ llwarns << "inventory_offer_callback: unknown offer type" << llendl;
+ break;
+ }
+ break;
+
+ case 2:
+ // MUTE falls through to decline
+ case 1:
+ // DECLINE. The math for the dialog works, because the decline
+ // for inventory_offered, task_inventory_offer or
+ // group_notice_inventory is 2 greater than the offer integer value.
+ // Generates IM_INVENTORY_DECLINED, IM_TASK_INVENTORY_DECLINED,
+ // or IM_GROUP_NOTICE_INVENTORY_DECLINED
+ default:
+ // close button probably
+ msg->addU8Fast(_PREHASH_Dialog, (U8)(info->mIM + 2));
+ msg->addBinaryDataFast(_PREHASH_BinaryBucket, EMPTY_BINARY_BUCKET, EMPTY_BINARY_BUCKET_SIZE);
+ // send the message
+ msg->sendReliable(info->mHost);
+
+ log_message = "You decline " + info->mDesc + " from " + info->mFromName + ".";
+ chat.mText = log_message;
+ LLFloaterChat::addChatHistory(chat);
+
+ // If it's from an agent, we have to fetch the item to throw
+ // it away. If it's from a task or group, just denying the
+ // request will suffice to discard the item.
+ if(IM_INVENTORY_OFFERED == info->mIM)
+ {
+ LLInventoryFetchComboObserver::folder_ref_t folders;
+ LLInventoryFetchComboObserver::item_ref_t items;
+ items.push_back(info->mObjectID);
+ LLDiscardAgentOffer* discard_agent_offer;
+ discard_agent_offer = new LLDiscardAgentOffer(info->mFolderID, info->mObjectID);
+ discard_agent_offer->fetch(folders, items);
+ if(catp || (itemp && itemp->isComplete()))
+ {
+ discard_agent_offer->done();
+ }
+ else
+ {
+ opener = discard_agent_offer;
+ }
+
+ }
+ if (!info->mFromGroup)
+ {
+ busy_message(msg,info->mFromID);
+ }
+ break;
+ }
+
+ if(opener)
+ {
+ gInventory.addObserver(opener);
+ }
+
+ delete info;
+ info = NULL;
+
+ // Allow these to stack up, but once you deal with one, reset the
+ // position.
+ gFloaterView->resetStartingFloaterPosition();
+}
+
+
+void inventory_offer_handler(LLOfferInfo* info, BOOL from_task)
+{
+ switch(info->mType)
+ {
+ // For certain types, just accept the items into the inventory,
+ // and we'll automatically open them on receipt.
+ case LLAssetType::AT_NOTECARD:
+ case LLAssetType::AT_LANDMARK:
+ case LLAssetType::AT_TEXTURE:
+ {
+ // 0 = accept button
+ inventory_offer_callback(0, info);
+ //LLInventoryView::sOpenNextNewItem = TRUE;
+ }
+ break;
+
+ case LLAssetType::AT_SOUND:
+ case LLAssetType::AT_CALLINGCARD:
+ case LLAssetType::AT_SCRIPT:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_OBJECT:
+ case LLAssetType::AT_CATEGORY:
+ case LLAssetType::AT_ROOT_CATEGORY:
+ case LLAssetType::AT_LSL_TEXT:
+ case LLAssetType::AT_LSL_BYTECODE:
+ case LLAssetType::AT_TEXTURE_TGA:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_TRASH:
+ case LLAssetType::AT_SNAPSHOT_CATEGORY:
+ case LLAssetType::AT_LOST_AND_FOUND:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_GESTURE:
+ default:
+ {
+ LLString::format_map_t args;
+ args["[OBJECTNAME]"] = info->mDesc;
+ args["[OBJECTTYPE]"] = LLAssetType::lookupHumanReadable(info->mType);
+
+ // Name cache callbacks don't store userdata, so can't save
+ // off the LLOfferInfo. Argh. JC
+ BOOL name_found = FALSE;
+ char first_name[MAX_STRING];
+ char last_name[MAX_STRING];
+ if (info->mFromGroup)
+ {
+ if (gCacheName->getGroupName(info->mFromID, first_name))
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = "";
+ name_found = TRUE;
+ }
+ }
+ else
+ {
+ if (gCacheName->getName(info->mFromID, first_name, last_name))
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = last_name;
+ name_found = TRUE;
+ }
+ }
+ if (from_task)
+ {
+ args["[OBJECTFROMNAME]"] = info->mFromName;
+ if (name_found)
+ {
+ LLNotifyBox::showXml("ObjectGiveItem", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ else
+ {
+ LLNotifyBox::showXml("ObjectGiveItemUnknownUser", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ }
+ else
+ {
+ // XUI:translate -> [FIRST] [LAST]
+ args["[NAME]"] = info->mFromName;
+ LLNotifyBox::showXml("UserGiveItem", args,
+ &inventory_offer_callback, (void*)info);
+ }
+ break;
+ }
+ }
+}
+
+
+void group_vote_callback(S32 option, void *userdata)
+{
+ LLUUID *group_id = (LLUUID *)userdata;
+ if (!group_id) return;
+
+ switch(option)
+ {
+ case 0:
+ // Vote Now
+ // Open up the voting tab
+ LLFloaterGroupInfo::showFromUUID(*group_id, "voting_tab");
+ break;
+ default:
+ // Vote Later or
+ // close button
+ break;
+ }
+ delete group_id;
+ group_id = NULL;
+}
+
+struct LLLureInfo
+{
+ LLLureInfo(const LLUUID& from, const LLUUID& lure_id, BOOL godlike) :
+ mFromID(from),
+ mLureID(lure_id),
+ mGodlike(godlike)
+ {}
+
+ LLUUID mFromID;
+ LLUUID mLureID;
+ BOOL mGodlike;
+};
+
+void lure_callback(S32 option, void* user_data)
+{
+ LLLureInfo* info = (LLLureInfo*)user_data;
+ if(!info) return;
+ switch(option)
+ {
+ case 0:
+ {
+ // accept
+ send_simple_im(info->mFromID,
+ "",
+ IM_LURE_ACCEPTED,
+ info->mLureID);
+ gAgent.teleportViaLure(info->mLureID, info->mGodlike);
+ }
+ break;
+ case 1:
+ default:
+ // decline
+ send_simple_im(info->mFromID,
+ "",
+ IM_LURE_DECLINED,
+ info->mLureID);
+ break;
+ }
+ delete info;
+ info = NULL;
+}
+
+void goto_url_callback(S32 option, void* user_data)
+{
+ char* url = (char*)user_data;
+ if(1 == option)
+ {
+ LLWeb::loadURL(url);
+ }
+ delete[] url;
+}
+
+void process_improved_im(LLMessageSystem *msg, void **user_data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+ LLUUID from_id;
+ BOOL from_group;
+ LLUUID to_id;
+ U8 offline;
+ U8 d = 0;
+ LLUUID session_id;
+ U32 t;
+ char name[DB_FULL_NAME_BUF_SIZE];
+ char message[DB_IM_MSG_BUF_SIZE];
+ U32 parent_estate_id = 0;
+ LLUUID region_id;
+ LLVector3 position;
+ char buffer[DB_IM_MSG_BUF_SIZE * 2];
+ U8 binary_bucket[MTUBYTES];
+ S32 binary_bucket_size;
+ LLChat chat;
+
+ //XUI:translate - need to fix the full name to first/last
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, from_id);
+ msg->getBOOLFast(_PREHASH_MessageBlock, _PREHASH_FromGroup, from_group);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ToAgentID, to_id);
+ msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Offline, offline);
+ msg->getU8Fast( _PREHASH_MessageBlock, _PREHASH_Dialog, d);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_ID, session_id);
+ msg->getU32Fast( _PREHASH_MessageBlock, _PREHASH_Timestamp, t);
+ //msg->getData("MessageBlock", "Count", &count);
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_FromAgentName, DB_FULL_NAME_BUF_SIZE, name);
+ msg->getStringFast(_PREHASH_MessageBlock, _PREHASH_Message, DB_IM_MSG_BUF_SIZE, message);
+ msg->getU32Fast(_PREHASH_MessageBlock, _PREHASH_ParentEstateID, parent_estate_id);
+ msg->getUUIDFast(_PREHASH_MessageBlock, _PREHASH_RegionID, region_id);
+ msg->getVector3Fast(_PREHASH_MessageBlock, _PREHASH_Position, position);
+ msg->getBinaryDataFast( _PREHASH_MessageBlock, _PREHASH_BinaryBucket, binary_bucket, 0, 0, MTUBYTES);
+ binary_bucket_size = msg->getSizeFast(_PREHASH_MessageBlock, _PREHASH_BinaryBucket);
+ EInstantMessage dialog = (EInstantMessage)d;
+ time_t timestamp = (time_t)t;
+
+ BOOL is_busy = gAgent.getBusy();
+ BOOL is_muted = gMuteListp->isMuted(from_id, name);
+ BOOL is_linden = gMuteListp->isLinden(name);
+ BOOL is_owned_by_me = FALSE;
+
+ chat.mMuted = is_muted && !is_linden;
+ chat.mFromID = from_id;
+ chat.mFromName = name;
+
+ LLViewerObject *source = gObjectList.findObject(session_id);
+ if (source)
+ {
+ is_owned_by_me = source->permYouOwner();
+ }
+
+ char separator_string[3]=": ";
+ int message_offset=0;
+
+ //Handle IRC styled /me messages.
+ if (!strncmp(message, "/me ", 4) || !strncmp(message, "/me'", 4))
+ {
+ strcpy(separator_string,"");
+ message_offset=3;
+ }
+
+ LLString::format_map_t args;
+ switch(dialog)
+ {
+ case IM_CONSOLE_AND_CHAT_HISTORY:
+ // These are used for system messages, hence don't need the name,
+ // as it is always "Second Life".
+ // XUI:translate
+ args["[MESSAGE]"] = message;
+
+ // Note: don't put the message in the IM history, even though was sent
+ // via the IM mechanism.
+ LLNotifyBox::showXml("SystemMessageTip",args);
+ break;
+
+ case IM_NOTHING_SPECIAL:
+ // Don't show dialog, just do IM
+ if (!gAgent.isGodlike()
+ && gAgent.getRegion()->isPrelude()
+ && to_id.isNull() )
+ {
+ // do nothing -- don't distract newbies in
+ // Prelude with global IMs
+ }
+ else if (offline == IM_ONLINE && !is_linden && is_busy && strcmp(name, SYSTEM_FROM))
+ {
+ // return a standard "busy" message, but only do it to online IM
+ // (i.e. not other auto responses and not store-and-forward IM)
+ if (!gIMView->hasSession(session_id))
+ {
+ // if there is not a panel for this conversation (i.e. it is a new IM conversation
+ // initiated by the other party) then...
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+ LLString response = gSavedPerAccountSettings.getText("BusyModeResponse");
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ from_id,
+ my_name.c_str(),
+ response.c_str(),
+ IM_ONLINE,
+ IM_BUSY_AUTO_RESPONSE,
+ session_id);
+ gAgent.sendReliableMessage();
+ }
+
+ // now store incoming IM in chat history
+
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ }
+ llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
+
+ // add to IM panel, but do not bother the user
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ NULL,
+ dialog,
+ parent_estate_id,
+ region_id,
+ position);
+
+ // pretend this is chat generated by self, so it does not show up on screen
+ sprintf(buffer, "IM: %s%s%s", name, separator_string, (message+message_offset));
+ chat.mText = buffer;
+ LLFloaterChat::addChat( chat, TRUE, TRUE );
+ }
+ else if (from_id.isNull())
+ {
+ // Messages from "Second Life" don't go to IM history
+ sprintf(buffer, "%s: %s", name, message);
+ chat.mText = buffer;
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ }
+ else if (to_id.isNull())
+ {
+ // Message to everyone from GOD
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("GodMessage", args);
+
+ // Treat like a system message and put in chat history.
+ // Claim to be from a local agent so it doesn't go into
+ // console.
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+ chat.mText = buffer;
+ BOOL local_agent = TRUE;
+ LLFloaterChat::addChat(chat, FALSE, local_agent);
+ }
+ else
+ {
+ // standard message, not from system
+ char saved[MAX_STRING];
+ saved[0] = '\0';
+ if(offline == IM_OFFLINE)
+ {
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(saved, "(Saved %s) ",
+ formatted_time(timestamp, time_buf));
+ }
+ sprintf(buffer, "%s%s%s%s", name, separator_string, saved,(message+message_offset));
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ }
+ llinfos << "process_improved_im: session_id( " << session_id << " ), from_id( " << from_id << " )" << llendl;
+
+ if (!is_muted || is_linden)
+ {
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ NULL,
+ dialog,
+ parent_estate_id,
+ region_id,
+ position);
+ sprintf(buffer, "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset));
+
+ chat.mText = buffer;
+ BOOL local_agent = FALSE;
+ LLFloaterChat::addChat( chat, TRUE, local_agent );
+ }
+ else
+ {
+ // muted user, so don't start an IM session, just record line in chat
+ // history. Pretend the chat is from a local agent,
+ // so it will go into the history but not be shown on screen.
+ chat.mText = buffer;
+ BOOL local_agent = TRUE;
+ LLFloaterChat::addChat( chat, TRUE, local_agent );
+ }
+ }
+ break;
+
+ case IM_TYPING_START:
+ {
+ LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
+ gIMView->processIMTypingStart(im_info);
+ }
+ break;
+
+ case IM_TYPING_STOP:
+ {
+ LLPointer<LLIMInfo> im_info = new LLIMInfo(gMessageSystem);
+ gIMView->processIMTypingStop(im_info);
+ }
+ break;
+
+ case IM_MESSAGEBOX:
+ {
+ // This is a block, modeless dialog.
+ //XUI:translate
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("SystemMessage", args);
+ }
+ break;
+ case IM_GROUP_NOTICE:
+ case IM_GROUP_NOTICE_REQUESTED:
+ {
+ llinfos << "Received IM_GROUP_NOTICE message." << llendl;
+ // Read the binary bucket for more information.
+ struct notice_bucket_header_t
+ {
+ U8 has_inventory;
+ U8 asset_type;
+ LLUUID group_id;
+ };
+ struct notice_bucket_full_t
+ {
+ struct notice_bucket_header_t header;
+ U8 item_name[DB_INV_ITEM_NAME_BUF_SIZE];
+ }* notice_bin_bucket;
+
+ // Make sure the binary bucket is big enough to hold the header
+ // and a null terminated item name.
+ if ( (binary_bucket_size < (sizeof(notice_bucket_header_t) + sizeof(U8)))
+ || (binary_bucket[binary_bucket_size - 1] != '\0') )
+ {
+ llwarns << "Malformed group notice binary bucket" << llendl;
+ break;
+ }
+
+ notice_bin_bucket = (struct notice_bucket_full_t*) &binary_bucket[0];
+ U8 has_inventory = notice_bin_bucket->header.has_inventory;
+ U8 asset_type = notice_bin_bucket->header.asset_type;
+ LLUUID group_id = notice_bin_bucket->header.group_id;
+ const char* item_name = (const char*) notice_bin_bucket->item_name;
+
+ // If there is inventory, give the user the inventory offer.
+ LLOfferInfo* info = NULL;
+ if (has_inventory)
+ {
+ info = new LLOfferInfo;
+ info->mIM = IM_GROUP_NOTICE;
+ info->mFromID = from_id;
+ info->mFromGroup = from_group;
+ info->mTransactionID = session_id;
+ info->mType = (LLAssetType::EType) asset_type;
+ info->mFolderID = gInventory.findCategoryUUIDForType(info->mType);
+ std::string from_name;
+
+ from_name += "A group member named ";
+ from_name += name;
+
+ info->mFromName = from_name;
+ info->mDesc = item_name;
+ info->mHost = msg->getSender();
+ }
+
+ std::string str(message);
+
+ // Tokenize the string.
+ // TODO: Support escaped tokens ("||" -> "|")
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("|","",boost::keep_empty_tokens);
+ tokenizer tokens(str, sep);
+ tokenizer::iterator iter = tokens.begin();
+
+ LLString subj(*iter++);
+ LLString mes(*iter++);
+
+ if (IM_GROUP_NOTICE == dialog)
+ {
+ subj += "\n";
+ mes = "\n\n" + mes;
+ LLGroupNotifyBox::show(subj.c_str(),mes.c_str(),name,group_id,t,has_inventory,item_name,info);
+ }
+ else if (IM_GROUP_NOTICE_REQUESTED == dialog)
+ {
+ LLFloaterGroupInfo::showNotice(subj.c_str(),mes.c_str(),group_id,has_inventory,item_name,info);
+ }
+ }
+ break;
+ case IM_GROUP_INVITATION:
+ {
+ //if (!is_linden && (is_busy || is_muted))
+ if ((is_busy || is_muted))
+ {
+ LLMessageSystem *msg = gMessageSystem;
+ join_group_callback(1, NULL);
+ busy_message(msg,from_id);
+ }
+ else
+ {
+ llinfos << "Received IM_GROUP_INVITATION message." << llendl;
+ // Read the binary bucket for more information.
+ struct invite_bucket_t
+ {
+ S32 membership_fee;
+ LLUUID role_id;
+ }* invite_bucket;
+
+ // Make sure the binary bucket is the correct size.
+ if (binary_bucket_size != sizeof(invite_bucket_t))
+ {
+ llwarns << "Malformed group invite binary bucket" << llendl;
+ break;
+ }
+
+ invite_bucket = (struct invite_bucket_t*) &binary_bucket[0];
+ S32 membership_fee = ntohl(invite_bucket->membership_fee);
+
+ LLJoinGroupData* userdata = new LLJoinGroupData;
+ userdata->mTransactionID = session_id;
+ userdata->mGroupID = from_id;
+ userdata->mName.assign(name);
+ userdata->mMessage.assign(message);
+ userdata->mFee = membership_fee;
+
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("JoinGroup", args,
+ &join_group_callback,
+ (void*)userdata);
+ }
+ }
+ break;
+
+ case IM_INVENTORY_OFFERED:
+ case IM_TASK_INVENTORY_OFFERED:
+ // Someone has offered us some inventory.
+ {
+ LLOfferInfo* info = new LLOfferInfo;
+
+ if (IM_INVENTORY_OFFERED == dialog)
+ {
+ struct offer_agent_bucket_t
+ {
+ S8 asset_type;
+ LLUUID object_id;
+ }* bucketp;
+
+ if (sizeof(offer_agent_bucket_t) != binary_bucket_size)
+ {
+ llwarns << "Malformed inventory offer from agent" << llendl;
+ break;
+ }
+ bucketp = (struct offer_agent_bucket_t*) &binary_bucket[0];
+ info->mType = (LLAssetType::EType) bucketp->asset_type;
+ info->mObjectID = bucketp->object_id;
+ }
+ else
+ {
+ if (sizeof(S8) != binary_bucket_size)
+ {
+ llwarns << "Malformed inventory offer from object" << llendl;
+ break;
+ }
+ info->mType = (LLAssetType::EType) binary_bucket[0];
+ info->mObjectID = LLUUID::null;
+ }
+
+ info->mIM = dialog;
+ info->mFromID = from_id;
+ info->mFromGroup = from_group;
+ info->mTransactionID = session_id;
+ info->mFolderID = gInventory.findCategoryUUIDForType(info->mType);
+
+ if (dialog == IM_TASK_INVENTORY_OFFERED)
+ {
+ info->mFromObject = TRUE;
+ }
+ else
+ {
+ info->mFromObject = FALSE;
+ }
+ info->mFromName = name;
+ info->mDesc = message;
+ info->mHost = msg->getSender();
+ //if (!is_linden && ((is_busy && !is_owned_by_me) || is_muted))
+ if (((is_busy && !is_owned_by_me) || is_muted))
+ {
+ // Same as closing window
+ inventory_offer_callback(-1, info);
+ }
+ else
+ {
+ if (dialog == IM_TASK_INVENTORY_OFFERED)
+ inventory_offer_handler(info, TRUE);
+ else
+ inventory_offer_handler(info, FALSE);
+ }
+ }
+ break;
+
+ case IM_INVENTORY_ACCEPTED:
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("InventoryAccepted", args);
+ break;
+ }
+ case IM_INVENTORY_DECLINED:
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("InventoryDeclined", args);
+ break;
+ }
+ case IM_GROUP_VOTE:
+ {
+ LLUUID *userdata = new LLUUID(session_id);
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("GroupVote", args,
+ &group_vote_callback, userdata);
+ }
+ break;
+
+ case IM_GROUP_ELECTION_DEPRECATED:
+ {
+ llwarns << "Received IM: IM_GROUP_ELECTION_DEPRECATED" << llendl;
+ }
+ break;
+ case IM_SESSION_SEND:
+ {
+ if (!is_linden && is_busy)
+ {
+ return;
+ }
+
+ // standard message, not from system
+ char saved[MAX_STRING];
+ saved[0] = '\0';
+ if(offline == IM_OFFLINE)
+ {
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(saved,
+ "(Saved %s) ",
+ formatted_time(timestamp, time_buf));
+ }
+ sprintf(buffer, "%s%s%s%s", name, separator_string, saved, (message+message_offset));
+ BOOL is_this_agent = FALSE;
+ if(from_id == gAgentID)
+ {
+ from_id = LLUUID::null;
+ is_this_agent = TRUE;
+ }
+ gIMView->addMessage(
+ session_id,
+ from_id,
+ name,
+ buffer,
+ (char*)binary_bucket,
+ IM_SESSION_ADD,
+ parent_estate_id,
+ region_id,
+ position);
+
+ sprintf(buffer, "IM: %s%s%s%s", name, separator_string, saved, (message+message_offset));
+ chat.mText = buffer;
+ LLFloaterChat::addChat(chat, TRUE, is_this_agent);
+ }
+ break;
+
+ case IM_FROM_TASK:
+ if (is_busy && !is_owned_by_me)
+ {
+ return;
+ }
+ sprintf(buffer, "%s%s%s", name, separator_string, (message+message_offset));
+ // Note: lie to LLFloaterChat::addChat(), pretending that this is NOT an IM, because
+ // IMs from objcts don't open IM sessions.
+ chat.mText = buffer;
+ chat.mSourceType = CHAT_SOURCE_OBJECT;
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ break;
+ case IM_FROM_TASK_AS_ALERT:
+ if (is_busy && !is_owned_by_me)
+ {
+ return;
+ }
+ {
+ // Construct a viewer alert for this message.
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("ObjectMessage", args);
+ }
+ break;
+ case IM_BUSY_AUTO_RESPONSE:
+ gIMView->addMessage(session_id, from_id, name, message);
+ break;
+
+ case IM_LURE_USER:
+ {
+ if (is_muted)
+ {
+ return;
+ }
+ else if (is_busy)
+ {
+ busy_message(msg,from_id);
+ }
+ else
+ {
+ // XUI:translate -> [FIRST] [LAST]
+ LLLureInfo* info = new LLLureInfo(from_id, session_id, FALSE);
+ args["[NAME]"] = name;
+ args["[MESSAGE]"] = message;
+ LLNotifyBox::showXml("OfferTeleport", args,
+ lure_callback, (void*)info);
+ }
+ }
+ break;
+
+ case IM_GODLIKE_LURE_USER:
+ {
+ LLLureInfo* info = new LLLureInfo(from_id, session_id, TRUE);
+ // do not show a message box, because you're about to be
+ // teleported.
+ lure_callback(0, (void *)info);
+ }
+ break;
+
+ case IM_LURE_911:
+ {
+ // HACK -- the from_id is the im_session_id
+ LLFloaterIMPanel* panel = gIMView->findFloaterBySession(session_id);
+ if (panel)
+ {
+ panel->addTeleportButton(from_id);
+ }
+ else
+ {
+ llinfos << "LLFloaterIMPanel not found for " << session_id << " from " << from_id << llendl;
+ }
+ }
+ break;
+
+ case IM_GOTO_URL:
+ {
+ char* url = new char[binary_bucket_size];
+ strcpy(url, (char*)binary_bucket);
+ args["[MESSAGE]"] = message;
+ args["[URL]"] = url;
+ LLNotifyBox::showXml("GotoURL", args,
+ goto_url_callback, (void*)url);
+ }
+ break;
+
+ case IM_FRIENDSHIP_OFFERED:
+ {
+ LLFriendshipOffer* offer = new LLFriendshipOffer;
+ offer->mFromID = from_id;
+ offer->mTransactionID = session_id;
+ offer->mOnline = (offline == IM_ONLINE);
+ offer->mHost = msg->getSender();
+
+ if (is_busy)
+ {
+ busy_message(msg, from_id);
+ friendship_offer_callback(1, (void*)offer);
+ }
+ else if (is_muted)
+ {
+ friendship_offer_callback(1, (void*)offer);
+ }
+ else
+ {
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("OfferFriendship", args,
+ &friendship_offer_callback, (void*)offer);
+ }
+ }
+ break;
+
+ case IM_FRIENDSHIP_ACCEPTED:
+ {
+ // In the case of an offline IM, the formFriendship() may be extraneous
+ // as the database should already include the relationship. But it
+ // doesn't hurt for dupes.
+ LLAvatarTracker::formFriendship(from_id);
+
+ std::vector<std::string> strings;
+ strings.push_back( from_id.getString() );
+ send_generic_message("requestonlinenotification", strings);
+
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("FriendshipAccepted", args);
+ }
+ break;
+
+ case IM_FRIENDSHIP_DECLINED:
+ args["[NAME]"] = name;
+ LLNotifyBox::showXml("FriendshipDeclined", args);
+ break;
+
+ default:
+ llwarns << "Instant message calling for unknown dialog "
+ << (S32)dialog << llendl;
+ break;
+ }
+
+ LLWindow* viewer_window = gViewerWindow->getWindow();
+ if (viewer_window && viewer_window->getMinimized())
+ {
+ viewer_window->flashIcon(5.f);
+ }
+}
+
+void busy_message (LLMessageSystem* msg, LLUUID from_id)
+{
+ if (gAgent.getBusy())
+ {
+ LLString response = gSavedPerAccountSettings.getText("BusyModeResponse");
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ from_id,
+ SYSTEM_FROM,
+ response.c_str(),
+ IM_ONLINE,
+ IM_CONSOLE_AND_CHAT_HISTORY);
+ gAgent.sendReliableMessage();
+ }
+}
+
+void friendship_offer_callback(S32 option, void* user_data)
+{
+ LLFriendshipOffer* offer = (LLFriendshipOffer*)user_data;
+ if(!offer) return;
+ LLUUID fid;
+ LLMessageSystem* msg = gMessageSystem;
+ switch(option)
+ {
+ case 0:
+ // accept
+ LLAvatarTracker::formFriendship(offer->mFromID);
+
+ fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+
+ // This will also trigger an onlinenotification if the user is online
+ msg->newMessageFast(_PREHASH_AcceptFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID);
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, fid);
+ msg->sendReliable(offer->mHost);
+ break;
+ case 1:
+ // decline
+ msg->newMessageFast(_PREHASH_DeclineFriendship);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offer->mTransactionID);
+ msg->sendReliable(offer->mHost);
+ break;
+ default:
+ // close button probably, possibly timed out
+ break;
+ }
+
+ delete offer;
+ offer = NULL;
+}
+
+struct LLCallingCardOfferData
+{
+ LLUUID mTransactionID;
+ LLUUID mSourceID;
+ LLHost mHost;
+};
+
+void callingcard_offer_callback(S32 option, void* user_data)
+{
+ LLCallingCardOfferData* offerdata = (LLCallingCardOfferData*)user_data;
+ if(!offerdata) return;
+ LLUUID fid;
+ LLUUID from_id;
+ LLMessageSystem* msg = gMessageSystem;
+ switch(option)
+ {
+ case 0:
+ // accept
+ msg->newMessageFast(_PREHASH_AcceptCallingCard);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID);
+ fid = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ msg->nextBlockFast(_PREHASH_FolderData);
+ msg->addUUIDFast(_PREHASH_FolderID, fid);
+ msg->sendReliable(offerdata->mHost);
+ break;
+ case 1:
+ // decline
+ msg->newMessageFast(_PREHASH_DeclineCallingCard);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_TransactionBlock);
+ msg->addUUIDFast(_PREHASH_TransactionID, offerdata->mTransactionID);
+ msg->sendReliable(offerdata->mHost);
+ busy_message(msg, offerdata->mSourceID);
+ break;
+ default:
+ // close button probably, possibly timed out
+ break;
+ }
+
+ delete offerdata;
+ offerdata = NULL;
+}
+
+void process_offer_callingcard(LLMessageSystem* msg, void**)
+{
+ // someone has offered to form a friendship
+ lldebugs << "callingcard offer" << llendl;
+
+ LLUUID source_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, source_id);
+ LLUUID tid;
+ msg->getUUIDFast(_PREHASH_AgentBlock, _PREHASH_TransactionID, tid);
+
+ LLCallingCardOfferData* offerdata = new LLCallingCardOfferData;
+ offerdata->mTransactionID = tid;
+ offerdata->mSourceID = source_id;
+ offerdata->mHost = msg->getSender();
+
+ LLViewerObject* source = gObjectList.findObject(source_id);
+ LLString::format_map_t args;
+ std::string source_name;
+ if(source && source->isAvatar())
+ {
+ LLNameValue* nvfirst = source->getNVPair("FirstName");
+ LLNameValue* nvlast = source->getNVPair("LastName");
+ if (nvfirst && nvlast)
+ {
+ args["[FIRST]"] = nvfirst->getString();
+ args["[LAST]"] = nvlast->getString();
+ source_name = std::string(nvfirst->getString()) + " " + nvlast->getString();
+ }
+ }
+
+ if(!source_name.empty())
+ {
+ if (gAgent.getBusy()
+ || gMuteListp->isMuted(source_id, source_name))
+ {
+ // automatically decline offer
+ callingcard_offer_callback(1, (void*)offerdata);
+ return;
+ }
+
+ LLNotifyBox::showXml("OfferCallingCard", args,
+ &callingcard_offer_callback, (void*)offerdata);
+ }
+ else
+ {
+ llwarns << "Calling card offer from an unknown source." << llendl;
+ }
+}
+
+void process_accept_callingcard(LLMessageSystem* msg, void**)
+{
+ LLNotifyBox::showXml("CallingCardAccepted");
+}
+
+void process_decline_callingcard(LLMessageSystem* msg, void**)
+{
+ LLNotifyBox::showXml("CallingCardDeclined");
+}
+
+
+void process_chat_from_simulator(LLMessageSystem *msg, void **user_data)
+{
+ LLChat chat;
+ char mesg[DB_CHAT_MSG_BUF_SIZE];
+ char from_name[DB_FULL_NAME_BUF_SIZE];
+ U8 source_temp;
+ U8 type_temp;
+ U8 audible_temp;
+ LLColor4 color(1.0f, 1.0f, 1.0f, 1.0f);
+ LLUUID from_id;
+ LLUUID owner_id;
+ BOOL is_owned_by_me = FALSE;
+ LLViewerObject* chatter;
+
+ msg->getString("ChatData", "FromName", DB_FULL_NAME_BUF_SIZE, from_name);
+ chat.mFromName = from_name;
+
+ msg->getUUID("ChatData", "SourceID", from_id);
+ chat.mFromID = from_id;
+
+ // Object owner for objects
+ msg->getUUID("ChatData", "OwnerID", owner_id);
+
+ msg->getU8Fast(_PREHASH_ChatData, _PREHASH_SourceType, source_temp);
+ chat.mSourceType = (EChatSourceType)source_temp;
+
+ msg->getU8("ChatData", "ChatType", type_temp);
+ chat.mChatType = (EChatType)type_temp;
+
+ msg->getU8Fast(_PREHASH_ChatData, _PREHASH_Audible, audible_temp);
+ chat.mAudible = (EChatAudible)audible_temp;
+
+ chat.mTime = LLFrameTimer::getElapsedSeconds();
+
+ BOOL is_self = (from_id == gAgent.getID());
+ BOOL is_busy = gAgent.getBusy();
+
+ // Apparently you can receive chat before app is fully initialized, hence
+ // gMuteListp can be null. JC
+ BOOL is_muted = FALSE;
+ BOOL is_linden = FALSE;
+ if (gMuteListp)
+ {
+ is_muted = gMuteListp->isMuted(from_id, from_name)
+ || gMuteListp->isMuted(owner_id);
+ is_linden = gMuteListp->isLinden(from_name);
+ }
+
+ chatter = gObjectList.findObject(from_id);
+ if (chatter)
+ {
+ chat.mPosAgent = chatter->getPositionAgent();
+
+ // Make swirly things only for talking objects. (not script debug messages, though)
+ if (chat.mSourceType == CHAT_SOURCE_OBJECT
+ && chat.mChatType != CHAT_TYPE_DEBUG_MSG)
+ {
+ LLViewerPartSourceChat *psc = new LLViewerPartSourceChat(chatter->getPositionAgent());
+ psc->setSourceObject(chatter);
+ psc->setColor(color);
+ //We set the particles to be owned by the object's owner,
+ //just in case they should be muted by the mute list
+ psc->setOwnerUUID(owner_id);
+ gWorldPointer->mPartSim.addPartSource(psc);
+ }
+
+ // only pay attention to other people chatting
+ if ((is_linden || (!is_muted && !is_busy))
+ && chatter != gAgent.getAvatarObject())
+ {
+ gAgent.heardChat(chat);
+ if (gLindenLabRandomNumber.llrand(2) == 0)
+ {
+ gAgent.setLookAt(LOOKAT_TARGET_AUTO_LISTEN, chatter, LLVector3::zero);
+ }
+ }
+
+ is_owned_by_me = chatter->permYouOwner();
+ }
+
+ BOOL is_audible = (CHAT_AUDIBLE_FULLY == chat.mAudible);
+ if (is_audible)
+ {
+ BOOL visible_in_chat_bubble = FALSE;
+ std::string verb;
+
+ color.setVec(1,1,1,1);
+ msg->getStringFast(_PREHASH_ChatData, _PREHASH_Message, DB_CHAT_MSG_BUF_SIZE, mesg);
+
+ BOOL ircstyle = FALSE;
+
+ // Look for IRC-style emotes here so chatbubbles work
+ if (!strncmp(mesg, "/me ", 4) || !strncmp(mesg, "/me'", 4))
+ {
+ chat.mText = from_name;
+ chat.mText += (mesg + 3);
+ ircstyle = TRUE;
+ }
+ else
+ {
+ chat.mText = mesg;
+ }
+
+ // Look for the start of typing so we can put "..." in the bubbles.
+ if (CHAT_TYPE_START == chat.mChatType)
+ {
+ // Might not have the avatar constructed yet, eg on login.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->startTyping();
+ }
+ return;
+ }
+ else if (CHAT_TYPE_STOP == chat.mChatType)
+ {
+ // Might not have the avatar constructed yet, eg on login.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->stopTyping();
+ }
+ return;
+ }
+
+ // We have a real utterance now, so can stop showing "..." and proceed.
+ if (chatter && chatter->isAvatar())
+ {
+ ((LLVOAvatar*)chatter)->stopTyping();
+
+ if (!is_muted && !is_busy)
+ {
+ visible_in_chat_bubble = gSavedSettings.getBOOL("UseChatBubbles");
+ ((LLVOAvatar*)chatter)->addChat(chat);
+ }
+ }
+
+ // Look for IRC-style emotes
+ if (ircstyle)
+ {
+ // Do nothing, ircstyle is fixed above for chat bubbles
+ }
+ else
+ {
+ switch(chat.mChatType)
+ {
+ case CHAT_TYPE_WHISPER:
+ if (is_self)
+ {
+ verb = " whisper: ";
+ }
+ else
+ {
+ verb = " whispers: ";
+ }
+ break;
+ case CHAT_TYPE_DEBUG_MSG:
+ case CHAT_TYPE_NORMAL:
+ verb = ": ";
+ break;
+ case CHAT_TYPE_SHOUT:
+ if (is_self)
+ {
+ verb = " shout: ";
+ }
+ else
+ {
+ verb = " shouts: ";
+ }
+ break;
+ case CHAT_TYPE_START:
+ case CHAT_TYPE_STOP:
+ llwarns << "Got chat type start/stop in main chat processing." << llendl;
+ break;
+ default:
+ llwarns << "Unknown type " << chat.mChatType << " in chat!" << llendl;
+ verb = " say, ";
+ break;
+ }
+
+ if (is_self)
+ {
+ chat.mText = "You";
+ }
+ else
+ {
+ chat.mText = from_name;
+ }
+ chat.mText += verb;
+ chat.mText += mesg;
+ }
+
+ if (chatter)
+ {
+ chat.mPosAgent = chatter->getPositionAgent();
+ }
+
+ // truth table:
+ // LINDEN BUSY MUTED OWNED_BY_YOU DISPLAY STORE IN HISTORY
+ // F F F F Yes Yes
+ // F F F T Yes Yes
+ // F F T F No No
+ // F F T T No No
+ // F T F F No Yes
+ // F T F T Yes Yes
+ // F T T F No No
+ // F T T T No No
+ // T * * * Yes Yes
+
+ chat.mMuted = is_muted && !is_linden;
+
+
+ if (!visible_in_chat_bubble
+ && (is_linden || !is_busy || is_owned_by_me))
+ {
+ // show on screen and add to history
+ LLFloaterChat::addChat(chat, FALSE, FALSE);
+ }
+ else
+ {
+ // just add to chat history
+ LLFloaterChat::addChatHistory(chat);
+ }
+ }
+}
+
+/*
+void process_agent_to_new_region(LLMessageSystem *mesgsys, void **user_data)
+{
+// LLFastTimer t(LLFastTimer::FTM_TEMP8);
+
+ U64 handle;
+ U32 ip;
+ U16 port;
+ LLUUID session_id;
+
+ // Actually, the agent itself should process this message.
+ // From a "AgentToNewRegion" message
+ mesgsys->getIPAddrFast(_PREHASH_RegionData, _PREHASH_IP, ip);
+ mesgsys->getIPPortFast(_PREHASH_RegionData, _PREHASH_Port, port);
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_Handle, handle);
+ mesgsys->getUUIDFast(_PREHASH_RegionData, _PREHASH_SessionID, session_id);
+
+ if (gAgent.getSessionID() != session_id)
+ {
+ llwarns << "Got AgentToNewRegion with invalid session ID, ignoring" << llendl;
+ return;
+ }
+
+ LLViewerRegion *regionp;
+
+ F32 x, y;
+ from_region_handle(handle, &x, &y);
+ regionp = gWorldp->getRegionFromHandle(handle);
+ if (!regionp)
+ {
+ if (gAgent.getRegion())
+ {
+ llwarns << "current region " << gAgent.getRegion()->getOriginGlobal() << llendl;
+ }
+
+ llwarns << "Agent being sent to invalid home region: "
+ << x << ":" << y
+ << " current pos " << gAgent.getPositionGlobal()
+ << llendl;
+ do_disconnect("You were sent to an invalid region.");
+ return;
+
+ }
+
+ if (regionp == gAgent.getRegion())
+ {
+ llinfos << "Agent being sent to current home region, skipping." << llendl;
+ return;
+ }
+
+
+ llinfos << "AgentToNewRegion - being sent to " << x << ":" << y
+ << ""
+
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal());
+
+ gAgent.setRegion(regionp);
+
+ gObjectList.shiftObjects(shift_vector);
+
+ llinfos << "Changing home region to " << x << ":" << y << llendl;
+
+ // send camera update to new region
+
+ send_agent_update(TRUE, TRUE);
+
+ // set our upstream asset provider to the new simulator
+ LLHost upstream(ip, port);
+ gAssetStorage->setUpstream(upstream);
+ gCacheName->setUpstream(upstream);
+
+ // Not needed, as simulator will always send request as it creates the new
+ // agent in the new region.
+ // send_current_avatar_info();
+}
+*/
+
+// Simulator we're on is informing the viewer that the agent
+// is starting to teleport (perhaps to another sim, perhaps to the
+// same sim). If we initiated the teleport process by sending some kind
+// of TeleportRequest, then this info is redundant, but if the sim
+// initiated the teleport (via a script call, being killed, etc.)
+// then this info is news to us.
+void process_teleport_start(LLMessageSystem *msg, void**)
+{
+ U32 teleport_flags = 0x0;
+ msg->getU32("Info", "TeleportFlags", teleport_flags);
+
+ if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL)
+ {
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+ }
+ else
+ {
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel");
+ }
+
+ // Freeze the UI and show progress bar
+ // Note: could add data here to differentiate between normal teleport and death.
+
+ if( gAgent.getTeleportState() == LLAgent::TELEPORT_NONE )
+ {
+ gTeleportDisplay = TRUE;
+ gAgent.setTeleportState( LLAgent::TELEPORT_START );
+ make_ui_sound("UISndTeleportOut");
+
+ // Don't call LLFirstUse::useTeleport here because this could be
+ // due to being killed, which would send you home, not to a Telehub
+ }
+}
+
+void process_teleport_progress(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if((gAgent.getID() != agent_id)
+ || (gAgent.getTeleportState() == LLAgent::TELEPORT_NONE))
+ {
+ llwarns << "Unexpected teleport progress message." << llendl;
+ return;
+ }
+ U32 teleport_flags = 0x0;
+ msg->getU32("Info", "TeleportFlags", teleport_flags);
+ if (teleport_flags & TELEPORT_FLAGS_DISABLE_CANCEL)
+ {
+ gViewerWindow->setProgressCancelButtonVisible(FALSE, "");
+ }
+ else
+ {
+ gViewerWindow->setProgressCancelButtonVisible(TRUE, "Cancel");
+ }
+ char buffer[MAX_STRING];
+ msg->getString("Info", "Message", MAX_STRING, buffer);
+ lldebugs << "teleport progress: " << buffer << llendl;
+ gAgent.setTeleportMessage(buffer);
+}
+
+class LLFetchInWelcomeArea : public LLInventoryFetchDescendentsObserver
+{
+public:
+ LLFetchInWelcomeArea() {}
+ virtual void done()
+ {
+ LLIsType is_landmark(LLAssetType::AT_LANDMARK);
+ LLIsType is_card(LLAssetType::AT_CALLINGCARD);
+ LLInventoryModel::cat_array_t cats;
+ LLInventoryModel::item_array_t items;
+ folder_ref_t::iterator it = mCompleteFolders.begin();
+ folder_ref_t::iterator end = mCompleteFolders.end();
+ for(; it != end; ++it)
+ {
+ gInventory.collectDescendentsIf(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_landmark);
+ gInventory.collectDescendentsIf(
+ (*it),
+ cats,
+ items,
+ LLInventoryModel::EXCLUDE_TRASH,
+ is_card);
+ }
+ S32 count = items.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ LLString::format_map_t args;
+ args["[NAME]"] = items[i]->getName();
+ switch(items[i]->getType())
+ {
+ case LLAssetType::AT_LANDMARK:
+ LLNotifyBox::showXml("TeleportToLandmark",args);
+ break;
+ case LLAssetType::AT_CALLINGCARD:
+ LLNotifyBox::showXml("TeleportToPerson",args);
+ break;
+ default:
+ break;
+ }
+ }
+ gInventory.removeObserver(this);
+ delete this;
+ }
+};
+
+// Teleport notification from the simulator
+// We're going to pretend to be a new agent
+void process_teleport_finish(LLMessageSystem* msg, void**)
+{
+ //llinfos << "Got teleport location message" << llendl;
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Got teleport notification for wrong agent!" << llendl;
+ return;
+ }
+
+ // Do teleport effect for where you're leaving
+ // VEFFECT: TeleportStart
+ LLHUDEffectSpiral *effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+
+ U32 location_id;
+ U32 sim_ip;
+ U16 sim_port;
+ LLVector3 pos, look_at;
+ U64 region_handle;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id);
+ msg->getIPAddrFast(_PREHASH_Info, _PREHASH_SimIP, sim_ip);
+ msg->getIPPortFast(_PREHASH_Info, _PREHASH_SimPort, sim_port);
+ //msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos);
+ //msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at);
+ msg->getU64Fast(_PREHASH_Info, _PREHASH_RegionHandle, region_handle);
+ U32 teleport_flags;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags);
+
+
+ char seedCap[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_Info, _PREHASH_SeedCapability,
+ STD_STRING_BUF_SIZE, seedCap);
+
+ // update home location if we are teleporting out of prelude
+ if((teleport_flags & TELEPORT_FLAGS_SET_HOME_TO_TARGET)
+ && (!gAgent.isGodlike()))
+ {
+ gAgent.setHomePosRegion(region_handle, pos);
+
+ // get callingcards and landmarks available to the user arriving.
+ LLInventoryFetchDescendentsObserver::folder_ref_t folders;
+ LLUUID folder_id;
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CALLINGCARD);
+ if(folder_id.notNull()) folders.push_back(folder_id);
+ folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK);
+ if(folder_id.notNull()) folders.push_back(folder_id);
+ if(!folders.empty())
+ {
+ LLFetchInWelcomeArea* fetcher = new LLFetchInWelcomeArea;
+ fetcher->fetchDescendents(folders);
+ if(fetcher->isEverythingComplete())
+ {
+ fetcher->done();
+ }
+ else
+ {
+ gInventory.addObserver(fetcher);
+ }
+ }
+ }
+
+ LLHost sim_host(sim_ip, sim_port);
+
+ // Viewer trusts the simulator.
+ gMessageSystem->enableCircuit(sim_host, TRUE);
+ LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host);
+
+/*
+ // send camera update to new region
+ gAgent.updateCamera();
+
+ // likewise make sure the camera is behind the avatar
+ gAgent.resetView(TRUE);
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(gAgent.getRegion()->getOriginGlobal());
+ gAgent.setRegion(regionp);
+ gObjectList.shiftObjects(shift_vector);
+
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->clearChatText();
+ gAgent.slamLookAt(look_at);
+ }
+ gAgent.setPositionAgent(pos);
+ gAssetStorage->setUpstream(sim);
+ gCacheName->setUpstream(sim);
+*/
+
+ // now, use the circuit info to tell simulator about us!
+ llinfos << "process_teleport_finish() Enabling "
+ << sim_host << " with code " << msg->mOurCircuitCode << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(sim_host);
+
+ send_complete_agent_movement(sim_host);
+ gAgent.setTeleportState( LLAgent::TELEPORT_MOVING );
+ gAgent.setTeleportMessage("Contacting New Region...");
+
+ regionp->setSeedCapability(std::string(seedCap));
+
+ // Don't send camera updates to the new region until we're
+ // actually there...
+
+
+ // Now do teleport effect for where you're going.
+ // VEFFECT: TeleportEnd
+ effectp = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_POINT, TRUE);
+ effectp->setPositionGlobal(gAgent.getPositionGlobal());
+
+ effectp->setColor(LLColor4U(gAgent.getEffectColor()));
+ gHUDManager->sendEffects();
+
+// gTeleportDisplay = TRUE;
+// gTeleportDisplayTimer.reset();
+// gViewerWindow->setShowProgress(TRUE);
+
+ // This could be first use of teleport, so test for that
+ LLFirstUse::useTeleport();
+}
+
+// stuff we have to do every time we get an AvatarInitComplete from a sim
+/*
+void process_avatar_init_complete(LLMessageSystem* msg, void**)
+{
+ LLVector3 agent_pos;
+ msg->getVector3Fast(_PREHASH_AvatarData, _PREHASH_Position, agent_pos);
+ agent_movement_complete(msg->getSender(), agent_pos);
+}
+*/
+
+void process_agent_movement_complete(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id))
+ {
+ llwarns << "Incorrect id in process_agent_movement_complete()"
+ << llendl;
+ return;
+ }
+
+ llinfos << "process_agent_movement_complete()" << llendl;
+
+ // *FIX: check timestamp.
+ LLVector3 agent_pos;
+ msg->getVector3Fast(_PREHASH_Data, _PREHASH_Position, agent_pos);
+ LLVector3 look_at;
+ msg->getVector3Fast(_PREHASH_Data, _PREHASH_LookAt, look_at);
+ U64 region_handle;
+ msg->getU64Fast(_PREHASH_Data, _PREHASH_RegionHandle, region_handle);
+
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (!avatarp)
+ {
+ // Could happen if you were immediately god-teleported away on login,
+ // maybe other cases. Continue, but warn. JC
+ llwarns << "agent_movement_complete() with NULL avatarp." << llendl;
+ }
+
+ F32 x, y;
+ from_region_handle(region_handle, &x, &y);
+ LLViewerRegion* regionp = gWorldp->getRegionFromHandle(region_handle);
+ if (!regionp)
+ {
+ if (gAgent.getRegion())
+ {
+ llwarns << "current region " << gAgent.getRegion()->getOriginGlobal() << llendl;
+ }
+
+ llwarns << "Agent being sent to invalid home region: "
+ << x << ":" << y
+ << " current pos " << gAgent.getPositionGlobal()
+ << llendl;
+ do_disconnect("You were sent to an invalid region.");
+ return;
+
+ }
+
+ llinfos << "Changing home region to " << x << ":" << y << llendl;
+
+ // set our upstream host the new simulator and shuffle things as
+ // appropriate.
+ LLVector3 shift_vector = regionp->getPosRegionFromGlobal(
+ gAgent.getRegion()->getOriginGlobal());
+ gAgent.setRegion(regionp);
+ gObjectList.shiftObjects(shift_vector);
+ gAssetStorage->setUpstream(msg->getSender());
+ gCacheName->setUpstream(msg->getSender());
+ gViewerThrottle.sendToSim();
+ gViewerWindow->sendShapeToSim();
+
+ bool is_teleport = false;
+
+ if( gAgent.getTeleportState() == LLAgent::TELEPORT_MOVING )
+ {
+ // Force the camera back onto the agent, don't animate. JC
+ gAgent.setFocusOnAvatar(TRUE, FALSE);
+ gAgent.slamLookAt(look_at);
+ gAgent.updateCamera();
+
+ gAgent.setTeleportState( LLAgent::TELEPORT_START_ARRIVAL );
+
+ // set the appearance on teleport since the new sim does not
+ // know what you look like.
+ gAgent.sendAgentSetAppearance();
+
+ if (avatarp)
+ {
+ avatarp->setPositionAgent(agent_pos);
+ avatarp->clearChat();
+ avatarp->slamPosition();
+ }
+
+ is_teleport = true;
+ }
+ else
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+
+ if ( LLTracker::isTracking(NULL) )
+ {
+ // Check distance to beacon, if < 5m, remove beacon
+ LLVector3d beacon_pos = LLTracker::getTrackedPositionGlobal();
+ LLVector3 beacon_dir(agent_pos.mV[VX] - (F32)fmod(beacon_pos.mdV[VX], 256.0), agent_pos.mV[VY] - (F32)fmod(beacon_pos.mdV[VY], 256.0), 0);
+ if (beacon_dir.magVecSquared() < 25.f)
+ {
+ LLTracker::stopTracking(NULL);
+ }
+ else if ( is_teleport )
+ {
+ //look at the beacon
+ LLVector3 global_agent_pos = agent_pos;
+ global_agent_pos[0] += x;
+ global_agent_pos[1] += y;
+ look_at = (LLVector3)beacon_pos - global_agent_pos;
+ look_at.normVec();
+ gAgent.slamLookAt(look_at);
+ }
+ }
+
+ // TODO: Put back a check for flying status! DK 12/19/05
+ // Sim tells us whether the new position is off the ground
+ /*
+ if (teleport_flags & TELEPORT_FLAGS_IS_FLYING)
+ {
+ gAgent.setFlying(TRUE);
+ }
+ else
+ {
+ gAgent.setFlying(FALSE);
+ }
+ */
+
+ send_agent_update(TRUE, TRUE);
+
+ if (gAgent.getRegion()->getBlockFly())
+ {
+ gAgent.setFlying(gAgent.canFly());
+ }
+
+ // force simulator to recognize busy state
+ if (gAgent.getBusy())
+ {
+ gAgent.setBusy();
+ }
+ else
+ {
+ gAgent.clearBusy();
+ }
+
+ if (avatarp)
+ {
+ avatarp->mFootPlane.clearVec();
+ }
+
+ // reset always run status
+ msg->newMessageFast(_PREHASH_SetAlwaysRun);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addBOOLFast(_PREHASH_AlwaysRun, gAgent.getAlwaysRun());
+ gAgent.sendReliableMessage();
+}
+
+void process_crossed_region(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ LLUUID session_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_SessionID, session_id);
+ if((gAgent.getID() != agent_id) || (gAgent.getSessionID() != session_id))
+ {
+ llwarns << "Incorrect id in process_crossed_region()"
+ << llendl;
+ return;
+ }
+ llinfos << "process_crossed_region()" << llendl;
+
+ U32 sim_ip;
+ msg->getIPAddrFast(_PREHASH_RegionData, _PREHASH_SimIP, sim_ip);
+ U16 sim_port;
+ msg->getIPPortFast(_PREHASH_RegionData, _PREHASH_SimPort, sim_port);
+ LLHost sim_host(sim_ip, sim_port);
+ U64 region_handle;
+ msg->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+
+ char seedCap[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_RegionData, _PREHASH_SeedCapability, STD_STRING_BUF_SIZE, seedCap);
+
+ send_complete_agent_movement(sim_host);
+
+ LLViewerRegion* regionp = gWorldp->addRegion(region_handle, sim_host);
+ regionp->setSeedCapability(std::string(seedCap));
+}
+
+
+
+// Sends avatar and camera information to simulator.
+// Sent roughly once per frame, or 20 times per second, whichever is less often
+
+const F32 THRESHOLD_HEAD_ROT_QDOT = 0.9997f; // ~= 2.5 degrees -- if its less than this we need to update head_rot
+const F32 MAX_HEAD_ROT_QDOT = 0.99999f; // ~= 0.5 degrees -- if its greater than this then no need to update head_rot
+ // between these values we delay the updates (but no more than one second)
+
+
+void send_agent_update(BOOL force_send, BOOL send_reliable)
+{
+ if (gAgent.getTeleportState() != LLAgent::TELEPORT_NONE)
+ {
+ // We don't care if they want to send an agent update, they're not allowed to until the simulator
+ // that's the target is ready to receive them (after avatar_init_complete is received)
+ return;
+ }
+
+ // We have already requested to log out. Don't send agent updates.
+ if(gLogoutRequestSent)
+ {
+ return;
+ }
+
+ const F32 TRANSLATE_THRESHOLD = 0.01f;
+
+ // NOTA BENE: This is (intentionally?) using the small angle sine approximation to test for rotation
+ // Plus, there is an extra 0.5 in the mix since the perpendicular between last_camera_at and getAtAxis() bisects cam_rot_change
+ // Thus, we're actually testing against 0.2 degrees
+ const F32 ROTATION_THRESHOLD = 0.1f * 2.f*F_PI/360.f; // Rotation thresh 0.2 deg, see note above
+
+ const U8 DUP_MSGS = 1; // HACK! number of times to repeat data on motionless agent
+
+ // Store data on last sent update so that if no changes, no send
+ static LLVector3 last_camera_pos_agent,
+ last_camera_at,
+ last_camera_left,
+ last_camera_up;
+
+ static LLVector3 cam_center_chg,
+ cam_rot_chg;
+
+ static LLQuaternion last_head_rot;
+ static U32 last_control_flags = 0;
+ static U8 last_render_state;
+ static U8 duplicate_count = 0;
+ static F32 head_rot_chg = 1.0;
+ static U8 last_flags;
+
+ LLMessageSystem *msg = gMessageSystem;
+ LLVector3 camera_pos_agent; // local to avatar's region
+ U8 render_state;
+
+ LLQuaternion body_rotation = gAgent.getFrameAgent().getQuaternion();
+ LLQuaternion head_rotation = gAgent.getHeadRotation();
+
+ camera_pos_agent = gAgent.getCameraPositionAgent();
+
+ render_state = gAgent.getRenderState();
+
+ U32 control_flag_change = 0;
+ U8 flag_change = 0;
+
+ cam_center_chg = last_camera_pos_agent - camera_pos_agent;
+ cam_rot_chg = last_camera_at - gCamera->getAtAxis();
+
+ // If a modifier key is held down, turn off
+ // LBUTTON and ML_LBUTTON so that using the camera (alt-key) doesn't
+ // trigger a control event.
+ U32 control_flags = gAgent.getControlFlags();
+ MASK key_mask = gKeyboard->currentMask(TRUE);
+ if (key_mask & MASK_ALT || key_mask & MASK_CONTROL)
+ {
+ control_flags &= ~( AGENT_CONTROL_LBUTTON_DOWN |
+ AGENT_CONTROL_ML_LBUTTON_DOWN );
+ control_flags |= AGENT_CONTROL_LBUTTON_UP |
+ AGENT_CONTROL_ML_LBUTTON_UP ;
+ }
+
+ control_flag_change = last_control_flags ^ control_flags;
+
+ U8 flags = AU_FLAGS_NONE;
+ if (gAgent.isGroupTitleHidden())
+ {
+ flags |= AU_FLAGS_HIDETITLE;
+ }
+
+ flag_change = last_flags ^ flags;
+
+ head_rot_chg = dot(last_head_rot, head_rotation);
+
+ if (force_send ||
+ (cam_center_chg.magVec() > TRANSLATE_THRESHOLD) ||
+ (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT) ||
+ (last_render_state != render_state) ||
+ (cam_rot_chg.magVec() > ROTATION_THRESHOLD) ||
+ control_flag_change != 0 ||
+ flag_change != 0)
+ {
+/*
+ if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT)
+ {
+ //llinfos << "head rot " << head_rotation << llendl;
+ llinfos << "head_rot_chg = " << head_rot_chg << llendl;
+ }
+ if (cam_rot_chg.magVec() > ROTATION_THRESHOLD)
+ {
+ llinfos << "cam rot " << cam_rot_chg.magVec() << llendl;
+ }
+ if (cam_center_chg.magVec() > TRANSLATE_THRESHOLD)
+ {
+ llinfos << "cam center " << cam_center_chg.magVec() << llendl;
+ }
+// if (drag_delta_chg.magVec() > TRANSLATE_THRESHOLD)
+// {
+// llinfos << "drag delta " << drag_delta_chg.magVec() << llendl;
+// }
+ if (control_flag_change)
+ {
+ llinfos << "dcf = " << control_flag_change << llendl;
+ }
+*/
+
+ duplicate_count = 0;
+ }
+ else
+ {
+ duplicate_count++;
+
+ if (head_rot_chg < MAX_HEAD_ROT_QDOT && duplicate_count < AGENT_UPDATES_PER_SECOND)
+ {
+ // The head_rotation is sent for updating things like attached guns.
+ // We only trigger a new update when head_rotation deviates beyond
+ // some threshold from the last update, however this can break fine
+ // adjustments when trying to aim an attached gun, so what we do here
+ // (where we would normally skip sending an update when nothing has changed)
+ // is gradually reduce the threshold to allow a better update to
+ // eventually get sent... should update to within 0.5 degrees in less
+ // than a second.
+ if (head_rot_chg < THRESHOLD_HEAD_ROT_QDOT + (MAX_HEAD_ROT_QDOT - THRESHOLD_HEAD_ROT_QDOT) * duplicate_count / AGENT_UPDATES_PER_SECOND)
+ {
+ duplicate_count = 0;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ if (duplicate_count < DUP_MSGS && !gDisconnected)
+ {
+ // Build the message
+ msg->newMessageFast(_PREHASH_AgentUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addQuatFast(_PREHASH_BodyRotation, body_rotation);
+ msg->addQuatFast(_PREHASH_HeadRotation, head_rotation);
+ msg->addU8Fast(_PREHASH_State, render_state);
+ msg->addU8Fast(_PREHASH_Flags, flags);
+
+// if (camera_pos_agent.mV[VY] > 255.f)
+// {
+// llinfos << "Sending camera center " << camera_pos_agent << llendl;
+// }
+
+ msg->addVector3Fast(_PREHASH_CameraCenter, camera_pos_agent);
+ msg->addVector3Fast(_PREHASH_CameraAtAxis, gCamera->getAtAxis());
+ msg->addVector3Fast(_PREHASH_CameraLeftAxis, gCamera->getLeftAxis());
+ msg->addVector3Fast(_PREHASH_CameraUpAxis, gCamera->getUpAxis());
+ msg->addF32Fast(_PREHASH_Far, gAgent.mDrawDistance);
+
+ msg->addU32Fast(_PREHASH_ControlFlags, control_flags);
+
+ if (gDebugClicks)
+ {
+ if (control_flags & AGENT_CONTROL_LBUTTON_DOWN)
+ {
+ llinfos << "AgentUpdate left button down" << llendl;
+ }
+
+ if (control_flags & AGENT_CONTROL_LBUTTON_UP)
+ {
+ llinfos << "AgentUpdate left button up" << llendl;
+ }
+ }
+
+ gAgent.enableControlFlagReset();
+
+ if (!send_reliable)
+ {
+ gAgent.sendMessage();
+ }
+ else
+ {
+ gAgent.sendReliableMessage();
+ }
+
+ //llinfos << "agent " << avatar_pos_agent << " cam " << camera_pos_agent << llendl;
+
+ // Copy the old data
+ last_head_rot = head_rotation;
+ last_render_state = render_state;
+ last_camera_pos_agent = camera_pos_agent;
+ last_camera_at = gCamera->getAtAxis();
+ last_camera_left = gCamera->getLeftAxis();
+ last_camera_up = gCamera->getUpAxis();
+ last_control_flags = control_flags;
+ last_flags = flags;
+ }
+}
+
+
+
+// TODO: FIX this dependency
+extern U32 gObjectBits;
+
+void process_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processObjectUpdate(mesgsys, user_data, OUT_FULL);
+ stop_glerror();
+}
+
+void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_FULL_COMPRESSED);
+ stop_glerror();
+}
+
+void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data)
+{
+ // Update the data counters
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ // Update the object...
+ gObjectList.processCachedObjectUpdate(mesgsys, user_data, OUT_FULL_CACHED);
+ stop_glerror();
+}
+
+
+void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data)
+{
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ gObjectBits += mesgsys->getReceiveCompressedSize() * 8;
+ }
+ else
+ {
+ gObjectBits += mesgsys->getReceiveSize() * 8;
+ }
+
+ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED);
+}
+
+
+
+void process_kill_object(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
+
+ LLUUID id;
+ U32 local_id;
+ S32 i;
+ S32 num_objects;
+
+ num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
+
+ for (i = 0; i < num_objects; i++)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+
+ LLViewerObjectList::getUUIDFromLocal(id,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (id == LLUUID::null)
+ {
+ //llinfos << "Unknown kill for local " << local_id << llendl;
+ gObjectList.mNumUnknownKills++;
+ continue;
+ }
+ else
+ {
+ //llinfos << "Kill message for local " << local_id << llendl;
+ }
+
+ gSelectMgr->selectionRemoveObject(id);
+
+ // ...don't kill the avatar
+ if (!(id == gAgentID))
+ {
+ LLViewerObject *objectp = gObjectList.findObject(id);
+ if (objectp)
+ {
+ // Display green bubble on kill
+ if ( gShowObjectUpdates )
+ {
+ LLViewerObject* newobject;
+ newobject = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, objectp->getRegion());
+
+ LLVOTextBubble* bubble = (LLVOTextBubble*) newobject;
+
+ bubble->mColor.setVec(0.f, 1.f, 0.f, 1.f);
+ bubble->setScale( 2.0f * bubble->getScale() );
+ bubble->setPositionGlobal(objectp->getPositionGlobal());
+ gPipeline.addObject(bubble);
+ }
+
+ // Do the kill
+ gObjectList.killObject(objectp);
+ }
+ else
+ {
+ llwarns << "Object in UUID lookup, but not on object list in kill!" << llendl;
+ gObjectList.mNumUnknownKills++;
+ }
+ }
+ }
+}
+
+void process_time_synch(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector3 sun_direction;
+ LLVector3 sun_ang_velocity;
+ F32 phase;
+ U64 space_time_usec;
+
+ // "SimulatorViewerTimeMessage"
+ mesgsys->getU64Fast(_PREHASH_TimeInfo, _PREHASH_UsecSinceStart, space_time_usec);
+ mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerDay, gSecondsPerDay);
+ mesgsys->getU32Fast(_PREHASH_TimeInfo, _PREHASH_SecPerYear, gSecondsPerYear);
+
+ // This should eventually be moved to an "UpdateHeavenlyBodies" message
+ mesgsys->getF32Fast(_PREHASH_TimeInfo, _PREHASH_SunPhase, phase);
+ mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunDirection, sun_direction);
+ mesgsys->getVector3Fast(_PREHASH_TimeInfo, _PREHASH_SunAngVelocity, sun_ang_velocity);
+
+ gWorldp->setSpaceTimeUSec(space_time_usec);
+
+ //lldebugs << "time_synch() - " << sun_direction << ", " << sun_ang_velocity
+ // << ", " << phase << llendl;
+
+ gSky.setSunPhase(phase);
+ gSky.setSunTargetDirection(sun_direction, sun_ang_velocity);
+ if (!gNoRender && !(gSavedSettings.getBOOL("SkyOverrideSimSunPosition") || gSky.getOverrideSun()))
+ {
+ gSky.setSunDirection(sun_direction, sun_ang_velocity);
+ }
+}
+
+void process_sound_trigger(LLMessageSystem *msg, void **)
+{
+ if (!gAudiop) return;
+ if (!gParcelMgr) return;
+ if (!gMuteListp) return;
+
+ U64 region_handle = 0;
+ F32 gain = 0;
+ LLUUID sound_id;
+ LLUUID owner_id;
+ LLUUID object_id;
+ LLUUID parent_id;
+ LLVector3 pos_local;
+
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_OwnerID, owner_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_SoundData, _PREHASH_ParentID, parent_id);
+ msg->getU64Fast(_PREHASH_SoundData, _PREHASH_Handle, region_handle);
+ msg->getVector3Fast(_PREHASH_SoundData, _PREHASH_Position, pos_local);
+ msg->getF32Fast(_PREHASH_SoundData, _PREHASH_Gain, gain);
+
+ // adjust sound location to true global coords
+ LLVector3d pos_global = from_region_handle(region_handle);
+ pos_global.mdV[VX] += pos_local.mV[VX];
+ pos_global.mdV[VY] += pos_local.mV[VY];
+ pos_global.mdV[VZ] += pos_local.mV[VZ];
+
+ // Don't play a trigger sound if you can't hear it due
+ // to parcel "local audio only" settings.
+ if (!gParcelMgr->canHearSound(pos_global)) return;
+
+ // Don't play sounds triggered by someone you muted.
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ // Don't play sounds from an object you muted
+ if (gMuteListp->isMuted(object_id)) return;
+
+ // Don't play sounds from an object whose parent you muted
+ if (parent_id.notNull()
+ && gMuteListp->isMuted(parent_id))
+ {
+ return;
+ }
+
+ gAudiop->triggerSound(sound_id, owner_id, gain, pos_global);
+}
+
+void process_preload_sound(LLMessageSystem *msg, void **user_data)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ LLUUID sound_id;
+ LLUUID object_id;
+ LLUUID owner_id;
+
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id);
+
+ LLViewerObject *objectp = gObjectList.findObject(object_id);
+ if (!objectp) return;
+
+ if (gMuteListp->isMuted(object_id)) return;
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ LLAudioSource *sourcep = objectp->getAudioSource(owner_id);
+ if (!sourcep) return;
+
+ LLAudioData *datap = gAudiop->getAudioData(sound_id);
+
+ // Note that I don't actually do any loading of the
+ // audio data into a buffer at this point, as it won't actually
+ // help us out.
+
+ // Add audioData starts a transfer internally.
+ sourcep->addAudioData(datap, FALSE);
+}
+
+void process_attached_sound(LLMessageSystem *msg, void **user_data)
+{
+ F32 gain = 0;
+ LLUUID sound_id;
+ LLUUID object_id;
+ LLUUID owner_id;
+ U8 flags;
+
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_SoundID, sound_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_id);
+ msg->getUUIDFast(_PREHASH_DataBlock, _PREHASH_OwnerID, owner_id);
+ msg->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain);
+ msg->getU8Fast(_PREHASH_DataBlock, _PREHASH_Flags, flags);
+
+ LLViewerObject *objectp = gObjectList.findObject(object_id);
+ if (!objectp)
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ if (gMuteListp->isMuted(object_id)) return;
+
+ if (gMuteListp->isMuted(owner_id)) return;
+
+ objectp->setAttachedSound(sound_id, owner_id, gain, flags);
+}
+
+
+void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 gain = 0;
+ LLUUID object_guid;
+ LLViewerObject *objectp = NULL;
+
+ mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid);
+
+ if (!((objectp = gObjectList.findObject(object_guid))))
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Gain, gain);
+
+ objectp->adjustAudioGain(gain);
+}
+
+/* Unused July 2006
+void process_attached_sound_cutoff_radius(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 radius = 0;
+ LLUUID object_guid;
+ LLViewerObject *objectp = NULL;
+
+ mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid);
+
+ if (!((objectp = gObjectList.findObject(object_guid))))
+ {
+ // we don't know about this object, just bail
+ return;
+ }
+
+ mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Radius, radius);
+
+ if (gAudiop)
+ {
+// gAudiop->attachToObject(sound_guid, object_guid, gain, priority, flags);
+ }
+}
+*/
+
+void process_health_message(LLMessageSystem *mesgsys, void **user_data)
+{
+ F32 health;
+
+ mesgsys->getF32Fast(_PREHASH_HealthData, _PREHASH_Health, health);
+
+ if (gStatusBar)
+ {
+ gStatusBar->setHealth((S32)health);
+ }
+}
+
+
+void process_sim_stats(LLMessageSystem *msg, void **user_data)
+{
+ S32 count = msg->getNumberOfBlocks("Stat");
+ for (S32 i = 0; i < count; ++i)
+ {
+ U32 stat_id;
+ F32 stat_value;
+ msg->getU32("Stat", "StatID", stat_id, i);
+ msg->getF32("Stat", "StatValue", stat_value, i);
+ switch (stat_id)
+ {
+ case LL_SIM_STAT_TIME_DILATION:
+ gViewerStats->mSimTimeDilation.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_FPS:
+ gViewerStats->mSimFPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PHYSFPS:
+ gViewerStats->mSimPhysicsFPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_AGENTUPS:
+ gViewerStats->mSimAgentUPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_FRAMEMS:
+ gViewerStats->mSimFrameMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NETMS:
+ gViewerStats->mSimNetMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SIMOTHERMS:
+ gViewerStats->mSimSimOtherMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SIMPHYSICSMS:
+ gViewerStats->mSimSimPhysicsMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_AGENTMS:
+ gViewerStats->mSimAgentMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_IMAGESMS:
+ gViewerStats->mSimImagesMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_SCRIPTMS:
+ gViewerStats->mSimScriptMsec.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMTASKS:
+ gViewerStats->mSimObjects.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMTASKSACTIVE:
+ gViewerStats->mSimActiveObjects.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMAGENTMAIN:
+ gViewerStats->mSimMainAgents.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMAGENTCHILD:
+ gViewerStats->mSimChildAgents.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_NUMSCRIPTSACTIVE:
+ gViewerStats->mSimActiveScripts.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_LSLIPS:
+ gViewerStats->mSimLSLIPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_INPPS:
+ gViewerStats->mSimInPPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_OUTPPS:
+ gViewerStats->mSimOutPPS.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_DOWNLOADS:
+ gViewerStats->mSimPendingDownloads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_UPLOADS:
+ gViewerStats->mSimPendingUploads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_PENDING_LOCAL_UPLOADS:
+ gViewerStats->mSimPendingLocalUploads.addValue(stat_value);
+ break;
+ case LL_SIM_STAT_TOTAL_UNACKED_BYTES:
+ gViewerStats->mSimTotalUnackedBytes.addValue(stat_value / 1024.f);
+ break;
+ default:
+ llwarns << "Unknown stat id" << stat_id << llendl;
+ }
+ }
+
+ /*
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_PhysicsTimeDilation, time_dilation);
+ gViewerStats->mSimTDStat.addValue(time_dilation);
+
+ // Process information
+ // { CpuUsage F32 }
+ // { SimMemTotal F32 }
+ // { SimMemRSS F32 }
+ // { ProcessUptime F32 }
+ F32 cpu_usage;
+ F32 sim_mem_total;
+ F32 sim_mem_rss;
+ F32 process_uptime;
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_CpuUsage, cpu_usage);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemTotal, sim_mem_total);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_SimMemRSS, sim_mem_rss);
+ msg->getF32Fast(_PREHASH_Statistics, _PREHASH_ProcessUptime, process_uptime);
+ gViewerStats->mSimCPUUsageStat.addValue(cpu_usage);
+ gViewerStats->mSimMemTotalStat.addValue(sim_mem_total);
+ gViewerStats->mSimMemRSSStat.addValue(sim_mem_rss);
+ */
+
+ //
+ // Various hacks that aren't statistics, but are being handled here.
+ //
+ U32 max_tasks_per_region;
+ U32 region_flags;
+ msg->getU32("Region", "ObjectCapacity", max_tasks_per_region);
+ msg->getU32("Region", "RegionFlags", region_flags);
+
+ LLViewerRegion* regionp = gAgent.getRegion();
+ if (regionp)
+ {
+ BOOL was_flying = gAgent.getFlying();
+ regionp->setRegionFlags(region_flags);
+ regionp->setMaxTasks(max_tasks_per_region);
+ // HACK: This makes agents drop from the sky if the region is
+ // set to no fly while people are still in the sim.
+ if (was_flying && regionp->getBlockFly())
+ {
+ gAgent.setFlying(gAgent.canFly());
+ }
+ }
+}
+
+
+// This info is requested by the simulator when the agent first logs in
+// or when it moves into a simulator in which it did not already have
+// a child agent.
+void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data)
+{
+ llinfos << "process_avatar_info_request()" << llendl;
+
+ // Send the avatar appearance (parameters and texture entry UUIDs)
+ gAgent.sendAgentSetAppearance();
+ send_agent_update(TRUE, TRUE);
+}
+
+
+void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID animation_id;
+ LLUUID uuid;
+ S32 anim_sequence_id;
+ LLVOAvatar *avatarp;
+
+ mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
+
+ //clear animation flags
+ avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
+
+ if (!avatarp)
+ {
+ // no agent by this ID...error?
+ llwarns << "Received animation state for unknown avatar" << uuid << llendl;
+ return;
+ }
+
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationList);
+ S32 num_source_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_AnimationSourceList);
+
+ avatarp->mSignaledAnimations.clear();
+
+ if (avatarp->mIsSelf)
+ {
+ LLUUID object_id;
+
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i);
+ mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i);
+
+ //llinfos << "Anim sequence ID: " << anim_sequence_id << llendl;
+
+ avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
+
+ if (i < num_source_blocks)
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationSourceList, _PREHASH_ObjectID, object_id, i);
+
+ LLViewerObject* object = gObjectList.findObject(object_id);
+ if (object)
+ {
+ object->mFlags |= FLAGS_ANIM_SOURCE;
+
+ BOOL anim_found = FALSE;
+ LLVOAvatar::AnimSourceIterator anim_it = avatarp->mAnimationSources.find(object_id);
+ for (;anim_it != avatarp->mAnimationSources.end(); ++anim_it)
+ {
+ if (anim_it->second == animation_id)
+ {
+ anim_found = TRUE;
+ break;
+ }
+ }
+
+ if (!anim_found)
+ {
+ avatarp->mAnimationSources.insert(LLVOAvatar::AnimationSourceMap::value_type(object_id, animation_id));
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ mesgsys->getUUIDFast(_PREHASH_AnimationList, _PREHASH_AnimID, animation_id, i);
+ mesgsys->getS32Fast(_PREHASH_AnimationList, _PREHASH_AnimSequenceID, anim_sequence_id, i);
+ avatarp->mSignaledAnimations[animation_id] = anim_sequence_id;
+ }
+ }
+
+ if (num_blocks)
+ {
+ avatarp->processAnimationStateChanges();
+ }
+}
+
+void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID uuid;
+ mesgsys->getUUIDFast(_PREHASH_Sender, _PREHASH_ID, uuid);
+
+ LLVOAvatar* avatarp = (LLVOAvatar *)gObjectList.findObject(uuid);
+ if( avatarp )
+ {
+ avatarp->processAvatarAppearance( mesgsys );
+ }
+ else
+ {
+ llwarns << "avatar_appearance sent for unknown avatar " << uuid << llendl;
+ }
+}
+
+void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector4 cameraCollidePlane;
+ mesgsys->getVector4Fast(_PREHASH_CameraCollidePlane, _PREHASH_Plane, cameraCollidePlane);
+
+ gAgent.setCameraCollidePlane(cameraCollidePlane);
+}
+
+void near_sit_object(BOOL success, void *data)
+{
+ // Send message to sit on object
+ gMessageSystem->newMessageFast(_PREHASH_AgentSit);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gAgent.sendReliableMessage();
+}
+
+void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLVector3 sitPosition;
+ LLQuaternion sitRotation;
+ LLUUID sitObjectID;
+ BOOL use_autopilot;
+ mesgsys->getUUIDFast(_PREHASH_SitObject, _PREHASH_ID, sitObjectID);
+ mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_AutoPilot, use_autopilot);
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_SitPosition, sitPosition);
+ mesgsys->getQuatFast(_PREHASH_SitTransform, _PREHASH_SitRotation, sitRotation);
+ LLVector3 camera_eye;
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraEyeOffset, camera_eye);
+ LLVector3 camera_at;
+ mesgsys->getVector3Fast(_PREHASH_SitTransform, _PREHASH_CameraAtOffset, camera_at);
+ BOOL force_mouselook;
+ mesgsys->getBOOLFast(_PREHASH_SitTransform, _PREHASH_ForceMouselook, force_mouselook);
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+
+ if (avatar && dist_vec_squared(camera_eye, camera_at) > 0.0001f)
+ {
+ gAgent.setSitCamera(sitObjectID, camera_eye, camera_at);
+ }
+
+ gAgent.mForceMouselook = force_mouselook;
+
+ LLViewerObject* object = gObjectList.findObject(sitObjectID);
+ if (object)
+ {
+ LLVector3 sit_spot = object->getPositionAgent() + (sitPosition * object->getRotation());
+ if (!use_autopilot || (avatar->mIsSitting && avatar->getRoot() == object->getRoot()))
+ {
+ //we're already sitting on this object, so don't autopilot
+ }
+ else
+ {
+ gAgent.startAutoPilotGlobal(gAgent.getPosGlobalFromAgent(sit_spot), "Sit", &sitRotation, near_sit_object, NULL, 0.5f);
+ }
+
+ // deselect transient selections (pie menu) when sitting
+ gSelectMgr->deselectTransient();
+ }
+ else
+ {
+ llwarns << "Received sit approval for unknown object " << sitObjectID << llendl;
+ }
+}
+
+void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLUUID source_id;
+
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id);
+
+ LLFollowCamMgr::removeFollowCamParams(source_id);
+}
+
+void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data)
+{
+ S32 type;
+ F32 value;
+ bool settingPosition = false;
+ bool settingFocus = false;
+ bool settingFocusOffset = false;
+ LLVector3 position;
+ LLVector3 focus;
+ LLVector3 focus_offset;
+
+ LLUUID source_id;
+
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_ObjectID, source_id);
+
+ LLViewerObject* objectp = gObjectList.findObject(source_id);
+ if (objectp)
+ {
+ objectp->mFlags |= FLAGS_CAMERA_SOURCE;
+ }
+
+ S32 num_objects = mesgsys->getNumberOfBlocks("CameraProperty");
+ for (S32 block_index = 0; block_index < num_objects; block_index++)
+ {
+ mesgsys->getS32("CameraProperty", "Type", type, block_index);
+ mesgsys->getF32("CameraProperty", "Value", value, block_index);
+ switch(type)
+ {
+ case FOLLOWCAM_PITCH:
+ LLFollowCamMgr::setPitch(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_X:
+ focus_offset.mV[VX] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_Y:
+ focus_offset.mV[VY] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_FOCUS_OFFSET_Z:
+ focus_offset.mV[VZ] = value;
+ settingFocusOffset = true;
+ break;
+ case FOLLOWCAM_POSITION_LAG:
+ LLFollowCamMgr::setPositionLag(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_LAG:
+ LLFollowCamMgr::setFocusLag(source_id, value);
+ break;
+ case FOLLOWCAM_DISTANCE:
+ LLFollowCamMgr::setDistance(source_id, value);
+ break;
+ case FOLLOWCAM_BEHINDNESS_ANGLE:
+ LLFollowCamMgr::setBehindnessAngle(source_id, value);
+ break;
+ case FOLLOWCAM_BEHINDNESS_LAG:
+ LLFollowCamMgr::setBehindnessLag(source_id, value);
+ break;
+ case FOLLOWCAM_POSITION_THRESHOLD:
+ LLFollowCamMgr::setPositionThreshold(source_id, value);
+ break;
+ case FOLLOWCAM_FOCUS_THRESHOLD:
+ LLFollowCamMgr::setFocusThreshold(source_id, value);
+ break;
+ case FOLLOWCAM_ACTIVE:
+ //if 1, set using followcam,.
+ LLFollowCamMgr::setCameraActive(source_id, value != 0.f);
+ break;
+ case FOLLOWCAM_POSITION_X:
+ settingPosition = true;
+ position.mV[ 0 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_Y:
+ settingPosition = true;
+ position.mV[ 1 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_Z:
+ settingPosition = true;
+ position.mV[ 2 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_X:
+ settingFocus = true;
+ focus.mV[ 0 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_Y:
+ settingFocus = true;
+ focus.mV[ 1 ] = value;
+ break;
+ case FOLLOWCAM_FOCUS_Z:
+ settingFocus = true;
+ focus.mV[ 2 ] = value;
+ break;
+ case FOLLOWCAM_POSITION_LOCKED:
+ LLFollowCamMgr::setPositionLocked(source_id, value != 0.f);
+ break;
+ case FOLLOWCAM_FOCUS_LOCKED:
+ LLFollowCamMgr::setFocusLocked(source_id, value != 0.f);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( settingPosition )
+ {
+ LLFollowCamMgr::setPosition(source_id, position);
+ }
+ if ( settingFocus )
+ {
+ LLFollowCamMgr::setFocus(source_id, focus);
+ }
+ if ( settingFocusOffset )
+ {
+ LLFollowCamMgr::setFocusOffset(source_id, focus_offset);
+ }
+}
+//end Ventrella
+
+
+// Culled from newsim lltask.cpp
+void process_name_value(LLMessageSystem *mesgsys, void **user_data)
+{
+ char temp_str[NAME_VALUE_BUF_SIZE];
+ LLUUID id;
+ S32 i, num_blocks;
+
+ mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id);
+
+ LLViewerObject* object = gObjectList.findObject(id);
+
+ if (object)
+ {
+ num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData);
+ for (i = 0; i < num_blocks; i++)
+ {
+ mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i);
+ llinfos << "Added to object Name Value: " << temp_str << llendl;
+ object->addNVPair(temp_str);
+ }
+ }
+ else
+ {
+ llinfos << "Can't find object " << id << " to add name value pair" << llendl;
+ }
+}
+
+void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data)
+{
+ char temp_str[NAME_VALUE_BUF_SIZE];
+ LLUUID id;
+ S32 i, num_blocks;
+
+ mesgsys->getUUIDFast(_PREHASH_TaskData, _PREHASH_ID, id);
+
+ LLViewerObject* object = gObjectList.findObject(id);
+
+ if (object)
+ {
+ num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_NameValueData);
+ for (i = 0; i < num_blocks; i++)
+ {
+ mesgsys->getStringFast(_PREHASH_NameValueData, _PREHASH_NVPair, NAME_VALUE_BUF_SIZE, temp_str, i);
+ llinfos << "Removed from object Name Value: " << temp_str << llendl;
+ object->removeNVPair(temp_str);
+ }
+ }
+ else
+ {
+ llinfos << "Can't find object " << id << " to remove name value pair" << llendl;
+ }
+}
+
+void process_kick_user(LLMessageSystem *msg, void** /*user_data*/)
+{
+ char message[2048];
+ message[0] = '\0';
+
+ msg->getStringFast(_PREHASH_UserInfo, _PREHASH_Reason, 2048, message);
+
+ do_disconnect(message);
+}
+
+
+/*
+void process_user_list_reply(LLMessageSystem *msg, void **user_data)
+{
+ LLUserList::processUserListReply(msg, user_data);
+ return;
+ char firstname[MAX_STRING+1];
+ char lastname[MAX_STRING+1];
+ U8 status;
+ S32 user_count;
+
+ user_count = msg->getNumberOfBlocks("UserBlock");
+
+ for (S32 i = 0; i < user_count; i++)
+ {
+ msg->getData("UserBlock", i, "FirstName", firstname);
+ msg->getData("UserBlock", i, "LastName", lastname);
+ msg->getData("UserBlock", i, "Status", &status);
+
+ if (status & 0x01)
+ {
+ dialog_friends_add_friend(buffer, TRUE);
+ }
+ else
+ {
+ dialog_friends_add_friend(buffer, FALSE);
+ }
+ }
+
+ dialog_friends_done_adding();
+}
+*/
+
+// this is not handled in processUpdateMessage
+/*
+void process_time_dilation(LLMessageSystem *msg, void **user_data)
+{
+ // get the time_dilation
+ U16 foo;
+ msg->getData("TimeDilation", "TimeDilation", &foo);
+ F32 time_dilation = ((F32) foo) / 65535.f;
+
+ // get the pointer to the right region
+ U32 ip = msg->getSenderIP();
+ U32 port = msg->getSenderPort();
+ LLViewerRegion *regionp = gWorldp->getRegion(ip, port);
+ if (regionp)
+ {
+ regionp->setTimeDilation(time_dilation);
+ }
+}
+*/
+
+
+
+void process_money_balance_reply( LLMessageSystem* msg, void** )
+{
+ S32 balance = 0;
+ S32 credit = 0;
+ S32 committed = 0;
+ char desc[STD_STRING_BUF_SIZE] = "";
+
+ msg->getS32("MoneyData", "MoneyBalance", balance);
+ msg->getS32("MoneyData", "SquareMetersCredit", credit);
+ msg->getS32("MoneyData", "SquareMetersCommitted", committed);
+ msg->getStringFast(_PREHASH_MoneyData, _PREHASH_Description, STD_STRING_BUF_SIZE, desc);
+ llinfos << "money, credit, committed: " << balance << " " << credit << " "
+ << committed << llendl;
+
+ if (gStatusBar)
+ {
+ S32 old_balance = gStatusBar->getBalance();
+
+ // This is an update, not the first transmission of balance
+ if (old_balance != 0)
+ {
+ // this is actually an update
+ if (balance > old_balance)
+ {
+ LLFirstUse::useBalanceIncrease(balance - old_balance);
+ }
+ else if (balance < old_balance)
+ {
+ LLFirstUse::useBalanceDecrease(balance - old_balance);
+ }
+ }
+
+ gStatusBar->setBalance(balance);
+ gStatusBar->setLandCredit(credit);
+ gStatusBar->setLandCommitted(committed);
+ }
+
+ LLUUID tid;
+ msg->getUUID("MoneyData", "TransactionID", tid);
+ static std::deque<LLUUID> recent;
+ if(desc[0] && gSavedSettings.getBOOL("NotifyMoneyChange")
+ && (std::find(recent.rbegin(), recent.rend(), tid) == recent.rend()))
+ {
+ // Make the user confirm the transaction, since they might
+ // have missed something during an event.
+ // XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = desc;
+ LLNotifyBox::showXml("SystemMessage", args);
+
+ // Once the 'recent' container gets large enough, chop some
+ // off the beginning.
+ const U32 MAX_LOOKBACK = 30;
+ const S32 POP_FRONT_SIZE = 12;
+ if(recent.size() > MAX_LOOKBACK)
+ {
+ lldebugs << "Removing oldest transaction records" << llendl;
+ recent.erase(recent.begin(), recent.begin() + POP_FRONT_SIZE);
+ }
+ //lldebugs << "Pushing back transaction " << tid << llendl;
+ recent.push_back(tid);
+ }
+}
+
+void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data)
+{
+ char buffer[MAX_STRING];
+ msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer);
+ BOOL modal = FALSE;
+ msgsystem->getBOOL("AlertData", "Modal", modal);
+ process_alert_core(buffer, modal);
+}
+
+void process_alert_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ char buffer[MAX_STRING];
+ msgsystem->getStringFast(_PREHASH_AlertData, _PREHASH_Message, MAX_STRING, buffer);
+ BOOL modal = FALSE;
+ process_alert_core(buffer, modal);
+}
+
+void process_alert_core(const char* buffer, BOOL modal)
+{
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ // HACK -- handle callbacks for specific alerts
+ if( !strcmp( buffer, "You died and have been teleported to your home location" ) )
+ {
+ gViewerStats->incStat(LLViewerStats::ST_KILLED_COUNT);
+ }
+ else if( !strcmp( buffer, "Home position set." ) )
+ {
+ // save the home location image to disk
+ char temp_str[LL_MAX_PATH];
+ strcpy(temp_str, gDirUtilp->getLindenUserDir().c_str());
+ strcat(temp_str, "/");
+ strcat(temp_str,SCREEN_HOME_FILENAME);
+ gViewerWindow->saveSnapshot(temp_str, gViewerWindow->getWindowWidth(), gViewerWindow->getWindowHeight(), FALSE, FALSE);
+ }
+
+ // Translate system messages here.
+ if (buffer[0] == '/')
+ {
+ // System message is important, show in upper-right box not tip
+ LLString text(buffer+1);
+ LLString::format_map_t args;
+ if (text.substr(0,17) == "RESTART_X_MINUTES")
+ {
+ S32 mins = 0;
+ LLString::convertToS32(text.substr(18), mins);
+ args["[MINUTES]"] = llformat("%d",mins);
+ LLNotifyBox::showXml("RegionRestartMinutes", args);
+ }
+ else if (text.substr(0,17) == "RESTART_X_SECONDS")
+ {
+ S32 secs = 0;
+ LLString::convertToS32(text.substr(18), secs);
+ args["[SECONDS]"] = llformat("%d",secs);
+ LLNotifyBox::showXml("RegionRestartSeconds", args);
+ }
+ else
+ {
+ //XUI:translate
+ args["[MESSAGE]"] = text;
+ LLNotifyBox::showXml("SystemMessage", args);
+ }
+ }
+ else if (modal)
+ {
+ //XUI:translate
+ LLString::format_map_t args;
+ args["[BUFFER]"] = buffer;
+ gViewerWindow->alertXml("ErrorMessage", args);
+ }
+ else
+ {
+ //XUI:translate
+ LLString::format_map_t args;
+ args["[MESSAGE]"] = buffer;
+ LLNotifyBox::showXml("SystemMessageTip", args);
+ }
+}
+
+LLLinkedList<LLMeanCollisionData> gMeanCollisionList;
+time_t gLastDisplayedTime = 0;
+
+void handle_show_mean_events(void *)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ LLFloaterBump::show(NULL);
+}
+
+void mean_name_callback(const LLUUID &id, const char *first, const char *last, BOOL always_false, void* data)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ while(gMeanCollisionList.getLength() > 20)
+ {
+ gMeanCollisionList.getLastData();
+ gMeanCollisionList.deleteCurrentData();
+ }
+
+ LLMeanCollisionData *mcd;
+ for (mcd = gMeanCollisionList.getFirstData(); mcd; mcd = gMeanCollisionList.getNextData())
+ {
+ if (mcd->mPerp == id)
+ {
+ strcpy(mcd->mFirstName, first);
+ strcpy(mcd->mLastName, last);
+ }
+ }
+}
+
+void process_mean_collision_alert_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ if (gAgent.inPrelude())
+ {
+ // JC: In prelude, bumping is OK. This dialog is rather confusing to
+ // newbies, so we don't show it. Drop the packet on the floor.
+ return;
+ }
+
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+
+ LLUUID perp;
+ U32 time;
+ U8 u8type;
+ EMeanCollisionType type;
+ F32 mag;
+
+ S32 i, num = msgsystem->getNumberOfBlocks(_PREHASH_MeanCollision);
+
+ for (i = 0; i < num; i++)
+ {
+ msgsystem->getUUIDFast(_PREHASH_MeanCollision, _PREHASH_Perp, perp);
+ msgsystem->getU32Fast(_PREHASH_MeanCollision, _PREHASH_Time, time);
+ msgsystem->getF32Fast(_PREHASH_MeanCollision, _PREHASH_Mag, mag);
+ msgsystem->getU8Fast(_PREHASH_MeanCollision, _PREHASH_Type, u8type);
+
+ type = (EMeanCollisionType)u8type;
+
+ LLMeanCollisionData *mcd;
+
+ BOOL b_found = FALSE;
+
+ for (mcd = gMeanCollisionList.getFirstData(); mcd; mcd = gMeanCollisionList.getNextData())
+ {
+ if ((mcd->mPerp == perp) && (mcd->mType == type))
+ {
+ mcd->mTime = time;
+ mcd->mMag = mag;
+ b_found = TRUE;
+ break;
+ }
+ }
+
+ if (!b_found)
+ {
+ LLMeanCollisionData *mcd = new LLMeanCollisionData(gAgentID, perp, time, type, mag);
+ gMeanCollisionList.addData(mcd);
+ const BOOL is_group = FALSE;
+ gCacheName->get(perp, is_group, mean_name_callback);
+ }
+ }
+}
+
+void process_frozen_message(LLMessageSystem *msgsystem, void **user_data)
+{
+ // make sure the cursor is back to the usual default since the
+ // alert is probably due to some kind of error.
+ gViewerWindow->getWindow()->resetBusyCount();
+ BOOL b_frozen;
+
+ msgsystem->getBOOL("FrozenData", "Data", b_frozen);
+
+ // TODO: make being frozen change view
+ if (b_frozen)
+ {
+ }
+ else
+ {
+ }
+}
+
+// do some extra stuff once we get our economy data
+void process_economy_data(LLMessageSystem *msg, void** /*user_data*/)
+{
+ LLGlobalEconomy::processEconomyData(msg, (void**)gGlobalEconomy);
+
+ S32 upload_cost = gGlobalEconomy->getPriceUpload();
+ LLFloaterImagePreview::setUploadAmount(upload_cost);
+
+ gMenuHolder->childSetLabelArg("Upload Image", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Upload Sound", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Upload Animation", "[COST]", llformat("%d", upload_cost));
+ gMenuHolder->childSetLabelArg("Bulk Upload", "[COST]", llformat("%d", upload_cost));
+}
+
+class LLScriptQuestionCBData
+{
+public:
+ LLScriptQuestionCBData(const LLUUID &taskid, const LLUUID &itemid, const LLHost &sender, S32 questions)
+ : mTaskID(taskid), mItemID(itemid), mSender(sender), mQuestions(questions)
+ {
+ }
+
+ LLUUID mTaskID;
+ LLUUID mItemID;
+ LLHost mSender;
+ S32 mQuestions;
+};
+
+
+void script_question_cb(S32 option, void* user_data)
+{
+ LLMessageSystem *msg = gMessageSystem;
+ LLScriptQuestionCBData *cbdata = (LLScriptQuestionCBData *)user_data;
+ if (option != 0)
+ {
+ cbdata->mQuestions = 0;
+ }
+ msg->newMessageFast(_PREHASH_ScriptAnswerYes);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addUUIDFast(_PREHASH_TaskID, cbdata->mTaskID);
+ msg->addUUIDFast(_PREHASH_ItemID, cbdata->mItemID);
+ msg->addS32Fast(_PREHASH_Questions, cbdata->mQuestions);
+ msg->sendReliable(cbdata->mSender);
+ delete cbdata;
+}
+
+
+void process_script_question(LLMessageSystem *msg, void **user_data)
+{
+ // XUI:translate owner name -> [FIRST] [LAST]
+ const LLString script_questions[SCRIPT_PERMISSION_EOF] =
+ {
+ "ScriptTakeMoney",
+ "ActOnControlInputs",
+ "RemapControlInputs",
+ "AnimateYourAvatar",
+ "AttachToYourAvatar",
+ "ReleaseOwnership",
+ "LinkAndDelink",
+ "AddAndRemoveJoints",
+ "ChangePermissions",
+ "TrackYourCamera",
+ "ControlYourCamera"
+ };
+
+ LLHost sender = msg->getSender();
+
+ LLUUID taskid;
+ LLUUID itemid;
+ S32 questions;
+ char object_name[255];
+ char owner_name[DB_FULL_NAME_BUF_SIZE];
+
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_TaskID, taskid );
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ItemID, itemid );
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectName, 255, object_name);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_ObjectOwner, DB_FULL_NAME_BUF_SIZE, owner_name);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Questions, questions );
+
+ LLString script_question;
+ if (questions)
+ {
+ S32 count = 0;
+ LLString::format_map_t args;
+ args["[OBJECTNAME]"] = object_name;
+ args["[NAME]"] = owner_name;
+ for (S32 i = 0; i < SCRIPT_PERMISSION_EOF; i++)
+ {
+ if (questions & LSCRIPTRunTimePermissionBits[i])
+ {
+ count++;
+ script_question += " " + LLNotifyBox::getTemplateMessage(script_questions[i]) + "\n";
+ }
+ }
+ args["[QUESTIONS]"] = script_question;
+
+ LLScriptQuestionCBData *cbdata = new LLScriptQuestionCBData(taskid, itemid, sender, questions);
+
+ LLNotifyBox::showXml("ScriptQuestion", args, script_question_cb, cbdata);
+ }
+}
+
+
+void process_derez_container(LLMessageSystem *msg, void**)
+{
+ llwarns << "call to deprecated process_derez_container" << llendl;
+}
+
+void container_inventory_arrived(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void* data)
+{
+ llinfos << "container_inventory_arrived()" << llendl;
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+
+ if (inventory->size() > 2)
+ {
+ // create a new inventory category to put this in
+ LLUUID cat_id;
+ cat_id = gInventory.createNewCategory(gAgent.getInventoryRootID(),
+ LLAssetType::AT_NONE,
+ "Acquired Items");
+
+ InventoryObjectList::const_iterator it = inventory->begin();
+ InventoryObjectList::const_iterator end = inventory->end();
+ for ( ; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY &&
+ (*it)->getType() != LLAssetType::AT_ROOT_CATEGORY)
+ {
+ LLInventoryObject* obj = (LLInventoryObject*)(*it);
+ LLInventoryItem* item = (LLInventoryItem*)(obj);
+ LLUUID item_id;
+ item_id.generate();
+ S32 creation_date_utc = time_corrected();
+ LLPointer<LLViewerInventoryItem> new_item
+ = new LLViewerInventoryItem(item_id,
+ cat_id,
+ item->getPermissions(),
+ item->getAssetUUID(),
+ item->getType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ LLSaleInfo::DEFAULT,
+ item->getFlags(),
+ creation_date_utc);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ }
+ }
+ gInventory.notifyObservers();
+ if(view)
+ {
+ view->getPanel()->setSelection(cat_id, TAKE_FOCUS_NO);
+ }
+ }
+ else if (inventory->size() == 2)
+ {
+ // we're going to get one fake root category as well as the
+ // one actual object
+ InventoryObjectList::iterator it = inventory->begin();
+
+ if ((*it)->getType() == LLAssetType::AT_CATEGORY ||
+ (*it)->getType() == LLAssetType::AT_ROOT_CATEGORY)
+ {
+ ++it;
+ }
+
+ LLInventoryItem* item = (LLInventoryItem*)((LLInventoryObject*)(*it));
+ LLUUID category = gInventory.findCategoryUUIDForType(item->getType());
+
+ LLUUID item_id;
+ item_id.generate();
+ S32 creation_date_utc = time_corrected();
+ LLPointer<LLViewerInventoryItem> new_item
+ = new LLViewerInventoryItem(item_id, category,
+ item->getPermissions(),
+ item->getAssetUUID(),
+ item->getType(),
+ item->getInventoryType(),
+ item->getName(),
+ item->getDescription(),
+ LLSaleInfo::DEFAULT,
+ item->getFlags(),
+ creation_date_utc);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+ if(view)
+ {
+ view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO);
+ }
+ }
+
+ // we've got the inventory, now delete this object if this was a take
+ BOOL delete_object = (BOOL)(intptr_t)data;
+ LLViewerRegion *region = gAgent.getRegion();
+ if (delete_object && region)
+ {
+ gMessageSystem->newMessageFast(_PREHASH_ObjectDelete);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ const U8 NO_FORCE = 0;
+ gMessageSystem->addU8Fast(_PREHASH_Force, NO_FORCE);
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, object->getLocalID());
+ gMessageSystem->sendReliable(region->getHost());
+ }
+}
+
+// method to format the time. Buffer should be at least
+// TIME_STR_LENGTH long, and the function returns buffer (for use in
+// sprintf and the like)
+char* formatted_time(const time_t& the_time, char* buffer)
+{
+ LLString::copy(buffer, ctime(&the_time), TIME_STR_LENGTH);
+ buffer[24] = '\0';
+ return buffer;
+}
+
+
+void process_teleport_failed(LLMessageSystem *msg, void**)
+{
+ char reason[STD_STRING_BUF_SIZE];
+ msg->getStringFast(_PREHASH_Info, _PREHASH_Reason, STD_STRING_BUF_SIZE, reason);
+
+ LLStringBase<char>::format_map_t args;
+ args["[REASON]"] = reason;
+ gViewerWindow->alertXml("CouldNotTeleportReason", args);
+
+ if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE )
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+}
+
+void process_teleport_local(LLMessageSystem *msg,void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_Info, _PREHASH_AgentID, agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Got teleport notification for wrong agent!" << llendl;
+ return;
+ }
+
+ U32 location_id;
+ LLVector3 pos, look_at;
+ U32 teleport_flags;
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_LocationID, location_id);
+ msg->getVector3Fast(_PREHASH_Info, _PREHASH_Position, pos);
+ msg->getVector3Fast(_PREHASH_Info, _PREHASH_LookAt, look_at);
+ msg->getU32Fast(_PREHASH_Info, _PREHASH_TeleportFlags, teleport_flags);
+
+ if( gAgent.getTeleportState() != LLAgent::TELEPORT_NONE )
+ {
+ gAgent.setTeleportState( LLAgent::TELEPORT_NONE );
+ }
+
+ // Sim tells us whether the new position is off the ground
+ if (teleport_flags & TELEPORT_FLAGS_IS_FLYING)
+ {
+ gAgent.setFlying(TRUE);
+ }
+ else
+ {
+ gAgent.setFlying(FALSE);
+ }
+
+ gAgent.setPositionAgent(pos);
+ gAgent.slamLookAt(look_at);
+
+ // likewise make sure the camera is behind the avatar
+ gAgent.resetView(TRUE);
+
+ // send camera update to new region
+ gAgent.updateCamera();
+
+ send_agent_update(TRUE, TRUE);
+}
+
+void send_simple_im(const LLUUID& to_id,
+ const char* message,
+ EInstantMessage dialog,
+ const LLUUID& id)
+{
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+ send_improved_im(to_id,
+ my_name.c_str(),
+ message,
+ IM_ONLINE,
+ dialog,
+ id,
+ NO_TIMESTAMP,
+ (U8*)EMPTY_BINARY_BUCKET,
+ EMPTY_BINARY_BUCKET_SIZE);
+}
+
+void send_group_notice(const LLUUID& group_id,
+ const char* subject,
+ const char* message,
+ const LLInventoryItem* item)
+{
+ // Put this notice into an instant message form.
+ // This will mean converting the item to a binary bucket,
+ // and the subject/message into a single field.
+ std::string my_name;
+ gAgent.buildFullname(my_name);
+
+ // Combine subject + message into a single string.
+ std::ostringstream subject_and_message;
+ // TODO: turn all existing |'s into ||'s in subject and message.
+ subject_and_message << subject << "|" << message;
+
+ // Create an empty binary bucket.
+ U8 bin_bucket[MAX_INVENTORY_BUFFER_SIZE];
+ U8* bucket_to_send = bin_bucket;
+ bin_bucket[0] = '\0';
+ S32 bin_bucket_size = EMPTY_BINARY_BUCKET_SIZE;
+ // If there is an item being sent, pack it into the binary bucket.
+ if (item)
+ {
+ LLSD item_def;
+ item_def["item_id"] = item->getUUID();
+ item_def["owner_id"] = item->getPermissions().getOwner();
+ std::ostringstream ostr;
+ LLSDSerialize::serialize(item_def, ostr, LLSDSerialize::LLSD_XML);
+ bin_bucket_size = ostr.str().copy(
+ (char*)bin_bucket, ostr.str().size());
+ bin_bucket[bin_bucket_size] = '\0';
+ }
+ else
+ {
+ bucket_to_send = (U8*) EMPTY_BINARY_BUCKET;
+ }
+
+
+ send_improved_im(
+ group_id,
+ my_name.c_str(),
+ subject_and_message.str().c_str(),
+ IM_ONLINE,
+ IM_GROUP_NOTICE,
+ LLUUID::null,
+ NO_TIMESTAMP,
+ bucket_to_send,
+ bin_bucket_size);
+}
+
+void handle_lure_callback(S32 option, const LLString& text, void* userdata)
+{
+ LLDynamicArray<LLUUID>* invitees = (LLDynamicArray<LLUUID>*)userdata;
+
+ if(0 == option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)0); // sim will fill this in.
+ msg->addStringFast(_PREHASH_Message, text.c_str());
+ for(LLDynamicArray<LLUUID>::iterator itr = invitees->begin(); itr != invitees->end(); ++itr)
+ {
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, *itr);
+ }
+ gAgent.sendReliableMessage();
+ }
+
+ delete invitees;
+ invitees = NULL;
+}
+
+void handle_lure_callback_godlike(S32 option, void* userdata)
+{
+ handle_lure_callback(option, LLString::null, userdata);
+}
+
+void send_lure_911(void** user_data, S32 result)
+{
+ LLUUID im_session_id(*((LLUUID*)user_data));
+ LLString name;
+ gAgent.getName(name);
+ LLString text = name + " needs help"; // this text is ignored for 911 lures
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_StartLure);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Info);
+ msg->addU8Fast(_PREHASH_LureType, (U8)IM_LURE_911);
+ msg->addStringFast(_PREHASH_Message, text.c_str());
+ msg->nextBlockFast(_PREHASH_TargetData);
+ msg->addUUIDFast(_PREHASH_TargetID, im_session_id);
+ gAgent.sendReliableMessage();
+}
+
+void handle_lure(const LLUUID& invitee)
+{
+ LLDynamicArray<LLUUID> ids;
+ ids.push_back(invitee);
+ handle_lure(ids);
+}
+
+// Prompt for a message to the invited user.
+void handle_lure(LLDynamicArray<LLUUID>& ids)
+{
+ LLDynamicArray<LLUUID>* userdata = new LLDynamicArray<LLUUID>(ids);
+
+ LLString::format_map_t edit_args;
+ edit_args["[REGION]"] = gAgent.getRegion()->getName();
+ if (gAgent.isGodlike())
+ {
+ gViewerWindow->alertXmlEditText("OfferTeleportFromGod", edit_args,
+ &handle_lure_callback_godlike, userdata,
+ NULL, NULL, edit_args);
+ }
+ else
+ {
+ gViewerWindow->alertXmlEditText("OfferTeleport", edit_args,
+ NULL, NULL,
+ handle_lure_callback, userdata, edit_args);
+ }
+}
+
+
+void send_improved_im(const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline,
+ EInstantMessage dialog,
+ const LLUUID& id,
+ U32 timestamp,
+ const U8* binary_bucket,
+ S32 binary_bucket_size)
+{
+ pack_instant_message(
+ gMessageSystem,
+ gAgent.getID(),
+ FALSE,
+ gAgent.getSessionID(),
+ to_id,
+ name,
+ message,
+ offline,
+ dialog,
+ id,
+ 0,
+ LLUUID::null,
+ gAgent.getPositionAgent(),
+ timestamp,
+ binary_bucket,
+ binary_bucket_size);
+ gAgent.sendReliableMessage();
+}
+
+
+void send_places_query(const LLUUID& query_id,
+ const LLUUID& trans_id,
+ const char* query_text,
+ U32 query_flags,
+ S32 category,
+ const char* sim_name)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessage("PlacesQuery");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->addUUID("QueryID", query_id);
+ msg->nextBlock("TransactionData");
+ msg->addUUID("TransactionID", trans_id);
+ msg->nextBlock("QueryData");
+ msg->addString("QueryText", query_text);
+ msg->addU32("QueryFlags", query_flags);
+ msg->addS8("Category", (S8)category);
+ msg->addString("SimName", sim_name);
+ gAgent.sendReliableMessage();
+}
+
+
+void process_user_info_reply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUIDFast(_PREHASH_AgentData, _PREHASH_AgentID, agent_id);
+ if(agent_id != gAgent.getID())
+ {
+ llwarns << "process_user_info_reply - "
+ << "wrong agent id." << llendl;
+ }
+
+ BOOL im_via_email;
+ msg->getBOOLFast(_PREHASH_UserData, _PREHASH_IMViaEMail, im_via_email);
+ char email[DB_USER_EMAIL_ADDR_BUF_SIZE];
+ msg->getStringFast(_PREHASH_UserData, _PREHASH_EMail, DB_USER_EMAIL_ADDR_BUF_SIZE,
+ email);
+ char dir_visibility[MAX_STRING];
+ msg->getString(
+ "UserData", "DirectoryVisibility", MAX_STRING, dir_visibility);
+
+ LLFloaterPreference::updateUserInfo(dir_visibility, im_via_email, email);
+ LLFloaterPostcard::updateUserInfo(email);
+}
+
+
+//---------------------------------------------------------------------------
+// Script Dialog
+//---------------------------------------------------------------------------
+
+const S32 SCRIPT_DIALOG_MAX_BUTTONS = 12;
+const S32 SCRIPT_DIALOG_BUTTON_STR_SIZE = 24;
+const S32 SCRIPT_DIALOG_MAX_MESSAGE_SIZE = 512;
+const char* SCRIPT_DIALOG_HEADER = "Script Dialog:\n";
+
+struct ScriptDialogInfo
+{
+ LLHost mSender;
+ LLUUID mObjectID;
+ S32 mChatChannel;
+ std::vector<LLString> mButtons;
+};
+
+void callback_script_dialog(S32 option, void* data)
+{
+ ScriptDialogInfo* info = (ScriptDialogInfo*)data;
+ if (!info) return;
+
+ // Didn't click "Ignore"
+ if (0 != option)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ScriptDialogReply");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("ObjectID", info->mObjectID);
+ msg->addS32("ChatChannel", info->mChatChannel);
+ msg->addS32("ButtonIndex", option);
+ msg->addString("ButtonLabel", info->mButtons[option-1]);
+ msg->sendReliable(info->mSender);
+ }
+
+ delete info;
+}
+
+void process_script_dialog(LLMessageSystem* msg, void**)
+{
+ S32 i;
+
+ ScriptDialogInfo* info = new ScriptDialogInfo;
+
+ const S32 messageLength = SCRIPT_DIALOG_MAX_MESSAGE_SIZE + sizeof(SCRIPT_DIALOG_HEADER);
+ char message[messageLength]; // Account for size of "Script Dialog:\n"
+
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ char last_name[DB_GROUP_NAME_BUF_SIZE];
+ char title[DB_INV_ITEM_NAME_BUF_SIZE];
+ info->mSender = msg->getSender();
+
+ msg->getUUID("Data", "ObjectID", info->mObjectID);
+ msg->getString("Data", "FirstName", DB_FIRST_NAME_BUF_SIZE, first_name);
+ msg->getString("Data", "LastName", DB_LAST_NAME_BUF_SIZE, last_name);
+ msg->getString("Data", "ObjectName", DB_INV_ITEM_NAME_BUF_SIZE, title);
+ msg->getString("Data", "Message", SCRIPT_DIALOG_MAX_MESSAGE_SIZE, message);
+ msg->getS32("Data", "ChatChannel", info->mChatChannel);
+
+ // unused for now
+ LLUUID image_id;
+ msg->getUUID("Data", "ImageID", image_id);
+
+ S32 button_count = msg->getNumberOfBlocks("Buttons");
+ if (button_count > SCRIPT_DIALOG_MAX_BUTTONS)
+ {
+ button_count = SCRIPT_DIALOG_MAX_BUTTONS;
+ }
+
+ for (i = 0; i < button_count; i++)
+ {
+ char tdesc[SCRIPT_DIALOG_BUTTON_STR_SIZE+1];
+ msg->getString("Buttons", "ButtonLabel", SCRIPT_DIALOG_BUTTON_STR_SIZE + 1, tdesc, i);
+ info->mButtons.push_back(LLString(tdesc));
+ }
+
+ LLStringBase<char>::format_map_t args;
+ args["[TITLE]"] = title;
+ args["[MESSAGE]"] = message;
+ if (strlen(first_name) > 0)
+ {
+ args["[FIRST]"] = first_name;
+ args["[LAST]"] = last_name;
+ LLNotifyBox::showXml("ScriptDialog", args,
+ callback_script_dialog, info,
+ info->mButtons,
+ TRUE);
+ }
+ else
+ {
+ args["[GROUPNAME]"] = last_name;
+ LLNotifyBox::showXml("ScriptDialogGroup", args,
+ callback_script_dialog, info,
+ info->mButtons,
+ TRUE);
+ }
+}
+
+//---------------------------------------------------------------------------
+
+struct LoadUrlInfo
+{
+ LLUUID mObjectID;
+ LLUUID mOwnerID;
+ BOOL mOwnerIsGroup;
+ char mObjectName[256];
+ char mMessage[256];
+ char mUrl[256];
+};
+
+std::vector<LoadUrlInfo*> gLoadUrlList;
+
+void callback_load_url(S32 option, void* data)
+{
+ LoadUrlInfo* infop = (LoadUrlInfo*)data;
+ if (!infop) return;
+
+ if (0 == option)
+ {
+ LLWeb::loadURL(infop->mUrl);
+ }
+
+ delete infop;
+ infop = NULL;
+}
+
+
+// We've got the name of the person who owns the object hurling the url.
+// Display confirmation dialog.
+void callback_load_url_name(const LLUUID& id, const char* first, const char* last, BOOL is_group, void* data)
+{
+ std::vector<LoadUrlInfo*>::iterator it;
+ for (it = gLoadUrlList.begin(); it != gLoadUrlList.end(); )
+ {
+ LoadUrlInfo* infop = *it;
+ if (infop->mOwnerID == id)
+ {
+ it = gLoadUrlList.erase(it);
+
+ std::string owner_name(first);
+ if (is_group)
+ {
+ owner_name += " (group)";
+ }
+ else
+ {
+ owner_name += " ";
+ owner_name += last;
+ }
+
+ // TODO: Talk to james about using an id instead of a name for this.
+ if (gMuteListp->isMuted(LLUUID::null, owner_name))
+ {
+ delete infop;
+ infop = NULL;
+ continue;
+ }
+ LLString::format_map_t args;
+ args["[URL]"] = infop->mUrl;
+ args["[MESSAGE]"] = infop->mMessage;
+ args["[OBJECTNAME]"] = infop->mObjectName;
+ args["[NAME]"] = owner_name;
+ LLNotifyBox::showXml("LoadWebPage", args, callback_load_url, infop);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
+
+void process_load_url(LLMessageSystem* msg, void**)
+{
+ LoadUrlInfo* infop = new LoadUrlInfo;
+
+ msg->getString("Data", "ObjectName", 256, infop->mObjectName);
+ msg->getUUID( "Data", "ObjectID", infop->mObjectID);
+ msg->getUUID( "Data", "OwnerID", infop->mOwnerID);
+ msg->getBOOL( "Data", "OwnerIsGroup", infop->mOwnerIsGroup);
+ msg->getString("Data", "Message", 256, infop->mMessage);
+ msg->getString("Data", "URL", 256, infop->mUrl);
+
+ // URL is safety checked in load_url above
+
+ // Check if object or owner is muted
+ if (gMuteListp->isMuted(infop->mObjectID, infop->mObjectName))
+ {
+ delete infop;
+ infop = NULL;
+ return;
+ }
+
+ // Add to list of pending name lookups
+ gLoadUrlList.push_back(infop);
+
+ gCacheName->get(infop->mOwnerID, infop->mOwnerIsGroup, callback_load_url_name);
+}
+
+
+void callback_download_complete(void** data, S32 result)
+{
+ LLString* filepath = (LLString*)data;
+ LLString::format_map_t args;
+ args["[DOWNLOAD_PATH]"] = *filepath;
+ gViewerWindow->alertXml("FinishedRawDownload", args);
+ delete filepath;
+}
+
+
+void process_initiate_download(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "Initiate download for wrong agent" << llendl;
+ return;
+ }
+
+ char sim_filename[MAX_PATH];
+ char viewer_filename[MAX_PATH];
+ msg->getString("FileData", "SimFilename", MAX_PATH, sim_filename);
+ msg->getString("FileData", "ViewerFilename", MAX_PATH, viewer_filename);
+
+ gXferManager->requestFile(viewer_filename,
+ sim_filename,
+ LL_PATH_NONE,
+ msg->getSender(),
+ FALSE, // don't delete remote
+ callback_download_complete,
+ (void**)new LLString(viewer_filename));
+}
+
+
+void process_script_teleport_request(LLMessageSystem* msg, void**)
+{
+ char object_name[256];
+ char sim_name[256];
+ LLVector3 pos;
+ LLVector3 look_at;
+
+ msg->getString("Data", "ObjectName", 255, object_name);
+ msg->getString( "Data", "SimName", 255, sim_name);
+ msg->getVector3("Data", "SimPosition", pos);
+ msg->getVector3("Data", "LookAt", look_at);
+
+ gFloaterWorldMap->trackURL(sim_name, (U32)pos.mV[VX], (U32)pos.mV[VY], (U32)pos.mV[VZ]);
+ LLFloaterWorldMap::show(NULL, TRUE);
+}
+
+void process_covenant_reply(LLMessageSystem* msg, void**)
+{
+ LLUUID covenant_id, estate_owner_id;
+ char estate_name[MAX_STRING];
+ U32 covenant_timestamp;
+ msg->getUUID("Data", "CovenantID", covenant_id);
+ msg->getU32("Data", "CovenantTimestamp", covenant_timestamp);
+ msg->getString("Data", "EstateName", MAX_STRING, estate_name);
+ msg->getUUID("Data", "EstateOwnerID", estate_owner_id);
+
+ LLPanelEstateCovenant::updateEstateName(estate_name);
+ LLPanelLandCovenant::updateEstateName(estate_name);
+ LLFloaterBuyLand::updateEstateName(estate_name);
+
+ // standard message, not from system
+ char last_modified[MAX_STRING];
+ last_modified[0] = '\0';
+ char time_buf[TIME_STR_LENGTH];
+ sprintf(last_modified, "Last Modified %s",
+ formatted_time((time_t)covenant_timestamp, time_buf));
+
+ LLPanelEstateCovenant::updateLastModified(last_modified);
+ LLPanelLandCovenant::updateLastModified(last_modified);
+ LLFloaterBuyLand::updateLastModified(last_modified);
+
+ gCacheName->getName(estate_owner_id, callbackCacheEstateOwnerName);
+
+ // load the actual covenant asset data
+ const BOOL high_priority = TRUE;
+ if (covenant_id.notNull())
+ {
+ gAssetStorage->getEstateAsset(gAgent.getRegionHost(),
+ gAgent.getID(),
+ gAgent.getSessionID(),
+ covenant_id,
+ LLAssetType::AT_NOTECARD,
+ ET_Covenant,
+ onCovenantLoadComplete,
+ NULL,
+ high_priority);
+ }
+ else
+ {
+ std::string covenant_text;
+ if (estate_owner_id.isNull())
+ {
+ // mainland
+ covenant_text = "There is no Covenant provided for this Estate.";
+ }
+ else
+ {
+ covenant_text = "There is no Covenant provided for this Estate. The land on this estate is being sold by the Estate owner, not Linden Lab. Please contact the Estate Owner for sales details.";
+ }
+ LLPanelEstateCovenant::updateCovenantText(covenant_text, covenant_id);
+ LLPanelLandCovenant::updateCovenantText(covenant_text);
+ LLFloaterBuyLand::updateCovenantText(covenant_text, covenant_id);
+ }
+}
+
+void callbackCacheEstateOwnerName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*)
+{
+ std::string name;
+
+ if (id.isNull())
+ {
+ name = "(none)";
+ }
+ else
+ {
+ name = first;
+ name += " ";
+ name += last;
+ }
+ LLPanelEstateCovenant::updateEstateOwnerName(name);
+ LLPanelLandCovenant::updateEstateOwnerName(name);
+ LLFloaterBuyLand::updateEstateOwnerName(name);
+}
+
+void onCovenantLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status)
+{
+ llinfos << "onCovenantLoadComplete()" << llendl;
+ std::string covenant_text;
+ if(0 == status)
+ {
+ LLVFile file(vfs, asset_uuid, type, LLVFile::READ);
+
+ S32 file_length = file.getSize();
+
+ char* buffer = new char[file_length+1];
+ file.read((U8*)buffer, file_length);
+
+ // put a EOS at the end
+ buffer[file_length] = 0;
+
+ if( (file_length > 19) && !strncmp( buffer, "Linden text version", 19 ) )
+ {
+ LLViewerTextEditor* editor = new LLViewerTextEditor("temp",
+ LLRect(0,0,0,0),
+ file_length+1);
+ if( !editor->importBuffer( buffer ) )
+ {
+ llwarns << "Problem importing estate covenant." << llendl;
+ covenant_text = "Problem importing estate covenant.";
+ }
+ else
+ {
+ // Version 0 (just text, doesn't include version number)
+ covenant_text = editor->getText();
+ }
+ delete[] buffer;
+ delete editor;
+ }
+ else
+ {
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ if( LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE == status ||
+ LL_ERR_FILE_EMPTY == status)
+ {
+ covenant_text = "Estate covenant notecard is missing from database.";
+ }
+ else if (LL_ERR_INSUFFICIENT_PERMISSIONS == status)
+ {
+ covenant_text = "Insufficient permissions to view estate covenant.";
+ }
+ else
+ {
+ covenant_text = "Unable to load estate covenant at this time.";
+ }
+
+ llwarns << "Problem loading notecard: " << status << llendl;
+ }
+ }
+ LLPanelEstateCovenant::updateCovenantText(covenant_text, asset_uuid);
+ LLPanelLandCovenant::updateCovenantText(covenant_text);
+ LLFloaterBuyLand::updateCovenantText(covenant_text, asset_uuid);
+}
+
+void send_generic_message(const char* method,
+ const std::vector<std::string>& strings,
+ const LLUUID& invoice)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("GenericMessage");
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlock("MethodData");
+ msg->addString("Method", method);
+ msg->addUUID("Invoice", invoice);
+ if(strings.empty())
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", NULL);
+ }
+ else
+ {
+ std::vector<std::string>::const_iterator it = strings.begin();
+ std::vector<std::string>::const_iterator end = strings.end();
+ for(; it != end; ++it)
+ {
+ msg->nextBlock("ParamList");
+ msg->addString("Parameter", (*it).c_str());
+ }
+ }
+ gAgent.sendReliableMessage();
+}
+
+
+void process_generic_message(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+ if (agent_id != gAgent.getID())
+ {
+ llwarns << "GenericMessage for wrong agent" << llendl;
+ return;
+ }
+
+ std::string request;
+ LLUUID invoice;
+ LLDispatcher::sparam_t strings;
+ LLDispatcher::unpackMessage(msg, request, invoice, strings);
+
+ if(!gGenericDispatcher.dispatch(request, invoice, strings))
+ {
+ llwarns << "GenericMessage " << request << " failed to dispatch"
+ << llendl;
+ }
+}
+
+void process_feature_disabled_message(LLMessageSystem* msg, void**)
+{
+ // Handle Blacklisted feature simulator response...
+ LLUUID agentID;
+ LLUUID transactionID;
+ char messageText[MAX_STRING];
+ msg->getStringFast(_PREHASH_FailureInfo,_PREHASH_ErrorMessage,MAX_STRING,&messageText[0],0);
+ msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_AgentID,agentID);
+ msg->getUUIDFast(_PREHASH_FailureInfo,_PREHASH_TransactionID,transactionID);
+
+ llwarns << "Blacklisted Feature Response:" << &messageText[0] << llendl;
+}
+
+// ------------------------------------------------------------
+// Message system exception callbacks
+// ------------------------------------------------------------
+
+void invalid_message_callback(LLMessageSystem* msg,
+ void*,
+ EMessageException exception)
+{
+ bad_network_handler();
+}
diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h
new file mode 100644
index 0000000000..c7d22656c7
--- /dev/null
+++ b/indra/newview/llviewermessage.h
@@ -0,0 +1,203 @@
+/**
+ * @file llviewermessage.h
+ * @brief Message system callbacks for viewer.
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERMESSAGE_H
+#define LL_LLVIEWERMESSAGE_H
+
+//#include "linked_lists.h"
+#include "llinstantmessage.h"
+#include "lltransactiontypes.h"
+#include "lluuid.h"
+#include "stdenums.h"
+#include "message.h"
+
+//
+// Forward declarations
+//
+class LLColor4;
+class LLViewerObject;
+class LLInventoryObject;
+class LLInventoryItem;
+class LLViewerRegion;
+
+//
+// Prototypes
+//
+
+BOOL can_afford_transaction(S32 cost);
+void give_money(const LLUUID& uuid, LLViewerRegion* region, S32 amount, BOOL is_group = FALSE,
+ S32 trx_type = TRANS_GIFT, const LLString& desc = LLString::null);
+void busy_message (LLMessageSystem* msg, LLUUID from_id);
+
+void process_logout_reply(LLMessageSystem* msg, void**);
+void process_layer_data(LLMessageSystem *mesgsys, void **user_data);
+void process_derez_ack(LLMessageSystem*, void**);
+void process_places_reply(LLMessageSystem* msg, void** data);
+void send_sound_trigger(const LLUUID& sound_id, F32 gain);
+void process_improved_im(LLMessageSystem *msg, void **user_data);
+void process_script_question(LLMessageSystem *msg, void **user_data);
+void process_chat_from_simulator(LLMessageSystem *mesgsys, void **user_data);
+
+//void process_agent_to_new_region(LLMessageSystem *mesgsys, void **user_data);
+void send_agent_update(BOOL force_send, BOOL send_reliable = FALSE);
+void process_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_compressed_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_cached_object_update(LLMessageSystem *mesgsys, void **user_data);
+void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_data);
+
+void send_simulator_throttle_settings(const LLHost &host);
+void process_kill_object( LLMessageSystem *mesgsys, void **user_data);
+void process_time_synch( LLMessageSystem *mesgsys, void **user_data);
+void process_sound_trigger(LLMessageSystem *mesgsys, void **user_data);
+void process_preload_sound( LLMessageSystem *mesgsys, void **user_data);
+void process_attached_sound( LLMessageSystem *mesgsys, void **user_data);
+void process_attached_sound_gain_change( LLMessageSystem *mesgsys, void **user_data);
+//void process_attached_sound_cutoff_radius( LLMessageSystem *mesgsys, void **user_data);
+void process_energy_statistics(LLMessageSystem *mesgsys, void **user_data);
+void process_health_message(LLMessageSystem *mesgsys, void **user_data);
+void process_sim_stats(LLMessageSystem *mesgsys, void **user_data);
+void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data);
+void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data);
+void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data);
+void process_avatar_sit_response(LLMessageSystem *mesgsys, void **user_data);
+void process_set_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data);
+void process_clear_follow_cam_properties(LLMessageSystem *mesgsys, void **user_data);
+void process_name_value(LLMessageSystem *mesgsys, void **user_data);
+void process_remove_name_value(LLMessageSystem *mesgsys, void **user_data);
+void process_kick_user(LLMessageSystem *msg, void** /*user_data*/);
+//void process_avatar_init_complete(LLMessageSystem *msg, void** /*user_data*/);
+
+void process_economy_data(LLMessageSystem *msg, void** /*user_data*/);
+void process_money_balance_reply(LLMessageSystem* msg_system, void**);
+void process_adjust_balance(LLMessageSystem* msg_system, void**);
+
+void process_alert_message(LLMessageSystem* msg, void**);
+void process_agent_alert_message(LLMessageSystem* msgsystem, void** user_data);
+void process_alert_core(const char* buffer, BOOL modal);
+
+// "Mean" or player-vs-player abuse
+void handle_show_mean_events(void *);
+void process_mean_collision_alert_message(LLMessageSystem* msg, void**);
+
+void process_frozen_message(LLMessageSystem* msg, void**);
+
+void process_derez_container(LLMessageSystem *msg, void**);
+void container_inventory_arrived(LLViewerObject* object,
+ std::list<LLPointer<LLInventoryObject> >* inventory, //InventoryObjectList
+ S32 serial_num,
+ void* data);
+
+// agent movement
+void send_complete_agent_movement(const LLHost& sim_host);
+void process_agent_movement_complete(LLMessageSystem* msg, void**);
+void process_crossed_region(LLMessageSystem* msg, void**);
+void process_teleport_start(LLMessageSystem* msg, void**);
+void process_teleport_progress(LLMessageSystem* msg, void**);
+void process_teleport_failed(LLMessageSystem *msg,void**);
+void process_teleport_finish(LLMessageSystem *msg, void**);
+
+//void process_user_sim_location_reply(LLMessageSystem *msg,void**);
+void process_teleport_local(LLMessageSystem *msg,void**);
+void process_user_sim_location_reply(LLMessageSystem *msg,void**);
+
+void send_simple_im(const LLUUID& to_id,
+ const char* message,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null);
+
+void send_group_notice(const LLUUID& group_id,
+ const char* subject,
+ const char* message,
+ const LLInventoryItem* item);
+
+void handle_lure(const LLUUID& invitee);
+void handle_lure(LLDynamicArray<LLUUID>& ids);
+
+// always from gAgent and
+// routes through the gAgent's current simulator
+void send_improved_im(const LLUUID& to_id,
+ const char* name,
+ const char* message,
+ U8 offline = IM_ONLINE,
+ EInstantMessage dialog = IM_NOTHING_SPECIAL,
+ const LLUUID& id = LLUUID::null,
+ U32 timestamp = NO_TIMESTAMP,
+ const U8* binary_bucket = (U8*)EMPTY_BINARY_BUCKET,
+ S32 binary_bucket_size = EMPTY_BINARY_BUCKET_SIZE);
+
+void process_user_info_reply(LLMessageSystem* msg, void**);
+
+// method to format the time. Buffer should be at least
+// TIME_STR_LENGTH long, and the function reutnrs buffer (for use in
+// sprintf and the like)
+const S32 TIME_STR_LENGTH = 30;
+char* formatted_time(const time_t& the_time, char* buffer);
+
+void send_places_query(const LLUUID& query_id,
+ const LLUUID& trans_id,
+ const char* query_text,
+ U32 query_flags,
+ S32 category,
+ const char* sim_name);
+void process_script_dialog(LLMessageSystem* msg, void**);
+void process_load_url(LLMessageSystem* msg, void**);
+void process_script_teleport_request(LLMessageSystem* msg, void**);
+void process_covenant_reply(LLMessageSystem* msg, void**);
+void onCovenantLoadComplete(LLVFS *vfs,
+ const LLUUID& asset_uuid,
+ LLAssetType::EType type,
+ void* user_data, S32 status);
+void callbackCacheEstateOwnerName(
+ const LLUUID& id,
+ const char* first,
+ const char* last,
+ BOOL is_group,
+ void*);
+
+// calling cards
+void process_offer_callingcard(LLMessageSystem* msg, void**);
+void process_accept_callingcard(LLMessageSystem* msg, void**);
+void process_decline_callingcard(LLMessageSystem* msg, void**);
+
+// Message system exception prototypes
+void invalid_message_callback(LLMessageSystem*, void*, EMessageException);
+
+void send_lure_911(void** user_data, S32 result);
+
+void process_initiate_download(LLMessageSystem* msg, void**);
+void inventory_offer_callback(S32 option, void* user_data);
+
+struct LLOfferInfo
+{
+ EInstantMessage mIM;
+ LLUUID mFromID;
+ BOOL mFromGroup;
+ BOOL mFromObject;
+ LLUUID mTransactionID;
+ LLUUID mFolderID;
+ LLUUID mObjectID;
+ LLAssetType::EType mType;
+ LLString mFromName;
+ LLString mDesc;
+ LLHost mHost;
+};
+
+void send_generic_message(const char* method,
+ const std::vector<std::string>& strings,
+ const LLUUID& invoice = LLUUID::null);
+
+void process_generic_message(LLMessageSystem* msg, void**);
+
+void process_feature_disabled_message(LLMessageSystem* msg, void**);
+
+extern LLDispatcher gGenericDispatcher;
+
+#endif
+
diff --git a/indra/newview/llviewernetwork.cpp b/indra/newview/llviewernetwork.cpp
new file mode 100644
index 0000000000..aecc015e0e
--- /dev/null
+++ b/indra/newview/llviewernetwork.cpp
@@ -0,0 +1,70 @@
+/**
+ * @file llviewernetwork.cpp
+ * @author James Cook, Richard Nelson
+ * @brief Networking constants and globals for viewer.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewernetwork.h"
+
+LLUserServerData gUserServerDomainName[USERSERVER_COUNT] =
+{
+ { "None", "", "", ""},
+ { "Aditi",
+ "userserver.aditi.lindenlab.com",
+ "https://login.aditi.lindenlab.com/cgi-bin/login.cgi",
+ "http://aditi-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Agni",
+ "userserver.agni.lindenlab.com",
+ "https://login.agni.lindenlab.com/cgi-bin/login.cgi",
+ "https://secondlife.com/helpers/" },
+ { "DMZ",
+ "userserver.dmz.lindenlab.com",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "http://dmz-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Siva",
+ "userserver.siva.lindenlab.com",
+ "https://login.siva.lindenlab.com/cgi-bin/login.cgi",
+ "http://siva-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Durga",
+ "userserver.durga.lindenlab.com",
+ "https://login.durga.lindenlab.com/cgi-bin/login.cgi",
+ "http://durga-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Shakti",
+ "userserver.shakti.lindenlab.com",
+ "https://login.shakti.lindenlab.com/cgi-bin/login.cgi",
+ "http://shakti-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Soma",
+ "userserver.soma.lindenlab.com",
+ "https://login.soma.lindenlab.com/cgi-bin/login.cgi",
+ "http://soma-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Ganga",
+ "userserver.ganga.lindenlab.com",
+ "https://login.ganga.lindenlab.com/cgi-bin/login.cgi",
+ "http://ganga-secondlife.webdev.lindenlab.com/helpers/" },
+ { "Local",
+ "localhost",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "" },
+ { "Other",
+ "",
+ "https://login.dmz.lindenlab.com/cgi-bin/login.cgi",
+ "" }
+};
+
+// Use this to figure out which domain name and login URI to use.
+
+EUserServerDomain gUserServerChoice = USERSERVER_NONE;
+char gUserServerName[MAX_STRING];
+
+LLHost gUserServer;
+
+F32 gPacketDropPercentage = 0.f;
+F32 gInBandwidth = 0.f;
+F32 gOutBandwidth = 0.f;
+
+unsigned char gMACAddress[MAC_ADDRESS_BYTES];
diff --git a/indra/newview/llviewernetwork.h b/indra/newview/llviewernetwork.h
new file mode 100644
index 0000000000..acb1b53525
--- /dev/null
+++ b/indra/newview/llviewernetwork.h
@@ -0,0 +1,52 @@
+/**
+ * @file llviewernetwork.h
+ * @author James Cook
+ * @brief Networking constants and globals for viewer.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERNETWORK_H
+#define LL_LLVIEWERNETWORK_H
+
+class LLHost;
+
+enum EUserServerDomain
+{
+ USERSERVER_NONE,
+ USERSERVER_ADITI,
+ USERSERVER_AGNI,
+ USERSERVER_DMZ,
+ USERSERVER_SIVA,
+ USERSERVER_DURGA,
+ USERSERVER_SHAKTI,
+ USERSERVER_SOMA,
+ USERSERVER_GANGA,
+ USERSERVER_LOCAL,
+ USERSERVER_OTHER, // IP address set via -user or other command line option
+ USERSERVER_COUNT
+};
+
+
+struct LLUserServerData
+{
+ const char* mLabel;
+ const char* mName;
+ const char* mLoginURI;
+ const char* mHelperURI;
+};
+
+extern LLHost gUserServer;
+
+extern F32 gPacketDropPercentage;
+extern F32 gInBandwidth;
+extern F32 gOutBandwidth;
+extern EUserServerDomain gUserServerChoice;
+extern LLUserServerData gUserServerDomainName[];
+extern char gUserServerName[MAX_STRING];
+
+const S32 MAC_ADDRESS_BYTES = 6;
+extern unsigned char gMACAddress[MAC_ADDRESS_BYTES];
+
+#endif
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
new file mode 100644
index 0000000000..b1e4ee1947
--- /dev/null
+++ b/indra/newview/llviewerobject.cpp
@@ -0,0 +1,4683 @@
+/**
+ * @file llviewerobject.cpp
+ * @brief Base class for viewer objects
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobject.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llflexibleobject.h"
+#include "llviewercontrol.h"
+#include "lldatapacker.h"
+#include "llfasttimer.h"
+#include "llfontgl.h"
+#include "llframetimer.h"
+#include "llinventory.h"
+#include "llmaterialtable.h"
+#include "llmutelist.h"
+#include "llnamevalue.h"
+#include "llprimitive.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "lltree_common.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "timing.h"
+
+#include "llaudiosourcevo.h"
+#include "llagent.h"
+#include "llbbox.h"
+#include "llbox.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llfloaterproperties.h"
+#include "llfollowcam.h"
+#include "llnetmap.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "lltooldraganddrop.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermedialist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llviewerwindow.h" // For getSpinAxis
+#include "llvoavatar.h"
+#include "llvoclouds.h"
+#include "llvograss.h"
+#include "llvoground.h"
+#include "llvolume.h"
+#include "llvolumemessage.h"
+#include "llvopart.h"
+#include "llvopartgroup.h"
+#include "llvosky.h"
+#include "llvostars.h"
+#include "llvosurfacepatch.h"
+#include "llvotextbubble.h"
+#include "llvotree.h"
+#include "llvotreenew.h"
+#include "llvovolume.h"
+#include "llvowater.h"
+#include "llworld.h"
+#include "llui.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+//#define DEBUG_UPDATE_TYPE
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern U32 gFrameCount;
+
+U32 LLViewerObject::sNumZombieObjects = 0;
+S32 LLViewerObject::sNumObjects = 0;
+BOOL LLViewerObject::sMapDebug = TRUE;
+LLColor4 LLViewerObject::sEditSelectColor( 1.0f, 1.f, 0.f, 0.3f); // Edit OK
+LLColor4 LLViewerObject::sNoEditSelectColor( 1.0f, 0.f, 0.f, 0.3f); // Can't edit
+S32 LLViewerObject::sAxisArrowLength(50);
+BOOL LLViewerObject::sPulseEnabled(FALSE);
+BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE
+
+// static
+LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *res = NULL;
+ LLFastTimer t1(LLFastTimer::FTM_CREATE_OBJECT);
+
+ switch (pcode)
+ {
+ case LL_PCODE_VOLUME:
+ res = new LLVOVolume(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_AVATAR:
+ res = new LLVOAvatar(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_GRASS:
+ res = new LLVOGrass(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_PART_SYS:
+ res = new LLVOPart(id, pcode, regionp); break;
+ case LL_PCODE_LEGACY_TREE:
+ res = new LLVOTree(id, pcode, regionp); break;
+ case LL_PCODE_TREE_NEW:
+ llwarns << "Creating new tree!" << llendl;
+// res = new LLVOTree(id, pcode, regionp); break;
+// res = new LLVOTreeNew(id, pcode, regionp); break;
+ res = NULL; break;
+ case LL_PCODE_LEGACY_TEXT_BUBBLE:
+ res = new LLVOTextBubble(id, pcode, regionp); break;
+ case LL_VO_CLOUDS:
+ res = new LLVOClouds(id, pcode, regionp); break;
+ case LL_VO_SURFACE_PATCH:
+ res = new LLVOSurfacePatch(id, pcode, regionp); break;
+ case LL_VO_SKY:
+ res = new LLVOSky(id, pcode, regionp); break;
+ case LL_VO_STARS:
+ res = new LLVOStars(id, pcode, regionp); break;
+ case LL_VO_WATER:
+ res = new LLVOWater(id, pcode, regionp); break;
+ case LL_VO_GROUND:
+ res = new LLVOGround(id, pcode, regionp); break;
+ case LL_VO_PART_GROUP:
+ res = new LLVOPartGroup(id, pcode, regionp); break;
+ default:
+ llwarns << "Unknown object pcode " << (S32)pcode << llendl;
+ res = NULL; break;
+ }
+ return res;
+}
+
+LLViewerObject::LLViewerObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLPrimitive(),
+ mChildList(),
+ mID(id),
+ mLocalID(0),
+ mTotalCRC(0),
+ mTEImages(NULL),
+ mGLName(0),
+ mbCanSelect(TRUE),
+ mFlags(0),
+ mDrawable(),
+ mCreateSelected(FALSE),
+ mRenderMedia(FALSE),
+ mBestUpdatePrecision(0),
+ mText(),
+ mLastInterpUpdateSecs(0.f),
+ mLastMessageUpdateSecs(0.f),
+ mData(NULL),
+ mAudioSourcep(NULL),
+ mAppAngle(0.f),
+ mPixelArea(0.f),
+ mInventory(NULL),
+ mInventorySerialNum(0),
+ mRegionp( regionp ),
+ mInventoryPending(FALSE),
+ mInventoryDirty(FALSE),
+ mDead(FALSE),
+ mOrphaned(FALSE),
+ mUserSelected(FALSE),
+ mOnActiveList(FALSE),
+ mOnMap(FALSE),
+ mStatic(FALSE),
+ mFaceIndexOffset(0),
+ mNumFaces(0),
+ mLastUpdateFrame(0),
+ mTimeDilation(1.f),
+ mRotTime(0.f),
+ mJointInfo(NULL),
+ mState(0),
+ mMedia(NULL),
+ mClickAction(0)
+{
+ llassert(mRegionp);
+
+ LLPrimitive::init(pcode);
+
+ // CP: added 12/2/2005 - this was being initialised to 0, not the current frame time
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+
+ mPositionRegion = LLVector3(0.f, 0.f, 0.f);
+ mPositionAgent = mRegionp->getOriginAgent();
+
+ LLViewerObject::sNumObjects++;
+}
+
+LLViewerObject::~LLViewerObject()
+{
+ deleteTEImages();
+
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ }
+
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Delete memory associated with extra parameters.
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ delete iter->second->data;
+ delete iter->second;
+ }
+ mExtraParameterList.clear();
+
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ delete[] mData;
+ mData = NULL;
+
+ delete mMedia;
+ mMedia = NULL;
+
+ sNumObjects--;
+ sNumZombieObjects--;
+ llassert(mChildList.size() == 0);
+
+ clearInventoryListeners();
+}
+
+void LLViewerObject::deleteTEImages()
+{
+ delete[] mTEImages;
+ mTEImages = NULL;
+}
+
+void LLViewerObject::markDead()
+{
+ if (!mDead)
+ {
+ //llinfos << "Marking self " << mLocalID << " as dead." << llendl;
+
+ // Root object of this hierarchy unlinks itself.
+ if (getParent())
+ {
+ ((LLViewerObject *)getParent())->removeChild(this);
+ // go ahead and delete any jointinfo's that we find
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ // Mark itself as dead
+ mDead = TRUE;
+ gObjectList.cleanupReferences(this);
+
+ LLViewerObject *childp;
+ while (mChildList.size() > 0)
+ {
+ childp = mChildList[0];
+ if (childp->getPCode() != LL_PCODE_LEGACY_AVATAR)
+ {
+ //llinfos << "Marking child " << childp->getLocalID() << " as dead." << llendl;
+ childp->setParent(NULL); // LLViewerObject::markDead 1
+ childp->markDead();
+ }
+ else
+ {
+ // make sure avatar is no longer parented,
+ // so we can properly set it's position
+ childp->setDrawableParent(NULL);
+ ((LLVOAvatar*)childp)->getOffObject();
+ childp->setParent(NULL); // LLViewerObject::markDead 2
+ }
+ mChildList.erase(mChildList.begin());
+ }
+
+ if (mDrawable.notNull())
+ {
+ // Drawables are reference counted, mark as dead, then nuke the pointer.
+ mDrawable->markDead();
+ mDrawable = NULL;
+ }
+
+ if (mText)
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (mIcon)
+ {
+ mIcon->markDead();
+ mIcon = NULL;
+ }
+
+ if (mPartSourcep)
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+
+ if (mAudioSourcep)
+ {
+ // Do some cleanup
+ if (gAudiop)
+ {
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ }
+ mAudioSourcep = NULL;
+ }
+
+ if (flagAnimSource())
+ {
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp && !avatarp->isDead())
+ {
+ // stop motions associated with this object
+ avatarp->stopMotionFromSource(mID);
+ }
+ }
+
+ if (flagCameraSource())
+ {
+ LLFollowCamMgr::removeFollowCamParams(mID);
+ }
+
+ sNumZombieObjects++;
+ }
+}
+
+void LLViewerObject::dump() const
+{
+ llinfos << "Type: " << pCodeToString(mPrimitiveCode) << llendl;
+ llinfos << "Drawable: " << (LLDrawable *)mDrawable << llendl;
+ llinfos << "Update Age: " << LLFrameTimer::getElapsedSeconds() - mLastMessageUpdateSecs << llendl;
+
+ llinfos << "Parent: " << getParent() << llendl;
+ llinfos << "ID: " << mID << llendl;
+ llinfos << "LocalID: " << mLocalID << llendl;
+ llinfos << "PositionRegion: " << getPositionRegion() << llendl;
+ llinfos << "PositionAgent: " << getPositionAgent() << llendl;
+ llinfos << "PositionGlobal: " << getPositionGlobal() << llendl;
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ if (mDrawable.notNull() && mDrawable->getNumFaces())
+ {
+ LLDrawPool *poolp = mDrawable->getFace(0)->getPool();
+ llinfos << "Pool: " << poolp << llendl;
+ llinfos << "Pool reference count: " << poolp->mReferences.size() << llendl;
+ llinfos << "Pool vertex count: " << poolp->getVertexCount() << llendl;
+ }
+ //llinfos << "BoxTree Min: " << mDrawable->getBox()->getMin() << llendl;
+ //llinfos << "BoxTree Max: " << mDrawable->getBox()->getMin() << llendl;
+ /*
+ llinfos << "Velocity: " << getVelocity() << llendl;
+ llinfos << "AnyOwner: " << permAnyOwner() << " YouOwner: " << permYouOwner() << " Edit: " << mPermEdit << llendl;
+ llinfos << "UsePhysics: " << usePhysics() << " CanSelect " << mbCanSelect << " UserSelected " << mUserSelected << llendl;
+ llinfos << "AppAngle: " << mAppAngle << llendl;
+ llinfos << "PixelArea: " << mPixelArea << llendl;
+
+ char buffer[1000];
+ char *key;
+ for (key = mNameValuePairs.getFirstKey(); key; key = mNameValuePairs.getNextKey() )
+ {
+ mNameValuePairs[key]->printNameValue(buffer);
+ llinfos << buffer << llendl;
+ }
+
+ S32 i;
+
+ LLViewerObject *child;
+ for (i = 0; i < mChildList.size(); i++)
+ {
+ child = mChildList[i];
+ llinfos << " child " << child->getID() << llendl;
+ }
+ */
+}
+
+void LLViewerObject::printNameValuePairs() const
+{
+ for (name_value_map_t::const_iterator iter = mNameValuePairs.begin();
+ iter != mNameValuePairs.end(); iter++)
+ {
+ LLNameValue* nv = iter->second;
+ llinfos << nv->printNameValue() << llendl;
+ }
+}
+
+void LLViewerObject::initVOClasses()
+{
+ // Initialized shared class stuff first.
+ LLVOAvatar::initClass();
+ LLVOTree::initClass();
+ if (gNoRender)
+ {
+ // Don't init anything else in drone mode
+ return;
+ }
+ llinfos << "Viewer Object size: " << sizeof(LLViewerObject) << llendl;
+ LLVOGrass::initClass();
+ LLVOPart::initClass();
+ LLVOWater::initClass();
+ LLVOSky::initClass();
+ LLVOVolume::initClass();
+}
+
+void LLViewerObject::cleanupVOClasses()
+{
+ LLVOGrass::cleanupClass();
+ LLVOWater::cleanupClass();
+ LLVOTree::cleanupClass();
+ LLVOAvatar::cleanupClass();
+}
+
+// Replaces all name value pairs with data from \n delimited list
+// Does not update server
+void LLViewerObject::setNameValueList(char* name_value_list)
+{
+ // Clear out the old
+ for_each(mNameValuePairs.begin(), mNameValuePairs.end(), DeletePairedPointer()) ;
+ mNameValuePairs.clear();
+
+ // Bring in the new
+ char* token_start = name_value_list;
+ char* scan = name_value_list;
+
+ if (*scan == '\0') return;
+
+ BOOL done = FALSE;
+ while (!done)
+ {
+ while ( (*scan != '\0') && (*scan != '\n') )
+ {
+ scan++;
+ }
+
+ if (*scan == '\n')
+ {
+ *scan = '\0';
+ addNVPair(token_start);
+ scan++;
+ token_start = scan;
+ }
+ else
+ {
+ addNVPair(token_start);
+ done = TRUE;
+ }
+ }
+}
+
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverAgentOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedSelf(getPositionRegion());
+}
+
+// This method returns true if the object is over land owned by the
+// agent.
+BOOL LLViewerObject::isOverGroupOwnedLand() const
+{
+ return mRegionp
+ && mRegionp->getParcelOverlay()
+ && mRegionp->getParcelOverlay()->isOwnedGroup(getPositionRegion());
+}
+
+void LLViewerObject::setParent(LLViewerObject* parent)
+{
+ LLPrimitive::setParent(parent);
+}
+
+void LLViewerObject::addChild(LLViewerObject *childp)
+{
+ BOOL result = TRUE;
+
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ { //already has child
+ return;
+ }
+ }
+
+ if (!isAvatar())
+ {
+ // propagate selection properties
+ childp->mbCanSelect = mbCanSelect;
+ }
+
+ childp->setParent(this);
+ mChildList.push_back(childp);
+
+ if (!result)
+ {
+ llwarns << "Failed to attach child " << childp->getID() << " to object " << getID() << llendl;
+ removeChild(childp);
+ if (mJointInfo)
+ {
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+ }
+}
+
+void LLViewerObject::removeChild(LLViewerObject *childp)
+{
+ for (child_list_t::iterator i = mChildList.begin(); i != mChildList.end(); ++i)
+ {
+ if (*i == childp)
+ {
+ if (!childp->isAvatar() && mDrawable.notNull() && mDrawable->isActive() && childp->mDrawable.notNull() && !isAvatar())
+ {
+ gPipeline.markRebuild(childp->mDrawable, LLDrawable::REBUILD_VOLUME);
+ }
+
+ mChildList.erase(i);
+ childp->setParent(NULL);
+ break;
+ }
+ }
+
+ if (childp->isSelected())
+ {
+ gSelectMgr->deselectObjectAndFamily(childp);
+ BOOL add_to_end = TRUE;
+ gSelectMgr->selectObjectAndFamily(childp, add_to_end);
+ }
+}
+
+LLViewerObject::child_list_t& LLViewerObject::getChildren()
+{
+ return mChildList;
+}
+
+void LLViewerObject::addThisAndAllChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if (!mChildList[i]->isAvatar())
+ {
+ (mChildList[i])->addThisAndAllChildren(objects);
+ }
+ }
+}
+
+void LLViewerObject::addThisAndNonJointChildren(LLDynamicArray<LLViewerObject*>& objects)
+{
+ objects.put(this);
+ // don't add any attachments when temporarily selecting avatar
+ if (isAvatar())
+ {
+ return;
+ }
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ if ( (!mChildList[i]->isAvatar())
+ && (!mChildList[i]->isJointChild()))
+ {
+ (mChildList[i])->addThisAndNonJointChildren(objects);
+ }
+ }
+}
+
+BOOL LLViewerObject::isChild(LLViewerObject *childp) const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *testChildp = &(*mChildList[i]);
+ if (testChildp == childp) return TRUE;
+ }
+ return FALSE;
+}
+
+
+// returns TRUE if at least one avatar is sitting on this object
+BOOL LLViewerObject::isSeat() const
+{
+ S32 count = mChildList.size();
+ for(S32 i = 0; i < count; i++)
+ {
+ const LLViewerObject *childp = &(*mChildList[i]);
+ if (childp->isAvatar())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+
+}
+
+BOOL LLViewerObject::setDrawableParent(LLDrawable* parentp)
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ mDrawable->mParent = parentp;
+
+ BOOL ret = mDrawable->mXform.setParent(parentp ? &parentp->mXform : NULL);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ gPipeline.markMoved(mDrawable, FALSE);
+
+ return ret;
+}
+
+U32 LLViewerObject::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 retval = 0x0;
+
+ // Coordinates of objects on simulators are region-local.
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ mRegionp = gWorldPointer->getRegionFromHandle(region_handle);
+ if (!mRegionp)
+ {
+ U32 x, y;
+ from_region_handle(region_handle, &x, &y);
+
+ llerrs << "Object has invalid region " << x << ":" << y << "!" << llendl;
+ }
+
+ U16 time_dilation16;
+ mesgsys->getU16Fast(_PREHASH_RegionData, _PREHASH_TimeDilation, time_dilation16);
+ F32 time_dilation = ((F32) time_dilation16) / 65535.f;
+ mTimeDilation = time_dilation;
+ mRegionp->setTimeDilation(time_dilation);
+
+ // this will be used to determine if we've really changed position
+ // 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;
+ const F32 size = gWorldPointer->getRegionWidthInMeters();
+ const F32 MAX_HEIGHT = gWorldPointer->getRegionMaxHeight();
+ const F32 MIN_HEIGHT = gWorldPointer->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;
+ LLQuaternion new_rot;
+ LLVector3 new_scale;
+
+ U32 parent_id = 0;
+ U8 material = 0;
+ U8 click_action = 0;
+ U32 crc = 0;
+
+ bool old_special_hover_cursor = specialHoverCursor();
+
+ LLViewerObject *cur_parentp = (LLViewerObject *)getParent();
+
+ if (cur_parentp)
+ {
+ parent_id = cur_parentp->mLocalID;
+ }
+
+ if (!dp)
+ {
+ switch(update_type)
+ {
+ case OUT_FULL:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "Full:" << getID() << llendl;
+#endif
+ LLUUID audio_uuid;
+ LLUUID owner_id; // only valid if audio_uuid or particle system is not null
+ F32 gain;
+ U8 sound_flags;
+
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_CRC, crc, block_num);
+ mesgsys->getU32Fast( _PREHASH_ObjectData, _PREHASH_ParentID, parent_id, block_num);
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_Sound, audio_uuid, block_num );
+ // HACK: Owner id only valid if non-null sound id or particle system
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_OwnerID, owner_id, block_num );
+ mesgsys->getF32Fast( _PREHASH_ObjectData, _PREHASH_Gain, gain, block_num );
+ mesgsys->getU8Fast( _PREHASH_ObjectData, _PREHASH_Flags, sound_flags, block_num );
+ 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;
+
+ // Owner ID used for sound muting or particle system muting
+ setAttachedSound(audio_uuid, owner_id, gain, sound_flags);
+
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ 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);
+ 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);
+ 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.
+ //
+
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // clear all but local flags
+ mFlags &= FLAGS_LOCAL;
+ mFlags |= flags;
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ // Set all name value pairs
+ S32 nv_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_NameValue);
+ if (nv_size > 0)
+ {
+ char* name_value_list = new char[nv_size];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_NameValue, nv_size, name_value_list, block_num);
+
+ setNameValueList(name_value_list);
+
+ delete [] name_value_list;
+ }
+
+ // Clear out any existing generic data
+ if (mData)
+ {
+ delete [] mData;
+ }
+
+ // Check for appended generic data
+ S32 data_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Data);
+ if (data_size == 0)
+ {
+ mData = NULL;
+ }
+ else
+ {
+ // ...has generic data
+ mData = new U8[data_size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, mData, data_size, block_num);
+ }
+
+ S32 text_size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_Text);
+ if (text_size > 1)
+ {
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_Text, 256, temp_string, block_num );
+
+ LLColor4U coloru;
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextColor, coloru.mV, 4, block_num);
+
+ // alpha was flipped so that it zero encoded better
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ if (mDrawable.notNull())
+ {
+ setChanged(MOVED | SILHOUETTE);
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ else if (mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ char media_url[MAX_STRING+1];
+ mesgsys->getStringFast(_PREHASH_ObjectData, _PREHASH_MediaURL, MAX_STRING+1, media_url, block_num);
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia && media_url[0] != '\0')
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia)
+ {
+ if (media_url[0] == '\0')
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ // We just added or changed a web page.
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+
+ //
+ // Unpack particle system data
+ //
+ unpackParticleSource(block_num, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra parameters
+ S32 size = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_ExtraParams);
+ if (size > 0)
+ {
+ U8 *buffer = new U8[size];
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_ExtraParams, buffer, size, block_num);
+ LLDataPackerBinaryBuffer dp(buffer, size);
+
+ U8 num_parameters;
+ dp.unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp.unpackU16(param_type, "param_type");
+ dp.unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+ delete[] buffer;
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ U8 joint_type = 0;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_JointType, joint_type, block_num);
+ if (joint_type)
+ {
+ // create new joint info
+ if (!mJointInfo)
+ {
+ mJointInfo = new LLVOJointInfo;
+ }
+ mJointInfo->mJointType = (EHavokJointType) joint_type;
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointPivot, mJointInfo->mPivot, block_num);
+ mesgsys->getVector3Fast(_PREHASH_ObjectData, _PREHASH_JointAxisOrAnchor, mJointInfo->mAxisOrAnchor, block_num);
+ }
+ else if (mJointInfo)
+ {
+ // this joint info is no longer needed
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ break;
+ }
+
+ case OUT_TERSE_IMPROVED:
+ {
+#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);
+ 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);
+ 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
+ setAngularVelocity( U16_to_F32(val[VX], -size, size),
+ U16_to_F32(val[VY], -size, size),
+ U16_to_F32(val[VZ], -size, size));
+ 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);
+
+ setAngularVelocity( U8_to_F32(data[13], -size, size),
+ U8_to_F32(data[14], -size, size),
+ U8_to_F32(data[15], -size, size) );
+ break;
+ }
+
+ U8 state;
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_State, state, block_num );
+ mState = state;
+ break;
+ }
+
+ default:
+ break;
+
+ }
+ }
+ else
+ {
+ // handle the compressed case
+ LLUUID sound_uuid;
+ LLUUID owner_id;
+ F32 gain = 0;
+ U8 sound_flags = 0;
+ F32 cutoff = 0;
+
+ U16 val[4];
+
+ U8 state;
+
+ dp->unpackU8(state, "State");
+ mState = state;
+
+ switch(update_type)
+ {
+ case OUT_TERSE_IMPROVED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompTI:" << getID() << llendl;
+#endif
+ U8 value;
+ dp->unpackU8(value, "agent");
+ if (value)
+ {
+ LLVector4 collision_plane;
+ dp->unpackVector4(collision_plane, "Plane");
+ ((LLVOAvatar*)this)->setFootPlane(collision_plane);
+ }
+ test_pos_parent = getPosition();
+ dp->unpackVector3(new_pos_parent, "Pos");
+ dp->unpackU16(val[VX], "VelX");
+ dp->unpackU16(val[VY], "VelY");
+ dp->unpackU16(val[VZ], "VelZ");
+ setVelocity(U16_to_F32(val[VX], -128.f, 128.f),
+ U16_to_F32(val[VY], -128.f, 128.f),
+ U16_to_F32(val[VZ], -128.f, 128.f));
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAcceleration(U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+
+ dp->unpackU16(val[VX], "ThetaX");
+ dp->unpackU16(val[VY], "ThetaY");
+ dp->unpackU16(val[VZ], "ThetaZ");
+ dp->unpackU16(val[VS], "ThetaS");
+ 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[VS] = U16_to_F32(val[VS], -1.f, 1.f);
+ dp->unpackU16(val[VX], "AccX");
+ dp->unpackU16(val[VY], "AccY");
+ dp->unpackU16(val[VZ], "AccZ");
+ setAngularVelocity( U16_to_F32(val[VX], -64.f, 64.f),
+ U16_to_F32(val[VY], -64.f, 64.f),
+ U16_to_F32(val[VZ], -64.f, 64.f));
+ }
+ break;
+ case OUT_FULL_COMPRESSED:
+ case OUT_FULL_CACHED:
+ {
+#ifdef DEBUG_UPDATE_TYPE
+ llinfos << "CompFull:" << getID() << llendl;
+#endif
+ dp->unpackU32(crc, "CRC");
+ mTotalCRC = crc;
+ dp->unpackU8(material, "Material");
+ U8 old_material = getMaterial();
+ if (old_material != material)
+ {
+ setMaterial(material);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ dp->unpackU8(click_action, "ClickAction");
+ setClickAction(click_action);
+ dp->unpackVector3(new_scale, "Scale");
+ dp->unpackVector3(new_pos_parent, "Pos");
+ LLVector3 vec;
+ dp->unpackVector3(vec, "Rot");
+ new_rot.unpackFromVector3(vec);
+ setAcceleration(LLVector3::zero);
+
+ U32 value;
+ dp->unpackU32(value, "SpecialCode");
+
+ dp->setPassFlags(value);
+
+
+ if (value & 0x80)
+ {
+ dp->unpackVector3(vec, "Omega");
+ setAngularVelocity(vec);
+ }
+
+ if (value & 0x20)
+ {
+ dp->unpackU32(parent_id, "ParentID");
+ }
+ else
+ {
+ parent_id = 0;
+ }
+
+ S32 sp_size;
+ U32 size;
+ if (value & 0x2)
+ {
+ sp_size = 1;
+ delete [] mData;
+ mData = new U8[1];
+ dp->unpackU8(((U8*)mData)[0], "TreeData");
+ }
+ else if (value & 0x1)
+ {
+ dp->unpackU32(size, "ScratchPadSize");
+ delete [] mData;
+ mData = new U8[size];
+ dp->unpackBinaryData((U8 *)mData, sp_size, "PartData");
+ }
+ else
+ {
+ mData = NULL;
+ }
+
+ // Setup object text
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1); // Set to match current agni behavior.
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+
+ if (value & 0x4)
+ {
+ char temp_string[256]; // not MAX_STRING, must hold 255 chars + \0
+ dp->unpackString(temp_string, "Text");
+ LLColor4U coloru;
+ dp->unpackBinaryDataFixed(coloru.mV, 4, "Color");
+ coloru.mV[3] = 255 - coloru.mV[3];
+ mText->setColor(LLColor4(coloru));
+ mText->setStringUTF8(temp_string);
+
+ setChanged(TEXTURE);
+ }
+ else
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+
+ if (value & 0x200)
+ {
+ char media_url[MAX_STRING+1];
+ dp->unpackString(media_url, "MediaURL");
+ //if (media_url[0])
+ //{
+ // llinfos << "WEBONPRIM media_url " << media_url << llendl;
+ //}
+ if (!mMedia)
+ {
+ retval |= MEDIA_URL_ADDED;
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mMediaType = LLViewerObject::MEDIA_TYPE_WEB_PAGE;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ retval |= MEDIA_URL_UPDATED;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ }
+ }
+ else if (mMedia)
+ {
+ retval |= MEDIA_URL_REMOVED;
+ delete mMedia;
+ mMedia = NULL;
+ }
+
+ //
+ // Unpack particle system data
+ //
+ if (value & 0x8)
+ unpackParticleSource(*dp, owner_id);
+
+ // Mark all extra parameters not used
+ std::map<U16, ExtraParameter*>::iterator iter;
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ iter->second->in_use = FALSE;
+ }
+
+ // Unpack extra params
+ U8 num_parameters;
+ dp->unpackU8(num_parameters, "num_params");
+ U8 param_block[MAX_OBJECT_PARAMS_SIZE];
+ for (U8 param=0; param<num_parameters; ++param)
+ {
+ U16 param_type;
+ S32 param_size;
+ dp->unpackU16(param_type, "param_type");
+ dp->unpackBinaryData(param_block, param_size, "param_data");
+ //llinfos << "Param type: " << param_type << ", Size: " << param_size << llendl;
+ LLDataPackerBinaryBuffer dp2(param_block, param_size);
+ unpackParameterEntry(param_type, &dp2);
+ }
+
+ for (iter = mExtraParameterList.begin(); iter != mExtraParameterList.end(); ++iter)
+ {
+ if (!iter->second->in_use)
+ {
+ // Send an update message in case it was formerly in use
+ parameterChanged(iter->first, iter->second->data, FALSE, false);
+ }
+ }
+
+ if (value & 0x10)
+ {
+ dp->unpackUUID(sound_uuid, "SoundUUID");
+ dp->unpackUUID(owner_id, "OwnerID");
+ dp->unpackF32(gain, "SoundGain");
+ dp->unpackU8(sound_flags, "SoundFlags");
+ dp->unpackF32(cutoff, "SoundRadius");
+ }
+
+ if (value & 0x100)
+ {
+ char name_value_list[2048];
+ dp->unpackString(name_value_list, "NV");
+
+ setNameValueList(name_value_list);
+ }
+
+ mTotalCRC = crc;
+
+ setAttachedSound(sound_uuid, owner_id, gain, sound_flags);
+
+ // only get these flags on updates from sim, not cached ones
+ // Preload these five flags for every object.
+ // Finer shades require the object to be selected, and the selection manager
+ // stores the extended permission info.
+ U32 flags;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, block_num);
+ // keep local flags and overwrite remote-controlled flags
+ mFlags = (mFlags & FLAGS_LOCAL) | flags;
+
+ // ...new objects that should come in selected need to be added to the selected list
+ mCreateSelected = ((flags & FLAGS_CREATE_SELECTED) != 0);
+
+ // Set the change flags for scale
+ if (new_scale != getScale())
+ {
+ setChanged(SCALED | SILHOUETTE);
+ setScale(new_scale); // Must follow setting permYouOwner()
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ //
+ // Fix object parenting.
+ //
+ BOOL b_changed_status = FALSE;
+
+ if (OUT_TERSE_IMPROVED != update_type)
+ {
+ // We only need to update parenting on full updates, terse updates
+ // don't send parenting information.
+ if (!cur_parentp)
+ {
+ if (parent_id == 0)
+ {
+ // No parent now, no parent in message -> do nothing
+ }
+ else
+ {
+ // No parent now, new parent in message -> attach to that parent if possible
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ mesgsys->getSenderIP(),
+ mesgsys->getSenderPort());
+
+ LLViewerObject *sent_parentp = gObjectList.findObject(parent_uuid);
+
+ //
+ // Check to see if we have the corresponding viewer object for the parent.
+ //
+ if (sent_parentp && sent_parentp->getParent() == this)
+ {
+ // Try to recover if we attempt to attach a parent to its child
+ llwarns << "Attempt to attach a parent to it's child: " << this->getID() << " to " << sent_parentp->getID() << llendl;
+ this->removeChild(sent_parentp);
+ sent_parentp->setDrawableParent(NULL);
+ }
+
+ if (sent_parentp && (sent_parentp != this) && !sent_parentp->isDead())
+ {
+ //
+ // We have a viewer object for the parent, and it's not dead.
+ // Do the actual reparenting here.
+ //
+
+ // new parent is valid
+ b_changed_status = TRUE;
+ // ...no current parent, so don't try to remove child
+ if (mDrawable.notNull())
+ {
+ if (mDrawable->isDead() || !mDrawable->getVObj())
+ {
+ llwarns << "Drawable is dead or no VObj!" << llendl;
+ sent_parentp->addChild(this);
+ }
+ else
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 1
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ sent_parentp->addChild(this);
+ // make sure this object gets a non-damped update
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ else
+ {
+ sent_parentp->addChild(this);
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+ }
+ else
+ {
+ //
+ // No corresponding viewer object for the parent, put the various
+ // pieces on the orphan list.
+ //
+
+ //parent_id
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+ }
+ else
+ {
+ // BUG: this is a bad assumption once border crossing is alowed
+ if ( (parent_id == cur_parentp->mLocalID)
+ &&(update_type == OUT_TERSE_IMPROVED))
+ {
+ // Parent now, same parent in message -> do nothing
+
+ // Debugging for suspected problems with local ids.
+ //LLUUID parent_uuid;
+ //LLViewerObjectList::getUUIDFromLocal(parent_uuid, parent_id, mesgsys->getSenderIP(), mesgsys->getSenderPort() );
+ //if (parent_uuid != cur_parentp->getID() )
+ //{
+ // llerrs << "Local ID match but UUID mismatch of viewer object" << llendl;
+ //}
+ }
+ else
+ {
+ // Parented now, different parent in message
+ LLViewerObject *sent_parentp;
+ if (parent_id == 0)
+ {
+ //
+ // This object is no longer parented, we sent in a zero parent ID.
+ //
+ sent_parentp = NULL;
+ }
+ else
+ {
+ LLUUID parent_uuid;
+ LLViewerObjectList::getUUIDFromLocal(parent_uuid,
+ parent_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ sent_parentp = gObjectList.findObject(parent_uuid);
+
+ if (isAvatar())
+ {
+ // This logic is meant to handle the case where a sitting avatar has reached a new sim
+ // ahead of the object she was sitting on (which is common as objects are transfered through
+ // a slower route than agents)...
+ // In this case, the local id for the object will not be valid, since the viewer has not received
+ // a full update for the object from that sim yet, so we assume that the agent is still sitting
+ // where she was originally. --RN
+ if (!sent_parentp)
+ {
+ sent_parentp = cur_parentp;
+ }
+ }
+ else if (!sent_parentp)
+ {
+ //
+ // Switching parents, but we don't know the new parent.
+ //
+ U32 ip = mesgsys->getSenderIP();
+ U32 port = mesgsys->getSenderPort();
+
+ // We're an orphan, flag things appropriately.
+ gObjectList.orphanize(this, parent_id, ip, port);
+ }
+ }
+
+ // Reattach if possible.
+ if (sent_parentp && sent_parentp != cur_parentp && sent_parentp != this)
+ {
+ // New parent is valid, detach and reattach
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ if (!setDrawableParent(sent_parentp->mDrawable)) // LLViewerObject::processUpdateMessage 2
+ {
+ // Bad, we got a cycle somehow.
+ // Kill both the parent and the child, and
+ // set cache misses for both of them.
+ llwarns << "Attempting to recover from parenting cycle!" << llendl
+ llwarns << "Killing " << sent_parentp->getID() << " and " << getID() << llendl;
+ llwarns << "Adding to cache miss list" << llendl;
+ setParent(NULL);
+ sent_parentp->setParent(NULL);
+ getRegion()->addCacheMissFull(getLocalID());
+ getRegion()->addCacheMissFull(sent_parentp->getLocalID());
+ gObjectList.killObject(sent_parentp);
+ gObjectList.killObject(this);
+ return retval;
+ }
+ // make sure this object gets a non-damped update
+ }
+ cur_parentp->removeChild(this);
+ sent_parentp->addChild(this);
+ setChanged(MOVED | SILHOUETTE);
+ sent_parentp->setChanged(MOVED | SILHOUETTE);
+ if (sent_parentp->mDrawable.notNull())
+ {
+ gPipeline.markMoved(sent_parentp->mDrawable, FALSE); // undamped
+ }
+ }
+ else if (!sent_parentp)
+ {
+ bool remove_parent = true;
+ // No new parent, or the parent that we sent doesn't exist on the viewer.
+ LLViewerObject *parentp = (LLViewerObject *)getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != getRegion())
+ {
+ // This is probably an object flying across a region boundary, the
+ // object probably ISN'T being reparented, but just got an object
+ // update out of order (child update before parent).
+ //llinfos << "Don't reparent object handoffs!" << llendl;
+ remove_parent = false;
+ }
+ }
+
+ if (remove_parent)
+ {
+ b_changed_status = TRUE;
+ if (mDrawable.notNull())
+ {
+ // clear parent to removeChild can put the drawable on the damped list
+ setDrawableParent(NULL); // LLViewerObject::processUpdateMessage 3
+ }
+
+ cur_parentp->removeChild(this);
+
+ if (mJointInfo && !parent_id)
+ {
+ // since this object is no longer parent-relative
+ // we make sure we delete any joint info
+ delete mJointInfo;
+ mJointInfo = NULL;
+ }
+
+ setChanged(MOVED | SILHOUETTE);
+
+ if (mDrawable.notNull())
+ {
+ // make sure this object gets a non-damped update
+ gPipeline.markMoved(mDrawable, FALSE); // undamped
+ }
+ }
+ }
+ }
+ }
+ }
+
+ new_rot.normQuat();
+
+ if (gPingInterpolate)
+ {
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mesgsys->getSender());
+ F32 ping_delay = 0.5f * mTimeDilation * ( ((F32)cdp->getPingDelay()) * 0.001f + gFrameDTClamped);
+ LLVector3 diff = getVelocity() * (0.5f*mTimeDilation*(gFrameDTClamped + ((F32)ping_delay)*0.001f));
+ new_pos_parent += diff;
+ }
+
+ //////////////////////////
+ //
+ // Set the generic change flags...
+ //
+ //
+
+ // first, let's see if the new position is actually a change
+
+ //static S32 counter = 0;
+
+ F32 vel_mag_sq = getVelocity().magVecSquared();
+ F32 accel_mag_sq = getAcceleration().magVecSquared();
+
+ if ( ((b_changed_status)||(test_pos_parent != new_pos_parent))
+ ||( (!isSelected())
+ &&( (vel_mag_sq != 0.f)
+ ||(accel_mag_sq != 0.f)
+ ||(this_update_precision > mBestUpdatePrecision))))
+ {
+ mBestUpdatePrecision = this_update_precision;
+ setPositionParent(new_pos_parent);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+
+ if (new_rot != mLastRot)
+ {
+ // if (getAngularVelocity().isExactlyZero() ||
+ // new_angv != getAngularVelocity())
+ {
+ mLastRot = new_rot;
+ setChanged(ROTATED | SILHOUETTE);
+ setRotation(new_rot);
+ resetRot();
+ }
+ }
+
+
+ if ( gShowObjectUpdates )
+ {
+ if (!((mPrimitiveCode == LL_PCODE_LEGACY_AVATAR) && (((LLVOAvatar *) this)->mIsSelf))
+ && mRegionp)
+ {
+ LLViewerObject* object = gObjectList.createObjectViewer(LL_PCODE_LEGACY_TEXT_BUBBLE, mRegionp);
+ LLVOTextBubble* bubble = (LLVOTextBubble*) object;
+
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ bubble->mColor.setVec(0.f, 0.f, 1.f, 1.f);
+ }
+ else
+ {
+ bubble->mColor.setVec(1.f, 0.f, 0.f, 1.f);
+ }
+ object->setPositionGlobal(getPositionGlobal());
+ gPipeline.addObject(object);
+ }
+ }
+
+ if ((0.0f == vel_mag_sq) &&
+ (0.0f == accel_mag_sq) &&
+ (0.0f == getAngularVelocity().magVecSquared()))
+ {
+ mStatic = TRUE; // This object doesn't move!
+ }
+ else
+ {
+ mStatic = FALSE;
+ }
+
+// BUG: This code leads to problems during group rotate and any scale operation.
+// Small discepencies between the simulator and viewer representations cause the
+// selection center to creep, leading to objects moving around the wrong center.
+//
+// Removing this, however, means that if someone else drags an object you have
+// selected, your selection center and dialog boxes will be wrong. It also means
+// that higher precision information on selected objects will be ignored.
+//
+// I believe the group rotation problem is fixed. JNC 1.21.2002
+//
+ // Additionally, if any child is selected, need to update the dialogs and selection
+ // center.
+ BOOL needs_refresh = mUserSelected;
+ LLViewerObject *childp;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ childp = mChildList[i];
+ needs_refresh = needs_refresh || childp->mUserSelected;
+ }
+
+ if (needs_refresh)
+ {
+ gSelectMgr->updateSelectionCenter();
+ dialog_refresh_all();
+ }
+
+
+ // Mark update time as approx. now, with the ping delay.
+ // Ping delay is off because it's not set for velocity interpolation, causing
+ // much jumping and hopping around...
+
+// U32 ping_delay = mesgsys->mCircuitInfo.getPingDelay();
+ mLastInterpUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ mLastMessageUpdateSecs = LLFrameTimer::getElapsedSeconds();
+ if (mDrawable.notNull())
+ {
+ // Don't clear invisibility flag on update if still orphaned!
+ if (mDrawable->isState(LLDrawable::FORCE_INVISIBLE) && !mOrphaned)
+ {
+// lldebugs << "Clearing force invisible: " << mID << ":" << getPCodeString() << ":" << getPositionAgent() << llendl;
+ mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ }
+ }
+
+ // Update special hover cursor status
+ bool special_hover_cursor = specialHoverCursor();
+ if (old_special_hover_cursor != special_hover_cursor
+ && mDrawable.notNull())
+ {
+ mDrawable->updateSpecialHoverCursor(special_hover_cursor);
+ }
+
+ return retval;
+}
+
+BOOL LLViewerObject::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLViewerObject::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead)
+ {
+ // It's dead. Don't update it.
+ return TRUE;
+ }
+
+ // CRO - don't velocity interp linked objects!
+ // Leviathan - but DO velocity interp joints
+ if (!mStatic && gVelocityInterpolate && !isSelected())
+ {
+ // calculate dt from last update
+ F32 dt_raw = (F32)(time - mLastInterpUpdateSecs);
+ F32 dt = mTimeDilation * dt_raw;
+
+ if (!mUserSelected && !mJointInfo)
+ {
+ applyAngularVelocity(dt);
+ }
+
+ LLViewerObject *parentp = (LLViewerObject *) getParent();
+ if (mJointInfo)
+ {
+ if (parentp)
+ {
+ // do parent-relative stuff
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+ dQ.setQuat(angle, ang_vel);
+ }
+ LLVector3 pos = getPosition();
+
+ if (HJT_HINGE == mJointInfo->mJointType)
+ {
+ // hinge = uniform circular motion
+ LLVector3 parent_pivot = getVelocity();
+ LLVector3 parent_axis = getAcceleration();
+
+ angle = dt * (ang_vel * mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ dQ.setQuat(angle, mJointInfo->mAxisOrAnchor); // AxisOrAnchor = axis
+ LLVector3 pivot_offset = pos - mJointInfo->mPivot; // pos in pivot-frame
+ pivot_offset = pivot_offset * dQ; // new rotated pivot-frame pos
+ pos = mJointInfo->mPivot + pivot_offset; // parent-frame
+ LLViewerObject::setPosition(pos);
+ LLQuaternion Q_PC = getRotation();
+ setRotation(Q_PC * dQ);
+ mLastInterpUpdateSecs = time;
+ }
+ else if (HJT_POINT == mJointInfo->mJointType)
+ // || HJT_LPOINT == mJointInfo->mJointType)
+ {
+ // point-to-point = spin about axis and uniform circular motion
+ // of axis about the pivot point
+ //
+ // NOTE: this interpolation scheme is not quite good enough to
+ // reduce the bandwidth -- needs a gravitational correction.
+ // Similarly for hinges with axes that deviate from vertical.
+
+ LLQuaternion Q_PC = getRotation();
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ LLVector3 pivot_to_child = - mJointInfo->mAxisOrAnchor; // AxisOrAnchor = anchor
+ pos = mJointInfo->mPivot + pivot_to_child * Q_PC;
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }
+ /* else if (HJT_WHEEL == mJointInfo->mJointInfo)
+ {
+ // wheel = uniform rotation about axis, with linear
+ // velocity interpolation (if any)
+ LLVector3 parent_axis = getAcceleration(); // HACK -- accel stores the parent-axis (parent-frame)
+
+ LLQuaternion Q_PC = getRotation();
+
+ angle = dt * (parent_axis * ang_vel);
+ dQ.setQuat(angle, parent_axis);
+
+ Q_PC = Q_PC * dQ;
+ setRotation(Q_PC);
+
+ pos = getPosition() + dt * getVelocity();
+ LLViewerObject::setPosition(pos);
+ mLastInterpUpdateSecs = time;
+ }*/
+ }
+ }
+ else if (isAttachment())
+ {
+ mLastInterpUpdateSecs = time;
+ return TRUE;
+ }
+ else
+ {
+ // linear motion
+ // HAVOK_TIMESTEP is used below to correct for the fact that the velocity in object
+ // updates represents the average velocity of the last timestep, rather than the final velocity.
+ // the time dilation above should guarrantee that dt is never less than HAVOK_TIMESTEP, theoretically
+ //
+ // There is a problem here if dt is negative. . .
+
+ //FIXME: should also wrap linear accel/velocity in check
+ // to see if object is selected, instead of explicitly
+ // zeroing it out
+ LLVector3 accel = getAcceleration();
+ LLVector3 vel = getVelocity();
+
+ if (!(accel.isExactlyZero() && vel.isExactlyZero()))
+ {
+ LLVector3 pos = (vel + (0.5f * (dt-HAVOK_TIMESTEP)) * accel) * dt;
+
+ // region local
+ setPositionRegion(pos + getPositionRegion());
+ setVelocity(vel + accel*dt);
+
+ // for objects that are spinning but not translating, make sure to flag them as having moved
+ setChanged(MOVED | SILHOUETTE);
+ }
+
+ mLastInterpUpdateSecs = time;
+ }
+ }
+
+ if (gNoRender)
+ {
+ // Skip drawable stuff if not rendering.
+ return TRUE;
+ }
+
+ updateDrawable(FALSE);
+
+ return TRUE;
+}
+
+
+BOOL LLViewerObject::setData(const U8 *datap, const U32 data_size)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ delete [] mData;
+
+ if (datap)
+ {
+ mData = new U8[data_size];
+ if (!mData)
+ {
+ return FALSE;
+ }
+ memcpy(mData, datap, data_size);
+ }
+ return TRUE;
+}
+
+// delete an item in the inventory, but don't tell the server. This is
+// used internally by remove, update, and savescript.
+// This will only delete the first item with an item_id in the list
+void LLViewerObject::deleteInventoryItem(const LLUUID& item_id)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it )
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ // This is safe only because we return immediatly.
+ mInventory->erase(it); // will deref and delete it
+ return;
+ }
+ }
+ doInventoryCallback();
+ }
+}
+
+void LLViewerObject::doUpdateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLViewerInventoryItem* old_item = NULL;
+ if(TASK_INVENTORY_ITEM_KEY == key)
+ {
+ old_item = (LLViewerInventoryItem*)getInventoryObject(item->getUUID());
+ }
+ else if(TASK_INVENTORY_ASSET_KEY == key)
+ {
+ old_item = getInventoryItemByAsset(item->getAssetUUID());
+ }
+ LLUUID item_id;
+ LLUUID new_owner;
+ LLUUID new_group;
+ BOOL group_owned = FALSE;
+ if(old_item)
+ {
+ item_id = old_item->getUUID();
+ new_owner = old_item->getPermissions().getOwner();
+ new_group = old_item->getPermissions().getGroup();
+ group_owned = old_item->getPermissions().isGroupOwned();
+ old_item = NULL;
+ }
+ else
+ {
+ item_id = item->getUUID();
+ }
+ if(!is_new && mInventory)
+ {
+ // Attempt to update the local inventory. If we can get the
+ // object perm, we have perfect visibility, so we want the
+ // serial number to match. Otherwise, take our best guess and
+ // make sure that the serial number does not match.
+ deleteInventoryItem(item_id);
+ LLPermissions perm(item->getPermissions());
+ LLPermissions* obj_perm = gSelectMgr->findObjectPermissions(this);
+ bool is_atomic = ((S32)LLAssetType::AT_OBJECT == item->getType()) ? false : true;
+ if(obj_perm)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, obj_perm->getOwner(), obj_perm->getGroup(), is_atomic);
+ }
+ else
+ {
+ if(group_owned)
+ {
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ else if(!new_owner.isNull())
+ {
+ // The object used to be in inventory, so we can
+ // assume the owner and group will match what they are
+ // there.
+ perm.setOwnerAndGroup(LLUUID::null, new_owner, new_group, is_atomic);
+ }
+ // *FIX: can make an even better guess by using the mPermGroup flags
+ else if(permYouOwner())
+ {
+ // best guess.
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), item->getPermissions().getGroup(), is_atomic);
+ --mInventorySerialNum;
+ }
+ else
+ {
+ // dummy it up.
+ perm.setOwnerAndGroup(LLUUID::null, LLUUID::null, LLUUID::null, is_atomic);
+ --mInventorySerialNum;
+ }
+ }
+ LLViewerInventoryItem* new_item = new LLViewerInventoryItem(item);
+ new_item->setPermissions(perm);
+ mInventory->push_front(new_item);
+ doInventoryCallback();
+ ++mInventorySerialNum;
+ }
+}
+
+// save a script, which involves removing the old one, and rezzing
+// in the new one. This method should be called with the asset id
+// of the new and old script AFTER the bytecode has been saved.
+void LLViewerObject::saveScript(
+ const LLViewerInventoryItem* item,
+ BOOL active,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ /*
+ * XXXPAM Investigate not making this copy. Seems unecessary, but I'm unsure about the
+ * interaction with doUpdateInventory() called below.
+ */
+ lldebugs << "LLViewerObject::saveScript() " << item->getUUID() << " " << item->getAssetUUID() << llendl;
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(), item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RezScript);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_GroupID, gAgent.getGroupID());
+ msg->nextBlockFast(_PREHASH_UpdateBlock);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, (mLocalID));
+ U8 enabled = active;
+ msg->addBOOLFast(_PREHASH_Enabled, enabled);
+ msg->nextBlockFast(_PREHASH_InventoryBlock);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, TASK_INVENTORY_ITEM_KEY, is_new);
+}
+
+void LLViewerObject::moveInventory(const LLUUID& folder_id,
+ const LLUUID& item_id)
+{
+ lldebugs << "LLViewerObject::moveInventory " << item_id << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_FolderID, folder_id);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+
+ LLInventoryObject* inv_obj = getInventoryObject(item_id);
+ if(inv_obj)
+ {
+ LLViewerInventoryItem* item = (LLViewerInventoryItem*)inv_obj;
+ if(!item->getPermissions().allowCopyBy(gAgent.getID()))
+ {
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+ }
+ }
+}
+
+void LLViewerObject::dirtyInventory()
+{
+ // If there aren't any LLVOInventoryListeners, we won't be
+ // able to update our mInventory when it comes back from the
+ // simulator, so we should not clear the inventory either.
+ if(mInventory && !mInventoryCallbacks.isEmpty())
+ {
+ mInventory->clear(); // will deref and delete entries
+ delete mInventory;
+ mInventory = NULL;
+ mInventoryDirty = TRUE;
+ }
+}
+
+void LLViewerObject::registerInventoryListener(LLVOInventoryListener* listener, void* user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLInventoryCallbackInfo* info = new LLInventoryCallbackInfo;
+ info->mListener = listener;
+ info->mInventoryData = user_data;
+ mInventoryCallbacks.addData(info);
+}
+
+void LLViewerObject::removeInventoryListener(LLVOInventoryListener* listener)
+{
+ if (listener == NULL) return;
+ LLInventoryCallbackInfo* info;
+ for (info = mInventoryCallbacks.getFirstData();
+ info;
+ info = mInventoryCallbacks.getNextData() )
+ {
+ if (info->mListener == listener)
+ {
+ mInventoryCallbacks.deleteCurrentData();
+ break;
+ }
+ }
+}
+
+void LLViewerObject::clearInventoryListeners()
+{
+ mInventoryCallbacks.deleteAllData();
+}
+
+void LLViewerObject::requestInventory()
+{
+ mInventoryDirty = FALSE;
+ if(mInventory)
+ {
+ //mInventory->clear() // will deref and delete it
+ //delete mInventory;
+ //mInventory = NULL;
+ doInventoryCallback();
+ }
+ // throw away duplicate requests
+ else if (! mInventoryPending)
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->sendReliable(mRegionp->getHost());
+
+ // this will get reset by dirtyInventory or doInventoryCallback
+ mInventoryPending = TRUE;
+ }
+}
+
+struct LLFilenameAndTask
+{
+ LLUUID mTaskID;
+ char mFilename[MAX_STRING]; // Just the filename, not the path
+#ifdef _DEBUG
+ static S32 sCount;
+ LLFilenameAndTask()
+ {
+ ++sCount;
+ lldebugs << "Constructing LLFilenameAndTask: " << sCount << llendl;
+ }
+ ~LLFilenameAndTask()
+ {
+ --sCount;
+ lldebugs << "Destroying LLFilenameAndTask: " << sCount << llendl;
+ }
+private:
+ LLFilenameAndTask(const LLFilenameAndTask& rhs);
+ const LLFilenameAndTask& operator=(const LLFilenameAndTask& rhs) const;
+#endif
+};
+
+#ifdef _DEBUG
+S32 LLFilenameAndTask::sCount = 0;
+#endif
+
+// static
+void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ LLUUID task_id;
+ msg->getUUIDFast(_PREHASH_InventoryData, _PREHASH_TaskID, task_id);
+ LLViewerObject* object = gObjectList.findObject(task_id);
+ if(object)
+ {
+ msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, object->mInventorySerialNum);
+ LLFilenameAndTask* ft = new LLFilenameAndTask;
+ ft->mTaskID = task_id;
+ msg->getStringFast(_PREHASH_InventoryData, _PREHASH_Filename, MAX_STRING, ft->mFilename);
+ if(!ft->mFilename[0])
+ {
+ lldebugs << "Task has no inventory" << llendl;
+ // mock up some inventory to make a drop target.
+ if(object->mInventory)
+ {
+ object->mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ object->mInventory = new InventoryObjectList();
+ }
+ LLPointer<LLInventoryObject> obj;
+ obj = new LLInventoryObject(object->mID, LLUUID::null,
+ LLAssetType::AT_CATEGORY,
+ "Contents");
+ object->mInventory->push_front(obj);
+ object->doInventoryCallback();
+ delete ft;
+ return;
+ }
+ gXferManager->requestFile(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ft->mFilename).c_str(),
+ ft->mFilename, LL_PATH_CACHE,
+ object->mRegionp->getHost(),
+ TRUE,
+ &LLViewerObject::processTaskInvFile,
+ (void**)ft,
+ LLXferManager::HIGH_PRIORITY);
+ }
+}
+
+void LLViewerObject::processTaskInvFile(void** user_data, S32 error_code)
+{
+ LLFilenameAndTask* ft = (LLFilenameAndTask*)user_data;
+ LLViewerObject* object = NULL;
+ if(ft && (0 == error_code) &&
+ (object = gObjectList.findObject(ft->mTaskID)))
+ {
+ object->loadTaskInvFile(ft->mFilename);
+ }
+ else
+ {
+ // This Occurs When to requests were made, and the first one
+ // has already handled it.
+ lldebugs << "Problem loading task inventory. Return code: "
+ << error_code << llendl;
+ }
+ delete ft;
+}
+
+void LLViewerObject::loadTaskInvFile(const char* filename)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ std::string filename_and_local_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, filename);
+ llifstream ifs(filename_and_local_path.c_str());
+ if(ifs.good())
+ {
+ char buffer[MAX_STRING];
+ char keyword[MAX_STRING];
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ mInventory = new InventoryObjectList;
+ }
+ while(ifs.good())
+ {
+ ifs.getline(buffer, MAX_STRING);
+ sscanf(buffer, " %s", keyword);
+ if(0 == strcmp("inv_item", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLViewerInventoryItem;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else if(0 == strcmp("inv_object", keyword))
+ {
+ LLPointer<LLInventoryObject> inv = new LLInventoryObject;
+ inv->importLegacyStream(ifs);
+ mInventory->push_front(inv);
+ }
+ else
+ {
+ llwarns << "Unknown token in inventory file '"
+ << keyword << "'" << llendl;
+ }
+ }
+ ifs.close();
+ LLFile::remove(filename_and_local_path.c_str());
+ }
+ else
+ {
+ llwarns << "unable to load task inventory: " << filename_and_local_path
+ << llendl;
+ }
+ doInventoryCallback();
+}
+
+void LLViewerObject::doInventoryCallback()
+{
+ for(LLInventoryCallbackInfo* info = mInventoryCallbacks.getFirstData();
+ info != NULL;
+ info = mInventoryCallbacks.getNextData())
+ {
+ if (info->mListener != NULL)
+ {
+ info->mListener->inventoryChanged(this,
+ mInventory,
+ mInventorySerialNum,
+ info->mInventoryData);
+ }
+ else
+ {
+ llinfos << "LLViewerObject::doInventoryCallback() deleting bad listener entry." << llendl;
+ mInventoryCallbacks.deleteCurrentData();
+ }
+ }
+ mInventoryPending = FALSE;
+}
+
+void LLViewerObject::removeInventory(const LLUUID& item_id)
+{
+ // close any associated floater properties
+ LLFloaterProperties::closeByID(item_id, mID);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RemoveTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addUUIDFast(_PREHASH_ItemID, item_id);
+ msg->sendReliable(mRegionp->getHost());
+ deleteInventoryItem(item_id);
+ ++mInventorySerialNum;
+
+ // The viewer object should not refresh UI since this is a utility
+ // function. The UI functionality that called this method should
+ // refresh the views if necessary.
+ //gBuildView->refresh();
+}
+
+void LLViewerObject::updateInventory(
+ LLViewerInventoryItem* item,
+ U8 key,
+ bool is_new)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ // This slices the object into what we're concerned about on the
+ // viewer. The simulator will take the permissions and transfer
+ // ownership.
+ LLPointer<LLViewerInventoryItem> task_item =
+ new LLViewerInventoryItem(item->getUUID(), mID, item->getPermissions(),
+ item->getAssetUUID(), item->getType(),
+ item->getInventoryType(),
+ item->getName(), item->getDescription(),
+ item->getSaleInfo(),
+ item->getFlags(),
+ item->getCreationDate());
+ task_item->setTransactionID(item->getTransactionID());
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_UpdateTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_UpdateData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->addU8Fast(_PREHASH_Key, key);
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ task_item->packMessage(msg);
+ msg->sendReliable(mRegionp->getHost());
+
+ // do the internal logic
+ doUpdateInventory(task_item, key, is_new);
+}
+
+LLInventoryObject* LLViewerObject::getInventoryObject(const LLUUID& item_id)
+{
+ LLInventoryObject* rv = NULL;
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for ( ; it != end; ++it)
+ {
+ if((*it)->getUUID() == item_id)
+ {
+ rv = *it;
+ break;
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::getInventoryContents(InventoryObjectList& objects)
+{
+ if(mInventory)
+ {
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ if ((*it)->getType() != LLAssetType::AT_CATEGORY)
+ {
+ objects.push_back(*it);
+ }
+ }
+ }
+}
+
+LLInventoryObject* LLViewerObject::getInventoryRoot()
+{
+ if (!mInventory || !mInventory->size())
+ {
+ return NULL;
+ }
+ return mInventory->back();
+}
+
+LLViewerInventoryItem* LLViewerObject::getInventoryItemByAsset(const LLUUID& asset_id)
+{
+ LLViewerInventoryItem* rv = NULL;
+ if(mInventory)
+ {
+ LLViewerInventoryItem* item = NULL;
+
+ InventoryObjectList::iterator it = mInventory->begin();
+ InventoryObjectList::iterator end = mInventory->end();
+ for( ; it != end; ++it)
+ {
+ LLInventoryObject* obj = *it;
+ if(obj->getType() != LLAssetType::AT_CATEGORY)
+ {
+ // *FIX: gank-ass down cast!
+ item = (LLViewerInventoryItem*)obj;
+ if(item->getAssetUUID() == asset_id)
+ {
+ rv = item;
+ break;
+ }
+ }
+ }
+ }
+ return rv;
+}
+
+void LLViewerObject::setPixelAreaAndAngle(LLAgent &agent)
+{
+ if (getVolume())
+ { //volumes calculate pixel area and angle per face
+ return;
+ }
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = getMinScale();
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f || isHUDAttachment()) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+ if (mPixelArea > gCamera->getScreenPixelArea())
+ {
+ mAppAngle = 180.f;
+ mPixelArea = (F32)gCamera->getScreenPixelArea();
+ }
+ }
+}
+
+BOOL LLViewerObject::updateLOD()
+{
+ return FALSE;
+}
+
+BOOL LLViewerObject::updateGeometry(LLDrawable *drawable)
+{
+ return TRUE;
+}
+
+LLDrawable* LLViewerObject::createDrawable(LLPipeline *pipeline)
+{
+ return NULL;
+}
+
+void LLViewerObject::setScale(const LLVector3 &scale, BOOL damped)
+{
+ LLPrimitive::setScale(scale);
+ if (mDrawable.notNull())
+ {
+ //encompass completely sheared objects by taking
+ //the most extreme point possible (<1,1,0.5>)
+ mDrawable->setRadius(LLVector3(1,1,0.5f).scaleVec(scale).magVec());
+ updateDrawable(damped);
+ }
+
+ if( (LL_PCODE_VOLUME == getPCode()) && !isDead() )
+ {
+ if (permYouOwner() || (scale.magVecSquared() > (7.5f * 7.5f)) )
+ {
+ if (!mOnMap)
+ {
+ gObjectList.addToMap(this);
+ mOnMap = TRUE;
+ }
+ }
+ else
+ {
+ if (mOnMap)
+ {
+ gObjectList.removeFromMap(this);
+ mOnMap = FALSE;
+ }
+ }
+ }
+}
+
+void LLViewerObject::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+ LLVector3 center = getRenderPosition();
+ F32 sz = llmin(mDrawable->getRadius(), 256.f);
+ LLVector3 size = LLVector3(sz,sz,sz);
+ newMin.setVec(center-size);
+ newMax.setVec(center+size);
+ mDrawable->setPositionGroup((newMin + newMax) * 0.5f);
+}
+
+F32 LLViewerObject::getMaxScale() const
+{
+ return llmax(getScale().mV[VX],getScale().mV[VY], getScale().mV[VZ]);
+}
+
+F32 LLViewerObject::getMinScale() const
+{
+ return llmin(getScale().mV[0],getScale().mV[1],getScale().mV[2]);
+}
+
+F32 LLViewerObject::getMidScale() const
+{
+ if (getScale().mV[VX] < getScale().mV[VY])
+ {
+ if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VY];
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VX];
+ }
+ }
+ else if (getScale().mV[VX] < getScale().mV[VZ])
+ {
+ return getScale().mV[VX];
+ }
+ else if (getScale().mV[VY] < getScale().mV[VZ])
+ {
+ return getScale().mV[VZ];
+ }
+ else
+ {
+ return getScale().mV[VY];
+ }
+}
+
+
+void LLViewerObject::updateTextures(LLAgent &agent)
+{
+}
+
+void LLViewerObject::boostTexturePriority(BOOL boost_children /* = TRUE */)
+{
+ if (isDead())
+ {
+ return;
+ }
+
+ S32 i;
+ S32 tex_count = getNumTEs();
+ for (i = 0; i < tex_count; i++)
+ {
+ getTEImage(i)->setBoostLevel(LLViewerImage::BOOST_SELECTED);
+ }
+
+ if (boost_children)
+ {
+ S32 num_children = mChildList.size();
+ for (i = 0; i < num_children; i++)
+ {
+ LLViewerObject *childp = mChildList[i];
+ childp->boostTexturePriority();
+ }
+ }
+}
+
+
+void LLViewerObject::setLineWidthForWindowSize(S32 window_width)
+{
+ if (window_width < 700)
+ {
+ LLUI::setLineWidth(2.0f);
+ }
+ else if (window_width < 1100)
+ {
+ LLUI::setLineWidth(3.0f);
+ }
+ else if (window_width < 2000)
+ {
+ LLUI::setLineWidth(4.0f);
+ }
+ else
+ {
+ // _damn_, what a nice monitor!
+ LLUI::setLineWidth(5.0f);
+ }
+}
+
+void LLViewerObject::increaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 50)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 150;
+ }
+*/
+}
+
+
+void LLViewerObject::decreaseArrowLength()
+{
+/* ???
+ if (mAxisArrowLength == 150)
+ {
+ mAxisArrowLength = 100;
+ }
+ else
+ {
+ mAxisArrowLength = 50;
+ }
+*/
+}
+
+// Culled from newsim LLTask::addNVPair
+void LLViewerObject::addNVPair(const char* data)
+{
+ // cout << "LLViewerObject::addNVPair() with ---" << data << "---" << endl;
+ LLNameValue *nv = new LLNameValue(data);
+
+// char splat[MAX_STRING];
+// temp->printNameValue(splat);
+// llinfos << "addNVPair " << splat << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(nv->mName);
+ if (iter != mNameValuePairs.end())
+ {
+ LLNameValue* foundnv = iter->second;
+ if (foundnv->mClass != NVC_READ_ONLY)
+ {
+ delete foundnv;
+ mNameValuePairs.erase(iter);
+ }
+ else
+ {
+ delete nv;
+// llinfos << "Trying to write to Read Only NVPair " << temp->mName << " in addNVPair()" << llendl;
+ return;
+ }
+ }
+ mNameValuePairs[nv->mName] = nv;
+}
+
+BOOL LLViewerObject::removeNVPair(const char *name)
+{
+ char* canonical_name = gNVNameTable.addString(name);
+
+ lldebugs << "LLViewerObject::removeNVPair(): " << name << llendl;
+
+ name_value_map_t::iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ if( mRegionp )
+ {
+ LLNameValue* nv = iter->second;
+/*
+ std::string buffer = nv->printNameValue();
+ gMessageSystem->newMessageFast(_PREHASH_RemoveNameValuePair);
+ gMessageSystem->nextBlockFast(_PREHASH_TaskData);
+ gMessageSystem->addUUIDFast(_PREHASH_ID, mID);
+
+ gMessageSystem->nextBlockFast(_PREHASH_NameValueData);
+ gMessageSystem->addStringFast(_PREHASH_NVPair, buffer.c_str());
+
+ gMessageSystem->sendReliable( mRegionp->getHost() );
+*/
+ // Remove the NV pair from the local list.
+ delete nv;
+ mNameValuePairs.erase(iter);
+ return TRUE;
+ }
+ else
+ {
+ lldebugs << "removeNVPair - No region for object" << llendl;
+ }
+ }
+ return FALSE;
+}
+
+
+LLNameValue *LLViewerObject::getNVPair(const char *name) const
+{
+ char *canonical_name;
+
+ canonical_name = gNVNameTable.addString(name);
+
+ // If you access a map with a name that isn't in it, it will add the name and a null pointer.
+ // So first check if the data is in the map.
+ name_value_map_t::const_iterator iter = mNameValuePairs.find(canonical_name);
+ if (iter != mNameValuePairs.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void LLViewerObject::updatePositionCaches() const
+{
+ if (!isRoot())
+ {
+ mPositionRegion = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ else
+ {
+ mPositionRegion = getPosition();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+const LLVector3d LLViewerObject::getPositionGlobal() const
+{
+ LLVector3d position_global = mRegionp->getPosGlobalFromRegion(getPositionRegion());;
+
+ if (isAttachment())
+ {
+ position_global = gAgent.getPosGlobalFromAgent(getRenderPosition());
+ }
+
+ return position_global;
+}
+
+const LLVector3 &LLViewerObject::getPositionAgent() const
+{
+ if (mRegionp)
+ {
+ if (mDrawable.notNull() && (!mDrawable->isRoot() && getParent()))
+ {
+ // Don't return cached position if you have a parent, recalc (until all dirtying is done correctly.
+ LLVector3 position_region;
+ position_region = ((LLViewerObject *)getParent())->getPositionRegion() + getPosition() * getParent()->getRotation();
+ mPositionAgent = mRegionp->getPosAgentFromRegion(position_region);
+ }
+ else
+ {
+ mPositionAgent = mRegionp->getPosAgentFromRegion(getPosition());
+ }
+ }
+ return mPositionAgent;
+}
+
+const LLVector3 &LLViewerObject::getPositionRegion() const
+{
+ if (!isRoot())
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ mPositionRegion = parent->getPositionRegion() + (getPosition() * parent->getRotation());
+ }
+ return mPositionRegion;
+}
+
+const LLVector3 LLViewerObject::getPositionEdit() const
+{
+ if (isRootEdit())
+ {
+ return getPosition();
+ }
+ else
+ {
+ LLViewerObject *parent = (LLViewerObject *)getParent();
+ LLVector3 position_edit = parent->getPositionEdit() + getPosition() * parent->getRotationEdit();
+ return position_edit;
+ }
+}
+
+const LLVector3 LLViewerObject::getRenderPosition() const
+{
+ if (mDrawable.isNull() || mDrawable->getGeneration() < 0)
+ {
+ return getPositionAgent();
+ }
+ else
+ {
+ if (isAvatar())
+ {
+ if (isRoot())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPosition() * mDrawable->getParent()->getRenderMatrix();
+ }
+ }
+
+ return mDrawable->getPositionAgent();
+ }
+}
+
+const LLVector3 LLViewerObject::getPivotPositionAgent() const
+{
+ return getRenderPosition();
+}
+
+const LLQuaternion LLViewerObject::getRenderRotation() const
+{
+ LLQuaternion ret;
+ if (mDrawable.isNull() || mDrawable->isStatic())
+ {
+ ret = getRotationEdit();
+ }
+ else
+ {
+ if (!mDrawable->isRoot())
+ {
+ ret = getRotation() * LLQuaternion(mDrawable->getParent()->getWorldMatrix());
+ }
+ else
+ {
+ ret = LLQuaternion(mDrawable->getWorldMatrix());
+ }
+ }
+
+ return ret;
+}
+
+const LLMatrix4 LLViewerObject::getRenderMatrix() const
+{
+ return mDrawable->getWorldMatrix();
+}
+
+const LLQuaternion LLViewerObject::getRotationRegion() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRoot())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+const LLQuaternion LLViewerObject::getRotationEdit() const
+{
+ LLQuaternion global_rotation = getRotation();
+ if (!((LLXform *)this)->isRootEdit())
+ {
+ global_rotation = global_rotation * getParent()->getRotation();
+ }
+ return global_rotation;
+}
+
+void LLViewerObject::setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped )
+{
+ if (isAttachment())
+ {
+ LLVector3 new_pos = mRegionp->getPosRegionFromGlobal(pos_global);
+ if (isRootEdit())
+ {
+ new_pos -= mDrawable->mXform.getParent()->getWorldPosition();
+ LLQuaternion world_rotation = mDrawable->mXform.getParent()->getWorldRotation();
+ new_pos = new_pos * ~world_rotation;
+ }
+ else
+ {
+ LLViewerObject* parentp = (LLViewerObject*)getParent();
+ new_pos -= parentp->getPositionAgent();
+ new_pos = new_pos * ~parentp->getRotationRegion();
+ }
+ LLViewerObject::setPosition(new_pos);
+
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if( isRoot() )
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is not constant
+ LLViewerObject* parent = (LLViewerObject *)getParent();
+ //RN: this assumes we are only calling this function from the edit tools
+ gPipeline.updateMoveNormalAsync(parent->mDrawable);
+
+ LLVector3 pos_local = mRegionp->getPosRegionFromGlobal(pos_global) - parent->getPositionRegion();
+ pos_local = pos_local * ~parent->getRotationRegion();
+ LLViewerObject::setPosition( pos_local );
+ }
+ }
+ //RN: assumes we always want to snap the object when calling this function
+ gPipeline.updateMoveNormalAsync(mDrawable);
+}
+
+void LLViewerObject::setPosition(const LLVector3 &pos, BOOL damped)
+{
+ if (getPosition() != pos)
+ {
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+
+ LLXform::setPosition(pos);
+ updateDrawable(damped);
+ if (isRoot())
+ {
+ // position caches need to be up to date on root objects
+ updatePositionCaches();
+ }
+}
+
+void LLViewerObject::setPositionGlobal(const LLVector3d &pos_global, BOOL damped)
+{
+ if (isAttachment())
+ {
+ if (isRootEdit())
+ {
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+
+ LLQuaternion invWorldRotation = mDrawable->mXform.getParent()->getWorldRotation();
+ invWorldRotation.transQuat();
+
+ newPos = newPos * invWorldRotation;
+ LLViewerObject::setPosition(newPos);
+ }
+ else
+ {
+ // assumes parent is root editable (root of attachment)
+ LLVector3 newPos = mRegionp->getPosRegionFromGlobal(pos_global);
+ newPos = newPos - mDrawable->mXform.getParent()->getWorldPosition();
+ LLVector3 delta_pos = newPos - getPosition();
+
+ LLQuaternion invRotation = mDrawable->getRotation();
+ invRotation.transQuat();
+
+ delta_pos = delta_pos * invRotation;
+
+ //FIXME: is this right? Shouldn't we be calling the LLViewerObject version of setPosition?
+ LLVector3 old_pos = mDrawable->mXform.getParent()->getPosition();
+ mDrawable->mXform.getParent()->setPosition(old_pos + delta_pos);
+ setChanged(TRANSLATED | SILHOUETTE);
+ }
+ if (mParent && ((LLViewerObject*)mParent)->isAvatar())
+ {
+ // we have changed the position of an attachment, so we need to clamp it
+ LLVOAvatar *avatar = (LLVOAvatar*)mParent;
+
+ avatar->clampAttachmentPositions();
+ }
+ }
+ else
+ {
+ if (isRoot())
+ {
+ setPositionRegion(mRegionp->getPosRegionFromGlobal(pos_global));
+ }
+ else
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3d position_offset;
+ position_offset.setVec(getPosition()*getParent()->getRotation());
+ LLVector3d new_pos_global = pos_global - position_offset;
+ ((LLViewerObject *)getParent())->setPositionGlobal(new_pos_global);
+ }
+ }
+ updateDrawable(damped);
+}
+
+
+void LLViewerObject::setPositionParent(const LLVector3 &pos_parent, BOOL damped)
+{
+ // Set position relative to parent, if no parent, relative to region
+ if (!isRoot())
+ {
+ LLViewerObject::setPosition(pos_parent);
+ updateDrawable(damped);
+ }
+ else
+ {
+ setPositionRegion(pos_parent, damped);
+ }
+}
+
+void LLViewerObject::setPositionRegion(const LLVector3 &pos_region, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ LLViewerObject* parent = (LLViewerObject*) getParent();
+ LLViewerObject::setPosition((pos_region-parent->getPositionRegion())*~parent->getRotationRegion());
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_region);
+ mPositionRegion = pos_region;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+}
+
+void LLViewerObject::setPositionAgent(const LLVector3 &pos_agent, BOOL damped)
+{
+ LLVector3 pos_region = getRegion()->getPosRegionFromAgent(pos_agent);
+ setPositionRegion(pos_region, damped);
+}
+
+// identical to setPositionRegion() except it checks for child-joints
+// and doesn't also move the joint-parent
+// TODO -- implement similar intelligence for joint-parents toward
+// their joint-children
+void LLViewerObject::setPositionEdit(const LLVector3 &pos_edit, BOOL damped)
+{
+ if (!isRootEdit())
+ {
+ // the relative position with the parent is constant, but the parent's position needs to be changed
+ LLVector3 position_offset = getPosition() * getParent()->getRotation();
+
+ ((LLViewerObject *)getParent())->setPositionEdit(pos_edit - position_offset);
+ }
+ else if (isJointChild())
+ {
+ // compute new parent-relative position
+ LLViewerObject *parent = (LLViewerObject *) getParent();
+ LLQuaternion inv_parent_rot = parent->getRotation();
+ inv_parent_rot.transQuat();
+ LLVector3 pos_parent = (pos_edit - parent->getPositionRegion()) * inv_parent_rot;
+ LLViewerObject::setPosition(pos_parent);
+ }
+ else
+ {
+ LLViewerObject::setPosition(pos_edit);
+ mPositionRegion = pos_edit;
+ mPositionAgent = mRegionp->getPosAgentFromRegion(mPositionRegion);
+ }
+ updateDrawable(damped);
+}
+
+
+LLViewerObject* LLViewerObject::getRootEdit() const
+{
+ const LLViewerObject* root = this;
+ while (root->mParent
+ && !(root->mJointInfo
+ || ((LLViewerObject*)root->mParent)->isAvatar()) )
+ {
+ root = (LLViewerObject*)root->mParent;
+ }
+ return (LLViewerObject*)root;
+}
+
+U8 LLViewerObject::getMediaType() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaType;
+ }
+ else
+ {
+ return LLViewerObject::MEDIA_TYPE_NONE;
+ }
+}
+
+void LLViewerObject::setMediaType(U8 media_type)
+{
+ if (!mMedia)
+ {
+ // JAMESDEBUG TODO what if we don't have a media pointer?
+ }
+ else if (mMedia->mMediaType != media_type)
+ {
+ mMedia->mMediaType = media_type;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+const LLString& LLViewerObject::getMediaURL() const
+{
+ if (mMedia)
+ {
+ return mMedia->mMediaURL;
+ }
+ else
+ {
+ return LLString::null;
+ }
+}
+
+void LLViewerObject::setMediaURL(const LLString& media_url)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mMedia)
+ {
+ mMedia = new LLViewerObjectMedia;
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ gMediaList->addedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ else if (mMedia->mMediaURL != media_url)
+ {
+ mMedia->mMediaURL = media_url;
+ mMedia->mPassedWhitelist = FALSE;
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ gMediaList->updatedMediaURL(this);
+ }
+ if (mDrawable.notNull())
+ {
+ // move this object's faces into LLDrawPoolMedia
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+BOOL LLViewerObject::getMediaPassedWhitelist() const
+{
+ if (mMedia)
+ {
+ return mMedia->mPassedWhitelist;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void LLViewerObject::setMediaPassedWhitelist(BOOL passed)
+{
+ if (mMedia)
+ {
+ mMedia->mPassedWhitelist = passed;
+ }
+}
+
+BOOL LLViewerObject::setMaterial(const U8 material)
+{
+ BOOL res = LLPrimitive::setMaterial(material);
+ if (res)
+ {
+ setChanged(TEXTURE);
+ }
+ return res;
+}
+
+void LLViewerObject::setNumTEs(const U8 num_tes)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ U32 i;
+ if (num_tes != getNumTEs())
+ {
+ if (num_tes)
+ {
+ LLPointer<LLViewerImage> *new_images;
+ new_images = new LLPointer<LLViewerImage>[num_tes];
+ for (i = 0; i < num_tes; i++)
+ {
+ if (i < getNumTEs())
+ {
+ new_images[i] = mTEImages[i];
+ }
+ else if (getNumTEs())
+ {
+ new_images[i] = mTEImages[getNumTEs()-1];
+ }
+ else
+ {
+ new_images[i] = NULL;
+ }
+ }
+
+ deleteTEImages();
+
+ mTEImages = new_images;
+ }
+ else
+ {
+ deleteTEImages();
+ }
+ LLPrimitive::setNumTEs(num_tes);
+ setChanged(TEXTURE);
+
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+void LLViewerObject::sendMaterialUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectMaterial);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addU8Fast(_PREHASH_Material, getMaterial() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+
+}
+
+// formerly send_object_rotation
+void LLViewerObject::sendRotationUpdate() const
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessageFast(_PREHASH_ObjectRotation);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID);
+ gMessageSystem->addQuatFast(_PREHASH_Rotation, getRotationEdit());
+ //llinfos << "Sent rotation " << getRotationEdit() << llendl;
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+// formerly send_object_position_global
+void LLViewerObject::sendPositionUpdate() const
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectPosition);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Position, getPositionRegion());
+ LLViewerRegion* regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost());
+}
+
+
+//formerly send_object_scale
+void LLViewerObject::sendScaleUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectScale);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ gMessageSystem->addVector3Fast(_PREHASH_Scale, (getScale()));
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable(regionp->getHost() );
+}
+
+
+//formerly send_object_shape(LLViewerObject *object)
+void LLViewerObject::sendShapeUpdate()
+{
+ gMessageSystem->newMessageFast(_PREHASH_ObjectShape);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ LLVolumeMessage::packVolumeParams(&getVolume()->getParams(), gMessageSystem);
+
+ LLViewerRegion *regionp = getRegion();
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+
+void LLViewerObject::sendTEUpdate() const
+{
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectImage);
+
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+ if (mMedia)
+ {
+ msg->addString("MediaURL", mMedia->mMediaURL);
+ }
+ else
+ {
+ msg->addString("MediaURL", NULL);
+ }
+
+ // JAMESDEBUG TODO send media type
+
+ packTEMessage(msg);
+
+ LLViewerRegion *regionp = getRegion();
+ msg->sendReliable( regionp->getHost() );
+}
+
+void LLViewerObject::setTE(const U8 te, const LLTextureEntry &texture_entry)
+{
+ LLPrimitive::setTE(te, texture_entry);
+// JAMESDEBUG This doesn't work, don't get any textures.
+// if (mDrawable.notNull() && mDrawable->isVisible())
+// {
+ const LLUUID& image_id = getTE(te)->getID();
+ mTEImages[te] = gImageList.getImage(image_id);
+// }
+}
+
+void LLViewerObject::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+ if (mTEImages[te] != imagep)
+ {
+ mTEImages[te] = imagep;
+ LLPrimitive::setTETexture(te, imagep->getID());
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+}
+
+
+S32 LLViewerObject::setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host)
+{
+ S32 retval = 0;
+ if (uuid != getTE(te)->getID() ||
+ uuid == LLUUID::null)
+ {
+ retval = LLPrimitive::setTETexture(te, uuid);
+ mTEImages[te] = gImageList.getImageFromHost(uuid, host);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // Invalid host == get from the agent's sim
+ return setTETextureCore(te, uuid, LLHost::invalid);
+}
+
+
+S32 LLViewerObject::setTEColor(const U8 te, const LLColor4 &color)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (color != tep->getColor())
+ {
+ retval = LLPrimitive::setTEColor(te, color);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ // These should only happen on updates which are not the initial update.
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEBumpmap(const U8 te, const U8 bump)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (bump != tep->getBumpmap())
+ {
+ retval = LLPrimitive::setTEBumpmap(te, bump);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTETexGen(const U8 te, const U8 texgen)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (texgen != tep->getTexGen())
+ {
+ retval = LLPrimitive::setTETexGen(te, texgen);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEShiny(const U8 te, const U8 shiny)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (shiny != tep->getShiny())
+ {
+ retval = LLPrimitive::setTEShiny(te, shiny);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (fullbright != tep->getFullbright())
+ {
+ retval = LLPrimitive::setTEFullbright(te, fullbright);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markTextured(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ // JAMESDEBUG this might need work for media type
+ S32 retval = 0;
+ const LLTextureEntry *tep = getTE(te);
+ if (!tep)
+ {
+ llwarns << "No texture entry for te " << (S32)te << ", object " << mID << llendl;
+ }
+ else if (media_flags != tep->getMediaFlags())
+ {
+ retval = LLPrimitive::setTEMediaFlags(te, media_flags);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ gPipeline.markTextured(mDrawable);
+ // JC - probably only need this if changes texture coords
+ //gPipeline.markRebuild(mDrawable);
+ }
+ }
+ return retval;
+}
+
+
+S32 LLViewerObject::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = 0;
+ retval = LLPrimitive::setTEScale(te, s, t);
+ setChanged(TEXTURE);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEScaleS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEScaleT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEScaleT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffset(const U8 te, const F32 s, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffset(te, s, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetS(const U8 te, const F32 s)
+{
+ S32 retval = LLPrimitive::setTEOffsetS(te, s);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTEOffsetT(const U8 te, const F32 t)
+{
+ S32 retval = LLPrimitive::setTEOffsetT(te, t);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+
+ return retval;
+}
+
+S32 LLViewerObject::setTERotation(const U8 te, const F32 r)
+{
+ S32 retval = LLPrimitive::setTERotation(te, r);
+ if (mDrawable.notNull() && retval)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD);
+ }
+ return retval;
+}
+
+
+LLViewerImage *LLViewerObject::getTEImage(const U8 face) const
+{
+// llassert(mTEImages);
+
+ if (face < getNumTEs())
+ {
+ LLViewerImage* image = mTEImages[face];
+ if (image)
+ {
+ return image;
+ }
+ else
+ {
+ return (LLViewerImage*)((LLImageGL*)LLViewerImage::sDefaultImagep);
+ }
+ }
+
+ llerrs << "Requested invalid face!" << llendl;
+
+ return NULL;
+}
+
+
+void LLViewerObject::fitFaceTexture(const U8 face)
+{
+ llinfos << "fitFaceTexture not implemented" << llendl;
+}
+
+
+LLBBox LLViewerObject::getBoundingBoxAgent() const
+{
+ LLVector3 position_agent;
+ LLQuaternion rot;
+ LLViewerObject* root_edit = (LLViewerObject*)getRootEdit();
+ LLViewerObject* avatar_parent = (LLViewerObject*)root_edit->getParent();
+ if (avatar_parent && avatar_parent->isAvatar() && root_edit->mDrawable.notNull())
+ {
+ LLXform* parent_xform = root_edit->mDrawable->getXform()->getParent();
+ position_agent = (getPositionEdit() * parent_xform->getWorldRotation()) + parent_xform->getWorldPosition();
+ rot = getRotationEdit() * parent_xform->getWorldRotation();
+ }
+ else
+ {
+ position_agent = getPositionAgent();
+ rot = getRotationRegion();
+ }
+
+ return LLBBox( position_agent, rot, getScale() * -0.5f, getScale() * 0.5f );
+}
+
+U32 LLViewerObject::getNumVertices() const
+{
+ U32 num_vertices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_vertices += mDrawable->getFace(i)->getGeomCount();
+ }
+ }
+ return num_vertices;
+}
+
+U32 LLViewerObject::getNumIndices() const
+{
+ U32 num_indices = 0;
+ if (mDrawable.notNull())
+ {
+ S32 i, num_faces;
+ num_faces = mDrawable->getNumFaces();
+ for (i = 0; i < num_faces; i++)
+ {
+ num_indices += mDrawable->getFace(i)->getIndicesCount();
+ }
+ }
+ return num_indices;
+}
+
+// Find the number of instances of this object's inventory that are of the given type
+S32 LLViewerObject::countInventoryContents(LLAssetType::EType type)
+{
+ S32 count = 0;
+ if( mInventory )
+ {
+ InventoryObjectList::const_iterator it = mInventory->begin();
+ InventoryObjectList::const_iterator end = mInventory->end();
+ for( ; it != end ; ++it )
+ {
+ if( (*it)->getType() == type )
+ {
+ ++count;
+ }
+ }
+ }
+ return count;
+}
+
+
+void LLViewerObject::setCanSelect(BOOL canSelect)
+{
+ mbCanSelect = canSelect;
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->mbCanSelect = canSelect;
+ }
+}
+
+void LLViewerObject::setDebugText(const std::string &utf8text)
+{
+ if (!mText)
+ {
+ mText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mText->setFont(LLFontGL::sSansSerif);
+ mText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mText->setMaxLines(-1);
+ mText->setSourceObject(this);
+ mText->setOnHUDAttachment(isHUDAttachment());
+ }
+ mText->setColor(LLColor4::white);
+ mText->setStringUTF8(utf8text);
+ mText->setZCompare(FALSE);
+ mText->setDoFade(FALSE);
+ updateText();
+}
+
+void LLViewerObject::setIcon(LLViewerImage* icon_image)
+{
+ if (!mIcon)
+ {
+ mIcon = (LLHUDIcon *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_ICON);
+ mIcon->setSourceObject(this);
+ mIcon->setImage(icon_image);
+ //FIXME: make this user configurable
+ mIcon->setScale(0.03f);
+ }
+ else
+ {
+ mIcon->restartLifeTimer();
+ }
+}
+
+void LLViewerObject::clearIcon()
+{
+ if (mIcon)
+ {
+ mIcon = NULL;
+ }
+}
+
+LLViewerObject* LLViewerObject::getSubParent()
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (LLViewerObject*) getParent();
+}
+
+const LLViewerObject* LLViewerObject::getSubParent() const
+{
+ if (isJointChild())
+ {
+ return this;
+ }
+ return (const LLViewerObject*) getParent();
+}
+
+BOOL LLViewerObject::isOnMap()
+{
+ return mOnMap;
+}
+
+
+void LLViewerObject::updateText()
+{
+ if (!isDead())
+ {
+ if (mText.notNull())
+ {
+ LLVector3 up_offset(0,0,0);
+ up_offset.mV[2] = getScale().mV[VZ]*0.6f;
+
+ if (mDrawable.notNull())
+ {
+ mText->setPositionAgent(getRenderPosition() + up_offset);
+ }
+ else
+ {
+ mText->setPositionAgent(getPositionAgent() + up_offset);
+ }
+ }
+ }
+}
+
+BOOL LLViewerObject::isParticleSource() const
+{
+ return !mPartSourcep.isNull() && !mPartSourcep->isDead();
+}
+
+void LLViewerObject::unpackParticleSource(const S32 block_num, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, block_num))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, block_num);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+void LLViewerObject::unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id)
+{
+ if (!mPartSourcep.isNull() && mPartSourcep->isDead())
+ {
+ mPartSourcep = NULL;
+ }
+ if (mPartSourcep)
+ {
+ // If we've got one already, just update the existing source (or remove it)
+ if (!LLViewerPartSourceScript::unpackPSS(this, mPartSourcep, dp))
+ {
+ mPartSourcep->setDead();
+ mPartSourcep = NULL;
+ }
+ }
+ else
+ {
+ //If the owner is muted, don't create the system
+ if(gMuteListp->isMuted(owner_id)) return;
+ LLViewerPartSourceScript *pss = LLViewerPartSourceScript::unpackPSS(this, NULL, dp);
+
+ // We need to be able to deal with a particle source that hasn't changed, but still got an update!
+ if (pss)
+ {
+// llinfos << "Making particle system with owner " << owner_id << llendl;
+ pss->setOwnerUUID(owner_id);
+ mPartSourcep = pss;
+ gWorldPointer->mPartSim.addPartSource(pss);
+ }
+ }
+ if (mPartSourcep)
+ {
+ if (mPartSourcep->getImage()->getID() != mPartSourcep->mPartSysData.mPartImageID)
+ {
+ LLViewerImage* image;
+ if (mPartSourcep->mPartSysData.mPartImageID == LLUUID::null)
+ {
+ LLUUID id(gViewerArt.getString("pixiesmall.tga"));
+ image = gImageList.getImage(id);
+ }
+ else
+ {
+ image = gImageList.getImage(mPartSourcep->mPartSysData.mPartImageID);
+ }
+ mPartSourcep->setImage(image);
+ }
+ }
+}
+
+// virtual
+void LLViewerObject::updateDrawable(BOOL force_damped)
+{
+ if (mDrawable.notNull() &&
+ !mDrawable->isState(LLDrawable::ON_MOVE_LIST) &&
+ isChanged(MOVED) &&
+ !isAvatar())
+ {
+ mLastUpdateFrame = LLFrameTimer::getFrameCount();
+ BOOL damped_motion =
+ !isChanged(SHIFTED) && // not shifted between regions this frame and...
+ (force_damped || // ...forced into damped motion by application logic or...
+ (!isSelected() && // ...not selected and...
+ (mDrawable->isRoot() || // ... is root or ...
+ !((LLViewerObject*)getParent())->isSelected()) && // ... parent is not selected and ...
+ getPCode() == LL_PCODE_VOLUME && // ...is a volume object and...
+ getVelocity().isExactlyZero() && // ...is not moving physically and...
+ mDrawable->getGeneration() != -1) // ...was not created this frame.
+ );
+ gPipeline.markMoved(mDrawable, damped_motion);
+ }
+ clearChanged(SHIFTED);
+}
+
+// virtual, overridden by LLVOVolume
+F32 LLViewerObject::getVObjRadius() const
+{
+ return mDrawable.notNull() ? mDrawable->getRadius() : 0.f;
+}
+
+void LLViewerObject::setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (audio_uuid.isNull())
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && !mAudioSourcep->hasPendingPreloads())
+ {
+ // We don't clear the sound if it's a loop, it'll go away on its own.
+ // At least, this appears to be how the scripts work.
+ // The attached sound ID is set to NULL to avoid it playing back when the
+ // object rezzes in on non-looping sounds.
+ //llinfos << "Clearing attached sound " << mAudioSourcep->getCurrentData()->getID() << llendl;
+ gAudiop->cleanupAudioSource(mAudioSourcep);
+ mAudioSourcep = NULL;
+ }
+ else if (mAudioSourcep)
+ {
+ if (mAudioSourcep->isLoop())
+ {
+ // Just shut off the sound
+ mAudioSourcep->play(LLUUID::null);
+ }
+ }
+ return;
+ }
+ if (flags & LL_SOUND_FLAG_LOOP)
+ {
+ if (mAudioSourcep && mAudioSourcep->isLoop() && mAudioSourcep->getCurrentData())
+ {
+ if (mAudioSourcep->getCurrentData()->getID() == audio_uuid)
+ {
+ //llinfos << "Already playing this sound on a loop, ignoring" << llendl;
+ return;
+ }
+ }
+ }
+
+ getAudioSource(owner_id);
+
+ if (mAudioSourcep)
+ {
+ mAudioSourcep->setGain(gain);
+ mAudioSourcep->setLoop((flags & LL_SOUND_FLAG_LOOP) ? TRUE : FALSE);
+ mAudioSourcep->setSyncMaster((flags & LL_SOUND_FLAG_SYNC_MASTER) ? TRUE : FALSE);
+ mAudioSourcep->setSyncSlave((flags & LL_SOUND_FLAG_SYNC_SLAVE) ? TRUE : FALSE);
+ mAudioSourcep->setQueueSounds((flags & LL_SOUND_FLAG_QUEUE) ? TRUE : FALSE);
+ //llinfos << "Playing attached sound " << audio_uuid << llendl;
+ mAudioSourcep->play(audio_uuid);
+ }
+}
+
+LLAudioSource *LLViewerObject::getAudioSource(const LLUUID& owner_id)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+
+ if (!mAudioSourcep)
+ {
+ // Arbitrary low gain for a sound that's not playing.
+ // This is used for sound preloads, for example.
+ LLAudioSourceVO *asvop = new LLAudioSourceVO(mID, owner_id, 0.01f, this);
+
+ mAudioSourcep = asvop;
+ if(gAudiop) gAudiop->addAudioSource(asvop);
+ }
+
+ return mAudioSourcep;
+}
+
+void LLViewerObject::adjustAudioGain(const F32 gain)
+{
+ if (!gAudiop)
+ {
+ return;
+ }
+
+ if (!mAudioSourcep)
+ {
+ return;
+ }
+ mAudioSourcep->setGain(gain);
+}
+
+//----------------------------------------------------------------------------
+
+bool LLViewerObject::unpackParameterEntry(U16 param_type, LLDataPacker *dp)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ param->data->unpack(*dp);
+ param->in_use = TRUE;
+ parameterChanged(param_type, param->data, TRUE, false);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::createNewParameterEntry(U16 param_type)
+{
+ LLNetworkData* new_block = NULL;
+ switch (param_type)
+ {
+ case LLNetworkData::PARAMS_FLEXIBLE:
+ {
+ new_block = new LLFlexibleObjectData();
+ break;
+ }
+ case LLNetworkData::PARAMS_LIGHT:
+ {
+ new_block = new LLLightParams();
+ break;
+ }
+ default:
+ {
+ llinfos << "Unknown param type." << llendl;
+ break;
+ }
+ };
+
+ if (new_block)
+ {
+ ExtraParameter* new_entry = new ExtraParameter;
+ new_entry->data = new_block;
+ new_entry->in_use = false; // not in use yet
+ mExtraParameterList[param_type] = new_entry;
+ return new_entry;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntry(U16 param_type) const
+{
+ std::map<U16, ExtraParameter*>::const_iterator itor = mExtraParameterList.find(param_type);
+ if (itor != mExtraParameterList.end())
+ {
+ return itor->second;
+ }
+ return NULL;
+}
+
+LLViewerObject::ExtraParameter* LLViewerObject::getExtraParameterEntryCreate(U16 param_type)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (!param)
+ {
+ param = createNewParameterEntry(param_type);
+ }
+ return param;
+}
+
+LLNetworkData* LLViewerObject::getParameterEntry(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->data;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+BOOL LLViewerObject::getParameterEntryInUse(U16 param_type) const
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ return param->in_use;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+bool LLViewerObject::setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param)
+ {
+ if (param->in_use && new_value == *(param->data))
+ {
+ return false;
+ }
+ param->in_use = true;
+ param->data->copy(new_value);
+ parameterChanged(param_type, param->data, TRUE, local_origin);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+// Assumed to be called locally
+// If in_use is TRUE, will crate a new extra parameter if none exists.
+// Should always return true.
+bool LLViewerObject::setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntryCreate(param_type);
+ if (param->in_use != in_use)
+ {
+ param->in_use = in_use;
+ parameterChanged(param_type, param->data, in_use, local_origin);
+ return true;
+ }
+ return false;
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, bool local_origin)
+{
+ ExtraParameter* param = getExtraParameterEntry(param_type);
+ if (param)
+ {
+ parameterChanged(param_type, param->data, param->in_use, local_origin);
+ }
+}
+
+void LLViewerObject::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ if (local_origin)
+ {
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+
+ // Change happened on the viewer. Send the change up
+ U8 tmp[MAX_OBJECT_PARAMS_SIZE];
+ LLDataPackerBinaryBuffer dpb(tmp, MAX_OBJECT_PARAMS_SIZE);
+ if (data->pack(dpb))
+ {
+ U32 datasize = (U32)dpb.getCurrentSize();
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ObjectExtraParams);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU32Fast(_PREHASH_ObjectLocalID, mLocalID );
+
+ msg->addU16Fast(_PREHASH_ParamType, param_type);
+ msg->addBOOLFast(_PREHASH_ParamInUse, in_use);
+
+ msg->addU32Fast(_PREHASH_ParamSize, datasize);
+ msg->addBinaryDataFast(_PREHASH_ParamData, tmp, datasize);
+
+ msg->sendReliable( regionp->getHost() );
+ }
+ else
+ {
+ llwarns << "Failed to send object extra parameters: " << param_type << llendl;
+ }
+ }
+}
+
+void LLViewerObject::setDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->setState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->setDrawableState(state, recursive);
+ }
+ }
+}
+
+void LLViewerObject::clearDrawableState(U32 state, BOOL recursive)
+{
+ if (mDrawable)
+ {
+ mDrawable->clearState(state);
+ }
+ if (recursive)
+ {
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ mChildList[i]->clearDrawableState(state, recursive);
+ }
+ }
+}
+
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// RN: these functions assume a 2-level hierarchy
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+// Owned by anyone?
+BOOL LLViewerObject::permAnyOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_ANY_OWNER) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permAnyOwner();
+ }
+}
+// Owned by this viewer?
+BOOL LLViewerObject::permYouOwner() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_YOU_OWNER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permYouOwner();
+ }
+}
+
+// Owned by a group?
+BOOL LLViewerObject::permGroupOwner() const
+{
+ if (isRootEdit())
+ {
+ return ((mFlags & FLAGS_OBJECT_GROUP_OWNED) != 0);
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permGroupOwner();
+ }
+}
+
+// Can the owner edit
+BOOL LLViewerObject::permOwnerModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_OWNER_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permOwnerModify();
+ }
+}
+
+// Can edit
+BOOL LLViewerObject::permModify() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MODIFY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permModify();
+ }
+}
+
+// Can copy
+BOOL LLViewerObject::permCopy() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_COPY) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permCopy();
+ }
+}
+
+// Can move
+BOOL LLViewerObject::permMove() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_MOVE) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permMove();
+ }
+}
+
+// Can be transferred
+BOOL LLViewerObject::permTransfer() const
+{
+ if (isRootEdit())
+ {
+#ifdef HACKED_GODLIKE_VIEWER
+ return TRUE;
+#else
+# ifdef TOGGLE_HACKED_GODLIKE_VIEWER
+ if (!gInProductionGrid && (gAgent.getGodLevel() >= GOD_MAINTENANCE))
+ {
+ return TRUE;
+ }
+# endif
+ return ((mFlags & FLAGS_OBJECT_TRANSFER) != 0);
+#endif
+ }
+ else
+ {
+ return ((LLViewerObject*)getParent())->permTransfer();
+ }
+}
+
+// Can only open objects that you own, or that someone has
+// given you modify rights to. JC
+BOOL LLViewerObject::allowOpen() const
+{
+ return !flagInventoryEmpty() && (permYouOwner() || permModify());
+}
+
+LLViewerObject::LLInventoryCallbackInfo::~LLInventoryCallbackInfo()
+{
+ if (mListener)
+ {
+ mListener->clearVOInventoryListener();
+ }
+}
+
+void LLViewerObject::updateVolume(const LLVolumeParams& volume_params)
+{
+ if (setVolume(volume_params, 1)) // FIXME: magic number, ack!
+ {
+ // Transmit the update to the simulator
+ sendShapeUpdate();
+ markForUpdate(TRUE);
+ }
+}
+
+void LLViewerObject::markForUpdate(BOOL priority)
+{
+ if (mDrawable.notNull())
+ {
+ gPipeline.markTextured(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, priority);
+ }
+}
+
+void LLViewerObject::setRegion(LLViewerRegion *regionp)
+{
+ llassert(regionp);
+ mRegionp = regionp;
+ setChanged(MOVED | SILHOUETTE);
+ updateDrawable(FALSE);
+}
+
+bool LLViewerObject::specialHoverCursor() const
+{
+ return (mFlags & FLAGS_USE_PHYSICS)
+ || (mFlags & FLAGS_HANDLE_TOUCH)
+ || (mClickAction != 0);
+}
+
+void LLViewerObject::updateFlags()
+{
+ LLViewerRegion* regionp = getRegion();
+ if(!regionp) return;
+ gMessageSystem->newMessage("ObjectFlagUpdate");
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ gMessageSystem->addU32Fast(_PREHASH_ObjectLocalID, getLocalID() );
+ gMessageSystem->addBOOLFast(_PREHASH_UsePhysics, usePhysics() );
+ gMessageSystem->addBOOL("IsTemporary", flagTemporaryOnRez() );
+ gMessageSystem->addBOOL("IsPhantom", flagPhantom() );
+ gMessageSystem->addBOOL("CastsShadows", flagCastShadows() );
+ gMessageSystem->sendReliable( regionp->getHost() );
+}
+
+BOOL LLViewerObject::setFlags(U32 flags, BOOL state)
+{
+ BOOL setit = FALSE;
+ if (state)
+ {
+ if ((mFlags & flags) != flags)
+ {
+ mFlags |= flags;
+ setit = TRUE;
+ }
+ }
+ else
+ {
+ if ((mFlags & flags) != 0)
+ {
+ mFlags &= ~flags;
+ setit = TRUE;
+ }
+ }
+
+ // BUG: Sometimes viewer physics and simulator physics get
+ // out of sync. To fix this, always send update to simulator.
+// if (setit)
+ {
+ updateFlags();
+ }
+ return setit;
+}
+
+BOOL LLViewerObject::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ return FALSE;
+}
+
+void LLViewerObject::applyAngularVelocity(F32 dt)
+{
+ //do target omega here
+ mRotTime += dt;
+ LLVector3 ang_vel = getAngularVelocity();
+ F32 omega = ang_vel.magVecSquared();
+ F32 angle = 0.0f;
+ LLQuaternion dQ;
+ if (omega > 0.00001f)
+ {
+ omega = sqrt(omega);
+ angle = omega * dt;
+
+ ang_vel *= 1.f/omega;
+
+ dQ.setQuat(angle, ang_vel);
+
+ setRotation(getRotation()*dQ);
+ setChanged(MOVED | SILHOUETTE);
+ }
+}
+
+void LLViewerObject::resetRot()
+{
+ mRotTime = 0.0f;
+}
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
new file mode 100644
index 0000000000..959c44abaa
--- /dev/null
+++ b/indra/newview/llviewerobject.h
@@ -0,0 +1,626 @@
+/**
+ * @file llviewerobject.h
+ * @brief Description of LLViewerObject class, which is the base class for most objects in the viewer.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWEROBJECT_H
+#define LL_LLVIEWEROBJECT_H
+
+#include <map>
+
+#include "linked_lists.h"
+#include "llassetstorage.h"
+#include "lldarrayptr.h"
+#include "llhudtext.h"
+#include "llhudicon.h"
+#include "llinventory.h"
+#include "llmemory.h"
+#include "llprimitive.h"
+#include "lluuid.h"
+#include "llvoinventorylistener.h"
+#include "object_flags.h"
+#include "llquaternion.h"
+#include "v3dmath.h"
+#include "v3math.h"
+
+class LLAgent; // TODO: Get rid of this.
+class LLAudioSource;
+class LLAudioSourceVO;
+class LLBBox;
+class LLDataPacker;
+class LLColor4;
+class LLFrameTimer;
+class LLDrawable;
+class LLHost;
+class LLWorld;
+class LLNameValue;
+class LLNetMap;
+class LLMessageSystem;
+class LLPrimitive;
+class LLPipeline;
+class LLTextureEntry;
+class LLViewerImage;
+class LLViewerInventoryItem;
+class LLViewerObject;
+class LLViewerPartSourceScript;
+class LLViewerRegion;
+class LLViewerObjectMedia;
+class LLVOInventoryListener;
+
+typedef enum e_object_update_type
+{
+ OUT_FULL,
+ OUT_TERSE_IMPROVED,
+ OUT_FULL_COMPRESSED,
+ OUT_FULL_CACHED,
+} EObjectUpdateType;
+
+
+// callback typedef for inventory
+typedef void (*inventory_callback)(LLViewerObject*,
+ InventoryObjectList*,
+ S32 serial_num,
+ void*);
+
+// a small struct for keeping track of joints
+struct LLVOJointInfo
+{
+ EHavokJointType mJointType;
+ LLVector3 mPivot; // parent-frame
+ // whether the below an axis or anchor (and thus its frame)
+ // depends on the joint type:
+ // HINGE ==> axis=parent-frame
+ // P2P ==> anchor=child-frame
+ LLVector3 mAxisOrAnchor;
+};
+
+// for exporting textured materials from SL
+struct LLMaterialExportInfo
+{
+public:
+ LLMaterialExportInfo(S32 mat_index, S32 texture_index, LLColor4 color) :
+ mMaterialIndex(mat_index), mTextureIndex(texture_index), mColor(color) {};
+
+ S32 mMaterialIndex;
+ S32 mTextureIndex;
+ LLColor4 mColor;
+};
+
+//============================================================================
+
+class LLViewerObject : public LLPrimitive, public LLRefCount
+{
+protected:
+ virtual ~LLViewerObject(); // use unref()
+
+ // TomY: Provide for a list of extra parameter structures, mapped by structure name
+ struct ExtraParameter
+ {
+ BOOL in_use;
+ LLNetworkData *data;
+ };
+ std::map<U16, ExtraParameter*> mExtraParameterList;
+
+public:
+ typedef std::vector<LLPointer<LLViewerObject> > child_list_t;
+
+ LLViewerObject(const LLUUID &id, const LLPCode type, LLViewerRegion *regionp);
+ MEM_TYPE_NEW(LLMemType::MTYPE_OBJECT);
+
+ virtual void markDead(); // Mark this object as dead, and clean up its references
+ BOOL isDead() const {return mDead;}
+ BOOL isOrphaned() const { return mOrphaned; }
+ BOOL isParticleSource() const;
+
+ static void initVOClasses();
+ static void cleanupVOClasses();
+
+ void addNVPair(const char* data);
+ BOOL removeNVPair(const char *name);
+ LLNameValue *getNVPair(const char *name) const; // null if no name value pair by that name
+
+ // Object create and update functions
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Types of media we can associate
+ enum { MEDIA_TYPE_NONE = 0, MEDIA_TYPE_WEB_PAGE = 1 };
+
+ // Return codes for processUpdateMessage
+ enum { MEDIA_URL_REMOVED = 0x1, MEDIA_URL_ADDED = 0x2, MEDIA_URL_UPDATED = 0x4 };
+
+ enum { CLICK_ACTION_TOUCH = 0, CLICK_ACTION_SIT = 1, CLICK_ACTION_BUY = 2 };
+
+ virtual U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+
+ virtual BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL onActiveList() const {return mOnActiveList;}
+ void setOnActiveList(BOOL on_active) { mOnActiveList = on_active; }
+
+ virtual BOOL isAttachment() const { return FALSE; }
+ virtual BOOL isHUDAttachment() const { return FALSE; }
+ virtual void updateRadius() {};
+ virtual F32 getVObjRadius() const; // default implemenation is mDrawable->getRadius()
+
+ BOOL isJointChild() const { return mJointInfo ? TRUE : FALSE; }
+ EHavokJointType getJointType() const { return mJointInfo ? mJointInfo->mJointType : HJT_INVALID; }
+ // for jointed and other parent-relative hacks
+ LLViewerObject* getSubParent();
+ const LLViewerObject* getSubParent() const;
+
+ // Object visiblility and GPW functions
+ virtual void setPixelAreaAndAngle(LLAgent &agent); // Override to generate accurate apparent angle and area
+
+ virtual U32 getNumVertices() const;
+ virtual U32 getNumIndices() const;
+ S32 getNumFaces() const { return mNumFaces; }
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+ virtual void updateTextures(LLAgent &agent);
+ virtual void boostTexturePriority(BOOL boost_children = TRUE); // When you just want to boost priority of this object
+
+ virtual LLDrawable* createDrawable(LLPipeline *pipeline);
+ virtual BOOL updateGeometry(LLDrawable *drawable);
+ virtual BOOL updateLOD();
+ virtual BOOL setDrawableParent(LLDrawable* parentp);
+ virtual BOOL updateLighting(BOOL do_lighting) { return TRUE; };
+ F32 getRotTime() { return mRotTime; }
+ void resetRot();
+ void applyAngularVelocity(F32 dt);
+
+ void setLineWidthForWindowSize(S32 window_width);
+
+ static void increaseArrowLength(); // makes axis arrows for selections longer
+ static void decreaseArrowLength(); // makes axis arrows for selections shorter
+
+ // Accessor functions
+ LLViewerRegion* getRegion() const { return mRegionp; }
+
+ BOOL isSelected() const { return mUserSelected; }
+ void setSelected(BOOL sel) { mUserSelected = sel; mRotTime = 0.f;}
+
+ const LLUUID &getID() const { return mID; }
+ U32 getLocalID() const { return mLocalID; }
+ U32 getCRC() const { return mTotalCRC; }
+
+ virtual BOOL isFlexible() const { return false; }
+
+ // This method returns true if the object is over land owned by
+ // the agent.
+ BOOL isOverAgentOwnedLand() const;
+
+ // True if over land owned by group of which the agent is
+ // either officer or member.
+ BOOL isOverGroupOwnedLand() const;
+
+ /*
+ // This method will scan through this object, and then query the
+ // selection manager to see if the local agent probably has the
+ // ability to modify the object. Since this calls into the
+ // selection manager, you should avoid calling this method from
+ // there.
+ BOOL isProbablyModifiable() const;
+ */
+
+ virtual void setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+ child_list_t& getChildren();
+ void addThisAndAllChildren(LLDynamicArray<LLViewerObject*>& objects);
+ void addThisAndNonJointChildren(LLDynamicArray<LLViewerObject*>& objects);
+ BOOL isChild(LLViewerObject *childp) const;
+ BOOL isSeat() const;
+
+
+ //detect if given line segment (in agent space) intersects with this viewer object.
+ //returns TRUE if intersection detected and moves end to the point of intersection
+ //closest to start.
+ virtual BOOL lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+
+ const LLVector3d getPositionGlobal() const;
+ const LLVector3 &getPositionRegion() const;
+ const LLVector3 getPositionEdit() const;
+ const LLVector3 &getPositionAgent() const;
+ const LLVector3 getRenderPosition() const;
+
+ virtual const LLVector3 getPivotPositionAgent() const; // Usually = to getPositionAgent, unless like flex objects it's not
+
+ LLViewerObject* getRootEdit() const;
+
+ const LLQuaternion getRotationRegion() const;
+ const LLQuaternion getRotationEdit() const;
+ const LLQuaternion getRenderRotation() const;
+ virtual const LLMatrix4 getRenderMatrix() const;
+
+ void setPosition(const LLVector3 &pos, BOOL damped = FALSE);
+ void setPositionGlobal(const LLVector3d &position, BOOL damped = FALSE);
+ void setPositionRegion(const LLVector3 &position, BOOL damped = FALSE);
+ void setPositionEdit(const LLVector3 &position, BOOL damped = FALSE);
+ void setPositionAgent(const LLVector3 &pos_agent, BOOL damped = FALSE);
+ void setPositionParent(const LLVector3 &pos_parent, BOOL damped = FALSE);
+ void setPositionAbsoluteGlobal( const LLVector3d &pos_global, BOOL damped = FALSE );
+ void sendPositionUpdate() const;
+
+ virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const { return xform->getWorldMatrix(); }
+
+ inline void setRotation(const F32 x, const F32 y, const F32 z, BOOL damped = FALSE);
+ inline void setRotation(const LLQuaternion& quat, BOOL damped = FALSE);
+ void sendRotationUpdate() const;
+
+ /*virtual*/ void setNumTEs(const U8 num_tes);
+ /*virtual*/ void setTE(const U8 te, const LLTextureEntry &texture_entry);
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid);
+ S32 setTETextureCore(const U8 te, const LLUUID& uuid, LLHost host);
+ /*virtual*/ S32 setTEColor(const U8 te, const LLColor4 &color);
+ /*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEScaleS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEScaleT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTEOffset(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEOffsetS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEOffsetT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTERotation(const U8 te, const F32 r);
+ /*virtual*/ S32 setTEBumpmap(const U8 te, const U8 bump );
+ /*virtual*/ S32 setTETexGen(const U8 te, const U8 texgen );
+ /*virtual*/ S32 setTEShiny(const U8 te, const U8 shiny );
+ /*virtual*/ S32 setTEFullbright(const U8 te, const U8 fullbright );
+ /*virtual*/ S32 setTEMediaFlags(const U8 te, const U8 media_flags );
+ /*virtual*/ BOOL setMaterial(const U8 material);
+ virtual void setTEImage(const U8 te, LLViewerImage *imagep); // Not derived from LLPrimitive
+ LLViewerImage *getTEImage(const U8 te) const;
+
+ S32 getFaceIndexOffset() { return mFaceIndexOffset; }
+
+ void fitFaceTexture(const U8 face);
+ void sendTEUpdate() const; // Sends packed representation of all texture entry information
+
+ virtual void setScale(const LLVector3 &scale, BOOL damped = FALSE);
+ void sendScaleUpdate();
+
+ void sendShapeUpdate();
+
+ U8 getState() { return mState; }
+
+ F32 getAppAngle() const { return mAppAngle; }
+ F32 getMaxScale() const;
+ F32 getMidScale() const;
+ F32 getMinScale() const;
+
+ // Owner id is this object's owner
+ void setAttachedSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, const U8 flags);
+ void adjustAudioGain(const F32 gain);
+ void clearAttachedSound() { mAudioSourcep = NULL; }
+
+ // Create if necessary
+ LLAudioSource *getAudioSource(const LLUUID& owner_id);
+
+ U8 getMediaType() const;
+ void setMediaType(U8 media_type);
+
+ const LLString& getMediaURL() const;
+ void setMediaURL(const LLString& media_url);
+
+ BOOL getMediaPassedWhitelist() const;
+ void setMediaPassedWhitelist(BOOL passed);
+
+ void sendMaterialUpdate() const;
+
+ void setCanSelect(BOOL canSelect);
+
+ void setDebugText(const std::string &utf8text);
+ void setIcon(LLViewerImage* icon_image);
+ void clearIcon();
+
+ void markForUpdate(BOOL priority);
+ void updateVolume(const LLVolumeParams& volume_params);
+ virtual void updateSpatialExtents(LLVector3& min, LLVector3& max);
+
+ LLBBox getBoundingBoxAgent() const;
+
+ void updatePositionCaches() const; // Update the global and region position caches from the object (and parent's) xform.
+ void updateText(); // update text label position
+ virtual void updateDrawable(BOOL force_damped); // force updates on static objects
+
+ void setDrawableState(U32 state, BOOL recursive = TRUE);
+ void clearDrawableState(U32 state, BOOL recursive = TRUE);
+
+ // Called when the drawable shifts
+ virtual void onShift(const LLVector3 &shift_vector) { }
+
+ //////////////////////////////////////
+ //
+ // Inventory methods
+ //
+
+ // This function is called when someone is interested in a viewer
+ // object's inventory. The callback is called as soon as the
+ // viewer object has the inventory stored locally.
+ void registerInventoryListener(LLVOInventoryListener* listener, void* user_data);
+ void removeInventoryListener(LLVOInventoryListener* listener);
+ BOOL isInventoryPending() { return mInventoryPending; }
+ void clearInventoryListeners();
+ void requestInventory();
+ static void processTaskInv(LLMessageSystem* msg, void** user_data);
+ void removeInventory(const LLUUID& item_id);
+
+ // The updateInventory() call potentially calls into the selection
+ // manager, so do no call updateInventory() from the selection
+ // manager until we have better iterators.
+ void updateInventory(LLViewerInventoryItem* item, U8 key, bool is_new);
+ LLInventoryObject* getInventoryObject(const LLUUID& item_id);
+ void getInventoryContents(InventoryObjectList& objects);
+ LLInventoryObject* getInventoryRoot();
+ LLViewerInventoryItem* getInventoryItemByAsset(const LLUUID& asset_id);
+ S16 getInventorySerial() const { return mInventorySerialNum; }
+
+ // This function will make sure that we refresh the inventory.
+ void dirtyInventory();
+ BOOL isInventoryDirty() { return mInventoryDirty; }
+
+ // save a script, which involves removing the old one, and rezzing
+ // in the new one. This method should be called with the asset id
+ // of the new and old script AFTER the bytecode has been saved.
+ void saveScript(const LLViewerInventoryItem* item, BOOL active, bool is_new);
+
+ // move an inventory item out of the task and into agent
+ // inventory. This operation is based on messaging. No permissions
+ // checks are made on the viewer - the server will double check.
+ void moveInventory(const LLUUID& agent_folder, const LLUUID& item_id);
+
+ // Find the number of instances of this object's inventory that are of the given type
+ S32 countInventoryContents( LLAssetType::EType type );
+
+ BOOL permAnyOwner() const;
+ BOOL permYouOwner() const;
+ BOOL permGroupOwner() const;
+ BOOL permOwnerModify() const;
+ BOOL permModify() const;
+ BOOL permCopy() const;
+ BOOL permMove() const;
+ BOOL permTransfer() const;
+ inline BOOL usePhysics() const { return ((mFlags & FLAGS_USE_PHYSICS) != 0); }
+ inline BOOL flagScripted() const { return ((mFlags & FLAGS_SCRIPTED) != 0); }
+ inline BOOL flagHandleTouch() const { return ((mFlags & FLAGS_HANDLE_TOUCH) != 0); }
+ inline BOOL flagTakesMoney() const { return ((mFlags & FLAGS_TAKES_MONEY) != 0); }
+ inline BOOL flagPhantom() const { return ((mFlags & FLAGS_PHANTOM) != 0); }
+ inline BOOL flagInventoryEmpty() const { return ((mFlags & FLAGS_INVENTORY_EMPTY) != 0); }
+ inline BOOL flagCastShadows() const { return ((mFlags & FLAGS_CAST_SHADOWS) != 0); }
+ inline BOOL flagAllowInventoryAdd() const { return ((mFlags & FLAGS_ALLOW_INVENTORY_DROP) != 0); }
+ inline BOOL flagTemporary() const { return ((mFlags & FLAGS_TEMPORARY) != 0); }
+ inline BOOL flagTemporaryOnRez() const { return ((mFlags & FLAGS_TEMPORARY_ON_REZ) != 0); }
+ inline BOOL flagAnimSource() const { return ((mFlags & FLAGS_ANIM_SOURCE) != 0); }
+ inline BOOL flagCameraSource() const { return ((mFlags & FLAGS_CAMERA_SOURCE) != 0); }
+ inline BOOL flagCameraDecoupled() const { return ((mFlags & FLAGS_CAMERA_DECOUPLED) != 0); }
+
+ // Does "open" object menu item apply?
+ BOOL allowOpen() const;
+
+ void setClickAction(U8 action) { mClickAction = action; }
+ U8 getClickAction() const { return mClickAction; }
+ bool specialHoverCursor() const; // does it have a special hover cursor?
+
+ void setRegion(LLViewerRegion *regionp);
+ virtual void updateRegion(LLViewerRegion *regionp) {}
+
+ void updateFlags();
+ BOOL setFlags(U32 flag, BOOL state);
+
+ virtual void dump() const;
+ static U32 getNumZombieObjects() { return sNumZombieObjects; }
+
+ void printNameValuePairs() const;
+
+ virtual S32 getLOD() const { return 3; }
+
+ virtual LLNetworkData* getParameterEntry(U16 param_type) const;
+ virtual bool setParameterEntry(U16 param_type, const LLNetworkData& new_value, bool local_origin);
+ virtual BOOL getParameterEntryInUse(U16 param_type) const;
+ virtual bool setParameterEntryInUse(U16 param_type, BOOL in_use, bool local_origin);
+ // Called when a parameter is changed
+ virtual void parameterChanged(U16 param_type, bool local_origin);
+ virtual void parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin);
+
+ friend class LLViewerObjectList;
+ friend class LLViewerMediaList;
+
+private:
+ ExtraParameter* createNewParameterEntry(U16 param_type);
+ ExtraParameter* getExtraParameterEntry(U16 param_type) const;
+ ExtraParameter* getExtraParameterEntryCreate(U16 param_type);
+ bool unpackParameterEntry(U16 param_type, LLDataPacker *dp);
+
+public:
+ //
+ // Viewer-side only types - use the LL_PCODE_APP mask.
+ //
+ typedef enum e_vo_types
+ {
+ LL_VO_CLOUDS = LL_PCODE_APP | 0x20,
+ LL_VO_SURFACE_PATCH = LL_PCODE_APP | 0x30,
+ LL_VO_STARS = LL_PCODE_APP | 0x40,
+ LL_VO_SQUARE_TORUS = LL_PCODE_APP | 0x50,
+ LL_VO_SKY = LL_PCODE_APP | 0x60,
+ LL_VO_WATER = LL_PCODE_APP | 0x70,
+ LL_VO_GROUND = LL_PCODE_APP | 0x80,
+ LL_VO_PART_GROUP = LL_PCODE_APP | 0x90,
+ LL_VO_TRIANGLE_TORUS = LL_PCODE_APP | 0xa0,
+ } EVOType;
+
+ child_list_t mChildList;
+ LLUUID mID;
+
+ // unique within region, not unique across regions
+ // Local ID = 0 is not used
+ U32 mLocalID;
+
+ // Last total CRC received from sim, used for caching
+ U32 mTotalCRC;
+
+ LLPointer<LLViewerImage> *mTEImages;
+
+ // Selection, picking and rendering variables
+ U32 mGLName; // GL "name" used by selection code
+ BOOL mbCanSelect; // true if user can select this object by clicking
+
+ // Grabbed from UPDATE_FLAGS
+ U32 mFlags;
+
+ // Pipeline classes
+ LLPointer<LLDrawable> mDrawable;
+
+ // Band-aid to select object after all creation initialization is done
+ BOOL mCreateSelected;
+
+ // Replace textures with web pages on this object while drawing
+ BOOL mRenderMedia;
+
+ // In bits
+ S32 mBestUpdatePrecision;
+
+ // TODO: Make all this stuff private. JC
+ LLPointer<LLHUDText> mText;
+ LLPointer<LLHUDIcon> mIcon;
+
+ static BOOL sUseSharedDrawables;
+
+protected:
+ // delete an item in the inventory, but don't tell the
+ // server. This is used internally by remove, update, and
+ // savescript.
+ void deleteInventoryItem(const LLUUID& item_id);
+
+ // do the update/caching logic. called by saveScript and
+ // updateInventory.
+ void doUpdateInventory(LLViewerInventoryItem* item, U8 key, bool is_new);
+
+
+ static LLViewerObject *createObject(const LLUUID &id, LLPCode pcode, LLViewerRegion *regionp);
+
+ BOOL setData(const U8 *datap, const U32 data_size);
+
+ //////////////////////////
+ //
+ // inventory functionality
+ //
+
+ static void processTaskInvFile(void** user_data, S32 error_code);
+ void loadTaskInvFile(const char* filename);
+ void doInventoryCallback();
+
+ BOOL isOnMap();
+
+ void unpackParticleSource(const S32 block_num, const LLUUID& owner_id);
+ void unpackParticleSource(LLDataPacker &dp, const LLUUID& owner_id);
+
+private:
+ void setNameValueList(char* list); // clears nv pairs and then individually adds \n separated NV pairs from \0 terminated string
+ void deleteTEImages(); // correctly deletes list of images
+
+protected:
+ typedef std::map<char *, LLNameValue *> name_value_map_t;
+ name_value_map_t mNameValuePairs; // Any name-value pairs stored by script
+
+ F64 mLastInterpUpdateSecs; // Last update for purposes of interpolation
+ F64 mLastMessageUpdateSecs; // Last update from a message from the simulator
+
+ // extra data sent from the sim...currently only used for tree species info
+ U8* mData;
+
+ LLPointer<LLViewerPartSourceScript> mPartSourcep; // Particle source associated with this object.
+ LLAudioSourceVO *mAudioSourcep;
+
+ F32 mAppAngle; // Apparent visual arc in degrees
+ F32 mPixelArea; // Apparent area in pixels
+
+ // This is the object's inventory from the viewer's perspective.
+ InventoryObjectList* mInventory;
+ class LLInventoryCallbackInfo
+ {
+ public:
+ ~LLInventoryCallbackInfo();
+ LLVOInventoryListener* mListener;
+ void* mInventoryData;
+ };
+ LLLinkedList<LLInventoryCallbackInfo> mInventoryCallbacks;
+ S16 mInventorySerialNum;
+
+ LLViewerRegion *mRegionp; // Region that this object belongs to.
+ BOOL mInventoryPending;
+ BOOL mInventoryDirty;
+ BOOL mDead;
+ BOOL mOrphaned; // This is an orphaned child
+ BOOL mUserSelected; // Cached user select information
+ BOOL mOnActiveList;
+ BOOL mOnMap; // On the map.
+ BOOL mStatic; // Object doesn't move.
+ S32 mFaceIndexOffset; // offset into drawable's faces, zero except in special cases
+ S32 mNumFaces;
+
+ S32 mLastUpdateFrame; // frames in which an object had last moved for smart coalescing of drawables
+ // (child objects not moving relative to parent)
+
+ F32 mTimeDilation; // Time dilation sent with the object.
+ F32 mRotTime; // Amount (in seconds) that object has rotated according to angular velocity (llSetTargetOmega)
+ LLQuaternion mLastRot; // last rotation received from the simulator
+
+ LLVOJointInfo* mJointInfo;
+ U8 mState; // legacy
+ LLViewerObjectMedia* mMedia; // NULL if no media associated
+ U8 mClickAction;
+
+ static U32 sNumZombieObjects; // Objects which are dead, but not deleted
+
+ static BOOL sMapDebug; // Map render mode
+ static LLColor4 sEditSelectColor;
+ static LLColor4 sNoEditSelectColor;
+ static F32 sCurrentPulse;
+ static BOOL sPulseEnabled;
+
+ static S32 sAxisArrowLength;
+
+ // These two caches are only correct for non-parented objects right now!
+ mutable LLVector3 mPositionRegion;
+ mutable LLVector3 mPositionAgent;
+
+private:
+ static S32 sNumObjects;
+};
+
+
+///////////////////
+//
+// Inlines
+//
+//
+
+inline void LLViewerObject::setRotation(const LLQuaternion& quat, BOOL damped)
+{
+ LLPrimitive::setRotation(quat);
+ setChanged(ROTATED | SILHOUETTE);
+ updateDrawable(damped);
+}
+
+inline void LLViewerObject::setRotation(const F32 x, const F32 y, const F32 z, BOOL damped)
+{
+ LLPrimitive::setRotation(x, y, z);
+ setChanged(ROTATED | SILHOUETTE);
+ updateDrawable(damped);
+}
+
+class LLViewerObjectMedia
+{
+public:
+ LLViewerObjectMedia() : mMediaURL(), mPassedWhitelist(FALSE), mMediaType(0) { }
+
+ LLString mMediaURL; // for web pages on surfaces, one per prim
+ BOOL mPassedWhitelist; // user has OK'd display
+ U8 mMediaType; // see LLTextureEntry::WEB_PAGE, etc.
+};
+
+#endif
diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp
new file mode 100644
index 0000000000..e1fed12563
--- /dev/null
+++ b/indra/newview/llviewerobjectlist.cpp
@@ -0,0 +1,1484 @@
+/**
+ * @file llviewerobjectlist.cpp
+ * @brief Implementation of LLViewerObjectList class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerobjectlist.h"
+
+#include "gpw.h"
+#include "message.h"
+#include "timing.h"
+#include "llfasttimer.h"
+
+#include "llviewercontrol.h"
+#include "llface.h"
+#include "llvoavatar.h"
+#include "llviewerobject.h"
+#include "llviewerwindow.h"
+#include "viewer.h"
+#include "llnetmap.h"
+#include "llagent.h"
+#include "pipeline.h"
+#include "llhoverview.h"
+#include "llworld.h"
+#include "llstring.h"
+#include "llhudtext.h"
+#include "lldrawable.h"
+#include "xform.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llselectmgr.h"
+#include "llresmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "llkeyboard.h"
+#include "u64.h"
+#include "llviewerimagelist.h"
+#include "lldatapacker.h"
+#include <zlib/zlib.h>
+#include "object_flags.h"
+#include "llviewermedialist.h"
+
+extern BOOL gVelocityInterpolate;
+extern BOOL gPingInterpolate;
+extern F32 gMinObjectDistance;
+extern U32 gFrameCount;
+extern LLTimer gRenderStartTime;
+extern BOOL gAnimateTextures;
+
+void dialog_refresh_all();
+
+#define CULL_VIS
+//#define ORPHAN_SPAM
+//#define IGNORE_DEAD
+
+// Global lists of objects - should go away soon.
+LLViewerObjectList gObjectList;
+
+extern LLPipeline gPipeline;
+
+// Statics for object lookup tables.
+U32 LLViewerObjectList::sSimulatorMachineIndex = 1; // Not zero deliberately, to speed up index check.
+LLMap<U64, U32> LLViewerObjectList::sIPAndPortToIndex;
+std::map<U64, LLUUID> LLViewerObjectList::sIndexAndLocalIDToUUID;
+
+LLViewerObjectList::LLViewerObjectList()
+{
+ mNumVisCulled = 0;
+ mNumSizeCulled = 0;
+ mCurLazyUpdateIndex = 0;
+ mCurBin = 0;
+ mNumDeadObjects = 0;
+ mNumOrphans = 0;
+ mNumNewObjects = 0;
+ mWasPaused = FALSE;
+ mNumDeadObjectUpdates = 0;
+ mNumUnknownKills = 0;
+ mNumUnknownUpdates = 0;
+}
+
+LLViewerObjectList::~LLViewerObjectList()
+{
+ destroy();
+}
+
+void LLViewerObjectList::destroy()
+{
+ killAllObjects();
+
+ resetObjectBeacons();
+ mActiveObjects.clear();
+ mDeadObjects.clear();
+ mMapObjects.clear();
+ mUUIDObjectMap.clear();
+}
+
+
+void LLViewerObjectList::getUUIDFromLocal(LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ id = get_if_there(sIndexAndLocalIDToUUID, indexid, LLUUID::null);
+}
+
+U64 LLViewerObjectList::getIndex(const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ return 0;
+ }
+
+ return (((U64)index) << 32) | (U64)local_id;
+}
+
+BOOL LLViewerObjectList::removeFromLocalIDTable(const LLViewerObject &object)
+{
+ U32 local_id = object.mLocalID;
+ LLHost region_host = object.getRegion()->getHost();
+ U32 ip = region_host.getAddress();
+ U32 port = region_host.getPort();
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+ U32 index = sIPAndPortToIndex[ipport];
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+ return sIndexAndLocalIDToUUID.erase(indexid) > 0 ? TRUE : FALSE;
+}
+
+void LLViewerObjectList::setUUIDAndLocal(const LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port)
+{
+ U64 ipport = (((U64)ip) << 32) | (U64)port;
+
+ U32 index = sIPAndPortToIndex[ipport];
+
+ if (!index)
+ {
+ index = sSimulatorMachineIndex++;
+ sIPAndPortToIndex[ipport] = index;
+ }
+
+ U64 indexid = (((U64)index) << 32) | (U64)local_id;
+
+ sIndexAndLocalIDToUUID[indexid] = id;
+}
+
+S32 gFullObjectUpdates = 0;
+S32 gTerseObjectUpdates = 0;
+
+void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp,
+ void** user_data,
+ U32 i,
+ const EObjectUpdateType update_type,
+ LLDataPacker* dpp,
+ BOOL justCreated)
+{
+ LLMessageSystem* msg = gMessageSystem;
+
+ U32 pum_flags = objectp->processUpdateMessage(msg, user_data, i, update_type, dpp);
+ if (objectp->isDead())
+ {
+ // The update failed
+ return;
+ }
+ updateActive(objectp);
+
+ // Also sets the approx. pixel area
+ objectp->setPixelAreaAndAngle(gAgent);
+
+ // Update the image levels of textures for this object.
+ objectp->updateTextures(gAgent);
+
+ if (justCreated)
+ {
+ gPipeline.addObject(objectp);
+ }
+
+ // RN: this must be called after we have a drawable
+ // (from gPipeline.addObject)
+ // so that the drawable parent is set properly
+ findOrphans(objectp, msg->getSenderIP(), msg->getSenderPort());
+
+ // If we're just wandering around, don't create new objects selected.
+ if (justCreated
+ && update_type != OUT_TERSE_IMPROVED
+ && objectp->mCreateSelected)
+ {
+ if ( gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) ) != gToolPie )
+ {
+ //llinfos << "DEBUG selecting " << objectp->mID << " "
+ // << objectp->mLocalID << llendl;
+ gSelectMgr->selectObjectAndFamily(objectp);
+ dialog_refresh_all();
+ }
+
+ objectp->mCreateSelected = false;
+ gViewerWindow->getWindow()->decBusyCount();
+ gViewerWindow->getWindow()->setCursor( UI_CURSOR_ARROW );
+ }
+
+ if (gMediaList)
+ {
+ // we're using web pages on prims
+ if (pum_flags & LLViewerObject::MEDIA_URL_ADDED)
+ {
+ //llwarns << "WEBONPRIM media url added " << objectp->getMediaURL() << llendl;
+ gMediaList->addedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_UPDATED)
+ {
+ //llwarns << "WEBONPRIM media url updated " << objectp->getMediaURL() << llendl;
+ gMediaList->updatedMediaURL(objectp);
+ }
+
+ if (pum_flags & LLViewerObject::MEDIA_URL_REMOVED)
+ {
+ //llwarns << "WEBONPRIM media url removed " << objectp->getMediaURL() << llendl;
+ gMediaList->removedMediaURL(objectp);
+ }
+
+ // Make sure we get moved in or out of LLDrawPoolMedia, as needed
+ if (pum_flags & (LLViewerObject::MEDIA_URL_ADDED | LLViewerObject::MEDIA_URL_REMOVED | LLViewerObject::MEDIA_URL_UPDATED))
+ {
+ if (objectp->mDrawable.notNull())
+ {
+ gPipeline.markTextured(objectp->mDrawable);
+ }
+ }
+ }
+}
+
+void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type,
+ bool cached, bool compressed)
+{
+ LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS);
+
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+ LLViewerObject *objectp;
+ S32 num_objects;
+ U32 local_id;
+ LLPCode pcode = 0;
+ LLUUID fullid;
+ S32 i;
+
+ // figure out which simulator these are from and get it's index
+ // Coordinates in simulators are region-local
+ // Until we get region-locality working on viewer we
+ // have to transform to absolute coordinates.
+ num_objects = mesgsys->getNumberOfBlocksFast(_PREHASH_ObjectData);
+
+ if (!cached && !compressed && update_type != OUT_FULL)
+ {
+ gTerseObjectUpdates += num_objects;
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+// llinfos << "Received terse " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ }
+ else
+ {
+ S32 size;
+ if (mesgsys->getReceiveCompressedSize())
+ {
+ size = mesgsys->getReceiveCompressedSize();
+ }
+ else
+ {
+ size = mesgsys->getReceiveSize();
+ }
+
+// llinfos << "Received " << num_objects << " in " << size << " byte (" << size/num_objects << ")" << llendl;
+ gFullObjectUpdates += num_objects;
+ }
+
+ U64 region_handle;
+ mesgsys->getU64Fast(_PREHASH_RegionData, _PREHASH_RegionHandle, region_handle);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromHandle(region_handle);
+
+ if (!regionp)
+ {
+ llwarns << "Object update from unknown region!" << llendl;
+ return;
+ }
+
+ U8 compressed_dpbuffer[2048];
+ LLDataPackerBinaryBuffer compressed_dp(compressed_dpbuffer, 2048);
+ LLDataPacker *cached_dpp = NULL;
+
+ for (i = 0; i < num_objects; i++)
+ {
+ LLTimer update_timer;
+ BOOL justCreated = FALSE;
+
+ if (cached)
+ {
+ U32 id;
+ U32 crc;
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, id, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_CRC, crc, i);
+
+ // Lookup data packer and add this id to cache miss lists if necessary.
+ cached_dpp = regionp->getDP(id, crc);
+ if (cached_dpp)
+ {
+ cached_dpp->reset();
+ cached_dpp->unpackUUID(fullid, "ID");
+ cached_dpp->unpackU32(local_id, "LocalID");
+ cached_dpp->unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ continue; // no data packer, skip this object
+ }
+ }
+ else if (compressed)
+ {
+ U8 compbuffer[2048];
+ S32 uncompressed_length = 2048;
+ S32 compressed_length;
+
+ compressed_dp.reset();
+
+ U32 flags = 0;
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_UpdateFlags, flags, i);
+ }
+
+ if (flags & FLAGS_ZLIB_COMPRESSED)
+ {
+ compressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compbuffer, 0, i);
+ uncompressed_length = 2048;
+ uncompress(compressed_dpbuffer, (unsigned long *)&uncompressed_length,
+ compbuffer, compressed_length);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+ else
+ {
+ uncompressed_length = mesgsys->getSizeFast(_PREHASH_ObjectData, i, _PREHASH_Data);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_Data, compressed_dpbuffer, 0, i);
+ compressed_dp.assignBuffer(compressed_dpbuffer, uncompressed_length);
+ }
+
+
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ compressed_dp.unpackUUID(fullid, "ID");
+ compressed_dp.unpackU32(local_id, "LocalID");
+ compressed_dp.unpackU8(pcode, "PCode");
+ }
+ else
+ {
+ compressed_dp.unpackU32(local_id, "LocalID");
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ }
+ else if (update_type != OUT_FULL)
+ {
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ getUUIDFromLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ if (fullid.isNull())
+ {
+ //llwarns << "update for unknown localid " << local_id << " host " << gMessageSystem->getSender() << llendl;
+ mNumUnknownUpdates++;
+ }
+ }
+ else
+ {
+ mesgsys->getUUIDFast(_PREHASH_ObjectData, _PREHASH_FullID, fullid, i);
+ mesgsys->getU32Fast(_PREHASH_ObjectData, _PREHASH_ID, local_id, i);
+ // llinfos << "Full Update, obj " << local_id << ", global ID" << fullid << "from " << mesgsys->getSender() << llendl;
+ }
+ objectp = findObject(fullid);
+
+ // This looks like it will break if the local_id of the object doesn't change
+ // upon boundary crossing, but we check for region id matching later...
+ if (objectp && (objectp->mLocalID != local_id))
+ {
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ }
+
+ if (!objectp)
+ {
+ if (compressed)
+ {
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ // llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+ }
+ else if (cached)
+ {
+ }
+ else
+ {
+ if (update_type != OUT_FULL)
+ {
+// llinfos << "terse update for an unknown object:" << fullid << llendl;
+ continue;
+ }
+
+ mesgsys->getU8Fast(_PREHASH_ObjectData, _PREHASH_PCode, pcode, i);
+ }
+#ifdef IGNORE_DEAD
+ if (mDeadObjects.find(fullid) != mDeadObjects.end())
+ {
+ mNumDeadObjectUpdates++;
+ //llinfos << "update for a dead object:" << fullid << llendl;
+ continue;
+ }
+#endif
+
+ objectp = createObject(pcode, regionp, fullid, local_id, gMessageSystem->getSender());
+ if (!objectp)
+ {
+ continue;
+ }
+ justCreated = TRUE;
+ mNumNewObjects++;
+ }
+ else
+ {
+ if (objectp->getRegion() != regionp)
+ {
+ // Object has changed region! Update lookup tables, set region pointer.
+ removeFromLocalIDTable(*objectp);
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+ objectp->setRegion(regionp);
+ }
+ objectp->updateRegion(regionp); // for LLVOAvatar
+ }
+
+
+ if (objectp->isDead())
+ {
+ llwarns << "Dead object " << objectp->mID << " in UUID map 1!" << llendl;
+ }
+
+ if (compressed)
+ {
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, &compressed_dp, justCreated);
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ objectp->mRegionp->cacheFullUpdate(objectp, compressed_dp);
+ }
+ }
+ else if (cached)
+ {
+ objectp->mLocalID = local_id;
+ processUpdateCore(objectp, user_data, i, update_type, cached_dpp, justCreated);
+ }
+ else
+ {
+ if (update_type == OUT_FULL)
+ {
+ objectp->mLocalID = local_id;
+ }
+ processUpdateCore(objectp, user_data, i, update_type, NULL, justCreated);
+ }
+ }
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+void LLViewerObjectList::processCompressedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, false, true);
+}
+
+void LLViewerObjectList::processCachedObjectUpdate(LLMessageSystem *mesgsys,
+ void **user_data,
+ const EObjectUpdateType update_type)
+{
+ processObjectUpdate(mesgsys, user_data, update_type, true, false);
+}
+
+void LLViewerObjectList::relightAllObjects()
+{
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ LLDrawable *drawable = mObjects[i]->mDrawable;
+ if (drawable)
+ {
+ gPipeline.markRelight(drawable);
+ }
+ }
+}
+
+void LLViewerObjectList::dirtyAllObjectInventory()
+{
+ S32 count = mObjects.count();
+ for(S32 i = 0; i < count; ++i)
+ {
+ mObjects[i]->dirtyInventory();
+ }
+}
+
+void LLViewerObjectList::updateApparentAngles(LLAgent &agent)
+{
+ S32 i;
+ S32 num_objects = 0;
+ LLViewerObject *objectp;
+
+ S32 num_updates, max_value;
+ if (NUM_BINS - 1 == mCurBin)
+ {
+ num_updates = mObjects.count() - mCurLazyUpdateIndex;
+ max_value = mObjects.count();
+ gImageList.setUpdateStats(TRUE);
+ }
+ else
+ {
+ num_updates = (mObjects.count() / NUM_BINS) + 1;
+ max_value = llmin(mObjects.count(), mCurLazyUpdateIndex + num_updates);
+ }
+
+
+ if (!gNoRender)
+ {
+ // Slam priorities for textures that we care about (hovered, selected, and focused)
+ // Hovered
+ // Assumes only one level deep of parenting
+ objectp = gHoverView->getLastHoverObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+ }
+
+ // Focused
+ objectp = gAgent.getFocusObject();
+ if (objectp)
+ {
+ objectp->boostTexturePriority();
+ }
+
+ // Selected
+ for (objectp = gSelectMgr->getFirstRootObject(); objectp; objectp = gSelectMgr->getNextRootObject())
+ {
+ objectp->boostTexturePriority();
+ }
+
+
+ // Iterate through some of the objects and lazy update their texture priorities
+ for (i = mCurLazyUpdateIndex; i < max_value; i++)
+ {
+ objectp = mObjects[i];
+ if (!objectp->isDead())
+ {
+ num_objects++;
+
+ // Update distance & gpw
+ objectp->setPixelAreaAndAngle(agent); // Also sets the approx. pixel area
+ objectp->updateTextures(agent); // Update the image levels of textures for this object.
+ }
+ }
+
+ mCurLazyUpdateIndex = max_value;
+ if (mCurLazyUpdateIndex == mObjects.count())
+ {
+ mCurLazyUpdateIndex = 0;
+ }
+
+ mCurBin = (++mCurBin) % NUM_BINS;
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+}
+
+
+void LLViewerObjectList::update(LLAgent &agent, LLWorld &world)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ // Update globals
+ gVelocityInterpolate = gSavedSettings.getBOOL("VelocityInterpolate");
+ gPingInterpolate = gSavedSettings.getBOOL("PingInterpolate");
+ gAnimateTextures = gSavedSettings.getBOOL("AnimateTextures");
+
+ // update global timer
+ F32 last_time = gFrameTimeSeconds;
+ U64 time = totalTime(); // this will become the new gFrameTime when the update is done
+ // Time _can_ go backwards, for example if the user changes the system clock.
+ // It doesn't cause any fatal problems (just some oddness with stats), so we shouldn't assert here.
+// llassert(time > gFrameTime);
+ F64 time_diff = U64_to_F64(time - gFrameTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTime = time;
+ F64 time_since_start = U64_to_F64(gFrameTime - gStartTime)/(F64)SEC_TO_MICROSEC;
+ gFrameTimeSeconds = (F32)time_since_start;
+
+ gFrameIntervalSeconds = gFrameTimeSeconds - last_time;
+ if (gFrameIntervalSeconds < 0.f)
+ {
+ gFrameIntervalSeconds = 0.f;
+ }
+
+ //clear avatar LOD change counter
+ LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+ const F64 frame_time = LLFrameTimer::getElapsedSeconds();
+
+ std::vector<LLViewerObject*> kill_list;
+ S32 num_active_objects = 0;
+
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (objectp->getPCode() == LLViewerObject::LL_VO_CLOUDS ||
+ objectp->isAvatar())
+ {
+ objectp->idleUpdate(agent, world, frame_time);
+ }
+ }
+ }
+ else
+ {
+ for (std::set<LLPointer<LLViewerObject> >::iterator iter = mActiveObjects.begin();
+ iter != mActiveObjects.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ if (!objectp->idleUpdate(agent, world, frame_time))
+ {
+ // If Idle Update returns false, kill object!
+ kill_list.push_back(objectp);
+ }
+ else
+ {
+ num_active_objects++;
+ }
+ }
+ for (std::vector<LLViewerObject*>::iterator iter = kill_list.begin();
+ iter != kill_list.end(); iter++)
+ {
+ LLViewerObject *objectp = *iter;
+ killObject(objectp);
+ }
+ }
+
+ mNumSizeCulled = 0;
+ mNumVisCulled = 0;
+
+ // compute all sorts of time-based stats
+ // don't factor frames that were paused into the stats
+ if (! mWasPaused)
+ {
+ gViewerStats->updateFrameStats(time_diff);
+ }
+
+ /*
+ // Debugging code for viewing orphans, and orphaned parents
+ LLUUID id;
+ char id_str[UUID_STR_LENGTH + 20];
+ for (i = 0; i < mOrphanParents.count(); i++)
+ {
+ id = sIndexAndLocalIDToUUID[mOrphanParents[i]];
+ LLViewerObject *objectp = findObject(id);
+ if (objectp)
+ {
+ sprintf(id_str, "Par: ");
+ objectp->mID.toString(id_str + 5);
+ addDebugBeacon(objectp->getPositionAgent(),
+ id_str,
+ LLColor4(1.f,0.f,0.f,1.f),
+ LLColor4(1.f,1.f,1.f,1.f));
+ }
+ }
+
+ LLColor4 text_color;
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ OrphanInfo oi = mOrphanChildren[i];
+ LLViewerObject *objectp = findObject(oi.mChildInfo);
+ if (objectp)
+ {
+ if (objectp->getParent())
+ {
+ sprintf(id_str, "ChP: ");
+ text_color = LLColor4(0.f, 1.f, 0.f, 1.f);
+ }
+ else
+ {
+ sprintf(id_str, "ChNoP: ");
+ text_color = LLColor4(1.f, 0.f, 0.f, 1.f);
+ }
+ id = sIndexAndLocalIDToUUID[oi.mParentInfo];
+ objectp->mID.toString(id_str + 8);
+ addDebugBeacon(objectp->getPositionAgent() + LLVector3(0.f, 0.f, -0.25f),
+ id_str,
+ LLColor4(0.25f,0.25f,0.25f,1.f),
+ text_color);
+ }
+ i++;
+ }
+ */
+
+ mNumObjectsStat.addValue(mObjects.count());
+ mNumActiveObjectsStat.addValue(num_active_objects);
+ mNumSizeCulledStat.addValue(mNumSizeCulled);
+ mNumVisCulledStat.addValue(mNumVisCulled);
+}
+
+void LLViewerObjectList::cleanupReferences(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (mDeadObjects.count(objectp->mID))
+ {
+ llinfos << "Object " << objectp->mID << " already on dead list, ignoring cleanup!" << llendl;
+ return;
+ }
+
+ mDeadObjects.insert(std::pair<LLUUID, LLPointer<LLViewerObject> >(objectp->mID, objectp));
+
+ // Cleanup any references we have to this object
+ // Remove from object map so noone can look it up.
+
+ mUUIDObjectMap.erase(objectp->mID);
+ removeFromLocalIDTable(*objectp);
+
+ if (objectp->onActiveList())
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list in cleanupReferences." << llendl;
+ objectp->setOnActiveList(FALSE);
+ mActiveObjects.erase(objectp);
+ }
+
+ if (objectp->isOnMap())
+ {
+ mMapObjects.removeObj(objectp);
+ }
+
+ // Don't clean up mObject references, these will be cleaned up more efficiently later!
+ // Also, not cleaned up
+ removeDrawable(objectp->mDrawable);
+
+ mNumDeadObjects++;
+}
+
+void LLViewerObjectList::removeDrawable(LLDrawable* drawablep)
+{
+ if (!drawablep)
+ {
+ return;
+ }
+
+ for (S32 i = 0; i < drawablep->getNumFaces(); i++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(i)->getViewerObject();
+ mSelectPickList.erase(objectp);
+ }
+}
+
+BOOL LLViewerObjectList::killObject(LLViewerObject *objectp)
+{
+ // When we're killing objects, all we do is mark them as dead.
+ // We clean up the dead objects later.
+
+ if (objectp)
+ {
+ if (objectp->isDead())
+ {
+ // This object is already dead. Don't need to do more.
+ return TRUE;
+ }
+ else
+ {
+ objectp->markDead();
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerObjectList::killObjects(LLViewerRegion *regionp)
+{
+ LLViewerObject *objectp;
+
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ if (objectp->mRegionp == regionp)
+ {
+ killObject(objectp);
+ }
+ }
+
+ // Have to clean right away because the region is becoming invalid.
+ cleanDeadObjects(FALSE);
+}
+
+void LLViewerObjectList::killAllObjects()
+{
+ // Used only on global destruction.
+ LLViewerObject *objectp;
+
+ for (S32 i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+
+ killObject(objectp);
+ llassert(objectp->isDead());
+ }
+
+ cleanDeadObjects(FALSE);
+
+ if(!mObjects.empty())
+ {
+ llwarns << "LLViewerObjectList::killAllObjects still has entries in mObjects: " << mObjects.count() << llendl;
+ mObjects.clear();
+ }
+
+ if (!mActiveObjects.empty())
+ {
+ llwarns << "Some objects still on active object list!" << llendl;
+ mActiveObjects.clear();
+ }
+
+ if (!mMapObjects.empty())
+ {
+ llwarns << "Some objects still on map object list!" << llendl;
+ mActiveObjects.clear();
+ }
+}
+
+void LLViewerObjectList::cleanDeadObjects(BOOL use_timer)
+{
+ if (!mNumDeadObjects)
+ {
+ // No dead objects, don't need to scan object list.
+ return;
+ }
+
+ S32 i = 0;
+ S32 num_removed = 0;
+ LLViewerObject *objectp;
+ while (i < mObjects.count())
+ {
+ // Scan for all of the dead objects and remove any "global" references to them.
+ objectp = mObjects[i];
+ if (objectp->isDead())
+ {
+ mObjects.remove(i);
+ num_removed++;
+
+ if (num_removed == mNumDeadObjects)
+ {
+ // We've cleaned up all of the dead objects.
+ break;
+ }
+ }
+ else
+ {
+ // iterate, this isn't a dead object.
+ i++;
+ }
+ }
+
+ // We've cleaned the global object list, now let's do some paranoia testing on objects
+ // before blowing away the dead list.
+ mDeadObjects.clear();
+ mNumDeadObjects = 0;
+}
+
+void LLViewerObjectList::updateActive(LLViewerObject *objectp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ if (objectp->isDead())
+ {
+ return; // We don't update dead objects!
+ }
+
+ BOOL active = objectp->isActive();
+ if (active != objectp->onActiveList())
+ {
+ if (active)
+ {
+ //llinfos << "Adding " << objectp->mID << " " << objectp->getPCodeString() << " to active list." << llendl;
+ mActiveObjects.insert(objectp);
+ objectp->setOnActiveList(TRUE);
+ }
+ else
+ {
+ //llinfos << "Removing " << objectp->mID << " " << objectp->getPCodeString() << " from active list." << llendl;
+ mActiveObjects.erase(objectp);
+ objectp->setOnActiveList(FALSE);
+ }
+ }
+}
+
+
+
+void LLViewerObjectList::shiftObjects(const LLVector3 &offset)
+{
+ // This is called when we shift our origin when we cross region boundaries...
+ // We need to update many object caches, I'll document this more as I dig through the code
+ // cleaning things out...
+
+ if (gNoRender || 0 == offset.magVecSquared())
+ {
+ return;
+ }
+
+ LLViewerObject *objectp;
+ S32 i;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = getObject(i);
+ // There could be dead objects on the object list, so don't update stuff if the object is dead.
+ if (objectp)
+ {
+ objectp->updatePositionCaches();
+
+ if (objectp->mDrawable.notNull() && !objectp->mDrawable->isDead())
+ {
+ gPipeline.markShift(objectp->mDrawable);
+ }
+ }
+ }
+
+ gPipeline.shiftObjects(offset);
+ gWorldPointer->mPartSim.shift(offset);
+}
+
+void LLViewerObjectList::renderObjectsForMap(LLNetMap &netmap)
+{
+ LLColor4 above_water_color = gColors.getColor( "NetMapOtherOwnAboveWater" );
+ LLColor4 below_water_color = gColors.getColor( "NetMapOtherOwnBelowWater" );
+ LLColor4 you_own_above_water_color =
+ gColors.getColor( "NetMapYouOwnAboveWater" );
+ LLColor4 you_own_below_water_color =
+ gColors.getColor( "NetMapYouOwnBelowWater" );
+ LLColor4 group_own_above_water_color =
+ gColors.getColor( "NetMapGroupOwnAboveWater" );
+ LLColor4 group_own_below_water_color =
+ gColors.getColor( "NetMapGroupOwnBelowWater" );
+
+
+ for (S32 i = 0; i < mMapObjects.count(); i++)
+ {
+ LLViewerObject* objectp = mMapObjects[i];
+ if (objectp->isOrphaned() || objectp->isAttachment())
+ {
+ continue;
+ }
+ const LLVector3& scale = objectp->getScale();
+ const LLVector3d pos = objectp->getPositionGlobal();
+ const F64 water_height = F64( objectp->getRegion()->getWaterHeight() );
+ // gWorldPointer->getWaterHeight();
+
+ F32 approx_radius = (scale.mV[VX] + scale.mV[VY]) * 0.5f * 0.5f * 1.3f; // 1.3 is a fudge
+
+ LLColor4U color = above_water_color;
+ if( objectp->permYouOwner() )
+ {
+ const F32 MIN_RADIUS_FOR_OWNED_OBJECTS = 2.f;
+ if( approx_radius < MIN_RADIUS_FOR_OWNED_OBJECTS )
+ {
+ approx_radius = MIN_RADIUS_FOR_OWNED_OBJECTS;
+ }
+
+ if( pos.mdV[VZ] >= water_height )
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_above_water_color;
+ }
+ else
+ {
+ color = you_own_above_water_color;
+ }
+ }
+ else
+ {
+ if ( objectp->permGroupOwner() )
+ {
+ color = group_own_below_water_color;
+ }
+ else
+ {
+ color = you_own_below_water_color;
+ }
+ }
+ }
+ else
+ if( pos.mdV[VZ] < water_height )
+ {
+ color = below_water_color;
+ }
+
+ netmap.renderScaledPointGlobal(
+ pos,
+ color,
+ approx_radius );
+ }
+}
+
+void LLViewerObjectList::renderObjectBounds(const LLVector3 &center)
+{
+}
+
+
+U32 LLViewerObjectList::renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall, BOOL keep_pick_list)
+{
+ gRenderForSelect = TRUE;
+
+ // LLTimer pick_timer;
+ if (!keep_pick_list)
+ {
+ LLViewerObject *objectp;
+ S32 i;
+ // Reset all of the GL names to zero.
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ objectp->mGLName = 0;
+ }
+
+ mSelectPickList.clear();
+
+ std::vector<LLDrawable*> pick_drawables;
+ gPipeline.mObjectPartition->cull(camera, &pick_drawables, TRUE);
+
+ for (std::vector<LLDrawable*>::iterator iter = pick_drawables.begin();
+ iter != pick_drawables.end(); iter++)
+ {
+ LLDrawable* drawablep = *iter;
+
+ LLViewerObject* last_objectp = NULL;
+ for (S32 face_num = 0; face_num < drawablep->getNumFaces(); face_num++)
+ {
+ LLViewerObject* objectp = drawablep->getFace(face_num)->getViewerObject();
+
+ if (objectp && objectp != last_objectp)
+ {
+ mSelectPickList.insert(objectp);
+ last_objectp = objectp;
+ }
+ }
+ }
+
+ LLHUDText::addPickable(mSelectPickList);
+
+ for (objectp = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ objectp;
+ objectp = (LLVOAvatar*)LLCharacter::sInstances.getNextData())
+ {
+ if (!objectp->isDead())
+ {
+ if (objectp->mDrawable.notNull() && objectp->mDrawable->isVisible())
+ {
+ mSelectPickList.insert(objectp);
+ }
+ }
+ }
+
+ // add all hud objects to pick list
+ LLVOAvatar* avatarp = gAgent.getAvatarObject();
+ if (avatarp)
+ {
+ LLViewerJointAttachment* attachmentp;
+ for (attachmentp = avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getIsHUDAttachment())
+ {
+ LLViewerObject* objectp = attachmentp->getObject(0);
+ if (objectp)
+ {
+ mSelectPickList.insert(objectp);
+ for (U32 i = 0; i < objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = objectp->mChildList[i];
+ if (childp)
+ {
+ mSelectPickList.insert(childp);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ S32 num_pickables = (S32)mSelectPickList.size() + LLHUDIcon::getNumInstances();
+
+ S32 step = (0x000fffff - GL_NAME_INDEX_OFFSET) / num_pickables;
+
+ std::set<LLViewerObject*>::iterator pick_it;
+ i = 0;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end();)
+ {
+ LLViewerObject* objp = (*pick_it);
+ if (!objp || objp->isDead() || !objp->mbCanSelect)
+ {
+ mSelectPickList.erase(pick_it++);
+ continue;
+ }
+
+ objp->mGLName = (i * step) + GL_NAME_INDEX_OFFSET;
+ i++;
+ ++pick_it;
+ }
+
+ LLHUDIcon::generatePickIDs(i * step, step);
+ }
+
+ // At this point, we should only have live drawables/viewer objects
+ gPipeline.renderForSelect();
+ //
+ // Render pass for selected objects
+ //
+ gViewerWindow->renderSelections( TRUE, pick_parcel_wall, FALSE );
+
+ // render pickable ui elements, like names, etc.
+ LLHUDObject::renderAllForSelect();
+
+ gRenderForSelect = FALSE;
+
+ //llinfos << "Rendered " << count << " for select" << llendl;
+ //llinfos << "Took " << pick_timer.getElapsedTimeF32()*1000.f << "ms to pick" << llendl;
+ return 0;
+}
+
+LLViewerObject *LLViewerObjectList::getSelectedObject(const U32 object_id)
+{
+ std::set<LLViewerObject*>::iterator pick_it;
+ for (pick_it = mSelectPickList.begin(); pick_it != mSelectPickList.end(); ++pick_it)
+ {
+ if ((*pick_it)->mGLName == object_id)
+ {
+ return (*pick_it);
+ }
+ }
+ return NULL;
+}
+
+void LLViewerObjectList::addDebugBeacon(const LLVector3 &pos_agent,
+ const LLString &string,
+ const LLColor4 &color,
+ const LLColor4 &text_color,
+ S32 line_width)
+{
+ LLDebugBeacon *beaconp = mDebugBeacons.reserve_block(1);
+ beaconp->mPositionAgent = pos_agent;
+ beaconp->mString = string;
+ beaconp->mColor = color;
+ beaconp->mTextColor = text_color;
+ beaconp->mLineWidth = line_width;
+}
+
+void LLViewerObjectList::resetObjectBeacons()
+{
+ mDebugBeacons.reset();
+}
+
+LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLUUID fullid;
+ fullid.generate();
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+
+
+LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRegion *regionp,
+ const LLUUID &uuid, const U32 local_id, const LLHost &sender)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+ LLFastTimer t(LLFastTimer::FTM_CREATE_OBJECT);
+
+ LLUUID fullid;
+ if (uuid == LLUUID::null)
+ {
+ fullid.generate();
+ }
+ else
+ {
+ fullid = uuid;
+ }
+
+ LLViewerObject *objectp = LLViewerObject::createObject(fullid, pcode, regionp);
+ if (!objectp)
+ {
+ llwarns << "Couldn't create object of type " << LLPrimitive::pCodeToString(pcode) << " id:" << fullid << llendl;
+ return NULL;
+ }
+
+ mUUIDObjectMap[fullid] = objectp;
+ setUUIDAndLocal(fullid,
+ local_id,
+ gMessageSystem->getSenderIP(),
+ gMessageSystem->getSenderPort());
+
+ mObjects.put(objectp);
+
+ updateActive(objectp);
+
+ return objectp;
+}
+
+LLViewerObject *LLViewerObjectList::replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+{
+ LLViewerObject *old_instance = findObject(id);
+ if (old_instance)
+ {
+ cleanupReferences(old_instance);
+ old_instance->markDead();
+
+ return createObject(pcode, regionp, id, old_instance->getLocalID(), LLHost());
+ }
+ return NULL;
+}
+
+S32 LLViewerObjectList::findReferences(LLDrawable *drawablep) const
+{
+ LLViewerObject *objectp;
+ S32 i;
+ S32 num_refs = 0;
+ for (i = 0; i < mObjects.count(); i++)
+ {
+ objectp = mObjects[i];
+ if (objectp->mDrawable.notNull())
+ {
+ num_refs += objectp->mDrawable->findReferences(drawablep);
+ }
+ }
+ return num_refs;
+}
+
+
+void LLViewerObjectList::orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port)
+{
+ LLMemType mt(LLMemType::MTYPE_OBJECT);
+#ifdef ORPHAN_SPAM
+ llinfos << "Orphaning object " << childp->getID() << " with parent " << parent_id << llendl;
+#endif
+
+ // We're an orphan, flag things appropriately.
+ childp->mOrphaned = TRUE;
+ if (childp->mDrawable.notNull())
+ {
+ bool make_invisible = true;
+ LLViewerObject *parentp = (LLViewerObject *)childp->getParent();
+ if (parentp)
+ {
+ if (parentp->getRegion() != childp->getRegion())
+ {
+ // This is probably an object flying across a region boundary, the
+ // object probably ISN'T being reparented, but just got an object
+ // update out of order (child update before parent).
+ make_invisible = false;
+ //llinfos << "Don't make object handoffs invisible!" << llendl;
+ }
+ }
+
+ if (make_invisible)
+ {
+ // Make sure that this object becomes invisible if it's an orphan
+ childp->mDrawable->setState(LLDrawable::FORCE_INVISIBLE);
+ }
+ }
+
+ // Unknown parent, add to orpaned child list
+ U64 parent_info = getIndex(parent_id, ip, port);
+
+ if (-1 == mOrphanParents.find(parent_info))
+ {
+ mOrphanParents.put(parent_info);
+ }
+
+ LLViewerObjectList::OrphanInfo oi(parent_info, childp->mID);
+ if (-1 == mOrphanChildren.find(oi))
+ {
+ mOrphanChildren.put(oi);
+ mNumOrphans++;
+ }
+}
+
+
+void LLViewerObjectList::findOrphans(LLViewerObject* objectp, U32 ip, U32 port)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if (objectp->isDead())
+ {
+ llwarns << "Trying to find orphans for dead obj " << objectp->mID
+ << ":" << objectp->getPCodeString() << llendl;
+ return;
+ }
+
+ // See if we are a parent of an orphan.
+ // Note: This code is fairly inefficient but it should happen very rarely.
+ // It can be sped up if this is somehow a performance issue...
+ if (0 == mOrphanParents.count())
+ {
+ // no known orphan parents
+ return;
+ }
+ if (-1 == mOrphanParents.find(getIndex(objectp->mLocalID, ip, port)))
+ {
+ // did not find objectp in OrphanParent list
+ return;
+ }
+
+ S32 i;
+ U64 parent_info = getIndex(objectp->mLocalID, ip, port);
+ BOOL orphans_found = FALSE;
+ // Iterate through the orphan list, and set parents of matching children.
+ for (i = 0; i < mOrphanChildren.count(); i++)
+ {
+ if (mOrphanChildren[i].mParentInfo != parent_info)
+ {
+ continue;
+ }
+ LLViewerObject *childp = findObject(mOrphanChildren[i].mChildInfo);
+ if (childp)
+ {
+ if (childp == objectp)
+ {
+ llwarns << objectp->mID << " has self as parent, skipping!"
+ << llendl;
+ continue;
+ }
+
+#ifdef ORPHAN_SPAM
+ llinfos << "Reunited parent " << objectp->mID
+ << " with child " << childp->mID << llendl;
+ llinfos << "Glob: " << objectp->getPositionGlobal() << llendl;
+ llinfos << "Agent: " << objectp->getPositionAgent() << llendl;
+ addDebugBeacon(objectp->getPositionAgent(),"");
+#endif
+ gPipeline.markMoved(objectp->mDrawable);
+ objectp->setChanged(LLXform::MOVED | LLXform::SILHOUETTE);
+
+ // Flag the object as no longer orphaned
+ childp->mOrphaned = FALSE;
+ if (childp->mDrawable.notNull())
+ {
+ // Make the drawable visible again and set the drawable parent
+ childp->mDrawable->setState(LLDrawable::CLEAR_INVISIBLE);
+ childp->setDrawableParent(objectp->mDrawable); // LLViewerObjectList::findOrphans()
+ }
+ objectp->addChild(childp);
+ orphans_found = TRUE;
+ }
+ else
+ {
+ llinfos << "Missing orphan child, removing from list" << llendl;
+ mOrphanChildren.remove(i);
+ i--;
+ }
+ }
+
+ // Remove orphan parent and children from lists now that they've been found
+ mOrphanParents.remove(mOrphanParents.find(parent_info));
+
+ i = 0;
+ while (i < mOrphanChildren.count())
+ {
+ if (mOrphanChildren[i].mParentInfo == parent_info)
+ {
+ mOrphanChildren.remove(i);
+ mNumOrphans--;
+ }
+ else
+ {
+ i++;
+ }
+ }
+
+ if (orphans_found && objectp->isSelected())
+ {
+ LLSelectNode* nodep = gSelectMgr->findSelectNode(objectp);
+ if (nodep && !nodep->mIndividualSelection)
+ {
+ // rebuild selection with orphans
+ gSelectMgr->deselectObjectAndFamily(objectp);
+ gSelectMgr->selectObjectAndFamily(objectp);
+ }
+ }
+}
+
+
+LLViewerObjectList::OrphanInfo::OrphanInfo()
+{
+}
+
+LLViewerObjectList::OrphanInfo::OrphanInfo(const U64 parent_info, const LLUUID child_info)
+ : mParentInfo(parent_info), mChildInfo(child_info)
+{
+}
+
+bool LLViewerObjectList::OrphanInfo::operator==(const OrphanInfo &rhs) const
+{
+ return (mParentInfo == rhs.mParentInfo) && (mChildInfo == rhs.mChildInfo);
+}
+
+bool LLViewerObjectList::OrphanInfo::operator!=(const OrphanInfo &rhs) const
+{
+ return !operator==(rhs);
+}
+
diff --git a/indra/newview/llviewerobjectlist.h b/indra/newview/llviewerobjectlist.h
new file mode 100644
index 0000000000..a2893c4b7d
--- /dev/null
+++ b/indra/newview/llviewerobjectlist.h
@@ -0,0 +1,256 @@
+/**
+ * @file llviewerobjectlist.h
+ * @brief Description of LLViewerObjectList class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWEROBJECTLIST_H
+#define LL_LLVIEWEROBJECTLIST_H
+
+#include <map>
+#include <set>
+
+// common includes
+#include "doublelinkedlist.h"
+#include "llstat.h"
+#include "lldarrayptr.h"
+#include "llskipmap.h"
+#include "llstring.h"
+
+// project includes
+#include "llviewerobject.h"
+
+class LLNetMap;
+class LLDebugBeacon;
+
+const U32 CLOSE_BIN_SIZE = 10;
+const U32 NUM_BINS = 16;
+
+// GL name = position in object list + GL_NAME_INDEX_OFFSET so that
+// we can have special numbers like zero.
+const U32 GL_NAME_LAND = 0;
+const U32 GL_NAME_PARCEL_WALL = 1;
+
+const U32 GL_NAME_INDEX_OFFSET = 10;
+
+class LLViewerObjectList
+{
+public:
+ LLViewerObjectList();
+ ~LLViewerObjectList();
+
+ void destroy();
+
+ // For internal use only. Does NOT take a local id, takes an index into
+ // an internal dynamic array.
+ inline LLViewerObject *getObject(const S32 index);
+
+ inline LLViewerObject *findObject(const LLUUID &id);
+ LLViewerObject *createObjectViewer(const LLPCode pcode, LLViewerRegion *regionp); // Create a viewer-side object
+ LLViewerObject *createObject(const LLPCode pcode, LLViewerRegion *regionp,
+ const LLUUID &uuid, const U32 local_id, const LLHost &sender);
+
+ LLViewerObject *replaceObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp); // TomY: hack to switch VO instances on the fly
+
+ BOOL killObject(LLViewerObject *objectp);
+ void killObjects(LLViewerRegion *regionp); // Kill all objects owned by a particular region.
+ void killAllObjects();
+ void removeDrawable(LLDrawable* drawablep);
+
+ void cleanDeadObjects(const BOOL use_timer = TRUE); // Clean up the dead object list.
+
+ // Simulator and viewer side object updates...
+ void processUpdateCore(LLViewerObject* objectp, void** data, U32 block, const EObjectUpdateType update_type, LLDataPacker* dpp, BOOL justCreated);
+ void processObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type, bool cached=false, bool compressed=false);
+ void processCompressedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
+ void processCachedObjectUpdate(LLMessageSystem *mesgsys, void **user_data, EObjectUpdateType update_type);
+ void updateApparentAngles(LLAgent &agent);
+ void update(LLAgent &agent, LLWorld &world);
+
+ void shiftObjects(const LLVector3 &offset);
+
+ void renderObjectsForMap(LLNetMap &netmap);
+ void renderObjectBounds(const LLVector3 &center);
+
+ void addDebugBeacon(const LLVector3 &pos_agent, const LLString &string,
+ const LLColor4 &color=LLColor4(1.f, 0.f, 0.f, 0.5f),
+ const LLColor4 &text_color=LLColor4(1.f, 1.f, 1.f, 1.f),
+ S32 line_width = 1);
+ void renderObjectBeacons();
+ void resetObjectBeacons();
+
+ void relightAllObjects();
+ void dirtyAllObjectInventory();
+
+ void updateActive(LLViewerObject *objectp);
+ void updateAvatarVisibility();
+
+ // Selection related stuff
+ U32 renderObjectsForSelect(LLCamera &camera, BOOL pick_parcel_wall = FALSE, BOOL keep_pick_list = FALSE);
+ LLViewerObject *getSelectedObject(const U32 object_id);
+
+ inline S32 getNumObjects() { return mObjects.count(); }
+
+ void addToMap(LLViewerObject *objectp);
+ void removeFromMap(LLViewerObject *objectp);
+
+
+ ////////////////////////////////////////////
+ //
+ // Only accessed by markDead in LLViewerObject
+ void cleanupReferences(LLViewerObject *objectp);
+
+ S32 findReferences(LLDrawable *drawablep) const; // Find references to drawable in all objects, and return value.
+
+ S32 getOrphanParentCount() const { return mOrphanParents.count(); }
+ S32 getOrphanCount() const { return mNumOrphans; }
+ void orphanize(LLViewerObject *childp, U32 parent_id, U32 ip, U32 port);
+ void findOrphans(LLViewerObject* objectp, U32 ip, U32 port);
+
+public:
+ // Class for keeping track of orphaned objects
+ class OrphanInfo
+ {
+ public:
+ OrphanInfo();
+ OrphanInfo(const U64 parent_info, const LLUUID child_info);
+ bool operator==(const OrphanInfo &rhs) const;
+ bool operator!=(const OrphanInfo &rhs) const;
+ U64 mParentInfo;
+ LLUUID mChildInfo;
+ };
+
+
+ U32 mCurBin; // Current bin we're working on...
+
+ //////////////////////
+ //
+ // Statistics data
+ //
+ //
+ LLStat mNumObjectsStat;
+ LLStat mNumActiveObjectsStat;
+ LLStat mNumNewObjectsStat;
+ LLStat mNumSizeCulledStat;
+ LLStat mNumVisCulledStat;
+
+ S32 mNumNewObjects;
+
+ S32 mNumSizeCulled;
+ S32 mNumVisCulled;
+
+ // if we paused in the last frame
+ // used to discount stats from this frame
+ BOOL mWasPaused;
+
+ static void getUUIDFromLocal(LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port);
+ static void setUUIDAndLocal(const LLUUID &id,
+ const U32 local_id,
+ const U32 ip,
+ const U32 port); // Requires knowledge of message system info!
+
+ static BOOL removeFromLocalIDTable(const LLViewerObject &object);
+ // Used ONLY by the orphaned object code.
+ static U64 getIndex(const U32 local_id, const U32 ip, const U32 port);
+
+ S32 mNumUnknownUpdates;
+ S32 mNumDeadObjectUpdates;
+ S32 mNumUnknownKills;
+ S32 mNumDeadObjects;
+protected:
+ LLDynamicArray<U64> mOrphanParents; // LocalID/ip,port of orphaned objects
+ LLDynamicArray<OrphanInfo> mOrphanChildren; // UUID's of orphaned objects
+ S32 mNumOrphans;
+
+ LLDynamicArrayPtr<LLPointer<LLViewerObject>, 256> mObjects;
+ std::set<LLPointer<LLViewerObject> > mActiveObjects;
+
+ LLDynamicArrayPtr<LLPointer<LLViewerObject> > mMapObjects;
+
+ typedef std::map<LLUUID, LLPointer<LLViewerObject> > vo_map;
+ vo_map mDeadObjects; // Need to keep multiple entries per UUID
+
+ std::map<LLUUID, LLPointer<LLViewerObject> > mUUIDObjectMap;
+
+ LLDynamicArray<LLDebugBeacon> mDebugBeacons;
+
+ S32 mCurLazyUpdateIndex;
+
+ static U32 sSimulatorMachineIndex;
+ static LLMap<U64, U32> sIPAndPortToIndex;
+
+ static std::map<U64, LLUUID> sIndexAndLocalIDToUUID;
+
+ std::set<LLViewerObject *> mSelectPickList;
+
+ friend class LLViewerObject;
+};
+
+
+class LLDebugBeacon
+{
+public:
+ ~LLDebugBeacon()
+ {
+ if (mHUDObject.notNull())
+ {
+ mHUDObject->markDead();
+ }
+ }
+
+ LLVector3 mPositionAgent;
+ LLString mString;
+ LLColor4 mColor;
+ LLColor4 mTextColor;
+ S32 mLineWidth;
+ LLPointer<LLHUDObject> mHUDObject;
+};
+
+
+
+// Global object list
+extern LLViewerObjectList gObjectList;
+
+// Inlines
+inline LLViewerObject *LLViewerObjectList::findObject(const LLUUID &id)
+{
+ std::map<LLUUID, LLPointer<LLViewerObject> >::iterator iter = mUUIDObjectMap.find(id);
+ if(iter != mUUIDObjectMap.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+inline LLViewerObject *LLViewerObjectList::getObject(const S32 index)
+{
+ LLViewerObject *objectp;
+ objectp = mObjects[index];
+ if (objectp->isDead())
+ {
+ //llwarns << "Dead object " << objectp->mID << " in getObject" << llendl;
+ return NULL;
+ }
+ return objectp;
+}
+
+inline void LLViewerObjectList::addToMap(LLViewerObject *objectp)
+{
+ mMapObjects.put(objectp);
+}
+
+inline void LLViewerObjectList::removeFromMap(LLViewerObject *objectp)
+{
+ mMapObjects.removeObj(objectp);
+}
+
+
+#endif // LL_VIEWER_OBJECT_LIST_H
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
new file mode 100644
index 0000000000..2b3c364454
--- /dev/null
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -0,0 +1,2568 @@
+/**
+ * @file llviewerparcelmgr.cpp
+ * @brief Viewer-side representation of owned land
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerparcelmgr.h"
+
+// Library includes
+#include "audioengine.h"
+#include "indra_constants.h"
+#include "llcachename.h"
+#include "llgl.h"
+#include "llmediaengine.h"
+#include "llparcel.h"
+#include "llsecondlifeurls.h"
+#include "message.h"
+
+// Viewer includes
+#include "llagent.h"
+#include "llfloatergroupinfo.h"
+#include "llviewerwindow.h"
+#include "llviewercontrol.h"
+#include "llfirstuse.h"
+#include "llfloaterbuyland.h"
+#include "llfloatergroups.h"
+#include "llfloaterhtml.h"
+#include "llfloatersellland.h"
+#include "llfloatertools.h"
+#include "llnotify.h"
+#include "llresmgr.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+//#include "llwebbrowserctrl.h"
+#include "llworld.h"
+#include "lloverlaybar.h"
+#include "roles_constants.h"
+
+const F32 PARCEL_COLLISION_DRAW_SECS = 1.f;
+
+// Globals
+LLViewerParcelMgr *gParcelMgr = NULL;
+
+U8* LLViewerParcelMgr::sPackedOverlay = NULL;
+
+LLUUID gCurrentMovieID = LLUUID::null;
+
+// Local functions
+void optionally_start_music(const LLString& music_url);
+void callback_start_music(S32 option, void* data);
+void optionally_prepare_video(const LLParcel *parcelp);
+void callback_prepare_video(S32 option, void* data);
+void prepare_video(const LLParcel *parcelp);
+void start_video(const LLParcel *parcelp);
+void stop_video();
+void callback_god_force_owner(S32 option, void* user_data);
+
+struct LLGodForceOwnerData
+{
+ LLUUID mOwnerID;
+ S32 mLocalID;
+ LLHost mHost;
+
+ LLGodForceOwnerData(
+ const LLUUID& owner_id,
+ S32 local_parcel_id,
+ const LLHost& host) :
+ mOwnerID(owner_id),
+ mLocalID(local_parcel_id),
+ mHost(host) {}
+};
+
+//
+// Methods
+//
+LLViewerParcelMgr::LLViewerParcelMgr()
+: mSelected(FALSE),
+ mSelectedMultipleOwners(FALSE),
+ mWholeParcelSelected(FALSE),
+ mWestSouth(),
+ mEastNorth(),
+ mSelectedDwell(0.f),
+ mAgentParcelSequenceID(-1),
+ mHoverWestSouth(),
+ mHoverEastNorth(),
+ mRenderCollision(FALSE),
+ mRenderSelection(TRUE),
+ mCollisionBanned(0),
+ mCollisionTimer(),
+ mMediaParcelId(0),
+ mMediaRegionId(0)
+{
+ mParcel = new LLParcel();
+ mAgentParcel = new LLParcel();
+ mHoverParcel = new LLParcel();
+ mCollisionParcel = new LLParcel();
+
+ mParcelsPerEdge = S32( REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
+ mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
+ resetSegments(mHighlightSegments);
+
+ mCollisionSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
+ resetSegments(mCollisionSegments);
+
+ mBlockedImageID.set(gViewerArt.getString("noentrylines.tga"));
+ mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE);
+
+ mPassImageID.set(gViewerArt.getString("noentrypasslines.tga"));
+ mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE);
+
+ S32 overlay_size = mParcelsPerEdge * mParcelsPerEdge / PARCEL_OVERLAY_CHUNKS;
+ sPackedOverlay = new U8[overlay_size];
+
+ mAgentParcelOverlay = new U8[mParcelsPerEdge * mParcelsPerEdge];
+ S32 i;
+ for (i = 0; i < mParcelsPerEdge * mParcelsPerEdge; i++)
+ {
+ mAgentParcelOverlay[i] = 0;
+ }
+}
+
+
+LLViewerParcelMgr::~LLViewerParcelMgr()
+{
+ delete mParcel;
+ mParcel = NULL;
+
+ delete mAgentParcel;
+ mAgentParcel = NULL;
+
+ delete mCollisionParcel;
+ mCollisionParcel = NULL;
+
+ delete mHoverParcel;
+ mHoverParcel = NULL;
+
+ delete[] mHighlightSegments;
+ mHighlightSegments = NULL;
+
+ delete[] mCollisionSegments;
+ mCollisionSegments = NULL;
+
+ // weird, this crashes if I use an array delete on it!
+ delete sPackedOverlay;
+ sPackedOverlay = NULL;
+
+ delete[] mAgentParcelOverlay;
+ mAgentParcelOverlay = NULL;
+}
+
+
+void LLViewerParcelMgr::destroyGL()
+{
+ mBlockedImage = NULL;
+ mPassImage = NULL;
+}
+
+
+void LLViewerParcelMgr::restoreGL()
+{
+ mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE);
+ mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE);
+}
+
+
+void LLViewerParcelMgr::dump()
+{
+ llinfos << "Parcel Manager Dump" << llendl;
+ llinfos << "mSelected " << S32(mSelected) << llendl;
+ llinfos << "Selected parcel: " << llendl;
+ llinfos << mWestSouth << " to " << mEastNorth << llendl;
+ mParcel->dump();
+ llinfos << "banning " << mParcel->mBanList.size() << llendl;
+
+ access_map_const_iterator cit = mParcel->mBanList.begin();
+ access_map_const_iterator end = mParcel->mBanList.end();
+ for ( ; cit != end; ++cit)
+ {
+ llinfos << "ban id " << (*cit).first << llendl;
+ }
+ llinfos << "Hover parcel:" << llendl;
+ mHoverParcel->dump();
+ llinfos << "Agent parcel:" << llendl;
+ mAgentParcel->dump();
+}
+
+
+LLViewerRegion* LLViewerParcelMgr::getSelectionRegion()
+{
+ if (!gWorldp) return NULL;
+
+ return gWorldp->getRegionFromPosGlobal( mWestSouth );
+}
+
+
+void LLViewerParcelMgr::getDisplayInfo(S32* area_out, S32* claim_out,
+ S32* rent_out,
+ BOOL* for_sale_out,
+ F32* dwell_out)
+{
+ S32 area = 0;
+ S32 price = 0;
+ S32 rent = 0;
+ BOOL for_sale = FALSE;
+ F32 dwell = 0.f;
+
+ if (mSelected)
+ {
+ if (mSelectedMultipleOwners)
+ {
+ area = getClaimableArea();
+ }
+ else
+ {
+ area = getSelectedArea();
+ }
+
+ if (mParcel->getForSale())
+ {
+ price = mParcel->getSalePrice();
+ for_sale = TRUE;
+ }
+ else
+ {
+ price = area * mParcel->getClaimPricePerMeter();
+ for_sale = FALSE;
+ }
+
+ rent = mParcel->getTotalRent();
+
+ dwell = mSelectedDwell;
+ }
+
+ *area_out = area;
+ *claim_out = price;
+ *rent_out = rent;
+ *for_sale_out = for_sale;
+ *dwell_out = dwell;
+}
+
+void LLViewerParcelMgr::getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean)
+{
+ if (mSelected && mParcel)
+ {
+ sw_max = mParcel->getSimWideMaxPrimCapacity();
+ sw_total = mParcel->getSimWidePrimCount();
+ max = llround(mParcel->getMaxPrimCapacity()*mParcel->getParcelPrimBonus());
+ total = mParcel->getPrimCount();
+ owner = mParcel->getOwnerPrimCount();
+ group = mParcel->getGroupPrimCount();
+ other = mParcel->getOtherPrimCount();
+ selected = mParcel->getSelectedPrimCount();
+ parcel_object_bonus = mParcel->getParcelPrimBonus();
+ other_clean = mParcel->getCleanOtherTime();
+ }
+}
+
+BOOL LLViewerParcelMgr::getMultipleOwners() const
+{
+ return mSelectedMultipleOwners;
+}
+
+
+BOOL LLViewerParcelMgr::getWholeParcelSelected() const
+{
+ return mWholeParcelSelected;
+}
+
+
+S32 LLViewerParcelMgr::getClaimableArea() const
+{
+ const S32 UNIT_AREA = S32( PARCEL_GRID_STEP_METERS * PARCEL_GRID_STEP_METERS );
+ return mSelectedPublicCount * UNIT_AREA;
+}
+
+bool LLViewerParcelMgr::hasOthersSelected() const
+{
+ return mSelectedOtherCount != 0;
+}
+
+
+S32 LLViewerParcelMgr::getSelectedArea() const
+{
+ S32 rv = 0;
+ if(mSelected && mParcel && mWholeParcelSelected)
+ {
+ rv = mParcel->getArea();
+ }
+ else if(mSelected)
+ {
+ F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX];
+ F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY];
+ F32 area = (F32)(width * height);
+ rv = llround(area);
+ }
+ return rv;
+}
+
+void LLViewerParcelMgr::resetSegments(U8* segments)
+{
+ S32 i;
+ S32 count = (mParcelsPerEdge+1)*(mParcelsPerEdge+1);
+ for (i = 0; i < count; i++)
+ {
+ segments[i] = 0x0;
+ }
+}
+
+
+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 );
+
+ const S32 STRIDE = mParcelsPerEdge+1;
+
+ // south edge
+ y = min_y;
+ for (x = min_x; x < max_x; x++)
+ {
+ // exclusive OR means that writing to this segment twice
+ // will turn it off
+ mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
+ }
+
+ // west edge
+ x = min_x;
+ for (y = min_y; y < max_y; y++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
+ }
+
+ // north edge - draw the south border on the y+1'th cell,
+ // which given C-style arrays, is item foo[max_y]
+ y = max_y;
+ for (x = min_x; x < max_x; x++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
+ }
+
+ // east edge - draw west border on x+1'th cell
+ x = max_x;
+ for (y = min_y; y < max_y; y++)
+ {
+ mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
+ }
+}
+
+
+void LLViewerParcelMgr::writeSegmentsFromBitmap(U8* bitmap, U8* segments)
+{
+ S32 x;
+ S32 y;
+ const S32 IN_STRIDE = mParcelsPerEdge;
+ const S32 OUT_STRIDE = mParcelsPerEdge+1;
+
+ for (y = 0; y < IN_STRIDE; y++)
+ {
+ x = 0;
+ while( x < IN_STRIDE )
+ {
+ U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];
+
+ S32 bit;
+ for (bit = 0; bit < 8; bit++)
+ {
+ if (byte & (1 << bit) )
+ {
+ S32 out = x+y*OUT_STRIDE;
+
+ // This and one above it
+ segments[out] ^= SOUTH_MASK;
+ segments[out+OUT_STRIDE] ^= SOUTH_MASK;
+
+ // This and one to the right
+ segments[out] ^= WEST_MASK;
+ segments[out+1] ^= WEST_MASK;
+ }
+ x++;
+ }
+ }
+ }
+}
+
+
+void LLViewerParcelMgr::writeAgentParcelFromBitmap(U8* bitmap)
+{
+ S32 x;
+ S32 y;
+ const S32 IN_STRIDE = mParcelsPerEdge;
+
+ for (y = 0; y < IN_STRIDE; y++)
+ {
+ x = 0;
+ while( x < IN_STRIDE )
+ {
+ U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];
+
+ S32 bit;
+ for (bit = 0; bit < 8; bit++)
+ {
+ if (byte & (1 << bit) )
+ {
+ mAgentParcelOverlay[x+y*IN_STRIDE] = 1;
+ }
+ else
+ {
+ mAgentParcelOverlay[x+y*IN_STRIDE] = 0;
+ }
+ x++;
+ }
+ }
+ }
+}
+
+
+// Given a point, find the PARCEL_GRID_STEP x PARCEL_GRID_STEP block
+// containing it and select that.
+void LLViewerParcelMgr::selectParcelAt(const LLVector3d& pos_global)
+{
+ LLVector3d southwest = pos_global;
+ 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 );
+
+ 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 );
+
+ // Snap to parcel
+ selectLand( southwest, northeast, TRUE );
+}
+
+
+// Tries to select the parcel inside the rectangle
+void LLViewerParcelMgr::selectParcelInRectangle()
+{
+ selectLand(mWestSouth, mEastNorth, TRUE);
+}
+
+
+void LLViewerParcelMgr::selectCollisionParcel()
+{
+ if (!gWorldp)
+ {
+ return;
+ }
+
+ // BUG: Claim to be in the agent's region
+ mWestSouth = gAgent.getRegion()->getOriginGlobal();
+ mEastNorth = mWestSouth;
+ mEastNorth += LLVector3d(PARCEL_GRID_STEP_METERS, PARCEL_GRID_STEP_METERS, 0.0);
+
+ // BUG: must be in the sim you are in
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequestByID);
+ msg->nextBlockFast(_PREHASH_AgentID);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
+ msg->addS32Fast(_PREHASH_LocalID, mCollisionParcel->getLocalID() );
+ gAgent.sendReliableMessage();
+
+ mRequestResult = PARCEL_RESULT_NO_DATA;
+
+ // Hack: Copy some data over temporarily
+ mParcel->setName( mCollisionParcel->getName() );
+ mParcel->setDesc( mCollisionParcel->getDesc() );
+ mParcel->setPassPrice(mCollisionParcel->getPassPrice());
+ mParcel->setPassHours(mCollisionParcel->getPassHours());
+
+ // clear the list of segments to prevent flashing
+ resetSegments(mHighlightSegments);
+
+ mSelected = TRUE;
+ mWholeParcelSelected = TRUE;
+ notifyObservers();
+ return;
+}
+
+
+// snap_selection = auto-select the hit parcel, if there is exactly one
+void LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
+ BOOL snap_selection)
+{
+ if (!gWorldp)
+ {
+ return;
+ }
+
+ sanitize_corners( corner1, corner2, mWestSouth, mEastNorth );
+
+ // ...x isn't more than one meter away
+ F32 delta_x = getSelectionWidth();
+ if (delta_x * delta_x <= 1.f * 1.f)
+ {
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // ...y isn't more than one meter away
+ F32 delta_y = getSelectionHeight();
+ if (delta_y * delta_y <= 1.f * 1.f)
+ {
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // Can't select across region boundary
+ // We need to pull in the upper right corner by a little bit to allow
+ // selection up to the x = 256 or y = 256 edge.
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal(mWestSouth);
+ LLViewerRegion *region_other = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+
+ if(!region)
+ {
+ // just in case they somehow selected no land.
+ mSelected = FALSE;
+ return;
+ }
+
+ if (region != region_other)
+ {
+ LLNotifyBox::showXml("CantSelectLandFromMultipleRegions");
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+
+ // Build region global copies of corners
+ LLVector3 wsb_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 ent_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ /*
+ // Check land to make sure all is either public, owned, or self
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay)
+ {
+ llerrs << "No overlay in LLViewerParcelMgr::selectLand" << llendl;
+ return;
+ }
+
+ U8 start_ownership = overlay->ownership( wsb_region );
+ BOOL identical = TRUE;
+ S32 x_steps = S32( getSelectionWidth() / PARCEL_GRID_STEP_METERS );
+ S32 y_steps = S32( getSelectionHeight() / PARCEL_GRID_STEP_METERS );
+
+ for (S32 x = 0; x < x_steps && identical; x++ )
+ {
+ for (S32 y = 0; y < y_steps && identical; y++ )
+ {
+ // strange recomputation each time to avoid error accumulation
+ LLVector3 check = wsb_region;
+ check.mV[VX] += x * PARCEL_GRID_STEP_METERS;
+ check.mV[VY] += y * PARCEL_GRID_STEP_METERS;
+
+ identical = (start_ownership == overlay->ownership(check));
+ }
+ }
+
+ if (!identical)
+ {
+ add_chat("Can't select mix of your own, other people's and public land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM);
+ add_chat("Try selecting a smaller piece of land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM);
+ mSelected = FALSE;
+ notifyObservers();
+ return;
+ }
+ */
+
+ // Send request message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
+ msg->addF32Fast(_PREHASH_West, wsb_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_South, wsb_region.mV[VY] );
+ msg->addF32Fast(_PREHASH_East, ent_region.mV[VX] );
+ msg->addF32Fast(_PREHASH_North, ent_region.mV[VY] );
+ msg->addBOOL("SnapSelection", snap_selection);
+ msg->sendReliable( region->getHost() );
+
+ mRequestResult = PARCEL_RESULT_NO_DATA;
+
+ // clear the list of segments to prevent flashing
+ resetSegments(mHighlightSegments);
+
+ mSelected = TRUE;
+ mWholeParcelSelected = snap_selection;
+ notifyObservers();
+ return;
+}
+
+
+void LLViewerParcelMgr::deselectLand()
+{
+ if (mSelected)
+ {
+ mSelected = FALSE;
+
+ // Invalidate the selected parcel
+ mParcel->setLocalID(-1);
+ mParcel->mAccessList.clear();
+ mParcel->mBanList.clear();
+ //mParcel->mRenterList.reset();
+
+ mSelectedDwell = 0.f;
+
+ notifyObservers();
+ }
+}
+
+
+void LLViewerParcelMgr::addObserver(LLParcelObserver* observer)
+{
+ mObservers.put(observer);
+}
+
+
+void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
+{
+ mObservers.removeObj(observer);
+}
+
+
+// Call this method when it's time to update everyone on a new state.
+// Copy the list because an observer could respond by removing itself
+// from the list.
+void LLViewerParcelMgr::notifyObservers()
+{
+ LLDynamicArray<LLParcelObserver*> observers;
+ S32 count = mObservers.count();
+ S32 i;
+ for(i = 0; i < count; ++i)
+ {
+ observers.put(mObservers.get(i));
+ }
+ for(i = 0; i < count; ++i)
+ {
+ observers.get(i)->changed();
+ }
+}
+
+
+//
+// ACCESSORS
+//
+BOOL LLViewerParcelMgr::selectionEmpty() const
+{
+ return !mSelected;
+}
+
+
+LLParcel *LLViewerParcelMgr::getSelectedParcel() const
+{
+ if (mSelected)
+ {
+ return mParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+LLParcel *LLViewerParcelMgr::getAgentParcel() const
+{
+ return mAgentParcel;
+}
+
+// Return whether the agent can build on the land they are on
+BOOL LLViewerParcelMgr::agentCanBuild() const
+{
+ if (mAgentParcel)
+ {
+ return gAgent.isGodlike()
+ || (mAgentParcel->getOwnerID() == gAgent.getID())
+ || (mAgentParcel->getAllowModify());
+ }
+ else
+ {
+ return gAgent.isGodlike();
+ }
+}
+
+BOOL LLViewerParcelMgr::agentCanTakeDamage() const
+{
+ return mAgentParcel->getAllowDamage();
+}
+
+BOOL LLViewerParcelMgr::agentCanFly() const
+{
+ return TRUE;
+}
+
+F32 LLViewerParcelMgr::agentDrawDistance() const
+{
+ return 512.f;
+}
+
+BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwned( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isOwnedSelfAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwnedSelf( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isOwnedOtherAt(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isOwnedOther( pos_region );
+}
+
+BOOL LLViewerParcelMgr::isSoundLocal(const LLVector3d& pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal( pos_global );
+ if (!region) return FALSE;
+
+ LLViewerParcelOverlay* overlay = region->getParcelOverlay();
+ if (!overlay) return FALSE;
+
+ LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );
+
+ return overlay->isSoundLocal( pos_region );
+}
+
+BOOL LLViewerParcelMgr::canHearSound(const LLVector3d &pos_global) const
+{
+ BOOL in_agent_parcel = inAgentParcel(pos_global);
+
+ if (in_agent_parcel)
+ {
+ // In same parcel as the agent
+ return TRUE;
+ }
+ else
+ {
+ if (gParcelMgr->getAgentParcel()->getSoundLocal())
+ {
+ // Not in same parcel, and agent parcel only has local sound
+ return FALSE;
+ }
+ else if (gParcelMgr->isSoundLocal(pos_global))
+ {
+ // Not in same parcel, and target parcel only has local sound
+ return FALSE;
+ }
+ else
+ {
+ // Not in same parcel, but neither are local sound
+ return TRUE;
+ }
+ }
+}
+
+
+BOOL LLViewerParcelMgr::inAgentParcel(const LLVector3d &pos_global) const
+{
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(pos_global);
+ if (region != gAgent.getRegion())
+ {
+ // Can't be in the agent parcel if you're not in the same region.
+ return FALSE;
+ }
+
+ LLVector3 pos_region = gAgent.getRegion()->getPosRegionFromGlobal(pos_global);
+ S32 row = S32(pos_region.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos_region.mV[VX] / PARCEL_GRID_STEP_METERS);
+
+ if (mAgentParcelOverlay[row*mParcelsPerEdge + column])
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+// Returns NULL when there is no valid data.
+LLParcel* LLViewerParcelMgr::getHoverParcel() const
+{
+ if (mHoverRequestResult == PARCEL_RESULT_SUCCESS)
+ {
+ return mHoverParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+// Returns NULL when there is no valid data.
+LLParcel* LLViewerParcelMgr::getCollisionParcel() const
+{
+ if (mRenderCollision)
+ {
+ return mCollisionParcel;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//
+// UTILITIES
+//
+
+void LLViewerParcelMgr::render()
+{
+ if (mSelected && mRenderSelection)
+ {
+ // Rendering is done in agent-coordinates, so need to supply
+ // an appropriate offset to the render code.
+ LLViewerRegion *regionp = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!regionp) return;
+
+ renderHighlightSegments(mHighlightSegments, regionp);
+ }
+}
+
+
+void LLViewerParcelMgr::renderParcelCollision()
+{
+ // check for expiration
+ if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS)
+ {
+ mRenderCollision = FALSE;
+ }
+
+ if (mRenderCollision)
+ {
+ LLViewerRegion* regionp = gAgent.getRegion();
+ BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST);
+ renderCollisionSegments(mCollisionSegments, use_pass, regionp);
+ }
+}
+
+
+void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
+{
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+
+ if (flags & AL_BAN)
+ {
+ mParcel->mBanList.clear();
+ }
+ if (flags & AL_ACCESS)
+ {
+ mParcel->mAccessList.clear();
+ }
+
+ // Only the headers differ
+ msg->newMessageFast(_PREHASH_ParcelAccessListRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addS32Fast(_PREHASH_SequenceID, 0);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::sendParcelDwellRequest()
+{
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ // Only the headers differ
+ msg->newMessage("ParcelDwellRequest");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", mParcel->getLocalID());
+ msg->addUUID("ParcelID", LLUUID::null); // filled in on simulator
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id)
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotSetLandOwnerNothingSelected");
+ return;
+ }
+
+ llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
+
+ // BUG: Only works for the region containing mWestSouthBottom
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ // TODO: Add a force owner version of this alert.
+ gViewerWindow->alertXml("CannotContentifyNoRegion");
+ return;
+ }
+
+ // BUG: Make work for cross-region selections
+ LLViewerRegion *region2 = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+ if (region != region2)
+ {
+ gViewerWindow->alertXml("CannotSetLandOwnerMultipleRegions");
+ return;
+ }
+
+ llinfos << "Region " << region->getOriginGlobal() << llendl;
+
+ LLGodForceOwnerData* data = new LLGodForceOwnerData(owner_id, mParcel->getLocalID(), region->getHost());
+ if(mParcel->getAuctionID())
+ {
+ gViewerWindow->alertXml("ForceOwnerAuctionWarning",
+ callback_god_force_owner,
+ (void*)data);
+ }
+ else
+ {
+ callback_god_force_owner(0, (void*)data);
+ }
+}
+
+void callback_god_force_owner(S32 option, void* user_data)
+{
+ LLGodForceOwnerData* data = (LLGodForceOwnerData*)user_data;
+ if(data && (0 == option))
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelGodForceOwner");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addUUID("OwnerID", data->mOwnerID);
+ msg->addS32( "LocalID", data->mLocalID);
+ msg->sendReliable(data->mHost);
+ }
+ delete data;
+}
+
+void LLViewerParcelMgr::sendParcelGodForceToContent()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotContentifyNothingSelected");
+ return;
+ }
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotContentifyNoRegion");
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelGodMarkAsContent");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addS32("LocalID", mParcel->getLocalID());
+ msg->sendReliable(region->getHost());
+}
+
+void LLViewerParcelMgr::sendParcelRelease()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNothingSelected");
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNoRegion");
+ return;
+ }
+
+ //U32 flags = PR_NONE;
+ //if (god_force) flags |= PR_GOD_FORCE;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelRelease");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ //msg->addU32("Flags", flags);
+ msg->sendReliable( region->getHost() );
+
+ // Blitz selection, since the parcel might be non-rectangular, and
+ // we won't have appropriate parcel information.
+ deselectLand();
+}
+
+class LLViewerParcelMgr::ParcelBuyInfo
+{
+public:
+ LLUUID mAgent;
+ LLUUID mSession;
+ LLUUID mGroup;
+ BOOL mIsGroupOwned;
+ BOOL mRemoveContribution;
+ BOOL mIsClaim;
+ LLHost mHost;
+
+ // for parcel buys
+ S32 mParcelID;
+
+ // for land claims
+ F32 mWest;
+ F32 mSouth;
+ F32 mEast;
+ F32 mNorth;
+};
+
+LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy(
+ const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& group_id,
+ BOOL is_group_owned,
+ BOOL is_claim,
+ BOOL remove_contribution)
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotBuyLandNothingSelected");
+ return NULL;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotBuyLandNoRegion");
+ return NULL;
+ }
+
+ if (is_claim)
+ {
+ llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
+ llinfos << "Region " << region->getOriginGlobal() << llendl;
+
+ // BUG: Only works for the region containing mWestSouthBottom
+ LLVector3d east_north_region_check( mEastNorth );
+ east_north_region_check.mdV[VX] -= 0.5;
+ east_north_region_check.mdV[VY] -= 0.5;
+
+ LLViewerRegion *region2 = gWorldp->getRegionFromPosGlobal( east_north_region_check );
+
+ if (region != region2)
+ {
+ gViewerWindow->alertXml("CantBuyLandAcrossMultipleRegions");
+ return NULL;
+ }
+ }
+
+
+ ParcelBuyInfo* info = new ParcelBuyInfo;
+
+ info->mAgent = agent_id;
+ info->mSession = session_id;
+ info->mGroup = group_id;
+ info->mIsGroupOwned = is_group_owned;
+ info->mIsClaim = is_claim;
+ info->mRemoveContribution = remove_contribution;
+ info->mHost = region->getHost();
+
+ if (!is_claim)
+ {
+ info->mParcelID = mParcel->getLocalID();
+ }
+ else
+ {
+ // BUG: Make work for cross-region selections
+ LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ info->mWest = west_south_bottom_region.mV[VX];
+ info->mSouth = west_south_bottom_region.mV[VY];
+ info->mEast = east_north_top_region.mV[VX];
+ info->mNorth = east_north_top_region.mV[VY];
+ }
+
+ return info;
+}
+
+void LLViewerParcelMgr::sendParcelBuy(ParcelBuyInfo* info)
+{
+ // send the message
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage(info->mIsClaim ? "ParcelClaim" : "ParcelBuy");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", info->mAgent);
+ msg->addUUID("SessionID", info->mSession);
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", info->mGroup);
+ msg->addBOOL("IsGroupOwned", info->mIsGroupOwned);
+ if (!info->mIsClaim)
+ {
+ msg->addBOOL("RemoveContribution", info->mRemoveContribution);
+ msg->addS32("LocalID", info->mParcelID);
+ }
+ msg->addBOOL("Final", TRUE); // don't allow escrow buys
+ if (info->mIsClaim)
+ {
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", info->mWest);
+ msg->addF32("South", info->mSouth);
+ msg->addF32("East", info->mEast);
+ msg->addF32("North", info->mNorth);
+ }
+ msg->sendReliable(info->mHost);
+}
+
+void LLViewerParcelMgr::deleteParcelBuy(ParcelBuyInfo*& info)
+{
+ delete info;
+ info = NULL;
+}
+
+void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id)
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNothingSelected");
+ return;
+ }
+ if(group_id.isNull())
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoGroup");
+ return;
+ }
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoRegion");
+ return;
+ }
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelDeedToGroup");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID() );
+ msg->addUUID("SessionID", gAgent.getSessionID() );
+ msg->nextBlock("Data");
+ msg->addUUID("GroupID", group_id );
+ msg->addS32("LocalID", mParcel->getLocalID() );
+ //msg->addU32("JoinNeighbors", join);
+ msg->sendReliable( region->getHost() );
+}
+
+
+/*
+// *NOTE: We cannot easily make landmarks at global positions because
+// global positions probably refer to a sim/local combination which
+// can move over time. We could implement this by looking up the
+// region global x,y, but it's easier to take it out for now.
+void LLViewerParcelMgr::makeLandmarkAtSelection()
+{
+ // Don't create for parcels you don't own
+ if (gAgent.getID() != mParcel->getOwnerID())
+ {
+ return;
+ }
+
+ LLVector3d global_center(mWestSouth);
+ global_center += mEastNorth;
+ global_center *= 0.5f;
+
+ LLViewerRegion* region;
+ region = gWorldp->getRegionFromPosGlobal(global_center);
+
+ LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
+ LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
+
+ LLString name("My Land");
+ char buffer[MAX_STRING];
+ S32 pos_x = (S32)floor((west_south_bottom_region.mV[VX] + east_north_top_region.mV[VX]) / 2.0f);
+ S32 pos_y = (S32)floor((west_south_bottom_region.mV[VY] + east_north_top_region.mV[VY]) / 2.0f);
+ sprintf(buffer, "%s in %s (%d, %d)",
+ name.c_str(),
+ region->getName().c_str(),
+ pos_x, pos_y);
+ name.assign(buffer);
+
+ const char* desc = "Claimed land";
+ create_landmark(name.c_str(), desc, global_center);
+}
+*/
+
+const char* LLViewerParcelMgr::getAgentParcelName() const
+{
+ return mAgentParcel->getName();
+}
+
+
+void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, BOOL want_reply_to_update)
+{
+ if (!parcel) return;
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem *msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_ParcelPropertiesUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
+
+ U32 flags = 0x0;
+ if (want_reply_to_update) flags |= 0x01;
+ msg->addU32("Flags", flags);
+
+ parcel->packMessage(msg);
+
+ msg->sendReliable( region->getHost() );
+}
+
+
+void LLViewerParcelMgr::requestHoverParcelProperties(const LLVector3d& pos)
+{
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( pos );
+ if (!region)
+ {
+ return;
+ }
+
+ // Send a rectangle around the point.
+ // This means the parcel sent back is at least a rectangle around the point,
+ // which is more efficient for public land. Fewer requests are sent. JC
+ LLVector3 wsb_region = region->getPosRegionFromGlobal( pos );
+
+ F32 west = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VX] / PARCEL_GRID_STEP_METERS );
+ F32 south = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VY] / PARCEL_GRID_STEP_METERS );
+
+ F32 east = west + PARCEL_GRID_STEP_METERS;
+ F32 north = south + PARCEL_GRID_STEP_METERS;
+
+ // Send request message
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_SequenceID, HOVERED_PARCEL_SEQ_ID );
+ msg->addF32Fast(_PREHASH_West, west );
+ msg->addF32Fast(_PREHASH_South, south );
+ msg->addF32Fast(_PREHASH_East, east );
+ msg->addF32Fast(_PREHASH_North, north );
+ msg->addBOOL("SnapSelection", FALSE );
+ msg->sendReliable( region->getHost() );
+
+ mHoverRequestResult = PARCEL_RESULT_NO_DATA;
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelOverlay(LLMessageSystem *msg, void **user)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ // Extract the packed overlay information
+ S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data);
+
+ if (packed_overlay_size == 0)
+ {
+ llwarns << "Overlay size 0" << llendl;
+ return;
+ }
+
+ S32 parcels_per_edge = gParcelMgr->mParcelsPerEdge;
+ S32 expected_size = parcels_per_edge * parcels_per_edge / PARCEL_OVERLAY_CHUNKS;
+ if (packed_overlay_size != expected_size)
+ {
+ llwarns << "Got parcel overlay size " << packed_overlay_size
+ << " expecting " << expected_size << llendl;
+ return;
+ }
+
+ S32 sequence_id;
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id);
+ msg->getBinaryDataFast(
+ _PREHASH_ParcelData,
+ _PREHASH_Data,
+ sPackedOverlay,
+ expected_size);
+
+ LLHost host = msg->getSender();
+ LLViewerRegion *region = gWorldp->getRegion(host);
+ if (region)
+ {
+ region->mParcelOverlay->uncompressLandOverlay( sequence_id, sPackedOverlay );
+ }
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **user)
+{
+ S32 request_result;
+ S32 sequence_id;
+ BOOL snap_selection = FALSE;
+ S32 self_count = 0;
+ S32 other_count = 0;
+ S32 public_count = 0;
+ S32 local_id;
+ LLUUID owner_id;
+ BOOL is_group_owned;
+ U32 auction_id = 0;
+ BOOL is_reserved = FALSE;
+ S32 claim_price_per_meter = 0;
+ S32 rent_price_per_meter = 0;
+ S32 claim_date = 0;
+ LLVector3 aabb_min;
+ LLVector3 aabb_max;
+ S32 area = 0;
+ S32 sw_max_prims = 0;
+ S32 sw_total_prims = 0;
+ //LLUUID buyer_id;
+ U8 status = 0;
+ S32 max_prims = 0;
+ S32 total_prims = 0;
+ S32 owner_prims = 0;
+ S32 group_prims = 0;
+ S32 other_prims = 0;
+ S32 selected_prims = 0;
+ F32 parcel_prim_bonus = 1.f;
+ BOOL region_push_override = false;
+ BOOL region_deny_anonymous_override = false;
+ BOOL region_deny_identified_override = false;
+ BOOL region_deny_transacted_override = false;
+
+ S32 other_clean_time = 0;
+
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_RequestResult, request_result );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id );
+
+ if (request_result == PARCEL_RESULT_NO_DATA)
+ {
+ // no valid parcel data
+ llinfos << "no valid parcel data" << llendl;
+ return;
+ }
+
+ // Decide where the data will go.
+ LLParcel* parcel = NULL;
+ if (sequence_id == SELECTED_PARCEL_SEQ_ID)
+ {
+ // ...selected parcels report this sequence id
+ gParcelMgr->mRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mParcel;
+ }
+ else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mHoverParcel;
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
+ parcel = gParcelMgr->mCollisionParcel;
+ }
+ else if (sequence_id == 0 || sequence_id > gParcelMgr->mAgentParcelSequenceID)
+ {
+ // new agent parcel
+ gParcelMgr->mAgentParcelSequenceID = sequence_id;
+ parcel = gParcelMgr->mAgentParcel;
+ }
+ else
+ {
+ llinfos << "out of order agent parcel sequence id " << sequence_id
+ << " last good " << gParcelMgr->mAgentParcelSequenceID
+ << llendl;
+ return;
+ }
+
+ msg->getBOOL("ParcelData", "SnapSelection", snap_selection);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelfCount, self_count);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherCount, other_count);
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_PublicCount, public_count);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_LocalID, local_id );
+ msg->getUUIDFast(_PREHASH_ParcelData, _PREHASH_OwnerID, owner_id);
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_IsGroupOwned, is_group_owned);
+ msg->getU32Fast(_PREHASH_ParcelData, _PREHASH_AuctionID, auction_id);
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_ReservedNewbie, is_reserved);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimDate, claim_date);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimPrice, claim_price_per_meter);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_RentPrice, rent_price_per_meter);
+ msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMin, aabb_min);
+ msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMax, aabb_max);
+ msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_Area, area );
+ //msg->getUUIDFast( _PREHASH_ParcelData, _PREHASH_BuyerID, buyer_id);
+ msg->getU8("ParcelData", "Status", status);
+ msg->getS32("ParcelData", "SimWideMaxPrims", sw_max_prims );
+ msg->getS32("ParcelData", "SimWideTotalPrims", sw_total_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_MaxPrims, max_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_TotalPrims, total_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OwnerPrims, owner_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_GroupPrims, group_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherPrims, other_prims );
+ msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelectedPrims, selected_prims );
+ msg->getF32Fast(_PREHASH_ParcelData, _PREHASH_ParcelPrimBonus, parcel_prim_bonus );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionPushOverride, region_push_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyAnonymous, region_deny_anonymous_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyIdentified, region_deny_identified_override );
+ msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyTransacted, region_deny_transacted_override );
+
+ msg->getS32("ParcelData", "OtherCleanTime", other_clean_time );
+
+ // Actually extract the data.
+ if (parcel)
+ {
+ parcel->init(owner_id,
+ FALSE, FALSE, FALSE,
+ claim_date, claim_price_per_meter, rent_price_per_meter,
+ area, other_prims, parcel_prim_bonus, is_group_owned);
+ parcel->setLocalID(local_id);
+ parcel->setAABBMin(aabb_min);
+ parcel->setAABBMax(aabb_max);
+
+ parcel->setAuctionID(auction_id);
+ parcel->setReservedForNewbie(is_reserved);
+ parcel->setOwnershipStatus((LLParcel::EOwnershipStatus)status);
+
+ parcel->setSimWideMaxPrimCapacity(sw_max_prims);
+ parcel->setSimWidePrimCount(sw_total_prims);
+ parcel->setMaxPrimCapacity(max_prims);
+ parcel->setOwnerPrimCount(owner_prims);
+ parcel->setGroupPrimCount(group_prims);
+ parcel->setOtherPrimCount(other_prims);
+ parcel->setSelectedPrimCount(selected_prims);
+ parcel->setParcelPrimBonus(parcel_prim_bonus);
+
+ parcel->setCleanOtherTime(other_clean_time);
+ parcel->setRegionPushOverride(region_push_override);
+ parcel->setRegionDenyAnonymousOverride(region_deny_anonymous_override);
+ parcel->setRegionDenyIdentifiedOverride(region_deny_identified_override);
+ parcel->setRegionDenyTransactedOverride(region_deny_transacted_override);
+ parcel->unpackMessage(msg);
+
+ if (parcel == gParcelMgr->mAgentParcel)
+ {
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->writeAgentParcelFromBitmap(bitmap);
+ delete[] bitmap;
+ }
+ }
+
+ // Handle updating selections, if necessary.
+ if (sequence_id == SELECTED_PARCEL_SEQ_ID)
+ {
+ // Update selected counts
+ gParcelMgr->mSelectedSelfCount = self_count;
+ gParcelMgr->mSelectedOtherCount = other_count;
+ gParcelMgr->mSelectedPublicCount = public_count;
+
+ gParcelMgr->mSelectedMultipleOwners =
+ (request_result == PARCEL_RESULT_MULTIPLE);
+
+ // Select the whole parcel
+ LLViewerRegion *region = gWorldp->getRegion( msg->getSender() );
+ if (region)
+ {
+ if (!snap_selection)
+ {
+ // don't muck with the westsouth and eastnorth.
+ // just highlight it
+ LLVector3 west_south = region->getPosRegionFromGlobal(gParcelMgr->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(gParcelMgr->mEastNorth);
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeHighlightSegments(
+ west_south.mV[VX],
+ west_south.mV[VY],
+ east_north.mV[VX],
+ east_north.mV[VY] );
+ gParcelMgr->mWholeParcelSelected = FALSE;
+ }
+ else if (0 == local_id)
+ {
+ // this is public land, just highlight the selection
+ gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeHighlightSegments(
+ aabb_min.mV[VX],
+ aabb_min.mV[VY],
+ aabb_max.mV[VX],
+ aabb_max.mV[VY] );
+ gParcelMgr->mWholeParcelSelected = TRUE;
+ }
+ else
+ {
+ gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
+
+ // Owned land, highlight the boundaries
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments);
+ gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mHighlightSegments );
+
+ delete bitmap;
+ bitmap = NULL;
+
+ gParcelMgr->mWholeParcelSelected = TRUE;
+ }
+
+ // Request access list information for this land
+ gParcelMgr->sendParcelAccessListRequest(AL_ACCESS | AL_BAN);
+
+ // Request dwell for this land, if it's not public land.
+ gParcelMgr->mSelectedDwell = 0.f;
+ if (0 != local_id)
+ {
+ gParcelMgr->sendParcelDwellRequest();
+ }
+
+ gParcelMgr->mSelected = TRUE;
+ gParcelMgr->notifyObservers();
+ }
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
+ sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ // We're about to collide with this parcel
+ gParcelMgr->mRenderCollision = TRUE;
+ gParcelMgr->mCollisionTimer.reset();
+
+ // Differentiate this parcel if we are banned from it.
+ if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mCollisionBanned = BA_BANNED;
+ }
+ else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID)
+ {
+ gParcelMgr->mCollisionBanned = BA_NOT_IN_GROUP;
+ }
+ else
+ {
+ gParcelMgr->mCollisionBanned = BA_NOT_ON_LIST;
+
+ }
+
+ S32 bitmap_size = gParcelMgr->mParcelsPerEdge
+ * gParcelMgr->mParcelsPerEdge
+ / 8;
+ U8* bitmap = new U8[ bitmap_size ];
+ msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
+
+ gParcelMgr->resetSegments(gParcelMgr->mCollisionSegments);
+ gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mCollisionSegments );
+
+ delete bitmap;
+ bitmap = NULL;
+
+ }
+ else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
+ {
+ LLViewerRegion *region = gWorldp->getRegion( msg->getSender() );
+ if (region)
+ {
+ gParcelMgr->mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min );
+ gParcelMgr->mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max );
+ }
+ else
+ {
+ gParcelMgr->mHoverWestSouth.clearVec();
+ gParcelMgr->mHoverEastNorth.clearVec();
+ }
+ }
+ else
+ {
+ // It's the agent parcel
+ BOOL new_parcel = parcel ? FALSE : TRUE;
+ if (parcel)
+ {
+ S32 parcelid = parcel->getLocalID();
+ U64 regionid = gAgent.getRegion()->getHandle();
+ if (parcelid != gParcelMgr->mMediaParcelId || regionid != gParcelMgr->mMediaRegionId)
+ {
+ gParcelMgr->mMediaParcelId = parcelid;
+ gParcelMgr->mMediaRegionId = regionid;
+ new_parcel = TRUE;
+ }
+ }
+ // look for music.
+ if (gAudiop)
+ {
+ if (parcel)
+ {
+ LLString music_url_raw = parcel->getMusicURL();
+
+ // Trim off whitespace from front and back
+ LLString music_url = music_url_raw;
+ LLString::trim(music_url);
+
+ // On entering a new parcel, stop the last stream if the
+ // new parcel has a different music url. (Empty URL counts
+ // as different.)
+ const char* stream_url = gAudiop->getInternetStreamURL();
+
+ if (music_url.empty() || music_url != stream_url)
+ {
+ // URL is different from one currently playing.
+ gAudiop->stopInternetStream();
+
+ // If there is a new music URL and it's valid, play it.
+ if (music_url.size() > 12)
+ {
+ if (music_url.substr(0,7) == "http://")
+ {
+ optionally_start_music(music_url);
+ }
+ }
+ else if (gAudiop->getInternetStreamURL()[0])
+ {
+ llinfos << "Stopping parcel music" << llendl;
+ gAudiop->startInternetStream(NULL);
+ }
+ }
+ }
+ else
+ {
+ // Public land has no music
+ gAudiop->stopInternetStream();
+ }
+ }//if gAudiop
+
+ // now check for video
+ if (LLMediaEngine::getInstance ()->isAvailable ())
+ {
+ // we have a player
+ if (parcel)
+ {
+ // we're in a parcel
+ std::string mediaUrl = std::string ( parcel->getMediaURL () );
+ LLString::trim(mediaUrl);
+
+ // something changed
+ LLMediaEngine* me = LLMediaEngine::getInstance();
+ if ( ( me->getUrl () != mediaUrl )
+ || ( me->getImageUUID () != parcel->getMediaID () )
+ || ( me->isAutoScaled () != parcel->getMediaAutoScale () ) )
+ {
+ BOOL video_was_playing = FALSE;
+ LLMediaBase* renderer = me->getMediaRenderer();
+ if (renderer && (renderer->isPlaying() || renderer->isLooping()))
+ {
+ video_was_playing = TRUE;
+ }
+
+ stop_video();
+
+ if ( !mediaUrl.empty () )
+ {
+ // Someone has "changed the channel", changing the URL of a video
+ // you were already watching. Do we want to automatically start playing? JC
+ if (!new_parcel
+ && gSavedSettings.getBOOL("AudioStreamingVideo")
+ && video_was_playing)
+ {
+ start_video(parcel);
+ }
+ else
+ {
+ // "Prepare" the media engine, but don't auto-play. JC
+ optionally_prepare_video(parcel);
+ }
+ }
+ }
+ }
+ else
+ {
+ stop_video();
+ }
+ }
+ else
+ {
+ // no audio player, do a first use dialog if their is media here
+ if (parcel)
+ {
+ std::string mediaUrl = std::string ( parcel->getMediaURL () );
+ if (!mediaUrl.empty ())
+ {
+ if (gSavedSettings.getWarning("QuickTimeInstalled"))
+ {
+ gSavedSettings.setWarning("QuickTimeInstalled", FALSE);
+
+ LLNotifyBox::showXml("NoQuickTime" );
+ };
+ }
+ }
+ }
+
+ };
+}
+
+void optionally_start_music(const LLString& music_url)
+{
+ if (gSavedSettings.getWarning("FirstStreamingMusic"))
+ {
+ void* data = (void*)strdup(music_url.c_str());
+ gViewerWindow->alertXml("ParcelCanPlayMusic",
+ callback_start_music,
+ (void*)data);
+
+ }
+ else if (gSavedSettings.getBOOL("AudioStreamingMusic"))
+ {
+ // Make the user click the start button on the overlay bar. JC
+ // llinfos << "Starting parcel music " << music_url << llendl;
+
+ // now only play music when you enter a new parcel if the control is in PLAY state
+ // changed as part of SL-4878
+ if ( gOverlayBar->getMusicRemoteControl ()->getTransportState () == LLMediaRemoteCtrl::Play )
+ {
+ if (gAudiop)
+ {
+ gAudiop->startInternetStream(music_url.c_str());
+ }
+ };
+ }
+}
+
+
+void callback_start_music(S32 option, void* data)
+{
+ const char* music_url = (const char*)data;
+
+ if (0 == option)
+ {
+ gSavedSettings.setBOOL("AudioStreamingMusic", TRUE);
+ llinfos << "Starting first parcel music " << music_url << llendl;
+ if (gAudiop)
+ {
+ gAudiop->startInternetStream(music_url);
+ LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl();
+ ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE );
+ }
+ }
+ else
+ {
+ gSavedSettings.setBOOL("AudioStreamingMusic", FALSE);
+ }
+
+ gSavedSettings.setWarning("FirstStreamingMusic", FALSE);
+
+ delete [] music_url;
+ music_url = NULL;
+}
+
+void prepare_video(const LLParcel *parcel)
+{
+ std::string mediaUrl;
+ if (parcel->getParcelFlag(PF_URL_RAW_HTML))
+ {
+ mediaUrl = std::string("data:");
+ mediaUrl.append(parcel->getMediaURL());
+ }
+ else
+ {
+ mediaUrl = std::string ( parcel->getMediaURL () );
+ }
+ LLMediaEngine::getInstance ()->setUrl ( mediaUrl );
+ LLMediaEngine::getInstance ()->setImageUUID ( parcel->getMediaID () );
+ LLMediaEngine::getInstance ()->setAutoScaled ( parcel->getMediaAutoScale () ? TRUE : FALSE ); // (U8 instead of BOOL for future expansion)
+}
+
+void start_video(const LLParcel *parcel)
+{
+ prepare_video(parcel);
+ bool web_url = (parcel->getParcelFlag(PF_URL_WEB_PAGE) || parcel->getParcelFlag(PF_URL_RAW_HTML));
+ std::string path( "" );
+ #if LL_MOZILLA_ENABLED
+ if (web_url)
+ {
+ path = get_mozilla_path();
+ }
+ #endif
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, web_url, path);
+}
+
+void stop_video()
+{
+ // set up remote control so stop is selected
+ LLMediaEngine::getInstance ()->stop ();
+ if (gOverlayBar)
+ {
+ gOverlayBar->refresh ();
+ }
+
+ if (LLMediaEngine::getInstance ()->isLoaded())
+ {
+ LLMediaEngine::getInstance ()->unload ();
+
+ gImageList.updateMovieImage(LLUUID::null, FALSE);
+ gCurrentMovieID.setNull();
+ }
+
+ LLMediaEngine::getInstance ()->setUrl ( "" );
+ LLMediaEngine::getInstance ()->setImageUUID ( LLUUID::null );
+
+}
+
+void optionally_prepare_video(const LLParcel *parcelp)
+{
+ if (gSavedSettings.getWarning("FirstStreamingVideo"))
+ {
+ gViewerWindow->alertXml("ParcelCanPlayMedia",
+ callback_prepare_video,
+ (void*)parcelp);
+ }
+ else
+ {
+ llinfos << "Entering parcel " << parcelp->getLocalID() << " with video " << parcelp->getMediaURL() << llendl;
+ prepare_video(parcelp);
+ }
+}
+
+
+void callback_prepare_video(S32 option, void* data)
+{
+ const LLParcel *parcelp = (const LLParcel *)data;
+
+ if (0 == option)
+ {
+ gSavedSettings.setBOOL("AudioStreamingVideo", TRUE);
+ llinfos << "Starting parcel video " << parcelp->getMediaURL() << " on parcel " << parcelp->getLocalID() << llendl;
+ gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update );
+ prepare_video(parcelp);
+ }
+ else
+ {
+ gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback);
+ gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback );
+ gSavedSettings.setBOOL("AudioStreamingVideo", FALSE);
+ }
+
+ gSavedSettings.setWarning("FirstStreamingVideo", FALSE);
+}
+
+// static
+void LLViewerParcelMgr::processParcelAccessListReply(LLMessageSystem *msg, void **user)
+{
+ LLUUID agent_id;
+ S32 sequence_id = 0;
+ U32 message_flags = 0x0;
+ S32 parcel_id = -1;
+
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_AgentID, agent_id);
+ msg->getS32Fast( _PREHASH_Data, _PREHASH_SequenceID, sequence_id ); //ignored
+ msg->getU32Fast( _PREHASH_Data, _PREHASH_Flags, message_flags);
+ msg->getS32Fast( _PREHASH_Data, _PREHASH_LocalID, parcel_id);
+
+ LLParcel* parcel = gParcelMgr->mParcel;
+ if (!parcel) return;
+
+ if (parcel_id != parcel->getLocalID())
+ {
+ llwarns << "processParcelAccessListReply for parcel " << parcel_id
+ << " which isn't the selected parcel" << llendl;
+ return;
+ }
+
+ if (message_flags & AL_ACCESS)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mAccessList) );
+ }
+ else if (message_flags & AL_BAN)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mBanList) );
+ }
+ /*else if (message_flags & AL_RENTER)
+ {
+ parcel->unpackAccessEntries(msg, &(parcel->mRenterList) );
+ }*/
+
+ gParcelMgr->notifyObservers();
+}
+
+
+// static
+void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**)
+{
+ LLUUID agent_id;
+ msg->getUUID("AgentData", "AgentID", agent_id);
+
+ S32 local_id;
+ msg->getS32("Data", "LocalID", local_id);
+
+ LLUUID parcel_id;
+ msg->getUUID("Data", "ParcelID", parcel_id);
+
+ F32 dwell;
+ msg->getF32("Data", "Dwell", dwell);
+
+ if (local_id == gParcelMgr->mParcel->getLocalID())
+ {
+ gParcelMgr->mSelectedDwell = dwell;
+ gParcelMgr->notifyObservers();
+ }
+}
+
+
+void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
+{
+
+ LLUUID transactionUUID;
+ transactionUUID.generate();
+
+ if (!mSelected)
+ {
+ return;
+ }
+
+ LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth );
+ if (!region) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+
+ LLParcel* parcel = mParcel;
+ if (!parcel) return;
+
+ if (which & AL_ACCESS)
+ {
+ S32 count = parcel->mAccessList.size();
+ S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
+ S32 sequence_id = 1;
+ BOOL start_message = TRUE;
+ BOOL initial = TRUE;
+
+ access_map_const_iterator cit = parcel->mAccessList.begin();
+ access_map_const_iterator end = parcel->mAccessList.end();
+ while ( (cit != end) || initial )
+ {
+ if (start_message)
+ {
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, AL_ACCESS);
+ msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
+ msg->addS32Fast(_PREHASH_Sections, num_sections);
+ start_message = FALSE;
+
+ if (initial && (cit == end))
+ {
+ // pack an empty block if there will be no data
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+ }
+
+ initial = FALSE;
+ sequence_id++;
+
+ }
+
+ while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ {
+
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ ++cit;
+ }
+
+ start_message = TRUE;
+ msg->sendReliable( region->getHost() );
+ }
+ }
+
+ if (which & AL_BAN)
+ {
+ S32 count = parcel->mBanList.size();
+ S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
+ S32 sequence_id = 1;
+ BOOL start_message = TRUE;
+ BOOL initial = TRUE;
+
+ access_map_const_iterator cit = parcel->mBanList.begin();
+ access_map_const_iterator end = parcel->mBanList.end();
+ while ( (cit != end) || initial )
+ {
+ if (start_message)
+ {
+ msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
+ msg->nextBlockFast(_PREHASH_Data);
+ msg->addU32Fast(_PREHASH_Flags, AL_BAN);
+ msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
+ msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
+ msg->addS32Fast(_PREHASH_Sections, num_sections);
+ start_message = FALSE;
+
+ if (initial && (cit == end))
+ {
+ // pack an empty block if there will be no data
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
+ msg->addS32Fast(_PREHASH_Time, 0 );
+ msg->addU32Fast(_PREHASH_Flags, 0 );
+ }
+
+ initial = FALSE;
+ sequence_id++;
+
+ }
+
+ while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
+ {
+ const LLAccessEntry& entry = (*cit).second;
+
+ msg->nextBlockFast(_PREHASH_List);
+ msg->addUUIDFast(_PREHASH_ID, entry.mID );
+ msg->addS32Fast(_PREHASH_Time, entry.mTime );
+ msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
+ ++cit;
+ }
+
+ start_message = TRUE;
+ msg->sendReliable( region->getHost() );
+ }
+ }
+}
+
+
+void LLViewerParcelMgr::deedLandToGroup()
+{
+ char group_name[MAX_STRING];
+ gCacheName->getGroupName(mParcel->getGroupID(), group_name);
+ LLString::format_map_t args;
+ args["[AREA]"] = llformat("%d", mParcel->getArea());
+ args["[GROUP_NAME]"] = group_name;
+ if(mParcel->getContributeWithDeed())
+ {
+ char first_name[DB_FIRST_NAME_BUF_SIZE];
+ first_name[0] = '\0';
+ char last_name[DB_FIRST_NAME_BUF_SIZE];
+ last_name[0] = '\0';
+ gCacheName->getName(mParcel->getOwnerID(), first_name, last_name);
+ args["[FIRST_NAME]"] = first_name;
+ args["[LAST_NAME]"] = last_name;
+ gViewerWindow->alertXml("DeedLandToGroupWithContribution",args, deedAlertCB, NULL);
+ }
+ else
+ {
+ gViewerWindow->alertXml("DeedLandToGroup",args, deedAlertCB, NULL);
+ }
+}
+
+// static
+void LLViewerParcelMgr::deedAlertCB(S32 option, void*)
+{
+ if (option == 0)
+ {
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLUUID group_id;
+ if(parcel)
+ {
+ group_id = parcel->getGroupID();
+ }
+ gParcelMgr->sendParcelDeed(group_id);
+ }
+}
+
+
+void LLViewerParcelMgr::startReleaseLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandNothingSelected");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandWatingForServer");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_MULTIPLE)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandSelected");
+ return;
+ }
+
+ if (!isParcelOwnedByAgent(mParcel, GP_LAND_RELEASE)
+ && !(gAgent.canManageEstate()))
+ {
+ gViewerWindow->alertXml("CannotReleaseLandDontOwn");
+ return;
+ }
+
+ LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandRegionNotFound");
+ return;
+ }
+/*
+ if ((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && !gAgent.isGodlike())
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = region->getName();
+ gViewerWindow->alertXml("CannotReleaseLandNoTransfer", args);
+ return;
+ }
+*/
+
+ if (!mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandPartialSelection");
+ return;
+ }
+
+ // Compute claim price
+ LLStringBase<char>::format_map_t args;
+ args["[AREA]"] = llformat("%d",mParcel->getArea());
+ gViewerWindow->alertXml("ReleaseLandWarning", args,
+ releaseAlertCB, this);
+}
+
+bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const
+{
+ if (!parcel)
+ {
+ return false;
+ }
+
+ if (mSelected && parcel == mParcel)
+ {
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ return false;
+ }
+ }
+
+ const LLUUID& parcelOwner = parcel->getOwnerID();
+ const LLUUID& authorizeBuyer = parcel->getAuthorizedBuyerID();
+
+ if (parcel->isPublic())
+ {
+ return true; // change this if want to make it gods only
+ }
+
+ bool isForSale = parcel->getForSale()
+ && ((parcel->getSalePrice() > 0) || (authorizeBuyer.notNull()));
+
+ bool isEmpowered
+ = forGroup ? gAgent.hasPowerInActiveGroup(GP_LAND_DEED) == TRUE : true;
+
+ bool isOwner
+ = parcelOwner == (forGroup ? gAgent.getGroupID() : gAgent.getID());
+
+ bool isAvailable
+ = parcel->getReservedForNewbie()
+ ? (!forGroup && gStatusBar->getSquareMetersCommitted() == 0)
+ : true;
+ //FIXME: should be based on never_owned_land, see SL-10728
+
+ bool isAuthorized
+ = (authorizeBuyer.isNull() || (gAgent.getID() == authorizeBuyer));
+
+ return isForSale && !isOwner && isAuthorized && isAvailable && isEmpowered;
+}
+
+
+void LLViewerParcelMgr::startBuyLand(BOOL is_for_group)
+{
+ LLFloaterBuyLand::buyLand(getSelectionRegion(), mParcel, is_for_group == TRUE);
+}
+
+void LLViewerParcelMgr::startSellLand()
+{
+ LLFloaterSellLand::sellLand(getSelectionRegion(), mParcel);
+}
+
+void LLViewerParcelMgr::startDivideLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotDivideLandNothingSelected");
+ return;
+ }
+
+ if (mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotDivideLandPartialSelection");
+ return;
+ }
+
+ gViewerWindow->alertXml("LandDivideWarning",
+ callbackDivideLand,
+ this);
+}
+
+// static
+void LLViewerParcelMgr::callbackDivideLand(S32 option, void* data)
+{
+ LLViewerParcelMgr* self = (LLViewerParcelMgr*)data;
+
+ LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDivideLandNoRegion");
+ return;
+ }
+
+ if (0 == option)
+ {
+ LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelDivide");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", west_south.mV[VX]);
+ msg->addF32("South", west_south.mV[VY]);
+ msg->addF32("East", east_north.mV[VX]);
+ msg->addF32("North", east_north.mV[VY]);
+ msg->sendReliable(region->getHost());
+ }
+}
+
+
+void LLViewerParcelMgr::startJoinLand()
+{
+ if (!mSelected)
+ {
+ gViewerWindow->alertXml("CannotJoinLandNothingSelected");
+ return;
+ }
+
+ if (mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotJoinLandEntireParcelSelected");
+ return;
+ }
+
+ if (!mSelectedMultipleOwners)
+ {
+ gViewerWindow->alertXml("CannotJoinLandSelection");
+ return;
+ }
+
+ gViewerWindow->alertXml("JoinLandWarning",
+ callbackJoinLand,
+ this);
+}
+
+// static
+void LLViewerParcelMgr::callbackJoinLand(S32 option, void* data)
+{
+ LLViewerParcelMgr* self = (LLViewerParcelMgr*)data;
+
+ LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotJoinLandNoRegion");
+ return;
+ }
+
+ if (0 == option)
+ {
+ LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth);
+ LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth);
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelJoin");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("ParcelData");
+ msg->addF32("West", west_south.mV[VX]);
+ msg->addF32("South", west_south.mV[VY]);
+ msg->addF32("East", east_north.mV[VX]);
+ msg->addF32("North", east_north.mV[VY]);
+ msg->sendReliable(region->getHost());
+ }
+}
+
+
+void LLViewerParcelMgr::startDeedLandToGroup()
+{
+ if (!mSelected || !mParcel)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNothingSelected");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_NO_DATA)
+ {
+ gViewerWindow->alertXml("CannotDeedLandWaitingForServer");
+ return;
+ }
+
+ if (mRequestResult == PARCEL_RESULT_MULTIPLE)
+ {
+ gViewerWindow->alertXml("CannotDeedLandMultipleSelected");
+ return;
+ }
+
+ LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
+ LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center);
+ if (!region)
+ {
+ gViewerWindow->alertXml("CannotDeedLandNoRegion");
+ return;
+ }
+
+ /*
+ if(!gAgent.isGodlike())
+ {
+ if((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
+ && (mParcel->getOwnerID() != region->getOwner()))
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[REGION]"] = region->getName();
+ gViewerWindow->alertXml("CannotDeedLandNoTransfer", args);
+ return;
+ }
+ }
+ */
+
+ deedLandToGroup();
+}
+void LLViewerParcelMgr::reclaimParcel()
+{
+ LLParcel* parcel = gParcelMgr->getSelectedParcel();
+ LLViewerRegion* regionp = gParcelMgr->getSelectionRegion();
+ if(parcel && parcel->getOwnerID().notNull()
+ && (parcel->getOwnerID() != gAgent.getID())
+ && regionp && (regionp->getOwner() == gAgent.getID()))
+ {
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessage("ParcelReclaim");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("Data");
+ msg->addS32("LocalID", parcel->getLocalID());
+ msg->sendReliable(regionp->getHost());
+ }
+}
+
+// static
+void LLViewerParcelMgr::releaseAlertCB(S32 option, void *)
+{
+ if (option == 0)
+ {
+ // Send the release message, not a force
+ gParcelMgr->sendParcelRelease();
+ }
+}
+
+void LLViewerParcelMgr::buyPass()
+{
+ LLParcel* parcel = getSelectedParcel();
+ if (!parcel) return;
+
+ LLViewerRegion* region = getSelectionRegion();
+ if (!region) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_ParcelBuyPass);
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_ParcelData);
+ msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
+ msg->sendReliable( region->getHost() );
+}
+
+//Tells whether we are allowed to buy a pass or not
+BOOL LLViewerParcelMgr::isCollisionBanned()
+{
+ if ((mCollisionBanned == BA_ALLOWED) || (mCollisionBanned == BA_NOT_ON_LIST))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+// This implementation should mirror LLSimParcelMgr::isParcelOwnedBy
+// static
+BOOL LLViewerParcelMgr::isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power)
+{
+ if (!parcelp)
+ {
+ return FALSE;
+ }
+
+ // Gods can always assume ownership.
+ if (gAgent.isGodlike())
+ {
+ return TRUE;
+ }
+
+ // The owner of a parcel automatically gets all powersr.
+ if (parcelp->getOwnerID() == gAgent.getID())
+ {
+ return TRUE;
+ }
+
+ // Only gods can assume 'ownership' of public land.
+ if (parcelp->isPublic())
+ {
+ return FALSE;
+ }
+
+ // Return whether or not the agent has group_proxy_power powers in the
+ // parcel's group.
+ return gAgent.hasPowerInGroup(parcelp->getOwnerID(), group_proxy_power);
+}
+
+// This implementation should mirror llSimParcelMgr::isParcelModifiableBy
+// static
+BOOL LLViewerParcelMgr::isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power)
+{
+ // If the agent can assume ownership, it is probably modifiable.
+ BOOL rv = FALSE;
+ if (parcelp)
+ {
+ // *FIX: This should only work for leased parcels, but group owned
+ // parcels cannot be OS_LEASED yet. Phoenix 2003-12-15.
+ rv = isParcelOwnedByAgent(parcelp, group_proxy_power);
+
+ // ... except for the case that the parcel is not OS_LEASED for agent-owned parcels.
+ if( (gAgent.getID() == parcelp->getOwnerID())
+ && !gAgent.isGodlike()
+ && (parcelp->getOwnershipStatus() != LLParcel::OS_LEASED) )
+ {
+ rv = FALSE;
+ }
+ }
+ return rv;
+}
+
+void sanitize_corners(const LLVector3d &corner1,
+ const LLVector3d &corner2,
+ LLVector3d &west_south_bottom,
+ LLVector3d &east_north_top)
+{
+ west_south_bottom.mdV[VX] = llmin( corner1.mdV[VX], corner2.mdV[VX] );
+ west_south_bottom.mdV[VY] = llmin( corner1.mdV[VY], corner2.mdV[VY] );
+ west_south_bottom.mdV[VZ] = llmin( corner1.mdV[VZ], corner2.mdV[VZ] );
+
+ east_north_top.mdV[VX] = llmax( corner1.mdV[VX], corner2.mdV[VX] );
+ east_north_top.mdV[VY] = llmax( corner1.mdV[VY], corner2.mdV[VY] );
+ east_north_top.mdV[VZ] = llmax( corner1.mdV[VZ], corner2.mdV[VZ] );
+}
+
diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h
new file mode 100644
index 0000000000..d9823c82a0
--- /dev/null
+++ b/indra/newview/llviewerparcelmgr.h
@@ -0,0 +1,327 @@
+/**
+ * @file llviewerparcelmgr.h
+ * @brief Viewer-side representation of owned land
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARCELMGR_H
+#define LL_LLVIEWERPARCELMGR_H
+
+#include "v3dmath.h"
+#include "lldarray.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+
+class LLUUID;
+class LLMessageSystem;
+class LLParcel;
+class LLViewerRegion;
+
+// Constants for sendLandOwner
+//const U32 NO_NEIGHBOR_JOIN = 0x0;
+//const U32 ALL_NEIGHBOR_JOIN = U32( NORTH_MASK
+// | SOUTH_MASK
+// | EAST_MASK
+// | WEST_MASK);
+
+const F32 PARCEL_POST_HEIGHT = 0.666f;
+//const F32 PARCEL_POST_HEIGHT = 20.f;
+
+// Specify the type of land transfer taking place
+//enum ELandTransferType
+//{
+// LTT_RELEASE_LAND = 0x1,
+// LTT_CLAIM_LAND = 0x2,
+// LTT_BUY_LAND = 0x4,
+// LTT_DEED_LAND = 0x8,
+// LTT_FOR_GROUP = 0x16
+//};
+
+// Base class for people who want to "observe" changes in the viewer
+// parcel selection.
+class LLParcelObserver
+{
+public:
+ virtual ~LLParcelObserver() {};
+ virtual void changed() = 0;
+};
+
+class LLViewerParcelMgr
+{
+public:
+ LLViewerParcelMgr();
+ ~LLViewerParcelMgr();
+
+ void destroyGL();
+ void restoreGL();
+
+ BOOL selectionEmpty() const;
+ F32 getSelectionWidth() const { return F32(mEastNorth.mdV[VX] - mWestSouth.mdV[VX]); }
+ F32 getSelectionHeight() const { return F32(mEastNorth.mdV[VY] - mWestSouth.mdV[VY]); }
+ BOOL getSelection(LLVector3d &min, LLVector3d &max) { min = mWestSouth; max = mEastNorth; return !selectionEmpty();}
+ LLViewerRegion* getSelectionRegion();
+
+ void getDisplayInfo(S32* area, S32* claim, S32* rent, BOOL* for_sale, F32* dwell);
+
+ void getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean);
+
+ // Does the selection have multiple land owners in it?
+ BOOL getMultipleOwners() const;
+
+ // Is the entire parcel selected, or just a part?
+ BOOL getWholeParcelSelected() const;
+
+ // Returns area that will actually be claimed in meters squared.
+ S32 getClaimableArea() const;
+ bool hasOthersSelected() const;
+
+ // Returns selected area
+ S32 getSelectedArea() const;
+
+ void resetSegments(U8* segments);
+
+ // write a rectangle's worth of line segments into the highlight array
+ void writeHighlightSegments(F32 west, F32 south, F32 east, F32 north);
+
+ // Write highlight segments from a packed bitmap of the appropriate
+ // parcel.
+ void writeSegmentsFromBitmap(U8* bitmap, U8* segments);
+
+ void writeAgentParcelFromBitmap(U8* bitmap);
+
+ // Select the collision parcel
+ void selectCollisionParcel();
+
+ // Select the parcel at a specific point
+ void selectParcelAt(const LLVector3d& pos_global);
+
+ // Take the current rectangle select, and select the parcel contained
+ // within it.
+ void selectParcelInRectangle();
+
+ // Select a piece of land
+ void selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
+ BOOL snap_to_parcel);
+
+ // Clear the selection, and stop drawing the highlight.
+ void deselectLand();
+
+ void addObserver(LLParcelObserver* observer);
+ void removeObserver(LLParcelObserver* observer);
+ void notifyObservers();
+
+ void setSelectionVisible(BOOL visible) { mRenderSelection = visible; }
+
+ BOOL isOwnedAt(const LLVector3d& pos_global) const;
+ BOOL isOwnedSelfAt(const LLVector3d& pos_global) const;
+ BOOL isOwnedOtherAt(const LLVector3d& pos_global) const;
+ BOOL isSoundLocal(const LLVector3d &pos_global) const;
+
+ BOOL canHearSound(const LLVector3d &pos_global) const;
+
+ LLParcel *getSelectedParcel() const;
+ LLParcel *getAgentParcel() const;
+
+ BOOL inAgentParcel(const LLVector3d &pos_global) const;
+
+ // Return the number of grid units that are owned by you within
+ // the selection (computed by server).
+ S32 getSelfCount() const { return mSelectedSelfCount; }
+
+ // Returns a pointer only when it has valid data.
+ LLParcel* getHoverParcel() const;
+
+ LLParcel* getCollisionParcel() const;
+
+ BOOL agentCanTakeDamage() const;
+ BOOL agentCanFly() const;
+ F32 agentDrawDistance() const;
+ BOOL agentCanBuild() const;
+
+ F32 getHoverParcelWidth() const
+ { return F32(mHoverEastNorth.mdV[VX] - mHoverWestSouth.mdV[VX]); }
+
+ F32 getHoverParcelHeight() const
+ { return F32(mHoverEastNorth.mdV[VY] - mHoverWestSouth.mdV[VY]); }
+
+ // UTILITIES
+ void render();
+ void renderParcelCollision();
+
+ void renderRect( const LLVector3d &west_south_bottom,
+ const LLVector3d &east_north_top );
+ void renderOneSegment(F32 x1, F32 y1, F32 x2, F32 y2, F32 height, U8 direction, LLViewerRegion* regionp);
+ void renderHighlightSegments(const U8* segments, LLViewerRegion* regionp);
+ void renderCollisionSegments(U8* segments, BOOL use_pass, LLViewerRegion* regionp);
+
+ void sendParcelGodForceOwner(const LLUUID& owner_id);
+
+ // make the selected parcel a content parcel.
+ void sendParcelGodForceToContent();
+
+ // Take the selected parcel, and toggle it's 'reserved for newbie'
+ // status.
+ // *NOTE: There is no longer a newbie toggle. It is a linden sale
+ // for newbie now.
+ //void toggleParcelGodReserveForNewbie();
+
+ // Pack information about this parcel and send it to the region
+ // containing the southwest corner of the selection.
+ // If want_reply_to_update, simulator will send back a ParcelProperties
+ // message.
+ void sendParcelPropertiesUpdate(LLParcel* parcel, BOOL want_reply_to_update);
+
+ // Takes an Access List flag, like AL_ACCESS or AL_BAN
+ void sendParcelAccessListUpdate(U32 which);
+
+ // Takes an Access List flag, like AL_ACCESS or AL_BAN
+ void sendParcelAccessListRequest(U32 flags);
+
+ // Dwell is not part of the usual parcel update information because the
+ // simulator doesn't actually know the per-parcel dwell. Ack! We have
+ // to get it out of the database.
+ void sendParcelDwellRequest();
+
+ // If the point is outside the current hover parcel, request more data
+ void requestHoverParcelProperties(const LLVector3d& pos_global);
+
+ bool canAgentBuyParcel(LLParcel*, bool forGroup) const;
+
+// void startClaimLand(BOOL is_for_group = FALSE);
+ void startBuyLand(BOOL is_for_group = FALSE);
+ void startSellLand();
+ void startReleaseLand();
+ void startDivideLand();
+ void startJoinLand();
+ void startDeedLandToGroup();
+ void reclaimParcel();
+
+ void buyPass();
+
+ // Buying Land
+
+ class ParcelBuyInfo;
+ ParcelBuyInfo* setupParcelBuy(const LLUUID& agent_id,
+ const LLUUID& session_id,
+ const LLUUID& group_id,
+ BOOL is_group_owned,
+ BOOL is_claim,
+ BOOL remove_contribution);
+ // callers responsibility to call deleteParcelBuy() on return value
+ void sendParcelBuy(ParcelBuyInfo*);
+ void deleteParcelBuy(ParcelBuyInfo*&);
+
+ void sendParcelDeed(const LLUUID& group_id);
+
+ // Send the ParcelRelease message
+ void sendParcelRelease();
+
+ // accessors for mAgentParcel
+ const char *getAgentParcelName() const;
+
+ // Create a landmark at the "appropriate" location for the
+ // currently selected parcel.
+ // *NOTE: Taken out 2005-03-21. Phoenix.
+ //void makeLandmarkAtSelection();
+
+ static void processParcelOverlay(LLMessageSystem *msg, void **user_data);
+ static void processParcelProperties(LLMessageSystem *msg, void **user_data);
+ static void processParcelAccessListReply(LLMessageSystem *msg, void **user);
+ static void processParcelDwellReply(LLMessageSystem *msg, void **user);
+
+ void dump();
+
+ // Whether or not the collision border around the parcel is there because
+ // the agent is banned or not in the allowed group
+ BOOL isCollisionBanned();
+
+ static BOOL isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power);
+ static BOOL isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power);
+
+protected:
+ static void releaseAlertCB(S32 option, void *data);
+
+ // If the user is claiming land and the current selection
+ // borders a piece of land the user already owns, ask if he
+ // wants to join this land to the other piece.
+ //void askJoinIfNecessary(ELandTransferType land_transfer_type);
+ //static void joinAlertCB(S32 option, void* data);
+
+ //void buyAskMoney(ELandTransferType land_transfer_type);
+
+ // move land from current owner to it's group.
+ void deedLandToGroup();
+
+ static void claimAlertCB(S32 option, void* data);
+ static void buyAlertCB(S32 option, void* data);
+ static void deedAlertCB(S32 option, void*);
+
+ static void callbackDivideLand(S32 option, void* data);
+ static void callbackJoinLand(S32 option, void* data);
+
+ //void finishClaim(BOOL user_to_user_sale, U32 join);
+
+private:
+ BOOL mSelected;
+ BOOL mSelectedMultipleOwners;
+ BOOL mWholeParcelSelected;
+ S32 mSelectedSelfCount;
+ S32 mSelectedOtherCount;
+ S32 mSelectedPublicCount;
+
+ LLParcel *mParcel; // selected parcel info
+ S32 mRequestResult; // result of last parcel request
+ LLVector3d mWestSouth;
+ LLVector3d mEastNorth;
+ F32 mSelectedDwell;
+
+ LLParcel *mAgentParcel; // info for parcel agent is in
+ S32 mAgentParcelSequenceID; // incrementing counter to suppress out of order updates
+
+ LLParcel* mHoverParcel;
+ S32 mHoverRequestResult;
+ LLVector3d mHoverWestSouth;
+ LLVector3d mHoverEastNorth;
+
+ LLDynamicArray<LLParcelObserver*> mObservers;
+
+ // Array of pieces of parcel edges to potentially draw
+ // Has (parcels_per_edge + 1) * (parcels_per_edge + 1) elements so
+ // we can represent edges of the grid.
+ // WEST_MASK = draw west edge
+ // SOUTH_MASK = draw south edge
+ S32 mParcelsPerEdge;
+ U8* mHighlightSegments;
+ U8* mAgentParcelOverlay;
+
+ // Raw data buffer for unpacking parcel overlay chunks
+ // Size = parcels_per_edge * parcels_per_edge / parcel_overlay_chunks
+ static U8* sPackedOverlay;
+
+ // Watch for pending collisions with a parcel you can't access.
+ // If it's coming, draw the parcel's boundaries.
+ LLParcel* mCollisionParcel;
+ U8* mCollisionSegments;
+ BOOL mRenderCollision;
+ BOOL mRenderSelection;
+ S32 mCollisionBanned;
+ LLFrameTimer mCollisionTimer;
+ LLUUID mBlockedImageID;
+ LLUUID mPassImageID;
+ LLPointer<LLViewerImage> mBlockedImage;
+ LLPointer<LLViewerImage> mPassImage;
+
+ // Media
+ S32 mMediaParcelId;
+ U64 mMediaRegionId;
+};
+
+extern LLViewerParcelMgr *gParcelMgr;
+
+void sanitize_corners(const LLVector3d &corner1, const LLVector3d &corner2,
+ LLVector3d &west_south_bottom, LLVector3d &east_north_top);
+
+#endif
diff --git a/indra/newview/llviewerparceloverlay.cpp b/indra/newview/llviewerparceloverlay.cpp
new file mode 100644
index 0000000000..2c04c073db
--- /dev/null
+++ b/indra/newview/llviewerparceloverlay.cpp
@@ -0,0 +1,850 @@
+/**
+ * @file llviewerparceloverlay.cpp
+ * @brief LLViewerParcelOverlay class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerparceloverlay.h"
+
+// indra includes
+#include "llparcel.h"
+#include "llgl.h"
+#include "v4color.h"
+#include "v2math.h"
+
+// newview includes
+#include "llviewerimage.h"
+#include "llviewercontrol.h"
+#include "llsurface.h"
+#include "llviewerregion.h"
+#include "llagent.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llglheaders.h"
+
+const U8 OVERLAY_IMG_COMPONENTS = 4;
+
+LLViewerParcelOverlay::LLViewerParcelOverlay(LLViewerRegion* region, F32 region_width_meters)
+: mRegion( region ),
+ mParcelGridsPerEdge( S32( region_width_meters / PARCEL_GRID_STEP_METERS ) ),
+ mDirty( FALSE ),
+ mTimeSinceLastUpdate(),
+ mOverlayTextureIdx(-1),
+ mLineImageID( gViewerArt.getString("propertyline.tga") ),
+ mVertexCount(0),
+ mVertexArray(NULL),
+ mColorArray(NULL)
+// mTexCoordArray(NULL),
+{
+ // Create a texture to hold color information.
+ // 4 components
+ // Use mipmaps = FALSE, clamped, NEAREST filter, for sharp edges
+ mTexture = new LLImageGL(FALSE);
+ mImageRaw = new LLImageRaw(mParcelGridsPerEdge, mParcelGridsPerEdge, OVERLAY_IMG_COMPONENTS);
+ mTexture->createGLTexture(0, mImageRaw);
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+ mTexture->bind(0);
+ mTexture->setClamp(TRUE, TRUE);
+ mTexture->setMipFilterNearest(TRUE);
+
+ //
+ // Initialize the GL texture with empty data.
+ //
+ // Create the base texture.
+ U8 *raw = mImageRaw->getData();
+ const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge * OVERLAY_IMG_COMPONENTS;
+ for (S32 i = 0; i < COUNT; i++)
+ {
+ raw[i] = 0;
+ }
+ mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
+
+ // Create storage for ownership information from simulator
+ // and initialize it.
+ mOwnership = new U8[ mParcelGridsPerEdge * mParcelGridsPerEdge ];
+ for (S32 i = 0; i < mParcelGridsPerEdge * mParcelGridsPerEdge; i++)
+ {
+ mOwnership[i] = PARCEL_PUBLIC;
+ }
+
+ // Make sure the texture matches the ownership information.
+ updateOverlayTexture();
+}
+
+
+LLViewerParcelOverlay::~LLViewerParcelOverlay()
+{
+ delete[] mOwnership;
+ mOwnership = NULL;
+
+ delete[] mVertexArray;
+ mVertexArray = NULL;
+
+ delete[] mColorArray;
+ mColorArray = NULL;
+
+// JC No textures.
+// delete mTexCoordArray;
+// mTexCoordArray = NULL;
+
+ mImageRaw = NULL;
+}
+
+//---------------------------------------------------------------------------
+// ACCESSORS
+//---------------------------------------------------------------------------
+BOOL LLViewerParcelOverlay::isOwned(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_PUBLIC != ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedSelf(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_SELF == ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedGroup(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return (PARCEL_GROUP == ownership(row, column));
+}
+
+BOOL LLViewerParcelOverlay::isOwnedOther(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ U8 overlay = ownership(row, column);
+ return (PARCEL_OWNED == overlay || PARCEL_FOR_SALE == overlay);
+}
+
+BOOL LLViewerParcelOverlay::isSoundLocal(const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return PARCEL_SOUND_LOCAL & mOwnership[row * mParcelGridsPerEdge + column];
+}
+
+U8 LLViewerParcelOverlay::ownership( const LLVector3& pos) const
+{
+ S32 row = S32(pos.mV[VY] / PARCEL_GRID_STEP_METERS);
+ S32 column = S32(pos.mV[VX] / PARCEL_GRID_STEP_METERS);
+ return ownership(row, column);
+}
+
+F32 LLViewerParcelOverlay::getOwnedRatio() const
+{
+ S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 total = 0;
+
+ for (S32 i = 0; i < size; i++)
+ {
+ if ((mOwnership[i] & PARCEL_COLOR_MASK) != PARCEL_PUBLIC)
+ {
+ total++;
+ }
+ }
+
+ return (F32)total / (F32)size;
+
+}
+
+//---------------------------------------------------------------------------
+// MANIPULATORS
+//---------------------------------------------------------------------------
+
+// Color tables for owned land
+// Available = index 0
+// Other = index 1
+// Group = index 2
+// Self = index 3
+
+// Make sure the texture colors match the ownership data.
+// Note: Assumes that the ownership array and
+void LLViewerParcelOverlay::updateOverlayTexture()
+{
+ if (mOverlayTextureIdx < 0 && mDirty)
+ {
+ mOverlayTextureIdx = 0;
+ }
+ if (mOverlayTextureIdx < 0)
+ {
+ return;
+ }
+ // Can do this because gColors are actually stored as LLColor4U
+ const LLColor4U avail = gColors.getColor4U("PropertyColorAvail");
+ const LLColor4U owned = gColors.getColor4U("PropertyColorOther");
+ const LLColor4U group = gColors.getColor4U("PropertyColorGroup");
+ const LLColor4U self = gColors.getColor4U("PropertyColorSelf");
+ const LLColor4U for_sale = gColors.getColor4U("PropertyColorForSale");
+ const LLColor4U auction = gColors.getColor4U("PropertyColorAuction");
+
+ // Create the base texture.
+ U8 *raw = mImageRaw->getData();
+ const S32 COUNT = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 max = mOverlayTextureIdx + mParcelGridsPerEdge;
+ if (max > COUNT) max = COUNT;
+ S32 pixel_index = mOverlayTextureIdx*OVERLAY_IMG_COMPONENTS;
+ S32 i;
+ for (i = mOverlayTextureIdx; i < max; i++)
+ {
+ U8 ownership = mOwnership[i];
+
+ U8 r,g,b,a;
+
+ // Color stored in low three bits
+ switch( ownership & 0x7 )
+ {
+ case PARCEL_PUBLIC:
+ r = avail.mV[VRED];
+ g = avail.mV[VGREEN];
+ b = avail.mV[VBLUE];
+ a = avail.mV[VALPHA];
+ break;
+ case PARCEL_OWNED:
+ r = owned.mV[VRED];
+ g = owned.mV[VGREEN];
+ b = owned.mV[VBLUE];
+ a = owned.mV[VALPHA];
+ break;
+ case PARCEL_GROUP:
+ r = group.mV[VRED];
+ g = group.mV[VGREEN];
+ b = group.mV[VBLUE];
+ a = group.mV[VALPHA];
+ break;
+ case PARCEL_SELF:
+ r = self.mV[VRED];
+ g = self.mV[VGREEN];
+ b = self.mV[VBLUE];
+ a = self.mV[VALPHA];
+ break;
+ case PARCEL_FOR_SALE:
+ r = for_sale.mV[VRED];
+ g = for_sale.mV[VGREEN];
+ b = for_sale.mV[VBLUE];
+ a = for_sale.mV[VALPHA];
+ break;
+ case PARCEL_AUCTION:
+ r = auction.mV[VRED];
+ g = auction.mV[VGREEN];
+ b = auction.mV[VBLUE];
+ a = auction.mV[VALPHA];
+ break;
+ default:
+ r = self.mV[VRED];
+ g = self.mV[VGREEN];
+ b = self.mV[VBLUE];
+ a = self.mV[VALPHA];
+ break;
+ }
+
+ raw[pixel_index + 0] = r;
+ raw[pixel_index + 1] = g;
+ raw[pixel_index + 2] = b;
+ raw[pixel_index + 3] = a;
+
+ pixel_index += OVERLAY_IMG_COMPONENTS;
+ }
+
+ // Copy data into GL texture from raw data
+ if (i >= COUNT)
+ {
+ mTexture->setSubImage(mImageRaw, 0, 0, mParcelGridsPerEdge, mParcelGridsPerEdge);
+ mOverlayTextureIdx = -1;
+ }
+ else
+ {
+ mOverlayTextureIdx = i;
+ }
+}
+
+
+void LLViewerParcelOverlay::uncompressLandOverlay(S32 chunk, U8 *packed_overlay)
+{
+ // Unpack the message data into the ownership array
+ S32 size = mParcelGridsPerEdge * mParcelGridsPerEdge;
+ S32 chunk_size = size / PARCEL_OVERLAY_CHUNKS;
+
+ memcpy(mOwnership + chunk*chunk_size, packed_overlay, chunk_size);
+
+ // Force property lines and overlay texture to update
+ setDirty();
+}
+
+
+void LLViewerParcelOverlay::updatePropertyLines()
+{
+ if (!gSavedSettings.getBOOL("ShowPropertyLines"))
+ return;
+
+ S32 row, col;
+
+ // Can do this because gColors are actually stored as LLColor4U
+ const LLColor4U self_coloru = gColors.getColor4U("PropertyColorSelf");
+ const LLColor4U other_coloru = gColors.getColor4U("PropertyColorOther");
+ const LLColor4U group_coloru = gColors.getColor4U("PropertyColorGroup");
+ const LLColor4U for_sale_coloru = gColors.getColor4U("PropertyColorForSale");
+ const LLColor4U auction_coloru = gColors.getColor4U("PropertyColorAuction");
+
+ // Build into dynamic arrays, then copy into static arrays.
+ LLDynamicArray<LLVector3, 256> new_vertex_array;
+ LLDynamicArray<LLColor4U, 256> new_color_array;
+ LLDynamicArray<LLVector2, 256> new_coord_array;
+
+ U8 overlay = 0;
+ BOOL add_edge = FALSE;
+ const F32 GRID_STEP = PARCEL_GRID_STEP_METERS;
+ const S32 GRIDS_PER_EDGE = mParcelGridsPerEdge;
+
+ for (row = 0; row < GRIDS_PER_EDGE; row++)
+ {
+ for (col = 0; col < GRIDS_PER_EDGE; col++)
+ {
+ overlay = mOwnership[row*GRIDS_PER_EDGE+col];
+
+ F32 left = col*GRID_STEP;
+ F32 right = left+GRID_STEP;
+
+ F32 bottom = row*GRID_STEP;
+ F32 top = bottom+GRID_STEP;
+
+ // West edge
+ if (overlay & PARCEL_WEST_LINE)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, WEST, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // East edge
+ if (col < GRIDS_PER_EDGE-1)
+ {
+ U8 east_overlay = mOwnership[row*GRIDS_PER_EDGE+col+1];
+ add_edge = east_overlay & PARCEL_WEST_LINE;
+ }
+ else
+ {
+ add_edge = TRUE;
+ }
+
+ if (add_edge)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ right, bottom, EAST, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+ // South edge
+ if (overlay & PARCEL_SOUTH_LINE)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, bottom, SOUTH, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ // North edge
+ if (row < GRIDS_PER_EDGE-1)
+ {
+ U8 north_overlay = mOwnership[(row+1)*GRIDS_PER_EDGE+col];
+ add_edge = north_overlay & PARCEL_SOUTH_LINE;
+ }
+ else
+ {
+ add_edge = TRUE;
+ }
+
+ if (add_edge)
+ {
+ switch(overlay & PARCEL_COLOR_MASK)
+ {
+ case PARCEL_SELF:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, self_coloru);
+ break;
+ case PARCEL_GROUP:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, group_coloru);
+ break;
+ case PARCEL_OWNED:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, other_coloru);
+ break;
+ case PARCEL_FOR_SALE:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, for_sale_coloru);
+ break;
+ case PARCEL_AUCTION:
+ addPropertyLine(new_vertex_array, new_color_array, new_coord_array,
+ left, top, NORTH, auction_coloru);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ // Now copy into static arrays for faster rendering.
+ // Attempt to recycle old arrays if possible to avoid memory
+ // shuffling.
+ S32 new_vertex_count = new_vertex_array.count();
+
+ // NOTE: If the new_vertex_count is 0 and wasn't 0 previously
+ // the arrays are still allocated as the arrays aren't set to NULL, etc.
+ // This won't cause any problems, but might waste a few cycles copying over
+ // old data. - jwolk
+ if ( !(mVertexArray && mColorArray && new_vertex_count == mVertexCount) && new_vertex_count > 0 )
+ {
+ // ...need new arrays
+ delete[] mVertexArray;
+ delete[] mColorArray;
+
+ mVertexCount = new_vertex_count;
+
+ mVertexArray = new F32[3 * mVertexCount];
+ mColorArray = new U8 [4 * mVertexCount];
+ }
+
+ // Copy the new data into the arrays
+ S32 i;
+ F32* vertex = mVertexArray;
+ for (i = 0; i < mVertexCount; i++)
+ {
+ const LLVector3& point = new_vertex_array.get(i);
+ *vertex = point.mV[VX];
+ vertex++;
+ *vertex = point.mV[VY];
+ vertex++;
+ *vertex = point.mV[VZ];
+ vertex++;
+ }
+
+ U8* colorp = mColorArray;
+ for (i = 0; i < mVertexCount; i++)
+ {
+ const LLColor4U& color = new_color_array.get(i);
+ *colorp = color.mV[VRED];
+ colorp++;
+ *colorp = color.mV[VGREEN];
+ colorp++;
+ *colorp = color.mV[VBLUE];
+ colorp++;
+ *colorp = color.mV[VALPHA];
+ colorp++;
+ }
+
+ // Everything's clean now
+ mDirty = FALSE;
+}
+
+
+void LLViewerParcelOverlay::addPropertyLine(
+ LLDynamicArray<LLVector3, 256>& vertex_array,
+ LLDynamicArray<LLColor4U, 256>& color_array,
+ LLDynamicArray<LLVector2, 256>& coord_array,
+ const F32 start_x, const F32 start_y,
+ const U32 edge,
+ const LLColor4U& color)
+{
+ LLColor4U underwater( color );
+ underwater.mV[VALPHA] /= 2;
+
+ LLSurface& land = mRegion->getLand();
+
+ F32 dx;
+ F32 dy;
+ F32 tick_dx;
+ F32 tick_dy;
+ //const F32 LINE_WIDTH = 0.125f;
+ const F32 LINE_WIDTH = 0.0625f;
+
+ switch(edge)
+ {
+ case WEST:
+ dx = 0.f;
+ dy = 1.f;
+ tick_dx = LINE_WIDTH;
+ tick_dy = 0.f;
+ break;
+
+ case EAST:
+ dx = 0.f;
+ dy = 1.f;
+ tick_dx = -LINE_WIDTH;
+ tick_dy = 0.f;
+ break;
+
+ case NORTH:
+ dx = 1.f;
+ dy = 0.f;
+ tick_dx = 0.f;
+ tick_dy = -LINE_WIDTH;
+ break;
+
+ case SOUTH:
+ dx = 1.f;
+ dy = 0.f;
+ tick_dx = 0.f;
+ tick_dy = LINE_WIDTH;
+ break;
+
+ default:
+ llerrs << "Invalid edge in addPropertyLine" << llendl;
+ return;
+ }
+
+ F32 outside_x = start_x;
+ F32 outside_y = start_y;
+ F32 outside_z = 0.f;
+ F32 inside_x = start_x + tick_dx;
+ F32 inside_y = start_y + tick_dy;
+ F32 inside_z = 0.f;
+
+ // First part, only one vertex
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * LINE_WIDTH;
+ inside_y += dy * LINE_WIDTH;
+
+ outside_x += dx * LINE_WIDTH;
+ outside_y += dy * LINE_WIDTH;
+
+ // Then the "actual edge"
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * (dx - LINE_WIDTH);
+ inside_y += dy * (dy - LINE_WIDTH);
+
+ outside_x += dx * (dx - LINE_WIDTH);
+ outside_y += dy * (dy - LINE_WIDTH);
+
+ // Middle part, full width
+ S32 i;
+ const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
+ for (i = 1; i < GRID_STEP; i++)
+ {
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx;
+ inside_y += dy;
+
+ outside_x += dx;
+ outside_y += dy;
+ }
+
+ // Extra buffer for edge
+ inside_x -= dx * LINE_WIDTH;
+ inside_y -= dy * LINE_WIDTH;
+
+ outside_x -= dx * LINE_WIDTH;
+ outside_y -= dy * LINE_WIDTH;
+
+ inside_z = land.resolveHeightRegion( inside_x, inside_y );
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (inside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(inside_x, inside_y, inside_z) );
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+
+ coord_array.put( LLVector2(outside_x - start_x, 1.f) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+
+ inside_x += dx * LINE_WIDTH;
+ inside_y += dy * LINE_WIDTH;
+
+ outside_x += dx * LINE_WIDTH;
+ outside_y += dy * LINE_WIDTH;
+
+ // Last edge is not drawn to the edge
+ outside_z = land.resolveHeightRegion( outside_x, outside_y );
+
+ if (outside_z > 20.f) color_array.put( color );
+ else color_array.put( underwater );
+
+ vertex_array.put( LLVector3(outside_x, outside_y, outside_z) );
+ coord_array.put( LLVector2(outside_x - start_x, 0.f) );
+}
+
+
+void LLViewerParcelOverlay::setDirty()
+{
+ mDirty = TRUE;
+}
+
+void LLViewerParcelOverlay::idleUpdate(bool force_update)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ return;
+ }
+ if (mOverlayTextureIdx >= 0 && (!(mDirty && force_update)))
+ {
+ // We are in the middle of updating the overlay texture
+ updateOverlayTexture();
+ return;
+ }
+ // Only if we're dirty and it's been a while since the last update.
+ if (mDirty)
+ {
+ if (force_update || mTimeSinceLastUpdate.getElapsedTimeF32() > 4.0f)
+ {
+ updateOverlayTexture();
+ updatePropertyLines();
+ mTimeSinceLastUpdate.reset();
+ }
+ }
+}
+
+S32 LLViewerParcelOverlay::renderPropertyLines ()
+{
+ if (!gSavedSettings.getBOOL("ShowPropertyLines"))
+ {
+ return 0;
+ }
+ if (!mVertexArray || !mColorArray)
+ {
+ return 0;
+ }
+
+ LLSurface& land = mRegion->getLand();
+
+ LLGLSUIDefault gls_ui; // called from pipeline
+ LLGLSNoTexture gls_no_texture;
+ LLGLDepthTest mDepthTest(GL_TRUE);
+
+ // JC - This doesn't work.
+ //gGLSUITextureDepth.set();
+ //LLViewerImage* image = gImageList.getImage( mLineImageID );
+ //image->bindTexture();
+
+ // Find camera height off the ground (not from zero)
+ F32 ground_height_at_camera = land.resolveHeightGlobal( gAgent.getCameraPositionGlobal() );
+ F32 camera_z = gCamera->getOrigin().mV[VZ];
+ F32 camera_height = camera_z - ground_height_at_camera;
+
+ camera_height = llclamp(camera_height, 0.f, 100.f);
+
+ // Pull lines toward camera by 1 cm per meter off the ground.
+ const LLVector3& CAMERA_AT = gCamera->getAtAxis();
+ F32 pull_toward_camera_scale = 0.01f * camera_height;
+ LLVector3 pull_toward_camera = CAMERA_AT;
+ pull_toward_camera *= -pull_toward_camera_scale;
+
+ // Always fudge a little vertically.
+ pull_toward_camera.mV[VZ] += 0.01f;
+
+ glMatrixMode( GL_MODELVIEW );
+ glPushMatrix();
+
+ // Move to appropriate region coords
+ LLVector3 origin = mRegion->getOriginAgent();
+ glTranslatef( origin.mV[VX], origin.mV[VY], origin.mV[VZ] );
+
+ glTranslatef(pull_toward_camera.mV[VX], pull_toward_camera.mV[VY],
+ pull_toward_camera.mV[VZ]);
+
+ // Include +1 because vertices are fenceposts.
+ // *2 because it's a quad strip
+ const S32 GRID_STEP = S32( PARCEL_GRID_STEP_METERS );
+ const S32 vertex_per_edge = 3 + 2 * (GRID_STEP-1) + 3;
+
+ /* JC - Don't do this. Unbinding AGP stalls the draw process,
+ dropping frame rate. Not unbinding AGP causes random crashes
+ on nVidia cards due to binding non-AGP arrays.
+
+ gPipeline.unbindAGP();
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, mVertexArray );
+ glColorPointer( 4, GL_UNSIGNED_BYTE, 0, mColorArray );
+
+ S32 i;
+ for (i = 0; i < mVertexCount; i += vertex_per_edge)
+ {
+ // Each edge is several vertices
+ glDrawArrays(GL_LINE_STRIP, i, vertex_per_edge);
+ }
+
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+ */
+
+ // Stomp the camera into two dimensions
+ LLVector3 camera_region = mRegion->getPosRegionFromGlobal( gAgent.getCameraPositionGlobal() );
+
+ // Set up a cull plane 2 * PARCEL_GRID_STEP_METERS behind
+ // the camera. The cull plane normal is the camera's at axis.
+ LLVector3 cull_plane_point = gCamera->getAtAxis();
+ cull_plane_point *= -2.f * PARCEL_GRID_STEP_METERS;
+ cull_plane_point += camera_region;
+
+ LLVector3 vertex;
+
+ const S32 BYTES_PER_COLOR = 4;
+ const S32 FLOATS_PER_VERTEX = 3;
+ //const S32 FLOATS_PER_TEX_COORD = 2;
+ S32 i, j;
+ S32 drawn = 0;
+ F32* vertexp;
+ U8* colorp;
+
+ const F32 PROPERTY_LINE_CLIP_DIST = 256.f;
+
+ for (i = 0; i < mVertexCount; i += vertex_per_edge)
+ {
+ colorp = mColorArray + BYTES_PER_COLOR * i;
+ vertexp = mVertexArray + FLOATS_PER_VERTEX * i;
+
+ vertex.mV[VX] = *(vertexp);
+ vertex.mV[VY] = *(vertexp+1);
+ vertex.mV[VZ] = *(vertexp+2);
+
+ if (dist_vec_squared2D(vertex, camera_region) > PROPERTY_LINE_CLIP_DIST*PROPERTY_LINE_CLIP_DIST)
+ {
+ continue;
+ }
+
+ // Destroy vertex, transform to plane-local.
+ vertex -= cull_plane_point;
+
+ // negative dot product means it is in back of the plane
+ if ( vertex * CAMERA_AT < 0.f )
+ {
+ continue;
+ }
+
+ glBegin(GL_TRIANGLE_STRIP);
+
+ for (j = 0; j < vertex_per_edge; j++)
+ {
+ // JC - This doesn't work
+ //glTexCoord2fv(mTexCoordArray + FLOATS_PER_TEX_COORD*offset);
+ glColor4ubv(colorp);
+ glVertex3fv(vertexp);
+
+ colorp += BYTES_PER_COLOR;
+ vertexp += FLOATS_PER_VERTEX;
+ }
+
+ drawn += vertex_per_edge;
+
+ glEnd();
+ }
+
+ glPopMatrix();
+
+ return drawn;
+}
diff --git a/indra/newview/llviewerparceloverlay.h b/indra/newview/llviewerparceloverlay.h
new file mode 100644
index 0000000000..2e98cde297
--- /dev/null
+++ b/indra/newview/llviewerparceloverlay.h
@@ -0,0 +1,97 @@
+/**
+ * @file llviewerparceloverlay.h
+ * @brief LLViewerParcelOverlay class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARCELOVERLAY_H
+#define LL_LLVIEWERPARCELOVERLAY_H
+
+// The ownership data for land parcels.
+// One of these structures per region.
+
+#include "lldarray.h"
+#include "llframetimer.h"
+#include "lluuid.h"
+#include "llviewerimage.h"
+
+class LLViewerRegion;
+class LLVector3;
+class LLColor4U;
+class LLVector2;
+
+class LLViewerParcelOverlay
+{
+public:
+ LLViewerParcelOverlay(LLViewerRegion* region, F32 region_width_meters);
+ ~LLViewerParcelOverlay();
+
+ // ACCESS
+ LLImageGL* getTexture() const { return mTexture; }
+
+ BOOL isOwned(const LLVector3& pos) const;
+ BOOL isOwnedSelf(const LLVector3& pos) const;
+ BOOL isOwnedGroup(const LLVector3& pos) const;
+ BOOL isOwnedOther(const LLVector3& pos) const;
+ BOOL isSoundLocal(const LLVector3& pos) const;
+
+ BOOL isBuildCameraAllowed(const LLVector3& pos) const;
+ F32 getOwnedRatio() const;
+
+ // Returns the number of vertices drawn
+ S32 renderPropertyLines();
+
+ U8 ownership( const LLVector3& pos) const;
+
+ // MANIPULATE
+ void uncompressLandOverlay(S32 chunk, U8 *compressed_overlay);
+
+ // Indicate property lines and overlay texture need to be rebuilt.
+ void setDirty();
+
+ void idleUpdate(bool update_now = false);
+
+private:
+ // This is in parcel rows and columns, not grid rows and columns
+ // Stored in bottom three bits.
+ U8 ownership(S32 row, S32 col) const
+ { return 0x7 & mOwnership[row * mParcelGridsPerEdge + col]; }
+
+ void addPropertyLine(LLDynamicArray<LLVector3, 256>& vertex_array,
+ LLDynamicArray<LLColor4U, 256>& color_array,
+ LLDynamicArray<LLVector2, 256>& coord_array,
+ const F32 start_x, const F32 start_y,
+ const U32 edge,
+ const LLColor4U& color);
+
+ void updateOverlayTexture();
+ void updatePropertyLines();
+
+private:
+ // Back pointer to the region that owns this structure.
+ LLViewerRegion* mRegion;
+
+ S32 mParcelGridsPerEdge;
+
+ LLPointer<LLImageGL> mTexture;
+ LLPointer<LLImageRaw> mImageRaw;
+
+ // Size: mParcelGridsPerEdge * mParcelGridsPerEdge
+ // Each value is 0-3, PARCEL_AVAIL to PARCEL_SELF in the two low bits
+ // and other flags in the upper bits.
+ U8 *mOwnership;
+
+ // Update propery lines and overlay texture
+ BOOL mDirty;
+ LLFrameTimer mTimeSinceLastUpdate;
+ S32 mOverlayTextureIdx;
+
+ LLUUID mLineImageID;
+ S32 mVertexCount;
+ F32* mVertexArray;
+ U8* mColorArray;
+};
+
+#endif
diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp
new file mode 100644
index 0000000000..b67062e0a9
--- /dev/null
+++ b/indra/newview/llviewerpartsim.cpp
@@ -0,0 +1,626 @@
+/**
+ * @file llviewerpartsim.cpp
+ * @brief LLViewerPart class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerpartsim.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewerobjectlist.h"
+#include "llviewerpartsource.h"
+#include "llviewerregion.h"
+#include "llvopartgroup.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+const S32 MAX_PART_COUNT = 4096;
+
+const F32 PART_SIM_BOX_SIDE = 32.f;
+const F32 PART_SIM_BOX_OFFSET = 0.5f*PART_SIM_BOX_SIDE;
+const F32 PART_SIM_BOX_RAD = 0.5f*F_SQRT3*PART_SIM_BOX_SIDE;
+
+//static
+S32 LLViewerPartSim::sMaxParticleCount = 0;
+S32 LLViewerPartSim::sParticleCount = 0;
+
+
+U32 LLViewerPart::sNextPartID = 1;
+
+LLViewerPart::LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart::~LLViewerPart()
+{
+ mPartSourcep = NULL;
+}
+
+LLViewerPart &LLViewerPart::operator=(const LLViewerPart &part)
+{
+ mPartID = part.mPartID;
+ mFlags = part.mFlags;
+ mMaxAge = part.mMaxAge;
+
+ mStartColor = part.mStartColor;
+ mEndColor = part.mEndColor;
+ mStartScale = part.mStartScale;
+ mEndScale = part.mEndScale;
+
+ mPosOffset = part.mPosOffset;
+ mParameter = part.mParameter;
+
+ mLastUpdateTime = part.mLastUpdateTime;
+ mVPCallback = part.mVPCallback;
+ mPartSourcep = part.mPartSourcep;
+
+ mImagep = part.mImagep;
+ mPosAgent = part.mPosAgent;
+ mVelocity = part.mVelocity;
+ mAccel = part.mAccel;
+ mColor = part.mColor;
+ mScale = part.mScale;
+
+
+ return *this;
+}
+
+void LLViewerPart::init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb)
+{
+ mPartID = LLViewerPart::sNextPartID;
+ LLViewerPart::sNextPartID++;
+ mFlags = 0x00f;
+ mLastUpdateTime = 0.f;
+ mMaxAge = 10.f;
+
+ mVPCallback = cb;
+ mPartSourcep = sourcep;
+
+ mImagep = imagep;
+}
+
+
+/////////////////////////////
+//
+// LLViewerPartGroup implementation
+//
+//
+
+
+LLViewerPartGroup::LLViewerPartGroup(const LLVector3 &center_agent, const F32 box_side)
+{
+ mVOPartGroupp = NULL;
+ mRegionp = gWorldPointer->getRegionFromPosAgent(center_agent);
+ if (!mRegionp)
+ {
+ //llwarns << "No region at position, using agent region!" << llendl;
+ mRegionp = gAgent.getRegion();
+ }
+ mCenterAgent = center_agent;
+ mBoxRadius = F_SQRT3*box_side*0.5f;
+
+ LLVector3 rad_vec(box_side*0.5f, box_side*0.5f, box_side*0.5f);
+ rad_vec += LLVector3(0.001f, 0.001f, 0.001f);
+ mMinObjPos = mCenterAgent - rad_vec;
+ mMaxObjPos = mCenterAgent + rad_vec;
+}
+
+LLViewerPartGroup::~LLViewerPartGroup()
+{
+ cleanup();
+ S32 count = mParticles.count();
+ S32 i;
+
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPartSourcep = NULL;
+ }
+ mParticles.reset();
+ LLViewerPartSim::decPartCount(count);
+}
+
+void LLViewerPartGroup::cleanup()
+{
+ if (mVOPartGroupp)
+ {
+ if (!mVOPartGroupp->isDead())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ }
+ mVOPartGroupp = NULL;
+ }
+}
+
+BOOL LLViewerPartGroup::posInGroup(const LLVector3 &pos)
+{
+ if ((pos.mV[VX] < mMinObjPos.mV[VX])
+ || (pos.mV[VY] < mMinObjPos.mV[VY])
+ || (pos.mV[VZ] < mMinObjPos.mV[VZ]))
+ {
+ return FALSE;
+ }
+
+ if ((pos.mV[VX] > mMaxObjPos.mV[VX])
+ || (pos.mV[VY] > mMaxObjPos.mV[VY])
+ || (pos.mV[VZ] > mMaxObjPos.mV[VZ]))
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+BOOL LLViewerPartGroup::addPart(LLViewerPart &part)
+{
+ if (!posInGroup(part.mPosAgent) ||
+ (mVOPartGroupp.notNull() && (part.mImagep != mVOPartGroupp->getTEImage(0))))
+ {
+ return FALSE;
+ }
+
+ if (!mVOPartGroupp)
+ {
+ mVOPartGroupp = (LLVOPartGroup *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_PART_GROUP, getRegion());
+ mVOPartGroupp->setViewerPartGroup(this);
+ mVOPartGroupp->setPositionAgent(getCenterAgent());
+ mVOPartGroupp->setScale(LLVector3(PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE, PART_SIM_BOX_SIDE));
+ mVOPartGroupp->setTEImage(0, part.mImagep);
+ gPipeline.addObject(mVOPartGroupp);
+ }
+
+ mParticles.put(part);
+ LLViewerPartSim::incPartCount(1);
+ return TRUE;
+}
+
+
+void LLViewerPartGroup::removePart(const S32 part_num)
+{
+ // Remove the entry for the particle we just deleted.
+ LLPointer<LLViewerPartSource> ps = mParticles[mParticles.count() - 1].mPartSourcep;
+
+ mParticles[mParticles.count() - 1].mPartSourcep = NULL;
+ mParticles.remove(part_num);
+ if (part_num < mParticles.count())
+ {
+ mParticles[part_num].mPartSourcep = ps;
+ }
+
+ LLViewerPartSim::decPartCount(1);
+}
+
+
+void LLViewerPartGroup::updateParticles(const F32 dt)
+{
+ S32 i, count;
+
+
+ LLVector3 gravity(0.f, 0.f, -9.8f);
+
+ LLViewerRegion *regionp = getRegion();
+ count = mParticles.count();
+ for (i = 0; i < count; i++)
+ {
+ LLVector3 a(0.f, 0.f, 0.f);
+ LLViewerPart &part = mParticles[i];
+
+ // Update current time
+ const F32 cur_time = part.mLastUpdateTime + dt;
+ const F32 frac = cur_time/part.mMaxAge;
+
+ // "Drift" the object based on the source object
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
+ {
+ part.mPosAgent = part.mPartSourcep->mPosAgent;
+ part.mPosAgent += part.mPosOffset;
+ }
+
+ // Do a custom callback if we have one...
+ if (part.mVPCallback)
+ {
+ (*part.mVPCallback)(part, dt);
+ }
+
+ if (part.mFlags & LLPartData::LL_PART_WIND_MASK)
+ {
+ LLVector3 tempVel(part.mVelocity);
+ part.mVelocity *= 1.f - 0.1f*dt;
+ part.mVelocity += 0.1f*dt*regionp->mWind.getVelocity(regionp->getPosRegionFromAgent(part.mPosAgent));
+ }
+
+ // Now do interpolation towards a target
+ if (part.mFlags & LLPartData::LL_PART_TARGET_POS_MASK)
+ {
+ F32 remaining = part.mMaxAge - part.mLastUpdateTime;
+ F32 step = dt / remaining;
+
+ step = llclamp(step, 0.f, 0.1f);
+ step *= 5.f;
+ // we want a velocity that will result in reaching the target in the
+ // Interpolate towards the target.
+ LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPosAgent;
+
+ delta_pos /= remaining;
+
+ part.mVelocity *= (1.f - step);
+ part.mVelocity += step*delta_pos;
+ //part.mPosAgent *= 1.f - to_target_frac;
+ //part.mPosAgent += to_target_frac*part.mPartSourcep->mTargetPosAgent;
+ }
+
+
+ if (part.mFlags & LLPartData::LL_PART_TARGET_LINEAR_MASK)
+ {
+ LLVector3 delta_pos = part.mPartSourcep->mTargetPosAgent - part.mPartSourcep->mPosAgent;
+ part.mPosAgent = part.mPartSourcep->mPosAgent;
+ part.mPosAgent += frac*delta_pos;
+ part.mVelocity = delta_pos;
+ }
+ else
+ {
+ // Do velocity interpolation
+ part.mPosAgent += dt*part.mVelocity;
+ part.mPosAgent += 0.5f*dt*dt*part.mAccel;
+ part.mVelocity += part.mAccel*dt;
+ }
+
+ // Do a bounce test
+ if (part.mFlags & LLPartData::LL_PART_BOUNCE_MASK)
+ {
+ // Need to do point vs. plane check...
+ // For now, just check relative to object height...
+ F32 dz = part.mPosAgent.mV[VZ] - part.mPartSourcep->mPosAgent.mV[VZ];
+ if (dz < 0)
+ {
+ part.mPosAgent.mV[VZ] += -2.f*dz;
+ part.mVelocity.mV[VZ] *= -0.75f;
+ }
+ }
+
+
+ // Reset the offset from the source position
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_SRC_MASK)
+ {
+ part.mPosOffset = part.mPosAgent;
+ part.mPosOffset -= part.mPartSourcep->mPosAgent;
+ }
+
+ // Do color interpolation
+ if (part.mFlags & LLPartData::LL_PART_INTERP_COLOR_MASK)
+ {
+ part.mColor.setVec(part.mStartColor);
+ part.mColor *= 1.f - frac;
+ part.mColor.mV[3] *= (1.f - frac)*part.mStartColor.mV[3];
+ part.mColor += frac*part.mEndColor;
+ part.mColor.mV[3] += frac*part.mEndColor.mV[3];
+ }
+
+ // Do scale interpolation
+ if (part.mFlags & LLPartData::LL_PART_INTERP_SCALE_MASK)
+ {
+ part.mScale.setVec(part.mStartScale);
+ part.mScale *= 1.f - frac;
+ part.mScale += frac*part.mEndScale;
+ }
+
+ // Set the last update time to now.
+ part.mLastUpdateTime = cur_time;
+
+
+ // Kill dead particles (either flagged dead, or too old)
+ if ((part.mLastUpdateTime > part.mMaxAge) || (LLViewerPart::LL_PART_DEAD_MASK == part.mFlags))
+ {
+ removePart(i);
+ i--;
+ count--;
+ }
+ else if (!posInGroup(part.mPosAgent))
+ {
+ // Transfer particles between groups
+ gWorldPointer->mPartSim.put(part);
+ removePart(i);
+ i--;
+ count--;
+ }
+ }
+
+ // Kill the viewer object if this particle group is empty
+ if (!mParticles.count())
+ {
+ gObjectList.killObject(mVOPartGroupp);
+ mVOPartGroupp = NULL;
+ }
+}
+
+
+void LLViewerPartGroup::shift(const LLVector3 &offset)
+{
+ mCenterAgent += offset;
+ mMinObjPos += offset;
+ mMaxObjPos += offset;
+
+ S32 count = mParticles.count();
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ mParticles[i].mPosAgent += offset;
+ }
+}
+
+
+//////////////////////////////////
+//
+// LLViewerPartSim implementation
+//
+//
+
+
+LLViewerPartSim::LLViewerPartSim()
+{
+ sMaxParticleCount = gSavedSettings.getS32("RenderMaxPartCount");
+}
+
+
+LLViewerPartSim::~LLViewerPartSim()
+{
+ S32 i;
+ S32 count;
+
+ // Kill all of the groups (and particles)
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ delete mViewerPartGroups[i];
+ }
+ mViewerPartGroups.reset();
+
+ // Kill all of the sources
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i] = NULL;
+ }
+ mViewerPartSources.reset();
+}
+
+BOOL LLViewerPartSim::shouldAddPart()
+{
+ if (sParticleCount > 0.75f*sMaxParticleCount)
+ {
+
+ F32 frac = (F32)sParticleCount/(F32)sMaxParticleCount;
+ frac -= 0.75;
+ frac *= 3.f;
+ if (frand(1.f) < frac)
+ {
+ // Skip...
+ return FALSE;
+ }
+ }
+ if (sParticleCount >= MAX_PART_COUNT)
+ {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLViewerPartSim::addPart(LLViewerPart &part)
+{
+ if (sParticleCount < MAX_PART_COUNT)
+ {
+ put(part);
+ }
+}
+
+LLViewerPartGroup *LLViewerPartSim::put(LLViewerPart &part)
+{
+ const F32 MAX_MAG = 1000000.f*1000000.f; // 1 million
+ if (part.mPosAgent.magVecSquared() > MAX_MAG)
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "LLViewerPartSim::put Part out of range!" << llendl;
+ llwarns << part.mPosAgent << llendl;
+#endif
+ return NULL;
+ }
+
+ S32 i;
+ S32 count;
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->addPart(part))
+ {
+ // We found a spatial group that we fit into, add us and exit
+ return mViewerPartGroups[i];
+ }
+ }
+
+ // Hmm, we didn't fit in any of the existing spatial groups
+ // Create a new one...
+ LLViewerPartGroup *groupp = createViewerPartGroup(part.mPosAgent);
+ if (!groupp->addPart(part))
+ {
+ llwarns << "LLViewerPartSim::put - Particle didn't go into its box!" << llendl;
+ llinfos << groupp->getCenterAgent() << llendl;
+ llinfos << part.mPosAgent << llendl;
+ return NULL;
+ }
+ return groupp;
+}
+
+LLViewerPartGroup *LLViewerPartSim::createViewerPartGroup(const LLVector3 &pos_agent)
+{
+ F32 x_origin = ((S32)(pos_agent.mV[VX]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (x_origin > pos_agent.mV[VX])
+ {
+ x_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 y_origin = ((S32)(pos_agent.mV[VY]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (y_origin > pos_agent.mV[VY])
+ {
+ y_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ F32 z_origin = ((S32)(pos_agent.mV[VZ]/PART_SIM_BOX_SIDE))*PART_SIM_BOX_SIDE;
+ if (z_origin > pos_agent.mV[VZ])
+ {
+ z_origin -= PART_SIM_BOX_SIDE;
+ }
+
+ LLVector3 group_center(x_origin + PART_SIM_BOX_OFFSET,
+ y_origin + PART_SIM_BOX_OFFSET,
+ z_origin + PART_SIM_BOX_OFFSET);
+
+ LLViewerPartGroup *groupp = new LLViewerPartGroup(group_center, PART_SIM_BOX_SIDE);
+ mViewerPartGroups.put(groupp);
+ return groupp;
+}
+
+
+void LLViewerPartSim::shift(const LLVector3 &offset)
+{
+ S32 i;
+ S32 count;
+
+ count = mViewerPartSources.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartSources[i]->mPosAgent += offset;
+ mViewerPartSources[i]->mTargetPosAgent += offset;
+ mViewerPartSources[i]->mLastUpdatePosAgent += offset;
+ }
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->shift(offset);
+ }
+}
+
+
+void LLViewerPartSim::updateSimulation()
+{
+ LLMemType mt(LLMemType::MTYPE_PARTICLES);
+
+ static LLFrameTimer update_timer;
+
+ const F32 dt = update_timer.getElapsedTimeAndResetF32();
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
+ {
+ return;
+ }
+
+ // Start at a random particle system so the same
+ // particle system doesn't always get first pick at the
+ // particles. Theoretically we'd want to do this in distance
+ // order or something, but sorting particle sources will be a big
+ // pain.
+ S32 i;
+ S32 count = mViewerPartSources.count();
+ S32 start = (S32)frand((F32)count);
+ S32 dir = 1;
+ if (frand(1.0) > 0.5f)
+ {
+ dir = -1;
+ }
+
+ S32 num_updates = 0;
+ for (i = start; num_updates < count;)
+ {
+ if (i >= count)
+ {
+ i = 0;
+ }
+ if (i < 0)
+ {
+ i = count - 1;
+ }
+
+ if (!mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources[i]->update(dt);
+ }
+
+ if (mViewerPartSources[i]->isDead())
+ {
+ mViewerPartSources.remove(i);
+ count--;
+ }
+ else
+ {
+ i += dir;
+ }
+ num_updates++;
+ }
+
+
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ mViewerPartGroups[i]->updateParticles(dt);
+ if (!mViewerPartGroups[i]->getCount())
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+ //llinfos << "Particles: " << sParticleCount << llendl;
+}
+
+
+void LLViewerPartSim::addPartSource(LLViewerPartSource *sourcep)
+{
+ if (!sourcep)
+ {
+ llwarns << "Null part source!" << llendl;
+ return;
+ }
+ mViewerPartSources.put(sourcep);
+}
+
+
+void LLViewerPartSim::cleanupRegion(LLViewerRegion *regionp)
+{
+ S32 i, count;
+ count = mViewerPartGroups.count();
+ for (i = 0; i < count; i++)
+ {
+ if (mViewerPartGroups[i]->getRegion() == regionp)
+ {
+ delete mViewerPartGroups[i];
+ mViewerPartGroups.remove(i);
+ i--;
+ count--;
+ }
+ }
+}
+
+void LLViewerPartSim::cleanMutedParticles(const LLUUID& task_id)
+{
+ S32 i;
+ S32 count = mViewerPartSources.count();
+ for (i = 0; i < count; ++i)
+ {
+ if (mViewerPartSources[i]->getOwnerUUID() == task_id)
+ {
+ mViewerPartSources.remove(i);
+ i--;
+ count--;
+ }
+ }
+}
diff --git a/indra/newview/llviewerpartsim.h b/indra/newview/llviewerpartsim.h
new file mode 100644
index 0000000000..83c374a42e
--- /dev/null
+++ b/indra/newview/llviewerpartsim.h
@@ -0,0 +1,141 @@
+/**
+ * @file llviewerpartsim.h
+ * @brief LLViewerPart class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARTSIM_H
+#define LL_LLVIEWERPARTSIM_H
+
+#include "lldarrayptr.h"
+#include "llskiplist.h"
+#include "llframetimer.h"
+#include "llmemory.h"
+
+#include "llpartdata.h"
+
+class LLViewerImage;
+class LLViewerPart;
+class LLViewerPartSource;
+class LLViewerRegion;
+class LLViewerImage;
+class LLVOPartGroup;
+
+typedef void (*LLVPCallback)(LLViewerPart &part, const F32 dt);
+
+///////////////////
+//
+// An individual particle
+//
+
+
+class LLViewerPart : public LLPartData
+{
+public:
+ LLViewerPart();
+ ~LLViewerPart();
+
+ LLViewerPart &operator=(const LLViewerPart &part);
+ void init(LLViewerPartSource *sourcep, LLViewerImage *imagep, LLVPCallback cb);
+
+
+ U32 mPartID; // Particle ID used primarily for moving between groups
+ F32 mLastUpdateTime; // Last time the particle was updated
+
+
+ LLVPCallback mVPCallback; // Callback function for more complicated behaviors
+ LLPointer<LLViewerPartSource> mPartSourcep; // Particle source used for this object
+
+
+ // Current particle state (possibly used for rendering)
+ LLPointer<LLViewerImage> mImagep;
+ LLVector3 mPosAgent;
+ LLVector3 mVelocity;
+ LLVector3 mAccel;
+ LLColor4 mColor;
+ LLVector2 mScale;
+
+ static U32 sNextPartID;
+};
+
+
+
+class LLViewerPartGroup
+{
+public:
+ LLViewerPartGroup(const LLVector3 &center,
+ const F32 box_radius);
+ virtual ~LLViewerPartGroup();
+
+ void cleanup();
+
+ BOOL addPart(LLViewerPart &part);
+
+ void updateParticles(const F32 dt);
+
+ BOOL posInGroup(const LLVector3 &pos);
+
+ void shift(const LLVector3 &offset);
+
+ LLDynamicArray<LLViewerPart> mParticles;
+
+ const LLVector3 &getCenterAgent() const { return mCenterAgent; }
+ S32 getCount() const { return mParticles.count(); }
+ LLViewerRegion *getRegion() const { return mRegionp; }
+protected:
+ void removePart(const S32 part_num);
+
+protected:
+ LLVector3 mCenterAgent;
+ F32 mBoxRadius;
+ LLVector3 mMinObjPos;
+ LLVector3 mMaxObjPos;
+
+ LLPointer<LLVOPartGroup> mVOPartGroupp;
+ LLViewerRegion *mRegionp;
+};
+
+
+class LLViewerPartSim
+{
+public:
+ LLViewerPartSim();
+ virtual ~LLViewerPartSim();
+
+ void shift(const LLVector3 &offset);
+
+ void updateSimulation();
+
+ void addPartSource(LLViewerPartSource *sourcep);
+
+ void cleanupRegion(LLViewerRegion *regionp);
+
+ BOOL shouldAddPart(); // Just decides whether this particle should be added or not (for particle count capping)
+ void addPart(LLViewerPart &part);
+ void cleanMutedParticles(const LLUUID& task_id);
+
+ friend class LLViewerPartGroup;
+
+ BOOL aboveParticleLimit() const { return sParticleCount > sMaxParticleCount; }
+
+ static void setMaxPartCount(const S32 max_parts) { sMaxParticleCount = max_parts; }
+ static S32 getMaxPartCount() { return sMaxParticleCount; }
+ static void incPartCount(const S32 count) { sParticleCount += count; }
+ static void decPartCount(const S32 count) { sParticleCount -= count; }
+
+protected:
+ LLViewerPartGroup *createViewerPartGroup(const LLVector3 &pos_agent);
+ LLViewerPartGroup *put(LLViewerPart &part);
+
+protected:
+ LLDynamicArray<LLViewerPartGroup *> mViewerPartGroups;
+ LLDynamicArrayPtr<LLPointer<LLViewerPartSource> > mViewerPartSources;
+ LLFrameTimer mSimulationTimer;
+
+ static S32 sMaxParticleCount;
+ static S32 sParticleCount;
+};
+
+#endif // LL_LLVIEWERPARTSIM_H
diff --git a/indra/newview/llviewerpartsource.cpp b/indra/newview/llviewerpartsource.cpp
new file mode 100644
index 0000000000..e73f77e463
--- /dev/null
+++ b/indra/newview/llviewerpartsource.cpp
@@ -0,0 +1,719 @@
+/**
+ * @file llviewerpartsource.cpp
+ * @brief LLViewerPartSource class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerpartsource.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llvoavatar.h"
+#include "llworld.h"
+
+LLViewerPartSource::LLViewerPartSource(const U32 type) :
+ mType(type),
+ mOwnerUUID(LLUUID::null)
+{
+ mLastUpdateTime = 0.f;
+ mLastPartTime = 0.f;
+ mIsDead = FALSE;
+}
+
+void LLViewerPartSource::setDead()
+{
+ mIsDead = TRUE;
+}
+
+
+void LLViewerPartSource::updatePart(LLViewerPart &part, const F32 dt)
+{
+}
+
+void LLViewerPartSource::update(const F32 dt)
+{
+ llerrs << "Creating default part source!" << llendl;
+}
+
+
+
+LLViewerPartSourceScript::LLViewerPartSourceScript(LLViewerObject *source_objp) :
+ LLViewerPartSource(LL_PART_SOURCE_SCRIPT)
+{
+ llassert(source_objp);
+ mSourceObjectp = source_objp;
+ mPosAgent = mSourceObjectp->getPositionAgent();
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ mImagep->bind();
+ mImagep->setClamp(TRUE, TRUE);
+}
+
+
+void LLViewerPartSourceScript::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+ mTargetObjectp = NULL;
+}
+
+
+
+void LLViewerPartSourceScript::update(const F32 dt)
+{
+ F32 old_update_time = mLastUpdateTime;
+ mLastUpdateTime += dt;
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+
+ // Update this for objects which have the follow flag set...
+ if (!mSourceObjectp.isNull())
+ {
+ if (mSourceObjectp->isDead())
+ {
+ mSourceObjectp = NULL;
+ }
+ else if (mSourceObjectp->mDrawable.notNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ }
+
+ if (mTargetObjectp.isNull()
+ && mPartSysData.mTargetUUID.notNull())
+ {
+ //
+ // Hmm, missing object, let's see if we can find it...
+ //
+
+ LLViewerObject *target_objp = gObjectList.findObject(mPartSysData.mTargetUUID);
+ setTargetObject(target_objp);
+ }
+
+ if (!mTargetObjectp.isNull())
+ {
+ if (mTargetObjectp->isDead())
+ {
+ mTargetObjectp = NULL;
+ }
+ else if (mTargetObjectp->mDrawable.notNull())
+ {
+ mTargetPosAgent = mTargetObjectp->getRenderPosition();
+ }
+ }
+
+ if (!mTargetObjectp)
+ {
+ mTargetPosAgent = mPosAgent;
+ }
+
+ if (mPartSysData.mMaxAge && ((mPartSysData.mStartAge + mLastUpdateTime + dt_update) > mPartSysData.mMaxAge))
+ {
+ // Kill particle source because it has outlived its max age...
+ setDead();
+ return;
+ }
+
+ BOOL first_run = FALSE;
+ if (old_update_time <= 0.f)
+ {
+ first_run = TRUE;
+ }
+
+ F32 max_time = llmax(1.f, 10.f*mPartSysData.mBurstRate);
+ dt_update = llmin(max_time, dt_update);
+ while ((dt_update > mPartSysData.mBurstRate) || first_run)
+ {
+ first_run = FALSE;
+ LLViewerPart part;
+
+ part.init(this, mImagep, NULL);
+ part.mFlags = mPartSysData.mPartData.mFlags;
+ part.mMaxAge = mPartSysData.mPartData.mMaxAge;
+ part.mStartColor = mPartSysData.mPartData.mStartColor;
+ part.mEndColor = mPartSysData.mPartData.mEndColor;
+ part.mColor = part.mStartColor;
+
+ part.mStartScale = mPartSysData.mPartData.mStartScale;
+ part.mEndScale = mPartSysData.mPartData.mEndScale;
+ part.mScale = part.mStartScale;
+
+ part.mAccel = mPartSysData.mPartAccel;
+
+ // Update the rotation of the particle source by the angular velocity
+ // First check to see if there is still an angular velocity.
+ F32 angular_velocity_mag = mPartSysData.mAngularVelocity.magVec();
+ if (angular_velocity_mag != 0.0f)
+ {
+ F32 av_angle = dt * angular_velocity_mag;
+ LLQuaternion dquat(av_angle, mPartSysData.mAngularVelocity);
+ mRotation *= dquat;
+ }
+ else
+ {
+ // No angular velocity. Reset our rotation.
+ mRotation.setQuat(0, 0, 0);
+ }
+
+ if (gWorldPointer->mPartSim.aboveParticleLimit())
+ {
+ // Don't bother doing any more updates if we're above the particle limit,
+ // just give up.
+ mLastPartTime = mLastUpdateTime;
+ break;
+ }
+
+ S32 i;
+ for (i = 0; i < mPartSysData.mBurstPartCount; i++)
+ {
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ continue;
+ }
+
+ if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_DROP)
+ {
+ part.mPosAgent = mPosAgent;
+ part.mVelocity.setVec(0.f, 0.f, 0.f);
+ }
+ else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_EXPLODE)
+ {
+ part.mPosAgent = mPosAgent;
+ LLVector3 part_dir_vector;
+
+ F32 mvs;
+ do
+ {
+ part_dir_vector.mV[VX] = frand(2.f) - 1.f;
+ part_dir_vector.mV[VY] = frand(2.f) - 1.f;
+ part_dir_vector.mV[VZ] = frand(2.f) - 1.f;
+ mvs = part_dir_vector.magVecSquared();
+ }
+ while ((mvs > 1.f) || (mvs < 0.01f));
+
+ part_dir_vector.normVec();
+ part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
+ part.mVelocity = part_dir_vector;
+ F32 speed = mPartSysData.mBurstSpeedMin + frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
+ part.mVelocity *= speed;
+ }
+ else if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE
+ || mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE)
+ {
+ part.mPosAgent = mPosAgent;
+
+ // original implemenetation for part_dir_vector was just:
+ LLVector3 part_dir_vector(0.0, 0.0, 1.0);
+
+ // params from the script...
+ // outer = outer cone angle
+ // inner = inner cone angle
+ // between outer and inner there will be particles
+ F32 innerAngle = mPartSysData.mInnerAngle;
+ F32 outerAngle = mPartSysData.mOuterAngle;
+
+ // generate a random angle within the given space...
+ F32 angle = innerAngle + frand(outerAngle - innerAngle);
+
+ // split which side it will go on randomly...
+ if (frand(1.0) < 0.5)
+ {
+ angle = -angle;
+ }
+
+ // Both patterns rotate around the x-axis first:
+ part_dir_vector.rotVec(angle, 1.0, 0.0, 0.0);
+
+ // If this is a cone pattern, rotate again to create the cone.
+ if (mPartSysData.mPattern & LLPartSysData::LL_PART_SRC_PATTERN_ANGLE_CONE)
+ {
+ part_dir_vector.rotVec(frand(4*F_PI), 0.0, 0.0, 1.0);
+ }
+
+ // Only apply this rotation if using the deprecated angles.
+ if (! (mPartSysData.mFlags & LLPartSysData::LL_PART_USE_NEW_ANGLE))
+ {
+ // Deprecated...
+ part_dir_vector.rotVec(outerAngle, 1.0, 0.0, 0.0);
+ }
+
+ if (mSourceObjectp)
+ {
+ part_dir_vector = part_dir_vector * mSourceObjectp->getRenderRotation();
+ }
+ part_dir_vector = part_dir_vector * mRotation;
+
+ part.mPosAgent += mPartSysData.mBurstRadius*part_dir_vector;
+
+ part.mVelocity = part_dir_vector;
+
+ F32 speed = mPartSysData.mBurstSpeedMin + frand(mPartSysData.mBurstSpeedMax - mPartSysData.mBurstSpeedMin);
+ part.mVelocity *= speed;
+ }
+ else
+ {
+ part.mPosAgent = mPosAgent;
+ part.mVelocity.setVec(0.f, 0.f, 0.f);
+ //llwarns << "Unknown source pattern " << (S32)mPartSysData.mPattern << llendl;
+ }
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+
+ mLastPartTime = mLastUpdateTime;
+ dt_update -= mPartSysData.mBurstRate;
+ }
+}
+
+// static
+LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num)
+{
+ if (!pssp)
+ {
+ if (LLPartSysData::isNullPS(block_num))
+ {
+ return NULL;
+ }
+ LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
+ if (!new_pssp->mPartSysData.unpackBlock(block_num))
+ {
+ return NULL;
+ }
+ if (new_pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(new_pssp->mPartSysData.mTargetUUID);
+ new_pssp->setTargetObject(target_objp);
+ }
+ return new_pssp;
+ }
+ else
+ {
+ if (LLPartSysData::isNullPS(block_num))
+ {
+ return NULL;
+ }
+
+ if (!pssp->mPartSysData.unpackBlock(block_num))
+ {
+ return NULL;
+ }
+ if (pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(pssp->mPartSysData.mTargetUUID);
+ pssp->setTargetObject(target_objp);
+ }
+ return pssp;
+ }
+}
+
+
+LLViewerPartSourceScript *LLViewerPartSourceScript::unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp)
+{
+ if (!pssp)
+ {
+ LLViewerPartSourceScript *new_pssp = new LLViewerPartSourceScript(source_objp);
+ if (!new_pssp->mPartSysData.unpack(dp))
+ {
+ return NULL;
+ }
+ if (new_pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(new_pssp->mPartSysData.mTargetUUID);
+ new_pssp->setTargetObject(target_objp);
+ }
+ return new_pssp;
+ }
+ else
+ {
+ if (!pssp->mPartSysData.unpack(dp))
+ {
+ return NULL;
+ }
+ if (pssp->mPartSysData.mTargetUUID.notNull())
+ {
+ LLViewerObject *target_objp = gObjectList.findObject(pssp->mPartSysData.mTargetUUID);
+ pssp->setTargetObject(target_objp);
+ }
+ return pssp;
+ }
+}
+
+void LLViewerPartSourceScript::setImage(LLViewerImage *imagep)
+{
+ mImagep = imagep;
+}
+
+void LLViewerPartSourceScript::setTargetObject(LLViewerObject *objp)
+{
+ mTargetObjectp = objp;
+}
+
+
+
+
+
+LLViewerPartSourceSpiral::LLViewerPartSourceSpiral(const LLVector3 &pos) :
+ LLViewerPartSource(LL_PART_SOURCE_CHAT)
+{
+ mPosAgent = pos;
+}
+
+void LLViewerPartSourceSpiral::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+}
+
+
+void LLViewerPartSourceSpiral::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLVector3 center_pos;
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceSpiral *pss = (LLViewerPartSourceSpiral *)ps;
+ if (!pss->mSourceObjectp.isNull() && !pss->mSourceObjectp->mDrawable.isNull())
+ {
+ part.mPosAgent = pss->mSourceObjectp->getRenderPosition();
+ }
+ else
+ {
+ part.mPosAgent = pss->mPosAgent;
+ }
+ F32 x = sin(F_TWO_PI*frac + part.mParameter);
+ F32 y = cos(F_TWO_PI*frac + part.mParameter);
+
+ part.mPosAgent.mV[VX] += x;
+ part.mPosAgent.mV[VY] += y;
+ part.mPosAgent.mV[VZ] += -0.5f + frac;
+}
+
+
+void LLViewerPartSourceSpiral::update(const F32 dt)
+{
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ LLViewerPart part;
+ part.init(this, mImagep, updatePart);
+ part.mStartColor = mColor;
+ part.mEndColor = mColor;
+ part.mEndColor.mV[3] = 0.f;
+ part.mPosAgent = mPosAgent;
+ part.mMaxAge = 1.f;
+ part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+ part.mLastUpdateTime = 0.f;
+ part.mScale.mV[0] = 0.25f;
+ part.mScale.mV[1] = 0.25f;
+ part.mParameter = frand(F_TWO_PI);
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceSpiral::setSourceObject(LLViewerObject *objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceSpiral::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+
+
+
+
+LLViewerPartSourceBeam::LLViewerPartSourceBeam() :
+ LLViewerPartSource(LL_PART_SOURCE_BEAM)
+{
+}
+
+LLViewerPartSourceBeam::~LLViewerPartSourceBeam()
+{
+}
+
+void LLViewerPartSourceBeam::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+ mTargetObjectp = NULL;
+}
+
+void LLViewerPartSourceBeam::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
+
+void LLViewerPartSourceBeam::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceBeam *psb = (LLViewerPartSourceBeam *)ps;
+ if (psb->mSourceObjectp.isNull())
+ {
+ part.mFlags = LLPartData::LL_PART_DEAD_MASK;
+ return;
+ }
+
+ LLVector3 source_pos_agent;
+ LLVector3 target_pos_agent;
+ if (!psb->mSourceObjectp.isNull() && !psb->mSourceObjectp->mDrawable.isNull())
+ {
+ if (psb->mSourceObjectp->isAvatar())
+ {
+ LLViewerObject *objp = psb->mSourceObjectp;
+ LLVOAvatar *avp = (LLVOAvatar *)objp;
+ source_pos_agent = avp->mWristLeftp->getWorldPosition();
+ }
+ else
+ {
+ source_pos_agent = psb->mSourceObjectp->getRenderPosition();
+ }
+ }
+ if (!psb->mTargetObjectp.isNull() && !psb->mTargetObjectp->mDrawable.isNull())
+ {
+ target_pos_agent = psb->mTargetObjectp->getRenderPosition();
+ }
+
+ part.mPosAgent = (1.f - frac) * source_pos_agent;
+ if (psb->mTargetObjectp.isNull())
+ {
+ part.mPosAgent += frac * (gAgent.getPosAgentFromGlobal(psb->mLKGTargetPosGlobal));
+ }
+ else
+ {
+ part.mPosAgent += frac * target_pos_agent;
+ }
+}
+
+
+void LLViewerPartSourceBeam::update(const F32 dt)
+{
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ if (mSourceObjectp->isAvatar())
+ {
+ LLViewerObject *objp = mSourceObjectp;
+ LLVOAvatar *avp = (LLVOAvatar *)objp;
+ mPosAgent = avp->mWristLeftp->getWorldPosition();
+ }
+ else
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ }
+
+ if (!mTargetObjectp.isNull() && !mTargetObjectp->mDrawable.isNull())
+ {
+ mTargetPosAgent = mTargetObjectp->getRenderPosition();
+ }
+ else if (!mLKGTargetPosGlobal.isExactlyZero())
+ {
+ mTargetPosAgent = gAgent.getPosAgentFromGlobal(mLKGTargetPosGlobal);
+ }
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+ LLViewerPart part;
+ part.init(this, mImagep, NULL);
+
+ part.mFlags = LLPartData::LL_PART_INTERP_COLOR_MASK |
+ LLPartData::LL_PART_INTERP_SCALE_MASK |
+ LLPartData::LL_PART_TARGET_POS_MASK |
+ LLPartData::LL_PART_FOLLOW_VELOCITY_MASK;
+ part.mMaxAge = 0.5f;
+ part.mStartColor = mColor;
+ part.mEndColor = part.mStartColor;
+ part.mEndColor.mV[3] = 0.4f;
+ part.mColor = part.mStartColor;
+
+ part.mStartScale = LLVector2(0.1f, 0.1f);
+ part.mEndScale = LLVector2(0.1f, 0.1f);
+ part.mScale = part.mStartScale;
+
+ part.mPosAgent = mPosAgent;
+ part.mVelocity = mTargetPosAgent - mPosAgent;
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceBeam::setSourceObject(LLViewerObject* objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceBeam::setTargetObject(LLViewerObject* objp)
+{
+ mTargetObjectp = objp;
+}
+
+
+
+
+LLViewerPartSourceChat::LLViewerPartSourceChat(const LLVector3 &pos) :
+ LLViewerPartSource(LL_PART_SOURCE_SPIRAL)
+{
+ mPosAgent = pos;
+}
+
+void LLViewerPartSourceChat::setDead()
+{
+ mIsDead = TRUE;
+ mSourceObjectp = NULL;
+}
+
+
+void LLViewerPartSourceChat::updatePart(LLViewerPart &part, const F32 dt)
+{
+ F32 frac = part.mLastUpdateTime/part.mMaxAge;
+
+ LLVector3 center_pos;
+ LLViewerPartSource *ps = (LLViewerPartSource*)part.mPartSourcep;
+ LLViewerPartSourceChat *pss = (LLViewerPartSourceChat *)ps;
+ if (!pss->mSourceObjectp.isNull() && !pss->mSourceObjectp->mDrawable.isNull())
+ {
+ part.mPosAgent = pss->mSourceObjectp->getRenderPosition();
+ }
+ else
+ {
+ part.mPosAgent = pss->mPosAgent;
+ }
+ F32 x = sin(F_TWO_PI*frac + part.mParameter);
+ F32 y = cos(F_TWO_PI*frac + part.mParameter);
+
+ part.mPosAgent.mV[VX] += x;
+ part.mPosAgent.mV[VY] += y;
+ part.mPosAgent.mV[VZ] += -0.5f + frac;
+}
+
+
+void LLViewerPartSourceChat::update(const F32 dt)
+{
+ if (!mImagep)
+ {
+ LLUUID id;
+ id.set( gViewerArt.getString("pixiesmall.tga") );
+ mImagep = gImageList.getImage(id);
+ }
+
+
+ const F32 RATE = 0.025f;
+
+ mLastUpdateTime += dt;
+
+ if (mLastUpdateTime > 2.f)
+ {
+ // Kill particle source because it has outlived its max age...
+ setDead();
+ return;
+ }
+
+ F32 dt_update = mLastUpdateTime - mLastPartTime;
+
+ // Clamp us to generating at most one second's worth of particles on a frame.
+ F32 max_time = llmax(1.f, 10.f*RATE);
+ dt_update = llmin(max_time, dt_update);
+
+ if (dt_update > RATE)
+ {
+ mLastPartTime = mLastUpdateTime;
+ if (!gWorldPointer->mPartSim.shouldAddPart())
+ {
+ // Particle simulation says we have too many particles, skip all this
+ return;
+ }
+
+ if (!mSourceObjectp.isNull() && !mSourceObjectp->mDrawable.isNull())
+ {
+ mPosAgent = mSourceObjectp->getRenderPosition();
+ }
+ LLViewerPart part;
+ part.init(this, mImagep, updatePart);
+ part.mStartColor = mColor;
+ part.mEndColor = mColor;
+ part.mEndColor.mV[3] = 0.f;
+ part.mPosAgent = mPosAgent;
+ part.mMaxAge = 1.f;
+ part.mFlags = LLViewerPart::LL_PART_INTERP_COLOR_MASK;
+ part.mLastUpdateTime = 0.f;
+ part.mScale.mV[0] = 0.25f;
+ part.mScale.mV[1] = 0.25f;
+ part.mParameter = frand(F_TWO_PI);
+
+ gWorldPointer->mPartSim.addPart(part);
+ }
+}
+
+void LLViewerPartSourceChat::setSourceObject(LLViewerObject *objp)
+{
+ mSourceObjectp = objp;
+}
+
+void LLViewerPartSourceChat::setColor(const LLColor4 &color)
+{
+ mColor = color;
+}
+
diff --git a/indra/newview/llviewerpartsource.h b/indra/newview/llviewerpartsource.h
new file mode 100644
index 0000000000..05fa007ead
--- /dev/null
+++ b/indra/newview/llviewerpartsource.h
@@ -0,0 +1,188 @@
+/**
+ * @file llviewerpartsource.h
+ * @brief LLViewerPartSource class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERPARTSOURCE_H
+#define LL_LLVIEWERPARTSOURCE_H
+
+#include "llmemory.h"
+#include "llpartdata.h"
+#include "llquaternion.h"
+#include "v3math.h"
+
+////////////////////
+//
+// A particle source - subclassed to generate particles with different behaviors
+//
+//
+
+class LLViewerImage;
+class LLViewerObject;
+class LLViewerPart;
+
+class LLViewerPartSource : public LLRefCount
+{
+public:
+ enum
+ {
+ LL_PART_SOURCE_NULL,
+ LL_PART_SOURCE_SCRIPT,
+ LL_PART_SOURCE_SPIRAL,
+ LL_PART_SOURCE_BEAM,
+ LL_PART_SOURCE_CHAT
+ };
+
+ LLViewerPartSource(const U32 type);
+
+ virtual void update(const F32 dt); // Return FALSE if this source is dead...
+
+ virtual void setDead();
+ BOOL isDead() const { return mIsDead; }
+
+ U32 getType() const { return mType; }
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ void setOwnerUUID(const LLUUID& owner_id) { mOwnerUUID = owner_id; }
+ LLUUID getOwnerUUID() const { return mOwnerUUID; }
+
+ LLVector3 mPosAgent; // Location of the particle source
+ LLVector3 mTargetPosAgent; // Location of the target position
+ LLVector3 mLastUpdatePosAgent;
+protected:
+ U32 mType;
+ BOOL mIsDead;
+ F32 mLastUpdateTime;
+ F32 mLastPartTime;
+ LLUUID mOwnerUUID;
+
+ // Particle information
+ U32 mPartFlags; // Flags for the particle
+};
+
+
+
+///////////////////////////////
+//
+// LLViewerPartSourceScript
+//
+// Particle source that handles the "generic" script-drive particle source
+// attached to objects
+//
+
+
+class LLViewerPartSourceScript : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceScript(LLViewerObject *source_objp);
+ /*virtual*/ void update(const F32 dt);
+
+ /*virtual*/ void setDead();
+
+ BOOL updateFromMesg();
+
+ // Returns a new particle source to attach to an object...
+ static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, const S32 block_num);
+ static LLViewerPartSourceScript *unpackPSS(LLViewerObject *source_objp, LLViewerPartSourceScript *pssp, LLDataPacker &dp);
+
+ LLViewerImage *getImage() const { return mImagep; }
+ void setImage(LLViewerImage *imagep);
+ LLPartSysData mPartSysData;
+
+ void setTargetObject(LLViewerObject *objp);
+
+protected:
+ LLQuaternion mRotation; // Current rotation for particle source
+ LLPointer<LLViewerImage> mImagep; // Cached image pointer of the mPartSysData UUID
+ LLPointer<LLViewerObject> mSourceObjectp; // Source object that this particle system is attached to
+ LLPointer<LLViewerObject> mTargetObjectp; // Target object for the particle source
+};
+
+
+////////////////////////////
+//
+// Particle source for spiral effect (customize avatar, mostly)
+//
+
+class LLViewerPartSourceSpiral : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceSpiral(const LLVector3 &pos);
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLColor4 mColor;
+protected:
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLVector3d mLKGSourcePosGlobal;
+};
+
+
+////////////////////////////
+//
+// Particle source for tractor(editing) beam
+//
+
+class LLViewerPartSourceBeam : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceBeam();
+ ~LLViewerPartSourceBeam();
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setTargetObject(LLViewerObject *objp);
+ void setSourcePosGlobal(const LLVector3d &pos_global);
+ void setTargetPosGlobal(const LLVector3d &pos_global);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLPointer<LLViewerObject> mTargetObjectp;
+ LLVector3d mLKGTargetPosGlobal;
+ LLColor4 mColor;
+protected:
+};
+
+
+
+//////////////////////////
+//
+// Particle source for chat effect
+//
+
+class LLViewerPartSourceChat : public LLViewerPartSource
+{
+public:
+ LLViewerPartSourceChat(const LLVector3 &pos);
+
+ /*virtual*/ void setDead();
+
+ /*virtual*/ void update(const F32 dt);
+
+ void setSourceObject(LLViewerObject *objp);
+ void setColor(const LLColor4 &color);
+
+ static void updatePart(LLViewerPart &part, const F32 dt);
+ LLColor4 mColor;
+protected:
+ LLPointer<LLViewerImage> mImagep;
+ LLPointer<LLViewerObject> mSourceObjectp;
+ LLVector3d mLKGSourcePosGlobal;
+};
+
+
+#endif // LL_LLVIEWERPARTSOURCE_H
diff --git a/indra/newview/llviewerprecompiledheaders.cpp b/indra/newview/llviewerprecompiledheaders.cpp
new file mode 100644
index 0000000000..3de6af18dd
--- /dev/null
+++ b/indra/newview/llviewerprecompiledheaders.cpp
@@ -0,0 +1,16 @@
+/**
+ * @file llviewerprecompiledheaders.cpp
+ * @brief precompiled headers for newview project
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// source file that includes just the standard includes
+// newview.pch will be the pre-compiled header
+// llviewerprecompiledheaders.obj will contain the pre-compiled type information
+
+#include "llviewerprecompiledheaders.h"
+
+// TODO: reference any additional headers you need in llviewerprecompiledheaders.h
+// and not in this file
diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h
new file mode 100644
index 0000000000..5c50fef58e
--- /dev/null
+++ b/indra/newview/llviewerprecompiledheaders.h
@@ -0,0 +1,218 @@
+/**
+ * @file llviewerprecompiledheaders.h
+ * @brief precompiled headers for newview project
+ * @author James Cook
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#ifndef LL_LLVIEWERPRECOMPILEDHEADERS_H
+#define LL_LLVIEWERPRECOMPILEDHEADERS_H
+
+// This file MUST be the first one included by each .cpp file
+// in viewer.
+// It is used to precompile headers for improved build speed.
+
+// Reference headers your program requires here:
+#include "linden_common.h"
+
+// The rest of the common system headers go here:
+
+// Work around stupid Microsoft STL warning
+#ifdef LL_WINDOWS
+#pragma warning (disable : 4702) // warning C4702: unreachable code
+#endif
+
+#include <algorithm>
+#include <deque>
+#include <functional>
+#include <map>
+#include <set>
+
+#ifdef LL_WINDOWS
+#pragma warning (3 : 4702) // we like level 3, not 4
+#endif
+
+// Library headers from llcommon project:
+#include "bitpack.h"
+#include "doublelinkedlist.h"
+#include "imageids.h"
+#include "indra_constants.h"
+//#include "linden_common.h"
+//#include "llpreprocessor.h"
+#include "linked_lists.h"
+#include "llapp.h"
+#include "llapr.h"
+#include "llassoclist.h"
+#include "llcriticaldamp.h"
+#include "lldarray.h"
+#include "lldarrayptr.h"
+#include "lldefs.h"
+#include "lldepthstack.h"
+#include "lldlinked.h"
+#include "lldqueueptr.h"
+#include "llendianswizzle.h"
+#include "llerror.h"
+#include "llerrorbuffer.h"
+#include "llerrorstream.h"
+#include "llfasttimer.h"
+#include "llfixedbuffer.h"
+#include "llframetimer.h"
+#include "llhash.h"
+#include "lllinkedqueue.h"
+#include "lllocalidhashmap.h"
+#include "llmap.h"
+#include "llmemory.h"
+#include "llnametable.h"
+#include "llpagemem.h"
+#include "llpriqueuemap.h"
+#include "llprocessor.h"
+#include "llptrskiplist.h"
+#include "llptrskipmap.h"
+//#include "llsecondlifeurls.h"
+#include "llskiplist.h"
+#include "llskipmap.h"
+#include "llstack.h"
+#include "llstat.h"
+#include "llstl.h"
+#include "llstrider.h"
+#include "llstring.h"
+#include "llstringtable.h"
+#include "llsys.h"
+#include "llthread.h"
+#include "lltimer.h"
+#include "lluuidhashmap.h"
+//#include "llversion.h"
+//#include "processor.h"
+#include "stdenums.h"
+#include "stdtypes.h"
+//#include "string_table.h"
+//#include "timer.h"
+#include "timing.h"
+#include "u64.h"
+
+// Library includes from llimage
+#include "kdc_flow_control.h"
+#include "kde_flow_control.h"
+#include "kdu_image.h"
+#include "kdu_image_local.h"
+#include "llblockdata.h"
+#include "llblockdecoder.h"
+#include "llblockencoder.h"
+#include "llimage.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llimagejpeg.h"
+#include "llimagetga.h"
+#include "llkdumem.h"
+#include "llmapimagetype.h"
+
+// Library includes from llmath project
+//#include "camera.h"
+//#include "coordframe.h"
+#include "llmath.h"
+#include "llbboxlocal.h"
+#include "llcamera.h"
+#include "llcoord.h"
+#include "llcoordframe.h"
+#include "llcrc.h"
+#include "llinterp.h"
+#include "llmd5.h"
+#include "llperlin.h"
+#include "llplane.h"
+#include "llquantize.h"
+#include "llrand.h"
+#include "llrect.h"
+#include "lluuid.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "llquaternion.h"
+#include "raytrace.h"
+#include "v2math.h"
+#include "v3color.h"
+#include "v3dmath.h"
+#include "v3math.h"
+#include "v4color.h"
+#include "v4coloru.h"
+#include "v4math.h"
+////#include "vmath.h"
+#include "xform.h"
+
+// Library includes from llmessage project
+//#include "llassetstorage.h"
+#include "llcachename.h"
+#include "llcallbacklisth.h"
+#include "llcircuit.h"
+#include "llcrypto.h"
+#include "lldatapacker.h"
+#include "lldbstrings.h"
+#include "lldispatcher.h"
+#include "lleventflags.h"
+#include "llhost.h"
+#include "llinstantmessage.h"
+#include "llinvite.h"
+//#include "llloginflags.h"
+#include "lllogtextmessage.h"
+#include "llmail.h"
+#include "llmessagethrottle.h"
+#include "llnamevalue.h"
+#include "llpacketack.h"
+#include "llpacketbuffer.h"
+#include "llpacketring.h"
+#include "llpartdata.h"
+//#include "llqueryflags.h"
+//#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "lltaskname.h"
+#include "llteleportflags.h"
+#include "llthrottle.h"
+#include "lltransfermanager.h"
+#include "lltransfersourceasset.h"
+#include "lltransfersourcefile.h"
+#include "lltransfertargetfile.h"
+#include "lltransfertargetvfile.h"
+#include "lluseroperation.h"
+#include "llvehicleparams.h"
+#include "llxfer.h"
+#include "llxfer_file.h"
+#include "llxfer_mem.h"
+#include "llxfer_vfile.h"
+#include "llxfermanager.h"
+#include "machine.h"
+#include "mean_collision_data.h"
+#include "message.h"
+#include "message_prehash.h"
+#include "net.h"
+//#include "network.h"
+#include "partsyspacket.h"
+#include "patch_code.h"
+#include "patch_dct.h"
+#include "sound_ids.h"
+
+// Library includes from llprimitive
+#include "imageids.h"
+#include "legacy_object_types.h"
+#include "llmaterialtable.h"
+//#include "llprimitive.h"
+#include "lltextureanim.h"
+//#include "lltextureentry.h"
+#include "lltreeparams.h"
+//#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "material_codes.h"
+
+// Library includes from llxml
+#include "llxmlnode.h"
+
+// Library includes from llvfs
+#include "llassettype.h"
+#include "lldir.h"
+//#include "lldir_linux.h"
+//#include "lldir_mac.h"
+//#include "lldir_win32.h"
+#include "llvfile.h"
+#include "llvfs.h"
+
+#endif
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
new file mode 100644
index 0000000000..bcbc81ce5c
--- /dev/null
+++ b/indra/newview/llviewerregion.cpp
@@ -0,0 +1,1301 @@
+/**
+ * @file llviewerregion.cpp
+ * @brief Implementation of the LLViewerRegion class.
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerregion.h"
+
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llhttpclient.h"
+#include "llregionflags.h"
+#include "llregionhandle.h"
+#include "llsdmessagesystem.h"
+#include "llsurface.h"
+#include "message.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "v4math.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "lldir.h"
+#include "lleventpoll.h"
+#include "llfloatergodtools.h"
+#include "llfloaterregioninfo.h"
+#include "llhttpnode.h"
+#include "llnetmap.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llvlmanager.h"
+#include "llvlcomposition.h"
+#include "llvocache.h"
+#include "llvoclouds.h"
+#include "llworld.h"
+
+extern BOOL gNoRender;
+
+const F32 WATER_TEXTURE_SCALE = 8.f; // Number of times to repeat the water texture across a region
+const S16 MAX_MAP_DIST = 10;
+
+
+
+
+LLViewerRegion::LLViewerRegion(const U64 &handle,
+ const LLHost &host,
+ const U32 grids_per_region_edge,
+ const U32 grids_per_patch_edge,
+ const F32 region_width_meters)
+: mCenterGlobal(),
+ mHandle(handle),
+ mHost( host ),
+ mTimeDilation(1.0f),
+ mName(""),
+ mZoning(""),
+ mOwnerID(),
+ mIsEstateManager(FALSE),
+ mCompositionp(NULL),
+ mRegionFlags( REGION_FLAGS_DEFAULT ),
+ mSimAccess( SIM_ACCESS_MIN ),
+ mBillableFactor(1.0),
+ mMaxTasks(MAX_TASKS_PER_REGION),
+ mCacheLoaded(FALSE),
+ mCacheMap(),
+ mCacheEntriesCount(0),
+ mCacheID(),
+ mEventPoll(NULL)
+{
+ mWidth = region_width_meters;
+
+ mOriginGlobal = from_region_handle(handle);
+
+ mLandp = new LLSurface('l', NULL);
+ if (!gNoRender)
+ {
+ // Create the composition layer for the surface
+ mCompositionp = new LLVLComposition(mLandp, grids_per_region_edge, region_width_meters/grids_per_region_edge);
+ mCompositionp->setSurface(mLandp);
+
+ // Create the surfaces
+ mLandp->setRegion(this);
+ mLandp->create(grids_per_region_edge,
+ grids_per_patch_edge,
+ mOriginGlobal,
+ mWidth);
+ }
+
+ if (!gNoRender)
+ {
+ mParcelOverlay = new LLViewerParcelOverlay(this, region_width_meters);
+ }
+ else
+ {
+ mParcelOverlay = NULL;
+ }
+
+ setOriginGlobal(from_region_handle(handle));
+ calculateCenterGlobal();
+
+ // Create the object lists
+ initStats();
+
+ mCacheStart.append(mCacheEnd);
+
+}
+
+
+void LLViewerRegion::initStats()
+{
+ mLastNetUpdate.reset();
+ mPacketsIn = 0;
+ mBitsIn = 0;
+ mLastBitsIn = 0;
+ mLastPacketsIn = 0;
+ mPacketsOut = 0;
+ mLastPacketsOut = 0;
+ mPacketsLost = 0;
+ mLastPacketsLost = 0;
+ mPingDelay = 0;
+ mAlive = FALSE; // can become false if circuit disconnects
+}
+
+
+
+LLViewerRegion::~LLViewerRegion()
+{
+ gVLManager.cleanupData(this);
+ // Can't do this on destruction, because the neighbor pointers might be invalid.
+ // This should be reference counted...
+ disconnectAllNeighbors();
+ mCloudLayer.destroy();
+ gWorldPointer->mPartSim.cleanupRegion(this);
+
+ gObjectList.killObjects(this);
+
+ delete mCompositionp;
+ delete mParcelOverlay;
+ delete mLandp;
+ delete mEventPoll;
+
+ saveCache();
+}
+
+
+void LLViewerRegion::loadCache()
+{
+ if (mCacheLoaded)
+ {
+ return;
+ }
+
+ // Presume success. If it fails, we don't want to try again.
+ mCacheLoaded = TRUE;
+
+ LLVOCacheEntry *entry;
+
+ char filename[256];
+ sprintf(filename, "%s%sobjects_%d_%d.slc",
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ U32(mHandle>>32)/REGION_WIDTH_UNITS,
+ U32(mHandle)/REGION_WIDTH_UNITS );
+
+ FILE *fp = LLFile::fopen(filename, "rb");
+ if (!fp)
+ {
+ // might not have a file, which is normal
+ return;
+ }
+
+ U32 zero;
+ fread(&zero, 1, sizeof(U32), fp);
+ if (zero)
+ {
+ // a non-zero value here means bad things!
+ // skip reading the cached values
+ llinfos << "Cache file invalid" << llendl;
+ fclose(fp);
+ return;
+ }
+
+ U32 version;
+ fread(&version, 1, sizeof(U32), fp);
+ if (version != INDRA_OBJECT_CACHE_VERSION)
+ {
+ // a version mismatch here means we've changed the binary format!
+ // skip reading the cached values
+ llinfos << "Cache version changed, discarding" << llendl;
+ fclose(fp);
+ return;
+ }
+
+ LLUUID cache_id;
+ fread(&cache_id.mData, UUID_BYTES, sizeof(U8), fp);
+ if (mCacheID != cache_id)
+ {
+ llinfos << "Cache ID doesn't match for this region, discarding"
+ << llendl;
+ fclose(fp);
+ return;
+ }
+
+ S32 num_entries;
+ fread(&num_entries, 1, sizeof(S32), fp);
+ S32 i;
+ for (i = 0; i < num_entries; i++)
+ {
+ entry = new LLVOCacheEntry(fp);
+ if (!entry->getLocalID())
+ {
+ llwarns << "Aborting cache file load for " << filename << ", cache file corruption!" << llendl;
+ delete entry;
+ entry = NULL;
+ break;
+ }
+ mCacheEnd.insert(*entry);
+ mCacheMap[entry->getLocalID()] = entry;
+ mCacheEntriesCount++;
+ }
+
+ fclose(fp);
+}
+
+
+void LLViewerRegion::saveCache()
+{
+ if (!mCacheLoaded)
+ {
+ return;
+ }
+
+ S32 num_entries = mCacheEntriesCount;
+ if (0 == num_entries)
+ {
+ return;
+ }
+
+ char filename[256];
+ sprintf(filename, "%s%sobjects_%d_%d.slc",
+ gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),
+ gDirUtilp->getDirDelimiter().c_str(),
+ U32(mHandle>>32)/REGION_WIDTH_UNITS,
+ U32(mHandle)/REGION_WIDTH_UNITS );
+
+ FILE *fp = LLFile::fopen(filename, "wb");
+ if (!fp)
+ {
+ llwarns << "Unable to write cache file " << filename << llendl;
+ return;
+ }
+
+ // write out zero to indicate a version cache file
+ U32 zero = 0;
+ fwrite(&zero, 1, sizeof(U32), fp);
+
+ // write out version number
+ U32 version = INDRA_OBJECT_CACHE_VERSION;
+ fwrite(&version, 1, sizeof(U32), fp);
+
+ // write the cache id for this sim
+ fwrite(&mCacheID.mData, UUID_BYTES, sizeof(U8), fp);
+
+ fwrite(&num_entries, 1, sizeof(S32), fp);
+
+ LLVOCacheEntry *entry;
+
+ for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
+ {
+ entry->writeToFile(fp);
+ }
+
+ mCacheMap.removeAllData();
+ mCacheEnd.unlink();
+ mCacheEnd.init();
+ mCacheStart.deleteAll();
+ mCacheStart.init();
+
+ fclose(fp);
+}
+
+void LLViewerRegion::sendMessage()
+{
+ gMessageSystem->sendMessage(mHost);
+}
+
+void LLViewerRegion::sendReliableMessage()
+{
+ gMessageSystem->sendReliable(mHost);
+}
+
+
+void LLViewerRegion::setAllowDamage(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_DAMAGE;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_DAMAGE;
+ }
+}
+
+
+void LLViewerRegion::setAllowLandmark(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_LANDMARK;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_LANDMARK;
+ }
+}
+
+void LLViewerRegion::setAllowSetHome(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_SET_HOME;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_SET_HOME;
+ }
+}
+
+void LLViewerRegion::setResetHomeOnTeleport(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_RESET_HOME_ON_TELEPORT;
+ }
+}
+
+void LLViewerRegion::setSunFixed(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_SUN_FIXED;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_SUN_FIXED;
+ }
+}
+
+void LLViewerRegion::setBlockFly(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_BLOCK_FLY;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_BLOCK_FLY;
+ }
+}
+
+void LLViewerRegion::setAllowDirectTeleport(BOOL b)
+{
+ if (b)
+ {
+ mRegionFlags |= REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+ else
+ {
+ mRegionFlags &= ~REGION_FLAGS_ALLOW_DIRECT_TELEPORT;
+ }
+}
+
+void LLViewerRegion::setWaterHeight(F32 water_level)
+{
+ mLandp->setWaterHeight(water_level);
+}
+
+F32 LLViewerRegion::getWaterHeight() const
+{
+ return mLandp->getWaterHeight();
+}
+
+void LLViewerRegion::setRegionFlags(U32 flags)
+{
+ mRegionFlags = flags;
+}
+
+
+void LLViewerRegion::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+ mLandp->setOriginGlobal(origin_global);
+ mWind.setOriginGlobal(origin_global);
+ mCloudLayer.setOriginGlobal(origin_global);
+ calculateCenterGlobal();
+}
+
+
+void LLViewerRegion::setTimeDilation(F32 time_dilation)
+{
+ mTimeDilation = time_dilation;
+}
+
+
+LLVector3 LLViewerRegion::getOriginAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mOriginGlobal);
+}
+
+
+LLVector3 LLViewerRegion::getCenterAgent() const
+{
+ return gAgent.getPosAgentFromGlobal(mCenterGlobal);
+}
+
+
+void LLViewerRegion::setRegionNameAndZone(const char* name_and_zone)
+{
+ LLString name_zone(name_and_zone);
+ std::string::size_type pipe_pos = name_zone.find('|');
+ S32 length = name_zone.size();
+ if (pipe_pos != std::string::npos)
+ {
+ mName = name_zone.substr(0, pipe_pos);
+ mZoning = name_zone.substr(pipe_pos+1, length-(pipe_pos+1));
+ }
+ else
+ {
+ mName = name_zone;
+ mZoning = "";
+ }
+
+ LLString::stripNonprintable(mName);
+ LLString::stripNonprintable(mZoning);
+}
+
+BOOL LLViewerRegion::canManageEstate() const
+{
+ return gAgent.isGodlike()
+ || isEstateManager()
+ || gAgent.getID() == getOwner();
+}
+
+const char* LLViewerRegion::getSimAccessString() const
+{
+ return accessToString(mSimAccess);
+}
+
+
+// static
+std::string LLViewerRegion::regionFlagsToString(U32 flags)
+{
+ std::string result;
+
+ if (flags & REGION_FLAGS_SANDBOX)
+ {
+ result += "Sandbox";
+ }
+
+ if (flags & REGION_FLAGS_ALLOW_DAMAGE)
+ {
+ result += " Not Safe";
+ }
+
+ return result;
+}
+
+char* SIM_ACCESS_STR[] = { "Free Trial",
+ "PG",
+ "Mature",
+ "Offline",
+ "Unknown" };
+
+// static
+const char* LLViewerRegion::accessToString(U8 access)
+{
+ switch(access)
+ {
+ case SIM_ACCESS_TRIAL:
+ return SIM_ACCESS_STR[0];
+
+ case SIM_ACCESS_PG:
+ return SIM_ACCESS_STR[1];
+
+ case SIM_ACCESS_MATURE:
+ return SIM_ACCESS_STR[2];
+
+ case SIM_ACCESS_DOWN:
+ return SIM_ACCESS_STR[3];
+
+ case SIM_ACCESS_MIN:
+ default:
+ return SIM_ACCESS_STR[4];
+ }
+}
+
+// static
+U8 LLViewerRegion::stringToAccess(const char* access_str)
+{
+ U8 access = SIM_ACCESS_MIN;
+ if (0 == strcmp(access_str, SIM_ACCESS_STR[0]))
+ {
+ access = SIM_ACCESS_TRIAL;
+ }
+ else if (0 == strcmp(access_str, SIM_ACCESS_STR[1]))
+ {
+ access = SIM_ACCESS_PG;
+ }
+ else if (0 == strcmp(access_str, SIM_ACCESS_STR[2]))
+ {
+ access = SIM_ACCESS_MATURE;
+ }
+ return access;
+}
+
+// static
+const char* LLViewerRegion::accessToShortString(U8 access)
+{
+ switch(access)
+ {
+ case SIM_ACCESS_PG:
+ return "PG";
+
+ case SIM_ACCESS_TRIAL:
+ return "TR";
+
+ case SIM_ACCESS_MATURE:
+ return "M";
+
+ case SIM_ACCESS_MIN:
+ default:
+ return "U";
+ }
+}
+
+// static
+void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**)
+{
+ // send it to 'observers'
+ LLFloaterGodTools::processRegionInfo(msg);
+ LLFloaterRegionInfo::processRegionInfo(msg);
+}
+
+
+
+S32 LLViewerRegion::renderPropertyLines()
+{
+ if (mParcelOverlay)
+ {
+ return mParcelOverlay->renderPropertyLines();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// This gets called when the height field changes.
+void LLViewerRegion::dirtyHeights()
+{
+ // Property lines need to be reconstructed when the land changes.
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->setDirty();
+ }
+}
+
+BOOL LLViewerRegion::idleUpdate(LLTimer &timer, const F32 max_time)
+{
+ BOOL done = mLandp->idleUpdate();
+
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->idleUpdate();
+ }
+
+ return done;
+}
+
+
+// As above, but forcibly do the update.
+void LLViewerRegion::forceUpdate()
+{
+ mLandp->idleUpdate();
+
+ if (mParcelOverlay)
+ {
+ mParcelOverlay->idleUpdate(true);
+ }
+}
+
+void LLViewerRegion::connectNeighbor(LLViewerRegion *neighborp, U32 direction)
+{
+ mLandp->connectNeighbor(neighborp->mLandp, direction);
+ mCloudLayer.connectNeighbor(&(neighborp->mCloudLayer), direction);
+}
+
+
+void LLViewerRegion::disconnectAllNeighbors()
+{
+ mLandp->disconnectAllNeighbors();
+ mCloudLayer.disconnectAllNeighbors();
+}
+
+
+F32 LLViewerRegion::getCompositionXY(const S32 x, const S32 y) const
+{
+ if (x >= 256)
+ {
+ if (y >= 256)
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(256.f, 256.f, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled(255, 255);
+ F32 adj_comp = regionp->getComposition()->getValueScaled(x - 256.f, y - 256.f);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+ else
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(256.f, 0, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled(255.f, (F32)y);
+ F32 adj_comp = regionp->getComposition()->getValueScaled(x - 256.f, (F32)y);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+ }
+ else if (y >= 256)
+ {
+ LLVector3d center = getCenterGlobal() + LLVector3d(0.f, 256.f, 0.f);
+ LLViewerRegion *regionp = gWorldPointer->getRegionFromPosGlobal(center);
+ if (regionp)
+ {
+ // OK, we need to do some hackery here - different simulators no longer use
+ // the same composition values, necessarily.
+ // If we're attempting to blend, then we want to make the fractional part of
+ // this region match the fractional of the adjacent. For now, just minimize
+ // the delta.
+ F32 our_comp = getComposition()->getValueScaled((F32)x, 255.f);
+ F32 adj_comp = regionp->getComposition()->getValueScaled((F32)x, y - 256.f);
+ while (llabs(our_comp - adj_comp) >= 1.f)
+ {
+ if (our_comp > adj_comp)
+ {
+ adj_comp += 1.f;
+ }
+ else
+ {
+ adj_comp -= 1.f;
+ }
+ }
+ return adj_comp;
+ }
+ }
+
+ return getComposition()->getValueScaled((F32)x, (F32)y);
+}
+
+
+// ---------------- Friends ----------------
+
+std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region)
+{
+ s << "{ ";
+ s << region.mHost;
+ s << " mOriginGlobal = " << region.getOriginGlobal()<< "\n";
+ s << "}";
+ return s;
+}
+
+
+// ---------------- Protected Member Functions ----------------
+
+void LLViewerRegion::calculateCenterGlobal()
+{
+ mCenterGlobal = mOriginGlobal;
+ mCenterGlobal.mdV[VX] += 0.5 * mWidth;
+ mCenterGlobal.mdV[VY] += 0.5 * mWidth;
+ if (mLandp)
+ {
+ mCenterGlobal.mdV[VZ] = 0.5*mLandp->getMinZ() + mLandp->getMaxZ();
+ }
+ else
+ {
+ mCenterGlobal.mdV[VZ] = F64( getWaterHeight() );
+ }
+}
+
+
+
+void LLViewerRegion::updateNetStats()
+{
+ F32 dt = mLastNetUpdate.getElapsedTimeAndResetF32();
+
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mHost);
+ if (!cdp)
+ {
+ mAlive = FALSE;
+ return;
+ }
+
+ mAlive = TRUE;
+ mDeltaTime = dt;
+
+ mLastPacketsIn = mPacketsIn;
+ mLastBitsIn = mBitsIn;
+ mLastPacketsOut = mPacketsOut;
+ mLastPacketsLost = mPacketsLost;
+
+ mPacketsIn = cdp->getPacketsIn();
+ mBitsIn = 8 * cdp->getBytesIn();
+ mPacketsOut = cdp->getPacketsOut();
+ mPacketsLost = cdp->getPacketsLost();
+ mPingDelay = cdp->getPingDelay();
+
+ mBitStat.addValue(mBitsIn - mLastBitsIn);
+ mPacketsStat.addValue(mPacketsIn - mLastPacketsIn);
+ mPacketsLostStat.addValue(mPacketsLost);
+}
+
+
+U32 LLViewerRegion::getPacketsLost() const
+{
+ LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(mHost);
+ if (!cdp)
+ {
+ llinfos << "LLViewerRegion::getPacketsLost couldn't find circuit for " << mHost << llendl;
+ return 0;
+ }
+ else
+ {
+ return cdp->getPacketsLost();
+ }
+}
+
+BOOL LLViewerRegion::pointInRegionGlobal(const LLVector3d &point_global) const
+{
+ LLVector3 pos_region = getPosRegionFromGlobal(point_global);
+
+ if (pos_region.mV[VX] < 0)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VX] >= mWidth)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VY] < 0)
+ {
+ return FALSE;
+ }
+ if (pos_region.mV[VY] >= mWidth)
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+LLVector3 LLViewerRegion::getPosRegionFromGlobal(const LLVector3d &point_global) const
+{
+ LLVector3 pos_region;
+ pos_region.setVec(point_global - mOriginGlobal);
+ return pos_region;
+}
+
+LLVector3d LLViewerRegion::getPosGlobalFromRegion(const LLVector3 &pos_region) const
+{
+ LLVector3d pos_region_d;
+ pos_region_d.setVec(pos_region);
+ return pos_region_d + mOriginGlobal;
+}
+
+LLVector3 LLViewerRegion::getPosAgentFromRegion(const LLVector3 &pos_region) const
+{
+ LLVector3d pos_global = getPosGlobalFromRegion(pos_region);
+
+ return gAgent.getPosAgentFromGlobal(pos_global);
+}
+
+LLVector3 LLViewerRegion::getPosRegionFromAgent(const LLVector3 &pos_agent) const
+{
+ return getPosRegionFromGlobal(gAgent.getPosGlobalFromAgent(pos_agent));
+}
+
+F32 LLViewerRegion::getLandHeightRegion(const LLVector3& region_pos)
+{
+ return mLandp->resolveHeightRegion( region_pos );
+}
+
+BOOL LLViewerRegion::isOwnedSelf(const LLVector3& pos)
+{
+ return mParcelOverlay->isOwnedSelf(pos);
+}
+
+// Owned by a group you belong to? (officer or member)
+BOOL LLViewerRegion::isOwnedGroup(const LLVector3& pos)
+{
+ return mParcelOverlay->isOwnedGroup(pos);
+}
+
+void LLViewerRegion::updateCoarseLocations(LLMessageSystem* msg)
+{
+ //llinfos << "CoarseLocationUpdate" << llendl;
+ mMapAvatars.reset();
+
+ U8 x_pos = 0;
+ U8 y_pos = 0;
+ U8 z_pos = 0;
+
+ U32 pos = 0x0;
+
+ S16 agent_index;
+ S16 target_index;
+ msg->getS16Fast(_PREHASH_Index, _PREHASH_You, agent_index);
+ msg->getS16Fast(_PREHASH_Index, _PREHASH_Prey, target_index);
+
+ S32 count = msg->getNumberOfBlocksFast(_PREHASH_Location);
+ for(S32 i = 0; i < count; i++)
+ {
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_X, x_pos, i);
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_Y, y_pos, i);
+ msg->getU8Fast(_PREHASH_Location, _PREHASH_Z, z_pos, i);
+
+ //llinfos << " object X: " << (S32)x_pos << " Y: " << (S32)y_pos
+ // << " Z: " << (S32)(z_pos * 4)
+ // << llendl;
+
+ // treat the target specially for the map, and don't add you
+ // or the target
+ if(i == target_index)
+ {
+ LLVector3d global_pos(mOriginGlobal);
+ global_pos.mdV[VX] += (F64)(x_pos);
+ global_pos.mdV[VY] += (F64)(y_pos);
+ global_pos.mdV[VZ] += (F64)(z_pos) * 4.0;
+ LLAvatarTracker::instance().setTrackedCoarseLocation(global_pos);
+ }
+ else if( i != agent_index)
+ {
+ pos = 0x0;
+ pos |= x_pos;
+ pos <<= 8;
+ pos |= y_pos;
+ pos <<= 8;
+ pos |= z_pos;
+ mMapAvatars.put(pos);
+ }
+ }
+}
+
+LLString LLViewerRegion::getInfoString()
+{
+ char tmp_buf[256];
+ LLString info;
+
+ info = "Region: ";
+ getHost().getString(tmp_buf, 256);
+ info += tmp_buf;
+ info += ":";
+ info += getName();
+ info += "\n";
+
+ U32 x, y;
+ from_region_handle(getHandle(), &x, &y);
+ sprintf(tmp_buf, "%d:%d", x, y);
+ info += "Handle:";
+ info += tmp_buf;
+ info += "\n";
+ return info;
+}
+
+
+void LLViewerRegion::cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp)
+{
+ U32 local_id = objectp->getLocalID();
+ U32 crc = objectp->getCRC();
+
+ LLVOCacheEntry *entry = mCacheMap.getIfThere(local_id);
+
+ if (entry)
+ {
+ // we've seen this object before
+ if (entry->getCRC() == crc)
+ {
+ // Record a hit
+ entry->recordDupe();
+ }
+ else
+ {
+ // Update the cache entry
+ mCacheMap.removeData(local_id);
+ delete entry;
+ entry = new LLVOCacheEntry(local_id, crc, dp);
+ mCacheEnd.insert(*entry);
+ mCacheMap[local_id] = entry;
+ }
+ }
+ else
+ {
+ // we haven't seen this object before
+
+ // Create new entry and add to map
+ if (mCacheEntriesCount > MAX_OBJECT_CACHE_ENTRIES)
+ {
+ entry = mCacheStart.getNext();
+ mCacheMap.removeData(entry->getLocalID());
+ delete entry;
+ mCacheEntriesCount--;
+ }
+ entry = new LLVOCacheEntry(local_id, crc, dp);
+
+ mCacheEnd.insert(*entry);
+ mCacheMap[local_id] = entry;
+ mCacheEntriesCount++;
+ }
+ return ;
+}
+
+// Get data packer for this object, if we have cached data
+// AND the CRC matches. JC
+LLDataPacker *LLViewerRegion::getDP(U32 local_id, U32 crc)
+{
+ llassert(mCacheLoaded);
+
+ LLVOCacheEntry *entry = mCacheMap.getIfThere(local_id);
+
+ if (entry)
+ {
+ // we've seen this object before
+ if (entry->getCRC() == crc)
+ {
+ // Record a hit
+ entry->recordHit();
+ return entry->getDP(crc);
+ }
+ else
+ {
+ // llinfos << "CRC miss for " << local_id << llendl;
+ mCacheMissCRC.put(local_id);
+ }
+ }
+ else
+ {
+ // llinfos << "Cache miss for " << local_id << llendl;
+ mCacheMissFull.put(local_id);
+ }
+ return NULL;
+}
+
+void LLViewerRegion::addCacheMissFull(const U32 local_id)
+{
+ mCacheMissFull.put(local_id);
+}
+
+void LLViewerRegion::requestCacheMisses()
+{
+ S32 full_count = mCacheMissFull.count();
+ S32 crc_count = mCacheMissCRC.count();
+ if (full_count == 0 && crc_count == 0) return;
+
+ LLMessageSystem* msg = gMessageSystem;
+ BOOL start_new_message = TRUE;
+ S32 blocks = 0;
+ S32 i;
+
+ const U8 CACHE_MISS_TYPE_FULL = 0;
+ const U8 CACHE_MISS_TYPE_CRC = 1;
+
+ // Send full cache miss updates. For these, we KNOW we don't
+ // have a viewer object.
+ for (i = 0; i < full_count; i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_RequestMultipleObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU8Fast(_PREHASH_CacheMissType, CACHE_MISS_TYPE_FULL);
+ msg->addU32Fast(_PREHASH_ID, mCacheMissFull[i]);
+ blocks++;
+
+ if (blocks >= 255)
+ {
+ sendReliableMessage();
+ start_new_message = TRUE;
+ blocks = 0;
+ }
+ }
+
+ // Send CRC miss updates. For these, we _might_ have a viewer object,
+ // but probably not.
+ for (i = 0; i < crc_count; i++)
+ {
+ if (start_new_message)
+ {
+ msg->newMessageFast(_PREHASH_RequestMultipleObjects);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ start_new_message = FALSE;
+ }
+
+ msg->nextBlockFast(_PREHASH_ObjectData);
+ msg->addU8Fast(_PREHASH_CacheMissType, CACHE_MISS_TYPE_CRC);
+ msg->addU32Fast(_PREHASH_ID, mCacheMissCRC[i]);
+ blocks++;
+
+ if (blocks >= 255)
+ {
+ sendReliableMessage();
+ start_new_message = TRUE;
+ blocks = 0;
+ }
+ }
+
+ // finish any pending message
+ if (!start_new_message)
+ {
+ sendReliableMessage();
+ }
+ mCacheMissFull.reset();
+ mCacheMissCRC.reset();
+
+ // llinfos << "KILLDEBUG Sent cache miss full " << full_count << " crc " << crc_count << llendl;
+}
+
+void LLViewerRegion::dumpCache()
+{
+ const S32 BINS = 4;
+ S32 hit_bin[BINS];
+ S32 change_bin[BINS];
+
+ S32 i;
+ for (i = 0; i < BINS; ++i)
+ {
+ hit_bin[i] = 0;
+ change_bin[i] = 0;
+ }
+
+ LLVOCacheEntry *entry;
+
+ for (entry = mCacheStart.getNext(); entry && (entry != &mCacheEnd); entry = entry->getNext())
+ {
+ S32 hits = entry->getHitCount();
+ S32 changes = entry->getCRCChangeCount();
+
+ hits = llclamp(hits, 0, BINS-1);
+ changes = llclamp(changes, 0, BINS-1);
+
+ hit_bin[hits]++;
+ change_bin[changes]++;
+ }
+
+ llinfos << "Count " << mCacheEntriesCount << llendl;
+ for (i = 0; i < BINS; i++)
+ {
+ llinfos << "Hits " << i << " " << hit_bin[i] << llendl;
+ }
+ for (i = 0; i < BINS; i++)
+ {
+ llinfos << "Changes " << i << " " << change_bin[i] << llendl;
+ }
+}
+
+void LLViewerRegion::unpackRegionHandshake()
+{
+ LLMessageSystem *msg = gMessageSystem;
+
+ const S32 SIM_NAME_BUF = 256;
+ U32 region_flags;
+ U8 sim_access;
+ char sim_name[SIM_NAME_BUF];
+ LLUUID sim_owner;
+ BOOL is_estate_manager;
+ F32 water_height;
+ F32 billable_factor;
+ LLUUID cache_id;
+
+ msg->getU32 ("RegionInfo", "RegionFlags", region_flags);
+ msg->getU8 ("RegionInfo", "SimAccess", sim_access);
+ msg->getString ("RegionInfo", "SimName", SIM_NAME_BUF, sim_name);
+ msg->getUUID ("RegionInfo", "SimOwner", sim_owner);
+ msg->getBOOL ("RegionInfo", "IsEstateManager", is_estate_manager);
+ msg->getF32 ("RegionInfo", "WaterHeight", water_height);
+ msg->getF32 ("RegionInfo", "BillableFactor", billable_factor);
+ msg->getUUID ("RegionInfo", "CacheID", cache_id );
+
+ setRegionFlags(region_flags);
+ setSimAccess(sim_access);
+ setRegionNameAndZone(sim_name);
+ setOwner(sim_owner);
+ setIsEstateManager(is_estate_manager);
+ setWaterHeight(water_height);
+ setBillableFactor(billable_factor);
+ setCacheID(cache_id);
+
+ LLVLComposition *compp = getComposition();
+ if (compp)
+ {
+ LLUUID tmp_id;
+
+ msg->getUUID("RegionInfo", "TerrainDetail0", tmp_id);
+ compp->setDetailTextureID(0, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail1", tmp_id);
+ compp->setDetailTextureID(1, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail2", tmp_id);
+ compp->setDetailTextureID(2, tmp_id);
+ msg->getUUID("RegionInfo", "TerrainDetail3", tmp_id);
+ compp->setDetailTextureID(3, tmp_id);
+
+ F32 tmp_f32;
+ msg->getF32("RegionInfo", "TerrainStartHeight00", tmp_f32);
+ compp->setStartHeight(0, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight01", tmp_f32);
+ compp->setStartHeight(1, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight10", tmp_f32);
+ compp->setStartHeight(2, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainStartHeight11", tmp_f32);
+ compp->setStartHeight(3, tmp_f32);
+
+ msg->getF32("RegionInfo", "TerrainHeightRange00", tmp_f32);
+ compp->setHeightRange(0, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange01", tmp_f32);
+ compp->setHeightRange(1, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange10", tmp_f32);
+ compp->setHeightRange(2, tmp_f32);
+ msg->getF32("RegionInfo", "TerrainHeightRange11", tmp_f32);
+ compp->setHeightRange(3, tmp_f32);
+
+ // If this is an UPDATE (params already ready, we need to regenerate
+ // all of our terrain stuff, by
+ if (compp->getParamsReady())
+ {
+ getLand().dirtyAllPatches();
+ }
+ else
+ {
+ compp->setParamsReady();
+ }
+ }
+
+
+ // Now that we have the name, we can load the cache file
+ // off disk.
+ loadCache();
+
+ // After loading cache, signal that simulator can start
+ // sending data.
+ // TODO: Send all upstream viewer->sim handshake info here.
+ LLHost host = msg->getSender();
+ msg->newMessage("RegionHandshakeReply");
+ msg->nextBlock("AgentData");
+ msg->addUUID("AgentID", gAgent.getID());
+ msg->addUUID("SessionID", gAgent.getSessionID());
+ msg->nextBlock("RegionInfo");
+ msg->addU32("Flags", 0x0 );
+ msg->sendReliable(host);
+}
+
+
+
+class BaseCapabilitiesComplete : public LLHTTPClient::Responder
+{
+public:
+ BaseCapabilitiesComplete(LLViewerRegion* region)
+ : mRegion(region)
+ { }
+
+ void error(U32 statusNum, const std::string& reason)
+ {
+ llinfos << "BaseCapabilitiesComplete::error "
+ << statusNum << ": " << reason << llendl;
+ }
+
+ void result(const LLSD& content)
+ {
+ LLSD::map_const_iterator iter;
+ for(iter = content.beginMap(); iter != content.endMap(); ++iter)
+ {
+ mRegion->setCapability(iter->first, iter->second);
+ llinfos << "BaseCapabilitiesComplete::result got capability for "
+ << iter->first << llendl;
+ }
+ }
+
+ static boost::intrusive_ptr<BaseCapabilitiesComplete> build(
+ LLViewerRegion* region)
+ {
+ return boost::intrusive_ptr<BaseCapabilitiesComplete>(
+ new BaseCapabilitiesComplete(region));
+ }
+
+private:
+ LLViewerRegion* mRegion;
+};
+
+
+void LLViewerRegion::setSeedCapability(const std::string& url)
+{
+ delete mEventPoll;
+ mEventPoll = NULL;
+
+ mCapabilities.clear();
+ setCapability("Seed", url);
+
+ LLSD capabilityNames = LLSD::emptyArray();
+ capabilityNames.append("MapLayer");
+ capabilityNames.append("MapLayerGod");
+ capabilityNames.append("NewAgentInventory");
+ capabilityNames.append("EventQueueGet");
+ LLHTTPClient::post(url, capabilityNames, BaseCapabilitiesComplete::build(this));
+}
+
+static
+LLEventPoll* createViewerEventPoll(const std::string& url)
+{
+ static LLHTTPNode eventRoot;
+ static bool eventRootServicesAdded = false;
+ if (!eventRootServicesAdded)
+ {
+ LLSDMessageSystem::useServices();
+ LLHTTPRegistrar::buildAllServices(eventRoot);
+ eventRootServicesAdded = true;
+ }
+
+ return new LLEventPoll(url, eventRoot);
+}
+
+
+void LLViewerRegion::setCapability(const std::string& name, const std::string& url)
+{
+ mCapabilities[name] = url;
+
+ if (name == "EventQueueGet")
+ {
+ delete mEventPoll;
+ mEventPoll = NULL;
+ mEventPoll = createViewerEventPoll(url);
+ }
+}
+
+std::string LLViewerRegion::getCapability(const std::string& name) const
+{
+ CapabilityMap::const_iterator iter = mCapabilities.find(name);
+ if(iter == mCapabilities.end())
+ {
+ return "";
+ }
+ return iter->second;
+}
+
diff --git a/indra/newview/llviewerregion.h b/indra/newview/llviewerregion.h
new file mode 100644
index 0000000000..984655144b
--- /dev/null
+++ b/indra/newview/llviewerregion.h
@@ -0,0 +1,348 @@
+/**
+ * @file llviewerregion.h
+ * @brief Description of the LLViewerRegion class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERREGION_H
+#define LL_LLVIEWERREGION_H
+
+// A ViewerRegion is a class that contains a bunch of objects and surfaces
+// that are in to a particular region.
+#include <string>
+
+#include "lldarray.h"
+#include "llwind.h"
+#include "llcloud.h"
+#include "llstat.h"
+#include "v3dmath.h"
+#include "llhost.h"
+#include "llstring.h"
+#include "llregionflags.h"
+#include "llptrskipmap.h"
+#include "lluuid.h"
+#include "lldatapacker.h"
+#include "llvocache.h"
+
+// Surface id's
+#define LAND 1
+#define WATER 2
+const U32 MAX_OBJECT_CACHE_ENTRIES = 10000;
+
+
+class LLEventPoll;
+class LLVLComposition;
+class LLViewerObject;
+class LLMessageSystem;
+class LLNetMap;
+class LLViewerParcelOverlay;
+class LLSurface;
+class LLVOCache;
+class LLVOCacheEntry;
+
+class LLViewerRegion
+{
+public:
+ LLViewerRegion(const U64 &handle,
+ const LLHost &host,
+ const U32 surface_grid_width,
+ const U32 patch_grid_width,
+ const F32 region_width_meters);
+ ~LLViewerRegion();
+
+ // Call this after you have the region name and handle.
+ void loadCache();
+
+ void saveCache();
+
+ void sendMessage(); // Send the current message to this region's simulator
+ void sendReliableMessage(); // Send the current message to this region's simulator
+
+ void setOriginGlobal(const LLVector3d &origin);
+ void setAgentOffset(const LLVector3d &offset);
+
+ void setAllowDamage(BOOL b);
+ void setAllowLandmark(BOOL b);
+ void setAllowSetHome(BOOL b);
+ void setResetHomeOnTeleport(BOOL b);
+ void setSunFixed(BOOL b);
+ void setBlockFly(BOOL b);
+ void setAllowDirectTeleport(BOOL b);
+
+ inline BOOL getAllowDamage() const;
+ inline BOOL getAllowLandmark() const;
+ inline BOOL getAllowSetHome() const;
+ inline BOOL getResetHomeOnTeleport() const;
+ inline BOOL getSunFixed() const;
+ inline BOOL getBlockFly() const;
+ inline BOOL getAllowDirectTeleport() const;
+ inline BOOL isPrelude() const;
+ inline BOOL getAllowTerraform() const;
+ inline BOOL getRestrictPushObject() const;
+
+ void setWaterHeight(F32 water_level);
+ F32 getWaterHeight() const;
+
+ void setBillableFactor(F32 billable_factor) { mBillableFactor = billable_factor; }
+ F32 getBillableFactor() const { return mBillableFactor; }
+
+ // Maximum number of primitives allowed, regardless of object
+ // bonus factor.
+ U32 getMaxTasks() const { return mMaxTasks; }
+ void setMaxTasks(U32 max_tasks) { mMaxTasks = max_tasks; }
+
+ // Draw lines in the dirt showing ownership. Return number of
+ // vertices drawn.
+ S32 renderPropertyLines();
+
+ // Call this whenever you change the height data in the region.
+ // (Automatically called by LLSurfacePatch's update routine)
+ void dirtyHeights();
+
+ LLViewerParcelOverlay *getParcelOverlay() const
+ { return mParcelOverlay; }
+
+ void setRegionFlags(U32 flags);
+ U32 getRegionFlags() const { return mRegionFlags; }
+
+ void setTimeDilation(F32 time_dilation);
+ F32 getTimeDilation() const { return mTimeDilation; }
+
+ // Origin height is at zero.
+ const LLVector3d &getOriginGlobal() const { return mOriginGlobal; }
+ LLVector3 getOriginAgent() const;
+
+ // Center is at the height of the water table.
+ const LLVector3d &getCenterGlobal() const { return mCenterGlobal; }
+ LLVector3 getCenterAgent() const;
+
+ void setRegionNameAndZone(const char* name_and_zone);
+ const LLString& getName() const { return mName; }
+ const LLString& getZoning() const { return mZoning; }
+
+ void setOwner(const LLUUID& owner_id) { mOwnerID = owner_id; }
+ const LLUUID& getOwner() const { return mOwnerID; }
+
+ // Is the current agent on the estate manager list for this region?
+ void setIsEstateManager(BOOL b) { mIsEstateManager = b; }
+ BOOL isEstateManager() const { return mIsEstateManager; }
+ BOOL canManageEstate() const;
+
+ void setSimAccess(U8 sim_access) { mSimAccess = sim_access; }
+ U8 getSimAccess() const { return mSimAccess; }
+ const char* getSimAccessString() const;
+
+ // Returns "Sandbox", "Expensive", etc.
+ static std::string regionFlagsToString(U32 flags);
+
+ // Returns "Mature", "PG", etc.
+ static const char* accessToString(U8 access);
+
+ static U8 stringToAccess(const char* access_str);
+
+ // Returns "M", "PG", etc.
+ static const char* accessToShortString(U8 access);
+
+ // helper function which just makes sure all interested parties
+ // can process the message.
+ static void processRegionInfo(LLMessageSystem* msg, void**);
+
+ void setCacheID(const LLUUID& id) { mCacheID = id; }
+
+ F32 getWidth() const { return mWidth; }
+
+ BOOL idleUpdate(LLTimer &timer, const F32 max_time);
+
+ // Like idleUpdate, but forces everything to complete regardless of
+ // how long it takes.
+ void forceUpdate();
+
+ void connectNeighbor(LLViewerRegion *neighborp, U32 direction);
+
+ void updateNetStats();
+
+ U32 getPacketsLost() const;
+
+ // Get/set named capability URLs for this region.
+ void setSeedCapability(const std::string& url);
+ void setCapability(const std::string& name, const std::string& url);
+ std::string getCapability(const std::string& name) const;
+
+ const LLHost &getHost() const { return mHost; }
+ const U64 &getHandle() const { return mHandle; }
+
+ LLSurface &getLand() const { return *mLandp; }
+
+ BOOL pointInRegionGlobal(const LLVector3d &point_global) const;
+ LLVector3 getPosRegionFromGlobal(const LLVector3d &point_global) const;
+ LLVector3 getPosRegionFromAgent(const LLVector3 &agent_pos) const;
+ LLVector3 getPosAgentFromRegion(const LLVector3 &region_pos) const;
+ LLVector3d getPosGlobalFromRegion(const LLVector3 &offset) const;
+
+ LLVLComposition *getComposition() const { return mCompositionp; }
+ F32 getCompositionXY(const S32 x, const S32 y) const;
+
+ BOOL isOwnedSelf(const LLVector3& pos);
+
+ // Owned by a group you belong to? (officer OR member)
+ BOOL isOwnedGroup(const LLVector3& pos);
+
+ // deal with map object updates in the world.
+ void updateCoarseLocations(LLMessageSystem* msg);
+
+ F32 getLandHeightRegion(const LLVector3& region_pos);
+
+ LLString getInfoString();
+
+ // handle a full update message
+ void cacheFullUpdate(LLViewerObject* objectp, LLDataPackerBinaryBuffer &dp);
+ LLDataPacker *getDP(U32 local_id, U32 crc);
+ void requestCacheMisses();
+ void addCacheMissFull(const U32 local_id);
+
+ void dumpCache();
+
+ void unpackRegionHandshake();
+
+ void calculateCenterGlobal();
+
+ friend std::ostream& operator<<(std::ostream &s, const LLViewerRegion &region);
+
+protected:
+ void disconnectAllNeighbors();
+ void initStats();
+
+public:
+ LLWind mWind;
+ LLCloudLayer mCloudLayer;
+ LLViewerParcelOverlay *mParcelOverlay;
+
+ BOOL mAlive; // can become false if circuit disconnects
+
+ LLStat mBitStat;
+ LLStat mPacketsStat;
+ LLStat mPacketsLostStat;
+
+ LLDynamicArray<U32> mMapAvatars;
+
+protected:
+ // The surfaces and other layers
+ LLSurface* mLandp;
+
+ // Region geometry data
+ LLVector3d mOriginGlobal; // Location of southwest corner of region (meters)
+ LLVector3d mCenterGlobal; // Location of center in world space (meters)
+ F32 mWidth; // Width of region on a side (meters)
+
+ U64 mHandle;
+ LLHost mHost;
+
+ F32 mTimeDilation; // time dilation of physics simulation on simulator
+
+ // simulator name
+ LLString mName;
+ LLString mZoning;
+
+ // region/estate owner - usually null.
+ LLUUID mOwnerID;
+
+ // Is this agent on the estate managers list for this region?
+ BOOL mIsEstateManager;
+
+ // Network statistics for the region's circuit...
+ LLTimer mLastNetUpdate;
+ U32 mPacketsIn;
+ U32 mBitsIn;
+ U32 mLastBitsIn;
+ U32 mLastPacketsIn;
+ U32 mPacketsOut;
+ U32 mLastPacketsOut;
+ S32 mPacketsLost;
+ S32 mLastPacketsLost;
+ U32 mPingDelay;
+ F32 mDeltaTime; // Time since last measurement of lastPackets, Bits, etc
+
+ LLVLComposition *mCompositionp; // Composition layer for the surface
+
+ U32 mRegionFlags; // includes damage flags
+ U8 mSimAccess;
+ F32 mBillableFactor;
+ U32 mMaxTasks; // max prim count
+
+ // Maps local ids to cache entries.
+ // Regions can have order 10,000 objects, so assume
+ // a structure of size 2^14 = 16,000
+ BOOL mCacheLoaded;
+ LLPtrSkipMap<U32, LLVOCacheEntry *, 14> mCacheMap;
+ LLVOCacheEntry mCacheStart;
+ LLVOCacheEntry mCacheEnd;
+ U32 mCacheEntriesCount;
+ LLDynamicArray<U32> mCacheMissFull;
+ LLDynamicArray<U32> mCacheMissCRC;
+ // time?
+ // LRU info?
+
+ // Cache ID is unique per-region, across renames, moving locations,
+ // etc.
+ LLUUID mCacheID;
+
+ typedef std::map<std::string, std::string> CapabilityMap;
+ CapabilityMap mCapabilities;
+
+ LLEventPoll* mEventPoll;
+};
+
+inline BOOL LLViewerRegion::getAllowDamage() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_DAMAGE) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowLandmark() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_LANDMARK) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowSetHome() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_SET_HOME) != 0);
+}
+
+inline BOOL LLViewerRegion::getResetHomeOnTeleport() const
+{
+ return ((mRegionFlags & REGION_FLAGS_RESET_HOME_ON_TELEPORT) !=0);
+}
+
+inline BOOL LLViewerRegion::getSunFixed() const
+{
+ return ((mRegionFlags & REGION_FLAGS_SUN_FIXED) !=0);
+}
+
+inline BOOL LLViewerRegion::getBlockFly() const
+{
+ return ((mRegionFlags & REGION_FLAGS_BLOCK_FLY) !=0);
+}
+
+inline BOOL LLViewerRegion::getAllowDirectTeleport() const
+{
+ return ((mRegionFlags & REGION_FLAGS_ALLOW_DIRECT_TELEPORT) !=0);
+}
+
+inline BOOL LLViewerRegion::isPrelude() const
+{
+ return is_prelude( mRegionFlags );
+}
+
+inline BOOL LLViewerRegion::getAllowTerraform() const
+{
+ return ((mRegionFlags & REGION_FLAGS_BLOCK_TERRAFORM) == 0);
+}
+
+inline BOOL LLViewerRegion::getRestrictPushObject() const
+{
+ return ((mRegionFlags & REGION_FLAGS_RESTRICT_PUSHOBJECT) != 0);
+}
+
+#endif
+
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
new file mode 100644
index 0000000000..bb490959c7
--- /dev/null
+++ b/indra/newview/llviewerstats.cpp
@@ -0,0 +1,301 @@
+/**
+ * @file llviewerstats.cpp
+ * @brief LLViewerStats class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewerstats.h"
+#include "llviewerthrottle.h"
+
+#include "message.h"
+#include "lltimer.h"
+
+LLViewerStats *gViewerStats = NULL;
+
+extern U32 gFrameCount;
+extern LLTimer gRenderStartTime;
+
+class StatAttributes
+{
+public:
+ StatAttributes(const char *name,
+ const BOOL enabled,
+ const BOOL is_timer)
+ : mName(name),
+ mEnabled(enabled),
+ mIsTimer(is_timer)
+ {
+ }
+
+ const char *mName;
+ const BOOL mEnabled;
+ const BOOL mIsTimer;
+};
+
+const StatAttributes STAT_INFO[LLViewerStats::ST_COUNT] =
+{
+ // ST_MOUSELOOK_SECONDS
+ StatAttributes("Seconds in Mouselook", FALSE, TRUE),
+ // ST_AVATAR_EDIT_SECONDS
+ StatAttributes("Seconds in Edit Appearence", FALSE, TRUE),
+ // ST_TOOLBOX_SECONDS
+ StatAttributes("Seconds using Toolbox", FALSE, TRUE),
+ // ST_CHAT_COUNT
+ StatAttributes("Chat messages sent", FALSE, FALSE),
+ // ST_IM_COUNT
+ StatAttributes("IMs sent", FALSE, FALSE),
+ // ST_FULLSCREEN_BOOL
+ StatAttributes("Fullscreen mode", TRUE, FALSE),
+ // ST_RELEASE_COUNT
+ StatAttributes("Object release count", FALSE, FALSE),
+ // ST_CREATE_COUNT
+ StatAttributes("Object create count", FALSE, FALSE),
+ // ST_REZ_COUNT
+ StatAttributes("Object rez count", FALSE, FALSE),
+ // ST_FPS_10_SECONDS
+ StatAttributes("Seconds below 10 FPS", FALSE, TRUE),
+ // ST_FPS_5_SECONDS
+ StatAttributes("Seconds below 5 FPS", FALSE, TRUE),
+ // ST_FPS_2_SECONDS
+ StatAttributes("Seconds below 2 FPS", FALSE, TRUE),
+ // ST_FLY_COUNT
+ StatAttributes("Fly count", FALSE, FALSE),
+ // ST_TELEPORT_COUNT
+ StatAttributes("Teleport count", FALSE, FALSE),
+ // ST_OBJECT_DELETE_COUNT
+ StatAttributes("Objects deleted", FALSE, FALSE),
+ // ST_SNAPSHOT_COUNT
+ StatAttributes("Snapshots taken", FALSE, FALSE),
+ // ST_UPLOAD_SOUND_COUNT
+ StatAttributes("Sounds uploaded", FALSE, FALSE),
+ // ST_UPLOAD_TEXTURE_COUNT
+ StatAttributes("Textures uploaded", FALSE, FALSE),
+ // ST_EDIT_TEXTURE_COUNT
+ StatAttributes("Changes to textures on objects", FALSE, FALSE),
+ // ST_KILLED_COUNT
+ StatAttributes("Number of times killed", FALSE, FALSE),
+ // ST_FRAMETIME_JITTER
+ StatAttributes("Average delta between sucessive frame times", FALSE, FALSE),
+ // ST_FRAMETIME_SLEW
+ StatAttributes("Average delta between frame time and mean", FALSE, FALSE),
+ // ST_INVENTORY_TOO_LONG
+ StatAttributes("Inventory took too long to load", FALSE, FALSE),
+ // ST_WEARABLES_TOO_LONG
+ StatAttributes("Wearables took too long to load", FALSE, FALSE),
+ // ST_LOGIN_SECONDS
+ StatAttributes("Time between LoginRequest and LoginReply", FALSE, FALSE),
+ // ST_LOGIN_TIMEOUT_COUNT
+ StatAttributes("Number of login attempts that timed out", FALSE, FALSE),
+ // ST_HAS_BAD_TIMER
+ StatAttributes("Known bad timer if != 0.0", FALSE, FALSE),
+ // ST_DOWNLOAD_FAILED
+ StatAttributes("Number of times LLAssetStorage::getAssetData() has failed", FALSE, FALSE),
+ // ST_LSL_SAVE_COUNT
+ StatAttributes("Number of times user has saved a script", FALSE, FALSE),
+ // ST_UPLOAD_ANIM_COUNT
+ StatAttributes("Animations uploaded", FALSE, FALSE),
+ // ST_FPS_8_SECONDS
+ StatAttributes("Seconds below 8 FPS", TRUE, TRUE),
+ // ST_SIM_FPS_20_SECONDS
+ StatAttributes("Seconds with sim FPS below 20", TRUE, TRUE),
+ // ST_PHYS_FPS_20_SECONDS
+ StatAttributes("Seconds with physics FPS below 20", TRUE, TRUE),
+ // ST_LOSS_05_SECONDS
+ StatAttributes("Seconds with packet loss > 5%", TRUE, TRUE),
+ // ST_FPS_DROP_50_RATIO
+ StatAttributes("Ratio of frames 2x longer than previous", TRUE, FALSE),
+ // ST_MEDIA_OBJECT_LIST_LENGTH
+ StatAttributes("Number of objects that want to display web pages", TRUE, FALSE),
+ // ST_DELTA_BANDWIDTH
+ StatAttributes("Increase/Decrease in bandwidth based on packet loss", TRUE, FALSE),
+ // ST_MAX_BANDWIDTH
+ StatAttributes("Max bandwidth setting", TRUE, FALSE),
+ // ST_LIGHTING_DETAIL
+ StatAttributes("Lighting Detail", TRUE, FALSE),
+ // ST_VISIBLE_AVATARS
+ StatAttributes("Visible Avatars", TRUE, FALSE),
+ // ST_SHADER_OJECTS
+ StatAttributes("Object Shaders", TRUE, FALSE),
+ // ST_SHADER_ENVIRONMENT
+ StatAttributes("Environment Shaders", TRUE, FALSE),
+ // ST_VISIBLE_DRAW_DIST
+ StatAttributes("Draw Distance", TRUE, FALSE),
+ // ST_VISIBLE_CHAT_BUBBLES
+ StatAttributes("Chat Bubbles Enabled", TRUE, FALSE),
+ // ST_SHADER_AVATAR
+ StatAttributes("Avatar Shaders", TRUE, FALSE),
+ // ST_FRAME_SECS
+ StatAttributes("FRAME_SECS", TRUE, FALSE),
+ // ST_UPDATE_SECS
+ StatAttributes("UPDATE_SECS", TRUE, FALSE),
+ // ST_NETWORK_SECS
+ StatAttributes("NETWORK_SECS", TRUE, FALSE),
+ // ST_IMAGE_SECS
+ StatAttributes("IMAGE_SECS", TRUE, FALSE),
+ // ST_REBUILD_SECS
+ StatAttributes("REBUILD_SECS", TRUE, FALSE),
+ // ST_RENDER_SECS
+ StatAttributes("RENDER_SECS", TRUE, FALSE),
+ // ST_CROSSING_AVG
+ StatAttributes("CROSSING_AVG", TRUE, FALSE),
+ // ST_CROSSING_MAX
+ StatAttributes("CROSSING_MAX", TRUE, FALSE),
+ // ST_LIBXUL_WIDGET_USED
+ StatAttributes("LibXUL Widget used", TRUE, FALSE),
+ // ST_WINDOW_WIDTH
+ StatAttributes("Window width", TRUE, FALSE),
+ // ST_WINDOW_HEIGHT
+ StatAttributes("Window height", TRUE, FALSE),
+ // ST_TEX_BAKES
+ StatAttributes("Texture Bakes", TRUE, FALSE),
+ // ST_TEX_REBAKES
+ StatAttributes("Texture Rebakes", TRUE, FALSE)
+};
+
+LLViewerStats::LLViewerStats()
+ : mPacketsLostPercentStat(64),
+ mLastTimeDiff(0.0)
+{
+ for (S32 i = 0; i < ST_COUNT; i++)
+ {
+ mStats[i] = 0.0;
+ }
+
+ if (LLTimer::knownBadTimer())
+ {
+ mStats[ST_HAS_BAD_TIMER] = 1.0;
+ }
+}
+
+LLViewerStats::~LLViewerStats()
+{
+}
+
+void LLViewerStats::resetStats()
+{
+ gViewerStats->mKBitStat.reset();
+ gViewerStats->mLayersKBitStat.reset();
+ gViewerStats->mObjectKBitStat.reset();
+ gViewerStats->mTextureKBitStat.reset();
+ gViewerStats->mVFSPendingOperations.reset();
+ gViewerStats->mAssetKBitStat.reset();
+ gViewerStats->mPacketsInStat.reset();
+ gViewerStats->mPacketsLostStat.reset();
+ gViewerStats->mPacketsOutStat.reset();
+ gViewerStats->mFPSStat.reset();
+ gViewerStats->mTexturePacketsStat.reset();
+}
+
+
+F64 LLViewerStats::getStat(EStatType type) const
+{
+ return mStats[type];
+}
+
+F64 LLViewerStats::setStat(EStatType type, F64 value)
+{
+ mStats[type] = value;
+ return mStats[type];
+}
+
+F64 LLViewerStats::incStat(EStatType type, F64 value)
+{
+ mStats[type] += value;
+ return mStats[type];
+}
+
+void LLViewerStats::updateFrameStats(const F64 time_diff)
+{
+ if (mPacketsLostPercentStat.getCurrent() > 5.0)
+ {
+ incStat(LLViewerStats::ST_LOSS_05_SECONDS, time_diff);
+ }
+
+ if (mSimFPS.getCurrent() < 20.f && mSimFPS.getCurrent() > 0.f)
+ {
+ incStat(LLViewerStats::ST_SIM_FPS_20_SECONDS, time_diff);
+ }
+
+ if (mSimPhysicsFPS.getCurrent() < 20.f && mSimPhysicsFPS.getCurrent() > 0.f)
+ {
+ incStat(LLViewerStats::ST_PHYS_FPS_20_SECONDS, time_diff);
+ }
+
+ if (time_diff >= 0.5)
+ {
+ incStat(LLViewerStats::ST_FPS_2_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.2)
+ {
+ incStat(LLViewerStats::ST_FPS_5_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.125)
+ {
+ incStat(LLViewerStats::ST_FPS_8_SECONDS, time_diff);
+ }
+ if (time_diff >= 0.1)
+ {
+ incStat(LLViewerStats::ST_FPS_10_SECONDS, time_diff);
+ }
+
+ if (gFrameCount && mLastTimeDiff > 0.0)
+ {
+ // new "stutter" meter
+ setStat(LLViewerStats::ST_FPS_DROP_50_RATIO,
+ (getStat(LLViewerStats::ST_FPS_DROP_50_RATIO) * (F64)(gFrameCount - 1) +
+ (time_diff >= 2.0 * mLastTimeDiff ? 1.0 : 0.0)) / gFrameCount);
+
+
+ // old stats that were never really used
+ setStat(LLViewerStats::ST_FRAMETIME_JITTER,
+ (getStat(LLViewerStats::ST_FRAMETIME_JITTER) * (gFrameCount - 1) +
+ fabs(mLastTimeDiff - time_diff) / mLastTimeDiff) / gFrameCount);
+
+ F32 average_frametime = gRenderStartTime.getElapsedTimeF32() / (F32)gFrameCount;
+ setStat(LLViewerStats::ST_FRAMETIME_SLEW,
+ (getStat(LLViewerStats::ST_FRAMETIME_SLEW) * (gFrameCount - 1) +
+ fabs(average_frametime - time_diff) / average_frametime) / gFrameCount);
+
+ F32 max_bandwidth = gViewerThrottle.getMaxBandwidth();
+ F32 delta_bandwidth = gViewerThrottle.getCurrentBandwidth() - max_bandwidth;
+ setStat(LLViewerStats::ST_DELTA_BANDWIDTH, delta_bandwidth / 1024.f);
+
+ setStat(LLViewerStats::ST_MAX_BANDWIDTH, max_bandwidth / 1024.f);
+
+ }
+
+ mLastTimeDiff = time_diff;
+
+}
+
+void LLViewerStats::addToMessage() const
+{
+ for (S32 i = 0; i < ST_COUNT; i++)
+ {
+ if (STAT_INFO[i].mEnabled)
+ {
+ // TODO: send timer value so dataserver can normalize
+ gMessageSystem->nextBlockFast(_PREHASH_MiscStats);
+ gMessageSystem->addU32Fast(_PREHASH_Type, (U32)i);
+ gMessageSystem->addF64Fast(_PREHASH_Value, mStats[i]);
+ llinfos << "STAT: " << STAT_INFO[i].mName << ": " << mStats[i] << llendl;
+ }
+ }
+}
+
+// static
+const char *LLViewerStats::statTypeToText(EStatType type)
+{
+ if (type >= 0 && type < ST_COUNT)
+ {
+ return STAT_INFO[type].mName;
+ }
+ else
+ {
+ return "Unknown statistic";
+ }
+}
diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h
new file mode 100644
index 0000000000..b58012d01c
--- /dev/null
+++ b/indra/newview/llviewerstats.h
@@ -0,0 +1,167 @@
+/**
+ * @file llviewerstats.h
+ * @brief LLViewerStats class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERSTATS_H
+#define LL_LLVIEWERSTATS_H
+
+#include "llstat.h"
+
+class LLViewerStats
+{
+public:
+ LLStat mKBitStat;
+ LLStat mLayersKBitStat;
+ LLStat mObjectKBitStat;
+ LLStat mAssetKBitStat;
+ LLStat mTextureKBitStat;
+ LLStat mVFSPendingOperations;
+ LLStat mObjectsDrawnStat;
+ LLStat mObjectsCulledStat;
+ LLStat mObjectsTestedStat;
+ LLStat mObjectsComparedStat;
+ LLStat mObjectsOccludedStat;
+ LLStat mFPSStat;
+ LLStat mPacketsInStat;
+ LLStat mPacketsLostStat;
+ LLStat mPacketsOutStat;
+ LLStat mPacketsLostPercentStat;
+ LLStat mTexturePacketsStat;
+ LLStat mActualInKBitStat; // From the packet ring (when faking a bad connection)
+ LLStat mActualOutKBitStat; // From the packet ring (when faking a bad connection)
+
+ // Simulator stats
+ LLStat mSimTimeDilation;
+
+ LLStat mSimFPS;
+ LLStat mSimPhysicsFPS;
+ LLStat mSimAgentUPS;
+ LLStat mSimLSLIPS;
+
+ LLStat mSimFrameMsec;
+ LLStat mSimNetMsec;
+ LLStat mSimSimOtherMsec;
+ LLStat mSimSimPhysicsMsec;
+ LLStat mSimAgentMsec;
+ LLStat mSimImagesMsec;
+ LLStat mSimScriptMsec;
+
+ LLStat mSimMainAgents;
+ LLStat mSimChildAgents;
+ LLStat mSimObjects;
+ LLStat mSimActiveObjects;
+ LLStat mSimActiveScripts;
+
+ LLStat mSimInPPS;
+ LLStat mSimOutPPS;
+ LLStat mSimPendingDownloads;
+ LLStat mSimPendingUploads;
+ LLStat mSimPendingLocalUploads;
+ LLStat mSimTotalUnackedBytes;
+
+ /*
+ LLStat mSimCPUUsageStat;
+ LLStat mSimMemTotalStat;
+ LLStat mSimMemRSSStat;
+ */
+
+
+ LLStat mSimPingStat;
+ LLStat mUserserverPingStat;
+
+ void resetStats();
+public:
+ // If you change this, please also add a corresponding text label
+ // in statTypeToText in llviewerstats.cpp
+ enum EStatType
+ {
+ ST_MOUSELOOK_SECONDS = 0,
+ ST_AVATAR_EDIT_SECONDS = 1,
+ ST_TOOLBOX_SECONDS = 2,
+ ST_CHAT_COUNT = 3,
+ ST_IM_COUNT = 4,
+ ST_FULLSCREEN_BOOL = 5,
+ ST_RELEASE_COUNT= 6,
+ ST_CREATE_COUNT = 7,
+ ST_REZ_COUNT = 8,
+ ST_FPS_10_SECONDS = 9,
+ ST_FPS_5_SECONDS = 10,
+ ST_FPS_2_SECONDS = 11,
+ ST_FLY_COUNT = 12,
+ ST_TELEPORT_COUNT = 13,
+ ST_OBJECT_DELETE_COUNT = 14,
+ ST_SNAPSHOT_COUNT = 15,
+ ST_UPLOAD_SOUND_COUNT = 16,
+ ST_UPLOAD_TEXTURE_COUNT = 17,
+ ST_EDIT_TEXTURE_COUNT = 18,
+ ST_KILLED_COUNT = 19,
+ ST_FRAMETIME_JITTER = 20,
+ ST_FRAMETIME_SLEW = 21,
+ ST_INVENTORY_TOO_LONG = 22,
+ ST_WEARABLES_TOO_LONG = 23,
+ ST_LOGIN_SECONDS = 24,
+ ST_LOGIN_TIMEOUT_COUNT = 25,
+ ST_HAS_BAD_TIMER = 26,
+ ST_DOWNLOAD_FAILED = 27,
+ ST_LSL_SAVE_COUNT = 28,
+ ST_UPLOAD_ANIM_COUNT = 29,
+ ST_FPS_8_SECONDS = 30,
+ ST_SIM_FPS_20_SECONDS = 31,
+ ST_PHYS_FPS_20_SECONDS = 32,
+ ST_LOSS_05_SECONDS = 33,
+ ST_FPS_DROP_50_RATIO = 34,
+ ST_MEDIA_OBJECT_LIST_LENGTH = 35,
+ ST_DELTA_BANDWIDTH = 36,
+ ST_MAX_BANDWIDTH = 37,
+ ST_LIGHTING_DETAIL = 38,
+ ST_VISIBLE_AVATARS = 39,
+ ST_SHADER_OBJECTS = 40,
+ ST_SHADER_ENVIRONMENT = 41,
+ ST_DRAW_DIST = 42,
+ ST_CHAT_BUBBLES = 43,
+ ST_SHADER_AVATAR = 44,
+ ST_FRAME_SECS = 45,
+ ST_UPDATE_SECS = 46,
+ ST_NETWORK_SECS = 47,
+ ST_IMAGE_SECS = 48,
+ ST_REBUILD_SECS = 49,
+ ST_RENDER_SECS = 50,
+ ST_CROSSING_AVG = 51,
+ ST_CROSSING_MAX = 52,
+ ST_LIBXUL_WIDGET_USED = 53,
+ ST_WINDOW_WIDTH = 54,
+ ST_WINDOW_HEIGHT = 55,
+ ST_TEX_BAKES = 56,
+ ST_TEX_REBAKES = 57,
+
+ ST_COUNT = 58
+ };
+
+
+ LLViewerStats();
+ ~LLViewerStats();
+
+ // all return latest value of given stat
+ F64 getStat(EStatType type) const;
+ F64 setStat(EStatType type, F64 value); // set the stat to value
+ F64 incStat(EStatType type, F64 value = 1.f); // add value to the stat
+
+ void updateFrameStats(const F64 time_diff);
+
+ void addToMessage() const;
+
+ static const char *statTypeToText(EStatType type);
+
+private:
+ F64 mStats[ST_COUNT];
+
+ F64 mLastTimeDiff; // used for time stat updates
+};
+
+extern LLViewerStats *gViewerStats;
+
+#endif // LL_LLVIEWERSTATS_H
diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp
new file mode 100644
index 0000000000..ddbf577f0a
--- /dev/null
+++ b/indra/newview/llviewertexteditor.cpp
@@ -0,0 +1,1475 @@
+/**
+ * @file llviewertexteditor.cpp
+ * @brief Text editor widget to let users enter a a multi-line ASCII document.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfocusmgr.h"
+#include "audioengine.h"
+#include "llagent.h"
+#include "llinventory.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+
+#include "llviewertexteditor.h"
+
+#include "llfloaterchat.h"
+#include "llfloaterworldmap.h"
+#include "llnotify.h"
+#include "llpreview.h"
+#include "llpreviewtexture.h"
+#include "llpreviewnotecard.h"
+#include "llpreviewlandmark.h"
+#include "llscrollbar.h"
+#include "lltooldraganddrop.h"
+#include "llviewercontrol.h"
+#include "llviewerimagelist.h"
+#include "llviewerwindow.h"
+#include "llviewerinventory.h"
+#include "llnotecard.h"
+#include "llmemorystream.h"
+
+extern BOOL gPacificDaylightTime;
+
+////////////////////////////////////////////////////////////
+// LLEmbeddedItems
+//
+// Embedded items are stored as:
+// * A global map of llwchar to LLInventoryItem
+// ** This is unique for each item embedded in any notecard
+// to support copy/paste across notecards
+// * A per-notecard set of embeded llwchars for easy removal
+// from the global list
+// * A per-notecard vector of embedded lwchars for mapping from
+// old style 0x80 + item format notechards
+
+class LLEmbeddedItems
+{
+public:
+ LLEmbeddedItems(const LLViewerTextEditor* editor);
+ ~LLEmbeddedItems();
+ void clear();
+
+ void bindEmbeddedChars(const LLFontGL* font);
+ void unbindEmbeddedChars(const LLFontGL* font);
+
+ BOOL insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new);
+ BOOL removeEmbeddedItem( llwchar ext_char );
+
+ BOOL hasEmbeddedItem(llwchar ext_char); // returns TRUE if /this/ editor has an entry for this item
+
+ void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items );
+ void addItems(const std::vector<LLPointer<LLInventoryItem> >& items);
+
+ llwchar getEmbeddedCharFromIndex(S32 index);
+
+ void removeUnusedChars();
+ void copyUsedCharsToIndexed();
+ S32 getIndexFromEmbeddedChar(llwchar wch);
+
+ void markSaved();
+
+ static LLInventoryItem* getEmbeddedItem(llwchar ext_char); // returns item from static list
+ static BOOL getEmbeddedItemSaved(llwchar ext_char); // returns whether item from static list is saved
+
+ struct embedded_info_t
+ {
+ LLPointer<LLInventoryItem> mItem;
+ BOOL mSaved;
+ };
+private:
+ typedef std::map<llwchar, embedded_info_t > item_map_t;
+ static item_map_t sEntries;
+ static std::stack<llwchar> sFreeEntries;
+
+ std::set<llwchar> mEmbeddedUsedChars; // list of used llwchars
+ std::vector<llwchar> mEmbeddedIndexedChars; // index -> wchar for 0x80 + index format
+ const LLViewerTextEditor* mEditor;
+};
+
+//statics
+LLEmbeddedItems::item_map_t LLEmbeddedItems::sEntries;
+std::stack<llwchar> LLEmbeddedItems::sFreeEntries;
+
+LLEmbeddedItems::LLEmbeddedItems(const LLViewerTextEditor* editor)
+ : mEditor(editor)
+{
+}
+
+LLEmbeddedItems::~LLEmbeddedItems()
+{
+ clear();
+}
+
+void LLEmbeddedItems::clear()
+{
+ // Remove entries for this editor from static list
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
+ iter != mEmbeddedUsedChars.end();)
+ {
+ std::set<llwchar>::iterator nextiter = iter++;
+ removeEmbeddedItem(*nextiter);
+ }
+ mEmbeddedUsedChars.clear();
+}
+
+// Inserts a new unique entry
+BOOL LLEmbeddedItems::insertEmbeddedItem( LLInventoryItem* item, llwchar* ext_char, bool is_new)
+{
+ // Now insert a new one
+ llwchar wc_emb;
+ if (!sFreeEntries.empty())
+ {
+ wc_emb = sFreeEntries.top();
+ sFreeEntries.pop();
+ }
+ else if (sEntries.empty())
+ {
+ wc_emb = FIRST_EMBEDDED_CHAR;
+ }
+ else
+ {
+ item_map_t::iterator last = sEntries.end();
+ --last;
+ wc_emb = last->first;
+ if (wc_emb >= LAST_EMBEDDED_CHAR)
+ {
+ return FALSE;
+ }
+ ++wc_emb;
+ }
+
+ sEntries[wc_emb].mItem = item;
+ sEntries[wc_emb].mSaved = is_new ? FALSE : TRUE;
+ *ext_char = wc_emb;
+ mEmbeddedUsedChars.insert(wc_emb);
+ return TRUE;
+}
+
+// Removes an entry (all entries are unique)
+BOOL LLEmbeddedItems::removeEmbeddedItem( llwchar ext_char )
+{
+ mEmbeddedUsedChars.erase(ext_char);
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ sEntries.erase(ext_char);
+ sFreeEntries.push(ext_char);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+// static
+LLInventoryItem* LLEmbeddedItems::getEmbeddedItem(llwchar ext_char)
+{
+ if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
+ {
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ return iter->second.mItem;
+ }
+ }
+ return NULL;
+}
+
+// static
+BOOL LLEmbeddedItems::getEmbeddedItemSaved(llwchar ext_char)
+{
+ if( ext_char >= FIRST_EMBEDDED_CHAR && ext_char <= LAST_EMBEDDED_CHAR )
+ {
+ item_map_t::iterator iter = sEntries.find(ext_char);
+ if (iter != sEntries.end())
+ {
+ return iter->second.mSaved;
+ }
+ }
+ return FALSE;
+}
+
+llwchar LLEmbeddedItems::getEmbeddedCharFromIndex(S32 index)
+{
+ if (index >= (S32)mEmbeddedIndexedChars.size())
+ {
+ llwarns << "No item for embedded char " << index << " using LL_UNKNOWN_CHAR" << llendl;
+ return LL_UNKNOWN_CHAR;
+ }
+ return mEmbeddedIndexedChars[index];
+}
+
+void LLEmbeddedItems::removeUnusedChars()
+{
+ std::set<llwchar> used = mEmbeddedUsedChars;
+ const LLWString& wtext = mEditor->getWText();
+ for (S32 i=0; i<(S32)wtext.size(); i++)
+ {
+ llwchar wc = wtext[i];
+ if( wc >= FIRST_EMBEDDED_CHAR && wc <= LAST_EMBEDDED_CHAR )
+ {
+ used.erase(wc);
+ }
+ }
+ // Remove chars not actually used
+ for (std::set<llwchar>::iterator iter = used.begin();
+ iter != used.end(); ++iter)
+ {
+ removeEmbeddedItem(*iter);
+ }
+}
+
+void LLEmbeddedItems::copyUsedCharsToIndexed()
+{
+ // Prune unused items
+ removeUnusedChars();
+
+ // Copy all used llwchars to mEmbeddedIndexedChars
+ mEmbeddedIndexedChars.clear();
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin();
+ iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ mEmbeddedIndexedChars.push_back(*iter);
+ }
+}
+
+S32 LLEmbeddedItems::getIndexFromEmbeddedChar(llwchar wch)
+{
+ S32 idx = 0;
+ for (std::vector<llwchar>::iterator iter = mEmbeddedIndexedChars.begin();
+ iter != mEmbeddedIndexedChars.end(); ++iter)
+ {
+ if (wch == *iter)
+ break;
+ ++idx;
+ }
+ if (idx < (S32)mEmbeddedIndexedChars.size())
+ {
+ return idx;
+ }
+ else
+ {
+ llwarns << "Embedded char " << wch << " not found, using 0" << llendl;
+ return 0;
+ }
+}
+
+BOOL LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char)
+{
+ std::set<llwchar>::iterator iter = mEmbeddedUsedChars.find(ext_char);
+ if (iter != mEmbeddedUsedChars.end())
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLEmbeddedItems::bindEmbeddedChars( const LLFontGL* font )
+{
+ if( sEntries.empty() )
+ {
+ return;
+ }
+
+ for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
+ {
+ llwchar wch = *iter1;
+ item_map_t::iterator iter2 = sEntries.find(wch);
+ if (iter2 == sEntries.end())
+ {
+ continue;
+ }
+ LLInventoryItem* item = iter2->second.mItem;
+ if (!item)
+ {
+ continue;
+ }
+ const char* img_name;
+ switch( item->getType() )
+ {
+ case LLAssetType::AT_TEXTURE:
+ if(item->getInventoryType() == LLInventoryType::IT_SNAPSHOT)
+ {
+ img_name = "inv_item_snapshot.tga";
+ }
+ else
+ {
+ img_name = "inv_item_texture.tga";
+ }
+
+ break;
+ case LLAssetType::AT_SOUND: img_name = "inv_item_sound.tga"; break;
+ case LLAssetType::AT_LANDMARK:
+ if (item->getFlags() & LLInventoryItem::II_FLAGS_LANDMARK_VISITED)
+ {
+ img_name = "inv_item_landmark_visited.tga";
+ }
+ else
+ {
+ img_name = "inv_item_landmark.tga";
+ }
+ break;
+ case LLAssetType::AT_CLOTHING: img_name = "inv_item_clothing.tga"; break;
+ case LLAssetType::AT_OBJECT: img_name = "inv_item_object.tga"; break;
+ case LLAssetType::AT_NOTECARD: img_name = "inv_item_notecard.tga"; break;
+ case LLAssetType::AT_LSL_TEXT: img_name = "inv_item_script.tga"; break;
+ case LLAssetType::AT_BODYPART: img_name = "inv_item_bodypart.tga"; break;
+ case LLAssetType::AT_ANIMATION: img_name = "inv_item_animation.tga";break;
+ case LLAssetType::AT_GESTURE: img_name = "inv_item_gesture.tga"; break;
+ default: llassert(0); continue;
+ }
+
+ LLViewerImage* image = gImageList.getImage(LLUUID(gViewerArt.getString(img_name)), MIPMAP_FALSE, TRUE);
+
+ ((LLFontGL*)font)->addEmbeddedChar( wch, image, item->getName() );
+ }
+}
+
+void LLEmbeddedItems::unbindEmbeddedChars( const LLFontGL* font )
+{
+ if( sEntries.empty() )
+ {
+ return;
+ }
+
+ for (std::set<llwchar>::iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1)
+ {
+ ((LLFontGL*)font)->removeEmbeddedChar(*iter1);
+ }
+}
+
+void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items)
+{
+ for (std::vector<LLPointer<LLInventoryItem> >::const_iterator iter = items.begin();
+ iter != items.end(); ++iter)
+ {
+ LLInventoryItem* item = *iter;
+ if (item)
+ {
+ llwchar wc;
+ if (!insertEmbeddedItem( item, &wc, false ))
+ {
+ break;
+ }
+ mEmbeddedIndexedChars.push_back(wc);
+ }
+ }
+}
+
+void LLEmbeddedItems::getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items )
+{
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ llwchar wc = *iter;
+ LLPointer<LLInventoryItem> item = getEmbeddedItem(wc);
+ if (item)
+ {
+ items.push_back(item);
+ }
+ }
+}
+
+void LLEmbeddedItems::markSaved()
+{
+ for (std::set<llwchar>::iterator iter = mEmbeddedUsedChars.begin(); iter != mEmbeddedUsedChars.end(); ++iter)
+ {
+ llwchar wc = *iter;
+ sEntries[wc].mSaved = TRUE;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+
+class LLTextCmdInsertEmbeddedItem : public LLTextCmd
+{
+public:
+ LLTextCmdInsertEmbeddedItem::LLTextCmdInsertEmbeddedItem( S32 pos, LLInventoryItem* item )
+ : LLTextCmd(pos, FALSE),
+ mExtCharValue(0)
+ {
+ mItem = item;
+ }
+
+ virtual BOOL execute( LLTextEditor* editor, S32* delta )
+ {
+ LLViewerTextEditor* viewer_editor = (LLViewerTextEditor*)editor;
+ // Take this opportunity to remove any unused embedded items from this editor
+ viewer_editor->mEmbeddedItemList->removeUnusedChars();
+ if(viewer_editor->mEmbeddedItemList->insertEmbeddedItem( mItem, &mExtCharValue, true ) )
+ {
+ LLWString ws;
+ ws.assign(1, mExtCharValue);
+ *delta = insert(editor, mPos, ws );
+ return (*delta != 0);
+ }
+ return FALSE;
+ }
+
+ virtual S32 undo( LLTextEditor* editor )
+ {
+ remove(editor, mPos, 1);
+ return mPos;
+ }
+
+ virtual S32 redo( LLTextEditor* editor )
+ {
+ LLWString ws;
+ ws += mExtCharValue;
+ insert(editor, mPos, ws );
+ return mPos + 1;
+ }
+ virtual BOOL hasExtCharValue( llwchar value )
+ {
+ return (value == mExtCharValue);
+ }
+
+private:
+ LLPointer<LLInventoryItem> mItem;
+ llwchar mExtCharValue;
+};
+
+struct LLNotecardCopyInfo
+{
+ LLNotecardCopyInfo(LLViewerTextEditor *ed, LLInventoryItem *item)
+ : mTextEd(ed)
+ {
+ mItem = item;
+ }
+
+ LLViewerTextEditor* mTextEd;
+ // need to make this be a copy (not a * here) because it isn't stable.
+ // I wish we had passed LLPointers all the way down, but we didn't
+ LLPointer<LLInventoryItem> mItem;
+};
+
+//----------------------------------------------------------------------------
+
+//
+// Member functions
+//
+
+LLViewerTextEditor::LLViewerTextEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString& default_text,
+ const LLFontGL* font,
+ BOOL allow_embedded_items)
+ : LLTextEditor(name, rect, max_length, default_text, font, allow_embedded_items),
+ mDragItemSaved(FALSE)
+{
+ mEmbeddedItemList = new LLEmbeddedItems(this);
+}
+
+LLViewerTextEditor::~LLViewerTextEditor()
+{
+ delete mEmbeddedItemList;
+}
+
+///////////////////////////////////////////////////////////////////
+// virtual
+void LLViewerTextEditor::makePristine()
+{
+ mEmbeddedItemList->markSaved();
+ LLTextEditor::makePristine();
+}
+
+///////////////////////////////////////////////////////////////////
+
+BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
+{
+ if (pointInView(x, y) && getVisible())
+ {
+ for (child_list_const_iter_t child_iter = getChildList()->begin();
+ child_iter != getChildList()->end(); ++child_iter)
+ {
+ LLView *viewp = *child_iter;
+ S32 local_x = x - viewp->getRect().mLeft;
+ S32 local_y = y - viewp->getRect().mBottom;
+ if( viewp->handleToolTip(local_x, local_y, msg, sticky_rect_screen ) )
+ {
+ return TRUE;
+ }
+ }
+
+ if( mSegments.empty() )
+ {
+ return TRUE;
+ }
+
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ BOOL has_tool_tip = FALSE;
+ if( cur_segment->getStyle().getIsEmbeddedItem() )
+ {
+ LLWString wtip;
+ has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip);
+ msg = wstring_to_utf8str(wtip);
+ }
+ else
+ {
+ has_tool_tip = cur_segment->getToolTip( msg );
+ }
+ if( has_tool_tip )
+ {
+ // Just use a slop area around the cursor
+ // Convert rect local to screen coordinates
+ S32 SLOP = 8;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // Let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL;
+
+ // enable I Agree checkbox if the user scrolled through entire text
+ BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
+ if (mOnScrollEndCallback && was_scrolled_to_bottom)
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if (!(mask & MASK_SHIFT))
+ {
+ deselect();
+ }
+
+ BOOL start_select = TRUE;
+ if( mAllowEmbeddedItems )
+ {
+ setCursorAtLocalPos( x, y, FALSE );
+ llwchar wc = 0;
+ if (mCursorPos < getLength())
+ {
+ wc = mWText[mCursorPos];
+ }
+ LLInventoryItem* item_at_pos = LLEmbeddedItems::getEmbeddedItem(wc);
+ if (item_at_pos)
+ {
+ mDragItem = item_at_pos;
+ mDragItemSaved = LLEmbeddedItems::getEmbeddedItemSaved(wc);
+ gFocusMgr.setMouseCapture( this, NULL );
+ mMouseDownX = x;
+ mMouseDownY = y;
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ gToolDragAndDrop->setDragStart( screen_x, screen_y );
+
+ start_select = FALSE;
+ }
+ else
+ {
+ mDragItem = NULL;
+ }
+ }
+
+ if( start_select )
+ {
+ // If we're not scrolling (handled by child), then we're selecting
+ if (mask & MASK_SHIFT)
+ {
+ S32 old_cursor_pos = mCursorPos;
+ setCursorAtLocalPos( x, y, TRUE );
+
+ if (hasSelection())
+ {
+ /* Mac-like behavior - extend selection towards the cursor
+ if (mCursorPos < mSelectionStart
+ && mCursorPos < mSelectionEnd)
+ {
+ // ...left of selection
+ mSelectionStart = llmax(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else if (mCursorPos > mSelectionStart
+ && mCursorPos > mSelectionEnd)
+ {
+ // ...right of selection
+ mSelectionStart = llmin(mSelectionStart, mSelectionEnd);
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionEnd = mCursorPos;
+ }
+ */
+ // Windows behavior
+ mSelectionEnd = mCursorPos;
+ }
+ else
+ {
+ mSelectionStart = old_cursor_pos;
+ mSelectionEnd = mCursorPos;
+ }
+ // assume we're starting a drag select
+ mIsSelecting = TRUE;
+ }
+ else
+ {
+ setCursorAtLocalPos( x, y, TRUE );
+ startSelection();
+ }
+ gFocusMgr.setMouseCapture( this, &LLTextEditor::onMouseCaptureLost );
+ //FIXME:
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ handled = TRUE;
+ }
+
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ if (!mDragItem)
+ {
+ // leave hover segment active during drag and drop
+ mHoverSegment = NULL;
+ }
+ if( getVisible() )
+ {
+ if(gFocusMgr.getMouseCapture() == this )
+ {
+ if( mIsSelecting )
+ {
+ if (x != mLastSelectionX || y != mLastSelectionY)
+ {
+ mLastSelectionX = x;
+ mLastSelectionY = y;
+ //FIXME:
+ //gViewerWindow->requestFastFrame(this);
+ }
+
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ mSelectionEnd = mCursorPos;
+
+ updateScrollFromCursor();
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ }
+ else if( mDragItem )
+ {
+ S32 screen_x;
+ S32 screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y );
+ if( gToolDragAndDrop->isOverThreshold( screen_x, screen_y ) )
+ {
+ gToolDragAndDrop->beginDrag(
+ LLAssetType::lookupDragAndDropType( mDragItem->getType() ),
+ mDragItem->getUUID(),
+ LLToolDragAndDrop::SOURCE_NOTECARD,
+ mSourceID, mObjectID);
+
+ return gToolDragAndDrop->handleHover( x, y, mask );
+ }
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ }
+
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl;
+ handled = TRUE;
+ }
+
+ if( !handled )
+ {
+ // Pass to children
+ handled = LLView::childrenHandleHover(x, y, mask) != NULL;
+ }
+
+ if( handled )
+ {
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+ }
+
+ // Opaque
+ if( !handled && mTakesNonScrollClicks)
+ {
+ // Check to see if we're over an HTML-style link
+ if( !mSegments.empty() )
+ {
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment )
+ {
+ if(cur_segment->getStyle().isLink())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ handled = TRUE;
+ }
+ else
+ if(cur_segment->getStyle().getIsEmbeddedItem())
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl;
+ getWindow()->setCursor(UI_CURSOR_HAND);
+ //getWindow()->setCursor(UI_CURSOR_ARROW);
+ handled = TRUE;
+ }
+ mHoverSegment = cur_segment;
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl;
+ if (!mScrollbar->getVisible() || x < mRect.getWidth() - SCROLLBAR_SIZE)
+ {
+ getWindow()->setCursor(UI_CURSOR_IBEAM);
+ }
+ else
+ {
+ getWindow()->setCursor(UI_CURSOR_ARROW);
+ }
+ handled = TRUE;
+ }
+ }
+ }
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL;
+
+ // enable I Agree checkbox if the user scrolled through entire text
+ BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax());
+ if (mOnScrollEndCallback && was_scrolled_to_bottom)
+ {
+ mOnScrollEndCallback(mOnScrollEndData);
+ }
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if( mIsSelecting )
+ {
+ // Finish selection
+ if( y > mTextRect.mTop )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 );
+ }
+ else
+ if( y < mTextRect.mBottom )
+ {
+ mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 );
+ }
+
+ setCursorAtLocalPos( x, y, TRUE );
+ endSelection();
+
+ updateScrollFromCursor();
+ }
+
+ if( !hasSelection() )
+ {
+ handleMouseUpOverSegment( x, y, mask );
+ }
+
+ handled = TRUE;
+ }
+
+ // Delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ if( gFocusMgr.getMouseCapture() == this )
+ {
+ if (mDragItem)
+ {
+ // mouse down was on an item
+ S32 dx = x - mMouseDownX;
+ S32 dy = y - mMouseDownY;
+ if (-2 < dx && dx < 2 && -2 < dy && dy < 2)
+ {
+ openEmbeddedItem(mDragItem, mDragItemSaved);
+ }
+ }
+ mDragItem = NULL;
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ handled = TRUE;
+ }
+
+ return handled;
+}
+
+
+BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask)
+{
+ BOOL handled = FALSE;
+
+ // let scrollbar have first dibs
+ handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL;
+
+ if( !handled && mTakesNonScrollClicks)
+ {
+ if( mAllowEmbeddedItems )
+ {
+ LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y );
+ if( cur_segment && cur_segment->getStyle().getIsEmbeddedItem() )
+ {
+ if( openEmbeddedItemAtPos( cur_segment->getStart() ) )
+ {
+ deselect();
+ setFocus( FALSE );
+ return TRUE;
+ }
+ }
+ }
+
+ if (mTakesFocus)
+ {
+ setFocus( TRUE );
+ }
+
+ setCursorAtLocalPos( x, y, FALSE );
+ deselect();
+
+ const LLWString &text = getWText();
+
+ if( isPartOfWord( text[mCursorPos] ) )
+ {
+ // Select word the cursor is over
+ while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1]))
+ {
+ mCursorPos--;
+ }
+ startSelection();
+
+ while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) )
+ {
+ mCursorPos++;
+ }
+
+ mSelectionEnd = mCursorPos;
+ }
+ else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) )
+ {
+ // Select the character the cursor is over
+ startSelection();
+ mCursorPos++;
+ mSelectionEnd = mCursorPos;
+ }
+
+ // We don't want handleMouseUp() to "finish" the selection (and thereby
+ // set mSelectionEnd to where the mouse is), so we finish the selection here.
+ mIsSelecting = FALSE;
+
+ // delay cursor flashing
+ mKeystrokeTimer.reset();
+
+ handled = TRUE;
+ }
+ return handled;
+}
+
+
+// Allow calling cards to be dropped onto text fields. Append the name and
+// a carriage return.
+// virtual
+BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type, void *cargo_data,
+ EAcceptance *accept,
+ LLString& tooltip_msg)
+{
+ BOOL handled = FALSE;
+
+ if (mTakesNonScrollClicks)
+ {
+ if (getEnabled() && !mReadOnly)
+ {
+ switch( cargo_type )
+ {
+ case DAD_CALLINGCARD:
+ if(mAcceptCallingCardNames)
+ {
+ if (drop)
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+ LLString name = item->getName();
+ appendText(name, true, true);
+ }
+ *accept = ACCEPT_YES_COPY_SINGLE;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ break;
+
+ case DAD_TEXTURE:
+ case DAD_SOUND:
+ case DAD_LANDMARK:
+ case DAD_SCRIPT:
+ case DAD_CLOTHING:
+ case DAD_OBJECT:
+ case DAD_NOTECARD:
+ case DAD_BODYPART:
+ case DAD_ANIMATION:
+ case DAD_GESTURE:
+ {
+ LLInventoryItem *item = (LLInventoryItem *)cargo_data;
+ if( mAllowEmbeddedItems )
+ {
+ U32 mask_next = item->getPermissions().getMaskNextOwner();
+ if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED)
+ {
+ if( drop )
+ {
+ deselect();
+ S32 old_cursor = mCursorPos;
+ setCursorAtLocalPos( x, y, TRUE );
+ S32 insert_pos = mCursorPos;
+ setCursorPos(old_cursor);
+ BOOL inserted = insertEmbeddedItem( insert_pos, item );
+ if( inserted && (old_cursor > mCursorPos) )
+ {
+ setCursorPos(mCursorPos + 1);
+ }
+
+ updateLineStartList();
+ }
+ *accept = ACCEPT_YES_COPY_MULTI;
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ if (tooltip_msg.empty())
+ {
+ tooltip_msg.assign("Only items with unrestricted\n"
+ "'next owner' permissions \n"
+ "can be attached to notecards.");
+ }
+ }
+ }
+ else
+ {
+ *accept = ACCEPT_NO;
+ }
+ break;
+ }
+
+ default:
+ *accept = ACCEPT_NO;
+ break;
+ }
+ }
+ else
+ {
+ // Not enabled
+ *accept = ACCEPT_NO;
+ }
+
+ handled = TRUE;
+ lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl;
+ }
+
+ return handled;
+}
+
+void LLViewerTextEditor::setASCIIEmbeddedText(const LLString& instr)
+{
+ LLWString wtext;
+ const U8* buffer = (U8*)(instr.c_str());
+ while (*buffer)
+ {
+ llwchar wch;
+ U8 c = *buffer++;
+ if (c >= 0x80)
+ {
+ S32 index = (S32)(c - 0x80);
+ wch = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
+ }
+ else
+ {
+ wch = (llwchar)c;
+ }
+ wtext.push_back(wch);
+ }
+ setWText(wtext);
+}
+
+void LLViewerTextEditor::setEmbeddedText(const LLString& instr)
+{
+ LLWString wtext = utf8str_to_wstring(instr);
+ for (S32 i=0; i<(S32)wtext.size(); i++)
+ {
+ llwchar wch = wtext[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = wch - FIRST_EMBEDDED_CHAR;
+ wtext[i] = mEmbeddedItemList->getEmbeddedCharFromIndex(index);
+ }
+ }
+ setWText(wtext);
+}
+
+LLString LLViewerTextEditor::getEmbeddedText()
+{
+#if 1
+ // New version (Version 2)
+ mEmbeddedItemList->copyUsedCharsToIndexed();
+ LLWString outtextw;
+ for (S32 i=0; i<(S32)mWText.size(); i++)
+ {
+ llwchar wch = mWText[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
+ wch = FIRST_EMBEDDED_CHAR + index;
+ }
+ outtextw.push_back(wch);
+ }
+ LLString outtext = wstring_to_utf8str(outtextw);
+ return outtext;
+#else
+ // Old version (Version 1)
+ mEmbeddedItemList->copyUsedCharsToIndexed();
+ LLString outtext;
+ for (S32 i=0; i<(S32)mWText.size(); i++)
+ {
+ llwchar wch = mWText[i];
+ if( wch >= FIRST_EMBEDDED_CHAR && wch <= LAST_EMBEDDED_CHAR )
+ {
+ S32 index = mEmbeddedItemList->getIndexFromEmbeddedChar(wch);
+ wch = 0x80 | index % 128;
+ }
+ else if (wch >= 0x80)
+ {
+ wch = LL_UNKNOWN_CHAR;
+ }
+ outtext.push_back((U8)wch);
+ }
+ return outtext;
+#endif
+}
+
+LLString LLViewerTextEditor::appendTime(bool prepend_newline)
+{
+ U32 utc_time;
+ utc_time = time_corrected();
+
+ // There's only one internal tm buffer.
+ struct tm* timep;
+
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(utc_time, gPacificDaylightTime);
+
+ LLString text = llformat("[%d:%02d] ", timep->tm_hour, timep->tm_min);
+ appendColoredText(text, false, prepend_newline, LLColor4::grey);
+
+ return text;
+}
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char)
+{
+ if (mEmbeddedItemList->hasEmbeddedItem(ext_char))
+ {
+ return ext_char; // already exists in my list
+ }
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(ext_char);
+ if (item)
+ {
+ // Add item to my list and return new llwchar associated with it
+ llwchar new_wc;
+ if (mEmbeddedItemList->insertEmbeddedItem( item, &new_wc, true ))
+ {
+ return new_wc;
+ }
+ }
+ return LL_UNKNOWN_CHAR; // item not found or list full
+}
+
+void LLViewerTextEditor::bindEmbeddedChars(const LLFontGL* font)
+{
+ mEmbeddedItemList->bindEmbeddedChars( font );
+}
+
+void LLViewerTextEditor::unbindEmbeddedChars(const LLFontGL* font)
+{
+ mEmbeddedItemList->unbindEmbeddedChars( font );
+}
+
+BOOL LLViewerTextEditor::getEmbeddedItemToolTipAtPos(S32 pos, LLWString &msg)
+{
+ if (pos < getLength())
+ {
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(mWText[pos]);
+ if( item )
+ {
+ msg = utf8str_to_wstring(item->getName());
+ msg += '\n';
+ msg += utf8str_to_wstring(item->getDescription());
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos)
+{
+ if( pos < getLength())
+ {
+ LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem( mWText[pos] );
+ if( item )
+ {
+ BOOL saved = LLEmbeddedItems::getEmbeddedItemSaved( mWText[pos] );
+ return openEmbeddedItem(item, saved);
+ }
+ }
+ return FALSE;
+}
+
+
+BOOL LLViewerTextEditor::openEmbeddedItem(LLInventoryItem* item, BOOL saved)
+{
+ switch( item->getType() )
+ {
+ case LLAssetType::AT_TEXTURE:
+ openEmbeddedTexture( item );
+ return TRUE;
+
+ case LLAssetType::AT_SOUND:
+ openEmbeddedSound( item );
+ return TRUE;
+
+ case LLAssetType::AT_NOTECARD:
+ openEmbeddedNotecard( item, saved );
+ return TRUE;
+
+ case LLAssetType::AT_LANDMARK:
+ showLandmarkDialog( item );
+ return TRUE;
+
+ case LLAssetType::AT_LSL_TEXT:
+ case LLAssetType::AT_CLOTHING:
+ case LLAssetType::AT_OBJECT:
+ case LLAssetType::AT_BODYPART:
+ case LLAssetType::AT_ANIMATION:
+ case LLAssetType::AT_GESTURE:
+ showCopyToInvDialog( item );
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+void LLViewerTextEditor::openEmbeddedTexture( LLInventoryItem* item )
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( item->getUUID() ) )
+ {
+ // There isn't one, so make a new preview
+ if(item)
+ {
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewTextureRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewTexture* preview = new LLPreviewTexture("preview texture",
+ rect,
+ item->getName(),
+ item->getAssetUUID(),
+ TRUE);
+ preview->setAuxItem( item );
+ preview->setNotecardInfo(mNotecardInventoryID, mObjectID);
+ }
+ }
+}
+
+void LLViewerTextEditor::openEmbeddedSound( LLInventoryItem* item )
+{
+ // Play sound locally
+ LLVector3d lpos_global = gAgent.getPositionGlobal();
+ const F32 SOUND_GAIN = 1.0f;
+ if(gAudiop)
+ {
+ gAudiop->triggerSound(
+ item->getAssetUUID(), gAgentID, SOUND_GAIN, lpos_global);
+ }
+ showCopyToInvDialog( item );
+}
+
+/*
+void LLViewerTextEditor::openEmbeddedLandmark( LLInventoryItem* item )
+{
+ // See if we can bring an existing preview to the front
+ if( !LLPreview::show( item->getUUID() ) )
+ {
+ // There isn't one, so make a new preview
+ S32 left, top;
+ gFloaterView->getNewFloaterPosition(&left, &top);
+ LLRect rect = gSavedSettings.getRect("PreviewLandmarkRect");
+ rect.translate( left - rect.mLeft, top - rect.mTop );
+
+ LLPreviewLandmark* preview = new LLPreviewLandmark(
+ "preview landmark",
+ rect,
+ item->getName(),
+ item->getUUID());
+ preview->setAuxItem( item );
+ preview->addCopyToInvButton();
+ preview->open();
+ }
+}*/
+
+void LLViewerTextEditor::openEmbeddedNotecard( LLInventoryItem* item, BOOL saved )
+{
+ if (saved)
+ {
+ // Copy to inventory
+ copyInventory(item);
+ }
+ else
+ {
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml("ConfirmNotecardSave",
+ LLViewerTextEditor::onNotecardDialog, (void*)info);
+ }
+}
+
+// static
+void LLViewerTextEditor::onNotecardDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( option == 0 )
+ {
+ // itemptr is deleted by LLPreview::save
+ LLPointer<LLInventoryItem>* itemptr = new LLPointer<LLInventoryItem>(info->mItem);
+ LLPreview::save( info->mTextEd->mNotecardInventoryID, itemptr);
+ }
+}
+
+
+void LLViewerTextEditor::showLandmarkDialog( LLInventoryItem* item )
+{
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml("ConfirmLandmarkCopy",
+ LLViewerTextEditor::onLandmarkDialog, (void*)info);
+}
+
+// static
+void LLViewerTextEditor::onLandmarkDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( option == 0 )
+ {
+ // Copy to inventory
+ info->mTextEd->copyInventory(info->mItem);
+ /*
+ * XXXPAM
+ *
+ * Yes, this is broken. We don't show the map yet.
+ *
+ LLInventoryItem* orig_item = (LLInventoryItem*)userdata;
+
+ // Copy to inventory
+ LLPointer<LLViewerInventoryItem> new_item = new LLViewerInventoryItem;
+ cloneInventoryItemToViewer(orig_item, new_item);
+ U32 flags = new_item->getFlags();
+ flags &= ~LLInventoryItem::II_FLAGS_LANDMARK_VISITED;
+ new_item->setFlags(flags);
+ new_item->updateServer(TRUE);
+ gInventory.updateItem(new_item);
+ gInventory.notifyObservers();
+
+ LLInventoryView* view = LLInventoryView::getActiveInventory();
+ if(view)
+ {
+ view->getPanel()->setSelection(new_item->getUUID(), TAKE_FOCUS_NO);
+ }
+
+ if( (0 == option) && gFloaterWorldMap )
+ {
+ // Note: there's a minor race condition here.
+ // If the user immediately tries to teleport to the landmark, the dataserver may
+ // not yet know that the user has the landmark in his inventory and so may
+ // disallow the teleport. However, the user will need to be pretty fast to make
+ // this happen, and, if it does, they haven't lost anything. Once the dataserver
+ // knows about the new item, the user will be able to teleport to it successfully.
+ gFloaterWorldMap->trackLandmark(new_item->getUUID());
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }*/
+ }
+ delete info;
+}
+
+
+void LLViewerTextEditor::showCopyToInvDialog( LLInventoryItem* item )
+{
+ LLNotecardCopyInfo *info = new LLNotecardCopyInfo(this, item);
+ gViewerWindow->alertXml( "ConfirmItemCopy",
+ LLViewerTextEditor::onCopyToInvDialog, (void*)info);
+}
+
+// static
+void LLViewerTextEditor::onCopyToInvDialog( S32 option, void* userdata )
+{
+ LLNotecardCopyInfo *info = (LLNotecardCopyInfo *)userdata;
+ if( 0 == option )
+ {
+ info->mTextEd->copyInventory(info->mItem);
+ }
+ delete info;
+}
+
+
+
+// Returns change in number of characters in mWText
+S32 LLViewerTextEditor::insertEmbeddedItem( S32 pos, LLInventoryItem* item )
+{
+ return execute( new LLTextCmdInsertEmbeddedItem( pos, item ) );
+}
+
+bool LLViewerTextEditor::importStream(std::istream& str)
+{
+ LLNotecard nc(MAX_NOTECARD_SIZE);
+ bool success = nc.importStream(str);
+ if (success)
+ {
+ const std::vector<LLPointer<LLInventoryItem> >& items = nc.getItems();
+ mEmbeddedItemList->addItems(items);
+ // Actually set the text
+ if (mAllowEmbeddedItems)
+ {
+ if (nc.getVersion() == 1)
+ setASCIIEmbeddedText( nc.getText() );
+ else
+ setEmbeddedText( nc.getText() );
+ }
+ else
+ {
+ setText( nc.getText() );
+ }
+ }
+ return success;
+}
+
+void LLViewerTextEditor::copyInventory(LLInventoryItem* item)
+{
+ copy_inventory_from_notecard(mObjectID,
+ mNotecardInventoryID,
+ item);
+}
+
+////////////////////////////////////////////////////////////////////////////
+
+BOOL LLViewerTextEditor::importBuffer( const LLString& buffer )
+{
+ LLMemoryStream str((U8*)buffer.c_str(), buffer.length());
+ return importStream(str);
+}
+
+BOOL LLViewerTextEditor::exportBuffer( LLString& buffer )
+{
+ LLNotecard nc(MAX_NOTECARD_SIZE);
+
+ std::vector<LLPointer<LLInventoryItem> > embedded_items;
+ mEmbeddedItemList->getEmbeddedItemList(embedded_items);
+
+ nc.setItems(embedded_items);
+ nc.setText(getEmbeddedText());
+
+ std::stringstream out_stream;
+ nc.exportStream(out_stream);
+
+ buffer = out_stream.str();
+
+ return TRUE;
+}
+
+LLView* LLViewerTextEditor::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
+{
+ LLString name("text_editor");
+ node->getAttributeString("name", name);
+
+ LLRect rect;
+ createRect(node, rect, parent, LLRect());
+
+ U32 max_text_length = 255;
+ node->getAttributeU32("max_length", max_text_length);
+
+ BOOL allow_embedded_items = FALSE;
+ node->getAttributeBOOL("embedded_items", allow_embedded_items);
+
+ LLFontGL* font = LLView::selectFont(node);
+
+ LLString text = node->getValue();
+
+ if (text.size() > max_text_length)
+ {
+ // Erase everything from max_text_length on.
+ text.erase(max_text_length);
+ }
+
+ LLViewerTextEditor* text_editor = new LLViewerTextEditor(name,
+ rect,
+ max_text_length,
+ text,
+ font,
+ allow_embedded_items);
+
+ BOOL ignore_tabs = text_editor->mTabToNextField;
+ node->getAttributeBOOL("ignore_tab", ignore_tabs);
+
+ text_editor->setTabToNextField(ignore_tabs);
+
+
+ text_editor->setTextEditorParameters(node);
+
+ BOOL hide_scrollbar = FALSE;
+ node->getAttributeBOOL("hide_scrollbar",hide_scrollbar);
+ text_editor->setHideScrollbarForShortDocs(hide_scrollbar);
+
+ text_editor->initFromXML(node, parent);
+
+ return text_editor;
+}
diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h
new file mode 100644
index 0000000000..b537d07ffa
--- /dev/null
+++ b/indra/newview/llviewertexteditor.h
@@ -0,0 +1,102 @@
+/**
+ * @file llviewertexteditor.h
+ * @brief Text editor widget to let users enter a a multi-line document//
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VIEWERTEXTEDITOR_H
+#define LL_VIEWERTEXTEDITOR_H
+
+#include "lltexteditor.h"
+
+class LLInventoryItem;
+
+
+//
+// Classes
+//
+class LLViewerTextEditor : public LLTextEditor
+{
+ friend class LLEmbeddedItems;
+ friend class LLTextCmdInsertEmbeddedItem;
+
+public:
+ LLViewerTextEditor(const LLString& name,
+ const LLRect& rect,
+ S32 max_length,
+ const LLString& default_text = "",
+ const LLFontGL* glfont = NULL,
+ BOOL allow_embedded_items = FALSE);
+
+ virtual ~LLViewerTextEditor();
+
+ virtual void makePristine();
+
+ static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory);
+
+ // mousehandler overrides
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleHover(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask );
+
+ virtual BOOL handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect);
+ virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask,
+ BOOL drop, EDragAndDropType cargo_type,
+ void *cargo_data, EAcceptance *accept, LLString& tooltip_msg);
+
+ const LLInventoryItem* getDragItem() { return mDragItem; }
+ virtual BOOL importBuffer(const LLString& buffer);
+ virtual bool importStream(std::istream& str);
+ virtual BOOL exportBuffer(LLString& buffer);
+ void setNotecardInfo(const LLUUID& notecard_item_id, const LLUUID& object_id)
+ {
+ mNotecardInventoryID = notecard_item_id;
+ mObjectID = object_id;
+ }
+
+ void setASCIIEmbeddedText(const LLString& instr);
+ void setEmbeddedText(const LLString& instr);
+ LLString getEmbeddedText();
+
+ LLString appendTime(bool prepend_newline);
+ // Appends Second Life time, small font, grey
+ // If this starts a line, you need to prepend a newline.
+
+ void copyInventory(LLInventoryItem* item);
+
+protected:
+ // Embedded object operations
+ virtual llwchar pasteEmbeddedItem(llwchar ext_char);
+ virtual void bindEmbeddedChars(const LLFontGL* font);
+ virtual void unbindEmbeddedChars(const LLFontGL* font);
+
+ BOOL getEmbeddedItemToolTipAtPos(S32 pos, LLWString &wmsg);
+ BOOL openEmbeddedItemAtPos( S32 pos );
+ BOOL openEmbeddedItem(LLInventoryItem* item, BOOL saved);
+
+ S32 insertEmbeddedItem(S32 pos, LLInventoryItem* item);
+
+ void openEmbeddedTexture( LLInventoryItem* item );
+ void openEmbeddedSound( LLInventoryItem* item );
+ //void openEmbeddedLandmark( LLInventoryItem* item );
+ void openEmbeddedNotecard( LLInventoryItem* item, BOOL saved );
+ void showCopyToInvDialog( LLInventoryItem* item );
+ void showLandmarkDialog( LLInventoryItem* item );
+
+ static void onCopyToInvDialog( S32 option, void* userdata );
+ static void onNotecardDialog( S32 option, void* userdata );
+ static void onLandmarkDialog( S32 option, void* userdata );
+
+protected:
+ LLPointer<LLInventoryItem> mDragItem;
+ BOOL mDragItemSaved;
+ LLEmbeddedItems* mEmbeddedItemList;
+
+ LLUUID mObjectID;
+ LLUUID mNotecardInventoryID;
+};
+
+#endif // LL_VIEWERTEXTEDITOR_H
diff --git a/indra/newview/llviewertextureanim.cpp b/indra/newview/llviewertextureanim.cpp
new file mode 100644
index 0000000000..b40320ad2d
--- /dev/null
+++ b/indra/newview/llviewertextureanim.cpp
@@ -0,0 +1,191 @@
+/**
+ * @file llviewertextureanim.cpp
+ * @brief LLViewerTextureAnim class implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewertextureanim.h"
+
+#include "llmath.h"
+#include "llerror.h"
+
+LLViewerTextureAnim::LLViewerTextureAnim() : LLTextureAnim()
+{
+ mLastFrame = -1.f; // Force an update initially
+ mLastTime = 0.f;
+}
+
+LLViewerTextureAnim::~LLViewerTextureAnim()
+{
+}
+
+void LLViewerTextureAnim::reset()
+{
+ LLTextureAnim::reset();
+ mTimer.reset();
+}
+
+
+S32 LLViewerTextureAnim::animateTextures(F32 &off_s, F32 &off_t,
+ F32 &scale_s, F32 &scale_t,
+ F32 &rot)
+{
+ S32 result = 0;
+ if (!(mMode & ON))
+ {
+ mLastTime = 0.f;
+ mLastFrame = -1.f;
+ return result;
+ }
+
+
+ F32 num_frames = 1.0;
+ F32 full_length = 1.0;
+
+ if (mLength)
+ {
+ num_frames = mLength;
+ }
+ else
+ {
+ num_frames = llmax(1.f, (F32)(mSizeX * mSizeY));
+ }
+
+ if (mMode & PING_PONG)
+ {
+ if (mMode & SMOOTH)
+ {
+ full_length = 2.f*num_frames;
+ }
+ else if (mMode & LOOP)
+ {
+ full_length = 2.f*num_frames - 2.f;
+ full_length = llmax(1.f, full_length);
+ }
+ else
+ {
+ full_length = 2.f*num_frames - 1.f;
+ full_length = llmax(1.f, full_length);
+ }
+ }
+ else
+ {
+ full_length = num_frames;
+ }
+
+
+ F32 frame_counter;
+ if (mMode & SMOOTH)
+ {
+ frame_counter = mTimer.getElapsedTimeAndResetF32() * mRate + (F32)mLastTime;
+ }
+ else
+ {
+ frame_counter = mTimer.getElapsedTimeF32() * mRate;
+ }
+ mLastTime = frame_counter;
+
+ if (mMode & LOOP)
+ {
+ frame_counter = fmod(frame_counter, full_length);
+ }
+ else
+ {
+ frame_counter = llmin(full_length - 1.f, frame_counter);
+ }
+
+ if (!(mMode & SMOOTH))
+ {
+ frame_counter = (F32)llfloor(frame_counter + 0.01f);
+ }
+
+ if (mMode & PING_PONG)
+ {
+ if (frame_counter >= num_frames)
+ {
+ if (mMode & SMOOTH)
+ {
+ frame_counter = num_frames - (frame_counter - num_frames);
+ }
+ else
+ {
+ frame_counter = (num_frames - 1.99f) - (frame_counter - num_frames);
+ }
+ }
+ }
+
+ if (mMode & REVERSE)
+ {
+ if (mMode & SMOOTH)
+ {
+ frame_counter = num_frames - frame_counter;
+ }
+ else
+ {
+ frame_counter = (num_frames - 0.99f) - frame_counter;
+ }
+ }
+
+ frame_counter += mStart;
+
+ if (!(mMode & SMOOTH))
+ {
+ frame_counter = (F32)llround(frame_counter);
+ }
+
+ //
+ // Now that we've calculated the frame time, do an update.
+ // Will we correctly update stuff if the texture anim has
+ // changed, but not the frame counter?
+ //
+ if (mLastFrame != frame_counter)
+ {
+ mLastFrame = frame_counter;
+ if (mMode & ROTATE)
+ {
+ result |= ROTATE;
+ rot = frame_counter;
+ }
+ else if (mMode & SCALE)
+ {
+ result |= SCALE;
+ scale_s = frame_counter;
+ scale_t = frame_counter;
+ }
+ else
+ {
+ result |= TRANSLATE;
+ F32 x_frame;
+ S32 y_frame;
+ F32 x_pos;
+ F32 y_pos;
+
+ if ( (mSizeX)
+ &&(mSizeY))
+ {
+ result |= SCALE;
+ scale_s = 1.f/mSizeX;
+ scale_t = 1.f/mSizeY;
+ x_frame = fmod(frame_counter, mSizeX);
+ y_frame = (S32)(frame_counter / mSizeX);
+ x_pos = x_frame * scale_s;
+ y_pos = y_frame * scale_t;
+ off_s = (-0.5f + 0.5f*scale_s)+ x_pos;
+ off_t = (0.5f - 0.5f*scale_t) - y_pos;
+ }
+ else
+ {
+ scale_s = 1.f;
+ scale_t = 1.f;
+ x_pos = frame_counter * scale_s;
+ off_s = (-0.5f + 0.5f*scale_s)+ x_pos;
+ off_t = 0.f;
+ }
+ }
+ }
+ return result;
+}
diff --git a/indra/newview/llviewertextureanim.h b/indra/newview/llviewertextureanim.h
new file mode 100644
index 0000000000..c7e75c565c
--- /dev/null
+++ b/indra/newview/llviewertextureanim.h
@@ -0,0 +1,33 @@
+/**
+ * @file llviewertextureanim.h
+ * @brief LLViewerTextureAnim class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERTEXTUREANIM_H
+#define LL_LLVIEWERTEXTUREANIM_H
+
+#include "lltextureanim.h"
+#include "llframetimer.h"
+
+class LLViewerTextureAnim : public LLTextureAnim
+{
+public:
+ LLViewerTextureAnim();
+ virtual ~LLViewerTextureAnim();
+
+ /*virtual*/ void reset();
+
+ S32 animateTextures(F32 &off_s, F32 &off_t, F32 &scale_s, F32 &scale_t, F32 &rotate);
+ enum
+ {
+ TRANSLATE = 0x01 // Result code JUST for animateTextures
+ };
+protected:
+ LLFrameTimer mTimer;
+ F64 mLastTime;
+ F32 mLastFrame;
+};
+#endif
diff --git a/indra/newview/llviewerthrottle.cpp b/indra/newview/llviewerthrottle.cpp
new file mode 100644
index 0000000000..4ffdabbbc9
--- /dev/null
+++ b/indra/newview/llviewerthrottle.cpp
@@ -0,0 +1,313 @@
+/**
+ * @file llviewerthrottle.cpp
+ * @brief LLViewerThrottle class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+#include "llviewerthrottle.h"
+
+#include "llviewercontrol.h"
+#include "message.h"
+#include "llagent.h"
+#include "llframetimer.h"
+#include "llviewerstats.h"
+#include "lldatapacker.h"
+
+// consts
+
+// The viewer is allowed to set the under-the-hood bandwidth to 50%
+// greater than the prefs UI shows, under the assumption that the
+// viewer won't receive all the different message types at once.
+// I didn't design this, don't know who did. JC
+const F32 MAX_FRACTIONAL = 1.5f;
+const F32 MIN_FRACTIONAL = 0.2f;
+
+const F32 MIN_BANDWIDTH = 50.f;
+const F32 MAX_BANDWIDTH = 1500.f;
+const F32 STEP_FRACTIONAL = 0.1f;
+const F32 TIGHTEN_THROTTLE_THRESHOLD = 3.0f; // packet loss % per s
+const F32 EASE_THROTTLE_THRESHOLD = 0.5f; // packet loss % per s
+const F32 DYNAMIC_UPDATE_DURATION = 5.0f; // seconds
+
+LLViewerThrottle gViewerThrottle;
+
+// static
+const char *LLViewerThrottle::sNames[TC_EOF] = {
+ "Resend",
+ "Land",
+ "Wind",
+ "Cloud",
+ "Task",
+ "Texture",
+ "Asset"
+ };
+
+
+// Bandwidth settings for different bit rates, they're interpolated/extrapolated.
+// Resend Land Wind Cloud Task Texture Asset
+const F32 BW_PRESET_50[TC_EOF] = { 5, 10, 3, 3, 10, 10, 9 };
+const F32 BW_PRESET_300[TC_EOF] = { 30, 40, 9, 9, 86, 86, 40 };
+const F32 BW_PRESET_500[TC_EOF] = { 50, 70, 14, 14, 136, 136, 80 };
+const F32 BW_PRESET_1000[TC_EOF] = { 100, 100, 20, 20, 310, 310, 140 };
+
+LLViewerThrottleGroup::LLViewerThrottleGroup()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottles[i] = 0.f;
+ }
+ mThrottleTotal = 0.f;
+}
+
+
+LLViewerThrottleGroup::LLViewerThrottleGroup(const F32 settings[])
+{
+ mThrottleTotal = 0.f;
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ mThrottles[i] = settings[i];
+ mThrottleTotal += settings[i];
+ }
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator*(const F32 frac) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] * frac;
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator+(const LLViewerThrottleGroup &b) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] + b.mThrottles[i];
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+LLViewerThrottleGroup LLViewerThrottleGroup::operator-(const LLViewerThrottleGroup &b) const
+{
+ LLViewerThrottleGroup res;
+ res.mThrottleTotal = 0.f;
+
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ res.mThrottles[i] = mThrottles[i] - b.mThrottles[i];
+ res.mThrottleTotal += res.mThrottles[i];
+ }
+
+ return res;
+}
+
+
+void LLViewerThrottleGroup::sendToSim() const
+{
+ llinfos << "Sending throttle settings, total BW " << mThrottleTotal << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+
+ msg->newMessageFast(_PREHASH_AgentThrottle);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, msg->mOurCircuitCode);
+
+ msg->nextBlockFast(_PREHASH_Throttle);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+
+ // Pack up the throttle data
+ U8 tmp[64];
+ LLDataPackerBinaryBuffer dp(tmp, MAX_THROTTLE_SIZE);
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ //sim wants BPS, not KBPS
+ dp.packF32(mThrottles[i] * 1024.0f, "Throttle");
+ }
+ S32 len = dp.getCurrentSize();
+ msg->addBinaryDataFast(_PREHASH_Throttles, tmp, len);
+
+ gAgent.sendReliableMessage();
+}
+
+
+void LLViewerThrottleGroup::dump()
+{
+ S32 i;
+ for (i = 0; i < TC_EOF; i++)
+ {
+ llinfos << LLViewerThrottle::sNames[i] << ": " << mThrottles[i] << llendl;
+ }
+ llinfos << "Total: " << mThrottleTotal << llendl;
+}
+
+class LLBPSListener : public LLSimpleListener
+{
+public:
+ virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata)
+ {
+ gViewerThrottle.setMaxBandwidth((F32) event->getValue().asReal()*1024);
+ return true;
+ }
+};
+
+LLViewerThrottle::LLViewerThrottle() :
+ mMaxBandwidth(0.f),
+ mCurrentBandwidth(0.f),
+ mThrottleFrac(1.f)
+{
+ // Need to be pushed on in bandwidth order
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_50));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_300));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_500));
+ mPresets.push_back(LLViewerThrottleGroup(BW_PRESET_1000));
+}
+
+
+void LLViewerThrottle::setMaxBandwidth(F32 kbits_per_second, BOOL from_event)
+{
+ if (!from_event)
+ {
+ gSavedSettings.setF32("ThrottleBandwidthKBPS", kbits_per_second);
+ }
+ gViewerThrottle.load();
+
+ if (gAgent.getRegion())
+ {
+ gViewerThrottle.sendToSim();
+ }
+}
+
+void LLViewerThrottle::load()
+{
+ mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS")*1024;
+ resetDynamicThrottle();
+ mCurrent.dump();
+}
+
+
+void LLViewerThrottle::save() const
+{
+ gSavedSettings.setF32("ThrottleBandwidthKBPS", mMaxBandwidth/1024);
+}
+
+
+void LLViewerThrottle::sendToSim() const
+{
+ mCurrent.sendToSim();
+}
+
+
+LLViewerThrottleGroup LLViewerThrottle::getThrottleGroup(const F32 bandwidth_kbps)
+{
+ //Clamp the bandwidth users can set.
+ F32 set_bandwidth = llclamp(bandwidth_kbps, MIN_BANDWIDTH, MAX_BANDWIDTH);
+
+ S32 count = mPresets.size();
+
+ S32 i;
+ for (i = 0; i < count; i++)
+ {
+ if (mPresets[i].getTotal() > set_bandwidth)
+ {
+ break;
+ }
+ }
+
+ if (i == 0)
+ {
+ // We return the minimum if it's less than the minimum
+ return mPresets[0];
+ }
+ else if (i == count)
+ {
+ // Higher than the highest preset, we extrapolate out based on the
+ // last two presets. This allows us to keep certain throttle channels from
+ // growing in bandwidth
+ F32 delta_bw = set_bandwidth - mPresets[count-1].getTotal();
+ LLViewerThrottleGroup delta_throttle = mPresets[count - 1] - mPresets[count - 2];
+ F32 delta_total = delta_throttle.getTotal();
+ F32 delta_frac = delta_bw / delta_total;
+ delta_throttle = delta_throttle * delta_frac;
+ return mPresets[count-1] + delta_throttle;
+ }
+ else
+ {
+ // In between two presets, just interpolate
+ F32 delta_bw = set_bandwidth - mPresets[i - 1].getTotal();
+ LLViewerThrottleGroup delta_throttle = mPresets[i] - mPresets[i - 1];
+ F32 delta_total = delta_throttle.getTotal();
+ F32 delta_frac = delta_bw / delta_total;
+ delta_throttle = delta_throttle * delta_frac;
+ return mPresets[i - 1] + delta_throttle;
+ }
+}
+
+
+// static
+void LLViewerThrottle::resetDynamicThrottle()
+{
+ mThrottleFrac = MAX_FRACTIONAL;
+
+ mCurrentBandwidth = mMaxBandwidth*MAX_FRACTIONAL;
+ mCurrent = getThrottleGroup(mCurrentBandwidth / 1024.0f);
+}
+
+void LLViewerThrottle::updateDynamicThrottle()
+{
+ if (mUpdateTimer.getElapsedTimeF32() < DYNAMIC_UPDATE_DURATION)
+ {
+ return;
+ }
+ mUpdateTimer.reset();
+
+ if (gViewerStats->mPacketsLostPercentStat.getMean() > TIGHTEN_THROTTLE_THRESHOLD)
+ {
+ if (mThrottleFrac <= MIN_FRACTIONAL || mCurrentBandwidth / 1024.0f <= MIN_BANDWIDTH)
+ {
+ return;
+ }
+ mThrottleFrac -= STEP_FRACTIONAL;
+ mThrottleFrac = llmax(MIN_FRACTIONAL, mThrottleFrac);
+ mCurrentBandwidth = mMaxBandwidth * mThrottleFrac;
+ mCurrent = getThrottleGroup(mCurrentBandwidth / 1024.0f);
+ mCurrent.sendToSim();
+ llinfos << "Tightening network throttle to " << mCurrentBandwidth << llendl;
+ }
+ else if (gViewerStats->mPacketsLostPercentStat.getMean() <= EASE_THROTTLE_THRESHOLD)
+ {
+ if (mThrottleFrac >= MAX_FRACTIONAL || mCurrentBandwidth / 1024.0f >= MAX_BANDWIDTH)
+ {
+ return;
+ }
+ mThrottleFrac += STEP_FRACTIONAL;
+ mThrottleFrac = llmin(MAX_FRACTIONAL, mThrottleFrac);
+ mCurrentBandwidth = mMaxBandwidth * mThrottleFrac;
+ mCurrent = getThrottleGroup(mCurrentBandwidth/1024.0f);
+ mCurrent.sendToSim();
+ llinfos << "Easing network throttle to " << mCurrentBandwidth << llendl;
+ }
+}
diff --git a/indra/newview/llviewerthrottle.h b/indra/newview/llviewerthrottle.h
new file mode 100644
index 0000000000..ec4323ba81
--- /dev/null
+++ b/indra/newview/llviewerthrottle.h
@@ -0,0 +1,72 @@
+/**
+ * @file llviewerthrottle.h
+ * @brief LLViewerThrottle class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVIEWERTHROTTLE_H
+#define LL_LLVIEWERTHROTTLE_H
+
+#include <vector>
+
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llthrottle.h"
+
+class LLViewerThrottleGroup
+{
+ LLViewerThrottleGroup();
+ LLViewerThrottleGroup(const F32 settings[TC_EOF]);
+
+ LLViewerThrottleGroup operator*(const F32 frac) const;
+ LLViewerThrottleGroup operator+(const LLViewerThrottleGroup &b) const;
+ LLViewerThrottleGroup operator-(const LLViewerThrottleGroup &b) const;
+
+ F32 getTotal() { return mThrottleTotal; }
+ void sendToSim() const;
+
+ void dump();
+
+ friend class LLViewerThrottle;
+protected:
+ F32 mThrottles[TC_EOF];
+ F32 mThrottleTotal;
+};
+
+class LLViewerThrottle
+{
+public:
+ LLViewerThrottle();
+
+ void setMaxBandwidth(F32 kbits_per_second, BOOL from_event = FALSE);
+
+ void load();
+ void save() const;
+ void sendToSim() const;
+
+ F32 getMaxBandwidth()const { return mMaxBandwidth; }
+ F32 getCurrentBandwidth() const { return mCurrentBandwidth; }
+
+ void updateDynamicThrottle();
+ void resetDynamicThrottle();
+
+ LLViewerThrottleGroup getThrottleGroup(const F32 bandwidth_kbps);
+
+ static const char *sNames[TC_EOF];
+protected:
+ F32 mMaxBandwidth;
+ F32 mCurrentBandwidth;
+
+ LLViewerThrottleGroup mCurrent;
+
+ std::vector<LLViewerThrottleGroup> mPresets;
+
+ LLFrameTimer mUpdateTimer;
+ F32 mThrottleFrac;
+};
+
+extern LLViewerThrottle gViewerThrottle;
+
+#endif // LL_LLVIEWERTHROTTLE_H
diff --git a/indra/newview/llviewervisualparam.cpp b/indra/newview/llviewervisualparam.cpp
new file mode 100644
index 0000000000..8c28d58f40
--- /dev/null
+++ b/indra/newview/llviewervisualparam.cpp
@@ -0,0 +1,132 @@
+/**
+ * @file llviewervisualparam.cpp
+ * @brief Implementation of LLViewerVisualParam class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//-----------------------------------------------------------------------------
+// Header Files
+//-----------------------------------------------------------------------------
+#include "llviewerprecompiledheaders.h"
+
+#include "llviewervisualparam.h"
+#include "llxmltree.h"
+#include "llui.h"
+#include "llwearable.h"
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo()
+//-----------------------------------------------------------------------------
+LLViewerVisualParamInfo::LLViewerVisualParamInfo()
+ :
+ mWearableType( WT_INVALID ),
+ mCamDist( 0.5f ),
+ mCamAngle( 0.f ),
+ mCamElevation( 0.f ),
+ mEditGroupDisplayOrder( 0 ),
+ mShowSimple(FALSE),
+ mSimpleMin(0.f),
+ mSimpleMax(100.f)
+{
+}
+
+LLViewerVisualParamInfo::~LLViewerVisualParamInfo()
+{
+}
+
+//-----------------------------------------------------------------------------
+// parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParamInfo::parseXml(LLXmlTreeNode *node)
+{
+ llassert( node->hasName( "param" ) );
+
+ if (!LLVisualParamInfo::parseXml(node))
+ return FALSE;
+
+ // VIEWER SPECIFIC PARAMS
+
+ LLString wearable;
+ static LLStdStringHandle wearable_string = LLXmlTree::addAttributeString("wearable");
+ if( node->getFastAttributeString( wearable_string, wearable) )
+ {
+ mWearableType = LLWearable::typeNameToType( wearable );
+ }
+
+ static LLStdStringHandle edit_group_string = LLXmlTree::addAttributeString("edit_group");
+ if (!node->getFastAttributeString( edit_group_string, mEditGroup))
+ {
+ mEditGroup = "";
+ }
+
+ // Optional camera offsets from the current joint center. Used for generating "hints" (thumbnails).
+ static LLStdStringHandle camera_distance_string = LLXmlTree::addAttributeString("camera_distance");
+ node->getFastAttributeF32( camera_distance_string, mCamDist );
+ static LLStdStringHandle camera_angle_string = LLXmlTree::addAttributeString("camera_angle");
+ node->getFastAttributeF32( camera_angle_string, mCamAngle ); // in degrees
+ static LLStdStringHandle camera_elevation_string = LLXmlTree::addAttributeString("camera_elevation");
+ node->getFastAttributeF32( camera_elevation_string, mCamElevation );
+ static LLStdStringHandle camera_target_string = LLXmlTree::addAttributeString("camera_target");
+ node->getFastAttributeString( camera_target_string, mCamTargetName );
+
+ mCamAngle += 180;
+
+ static S32 params_loaded = 0;
+
+ // By default, parameters are displayed in the order in which they appear in the xml file.
+ // "edit_group_order" overriddes.
+ static LLStdStringHandle edit_group_order_string = LLXmlTree::addAttributeString("edit_group_order");
+ if( !node->getFastAttributeF32( edit_group_order_string, mEditGroupDisplayOrder ) )
+ {
+ mEditGroupDisplayOrder = (F32)params_loaded;
+ }
+
+ params_loaded++;
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam()
+//-----------------------------------------------------------------------------
+LLViewerVisualParam::LLViewerVisualParam()
+{
+}
+
+/*
+//=============================================================================
+// These virtual functions should always be overridden,
+// but are included here for use as templates
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// setInfo()
+//-----------------------------------------------------------------------------
+
+BOOL LLViewerVisualParam::setInfo(LLViewerVisualParamInfo *info)
+{
+ llassert(mInfo == NULL);
+ if (info->mID < 0)
+ return FALSE;
+ mInfo = info;
+ mID = info->mID;
+ setWeight(getDefaultWeight(), FALSE );
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseData()
+//-----------------------------------------------------------------------------
+BOOL LLViewerVisualParam::parseData(LLXmlTreeNode *node)
+{
+ LLViewerVisualParamInfo* info = new LLViewerVisualParamInfo;
+
+ info->parseXml(node);
+ if (!setInfo(info))
+ return FALSE;
+
+ return TRUE;
+}
+*/
diff --git a/indra/newview/llviewervisualparam.h b/indra/newview/llviewervisualparam.h
new file mode 100644
index 0000000000..053d2b6e0c
--- /dev/null
+++ b/indra/newview/llviewervisualparam.h
@@ -0,0 +1,83 @@
+/**
+ * @file llviewervisualparam.h
+ * @brief viewer side visual params (with data file parsing)
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLViewerVisualParam_H
+#define LL_LLViewerVisualParam_H
+
+#include "v3math.h"
+#include "llstring.h"
+#include "llvisualparam.h"
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParamInfo
+//-----------------------------------------------------------------------------
+class LLViewerVisualParamInfo : public LLVisualParamInfo
+{
+ friend class LLViewerVisualParam;
+public:
+ LLViewerVisualParamInfo();
+ /*virtual*/ ~LLViewerVisualParamInfo();
+
+ /*virtual*/ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ S32 mWearableType;
+ LLString mEditGroup;
+ F32 mCamDist;
+ F32 mCamAngle; // degrees
+ F32 mCamElevation;
+ LLString mCamTargetName;
+ F32 mEditGroupDisplayOrder;
+ BOOL mShowSimple; // show edit controls when in "simple ui" mode?
+ F32 mSimpleMin; // when in simple UI, apply this minimum, range 0.f to 100.f
+ F32 mSimpleMax; // when in simple UI, apply this maximum, range 0.f to 100.f
+};
+
+//-----------------------------------------------------------------------------
+// LLViewerVisualParam
+// VIRTUAL CLASS
+// a viewer side interface class for a generalized parametric modification of the avatar mesh
+//-----------------------------------------------------------------------------
+class LLViewerVisualParam : public LLVisualParam
+{
+public:
+ LLViewerVisualParam();
+ /*virtual*/ ~LLViewerVisualParam(){};
+
+ // Special: These functions are overridden by child classes
+ LLViewerVisualParamInfo *getInfo() const { return (LLViewerVisualParamInfo*)mInfo; };
+ // This sets mInfo and calls initialization functions
+ BOOL setInfo(LLViewerVisualParamInfo *info);
+
+ // LLVisualParam Virtual functions
+ ///*virtual*/ BOOL parseData(LLXmlTreeNode* node);
+
+ // New Virtual functions
+ virtual F32 getTotalDistortion() = 0;
+ virtual const LLVector3& getAvgDistortion() = 0;
+ virtual F32 getMaxDistortion() = 0;
+ virtual LLVector3 getVertexDistortion(S32 index, LLPolyMesh *mesh) = 0;
+ virtual const LLVector3* getFirstDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+ virtual const LLVector3* getNextDistortion(U32 *index, LLPolyMesh **mesh) = 0;
+
+ // interface methods
+ F32 getDisplayOrder() { return getInfo()->mEditGroupDisplayOrder; }
+ S32 getWearableType() const { return getInfo()->mWearableType; }
+ const LLString& getEditGroup() const { return getInfo()->mEditGroup; }
+
+ F32 getCameraDistance() const { return getInfo()->mCamDist; }
+ F32 getCameraAngle() const { return getInfo()->mCamAngle; } // degrees
+ F32 getCameraElevation() const { return getInfo()->mCamElevation; }
+ const std::string& getCameraTargetName() const { return getInfo()->mCamTargetName; }
+
+ BOOL getShowSimple() const { return getInfo()->mShowSimple; }
+ F32 getSimpleMin() const { return getInfo()->mSimpleMin; }
+ F32 getSimpleMax() const { return getInfo()->mSimpleMax; }
+};
+
+#endif // LL_LLViewerVisualParam_H
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
new file mode 100644
index 0000000000..3836b2d1d7
--- /dev/null
+++ b/indra/newview/llviewerwindow.cpp
@@ -0,0 +1,4816 @@
+/**
+ * @file llviewerwindow.cpp
+ * @brief Implementation of the LLViewerWindow class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// system library includes
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+
+#include "llviewerwindow.h"
+#include "llviewquery.h"
+#include "llxmltree.h"
+#include "llviewercamera.h"
+//#include "imdebug.h"
+
+#ifdef SABINRIG
+#include "cbw.h"
+#endif //SABINRIG
+
+//
+// TODO: Many of these includes are unnecessary. Remove them.
+//
+
+// linden library includes
+#include "audioengine.h" // mute on minimize
+#include "indra_constants.h"
+#include "linked_lists.h"
+#include "llassetstorage.h"
+#include "llfontgl.h"
+#include "llmediaengine.h" // mute on minimize
+#include "llrect.h"
+#include "llsky.h"
+#include "llstring.h"
+#include "llui.h"
+#include "lluuid.h"
+#include "llview.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "text_out.h"
+#include "lltimer.h"
+#include "timing.h"
+#include "llviewermenu.h"
+
+// newview includes
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbox.h"
+#include "llcameraview.h"
+#include "llchatbar.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "llcylinder.h"
+#include "lldebugview.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolbump.h"
+#include "lldrawpoolwater.h"
+#include "llmaniptranslate.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfilepicker.h"
+#include "llfloater.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
+#include "llfloaterland.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloaternamedesc.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llframestatview.h"
+#include "llgesturemgr.h"
+#include "llglheaders.h"
+#include "llhippo.h"
+#include "llhoverview.h"
+#include "llhudmanager.h"
+#include "llhudview.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llmodaldialog.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llnotify.h"
+#include "lloverlaybar.h"
+#include "llpreviewtexture.h"
+#include "llprogressview.h"
+#include "llresmgr.h"
+#include "llrootview.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "llstartup.h"
+#include "llstatusbar.h"
+#include "llstatview.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llimview.h"
+#include "lltexlayer.h"
+#include "lltextbox.h"
+#include "lltextureview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolmorph.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "lltoolview.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "llviewercamera.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerkeyboard.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "llworldmapview.h"
+#include "moviemaker.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+#if LL_WINDOWS
+#include "llwindebug.h"
+#include <tchar.h> // For Unicode conversion methods
+#endif
+
+//
+// Globals
+//
+
+LLBottomPanel* gBottomPanel = NULL;
+
+extern BOOL gDebugClicks;
+extern BOOL gDisplaySwapBuffers;
+extern S32 gJamesInt;
+
+LLViewerWindow *gViewerWindow = NULL;
+LLVelocityBar *gVelocityBar = NULL;
+
+LLVector3d gLastHitPosGlobal;
+LLVector3d gLastHitObjectOffset;
+LLUUID gLastHitObjectID;
+S32 gLastHitObjectFace = -1;
+BOOL gLastHitLand = FALSE;
+F32 gLastHitUCoord;
+F32 gLastHitVCoord;
+
+
+LLVector3d gLastHitNonFloraPosGlobal;
+LLVector3d gLastHitNonFloraObjectOffset;
+LLUUID gLastHitNonFloraObjectID;
+S32 gLastHitNonFloraObjectFace = -1;
+BOOL gLastHitParcelWall = FALSE;
+
+S32 gLastHitUIElement = 0;
+LLHUDIcon* gLastHitHUDIcon = NULL;
+
+BOOL gDebugSelect = FALSE;
+U8 gLastPickAlpha = 255;
+BOOL gUseGLPick = FALSE;
+
+// On the next pick pass (whenever that happens)
+// should we try to pick individual faces?
+// Cleared to FALSE every time a pick happens.
+BOOL gPickFaces = FALSE;
+
+LLFrameTimer gMouseIdleTimer;
+LLFrameTimer gAwayTimer;
+LLFrameTimer gAwayTriggerTimer;
+LLFrameTimer gAlphaFadeTimer;
+
+BOOL gShowOverlayTitle = FALSE;
+BOOL gPickTransparent = TRUE;
+
+BOOL gDebugFastUIRender = FALSE;
+
+BOOL gbCapturing = FALSE;
+MovieMaker gMovieMaker;
+
+S32 CHAT_BAR_HEIGHT = 28;
+S32 OVERLAY_BAR_HEIGHT = 20;
+
+const U8 NO_FACE = 255;
+BOOL gQuietSnapshot = FALSE;
+
+const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back
+const F32 MAX_FAST_FRAME_TIME = 0.5f;
+const F32 FAST_FRAME_INCREMENT = 0.1f;
+
+const S32 PICK_HALF_WIDTH = 5;
+const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1;
+
+const F32 MIN_DISPLAY_SCALE = 0.85f;
+
+#ifdef SABINRIG
+/// ALL RIG STUFF
+bool rigControl = false;
+bool voltDisplay = true;
+bool nominalX = false;
+bool nominalY = false;
+static F32 nomerX = 0.0f;
+static F32 nomerY = 0.0f;
+const BOARD_NUM = 0; // rig stuff!
+const ADRANGE = BIP10VOLTS; // rig stuff!
+static unsigned short DataVal; // rig stuff!
+static F32 oldValueX = 0;
+static F32 newValueX = 50;
+static F32 oldValueY = 0;
+static F32 newValueY = 50;
+static S32 mouseX = 50;
+static S32 mouseY = 50;
+static float VoltageX = 50; // rig stuff!
+static float VoltageY = 50; // rig stuff!
+static float nVoltX = 0;
+static float nVoltY = 0;
+static F32 temp1 = 50.f;
+static F32 temp2 = 20.f;
+LLCoordGL new_gl;
+#endif //SABINRIG
+
+char LLViewerWindow::sSnapshotBaseName[LL_MAX_PATH];
+char LLViewerWindow::sSnapshotDir[LL_MAX_PATH];
+
+char LLViewerWindow::sMovieBaseName[LL_MAX_PATH];
+
+extern void toggle_debug_menus(void*);
+
+#ifdef SABINRIG
+// static
+void LLViewerWindow::printFeedback()
+{
+ if(rigControl == true)
+ {
+ cbAIn (BOARD_NUM, 0, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageX); //Convert raw to voltage for X-axis
+ cbAIn (BOARD_NUM, 1, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageY); //Convert raw to voltage for Y-axis
+ if(voltDisplay == true)
+ {
+ llinfos << "Current Voltages - X:" << VoltageX << " Y:" << VoltageY << llendl; //Display voltage
+ }
+
+ if(nVoltX == 0)
+ {
+ nVoltX = VoltageX;
+ nVoltY = VoltageY; //First time setup of nominal values.
+ }
+
+ newValueX = VoltageX;
+ newValueY = VoltageY; //Take in current voltage and set to a separate value for adjustment.
+
+ mouseX = mCurrentMousePoint.mX;
+ mouseY = mCurrentMousePoint.mY; //Take in current cursor position and set to separate value for adjustment.
+
+ if( abs(newValueX - nVoltX) > nomerX )
+ {
+ if( (newValueX - oldValueX) < 0)
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5)) * -temp;
+ }
+ else
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5) * temp1);
+ }
+ }
+ else
+ {
+ mouseX = getWindowWidth() / 2;
+ }
+ if( abs(newValueY - nVoltY) > nomerY )
+ {
+ if( (newValueY - oldValueY) < 0)
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * -temp2);
+ }
+ else
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * temp2);
+ }
+ }
+ else
+ {
+ mouseY = getWindowHeight() / 2;
+ }
+ //mouseX += (S32)( (newValueX - nVoltX) * temp1 + 0.5 );
+ // (newValueX - oldValueX) = difference between current position and nominal position
+ // * temp1 = the amplification of the number that sets sensitivity
+ // + 0.5 = fixes rounding errors
+
+
+ //mouseY += (S32)( (newValueY - nVoltY) * temp2 + 0.5 ); //Algorithm to adjust voltage for mouse adjustment.
+
+ oldValueX = newValueX;
+ oldValueY = newValueY;
+
+ new_gl.mX = mouseX;
+ new_gl.mY = mouseY; //Setup final coordinate to move mouse to.
+
+ setCursorPosition(new_gl); //Set final cursor position
+ }
+}
+#endif //SABINRIG
+
+
+//
+// LLViewerWindow
+//
+
+BOOL LLViewerWindow::handleMouseDown(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]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse down at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mousedown
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseDown(local_x, local_y, mask);
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ // Give the UI views a chance to process the click
+ if( mRootView->handleMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ if (gDisconnected)
+ {
+ return FALSE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLViewerWindow::handleDoubleClick(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]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse double-click at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleDoubleClick(local_x, local_y, mask);
+ }
+
+ // Check for hit on UI.
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleDoubleClick(local_x, local_y, mask)) return TRUE;
+ }
+
+ if (mRootView->handleDoubleClick(x, y, mask))
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ // Why is this here? JC 9/3/2002
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleDoubleClick( x, y, mask ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // if we got this far and nothing handled a double click, pass a normal mouse down
+ return handleMouseDown(window, pos, mask);
+}
+
+BOOL LLViewerWindow::handleMouseUp(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]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse up" << llendl;
+ }
+
+ mLeftMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Left Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Left Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+BOOL LLViewerWindow::handleRightMouseDown(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]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse down at " << x << "," << y << llendl;
+ }
+
+ mRightMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseDown(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleRightMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ if( mRootView->handleRightMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down not handled by view" << llendl;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleRightMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ //FIXME: this should be rolled into the composite tool logic, not hardcoded at the top level
+ if (gToolPie && (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode()) )
+ {
+ // If the current tool didn't process the click, we should show
+ // the pie menu. This can be done by passing the event to the pie
+ // menu tool.
+ gToolPie->handleRightMouseDown(x, y, mask);
+ // show_context_menu( x, y, mask );
+ }
+
+ return TRUE;
+}
+
+BOOL LLViewerWindow::handleRightMouseUp(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]);
+
+ // Don't care about caps lock for mouse events.
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse up" << llendl;
+ }
+
+ mRightMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleRightMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Right Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Right Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleRightMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+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]);
+
+ mMouseInWindow = TRUE;
+
+ // Save mouse point for access during idle() and display()
+
+ LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
+ LLCoordGL mouse_point(x, y);
+ saveLastMouse(mouse_point);
+ BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY);
+
+ gMouseIdleTimer.reset();
+
+ mWindow->showCursorFromMouseMove();
+
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ if(mToolTip && mouse_actually_moved)
+ {
+ mToolTipBlocked = FALSE; // Blocking starts on keyboard events and (only) ends here.
+ if( mToolTip->getVisible() && !mToolTipStickyRect.pointInRect( x, y ) )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+ }
+
+ // Activate the hover picker on mouse move.
+ if (gHoverView)
+ {
+ gHoverView->setTyping(FALSE);
+ }
+}
+
+void LLViewerWindow::handleMouseLeave(LLWindow *window)
+{
+ // Note: we won't get this if we have captured the mouse.
+ llassert( gFocusMgr.getMouseCapture() == NULL );
+ mMouseInWindow = FALSE;
+ if (mToolTip)
+ {
+ mToolTip->setVisible( FALSE );
+ }
+}
+
+BOOL LLViewerWindow::handleCloseRequest(LLWindow *window)
+{
+ // User has indicated they want to close, but we may need to ask
+ // about modified documents.
+ app_request_quit();
+ // Don't quit immediately
+ return FALSE;
+}
+
+void LLViewerWindow::handleQuit(LLWindow *window)
+{
+ app_force_quit(NULL);
+}
+
+void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height)
+{
+ reshape(width, height);
+}
+
+// The top-level window has gained focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocus(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(TRUE);
+ LLModalDialog::onAppFocusGained();
+ if (gToolMgr)
+ {
+ gToolMgr->onAppFocusGained();
+ }
+
+ gShowTextEditCursor = TRUE;
+
+ // See if we're coming in with modifier keys held down
+ if (gKeyboard)
+ {
+ gKeyboard->resetMaskKeys();
+ }
+}
+
+// The top-level window has lost focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocusLost(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(FALSE);
+ //LLModalDialog::onAppFocusLost();
+ if( gToolMgr )
+ {
+ gToolMgr->onAppFocusLost();
+ }
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ // restore mouse cursor
+ gViewerWindow->showCursor();
+ gViewerWindow->getWindow()->setMouseClipping(FALSE);
+
+ // JC - Leave keyboard focus, so if you're popping in and out editing
+ // a script, you don't have to click in the editor again and again.
+ // gFocusMgr.setKeyboardFocus( NULL, NULL );
+ gShowTextEditCursor = FALSE;
+
+ // If losing focus while keys are down, reset them.
+ if (gKeyboard)
+ {
+ gKeyboard->resetKeys();
+ }
+}
+
+
+BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
+{
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ // HACK: We want to interpret KEY_RETURN later when it arrives as a Unicode char,
+ // not as a keydown. Otherwise when client frame rate is really low, hitting
+ // return sends your chat text before it's all entered/processed.
+ if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ return FALSE;
+ }
+
+ return gViewerKeyboard.handleKey(key, mask, repeated);
+}
+
+BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+ return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+}
+
+
+
+
+BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
+{
+ if (activated)
+ {
+ mActive = TRUE;
+ send_agent_resume();
+ gAgent.clearAFK();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ if (!gQuit)
+ {
+ if (gStartupState >= STATE_STARTED)
+ {
+ // if we're in world, show a progress bar to hide reloading of textures
+ llinfos << "Restoring GL during activate" << llendl;
+ restoreGL("Restoring...");
+ }
+ else
+ {
+ // otherwise restore immediately
+ restoreGL();
+ }
+ }
+ else
+ {
+ llwarns << "Activating while quitting" << llendl;
+ }
+ }
+
+ // Unmute audio
+ if (!gSavedSettings.getBOOL("MuteAudio"))
+ {
+ if (gAudiop) gAudiop->setMuted(FALSE);
+ F32 volume = gSavedSettings.getF32("MediaAudioVolume");
+ if(LLMediaEngine::getInstance())
+ {
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ }
+ else
+ {
+ mActive = FALSE;
+ gAgent.setAFK();
+ send_agent_pause();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ llinfos << "Stopping GL during deactivation" << llendl;
+ stopGL();
+ }
+ // Mute audio
+ if (gSavedSettings.getBOOL("MuteWhenMinimized"))
+ {
+ llinfos << "Muting audio on minimize" << llendl;
+ if (gAudiop) gAudiop->setMuted(TRUE);
+ F32 volume = 0.f;
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ return TRUE;
+}
+
+
+void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item)
+{
+}
+
+
+BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height)
+{
+#if LL_WINDOWS
+ if (gNoRender)
+ {
+ HWND window_handle = llwindow_get_hwnd(window);
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ RECT wnd_rect;
+ wnd_rect.left = 0;
+ wnd_rect.top = 0;
+ wnd_rect.bottom = 200;
+ wnd_rect.right = 500;
+
+ hdc = BeginPaint(window_handle, &ps);
+ //SetBKColor(hdc, RGB(255, 255, 255));
+ FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255)));
+
+ LLString name_str;
+ gAgent.getName(name_str);
+
+ S32 len;
+ char temp_str[255];
+ sprintf(temp_str, "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f",
+ name_str.c_str(),
+ gViewerStats->mFPSStat.getMeanPerSec(),
+ gViewerStats->mSimPhysicsFPS.getPrev(0),
+ gViewerStats->mSimTimeDilation.getPrev(0));
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 0, temp_str, len);
+
+
+ LLVector3d pos_global = gAgent.getPositionGlobal();
+ sprintf(temp_str, "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]);
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 25, temp_str, len);
+
+ TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61);
+ EndPaint(window_handle, &ps);
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks)
+{
+ gViewerWindow->handleScrollWheel( clicks );
+}
+
+void LLViewerWindow::handleWindowBlock(LLWindow *window)
+{
+ send_agent_pause();
+}
+
+void LLViewerWindow::handleWindowUnblock(LLWindow *window)
+{
+ send_agent_resume();
+}
+
+void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data)
+{
+ switch (data_type)
+ {
+ case 0:
+ // received URL
+ if (LLURLSimString::unpack_data(data))
+ {
+ if(gFloaterWorldMap)
+ {
+ gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
+ LLURLSimString::sInstance.mX,
+ LLURLSimString::sInstance.mY,
+ LLURLSimString::sInstance.mZ);
+
+ LLFloaterWorldMap::show(NULL, TRUE);
+ }
+
+ // bring window to foreground, as it has just been "launched" from a URL
+ mWindow->bringToFront();
+ }
+ break;
+ }
+}
+
+
+//
+// Classes
+//
+LLViewerWindow::LLViewerWindow(
+ char* title, char* name,
+ S32 x, S32 y,
+ S32 width, S32 height,
+ BOOL fullscreen, BOOL ignore_pixel_depth)
+ :
+ mActive(TRUE),
+ mWantFullscreen(fullscreen),
+ mShowFullscreenProgress(FALSE),
+ mWindowRect(0, height, width, 0),
+ mVirtualWindowRect(0, height, width, 0),
+ mLeftMouseDown(FALSE),
+ mRightMouseDown(FALSE),
+ mToolTip(NULL),
+ mToolTipBlocked(FALSE),
+ mMouseInWindow( FALSE ),
+ mLastMask( MASK_NONE ),
+ mToolStored( NULL ),
+ mSuppressToolbox( FALSE ),
+ mHideCursorPermanent( FALSE ),
+ mPickPending(FALSE),
+ mIgnoreActivate( FALSE ),
+ mRenderFullFrame(FALSE)
+{
+ // Default to application directory.
+ strcpy(LLViewerWindow::sSnapshotBaseName, "Snapshot");
+ strcpy(LLViewerWindow::sMovieBaseName, "SLmovie");
+ LLViewerWindow::sSnapshotDir[0] = '\0';
+
+ mFastFrameTimer.stop();
+
+ // create window
+ mWindow = LLWindowManager::createWindow(
+ title, name, x, y, width, height, 0,
+ fullscreen,
+ gNoRender,
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ !gNoRender,
+ ignore_pixel_depth);
+#if LL_WINDOWS
+ if (!LLWinDebug::setupExceptionHandler())
+ {
+ llwarns << " Someone took over my exception handler (post createWindow)!" << llendl;
+ }
+#endif
+
+ if (NULL == mWindow)
+ {
+ LLSplashScreen::update("Shutting down...");
+#if LL_LINUX
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color and your graphics driver is configured correctly. See README-linux.txt for further information."
+ << llendl;
+#else
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings"
+ << llendl;
+#endif
+ app_force_exit(1);
+ }
+
+ // Get the real window rect the window was created with (since there are various OS-dependent reasons why
+ // the size of a window or fullscreen context may have been adjusted slightly...)
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+
+ mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ mDisplayScale *= ui_scale_factor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ {
+ LLCoordWindow size;
+ mWindow->getSize(&size);
+ mWindowRect.set(0, size.mY, size.mX, 0);
+ mVirtualWindowRect.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0);
+ }
+
+ LLFontManager::initClass();
+
+ //
+ // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off
+ // stuff like AGP if we think that it'll crash the viewer.
+ //
+ gFeatureManagerp->initGraphicsFeatureMasks();
+ if (gFeatureManagerp->isSafe()
+ || (gSavedSettings.getS32("LastFeatureVersion") != gFeatureManagerp->getVersion()))
+ {
+ gFeatureManagerp->applyRecommendedFeatures();
+ }
+
+ S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
+ // -1 indicates use default (max)
+ if (idx == -1)
+ {
+ idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
+ gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
+ }
+
+ if (!gNoRender)
+ {
+ //
+ // Initialize GL stuff
+ //
+
+ gPipeline.init();
+ stop_glerror();
+ initGLDefaults();
+ LLViewerImage::initClass();
+ }
+
+ //
+ // Done initing GL stuff.
+ //
+
+ // set callbacks
+ mWindow->setCallbacks(this);
+
+ // Init the image list. Must happen after GL is initialized and before the images that
+ // LLViewerWindow needs are requested.
+ gImageList.init();
+ gBumpImageList.init();
+
+ // Create container for all sub-views
+ mRootView = new LLRootView("root", mVirtualWindowRect, FALSE);
+ mRootView->setRenderInFastFrame(FALSE);
+
+ if (!gNoRender)
+ {
+ // Init default fonts
+ initFonts();
+ }
+
+ // Init Resource Manager
+ gResMgr = new LLResMgr();
+
+ // Make avatar head look forward at start
+ mCurrentMousePoint.mX = getWindowWidth() / 2;
+ mCurrentMousePoint.mY = getWindowHeight() / 2;
+
+ mPickBuffer = new U8[PICK_DIAMETER * PICK_DIAMETER * 4];
+
+ gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle");
+ mOverlayTitle = gSavedSettings.getString("OverlayTitle");
+ // Can't have spaces in settings.ini strings, so use underscores instead and convert them.
+ LLString::replaceChar(mOverlayTitle, '_', ' ');
+
+ gAwayTimer.stop();
+
+ LLAlertDialog::setDisplayCallback(alertCallback); // call this before calling any modal dialogs
+
+ // sync the keyboard's setting with the saved setting
+ gSavedSettings.getControl("NumpadControl")->firePropertyChanged();
+}
+
+void LLViewerWindow::initGLDefaults()
+{
+ //LLGLState::reset();
+ //gGLSDefault.set();
+ //LLGLState::verify(TRUE);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
+
+ F32 ambient[4] = {0.f,0.f,0.f,0.f };
+ F32 diffuse[4] = {1.f,1.f,1.f,1.f };
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);
+
+ glPixelStorei(GL_PACK_ALIGNMENT,1);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+
+ // lights for objects
+ glShadeModel( GL_SMOOTH );
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+
+ glCullFace(GL_BACK);
+
+ // RN: Need this for translation and stretch manip.
+ gCone.prerender();
+ gBox.prerender();
+ gSphere.prerender();
+ gCylinder.prerender();
+
+ LLVOAvatar::initVertexPrograms();
+}
+
+void LLViewerWindow::initBase()
+{
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ LLRect full_window(0, height, width, 0);
+
+ adjustRectanglesForFirstUse(full_window);
+
+ ////////////////////
+ //
+ // Set the gamma
+ //
+
+ F32 gamma = gSavedSettings.getF32("RenderGamma");
+ if (gamma != 0.0f)
+ {
+ gViewerWindow->getWindow()->setGamma(gamma);
+ }
+
+ // Create global views
+
+ // Create the floater view at the start so that other views can add children to it.
+ // (But wait to add it as a child of the root view so that it will be in front of the
+ // other views.)
+
+ // Constrain floaters to inside the menu and status bar regions.
+ LLRect floater_view_rect = full_window;
+ // make space for menu bar if we have one
+ floater_view_rect.mTop -= MENU_BAR_HEIGHT;
+ floater_view_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 2;
+
+ // Check for non-first startup
+ S32 floater_view_bottom = gSavedSettings.getS32("FloaterViewBottom");
+ if (floater_view_bottom >= 0)
+ {
+ floater_view_rect.mBottom = floater_view_bottom;
+ }
+ gFloaterView = new LLFloaterView("Floater View", floater_view_rect );
+ gFloaterView->setVisible(TRUE);
+
+ gSnapshotFloaterView = new LLSnapshotFloaterView("Snapshot Floater View", full_window);
+ gSnapshotFloaterView->setVisible(TRUE);
+
+ // Console
+ llassert( !gConsole );
+ LLRect console_rect = full_window;
+ console_rect.mTop -= 24;
+ console_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 12;
+ console_rect.mLeft += 24; //gSavedSettings.getS32("StatusBarButtonWidth") + gSavedSettings.getS32("StatusBarPad");
+
+ if (gSavedSettings.getBOOL("ChatFullWidth"))
+ {
+ console_rect.mRight -= 10;
+ }
+ else
+ {
+ // Make console rect somewhat narrow so having inventory open is
+ // less of a problem.
+ console_rect.mRight = console_rect.mLeft + 2 * width / 3;
+ }
+
+ gConsole = new LLConsole(
+ "console",
+ gSavedSettings.getS32("ConsoleBufferSize"),
+ console_rect,
+ gSavedSettings.getS32("ChatFontSize"),
+ gSavedSettings.getF32("ChatPersistTime") );
+ gConsole->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ mRootView->addChild(gConsole);
+
+ // Debug view over the console
+ gDebugView = new LLDebugView("gDebugView", full_window);
+ gDebugView->setFollowsAll();
+ gDebugView->setVisible(TRUE);
+ mRootView->addChild(gDebugView);
+
+ // HUD elements just below floaters
+ LLRect hud_rect = full_window;
+ hud_rect.mTop -= 24;
+ hud_rect.mBottom += STATUS_BAR_HEIGHT;
+ gHUDView = new LLHUDView("hud_view", hud_rect);
+ gHUDView->setFollowsAll();
+ mRootView->addChild(gHUDView);
+
+ // Add floater view at the end so it will be on top, and give it tab priority over others
+ mRootView->addChild(gFloaterView, -1);
+ mRootView->addChild(gSnapshotFloaterView);
+
+ // notify above floaters!
+ LLRect notify_rect = full_window;
+ //notify_rect.mTop -= 24;
+ notify_rect.mBottom += STATUS_BAR_HEIGHT;
+ gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL);
+ mRootView->addChild(gNotifyBoxView, -2);
+
+ // Tooltips go above floaters
+ mToolTip = new LLTextBox( "tool tip", LLRect(0, 1, 1, 0 ) );
+ mToolTip->setHPad( 4 );
+ mToolTip->setVPad( 2 );
+ mToolTip->setColor( gColors.getColor( "ToolTipTextColor" ) );
+ mToolTip->setBorderColor( gColors.getColor( "ToolTipBorderColor" ) );
+ mToolTip->setBorderVisible( FALSE );
+ mToolTip->setBackgroundColor( gColors.getColor( "ToolTipBgColor" ) );
+ mToolTip->setBackgroundVisible( TRUE );
+ mToolTip->setDropshadowVisible( FALSE );
+ mToolTip->setBorderDropshadowVisible( TRUE );
+ mToolTip->setVisible( FALSE );
+
+ // Add the progress bar view (startup view), which overrides everything
+ mProgressView = new LLProgressView("ProgressView", full_window);
+ mRootView->addChild(mProgressView);
+ setShowProgress(FALSE);
+ setProgressCancelButtonVisible(FALSE, "");
+}
+
+
+void adjust_rect_top_left(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(0, window.getHeight(), r.getWidth(), r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_top_right(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(window.getWidth() - r.getWidth(),
+ window.getHeight(),
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_bottom_center(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth()/2 - r.getWidth()/2,
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+// Many rectangles can't be placed until we know the screen size.
+// These rectangles have their bottom-left corner as 0,0
+void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window)
+{
+ LLRect r;
+
+ adjust_rect_bottom_center("FloaterMoveRect", window);
+
+ adjust_rect_bottom_center("FloaterCameraRect", window);
+
+ adjust_rect_top_left("FloaterCustomizeAppearanceRect", window);
+
+ adjust_rect_top_left("FloaterLandRect5", window);
+
+ adjust_rect_top_left("FloaterFindRect2", window);
+
+ adjust_rect_top_left("FloaterGestureRect", window);
+
+ adjust_rect_top_right("FloaterMapRect", window);
+
+ adjust_rect_top_left("FloaterBuildOptionsRect", window);
+
+ // bottom-right
+ r = gSavedSettings.getRect("FloaterInventoryRect");
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth() - r.getWidth(),
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect("FloaterInventoryRect", r);
+ }
+}
+
+
+void LLViewerWindow::initWorldUI()
+{
+ pre_init_menus();
+
+ S32 height = mRootView->getRect().getHeight();
+ S32 width = mRootView->getRect().getWidth();
+ LLRect full_window(0, height, width, 0);
+
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, width+1, -1);
+ gToolBar = new LLToolBar("toolbar", bar_rect);
+
+ LLRect chat_bar_rect(-1,CHAT_BAR_HEIGHT, width+1, -1);
+ chat_bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ gChatBar = new LLChatBar("chat", chat_bar_rect);
+
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ gOverlayBar = new LLOverlayBar("overlay", bar_rect);
+
+ // panel containing chatbar, toolbar, and overlay, over floaters
+ LLRect bottom_rect(-1, 2*STATUS_BAR_HEIGHT + CHAT_BAR_HEIGHT, width+1, -1);
+ gBottomPanel = new LLBottomPanel("bottom panel", bottom_rect);
+
+ // the order here is important
+ gBottomPanel->addChild(gChatBar);
+ gBottomPanel->addChild(gToolBar);
+ gBottomPanel->addChild(gOverlayBar);
+ mRootView->addChild(gBottomPanel);
+
+ // View for hover information
+ gHoverView = new LLHoverView("gHoverView", full_window);
+ gHoverView->setVisible(TRUE);
+ mRootView->addChild(gHoverView);
+
+ //
+ // Map
+ //
+ // TODO: Move instance management into class
+ gFloaterMap = new LLFloaterMap("Map");
+ gFloaterMap->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
+ gFloaterMap->setVisible( gSavedSettings.getBOOL("ShowMiniMap") );
+
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(gFloaterMap, FALSE);
+
+ if (gSavedSettings.getBOOL("ShowCameraControls"))
+ {
+ LLFloaterCamera::show(NULL);
+ }
+
+ if (gSavedSettings.getBOOL("ShowMovementControls"))
+ {
+ LLFloaterMove::show(NULL);
+ }
+
+ // Must have one global chat floater so it can actually store
+ // the history. JC
+ gFloaterChat = new LLFloaterChat();
+ gFloaterChat->setVisible( FALSE );
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory();
+
+ gIMView = new LLIMView("gIMView", LLRect() );
+ gIMView->setFollowsAll();
+
+ LLRect morph_view_rect = full_window;
+ morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
+ morph_view_rect.mTop = full_window.mTop - 32;
+ gMorphView = new LLMorphView("gMorphView", morph_view_rect );
+ mRootView->addChild(gMorphView);
+ gMorphView->setVisible(FALSE);
+
+ gFloaterMute = new LLFloaterMute();
+ gFloaterMute->setVisible(FALSE);
+
+ LLWorldMapView::initClass();
+
+ LLRect world_map_rect = gSavedSettings.getRect("FloaterWorldMapRect");
+ // if 0,0,0,0 then use fullscreen
+ if (world_map_rect.mTop == 0
+ && world_map_rect.mLeft == 0
+ && world_map_rect.mRight == 0
+ && world_map_rect.mBottom == 0)
+ {
+ world_map_rect.set(0, height-TOOL_BAR_HEIGHT, width, STATUS_BAR_HEIGHT);
+ world_map_rect.stretch(-4);
+ gSavedSettings.setRect("FloaterWorldMapRect", world_map_rect);
+ }
+ gFloaterWorldMap = new LLFloaterWorldMap();
+ gFloaterWorldMap->setVisible(FALSE);
+
+ //
+ // Tools for building
+ //
+
+ // Toolbox floater
+ init_menus();
+
+ gFloaterTools = new LLFloaterTools();
+ gFloaterTools->setVisible(FALSE);
+
+ // Status bar
+ //FIXME change this back
+ S32 menu_bar_height = gMenuBarView->getRect().getHeight();
+ LLRect root_rect = gViewerWindow->getRootView()->getRect();
+ LLRect status_rect(0, root_rect.getHeight(), root_rect.getWidth(), root_rect.getHeight() - menu_bar_height);
+ gStatusBar = new LLStatusBar("status", status_rect);
+ gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ gStatusBar->reshape(root_rect.getWidth(), gStatusBar->getRect().getHeight(), TRUE);
+ gStatusBar->translate(0, root_rect.getHeight() - gStatusBar->getRect().getHeight());
+
+ gViewerWindow->getRootView()->addChild(gStatusBar);
+}
+
+
+LLViewerWindow::~LLViewerWindow()
+{
+ gSavedSettings.setS32("FloaterViewBottom", gFloaterView->getRect().mBottom);
+
+ // Cleanup global views
+ if (gMorphView)
+ {
+ gMorphView->setVisible(FALSE);
+ }
+
+ // Delete all child views.
+ delete mRootView;
+ mRootView = NULL;
+
+ // Automatically deleted as children of mRootView. Fix the globals.
+ gFloaterTools = NULL;
+ gStatusBar = NULL;
+ gFloaterCamera = NULL;
+ gIMView = NULL;
+ gHoverView = NULL;
+
+ gFloaterView = NULL;
+ gMorphView = NULL;
+
+ gFloaterChat = NULL;
+ gFloaterMute = NULL;
+
+ gFloaterMap = NULL;
+ gHUDView = NULL;
+
+ gNotifyBoxView = NULL;
+
+ delete mToolTip;
+ mToolTip = NULL;
+
+ delete gResMgr;
+ gResMgr = NULL;
+
+ //--------------------------------------------------------
+ // Shutdown GL cleanly. Order is very important here.
+ //--------------------------------------------------------
+ LLFontGL::destroyDefaultFonts();
+ LLFontManager::cleanupClass();
+ stop_glerror();
+
+ gSky.cleanup();
+ stop_glerror();
+
+ gImageList.shutdown();
+ stop_glerror();
+
+ gBumpImageList.shutdown();
+ stop_glerror();
+
+ LLWorldMapView::cleanupTextures();
+
+ LLViewerImage::cleanupClass();
+
+ delete[] mPickBuffer;
+ mPickBuffer = NULL;
+
+ llinfos << "Stopping GL during shutdown" << llendl;
+ if (!gNoRender)
+ {
+ stopGL(FALSE);
+ stop_glerror();
+ }
+
+ if (gSelectMgr)
+ {
+ llinfos << "Cleaning up select manager" << llendl;
+ gSelectMgr->cleanup();
+ }
+
+ llinfos << "Cleaning up pipeline" << llendl;
+ gPipeline.cleanup();
+ stop_glerror();
+ llinfos << "Destroying Window" << llendl;
+ destroyWindow();
+}
+
+
+void LLViewerWindow::setCursor( ECursorType c )
+{
+ mWindow->setCursor( c );
+}
+
+void LLViewerWindow::showCursor()
+{
+ mWindow->showCursor();
+}
+
+void LLViewerWindow::hideCursor()
+{
+ // Hide tooltips
+ if(mToolTip ) mToolTip->setVisible( FALSE );
+
+ // Also hide hover info
+ if (gHoverView) gHoverView->cancelHover();
+
+ // And hide the cursor
+ mWindow->hideCursor();
+}
+
+void LLViewerWindow::sendShapeToSim()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ if(!msg) return;
+ msg->newMessageFast(_PREHASH_AgentHeightWidth);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->nextBlockFast(_PREHASH_HeightWidthBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ U16 height16 = (U16) mWindowRect.getHeight();
+ U16 width16 = (U16) mWindowRect.getWidth();
+ msg->addU16Fast(_PREHASH_Height, height16);
+ msg->addU16Fast(_PREHASH_Width, width16);
+ gAgent.sendReliableMessage();
+}
+
+// Must be called after window is created to set up agent
+// camera variables and UI variables.
+void LLViewerWindow::reshape(S32 width, S32 height)
+{
+ // Destroying the window at quit time generates spurious
+ // reshape messages. We don't care about these, and we
+ // don't want to send messages because the message system
+ // may have been destructed.
+ if (!gQuit)
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ glViewport(0, 0, width, height );
+
+ if (height > 0 && gCamera)
+ {
+ gCamera->setViewHeightInPixels( height );
+ if (mWindow->getFullscreen())
+ {
+ // force to 4:3 aspect for odd resolutions
+ gCamera->setAspect( getDisplayAspectRatio() );
+ }
+ else
+ {
+ gCamera->setAspect( width / (F32) height);
+ }
+ }
+
+ // update our window rectangle
+ mWindowRect.mRight = mWindowRect.mLeft + width;
+ mWindowRect.mTop = mWindowRect.mBottom + height;
+ calcDisplayScale();
+
+ BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ // update our window rectangle
+ mVirtualWindowRect.mRight = mVirtualWindowRect.mLeft + llround((F32)width / mDisplayScale.mV[VX]);
+ mVirtualWindowRect.mTop = mVirtualWindowRect.mBottom + llround((F32)height / mDisplayScale.mV[VY]);
+
+ setupViewport();
+
+ // Inform lower views of the change
+ // round up when converting coordinates to make sure there are no gaps at edge of window
+ LLView::sForceReshape = display_scale_changed;
+ mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY]));
+ LLView::sForceReshape = FALSE;
+
+ // clear font width caches
+ if (display_scale_changed)
+ {
+ LLHUDText::reshape();
+ }
+
+ sendShapeToSim();
+
+
+ // store the mode the user wants (even if not there yet)
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ // store new settings for the mode we are in, regardless
+ if (mWindow->getFullscreen())
+ {
+ gSavedSettings.setS32("FullScreenWidth", width);
+ gSavedSettings.setS32("FullScreenHeight", height);
+ }
+ else
+ {
+ // Only save size if not maximized
+ BOOL maximized = mWindow->getMaximized();
+ gSavedSettings.setBOOL("WindowMaximized", maximized);
+
+ LLCoordScreen window_size;
+ if (!maximized
+ && mWindow->getSize(&window_size))
+ {
+ gSavedSettings.setS32("WindowWidth", window_size.mX);
+ gSavedSettings.setS32("WindowHeight", window_size.mY);
+ }
+ }
+
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
+ }
+}
+
+
+void LLViewerWindow::draw()
+{
+#if LL_DEBUG
+ LLView::sIsDrawing = TRUE;
+#endif
+ stop_glerror();
+
+ LLUI::setLineWidth(1.f);
+ //popup alerts from the UI
+ LLAlertInfo alert;
+ while (LLPanel::nextAlert(alert))
+ {
+ alertXml(alert.mLabel, alert.mArgs);
+ }
+
+ LLUI::setLineWidth(1.f);
+ // Reset any left-over transforms
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+
+ //S32 screen_x, screen_y;
+
+ // HACK for timecode debugging
+ if (gSavedSettings.getBOOL("DisplayTimecode"))
+ {
+ // draw timecode block
+ char text[256];
+
+ glLoadIdentity();
+
+ microsecondsToTimecodeString(gFrameTime,text);
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ font->renderUTF8(text, 0,
+ llround((getWindowWidth()/2)-100.f),
+ llround((getWindowHeight()-60.f)),
+ LLColor4( 1.f, 1.f, 1.f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+
+ glPushMatrix();
+ {
+ // scale view by UI global scale factor and aspect ratio correction factor
+ glScalef(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f);
+
+ LLVector2 old_scale_factor = LLUI::sGLScaleFactor;
+ if (gCamera)
+ {
+ // apply camera zoom transform (for high res screenshots)
+ F32 zoom_factor = gCamera->getZoomFactor();
+ S16 sub_region = gCamera->getZoomSubRegion();
+ if (zoom_factor > 1.f)
+ {
+ //decompose subregion number to x and y values
+ int pos_y = sub_region / llceil(zoom_factor);
+ int pos_x = sub_region - (pos_y*llceil(zoom_factor));
+ // offset for this tile
+ glTranslatef((F32)gViewerWindow->getWindowWidth() * -(F32)pos_x,
+ (F32)gViewerWindow->getWindowHeight() * -(F32)pos_y,
+ 0.f);
+ glScalef(zoom_factor, zoom_factor, 1.f);
+ LLUI::sGLScaleFactor *= zoom_factor;
+ }
+ }
+
+ {
+ LLGLSTexture gls_texture;
+ show_text_gl();
+ }
+
+ if (gToolMgr)
+ {
+ // Draw tool specific overlay on world
+ gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->draw();
+ }
+
+ if( gAgent.cameraMouselook() )
+ {
+ drawMouselookInstructions();
+ stop_glerror();
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+ mRootView->draw();
+
+ // Draw optional on-top-of-everyone view
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->getVisible())
+ {
+ S32 screen_x, screen_y;
+ top_view->localPointToScreen(0, 0, &screen_x, &screen_y);
+
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f);
+ top_view->draw();
+ LLUI::popMatrix();
+ }
+
+ // Draw tooltips
+ // Adjust their rectangle so they don't go off the top or bottom
+ // of the screen.
+ if( mToolTip && mToolTip->getVisible() )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ S32 tip_height = mToolTip->getRect().getHeight();
+
+ S32 screen_x, screen_y;
+ mToolTip->localPointToScreen(0, -24 - tip_height,
+ &screen_x, &screen_y);
+
+ // If tooltip would draw off the bottom of the screen,
+ // show it from the cursor tip position.
+ if (screen_y < tip_height)
+ {
+ mToolTip->localPointToScreen(0, 0, &screen_x, &screen_y);
+ }
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0);
+ mToolTip->draw();
+ }
+ LLUI::popMatrix();
+ }
+
+ if( gShowOverlayTitle && !mOverlayTitle.empty() )
+ {
+ // Used for special titles such as "Second Life - Special E3 2003 Beta"
+ const S32 DIST_FROM_TOP = 20;
+ LLGLSTexture gls_texture;
+ LLFontGL::sSansSerifBig->renderUTF8(
+ mOverlayTitle, 0,
+ llround( gViewerWindow->getWindowWidth() * 0.5f),
+ gViewerWindow->getWindowHeight() - DIST_FROM_TOP,
+ LLColor4(1, 1, 1, 0.4f),
+ LLFontGL::HCENTER, LLFontGL::TOP);
+ }
+
+ LLUI::sGLScaleFactor = old_scale_factor;
+ }
+ glPopMatrix();
+
+
+#if LL_DEBUG
+ LLView::sIsDrawing = FALSE;
+#endif
+}
+
+// Takes a single keydown event, usually when UI is visible
+BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
+{
+ if (gFocusMgr.getKeyboardFocus() && !(mask & (MASK_CONTROL | MASK_ALT)))
+ {
+ // We have keyboard focus, and it's not an accelerator
+
+ if (key < 0x80)
+ {
+ // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first.
+ return gFocusMgr.childHasKeyboardFocus(mRootView);
+ }
+ }
+
+ // HACK look for UI editing keys
+ if (LLView::sEditingUI)
+ {
+ if (LLFloaterEditUI::handleKey(key, mask))
+ {
+ return TRUE;
+ }
+ }
+
+ // Hide tooltips on keypress
+ if(mToolTip )
+ {
+ mToolTipBlocked = TRUE; // block until next time mouse is moved
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on keypress
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+
+ gHoverView->setTyping(TRUE);
+ }
+
+ // Explicit hack for debug menu.
+ if ((MASK_ALT & mask) &&
+ (MASK_CONTROL & mask) &&
+ ('D' == key || 'd' == key))
+ {
+ toggle_debug_menus(NULL);
+ }
+
+ // Example "bug" for bug reporter web page
+ if ((MASK_SHIFT & mask)
+ && (MASK_ALT & mask)
+ && (MASK_CONTROL & mask)
+ && ('H' == key || 'h' == key))
+ {
+ trigger_hippo_bug(NULL);
+ }
+
+ // handle escape key
+ if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ if (gMenuHolder && gMenuHolder->hideMenus())
+ {
+ return TRUE;
+ }
+ //FIXME: get this to play well with mouselook and hidden cursor modes, etc.
+ //if (gFocusMgr.getMouseCapture())
+ //{
+ // gFocusMgr.setMouseCapture(NULL, NULL);
+ // return TRUE;
+ //}
+ }
+
+ // let menus handle navigation keys
+ if (gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE))
+ {
+ return TRUE;
+ }
+
+ // Traverses up the hierarchy
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ // arrow keys move avatar while chatting hack
+ if (gChatBar && gChatBar->inputEditorHasFocus())
+ {
+ if (gChatBar->getCurrentChat().empty() || gSavedSettings.getBOOL("ArrowKeysMoveAvatar"))
+ {
+ switch(key)
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGE_UP:
+ case KEY_PAGE_DOWN:
+ case KEY_HOME:
+ // when chatbar is empty or ArrowKeysMoveAvatar set, pass arrow keys on to avatar...
+ return FALSE;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (keyboard_focus->handleKey(key, mask, FALSE))
+ {
+ return TRUE;
+ }
+ }
+
+ if (gToolMgr)
+ {
+ if( gToolMgr->getCurrentTool(mask)->handleKey(key, mask) )
+ {
+ return TRUE;
+ }
+ }
+
+ // Try for a new-format gesture
+ if (gGestureManager.triggerGesture(key, mask))
+ {
+ return TRUE;
+ }
+
+ // See if this is a gesture trigger. If so, eat the key and
+ // don't pass it down to the menus.
+ if (gGestureList.trigger(key, mask))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ //FIXME: get rid of this?
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ if( top_view->handleKey( key, mask, TRUE ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // give floaters first chance to handle TAB key
+ // so frontmost floater gets focus
+ if (key == KEY_TAB)
+ {
+ // if nothing has focus, go to first or last UI element as appropriate
+ if (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ if (gMenuHolder) gMenuHolder->hideMenus();
+
+ // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
+ gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0);
+
+ // do CTRL-TAB and CTRL-SHIFT-TAB logic
+ if (mask & MASK_SHIFT)
+ {
+ mRootView->focusPrevRoot();
+ }
+ else
+ {
+ mRootView->focusNextRoot();
+ }
+ return TRUE;
+ }
+ }
+
+ // give menus a chance to handle keys
+ if (gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
+ {
+ return TRUE;
+ }
+
+ // don't pass keys on to world when something in ui has focus
+ return gFocusMgr.childHasKeyboardFocus(mRootView);
+}
+
+
+BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
+{
+ // HACK: We delay processing of return keys until they arrive as a Unicode char,
+ // so that if you're typing chat text at low frame rate, we don't send the chat
+ // until all keystrokes have been entered. JC
+ // HACK: Numeric keypad <enter> on Mac is Unicode 3
+ // HACK: Control-M on Windows is Unicode 13
+ if ((uni_char == 13 && mask != MASK_CONTROL)
+ || (uni_char == 3 && mask == MASK_NONE))
+ {
+ return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
+ }
+
+ // Traverses up the hierarchy
+ LLView* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ if (keyboard_focus->handleUnicodeChar(uni_char, FALSE))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->handleUnicodeChar( uni_char, FALSE ) )
+ {
+ return TRUE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(S32 clicks)
+{
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ mouse_captor->handleScrollWheel(local_x, local_y, clicks);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return;
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x;
+ S32 local_y;
+ top_view->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ if (top_view->handleScrollWheel(local_x, local_y, clicks)) return;
+ }
+
+ if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel not handled by view" << llendl;
+ }
+
+ if (gWorldPointer)
+ {
+ // Zoom the camera in and out behavior
+ gAgent.handleScrollWheel(clicks);
+ }
+
+ return;
+}
+
+void LLViewerWindow::moveCursorToCenter()
+{
+#if 1 // old version
+
+#if 0 // Dave's changes - this reportedly is making the drift worse on some systems?
+ S32 x = llround((F32) mVirtualWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mVirtualWindowRect.getHeight() / 2);
+#else
+ S32 x = mVirtualWindowRect.getWidth() / 2;
+ S32 y = mVirtualWindowRect.getHeight() / 2;
+#endif
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint.set(x,y);
+ mLastMousePoint.set(x,y);
+ mCurrentMouseDelta.set(0,0);
+
+ LLUI::setCursorPositionScreen(x, y);
+
+#else // Richard's version - fails on intel macs
+
+ S32 x = llround((F32) mWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mWindowRect.getHeight() / 2);
+
+ LLCoordWindow window_point;
+ mWindow->convertCoords(LLCoordGL(x, y), &window_point);
+ mWindow->setCursorPosition(window_point);
+
+ // read back cursor position
+ mWindow->getCursorPosition(&window_point);
+ LLCoordGL new_mouse_pos;
+ mWindow->convertCoords(window_point, &new_mouse_pos);
+ new_mouse_pos.mX = llround((F32)new_mouse_pos.mX / mDisplayScale.mV[VX]);
+ new_mouse_pos.mY = llround((F32)new_mouse_pos.mY / mDisplayScale.mV[VY]);
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint = new_mouse_pos;
+ mLastMousePoint = new_mouse_pos;
+ mCurrentMouseDelta.set(0,0);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Hover handlers
+//
+
+// Update UI based on stored mouse position from mouse-move
+// event processing.
+BOOL LLViewerWindow::handlePerFrameHover()
+{
+ static std::string last_handle_msg;
+
+ //RN: fix for asynchronous notification of mouse leaving window not working
+ LLCoordWindow mouse_pos;
+ mWindow->getCursorPosition(&mouse_pos);
+ if (mouse_pos.mX < 0 ||
+ mouse_pos.mY < 0 ||
+ mouse_pos.mX > mWindowRect.getWidth() ||
+ mouse_pos.mY > mWindowRect.getHeight())
+ {
+ mMouseInWindow = FALSE;
+ }
+ else
+ {
+ mMouseInWindow = TRUE;
+ }
+
+ S32 dx = mCurrentMousePoint.mX - mLastMousePoint.mX;
+ S32 dy = mCurrentMousePoint.mY - mLastMousePoint.mY;
+ mCurrentMouseDelta.set(dx,dy);
+ LLVector2 mouse_vel((F32)dx, (F32)dy);
+ mMouseVelocityStat.addValue(mouse_vel.magVec());
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ S32 x = mCurrentMousePoint.mX;
+ S32 y = mCurrentMousePoint.mY;
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ // clean up current focus
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus)
+ {
+ if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain())
+ {
+ gFocusMgr.releaseFocusIfNeeded(cur_focus);
+
+ LLView* parent = cur_focus->getParent();
+ LLView* focus_root = cur_focus->findRootMostFocusRoot();
+ while(parent)
+ {
+ if (parent->isCtrl() &&
+ (((LLUICtrl*)parent)->hasTabStop() || parent == focus_root) &&
+ !((LLUICtrl*)parent)->getIsChrome() &&
+ parent->isInVisibleChain() &&
+ parent->isInEnabledChain())
+ {
+ if (!parent->focusFirstItem())
+ {
+ ((LLUICtrl*)parent)->setFocus(TRUE);
+ }
+ break;
+ }
+ parent = parent->getParent();
+ }
+ }
+ else if (cur_focus->isFocusRoot())
+ {
+ // focus roots keep trying to delegate focus to their first valid descendant
+ // this assumes that focus roots are not valid focus holders on their own
+ cur_focus->focusFirstItem();
+ }
+ }
+
+ // Show joints while in edit mode and hold down alt key.
+ if (gHUDManager)
+ {
+ if (gSavedSettings.getBOOL("AltShowsPhysical")
+ || (gFloaterTools && gFloaterTools->getVisible()))
+ {
+ gHUDManager->toggleShowPhysical( mask & MASK_ALT );
+ }
+ }
+
+ BOOL handled = FALSE;
+
+ BOOL handled_by_top_view = FALSE;
+ LLView* top_view = gFocusMgr.getTopView();
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ // Pass hover events to object capturing mouse events.
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = mouse_captor->handleHover(local_x, local_y, mask);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl;
+ }
+ }
+ else
+ {
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleHover(local_x, local_y, mask);
+ handled_by_top_view = TRUE;
+ }
+
+ if ( !handled )
+ {
+ // x and y are from last time mouse was in window
+ // mMouseInWindow tracks *actual* mouse location
+ if (mMouseInWindow && mRootView->handleHover(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg)
+ {
+ last_handle_msg = LLView::sMouseHandlerMessage;
+ llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl;
+ }
+ LLView::sMouseHandlerMessage = "";
+ handled = TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ if (last_handle_msg != "")
+ {
+ last_handle_msg = "";
+ llinfos << "Hover not handled by view" << llendl;
+ }
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl;
+ }
+ }
+
+ //FIXME: sometimes tools handle the mouse as a captor, so this
+ // logic is a little confusing
+ LLTool *tool = NULL;
+ if (gToolMgr && gHoverView)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if(!handled && tool)
+ {
+ handled = tool->handleHover(x, y, mask);
+
+ if (!mWindow->isCursorHidden())
+ {
+ gHoverView->updateHover(tool);
+ }
+ }
+ else
+ {
+ // Cancel hovering if any UI element handled the event.
+ gHoverView->cancelHover();
+ }
+
+ // Suppress the toolbox view if our source tool was the pie tool,
+ // and we've overridden to something else.
+ mSuppressToolbox =
+ (gToolMgr->getCurrentTool(MASK_NONE) == gToolPie) &&
+ (gToolMgr->getCurrentTool(mask) != gToolPie);
+
+ }
+
+ //llinfos << (mToolTipBlocked ? "BLOCKED" : "NOT BLOCKED") << llendl;
+ // Show a new tool tip (or update one that is alrady shown)
+ BOOL tool_tip_handled = FALSE;
+ LLString tool_tip_msg;
+ F32 tooltip_delay = gSavedSettings.getF32( "ToolTipDelay" );
+ //HACK: hack for tool-based tooltips which need to pop up more quickly
+ //Also for show xui names as tooltips debug mode
+ if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames)
+ {
+ tooltip_delay = gSavedSettings.getF32( "DragAndDropToolTipDelay" );
+ }
+ if( handled &&
+ !mToolTipBlocked &&
+ (gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay) &&
+ !mWindow->isCursorHidden() )
+ {
+ LLRect screen_sticky_rect;
+
+ if (mouse_captor)
+ {
+ S32 local_x, local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = mouse_captor->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else if (handled_by_top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = top_view->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else
+ {
+ tool_tip_handled = mRootView->handleToolTip(x, y, tool_tip_msg, &screen_sticky_rect );
+ }
+
+ if( tool_tip_handled && !tool_tip_msg.empty() )
+ {
+ mToolTipStickyRect = screen_sticky_rect;
+ mToolTip->setWrappedText( tool_tip_msg, 200 );
+ mToolTip->reshapeToFitText();
+ mToolTip->setOrigin( x, y );
+ LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0);
+ mToolTip->translateIntoRect( virtual_window_rect, FALSE );
+ mToolTip->setVisible( TRUE );
+ }
+ }
+
+ if (tool != gToolNull && tool != gToolDragAndDrop && !gSavedSettings.getBOOL("FreezeTime"))
+ {
+ LLMouseHandler *captor = gFocusMgr.getMouseCapture();
+ // With the null tool or drag and drop tool, don't muck
+ // with visibility.
+
+ if (gFloaterTools->isMinimized() ||
+ (tool != gToolPie // not default tool
+ && tool != gToolGun // not coming out of mouselook
+ && !mSuppressToolbox // not override in third person
+ && gCurrentToolset != gFaceEditToolset // not special mode
+ && gCurrentToolset != gMouselookToolset
+ && (!captor || captor->isView())) // not dragging
+ )
+ {
+ // Force floater tools to be visible (unless minimized)
+ if (!gFloaterTools->isMinimized() && !gFloaterTools->getVisible())
+ {
+ gFloaterTools->open();
+ }
+ // Update the location of the blue box tool popup
+ LLCoordGL select_center_screen;
+ gFloaterTools->updatePopup( select_center_screen, mask );
+ }
+ else
+ {
+ gFloaterTools->setVisible(FALSE);
+ }
+ }
+ if (gToolBar)
+ {
+ gToolBar->refresh();
+ }
+
+ if (gChatBar)
+ {
+ gChatBar->refresh();
+ }
+
+ if (gOverlayBar)
+ {
+ gOverlayBar->refresh();
+ }
+
+ // Update rectangles for the various toolbars
+ if (gToolBar && gChatBar && gOverlayBar && gNotifyBoxView && gConsole)
+ {
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1);
+ if (gToolBar->getVisible())
+ {
+ gToolBar->setRect(bar_rect);
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ }
+
+ if (gChatBar->getVisible())
+ {
+ // fix up the height
+ LLRect chat_bar_rect = bar_rect;
+ chat_bar_rect.mTop = chat_bar_rect.mBottom + CHAT_BAR_HEIGHT + 1;
+ gChatBar->setRect(chat_bar_rect);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ }
+
+ LLRect notify_box_rect = gNotifyBoxView->getRect();
+ notify_box_rect.mBottom = bar_rect.mBottom;
+ gNotifyBoxView->reshape(notify_box_rect.getWidth(), notify_box_rect.getHeight());
+ gNotifyBoxView->setRect(notify_box_rect);
+
+ // make sure floaters snap to visible rect by adjusting floater view rect
+ LLRect floater_rect = gFloaterView->getRect();
+ if (floater_rect.mBottom != bar_rect.mBottom+1)
+ {
+ floater_rect.mBottom = bar_rect.mBottom+1;
+ // Don't bounce the floaters up and down.
+ gFloaterView->reshape(floater_rect.getWidth(), floater_rect.getHeight(),
+ TRUE, ADJUST_VERTICAL_NO);
+ gFloaterView->setRect(floater_rect);
+ }
+
+ if (gOverlayBar->getVisible())
+ {
+ LLRect overlay_rect = bar_rect;
+ overlay_rect.mTop = overlay_rect.mBottom + OVERLAY_BAR_HEIGHT;
+
+ // Fitt's Law: Push buttons flush with bottom of screen if
+ // nothing else visible.
+ if (!gToolBar->getVisible()
+ && !gChatBar->getVisible())
+ {
+ // FIXME: this is highly depenent on the XML describing the position of the buttons
+ overlay_rect.translate(0, 0);
+ }
+
+ gOverlayBar->setRect(overlay_rect);
+ gOverlayBar->updateRect();
+ bar_rect.translate(0, gOverlayBar->getRect().getHeight());
+
+ gFloaterView->setSnapOffsetBottom(OVERLAY_BAR_HEIGHT);
+ }
+ else
+ {
+ gFloaterView->setSnapOffsetBottom(0);
+ }
+
+ // fix rectangle of bottom panel focus indicator
+ if(gBottomPanel && gBottomPanel->getFocusIndicator())
+ {
+ LLRect focus_rect = gBottomPanel->getFocusIndicator()->getRect();
+ focus_rect.mTop = (gToolBar->getVisible() ? STATUS_BAR_HEIGHT : 0) +
+ (gChatBar->getVisible() ? CHAT_BAR_HEIGHT : 0) - 2;
+ gBottomPanel->getFocusIndicator()->setRect(focus_rect);
+ }
+
+ // Always update console
+ LLRect console_rect = gConsole->getRect();
+ console_rect.mBottom = bar_rect.mBottom + 8;
+ gConsole->reshape(console_rect.getWidth(), console_rect.getHeight());
+ gConsole->setRect(console_rect);
+ }
+
+ mLastMousePoint = mCurrentMousePoint;
+
+ // last ditch force of edit menu to selection manager
+ if (gEditMenuHandler == NULL && gSelectMgr && gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+
+ if (gFloaterView->getCycleMode())
+ {
+ // sync all floaters with their focus state
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0)
+ {
+ // control key no longer held down, finish cycle mode
+ gFloaterView->setCycleMode(FALSE);
+
+ gFloaterView->syncFloaterTabOrder();
+ }
+ else
+ {
+ // user holding down CTRL, don't update tab order of floaters
+ }
+ }
+ else
+ {
+ // update focused floater
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ // make sure floater visible order is in sync with tab order
+ gFloaterView->syncFloaterTabOrder();
+ }
+
+ if (gSavedSettings.getBOOL("ChatBarStealsFocus") && gChatBar && gFocusMgr.getKeyboardFocus() == NULL && gChatBar->getVisible())
+ {
+ gChatBar->startChat(NULL);
+ }
+
+ // sync land selection with edit and about land dialogs
+ if (gParcelMgr && !LLFloaterLand::floaterVisible() && (!gFloaterTools || !gFloaterTools->getVisible()))
+ {
+ gParcelMgr->deselectLand();
+ }
+
+ return handled;
+}
+
+
+void LLViewerWindow::saveLastMouse(const LLCoordGL &point)
+{
+ // Store last mouse location.
+ // If mouse leaves window, pretend last point was on edge of window
+ if (point.mX < 0)
+ {
+ mCurrentMousePoint.mX = 0;
+ }
+ else if (point.mX > getWindowWidth())
+ {
+ mCurrentMousePoint.mX = getWindowWidth();
+ }
+ else
+ {
+ mCurrentMousePoint.mX = point.mX;
+ }
+
+ if (point.mY < 0)
+ {
+ mCurrentMousePoint.mY = 0;
+ }
+ else if (point.mY > getWindowHeight() )
+ {
+ mCurrentMousePoint.mY = getWindowHeight();
+ }
+ else
+ {
+ mCurrentMousePoint.mY = point.mY;
+ }
+}
+
+
+// Draws the selection outlines for the currently selected objects
+// Must be called after displayObjects is called, which sets the mGLName parameter
+// NOTE: This function gets called 3 times:
+// render_ui_3d: FALSE, FALSE, TRUE
+// renderObjectsForSelect: TRUE, pick_parcel_wall, FALSE
+// render_hud_elements: FALSE, FALSE, FALSE
+void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud )
+{
+ LLViewerObject* object;
+
+ if (!for_hud && !for_gl_pick)
+ {
+ // Call this once and only once
+ gSelectMgr->updateSilhouettes();
+ }
+
+ // Draw fence around land selections
+ if (for_gl_pick)
+ {
+ if (pick_parcel_walls)
+ {
+ gParcelMgr->renderParcelCollision();
+ }
+ }
+ else if (( for_hud && gSelectMgr->getSelectType() == SELECT_TYPE_HUD) ||
+ (!for_hud && gSelectMgr->getSelectType() != SELECT_TYPE_HUD))
+ {
+ gSelectMgr->renderSilhouettes(for_hud);
+
+ stop_glerror();
+
+ // setup HUD render
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ LLBBox hud_bbox = gAgent.getAvatarObject()->getHUDBBox();
+
+ // set up transform to encompass bounding box of HUD
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+ glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+ glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
+ }
+
+ // Render light for editing
+ if (LLSelectMgr::sRenderLightRadius)
+ {
+ LLGLEnable gls_blend(GL_BLEND);
+ LLGLEnable gls_cull(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ LLDrawable* drawable = object->mDrawable;
+ if (drawable && drawable->isLight())
+ {
+ LLVOVolume* vovolume = drawable->getVOVolume();
+ glPushMatrix();
+
+ LLVector3 center = drawable->getPositionAgent();
+ glTranslatef(center[0], center[1], center[2]);
+ F32 scale = vovolume->getLightRadius();
+ glScalef(scale, scale, scale);
+
+ LLColor4 color(vovolume->getLightColor(), .5f);
+ glColor4fv(color.mV);
+
+ F32 pixel_area = 100000.f;
+ // Render Outside
+ gSphere.render(pixel_area);
+
+ // Render Inside
+ glCullFace(GL_FRONT);
+ gSphere.render(pixel_area);
+ glCullFace(GL_BACK);
+
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+ }
+
+ // NOTE: The average position for the axis arrows of the selected objects should
+ // not be recalculated at this time. If they are, then group rotations will break.
+
+ // Draw arrows at average center of all selected objects
+ LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool)
+ {
+ if(tool->isAlwaysRendered())
+ {
+ tool->render();
+ }
+ else
+ {
+ if( !gSelectMgr->isEmpty() )
+ {
+ BOOL moveable_object_selected = FALSE;
+ BOOL all_selected_objects_move = TRUE;
+ BOOL all_selected_objects_modify = TRUE;
+ BOOL selecting_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ BOOL this_object_movable = FALSE;
+ if (object->permMove() && (object->permModify() || selecting_linked_set))
+ {
+ moveable_object_selected = TRUE;
+ this_object_movable = TRUE;
+ }
+ all_selected_objects_move = all_selected_objects_move && this_object_movable;
+ all_selected_objects_modify = all_selected_objects_modify && object->permModify();
+ }
+
+ BOOL draw_handles = TRUE;
+
+ if (tool == gToolTranslate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if (tool == gToolRotate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if ( !all_selected_objects_modify && tool == gToolStretch )
+ {
+ draw_handles = FALSE;
+ }
+
+ if( draw_handles )
+ {
+ tool->render();
+ }
+ }
+ }
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ stop_glerror();
+ }
+ }
+ }
+}
+
+// Return a point near the clicked object representative of the place the object was clicked.
+LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const
+{
+ // create a normalized vector pointing from the camera center into the
+ // world at the location of the mouse click
+ LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot );
+
+ LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgent.getCameraPositionGlobal();
+
+ // make mouse vector as long as object vector, so it touchs a point near
+ // where the user clicked on the object
+ mouse_direction_global *= (F32) relative_object.magVec();
+
+ LLVector3d new_pos;
+ new_pos.setVec(mouse_direction_global);
+ // transform mouse vector back to world coords
+ new_pos += gAgent.getCameraPositionGlobal();
+
+ return new_pos;
+}
+
+
+BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const
+{
+ BOOL intersect = FALSE;
+
+// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK;
+ if (!intersect)
+ {
+ point_global = clickPointInWorldGlobal(x, y, objectp);
+ llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+ else
+ {
+ llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+
+ return intersect;
+}
+
+void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent, BOOL pick_parcel_walls)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+
+ F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
+ if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
+ {
+ // build mode allows interaction with all transparent objects
+ // "Show Debug Alpha" means no object actually transparent
+ pick_transparent = TRUE;
+ }
+ gPickTransparent = pick_transparent;
+
+ if (gPickTransparent)
+ {
+ gPickAlphaTargetThreshold = 0.f;
+ gPickAlphaThreshold = llmax(gPickAlphaTargetThreshold, gPickAlphaThreshold - (delta_time * 0.7f));
+ }
+ else
+ {
+ gPickAlphaTargetThreshold = 1.f;
+ gPickAlphaThreshold = llmin(gPickAlphaTargetThreshold, gPickAlphaThreshold + (delta_time * 0.7f));
+ }
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitPosGlobal.zeroVec();
+ gLastHitObjectOffset.zeroVec();
+ gLastHitObjectID.setNull();
+ gLastHitObjectFace = -1;
+
+ gLastHitNonFloraPosGlobal.zeroVec();
+ gLastHitNonFloraObjectOffset.zeroVec();
+ gLastHitNonFloraObjectID.setNull();
+ gLastHitNonFloraObjectFace = -1;
+
+ gLastHitParcelWall = FALSE;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build perspective transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ // make viewport big enough to handle antialiased frame buffers
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4, FALSE);
+ pick_camera.calcAgentFrustumPlanes(gCamera->mAgentFrustum);
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ gObjectList.renderObjectsForSelect(pick_camera, pick_parcel_walls);
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+
+ // delay further event processing until we receive results of pick
+ mWindow->delayInputProcessing();
+}
+
+void LLViewerWindow::hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ // Performs the GL UI pick.
+ // Stores its results in global, gLastHitUIElement
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitUIElementAsync(x, y, gKeyboard->currentMask(TRUE), NULL);
+ performPick();
+ if (callback)
+ {
+ callback(x, y, gKeyboard->currentMask(TRUE));
+ }
+}
+
+//RN: this currently doesn't do anything
+void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+// F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitUIElement = 0;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build orthogonal transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ gViewerWindow->setup2DRender();
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(x - (PICK_HALF_WIDTH + 2), y_from_bot - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ //gViewerWindow->drawForSelect();
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+}
+
+void LLViewerWindow::performPick()
+{
+ if (gNoRender || !mPickPending)
+ {
+ return;
+ }
+
+ finishFastFrame();
+
+ mPickPending = FALSE;
+ U32 te_offset = NO_FACE;
+
+ // find pick region that is fully onscreen
+ LLCoordGL scaled_pick_point = mPickPoint;
+ scaled_pick_point.mX = llclamp(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayWidth() - PICK_HALF_WIDTH);
+ scaled_pick_point.mY = llclamp(llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayHeight() - PICK_HALF_WIDTH);
+
+ glReadPixels(scaled_pick_point.mX - PICK_HALF_WIDTH, scaled_pick_point.mY - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+
+ S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH;
+ S32 name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+
+ if (name >= (S32)GL_NAME_UI_RESERVED && name < (S32)GL_NAME_INDEX_OFFSET)
+ {
+ // hit a UI element
+ gLastHitUIElement = name;
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+ }
+
+ //imdebug("rgba rbga=bbba b=8 w=%d h=%d %p", PICK_DIAMETER, PICK_DIAMETER, mPickBuffer);
+
+ S32 x_offset = mPickPoint.mX - llround((F32)scaled_pick_point.mX / mDisplayScale.mV[VX]);
+ S32 y_offset = mPickPoint.mY - llround((F32)scaled_pick_point.mY / mDisplayScale.mV[VY]);
+
+
+ // we hit nothing, scan surrounding pixels for something useful
+ if (!name)
+ {
+ S32 closest_distance = 10000;
+ //S32 closest_pick_name = 0;
+ for (S32 col = 0; col < PICK_DIAMETER; col++)
+ {
+ for (S32 row = 0; row < PICK_DIAMETER; row++)
+ {
+ S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH));
+ pixel_index = row * PICK_DIAMETER + col;
+ S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ if (test_name && distance_squared < closest_distance)
+ {
+ closest_distance = distance_squared;
+ name = test_name;
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ mPickOffset.mX = col - PICK_HALF_WIDTH;
+ mPickOffset.mY = row - PICK_HALF_WIDTH;
+ }
+ }
+ }
+ }
+
+ if (name)
+ {
+ mPickPoint.mX += llround((F32)mPickOffset.mX * mDisplayScale.mV[VX]);
+ mPickPoint.mY += llround((F32)mPickOffset.mY * mDisplayScale.mV[VY]);
+ }
+
+ if (gPickFaces)
+ {
+ te_offset = ((U32)name >> 20);
+ name &= 0x000fffff;
+ // don't clear gPickFaces, as we still need to check for UV coordinates
+ }
+
+ LLViewerObject *objectp = NULL;
+
+ // Frontmost non-foreground object that isn't trees or grass
+ LLViewerObject* nonflora_objectp = NULL;
+ S32 nonflora_name = -1;
+ S32 nonflora_te_offset = NO_FACE;
+
+ if (name == (S32)GL_NAME_PARCEL_WALL)
+ {
+ gLastHitParcelWall = TRUE;
+ }
+
+ gLastHitHUDIcon = NULL;
+
+ objectp = gObjectList.getSelectedObject(name);
+ if (objectp)
+ {
+ if (objectp->mbCanSelect)
+ {
+ te_offset = (te_offset == 16) ? NO_FACE : te_offset;
+
+ // If the hit object isn't a plant, store it as the frontmost non-flora object.
+ LLPCode pcode = objectp->getPCode();
+ if( (LL_PCODE_LEGACY_GRASS != pcode) &&
+ (LL_PCODE_LEGACY_TREE != pcode) &&
+ (LL_PCODE_TREE_NEW != pcode))
+ {
+ nonflora_objectp = objectp;
+ nonflora_name = name;
+ nonflora_te_offset = te_offset;
+ }
+ }
+ else
+ {
+ //llinfos << "Hit object you can't select" << llendl;
+ }
+ }
+ else
+ {
+ // was this name referring to a hud icon?
+ gLastHitHUDIcon = LLHUDIcon::handlePick(name);
+ }
+
+ analyzeHit(
+ mPickPoint.mX, mPickPoint.mY, objectp, te_offset,
+ &gLastHitObjectID, &gLastHitObjectFace, &gLastHitPosGlobal, &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord );
+
+ if (objectp && !gLastHitObjectID.isNull())
+ {
+ gLastHitObjectOffset = gAgent.calcFocusOffset(objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+
+ if( objectp == nonflora_objectp )
+ {
+ gLastHitNonFloraObjectID = gLastHitObjectID;
+ gLastHitNonFloraObjectFace = gLastHitObjectFace;
+ gLastHitNonFloraPosGlobal = gLastHitPosGlobal;
+ gLastHitNonFloraObjectOffset= gLastHitObjectOffset;
+ }
+ else
+ {
+ analyzeHit( mPickPoint.mX, mPickPoint.mY, nonflora_objectp, nonflora_te_offset,
+ &gLastHitNonFloraObjectID, &gLastHitNonFloraObjectFace, &gLastHitNonFloraPosGlobal,
+ &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord);
+
+ if( nonflora_objectp )
+ {
+ gLastHitNonFloraObjectOffset = gAgent.calcFocusOffset(nonflora_objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+ }
+
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+
+ gPickFaces = FALSE;
+}
+
+// Performs the GL object/land pick.
+// Stores its results in globals, gHit*
+void LLViewerWindow::hitObjectOrLandGlobalImmediate(S32 x, S32 y_from_bot, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitObjectOrLandGlobalAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent);
+ performPick();
+ if (callback)
+ {
+ callback(x, y_from_bot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+LLViewerObject* LLViewerWindow::getObjectUnderCursor(const F32 depth)
+{
+ S32 x = getCurrentMouseX();
+ S32 y = getCurrentMouseY();
+
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ LLVector3 camera_pos_global = gCamera->getOrigin();
+ LLVector3 pick_end = camera_pos_global + mouse_direction_global * depth;
+ LLVector3 collision_point;
+ return gPipeline.pickObject(camera_pos_global, pick_end, collision_point);
+}
+
+void LLViewerWindow::analyzeHit(
+ S32 x, // input
+ S32 y_from_bot, // input
+ LLViewerObject* objectp, // input
+ U32 te_offset, // input
+ LLUUID* hit_object_id_p,// output
+ S32* hit_face_p, // output
+ LLVector3d* hit_pos_p, // output
+ BOOL* hit_land, // output
+ F32* hit_u_coord, // output
+ F32* hit_v_coord) // output
+{
+ // Clean up inputs
+ S32 face = -1;
+
+ if (te_offset != NO_FACE )
+ {
+ face = te_offset;
+ }
+
+ *hit_land = FALSE;
+
+ if (objectp)
+ {
+ if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
+ {
+ // Hit land
+ *hit_land = TRUE;
+
+ // put global position into land_pos
+ LLVector3d land_pos;
+ if (mousePointOnLandGlobal(x, y_from_bot, &land_pos))
+ {
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+
+ // Fudge the land focus a little bit above ground.
+ *hit_pos_p = land_pos + LLVector3d(0.f, 0.f, 0.1f);
+ //llinfos << "DEBUG Hit Land " << *hit_pos_p << llendl;
+ return;
+ }
+ else
+ {
+ //llinfos << "Hit land but couldn't find position" << llendl;
+ // Fall through to "Didn't hit anything"
+ }
+ }
+ else
+ {
+ *hit_object_id_p = objectp->mID;
+ *hit_face_p = face;
+
+ // Hit an object
+ if (objectp->isAvatar())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(((LLVOAvatar*)objectp)->mPelvisp->getWorldPosition());
+ }
+ else if (objectp->mDrawable.notNull())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(objectp->getRenderPosition());
+ }
+ else
+ {
+ // regular object
+ *hit_pos_p = objectp->getPositionGlobal();
+ }
+
+ if (gPickFaces && face > -1 &&
+ objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
+ face < objectp->mDrawable->getNumFaces())
+ {
+ // render red-blue gradient to get 1/256 precision
+ // then render green grid to get final 1/4096 precision
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+ const S32 UV_PICK_WIDTH = 41;
+ const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
+ U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
+ S32 pick_face = ((LLVOVolume*)objectp)->getAllTEsSame() ? 0 : face;
+ LLFace* facep = objectp->mDrawable->getFace(objectp->getFaceIndexOffset() + pick_face);
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
+ glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
+ gPipeline.renderFaceForUVSelect(facep);
+
+ glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
+ U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
+ *hit_u_coord = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
+ *hit_v_coord = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
+ }
+ else
+ {
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ }
+
+ //llinfos << "DEBUG Hit Object " << *hit_pos_p << llendl;
+ return;
+ }
+ }
+
+ // Didn't hit anything.
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+ *hit_pos_p = LLVector3d::zero;
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ //llinfos << "DEBUG Hit Nothing " << llendl;
+}
+
+
+void LLViewerWindow::requestFastFrame(LLView *view)
+{
+ if (!mPickPending &&
+ mWindow->getSwapMethod() != LLWindow::SWAP_METHOD_UNDEFINED &&
+ gStartupState >= STATE_STARTED &&
+ gSavedSettings.getBOOL("RenderFastUI") &&
+ !gbCapturing)
+ {
+ if (!mFastFrameTimer.getStarted())
+ {
+ // we're double buffered, so when first requesting a fast ui update
+ // we need to render the scene again so that the front and back buffers
+ // are synced
+ mRenderFullFrame = TRUE;
+ }
+ // calculation new expiration time and reset timer
+ F32 expiration;
+ if (mFastFrameTimer.hasExpired())
+ {
+ expiration = FAST_FRAME_INCREMENT;
+ }
+ else
+ {
+ expiration = llmin(MAX_FAST_FRAME_TIME, mFastFrameTimer.getTimeToExpireF32() + FAST_FRAME_INCREMENT);
+ }
+
+ mFastFrameTimer.start();
+ mFastFrameTimer.setTimerExpirySec(expiration);
+
+ LLView::sFastFrameView = view->getRootMostFastFrameView();
+ if (!LLView::sFastFrameView)
+ {
+ LLView::sFastFrameView = view;
+ }
+ }
+}
+
+
+// Returns unit vector relative to camera
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov = gCamera->getView();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate pixel distance to screen
+ F32 distance = (height / 2.f) / (tan(fov / 2.f));
+
+ // calculate click point relative to middle of screen
+ F32 click_x = x - width / 2.f;
+ F32 click_y = y - height / 2.f;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = distance * gCamera->getAtAxis()
+ - click_x * gCamera->getLeftAxis()
+ + click_y * gCamera->getUpAxis();
+
+ mouse_vector.normVec();
+
+ return mouse_vector;
+}
+
+
+// Returns unit vector relative to camera in camera space
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov_height = gCamera->getView();
+ F32 fov_width = fov_height * gCamera->getAspect();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate click point relative to middle of screen
+ F32 click_x = (((F32)x / (F32)width) - 0.5f) * fov_width * -1.f;
+ F32 click_y = (((F32)y / (F32)height) - 0.5f) * fov_height;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f);
+ LLQuaternion mouse_rotate;
+ mouse_rotate.setQuat(click_y, click_x, 0.f);
+
+ mouse_vector = mouse_vector * mouse_rotate;
+ // project to z = -1 plane;
+ mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]);
+
+ return mouse_vector;
+}
+
+
+
+BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y,
+ const LLVector3d &plane_point_global,
+ const LLVector3 &plane_normal_global)
+{
+ LLVector3d mouse_direction_global_d;
+
+ mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y));
+ LLVector3d plane_normal_global_d;
+ plane_normal_global_d.setVec(plane_normal_global);
+ F64 mouse_look_at_scale = (plane_normal_global_d * (plane_point_global - gAgent.getCameraPositionGlobal()))
+ / (plane_normal_global_d * mouse_direction_global_d);
+ point = gAgent.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d;
+
+ return mouse_look_at_scale > 0.0;
+}
+
+
+// Returns global position
+BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global)
+{
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ F32 mouse_dir_scale;
+ BOOL hit_land = FALSE;
+ LLViewerRegion *regionp;
+ F32 land_z;
+ const F32 FIRST_PASS_STEP = 1.0f; // meters
+ const F32 SECOND_PASS_STEP = 0.1f; // meters
+ LLVector3d camera_pos_global;
+
+ camera_pos_global = gAgent.getCameraPositionGlobal();
+ LLVector3d probe_point_global;
+ LLVector3 probe_point_region;
+
+ // walk forwards to find the point
+ for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgent.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid());
+ S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid());
+ S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge;
+ if ((i >= grids_per_edge) || (j >= grids_per_edge))
+ {
+ //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand initial z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land
+
+ // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl;
+
+ hit_land = TRUE;
+ break;
+ }
+ }
+
+
+ if (hit_land)
+ {
+ // Don't go more than one step beyond where we stopped above.
+ // This can't just be "mouse_vec_scale" because floating point error
+ // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X
+ F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP;
+
+ // take a step backwards, then walk forwards again to refine position
+ for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ /*
+ i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid());
+ j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid());
+ if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge))
+ {
+ // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+ land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ];
+ */
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand refine z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land again
+
+ *land_position_global = probe_point_global;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+// Saves an image to the harddrive as "SnapshotX" where X >= 1.
+BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
+{
+ if (! raw)
+ {
+ return FALSE;
+ }
+
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sSnapshotBaseName );
+ proposed_name.append( ".bmp" );
+
+ // pick a directory in which to save
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_BMP, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return FALSE;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sSnapshotBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+ sprintf( extension, "_%.3d.bmp", i );
+ filepath = sSnapshotDir;
+ filepath += sSnapshotBaseName;
+ filepath += extension;
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ LLImageBase::setSizeOverride(TRUE);
+ BOOL success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ LLImageBase::setSizeOverride(FALSE);
+
+ return success;
+}
+
+void LLViewerWindow::saveMovieNumbered(void*)
+{
+ if (!gbCapturing)
+ {
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sMovieBaseName );
+#if LL_DARWIN
+ proposed_name.append( ".mov" );
+#else
+ proposed_name.append( ".avi" );
+#endif
+
+ // pick a directory in which to save
+ LLFilePicker &picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_AVI, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sMovieBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+#if LL_DARWIN
+ sprintf( extension, "_%.3d.mov", i );
+#else
+ sprintf( extension, "_%.3d.avi", i );
+#endif
+ filepath.assign( sSnapshotDir );
+ filepath.append( sMovieBaseName );
+ filepath.append( extension );
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+
+ gbCapturing = TRUE;
+ gMovieMaker.StartCapture((char *)filepath.c_str(), x, y);
+ }
+ else
+ {
+ gMovieMaker.EndCapture();
+ gbCapturing = FALSE;
+ }
+}
+
+static S32 BORDERHEIGHT = 0;
+static S32 BORDERWIDTH = 0;
+
+void LLViewerWindow::movieSize(S32 new_width, S32 new_height)
+{
+ LLCoordScreen size;
+ gViewerWindow->mWindow->getSize(&size);
+ if ( (size.mX != new_width + BORDERWIDTH)
+ ||(size.mY != new_height + BORDERHEIGHT))
+ {
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+ BORDERWIDTH = size.mX - x;
+ BORDERHEIGHT = size.mY- y;
+ LLCoordScreen new_size(new_width + BORDERWIDTH,
+ new_height + BORDERHEIGHT);
+ BOOL disable_sync = gSavedSettings.getBOOL("DisableVerticalSync");
+ if (gViewerWindow->mWindow->getFullscreen())
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ new_size,
+ disable_sync,
+ TRUE);
+ }
+ else
+ {
+ gViewerWindow->mWindow->setSize(new_size);
+ }
+ }
+}
+
+BOOL LLViewerWindow::saveSnapshot( const LLString& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ llinfos << "Saving snapshot to: " << filepath << llendl;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, show_ui, do_rebuild);
+
+ if (success)
+ {
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Unable to capture raw snapshot" << llendl;
+ }
+
+ return success;
+}
+
+
+void LLViewerWindow::playSnapshotAnimAndSound()
+{
+ gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START);
+ send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f);
+}
+
+
+// Saves the image from the screen to the specified filename and path.
+BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height,
+ BOOL keep_window_aspect, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ F32 image_aspect_ratio = ((F32)image_width) / ((F32)image_height);
+ F32 window_aspect_ratio = ((F32)getWindowWidth()) / ((F32)getWindowHeight());
+
+ if ((!gWorldPointer) ||
+ (!raw))
+ {
+ return FALSE;
+ }
+
+ // IW 3/5/04 We don'a wan' nunna yer fest frumes har!
+ finishFastFrame();
+
+ // PRE SNAPSHOT
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ setCursor(UI_CURSOR_WAIT);
+
+ // Hide all the UI widgets first and draw a frame
+ BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
+
+ if ( prev_draw_ui != show_ui)
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD);
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ // Copy screen to a buffer
+ // crop sides or top and bottom, if taking a snapshot of different aspect ratio
+ // from window
+ S32 snapshot_width = mWindowRect.getWidth();
+ S32 snapshot_height = mWindowRect.getHeight();
+ if (!keep_window_aspect)
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ snapshot_height = llround((F32)snapshot_width / image_aspect_ratio);
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ snapshot_width = llround((F32)snapshot_height * image_aspect_ratio);
+ }
+ }
+
+ F32 scale_factor = llmax(1.f, (F32)image_width / snapshot_width, (F32)image_height / snapshot_height);
+ raw->resize(llfloor(snapshot_width*scale_factor), llfloor(snapshot_height *scale_factor), type == SNAPSHOT_TYPE_DEPTH ? 4 : 3);
+
+ BOOL high_res = scale_factor > 1.f;
+ if (high_res)
+ {
+ send_agent_pause();
+ //rescale fonts
+ initFonts(scale_factor);
+ LLHUDText::reshape();
+ }
+
+ // SNAPSHOT
+ S32 window_width = mWindowRect.getWidth();
+ S32 window_height = mWindowRect.getHeight();
+ S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f);
+ S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f);
+
+ S32 output_buffer_offset_y = 0;
+
+ F32 depth_conversion_factor_1 = (gCamera->getFar() + gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+ F32 depth_conversion_factor_2 = (gCamera->getFar() - gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+
+ for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y)
+ {
+ S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);;
+ // handle fractional columns
+ U32 read_height = llmax(0, (window_height - subimage_y_offset) -
+ llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight())));
+
+ S32 output_buffer_offset_x = 0;
+ for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x)
+ {
+ gDisplaySwapBuffers = FALSE;
+ if (type == SNAPSHOT_TYPE_OBJECT_ID)
+ {
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ gCamera->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ setup3DRender();
+ setupViewport();
+ BOOL first_time_through = (subimage_x + subimage_y == 0);
+ gPickTransparent = FALSE;
+ gPickAlphaThreshold = 0.1f;
+ gObjectList.renderObjectsForSelect(*gCamera, FALSE, !first_time_through);
+ }
+ else
+ {
+ display(do_rebuild, scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ }
+ glFlush();
+ S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width);
+ // handle fractional rows
+ U32 read_width = llmax(0, (window_width - subimage_x_offset) -
+ llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth())));
+ for(U32 out_y = 0; out_y < read_height ; out_y++)
+ {
+ if (type == SNAPSHOT_TYPE_OBJECT_ID || type == SNAPSHOT_TYPE_COLOR)
+ {
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ raw->getData() + // current output pixel is beginning of buffer...
+ (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 3 // times 3 bytes per pixel
+ );
+ }
+ else // SNAPSHOT_TYPE_DEPTH
+ {
+ S32 output_buffer_offset = (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 4; // times 4 bytes per pixel
+
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_DEPTH_COMPONENT, GL_FLOAT,
+ raw->getData() + output_buffer_offset// current output pixel is beginning of buffer...
+ );
+
+ for (S32 i = output_buffer_offset; i < output_buffer_offset + (S32)read_width * 4; i += 4)
+ {
+ F32 depth_float = *(F32*)(raw->getData() + i);
+
+ F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2));
+ U8 depth_byte = F32_to_U8(linear_depth_float, gCamera->getNear(), gCamera->getFar());
+ *(raw->getData() + i + 0) = depth_byte;
+ *(raw->getData() + i + 1) = depth_byte;
+ *(raw->getData() + i + 2) = depth_byte;
+ *(raw->getData() + i + 3) = 255;
+ }
+ }
+ }
+ output_buffer_offset_x += subimage_x_offset;
+ stop_glerror();
+ }
+ output_buffer_offset_y += subimage_y_offset;
+ }
+
+ // POST SNAPSHOT
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ if (high_res)
+ {
+ initFonts(1.f);
+ LLHUDText::reshape();
+ }
+
+ gDisplaySwapBuffers = TRUE;
+
+ // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding)
+ // Note: this formula depends on the number of components being 3. Not obvious, but it's correct.
+ image_width += (image_width * (type == SNAPSHOT_TYPE_DEPTH ? 4 : 3)) % 4;
+
+ // Resize image
+ raw->scale( image_width, image_height );
+
+ setCursor(UI_CURSOR_ARROW);
+
+ if (do_rebuild)
+ {
+ // If we had to do a rebuild, that means that the lists of drawables to be rendered
+ // was empty before we started.
+ // Need to reset these, otherwise we call state sort on it again when render gets called the next time
+ // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of
+ // objects on them.
+ gPipeline.resetDrawOrders();
+ }
+
+ if (high_res)
+ {
+ send_agent_resume();
+ }
+
+ return TRUE;
+}
+
+void LLViewerWindow::destroyWindow()
+{
+ if (mWindow)
+ {
+ LLWindowManager::destroyWindow(mWindow);
+ }
+ mWindow = NULL;
+}
+
+
+void LLViewerWindow::drawMouselookInstructions()
+{
+ // Draw instructions for mouselook ("Press ESC to leave Mouselook" in a box at the top of the screen.)
+ const char* instructions = "Press ESC to leave Mouselook.";
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ const S32 INSTRUCTIONS_PAD = 5;
+ LLRect instructions_rect;
+ instructions_rect.setLeftTopAndSize(
+ INSTRUCTIONS_PAD,
+ gViewerWindow->getWindowHeight() - INSTRUCTIONS_PAD,
+ font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD,
+ llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD));
+
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4f( 0.9f, 0.9f, 0.9f, 1.0f );
+ gl_rect_2d( instructions_rect );
+ }
+
+ font->renderUTF8(
+ instructions, 0,
+ instructions_rect.mLeft + INSTRUCTIONS_PAD,
+ instructions_rect.mTop - INSTRUCTIONS_PAD,
+ LLColor4( 0.0f, 0.0f, 0.0f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+}
+
+
+// These functions are here only because LLViewerWindow used to do the work that gFocusMgr does now.
+// They let other objects continue to work without change.
+
+void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus))
+{
+ gFocusMgr.setKeyboardFocus( new_focus, on_focus_lost );
+}
+
+LLUICtrl* LLViewerWindow::getKeyboardFocus()
+{
+ return gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::hasKeyboardFocus(const LLUICtrl* possible_focus) const
+{
+ return possible_focus == gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::childHasKeyboardFocus(const LLView* parent) const
+{
+ return gFocusMgr.childHasKeyboardFocus( parent );
+}
+
+void LLViewerWindow::setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor))
+{
+ gFocusMgr.setMouseCapture( new_captor, on_capture_lost );
+}
+
+LLMouseHandler* LLViewerWindow::getMouseCaptor() const
+{
+ return gFocusMgr.getMouseCapture();
+}
+
+BOOL LLViewerWindow::hasMouseCapture(const LLMouseHandler* possible_captor) const
+{
+ return possible_captor == gFocusMgr.getMouseCapture();
+}
+
+S32 LLViewerWindow::getWindowHeight() const
+{
+ return mVirtualWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowWidth() const
+{
+ return mVirtualWindowRect.getWidth();
+}
+
+S32 LLViewerWindow::getWindowDisplayHeight() const
+{
+ return mWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowDisplayWidth() const
+{
+ return mWindowRect.getWidth();
+}
+
+LLView* LLViewerWindow::getTopView() const
+{
+ return gFocusMgr.getTopView();
+}
+
+BOOL LLViewerWindow::hasTopView(LLView* view) const
+{
+ return view == gFocusMgr.getTopView();
+}
+
+void LLViewerWindow::setTopView(LLView* new_top,void (*on_top_lost)(LLView* old_top))
+{
+ gFocusMgr.setTopView( new_top, on_top_lost );
+}
+
+void LLViewerWindow::setupViewport(S32 x_offset, S32 y_offset)
+{
+ glViewport(x_offset, y_offset, mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+void LLViewerWindow::setup3DRender()
+{
+ gCamera->setPerspective(NOT_FOR_SELECTION, 0, 0, mWindowRect.getWidth(), mWindowRect.getHeight(), FALSE, gCamera->getNear(), MAX_FAR_PLANE);
+}
+
+void LLViewerWindow::setup2DRender()
+{
+ gl_state_for_2d(mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastObjectHit()
+{
+ return gObjectList.findObject( gLastHitObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastObjectHitOffset()
+{
+ return gLastHitObjectOffset;
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastNonFloraObjectHit()
+{
+ return gObjectList.findObject( gLastHitNonFloraObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastNonFloraObjectHitOffset()
+{
+ return gLastHitNonFloraObjectOffset;
+}
+
+
+void LLViewerWindow::setShowProgress(const BOOL show)
+{
+ if (mProgressView)
+ {
+ mProgressView->setVisible(show);
+ }
+}
+
+BOOL LLViewerWindow::getShowProgress() const
+{
+ return (mProgressView && mProgressView->getVisible());
+}
+
+
+void LLViewerWindow::moveProgressViewToFront()
+{
+ if( mProgressView && mRootView )
+ {
+ mRootView->removeChild( mProgressView );
+ mRootView->addChild( mProgressView );
+ }
+}
+
+void LLViewerWindow::setProgressString(const LLString& string)
+{
+ if (mProgressView)
+ {
+ mProgressView->setText(string);
+ }
+}
+
+void LLViewerWindow::setProgressMessage(const LLString& msg)
+{
+ if(mProgressView)
+ {
+ mProgressView->setMessage(msg);
+ }
+}
+
+void LLViewerWindow::setProgressPercent(const F32 percent)
+{
+ if (mProgressView)
+ {
+ mProgressView->setPercent(percent);
+ }
+}
+
+void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const LLString& label )
+{
+ if (mProgressView)
+ {
+ mProgressView->setCancelButtonVisible( b, label );
+ }
+}
+
+
+LLProgressView *LLViewerWindow::getProgressView() const
+{
+ return mProgressView;
+}
+
+void LLViewerWindow::dumpState()
+{
+ llinfos << "LLViewerWindow Active " << S32(mActive) << llendl;
+ llinfos << "mWindow visible " << S32(mWindow->getVisible())
+ << " minimized " << S32(mWindow->getMinimized())
+ << llendl;
+}
+
+void LLViewerWindow::stopGL(BOOL save_state)
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ llinfos << "Shutting down GL..." << llendl;
+ gSky.destroyGL();
+ stop_glerror();
+
+ gImageList.destroyGL(save_state);
+ stop_glerror();
+
+ gBumpImageList.destroyGL();
+ stop_glerror();
+
+ LLFontGL::destroyGL();
+ stop_glerror();
+
+ LLVOAvatar::destroyGL();
+ stop_glerror();
+
+ LLDynamicTexture::destroyGL();
+ stop_glerror();
+
+ if(gParcelMgr) gParcelMgr->destroyGL();
+
+ gPipeline.destroyGL();
+
+ gCone.cleanupGL();
+ gBox.cleanupGL();
+ gSphere.cleanupGL();
+ gCylinder.cleanupGL();
+
+ gGLManager.mIsDisabled = TRUE;
+ stop_glerror();
+
+ llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemory << " bytes" << llendl;
+ }
+}
+
+void LLViewerWindow::restoreGL(const LLString& progress_message)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llinfos << "Restoring GL..." << llendl;
+ gGLManager.mIsDisabled = FALSE;
+
+ // for future support of non-square pixels, and fonts that are properly stretched
+ //LLFontGL::destroyDefaultFonts();
+ initFonts();
+ initGLDefaults();
+ LLGLState::restoreGL();
+ gSky.restoreGL();
+ gPipeline.restoreGL();
+ LLDrawPoolWater::restoreGL();
+ LLManipTranslate::restoreGL();
+ gImageList.restoreGL();
+ gBumpImageList.restoreGL();
+ gPipeline.setUseAGP(gSavedSettings.getBOOL("RenderUseAGP"));
+ LLDynamicTexture::restoreGL();
+ LLVOAvatar::restoreGL();
+ if (gParcelMgr) gParcelMgr->restoreGL();
+
+ if (gFloaterCustomize && gFloaterCustomize->getVisible())
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+
+ if (!progress_message.empty())
+ {
+ gRestoreGLTimer.reset();
+ gRestoreGL = TRUE;
+ setShowProgress(TRUE);
+ setProgressString(progress_message);
+ }
+ llinfos << "...Restoring GL done" << llendl;
+#if LL_WINDOWS
+ if (SetUnhandledExceptionFilter(LLWinDebug::handleException) != LLWinDebug::handleException)
+ {
+ llwarns << " Someone took over my exception handler (post restoreGL)!" << llendl;
+ }
+#endif
+
+ }
+}
+
+void LLViewerWindow::initFonts(F32 zoom_factor)
+{
+ LLFontGL::destroyGL();
+ LLFontGL::initDefaultFonts( gSavedSettings.getF32("FontScreenDPI"),
+ mDisplayScale.mV[VX] * zoom_factor,
+ mDisplayScale.mV[VY] * zoom_factor,
+ gSavedSettings.getString("FontMonospace"),
+ gSavedSettings.getF32("FontSizeMonospace"),
+ gSavedSettings.getString("FontSansSerif"),
+ gSavedSettings.getString("FontSansSerifFallback"),
+ gSavedSettings.getF32("FontSansSerifFallbackScale"),
+ gSavedSettings.getF32("FontSizeSmall"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gSavedSettings.getF32("FontSizeLarge"),
+ gSavedSettings.getF32("FontSizeHuge"),
+ gSavedSettings.getString("FontSansSerifBold"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gDirUtilp->getAppRODataDir()
+ );
+}
+void LLViewerWindow::toggleFullscreen(BOOL show_progress)
+{
+ if (mWindow)
+ {
+ mWantFullscreen = mWindow->getFullscreen() ? FALSE : TRUE;
+ mShowFullscreenProgress = show_progress;
+ }
+}
+
+void LLViewerWindow::getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const
+{
+ fullscreen = mWantFullscreen;
+
+ if (gViewerWindow->mWindow
+ && gViewerWindow->mWindow->getFullscreen() == mWantFullscreen)
+ {
+ width = gViewerWindow->getWindowDisplayWidth();
+ height = gViewerWindow->getWindowDisplayHeight();
+ }
+ else if (mWantFullscreen)
+ {
+ width = gSavedSettings.getS32("FullScreenWidth");
+ height = gSavedSettings.getS32("FullScreenHeight");
+ }
+ else
+ {
+ width = gSavedSettings.getS32("WindowWidth");
+ height = gSavedSettings.getS32("WindowHeight");
+ }
+}
+
+
+BOOL LLViewerWindow::checkSettings()
+{
+ BOOL is_fullscreen = gViewerWindow->mWindow->getFullscreen();
+ if (is_fullscreen && !mWantFullscreen)
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ LLCoordScreen(gSavedSettings.getS32("WindowWidth"),
+ gSavedSettings.getS32("WindowHeight")),
+ TRUE,
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ else if (!is_fullscreen && mWantFullscreen)
+ {
+ if (!LLStartUp::canGoFullscreen())
+ {
+ return FALSE;
+ }
+
+ gViewerWindow->changeDisplaySettings(TRUE,
+ LLCoordScreen(gSavedSettings.getS32("FullScreenWidth"),
+ gSavedSettings.getS32("FullScreenHeight")),
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerWindow::restartDisplay(BOOL show_progress_bar)
+{
+ llinfos << "Restaring GL" << llendl;
+ stopGL();
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+}
+
+BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar)
+{
+ BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized");
+ mWantFullscreen = fullscreen;
+ mShowFullscreenProgress = show_progress_bar;
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ BOOL old_fullscreen = mWindow->getFullscreen();
+ if (!old_fullscreen && fullscreen && !LLStartUp::canGoFullscreen())
+ {
+ // we can't do this now, so do it later
+
+ gSavedSettings.setS32("FullScreenWidth", size.mX);
+ gSavedSettings.setS32("FullScreenHeight", size.mY);
+ //gSavedSettings.setBOOL("DisableVerticalSync", disable_vsync);
+
+ return TRUE; // a lie..., because we'll get to it later
+ }
+
+ // going from windowed to windowed
+ if (!old_fullscreen && !fullscreen)
+ {
+ // if not maximized, use the request size
+ if (!mWindow->getMaximized())
+ {
+ mWindow->setSize(size);
+ }
+ return TRUE;
+ }
+
+ // Close floaters that don't handle settings change
+ LLFloaterSnapshot::hide(0);
+
+ BOOL result_first_try = FALSE;
+ BOOL result_second_try = FALSE;
+
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ send_agent_pause();
+ llinfos << "Stopping GL during changeDisplaySettings" << llendl;
+ stopGL();
+ mIgnoreActivate = TRUE;
+ LLCoordScreen old_size;
+ LLCoordScreen old_pos;
+ mWindow->getSize(&old_size);
+ BOOL got_position = mWindow->getPosition(&old_pos);
+
+ if (!old_fullscreen && fullscreen && got_position)
+ {
+ // switching from windowed to fullscreen, so save window position
+ gSavedSettings.setS32("WindowX", old_pos.mX);
+ gSavedSettings.setS32("WindowY", old_pos.mY);
+ }
+
+ result_first_try = mWindow->switchContext(fullscreen, size, disable_vsync);
+ if (!result_first_try)
+ {
+ // try to switch back
+ result_second_try = mWindow->switchContext(old_fullscreen, old_size, disable_vsync);
+
+ if (!result_second_try)
+ {
+ // we are stuck...try once again with a minimal resolution?
+ send_agent_resume();
+ mIgnoreActivate = FALSE;
+ return FALSE;
+ }
+ }
+ send_agent_resume();
+
+ llinfos << "Restoring GL during resolution change" << llendl;
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+
+ if (!result_first_try)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[RESX]"] = llformat("%d",size.mX);
+ args["[RESY]"] = llformat("%d",size.mY);
+ alertXml("ResolutionSwitchFail", args);
+ size = old_size; // for reshape below
+ }
+
+ BOOL success = result_first_try || result_second_try;
+ if (success)
+ {
+#if LL_WINDOWS
+ // Only trigger a reshape after switching to fullscreen; otherwise rely on the windows callback
+ // (otherwise size is wrong; this is the entire window size, reshape wants the visible window size)
+ if (fullscreen)
+#endif
+ {
+ reshape(size.mX, size.mY);
+ }
+ }
+
+ if (!mWindow->getFullscreen() && success)
+ {
+ // maximize window if was maximized, else reposition
+ if (was_maximized)
+ {
+ mWindow->maximize();
+ }
+ else
+ {
+ S32 windowX = gSavedSettings.getS32("WindowX");
+ S32 windowY = gSavedSettings.getS32("WindowY");
+
+ mWindow->setPosition(LLCoordScreen ( windowX, windowY ) );
+ }
+ }
+
+ mIgnoreActivate = FALSE;
+ gFocusMgr.setKeyboardFocus(keyboard_focus, NULL);
+ mWantFullscreen = mWindow->getFullscreen();
+ mShowFullscreenProgress = FALSE;
+
+ return success;
+}
+
+
+F32 LLViewerWindow::getDisplayAspectRatio() const
+{
+ if (mWindow->getFullscreen())
+ {
+ if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio"))
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+ else
+ {
+ return gSavedSettings.getF32("FullScreenAspectRatio");
+ }
+ }
+ else
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+}
+
+
+void LLViewerWindow::drawPickBuffer() const
+{
+ if (mPickBuffer)
+ {
+ LLGLDisable no_blend(GL_BLEND);
+ LLGLDisable no_alpha_test(GL_ALPHA_TEST);
+ LLGLSNoTexture no_texture;
+ glPixelZoom(10.f, 10.f);
+ glRasterPos2f(((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ ((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+ glPixelZoom(1.f, 1.f);
+ glColor4fv(LLColor4::white.mV);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ FALSE);
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f));
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glTranslatef(10.f, 10.f, 0.f);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]),
+ FALSE);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX)* 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY + 1) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX + 1) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY) * 10.f),
+ FALSE);
+ glPopMatrix();
+ }
+}
+
+void LLViewerWindow::calcDisplayScale()
+{
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+ LLVector2 display_scale;
+ display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ F32 height_normalization = gSavedSettings.getBOOL("UIAutoScale") ? ((F32)mWindowRect.getHeight() / display_scale.mV[VY]) / 768.f : 1.f;
+ if(mWindow->getFullscreen())
+ {
+ display_scale *= (ui_scale_factor * height_normalization);
+ }
+ else
+ {
+ display_scale *= ui_scale_factor;
+ }
+
+ // limit minimum display scale
+ if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE)
+ {
+ display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]);
+ }
+
+ if (mWindow->getFullscreen())
+ {
+ display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRect.getWidth());
+ display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRect.getHeight());
+ }
+
+ if (display_scale != mDisplayScale)
+ {
+ llinfos << "Setting display scale to " << display_scale << llendl;
+
+ mDisplayScale = display_scale;
+ // Init default fonts
+ initFonts();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// static
+bool LLViewerWindow::alertCallback(S32 modal)
+{
+ if (gNoRender)
+ {
+ return false;
+ }
+ else
+ {
+// if (modal) // we really always want to take you out of mouselook
+ {
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+ }
+ return true;
+ }
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ LLString::format_map_t args;
+ return alertXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ return LLAlertDialog::showXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data,
+ LLAlertDialog::alert_text_callback_t text_callback, void *text_data,
+ const LLString::format_map_t& edit_args, BOOL draw_asterixes)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ LLAlertDialog* alert = LLAlertDialog::createXml( xml_filename, args, callback, user_data );
+ if (alert)
+ {
+ if (text_callback)
+ {
+ alert->setEditTextCallback(text_callback, text_data);
+ }
+ alert->setEditTextArgs(edit_args);
+ alert->setDrawAsterixes(draw_asterixes);
+ alert->show();
+ }
+ return alert;
+}
+
+LLBottomPanel::LLBottomPanel(const LLString &name, const LLRect &rect) :
+ LLPanel(name, rect, FALSE),
+ mIndicator(NULL)
+{
+ // bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay
+ setFocusRoot(TRUE);
+ // don't capture mouse clicks that don't hit a child
+ setMouseOpaque(FALSE);
+ setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ setIsChrome(TRUE);
+}
+
+void LLBottomPanel::setFocusIndicator(LLView * indicator)
+{
+ mIndicator = indicator;
+}
+
+void LLBottomPanel::draw()
+{
+ if(mIndicator)
+ {
+ BOOL hasFocus = gFocusMgr.childHasKeyboardFocus(this);
+ mIndicator->setVisible(hasFocus);
+ mIndicator->setEnabled(hasFocus);
+ }
+ LLPanel::draw();
+}
diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h
new file mode 100644
index 0000000000..9c8e78a29d
--- /dev/null
+++ b/indra/newview/llviewerwindow.h
@@ -0,0 +1,391 @@
+/**
+ * @file llviewerwindow.h
+ * @brief Description of the LLViewerWindow class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//
+// A note about X,Y coordinates:
+//
+// X coordinates are in pixels, from the left edge of the window client area
+// Y coordinates are in pixels, from the BOTTOM edge of the window client area
+//
+// The Y coordinates therefore match OpenGL window coords, not Windows(tm) window coords.
+// If Y is from the top, the variable will be called "y_from_top"
+
+#ifndef LL_LLVIEWERWINDOW_H
+#define LL_LLVIEWERWINDOW_H
+
+#include "linked_lists.h"
+#include "v3dmath.h"
+#include "v2math.h"
+#include "llwindow.h"
+#include "lltimer.h"
+#include "llstat.h"
+#include "llalertdialog.h"
+
+class LLView;
+class LLViewerObject;
+class LLUUID;
+class LLMouseHandler;
+class LLProgressView;
+class LLTool;
+class LLVelocityBar;
+class LLViewerWindow;
+class LLTextBox;
+class LLImageRaw;
+class LLHUDIcon;
+
+class LLViewerWindow : public LLWindowCallbacks
+{
+public:
+ //
+ // CREATORS
+ //
+ LLViewerWindow(char* title, char* name, S32 x, S32 y, S32 width, S32 height, BOOL fullscreen, BOOL ignore_pixel_depth);
+ virtual ~LLViewerWindow();
+
+ void initGLDefaults();
+ void initBase();
+ void adjustRectanglesForFirstUse(const LLRect& full_window);
+ void initWorldUI();
+
+ //
+ // LLWindowCallback interface implementation
+ //
+ /*virtual*/ BOOL handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated);
+ /*virtual*/ BOOL handleTranslatedKeyUp(KEY key, MASK mask);
+ /*virtual*/ void handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
+ /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, MASK mask); // NOT going to handle extended
+ /*virtual*/ BOOL handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleCloseRequest(LLWindow *window);
+ /*virtual*/ void handleQuit(LLWindow *window);
+ /*virtual*/ BOOL handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ BOOL handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleMouseLeave(LLWindow *window);
+ /*virtual*/ void handleResize(LLWindow *window, S32 x, S32 y);
+ /*virtual*/ void handleFocus(LLWindow *window);
+ /*virtual*/ void handleFocusLost(LLWindow *window);
+ /*virtual*/ BOOL handleActivate(LLWindow *window, BOOL activated);
+ /*virtual*/ void handleMenuSelect(LLWindow *window, S32 menu_item);
+ /*virtual*/ BOOL handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height);
+ /*virtual*/ void handleScrollWheel(LLWindow *window, S32 clicks);
+ /*virtual*/ BOOL handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask);
+ /*virtual*/ void handleWindowBlock(LLWindow *window);
+ /*virtual*/ void handleWindowUnblock(LLWindow *window);
+ /*virtual*/ void handleDataCopy(LLWindow *window, S32 data_type, void *data);
+
+
+ //
+ // ACCESSORS
+ //
+ LLView* getRootView() const { return mRootView; }
+ const LLRect& getWindowRect() const { return mWindowRect; };
+ const LLRect& getVirtualWindowRect() const { return mVirtualWindowRect; };
+ S32 getWindowHeight() const;
+ S32 getWindowWidth() const;
+ S32 getWindowDisplayHeight() const;
+ S32 getWindowDisplayWidth() const;
+ LLWindow* getWindow() const { return mWindow; }
+ void* getPlatformWindow() const { return mWindow->getPlatformWindow(); }
+ void focusClient() const { return mWindow->focusClient(); };
+
+ LLCoordGL getLastMouse() const { return mLastMousePoint; }
+ S32 getLastMouseX() const { return mLastMousePoint.mX; }
+ S32 getLastMouseY() const { return mLastMousePoint.mY; }
+ LLCoordGL getCurrentMouse() const { return mCurrentMousePoint; }
+ S32 getCurrentMouseX() const { return mCurrentMousePoint.mX; }
+ S32 getCurrentMouseY() const { return mCurrentMousePoint.mY; }
+ S32 getCurrentMouseDX() const { return mCurrentMouseDelta.mX; }
+ S32 getCurrentMouseDY() const { return mCurrentMouseDelta.mY; }
+ LLCoordGL getCurrentMouseDelta() const { return mCurrentMouseDelta; }
+ LLStat * getMouseVelocityStat() { return &mMouseVelocityStat; }
+ BOOL getLeftMouseDown() const { return mLeftMouseDown; }
+ BOOL getRightMouseDown() const { return mRightMouseDown; }
+
+ LLView* getTopView() const;
+ BOOL hasTopView(LLView* view) const;
+
+ void setupViewport(S32 x_offset = 0, S32 y_offset = 0);
+ void setup3DRender();
+ void setup2DRender();
+
+ BOOL isPickPending() { return mPickPending; }
+
+ LLVector3 mouseDirectionGlobal(const S32 x, const S32 y) const;
+ LLVector3 mouseDirectionCamera(const S32 x, const S32 y) const;
+
+ // Is window of our application frontmost?
+ BOOL getActive() const { return mActive; }
+
+ void getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const;
+ // The 'target' is where the user wants the window to be. It may not be
+ // there yet, because we may be supressing fullscreen prior to login.
+
+ //
+ // MANIPULATORS
+ //
+ void saveLastMouse(const LLCoordGL &point);
+
+ void setCursor( ECursorType c );
+ void showCursor();
+ void hideCursor();
+ void moveCursorToCenter(); // move to center of window
+
+ void setShowProgress(const BOOL show);
+ BOOL getShowProgress() const;
+ void moveProgressViewToFront();
+ void setProgressString(const LLString& string);
+ void setProgressPercent(const F32 percent);
+ void setProgressMessage(const LLString& msg);
+ void setProgressCancelButtonVisible( BOOL b, const LLString& label );
+ LLProgressView *getProgressView() const;
+
+ void updateObjectUnderCursor();
+
+ BOOL handlePerFrameHover(); // Once per frame, update UI based on mouse position
+
+ BOOL handleKey(KEY key, MASK mask);
+ void handleScrollWheel (S32 clicks);
+
+ // Handle the application becoming active (frontmost) or inactive
+ //BOOL handleActivate(BOOL activate);
+
+ void setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus)); // new_focus = NULL to release the focus.
+ LLUICtrl* getKeyboardFocus();
+ BOOL hasKeyboardFocus( const LLUICtrl* possible_focus ) const;
+ BOOL childHasKeyboardFocus( const LLView* parent ) const;
+
+ void setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor)); // new_captor = NULL to release the mouse.
+ BOOL hasMouseCapture(const LLMouseHandler* possible_captor ) const;
+ LLMouseHandler* getMouseCaptor() const;
+
+ void setTopView(LLView* new_top, void (*on_top_lost)(LLView* old_top)); // set new_top = NULL to release top_view.
+
+ void reshape(S32 width, S32 height);
+ void sendShapeToSim();
+
+ void draw();
+// void drawSelectedObjects();
+
+ static void loadUserImage(void **cb_data, const LLUUID &uuid);
+
+ // Save snapshot like Snapshot 1, 2, 3, etc.
+ static void saveMovieNumbered(void*);
+
+ static void movieSize(S32 new_width, S32 new_height);
+
+ typedef enum e_snapshot_type
+ {
+ SNAPSHOT_TYPE_COLOR,
+ SNAPSHOT_TYPE_DEPTH,
+ SNAPSHOT_TYPE_OBJECT_ID
+ } ESnapshotType;
+
+ BOOL saveSnapshot(const LLString& filename, S32 image_width, S32 image_height, BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR);
+ BOOL rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height, BOOL keep_window_aspect = TRUE,
+ BOOL show_ui = TRUE, BOOL do_rebuild = FALSE, ESnapshotType type = SNAPSHOT_TYPE_COLOR );
+ BOOL saveImageNumbered(LLImageRaw *raw);
+
+ void playSnapshotAnimAndSound();
+
+ // draws selection boxes around selected objects, must call displayObjects first
+ void renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud );
+ void performPick();
+
+ void hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent = FALSE, BOOL pick_parcel_walls = FALSE);
+ void hitObjectOrLandGlobalImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent);
+
+ void hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask));
+ void hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask));
+
+ LLViewerObject* getObjectUnderCursor(const F32 depth = 16.0f);
+
+ void requestFastFrame(LLView* view);
+ BOOL renderingFastFrame() { return mFastFrameTimer.getStarted() && !firstFastFrame(); }
+ void finishFastFrame() { mFastFrameTimer.stop(); mRenderFullFrame = FALSE; }
+ BOOL firstFastFrame() { return mRenderFullFrame; }
+ void finishFirstFastFrame() { mRenderFullFrame = FALSE; }
+
+ // Returns a pointer to the last object hit
+ LLViewerObject *lastObjectHit();
+ LLViewerObject *lastNonFloraObjectHit();
+
+ const LLVector3d& lastObjectHitOffset();
+ const LLVector3d& lastNonFloraObjectHitOffset();
+
+ // mousePointOnLand() returns true if found point
+ BOOL mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_pos_global);
+ BOOL mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y, const LLVector3d &plane_point, const LLVector3 &plane_normal);
+ LLVector3d clickPointInWorldGlobal(const S32 x, const S32 y_from_bot, LLViewerObject* clicked_object) const;
+ BOOL clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const;
+
+ // Prints window implementation details
+ void dumpState();
+
+ // Request display setting changes
+ void toggleFullscreen(BOOL show_progress);
+
+ // handle shutting down GL and bringing it back up
+ BOOL checkSettings();
+ void restartDisplay(BOOL show_progress_bar);
+ BOOL changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar);
+ BOOL getIgnoreDestroyWindow() { return mIgnoreActivate; }
+ F32 getDisplayAspectRatio() const;
+ const LLVector2& getDisplayScale() const { return mDisplayScale; }
+ void calcDisplayScale();
+
+ void drawPickBuffer() const;
+
+ LLAlertDialog* alertXml(const std::string& xml_filename,
+ LLAlertDialog::alert_callback_t callback = NULL, void* user_data = NULL);
+ LLAlertDialog* alertXml(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback = NULL, void* user_data = NULL);
+ LLAlertDialog* alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data,
+ LLAlertDialog::alert_text_callback_t text_callback, void *text_data,
+ const LLString::format_map_t& edit_args = LLString::format_map_t(),
+ BOOL draw_asterixes = FALSE);
+
+ static bool alertCallback(S32 modal);
+
+#ifdef SABINRIG
+ //Silly rig stuff
+ void printFeedback(); //RIG STUFF!
+#endif //SABINRIG
+
+private:
+ void switchToolByMask(MASK mask);
+ void destroyWindow();
+ void drawMouselookInstructions();
+ void stopGL(BOOL save_state = TRUE);
+ void restoreGL(const LLString& progress_message = LLString::null);
+ void initFonts(F32 zoom_factor = 1.f);
+
+ void analyzeHit(
+ S32 x, // input
+ S32 y_from_bot, // input
+ LLViewerObject* objectp, // input
+ U32 te_offset, // input
+ LLUUID* hit_object_id_p,// output
+ S32* hit_face_p, // output
+ LLVector3d* hit_pos_p, // output
+ BOOL* hit_land, // output
+ F32* hit_u_coord, // output
+ F32* hit_v_coord); // output
+
+public:
+ LLWindow* mWindow; // graphical window object
+
+protected:
+ BOOL mActive;
+ BOOL mWantFullscreen;
+ BOOL mShowFullscreenProgress;
+ LLRect mWindowRect;
+ LLRect mVirtualWindowRect;
+ LLView* mRootView; // a view of size mWindowRect, containing all child views
+ LLVector2 mDisplayScale;
+
+ LLCoordGL mCurrentMousePoint; // last mouse position in GL coords
+ LLCoordGL mLastMousePoint; // Mouse point at last frame.
+ LLCoordGL mCurrentMouseDelta; //amount mouse moved this frame
+ LLStat mMouseVelocityStat;
+ BOOL mLeftMouseDown;
+ BOOL mRightMouseDown;
+
+ LLProgressView *mProgressView;
+
+ LLTextBox* mToolTip;
+ BOOL mToolTipBlocked; // True after a key press or a mouse button event. False once the mouse moves again.
+ LLRect mToolTipStickyRect; // Once a tool tip is shown, it will stay visible until the mouse leaves this rect.
+
+ BOOL mMouseInWindow; // True if the mouse is over our window or if we have captured the mouse.
+ BOOL mFocusCycleMode;
+
+ // Variables used for tool override switching based on modifier keys. JC
+ MASK mLastMask; // used to detect changes in modifier mask
+ LLTool* mToolStored; // the tool we're overriding
+ BOOL mSuppressToolbox; // sometimes hide the toolbox, despite
+ // having a camera tool selected
+ BOOL mHideCursorPermanent; // true during drags, mouselook
+ LLCoordGL mPickPoint;
+ LLCoordGL mPickOffset;
+ MASK mPickMask;
+ BOOL mPickPending;
+ void (*mPickCallback)(S32 x, S32 y, MASK mask);
+
+ LLString mOverlayTitle; // Used for special titles such as "Second Life - Special E3 2003 Beta"
+
+ static char sSnapshotBaseName[LL_MAX_PATH];
+ static char sSnapshotDir[LL_MAX_PATH];
+
+ static char sMovieBaseName[LL_MAX_PATH];
+
+ BOOL mIgnoreActivate;
+ LLFrameTimer mFastFrameTimer;
+ BOOL mRenderFullFrame;
+ U8* mPickBuffer;
+};
+
+class LLBottomPanel : public LLPanel
+{
+public:
+ LLBottomPanel(const LLString& name, const LLRect& rect);
+ void setFocusIndicator(LLView * indicator);
+ LLView * getFocusIndicator() { return mIndicator; }
+ /*virtual*/ void draw();
+protected:
+ LLView * mIndicator;
+};
+extern LLBottomPanel * gBottomPanel;
+
+void toggle_flying(void*);
+void toggle_first_person();
+void toggle_build(void*);
+void reset_viewer_state_on_sim(void);
+
+//
+// Constants
+//
+
+
+//
+// Globals
+//
+
+extern LLVelocityBar* gVelocityBar;
+extern LLViewerWindow* gViewerWindow;
+extern BOOL gQuietSnapshot;
+
+extern LLFrameTimer gMouseIdleTimer; // how long has it been since the mouse last moved?
+extern LLFrameTimer gAwayTimer; // tracks time before setting the avatar away state to true
+extern LLFrameTimer gAwayTriggerTimer; // how long the avatar has been away
+
+extern LLVector3d gLastHitPosGlobal;
+extern LLVector3d gLastHitObjectOffset;
+extern LLUUID gLastHitObjectID;
+extern S32 gLastHitObjectFace;
+extern BOOL gLastHitLand;
+extern F32 gLastHitUCoord;
+extern F32 gLastHitVCoord;
+
+
+extern LLVector3d gLastHitNonFloraPosGlobal;
+extern LLVector3d gLastHitNonFloraObjectOffset;
+extern LLUUID gLastHitNonFloraObjectID;
+extern S32 gLastHitNonFloraObjectFace;
+
+extern S32 gLastHitUIElement;
+extern LLHUDIcon* gLastHitHUDIcon;
+extern BOOL gLastHitParcelWall;
+extern BOOL gDebugSelect;
+extern BOOL gPickFaces;
+extern BOOL gPickTransparent;
+
+extern BOOL gDebugFastUIRender;
+extern S32 CHAT_BAR_HEIGHT;
+#endif
diff --git a/indra/newview/llvlcomposition.cpp b/indra/newview/llvlcomposition.cpp
new file mode 100644
index 0000000000..841b2cbb47
--- /dev/null
+++ b/indra/newview/llvlcomposition.cpp
@@ -0,0 +1,456 @@
+/**
+ * @file llvlcomposition.cpp
+ * @brief Viewer-side representation of a composition layer...
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvlcomposition.h"
+#include "llerror.h"
+#include "v3math.h"
+#include "llsurface.h"
+#include "lltextureview.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "noise.h"
+#include "llregionhandle.h" // for from_region_handle
+#include "llviewercontrol.h"
+
+
+
+F32 bilinear(const F32 v00, const F32 v01, const F32 v10, const F32 v11, const F32 x_frac, const F32 y_frac)
+{
+ // Not sure if this is the right math...
+ // Take weighted average of all four points (bilinear interpolation)
+ F32 result;
+
+ const F32 inv_x_frac = 1.f - x_frac;
+ const F32 inv_y_frac = 1.f - y_frac;
+ result = inv_x_frac*inv_y_frac*v00
+ + x_frac*inv_y_frac*v10
+ + inv_x_frac*y_frac*v01
+ + x_frac*y_frac*v11;
+
+ return result;
+}
+
+
+LLVLComposition::LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale) :
+ LLViewerLayer(width, scale),
+ mParamsReady(FALSE)
+{
+ mSurfacep = surfacep;
+
+ // Load Terrain Textures - Original ones
+ LLUUID id;
+ // Dirt
+ id.set( gViewerArt.getString("terrain_dirt_detail.tga") );
+ setDetailTextureID(0, id);
+
+ // Grass
+ id.set( gViewerArt.getString("terrain_grass_detail.tga") );
+ setDetailTextureID(1, id);
+
+ // Rock mountain
+ id.set( gViewerArt.getString("terrain_mountain_detail.tga") );
+ setDetailTextureID(2, id);
+
+ // Rock face
+ id.set( gViewerArt.getString("terrain_rock_detail.tga") );
+ setDetailTextureID(3, id);
+
+ // Initialize the texture matrix to defaults.
+ for (S32 i = 0; i < CORNER_COUNT; ++i)
+ {
+ mStartHeight[i] = gSavedSettings.getF32("TerrainColorStartHeight");
+ mHeightRange[i] = gSavedSettings.getF32("TerrainColorHeightRange");
+ }
+ mTexScaleX = 16.f;
+ mTexScaleY = 16.f;
+ mTexturesLoaded = FALSE;
+}
+
+
+LLVLComposition::~LLVLComposition()
+{
+}
+
+
+void LLVLComposition::setSurface(LLSurface *surfacep)
+{
+ mSurfacep = surfacep;
+}
+
+
+void LLVLComposition::setDetailTextureID(S32 corner, const LLUUID& id)
+{
+ if(id.isNull())
+ {
+ return;
+ }
+ mDetailTextures[corner] = gImageList.getImage(id);
+}
+
+BOOL LLVLComposition::generateHeights(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ if (!mParamsReady)
+ {
+ // All the parameters haven't been set yet (we haven't gotten the message from the sim)
+ return FALSE;
+ }
+
+ llassert(mSurfacep);
+ 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 );
+
+ if (x_end > mWidth)
+ {
+ x_end = mWidth;
+ }
+ if (y_end > mWidth)
+ {
+ y_end = mWidth;
+ }
+
+ LLVector3d origin_global = from_region_handle(mSurfacep->getRegion()->getHandle());
+
+ // For perlin noise generation...
+ const F32 slope_squared = 1.5f*1.5f;
+ const F32 xyScale = 4.9215f; //0.93284f;
+ const F32 zScale = 4; //0.92165f;
+ const F32 z_offset = 0.f;
+ const F32 noise_magnitude = 2.f; // Degree to which noise modulates composition layer (versus
+ // simple height)
+
+ // Heights map into textures as 0-1 = first, 1-2 = second, etc.
+ // So we need to compress heights into this range.
+ const S32 NUM_TEXTURES = 4;
+
+ const F32 xyScaleInv = (1.f / xyScale);
+ const F32 zScaleInv = (1.f / zScale);
+
+ const F32 inv_width = 1.f/mWidth;
+
+ // OK, for now, just have the composition value equal the height at the point.
+ for (S32 j = y_begin; j < y_end; j++)
+ {
+ for (S32 i = x_begin; i < x_end; i++)
+ {
+
+ F32 vec[3];
+ F32 vec1[3];
+ F32 twiddle;
+
+ // Bilinearly interpolate the start height and height range of the textures
+ F32 start_height = bilinear(mStartHeight[SOUTHWEST],
+ mStartHeight[SOUTHEAST],
+ mStartHeight[NORTHWEST],
+ mStartHeight[NORTHEAST],
+ i*inv_width, j*inv_width); // These will be bilinearly interpolated
+ F32 height_range = bilinear(mHeightRange[SOUTHWEST],
+ mHeightRange[SOUTHEAST],
+ mHeightRange[NORTHWEST],
+ mHeightRange[NORTHEAST],
+ i*inv_width, j*inv_width); // These will be bilinearly interpolated
+
+ LLVector3 location(i*mScale, j*mScale, 0.f);
+
+ F32 height = mSurfacep->resolveHeightRegion(location) + z_offset;
+
+ // Step 0: Measure the exact height at this texel
+ vec[0] = (F32)(origin_global.mdV[VX]+location.mV[VX])*xyScaleInv; // Adjust to non-integer lattice
+ vec[1] = (F32)(origin_global.mdV[VY]+location.mV[VY])*xyScaleInv;
+ vec[2] = height*zScaleInv;
+ //
+ // Choose material value by adding to the exact height a random value
+ //
+ vec1[0] = vec[0]*(0.2222222222f);
+ vec1[1] = vec[1]*(0.2222222222f);
+ vec1[2] = vec[2]*(0.2222222222f);
+ twiddle = noise2(vec1)*6.5f; // Low freq component for large divisions
+
+ twiddle += turbulence2(vec, 2)*slope_squared; // High frequency component
+ twiddle *= noise_magnitude;
+
+ F32 scaled_noisy_height = (height + twiddle - start_height) * F32(NUM_TEXTURES) / height_range;
+
+ scaled_noisy_height = llmax(0.f, scaled_noisy_height);
+ scaled_noisy_height = llmin(3.f, scaled_noisy_height);
+ *(mDatap + i + j*mWidth) = scaled_noisy_height;
+ }
+ }
+ return TRUE;
+}
+
+static const S32 BASE_SIZE = 128;
+
+BOOL LLVLComposition::generateComposition()
+{
+
+ if (!mParamsReady)
+ {
+ // All the parameters haven't been set yet (we haven't gotten the message from the sim)
+ return FALSE;
+ }
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ if (mDetailTextures[i]->getDiscardLevel() < 0)
+ {
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail
+ mDetailTextures[i]->addTextureStats(BASE_SIZE*BASE_SIZE);
+ return FALSE;
+ }
+ if ((mDetailTextures[i]->getDiscardLevel() != 0 &&
+ (mDetailTextures[i]->getWidth() < BASE_SIZE ||
+ mDetailTextures[i]->getHeight() < BASE_SIZE)))
+ {
+ S32 width = mDetailTextures[i]->getWidth(0);
+ S32 height = mDetailTextures[i]->getHeight(0);
+ S32 min_dim = llmin(width, height);
+ S32 ddiscard = 0;
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_TERRAIN); // in case we are at low detail
+ mDetailTextures[i]->setMinDiscardLevel(ddiscard);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL LLVLComposition::generateTexture(const F32 x, const F32 y,
+ const F32 width, const F32 height)
+{
+ llassert(mSurfacep);
+ llassert(x >= 0.f);
+ llassert(y >= 0.f);
+
+ LLTimer gen_timer;
+
+ ///////////////////////////
+ //
+ // Generate raw data arrays for surface textures
+ //
+ //
+
+ // These have already been validated by generateComposition.
+ LLPointer<LLImageRaw> st_raw[4];
+ U8* st_data[4];
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ // Read back a raw image for this discard level, if it exists
+ st_raw[i] = new LLImageRaw;
+ S32 min_dim = llmin(mDetailTextures[i]->getWidth(0), mDetailTextures[i]->getHeight(0));
+ S32 ddiscard = 0;
+ while (min_dim > BASE_SIZE && ddiscard < MAX_DISCARD_LEVEL)
+ {
+ ddiscard++;
+ min_dim /= 2;
+ }
+ if (!mDetailTextures[i]->readBackRaw(ddiscard, st_raw[i]))
+ {
+ llwarns << "Unable to read raw data for terrain detail texture: " << mDetailTextures[i]->getID() << llendl;
+ return FALSE;
+ }
+ if (mDetailTextures[i]->getWidth(ddiscard) != BASE_SIZE ||
+ mDetailTextures[i]->getHeight(ddiscard) != BASE_SIZE ||
+ mDetailTextures[i]->getComponents() != 3)
+ {
+ LLPointer<LLImageRaw> newraw = new LLImageRaw(BASE_SIZE, BASE_SIZE, 3);
+ newraw->composite(st_raw[i]);
+ st_raw[i] = newraw; // deletes old
+ }
+ st_data[i] = st_raw[i]->getData();
+ }
+
+ ///////////////////////////////////////
+ //
+ // Generate and clamp x/y bounding box.
+ //
+ //
+
+ 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 );
+
+ if (x_end > mWidth)
+ {
+ llwarns << "x end > width" << llendl;
+ x_end = mWidth;
+ }
+ if (y_end > mWidth)
+ {
+ llwarns << "y end > width" << llendl;
+ y_end = mWidth;
+ }
+
+
+ ///////////////////////////////////////////
+ //
+ // Generate target texture information, stride ratios.
+ //
+ //
+
+ LLViewerImage *texturep;
+ U32 tex_width, tex_height, tex_comps;
+ U32 tex_stride;
+ F32 tex_x_scalef, tex_y_scalef;
+ S32 tex_x_begin, tex_y_begin, tex_x_end, tex_y_end;
+ F32 tex_x_ratiof, tex_y_ratiof;
+
+ texturep = mSurfacep->getSTexture();
+ tex_width = texturep->getWidth();
+ tex_height = texturep->getHeight();
+ tex_comps = texturep->getComponents();
+ tex_stride = tex_width * tex_comps;
+
+ S32 st_comps = 3;
+ S32 st_width = BASE_SIZE;
+ S32 st_height = BASE_SIZE;
+
+ if (tex_comps != st_comps)
+ {
+ llwarns << "Base texture comps != input texture comps" << llendl;
+ return FALSE;
+ }
+
+ tex_x_scalef = (F32)tex_width / (F32)mWidth;
+ tex_y_scalef = (F32)tex_height / (F32)mWidth;
+ tex_x_begin = (S32)((F32)x_begin * tex_x_scalef);
+ tex_y_begin = (S32)((F32)y_begin * tex_y_scalef);
+ tex_x_end = (S32)((F32)x_end * tex_x_scalef);
+ tex_y_end = (S32)((F32)y_end * tex_y_scalef);
+
+ tex_x_ratiof = (F32)mWidth*mScale / (F32)tex_width;
+ tex_y_ratiof = (F32)mWidth*mScale / (F32)tex_height;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(tex_width, tex_height, tex_comps);
+ U8 *rawp = raw->getData();
+
+ F32 tex_width_inv = 1.f/tex_width;
+ F32 tex_height_inv = 1.f/tex_height;
+
+ F32 st_x_stride, st_y_stride;
+ st_x_stride = ((F32)st_width / (F32)mTexScaleX)*((F32)mWidth / (F32)tex_width);
+ st_y_stride = ((F32)st_height / (F32)mTexScaleY)*((F32)mWidth / (F32)tex_height);
+
+ llassert(st_x_stride > 0.f);
+ llassert(st_y_stride > 0.f);
+ ////////////////////////////////
+ //
+ // Iterate through the target texture, striding through the
+ // subtextures and interpolating appropriately.
+ //
+ //
+
+ F32 sti, stj;
+ S32 st_offset;
+ sti = (tex_x_begin * st_x_stride) - st_width*(llfloor((tex_x_begin * st_x_stride)/st_width));
+ stj = (tex_y_begin * st_y_stride) - st_height*(llfloor((tex_y_begin * st_y_stride)/st_height));
+
+ st_offset = (llfloor(stj * st_width) + llfloor(sti)) * st_comps;
+ for (S32 j = tex_y_begin; j < tex_y_end; j++)
+ {
+ U32 offset = j * tex_stride + tex_x_begin * tex_comps;
+ sti = (tex_x_begin * st_x_stride) - st_width*((U32)(tex_x_begin * st_x_stride)/st_width);
+ for (S32 i = tex_x_begin; i < tex_x_end; i++)
+ {
+ S32 tex0, tex1;
+ F32 composition = getValueScaled(i*tex_x_ratiof, j*tex_y_ratiof);
+
+ tex0 = llfloor( composition );
+ tex0 = llclamp(tex0, 0, 3);
+ composition -= tex0;
+ tex1 = tex0 + 1;
+ tex1 = llclamp(tex1, 0, 3);
+
+ F32 xy_int_i, xy_int_j;
+
+ xy_int_i = i * tex_width_inv;
+ xy_int_j = j * tex_height_inv;
+
+ st_offset = (lltrunc(sti) + lltrunc(stj)*st_width) * st_comps;
+ for (U32 k = 0; k < tex_comps; k++)
+ {
+ // Linearly interpolate based on composition.
+ F32 a = *(st_data[tex0] + st_offset);
+ F32 b = *(st_data[tex1] + st_offset);
+ rawp[ offset ] = (U8)lltrunc( a + composition * (b - a) );
+ offset++;
+ st_offset++;
+ }
+
+ sti += st_x_stride;
+ if (sti >= st_width)
+ {
+ sti -= st_width;
+ }
+ }
+
+ stj += st_y_stride;
+ if (stj >= st_height)
+ {
+ stj -= st_height;
+ }
+ }
+
+ texturep->setSubImage(raw, tex_x_begin, tex_y_begin, tex_x_end - tex_x_begin, tex_y_end - tex_y_begin);
+ LLSurface::sTextureUpdateTime += gen_timer.getElapsedTimeF32();
+ LLSurface::sTexelsUpdated += (tex_x_end - tex_x_begin) * (tex_y_end - tex_y_begin);
+
+ for (S32 i = 0; i < 4; i++)
+ {
+ // Un-boost detatil textures (will get re-boosted if rendering in high detail)
+ mDetailTextures[i]->setBoostLevel(LLViewerImage::BOOST_NONE);
+ mDetailTextures[i]->setMinDiscardLevel(MAX_DISCARD_LEVEL + 1);
+ }
+
+ return TRUE;
+}
+
+LLUUID LLVLComposition::getDetailTextureID(S32 corner)
+{
+ return mDetailTextures[corner]->getID();
+}
+
+LLViewerImage* LLVLComposition::getDetailTexture(S32 corner)
+{
+ return mDetailTextures[corner];
+}
+
+F32 LLVLComposition::getStartHeight(S32 corner)
+{
+ return mStartHeight[corner];
+}
+
+void LLVLComposition::setStartHeight(S32 corner, const F32 start_height)
+{
+ mStartHeight[corner] = start_height;
+}
+
+F32 LLVLComposition::getHeightRange(S32 corner)
+{
+ return mHeightRange[corner];
+}
+
+void LLVLComposition::setHeightRange(S32 corner, const F32 range)
+{
+ mHeightRange[corner] = range;
+}
diff --git a/indra/newview/llvlcomposition.h b/indra/newview/llvlcomposition.h
new file mode 100644
index 0000000000..1e929f33dd
--- /dev/null
+++ b/indra/newview/llvlcomposition.h
@@ -0,0 +1,67 @@
+/**
+ * @file llvlcomposition.h
+ * @brief Viewer-side representation of a composition layer...
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVLCOMPOSITION_H
+#define LL_LLVLCOMPOSITION_H
+
+#include "llviewerlayer.h"
+#include "llviewerimage.h"
+
+class LLSurface;
+
+class LLVLComposition : public LLViewerLayer
+{
+public:
+ LLVLComposition(LLSurface *surfacep, const U32 width, const F32 scale);
+ /*virtual*/ ~LLVLComposition();
+
+ void setSurface(LLSurface *surfacep);
+
+ // Viewer side hack to generate composition values
+ BOOL generateHeights(const F32 x, const F32 y, const F32 width, const F32 height);
+ BOOL generateComposition();
+ // Generate texture from composition values.
+ BOOL generateTexture(const F32 x, const F32 y, const F32 width, const F32 height);
+
+ // Use these as indeces ito the get/setters below that use 'corner'
+ enum ECorner
+ {
+ SOUTHWEST = 0,
+ SOUTHEAST = 1,
+ NORTHWEST = 2,
+ NORTHEAST = 3,
+ CORNER_COUNT = 4
+ };
+ LLUUID getDetailTextureID(S32 corner);
+ LLViewerImage* getDetailTexture(S32 corner);
+ F32 getStartHeight(S32 corner);
+ F32 getHeightRange(S32 corner);
+
+ void setDetailTextureID(S32 corner, const LLUUID& id);
+ void setStartHeight(S32 corner, F32 start_height);
+ void setHeightRange(S32 corner, F32 range);
+
+ friend class LLVOSurfacePatch;
+ friend class LLDrawPoolTerrain;
+ void setParamsReady() { mParamsReady = TRUE; }
+ BOOL getParamsReady() const { return mParamsReady; }
+protected:
+ BOOL mParamsReady;
+ LLSurface *mSurfacep;
+ BOOL mTexturesLoaded;
+
+ LLPointer<LLViewerImage> mDetailTextures[CORNER_COUNT];
+
+ F32 mStartHeight[CORNER_COUNT];
+ F32 mHeightRange[CORNER_COUNT];
+
+ F32 mTexScaleX;
+ F32 mTexScaleY;
+};
+
+#endif //LL_LLVLCOMPOSITION_H
diff --git a/indra/newview/llvlmanager.cpp b/indra/newview/llvlmanager.cpp
new file mode 100644
index 0000000000..9a5c1e533b
--- /dev/null
+++ b/indra/newview/llvlmanager.cpp
@@ -0,0 +1,147 @@
+/**
+ * @file llvlmanager.cpp
+ * @brief LLVLManager class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvlmanager.h"
+
+#include "indra_constants.h"
+#include "bitpack.h"
+#include "patch_code.h"
+#include "patch_dct.h"
+#include "llviewerregion.h"
+#include "llframetimer.h"
+#include "llagent.h"
+#include "llsurface.h"
+
+LLVLManager gVLManager;
+
+LLVLManager::~LLVLManager()
+{
+ S32 i;
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ delete mPacketData[i];
+ }
+ mPacketData.reset();
+}
+
+void LLVLManager::addLayerData(LLVLData *vl_datap, const S32 mesg_size)
+{
+ if (LAND_LAYER_CODE == vl_datap->mType)
+ {
+ mLandBits += mesg_size * 8;
+ }
+ else if (WIND_LAYER_CODE == vl_datap->mType)
+ {
+ mWindBits += mesg_size * 8;
+ }
+ else if (CLOUD_LAYER_CODE == vl_datap->mType)
+ {
+ mCloudBits += mesg_size * 8;
+ }
+ else
+ {
+ llerrs << "Unknown layer type!" << (S32)vl_datap->mType << llendl;
+ }
+
+ mPacketData.put(vl_datap);
+}
+
+void LLVLManager::unpackData(const S32 num_packets)
+{
+ static LLFrameTimer decode_timer;
+
+ S32 i;
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ LLVLData *datap = mPacketData[i];
+
+ LLBitPack bit_pack(datap->mData, datap->mSize);
+ LLGroupHeader goph;
+
+ decode_patch_group_header(bit_pack, &goph);
+ if (LAND_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->getLand().decompressDCTPatch(bit_pack, &goph, FALSE);
+ }
+ else if (WIND_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->mWind.decompress(bit_pack, &goph);
+
+ }
+ else if (CLOUD_LAYER_CODE == datap->mType)
+ {
+ datap->mRegionp->mCloudLayer.decompress(bit_pack, &goph);
+ }
+ }
+
+ for (i = 0; i < mPacketData.count(); i++)
+ {
+ delete mPacketData[i];
+ }
+ mPacketData.reset();
+
+}
+
+void LLVLManager::resetBitCounts()
+{
+ mLandBits = mWindBits = mCloudBits = 0;
+}
+
+S32 LLVLManager::getLandBits() const
+{
+ return mLandBits;
+}
+
+S32 LLVLManager::getWindBits() const
+{
+ return mWindBits;
+}
+
+S32 LLVLManager::getCloudBits() const
+{
+ return mCloudBits;
+}
+
+S32 LLVLManager::getTotalBytes() const
+{
+ return mLandBits + mWindBits + mCloudBits;
+}
+
+void LLVLManager::cleanupData(LLViewerRegion *regionp)
+{
+ S32 cur = 0;
+ while (cur < mPacketData.count())
+ {
+ if (mPacketData[cur]->mRegionp == regionp)
+ {
+ delete mPacketData[cur];
+ mPacketData.remove(cur);
+ }
+ else
+ {
+ cur++;
+ }
+ }
+}
+
+LLVLData::LLVLData(LLViewerRegion *regionp, const S8 type, U8 *data, const S32 size)
+{
+ mType = type;
+ mData = data;
+ mRegionp = regionp;
+ mSize = size;
+}
+
+LLVLData::~LLVLData()
+{
+ delete [] mData;
+ mData = NULL;
+ mRegionp = NULL;
+}
diff --git a/indra/newview/llvlmanager.h b/indra/newview/llvlmanager.h
new file mode 100644
index 0000000000..98303fa68b
--- /dev/null
+++ b/indra/newview/llvlmanager.h
@@ -0,0 +1,61 @@
+/**
+ * @file llvlmanager.h
+ * @brief LLVLManager class definition
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVLMANAGER_H
+#define LL_LLVLMANAGER_H
+
+// This class manages the data coming in for viewer layers from the network.
+
+#include "stdtypes.h"
+#include "lldarray.h"
+
+class LLVLData;
+class LLViewerRegion;
+
+class LLVLManager
+{
+public:
+ ~LLVLManager();
+
+ void addLayerData(LLVLData *vl_datap, const S32 mesg_size);
+
+ void unpackData(const S32 num_packets = 10);
+
+ S32 getTotalBytes() const;
+
+ S32 getLandBits() const;
+ S32 getWindBits() const;
+ S32 getCloudBits() const;
+
+ void resetBitCounts();
+
+ void cleanupData(LLViewerRegion *regionp);
+protected:
+
+ LLDynamicArray<LLVLData *> mPacketData;
+ U32 mLandBits;
+ U32 mWindBits;
+ U32 mCloudBits;
+};
+
+class LLVLData
+{
+public:
+ LLVLData(LLViewerRegion *regionp,
+ const S8 type, U8 *data, const S32 size);
+ ~LLVLData();
+
+ S8 mType;
+ U8 *mData;
+ S32 mSize;
+ LLViewerRegion *mRegionp;
+};
+
+extern LLVLManager gVLManager;
+
+#endif // LL_LLVIEWERLAYERMANAGER_H
diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp
new file mode 100644
index 0000000000..acec80c95a
--- /dev/null
+++ b/indra/newview/llvoavatar.cpp
@@ -0,0 +1,9426 @@
+/**
+ * @file llvoavatar.cpp
+ * @brief Implementation of LLVOAvatar class which is a derivation fo LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include <algorithm>
+#include <vector>
+#include "llstl.h"
+
+#include "llvoavatar.h"
+
+#include "audioengine.h"
+#include "imageids.h"
+#include "indra_constants.h"
+#include "llchat.h"
+#include "llfontgl.h"
+#include "llprimitive.h"
+#include "lltextureentry.h"
+#include "message.h"
+#include "noise.h"
+#include "sound_ids.h"
+#include "lltimer.h"
+#include "timing.h"
+
+#include "llagent.h" // Get state values from here
+#include "llagparray.h"
+#include "llviewercontrol.h"
+#include "llcriticaldamp.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolmedia.h"
+#include "lldrawpoolbump.h"
+#include "lldriverparam.h"
+#include "lleditingmotion.h"
+#include "llemote.h"
+#include "llface.h"
+#include "llfasttimer.h"
+#include "llfirstuse.h"
+#include "llfloatercustomize.h"
+#include "llfloatertools.h"
+#include "llgldbg.h"
+#include "llhandmotion.h"
+#include "llheadrotmotion.h"
+#include "llhudeffectbeam.h"
+#include "llhudeffectlookat.h"
+#include "llhudeffecttrail.h"
+#include "llhudmanager.h"
+#include "llhudtext.h"
+#include "llinventorymodel.h"
+#include "llinventoryview.h"
+#include "llkeyframefallmotion.h"
+#include "llkeyframemotion.h"
+#include "llkeyframemotionparam.h"
+#include "llkeyframestandmotion.h"
+#include "llkeyframewalkmotion.h"
+#include "llmenugl.h"
+#include "llmutelist.h"
+#include "llnetmap.h"
+#include "llnotify.h"
+#include "llquantize.h"
+#include "llregionhandle.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsprite.h"
+#include "llstatusbar.h"
+#include "lltargetingmotion.h"
+#include "lltexlayer.h"
+#include "lltoolbar.h"
+#include "lltoolgrab.h" // for needsRenderBeam
+#include "lltoolmgr.h" // for needsRenderBeam
+#include "lltoolmorph.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvosky.h"
+#include "llvovolume.h"
+#include "llwearable.h"
+#include "llwearablelist.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+#include "lscript_byteformat.h"
+
+//#include "vtune/vtuneapi.h"
+
+// Direct imports, evil
+extern LLSky gSky;
+extern void set_avatar_character(void* charNameArg);
+extern BOOL gRenderForSelect;
+
+LLXmlTree LLVOAvatar::sXMLTree;
+LLXmlTree LLVOAvatar::sSkeletonXMLTree;
+LLVOAvatarSkeletonInfo* LLVOAvatar::sSkeletonInfo = NULL;
+LLVOAvatarInfo* LLVOAvatar::sAvatarInfo = NULL;
+
+BOOL gDebugAvatarRotation = FALSE;
+
+//extern BOOL gVelocityInterpolate;
+
+//-----------------------------------------------------------------------------
+// Constants
+//-----------------------------------------------------------------------------
+const F32 MIN_PIXEL_AREA_FOR_COMPOSITE = 200.f;
+
+F32 SHADOW_OFFSET_AMT = 0.03f;
+
+#define DELTA_TIME_MIN 0.01f // we clamp measured deltaTime to this
+#define DELTA_TIME_MAX 0.2f // range to insure stability of computations.
+
+const F32 PELVIS_LAG_FLYING = 0.22f;// pelvis follow half life while flying
+
+const F32 PELVIS_LAG_WALKING = 0.4f; // ...while walking
+
+const F32 PELVIS_LAG_MOUSELOOK = 0.15f;
+const F32 MOUSELOOK_PELVIS_FOLLOW_FACTOR = 0.5f;
+
+//Ventrella
+const F32 PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON = 0.0001f; // not zero! - something gets divided by this!
+//end Ventrella
+
+#define PELVIS_ROT_THRESHOLD_SLOW 60.0f // amount of deviation allowed between
+#define PELVIS_ROT_THRESHOLD_FAST 2.0f // the pelvis and the view direction
+ // when moving fast & slow
+
+const F32 MIN_SPEED_PELVIS_FOLLOW = 0.1f;
+
+#define TORSO_NOISE_AMOUNT 1.f // Amount of deviation from up-axis, in degrees
+#define TORSO_NOISE_SPEED 0.2f // Time scale factor on torso noise.
+
+const F32 BREATHE_ROT_MOTION_STRENGTH = 0.05f;
+
+const F32 BREATHE_SCALE_MOTION_STRENGTH = 0.005f;
+
+#define PELVIS_NOISE_FACTOR 0.5f // amount of random noise
+
+#define AUDIO_STEP_PRI 0xC0000000
+#define AUDIO_STEP_LO_SPEED 0.01f // as average speed goes from lo to hi,
+#define AUDIO_STEP_HI_SPEED 3.0f // from lo to hi
+#define AUDIO_STEP_LO_GAIN 0.15f // the resulting gain will ramp linearly
+#define AUDIO_STEP_HI_GAIN 0.15f
+
+const F32 DAMPED_MOTION_TIME_SCALE = 0.15f;
+
+const F32 LOOKAT_CAMERA_DIST_SQUARED = 25.f;
+
+#define AVATAR_HEADER "Linden Avatar 1.0"
+#define AVATAR_SECTION "[avatar]"
+
+#define AVATAR_DEFAULT_CHAR "avatar"
+
+const F32 MIN_SHADOW_HEIGHT = 0.f;
+const F32 MAX_SHADOW_HEIGHT = 0.3f;
+
+#define MIN_REQUIRED_PIXEL_AREA_BODY_NOISE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_BREATHE (10000.f)
+#define MIN_REQUIRED_PIXEL_AREA_PELVIS_FIX (40.f)
+
+const S32 LOCTEX_IMAGE_SIZE_SELF = 512;
+const S32 LOCTEX_IMAGE_AREA_SELF = LOCTEX_IMAGE_SIZE_SELF * LOCTEX_IMAGE_SIZE_SELF;
+const S32 LOCTEX_IMAGE_SIZE_OTHER = LOCTEX_IMAGE_SIZE_SELF / 4; // The size of local textures for other (!mIsSelf) avatars
+const S32 LOCTEX_IMAGE_AREA_OTHER = LOCTEX_IMAGE_SIZE_OTHER * LOCTEX_IMAGE_SIZE_OTHER;
+
+const F32 HEAD_MOVEMENT_AVG_TIME = 0.9f;
+
+const S32 MORPH_MASK_REQUESTED_DISCARD = 0;
+const S32 MIN_PIXEL_AREA_BUMP = 500;
+
+// Discard level at which to switch to baked textures
+// Should probably be 4 or 3, but didn't want to change it while change other logic - SJB
+const S32 SWITCH_TO_BAKED_DISCARD = 5;
+
+const F32 FOOT_COLLIDE_FUDGE = 0.04f;
+
+const F32 HOVER_EFFECT_MAX_SPEED = 3.f;
+const F32 HOVER_EFFECT_STRENGTH = 0.f;
+F32 UNDERWATER_EFFECT_STRENGTH = 0.1f;
+const F32 UNDERWATER_FREQUENCY_DAMP = 0.33f;
+const F32 APPEARANCE_MORPH_TIME = 0.65f;
+const F32 CAMERA_SHAKE_ACCEL_THRESHOLD_SQUARED = 5.f * 5.f;
+const F32 TIME_BEFORE_MESH_CLEANUP = 5.f; // seconds
+const S32 AVATAR_AGP_RELEASE_THRESHOLD = 10; // number of avatar instances before releasing AGP memory
+const F32 FOOT_GROUND_COLLISION_TOLERANCE = 0.25f;
+const F32 AVATAR_LOD_TWEAK_RANGE = 0.7f;
+const S32 MAX_LOD_CHANGES_PER_FRAME = 2;
+const S32 MAX_BUBBLE_CHAT_LENGTH = 1023;
+const S32 MAX_BUBBLE_CHAT_UTTERANCES = 12;
+const F32 CHAT_FADE_TIME = 8.0;
+const F32 BUBBLE_CHAT_TIME = CHAT_FADE_TIME * 3.f;
+const S32 MAX_BUBBLES = 7;
+
+
+const bool USING_VENTRELLA_AVATAR_MOTION_TEST = false;
+
+
+S32 LLVOAvatar::sMaxVisible = 50;
+
+LLVOAvatar::ETextureIndex LLVOAvatar::sBakedTextureIndices[BAKED_TEXTURE_COUNT] =
+{
+ LLVOAvatar::TEX_HEAD_BAKED,
+ LLVOAvatar::TEX_UPPER_BAKED,
+ LLVOAvatar::TEX_LOWER_BAKED,
+ LLVOAvatar::TEX_EYES_BAKED,
+ LLVOAvatar::TEX_SKIRT_BAKED
+};
+
+//-----------------------------------------------------------------------------
+// Utility functions
+//-----------------------------------------------------------------------------
+
+static F32 calc_bouncy_animation(F32 x)
+{
+ return -(cosf(x * F_PI * 2.5f - F_PI_BY_TWO))*(0.4f + x * -0.1f) + x * 1.3f;
+}
+
+//-----------------------------------------------------------------------------
+// Static Data
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::sMaxOtherAvatarsToComposite = 1; // Only this many avatars (other than yourself) can be composited at a time. Set in initClass().
+LLMap< LLGLenum, LLGLuint*> LLVOAvatar::sScratchTexNames;
+LLMap< LLGLenum, F32*> LLVOAvatar::sScratchTexLastBindTime;
+S32 LLVOAvatar::sScratchTexBytes = 0;
+S32 LLVOAvatar::sNumVisibleAvatars = 0;
+S32 LLVOAvatar::sNumLODChangesThisFrame = 0;
+
+LLUUID LLVOAvatar::sStepSoundOnLand = LLUUID("e8af4a28-aa83-4310-a7c4-c047e15ea0df");
+LLUUID LLVOAvatar::sStepSounds[LL_MCODE_END] =
+{
+ LLUUID(SND_STONE_RUBBER),
+ LLUUID(SND_METAL_RUBBER),
+ LLUUID(SND_GLASS_RUBBER),
+ LLUUID(SND_WOOD_RUBBER),
+ LLUUID(SND_FLESH_RUBBER),
+ LLUUID(SND_RUBBER_PLASTIC),
+ LLUUID(SND_RUBBER_RUBBER)
+};
+
+S32 LLVOAvatar::sRenderName = RENDER_NAME_ALWAYS;
+S32 LLVOAvatar::sNumVisibleChatBubbles = 0;
+BOOL LLVOAvatar::sDebugInvisible = FALSE;
+BOOL LLVOAvatar::sShowAttachmentPoints = FALSE;
+BOOL LLVOAvatar::sShowAnimationDebug = FALSE;
+BOOL LLVOAvatar::sShowFootPlane = FALSE;
+BOOL LLVOAvatar::sShowCollisionVolumes = FALSE;
+BOOL LLVOAvatar::sVisibleInFirstPerson = FALSE;
+BOOL LLVOAvatar::sAvatarLoadTest = FALSE;
+F32 LLVOAvatar::sLODFactor = 1.f;
+BOOL LLVOAvatar::sJointDebug = FALSE;
+
+S32 LLVOAvatar::sCurJoint = 0;
+S32 LLVOAvatar::sCurVolume = 0;
+
+struct LLAvatarTexData
+{
+ LLAvatarTexData( const LLUUID& id, LLVOAvatar::ELocTexIndex index )
+ : mAvatarID(id), mIndex(index) {}
+ LLUUID mAvatarID;
+ LLVOAvatar::ELocTexIndex mIndex;
+};
+
+struct LLTextureMaskData
+{
+ LLTextureMaskData( const LLUUID& id )
+ : mAvatarID(id), mLastDiscardLevel(S32_MAX) {}
+ LLUUID mAvatarID;
+ S32 mLastDiscardLevel;
+};
+
+
+//-----------------------------------------------------------------------------
+// class LLBodyNoiseMotion
+//-----------------------------------------------------------------------------
+class LLBodyNoiseMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBodyNoiseMotion(const LLUUID &id) : LLMotion(id) {mName = "body_noise";}
+
+ // Destructor
+ virtual ~LLBodyNoiseMotion() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLBodyNoiseMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::HIGH_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return ADDITIVE_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_BODY_NOISE; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ if( !mTorsoState.setJoint( character->getJoint("mTorso") ))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mTorsoState.setUsage(LLJointState::ROT);
+
+ addJointState( &mTorsoState );
+ return STATUS_SUCCESS;
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ F32 nx[2];
+ nx[0]=time*TORSO_NOISE_SPEED;
+ nx[1]=0.0f;
+ F32 ny[2];
+ ny[0]=0.0f;
+ ny[1]=time*TORSO_NOISE_SPEED;
+ F32 noiseX = noise2(nx);
+ F32 noiseY = noise2(ny);
+
+ F32 rx = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseX / 0.42f;
+ F32 ry = TORSO_NOISE_AMOUNT * DEG_TO_RAD * noiseY / 0.42f;
+ LLQuaternion tQn;
+ tQn.setQuat( rx, ry, 0.0f );
+ mTorsoState.setRotation( tQn );
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mTorsoState;
+};
+
+//-----------------------------------------------------------------------------
+// class LLBreatheMotionRot
+//-----------------------------------------------------------------------------
+class LLBreatheMotionRot :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLBreatheMotionRot(const LLUUID &id) :
+ LLMotion(id),
+ mBreatheRate(1.f),
+ mCharacter(NULL)
+ {
+ mName = "breathe_rot";
+ }
+
+ // Destructor
+ virtual ~LLBreatheMotionRot() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID &id) { return new LLBreatheMotionRot(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.0; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.0; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::MEDIUM_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_BREATHE; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ //Ventrella
+ // I'm replacing the code below because I need to change
+ // the logic in order to add other body parts
+ /*
+ mCharacter = character;
+
+ if (!mChestState.setJoint( character->getJoint("mChest")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mChestState.setUsage(LLJointState::ROT);
+
+ addJointState( &mChestState );
+ return STATUS_SUCCESS;
+ */
+
+
+ bool success = true;
+
+ if ( !mChestState.setJoint( character->getJoint( "mChest" ) ) ) { success = false; }
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ if ( !mNeckState.setJoint ( character->getJoint( "mNeck" )) ) { success = false; }
+
+ if ( !mCollarLeftState.setJoint ( character->getJoint( "mCollarLeft" )) ) { success = false; }
+ if ( !mShoulderLeftState.setJoint ( character->getJoint( "mShoulderLeft" )) ) { success = false; }
+ if ( !mElbowLeftState.setJoint ( character->getJoint( "mElbowLeft" )) ) { success = false; }
+ if ( !mWristLeftState.setJoint ( character->getJoint( "mWristLeft" )) ) { success = false; }
+
+ if ( !mCollarRightState.setJoint ( character->getJoint( "mCollarRight" )) ) { success = false; }
+ if ( !mShoulderRightState.setJoint ( character->getJoint( "mShoulderRight" )) ) { success = false; }
+ if ( !mElbowRightState.setJoint ( character->getJoint( "mElbowRight" )) ) { success = false; }
+ if ( !mWristRightState.setJoint ( character->getJoint( "mWristRight" )) ) { success = false; }
+
+ if ( !mHipLeftState.setJoint ( character->getJoint( "mHipLeft" )) ) { success = false; }
+ if ( !mKneeLeftState.setJoint ( character->getJoint( "mKneeLeft" )) ) { success = false; }
+ if ( !mAnkleLeftState.setJoint ( character->getJoint( "mAnkleLeft" )) ) { success = false; }
+
+ if ( !mHipRightState.setJoint ( character->getJoint( "mHipRight" )) ) { success = false; }
+ if ( !mKneeRightState.setJoint ( character->getJoint( "mKneeRight" )) ) { success = false; }
+ if ( !mAnkleRightState.setJoint ( character->getJoint( "mAnkleRight" )) ) { success = false; }
+ }
+
+ if ( success )
+ {
+ mChestState.setUsage(LLJointState::ROT);
+ addJointState( &mChestState );
+
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ //-------------------------------------------
+ // neck
+ //-------------------------------------------
+ mNeckState.setUsage(LLJointState::ROT);
+ addJointState( &mNeckState );
+
+ //-------------------------------------------
+ // left arm
+ //-------------------------------------------
+ mCollarLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarLeftState );
+
+ mShoulderLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderLeftState );
+
+ mElbowLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowLeftState );
+
+ mWristLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mWristLeftState );
+
+
+ //-------------------------------------------
+ // right arm
+ //-------------------------------------------
+ mCollarRightState.setUsage(LLJointState::ROT);
+ addJointState( &mCollarRightState );
+
+ mShoulderRightState.setUsage(LLJointState::ROT);
+ addJointState( &mShoulderRightState );
+
+ mElbowRightState.setUsage(LLJointState::ROT);
+ addJointState( &mElbowRightState );
+
+ mWristRightState.setUsage(LLJointState::ROT);
+ addJointState( &mWristRightState );
+
+ //-------------------------------------------
+ // left leg
+ //-------------------------------------------
+ mHipLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mHipLeftState );
+
+ mKneeLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeLeftState );
+
+ mAnkleLeftState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleLeftState );
+
+
+ //-------------------------------------------
+ // right leg
+ //-------------------------------------------
+ mHipRightState.setUsage(LLJointState::ROT);
+ addJointState( &mHipRightState );
+
+ mKneeRightState.setUsage(LLJointState::ROT);
+ addJointState( &mKneeRightState );
+
+ mAnkleRightState.setUsage(LLJointState::ROT);
+ addJointState( &mAnkleRightState );
+ }
+ }
+
+ if ( success )
+ {
+ return STATUS_SUCCESS;
+ }
+ else
+ {
+ return STATUS_FAILURE;
+ }
+ //end Ventrella
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mBreatheRate = 1.f;
+
+ F32 breathe_amt = (sinf(mBreatheRate * time) * BREATHE_ROT_MOTION_STRENGTH);
+
+ mChestState.setRotation(LLQuaternion(breathe_amt, LLVector3(0.f, 1.f, 0.f)));
+
+ //Ventrella
+ if ( USING_VENTRELLA_AVATAR_MOTION_TEST )
+ {
+ F32 wave = ( sinf ( time * 2.0f ) * 0.5f );
+
+ mChestState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mShoulderLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristLeftState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mCollarRightState.setRotation ( LLQuaternion( wave, LLVector3( -1.0f, 0.0f, 0.0f ) ) );
+ mShoulderRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+ mElbowRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 0.0f, 1.0f ) ) );
+ mWristRightState.setRotation ( LLQuaternion( wave, LLVector3( 1.0f, 0.0f, 0.0f ) ) );
+
+ mHipLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleLeftState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+
+ mHipRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ mKneeRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, -1.0f, 0.0f ) ) );
+ mAnkleRightState.setRotation ( LLQuaternion( wave, LLVector3( 0.0f, 1.0f, 0.0f ) ) );
+ }
+ //end Ventrella
+
+ return TRUE;
+ }
+
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mChestState;
+
+ //Ventrella
+ LLJointState mNeckState;
+ LLJointState mCollarLeftState;
+ LLJointState mShoulderLeftState;
+ LLJointState mElbowLeftState;
+ LLJointState mWristLeftState;
+ LLJointState mCollarRightState;
+ LLJointState mShoulderRightState;
+ LLJointState mElbowRightState;
+ LLJointState mWristRightState;
+ LLJointState mHipLeftState;
+ LLJointState mKneeLeftState;
+ LLJointState mAnkleLeftState;
+ LLJointState mHipRightState;
+ LLJointState mKneeRightState;
+ LLJointState mAnkleRightState;
+ //end Ventrella
+
+ F32 mBreatheRate;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// class LLPelvisFixMotion
+//-----------------------------------------------------------------------------
+class LLPelvisFixMotion :
+ public LLMotion
+{
+public:
+ // Constructor
+ LLPelvisFixMotion(const LLUUID &id) : LLMotion(id), mCharacter(NULL) {mName = "pelvis_fix";}
+
+ // Destructor
+ virtual ~LLPelvisFixMotion() { }
+
+public:
+ //-------------------------------------------------------------------------
+ // functions to support MotionController and MotionRegistry
+ //-------------------------------------------------------------------------
+ // static constructor
+ // all subclasses must implement such a function and register it
+ static LLMotion *create(const LLUUID& id) { return new LLPelvisFixMotion(id); }
+
+public:
+ //-------------------------------------------------------------------------
+ // animation callbacks to be implemented by subclasses
+ //-------------------------------------------------------------------------
+
+ // motions must specify whether or not they loop
+ virtual BOOL getLoop() { return TRUE; }
+
+ // motions must report their total duration
+ virtual F32 getDuration() { return 0.0; }
+
+ // motions must report their "ease in" duration
+ virtual F32 getEaseInDuration() { return 0.5f; }
+
+ // motions must report their "ease out" duration.
+ virtual F32 getEaseOutDuration() { return 0.5f; }
+
+ // motions must report their priority
+ virtual LLJoint::JointPriority getPriority() { return LLJoint::LOW_PRIORITY; }
+
+ virtual LLMotionBlendType getBlendType() { return NORMAL_BLEND; }
+
+ // called to determine when a motion should be activated/deactivated based on avatar pixel coverage
+ virtual F32 getMinPixelArea() { return MIN_REQUIRED_PIXEL_AREA_PELVIS_FIX; }
+
+ // run-time (post constructor) initialization,
+ // called after parameters have been set
+ // must return true to indicate success and be available for activation
+ virtual LLMotionInitStatus onInitialize(LLCharacter *character)
+ {
+ mCharacter = character;
+
+ if (!mPelvisState.setJoint( character->getJoint("mPelvis")))
+ {
+ return STATUS_FAILURE;
+ }
+
+ mPelvisState.setUsage(LLJointState::POS);
+
+ addJointState( &mPelvisState );
+ return STATUS_SUCCESS;
+ }
+
+ // called when a motion is activated
+ // must return TRUE to indicate success, or else
+ // it will be deactivated
+ virtual BOOL onActivate() { return TRUE; }
+
+ // called per time step
+ // must return TRUE while it is active, and
+ // must return FALSE when the motion is completed.
+ virtual BOOL onUpdate(F32 time, U8* joint_mask)
+ {
+ mPelvisState.setPosition(LLVector3::zero);
+
+ return TRUE;
+ }
+
+ // called when a motion is deactivated
+ virtual void onDeactivate() {}
+
+public:
+ //-------------------------------------------------------------------------
+ // joint states to be animated
+ //-------------------------------------------------------------------------
+ LLJointState mPelvisState;
+ LLCharacter* mCharacter;
+};
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar()
+//-----------------------------------------------------------------------------
+LLVOAvatar::LLVOAvatar(
+ const LLUUID& id,
+ const LLPCode pcode,
+ LLViewerRegion* regionp)
+ :
+ LLViewerObject(id, pcode, regionp),
+ mHUDTargetZoom(1.f),
+ mHUDCurZoom(1.f),
+ mLastHeadBakedID( IMG_DEFAULT_AVATAR ),
+ mLastUpperBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastLowerBodyBakedID( IMG_DEFAULT_AVATAR ),
+ mLastEyesBakedID( IMG_DEFAULT_AVATAR ),
+ mLastSkirtBakedID( IMG_DEFAULT_AVATAR ),
+ mIsDummy(FALSE),
+ mSpecialRenderMode(0),
+ mPelvisToFoot(0.f),
+ mLastSkeletonSerialNum( 0 ),
+ mTurning(FALSE),
+ mHeadOffset(),
+ mIsSitting(FALSE),
+ mTimeVisible(),
+ mTyping(FALSE),
+ mMeshValid(FALSE),
+ mVisible(FALSE),
+ mWindFreq(0.f),
+ mRipplePhase( 0.f ),
+ mBelowWater(FALSE),
+ mAppearanceAnimSetByUser(FALSE),
+ mLastAppearanceBlendTime(0.f),
+ mAppearanceAnimating(FALSE),
+ mHeadLayerSet( NULL ),
+ mUpperBodyLayerSet( NULL ),
+ mLowerBodyLayerSet( NULL ),
+ mEyesLayerSet( NULL ),
+ mSkirtLayerSet( NULL ),
+ mRenderPriority(1.0f),
+ mNumAGPVertices(0),
+ mNameString(),
+ mTitle(),
+ mNameAway(FALSE),
+ mNameBusy(FALSE),
+ mNameMute(FALSE),
+ mNameAppearance(FALSE),
+ mLastRegionHandle(0),
+ mRegionCrossingCount(0),
+ mFirstTEMessageReceived( FALSE ),
+ mFirstAppearanceMessageReceived( FALSE ),
+ mHeadBakedLoaded(FALSE),
+ mHeadMaskDiscard(-1),
+ mUpperBakedLoaded(FALSE),
+ mUpperMaskDiscard(-1),
+ mLowerBakedLoaded(FALSE),
+ mLowerMaskDiscard(-1),
+ mEyesBakedLoaded(FALSE),
+ mSkirtBakedLoaded(FALSE),
+ mHeadMaskTexName(0),
+ mUpperMaskTexName(0),
+ mLowerMaskTexName(0),
+ mCulled( FALSE ),
+ mTexSkinColor( NULL ),
+ mTexHairColor( NULL ),
+ mTexEyeColor( NULL )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //VTResume(); // VTune
+
+ lldebugs << "LLVOAvatar Constructor (0x" << this << ") id:" << mID << llendl;
+
+ mPelvisp = NULL;
+
+ for( S32 i=0; i<LOCTEX_NUM_ENTRIES; i++ )
+ {
+ mLocalTextureBaked[i] = FALSE;
+ mLocalTextureDiscard[i] = MAX_DISCARD_LEVEL+1;
+ }
+
+ mDirtyMesh = TRUE; // Dirty geometry, need to regenerate.
+ mShadow0Facep = NULL;
+ mShadow1Facep = NULL;
+ mHeadp = NULL;
+
+ mIsBuilt = FALSE;
+
+ mNumJoints = 0;
+ mSkeleton = NULL;
+ mScreenp = NULL;
+
+ mNumCollisionVolumes = 0;
+ mCollisionVolumes = NULL;
+
+ // set up animation variables
+ mSpeed = 0.f;
+ setAnimationData("Speed", &mSpeed);
+
+ strcpy(mAvatarDefinition, AVATAR_DEFAULT_CHAR);
+
+ if (id == gAgentID)
+ {
+ mIsSelf = TRUE;
+ gAgent.setAvatarObject(this);
+ lldebugs << "Marking avatar as self " << id << llendl;
+ }
+ else
+ {
+ mIsSelf = FALSE;
+ }
+
+ setNumTEs(TEX_NUM_ENTRIES);
+
+ mbCanSelect = TRUE;
+
+ mSignaledAnimations.clear();
+ mPlayingAnimations.clear();
+
+ mWasOnGroundLeft = FALSE;
+ mWasOnGroundRight = FALSE;
+
+ mTimeLast = 0.0f;
+ mSpeedAccum = 0.0f;
+
+ mRippleTimeLast = 0.f;
+
+ mShadowImageID = LLUUID( gViewerArt.getString("foot_shadow.tga"));
+ mShadowImagep = gImageList.getImage(mShadowImageID);
+ mShadowImagep->bind();
+ mShadowImagep->setClamp(TRUE, TRUE);
+
+ mInAir = FALSE;
+
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+
+ //-------------------------------------------------------------------------
+ // initialize joint, mesh and shape members
+ //-------------------------------------------------------------------------
+ mRoot.setName( "mRoot" );
+
+ // skinned mesh objects
+ mHairLOD.setName("mHairLOD");
+ mHairMesh0.setName("mHairMesh0");
+ mHairMesh0.setMeshID(MESH_ID_HAIR);
+ mHairMesh1.setName("mHairMesh1");
+ mHairMesh2.setName("mHairMesh2");
+ mHairMesh3.setName("mHairMesh3");
+ mHairMesh4.setName("mHairMesh4");
+ mHairMesh5.setName("mHairMesh5");
+
+ mHairMesh0.setIsTransparent(TRUE);
+ mHairMesh1.setIsTransparent(TRUE);
+ mHairMesh2.setIsTransparent(TRUE);
+ mHairMesh3.setIsTransparent(TRUE);
+ mHairMesh4.setIsTransparent(TRUE);
+ mHairMesh5.setIsTransparent(TRUE);
+
+ mHeadLOD.setName("mHeadLOD");
+ mHeadMesh0.setName("mHeadMesh0");
+ mHeadMesh0.setMeshID(MESH_ID_HEAD);
+ mHeadMesh1.setName("mHeadMesh1");
+ mHeadMesh2.setName("mHeadMesh2");
+ mHeadMesh3.setName("mHeadMesh3");
+ mHeadMesh4.setName("mHeadMesh4");
+
+ mEyeLashLOD.setName("mEyeLashLOD");
+ mEyeLashMesh0.setName("mEyeLashMesh0");
+ mEyeLashMesh0.setMeshID(MESH_ID_HEAD);
+ mEyeLashMesh0.setIsTransparent(TRUE);
+
+ mUpperBodyLOD.setName("mUpperBodyLOD");
+ mUpperBodyMesh0.setName("mUpperBodyMesh0");
+ mUpperBodyMesh0.setMeshID(MESH_ID_UPPER_BODY);
+ mUpperBodyMesh1.setName("mUpperBodyMesh1");
+ mUpperBodyMesh2.setName("mUpperBodyMesh2");
+ mUpperBodyMesh3.setName("mUpperBodyMesh3");
+ mUpperBodyMesh4.setName("mUpperBodyMesh4");
+
+ mLowerBodyLOD.setName("mLowerBodyLOD");
+ mLowerBodyMesh0.setName("mLowerBodyMesh0");
+ mLowerBodyMesh0.setMeshID(MESH_ID_LOWER_BODY);
+ mLowerBodyMesh1.setName("mLowerBodyMesh1");
+ mLowerBodyMesh2.setName("mLowerBodyMesh2");
+ mLowerBodyMesh3.setName("mLowerBodyMesh3");
+ mLowerBodyMesh4.setName("mLowerBodyMesh4");
+
+ mEyeBallLeftLOD.setName("mEyeBallLeftLOD");
+ mEyeBallLeftMesh0.setName("mEyeBallLeftMesh0");
+ mEyeBallLeftMesh1.setName("mEyeBallLeftMesh1");
+
+ mEyeBallRightLOD.setName("mEyeBallRightLOD");
+ mEyeBallRightMesh0.setName("mEyeBallRightMesh0");
+ mEyeBallRightMesh1.setName("mEyeBallRightMesh1");
+
+ mSkirtLOD.setName("mSkirtLOD");
+ mSkirtMesh0.setName("mSkirtMesh0");
+ mSkirtMesh0.setMeshID(MESH_ID_SKIRT);
+ mSkirtMesh1.setName("mSkirtMesh1");
+ mSkirtMesh2.setName("mSkirtMesh2");
+ mSkirtMesh3.setName("mSkirtMesh3");
+ mSkirtMesh4.setName("mSkirtMesh4");
+
+ mSkirtMesh0.setIsTransparent(TRUE);
+ mSkirtMesh1.setIsTransparent(TRUE);
+ mSkirtMesh2.setIsTransparent(TRUE);
+ mSkirtMesh3.setIsTransparent(TRUE);
+ mSkirtMesh4.setIsTransparent(TRUE);
+
+ // set the pick names for the avatar
+ mHeadMesh0.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh1.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh2.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh3.setPickName( LLViewerJoint::PN_0 );
+ mHeadMesh4.setPickName( LLViewerJoint::PN_0 );
+ mEyeLashMesh0.setPickName( LLViewerJoint::PN_0 );
+
+ mUpperBodyMesh0.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh1.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh2.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh3.setPickName( LLViewerJoint::PN_1 );
+ mUpperBodyMesh4.setPickName( LLViewerJoint::PN_1 );
+
+ mLowerBodyMesh0.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh1.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh2.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh3.setPickName( LLViewerJoint::PN_2 );
+ mLowerBodyMesh4.setPickName( LLViewerJoint::PN_2 );
+
+ mEyeBallLeftMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallLeftMesh1.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh0.setPickName( LLViewerJoint::PN_3 );
+ mEyeBallRightMesh1.setPickName( LLViewerJoint::PN_3 );
+
+ mHairMesh0.setPickName( LLViewerJoint::PN_4);
+ mHairMesh1.setPickName( LLViewerJoint::PN_4);
+ mHairMesh2.setPickName( LLViewerJoint::PN_4);
+ mHairMesh3.setPickName( LLViewerJoint::PN_4);
+ mHairMesh4.setPickName( LLViewerJoint::PN_4);
+ mHairMesh5.setPickName( LLViewerJoint::PN_4);
+
+ mSkirtMesh0.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh1.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh2.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh3.setPickName( LLViewerJoint::PN_5 );
+ mSkirtMesh4.setPickName( LLViewerJoint::PN_5 );
+
+ // material settings
+
+ mEyeBallLeftMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallLeftMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh0.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+ mEyeBallRightMesh1.setSpecular( LLColor4( 1.0f, 1.0f, 1.0f, 1.0f ), 1.f );
+
+ //-------------------------------------------------------------------------
+ // register motions
+ //-------------------------------------------------------------------------
+ if (LLCharacter::sInstances.getLength() == 1)
+ {
+ LLKeyframeMotion::setVFS(gStaticVFS);
+ addMotion( ANIM_AGENT_BUSY, LLNullMotion::create );
+ addMotion( ANIM_AGENT_CROUCH, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_CROUCHWALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_EXPRESS_AFRAID, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_ANGER, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_BORED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_CRY, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_DISDAIN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_EMBARRASSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_FROWN, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_KISS, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_LAUGH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_OPEN_MOUTH, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_REPULSED, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SAD, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SHRUG, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_SURPRISE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TONGUE_OUT, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_TOOTHSMILE, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WINK, LLEmote::create );
+ addMotion( ANIM_AGENT_EXPRESS_WORRY, LLEmote::create );
+ addMotion( ANIM_AGENT_RUN, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_STAND, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_1, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_2, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_3, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STAND_4, LLKeyframeStandMotion::create );
+ addMotion( ANIM_AGENT_STANDUP, LLKeyframeFallMotion::create );
+ addMotion( ANIM_AGENT_TURNLEFT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_TURNRIGHT, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_WALK, LLKeyframeWalkMotion::create );
+
+ // motions without a start/stop bit
+ addMotion( ANIM_AGENT_BODY_NOISE, LLBodyNoiseMotion::create );
+ addMotion( ANIM_AGENT_BREATHE_ROT, LLBreatheMotionRot::create );
+ addMotion( ANIM_AGENT_EDITING, LLEditingMotion::create );
+ addMotion( ANIM_AGENT_EYE, LLEyeMotion::create );
+ addMotion( ANIM_AGENT_FEMALE_WALK, LLKeyframeWalkMotion::create );
+ addMotion( ANIM_AGENT_FLY_ADJUST, LLFlyAdjustMotion::create );
+ addMotion( ANIM_AGENT_HAND_MOTION, LLHandMotion::create );
+ addMotion( ANIM_AGENT_HEAD_ROT, LLHeadRotMotion::create );
+ addMotion( ANIM_AGENT_PELVIS_FIX, LLPelvisFixMotion::create );
+ addMotion( ANIM_AGENT_SIT_FEMALE, LLKeyframeMotion::create );
+ addMotion( ANIM_AGENT_TARGET, LLTargetingMotion::create );
+ addMotion( ANIM_AGENT_WALK_ADJUST, LLWalkAdjustMotion::create );
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+ buildCharacter();
+
+ // preload specific motions here
+ createMotion( ANIM_AGENT_CUSTOMIZE);
+ createMotion( ANIM_AGENT_CUSTOMIZE_DONE);
+
+ //VTPause(); // VTune
+}
+
+
+//------------------------------------------------------------------------
+// LLVOAvatar::~LLVOAvatar()
+//------------------------------------------------------------------------
+LLVOAvatar::~LLVOAvatar()
+{
+ lldebugs << "LLVOAvatar Destructor (0x" << this << ") id:" << mID << llendl;
+
+ if (mIsSelf)
+ {
+ gAgent.setAvatarObject(NULL);
+ }
+
+ mRoot.removeAllChildren();
+
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+
+ delete mScreenp;
+ mScreenp = NULL;
+
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+
+
+ mNumJoints = 0;
+
+ delete mHeadLayerSet;
+ mHeadLayerSet = NULL;
+
+ delete mUpperBodyLayerSet;
+ mUpperBodyLayerSet = NULL;
+
+ delete mLowerBodyLayerSet;
+ mLowerBodyLayerSet = NULL;
+
+ delete mEyesLayerSet;
+ mEyesLayerSet = NULL;
+
+ delete mSkirtLayerSet;
+ mSkirtLayerSet = NULL;
+
+ mAttachmentPoints.deleteAllData();
+
+ delete mTexSkinColor;
+ mTexSkinColor = NULL;
+ delete mTexHairColor;
+ mTexHairColor = NULL;
+ delete mTexEyeColor;
+ mTexEyeColor = NULL;
+
+ std::for_each(mMeshes.begin(), mMeshes.end(), DeletePairedPointer());
+
+ mDead = TRUE;
+
+ // Clean up class data
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ mAnimationSources.clear();
+
+ lldebugs << "LLVOAvatar Destructor end" << llendl;
+}
+
+void LLVOAvatar::markDead()
+{
+ if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ mBeam = NULL;
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOAvatar::isFullyBaked()
+{
+ if (mIsDummy) return TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (isWearingWearableType(WT_SKIRT))
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked && skirt_baked;
+ }
+ else
+ {
+ return head_baked && upper_baked && lower_baked && eyes_baked;
+ }
+}
+
+void LLVOAvatar::deleteLayerSetCaches()
+{
+ if( mHeadLayerSet ) mHeadLayerSet->deleteCaches();
+ if( mUpperBodyLayerSet ) mUpperBodyLayerSet->deleteCaches();
+ if( mLowerBodyLayerSet ) mLowerBodyLayerSet->deleteCaches();
+ if( mEyesLayerSet ) mEyesLayerSet->deleteCaches();
+ if( mSkirtLayerSet ) mSkirtLayerSet->deleteCaches();
+}
+
+// static
+BOOL LLVOAvatar::areAllNearbyInstancesBaked()
+{
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst->isDead() )
+ {
+ continue;
+ }
+ else
+ if( inst->getPixelArea() < MIN_PIXEL_AREA_FOR_COMPOSITE )
+ {
+ return TRUE; // Assumes sInstances is sorted by pixel area.
+ }
+ else
+ if( !inst->isFullyBaked() )
+ {
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+// static
+void LLVOAvatar::dumpScratchTextureByteCount()
+{
+ llinfos << "Scratch Texture GL: " << (sScratchTexBytes/1024) << "KB" << llendl;
+}
+
+// static
+void LLVOAvatar::dumpBakedStatus()
+{
+ LLVector3d camera_pos_global = gAgent.getCameraPositionGlobal();
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ llinfos << "Avatar ";
+
+ LLNameValue* firstname = inst->getNVPair("FirstName");
+ LLNameValue* lastname = inst->getNVPair("LastName");
+
+ if( firstname )
+ {
+ llcont << firstname->getString();
+ }
+ if( lastname )
+ {
+ llcont << " " << lastname->getString();
+ }
+
+ llcont << " " << inst->mID;
+
+ if( inst->isDead() )
+ {
+ llcont << " DEAD ("<< inst->getNumRefs() << " refs)";
+ }
+
+ if( inst->mIsSelf )
+ {
+ llcont << " (self)";
+ }
+
+
+ F64 dist_to_camera = (inst->getPositionGlobal() - camera_pos_global).magVec();
+ llcont << " " << dist_to_camera << "m ";
+
+ llcont << " " << inst->mPixelArea << " pixels";
+
+ if( inst->isVisible() )
+ {
+ llcont << " (visible)";
+ }
+ else
+ {
+ llcont << " (not visible)";
+ }
+
+ if( inst->isFullyBaked() )
+ {
+ llcont << " Baked";
+ }
+ else
+ {
+ llcont << " Unbaked (";
+ if( inst->getTEImage( TEX_HEAD_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " head";
+ }
+
+ if( inst->getTEImage( TEX_UPPER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " upper";
+ }
+
+ if( inst->getTEImage( TEX_LOWER_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " lower";
+ }
+
+ if( inst->getTEImage( TEX_EYES_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " eyes";
+ }
+
+ if (inst->isWearingWearableType(WT_SKIRT))
+ {
+ if( inst->getTEImage( TEX_SKIRT_BAKED )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llcont << " skirt";
+ }
+ }
+
+ llcont << " ) " << inst->getUnbakedPixelAreaRank() << "/" << LLVOAvatar::sMaxOtherAvatarsToComposite;
+ if( inst->isCulled() )
+ {
+ llcont << " culled";
+ }
+ }
+ llcont << llendl;
+/*
+ if( inst->isDead() )
+ {
+ llinfos << "DEAD LIST " << llendl;
+
+
+ for( S32 i = 0; i < inst->mOwners.count(); i++ )
+ {
+ llinfos << i << llendl;
+ LLPointer<LLViewerObject>* owner = (LLPointer<LLViewerObject>*)(inst->mOwners[i]);
+ LLPointer<LLViewerObject>* cur;
+ if( !owner->mName.isEmpty() )
+ {
+ llinfos << " " << owner->mName << llendl;
+ }
+
+ LLViewerObject* key_vo;
+ for( key_vo = gObjectList.mActiveObjects.getFirstKey(); key_vo; key_vo = gObjectList.mActiveObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mActiveObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mActiveObjects" << llendl;
+ }
+ }
+
+ for( key_vo = gObjectList.mAvatarObjects.getFirstKey(); key_vo; key_vo = gObjectList.mAvatarObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mAvatarObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mAvatarObjects" << llendl;
+ }
+ }
+
+ LLUUID id;
+ for( id = gObjectList.mDeadObjects.getFirstKey(); id; id = gObjectList.mDeadObjects.getNextKey() )
+ {
+ cur = &(gObjectList.mDeadObjects.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mDeadObjects" << llendl;
+ }
+ }
+
+
+ for( id = gObjectList.mUUIDObjectMap.getFirstKey(); id; id = gObjectList.mUUIDObjectMap.getNextKey() )
+ {
+ cur = &(gObjectList.mUUIDObjectMap.getCurrentDataWithoutIncrement());
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mUUIDObjectMap" << llendl;
+ }
+ }
+
+ S32 j;
+ S32 k;
+ for( j = 0; j < 16; j++ )
+ {
+ for( k = 0; k < 10; k++ )
+ {
+ cur = &(gObjectList.mCloseObjects[j][k]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mCloseObjects" << llendl;
+ }
+ }
+ }
+
+ for( j = 0; j < gObjectList.mObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mObjects" << llendl;
+ }
+ }
+
+ for( j = 0; j < gObjectList.mMapObjects.count(); j++ )
+ {
+ cur = &(gObjectList.mMapObjects[j]);
+ if( cur == owner )
+ {
+ llinfos << " gObjectList.mMapObjects" << llendl;
+ }
+ }
+ }
+ }
+ */
+ }
+}
+
+//static
+void LLVOAvatar::cleanupVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::initVertexPrograms()
+{
+}
+
+//static
+void LLVOAvatar::restoreGL()
+{
+ LLVOAvatar* inst;
+ for( inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->setCompositeUpdatesEnabled( TRUE );
+ inst->invalidateComposite( inst->mHeadLayerSet, FALSE );
+ inst->invalidateComposite( inst->mLowerBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mUpperBodyLayerSet, FALSE );
+ inst->invalidateComposite( inst->mEyesLayerSet, FALSE );
+ inst->invalidateComposite( inst->mSkirtLayerSet, FALSE );
+ inst->updateMeshTextures();
+ }
+}
+
+//static
+void LLVOAvatar::destroyGL()
+{
+ deleteCachedImages();
+ cleanupVertexPrograms();
+}
+
+// static
+void LLVOAvatar::deleteCachedImages()
+{
+ if (LLTexLayerSet::sHasCaches)
+ {
+ lldebugs << "Deleting layer set caches" << llendl;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ inst->deleteLayerSetCaches();
+ }
+ LLTexLayerSet::sHasCaches = FALSE;
+ }
+
+ for( GLuint* namep = (GLuint*)sScratchTexNames.getFirstData();
+ namep;
+ namep = (GLuint*)sScratchTexNames.getNextData() )
+ {
+ glDeleteTextures(1, namep );
+ stop_glerror();
+ }
+
+ if( sScratchTexBytes )
+ {
+ lldebugs << "Clearing Scratch Textures " << (sScratchTexBytes/1024) << "KB" << llendl;
+
+ sScratchTexNames.deleteAllData();
+ LLVOAvatar::sScratchTexLastBindTime.deleteAllData();
+ LLImageGL::sGlobalTextureMemory -= sScratchTexBytes;
+ sScratchTexBytes = 0;
+ }
+
+ gTexStaticImageList.deleteCachedImages();
+}
+
+
+//------------------------------------------------------------------------
+// static
+// LLVOAvatar::initClass()
+//------------------------------------------------------------------------
+void LLVOAvatar::initClass()
+{
+ LLVOAvatar::sMaxOtherAvatarsToComposite = gSavedSettings.getS32("AvatarCompositeLimit");
+
+ char xmlFile[MAX_PATH];
+
+ sprintf(xmlFile, "%s_lad.xml", gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,AVATAR_DEFAULT_CHAR).c_str());
+ BOOL success = sXMLTree.parseFile( xmlFile, FALSE );
+ if (!success)
+ {
+ llerrs << "Problem reading avatar configuration file:" << xmlFile << llendl;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // <linden_avatar version="1.0"> (root)
+ //-------------------------------------------------------------------------
+ if( !root->hasName( "linden_avatar" ) )
+ {
+ llerrs << "Invalid avatar file header: " << xmlFile << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar file version: " << version << " in file: " << xmlFile << llendl;
+ }
+
+ S32 wearable_def_version = 1;
+ static LLStdStringHandle wearable_definition_version_string = LLXmlTree::addAttributeString("wearable_definition_version");
+ root->getFastAttributeS32( wearable_definition_version_string, wearable_def_version );
+ LLWearable::setCurrentDefinitionVersion( wearable_def_version );
+
+ LLString mesh_file_name;
+
+ LLXmlTreeNode* skeleton_node = root->getChildByName( "skeleton" );
+ if (!skeleton_node)
+ {
+ llerrs << "No skeleton in avatar configuration file: " << xmlFile << llendl;
+ }
+
+ LLString skeleton_file_name;
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if (!skeleton_node->getFastAttributeString(file_name_string, skeleton_file_name))
+ {
+ llerrs << "No file name in skeleton node in avatar config file: " << xmlFile << llendl;
+ }
+
+ std::string skeleton_path;
+ skeleton_path = gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,skeleton_file_name);
+ if (!parseSkeletonFile(skeleton_path))
+ {
+ llerrs << "Error parsing skeleton file: " << skeleton_path << llendl;
+ }
+
+ // Process XML data
+
+ // avatar_skeleton.xml
+ llassert(!sSkeletonInfo);
+ sSkeletonInfo = new LLVOAvatarSkeletonInfo;
+ if (!sSkeletonInfo->parseXml(sSkeletonXMLTree.getRoot()))
+ {
+ llerrs << "Error parsing skeleton XML file" << llendl;
+ }
+ // parse avatar_lad.xml
+ llassert(!sAvatarInfo);
+ sAvatarInfo = new LLVOAvatarInfo;
+ if (!sAvatarInfo->parseXmlSkeletonNode(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlMeshNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlColorNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlLayerNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+ if (!sAvatarInfo->parseXmlDriverNodes(root))
+ {
+ llerrs << "Error parsing skeleton node in avatar XML file" << llendl;
+ }
+}
+
+
+void LLVOAvatar::cleanupClass()
+{
+ delete sAvatarInfo;
+ sAvatarInfo = NULL;
+ delete sSkeletonInfo;
+ sSkeletonInfo = NULL;
+ sSkeletonXMLTree.cleanup();
+ sXMLTree.cleanup();
+}
+
+
+//-----------------------------------------------------------------------------
+// parseSkeletonFile()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::parseSkeletonFile(const LLString& filename)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // parse the file
+ //-------------------------------------------------------------------------
+ BOOL success = sSkeletonXMLTree.parseFile( filename, FALSE );
+
+ if (!success)
+ {
+ llerrs << "Can't parse skeleton file: " << filename << llendl;
+ return FALSE;
+ }
+
+ // now sanity check xml file
+ LLXmlTreeNode* root = sSkeletonXMLTree.getRoot();
+ if (!root)
+ {
+ llerrs << "No root node found in avatar skeleton file: " << filename << llendl;
+ }
+
+ if( !root->hasName( "linden_skeleton" ) )
+ {
+ llerrs << "Invalid avatar skeleton file header: " << filename << llendl;
+ }
+
+ LLString version;
+ static LLStdStringHandle version_string = LLXmlTree::addAttributeString("version");
+ if( !root->getFastAttributeString( version_string, version ) || (version != "1.0") )
+ {
+ llerrs << "Invalid avatar skeleton file version: " << version << " in file: " << filename << llendl;
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setupBone()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::setupBone(LLVOAvatarBoneInfo* info, LLViewerJoint* parent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLViewerJoint* joint = NULL;
+
+ if (info->mIsJoint)
+ {
+ joint = (LLViewerJoint*)getCharacterJoint(sCurJoint);
+ if (!joint)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint->setName( info->mName );
+ }
+ else // collision volume
+ {
+ if (sCurVolume >= (S32)mNumCollisionVolumes)
+ {
+ llwarns << "Too many bones" << llendl;
+ return FALSE;
+ }
+ joint = (LLViewerJoint*)(&mCollisionVolumes[sCurVolume]);
+
+ joint->setName( info->mName );
+ }
+
+ // add to parent
+ if (parent)
+ {
+ parent->addChild( joint );
+ }
+
+ joint->setPosition(info->mPos);
+
+ joint->setRotation(mayaQ(info->mRot.mV[VX], info->mRot.mV[VY],
+ info->mRot.mV[VZ], LLQuaternion::XYZ));
+
+ joint->setScale(info->mScale);
+
+
+ if (info->mIsJoint)
+ {
+ joint->setSkinOffset( info->mPivot );
+ sCurJoint++;
+ }
+ else // collision volume
+ {
+ sCurVolume++;
+ }
+
+ // setup children
+ LLVOAvatarBoneInfo::child_list_t::iterator iter;
+ for (iter = info->mChildList.begin(); iter != info->mChildList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *child_info = *iter;
+ if (!setupBone(child_info, joint))
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// buildSkeleton()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::buildSkeleton(LLVOAvatarSkeletonInfo *info)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // allocate joints
+ //-------------------------------------------------------------------------
+ if (!allocateCharacterJoints(info->mNumBones))
+ {
+ llerrs << "Can't allocate " << info->mNumBones << " joints" << llendl;
+ return FALSE;
+ }
+
+ //-------------------------------------------------------------------------
+ // allocate volumes
+ //-------------------------------------------------------------------------
+ if (info->mNumCollisionVolumes)
+ {
+ if (!allocateCollisionVolumes(info->mNumCollisionVolumes))
+ {
+ llerrs << "Can't allocate " << info->mNumCollisionVolumes << " collision volumes" << llendl;
+ return FALSE;
+ }
+ }
+
+ sCurJoint = 0;
+ sCurVolume = 0;
+
+ LLVOAvatarSkeletonInfo::bone_info_list_t::iterator iter;
+ for (iter = info->mBoneInfoList.begin(); iter != info->mBoneInfoList.end(); iter++)
+ {
+ LLVOAvatarBoneInfo *info = *iter;
+ if (!setupBone(info, NULL))
+ {
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ return FALSE;
+ }
+ }
+
+ // add special-purpose "screen" joint
+ mScreenp = new LLViewerJoint("mScreen", NULL);
+ // for now, put screen at origin, as it is only used during special
+ // HUD rendering mode
+ mScreenp->setWorldPosition(LLVector3::zero);
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::buildCharacter()
+// Deferred initialization and rebuild of the avatar.
+//-----------------------------------------------------------------------------
+extern BOOL gPrintMessagesThisFrame;
+void LLVOAvatar::buildCharacter()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //-------------------------------------------------------------------------
+ // remove all references to our existing skeleton
+ // so we can rebuild it
+ //-------------------------------------------------------------------------
+ flushAllMotions();
+
+ //-------------------------------------------------------------------------
+ // remove all of mRoot's children
+ //-------------------------------------------------------------------------
+ mRoot.removeAllChildren();
+ mIsBuilt = FALSE;
+
+ //-------------------------------------------------------------------------
+ // clear mesh data
+ //-------------------------------------------------------------------------
+ mHairMesh0.setMesh(NULL);
+ mHairMesh1.setMesh(NULL);
+ mHairMesh2.setMesh(NULL);
+ mHairMesh3.setMesh(NULL);
+ mHairMesh4.setMesh(NULL);
+ mHairMesh5.setMesh(NULL);
+
+ mHeadMesh0.setMesh(NULL);
+ mHeadMesh1.setMesh(NULL);
+ mHeadMesh2.setMesh(NULL);
+ mHeadMesh3.setMesh(NULL);
+ mHeadMesh4.setMesh(NULL);
+
+ mEyeLashMesh0.setMesh(NULL);
+
+ mUpperBodyMesh0.setMesh(NULL);
+ mUpperBodyMesh1.setMesh(NULL);
+ mUpperBodyMesh2.setMesh(NULL);
+ mUpperBodyMesh3.setMesh(NULL);
+ mUpperBodyMesh4.setMesh(NULL);
+
+ mLowerBodyMesh0.setMesh(NULL);
+ mLowerBodyMesh1.setMesh(NULL);
+ mLowerBodyMesh2.setMesh(NULL);
+ mLowerBodyMesh3.setMesh(NULL);
+ mLowerBodyMesh4.setMesh(NULL);
+
+ mEyeBallLeftMesh0.setMesh(NULL);
+ mEyeBallLeftMesh1.setMesh(NULL);
+ mEyeBallRightMesh0.setMesh(NULL);
+ mEyeBallRightMesh1.setMesh(NULL);
+
+ mSkirtMesh0.setMesh(NULL);
+ mSkirtMesh1.setMesh(NULL);
+ mSkirtMesh2.setMesh(NULL);
+ mSkirtMesh3.setMesh(NULL);
+ mSkirtMesh4.setMesh(NULL);
+
+ //-------------------------------------------------------------------------
+ // (re)load our skeleton and meshes
+ //-------------------------------------------------------------------------
+ LLTimer timer;
+
+ BOOL status = loadAvatar();
+ stop_glerror();
+
+ if (gNoRender)
+ {
+ // Still want to load the avatar skeleton so visual parameters work.
+ return;
+ }
+
+ gPrintMessagesThisFrame = TRUE;
+ lldebugs << "Avatar load took " << timer.getElapsedTimeF32() << " seconds." << llendl;
+
+ if ( ! status )
+ {
+ if ( mIsSelf )
+ {
+ llerrs << "Unable to load user's avatar" << llendl;
+ //set_avatar_character( &LLString(AVATAR_DEFAULT_CHAR));
+ }
+ else
+ {
+ llwarns << "Unable to load other's avatar" << llendl;
+ }
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize "well known" joint pointers
+ //-------------------------------------------------------------------------
+ mPelvisp = (LLViewerJoint*)mRoot.findJoint("mPelvis");
+ mTorsop = (LLViewerJoint*)mRoot.findJoint("mTorso");
+ mChestp = (LLViewerJoint*)mRoot.findJoint("mChest");
+ mNeckp = (LLViewerJoint*)mRoot.findJoint("mNeck");
+ mHeadp = (LLViewerJoint*)mRoot.findJoint("mHead");
+ mSkullp = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ mHipLeftp = (LLViewerJoint*)mRoot.findJoint("mHipLeft");
+ mHipRightp = (LLViewerJoint*)mRoot.findJoint("mHipRight");
+ mKneeLeftp = (LLViewerJoint*)mRoot.findJoint("mKneeLeft");
+ mKneeRightp = (LLViewerJoint*)mRoot.findJoint("mKneeRight");
+ mAnkleLeftp = (LLViewerJoint*)mRoot.findJoint("mAnkleLeft");
+ mAnkleRightp = (LLViewerJoint*)mRoot.findJoint("mAnkleRight");
+ mFootLeftp = (LLViewerJoint*)mRoot.findJoint("mFootLeft");
+ mFootRightp = (LLViewerJoint*)mRoot.findJoint("mFootRight");
+ mWristLeftp = (LLViewerJoint*)mRoot.findJoint("mWristLeft");
+ mWristRightp = (LLViewerJoint*)mRoot.findJoint("mWristRight");
+ mEyeLeftp = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ mEyeRightp = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+
+ //-------------------------------------------------------------------------
+ // Make sure "well known" pointers exist
+ //-------------------------------------------------------------------------
+ if (!(mPelvisp &&
+ mTorsop &&
+ mChestp &&
+ mNeckp &&
+ mHeadp &&
+ mSkullp &&
+ mHipLeftp &&
+ mHipRightp &&
+ mKneeLeftp &&
+ mKneeRightp &&
+ mAnkleLeftp &&
+ mAnkleRightp &&
+ mFootLeftp &&
+ mFootRightp &&
+ mWristLeftp &&
+ mWristRightp &&
+ mEyeLeftp &&
+ mEyeRightp))
+ {
+ llerrs << "Failed to create avatar." << llendl;
+ }
+
+ //-------------------------------------------------------------------------
+ // initialize the pelvis
+ //-------------------------------------------------------------------------
+ mPelvisp->setPosition( LLVector3(0.0f, 0.0f, 0.0f) );
+
+ //-------------------------------------------------------------------------
+ // set head offset from pelvis
+ //-------------------------------------------------------------------------
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // start default motions
+ //-------------------------------------------------------------------------
+ startMotion( ANIM_AGENT_HEAD_ROT );
+ startMotion( ANIM_AGENT_EYE );
+ startMotion( ANIM_AGENT_BODY_NOISE );
+ startMotion( ANIM_AGENT_BREATHE_ROT );
+ startMotion( ANIM_AGENT_HAND_MOTION );
+ startMotion( ANIM_AGENT_PELVIS_FIX );
+
+ //-------------------------------------------------------------------------
+ // restart any currently active motions
+ //-------------------------------------------------------------------------
+ processAnimationStateChanges();
+
+ mIsBuilt = TRUE;
+ stop_glerror();
+
+ //-------------------------------------------------------------------------
+ // build the attach and detach menus
+ //-------------------------------------------------------------------------
+ if (mIsSelf)
+ {
+ gAttachBodyPartPieMenus[0] = NULL;
+ gAttachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gAttachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gAttachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gAttachBodyPartPieMenus[4] = NULL;
+ gAttachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gAttachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gAttachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ gDetachBodyPartPieMenus[0] = NULL;
+ gDetachBodyPartPieMenus[1] = new LLPieMenu("Right Arm >");
+ gDetachBodyPartPieMenus[2] = new LLPieMenu("Head >");
+ gDetachBodyPartPieMenus[3] = new LLPieMenu("Left Arm >");
+ gDetachBodyPartPieMenus[4] = NULL;
+ gDetachBodyPartPieMenus[5] = new LLPieMenu("Left Leg >");
+ gDetachBodyPartPieMenus[6] = new LLPieMenu("Torso >");
+ gDetachBodyPartPieMenus[7] = new LLPieMenu("Right Leg >");
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ if (gAttachBodyPartPieMenus[i])
+ {
+ gAttachPieMenu->appendMenu( gAttachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachPieMenu->append(item);
+
+ attachment_found = TRUE;
+ break;
+
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gAttachPieMenu->appendSeparator();
+ }
+ }
+
+ if (gDetachBodyPartPieMenus[i])
+ {
+ gDetachPieMenu->appendMenu( gDetachBodyPartPieMenus[i] );
+ }
+ else
+ {
+ BOOL attachment_found = FALSE;
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == i)
+ {
+ gDetachPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ attachment_found = TRUE;
+ break;
+ }
+ }
+
+ if (!attachment_found)
+ {
+ gDetachPieMenu->appendSeparator();
+ }
+ }
+ }
+
+ // add screen attachments
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getGroup() == 8)
+ {
+ LLMenuItemCallGL* item;
+ item = new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar,
+ object_selected_and_point_valid,
+ attachment);
+ gAttachScreenPieMenu->append(item);
+ gDetachScreenPieMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+ }
+ }
+
+ for (S32 pass = 0; pass < 2; pass++)
+ {
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() != (pass == 1))
+ {
+ continue;
+ }
+ gAttachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, &attach_label, attachment));
+ gDetachSubMenu->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, &detach_label, attachment));
+
+ }
+ if (pass == 0)
+ {
+ // put separator between non-hud and hud attachments
+ gAttachSubMenu->appendSeparator();
+ gDetachSubMenu->appendSeparator();
+ }
+ }
+
+ for (S32 group = 0; group < 8; group++)
+ {
+ // skip over groups that don't have sub menus
+ if (!gAttachBodyPartPieMenus[group] || !gDetachBodyPartPieMenus[group])
+ {
+ continue;
+ }
+
+ std::multimap<S32, LLViewerJointAttachment*> attachment_pie_menu_map;
+
+ // gather up all attachment points assigned to this group, and throw into map sorted by pie slice number
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if(attachment->getGroup() == group)
+ {
+ // use multimap to provide a partial order off of the pie slice key
+ attachment_pie_menu_map.insert(std::pair<S32, LLViewerJointAttachment*>(attachment->getPieSlice(), attachment));
+ }
+ }
+
+ // add in requested order to pie menu, inserting separators as necessary
+ std::multimap<S32, LLViewerJointAttachment*>::iterator attach_it;
+ S32 cur_pie_slice = 0;
+ for (attach_it = attachment_pie_menu_map.begin(); attach_it != attachment_pie_menu_map.end(); ++attach_it)
+ {
+ S32 requested_pie_slice = attach_it->first;
+ while (cur_pie_slice < requested_pie_slice)
+ {
+ gAttachBodyPartPieMenus[group]->appendSeparator();
+ gDetachBodyPartPieMenus[group]->appendSeparator();
+ cur_pie_slice++;
+ }
+
+ LLViewerJointAttachment* attachment = attach_it->second;
+
+ gAttachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_attach_to_avatar, object_selected_and_point_valid, attachment));
+ gDetachBodyPartPieMenus[group]->append(new LLMenuItemCallGL(attachment->getName(),
+ &handle_detach_from_avatar, object_attached, attachment));
+
+ cur_pie_slice++;
+ }
+ }
+ }
+
+ mMeshValid = TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// releaseMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::releaseMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (sInstances.getLength() < AVATAR_AGP_RELEASE_THRESHOLD || mIsDummy)
+ {
+ return;
+ }
+
+ //llinfos << "Releasing" << llendl;
+
+ // cleanup mesh data
+ mHairLOD.setValid(FALSE, TRUE);
+ mHeadLOD.setValid(FALSE, TRUE);
+ mEyeLashLOD.setValid(FALSE, TRUE);
+ mUpperBodyLOD.setValid(FALSE, TRUE);
+ mLowerBodyLOD.setValid(FALSE, TRUE);
+ mEyeBallLeftLOD.setValid(FALSE, TRUE);
+ mEyeBallRightLOD.setValid(FALSE, TRUE);
+ mSkirtLOD.setValid(FALSE, TRUE);
+
+ //cleanup AGP data
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+ facep->setSize(0, 0);
+ mNumAGPVertices = 0;
+
+ // You need to reset the vertex data in order to guarantee
+ // that the data is deallocated, AND you start at the 0 index
+ // when you restore the face. We're assuming ONLY ONE FACE per
+ // avatar pool, this logic is broken if that isn't the case!
+ facep->getPool()->resetVertexData(0);
+ }
+
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ }
+ mMeshValid = FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// restoreMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::restoreMeshData()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ //llinfos << "Restoring" << llendl;
+ mMeshValid = TRUE;
+ updateJointLODs();
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ else
+ {
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (!attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ }
+ }
+
+ // force mesh update as LOD might not have changed to trigger this
+ updateMeshData();
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshData()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshData()
+{
+ if (mDrawable.notNull())
+ {
+ LLFace* facep = mDrawable->getFace(0);
+
+ U32 num_vertices = 0;
+
+ // this order is determined by number of LODS
+ // if a mesh earlier in this list changed LODs while a later mesh doesn't,
+ // the later mesh's index offset will be inaccurate
+ mEyeBallLeftLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHeadLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+ mHairLOD.updateFaceSizes(num_vertices, mAdjustedPixelArea);
+
+ if (num_vertices != mNumAGPVertices)
+ {
+ // resize immediately
+// llinfos << "Resizing avatar AGP buffer!" << llendl;
+ facep->getPool()->resetVertexData(num_vertices);
+ facep->setSize(0,0);
+ facep->setSize(num_vertices, 0);
+ mNumAGPVertices = num_vertices;
+ }
+
+ // This is a hack! Avatars have their own pool, so we are detecting
+ // the case of more than one avatar in the pool (thus > 0 instead of >= 0)
+ if (facep->getGeomIndex() > 0)
+ {
+ llerrs << "non-zero geom index: " << facep->getGeomIndex() << " in LLVOAvatar::restoreMeshData" << llendl;
+ }
+
+ mEyeBallLeftLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeBallRightLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mEyeLashLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHeadLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mLowerBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mSkirtLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mUpperBodyLOD.updateFaceData(facep, mAdjustedPixelArea);
+ mHairLOD.updateFaceData(facep, mAdjustedPixelArea, TRUE);
+ }
+}
+
+//------------------------------------------------------------------------
+
+//------------------------------------------------------------------------
+// The viewer can only suggest a good size for the agent,
+// the simulator will keep it inside a reasonable range.
+void LLVOAvatar::computeBodySize()
+{
+ LLVector3 pelvis_scale = mPelvisp->getScale();
+
+ // some of the joints have not been cached
+ LLVector3 skull = mSkullp->getPosition();
+ LLVector3 skull_scale = mSkullp->getScale();
+
+ LLVector3 neck = mNeckp->getPosition();
+ LLVector3 neck_scale = mNeckp->getScale();
+
+ LLVector3 chest = mChestp->getPosition();
+ LLVector3 chest_scale = mChestp->getScale();
+
+ // the rest of the joints have been cached
+ LLVector3 head = mHeadp->getPosition();
+ LLVector3 head_scale = mHeadp->getScale();
+
+ LLVector3 torso = mTorsop->getPosition();
+ LLVector3 torso_scale = mTorsop->getScale();
+
+ LLVector3 hip = mHipLeftp->getPosition();
+ LLVector3 hip_scale = mHipLeftp->getScale();
+
+ LLVector3 knee = mKneeLeftp->getPosition();
+ LLVector3 knee_scale = mKneeLeftp->getScale();
+
+ LLVector3 ankle = mAnkleLeftp->getPosition();
+ LLVector3 ankle_scale = mAnkleLeftp->getScale();
+
+ LLVector3 foot = mFootLeftp->getPosition();
+
+ mPelvisToFoot = hip.mV[VZ] * pelvis_scale.mV[VZ] -
+ knee.mV[VZ] * hip_scale.mV[VZ] -
+ ankle.mV[VZ] * knee_scale.mV[VZ] -
+ foot.mV[VZ] * ankle_scale.mV[VZ];
+
+ mBodySize.mV[VZ] = mPelvisToFoot +
+ // the sqrt(2) correction below is an approximate
+ // correction to get to the top of the head
+ F_SQRT2 * (skull.mV[VZ] * head_scale.mV[VZ]) +
+ head.mV[VZ] * neck_scale.mV[VZ] +
+ neck.mV[VZ] * chest_scale.mV[VZ] +
+ chest.mV[VZ] * torso_scale.mV[VZ] +
+ torso.mV[VZ] * pelvis_scale.mV[VZ];
+
+ // TODO -- measure the real depth and width
+ mBodySize.mV[VX] = DEFAULT_AGENT_DEPTH;
+ mBodySize.mV[VY] = DEFAULT_AGENT_WIDTH;
+
+/* debug spam
+ std::cout << "skull = " << skull << std::endl; // adebug
+ std::cout << "head = " << head << std::endl; // adebug
+ std::cout << "head_scale = " << head_scale << std::endl; // adebug
+ std::cout << "neck = " << neck << std::endl; // adebug
+ std::cout << "neck_scale = " << neck_scale << std::endl; // adebug
+ std::cout << "chest = " << chest << std::endl; // adebug
+ std::cout << "chest_scale = " << chest_scale << std::endl; // adebug
+ std::cout << "torso = " << torso << std::endl; // adebug
+ std::cout << "torso_scale = " << torso_scale << std::endl; // adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "pelvis_scale = " << pelvis_scale << std::endl;// adebug
+ std::cout << std::endl; // adebug
+
+ std::cout << "hip = " << hip << std::endl; // adebug
+ std::cout << "hip_scale = " << hip_scale << std::endl; // adebug
+ std::cout << "ankle = " << ankle << std::endl; // adebug
+ std::cout << "ankle_scale = " << ankle_scale << std::endl; // adebug
+ std::cout << "foot = " << foot << std::endl; // adebug
+ std::cout << "mBodySize = " << mBodySize << std::endl; // adebug
+ std::cout << "mPelvisToFoot = " << mPelvisToFoot << std::endl; // adebug
+ std::cout << std::endl; // adebug
+*/
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::processUpdateMessage()
+//------------------------------------------------------------------------
+U32 LLVOAvatar::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 old_vel = getVelocity();
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ //llinfos << getRotation() << llendl;
+ //llinfos << getPosition() << llendl;
+ if (update_type == OUT_FULL )
+ {
+ if( !mIsSelf || !mFirstTEMessageReceived )
+ {
+// dumpAvatarTEs( "PRE processUpdateMessage()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num);
+// dumpAvatarTEs( "POST processUpdateMessage()" );
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ // Disable updates to composites. We'll decide whether we need to do
+ // any updates after we find out whether this update message has any
+ // "baked" (pre-composited) textures.
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures();
+ setCompositeUpdatesEnabled( TRUE );
+ }
+ }
+
+ return retval;
+}
+
+// virtual
+S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid)
+{
+ // The core setTETexture() method requests images, so we need
+ // to redirect certain avatar texture requests to different sims.
+ if (isTextureIndexBaked(te))
+ {
+ LLHost target_host = getObjectHost();
+ return setTETextureCore(te, uuid, target_host);
+ }
+ else
+ {
+ return setTETextureCore(te, uuid, LLHost::invalid);
+ }
+}
+
+
+// setTEImage
+
+//------------------------------------------------------------------------
+// idleUpdate()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+ LLFastTimer t(LLFastTimer::FTM_AVATAR_UPDATE);
+
+ if (isDead())
+ {
+ llinfos << "Warning! Idle on dead avatar" << llendl;
+ return TRUE;
+ }
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ // force immediate pixel area update on avatars using last frames data (before drawable or camera updates)
+ setPixelAreaAndAngle(gAgent);
+
+ // Update the LOD of the joints
+ static const F32 UPDATE_TIME = .5f;
+ if (mUpdateLODTimer.hasExpired())
+ {
+ mUpdateLODTimer.setTimerExpirySec(UPDATE_TIME * (.75f + frand(0.5f)));
+ updateJointLODs();
+ }
+
+ // force asynchronous drawable update
+ if(mDrawable.notNull() && !gNoRender)
+ {
+ LLFastTimer t(LLFastTimer::FTM_JOINT_UPDATE);
+
+ if (mIsSitting && getParent())
+ {
+ LLViewerObject *root_object = (LLViewerObject*)getRoot();
+ LLDrawable* drawablep = root_object->mDrawable;
+ // if this object hasn't already been updated by another avatar...
+ if (drawablep) // && !drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ if (root_object->isSelected())
+ {
+ gPipeline.updateMoveNormalAsync(drawablep);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(drawablep);
+ }
+ }
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(mDrawable);
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // set alpha flag depending on state
+ //--------------------------------------------------------------------
+
+ if (mIsSelf)
+ {
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ // trigger fidget anims
+ if (isAnyAnimationSignaled(AGENT_STAND_ANIMS, NUM_AGENT_STAND_ANIMS))
+ {
+ agent.fidget();
+ }
+ }
+ else
+ {
+ // Should override the idleUpdate stuff and leave out the angular update part.
+ LLQuaternion rotation = getRotation();
+ LLViewerObject::idleUpdate(agent, world, time);
+ setRotation(rotation);
+ }
+
+ // attach objects that were waiting for a drawable
+ lazyAttach();
+
+ // animate the character
+ // store off last frame's root position to be consistent with camera position
+ LLVector3 root_pos_last = mRoot.getWorldPosition();
+
+ updateCharacter(agent);
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ // HACK!!!!
+ // this is necessary for the floating name text above your head
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ // update attachments positions
+ {
+ LLFastTimer t(LLFastTimer::FTM_ATTACHMENT_UPDATE);
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attachment->getValid())
+ {
+ // if selecting any attachments, update all of them as non-damped
+ if (gSelectMgr->getObjectCount() && gSelectMgr->selectionIsAttachment())
+ {
+ gPipeline.updateMoveNormalAsync(attached_object->mDrawable);
+ }
+ else
+ {
+ gPipeline.updateMoveDampedAsync(attached_object->mDrawable);
+ }
+ attached_object->updateText();
+ }
+ }
+ }
+
+ //force a move if sitting on an active object
+ if (getParent() && ((LLViewerObject*) getParent())->mDrawable->isActive())
+ {
+ gPipeline.markMoved(mDrawable, TRUE);
+ }
+
+ // update morphing params
+ if (mAppearanceAnimating)
+ {
+ ESex avatar_sex = getSex();
+ F32 appearance_anim_time = mAppearanceMorphTimer.getElapsedTimeF32();
+ if (appearance_anim_time >= APPEARANCE_MORPH_TIME)
+ {
+ mAppearanceAnimating = FALSE;
+ for (LLVisualParam *param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->stopAnimating(mAppearanceAnimSetByUser);
+ }
+ }
+ updateVisualParams();
+ if (mIsSelf)
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+ }
+ else
+ {
+ F32 blend_frac = calc_bouncy_animation(appearance_anim_time / APPEARANCE_MORPH_TIME);
+ F32 last_blend_frac = calc_bouncy_animation(mLastAppearanceBlendTime / APPEARANCE_MORPH_TIME);
+ F32 morph_amt;
+ if (last_blend_frac == 1.f)
+ {
+ morph_amt = 1.f;
+ }
+ else
+ {
+ morph_amt = (blend_frac - last_blend_frac) / (1.f - last_blend_frac);
+ }
+
+ LLVisualParam *param;
+
+ // animate only top level params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ if (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE)
+ {
+ param->animate(morph_amt, mAppearanceAnimSetByUser);
+ }
+ }
+
+ // apply all params
+ for (param = getFirstVisualParam();
+ param;
+ param = getNextVisualParam())
+ {
+ param->apply(avatar_sex);
+ }
+
+ mLastAppearanceBlendTime = appearance_anim_time;
+ }
+ dirtyMesh();
+ }
+
+ // update wind effect
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_AVATAR) >= LLDrawPoolAvatar::SHADER_LEVEL_CLOTH))
+ {
+ F32 hover_strength = 0.f;
+ F32 time_delta = mRippleTimer.getElapsedTimeF32() - mRippleTimeLast;
+ mRippleTimeLast = mRippleTimer.getElapsedTimeF32();
+ LLVector3 velocity = getVelocity();
+ F32 speed = velocity.magVec();
+ //RN: velocity varies too much frame to frame for this to work
+ mRippleAccel.clearVec();//lerp(mRippleAccel, (velocity - mLastVel) * time_delta, LLCriticalDamp::getInterpolant(0.02f));
+ mLastVel = velocity;
+ LLVector4 wind;
+ wind.setVec(getRegion()->mWind.getVelocityNoisy(getPositionAgent(), 4.f) - velocity);
+
+ if (mInAir)
+ {
+ hover_strength = HOVER_EFFECT_STRENGTH * llmax(0.f, HOVER_EFFECT_MAX_SPEED - speed);
+ }
+
+ if (mBelowWater)
+ {
+ // TODO: make cloth flow more gracefully when underwater
+ hover_strength += UNDERWATER_EFFECT_STRENGTH;
+ }
+
+ wind.mV[VZ] += hover_strength;
+ wind.normVec();
+
+ wind.mV[VW] = llmin(0.025f + (speed * 0.015f) + hover_strength, 0.5f);
+ F32 interp;
+ if (wind.mV[VW] > mWindVec.mV[VW])
+ {
+ interp = LLCriticalDamp::getInterpolant(0.2f);
+ }
+ else
+ {
+ interp = LLCriticalDamp::getInterpolant(0.4f);
+ }
+ mWindVec = lerp(mWindVec, wind, interp);
+
+ F32 wind_freq = hover_strength + llclamp(8.f + (speed * 0.7f) + (noise1(mRipplePhase) * 4.f), 8.f, 25.f);
+ mWindFreq = lerp(mWindFreq, wind_freq, interp);
+
+ if (mBelowWater)
+ {
+ mWindFreq *= UNDERWATER_FREQUENCY_DAMP;
+ }
+
+ mRipplePhase += (time_delta * mWindFreq);
+ if (mRipplePhase > F_TWO_PI)
+ {
+ mRipplePhase = fmodf(mRipplePhase, F_TWO_PI);
+ }
+ }
+
+ // update chat bubble
+ //--------------------------------------------------------------------
+ // draw text label over characters head
+ //--------------------------------------------------------------------
+ if (mChatTimer.getElapsedTimeF32() > BUBBLE_CHAT_TIME)
+ {
+ mChats.clear();
+ }
+
+ const F32 time_visible = mTimeVisible.getElapsedTimeF32();
+ const F32 NAME_SHOW_TIME = gSavedSettings.getF32("RenderNameShowTime"); // seconds
+ const F32 FADE_DURATION = gSavedSettings.getF32("RenderNameFadeDuration"); // seconds
+ BOOL visible_chat = gSavedSettings.getBOOL("UseChatBubbles") && (mChats.size() || mTyping);
+ BOOL render_name = visible_chat ||
+ (mVisible &&
+ (sRenderName == RENDER_NAME_ALWAYS) ||
+ (sRenderName == RENDER_NAME_FADE && time_visible < NAME_SHOW_TIME));
+ // If it's your own avatar, don't draw in mouselook, and don't
+ // draw if we're specifically hiding our own name.
+ if (mIsSelf)
+ {
+ render_name = render_name
+ && !gAgent.cameraMouselook()
+ && (visible_chat || !gSavedSettings.getBOOL("RenderNameHideSelf"));
+ }
+
+ if ( render_name )
+ {
+ BOOL new_name = FALSE;
+ if (visible_chat != mVisibleChat)
+ {
+ mVisibleChat = visible_chat;
+ new_name = TRUE;
+ }
+
+ // First Calculate Alpha
+ // If alpha > 0, create mNameText if necessary, otherwise delete it
+ {
+ F32 alpha = 0.f;
+ if (mAppAngle > 5.f)
+ {
+ const F32 START_FADE_TIME = NAME_SHOW_TIME - FADE_DURATION;
+ if (!visible_chat && sRenderName == RENDER_NAME_FADE && time_visible > START_FADE_TIME)
+ {
+ alpha = 1.f - (time_visible - START_FADE_TIME) / FADE_DURATION;
+ }
+ else
+ {
+ // ...not fading, full alpha
+ alpha = 1.f;
+ }
+ }
+ else if (mAppAngle > 2.f)
+ {
+ // far away is faded out also
+ alpha = (mAppAngle-2.f)/3.f;
+ }
+
+ if (alpha > 0.f)
+ {
+ if (!mNameText)
+ {
+ mNameText = (LLHUDText *)LLHUDObject::addHUDObject(LLHUDObject::LL_HUD_TEXT);
+ mNameText->setMass(10.f);
+ mNameText->setSourceObject(this);
+ mNameText->setVertAlignment(LLHUDText::ALIGN_VERT_TOP);
+ mNameText->setVisibleOffScreen(TRUE);
+ mNameText->setMaxLines(11);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setUseBubble(TRUE);
+ sNumVisibleChatBubbles++;
+ new_name = TRUE;
+ }
+
+ LLColor4 avatar_name_color = gColors.getColor( "AvatarNameColor" );
+ avatar_name_color.setAlpha(alpha);
+ mNameText->setColor(avatar_name_color);
+
+ LLQuaternion root_rot = mRoot.getWorldRotation();
+ mNameText->setUsePixelSize(TRUE);
+ LLVector3 pixel_right_vec;
+ LLVector3 pixel_up_vec;
+ gCamera->getPixelVectors(root_pos_last, pixel_up_vec, pixel_right_vec);
+ LLVector3 camera_to_av = root_pos_last - gCamera->getOrigin();
+ camera_to_av.normVec();
+ LLVector3 local_camera_at = camera_to_av * ~root_rot;
+ LLVector3 local_camera_up = camera_to_av % gCamera->getLeftAxis();
+ local_camera_up.normVec();
+ local_camera_up = local_camera_up * ~root_rot;
+
+ local_camera_up.scaleVec(mBodySize * 0.5f);
+ local_camera_at.scaleVec(mBodySize * 0.5f);
+
+ LLVector3 name_position = mRoot.getWorldPosition() +
+ (local_camera_up * root_rot) -
+ (projected_vec(local_camera_at * root_rot, camera_to_av));
+ name_position += pixel_up_vec * 15.f;
+ mNameText->setPositionAgent(name_position);
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+ }
+
+ LLNameValue *title = getNVPair("Title");
+ LLNameValue* firstname = getNVPair("FirstName");
+ LLNameValue* lastname = getNVPair("LastName");
+
+ if (mNameText.notNull() && firstname && lastname)
+ {
+ BOOL is_away = mSignaledAnimations.find(ANIM_AGENT_AWAY) != mSignaledAnimations.end();
+ BOOL is_busy = mSignaledAnimations.find(ANIM_AGENT_BUSY) != mSignaledAnimations.end();
+ BOOL is_appearance = mSignaledAnimations.find(ANIM_AGENT_CUSTOMIZE) != mSignaledAnimations.end();
+ BOOL is_muted;
+ if (mIsSelf)
+ {
+ is_muted = FALSE;
+ }
+ else
+ {
+ is_muted = gMuteListp->isMuted(getID());
+ }
+
+ if (mNameString.empty() ||
+ new_name ||
+ (!title && !mTitle.empty()) ||
+ (title && mTitle != title->getString()) ||
+ (is_away != mNameAway || is_busy != mNameBusy || is_muted != mNameMute)
+ || is_appearance != mNameAppearance)
+ {
+ char line[MAX_STRING];
+ if (title && title->getString() && title->getString()[0] != '\0')
+ {
+ strcpy(line, title->getString() );
+ strcat(line, "\n");
+ strcat(line, firstname->getString() );
+ }
+ else
+ {
+ strcpy(line, firstname->getString() );
+ }
+
+ strcat(line, " ");
+ strcat(line, lastname->getString());
+ BOOL need_comma = FALSE;
+
+ if (is_away || is_muted || is_busy)
+ {
+ strcat(line, " (");
+ if (is_away)
+ {
+ strcat(line, "Away");
+ need_comma = TRUE;
+ }
+ if (is_busy)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Busy");
+ need_comma = TRUE;
+ }
+ if (is_muted)
+ {
+ if (need_comma)
+ {
+ strcat(line, ", ");
+ }
+ strcat(line, "Muted");
+ need_comma = TRUE;
+ }
+ strcat(line,")");
+ }
+ if (is_appearance)
+ {
+ strcat(line, "\n(Editing Appearance)");
+ }
+ mNameAway = is_away;
+ mNameBusy = is_busy;
+ mNameMute = is_muted;
+ mNameAppearance = is_appearance;
+ mTitle = title ? title->getString() : "";
+ mNameString = utf8str_to_wstring(line);
+ new_name = TRUE;
+ }
+
+ if (visible_chat)
+ {
+ mNameText->setDropShadow(TRUE);
+ mNameText->setFont(LLFontGL::sSansSerif);
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_LEFT);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS * 2.f, 5.f);
+ if (new_name)
+ {
+ mNameText->setLabel(mNameString);
+ }
+
+ char line[MAX_STRING];
+ line[0] = '\0';
+ std::deque<LLChat>::iterator chat_iter = mChats.begin();
+ mNameText->clearString();
+
+ LLColor4 new_chat = gColors.getColor( "AvatarNameColor" );
+ LLColor4 normal_chat = lerp(new_chat, LLColor4(0.8f, 0.8f, 0.8f, 1.f), 0.7f);
+ LLColor4 old_chat = lerp(normal_chat, LLColor4(0.6f, 0.6f, 0.6f, 1.f), 0.7f);
+ if (mTyping && mChats.size() >= MAX_BUBBLE_CHAT_UTTERANCES)
+ {
+ ++chat_iter;
+ }
+
+ for(; chat_iter != mChats.end(); ++chat_iter)
+ {
+ F32 chat_fade_amt = llclamp((F32)((LLFrameTimer::getElapsedSeconds() - chat_iter->mTime) / CHAT_FADE_TIME), 0.f, 4.f);
+ LLFontGL::StyleFlags style;
+ switch(chat_iter->mChatType)
+ {
+ case CHAT_TYPE_WHISPER:
+ style = LLFontGL::ITALIC;
+ break;
+ case CHAT_TYPE_SHOUT:
+ style = LLFontGL::BOLD;
+ break;
+ default:
+ style = LLFontGL::NORMAL;
+ break;
+ }
+ if (chat_fade_amt < 1.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 0.9f, 1.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(new_chat, normal_chat, u), style);
+ }
+ else if (chat_fade_amt < 2.f)
+ {
+ F32 u = clamp_rescale(chat_fade_amt, 1.9f, 2.f, 0.f, 1.f);
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), lerp(normal_chat, old_chat, u), style);
+ }
+ else if (chat_fade_amt < 3.f)
+ {
+ //FIXME: only remove lines down to minimum number
+ mNameText->addLine(utf8str_to_wstring(chat_iter->mText), old_chat, style);
+ }
+ }
+ mNameText->setVisibleOffScreen(TRUE);
+
+ if (mTyping)
+ {
+ S32 dot_count = (llfloor(mTypingTimer.getElapsedTimeF32() * 3.f) + 2) % 3 + 1;
+ switch(dot_count)
+ {
+ case 1:
+ mNameText->addLine(".", new_chat);
+ break;
+ case 2:
+ mNameText->addLine("..", new_chat);
+ break;
+ case 3:
+ mNameText->addLine("...", new_chat);
+ break;
+ }
+
+ }
+ }
+ else
+ {
+ if (gSavedSettings.getBOOL("SmallAvatarNames"))
+ {
+ mNameText->setFont(LLFontGL::sSansSerif);
+ }
+ else
+ {
+ mNameText->setFont(LLFontGL::sSansSerifBig);
+ }
+ mNameText->setTextAlignment(LLHUDText::ALIGN_TEXT_CENTER);
+ mNameText->setFadeDistance(CHAT_NORMAL_RADIUS, 5.f);
+ mNameText->setVisibleOffScreen(FALSE);
+ if (new_name)
+ {
+ mNameText->setLabel("");
+ mNameText->setString(mNameString);
+ }
+ }
+ }
+ }
+ else if (mNameText)
+ {
+ mNameText->markDead();
+ mNameText = NULL;
+ sNumVisibleChatBubbles--;
+ }
+
+ //--------------------------------------------------------------------
+ // draw tractor beam when editing objects
+ //--------------------------------------------------------------------
+ if (!mIsSelf)
+ {
+ return TRUE;
+ }
+
+ // This is only done for yourself (maybe it should be in the agent?)
+ if (!needsRenderBeam() || !mIsBuilt)
+ {
+ mBeam = NULL;
+ }
+ else if (!mBeam || mBeam->isDead())
+ {
+ // VEFFECT: Tractor Beam
+ mBeam = (LLHUDEffectSpiral *)gHUDManager->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BEAM);
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setSourceObject(this);
+ mBeamTimer.reset();
+ }
+
+ if (!mBeam.isNull())
+ {
+ if (gAgent.mPointAt.notNull())
+ {
+ // get point from pointat effect
+ mBeam->setPositionGlobal(gAgent.mPointAt->getPointAtPosGlobal());
+ mBeam->triggerLocal();
+ }
+ else if (gSelectMgr->getFirstRootObject() &&
+ gSelectMgr->getSelectType() != SELECT_TYPE_HUD)
+ {
+ LLViewerObject* objectp = gSelectMgr->getFirstRootObject();
+ mBeam->setTargetObject(objectp);
+ }
+ else
+ {
+ mBeam->setTargetObject(NULL);
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool->isEditing())
+ {
+ if (tool->getEditingObject())
+ {
+ mBeam->setTargetObject(tool->getEditingObject());
+ }
+ else
+ {
+ mBeam->setPositionGlobal(tool->getEditingPointGlobal());
+ }
+ }
+ else
+ {
+ mBeam->setPositionGlobal(gLastHitNonFloraPosGlobal + gLastHitNonFloraObjectOffset);
+ }
+
+ }
+ if (mBeamTimer.getElapsedTimeF32() > 0.25f)
+ {
+ mBeam->setColor(LLColor4U(gAgent.getEffectColor()));
+ mBeam->setNeedsSendToSim(TRUE);
+ mBeamTimer.reset();
+ }
+ }
+
+ F32 avatar_height = (F32)(getPositionGlobal().mdV[VZ]);
+
+ F32 water_height;
+ water_height = getRegion()->getWaterHeight();
+
+ mBelowWater = avatar_height < water_height;
+
+ return TRUE;
+}
+
+void LLVOAvatar::slamPosition()
+{
+ gAgent.setPositionAgent(getPositionAgent());
+ mRoot.setWorldPosition(getPositionAgent()); // teleport
+ setChanged(TRANSLATED);
+ if (mDrawable.notNull())
+ {
+ gPipeline.updateMoveNormalAsync(mDrawable);
+ }
+ mRoot.updateWorldMatrixChildren();
+}
+
+//------------------------------------------------------------------------
+// updateCharacter()
+// called on both your avatar and other avatars
+//------------------------------------------------------------------------
+void LLVOAvatar::updateCharacter(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // clear debug text
+ mDebugText.clear();
+
+ if (LLVOAvatar::sShowAnimationDebug)
+ {
+ LLString playing_anims;
+ for (LLMotion* motionp = mMotionController.getFirstActiveMotion();
+ motionp;
+ motionp = mMotionController.getNextActiveMotion())
+ {
+ if (motionp->getMinPixelArea() < getPixelArea())
+ {
+ char output[256];
+ if (motionp->getName().empty())
+ {
+ sprintf(output, "%s - %d", motionp->getID().getString().c_str(), (U32)motionp->getPriority());
+ }
+ else
+ {
+ sprintf(output, "%s - %d", motionp->getName().c_str(), (U32)motionp->getPriority());
+ }
+ addDebugText(output);
+ }
+ }
+ }
+
+ if (LLVOAvatar::sJointDebug)
+ {
+ llinfos << getNVPair("FirstName")->getString() << ": joint touches: " << LLJoint::sNumTouches << " updates: " << LLJoint::sNumUpdates << llendl;
+
+ LLJoint::sNumUpdates = 0;
+ LLJoint::sNumTouches = 0;
+ }
+// llinfos << mPixelArea << llendl;
+ if (gNoRender)
+ {
+ // Hack if we're running drones...
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getPositionAgent());
+ }
+ return;
+ }
+
+
+ LLVector3d root_pos_global;
+
+ if (!mIsBuilt)
+ {
+ return;
+ }
+
+ // change animation time quanta based on avatar render load
+ if (!mIsSelf)
+ {
+ F32 time_quantum = clamp_rescale((F32)sInstances.getLength(), 10.f, 35.f, 0.f, 0.25f);
+ F32 pixel_area_scale = clamp_rescale(mPixelArea, 100, 5000, 1.f, 0.f);
+ F32 time_step = time_quantum * pixel_area_scale;
+ if (time_step != 0.f)
+ {
+ // disable walk motion servo controller as it doesn't work with motion timesteps
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ removeAnimationData("Walk Speed");
+ }
+ mMotionController.setTimeStep(time_step);
+// llinfos << "Setting timestep to " << time_quantum * pixel_area_scale << llendl;
+ }
+
+ if (getParent() && !mIsSitting)
+ {
+ sitOnObject((LLViewerObject*)getParent());
+ }
+ else if (!getParent() && mIsSitting && !isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED))
+ {
+ getOffObject();
+ }
+
+ // For fading out the names above heads, only let the timer
+ // run if we're visible.
+ if (mDrawable.notNull() && !mDrawable->isVisible())
+ {
+ mTimeVisible.reset();
+ }
+
+ // update screen joint size
+ mScreenp->setScale(LLVector3(1.f, gCamera->getAspect(), 1.f));
+ mScreenp->updateWorldMatrixChildren();
+
+ //--------------------------------------------------------------------
+ // create local variables in world coords for region position values
+ //--------------------------------------------------------------------
+ F32 speed;
+ LLVector3 normal;
+
+ LLVector3 xyVel = getVelocity();
+ xyVel.mV[VZ] = 0.0f;
+ speed = xyVel.magVec();
+
+ if (!(mIsSitting && getParent()))
+ {
+ //--------------------------------------------------------------------
+ // get timing info
+ // handle initial condition case
+ //--------------------------------------------------------------------
+ F32 animation_time = mAnimTimer.getElapsedTimeF32();
+ if (mTimeLast == 0.0f)
+ {
+ mTimeLast = animation_time;
+
+ // put the pelvis at slaved position/mRotation
+ mRoot.setWorldPosition( getPositionAgent() ); // first frame
+ mRoot.setWorldRotation( getRotation() );
+ }
+
+ //--------------------------------------------------------------------
+ // dont' let dT get larger than 1/5th of a second
+ //--------------------------------------------------------------------
+ F32 deltaTime = animation_time - mTimeLast;
+
+ deltaTime = llclamp( deltaTime, DELTA_TIME_MIN, DELTA_TIME_MAX );
+ mTimeLast = animation_time;
+
+ mSpeedAccum = (mSpeedAccum * 0.95f) + (speed * 0.05f);
+
+ //--------------------------------------------------------------------
+ // compute the position of the avatar's root
+ //--------------------------------------------------------------------
+ LLVector3d root_pos;
+ LLVector3d ground_under_pelvis;
+
+ if (mIsSelf)
+ {
+ gAgent.setPositionAgent(getRenderPosition());
+ }
+
+ root_pos = gAgent.getPosGlobalFromAgent(getRenderPosition());
+
+ resolveHeightGlobal(root_pos, ground_under_pelvis, normal);
+ F32 foot_to_ground = (F32) (root_pos.mdV[VZ] - mPelvisToFoot - ground_under_pelvis.mdV[VZ]);
+ BOOL in_air = ( (!gWorldPointer->getRegionFromPosGlobal(ground_under_pelvis)) ||
+ foot_to_ground > FOOT_GROUND_COLLISION_TOLERANCE);
+
+ if (in_air && !mInAir)
+ {
+ mTimeInAir.reset();
+ }
+ mInAir = in_air;
+
+ // correct for the fact that the pelvis is not necessarily the center
+ // of the agent's physical representation
+ root_pos.mdV[VZ] -= (0.5f * mBodySize.mV[VZ]) - mPelvisToFoot;
+ mRoot.setWorldPosition(gAgent.getPosAgentFromGlobal(root_pos) ); // regular update
+
+ //--------------------------------------------------------------------
+ // Propagate viewer object rotation to root of avatar
+ //--------------------------------------------------------------------
+ if (!isAnyAnimationSignaled(AGENT_NO_ROTATE_ANIMS, NUM_AGENT_NO_ROTATE_ANIMS))
+ {
+ LLQuaternion iQ;
+ LLVector3 upDir( 0.0f, 0.0f, 1.0f );
+
+ // Compute a forward direction vector derived from the primitive rotation
+ // and the velocity vector. When walking or jumping, don't let body deviate
+ // more than 90 from the view, if necessary, flip the velocity vector.
+
+ LLVector3 primDir;
+ if (mIsSelf)
+ {
+ primDir = agent.getAtAxis() - projected_vec(agent.getAtAxis(), agent.getReferenceUpVector());
+ primDir.normVec();
+ }
+ else
+ {
+ primDir = getRotation().getMatrix3().getFwdRow();
+ }
+ LLVector3 velDir = getVelocity();
+ velDir.normVec();
+ if ( mSignaledAnimations.find(ANIM_AGENT_WALK) != mSignaledAnimations.end())
+ {
+ F32 vpD = velDir * primDir;
+ if (vpD < -0.5f)
+ {
+ velDir *= -1.0f;
+ }
+ }
+ LLVector3 fwdDir = lerp(primDir, velDir, clamp_rescale(speed, 0.5f, 2.0f, 0.0f, 1.0f));
+ if (mIsSelf && gAgent.cameraMouselook())
+ {
+ // make sure fwdDir stays in same general direction as primdir
+ if (gAgent.getFlying())
+ {
+ fwdDir = gCamera->getAtAxis();
+ }
+ else
+ {
+ LLVector3 at_axis = gCamera->getAtAxis();
+ LLVector3 up_vector = gAgent.getReferenceUpVector();
+ at_axis -= up_vector * (at_axis * up_vector);
+ at_axis.normVec();
+
+ F32 dot = fwdDir * at_axis;
+ if (dot < 0.f)
+ {
+ fwdDir -= 2.f * at_axis * dot;
+ fwdDir.normVec();
+ }
+ }
+
+ }
+
+ LLQuaternion root_rotation = mRoot.getWorldMatrix().quaternion();
+ F32 root_roll, root_pitch, root_yaw;
+ root_rotation.getEulerAngles(&root_roll, &root_pitch, &root_yaw);
+
+ if (gDebugAvatarRotation)
+ {
+ llinfos << "root_roll " << RAD_TO_DEG * root_roll
+ << " root_pitch " << RAD_TO_DEG * root_pitch
+ << " root_yaw " << RAD_TO_DEG * root_yaw
+ << llendl;
+ }
+
+ // When moving very slow, the pelvis is allowed to deviate from the
+ // forward direction to allow it to hold it's position while the torso
+ // and head turn. Once in motion, it must conform however.
+ BOOL self_in_mouselook = mIsSelf && gAgent.cameraMouselook();
+
+ LLVector3 pelvisDir( mRoot.getWorldMatrix().getFwdRow4().mV );
+ F32 pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, PELVIS_ROT_THRESHOLD_SLOW, PELVIS_ROT_THRESHOLD_FAST);
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_rot_threshold = clamp_rescale(speed, 0.1f, 1.0f, 1.0f, 1.0f);
+ //}
+ //end Ventrella
+
+ if (self_in_mouselook)
+ {
+ pelvis_rot_threshold *= MOUSELOOK_PELVIS_FOLLOW_FACTOR;
+ }
+ pelvis_rot_threshold *= DEG_TO_RAD;
+
+ F32 angle = angle_between( pelvisDir, fwdDir );
+
+ // The avatar's root is allowed to have a yaw that deviates widely
+ // from the forward direction, but if roll or pitch are off even
+ // a little bit we need to correct the rotation.
+ if(root_roll < 1.f * DEG_TO_RAD
+ && root_pitch < 5.f * DEG_TO_RAD)
+ {
+ // smaller correction vector means pelvis follows prim direction more closely
+ if (!mTurning && angle > pelvis_rot_threshold*0.75f)
+ {
+ mTurning = TRUE;
+ }
+
+ // use tighter threshold when turning
+ if (mTurning)
+ {
+ pelvis_rot_threshold *= 0.4f;
+ }
+
+ // am I done turning?
+ if (angle < pelvis_rot_threshold)
+ {
+ mTurning = FALSE;
+ }
+
+ LLVector3 correction_vector = (pelvisDir - fwdDir) * clamp_rescale(angle, pelvis_rot_threshold*0.75f, pelvis_rot_threshold, 1.0f, 0.0f);
+ fwdDir += correction_vector;
+ }
+ else
+ {
+ mTurning = FALSE;
+ }
+
+ // Now compute the full world space rotation for the whole body (wQv)
+ LLVector3 leftDir = upDir % fwdDir;
+ leftDir.normVec();
+ fwdDir = leftDir % upDir;
+ LLQuaternion wQv( fwdDir, leftDir, upDir );
+
+ if (mIsSelf && mTurning)
+ {
+ if ((fwdDir % pelvisDir) * upDir > 0.f)
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_RIGHT);
+ }
+ else
+ {
+ gAgent.setControlFlags(AGENT_CONTROL_TURN_LEFT);
+ }
+ }
+
+ // Set the root rotation, but do so incrementally so that it
+ // lags in time by some fixed amount.
+ //F32 u = LLCriticalDamp::getInterpolant(PELVIS_LAG);
+ F32 pelvis_lag_time = 0.f;
+ if (self_in_mouselook)
+ {
+ pelvis_lag_time = PELVIS_LAG_MOUSELOOK;
+ }
+ else if (mInAir)
+ {
+ pelvis_lag_time = PELVIS_LAG_FLYING;
+ // increase pelvis lag time when moving slowly
+ pelvis_lag_time *= clamp_rescale(mSpeedAccum, 0.f, 15.f, 3.f, 1.f);
+ }
+ else
+ {
+ pelvis_lag_time = PELVIS_LAG_WALKING;
+ }
+
+ //Ventrella
+ //if ( gAgent.getCameraMode() == CAMERA_MODE_FOLLOW )
+ //{
+ // pelvis_lag_time = PELVIS_LAG_WHEN_FOLLOW_CAM_IS_ON;
+ //}
+ //end Ventrella
+
+ F32 u = llclamp((deltaTime / pelvis_lag_time), 0.0f, 1.0f);
+
+ mRoot.setWorldRotation( slerp(u, mRoot.getWorldRotation(), wQv) );
+ }
+ }
+ else if (mDrawable.notNull())
+ {
+ mRoot.setPosition(mDrawable->getPosition());
+ mRoot.setRotation(mDrawable->getRotation());
+ }
+
+ //--------------------------------------------------------------------
+ // the rest should only be done when close enough to see it
+ //--------------------------------------------------------------------
+ if ( !(mIsSitting && getParent()) &&
+ ((mPixelArea < 12.0f) ||
+ (!isVisible() && (mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE))) )
+ {
+ mRoot.setWorldRotation( getRotation() );
+ mRoot.updateWorldMatrixChildren();
+ return;
+ }
+
+ //-------------------------------------------------------------------------
+ // Update character motions
+ //-------------------------------------------------------------------------
+ // store data relevant to motions
+ mSpeed = speed;
+
+ // update animations
+ {
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION);
+ updateMotion();
+ }
+
+ // update head position
+ updateHeadOffset();
+
+ //-------------------------------------------------------------------------
+ // Find the ground under each foot, these are used for a variety
+ // of things that follow
+ //-------------------------------------------------------------------------
+ LLVector3 ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ LLVector3 ankle_right_pos_agent = mFootRightp->getWorldPosition();
+
+ LLVector3 ankle_left_ground_agent = ankle_left_pos_agent;
+ LLVector3 ankle_right_ground_agent = ankle_right_pos_agent;
+ resolveHeightAgent(ankle_left_pos_agent, ankle_left_ground_agent, normal);
+ resolveHeightAgent(ankle_right_pos_agent, ankle_right_ground_agent, normal);
+
+ F32 leftElev = llmax(-0.2f, ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ]);
+ F32 rightElev = llmax(-0.2f, ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ]);
+
+ if (!mIsSitting)
+ {
+ //-------------------------------------------------------------------------
+ // Figure out which foot is on ground
+ //-------------------------------------------------------------------------
+ if (!mInAir)
+ {
+ if ((leftElev < 0.0f) || (rightElev < 0.0f))
+ {
+ ankle_left_pos_agent = mFootLeftp->getWorldPosition();
+ ankle_right_pos_agent = mFootRightp->getWorldPosition();
+ leftElev = ankle_left_pos_agent.mV[VZ] - ankle_left_ground_agent.mV[VZ];
+ rightElev = ankle_right_pos_agent.mV[VZ] - ankle_right_ground_agent.mV[VZ];
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // Generate footstep sounds when feet hit the ground
+ //-------------------------------------------------------------------------
+ const LLUUID AGENT_FOOTSTEP_ANIMS[] = {ANIM_AGENT_WALK, ANIM_AGENT_RUN, ANIM_AGENT_LAND};
+ const S32 NUM_AGENT_FOOTSTEP_ANIMS = sizeof(AGENT_FOOTSTEP_ANIMS) / sizeof(LLUUID);
+
+ if ( gAudiop && isAnyAnimationSignaled(AGENT_FOOTSTEP_ANIMS, NUM_AGENT_FOOTSTEP_ANIMS) )
+ {
+ BOOL playSound = FALSE;
+ LLVector3 foot_pos_agent;
+
+ BOOL onGroundLeft = (leftElev <= 0.05f);
+ BOOL onGroundRight = (rightElev <= 0.05f);
+
+ // did left foot hit the ground?
+ if ( onGroundLeft && !mWasOnGroundLeft )
+ {
+ foot_pos_agent = ankle_left_pos_agent;
+ playSound = TRUE;
+ }
+
+ // did right foot hit the ground?
+ if ( onGroundRight && !mWasOnGroundRight )
+ {
+ foot_pos_agent = ankle_right_pos_agent;
+ playSound = TRUE;
+ }
+
+ mWasOnGroundLeft = onGroundLeft;
+ mWasOnGroundRight = onGroundRight;
+
+ if ( playSound )
+ {
+// F32 gain = clamp_rescale( mSpeedAccum,
+// AUDIO_STEP_LO_SPEED, AUDIO_STEP_HI_SPEED,
+// AUDIO_STEP_LO_GAIN, AUDIO_STEP_HI_GAIN );
+
+ F32 gain = gSavedSettings.getF32("AudioLevelFootsteps");
+ LLUUID& step_sound_id = getStepSound();
+
+ LLVector3d foot_pos_global = gAgent.getPosGlobalFromAgent(foot_pos_agent);
+
+ if (gParcelMgr && gParcelMgr->canHearSound(foot_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ gAudiop->triggerSound(step_sound_id, getID(), gain, foot_pos_global);
+ }
+ }
+ }
+
+ mRoot.updateWorldMatrixChildren();
+
+ if (!mDebugText.size() && mText.notNull())
+ {
+ mText->markDead();
+ mText = NULL;
+ }
+ else if (mDebugText.size())
+ {
+ setDebugText(mDebugText);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateHeadOffset()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateHeadOffset()
+{
+ // since we only care about Z, just grab one of the eyes
+ LLVector3 midEyePt = mEyeLeftp->getWorldPosition();
+ midEyePt -= mDrawable.notNull() ? mDrawable->getWorldPosition() : mRoot.getWorldPosition();
+ midEyePt.mV[VZ] = llmax(-mPelvisToFoot + gCamera->getNear(), midEyePt.mV[VZ]);
+
+ if (mDrawable.notNull())
+ {
+ midEyePt = midEyePt * ~mDrawable->getWorldRotation();
+ }
+ if (mIsSitting)
+ {
+ mHeadOffset = midEyePt;
+ }
+ else
+ {
+ F32 u = llmax(0.f, HEAD_MOVEMENT_AVG_TIME - (1.f / gFPSClamped));
+ mHeadOffset = lerp(midEyePt, mHeadOffset, u);
+ }
+}
+
+//------------------------------------------------------------------------
+// updateVisibility()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateVisibility(BOOL force_invisible)
+{
+ BOOL visible = FALSE;
+
+ if (mIsDummy)
+ {
+ visible = TRUE;
+ }
+ else if (mDrawable.isNull())
+ {
+ visible = FALSE;
+ }
+ else if (!force_invisible)
+ {
+ // calculate avatar distance wrt head
+ LLVector3 pos = mDrawable->getPositionAgent();
+ pos -= gCamera->getOrigin();
+ mDrawable->mDistanceWRTCamera = pos.magVec();
+
+ if (!mDrawable->getSpatialGroup() || mDrawable->getSpatialGroup()->isVisible())
+ {
+ visible = TRUE;
+ }
+ else
+ {
+ visible = FALSE;
+ }
+
+ if( mIsSelf )
+ {
+ if( !gAgent.areWearablesLoaded())
+ {
+ visible = FALSE;
+ }
+ }
+ else
+ if( !mFirstAppearanceMessageReceived )
+ {
+ visible = FALSE;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " updating visiblity" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " updating visiblity" << llendl;
+ }
+
+ if (visible)
+ {
+ llinfos << "Visible" << llendl;
+ }
+ else
+ {
+ llinfos << "Not visible" << llendl;
+ }
+
+ /*if (avatar_in_frustum)
+ {
+ llinfos << "Avatar in frustum" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar not in frustum" << llendl;
+ }*/
+
+ /*if (gCamera->sphereInFrustum(sel_pos_agent, 2.0f))
+ {
+ llinfos << "Sel pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(wrist_right_pos_agent, 0.2f))
+ {
+ llinfos << "Wrist pos visible" << llendl;
+ }
+ if (gCamera->sphereInFrustum(getPositionAgent(), getMaxScale()*2.f))
+ {
+ llinfos << "Agent visible" << llendl;
+ }*/
+ llinfos << "PA: " << getPositionAgent() << llendl;
+ /*llinfos << "SPA: " << sel_pos_agent << llendl;
+ llinfos << "WPA: " << wrist_right_pos_agent << llendl;*/
+ for (LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getObject(0))
+ {
+ if(attachment->getObject(0)->mDrawable->isVisible())
+ {
+ llinfos << attachment->getName() << " visible" << llendl;
+ }
+ else
+ {
+ llinfos << attachment->getName() << " not visible at " << mDrawable->getWorldPosition() << " and radius " << mDrawable->getRadius() << llendl;
+ }
+ }
+ }
+ }
+ }
+
+ if (!visible && mVisible)
+ {
+ mMeshInvisibleTime.reset();
+ }
+
+ if (visible)
+ {
+ if (!mMeshValid)
+ {
+ restoreMeshData();
+ }
+ gPipeline.markVisible(mDrawable);
+ }
+ else
+ {
+ if (mMeshValid && mMeshInvisibleTime.getElapsedTimeF32() > TIME_BEFORE_MESH_CLEANUP)
+ {
+ releaseMeshData();
+ }
+ // this breaks off-screen chat bubbles
+ //if (mNameText)
+ //{
+ // mNameText->markDead();
+ // mNameText = NULL;
+ // sNumVisibleChatBubbles--;
+ //}
+ }
+
+ mVisible = visible;
+}
+
+//------------------------------------------------------------------------
+// updateAllVisibility()
+//------------------------------------------------------------------------
+//static
+void LLVOAvatar::updateAllAvatarVisiblity()
+{
+ LLVOAvatar::sNumVisibleAvatars = 0;
+ LLVOAvatar *avatarp;
+
+ F32 render_priority = (F32)LLVOAvatar::sMaxVisible;
+ for (avatarp = (LLVOAvatar*)sInstances.getFirstData(); avatarp; avatarp = (LLVOAvatar*)sInstances.getNextData())
+ {
+ if (avatarp->isDead())
+ {
+ continue;
+ }
+ if (avatarp->isSelf())
+ {
+ avatarp->mRenderPriority = 1000.f;
+ }
+ else
+ {
+ avatarp->mRenderPriority = render_priority * 10.f; // 500 -> 10
+ if (render_priority > 0.f)
+ {
+ render_priority -= 1.f;
+ }
+ }
+ avatarp->updateVisibility(LLVOAvatar::sNumVisibleAvatars > LLVOAvatar::sMaxVisible);
+
+ if (avatarp->mDrawable.isNull())
+ {
+ llwarns << "Avatar with no drawable" << llendl;
+ }
+ else if (avatarp->mDrawable->isVisible())
+ {
+ LLVOAvatar::sNumVisibleAvatars++;
+ }
+ }
+}
+
+//------------------------------------------------------------------------
+// needsRenderBeam()
+//------------------------------------------------------------------------
+BOOL LLVOAvatar::needsRenderBeam()
+{
+ if (gNoRender)
+ {
+ return FALSE;
+ }
+ LLTool *tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+
+ BOOL is_touching_or_grabbing = (tool == gToolGrab && gToolGrab->isEditing());
+ if (gToolGrab->getEditingObject() &&
+ gToolGrab->getEditingObject()->isAttachment())
+ {
+ // don't render selection beam on hud objects
+ is_touching_or_grabbing = FALSE;
+ }
+ return is_touching_or_grabbing || (mState & AGENT_STATE_EDITING && gSelectMgr->shouldShowSelection());
+}
+
+//-----------------------------------------------------------------------------
+// renderSkinned()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderSkinned(EAvatarRenderPass pass)
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (sDebugInvisible)
+ {
+ LLNameValue* firstname = getNVPair("FirstName");
+ if (firstname)
+ {
+ llinfos << "Avatar " << firstname->getString() << " in render" << llendl;
+ }
+ else
+ {
+ llinfos << "Avatar " << this << " in render" << llendl;
+ }
+ if (!mIsBuilt)
+ {
+ llinfos << "Not built!" << llendl;
+ }
+ else if (!gAgent.needsRenderAvatar())
+ {
+ llinfos << "Doesn't need avatar render!" << llendl;
+ }
+ else
+ {
+ llinfos << "Rendering!" << llendl;
+ }
+ }
+
+ if (!mIsBuilt)
+ {
+ return num_indices;
+ }
+
+ if (mIsSelf && !gAgent.needsRenderAvatar())
+ {
+ return num_indices;
+ }
+
+ // render collision normal
+ if (sShowFootPlane && mDrawable.notNull())
+ {
+ LLVector3 slaved_pos = mDrawable->getPositionAgent();
+ LLVector3 foot_plane_normal(mFootPlane.mV[VX], mFootPlane.mV[VY], mFootPlane.mV[VZ]);
+ F32 dist_from_plane = (slaved_pos * foot_plane_normal) - mFootPlane.mV[VW];
+ LLVector3 collide_point = slaved_pos;
+ collide_point.mV[VZ] -= foot_plane_normal.mV[VZ] * (dist_from_plane + COLLISION_TOLERANCE - FOOT_COLLIDE_FUDGE);
+
+ glBegin(GL_LINES);
+ {
+ F32 SQUARE_SIZE = 0.2f;
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] + SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] + SQUARE_SIZE, collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] - SQUARE_SIZE, collide_point.mV[VY] - SQUARE_SIZE, collide_point.mV[VZ]);
+
+ glVertex3f(collide_point.mV[VX], collide_point.mV[VY], collide_point.mV[VZ]);
+ glVertex3f(collide_point.mV[VX] + mFootPlane.mV[VX], collide_point.mV[VY] + mFootPlane.mV[VY], collide_point.mV[VZ] + mFootPlane.mV[VZ]);
+
+ }glEnd();
+ }
+ //--------------------------------------------------------------------
+ // render all geomety attached to the skeleton
+ //--------------------------------------------------------------------
+ static LLStat render_stat;
+
+ LLViewerJointMesh::sRenderPass = pass;
+
+ if (pass == AVATAR_RENDER_PASS_SINGLE)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_INNER)
+ {
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mHeadLOD.render(mAdjustedPixelArea);
+ }
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ if( isWearingWearableType( WT_SKIRT ) )
+ {
+ glAlphaFunc(GL_GREATER,0.25f);
+ num_indices += mSkirtLOD.render(mAdjustedPixelArea);
+ glAlphaFunc(GL_GREATER,0.01f);
+ }
+
+ if (!mIsSelf || gAgent.needsRenderHead())
+ {
+ num_indices += mEyeLashLOD.render(mAdjustedPixelArea);
+ num_indices += mHairLOD.render(mAdjustedPixelArea);
+ }
+ }
+ else if (pass == AVATAR_RENDER_PASS_CLOTHING_OUTER)
+ {
+ LLViewerJointMesh::sClothingInnerColor = mTexSkinColor->getColor() * 0.5f;
+ LLViewerJointMesh::sClothingMaskImageName = mUpperMaskTexName;
+ num_indices += mUpperBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = mLowerMaskTexName;
+ num_indices += mLowerBodyLOD.render(mAdjustedPixelArea);
+ LLViewerJointMesh::sClothingMaskImageName = 0;
+ }
+
+ LLViewerJointMesh::sRenderPass = AVATAR_RENDER_PASS_SINGLE;
+
+ //llinfos << "Avatar render: " << render_timer.getElapsedTimeF32() << llendl;
+
+ //render_stat.addValue(render_timer.getElapsedTimeF32()*1000.f);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderRigid()
+//-----------------------------------------------------------------------------
+U32 LLVOAvatar::renderRigid()
+{
+ U32 num_indices = 0;
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ if (mIsSelf && (!gAgent.needsRenderAvatar() || !gAgent.needsRenderHead()))
+ {
+ return 0;
+ }
+
+ if (!mIsBuilt)
+ {
+ return 0;
+ }
+
+ num_indices += mEyeBallLeftLOD.render(mAdjustedPixelArea);
+ num_indices += mEyeBallRightLOD.render(mAdjustedPixelArea);
+
+ return num_indices;
+}
+
+//-----------------------------------------------------------------------------
+// renderCollisionVolumes()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::renderCollisionVolumes()
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ mCollisionVolumes[i].render();
+ }
+}
+
+//------------------------------------------------------------------------
+// LLVOAvatar::updateTextures()
+//------------------------------------------------------------------------
+void LLVOAvatar::updateTextures(LLAgent &agent)
+{
+ BOOL render_avatar = TRUE;
+
+ if (mIsDummy || gNoRender)
+ {
+ return;
+ }
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( mIsSelf )
+ {
+ render_avatar = TRUE;
+ }
+ else
+ {
+ render_avatar = isVisible() && !mCulled;
+ }
+
+ // bind the texture so that they'll be decoded
+ // slightly inefficient, we can short-circuit this
+ // if we have to
+ if( render_avatar && !gGLManager.mIsDisabled )
+ {
+ if( head_baked && ! mHeadBakedLoaded )
+ {
+ getTEImage( TEX_HEAD_BAKED )->bind();
+ }
+ if( upper_baked && ! mUpperBakedLoaded )
+ {
+ getTEImage( TEX_UPPER_BAKED )->bind();
+ }
+ if( lower_baked && ! mLowerBakedLoaded )
+ {
+ getTEImage( TEX_LOWER_BAKED )->bind();
+ }
+ if( eyes_baked && ! mEyesBakedLoaded )
+ {
+ getTEImage( TEX_EYES_BAKED )->bind();
+ }
+ if( skirt_baked && ! mSkirtBakedLoaded )
+ {
+ getTEImage( TEX_SKIRT_BAKED )->bind();
+ }
+ }
+
+ /*
+ // JAMESDEBUG
+ if (mIsSelf)
+ {
+ S32 null_count = 0;
+ S32 default_count = 0;
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ const LLTextureEntry* te = getTE(i);
+ if (te)
+ {
+ if (te->getID() == LLUUID::null)
+ {
+ null_count++;
+ }
+ else if (te->getID() == IMG_DEFAULT_AVATAR)
+ {
+ default_count++;
+ }
+ }
+ }
+ llinfos << "JAMESDEBUG my avatar TE null " << null_count << " default " << default_count << llendl;
+ }
+ */
+
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ LLViewerImage *imagep = getTEImage(i);
+ if (imagep)
+ {
+ // Debugging code - maybe non-self avatars are downloading textures?
+ //llinfos << "avatar self " << mIsSelf << " tex " << i
+ // << " decode " << imagep->getDecodePriority()
+ // << " boost " << boost_avatar
+ // << " size " << imagep->getWidth() << "x" << imagep->getHeight()
+ // << " discard " << imagep->getDiscardLevel()
+ // << " desired " << imagep->getDesiredDiscardLevel()
+ // << llendl;
+
+ const LLTextureEntry *te = getTE(i);
+ F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT);
+// BOOL boost_aux = (imagep->needsAux() && (!imagep->mFullWidth || !imagep->mFullHeight));
+ S32 boost_level = mIsSelf ? LLViewerImage::BOOST_AVATAR_BAKED_SELF : LLViewerImage::BOOST_AVATAR_BAKED;
+
+ // Spam if this is a baked texture, not set to default image, without valid host info
+ if (isTextureIndexBaked(i)
+ && imagep->getID() != IMG_DEFAULT_AVATAR
+ && !imagep->getTargetHost().isOk())
+ {
+ llwarns << "LLVOAvatar::updateTextures No host for texture " << imagep->getID()
+ << " for avatar " << (mIsSelf ? "<myself>" : getID().getString())
+ << " on host " << getRegion()->getHost() << llendl;
+ }
+
+ switch( i )
+ {
+ // Head
+ case TEX_HEAD_BODYPAINT:
+ addLocalTextureStats( LOCTEX_HEAD_BODYPAINT, imagep, texel_area_ratio, render_avatar, head_baked );
+ break;
+
+ // Upper
+ case TEX_UPPER_JACKET:
+ addLocalTextureStats( LOCTEX_UPPER_JACKET, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_SHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_SHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_GLOVES:
+ addLocalTextureStats( LOCTEX_UPPER_GLOVES, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ addLocalTextureStats( LOCTEX_UPPER_UNDERSHIRT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ case TEX_UPPER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_UPPER_BODYPAINT, imagep, texel_area_ratio, render_avatar, upper_baked );
+ break;
+
+ // Lower
+ case TEX_LOWER_JACKET:
+ addLocalTextureStats( LOCTEX_LOWER_JACKET, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_PANTS:
+ addLocalTextureStats( LOCTEX_LOWER_PANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SHOES:
+ addLocalTextureStats( LOCTEX_LOWER_SHOES, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_SOCKS:
+ addLocalTextureStats( LOCTEX_LOWER_SOCKS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ addLocalTextureStats( LOCTEX_LOWER_UNDERPANTS, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ case TEX_LOWER_BODYPAINT:
+ addLocalTextureStats( LOCTEX_LOWER_BODYPAINT, imagep, texel_area_ratio, render_avatar, lower_baked );
+ break;
+
+ // Eyes
+ case TEX_EYES_IRIS:
+ addLocalTextureStats( LOCTEX_EYES_IRIS, imagep, texel_area_ratio, render_avatar, eyes_baked );
+ break;
+
+ // Skirt
+ case TEX_SKIRT:
+ addLocalTextureStats( LOCTEX_SKIRT, imagep, texel_area_ratio, render_avatar, skirt_baked );
+ break;
+
+ // Baked
+ case TEX_HEAD_BAKED:
+ if (head_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_UPPER_BAKED:
+ if (upper_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_LOWER_BAKED:
+ if (lower_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_EYES_BAKED:
+ if (eyes_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_SKIRT_BAKED:
+ if (skirt_baked)
+ {
+ imagep->setBoostLevel(boost_level);
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ }
+ break;
+
+ case TEX_HAIR:
+ // Hair is neither a local texture used for baking, nor the output
+ // of the baking process. It's just a texture that happens to be
+ // used to draw avatars. Hence BOOST_AVATAR. JC
+ if (mIsSelf)
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats(mPixelArea, texel_area_ratio);
+ break;
+
+ default:
+ llassert(0);
+ break;
+ }
+ }
+ }
+
+ if( render_avatar )
+ {
+ mShadowImagep->addTextureStats(mPixelArea, 1.f);
+ }
+}
+
+
+void LLVOAvatar::addLocalTextureStats( LLVOAvatar::ELocTexIndex idx, LLViewerImage* imagep,
+ F32 texel_area_ratio, BOOL render_avatar, BOOL covered_by_baked )
+{
+ if (!covered_by_baked &&
+ render_avatar && // always true if mIsSelf
+ mLocalTexture[ idx ].notNull() && mLocalTexture[idx]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ F32 desired_pixels;
+ if( mIsSelf )
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_SELF );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR_SELF);
+ }
+ else
+ {
+ desired_pixels = llmin(mPixelArea, (F32)LOCTEX_IMAGE_AREA_OTHER );
+ imagep->setBoostLevel(LLViewerImage::BOOST_AVATAR);
+ }
+ imagep->addTextureStats( desired_pixels, texel_area_ratio );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// resolveHeight()
+//-----------------------------------------------------------------------------
+
+void LLVOAvatar::resolveHeightAgent(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &out_norm)
+{
+ LLVector3d in_pos_global, out_pos_global;
+
+ in_pos_global = gAgent.getPosGlobalFromAgent(in_pos_agent);
+ resolveHeightGlobal(in_pos_global, out_pos_global, out_norm);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+
+void LLVOAvatar::resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm)
+{
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, start_pt, end_pt, out_pos, out_norm, &obj);
+}
+
+
+void LLVOAvatar::resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm)
+{
+ LLVector3d zVec(0.0f, 0.0f, 0.5f);
+ LLVector3d p0 = inPos + zVec;
+ LLVector3d p1 = inPos - zVec;
+ LLViewerObject *obj;
+ gWorldPointer->resolveStepHeightGlobal(this, p0, p1, outPos, outNorm, &obj);
+ if (!obj)
+ {
+ mStepOnLand = TRUE;
+ mStepMaterial = 0;
+ mStepObjectVelocity.setVec(0.0f, 0.0f, 0.0f);
+ }
+ else
+ {
+ mStepOnLand = FALSE;
+ mStepMaterial = obj->getMaterial();
+
+ // We want the primitive velocity, not our velocity... (which actually subtracts the
+ // step object velocity)
+ LLVector3 angularVelocity = obj->getAngularVelocity();
+ LLVector3 relativePos = gAgent.getPosAgentFromGlobal(outPos) - obj->getPositionAgent();
+
+ LLVector3 linearComponent = angularVelocity % relativePos;
+// llinfos << "Linear Component of Rotation Velocity " << linearComponent << llendl;
+ mStepObjectVelocity = obj->getVelocity() + linearComponent;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// getStepSound()
+//-----------------------------------------------------------------------------
+LLUUID& LLVOAvatar::getStepSound()
+{
+ if ( mStepOnLand )
+ {
+ return sStepSoundOnLand;
+ }
+
+ return sStepSounds[mStepMaterial];
+}
+
+
+//-----------------------------------------------------------------------------
+// processAnimationStateChanges()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAnimationStateChanges()
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_WALK_ANIMS, NUM_AGENT_WALK_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else if (mInAir && !mIsSitting)
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ startMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_WALK_ADJUST);
+ stopMotion(ANIM_AGENT_FLY_ADJUST);
+ }
+
+ if ( isAnyAnimationSignaled(AGENT_GUN_AIM_ANIMS, NUM_AGENT_GUN_AIM_ANIMS) )
+ {
+ startMotion(ANIM_AGENT_TARGET);
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+ }
+ else
+ {
+ stopMotion(ANIM_AGENT_TARGET);
+ startMotion(ANIM_AGENT_BODY_NOISE);
+ }
+
+ // clear all current animations
+ AnimIterator anim_it;
+ for (anim_it = mPlayingAnimations.begin(); anim_it != mPlayingAnimations.end();)
+ {
+ AnimIterator found_anim = mSignaledAnimations.find(anim_it->first);
+
+ // playing, but not signaled, so stop
+ if (found_anim == mSignaledAnimations.end())
+ {
+ processSingleAnimationStateChange(anim_it->first, FALSE);
+ mPlayingAnimations.erase(anim_it++);
+ continue;
+ }
+
+ ++anim_it;
+ }
+
+ // start up all new anims
+ for (anim_it = mSignaledAnimations.begin(); anim_it != mSignaledAnimations.end();)
+ {
+ AnimIterator found_anim = mPlayingAnimations.find(anim_it->first);
+
+ // signaled but not playing, or different sequence id, start motion
+ if (found_anim == mPlayingAnimations.end() || found_anim->second != anim_it->second)
+ {
+ if (processSingleAnimationStateChange(anim_it->first, TRUE))
+ {
+ mPlayingAnimations[anim_it->first] = anim_it->second;
+ ++anim_it;
+ continue;
+ }
+ }
+
+ ++anim_it;
+ }
+
+ // clear source information for animations which have been stopped
+ if (mIsSelf)
+ {
+ AnimSourceIterator source_it = mAnimationSources.begin();
+
+ for (source_it = mAnimationSources.begin(); source_it != mAnimationSources.end();)
+ {
+ if (mSignaledAnimations.find(source_it->second) == mSignaledAnimations.end())
+ {
+ mAnimationSources.erase(source_it++);
+ }
+ else
+ {
+ ++source_it;
+ }
+ }
+ }
+
+ stop_glerror();
+}
+
+
+//-----------------------------------------------------------------------------
+// processSingleAnimationStateChange();
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::processSingleAnimationStateChange( const LLUUID& anim_id, BOOL start )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ BOOL result = FALSE;
+
+ if ( start ) // start animation
+ {
+ if (anim_id == ANIM_AGENT_TYPE)
+ {
+ if (gAudiop)
+ {
+ LLVector3d char_pos_global = gAgent.getPosGlobalFromAgent(getCharacterPosition());
+ if (gParcelMgr && gParcelMgr->canHearSound(char_pos_global)
+ && gMuteListp && !gMuteListp->isMuted(getID()))
+ {
+ // RN: uncomment this to play on typing sound at fixed volume once sound engine is fixed
+ // to support both spatialized and non-spatialized instances of the same sound
+ //if (mIsSelf)
+ //{
+ // gAudiop->triggerSound(LLUUID(gSavedSettings.getString("UISndTyping")), 0.8f);
+ //}
+ //else
+ {
+ LLUUID sound_id = LLUUID(gSavedSettings.getString("UISndTyping"));
+ gAudiop->triggerSound(sound_id, getID(), 1.f, char_pos_global);
+ }
+ }
+ }
+ }
+ else if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = TRUE;
+ }
+
+
+ if (startMotion(anim_id))
+ {
+ result = TRUE;
+ }
+ else
+ {
+ llwarns << "Failed to start motion!" << llendl;
+ }
+ }
+ else //stop animation
+ {
+ if (anim_id == ANIM_AGENT_SIT_GROUND_CONSTRAINED)
+ {
+ mIsSitting = FALSE;
+ }
+ stopMotion(anim_id);
+ result = TRUE;
+ }
+
+ return result;
+}
+
+//-----------------------------------------------------------------------------
+// isAnyAnimationSignaled()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims)
+{
+ for (S32 i = 0; i < num_anims; i++)
+ {
+ if(mSignaledAnimations.find(anim_array[i]) != mSignaledAnimations.end())
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// resetAnimations()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::resetAnimations()
+{
+ LLKeyframeMotion::flushKeyframeCache();
+ flushAllMotions();
+}
+
+//-----------------------------------------------------------------------------
+// startMotion()
+// id is the asset if of the animation to start
+// time_offset is the offset into the animation at which to start playing
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::startMotion(const LLUUID& id, F32 time_offset)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ // start special case female walk for female avatars
+ if (getSex() == SEX_FEMALE)
+ {
+ if (id == ANIM_AGENT_WALK)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_FEMALE_WALK, time_offset);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ return LLCharacter::startMotion(ANIM_AGENT_SIT_FEMALE, time_offset);
+ }
+ }
+
+ if (mIsSelf && id == ANIM_AGENT_AWAY)
+ {
+ gAgent.setAFK();
+ }
+
+ return LLCharacter::startMotion(id, time_offset);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotion()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::stopMotion(const LLUUID& id, BOOL stop_immediate)
+{
+ if (mIsSelf)
+ {
+ if (id == ANIM_AGENT_STAND)
+ {
+ LLAgent::stopFidget();
+ }
+ else if (id == ANIM_AGENT_AWAY)
+ {
+ gAgent.clearAFK();
+ }
+ }
+
+ if (id == ANIM_AGENT_WALK)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_FEMALE_WALK, stop_immediate);
+ }
+ else if (id == ANIM_AGENT_SIT)
+ {
+ LLCharacter::stopMotion(ANIM_AGENT_SIT_FEMALE, stop_immediate);
+ }
+
+ return LLCharacter::stopMotion(id, stop_immediate);
+}
+
+//-----------------------------------------------------------------------------
+// stopMotionFromSource()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::stopMotionFromSource(const LLUUID& source_id)
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ AnimSourceIterator motion_it;
+
+ for(motion_it = mAnimationSources.find(source_id); motion_it != mAnimationSources.end();)
+ {
+ gAgent.sendAnimationRequest( motion_it->second, ANIM_REQUEST_STOP );
+ mAnimationSources.erase(motion_it++);
+ }
+
+ LLViewerObject* object = gObjectList.findObject(source_id);
+ if (object)
+ {
+ object->mFlags &= ~FLAGS_ANIM_SOURCE;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getVolumePos()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getVolumePos(S32 joint_index, LLVector3& volume_offset)
+{
+ if (joint_index > mNumCollisionVolumes)
+ {
+ return LLVector3::zero;
+ }
+
+ return mCollisionVolumes[joint_index].getVolumePos(volume_offset);
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+LLJoint* LLVOAvatar::findCollisionVolume(U32 volume_id)
+{
+ if ((S32)volume_id > mNumCollisionVolumes)
+ {
+ return NULL;
+ }
+
+ return &mCollisionVolumes[volume_id];
+}
+
+//-----------------------------------------------------------------------------
+// findCollisionVolume()
+//-----------------------------------------------------------------------------
+S32 LLVOAvatar::getCollisionVolumeID(std::string &name)
+{
+ for (S32 i = 0; i < mNumCollisionVolumes; i++)
+ {
+ if (mCollisionVolumes[i].getName() == name)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// addDebugText()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::addDebugText(const char* text)
+{
+ mDebugText.append(1, '\n');
+ mDebugText.append(text);
+}
+
+//-----------------------------------------------------------------------------
+// getID()
+//-----------------------------------------------------------------------------
+const LLUUID& LLVOAvatar::getID()
+{
+ return mID;
+}
+
+//-----------------------------------------------------------------------------
+// getJoint()
+//-----------------------------------------------------------------------------
+// RN: avatar joints are multi-rooted to include screen-based attachments
+LLJoint *LLVOAvatar::getJoint( const std::string &name )
+{
+ LLJoint* jointp = NULL;
+ if (mScreenp)
+ {
+ jointp = mScreenp->findJoint(name);
+ }
+ if (!jointp)
+ {
+ jointp = mRoot.findJoint(name);
+ }
+ return jointp;
+}
+
+//-----------------------------------------------------------------------------
+// getCharacterPosition()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterPosition()
+{
+ if (mDrawable.notNull())
+ {
+ return mDrawable->getPositionAgent();
+ }
+ else
+ {
+ return getPositionAgent();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterRotation()
+//-----------------------------------------------------------------------------
+LLQuaternion LLVOAvatar::getCharacterRotation()
+{
+ return getRotation();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterVelocity()
+{
+ return getVelocity() - mStepObjectVelocity;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getCharacterAngularVelocity()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getCharacterAngularVelocity()
+{
+ return getAngularVelocity();
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getGround()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getGround(const LLVector3 &in_pos_agent, LLVector3 &out_pos_agent, LLVector3 &outNorm)
+{
+ LLVector3d z_vec(0.0f, 0.0f, 1.0f);
+ LLVector3d p0_global, p1_global;
+
+ if (gNoRender || mIsDummy)
+ {
+ outNorm.setVec(z_vec);
+ out_pos_agent = in_pos_agent;
+ return;
+ }
+
+ p0_global = gAgent.getPosGlobalFromAgent(in_pos_agent) + z_vec;
+ p1_global = gAgent.getPosGlobalFromAgent(in_pos_agent) - z_vec;
+ LLViewerObject *obj;
+ LLVector3d out_pos_global;
+ gWorldPointer->resolveStepHeightGlobal(this, p0_global, p1_global, out_pos_global, outNorm, &obj);
+ out_pos_agent = gAgent.getPosAgentFromGlobal(out_pos_global);
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getTimeDilation()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getTimeDilation()
+{
+ return mTimeDilation;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPixelArea()
+//-----------------------------------------------------------------------------
+F32 LLVOAvatar::getPixelArea()
+{
+ if (mIsDummy)
+ {
+ return 100000.f;
+ }
+ return mPixelArea;
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getHeadMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getHeadMesh()
+{
+ return mHeadMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getUpperBodyMesh()
+//-----------------------------------------------------------------------------
+LLPolyMesh* LLVOAvatar::getUpperBodyMesh()
+{
+ return mUpperBodyMesh0.getMesh();
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatar::getPosGlobalFromAgent()
+//-----------------------------------------------------------------------------
+LLVector3d LLVOAvatar::getPosGlobalFromAgent(const LLVector3 &position)
+{
+ return gAgent.getPosGlobalFromAgent(position);
+}
+
+//-----------------------------------------------------------------------------
+// getPosAgentFromGlobal()
+//-----------------------------------------------------------------------------
+LLVector3 LLVOAvatar::getPosAgentFromGlobal(const LLVector3d &position)
+{
+ return gAgent.getPosAgentFromGlobal(position);
+}
+
+//-----------------------------------------------------------------------------
+// allocateCharacterJoints()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCharacterJoints( U32 num )
+{
+ delete [] mSkeleton;
+ mSkeleton = NULL;
+ mNumJoints = 0;
+
+ mSkeleton = new LLViewerJoint[num];
+
+ for(S32 joint_num = 0; joint_num < (S32)num; joint_num++)
+ {
+ mSkeleton[joint_num].setJointNum(joint_num);
+ }
+
+ if (!mSkeleton)
+ {
+ return FALSE;
+ }
+
+ mNumJoints = num;
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// allocateCollisionVolumes()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::allocateCollisionVolumes( U32 num )
+{
+ delete [] mCollisionVolumes;
+ mCollisionVolumes = NULL;
+ mNumCollisionVolumes = 0;
+
+ mCollisionVolumes = new LLViewerJointCollisionVolume[num];
+ if (!mCollisionVolumes)
+ {
+ return FALSE;
+ }
+
+ mNumCollisionVolumes = num;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getCharacterJoint()
+//-----------------------------------------------------------------------------
+LLJoint *LLVOAvatar::getCharacterJoint( U32 num )
+{
+ if ((S32)num >= mNumJoints
+ || num < 0)
+ {
+ return NULL;
+ }
+ return (LLJoint*)&mSkeleton[num];
+}
+
+
+//-----------------------------------------------------------------------------
+// requestStopMotion()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestStopMotion( LLMotion* motion )
+{
+ // Only agent avatars should handle the stop motion notifications.
+ if ( mIsSelf )
+ {
+ // Notify agent that motion has stopped
+ gAgent.requestStopMotion( motion );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// loadAvatar()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadAvatar()
+{
+// LLFastTimer t(LLFastTimer::FTM_LOAD_AVATAR);
+
+ // avatar_skeleton.xml
+ if( !buildSkeleton(sSkeletonInfo) )
+ {
+ llwarns << "avatar file: buildSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <skeleton>
+ if( !loadSkeletonNode() )
+ {
+ llwarns << "avatar file: loadNodeSkeleton() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <mesh>
+ if( !loadMeshNodes() )
+ {
+ llwarns << "avatar file: loadNodeMesh() failed" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <global_color>
+ if( sAvatarInfo->mTexSkinColorInfo )
+ {
+ mTexSkinColor = new LLTexGlobalColor( this );
+ if( !mTexSkinColor->setInfo( sAvatarInfo->mTexSkinColorInfo ) )
+ {
+ llwarns << "avatar file: mTexSkinColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"skin_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexHairColorInfo )
+ {
+ mTexHairColor = new LLTexGlobalColor( this );
+ if( !mTexHairColor->setInfo( sAvatarInfo->mTexHairColorInfo ) )
+ {
+ llwarns << "avatar file: mTexHairColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"hair_color\" not found" << llendl;
+ return FALSE;
+ }
+ if( sAvatarInfo->mTexEyeColorInfo )
+ {
+ mTexEyeColor = new LLTexGlobalColor( this );
+ if( !mTexEyeColor->setInfo( sAvatarInfo->mTexEyeColorInfo ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->setInfo() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else
+ {
+ llwarns << "<global_color> name=\"eye_color\" not found" << llendl;
+ return FALSE;
+ }
+
+ // avatar_lad.xml : <layer_set>
+ if (sAvatarInfo->mLayerInfoList.empty())
+ {
+ llwarns << "avatar file: missing <layer_set> node" << llendl;
+ }
+ else
+ {
+ LLVOAvatarInfo::layer_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mLayerInfoList.begin();
+ iter != sAvatarInfo->mLayerInfoList.end(); iter++)
+ {
+ LLTexLayerSetInfo *info = *iter;
+ LLTexLayerSet* layer_set = new LLTexLayerSet( this );
+ if (!layer_set->setInfo(info))
+ {
+ stop_glerror();
+ delete layer_set;
+ llwarns << "avatar file: layer_set->parseData() failed" << llendl;
+ return FALSE;
+ }
+ if( layer_set->isBodyRegion( "head" ) )
+ {
+ mHeadLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "upper_body" ) )
+ {
+ mUpperBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "lower_body" ) )
+ {
+ mLowerBodyLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "eyes" ) )
+ {
+ mEyesLayerSet = layer_set;
+ }
+ else if( layer_set->isBodyRegion( "skirt" ) )
+ {
+ mSkirtLayerSet = layer_set;
+ }
+ else
+ {
+ llwarns << "<layer_set> has invalid body_region attribute" << llendl;
+ delete layer_set;
+ return FALSE;
+ }
+ }
+ }
+
+ // avatar_lad.xml : <driver_parameters>
+ {
+ LLVOAvatarInfo::driver_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mDriverInfoList.begin();
+ iter != sAvatarInfo->mDriverInfoList.end(); iter++)
+ {
+ LLDriverParamInfo *info = *iter;
+ LLDriverParam* driver_param = new LLDriverParam( this );
+ if (driver_param->setInfo(info))
+ {
+ addVisualParam( driver_param );
+ }
+ else
+ {
+ delete driver_param;
+ llwarns << "avatar file: driver_param->parseData() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadSkeletonNode(): loads <skeleton> node from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadSkeletonNode ()
+{
+ mRoot.addChild( &mSkeleton[0] );
+
+ mRoot.addChild( &mHeadLOD );
+ mHeadLOD.mUpdateXform = FALSE;
+ mHeadLOD.addChild( &mHeadMesh0 );
+ mHeadLOD.addChild( &mHeadMesh1 );
+ mHeadLOD.addChild( &mHeadMesh2 );
+ mHeadLOD.addChild( &mHeadMesh3 );
+ mHeadLOD.addChild( &mHeadMesh4 );
+
+ mRoot.addChild( &mEyeLashLOD );
+ mEyeLashLOD.mUpdateXform = FALSE;
+ mEyeLashLOD.addChild( &mEyeLashMesh0 );
+
+ mRoot.addChild( &mUpperBodyLOD );
+ mUpperBodyLOD.mUpdateXform = FALSE;
+ mUpperBodyLOD.addChild( &mUpperBodyMesh0 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh1 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh2 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh3 );
+ mUpperBodyLOD.addChild( &mUpperBodyMesh4 );
+
+ mRoot.addChild( &mLowerBodyLOD );
+ mLowerBodyLOD.mUpdateXform = FALSE;
+ mLowerBodyLOD.addChild( &mLowerBodyMesh0 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh1 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh2 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh3 );
+ mLowerBodyLOD.addChild( &mLowerBodyMesh4 );
+
+ mRoot.addChild( &mSkirtLOD );
+ mSkirtLOD.mUpdateXform = FALSE;
+ mSkirtLOD.addChild( &mSkirtMesh0 );
+ mSkirtLOD.addChild( &mSkirtMesh1 );
+ mSkirtLOD.addChild( &mSkirtMesh2 );
+ mSkirtLOD.addChild( &mSkirtMesh3 );
+ mSkirtLOD.addChild( &mSkirtMesh4 );
+
+ LLViewerJoint *skull = (LLViewerJoint*)mRoot.findJoint("mSkull");
+ if (skull)
+ {
+ skull->addChild( &mHairLOD );
+ mHairLOD.mUpdateXform = FALSE;
+ mHairLOD.addChild( &mHairMesh0 );
+ mHairLOD.addChild( &mHairMesh1 );
+ mHairLOD.addChild( &mHairMesh2 );
+ mHairLOD.addChild( &mHairMesh3 );
+ mHairLOD.addChild( &mHairMesh4 );
+ mHairLOD.addChild( &mHairMesh5 );
+ }
+
+ LLViewerJoint *eyeL = (LLViewerJoint*)mRoot.findJoint("mEyeLeft");
+ if (eyeL)
+ {
+ eyeL->addChild( &mEyeBallLeftLOD );
+ mEyeBallLeftLOD.mUpdateXform = FALSE;
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh0 );
+ mEyeBallLeftLOD.addChild( &mEyeBallLeftMesh1 );
+ }
+
+ LLViewerJoint *eyeR = (LLViewerJoint*)mRoot.findJoint("mEyeRight");
+ if (eyeR)
+ {
+ eyeR->addChild( &mEyeBallRightLOD );
+ mEyeBallRightLOD.mUpdateXform = FALSE;
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh0 );
+ mEyeBallRightLOD.addChild( &mEyeBallRightMesh1 );
+ }
+
+ // SKELETAL DISTORTIONS
+ {
+ LLVOAvatarInfo::skeletal_distortion_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mSkeletalDistortionInfoList.begin();
+ iter != sAvatarInfo->mSkeletalDistortionInfoList.end(); iter++)
+ {
+ LLPolySkeletalDistortionInfo *info = *iter;
+ LLPolySkeletalDistortion *param = new LLPolySkeletalDistortion(this);
+ if (!param->setInfo(info))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+
+ // ATTACHMENTS
+ {
+ LLVOAvatarInfo::attachment_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mAttachmentInfoList.begin();
+ iter != sAvatarInfo->mAttachmentInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarAttachmentInfo *info = *iter;
+ LLViewerJointAttachment* attachment = new LLViewerJointAttachment();
+
+ attachment->setName(info->mName);
+ LLJoint *parentJoint = getJoint(info->mJointName);
+ if (!parentJoint)
+ {
+ llwarns << "No parent joint by name " << info->mJointName << " found for attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ if (info->mHasPosition)
+ {
+ attachment->setOriginalPosition(info->mPosition);
+ }
+
+ if (info->mHasRotation)
+ {
+ LLQuaternion rotation;
+ rotation.setQuat(info->mRotationEuler.mV[VX] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VY] * DEG_TO_RAD,
+ info->mRotationEuler.mV[VZ] * DEG_TO_RAD);
+ attachment->setRotation(rotation);
+ }
+
+ int group = info->mGroup;
+ if (group >= 0)
+ {
+ if (group < 0 || group >= 9)
+ {
+ llwarns << "Invalid group number (" << group << ") for attachment point " << info->mName << llendl;
+ }
+ else
+ {
+ attachment->setGroup(group);
+ }
+ }
+
+ S32 attachmentID = info->mAttachmentID;
+ if (attachmentID < 1 || attachmentID > 255)
+ {
+ llwarns << "Attachment point out of range [1-255]: " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+ if (mAttachmentPoints.checkData(attachmentID))
+ {
+ llwarns << "Attachment point redefined with id " << attachmentID << " on attachment point " << info->mName << llendl;
+ delete attachment;
+ continue;
+ }
+
+ attachment->setPieSlice(info->mPieMenuSlice);
+ attachment->setVisibleInFirstPerson(info->mVisibleFirstPerson);
+ attachment->setIsHUDAttachment(info->mIsHUDAttachment);
+
+ mAttachmentPoints[attachmentID] = attachment;
+
+ // now add attachment joint
+ parentJoint->addChild(attachment);
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// loadMeshNodes(): loads <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::loadMeshNodes()
+{
+ LLVOAvatarInfo::mesh_info_list_t::iterator iter;
+ for (iter = sAvatarInfo->mMeshInfoList.begin();
+ iter != sAvatarInfo->mMeshInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo *info = *iter;
+ LLString &type = info->mType;
+ S32 lod = info->mLOD;
+
+ LLViewerJointMesh* mesh = NULL;
+ if (type == "hairMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHairMesh0;
+ break;
+ case 1:
+ mesh = &mHairMesh1;
+ break;
+ case 2:
+ mesh = &mHairMesh2;
+ break;
+ case 3:
+ mesh = &mHairMesh3;
+ break;
+ case 4:
+ mesh = &mHairMesh4;
+ break;
+ case 5:
+ mesh = &mHairMesh5;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "headMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mHeadMesh0;
+ break;
+ case 1:
+ mesh = &mHeadMesh1;
+ break;
+ case 2:
+ mesh = &mHeadMesh2;
+ break;
+ case 3:
+ mesh = &mHeadMesh3;
+ break;
+ case 4:
+ mesh = &mHeadMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "upperBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mUpperBodyMesh0;
+ break;
+ case 1:
+ mesh = &mUpperBodyMesh1;
+ break;
+ case 2:
+ mesh = &mUpperBodyMesh2;
+ break;
+ case 3:
+ mesh = &mUpperBodyMesh3;
+ break;
+ case 4:
+ mesh = &mUpperBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "lowerBodyMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mLowerBodyMesh0;
+ break;
+ case 1:
+ mesh = &mLowerBodyMesh1;
+ break;
+ case 2:
+ mesh = &mLowerBodyMesh2;
+ break;
+ case 3:
+ mesh = &mLowerBodyMesh3;
+ break;
+ case 4:
+ mesh = &mLowerBodyMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "skirtMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mSkirtMesh0;
+ break;
+ case 1:
+ mesh = &mSkirtMesh1;
+ break;
+ case 2:
+ mesh = &mSkirtMesh2;
+ break;
+ case 3:
+ mesh = &mSkirtMesh3;
+ break;
+ case 4:
+ mesh = &mSkirtMesh4;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyelashMesh")
+ {
+ mesh = &mEyeLashMesh0;
+ }
+ else if (type == "eyeBallLeftMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallLeftMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallLeftMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+ else if (type == "eyeBallRightMesh")
+ {
+ switch (lod)
+ {
+ case 0:
+ mesh = &mEyeBallRightMesh0;
+ break;
+ case 1:
+ mesh = &mEyeBallRightMesh1;
+ break;
+ default:
+ llwarns << "Avatar file: <mesh> has invalid lod setting " << lod << llendl;
+ return FALSE;
+ }
+ }
+
+ if( !mesh )
+ {
+ llwarns << "Ignoring unrecognized mesh type: " << type << llendl;
+ return FALSE;
+ }
+
+ // llinfos << "Parsing mesh data for " << type << "..." << llendl;
+
+ mesh->setColor( 0.8f, 0.8f, 0.8f, 1.0f );
+
+ LLPolyMesh *poly_mesh = NULL;
+
+ if (!info->mReferenceMeshName.empty())
+ {
+ mesh_map_t::iterator iter = mMeshes.find(info->mReferenceMeshName);
+ if (iter != mMeshes.end())
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName, iter->second);
+ poly_mesh->setAvatar(this);
+ }
+ else
+ {
+ // This should never happen
+ }
+ }
+ else
+ {
+ poly_mesh = LLPolyMesh::getMesh(info->mMeshFileName);
+ poly_mesh->setAvatar(this);
+ }
+
+ if( !poly_mesh )
+ {
+ llwarns << "Failed to load mesh of type " << type << llendl;
+ return FALSE;
+ }
+
+ // Multimap insert
+ mMeshes.insert(std::pair<LLString, LLPolyMesh*>(info->mMeshFileName, poly_mesh));
+
+ mesh->setMesh( poly_mesh );
+
+ mesh->setLOD( info->mMinPixelArea );
+
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_list_t::iterator iter;
+ for (iter = info->mPolyMorphTargetInfoList.begin();
+ iter != info->mPolyMorphTargetInfoList.end(); iter++)
+ {
+ LLVOAvatarInfo::LLVOAvatarMeshInfo::morph_info_pair_t *info_pair = &(*iter);
+ LLPolyMorphTarget *param = new LLPolyMorphTarget(mesh->getMesh());
+ if (!param->setInfo(info_pair->first))
+ {
+ delete param;
+ return FALSE;
+ }
+ else
+ {
+ if (info_pair->second)
+ {
+ addSharedVisualParam(param);
+ }
+ else
+ {
+ addVisualParam(param);
+ }
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// updateVisualParams()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateVisualParams()
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ setSex( (getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE );
+
+ LLCharacter::updateVisualParams();
+
+ if (mLastSkeletonSerialNum != mSkeletonSerialNum)
+ {
+ computeBodySize();
+ mLastSkeletonSerialNum = mSkeletonSerialNum;
+ mRoot.updateWorldMatrixChildren();
+ }
+
+ dirtyMesh();
+ updateHeadOffset();
+}
+
+//-----------------------------------------------------------------------------
+// isActive()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isActive() const
+{
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// setPixelAreaAndAngle()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setPixelAreaAndAngle(LLAgent &agent)
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent;
+
+ pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 max_scale = getMaxScale();
+ F32 mid_scale = getMidScale();
+ F32 min_scale = llmin( getScale().mV[VX], llmin( getScale().mV[VY], getScale().mV[VZ] ) );
+
+ // IW: esitmate - when close to large objects, computing range based on distance from center is no good
+ // to try to get a min distance from face, subtract min_scale/2 from the range.
+ // This means we'll load too much detail sometimes, but that's better than not enough
+ // I don't think there's a better way to do this without calculating distance per-poly
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz) - min_scale/2;
+
+ if (range < 0.001f) // range == zero
+ {
+ mAppAngle = 180.f;
+ mPixelArea = gCamera->getViewHeightInPixels() *
+ gCamera->getViewHeightInPixels() *
+ gCamera->getAspect();
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ F32 pixels_per_meter = gCamera->getPixelMeterRatio() / range;
+
+ mPixelArea = (pixels_per_meter * max_scale) * (pixels_per_meter * mid_scale);
+// if( !mIsSelf )
+// {
+// llinfos << "range " << range << llendl;
+// llinfos << "pixels_per_meter " << pixels_per_meter << llendl;
+// llinfos << "scale " << max_scale << "x" << mid_scale << llendl;
+// llinfos << "pixel area " << mPixelArea << llendl;
+// }
+ }
+
+ // We always want to look good to ourselves
+ if( mIsSelf )
+ {
+ mPixelArea = llmax( mPixelArea, F32(LOCTEX_IMAGE_SIZE_SELF / 16) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateJointLODs()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateJointLODs()
+{
+ if (!mMeshValid)
+ {
+ return;
+ }
+
+ F32 lod_factor = (sLODFactor * AVATAR_LOD_TWEAK_RANGE + (1.f - AVATAR_LOD_TWEAK_RANGE));
+ F32 avatar_num_min_factor = clamp_rescale(sLODFactor, 0.f, 1.f, 0.25f, 0.6f);
+ F32 avatar_num_factor = clamp_rescale((F32)sNumVisibleAvatars, 8, 25, 1.f, avatar_num_min_factor);
+
+ {
+ if (mIsSelf)
+ {
+ if(gAgent.cameraCustomizeAvatar() || gAgent.cameraMouselook())
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ mAdjustedPixelArea = mPixelArea;
+ }
+ }
+ else if (mIsDummy)
+ {
+ mAdjustedPixelArea = 1000000;
+ }
+ else
+ {
+ // reported avatar pixel area is dependent on avatar render load, based on number of visible avatars
+ mAdjustedPixelArea = (F32)mPixelArea * lod_factor * lod_factor * avatar_num_factor * avatar_num_factor;
+ }
+
+ // now select meshes to render based on adjusted pixel area, and perform AGP data push as necessary
+ BOOL res = mRoot.updateLOD(mAdjustedPixelArea, TRUE);
+ if (res)
+ {
+ sNumLODChangesThisFrame++;
+ updateMeshData();
+ }
+ }
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// createDrawable()
+//-----------------------------------------------------------------------------
+LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_AVATAR);
+
+ // Only a single face (one per avatar)
+ mDrawable->setState(LLDrawable::ACTIVE);
+ mDrawable->addFace(poolp, NULL);
+
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+
+ LLFace *facep;
+
+ // Add faces for the foot shadows
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow0Facep = facep;
+
+ facep = mDrawable->addFace(poolp, mShadowImagep);
+ mShadow1Facep = facep;
+
+ gPipeline.markMaterialed(mDrawable);
+ dirtyMesh();
+ return mDrawable;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateGeometry()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR)))
+ {
+ return TRUE;
+ }
+
+ if (!mMeshValid)
+ {
+ return TRUE;
+ }
+
+ if (!drawable)
+ {
+ llerrs << "LLVOAvatar::updateGeometry() called with NULL drawable" << llendl;
+ }
+
+ // Update the shadow, tractor, and text label geometry.
+
+ updateShadowFaces();
+
+ if (!drawable->isVisible())
+ {
+ return TRUE;
+ }
+
+ LLFace* facep = drawable->getFace(0);
+ if (!mDirtyMesh && !facep->getDirty())
+ {
+ return TRUE;
+ }
+
+ // U32 num_vertices = 0;
+
+ updateMeshData();
+
+ mDirtyMesh = FALSE;
+ return TRUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// updateShadowFaces()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateShadowFaces()
+{
+ LLFace *face0p = mShadow0Facep;
+ LLFace *face1p = mShadow1Facep;
+ //
+ // render avatar shadows
+ //
+ if (mInAir)
+ {
+ face0p->setSize(0, 0);
+ face1p->setSize(0, 0);
+ return;
+ }
+
+ LLSprite sprite(mShadowImageID);
+ sprite.setFollow(FALSE);
+ const F32 cos_angle = gSky.getSunDirection().mV[2];
+ F32 cos_elev = sqrt(1 - cos_angle * cos_angle);
+ if (cos_angle < 0) cos_elev = -cos_elev;
+ sprite.setSize(0.4f + cos_elev * 0.8f, 0.3f);
+ LLVector3 sun_vec = gSky.mVOSkyp->getToSun();
+
+ if (mShadowImagep->getHasGLTexture())
+ {
+ LLVector3 normal;
+ LLVector3d shadow_pos;
+ LLVector3 shadow_pos_agent;
+ F32 foot_height;
+
+ if (mFootLeftp)
+ {
+ LLVector3 joint_world_pos = mFootLeftp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeLeftp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face0p);
+ }
+
+ if (mFootRightp)
+ {
+ LLVector3 joint_world_pos = mFootRightp->getWorldPosition();
+ // this only does a ray straight down from the foot, as our client-side ray-tracing is very limited now
+ // but we make an explicit ray trace call in expectation of future improvements
+ resolveRayCollisionAgent(gAgent.getPosGlobalFromAgent(joint_world_pos),
+ gAgent.getPosGlobalFromAgent(gSky.getSunDirection() + joint_world_pos), shadow_pos, normal);
+ shadow_pos_agent = gAgent.getPosAgentFromGlobal(shadow_pos);
+ foot_height = joint_world_pos.mV[VZ] - shadow_pos_agent.mV[VZ];
+
+ // Pull sprite in direction of surface normal
+ shadow_pos_agent += normal * SHADOW_OFFSET_AMT;
+
+ // Render sprite
+ sprite.setNormal(normal);
+ if (mIsSelf && gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK)
+ {
+ sprite.setColor(0.f, 0.f, 0.f, 0.f);
+ }
+ else
+ {
+ sprite.setColor(0.f, 0.f, 0.f, clamp_rescale(foot_height, MIN_SHADOW_HEIGHT, MAX_SHADOW_HEIGHT, 0.5f, 0.f));
+ }
+ sprite.setPosition(shadow_pos_agent);
+
+ LLVector3 foot_to_knee = mKneeRightp->getWorldPosition() - joint_world_pos;
+ //foot_to_knee.normVec();
+ foot_to_knee -= projected_vec(foot_to_knee, sun_vec);
+ sprite.setYaw(azimuth(sun_vec - foot_to_knee));
+
+ sprite.updateFace(*face1p);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateSexDependentLayerSets()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateSexDependentLayerSets( BOOL set_by_user )
+{
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ updateMeshTextures();
+}
+
+//-----------------------------------------------------------------------------
+// dirtyMesh()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::dirtyMesh()
+{
+ mDirtyMesh = TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUpdate()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUpdate( LLVOAvatar::ELocTexIndex i )
+{
+ switch( i )
+ {
+ case LOCTEX_HEAD_BODYPAINT:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_UPPER_BODYPAINT:
+ case LOCTEX_UPPER_SHIRT:
+ case LOCTEX_UPPER_GLOVES:
+ case LOCTEX_UPPER_UNDERSHIRT:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_LOWER_BODYPAINT:
+ case LOCTEX_LOWER_PANTS:
+ case LOCTEX_LOWER_SHOES:
+ case LOCTEX_LOWER_SOCKS:
+ case LOCTEX_LOWER_UNDERPANTS:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+
+ case LOCTEX_EYES_IRIS:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_SKIRT:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpdate();
+ }
+ break;
+
+
+ case LOCTEX_LOWER_JACKET:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpdate();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpdate();
+ }
+ break;
+ }
+
+}
+
+void LLVOAvatar::setParent(LLViewerObject* parent)
+{
+ if (parent == NULL)
+ {
+ getOffObject();
+ LLViewerObject::setParent(parent);
+ if (isSelf())
+ {
+ gAgent.resetCamera();
+ }
+ }
+ else
+ {
+ LLViewerObject::setParent(parent);
+ sitOnObject(parent);
+ }
+}
+
+void LLVOAvatar::addChild(LLViewerObject *childp)
+{
+ LLViewerObject::addChild(childp);
+ attachObject(childp);
+}
+
+void LLVOAvatar::removeChild(LLViewerObject *childp)
+{
+ LLViewerObject::removeChild(childp);
+ detachObject(childp);
+}
+
+//-----------------------------------------------------------------------------
+// attachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::attachObject(LLViewerObject *viewer_object)
+{
+ S32 attachmentID = ATTACHMENT_ID_FROM_STATE(viewer_object->getState());
+ //clamp((S32)(viewer_object->getState() & AGENT_ATTACH_MASK) >> AGENT_ATTACH_OFFSET, 1, 0xff);
+
+ //if (mIsSelf)
+ //{
+ // gSelectMgr->deselectObjectAndFamily(viewer_object);
+ //}
+
+ LLViewerJointAttachment* attachment = mAttachmentPoints.getIfThere(attachmentID);
+
+ if (!attachment)
+ {
+ llwarns << "Tried to attach object to invalid attachment point: " << attachmentID << llendl;
+ return FALSE;
+ }
+
+// LLQuaternion object_world_rot = viewer_object->getWorldRotation();
+
+ if (!attachment->addObject(viewer_object))
+ {
+ return FALSE;
+ }
+
+ if (viewer_object->isSelected())
+ {
+ gSelectMgr->updateSelectionCenter();
+ gSelectMgr->updatePointAt();
+ }
+
+// LLQuaternion desired_rot = (object_world_rot * ~attachment->getWorldRotation());
+
+ lldebugs << "Attaching object (" << attachmentID << ") item_id=" << attachment->getItemID() << " task_id=" << viewer_object->getID() << "to " << attachment->getName() << llendl;
+
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask( LLInventoryObserver::LABEL, attachment->getItemID() );
+ gInventory.notifyObservers();
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// lazyAttach()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::lazyAttach()
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getAttachmentDirty())
+ {
+ attachment->lazyAttach();
+ if (mIsSelf)
+ {
+ updateAttachmentVisibility(gAgent.getCameraMode());
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// detachObject()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::detachObject(LLViewerObject *viewer_object)
+{
+ for(LLViewerJointAttachment* attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ // only one object per attachment point for now
+ if (attachment->getObject(0) == viewer_object)
+ {
+ LLUUID item_id = attachment->getItemID();
+ attachment->removeObject(viewer_object);
+ if (mIsSelf)
+ {
+ // the simulator should automatically handle
+ // permissiosn revokation
+
+ stopMotionFromSource(viewer_object->getID());
+ LLFollowCamMgr::setCameraActive(viewer_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)viewer_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = viewer_object->mChildList[i];
+ // the simulator should automatically handle
+ // permissions revokation
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+
+ }
+ lldebugs << "Detaching object " << viewer_object->mID << " from " << attachment->getName() << llendl;
+ if (mIsSelf)
+ {
+ // Then make sure the inventory is in sync with the avatar.
+ gInventory.addChangedMask(LLInventoryObserver::LABEL, item_id);
+ gInventory.notifyObservers();
+ }
+ return TRUE;
+ }
+ }
+
+
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// sitOnObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::sitOnObject(LLViewerObject *sit_object)
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+ LLQuaternion inv_obj_rot = ~sit_object->getRenderRotation();
+ LLVector3 obj_pos = sit_object->getRenderPosition();
+
+ LLVector3 rel_pos = getRenderPosition() - obj_pos;
+ rel_pos.rotVec(inv_obj_rot);
+
+ mDrawable->mXform.setPosition(rel_pos);
+ mDrawable->mXform.setRotation(mDrawable->getWorldRotation() * inv_obj_rot);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+ mIsSitting = TRUE;
+ mRoot.getXform()->setParent(&sit_object->mDrawable->mXform); // LLVOAvatar::sitOnObject
+ mRoot.setPosition(getPosition());
+ mRoot.updateWorldMatrixChildren();
+
+ stopMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ // Might be first sit
+ LLFirstUse::useSit();
+
+ gAgent.setFlying(FALSE);
+ gAgent.setThirdPersonHeadOffset(LLVector3::zero);
+ //interpolate to new camera position
+ gAgent.startCameraAnimation();
+ // make sure we are not trying to autopilot
+ gAgent.stopAutoPilot();
+ gAgent.setupSitCamera();
+ if (gAgent.mForceMouselook) gAgent.changeCameraToMouselook();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// getOffObject()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::getOffObject()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLViewerObject* sit_object = (LLViewerObject*)getParent();
+
+ if (sit_object)
+ {
+ stopMotionFromSource(sit_object->getID());
+ LLFollowCamMgr::setCameraActive(sit_object->getID(), FALSE);
+
+ for (S32 i = 0; i < (S32)sit_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_objectp = sit_object->mChildList[i];
+
+ stopMotionFromSource(child_objectp->getID());
+ LLFollowCamMgr::setCameraActive(child_objectp->getID(), FALSE);
+ }
+ }
+
+ // assumes that transform will not be updated with drawable still having a parent
+ LLVector3 cur_position_world = mDrawable->getWorldPosition();
+ LLQuaternion cur_rotation_world = mDrawable->getWorldRotation();
+
+ // set *local* position based on last *world* position, since we're unparenting the avatar
+ mDrawable->mXform.setPosition(cur_position_world);
+ mDrawable->mXform.setRotation(cur_rotation_world);
+
+ gPipeline.markMoved(mDrawable, TRUE);
+
+ mIsSitting = FALSE;
+ mRoot.getXform()->setParent(NULL); // LLVOAvatar::getOffObject
+ mRoot.setPosition(cur_position_world);
+ mRoot.setRotation(cur_rotation_world);
+ mRoot.getXform()->update();
+
+ startMotion(ANIM_AGENT_BODY_NOISE);
+
+ if (mIsSelf)
+ {
+ LLQuaternion av_rot = gAgent.getFrameAgent().getQuaternion();
+ LLQuaternion obj_rot = sit_object ? sit_object->getRenderRotation() : LLQuaternion::DEFAULT;
+ av_rot = av_rot * obj_rot;
+ LLVector3 at_axis = LLVector3::x_axis;
+ at_axis = at_axis * av_rot;
+ at_axis.mV[VZ] = 0.f;
+ at_axis.normVec();
+ gAgent.resetAxes(at_axis);
+
+ //reset orientation
+// mRoot.setRotation(avWorldRot);
+ gAgent.setThirdPersonHeadOffset(LLVector3(0.f, 0.f, 1.f));
+
+ gAgent.setSitCamera(LLUUID::null);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// findAvatarFromAttachment()
+//-----------------------------------------------------------------------------
+// static
+LLVOAvatar* LLVOAvatar::findAvatarFromAttachment( LLViewerObject* obj )
+{
+ if( obj->isAttachment() )
+ {
+ do
+ {
+ obj = (LLViewerObject*) obj->getParent();
+ }
+ while( obj && !obj->isAvatar() );
+
+ if( obj && !obj->isDead() )
+ {
+ return (LLVOAvatar*)obj;
+ }
+ }
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// isWearingAttachment()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isWearingAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// getWornAttachment()
+//-----------------------------------------------------------------------------
+LLViewerObject* LLVOAvatar::getWornAttachment( const LLUUID& inv_item_id )
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return attachment_point->getObject(0);
+ }
+ }
+ return NULL;
+}
+
+const LLString LLVOAvatar::getAttachedPointName(const LLUUID& inv_item_id)
+{
+ for (LLViewerJointAttachment *attachment_point = mAttachmentPoints.getFirstData();
+ attachment_point;
+ attachment_point = mAttachmentPoints.getNextData())
+ {
+ if( attachment_point->getItemID() == inv_item_id )
+ {
+ return (LLString)attachment_point->getName();
+ }
+ }
+
+ return LLString::null;
+}
+
+
+//-----------------------------------------------------------------------------
+// static
+// onLocalTextureLoaded()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onLocalTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src_raw, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onLocalTextureLoaded: " << src_vi->getID() << llendl;
+
+ const LLUUID& src_id = src_vi->getID();
+ LLAvatarTexData *data = (LLAvatarTexData *)userdata;
+ if (success)
+ {
+ LLVOAvatar *self = (LLVOAvatar *)gObjectList.findObject(data->mAvatarID);
+ LLVOAvatar::ELocTexIndex idx = data->mIndex;
+ if( self &&
+ (!self->mLocalTextureBaked[ idx ]) &&
+ (self->mLocalTexture[ idx ].notNull()) &&
+ (self->mLocalTexture[ idx ]->getID() == src_id) &&
+ (discard_level < self->mLocalTextureDiscard[idx]))
+ {
+ self->mLocalTextureDiscard[idx] = discard_level;
+ self->requestLayerSetUpdate( idx );
+ if( self->mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ self->updateMeshTextures();
+ }
+ }
+
+ if( final || !success )
+ {
+ delete data;
+ }
+}
+
+void LLVOAvatar::updateComposites()
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->updateComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->updateComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->updateComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->updateComposite();
+ }
+
+ if( mSkirtLayerSet && isWearingWearableType( WT_SKIRT ))
+ {
+ mSkirtLayerSet->updateComposite();
+ }
+}
+
+LLColor4 LLVOAvatar::getGlobalColor( const LLString& color_name )
+{
+ if( color_name=="skin_color" && mTexSkinColor )
+ {
+ return mTexSkinColor->getColor();
+ }
+ else
+ if( color_name=="hair_color" && mTexHairColor )
+ {
+ return mTexHairColor->getColor();
+ }
+ if( color_name=="eye_color" && mTexEyeColor )
+ {
+ return mTexEyeColor->getColor();
+ }
+ else
+ {
+// return LLColor4( .5f, .5f, .5f, .5f );
+ return LLColor4( 0.f, 1.f, 1.f, 1.f ); // good debugging color
+ }
+}
+
+
+void LLVOAvatar::invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_user )
+{
+ if( !layerset || !layerset->getUpdatesEnabled() )
+ {
+ return;
+ }
+
+ /* Debug spam. JC
+ const char* layer_name = "";
+ if (layerset == mHeadLayerSet)
+ {
+ layer_name = "head";
+ }
+ else if (layerset == mUpperBodyLayerSet)
+ {
+ layer_name = "upperbody";
+ }
+ else if (layerset == mLowerBodyLayerSet)
+ {
+ layer_name = "lowerbody";
+ }
+ else if (layerset == mEyesLayerSet)
+ {
+ layer_name = "eyes";
+ }
+ else if (layerset == mSkirtLayerSet)
+ {
+ layer_name = "skirt";
+ }
+ else
+ {
+ layer_name = "unknown";
+ }
+ llinfos << "LLVOAvatar::invalidComposite() " << layer_name << llendl;
+ */
+
+ layerset->requestUpdate();
+
+ if( set_by_user )
+ {
+ llassert( mIsSelf );
+
+ ETextureIndex baked_te = getBakedTE( layerset );
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ mSavedTE[ baked_te ].setNull();
+ }
+ else
+ {
+ setTEImage( baked_te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ layerset->requestUpload();
+ }
+ }
+}
+
+
+void LLVOAvatar::onGlobalColorChanged( LLTexGlobalColor* global_color, BOOL set_by_user )
+{
+ if( global_color == mTexSkinColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( skin color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+ invalidateComposite( mUpperBodyLayerSet, set_by_user );
+ invalidateComposite( mLowerBodyLayerSet, set_by_user );
+ }
+ else
+ if( global_color == mTexHairColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( hair color )" << llendl;
+ invalidateComposite( mHeadLayerSet, set_by_user );
+
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+ else
+ if( global_color == mTexEyeColor )
+ {
+// llinfos << "invalidateComposite cause: onGlobalColorChanged( eyecolor )" << llendl;
+ invalidateComposite( mEyesLayerSet, set_by_user );
+ }
+ updateMeshTextures();
+}
+
+void LLVOAvatar::forceBakeAllTextures(bool slam_for_debug)
+{
+ llinfos << "TAT: forced full rebake. " << llendl;
+
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ LLTexLayerSet* layer_set = getLayerSet(baked_index);
+ if (layer_set)
+ {
+ if (slam_for_debug)
+ {
+ layer_set->setUpdatesEnabled(TRUE);
+ layer_set->cancelUpload();
+ }
+
+ BOOL set_by_user = TRUE;
+ invalidateComposite(layer_set, set_by_user);
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ else
+ {
+ llwarns << "TAT: NO LAYER SET FOR " << (S32)baked_index << llendl;
+ }
+ }
+
+ // Don't know if this is needed
+ updateMeshTextures();
+}
+
+
+// static
+void LLVOAvatar::processRebakeAvatarTextures(LLMessageSystem* msg, void**)
+{
+ LLUUID texture_id;
+ msg->getUUID("TextureData", "TextureID", texture_id);
+
+ LLVOAvatar* self = gAgent.getAvatarObject();
+ if (!self) return;
+
+ // If this is a texture corresponding to one of our baked entries,
+ // just rebake that layer set.
+ BOOL found = FALSE;
+ for (S32 i = 0; i < BAKED_TEXTURE_COUNT; i++)
+ {
+ ETextureIndex baked_index = sBakedTextureIndices[i];
+ if (texture_id == self->getTEImage(baked_index)->getID())
+ {
+ LLTexLayerSet* layer_set = self->getLayerSet(baked_index);
+ if (layer_set)
+ {
+ llinfos << "TAT: rebake - matched entry " << (S32)baked_index << llendl;
+ // Apparently set_by_user == force upload
+ BOOL set_by_user = TRUE;
+ self->invalidateComposite(layer_set, set_by_user);
+ found = TRUE;
+ gViewerStats->incStat(LLViewerStats::ST_TEX_REBAKES);
+ }
+ }
+ }
+
+ // If texture not found, rebake all entries.
+ if (!found)
+ {
+ self->forceBakeAllTextures();
+ }
+ else
+ {
+ // Not sure if this is necessary, but forceBakeAllTextures() does it.
+ self->updateMeshTextures();
+ }
+}
+
+
+BOOL LLVOAvatar::getLocalTextureRaw(S32 index, LLImageRaw* image_raw)
+{
+ BOOL success = FALSE;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if (mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ if( mLocalTexture[ index ]->readBackRaw(-1, image_raw) )
+ {
+ success = TRUE;
+ }
+ else
+ {
+ // No data loaded yet
+ setLocalTexture( (ELocTexIndex)index, getTEImage( index ), FALSE );
+ }
+ }
+ }
+ return success;
+}
+
+BOOL LLVOAvatar::getLocalTextureGL(S32 index, LLImageGL** image_gl_pp)
+{
+ BOOL success = FALSE;
+ *image_gl_pp = NULL;
+
+ if( (0 <= index) && (index < LOCTEX_NUM_ENTRIES) )
+ {
+ if( mLocalTexture[ index ].isNull() || mLocalTexture[ index ]->getID() == IMG_DEFAULT_AVATAR)
+ {
+ success = TRUE;
+ }
+ else
+ {
+ *image_gl_pp = mLocalTexture[ index ];
+ success = TRUE;
+ }
+ }
+
+ if( !success )
+ {
+// llinfos << "getLocalTextureGL(" << index << ") had no data" << llendl;
+ }
+ return success;
+}
+
+const LLUUID& LLVOAvatar::getLocalTextureID( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull())
+ {
+ return mLocalTexture[index]->getID();
+ }
+ else
+ {
+ return IMG_DEFAULT_AVATAR;
+ }
+}
+
+// static
+void LLVOAvatar::dumpTotalLocalTextureByteCount()
+{
+ S32 total_gl_bytes = 0;
+ for( LLVOAvatar* cur = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ cur;
+ cur = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ S32 gl_bytes = 0;
+ cur->getLocalTextureByteCount(&gl_bytes );
+ total_gl_bytes += gl_bytes;
+ }
+ llinfos << "Total Avatar LocTex GL:" << (total_gl_bytes/1024) << "KB" << llendl;
+}
+
+BOOL LLVOAvatar::isVisible()
+{
+ return mDrawable.notNull() && mDrawable->isVisible();
+}
+
+
+//-----------------------------------------------------------------------------
+// findMotion()
+//-----------------------------------------------------------------------------
+LLMotion* LLVOAvatar::findMotion(const LLUUID& id)
+{
+ return mMotionController.findMotion(id);
+}
+
+// Counts the memory footprint of local textures.
+void LLVOAvatar::getLocalTextureByteCount( S32* gl_bytes )
+{
+ *gl_bytes = 0;
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ LLViewerImage* image_gl = mLocalTexture[i];
+ if( image_gl )
+ {
+ S32 bytes = (S32)image_gl->getWidth() * image_gl->getHeight() * image_gl->getComponents();
+
+ if( image_gl->getHasGLTexture() )
+ {
+ *gl_bytes += bytes;
+ }
+ }
+ }
+}
+
+
+BOOL LLVOAvatar::bindScratchTexture( LLGLenum format )
+{
+ U32 texture_bytes = 0;
+ GLuint gl_name = getScratchTexName( format, &texture_bytes );
+ if( gl_name )
+ {
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ F32* last_bind_time = LLVOAvatar::sScratchTexLastBindTime.getIfThere( format );
+ if( last_bind_time )
+ {
+ if( *last_bind_time != LLImageGL::sLastFrameTime )
+ {
+ *last_bind_time = LLImageGL::sLastFrameTime;
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ }
+ }
+ else
+ {
+ LLImageGL::updateBoundTexMem(texture_bytes);
+ LLVOAvatar::sScratchTexLastBindTime.addData( format, new F32(LLImageGL::sLastFrameTime) );
+ }
+
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+
+LLGLuint LLVOAvatar::getScratchTexName( LLGLenum format, U32* texture_bytes )
+{
+ S32 components;
+ GLenum internal_format;
+ switch( format )
+ {
+ case GL_LUMINANCE: components = 1; internal_format = GL_LUMINANCE8; break;
+ case GL_ALPHA: components = 1; internal_format = GL_ALPHA8; break;
+ case GL_COLOR_INDEX: components = 1; internal_format = GL_COLOR_INDEX8_EXT; break;
+ case GL_LUMINANCE_ALPHA: components = 2; internal_format = GL_LUMINANCE8_ALPHA8; break;
+ case GL_RGB: components = 3; internal_format = GL_RGB8; break;
+ case GL_RGBA: components = 4; internal_format = GL_RGBA8; break;
+ default: llassert(0); components = 4; internal_format = GL_RGBA8; break;
+ }
+
+ *texture_bytes = components * VOAVATAR_SCRATCH_TEX_WIDTH * VOAVATAR_SCRATCH_TEX_HEIGHT;
+
+ if( LLVOAvatar::sScratchTexNames.checkData( format ) )
+ {
+ return *( LLVOAvatar::sScratchTexNames.getData( format ) );
+ }
+ else
+ {
+
+ LLGLSUIDefault gls_ui;
+
+ GLuint name = 0;
+ glGenTextures(1, &name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, internal_format,
+ VOAVATAR_SCRATCH_TEX_WIDTH, VOAVATAR_SCRATCH_TEX_HEIGHT,
+ 0, format, GL_UNSIGNED_BYTE, NULL );
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+ stop_glerror();
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ stop_glerror();
+
+ LLVOAvatar::sScratchTexNames.addData( format, new LLGLuint( name ) );
+
+ LLVOAvatar::sScratchTexBytes += *texture_bytes;
+ LLImageGL::sGlobalTextureMemory += *texture_bytes;
+ return name;
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// setLocalTextureTE()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocTexTE( U8 te, LLViewerImage* image, BOOL set_by_user )
+{
+ if( !mIsSelf )
+ {
+ llassert( 0 );
+ return;
+ }
+
+ if( te >= TEX_NUM_ENTRIES )
+ {
+ llassert(0);
+ return;
+ }
+
+ if( getTEImage( te )->getID() == image->getID() )
+ {
+ return;
+ }
+
+ if (isTextureIndexBaked(te))
+ {
+ llassert(0);
+ return;
+ }
+
+ LLTexLayerSet* layer_set = getLayerSet((ETextureIndex)te);
+ if (layer_set)
+ {
+ invalidateComposite(layer_set, set_by_user);
+ }
+
+ setTEImage( te, image );
+ updateMeshTextures();
+
+ if( gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+}
+
+void LLVOAvatar::setupComposites()
+{
+ // Don't invalidate the baked textures we had on start-up.
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if (mHeadLayerSet)
+ {
+ mHeadLayerSet->setUpdatesEnabled( !head_baked );
+ }
+ if (mUpperBodyLayerSet)
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( !upper_baked );
+ }
+ if (mLowerBodyLayerSet)
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( !lower_baked );
+ }
+ if (mEyesLayerSet)
+ {
+ mEyesLayerSet->setUpdatesEnabled( !eyes_baked );
+ }
+ if (mSkirtLayerSet)
+ {
+ mSkirtLayerSet->setUpdatesEnabled( !skirt_baked );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateMeshTextures()
+// Uses the current TE values to set the meshes' and layersets' textures.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateMeshTextures()
+{
+// llinfos << "updateMeshTextures" << llendl;
+ if (gNoRender)
+ {
+ return;
+ }
+ // if user has never specified a texture, assign the default
+ LLViewerImage* default_tex = gImageList.getImage(IMG_DEFAULT);
+ U8 num_TEs = getNumTEs();
+ for (U32 i=0; i<num_TEs; i++)
+ {
+ LLViewerImage* te_image = getTEImage(i);
+ if( (NULL == te_image) || te_image->getID().isNull() || (te_image->getID() == IMG_DEFAULT) )
+ {
+ if( TEX_HAIR == i )
+ {
+ setTEImage(i, default_tex );
+ }
+ else
+ {
+ setTEImage(i, gImageList.getImage(IMG_DEFAULT_AVATAR)); // a special texture that's never rendered.
+ }
+ }
+ }
+
+ // During face edit mode, we don't use baked textures
+ BOOL self_customize = mIsSelf && gAgent.cameraCustomizeAvatar();
+
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Nothing should be baked if we're in customize avatar mode.
+ llassert( !( self_customize &&
+ ( head_baked || upper_baked || lower_baked || eyes_baked ) ) );
+
+ BOOL use_lkg_head_baked = FALSE;
+ BOOL use_lkg_upper_baked = FALSE;
+ BOOL use_lkg_lower_baked = FALSE;
+ BOOL use_lkg_eyes_baked = FALSE;
+ BOOL use_lkg_skirt_baked = FALSE;
+
+ BOOL other_culled = !mIsSelf && mCulled;
+ if( other_culled )
+ {
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR);
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR);
+
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ }
+ else
+ if( !self_customize )
+ {
+ // When you're changing clothes and you're not in Appearance mode,
+ // use the last-known good baked texture until you finish the first
+ // render of the new layerset.
+ use_lkg_head_baked = !head_baked && (mLastHeadBakedID != IMG_DEFAULT_AVATAR) && mHeadLayerSet && !mHeadLayerSet->getComposite()->isInitialized();
+ use_lkg_upper_baked = !upper_baked && (mLastUpperBodyBakedID != IMG_DEFAULT_AVATAR) && mUpperBodyLayerSet && !mUpperBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_lower_baked = !lower_baked && (mLastLowerBodyBakedID != IMG_DEFAULT_AVATAR) && mLowerBodyLayerSet && !mLowerBodyLayerSet->getComposite()->isInitialized();
+ use_lkg_eyes_baked = !eyes_baked && (mLastEyesBakedID != IMG_DEFAULT_AVATAR) && mEyesLayerSet && !mEyesLayerSet->getComposite()->isInitialized();
+ use_lkg_skirt_baked = !skirt_baked && (mLastSkirtBakedID != IMG_DEFAULT_AVATAR) && mSkirtLayerSet && !mSkirtLayerSet->getComposite()->isInitialized();
+
+ if( use_lkg_head_baked )
+ {
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_upper_baked )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_lower_baked )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_eyes_baked )
+ {
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ }
+
+ if( use_lkg_skirt_baked )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ }
+ }
+
+ // Baked textures should be requested from the sim this avatar is on. JC
+ LLHost target_host = getObjectHost();
+ if (!target_host.isOk())
+ {
+ llwarns << "updateMeshTextures: invalid host for object: " << getID() << llendl;
+ }
+
+ // Head
+ if( use_lkg_head_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastHeadBakedID, target_host );
+ mHeadMesh0.setTexture( baked );
+ mHeadMesh1.setTexture( baked );
+ mHeadMesh2.setTexture( baked );
+ mHeadMesh3.setTexture( baked );
+ mHeadMesh4.setTexture( baked );
+ mEyeLashMesh0.setTexture( baked );
+ }
+ else
+ if( !self_customize && head_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_HEAD_BAKED );
+ if( baked->getID() == mLastHeadBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mHeadBakedLoaded = FALSE;
+ mHeadMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mHeadLayerSet && !other_culled )
+ {
+ mHeadLayerSet->createComposite();
+ mHeadLayerSet->setUpdatesEnabled( TRUE );
+ mHeadMesh0.setLayerSet( mHeadLayerSet );
+ mHeadMesh1.setLayerSet( mHeadLayerSet );
+ mHeadMesh2.setLayerSet( mHeadLayerSet );
+ mHeadMesh3.setLayerSet( mHeadLayerSet );
+ mHeadMesh4.setLayerSet( mHeadLayerSet );
+ mEyeLashMesh0.setLayerSet( mHeadLayerSet );
+ }
+ else
+ {
+ mHeadMesh0.setTexture( default_tex );
+ mHeadMesh1.setTexture( default_tex );
+ mHeadMesh2.setTexture( default_tex );
+ mHeadMesh3.setTexture( default_tex );
+ mHeadMesh4.setTexture( default_tex );
+ mEyeLashMesh0.setTexture( default_tex );
+ }
+
+ // Upper body
+ if( use_lkg_upper_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastUpperBodyBakedID, target_host );
+ mUpperBodyMesh0.setTexture( baked );
+ mUpperBodyMesh1.setTexture( baked );
+ mUpperBodyMesh2.setTexture( baked );
+ mUpperBodyMesh3.setTexture( baked );
+ mUpperBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && upper_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_UPPER_BAKED );
+
+ if( baked->getID() == mLastUpperBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mUpperBakedLoaded = FALSE;
+ mUpperMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mUpperBodyLayerSet && !other_culled )
+ {
+ mUpperBodyLayerSet->createComposite();
+ mUpperBodyLayerSet->setUpdatesEnabled( TRUE );
+ mUpperBodyMesh0.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh1.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh2.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh3.setLayerSet( mUpperBodyLayerSet );
+ mUpperBodyMesh4.setLayerSet( mUpperBodyLayerSet );
+ }
+ else
+ {
+ mUpperBodyMesh0.setTexture( default_tex );
+ mUpperBodyMesh1.setTexture( default_tex );
+ mUpperBodyMesh2.setTexture( default_tex );
+ mUpperBodyMesh3.setTexture( default_tex );
+ mUpperBodyMesh4.setTexture( default_tex );
+ }
+
+ // Lower body
+ if( use_lkg_lower_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastLowerBodyBakedID, target_host );
+ mLowerBodyMesh0.setTexture( baked );
+ mLowerBodyMesh1.setTexture( baked );
+ mLowerBodyMesh2.setTexture( baked );
+ mLowerBodyMesh3.setTexture( baked );
+ mLowerBodyMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && lower_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_LOWER_BAKED );
+ if( baked->getID() == mLastLowerBodyBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mLowerBakedLoaded = FALSE;
+ mLowerMaskDiscard = -1;
+ baked->setNeedsAux(TRUE);
+ baked->setLoadedCallback(onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mLowerBodyLayerSet && !other_culled )
+ {
+ mLowerBodyLayerSet->createComposite();
+ mLowerBodyLayerSet->setUpdatesEnabled( TRUE );
+ mLowerBodyMesh0.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh1.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh2.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh3.setLayerSet( mLowerBodyLayerSet );
+ mLowerBodyMesh4.setLayerSet( mLowerBodyLayerSet );
+ }
+ else
+ {
+ mLowerBodyMesh0.setTexture( default_tex );
+ mLowerBodyMesh1.setTexture( default_tex );
+ mLowerBodyMesh2.setTexture( default_tex );
+ mLowerBodyMesh3.setTexture( default_tex );
+ mLowerBodyMesh4.setTexture( default_tex );
+ }
+
+ // Eyes
+ if( use_lkg_eyes_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastEyesBakedID, target_host );
+ mEyeBallLeftMesh0.setTexture( baked );
+ mEyeBallLeftMesh1.setTexture( baked );
+ mEyeBallRightMesh0.setTexture( baked );
+ mEyeBallRightMesh1.setTexture( baked );
+ }
+ else
+ if( !self_customize && eyes_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_EYES_BAKED );
+ if( baked->getID() == mLastEyesBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mEyesBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mEyesLayerSet && !other_culled )
+ {
+ mEyesLayerSet->createComposite();
+ mEyesLayerSet->setUpdatesEnabled( TRUE );
+ mEyeBallLeftMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallLeftMesh1.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh0.setLayerSet( mEyesLayerSet );
+ mEyeBallRightMesh1.setLayerSet( mEyesLayerSet );
+ }
+ else
+ {
+ mEyeBallLeftMesh0.setTexture( default_tex );
+ mEyeBallLeftMesh1.setTexture( default_tex );
+ mEyeBallRightMesh0.setTexture( default_tex );
+ mEyeBallRightMesh1.setTexture( default_tex );
+ }
+
+ // Skirt
+ if( use_lkg_skirt_baked )
+ {
+ LLViewerImage* baked = gImageList.getImageFromHost( mLastSkirtBakedID, target_host );
+ mSkirtMesh0.setTexture( baked );
+ mSkirtMesh1.setTexture( baked );
+ mSkirtMesh2.setTexture( baked );
+ mSkirtMesh3.setTexture( baked );
+ mSkirtMesh4.setTexture( baked );
+ }
+ else
+ if( !self_customize && skirt_baked )
+ {
+ LLViewerImage* baked = getTEImage( TEX_SKIRT_BAKED );
+ if( baked->getID() == mLastSkirtBakedID )
+ {
+ // Even though the file may not be finished loading, we'll consider it loaded and use it (rather than doing compositing).
+ useBakedTexture( baked->getID() );
+ }
+ else
+ {
+ mSkirtBakedLoaded = FALSE;
+ baked->setLoadedCallback(onBakedTextureLoaded, SWITCH_TO_BAKED_DISCARD, FALSE, new LLUUID( mID ) );
+ }
+ }
+ else
+ if( mSkirtLayerSet && !other_culled)
+ {
+ mSkirtLayerSet->createComposite();
+ mSkirtLayerSet->setUpdatesEnabled( TRUE );
+ mSkirtMesh0.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh1.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh2.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh3.setLayerSet( mSkirtLayerSet );
+ mSkirtMesh4.setLayerSet( mSkirtLayerSet );
+ }
+ else
+ {
+ mSkirtMesh0.setTexture( default_tex );
+ mSkirtMesh1.setTexture( default_tex );
+ mSkirtMesh2.setTexture( default_tex );
+ mSkirtMesh3.setTexture( default_tex );
+ mSkirtMesh4.setTexture( default_tex );
+ }
+
+ mHairMesh0.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh1.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh2.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh3.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh4.setTexture( getTEImage( TEX_HAIR ) );
+ mHairMesh5.setTexture( getTEImage( TEX_HAIR ) );
+
+ if( mTexHairColor )
+ {
+ LLColor4 color = mTexHairColor->getColor();
+ mHairMesh0.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh1.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh2.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh3.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh4.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ mHairMesh5.setColor( color.mV[VX], color.mV[VY], color.mV[VZ], color.mV[VW] );
+ }
+
+ // Head
+ BOOL head_baked_ready = (head_baked && mHeadBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), head_baked_ready );
+
+ // Upper body
+ BOOL upper_baked_ready = (upper_baked && mUpperBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), upper_baked_ready );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), upper_baked_ready );
+
+ // Lower body
+ BOOL lower_baked_ready = (lower_baked && mLowerBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), lower_baked_ready );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), lower_baked_ready );
+
+ // Eyes
+ BOOL eyes_baked_ready = (eyes_baked && mEyesBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), eyes_baked_ready );
+
+ // Skirt
+ BOOL skirt_baked_ready = (skirt_baked && mSkirtBakedLoaded) || other_culled;
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), skirt_baked_ready );
+
+ removeMissingBakedTextures();
+}
+
+//-----------------------------------------------------------------------------
+// setLocalTexture()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setLocalTexture( ELocTexIndex idx, LLViewerImage* tex, BOOL baked_version_ready )
+{
+ S32 desired_discard = mIsSelf ? 0 : 2;
+ if (!baked_version_ready)
+ {
+ if (tex != mLocalTexture[idx] || mLocalTextureBaked[idx])
+ {
+ mLocalTextureDiscard[idx] = MAX_DISCARD_LEVEL+1;
+ }
+ if (tex->getID() != IMG_DEFAULT_AVATAR)
+ {
+ if (mLocalTextureDiscard[idx] > desired_discard)
+ {
+ S32 tex_discard = tex->getDiscardLevel();
+ if (tex_discard >= 0 && tex_discard <= desired_discard)
+ {
+ mLocalTextureDiscard[idx] = tex_discard;
+ requestLayerSetUpdate( idx );
+ if( mIsSelf && gAgent.cameraCustomizeAvatar() )
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+ }
+ else
+ {
+ tex->setLoadedCallback( onLocalTextureLoaded, desired_discard, TRUE, new LLAvatarTexData(getID(), idx) );
+ }
+ }
+ tex->setMinDiscardLevel(desired_discard);
+ }
+ }
+ mLocalTextureBaked[idx] = baked_version_ready;
+ mLocalTexture[idx] = tex;
+}
+
+//-----------------------------------------------------------------------------
+// requestLayerSetUploads()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::requestLayerSetUploads()
+{
+ BOOL upper_baked = (getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = (getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL head_baked = (getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = (getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = (getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ if( !head_baked && mHeadLayerSet )
+ {
+ mHeadLayerSet->requestUpload();
+ }
+
+ if( !upper_baked && mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->requestUpload();
+ }
+
+ if( !lower_baked && mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->requestUpload();
+ }
+
+ if( !eyes_baked && mEyesLayerSet )
+ {
+ mEyesLayerSet->requestUpload();
+ }
+
+ if( !skirt_baked && mSkirtLayerSet )
+ {
+ mSkirtLayerSet->requestUpload();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// setCompositeUpdatesEnabled()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCompositeUpdatesEnabled( BOOL b )
+{
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->setUpdatesEnabled( b );
+ }
+
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->setUpdatesEnabled( b );
+ }
+
+}
+
+void LLVOAvatar::addChat(const LLChat& chat)
+{
+ std::deque<LLChat>::iterator chat_iter;
+
+ mChats.push_back(chat);
+
+ S32 chat_length = 0;
+ for( chat_iter = mChats.begin(); chat_iter != mChats.end(); ++chat_iter)
+ {
+ chat_length += chat_iter->mText.size();
+ }
+
+ // remove any excess chat
+ chat_iter = mChats.begin();
+ while ((chat_length > MAX_BUBBLE_CHAT_LENGTH || mChats.size() > MAX_BUBBLE_CHAT_UTTERANCES) && chat_iter != mChats.end())
+ {
+ chat_length -= chat_iter->mText.size();
+ mChats.pop_front();
+ chat_iter = mChats.begin();
+ }
+
+ mChatTimer.reset();
+}
+
+void LLVOAvatar::clearChat()
+{
+ mChats.clear();
+}
+
+S32 LLVOAvatar::getLocalDiscardLevel( S32 index )
+{
+ if (index >= 0 && mLocalTexture[index].notNull() && mLocalTexture[index]->getID() != IMG_DEFAULT_AVATAR)
+ {
+ return mLocalTexture[index]->getDiscardLevel();
+ }
+ else
+ {
+ // We don't care about this (no image associated with the layer) treat as fully loaded.
+ return 0;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataFinal()
+// Returns true is the highest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataFinal( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) == 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) == 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) == 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) == 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) == 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) == 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+//-----------------------------------------------------------------------------
+// isLocalTextureDataAvailable()
+// Returns true is at least the lowest quality discard level exists for every texture
+// in the layerset.
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatar::isLocalTextureDataAvailable( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_HEAD_BODYPAINT ) >= 0;
+ }
+ else if( layerset == mUpperBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_UPPER_SHIRT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_GLOVES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_UPPER_UNDERSHIRT ) >= 0;
+ }
+ else if( layerset == mLowerBodyLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_LOWER_PANTS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_BODYPAINT ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SHOES ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_SOCKS ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_JACKET ) >= 0 &&
+ getLocalDiscardLevel( LOCTEX_LOWER_UNDERPANTS ) >= 0;
+ }
+ else if( layerset == mEyesLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_EYES_IRIS ) >= 0;
+ }
+ else if( layerset == mSkirtLayerSet )
+ {
+ return getLocalDiscardLevel( LOCTEX_SKIRT ) >= 0;
+ }
+
+ llassert(0);
+ return FALSE;
+}
+
+
+//-----------------------------------------------------------------------------
+// getBakedTE()
+// Used by the LayerSet. (Layer sets don't in general know what textures depend on them.)
+//-----------------------------------------------------------------------------
+LLVOAvatar::ETextureIndex LLVOAvatar::getBakedTE( LLTexLayerSet* layerset )
+{
+ if( layerset == mHeadLayerSet )
+ {
+ return TEX_HEAD_BAKED;
+ }
+ else
+ if( layerset == mUpperBodyLayerSet )
+ {
+ return TEX_UPPER_BAKED;
+ }
+ else
+ if( layerset == mLowerBodyLayerSet )
+ {
+ return TEX_LOWER_BAKED;
+ }
+ else
+ if( layerset == mEyesLayerSet )
+ {
+ return TEX_EYES_BAKED;
+ }
+ else
+ if( layerset == mSkirtLayerSet )
+ {
+ return TEX_SKIRT_BAKED;
+ }
+
+ llassert(0);
+ return TEX_HEAD_BAKED;
+}
+
+//-----------------------------------------------------------------------------
+// setNewBakedTexture()
+// A new baked texture has been successfully uploaded and we can start using it now.
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setNewBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ // Baked textures live on other sims.
+ LLHost target_host = getObjectHost();
+ setTEImage( te, gImageList.getImageFromHost( uuid, target_host ) );
+ updateMeshTextures();
+ dirtyMesh();
+
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+ switch( te )
+ {
+ case TEX_HEAD_BAKED:
+ llinfos << "New baked texture: HEAD" << llendl;
+ break;
+ case TEX_UPPER_BAKED:
+ llinfos << "New baked texture: UPPER" << llendl;
+ break;
+ case TEX_LOWER_BAKED:
+ llinfos << "New baked texture: LOWER" << llendl;
+ break;
+ case TEX_EYES_BAKED:
+ llinfos << "New baked texture: EYES" << llendl;
+ break;
+ case TEX_SKIRT_BAKED:
+ llinfos << "New baked texture: SKIRT" << llendl;
+ break;
+ default:
+ llwarns << "New baked texture: unknown te " << te << llendl;
+ break;
+ }
+
+ // dumpAvatarTEs( "setNewBakedTexture() send" );
+ // RN: throttle uploads
+ if (!hasPendingBakedUploads())
+ {
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+bool LLVOAvatar::hasPendingBakedUploads()
+{
+ bool head_pending = (mHeadLayerSet && mHeadLayerSet->getComposite()->uploadPending());
+ bool upper_pending = (mUpperBodyLayerSet && mUpperBodyLayerSet->getComposite()->uploadPending());
+ bool lower_pending = (mLowerBodyLayerSet && mLowerBodyLayerSet->getComposite()->uploadPending());
+ bool eyes_pending = (mEyesLayerSet && mEyesLayerSet->getComposite()->uploadPending());
+ bool skirt_pending = (mSkirtLayerSet && mSkirtLayerSet->getComposite()->uploadPending());
+
+ //llinfos << "TAT: LLVOAvatar::hasPendingBakedUploads()"
+ // << " head_pending " << head_pending
+ // << " upper_pending " << upper_pending
+ // << " lower_pending " << lower_pending
+ // << " eyes_pending " << eyes_pending
+ // << " skirt_pending " << skirt_pending
+ // << llendl;
+
+ if (head_pending || upper_pending || lower_pending || eyes_pending || skirt_pending)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// setCachedBakedTexture()
+// A baked texture id was received from a cache query, make it active
+//-----------------------------------------------------------------------------
+void LLVOAvatar::setCachedBakedTexture( ETextureIndex te, const LLUUID& uuid )
+{
+ setTETexture( te, uuid );
+
+ switch(te)
+ {
+ case TEX_HEAD_BAKED:
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_UPPER_BAKED:
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_LOWER_BAKED:
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_EYES_BAKED:
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->cancelUpload();
+ }
+ break;
+ case TEX_SKIRT_BAKED:
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->cancelUpload();
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeStart()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeStart()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( avatar )
+ {
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ avatar->mSavedTE[ tex_index ] = avatar->getTEImage(tex_index)->getID();
+ avatar->setTEImage( tex_index, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ }
+
+ avatar->updateMeshTextures();
+
+// avatar->dumpAvatarTEs( "onCustomizeStart() send" );
+ gAgent.sendAgentSetAppearance();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// static
+// onCustomizeEnd()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onCustomizeEnd()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ if( !avatar ) return;
+
+ LLHost target_host = avatar->getObjectHost();
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 tex_index = sBakedTextureIndices[i];
+ const LLUUID& saved = avatar->mSavedTE[ tex_index ];
+ if( !saved.isNull() )
+ {
+ avatar->setTEImage( tex_index, gImageList.getImageFromHost( saved, target_host ) );
+ }
+ }
+
+ avatar->updateMeshTextures();
+
+ if( !gQuit )
+ {
+ avatar->requestLayerSetUploads();
+ }
+
+ gAgent.sendAgentSetAppearance();
+ }
+
+BOOL LLVOAvatar::teToColorParams( ETextureIndex te, const char* param_name[3] )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ param_name[0] = "shirt_red";
+ param_name[1] = "shirt_green";
+ param_name[2] = "shirt_blue";
+ break;
+
+ case TEX_LOWER_PANTS:
+ param_name[0] = "pants_red";
+ param_name[1] = "pants_green";
+ param_name[2] = "pants_blue";
+ break;
+
+ case TEX_LOWER_SHOES:
+ param_name[0] = "shoes_red";
+ param_name[1] = "shoes_green";
+ param_name[2] = "shoes_blue";
+ break;
+
+ case TEX_LOWER_SOCKS:
+ param_name[0] = "socks_red";
+ param_name[1] = "socks_green";
+ param_name[2] = "socks_blue";
+ break;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ param_name[0] = "jacket_red";
+ param_name[1] = "jacket_green";
+ param_name[2] = "jacket_blue";
+ break;
+
+ case TEX_UPPER_GLOVES:
+ param_name[0] = "gloves_red";
+ param_name[1] = "gloves_green";
+ param_name[2] = "gloves_blue";
+ break;
+
+ case TEX_UPPER_UNDERSHIRT:
+ param_name[0] = "undershirt_red";
+ param_name[1] = "undershirt_green";
+ param_name[2] = "undershirt_blue";
+ break;
+
+ case TEX_LOWER_UNDERPANTS:
+ param_name[0] = "underpants_red";
+ param_name[1] = "underpants_green";
+ param_name[2] = "underpants_blue";
+ break;
+
+ case TEX_SKIRT:
+ param_name[0] = "skirt_red";
+ param_name[1] = "skirt_green";
+ param_name[2] = "skirt_blue";
+ break;
+
+ default:
+ llassert(0);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL set_by_user )
+{
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ setVisualParamWeight( param_name[0], new_color.mV[VX], set_by_user );
+ setVisualParamWeight( param_name[1], new_color.mV[VY], set_by_user );
+ setVisualParamWeight( param_name[2], new_color.mV[VZ], set_by_user );
+ }
+}
+
+LLColor4 LLVOAvatar::getClothesColor( ETextureIndex te )
+{
+ LLColor4 color;
+ const char* param_name[3];
+ if( teToColorParams( te, param_name ) )
+ {
+ color.mV[VX] = getVisualParamWeight( param_name[0] );
+ color.mV[VY] = getVisualParamWeight( param_name[1] );
+ color.mV[VZ] = getVisualParamWeight( param_name[2] );
+ }
+ return color;
+}
+
+
+
+
+void LLVOAvatar::dumpAvatarTEs( const char* context )
+{
+ llinfos << (mIsSelf ? "Self: " : "Other: ") << context << llendl;
+ for( S32 i=0; i<TEX_NUM_ENTRIES; i++ )
+ {
+ const char* te_name[] = {
+ "TEX_HEAD_BODYPAINT ",
+ "TEX_UPPER_SHIRT ",
+ "TEX_LOWER_PANTS ",
+ "TEX_EYES_IRIS ",
+ "TEX_HAIR ",
+ "TEX_UPPER_BODYPAINT ",
+ "TEX_LOWER_BODYPAINT ",
+ "TEX_LOWER_SHOES ",
+ "TEX_HEAD_BAKED ",
+ "TEX_UPPER_BAKED ",
+ "TEX_LOWER_BAKED ",
+ "TEX_EYES_BAKED ",
+ "TEX_LOWER_SOCKS ",
+ "TEX_UPPER_JACKET ",
+ "TEX_LOWER_JACKET ",
+ "TEX_UPPER_GLOVES ",
+ "TEX_UPPER_UNDERSHIRT ",
+ "TEX_LOWER_UNDERPANTS ",
+ "TEX_SKIRT ",
+ "TEX_SKIRT_BAKED "
+ };
+
+ LLViewerImage* te_image = getTEImage(i);
+ if( !te_image )
+ {
+ llinfos << " " << te_name[i] << ": null ptr" << llendl;
+ }
+ else
+ if( te_image->getID().isNull() )
+ {
+ llinfos << " " << te_name[i] << ": null UUID" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT" << llendl;
+ }
+ else
+ if( te_image->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << " " << te_name[i] << ": IMG_DEFAULT_AVATAR" << llendl;
+ }
+ else
+ {
+ llinfos << " " << te_name[i] << ": " << te_image->getID() << llendl;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// updateAttachmentVisibility()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::updateAttachmentVisibility(U32 camera_mode)
+{
+ for (LLViewerJointAttachment *attachmentPoint = mAttachmentPoints.getFirstData();
+ attachmentPoint;
+ attachmentPoint = mAttachmentPoints.getNextData())
+ {
+ if (attachmentPoint->getIsHUDAttachment())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ switch (camera_mode)
+ {
+ case CAMERA_MODE_MOUSELOOK:
+ if (LLVOAvatar::sVisibleInFirstPerson && attachmentPoint->getVisibleInFirstPerson())
+ {
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ }
+ else
+ {
+ attachmentPoint->setAttachmentVisibility(FALSE);
+ }
+ break;
+ default:
+ attachmentPoint->setAttachmentVisibility(TRUE);
+ break;
+ }
+ }
+ }
+}
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+LLUUID LLVOAvatar::getDefaultTEImageID( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultShirtUUID") );
+ case TEX_LOWER_PANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultPantsUUID") );
+ case TEX_EYES_IRIS: return LLUUID( gSavedSettings.getString("UIImgDefaultEyesUUID") );
+ case TEX_HAIR: return LLUUID( gSavedSettings.getString("UIImgDefaultHairUUID") );
+ case TEX_LOWER_SHOES: return LLUUID( gSavedSettings.getString("UIImgDefaultShoesUUID") );
+ case TEX_LOWER_SOCKS: return LLUUID( gSavedSettings.getString("UIImgDefaultSocksUUID") );
+ case TEX_UPPER_GLOVES: return LLUUID( gSavedSettings.getString("UIImgDefaultGlovesUUID") );
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET: return LLUUID( gSavedSettings.getString("UIImgDefaultJacketUUID") );
+
+ case TEX_UPPER_UNDERSHIRT:
+ case TEX_LOWER_UNDERPANTS: return LLUUID( gSavedSettings.getString("UIImgDefaultUnderwearUUID") );
+
+ case TEX_SKIRT: return LLUUID( gSavedSettings.getString("UIImgDefaultSkirtUUID") );
+
+ default: return IMG_DEFAULT_AVATAR;
+ }
+}
+
+
+
+// Given a texture entry, determine which wearable type owns it.
+// static
+EWearableType LLVOAvatar::getTEWearableType( S32 te )
+{
+ switch( te )
+ {
+ case TEX_UPPER_SHIRT:
+ return WT_SHIRT;
+
+ case TEX_LOWER_PANTS:
+ return WT_PANTS;
+
+ case TEX_EYES_IRIS:
+ return WT_EYES;
+
+ case TEX_HAIR:
+ return WT_HAIR;
+
+ case TEX_HEAD_BODYPAINT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_LOWER_BODYPAINT:
+ return WT_SKIN;
+
+ case TEX_LOWER_SHOES:
+ return WT_SHOES;
+
+ case TEX_LOWER_SOCKS:
+ return WT_SOCKS;
+
+ case TEX_UPPER_JACKET:
+ case TEX_LOWER_JACKET:
+ return WT_JACKET;
+
+ case TEX_UPPER_GLOVES:
+ return WT_GLOVES;
+
+ case TEX_UPPER_UNDERSHIRT:
+ return WT_UNDERSHIRT;
+
+ case TEX_LOWER_UNDERPANTS:
+ return WT_UNDERPANTS;
+
+ case TEX_SKIRT:
+ return WT_SKIRT;
+
+ default:
+ return WT_INVALID;
+ }
+}
+
+// Unlike most wearable functions, this works for both self and other.
+BOOL LLVOAvatar::isWearingWearableType( EWearableType type )
+{
+ if (mIsDummy) return TRUE;
+
+ ETextureIndex indicator_te;
+ switch( type )
+ {
+ case WT_SHIRT:
+ indicator_te = TEX_UPPER_SHIRT;
+ break;
+
+ case WT_PANTS:
+ indicator_te = TEX_LOWER_PANTS;
+ break;
+
+ case WT_SHOES:
+ indicator_te = TEX_LOWER_SHOES;
+ break;
+
+ case WT_SOCKS:
+ indicator_te = TEX_LOWER_SOCKS;
+ break;
+
+ case WT_JACKET:
+ indicator_te = TEX_UPPER_JACKET;
+ // Note: no need to test both upper and lower jacket
+ break;
+
+ case WT_GLOVES:
+ indicator_te = TEX_UPPER_GLOVES;
+ break;
+
+ case WT_UNDERSHIRT:
+ indicator_te = TEX_UPPER_UNDERSHIRT;
+ break;
+
+ case WT_UNDERPANTS:
+ indicator_te = TEX_LOWER_UNDERPANTS;
+ break;
+
+ case WT_SKIRT:
+ indicator_te = TEX_SKIRT;
+ break;
+
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return TRUE; // everyone has all bodyparts
+
+ default:
+ return FALSE;
+ }
+
+ return ( getTEImage(indicator_te)->getID() != IMG_DEFAULT_AVATAR );
+}
+
+
+//-----------------------------------------------------------------------------
+// clampAttachmentPositions()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::clampAttachmentPositions()
+{
+ if (isDead()) return;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment)
+ {
+ attachment->clampObjectPosition();
+ }
+ }
+}
+
+BOOL LLVOAvatar::hasHUDAttachment()
+{
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+LLBBox LLVOAvatar::getHUDBBox()
+{
+ LLBBox bbox;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+
+ // initialize bounding box to contain identity orientation and center point for attached object
+ bbox.addPointLocal(hud_object->getPosition());
+ // add rotated bounding box for attached object
+ bbox.addBBoxAgent(hud_object->getBoundingBoxAgent());
+ for (U32 i = 0; i < hud_object->mChildList.size(); i++)
+ {
+ bbox.addBBoxAgent(hud_object->mChildList[i]->getBoundingBoxAgent());
+ }
+ }
+ }
+
+ return bbox;
+}
+
+void LLVOAvatar::rebuildHUD()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ if (attachment->getIsHUDAttachment() && attachment->getObject(0))
+ {
+ LLViewerObject* hud_object = attachment->getObject(0);
+ LLDrawable* hud_drawable = hud_object->mDrawable;
+
+ if (hud_drawable)
+ {
+ // this assumes that an AGP sync will happen because face has been backlisted,
+ // so that pool has been rebuilt this frame and is scheduled for a sync
+ for(S32 face_index = 0; face_index < hud_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = hud_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ for (U32 child_num = 0; child_num < hud_object->mChildList.size(); ++child_num)
+ {
+ LLViewerObject* childp = hud_object->mChildList[child_num];
+ LLDrawable* child_drawable = childp->mDrawable;
+
+ if (child_drawable)
+ {
+ for(S32 face_index = 0; face_index < child_drawable->getNumFaces(); ++face_index)
+ {
+ LLFace* facep = child_drawable->getFace(face_index);
+ if (facep->isState(LLFace::BACKLIST))
+ {
+ facep->restore();
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// onFirstTEMessageReceived()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::onFirstTEMessageReceived()
+{
+ if( !mFirstTEMessageReceived )
+ {
+ mFirstTEMessageReceived = TRUE;
+
+ BOOL head_baked = ( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL upper_baked = ( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL lower_baked = ( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL eyes_baked = ( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+ BOOL skirt_baked = ( getTEImage( TEX_SKIRT_BAKED )->getID() != IMG_DEFAULT_AVATAR );
+
+ // Use any baked textures that we have even if they haven't downloaded yet.
+ // (That is, don't do a transition from unbaked to baked.)
+ if( head_baked )
+ {
+ mLastHeadBakedID = getTEImage( TEX_HEAD_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_HEAD_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( upper_baked )
+ {
+ mLastUpperBodyBakedID = getTEImage( TEX_UPPER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_UPPER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( lower_baked )
+ {
+ mLastLowerBodyBakedID = getTEImage( TEX_LOWER_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_LOWER_BAKED );
+ image->setNeedsAux(TRUE);
+ image->setLoadedCallback( onBakedTextureMasksLoaded, MORPH_MASK_REQUESTED_DISCARD, TRUE, new LLTextureMaskData( mID ));
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( eyes_baked )
+ {
+ mLastEyesBakedID = getTEImage( TEX_EYES_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_EYES_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ if( skirt_baked )
+ {
+ mLastSkirtBakedID = getTEImage( TEX_SKIRT_BAKED )->getID();
+ LLViewerImage* image = getTEImage( TEX_SKIRT_BAKED );
+ image->setLoadedCallback( onInitialBakedTextureLoaded, MAX_DISCARD_LEVEL, FALSE, new LLUUID( mID ) );
+ }
+
+ updateMeshTextures();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// processAvatarAppearance()
+//-----------------------------------------------------------------------------
+void LLVOAvatar::processAvatarAppearance( LLMessageSystem* mesgsys )
+{
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+// llinfos << "processAvatarAppearance start " << mID << llendl;
+ BOOL is_first_appearance_message = !mFirstAppearanceMessageReceived;
+
+ mFirstAppearanceMessageReceived = TRUE;
+
+ if( mIsSelf )
+ {
+ llwarns << "Received AvatarAppearance for self" << llendl;
+ if( mFirstTEMessageReceived )
+ {
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+ return;
+ }
+ }
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ ESex old_sex = getSex();
+
+// llinfos << "ady LLVOAvatar::processAvatarAppearance()" << llendl;
+// dumpAvatarTEs( "PRE processAvatarAppearance()" );
+ unpackTEMessage(mesgsys, _PREHASH_ObjectData);
+// dumpAvatarTEs( "POST processAvatarAppearance()" );
+
+// llinfos << "Received AvatarAppearance: " << (mIsSelf ? "(self): " : "(other): " ) <<
+// (( getTEImage( TEX_HEAD_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "HEAD " : "head " ) <<
+// (( getTEImage( TEX_UPPER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "UPPER " : "upper " ) <<
+// (( getTEImage( TEX_LOWER_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "LOWER " : "lower " ) <<
+// (( getTEImage( TEX_EYES_BAKED )->getID() != IMG_DEFAULT_AVATAR ) ? "EYES" : "eyes" ) << llendl;
+
+ if( !mFirstTEMessageReceived )
+ {
+ onFirstTEMessageReceived();
+ }
+
+ setCompositeUpdatesEnabled( FALSE );
+ updateMeshTextures(); // enables updates for laysets without baked textures.
+
+ // parse visual params
+ S32 num_blocks = mesgsys->getNumberOfBlocksFast(_PREHASH_VisualParam);
+ if( num_blocks > 1 )
+ {
+ BOOL params_changed = FALSE;
+ BOOL interp_params = FALSE;
+
+ LLVisualParam* param = getFirstVisualParam();
+ if (!param)
+ {
+ llwarns << "No visual params!" << llendl;
+ }
+ else
+ {
+ for( S32 i = 0; i < num_blocks; i++ )
+ {
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+
+ if( !param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ U8 value;
+ mesgsys->getU8Fast(_PREHASH_VisualParam, _PREHASH_ParamValue, value, i);
+ F32 newWeight = U8_to_F32(value, param->getMinWeight(), param->getMaxWeight());
+
+ if (is_first_appearance_message || (param->getWeight() != newWeight))
+ {
+ //llinfos << "Received update for param " << param->getDisplayName() << " at value " << newWeight << llendl;
+ params_changed = TRUE;
+ if(is_first_appearance_message)
+ {
+ param->setWeight(newWeight, FALSE);
+ }
+ else
+ {
+ interp_params = TRUE;
+ param->setAnimationTarget(newWeight, FALSE);
+ }
+ }
+
+ param = getNextVisualParam();
+ }
+ }
+
+ while( param && (param->getGroup() != VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ param = getNextVisualParam();
+ }
+ if( param )
+ {
+ llwarns << "Number of params in AvatarAppearance msg does not match number of params in avatar xml file." << llendl;
+ return;
+ }
+
+ if (params_changed)
+ {
+ if (interp_params)
+ {
+ startAppearanceAnimation(FALSE, FALSE);
+ }
+ updateVisualParams();
+
+ ESex new_sex = getSex();
+ if( old_sex != new_sex )
+ {
+ updateSexDependentLayerSets( FALSE );
+ }
+ }
+ }
+ else
+ {
+ llwarns << "AvatarAppearance msg received without any parameters" << llendl;
+ }
+
+ setCompositeUpdatesEnabled( TRUE );
+
+ llassert( getSex() == ((getVisualParamWeight( "male" ) > 0.5f) ? SEX_MALE : SEX_FEMALE) );
+
+ // If all of the avatars are completely baked, release the global image caches to conserve memory.
+ LLVOAvatar::cullAvatarsByPixelArea();
+
+// llinfos << "processAvatarAppearance end " << mID << llendl;
+}
+
+// static
+void LLVOAvatar::getAnimLabels( LLDynamicArray<const char*>* labels )
+{
+ S32 i;
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ labels->put( gUserAnimStates[i].mLabel );
+ }
+
+ // Special case to trigger away (AFK) state
+ labels->put( "Away From Keyboard" );
+}
+
+// static
+void LLVOAvatar::getAnimNames( LLDynamicArray<const char*>* names )
+{
+ S32 i;
+
+ for( i = 0; i < gUserAnimStatesCount; i++ )
+ {
+ names->put( gUserAnimStates[i].mName );
+ }
+
+ // Special case to trigger away (AFK) state
+ names->put( "enter_away_from_keyboard_state" );
+}
+
+void LLVOAvatar::onBakedTextureMasksLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ //llinfos << "onBakedTextureMasksLoaded: " << src_vi->getID() << llendl;
+ LLMemType mt(LLMemType::MTYPE_AVATAR);
+
+ LLUUID id = src_vi->getID();
+
+ if (!userdata)
+ {
+ return;
+ }
+
+ LLTextureMaskData* maskData = (LLTextureMaskData*) userdata;
+ LLVOAvatar* self = (LLVOAvatar*) gObjectList.findObject( maskData->mAvatarID );
+
+ // if discard level is 2 less than last discard level we processed, or we hit 0,
+ // then generate morph masks
+ if( self && success && (discard_level < maskData->mLastDiscardLevel - 2 || discard_level == 0) )
+ {
+ LLViewerImage* head_baked = self->getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = self->getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = self->getTEImage( TEX_LOWER_BAKED );
+
+ if( aux_src && aux_src->getComponents() == 1 )
+ {
+ if (!aux_src->getData())
+ {
+ llwarns << "No auxiliary source data for onBakedTextureMasksLoaded" << llendl;
+ src_vi->startImageDecode();
+ return;
+ }
+
+ U32 gl_name;
+ glGenTextures(1, (GLuint*) &gl_name );
+ stop_glerror();
+
+ LLImageGL::bindExternalTexture( gl_name, 0, GL_TEXTURE_2D );
+ stop_glerror();
+
+ glTexImage2D(
+ GL_TEXTURE_2D, 0, GL_ALPHA8,
+ aux_src->getWidth(), aux_src->getHeight(),
+ 0, GL_ALPHA, GL_UNSIGNED_BYTE, aux_src->getData());
+ stop_glerror();
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ if( id == head_baked->getID() )
+ {
+ if (self->mHeadLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for head " << id << " discard = " << discard_level << llendl;
+ self->mHeadLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mHeadMaskDiscard = discard_level;
+ if (self->mHeadMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mHeadMaskTexName);
+ }
+ self->mHeadMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ if ( self->mUpperBodyLayerSet)
+ {
+ //llinfos << "onBakedTextureMasksLoaded for upper body " << id << " discard = " << discard_level << llendl;
+ self->mUpperBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mUpperMaskDiscard = discard_level;
+ if (self->mUpperMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mUpperMaskTexName);
+ }
+ self->mUpperMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ if ( self->mLowerBodyLayerSet )
+ {
+ //llinfos << "onBakedTextureMasksLoaded for lower body " << id << " discard = " << discard_level << llendl;
+ self->mLowerBodyLayerSet->applyMorphMask(aux_src->getData(), aux_src->getWidth(), aux_src->getHeight(), 1);
+ maskData->mLastDiscardLevel = discard_level;
+ self->mLowerMaskDiscard = discard_level;
+ if (self->mLowerMaskTexName)
+ {
+ glDeleteTextures(1, (GLuint*) &self->mLowerMaskTexName);
+ }
+ self->mLowerMaskTexName = gl_name;
+ }
+ else
+ {
+ llwarns << "onBakedTextureMasksLoaded: no mHeadLayerSet." << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "onBakedTextureMasksLoaded(): unexpected image id: " << id << llendl;
+ }
+
+ self->dirtyMesh();
+ }
+ else
+ {
+ // this can happen when someone uses an old baked texture possibly provided by
+ // viewer-side baked texture caching
+ llwarns << "Masks loaded callback but NO aux source!" << llendl;
+ }
+ }
+
+ if (final || !success)
+ {
+ delete maskData;
+ }
+
+}
+
+// static
+void LLVOAvatar::onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata )
+{
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (!success && selfp)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+ if (final || !success )
+ {
+ delete avatar_idp;
+ }
+}
+
+void LLVOAvatar::onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata)
+{
+ //llinfos << "onBakedTextureLoaded: " << src_vi->getID() << llendl;
+
+ LLUUID id = src_vi->getID();
+ LLUUID *avatar_idp = (LLUUID *)userdata;
+ LLVOAvatar *selfp = (LLVOAvatar *)gObjectList.findObject(*avatar_idp);
+
+ if (selfp && !success)
+ {
+ selfp->removeMissingBakedTextures();
+ }
+
+ if( final || !success )
+ {
+ delete avatar_idp;
+ }
+
+ if( selfp && success && final )
+ {
+ selfp->useBakedTexture( id );
+ }
+}
+
+
+// Called when baked texture is loaded and also when we start up with a baked texture
+void LLVOAvatar::useBakedTexture( const LLUUID& id )
+{
+// llinfos << "useBakedTexture" << llendl;
+ LLViewerImage* head_baked = getTEImage( TEX_HEAD_BAKED );
+ LLViewerImage* upper_baked = getTEImage( TEX_UPPER_BAKED );
+ LLViewerImage* lower_baked = getTEImage( TEX_LOWER_BAKED );
+ LLViewerImage* eyes_baked = getTEImage( TEX_EYES_BAKED );
+ LLViewerImage* skirt_baked = getTEImage( TEX_SKIRT_BAKED );
+
+ if( id == head_baked->getID() )
+ {
+ mHeadBakedLoaded = TRUE;
+
+ mLastHeadBakedID = id;
+ mHeadMesh0.setTexture( head_baked );
+ mHeadMesh1.setTexture( head_baked );
+ mHeadMesh2.setTexture( head_baked );
+ mHeadMesh3.setTexture( head_baked );
+ mHeadMesh4.setTexture( head_baked );
+ mEyeLashMesh0.setTexture( head_baked );
+ if( mHeadLayerSet )
+ {
+ mHeadLayerSet->destroyComposite();
+ }
+ setLocalTexture( LOCTEX_HEAD_BODYPAINT, getTEImage( TEX_HEAD_BODYPAINT ), TRUE );
+ }
+ else
+ if( id == upper_baked->getID() )
+ {
+ mUpperBakedLoaded = TRUE;
+
+ mLastUpperBodyBakedID = id;
+ mUpperBodyMesh0.setTexture( upper_baked );
+ mUpperBodyMesh1.setTexture( upper_baked );
+ mUpperBodyMesh2.setTexture( upper_baked );
+ mUpperBodyMesh3.setTexture( upper_baked );
+ mUpperBodyMesh4.setTexture( upper_baked );
+ if( mUpperBodyLayerSet )
+ {
+ mUpperBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_UPPER_SHIRT, getTEImage( TEX_UPPER_SHIRT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_BODYPAINT, getTEImage( TEX_UPPER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_JACKET, getTEImage( TEX_UPPER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_GLOVES, getTEImage( TEX_UPPER_GLOVES ), TRUE );
+ setLocalTexture( LOCTEX_UPPER_UNDERSHIRT, getTEImage( TEX_UPPER_UNDERSHIRT ), TRUE );
+ }
+ else
+ if( id == lower_baked->getID() )
+ {
+ mLowerBakedLoaded = TRUE;
+
+ mLastLowerBodyBakedID = id;
+ mLowerBodyMesh0.setTexture( lower_baked );
+ mLowerBodyMesh1.setTexture( lower_baked );
+ mLowerBodyMesh2.setTexture( lower_baked );
+ mLowerBodyMesh3.setTexture( lower_baked );
+ mLowerBodyMesh4.setTexture( lower_baked );
+ if( mLowerBodyLayerSet )
+ {
+ mLowerBodyLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_LOWER_PANTS, getTEImage( TEX_LOWER_PANTS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_BODYPAINT, getTEImage( TEX_LOWER_BODYPAINT ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SHOES, getTEImage( TEX_LOWER_SHOES ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_SOCKS, getTEImage( TEX_LOWER_SOCKS ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_JACKET, getTEImage( TEX_LOWER_JACKET ), TRUE );
+ setLocalTexture( LOCTEX_LOWER_UNDERPANTS, getTEImage( TEX_LOWER_UNDERPANTS ), TRUE );
+ }
+ else
+ if( id == eyes_baked->getID() )
+ {
+ mEyesBakedLoaded = TRUE;
+
+ mLastEyesBakedID = id;
+ mEyeBallLeftMesh0.setTexture( eyes_baked );
+ mEyeBallLeftMesh1.setTexture( eyes_baked );
+ mEyeBallRightMesh0.setTexture( eyes_baked );
+ mEyeBallRightMesh1.setTexture( eyes_baked );
+ if( mEyesLayerSet )
+ {
+ mEyesLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_EYES_IRIS, getTEImage( TEX_EYES_IRIS ), TRUE );
+ }
+ else
+ if( id == skirt_baked->getID() )
+ {
+ mSkirtBakedLoaded = TRUE;
+
+ mLastSkirtBakedID = id;
+ mSkirtMesh0.setTexture( skirt_baked );
+ mSkirtMesh1.setTexture( skirt_baked );
+ mSkirtMesh2.setTexture( skirt_baked );
+ mSkirtMesh3.setTexture( skirt_baked );
+ mSkirtMesh4.setTexture( skirt_baked );
+ if( mSkirtLayerSet )
+ {
+ mSkirtLayerSet->destroyComposite();
+ }
+
+ setLocalTexture( LOCTEX_SKIRT, getTEImage( TEX_SKIRT ), TRUE );
+ }
+
+ dirtyMesh();
+}
+
+// static
+void LLVOAvatar::dumpArchetypeXML( void* )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ apr_file_t* file = ll_apr_file_open(gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"new archetype.xml"), LL_APR_WB );
+ if( !file )
+ {
+ return;
+ }
+
+ apr_file_printf( file, "<?xml version=\"1.0\" encoding=\"US-ASCII\" standalone=\"yes\"?>\n" );
+ apr_file_printf( file, "<linden_genepool version=\"1.0\">\n" );
+ apr_file_printf( file, "\n\t<archetype name=\"???\">\n" );
+
+ // only body parts, not clothing.
+ for( S32 type = WT_SHAPE; type <= WT_EYES; type++ )
+ {
+ const char* wearable_name = LLWearable::typeToTypeName( (EWearableType) type );
+ apr_file_printf( file, "\n\t\t<!-- wearable: %s -->\n", wearable_name );
+
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ LLViewerVisualParam* viewer_param = (LLViewerVisualParam*)param;
+ if( (viewer_param->getWearableType() == type) &&
+ (viewer_param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE) )
+ {
+ apr_file_printf( file, "\t\t<param id=\"%d\" name=\"%s\" value=\"%.3f\"/>\n",
+ viewer_param->getID(), viewer_param->getName().c_str(), viewer_param->getWeight() );
+ }
+ }
+
+ for( S32 te = 0; te < TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ LLViewerImage* te_image = avatar->getTEImage( te );
+ if( te_image )
+ {
+ char uuid_str[UUID_STR_LENGTH];
+ te_image->getID().toString( uuid_str );
+ apr_file_printf( file, "\t\t<texture te=\"%i\" uuid=\"%s\"/>\n", te, uuid_str);
+ }
+ }
+ }
+ }
+ apr_file_printf( file, "\t</archetype>\n" );
+ apr_file_printf( file, "\n</linden_genepool>\n" );
+ apr_file_close( file );
+}
+
+
+// Assumes LLVOAvatar::sInstances has already been sorted.
+S32 LLVOAvatar::getUnbakedPixelAreaRank()
+{
+ S32 rank = 1;
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ if( inst == this )
+ {
+ return rank;
+ }
+ else
+ if( !inst->isDead() && !inst->isFullyBaked() )
+ {
+ rank++;
+ }
+ }
+
+ llassert(0);
+ return 0;
+}
+
+// static
+void LLVOAvatar::cullAvatarsByPixelArea()
+{
+ LLVOAvatar::sInstances.bubbleSortList();
+
+
+ // Update the avatars that have changed status
+ S32 rank = 1;
+
+ for( LLVOAvatar* inst = (LLVOAvatar*)LLCharacter::sInstances.getFirstData();
+ inst;
+ inst = (LLVOAvatar*)LLCharacter::sInstances.getNextData() )
+ {
+ BOOL culled;
+ if( inst->isDead() )
+ {
+ culled = TRUE;
+ }
+ else if( inst->isSelf() || inst->isFullyBaked() )
+ {
+ culled = FALSE;
+ }
+ else
+ {
+ culled = (rank > LLVOAvatar::sMaxOtherAvatarsToComposite) || (inst->mPixelArea < MIN_PIXEL_AREA_FOR_COMPOSITE);
+ rank++;
+ }
+
+ if( inst->mCulled != culled )
+ {
+ inst->mCulled = culled;
+
+ lldebugs << "avatar " << inst->getID() << (culled ? " start culled" : " start not culled" ) << llendl;
+
+ inst->updateMeshTextures();
+ }
+ }
+
+ if( LLVOAvatar::areAllNearbyInstancesBaked() )
+ {
+ LLVOAvatar::deleteCachedImages();
+ }
+}
+
+const LLUUID& LLVOAvatar::grabLocalTexture(ETextureIndex index)
+{
+ if (canGrabLocalTexture(index))
+ {
+ return getTEImage( index )->getID();
+ }
+ return LLUUID::null;
+}
+
+BOOL LLVOAvatar::canGrabLocalTexture(ETextureIndex index)
+{
+ // Check if the texture hasn't been baked yet.
+ if ( getTEImage( index )->getID() == IMG_DEFAULT_AVATAR )
+ {
+ lldebugs << "getTEImage( " << (U32) index << " )->getID() == IMG_DEFAULT_AVATAR" << llendl;
+ return FALSE;
+ }
+
+ // Check permissions of textures that show up in the
+ // baked texture. We don't want people copying people's
+ // work via baked textures.
+ std::vector<ETextureIndex> textures;
+ switch (index)
+ {
+ case TEX_EYES_BAKED:
+ textures.push_back(TEX_EYES_IRIS);
+ break;
+ case TEX_HEAD_BAKED:
+ textures.push_back(TEX_HEAD_BODYPAINT);
+ break;
+ case TEX_UPPER_BAKED:
+ textures.push_back(TEX_UPPER_BODYPAINT);
+ textures.push_back(TEX_UPPER_UNDERSHIRT);
+ textures.push_back(TEX_UPPER_SHIRT);
+ textures.push_back(TEX_UPPER_JACKET);
+ textures.push_back(TEX_UPPER_GLOVES);
+ break;
+ case TEX_LOWER_BAKED:
+ textures.push_back(TEX_LOWER_BODYPAINT);
+ textures.push_back(TEX_LOWER_UNDERPANTS);
+ textures.push_back(TEX_LOWER_PANTS);
+ textures.push_back(TEX_LOWER_JACKET);
+ textures.push_back(TEX_LOWER_SOCKS);
+ textures.push_back(TEX_LOWER_SHOES);
+ break;
+ case TEX_SKIRT_BAKED:
+ textures.push_back(TEX_SKIRT);
+ break;
+ default:
+ return FALSE;
+ break;
+ }
+
+ std::vector<ETextureIndex>::iterator iter = textures.begin();
+ std::vector<ETextureIndex>::iterator end = textures.end();
+ for (; iter != end; ++iter)
+ {
+ ETextureIndex t_index = (*iter);
+ lldebugs << "Checking index " << (U32) t_index << llendl;
+ const LLUUID& texture_id = getTEImage( t_index )->getID();
+ if (texture_id != IMG_DEFAULT_AVATAR)
+ {
+ // Search inventory for this texture.
+ LLViewerInventoryCategory::cat_array_t cats;
+ LLViewerInventoryItem::item_array_t items;
+ LLAssetIDMatches asset_id_matches(texture_id);
+ gInventory.collectDescendentsIf(LLUUID::null,
+ cats,
+ items,
+ LLInventoryModel::INCLUDE_TRASH,
+ asset_id_matches);
+
+ BOOL can_grab = FALSE;
+ lldebugs << "item count for asset " << texture_id << ": " << items.count() << llendl;
+ if (items.count())
+ {
+ // search for full permissions version
+ for (S32 i = 0; i < items.count(); i++)
+ {
+ LLInventoryItem* itemp = items[i];
+ LLPermissions item_permissions = itemp->getPermissions();
+ if ( item_permissions.allowOperationBy(
+ PERM_MODIFY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_COPY, gAgent.getID(), gAgent.getGroupID()) &&
+ item_permissions.allowOperationBy(
+ PERM_TRANSFER, gAgent.getID(), gAgent.getGroupID()) )
+ {
+ can_grab = TRUE;
+ break;
+ }
+ }
+ }
+ if (!can_grab) return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOAvatar::dumpLocalTextures()
+{
+ llinfos << "Local Textures:" << llendl;
+
+ char* names[] = {
+ "Shirt ",
+ "UpperTatoo",
+ "Pants ",
+ "LowerTatoo",
+ "Head Tatoo",
+ "Shoes ",
+ "Socks ",
+ "Upper Jckt",
+ "Lower Jckt",
+ "Gloves ",
+ "Undershirt",
+ "Underpants",
+ "Iris ",
+ "Skirt "};
+
+ ETextureIndex baked_equiv[] = {
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_HEAD_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_UPPER_BAKED,
+ TEX_LOWER_BAKED,
+ TEX_EYES_BAKED,
+ TEX_SKIRT_BAKED };
+
+
+ for( S32 i = 0; i < LOCTEX_NUM_ENTRIES; i++ )
+ {
+ if( getTEImage( baked_equiv[i] )->getID() != IMG_DEFAULT_AVATAR )
+ {
+#if LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs, makes textures
+ // easier to steal. JC
+ llinfos << "LocTex " << names[i] << ": Baked " << llendl;
+#else
+ llinfos << "LocTex " << names[i] << ": Baked " << getTEImage( baked_equiv[i] )->getID() << llendl;
+#endif
+ }
+ else if (mLocalTexture[i].notNull())
+ {
+ if( mLocalTexture[i]->getID() == IMG_DEFAULT_AVATAR )
+ {
+ llinfos << "LocTex " << names[i] << ": None" << llendl;
+ }
+ else
+ {
+ LLViewerImage* image = mLocalTexture[i];
+ F32 data_progress = 0.0f;
+ F32 decode_progress = image->getDecodeProgress(&data_progress);
+
+ llinfos << "LocTex " << names[i] << ": "
+ << "Discard " << image->getDiscardLevel() << ", "
+ << "(" << image->getWidth() << ", " << image->getHeight() << ") "
+#if !LL_RELEASE_FOR_DOWNLOAD
+ // End users don't get to trivially see avatar texture IDs,
+ // makes textures easier to steal
+ << image->getID() << " "
+#endif
+ << "Data: " << (data_progress * 100) << "% "
+ << "Decode: " << (decode_progress * 100) << "% "
+ << "Priority: " << image->getDecodePriority() << " "
+ << (image->needsDecode() ? "pending decode" : "not pending decode")
+ << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "LocTex " << names[i] << ": No LLViewerImage" << llendl;
+ }
+ }
+}
+
+void LLVOAvatar::startAppearanceAnimation(BOOL set_by_user, BOOL play_sound)
+{
+ if(!mAppearanceAnimating)
+ {
+ mAppearanceAnimSetByUser = set_by_user;
+ mAppearanceAnimating = TRUE;
+ mAppearanceMorphTimer.reset();
+ mLastAppearanceBlendTime = 0.f;
+ }
+}
+
+
+void LLVOAvatar::removeMissingBakedTextures()
+{
+ if (!mIsSelf)
+ {
+ return;
+ }
+ BOOL removed = FALSE;
+
+ for( S32 i = 0; i < BAKED_TEXTURE_COUNT; i++ )
+ {
+ S32 te = sBakedTextureIndices[i];
+
+ if( getTEImage( te )->isMissingAsset() )
+ {
+ setTEImage( te, gImageList.getImage(IMG_DEFAULT_AVATAR) );
+ removed = TRUE;
+ }
+ }
+
+ if( removed )
+ {
+ invalidateComposite( mEyesLayerSet, FALSE );
+ invalidateComposite( mHeadLayerSet, FALSE );
+ invalidateComposite( mUpperBodyLayerSet, FALSE );
+ invalidateComposite( mLowerBodyLayerSet, FALSE );
+ invalidateComposite( mSkirtLayerSet, FALSE );
+ updateMeshTextures();
+ requestLayerSetUploads();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarInfo
+//-----------------------------------------------------------------------------
+
+LLVOAvatarInfo::LLVOAvatarInfo()
+ : mTexSkinColorInfo(0), mTexHairColorInfo(0), mTexEyeColorInfo(0)
+{
+}
+
+LLVOAvatarInfo::~LLVOAvatarInfo()
+{
+ std::for_each(mMeshInfoList.begin(), mMeshInfoList.end(), DeletePointer());
+ std::for_each(mSkeletalDistortionInfoList.begin(), mSkeletalDistortionInfoList.end(), DeletePointer());
+ std::for_each(mAttachmentInfoList.begin(), mAttachmentInfoList.end(), DeletePointer());
+ delete mTexSkinColorInfo;
+ delete mTexHairColorInfo;
+ delete mTexEyeColorInfo;
+ std::for_each(mLayerInfoList.begin(), mLayerInfoList.end(), DeletePointer());
+ std::for_each(mDriverInfoList.begin(), mDriverInfoList.end(), DeletePointer());
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarBoneInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarBoneInfo::parseXml(LLXmlTreeNode* node)
+{
+ if (node->hasName("bone"))
+ {
+ mIsJoint = TRUE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ llwarns << "Bone without name" << llendl;
+ return FALSE;
+ }
+ }
+ else if (node->hasName("collision_volume"))
+ {
+ mIsJoint = FALSE;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!node->getFastAttributeString(name_string, mName))
+ {
+ mName = "Collision Volume";
+ }
+ }
+ else
+ {
+ llerrs << "Invalid node " << node->getName() << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle pos_string = LLXmlTree::addAttributeString("pos");
+ if (!node->getFastAttributeVector3(pos_string, mPos))
+ {
+ llwarns << "Bone without position" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle rot_string = LLXmlTree::addAttributeString("rot");
+ if (!node->getFastAttributeVector3(rot_string, mRot))
+ {
+ llwarns << "Bone without rotation" << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle scale_string = LLXmlTree::addAttributeString("scale");
+ if (!node->getFastAttributeVector3(scale_string, mScale))
+ {
+ llwarns << "Bone without scale" << llendl;
+ return FALSE;
+ }
+
+ if (mIsJoint)
+ {
+ static LLStdStringHandle pivot_string = LLXmlTree::addAttributeString("pivot");
+ if (!node->getFastAttributeVector3(pivot_string, mPivot))
+ {
+ llwarns << "Bone without pivot" << llendl;
+ return FALSE;
+ }
+ }
+
+ // parse children
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *child_info = new LLVOAvatarBoneInfo;
+ if (!child_info->parseXml(child))
+ {
+ delete child_info;
+ return FALSE;
+ }
+ mChildList.push_back(child_info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// LLVOAvatarSkeletonInfo::parseXml()
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarSkeletonInfo::parseXml(LLXmlTreeNode* node)
+{
+ static LLStdStringHandle num_bones_string = LLXmlTree::addAttributeString("num_bones");
+ if (!node->getFastAttributeS32(num_bones_string, mNumBones))
+ {
+ llerrs << "Couldn't find number of bones." << llendl;
+ return FALSE;
+ }
+
+ static LLStdStringHandle num_collision_volumes_string = LLXmlTree::addAttributeString("num_collision_volumes");
+ node->getFastAttributeS32(num_collision_volumes_string, mNumCollisionVolumes);
+
+ LLXmlTreeNode* child;
+ for( child = node->getFirstChild(); child; child = node->getNextChild() )
+ {
+ LLVOAvatarBoneInfo *info = new LLVOAvatarBoneInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ llerrs << "Error parsing bone in skeleton file" << llendl;
+ }
+ mBoneInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlSkeletonNode(): parses <skeleton> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlSkeletonNode(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* node = root->getChildByName( "skeleton" );
+ if( !node )
+ {
+ llwarns << "avatar file: missing <skeleton>" << llendl;
+ return FALSE;
+ }
+
+ LLXmlTreeNode* child;
+
+ // SKELETON DISTORTIONS
+ for (child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_skeleton"))
+ {
+ if (child->getChildByName("param_morph"))
+ {
+ llwarns << "Can't specify morph param in skeleton definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolySkeletalDistortionInfo *info = new LLPolySkeletalDistortionInfo;
+ if (!info->parseXml(child))
+ {
+ delete info;
+ return FALSE;
+ }
+
+ mSkeletalDistortionInfoList.push_back(info);
+ }
+
+ // ATTACHMENT POINTS
+ for (child = node->getChildByName( "attachment_point" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ LLVOAvatarAttachmentInfo* info = new LLVOAvatarAttachmentInfo();
+
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (!child->getFastAttributeString(name_string, info->mName))
+ {
+ llwarns << "No name supplied for attachment point." << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle joint_string = LLXmlTree::addAttributeString("joint");
+ if (!child->getFastAttributeString(joint_string, info->mJointName))
+ {
+ llwarns << "No bone declared in attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle position_string = LLXmlTree::addAttributeString("position");
+ if (child->getFastAttributeVector3(position_string, info->mPosition))
+ {
+ info->mHasPosition = TRUE;
+ }
+
+ static LLStdStringHandle rotation_string = LLXmlTree::addAttributeString("rotation");
+ if (child->getFastAttributeVector3(rotation_string, info->mRotationEuler))
+ {
+ info->mHasRotation = TRUE;
+ }
+ static LLStdStringHandle group_string = LLXmlTree::addAttributeString("group");
+ if (child->getFastAttributeS32(group_string, info->mGroup))
+ {
+ if (info->mGroup == -1)
+ info->mGroup = -1111; // -1 = none parsed, < -1 = bad value
+ }
+
+ static LLStdStringHandle id_string = LLXmlTree::addAttributeString("id");
+ if (!child->getFastAttributeS32(id_string, info->mAttachmentID))
+ {
+ llwarns << "No id supplied for attachment point " << info->mName << llendl;
+ delete info;
+ continue;
+ }
+
+ static LLStdStringHandle slot_string = LLXmlTree::addAttributeString("pie_slice");
+ child->getFastAttributeS32(slot_string, info->mPieMenuSlice);
+
+ static LLStdStringHandle visible_in_first_person_string = LLXmlTree::addAttributeString("visible_in_first_person");
+ child->getFastAttributeBOOL(visible_in_first_person_string, info->mVisibleFirstPerson);
+
+ static LLStdStringHandle hud_attachment_string = LLXmlTree::addAttributeString("hud");
+ child->getFastAttributeBOOL(hud_attachment_string, info->mIsHUDAttachment);
+
+ mAttachmentInfoList.push_back(info);
+ }
+
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlMeshNodes(): parses <mesh> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlMeshNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* node = root->getChildByName( "mesh" );
+ node;
+ node = root->getNextNamedChild())
+ {
+ LLVOAvatarMeshInfo *info = new LLVOAvatarMeshInfo;
+
+ // attribute: type
+ static LLStdStringHandle type_string = LLXmlTree::addAttributeString("type");
+ if( !node->getFastAttributeString( type_string, info->mType ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing type attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle lod_string = LLXmlTree::addAttributeString("lod");
+ if (!node->getFastAttributeS32( lod_string, info->mLOD ))
+ {
+ llwarns << "Avatar file: <mesh> is missing lod attribute. Ignoring element. " << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle file_name_string = LLXmlTree::addAttributeString("file_name");
+ if( !node->getFastAttributeString( file_name_string, info->mMeshFileName ) )
+ {
+ llwarns << "Avatar file: <mesh> is missing file_name attribute. Ignoring: " << info->mType << llendl;
+ delete info;
+ return FALSE; // Ignore this element
+ }
+
+ static LLStdStringHandle reference_string = LLXmlTree::addAttributeString("reference");
+ node->getFastAttributeString( reference_string, info->mReferenceMeshName );
+
+ // attribute: min_pixel_area
+ static LLStdStringHandle min_pixel_area_string = LLXmlTree::addAttributeString("min_pixel_area");
+ static LLStdStringHandle min_pixel_width_string = LLXmlTree::addAttributeString("min_pixel_width");
+ if (!node->getFastAttributeF32( min_pixel_area_string, info->mMinPixelArea ))
+ {
+ F32 min_pixel_area = 0.1f;
+ if (node->getFastAttributeF32( min_pixel_width_string, min_pixel_area ))
+ {
+ // this is square root of pixel area (sensible to use linear space in defining lods)
+ min_pixel_area = min_pixel_area * min_pixel_area;
+ }
+ info->mMinPixelArea = min_pixel_area;
+ }
+
+ // Parse visual params for this node only if we haven't already
+ for (LLXmlTreeNode* child = node->getChildByName( "param" );
+ child;
+ child = node->getNextNamedChild())
+ {
+ if (!child->getChildByName("param_morph"))
+ {
+ if (child->getChildByName("param_skeleton"))
+ {
+ llwarns << "Can't specify skeleton param in a mesh definition." << llendl;
+ }
+ else
+ {
+ llwarns << "Unknown param type." << llendl;
+ }
+ continue;
+ }
+
+ LLPolyMorphTargetInfo *morphinfo = new LLPolyMorphTargetInfo();
+ if (!morphinfo->parseXml(child))
+ {
+ delete morphinfo;
+ delete info;
+ return -1;
+ }
+ BOOL shared = FALSE;
+ static LLStdStringHandle shared_string = LLXmlTree::addAttributeString("shared");
+ child->getFastAttributeBOOL(shared_string, shared);
+
+ info->mPolyMorphTargetInfoList.push_back(LLVOAvatarMeshInfo::morph_info_pair_t(morphinfo, shared));
+ }
+
+ mMeshInfoList.push_back(info);
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlColorNodes(): parses <global_color> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlColorNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* color_node = root->getChildByName( "global_color" );
+ color_node;
+ color_node = root->getNextNamedChild())
+ {
+ LLString global_color_name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ if (color_node->getFastAttributeString( name_string, global_color_name ) )
+ {
+ if( global_color_name == "skin_color" )
+ {
+ if (mTexSkinColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of skin_color" << llendl;
+ return FALSE;
+ }
+ mTexSkinColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexSkinColorInfo->parseXml( color_node ) )
+ {
+ delete mTexSkinColorInfo; mTexSkinColorInfo = 0;
+ llwarns << "avatar file: mTexSkinColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "hair_color" )
+ {
+ if (mTexHairColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of hair_color" << llendl;
+ return FALSE;
+ }
+ mTexHairColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexHairColorInfo->parseXml( color_node ) )
+ {
+ delete mTexHairColorInfo; mTexHairColorInfo = 0;
+ llwarns << "avatar file: mTexHairColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ else if( global_color_name == "eye_color" )
+ {
+ if (mTexEyeColorInfo)
+ {
+ llwarns << "avatar file: multiple instances of eye_color" << llendl;
+ return FALSE;
+ }
+ mTexEyeColorInfo = new LLTexGlobalColorInfo;
+ if( !mTexEyeColorInfo->parseXml( color_node ) )
+ {
+ llwarns << "avatar file: mTexEyeColor->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlLayerNodes(): parses <layer_set> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlLayerNodes(LLXmlTreeNode* root)
+{
+ for (LLXmlTreeNode* layer_node = root->getChildByName( "layer_set" );
+ layer_node;
+ layer_node = root->getNextNamedChild())
+ {
+ LLTexLayerSetInfo* layer_info = new LLTexLayerSetInfo();
+ if( layer_info->parseXml( layer_node ) )
+ {
+ mLayerInfoList.push_back(layer_info);
+ }
+ else
+ {
+ delete layer_info;
+ llwarns << "avatar file: layer_set->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+//-----------------------------------------------------------------------------
+// parseXmlDriverNodes(): parses <driver_parameters> nodes from XML tree
+//-----------------------------------------------------------------------------
+BOOL LLVOAvatarInfo::parseXmlDriverNodes(LLXmlTreeNode* root)
+{
+ LLXmlTreeNode* driver = root->getChildByName( "driver_parameters" );
+ if( driver )
+ {
+ for (LLXmlTreeNode* grand_child = driver->getChildByName( "param" );
+ grand_child;
+ grand_child = driver->getNextNamedChild())
+ {
+ if( grand_child->getChildByName( "param_driver" ) )
+ {
+ LLDriverParamInfo* driver_info = new LLDriverParamInfo();
+ if( driver_info->parseXml( grand_child ) )
+ {
+ mDriverInfoList.push_back(driver_info);
+ }
+ else
+ {
+ delete driver_info;
+ llwarns << "avatar file: driver_param->parseXml() failed" << llendl;
+ return FALSE;
+ }
+ }
+ }
+ }
+ return TRUE;
+}
+
+void LLVOAvatar::writeCAL3D(std::string& path, std::string& file_base)
+{
+ char filename[MAX_PATH];
+
+ // reset animated morphs
+ setVisualParamWeight("Blink_Left", 0.f);
+ setVisualParamWeight("Blink_Right", 0.f);
+ setVisualParamWeight("Hands_Relaxed", 1.f);
+ setVisualParamWeight("Hands_Point", 0.f);
+ setVisualParamWeight("Hands_Fist", 0.f);
+ setVisualParamWeight("Hands_Relaxed_L", 0.f);
+ setVisualParamWeight("Hands_Point_L", 0.f);
+ setVisualParamWeight("Hands_Fist_L", 0.f);
+ setVisualParamWeight("Hands_Relaxed_R", 0.f);
+ setVisualParamWeight("Hands_Point_R", 0.f);
+ setVisualParamWeight("Hands_Fist_R", 0.f);
+ setVisualParamWeight("Hands_Salute_R", 0.f);
+ setVisualParamWeight("Hands_Typing", 0.f);
+ setVisualParamWeight("Hands_Peace_R", 0.f);
+ setVisualParamWeight("Hands_Spread_R", 0.f);
+ updateVisualParams();
+
+ sprintf(filename, "%s\\%s_skeleton.xsf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+ apr_file_printf(fp, "<SKELETON VERSION=\"1000\" NUMBONES=\"%d\">\n", sSkeletonInfo->getNumBones() - sSkeletonInfo->getNumCollisionVolumes());
+ mRoot.writeCAL3D(fp);
+ apr_file_printf(fp, "</SKELETON>\n");
+ apr_file_close(fp);
+
+ sprintf(filename, "%s\\%s_mesh_body.xmf", path.c_str(), file_base.c_str());
+ //gDirUtilp->getExpandedFilename(LL_PATH_CHARACTER,"avatar.cal").c_str()
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar file " << filename << llendl;
+ return;
+ }
+
+ BOOL has_skirt = isWearingWearableType(WT_SKIRT);
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", has_skirt ? 8 : 7);
+ mHairMesh0.writeCAL3D(fp, 5, this);
+ mHeadMesh0.writeCAL3D(fp, 0, this);
+ mEyeLashMesh0.writeCAL3D(fp, 0, this);
+ mUpperBodyMesh0.writeCAL3D(fp, 1, this);
+ mLowerBodyMesh0.writeCAL3D(fp, 2, this);
+ mEyeBallLeftMesh0.writeCAL3D(fp, 3, this);
+ mEyeBallRightMesh0.writeCAL3D(fp, 3, this);
+ if (has_skirt)
+ {
+ mSkirtMesh0.writeCAL3D(fp, 4, this);
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // write out material files
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ for (S32 i = 0; i < (has_skirt ? BAKED_TEXTURE_COUNT : BAKED_TEXTURE_COUNT - 1); i++)
+ {
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), i);
+
+ LLViewerImage* viewer_imagep = mTEImages[sBakedTextureIndices[i]];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // output image for hair
+ sprintf(filename, "%s\\%s_material_tex_5.tga", path.c_str(), file_base.c_str());
+ LLViewerImage* viewer_imagep = mTEImages[TEX_HAIR];
+ if (!viewer_imagep->getHasGLTexture())
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ }
+ else
+ {
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ viewer_imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ // save out attachments
+ sprintf(filename, "%s\\%s_mesh_attachments.xmf", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write attachments file " << filename << llendl;
+ return;
+ }
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::multimap<LLUUID, LLMaterialExportInfo*> material_map;
+
+ S32 num_attachment_objects = 0;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->mDrawable.notNull() &&
+ attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ num_attachment_objects += attached_object->mDrawable->getNumFaces();
+ for (U32 i = 0; i < attached_object->mChildList.size(); i++)
+ {
+ LLViewerObject* child_object = attached_object->mChildList[i];
+ num_attachment_objects += child_object->mDrawable->getNumFaces();
+ }
+ }
+ }
+
+ apr_file_printf(fp, "<MESH VERSION=\"1000\" NUMSUBMESH=\"%d\">\n", num_attachment_objects);
+
+ S32 material_index = 6;
+ S32 texture_index = 6;
+ for(LLViewerJointAttachment *attachment = mAttachmentPoints.getFirstData();
+ attachment;
+ attachment = mAttachmentPoints.getNextData())
+ {
+ LLViewerObject *attached_object = attachment->getObject(0);
+ if (attached_object && !attached_object->isDead() && attached_object->getPCode() == LL_PCODE_VOLUME)
+ {
+ LLVOVolume* attached_volume = (LLVOVolume*)attached_object;
+ LLVector3 pos = attachment->getPosition();
+ LLJoint* cur_joint = attachment->getParent();
+ while (cur_joint)
+ {
+ pos += cur_joint->getSkinOffset();
+ cur_joint = (LLViewerJoint*)cur_joint->getParent();
+ }
+ pos *= 100.f;
+ S32 attached_joint_num = attachment->getParent()->mJointNum;
+ LLQuaternion rot = attachment->getRotation();
+ attached_volume->writeCAL3D(fp, path, file_base, attached_joint_num, pos, rot, material_index, texture_index, material_map);
+ }
+ }
+ apr_file_printf(fp, "</MESH>\n");
+ apr_file_close(fp);
+
+ // now dump sample animation
+ LLKeyframeMotion* walk_motion =
+ getSex() == SEX_MALE ? (LLKeyframeMotion*)findMotion(ANIM_AGENT_WALK) : (LLKeyframeMotion*)findMotion(ANIM_AGENT_FEMALE_WALK);
+ if (FALSE)//(walk_motion)
+ {
+ sprintf(filename, "%s\\%s_anim.xaf", path.c_str(), file_base.c_str());
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar animation file " << filename << llendl;
+ return;
+ }
+
+ walk_motion->writeCAL3D(fp);
+
+ apr_file_close(fp);
+ }
+
+ // finally, write out .cfg file
+ sprintf(filename, "%s\\%s_avatar.cfg", path.c_str(), file_base.c_str());
+ fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write avatar config file " << filename << llendl;
+ return;
+ }
+
+ // this version exports animation
+ //apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\nanimation=%s_anim.xaf\n\n", file_base.c_str(), file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "#\n# cal3d model configuration file\n#\n# model: %s_avatar\n#\n\nscale=1.0\n\nskeleton=%s_skeleton.xsf\n\n", file_base.c_str(), file_base.c_str());
+ apr_file_printf(fp, "mesh=%s_mesh_body.xmf\nmesh=%s_mesh_attachments.xmf\n", file_base.c_str(), file_base.c_str());
+
+ for (S32 i = 0; i < material_index; i++)
+ {
+ apr_file_printf(fp, "material=%s_material_%d.xrf\n", file_base.c_str(), i);
+ }
+ apr_file_close(fp);
+
+ for(S32 i = 0; i < 6; i++)
+ {
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), i);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ // for hair material, use hair color...otherwise use white for entire body
+ LLColor4U material_color = (i == 5) ? mTexHairColor->getColor() : LLColor4U::white;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), i);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+ // write out material files
+ for(material_it_t material_it = material_map.begin(); material_it != material_map.end(); ++material_it)
+ {
+ LLMaterialExportInfo* export_info = material_it->second;
+
+ sprintf(filename, "%s\\%s_material_%d.xrf", path.c_str(), file_base.c_str(), export_info->mMaterialIndex);
+ apr_file_t* fp = ll_apr_file_open(filename, LL_APR_W);
+ if (!fp)
+ {
+ llwarns << "Unable to write material definition file " << filename << llendl;
+ return;
+ }
+
+ LLColor4U material_color = export_info->mColor;
+
+ apr_file_printf(fp, "<HEADER MAGIC=\"XRF\" VERSION=\"900\" />\n<MATERIAL NUMMAPS=\"1\">\n");
+ apr_file_printf(fp, " <AMBIENT>%d %d %d %d</AMBIENT>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <DIFFUSE>%d %d %d %d</DIFFUSE>\n", material_color.mV[VX], material_color.mV[VY], material_color.mV[VZ], material_color.mV[VW]);
+ apr_file_printf(fp, " <SPECULAR>0 0 0 0</SPECULAR>\n");
+ apr_file_printf(fp, " <SHININESS>1.0</SHININESS>\n");
+ apr_file_printf(fp, " <MAP>%s_material_tex_%d.tga</MAP>\n", file_base.c_str(), export_info->mTextureIndex);
+ apr_file_printf(fp, "</MATERIAL>\n");
+
+ apr_file_close(fp);
+ }
+
+
+ std::for_each(material_map.begin(), material_map.end(), DeletePairedPointer());
+ material_map.clear();
+}
+
+// warning: order(N) not order(1)
+S32 LLVOAvatar::getAttachmentCount()
+{
+ S32 count = mAttachmentPoints.getLength();
+ return count;
+}
+
+//virtual
+void LLVOAvatar::updateRegion(LLViewerRegion *regionp)
+{
+ if (mIsSelf)
+ {
+ if (regionp->getHandle() != mLastRegionHandle)
+ {
+ if (mLastRegionHandle != 0)
+ {
+ ++mRegionCrossingCount;
+ F64 delta = (F64)mRegionCrossingTimer.getElapsedTimeF32();
+ F64 avg = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_AVG);
+ F64 delta_avg = (delta + avg*(mRegionCrossingCount-1)) / mRegionCrossingCount;
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_AVG, delta_avg);
+
+ F64 max = (mRegionCrossingCount == 1) ? 0 : gViewerStats->getStat(LLViewerStats::ST_CROSSING_MAX);
+ max = llmax(delta, max);
+ gViewerStats->setStat(LLViewerStats::ST_CROSSING_MAX, max);
+ }
+ mLastRegionHandle = regionp->getHandle();
+ }
+ mRegionCrossingTimer.reset();
+ }
+}
+
+LLString LLVOAvatar::getFullname() const
+{
+ LLString name;
+
+ LLNameValue* first = getNVPair("FirstName");
+ LLNameValue* last = getNVPair("LastName");
+ if (first && last)
+ {
+ name += first->getString();
+ name += " ";
+ name += last->getString();
+ }
+
+ return name;
+}
+
+LLTexLayerSet* LLVOAvatar::getLayerSet(ETextureIndex index) const
+{
+ switch( index )
+ {
+ case TEX_HEAD_BAKED:
+ case TEX_HEAD_BODYPAINT:
+ return mHeadLayerSet;
+
+ case TEX_UPPER_BAKED:
+ case TEX_UPPER_SHIRT:
+ case TEX_UPPER_BODYPAINT:
+ case TEX_UPPER_JACKET:
+ case TEX_UPPER_GLOVES:
+ case TEX_UPPER_UNDERSHIRT:
+ return mUpperBodyLayerSet;
+
+ case TEX_LOWER_BAKED:
+ case TEX_LOWER_PANTS:
+ case TEX_LOWER_BODYPAINT:
+ case TEX_LOWER_SHOES:
+ case TEX_LOWER_SOCKS:
+ case TEX_LOWER_JACKET:
+ case TEX_LOWER_UNDERPANTS:
+ return mLowerBodyLayerSet;
+
+ case TEX_EYES_BAKED:
+ case TEX_EYES_IRIS:
+ return mEyesLayerSet;
+
+ case TEX_SKIRT_BAKED:
+ case TEX_SKIRT:
+ return mSkirtLayerSet;
+
+ case TEX_HAIR:
+ default:
+ return NULL;
+ }
+}
+
+LLHost LLVOAvatar::getObjectHost() const
+{
+ LLViewerRegion* region = getRegion();
+ if (region && !isDead())
+ {
+ return region->getHost();
+ }
+ else
+ {
+ return LLHost::invalid;
+ }
+}
diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h
new file mode 100644
index 0000000000..a47321cb90
--- /dev/null
+++ b/indra/newview/llvoavatar.h
@@ -0,0 +1,897 @@
+/**
+ * @file llvoavatar.h
+ * @brief Declaration of LLVOAvatar class which is a derivation fo
+ * LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOAVATAR_H
+#define LL_LLVOAVATAR_H
+
+// May help Visual Studio avoid opening this file.
+
+#include <set>
+#include <map>
+#include <algorithm>
+#include <deque>
+
+#include "llchat.h"
+#include "llviewerobject.h"
+#include "lljointsolverrp3.h"
+#include "llviewerjointshape.h"
+#include "llviewerjointmesh.h"
+#include "llviewerjointattachment.h"
+#include "llcharacter.h"
+#include "material_codes.h"
+#include "llanimationstates.h"
+#include "v4coloru.h"
+#include "llstring.h"
+#include "llframetimer.h"
+#include "llxmltree.h"
+#include "llwearable.h"
+
+const S32 VOAVATAR_SCRATCH_TEX_WIDTH = 512;
+const S32 VOAVATAR_SCRATCH_TEX_HEIGHT = 512;
+
+const LLUUID ANIM_AGENT_BODY_NOISE = LLUUID("9aa8b0a6-0c6f-9518-c7c3-4f41f2c001ad"); //"body_noise"
+const LLUUID ANIM_AGENT_BREATHE_ROT = LLUUID("4c5a103e-b830-2f1c-16bc-224aa0ad5bc8"); //"breathe_rot"
+const LLUUID ANIM_AGENT_EDITING = LLUUID("2a8eba1d-a7f8-5596-d44a-b4977bf8c8bb"); //"editing"
+const LLUUID ANIM_AGENT_EYE = LLUUID("5c780ea8-1cd1-c463-a128-48c023f6fbea"); //"eye"
+const LLUUID ANIM_AGENT_FLY_ADJUST = LLUUID("db95561f-f1b0-9f9a-7224-b12f71af126e"); //"fly_adjust"
+const LLUUID ANIM_AGENT_HAND_MOTION = LLUUID("ce986325-0ba7-6e6e-cc24-b17c4b795578"); //"hand_motion"
+const LLUUID ANIM_AGENT_HEAD_ROT = LLUUID("e6e8d1dd-e643-fff7-b238-c6b4b056a68d"); //"head_rot"
+const LLUUID ANIM_AGENT_PELVIS_FIX = LLUUID("0c5dd2a2-514d-8893-d44d-05beffad208b"); //"pelvis_fix"
+const LLUUID ANIM_AGENT_TARGET = LLUUID("0e4896cb-fba4-926c-f355-8720189d5b55"); //"target"
+const LLUUID ANIM_AGENT_WALK_ADJUST = LLUUID("829bc85b-02fc-ec41-be2e-74cc6dd7215d"); //"walk_adjust"
+
+class LLChat;
+class LLXmlTreeNode;
+class LLTexLayerSet;
+class LLTexGlobalColor;
+class LLTexGlobalColorInfo;
+class LLTexLayerSetInfo;
+class LLDriverParamInfo;
+
+class LLHUDText;
+class LLHUDEffectSpiral;
+
+typedef enum e_mesh_id
+{
+ MESH_ID_HAIR,
+ MESH_ID_HEAD,
+ MESH_ID_UPPER_BODY,
+ MESH_ID_LOWER_BODY,
+ MESH_ID_SKIRT
+} eMeshID;
+
+typedef enum e_render_name
+{
+ RENDER_NAME_NEVER,
+ RENDER_NAME_FADE,
+ RENDER_NAME_ALWAYS
+} eRenderName;
+
+const S32 BAKED_TEXTURE_COUNT = 5; // number of values in ETextureIndex that are pre-composited
+
+//------------------------------------------------------------------------
+// LLVOAvatar Support classes
+//------------------------------------------------------------------------
+
+class LLVOAvatarBoneInfo
+{
+ friend class LLVOAvatar;
+ friend class LLVOAvatarSkeletonInfo;
+public:
+ LLVOAvatarBoneInfo() : mIsJoint(FALSE) {}
+ ~LLVOAvatarBoneInfo()
+ {
+ std::for_each(mChildList.begin(), mChildList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+
+protected:
+ LLString mName;
+ BOOL mIsJoint;
+ LLVector3 mPos;
+ LLVector3 mRot;
+ LLVector3 mScale;
+ LLVector3 mPivot;
+ typedef std::vector<LLVOAvatarBoneInfo*> child_list_t;
+ child_list_t mChildList;
+};
+
+class LLVOAvatarSkeletonInfo
+{
+ friend class LLVOAvatar;
+public:
+ LLVOAvatarSkeletonInfo() :
+ mNumBones(0), mNumCollisionVolumes(0) {}
+ ~LLVOAvatarSkeletonInfo()
+ {
+ std::for_each(mBoneInfoList.begin(), mBoneInfoList.end(), DeletePointer());
+ }
+ BOOL parseXml(LLXmlTreeNode* node);
+ S32 getNumBones() { return mNumBones; }
+ S32 getNumCollisionVolumes() { return mNumCollisionVolumes; }
+
+protected:
+ S32 mNumBones;
+ S32 mNumCollisionVolumes;
+ typedef std::vector<LLVOAvatarBoneInfo*> bone_info_list_t;
+ bone_info_list_t mBoneInfoList;
+};
+
+
+//------------------------------------------------------------------------
+// LLVOAvatarInfo
+// One instance (in LLVOAvatar) with common data parsed from the XML files
+//------------------------------------------------------------------------
+class LLVOAvatarInfo
+{
+ friend class LLVOAvatar;
+public:
+ LLVOAvatarInfo();
+ ~LLVOAvatarInfo();
+
+protected:
+ BOOL parseXmlSkeletonNode(LLXmlTreeNode* root);
+ BOOL parseXmlMeshNodes(LLXmlTreeNode* root);
+ BOOL parseXmlColorNodes(LLXmlTreeNode* root);
+ BOOL parseXmlLayerNodes(LLXmlTreeNode* root);
+ BOOL parseXmlDriverNodes(LLXmlTreeNode* root);
+
+ struct LLVOAvatarMeshInfo
+ {
+ typedef std::pair<LLPolyMorphTargetInfo*,BOOL> morph_info_pair_t;
+ typedef std::vector<morph_info_pair_t> morph_info_list_t;
+
+ LLVOAvatarMeshInfo() : mLOD(0), mMinPixelArea(.1f) {}
+ ~LLVOAvatarMeshInfo()
+ {
+ morph_info_list_t::iterator iter;
+ for (iter = mPolyMorphTargetInfoList.begin(); iter != mPolyMorphTargetInfoList.end(); iter++)
+ {
+ delete iter->first;
+ }
+ mPolyMorphTargetInfoList.clear();
+ }
+
+ LLString mType;
+ S32 mLOD;
+ LLString mMeshFileName;
+ LLString mReferenceMeshName;
+ F32 mMinPixelArea;
+ morph_info_list_t mPolyMorphTargetInfoList;
+ };
+ typedef std::vector<LLVOAvatarMeshInfo*> mesh_info_list_t;
+ mesh_info_list_t mMeshInfoList;
+
+ typedef std::vector<LLPolySkeletalDistortionInfo*> skeletal_distortion_info_list_t;
+ skeletal_distortion_info_list_t mSkeletalDistortionInfoList;
+
+ struct LLVOAvatarAttachmentInfo
+ {
+ LLVOAvatarAttachmentInfo()
+ : mGroup(-1), mAttachmentID(-1), mPieMenuSlice(-1), mVisibleFirstPerson(FALSE),
+ mIsHUDAttachment(FALSE), mHasPosition(FALSE), mHasRotation(FALSE) {}
+ LLString mName;
+ LLString mJointName;
+ LLVector3 mPosition;
+ LLVector3 mRotationEuler;
+ S32 mGroup;
+ S32 mAttachmentID;
+ S32 mPieMenuSlice;
+ BOOL mVisibleFirstPerson;
+ BOOL mIsHUDAttachment;
+ BOOL mHasPosition;
+ BOOL mHasRotation;
+ };
+ typedef std::vector<LLVOAvatarAttachmentInfo*> attachment_info_list_t;
+ attachment_info_list_t mAttachmentInfoList;
+
+ LLTexGlobalColorInfo *mTexSkinColorInfo;
+ LLTexGlobalColorInfo *mTexHairColorInfo;
+ LLTexGlobalColorInfo *mTexEyeColorInfo;
+
+ typedef std::vector<LLTexLayerSetInfo*> layer_info_list_t;
+ layer_info_list_t mLayerInfoList;
+
+ typedef std::vector<LLDriverParamInfo*> driver_info_list_t;
+ driver_info_list_t mDriverInfoList;
+};
+
+//------------------------------------------------------------------------
+// LLVOAvatar
+//------------------------------------------------------------------------
+class LLVOAvatar :
+ public LLViewerObject,
+ public LLCharacter
+{
+protected:
+ virtual ~LLVOAvatar();
+
+public:
+ LLVOAvatar(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ /*virtual*/ void markDead();
+
+ //--------------------------------------------------------------------
+ // LLViewerObject interface
+ //--------------------------------------------------------------------
+ static void initClass(); // Initialize data that's only inited once per class.
+ static void cleanupClass(); // Cleanup data that's only inited once per class.
+ static BOOL parseSkeletonFile(const LLString& filename);
+ virtual U32 processUpdateMessage( LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ virtual BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ void setFootPlane(const LLVector4 &plane) { mFootPlane = plane; }
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+
+ U32 renderSkinned(EAvatarRenderPass pass);
+ U32 renderRigid();
+
+ void renderCollisionVolumes();
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ // If setting a baked texture, need to request it from a non-local sim.
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID& uuid);
+
+ void updateVisibility(BOOL force_invisible);
+ void updateAttachmentVisibility(U32 camera_mode);
+ void clampAttachmentPositions();
+ S32 getAttachmentCount(); // Warning: order(N) not order(1)
+
+ BOOL hasHUDAttachment();
+ LLBBox getHUDBBox();
+// void renderHUD(BOOL for_select); // old
+ void rebuildHUD();
+
+ static void updateAllAvatarVisiblity();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ void updateShadowFaces();
+
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ void updateJointLODs();
+
+ void writeCAL3D(std::string& path, std::string& file_base);
+
+ virtual void updateRegion(LLViewerRegion *regionp);
+
+ //--------------------------------------------------------------------
+ // texture entry assignment
+ //--------------------------------------------------------------------
+ enum ETextureIndex
+ {
+ TEX_HEAD_BODYPAINT = 0,
+ TEX_UPPER_SHIRT = 1,
+ TEX_LOWER_PANTS = 2,
+ TEX_EYES_IRIS = 3,
+ TEX_HAIR = 4,
+ TEX_UPPER_BODYPAINT = 5,
+ TEX_LOWER_BODYPAINT = 6,
+ TEX_LOWER_SHOES = 7,
+ TEX_HEAD_BAKED = 8, // Pre-composited
+ TEX_UPPER_BAKED = 9, // Pre-composited
+ TEX_LOWER_BAKED = 10, // Pre-composited
+ TEX_EYES_BAKED = 11, // Pre-composited
+ TEX_LOWER_SOCKS = 12,
+ TEX_UPPER_JACKET = 13,
+ TEX_LOWER_JACKET = 14,
+ TEX_UPPER_GLOVES = 15,
+ TEX_UPPER_UNDERSHIRT = 16,
+ TEX_LOWER_UNDERPANTS = 17,
+ TEX_SKIRT = 18,
+ TEX_SKIRT_BAKED = 19, // Pre-composited
+ TEX_NUM_ENTRIES = 20
+ };
+ // Note: if TEX_NUM_ENTRIES changes, update AGENT_TEXTURES in llagentinfo.h, mTextureIndexBaked, and BAKED_TEXTURE_COUNT
+
+ static BOOL isTextureIndexBaked( S32 i )
+ {
+ switch(i)
+ {
+ case TEX_HEAD_BAKED:
+ case TEX_UPPER_BAKED:
+ case TEX_LOWER_BAKED:
+ case TEX_EYES_BAKED:
+ case TEX_SKIRT_BAKED:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ //--------------------------------------------------------------------
+ // LLCharacter interface
+ //--------------------------------------------------------------------
+ virtual const char *getAnimationPrefix() { return "avatar"; }
+ virtual LLJoint *getRootJoint() { return &mRoot; }
+ virtual LLVector3 getCharacterPosition();
+ virtual LLQuaternion getCharacterRotation();
+ virtual LLVector3 getCharacterVelocity();
+ virtual LLVector3 getCharacterAngularVelocity();
+ virtual F32 getTimeDilation();
+ virtual void getGround(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ virtual BOOL allocateCharacterJoints( U32 num );
+ virtual LLJoint *getCharacterJoint( U32 num );
+ virtual void requestStopMotion( LLMotion* motion );
+ virtual F32 getPixelArea();
+ virtual LLPolyMesh* getHeadMesh();
+ virtual LLPolyMesh* getUpperBodyMesh();
+ virtual LLVector3d getPosGlobalFromAgent(const LLVector3 &position);
+ virtual LLVector3 getPosAgentFromGlobal(const LLVector3d &position);
+ virtual void updateVisualParams();
+ virtual BOOL startMotion(const LLUUID& id, F32 time_offset = 0.f);
+ virtual BOOL stopMotion(const LLUUID& id, BOOL stop_immediate = FALSE);
+ virtual void stopMotionFromSource(const LLUUID& source_id);
+ virtual LLVector3 getVolumePos(S32 joint_index, LLVector3& volume_offset);
+ virtual LLJoint* findCollisionVolume(U32 volume_id);
+ virtual S32 getCollisionVolumeID(std::string &name);
+ virtual void addDebugText(const char* text);
+ virtual const LLUUID& getID();
+ virtual LLJoint *getJoint( const std::string &name );
+
+ //--------------------------------------------------------------------
+ // Other public functions
+ //--------------------------------------------------------------------
+ BOOL allocateCollisionVolumes( U32 num );
+
+ static void getAnimLabels( LLDynamicArray<const char*>* labels );
+ static void getAnimNames( LLDynamicArray<const char*>* names );
+
+ static void onCustomizeStart();
+ static void onCustomizeEnd();
+
+ void getLocalTextureByteCount( S32* gl_byte_count );
+ static void dumpTotalLocalTextureByteCount();
+ LLMotion* findMotion(const LLUUID& id);
+
+ BOOL isVisible();
+ BOOL isSelf() { return mIsSelf; }
+ BOOL isCulled() { return mCulled; }
+
+ S32 getUnbakedPixelAreaRank();
+ static void cullAvatarsByPixelArea();
+
+ void dumpLocalTextures();
+ const LLUUID& grabLocalTexture(ETextureIndex index);
+ BOOL canGrabLocalTexture(ETextureIndex index);
+ void startAppearanceAnimation(BOOL set_by_user, BOOL play_sound);
+
+ void setCompositeUpdatesEnabled(BOOL b);
+
+ void addChat(const LLChat& chat);
+ void clearChat();
+ void startTyping() { mTyping = TRUE; mTypingTimer.reset(); }
+ void stopTyping() { mTyping = FALSE; }
+
+ // Returns "FirstName LastName"
+ LLString getFullname() const;
+
+ //--------------------------------------------------------------------
+ // internal (pseudo-private) functions
+ //--------------------------------------------------------------------
+ F32 getPelvisToFoot() { return mPelvisToFoot; }
+
+ void buildCharacter();
+ void releaseMeshData();
+ void restoreMeshData();
+ void updateMeshData();
+
+ void computeBodySize();
+
+ void updateCharacter(LLAgent &agent);
+ void updateHeadOffset();
+
+ LLUUID& getStepSound();
+ void processAnimationStateChanges();
+ BOOL processSingleAnimationStateChange(const LLUUID &anim_id, BOOL start);
+ void resetAnimations();
+ BOOL isAnyAnimationSignaled(const LLUUID *anim_array, const S32 num_anims);
+
+ BOOL needsRenderBeam();
+
+ // Utility functions
+ void resolveHeightGlobal(const LLVector3d &inPos, LLVector3d &outPos, LLVector3 &outNorm);
+ void resolveHeightAgent(const LLVector3 &inPos, LLVector3 &outPos, LLVector3 &outNorm);
+ void resolveRayCollisionAgent(const LLVector3d start_pt, const LLVector3d end_pt, LLVector3d &out_pos, LLVector3 &out_norm);
+
+ void slamPosition(); // Slam position to transmitted position (for teleport);
+
+ BOOL loadAvatar();
+ BOOL setupBone(LLVOAvatarBoneInfo* info, LLViewerJoint* parent);
+ BOOL buildSkeleton(LLVOAvatarSkeletonInfo *info);
+
+ // morph targets and such
+ void processAvatarAppearance( LLMessageSystem* mesgsys );
+ void onFirstTEMessageReceived();
+ void updateSexDependentLayerSets( BOOL set_by_user );
+ void dirtyMesh(); // Dirty the avatar mesh
+
+ virtual void setParent(LLViewerObject* parent);
+ virtual void addChild(LLViewerObject *childp);
+ virtual void removeChild(LLViewerObject *childp);
+
+ BOOL attachObject(LLViewerObject *viewer_object);
+ BOOL detachObject(LLViewerObject *viewer_object);
+ void lazyAttach();
+
+ void sitOnObject(LLViewerObject *sit_object);
+ void getOffObject();
+
+ BOOL isWearingAttachment( const LLUUID& inv_item_id );
+ LLViewerObject* getWornAttachment( const LLUUID& inv_item_id );
+ const LLString getAttachedPointName(const LLUUID& inv_item_id);
+
+ static LLVOAvatar* findAvatarFromAttachment( LLViewerObject* obj );
+
+ void updateMeshTextures();
+
+ //--------------------------------------------------------------------
+ // local textures for compositing.
+ //--------------------------------------------------------------------
+ enum ELocTexIndex
+ {
+ LOCTEX_UPPER_SHIRT = 0,
+ LOCTEX_UPPER_BODYPAINT = 1,
+ LOCTEX_LOWER_PANTS = 2,
+ LOCTEX_LOWER_BODYPAINT = 3,
+ LOCTEX_HEAD_BODYPAINT = 4,
+ LOCTEX_LOWER_SHOES = 5,
+ LOCTEX_LOWER_SOCKS = 6,
+ LOCTEX_UPPER_JACKET = 7,
+ LOCTEX_LOWER_JACKET = 8,
+ LOCTEX_UPPER_GLOVES = 9,
+ LOCTEX_UPPER_UNDERSHIRT = 10,
+ LOCTEX_LOWER_UNDERPANTS = 11,
+ LOCTEX_EYES_IRIS = 12,
+ LOCTEX_SKIRT = 13,
+ LOCTEX_NUM_ENTRIES = 14
+ };
+
+ //--------------------------------------------------------------------
+ // texture compositing (used only by the LLTexLayer series of classes)
+ //--------------------------------------------------------------------
+ LLColor4 getGlobalColor( const LLString& color_name );
+ BOOL isLocalTextureDataAvailable( LLTexLayerSet* layerset );
+ BOOL isLocalTextureDataFinal( LLTexLayerSet* layerset );
+ ETextureIndex getBakedTE( LLTexLayerSet* layerset );
+ void updateComposites();
+ void onGlobalColorChanged( LLTexGlobalColor* global_color, BOOL set_by_user );
+ BOOL getLocalTextureRaw( S32 index, LLImageRaw* image_raw_pp );
+ BOOL getLocalTextureGL( S32 index, LLImageGL** image_gl_pp );
+ const LLUUID& getLocalTextureID( S32 index );
+ LLGLuint getScratchTexName( LLGLenum format, U32* texture_bytes );
+ BOOL bindScratchTexture( LLGLenum format );
+ void invalidateComposite( LLTexLayerSet* layerset, BOOL set_by_user );
+ void forceBakeAllTextures(bool slam_for_debug = false);
+ static void processRebakeAvatarTextures(LLMessageSystem* msg, void**);
+ void setNewBakedTexture( ETextureIndex i, const LLUUID& uuid );
+ void setCachedBakedTexture( ETextureIndex i, const LLUUID& uuid );
+ void requestLayerSetUploads();
+ bool hasPendingBakedUploads();
+ static void onLocalTextureLoaded( BOOL succcess, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void dumpArchetypeXML( void* );
+ static void dumpScratchTextureByteCount();
+ static void dumpBakedStatus();
+ static void deleteCachedImages();
+ static void destroyGL();
+ static void restoreGL();
+ static void initVertexPrograms();
+ static void cleanupVertexPrograms();
+ static enum EWearableType getTEWearableType( S32 te );
+ static LLUUID getDefaultTEImageID( S32 te );
+
+ //--------------------------------------------------------------------
+ // Clothing colors (conventience functions to access visual parameters
+ //--------------------------------------------------------------------
+ void setClothesColor( ETextureIndex te, const LLColor4& new_color, BOOL set_by_user );
+ LLColor4 getClothesColor( ETextureIndex te );
+ BOOL teToColorParams( ETextureIndex te, const char* param_name[3] );
+
+ BOOL isWearingWearableType( EWearableType type );
+
+ //--------------------------------------------------------------------
+ // texture compositing
+ //--------------------------------------------------------------------
+ void setLocTexTE( U8 te, LLViewerImage* image, BOOL set_by_user );
+ void setupComposites();
+
+ //--------------------------------------------------------------------
+ // member variables
+ //--------------------------------------------------------------------
+
+ BOOL mDirtyMesh;
+
+ LLFace *mShadow0Facep;
+ LLFace *mShadow1Facep;
+
+ LLFrameTimer mUpdateLODTimer; // controls frequency of LOD change calculations
+
+ //--------------------------------------------------------------------
+ // State of deferred character building
+ //--------------------------------------------------------------------
+ BOOL mIsBuilt;
+
+ //--------------------------------------------------------------------
+ // avatar definition name
+ //--------------------------------------------------------------------
+ char mAvatarDefinition[64];
+
+ //--------------------------------------------------------------------
+ // skeleton for skinned avatar
+ //--------------------------------------------------------------------
+ S32 mNumJoints;
+ LLViewerJoint *mSkeleton;
+
+ S32 mNumCollisionVolumes;
+ LLViewerJointCollisionVolume* mCollisionVolumes;
+
+ //--------------------------------------------------------------------
+ // cached pointers to well known joints
+ //--------------------------------------------------------------------
+ LLViewerJoint *mPelvisp;
+ LLViewerJoint *mTorsop;
+ LLViewerJoint *mChestp;
+ LLViewerJoint *mNeckp;
+ LLViewerJoint *mHeadp;
+ LLViewerJoint *mSkullp;
+ LLViewerJoint *mEyeLeftp;
+ LLViewerJoint *mEyeRightp;
+ LLViewerJoint *mHipLeftp;
+ LLViewerJoint *mHipRightp;
+ LLViewerJoint *mKneeLeftp;
+ LLViewerJoint *mKneeRightp;
+ LLViewerJoint *mAnkleLeftp;
+ LLViewerJoint *mAnkleRightp;
+ LLViewerJoint *mFootLeftp;
+ LLViewerJoint *mFootRightp;
+ LLViewerJoint *mWristLeftp;
+ LLViewerJoint *mWristRightp;
+
+ //--------------------------------------------------------------------
+ // special purpose joint for HUD attachments
+ //--------------------------------------------------------------------
+ LLViewerJoint *mScreenp;
+ F32 mHUDTargetZoom;
+ F32 mHUDCurZoom;
+
+ //--------------------------------------------------------------------
+ // mesh objects for skinned avatar
+ //--------------------------------------------------------------------
+ LLViewerJoint mHairLOD;
+ LLViewerJointMesh mHairMesh0;
+ LLViewerJointMesh mHairMesh1;
+ LLViewerJointMesh mHairMesh2;
+ LLViewerJointMesh mHairMesh3;
+ LLViewerJointMesh mHairMesh4;
+ LLViewerJointMesh mHairMesh5;
+
+ LLViewerJoint mHeadLOD;
+ LLViewerJointMesh mHeadMesh0;
+ LLViewerJointMesh mHeadMesh1;
+ LLViewerJointMesh mHeadMesh2;
+ LLViewerJointMesh mHeadMesh3;
+ LLViewerJointMesh mHeadMesh4;
+
+ LLViewerJoint mEyeLashLOD;
+ LLViewerJointMesh mEyeLashMesh0;
+
+ LLViewerJoint mUpperBodyLOD;
+ LLViewerJointMesh mUpperBodyMesh0;
+ LLViewerJointMesh mUpperBodyMesh1;
+ LLViewerJointMesh mUpperBodyMesh2;
+ LLViewerJointMesh mUpperBodyMesh3;
+ LLViewerJointMesh mUpperBodyMesh4;
+
+ LLViewerJoint mLowerBodyLOD;
+ LLViewerJointMesh mLowerBodyMesh0;
+ LLViewerJointMesh mLowerBodyMesh1;
+ LLViewerJointMesh mLowerBodyMesh2;
+ LLViewerJointMesh mLowerBodyMesh3;
+ LLViewerJointMesh mLowerBodyMesh4;
+
+ LLViewerJoint mEyeBallLeftLOD;
+ LLViewerJointMesh mEyeBallLeftMesh0;
+ LLViewerJointMesh mEyeBallLeftMesh1;
+
+ LLViewerJoint mEyeBallRightLOD;
+ LLViewerJointMesh mEyeBallRightMesh0;
+ LLViewerJointMesh mEyeBallRightMesh1;
+
+ LLViewerJoint mSkirtLOD;
+ LLViewerJointMesh mSkirtMesh0;
+ LLViewerJointMesh mSkirtMesh1;
+ LLViewerJointMesh mSkirtMesh2;
+ LLViewerJointMesh mSkirtMesh3;
+ LLViewerJointMesh mSkirtMesh4;
+
+ typedef std::multimap<LLString, LLPolyMesh*> mesh_map_t;
+ mesh_map_t mMeshes;
+
+ //--------------------------------------------------------------------
+ // true if this avatar is for this viewers agent
+ //--------------------------------------------------------------------
+ BOOL mIsSelf;
+
+ //--------------------------------------------------------------------
+ // texture ids and pointers
+ //--------------------------------------------------------------------
+ LLUUID mShadowImageID;
+ LLPointer<LLViewerImage> mShadowImagep;
+
+ LLUUID mLastHeadBakedID;
+ LLUUID mLastUpperBodyBakedID;
+ LLUUID mLastLowerBodyBakedID;
+ LLUUID mLastEyesBakedID;
+ LLUUID mLastSkirtBakedID;
+
+ //--------------------------------------------------------------------
+ // Misc Render State
+ //--------------------------------------------------------------------
+ BOOL mIsDummy; // For special views
+ S32 mSpecialRenderMode; // Special lighting
+
+ //--------------------------------------------------------------------
+ // Animation timer
+ //--------------------------------------------------------------------
+ LLTimer mAnimTimer;
+ F32 mTimeLast;
+
+ //--------------------------------------------------------------------
+ // Measures speed (for diagnostics mostly).
+ //--------------------------------------------------------------------
+ F32 mSpeedAccum;
+
+ //--------------------------------------------------------------------
+ // animation state data
+ //--------------------------------------------------------------------
+ typedef std::map<LLUUID, S32>::iterator AnimIterator;
+
+ std::map<LLUUID, S32> mSignaledAnimations; // requested state of Animation name/value
+ std::map<LLUUID, S32> mPlayingAnimations; // current state of Animation name/value
+
+ typedef std::multimap<LLUUID, LLUUID> AnimationSourceMap;
+ typedef AnimationSourceMap::iterator AnimSourceIterator;
+ AnimationSourceMap mAnimationSources; // object ids that triggered anim ids
+
+ BOOL mTurning; // controls hysteresis on avatar rotation
+
+ //--------------------------------------------------------------------
+ // misc. animation related state
+ //--------------------------------------------------------------------
+ F32 mSpeed;
+
+ //
+ // Shadow stuff
+ //
+ LLDrawable* mShadow;
+ BOOL mInAir;
+ LLFrameTimer mTimeInAir;
+
+ //--------------------------------------------------------------------
+ // Keeps track of foot step state for generating sounds
+ //--------------------------------------------------------------------
+ BOOL mWasOnGroundLeft;
+ BOOL mWasOnGroundRight;
+ LLVector4 mFootPlane;
+
+ //--------------------------------------------------------------------
+ // Keep track of the material being stepped on
+ //--------------------------------------------------------------------
+ BOOL mStepOnLand;
+ U8 mStepMaterial;
+ LLVector3 mStepObjectVelocity;
+
+ //--------------------------------------------------------------------
+ // Pelvis height adjustment members.
+ //--------------------------------------------------------------------
+ F32 mPelvisToFoot;
+ LLVector3 mBodySize;
+ S32 mLastSkeletonSerialNum;
+
+ //--------------------------------------------------------------------
+ // current head position
+ //--------------------------------------------------------------------
+ LLVector3 mHeadOffset;
+
+ //--------------------------------------------------------------------
+ // avatar skeleton
+ //--------------------------------------------------------------------
+ LLViewerJoint mRoot;
+
+ //--------------------------------------------------------------------
+ // sitting state
+ //--------------------------------------------------------------------
+ BOOL mIsSitting;
+
+ //--------------------------------------------------------------------
+ // Display the name, then optionally fade it out
+ //--------------------------------------------------------------------
+ LLFrameTimer mTimeVisible;
+ LLPointer<LLHUDText> mNameText;
+ std::deque<LLChat> mChats;
+ LLFrameTimer mChatTimer;
+ BOOL mTyping;
+ LLFrameTimer mTypingTimer;
+
+ //--------------------------------------------------------------------
+ // destroy mesh data after being invisible for a while
+ //--------------------------------------------------------------------
+ BOOL mMeshValid;
+ BOOL mVisible;
+ LLFrameTimer mMeshInvisibleTime;
+
+ //--------------------------------------------------------------------
+ // wind rippling in clothes
+ //--------------------------------------------------------------------
+ LLVector4 mWindVec;
+ F32 mWindFreq;
+ F32 mRipplePhase;
+ LLFrameTimer mRippleTimer;
+ F32 mRippleTimeLast;
+ LLVector3 mRippleAccel;
+ LLVector3 mLastVel;
+ BOOL mBelowWater;
+
+ //--------------------------------------------------------------------
+ // appearance morphing
+ //--------------------------------------------------------------------
+ LLFrameTimer mAppearanceMorphTimer;
+ BOOL mAppearanceAnimSetByUser;
+ F32 mLastAppearanceBlendTime;
+ BOOL mAppearanceAnimating;
+
+ //--------------------------------------------------------------------
+ // static members
+ //--------------------------------------------------------------------
+ static S32 sMaxVisible;
+ static S32 sCurJoint;
+ static S32 sCurVolume;
+ static BOOL sShowAnimationDebug; // show animation debug info
+ static BOOL sShowFootPlane; // show foot collision plane reported by server
+ static BOOL sShowCollisionVolumes; // show skeletal collision volumes
+ static BOOL sVisibleInFirstPerson;
+
+ static S32 sMaxOtherAvatarsToComposite;
+
+ static S32 sNumLODChangesThisFrame;
+
+ // global table of sound ids per material, and the ground
+ static LLUUID sStepSounds[LL_MCODE_END];
+ static LLUUID sStepSoundOnLand;
+
+ static S32 sRenderName;
+ static S32 sNumVisibleChatBubbles;
+ static BOOL sDebugInvisible;
+ static BOOL sShowAttachmentPoints;
+
+ // Number of instances of this class
+ static S32 sNumVisibleAvatars;
+
+ // Scratch textures used for compositing
+ static LLMap< LLGLenum, LLGLuint*> sScratchTexNames;
+ static LLMap< LLGLenum, F32*> sScratchTexLastBindTime;
+ static S32 sScratchTexBytes;
+
+ // map of attachment points, by ID
+ LLPtrSkipMap<S32, LLViewerJointAttachment*> mAttachmentPoints;
+
+ // xml parse tree of avatar config file
+ static LLXmlTree sXMLTree;
+ // xml parse tree of avatar skeleton file
+ static LLXmlTree sSkeletonXMLTree;
+
+ // number of avatar duplicates, for debugging purposes
+ static BOOL sAvatarLoadTest;
+
+ // user-settable LOD factor
+ static F32 sLODFactor;
+
+ // output total number of joints being touched for each avatar
+ static BOOL sJointDebug;
+ static ETextureIndex sBakedTextureIndices[BAKED_TEXTURE_COUNT];
+
+ //--------------------------------------------------------------------
+ // Texture Layer Sets and Global Colors
+ //--------------------------------------------------------------------
+ LLTexLayerSet* mHeadLayerSet;
+ LLTexLayerSet* mUpperBodyLayerSet;
+ LLTexLayerSet* mLowerBodyLayerSet;
+ LLTexLayerSet* mEyesLayerSet;
+ LLTexLayerSet* mSkirtLayerSet;
+
+
+protected:
+ LLPointer<LLHUDEffectSpiral> mBeam;
+ LLFrameTimer mBeamTimer;
+ LLFrameTimer mEditEffectTimer;
+
+ F32 mRenderPriority;
+ F32 mAdjustedPixelArea;
+ S32 mNumAGPVertices;
+
+ LLWString mNameString;
+ LLString mTitle;
+ BOOL mNameAway;
+ BOOL mNameBusy;
+ BOOL mNameMute;
+ BOOL mNameAppearance;
+ BOOL mVisibleChat;
+
+ LLString mDebugText;
+ U64 mLastRegionHandle;
+ LLFrameTimer mRegionCrossingTimer;
+ S32 mRegionCrossingCount;
+
+ //--------------------------------------------------------------------
+ // local textures for compositing.
+ //--------------------------------------------------------------------
+
+ LLPointer<LLViewerImage> mLocalTexture[ LOCTEX_NUM_ENTRIES ];
+ BOOL mLocalTextureBaked[ LOCTEX_NUM_ENTRIES ]; // Texture is covered by a baked texture
+ S32 mLocalTextureDiscard[ LOCTEX_NUM_ENTRIES ];
+ LLUUID mSavedTE[ TEX_NUM_ENTRIES ];
+ BOOL mFirstTEMessageReceived;
+ BOOL mFirstAppearanceMessageReceived;
+
+ BOOL mHeadBakedLoaded;
+ S32 mHeadMaskDiscard;
+ BOOL mUpperBakedLoaded;
+ S32 mUpperMaskDiscard;
+ BOOL mLowerBakedLoaded;
+ S32 mLowerMaskDiscard;
+ BOOL mEyesBakedLoaded;
+ BOOL mSkirtBakedLoaded;
+
+ //RN: testing 2 pass rendering
+ U32 mHeadMaskTexName;
+ U32 mUpperMaskTexName;
+ U32 mLowerMaskTexName;
+
+ BOOL mCulled;
+
+ //--------------------------------------------------------------------
+ // Global Colors
+ //--------------------------------------------------------------------
+ LLTexGlobalColor* mTexSkinColor;
+ LLTexGlobalColor* mTexHairColor;
+ LLTexGlobalColor* mTexEyeColor;
+
+ static LLVOAvatarSkeletonInfo* sSkeletonInfo;
+ static LLVOAvatarInfo* sAvatarInfo;
+
+protected:
+
+ BOOL loadSkeletonNode();
+ BOOL loadMeshNodes();
+
+ BOOL isFullyBaked();
+ void deleteLayerSetCaches();
+ static BOOL areAllNearbyInstancesBaked();
+
+ static void onBakedTextureMasksLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+
+ void setLocalTexture(ELocTexIndex i, LLViewerImage* tex, BOOL baked_version_exits);
+
+ void requestLayerSetUpdate(LLVOAvatar::ELocTexIndex i);
+ void addLocalTextureStats(LLVOAvatar::ELocTexIndex i, LLViewerImage* imagep, F32 texel_area_ratio, BOOL rendered, BOOL covered_by_baked);
+ static void onInitialBakedTextureLoaded( BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata );
+ static void onBakedTextureLoaded(BOOL success, LLViewerImage *src_vi, LLImageRaw* src, LLImageRaw* aux_src, S32 discard_level, BOOL final, void* userdata);
+ void useBakedTexture(const LLUUID& id);
+ void dumpAvatarTEs(const char* context);
+ void removeMissingBakedTextures();
+ LLTexLayerSet* getLayerSet(ETextureIndex index) const;
+ LLHost getObjectHost() const;
+ S32 getLocalDiscardLevel( S32 index);
+};
+
+#endif // LL_VO_AVATAR_H
diff --git a/indra/newview/llvocache.cpp b/indra/newview/llvocache.cpp
new file mode 100644
index 0000000000..611f5ddab0
--- /dev/null
+++ b/indra/newview/llvocache.cpp
@@ -0,0 +1,134 @@
+/**
+ * @file llvocache.cpp
+ * @brief Cache of objects on the viewer.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvocache.h"
+
+#include "llerror.h"
+
+//---------------------------------------------------------------------------
+// LLVOCacheEntry
+//---------------------------------------------------------------------------
+
+LLVOCacheEntry::LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ mLocalID = local_id;
+ mCRC = crc;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+}
+
+LLVOCacheEntry::LLVOCacheEntry()
+{
+ mLocalID = 0;
+ mCRC = 0;
+ mHitCount = 0;
+ mDupeCount = 0;
+ mCRCChangeCount = 0;
+ mBuffer = NULL;
+ mDP.assignBuffer(mBuffer, 0);
+}
+
+
+LLVOCacheEntry::LLVOCacheEntry(FILE *fp)
+{
+ S32 size;
+ fread(&mLocalID, 1, sizeof(U32), fp);
+ fread(&mCRC, 1, sizeof(U32), fp);
+ fread(&mHitCount, 1, sizeof(S32), fp);
+ fread(&mDupeCount, 1, sizeof(S32), fp);
+ fread(&mCRCChangeCount, 1, sizeof(S32), fp);
+
+ fread(&size, 1, sizeof(S32), fp);
+
+ // Corruption in the cache entries
+ if ((size > 10000) || (size < 1))
+ {
+ // We've got a bogus size, skip reading it.
+ // We won't bother seeking, because the rest of this file
+ // is likely bogus, and will be tossed anyway.
+ llwarns << "Bogus cache entry, size " << size << ", aborting!" << llendl;
+ mLocalID = 0;
+ mCRC = 0;
+ mBuffer = NULL;
+ return;
+ }
+
+ mBuffer = new U8[size];
+ fread(mBuffer, 1, size, fp);
+ mDP.assignBuffer(mBuffer, size);
+}
+
+LLVOCacheEntry::~LLVOCacheEntry()
+{
+ delete [] mBuffer;
+}
+
+
+// New CRC means the object has changed.
+void LLVOCacheEntry::assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ mCRC = crc;
+ mHitCount = 0;
+ mCRCChangeCount++;
+
+ mDP.freeBuffer();
+ mBuffer = new U8[dp.getBufferSize()];
+ mDP.assignBuffer(mBuffer, dp.getBufferSize());
+ mDP = dp;
+ }
+}
+
+LLDataPackerBinaryBuffer *LLVOCacheEntry::getDP(U32 crc)
+{
+ if ( (mCRC != crc)
+ ||(mDP.getBufferSize() == 0))
+ {
+ //llinfos << "Not getting cache entry, invalid!" << llendl;
+ return NULL;
+ }
+ mHitCount++;
+ return &mDP;
+}
+
+
+void LLVOCacheEntry::recordHit()
+{
+ mHitCount++;
+}
+
+
+void LLVOCacheEntry::dump() const
+{
+ llinfos << "local " << mLocalID
+ << " crc " << mCRC
+ << " hits " << mHitCount
+ << " dupes " << mDupeCount
+ << " change " << mCRCChangeCount
+ << llendl;
+}
+
+void LLVOCacheEntry::writeToFile(FILE *fp) const
+{
+ fwrite(&mLocalID, 1, sizeof(U32), fp);
+ fwrite(&mCRC, 1, sizeof(U32), fp);
+ fwrite(&mHitCount, 1, sizeof(S32), fp);
+ fwrite(&mDupeCount, 1, sizeof(S32), fp);
+ fwrite(&mCRCChangeCount, 1, sizeof(S32), fp);
+ S32 size = mDP.getBufferSize();
+ fwrite(&size, 1, sizeof(S32), fp);
+ fwrite(mBuffer, 1, size, fp);
+}
diff --git a/indra/newview/llvocache.h b/indra/newview/llvocache.h
new file mode 100644
index 0000000000..7dc06a0b49
--- /dev/null
+++ b/indra/newview/llvocache.h
@@ -0,0 +1,51 @@
+/**
+ * @file llvocache.h
+ * @brief Cache of objects on the viewer.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOCACHE_H
+#define LL_LLVOCACHE_H
+
+#include "lluuid.h"
+#include "lldatapacker.h"
+#include "lldlinked.h"
+
+
+//---------------------------------------------------------------------------
+// Cache entries
+class LLVOCacheEntry;
+
+class LLVOCacheEntry : public LLDLinked<LLVOCacheEntry>
+{
+public:
+ LLVOCacheEntry(U32 local_id, U32 crc, LLDataPackerBinaryBuffer &dp);
+ LLVOCacheEntry(FILE *fp);
+ LLVOCacheEntry();
+ ~LLVOCacheEntry();
+
+ U32 getLocalID() const { return mLocalID; }
+ U32 getCRC() const { return mCRC; }
+ S32 getHitCount() const { return mHitCount; }
+ S32 getCRCChangeCount() const { return mCRCChangeCount; }
+
+ void dump() const;
+ void writeToFile(FILE *fp) const;
+ void assignCRC(U32 crc, LLDataPackerBinaryBuffer &dp);
+ LLDataPackerBinaryBuffer *getDP(U32 crc);
+ void recordHit();
+ void recordDupe() { mDupeCount++; }
+
+protected:
+ U32 mLocalID;
+ U32 mCRC;
+ S32 mHitCount;
+ S32 mDupeCount;
+ S32 mCRCChangeCount;
+ LLDataPackerBinaryBuffer mDP;
+ U8 *mBuffer;
+};
+
+#endif
diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp
new file mode 100644
index 0000000000..b4924a45f1
--- /dev/null
+++ b/indra/newview/llvoclouds.cpp
@@ -0,0 +1,188 @@
+/**
+ * @file llvoclouds.cpp
+ * @brief Implementation of LLVOClouds class which is a derivation fo LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoclouds.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h" // to get camera position
+#include "lldrawable.h"
+#include "llface.h"
+#include "llprimitive.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvosky.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+LLVOClouds::LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_CLOUDS, regionp)
+{
+ mCloudGroupp = NULL;
+ mbCanSelect = FALSE;
+ setNumTEs(1);
+ LLViewerImage* image = gImageList.getImage(gCloudTextureID);
+ image->setBoostLevel(LLViewerImage::BOOST_CLOUDS);
+ setTEImage(0, image);
+}
+
+
+LLVOClouds::~LLVOClouds()
+{
+}
+
+
+BOOL LLVOClouds::isActive() const
+{
+ return TRUE;
+}
+
+
+BOOL LLVOClouds::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ return TRUE;
+
+ // Set dirty flag (so renderer will rebuild primitive)
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOClouds::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 1500*100;
+}
+
+void LLVOClouds::updateTextures(LLAgent &agent)
+{
+ getTEImage(0)->addTextureStats(mPixelArea);
+}
+
+LLDrawable* LLVOClouds::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_CLOUDS);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_CLOUDS);
+
+ mDrawable->setNumFaces(1, pool, getTEImage(0));
+
+ return mDrawable;
+}
+
+BOOL LLVOClouds::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS)))
+ return TRUE;
+
+ LLVector3 at;
+ LLVector3 up;
+ LLVector3 right;
+ LLVector3 normal(0.f,0.f,-1.f);
+ LLVector3 position_agent;
+ //LLVector3 v[4];
+ LLFace *facep;
+ const LLVector3 region_pos_agent = mRegionp->getOriginAgent();
+ const LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector3 center_offset = getPositionRegion();
+ LLVector2 uvs[4];
+
+ uvs[0].setVec(0.f, 1.f);
+ uvs[1].setVec(0.f, 0.f);
+ uvs[2].setVec(1.f, 1.f);
+ uvs[3].setVec(1.f, 0.f);
+
+ LLVector3 vtx[4];
+
+ S32 num_faces = mCloudGroupp->getNumPuffs();
+
+ drawable->setNumFacesFast(num_faces, gPipeline.getPool(LLDrawPool::POOL_CLOUDS), getTEImage(0));
+
+ S32 face_indx = 0;
+ for ( ; face_indx < num_faces; face_indx++)
+ {
+ facep = drawable->getFace(face_indx);
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ facep->setPrimType(LLTriangles);
+ facep->setSize(4, 6);
+ index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ const LLCloudPuff &puff = mCloudGroupp->getPuff(face_indx);
+ const LLVector3 puff_pos_agent = gAgent.getPosAgentFromGlobal(puff.getPositionGlobal());
+ facep->mCenterAgent = puff_pos_agent;
+
+ LLVector3 from_camera_vec = gCamera->getAtAxis();//puff_pos_agent - camera_agent;
+ at = from_camera_vec;
+ right = at % LLVector3(0.f, 0.f, 1.f);
+ right.normVec();
+ up = right % at;
+ up.normVec();
+ right *= 0.5f*CLOUD_PUFF_WIDTH;
+ up *= 0.5f*CLOUD_PUFF_HEIGHT;;
+
+ facep->mCenterAgent = puff_pos_agent;
+
+ LLColor4 color(1.f, 1.f, 1.f, puff.getAlpha());
+ facep->setFaceColor(color);
+
+ vtx[0] = puff_pos_agent - right + up;
+ vtx[1] = puff_pos_agent - right - up;
+ vtx[2] = puff_pos_agent + right + up;
+ vtx[3] = puff_pos_agent + right - up;
+
+ *verticesp++ = vtx[0];
+ *verticesp++ = vtx[1];
+ *verticesp++ = vtx[2];
+ *verticesp++ = vtx[3];
+
+ *texCoordsp++ = uvs[0];
+ *texCoordsp++ = uvs[1];
+ *texCoordsp++ = uvs[2];
+ *texCoordsp++ = uvs[3];
+
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+ }
+ for ( ; face_indx < drawable->getNumFaces(); face_indx++)
+ {
+ drawable->getFace(face_indx)->setSize(0,0);
+ }
+
+ return TRUE;
+}
diff --git a/indra/newview/llvoclouds.h b/indra/newview/llvoclouds.h
new file mode 100644
index 0000000000..0debe20730
--- /dev/null
+++ b/indra/newview/llvoclouds.h
@@ -0,0 +1,48 @@
+/**
+ * @file llvoclouds.h
+ * @brief Description of LLVOClouds class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOCLOUDS_H
+#define LL_LLVOCLOUDS_H
+
+#include "llviewerobject.h"
+#include "lltable.h"
+#include "v4coloru.h"
+
+class LLViewerImage;
+class LLViewerCloudGroup;
+
+class LLCloudGroup;
+
+
+class LLVOClouds : public LLViewerObject
+{
+public:
+ LLVOClouds(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp );
+ virtual ~LLVOClouds();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ void setCloudGroup(LLCloudGroup *cgp) { mCloudGroupp = cgp; }
+protected:
+ LLCloudGroup *mCloudGroupp;
+};
+
+extern LLUUID gCloudTextureID;
+
+#endif // LL_VO_CLOUDS_H
diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp
new file mode 100644
index 0000000000..a1bfe31f1e
--- /dev/null
+++ b/indra/newview/llvograss.cpp
@@ -0,0 +1,499 @@
+/**
+ * @file llvograss.cpp
+ * @brief Not a blade, but a clump of grass
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvograss.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llvosky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+#include "llworld.h"
+#include "lldir.h"
+#include "llxmltree.h"
+
+const S32 GRASS_MAX_BLADES = 32;
+const F32 GRASS_BLADE_BASE = 0.25f; // Width of grass at base
+const F32 GRASS_BLADE_TOP = 0.25f; // Width of grass at top
+const F32 GRASS_BLADE_HEIGHT = 0.5f; // meters
+const F32 GRASS_DISTRIBUTION_SD = 0.15f; // empirically defined
+
+F32 exp_x[GRASS_MAX_BLADES];
+F32 exp_y[GRASS_MAX_BLADES];
+F32 rot_x[GRASS_MAX_BLADES];
+F32 rot_y[GRASS_MAX_BLADES];
+F32 dz_x [GRASS_MAX_BLADES];
+F32 dz_y [GRASS_MAX_BLADES];
+
+F32 w_mod[GRASS_MAX_BLADES]; // Factor to modulate wind movement by to randomize appearance
+
+LLVOGrass::SpeciesMap LLVOGrass::sSpeciesTable;
+S32 LLVOGrass::sMaxGrassSpecies = 0;
+
+
+LLVOGrass::LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ mPatch = NULL;
+ mLastPatchUpdateTime = 0;
+ mGrassVel.clearVec();
+ mGrassBend.clearVec();
+ mbCanSelect = TRUE;
+
+ mBladeWindAngle = 35.f;
+ mBWAOverlap = 2.f;
+
+ setNumTEs(1);
+
+ setTEColor(0, LLColor4(1.0f, 1.0f, 1.0f, 1.f));
+ mNumBlades = GRASS_MAX_BLADES;
+}
+
+LLVOGrass::~LLVOGrass()
+{
+}
+
+
+void LLVOGrass::updateSpecies()
+{
+ mSpecies = mState;
+
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ llinfos << "Unknown grass type, substituting grass type." << llendl;
+ SpeciesMap::const_iterator it = sSpeciesTable.begin();
+ mSpecies = (*it).first;
+ }
+ setTEImage(0, gImageList.getImage(sSpeciesTable[mSpecies]->mTextureID));
+}
+
+
+void alert_done(S32 option, void* user_data)
+{
+ return;
+}
+
+
+void LLVOGrass::initClass()
+{
+ LLVector3 pos(0.0f, 0.0f, 0.0f);
+ // Create nifty list of exponential distribution 0-1
+ F32 x = 0.f;
+ F32 y = 0.f;
+ F32 rot;
+
+ std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"grass.xml");
+
+ LLXmlTree grass_def_grass;
+
+ if (!grass_def_grass.parseFile(xml_filename))
+ {
+ llerrs << "Failed to parse grass file." << llendl;
+ return;
+ }
+
+ LLXmlTreeNode* rootp = grass_def_grass.getRoot();
+
+ for (LLXmlTreeNode* grass_def = rootp->getFirstChild();
+ grass_def;
+ grass_def = rootp->getNextChild())
+ {
+ if (!grass_def->hasName("grass"))
+ {
+ llwarns << "Invalid grass definition node " << grass_def->getName() << llendl;
+ continue;
+ }
+ F32 F32_val;
+ LLUUID id;
+
+ BOOL success = TRUE;
+
+ S32 species;
+ static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
+ if (!grass_def->getFastAttributeS32(species_id_string, species))
+ {
+ llwarns << "No species id defined" << llendl;
+ continue;
+ }
+
+ if (species < 0)
+ {
+ llwarns << "Invalid species id " << species << llendl;
+ continue;
+ }
+
+ GrassSpeciesData* newGrass = new GrassSpeciesData();
+
+
+ static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
+ grass_def->getFastAttributeUUID(texture_id_string, id);
+ newGrass->mTextureID = id;
+
+ if (newGrass->mTextureID.isNull())
+ {
+ LLString textureName;
+
+ static LLStdStringHandle texture_name_string = LLXmlTree::addAttributeString("texture_name");
+ success &= grass_def->getFastAttributeString(texture_name_string, textureName);
+ newGrass->mTextureID.set( gViewerArt.getString(textureName) );
+ }
+
+ static LLStdStringHandle blade_sizex_string = LLXmlTree::addAttributeString("blade_size_x");
+ success &= grass_def->getFastAttributeF32(blade_sizex_string, F32_val);
+ newGrass->mBladeSizeX = F32_val;
+
+ static LLStdStringHandle blade_sizey_string = LLXmlTree::addAttributeString("blade_size_y");
+ success &= grass_def->getFastAttributeF32(blade_sizey_string, F32_val);
+ newGrass->mBladeSizeY = F32_val;
+
+ if (sSpeciesTable.count(species))
+ {
+ llinfos << "Grass species " << species << " already defined! Duplicate discarded." << llendl;
+ delete newGrass;
+ continue;
+ }
+ else
+ {
+ sSpeciesTable[species] = newGrass;
+ }
+
+ if (species >= sMaxGrassSpecies) sMaxGrassSpecies = species + 1;
+
+ if (!success)
+ {
+ LLString name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ grass_def->getFastAttributeString(name_string, name);
+ llwarns << "Incomplete definition of grass " << name << llendl;
+ }
+ }
+
+ BOOL have_all_grass = TRUE;
+ LLString err;
+ char buffer[10];
+
+ for (S32 i=0;i<sMaxGrassSpecies;++i)
+ {
+ if (!sSpeciesTable.count(i))
+ {
+ snprintf(buffer,10," %d",i);
+ err.append(buffer);
+ have_all_grass = FALSE;
+ }
+ }
+
+ if (!have_all_grass)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[SPECIES]"] = err;
+ gViewerWindow->alertXml("ErrorUndefinedGrasses", args, alert_done );
+ }
+
+ for (S32 i = 0; i < GRASS_MAX_BLADES; ++i)
+ {
+ if (1) //(i%2 == 0) Uncomment for X blading
+ {
+ F32 u = sqrt(-2.0f * log(frand(1.0)));
+ F32 v = 2.0f * F_PI * frand(1.0);
+
+ x = u * sin(v) * GRASS_DISTRIBUTION_SD;
+ y = u * cos(v) * GRASS_DISTRIBUTION_SD;
+
+ rot = frand(F_PI);
+ }
+ else
+ {
+ rot += (F_PI*0.4f + frand(0.2f*F_PI));
+ }
+
+ exp_x[i] = x;
+ exp_y[i] = y;
+ rot_x[i] = sin(rot);
+ rot_y[i] = cos(rot);
+ dz_x[i] = frand(GRASS_BLADE_BASE * 0.25f);
+ dz_y[i] = frand(GRASS_BLADE_BASE * 0.25f);
+ w_mod[i] = 0.5f + frand(1.f); // Degree to which blade is moved by wind
+
+ }
+}
+
+void LLVOGrass::cleanupClass()
+{
+ for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
+}
+
+U32 LLVOGrass::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ updateSpecies();
+
+ if ( (getVelocity().magVecSquared() > 0.f)
+ ||(getAcceleration().magVecSquared() > 0.f)
+ ||(getAngularVelocity().magVecSquared() > 0.f))
+ {
+ llinfos << "ACK! Moving grass!" << llendl;
+ setVelocity(LLVector3::zero);
+ setAcceleration(LLVector3::zero);
+ setAngularVelocity(LLVector3::zero);
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return retval;
+}
+
+BOOL LLVOGrass::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOGrass::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GRASS)))
+ {
+ return TRUE;
+ }
+
+ if (!mDrawable)
+ {
+ // So drones work.
+ return TRUE;
+ }
+
+ if (mPatch && (mLastPatchUpdateTime != mPatch->getLastUpdateTime()))
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ return TRUE;
+}
+
+
+void LLVOGrass::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // This should be the camera's center, as soon as we move to all region-local.
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 range = relative_position.magVec(); // ugh, square root
+
+ F32 max_scale = getMaxScale();
+
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+
+ // Compute pixels per meter at the given range
+ F32 pixels_per_meter = gCamera->getViewHeightInPixels() /
+ (tan(gCamera->getView()) * range);
+
+ // Assume grass texture is a 1 meter by 1 meter sprite at the grass object's center
+ mPixelArea = (pixels_per_meter) * (pixels_per_meter);
+}
+
+
+// BUG could speed this up by caching the relative_position and range calculations
+void LLVOGrass::updateTextures(LLAgent &agent)
+{
+ // dot_product = A B cos T
+ // BUT at_axis is unit, so dot_product = B cos T
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 dot_product = relative_position * agent.getFrameAgent().getAtAxis();
+ F32 cos_angle = dot_product / relative_position.magVec();
+
+ if (getTEImage(0))
+ {
+ getTEImage(0)->addTextureStats(mPixelArea*20.f, 1.f, cos_angle);
+ }
+}
+
+BOOL LLVOGrass::updateLOD()
+{
+ F32 tan_angle = 0.f;
+ S32 num_blades = 0;
+
+ tan_angle = (mScale.mV[0]*mScale.mV[1])/mDrawable->mDistanceWRTCamera;
+ num_blades = llmin(GRASS_MAX_BLADES, lltrunc(tan_angle * 5));
+ num_blades = llmax(1, num_blades);
+ if (num_blades >= (mNumBlades << 1))
+ {
+ while (mNumBlades < num_blades)
+ {
+ mNumBlades <<= 1;
+ }
+
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ else if (num_blades <= (mNumBlades >> 1))
+ {
+ while (mNumBlades > num_blades)
+ {
+ mNumBlades >>=1;
+ }
+
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+LLDrawable* LLVOGrass::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+// mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_GRASS);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+
+ mDrawable->setNumFaces(1, pool, getTEImage(0));
+
+ return mDrawable;
+}
+
+BOOL LLVOGrass::updateGeometry(LLDrawable *drawable)
+{
+ plantBlades();
+ return TRUE;
+}
+
+void LLVOGrass::plantBlades()
+{
+ mPatch = mRegionp->getLand().resolvePatchRegion(getPositionRegion());
+ mLastPatchUpdateTime = mPatch->getLastUpdateTime();
+
+ LLVector3 position;
+ // Create random blades of grass with gaussian distribution
+ F32 x,y,xf,yf,dzx,dzy;
+
+ LLVector3 normal(0,0,1);
+ LLColor4U color(0,0,0,1);
+
+ LLFace *face = mDrawable->getFace(0);
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ LLStrider<LLColor4U> colorsp;
+
+ U32 *indicesp;
+
+ face->setPool(face->getPool(), getTEImage(0));
+ face->setState(LLFace::GLOBAL);
+ face->setSize(mNumBlades * 4, mNumBlades * 12);
+ face->setPrimType(LLTriangles);
+ S32 index_offset = face->getGeometryColors(verticesp,normalsp,texCoordsp,colorsp,indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ // It is possible that the species of a grass is not defined
+ // This is bad, but not the end of the world.
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ llinfos << "Unknown grass species " << mSpecies << llendl;
+ return;
+ }
+
+ F32 width = sSpeciesTable[mSpecies]->mBladeSizeX;
+ F32 height = sSpeciesTable[mSpecies]->mBladeSizeY;
+
+ for (S32 i = 0; i < mNumBlades; i++)
+ {
+ x = exp_x[i] * mScale.mV[VX];
+ y = exp_y[i] * mScale.mV[VY];
+ xf = rot_x[i] * GRASS_BLADE_BASE * width * w_mod[i];
+ yf = rot_y[i] * GRASS_BLADE_BASE * width * w_mod[i];
+ dzx = dz_x [i];
+ dzy = dz_y [i];
+
+ F32 blade_height= GRASS_BLADE_HEIGHT * height * w_mod[i];
+
+ *texCoordsp++ = LLVector2(0, 0);
+ *texCoordsp++ = LLVector2(0, 0.98f);
+ *texCoordsp++ = LLVector2(1, 0);
+ *texCoordsp++ = LLVector2(1, 0.98f);
+
+ position.mV[0] = mPosition.mV[VX] + x + xf;
+ position.mV[1] = mPosition.mV[VY] + y + yf;
+ position.mV[2] = 0.f;
+ position.mV[2] = mRegionp->getLand().resolveHeightRegion(position);
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] += dzx;
+ position.mV[1] += dzy;
+ position.mV[2] += blade_height;
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] = mPosition.mV[VX] + x - xf;
+ position.mV[1] = mPosition.mV[VY] + y - xf;
+ position.mV[2] = 0.f;
+ position.mV[2] = mRegionp->getLand().resolveHeightRegion(position);
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ position.mV[0] += dzx;
+ position.mV[1] += dzy;
+ position.mV[2] += blade_height;
+ *verticesp++ = position + mRegionp->getOriginAgent();
+
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+ *(colorsp++) = color;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+ index_offset += 4;
+ }
+
+ LLPipeline::sCompiles++;
+
+ face->mCenterLocal = mPosition;
+
+}
+
+
+
+
+
diff --git a/indra/newview/llvograss.h b/indra/newview/llvograss.h
new file mode 100644
index 0000000000..ef3d30c5ea
--- /dev/null
+++ b/indra/newview/llvograss.h
@@ -0,0 +1,84 @@
+/**
+ * @file llvograss.h
+ * @brief Description of LLVOGrass class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOGRASS_H
+#define LL_LLVOGRASS_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include <map>
+
+class LLSurfacePatch;
+class LLViewerImage;
+
+
+class LLVOGrass : public LLViewerObject
+{
+public:
+ LLVOGrass(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOGrass();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num,
+ const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ static void import(FILE *file, LLMessageSystem *mesgsys, const LLVector3 &pos);
+ /*virtual*/ void exportFile(FILE *file, const LLVector3 &position);
+
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ BOOL updateLOD();
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ void plantBlades();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ static S32 sMaxGrassSpecies;
+
+ struct GrassSpeciesData
+ {
+ LLUUID mTextureID;
+
+ F32 mBladeSizeX;
+ F32 mBladeSizeY;
+ };
+
+ typedef std::map<U32, GrassSpeciesData*> SpeciesMap;
+
+ U8 mSpecies; // Species of grass
+ F32 mBladeSizeX;
+ F32 mBladeSizeY;
+
+ LLSurfacePatch *mPatch; // Stores the land patch where the grass is centered
+
+ U64 mLastPatchUpdateTime;
+
+ LLVector3 mGrassBend; // Accumulated wind (used for blowing trees)
+ LLVector3 mGrassVel;
+ LLVector3 mWind;
+ F32 mBladeWindAngle;
+ F32 mBWAOverlap;
+
+private:
+ void updateSpecies();
+ F32 mLastHeight; // For cheap update hack
+ S32 mNumBlades;
+
+ static SpeciesMap sSpeciesTable;
+};
+#endif // LL_VO_GRASS_
diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp
new file mode 100644
index 0000000000..dd3ef61173
--- /dev/null
+++ b/indra/newview/llvoground.cpp
@@ -0,0 +1,165 @@
+/**
+ * @file llvoground.cpp
+ * @brief LLVOGround class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoground.h"
+
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+
+LLVOGround::LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ mbCanSelect = FALSE;
+}
+
+
+LLVOGround::~LLVOGround()
+{
+}
+
+BOOL LLVOGround::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_GROUND)))
+ {
+ return TRUE;
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOGround::updateTextures(LLAgent &agent)
+{
+}
+
+
+LLDrawable *LLVOGround::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_GROUND);
+
+ mDrawable->addFace(poolp, NULL);
+
+ return mDrawable;
+}
+
+BOOL LLVOGround::updateGeometry(LLDrawable *drawable)
+{
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ if (drawable->getNumFaces() < 1)
+ drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_GROUND), NULL);
+ face = drawable->getFace(0);
+ face->setPrimType(LLTriangles);
+ face->setSize(6, 12);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ ///////////////////////////////////////
+ //
+ //
+ //
+ LLVector3 at_dir = gCamera->getAtAxis();
+ at_dir.mV[VZ] = 0.f;
+ if (at_dir.normVec() < 0.01)
+ {
+ // We really don't care, as we're not looking anywhere near the horizon.
+ }
+ LLVector3 left_dir = gCamera->getLeftAxis();
+ left_dir.mV[VZ] = 0.f;
+ left_dir.normVec();
+
+ // Our center top point
+ LLVector3 center_top, center_bottom;
+
+ LLColor4 ground_color = gSky.getFogColor();
+ ground_color.mV[3] = 1.f;
+ face->setFaceColor(ground_color);
+
+ if (gCamera->getOrigin().mV[VZ] < gAgent.getRegion()->getWaterHeight())
+ {
+ // Underwater
+ //center_top = gCamera->getOrigin() + at_dir*gCamera->getFar();
+ center_top = gCamera->getOrigin() - LLVector3(0, 0, 5);
+ center_bottom = gCamera->getOrigin() + at_dir*gCamera->getFar();;
+ //center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight() + 0.5f;
+ center_bottom.mV[VZ] = -100.f;
+ }
+ else
+ {
+ // Above water
+ center_top = gCamera->getOrigin() - LLVector3(0, 0, 30);
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > 0))
+ {
+ center_top.mV[VZ] = gAgent.getRegion()->getWaterHeight();
+ }
+ //center_top = gCamera->getOrigin() + at_dir*9000.f;
+ center_bottom = gCamera->getOrigin() - at_dir*gCamera->getFar();
+ //center_top.mV[VZ] = 0.f;
+ //center_bottom.mV[VZ] = gAgent.getRegion()->getWaterHeight();
+ }
+
+ *(verticesp++) = center_top + LLVector3(6000, 6000, 0);
+ *(verticesp++) = center_top + LLVector3(-6000, 6000, 0);
+ *(verticesp++) = center_top + LLVector3(-6000, -6000, 0);
+
+ *(verticesp++) = center_top + LLVector3(-6000, -6000, 0);
+ *(verticesp++) = center_top + LLVector3(6000, -6000, 0);
+ *(verticesp++) = center_top + LLVector3(6000, 6000, 0);
+
+
+ // Triangles for each side
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 5;
+
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 5;
+ *indicesp++ = index_offset + 4;
+
+ *(texCoordsp++) = LLVector2(0.f, 0.f);
+ *(texCoordsp++) = LLVector2(1.f, 0.f);
+ *(texCoordsp++) = LLVector2(0.f, 1.f);
+ *(texCoordsp++) = LLVector2(1.f, 1.f);
+ *(texCoordsp++) = LLVector2(0.f, 2.f);
+ *(texCoordsp++) = LLVector2(1.f, 2.f);
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
diff --git a/indra/newview/llvoground.h b/indra/newview/llvoground.h
new file mode 100644
index 0000000000..228d872eaf
--- /dev/null
+++ b/indra/newview/llvoground.h
@@ -0,0 +1,36 @@
+/**
+ * @file llvoground.h
+ * @brief LLVOGround class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOGROUND_H
+#define LL_LLVOGROUND_H
+
+#include "stdtypes.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "llviewerimage.h"
+#include "llviewerobject.h"
+
+class LLVOGround : public LLViewerObject
+{
+protected:
+public:
+ LLVOGround(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOGround();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Graphical stuff for objects - maybe broken out into render class
+ // later?
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void cleanupGL();
+};
+
+#endif // LL_LLVOGROUND_H
diff --git a/indra/newview/llvoinventorylistener.cpp b/indra/newview/llvoinventorylistener.cpp
new file mode 100644
index 0000000000..a0b623db71
--- /dev/null
+++ b/indra/newview/llvoinventorylistener.cpp
@@ -0,0 +1,45 @@
+/**
+ * @file llvoinventorylistener.cpp
+ * @brief Interface for classes that wish to receive updates about viewer object inventory
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvoinventorylistener.h"
+#include "llviewerobject.h"
+
+void LLVOInventoryListener::removeVOInventoryListener()
+{
+ if (mListenerVObject != NULL)
+ {
+ mListenerVObject->removeInventoryListener(this);
+ mListenerVObject = NULL;
+ }
+}
+
+void LLVOInventoryListener::registerVOInventoryListener(LLViewerObject* object, void* user_data)
+{
+ removeVOInventoryListener();
+ if (object != NULL)
+ {
+ mListenerVObject = object;
+ object->registerInventoryListener(this,user_data);
+ }
+}
+
+void LLVOInventoryListener::requestVOInventory()
+{
+ if (mListenerVObject != NULL)
+ {
+ mListenerVObject->requestInventory();
+ }
+}
+
+// This assumes mListenerVObject is clearing it's own lists
+void LLVOInventoryListener::clearVOInventoryListener()
+{
+ mListenerVObject = NULL;
+}
diff --git a/indra/newview/llvoinventorylistener.h b/indra/newview/llvoinventorylistener.h
new file mode 100644
index 0000000000..1cca42fc93
--- /dev/null
+++ b/indra/newview/llvoinventorylistener.h
@@ -0,0 +1,43 @@
+/**
+ * @file llvoinventorylistener.h
+ * @brief Interface for classes that wish to receive updates about viewer object inventory
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Description of LLVOInventoryListener class, which is an interface
+// for windows that are interested in updates to a ViewerObject's inventory.
+
+#ifndef LL_LLVOINVENTORYLISTENER_H
+#define LL_LLVOINVENTORYLISTENER_H
+
+#include "llviewerobject.h"
+
+class LLVOInventoryListener
+{
+public:
+ virtual void inventoryChanged(LLViewerObject* object,
+ InventoryObjectList* inventory,
+ S32 serial_num,
+ void* user_data) = 0;
+
+ // Remove the listener from the object and clear this listener
+ void removeVOInventoryListener();
+
+ // Just clear this listener, don't worry about the object.
+ void clearVOInventoryListener();
+
+protected:
+ LLVOInventoryListener() : mListenerVObject(NULL) { }
+ virtual ~LLVOInventoryListener() { removeVOInventoryListener(); }
+
+ void registerVOInventoryListener(LLViewerObject* object, void* user_data);
+ void requestVOInventory();
+
+private:
+ LLViewerObject* mListenerVObject;
+};
+
+#endif
+
diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp
new file mode 100644
index 0000000000..caba9e3aef
--- /dev/null
+++ b/indra/newview/llvopartgroup.cpp
@@ -0,0 +1,266 @@
+/**
+ * @file llvopartgroup.cpp
+ * @brief Group of particle systems
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvopartgroup.h"
+
+#include "llfasttimer.h"
+#include "message.h"
+#include "v2math.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerpartsim.h"
+#include "llviewerregion.h"
+#include "pipeline.h"
+
+const F32 MAX_PART_LIFETIME = 120.f;
+
+extern U64 gFrameTime;
+
+LLVOPartGroup::LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mViewerPartGroupp(NULL)
+{
+ setNumTEs(1);
+ setTETexture(0, LLUUID::null);
+ mbCanSelect = FALSE; // users can't select particle systems
+ mDebugColor = LLColor4(frand(1.f), frand(1.f), frand(1.f), 1.f);
+}
+
+
+LLVOPartGroup::~LLVOPartGroup()
+{
+}
+
+BOOL LLVOPartGroup::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOPartGroup::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES))
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+ // Particle groups don't need any of default idleUpdate velocity interpolation stuff.
+ //LLViewerObject::idleUpdate(agent, world, time);
+ return TRUE;
+}
+
+
+void LLVOPartGroup::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // mPixelArea is calculated during render
+
+ LLVector3 viewer_pos_agent = agent.getCameraPositionAgent();
+ LLVector3 pos_agent = getRenderPosition();
+
+ F32 dx = viewer_pos_agent.mV[VX] - pos_agent.mV[VX];
+ F32 dy = viewer_pos_agent.mV[VY] - pos_agent.mV[VY];
+ F32 dz = viewer_pos_agent.mV[VZ] - pos_agent.mV[VZ];
+
+ F32 mid_scale = getMidScale();
+ F32 range = sqrt(dx*dx + dy*dy + dz*dz);
+
+ if (range < 0.001f || isHUDAttachment()) // range == zero
+ {
+ mAppAngle = 180.f;
+ }
+ else
+ {
+ mAppAngle = (F32) atan2( mid_scale, range) * RAD_TO_DEG;
+ }
+}
+
+void LLVOPartGroup::updateTextures(LLAgent &agent)
+{
+ // Texture stats for particles will need to be updated in a different way...
+}
+
+
+LLDrawable* LLVOPartGroup::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_PARTICLES);
+
+ LLDrawPool *pool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ mDrawable->setNumFaces(mViewerPartGroupp->getCount(), pool, getTEImage(0));
+ return mDrawable;
+}
+
+ const F32 MAX_PARTICLE_AREA_SCALE = 0.02f; // some tuned constant, limits on how much particle area to draw
+
+BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_PARTICLES);
+
+ LLVector3 at;
+ LLVector3 up;
+ LLVector3 right;
+ LLVector3 position_agent;
+ LLVector3 camera_agent = gAgent.getCameraPositionAgent();
+ LLVector2 uvs[4];
+
+ uvs[0].setVec(0.f, 1.f);
+ uvs[1].setVec(0.f, 0.f);
+ uvs[2].setVec(1.f, 1.f);
+ uvs[3].setVec(1.f, 0.f);
+
+ LLDrawPool *drawpool = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ S32 num_parts = mViewerPartGroupp->getCount();
+ LLFace *facep;
+
+ if (!num_parts)
+ {
+ drawable->setNumFaces(0, drawpool, getTEImage(0));
+ LLPipeline::sCompiles++;
+ return TRUE;
+ }
+
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_PARTICLES)))
+ {
+ return TRUE;
+ }
+
+// drawable->setNumFaces(num_parts, drawpool, getTEImage(0));
+ drawable->setNumFacesFast(num_parts, drawpool, getTEImage(0));
+
+ LLVector3 normal(-gCamera->getXAxis()); // make point agent face camera
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp = 0;
+
+ S32 vert_offset;
+ F32 tot_area = 0;
+ F32 max_area = LLViewerPartSim::getMaxPartCount() * MAX_PARTICLE_AREA_SCALE;
+
+ S32 count=0;
+ for (S32 i = 0; i < num_parts; i++)
+ {
+ const LLViewerPart &part = mViewerPartGroupp->mParticles[i];
+
+ LLVector3 part_pos_agent(part.mPosAgent);
+ at = part_pos_agent - camera_agent;
+
+ //F32 invcamdist = 1.0f / at.magVec();
+ //area += (part.mScale.mV[0]*invcamdist)*(part.mScale.mV[1]*invcamdist);
+ F32 camera_dist_squared = at.magVecSquared();
+ F32 inv_camera_dist_squared;
+ if (camera_dist_squared > 1.f)
+ inv_camera_dist_squared = 1.f / camera_dist_squared;
+ else
+ inv_camera_dist_squared = 1.f;
+ F32 area = part.mScale.mV[0] * part.mScale.mV[1] * inv_camera_dist_squared;
+ tot_area += area;
+ if (tot_area > max_area)
+ {
+ break;
+ }
+
+ count++;
+
+ facep = drawable->getFace(i);
+ const F32 NEAR_PART_DIST_SQ = 5.f*5.f; // Only discard particles > 5 m from the camera
+ const F32 MIN_PART_AREA = .005f*.005f; // only less than 5 mm x 5 mm at 1 m from camera
+ if (camera_dist_squared > NEAR_PART_DIST_SQ && area < MIN_PART_AREA)
+ {
+ facep->setSize(0, 0);
+ continue;
+ }
+ facep->setSize(4, 6);
+ facep->mCenterAgent = part_pos_agent;
+ vert_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == vert_offset)
+ {
+ return TRUE;
+ }
+
+ right = at % LLVector3(0.f, 0.f, 1.f);
+ right.normVec();
+ up = right % at;
+ up.normVec();
+
+ if (part.mFlags & LLPartData::LL_PART_FOLLOW_VELOCITY_MASK)
+ {
+ LLVector3 normvel = part.mVelocity;
+ normvel.normVec();
+ LLVector2 up_fracs;
+ up_fracs.mV[0] = normvel*right;
+ up_fracs.mV[1] = normvel*up;
+ up_fracs.normVec();
+
+ LLVector3 new_up;
+ LLVector3 new_right;
+ new_up = up_fracs.mV[0] * right + up_fracs.mV[1]*up;
+ new_right = up_fracs.mV[1] * right - up_fracs.mV[0]*up;
+ up = new_up;
+ right = new_right;
+ up.normVec();
+ right.normVec();
+ }
+
+ right *= 0.5f*part.mScale.mV[0];
+ up *= 0.5f*part.mScale.mV[1];
+
+ *verticesp++ = part_pos_agent + up - right;
+ *verticesp++ = part_pos_agent - up - right;
+ *verticesp++ = part_pos_agent + up + right;
+ *verticesp++ = part_pos_agent - up + right;
+
+ *texCoordsp++ = uvs[0];
+ *texCoordsp++ = uvs[1];
+ *texCoordsp++ = uvs[2];
+ *texCoordsp++ = uvs[3];
+
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+ *normalsp++ = normal;
+
+ if (part.mFlags & LLPartData::LL_PART_EMISSIVE_MASK)
+ {
+ facep->setState(LLFace::FULLBRIGHT);
+ }
+ else
+ {
+ facep->clearState(LLFace::FULLBRIGHT);
+ }
+ facep->setFaceColor(part.mColor);
+
+ *indicesp++ = vert_offset + 0;
+ *indicesp++ = vert_offset + 1;
+ *indicesp++ = vert_offset + 2;
+
+ *indicesp++ = vert_offset + 1;
+ *indicesp++ = vert_offset + 3;
+ *indicesp++ = vert_offset + 2;
+ }
+ for (S32 j = count; j < drawable->getNumFaces(); j++)
+ {
+ drawable->getFace(j)->setSize(0,0);
+ }
+
+ mPixelArea = tot_area * gCamera->getPixelMeterRatio() * gCamera->getPixelMeterRatio();
+ const F32 area_scale = 10.f; // scale area to increase priority a bit
+ getTEImage(0)->addTextureStats(mPixelArea * area_scale);
+
+ LLPipeline::sCompiles++;
+
+ return TRUE;
+}
+
diff --git a/indra/newview/llvopartgroup.h b/indra/newview/llvopartgroup.h
new file mode 100644
index 0000000000..657d1824d0
--- /dev/null
+++ b/indra/newview/llvopartgroup.h
@@ -0,0 +1,42 @@
+/**
+ * @file llvopartgroup.h
+ * @brief Group of particle systems
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOPARTGROUP_H
+#define LL_LLVOPARTGROUP_H
+
+#include "llviewerobject.h"
+#include "v3math.h"
+#include "v3color.h"
+#include "llframetimer.h"
+
+class LLViewerPartGroup;
+
+class LLVOPartGroup : public LLViewerObject
+{
+public:
+ LLVOPartGroup(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+
+ ~LLVOPartGroup();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void setViewerPartGroup(LLViewerPartGroup *part_groupp) { mViewerPartGroupp = part_groupp; }
+protected:
+ LLViewerPartGroup *mViewerPartGroupp;
+
+ LLColor4 mDebugColor;
+};
+
+#endif // LL_LLVOPARTGROUP_H
diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp
new file mode 100644
index 0000000000..5c76dc5a87
--- /dev/null
+++ b/indra/newview/llvosky.cpp
@@ -0,0 +1,2429 @@
+/**
+ * @file llvosky.cpp
+ * @brief LLVOSky class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvosky.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+#include "llframetimer.h"
+#include "timing.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llcubemap.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolwater.h"
+#include "llglheaders.h"
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h" // for gSunTextureID
+
+const S32 NUM_TILES_X = 8;
+const S32 NUM_TILES_Y = 4;
+const S32 NUM_TILES = NUM_TILES_X * NUM_TILES_Y;
+
+// Heavenly body constants
+const F32 SUN_DISK_RADIUS = 0.5f;
+const F32 MOON_DISK_RADIUS = SUN_DISK_RADIUS * 0.9f;
+const F32 SUN_INTENSITY = 1e5;
+const F32 SUN_DISK_INTENSITY = 24.f;
+
+
+// Texture coordinates:
+const LLVector2 TEX00 = LLVector2(0.f, 0.f);
+const LLVector2 TEX01 = LLVector2(0.f, 1.f);
+const LLVector2 TEX10 = LLVector2(1.f, 0.f);
+const LLVector2 TEX11 = LLVector2(1.f, 1.f);
+
+//static
+LLColor3 LLHaze::sAirScaSeaLevel;
+
+class LLFastLn
+{
+public:
+ LLFastLn()
+ {
+ mTable[0] = 0;
+ for( S32 i = 1; i < 257; i++ )
+ {
+ mTable[i] = log((F32)i);
+ }
+ }
+
+ F32 ln( F32 x )
+ {
+ const F32 OO_255 = 0.003921568627450980392156862745098f;
+ const F32 LN_255 = 5.5412635451584261462455391880218f;
+
+ if( x < OO_255 )
+ {
+ return log(x);
+ }
+ else
+ if( x < 1 )
+ {
+ x *= 255.f;
+ S32 index = llfloor(x);
+ F32 t = x - index;
+ F32 low = mTable[index];
+ F32 high = mTable[index + 1];
+ return low + t * (high - low) - LN_255;
+ }
+ else
+ if( x <= 255 )
+ {
+ S32 index = llfloor(x);
+ F32 t = x - index;
+ F32 low = mTable[index];
+ F32 high = mTable[index + 1];
+ return low + t * (high - low);
+ }
+ else
+ {
+ return log( x );
+ }
+ }
+
+ F32 pow( F32 x, F32 y )
+ {
+ return (F32)LL_FAST_EXP(y * ln(x));
+ }
+
+
+private:
+ F32 mTable[257]; // index 0 is unused
+};
+
+LLFastLn gFastLn;
+
+
+// Functions used a lot.
+
+inline F32 LLHaze::calcPhase(const F32 cos_theta) const
+{
+ const F32 g2 = mG * mG;
+ const F32 den = 1 + g2 - 2 * mG * cos_theta;
+ return (1 - g2) * gFastLn.pow(den, -1.5);
+}
+
+inline void color_pow(LLColor3 &col, const F32 e)
+{
+ col.mV[0] = gFastLn.pow(col.mV[0], e);
+ col.mV[1] = gFastLn.pow(col.mV[1], e);
+ col.mV[2] = gFastLn.pow(col.mV[2], e);
+}
+
+inline LLColor3 color_norm(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1.f)
+ {
+ return 1.f/m * col;
+ }
+ else return col;
+}
+
+inline LLColor3 color_norm_fog(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 0.75f)
+ {
+ return 0.75f/m * col;
+ }
+ else return col;
+}
+
+
+inline LLColor4 color_norm_abs(const LLColor4 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1e-6)
+ {
+ return 1.f/m * col;
+ }
+ else
+ {
+ return col;
+ }
+}
+
+
+inline F32 color_intens ( const LLColor4 &col )
+{
+ return col.mV[0] + col.mV[1] + col.mV[2];
+}
+
+
+inline F32 color_avg ( const LLColor3 &col )
+{
+ return color_intens(col) / 3;
+}
+
+inline void color_gamma_correct(LLColor3 &col)
+{
+ const F32 gamma_inv = 1.f/1.2f;
+ if (col.mV[0] != 0.f)
+ {
+ col.mV[0] = gFastLn.pow(col.mV[0], gamma_inv);
+ }
+ if (col.mV[1] != 0.f)
+ {
+ col.mV[1] = gFastLn.pow(col.mV[1], gamma_inv);
+ }
+ if (col.mV[2] != 0.f)
+ {
+ col.mV[2] = gFastLn.pow(col.mV[2], gamma_inv);
+ }
+}
+
+inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply = FALSE);
+inline F32 min_intens_factor( LLColor3& col, F32 min_intens, BOOL postmultiply)
+{
+ const F32 intens = color_intens(col);
+ F32 factor = 1;
+ if (0 == intens)
+ {
+ return 0;
+ }
+
+ if (intens < min_intens)
+ {
+ factor = min_intens / intens;
+ if (postmultiply)
+ col *= factor;
+ }
+ return factor;
+}
+
+inline LLVector3 move_vec(const LLVector3& v, const F32 cos_max_angle)
+{
+ LLVector3 v_norm = v;
+ v_norm.normVec();
+
+ LLVector2 v_norm_proj(v_norm.mV[0], v_norm.mV[1]);
+ const F32 projection2 = v_norm_proj.magVecSquared();
+ const F32 scale = sqrt((1 - cos_max_angle * cos_max_angle) / projection2);
+ return LLVector3(scale * v_norm_proj.mV[0], scale * v_norm_proj.mV[1], cos_max_angle);
+}
+
+
+/***************************************
+ Transparency Map
+***************************************/
+
+void LLTranspMap::init(const F32 elev, const F32 step, const F32 h, const LLHaze* const haze)
+{
+ mHaze = haze;
+ mAtmHeight = h;
+ mElevation = elev;
+ mStep = step;
+ mStepInv = 1.f / step;
+ F32 sin_angle = EARTH_RADIUS/(EARTH_RADIUS + mElevation);
+ mCosMaxAngle = -sqrt(1 - sin_angle * sin_angle);
+ mMapSize = S32(ceil((1 - mCosMaxAngle) * mStepInv + 1) + 0.5);
+ delete mT;
+ mT = new LLColor3[mMapSize];
+
+ for (S32 i = 0; i < mMapSize; ++i)
+ {
+ const F32 cos_a = 1 - i*mStep;
+ const LLVector3 dir(0, sqrt(1-cos_a*cos_a), cos_a);
+ mT[i] = calcAirTranspDir(mElevation, dir);
+ }
+}
+
+
+
+LLColor3 LLTranspMap::calcAirTranspDir(const F32 elevation, const LLVector3 &dir) const
+{
+ LLColor3 opt_depth(0, 0, 0);
+ const LLVector3 point(0, 0, EARTH_RADIUS + elevation);
+ F32 dist = -dir * point;
+ LLVector3 cur_point;
+ S32 s;
+
+ if (dist > 0)
+ {
+ cur_point = point + dist * dir;
+// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
+// const F32 e_pow_k = LL_FAST_EXP(K);
+ const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (s = 0; s < NO_STEPS; ++s)
+ {
+ const F32 h = cur_point.magVec() - EARTH_RADIUS;
+ step *= e_pow_k;
+ opt_depth += calcSigExt(h) * step;
+ cur_point -= dir * step;
+ }
+ opt_depth *= 2;
+ cur_point = point + 2 * dist * dir;
+ }
+ else
+ {
+ cur_point = point;
+ }
+
+ dist = hitsAtmEdge(cur_point, dir);
+// const F32 K = log(dist * INV_FIRST_STEP + 1) * INV_NO_STEPS;
+// const F32 e_pow_k = LL_FAST_EXP(K);
+ const F32 e_pow_k = gFastLn.pow( dist * INV_FIRST_STEP + 1, INV_NO_STEPS );
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (s = 0; s < NO_STEPS; ++s)
+ {
+ const F32 h = cur_point.magVec() - EARTH_RADIUS;
+ step *= e_pow_k;
+ opt_depth += calcSigExt(h) * step;
+ cur_point += dir * step;
+ }
+
+ opt_depth *= -4.0f*F_PI;
+ opt_depth.exp();
+ return opt_depth;
+}
+
+
+
+F32 LLTranspMap::hitsAtmEdge(const LLVector3& X, const LLVector3& dir) const
+{
+ const F32 tca = -dir * X;
+ const F32 R = EARTH_RADIUS + mAtmHeight;
+ const F32 thc2 = R * R - X.magVecSquared() + tca * tca;
+ return tca + sqrt ( thc2 );
+}
+
+
+
+
+
+void LLTranspMapSet::init(const S32 size, const F32 first_step, const F32 media_height, const LLHaze* const haze)
+{
+ const F32 angle_step = 0.005f;
+ mSize = size;
+ mMediaHeight = media_height;
+
+ delete[] mTransp;
+ mTransp = new LLTranspMap[mSize];
+
+ delete[] mHeights;
+ mHeights = new F32[mSize];
+
+ F32 h = 0;
+ mHeights[0] = h;
+ mTransp[0].init(h, angle_step, mMediaHeight, haze);
+ const F32 K = log(mMediaHeight / first_step + 1) / (mSize - 1);
+ const F32 e_pow_k = exp(K);
+ F32 step = first_step * (e_pow_k - 1);
+
+ for (S32 s = 1; s < mSize; ++s)
+ {
+ h += step;
+ mHeights[s] = h;
+ mTransp[s].init(h, angle_step, mMediaHeight, haze);
+ step *= e_pow_k;
+ }
+}
+
+LLTranspMapSet::~LLTranspMapSet()
+{
+ delete[] mTransp;
+ mTransp = NULL;
+ delete[] mHeights;
+ mHeights = NULL;
+}
+
+
+
+/***************************************
+ SkyTex
+***************************************/
+
+S32 LLSkyTex::sComponents = 4;
+S32 LLSkyTex::sResolution = 64;
+F32 LLSkyTex::sInterpVal = 0.f;
+S32 LLSkyTex::sCurrent = 0;
+
+
+LLSkyTex::LLSkyTex()
+{
+}
+
+void LLSkyTex::init()
+{
+ mSkyData = new LLColor3[sResolution * sResolution];
+ mSkyDirs = new LLVector3[sResolution * sResolution];
+
+ for (S32 i = 0; i < 2; ++i)
+ {
+ mImageGL[i] = new LLImageGL(FALSE);
+ mImageGL[i]->setClamp(TRUE, TRUE);
+ mImageRaw[i] = new LLImageRaw(sResolution, sResolution, sComponents);
+
+ initEmpty(i);
+ }
+}
+
+void LLSkyTex::cleanupGL()
+{
+ mImageGL[0] = NULL;
+ mImageGL[1] = NULL;
+}
+
+void LLSkyTex::restoreGL()
+{
+ for (S32 i = 0; i < 2; i++)
+ {
+ mImageGL[i] = new LLImageGL(FALSE);
+ mImageGL[i]->setClamp(TRUE, TRUE);
+ }
+}
+
+LLSkyTex::~LLSkyTex()
+{
+ delete[] mSkyData;
+ mSkyData = NULL;
+
+ delete[] mSkyDirs;
+ mSkyDirs = NULL;
+}
+
+
+void LLSkyTex::initEmpty(const S32 tex)
+{
+ U8* data = mImageRaw[tex]->getData();
+ for (S32 i = 0; i < sResolution; ++i)
+ {
+ for (S32 j = 0; j < sResolution; ++j)
+ {
+ const S32 basic_offset = (i * sResolution + j);
+ S32 offset = basic_offset * sComponents;
+ data[offset] = 0;
+ data[offset+1] = 0;
+ data[offset+2] = 0;
+ data[offset+3] = 255;
+
+ mSkyData[basic_offset].setToBlack();
+ }
+ }
+
+ createTexture(tex);
+}
+
+
+void LLSkyTex::create(const F32 brightness_scale, const LLColor3& multiscatt)
+{
+ U8* data = mImageRaw[sCurrent]->getData();
+ for (S32 i = 0; i < sResolution; ++i)
+ {
+ for (S32 j = 0; j < sResolution; ++j)
+ {
+ const S32 basic_offset = (i * sResolution + j);
+ S32 offset = basic_offset * sComponents;
+ LLColor3 col(mSkyData[basic_offset]);
+ if (getDir(i, j).mV[VZ] >= -0.02f) {
+ col += 0.1f * multiscatt;
+ col *= brightness_scale;
+ col.clamp();
+ color_gamma_correct(col);
+ }
+
+ U32* pix = (U32*)(data + offset);
+ LLColor4 temp = LLColor4(col);
+ LLColor4U temp1 = LLColor4U(temp);
+ *pix = temp1.mAll;
+ }
+ }
+ createTexture(sCurrent);
+}
+
+void LLSkyTex::createTexture(S32 which)
+{
+ mImageGL[which]->createGLTexture(0, mImageRaw[which]);
+ mImageGL[which]->setClamp(TRUE, TRUE);
+}
+
+void LLSkyTex::bindTexture(BOOL curr)
+{
+ mImageGL[getWhich(curr)]->bind();
+}
+
+/***************************************
+ Sky
+***************************************/
+
+F32 LLHeavenBody::sInterpVal = 0;
+
+F32 LLVOSky::sNighttimeBrightness = 1.5f;
+
+S32 LLVOSky::sResolution = LLSkyTex::getResolution();
+S32 LLVOSky::sTileResX = sResolution/NUM_TILES_X;
+S32 LLVOSky::sTileResY = sResolution/NUM_TILES_Y;
+
+LLVOSky::LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp),
+ mSun(SUN_DISK_RADIUS), mMoon(MOON_DISK_RADIUS),
+ mBrightnessScale(1.f),
+ mBrightnessScaleNew(0.f),
+ mBrightnessScaleGuess(1.f),
+ mWeatherChange(FALSE),
+ mCloudDensity(0.2f),
+ mWind(0.f),
+ mForceUpdate(FALSE),
+ mWorldScale(1.f)
+{
+ mInitialized = FALSE;
+ mbCanSelect = FALSE;
+ mUpdateTimer.reset();
+
+ for (S32 i = 0; i < 6; i++)
+ {
+ mSkyTex[i].init();
+ }
+ for (S32 i=0; i<FACE_COUNT; i++)
+ {
+ mFace[i] = NULL;
+ }
+
+ mCameraPosAgent = gAgent.getCameraPositionAgent();
+ mAtmHeight = ATM_HEIGHT;
+ mEarthCenter = LLVector3(mCameraPosAgent.mV[0], mCameraPosAgent.mV[1], -EARTH_RADIUS);
+ updateHaze();
+
+ mSunDefaultPosition = gSavedSettings.getVector3("SkySunDefaultPosition");
+ if (gSavedSettings.getBOOL("SkyOverrideSimSunPosition"))
+ {
+ initSunDirection(mSunDefaultPosition, LLVector3(0, 0, 0));
+ }
+ mAmbientScale = gSavedSettings.getF32("SkyAmbientScale");
+ mNightColorShift = gSavedSettings.getColor3("SkyNightColorShift");
+ mFogColor.mV[VRED] = mFogColor.mV[VGREEN] = mFogColor.mV[VBLUE] = 0.5f;
+ mFogColor.mV[VALPHA] = 0.0f;
+ mFogRatio = 1.2f;
+
+ mSun.setIntensity(SUN_INTENSITY);
+ mMoon.setIntensity(0.1f * SUN_INTENSITY);
+
+ mCubeMap = NULL;
+
+ mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mSunTexturep->setClamp(TRUE, TRUE);
+ mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mMoonTexturep->setClamp(TRUE, TRUE);
+ mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
+ mBloomTexturep->setClamp(TRUE, TRUE);
+}
+
+
+LLVOSky::~LLVOSky()
+{
+ // Don't delete images - it'll get deleted by gImageList on shutdown
+ // This needs to be done for each texture
+
+ delete mCubeMap;
+ mCubeMap = NULL;
+}
+
+void LLVOSky::initClass()
+{
+ LLHaze::initClass();
+}
+
+
+void LLVOSky::init()
+{
+ // index of refraction calculation.
+ mTransp.init(NO_STEPS+1+4, FIRST_STEP, mAtmHeight, &mHaze);
+
+ const F32 haze_int = color_intens(mHaze.calcSigSca(0));
+ mHazeConcentration = haze_int /
+ (color_intens(LLHaze::calcAirSca(0)) + haze_int);
+
+ mBrightnessScaleNew = 0;
+
+ // Initialize the cached normalized direction vectors
+ for (S32 side = 0; side < 6; ++side)
+ {
+ for (S32 tile = 0; tile < NUM_TILES; ++tile)
+ {
+ initSkyTextureDirs(side, tile);
+ createSkyTexture(side, tile);
+ }
+ }
+
+ calcBrightnessScaleAndColors();
+ initCubeMap();
+}
+
+void LLVOSky::initCubeMap()
+{
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw());
+ }
+ if (mCubeMap != NULL)
+ {
+ mCubeMap->init(images);
+ }
+ else if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
+ {
+ mCubeMap = new LLCubeMap();
+ mCubeMap->init(images);
+ }
+}
+
+
+void LLVOSky::cleanupGL()
+{
+ S32 i;
+ for (i = 0; i < 6; i++)
+ {
+ mSkyTex[i].cleanupGL();
+ }
+ if (getCubeMap())
+ {
+ getCubeMap()->destroyGL();
+ }
+}
+
+void LLVOSky::restoreGL()
+{
+ S32 i;
+ for (i = 0; i < 6; i++)
+ {
+ mSkyTex[i].restoreGL();
+ }
+ mSunTexturep = gImageList.getImage(gSunTextureID, TRUE, TRUE);
+ mSunTexturep->setClamp(TRUE, TRUE);
+ mMoonTexturep = gImageList.getImage(gMoonTextureID, TRUE, TRUE);
+ mMoonTexturep->setClamp(TRUE, TRUE);
+ mBloomTexturep = gImageList.getImage(IMG_BLOOM1);
+ mBloomTexturep->setClamp(TRUE, TRUE);
+
+ calcBrightnessScaleAndColors();
+
+ // Water is currently broken on Mac.
+ if (gSavedSettings.getBOOL("RenderWater") && gGLManager.mHasCubeMap)
+ {
+ LLCubeMap* cube_map = getCubeMap();
+
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw());
+ }
+
+ if(cube_map)
+ {
+ cube_map->init(images);
+ mForceUpdate = TRUE;
+ }
+ }
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+
+}
+
+
+void LLVOSky::updateHaze()
+{
+ time_t timer;
+ time(&timer);
+ static LLRand WeatherRandomNumber(gmtime(&timer)->tm_mday);
+ if (gSavedSettings.getBOOL("FixedWeather"))
+ {
+ WeatherRandomNumber.seed(8008135);
+ }
+
+ const F32 fo_upper_bound = 5;
+ const F32 sca_upper_bound = 6;
+ const F32 fo = 1 + WeatherRandomNumber.llfrand(fo_upper_bound - 1);
+ const static F32 upper = 0.5f / gFastLn.ln(fo_upper_bound);
+ mHaze.setFalloff(fo);
+ mHaze.setG(WeatherRandomNumber.llfrand(0.0f + upper * gFastLn.ln(fo)));
+ LLColor3 sca;
+ const F32 cd = mCloudDensity * 3;
+ F32 min_r = cd - 1;
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ F32 max_r = cd + 1;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[0] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);//frand(6);
+
+ min_r = sca.mV[0] - 0.1f;
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ max_r = sca.mV[0] + 0.5f;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[1] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);
+
+ min_r = sca.mV[1];
+ if (min_r < 0)
+ {
+ min_r = 0;
+ }
+ max_r = sca.mV[1] + 1;
+ if (max_r > sca_upper_bound)
+ {
+ max_r = sca_upper_bound;
+ }
+
+ sca.mV[2] = min_r + WeatherRandomNumber.llfrand(max_r - min_r);
+
+ sca = AIR_SCA_AVG * sca;
+
+ mHaze.setSigSca(sca);
+}
+
+void LLVOSky::initSkyTextureDirs(const S32 side, const S32 tile)
+{
+ S32 tile_x = tile % NUM_TILES_X;
+ S32 tile_y = tile / NUM_TILES_X;
+
+ S32 tile_x_pos = tile_x * sTileResX;
+ S32 tile_y_pos = tile_y * sTileResY;
+
+ F32 coeff[3] = {0, 0, 0};
+ const S32 curr_coef = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
+ const S32 side_dir = (((side & 1) << 1) - 1); // even = -1, odd = 1
+ const S32 x_coef = (curr_coef + 1) % 3;
+ const S32 y_coef = (x_coef + 1) % 3;
+
+ coeff[curr_coef] = (F32)side_dir;
+
+ F32 inv_res = 1.f/sResolution;
+ S32 x, y;
+ for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
+ {
+ for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
+ {
+ coeff[x_coef] = F32((x<<1) + 1) * inv_res - 1.f;
+ coeff[y_coef] = F32((y<<1) + 1) * inv_res - 1.f;
+ LLVector3 dir(coeff[0], coeff[1], coeff[2]);
+ dir.normVec();
+ mSkyTex[side].setDir(dir, x, y);
+ }
+ }
+}
+
+void LLVOSky::createSkyTexture(const S32 side, const S32 tile)
+{
+ S32 tile_x = tile % NUM_TILES_X;
+ S32 tile_y = tile / NUM_TILES_X;
+
+ S32 tile_x_pos = tile_x * sTileResX;
+ S32 tile_y_pos = tile_y * sTileResY;
+
+ S32 x, y;
+ for (y = tile_y_pos; y < (tile_y_pos + sTileResY); ++y)
+ {
+ for (x = tile_x_pos; x < (tile_x_pos + sTileResX); ++x)
+ {
+ mSkyTex[side].setPixel(calcSkyColorInDir(mSkyTex[side].getDir(x, y)), x, y);
+ }
+ }
+}
+
+
+LLColor3 LLVOSky::calcSkyColorInDir(const LLVector3 &dir)
+{
+ LLColor3 col, transp;
+
+ if (dir.mV[VZ] < -0.02)
+ {
+ col = LLColor3(llmax(mFogColor[0],0.2f), llmax(mFogColor[1],0.2f), llmax(mFogColor[2],0.27f));
+ float x = 1.0f-fabsf(-0.1f-dir.mV[VZ]);
+ x *= x;
+ col.mV[0] *= x*x;
+ col.mV[1] *= powf(x, 2.5f);
+ col.mV[2] *= x*x*x;
+ return col;
+ }
+
+
+ calcSkyColorInDir(col, transp, dir);
+ F32 br = color_max(col);
+ if (br > mBrightnessScaleNew)
+ {
+ mBrightnessScaleNew = br;
+ mBrightestPointNew = col;
+ }
+ return col;
+}
+
+
+LLColor4 LLVOSky::calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exager = 1) const
+{
+ LLColor3 col, tr;
+ calcInScatter(col, tr, point, exager);
+ col *= mBrightnessScaleGuess;
+ transp = LLColor4(tr);
+ return LLColor4(col);
+}
+
+
+
+void LLVOSky::calcSkyColorInDir(LLColor3& res, LLColor3& transp, const LLVector3& dir) const
+{
+ const LLVector3& tosun = getToSunLast();
+ res.setToBlack();
+ LLColor3 haze_res(0.f, 0.f, 0.f);
+ transp.setToWhite();
+ LLVector3 step_v ;
+ LLVector3 cur_pos = mCameraPosAgent;
+ F32 h;
+
+ F32 dist = calcHitsAtmEdge(mCameraPosAgent, dir);
+// const F32 K = log(dist / FIRST_STEP + 1) / NO_STEPS;
+ const F32 K = gFastLn.ln(dist / FIRST_STEP + 1) / NO_STEPS;
+ const F32 e_pow_k = (F32)LL_FAST_EXP(K);
+ F32 step = FIRST_STEP * (1 - 1 / e_pow_k);
+
+ for (S32 s = 0; s < NO_STEPS; ++s)
+ {
+ h = calcHeight(cur_pos);
+ step *= e_pow_k;
+ LLColor3 air_sca_opt_depth;
+ LLHaze::calcAirSca(h, air_sca_opt_depth);
+ air_sca_opt_depth *= step;
+
+ LLColor3 haze_sca_opt_depth;
+ mHaze.calcSigSca(h, haze_sca_opt_depth);
+ haze_sca_opt_depth *= step;
+
+ LLColor3 haze_ext_opt_depth = haze_sca_opt_depth;
+ haze_ext_opt_depth *= (1.f + mHaze.getAbsCoef());
+
+ if (calcHitsEarth(cur_pos, tosun) < 0) // calculates amount of in-scattered light from the sun
+ {
+ //visibility check is too expensive
+ LLColor3 air_transp;
+ mTransp.calcTransp(calcUpVec(cur_pos) * tosun, h, air_transp);
+ air_transp *= transp;
+ res += air_sca_opt_depth * air_transp;
+ haze_res += haze_sca_opt_depth * air_transp;
+ }
+ LLColor3 temp(-4.f * F_PI * (air_sca_opt_depth + haze_ext_opt_depth));
+ temp.exp();
+ transp *= temp;
+ step_v = dir * step;
+ cur_pos += step_v;
+ }
+ const F32 cos_dir = dir * tosun;
+ res *= calcAirPhaseFunc(cos_dir);
+ res += haze_res * mHaze.calcPhase(cos_dir);
+ res *= mSun.getIntensity();
+}
+
+
+
+
+void LLVOSky::calcInScatter(LLColor3& res, LLColor3& transp,
+ const LLVector3& P, const F32 exaggeration) const
+{
+ const LLVector3& tosun = getToSunLast();
+ res.setToBlack();
+ transp.setToWhite();
+
+ LLVector3 lower, upper;
+ LLVector3 dir = P - mCameraPosAgent;
+
+ F32 dist = exaggeration * dir.normVec();
+
+ const F32 cos_dir = dir * tosun;
+
+ if (dir.mV[VZ] > 0)
+ {
+ lower = mCameraPosAgent;
+ upper = P;
+ }
+ else
+ {
+ lower = P;
+ upper = mCameraPosAgent;
+ dir = -dir;
+ }
+
+ const F32 lower_h = calcHeight(lower);
+ const F32 upper_h = calcHeight(upper);
+ const LLVector3 up_upper = calcUpVec(upper);
+ const LLVector3 up_lower = calcUpVec(lower);
+
+ transp = color_div(mTransp.calcTransp(up_lower * dir, lower_h),
+ mTransp.calcTransp(up_upper * dir, upper_h));
+ color_pow(transp, exaggeration);
+
+ if (calcHitsEarth(upper, tosun) > 0)
+ {
+ const F32 avg = color_avg(transp);
+ //const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
+ transp.setVec(avg, avg, avg);
+ return;
+ }
+
+ LLColor3 air_sca_opt_depth = LLHaze::calcAirSca(upper_h);
+ LLColor3 haze_sca_opt_depth = mHaze.calcSigSca(upper_h);
+ LLColor3 sun_transp;
+ mTransp.calcTransp(up_upper * tosun, upper_h, sun_transp);
+
+ if (calcHitsEarth(lower, tosun) < 0)
+ {
+ air_sca_opt_depth += LLHaze::calcAirSca(lower_h);
+ air_sca_opt_depth *= 0.5;
+ haze_sca_opt_depth += mHaze.calcSigSca(lower_h);
+ haze_sca_opt_depth *= 0.5;
+ sun_transp += mTransp.calcTransp(up_lower * tosun, lower_h);
+ sun_transp *= 0.5;
+ }
+
+ res = calcAirPhaseFunc(cos_dir) * air_sca_opt_depth;
+ res += mHaze.calcPhase(cos_dir) * haze_sca_opt_depth;
+ res = mSun.getIntensity() * dist * sun_transp * res;
+}
+
+
+
+
+
+
+F32 LLVOSky::calcHitsEarth(const LLVector3& orig, const LLVector3& dir) const
+{
+ const LLVector3 from_earth_center = mEarthCenter - orig;
+ const F32 tca = dir * from_earth_center;
+ if ( tca < 0 )
+ {
+ return -1;
+ }
+
+ const F32 thc2 = EARTH_RADIUS * EARTH_RADIUS -
+ from_earth_center.magVecSquared() + tca * tca;
+ if (thc2 < 0 )
+ {
+ return -1;
+ }
+
+ return tca - sqrt ( thc2 );
+}
+
+F32 LLVOSky::calcHitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const
+{
+ const LLVector3 from_earth_center = mEarthCenter - orig;
+ const F32 tca = dir * from_earth_center;
+
+ const F32 thc2 = (EARTH_RADIUS + mAtmHeight) * (EARTH_RADIUS + mAtmHeight) -
+ from_earth_center.magVecSquared() + tca * tca;
+ return tca + sqrt(thc2);
+}
+
+
+void LLVOSky::updateBrightestDir()
+{
+ LLColor3 br_pt, transp;
+ const S32 test_no = 5;
+ const F32 step = F_PI_BY_TWO / (test_no + 1);
+ for (S32 i = 0; i < test_no; ++i)
+ {
+ F32 cos_dir = cos ((i + 1) * step);
+ calcSkyColorInDir(br_pt, transp, move_vec(getToSunLast(), cos_dir));
+ const F32 br = color_max(br_pt);
+ if (br > mBrightnessScaleGuess)
+ {
+ mBrightnessScaleGuess = br;
+ mBrightestPointGuess = br_pt;
+ }
+ }
+}
+
+
+void LLVOSky::calcBrightnessScaleAndColors()
+{
+ // new correct normalization.
+ if (mBrightnessScaleNew < 1e-7)
+ {
+ mBrightnessScale = 1;
+ mBrightestPoint.setToBlack();
+ }
+ else
+ {
+ mBrightnessScale = 1.f/mBrightnessScaleNew;
+ mBrightestPoint = mBrightestPointNew;
+ }
+
+ mBrightnessScaleNew = 0;
+ // and addition
+
+ // Calculate Sun and Moon color
+ const F32 h = llmax(0.0f, mCameraPosAgent.mV[2]);
+ const LLColor3 sun_color = mSun.getIntensity() * mTransp.calcTransp(getToSunLast().mV[2], h);
+ const LLColor3 moon_color = mNightColorShift *
+ mMoon.getIntensity() * mTransp.calcTransp(getToMoonLast().mV[2], h);
+
+ F32 intens = color_intens(sun_color);
+ F32 increase_sun_br = (intens > 0) ? 1.2f * color_intens(mBrightestPoint) / intens : 1;
+
+ intens = color_intens(moon_color);
+ F32 increase_moon_br = (intens > 0) ? 1.2f * llmax(1.0f, color_intens(mBrightestPoint) / intens) : 1;
+
+ mSun.setColor(mBrightnessScale * increase_sun_br * sun_color);
+ mMoon.setColor(mBrightnessScale * increase_moon_br * moon_color);
+
+ const LLColor3 haze_col = color_norm_abs(mHaze.getSigSca());
+ for (S32 i = 0; i < 6; ++i)
+ {
+ mSkyTex[i].create(mBrightnessScale, mHazeConcentration * mBrightestPoint * haze_col);
+ }
+
+ mBrightnessScaleGuess = mBrightnessScale;
+ mBrightestPointGuess = mBrightestPoint;
+
+// calculateColors(); // MSMSM Moving this down to before generateScatterMap(), per Milo Lindens suggestion, to fix orange flashing bug.
+
+ mSun.renewDirection();
+ mSun.renewColor();
+ mMoon.renewDirection();
+ mMoon.renewColor();
+
+ LLColor3 transp;
+
+ if (calcHitsEarth(mCameraPosAgent, getToSunLast()) < 0)
+ {
+ calcSkyColorInDir(mBrightestPointGuess, transp, getToSunLast());
+ mBrightnessScaleGuess = color_max(mBrightestPointGuess);
+ updateBrightestDir();
+ mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
+ }
+ else if (getToSunLast().mV[2] > -0.5)
+ {
+ const LLVector3 almost_to_sun = toHorizon(getToSunLast());
+ calcSkyColorInDir(mBrightestPointGuess, transp, almost_to_sun);
+ mBrightnessScaleGuess = color_max(mBrightestPointGuess);
+ updateBrightestDir();
+ mBrightnessScaleGuess = 1.f / llmax(1.0f, mBrightnessScaleGuess);
+ }
+ else
+ {
+ mBrightestPointGuess.setToBlack();
+ mBrightnessScaleGuess = 1;
+ }
+
+ calculateColors(); // MSMSM Moved this down here per Milo Lindens suggestion, to fix orange flashing bug at sunset.
+ generateScatterMap();
+}
+
+
+
+void LLVOSky::calculateColors()
+{
+ const F32 h = -0.1f;
+ const LLVector3& tosun = getToSunLast();
+
+ F32 full_on, full_off, on, on_cl;
+ F32 sun_factor = 1;
+
+ // Sun Diffuse
+ if (calcHitsEarth(mCameraPosAgent, tosun) < 0)
+ {
+ mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(tosun.mV[2], h);
+ }
+ else
+ {
+ mSunDiffuse = mBrightnessScaleGuess * mSun.getIntensity() * mTransp.calcTransp(0, h);
+ }
+ mSunDiffuse = 1.0f * color_norm(mSunDiffuse);
+
+ // Sun Ambient
+ full_off = -0.3f;
+ full_on = -0.03f;
+ if (tosun.mV[2] < full_off)
+ {
+ mSunAmbient.setToBlack();
+ }
+ else
+ {
+ on = (tosun.mV[2] - full_off) / (full_on - full_off);
+ sun_factor = llmax(0.0f, llmin(on, 1.0f));
+
+ LLColor3 sun_amb = mAmbientScale * (0.8f * mSunDiffuse +
+ 0.2f * mBrightnessScaleGuess * mBrightestPointGuess);
+
+ color_norm_pow(sun_amb, 0.1f, TRUE);
+ sun_factor *= min_intens_factor(sun_amb, 1.9f);
+ mSunAmbient = LLColor4(sun_factor * sun_amb);
+ }
+
+
+ // Moon Diffuse
+ full_on = 0.3f;
+ full_off = 0.01f;
+ if (getToMoonLast().mV[2] < full_off)
+ {
+ mMoonDiffuse.setToBlack();
+ }
+ else
+ {
+ // Steve: Added moonlight diffuse factor scalar (was constant .3)
+ F32 diffuse_factor = .1f + sNighttimeBrightness * .2f; // [.1, .5] default = .3
+ on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
+ on_cl = llmin(on, 1.0f);
+ mMoonDiffuse = on_cl * mNightColorShift * diffuse_factor;
+ }
+
+ // Moon Ambient
+
+ F32 moon_amb_factor = 1.f;
+
+ if (gAgent.inPrelude())
+ {
+ moon_amb_factor *= 2.0f;
+ }
+
+ full_on = 0.30f;
+ full_off = 0.01f;
+ if (getToMoonLast().mV[2] < full_off)
+ {
+ mMoonAmbient.setToBlack();
+ }
+ else
+ {
+ on = (getToMoonLast().mV[2] - full_off) / (full_on - full_off);
+ on_cl = llmax(0.0f, llmin(on, 1.0f));
+ mMoonAmbient = on_cl * moon_amb_factor * mMoonDiffuse;
+ }
+
+
+ // Sun Diffuse
+ full_off = -0.05f;
+ full_on = -0.00f;
+ if (tosun.mV[2] < full_off)
+ {
+ mSunDiffuse.setToBlack();
+ }
+ else
+ {
+ on = (getToSunLast().mV[2] - full_off) / (full_on - full_off);
+ sun_factor = llmax(0.0f, llmin(on, 1.0f));
+
+ color_norm_pow(mSunDiffuse, 0.12f, TRUE);
+ sun_factor *= min_intens_factor(mSunDiffuse, 2.1f);
+ mSunDiffuse *= sun_factor;
+ }
+
+
+ mTotalAmbient = mSunAmbient + mMoonAmbient;
+ mTotalAmbient.setAlpha(1);
+ //llinfos << "MoonDiffuse: " << mMoonDiffuse << llendl;
+ //llinfos << "TotalAmbient: " << mTotalAmbient << llendl;
+
+ mFadeColor = mTotalAmbient + (mSunDiffuse + mMoonDiffuse) * 0.5f;
+ mFadeColor.setAlpha(0);
+}
+
+
+BOOL LLVOSky::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ return TRUE;
+}
+
+BOOL LLVOSky::updateSky()
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_SKY)))
+ {
+ return TRUE;
+ }
+
+ if (mDead)
+ {
+ // It's dead. Don't update it.
+ return TRUE;
+ }
+ if (gGLManager.mIsDisabled)
+ {
+ return TRUE;
+ }
+
+ setPositionAgent(gAgent.getCameraPositionAgent());
+
+ static S32 next_frame = 0;
+ const S32 total_no_tiles = 6 * NUM_TILES;
+ const S32 cycle_frame_no = total_no_tiles + 1;
+
+// if (mUpdateTimer.getElapsedTimeF32() > 0.1f)
+ {
+ mUpdateTimer.reset();
+ const S32 frame = next_frame;
+
+ ++next_frame;
+ next_frame = next_frame % cycle_frame_no;
+
+ sInterpVal = (!mInitialized) ? 1 : (F32)next_frame / cycle_frame_no;
+ LLSkyTex::setInterpVal( sInterpVal );
+ LLHeavenBody::setInterpVal( sInterpVal );
+ calculateColors();
+ if (mForceUpdate || total_no_tiles == frame)
+ {
+ calcBrightnessScaleAndColors();
+ LLSkyTex::stepCurrent();
+
+ const static F32 LIGHT_DIRECTION_THRESHOLD = (F32) cos(DEG_TO_RAD * 1.f);
+ const static F32 COLOR_CHANGE_THRESHOLD = 0.01f;
+
+ LLVector3 direction = mSun.getDirection();
+ direction.normVec();
+ const F32 dot_lighting = direction * mLastLightingDirection;
+
+ LLColor3 delta_color;
+ delta_color.setVec(mLastTotalAmbient.mV[0] - mTotalAmbient.mV[0],
+ mLastTotalAmbient.mV[1] - mTotalAmbient.mV[1],
+ mLastTotalAmbient.mV[2] - mTotalAmbient.mV[2]);
+
+ if ( mForceUpdate
+ || ((dot_lighting < LIGHT_DIRECTION_THRESHOLD)
+ || (delta_color.magVec() > COLOR_CHANGE_THRESHOLD)
+ || !mInitialized)
+ && !direction.isExactlyZero())
+ {
+ mLastLightingDirection = direction;
+ mLastTotalAmbient = mTotalAmbient;
+ mInitialized = TRUE;
+
+ if (mCubeMap)
+ {
+ if (mForceUpdate)
+ {
+ updateFog(gCamera->getFar());
+ for (int side = 0; side < 6; side++)
+ {
+ for (int tile = 0; tile < NUM_TILES; tile++)
+ {
+ createSkyTexture(side, tile);
+ }
+ }
+
+ calcBrightnessScaleAndColors();
+
+ for (int side = 0; side < 6; side++)
+ {
+ LLImageRaw* raw1 = mSkyTex[side].getImageRaw(TRUE);
+ LLImageRaw* raw2 = mSkyTex[side].getImageRaw(FALSE);
+ raw2->copy(raw1);
+ mSkyTex[side].createTexture(mSkyTex[side].getWhich(FALSE));
+ }
+ next_frame = 0;
+ //llSkyTex::stepCurrent();
+ }
+
+ std::vector<LLPointer<LLImageRaw> > images;
+ for (S32 side = 0; side < 6; side++)
+ {
+ images.push_back(mSkyTex[side].getImageRaw(FALSE));
+ }
+ mCubeMap->init(images);
+ }
+ }
+
+ mForceUpdate = FALSE;
+ }
+ else
+ {
+ const S32 side = frame / NUM_TILES;
+ const S32 tile = frame % NUM_TILES;
+ createSkyTexture(side, tile);
+ }
+ }
+
+
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+
+void LLVOSky::updateTextures(LLAgent &agent)
+{
+ if (mSunTexturep)
+ {
+ mSunTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ mMoonTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ mBloomTexturep->addTextureStats( (F32)MAX_IMAGE_AREA );
+ }
+}
+
+LLDrawable *LLVOSky::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ LLDrawPoolSky *poolp = (LLDrawPoolSky*) gPipeline.getPool(LLDrawPool::POOL_SKY);
+ poolp->setSkyTex(mSkyTex);
+ poolp->setSun(&mSun);
+ poolp->setMoon(&mMoon);
+
+ for (S32 i = 0; i < 6; ++i)
+ {
+ mFace[FACE_SIDE0 + i] = mDrawable->addFace(poolp, NULL);
+ }
+
+ mFace[FACE_SUN] = mDrawable->addFace(poolp, mSunTexturep);
+ mFace[FACE_MOON] = mDrawable->addFace(poolp, mMoonTexturep);
+ mFace[FACE_BLOOM] = mDrawable->addFace(poolp, mBloomTexturep);
+
+ //mDrawable->addFace(poolp, LLViewerImage::sDefaultImagep);
+ gPipeline.markMaterialed(mDrawable);
+
+ return mDrawable;
+}
+
+BOOL LLVOSky::updateGeometry(LLDrawable *drawable)
+{
+ if (mFace[FACE_REFLECTION] == NULL)
+ {
+ mFace[FACE_REFLECTION] = drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+ }
+
+ mCameraPosAgent = drawable->getPositionAgent();
+ mEarthCenter.mV[0] = mCameraPosAgent.mV[0];
+ mEarthCenter.mV[1] = mCameraPosAgent.mV[1];
+
+
+ LLVector3 v_agent[8];
+ for (S32 i = 0; i < 8; ++i)
+ {
+ F32 x_sgn = (i&1) ? 1.f : -1.f;
+ F32 y_sgn = (i&2) ? 1.f : -1.f;
+ F32 z_sgn = (i&4) ? 1.f : -1.f;
+ v_agent[i] = mCameraPosAgent + HORIZON_DIST * LLVector3(x_sgn, y_sgn, z_sgn);
+ }
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ for (S32 side = 0; side < 6; ++side)
+ {
+ face = mFace[FACE_SIDE0 + side];
+ face->setPrimType(LLTriangles);
+ face->setSize(4, 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ S32 vtx = 0;
+ S32 curr_bit = side >> 1; // 0/1 = Z axis, 2/3 = Y, 4/5 = X
+ S32 side_dir = side & 1; // even - 0, odd - 1
+ S32 i_bit = (curr_bit + 2) % 3;
+ S32 j_bit = (i_bit + 2) % 3;
+
+ LLVector3 axis;
+ axis.mV[curr_bit] = 1;
+ face->mCenterAgent = mCameraPosAgent + (F32)((side_dir << 1) - 1) * axis * HORIZON_DIST;
+
+ vtx = side_dir << curr_bit;
+ *(verticesp++) = v_agent[vtx];
+ *(verticesp++) = v_agent[vtx | 1 << j_bit];
+ *(verticesp++) = v_agent[vtx | 1 << i_bit];
+ *(verticesp++) = v_agent[vtx | 1 << i_bit | 1 << j_bit];
+
+ *(texCoordsp++) = TEX00;
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX10;
+ *(texCoordsp++) = TEX11;
+
+ // Triangles for each side
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 3;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 3;
+ *indicesp++ = index_offset + 2;
+ }
+
+ const LLVector3 &look_at = gCamera->getAtAxis();
+ LLVector3 right = look_at % LLVector3::z_axis;
+ LLVector3 up = right % look_at;
+ right.normVec();
+ up.normVec();
+
+ const static F32 elevation_factor = 0.0f/sResolution;
+ const F32 cos_max_angle = cosHorizon(elevation_factor);
+ mSun.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_SUN, TRUE, mSun, cos_max_angle, up, right));
+ mMoon.setDraw(updateHeavenlyBodyGeometry(drawable, FACE_MOON, FALSE, mMoon, cos_max_angle, up, right));
+
+ const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.01f;
+ // gWorldPointer->getWaterHeight() + 0.01f;
+ const F32 camera_height = mCameraPosAgent.mV[2];
+ const F32 height_above_water = camera_height - water_height;
+
+ BOOL sun = FALSE;
+
+ if (mSun.isVisible())
+ {
+ if (mMoon.isVisible())
+ {
+ sun = look_at * mSun.getDirection() > 0;
+ }
+ else
+ {
+ sun = TRUE;
+ }
+ }
+
+ if (height_above_water > 0)
+ {
+#if 1 //1.9.1
+ BOOL render_ref = gPipeline.getPool(LLDrawPool::POOL_WATER)->getVertexShaderLevel() == 0;
+#else
+ BOOL render_ref = !(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) >= LLDrawPoolWater::SHADER_LEVEL_RIPPLE);
+#endif
+ if (sun)
+ {
+ setDrawRefl(0);
+ if (render_ref)
+ {
+ updateReflectionGeometry(drawable, height_above_water, mSun);
+ }
+ }
+ else
+ {
+ setDrawRefl(1);
+ if (render_ref)
+ {
+ updateReflectionGeometry(drawable, height_above_water, mMoon);
+ }
+ }
+ }
+ else
+ {
+ setDrawRefl(-1);
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+BOOL LLVOSky::updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 f, const BOOL is_sun,
+ LLHeavenBody& hb, const F32 cos_max_angle,
+ const LLVector3 &up, const LLVector3 &right)
+{
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *facep;
+
+ LLVector3 to_dir = hb.getDirection();
+ LLVector3 draw_pos = to_dir * HEAVENLY_BODY_DIST;
+
+
+ LLVector3 hb_right = to_dir % LLVector3::z_axis;
+ LLVector3 hb_up = hb_right % to_dir;
+ hb_right.normVec();
+ hb_up.normVec();
+
+ //const static F32 cos_max_turn = sqrt(3.f) / 2; // 30 degrees
+ //const F32 cos_turn_right = 1. / (llmax(cos_max_turn, hb_right * right));
+ //const F32 cos_turn_up = 1. / llmax(cos_max_turn, hb_up * up);
+
+ const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
+ F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
+ F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
+
+ // Parameters for the water reflection
+ hb.setU(HEAVENLY_BODY_FACTOR * horiz_enlargement * hb.getDiskRadius() * hb_right);
+ hb.setV(HEAVENLY_BODY_FACTOR * vert_enlargement * hb.getDiskRadius() * hb_up);
+ // End of parameters for the water reflection
+
+ const LLVector3 scaled_right = HEAVENLY_BODY_DIST * hb.getU();
+ const LLVector3 scaled_up = HEAVENLY_BODY_DIST * hb.getV();
+
+ //const LLVector3 scaled_right = horiz_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_right;//right;
+ //const LLVector3 scaled_up = vert_enlargement * HEAVENLY_BODY_SCALE * hb.getDiskRadius() * hb_up;//up;
+ LLVector3 v_clipped[4];
+
+ hb.corner(0) = draw_pos - scaled_right + scaled_up;
+ hb.corner(1) = draw_pos - scaled_right - scaled_up;
+ hb.corner(2) = draw_pos + scaled_right + scaled_up;
+ hb.corner(3) = draw_pos + scaled_right - scaled_up;
+
+
+ F32 t_left, t_right;
+ if (!clip_quad_to_horizon(t_left, t_right, v_clipped, hb.corners(), cos_max_angle))
+ {
+ hb.setVisible(FALSE);
+ return FALSE;
+ }
+ hb.setVisible(TRUE);
+
+ facep = mFace[f];
+ facep->setPrimType(LLTriangles);
+ facep->setSize(4, 6);
+ index_offset = facep->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ hb.corner(vtx) = v_clipped[vtx];
+ *(verticesp++) = hb.corner(vtx) + mCameraPosAgent;
+ }
+
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX00;
+ //*(texCoordsp++) = (t_left > 0) ? LLVector2(0, t_left) : TEX00;
+ *(texCoordsp++) = TEX11;
+ *(texCoordsp++) = TEX10;
+ //*(texCoordsp++) = (t_right > 0) ? LLVector2(1, t_right) : TEX10;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ if (is_sun)
+ {
+ if ((t_left > 0) && (t_right > 0))
+ {
+ F32 t = (t_left + t_right) * 0.5f;
+ mSun.setHorizonVisibility(0.5f * (1 + cos(t * F_PI)));
+ }
+ else
+ {
+ mSun.setHorizonVisibility();
+ }
+ updateSunHaloGeometry(drawable);
+ }
+
+ return TRUE;
+}
+
+
+
+
+// Clips quads with top and bottom sides parallel to horizon.
+
+BOOL clip_quad_to_horizon(F32& t_left, F32& t_right, LLVector3 v_clipped[4],
+ const LLVector3 v_corner[4], const F32 cos_max_angle)
+{
+ t_left = clip_side_to_horizon(v_corner[1], v_corner[0], cos_max_angle);
+ t_right = clip_side_to_horizon(v_corner[3], v_corner[2], cos_max_angle);
+
+ if ((t_left >= 1) || (t_right >= 1))
+ {
+ return FALSE;
+ }
+
+ //const BOOL left_clip = (t_left > 0);
+ //const BOOL right_clip = (t_right > 0);
+
+ //if (!left_clip && !right_clip)
+ {
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ v_clipped[vtx] = v_corner[vtx];
+ }
+ }
+/* else
+ {
+ v_clipped[0] = v_corner[0];
+ v_clipped[1] = left_clip ? ((1 - t_left) * v_corner[1] + t_left * v_corner[0])
+ : v_corner[1];
+ v_clipped[2] = v_corner[2];
+ v_clipped[3] = right_clip ? ((1 - t_right) * v_corner[3] + t_right * v_corner[2])
+ : v_corner[3];
+ }*/
+
+ return TRUE;
+}
+
+
+F32 clip_side_to_horizon(const LLVector3& V0, const LLVector3& V1, const F32 cos_max_angle)
+{
+ const LLVector3 V = V1 - V0;
+ const F32 k2 = 1.f/(cos_max_angle * cos_max_angle) - 1;
+ const F32 A = V.mV[0] * V.mV[0] + V.mV[1] * V.mV[1] - k2 * V.mV[2] * V.mV[2];
+ const F32 B = V0.mV[0] * V.mV[0] + V0.mV[1] * V.mV[1] - k2 * V0.mV[2] * V.mV[2];
+ const F32 C = V0.mV[0] * V0.mV[0] + V0.mV[1] * V0.mV[1] - k2 * V0.mV[2] * V0.mV[2];
+
+ if (fabs(A) < 1e-7)
+ {
+ return -0.1f; // v0 is cone origin and v1 is on the surface of the cone.
+ }
+
+ const F32 det = sqrt(B*B - A*C);
+ const F32 t1 = (-B - det) / A;
+ const F32 t2 = (-B + det) / A;
+ const F32 z1 = V0.mV[2] + t1 * V.mV[2];
+ const F32 z2 = V0.mV[2] + t2 * V.mV[2];
+ if (z1 * cos_max_angle < 0)
+ {
+ return t2;
+ }
+ else if (z2 * cos_max_angle < 0)
+ {
+ return t1;
+ }
+ else if ((t1 < 0) || (t1 > 1))
+ {
+ return t2;
+ }
+ else
+ {
+ return t1;
+ }
+}
+
+
+void LLVOSky::updateSunHaloGeometry(LLDrawable *drawable )
+{
+ const LLVector3* v_corner = mSun.corners();
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face;
+
+ const LLVector3 right = 2 * (v_corner[2] - v_corner[0]);
+ LLVector3 up = 2 * (v_corner[2] - v_corner[3]);
+ up.normVec();
+ F32 size = right.magVec();
+ up = size * up;
+ const LLVector3 draw_pos = 0.25 * (v_corner[0] + v_corner[1] + v_corner[2] + v_corner[3]);
+
+ LLVector3 v_glow_corner[4];
+
+ v_glow_corner[0] = draw_pos - right + up;
+ v_glow_corner[1] = draw_pos - right - up;
+ v_glow_corner[2] = draw_pos + right + up;
+ v_glow_corner[3] = draw_pos + right - up;
+
+ face = mFace[FACE_BLOOM];
+ face->setPrimType(LLTriangles);
+ face->setSize(4, 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ *(verticesp++) = v_glow_corner[vtx] + mCameraPosAgent;
+ }
+
+ *(texCoordsp++) = TEX01;
+ *(texCoordsp++) = TEX00;
+ *(texCoordsp++) = TEX11;
+ *(texCoordsp++) = TEX10;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+}
+
+
+F32 dtReflection(const LLVector3& p, F32 cos_dir_from_top, F32 sin_dir_from_top, F32 diff_angl_dir)
+{
+ LLVector3 P = p;
+ P.normVec();
+
+ const F32 cos_dir_angle = -P.mV[VZ];
+ const F32 sin_dir_angle = sqrt(1 - cos_dir_angle * cos_dir_angle);
+
+ F32 cos_diff_angles = cos_dir_angle * cos_dir_from_top
+ + sin_dir_angle * sin_dir_from_top;
+
+ F32 diff_angles;
+ if (cos_diff_angles > (1 - 1e-7))
+ diff_angles = 0;
+ else
+ diff_angles = acos(cos_diff_angles);
+
+ const F32 rel_diff_angles = diff_angles / diff_angl_dir;
+ const F32 dt = 1 - rel_diff_angles;
+
+ return (dt < 0) ? 0 : dt;
+}
+
+
+F32 dtClip(const LLVector3& v0, const LLVector3& v1, F32 far_clip2)
+{
+ F32 dt_clip;
+ const LLVector3 otrezok = v1 - v0;
+ const F32 A = otrezok.magVecSquared();
+ const F32 B = v0 * otrezok;
+ const F32 C = v0.magVecSquared() - far_clip2;
+ const F32 det = sqrt(B*B - A*C);
+ dt_clip = (-B - det) / A;
+ if ((dt_clip < 0) || (dt_clip > 1))
+ dt_clip = (-B + det) / A;
+ return dt_clip;
+}
+
+
+void LLVOSky::updateReflectionGeometry(LLDrawable *drawable, F32 H,
+ const LLHeavenBody& HB)
+{
+ const LLVector3 &look_at = gCamera->getAtAxis();
+ // const F32 water_height = gAgent.getRegion()->getWaterHeight() + 0.001f;
+ // gWorldPointer->getWaterHeight() + 0.001f;
+
+ LLVector3 to_dir = HB.getDirection();
+ LLVector3 hb_pos = to_dir * (HORIZON_DIST - 10);
+ LLVector3 to_dir_proj = to_dir;
+ to_dir_proj.mV[VZ] = 0;
+ to_dir_proj.normVec();
+
+ LLVector3 Right = to_dir % LLVector3::z_axis;
+ LLVector3 Up = Right % to_dir;
+ Right.normVec();
+ Up.normVec();
+
+ // finding angle between look direction and sprite.
+ LLVector3 look_at_right = look_at % LLVector3::z_axis;
+ look_at_right.normVec();
+
+ const static F32 cos_horizon_angle = cosHorizon(0.0f/sResolution);
+ //const static F32 horizon_angle = acos(cos_horizon_angle);
+
+ const F32 enlargm_factor = ( 1 - to_dir.mV[2] );
+ F32 horiz_enlargement = 1 + enlargm_factor * 0.3f;
+ F32 vert_enlargement = 1 + enlargm_factor * 0.2f;
+
+ F32 vert_size = vert_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
+ Right *= /*cos_lookAt_toDir */ horiz_enlargement * HEAVENLY_BODY_SCALE * HB.getDiskRadius();
+ Up *= vert_size;
+
+ LLVector3 v_corner[2];
+ LLVector3 stretch_corner[2];
+
+ LLVector3 top_hb = v_corner[0] = stretch_corner[0] = hb_pos - Right + Up;
+ v_corner[1] = stretch_corner[1] = hb_pos - Right - Up;
+
+ F32 dt_hor, dt;
+ dt_hor = clip_side_to_horizon(v_corner[1], v_corner[0], cos_horizon_angle);
+
+ LLVector2 TEX0t = TEX00;
+ LLVector2 TEX1t = TEX10;
+ LLVector3 lower_corner = v_corner[1];
+
+ if ((dt_hor > 0) && (dt_hor < 1))
+ {
+ TEX0t = LLVector2(0, dt_hor);
+ TEX1t = LLVector2(1, dt_hor);
+ lower_corner = (1 - dt_hor) * v_corner[1] + dt_hor * v_corner[0];
+ }
+ else
+ dt_hor = llmax(0.0f, llmin(1.0f, dt_hor));
+
+ top_hb.normVec();
+ const F32 cos_angle_of_view = fabs(top_hb.mV[VZ]);
+ const F32 extension = llmin (5.0f, 1.0f / cos_angle_of_view);
+
+ const S32 cols = 1;
+ const S32 raws = lltrunc(16 * extension);
+ S32 quads = cols * raws;
+
+ stretch_corner[0] = lower_corner + extension * (stretch_corner[0] - lower_corner);
+ stretch_corner[1] = lower_corner + extension * (stretch_corner[1] - lower_corner);
+
+ dt = dt_hor;
+
+
+ F32 cos_dir_from_top[2];
+
+ LLVector3 dir = stretch_corner[0];
+ dir.normVec();
+ cos_dir_from_top[0] = dir.mV[VZ];
+
+ dir = stretch_corner[1];
+ dir.normVec();
+ cos_dir_from_top[1] = dir.mV[VZ];
+
+ const F32 sin_dir_from_top = sqrt(1 - cos_dir_from_top[0] * cos_dir_from_top[0]);
+ const F32 sin_dir_from_top2 = sqrt(1 - cos_dir_from_top[1] * cos_dir_from_top[1]);
+ const F32 cos_diff_dir = cos_dir_from_top[0] * cos_dir_from_top[1]
+ + sin_dir_from_top * sin_dir_from_top2;
+ const F32 diff_angl_dir = acos(cos_diff_dir);
+
+ v_corner[0] = stretch_corner[0];
+ v_corner[1] = lower_corner;
+
+
+ LLVector2 TEX0tt = TEX01;
+ LLVector2 TEX1tt = TEX11;
+
+ LLVector3 v_refl_corner[4];
+ LLVector3 v_sprite_corner[4];
+
+ S32 vtx;
+ for (vtx = 0; vtx < 2; ++vtx)
+ {
+ LLVector3 light_proj = v_corner[vtx];
+ light_proj.normVec();
+
+ const F32 z = light_proj.mV[VZ];
+ const F32 sin_angle = sqrt(1 - z * z);
+ light_proj *= 1.f / sin_angle;
+ light_proj.mV[VZ] = 0;
+ const F32 to_refl_point = H * sin_angle / fabs(z);
+
+ v_refl_corner[vtx] = to_refl_point * light_proj;
+ }
+
+
+ for (vtx = 2; vtx < 4; ++vtx)
+ {
+ const LLVector3 to_dir_vec = (to_dir_proj * v_refl_corner[vtx-2]) * to_dir_proj;
+ v_refl_corner[vtx] = v_refl_corner[vtx-2] + 2 * (to_dir_vec - v_refl_corner[vtx-2]);
+ }
+
+ for (vtx = 0; vtx < 4; ++vtx)
+ v_refl_corner[vtx].mV[VZ] -= H;
+
+ S32 side = 0;
+ LLVector3 refl_corn_norm[2];
+ refl_corn_norm[0] = v_refl_corner[1];
+ refl_corn_norm[0].normVec();
+ refl_corn_norm[1] = v_refl_corner[3];
+ refl_corn_norm[1].normVec();
+
+ F32 cos_refl_look_at[2];
+ cos_refl_look_at[0] = refl_corn_norm[0] * look_at;
+ cos_refl_look_at[1] = refl_corn_norm[1] * look_at;
+
+ if (cos_refl_look_at[1] > cos_refl_look_at[0])
+ {
+ side = 2;
+ }
+
+ //const F32 far_clip = (gCamera->getFar() - 0.01) / far_clip_factor;
+ const F32 far_clip = 512;
+ const F32 far_clip2 = far_clip*far_clip;
+
+ F32 dt_clip;
+ F32 vtx_near2, vtx_far2;
+
+ if ((vtx_far2 = v_refl_corner[side].magVecSquared()) > far_clip2)
+ {
+ // whole thing is sprite: reflection is beyond far clip plane.
+ dt_clip = 1.1f;
+ quads = 1;
+ }
+ else if ((vtx_near2 = v_refl_corner[side+1].magVecSquared()) > far_clip2)
+ {
+ // part is reflection, the rest is sprite.
+ dt_clip = dtClip(v_refl_corner[side + 1], v_refl_corner[side], far_clip2);
+ const LLVector3 P = (1 - dt_clip) * v_refl_corner[side + 1] + dt_clip * v_refl_corner[side];
+
+ F32 dt_tex = dtReflection(P, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
+
+ dt = dt_tex;
+ TEX0tt = LLVector2(0, dt);
+ TEX1tt = LLVector2(1, dt);
+ quads++;
+ }
+ else
+ {
+ // whole thing is correct reflection.
+ dt_clip = -0.1f;
+ }
+
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+ LLFace *face = mFace[FACE_REFLECTION];
+
+ face->setPrimType(LLTriangles);
+ face->setSize(quads * 4, quads * 6);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return;
+ }
+
+ LLColor3 hb_col3 = HB.getInterpColor();
+ hb_col3.clamp();
+ const LLColor4 hb_col = LLColor4(hb_col3);
+
+ const F32 min_attenuation = 0.4f;
+ const F32 max_attenuation = 0.7f;
+ const F32 attenuation = min_attenuation
+ + cos_angle_of_view * (max_attenuation - min_attenuation);
+
+ LLColor4 hb_refl_col = (1-attenuation) * hb_col + attenuation * mFogColor;
+ face->setFaceColor(hb_refl_col);
+
+ LLVector3 v_far[2];
+ v_far[0] = v_refl_corner[1];
+ v_far[1] = v_refl_corner[3];
+
+ if(dt_clip > 0)
+ {
+ if (dt_clip >= 1)
+ {
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ F32 ratio = far_clip / v_refl_corner[vtx].magVec();
+ *(verticesp++) = v_refl_corner[vtx] = ratio * v_refl_corner[vtx] + mCameraPosAgent;
+ }
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+ else
+ {
+ F32 ratio = far_clip / v_refl_corner[1].magVec();
+ v_sprite_corner[1] = v_refl_corner[1] * ratio;
+
+ ratio = far_clip / v_refl_corner[3].magVec();
+ v_sprite_corner[3] = v_refl_corner[3] * ratio;
+
+ v_refl_corner[1] = (1 - dt_clip) * v_refl_corner[1] + dt_clip * v_refl_corner[0];
+ v_refl_corner[3] = (1 - dt_clip) * v_refl_corner[3] + dt_clip * v_refl_corner[2];
+ v_sprite_corner[0] = v_refl_corner[1];
+ v_sprite_corner[2] = v_refl_corner[3];
+
+ for (S32 vtx = 0; vtx < 4; ++vtx)
+ {
+ *(verticesp++) = v_sprite_corner[vtx] + mCameraPosAgent;
+ }
+
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_sprite_corner[1] + v_refl_corner[2] + v_sprite_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+
+ *(texCoordsp++) = TEX0tt;
+ *(texCoordsp++) = TEX0t;
+ *(texCoordsp++) = TEX1tt;
+ *(texCoordsp++) = TEX1t;
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ index_offset += 4;
+ }
+
+ if (dt_clip < 1)
+ {
+ if (dt_clip <= 0)
+ {
+ const LLVector3 draw_pos = 0.25 *
+ (v_refl_corner[0] + v_refl_corner[1] + v_refl_corner[2] + v_refl_corner[3]);
+ face->mCenterAgent = draw_pos;
+ }
+
+ const F32 raws_inv = 1.f/raws;
+ const F32 cols_inv = 1.f/cols;
+ LLVector3 left = v_refl_corner[0] - v_refl_corner[1];
+ LLVector3 right = v_refl_corner[2] - v_refl_corner[3];
+ left *= raws_inv;
+ right *= raws_inv;
+
+ F32 dt_raw = dt;
+
+ for (S32 raw = 0; raw < raws; ++raw)
+ {
+ F32 dt_v0 = raw * raws_inv;
+ F32 dt_v1 = (raw + 1) * raws_inv;
+ const LLVector3 BL = v_refl_corner[1] + (F32)raw * left;
+ const LLVector3 BR = v_refl_corner[3] + (F32)raw * right;
+ const LLVector3 EL = BL + left;
+ const LLVector3 ER = BR + right;
+ dt_v0 = dt_raw;
+ dt_raw = dt_v1 = dtReflection(EL, cos_dir_from_top[0], sin_dir_from_top, diff_angl_dir);
+ for (S32 col = 0; col < cols; ++col)
+ {
+ F32 dt_h0 = col * cols_inv;
+ *(verticesp++) = (1 - dt_h0) * EL + dt_h0 * ER + mCameraPosAgent;
+ *(verticesp++) = (1 - dt_h0) * BL + dt_h0 * BR + mCameraPosAgent;
+ F32 dt_h1 = (col + 1) * cols_inv;
+ *(verticesp++) = (1 - dt_h1) * EL + dt_h1 * ER + mCameraPosAgent;
+ *(verticesp++) = (1 - dt_h1) * BL + dt_h1 * BR + mCameraPosAgent;
+
+ *(texCoordsp++) = LLVector2(dt_h0, dt_v1);
+ *(texCoordsp++) = LLVector2(dt_h0, dt_v0);
+ *(texCoordsp++) = LLVector2(dt_h1, dt_v1);
+ *(texCoordsp++) = LLVector2(dt_h1, dt_v0);
+
+ *indicesp++ = index_offset + 0;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 1;
+
+ *indicesp++ = index_offset + 1;
+ *indicesp++ = index_offset + 2;
+ *indicesp++ = index_offset + 3;
+
+ index_offset += 4;
+ }
+ }
+ }
+}
+
+
+
+
+void LLVOSky::updateFog(const F32 distance)
+{
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG))
+ {
+ /*gGLSFog.addCap(GL_FOG, FALSE);
+ gGLSPipeline.addCap(GL_FOG, FALSE);
+ gGLSPipelineAlpha.addCap(GL_FOG, FALSE);
+ gGLSPipelinePixieDust.addCap(GL_FOG, FALSE);
+ gGLSPipelineSelection.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatar.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, FALSE);
+ gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, FALSE);*/
+ glFogf(GL_FOG_DENSITY, 0);
+ glFogfv(GL_FOG_COLOR, (F32 *) &LLColor4::white.mV);
+ glFogf(GL_FOG_END, 1000000.f);
+ return;
+ }
+ else
+ {
+ /*gGLSFog.addCap(GL_FOG, TRUE);
+ gGLSPipeline.addCap(GL_FOG, TRUE);
+ gGLSPipelineAlpha.addCap(GL_FOG, TRUE);
+ gGLSPipelinePixieDust.addCap(GL_FOG, TRUE);
+ gGLSPipelineSelection.addCap(GL_FOG, TRUE);
+ if (!gGLManager.mIsATI)
+ {
+ gGLSPipelineAvatar.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaOnePass.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass1.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass2.addCap(GL_FOG, TRUE);
+ gGLSPipelineAvatarAlphaPass3.addCap(GL_FOG, TRUE);
+ }*/
+ }
+
+ const BOOL hide_clip_plane = TRUE;
+ LLColor4 target_fog(0.f, 0.2f, 0.5f, 0.f);
+
+ const F32 water_height = gAgent.getRegion()->getWaterHeight();
+ // gWorldPointer->getWaterHeight();
+ F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
+
+ F32 near_clip_height = gCamera->getAtAxis().mV[VZ] * gCamera->getNear();
+ camera_height += near_clip_height;
+
+ F32 fog_distance = 0.f;
+ LLColor3 res_color[3];
+
+ LLColor3 sky_fog_color = LLColor3::white;
+ LLColor3 render_fog_color = LLColor3::white;
+
+ LLColor3 transp;
+ LLVector3 tosun = getToSunLast();
+ const F32 tosun_z = tosun.mV[VZ];
+ tosun.mV[VZ] = 0.f;
+ tosun.normVec();
+ LLVector3 perp_tosun;
+ perp_tosun.mV[VX] = -tosun.mV[VY];
+ perp_tosun.mV[VY] = tosun.mV[VX];
+ LLVector3 tosun_45 = tosun + perp_tosun;
+ tosun_45.normVec();
+
+ F32 delta = 0.06f;
+ tosun.mV[VZ] = delta;
+ perp_tosun.mV[VZ] = delta;
+ tosun_45.mV[VZ] = delta;
+ tosun.normVec();
+ perp_tosun.normVec();
+ tosun_45.normVec();
+
+ // Sky colors, just slightly above the horizon in the direction of the sun, perpendicular to the sun, and at a 45 degree angle to the sun.
+ calcSkyColorInDir(res_color[0],transp, tosun);
+ calcSkyColorInDir(res_color[1],transp, perp_tosun);
+ calcSkyColorInDir(res_color[2],transp, tosun_45);
+
+ sky_fog_color = color_norm(res_color[0] + res_color[1] + res_color[2]);
+
+ F32 full_off = -0.25f;
+ F32 full_on = 0.00f;
+ F32 on = (tosun_z - full_off) / (full_on - full_off);
+ on = llclamp(on, 0.01f, 1.f);
+ sky_fog_color *= 0.5f * on;
+
+
+ // We need to clamp these to non-zero, in order for the gamma correction to work. 0^y = ???
+ S32 i;
+ for (i = 0; i < 3; i++)
+ {
+ sky_fog_color.mV[i] = llmax(0.0001f, sky_fog_color.mV[i]);
+ }
+
+ color_gamma_correct(sky_fog_color);
+
+ if (!(gPipeline.getVertexShaderLevel(LLPipeline::SHADER_ENVIRONMENT) > LLDrawPool::SHADER_LEVEL_SCATTERING))
+ {
+ render_fog_color = sky_fog_color;
+ }
+
+ if (camera_height > water_height)
+ {
+ fog_distance = mFogRatio * distance;
+ LLColor4 fog(render_fog_color);
+ glFogfv(GL_FOG_COLOR, fog.mV);
+ }
+ else
+ {
+ // Interpolate between sky fog and water fog...
+ F32 depth = water_height - camera_height;
+ F32 depth_frac = 1.f/(1.f + 200.f*depth);
+ F32 color_frac = 1.f/(1.f + 0.5f* depth)* 0.2f;
+ fog_distance = (mFogRatio * distance) * depth_frac + 30.f * (1.f-depth_frac);
+ fog_distance = llmin(75.f, fog_distance);
+
+ F32 brightness = 1.f/(1.f + 0.05f*depth);
+ F32 sun_brightness = getSunDiffuseColor().magVec() * 0.3f;
+ brightness = llmin(1.f, brightness);
+ brightness = llmin(brightness, sun_brightness);
+ color_frac = llmin(0.7f, color_frac);
+
+ LLColor4 fogCol = brightness * (color_frac * render_fog_color + (1.f - color_frac) * LLColor4(0.f, 0.2f, 0.3f, 1.f));
+ fogCol.setAlpha(1);
+ glFogfv(GL_FOG_COLOR, (F32 *) &fogCol.mV);
+ }
+
+ mFogColor = sky_fog_color;
+ mFogColor.setAlpha(1);
+ LLGLSFog gls_fog;
+
+ F32 fog_density;
+ if (hide_clip_plane)
+ {
+ // For now, set the density to extend to the cull distance.
+ const F32 f_log = 2.14596602628934723963618357029f; // sqrt(fabs(log(0.01f)))
+ fog_density = f_log/fog_distance;
+ glFogi(GL_FOG_MODE, GL_EXP2);
+ }
+ else
+ {
+ const F32 f_log = 4.6051701859880913680359829093687f; // fabs(log(0.01f))
+ fog_density = (f_log)/fog_distance;
+ glFogi(GL_FOG_MODE, GL_EXP);
+ }
+
+ glFogf(GL_FOG_END, fog_distance*2.2f);
+
+ glFogf(GL_FOG_DENSITY, fog_density);
+
+ glHint(GL_FOG_HINT, GL_NICEST);
+ stop_glerror();
+}
+
+// static
+void LLHaze::initClass()
+{
+ sAirScaSeaLevel = LLHaze::calcAirScaSeaLevel();
+}
+
+
+
+// Functions used a lot.
+
+
+F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply)
+{
+ F32 mv = color_max(col);
+ if (0 == mv)
+ {
+ return 0;
+ }
+
+ col *= 1.f / mv;
+ color_pow(col, e);
+ if (postmultiply)
+ {
+ col *= mv;
+ }
+ return mv;
+}
+
+// Returns angle (RADIANs) between the horizontal projection of "v" and the x_axis.
+// Range of output is 0.0f to 2pi //359.99999...f
+// Returns 0.0f when "v" = +/- z_axis.
+F32 azimuth(const LLVector3 &v)
+{
+ F32 azimuth = 0.0f;
+ if (v.mV[VX] == 0.0f)
+ {
+ if (v.mV[VY] > 0.0f)
+ {
+ azimuth = F_PI * 0.5f;
+ }
+ else if (v.mV[VY] < 0.0f)
+ {
+ azimuth = F_PI * 1.5f;// 270.f;
+ }
+ }
+ else
+ {
+ azimuth = (F32) atan(v.mV[VY] / v.mV[VX]);
+ if (v.mV[VX] < 0.0f)
+ {
+ azimuth += F_PI;
+ }
+ else if (v.mV[VY] < 0.0f)
+ {
+ azimuth += F_PI * 2;
+ }
+ }
+ return azimuth;
+}
+
+
+#if 0
+// Not currently used
+LLColor3 LLVOSky::calcGroundFog(LLColor3& transp, const LLVector3 &view_dir, F32 obj_dist) const
+{
+ LLColor3 col;
+ calcGroundFog(col, transp, view_dir, obj_dist);
+ col *= mBrightnessScaleGuess;
+ return col;
+}
+#endif
+
+void LLVOSky::setSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity)
+{
+ LLVector3 sun_direction = (sun_dir.magVec() == 0) ? LLVector3::x_axis : sun_dir;
+ sun_direction.normVec();
+ F32 dp = mSun.getDirection() * sun_direction;
+ mSun.setDirection(sun_direction);
+ mSun.setAngularVelocity(sun_ang_velocity);
+ mMoon.setDirection(-sun_direction);
+ if (dp < 0.995f) { //the sun jumped a great deal, update immediately
+ updateHaze();
+ mWeatherChange = FALSE;
+ mForceUpdate = TRUE;
+ }
+ else if (mWeatherChange && (mSun.getDirection().mV[VZ] > -0.5) )
+ {
+ updateHaze();
+ init();
+ mWeatherChange = FALSE;
+ }
+ else if (mSun.getDirection().mV[VZ] < -0.5)
+ {
+ mWeatherChange = TRUE;
+ }
+}
+
+#define INV_WAVELENGTH_R_POW4 (1.f/0.2401f) // = 1/0.7^4
+#define INV_WAVELENGTH_G_POW4 (1.f/0.0789f) // = 1/0.53^4
+#define INV_WAVELENGTH_B_POW4 (1.f/0.03748f) // = 1/0.44^4
+
+// Dummy class for globals used below. Replace when KILLERSKY is merged in.
+class LLKillerSky
+{
+public:
+ static F32 sRaleighGroundDensity;
+ static F32 sMieFactor;
+ static F32 sNearFalloffFactor;
+ static F32 sSkyContrib;
+
+ static void getRaleighCoefficients(float eye_sun_dp, float density, float *coefficients)
+ {
+ float dp = eye_sun_dp;
+ float angle_dep = density*(1 + dp*dp);
+ coefficients[0] = angle_dep * INV_WAVELENGTH_R_POW4;
+ coefficients[1] = angle_dep * INV_WAVELENGTH_G_POW4;
+ coefficients[2] = angle_dep * INV_WAVELENGTH_B_POW4;
+ }
+
+ static void getMieCoefficients(float eye_sun_dp, float density, float *coefficient)
+ {
+ // TOTALLY ARBITRARY FUNCTION. Seems to work though
+ // If anyone can replace this with some *actual* mie function, that'd be great
+ float dp = eye_sun_dp;
+ float dp_highpower = dp*dp;
+ float angle_dep = density * (llclamp(dp_highpower*dp, 0.f, 1.f) + 0.4f);
+ *coefficient = angle_dep;
+ }
+};
+
+F32 LLKillerSky::sRaleighGroundDensity = 0.013f;
+F32 LLKillerSky::sMieFactor = 50;
+F32 LLKillerSky::sNearFalloffFactor = 1.5f;
+F32 LLKillerSky::sSkyContrib = 0.06f;
+
+void LLVOSky::generateScatterMap()
+{
+ float raleigh[3], mie;
+
+ mScatterMap = new LLImageGL(FALSE);
+ mScatterMapRaw = new LLImageRaw(256, 256, 4);
+ U8 *data = mScatterMapRaw->getData();
+
+ F32 light_brightness = gSky.getSunDirection().mV[VZ]+0.1f;
+ LLColor4 light_color;
+ LLColor4 sky_color;
+ if (light_brightness > 0)
+ {
+ F32 interp = sqrtf(light_brightness);
+ light_brightness = sqrt(sqrtf(interp));
+ light_color = lerp(gSky.getSunDiffuseColor(), LLColor4(1,1,1,1), interp) * light_brightness;
+ sky_color = lerp(LLColor4(0,0,0,0), LLColor4(0.4f, 0.6f, 1.f, 1.f), light_brightness)*LLKillerSky::sSkyContrib;
+ }
+ else
+ {
+ light_brightness = /*0.3f*/sqrt(-light_brightness);
+ light_color = gSky.getMoonDiffuseColor() * light_brightness;
+ sky_color = LLColor4(0,0,0,1);
+ }
+
+ // x = distance [0..1024m]
+ // y = dot product [-1,1]
+ for (int y=0;y<256;y++)
+ {
+ // Accumulate outward
+ float accum_r = 0, accum_g = 0, accum_b = 0;
+
+ float dp = (((float)y)/255.f)*1.95f - 0.975f;
+ U8 *scanline = &data[y*256*4];
+ for (int x=0;x<256;x++)
+ {
+ float dist = ((float)x+1)*4; // x -> 2048
+
+ float raleigh_density = LLKillerSky::sRaleighGroundDensity * 0.05f; // Arbitrary? Perhaps...
+ float mie_density = raleigh_density*LLKillerSky::sMieFactor;
+
+ float extinction_factor = dist/LLKillerSky::sNearFalloffFactor;
+
+ LLKillerSky::getRaleighCoefficients(dp, raleigh_density, raleigh);
+ LLKillerSky::getMieCoefficients(dp, mie_density, &mie);
+
+ float falloff_r = pow(llclamp(0.985f-raleigh[0],0.f,1.f), extinction_factor);
+ float falloff_g = pow(llclamp(0.985f-raleigh[1],0.f,1.f), extinction_factor);
+ float falloff_b = pow(llclamp(0.985f-raleigh[2],0.f,1.f), extinction_factor);
+
+ float light_r = light_color.mV[0] * (raleigh[0]+mie+sky_color.mV[0]) * falloff_r;
+ float light_g = light_color.mV[1] * (raleigh[1]+mie+sky_color.mV[1]) * falloff_g;
+ float light_b = light_color.mV[2] * (raleigh[2]+mie+sky_color.mV[2]) * falloff_b;
+
+ accum_r += light_r;
+ accum_g += light_g;
+ accum_b += light_b;
+
+ scanline[x*4] = (U8)llclamp(accum_r*255.f, 0.f, 255.f);
+ scanline[x*4+1] = (U8)llclamp(accum_g*255.f, 0.f, 255.f);
+ scanline[x*4+2] = (U8)llclamp(accum_b*255.f, 0.f, 255.f);
+ float alpha = ((falloff_r+falloff_g+falloff_b)*0.33f);
+ scanline[x*4+3] = (U8)llclamp(alpha*255.f, 0.f, 255.f); // Avg falloff
+
+ // Output color Co, Input color Ci, Map channels Mrgb, Ma:
+ // Co = (Ci * Ma) + Mrgb
+ }
+ }
+
+ mScatterMap->createGLTexture(0, mScatterMapRaw);
+ mScatterMap->bind(0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+#if 0
+// Not currently used
+void LLVOSky::calcGroundFog(LLColor3& res, LLColor3& transp, const LLVector3 view_dir, F32 obj_dist) const
+{
+ const LLVector3& tosun = getToSunLast();//use_old_value ? sunDir() : toSunLast();
+ res.setToBlack();
+ transp.setToWhite();
+ const F32 dist = obj_dist * mWorldScale;
+
+ //LLVector3 view_dir = gCamera->getAtAxis();
+
+ const F32 cos_dir = view_dir * tosun;
+ LLVector3 dir = view_dir;
+ LLVector3 virtual_P = mCameraPosAgent + dist * dir;
+
+ if (dir.mV[VZ] < 0)
+ {
+ dir = -dir;
+ }
+
+ const F32 z_dir = dir.mV[2];
+
+ const F32 h = mCameraPosAgent.mV[2];
+
+ transp = color_div(mTransp.calcTransp(dir * calcUpVec(virtual_P), 0),
+ mTransp.calcTransp(z_dir, h));
+
+ if (calcHitsEarth(mCameraPosAgent, tosun) > 0)
+ {
+ const F32 avg = llmin(1.f, 1.2f * color_avg(transp));
+ transp = LLColor3(avg, avg, avg);
+ return;
+ }
+
+ LLColor3 haze_sca_opt_depth = mHaze.getSigSca();
+ LLColor3 sun_transp;
+ mTransp.calcTransp(tosun.mV[2], -0.1f, sun_transp);
+
+ res = calcAirPhaseFunc(cos_dir) * LLHaze::getAirScaSeaLevel();
+ res += mHaze.calcPhase(cos_dir) * mHaze.getSigSca();
+ res = mSun.getIntensity() * dist * sun_transp * res;
+}
+
+#endif
diff --git a/indra/newview/llvosky.h b/indra/newview/llvosky.h
new file mode 100644
index 0000000000..1c819ebcd8
--- /dev/null
+++ b/indra/newview/llvosky.h
@@ -0,0 +1,907 @@
+/**
+ * @file llvosky.h
+ * @brief LLVOSky class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOSKY_H
+#define LL_LLVOSKY_H
+
+#include "stdtypes.h"
+#include "v3color.h"
+#include "v4coloru.h"
+#include "llviewerimage.h"
+#include "llviewerobject.h"
+#include "llframetimer.h"
+
+
+//////////////////////////////////
+//
+// Lots of constants
+//
+// Will clean these up at some point...
+//
+
+const F32 HORIZON_DIST = 1024.0f;
+const F32 HEAVENLY_BODY_DIST = HORIZON_DIST - 10.f;
+const F32 HEAVENLY_BODY_FACTOR = 0.1f;
+const F32 HEAVENLY_BODY_SCALE = HEAVENLY_BODY_DIST * HEAVENLY_BODY_FACTOR;
+const F32 EARTH_RADIUS = 6.4e6f; // exact radius = 6.37 x 10^6 m
+const F32 ATM_EXP_FALLOFF = 0.000126f;
+const F32 ATM_SEA_LEVEL_NDENS = 2.55e25f;
+// Somewhat arbitrary:
+const F32 ATM_HEIGHT = 100000.f;
+
+const F32 FIRST_STEP = 5000.f;
+const F32 INV_FIRST_STEP = 1.f/FIRST_STEP;
+const S32 NO_STEPS = 15;
+const F32 INV_NO_STEPS = 1.f/NO_STEPS;
+
+
+// constants used in calculation of scattering coeff of clear air
+const F32 sigma = 0.035f;
+const F32 fsigma = (6+3*sigma)/(6.f-7.f*sigma);
+const F64 Ndens = 2.55e25;
+const F64 Ndens2 = Ndens*Ndens;
+
+// !!! FIXME: This #define should be in llcommon somewhere...
+#ifdef __GNUC__
+#define __forceinline inline __attribute__((always_inline))
+#endif
+
+__forceinline LLColor3 color_div(const LLColor3 &col1, const LLColor3 &col2)
+{
+ return LLColor3(
+ col1.mV[0] / col2.mV[0],
+ col1.mV[1] / col2.mV[1],
+ col1.mV[2] / col2.mV[2] );
+}
+
+LLColor3 color_norm(const LLColor3 &col);
+LLVector3 move_vec (const LLVector3& v, F32 cos_max_angle);
+BOOL clip_quad_to_horizon(F32& t_left, F32& t_right, LLVector3 v_clipped[4],
+ const LLVector3 v_corner[4], const F32 cos_max_angle);
+F32 clip_side_to_horizon(const LLVector3& v0, const LLVector3& v1, const F32 cos_max_angle);
+
+inline F32 color_intens ( const LLColor3 &col )
+{
+ return col.mV[0] + col.mV[1] + col.mV[2];
+}
+
+inline F32 color_max(const LLColor3 &col)
+{
+ return llmax(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+inline F32 color_max(const LLColor4 &col)
+{
+ return llmax(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+
+inline F32 color_min(const LLColor3 &col)
+{
+ return llmin(col.mV[0], col.mV[1], col.mV[2]);
+}
+
+inline LLColor3 color_norm_abs(const LLColor3 &col)
+{
+ const F32 m = color_max(col);
+ if (m > 1e-6)
+ {
+ return 1.f/m * col;
+ }
+ else return col;
+}
+
+
+
+class LLFace;
+class LLHaze;
+
+
+class LLSkyTex
+{
+ friend class LLVOSky;
+private:
+ static S32 sResolution;
+ static S32 sComponents;
+ LLPointer<LLImageGL> mImageGL[2];
+ LLPointer<LLImageRaw> mImageRaw[2];
+ LLColor3 *mSkyData;
+ LLVector3 *mSkyDirs; // Cache of sky direction vectors
+ static S32 sCurrent;
+ static F32 sInterpVal;
+
+public:
+ static F32 getInterpVal() { return sInterpVal; }
+ static void setInterpVal(const F32 v) { sInterpVal = v; }
+ static BOOL doInterpolate() { return sInterpVal > 0.001; }
+
+ void bindTexture(BOOL curr = TRUE);
+
+protected:
+ LLSkyTex();
+ void init();
+ void cleanupGL();
+ void restoreGL();
+
+ ~LLSkyTex();
+
+
+ static S32 getResolution() { return sResolution; }
+ static S32 getCurrent() { return sCurrent; }
+ static S32 stepCurrent() { return (sCurrent = ++sCurrent % 2); }
+ static S32 getNext() { return ((sCurrent+1) % 2); }
+ static S32 getWhich(const BOOL curr) { return curr ? sCurrent : getNext(); }
+
+ void initEmpty(const S32 tex);
+ void create(F32 brightness_scale, const LLColor3& multiscatt);
+
+ void setDir(const LLVector3 &dir, const S32 i, const S32 j)
+ {
+ S32 offset = i * sResolution + j;
+ mSkyDirs[offset] = dir;
+ }
+
+ const LLVector3 &getDir(const S32 i, const S32 j) const
+ {
+ S32 offset = i * sResolution + j;
+ return mSkyDirs[offset];
+ }
+
+ void setPixel(const LLColor3 &col, const S32 i, const S32 j)
+ {
+ S32 offset = i * sResolution + j;
+ mSkyData[offset] = col;
+ }
+
+ void setPixel(const LLColor4U &col, const S32 i, const S32 j)
+ {
+ S32 offset = (i * sResolution + j) * sComponents;
+ U32* pix = (U32*) &(mImageRaw[sCurrent]->getData()[offset]);
+ *pix = col.mAll;
+ }
+
+ LLColor4U getPixel(const S32 i, const S32 j)
+ {
+ LLColor4U col;
+ S32 offset = (i * sResolution + j) * sComponents;
+ U32* pix = (U32*) &(mImageRaw[sCurrent]->getData()[offset]);
+ col.mAll = *pix;
+ return col;
+ }
+
+ LLImageRaw* getImageRaw(BOOL curr=TRUE) { return mImageRaw[getWhich(curr)]; }
+ void createTexture(BOOL curr=TRUE);
+};
+
+
+class LLHeavenBody
+{
+protected:
+ LLVector3 mDirectionCached; // hack for events that shouldn't happen every frame
+
+ LLColor3 mColor;
+ LLColor3 mColorCached;
+ F32 mIntensity;
+ LLVector3 mDirection; // direction of the local heavenly body
+ LLVector3 mAngularVelocity; // velocity of the local heavenly body
+
+ F32 mDiskRadius;
+ BOOL mDraw; // FALSE - do not draw.
+ F32 mHorizonVisibility; // number [0, 1] due to how horizon
+ F32 mVisibility; // same but due to other objects being in frong.
+ BOOL mVisible;
+ static F32 sInterpVal;
+ LLVector3 mQuadCorner[4];
+ LLVector3 mU;
+ LLVector3 mV;
+ LLVector3 mO;
+
+public:
+ LLHeavenBody(const F32 rad) :
+ mDirectionCached(LLVector3(0,0,0)), mDirection(LLVector3(0,0,0)),
+ mDiskRadius(rad), mDraw(FALSE),
+ mHorizonVisibility(1), mVisibility(1)
+
+ {
+ mColor.setToBlack();
+ mColorCached.setToBlack();
+ }
+ ~LLHeavenBody() {}
+
+ const LLVector3& getDirection() const { return mDirection; }
+ void setDirection(const LLVector3 &direction) { mDirection = direction; }
+ void setAngularVelocity(const LLVector3 &ang_vel) { mAngularVelocity = ang_vel; }
+ const LLVector3& getAngularVelocity() const { return mAngularVelocity; }
+
+ const LLVector3& getDirectionCached() const { return mDirectionCached; }
+ void renewDirection() { mDirectionCached = mDirection; }
+
+ const LLColor3& getColorCached() const { return mColorCached; }
+ void setColorCached(const LLColor3& c) { mColorCached = c; }
+ const LLColor3& getColor() const { return mColor; }
+ void setColor(const LLColor3& c) { mColor = c; }
+
+ void renewColor() { mColorCached = mColor; }
+
+ static F32 interpVal() { return sInterpVal; }
+ static void setInterpVal(const F32 v) { sInterpVal = v; }
+
+ LLColor3 getInterpColor() const
+ {
+ return sInterpVal * mColor + (1 - sInterpVal) * mColorCached;
+ }
+
+// LLColor3 getDiffuseColor() const
+// {
+// LLColor3 dif = mColorCached;
+// dif.clamp();
+// return 2 * dif;
+// }
+
+// LLColor4 getAmbientColor(const LLColor3& scatt, F32 scale) const
+// {
+// const F32 min_val = 0.05f;
+// LLColor4 col = LLColor4(scale * (0.8f * color_norm_abs(getDiffuseColor()) + 0.2f * scatt));
+// //F32 left = max(0, 1 - col.mV[0]);
+// if (col.mV[0] >= 0.9)
+// {
+// col.mV[1] = llmax(col.mV[1], 2.f * min_val);
+// col.mV[2] = llmax(col.mV[2], min_val);
+// }
+// col.setAlpha(1.f);
+// return col;
+// }
+
+ const F32& getHorizonVisibility() const { return mHorizonVisibility; }
+ void setHorizonVisibility(const F32 c = 1) { mHorizonVisibility = c; }
+ const F32& getVisibility() const { return mVisibility; }
+ void setVisibility(const F32 c = 1) { mVisibility = c; }
+ const F32 getHaloBrighness() const
+ {
+ return llmax(0.f, llmin(0.9f, mHorizonVisibility)) * mVisibility;
+ }
+ BOOL isVisible() const { return mVisible; }
+ void setVisible(const BOOL v) { mVisible = v; }
+
+
+ const F32& getIntensity() const { return mIntensity; }
+ void setIntensity(const F32 c) { mIntensity = c; }
+
+ void setDiskRadius(const F32 radius) { mDiskRadius = radius; }
+ F32 getDiskRadius() const { return mDiskRadius; }
+
+ void setDraw(const BOOL draw) { mDraw = draw; }
+ BOOL getDraw() const { return mDraw; }
+
+ const LLVector3& corner(const S32 n) const { return mQuadCorner[n]; }
+ LLVector3& corner(const S32 n) { return mQuadCorner[n]; }
+ const LLVector3* corners() const { return mQuadCorner; }
+
+ const LLVector3& getU() const { return mU; }
+ const LLVector3& getV() const { return mV; }
+ void setU(const LLVector3& u) { mU = u; }
+ void setV(const LLVector3& v) { mV = v; }
+};
+
+
+__forceinline LLColor3 refr_ind_calc(const LLColor3 &wave_length)
+{
+ LLColor3 refr_ind;
+ for (S32 i = 0; i < 3; ++i)
+ {
+ const F32 wl2 = wave_length.mV[i] * wave_length.mV[i] * 1e-6f;
+ refr_ind.mV[i] = 6.43e3f + ( 2.95e6f / ( 146.0f - 1.f/wl2 ) ) + ( 2.55e4f / ( 41.0f - 1.f/wl2 ) );
+ refr_ind.mV[i] *= 1.0e-8f;
+ refr_ind.mV[i] += 1.f;
+ }
+ return refr_ind;
+}
+
+
+__forceinline LLColor3 calc_air_sca_sea_level()
+{
+ const static LLColor3 WAVE_LEN(675, 520, 445);
+ const static LLColor3 refr_ind = refr_ind_calc(WAVE_LEN);
+ const static LLColor3 n21 = refr_ind * refr_ind - LLColor3(1, 1, 1);
+ const static LLColor3 n4 = n21 * n21;
+ const static LLColor3 wl2 = WAVE_LEN * WAVE_LEN * 1e-6f;
+ const static LLColor3 wl4 = wl2 * wl2;
+ const static LLColor3 mult_const = fsigma * 2.0f/ 3.0f * 1e24f * (F_PI * F_PI) * n4;
+ const static F32 dens_div_N = F32( ATM_SEA_LEVEL_NDENS / Ndens2);
+ return dens_div_N * color_div ( mult_const, wl4 );
+}
+
+const LLColor3 gAirScaSeaLevel = calc_air_sca_sea_level();
+const F32 AIR_SCA_INTENS = color_intens(gAirScaSeaLevel);
+const F32 AIR_SCA_AVG = AIR_SCA_INTENS / 3;
+
+class LLHaze
+{
+public:
+ LLHaze() : mG(0), mFalloff(1) {mSigSca.setToBlack();}
+ LLHaze(const F32 g, const LLColor3& sca, const F32 fo = 2) :
+ mG(g), mSigSca(0.25f/F_PI * sca), mFalloff(fo), mAbsCoef(0)
+ {
+ mAbsCoef = color_intens(mSigSca) / AIR_SCA_INTENS;
+ }
+
+ LLHaze(const F32 g, const F32 sca, const F32 fo = 2) : mG(g),
+ mSigSca(0.25f/F_PI * LLColor3(sca, sca, sca)), mFalloff(fo)
+ {
+ mAbsCoef = 0.01f * sca / AIR_SCA_AVG;
+ }
+
+ static void initClass();
+
+
+ F32 getG() const { return mG; }
+
+ void setG(const F32 g)
+ {
+ mG = g;
+ }
+
+ const LLColor3& getSigSca() const // sea level
+ {
+ return mSigSca;
+ }
+
+ void setSigSca(const LLColor3& s)
+ {
+ mSigSca = s;
+ mAbsCoef = 0.01f * color_intens(mSigSca) / AIR_SCA_INTENS;
+ }
+
+ void setSigSca(const F32 s0, const F32 s1, const F32 s2)
+ {
+ mSigSca = AIR_SCA_AVG * LLColor3 (s0, s1, s2);
+ mAbsCoef = 0.01f * (s0 + s1 + s2) / 3;
+ }
+
+ F32 getFalloff() const
+ {
+ return mFalloff;
+ }
+
+ void setFalloff(const F32 fo)
+ {
+ mFalloff = fo;
+ }
+
+ F32 getAbsCoef() const
+ {
+ return mAbsCoef;
+ }
+
+ inline static F32 calcFalloff(const F32 h)
+ {
+ return (h <= 0) ? 1.0f : (F32)LL_FAST_EXP(-ATM_EXP_FALLOFF * h);
+ }
+
+ inline LLColor3 calcSigSca(const F32 h) const
+ {
+ return calcFalloff(h * mFalloff) * mSigSca;
+ }
+
+ inline void calcSigSca(const F32 h, LLColor3 &result) const
+ {
+ result = mSigSca;
+ result *= calcFalloff(h * mFalloff);
+ }
+
+ LLColor3 calcSigExt(const F32 h) const
+ {
+ return calcFalloff(h * mFalloff) * (1 + mAbsCoef) * mSigSca;
+ }
+
+ F32 calcPhase(const F32 cos_theta) const;
+
+ static inline LLColor3 calcAirSca(const F32 h);
+ static inline void calcAirSca(const F32 h, LLColor3 &result);
+ static LLColor3 calcAirScaSeaLevel() { return gAirScaSeaLevel; }
+ static const LLColor3 &getAirScaSeaLevel() { return sAirScaSeaLevel; }
+public:
+ static LLColor3 sAirScaSeaLevel;
+
+protected:
+ F32 mG;
+ LLColor3 mSigSca;
+ F32 mFalloff; // 1 - slow, >1 - faster
+ F32 mAbsCoef;
+};
+
+class LLTranspMap
+{
+public:
+ LLTranspMap() : mElevation(0), mMaxAngle(0), mStep(5), mHaze(NULL), mT(NULL) {}
+ ~LLTranspMap()
+ {
+ delete[] mT;
+ mT = NULL;
+ }
+
+ void init(const F32 elev, const F32 step, const F32 h, const LLHaze* const haze);
+
+ F32 calcHeight(const LLVector3& pos) const
+ {
+ return pos.magVec() - EARTH_RADIUS ;
+ }
+
+ BOOL hasHaze() const
+ {
+ return mHaze != NULL;
+ }
+
+ LLColor3 calcSigExt(const F32 h) const
+ {
+ return LLHaze::calcAirSca(h) + (hasHaze() ? mHaze->calcSigExt(h) : LLColor3(0, 0, 0));
+ }
+
+ inline void calcAirTransp(const F32 cos_angle, LLColor3 &result) const;
+ LLColor3 calcAirTranspDir(const F32 elevation, const LLVector3 &dir) const;
+ LLColor3 getHorizonAirTransp () const { return mT[mMapSize-1]; }
+ F32 hitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const;
+
+protected:
+ F32 mAtmHeight;
+ F32 mElevation;
+ F32 mMaxAngle;
+ F32 mCosMaxAngle;
+ F32 mStep;
+ F32 mStepInv;
+ S32 mMapSize;
+ const LLHaze *mHaze;
+ LLColor3 *mT; // transparency values in all directions
+ //starting with mAngleBelowHorz at mElevation
+};
+
+class LLTranspMapSet
+{
+protected:
+ F32 *mHeights;
+ LLTranspMap *mTransp;
+ S32 mSize;
+ F32 mMediaHeight;
+ const LLHaze *mHaze;
+ S32 lerp(F32& dt, S32& indx, const F32 h) const;
+public:
+ LLTranspMapSet() : mHeights(NULL), mTransp(NULL), mHaze(NULL) {}
+ ~LLTranspMapSet();
+
+ void init (S32 size, F32 first_step, F32 media_height, const LLHaze* const haze);
+ S32 getSize() const { return mSize; }
+ F32 getMediaHeight() const { return mMediaHeight; }
+ const LLTranspMap& getLastTransp() const { return mTransp[mSize-1]; }
+ F32 getLastHeight() const { return mHeights[mSize-1]; }
+ const LLTranspMap& getMap(const S32 n) const { return mTransp[n]; }
+ F32 getHeight(const S32 n) const { return mHeights[n]; }
+ BOOL isReady() const { return mTransp != NULL; }
+
+ inline LLColor3 calcTransp(const F32 cos_angle, const F32 h) const;
+ inline void calcTransp(const F32 cos_angle, const F32 h, LLColor3 &result) const;
+};
+
+class LLCubeMap;
+
+
+class LLVOSky : public LLViewerObject
+{
+public:
+ enum
+ {
+ FACE_SIDE0,
+ FACE_SIDE1,
+ FACE_SIDE2,
+ FACE_SIDE3,
+ FACE_SIDE4,
+ FACE_SIDE5,
+ FACE_SUN, // was 6
+ FACE_MOON, // was 7
+ FACE_BLOOM, // was 8
+ FACE_REFLECTION, // was 10
+ FACE_COUNT
+ };
+
+ LLVOSky(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOSky();
+
+ // Initialize/delete data that's only inited once per class.
+ static void initClass();
+ void init();
+ void initCubeMap();
+ void initEmpty();
+ BOOL isReady() const { return mTransp.isReady(); }
+ const LLTranspMapSet& getTransp() const { return mTransp; }
+
+ void cleanupGL();
+ void restoreGL();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ BOOL updateSky();
+
+ // Graphical stuff for objects - maybe broken out into render class
+ // later?
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void initSkyTextureDirs(const S32 side, const S32 tile);
+ void createSkyTexture(const S32 side, const S32 tile);
+
+ void updateBrightestDir();
+ void calcBrightnessScaleAndColors();
+
+ LLColor3 calcSkyColorInDir(const LLVector3& dir);
+ void calcSkyColorInDir(LLColor3& res, LLColor3& transp,
+ const LLVector3& dir) const;
+ LLColor4 calcInScatter(LLColor4& transp, const LLVector3 &point, F32 exag) const;
+ void calcInScatter( LLColor3& res, LLColor3& transp,
+ const LLVector3& P, F32 exag) const;
+
+ // Not currently used.
+ //LLColor3 calcGroundFog(LLColor3& transp, const LLVector3 &view_dir, F32 obj_dist) const;
+ //void calcGroundFog(LLColor3& res, LLColor3& transp, const LLVector3 view_dir, F32 dist) const;
+
+ LLColor3 calcRadianceAtPoint(const LLVector3& pos) const
+ {
+ const F32 cos_angle = calcUpVec(pos) * getToSunLast();
+ LLColor3 tr;
+ mTransp.calcTransp(cos_angle, calcHeight(pos), tr);
+ return mBrightnessScaleGuess * mSun.getIntensity() * tr;
+ }
+
+ const LLHeavenBody& getSun() const { return mSun; }
+ const LLHeavenBody& getMoon() const { return mMoon; }
+
+ const LLVector3& getToSunLast() const { return mSun.getDirectionCached(); }
+ const LLVector3& getToSun() const { return mSun.getDirection(); }
+ const LLVector3& getToMoon() const { return mMoon.getDirection(); }
+ const LLVector3& getToMoonLast() const { return mMoon.getDirectionCached(); }
+ BOOL isSunUp() const { return mSun.getDirectionCached().mV[2] > -0.05f; }
+ void calculateColors();
+
+ LLColor3 getSunDiffuseColor() const { return mSunDiffuse; }
+ LLColor3 getMoonDiffuseColor() const { return mMoonDiffuse; }
+ LLColor4 getSunAmbientColor() const { return mSunAmbient; }
+ LLColor4 getMoonAmbientColor() const { return mMoonAmbient; }
+ const LLColor4& getTotalAmbientColor() const { return mTotalAmbient; }
+ LLColor4 getFogColor() const { return mFogColor; }
+
+ LLVector3 calcUpVec(const LLVector3 &pos) const
+ {
+ LLVector3 v = pos - mEarthCenter;
+ v.normVec();
+ return v;
+ }
+
+ F32 calcHeight(const LLVector3& pos) const
+ {
+ return dist_vec(pos, mEarthCenter) - EARTH_RADIUS;
+ }
+
+ // Phase function for atmospheric scattering.
+ // co = cos ( theta )
+ F32 calcAirPhaseFunc(const F32 co) const
+ {
+ return (0.75f * (1.f + co*co));
+ }
+
+
+ BOOL isSameFace(S32 idx, const LLFace* face) const { return mFace[idx] == face; }
+
+ void initSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity)
+ {
+ LLVector3 sun_direction = (sun_dir.magVec() == 0) ? LLVector3::x_axis : sun_dir;
+ sun_direction.normVec();
+ mSun.setDirection(sun_direction);
+ mSun.renewDirection();
+ mSun.setAngularVelocity(sun_ang_velocity);
+ mMoon.setDirection(-mSun.getDirection());
+ mMoon.renewDirection();
+ mLastLightingDirection = mSun.getDirection();
+
+ if ( !isReady() )
+ {
+ init();
+ LLSkyTex::stepCurrent();
+ }
+ }
+
+ void setSunDirection(const LLVector3 &sun_dir, const LLVector3 &sun_ang_velocity);
+
+ void updateHaze();
+
+ BOOL updateHeavenlyBodyGeometry(LLDrawable *drawable, const S32 side, const BOOL is_sun,
+ LLHeavenBody& hb, const F32 sin_max_angle,
+ const LLVector3 &up, const LLVector3 &right);
+
+ LLVector3 toHorizon(const LLVector3& dir, F32 delta = 0) const
+ {
+ return move_vec(dir, cosHorizon(delta));
+ }
+ F32 cosHorizon(const F32 delta = 0) const
+ {
+ const F32 sin_angle = EARTH_RADIUS/(EARTH_RADIUS + mCameraPosAgent.mV[2]);
+ return delta - (F32)sqrt(1.f - sin_angle * sin_angle);
+ }
+
+ void updateSunHaloGeometry(LLDrawable *drawable);
+ void updateReflectionGeometry(LLDrawable *drawable, F32 H, const LLHeavenBody& HB);
+
+
+ const LLHaze& getHaze() const { return mHaze; }
+ LLHaze& getHaze() { return mHaze; }
+ F32 getHazeConcentration() const { return mHazeConcentration; }
+ void setHaze(const LLHaze& h) { mHaze = h; }
+ F32 getWorldScale() const { return mWorldScale; }
+ void setWorldScale(const F32 s) { mWorldScale = s; }
+ void updateFog(const F32 distance);
+ void setFogRatio(const F32 fog_ratio) { mFogRatio = fog_ratio; }
+ LLColor4U getFadeColor() const { return mFadeColor; }
+ F32 getFogRatio() const { return mFogRatio; }
+ void setCloudDensity(F32 cloud_density) { mCloudDensity = cloud_density; }
+ void setWind ( const LLVector3& wind ) { mWind = wind.magVec(); }
+
+ const LLVector3 &getCameraPosAgent() const { return mCameraPosAgent; }
+ LLVector3 getEarthCenter() const { return mEarthCenter; }
+
+ LLCubeMap *getCubeMap() const { return mCubeMap; }
+ S32 getDrawRefl() const { return mDrawRefl; }
+ void setDrawRefl(const S32 r) { mDrawRefl = r; }
+ BOOL isReflFace(const LLFace* face) const { return face == mFace[FACE_REFLECTION]; }
+ LLFace* getReflFace() const { return mFace[FACE_REFLECTION]; }
+
+ F32 calcHitsEarth(const LLVector3& orig, const LLVector3& dir) const;
+ F32 calcHitsAtmEdge(const LLVector3& orig, const LLVector3& dir) const;
+ LLViewerImage* getSunTex() const { return mSunTexturep; }
+ LLViewerImage* getMoonTex() const { return mMoonTexturep; }
+ LLViewerImage* getBloomTex() const { return mBloomTexturep; }
+
+ void generateScatterMap();
+ LLImageGL* getScatterMap() { return mScatterMap; }
+
+public:
+ static F32 sNighttimeBrightness; // [0,2] default = 1.0
+
+protected:
+ LLPointer<LLViewerImage> mSunTexturep;
+ LLPointer<LLViewerImage> mMoonTexturep;
+ LLPointer<LLViewerImage> mBloomTexturep;
+
+ static S32 sResolution;
+ static S32 sTileResX;
+ static S32 sTileResY;
+ LLSkyTex mSkyTex[6];
+ LLHeavenBody mSun;
+ LLHeavenBody mMoon;
+ LLVector3 mSunDefaultPosition;
+ LLVector3 mSunAngVel;
+ F32 mAtmHeight;
+ LLVector3 mEarthCenter;
+ LLVector3 mCameraPosAgent;
+ F32 mBrightnessScale;
+ LLColor3 mBrightestPoint;
+ F32 mBrightnessScaleNew;
+ LLColor3 mBrightestPointNew;
+ F32 mBrightnessScaleGuess;
+ LLColor3 mBrightestPointGuess;
+ LLTranspMapSet mTransp;
+ LLHaze mHaze;
+ F32 mHazeConcentration;
+ BOOL mWeatherChange;
+ F32 mCloudDensity;
+ F32 mWind;
+ LLFace *mFace[FACE_COUNT];
+
+ BOOL mInitialized;
+ BOOL mForceUpdate; //flag to force instantaneous update of cubemap
+ LLVector3 mLastLightingDirection;
+ LLColor3 mLastTotalAmbient;
+ F32 mAmbientScale;
+ LLColor3 mNightColorShift;
+ F32 sInterpVal;
+
+ LLColor4 mFogColor;
+ F32 mFogRatio;
+ F32 mWorldScale;
+
+ LLColor4 mSunAmbient;
+ LLColor4 mMoonAmbient;
+ LLColor4 mTotalAmbient;
+ LLColor3 mSunDiffuse;
+ LLColor3 mMoonDiffuse;
+ LLColor4U mFadeColor; // Color to fade in from
+
+ LLCubeMap *mCubeMap; // Cube map for the sky
+ S32 mDrawRefl;
+
+ LLFrameTimer mUpdateTimer;
+
+ LLPointer<LLImageGL> mScatterMap;
+ LLPointer<LLImageRaw> mScatterMapRaw;
+};
+
+// Utility functions
+F32 azimuth(const LLVector3 &v);
+F32 color_norm_pow(LLColor3& col, F32 e, BOOL postmultiply = FALSE);
+
+
+/* Proportion of light that is scattered into 'path' from 'in' over distance dt. */
+/* assumes that vectors 'path' and 'in' are normalized. Scattering coef / 2pi */
+
+inline LLColor3 LLHaze::calcAirSca(const F32 h)
+{
+ static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
+ return calcFalloff(h) * air_sca_sea_level;
+}
+
+inline void LLHaze::calcAirSca(const F32 h, LLColor3 &result)
+{
+ static const LLColor3 air_sca_sea_level = calcAirScaSeaLevel();
+ result = air_sca_sea_level;
+ result *= calcFalloff(h);
+}
+
+// Given cos of the angle between direction of interest and zenith,
+// compute transparency by interpolation of known values.
+inline void LLTranspMap::calcAirTransp(const F32 cos_angle, LLColor3 &result) const
+{
+ if (cos_angle > 1.f)
+ {
+ result = mT[0];
+ return;
+ }
+ if (cos_angle < mCosMaxAngle - 0.1f)
+ {
+ result.setVec(0.f, 0.f, 0.f);
+ return;
+ }
+ if (cos_angle < mCosMaxAngle)
+ {
+ result = mT[mMapSize-1];
+ return;
+ }
+
+
+ const F32 relative = (1 - cos_angle)*mStepInv;
+ const S32 index = llfloor(relative);
+ const F32 dt = relative - index;
+
+ if (index >= (mMapSize-1))
+ {
+ result = mT[0];
+ return;
+ }
+// result = mT[index];
+// LLColor3 res2(mT[index+1]);
+// result *= 1 - dt;
+// res2 *= dt;
+// result += res2;
+
+ const LLColor3& color1 = mT[index];
+ const LLColor3& color2 = mT[index + 1];
+
+ const F32 x1 = color1.mV[VX];
+ const F32 x2 = color2.mV[VX];
+ result.mV[VX] = x1 - dt * (x1 - x2);
+
+ const F32 y1 = color1.mV[VY];
+ const F32 y2 = color2.mV[VY];
+ result.mV[VY] = y1 - dt * (y1 - y2);
+
+ const F32 z1 = color1.mV[VZ];
+ const F32 z2 = color2.mV[VZ];
+ result.mV[VZ] = z1 - dt * (z1 - z2);
+}
+
+
+
+// Returns the translucency of the atmosphere along the ray in the sky.
+// dir is assumed to be normalized
+inline void LLTranspMapSet::calcTransp(const F32 cos_angle, const F32 h, LLColor3 &result) const
+{
+ S32 indx = 0;
+ F32 dt = 0.f;
+ const S32 status = lerp(dt, indx, h);
+
+ if (status < 0)
+ {
+ mTransp[0].calcAirTransp(cos_angle, result);
+ return;
+ }
+ if (status > 0)
+ {
+ mTransp[NO_STEPS].calcAirTransp(cos_angle, result);
+ return;
+ }
+
+ mTransp[indx].calcAirTransp(cos_angle, result);
+ result *= 1 - dt;
+
+ LLColor3 transp_above;
+
+ mTransp[indx + 1].calcAirTransp(cos_angle, transp_above);
+ transp_above *= dt;
+ result += transp_above;
+}
+
+
+inline LLColor3 LLTranspMapSet::calcTransp(const F32 cos_angle, const F32 h) const
+{
+ LLColor3 result;
+ S32 indx = 0;
+ F32 dt = 0;
+ const S32 status = lerp(dt, indx, h);
+
+ if (status < 0)
+ {
+ mTransp[0].calcAirTransp(cos_angle, result);
+ return result;
+ }
+ if (status > 0)
+ {
+ mTransp[NO_STEPS].calcAirTransp(cos_angle, result);
+ return result;
+ }
+
+ mTransp[indx].calcAirTransp(cos_angle, result);
+ result *= 1 - dt;
+
+ LLColor3 transp_above;
+
+ mTransp[indx + 1].calcAirTransp(cos_angle, transp_above);
+ transp_above *= dt;
+ result += transp_above;
+ return result;
+}
+
+
+// Returns -1 if height < 0; +1 if height > max height; 0 if within range
+inline S32 LLTranspMapSet::lerp(F32& dt, S32& indx, const F32 h) const
+{
+ static S32 last_indx = 0;
+
+ if (h < 0)
+ {
+ return -1;
+ }
+ if (h > getLastHeight())
+ {
+ return 1;
+ }
+
+ if (h < mHeights[last_indx])
+ {
+ indx = last_indx-1;
+ while (mHeights[indx] > h)
+ {
+ indx--;
+ }
+ last_indx = indx;
+ }
+ else if (h > mHeights[last_indx+1])
+ {
+ indx = last_indx+1;
+ while (mHeights[indx+1] < h)
+ {
+ indx++;
+ }
+ last_indx = indx;
+ }
+ else
+ {
+ indx = last_indx;
+ }
+
+ const F32 h_below = mHeights[indx];
+ const F32 h_above = mHeights[indx+1];
+ dt = (h - h_below) / (h_above - h_below);
+ return 0;
+}
+
+#endif
diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp
new file mode 100644
index 0000000000..ae7d46dfee
--- /dev/null
+++ b/indra/newview/llvosurfacepatch.cpp
@@ -0,0 +1,924 @@
+/**
+ * @file llvosurfacepatch.cpp
+ * @brief Viewer-object derived "surface patch", which is a piece of terrain
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvosurfacepatch.h"
+
+#include "lldrawable.h"
+#include "llface.h"
+#include "llprimitive.h"
+#include "llsky.h"
+#include "llsurfacepatch.h"
+#include "llsurface.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llvlcomposition.h"
+#include "llvovolume.h"
+#include "pipeline.h"
+
+LLVOSurfacePatch::LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_SURFACE_PATCH, regionp)
+{
+ // Terrain must draw during selection passes so it can block objects behind it.
+ mbCanSelect = TRUE;
+
+ mBaseComp = 0;
+ setScale(LLVector3(16.f, 16.f, 16.f)); // Hack for setting scale for bounding boxes/visibility.
+ mPool = NULL;
+ mDirtiedPatch = FALSE;
+ mLastStride = 0;
+ mLastNorthStride = 0;
+ mLastEastStride = 0;
+ mLastLength = 0;
+
+ mDirtyTerrain = TRUE;
+}
+
+
+LLVOSurfacePatch::~LLVOSurfacePatch()
+{
+ mPatchp = NULL;
+}
+
+
+void LLVOSurfacePatch::markDead()
+{
+ if (mPatchp)
+ {
+ mPatchp->clearVObj();
+ mPatchp = NULL;
+ }
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOSurfacePatch::isActive() const
+{
+ return FALSE;
+}
+
+
+void LLVOSurfacePatch::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 500*500;
+}
+
+
+void LLVOSurfacePatch::updateTextures(LLAgent &agent)
+{
+}
+
+
+LLDrawPool *LLVOSurfacePatch::getPool()
+{
+ mPool = gPipeline.getPool(LLDrawPool::POOL_TERRAIN, mPatchp->getSurface()->getSTexture());
+
+ return mPool;
+}
+
+
+LLDrawable *LLVOSurfacePatch::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TERRAIN);
+
+ mBaseComp = llfloor(mPatchp->getMinComposition());
+ S32 min_comp, max_comp, range;
+ min_comp = llfloor(mPatchp->getMinComposition());
+ max_comp = llceil(mPatchp->getMaxComposition());
+ range = (max_comp - min_comp);
+ range++;
+ if (range > 3)
+ {
+ if ((mPatchp->getMinComposition() - min_comp) > (max_comp - mPatchp->getMaxComposition()))
+ {
+ // The top side runs over more
+ mBaseComp++;
+ }
+ range = 3;
+ }
+
+ LLDrawPool *poolp = getPool();
+
+ mDrawable->addFace(poolp, NULL);
+ return mDrawable;
+}
+
+
+BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable)
+{
+ S32 min_comp, max_comp, range;
+ min_comp = lltrunc(mPatchp->getMinComposition());
+ max_comp = lltrunc(ceil(mPatchp->getMaxComposition()));
+ range = (max_comp - min_comp);
+ range++;
+ S32 new_base_comp = lltrunc(mPatchp->getMinComposition());
+ if (range > 3)
+ {
+ if ((mPatchp->getMinComposition() - min_comp) > (max_comp - mPatchp->getMaxComposition()))
+ {
+ // The top side runs over more
+ new_base_comp++;
+ }
+ range = 3;
+ }
+
+ // Pick the two closest detail textures for this patch...
+ // Then create the draw pool for it.
+ // Actually, should get the average composition instead of the center.
+ mBaseComp = new_base_comp;
+
+ //////////////////////////
+ //
+ // Figure out the strides
+ //
+ //
+
+ U32 patch_width, render_stride, north_stride, east_stride, length;
+ render_stride = mPatchp->getRenderStride();
+ patch_width = mPatchp->getSurface()->getGridsPerPatchEdge();
+
+ length = patch_width / render_stride;
+
+ if (mPatchp->getNeighborPatch(NORTH))
+ {
+ north_stride = mPatchp->getNeighborPatch(NORTH)->getRenderStride();
+ }
+ else
+ {
+ north_stride = render_stride;
+ }
+
+ if (mPatchp->getNeighborPatch(EAST))
+ {
+ east_stride = mPatchp->getNeighborPatch(EAST)->getRenderStride();
+ }
+ else
+ {
+ east_stride = render_stride;
+ }
+
+ S32 num_vertices = 0;
+ S32 num_indices = 0;
+ S32 new_north_offset = 0;
+ S32 new_east_offset = 0;
+
+ getGeomSizesMain(render_stride, num_vertices, num_indices);
+ new_north_offset = num_vertices;
+ getGeomSizesNorth(render_stride, north_stride, num_vertices, num_indices);
+ new_east_offset = num_vertices;
+ getGeomSizesEast(render_stride, east_stride, num_vertices, num_indices);
+ S32 new_num_vertices = num_vertices;
+ S32 new_num_indices = num_indices;
+
+ LLFace *facep = NULL;
+
+ // Update the allocated face
+ LLStrider<LLVector3> verticesp;
+ LLStrider<LLVector3> normalsp;
+ LLStrider<LLVector2> texCoords0p;
+ LLStrider<LLVector2> texCoords1p;
+ LLStrider<LLColor4U> colorsp;
+ U32* indicesp = NULL;
+ S32 index_offset;
+
+ facep = mDrawable->getFace(0);
+
+ facep->setSize(new_num_vertices, new_num_indices);
+ facep->setPrimType(LLTriangles);
+
+ index_offset = facep->getGeometryTerrain(
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ mDrawable->updateLightSet();
+
+ updateMainGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateNorthGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+ updateEastGeometry(facep,
+ verticesp,
+ normalsp,
+ colorsp,
+ texCoords0p,
+ texCoords1p,
+ indicesp,
+ index_offset);
+
+ if (mLastLength != 0)
+ {
+ // lazy, should cache the geom sizes so we know the offsets.
+ num_vertices = 0;
+ num_indices = 0;
+
+ }
+
+ mLastLength = length;
+ mLastStride = render_stride;
+ mLastNorthStride = north_stride;
+ mLastEastStride = east_stride;
+
+ mDrawable->setState(LLDrawable::LIGHTING_BUILT);
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+void LLVOSurfacePatch::updateMainGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 i, j, x, y;
+
+ U32 patch_size, render_stride;
+ S32 num_vertices, num_indices;
+ U32 index;
+
+ render_stride = mPatchp->getRenderStride();
+ patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 vert_size = patch_size / render_stride;
+
+ ///////////////////////////
+ //
+ // Render the main patch
+ //
+ //
+
+ num_vertices = 0;
+ num_indices = 0;
+ // First, figure out how many vertices we need...
+ getGeomSizesMain(render_stride, num_vertices, num_indices);
+
+ if (num_vertices > 0)
+ {
+ facep->mCenterAgent = mPatchp->getPointAgent(8, 8);
+
+ // Generate patch points first
+ for (j = 0; j < vert_size; j++)
+ {
+ for (i = 0; i < vert_size; i++)
+ {
+ x = i * render_stride;
+ y = j * render_stride;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ }
+
+ for (j = 0; j < (vert_size - 1); j++)
+ {
+ if (j % 2)
+ {
+ for (i = (vert_size - 1); i > 0; i--)
+ {
+ index = (i - 1)+ j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i - 1) + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i - 1) + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+ }
+ }
+ else
+ {
+ for (i = 0; i < (vert_size - 1); i++)
+ {
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + (j+1)*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = i + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + j*vert_size;
+ *(indicesp++) = index_offset + index;
+
+ index = (i + 1) + (j + 1)*vert_size;
+ *(indicesp++) = index_offset + index;
+ }
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+
+void LLVOSurfacePatch::updateNorthGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 vertex_count = 0;
+ S32 i, x, y;
+
+ S32 num_vertices, num_indices;
+
+ U32 render_stride = mPatchp->getRenderStride();
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / render_stride;
+ S32 half_length = length / 2;
+
+ U32 north_stride;
+ if (mPatchp->getNeighborPatch(NORTH))
+ {
+ north_stride = mPatchp->getNeighborPatch(NORTH)->getRenderStride();
+ }
+ else
+ {
+ north_stride = render_stride;
+ }
+
+ ///////////////////////////
+ //
+ // Render the north strip
+ //
+ //
+
+ // Stride lengths are the same
+ if (north_stride == render_stride)
+ {
+ num_vertices = 2 * length + 1;
+ num_indices = length * 6 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(8, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Main patch
+ for (i = 0; i < length; i++)
+ {
+ x = i * render_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // North patch
+ for (i = 0; i <= length; i++)
+ {
+ x = i * render_stride;
+ y = 16;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ // Generate indices
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i + 1;
+ *(indicesp++) = index_offset + length + i;
+
+ if (i != length - 1)
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + i + 1;
+ }
+ }
+ }
+ else if (north_stride > render_stride)
+ {
+ // North stride is longer (has less vertices)
+ num_vertices = length + length/2 + 1;
+ num_indices = half_length*9 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(7, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i++)
+ {
+ x = i * render_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // Iterate through the north patch's points
+ for (i = 0; i <= length; i+=2)
+ {
+ x = i * render_stride;
+ y = 16;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i % 2))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+ }
+ else if (i < (length - 1))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ }
+ }
+ }
+ else
+ {
+ // North stride is shorter (more vertices)
+ length = patch_size / north_stride;
+ half_length = length / 2;
+ num_vertices = length + half_length + 1;
+ num_indices = 9*half_length - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(15, 7) + mPatchp->getPointAgent(16, 8))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i+=2)
+ {
+ x = i * north_stride;
+ y = 16 - render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ // Iterate through the north patch's points
+ for (i = 0; i <= length; i++)
+ {
+ x = i * north_stride;
+ y = 16;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ vertex_count++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i%2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ else if (i < (length - 2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + i/2 + 1;
+
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2 + 1;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ else
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+void LLVOSurfacePatch::updateEastGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset)
+{
+ S32 i, x, y;
+
+ S32 num_vertices, num_indices;
+
+ U32 render_stride = mPatchp->getRenderStride();
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / render_stride;
+ S32 half_length = length / 2;
+
+ U32 east_stride;
+ if (mPatchp->getNeighborPatch(EAST))
+ {
+ east_stride = mPatchp->getNeighborPatch(EAST)->getRenderStride();
+ }
+ else
+ {
+ east_stride = render_stride;
+ }
+
+ // Stride lengths are the same
+ if (east_stride == render_stride)
+ {
+ num_vertices = 2 * length + 1;
+ num_indices = length * 6 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(8, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Main patch
+ for (i = 0; i < length; i++)
+ {
+ x = 16 - render_stride;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ // East patch
+ for (i = 0; i <= length; i++)
+ {
+ x = 16;
+ y = i * render_stride;
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+
+ for (i = 0; i < length; i++)
+ {
+ // Generate indices
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i;
+ *(indicesp++) = index_offset + length + i + 1;
+
+ if (i != length - 1)
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + i + 1;
+ *(indicesp++) = index_offset + i + 1;
+ }
+ }
+ }
+ else if (east_stride > render_stride)
+ {
+ // East stride is longer (has less vertices)
+ num_vertices = length + half_length + 1;
+ num_indices = half_length*9 - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(7, 15) + mPatchp->getPointAgent(8, 16))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i++)
+ {
+ x = 16 - render_stride;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ // Iterate through the east patch's points
+ for (i = 0; i <= length; i+=2)
+ {
+ x = 16;
+ y = i * render_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i % 2))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + (i/2);
+ *(indicesp++) = index_offset + i + 1;
+
+ *(indicesp++) = index_offset + i + 1;
+ *(indicesp++) = index_offset + length + (i/2);
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ }
+ else if (i < (length - 1))
+ {
+ *(indicesp++) = index_offset + i;
+ *(indicesp++) = index_offset + length + (i/2) + 1;
+ *(indicesp++) = index_offset + i + 1;
+ }
+ }
+ }
+ else
+ {
+ // East stride is shorter (more vertices)
+ length = patch_size / east_stride;
+ half_length = length / 2;
+ num_vertices = length + length/2 + 1;
+ num_indices = 9*(length/2) - 3;
+
+ facep->mCenterAgent = (mPatchp->getPointAgent(15, 7) + mPatchp->getPointAgent(16, 8))*0.5f;
+
+ // Iterate through this patch's points
+ for (i = 0; i < length; i+=2)
+ {
+ x = 16 - render_stride;
+ y = i * east_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+ // Iterate through the east patch's points
+ for (i = 0; i <= length; i++)
+ {
+ x = 16;
+ y = i * east_stride;
+
+ mPatchp->eval(x, y, render_stride, verticesp.get(), normalsp.get(), texCoords0p.get(), texCoords1p.get());
+ calcColor(verticesp.get(), normalsp.get(), colorsp.get());
+ verticesp++;
+ normalsp++;
+ colorsp++;
+ texCoords0p++;
+ texCoords1p++;
+ }
+
+ for (i = 0; i < length; i++)
+ {
+ if (!(i%2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2;
+ }
+ else if (i < (length - 2))
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + i/2 + 1;
+ *(indicesp++) = index_offset + i/2;
+
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2 + 1;
+ }
+ else
+ {
+ *(indicesp++) = index_offset + half_length + i;
+ *(indicesp++) = index_offset + half_length + i + 1;
+ *(indicesp++) = index_offset + i/2;
+ }
+ }
+ }
+ index_offset += num_vertices;
+}
+
+void LLVOSurfacePatch::calcColor(const LLVector3* vertex, const LLVector3* normal, LLColor4U* colorp)
+{
+ LLColor4 color(0,0,0,0);
+ if (gPipeline.getLightingDetail() >= 2)
+ {
+ for (LLDrawable::drawable_set_t::iterator iter = mDrawable->mLightSet.begin();
+ iter != mDrawable->mLightSet.end(); ++iter)
+ {
+ LLDrawable* light_drawable = *iter;
+ LLVOVolume* light = light_drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ LLColor4 light_color;
+ light->calcLightAtPoint(*vertex, *normal, light_color);
+ color += light_color;
+ }
+
+ color.mV[3] = 1.0f;
+ }
+ colorp->setVecScaleClamp(color);
+}
+
+BOOL LLVOSurfacePatch::updateShadows(BOOL use_shadow_factor)
+{
+ return FALSE; //terrain updates its shadows during standard relight
+}
+
+void LLVOSurfacePatch::setPatch(LLSurfacePatch *patchp)
+{
+ mPatchp = patchp;
+
+ dirtyPatch();
+};
+
+
+void LLVOSurfacePatch::dirtyPatch()
+{
+ if (mDrawable)
+ {
+ gPipeline.markMoved(mDrawable);
+ }
+ mDirtiedPatch = TRUE;
+ dirtyGeom();
+ mDirtyTerrain = TRUE;
+ LLVector3 center = mPatchp->getCenterRegion();
+ LLSurface *surfacep = mPatchp->getSurface();
+
+ setPositionRegion(center);
+
+ F32 scale_factor = surfacep->getGridsPerPatchEdge() * surfacep->getMetersPerGrid();
+ setScale(LLVector3(scale_factor, scale_factor, mPatchp->getMaxZ() - mPatchp->getMinZ()));
+}
+
+void LLVOSurfacePatch::dirtyGeom()
+{
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesMain(const S32 stride, S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+
+ // First, figure out how many vertices we need...
+ S32 vert_size = patch_size / stride;
+ if (vert_size >= 2)
+ {
+ num_vertices += vert_size * vert_size;
+ num_indices += 6 * (vert_size - 1)*(vert_size - 1);
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesNorth(const S32 stride, const S32 north_stride,
+ S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / stride;
+ // Stride lengths are the same
+ if (north_stride == stride)
+ {
+ num_vertices += 2 * length + 1;
+ num_indices += length * 6 - 3;
+ }
+ else if (north_stride > stride)
+ {
+ // North stride is longer (has less vertices)
+ num_vertices += length + (length/2) + 1;
+ num_indices += (length/2)*9 - 3;
+ }
+ else
+ {
+ // North stride is shorter (more vertices)
+ length = patch_size / north_stride;
+ num_vertices += length + (length/2) + 1;
+ num_indices += 9*(length/2) - 3;
+ }
+}
+
+void LLVOSurfacePatch::getGeomSizesEast(const S32 stride, const S32 east_stride,
+ S32 &num_vertices, S32 &num_indices)
+{
+ S32 patch_size = mPatchp->getSurface()->getGridsPerPatchEdge();
+ S32 length = patch_size / stride;
+ // Stride lengths are the same
+ if (east_stride == stride)
+ {
+ num_vertices += 2 * length + 1;
+ num_indices += length * 6 - 3;
+ }
+ else if (east_stride > stride)
+ {
+ // East stride is longer (has less vertices)
+ num_vertices += length + (length/2) + 1;
+ num_indices += (length/2)*9 - 3;
+ }
+ else
+ {
+ // East stride is shorter (more vertices)
+ length = patch_size / east_stride;
+ num_vertices += length + (length/2) + 1;
+ num_indices += 9*(length/2) - 3;
+ }
+}
+
+void LLVOSurfacePatch::updateSpatialExtents(LLVector3& newMin, LLVector3 &newMax)
+{
+ LLVector3 posAgent = getPositionAgent();
+ LLVector3 scale = getScale();
+ newMin = posAgent-scale;
+ newMax = posAgent+scale;
+ mDrawable->setPositionGroup((newMin+newMax)*0.5f);
+}
diff --git a/indra/newview/llvosurfacepatch.h b/indra/newview/llvosurfacepatch.h
new file mode 100644
index 0000000000..f1c44aa598
--- /dev/null
+++ b/indra/newview/llvosurfacepatch.h
@@ -0,0 +1,93 @@
+/**
+ * @file llvosurfacepatch.h
+ * @brief Description of LLVOSurfacePatch class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VOSURFACEPATCH_H
+#define LL_VOSURFACEPATCH_H
+
+#include "llviewerobject.h"
+#include "llstrider.h"
+
+class LLSurfacePatch;
+class LLDrawPool;
+class LLVector2;
+
+class LLVOSurfacePatch : public LLViewerObject
+{
+public:
+ LLVOSurfacePatch(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOSurfacePatch();
+
+ /*virtual*/ void markDead();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ /*virtual*/ void updateSpatialExtents(LLVector3& newMin, LLVector3& newMax);
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ void setPatch(LLSurfacePatch *patchp);
+ LLSurfacePatch *getPatch() const { return mPatchp; }
+
+ void dirtyPatch();
+ void dirtyGeom();
+
+ BOOL mDirtiedPatch;
+protected:
+ LLDrawPool *mPool;
+ LLDrawPool *getPool();
+ S32 mBaseComp;
+ LLSurfacePatch *mPatchp;
+ BOOL mDirtyTexture;
+ BOOL mDirtyTerrain;
+
+ S32 mLastNorthStride;
+ S32 mLastEastStride;
+ S32 mLastStride;
+ S32 mLastLength;
+
+ void calcColor(const LLVector3* vertex, const LLVector3* normal, LLColor4U* colorp);
+ BOOL updateShadows(BOOL use_shadow_factor = FALSE);
+ void getGeomSizesMain(const S32 stride, S32 &num_vertices, S32 &num_indices);
+ void getGeomSizesNorth(const S32 stride, const S32 north_stride,
+ S32 &num_vertices, S32 &num_indices);
+ void getGeomSizesEast(const S32 stride, const S32 east_stride,
+ S32 &num_vertices, S32 &num_indices);
+
+ void updateMainGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+ void updateNorthGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+ void updateEastGeometry(LLFace *facep,
+ LLStrider<LLVector3> &verticesp,
+ LLStrider<LLVector3> &normalsp,
+ LLStrider<LLColor4U> &colorsp,
+ LLStrider<LLVector2> &texCoords0p,
+ LLStrider<LLVector2> &texCoords1p,
+ U32* &indicesp,
+ S32 &index_offset);
+};
+
+#endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp
new file mode 100644
index 0000000000..d9f3d83f11
--- /dev/null
+++ b/indra/newview/llvotextbubble.cpp
@@ -0,0 +1,204 @@
+/**
+ * @file llvotextbubble.cpp
+ * @brief Viewer-object text bubble.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvotextbubble.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+#include "llprimitive.h"
+#include "llsphere.h"
+
+#include "llagent.h"
+#include "llbox.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewerimagelist.h"
+#include "llvolume.h"
+#include "pipeline.h"
+
+LLVOTextBubble::LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, pcode, regionp)
+{
+ setScale(LLVector3(1.5f, 1.5f, 0.25f));
+ mbCanSelect = FALSE;
+ mLOD = MIN_LOD;
+ mVolumeChanged = TRUE;
+ setVelocity(LLVector3(0.f, 0.f, 0.75f));
+ LLVolumeParams volume_params;
+ volume_params.setType(LL_PCODE_PROFILE_CIRCLE, LL_PCODE_PATH_LINE);
+ volume_params.setBeginAndEndS(0.f, 1.f);
+ volume_params.setBeginAndEndT(0.f, 1.f);
+ volume_params.setRatio(0.25f, 0.25f);
+ volume_params.setShear(0.f, 0.f);
+ setVolume(volume_params);
+ mColor = LLColor4(1.0f, 0.0f, 0.0f, 1.f);
+ S32 i;
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ setTEColor(i, mColor);
+ setTETexture(i, LLUUID(IMG_DEFAULT));
+ }
+}
+
+
+LLVOTextBubble::~LLVOTextBubble()
+{
+}
+
+
+BOOL LLVOTextBubble::isActive() const
+{
+ return TRUE;
+}
+
+BOOL LLVOTextBubble::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ F32 dt = mUpdateTimer.getElapsedTimeF32();
+ // Die after a few seconds.
+ if (dt > 1.5f)
+ {
+ return FALSE;
+ }
+
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ setScale(0.5f * (1.f+dt) * LLVector3(1.5f, 1.5f, 0.5f));
+
+ F32 alpha = 0.35f*dt;
+
+ LLColor4 color = mColor;
+ color.mV[VALPHA] -= alpha;
+ if (color.mV[VALPHA] <= 0.05f)
+ {
+ return FALSE;
+ }
+ S32 i;
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ setTEColor(i, color);
+ }
+
+ return TRUE;
+}
+
+
+void LLVOTextBubble::updateTextures(LLAgent &agent)
+{
+ // Update the image levels of all textures...
+ // First we do some quick checks.
+ U32 i;
+
+ // This doesn't take into account whether the object is in front
+ // or behind...
+
+ LLVector3 position_local = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 dot_product = position_local * agent.getFrameAgent().getAtAxis();
+ F32 cos_angle = dot_product / position_local.magVec();
+
+ if (cos_angle > 1.f)
+ {
+ cos_angle = 1.f;
+ }
+
+ for (i = 0; i < getNumTEs(); i++)
+ {
+ const LLTextureEntry *te = getTE(i);
+ F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT);
+
+ LLViewerImage *imagep = getTEImage(i);
+ if (imagep)
+ {
+ imagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
+ }
+ }
+}
+
+
+LLDrawable *LLVOTextBubble::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+ LLDrawPool *poolp;
+ for (U32 i = 0; i < getNumTEs(); i++)
+ {
+ LLViewerImage *imagep;
+ const LLTextureEntry *texture_entry = getTE(i);
+ imagep = gImageList.getImage(texture_entry->getID());
+
+ poolp = gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ mDrawable->addFace(poolp, imagep);
+ }
+
+ return mDrawable;
+}
+
+
+BOOL LLVOTextBubble::setVolume(const LLVolumeParams &volume_params)
+{
+ if (LLPrimitive::setVolume(volume_params, mLOD))
+ {
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ mVolumeChanged = TRUE;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+BOOL LLVOTextBubble::updateLOD()
+{
+ return FALSE;
+}
+
+BOOL LLVOTextBubble::updateGeometry(LLDrawable *drawable)
+{
+ if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_VOLUME)))
+ return TRUE;
+
+ if (mVolumeChanged)
+ {
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params);
+
+ LLPipeline::sCompiles++;
+
+ drawable->setNumFaces(getVolume()->getNumFaces(), drawable->getFace(0)->getPool(), getTEImage(0));
+ }
+
+ LLMatrix4 identity4;
+ LLMatrix3 identity3;
+ for (S32 i = 0; i < drawable->getNumFaces(); i++)
+ {
+ LLFace *face = drawable->getFace(i);
+
+ if (i == 0)
+ {
+ face->setSize(0);
+ continue;
+ }
+ if (i == 2)
+ {
+ face->setSize(0);
+ continue;
+ }
+
+ // Need to figure out how to readd logic to determine face dirty vs. entire object dirty.
+ face->setTEOffset(i);
+ face->clearState(LLFace::GLOBAL);
+ face->genVolumeTriangles(*getVolume(), i, identity4, identity3);
+ }
+ mVolumeChanged = FALSE;
+
+ return TRUE;
+}
diff --git a/indra/newview/llvotextbubble.h b/indra/newview/llvotextbubble.h
new file mode 100644
index 0000000000..9fe6386a3b
--- /dev/null
+++ b/indra/newview/llvotextbubble.h
@@ -0,0 +1,37 @@
+/**
+ * @file llvotextbubble.h
+ * @brief Description of LLVORock class, which a derivation of LLViewerObject
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTEXTBUBBLE_H
+#define LL_LLVOTEXTBUBBLE_H
+
+#include "llviewerobject.h"
+#include "llframetimer.h"
+
+class LLVOTextBubble : public LLViewerObject
+{
+public:
+ LLVOTextBubble(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTextBubble();
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ /*virtual*/ BOOL updateLOD();
+
+ LLColor4 mColor;
+ S32 mLOD;
+ BOOL mVolumeChanged;
+protected:
+ BOOL setVolume(const LLVolumeParams &volume_params);
+ LLFrameTimer mUpdateTimer;
+};
+
+#endif // LL_VO_TEXT_BUBBLE
diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp
new file mode 100644
index 0000000000..d2c7ed9b6a
--- /dev/null
+++ b/indra/newview/llvotree.cpp
@@ -0,0 +1,906 @@
+/**
+ * @file llvotree.cpp
+ * @brief LLVOTree class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvotree.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llprimitive.h"
+#include "lltree_common.h"
+#include "llxmltree.h"
+#include "material_codes.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "llagparray.h"
+#include "llcylinder.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobjectlist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "noise.h"
+#include "pipeline.h"
+#include "llviewerwindow.h"
+
+extern LLPipeline gPipeline;
+
+LLGLuint mLeafDList;
+
+S32 LLVOTree::sLODVertexOffset[4];
+S32 LLVOTree::sLODVertexCount[4];
+S32 LLVOTree::sLODIndexOffset[4];
+S32 LLVOTree::sLODIndexCount[4];
+S32 LLVOTree::sLODSlices[4] = {10, 5, 4, 3};
+F32 LLVOTree::sLODAngles[4] = {30.f, 20.f, 15.f, 0.f};
+
+F32 LLVOTree::sTreeFactor = 1.f;
+
+LLVOTree::SpeciesMap LLVOTree::sSpeciesTable;
+S32 LLVOTree::sMaxTreeSpecies = 0;
+
+// Tree variables and functions
+
+LLVOTree::LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp):
+ LLViewerObject(id, pcode, regionp)
+{
+ mSpecies = 0;
+ mFrameCount = 0;
+ mWind = mRegionp->mWind.getVelocity(getPositionRegion());
+}
+
+
+LLVOTree::~LLVOTree()
+{
+ if (mData)
+ {
+ delete[] mData;
+ mData = NULL;
+ }
+}
+
+// static
+void LLVOTree::initClass()
+{
+ std::string xml_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,"trees.xml");
+
+ LLXmlTree tree_def_tree;
+
+ if (!tree_def_tree.parseFile(xml_filename))
+ {
+ llerrs << "Failed to parse tree file." << llendl;
+ }
+
+ LLXmlTreeNode* rootp = tree_def_tree.getRoot();
+
+ for (LLXmlTreeNode* tree_def = rootp->getFirstChild();
+ tree_def;
+ tree_def = rootp->getNextChild())
+ {
+ if (!tree_def->hasName("tree"))
+ {
+ llwarns << "Invalid tree definition node " << tree_def->getName() << llendl;
+ continue;
+ }
+ F32 F32_val;
+ LLUUID id;
+ S32 S32_val;
+
+ BOOL success = TRUE;
+
+
+
+ S32 species;
+ static LLStdStringHandle species_id_string = LLXmlTree::addAttributeString("species_id");
+ if (!tree_def->getFastAttributeS32(species_id_string, species))
+ {
+ llwarns << "No species id defined" << llendl;
+ continue;
+ }
+
+ if (species < 0)
+ {
+ llwarns << "Invalid species id " << species << llendl;
+ continue;
+ }
+
+ if (sSpeciesTable.count(species))
+ {
+ llwarns << "Tree species " << species << " already defined! Duplicate discarded." << llendl;
+ continue;
+ }
+
+ TreeSpeciesData* newTree = new TreeSpeciesData();
+
+ static LLStdStringHandle texture_id_string = LLXmlTree::addAttributeString("texture_id");
+ success &= tree_def->getFastAttributeUUID(texture_id_string, id);
+ newTree->mTextureID = id;
+
+ static LLStdStringHandle droop_string = LLXmlTree::addAttributeString("droop");
+ success &= tree_def->getFastAttributeF32(droop_string, F32_val);
+ newTree->mDroop = F32_val;
+
+ static LLStdStringHandle twist_string = LLXmlTree::addAttributeString("twist");
+ success &= tree_def->getFastAttributeF32(twist_string, F32_val);
+ newTree->mTwist = F32_val;
+
+ static LLStdStringHandle branches_string = LLXmlTree::addAttributeString("branches");
+ success &= tree_def->getFastAttributeF32(branches_string, F32_val);
+ newTree->mBranches = F32_val;
+
+ static LLStdStringHandle depth_string = LLXmlTree::addAttributeString("depth");
+ success &= tree_def->getFastAttributeS32(depth_string, S32_val);
+ newTree->mDepth = S32_val;
+
+ static LLStdStringHandle scale_step_string = LLXmlTree::addAttributeString("scale_step");
+ success &= tree_def->getFastAttributeF32(scale_step_string, F32_val);
+ newTree->mScaleStep = F32_val;
+
+ static LLStdStringHandle trunk_depth_string = LLXmlTree::addAttributeString("trunk_depth");
+ success &= tree_def->getFastAttributeS32(trunk_depth_string, S32_val);
+ newTree->mTrunkDepth = S32_val;
+
+ static LLStdStringHandle branch_length_string = LLXmlTree::addAttributeString("branch_length");
+ success &= tree_def->getFastAttributeF32(branch_length_string, F32_val);
+ newTree->mBranchLength = F32_val;
+
+ static LLStdStringHandle trunk_length_string = LLXmlTree::addAttributeString("trunk_length");
+ success &= tree_def->getFastAttributeF32(trunk_length_string, F32_val);
+ newTree->mTrunkLength = F32_val;
+
+ static LLStdStringHandle leaf_scale_string = LLXmlTree::addAttributeString("leaf_scale");
+ success &= tree_def->getFastAttributeF32(leaf_scale_string, F32_val);
+ newTree->mLeafScale = F32_val;
+
+ static LLStdStringHandle billboard_scale_string = LLXmlTree::addAttributeString("billboard_scale");
+ success &= tree_def->getFastAttributeF32(billboard_scale_string, F32_val);
+ newTree->mBillboardScale = F32_val;
+
+ static LLStdStringHandle billboard_ratio_string = LLXmlTree::addAttributeString("billboard_ratio");
+ success &= tree_def->getFastAttributeF32(billboard_ratio_string, F32_val);
+ newTree->mBillboardRatio = F32_val;
+
+ static LLStdStringHandle trunk_aspect_string = LLXmlTree::addAttributeString("trunk_aspect");
+ success &= tree_def->getFastAttributeF32(trunk_aspect_string, F32_val);
+ newTree->mTrunkAspect = F32_val;
+
+ static LLStdStringHandle branch_aspect_string = LLXmlTree::addAttributeString("branch_aspect");
+ success &= tree_def->getFastAttributeF32(branch_aspect_string, F32_val);
+ newTree->mBranchAspect = F32_val;
+
+ static LLStdStringHandle leaf_rotate_string = LLXmlTree::addAttributeString("leaf_rotate");
+ success &= tree_def->getFastAttributeF32(leaf_rotate_string, F32_val);
+ newTree->mRandomLeafRotate = F32_val;
+
+ static LLStdStringHandle noise_mag_string = LLXmlTree::addAttributeString("noise_mag");
+ success &= tree_def->getFastAttributeF32(noise_mag_string, F32_val);
+ newTree->mNoiseMag = F32_val;
+
+ static LLStdStringHandle noise_scale_string = LLXmlTree::addAttributeString("noise_scale");
+ success &= tree_def->getFastAttributeF32(noise_scale_string, F32_val);
+ newTree->mNoiseScale = F32_val;
+
+ static LLStdStringHandle taper_string = LLXmlTree::addAttributeString("taper");
+ success &= tree_def->getFastAttributeF32(taper_string, F32_val);
+ newTree->mTaper = F32_val;
+
+ static LLStdStringHandle repeat_z_string = LLXmlTree::addAttributeString("repeat_z");
+ success &= tree_def->getFastAttributeF32(repeat_z_string, F32_val);
+ newTree->mRepeatTrunkZ = F32_val;
+
+ sSpeciesTable[species] = newTree;
+
+ if (species >= sMaxTreeSpecies) sMaxTreeSpecies = species + 1;
+
+ if (!success)
+ {
+ LLString name;
+ static LLStdStringHandle name_string = LLXmlTree::addAttributeString("name");
+ tree_def->getFastAttributeString(name_string, name);
+ llwarns << "Incomplete definition of tree " << name << llendl;
+ }
+ }
+
+ BOOL have_all_trees = TRUE;
+ LLString err;
+ char buffer[10];
+
+ for (S32 i=0;i<sMaxTreeSpecies;++i)
+ {
+ if (!sSpeciesTable.count(i))
+ {
+ snprintf(buffer,10," %d",i);
+ err.append(buffer);
+ have_all_trees = FALSE;
+ }
+ }
+
+ if (!have_all_trees)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[SPECIES]"] = err;
+ gViewerWindow->alertXml("ErrorUndefinedTrees", args );
+ }
+};
+
+//static
+void LLVOTree::cleanupClass()
+{
+ std::for_each(sSpeciesTable.begin(), sSpeciesTable.end(), DeletePairedPointer());
+}
+
+U32 LLVOTree::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ if ( (getVelocity().magVecSquared() > 0.f)
+ ||(getAcceleration().magVecSquared() > 0.f)
+ ||(getAngularVelocity().magVecSquared() > 0.f))
+ {
+ llinfos << "ACK! Moving tree!" << llendl;
+ setVelocity(LLVector3::zero);
+ setAcceleration(LLVector3::zero);
+ setAngularVelocity(LLVector3::zero);
+ }
+
+ if (update_type == OUT_TERSE_IMPROVED)
+ {
+ // Nothing else needs to be done for the terse message.
+ return retval;
+ }
+
+ //
+ // Load Instance-Specific data
+ //
+ if (mData)
+ {
+ mSpecies = ((U8 *)mData)[0];
+ }
+
+ if (!sSpeciesTable.count(mSpecies))
+ {
+ if (sSpeciesTable.size())
+ {
+ SpeciesMap::const_iterator it = sSpeciesTable.begin();
+ mSpecies = (*it).first;
+ }
+ }
+
+ //
+ // Load Species-Specific data
+ //
+ mTreeImagep = gImageList.getImage(sSpeciesTable[mSpecies]->mTextureID);
+ if (mTreeImagep)
+ {
+ mTreeImagep->bindTexture(0);
+ mTreeImagep->setClamp(TRUE, TRUE);
+ }
+ mBranchLength = sSpeciesTable[mSpecies]->mBranchLength;
+ mTrunkLength = sSpeciesTable[mSpecies]->mTrunkLength;
+ mLeafScale = sSpeciesTable[mSpecies]->mLeafScale;
+ mDroop = sSpeciesTable[mSpecies]->mDroop;
+ mTwist = sSpeciesTable[mSpecies]->mTwist;
+ mBranches = sSpeciesTable[mSpecies]->mBranches;
+ mDepth = sSpeciesTable[mSpecies]->mDepth;
+ mScaleStep = sSpeciesTable[mSpecies]->mScaleStep;
+ mTrunkDepth = sSpeciesTable[mSpecies]->mTrunkDepth;
+ mBillboardScale = sSpeciesTable[mSpecies]->mBillboardScale;
+ mBillboardRatio = sSpeciesTable[mSpecies]->mBillboardRatio;
+ mTrunkAspect = sSpeciesTable[mSpecies]->mTrunkAspect;
+ mBranchAspect = sSpeciesTable[mSpecies]->mBranchAspect;
+
+ return retval;
+}
+
+BOOL LLVOTree::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ const U16 FRAMES_PER_WIND_UPDATE = 20; // How many frames between wind update per tree
+ const F32 TREE_WIND_SENSITIVITY = 0.005f;
+ const F32 TREE_TRUNK_STIFFNESS = 0.1f;
+
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_TREE)))
+ {
+ return TRUE;
+ }
+
+ F32 mass_inv;
+
+ // For all tree objects, update the trunk bending with the current wind
+ // Walk sprite list in order away from viewer
+ if (!(mFrameCount % FRAMES_PER_WIND_UPDATE))
+ {
+ // If needed, Get latest wind for this tree
+ mWind = mRegionp->mWind.getVelocity(getPositionRegion());
+ }
+ mFrameCount++;
+
+ mass_inv = 1.f/(5.f + mDepth*mBranches*0.2f);
+ mTrunkVel += (mWind * mass_inv * TREE_WIND_SENSITIVITY); // Pull in direction of wind
+ mTrunkVel -= (mTrunkBend * mass_inv * TREE_TRUNK_STIFFNESS); // Restoring force in direction of trunk
+ mTrunkBend += mTrunkVel;
+ mTrunkVel *= 0.99f; // Add damping
+
+ if (mTrunkBend.magVec() > 1.f)
+ {
+ mTrunkBend.normVec();
+ }
+
+ if (mTrunkVel.magVec() > 1.f)
+ {
+ mTrunkVel.normVec();
+ }
+
+ return TRUE;
+}
+
+
+const F32 TREE_BLEND_MIN = 1.f;
+const F32 TREE_BLEND_RANGE = 1.f;
+
+
+void LLVOTree::render(LLAgent &agent)
+{
+}
+
+
+void LLVOTree::setPixelAreaAndAngle(LLAgent &agent)
+{
+ // First calculate values as for any other object (for mAppAngle)
+ LLViewerObject::setPixelAreaAndAngle(agent);
+
+ // Re-calculate mPixelArea accurately
+
+ // This should be the camera's center, as soon as we move to all region-local.
+ LLVector3 relative_position = getPositionAgent() - agent.getCameraPositionAgent();
+ F32 range = relative_position.magVec(); // ugh, square root
+
+ F32 max_scale = mBillboardScale * getMaxScale();
+ F32 area = max_scale * (max_scale*mBillboardRatio);
+
+ // Compute pixels per meter at the given range
+ F32 pixels_per_meter = gCamera->getViewHeightInPixels() /
+ (tan(gCamera->getView()) * range);
+
+ mPixelArea = (pixels_per_meter) * (pixels_per_meter) * area;
+#if 0
+ // mAppAngle is a bit of voodoo;
+ // use the one calculated LLViewerObject::setPixelAreaAndAngle above
+ // to avoid LOD miscalculations
+ mAppAngle = (F32) atan2( max_scale, range) * RAD_TO_DEG;
+#endif
+}
+
+void LLVOTree::updateTextures(LLAgent &agent)
+{
+ F32 texel_area_ratio = 1.f;
+ F32 cos_angle = 1.f;
+ if (mTreeImagep)
+ {
+ mTreeImagep->addTextureStats(mPixelArea, texel_area_ratio, cos_angle);
+ }
+
+}
+
+
+LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_TREE);
+
+ LLDrawPool *poolp = gPipeline.getPool(LLDrawPool::POOL_TREE, mTreeImagep);
+
+ // Just a placeholder for an actual object...
+ LLFace *facep = mDrawable->addFace(poolp, mTreeImagep, TRUE);
+ facep->setSize(1, 3);
+
+ gPipeline.markMaterialed(mDrawable);
+
+ updateRadius();
+
+ return mDrawable;
+}
+
+
+// Yes, I know this is bad. I'll clean this up soon. - djs 04/02/02
+const S32 LEAF_INDICES = 24;
+const S32 LEAF_VERTICES = 16;
+
+BOOL LLVOTree::updateGeometry(LLDrawable *drawable)
+{
+ U32 i, j;
+ const S32 MAX_SLICES = 32;
+
+ const F32 LEAF_LEFT = 0.52f;
+ const F32 LEAF_RIGHT = 0.98f;
+ const F32 LEAF_TOP = 1.0f;
+ const F32 LEAF_BOTTOM = 0.52f;
+
+ const F32 LEAF_WIDTH = 1.f;
+
+
+ U32 slices = MAX_SLICES;
+
+ S32 max_indices = LEAF_INDICES;
+ S32 max_vertices = LEAF_VERTICES;
+ S32 lod;
+
+ LLFace *face = drawable->getFace(0);
+
+ face->mCenterAgent = getPositionAgent();
+ face->mCenterLocal = face->mCenterAgent;
+
+ LLDrawPool *poolp = face->getPool();
+
+ if (poolp->getVertexCount())
+ {
+ return TRUE;
+ }
+
+ if (!face->getDirty())
+ {
+ return FALSE;
+ }
+
+ poolp->setDirty();
+
+ for (lod = 0; lod < 4; lod++)
+ {
+ slices = sLODSlices[lod];
+ sLODVertexOffset[lod] = max_vertices;
+ sLODVertexCount[lod] = slices*slices;
+ sLODIndexOffset[lod] = max_indices;
+ sLODIndexCount[lod] = (slices-1)*(slices-1)*6;
+ max_indices += sLODIndexCount[lod];
+ max_vertices += sLODVertexCount[lod];
+ }
+
+ LLStrider<LLVector3> vertices;
+ LLStrider<LLVector3> normals;
+ LLStrider<LLVector2> tex_coords;
+ U32 *indicesp;
+
+ face->setSize(max_vertices, max_indices);
+ face->getGeometry(vertices, normals, tex_coords, indicesp);
+
+ S32 vertex_count = 0;
+ S32 index_count = 0;
+
+ // First leaf
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+
+ *(indicesp++) = 0;
+ index_count++;
+ *(indicesp++) = 1;
+ index_count++;
+ *(indicesp++) = 2;
+ index_count++;
+
+ *(indicesp++) = 0;
+ index_count++;
+ *(indicesp++) = 3;
+ index_count++;
+ *(indicesp++) = 1;
+ index_count++;
+
+ // Same leaf, inverse winding/normals
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(1.f, 1.0f);
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(0.52f, 1.0f);
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(-0.5f*LEAF_WIDTH, 0.f, 1.f);
+ vertex_count++;
+
+ //*(tex_coords++) = LLVector2(1.f, 0.52f);
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.5f*LEAF_WIDTH, 0.f, 0.f);
+ vertex_count++;
+
+ *(indicesp++) = 4;
+ index_count++;
+ *(indicesp++) = 6;
+ index_count++;
+ *(indicesp++) = 5;
+ index_count++;
+
+ *(indicesp++) = 4;
+ index_count++;
+ *(indicesp++) = 5;
+ index_count++;
+ *(indicesp++) = 7;
+ index_count++;
+
+
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(indicesp++) = 8;
+ index_count++;
+ *(indicesp++) = 9;
+ index_count++;
+ *(indicesp++) = 10;
+ index_count++;
+
+ *(indicesp++) = 8;
+ index_count++;
+ *(indicesp++) = 11;
+ index_count++;
+ *(indicesp++) = 9;
+ index_count++;
+
+ for (i = 0; i < 4; i++)
+ {
+ *(normals++) = LLVector3(0.f, 0.f, 1.f);
+ }
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_LEFT, LEAF_TOP);
+ *(vertices++) = LLVector3(0.f, -0.5f*LEAF_WIDTH, 1.f);
+ vertex_count++;
+
+ *(tex_coords++) = LLVector2(LEAF_RIGHT, LEAF_BOTTOM);
+ *(vertices++) = LLVector3(0.f, 0.5f*LEAF_WIDTH, 0.f);
+ vertex_count++;
+
+
+ *(indicesp++) = 12;
+ index_count++;
+ *(indicesp++) = 14;
+ index_count++;
+ *(indicesp++) = 13;
+ index_count++;
+
+ *(indicesp++) = 12;
+ index_count++;
+ *(indicesp++) = 13;
+ index_count++;
+ *(indicesp++) = 15;
+ index_count++;
+
+ // Generate geometry for the cylinders
+
+ // Different LOD's
+
+ // Generate the vertices
+ // Generate the indices
+
+ for (lod = 0; lod < 4; lod++)
+ {
+ slices = sLODSlices[lod];
+ F32 base_radius = 0.65f;
+ F32 top_radius = base_radius * sSpeciesTable[mSpecies]->mTaper;
+ //llinfos << "Species " << ((U32) mSpecies) << ", taper = " << sSpeciesTable[mSpecies].mTaper << llendl;
+ //llinfos << "Droop " << mDroop << ", branchlength: " << mBranchLength << llendl;
+ F32 angle = 0;
+ F32 angle_inc = 360.f/(slices-1);
+ F32 z = 0.f;
+ F32 z_inc = 1.f;
+ if (slices > 3)
+ {
+ z_inc = 1.f/(slices - 3);
+ }
+ F32 radius = base_radius;
+
+ F32 x1,y1;
+ F32 noise_scale = sSpeciesTable[mSpecies]->mNoiseMag;
+ LLVector3 nvec;
+
+ const F32 cap_nudge = 0.1f; // Height to 'peak' the caps on top/bottom of branch
+
+ const S32 fractal_depth = 5;
+ F32 nvec_scale = 1.f * sSpeciesTable[mSpecies]->mNoiseScale;
+ F32 nvec_scalez = 4.f * sSpeciesTable[mSpecies]->mNoiseScale;
+
+ F32 tex_z_repeat = sSpeciesTable[mSpecies]->mRepeatTrunkZ;
+
+ F32 start_radius;
+ F32 nangle = 0;
+ F32 height = 1.f;
+ F32 r0;
+
+ for (i = 0; i < slices; i++)
+ {
+ if (i == 0)
+ {
+ z = - cap_nudge;
+ r0 = 0.0;
+ }
+ else if (i == (slices - 1))
+ {
+ z = 1.f + cap_nudge;//((i - 2) * z_inc) + cap_nudge;
+ r0 = 0.0;
+ }
+ else
+ {
+ z = (i - 1) * z_inc;
+ r0 = base_radius + (top_radius - base_radius)*z;
+ }
+
+ for (j = 0; j < slices; j++)
+ {
+ if (slices - 1 == j)
+ {
+ angle = 0.f;
+ }
+ else
+ {
+ angle = j*angle_inc;
+ }
+
+ nangle = angle;
+
+ x1 = cos(angle * DEG_TO_RAD);
+ y1 = sin(angle * DEG_TO_RAD);
+ LLVector2 tc;
+ // This isn't totally accurate. Should compute based on slope as well.
+ start_radius = r0 * (1.f + 1.2f*fabs(z - 0.66f*height)/height);
+ nvec.setVec( cos(nangle * DEG_TO_RAD)*start_radius*nvec_scale,
+ sin(nangle * DEG_TO_RAD)*start_radius*nvec_scale,
+ z*nvec_scalez);
+ // First and last slice at 0 radius (to bring in top/bottom of structure)
+ radius = start_radius + turbulence3((F32*)&nvec.mV, (F32)fractal_depth)*noise_scale;
+
+ if (slices - 1 == j)
+ {
+ // Not 0.5 for slight slop factor to avoid edges on leaves
+ tc = LLVector2(0.490f, (1.f - z/2.f)*tex_z_repeat);
+ }
+ else
+ {
+ tc = LLVector2((angle/360.f)*0.5f, (1.f - z/2.f)*tex_z_repeat);
+ }
+
+ *(vertices++) = LLVector3(x1*radius, y1*radius, z);
+ *(normals++) = LLVector3(x1, y1, 0.f);
+ *(tex_coords++) = tc;
+ vertex_count++;
+ }
+ }
+
+ for (i = 0; i < (slices - 1); i++)
+ {
+ for (j = 0; j < (slices - 1); j++)
+ {
+ S32 x1_offset = j+1;
+ if ((j+1) == slices)
+ {
+ x1_offset = 0;
+ }
+ // Generate the matching quads
+ *(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = j + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+
+ *(indicesp) = j + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + (i*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ *(indicesp) = x1_offset + ((i+1)*slices) + sLODVertexOffset[lod];
+ llassert(*(indicesp) < (U32)max_vertices);
+ indicesp++;
+ index_count++;
+ }
+ }
+ slices /= 2;
+ }
+
+ llassert(vertex_count == max_vertices);
+ llassert(index_count == max_indices);
+
+ return TRUE;
+}
+
+void LLVOTree::drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha)
+{
+ //
+ // Draws a tree by recursing, drawing branches and then a 'leaf' texture.
+ // If stop_level = -1, simply draws the whole tree as a billboarded texture
+ //
+
+ if (!draw_pool->getIndexCount())
+ {
+ return; // safety net
+ }
+
+ static F32 constant_twist;
+ static F32 width = 0;
+
+ //F32 length = ((scale == 1.f)? mTrunkLength:mBranchLength);
+ //F32 aspect = ((scale == 1.f)? mTrunkAspect:mBranchAspect);
+ F32 length = ((trunk_depth || (scale == 1.f))? mTrunkLength:mBranchLength);
+ F32 aspect = ((trunk_depth || (scale == 1.f))? mTrunkAspect:mBranchAspect);
+
+ constant_twist = 360.f/branches;
+
+ if (stop_level >= 0)
+ {
+ //
+ // Draw the tree using recursion
+ //
+ if (depth > stop_level)
+ {
+ {
+ llassert(sLODIndexCount[trunk_LOD] > 0);
+ width = scale * length * aspect;
+ glPushMatrix();
+ glScalef(width,width,scale * length);
+ //glDrawElements(GL_TRIANGLES, sLODIndexCount[trunk_LOD], GL_UNSIGNED_INT, draw_pool.getRawIndices() + sLODIndexOffset[trunk_LOD]);
+ glDrawRangeElements(GL_TRIANGLES,
+ sLODVertexOffset[trunk_LOD],
+ sLODVertexOffset[trunk_LOD] + sLODVertexCount[trunk_LOD]-1,
+ sLODIndexCount[trunk_LOD],
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices() + sLODIndexOffset[trunk_LOD]);
+ stop_glerror();
+ draw_pool->addIndicesDrawn(sLODIndexCount[trunk_LOD]);
+ glPopMatrix();
+ }
+
+ // Recurse to create more branches
+ for (S32 i=0; i < (S32)branches; i++)
+ {
+ glPushMatrix();
+ glTranslatef(0.f, 0.f, scale * length);
+ glRotatef((constant_twist + ((i%2==0)?twist:-twist))*i, 0.f, 0.f, 1.f);
+ glRotatef(droop, 0.f, 1.f, 0.f);
+ glRotatef(20.f, 0.f, 0.f, 1.f); // rotate 20deg about axis of new branch to add some random variation
+ drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth - 1, 0, scale*mScaleStep, twist, droop, branches, alpha);
+ glPopMatrix();
+ }
+ // Recurse to continue trunk
+ if (trunk_depth)
+ {
+ glPushMatrix();
+ glTranslatef(0.f, 0.f, scale * length);
+ glRotatef(70.5f, 0.f, 0.f, 1.f); // rotate a bit around Z when ascending
+ drawBranchPipeline(draw_pool, trunk_LOD, stop_level, depth, trunk_depth-1, scale*mScaleStep, twist, droop, branches, alpha);
+ glPopMatrix();
+ }
+ }
+ else
+ {
+ //
+ // Draw leaves as two 90 deg crossed quads with leaf textures
+ //
+ {
+ glPushMatrix();
+ //glRotatef(llFrand(50.0), llFrand(1.0), llFrand(1.0), llFrand(1.0);
+ //width = scale * (TREE_BRANCH_ASPECT + TREE_LEAF_ASPECT);
+ glScalef(scale*mLeafScale, scale*mLeafScale, scale*mLeafScale);
+ //glScalef(1.5f*width*mLeafScale,1,1.5f*scale*mLeafScale);
+// glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
+ glDrawRangeElements(GL_TRIANGLES,
+ 0,
+ LEAF_VERTICES-1,
+ LEAF_INDICES,
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices());
+ stop_glerror();
+ draw_pool->addIndicesDrawn(LEAF_INDICES);
+ glPopMatrix();
+ }
+ }
+ }
+ else
+ {
+ //
+ // Draw the tree as a single billboard texture
+ //
+
+ glMatrixMode(GL_TEXTURE);
+ glPushMatrix();
+ glTranslatef(0.0, -0.5, 0.0);
+ glMatrixMode(GL_MODELVIEW);
+ {
+ glPushMatrix();
+ glScalef(mBillboardScale*mBillboardRatio, mBillboardScale*mBillboardRatio, mBillboardScale);
+// glDrawElements(GL_TRIANGLES, LEAF_INDICES, GL_UNSIGNED_INT, draw_pool.getRawIndices());
+ glDrawRangeElements(GL_TRIANGLES,
+ 0,
+ LEAF_VERTICES-1,
+ LEAF_INDICES,
+ GL_UNSIGNED_INT,
+ draw_pool->getRawIndices());
+ stop_glerror();
+ draw_pool->addIndicesDrawn(LEAF_INDICES);
+ glPopMatrix();
+ }
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ }
+}
+
+void LLVOTree::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mDrawable->setRadius(32.0f);
+}
diff --git a/indra/newview/llvotree.h b/indra/newview/llvotree.h
new file mode 100644
index 0000000000..f962516803
--- /dev/null
+++ b/indra/newview/llvotree.h
@@ -0,0 +1,118 @@
+/**
+ * @file llvotree.h
+ * @brief LLVOTree class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTREE_H
+#define LL_LLVOTREE_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include "xform.h"
+
+class LLFace;
+class LLDrawPool;
+
+
+class LLVOTree : public LLViewerObject
+{
+public:
+ LLVOTree(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTree();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ // Graphical stuff for objects - maybe broken out into render class later?
+ /*virtual*/ void render(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ void updateRadius();
+
+ void drawBranchPipeline(LLDrawPool *draw_pool, S32 trunk_LOD, S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha);
+
+ void drawBranch(S32 stop_level, U16 depth, U16 trunk_depth, F32 scale, F32 twist, F32 droop, F32 branches, F32 alpha, BOOL draw_leaves);
+
+ static S32 sMaxTreeSpecies;
+
+ struct TreeSpeciesData
+ {
+ LLUUID mTextureID;
+
+ F32 mBranchLength; // Scale (length) of tree branches
+ F32 mDroop; // Droop from vertical (degrees) at each branch recursion
+ F32 mTwist; // Twist
+ F32 mBranches; // Number of branches emitted at each recursion level
+ U8 mDepth; // Number of recursions to tips of branches
+ F32 mScaleStep; // Multiplier for scale at each recursion level
+ U8 mTrunkDepth;
+
+ F32 mLeafScale; // Scales leaf texture when rendering
+ F32 mTrunkLength; // Scales branch diameters when rendering
+ F32 mBillboardScale; // Scales the billboard representation
+ F32 mBillboardRatio; // Height to width aspect ratio
+ F32 mTrunkAspect;
+ F32 mBranchAspect;
+ F32 mRandomLeafRotate;
+ F32 mNoiseScale; // Scaling of noise function in perlin space (norm = 1.0)
+ F32 mNoiseMag; // amount of perlin noise to deform by (0 = none)
+ F32 mTaper; // amount of perlin noise to deform by (0 = none)
+ F32 mRepeatTrunkZ; // Times to repeat the trunk texture vertically along trunk
+ };
+
+ static F32 sTreeFactor; // Tree level of detail factor
+
+ friend class LLDrawPoolTree;
+protected:
+ LLVector3 mTrunkBend; // Accumulated wind (used for blowing trees)
+ LLVector3 mTrunkVel; //
+ LLVector3 mWind;
+
+ LLPointer<LLViewerImage> mTreeImagep; // Pointer to proper tree image
+
+ U8 mSpecies; // Species of tree
+ F32 mBranchLength; // Scale (length) of tree branches
+ F32 mTrunkLength; // Trunk length (first recursion)
+ F32 mDroop; // Droop from vertical (degrees) at each branch recursion
+ F32 mTwist; // Twist
+ F32 mBranches; // Number of branches emitted at each recursion level
+ U8 mDepth; // Number of recursions to tips of branches
+ F32 mScaleStep; // Multiplier for scale at each recursion level
+ U8 mTrunkDepth;
+
+ F32 mLeafScale; // Scales leaf texture when rendering
+
+ F32 mBillboardScale; // How big to draw the billboard?
+ F32 mBillboardRatio; // Height to width ratio of billboard
+ F32 mTrunkAspect; // Ratio between width/length of trunk
+ F32 mBranchAspect; // Ratio between width/length of branch
+ F32 mRandomLeafRotate; // How much to randomly rotate leaves about arbitrary axis
+
+ U32 mFrameCount;
+
+ typedef std::map<U32, TreeSpeciesData*> SpeciesMap;
+ static SpeciesMap sSpeciesTable;
+
+ static S32 sLODIndexOffset[4];
+ static S32 sLODIndexCount[4];
+ static S32 sLODVertexOffset[4];
+ static S32 sLODVertexCount[4];
+ static S32 sLODSlices[4];
+ static F32 sLODAngles[4];
+};
+
+#endif
diff --git a/indra/newview/llvotreenew.h b/indra/newview/llvotreenew.h
new file mode 100644
index 0000000000..e027c3860a
--- /dev/null
+++ b/indra/newview/llvotreenew.h
@@ -0,0 +1,200 @@
+/**
+ * @file llvotreenew.h
+ * @brief LLVOTreeNew class header file
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOTREENEW_H
+#define LL_LLVOTREENEW_H
+
+#include "llviewerobject.h"
+#include "lldarray.h"
+#include "xform.h"
+
+#include "lltreeparams.h"
+#include "llstrider.h"
+#include "v2math.h"
+#include "v3math.h"
+#include "llviewerimage.h"
+
+class LLFace;
+class LLDrawPool;
+
+// number of static arrays created
+const U8 MAX_SPECIES = 16; // max species of trees
+const U8 MAX_PARTS = 15; // trunk, 2 or 3 branches per species?
+const U8 MAX_RES = 6; // max # cross sections for a branch curve
+const U8 MAX_FLARE = 6; // max # cross sections for flare of trunk
+const U8 MAX_LEVELS = 3;
+
+// initial vertex array allocations
+const U32 NUM_INIT_VERTS = 5000; // number of vertices/normals/texcoords
+const U32 NUM_INIT_INDICES = 15000; // number of indices to vert array (3 vertices per triangle, roughly 3x)
+const U32 NUM_TIMES_TO_DOUBLE = 2; // if we go over initial allocations, num times to double each step
+
+// for finding the closest parts...
+
+// the parts are searched based on:
+const F32 MAX_LOBES_DIFF = 2;
+const F32 MAX_LOBEDEPTH_DIFF = .3f;
+const F32 MAX_CURVEBACK_DIFF = 20.0f;
+const F32 MAX_CURVE_DIFF = 15.0f;
+const F32 MAX_CURVE_V_DIFF = 20.0f;
+
+const F32 CURVEV_DIVIDER = 10.0f; // curveV/CURVEV_DIVIDER = # branch variances...
+const U8 MAX_VARS = 3; // max number of variations of branches
+
+const U8 MAX_RAND_NUMS = 100; // max number of rand numbers to pregenerate and store
+
+// texture params
+const F32 WIDTH_OF_BARK = .48f;
+
+class LLVOTreeNew : public LLViewerObject
+{
+public:
+
+ // Some random number generators using the pre-generated random numbers
+ // return +- negPos
+ static S32 llrand_signed(S32 negPos)
+ {
+ return (gLindenLabRandomNumber.llrand((U32)negPos * 2) - negPos);
+ };
+
+ static S32 llrand_signed(S32 negPos, U32 index)
+ {
+ return lltrunc((sRandNums[index % MAX_RAND_NUMS] * (negPos * 2.0f) - negPos));
+ };
+
+ static S32 llrand_unsigned(S32 pos, U32 index)
+ {
+ return lltrunc((sRandNums[index % MAX_RAND_NUMS] * pos));
+ };
+
+ // return +- negPos
+ static F32 llfrand_signed(F32 negPos)
+ {
+ return (gLindenLabRandomNumber.llfrand(negPos * 2.0f) - negPos);
+ };
+
+ static F32 llfrand_signed(F32 negPos, U32 index)
+ {
+ return (sRandNums[index % MAX_RAND_NUMS] * negPos * 2.0f) - negPos;
+ };
+
+ static F32 llfrand_unsigned(F32 pos, U32 index)
+ {
+ return sRandNums[index % MAX_RAND_NUMS] * pos;
+ };
+
+ // return between 0-pos
+ static F32 llfrand_unsigned(F32 pos)
+ {
+ return gLindenLabRandomNumber.llfrand(pos);
+ };
+
+ static void cleanupTextures() {}; // not needed anymore
+
+ struct TreePart
+ {
+ F32 mRadius; // scale x/y
+ F32 mLength; // scale z
+ F32 mCurve;
+ F32 mCurveV;
+ F32 mCurveRes;
+ F32 mCurveBack;
+ U8 mLobes;
+ F32 mLobeDepth;
+ U8 mLevel;
+ U32 mNumTris;
+ U8 mVertsPerSection;
+ U8 mNumVariants;
+
+ // first index into the drawpool arrays for this particular branch
+ U32 mIndiceIndex[MAX_VARS];
+ U32 mOffsets[MAX_VARS][MAX_RES]; // offsets for the partial branch pieces
+ // local section frames for this branch
+ LLMatrix4 mFrames[MAX_VARS][(MAX_RES*(MAX_RES + 1))/2]; // (0...n) + (1...n) + ... + (n-1..n)
+ LLDynamicArray<LLVector3> mFaceNormals;
+
+ };
+
+ LLVOTreeNew(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOTreeNew();
+
+ /*virtual*/
+ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ void render(LLAgent &agent);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+
+ F32 CalcZStep(TreePart *part, U8 section);
+
+ void createPart(U8 level, F32 length, F32 radius, LLStrider<LLVector3> &vertices, LLStrider<LLVector3> &normals,
+ LLStrider<LLVector2> &tex_coords, U32 *indices,
+ U32 &curVertexIndex, U32 &curTexCoordIndex,
+ U32 &curNormalIndex, U32 &curIndiceIndex);
+
+ S32 findSimilarPart(U8 level);
+
+ F32 CalculateSectionRadius(U8 level, F32 y, F32 stemLength, F32 stemRadius);
+ //F32 CalculateVerticalAttraction(U8 level, LLMatrix4 &sectionFrame);
+
+ void createSection(LLMatrix4 &frame, TreePart *part, F32 sectionRadius, F32 stemZ,
+ LLStrider<LLVector3> &vertices, LLStrider<LLVector2> &tex_coords, U32 *indices,
+ U32 &curVertexIndex, U32 &curTexCoordIndex, U32 &curIndiceIndex, U8 curSection, BOOL firstBranch);
+
+ void genIndicesAndFaceNormalsForLastSection(TreePart *part, U8 numVerts, LLStrider<LLVector3> &vertices, U32 curVertexIndex, U32 *indices, U32 &curIndiceIndex, BOOL firstBranch);
+
+ void genVertexNormals(TreePart *part, LLStrider<LLVector3> &normals, U8 numSections, U32 curNormalOffset);
+
+ void drawTree(LLDrawPool &draw_pool, const LLMatrix4 &frame, U8 level, F32 offsetChild, F32 curLength, F32 parentLength, F32 curRadius, F32 parentRadius, U8 part, U8 variant, U8 startSection);
+ void drawTree(LLDrawPool &draw_pool);
+
+
+ //LLTreeParams mParams;
+ U8 mSpecies;
+ LLPointer<LLViewerImage> mTreeImagep;
+ LLMatrix4 mTrunkFlareFrames[MAX_FLARE];
+ F32 mSegSplitsError[3];
+ U32 mRandOffset[MAX_LEVELS];
+
+ U32 mNumTrisDrawn;
+ U32 mTotalIndices;
+ U32 mTotalVerts;
+
+ static void initClass();
+
+ // tree params
+ static LLTreeParams sParameters;
+
+ // next indexes used to drawpool arrays
+ static U32 sNextVertexIndex[MAX_SPECIES];
+ static U32 sNextIndiceIndex[MAX_SPECIES];
+
+ // tree parts
+ static U32 sNextPartIndex[MAX_PARTS];
+ static TreePart sTreeParts[MAX_SPECIES][MAX_PARTS];
+
+ // species images
+ static LLUUID sTreeImageIDs[MAX_SPECIES];
+
+ // random numbers
+ static F32 sRandNums[MAX_RAND_NUMS];
+
+ // usage data
+ static U32 sTreePartsUsed[MAX_SPECIES][MAX_PARTS][MAX_VARS];
+
+
+};
+
+#endif
diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp
new file mode 100644
index 0000000000..03074d8a73
--- /dev/null
+++ b/indra/newview/llvovolume.cpp
@@ -0,0 +1,2027 @@
+/**
+ * @file llvovolume.cpp
+ * @brief LLVOVolume class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// A "volume" is a box, cylinder, sphere, or other primitive shape.
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvovolume.h"
+
+#include "llviewercontrol.h"
+#include "lldir.h"
+#include "llflexibleobject.h"
+#include "llmaterialtable.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "llvolumemgr.h"
+#include "llvolumemessage.h"
+#include "material_codes.h"
+#include "message.h"
+#include "object_flags.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolbump.h"
+#include "llface.h"
+
+// TEMP HACK ventrella
+#include "llhudmanager.h"
+#include "llflexibleobject.h"
+#include "llanimalcontrols.h"
+
+#include "llsky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llviewertextureanim.h"
+#include "llworld.h"
+#include "llselectmgr.h"
+#include "pipeline.h"
+
+const S32 MIN_QUIET_FRAMES_COALESCE = 30;
+
+//#define LLDEBUG_DISPLAY_LODS 1
+
+BOOL gAnimateTextures = TRUE;
+
+F32 LLVOVolume::sLODFactor = 1.f;
+F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectively disables the LOD transition slop
+F32 LLVOVolume::sLODComplexityDistanceBias = 0.0f;//Changing this to zero makes all prims LOD equally regardless of complexity
+F32 LLVOVolume::sDistanceFactor = 1.0f;
+S32 LLVOVolume::sNumLODChanges = 0;
+
+LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+ : LLViewerObject(id, pcode, regionp),
+ mVolumeImpl(NULL)
+{
+ mRelativeXform.identity();
+ mRelativeXformInvTrans.identity();
+
+ mLOD = MIN_LOD;
+ mInited = FALSE;
+ mAllTEsSame = FALSE;
+ mTextureAnimp = NULL;
+ mGlobalVolume = FALSE;
+
+ mTextureAnimp = NULL;
+ mAllTEsSame = FALSE;
+ mVObjRadius = LLVector3(1,1,0.5f).magVec();
+ mNumFaces = 0;
+}
+
+LLVOVolume::~LLVOVolume()
+{
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+}
+
+
+// static
+void LLVOVolume::initClass()
+{
+}
+
+
+U32 LLVOVolume::processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, EObjectUpdateType update_type,
+ LLDataPacker *dp)
+{
+ LLColor4U color;
+
+ // Do base class updates...
+ U32 retval = LLViewerObject::processUpdateMessage(mesgsys, user_data, block_num, update_type, dp);
+
+ if (!dp)
+ {
+ if (update_type == OUT_FULL)
+ {
+ ////////////////////////////////
+ //
+ // Unpack texture animation data
+ //
+ //
+
+ if (mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureAnim))
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(mesgsys, block_num);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+
+ // Unpack volume data
+ LLVolumeParams volume_params;
+ LLVolumeMessage::unpackVolumeParams(&volume_params, mesgsys, _PREHASH_ObjectData, block_num);
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ }
+
+ // Sigh, this needs to be done AFTER the volume is set as well, otherwise bad stuff happens...
+ ////////////////////////////
+ //
+ // Unpack texture entry data
+ //
+ if (unpackTEMessage(mesgsys, _PREHASH_ObjectData, block_num) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ else
+ {
+ // CORY TO DO: Figure out how to get the value here
+ if (update_type != OUT_TERSE_IMPROVED)
+ {
+ LLVolumeParams volume_params;
+ BOOL res = LLVolumeMessage::unpackVolumeParams(&volume_params, *dp);
+ if (!res)
+ {
+ llwarns << "Bogus volume parameters in object " << getID() << llendl;
+ llwarns << getRegion()->getOriginGlobal() << llendl;
+ }
+
+ if (setVolume(volume_params, 0))
+ {
+ markForUpdate(TRUE);
+ }
+ S32 res2 = unpackTEMessage(*dp);
+ if (TEM_INVALID == res2)
+ {
+ // Well, crap, there's something bogus in the data that we're unpacking.
+ dp->dumpBufferToLog();
+ llwarns << "Flushing cache files" << llendl;
+ char mask[LL_MAX_PATH];
+ sprintf(mask, "%s*.slc", gDirUtilp->getDirDelimiter().c_str());
+ gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,"").c_str(),mask);
+// llerrs << "Bogus TE data in " << getID() << ", crashing!" << llendl;
+ llwarns << "Bogus TE data in " << getID() << llendl;
+ }
+ else if (res2 & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+
+ U32 value = dp->getPassFlags();
+
+ if (value & 0x40)
+ {
+ if (!mTextureAnimp)
+ {
+ mTextureAnimp = new LLViewerTextureAnim();
+ }
+ else
+ {
+ if (!(mTextureAnimp->mMode & LLTextureAnim::SMOOTH))
+ {
+ mTextureAnimp->reset();
+ }
+ }
+ mTextureAnimp->unpackTAMessage(*dp);
+ }
+ else
+ {
+ delete mTextureAnimp;
+ mTextureAnimp = NULL;
+ }
+ }
+ else
+ {
+ S32 texture_length = mesgsys->getSizeFast(_PREHASH_ObjectData, block_num, _PREHASH_TextureEntry);
+ if (texture_length)
+ {
+ U8 tdpbuffer[1024];
+ LLDataPackerBinaryBuffer tdp(tdpbuffer, 1024);
+ mesgsys->getBinaryDataFast(_PREHASH_ObjectData, _PREHASH_TextureEntry, tdpbuffer, 0, block_num);
+ if ( unpackTEMessage(tdp) & (TEM_CHANGE_TEXTURE|TEM_CHANGE_COLOR))
+ {
+ updateTEData();
+ }
+ }
+ }
+ }
+
+ return retval;
+}
+
+
+BOOL LLVOVolume::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ LLViewerObject::idleUpdate(agent, world, time);
+
+ ///////////////////////
+ //
+ // Do texture animation stuff
+ //
+
+ if (mTextureAnimp && gAnimateTextures)
+ {
+ F32 off_s, off_t, scale_s, scale_t, rot;
+ S32 result;
+ if ((result = mTextureAnimp->animateTextures(off_s, off_t, scale_s, scale_t, rot)))
+ {
+ U8 has_bump = 0;
+ if (mTextureAnimp->mFace <= -1)
+ {
+ S32 face;
+ for (face = 0; face < getNumTEs(); face++)
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(face, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(face, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(face, rot);
+ }
+ has_bump |= getTE(face)->getBumpmap();
+ }
+ }
+ else if (mTextureAnimp->mFace < getNumTEs())
+ {
+ if (result & LLViewerTextureAnim::TRANSLATE)
+ {
+ setTEOffset(mTextureAnimp->mFace, off_s, off_t);
+ }
+ if (result & LLViewerTextureAnim::SCALE)
+ {
+ setTEScale(mTextureAnimp->mFace, scale_s, scale_t);
+ }
+ if (result & LLViewerTextureAnim::ROTATE)
+ {
+ setTERotation(mTextureAnimp->mFace, rot);
+ }
+ has_bump |= getTE(mTextureAnimp->mFace)->getBumpmap();
+ }
+// mFaceMappingChanged = TRUE;
+ if (mDrawable->isVisible())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_TCOORD, TRUE);
+ }
+ }
+ }
+
+ // Dispatch to implementation
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->doIdleUpdate(agent, world, time);
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateTextures(LLAgent &agent)
+{
+
+}
+
+//static
+F32 LLVOVolume::getTextureVirtualSize(const LLFace* face)
+{
+ //LLVector2 tdim = face->mTexExtents[1] - face->mTexExtents[0];
+ //F32 pixel_area = 1.f/llmin(llmax(tdim.mV[0] * tdim.mV[1], 1.f), 10.f);
+ LLVector3 cross_vec = (face->mExtents[1] - face->mExtents[0]);
+
+
+ LLVector3 lookAt = (face->getPositionAgent()-gCamera->getOrigin());
+ F32 dist = lookAt.normVec();
+
+ F32 face_area;
+
+ if (face->isState(LLFace::GLOBAL))
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1]*fabsf(lookAt.mV[2]) +
+ cross_vec.mV[1]*cross_vec.mV[2]*fabsf(lookAt.mV[0]) +
+ cross_vec.mV[0]*cross_vec.mV[2]*fabsf(lookAt.mV[1]);
+ }
+ else
+ {
+ face_area = cross_vec.mV[0]*cross_vec.mV[1] +
+ cross_vec.mV[1]*cross_vec.mV[2] +
+ cross_vec.mV[0]*cross_vec.mV[2];
+ }
+
+ if (face_area <= 0)
+ {
+ return 0.f;
+ }
+
+ F32 view = llmax(lookAt*gCamera->getAtAxis(), 0.5f);
+ F32 dist_ramp = dist * view/face_area;
+ //ramp down distance for things closer than 16 m * lookAt
+ dist /= dist_ramp;
+ dist *= dist;
+ dist *= dist_ramp;
+
+ F32 dist_ratio = face_area / llmax(dist, 0.1f);
+ F32 pixel_area = dist_ratio*gCamera->getScreenPixelArea();
+
+ return view*pixel_area;
+}
+
+void LLVOVolume::updateTextures(S32 lod)
+{
+ // Update the image levels of all textures...
+ // First we do some quick checks.
+
+ // This doesn't take into account whether the object is in front
+ // or behind...
+
+ if (LLViewerImage::sDontLoadVolumeTextures || mDrawable.isNull() || !mDrawable->isVisible())
+ {
+ return;
+ }
+
+ const S32 num_faces = mDrawable->getNumFaces();
+
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLFace* face = mDrawable->getFace(i);
+ const LLTextureEntry *te = face->getTextureEntry();
+ LLViewerImage *imagep = face->getTexture();
+
+ if (!imagep || !te)
+ {
+ continue;
+ }
+
+ F32 vsize;
+
+ if (isHUDAttachment())
+ {
+ vsize = (F32) (imagep->getWidth(0) * imagep->getHeight(0));
+ imagep->setBoostLevel(LLViewerImage::BOOST_HUD);
+ }
+ else
+ {
+ vsize = getTextureVirtualSize(face);
+ }
+
+ imagep->addTextureStats(vsize);
+
+
+ U8 bump = te->getBumpmap();
+ if( te && bump)
+ {
+ gBumpImageList.addTextureStats( bump, imagep->getID(), vsize, 1, 1);
+ }
+ }
+}
+
+BOOL LLVOVolume::isActive() const
+{
+ return !mStatic || mTextureAnimp || isAttachment() || (mVolumeImpl && mVolumeImpl->isActive());
+}
+
+BOOL LLVOVolume::setMaterial(const U8 material)
+{
+ BOOL res = LLViewerObject::setMaterial(material);
+ if (res)
+ {
+ // for deprecated LL_MCODE_LIGHT
+ if (mDrawable.notNull())
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_LIGHTING, TRUE);
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::setTexture(const S32 face)
+{
+ llassert(face < getNumTEs());
+ LLViewerImage::bindTexture(getTEImage(face));
+}
+
+void LLVOVolume::setScale(const LLVector3 &scale, BOOL damped)
+{
+ if (scale != getScale())
+ {
+ // store local radius
+ LLViewerObject::setScale(scale);
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetScale(scale, damped);
+ }
+
+ updateRadius();
+
+ //since drawable transforms do not include scale, changing volume scale
+ //requires an immediate rebuild of volume verts.
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+}
+
+LLFace* LLVOVolume::addFace(S32 f)
+{
+ const LLTextureEntry* te = getTE(f);
+ LLViewerImage* imagep = getTEImage(f);
+ LLDrawPool* poolp;
+ if (isHUDAttachment())
+ {
+ poolp = gPipeline.getPool(LLDrawPool::POOL_HUD);
+ }
+ else
+ {
+ poolp = LLPipeline::getPoolFromTE(te, imagep);
+ }
+ return mDrawable->addFace(poolp, imagep);
+}
+
+LLDrawable *LLVOVolume::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_VOLUME);
+
+ S32 max_tes_to_set = calcAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < max_tes_to_set; i++)
+ {
+ LLFace* face = addFace(i);
+ // JC - should there be a setViewerObject(this) call here?
+ face->setTEOffset(i);
+ }
+ mNumFaces = max_tes_to_set;
+
+ if (isAttachment())
+ {
+ mDrawable->makeActive();
+ }
+
+ if (getIsLight())
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+
+ updateRadius();
+ mDrawable->updateDistance(*gCamera);
+
+ return mDrawable;
+}
+
+
+BOOL LLVOVolume::setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume)
+{
+ // Check if we need to change implementations
+ bool is_flexible = (volume_params.getPathParams().getCurveType() == LL_PCODE_PATH_FLEXIBLE);
+ if (is_flexible)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, false);
+ if (!mVolumeImpl)
+ {
+ LLFlexibleObjectData* data = (LLFlexibleObjectData*)getParameterEntry(LLNetworkData::PARAMS_FLEXIBLE);
+ mVolumeImpl = new LLVolumeImplFlexible(this, data);
+ }
+ }
+ else
+ {
+ // Mark the parameter not in use
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, false);
+ if (mVolumeImpl)
+ {
+ delete mVolumeImpl;
+ mVolumeImpl = NULL;
+ if (mDrawable.notNull())
+ {
+ // Undo the damage we did to this matrix
+ mDrawable->updateXform(FALSE);
+ }
+ }
+ }
+ mGlobalVolume = (mVolumeImpl && mVolumeImpl->isVolumeGlobal());
+
+ //MSMSM Recompute LOD here in case the object was just created,
+ // its LOD might be incorrectly set to minumum detail...
+ calcLOD();
+
+ if (LLPrimitive::setVolume(volume_params, mLOD, (mVolumeImpl && mVolumeImpl->isVolumeUnique())))
+ {
+ mFaceMappingChanged = TRUE;
+
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onSetVolume(volume_params, detail);
+ }
+
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+F32 LLVOVolume::computeLODProfilePathComplexityBias(){
+ //compute a complexity cost from 0 to 1.0 where the 'simplest' prim has a cost of 0.0
+ // and the 'heaviest' prim has a cost of 1.0
+// LLVolume* volume = getVolume();
+ F32 complexity = 0.0f;
+// const LLVolumeParams& params = volume->getParams();
+// U8 type = volume->getPathType();
+// U8 pcode = this->getPCode();
+// U8 proftype = volume->getProfileType();
+
+ //if(params.getHollow()>0.0f){// || (proftype == 1) || (proftype == 0)){
+ //If it is hollow, or a cube/pyramid(subdivided), the complexity is roughly doubled
+ // complexity+=0.5f;
+ //}
+
+ if(this->getVolume()->getProfile().mParams.getCurveType()==LL_PCODE_PROFILE_SQUARE &&
+ this->getVolume()->getPath().mParams.getCurveType()==LL_PCODE_PATH_LINE)
+ {
+ //Object is a cube so bias it heavily since cubes are subdivided alot.
+// this->setDebugText("CUBE");
+ complexity += 1.0f;
+ }
+
+// if(params.getTwist() != params.getTwistBegin()){
+ //if there is twist.. the complexity is bumped
+// complexity+=0.25f;
+// }
+// if(type != LL_PCODE_PATH_LINE)//If the path is not a line it is more complex
+// complexity+=0.2f;
+ return complexity * sLODComplexityDistanceBias;
+}
+
+S32 LLVOVolume::computeLODDetail(F32 distance, F32 radius)
+{
+ S32 cur_detail;
+ // We've got LOD in the profile, and in the twist. Use radius.
+ F32 tan_angle = (LLVOVolume::sLODFactor*radius)/distance;
+ cur_detail = LLVolumeLODGroup::getDetailFromTan(tan_angle);
+ return cur_detail;
+}
+
+BOOL LLVOVolume::calcLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+ S32 cur_detail = 0;
+ /*if (isHUDAttachment())
+ {
+ cur_detail = LLVolumeLODGroup::NUM_LODS-1; // max detail
+ }
+ else*/
+ {
+ F32 radius = (mVolumep->mLODScaleBias.scaledVec(getScale())).magVec();
+ F32 distance = mDrawable->mDistanceWRTCamera;
+ distance *= sDistanceFactor;
+
+ F32 rampDist = LLVOVolume::sLODFactor * 2;
+
+ if (distance < rampDist)
+ {
+ // Boost LOD when you're REALLY close
+ distance *= 1.0f/rampDist;
+ distance *= distance;
+ distance *= rampDist;
+ }
+ else
+ {
+ //Now adjust the computed distance by some factor based on the geometric complexity of the primitive
+ distance += computeLODProfilePathComplexityBias();
+ }
+ // Compensate for field of view changing on FOV zoom.
+ distance *= gCamera->getView();
+
+ cur_detail = computeLODDetail(distance, radius);
+
+ //update textures with what the real LOD is
+ updateTextures(cur_detail);
+
+ if(cur_detail != mLOD)
+ {
+ // Here we test whether the LOD is increasing or decreasing to introduce a slop factor
+ if(cur_detail < mLOD)
+ {
+ // Viewer is moving away from the object
+ // so bias our LOD by adding a fixed amount to the distance.
+ // This will reduce the problem of LOD twitching when the
+ // user makes slight movements near the LOD transition threshhold.
+ F32 test_distance = distance - (distance*sLODSlopDistanceFactor/(1.0f+sLODFactor));
+ if(test_distance < 0.0f) test_distance = 0.0f;
+ S32 potential_detail = computeLODDetail( test_distance, radius );
+ if(potential_detail >= mLOD )
+ { //The LOD has truly not changed
+ cur_detail = mLOD;
+ }
+ }
+ }
+ }
+
+ if (cur_detail != mLOD)
+ {
+ mAppAngle = (F32) atan2( mDrawable->getRadius(), mDrawable->mDistanceWRTCamera) * RAD_TO_DEG;
+ mLOD = cur_detail;
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::updateLOD()
+{
+ if (mDrawable.isNull())
+ {
+ return FALSE;
+ }
+
+ BOOL lod_changed = calcLOD();
+
+#if LLDEBUG_DISPLAY_LODS
+ //MS Enable this to display LOD numbers on objects
+ std::ostringstream msg;
+ msg << cur_detail;//((cur_detail<mLOD)?"-":cur_detail==mLOD?"=":"+") << (int)cur_detail << " , " << mDrawable->mDistanceWRTCamera << " , " << ((LLVOVolume::sLODFactor*mVObjRadius)/mDrawable->mDistanceWRTCamera);
+ this->setDebugText(msg.str());
+#endif
+
+ if (lod_changed)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, FALSE);
+ mLODChanged = TRUE;
+ }
+
+ return lod_changed;
+}
+
+BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp)
+{
+ if (!LLViewerObject::setDrawableParent(parentp))
+ {
+ // no change in drawable parent
+ return FALSE;
+ }
+
+ if (!mDrawable->isRoot())
+ {
+ // parent is dynamic, so I'll need to share its drawable, must rebuild to share drawables
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+
+ if (mDrawable->isActive() && !parentp->isActive())
+ {
+ parentp->makeActive();
+ }
+ else if (mDrawable->isStatic() && parentp->isActive())
+ {
+ mDrawable->makeActive();
+ }
+ }
+
+ return TRUE;
+}
+
+void LLVOVolume::updateFaceFlags()
+{
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ BOOL fullbright = getTE(i)->getFullbright();
+ face->clearState(LLFace::FULLBRIGHT | LLFace::HUD_RENDER | LLFace::LIGHT);
+
+ if (fullbright || (mMaterial == LL_MCODE_LIGHT))
+ {
+ face->setState(LLFace::FULLBRIGHT);
+ }
+ if (mDrawable->isLight())
+ {
+ face->setState(LLFace::LIGHT);
+ }
+ if (isHUDAttachment())
+ {
+ face->setState(LLFace::HUD_RENDER);
+ }
+ if (getAllTEsSame())
+ {
+ break; // only 1 face
+ }
+ }
+}
+
+// NOTE: regenFaces() MUST be followed by genTriangles()!
+void LLVOVolume::regenFaces()
+{
+ // remove existing faces
+ // use mDrawable->getVOVolume() in case of shared drawables
+ mDrawable->getVOVolume()->deleteFaces(this);
+ mFaceIndexOffset = mDrawable->getNumFaces();
+ // add new faces
+ mNumFaces = getAllTEsSame() ? 1 : getNumTEs();
+ for (S32 i = 0; i < mNumFaces; i++)
+ {
+ LLFace* facep = addFace(i);
+ facep->setViewerObject(this);
+ facep->setTEOffset(i);
+ }
+ // Need to do this as texture entries may not correspond to faces any more!
+ mDrawable->updateTexture();
+ gPipeline.markMaterialed(mDrawable);
+}
+
+BOOL LLVOVolume::genTriangles(BOOL force_global)
+{
+ BOOL res = TRUE;
+
+ LLVector3 min,max;
+
+ if (getAllTEsSame())
+ {
+ setupSingleFace(mFaceIndexOffset);
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = getVolume()->getNumFaces();
+ res = face->genVolumeTriangles(*getVolume(), 0, num_faces-1,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ mWereAllTEsSame = TRUE;
+ }
+ else
+ {
+ for (S32 i = 0; i < getVolume()->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ res &= face->genVolumeTriangles(*getVolume(), i,
+ mRelativeXform, mRelativeXformInvTrans,
+ mGlobalVolume | force_global);
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ if (i == 0)
+ {
+ min = face->mExtents[0];
+ max = face->mExtents[1];
+ }
+ else
+ {
+ for (U32 i = 0; i < 3; i++)
+ {
+ if (face->mExtents[0].mV[i] < min.mV[i])
+ {
+ min.mV[i] = face->mExtents[0].mV[i];
+ }
+ if (face->mExtents[1].mV[i] > max.mV[i])
+ {
+ max.mV[i] = face->mExtents[1].mV[i];
+ }
+ }
+ }
+ }
+ }
+ mWereAllTEsSame = FALSE;
+ }
+
+ if (mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ mDrawable->setSpatialExtents(min,max);
+ if (!isVolumeGlobal())
+ {
+ mDrawable->setPositionGroup((min+max)*0.5f);
+ }
+ else
+ {
+ mDrawable->setPositionGroup(getPosition());
+ }
+
+ updateRadius();
+ mDrawable->updateBinRadius();
+ mDrawable->movePartition();
+ }
+
+ return res;
+}
+
+void LLVOVolume::updateRelativeXform(BOOL global_volume)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->updateRelativeXform(global_volume);
+ return;
+ }
+
+ LLDrawable* drawable = mDrawable;
+
+ if (drawable->isActive())
+ {
+ // setup relative transforms
+ LLQuaternion delta_rot;
+ LLVector3 delta_pos, delta_scale;
+
+ //matrix from local space to parent relative/global space
+ delta_rot = drawable->isSpatialRoot() ? LLQuaternion() : mDrawable->getRotation();
+ delta_pos = drawable->isSpatialRoot() ? LLVector3(0,0,0) : mDrawable->getPosition();
+ delta_scale = mDrawable->getScale();
+
+ // Vertex transform (4x4)
+ LLVector3 x_axis = LLVector3(delta_scale.mV[VX], 0.f, 0.f) * delta_rot;
+ LLVector3 y_axis = LLVector3(0.f, delta_scale.mV[VY], 0.f) * delta_rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, delta_scale.mV[VZ]) * delta_rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(delta_pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+ else
+ {
+ LLVector3 pos = getPosition();
+ LLVector3 scale = getScale();
+ LLQuaternion rot = getRotation();
+
+ if (mParent)
+ {
+ pos *= mParent->getRotation();
+ pos += mParent->getPosition();
+ rot *= mParent->getRotation();
+ }
+
+ LLViewerRegion* region = getRegion();
+ pos += region->getOriginAgent();
+
+ LLVector3 x_axis = LLVector3(scale.mV[VX], 0.f, 0.f) * rot;
+ LLVector3 y_axis = LLVector3(0.f, scale.mV[VY], 0.f) * rot;
+ LLVector3 z_axis = LLVector3(0.f, 0.f, scale.mV[VZ]) * rot;
+
+ mRelativeXform.initRows(LLVector4(x_axis, 0.f),
+ LLVector4(y_axis, 0.f),
+ LLVector4(z_axis, 0.f),
+ LLVector4(pos, 1.f));
+
+ x_axis.normVec();
+ y_axis.normVec();
+ z_axis.normVec();
+
+ mRelativeXformInvTrans.setRows(x_axis, y_axis, z_axis);
+ }
+}
+
+BOOL LLVOVolume::updateGeometry(LLDrawable *drawable)
+{
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES);
+
+ if (mVolumeImpl != NULL)
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_FLEX);
+ BOOL res = mVolumeImpl->doUpdateGeometry(drawable);
+ updateFaceFlags();
+ if (res)
+ {
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+ }
+
+ return res;
+ }
+
+ BOOL compiled = FALSE;
+ BOOL change_shared = FALSE;
+
+ updateRelativeXform();
+
+ if (mDrawable.isNull()) // Not sure why this is happening, but it is...
+ {
+ return TRUE; // No update to complete
+ }
+
+ calcAllTEsSame();
+
+ if (mVolumeChanged || mFaceMappingChanged || change_shared)
+ {
+ compiled = TRUE;
+ mInited = TRUE;
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ drawable->setState(LLDrawable::REBUILD_GEOMETRY);
+ if (mVolumeChanged || change_shared)
+ {
+ drawable->setState(LLDrawable::REBUILD_LIGHTING);
+ }
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ regenFaces();
+ genTriangles(FALSE);
+ }
+ }
+ else if (mLODChanged)
+ {
+ LLPointer<LLVolume> old_volumep, new_volumep;
+ F32 old_lod, new_lod;
+
+ old_volumep = getVolume();
+ old_lod = old_volumep->getDetail();
+
+ {
+ LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME);
+ LLVolumeParams volume_params = getVolume()->getParams();
+ setVolume(volume_params, 0);
+ }
+ new_volumep = getVolume();
+ new_lod = new_volumep->getDetail();
+
+ if (new_lod != old_lod)
+ {
+ compiled = TRUE;
+ sNumLODChanges += (getAllTEsSame() ? 1 : getVolume()->getNumFaces());
+
+ drawable->setState(LLDrawable::REBUILD_ALL); // for face->genVolumeTriangles()
+
+ {
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ if (new_volumep->getNumFaces() != old_volumep->getNumFaces())
+ {
+ regenFaces();
+ }
+ genTriangles(FALSE);
+ }
+ }
+ }
+ // it has its own drawable (it's moved) or it has changed UVs or it has changed xforms from global<->local
+ else
+ {
+ compiled = TRUE;
+ // All it did was move or we changed the texture coordinate offset
+ LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES);
+ genTriangles(FALSE);
+ }
+
+ // Update face flags
+ updateFaceFlags();
+
+ if(compiled)
+ {
+ LLPipeline::sCompiles++;
+ }
+
+ mVolumeChanged = FALSE;
+ mLODChanged = FALSE;
+ mFaceMappingChanged = FALSE;
+
+ drawable->clearState(LLDrawable::REBUILD_GEOMETRY);
+
+ return TRUE;
+}
+
+BOOL LLVOVolume::isRootEdit() const
+{
+ if (mParent && !((LLViewerObject*)mParent)->isAvatar())
+ {
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void LLVOVolume::setTEImage(const U8 te, LLViewerImage *imagep)
+{
+// llinfos << "SetTEImage:" << llendl;
+ BOOL changed = (mTEImages[te] != imagep);
+ LLViewerObject::setTEImage(te, imagep);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+}
+
+S32 LLVOVolume::setTETexture(const U8 te, const LLUUID &uuid)
+{
+ BOOL changed = (uuid != getTE(te)->getID() || (uuid == LLUUID::null));
+
+ S32 res = LLViewerObject::setTETexture(te, uuid);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEColor(const U8 te, const LLColor4 &color)
+{
+ BOOL changed = (color != getTE(te)->getColor());
+ S32 res = LLViewerObject::setTEColor(te, color);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+// mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEBumpmap(const U8 te, const U8 bumpmap)
+{
+ BOOL changed = (bumpmap != getTE(te)->getBumpmap());
+ S32 res = LLViewerObject::setTEBumpmap(te, bumpmap);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTETexGen(const U8 te, const U8 texgen)
+{
+ BOOL changed = (texgen != getTE(te)->getTexGen());
+ S32 res = LLViewerObject::setTETexGen(te, texgen);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEShiny(const U8 te, const U8 shiny)
+{
+ BOOL changed = (shiny != getTE(te)->getShiny());
+ S32 res = LLViewerObject::setTEShiny(te, shiny);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEFullbright(const U8 te, const U8 fullbright)
+{
+ BOOL changed = (fullbright != getTE(te)->getFullbright());
+ S32 res = LLViewerObject::setTEFullbright(te, fullbright);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ if (!mDrawable->isState(LLDrawable::REBUILD_VOLUME))
+ {
+ updateFaceFlags();
+ }
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEMediaFlags(const U8 te, const U8 media_flags)
+{
+ bool changed = (media_flags != getTE(te)->getMediaFlags());
+ S32 res = LLViewerObject::setTEMediaFlags(te, media_flags);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScale(const U8 te, const F32 s, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds || t != oldt);
+ S32 res = LLViewerObject::setTEScale(te, s, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleS(const U8 te, const F32 s)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (s != olds);
+ S32 res = LLViewerObject::setTEScaleS(te, s);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+S32 LLVOVolume::setTEScaleT(const U8 te, const F32 t)
+{
+ F32 olds,oldt;
+ getTE(te)->getScale(&olds, &oldt);
+ bool changed = (t != oldt);
+ S32 res = LLViewerObject::setTEScaleT(te, t);
+ if (mDrawable.notNull())
+ {
+ if (changed)
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ }
+ }
+ return res;
+}
+
+void LLVOVolume::updateTEData()
+{
+ if (mDrawable.notNull())
+ {
+ calcAllTEsSame();
+ mFaceMappingChanged = TRUE;
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_GEOMETRY, TRUE);
+ }
+}
+
+BOOL LLVOVolume::calcAllTEsSame()
+{
+ BOOL is_alpha = FALSE;
+ BOOL was_same = mAllTEsSame;
+ BOOL all_same = TRUE;
+ S32 num_tes = getNumTEs();
+
+ LLViewerImage *first_texturep = getTEImage(0);
+ if (!first_texturep)
+ {
+ return FALSE;
+ }
+
+ const LLTextureEntry *tep = getTE(0);
+ if (!tep)
+ {
+ llwarns << "Volume with zero textures!" << llendl;
+ return FALSE;
+ }
+
+ if (tep->getColor().mV[3] != 1.f)
+ {
+ is_alpha = TRUE;
+ }
+ const LLColor4 first_color = tep->getColor();
+ const U8 first_bump = tep->getBumpShinyFullbright();
+ const U8 first_media_flags = tep->getMediaTexGen();
+
+ if (first_texturep->getComponents() == 4)
+ {
+ is_alpha = TRUE;
+ }
+
+ F32 s_scale, t_scale;
+ tep->getScale(&s_scale, &t_scale);
+
+ for (S32 f = 1; f < num_tes; f++)
+ {
+ LLViewerImage *texturep = getTEImage(f);
+ if (texturep != first_texturep)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ tep = getTE(f);
+
+ if( tep->getBumpShinyFullbright() != first_bump )
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (first_bump)
+ {
+ F32 cur_s, cur_t;
+ tep->getScale(&cur_s, &cur_t);
+ if ((cur_s != s_scale) || (cur_t != t_scale))
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ if ((texturep->getComponents() == 4) || (tep->getColor().mV[3] != 1.f))
+ {
+ if (!is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+ else if (is_alpha)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getColor() != first_color)
+ {
+ all_same = FALSE;
+ break;
+ }
+
+ if (tep->getMediaTexGen() != first_media_flags)
+ {
+ all_same = FALSE;
+ break;
+ }
+ }
+
+ mAllTEsSame = all_same;
+ if (was_same != all_same)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); // rebuild NOW
+ mFaceMappingChanged = TRUE;
+ }
+ return mAllTEsSame;
+}
+
+void LLVOVolume::setupSingleFace(S32 face_offset)
+{
+ S32 num_indices = 0;
+ S32 num_vertices = 0;
+
+ if (mDrawable.isNull())
+ {
+ llerrs << "setupSingleFace called with NULL mDrawable" << llendl;
+ }
+ if (face_offset >= mDrawable->getNumFaces())
+ {
+ llerrs << "setupSingleFace called with invalid face_offset" << llendl;
+ }
+
+ const S32 num_faces = getVolume()->getNumFaces();
+ for (S32 i = 0; i < num_faces; i++)
+ {
+ const LLVolumeFace &vf = getVolume()->getVolumeFace(i);
+ num_vertices += vf.mVertices.size();
+ num_indices += vf.mIndices.size();
+ }
+ LLFace *facep = mDrawable->getFace(face_offset);
+ facep->setSize(num_vertices, num_indices);
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::setIsLight(BOOL is_light)
+{
+ if (is_light != getIsLight())
+ {
+ if (is_light)
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, TRUE, true);
+ }
+ else
+ {
+ setParameterEntryInUse(LLNetworkData::PARAMS_LIGHT, FALSE, true);
+ }
+
+ if (is_light)
+ {
+ // Add it to the pipeline mLightSet
+ gPipeline.setLight(mDrawable, TRUE);
+ }
+ else
+ {
+ // Not a light. Remove it from the pipeline's light set.
+ gPipeline.setLight(mDrawable, FALSE);
+
+ // Remove this object from any object which has it as a light
+ if (mDrawable)
+ {
+ mDrawable->clearLightSet();
+ }
+ }
+ }
+}
+
+void LLVOVolume::setLightColor(const LLColor3& color)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor() != color)
+ {
+ param_block->setColor(LLColor4(color, param_block->getColor().mV[3]));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightIntensity(F32 intensity)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getColor().mV[3] != intensity)
+ {
+ param_block->setColor(LLColor4(LLColor3(param_block->getColor()), intensity));
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightRadius(F32 radius)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getRadius() != radius)
+ {
+ param_block->setRadius(radius);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightFalloff(F32 falloff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getFalloff() != falloff)
+ {
+ param_block->setFalloff(falloff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+void LLVOVolume::setLightCutoff(F32 cutoff)
+{
+ LLLightParams *param_block = (LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ if (param_block->getCutoff() != cutoff)
+ {
+ param_block->setCutoff(cutoff);
+ parameterChanged(LLNetworkData::PARAMS_LIGHT, true);
+ }
+ }
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::getIsLight() const
+{
+ return getParameterEntryInUse(LLNetworkData::PARAMS_LIGHT);
+}
+
+LLColor3 LLVOVolume::getLightBaseColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor());
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+LLColor3 LLVOVolume::getLightColor() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return LLColor3(param_block->getColor()) * param_block->getColor().mV[3];
+ }
+ else
+ {
+ return LLColor3(1,1,1);
+ }
+}
+
+F32 LLVOVolume::getLightIntensity() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getColor().mV[3];
+ }
+ else
+ {
+ return 1.f;
+ }
+}
+
+F32 LLVOVolume::getLightRadius() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getRadius();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightFalloff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getFalloff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+F32 LLVOVolume::getLightCutoff() const
+{
+ const LLLightParams *param_block = (const LLLightParams *)getParameterEntry(LLNetworkData::PARAMS_LIGHT);
+ if (param_block)
+ {
+ return param_block->getCutoff();
+ }
+ else
+ {
+ return 0.f;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// returns < 0 if inside radius
+F32 LLVOVolume::getLightDistance(const LLVector3& pos) const
+{
+ LLVector3 dpos = getRenderPosition() - pos;
+ F32 dist = dpos.magVec() - getLightRadius();
+ return dist;
+}
+
+// returns intensity, modifies color in result
+F32 LLVOVolume::calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LLColor4& result)
+{
+ if (!getIsLight())
+ {
+ return 0.0f;
+ }
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ if ((gPipeline.getVertexShaderLevel(LLPipeline::SHADER_OBJECT) >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS))
+ {
+ if (dp <= 0)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ if (dist >= light_radius)
+ {
+ result *= 0;
+ return 0;
+ }
+
+ F32 mag = 1.0f-(dist/light_radius);
+ mag = powf(mag, 0.75f);
+ mag *= dp;
+ result = getLightColor() * mag;
+ return mag;
+ }
+ else
+ {
+ F32 light_radius = getLightRadius();
+ LLVector3 light_pos = getRenderPosition();
+ LLVector3 light_dir = light_pos - pos;
+ F32 dist = light_dir.normVec();
+ F32 dp = norm * light_dir;
+ F32 atten = (1.f/.2f) / (light_radius); // 20% of brightness at radius
+ F32 falloff = 1.f / (dist * atten);
+ F32 mag = falloff * dp;
+ mag = llmax(mag, 0.0f);
+ result = getLightColor() * mag;
+ return mag;
+ }
+}
+
+BOOL LLVOVolume::updateLighting(BOOL do_lighting)
+{
+ LLMemType mt1(LLMemType::MTYPE_DRAWABLE);
+
+ if (mDrawable->isStatic())
+ {
+ do_lighting = FALSE;
+ }
+
+ const LLMatrix4& mat_vert = mDrawable->getWorldMatrix();
+ const LLMatrix3& mat_normal = LLMatrix3(mDrawable->getWorldRotation());
+
+ LLVolume* volume = getVolume();
+ if (getAllTEsSame())
+ {
+ LLFace *face = mDrawable->getFace(mFaceIndexOffset);
+ S32 num_faces = volume->getNumFaces();
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, 0, num_faces-1, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ else
+ {
+ for (S32 i = 0; i < volume->getNumFaces(); i++)
+ {
+ LLFace *face = mDrawable->getFace(i + mFaceIndexOffset);
+ if (face && face->getGeomCount())
+ {
+ face->genLighting(volume, mDrawable, i, i, mat_vert, mat_normal, do_lighting);
+ }
+ }
+ }
+ return TRUE;
+}
+
+//----------------------------------------------------------------------------
+
+BOOL LLVOVolume::isFlexible() const
+{
+ if (getParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE))
+ {
+ if (getVolume()->getParams().getPathParams().getCurveType() != LL_PCODE_PATH_FLEXIBLE)
+ {
+ llwarns << "wtf" << llendl;
+ LLVolumeParams volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+BOOL LLVOVolume::isVolumeGlobal() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->isVolumeGlobal() ? TRUE : FALSE;
+ }
+ return FALSE;
+}
+
+BOOL LLVOVolume::canBeFlexible() const
+{
+ U8 path = getVolume()->getParams().getPathParams().getCurveType();
+ return (path == LL_PCODE_PATH_FLEXIBLE || path == LL_PCODE_PATH_LINE);
+}
+
+BOOL LLVOVolume::setIsFlexible(BOOL is_flexible)
+{
+ BOOL res = FALSE;
+ BOOL was_flexible = isFlexible();
+ LLVolumeParams volume_params;
+ if (is_flexible)
+ {
+ if (!was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_FLEXIBLE);
+ res = TRUE;
+ setFlags(FLAGS_USE_PHYSICS, FALSE);
+ setFlags(FLAGS_PHANTOM, TRUE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, TRUE, true);
+ if (mDrawable)
+ {
+ mDrawable->makeActive();
+ }
+ }
+ }
+ else
+ {
+ if (was_flexible)
+ {
+ volume_params = getVolume()->getParams();
+ U8 profile_and_hole = volume_params.getProfileParams().getCurveType();
+ volume_params.setType(profile_and_hole, LL_PCODE_PATH_LINE);
+ res = TRUE;
+ setFlags(FLAGS_PHANTOM, FALSE);
+ setParameterEntryInUse(LLNetworkData::PARAMS_FLEXIBLE, FALSE, true);
+ }
+ }
+ if (res)
+ {
+ res = setVolume(volume_params, 1);
+ if (res)
+ {
+ markForUpdate(TRUE);
+ }
+ }
+ return res;
+}
+
+//----------------------------------------------------------------------------
+
+void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point)
+{
+ LLVolume *volume = getVolume();
+
+ if (volume)
+ {
+ LLVector3 view_vector;
+ view_vector = view_point;
+
+ if (!isVolumeGlobal())
+ { //transform view vector into volume space
+ view_vector -= getRenderPosition();
+ LLQuaternion worldRot = getRenderRotation();
+ view_vector = view_vector * ~worldRot;
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ view_vector.scaleVec(invObjScale);
+ }
+
+ updateRelativeXform();
+ volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, mRelativeXform, mRelativeXformInvTrans);
+
+ nodep->mSilhouetteExists = TRUE;
+ }
+}
+
+void LLVOVolume::deleteFaces(LLVOVolume* childp)
+{
+ S32 face_count = childp->mNumFaces;
+ S32 start_index = childp->mFaceIndexOffset;
+ if (mDrawable.notNull())
+ {
+ mDrawable->deleteFaces(start_index, face_count);
+ }
+ if (mFaceIndexOffset > start_index)
+ {
+ mFaceIndexOffset -= face_count;
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ LLViewerObject* siblingp = mChildList[i];
+ if (siblingp != childp)
+ {
+ if (siblingp->getPCode() == LL_PCODE_VOLUME &&
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset > start_index)
+ {
+ ((LLVOVolume*)siblingp)->mFaceIndexOffset -= face_count;
+ }
+ }
+ }
+ childp->mFaceIndexOffset = 0;
+ childp->mNumFaces = 0;
+}
+
+void LLVOVolume::updateRadius()
+{
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ mVObjRadius = getScale().magVec();
+ mDrawable->setRadius(mVObjRadius);
+}
+
+
+BOOL LLVOVolume::isAttachment() const
+{
+ if (mState == 0)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+BOOL LLVOVolume::isHUDAttachment() const
+{
+ //HACK assume hud attachment points are in defined range
+ // since this range is constant for backwards compatibility reasons
+ // this is probably a reasonable assumption to make
+ S32 attachment_id = ATTACHMENT_ID_FROM_STATE(mState);
+ return ( attachment_id >= 31 && attachment_id <= 38 );
+}
+
+
+const LLMatrix4 LLVOVolume::getRenderMatrix() const
+{
+ if (mDrawable->isActive() && !mDrawable->isRoot())
+ {
+ return mDrawable->getParent()->getWorldMatrix();
+ }
+ return mDrawable->getWorldMatrix();
+}
+
+void LLVOVolume::writeCAL3D(apr_file_t* fp, std::string& path, std::string& file_base, S32 joint_num, LLVector3& pos, LLQuaternion& rot, S32& material_index, S32& texture_index, std::multimap<LLUUID, LLMaterialExportInfo*>& material_map)
+{
+ LLPointer<LLImageTGA> tga_image = new LLImageTGA;
+
+ if (mDrawable.isNull())
+ {
+ return;
+ }
+
+ LLVector3 final_pos = getPosition();
+ final_pos *= 100.f;
+
+ final_pos = final_pos * rot;
+ final_pos += pos;
+ LLQuaternion final_rot;
+ final_rot = getRotation() * rot;
+ LLMatrix4 transform;
+ transform.initAll(getScale(), final_rot, final_pos);
+
+ LLMatrix4 int_transpose_transform;
+ int_transpose_transform.initAll(LLVector3(1.f / getScale().mV[VX], 1.f / getScale().mV[VY], 1.f / getScale().mV[VZ]), final_rot, LLVector3::zero);
+
+ for (S32 i = 0; i < mDrawable->getNumFaces(); i++)
+ {
+ S32 vert_num = 0;
+ LLFace* facep = mDrawable->getFace(i);
+ LLDrawPool* poolp = facep->getPool();
+
+ const LLTextureEntry* tep = facep->getTextureEntry();
+ if (!tep)
+ {
+ continue;
+ }
+
+ S32 my_material = -1;
+ S32 my_texture = -1;
+ LLColor4 face_color = tep->getColor();
+
+ typedef std::multimap<LLUUID, LLMaterialExportInfo*>::iterator material_it_t;
+ std::pair<material_it_t, material_it_t> found_range = material_map.equal_range(tep->getID());
+ material_it_t material_it = found_range.first;
+
+ LLMaterialExportInfo* material_info = NULL;
+
+ while(material_it != material_map.end() && material_it != found_range.second)
+ {
+ // we've at least found a matching texture, so reuse it
+ my_texture = material_it->second->mTextureIndex;
+ if (material_it->second->mColor == face_color)
+ {
+ // we've found a matching material
+ material_info = material_it->second;
+ }
+ ++material_it;
+ }
+
+ if (material_info)
+ {
+ // material already exported, just reuse it
+ my_material = material_info->mMaterialIndex;
+ my_texture = material_info->mTextureIndex;
+ }
+ else
+ {
+ // reserve new material number
+ my_material = material_index++;
+
+ // if we didn't already find a matching texture...
+ if (my_texture == -1)
+ {
+ //...use the next available slot...
+ my_texture = texture_index++;
+
+ //...and export texture as image file
+ char filename[MAX_PATH];
+ sprintf(filename, "%s\\%s_material_tex_%d.tga", path.c_str(), file_base.c_str(), my_texture);
+
+ LLViewerImage* imagep = facep->getTexture();
+ if (imagep->getTexName() == 0)
+ {
+ llinfos << "No image data available for " << filename << llendl;
+ continue;
+ }
+ LLPointer<LLImageRaw> raw_image = new LLImageRaw;
+ imagep->readBackRaw(-1, raw_image);
+ BOOL success = tga_image->encode(raw_image);
+ success = tga_image->save(filename);
+ }
+
+ material_info = new LLMaterialExportInfo(my_material, my_texture, face_color);
+ material_map.insert(std::make_pair<LLUUID, LLMaterialExportInfo*>(tep->getID(), material_info));
+ }
+
+ apr_file_printf(fp, "\t<SUBMESH NUMVERTICES=\"%d\" NUMFACES=\"%d\" MATERIAL=\"%d\" NUMLODSTEPS=\"0\" NUMSPRINGS=\"0\" NUMTEXCOORDS=\"1\">\n",
+ facep->getGeomCount(), facep->getIndicesCount() / 3, my_material);
+
+ for (S32 vert_index = 0; vert_index < facep->getGeomCount(); vert_index++)
+ {
+ LLVector3 vert_pos = poolp->getVertex(facep->getGeomStart() + vert_index);
+ vert_pos *= 100.f;
+ vert_pos = vert_pos * transform;
+ LLVector3 vert_norm = poolp->getNormal(facep->getGeomStart() + vert_index);
+ vert_norm = vert_norm * int_transpose_transform;
+ LLVector2 vert_tc = poolp->getTexCoord(facep->getGeomStart() + vert_index, 0);
+ apr_file_printf(fp, " <VERTEX ID=\"%d\" NUMINFLUENCES=\"1\">\n", vert_num++);
+ apr_file_printf(fp, " <POS>%.4f %.4f %.4f</POS>\n", vert_pos.mV[VX], vert_pos.mV[VY], vert_pos.mV[VZ]);
+ apr_file_printf(fp, " <NORM>%.6f %.6f %.6f</NORM>\n", vert_norm.mV[VX], vert_norm.mV[VY], vert_norm.mV[VZ]);
+ apr_file_printf(fp, " <TEXCOORD>%.6f %.6f</TEXCOORD>\n", vert_tc.mV[VX], 1.f - vert_tc.mV[VY]);
+ apr_file_printf(fp, " <INFLUENCE ID=\"%d\">1.0</INFLUENCE>\n", joint_num + 1);
+ apr_file_printf(fp, " </VERTEX>\n");
+ }
+
+ for (U32 index_i = 0; index_i < facep->getIndicesCount(); index_i += 3)
+ {
+ U32 index_a = poolp->getIndex(facep->getIndicesStart() + index_i) - facep->getGeomStart();
+ U32 index_b = poolp->getIndex(facep->getIndicesStart() + index_i + 1) - facep->getGeomStart();
+ U32 index_c = poolp->getIndex(facep->getIndicesStart() + index_i + 2) - facep->getGeomStart();
+ apr_file_printf(fp, " <FACE VERTEXID=\"%d %d %d\" />\n", index_a, index_b, index_c);
+ }
+
+ apr_file_printf(fp, " </SUBMESH>\n");
+ }
+
+ for (U32 i = 0; i < mChildList.size(); i++)
+ {
+ ((LLVOVolume*)(LLViewerObject*)mChildList[i])->writeCAL3D(fp, path, file_base, joint_num, final_pos, final_rot, material_index, texture_index, material_map);
+ }
+}
+
+//static
+void LLVOVolume::preUpdateGeom()
+{
+ sNumLODChanges = 0;
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, local_origin);
+}
+
+void LLVOVolume::parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin)
+{
+ LLViewerObject::parameterChanged(param_type, data, in_use, local_origin);
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onParameterChanged(param_type, data, in_use, local_origin);
+ }
+ if (mDrawable.notNull())
+ {
+ BOOL is_light = getIsLight();
+ if (is_light != mDrawable->isState(LLDrawable::LIGHT))
+ {
+ gPipeline.setLight(mDrawable, is_light);
+ }
+ }
+}
+
+void LLVOVolume::updateSpatialExtents(LLVector3& newMin, LLVector3& newMax)
+{
+}
+
+const LLVector3 LLVOVolume::getPivotPositionAgent() const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getPivotPosition();
+ }
+ return LLViewerObject::getPivotPositionAgent();
+}
+
+void LLVOVolume::onShift(const LLVector3 &shift_vector)
+{
+ if (mVolumeImpl)
+ {
+ mVolumeImpl->onShift(shift_vector);
+ }
+}
+
+const LLMatrix4& LLVOVolume::getWorldMatrix(LLXformMatrix* xform) const
+{
+ if (mVolumeImpl)
+ {
+ return mVolumeImpl->getWorldMatrix(xform);
+ }
+ return xform->getWorldMatrix();
+}
+
+LLVector3 LLVOVolume::agentPositionToVolume(const LLVector3& pos) const
+{
+ if (isVolumeGlobal())
+ {
+ return pos;
+ }
+
+ LLVector3 ret = pos - getRenderPosition();
+ ret = ret * ~getRenderRotation();
+ LLVector3 objScale = getScale();
+ LLVector3 invObjScale(1.f / objScale.mV[VX], 1.f / objScale.mV[VY], 1.f / objScale.mV[VZ]);
+ ret.scaleVec(invObjScale);
+
+ return ret;
+}
+
+LLVector3 LLVOVolume::agentDirectionToVolume(const LLVector3& dir) const
+{
+ return isVolumeGlobal() ? dir : (dir * ~getRenderRotation());
+}
+
+LLVector3 LLVOVolume::volumePositionToAgent(const LLVector3& dir) const
+{
+ LLVector3 ret = dir;
+ ret.scaleVec(getScale());
+ ret = ret * getRenderRotation();
+ ret += getRenderPosition();
+
+ return ret;
+}
+
+BOOL LLVOVolume::lineSegmentIntersect(const LLVector3& start, LLVector3& end) const
+{
+ LLVolume* volume = getVolume();
+ BOOL ret = FALSE;
+ if (volume)
+ {
+ LLVector3 v_start, v_end, v_dir;
+
+ v_start = agentPositionToVolume(start);
+ v_end = agentPositionToVolume(end);
+
+ if (LLLineSegmentAABB(v_start, v_end, volume->mBounds[0], volume->mBounds[1]))
+ {
+ if (volume->lineSegmentIntersect(v_start, v_end) >= 0)
+ {
+ end = volumePositionToAgent(v_end);
+ ret = TRUE;
+ }
+ }
+ }
+ return ret;
+}
diff --git a/indra/newview/llvovolume.h b/indra/newview/llvovolume.h
new file mode 100644
index 0000000000..26764d62c9
--- /dev/null
+++ b/indra/newview/llvovolume.h
@@ -0,0 +1,213 @@
+/**
+ * @file llvovolume.h
+ * @brief LLVOVolume class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLVOVOLUME_H
+#define LL_LLVOVOLUME_H
+
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "llframetimer.h"
+#include "llapr.h"
+#include <map>
+
+class LLViewerTextureAnim;
+class LLDrawPool;
+class LLSelectNode;
+
+enum LLVolumeInterfaceType
+{
+ INTERFACE_FLEXIBLE = 1,
+};
+
+// Base class for implementations of the volume - Primitive, Flexible Object, etc.
+class LLVolumeInterface
+{
+public:
+ virtual ~LLVolumeInterface() { }
+ virtual LLVolumeInterfaceType getInterfaceType() const = 0;
+ virtual BOOL doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) = 0;
+ virtual BOOL doUpdateGeometry(LLDrawable *drawable) = 0;
+ virtual LLVector3 getPivotPosition() const = 0;
+ virtual void onSetVolume(const LLVolumeParams &volume_params, const S32 detail) = 0;
+ virtual void onSetScale(const LLVector3 &scale, BOOL damped) = 0;
+ virtual void onParameterChanged(U16 param_type, LLNetworkData *data, BOOL in_use, bool local_origin) = 0;
+ virtual void onShift(const LLVector3 &shift_vector) = 0;
+ virtual bool isVolumeUnique() const = 0; // Do we need a unique LLVolume instance?
+ virtual bool isVolumeGlobal() const = 0; // Are we in global space?
+ virtual bool isActive() const = 0; // Is this object currently active?
+ virtual const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const = 0;
+ virtual void updateRelativeXform(BOOL global_volume = FALSE) = 0;
+};
+
+// Class which embodies all Volume objects (with pcode LL_PCODE_VOLUME)
+class LLVOVolume : public LLViewerObject
+{
+public:
+ static void initClass();
+ static void preUpdateGeom();
+ static F32 getTextureVirtualSize(const LLFace* face);
+
+ BOOL mWereAllTEsSame;
+
+public:
+ LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOVolume();
+
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+
+ void deleteFaces(LLVOVolume* childp);
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+
+ /*virtual*/ BOOL isActive() const;
+ /*virtual*/ BOOL isAttachment() const;
+ /*virtual*/ BOOL isRootEdit() const; // overridden for sake of attachments treating themselves as a root object
+ /*virtual*/ BOOL isHUDAttachment() const;
+
+ void generateSilhouette(LLSelectNode* nodep, const LLVector3& view_point);
+
+ BOOL getAllTEsSame() const { return mAllTEsSame; }
+ F32 getIndividualRadius() { return mRadius; }
+ S32 getLOD() const { return mLOD; }
+ const LLVector3 getPivotPositionAgent() const;
+ const LLMatrix4& getRelativeXform() const { return mRelativeXform; }
+ const LLMatrix3& getRelativeXformInvTrans() const { return mRelativeXformInvTrans; }
+ /*virtual*/ const LLMatrix4 getRenderMatrix() const;
+
+ /*virtual*/ BOOL lineSegmentIntersect(const LLVector3& start, LLVector3& end) const;
+ LLVector3 agentPositionToVolume(const LLVector3& pos) const;
+ LLVector3 agentDirectionToVolume(const LLVector3& dir) const;
+ LLVector3 volumePositionToAgent(const LLVector3& dir) const;
+
+
+ BOOL getVolumeChanged() const { return mVolumeChanged; }
+ /*virtual*/ F32 getRadius() const { return mVObjRadius; };
+ const LLMatrix4& getWorldMatrix(LLXformMatrix* xform) const;
+
+ void markForUpdate(BOOL priority) { LLViewerObject::markForUpdate(priority); mVolumeChanged = TRUE; }
+
+ /*virtual*/ void onShift(const LLVector3 &shift_vector); // Called when the drawable shifts
+
+ /*virtual*/ void parameterChanged(U16 param_type, bool local_origin);
+ /*virtual*/ void parameterChanged(U16 param_type, LLNetworkData* data, BOOL in_use, bool local_origin);
+
+ /*virtual*/ U32 processUpdateMessage(LLMessageSystem *mesgsys,
+ void **user_data,
+ U32 block_num, const EObjectUpdateType update_type,
+ LLDataPacker *dp);
+
+ /*virtual*/ BOOL setDrawableParent(LLDrawable* parentp);
+
+ /*virtual*/ void setScale(const LLVector3 &scale, BOOL damped);
+
+ /*virtual*/ void setTEImage(const U8 te, LLViewerImage *imagep);
+ /*virtual*/ S32 setTETexture(const U8 te, const LLUUID &uuid);
+ /*virtual*/ S32 setTEColor(const U8 te, const LLColor4 &color);
+ /*virtual*/ S32 setTEBumpmap(const U8 te, const U8 bump);
+ /*virtual*/ S32 setTEShiny(const U8 te, const U8 shiny);
+ /*virtual*/ S32 setTEFullbright(const U8 te, const U8 fullbright);
+ /*virtual*/ S32 setTEMediaFlags(const U8 te, const U8 media_flags);
+ /*virtual*/ S32 setTEScale(const U8 te, const F32 s, const F32 t);
+ /*virtual*/ S32 setTEScaleS(const U8 te, const F32 s);
+ /*virtual*/ S32 setTEScaleT(const U8 te, const F32 t);
+ /*virtual*/ S32 setTETexGen(const U8 te, const U8 texgen);
+ /*virtual*/ BOOL setMaterial(const U8 material);
+
+ void setTexture(const S32 face);
+
+ /*virtual*/ BOOL setVolume(const LLVolumeParams &volume_params, const S32 detail, bool unique_volume = false);
+ void updateRelativeXform(BOOL global_volume = FALSE);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ /*virtual*/ BOOL updateLOD();
+ void updateRadius();
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ void updateTextures(S32 lod);
+
+ void updateFaceFlags();
+ void regenFaces();
+ BOOL genTriangles(BOOL force_global);
+ virtual void updateSpatialExtents(LLVector3& min, LLVector3& max);
+ virtual void writeCAL3D(apr_file_t* fp,
+ std::string& path,
+ std::string& file_base,
+ S32 joint_num,
+ LLVector3& pos,
+ LLQuaternion& rot,
+ S32& material_index,
+ S32& texture_index,
+ std::multimap<LLUUID, LLMaterialExportInfo*>& material_map);
+
+ // For Lights
+ void setIsLight(BOOL is_light);
+ void setLightColor(const LLColor3& color);
+ void setLightIntensity(F32 intensity);
+ void setLightRadius(F32 radius);
+ void setLightFalloff(F32 falloff);
+ void setLightCutoff(F32 cutoff);
+ BOOL getIsLight() const;
+ LLColor3 getLightBaseColor() const; // not scaled by intensity
+ LLColor3 getLightColor() const; // scaled by intensity
+ F32 getLightIntensity() const;
+ F32 getLightRadius() const;
+ F32 getLightFalloff() const;
+ F32 getLightCutoff() const;
+ F32 getLightDistance(const LLVector3& pos) const; // returns < 0 if inside radius
+
+ // Flexible Objects
+ virtual BOOL isFlexible() const;
+ BOOL isVolumeGlobal() const;
+ BOOL canBeFlexible() const;
+ BOOL setIsFlexible(BOOL is_flexible);
+
+ // Lighting
+ F32 calcLightAtPoint(const LLVector3& pos, const LLVector3& norm, LLColor4& result);
+ BOOL updateLighting(BOOL do_lighting);
+
+protected:
+ F32 computeLODProfilePathComplexityBias();
+ S32 computeLODDetail(F32 distance, F32 radius);
+ BOOL calcLOD();
+ void setupSingleFace(S32 face_offset); // Set up the face for combined volumes.
+ LLFace* addFace(S32 face_index);
+ void updateTEData();
+ BOOL calcAllTEsSame();
+
+public:
+ LLViewerTextureAnim *mTextureAnimp;
+
+protected:
+ friend class LLDrawable;
+
+ BOOL mAllTEsSame; // All TE's have the same pool/texture
+ BOOL mFaceMappingChanged;
+ BOOL mGlobalVolume;
+ BOOL mInited;
+ S32 mLOD;
+ BOOL mLODChanged;
+ F32 mRadius;
+ LLMatrix4 mRelativeXform;
+ LLMatrix3 mRelativeXformInvTrans;
+ BOOL mVolumeChanged;
+ F32 mVObjRadius;
+ LLVolumeInterface *mVolumeImpl;
+
+ // statics
+public:
+ static F32 sLODSlopDistanceFactor;// Changing this to zero, effectively disables the LOD transition slop
+ static F32 sLODComplexityDistanceBias; // Changing this to zero makes all prims LOD at the same distance,
+ // regardless of complexity
+ static F32 sLODFactor; // LOD scale factor
+ static F32 sDistanceFactor; // LOD distance factor
+
+protected:
+ static S32 sNumLODChanges;
+
+ friend class LLVolumeImplFlexible;
+};
+
+#endif // LL_LLVOVOLUME_H
diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp
new file mode 100644
index 0000000000..135873b5b8
--- /dev/null
+++ b/indra/newview/llvowater.cpp
@@ -0,0 +1,1028 @@
+/**
+ * @file llvowater.cpp
+ * @brief LLVOWater class implementation
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llvowater.h"
+
+#include "imageids.h"
+#include "llviewercontrol.h"
+
+#include "llagent.h"
+#include "lldrawable.h"
+#include "lldrawpoolwater.h"
+#include "llface.h"
+#include "llsky.h"
+#include "llsurface.h"
+#include "llvosky.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llworld.h"
+#include "pipeline.h"
+
+const BOOL gUseRoam = FALSE;
+
+
+///////////////////////////////////
+
+#include "randgauss.h"
+
+template<class T> inline T LERP(T a, T b, F32 factor)
+{
+ return a + (b - a) * factor;
+}
+
+const U32 N_RES_HALF = (N_RES >> 1);
+
+const U32 WIDTH = (N_RES * WAVE_STEP); //128.f //64 // width of wave tile, in meters
+const F32 WAVE_STEP_INV = (1. / WAVE_STEP);
+
+const F32 g = 9.81f; // gravitational constant (m/s^2)
+
+///////////////////////////////////
+
+LLWaterSurface::LLWaterSurface() :
+ mInitialized(FALSE),
+ mWind(9, 0, 0),
+ mA(0.2f),
+ mVisc(0.001f),
+ mShininess(8.0f)
+{}
+
+
+LLWaterGrid *LLVOWater::sGrid = 0;
+
+
+LLVOWater::LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp)
+: LLViewerObject(id, LL_VO_WATER, regionp)
+{
+ // Terrain must draw during selection passes so it can block objects behind it.
+ mbCanSelect = FALSE;
+ setScale(LLVector3(256.f, 256.f, 0.f)); // Hack for setting scale for bounding boxes/visibility.
+
+ mUseTexture = TRUE;
+}
+
+
+void LLVOWater::markDead()
+{
+ LLViewerObject::markDead();
+}
+
+
+BOOL LLVOWater::isActive() const
+{
+ return FALSE;
+}
+
+
+void LLVOWater::setPixelAreaAndAngle(LLAgent &agent)
+{
+ mAppAngle = 50;
+ mPixelArea = 500*500;
+}
+
+
+// virtual
+void LLVOWater::updateTextures(LLAgent &agent)
+{
+}
+
+// virtual
+void LLVOWater::updateDrawable(BOOL force_damped)
+{
+ // Force an immediate rebuild on any update
+ if (mDrawable.notNull())
+ {
+ gPipeline.updateMoveNormalAsync(mDrawable);
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE);
+ }
+ clearChanged(SHIFTED);
+}
+
+// Never gets called
+BOOL LLVOWater::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time)
+{
+ if (mDead || !(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_WATER)))
+ {
+ return TRUE;
+ }
+ if (mDrawable)
+ {
+ gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_VOLUME, TRUE);
+ }
+ return TRUE;
+}
+
+LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline)
+{
+ pipeline->allocDrawable(this);
+ mDrawable->setLit(FALSE);
+ mDrawable->setRenderType(LLPipeline::RENDER_TYPE_WATER);
+
+ LLDrawPoolWater *pool = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER);
+
+ if (mUseTexture)
+ {
+ mDrawable->setNumFaces(1, pool, mRegionp->getLand().getWaterTexture());
+ }
+ else
+ {
+ mDrawable->setNumFaces(1, pool, gWorldp->getDefaultWaterTexture());
+ }
+
+ return mDrawable;
+}
+
+BOOL LLVOWater::updateGeometry(LLDrawable *drawable)
+{
+ return updateGeometryFlat(drawable);
+}
+
+
+BOOL LLVOWater::updateGeometryFlat(LLDrawable *drawable)
+{
+ LLFace *face;
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace(gPipeline.getPool(LLDrawPool::POOL_WATER), NULL);
+ }
+ face = drawable->getFace(0);
+
+ LLVector2 uvs[4];
+ LLVector3 vtx[4];
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ S32 size = 16;
+
+ S32 num_quads = size*size;
+
+ face->setPrimType(LLTriangles);
+ face->setSize(4*num_quads, 6*num_quads);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ LLVector3 position_agent;
+ position_agent = getPositionAgent();
+ face->mCenterAgent = position_agent;
+
+ S32 x, y;
+ F32 step_x = getScale().mV[0] / size;
+ F32 step_y = getScale().mV[1] / size;
+
+ const LLVector3 up(0.f, step_y * 0.5f, 0.f);
+ const LLVector3 right(step_x * 0.5f, 0.f, 0.f);
+ const LLVector3 normal(0.f, 0.f, 1.f);
+
+ F32 size_inv = 1.f / size;
+
+ for (y = 0; y < size; y++)
+ {
+ for (x = 0; x < size; x++)
+ {
+ S32 toffset = index_offset + 4*(y*size + x);
+ position_agent = getPositionAgent() - getScale() * 0.5f;
+ position_agent.mV[VX] += (x + 0.5f) * step_x;
+ position_agent.mV[VY] += (y + 0.5f) * step_y;
+
+ vtx[0] = position_agent - right + up;
+ vtx[1] = position_agent - right - up;
+ vtx[2] = position_agent + right + up;
+ vtx[3] = position_agent + right - up;
+
+ *(verticesp++) = vtx[0];
+ *(verticesp++) = vtx[1];
+ *(verticesp++) = vtx[2];
+ *(verticesp++) = vtx[3];
+
+ uvs[0].setVec(x*size_inv, (y+1)*size_inv);
+ uvs[1].setVec(x*size_inv, y*size_inv);
+ uvs[2].setVec((x+1)*size_inv, (y+1)*size_inv);
+ uvs[3].setVec((x+1)*size_inv, y*size_inv);
+
+ *(texCoordsp) = uvs[0];
+ texCoordsp++;
+ *(texCoordsp) = uvs[1];
+ texCoordsp++;
+ *(texCoordsp) = uvs[2];
+ texCoordsp++;
+ *(texCoordsp) = uvs[3];
+ texCoordsp++;
+
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+ *(normalsp++) = normal;
+
+ *indicesp++ = toffset + 0;
+ *indicesp++ = toffset + 1;
+ *indicesp++ = toffset + 2;
+
+ *indicesp++ = toffset + 1;
+ *indicesp++ = toffset + 3;
+ *indicesp++ = toffset + 2;
+ }
+ }
+
+
+ mDrawable->movePartition();
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+BOOL LLVOWater::updateGeometryHeightFieldRoam(LLDrawable *drawable)
+{
+ LLVector3 position_agent = getPositionAgent();
+ const LLVector3 region_size = getScale();
+ const LLVector3 region_origin = position_agent - region_size * 0.5f;
+
+ S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
+ S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
+
+ static S32 res = (S32)sGrid->mPatchRes;
+ if (patch_origx < 0)
+ {
+ patch_dimx -= - patch_origx;
+ if (patch_dimx < 1)
+ {
+ return TRUE;
+ }
+ patch_origx = 0;
+ }
+ if (patch_origy < 0)
+ {
+ patch_dimy -= - patch_origy;
+ if (patch_dimy < 1)
+ {
+ return TRUE;
+ }
+ patch_origy = 0;
+ }
+ if (patch_origx >= res)
+ {
+ return TRUE;
+ }
+ if (patch_origy >= res)
+ {
+ return TRUE;
+ }
+
+ patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
+ patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
+
+ U32 num_of_tris = 0;
+ S32 px, py;
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ const U32 ind = py * sGrid->mPatchRes + px;
+ if (sGrid->mPatches[ind].visible() && sGrid->mTab[px][py] == 0)
+ {
+ num_of_tris += sGrid->mPatches[ind].numTris();
+ sGrid->mTab[px][py] = this;
+ }
+ }
+ }
+
+ if (num_of_tris == 0)
+ {
+ return TRUE;
+ }
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
+ gWorldp->getDefaultWaterTexture());
+ }
+
+ LLFace *face;
+
+ face = drawable->getFace(0);
+ face->mCenterAgent = position_agent;
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ const F32 water_height = getRegion()->getWaterHeight();
+
+
+ face->setPrimType(LLTriangles);
+ face->setSize(3 * num_of_tris, 3 * num_of_tris);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ U32 num_of_vtx = 0;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ for (U8 h = 0; h < 2; h++)
+ {
+ const U32 ind = py * sGrid->mPatchRes + px;
+ if (!sGrid->mPatches[ind].visible() || sGrid->mTab[px][py] != this)
+ continue;
+ LLWaterTri* half = (LLWaterTri*) sGrid->mPatches[ind].half(h);
+ for (const LLWaterTri* tri = (LLWaterTri*) half->getFirstLeaf();
+ tri != NULL;
+ tri = (LLWaterTri*) tri->getNextLeaf())
+ {
+ /////// check for coordinates
+ *(verticesp++) = sGrid->vtx(tri->Lvtx(), water_height);
+ *(verticesp++) = sGrid->vtx(tri->Rvtx(), water_height);
+ *(verticesp++) = sGrid->vtx(tri->Tvtx(), water_height);
+
+ *(normalsp++) = sGrid->norm(tri->Lvtx());
+ *(normalsp++) = sGrid->norm(tri->Rvtx());
+ *(normalsp++) = sGrid->norm(tri->Tvtx());
+
+ *(indicesp++) = index_offset + num_of_vtx + 0;
+ *(indicesp++) = index_offset + num_of_vtx + 1;
+ *(indicesp++) = index_offset + num_of_vtx + 2;
+ num_of_vtx += 3;
+ }
+ }
+ }
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+
+
+BOOL LLVOWater::updateGeometryHeightFieldSimple(LLDrawable *drawable)
+{
+ LLVector3 position_agent = getPositionAgent();
+ const LLVector3 region_size = getScale();
+ const LLVector3 region_origin = position_agent - region_size * 0.5f;
+
+ S32 patch_origx = llround(region_origin.mV[VX] - sGrid->mRegionOrigin.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_origy = llround(region_origin.mV[VY] - sGrid->mRegionOrigin.mV[VY]) / sGrid->mRegionWidth;
+ S32 patch_dimx = llround(region_size.mV[VX]) / sGrid->mRegionWidth;
+ S32 patch_dimy = llround(region_size.mV[VY]) / sGrid->mRegionWidth;
+
+ static S32 res = sGrid->mPatchRes;
+ if (patch_origx < 0)
+ {
+ patch_dimx -= - patch_origx;
+ if (patch_dimx < 1)
+ {
+ return TRUE;
+ }
+ patch_origx = 0;
+ }
+ if (patch_origy < 0)
+ {
+ patch_dimy -= - patch_origy;
+ if (patch_dimy < 1)
+ {
+ return TRUE;
+ }
+ patch_origy = 0;
+ }
+ if (patch_origx >= res)
+ {
+ return TRUE;
+ }
+ if (patch_origy >= res)
+ {
+ return TRUE;
+ }
+
+ patch_dimx = llmin<U32>(patch_dimx, res - patch_origx);
+ patch_dimy = llmin<U32>(patch_dimy, res - patch_origy);
+
+
+ U32 num_of_regions = 0;
+ S32 px, py;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ // if (sGrid->mTab[px][py] != 0)
+ // bool stop = true;
+ if (sGrid->mPatches[py * sGrid->mPatchRes + px].visible() && sGrid->mTab[px][py] == 0)
+ {
+ num_of_regions++;
+ sGrid->mTab[px][py] = this;
+ }
+ }
+ }
+
+ if (num_of_regions == 0)
+ {
+ return TRUE;
+ }
+
+ if (drawable->getNumFaces() < 1)
+ {
+ drawable->addFace((LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER),
+ gWorldp->getDefaultWaterTexture());
+ }
+
+ LLFace *face;
+
+ face = drawable->getFace(0);
+ face->mCenterAgent = position_agent;
+
+ LLStrider<LLVector3> verticesp, normalsp;
+ LLStrider<LLVector2> texCoordsp;
+ U32 *indicesp;
+ S32 index_offset;
+
+ const F32 water_height = getRegion()->getWaterHeight();
+
+ const U32 steps_in_region = sGrid->mStepsInRegion / sGrid->mResDecrease;
+ const U32 num_quads = steps_in_region * steps_in_region * num_of_regions;
+
+ face->setPrimType(LLTriangles);
+ face->setSize(4*num_quads, 6*num_quads);
+ index_offset = face->getGeometry(verticesp,normalsp,texCoordsp, indicesp);
+ if (-1 == index_offset)
+ {
+ return TRUE;
+ }
+
+ U32 num_of_vtx = 0;
+
+ for (py = patch_origy; py < patch_origy + patch_dimy; py++)
+ {
+ for (px = patch_origx; px < patch_origx + patch_dimx; px++)
+ {
+ if (!sGrid->mPatches[py * sGrid->mPatchRes + px].visible() || sGrid->mTab[px][py] != this)
+ {
+ continue;
+ }
+
+ U32 orig_indx = px * sGrid->mStepsInRegion;
+ U32 orig_indy = py * sGrid->mStepsInRegion;
+
+ for (U32 qy = 0; qy < steps_in_region; qy++)
+ {
+ for (U32 qx = 0; qx < steps_in_region; qx++)
+ {
+ const S32 x0 = orig_indx + qx * sGrid->mResDecrease;
+ const S32 y0 = orig_indy + qy * sGrid->mResDecrease;
+ const S32 x1 = x0 + sGrid->mResDecrease;
+ const S32 y1 = y0 + sGrid->mResDecrease;
+
+ sGrid->setVertex(x0, y1, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x0, y0, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x1, y1, water_height, *(verticesp));
+ verticesp++;
+ sGrid->setVertex(x1, y0, water_height, *(verticesp));
+ verticesp++;
+ /*
+ *(verticesp++) = sGrid->vtx(x0, y1, water_height);
+ *(verticesp++) = sGrid->vtx(x0, y0, water_height);
+ *(verticesp++) = sGrid->vtx(x1, y1, water_height);
+ *(verticesp++) = sGrid->vtx(x1, y0, water_height);
+ */
+ *(normalsp++) = sGrid->norm(x0, y1);
+ *(normalsp++) = sGrid->norm(x0, y0);
+ *(normalsp++) = sGrid->norm(x1, y1);
+ *(normalsp++) = sGrid->norm(x1, y0);
+
+ const S32 curr_index_offset = index_offset + num_of_vtx;
+
+ *indicesp++ = curr_index_offset + 0;
+ *indicesp++ = curr_index_offset + 1;
+ *indicesp++ = curr_index_offset + 2;
+
+ *indicesp++ = curr_index_offset + 1;
+ *indicesp++ = curr_index_offset + 3;
+ *indicesp++ = curr_index_offset + 2;
+ num_of_vtx += 4;
+ }
+ }
+ }
+ }
+
+
+ LLPipeline::sCompiles++;
+ return TRUE;
+}
+
+void LLVOWater::initClass()
+{
+ sGrid = new LLWaterGrid;
+}
+
+void LLVOWater::cleanupClass()
+{
+ if (sGrid)
+ {
+ sGrid->cleanup();
+ delete sGrid;
+ sGrid = 0;
+ }
+}
+
+
+LLWaterGrid::LLWaterGrid() : mResIncrease(1)//0.5)
+{
+ init();
+}
+
+
+void LLWaterGrid::init()
+{
+ //mRegionOrigin = LLVector3(-2 * mRegionWidth, -2 * mRegionWidth, 0);
+ mRegionWidth = 256;
+ mPatchRes = 5;
+ mMaxGridSize = mPatchRes * mRegionWidth;
+ mMinStep = (U32)(WAVE_STEP * mResIncrease);
+
+ LLWaterTri::sMinStep = mMinStep;
+ LLWaterTri::sQueues = &mRoam;
+
+ setGridDim(mMaxGridSize / mMinStep);
+
+ mVtx = new LLVector3[mGridDim1 * mGridDim1];
+ mNorms = new LLVector3[mGridDim1 * mGridDim1];
+
+ mPatches = new LLWaterPatch[mPatchRes * mPatchRes];
+
+ mStepsInRegion = mRegionWidth / mMinStep;
+ const U32 max_div_level = 2 * (U32)(log((F32)mStepsInRegion) / log(2.0f));
+
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ LLVector3 patch_center(mRegionWidth * (x + 0.5f), mRegionWidth * (y + 0.5f), 0);
+
+ mPatches[y * mPatchRes + x].set(x * mStepsInRegion, y * mStepsInRegion,
+ mStepsInRegion, mRegionWidth, patch_center, max_div_level);
+ if (x > 0)
+ {
+ mPatches[y * mPatchRes + x].left()->setRight(mPatches[y * mPatchRes + x - 1].right());
+ mPatches[y * mPatchRes + x - 1].right()->setRight(mPatches[y * mPatchRes + x].left());
+ }
+ if (y > 0)
+ {
+ mPatches[y * mPatchRes + x].left()->setLeft(mPatches[(y - 1) * mPatchRes + x].right());
+ mPatches[(y - 1) * mPatchRes + x].right()->setLeft(mPatches[y * mPatchRes + x].left());
+ }
+ }
+ }
+}
+
+void LLWaterGrid::cleanup()
+{
+ delete[] mVtx;
+ mVtx = NULL;
+
+ delete[] mNorms;
+ mNorms = NULL;
+
+ delete[] mPatches;
+ mPatches = NULL;
+}
+
+
+void setVecZ(LLVector3& v)
+{
+ v.mV[VX] = 0;
+ v.mV[VY] = 0;
+ v.mV[VZ] = 1;
+}
+
+void LLWaterGrid::update()
+{
+ static LLViewerRegion* prev_region = gAgent.getRegion();
+ LLViewerRegion* region = gAgent.getRegion();
+
+ mRegionOrigin = region->getOriginAgent();
+ mRegionOrigin.mV[VX] -= 2 * mRegionWidth;
+ mRegionOrigin.mV[VY] -= 2 * mRegionWidth;
+ mRegionOrigin.mV[VZ] = 0;
+
+ const F32 clip_far = gCamera->getFar() - 31;
+ const F32 clip_far2 = clip_far * clip_far;
+
+ const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
+ const LLVector3 look_at = gCamera->getAtAxis();
+
+
+ if (camera_pos.mV[VZ] > 200)
+ {
+ mResDecrease = 4;
+ }
+ else if (camera_pos.mV[VZ] > 100)
+ {
+ mResDecrease = 2;
+ }
+ else
+ {
+ mResDecrease = 1;
+ }
+
+
+ //U32 mResDecrease = res_decrease;
+ U32 res_decrease = 1;
+
+ const F32 res_change = mResIncrease;// * res_decrease ;
+
+ F32 height;
+
+ // Set the grid
+
+ //U32 fractions = 1;
+ U32 fractions_res = res_decrease;
+ if (res_change < 1)
+ {
+ //fractions = llround(1. / res_change);
+ fractions_res = llround(1.f / mResIncrease);
+ }
+
+
+ //const U32 fractions_res = fractions * res_decrease;
+
+ LLVector3 cur_pos;
+ U32 x, y;
+ U32 ind = 0;
+ for (y = 0; y < mGridDim1; y += fractions_res)
+ {
+ const F32 dispy = (F32)(y * mMinStep);//step;
+ for (x = 0; x < mGridDim1; x += fractions_res)
+ {
+ const F32 dispx = (F32)(x * mMinStep);//step;
+ cur_pos = mRegionOrigin;
+ cur_pos.mV[VX] += dispx;
+ cur_pos.mV[VY] += dispy;
+
+ const F32 x_dist = cur_pos.mV[VX] - camera_pos.mV[VX];
+ const F32 y_dist = cur_pos.mV[VY] - camera_pos.mV[VY];
+
+ if (x_dist * look_at.mV[VX] + y_dist * look_at.mV[VY] < 0)
+ {
+ mVtx[ind] = cur_pos;
+ setVecZ(mNorms[ind]);
+ ind++;
+ continue;
+ }
+
+ const F32 dist_to_vtx2 = x_dist * x_dist + y_dist * y_dist;
+ if (dist_to_vtx2 > .81 * clip_far2)
+ {
+ mVtx[ind] = cur_pos;
+ setVecZ(mNorms[ind]);
+ ind++;
+ continue;
+ }
+
+ mWater.getIntegerHeightAndNormal(llround(WAVE_STEP_INV * dispx),
+ llround(WAVE_STEP_INV * dispy), height, mNorms[ind]);
+
+ cur_pos.mV[VZ] += height;
+ mVtx[ind] = cur_pos;
+ ind++;
+ }
+ }
+
+ if (res_change < 1)
+ {
+ U32 fractions = llround(1.f / mResIncrease);
+ for (y = 0; y < mGridDim1; y += fractions_res)
+ {
+ for (x = 0; x < mGridDim; x += fractions_res)
+ {
+ const U32 ind00 = index(x, y);
+ const U32 ind01 = ind00 + fractions_res;
+ for (U32 frx = 1; frx < fractions; frx += res_decrease)
+ {
+ const U32 ind = ind00 + frx;
+ mNorms[ind] = LERP(mNorms[ind00], mNorms[ind01], frx * res_change);
+ mVtx[ind] = LERP( mVtx[ind00], mVtx[ind01], frx * res_change);
+ }
+ }
+ }
+ for (x = 0; x < mGridDim1; x += res_decrease)
+ {
+ for (y = 0; y < mGridDim; y += fractions_res)
+ {
+ const U32 ind00 = index(x, y);
+ const U32 ind10 = ind00 + fractions_res * mGridDim1;//(y + fractions) * quad_resx1 + x;
+ for (U32 fry = 1; fry < fractions; fry += res_decrease)
+ {
+ const U32 ind = ind00 + fry * mGridDim1;//(y + fry) * quad_resx1 + x;
+ mNorms[ind] = LERP(mNorms[ind00], mNorms[ind10], fry * res_change);
+ mVtx[ind] = LERP( mVtx[ind00], mVtx[ind10], fry * res_change);
+ }
+ }
+ }
+ }
+
+ if (gUseRoam)
+ {
+ updateTree(camera_pos, look_at, clip_far, prev_region != region);
+ }
+ else
+ {
+ updateVisibility(camera_pos, look_at, clip_far);
+ }
+
+ prev_region = region;
+
+
+ //mTab[0][0] = 0;
+ for (y = 0; y < mPatchRes; y++)
+ {
+ for (x = 0; x < mPatchRes; x++)
+ mTab[x][y] = 0;
+ }
+
+}
+
+void LLWaterGrid::updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
+ BOOL restart = FALSE)
+{
+ static S8 recalculate_frame = 0;
+
+ if (restart)
+ {
+ recalculate_frame = 0;
+ }
+
+ if (recalculate_frame == 0)
+ {
+ LLWaterTri::nextRound();
+ setCamPosition(LLWaterTri::sCam, camera_pos);
+ LLWaterTri::sClipFar = clip_far;
+
+
+ const U32 step = (U32)(WAVE_STEP * mResIncrease * mResDecrease);
+ const U32 steps_in_region = mRegionWidth / step;
+ LLWaterTri::sMaxDivLevel = 2 * llround(log((F32)steps_in_region) / log(2.0f));
+
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ U32 patch_ind = y * mPatchRes + x;
+ mPatches[patch_ind].updateTree(camera_pos, look_at, mRegionOrigin);
+ }
+ }
+
+ mRoam.process();
+
+ // debug
+ /*
+ for (y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ //mPatches[y * mPatchRes + x].checkUpToDate();
+ //mPatches[y * mPatchRes + x].checkConsistensy();
+ mPatches[y * mPatchRes + x].checkCount();
+ }
+ }
+ */
+ }
+ ++recalculate_frame;
+ recalculate_frame = recalculate_frame % 2;
+}
+
+void LLWaterGrid::updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far)
+{
+ for (U32 y = 0; y < mPatchRes; y++)
+ {
+ for (U32 x = 0; x < mPatchRes; x++)
+ {
+ mPatches[y * mPatchRes + x].updateVisibility(camera_pos, look_at, mRegionOrigin);
+ }
+ }
+}
+
+
+void LLVOWater::setUseTexture(const BOOL use_texture)
+{
+ mUseTexture = use_texture;
+}
+
+F32 LLWaterSurface::agentDepth() const
+{
+ const LLViewerRegion* region = gAgent.getRegion();
+ LLVector3 position_agent = region->getOriginAgent();// getPositionAgent();
+ const LLVector3 region_origin = position_agent;
+ const LLVector3 camera_pos = gAgent.getCameraPositionAgent();
+
+ F32 height;
+ LLVector3 normal;
+
+ getHeightAndNormal(WAVE_STEP_INV * camera_pos.mV[VX],
+ WAVE_STEP_INV * camera_pos.mV[VY], height, normal);
+ F32 agent_water_height = gAgent.getRegion()->getWaterHeight();
+ return camera_pos.mV[VZ] - (agent_water_height + height);
+}
+
+////////////////////////////////////////////////
+
+
+void LLWaterSurface::getHeightAndNormal(F32 i, F32 j, F32& wave_height, LLVector3& normal) const
+{
+ S32 i_ind = llfloor(i);
+ S32 j_ind = llfloor(j);
+ F32 i_fr = i - i_ind;
+ F32 j_fr = j - j_ind;
+
+ i_ind = i_ind % N_RES;
+ j_ind = j_ind % N_RES;
+
+ S32 i_ind_next = i_ind + 1;
+ S32 j_ind_next = j_ind + 1;
+ if (i_ind_next == (S32)N_RES) i_ind_next = 0;
+ if (j_ind_next == (S32)N_RES) j_ind_next = 0;
+
+ const F32 i_fr1 = 1 - i_fr;
+ const F32 j_fr1 = 1 - j_fr;
+
+ const F32 hi0 = i_fr1 * height(i_ind, j_ind) + i_fr * height(i_ind_next, j_ind);
+ const F32 hi1 = i_fr1 * height(i_ind, j_ind_next) + i_fr * height(i_ind_next, j_ind_next);
+ wave_height = j_fr1 * hi0 + j_fr * hi1;
+
+ normal = i_fr1 * mNorms[i_ind][j_ind];
+ normal += i_fr * mNorms[i_ind_next][j_ind];
+ LLVector3 vi1 = i_fr1 * mNorms[i_ind][j_ind_next];
+ vi1 += i_fr * mNorms[i_ind_next][j_ind_next];
+ normal *= j_fr1;
+ normal += j_fr * vi1;
+
+ //normal.normVec();
+}
+
+void LLWaterSurface::getIntegerHeightAndNormal(S32 i, S32 j, F32& wave_height, LLVector3& normal) const
+{
+ S32 i_ind = i % N_RES;
+ S32 j_ind = j % N_RES;
+
+ wave_height = height(i_ind, j_ind);
+ normal = mNorms[i_ind][j_ind];
+}
+
+F32 LLWaterSurface::phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small)
+{
+ F32 k2 = k * k;
+ F32 k_dot_wind = k * wind_n;
+ F32 spectrum = mA * (F32) exp(-1 / (L * L * k2)) / (k2 * k2) * (k_dot_wind * k_dot_wind / k2);
+
+ if (k_dot_wind < 0) spectrum *= .25f; // scale down waves that move opposite to the wind
+
+ F32 damp = (F32) exp(- k2 * L_small * L_small);
+
+ return (spectrum * damp);
+}
+
+
+
+void LLWaterSurface::initAmplitudes()
+{
+ U16 i, j;
+ LLVector2 k;
+ F32 sqrtPhillips;
+
+ const LLVector2 wind(mWind.mV);
+
+ LLVector2 wind_n = wind;
+ const F32 wind_vel = wind_n.normVec();
+
+ const F32 L = wind_vel * wind_vel / g; // largest wave arising from constant wind of speed wind_vel
+
+ const F32 L_small = L / 70; // eliminate waves with very small length (L_small << L)
+
+
+ for (i = 0; i <= N_RES; i++)
+ {
+ k.mV[VX] = (- (S32)N_RES_HALF + i) * (F_TWO_PI / WIDTH);
+ for (j = 0; j <= N_RES; j++)
+ {
+ k.mV[VY] = (- (S32)N_RES_HALF + j) * (F_TWO_PI / WIDTH);
+
+ const F32 k_mag = k.magVec();
+ mOmega[i][j] = (F32) sqrt(g * k_mag);
+
+ if (k_mag < F_APPROXIMATELY_ZERO)
+ sqrtPhillips = 0;
+ else
+ sqrtPhillips = (F32) sqrt(phillips(k, wind_n, L, L_small));
+
+ //const F32 r1 = rand() / (F32) RAND_MAX;
+ //const F32 r2 = rand() / (F32) RAND_MAX;
+ const F32 r1 = randGauss(0, 1);
+ const F32 r2 = randGauss(0, 1);
+
+ mHtilda0[i][j].re = sqrtPhillips * r1 * OO_SQRT2;
+ mHtilda0[i][j].im = sqrtPhillips * r2 * OO_SQRT2;
+
+ }
+ }
+
+ mPlan.init(N_RES, N_RES);
+ mInitialized = 1; // initialization complete
+}
+
+void LLWaterSurface::generateWaterHeightField(F64 t)
+{
+ S32 i, j;
+ S32 mi, mj; // -K indices
+ COMPLEX plus, minus;
+
+ if (!mInitialized) initAmplitudes();
+
+ for (i = 0; i < (S32)N_RES_HALF; i++)
+ {
+ mi = N_RES - i;
+ for (j = 0; j < (S32)N_RES ; j++)
+ {
+ mj = N_RES - j;
+
+ const F32 cos_wt = cosf(mOmega[i][j] * t); // = cos(-mOmega[i][j] * t)
+ const F32 sin_wt = sinf(mOmega[i][j] * t); // = -sin(-mOmega[i][j] * t)
+ plus.re = mHtilda0[i][j].re * cos_wt - mHtilda0[i][j].im * sin_wt;
+ plus.im = mHtilda0[i][j].re * sin_wt + mHtilda0[i][j].im * cos_wt;
+ minus.re = mHtilda0[mi][mj].re * cos_wt - mHtilda0[mi][mj].im * sin_wt;
+ minus.im = -mHtilda0[mi][mj].re * sin_wt - mHtilda0[mi][mj].im * cos_wt;
+
+ // now sum the plus and minus waves to get the total wave amplitude h
+ mHtilda[i * N_RES + j].re = plus.re + minus.re;
+ mHtilda[i * N_RES + j].im = plus.im + minus.im;
+ if (mi < (S32)N_RES && mj < (S32)N_RES)
+ {
+ mHtilda[mi * N_RES + mj].re = plus.re + minus.re;
+ mHtilda[mi * N_RES + mj].im = -plus.im - minus.im;
+ }
+ }
+ }
+
+ inverse_fft(mPlan, mHtilda, N_RES, N_RES);
+
+ calcNormals();
+}
+
+
+/*
+ * Computer normals by taking finite differences.
+ */
+
+void LLWaterSurface::calcNormals()
+{
+ LLVector3 n;
+
+ for (U32 i = 0; i < N_RES; i++)
+ {
+ for (U32 j = 0; j < N_RES; j++)
+ {
+ F32 px = heightWrapped(i + 1, j);
+ F32 mx = heightWrapped(i - 1, j);
+ F32 py = heightWrapped(i, j + 1);
+ F32 my = heightWrapped(i, j - 1);
+ F32 pxpy = heightWrapped(i + 1, j + 1);
+ F32 pxmy = heightWrapped(i + 1, j - 1);
+ F32 mxpy = heightWrapped(i - 1, j + 1);
+ F32 mxmy = heightWrapped(i - 1, j - 1);
+
+ n.mV[VX] = -((2 * px + pxpy + pxmy) - (2 * mx + mxpy + mxmy));
+ n.mV[VY] = -((2 * py + pxpy + mxpy) - (2 * my + pxmy + mxmy));
+ n.mV[VZ] = 8 * WIDTH / (F32) N_RES;
+ n.normVec();
+
+ mNorms[i][j] = n;
+ }
+ }
+}
+
+void LLVOWater::generateNewWaves(F64 time)
+{
+ getWaterSurface()->generateWaterHeightField(time);
+ sGrid->update();
+}
+
diff --git a/indra/newview/llvowater.h b/indra/newview/llvowater.h
new file mode 100644
index 0000000000..2b846dba20
--- /dev/null
+++ b/indra/newview/llvowater.h
@@ -0,0 +1,240 @@
+/**
+ * @file llvowater.h
+ * @brief Description of LLVOWater class
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_VOWATER_H
+#define LL_VOWATER_H
+
+#include "llviewerobject.h"
+#include "llviewerimage.h"
+#include "v2math.h"
+#include "llfft.h"
+
+#include "llwaterpatch.h"
+
+
+const U32 N_RES = 16; //32 // number of subdivisions of wave tile
+const U8 WAVE_STEP = 8;
+
+/*
+#define N_DET 32 // number of subdivisions of wave tile for details
+
+class LLWaterDetail
+{
+protected:
+ S32 mResolution;
+ LLViewerImage *mTex;
+ U8 *mTexData;
+
+public:
+ LLWaterDetail() : mResolution(N_DET), mTex(0), mTexData(0) {init();}
+ void init();
+
+ ~LLWaterDetail()
+ {
+ delete[] mTexData;
+ mTexData = NULL;
+ }
+
+
+ //void initEmpty();
+
+ void setPixel(const LLVector3 &norm, const S32 i, const S32 j)
+ {
+ S32 offset = (i * mResolution + j) * 3;
+ mTexData[offset] = llround(norm.mV[VX] * 255);
+ mTexData[offset+1] = llround(norm.mV[VX] * 255);
+ mTexData[offset+2] = llround(norm.mV[VX] * 255);
+ }
+ void setPixel(F32 x, F32 y, F32 z, const S32 i, const S32 j)
+ {
+ S32 offset = (i * mResolution + j) * 3;
+ mTexData[offset] = llround(x * 255);
+ mTexData[offset+1] = llround(y * 255);
+ mTexData[offset+2] = llround(z * 255);
+ }
+
+ S32 getResolution() { return mResolution; }
+
+ void createDetailBumpmap(F32* u, F32* v);
+ void createTexture() const { mTex->createTexture(); }
+ void bindTexture() const { mTex->bindTexture(); }
+ LLViewerImage* getTexture() const { return mTex; }
+};
+*/
+
+class LLWaterSurface
+{
+protected:
+ BOOL mInitialized;
+ LLVector3 mWind;
+ F32 mA;
+ F32 mVisc; // viscosity of the fluid
+ F32 mShininess;
+
+ //LLWaterDetail* mDetail;
+
+ LLFFTPlan mPlan;
+ F32 mOmega[N_RES+1][N_RES+1]; // wave frequency
+ COMPLEX mHtilda0[N_RES+1][N_RES+1]; // wave amplitudes and phases at time 0.
+ LLVector3 mNorms[N_RES][N_RES];
+ COMPLEX mHtilda[N_RES * N_RES];
+
+public:
+ LLWaterSurface();
+ ~LLWaterSurface() {}
+
+ //void initSpecularLookup();
+
+ F32 phillips(const LLVector2& k, const LLVector2& wind_n, F32 L, F32 L_small);
+
+ void initAmplitudes();
+
+ F32 height(S32 i, S32 j) const { return mHtilda[i * N_RES + j].re; }
+ F32 heightWrapped(S32 i, S32 j) const
+ {
+ return height((i + N_RES) % N_RES, (j + N_RES) % N_RES);
+ }
+
+ void generateWaterHeightField(F64 time);
+ void calcNormals();
+
+ void getHeightAndNormal(F32 i, F32 j, F32& height, LLVector3& normal) const;
+ void getIntegerHeightAndNormal(S32 i, S32 j, F32& height, LLVector3& normal) const;
+ F32 agentDepth() const;
+
+// const LLWaterHeightField* hField() const { return &mHeightField; }
+
+ //void generateDetail(F64 t);
+ //void fluidSolver(F32* u, F32* v, F32* u0, F32* v0, F64 dt);
+
+ const LLVector3& getWind() const { return mWind; }
+ void setWind(const LLVector3& w) { mWind = w; } // initialized = 0?
+ F32 A() const { return mA; }
+ void setA(F32 a) { mA = a; }
+ F32 getShininess() const { return mShininess; }
+ //LLViewerImage* getSpecularLookup() const { return mSpecularLookup; }
+ //LLViewerImage* getDetail() const { return mDetail->getTexture(); }
+};
+
+class LLVOWater;
+
+class LLWaterGrid
+{
+public:
+ LLWaterGrid();
+
+ void init();
+ void cleanup();
+
+ LLWaterSurface* getWaterSurface() { return &mWater; }
+
+ void update();
+ void updateTree(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far,
+ BOOL restart);
+ void updateVisibility(const LLVector3 &camera_pos, const LLVector3 &look_at, F32 clip_far);
+
+ LLVector3 mRegionOrigin;
+
+ LLVector3* mVtx;
+ LLVector3* mNorms;
+ U32 mRegionWidth;
+ U32 mMaxGridSize;
+ U32 mPatchRes;
+ U32 mMinStep;
+ U32 mStepsInRegion;
+ LLWaterPatch* mPatches;
+ LLRoam mRoam;
+ F32 mResIncrease;
+ U32 mResDecrease;
+
+ LLVOWater* mTab[5][5];
+
+ U32 gridDim() const { return mGridDim; }
+ U32 rowSize() const { return mGridDim1; }
+ void setGridDim(U32 gd) { mGridDim = gd; mGridDim1 = mGridDim + 1; }
+ U32 index(const LL2Coord& c) const { return c.y() * mGridDim1 + c.x(); }
+ U32 index(U32 x, U32 y) const { return y * mGridDim1 + x; }
+
+ LLVector3 vtx(const LL2Coord& c, F32 raised) const
+ {
+ LLVector3 v = mVtx[index(c)];
+ v.mV[VZ] += raised;
+ return v;
+ }
+
+ LLVector3 vtx(U32 x, U32 y, F32 raised) const
+ {
+ LLVector3 v = mVtx[index(x, y)];
+ v.mV[VZ] += raised;
+ return v;
+ }
+
+ void setVertex(const U32 x, const U32 y, const F32 raised, LLVector3 &vertex) const
+ {
+ vertex = mVtx[index(x, y)];
+ vertex.mV[VZ] += raised;
+ }
+
+ void setCamPosition(LL2Coord& cam, const LLVector3& cam_pos)
+ {
+ cam.x() = llround((cam_pos.mV[VX] - mRegionOrigin.mV[VX]) / mMinStep);
+ cam.y() = llround((cam_pos.mV[VY] - mRegionOrigin.mV[VY]) / mMinStep);
+ }
+
+ LLVector3 vtx(const LL2Coord& c) const { return mVtx[index(c)]; }
+ LLVector3 norm(const LL2Coord& c) const { return mNorms[index(c)]; }
+ LLVector3 vtx(U32 x, U32 y) const { return mVtx[index(x, y)]; }
+ LLVector3 norm(U32 x, U32 y) const { return mNorms[index(x, y)]; }
+
+protected:
+ LLWaterSurface mWater;
+ U32 mGridDim;
+ U32 mGridDim1;
+};
+
+class LLSurface;
+class LLHeavenBody;
+class LLVOSky;
+class LLFace;
+
+class LLVOWater : public LLViewerObject
+{
+public:
+ LLVOWater(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp);
+ virtual ~LLVOWater() {}
+
+ /*virtual*/ void markDead();
+
+ // Initialize data that's only inited once per class.
+ static void initClass();
+ static void cleanupClass();
+
+ /*virtual*/ BOOL idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time);
+ /*virtual*/ LLDrawable* createDrawable(LLPipeline *pipeline);
+ /*virtual*/ BOOL updateGeometry(LLDrawable *drawable);
+ BOOL updateGeometryFlat(LLDrawable *drawable);
+ BOOL updateGeometryHeightFieldSimple(LLDrawable *drawable);
+ BOOL updateGeometryHeightFieldRoam(LLDrawable *drawable);
+
+ /*virtual*/ void updateDrawable(BOOL force_damped);
+ /*virtual*/ void updateTextures(LLAgent &agent);
+ /*virtual*/ void setPixelAreaAndAngle(LLAgent &agent); // generate accurate apparent angle and area
+
+ /*virtual*/ BOOL isActive() const; // Whether this object needs to do an idleUpdate.
+
+ void setUseTexture(const BOOL use_texture);
+
+ static void generateNewWaves(F64 time);
+ static LLWaterSurface* getWaterSurface() { return sGrid->getWaterSurface(); }//return &mWater; }
+ static const LLWaterGrid* getGrid() { return sGrid; }
+protected:
+ BOOL mUseTexture;
+ static LLWaterGrid *sGrid;
+};
+
+#endif // LL_VOSURFACEPATCH_H
diff --git a/indra/newview/llwearable.cpp b/indra/newview/llwearable.cpp
new file mode 100644
index 0000000000..ebe3c69c5c
--- /dev/null
+++ b/indra/newview/llwearable.cpp
@@ -0,0 +1,952 @@
+/**
+ * @file llwearable.cpp
+ * @brief LLWearable class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "imageids.h"
+#include "llassetstorage.h"
+#include "lldbstrings.h"
+#include "lldir.h"
+#include "llquantize.h"
+
+#include "llagent.h"
+#include "llviewerwindow.h"
+#include "llfloatercustomize.h"
+#include "llinventorymodel.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llvoavatar.h"
+#include "llwearable.h"
+
+//#include "viewer.h"
+//#include "llvfs.h"
+
+// static
+S32 LLWearable::sCurrentDefinitionVersion = 1;
+
+// static
+const char* LLWearable::sTypeName[ WT_COUNT ] =
+{
+ "shape",
+ "skin",
+ "hair",
+ "eyes",
+ "shirt",
+ "pants",
+ "shoes",
+ "socks",
+ "jacket",
+ "gloves",
+ "undershirt",
+ "underpants",
+ "skirt"
+};
+
+// static
+const char* LLWearable::sTypeLabel[ WT_COUNT ] =
+{
+ "Shape",
+ "Skin",
+ "Hair",
+ "Eyes",
+ "Shirt",
+ "Pants",
+ "Shoes",
+ "Socks",
+ "Jacket",
+ "Gloves",
+ "Undershirt",
+ "Underpants",
+ "Skirt"
+};
+
+
+// static
+LLAssetType::EType LLWearable::typeToAssetType(EWearableType wearable_type)
+{
+ switch( wearable_type )
+ {
+ case WT_SHAPE:
+ case WT_SKIN:
+ case WT_HAIR:
+ case WT_EYES:
+ return LLAssetType::AT_BODYPART;
+ case WT_SHIRT:
+ case WT_PANTS:
+ case WT_SHOES:
+ case WT_SOCKS:
+ case WT_JACKET:
+ case WT_GLOVES:
+ case WT_UNDERSHIRT:
+ case WT_UNDERPANTS:
+ case WT_SKIRT:
+ return LLAssetType::AT_CLOTHING;
+ default:
+ return LLAssetType::AT_NONE;
+ }
+}
+
+
+LLWearable::LLWearable(const LLTransactionID& transaction_id) :
+ mDefinitionVersion(LLWearable::sCurrentDefinitionVersion),
+ mType(WT_SHAPE)
+{
+ mTransactionID = transaction_id;
+ mAssetID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+}
+
+LLWearable::LLWearable(const LLAssetID& asset_id) :
+ mDefinitionVersion( LLWearable::sCurrentDefinitionVersion ),
+ mType(WT_SHAPE)
+{
+ mAssetID = asset_id;
+ mTransactionID.setNull();
+}
+
+LLWearable::~LLWearable()
+{
+ mVisualParamMap.deleteAllData();
+ mTEMap.deleteAllData();
+}
+
+
+// static
+EWearableType LLWearable::typeNameToType( const LLString& type_name )
+{
+ for( S32 i = 0; i < WT_COUNT; i++ )
+ {
+ if( type_name == LLWearable::sTypeName[ i ] )
+ {
+ return (EWearableType)i;
+ }
+ }
+ return WT_INVALID;
+}
+
+
+const char* terse_F32_to_string( F32 f, char s[MAX_STRING] )
+{
+ char* r = s;
+ S32 len = sprintf( s, "%.2f", f );
+
+ // "1.20" -> "1.2"
+ // "24.00" -> "24."
+ while( '0' == r[len - 1] )
+ {
+ len--;
+ r[len] = '\0';
+ }
+
+ if( '.' == r[len - 1] )
+ {
+ // "24." -> "24"
+ len--;
+ r[len] = '\0';
+ }
+ else
+ if( ('-' == r[0]) && ('0' == r[1]) )
+ {
+ // "-0.59" -> "-.59"
+ r++;
+ r[0] = '-';
+ }
+ else
+ if( '0' == r[0] )
+ {
+ // "0.59" -> ".59"
+ r++;
+ }
+
+ return r;
+}
+
+BOOL LLWearable::exportFile( FILE* file )
+{
+ // header and version
+ if( fprintf( file, "LLWearable version %d\n", mDefinitionVersion ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // name
+ if( fprintf( file, "%s\n", mName.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // description
+ if( fprintf( file, "%s\n", mDescription.c_str() ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // permissions
+ if( !mPermissions.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ if( !mSaleInfo.exportFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // wearable type
+ S32 type = (S32)mType;
+ if( fprintf( file, "type %d\n", type ) < 0 )
+ {
+ return FALSE;
+ }
+
+ // parameters
+ S32 num_parameters = mVisualParamMap.getLength();
+ if( fprintf( file, "parameters %d\n", num_parameters ) < 0 )
+ {
+ return FALSE;
+ }
+
+ char s[ MAX_STRING ];
+ for( F32* param_weightp = mVisualParamMap.getFirstData(); param_weightp; param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ if( fprintf( file, "%d %s\n", param_id, terse_F32_to_string( *param_weightp, s ) ) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ // texture entries
+ S32 num_textures = mTEMap.getLength();
+ if( fprintf( file, "textures %d\n", num_textures ) < 0 )
+ {
+ return FALSE;
+ }
+
+ for( LLUUID* image_id = mTEMap.getFirstData(); image_id; image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ char image_id_string[UUID_STR_LENGTH];
+ image_id->toString( image_id_string );
+ if( fprintf( file, "%d %s\n", te, image_id_string) < 0 )
+ {
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+
+BOOL LLWearable::importFile( FILE* file )
+{
+ char text_buffer[2048];
+ S32 fields_read = 0;
+
+ // read header and version
+ fields_read = fscanf( file, "LLWearable version %d\n", &mDefinitionVersion );
+ if( fields_read != 1 )
+ {
+ // Shouldn't really log the asset id for security reasons, but
+ // we need it in this case.
+ llwarns << "Bad Wearable asset header: " << mAssetID << llendl;
+ //gVFS->dumpMap();
+ return FALSE;
+ }
+
+ if( mDefinitionVersion > LLWearable::sCurrentDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ return FALSE;
+ }
+
+ // name
+ char next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no name
+ mName = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mName = text_buffer;
+ LLString::truncate(mName, DB_INV_ITEM_NAME_STR_LEN );
+ }
+
+ // description
+ next_char = fgetc( file );
+ if( '\n' == next_char )
+ {
+ // no description
+ mDescription = "";
+ }
+ else
+ {
+ ungetc( next_char, file );
+ fields_read = fscanf( file, "%[^\n]", text_buffer );
+ if( (1 != fields_read) || (fgetc( file ) != '\n') )
+ {
+ llwarns << "Bad Wearable asset: early end of file" << llendl;
+ return FALSE;
+ }
+ mDescription = text_buffer;
+ LLString::truncate(mDescription, DB_INV_ITEM_DESC_STR_LEN );
+ }
+
+ // permissions
+ S32 perm_version;
+ fields_read = fscanf( file, " permissions %d\n", &perm_version );
+ if( (fields_read != 1) || (perm_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing permissions" << llendl;
+ return FALSE;
+ }
+ if( !mPermissions.importFile( file ) )
+ {
+ return FALSE;
+ }
+
+ // sale info
+ S32 sale_info_version;
+ fields_read = fscanf( file, " sale_info %d\n", &sale_info_version );
+ if( (fields_read != 1) || (sale_info_version != 0) )
+ {
+ llwarns << "Bad Wearable asset: missing sale_info" << llendl;
+ return FALSE;
+ }
+ // Sale info used to contain next owner perm. It is now in the
+ // permissions. Thus, we read that out, and fix legacy
+ // objects. It's possible this op would fail, but it should pick
+ // up the vast majority of the tasks.
+ BOOL has_perm_mask = FALSE;
+ U32 perm_mask = 0;
+ if( !mSaleInfo.importFile(file, has_perm_mask, perm_mask) )
+ {
+ return FALSE;
+ }
+ if(has_perm_mask)
+ {
+ // fair use fix.
+ if(!(perm_mask & PERM_COPY))
+ {
+ perm_mask |= PERM_TRANSFER;
+ }
+ mPermissions.setMaskNext(perm_mask);
+ }
+
+ // wearable type
+ S32 type = -1;
+ fields_read = fscanf( file, "type %d\n", &type );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: bad type" << llendl;
+ return FALSE;
+ }
+ if( 0 <= type && type < WT_COUNT )
+ {
+ mType = (EWearableType)type;
+ }
+ else
+ {
+ llwarns << "Bad Wearable asset: bad type #" << type << llendl;
+ return FALSE;
+ }
+
+
+ // parameters header
+ S32 num_parameters = 0;
+ fields_read = fscanf( file, "parameters %d\n", &num_parameters );
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing parameters block" << llendl;
+ return FALSE;
+ }
+
+ // parameters
+ S32 i;
+ for( i = 0; i < num_parameters; i++ )
+ {
+ S32 param_id = 0;
+ F32 param_weight = 0.f;
+ fields_read = fscanf( file, "%d %f\n", &param_id, &param_weight );
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad parameter, #" << i << llendl;
+ return FALSE;
+ }
+ mVisualParamMap.addData( param_id, new F32(param_weight) );
+ }
+
+ // textures header
+ S32 num_textures = 0;
+ fields_read = fscanf( file, "textures %d\n", &num_textures);
+ if( fields_read != 1 )
+ {
+ llwarns << "Bad Wearable asset: missing textures block" << llendl;
+ return FALSE;
+ }
+
+ // textures
+ for( i = 0; i < num_textures; i++ )
+ {
+ S32 te = 0;
+ fields_read = fscanf( file, "%d %s\n", &te, text_buffer);
+ if( fields_read != 2 )
+ {
+ llwarns << "Bad Wearable asset: bad texture, #" << i << llendl;
+ return FALSE;
+ }
+
+ if( !LLUUID::validate( text_buffer ) )
+ {
+ llwarns << "Bad Wearable asset: bad texture uuid: " << text_buffer << llendl;
+ return FALSE;
+ }
+
+ mTEMap.addData( te, new LLUUID( text_buffer ) );
+ }
+
+ return TRUE;
+}
+
+
+// Avatar parameter and texture definitions can change over time.
+// This function returns true if parameters or textures have been added or removed
+// since this wearable was created.
+BOOL LLWearable::isOldVersion()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion < mDefinitionVersion )
+ {
+ llwarns << "Wearable asset has newer version (" << mDefinitionVersion << ") than XML (" << LLWearable::sCurrentDefinitionVersion << ")" << llendl;
+ llassert(0);
+ }
+
+ if( LLWearable::sCurrentDefinitionVersion != mDefinitionVersion )
+ {
+ return TRUE;
+ }
+
+ S32 param_count = 0;
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ param_count++;
+ if( !mVisualParamMap.checkKey( param->getID() ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( param_count != mVisualParamMap.getLength() )
+ {
+ return TRUE;
+ }
+
+
+ S32 te_count = 0;
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ te_count++;
+ if( !mTEMap.checkKey( te ) )
+ {
+ return TRUE;
+ }
+ }
+ }
+ if( te_count != mTEMap.getLength() )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+// Avatar parameter and texture definitions can change over time.
+// * If parameters or textures have been REMOVED since the wearable was created,
+// they're just ignored, so we consider the wearable clean even though isOldVersion()
+// will return true.
+// * If parameters or textures have been ADDED since the wearable was created,
+// they are taken to have default values, so we consider the wearable clean
+// only if those values are the same as the defaults.
+BOOL LLWearable::isDirty()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return FALSE;
+ }
+
+
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ F32* weightp = mVisualParamMap.getIfThere( param->getID() );
+ F32 weight;
+ if( weightp )
+ {
+ weight = llclamp( *weightp, param->getMinWeight(), param->getMaxWeight() );
+ }
+ else
+ {
+ weight = param->getDefaultWeight();
+ }
+
+ U8 a = F32_to_U8( param->getWeight(), param->getMinWeight(), param->getMaxWeight() );
+ U8 b = F32_to_U8( weight, param->getMinWeight(), param->getMaxWeight() );
+ if( a != b )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* avatar_image = avatar->getTEImage( te );
+ if( !avatar_image )
+ {
+ llassert( 0 );
+ continue;
+ }
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ if( avatar_image->getID() != image_id )
+ {
+ return TRUE;
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // if( mDescription != gFloaterCustomize->getWearableDescription( mType ) )
+ // {
+ // return TRUE;
+ // }
+ //}
+
+ return FALSE;
+}
+
+
+void LLWearable::setParamsToDefaults()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType ) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getDefaultWeight() ) );
+ }
+ }
+}
+
+void LLWearable::setTexturesToDefaults()
+{
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ mTEMap.addData( te, new LLUUID( LLVOAvatar::getDefaultTEImageID( te ) ) );
+ }
+ }
+}
+
+// Updates the user's avatar's appearance
+void LLWearable::writeToAvatar( BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ ESex old_sex = avatar->getSex();
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ F32* weight = mVisualParamMap.getIfThere( param_id );
+ if( weight )
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(*weight, set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, *weight, set_by_user );
+ }
+ }
+ else
+ {
+ // only animate with user-originated changes
+ if (set_by_user)
+ {
+ param->setAnimationTarget(param->getDefaultWeight(), set_by_user);
+ }
+ else
+ {
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+ }
+ }
+
+ // only interpolate with user-originated changes
+ if (set_by_user)
+ {
+ avatar->startAppearanceAnimation(TRUE, TRUE);
+ }
+
+ // Pull texture entries
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ LLViewerImage* image = gImageList.getImage( image_id );
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ avatar->updateVisualParams();
+
+ if( gFloaterCustomize )
+ {
+ LLViewerInventoryItem* item;
+ item = (LLViewerInventoryItem*)gInventory.getItem(gAgent.getWearableItem(mType));
+ U32 perm_mask = PERM_NONE;
+ BOOL is_complete = FALSE;
+ if(item)
+ {
+ perm_mask = item->getPermissions().getMaskOwner();
+ is_complete = item->isComplete();
+ if(!is_complete)
+ {
+ item->fetchFromServer();
+ }
+ }
+ gFloaterCustomize->setWearable(mType, this, perm_mask, is_complete);
+ LLFloaterCustomize::setCurrentWearableType( mType );
+ }
+
+ ESex new_sex = avatar->getSex();
+ if( old_sex != new_sex )
+ {
+ avatar->updateSexDependentLayerSets( set_by_user );
+ }
+
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+// Updates the user's avatar's appearance, replacing this wearables' parameters and textures with default values.
+// static
+void LLWearable::removeFromAvatar( EWearableType type, BOOL set_by_user )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ // You can't just remove body parts.
+ if( (type == WT_SHAPE) ||
+ (type == WT_SKIN) ||
+ (type == WT_HAIR) ||
+ (type == WT_EYES) )
+ {
+ return;
+ }
+
+ // Pull params
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == type) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 param_id = param->getID();
+ avatar->setVisualParamWeight( param_id, param->getDefaultWeight(), set_by_user );
+ }
+ }
+
+ // Pull textures
+ LLViewerImage* image = gImageList.getImage( IMG_DEFAULT_AVATAR );
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == type )
+ {
+ avatar->setLocTexTE( te, image, set_by_user );
+ }
+ }
+
+ if( gFloaterCustomize )
+ {
+ gFloaterCustomize->setWearable(type, NULL, PERM_ALL, TRUE);
+ }
+
+ avatar->updateVisualParams();
+ avatar->updateMeshTextures();
+
+// if( set_by_user )
+// {
+// gAgent.sendAgentSetAppearance();
+// }
+}
+
+
+
+// Updates asset from the user's avatar
+void LLWearable::readFromAvatar()
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mVisualParamMap.deleteAllData();
+ for( LLVisualParam* param = avatar->getFirstVisualParam(); param; param = avatar->getNextVisualParam() )
+ {
+ if( (((LLViewerVisualParam*)param)->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ mVisualParamMap.addData( param->getID(), new F32( param->getWeight() ) );
+ }
+ }
+
+ mTEMap.deleteAllData();
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLViewerImage* image = avatar->getTEImage( te );
+ if( image )
+ {
+ mTEMap.addData( te, new LLUUID( image->getID() ) );
+ }
+ }
+ }
+
+ //if( gFloaterCustomize )
+ //{
+ // mDescription = gFloaterCustomize->getWearableDescription( mType );
+ //}
+}
+
+// Does not copy mAssetID.
+// Definition version is current: removes obsolete enties and creates default values for new ones.
+void LLWearable::copyDataFrom( LLWearable* src )
+{
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ llassert( avatar );
+ if( !avatar )
+ {
+ return;
+ }
+
+ mDefinitionVersion = LLWearable::sCurrentDefinitionVersion;
+
+ mName = src->mName;
+ mDescription = src->mDescription;
+ mPermissions = src->mPermissions;
+ mSaleInfo = src->mSaleInfo;
+ mType = src->mType;
+
+ // Deep copy of mVisualParamMap (copies only those params that are current, filling in defaults where needed)
+ for( LLViewerVisualParam* param = (LLViewerVisualParam*) avatar->getFirstVisualParam();
+ param;
+ param = (LLViewerVisualParam*) avatar->getNextVisualParam() )
+ {
+ if( (param->getWearableType() == mType) && (param->getGroup() == VISUAL_PARAM_GROUP_TWEAKABLE ) )
+ {
+ S32 id = param->getID();
+ F32* weightp = src->mVisualParamMap.getIfThere( id );
+ F32 weight = weightp ? *weightp : param->getDefaultWeight();
+ mVisualParamMap.addData( id, new F32( weight ) );
+ }
+ }
+
+ // Deep copy of mTEMap (copies only those tes that are current, filling in defaults where needed)
+ for( S32 te = 0; te < LLVOAvatar::TEX_NUM_ENTRIES; te++ )
+ {
+ if( LLVOAvatar::getTEWearableType( te ) == mType )
+ {
+ LLUUID* mapped_image_id = src->mTEMap.getIfThere( te );
+ const LLUUID& image_id = mapped_image_id ? *mapped_image_id : LLVOAvatar::getDefaultTEImageID( te );
+ mTEMap.addData( te, new LLUUID( image_id ) );
+ }
+ }
+}
+
+struct LLWearableSaveData
+{
+ EWearableType mType;
+};
+
+void LLWearable::saveNewAsset()
+{
+// llinfos << "LLWearable::saveNewAsset() type: " << getTypeName() << llendl;
+ //dump();
+
+ char new_asset_id_string[UUID_STR_LENGTH];
+ mAssetID.toString(new_asset_id_string);
+ char filename[LL_MAX_PATH];
+ sprintf(filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ FILE* fp = LLFile::fopen(filename, "wb");
+ BOOL successful_save = FALSE;
+ if(fp && exportFile(fp))
+ {
+ successful_save = TRUE;
+ }
+ if(fp)
+ {
+ fclose(fp);
+ fp = NULL;
+ }
+ if(!successful_save)
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save '%s' to wearable file.",
+ mName.c_str());
+ llwarns << buffer << llendl;
+
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = mName;
+ gViewerWindow->alertXml("CannotSaveWearableOutOfSpace", args);
+ return;
+ }
+
+ // save it out to database
+ if( gAssetStorage )
+ {
+ LLWearableSaveData* data = new LLWearableSaveData;
+ data->mType = mType;
+ gAssetStorage->storeAssetData(filename, mTransactionID, getAssetType(),
+ &LLWearable::onSaveNewAssetComplete,
+ (void*)data);
+ }
+}
+
+// static
+void LLWearable::onSaveNewAssetComplete(const LLUUID& new_asset_id, void* userdata, S32 status) // StoreAssetData callback (fixed)
+{
+ LLWearableSaveData* data = (LLWearableSaveData*)userdata;
+ const char* type_name = LLWearable::typeToTypeName(data->mType);
+ if(0 == status)
+ {
+ // Success
+ llinfos << "Saved wearable " << type_name << llendl;
+ }
+ else
+ {
+ char buffer[2*MAX_STRING];
+ sprintf(buffer,
+ "Unable to save %s to central asset store.",
+ type_name);
+ llwarns << buffer << " Status: " << status << llendl;
+ LLStringBase<char>::format_map_t args;
+ args["[NAME]"] = type_name;
+ gViewerWindow->alertXml("CannotSaveToAssetStore", args);
+ }
+
+ // Delete temp file
+ char new_asset_id_string[UUID_STR_LENGTH];
+ new_asset_id.toString(new_asset_id_string);
+ char src_filename[LL_MAX_PATH];
+ sprintf(src_filename, "%s.wbl", gDirUtilp->getExpandedFilename(LL_PATH_CACHE,new_asset_id_string).c_str());
+ LLFile::remove(src_filename);
+
+ // delete the context data
+ delete data;
+}
+
+BOOL LLWearable::isMatchedToInventoryItem( LLViewerInventoryItem* item )
+{
+ return
+ ( mName == item->getName() ) &&
+ ( mDescription == item->getDescription() ) &&
+ ( mPermissions == item->getPermissions() ) &&
+ ( mSaleInfo == item->getSaleInfo() );
+}
+
+void LLWearable::dump()
+{
+ llinfos << "wearable " << LLWearable::typeToTypeName( mType ) << llendl;
+ llinfos << " Name: " << mName << llendl;
+ llinfos << " Desc: " << mDescription << llendl;
+ //mPermissions
+ //mSaleInfo
+
+ llinfos << " Params:" << llendl;
+ for( F32* param_weightp = mVisualParamMap.getFirstData();
+ param_weightp;
+ param_weightp = mVisualParamMap.getNextData() )
+ {
+ S32 param_id = mVisualParamMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << param_id << " " << *param_weightp << llendl;
+ }
+
+ llinfos << " Textures:" << llendl;
+ for( LLUUID* image_id = mTEMap.getFirstData();
+ image_id;
+ image_id = mTEMap.getNextData() )
+ {
+ S32 te = mTEMap.getCurrentKeyWithoutIncrement();
+ llinfos << " " << te << " " << *image_id << llendl;
+ }
+}
+
diff --git a/indra/newview/llwearable.h b/indra/newview/llwearable.h
new file mode 100644
index 0000000000..d2dcd764d5
--- /dev/null
+++ b/indra/newview/llwearable.h
@@ -0,0 +1,119 @@
+/**
+ * @file llwearable.h
+ * @brief LLWearable class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEARABLE_H
+#define LL_LLWEARABLE_H
+
+#include "lluuid.h"
+#include "llptrskipmap.h"
+#include "llstring.h"
+#include "llpermissions.h"
+#include "llsaleinfo.h"
+#include "llassetstorage.h"
+
+class LLViewerInventoryItem;
+
+enum EWearableType // If you change this, update LLWearable::getTypeName(), getTypeLabel(), and LLVOAvatar::getTEWearableType()
+{
+ WT_SHAPE = 0,
+ WT_SKIN = 1,
+ WT_HAIR = 2,
+ WT_EYES = 3,
+ WT_SHIRT = 4,
+ WT_PANTS = 5,
+ WT_SHOES = 6,
+ WT_SOCKS = 7,
+ WT_JACKET = 8,
+ WT_GLOVES = 9,
+ WT_UNDERSHIRT = 10,
+ WT_UNDERPANTS = 11,
+ WT_SKIRT = 12,
+ WT_COUNT = 13,
+ WT_INVALID = 255
+};
+
+class LLWearable
+{
+public:
+ LLWearable(const LLTransactionID& transactionID);
+ LLWearable(const LLAssetID& assetID);
+ ~LLWearable();
+
+ const LLAssetID& getID() { return mAssetID; }
+ const LLTransactionID& getTransactionID() { return mTransactionID; }
+
+ BOOL readData( const char *buffer );
+ BOOL isDirty();
+ BOOL isOldVersion();
+
+ void writeToAvatar( BOOL set_by_user );
+ void readFromAvatar();
+ void removeFromAvatar( BOOL set_by_user ) { LLWearable::removeFromAvatar( mType, set_by_user ); }
+ static void removeFromAvatar( EWearableType type, BOOL set_by_user );
+
+ BOOL exportFile(FILE* file);
+ BOOL importFile(FILE* file);
+
+ EWearableType getType() const { return mType; }
+ void setType( EWearableType type ) { mType = type; }
+
+ void setName( const std::string& name ) { mName = name; }
+ const std::string& getName() { return mName; }
+
+ void setDescription( const std::string& desc ) { mDescription = desc; }
+ const std::string& getDescription() { return mDescription; }
+
+ void setPermissions( const LLPermissions& p ) { mPermissions = p; }
+ const LLPermissions& getPermissions() { return mPermissions; }
+
+ void setSaleInfo( const LLSaleInfo& info ) { mSaleInfo = info; }
+ const LLSaleInfo& getSaleInfo() { return mSaleInfo; }
+
+ const char* getTypeLabel() const { return LLWearable::sTypeLabel[ mType ]; }
+ const char* getTypeName() const { return LLWearable::sTypeName[ mType ]; }
+
+ void setParamsToDefaults();
+ void setTexturesToDefaults();
+
+ LLAssetType::EType getAssetType() const { return LLWearable::typeToAssetType( mType ); }
+
+ static EWearableType typeNameToType( const LLString& type_name );
+ static const char* typeToTypeName( EWearableType type ) { return (type<WT_COUNT) ? LLWearable::sTypeName[type] : "invalid"; }
+ static const char* typeToTypeLabel( EWearableType type ) { return (type<WT_COUNT) ? LLWearable::sTypeLabel[type] : "invalid"; }
+ static LLAssetType::EType typeToAssetType( EWearableType wearable_type );
+
+ void saveNewAsset();
+ static void onSaveNewAssetComplete( const LLUUID& asset_uuid, void* user_data, S32 status );
+
+ BOOL isMatchedToInventoryItem( LLViewerInventoryItem* item );
+
+ void copyDataFrom( LLWearable* src );
+
+ static void setCurrentDefinitionVersion( S32 version ) { LLWearable::sCurrentDefinitionVersion = version; }
+
+ void dump();
+
+private:
+ static S32 sCurrentDefinitionVersion; // Depends on the current state of the avatar_lad.xml.
+ S32 mDefinitionVersion; // Depends on the state of the avatar_lad.xml when this asset was created.
+ LLString mName;
+ LLString mDescription;
+ LLPermissions mPermissions;
+ LLSaleInfo mSaleInfo;
+ LLAssetID mAssetID;
+ LLTransactionID mTransactionID;
+ EWearableType mType;
+
+ LLPtrSkipMap<S32, F32*> mVisualParamMap; // maps visual param id to weight
+ LLPtrSkipMap<S32, LLUUID*> mTEMap; // maps TE to Image ID
+
+ static const char* LLWearable::sTypeName[ WT_COUNT ];
+ static const char* LLWearable::sTypeLabel[ WT_COUNT ];
+};
+
+#endif // LL_LLWEARABLE_H
diff --git a/indra/newview/llwearablelist.cpp b/indra/newview/llwearablelist.cpp
new file mode 100644
index 0000000000..297ced71fc
--- /dev/null
+++ b/indra/newview/llwearablelist.cpp
@@ -0,0 +1,325 @@
+/**
+ * @file llwearablelist.cpp
+ * @brief LLWearableList class implementation
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llwearablelist.h"
+
+#include "message.h"
+#include "llassetstorage.h"
+#include "llagent.h"
+#include "llvoavatar.h"
+#include "llviewerinventory.h"
+//#include "llfloaterchat.h"
+#include "llviewerstats.h"
+#include "llnotify.h"
+
+// Globals
+LLWearableList gWearableList;
+
+
+struct LLWearableArrivedData
+{
+ LLWearableArrivedData(
+ LLAssetType::EType asset_type,
+ const LLString& wearable_name,
+ void(*asset_arrived_callback)(LLWearable*, void* userdata),
+ void* userdata )
+ :
+ mAssetType( asset_type ),
+ mCallback( asset_arrived_callback ),
+ mUserdata( userdata ),
+ mName( wearable_name )
+ {}
+
+ LLAssetType::EType mAssetType;
+ void (*mCallback)(LLWearable*, void* userdata);
+ void* mUserdata;
+ LLString mName;
+};
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// LLWearableList
+
+LLWearableList::~LLWearableList()
+{
+ mList.deleteAllData();
+}
+
+void LLWearableList::getAsset( const LLAssetID& assetID, const LLString& wearable_name, LLAssetType::EType asset_type, void(*asset_arrived_callback)(LLWearable*, void* userdata), void* userdata )
+{
+ llassert( (asset_type == LLAssetType::AT_CLOTHING) || (asset_type == LLAssetType::AT_BODYPART) );
+ LLWearable* instance = mList.getIfThere( assetID );
+ if( instance )
+ {
+ asset_arrived_callback( instance, userdata );
+ }
+ else
+ {
+ gAssetStorage->getAssetData(
+ assetID,
+ asset_type,
+ LLWearableList::processGetAssetReply,
+ (void*)new LLWearableArrivedData( asset_type, wearable_name, asset_arrived_callback, userdata ),
+ TRUE);
+ }
+}
+
+// static
+void LLWearableList::processGetAssetReply( const char* filename, const LLAssetID& uuid, void* userdata, S32 status )
+{
+ BOOL success = FALSE;
+ LLWearableArrivedData* data = (LLWearableArrivedData*) userdata;
+
+ if( !filename )
+ {
+ llinfos << "Bad Wearable Asset: missing file." << llendl;
+ }
+ else
+ if( status >= 0 )
+ {
+ // read the file
+ FILE* fp = LLFile::fopen(filename, "rb");
+ if( !fp )
+ {
+ llinfos << "Bad Wearable Asset: unable to open file: '" << filename << "'" << llendl;
+ }
+ else
+ {
+ LLWearable *wearable = new LLWearable(uuid);
+ if( wearable->importFile( fp ) )
+ {
+// llinfos << "processGetAssetReply()" << llendl;
+// wearable->dump();
+
+ gWearableList.mList[ uuid ] = wearable;
+ if( data->mCallback )
+ {
+ data->mCallback( wearable, data->mUserdata );
+ }
+ success = TRUE;
+ }
+ else
+ {
+ LLString::format_map_t args;
+ // XUI:translate
+ args["[TYPE]"] = LLAssetType::lookupHumanReadable(data->mAssetType);
+ if (data->mName.empty())
+ {
+ LLNotifyBox::showXml("FailedToLoadWearableUnnamed", args);
+ }
+ else
+ {
+ args["[DESC]"] = data->mName;
+ LLNotifyBox::showXml("FailedToLoadWearable", args);
+ }
+ delete wearable;
+ }
+
+ fclose( fp );
+ if(filename)
+ {
+ LLFile::remove(filename);
+ }
+ }
+ }
+ else
+ {
+ if(filename)
+ {
+ LLFile::remove(filename);
+ }
+ if( gViewerStats )
+ {
+ gViewerStats->incStat( LLViewerStats::ST_DOWNLOAD_FAILED );
+ }
+
+ llwarns << "Wearable download failed: " << LLAssetStorage::getErrorString( status ) << " " << uuid << llendl;
+ switch( status )
+ {
+ case LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE:
+ {
+ LLString::format_map_t args;
+ // XUI:translate
+ args["[TYPE]"] = LLAssetType::lookupHumanReadable(data->mAssetType);
+ if (data->mName.empty())
+ {
+ LLNotifyBox::showXml("FailedToFindWearableUnnamed", args);
+ }
+ else
+ {
+ args["[DESC]"] = data->mName;
+ LLNotifyBox::showXml("FailedToFindWearable", args);
+ }
+
+ // Asset does not exist in the database.
+ // Can't load asset, so return NULL
+ if( data->mCallback )
+ {
+ data->mCallback( NULL, data->mUserdata );
+ }
+ break;
+ }
+ default:
+ {
+ // Try again
+ gAssetStorage->getAssetData(uuid,
+ data->mAssetType,
+ LLWearableList::processGetAssetReply,
+ userdata); // re-use instead of deleting.
+ return;
+ }
+ }
+ }
+
+ delete data;
+}
+
+
+LLWearable* LLWearableList::createLegacyWearableFromAvatar( EWearableType type )
+{
+ llinfos << "LLWearableList::createLegacyWearableFromAvatar" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->setType( type );
+ wearable->setName( wearable->getTypeLabel() );
+ wearable->setDescription( "Recovered from lost asset." );
+
+ LLVOAvatar* avatar = gAgent.getAvatarObject();
+ LLPermissions perm;
+ perm.init( avatar->getID(), avatar->getID(), LLUUID::null, LLUUID::null );
+ perm.initMasks(PERM_TRANSFER, PERM_TRANSFER, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ wearable->setPermissions( perm );
+
+ // Save info is the default.
+
+ wearable->readFromAvatar();
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+
+// Creates a new wearable just like the old_wearable but with data copied over from item
+LLWearable* LLWearableList::createWearableMatchedToInventoryItem( LLWearable* old_wearable, LLViewerInventoryItem* item )
+{
+ lldebugs << "LLWearableList::createWearableMatchedToInventoryItem()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+
+ wearable->setName( item->getName() );
+ wearable->setDescription( item->getDescription() );
+ wearable->setPermissions( item->getPermissions() );
+ wearable->setSaleInfo( item->getSaleInfo() );
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+LLWearable* LLWearableList::createCopyFromAvatar( LLWearable* old_wearable, const std::string& new_name )
+{
+ lldebugs << "LLWearableList::createCopyFromAvatar()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+ LLPermissions perm(old_wearable->getPermissions());
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true);
+ wearable->setPermissions(perm);
+ wearable->readFromAvatar(); // update from the avatar
+
+ if (!new_name.empty()) wearable->setName(new_name);
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+
+LLWearable* LLWearableList::createCopy( LLWearable* old_wearable )
+{
+ lldebugs << "LLWearableList::createCopy()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->copyDataFrom( old_wearable );
+ LLPermissions perm(old_wearable->getPermissions());
+ perm.setOwnerAndGroup(LLUUID::null, gAgent.getID(), LLUUID::null, true);
+ wearable->setPermissions(perm);
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
+
+LLWearable* LLWearableList::createNewWearable( EWearableType type )
+{
+ lldebugs << "LLWearableList::createNewWearable()" << llendl;
+
+ LLTransactionID tid;
+ LLAssetID new_asset_id;
+ tid.generate();
+ new_asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
+
+ LLWearable* wearable = new LLWearable( tid );
+ wearable->setType( type );
+
+ LLString name = "New ";
+ name.append( wearable->getTypeLabel() );
+ wearable->setName( name );
+
+ LLPermissions perm;
+ perm.init(gAgent.getID(), gAgent.getID(), LLUUID::null, LLUUID::null);
+ perm.initMasks(PERM_ALL, PERM_ALL, PERM_NONE, PERM_NONE, PERM_MOVE | PERM_TRANSFER);
+ wearable->setPermissions(perm);
+
+ // Description and sale info have default values.
+
+ wearable->setParamsToDefaults();
+ wearable->setTexturesToDefaults();
+
+ mList[ new_asset_id ] = wearable;
+
+ // Send to the dataserver
+ wearable->saveNewAsset();
+
+ return wearable;
+}
diff --git a/indra/newview/llwearablelist.h b/indra/newview/llwearablelist.h
new file mode 100644
index 0000000000..587ea9e507
--- /dev/null
+++ b/indra/newview/llwearablelist.h
@@ -0,0 +1,50 @@
+/**
+ * @file llwearablelist.h
+ * @brief LLWearableList class header file
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEARABLELIST_H
+#define LL_LLWEARABLELIST_H
+
+#include "llwearable.h"
+#include "llskiplist.h"
+#include "lluuid.h"
+#include "llassetstorage.h"
+
+class LLWearableList
+{
+public:
+ LLWearableList() {}
+ ~LLWearableList();
+
+ S32 getLength() { return mList.getLength(); }
+ const LLWearable* getFirst() { return mList.getFirstData(); }
+ const LLWearable* getNext() { return mList.getNextData(); }
+
+ void getAsset(
+ const LLAssetID& assetID,
+ const LLString& wearable_name,
+ LLAssetType::EType asset_type,
+ void(*asset_arrived_callback)(LLWearable*, void* userdata),
+ void* userdata );
+
+ LLWearable* createLegacyWearableFromAvatar( EWearableType type );
+
+ LLWearable* createWearableMatchedToInventoryItem( LLWearable* old_wearable, LLViewerInventoryItem* item );
+ LLWearable* createCopyFromAvatar( LLWearable* old_wearable, const std::string& new_name = "" );
+ LLWearable* createCopy( LLWearable* old_wearable );
+ LLWearable* createNewWearable( EWearableType type );
+
+ // Pseudo-private
+ static void processGetAssetReply(const char* filename, const LLAssetID& assetID, void* user_data, S32 status);
+
+protected:
+ LLPtrSkipMap< const LLUUID, LLWearable* > mList;
+};
+
+extern LLWearableList gWearableList;
+
+#endif // LL_LLWEARABLELIST_H
diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp
new file mode 100644
index 0000000000..d57bb7da82
--- /dev/null
+++ b/indra/newview/llweb.cpp
@@ -0,0 +1,69 @@
+/**
+ * @file llweb.cpp
+ * @brief Functions dealing with web browsers
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llweb.h"
+
+#include "llwindow.h"
+
+#include "llfloaterhtml.h"
+#include "llviewercontrol.h"
+
+// static
+void LLWeb::loadURL(std::string url)
+{
+#if LL_MOZILLA_ENABLED
+ if (gSavedSettings.getBOOL("UseExternalBrowser"))
+ {
+ loadURLExternal(url);
+ }
+ else
+ {
+ LLFloaterHTML::show((void*)url.c_str());
+ }
+#else
+ loadURLExternal(url);
+#endif
+}
+
+
+// static
+void LLWeb::loadURLExternal(std::string url)
+{
+ std::string escaped_url = escapeURL(url);
+ spawn_web_browser(escaped_url.c_str());
+}
+
+
+// static
+std::string LLWeb::escapeURL(std::string url)
+{
+ // The CURL curl_escape() function escapes colons, slashes,
+ // and all characters but A-Z and 0-9. Do a cheesy mini-escape.
+ std::string escaped_url;
+ S32 len = url.length();
+ for (S32 i = 0; i < len; i++)
+ {
+ char c = url[i];
+ if (c == ' ')
+ {
+ escaped_url += "%20";
+ }
+ else if (c == '\\')
+ {
+ escaped_url += "%5C";
+ }
+ else
+ {
+ escaped_url += c;
+ }
+ }
+ return escaped_url;
+}
diff --git a/indra/newview/llweb.h b/indra/newview/llweb.h
new file mode 100644
index 0000000000..69bfd4f3ae
--- /dev/null
+++ b/indra/newview/llweb.h
@@ -0,0 +1,31 @@
+/**
+ * @file llweb.h
+ * @brief Functions dealing with web browsers
+ * @author James Cook
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWEB_H
+#define LL_LLWEB_H
+
+#include <string>
+
+class LLWeb
+{
+public:
+ // Loads unescaped url in either internal web browser or external
+ // browser, depending on user settings.
+ static void loadURL(std::string url);
+
+ static void loadURL(const char* url) { loadURL( std::string(url) ); }
+
+ // Loads unescaped url in external browser.
+ static void loadURLExternal(std::string url);
+
+ // Returns escaped (eg, " " to "%20") url
+ static std::string escapeURL(std::string url);
+};
+
+#endif
diff --git a/indra/newview/llwind.cpp b/indra/newview/llwind.cpp
new file mode 100644
index 0000000000..15b6304136
--- /dev/null
+++ b/indra/newview/llwind.cpp
@@ -0,0 +1,340 @@
+/**
+ * @file llwind.cpp
+ * @brief LLWind class implementation
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Wind is a lattice. It is computed on the simulator, and transmitted to the viewer.
+// It drives special effects like smoke blowing, trees bending, and grass wiggling.
+//
+// Currently wind lattice does not interpolate correctly to neighbors. This will need
+// work.
+
+#include "llviewerprecompiledheaders.h"
+#include "indra_constants.h"
+
+#include "llwind.h"
+
+// linden libraries
+#include "llgl.h"
+#include "patch_dct.h"
+#include "patch_code.h"
+
+// viewer
+#include "noise.h"
+#include "v4color.h"
+#include "viewer.h"
+#include "llagent.h"
+#include "llworld.h"
+
+
+const F32 CLOUD_DIVERGENCE_COEF = 0.5f;
+
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+LLWind::LLWind()
+: mSize(16),
+ mCloudDensityp(NULL)
+{
+ init();
+}
+
+
+LLWind::~LLWind()
+{
+ delete [] mVelX;
+ delete [] mVelY;
+ delete [] mCloudVelX;
+ delete [] mCloudVelY;
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// Public Methods
+//////////////////////////////////////////////////////////////////////
+
+
+void LLWind::init()
+{
+ // Initialize vector data
+ mVelX = new F32[mSize*mSize];
+ mVelY = new F32[mSize*mSize];
+
+ mCloudVelX = new F32[mSize*mSize];
+ mCloudVelY = new F32[mSize*mSize];
+
+ S32 i;
+ for (i = 0; i < mSize*mSize; i++)
+ {
+ mVelX[i] = 0.5f;
+ mVelY[i] = 0.5f;
+ mCloudVelX[i] = 0.0f;
+ mCloudVelY[i] = 0.0f;
+ }
+}
+
+
+void LLWind::decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp)
+{
+ if (!mCloudDensityp)
+ {
+ return;
+ }
+
+ LLPatchHeader patch_header;
+ S32 buffer[16*16];
+
+ init_patch_decompressor(group_headerp->patch_size);
+
+ // Don't use the packed group_header stride because the strides used on
+ // simulator and viewer are not equal.
+ group_headerp->stride = group_headerp->patch_size;
+ set_group_of_patch_header(group_headerp);
+
+ // X component
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, buffer);
+ decompress_patch(mVelX, buffer, &patch_header);
+
+ // Y component
+ decode_patch_header(bitpack, &patch_header);
+ decode_patch(bitpack, buffer);
+ decompress_patch(mVelY, buffer, &patch_header);
+
+
+
+ S32 i, j, k;
+ // HACK -- mCloudVelXY is the same as mVelXY, except we add a divergence
+ // that is proportional to the gradient of the cloud density
+ // ==> this helps to clump clouds together
+ // NOTE ASSUMPTION: cloud density has the same dimensions as the wind field
+ // This needs to be fixed... causes discrepency at region boundaries
+
+ for (j=1; j<mSize-1; j++)
+ {
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k - 1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k - mSize));
+ }
+ }
+
+ i = mSize - 1;
+ for (j=1; j<mSize-1; j++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k) - *(mCloudDensityp + k - 2));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k - mSize));
+ }
+ i = 0;
+ for (j=1; j<mSize-1; j++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 2) - *(mCloudDensityp + k));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + mSize) - *(mCloudDensityp + k + mSize));
+ }
+ j = mSize - 1;
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k - 1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k) - *(mCloudDensityp + k - 2*mSize));
+ }
+ j = 0;
+ for (i=1; i<mSize-1; i++)
+ {
+ k = i + j * mSize;
+ *(mCloudVelX + k) = *(mVelX + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 1) - *(mCloudDensityp + k -1));
+ *(mCloudVelY + k) = *(mVelY + k) + CLOUD_DIVERGENCE_COEF * (*(mCloudDensityp + k + 2*mSize) - *(mCloudDensityp + k));
+ }
+}
+
+
+LLVector3 LLWind::getAverage()
+{
+ // Returns in average_wind the average wind velocity
+ LLVector3 average(0.0f, 0.0f, 0.0f);
+ S32 i, grid_count;
+ grid_count = mSize * mSize;
+ for (i = 0; i < grid_count; i++)
+ {
+ average.mV[VX] += mVelX[i];
+ average.mV[VY] += mVelY[i];
+ }
+
+ average *= 1.f/((F32)(grid_count)) * WIND_SCALE_HACK;
+ return average;
+}
+
+
+LLVector3 LLWind::getVelocityNoisy(const LLVector3 &pos_region, const F32 dim)
+{
+ // Resolve a value, using fractal summing to perturb the returned value
+ LLVector3 r_val(0,0,0);
+ F32 norm = 1.0f;
+ if (dim == 8)
+ {
+ norm = 1.875;
+ }
+ else if (dim == 4)
+ {
+ norm = 1.75;
+ }
+ else if (dim == 2)
+ {
+ norm = 1.5;
+ }
+
+ F32 temp_dim = dim;
+ while (temp_dim >= 1.0)
+ {
+ LLVector3 pos_region_scaled(pos_region * temp_dim);
+ r_val += getVelocity(pos_region_scaled) * (1.0f/temp_dim);
+ temp_dim /= 2.0;
+ }
+
+ return r_val * (1.0f/norm) * WIND_SCALE_HACK;
+}
+
+
+LLVector3 LLWind::getVelocity(const LLVector3 &pos_region)
+{
+ llassert(mSize == 16);
+ // Resolves value of wind at a location relative to SW corner of region
+ //
+ // Returns wind magnitude in X,Y components of vector3
+ LLVector3 r_val;
+ F32 dx,dy;
+ S32 k;
+
+ LLVector3 pos_clamped_region(pos_region);
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ if (pos_clamped_region.mV[VX] < 0.f)
+ {
+ pos_clamped_region.mV[VX] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VX] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VX] = (F32) fmod(pos_clamped_region.mV[VX], region_width_meters);
+ }
+
+ if (pos_clamped_region.mV[VY] < 0.f)
+ {
+ pos_clamped_region.mV[VY] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VY] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VY] = (F32) fmod(pos_clamped_region.mV[VY], region_width_meters);
+ }
+
+
+ S32 i = llfloor(pos_clamped_region.mV[VX] * mSize / region_width_meters);
+ S32 j = llfloor(pos_clamped_region.mV[VY] * mSize / region_width_meters);
+ k = i + j*mSize;
+ dx = ((pos_clamped_region.mV[VX] * mSize / region_width_meters) - (F32) i);
+ dy = ((pos_clamped_region.mV[VY] * mSize / region_width_meters) - (F32) j);
+
+ if ((i < mSize-1) && (j < mSize-1))
+ {
+ // Interior points, no edges
+ r_val.mV[VX] = mVelX[k]*(1.0f - dx)*(1.0f - dy) +
+ mVelX[k + 1]*dx*(1.0f - dy) +
+ mVelX[k + mSize]*dy*(1.0f - dx) +
+ mVelX[k + mSize + 1]*dx*dy;
+ r_val.mV[VY] = mVelY[k]*(1.0f - dx)*(1.0f - dy) +
+ mVelY[k + 1]*dx*(1.0f - dy) +
+ mVelY[k + mSize]*dy*(1.0f - dx) +
+ mVelY[k + mSize + 1]*dx*dy;
+ }
+ else
+ {
+ r_val.mV[VX] = mVelX[k];
+ r_val.mV[VY] = mVelY[k];
+ }
+
+ r_val.mV[VZ] = 0.f;
+ return r_val * WIND_SCALE_HACK;
+}
+
+
+LLVector3 LLWind::getCloudVelocity(const LLVector3 &pos_region)
+{
+ llassert(mSize == 16);
+ // Resolves value of wind at a location relative to SW corner of region
+ //
+ // Returns wind magnitude in X,Y components of vector3
+ LLVector3 r_val;
+ F32 dx,dy;
+ S32 k;
+
+ LLVector3 pos_clamped_region(pos_region);
+
+ F32 region_width_meters = gWorldPointer->getRegionWidthInMeters();
+
+ if (pos_clamped_region.mV[VX] < 0.f)
+ {
+ pos_clamped_region.mV[VX] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VX] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VX] = (F32) fmod(pos_clamped_region.mV[VX], region_width_meters);
+ }
+
+ if (pos_clamped_region.mV[VY] < 0.f)
+ {
+ pos_clamped_region.mV[VY] = 0.f;
+ }
+ else if (pos_clamped_region.mV[VY] >= region_width_meters)
+ {
+ pos_clamped_region.mV[VY] = (F32) fmod(pos_clamped_region.mV[VY], region_width_meters);
+ }
+
+
+ S32 i = llfloor(pos_clamped_region.mV[VX] * mSize / region_width_meters);
+ S32 j = llfloor(pos_clamped_region.mV[VY] * mSize / region_width_meters);
+ k = i + j*mSize;
+ dx = ((pos_clamped_region.mV[VX] * mSize / region_width_meters) - (F32) i);
+ dy = ((pos_clamped_region.mV[VY] * mSize / region_width_meters) - (F32) j);
+
+ if ((i < mSize-1) && (j < mSize-1))
+ {
+ // Interior points, no edges
+ r_val.mV[VX] = mCloudVelX[k]*(1.0f - dx)*(1.0f - dy) +
+ mCloudVelX[k + 1]*dx*(1.0f - dy) +
+ mCloudVelX[k + mSize]*dy*(1.0f - dx) +
+ mCloudVelX[k + mSize + 1]*dx*dy;
+ r_val.mV[VY] = mCloudVelY[k]*(1.0f - dx)*(1.0f - dy) +
+ mCloudVelY[k + 1]*dx*(1.0f - dy) +
+ mCloudVelY[k + mSize]*dy*(1.0f - dx) +
+ mCloudVelY[k + mSize + 1]*dx*dy;
+ }
+ else
+ {
+ r_val.mV[VX] = mCloudVelX[k];
+ r_val.mV[VY] = mCloudVelY[k];
+ }
+
+ r_val.mV[VZ] = 0.f;
+ return r_val * WIND_SCALE_HACK;
+}
+
+
+void LLWind::setCloudDensityPointer(F32 *densityp)
+{
+ mCloudDensityp = densityp;
+}
+
+void LLWind::setOriginGlobal(const LLVector3d &origin_global)
+{
+ mOriginGlobal = origin_global;
+}
+
+
diff --git a/indra/newview/llwind.h b/indra/newview/llwind.h
new file mode 100644
index 0000000000..0a9e343945
--- /dev/null
+++ b/indra/newview/llwind.h
@@ -0,0 +1,50 @@
+/**
+ * @file llwind.h
+ * @brief LLWind class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWIND_H
+#define LL_LLWIND_H
+
+//#include "vmath.h"
+#include "llmath.h"
+#include "v3math.h"
+#include "v3dmath.h"
+
+class LLVector3;
+class LLBitPack;
+class LLGroupHeader;
+
+
+class LLWind
+{
+public:
+ LLWind();
+ ~LLWind();
+ void renderVectors();
+ LLVector3 getVelocity(const LLVector3 &location); // "location" is region-local
+ LLVector3 getCloudVelocity(const LLVector3 &location); // "location" is region-local
+ LLVector3 getVelocityNoisy(const LLVector3 &location, const F32 dim); // "location" is region-local
+
+ void decompress(LLBitPack &bitpack, LLGroupHeader *group_headerp);
+ LLVector3 getAverage();
+ void setCloudDensityPointer(F32 *densityp);
+
+ void setOriginGlobal(const LLVector3d &origin_global);
+private:
+ S32 mSize;
+ F32 * mVelX;
+ F32 * mVelY;
+ F32 * mCloudVelX;
+ F32 * mCloudVelY;
+ F32 * mCloudDensityp;
+
+ LLVector3d mOriginGlobal;
+ void init();
+
+};
+
+#endif
diff --git a/indra/newview/llwindebug.cpp b/indra/newview/llwindebug.cpp
new file mode 100644
index 0000000000..428df0b45a
--- /dev/null
+++ b/indra/newview/llwindebug.cpp
@@ -0,0 +1,254 @@
+/**
+ * @file llwindebug.cpp
+ * @brief Windows debugging functions
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#ifdef LL_WINDOWS
+
+#include "llwindebug.h"
+#include "llviewercontrol.h"
+#include "lldir.h"
+
+// From viewer.h
+extern BOOL gInProductionGrid;
+
+extern void (*gCrashCallback)(void);
+extern void write_debug(const char *str);
+extern void write_debug(const std::string &str);
+
+// based on dbghelp.h
+typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
+ CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
+ CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
+ CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
+ );
+
+MINIDUMPWRITEDUMP f_mdwp = NULL;
+
+
+
+class LLMemoryReserve {
+public:
+ LLMemoryReserve();
+ ~LLMemoryReserve();
+ void reserve();
+ void release();
+protected:
+ unsigned char *mReserve;
+ static const size_t MEMORY_RESERVATION_SIZE;
+};
+
+LLMemoryReserve::LLMemoryReserve() :
+ mReserve(NULL)
+{
+};
+
+LLMemoryReserve::~LLMemoryReserve()
+{
+ release();
+}
+
+// I dunno - this just seemed like a pretty good value.
+const size_t LLMemoryReserve::MEMORY_RESERVATION_SIZE = 5 * 1024 * 1024;
+
+void LLMemoryReserve::reserve()
+{
+ if(NULL == mReserve)
+ mReserve = new unsigned char[MEMORY_RESERVATION_SIZE];
+};
+
+void LLMemoryReserve::release()
+{
+ delete [] mReserve;
+ mReserve = NULL;
+};
+
+static LLMemoryReserve gEmergencyMemoryReserve;
+
+// static
+BOOL LLWinDebug::setupExceptionHandler()
+{
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+
+ static BOOL s_first_run = TRUE;
+ // Load the dbghelp dll now, instead of waiting for the crash.
+ // Less potential for stack mangling
+
+ BOOL ok = TRUE;
+ if (s_first_run)
+ {
+ // First, try loading from the directory that the app resides in.
+ std::string local_dll_name = gDirUtilp->findFile("dbghelp.dll", gDirUtilp->getWorkingDir(), gDirUtilp->getExecutableDir());
+
+ HMODULE hDll = NULL;
+ hDll = LoadLibraryA(local_dll_name.c_str());
+ if (!hDll)
+ {
+ hDll = LoadLibrary(L"dbghelp.dll");
+ }
+
+ if (!hDll)
+ {
+ llwarns << "Couldn't find dbghelp.dll!" << llendl;
+
+ std::string msg = "Couldn't find dbghelp.dll at ";
+ msg += local_dll_name;
+ msg += "!\n";
+
+ write_debug(msg.c_str());
+
+ ok = FALSE;
+ }
+ else
+ {
+ f_mdwp = (MINIDUMPWRITEDUMP) GetProcAddress(hDll, "MiniDumpWriteDump");
+
+ if (!f_mdwp)
+ {
+ write_debug("No MiniDumpWriteDump!\n");
+ ok = FALSE;
+ }
+ }
+
+ gEmergencyMemoryReserve.reserve();
+ }
+
+ LPTOP_LEVEL_EXCEPTION_FILTER prev_filter;
+ prev_filter = SetUnhandledExceptionFilter(LLWinDebug::handleException);
+
+ if (s_first_run)
+ {
+ // We're fine, this is the first run.
+ s_first_run = FALSE;
+ return ok;
+ }
+ if (!prev_filter)
+ {
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with NULL!" << llendl;
+ ok = FALSE;
+ }
+ if (prev_filter != LLWinDebug::handleException)
+ {
+ llwarns << "Our exception handler (" << (void *)LLWinDebug::handleException << ") replaced with " << prev_filter << "!" << llendl;
+ ok = FALSE;
+ }
+ return ok;
+#else
+ // Internal builds don't mess with exception handling.
+ return TRUE;
+#endif
+}
+
+void LLWinDebug::writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename)
+{
+ if(f_mdwp == NULL)
+ {
+ write_debug("No way to generate a minidump, no MiniDumpWriteDump function!\n");
+ }
+ else if(gDirUtilp == NULL)
+ {
+ write_debug("No way to generate a minidump, no gDirUtilp!\n");
+ }
+ else
+ {
+ std::string dump_path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,
+ filename);
+
+ HANDLE hFile = CreateFileA(dump_path.c_str(),
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ // Write the dump, ignoring the return value
+ f_mdwp(GetCurrentProcess(),
+ GetCurrentProcessId(),
+ hFile,
+ type,
+ ExInfop,
+ NULL,
+ NULL);
+
+ CloseHandle(hFile);
+ }
+
+ }
+}
+
+// static
+LONG LLWinDebug::handleException(struct _EXCEPTION_POINTERS *exception_infop)
+{
+
+ //
+ // Let go of a bunch of reserved memory to give library calls etc
+ // a chance to execute normally in the case that we ran out of
+ // memory.
+ //
+ gEmergencyMemoryReserve.release();
+
+ BOOL userWantsMaxiDump =
+ (stricmp(gSavedSettings.getString("LastName").c_str(), "linden") == 0)
+ || (stricmp(gSavedSettings.getString("LastName").c_str(), "tester") == 0);
+
+ BOOL alsoSaveMaxiDump = userWantsMaxiDump && !gInProductionGrid;
+
+ /* Calculate alsoSaveMaxiDump here */
+
+ if (exception_infop)
+ {
+ _MINIDUMP_EXCEPTION_INFORMATION ExInfo;
+
+ ExInfo.ThreadId = ::GetCurrentThreadId();
+ ExInfo.ExceptionPointers = exception_infop;
+ ExInfo.ClientPointers = NULL;
+
+ writeDumpToFile(MiniDumpNormal, &ExInfo, "SecondLife.dmp");
+
+ if(alsoSaveMaxiDump)
+ writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), &ExInfo, "SecondLifePlus.dmp");
+ }
+ else
+ {
+ writeDumpToFile(MiniDumpNormal, NULL, "SecondLife.dmp");
+
+ if(alsoSaveMaxiDump)
+ writeDumpToFile((MINIDUMP_TYPE)(MiniDumpWithDataSegs | MiniDumpWithIndirectlyReferencedMemory), NULL, "SecondLifePlus.dmp");
+ }
+
+ if (!exception_infop)
+ {
+ // We're calling this due to a network error, not due to an actual exception.
+ // It doesn't realy matter what we return.
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
+ //
+ // Call the newview crash callback, which will spawn the crash
+ // reporter. It may or may not spawn a dialog.
+ //
+ if (gCrashCallback)
+ {
+ gCrashCallback();
+ }
+
+ //
+ // At this point, we always want to exit the app. There's no graceful
+ // recovery for an unhandled exception.
+ //
+ // Just kill the process.
+ LONG retval = EXCEPTION_EXECUTE_HANDLER;
+
+ return retval;
+}
+
+#endif
+
diff --git a/indra/newview/llwindebug.h b/indra/newview/llwindebug.h
new file mode 100644
index 0000000000..e328ffe58d
--- /dev/null
+++ b/indra/newview/llwindebug.h
@@ -0,0 +1,24 @@
+/**
+ * @file llwindebug.h
+ * @brief LLWinDebug class header file
+ *
+ * Copyright (c) 2004-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWINDEBUG_H
+#define LL_LLWINDEBUG_H
+
+#include "stdtypes.h"
+#include <dbghelp.h>
+
+class LLWinDebug
+{
+public:
+ static BOOL setupExceptionHandler();
+
+ static LONG WINAPI handleException(struct _EXCEPTION_POINTERS *pExceptionInfo);
+ static void writeDumpToFile(MINIDUMP_TYPE type, MINIDUMP_EXCEPTION_INFORMATION *ExInfop, const char *filename);
+};
+
+#endif // LL_LLWINDEBUG_H
diff --git a/indra/newview/llworld.cpp b/indra/newview/llworld.cpp
new file mode 100644
index 0000000000..813eb0c7ea
--- /dev/null
+++ b/indra/newview/llworld.cpp
@@ -0,0 +1,1141 @@
+/**
+ * @file llworld.cpp
+ * @brief Initial test structure to organize viewer regions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworld.h"
+
+#include "indra_constants.h"
+
+#include "llagent.h"
+#include "llviewercontrol.h"
+#include "lldrawpool.h"
+#include "llglheaders.h"
+#include "llregionhandle.h"
+#include "llsurface.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewernetwork.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvlcomposition.h"
+#include "llvoavatar.h"
+#include "llvowater.h"
+#include "message.h"
+#include "pipeline.h"
+#include "viewer.h" // for do_disconnect()
+
+//
+// Globals
+//
+LLWorld* gWorldp = NULL;
+U32 gAgentPauseSerialNum = 0;
+
+//
+// Constants
+//
+const S32 MAX_NUMBER_OF_CLOUDS = 750;
+const F32 MIN_IDLE_UPDATE_TIME = 0.025f;
+const S32 WORLD_PATCH_SIZE = 16;
+
+extern LLColor4U MAX_WATER_COLOR;
+
+//
+// Functions
+//
+
+// allocate the stack
+LLWorld::LLWorld(const U32 grids_per_region, const F32 meters_per_grid)
+: mWidth(grids_per_region),
+ mScale(meters_per_grid),
+ mWidthInMeters( grids_per_region * meters_per_grid )
+{
+ mSpaceTimeUSec = 0;
+ mLastPacketsIn = 0;
+ mLastPacketsOut = 0;
+ mLastPacketsLost = 0;
+ mLandFarClip = DEFAULT_FAR_PLANE;
+ mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
+
+ if (gNoRender)
+ {
+ return;
+ }
+
+ for (S32 i = 0; i < 8; i++)
+ {
+ mEdgeWaterObjects[i] = NULL;
+ }
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw(1,1,4);
+ U8 *default_texture = raw->getData();
+ *(default_texture++) = MAX_WATER_COLOR.mV[0];
+ *(default_texture++) = MAX_WATER_COLOR.mV[1];
+ *(default_texture++) = MAX_WATER_COLOR.mV[2];
+ *(default_texture++) = MAX_WATER_COLOR.mV[3];
+
+ mDefaultWaterTexturep = new LLViewerImage(raw, FALSE);
+ mDefaultWaterTexturep->bind();
+ mDefaultWaterTexturep->setClamp(TRUE, TRUE);
+
+}
+
+
+LLWorld::~LLWorld()
+{
+ gObjectList.killAllObjects();
+
+ mRegionList.deleteAllData();
+}
+
+
+LLViewerRegion* LLWorld::addRegion(const U64 &region_handle, const LLHost &host)
+{
+ LLMemType mt(LLMemType::MTYPE_REGIONS);
+
+ LLViewerRegion *regionp = getRegionFromHandle(region_handle);
+ if (regionp)
+ {
+ LLHost old_host = regionp->getHost();
+ // region already exists!
+ if (host == old_host && regionp->mAlive)
+ {
+ // This is a duplicate for the same host and it's alive, don't bother.
+ return regionp;
+ }
+
+ if (host != old_host)
+ {
+ llwarns << "LLWorld::addRegion exists, but old host " << old_host
+ << " does not match new host " << host << llendl;
+ }
+ if (!regionp->mAlive)
+ {
+ llwarns << "LLWorld::addRegion exists, but isn't alive" << llendl;
+ }
+
+ // Kill the old host, and then we can continue on and add the new host. We have to kill even if the host
+ // matches, because all the agent state for the new camera is completely different.
+ removeRegion(old_host);
+ }
+
+ U32 iindex = 0;
+ U32 jindex = 0;
+ from_region_handle(region_handle, &iindex, &jindex);
+ S32 x = (S32)(iindex/mWidth);
+ S32 y = (S32)(jindex/mWidth);
+ llinfos << "Adding new region (" << x << ":" << y << ")" << llendl;
+ llinfos << "Host: " << host << llendl;
+
+ LLVector3d origin_global;
+
+ origin_global = from_region_handle(region_handle);
+
+ regionp = new LLViewerRegion(region_handle,
+ host,
+ mWidth,
+ WORLD_PATCH_SIZE,
+ getRegionWidthInMeters() );
+ if (!regionp)
+ {
+ llerrs << "Unable to create new region!" << llendl;
+ }
+
+ regionp->mCloudLayer.create(regionp);
+ regionp->mCloudLayer.setWidth((F32)mWidth);
+ regionp->mCloudLayer.setWindPointer(&regionp->mWind);
+
+ mRegionList.addData(regionp);
+ mActiveRegionList.addData(regionp);
+ mCulledRegionList.addData(regionp);
+
+
+ // Find all the adjacent regions, and attach them.
+ // Generate handles for all of the adjacent regions, and attach them in the correct way.
+ // connect the edges
+ F32 adj_x = 0.f;
+ F32 adj_y = 0.f;
+ F32 region_x = 0.f;
+ F32 region_y = 0.f;
+ U64 adj_handle = 0;
+
+ F32 width = getRegionWidthInMeters();
+
+ LLViewerRegion *neighborp;
+ from_region_handle(region_handle, &region_x, &region_y);
+
+ // Iterate through all directions, and connect neighbors if there.
+ S32 dir;
+ for (dir = 0; dir < 8; dir++)
+ {
+ adj_x = region_x + width * gDirAxes[dir][0];
+ adj_y = region_y + width * gDirAxes[dir][1];
+ to_region_handle(adj_x, adj_y, &adj_handle);
+
+ neighborp = getRegionFromHandle(adj_handle);
+ if (neighborp)
+ {
+ //llinfos << "Connecting " << region_x << ":" << region_y << " -> " << adj_x << ":" << adj_y << llendl;
+ regionp->connectNeighbor(neighborp, dir);
+ }
+ }
+
+ updateWaterObjects();
+
+ return regionp;
+}
+
+
+void LLWorld::removeRegion(const LLHost &host)
+{
+ F32 x, y;
+
+ LLViewerRegion *regionp = getRegion(host);
+ if (!regionp)
+ {
+ llwarns << "Trying to remove region that doesn't exist!" << llendl;
+ return;
+ }
+
+ if (regionp == gAgent.getRegion())
+ {
+ LLViewerRegion *reg;
+ for (reg = mRegionList.getFirstData(); reg; reg = mRegionList.getNextData())
+ {
+ llwarns << "RegionDump: " << reg->getName()
+ << " " << reg->getHost()
+ << " " << reg->getOriginGlobal()
+ << llendl;
+ }
+
+ llwarns << "Agent position global " << gAgent.getPositionGlobal()
+ << " agent " << gAgent.getPositionAgent()
+ << llendl;
+
+ llwarns << "Regions visited " << gAgent.getRegionsVisited() << llendl;
+
+ llwarns << "gFrameTimeSeconds " << gFrameTimeSeconds << llendl;
+
+ llwarns << "Disabling region " << regionp->getName() << " that agent is in!" << llendl;
+ do_disconnect("You have been disconnected from the region you were in.");
+ return;
+ }
+
+ from_region_handle(regionp->getHandle(), &x, &y);
+ llinfos << "Removing region " << x << ":" << y << llendl;
+
+ // This code can probably be blitzed now...
+ if (!mRegionList.removeData(regionp))
+ {
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ llwarns << "RegionDump: " << regionp->getName()
+ << " " << regionp->getHost()
+ << " " << regionp->getOriginGlobal()
+ << llendl;
+ }
+
+ llerrs << "Region list is broken" << llendl;
+ }
+
+ if (!mActiveRegionList.removeData(regionp))
+ {
+ llwarns << "LLWorld.mActiveRegionList is broken." << llendl;
+ }
+ if (!mCulledRegionList.removeData(regionp))
+ {
+ if (!mVisibleRegionList.removeData(regionp))
+ {
+ llwarns << "LLWorld.mCulled/mVisibleRegionList are broken" << llendl;;
+ }
+ }
+ delete regionp;
+
+ updateWaterObjects();
+}
+
+
+LLViewerRegion *LLWorld::getRegion(const LLHost &host)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->getHost() == host)
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+LLViewerRegion *LLWorld::getRegionFromPosAgent(const LLVector3 &pos)
+{
+ return getRegionFromPosGlobal(gAgent.getPosGlobalFromAgent(pos));
+}
+
+LLViewerRegion *LLWorld::getRegionFromPosGlobal(const LLVector3d &pos)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->pointInRegionGlobal(pos))
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+
+LLVector3d LLWorld::clipToVisibleRegions(const LLVector3d &start_pos, const LLVector3d &end_pos)
+{
+ if (positionRegionValidGlobal(end_pos))
+ {
+ return end_pos;
+ }
+
+ LLViewerRegion* regionp = getRegionFromPosGlobal(start_pos);
+ if (!regionp)
+ {
+ return start_pos;
+ }
+
+ LLVector3d delta_pos = end_pos - start_pos;
+ LLVector3d delta_pos_abs;
+ delta_pos_abs.setVec(delta_pos);
+ delta_pos_abs.abs();
+
+ LLVector3 region_coord = regionp->getPosRegionFromGlobal(end_pos);
+ F64 clip_factor = 1.0;
+ F32 region_width = regionp->getWidth();
+ if (region_coord.mV[VX] < 0.f)
+ {
+ if (region_coord.mV[VY] < region_coord.mV[VX])
+ {
+ // clip along y -
+ clip_factor = -(region_coord.mV[VY] / delta_pos_abs.mdV[VY]);
+ }
+ else
+ {
+ // clip along x -
+ clip_factor = -(region_coord.mV[VX] / delta_pos_abs.mdV[VX]);
+ }
+ }
+ else if (region_coord.mV[VX] > region_width)
+ {
+ if (region_coord.mV[VY] > region_coord.mV[VX])
+ {
+ // clip along y +
+ clip_factor = (region_coord.mV[VY] - region_width) / delta_pos_abs.mdV[VY];
+ }
+ else
+ {
+ //clip along x +
+ clip_factor = (region_coord.mV[VX] - region_width) / delta_pos_abs.mdV[VX];
+ }
+ }
+ else if (region_coord.mV[VY] < 0.f)
+ {
+ // clip along y -
+ clip_factor = -(region_coord.mV[VY] / delta_pos_abs.mdV[VY]);
+ }
+ else if (region_coord.mV[VY] > region_width)
+ {
+ // clip along y +
+ clip_factor = (region_coord.mV[VY] - region_width) / delta_pos_abs.mdV[VY];
+ }
+
+ // clamp to < 256 to stay in sim
+ LLVector3d final_region_pos = LLVector3d(region_coord) - (delta_pos * clip_factor);
+ final_region_pos.clamp(0.0, 255.999);
+ return regionp->getPosGlobalFromRegion(LLVector3(final_region_pos));
+}
+
+LLViewerRegion *LLWorld::getRegionFromHandle(const U64 &handle)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->getHandle() == handle)
+ {
+ return regionp;
+ }
+ }
+ return NULL;
+}
+
+
+void LLWorld::updateAgentOffset(const LLVector3d &offset_global)
+{
+#if 0
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ regionp->setAgentOffset(offset_global);
+ }
+#endif
+}
+
+
+BOOL LLWorld::positionRegionValidGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp;
+ for (regionp = mRegionList.getFirstData(); regionp; regionp = mRegionList.getNextData())
+ {
+ if (regionp->pointInRegionGlobal(pos_global))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+// Allow objects to go up to their radius underground.
+F32 LLWorld::getMinAllowedZ(LLViewerObject* object)
+{
+ F32 land_height = resolveLandHeightGlobal(object->getPositionGlobal());
+ F32 radius = 0.5f * object->getScale().magVec();
+ return land_height - radius;
+}
+
+
+LLViewerRegion* LLWorld::resolveRegionGlobal(LLVector3 &pos_region, const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+
+ if (regionp)
+ {
+ pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ return regionp;
+ }
+
+ return NULL;
+}
+
+
+LLViewerRegion* LLWorld::resolveRegionAgent(LLVector3 &pos_region, const LLVector3 &pos_agent)
+{
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+
+ if (regionp)
+ {
+ pos_region = regionp->getPosRegionFromGlobal(pos_global);
+ return regionp;
+ }
+
+ return NULL;
+}
+
+
+F32 LLWorld::resolveLandHeightAgent(const LLVector3 &pos_agent)
+{
+ LLVector3d pos_global = gAgent.getPosGlobalFromAgent(pos_agent);
+ return resolveLandHeightGlobal(pos_global);
+}
+
+
+F32 LLWorld::resolveLandHeightGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (regionp)
+ {
+ return regionp->getLand().resolveHeightGlobal(pos_global);
+ }
+ return 0.0f;
+}
+
+
+// Takes a line defined by "point_a" and "point_b" and determines the closest (to point_a)
+// point where the the line intersects an object or the land surface. Stores the results
+// in "intersection" and "intersection_normal" and returns a scalar value that represents
+// the normalized distance along the line from "point_a" to "intersection".
+//
+// Currently assumes point_a and point_b only differ in z-direction,
+// but it may eventually become more general.
+F32 LLWorld::resolveStepHeightGlobal(const LLVOAvatar* avatarp, const LLVector3d &point_a, const LLVector3d &point_b,
+ LLVector3d &intersection, LLVector3 &intersection_normal,
+ LLViewerObject **viewerObjectPtr)
+{
+ // initialize return value to null
+ if (viewerObjectPtr)
+ {
+ *viewerObjectPtr = NULL;
+ }
+
+ LLViewerRegion *regionp = getRegionFromPosGlobal(point_a);
+ if (!regionp)
+ {
+ // We're outside the world
+ intersection = 0.5f * (point_a + point_b);
+ intersection_normal.setVec(0.0f, 0.0f, 1.0f);
+ return 0.5f;
+ }
+
+ // calculate the length of the segment
+ F32 segment_length = (F32)((point_a - point_b).magVec());
+ if (0.0f == segment_length)
+ {
+ intersection = point_a;
+ intersection_normal.setVec(0.0f, 0.0f, 1.0f);
+ return segment_length;
+ }
+
+ // get land height
+ // Note: we assume that the line is parallel to z-axis here
+ LLVector3d land_intersection = point_a;
+ F32 normalized_land_distance;
+
+ land_intersection.mdV[VZ] = regionp->getLand().resolveHeightGlobal(point_a);
+ normalized_land_distance = (F32)(point_a.mdV[VZ] - land_intersection.mdV[VZ]) / segment_length;
+
+ if (avatarp && !avatarp->mFootPlane.isExactlyClear())
+ {
+ LLVector3 foot_plane_normal(avatarp->mFootPlane.mV);
+ LLVector3 start_pt = avatarp->getRegion()->getPosRegionFromGlobal(point_a);
+ // added 0.05 meters to compensate for error in foot plane reported by Havok
+ F32 norm_dist_from_plane = ((start_pt * foot_plane_normal) - avatarp->mFootPlane.mV[VW]) + 0.05f;
+ norm_dist_from_plane = llclamp(norm_dist_from_plane / segment_length, 0.f, 1.f);
+ if (norm_dist_from_plane < normalized_land_distance)
+ {
+ normalized_land_distance = norm_dist_from_plane;
+ intersection = point_a;
+ intersection.mdV[VZ] -= norm_dist_from_plane * segment_length;
+ intersection_normal = foot_plane_normal;
+ }
+ }
+ else
+ {
+ intersection = land_intersection;
+ intersection_normal = resolveLandNormalGlobal(land_intersection);
+ }
+
+ return normalized_land_distance;
+}
+
+
+LLSurfacePatch * LLWorld::resolveLandPatchGlobal(const LLVector3d &pos_global)
+{
+ // returns a pointer to the patch at this location
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (!regionp)
+ {
+ return NULL;
+ }
+
+ return regionp->getLand().resolvePatchGlobal(pos_global);
+}
+
+
+LLVector3 LLWorld::resolveLandNormalGlobal(const LLVector3d &pos_global)
+{
+ LLViewerRegion *regionp = getRegionFromPosGlobal(pos_global);
+ if (!regionp)
+ {
+ return LLVector3::z_axis;
+ }
+
+ return regionp->getLand().resolveNormalGlobal(pos_global);
+}
+
+
+void LLWorld::updateVisibilities()
+{
+ F32 cur_far_clip = gCamera->getFar();
+
+ gCamera->setFar(mLandFarClip);
+
+ LLViewerRegion *regionp;
+
+ F32 diagonal_squared = F_SQRT2 * F_SQRT2 * mWidth * mWidth;
+ // Go through the culled list and check for visible regions
+ for (regionp = mCulledRegionList.getFirstData();
+ regionp;
+ regionp = mCulledRegionList.getNextData())
+ {
+ F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
+ F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+ if (!regionp->getLand().hasZData()
+ || gCamera->sphereInFrustum(regionp->getCenterAgent(), radius))
+ {
+ mCulledRegionList.removeCurrentData();
+ mVisibleRegionList.addDataAtEnd(regionp);
+ }
+ }
+
+ F32 last_dist_squared = 0.0f;
+ F32 dist_squared;
+
+ // Update all of the visible regions and make single bubble-sort pass
+ for (regionp = mVisibleRegionList.getFirstData();
+ regionp;
+ regionp = mVisibleRegionList.getNextData())
+ {
+ if (!regionp->getLand().hasZData())
+ {
+ continue;
+ }
+
+ F32 height = regionp->getLand().getMaxZ() - regionp->getLand().getMinZ();
+ F32 radius = 0.5f*fsqrtf(height * height + diagonal_squared);
+ if (gCamera->sphereInFrustum(regionp->getCenterAgent(), radius))
+ {
+ if (!gNoRender)
+ {
+ regionp->getLand().updatePatchVisibilities(gAgent);
+ }
+
+ // sort by distance... closer regions to the front
+ // Note: regions use absolute frame so we use the agent's center
+ dist_squared = (F32)(gAgent.getCameraPositionGlobal() - regionp->getCenterGlobal()).magVecSquared();
+ if (dist_squared < last_dist_squared)
+ {
+ mVisibleRegionList.swapCurrentWithPrevious();
+ }
+ else
+ {
+ last_dist_squared = dist_squared;
+ }
+ }
+ else
+ {
+ mVisibleRegionList.removeCurrentData();
+ mCulledRegionList.addData(regionp);
+ }
+ }
+
+ gCamera->setFar(cur_far_clip);
+}
+
+
+void LLWorld::updateRegions()
+{
+ LLViewerRegion *regionp;
+ LLTimer update_timer;
+
+ // Perform idle time updates for the regions (and associated surfaces)
+ for (regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ update_timer.reset();
+ if (!regionp->idleUpdate(update_timer, mIdleUpdateTime))
+ {
+ // Didn't finish all the updates. Slightly increase the idle update time.
+ mIdleUpdateTime *= 1.05f;
+ }
+ else
+ {
+ mIdleUpdateTime *= 0.9f;
+ if (mIdleUpdateTime < MIN_IDLE_UPDATE_TIME)
+ {
+ mIdleUpdateTime = MIN_IDLE_UPDATE_TIME;
+ }
+ }
+ }
+}
+
+void LLWorld::updateParticles()
+{
+ mPartSim.updateSimulation();
+}
+
+void LLWorld::updateClouds(const F32 dt)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ // don't move clouds in snapshot mode
+ return;
+ }
+ LLViewerRegion *regionp;
+ if (mActiveRegionList.getLength())
+ {
+ // Update all the cloud puff positions, and timer based stuff
+ // such as death decay
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffs(dt);
+ }
+
+ // Reshuffle who owns which puffs
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffOwnership();
+ }
+
+ // Add new puffs
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ regionp->mCloudLayer.updatePuffCount();
+ }
+ }
+}
+
+LLCloudGroup *LLWorld::findCloudGroup(const LLCloudPuff &puff)
+{
+ LLViewerRegion *regionp;
+ if (mActiveRegionList.getLength())
+ {
+ // Update all the cloud puff positions, and timer based stuff
+ // such as death decay
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ LLCloudGroup *groupp = regionp->mCloudLayer.findCloudGroup(puff);
+ if (groupp)
+ {
+ return groupp;
+ }
+ }
+ }
+ return NULL;
+}
+
+
+void LLWorld::renderPropertyLines()
+{
+ S32 region_count = 0;
+ S32 vertex_count = 0;
+
+ LLViewerRegion* region;
+ for (region = mVisibleRegionList.getFirstData(); region; region = mVisibleRegionList.getNextData() )
+ {
+ region_count++;
+ vertex_count += region->renderPropertyLines();
+ }
+}
+
+
+void LLWorld::updateNetStats()
+{
+ F32 bits = 0.f;
+ U32 packets = 0;
+ LLViewerRegion *regionp;
+
+ for (regionp = mActiveRegionList.getFirstData(); regionp; regionp = mActiveRegionList.getNextData())
+ {
+ regionp->updateNetStats();
+ bits += regionp->mBitStat.getCurrent();
+ packets += llfloor( regionp->mPacketsStat.getCurrent() );
+ }
+
+ S32 packets_in = gMessageSystem->mPacketsIn - mLastPacketsIn;
+ S32 packets_out = gMessageSystem->mPacketsOut - mLastPacketsOut;
+ S32 packets_lost = gMessageSystem->mDroppedPackets - mLastPacketsLost;
+
+ S32 actual_in_bits = gMessageSystem->mPacketRing.getAndResetActualInBits();
+ S32 actual_out_bits = gMessageSystem->mPacketRing.getAndResetActualOutBits();
+ gViewerStats->mActualInKBitStat.addValue(actual_in_bits/1024.f);
+ gViewerStats->mActualOutKBitStat.addValue(actual_out_bits/1024.f);
+ gViewerStats->mKBitStat.addValue(bits/1024.f);
+ gViewerStats->mPacketsInStat.addValue(packets_in);
+ gViewerStats->mPacketsOutStat.addValue(packets_out);
+ gViewerStats->mPacketsLostStat.addValue(gMessageSystem->mDroppedPackets);
+ if (packets_in)
+ {
+ gViewerStats->mPacketsLostPercentStat.addValue(100.f*((F32)packets_lost/(F32)packets_in));
+ }
+ else
+ {
+ gViewerStats->mPacketsLostPercentStat.addValue(0.f);
+ }
+
+ mLastPacketsIn = gMessageSystem->mPacketsIn;
+ mLastPacketsOut = gMessageSystem->mPacketsOut;
+ mLastPacketsLost = gMessageSystem->mDroppedPackets;
+}
+
+
+void LLWorld::printPacketsLost()
+{
+ LLViewerRegion *regionp;
+
+ llinfos << "Simulators:" << llendl;
+ llinfos << "----------" << llendl;
+
+ LLCircuitData *cdp = NULL;
+ for (regionp = mActiveRegionList.getFirstData();
+ regionp;
+ regionp = mActiveRegionList.getNextData())
+ {
+ cdp = gMessageSystem->mCircuitInfo.findCircuit(regionp->getHost());
+ if (cdp)
+ {
+ LLVector3d range = regionp->getCenterGlobal() - gAgent.getPositionGlobal();
+
+ llinfos << regionp->getHost() << ", range: " << range.magVec() <<
+ " packets lost: " <<
+ cdp->getPacketsLost() << llendl;
+ }
+ }
+
+ llinfos << "UserServer:" << llendl;
+ llinfos << "-----------" << llendl;
+
+ cdp = gMessageSystem->mCircuitInfo.findCircuit(gUserServer);
+ if (cdp)
+ {
+ llinfos << gUserServer << " packets lost: " << cdp->getPacketsLost() << llendl;
+ }
+}
+
+void LLWorld::processCoarseUpdate(LLMessageSystem* msg, void** user_data)
+{
+ LLViewerRegion* region = NULL;
+ region = gWorldp->getRegion(msg->getSender());
+ if( region ) region->updateCoarseLocations(msg);
+}
+
+F32 LLWorld::getLandFarClip() const
+{
+ return mLandFarClip;
+}
+
+void LLWorld::setLandFarClip(const F32 far_clip)
+{
+ mLandFarClip = far_clip;
+}
+
+
+void LLWorld::updateWaterObjects()
+{
+ //llinfos << "Start water update" << llendl;
+ if (!gAgent.getRegion())
+ {
+ return;
+ }
+ S32 min_x, min_y, max_x, max_y;
+ U32 region_x, region_y;
+
+ S32 rwidth = llfloor(getRegionWidthInMeters());
+
+ // First, determine the min and max "box" of water objects
+ LLViewerRegion *regionp;
+ regionp = mRegionList.getFirstData();
+
+ if (!regionp)
+ {
+ llwarns << "No regions!" << llendl;
+ return;
+ }
+
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+ min_x = max_x = region_x;
+ min_y = max_y = region_y;
+
+ LLVOWater *waterp;
+
+ for (; regionp; regionp = mRegionList.getNextData())
+ {
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+ min_x = llmin(min_x, (S32)region_x);
+ min_y = llmin(min_y, (S32)region_y);
+ max_x = llmax(max_x, (S32)region_x);
+ max_y = llmax(max_y, (S32)region_y);
+ waterp = regionp->getLand().getWaterObj();
+ if (waterp)
+ {
+ gObjectList.updateActive(waterp);
+ }
+ }
+
+ for (waterp = mHoleWaterObjects.getFirstData(); waterp; waterp = mHoleWaterObjects.getNextData())
+ {
+ gObjectList.killObject(waterp);
+ }
+ mHoleWaterObjects.removeAllNodes();
+
+ // We only want to fill in holes for stuff that's near us, say, within 512m
+ regionp = gAgent.getRegion();
+ from_region_handle(regionp->getHandle(), &region_x, &region_y);
+
+ min_x = llmax((S32)region_x - 512, min_x);
+ min_y = llmax((S32)region_y - 512, min_y);
+ max_x = llmin((S32)region_x + 512, max_x);
+ max_y = llmin((S32)region_y + 512, max_y);
+
+ // Now, get a list of the holes
+ S32 x, y;
+ for (x = min_x; x <= max_x; x += rwidth)
+ {
+ for (y = min_y; y <= max_y; y += rwidth)
+ {
+ U64 region_handle = to_region_handle(x, y);
+ if (!getRegionFromHandle(region_handle))
+ {
+ waterp = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER, gAgent.getRegion());
+ waterp->setUseTexture(FALSE);
+ gPipeline.addObject(waterp);
+ waterp->setPositionGlobal(LLVector3d(x + rwidth/2,
+ y + rwidth/2,
+ DEFAULT_WATER_HEIGHT));
+ waterp->setScale(LLVector3((F32)rwidth, (F32)rwidth, 0.f));
+ mHoleWaterObjects.addData(waterp);
+ }
+ }
+ }
+
+ // Update edge water objects
+ S32 wx, wy;
+ S32 center_x, center_y;
+ wx = (max_x - min_x) + rwidth;
+ wy = (max_y - min_y) + rwidth;
+ center_x = min_x + (wx >> 1);
+ center_y = min_y + (wy >> 1);
+
+
+
+ S32 add_boundary[4] = {
+ 512 - (max_x - region_x),
+ 512 - (max_y - region_y),
+ 512 - (region_x - min_x),
+ 512 - (region_y - min_y) };
+
+
+ S32 dir;
+ for (dir = 0; dir < 8; dir++)
+ {
+ S32 dim[2] = { 0 };
+ switch (gDirAxes[dir][0])
+ {
+ case -1: dim[0] = add_boundary[2]; break;
+ case 0: dim[0] = wx; break;
+ default: dim[0] = add_boundary[0]; break;
+ }
+ switch (gDirAxes[dir][1])
+ {
+ case -1: dim[1] = add_boundary[3]; break;
+ case 0: dim[1] = wy; break;
+ default: dim[1] = add_boundary[1]; break;
+ }
+
+ if (dim[0] == 0 || dim[1] == 0)
+ {
+ continue;
+ }
+
+ // Resize and reshape the water objects
+ const S32 water_center_x = center_x + llround((wx + dim[0]) * 0.5f * gDirAxes[dir][0]);
+ const S32 water_center_y = center_y + llround((wy + dim[1]) * 0.5f * gDirAxes[dir][1]);
+
+
+ waterp = mEdgeWaterObjects[dir];
+ if (!waterp || waterp->isDead())
+ {
+ // The edge water objects can be dead because they're attached to the region that the
+ // agent was in when they were originally created.
+ mEdgeWaterObjects[dir] = (LLVOWater *)gObjectList.createObjectViewer(LLViewerObject::LL_VO_WATER,
+ gAgent.getRegion());
+ waterp = mEdgeWaterObjects[dir];
+ waterp->setUseTexture(FALSE);
+ gPipeline.addObject(waterp);
+ }
+
+ waterp->setRegion(gAgent.getRegion());
+ LLVector3d water_pos(water_center_x, water_center_y,
+ DEFAULT_WATER_HEIGHT);
+ waterp->setPositionGlobal(water_pos);
+ waterp->setScale(LLVector3((F32)dim[0], (F32)dim[1], 0.f));
+ gObjectList.updateActive(waterp);
+ /*if (!gNoRender)
+ {
+ gPipeline.markMoved(waterp->mDrawable);
+ }*/
+ }
+
+
+ //llinfos << "End water update" << llendl;
+}
+
+LLViewerImage *LLWorld::getDefaultWaterTexture()
+{
+ return mDefaultWaterTexturep;
+}
+
+void LLWorld::setSpaceTimeUSec(const U64 space_time_usec)
+{
+ mSpaceTimeUSec = space_time_usec;
+}
+
+U64 LLWorld::getSpaceTimeUSec() const
+{
+ return mSpaceTimeUSec;
+}
+
+void LLWorld::requestCacheMisses()
+{
+ for(LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ regionp->requestCacheMisses();
+ }
+}
+
+LLString LLWorld::getInfoString()
+{
+ LLString info_string("World Info:\n");
+ for (LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ info_string += regionp->getInfoString();
+ }
+ return info_string;
+}
+
+void LLWorld::disconnectRegions()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ for(LLViewerRegion* regionp = mRegionList.getFirstData();
+ regionp;
+ regionp = mRegionList.getNextData())
+ {
+ if (regionp == gAgent.getRegion())
+ {
+ // Skip the main agent
+ continue;
+ }
+
+ llinfos << "Sending AgentQuitCopy to: " << regionp->getHost() << llendl;
+ msg->newMessageFast(_PREHASH_AgentQuitCopy);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_FuseBlock);
+ msg->addU32Fast(_PREHASH_ViewerCircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->sendMessage(regionp->getHost());
+ }
+}
+
+
+void process_enable_simulator(LLMessageSystem *msg, void **user_data)
+{
+ // enable the appropriate circuit for this simulator and
+ // add its values into the gSimulator structure
+ U64 handle;
+ U32 ip_u32;
+ U16 port;
+
+ msg->getU64Fast(_PREHASH_SimulatorInfo, _PREHASH_Handle, handle);
+ msg->getIPAddrFast(_PREHASH_SimulatorInfo, _PREHASH_IP, ip_u32);
+ msg->getIPPortFast(_PREHASH_SimulatorInfo, _PREHASH_Port, port);
+
+ // which simulator should we modify?
+ LLHost sim(ip_u32, port);
+
+ // Viewer trusts the simulator.
+ msg->enableCircuit(sim, TRUE);
+ gWorldp->addRegion(handle, sim);
+
+ // give the simulator a message it can use to get ip and port
+ llinfos << "simulator_enable() Enabling " << sim << " with code " << msg->getOurCircuitCode() << llendl;
+ msg->newMessageFast(_PREHASH_UseCircuitCode);
+ msg->nextBlockFast(_PREHASH_CircuitCode);
+ msg->addU32Fast(_PREHASH_Code, msg->getOurCircuitCode());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addUUIDFast(_PREHASH_ID, gAgent.getID());
+ msg->sendReliable(sim);
+}
+
+
+// disable the circuit to this simulator
+// Called in response to "DisableSimulator" message.
+void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data)
+{
+ LLHost host = mesgsys->getSender();
+
+ //llinfos << "Disabling simulator with message from " << host << llendl;
+ gWorldp->removeRegion(host);
+
+ mesgsys->disableCircuit(host);
+}
+
+
+void process_region_handshake(LLMessageSystem* msg, void** user_data)
+{
+ LLHost host = msg->getSender();
+ LLViewerRegion* regionp = gWorldp->getRegion(host);
+ if (!regionp)
+ {
+ llwarns << "Got region handshake for unknown region "
+ << host << llendl;
+ return;
+ }
+
+ regionp->unpackRegionHandshake();
+}
+
+
+void send_agent_pause()
+{
+ // world not initialized yet
+ if (!gWorldp) return;
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentPause);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
+
+ gAgentPauseSerialNum++;
+ gMessageSystem->addU32Fast(_PREHASH_SerialNum, gAgentPauseSerialNum);
+
+ LLViewerRegion *regionp;
+ for (regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ gMessageSystem->sendReliable(regionp->getHost());
+ }
+
+ gMessageSystem->sendReliable(gUserServer);
+
+ gObjectList.mWasPaused = TRUE;
+}
+
+
+void send_agent_resume()
+{
+ // world not initialized yet
+ if (!gWorldp) return;
+
+ gMessageSystem->newMessageFast(_PREHASH_AgentResume);
+ gMessageSystem->nextBlockFast(_PREHASH_AgentData);
+ gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
+ gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
+
+ gAgentPauseSerialNum++;
+ gMessageSystem->addU32Fast(_PREHASH_SerialNum, gAgentPauseSerialNum);
+
+
+ LLViewerRegion *regionp;
+ for (regionp = gWorldp->mActiveRegionList.getFirstData();
+ regionp;
+ regionp = gWorldp->mActiveRegionList.getNextData())
+ {
+ gMessageSystem->sendReliable(regionp->getHost());
+ }
+
+ gMessageSystem->sendReliable(gUserServer);
+
+ // Reset the FPS counter to avoid an invalid fps
+ gViewerStats->mFPSStat.start();
+}
+
+
diff --git a/indra/newview/llworld.h b/indra/newview/llworld.h
new file mode 100644
index 0000000000..71d7a02322
--- /dev/null
+++ b/indra/newview/llworld.h
@@ -0,0 +1,178 @@
+/**
+ * @file llworld.h
+ * @brief Initial test structure to organize viewer regions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORLD_H
+#define LL_LLWORLD_H
+
+#include "llpatchvertexarray.h"
+#include "doublelinkedlist.h"
+#include "linked_lists.h"
+
+#include "llmath.h"
+//#include "vmath.h"
+#include "v3math.h"
+#include "llmemory.h"
+#include "llstring.h"
+#include "llviewerpartsim.h"
+#include "llviewerimage.h"
+
+class LLViewerRegion;
+class LLVector3d;
+class LLMessageSystem;
+class LLNetMap;
+class LLHost;
+
+class LLViewerObject;
+class LLVOWater;
+class LLSurfacePatch;
+
+class LLCloudPuff;
+class LLCloudGroup;
+class LLVOAvatar;
+
+// LLWorld maintains a stack of unused viewer_regions and an array of pointers to viewer regions
+// as simulators are connected to, viewer_regions are popped off the stack and connected as required
+// as simulators are removed, they are pushed back onto the stack
+
+class LLWorld
+{
+public:
+ LLWorld(const U32 grids_per_region, const F32 meters_per_grid);
+ ~LLWorld();
+
+ LLViewerRegion* addRegion(const U64 &region_handle, const LLHost &host);
+ // safe to call if already present, does the "right thing" if
+ // hosts are same, or if hosts are different, etc...
+ void removeRegion(const LLHost &host);
+
+ void disconnectRegions(); // Send quit messages to all child regions
+
+ LLViewerRegion* getRegion(const LLHost &host);
+ LLViewerRegion* getRegionFromPosGlobal(const LLVector3d &pos);
+ LLViewerRegion* getRegionFromPosAgent(const LLVector3 &pos);
+ LLViewerRegion* getRegionFromHandle(const U64 &handle);
+ BOOL positionRegionValidGlobal(const LLVector3d& pos); // true if position is in valid region
+ LLVector3d clipToVisibleRegions(const LLVector3d &start_pos, const LLVector3d &end_pos);
+
+ void updateAgentOffset(const LLVector3d &offset);
+
+ // All of these should be in the agent coordinate frame
+ LLViewerRegion* resolveRegionGlobal(LLVector3 &localpos, const LLVector3d &position);
+ LLViewerRegion* resolveRegionAgent(LLVector3 &localpos, const LLVector3 &position);
+ F32 resolveLandHeightGlobal(const LLVector3d &position);
+ F32 resolveLandHeightAgent(const LLVector3 &position);
+
+ // Return the lowest allowed Z point to prevent objects from being moved
+ // underground.
+ F32 getMinAllowedZ(LLViewerObject* object);
+
+ // takes a line segment defined by point_a and point_b, then
+ // determines the closest (to point_a) point of intersection that is
+ // on the land surface or on an object of the world.
+ // Stores results in "intersection" and "intersection_normal" and
+ // returns a scalar value that is the normalized (by length of line segment)
+ // distance along the line from "point_a" to "intersection".
+ //
+ // Currently assumes point_a and point_b only differ in z-direction,
+ // but it may eventually become more general.
+ F32 resolveStepHeightGlobal(const LLVOAvatar* avatarp, const LLVector3d &point_a, const LLVector3d &point_b,
+ LLVector3d &intersection, LLVector3 &intersection_normal,
+ LLViewerObject** viewerObjectPtr=NULL);
+
+ LLSurfacePatch * resolveLandPatchGlobal(const LLVector3d &position);
+ LLVector3 resolveLandNormalGlobal(const LLVector3d &position); // absolute frame
+
+ U32 getRegionWidthInPoints() const { return mWidth; }
+ F32 getRegionScale() const { return mScale; }
+
+ // region X and Y size in meters
+ F32 getRegionWidthInMeters() const { return mWidthInMeters; }
+ F32 getRegionMinHeight() const { return -mWidthInMeters; }
+ F32 getRegionMaxHeight() const { return 3.f*mWidthInMeters; }
+
+ void updateRegions();
+ void updateVisibilities();
+ void updateParticles();
+ void updateClouds(const F32 dt);
+ LLCloudGroup * findCloudGroup(const LLCloudPuff &puff);
+
+ void renderPropertyLines();
+
+ void resetStats();
+ void updateNetStats(); // Update network statistics for all the regions...
+
+ void printPacketsLost();
+ void requestCacheMisses();
+
+ // deal with map object updates in the world.
+ static void processCoarseUpdate(LLMessageSystem* msg, void** user_data);
+
+ F32 getLandFarClip() const;
+ void setLandFarClip(const F32 far_clip);
+
+ LLViewerImage *getDefaultWaterTexture();
+ void updateWaterObjects();
+
+ void setSpaceTimeUSec(const U64 space_time_usec);
+ U64 getSpaceTimeUSec() const;
+
+ LLString getInfoString();
+public:
+ LLDoubleLinkedList<LLViewerRegion> mActiveRegionList;
+ LLViewerPartSim mPartSim;
+
+private:
+ LLLinkedList<LLViewerRegion> mRegionList;
+ LLDoubleLinkedList<LLViewerRegion> mVisibleRegionList;
+ LLDoubleLinkedList<LLViewerRegion> mCulledRegionList;
+
+ // Number of points on edge
+ const U32 mWidth;
+
+ // meters/point, therefore mWidth * mScale = meters per edge
+ const F32 mScale;
+
+ const F32 mWidthInMeters;
+
+ F32 mLandFarClip; // Far clip distance for land.
+ F32 mIdleUpdateTime;
+ LLPatchVertexArray mLandPatch;
+ S32 mLastPacketsIn;
+ S32 mLastPacketsOut;
+ S32 mLastPacketsLost;
+
+ ////////////////////////////
+ //
+ // Data for "Fake" objects
+ //
+
+ // Used to define the "Square" which we need to fill in
+ U32 mMinRegionX;
+ U32 mMaxRegionX;
+ U32 mMinRegionY;
+ U32 mMaxRegionY;
+
+ LLLinkedList<LLVOWater> mHoleWaterObjects;
+ LLPointer<LLVOWater> mEdgeWaterObjects[8];
+
+ LLPointer<LLViewerImage> mDefaultWaterTexturep;
+ U64 mSpaceTimeUSec;
+};
+
+extern LLWorld *gWorldp;
+#define gWorldPointer gWorldp
+
+void process_enable_simulator(LLMessageSystem *mesgsys, void **user_data);
+void process_disable_simulator(LLMessageSystem *mesgsys, void **user_data);
+
+void process_region_handshake(LLMessageSystem* msg, void** user_data);
+
+void send_agent_pause();
+void send_agent_resume();
+
+#endif
diff --git a/indra/newview/llworldmap.cpp b/indra/newview/llworldmap.cpp
new file mode 100644
index 0000000000..42d8b9fd20
--- /dev/null
+++ b/indra/newview/llworldmap.cpp
@@ -0,0 +1,915 @@
+/**
+ * @file llworldmap.cpp
+ * @brief Underlying data representation for map of the world
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmap.h"
+
+#include "llregionhandle.h"
+#include "message.h"
+
+#include "viewer.h" // for gPacificDaylightTime
+#include "llagent.h"
+#include "llmapresponders.h"
+#include "llviewercontrol.h"
+#include "llfloaterworldmap.h"
+#include "lltracker.h"
+#include "llviewerimagelist.h"
+#include "llviewerregion.h"
+#include "llregionflags.h"
+
+LLWorldMap* gWorldMap = NULL;
+
+const F32 REQUEST_ITEMS_TIMER = 10.f * 60.f; // 10 minutes
+
+LLItemInfo::LLItemInfo(F32 global_x, F32 global_y,
+ const std::string& name,
+ LLUUID id,
+ S32 extra, S32 extra2)
+: mName(name),
+ mToolTip(""),
+ mPosGlobal(global_x, global_y, 40.0),
+ mID(id),
+ mSelected(FALSE),
+ mExtra(extra),
+ mExtra2(extra2)
+{
+ mRegionHandle = to_region_handle(mPosGlobal);
+}
+
+LLSimInfo::LLSimInfo()
+: mHandle(0),
+ mName(),
+ mAgentsUpdateTime(0),
+ mAccess(0x0),
+ mRegionFlags(0x0),
+ mWaterHeight(0.f),
+ mAlpha(-1.f)
+{
+}
+
+
+LLVector3d LLSimInfo::getGlobalPos(LLVector3 local_pos) const
+{
+ LLVector3d pos = from_region_handle(mHandle);
+ pos.mdV[VX] += local_pos.mV[VX];
+ pos.mdV[VY] += local_pos.mV[VY];
+ pos.mdV[VZ] += local_pos.mV[VZ];
+ return pos;
+}
+
+
+//---------------------------------------------------------------------------
+// World Map
+//---------------------------------------------------------------------------
+
+LLWorldMap::LLWorldMap() :
+ mIsTrackingUnknownLocation( FALSE ),
+ mInvalidLocation( FALSE ),
+ mIsTrackingDoubleClick( FALSE ),
+ mIsTrackingCommit( FALSE ),
+ mUnknownLocation( 0, 0, 0 ),
+ mRequestLandForSale(true),
+ mCurrentMap(0),
+ mMinX(U32_MAX),
+ mMaxX(U32_MIN),
+ mMinY(U32_MAX),
+ mMaxY(U32_MIN),
+ mNeighborMap(NULL),
+ mTelehubCoverageMap(NULL),
+ mNeighborMapWidth(0),
+ mNeighborMapHeight(0)
+{
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ mMapLoaded[map] = FALSE;
+ mMapBlockLoaded[map] = new BOOL[MAP_BLOCK_RES*MAP_BLOCK_RES];
+ for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
+ {
+ mMapBlockLoaded[map][idx] = FALSE;
+ }
+ }
+}
+
+
+LLWorldMap::~LLWorldMap()
+{
+ reset();
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ delete[] mMapBlockLoaded[map];
+ }
+}
+
+
+void LLWorldMap::reset()
+{
+ for_each(mSimInfoMap.begin(), mSimInfoMap.end(), DeletePairedPointer());
+ mSimInfoMap.clear();
+
+ for (S32 m=0; m<MAP_SIM_IMAGE_TYPES; ++m)
+ {
+ mMapLoaded[m] = FALSE;
+ }
+
+ clearSimFlags();
+
+ eraseItems();
+
+ mMinX = U32_MAX;
+ mMaxX = U32_MIN;
+
+ mMinY = U32_MAX;
+ mMaxY = U32_MIN;
+
+ delete [] mNeighborMap;
+ mNeighborMap = NULL;
+ delete [] mTelehubCoverageMap;
+ mTelehubCoverageMap = NULL;
+
+ mNeighborMapWidth = 0;
+ mNeighborMapHeight = 0;
+}
+
+void LLWorldMap::eraseItems()
+{
+ if (mRequestTimer.getElapsedTimeF32() > REQUEST_ITEMS_TIMER)
+ {
+ mRequestTimer.reset();
+
+ mTelehubs.clear();
+ mInfohubs.clear();
+ mPGEvents.clear();
+ mMatureEvents.clear();
+ mPopular.clear();
+ mLandForSale.clear();
+ mClassifieds.clear();
+ }
+// mAgentLocationsMap.clear(); // persists
+// mNumAgents.clear(); // persists
+}
+
+
+void LLWorldMap::clearImageRefs()
+{
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ LLSimInfo* info = (*it).second;
+ if (info->mCurrentImage)
+ {
+ info->mCurrentImage->setBoostLevel(0);
+ info->mCurrentImage = NULL;
+ }
+ if (info->mOverlayImage)
+ {
+ info->mOverlayImage->setBoostLevel(0);
+ info->mOverlayImage = NULL;
+ }
+ }
+}
+
+// Doesn't clear the already-loaded sim infos, just re-requests them
+void LLWorldMap::clearSimFlags()
+{
+ for (S32 map=0; map<MAP_SIM_IMAGE_TYPES; ++map)
+ {
+ for (S32 idx=0; idx<MAP_BLOCK_RES*MAP_BLOCK_RES; ++idx)
+ {
+ mMapBlockLoaded[map][idx] = FALSE;
+ }
+ }
+}
+
+LLSimInfo* LLWorldMap::simInfoFromPosGlobal(const LLVector3d& pos_global)
+{
+ U64 handle = to_region_handle(pos_global);
+ return simInfoFromHandle(handle);
+}
+
+LLSimInfo* LLWorldMap::simInfoFromHandle(const U64 handle)
+{
+ sim_info_map_t::iterator it = mSimInfoMap.find(handle);
+ if (it != mSimInfoMap.end())
+ {
+ LLSimInfo* sim_info = (*it).second;
+ if (sim_info)
+ {
+ return sim_info;
+ }
+ }
+ return NULL;
+}
+
+
+LLSimInfo* LLWorldMap::simInfoFromName(const LLString& sim_name)
+{
+ LLSimInfo* sim_info = NULL;
+ if (!sim_name.empty())
+ {
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ sim_info = (*it).second;
+ if (sim_info
+ && (0 == LLString::compareInsensitive(sim_name.c_str(), sim_info->mName.c_str())) )
+ {
+ break;
+ }
+ sim_info = NULL;
+ }
+ }
+ return sim_info;
+}
+
+LLString LLWorldMap::simNameFromPosGlobal(const LLVector3d& pos_global)
+{
+ U64 handle = to_region_handle(pos_global);
+
+ sim_info_map_t::iterator it = mSimInfoMap.find(handle);
+ if (it != mSimInfoMap.end())
+ {
+ LLSimInfo* info = (*it).second;
+ return info->mName.c_str();
+ }
+ else
+ {
+ return "(unknown region)";
+ }
+}
+
+void LLWorldMap::setCurrentLayer(S32 layer, bool request_layer)
+{
+ mCurrentMap = layer;
+ if (!mMapLoaded[layer] || request_layer)
+ {
+ sendMapLayerRequest();
+ sendItemRequest(MAP_ITEM_AGENT_COUNT);
+ }
+
+ if (mTelehubs.size() == 0 ||
+ mInfohubs.size() == 0)
+ {
+ // Request for telehubs
+ sendItemRequest(MAP_ITEM_TELEHUB);
+ }
+
+ if (mPGEvents.size() == 0)
+ {
+ // Request for events
+ sendItemRequest(MAP_ITEM_PG_EVENT);
+ }
+
+ if (mMatureEvents.size() == 0)
+ {
+ // Request for events (mature)
+ sendItemRequest(MAP_ITEM_MATURE_EVENT);
+ }
+
+ if (mPopular.size() == 0)
+ {
+ // Request for popular
+ sendItemRequest(MAP_ITEM_POPULAR);
+ }
+
+ if (mLandForSale.size() == 0)
+ {
+ // Request for Land For Sale
+ sendItemRequest(MAP_ITEM_LAND_FOR_SALE);
+ }
+
+ if (mClassifieds.size() == 0)
+ {
+ sendItemRequest(MAP_ITEM_CLASSIFIED);
+ }
+
+ clearImageRefs();
+ clearSimFlags();
+}
+
+void LLWorldMap::sendItemRequest(U32 type, U64 handle)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ msg->newMessageFast(_PREHASH_MapItemRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+
+ msg->nextBlockFast(_PREHASH_RequestData);
+ msg->addU32Fast(_PREHASH_ItemType, type);
+ msg->addU64Fast(_PREHASH_RegionHandle, handle); // If zero, filled in on sim
+
+ gAgent.sendReliableMessage();
+}
+
+// public
+void LLWorldMap::sendMapLayerRequest()
+{
+ LLSD body;
+ body["Flags"] = mCurrentMap;
+ std::string url = gAgent.getRegion()->getCapability(
+ gAgent.isGodlike() ? "MapLayerGod" : "MapLayer");
+
+ if (!url.empty())
+ {
+ llinfos << "LLWorldMap::sendMapLayerRequest via capability" << llendl;
+ LLHTTPClient::post(url, body, new LLMapLayerResponder());
+ }
+ else
+ {
+ llinfos << "LLWorldMap::sendMapLayerRequest via message system" << llendl;
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ // Request for layer
+ msg->newMessageFast(_PREHASH_MapLayerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ gAgent.sendReliableMessage();
+
+ if (mRequestLandForSale)
+ {
+ msg->newMessageFast(_PREHASH_MapLayerRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, 2);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ gAgent.sendReliableMessage();
+ }
+ }
+}
+
+// public
+void LLWorldMap::sendNamedRegionRequest(std::string region_name)
+{
+ LLMessageSystem* msg = gMessageSystem;
+ S32 layer = mCurrentMap;
+
+ // Request for layer
+ msg->newMessageFast(_PREHASH_MapNameRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, layer);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_NameData);
+ msg->addStringFast(_PREHASH_Name, region_name);
+ gAgent.sendReliableMessage();
+}
+
+// public
+void LLWorldMap::sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent)
+{
+ S32 layer = mCurrentMap;
+ LLMessageSystem* msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_MapBlockRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ U32 flags = layer;
+ flags |= (return_nonexistent ? 0x10000 : 0);
+ msg->addU32Fast(_PREHASH_Flags, flags);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_PositionData);
+ msg->addU16Fast(_PREHASH_MinX, min_x);
+ msg->addU16Fast(_PREHASH_MinY, min_y);
+ msg->addU16Fast(_PREHASH_MaxX, max_x);
+ msg->addU16Fast(_PREHASH_MaxY, max_y);
+ gAgent.sendReliableMessage();
+
+ if (mRequestLandForSale)
+ {
+ msg->newMessageFast(_PREHASH_MapBlockRequest);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_Flags, 2);
+ msg->addU32Fast(_PREHASH_EstateID, 0); // Filled in on sim
+ msg->addBOOLFast(_PREHASH_Godlike, FALSE); // Filled in on sim
+ msg->nextBlockFast(_PREHASH_PositionData);
+ msg->addU16Fast(_PREHASH_MinX, min_x);
+ msg->addU16Fast(_PREHASH_MinY, min_y);
+ msg->addU16Fast(_PREHASH_MaxX, max_x);
+ msg->addU16Fast(_PREHASH_MaxY, max_y);
+ gAgent.sendReliableMessage();
+ }
+}
+
+// public static
+void LLWorldMap::processMapLayerReply(LLMessageSystem* msg, void**)
+{
+ llinfos << "LLWorldMap::processMapLayerReply from message system" << llendl;
+
+ U32 agent_flags;
+ msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
+
+ if (agent_flags != (U32)gWorldMap->mCurrentMap)
+ {
+ llwarns << "Invalid or out of date map image type returned!" << llendl;
+ return;
+ }
+
+ LLUUID image_id;
+ //U32 left, right, top, bottom;
+
+ S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_LayerData);
+
+ gWorldMap->mMapLayers[agent_flags].clear();
+
+ BOOL adjust = FALSE;
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ LLWorldMapLayer new_layer;
+ new_layer.LayerDefined = TRUE;
+ msg->getUUIDFast(_PREHASH_LayerData, _PREHASH_ImageID, new_layer.LayerImageID, block);
+ new_layer.LayerImage = gImageList.getImage(new_layer.LayerImageID, MIPMAP_TRUE, FALSE);
+ new_layer.LayerImage->bindTexture(0);
+ new_layer.LayerImage->setClamp(TRUE, TRUE);
+
+ U32 left, right, top, bottom;
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Left, left, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Right, right, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Top, top, block);
+ msg->getU32Fast(_PREHASH_LayerData, _PREHASH_Bottom, bottom, block);
+
+ new_layer.LayerExtents.mLeft = left;
+ new_layer.LayerExtents.mRight = right;
+ new_layer.LayerExtents.mBottom = bottom;
+ new_layer.LayerExtents.mTop = top;
+
+ F32 x_meters = F32(left*REGION_WIDTH_UNITS);
+ F32 y_meters = F32(bottom*REGION_WIDTH_UNITS);
+ adjust = gWorldMap->extendAABB(U32(x_meters), U32(y_meters),
+ U32(x_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getWidth()),
+ U32(y_meters+REGION_WIDTH_UNITS*new_layer.LayerExtents.getHeight())) || adjust;
+
+ gWorldMap->mMapLayers[agent_flags].push_back(new_layer);
+ }
+
+ gWorldMap->mMapLoaded[agent_flags] = TRUE;
+ if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
+}
+
+// public static
+void LLWorldMap::processMapBlockReply(LLMessageSystem* msg, void**)
+{
+ U32 agent_flags;
+ msg->getU32Fast(_PREHASH_AgentData, _PREHASH_Flags, agent_flags);
+
+ if (agent_flags < 0 || agent_flags >= MAP_SIM_IMAGE_TYPES)
+ {
+ llwarns << "Invalid map image type returned! " << agent_flags << llendl;
+ return;
+ }
+
+ S32 num_blocks = msg->getNumberOfBlocksFast(_PREHASH_Data);
+
+ bool found_null_sim = false;
+
+ BOOL adjust = FALSE;
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U16 x_regions;
+ U16 y_regions;
+ char name[MAX_STRING];
+ U8 access;
+ U32 region_flags;
+ U8 water_height;
+ U8 agents;
+ LLUUID image_id;
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_X, x_regions, block);
+ msg->getU16Fast(_PREHASH_Data, _PREHASH_Y, y_regions, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, MAX_STRING, name, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_Access, access, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_RegionFlags, region_flags, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_WaterHeight, water_height, block);
+ msg->getU8Fast(_PREHASH_Data, _PREHASH_Agents, agents, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_MapImageID, image_id, block);
+
+ U32 x_meters = x_regions * REGION_WIDTH_UNITS;
+ U32 y_meters = y_regions * REGION_WIDTH_UNITS;
+
+ if (access == 255)
+ {
+ // This region doesn't exist
+ if (gWorldMap->mIsTrackingUnknownLocation &&
+ gWorldMap->mUnknownLocation.mdV[0] >= x_meters &&
+ gWorldMap->mUnknownLocation.mdV[0] < x_meters + 256 &&
+ gWorldMap->mUnknownLocation.mdV[1] >= y_meters &&
+ gWorldMap->mUnknownLocation.mdV[1] < y_meters + 256)
+ {
+ // We were tracking this location, but it doesn't exist
+ gWorldMap->mInvalidLocation = TRUE;
+ }
+
+ found_null_sim = true;
+ }
+ else
+ {
+ adjust = gWorldMap->extendAABB(x_meters,
+ y_meters,
+ x_meters+REGION_WIDTH_UNITS,
+ y_meters+REGION_WIDTH_UNITS) || adjust;
+ U64 handle = to_region_handle(x_meters, y_meters);
+
+// llinfos << "Map sim " << name << " image layer " << agent_flags << " ID " << image_id.getString() << llendl;
+
+ LLSimInfo* siminfo = new LLSimInfo();
+ sim_info_map_t::iterator iter = gWorldMap->mSimInfoMap.find(handle);
+ if (iter != gWorldMap->mSimInfoMap.end())
+ {
+ LLSimInfo* oldinfo = iter->second;
+ for (S32 image=0; image<MAP_SIM_IMAGE_TYPES; ++image)
+ {
+ siminfo->mMapImageID[image] = oldinfo->mMapImageID[image];
+ }
+ delete oldinfo;
+ }
+ gWorldMap->mSimInfoMap[handle] = siminfo;
+
+ siminfo->mHandle = handle;
+ siminfo->mName.assign( name );
+ siminfo->mAccess = access;
+ siminfo->mRegionFlags = region_flags;
+ siminfo->mWaterHeight = (F32) water_height;
+ siminfo->mMapImageID[agent_flags] = image_id;
+ siminfo->mCurrentImage = gImageList.getImage(siminfo->mMapImageID[gWorldMap->mCurrentMap], MIPMAP_TRUE, FALSE);
+ siminfo->mCurrentImage->bindTexture(0);
+ siminfo->mCurrentImage->setClamp(TRUE, TRUE);
+
+ if (siminfo->mMapImageID[2].notNull())
+ {
+ siminfo->mOverlayImage = gImageList.getImage(siminfo->mMapImageID[2], MIPMAP_TRUE, FALSE);
+ }
+ else
+ {
+ siminfo->mOverlayImage = NULL;
+ }
+
+ if (gWorldMap->mIsTrackingUnknownLocation &&
+ gWorldMap->mUnknownLocation.mdV[0] >= x_meters &&
+ gWorldMap->mUnknownLocation.mdV[0] < x_meters + 256 &&
+ gWorldMap->mUnknownLocation.mdV[1] >= y_meters &&
+ gWorldMap->mUnknownLocation.mdV[1] < y_meters + 256)
+ {
+ if (siminfo->mAccess == SIM_ACCESS_DOWN)
+ {
+ // We were tracking this location, but it doesn't exist
+ gWorldMap->mInvalidLocation = true;
+ }
+ else
+ {
+ // We were tracking this location, and it does exist
+ bool is_tracking_dbl = gWorldMap->mIsTrackingDoubleClick == TRUE;
+ gFloaterWorldMap->trackLocation(gWorldMap->mUnknownLocation);
+ if (is_tracking_dbl)
+ {
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+ }
+ }
+ }
+ if(adjust) gFloaterWorldMap->adjustZoomSliderBounds();
+ gFloaterWorldMap->updateSims(found_null_sim);
+}
+
+// public static
+void LLWorldMap::processMapItemReply(LLMessageSystem* msg, void**)
+{
+ U32 type;
+ msg->getU32Fast(_PREHASH_RequestData, _PREHASH_ItemType, type);
+
+ S32 num_blocks = msg->getNumberOfBlocks("Data");
+
+ for (S32 block=0; block<num_blocks; ++block)
+ {
+ U32 X, Y;
+ char name[MAX_STRING];
+ S32 extra, extra2;
+ LLUUID uuid;
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_X, X, block);
+ msg->getU32Fast(_PREHASH_Data, _PREHASH_Y, Y, block);
+ msg->getStringFast(_PREHASH_Data, _PREHASH_Name, MAX_STRING, name, block);
+ msg->getUUIDFast(_PREHASH_Data, _PREHASH_ID, uuid, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra, extra, block);
+ msg->getS32Fast(_PREHASH_Data, _PREHASH_Extra2, extra2, block);
+
+ F32 world_x = (F32)X;
+ X /= REGION_WIDTH_UNITS;
+ F32 world_y = (F32)Y;
+ Y /= REGION_WIDTH_UNITS;
+
+ LLItemInfo new_item(world_x, world_y, name, uuid, extra, extra2);
+ LLSimInfo* siminfo = gWorldMap->simInfoFromHandle(new_item.mRegionHandle);
+
+ switch (type)
+ {
+ case MAP_ITEM_TELEHUB: // telehubs
+ {
+ // Telehub color, store in extra as 4 U8's
+ U8 *color = (U8 *)&new_item.mExtra;
+
+ F32 red = fmod((F32)X * 0.11f, 1.f) * 0.8f;
+ F32 green = fmod((F32)Y * 0.11f, 1.f) * 0.8f;
+ F32 blue = fmod(1.5f * (F32)(X + Y) * 0.11f, 1.f) * 0.8f;
+ F32 add_amt = (X % 2) ? 0.15f : -0.15f;
+ add_amt += (Y % 2) ? -0.15f : 0.15f;
+ color[0] = U8((red + add_amt) * 255);
+ color[1] = U8((green + add_amt) * 255);
+ color[2] = U8((blue + add_amt) * 255);
+ color[3] = 255;
+
+ // extra2 specifies whether this is an infohub or a telehub.
+ if (extra2)
+ {
+ gWorldMap->mInfohubs.push_back(new_item);
+ }
+ else
+ {
+ gWorldMap->mTelehubs.push_back(new_item);
+ }
+
+ break;
+ }
+ case MAP_ITEM_PG_EVENT: // events
+ case MAP_ITEM_MATURE_EVENT:
+ {
+ char buffer[32];
+ struct tm* timep;
+ // Convert to Pacific, based on server's opinion of whether
+ // it's daylight savings time there.
+ timep = utc_to_pacific_time(extra, gPacificDaylightTime);
+
+ S32 display_hour = timep->tm_hour % 12;
+ if (display_hour == 0) display_hour = 12;
+
+ sprintf(buffer, "%d:%02d %s",
+ display_hour,
+ timep->tm_min,
+ (timep->tm_hour < 12 ? "AM" : "PM") );
+ new_item.mToolTip.assign( buffer );
+
+ // HACK: store Z in extra2
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ if (type == MAP_ITEM_PG_EVENT)
+ {
+ gWorldMap->mPGEvents.push_back(new_item);
+ }
+ else
+ {
+ gWorldMap->mMatureEvents.push_back(new_item);
+ }
+ break;
+ }
+ case MAP_ITEM_POPULAR: // popular
+ {
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ gWorldMap->mPopular.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_LAND_FOR_SALE: // land for sale
+ {
+ new_item.mToolTip = llformat("%d sq. m. L$%d", new_item.mExtra, new_item.mExtra2);
+ gWorldMap->mLandForSale.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_CLASSIFIED: // classifieds
+ {
+ // HACK: Z-height is in Extra2 field.
+ new_item.mPosGlobal.mdV[VZ] = (F64)extra2;
+ gWorldMap->mClassifieds.push_back(new_item);
+ break;
+ }
+ case MAP_ITEM_AGENT_COUNT: // agent counts
+ {
+ // We only ever receive one per region, i.e. this update superceeds any others
+ gWorldMap->mNumAgents[new_item.mRegionHandle] = new_item.mExtra;
+ break;
+ }
+ case MAP_ITEM_AGENT_LOCATIONS: // agent locations
+ {
+ if (!siminfo)
+ {
+ llinfos << "siminfo missing for " << new_item.mPosGlobal.mdV[0] << ", " << new_item.mPosGlobal.mdV[1] << llendl;
+ break;
+ }
+// llinfos << "New Location " << new_item.mName << llendl;
+
+ item_info_list_t& agentcounts = gWorldMap->mAgentLocationsMap[new_item.mRegionHandle];
+
+ // Find the last item in the list with a different name and erase them
+ item_info_list_t::iterator lastiter;
+ for (lastiter = agentcounts.begin(); lastiter!=agentcounts.end(); ++lastiter)
+ {
+ const LLItemInfo& info = *lastiter;
+ if (info.mName == new_item.mName)
+ {
+ break;
+ }
+ }
+ if (lastiter != agentcounts.begin())
+ {
+ agentcounts.erase(agentcounts.begin(), lastiter);
+ }
+ // Now append the new location
+ if (new_item.mExtra > 0)
+ {
+ agentcounts.push_back(new_item);
+ }
+ break;
+ }
+ default:
+ break;
+ };
+ }
+}
+
+void LLWorldMap::dump()
+{
+ for (sim_info_map_t::iterator it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+
+ llinfos << x_pos << "," << y_pos
+ << " " << info->mName.c_str()
+ << " " << (S32)info->mAccess
+ << " " << std::hex << info->mRegionFlags << std::dec
+ << " " << info->mWaterHeight
+ //<< " " << info->mTelehubName
+ //<< " " << info->mTelehubPosition
+ << llendl;
+
+ if (info->mCurrentImage)
+ {
+ llinfos << "image discard " << (S32)info->mCurrentImage->getDiscardLevel()
+ << " fullwidth " << info->mCurrentImage->getWidth(0)
+ << " fullheight " << info->mCurrentImage->getHeight(0)
+ << " maxvirt " << info->mCurrentImage->mMaxVirtualSize
+ << " maxdisc " << (S32)info->mCurrentImage->getMaxDiscardLevel()
+ << llendl;
+ }
+ }
+}
+
+
+BOOL LLWorldMap::extendAABB(U32 min_x, U32 min_y, U32 max_x, U32 max_y)
+{
+ BOOL rv = FALSE;
+ if (min_x < mMinX)
+ {
+ rv = TRUE;
+ mMinX = min_x;
+ }
+ if (min_y < mMinY)
+ {
+ rv = TRUE;
+ mMinY = min_y;
+ }
+ if (max_x > mMaxX)
+ {
+ rv = TRUE;
+ mMaxX = max_x;
+ }
+ if (max_y > mMaxY)
+ {
+ rv = TRUE;
+ mMaxY = max_y;
+ }
+ lldebugs << "World map aabb: (" << mMinX << ", " << mMinY << "), ("
+ << mMaxX << ", " << mMaxY << ")" << llendl;
+ return rv;
+}
+
+
+U32 LLWorldMap::getWorldWidth() const
+{
+ return mMaxX - mMinX;
+}
+
+
+U32 LLWorldMap::getWorldHeight() const
+{
+ return mMaxY - mMinY;
+}
+
+BOOL LLWorldMap::coveredByTelehub(LLSimInfo* infop)
+{
+ /*if (!mTelehubCoverageMap)
+ {
+ return FALSE;
+ }
+ U32 x_pos, y_pos;
+ from_region_handle(infop->mHandle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+
+ S32 index = x_pos - (mMinX / REGION_WIDTH_UNITS - 1) + (mNeighborMapWidth * (y_pos - (mMinY / REGION_WIDTH_UNITS - 1)));
+ return mTelehubCoverageMap[index] != 0; */
+ return FALSE;
+}
+
+void LLWorldMap::updateTelehubCoverage()
+{
+ /*S32 neighbor_width = getWorldWidth() / REGION_WIDTH_UNITS + 2;
+ S32 neighbor_height = getWorldHeight() / REGION_WIDTH_UNITS + 2;
+ if (neighbor_width > mNeighborMapWidth || neighbor_height > mNeighborMapHeight)
+ {
+ mNeighborMapWidth = neighbor_width;
+ mNeighborMapHeight = neighbor_height;
+ delete mNeighborMap;
+ delete mTelehubCoverageMap;
+
+ mNeighborMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
+ mTelehubCoverageMap = new U8[mNeighborMapWidth * mNeighborMapHeight];
+ }
+
+ memset(mNeighborMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
+ memset(mTelehubCoverageMap, 0, mNeighborMapWidth * mNeighborMapHeight * sizeof(U8));
+
+ // leave 1 sim border
+ S32 min_x = (mMinX / REGION_WIDTH_UNITS) - 1;
+ S32 min_y = (mMinY / REGION_WIDTH_UNITS) - 1;
+
+ std::map<U64, LLSimInfo*>::const_iterator it;
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ //LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+ x_pos -= min_x;
+ y_pos -= min_y;
+
+ S32 index = x_pos + (mNeighborMapWidth * y_pos);
+ mNeighborMap[index - 1]++;
+ mNeighborMap[index + 1]++;
+ mNeighborMap[index - mNeighborMapWidth]++;
+ mNeighborMap[index + mNeighborMapWidth]++;
+ }
+
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+ x_pos -= min_x;
+ y_pos -= min_y;
+
+ S32 index = x_pos + (mNeighborMapWidth * y_pos);
+
+ if (!info->mTelehubName.empty() && mNeighborMap[index])
+ {
+ S32 x_start = llmax(0, S32(x_pos - 5));
+ S32 x_span = llmin(mNeighborMapWidth - 1, (S32)(x_pos + 5)) - x_start + 1;
+ S32 y_start = llmax(0, (S32)y_pos - 5);
+ S32 y_end = llmin(mNeighborMapHeight - 1, (S32)(y_pos + 5));
+ for (S32 y_index = y_start; y_index <= y_end; y_index++)
+ {
+ memset(&mTelehubCoverageMap[x_start + y_index * mNeighborMapWidth], 0xff, sizeof(U8) * x_span);
+ }
+ }
+ }
+
+ for (it = mSimInfoMap.begin(); it != mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ //LLSimInfo* info = (*it).second;
+
+ U32 x_pos, y_pos;
+ from_region_handle(handle, &x_pos, &y_pos);
+ x_pos /= REGION_WIDTH_UNITS;
+ y_pos /= REGION_WIDTH_UNITS;
+
+ S32 index = x_pos - min_x + (mNeighborMapWidth * (y_pos - min_y));
+ mTelehubCoverageMap[index] *= mNeighborMap[index];
+ }*/
+}
diff --git a/indra/newview/llworldmap.h b/indra/newview/llworldmap.h
new file mode 100644
index 0000000000..76ab88de1e
--- /dev/null
+++ b/indra/newview/llworldmap.h
@@ -0,0 +1,192 @@
+/**
+ * @file llworldmap.h
+ * @brief Underlying data storage for the map of the entire world.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLWORLDMAP_H
+#define LL_LLWORLDMAP_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "v3math.h"
+#include "v3dmath.h"
+#include "llframetimer.h"
+#include "llmapimagetype.h"
+#include "lluuid.h"
+#include "llmemory.h"
+#include "llviewerimage.h"
+#include "lleventinfo.h"
+#include "v3color.h"
+
+class LLMessageSystem;
+
+
+class LLItemInfo
+{
+public:
+ LLItemInfo(F32 global_x, F32 global_y, const std::string& name, LLUUID id, S32 extra = 0, S32 extra2 = 0);
+
+ std::string mName;
+ std::string mToolTip;
+ LLVector3d mPosGlobal;
+ LLUUID mID;
+ BOOL mSelected;
+ S32 mExtra;
+ S32 mExtra2;
+ U64 mRegionHandle;
+};
+
+#define MAP_SIM_IMAGE_TYPES 3
+// 0 - Prim
+// 1 - Terrain Only
+// 2 - Overlay: Land For Sale
+
+class LLSimInfo
+{
+public:
+ LLSimInfo();
+
+ LLVector3d getGlobalPos(LLVector3 local_pos) const;
+
+public:
+ U64 mHandle;
+ std::string mName;
+
+ F64 mAgentsUpdateTime;
+ BOOL mShowAgentLocations; // are agents visible?
+
+ U8 mAccess;
+ U32 mRegionFlags;
+ F32 mWaterHeight;
+
+ F32 mAlpha;
+
+ // Image ID for the current overlay mode.
+ LLUUID mMapImageID[MAP_SIM_IMAGE_TYPES];
+
+ // Hold a reference to the currently displayed image.
+ LLPointer<LLViewerImage> mCurrentImage;
+ LLPointer<LLViewerImage> mOverlayImage;
+};
+
+#define MAP_BLOCK_RES 256
+
+struct LLWorldMapLayer
+{
+ BOOL LayerDefined;
+ LLPointer<LLViewerImage> LayerImage;
+ LLUUID LayerImageID;
+ LLRect LayerExtents;
+
+ LLWorldMapLayer() : LayerDefined(FALSE) { }
+};
+
+
+class LLWorldMap
+{
+public:
+ LLWorldMap();
+ ~LLWorldMap();
+
+ // clears the list
+ void reset();
+
+ // clear the visible items
+ void eraseItems();
+
+ // Removes references to cached images
+ void clearImageRefs();
+
+ // Clears the flags indicating that we've received sim infos
+ // Causes a re-request of the sim info without erasing extisting info
+ void clearSimFlags();
+
+ // Returns simulator information, or NULL if out of range
+ LLSimInfo* simInfoFromHandle(const U64 handle);
+
+ // Returns simulator information, or NULL if out of range
+ LLSimInfo* simInfoFromPosGlobal(const LLVector3d& pos_global);
+
+ // Returns simulator information for named sim, or NULL if non-existent
+ LLSimInfo* simInfoFromName(const LLString& sim_name);
+
+ // Returns simulator name
+ LLString simNameFromPosGlobal(const LLVector3d& pos_global);
+
+ // Sets the current layer
+ void setCurrentLayer(S32 layer, bool request_layer = false);
+
+ void sendMapLayerRequest();
+ void sendMapBlockRequest(U16 min_x, U16 min_y, U16 max_x, U16 max_y, bool return_nonexistent = false);
+ void sendNamedRegionRequest(std::string region_name);
+ void sendItemRequest(U32 type, U64 handle = 0);
+
+ static void processMapLayerReply(LLMessageSystem*, void**);
+ static void processMapBlockReply(LLMessageSystem*, void**);
+ static void processMapItemReply(LLMessageSystem*, void**);
+
+ void dump();
+
+ // Extend the bounding box of the list of simulators. Returns true
+ // if the extents changed.
+ BOOL extendAABB(U32 x_min, U32 y_min, U32 x_max, U32 y_max);
+
+ // build coverage maps for telehub region visualization
+ void updateTelehubCoverage();
+ BOOL coveredByTelehub(LLSimInfo* infop);
+
+ // Bounds of the world, in meters
+ U32 getWorldWidth() const;
+ U32 getWorldHeight() const;
+public:
+ // Map from region-handle to simulator info
+ typedef std::map<U64, LLSimInfo*> sim_info_map_t;
+ sim_info_map_t mSimInfoMap;
+
+ BOOL mIsTrackingUnknownLocation, mInvalidLocation, mIsTrackingDoubleClick, mIsTrackingCommit;
+ LLVector3d mUnknownLocation;
+
+ bool mRequestLandForSale;
+
+ typedef std::vector<LLItemInfo> item_info_list_t;
+ item_info_list_t mTelehubs;
+ item_info_list_t mInfohubs;
+ item_info_list_t mPGEvents;
+ item_info_list_t mMatureEvents;
+ item_info_list_t mPopular;
+ item_info_list_t mLandForSale;
+ item_info_list_t mClassifieds;
+
+ std::map<U64,S32> mNumAgents;
+
+ typedef std::map<U64, item_info_list_t> agent_list_map_t;
+ agent_list_map_t mAgentLocationsMap;
+
+ std::vector<LLWorldMapLayer> mMapLayers[MAP_SIM_IMAGE_TYPES];
+ BOOL mMapLoaded[MAP_SIM_IMAGE_TYPES];
+ BOOL * mMapBlockLoaded[MAP_SIM_IMAGE_TYPES];
+ S32 mCurrentMap;
+
+ // AABB of the list of simulators
+ U32 mMinX;
+ U32 mMaxX;
+ U32 mMinY;
+ U32 mMaxY;
+
+ U8* mNeighborMap;
+ U8* mTelehubCoverageMap;
+ S32 mNeighborMapWidth;
+ S32 mNeighborMapHeight;
+
+private:
+ LLTimer mRequestTimer;
+};
+
+extern LLWorldMap* gWorldMap;
+
+#endif
diff --git a/indra/newview/llworldmapview.cpp b/indra/newview/llworldmapview.cpp
new file mode 100644
index 0000000000..56e496e303
--- /dev/null
+++ b/indra/newview/llworldmapview.cpp
@@ -0,0 +1,1940 @@
+/**
+ * @file llworldmapview.cpp
+ * @brief LLWorldMapView class implementation
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llworldmapview.h"
+
+#include "indra_constants.h"
+#include "llui.h"
+#include "linked_lists.h"
+#include "llmath.h" // clampf()
+#include "llregionhandle.h"
+#include "lleventflags.h"
+
+#include "llagent.h"
+#include "llcallingcard.h"
+#include "llcolorscheme.h"
+#include "llviewercontrol.h"
+#include "llcylinder.h"
+#include "llfloaterdirectory.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "lltextbox.h"
+#include "lltextureview.h"
+#include "lltracker.h"
+#include "llviewercamera.h"
+#include "llviewerimage.h"
+#include "llviewerimagelist.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerwindow.h"
+#include "llworld.h"
+#include "llworldmap.h"
+#include "viewer.h" // Only for constants!
+
+#include "llglheaders.h"
+
+const F32 GODLY_TELEPORT_HEIGHT = 200.f;
+const S32 SCROLL_HINT_WIDTH = 65;
+const F32 BIG_DOT_RADIUS = 5.f;
+BOOL LLWorldMapView::sHandledLastClick = FALSE;
+
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarYouSmallImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarSmallImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sAvatarLargeImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sTelehubImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sInfohubImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sHomeImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sEventImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sEventMatureImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sTrackCircleImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sTrackArrowImage = NULL;
+
+LLPointer<LLViewerImage> LLWorldMapView::sClassifiedsImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sPopularImage = NULL;
+LLPointer<LLViewerImage> LLWorldMapView::sForSaleImage = NULL;
+
+F32 LLWorldMapView::sThresholdA = 48.f;
+F32 LLWorldMapView::sThresholdB = 96.f;
+F32 LLWorldMapView::sPanX = 0.f;
+F32 LLWorldMapView::sPanY = 0.f;
+F32 LLWorldMapView::sTargetPanX = 0.f;
+F32 LLWorldMapView::sTargetPanY = 0.f;
+S32 LLWorldMapView::sTrackingArrowX = 0;
+S32 LLWorldMapView::sTrackingArrowY = 0;
+F32 LLWorldMapView::sPixelsPerMeter = 1.f;
+F32 CONE_SIZE = 0.6f;
+
+void LLWorldMapView::initClass()
+{
+ LLUUID image_id;
+
+ image_id.set( gViewerArt.getString("map_avatar_you_8.tga") );
+ sAvatarYouSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_avatar_8.tga") );
+ sAvatarSmallImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_avatar_16.tga") );
+ sAvatarLargeImage = gImageList.getImage( image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_home.tga") );
+ sHomeImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_telehub.tga") );
+ sTelehubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_infohub.tga") );
+ sInfohubImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_event.tga") );
+ sEventImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_event_mature.tga") );
+ sEventMatureImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("map_track_16.tga") );
+ sTrackCircleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("direction_arrow.tga") );
+ sTrackArrowImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+ // Make sure tracker arrow doesn't wrap
+ sTrackArrowImage->bindTexture(0);
+ sTrackArrowImage->setClamp(TRUE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_top_pick.tga") );
+ sClassifiedsImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_popular.tga") );
+ sPopularImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+
+ image_id.set( gViewerArt.getString("icon_for_sale.tga") );
+ sForSaleImage = gImageList.getImage(image_id, MIPMAP_FALSE, TRUE);
+}
+
+// static
+void LLWorldMapView::cleanupClass()
+{
+ sAvatarYouSmallImage = NULL;
+ sAvatarSmallImage = NULL;
+ sAvatarLargeImage = NULL;
+ sTelehubImage = NULL;
+ sInfohubImage = NULL;
+ sHomeImage = NULL;
+ sEventImage = NULL;
+ sEventMatureImage = NULL;
+ sTrackCircleImage = NULL;
+ sTrackArrowImage = NULL;
+ sClassifiedsImage = NULL;
+ sPopularImage = NULL;
+ sForSaleImage = NULL;
+}
+
+LLWorldMapView::LLWorldMapView(const std::string& name, const LLRect& rect )
+: LLPanel(name, rect, BORDER_NO),
+ mBackgroundColor( LLColor4( 4.f/255.f, 4.f/255.f, 75.f/255.f, 1.f ) ),
+ mItemPicked(FALSE),
+ mPanning( FALSE ),
+ mMouseDownPanX( 0 ),
+ mMouseDownPanY( 0 ),
+ mMouseDownX( 0 ),
+ mMouseDownY( 0 ),
+ mSelectIDStart(0),
+ mAgentCountsUpdateTime(0)
+{
+ sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ clearLastClick();
+
+ const S32 DIR_WIDTH = 10;
+ const S32 DIR_HEIGHT = 10;
+ LLRect major_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH, 0 );
+
+ mTextBoxNorth = new LLTextBox( "N", major_dir_rect );
+ mTextBoxNorth->setDropshadowVisible( TRUE );
+ addChild( mTextBoxNorth );
+
+ LLColor4 minor_color( 1.f, 1.f, 1.f, .7f );
+
+ mTextBoxEast = new LLTextBox( "E", major_dir_rect );
+ mTextBoxEast->setColor( minor_color );
+ addChild( mTextBoxEast );
+
+ mTextBoxWest = new LLTextBox( "W", major_dir_rect );
+ mTextBoxWest->setColor( minor_color );
+ addChild( mTextBoxWest );
+
+ mTextBoxSouth = new LLTextBox( "S", major_dir_rect );
+ mTextBoxSouth->setColor( minor_color );
+ addChild( mTextBoxSouth );
+
+ LLRect minor_dir_rect( 0, DIR_HEIGHT, DIR_WIDTH * 2, 0 );
+
+ mTextBoxSouthEast = new LLTextBox( "SE", minor_dir_rect );
+ mTextBoxSouthEast->setColor( minor_color );
+ addChild( mTextBoxSouthEast );
+
+ mTextBoxNorthEast = new LLTextBox( "NE", minor_dir_rect );
+ mTextBoxNorthEast->setColor( minor_color );
+ addChild( mTextBoxNorthEast );
+
+ mTextBoxSouthWest = new LLTextBox( "SW", minor_dir_rect );
+ mTextBoxSouthWest->setColor( minor_color );
+ addChild( mTextBoxSouthWest );
+
+ mTextBoxNorthWest = new LLTextBox( "NW", minor_dir_rect );
+ mTextBoxNorthWest->setColor( minor_color );
+ addChild( mTextBoxNorthWest );
+}
+
+
+LLWorldMapView::~LLWorldMapView()
+{
+ cleanupTextures();
+}
+
+
+// static
+void LLWorldMapView::cleanupTextures()
+{
+}
+
+
+// static
+void LLWorldMapView::setScale( F32 scale )
+{
+ if (scale != gMapScale)
+ {
+ F32 old_scale = gMapScale;
+
+ gMapScale = scale;
+ if (gMapScale == 0.f)
+ {
+ gMapScale = 0.1f;
+ }
+
+ F32 ratio = (scale / old_scale);
+ sPanX *= ratio;
+ sPanY *= ratio;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+
+ sPixelsPerMeter = gMapScale / REGION_WIDTH_METERS;
+ }
+}
+
+
+// static
+void LLWorldMapView::translatePan( S32 delta_x, S32 delta_y )
+{
+ sPanX += delta_x;
+ sPanY += delta_y;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+}
+
+
+// static
+void LLWorldMapView::setPan( S32 x, S32 y, BOOL snap )
+{
+ sTargetPanX = (F32)x;
+ sTargetPanY = (F32)y;
+ if (snap)
+ {
+ sPanX = sTargetPanX;
+ sPanY = sTargetPanY;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////
+
+// dumb helper function
+BOOL is_agent_in_region(LLViewerRegion* region, LLSimInfo* info)
+{
+ if((region && info)
+ && (info->mName == region->getName()))
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLWorldMapView::draw()
+{
+ if (!getVisible() || !gWorldPointer)
+ {
+ return;
+ }
+
+ LLTextureView::clearDebugImages();
+
+ F64 current_time = LLTimer::getElapsedSeconds();
+
+ if (current_time - mAgentCountsUpdateTime > AGENT_COUNTS_UPDATE_TIME)
+ {
+ gWorldMap->sendItemRequest(MAP_ITEM_AGENT_COUNT);
+ mAgentCountsUpdateTime = current_time;
+ }
+
+ mVisibleRegions.clear();
+
+ // animate pan if necessary
+ sPanX = lerp(sPanX, sTargetPanX, LLCriticalDamp::getInterpolant(0.1f));
+ sPanY = lerp(sPanY, sTargetPanY, LLCriticalDamp::getInterpolant(0.1f));
+
+ LLVector3d pos_global;
+ LLVector3 pos_map;
+
+ const S32 width = mRect.getWidth();
+ const S32 height = mRect.getHeight();
+ const S32 half_width = width / 2;
+ const S32 half_height = height / 2;
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ LLGLEnable scissor_test(GL_SCISSOR_TEST);
+ LLUI::setScissorRegionLocal(LLRect(0, height, width, 0));
+ {
+ LLGLSNoTexture no_texture;
+
+
+ glMatrixMode(GL_MODELVIEW);
+
+ // Clear the background alpha to 0
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glAlphaFunc(GL_GEQUAL, 0.00f);
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
+ gl_rect_2d(0, height, width, 0);
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ F32 layer_alpha = 1.f;
+
+ // Draw one image per layer
+ for (U32 layer_idx=0; layer_idx<gWorldMap->mMapLayers[gWorldMap->mCurrentMap].size(); ++layer_idx)
+ {
+ if (!gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx].LayerDefined)
+ {
+ continue;
+ }
+ LLWorldMapLayer *layer = &gWorldMap->mMapLayers[gWorldMap->mCurrentMap][layer_idx];
+ LLViewerImage *current_image = layer->LayerImage;
+#if 1 || LL_RELEASE_FOR_DOWNLOAD
+ if (current_image->isMissingAsset())
+ {
+ continue; // better to draw nothing than the missing asset image
+ }
+#endif
+
+ LLVector3d origin_global((F64)layer->LayerExtents.mLeft * REGION_WIDTH_METERS, (F64)layer->LayerExtents.mBottom * REGION_WIDTH_METERS, 0.f);
+
+ // Find x and y position relative to camera's center.
+ LLVector3d rel_region_pos = origin_global - camera_global;
+ S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale);
+ S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale);
+
+ F32 pix_width = gMapScale*(layer->LayerExtents.getWidth() + 1);
+ F32 pix_height = gMapScale*(layer->LayerExtents.getHeight() + 1);
+
+ // When the view isn't panned, 0,0 = center of rectangle
+ F32 bottom = sPanY + half_height + relative_y;
+ F32 left = sPanX + half_width + relative_x;
+ F32 top = bottom + pix_height;
+ F32 right = left + pix_width;
+ F32 pixel_area = pix_width*pix_height;
+ // discard layers that are outside the rectangle
+ // and discard small layers
+ if (top < 0.f ||
+ bottom > height ||
+ right < 0.f ||
+ left > width ||
+ (pixel_area < 4*4))
+ {
+ current_image->setBoostLevel(0);
+ continue;
+ }
+
+ current_image->setBoostLevel(LLViewerImage::BOOST_MAP_LAYER);
+ current_image->setKnownDrawSize(llround(pix_width), llround(pix_height));
+
+#if 1 || LL_RELEASE_FOR_DOWNLOAD
+ if (!current_image->getHasGLTexture())
+ {
+ continue; // better to draw nothing than the default image
+ }
+#endif
+
+ LLTextureView::addDebugImage(current_image);
+
+ // Draw using the texture. If we don't clamp we get artifact at
+ // the edge.
+ LLViewerImage::bindTexture(current_image);
+
+ // Draw map image into RGB
+ //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMask(TRUE, TRUE, TRUE, FALSE);
+ glColor4f(1.f, 1.f, 1.f, layer_alpha);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex3f(left, top, -1.0f);
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex3f(left, bottom, -1.0f);
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex3f(right, bottom, -1.0f);
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex3f(right, top, -1.0f);
+ glEnd();
+
+ // draw an alpha of 1 where the sims are visible
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.0f, 1.0f);
+ glVertex2f(left, top);
+ glTexCoord2f(0.0f, 0.0f);
+ glVertex2f(left, bottom);
+ glTexCoord2f(1.0f, 0.0f);
+ glVertex2f(right, bottom);
+ glTexCoord2f(1.0f, 1.0f);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+
+#if 1
+ F32 sim_alpha = 1.f;
+
+ // Draw one image per region, centered on the camera position.
+ for (LLWorldMap::sim_info_map_t::iterator it = gWorldMap->mSimInfoMap.begin();
+ it != gWorldMap->mSimInfoMap.end(); ++it)
+ {
+ U64 handle = (*it).first;
+ LLSimInfo* info = (*it).second;
+
+ if (info->mCurrentImage.isNull())
+ {
+ info->mCurrentImage = gImageList.getImage(info->mMapImageID[gWorldMap->mCurrentMap], MIPMAP_TRUE, FALSE);
+ }
+ if (info->mOverlayImage.isNull() && info->mMapImageID[2].notNull())
+ {
+ info->mOverlayImage = gImageList.getImage(info->mMapImageID[2], MIPMAP_TRUE, FALSE);
+ info->mOverlayImage->bind(0);
+ info->mOverlayImage->setClamp(TRUE, TRUE);
+ }
+
+ LLViewerImage* simimage = info->mCurrentImage;
+ LLViewerImage* overlayimage = info->mOverlayImage;
+
+ if (gMapScale < 8.f)
+ {
+ simimage->setBoostLevel(0);
+ if (overlayimage) overlayimage->setBoostLevel(0);
+ continue;
+ }
+
+ LLVector3d origin_global = from_region_handle(handle);
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ // Find x and y position relative to camera's center.
+ LLVector3d rel_region_pos = origin_global - camera_global;
+ S32 relative_x = lltrunc((rel_region_pos.mdV[0] / REGION_WIDTH_METERS) * gMapScale);
+ S32 relative_y = lltrunc((rel_region_pos.mdV[1] / REGION_WIDTH_METERS) * gMapScale);
+
+ // When the view isn't panned, 0,0 = center of rectangle
+ F32 bottom = sPanY + half_height + relative_y;
+ F32 left = sPanX + half_width + relative_x;
+ F32 top = bottom + gMapScale ;
+ F32 right = left + gMapScale ;
+
+ // Switch to world map texture (if available for this region) if either:
+ // 1. Tiles are zoomed out small enough, or
+ // 2. Sim's texture has not been loaded yet
+ F32 map_scale_cutoff = SIM_MAP_SCALE;
+ if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) > 0)
+ {
+ map_scale_cutoff = SIM_NULL_MAP_SCALE;
+ }
+
+ info->mShowAgentLocations = (gMapScale >= SIM_MAP_AGENT_SCALE);
+
+ bool sim_visible =
+ (gMapScale >= map_scale_cutoff) &&
+ (simimage->getHasGLTexture());
+
+ if (sim_visible)
+ {
+ // Fade in
+ if (info->mAlpha < 0.0f)
+ info->mAlpha = 1.f; // don't fade initially
+ else
+ info->mAlpha = lerp(info->mAlpha, 1.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+ else
+ {
+ // Fade out
+ if (info->mAlpha < 0.0f)
+ info->mAlpha = 0.f; // don't fade initially
+ else
+ info->mAlpha = lerp(info->mAlpha, 0.f, LLCriticalDamp::getInterpolant(0.15f));
+ }
+
+ // discard regions that are outside the rectangle
+ // and discard small regions
+ if (top < 0.f ||
+ bottom > height ||
+ right < 0.f ||
+ left > width )
+ {
+ simimage->setBoostLevel(0);
+ if (overlayimage) overlayimage->setBoostLevel(0);
+ continue;
+ }
+
+ mVisibleRegions.push_back(handle);
+ // See if the agents need updating
+ if (current_time - info->mAgentsUpdateTime > AGENTS_UPDATE_TIME)
+ {
+ gWorldMap->sendItemRequest(MAP_ITEM_AGENT_LOCATIONS, info->mHandle);
+ info->mAgentsUpdateTime = current_time;
+ }
+
+ // Bias the priority escalation for images nearer
+ LLVector3d center_global = origin_global;
+ center_global.mdV[VX] += 128.0;
+ center_global.mdV[VY] += 128.0;
+
+ S32 draw_size = llround(gMapScale);
+ simimage->setBoostLevel(LLViewerImage::BOOST_MAP);
+ simimage->setKnownDrawSize(draw_size, draw_size);
+
+ if (overlayimage)
+ {
+ overlayimage->setBoostLevel(LLViewerImage::BOOST_MAP);
+ overlayimage->setKnownDrawSize(draw_size, draw_size);
+ }
+
+ LLTextureView::addDebugImage(simimage);
+
+ if (sim_visible && info->mAlpha > 0.001f)
+ {
+ // Draw using the texture. If we don't clamp we get artifact at
+ // the edge.
+ LLGLSUIDefault gls_ui;
+ LLViewerImage::bindTexture(simimage);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ F32 alpha = sim_alpha * info->mAlpha;
+ glColor4f(1.f, 1.0f, 1.0f, alpha);
+
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex3f(left, top, 0.f);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3f(left, bottom, 0.f);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3f(right, bottom, 0.f);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3f(right, top, 0.f);
+ glEnd();
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale") && overlayimage && overlayimage->getHasGLTexture())
+ {
+ LLViewerImage::bindTexture(overlayimage);
+ glColor4f(1.f, 1.f, 1.f, alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(0.f, 1.f);
+ glVertex3f(left, top, -0.5f);
+ glTexCoord2f(0.f, 0.f);
+ glVertex3f(left, bottom, -0.5f);
+ glTexCoord2f(1.f, 0.f);
+ glVertex3f(right, bottom, -0.5f);
+ glTexCoord2f(1.f, 1.f);
+ glVertex3f(right, top, -0.5f);
+ glEnd();
+ }
+
+ if ((info->mRegionFlags & REGION_FLAGS_NULL_LAYER) == 0)
+ {
+ // draw an alpha of 1 where the sims are visible (except NULL sims)
+ glBlendFunc(GL_ONE, GL_ZERO);
+ glColorMask(FALSE, FALSE, FALSE, TRUE);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_QUADS);
+ glVertex2f(left, top);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glVertex2f(right, top);
+ glEnd();
+
+ glColorMask(TRUE, TRUE, TRUE, TRUE);
+ }
+ }
+
+ if (info->mAccess == SIM_ACCESS_DOWN)
+ {
+ // Draw a transparent red square over down sims
+ glBlendFunc(GL_DST_ALPHA, GL_SRC_ALPHA);
+ glColor4f(0.2f, 0.0f, 0.0f, 0.4f);
+
+ LLGLSNoTexture gls_no_texture;
+ glBegin(GL_QUADS);
+ glVertex2f(left, top);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ // If this is mature, and you are not, draw a line across it
+ if (info->mAccess != SIM_ACCESS_DOWN && info->mAccess > gAgent.mAccess)
+ {
+ glBlendFunc(GL_DST_ALPHA, GL_ZERO);
+
+ LLGLSNoTexture gls_no_texture;
+ glColor3f(1.f, 0.f, 0.f);
+ glBegin(GL_LINES);
+ glVertex2f(left, top);
+ glVertex2f(right, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, top);
+ glEnd();
+ }
+
+ // Draw the region name in the lower left corner
+ LLFontGL* font = LLFontGL::sSansSerifSmall;
+
+ char mesg[MAX_STRING];
+ if (gMapScale < sThresholdA)
+ {
+ mesg[0] = '\0';
+ }
+ else if (gMapScale < sThresholdB)
+ {
+ //sprintf(mesg, "%d", info->mAgents);
+ mesg[0] = '\0';
+ }
+ else
+ {
+ //sprintf(mesg, "%d / %s (%s)",
+ // info->mAgents,
+ // info->mName.c_str(),
+ // LLViewerRegion::accessToShortString(info->mAccess) );
+ if (info->mAccess == SIM_ACCESS_DOWN)
+ {
+ sprintf(mesg, "%s (Offline)", info->mName.c_str());
+ }
+ else
+ {
+ sprintf(mesg, "%s", info->mName.c_str());
+ }
+ }
+
+ if (mesg[0] != '\0')
+ {
+ font->renderUTF8(
+ mesg, 0,
+ llfloor(left + 3),
+ llfloor(bottom + 2),
+ LLColor4::white,
+ LLFontGL::LEFT,
+ LLFontGL::BASELINE,
+ LLFontGL::DROP_SHADOW);
+ }
+ }
+#endif
+
+
+#if 1
+ // Draw background rectangle
+ LLGLSUIDefault gls_ui;
+ {
+ LLGLSNoTexture gls_no_texture;
+ glAlphaFunc(GL_GEQUAL, 0.0f);
+ glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA);
+ glColor4fv( mBackgroundColor.mV );
+ gl_rect_2d(0, height, width, 0);
+ }
+
+ glAlphaFunc(GL_GEQUAL, 0.01f);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ // Infohubs
+ // Draw under avatar so you can find yourself.
+ if (gSavedSettings.getBOOL("MapShowInfohubs")) //(gMapScale >= sThresholdB)
+ {
+ S32 infohub_icon_size = (S32)sInfohubImage->getWidth();
+ for (U32 infohub=0; infohub<gWorldMap->mInfohubs.size(); ++infohub)
+ {
+ LLItemInfo *infohub_info = &gWorldMap->mInfohubs[infohub];
+
+ pos_map = globalPosToView(infohub_info->mPosGlobal);
+ gl_draw_image(llround(pos_map.mV[VX])-infohub_icon_size/2,
+ llround(pos_map.mV[VY])-infohub_icon_size/2,
+ sInfohubImage,
+ LLColor4::white);
+ }
+ }
+
+ // Telehubs
+ // Draw under avatar so you can find yourself.
+ if (gSavedSettings.getBOOL("MapShowTelehubs")) //(gMapScale >= sThresholdB)
+ {
+ S32 telehub_icon_size = (S32)sTelehubImage->getWidth();
+ for (U32 telehub=0; telehub<gWorldMap->mTelehubs.size(); ++telehub)
+ {
+ LLItemInfo *telehub_info = &gWorldMap->mTelehubs[telehub];
+
+ pos_map = globalPosToView(telehub_info->mPosGlobal);
+ gl_draw_image(llround(pos_map.mV[VX])-telehub_icon_size/2,
+ llround(pos_map.mV[VY])-telehub_icon_size/2,
+ sTelehubImage,
+ LLColor4::white);
+ }
+ }
+
+ // Home
+ // Always Draw
+ if (1) //(gMapScale >= sThresholdB)
+ {
+ S32 home_icon_size = (S32)sHomeImage->getWidth();
+ if (gAgent.getHomePosGlobal(&pos_global))
+ {
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX])-home_icon_size/2,
+ llround(pos_map.mV[VY])-home_icon_size/2,
+ sHomeImage,
+ LLColor4::white);
+ }
+ }
+
+ // Draw all these under the avatar, so you can find yourself
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
+ {
+ drawGenericItems(gWorldMap->mLandForSale, sForSaleImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowClassifieds"))
+ {
+ drawGenericItems(gWorldMap->mClassifieds, sClassifiedsImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowPopular"))
+ {
+ drawGenericItems(gWorldMap->mPopular, sPopularImage);
+ }
+
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ drawEvents();
+ }
+
+ // Your avatar.
+ // Draw avatar position, always, and underneath other avatars
+ pos_global = gAgent.getPositionGlobal();
+ pos_map = globalPosToView(pos_global);
+ gl_draw_image(llround(pos_map.mV[VX])-8,
+ llround(pos_map.mV[VY])-8,
+ sAvatarLargeImage,
+ LLColor4::white);
+
+ if (!pointInView(llround(pos_map.mV[VX]), llround(pos_map.mV[VY])))
+ {
+ drawTracking(pos_global,
+ lerp(LLColor4::yellow, LLColor4::orange, 0.4f),
+ TRUE,
+ "You are here",
+ "",
+ llround(LLFontGL::sSansSerifSmall->getLineHeight())); // offset vertically by one line, to avoid overlap with target tracking
+ }
+
+ drawFrustum();
+
+ // Draw icons for the avatars in each region.
+ // Draw after avatar so you can see nearby people.
+ if (gSavedSettings.getBOOL("MapShowPeople"))
+ {
+ drawAgents();
+ }
+
+ //-----------------------------------------------------------------------
+
+ // Always draw tracking information
+ LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
+ if ( LLTracker::TRACKING_AVATAR == tracking_status )
+ {
+ drawTracking( LLAvatarTracker::instance().getGlobalPos(), gTrackColor, TRUE, LLTracker::getLabel(), "" );
+ }
+ else if ( LLTracker::TRACKING_LANDMARK == tracking_status
+ || LLTracker::TRACKING_LOCATION == tracking_status )
+ {
+ // While fetching landmarks, will have 0,0,0 location for a while,
+ // so don't draw. JC
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (!pos_global.isExactlyZero())
+ {
+ drawTracking( pos_global, gTrackColor, TRUE, LLTracker::getLabel(), LLTracker::getToolTip() );
+ }
+ }
+ else if (gWorldMap->mIsTrackingUnknownLocation)
+ {
+ if (gWorldMap->mInvalidLocation)
+ {
+ // We know this location to be invalid
+ LLColor4 loading_color(0.0, 0.5, 1.0, 1.0);
+ drawTracking( gWorldMap->mUnknownLocation, loading_color, TRUE, "Invalid Location", "");
+ }
+ else
+ {
+ double value = fmod(current_time, 2);
+ value = 0.5 + 0.5*cos(value * 3.14159f);
+ LLColor4 loading_color(0.0, F32(value/2), F32(value), 1.0);
+ drawTracking( gWorldMap->mUnknownLocation, loading_color, TRUE, "Loading...", "");
+ }
+ }
+#endif
+
+ // turn off the scissor
+ LLGLDisable no_scissor(GL_SCISSOR_TEST);
+
+ updateDirections();
+
+ LLView::draw();
+
+ updateVisibleBlocks();
+}
+
+void LLWorldMapView::drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image)
+{
+ LLWorldMap::item_info_list_t::const_iterator e;
+ for (e = items.begin(); e != items.end(); ++e)
+ {
+ const LLItemInfo& info = *e;
+
+ drawGenericItem(info, image);
+ }
+}
+
+void LLWorldMapView::drawGenericItem(const LLItemInfo& item, LLPointer<LLViewerImage> image)
+{
+ S32 half_width = image->getWidth()/2;
+ S32 half_height = image->getHeight()/2;
+
+ LLVector3 pos_map = globalPosToView( item.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - half_width,
+ llround(pos_map.mV[VY]) - half_height,
+ image,
+ LLColor4::white);
+}
+
+void LLWorldMapView::drawAgents()
+{
+ //S32 half_width = sPopularImage->getWidth()/2;
+ //S32 half_height = sPopularImage->getHeight()/2;
+
+ F32 agents_scale = (gMapScale * 0.9f) / 256.f;
+
+ for (handle_list_t::iterator iter = mVisibleRegions.begin(); iter != mVisibleRegions.end(); ++iter)
+ {
+ U64 handle = *iter;
+ LLSimInfo* siminfo = gWorldMap->simInfoFromHandle(handle);
+ if (siminfo && (siminfo->mAccess == SIM_ACCESS_DOWN))
+ {
+ continue;
+ }
+ LLWorldMap::agent_list_map_t::iterator countsiter = gWorldMap->mAgentLocationsMap.find(handle);
+ if (siminfo && siminfo->mShowAgentLocations && countsiter != gWorldMap->mAgentLocationsMap.end())
+ {
+ // Show Individual agents
+ LLWorldMap::item_info_list_t& agentcounts = countsiter->second;
+ S32 sim_agent_count = 0;
+ for (LLWorldMap::item_info_list_t::iterator iter = agentcounts.begin();
+ iter != agentcounts.end(); ++iter)
+ {
+ const LLItemInfo& info = *iter;
+ LLVector3 pos_map = globalPosToView( info.mPosGlobal );
+ S32 agent_count = info.mExtra;
+ if (agent_count > 0)
+ {
+ sim_agent_count += agent_count;
+ F32 y = 0;
+ for (S32 cur_agent = 0; cur_agent < agent_count; cur_agent++)
+ {
+ gl_draw_image(llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY] + y) - 4,
+ sAvatarSmallImage,
+ LLColor4::white );
+ // move up a bit
+ y += 3.f;
+ }
+ }
+ }
+ gWorldMap->mNumAgents[handle] = sim_agent_count; // override mNumAgents for this sim
+ }
+ else
+ {
+ // Show agent 'stack' at center of sim
+ S32 num_agents = gWorldMap->mNumAgents[handle];
+ if (num_agents > 0)
+ {
+ LLVector3d region_pos = from_region_handle(handle);
+ region_pos[VX] += REGION_WIDTH_METERS * .5f;
+ region_pos[VY] += REGION_WIDTH_METERS * .5f;
+ LLVector3 pos_map = globalPosToView(region_pos);
+ // Reduce the stack size as you zoom out - always display at lease one agent where there is one or more
+ S32 agent_count = (S32)(((num_agents-1) * agents_scale + (num_agents-1) * 0.1f)+.1f) + 1;
+ S32 y = 0;
+ S32 cur_agent;
+ for (cur_agent = 0; cur_agent < agent_count; cur_agent++)
+ {
+ gl_draw_image(
+ llround(pos_map.mV[VX]) - 4,
+ llround(pos_map.mV[VY]) + y - 4,
+ sAvatarSmallImage,
+ LLColor4::white );
+ // move up a bit
+ y += 3;
+ }
+ }
+ }
+ }
+}
+
+
+void LLWorldMapView::drawEvents()
+{
+ BOOL show_mature = gSavedSettings.getBOOL("ShowMatureEvents");
+
+ // Non-selected events
+ // Draw under avatar so you can find yourself.
+ LLWorldMap::item_info_list_t::const_iterator e;
+ for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (!event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventImage->getHeight()/2,
+ sEventImage,
+ LLColor4::white);
+ }
+ }
+ if (show_mature)
+ {
+ for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (!event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2,
+ sEventMatureImage,
+ LLColor4::white);
+ }
+ }
+ }
+
+ // Selected events
+ // Draw under avatar so you can find yourself.
+ for (e = gWorldMap->mPGEvents.begin(); e != gWorldMap->mPGEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventImage->getHeight()/2,
+ sEventImage,
+ LLColor4::white);
+
+ //drawIconName(
+ // pos_map.mV[VX],
+ // pos_map.mV[VY],
+ // gEventColor,
+ // event.mToolTip.c_str(),
+ // event.mName.c_str() );
+ }
+ }
+ if (show_mature)
+ {
+ for (e = gWorldMap->mMatureEvents.begin(); e != gWorldMap->mMatureEvents.end(); ++e)
+ {
+ const LLItemInfo& event = *e;
+
+ // Draw, but without relative-Z icons
+ if (event.mSelected)
+ {
+ LLVector3 pos_map = globalPosToView( event.mPosGlobal );
+ gl_draw_image(llround(pos_map.mV[VX]) - sEventMatureImage->getWidth()/2,
+ llround(pos_map.mV[VY]) - sEventMatureImage->getHeight()/2,
+ sEventMatureImage,
+ LLColor4::white);
+
+ //drawIconName(
+ // pos_map.mV[VX],
+ // pos_map.mV[VY],
+ // gEventColor,
+ // event.mToolTip.c_str(),
+ // event.mName.c_str() );
+ }
+ }
+ }
+}
+
+void LLWorldMapView::drawDots()
+{
+
+}
+
+
+void LLWorldMapView::drawFrustum()
+{
+ // Draw frustum
+ F32 meters_to_pixels = gMapScale/ REGION_WIDTH_METERS;
+
+ F32 horiz_fov = gCamera->getView() * gCamera->getAspect();
+ F32 far_clip_meters = gCamera->getFar();
+ F32 far_clip_pixels = far_clip_meters * meters_to_pixels;
+
+ F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
+ F32 half_width_pixels = half_width_meters * meters_to_pixels;
+
+ F32 ctr_x = mRect.getWidth() * 0.5f + sPanX;
+ F32 ctr_y = mRect.getHeight() * 0.5f + sPanY;
+
+ LLGLSNoTexture gls_no_texture;
+
+ // Since we don't rotate the map, we have to rotate the frustum.
+ glPushMatrix();
+ glTranslatef( ctr_x, ctr_y, 0 );
+ glRotatef( atan2( gCamera->getAtAxis().mV[VX], gCamera->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
+
+ // Draw triangle with more alpha in far pixels to make it
+ // fade out in distance.
+ glBegin( GL_TRIANGLES );
+ glColor4f(1.f, 1.f, 1.f, 0.25f);
+ glVertex2f( 0, 0 );
+
+ glColor4f(1.f, 1.f, 1.f, 0.02f);
+ glVertex2f( -half_width_pixels, far_clip_pixels );
+
+ glColor4f(1.f, 1.f, 1.f, 0.02f);
+ glVertex2f( half_width_pixels, far_clip_pixels );
+ glEnd();
+ glPopMatrix();
+}
+
+
+LLVector3 LLWorldMapView::globalPosToView( const LLVector3d& global_pos )
+{
+ LLVector3d relative_pos_global = global_pos - gAgent.getCameraPositionGlobal();
+ LLVector3 pos_local;
+ pos_local.setVec(relative_pos_global); // convert to floats from doubles
+
+ pos_local.mV[VX] *= sPixelsPerMeter;
+ pos_local.mV[VY] *= sPixelsPerMeter;
+ // leave Z component in meters
+
+
+ pos_local.mV[VX] += mRect.getWidth() / 2 + sPanX;
+ pos_local.mV[VY] += mRect.getHeight() / 2 + sPanY;
+
+ return pos_local;
+}
+
+
+void LLWorldMapView::drawTracking(const LLVector3d& pos_global, const LLColor4& color,
+ BOOL draw_arrow, LLString label, LLString tooltip, S32 vert_offset )
+{
+ LLVector3 pos_local = globalPosToView( pos_global );
+ S32 x = llround( pos_local.mV[VX] );
+ S32 y = llround( pos_local.mV[VY] );
+ LLFontGL* font = LLFontGL::sSansSerifSmall;
+ S32 text_x = x;
+ S32 text_y = (S32)(y - sTrackCircleImage->getHeight()/2 - font->getLineHeight());
+
+ BOOL is_in_window = true;
+
+ if( x < 0
+ || y < 0
+ || x >= mRect.getWidth()
+ || y >= mRect.getHeight() )
+ {
+ if (draw_arrow)
+ {
+ drawTrackingCircle( mRect, x, y, color, 3, 15 );
+ drawTrackingArrow( mRect, x, y, color );
+ text_x = sTrackingArrowX;
+ text_y = sTrackingArrowY;
+ }
+ is_in_window = false;
+ }
+ else if (LLTracker::getTrackingStatus() == LLTracker::TRACKING_LOCATION &&
+ LLTracker::getTrackedLocationType() != LLTracker::LOCATION_NOTHING)
+ {
+ drawTrackingCircle( mRect, x, y, color, 3, 15 );
+ }
+ else
+ {
+ // Draw, but without relative Z
+ gl_draw_image(x - sTrackCircleImage->getWidth()/2,
+ y - sTrackCircleImage->getHeight()/2,
+ sTrackCircleImage,
+ color);
+ }
+
+ // clamp text position to on-screen
+ 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, mRect.getWidth() - half_text_width - TEXT_PADDING);
+ text_y = llclamp(text_y + vert_offset, TEXT_PADDING + vert_offset, mRect.getHeight() - llround(font->getLineHeight()) - TEXT_PADDING - vert_offset);
+
+ if (label != "")
+ {
+ font->renderUTF8(
+ label, 0,
+ text_x,
+ text_y,
+ LLColor4::white, LLFontGL::HCENTER,
+ LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+
+ if (tooltip != "")
+ {
+ text_y -= (S32)font->getLineHeight();
+
+ font->renderUTF8(
+ tooltip, 0,
+ text_x,
+ text_y,
+ LLColor4::white, LLFontGL::HCENTER,
+ LLFontGL::BASELINE, LLFontGL::DROP_SHADOW);
+ }
+ }
+}
+
+// If you change this, then you need to change LLTracker::getTrackedPositionGlobal() as well
+LLVector3d LLWorldMapView::viewPosToGlobal( S32 x, S32 y )
+{
+ x -= llfloor((mRect.getWidth() / 2 + sPanX));
+ y -= llfloor((mRect.getHeight() / 2 + sPanY));
+
+ LLVector3 pos_local( (F32)x, (F32)y, 0.f );
+
+ pos_local *= ( REGION_WIDTH_METERS / gMapScale );
+
+ LLVector3d pos_global;
+ pos_global.setVec( pos_local );
+ pos_global += gAgent.getCameraPositionGlobal();
+ if(gAgent.isGodlike())
+ {
+ pos_global.mdV[VZ] = GODLY_TELEPORT_HEIGHT; // Godly height should always be 200.
+ }
+ else
+ {
+ pos_global.mdV[VZ] = gAgent.getPositionAgent().mV[VZ]; // Want agent's height, not camera's
+ }
+
+ return pos_global;
+}
+
+
+BOOL LLWorldMapView::handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen )
+{
+ if( !getVisible() || !pointInView( x, y ) )
+ {
+ return FALSE;
+ }
+
+ LLVector3d pos_global = viewPosToGlobal(x, y);
+
+ LLSimInfo* info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (info)
+ {
+ LLViewerRegion *region = gAgent.getRegion();
+
+ std::string message =
+ llformat("%s (%s)",
+ info->mName.c_str(),
+ LLViewerRegion::accessToString(info->mAccess));
+
+ if (info->mAccess != SIM_ACCESS_DOWN)
+ {
+ S32 agent_count = gWorldMap->mNumAgents[info->mHandle];
+ if (region && region->getHandle() == info->mHandle)
+ {
+ ++agent_count; // Bump by 1 if we're here
+ }
+
+ // We may not have an agent count when the map is really
+ // zoomed out, so don't display anything about the count. JC
+ if (agent_count > 0)
+ {
+ message += llformat("\n%d ", agent_count);
+
+ if (agent_count == 1)
+ {
+ message += "person";
+ }
+ else
+ {
+ message += "people";
+ }
+ }
+ }
+ msg.assign( message );
+
+ // Optionally show region flags
+ std::string region_flags = LLViewerRegion::regionFlagsToString(info->mRegionFlags);
+
+ if (!region_flags.empty())
+ {
+ msg += '\n';
+ msg += region_flags;
+ }
+
+ S32 SLOP = 4;
+ localPointToScreen(
+ x - SLOP, y - SLOP,
+ &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
+ sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP;
+ sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP;
+ }
+ return TRUE;
+}
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawAvatar(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z,
+ F32 dot_radius)
+{
+ F32 left = x_pixels - dot_radius;
+ F32 right = x_pixels + dot_radius;
+ F32 center = (left + right) * 0.5f;
+ F32 top = y_pixels + dot_radius;
+ F32 bottom = y_pixels - dot_radius;
+
+ const F32 HEIGHT_THRESHOLD = 7.f;
+
+ if (relative_z > HEIGHT_THRESHOLD)
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(left, top);
+ glVertex2f(right, top);
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+ else if (relative_z > -HEIGHT_THRESHOLD)
+ {
+ gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2,
+ llround(y_pixels) - sAvatarSmallImage->getHeight()/2,
+ sAvatarSmallImage,
+ color);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+}
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawTrackingDot( F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z,
+ F32 dot_radius)
+{
+ F32 left = x_pixels - dot_radius;
+ F32 right = x_pixels + dot_radius;
+ F32 center = (left + right) * 0.5f;
+ F32 top = y_pixels + dot_radius;
+ F32 bottom = y_pixels - dot_radius;
+
+ const F32 HEIGHT_THRESHOLD = 7.f;
+
+ if (relative_z > HEIGHT_THRESHOLD)
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(left, top);
+ glVertex2f(right, top);
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+ else if (relative_z > -HEIGHT_THRESHOLD)
+ {
+ gl_draw_image( llround(x_pixels) - sAvatarSmallImage->getWidth()/2,
+ llround(y_pixels) - sAvatarSmallImage->getHeight()/2,
+ sTrackCircleImage,
+ color);
+ }
+ else
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4fv( color.mV );
+ LLUI::setLineWidth(1.5f);
+ glBegin( GL_LINES );
+ glVertex2f(center, top);
+ glVertex2f(center, bottom);
+ glVertex2f(left, bottom);
+ glVertex2f(right, bottom);
+ glEnd();
+ LLUI::setLineWidth(1.0f);
+ }
+}
+
+
+// Pass relative Z of 0 to draw at same level.
+// static
+void LLWorldMapView::drawIconName(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ const std::string& first_line,
+ const std::string& second_line)
+{
+ const S32 VERT_PAD = 8;
+ S32 text_x = llround(x_pixels);
+ S32 text_y = llround(y_pixels
+ - BIG_DOT_RADIUS
+ - VERT_PAD);
+
+ // render text
+ LLFontGL::sSansSerif->renderUTF8(first_line, 0,
+ text_x,
+ text_y,
+ color,
+ LLFontGL::HCENTER,
+ LLFontGL::TOP,
+ LLFontGL::DROP_SHADOW);
+
+ text_y -= llround(LLFontGL::sSansSerif->getLineHeight());
+
+ // render text
+ LLFontGL::sSansSerif->renderUTF8(second_line, 0,
+ text_x,
+ text_y,
+ color,
+ LLFontGL::HCENTER,
+ LLFontGL::TOP,
+ LLFontGL::DROP_SHADOW);
+}
+
+
+//static
+void LLWorldMapView::drawTrackingCircle( const LLRect& rect, S32 x, S32 y, const LLColor4& color, S32 min_thickness, S32 overlap )
+{
+ F32 start_theta = 0.f;
+ F32 end_theta = F_TWO_PI;
+ F32 x_delta = 0.f;
+ F32 y_delta = 0.f;
+
+ if (x < 0)
+ {
+ x_delta = 0.f - (F32)x;
+ start_theta = F_PI + F_PI_BY_TWO;
+ end_theta = F_TWO_PI + F_PI_BY_TWO;
+ }
+ else if (x > rect.getWidth())
+ {
+ x_delta = (F32)(x - rect.getWidth());
+ start_theta = F_PI_BY_TWO;
+ end_theta = F_PI + F_PI_BY_TWO;
+ }
+
+ if (y < 0)
+ {
+ y_delta = 0.f - (F32)y;
+ if (x < 0)
+ {
+ start_theta = 0.f;
+ end_theta = F_PI_BY_TWO;
+ }
+ else if (x > rect.getWidth())
+ {
+ start_theta = F_PI_BY_TWO;
+ end_theta = F_PI;
+ }
+ else
+ {
+ start_theta = 0.f;
+ end_theta = F_PI;
+ }
+ }
+ else if (y > rect.getHeight())
+ {
+ y_delta = (F32)(y - rect.getHeight());
+ if (x < 0)
+ {
+ start_theta = F_PI + F_PI_BY_TWO;
+ end_theta = F_TWO_PI;
+ }
+ else if (x > rect.getWidth())
+ {
+ start_theta = F_PI;
+ end_theta = F_PI + F_PI_BY_TWO;
+ }
+ else
+ {
+ start_theta = F_PI;
+ end_theta = F_TWO_PI;
+ }
+ }
+
+ F32 distance = sqrtf(x_delta * x_delta + y_delta * y_delta);
+
+ distance = llmax(0.1f, distance);
+
+ F32 outer_radius = distance + (1.f + (9.f * sqrtf(x_delta * y_delta) / distance)) * (F32)overlap;
+ F32 inner_radius = outer_radius - (F32)min_thickness;
+
+ F32 angle_adjust_x = asin(x_delta / outer_radius);
+ F32 angle_adjust_y = asin(y_delta / outer_radius);
+
+ if (angle_adjust_x)
+ {
+ if (angle_adjust_y)
+ {
+ F32 angle_adjust = llmin(angle_adjust_x, angle_adjust_y);
+ start_theta += angle_adjust;
+ end_theta -= angle_adjust;
+ }
+ else
+ {
+ start_theta += angle_adjust_x;
+ end_theta -= angle_adjust_x;
+ }
+ }
+ else if (angle_adjust_y)
+ {
+ start_theta += angle_adjust_y;
+ end_theta -= angle_adjust_y;
+ }
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glTranslatef((F32)x, (F32)y, 0.f);
+ gl_washer_segment_2d(inner_radius, outer_radius, start_theta, end_theta, 40, color, color);
+ glPopMatrix();
+
+}
+
+// static
+void LLWorldMapView::drawTrackingArrow(const LLRect& rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 arrow_size)
+{
+ F32 x_center = (F32)rect.getWidth() / 2.f;
+ F32 y_center = (F32)rect.getHeight() / 2.f;
+
+ F32 x_clamped = (F32)llclamp( x, 0, rect.getWidth() - arrow_size );
+ F32 y_clamped = (F32)llclamp( y, 0, rect.getHeight() - arrow_size );
+
+ F32 slope = (F32)(y - y_center) / (F32)(x - x_center);
+ F32 window_ratio = (F32)rect.getHeight() / (F32)rect.getWidth();
+
+ if (llabs(slope) > window_ratio && y_clamped != (F32)y)
+ {
+ // clamp by y
+ x_clamped = (y_clamped - y_center) / slope + x_center;
+ // adjust for arrow size
+ x_clamped = llclamp(x_clamped , 0.f, (F32)(rect.getWidth() - arrow_size) );
+ }
+ else if (x_clamped != (F32)x)
+ {
+ // clamp by x
+ y_clamped = (x_clamped - x_center) * slope + y_center;
+ // adjust for arrow size
+ y_clamped = llclamp( y_clamped, 0.f, (F32)(rect.getHeight() - arrow_size) );
+ }
+
+ //FIXME: deal with non-square window properly
+ S32 half_arrow_size = (S32) (0.5f * arrow_size);
+
+ F32 angle = atan2( y + half_arrow_size - y_center, x + half_arrow_size - x_center);
+
+ sTrackingArrowX = llfloor(x_clamped);
+ sTrackingArrowY = llfloor(y_clamped);
+
+ gl_draw_scaled_rotated_image(
+ sTrackingArrowX,
+ sTrackingArrowY,
+ arrow_size, arrow_size,
+ RAD_TO_DEG * angle,
+ sTrackArrowImage,
+ color);
+}
+
+void LLWorldMapView::setDirectionPos( LLTextBox* text_box, F32 rotation )
+{
+ // Rotation is in radians.
+ // Rotation of 0 means x = 1, y = 0 on the unit circle.
+
+
+ F32 map_half_height = mRect.getHeight() * 0.5f;
+ F32 map_half_width = mRect.getWidth() * 0.5f;
+ F32 text_half_height = text_box->getRect().getHeight() * 0.5f;
+ F32 text_half_width = text_box->getRect().getWidth() * 0.5f;
+ 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 )) );
+}
+
+
+void LLWorldMapView::updateDirections()
+{
+ S32 width = mRect.getWidth();
+ S32 height = mRect.getHeight();
+
+ S32 text_height = mTextBoxNorth->getRect().getHeight();
+ S32 text_width = mTextBoxNorth->getRect().getWidth();
+
+ const S32 PAD = 2;
+ S32 top = height - text_height - PAD;
+ S32 left = PAD*2;
+ S32 bottom = PAD;
+ S32 right = width - text_width - PAD;
+ S32 center_x = width/2 - text_width/2;
+ S32 center_y = height/2 - text_height/2;
+
+ mTextBoxNorth->setOrigin( center_x, top );
+ mTextBoxEast->setOrigin( right, center_y );
+ mTextBoxSouth->setOrigin( center_x, bottom );
+ mTextBoxWest->setOrigin( left, center_y );
+
+ // These have wider text boxes
+ text_width = mTextBoxNorthWest->getRect().getWidth();
+ right = width - text_width - PAD;
+
+ mTextBoxNorthWest->setOrigin(left, top);
+ mTextBoxNorthEast->setOrigin(right, top);
+ mTextBoxSouthWest->setOrigin(left, bottom);
+ mTextBoxSouthEast->setOrigin(right, bottom);
+
+// S32 hint_width = mTextBoxScrollHint->getRect().getWidth();
+// mTextBoxScrollHint->setOrigin( width - hint_width - text_width - 2 * PAD,
+// PAD * 2 + text_height );
+}
+
+
+void LLWorldMapView::reshape( S32 width, S32 height, BOOL called_from_parent )
+{
+ LLView::reshape( width, height, called_from_parent );
+}
+
+bool LLWorldMapView::checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track)
+{
+ LLVector3 pos_view = globalPosToView(item.mPosGlobal);
+ S32 item_x = llround(pos_view.mV[VX]);
+ S32 item_y = llround(pos_view.mV[VY]);
+
+ if (x < item_x - BIG_DOT_RADIUS) return false;
+ if (x > item_x + BIG_DOT_RADIUS) return false;
+ if (y < item_y - BIG_DOT_RADIUS) return false;
+ if (y > item_y + BIG_DOT_RADIUS) return false;
+
+ LLSimInfo* sim_info = gWorldMap->simInfoFromHandle(item.mRegionHandle);
+ if (sim_info)
+ {
+ if (track)
+ {
+ gFloaterWorldMap->trackLocation(item.mPosGlobal);
+ }
+ }
+
+ if (track)
+ {
+ gFloaterWorldMap->trackGenericItem(item);
+ }
+
+ item.mSelected = TRUE;
+ *id = item.mID;
+
+ return true;
+}
+
+// Handle a click, which might be on a dot
+void LLWorldMapView::handleClick(S32 x, S32 y, MASK mask,
+ S32* hit_type,
+ LLUUID* id)
+{
+ LLVector3d pos_global = viewPosToGlobal(x, y);
+
+ // *HACK: Adjust Z values automatically for liaisons & gods so
+ // we swoop down when they click on the map. Sadly, the P2P
+ // branch does not pay attention to this value; however, the
+ // Distributed Messaging branch honors kt.
+ if(gAgent.isGodlike())
+ {
+ pos_global.mdV[VZ] = 200.0;
+ }
+
+ *hit_type = 0; // hit nothing
+
+ gWorldMap->mIsTrackingUnknownLocation = FALSE;
+ gWorldMap->mIsTrackingDoubleClick = FALSE;
+ gWorldMap->mIsTrackingCommit = FALSE;
+
+ LLWorldMap::item_info_list_t::iterator it;
+
+ // clear old selected stuff
+ for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->mPGEvents.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->mMatureEvents.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+ for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it)
+ {
+ (*it).mSelected = FALSE;
+ }
+
+ // Select event you clicked on
+ if (gSavedSettings.getBOOL("MapShowEvents"))
+ {
+ for (it = gWorldMap->mPGEvents.begin(); it != gWorldMap->mPGEvents.end(); ++it)
+ {
+ LLItemInfo& event = *it;
+
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_PG_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ }
+ if (gSavedSettings.getBOOL("ShowMatureEvents"))
+ {
+ for (it = gWorldMap->mMatureEvents.begin(); it != gWorldMap->mMatureEvents.end(); ++it)
+ {
+ LLItemInfo& event = *it;
+
+ if (checkItemHit(x, y, event, id, false))
+ {
+ *hit_type = MAP_ITEM_MATURE_EVENT;
+ mItemPicked = TRUE;
+ gFloaterWorldMap->trackEvent(event);
+ return;
+ }
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowPopular"))
+ {
+ for (it = gWorldMap->mPopular.begin(); it != gWorldMap->mPopular.end(); ++it)
+ {
+ LLItemInfo& popular = *it;
+
+ if (checkItemHit(x, y, popular, id, true))
+ {
+ *hit_type = MAP_ITEM_POPULAR;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowLandForSale"))
+ {
+ for (it = gWorldMap->mLandForSale.begin(); it != gWorldMap->mLandForSale.end(); ++it)
+ {
+ LLItemInfo& land = *it;
+
+ if (checkItemHit(x, y, land, id, true))
+ {
+ *hit_type = MAP_ITEM_LAND_FOR_SALE;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ if (gSavedSettings.getBOOL("MapShowClassifieds"))
+ {
+ for (it = gWorldMap->mClassifieds.begin(); it != gWorldMap->mClassifieds.end(); ++it)
+ {
+ LLItemInfo& classified = *it;
+
+ if (checkItemHit(x, y, classified, id, true))
+ {
+ *hit_type = MAP_ITEM_CLASSIFIED;
+ mItemPicked = TRUE;
+ return;
+ }
+ }
+ }
+
+ // If we get here, we haven't clicked on an icon
+
+ gFloaterWorldMap->trackLocation(pos_global);
+ mItemPicked = FALSE;
+
+ *id = LLUUID::null;
+ return;
+}
+
+
+BOOL outside_slop(S32 x, S32 y, S32 start_x, S32 start_y)
+{
+ S32 dx = x - start_x;
+ S32 dy = y - start_y;
+
+ return (dx <= -2 || 2 <= dx || dy <= -2 || 2 <= dy);
+}
+
+BOOL LLWorldMapView::handleMouseDown( S32 x, S32 y, MASK mask )
+{
+ gFocusMgr.setMouseCapture( this, NULL );
+
+ mMouseDownPanX = llround(sPanX);
+ mMouseDownPanY = llround(sPanY);
+ mMouseDownX = x;
+ mMouseDownY = y;
+ sHandledLastClick = TRUE;
+ return TRUE;
+}
+
+BOOL LLWorldMapView::handleMouseUp( S32 x, S32 y, MASK mask )
+{
+ if (this == gFocusMgr.getMouseCapture())
+ {
+ if (mPanning)
+ {
+ // restore mouse cursor
+ S32 local_x, local_y;
+ local_x = mMouseDownX + llfloor(sPanX - mMouseDownPanX);
+ local_y = mMouseDownY + llfloor(sPanY - mMouseDownPanY);
+ LLRect clip_rect = mRect;
+ clip_rect.stretch(-8);
+ clip_rect.clipPointToRect(mMouseDownX, mMouseDownY, local_x, local_y);
+ LLUI::setCursorPositionLocal(this, local_x, local_y);
+
+ // finish the pan
+ mPanning = FALSE;
+
+ mMouseDownX = 0;
+ mMouseDownY = 0;
+ }
+ else
+ {
+ // ignore whether we hit an event or not
+ S32 hit_type;
+ LLUUID id;
+ handleClick(x, y, mask, &hit_type, &id);
+ }
+ gViewerWindow->showCursor();
+ gFocusMgr.setMouseCapture( NULL, NULL );
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLWorldMapView::updateBlock(S32 block_x, S32 block_y)
+{
+ S32 offset = block_x | (block_y * MAP_BLOCK_RES);
+ if (!gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset])
+ {
+// llinfos << "Loading Block (" << block_x << "," << block_y << ")" << llendl;
+ gWorldMap->sendMapBlockRequest(block_x << 3, block_y << 3, (block_x << 3) + 7, (block_y << 3) + 7);
+ gWorldMap->mMapBlockLoaded[gWorldMap->mCurrentMap][offset] = TRUE;
+ }
+}
+
+void LLWorldMapView::updateVisibleBlocks()
+{
+ if (gMapScale < SIM_MAP_SCALE)
+ {
+ // We don't care what is loaded if we're zoomed out
+ return;
+ }
+ // check if we've loaded the 9 potentially visible zones
+ LLVector3d camera_global = gAgent.getCameraPositionGlobal();
+
+ // Convert pan to sim coordinates
+ S32 world_center_x = S32((-sPanX / gMapScale) + (camera_global.mdV[0] / REGION_WIDTH_METERS));
+ S32 world_center_y = S32((-sPanY / gMapScale) + (camera_global.mdV[1] / REGION_WIDTH_METERS));
+
+ // Find the corresponding 8x8 block
+ S32 world_block_x = world_center_x >> 3;
+ S32 world_block_y = world_center_y >> 3;
+
+ for (S32 block_x = llmax(world_block_x-1, 0); block_x <= llmin(world_block_x+1, MAP_BLOCK_RES-1); ++block_x)
+ {
+ for (S32 block_y = llmax(world_block_y-1, 0); block_y <= llmin(world_block_y+1, MAP_BLOCK_RES-1); ++block_y)
+ {
+ updateBlock(block_x, block_y);
+ }
+ }
+}
+
+BOOL LLWorldMapView::handleHover( S32 x, S32 y, MASK mask )
+{
+ if (this == gFocusMgr.getMouseCapture())
+ {
+ if (mPanning || outside_slop(x, y, mMouseDownX, mMouseDownY))
+ {
+ // just started panning, so hide cursor
+ if (!mPanning)
+ {
+ mPanning = TRUE;
+ gViewerWindow->hideCursor();
+ }
+
+ F32 delta_x = (F32)(gViewerWindow->getCurrentMouseDX());
+ F32 delta_y = (F32)(gViewerWindow->getCurrentMouseDY());
+
+ // Set pan to value at start of drag + offset
+ sPanX += delta_x;
+ sPanY += delta_y;
+ sTargetPanX = sPanX;
+ sTargetPanY = sPanY;
+
+ gViewerWindow->moveCursorToCenter();
+ }
+
+ // doesn't matter, cursor should be hidden
+ gViewerWindow->setCursor(UI_CURSOR_CROSS );
+ return TRUE;
+ }
+ else
+ {
+ // While we're waiting for data from the tracker, we're busy. JC
+ LLVector3d pos_global = LLTracker::getTrackedPositionGlobal();
+ if (LLTracker::isTracking(NULL)
+ && pos_global.isExactlyZero())
+ {
+ gViewerWindow->setCursor( UI_CURSOR_WAIT );
+ }
+ else
+ {
+ gViewerWindow->setCursor( UI_CURSOR_CROSS );
+ }
+ lldebugst(LLERR_USER_INPUT) << "hover handled by LLWorldMapView" << llendl;
+ return TRUE;
+ }
+}
+
+
+BOOL LLWorldMapView::handleDoubleClick( S32 x, S32 y, MASK mask )
+{
+ if( sHandledLastClick )
+ {
+ S32 hit_type;
+ LLUUID id;
+ handleClick(x, y, mask, &hit_type, &id);
+
+ switch (hit_type)
+ {
+ case MAP_ITEM_PG_EVENT:
+ case MAP_ITEM_MATURE_EVENT:
+ {
+ gFloaterWorldMap->close();
+ // This is an ungainly hack
+ char uuid_str[38];
+ S32 event_id;
+ id.toString(uuid_str);
+ sscanf(&uuid_str[28], "%X", &event_id);
+ LLFloaterDirectory::showEvents(event_id);
+ break;
+ }
+ case MAP_ITEM_POPULAR:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showPopular(id);
+ break;
+ }
+ case MAP_ITEM_LAND_FOR_SALE:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showLandForSale(id);
+ break;
+ }
+ case MAP_ITEM_CLASSIFIED:
+ {
+ gFloaterWorldMap->close();
+ LLFloaterDirectory::showClassified(id);
+ break;
+ }
+ default:
+ {
+ if (gWorldMap->mIsTrackingUnknownLocation)
+ {
+ gWorldMap->mIsTrackingDoubleClick = TRUE;
+ }
+ else
+ {
+ // Teleport if we got a valid location
+ LLVector3d pos_global = viewPosToGlobal(x,y);
+ LLSimInfo* sim_info = gWorldMap->simInfoFromPosGlobal(pos_global);
+ if (sim_info && sim_info->mAccess != SIM_ACCESS_DOWN)
+ {
+ gAgent.teleportViaLocation( pos_global );
+ }
+ }
+ }
+ };
+
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/indra/newview/llworldmapview.h b/indra/newview/llworldmapview.h
new file mode 100644
index 0000000000..7cf0f655a1
--- /dev/null
+++ b/indra/newview/llworldmapview.h
@@ -0,0 +1,173 @@
+/**
+ * @file llworldmapview.h
+ * @brief LLWorldMapView class header file
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// Global map of the world.
+
+#ifndef LL_LLWORLDMAPVIEW_H
+#define LL_LLWORLDMAPVIEW_H
+
+#include "llpanel.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4color.h"
+#include "llviewerimage.h"
+#include "llmapimagetype.h"
+#include "llworldmap.h"
+
+class LLItemInfo;
+
+const S32 DEFAULT_TRACKING_ARROW_SIZE = 16;
+
+class LLColor4;
+class LLColor4U;
+class LLCoordGL;
+class LLViewerImage;
+class LLTextBox;
+
+#define SIM_NULL_MAP_SCALE 2 // width in pixels, where we start drawing "null" sims
+#define SIM_MAP_AGENT_SCALE 20 // width in pixels, where we start drawing agents
+#define SIM_MAP_SCALE 90 // width in pixels, where we start drawing sim tiles
+
+#define AGENTS_UPDATE_TIME 60.0 // in seconds
+#define AGENT_COUNTS_UPDATE_TIME 60.0 // in seconds
+
+class LLWorldMapView : public LLPanel
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+
+ LLWorldMapView(const std::string& name, const LLRect& rect );
+ virtual ~LLWorldMapView();
+
+ virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE );
+
+ virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask);
+ virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask);
+ virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask );
+ virtual BOOL handleHover( S32 x, S32 y, MASK mask );
+ /*virtual*/ BOOL handleToolTip( S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen );
+
+ bool checkItemHit(S32 x, S32 y, LLItemInfo& item, LLUUID* id, bool track);
+ void handleClick(S32 x, S32 y, MASK mask, S32* hit_type, LLUUID* id);
+
+ // Scale and pan are shared across all instances.
+ static void setScale( F32 scale );
+ static void translatePan( S32 delta_x, S32 delta_y );
+ static void setPan( S32 x, S32 y, BOOL snap = TRUE );
+
+ LLVector3 globalPosToView(const LLVector3d& global_pos);
+ LLVector3d viewPosToGlobal(S32 x,S32 y);
+
+ virtual void draw();
+ void drawGenericItems(const LLWorldMap::item_info_list_t& items, LLPointer<LLViewerImage> image);
+ void drawGenericItem(const LLItemInfo& item, LLPointer<LLViewerImage> image);
+ void drawAgents();
+ void drawEvents();
+ void drawDots();
+ void drawFrustum();
+
+ static void cleanupTextures();
+
+ // Draw the tracking indicator, doing the right thing if it's outside
+ // the view area.
+ void drawTracking( const LLVector3d& pos_global,
+ const LLColor4& color,
+ BOOL draw_arrow = TRUE, LLString label = "", LLString tooltip = "", S32 vert_offset = 0);
+ static void drawTrackingArrow(const LLRect& view_rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 arrow_size = DEFAULT_TRACKING_ARROW_SIZE);
+ static void drawTrackingDot(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z = 0.f,
+ F32 dot_radius = 3.f);
+
+ static void drawTrackingCircle( const LLRect& rect, S32 x, S32 y,
+ const LLColor4& color,
+ S32 min_thickness,
+ S32 overlap );
+ static void drawAvatar( F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ F32 relative_z = 0.f,
+ F32 dot_radius = 3.f);
+ static void drawIconName(F32 x_pixels,
+ F32 y_pixels,
+ const LLColor4& color,
+ const std::string& first_line,
+ const std::string& second_line);
+
+ // Prevents accidental double clicks
+ static void clearLastClick() { sHandledLastClick = FALSE; }
+
+ // if the view changes, download additional sim info as needed
+ void updateBlock(S32 block_x, S32 block_y);
+ void updateVisibleBlocks();
+
+protected:
+ void setDirectionPos( LLTextBox* text_box, F32 rotation );
+ void updateDirections();
+
+public:
+ LLColor4 mBackgroundColor;
+
+ static LLPointer<LLViewerImage> sAvatarYouSmallImage;
+ static LLPointer<LLViewerImage> sAvatarSmallImage;
+ static LLPointer<LLViewerImage> sAvatarLargeImage;
+ static LLPointer<LLViewerImage> sTelehubImage;
+ static LLPointer<LLViewerImage> sInfohubImage;
+ static LLPointer<LLViewerImage> sHomeImage;
+ static LLPointer<LLViewerImage> sEventImage;
+ static LLPointer<LLViewerImage> sEventMatureImage;
+ static LLPointer<LLViewerImage> sTrackCircleImage;
+ static LLPointer<LLViewerImage> sTrackArrowImage;
+ static LLPointer<LLViewerImage> sClassifiedsImage;
+ static LLPointer<LLViewerImage> sPopularImage;
+ static LLPointer<LLViewerImage> sForSaleImage;
+
+ static F32 sThresholdA;
+ static F32 sThresholdB;
+ static F32 sPixelsPerMeter; // world meters to map pixels
+
+ BOOL mItemPicked;
+
+ static F32 sPanX; // in pixels
+ static F32 sPanY; // in pixels
+ static F32 sTargetPanX; // in pixels
+ static F32 sTargetPanY; // in pixels
+ static S32 sTrackingArrowX;
+ static S32 sTrackingArrowY;
+
+ // Are we mid-pan from a user drag?
+ BOOL mPanning;
+ S32 mMouseDownPanX; // value at start of drag
+ S32 mMouseDownPanY; // value at start of drag
+ S32 mMouseDownX;
+ S32 mMouseDownY;
+
+ LLTextBox* mTextBoxEast;
+ LLTextBox* mTextBoxNorth;
+ LLTextBox* mTextBoxWest;
+ LLTextBox* mTextBoxSouth;
+
+ LLTextBox* mTextBoxSouthEast;
+ LLTextBox* mTextBoxNorthEast;
+ LLTextBox* mTextBoxNorthWest;
+ LLTextBox* mTextBoxSouthWest;
+ LLTextBox* mTextBoxScrollHint;
+
+ static BOOL sHandledLastClick;
+ S32 mSelectIDStart;
+ F64 mAgentCountsUpdateTime;
+
+ typedef std::vector<U64> handle_list_t;
+ handle_list_t mVisibleRegions; // set every frame
+};
+
+#endif
diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp
new file mode 100644
index 0000000000..9cbee095f2
--- /dev/null
+++ b/indra/newview/llxmlrpctransaction.cpp
@@ -0,0 +1,577 @@
+/**
+ * @file llxmlrpctransaction.cpp
+ * @brief LLXMLRPCTransaction and related class implementations
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llxmlrpctransaction.h"
+
+#include "llviewercontrol.h"
+
+// Have to include these last to avoid queue redefinition!
+#include <curl/curl.h>
+#include <xmlrpc-epi/xmlrpc.h>
+
+#include "viewer.h"
+
+LLXMLRPCValue LLXMLRPCValue::operator[](const char* id) const
+{
+ return LLXMLRPCValue(XMLRPC_VectorGetValueWithID(mV, id));
+}
+
+std::string LLXMLRPCValue::asString() const
+{
+ const char* s = XMLRPC_GetValueString(mV);
+ return s ? s : "";
+}
+
+int LLXMLRPCValue::asInt() const { return XMLRPC_GetValueInt(mV); }
+bool LLXMLRPCValue::asBool() const { return XMLRPC_GetValueBoolean(mV) != 0; }
+double LLXMLRPCValue::asDouble() const { return XMLRPC_GetValueDouble(mV); }
+
+LLXMLRPCValue LLXMLRPCValue::rewind()
+{
+ return LLXMLRPCValue(XMLRPC_VectorRewind(mV));
+}
+
+LLXMLRPCValue LLXMLRPCValue::next()
+{
+ return LLXMLRPCValue(XMLRPC_VectorNext(mV));
+}
+
+bool LLXMLRPCValue::isValid() const
+{
+ return mV != NULL;
+}
+
+LLXMLRPCValue LLXMLRPCValue::createArray()
+{
+ return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_array));
+}
+
+LLXMLRPCValue LLXMLRPCValue::createStruct()
+{
+ return LLXMLRPCValue(XMLRPC_CreateVector(NULL, xmlrpc_vector_struct));
+}
+
+
+void LLXMLRPCValue::append(LLXMLRPCValue& v)
+{
+ XMLRPC_AddValueToVector(mV, v.mV);
+}
+
+void LLXMLRPCValue::appendString(const std::string& v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(NULL, v.c_str(), 0));
+}
+
+void LLXMLRPCValue::appendInt(int v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(NULL, v));
+}
+
+void LLXMLRPCValue::appendBool(bool v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(NULL, v));
+}
+
+void LLXMLRPCValue::appendDouble(double v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(NULL, v));
+}
+
+
+void LLXMLRPCValue::append(const char* id, LLXMLRPCValue& v)
+{
+ XMLRPC_SetValueID(v.mV, id, 0);
+ XMLRPC_AddValueToVector(mV, v.mV);
+}
+
+void LLXMLRPCValue::appendString(const char* id, const std::string& v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueString(id, v.c_str(), 0));
+}
+
+void LLXMLRPCValue::appendInt(const char* id, int v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueInt(id, v));
+}
+
+void LLXMLRPCValue::appendBool(const char* id, bool v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueBoolean(id, v));
+}
+
+void LLXMLRPCValue::appendDouble(const char* id, double v)
+{
+ XMLRPC_AddValueToVector(mV, XMLRPC_CreateValueDouble(id, v));
+}
+
+void LLXMLRPCValue::free()
+{
+ XMLRPC_CleanupValue(mV);
+ mV = NULL;
+}
+
+XMLRPC_VALUE LLXMLRPCValue::getValue() const
+{
+ return mV;
+}
+
+
+class LLXMLRPCTransaction::Impl
+{
+public:
+ typedef LLXMLRPCTransaction::Status Status;
+
+ CURL* mCurl;
+ CURLM* mCurlMulti;
+
+ Status mStatus;
+ CURLcode mCurlCode;
+ std::string mStatusMessage;
+ std::string mStatusURI;
+
+ char mCurlErrorBuffer[CURL_ERROR_SIZE];
+
+ std::string mURI;
+ char* mRequestText;
+ int mRequestTextSize;
+
+ std::string mProxyAddress;
+ struct curl_slist* mHeaders;
+
+ std::string mResponseText;
+ XMLRPC_REQUEST mResponse;
+
+ Impl(const std::string& uri, XMLRPC_REQUEST request, bool useGzip);
+ Impl(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip);
+ ~Impl();
+
+ bool process();
+
+ void setStatus(Status code,
+ const std::string& message = "", const std::string& uri = "");
+ void setCurlStatus(CURLcode);
+
+private:
+ void init(XMLRPC_REQUEST request, bool useGzip);
+
+ static size_t curlDownloadCallback(
+ void* data, size_t size, size_t nmemb, void* user_data);
+};
+
+LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
+ XMLRPC_REQUEST request, bool useGzip)
+ : mCurl(0), mCurlMulti(0),
+ mStatus(LLXMLRPCTransaction::StatusNotStarted),
+ mURI(uri),
+ mRequestText(0), mHeaders(0),
+ mResponse(0)
+{
+ init(request, useGzip);
+}
+
+
+LLXMLRPCTransaction::Impl::Impl(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip)
+ : mCurl(0), mCurlMulti(0),
+ mStatus(LLXMLRPCTransaction::StatusNotStarted),
+ mURI(uri),
+ mRequestText(0), mHeaders(0),
+ mResponse(0)
+{
+ XMLRPC_REQUEST request = XMLRPC_RequestNew();
+ XMLRPC_RequestSetMethodName(request, method.c_str());
+ XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
+ XMLRPC_RequestSetData(request, params.getValue());
+
+ init(request, useGzip);
+}
+
+
+
+
+void LLXMLRPCTransaction::Impl::init(XMLRPC_REQUEST request, bool useGzip)
+{
+ mCurl = curl_easy_init();
+
+ if (gSavedSettings.getBOOL("BrowserProxyEnabled"))
+ {
+ mProxyAddress = gSavedSettings.getString("BrowserProxyAddress");
+ S32 port = gSavedSettings.getS32 ( "BrowserProxyPort" );
+
+ // tell curl about the settings
+ curl_easy_setopt(mCurl, CURLOPT_PROXY, mProxyAddress.c_str());
+ curl_easy_setopt(mCurl, CURLOPT_PROXYPORT, (long)port);
+ curl_easy_setopt(mCurl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+ };
+
+// curl_easy_setopt(mCurl, CURLOPT_VERBOSE, 1); // usefull for debugging
+ curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &curlDownloadCallback);
+ curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this);
+ curl_easy_setopt(mCurl, CURLOPT_ERRORBUFFER, &mCurlErrorBuffer);
+ curl_easy_setopt(mCurl, CURLOPT_CAINFO, gDirUtilp->getCAFile().c_str());
+ curl_easy_setopt(mCurl, CURLOPT_SSL_VERIFYPEER, gVerifySSLCert);
+
+ /* Setting the DNS cache timeout to -1 disables it completely.
+ This might help with bug #503 */
+ curl_easy_setopt(mCurl, CURLOPT_DNS_CACHE_TIMEOUT, -1);
+
+ mHeaders = curl_slist_append(mHeaders, "Content-Type: text/xml");
+ curl_easy_setopt(mCurl, CURLOPT_URL, mURI.c_str());
+ curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaders);
+ if (useGzip)
+ {
+ curl_easy_setopt(mCurl, CURLOPT_ENCODING, "");
+ }
+
+ mRequestText = XMLRPC_REQUEST_ToXML(request, &mRequestTextSize);
+ if (mRequestText)
+ {
+ curl_easy_setopt(mCurl, CURLOPT_POSTFIELDS, mRequestText);
+ curl_easy_setopt(mCurl, CURLOPT_POSTFIELDSIZE, mRequestTextSize);
+ }
+ else
+ {
+ setStatus(StatusOtherError);
+ }
+
+ mCurlMulti = curl_multi_init();
+ curl_multi_add_handle(mCurlMulti, mCurl);
+}
+
+
+LLXMLRPCTransaction::Impl::~Impl()
+{
+ if (mResponse)
+ {
+ XMLRPC_RequestFree(mResponse, 1);
+ }
+
+ if (mHeaders)
+ {
+ curl_slist_free_all(mHeaders);
+ }
+
+ if (mRequestText)
+ {
+ XMLRPC_Free(mRequestText);
+ }
+
+ if (mCurl)
+ {
+ if (mCurlMulti)
+ {
+ curl_multi_remove_handle(mCurlMulti, mCurl);
+ }
+ curl_easy_cleanup(mCurl);
+ }
+
+ if (mCurlMulti)
+ {
+ curl_multi_cleanup(mCurlMulti);
+ }
+
+}
+
+bool LLXMLRPCTransaction::Impl::process()
+{
+ switch(mStatus)
+ {
+ case LLXMLRPCTransaction::StatusComplete:
+ case LLXMLRPCTransaction::StatusCURLError:
+ case LLXMLRPCTransaction::StatusXMLRPCError:
+ case LLXMLRPCTransaction::StatusOtherError:
+ {
+ return true;
+ }
+
+ case LLXMLRPCTransaction::StatusNotStarted:
+ {
+ setStatus(LLXMLRPCTransaction::StatusStarted);
+ break;
+ }
+
+ default:
+ {
+ // continue onward
+ }
+ }
+
+ const F32 MAX_PROCESSING_TIME = 0.05f;
+ LLTimer timer;
+ int count;
+
+ while (CURLM_CALL_MULTI_PERFORM == curl_multi_perform(mCurlMulti, &count))
+ {
+ if (timer.getElapsedTimeF32() >= MAX_PROCESSING_TIME)
+ {
+ return false;
+ }
+ }
+
+ while(CURLMsg* curl_msg = curl_multi_info_read(mCurlMulti, &count))
+ {
+ if (CURLMSG_DONE == curl_msg->msg)
+ {
+ if (curl_msg->data.result != CURLE_OK)
+ {
+ setCurlStatus(curl_msg->data.result);
+ llalerts << "LLXMLRPCTransaction CURL error "
+ << mCurlCode << ": " << mCurlErrorBuffer << llendl;
+ llalerts << "LLXMLRPCTransaction request URI: "
+ << mURI << llendl;
+
+ return true;
+ }
+
+ setStatus(LLXMLRPCTransaction::StatusComplete);
+
+ mResponse = XMLRPC_REQUEST_FromXML(
+ mResponseText.data(), mResponseText.size(), NULL);
+
+ bool hasError = false;
+ bool hasFault = false;
+ int faultCode = 0;
+ std::string faultString;
+
+ LLXMLRPCValue error(XMLRPC_RequestGetError(mResponse));
+ if (error.isValid())
+ {
+ hasError = true;
+ faultCode = error["faultCode"].asInt();
+ faultString = error["faultString"].asString();
+ }
+ else if (XMLRPC_ResponseIsFault(mResponse))
+ {
+ hasFault = true;
+ faultCode = XMLRPC_GetResponseFaultCode(mResponse);
+ faultString = XMLRPC_GetResponseFaultString(mResponse);
+ }
+
+ if (hasError || hasFault)
+ {
+ setStatus(LLXMLRPCTransaction::StatusXMLRPCError);
+
+ llalerts << "LLXMLRPCTransaction XMLRPC "
+ << (hasError ? "error " : "fault ")
+ << faultCode << ": "
+ << faultString << llendl;
+ llalerts << "LLXMLRPCTransaction request URI: "
+ << mURI << llendl;
+ }
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void LLXMLRPCTransaction::Impl::setStatus(Status status,
+ const std::string& message, const std::string& uri)
+{
+ mStatus = status;
+ mStatusMessage = message;
+ mStatusURI = uri;
+
+ if (mStatusMessage.empty())
+ {
+ switch (mStatus)
+ {
+ case StatusNotStarted:
+ mStatusMessage = "(not started)";
+ break;
+
+ case StatusStarted:
+ mStatusMessage = "(waiting for server response)";
+ break;
+
+ case StatusDownloading:
+ mStatusMessage = "(reading server response)";
+ break;
+
+ case StatusComplete:
+ mStatusMessage = "(done)";
+ break;
+
+ default:
+ // Usually this means that there's a problem with the login server,
+ // not with the client. Direct user to status page. JC
+ mStatusMessage =
+ "Despite our best efforts, something unexpected has gone wrong. \n"
+ " \n"
+ "Please check www.secondlife.com/status and the Second Life \n"
+ "Announcements forum to see if there is a known problem with \n"
+ "the service.";
+ mStatusURI = "http://secondlife.com/status/";
+ /*
+ mStatusMessage =
+ "Despite our best efforts, something unexpected has gone wrong.\n"
+ "Please go to the Support section of the SecondLife.com web site\n"
+ "and report the problem. If possible, include your SecondLife.log\n"
+ "file from:\n"
+#if LL_WINDOWS
+ "C:\\Documents and Settings\\<name>\\Application Data\\SecondLife\\logs\n"
+#elif LL_DARWIN
+ "~/Library/Application Support/SecondLife/logs\n"
+#elif LL_LINUX
+ "~/.secondlife/logs\n"
+#else
+#error "Need platform here."
+#endif
+ "Thank you.";
+ mStatusURI = "http://secondlife.com/community/support.php";
+ */
+ }
+ }
+}
+
+void LLXMLRPCTransaction::Impl::setCurlStatus(CURLcode code)
+{
+ std::string message;
+ std::string uri = "http://secondlife.com/community/support.php";
+
+ switch (code)
+ {
+ case CURLE_COULDNT_RESOLVE_HOST:
+ message =
+ "DNS could not resolve the host name.\n"
+ "Please verify that you can connect to the www.secondlife.com\n"
+ "web site. If you can, but continue to receive this error,\n"
+ "please go to the support section and report this problem.";
+ break;
+
+ case CURLE_SSL_PEER_CERTIFICATE:
+ message =
+ "The login server couldn't verify itself via SSL.\n"
+ "If you continue to receive this error, please go\n"
+ "to the Support section of the SecondLife.com web site\n"
+ "and report the problem.";
+ break;
+
+ case CURLE_SSL_CACERT:
+ case CURLE_SSL_CONNECT_ERROR:
+ message =
+ "Often this means that your computer\'s clock is set incorrectly.\n"
+ "Please go to Control Panels and make sure the time and date\n"
+ "are set correctly.\n"
+ "\n"
+ "If you continue to receive this error, please go\n"
+ "to the Support section of the SecondLife.com web site\n"
+ "and report the problem.";
+ break;
+
+ default:
+ break;
+ }
+
+ mCurlCode = code;
+ setStatus(StatusCURLError, message, uri);
+}
+
+size_t LLXMLRPCTransaction::Impl::curlDownloadCallback(
+ void* data, size_t size, size_t nmemb, void* user_data)
+{
+ Impl& impl(*(Impl*)user_data);
+
+ size_t n = size * nmemb;
+
+ impl.mResponseText.append((const char*)data, n);
+
+ if (impl.mStatus == LLXMLRPCTransaction::StatusStarted)
+ {
+ impl.setStatus(LLXMLRPCTransaction::StatusDownloading);
+ }
+
+ return n;
+}
+
+
+LLXMLRPCTransaction::LLXMLRPCTransaction(
+ const std::string& uri, XMLRPC_REQUEST request, bool useGzip)
+: impl(* new Impl(uri, request, useGzip))
+{ }
+
+
+LLXMLRPCTransaction::LLXMLRPCTransaction(
+ const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip)
+: impl(* new Impl(uri, method, params, useGzip))
+{ }
+
+LLXMLRPCTransaction::~LLXMLRPCTransaction()
+{
+ delete &impl;
+}
+
+bool LLXMLRPCTransaction::process()
+{
+ return impl.process();
+}
+
+LLXMLRPCTransaction::Status LLXMLRPCTransaction::status(int* curlCode)
+{
+ if (curlCode)
+ {
+ *curlCode =
+ (impl.mStatus == StatusCURLError)
+ ? impl.mCurlCode
+ : CURLE_OK;
+ }
+
+ return impl.mStatus;
+}
+
+std::string LLXMLRPCTransaction::statusMessage()
+{
+ return impl.mStatusMessage;
+}
+
+std::string LLXMLRPCTransaction::statusURI()
+{
+ return impl.mStatusURI;
+}
+
+XMLRPC_REQUEST LLXMLRPCTransaction::response()
+{
+ return impl.mResponse;
+}
+
+LLXMLRPCValue LLXMLRPCTransaction::responseValue()
+{
+ return LLXMLRPCValue(XMLRPC_RequestGetData(impl.mResponse));
+}
+
+
+F64 LLXMLRPCTransaction::transferRate()
+{
+ if (!impl.mCurl || impl.mStatus != StatusComplete)
+ {
+ return 0.0L;
+ }
+
+ double size_bytes = 0.0;
+ double time_seconds = 0.0;
+ double rate_bytes_per_sec = 0.0;
+
+ curl_easy_getinfo(impl.mCurl, CURLINFO_SIZE_DOWNLOAD, &size_bytes);
+ curl_easy_getinfo(impl.mCurl, CURLINFO_TOTAL_TIME, &time_seconds);
+ curl_easy_getinfo(impl.mCurl, CURLINFO_SPEED_DOWNLOAD, &rate_bytes_per_sec);
+
+ double rate_bits_per_sec = rate_bytes_per_sec * 8.0;
+
+ llinfos << "Buffer size: " << impl.mResponseText.size() << " B" << llendl;
+ llinfos << "Transfer size: " << size_bytes << " B" << llendl;
+ llinfos << "Transfer time: " << time_seconds << " s" << llendl;
+ llinfos << "Transfer rate: " << rate_bits_per_sec/1000.0 << " Kb/s" << llendl;
+
+ return rate_bits_per_sec;
+}
diff --git a/indra/newview/llxmlrpctransaction.h b/indra/newview/llxmlrpctransaction.h
new file mode 100644
index 0000000000..1ec8d600d6
--- /dev/null
+++ b/indra/newview/llxmlrpctransaction.h
@@ -0,0 +1,115 @@
+/**
+ * @file llxmlrpctransaction.h
+ * @brief LLXMLRPCTransaction and related class header file
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LLXMLRPCTRANSACTION_H
+#define LLXMLRPCTRANSACTION_H
+
+#include <string>
+
+typedef struct _xmlrpc_request* XMLRPC_REQUEST;
+typedef struct _xmlrpc_value* XMLRPC_VALUE;
+ // foward decl of types from xmlrpc.h (this usage is type safe)
+
+class LLXMLRPCValue
+ // a c++ wrapper around XMLRPC_VALUE
+{
+public:
+ LLXMLRPCValue() : mV(NULL) { }
+ LLXMLRPCValue(XMLRPC_VALUE value) : mV(value) { }
+
+ bool isValid() const;
+
+ std::string asString() const;
+ int asInt() const;
+ bool asBool() const;
+ double asDouble() const;
+
+ LLXMLRPCValue operator[](const char*) const;
+
+ LLXMLRPCValue rewind();
+ LLXMLRPCValue next();
+
+ static LLXMLRPCValue createArray();
+ static LLXMLRPCValue createStruct();
+
+ void append(LLXMLRPCValue&);
+ void appendString(const std::string&);
+ void appendInt(int);
+ void appendBool(bool);
+ void appendDouble(double);
+ void appendValue(LLXMLRPCValue&);
+
+ void append(const char*, LLXMLRPCValue&);
+ void appendString(const char*, const std::string&);
+ void appendInt(const char*, int);
+ void appendBool(const char*, bool);
+ void appendDouble(const char*, double);
+ void appendValue(const char*, LLXMLRPCValue&);
+
+ void free();
+ // only call this on the top level created value
+
+ XMLRPC_VALUE getValue() const;
+
+private:
+ XMLRPC_VALUE mV;
+};
+
+
+class LLXMLRPCTransaction
+ // an asynchronous request and respones via XML-RPC
+{
+public:
+ LLXMLRPCTransaction(const std::string& uri,
+ XMLRPC_REQUEST request, bool useGzip = true);
+ // does not take ownership of the request object
+ // request can be freed as soon as the transaction is constructed
+
+ LLXMLRPCTransaction(const std::string& uri,
+ const std::string& method, LLXMLRPCValue params, bool useGzip = true);
+ // *does* take control of the request value, you must not free it
+
+ ~LLXMLRPCTransaction();
+
+ typedef enum {
+ StatusNotStarted,
+ StatusStarted,
+ StatusDownloading,
+ StatusComplete,
+ StatusCURLError,
+ StatusXMLRPCError,
+ StatusOtherError
+ } Status;
+
+ bool process();
+ // run the request a little, returns true when done
+
+ Status status(int* curlCode);
+ // return status, and extended CURL code, if code isn't null
+ std::string statusMessage();
+ // return a message string, suitable for showing the user
+ std::string statusURI();
+ // return a URI for the user with more information
+ // can be empty
+
+ XMLRPC_REQUEST response();
+ LLXMLRPCValue responseValue();
+ // only valid if StatusComplete, otherwise NULL
+ // retains ownership of the result object, don't free it
+
+ F64 transferRate();
+ // only valid if StsatusComplete, otherwise 0.0
+
+private:
+ class Impl;
+ Impl& impl;
+};
+
+
+
+#endif // LLXMLRPCTRANSACTION_H
diff --git a/indra/newview/macmain.h b/indra/newview/macmain.h
new file mode 100644
index 0000000000..112d9b87ee
--- /dev/null
+++ b/indra/newview/macmain.h
@@ -0,0 +1,26 @@
+/**
+ * @file macmain.h
+ * @brief Main Mac viewer defines
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/* main.h */
+
+#define rMenuBar 128 /* menu bar */
+
+#define mApple 128 /* Apple menu */
+#define iAbout 1
+
+#define mFile 129 /* File menu */
+#define iNew 1
+#define iClose 4
+#define iQuitSeparator 10
+#define iQuit 11
+
+#define mEdit 130 /* Edit menu */
+
+#define kAboutBox 200 /* Dialog resource for About box */
+
+#define kSleepTime 32767
diff --git a/indra/newview/macutil_Prefix.h b/indra/newview/macutil_Prefix.h
new file mode 100644
index 0000000000..8674a01c66
--- /dev/null
+++ b/indra/newview/macutil_Prefix.h
@@ -0,0 +1,18 @@
+/**
+ * @file macutil_Prefix.h
+ * @brief Precompiled prefix file
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/*
+ *
+ * Precompiled prefix file used for
+ * AutoUpdater
+ * crashreporter
+ *
+ */
+
+#include <Carbon/Carbon.h>
+
diff --git a/indra/newview/macview.r b/indra/newview/macview.r
new file mode 100644
index 0000000000..8124232cf1
--- /dev/null
+++ b/indra/newview/macview.r
@@ -0,0 +1,123 @@
+/* main.r */
+
+#include <Carbon/Carbon.r>
+#include "macmain.h"
+
+resource 'MBAR' (rMenuBar, preload) {
+ { /* array MenuArray: 3 elements */
+ /* [1] */
+ 128,
+ /* [2] */
+ 129,
+ /* [3] */
+ 130
+ }
+};
+
+resource 'MENU' (mApple, preload) {
+ 128,
+ textMenuProc,
+ 0x7FFFFFFD,
+ enabled,
+ apple,
+ { /* array: 2 elements */
+ /* [1] */
+ "About Second Life", noIcon, noKey, noMark, plain,
+ /* [2] */
+ "-", noIcon, noKey, noMark, plain
+ }
+};
+
+resource 'MENU' (mFile, preload) {
+ 129,
+ textMenuProc,
+ 0x1400,
+ enabled,
+ "File",
+ { /* array: 11 elements */
+ /* [1] */
+ "New", noIcon, "N", noMark, plain,
+ /* [2] */
+ "Open", noIcon, "O", noMark, plain,
+ /* [3] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [4] */
+ "Close", noIcon, "W", noMark, plain,
+ /* [5] */
+ "Save", noIcon, "S", noMark, plain,
+ /* [6] */
+ "Save AsÉ", noIcon, noKey, noMark, plain,
+ /* [7] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [8] */
+ "Page SetupÉ", noIcon, noKey, noMark, plain,
+ /* [9] */
+ "PrintÉ", noIcon, noKey, noMark, plain,
+ /* [10] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [11] */
+ "Quit", noIcon, "Q", noMark, plain
+ }
+};
+
+resource 'MENU' (mEdit, preload) {
+ 130,
+ textMenuProc,
+ 0x0,
+ enabled,
+ "Edit",
+ { /* array: 6 elements */
+ /* [1] */
+ "Undo", noIcon, "Z", noMark, plain,
+ /* [2] */
+ "-", noIcon, noKey, noMark, plain,
+ /* [3] */
+ "Cut", noIcon, "X", noMark, plain,
+ /* [4] */
+ "Copy", noIcon, "C", noMark, plain,
+ /* [5] */
+ "Paste", noIcon, "V", noMark, plain,
+ /* [6] */
+ "Clear", noIcon, noKey, noMark, plain
+ }
+};
+
+resource 'DITL' (kAboutBox) {
+ { /* array DITLarray: 3 elements */
+ /* [1] */
+ {16, 21, 38, 208},
+ StaticText {
+ disabled,
+ "Hello hello hello..."
+ },
+ /* [2] */
+ {116, 287, 136, 345},
+ Button {
+ enabled,
+ "OK"
+ },
+ /* [3] */
+ {54, 139, 74, 197},
+ Button {
+ enabled,
+ "OK"
+ }
+ }
+};
+
+resource 'ALRT' (kAboutBox) {
+ {40, 40, 139, 280},
+ 200,
+ { /* array: 4 elements */
+ /* [1] */
+ OK, visible, sound1,
+ /* [2] */
+ OK, visible, sound1,
+ /* [3] */
+ OK, visible, sound1,
+ /* [4] */
+ OK, visible, sound1
+ },
+ noAutoCenter
+};
+
diff --git a/indra/newview/macview_Prefix.h b/indra/newview/macview_Prefix.h
new file mode 100644
index 0000000000..60097bd422
--- /dev/null
+++ b/indra/newview/macview_Prefix.h
@@ -0,0 +1,221 @@
+/**
+ * @file macview_Prefix.h
+ * @brief Prefix header for all source files of the 'newview' target in the 'newview' project.
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// MBW -- This doesn't work. There are some conflicts between things in Carbon.h and some of the linden source.
+//#include <Carbon/Carbon.h>
+
+////////////////// From llagent.cpp
+#include "llpreprocessor.h"
+#include "stdtypes.h"
+#include "stdenums.h"
+
+#include "llagent.h"
+
+#include "llcoordframe.h"
+#include "indra_constants.h"
+#include "llmath.h"
+#include "llcriticaldamp.h"
+#include "llfocusmgr.h"
+#include "llparcel.h"
+#include "llpermissions.h"
+#include "llregionhandle.h"
+#include "m3math.h"
+#include "m4math.h"
+#include "message.h"
+#include "qmath.h"
+#include "v3math.h"
+#include "v4math.h"
+#include "vmath.h"
+//#include "llteleportflags.h"
+
+#include "llbox.h"
+#include "llbutton.h"
+#include "llcameraview.h"
+#include "llconsole.h"
+#include "lldrawable.h"
+#include "llfirstuse.h"
+#include "llfloater.h"
+#include "llfloateravatarinfo.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloaterdirectory.h"
+#include "llfloatergroups.h"
+#include "llfloatermap.h"
+#include "llfloaterworldmap.h"
+#include "llfloatermute.h"
+#include "llconversation.h"
+#include "llfloatertools.h"
+#include "llhudeffectlookat.h"
+#include "llhudmanager.h"
+#include "llinventoryview.h"
+#include "lljoystickbutton.h"
+#include "llmenugl.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "llsphere.h"
+#include "llstatusbar.h"
+#include "lltalkview.h"
+#include "lltool.h"
+#include "lltoolfocus.h"
+#include "lltoolcomp.h" // for gToolGun
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolpie.h"
+#include "lltoolview.h"
+#include "llui.h" // for make_ui_sound
+#include "llviewercamera.h"
+#include "llviewermenu.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerparceloverlay.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llviewerwindow.h"
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llworld.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+/////////////////// From llfloater.cpp
+#include "llbutton.h"
+#include "lldraghandle.h"
+#include "llfocusmgr.h"
+#include "llresizebar.h"
+#include "llresizehandle.h"
+#include "llresmgr.h"
+#include "llui.h"
+#include "llviewborder.h"
+#include "llvieweruictrlfactory.h"
+
+
+/////////////////// From lldrawpool.cpp
+#include "llface.h"
+#include "llcontrol.h"
+#include "pipeline.h"
+
+#include "llviewerobjectlist.h" // For debug listing.
+
+//extern LLPipeline gPipeline;
+
+#include "lldrawpoolsimple.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpooltree.h"
+#include "lldrawpooltreenew.h"
+#include "lldrawpoolterrain.h"
+#include "lldrawpoolsky.h"
+#include "lldrawpoolwater.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolbump.h"
+#include "llvotreenew.h"
+
+/////////////////// From llface.cpp
+#include "llgl.h"
+#include "llviewerimagelist.h"
+#include "llsky.h"
+#include "llvosky.h"
+#include "llcontrol.h"
+#include "v3color.h"
+#include "pipeline.h"
+#include "llvolume.h"
+#include "llviewercamera.h"
+#include "lllightconstants.h"
+
+#include "llvovolume.h"
+#include "m3math.h"
+#include "lldrawpoolbump.h"
+
+
+
+/////////////////// From llpanel.cpp
+#include "llpanel.h"
+
+#include "llfontgl.h"
+#include "llrect.h"
+#include "llerror.h"
+#include "lltimer.h"
+
+#include "llmenugl.h"
+#include "llstatusbar.h"
+#include "llui.h"
+#include "llkeyboard.h"
+#include "llviewerwindow.h"
+#include "llcontrol.h"
+#include "lluictrl.h"
+#include "llvieweruictrlfactory.h"
+#include "llviewborder.h"
+#include "llviewerimagelist.h"
+#include "llbutton.h"
+#include "llfocusmgr.h"
+
+
+
+/////////////////// From llvovolume.cpp
+#include "llvovolume.h"
+#include "llviewerimagelist.h"
+
+#include "llcontrol.h"
+
+#include "object_flags.h"
+
+#include "material_codes.h"
+#include "llagent.h"
+#include "llworld.h"
+#include "llviewerregion.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "lldrawable.h"
+#include "llface.h"
+#include "llvolumemgr.h"
+#include "llsky.h"
+
+#include "pipeline.h"
+#include "llmaterialtable.h"
+#include "message.h"
+#include "llviewertextureanim.h"
+#include "llviewercamera.h"
+#include "lldrawpoolbump.h"
+
+
+/////////////////// From llagentpilot.cpp
+#include "llagentpilot.h"
+#include "llagent.h"
+#include "llframestats.h"
+#include "viewer.h"
+#include "llcontrol.h"
+
+
+/////////////////// From llloginview.cpp
+#include "llloginview.h"
+
+#include "indra_constants.h" // for key and mask constants
+#include "llfontgl.h"
+#include "v4color.h"
+#include "llwindow_impl.h" // shell_open()
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "llcombobox.h"
+#include "llcontrol.h"
+#include "lllineeditor.h"
+#include "lltextbox.h"
+#include "llui.h"
+//#include "lluiconstants.h"
+#include "llviewerimagelist.h"
+#include "llviewermenu.h" // for handle_preferences()
+#include "llviewerwindow.h" // to link into child list
+#include "llfocusmgr.h"
+#include "llmd5.h"
+#include "llversion.h"
+#include "viewer.h"
+
diff --git a/indra/newview/noise.cpp b/indra/newview/noise.cpp
new file mode 100644
index 0000000000..d2e837a5a7
--- /dev/null
+++ b/indra/newview/noise.cpp
@@ -0,0 +1,66 @@
+/**
+ * @file noise.cpp
+ * @brief Perlin noise routines for procedural textures, etc
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "noise.h"
+
+#include "llrand.h"
+
+// static
+#define B 0x100
+S32 p[B + B + 2];
+F32 g3[B + B + 2][3];
+F32 g2[B + B + 2][2];
+F32 g1[B + B + 2];
+S32 gNoiseStart = 1;
+
+
+F32 noise2(F32 *vec)
+{
+ U8 bx0, bx1, by0, by1;
+ U32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, *q, sx, sy, a, b, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+
+ fast_setup(*vec, bx0, bx1, rx0, rx1);
+ fast_setup(*(vec + 1), by0, by1, ry0, ry1);
+
+ i = *(p + bx0);
+ j = *(p + bx1);
+
+ b00 = *(p + i + by0);
+ b10 = *(p + j + by0);
+ b01 = *(p + i + by1);
+ b11 = *(p + j + by1);
+
+ sx = s_curve(rx0);
+ sy = s_curve(ry0);
+
+
+ q = *(g2 + b00);
+ u = fast_at2(rx0, ry0, q);
+ q = *(g2 + b10);
+ v = fast_at2(rx1, ry0, q);
+ a = lerp_m(sx, u, v);
+
+ q = *(g2 + b01);
+ u = fast_at2(rx0,ry1,q);
+ q = *(g2 + b11);
+ v = fast_at2(rx1,ry1,q);
+ b = lerp_m(sx, u, v);
+
+ return lerp_m(sy, a, b);
+}
+
diff --git a/indra/newview/noise.h b/indra/newview/noise.h
new file mode 100644
index 0000000000..c436450e41
--- /dev/null
+++ b/indra/newview/noise.h
@@ -0,0 +1,334 @@
+/**
+ * @file noise.h
+ * @brief Perlin noise routines for procedural textures, etc
+ *
+ * Copyright (c) 2000-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_NOISE_H
+#define LL_NOISE_H
+
+#include "llmath.h"
+
+F32 turbulence2(F32 *v, F32 freq);
+F32 turbulence3(float *v, float freq);
+F32 clouds3(float *v, float freq);
+F32 noise2(float *vec);
+F32 noise3(float *vec);
+
+inline F32 bias(F32 a, F32 b)
+{
+ return (F32)pow(a, (F32)(log(b) / log(0.5f)));
+}
+
+inline F32 gain(F32 a, F32 b)
+{
+ F32 p = (F32) (log(1.f - b) / log(0.5f));
+
+ if (a < .001f)
+ return 0.f;
+ else if (a > .999f)
+ return 1.f;
+ if (a < 0.5f)
+ return (F32)(pow(2 * a, p) / 2.f);
+ else
+ return (F32)(1.f - pow(2 * (1.f - a), p) / 2.f);
+}
+
+inline F32 turbulence2(F32 *v, F32 freq)
+{
+ F32 t, vec[2];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ t += noise2(vec)/freq;
+ }
+ return t;
+}
+
+inline F32 turbulence3(F32 *v, F32 freq)
+{
+ F32 t, vec[3];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ vec[2] = freq * v[2];
+ t += noise3(vec)/freq;
+// t += fabs(noise3(vec)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(vec))) / freq; // Better at low freq
+// t += (noise3(vec)*noise3(vec)) / freq;
+ }
+ return t;
+}
+
+inline F32 clouds3(F32 *v, F32 freq)
+{
+ F32 t, vec[3];
+
+ for (t = 0.f ; freq >= 1.f ; freq *= 0.5f) {
+ vec[0] = freq * v[0];
+ vec[1] = freq * v[1];
+ vec[2] = freq * v[2];
+ //t += noise3(vec)/freq;
+// t += fabs(noise3(vec)) / freq; // Like snow - bubbly at low frequencies
+// t += sqrt(fabs(noise3(vec))) / freq; // Better at low freq
+ t += (noise3(vec)*noise3(vec)) / freq;
+ }
+ return t;
+}
+
+/* noise functions over 1, 2, and 3 dimensions */
+
+#define B 0x100
+#define BM 0xff
+
+#define N 0x1000
+#define NF32 (4096.f)
+#define NP 12 /* 2^N */
+#define NM 0xfff
+
+extern S32 p[B + B + 2];
+extern F32 g3[B + B + 2][3];
+extern F32 g2[B + B + 2][2];
+extern F32 g1[B + B + 2];
+extern S32 gNoiseStart;
+
+static void init(void);
+
+#define s_curve(t) ( t * t * (3.f - 2.f * t) )
+
+#define lerp_m(t, a, b) ( a + t * (b - a) )
+
+#define setup_noise(i,b0,b1,r0,r1)\
+ t = vec[i] + N;\
+ b0 = (lltrunc(t)) & BM;\
+ b1 = (b0+1) & BM;\
+ r0 = t - lltrunc(t);\
+ r1 = r0 - 1.f;
+
+
+inline void fast_setup(F32 vec, U8 &b0, U8 &b1, F32 &r0, F32 &r1)
+{
+ S32 t_S32;
+
+ r1 = vec + NF32;
+ t_S32 = lltrunc(r1);
+ b0 = (U8)t_S32;
+ b1 = b0 + 1;
+ r0 = r1 - t_S32;
+ r1 = r0 - 1.f;
+}
+
+inline F32 noise1(const F32 arg)
+{
+ int bx0, bx1;
+ F32 rx0, rx1, sx, t, u, v, vec[1];
+
+ vec[0] = arg;
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ setup_noise(0, bx0,bx1, rx0,rx1);
+
+ sx = s_curve(rx0);
+
+ u = rx0 * g1[ p[ bx0 ] ];
+ v = rx1 * g1[ p[ bx1 ] ];
+
+ return lerp_m(sx, u, v);
+}
+
+inline F32 fast_at2(F32 rx, F32 ry, F32 *q)
+{
+ return rx * (*q) + ry * (*(q + 1));
+}
+
+
+
+inline F32 fast_at3(F32 rx, F32 ry, F32 rz, F32 *q)
+{
+ return rx * (*q) + ry * (*(q + 1)) + rz * (*(q + 2));
+}
+
+
+
+inline F32 noise3(F32 *vec)
+{
+ U8 bx0, bx1, by0, by1, bz0, bz1;
+ S32 b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ fast_setup(*vec, bx0,bx1, rx0,rx1);
+ fast_setup(*(vec + 1), by0,by1, ry0,ry1);
+ fast_setup(*(vec + 2), bz0,bz1, rz0,rz1);
+
+ i = p[ bx0 ];
+ j = p[ bx1 ];
+
+ b00 = p[ i + by0 ];
+ b10 = p[ j + by0 ];
+ b01 = p[ i + by1 ];
+ b11 = p[ j + by1 ];
+
+ t = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+ q = g3[ b00 + bz0 ];
+ u = fast_at3(rx0,ry0,rz0,q);
+ q = g3[ b10 + bz0 ];
+ v = fast_at3(rx1,ry0,rz0,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz0 ];
+ u = fast_at3(rx0,ry1,rz0,q);
+ q = g3[ b11 + bz0 ];
+ v = fast_at3(rx1,ry1,rz0,q);
+ b = lerp_m(t, u, v);
+
+ c = lerp_m(sy, a, b);
+
+ q = g3[ b00 + bz1 ];
+ u = fast_at3(rx0,ry0,rz1,q);
+ q = g3[ b10 + bz1 ];
+ v = fast_at3(rx1,ry0,rz1,q);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz1 ];
+ u = fast_at3(rx0,ry1,rz1,q);
+ q = g3[ b11 + bz1 ];
+ v = fast_at3(rx1,ry1,rz1,q);
+ b = lerp_m(t, u, v);
+
+ d = lerp_m(sy, a, b);
+
+ return lerp_m(sz, c, d);
+}
+
+
+/*
+F32 noise3(F32 *vec)
+{
+ int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
+ F32 rx0, rx1, ry0, ry1, rz0, rz1, *q, sy, sz, a, b, c, d, t, u, v;
+ S32 i, j;
+
+ if (gNoiseStart) {
+ gNoiseStart = 0;
+ init();
+ }
+
+ setup_noise(0, bx0,bx1, rx0,rx1);
+ setup_noise(1, by0,by1, ry0,ry1);
+ setup_noise(2, bz0,bz1, rz0,rz1);
+
+ i = p[ bx0 ];
+ j = p[ bx1 ];
+
+ b00 = p[ i + by0 ];
+ b10 = p[ j + by0 ];
+ b01 = p[ i + by1 ];
+ b11 = p[ j + by1 ];
+
+ t = s_curve(rx0);
+ sy = s_curve(ry0);
+ sz = s_curve(rz0);
+
+#define at3(rx,ry,rz) ( rx * q[0] + ry * q[1] + rz * q[2] )
+
+ q = g3[ b00 + bz0 ] ; u = at3(rx0,ry0,rz0);
+ q = g3[ b10 + bz0 ] ; v = at3(rx1,ry0,rz0);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz0 ] ; u = at3(rx0,ry1,rz0);
+ q = g3[ b11 + bz0 ] ; v = at3(rx1,ry1,rz0);
+ b = lerp_m(t, u, v);
+
+ c = lerp_m(sy, a, b);
+
+ q = g3[ b00 + bz1 ] ; u = at3(rx0,ry0,rz1);
+ q = g3[ b10 + bz1 ] ; v = at3(rx1,ry0,rz1);
+ a = lerp_m(t, u, v);
+
+ q = g3[ b01 + bz1 ] ; u = at3(rx0,ry1,rz1);
+ q = g3[ b11 + bz1 ] ; v = at3(rx1,ry1,rz1);
+ b = lerp_m(t, u, v);
+
+ d = lerp_m(sy, a, b);
+
+ return lerp_m(sz, c, d);
+}
+*/
+
+static void normalize2(F32 v[2])
+{
+ F32 s;
+
+ s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+}
+
+static void normalize3(F32 v[3])
+{
+ F32 s;
+
+ s = 1.f/(F32)sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
+ v[0] = v[0] * s;
+ v[1] = v[1] * s;
+ v[2] = v[2] * s;
+}
+
+static void init(void)
+{
+ int i, j, k;
+
+ for (i = 0 ; i < B ; i++) {
+ p[i] = i;
+
+ g1[i] = (F32)((rand() % (B + B)) - B) / B;
+
+ for (j = 0 ; j < 2 ; j++)
+ g2[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize2(g2[i]);
+
+ for (j = 0 ; j < 3 ; j++)
+ g3[i][j] = (F32)((rand() % (B + B)) - B) / B;
+ normalize3(g3[i]);
+ }
+
+ while (--i) {
+ k = p[i];
+ p[i] = p[j = rand() % B];
+ p[j] = k;
+ }
+
+ for (i = 0 ; i < B + 2 ; i++) {
+ p[B + i] = p[i];
+ g1[B + i] = g1[i];
+ for (j = 0 ; j < 2 ; j++)
+ g2[B + i][j] = g2[i][j];
+ for (j = 0 ; j < 3 ; j++)
+ g3[B + i][j] = g3[i][j];
+ }
+}
+
+#undef B
+#undef BM
+#undef N
+#undef NF32
+#undef NP
+#undef NM
+
+#endif // LL_NOISE_
diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp
new file mode 100644
index 0000000000..8bc9007310
--- /dev/null
+++ b/indra/newview/pipeline.cpp
@@ -0,0 +1,5316 @@
+/**
+ * @file pipeline.cpp
+ * @brief Rendering pipeline.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "pipeline.h"
+
+// library includes
+#include "audioengine.h" // For MAX_BUFFERS for debugging.
+#include "imageids.h"
+#include "llagpmempool.h"
+#include "llerror.h"
+#include "llviewercontrol.h"
+#include "llfasttimer.h"
+#include "llfontgl.h"
+#include "llmemory.h"
+#include "llnamevalue.h"
+#include "llprimitive.h"
+#include "llvolume.h"
+#include "material_codes.h"
+#include "timing.h"
+#include "v3color.h"
+#include "llui.h"
+
+// newview includes
+#include "llagent.h"
+#include "llagparray.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolavatar.h"
+#include "lldrawpoolground.h"
+#include "lldrawpoolsimple.h"
+#include "lldrawpooltree.h"
+#include "lldrawpoolhud.h"
+#include "lldrawpoolwater.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfloatertelehub.h"
+#include "llframestats.h"
+#include "llgldbg.h"
+#include "llhudmanager.h"
+#include "lllightconstants.h"
+#include "llresmgr.h"
+#include "llselectmgr.h"
+#include "llsky.h"
+#include "lltracker.h"
+#include "lltool.h"
+#include "lltoolmgr.h"
+#include "llviewercamera.h"
+#include "llviewerimagelist.h"
+#include "llviewerobject.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h" // for audio debugging.
+#include "llviewerwindow.h" // For getSpinAxis
+#include "llvoavatar.h"
+#include "llvoground.h"
+#include "llvosky.h"
+#include "llvotree.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "viewer.h"
+#include "llagpmempoolarb.h"
+#include "llagparray.inl"
+
+#ifdef _DEBUG
+// Debug indices is disabled for now for debug performance - djs 4/24/02
+//#define DEBUG_INDICES
+#else
+//#define DEBUG_INDICES
+#endif
+
+const F32 BACKLIGHT_DAY_MAGNITUDE_AVATAR = 0.2f;
+const F32 BACKLIGHT_NIGHT_MAGNITUDE_AVATAR = 0.1f;
+const F32 BACKLIGHT_DAY_MAGNITUDE_OBJECT = 0.1f;
+const F32 BACKLIGHT_NIGHT_MAGNITUDE_OBJECT = 0.08f;
+const S32 MAX_ACTIVE_OBJECT_QUIET_FRAMES = 40;
+const S32 MAX_OFFSCREEN_GEOMETRY_CHANGES_PER_FRAME = 10;
+
+// Guess on the number of visible objects in the scene, used to
+// pre-size std::vector and other arrays. JC
+const S32 ESTIMATED_VISIBLE_OBJECT_COUNT = 8192;
+
+// If the sum of the X + Y + Z scale of an object exceeds this number,
+// it will be considered a potential occluder. For instance,
+// a box of size 6 x 6 x 1 has sum 13, which might be an occluder. JC
+const F32 OCCLUDE_SCALE_SUM_THRESHOLD = 8.f;
+
+// Max number of occluders to search for. JC
+const S32 MAX_OCCLUDER_COUNT = 2;
+
+extern S32 gBoxFrame;
+extern BOOL gRenderLightGlows;
+extern BOOL gHideSelectedObjects;
+
+
+BOOL gAvatarBacklight = FALSE;
+
+F32 gMinObjectDistance = MIN_NEAR_PLANE;
+S32 gTrivialAccepts = 0;
+
+BOOL gRenderForSelect = FALSE;
+
+BOOL gUsePickAlpha = TRUE;
+F32 gPickAlphaThreshold = 0.f;
+F32 gPickAlphaTargetThreshold = 0.f;
+
+//glsl parameter tables
+const char* LLPipeline::sReservedAttribs[] =
+{
+ "materialColor",
+ "specularColor",
+ "binormal"
+};
+
+U32 LLPipeline::sReservedAttribCount = LLPipeline::GLSL_END_RESERVED_ATTRIBS;
+
+const char* LLPipeline::sAvatarAttribs[] =
+{
+ "weight",
+ "clothing",
+ "gWindDir",
+ "gSinWaveParams",
+ "gGravity"
+};
+
+U32 LLPipeline::sAvatarAttribCount = sizeof(LLPipeline::sAvatarAttribs)/sizeof(char*);
+
+const char* LLPipeline::sAvatarUniforms[] =
+{
+ "matrixPalette"
+};
+
+U32 LLPipeline::sAvatarUniformCount = 1;
+
+const char* LLPipeline::sReservedUniforms[] =
+{
+ "diffuseMap",
+ "specularMap",
+ "bumpMap",
+ "environmentMap",
+ "scatterMap"
+};
+
+U32 LLPipeline::sReservedUniformCount = LLPipeline::GLSL_END_RESERVED_UNIFORMS;
+
+const char* LLPipeline::sTerrainUniforms[] =
+{
+ "detail0",
+ "detail1",
+ "alphaRamp"
+};
+
+U32 LLPipeline::sTerrainUniformCount = sizeof(LLPipeline::sTerrainUniforms)/sizeof(char*);
+
+const char* LLPipeline::sWaterUniforms[] =
+{
+ "screenTex",
+ "eyeVec",
+ "time",
+ "d1",
+ "d2",
+ "lightDir",
+ "specular",
+ "lightExp",
+ "fbScale",
+ "refScale"
+};
+
+U32 LLPipeline::sWaterUniformCount = sizeof(LLPipeline::sWaterUniforms)/sizeof(char*);
+
+// the SSE variable is dependent on software blending being enabled.
+
+//----------------------------------------
+
+void stamp(F32 x, F32 y, F32 xs, F32 ys)
+{
+ glBegin(GL_QUADS);
+ glTexCoord2f(0,0);
+ glVertex3f(x, y, 0.0f);
+ glTexCoord2f(1,0);
+ glVertex3f(x+xs,y, 0.0f);
+ glTexCoord2f(1,1);
+ glVertex3f(x+xs,y+ys,0.0f);
+ glTexCoord2f(0,1);
+ glVertex3f(x, y+ys,0.0f);
+ glEnd();
+}
+
+
+
+//----------------------------------------
+
+S32 LLPipeline::sCompiles = 0;
+S32 LLPipeline::sAGPMaxPoolSize = 1 << 25; // 32MB
+BOOL LLPipeline::sRenderPhysicalBeacons = FALSE;
+BOOL LLPipeline::sRenderScriptedBeacons = FALSE;
+BOOL LLPipeline::sRenderParticleBeacons = FALSE;
+BOOL LLPipeline::sRenderSoundBeacons = FALSE;
+
+LLPipeline::LLPipeline() :
+ mVertexShadersEnabled(FALSE),
+ mVertexShadersLoaded(0),
+ mLastRebuildPool(NULL),
+ mAlphaPool(NULL),
+ mSkyPool(NULL),
+ mStarsPool(NULL),
+ mCloudsPool(NULL),
+ mTerrainPool(NULL),
+ mWaterPool(NULL),
+ mGroundPool(NULL),
+ mHUDPool(NULL),
+ mAGPMemPool(NULL),
+ mGlobalFence(0),
+ mBufferIndex(0),
+ mBufferCount(kMaxBufferCount),
+ mUseOcclusionCulling(FALSE),
+ mLightMask(0),
+ mLightMovingMask(0)
+{
+ for(S32 i = 0; i < kMaxBufferCount; i++)
+ {
+ mBufferMemory[i] = NULL;
+ }
+ for (S32 i = 0; i < kMaxBufferCount; i++)
+ {
+ mBufferFence[i] = 0;
+ }
+}
+
+void LLPipeline::init()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ stop_glerror();
+
+ mAGPBound = FALSE;
+ mObjectPartition = new LLSpatialPartition;
+ mTrianglesDrawnStat.reset();
+ resetFrameStats();
+
+ mRenderTypeMask = 0xffffffff; // All render types start on
+ mRenderDebugFeatureMask = 0xffffffff; // All debugging features on
+ mRenderFeatureMask = 0; // All features start off
+ mRenderDebugMask = 0; // All debug starts off
+
+ mBackfaceCull = TRUE;
+
+ // Disable AGP initially.
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
+
+ stop_glerror();
+
+ // Enable features
+
+ mUseVBO = gSavedSettings.getBOOL("RenderUseVBO");
+
+ // Allocate the shared buffers for software skinning
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i] = new LLAGPArray<U8>;
+ mBufferMemory[i]->reserve_block(AVATAR_VERTEX_BYTES*AVATAR_BUFFER_ELEMENTS);
+ }
+
+ if (gFeatureManagerp->isFeatureAvailable("RenderAGP"))
+ {
+ setUseAGP(gSavedSettings.getBOOL("RenderUseAGP") && gGLManager.mHasAnyAGP);
+ }
+ else
+ {
+ setUseAGP(FALSE);
+ }
+
+ stop_glerror();
+
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ if (!mBufferMemory[i]->isAGP() && usingAGP())
+ {
+ llwarns << "pipeline buffer memory is non-AGP when AGP available!" << llendl;
+ }
+ }
+
+ setShaders();
+}
+
+void LLPipeline::LLScatterShader::init(GLhandleARB shader, int map_stage)
+{
+ glUseProgramObjectARB(shader);
+ glUniform1iARB(glGetUniformLocationARB(shader, "scatterMap"), map_stage);
+ glUseProgramObjectARB(0);
+}
+
+LLPipeline::~LLPipeline()
+{
+
+}
+
+void LLPipeline::cleanup()
+{
+ for(pool_set_t::iterator iter = mPools.begin();
+ iter != mPools.end(); )
+ {
+ pool_set_t::iterator curiter = iter++;
+ LLDrawPool* poolp = *curiter;
+ if (poolp->mReferences.empty())
+ {
+ mPools.erase(curiter);
+ removeFromQuickLookup( poolp );
+ delete poolp;
+ }
+ }
+
+ if (!mSimplePools.empty())
+ {
+ llwarns << "Simple Pools not cleaned up" << llendl;
+ }
+ if (!mTerrainPools.empty())
+ {
+ llwarns << "Terrain Pools not cleaned up" << llendl;
+ }
+ if (!mTreePools.empty())
+ {
+ llwarns << "Tree Pools not cleaned up" << llendl;
+ }
+ if (!mTreeNewPools.empty())
+ {
+ llwarns << "TreeNew Pools not cleaned up" << llendl;
+ }
+ if (!mBumpPools.empty())
+ {
+ llwarns << "Bump Pools not cleaned up" << llendl;
+ }
+ delete mAlphaPool;
+ mAlphaPool = NULL;
+ delete mSkyPool;
+ mSkyPool = NULL;
+ delete mStarsPool;
+ mStarsPool = NULL;
+ delete mCloudsPool;
+ mCloudsPool = NULL;
+ delete mTerrainPool;
+ mTerrainPool = NULL;
+ delete mWaterPool;
+ mWaterPool = NULL;
+ delete mGroundPool;
+ mGroundPool = NULL;
+ delete mHUDPool;
+ mHUDPool = NULL;
+
+ mBloomImagep = NULL;
+ mBloomImage2p = NULL;
+ mFaceSelectImagep = NULL;
+ mAlphaSizzleImagep = NULL;
+
+ for(S32 i=0; i < mBufferCount; i++)
+ {
+ delete mBufferMemory[i];
+ mBufferMemory[i] = NULL;
+ }
+
+ delete mObjectPartition;
+ mObjectPartition = NULL;
+
+ if (mAGPMemPool && mGlobalFence)
+ {
+ mAGPMemPool->deleteFence(mGlobalFence);
+ mGlobalFence = 0;
+ }
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+}
+
+//============================================================================
+
+BOOL LLPipeline::initAGP()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ mAGPMemPool = LLAGPMemPool::createPool(sAGPMaxPoolSize, mUseVBO);
+
+ if (!mAGPMemPool)
+ {
+ llinfos << "Warning! Couldn't allocate AGP memory!" << llendl;
+ llinfos << "Disabling AGP!" << llendl;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+ else if (!mAGPMemPool->getSize())
+ {
+ llinfos << "Warning! Unable to allocate AGP memory! Disabling AGP" << llendl;
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+ else
+ {
+ llinfos << "Allocated " << mAGPMemPool->getSize() << " bytes of AGP memory" << llendl;
+ mAGPMemPool->bind();
+
+ if (mAGPMemPool->getSize() < MIN_AGP_SIZE)
+ {
+ llwarns << "Not enough AGP memory!" << llendl;
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP; // Need to disable the using AGP flag
+ return FALSE;
+ }
+
+
+ if (mAGPMemPool)
+ {
+ // Create the fence that we use for global synchronization.
+ mGlobalFence = mAGPMemPool->createFence();
+ }
+ return TRUE;
+ }
+
+}
+
+void LLPipeline::cleanupAGP()
+{
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i]->deleteFence(mBufferFence[i]);
+ mBufferMemory[i]->setUseAGP(FALSE);
+ }
+
+ flushAGPMemory();
+ if (mAGPMemPool && mGlobalFence)
+ {
+ mAGPMemPool->deleteFence(mGlobalFence);
+ mGlobalFence = 0;
+ }
+ delete mAGPMemPool;
+ mAGPMemPool = NULL;
+}
+
+BOOL LLPipeline::usingAGP() const
+{
+ return (mRenderFeatureMask & RENDER_FEATURE_AGP) ? TRUE : FALSE;
+}
+
+void LLPipeline::setUseAGP(const BOOL use_agp)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (use_agp == usingAGP())
+ {
+ return;
+ }
+ else if (use_agp)
+ {
+ mRenderFeatureMask |= RENDER_FEATURE_AGP;
+ initAGP();
+
+ // Forces us to allocate an AGP memory block immediately.
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ mBufferMemory[i]->setUseAGP(use_agp);
+ mBufferMemory[i]->realloc(mBufferMemory[i]->getMax());
+ mBufferFence[i] = mBufferMemory[i]->createFence();
+ }
+
+ // Must be done AFTER you initialize AGP
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->setUseAGP(use_agp);
+ }
+ }
+ else
+ {
+ unbindAGP();
+ mRenderFeatureMask &= ~RENDER_FEATURE_AGP;
+
+ // Must be done BEFORE you blow away AGP
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->setUseAGP(use_agp);
+ }
+
+ int i;
+ for(i=0; i < mBufferCount; i++)
+ {
+ if (mBufferMemory[i])
+ {
+ mBufferMemory[i]->setUseAGP(use_agp);
+ mBufferMemory[i]->deleteFence(mBufferFence[i]);
+ mBufferFence[i] = 0;
+ }
+ else
+ {
+ llerrs << "setUseAGP without buffer memory" << llendl;
+ }
+ }
+
+ cleanupAGP();
+ }
+
+}
+
+//============================================================================
+
+void LLPipeline::destroyGL()
+{
+ setUseAGP(FALSE);
+ stop_glerror();
+ unloadShaders();
+ mHighlightFaces.reset();
+}
+
+void LLPipeline::restoreGL()
+{
+ if (mVertexShadersEnabled)
+ {
+ setShaders();
+ }
+
+ if (mObjectPartition)
+ {
+ mObjectPartition->restoreGL();
+ }
+}
+
+//============================================================================
+// Load Shader
+
+static LLString get_object_log(GLhandleARB ret)
+{
+ LLString res;
+
+ //get log length
+ GLint length;
+ glGetObjectParameterivARB(ret, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
+ if (length > 0)
+ {
+ //the log could be any size, so allocate appropriately
+ GLcharARB* log = new GLcharARB[length];
+ glGetInfoLogARB(ret, length, &length, log);
+ res = LLString(log);
+ delete[] log;
+ }
+ return res;
+}
+
+void LLPipeline::dumpObjectLog(GLhandleARB ret, BOOL warns)
+{
+ LLString log = get_object_log(ret);
+ if (warns)
+ {
+ llwarns << log << llendl;
+ }
+ else
+ {
+ llinfos << log << llendl;
+ }
+}
+
+GLhandleARB LLPipeline::loadShader(const LLString& filename, S32 cls, GLenum type)
+{
+ GLenum error;
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR entering loadShader(): " << error << llendl;
+ }
+
+ llinfos << "Loading shader file: " << filename << llendl;
+
+ if (filename.empty())
+ {
+ return 0;
+ }
+
+
+ //read in from file
+ FILE* file = NULL;
+
+ S32 try_gpu_class = mVertexShaderLevel[cls];
+ S32 gpu_class;
+
+ //find the most relevant file
+ for (gpu_class = try_gpu_class; gpu_class > 0; gpu_class--)
+ { //search from the current gpu class down to class 1 to find the most relevant shader
+ std::stringstream fname;
+ fname << gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "shaders/class");
+ fname << gpu_class << "/" << filename;
+
+ llinfos << "Looking in " << fname.str().c_str() << llendl;
+ file = fopen(fname.str().c_str(), "r");
+ if (file)
+ {
+ break; // done
+ }
+ }
+
+ if (file == NULL)
+ {
+ llinfos << "GLSL Shader file not found: " << filename << llendl;
+ return 0;
+ }
+
+ //we can't have any lines longer than 1024 characters
+ //or any shaders longer than 1024 lines... deal - DaveP
+ GLcharARB buff[1024];
+ GLcharARB* text[1024];
+ GLuint count = 0;
+
+ //copy file into memory
+ while(fgets(buff, 1024, file) != NULL)
+ {
+ text[count++] = strdup(buff);
+ }
+ fclose(file);
+
+ //create shader object
+ GLhandleARB ret = glCreateShaderObjectARB(type);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glCreateShaderObjectARB: " << error << llendl;
+ }
+ else
+ {
+ //load source
+ glShaderSourceARB(ret, count, (const GLcharARB**) text, NULL);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glShaderSourceARB: " << error << llendl;
+ }
+ else
+ {
+ //compile source
+ glCompileShaderARB(ret);
+ error = glGetError();
+ if (error != GL_NO_ERROR)
+ {
+ llwarns << "GL ERROR in glCompileShaderARB: " << error << llendl;
+ }
+ }
+ }
+ //free memory
+ for (GLuint i = 0; i < count; i++)
+ {
+ free(text[i]);
+ }
+ if (error == GL_NO_ERROR)
+ {
+ //check for errors
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(ret, GL_OBJECT_COMPILE_STATUS_ARB, &success);
+ error = glGetError();
+ if (error != GL_NO_ERROR || success == GL_FALSE)
+ {
+ //an error occured, print log
+ llwarns << "GLSL Compilation Error: (" << error << ") in " << filename << llendl;
+ dumpObjectLog(ret);
+ ret = 0;
+ }
+ }
+ else
+ {
+ ret = 0;
+ }
+ stop_glerror();
+
+ //successfully loaded, save results
+#if 1 // 1.9.1
+ if (ret)
+ {
+ mVertexShaderLevel[cls] = try_gpu_class;
+ }
+ else
+ {
+ if (mVertexShaderLevel[cls] > 1)
+ {
+ mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1;
+ ret = loadShader(filename,cls,type);
+ if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls])
+ {
+ mMaxVertexShaderLevel[cls] = mVertexShaderLevel[cls];
+ }
+ }
+ }
+#else
+ if (ret)
+ {
+ S32 max = -1;
+ /*if (try_gpu_class == mMaxVertexShaderLevel[cls])
+ {
+ max = gpu_class;
+ }*/
+ saveVertexShaderLevel(cls,try_gpu_class,max);
+ }
+ else
+ {
+ if (mVertexShaderLevel[cls] > 1)
+ {
+ mVertexShaderLevel[cls] = mVertexShaderLevel[cls] - 1;
+ ret = loadShader(f,cls,type);
+ if (ret && mMaxVertexShaderLevel[cls] > mVertexShaderLevel[cls])
+ {
+ saveVertexShaderLevel(cls, mVertexShaderLevel[cls], mVertexShaderLevel[cls]);
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+BOOL LLPipeline::linkProgramObject(GLhandleARB obj, BOOL suppress_errors)
+{
+ //check for errors
+ glLinkProgramARB(obj);
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(obj, GL_OBJECT_LINK_STATUS_ARB, &success);
+ if (!suppress_errors && success == GL_FALSE)
+ {
+ //an error occured, print log
+ llwarns << "GLSL Linker Error:" << llendl;
+ }
+
+ LLString log = get_object_log(obj);
+ LLString::toLower(log);
+ if (log.find("software") != LLString::npos)
+ {
+ llwarns << "GLSL Linker: Running in Software:" << llendl;
+ success = GL_FALSE;
+ suppress_errors = FALSE;
+ }
+ if (!suppress_errors)
+ {
+ dumpObjectLog(obj, !success);
+ }
+
+ return success;
+}
+
+BOOL LLPipeline::validateProgramObject(GLhandleARB obj)
+{
+ //check program validity against current GL
+ glValidateProgramARB(obj);
+ GLint success = GL_TRUE;
+ glGetObjectParameterivARB(obj, GL_OBJECT_VALIDATE_STATUS_ARB, &success);
+ if (success == GL_FALSE)
+ {
+ llwarns << "GLSL program not valid: " << llendl;
+ dumpObjectLog(obj);
+ }
+ else
+ {
+ dumpObjectLog(obj, FALSE);
+ }
+
+ return success;
+}
+
+//============================================================================
+// Shader Management
+
+void LLPipeline::setShaders()
+{
+ if (gViewerWindow)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_WAIT);
+ }
+
+ // Lighting
+ setLightingDetail(-1);
+
+ // Shaders
+ for (S32 i=0; i<SHADER_COUNT; i++)
+ {
+ mVertexShaderLevel[i] = 0;
+ mMaxVertexShaderLevel[i] = 0;
+ }
+ if (canUseVertexShaders())
+ {
+ S32 light_class = 2;
+ S32 env_class = 2;
+ if (getLightingDetail() == 0)
+ {
+ light_class = 1;
+ }
+ // Load lighting shaders
+ mVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mMaxVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
+ mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = env_class;
+ BOOL loaded = loadShadersLighting();
+ if (loaded)
+ {
+ mVertexShadersEnabled = TRUE;
+ mVertexShadersLoaded = 1;
+
+ // Load all shaders to set max levels
+ loadShadersEnvironment();
+
+ // Load max avatar shaders to set the max level
+ mVertexShaderLevel[SHADER_AVATAR] = 3;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 3;
+ loadShadersAvatar();
+
+ // Load shaders to correct levels
+ if (!gSavedSettings.getBOOL("RenderRippleWater"))
+ {
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ loadShadersEnvironment(); // unloads
+ }
+
+#if LL_DARWIN // force avatar shaders off for mac
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 0;
+#else
+ if (gSavedSettings.getBOOL("RenderAvatarVP"))
+ {
+ S32 avatar = gSavedSettings.getS32("RenderAvatarMode");
+ S32 avatar_class = 1 + avatar;
+ // Set the actual level
+ mVertexShaderLevel[SHADER_AVATAR] = avatar_class;
+ loadShadersAvatar();
+ if (mVertexShaderLevel[SHADER_AVATAR] != avatar_class)
+ {
+ if (mVertexShaderLevel[SHADER_AVATAR] == 0)
+ {
+ gSavedSettings.setBOOL("RenderAvatarVP", FALSE);
+ }
+ avatar = llmax(mVertexShaderLevel[SHADER_AVATAR]-1,0);
+ gSavedSettings.setS32("RenderAvatarMode", avatar);
+ }
+ }
+ else
+ {
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ gSavedSettings.setS32("RenderAvatarMode", 0);
+ loadShadersAvatar(); // unloads
+ }
+#endif
+ }
+ else
+ {
+ mVertexShadersEnabled = FALSE;
+ mVertexShadersLoaded = 0;
+ }
+ }
+ if (gViewerWindow)
+ {
+ gViewerWindow->setCursor(UI_CURSOR_ARROW);
+ }
+}
+
+BOOL LLPipeline::canUseVertexShaders()
+{
+ if (!gGLManager.mHasVertexShader ||
+ !gGLManager.mHasFragmentShader ||
+ !gFeatureManagerp->isFeatureAvailable("VertexShaderEnable") ||
+ mVertexShadersLoaded == -1)
+ {
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+void LLPipeline::unloadShaders()
+{
+ mObjectSimpleProgram.unload();
+ mObjectBumpProgram.unload();
+ mObjectAlphaProgram.unload();
+ mWaterProgram.unload();
+ mTerrainProgram.unload();
+ mGroundProgram.unload();
+ mAvatarProgram.unload();
+ mAvatarEyeballProgram.unload();
+ mAvatarPickProgram.unload();
+ mHighlightProgram.unload();
+
+ mVertexShaderLevel[SHADER_LIGHTING] = 0;
+ mVertexShaderLevel[SHADER_OBJECT] = 0;
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ mVertexShaderLevel[SHADER_INTERFACE] = 0;
+
+ mLightVertex = mLightFragment = mScatterVertex = mScatterFragment = 0;
+ mVertexShadersLoaded = 0;
+}
+
+#if 0 // 1.9.2
+// Any time shader options change
+BOOL LLPipeline::loadShaders()
+{
+ unloadShaders();
+
+ if (!canUseVertexShaders())
+ {
+ return FALSE;
+ }
+
+ S32 light_class = mMaxVertexShaderLevel[SHADER_LIGHTING];
+ if (getLightingDetail() == 0)
+ {
+ light_class = 1; // Use minimum lighting shader
+ }
+ else if (getLightingDetail() == 1)
+ {
+ light_class = 2; // Use medium lighting shader
+ }
+ mVertexShaderLevel[SHADER_LIGHTING] = light_class;
+ mVertexShaderLevel[SHADER_OBJECT] = llmin(mMaxVertexShaderLevel[SHADER_OBJECT], gSavedSettings.getS32("VertexShaderLevelObject"));
+ mVertexShaderLevel[SHADER_AVATAR] = llmin(mMaxVertexShaderLevel[SHADER_AVATAR], gSavedSettings.getS32("VertexShaderLevelAvatar"));
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = llmin(mMaxVertexShaderLevel[SHADER_ENVIRONMENT], gSavedSettings.getS32("VertexShaderLevelEnvironment"));
+ mVertexShaderLevel[SHADER_INTERFACE] = mMaxVertexShaderLevel[SHADER_INTERFACE];
+
+ BOOL loaded = loadShadersLighting();
+ if (loaded)
+ {
+ loadShadersEnvironment(); // Must load this before object/avatar for scatter
+ loadShadersObject();
+ loadShadersAvatar();
+ loadShadersInterface();
+ mVertexShadersLoaded = 1;
+ }
+ else
+ {
+ unloadShaders();
+ mVertexShadersEnabled = FALSE;
+ mVertexShadersLoaded = 0; //-1; // -1 = failed
+ setLightingDetail(-1);
+ }
+
+ return loaded;
+}
+#endif
+
+BOOL LLPipeline::loadShadersLighting()
+{
+ // Load light dependency shaders first
+ // All of these have to load for any shaders to function
+
+ std::string lightvertex = "lighting/lightV.glsl";
+ //get default light function implementation
+ mLightVertex = loadShader(lightvertex, SHADER_LIGHTING, GL_VERTEX_SHADER_ARB);
+ if( !mLightVertex )
+ {
+ llwarns << "Failed to load " << lightvertex << llendl;
+ return FALSE;
+ }
+
+ std::string lightfragment = "lighting/lightF.glsl";
+ mLightFragment = loadShader(lightfragment, SHADER_LIGHTING, GL_FRAGMENT_SHADER_ARB);
+ if ( !mLightFragment )
+ {
+ llwarns << "Failed to load " << lightfragment << llendl;
+ return FALSE;
+ }
+
+ // NOTE: Scatter shaders use the ENVIRONMENT detail level
+
+ std::string scattervertex = "environment/scatterV.glsl";
+ mScatterVertex = loadShader(scattervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB);
+ if ( !mScatterVertex )
+ {
+ llwarns << "Failed to load " << scattervertex << llendl;
+ return FALSE;
+ }
+
+ std::string scatterfragment = "environment/scatterF.glsl";
+ mScatterFragment = loadShader(scatterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB);
+ if ( !mScatterFragment )
+ {
+ llwarns << "Failed to load " << scatterfragment << llendl;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersEnvironment()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_ENVIRONMENT] == 0)
+ {
+ mWaterProgram.unload();
+ mGroundProgram.unload();
+ mTerrainProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load water vertex shader
+ std::string waterfragment = "environment/waterF.glsl";
+ std::string watervertex = "environment/waterV.glsl";
+ mWaterProgram.mProgramObject = glCreateProgramObjectARB();
+ mWaterProgram.attachObjects(baseObjects, baseCount);
+ mWaterProgram.attachObject(loadShader(watervertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mWaterProgram.attachObject(loadShader(waterfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mWaterProgram.mapAttributes();
+ if (success)
+ {
+ success = mWaterProgram.mapUniforms(sWaterUniforms, sWaterUniformCount);
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << watervertex << llendl;
+ }
+ }
+ if (success)
+ {
+ //load ground vertex shader
+ std::string groundvertex = "environment/groundV.glsl";
+ std::string groundfragment = "environment/groundF.glsl";
+ mGroundProgram.mProgramObject = glCreateProgramObjectARB();
+ mGroundProgram.attachObjects(baseObjects, baseCount);
+ mGroundProgram.attachObject(loadShader(groundvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mGroundProgram.attachObject(loadShader(groundfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mGroundProgram.mapAttributes();
+ if (success)
+ {
+ success = mGroundProgram.mapUniforms();
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << groundvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load terrain vertex shader
+ std::string terrainvertex = "environment/terrainV.glsl";
+ std::string terrainfragment = "environment/terrainF.glsl";
+ mTerrainProgram.mProgramObject = glCreateProgramObjectARB();
+ mTerrainProgram.attachObjects(baseObjects, baseCount);
+ mTerrainProgram.attachObject(loadShader(terrainvertex, SHADER_ENVIRONMENT, GL_VERTEX_SHADER_ARB));
+ mTerrainProgram.attachObject(loadShader(terrainfragment, SHADER_ENVIRONMENT, GL_FRAGMENT_SHADER_ARB));
+ success = mTerrainProgram.mapAttributes();
+ if (success)
+ {
+ success = mTerrainProgram.mapUniforms(sTerrainUniforms, sTerrainUniformCount);
+ }
+ if (!success)
+ {
+ llwarns << "Failed to load " << terrainvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ mMaxVertexShaderLevel[SHADER_ENVIRONMENT] = 0;
+ return FALSE;
+ }
+
+ if (gWorldPointer)
+ {
+ gWorldPointer->updateWaterObjects();
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersObject()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_OBJECT] == 0)
+ {
+ mObjectSimpleProgram.unload();
+ mObjectBumpProgram.unload();
+ mObjectAlphaProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load object (volume/tree) vertex shader
+ std::string simplevertex = "objects/simpleV.glsl";
+ std::string simplefragment = "objects/simpleF.glsl";
+ mObjectSimpleProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectSimpleProgram.attachObjects(baseObjects, baseCount);
+ mObjectSimpleProgram.attachObject(loadShader(simplevertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectSimpleProgram.attachObject(loadShader(simplefragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+ success = mObjectSimpleProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectSimpleProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << simplevertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load object bumpy vertex shader
+ std::string bumpshinyvertex = "objects/bumpshinyV.glsl";
+ std::string bumpshinyfragment = "objects/bumpshinyF.glsl";
+ mObjectBumpProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectBumpProgram.attachObjects(baseObjects, baseCount);
+ mObjectBumpProgram.attachObject(loadShader(bumpshinyvertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectBumpProgram.attachObject(loadShader(bumpshinyfragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+ success = mObjectBumpProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectBumpProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << bumpshinyvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load object alpha vertex shader
+ std::string alphavertex = "objects/alphaV.glsl";
+ std::string alphafragment = "objects/alphaF.glsl";
+ mObjectAlphaProgram.mProgramObject = glCreateProgramObjectARB();
+ mObjectAlphaProgram.attachObjects(baseObjects, baseCount);
+ mObjectAlphaProgram.attachObject(loadShader(alphavertex, SHADER_OBJECT, GL_VERTEX_SHADER_ARB));
+ mObjectAlphaProgram.attachObject(loadShader(alphafragment, SHADER_OBJECT, GL_FRAGMENT_SHADER_ARB));
+
+ success = mObjectAlphaProgram.mapAttributes();
+ if (success)
+ {
+ success = mObjectAlphaProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << alphavertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_OBJECT] = 0;
+ mMaxVertexShaderLevel[SHADER_OBJECT] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersAvatar()
+{
+ GLhandleARB baseObjects[] =
+ {
+ mLightFragment,
+ mLightVertex,
+ mScatterFragment,
+ mScatterVertex
+ };
+ S32 baseCount = 4;
+
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_AVATAR] == 0)
+ {
+ mAvatarProgram.unload();
+ mAvatarEyeballProgram.unload();
+ mAvatarPickProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load specular (eyeball) vertex program
+ std::string eyeballvertex = "avatar/eyeballV.glsl";
+ std::string eyeballfragment = "avatar/eyeballF.glsl";
+ mAvatarEyeballProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarEyeballProgram.attachObjects(baseObjects, baseCount);
+ mAvatarEyeballProgram.attachObject(loadShader(eyeballvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarEyeballProgram.attachObject(loadShader(eyeballfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+ success = mAvatarEyeballProgram.mapAttributes();
+ if (success)
+ {
+ success = mAvatarEyeballProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << eyeballvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ mAvatarSkinVertex = loadShader("avatar/avatarSkinV.glsl", SHADER_AVATAR, GL_VERTEX_SHADER_ARB);
+ //load avatar vertex shader
+ std::string avatarvertex = "avatar/avatarV.glsl";
+ std::string avatarfragment = "avatar/avatarF.glsl";
+
+ mAvatarProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarProgram.attachObjects(baseObjects, baseCount);
+ mAvatarProgram.attachObject(mAvatarSkinVertex);
+ mAvatarProgram.attachObject(loadShader(avatarvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarProgram.attachObject(loadShader(avatarfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+
+ success = mAvatarProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount);
+ if (success)
+ {
+ success = mAvatarProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount);
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << avatarvertex << llendl;
+ }
+ }
+
+ if (success)
+ {
+ //load avatar picking shader
+ std::string pickvertex = "avatar/pickAvatarV.glsl";
+ std::string pickfragment = "avatar/pickAvatarF.glsl";
+ mAvatarPickProgram.mProgramObject = glCreateProgramObjectARB();
+ mAvatarPickProgram.attachObject(loadShader(pickvertex, SHADER_AVATAR, GL_VERTEX_SHADER_ARB));
+ mAvatarPickProgram.attachObject(loadShader(pickfragment, SHADER_AVATAR, GL_FRAGMENT_SHADER_ARB));
+ mAvatarPickProgram.attachObject(mAvatarSkinVertex);
+
+ success = mAvatarPickProgram.mapAttributes(sAvatarAttribs, sAvatarAttribCount);
+ if (success)
+ {
+ success = mAvatarPickProgram.mapUniforms(sAvatarUniforms, sAvatarUniformCount);
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << pickvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_AVATAR] = 0;
+ mMaxVertexShaderLevel[SHADER_AVATAR] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+BOOL LLPipeline::loadShadersInterface()
+{
+ BOOL success = TRUE;
+
+ if (mVertexShaderLevel[SHADER_INTERFACE] == 0)
+ {
+ mHighlightProgram.unload();
+ return FALSE;
+ }
+
+ if (success)
+ {
+ //load highlighting shader
+ std::string highlightvertex = "interface/highlightV.glsl";
+ std::string highlightfragment = "interface/highlightF.glsl";
+ mHighlightProgram.mProgramObject = glCreateProgramObjectARB();
+ mHighlightProgram.attachObject(loadShader(highlightvertex, SHADER_INTERFACE, GL_VERTEX_SHADER_ARB));
+ mHighlightProgram.attachObject(loadShader(highlightfragment, SHADER_INTERFACE, GL_FRAGMENT_SHADER_ARB));
+
+ success = mHighlightProgram.mapAttributes();
+ if (success)
+ {
+ success = mHighlightProgram.mapUniforms();
+ }
+ if( !success )
+ {
+ llwarns << "Failed to load " << highlightvertex << llendl;
+ }
+ }
+
+ if( !success )
+ {
+ mVertexShaderLevel[SHADER_INTERFACE] = 0;
+ mMaxVertexShaderLevel[SHADER_INTERFACE] = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+//============================================================================
+
+void LLPipeline::enableShadows(const BOOL enable_shadows)
+{
+ //should probably do something here to wrangle shadows....
+}
+
+S32 LLPipeline::getMaxLightingDetail() const
+{
+ if (mVertexShaderLevel[SHADER_OBJECT] >= LLDrawPoolSimple::SHADER_LEVEL_LOCAL_LIGHTS)
+ {
+ return 3;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+S32 LLPipeline::setLightingDetail(S32 level)
+{
+ if (level < 0)
+ {
+ level = gSavedSettings.getS32("RenderLightingDetail");
+ }
+ level = llclamp(level, 0, getMaxLightingDetail());
+ if (level != mLightingDetail)
+ {
+ gSavedSettings.setS32("RenderLightingDetail", level);
+ if (level >= 2)
+ {
+ gObjectList.relightAllObjects();
+ }
+ mLightingDetail = level;
+
+ if (mVertexShadersLoaded == 1)
+ {
+ gPipeline.setShaders();
+ }
+ }
+ return mLightingDetail;
+}
+
+LLAGPMemBlock *LLPipeline::allocAGPFromPool(const S32 bytes, const U32 target)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (!mAGPMemPool)
+ {
+ llwarns << "Attempting to allocate AGP memory when AGP disabled!" << llendl;
+ return NULL;
+ }
+ else
+ {
+ if (mUseVBO)
+ {
+ return ((LLAGPMemPoolARB*) mAGPMemPool)->allocBlock(bytes, target);
+ }
+ else
+ {
+ return mAGPMemPool->allocBlock(bytes);
+ }
+ }
+}
+
+
+void LLPipeline::unbindAGP()
+{
+ if (mAGPMemPool && mAGPBound)
+ {
+ mAGPMemPool->disable();
+ mAGPBound = FALSE;
+ }
+}
+
+void LLPipeline::bindAGP()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (mAGPMemPool && !mAGPBound && usingAGP())
+ {
+ mAGPMemPool->enable();
+ mAGPBound = TRUE;
+ }
+}
+
+U8* LLPipeline::bufferGetScratchMemory(void)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ return(mBufferMemory[mBufferIndex]->getScratchMemory());
+}
+
+void LLPipeline::bufferWaitFence(void)
+{
+ mBufferMemory[mBufferIndex]->waitFence(mBufferFence[mBufferIndex]);
+}
+
+void LLPipeline::bufferSendFence(void)
+{
+ mBufferMemory[mBufferIndex]->sendFence(mBufferFence[mBufferIndex]);
+}
+
+void LLPipeline::bufferRotate(void)
+{
+ mBufferIndex++;
+ if(mBufferIndex >= mBufferCount)
+ mBufferIndex = 0;
+}
+
+// Called when a texture changes # of channels (rare, may cause faces to move to alpha pool)
+void LLPipeline::dirtyPoolObjectTextures(const LLViewerImage *texturep)
+{
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->dirtyTexture(texturep);
+ }
+}
+
+LLDrawPool *LLPipeline::findPool(const U32 type, LLViewerImage *tex0)
+{
+ LLDrawPool *poolp = NULL;
+ switch( type )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ poolp = get_if_there(mSimplePools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ poolp = get_if_there(mTreePools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ poolp = get_if_there(mTreeNewPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ poolp = get_if_there(mTerrainPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ poolp = get_if_there(mBumpPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ poolp = get_if_there(mMediaPools, (uintptr_t)tex0, (LLDrawPool*)0 );
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ poolp = mAlphaPool;
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ poolp = mSkyPool;
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ poolp = mStarsPool;
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ poolp = mCloudsPool;
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ poolp = mWaterPool;
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ poolp = mGroundPool;
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ poolp = mHUDPool;
+ break;
+
+ default:
+ llassert(0);
+ llerrs << "Invalid Pool Type in LLPipeline::findPool() type=" << type << llendl;
+ break;
+ }
+
+ return poolp;
+}
+
+
+LLDrawPool *LLPipeline::getPool(const U32 type, LLViewerImage *tex0)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLDrawPool *poolp = findPool(type, tex0);
+ if (poolp)
+ {
+ return poolp;
+ }
+
+ LLDrawPool *new_poolp = LLDrawPool::createPool(type, tex0);
+ addPool( new_poolp );
+
+ return new_poolp;
+}
+
+
+// static
+LLDrawPool* LLPipeline::getPoolFromTE(const LLTextureEntry* te, LLViewerImage* imagep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ bool alpha = te->getColor().mV[3] < 0.999f;
+ if (imagep)
+ {
+ alpha = alpha || (imagep->getComponents() == 4) || (imagep->getComponents() == 2);
+ }
+
+ if (te->getMediaFlags() == LLTextureEntry::MF_WEB_PAGE)
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_MEDIA, imagep);
+ }
+ else if (alpha)
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_ALPHA);
+ }
+ else if ((te->getBumpmap() || te->getShiny()))
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_BUMP, imagep);
+ }
+ else
+ {
+ return gPipeline.getPool(LLDrawPool::POOL_SIMPLE, imagep);
+ }
+}
+
+
+void LLPipeline::addPool(LLDrawPool *new_poolp)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ mPools.insert(new_poolp);
+ addToQuickLookup( new_poolp );
+}
+
+void LLPipeline::allocDrawable(LLViewerObject *vobj)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+ LLDrawable *drawable = new LLDrawable();
+ vobj->mDrawable = drawable;
+
+ drawable->mVObjp = vobj;
+
+ //encompass completely sheared objects by taking
+ //the most extreme point possible (<1,1,0.5>)
+ drawable->setRadius(LLVector3(1,1,0.5f).scaleVec(vobj->getScale()).magVec());
+ if (vobj->isOrphaned())
+ {
+ drawable->setState(LLDrawable::FORCE_INVISIBLE);
+ }
+ drawable->updateXform(TRUE);
+}
+
+
+void LLPipeline::unlinkDrawable(LLDrawable *drawablep)
+{
+ LLFastTimer t(LLFastTimer::FTM_PIPELINE);
+
+ // Based on flags, remove the drawable from the queues that it's on.
+ if (drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.erase(drawablep);
+ }
+
+ if (drawablep->getSpatialGroup())
+ {
+ if (!drawablep->getSpatialGroup()->mSpatialPartition->remove(drawablep, drawablep->getSpatialGroup()))
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Couldn't remove object from spatial group!" << llendl;
+#else
+ llerrs << "Couldn't remove object from spatial group!" << llendl;
+#endif
+ }
+ }
+
+ mLights.erase(drawablep);
+}
+
+U32 LLPipeline::addObject(LLViewerObject *vobj)
+{
+ LLMemType mt(LLMemType::MTYPE_DRAWABLE);
+ if (gNoRender)
+ {
+ return 0;
+ }
+
+ LLDrawable *drawablep = vobj->createDrawable(this);
+
+ llassert(drawablep);
+
+ //mCompleteSet.put(drawable);
+ //gResyncObjects = TRUE;
+
+ if (vobj->getParent())
+ {
+ vobj->setDrawableParent(((LLViewerObject*)vobj->getParent())->mDrawable); // LLPipeline::addObject 1
+ }
+ else
+ {
+ vobj->setDrawableParent(NULL); // LLPipeline::addObject 2
+ }
+
+
+ if ((!drawablep->getVOVolume()) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_SKY) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_STARS) &&
+ (vobj->getPCode() != LLViewerObject::LL_VO_GROUND))
+ {
+ drawablep->getSpatialPartition()->put(drawablep);
+ if (!drawablep->getSpatialGroup())
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ llwarns << "Failure adding drawable to object partition!" << llendl;
+#else
+ llerrs << "Failure adding drawable to object partition!" << llendl;
+#endif
+ }
+ }
+ else
+ {
+ markMoved(drawablep);
+ }
+
+ markMaterialed(drawablep);
+ markRebuild(drawablep, LLDrawable::REBUILD_ALL, TRUE);
+
+ return 1;
+}
+
+
+void LLPipeline::resetFrameStats()
+{
+ sCompiles = 0;
+ mVerticesRelit = 0;
+ mLightingChanges = 0;
+ mGeometryChanges = 0;
+ mNumVisibleFaces = 0;
+}
+
+//external functions for asynchronous updating
+void LLPipeline::updateMoveDampedAsync(LLDrawable* drawablep)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+ if (!drawablep)
+ {
+ llerrs << "updateMove called with NULL drawablep" << llendl;
+ }
+ if (drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ return;
+ }
+ // update drawable now
+ drawablep->clearState(LLDrawable::MOVE_UNDAMPED); // force to DAMPED
+ drawablep->updateMove(); // returns done
+ drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame
+ // Put on move list so that EARLY_MOVE gets cleared
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+}
+
+void LLPipeline::updateMoveNormalAsync(LLDrawable* drawablep)
+{
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+ if (!drawablep)
+ {
+ llerrs << "updateMove called with NULL drawablep" << llendl;
+ }
+ if (drawablep->isState(LLDrawable::EARLY_MOVE))
+ {
+ return;
+ }
+ // update drawable now
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // force to UNDAMPED
+ drawablep->updateMove();
+ drawablep->setState(LLDrawable::EARLY_MOVE); // flag says we already did an undamped move this frame
+ // Put on move list so that EARLY_MOVE gets cleared
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+}
+
+void LLPipeline::updateMove()
+{
+ mObjectPartition->mOctree->validate();
+ LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ if (gSavedSettings.getBOOL("FreezeTime"))
+ {
+ return;
+ }
+
+ mMoveChangesStat.addValue((F32)mMovedList.size());
+
+ for (LLDrawable::drawable_set_t::iterator iter = mMovedList.begin();
+ iter != mMovedList.end(); )
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable *drawablep = *curiter;
+ BOOL done = TRUE;
+ if (!drawablep->isDead() && (!drawablep->isState(LLDrawable::EARLY_MOVE)))
+ {
+ done = drawablep->updateMove();
+ }
+ drawablep->clearState(LLDrawable::EARLY_MOVE | LLDrawable::MOVE_UNDAMPED);
+ if (done)
+ {
+ mMovedList.erase(curiter);
+ drawablep->clearState(LLDrawable::ON_MOVE_LIST);
+ }
+ }
+
+ for (LLDrawable::drawable_set_t::iterator iter = mActiveQ.begin();
+ iter != mActiveQ.end(); )
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ if (drawablep && !drawablep->isDead())
+ {
+ if (drawablep->mQuietCount++ > MAX_ACTIVE_OBJECT_QUIET_FRAMES &&
+ (!drawablep->getParent() || !drawablep->getParent()->isActive()))
+ {
+ drawablep->makeStatic(); // removes drawable and its children from mActiveQ
+ iter = mActiveQ.upper_bound(drawablep); // next valid entry
+ }
+ }
+ else
+ {
+ mActiveQ.erase(curiter);
+ }
+ }
+
+ for (LLDrawable::drawable_set_t::iterator iter = mRetexturedList.begin();
+ iter != mRetexturedList.end(); ++iter)
+ {
+ LLDrawable* drawablep = *iter;
+ if (drawablep && !drawablep->isDead())
+ {
+ drawablep->updateTexture();
+ }
+ }
+ mRetexturedList.clear();
+
+ for (LLDrawable::drawable_set_t::iterator iter = mRematerialedList.begin();
+ iter != mRematerialedList.end(); ++iter)
+ {
+ LLDrawable* drawablep = *iter;
+ if (drawablep && !drawablep->isDead())
+ {
+ drawablep->updateMaterial();
+ }
+ }
+ mRematerialedList.clear();
+
+ if (mObjectPartition->mOctree)
+ {
+ //balance octree
+ LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE);
+ mObjectPartition->mOctree->validate();
+ mObjectPartition->mOctree->balance();
+ mObjectPartition->mOctree->validate();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// Culling and occlusion testing
+/////////////////////////////////////////////////////////////////////////////
+
+void LLPipeline::updateCull()
+{
+ LLFastTimer t(LLFastTimer::FTM_CULL);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ LLDrawable::incrementVisible();
+ mVisibleList.resize(0);
+ mVisibleList.reserve(ESTIMATED_VISIBLE_OBJECT_COUNT);
+
+ gTrivialAccepts = 0;
+
+ if (mObjectPartition)
+ {
+ if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
+ {
+ mObjectPartition->processOcclusion(gCamera);
+ stop_glerror();
+ }
+ mObjectPartition->cull(*gCamera);
+ }
+
+ // Hack for avatars - warning - this is really FRAGILE! - djs 05/06/02
+ LLVOAvatar::updateAllAvatarVisiblity();
+
+ // If there are any other hacks here, make sure to add them to the
+ // standard pick code.
+
+ gMinObjectDistance = llclamp(gMinObjectDistance, MIN_NEAR_PLANE, MAX_NEAR_PLANE);
+
+ F32 water_height = gAgent.getRegion()->getWaterHeight();
+ F32 camera_height = gAgent.getCameraPositionAgent().mV[2];
+ if (fabs(camera_height - water_height) < 2.f)
+ {
+ gMinObjectDistance = MIN_NEAR_PLANE;
+ }
+
+ gCamera->setNear(gMinObjectDistance);
+
+ // Disable near clip stuff for now...
+
+ // now push it back out to max value
+ gMinObjectDistance = MIN_NEAR_PLANE;
+
+ if (gSky.mVOSkyp.notNull() && gSky.mVOSkyp->mDrawable.notNull())
+ {
+ // Hack for sky - always visible.
+ gSky.mVOSkyp->mDrawable->setVisible(*gCamera);
+ mVisibleList.push_back(gSky.mVOSkyp->mDrawable);
+ gSky.updateCull();
+ stop_glerror();
+ }
+ else
+ {
+ llinfos << "No sky drawable!" << llendl;
+ }
+
+ if (gSky.mVOGroundp.notNull() && gSky.mVOGroundp->mDrawable.notNull())
+ {
+ gSky.mVOGroundp->mDrawable->setVisible(*gCamera);
+ mVisibleList.push_back(gSky.mVOGroundp->mDrawable);
+ }
+
+ // add all HUD attachments
+ LLVOAvatar* my_avatarp = gAgent.getAvatarObject();
+ if (my_avatarp && my_avatarp->hasHUDAttachment())
+ {
+ for (LLViewerJointAttachment* attachmentp = my_avatarp->mAttachmentPoints.getFirstData();
+ attachmentp;
+ attachmentp = my_avatarp->mAttachmentPoints.getNextData())
+ {
+ if (attachmentp->getIsHUDAttachment() && attachmentp->getObject(0))
+ {
+ LLViewerObject* objectp = attachmentp->getObject(0);
+ markVisible(objectp->mDrawable);
+ objectp->mDrawable->updateDistance(*gCamera);
+ for (S32 i = 0; i < (S32)objectp->mChildList.size(); i++)
+ {
+ LLViewerObject* childp = objectp->mChildList[i];
+ if (childp->mDrawable.notNull())
+ {
+ markVisible(childp->mDrawable);
+ childp->mDrawable->updateDistance(*gCamera);
+ }
+ }
+ }
+ }
+ }
+}
+
+void LLPipeline::markNotCulled(LLDrawable* drawablep, LLCamera& camera)
+{
+ if (drawablep->isVisible())
+ {
+ return;
+ }
+
+ // Tricky render mode to hide selected objects, but we definitely
+ // don't want to do any unnecessary pointer dereferences. JC
+ if (gHideSelectedObjects)
+ {
+ if (drawablep->getVObj() && drawablep->getVObj()->isSelected())
+ {
+ return;
+ }
+ }
+
+ if (drawablep && (hasRenderType(drawablep->mRenderType)))
+ {
+ if (!drawablep->isState(LLDrawable::INVISIBLE|LLDrawable::FORCE_INVISIBLE))
+ {
+ mVisibleList.push_back(drawablep);
+ drawablep->setVisible(camera, NULL, FALSE);
+ }
+ else if (drawablep->isState(LLDrawable::CLEAR_INVISIBLE))
+ {
+ // clear invisible flag here to avoid single frame glitch
+ drawablep->clearState(LLDrawable::FORCE_INVISIBLE|LLDrawable::CLEAR_INVISIBLE);
+ }
+ }
+}
+
+void LLPipeline::doOcclusion()
+{
+ if (gSavedSettings.getBOOL("UseOcclusion") && gGLManager.mHasOcclusionQuery)
+ {
+ mObjectPartition->doOcclusion(gCamera);
+ }
+}
+
+
+BOOL LLPipeline::updateDrawableGeom(LLDrawable* drawablep, BOOL priority)
+{
+ BOOL update_complete = drawablep->updateGeometry(priority);
+ if (update_complete)
+ {
+ drawablep->setState(LLDrawable::BUILT);
+ mGeometryChanges++;
+ }
+ return update_complete;
+}
+
+void LLPipeline::updateGeom(F32 max_dtime)
+{
+ LLTimer update_timer;
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLPointer<LLDrawable> drawablep;
+
+ LLFastTimer t(LLFastTimer::FTM_GEO_UPDATE);
+
+ // notify various object types to reset internal cost metrics, etc.
+ // for now, only LLVOVolume does this to throttle LOD changes
+ LLVOVolume::preUpdateGeom();
+
+ // Iterate through all drawables on the priority build queue,
+ for (LLDrawable::drawable_set_t::iterator iter = mBuildQ1.begin();
+ iter != mBuildQ1.end();)
+ {
+ LLDrawable::drawable_set_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ BOOL update_complete = TRUE;
+ if (drawablep && !drawablep->isDead())
+ {
+ update_complete = updateDrawableGeom(drawablep, TRUE);
+ }
+ if (update_complete)
+ {
+ drawablep->clearState(LLDrawable::IN_REBUILD_Q1);
+ mBuildQ1.erase(curiter);
+ }
+ }
+
+ // Iterate through some drawables on the non-priority build queue
+ S32 min_count = 16;
+ if (mBuildQ2.size() > 1000)
+ {
+ min_count = mBuildQ2.size();
+ }
+ else
+ {
+ mBuildQ2.sort(LLDrawable::CompareDistanceGreaterVisibleFirst());
+ }
+
+ S32 count = 0;
+
+ max_dtime = llmax(update_timer.getElapsedTimeF32()+0.001f, max_dtime);
+
+ for (LLDrawable::drawable_list_t::iterator iter = mBuildQ2.begin();
+ iter != mBuildQ2.end(); )
+ {
+ LLDrawable::drawable_list_t::iterator curiter = iter++;
+ LLDrawable* drawablep = *curiter;
+ BOOL update_complete = TRUE;
+ if (drawablep && !drawablep->isDead())
+ {
+ update_complete = updateDrawableGeom(drawablep, FALSE);
+ count++;
+ }
+ if (update_complete)
+ {
+ drawablep->clearState(LLDrawable::IN_REBUILD_Q2);
+ mBuildQ2.erase(curiter);
+ }
+ if ((update_timer.getElapsedTimeF32() >= max_dtime) && count > min_count)
+ {
+ break;
+ }
+ }
+}
+
+void LLPipeline::markVisible(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if(!drawablep || drawablep->isDead())
+ {
+ llwarns << "LLPipeline::markVisible called with NULL drawablep" << llendl;
+ return;
+ }
+ if (!drawablep->isVisible())
+ {
+ drawablep->setVisible(*gCamera);
+ mVisibleList.push_back(drawablep);
+ }
+}
+
+void LLPipeline::markMoved(LLDrawable *drawablep, BOOL damped_motion)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep)
+ {
+ llerrs << "Sending null drawable to moved list!" << llendl;
+ return;
+ }
+
+ if (drawablep->isDead())
+ {
+ llwarns << "Marking NULL or dead drawable moved!" << llendl;
+ return;
+ }
+
+ if (drawablep->getParent())
+ {
+ //ensure that parent drawables are moved first
+ markMoved(drawablep->getParent(), damped_motion);
+ }
+
+
+ if (!drawablep->isState(LLDrawable::ON_MOVE_LIST))
+ {
+ mMovedList.insert(drawablep);
+ drawablep->setState(LLDrawable::ON_MOVE_LIST);
+ }
+ if (damped_motion == FALSE)
+ {
+ drawablep->setState(LLDrawable::MOVE_UNDAMPED); // UNDAMPED trumps DAMPED
+ }
+ else if (drawablep->isState(LLDrawable::MOVE_UNDAMPED))
+ {
+ drawablep->clearState(LLDrawable::MOVE_UNDAMPED);
+ }
+}
+
+void LLPipeline::markShift(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep || drawablep->isDead())
+ {
+ return;
+ }
+
+ if (!drawablep->isState(LLDrawable::ON_SHIFT_LIST))
+ {
+ drawablep->getVObj()->setChanged(LLXform::SHIFTED | LLXform::SILHOUETTE);
+ if (drawablep->getParent())
+ {
+ markShift(drawablep->getParent());
+ }
+ mShiftList.push_back(drawablep);
+ drawablep->setState(LLDrawable::ON_SHIFT_LIST);
+ }
+}
+
+void LLPipeline::shiftObjects(const LLVector3 &offset)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ for (LLDrawable::drawable_vector_t::iterator iter = mShiftList.begin();
+ iter != mShiftList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ drawablep->shiftPos(offset);
+ drawablep->clearState(LLDrawable::ON_SHIFT_LIST);
+ }
+ mShiftList.resize(0);
+
+ mObjectPartition->shift(offset);
+}
+
+void LLPipeline::markTextured(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep->isDead())
+ {
+ mRetexturedList.insert(drawablep);
+ }
+}
+
+void LLPipeline::markMaterialed(LLDrawable *drawablep)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (!drawablep->isDead())
+ {
+ mRematerialedList.insert(drawablep);
+ }
+}
+
+void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag, BOOL priority)
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ if (drawablep && !drawablep->isDead())
+ {
+ if (!drawablep->isState(LLDrawable::BUILT))
+ {
+ priority = TRUE;
+ }
+ if (priority)
+ {
+ mBuildQ1.insert(drawablep);
+ drawablep->setState(LLDrawable::IN_REBUILD_Q1); // flag is not needed, just for debugging
+ }
+ else if (!drawablep->isState(LLDrawable::IN_REBUILD_Q2))
+ {
+ mBuildQ2.push_back(drawablep);
+ drawablep->setState(LLDrawable::IN_REBUILD_Q2); // need flag here because it is just a list
+ }
+ if (flag & LLDrawable::REBUILD_VOLUME)
+ {
+ drawablep->getVObj()->setChanged(LLXform::SILHOUETTE);
+ }
+ drawablep->setState(flag);
+ if ((flag & LLDrawable::REBUILD_LIGHTING) && drawablep->getLit())
+ {
+ if (drawablep->isLight())
+ {
+ drawablep->clearState(LLDrawable::LIGHTING_BUILT);
+ }
+ else
+ {
+ drawablep->clearState(LLDrawable::LIGHTING_BUILT);
+ }
+ }
+ }
+}
+
+void LLPipeline::markRelight(LLDrawable *drawablep, const BOOL priority)
+{
+ if (getLightingDetail() >= 2)
+ {
+ markRebuild(drawablep, LLDrawable::REBUILD_LIGHTING, FALSE);
+ }
+}
+
+void LLPipeline::stateSort()
+{
+ LLFastTimer ftm(LLFastTimer::FTM_STATESORT);
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin();
+ iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+
+ if (!drawablep->isActive())
+ {
+ drawablep->updateDistance(*gCamera);
+ }
+
+ /*
+ if (!drawablep->isState(LLDrawable::BUILT))
+ {
+ // This geometry hasn't been rebuilt but it's visible, make sure it gets put on the rebuild list.
+ llerrs << "Visible object " << drawablep << ":" << drawablep->getVObj()->getPCodeString();
+ llcont << " visible but not built, put on rebuild" << llendl;
+ markRebuild(drawablep);
+ continue;
+ }
+ */
+
+ for (LLDrawable::face_list_t::iterator iter = drawablep->mFaces.begin();
+ iter != drawablep->mFaces.end(); iter++)
+ {
+ LLFace* facep = *iter;
+ if (facep->hasGeometry())
+ {
+ facep->getPool()->enqueue(facep);
+ }
+ }
+
+ if (sRenderPhysicalBeacons)
+ {
+ // Only show the beacon on the root object.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && !vobj->isAvatar()
+ && !vobj->getParent()
+ && vobj->usePhysics())
+ {
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(0.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ if (sRenderScriptedBeacons)
+ {
+ // Only show the beacon on the root object.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && !vobj->isAvatar()
+ && !vobj->getParent()
+ && vobj->flagScripted())
+ {
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", LLColor4(1.f, 0.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ if (sRenderParticleBeacons)
+ {
+ // Look for attachments, objects, etc.
+ LLViewerObject *vobj = drawablep->getVObj();
+ if (vobj
+ && vobj->isParticleSource())
+ {
+ LLColor4 light_blue(0.5f, 0.5f, 1.f, 0.5f);
+ gObjectList.addDebugBeacon(vobj->getPositionAgent(), "", light_blue, LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ // Draw physical objects in red.
+ if (gHUDManager->getShowPhysical())
+ {
+ LLViewerObject *vobj;
+ vobj = drawablep->getVObj();
+ if (vobj && !vobj->isAvatar())
+ {
+ if (!vobj->isAvatar() &&
+ (vobj->usePhysics() || vobj->flagHandleTouch()))
+ {
+ if (!drawablep->isVisible())
+ {
+ // Skip objects that aren't visible.
+ continue;
+ }
+ S32 face_id;
+ for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
+ {
+ mHighlightFaces.put(drawablep->getFace(face_id) );
+ }
+ }
+ }
+ }
+
+ mNumVisibleFaces += drawablep->getNumFaces();
+ }
+
+ // If god mode, also show audio cues
+ if (sRenderSoundBeacons && gAudiop)
+ {
+ // Update all of our audio sources, clean up dead ones.
+ LLAudioEngine::source_map::iterator iter;
+ for (iter = gAudiop->mAllSources.begin(); iter != gAudiop->mAllSources.end(); ++iter)
+ {
+ LLAudioSource *sourcep = iter->second;
+
+ LLVector3d pos_global = sourcep->getPositionGlobal();
+ LLVector3 pos = gAgent.getPosAgentFromGlobal(pos_global);
+ //pos += LLVector3(0.f, 0.f, 0.2f);
+ gObjectList.addDebugBeacon(pos, "", LLColor4(1.f, 1.f, 0.f, 0.5f), LLColor4(1.f, 1.f, 1.f, 0.5f));
+ }
+ }
+
+ // If managing your telehub, draw beacons at telehub and currently selected spawnpoint.
+ if (LLFloaterTelehub::renderBeacons())
+ {
+ LLFloaterTelehub::addBeacons();
+ }
+
+ mSelectedFaces.reset();
+
+ // Draw face highlights for selected faces.
+ if (gSelectMgr->getTEMode())
+ {
+ LLViewerObject *vobjp;
+ S32 te;
+ gSelectMgr->getFirstTE(&vobjp,&te);
+
+ while (vobjp)
+ {
+ LLDrawable *drawablep = vobjp->mDrawable;
+ if (!drawablep || drawablep->isDead() || (!vobjp->isHUDAttachment() && !drawablep->isVisible()))
+ {
+ llwarns << "Dead drawable on selected face list!" << llendl;
+ }
+ else
+ {
+ LLVOVolume *volp = drawablep->getVOVolume();
+ if (volp)
+ {
+ if (volp->getAllTEsSame())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = drawablep->getFace(vobjp->getFaceIndexOffset());
+ faceinfo->mTE = te;
+ }
+ else
+ {
+ // This is somewhat inefficient, but works correctly.
+ S32 face_id;
+ for (face_id = 0; face_id < vobjp->getVolume()->getNumFaces(); face_id++)
+ {
+ LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
+ if (te == facep->getTEOffset())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = facep;
+ faceinfo->mTE = -1;
+ }
+ }
+ }
+ }
+ else
+ {
+ // This is somewhat inefficient, but works correctly.
+ S32 face_id;
+ for (face_id = 0; face_id < drawablep->getNumFaces(); face_id++)
+ {
+ LLFace *facep = drawablep->getFace(face_id + vobjp->getFaceIndexOffset());
+ if (te == facep->getTEOffset())
+ {
+ SelectedFaceInfo* faceinfo = mSelectedFaces.reserve_block(1);
+ faceinfo->mFacep = facep;
+ faceinfo->mTE = -1;
+ }
+ }
+ }
+ }
+ gSelectMgr->getNextTE(&vobjp,&te);
+ }
+ }
+}
+
+
+static void render_hud_elements()
+{
+ LLFastTimer t(LLFastTimer::FTM_RENDER_UI);
+ gPipeline.disableLights();
+
+ gPipeline.renderDebug();
+
+ LLGLDisable fog(GL_FOG);
+ LLGLSUIDefault gls_ui;
+
+ if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ gViewerWindow->renderSelections(FALSE, FALSE, FALSE); // For HUD bersion in render_ui_3d()
+
+ // Draw the tracking overlays
+ LLTracker::render3D();
+
+ // Show the property lines
+ if (gWorldp)
+ {
+ gWorldp->renderPropertyLines();
+ }
+ if (gParcelMgr)
+ {
+ gParcelMgr->render();
+ gParcelMgr->renderParcelCollision();
+ }
+
+ // Render debugging beacons.
+ gObjectList.renderObjectBeacons();
+ LLHUDObject::renderAll();
+ gObjectList.resetObjectBeacons();
+ }
+ else if (gForceRenderLandFence)
+ {
+ // This is only set when not rendering the UI, for parcel snapshots
+ gParcelMgr->render();
+ }
+
+}
+
+void LLPipeline::renderHighlights()
+{
+ // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
+ // Render highlighted faces.
+ LLColor4 color(1.f, 1.f, 1.f, 0.5f);
+ LLGLEnable color_mat(GL_COLOR_MATERIAL);
+ disableLights();
+
+ if ((mVertexShaderLevel[SHADER_INTERFACE] > 0))
+ {
+ mHighlightProgram.bind();
+ gPipeline.mHighlightProgram.vertexAttrib4f(LLPipeline::GLSL_MATERIAL_COLOR,1,0,0,0.5f);
+ }
+
+ if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED))
+ {
+ // Make sure the selection image gets downloaded and decoded
+ if (!mFaceSelectImagep)
+ {
+ mFaceSelectImagep = gImageList.getImage(IMG_FACE_SELECT);
+ }
+ mFaceSelectImagep->addTextureStats((F32)MAX_IMAGE_AREA);
+
+ for (S32 i = 0; i < mSelectedFaces.count(); i++)
+ {
+ LLFace *facep = mSelectedFaces[i].mFacep;
+ if (!facep || facep->getDrawable()->isDead())
+ {
+ llerrs << "Bad face on selection" << llendl;
+ }
+
+ LLDrawPool* poolp = facep->getPool();
+
+ if (!poolp->canUseAGP())
+ {
+ unbindAGP();
+ }
+ else if (usingAGP())
+ {
+ bindAGP();
+ }
+
+ if (mSelectedFaces[i].mTE == -1)
+ {
+ // Yes, I KNOW this is stupid...
+ poolp->renderFaceSelected(facep, mFaceSelectImagep, color);
+ }
+ else
+ {
+ LLVOVolume *volp = (LLVOVolume *)facep->getViewerObject();
+ // Do the special coalesced face mode.
+ S32 j;
+ S32 offset = 0;
+ S32 count = volp->getVolume()->getVolumeFace(0).mIndices.size();
+ for (j = 0; j <= mSelectedFaces[i].mTE; j++)
+ {
+ count = volp->getVolume()->getVolumeFace(j).mIndices.size();
+ if (j < mSelectedFaces[i].mTE)
+ {
+ offset += count;
+ }
+ }
+
+ poolp->renderFaceSelected(facep, mFaceSelectImagep, color, offset, count);
+ }
+ }
+ }
+
+ if (hasRenderDebugFeatureMask(RENDER_DEBUG_FEATURE_SELECTED))
+ {
+ // Paint 'em red!
+ color.setVec(1.f, 0.f, 0.f, 0.5f);
+ for (S32 i = 0; i < mHighlightFaces.count(); i++)
+ {
+ LLFace* facep = mHighlightFaces[i];
+ LLDrawPool* poolp = facep->getPool();
+ if (!poolp->canUseAGP())
+ {
+ unbindAGP();
+ }
+ else if (usingAGP())
+ {
+ bindAGP();
+ }
+
+ poolp->renderFaceSelected(facep, LLViewerImage::sNullImagep, color);
+ }
+ }
+
+ // Contains a list of the faces of objects that are physical or
+ // have touch-handlers.
+ mHighlightFaces.reset();
+
+ if (mVertexShaderLevel[SHADER_INTERFACE] > 0)
+ {
+ mHighlightProgram.unbind();
+ }
+}
+
+void LLPipeline::renderGeom()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY);
+
+ if (!mAlphaSizzleImagep)
+ {
+ mAlphaSizzleImagep = gImageList.getImage(LLUUID(gViewerArt.getString("alpha_sizzle.tga")), MIPMAP_TRUE, TRUE);
+ }
+
+ ///////////////////////////////////////////
+ //
+ // Sync and verify GL state
+ //
+ //
+
+ stop_glerror();
+ gFrameStats.start(LLFrameStats::RENDER_SYNC);
+
+ // Do verification of GL state
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+#endif
+ if (mRenderDebugMask & RENDER_DEBUG_VERIFY)
+ {
+ if (!verify())
+ {
+ llerrs << "Pipeline verification failed!" << llendl;
+ }
+ }
+
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ }
+
+ unbindAGP();
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (hasRenderType(poolp->getType()))
+ {
+ poolp->prerender();
+ poolp->syncAGP();
+ }
+ }
+
+ gFrameStats.start(LLFrameStats::RENDER_GEOM);
+
+ // Initialize lots of GL state to "safe" values
+ mTrianglesDrawn = 0;
+
+ glMatrixMode(GL_TEXTURE);
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLSPipeline gls_pipeline;
+
+ LLGLState gls_color_material(GL_COLOR_MATERIAL, mLightingDetail < 2);
+ LLGLState normalize(GL_NORMALIZE, TRUE);
+
+ // Toggle backface culling for debugging
+ LLGLEnable cull_face(mBackfaceCull ? GL_CULL_FACE : 0);
+ // Set fog
+ LLGLEnable fog_enable(hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_FOG) ? GL_FOG : 0);
+
+
+ LLViewerImage::sDefaultImagep->bind(0);
+ LLViewerImage::sDefaultImagep->setClamp(FALSE, FALSE);
+
+ //////////////////////////////////////////////
+ //
+ // Actually render all of the geometry
+ //
+ //
+
+ stop_glerror();
+ BOOL non_agp = FALSE;
+ BOOL did_hud_elements = FALSE;
+
+ U32 cur_type = 0;
+
+ S32 skipped_vertices = 0;
+ {
+ LLFastTimer t(LLFastTimer::FTM_POOLS);
+ BOOL occlude = TRUE;
+
+ calcNearbyLights();
+
+ pool_set_t::iterator iter1 = mPools.begin();
+ while ( iter1 != mPools.end() )
+ {
+ LLDrawPool *poolp = *iter1;
+
+ cur_type = poolp->getType();
+
+ if (cur_type >= LLDrawPool::POOL_TREE && occlude)
+ { //all the occluders have been drawn, do occlusion queries
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ doOcclusion();
+ occlude = FALSE;
+ }
+
+ if (cur_type >= LLDrawPool::POOL_HUD && !did_hud_elements)
+ {
+ renderHighlights();
+ // Draw 3D UI elements here (before we clear the Z buffer in POOL_HUD)
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ render_hud_elements();
+ did_hud_elements = TRUE;
+ }
+
+ pool_set_t::iterator iter2 = iter1;
+ if (hasRenderType(poolp->getType()))
+ {
+ LLFastTimer t(LLFastTimer::FTM_POOLRENDER);
+
+ setupHWLights(poolp);
+
+ if (mVertexShadersEnabled && poolp->getVertexShaderLevel() == 0)
+ {
+ glUseProgramObjectARB(0);
+ }
+ else if (mVertexShadersEnabled)
+ {
+ mMaterialIndex = mSpecularIndex = 0;
+ switch(cur_type)
+ {
+ case LLDrawPool::POOL_SKY:
+ case LLDrawPool::POOL_STARS:
+ case LLDrawPool::POOL_CLOUDS:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_TERRAIN:
+ mTerrainProgram.bind();
+ break;
+ case LLDrawPool::POOL_GROUND:
+ mGroundProgram.bind();
+ break;
+ case LLDrawPool::POOL_TREE:
+ case LLDrawPool::POOL_TREE_NEW:
+ case LLDrawPool::POOL_SIMPLE:
+ case LLDrawPool::POOL_MEDIA:
+ mObjectSimpleProgram.bind();
+ break;
+ case LLDrawPool::POOL_BUMP:
+ mObjectBumpProgram.bind();
+ break;
+ case LLDrawPool::POOL_AVATAR:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_WATER:
+ glUseProgramObjectARB(0);
+ break;
+ case LLDrawPool::POOL_ALPHA:
+ mObjectAlphaProgram.bind();
+ break;
+ case LLDrawPool::POOL_HUD:
+ default:
+ glUseProgramObjectARB(0);
+ break;
+ }
+ }
+
+ for( S32 i = 0; i < poolp->getNumPasses(); i++ )
+ {
+ poolp->beginRenderPass(i);
+ for (iter2 = iter1; iter2 != mPools.end(); iter2++)
+ {
+ LLDrawPool *p = *iter2;
+ if (p->getType() != cur_type)
+ {
+ break;
+ }
+ if (p->getType() != LLDrawPool::POOL_AVATAR
+ && p->getType() != LLDrawPool::POOL_ALPHA
+ && p->getType() != LLDrawPool::POOL_HUD
+ && (!p->getIndexCount() || !p->getVertexCount()))
+ {
+ continue;
+ }
+
+ if (p->canUseAGP() && usingAGP())
+ {
+ bindAGP();
+ }
+ else
+ {
+ //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
+ unbindAGP();
+ non_agp = TRUE;
+ }
+
+ p->resetTrianglesDrawn();
+ p->render(i);
+ mTrianglesDrawn += p->getTrianglesDrawn();
+ skipped_vertices += p->mSkippedVertices;
+ p->mSkippedVertices = 0;
+ }
+ poolp->endRenderPass(i);
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+#endif
+ }
+ }
+ else
+ {
+ // Skip all pools of this type
+ for (iter2 = iter1; iter2 != mPools.end(); iter2++)
+ {
+ LLDrawPool *p = *iter2;
+ if (p->getType() != cur_type)
+ {
+ break;
+ }
+ }
+ }
+ iter1 = iter2;
+ stop_glerror();
+ }
+
+ if (occlude)
+ {
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+ doOcclusion();
+ }
+ }
+ stop_glerror();
+
+ if (mVertexShadersEnabled)
+ {
+ glUseProgramObjectARB(0);
+ }
+
+ if (!did_hud_elements)
+ {
+ renderHighlights();
+ render_hud_elements();
+ }
+
+ static S32 agp_mix_count = 0;
+ if (non_agp && usingAGP())
+ {
+ if (0 == agp_mix_count % 16)
+ {
+ lldebugs << "Mixing AGP and non-AGP pools, slow!" << llendl;
+ }
+ agp_mix_count++;
+ }
+ else
+ {
+ agp_mix_count = 0;
+ }
+
+ // Contains a list of the faces of objects that are physical or
+ // have touch-handlers.
+ mHighlightFaces.reset();
+
+ // This wait is in case we try to do multiple renders of a frame,
+ // I don't know what happens when we send a fence multiple times without
+ // checking it.
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ mAGPMemPool->sendFence(mGlobalFence);
+ }
+}
+
+void LLPipeline::renderDebug()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ // Disable all client state
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisableClientState(GL_NORMAL_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ // Debug stuff.
+ mObjectPartition->renderDebug();
+
+ if (mRenderDebugMask & LLPipeline::RENDER_DEBUG_LIGHT_TRACE)
+ {
+ LLGLSNoTexture no_texture;
+
+ LLVector3 pos, pos1;
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin();
+ iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ for (LLDrawable::drawable_set_t::iterator iter = drawablep->mLightSet.begin();
+ iter != drawablep->mLightSet.end(); iter++)
+ {
+ LLDrawable *targetp = *iter;
+ if (targetp->isDead() || !targetp->getVObj()->getNumTEs())
+ {
+ continue;
+ }
+ else
+ {
+ if (targetp->getTextureEntry(0))
+ {
+ if (drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ glColor4f(0.f, 1.f, 0.f, 1.f);
+ gObjectList.addDebugBeacon(drawablep->getPositionAgent(), "TC");
+ }
+ else
+ {
+ glColor4fv (targetp->getTextureEntry(0)->getColor().mV);
+ }
+ glBegin(GL_LINES);
+ glVertex3fv(targetp->getPositionAgent().mV);
+ glVertex3fv(drawablep->getPositionAgent().mV);
+ glEnd();
+ }
+ }
+ }
+ }
+ }
+
+ mCompilesStat.addValue(sCompiles);
+ mLightingChangesStat.addValue(mLightingChanges);
+ mGeometryChangesStat.addValue(mGeometryChanges);
+ mTrianglesDrawnStat.addValue(mTrianglesDrawn/1000.f);
+ mVerticesRelitStat.addValue(mVerticesRelit);
+ mNumVisibleFacesStat.addValue(mNumVisibleFaces);
+ mNumVisibleDrawablesStat.addValue((S32)mVisibleList.size());
+
+ if (gRenderLightGlows)
+ {
+ displaySSBB();
+ }
+
+ /*if (mRenderDebugMask & RENDER_DEBUG_BBOXES)
+ {
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+
+ for (LLDrawable::drawable_vector_t::iterator iter = mVisibleList.begin(); iter != mVisibleList.end(); iter++)
+ {
+ LLDrawable *drawablep = *iter;
+ if (drawablep->isDead())
+ {
+ continue;
+ }
+ LLVector3 min, max;
+
+ if (drawablep->getVObj() && drawablep->getVObj()->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH)
+ {
+ // Render drawable bbox
+ drawablep->getBounds(min, max);
+ glColor4f(0.f, 1.f, 0.f, 0.25f);
+ render_bbox(min, max);
+
+ // Render object bbox
+ LLVector3 scale = drawablep->getVObj()->getScale();
+ LLVector3 pos = drawablep->getVObj()->getPositionAgent();
+ min = pos - scale * 0.5f;
+ max = pos + scale * 0.5f;
+ glColor4f(1.f, 0.f, 0.f, 0.25f);
+ render_bbox(min, max);
+ }
+ }
+ }*/
+
+ /*
+ // Debugging code for parcel sound.
+ F32 x, y;
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin(GL_POINTS);
+ if (gAgent.getRegion())
+ {
+ // Draw the composition layer for the region that I'm in.
+ for (x = 0; x <= 260; x++)
+ {
+ for (y = 0; y <= 260; y++)
+ {
+ if (gParcelMgr->isSoundLocal(gAgent.getRegion()->getOriginGlobal() + LLVector3d(x, y, 0.f)))
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.f, 0.f, 1.f, 1.f);
+ }
+
+ glVertex3f(x, y, gAgent.getRegion()->getLandHeightRegion(LLVector3(x, y, 0.f)));
+ }
+ }
+ }
+ glEnd();
+ */
+
+ if (mRenderDebugMask & RENDER_DEBUG_COMPOSITION)
+ {
+ // Debug composition layers
+ F32 x, y;
+
+ LLGLSNoTexture gls_no_texture;
+
+ glBegin(GL_POINTS);
+ if (gAgent.getRegion())
+ {
+ // Draw the composition layer for the region that I'm in.
+ for (x = 0; x <= 260; x++)
+ {
+ for (y = 0; y <= 260; y++)
+ {
+ if ((x > 255) || (y > 255))
+ {
+ glColor4f(1.f, 0.f, 0.f, 1.f);
+ }
+ else
+ {
+ glColor4f(0.f, 0.f, 1.f, 1.f);
+ }
+ F32 z = gAgent.getRegion()->getCompositionXY((S32)x, (S32)y);
+ z *= 5.f;
+ z += 50.f;
+ glVertex3f(x, y, z);
+ }
+ }
+ }
+ glEnd();
+ }
+
+ if (mRenderDebugMask & RENDER_DEBUG_AGP_MEM)
+ {
+ displayAGP();
+ }
+
+ if (mRenderDebugMask & RENDER_DEBUG_POOLS)
+ {
+ displayPools();
+ }
+
+// if (mRenderDebugMask & RENDER_DEBUG_QUEUES)
+// {
+// displayQueues();
+// }
+
+ if (mRenderDebugMask & RENDER_DEBUG_MAP)
+ {
+ displayMap();
+ }
+}
+
+
+
+
+BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max)
+{
+ min.setVec(1000,1000);
+ max.setVec(-1000,-1000);
+
+ if (box.mMatrix[3][3] <= 0.0f) return FALSE;
+
+ const F32 vec[8][3] = {
+ { -0.5f,-0.5f,-0.5f },
+ { -0.5f,-0.5f,+0.5f },
+ { -0.5f,+0.5f,-0.5f },
+ { -0.5f,+0.5f,+0.5f },
+ { +0.5f,-0.5f,-0.5f },
+ { +0.5f,-0.5f,+0.5f },
+ { +0.5f,+0.5f,-0.5f },
+ { +0.5f,+0.5f,+0.5f } };
+
+ LLVector4 v;
+
+ for (S32 i=0;i<8;i++)
+ {
+ v.setVec(vec[i][0],vec[i][1],vec[i][2],1);
+ v = v * box;
+ F32 iw = 1.0f / v.mV[3];
+ v.mV[0] *= iw;
+ v.mV[1] *= iw;
+
+ min.mV[0] = llmin(min.mV[0],v.mV[0]);
+ max.mV[0] = llmax(max.mV[0],v.mV[0]);
+
+ min.mV[1] = llmin(min.mV[1],v.mV[1]);
+ max.mV[1] = llmax(max.mV[1],v.mV[1]);
+ }
+
+ /*
+ min.mV[0] = max.mV[0] = box.mMatrix[3][0];
+ min.mV[1] = max.mV[1] = box.mMatrix[3][1];
+ F32 iw = 1.0f / box.mMatrix[3][3];
+
+ F32 f0 = (fabs(box.mMatrix[0][0])+fabs(box.mMatrix[1][0])+fabs(box.mMatrix[2][0])) * 0.5f;
+ F32 f1 = (fabs(box.mMatrix[0][1])+fabs(box.mMatrix[1][1])+fabs(box.mMatrix[2][1])) * 0.5f;
+ F32 f2 = (fabs(box.mMatrix[0][2])+fabs(box.mMatrix[1][2])+fabs(box.mMatrix[2][2])) * 0.5f;
+
+ min.mV[0] -= f0;
+ min.mV[1] -= f1;
+
+ max.mV[0] += f0;
+ max.mV[1] += f1;
+
+ min.mV[0] *= iw;
+ min.mV[1] *= iw;
+
+ max.mV[0] *= iw;
+ max.mV[1] *= iw;
+ */
+ return TRUE;
+}
+
+void LLPipeline::displaySSBB()
+{
+ LLMatrix4 proj;
+ LLMatrix4 cfr(OGL_TO_CFR_ROTATION);
+ LLMatrix4 camera;
+ LLMatrix4 comb;
+
+ gCamera->getMatrixToLocal(camera);
+
+ if (!mBloomImagep)
+ {
+ mBloomImagep = gImageList.getImage(IMG_BLOOM1);
+ }
+
+ // don't write to depth buffer with light glows so that chat bubbles can pop through
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+ LLViewerImage::bindTexture(mBloomImagep);
+
+ glGetFloatv(GL_PROJECTION_MATRIX,(float*)proj.mMatrix);
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+
+ //glScalef(0.25,0.25,0.25);
+
+ S32 sizex = gViewerWindow->getWindowWidth() / 2;
+ S32 sizey = gViewerWindow->getWindowHeight() / 2;
+
+ F32 aspect = (float)sizey / (float)sizex;
+
+ for (LLDrawable::drawable_set_t::iterator iter = mLights.begin();
+ iter != mLights.end(); iter++)
+ {
+ LLDrawable *lightp = *iter;
+ if (lightp->isDead())
+ {
+ continue;
+ }
+
+ LLMatrix4 mat = lightp->mXform.getWorldMatrix();
+
+ mat *= camera;
+ mat *= cfr;
+ mat *= proj;
+
+ U8 color[64];
+
+ LLVector2 min,max;
+ if (mat.mMatrix[3][3] < 160 && compute_min_max(mat,min,max))
+ {
+ F32 cx = (max.mV[0] + min.mV[0]) * 0.5f;
+ F32 cy = (max.mV[1] + min.mV[1]) * 0.5f;
+ F32 sx = (max.mV[0] - min.mV[0]) * 2.0f;
+ F32 sy = (max.mV[1] - min.mV[1]) * 2.0f;
+ S32 x = (S32)(cx * (F32)sizex) + sizex;
+ S32 y = (S32)(cy * (F32)sizey) + sizey;
+
+ if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+ {
+ glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
+
+ S32 total = 0;
+ for (S32 i=0;i<64;i++)
+ {
+ total += color[i];
+ }
+ total /= 64;
+
+ sx = (sy = (sx + sy) * 0.5f * ((float)total/255.0f)) * aspect;
+
+
+ if (total > 60)
+ {
+ color[3+32] = total >> 1;
+ glBegin(GL_QUADS);
+ glColor4ubv(&color[32]);
+ glTexCoord2f(0,0);
+ glVertex3f(cx-sx,cy-sy,0);
+ glTexCoord2f(1,0);
+ glVertex3f(cx+sx,cy-sy,0);
+ glTexCoord2f(1,1);
+ glVertex3f(cx+sx,cy+sy,0);
+ glTexCoord2f(0,1);
+ glVertex3f(cx-sx,cy+sy,0);
+ glEnd();
+ }
+ }
+ }
+
+ }
+
+ // sun
+ {
+ LLVector4 sdir(gSky.getSunDirection() * 10000.0f + gAgent.getPositionAgent());
+ sdir.mV[3] = 1.0f;
+ sdir = sdir * camera;
+ sdir = sdir * cfr;
+ sdir = sdir * proj; // todo: preconcat
+
+ sdir.mV[0] /= sdir.mV[3];
+ sdir.mV[1] /= sdir.mV[3];
+
+ U8 color[64];
+
+ if (sdir.mV[3] > 0)
+ {
+ F32 cx = sdir.mV[0];
+ F32 cy = sdir.mV[1];
+ F32 sx, sy;
+ S32 x = (S32)(cx * (F32)sizex) + sizex;
+ S32 y = (S32)(cy * (F32)sizey) + sizey;
+
+ if (cx > -1 && cx < 1 && cy > -1 && cy < 1)
+ {
+ glReadPixels(x-2,y-2,4,4,GL_RGBA,GL_UNSIGNED_BYTE,&color[0]);
+
+ S32 total = 0;
+ for (S32 i=0;i<64;i++)
+ {
+ total += color[i];
+ }
+ total >>= 7;
+
+ sx = (sy = ((float)total/255.0f)) * aspect;
+
+ const F32 fix = -0.1f;
+
+ color[32] = (U8)(color[32] * 0.5f + 255 * 0.5f);
+ color[33] = (U8)(color[33] * 0.5f + 255 * 0.5f);
+ color[34] = (U8)(color[34] * 0.5f + 255 * 0.5f);
+
+ if (total > 80)
+ {
+ color[32+3] = (U8)total;
+ glBegin(GL_QUADS);
+ glColor4ubv(&color[32]);
+ glTexCoord2f(0,0);
+ glVertex3f(cx-sx,cy-sy+fix,0);
+ glTexCoord2f(1,0);
+ glVertex3f(cx+sx,cy-sy+fix,0);
+ glTexCoord2f(1,1);
+ glVertex3f(cx+sx,cy+sy+fix,0);
+ glTexCoord2f(0,1);
+ glVertex3f(cx-sx,cy+sy+fix,0);
+ glEnd();
+ }
+ }
+ }
+ }
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+void LLPipeline::displayMap()
+{
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glTranslatef(-1,-1,0);
+ glScalef(2,2,0);
+
+ glColor4f(0,0,0,0.5);
+ glBegin(GL_QUADS);
+ glVertex3f(0,0,0);
+ glVertex3f(1,0,0);
+ glVertex3f(1,1,0);
+ glVertex3f(0,1,0);
+ glEnd();
+
+ static F32 totalW = 1.5f;
+ static F32 offset = 0.5f;
+ static F32 scale = 1.0f;
+
+ S32 mousex = gViewerWindow->getCurrentMouseX();
+ S32 mousey = gViewerWindow->getCurrentMouseY();
+ S32 w = gViewerWindow->getWindowWidth();
+ S32 h = gViewerWindow->getWindowHeight();
+
+ if (mousex < 20 && offset > 0) offset -= (20 - mousex) * 0.02f;
+ if (mousex > (w - 20)) offset += (20 - (w - mousex)) * 0.02f;
+ if (offset > (totalW-1)) offset = (totalW-1);
+
+ if (mousey < 20 && scale > 0.1) scale -= (20 - mousey) * 0.001f;
+ if (mousey > (h - 20) && scale < 1.0f) scale += (20 - (h - mousey)) * 0.001f;
+
+ glScalef(scale*scale,scale,1);
+ glTranslatef(-offset,0,0);
+ //totalW = mStaticTree->render2D(0,0.8f/scale);
+ //mDynamicTree->render2D(0,0.4f/scale);
+
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+}
+
+void LLPipeline::renderForSelect()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ //for each drawpool
+
+ glMatrixMode(GL_MODELVIEW);
+
+ LLGLSDefault gls_default;
+ LLGLSObjectSelect gls_object_select;
+ LLGLDepthTest gls_depth(GL_TRUE);
+ disableLights();
+
+ glEnableClientState ( GL_VERTEX_ARRAY );
+ glDisableClientState( GL_NORMAL_ARRAY );
+ glDisableClientState( GL_TEXTURE_COORD_ARRAY );
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+ U32 last_type = 0;
+#endif
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (poolp->canUseAGP() && usingAGP())
+ {
+ bindAGP();
+ }
+ else
+ {
+ //llinfos << "Rendering pool type " << p->getType() << " without AGP!" << llendl;
+ unbindAGP();
+ }
+ poolp->renderForSelect();
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ if (poolp->getType() != last_type)
+ {
+ last_type = poolp->getType();
+ LLGLState::checkStates();
+ LLGLState::checkTextureChannels();
+ LLGLState::checkClientArrays();
+ }
+#endif
+ }
+
+ // Disable all of the client state
+ glDisableClientState( GL_VERTEX_ARRAY );
+
+ if (mAGPMemPool)
+ {
+ mAGPMemPool->waitFence(mGlobalFence);
+ mAGPMemPool->sendFence(mGlobalFence);
+ }
+}
+
+void LLPipeline::renderFaceForUVSelect(LLFace* facep)
+{
+ if (facep) facep->renderSelectedUV();
+}
+
+void LLPipeline::rebuildPools()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ S32 max_count = mPools.size();
+ S32 num_rebuilds = 0;
+ pool_set_t::iterator iter1 = mPools.upper_bound(mLastRebuildPool);
+ while(max_count > 0 && mPools.size() > 0) // && num_rebuilds < MAX_REBUILDS)
+ {
+ if (iter1 == mPools.end())
+ {
+ iter1 = mPools.begin();
+ }
+ LLDrawPool* poolp = *iter1;
+ num_rebuilds += poolp->rebuild();
+ if (poolp->mReferences.empty())
+ {
+ mPools.erase(iter1++);
+ removeFromQuickLookup( poolp );
+ if (poolp == mLastRebuildPool)
+ {
+ mLastRebuildPool = NULL;
+ }
+ delete poolp;
+ }
+ else
+ {
+ mLastRebuildPool = poolp;
+ iter1++;
+ }
+ max_count--;
+ }
+
+ if (gAgent.getAvatarObject())
+ {
+ gAgent.getAvatarObject()->rebuildHUD();
+ }
+}
+
+void LLPipeline::addToQuickLookup( LLDrawPool* new_poolp )
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ switch( new_poolp->getType() )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ mSimplePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ mTreePools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ mTreeNewPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ mTerrainPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ mBumpPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ mMediaPools[ uintptr_t(new_poolp->getTexture()) ] = new_poolp ;
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ if( mAlphaPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Alpha pool" << llendl;
+ }
+ else
+ {
+ mAlphaPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ if( mSkyPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Sky pool" << llendl;
+ }
+ else
+ {
+ mSkyPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ if( mStarsPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Stars pool" << llendl;
+ }
+ else
+ {
+ mStarsPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ if( mCloudsPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Clouds pool" << llendl;
+ }
+ else
+ {
+ mCloudsPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ if( mWaterPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Water pool" << llendl;
+ }
+ else
+ {
+ mWaterPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ if( mGroundPool )
+ {
+ llassert(0);
+ llwarns << "LLPipeline::addPool(): Ignoring duplicate Ground Pool" << llendl;
+ }
+ else
+ {
+ mGroundPool = new_poolp;
+ }
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ if( mHUDPool )
+ {
+ llerrs << "LLPipeline::addPool(): Duplicate HUD Pool!" << llendl;
+ }
+ mHUDPool = new_poolp;
+ break;
+
+ default:
+ llassert(0);
+ llwarns << "Invalid Pool Type in LLPipeline::addPool()" << llendl;
+ break;
+ }
+}
+
+void LLPipeline::removePool( LLDrawPool* poolp )
+{
+ removeFromQuickLookup(poolp);
+ mPools.erase(poolp);
+ delete poolp;
+}
+
+void LLPipeline::removeFromQuickLookup( LLDrawPool* poolp )
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+ switch( poolp->getType() )
+ {
+ case LLDrawPool::POOL_SIMPLE:
+ #ifdef _DEBUG
+ {
+ BOOL found = mSimplePools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mSimplePools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TREE:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTreePools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTreePools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TREE_NEW:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTreeNewPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_TERRAIN:
+ #ifdef _DEBUG
+ {
+ BOOL found = mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mTerrainPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_BUMP:
+ #ifdef _DEBUG
+ {
+ BOOL found = mBumpPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mBumpPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_MEDIA:
+ #ifdef _DEBUG
+ {
+ BOOL found = mMediaPools.erase( (uintptr_t)poolp->getTexture() );
+ llassert( found );
+ }
+ #else
+ mMediaPools.erase( (uintptr_t)poolp->getTexture() );
+ #endif
+ break;
+
+ case LLDrawPool::POOL_ALPHA:
+ llassert( poolp == mAlphaPool );
+ mAlphaPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_AVATAR:
+ break; // Do nothing
+
+ case LLDrawPool::POOL_SKY:
+ llassert( poolp == mSkyPool );
+ mSkyPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_STARS:
+ llassert( poolp == mStarsPool );
+ mStarsPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_CLOUDS:
+ llassert( poolp == mCloudsPool );
+ mCloudsPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_WATER:
+ llassert( poolp == mWaterPool );
+ mWaterPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_GROUND:
+ llassert( poolp == mGroundPool );
+ mGroundPool = NULL;
+ break;
+
+ case LLDrawPool::POOL_HUD:
+ llassert( poolp == mHUDPool );
+ mHUDPool = NULL;
+ break;
+
+ default:
+ llassert(0);
+ llwarns << "Invalid Pool Type in LLPipeline::removeFromQuickLookup() type=" << poolp->getType() << llendl;
+ break;
+ }
+}
+
+void LLPipeline::flushAGPMemory()
+{
+ LLMemType mt(LLMemType::MTYPE_PIPELINE);
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->flushAGP();
+ }
+}
+
+void LLPipeline::resetDrawOrders()
+{
+ // Iterate through all of the draw pools and rebuild them.
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ poolp->resetDrawOrders();
+ }
+}
+
+//-------------------------------
+
+
+LLViewerObject *LLPipeline::nearestObjectAt(F32 yaw, F32 pitch)
+{
+ // Stub to find nearest Object at given yaw and pitch
+
+ /*
+ LLEdge *vd = NULL;
+
+ if (vd)
+ {
+ return (LLViewerObject*)vd->mDrawablep->getVObj();
+ }
+ */
+
+ return NULL;
+}
+
+void LLPipeline::printPools()
+{
+ /*
+ // Iterate through all of the draw pools and rebuild them.
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (pool->mTexturep[0])
+ {
+ llinfos << "Op pool " << pool->mTexturep[0]->mID << llendflush;
+ }
+ else
+ {
+ llinfos << "Opaque pool NULL" << llendflush;
+ }
+ llinfos << " Vertices: \t" << pool->getVertexCount() << llendl;
+ }
+
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ llinfos << "Al pool " << pool;
+ llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
+ }
+
+ pool = mHighlightPools.getFirst();
+ llinfos << "Si pool " << pool;
+ llcont << " Vertices: \t" << pool->getVertexCount() << llendl;
+ */
+}
+
+
+void LLPipeline::displayPools()
+{
+ // Needs to be fixed to handle chained pools - djs
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ F32 x = 0.0f, y = 0.05f;
+ F32 xs = 0.01f, ys = 0.01f;
+ F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLGLSTexture gls_texture;
+ LLDrawPool *poolp = *iter;
+ if (poolp->getDebugTexture())
+ {
+ poolp->getDebugTexture()->bind();
+ glColor4f(1,1,1,1);
+ stamp(x,y,xs*4,ys*4);
+ }
+
+ LLGLSNoTexture no_texture;
+
+ F32 a = 1.f - (F32)poolp->mRebuildTime / (F32)poolp->mRebuildFreq;
+ glColor4f(a,a,a,1);
+ stamp(x,y,xs,ys);
+
+ x = (xs + xs2) * 4.0f;
+
+ F32 h = ys * 0.5f;
+
+ S32 total = 0;
+
+ for (std::vector<LLFace*>::iterator iter = poolp->mReferences.begin();
+ iter != poolp->mReferences.end(); iter++)
+ {
+ LLFace *face = *iter;
+ F32 w = xs;
+
+ if (!face || !face->getDrawable())
+ {
+ w = 16 / 3000.0f;
+
+ stamp(x,y,w,h);
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+
+ continue;
+ }
+
+ if (face->getDrawable()->isVisible())
+ {
+ if (face->isState(LLFace::BACKLIST))
+ {
+ glColor4f(1,0,1,1);
+ }
+ else if (total > poolp->getMaxVertices())
+ {
+ glColor4f(1,0,0,1);
+ }
+ else
+ {
+ glColor4f(0,1,0,1);
+ total += face->getGeomCount();
+ }
+ }
+ else
+ {
+ if (face->isState(LLFace::BACKLIST))
+ {
+ glColor4f(0,0,1,1);
+ }
+ else
+ {
+ glColor4f(1,1,0,1);
+ }
+ }
+
+ w = face->getGeomCount() / 3000.0f;
+
+ stamp(x,y,w,h);
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+ }
+
+ y += ys + ys2;
+ x = 0;
+ }
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
+
+static F32 xs = 0.01f, ys = 0.01f;
+static F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+static F32 winx, winy;
+
+void displayDrawable(F32 &x, F32 &y, LLDrawable *drawable, F32 alpha = 0.5f)
+{
+ F32 w = 0;
+ F32 h = ys * 0.5f;
+
+ if (drawable && !drawable->isDead())
+ {
+ for (S32 f=0;f < drawable->getNumFaces(); f++)
+ {
+ w += drawable->getFace(f)->getGeomCount() / 30000.0f;
+ }
+ w+=xs;
+ glColor4f(1,1,0, alpha);
+ }
+ else
+ {
+ w = 0.01f;
+ glColor4f(0,0,0,alpha);
+ }
+
+// const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF_SMALL );
+
+// U8 pcode = drawable->getVObj()->getPCode();
+
+ //char *string = (char*)LLPrimitive::pCodeToString(pcode);
+ //if (pcode == 0x3e) string = "terrain";
+ //else if (pcode == 0x2e) string = "cloud";
+ //string[3] = 0;
+
+ stamp(x * winx,y * winy,w * winx,h * winy);
+
+ /*
+ glColor4f(0,0,0,1);
+ font->render(string,x*winx+1,y*winy+1);
+ LLGLSNoTexture no_texture;
+ */
+
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h + ys2;
+ }
+ else
+ {
+ if (w) x += w + xs2;
+ }
+
+}
+
+#if 0 // No longer up date
+
+void displayQueue(F32 &x, F32 &y, LLDynamicArray<LLDrawable*>& processed, LLDynamicQueuePtr<LLPointer<LLDrawable> >& remaining)
+{
+ S32 i;
+ for (i=0;i<processed.count();i++)
+ {
+ displayDrawable(x,y,processed[i],1);
+ }
+
+ x += xs * 2;
+
+ S32 count = remaining.count();
+ for (i=0;i<count;i++)
+ {
+ LLDrawable* drawablep = remaining[(i + remaining.getFirst()) % remaining.getMax()];
+ if (drawablep && !drawablep->isDead())
+ {
+ displayDrawable(x,y,drawable,0.5);
+ }
+ }
+
+ y += ys * 4;
+ x = (xs + xs2) * 4.0f;
+
+}
+
+void LLPipeline::displayQueues()
+{
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_pipeline_alpha;
+ LLGLSNoTexture no_texture;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ winx = (F32)gViewerWindow->getWindowWidth();
+ winy = (F32)gViewerWindow->getWindowHeight();
+
+ glScalef(1.0f/winx,1.0f/winy,1);
+
+ F32 x = (xs + xs2) * 4.0f;
+ F32 y = 0.1f;
+
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ font->renderUTF8("Build1", 0,0,(S32)(y*winy),LLColor4(1,1,1,1));
+ displayQueue(x,y, gBuildProcessed, mBuildQ1);
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+
+}
+
+#endif
+
+// static
+void render_bbox(const LLVector3 &min, const LLVector3 &max)
+{
+ S32 i;
+ LLVector3 verticesp[16];
+
+ verticesp[0].setVec(min.mV[0],min.mV[1],max.mV[2]);
+ verticesp[1].setVec(min.mV[0],min.mV[1],min.mV[2]);
+ verticesp[2].setVec(min.mV[0],max.mV[1],min.mV[2]);
+ verticesp[3].setVec(min.mV[0],max.mV[1],max.mV[2]);
+ verticesp[4].setVec(max.mV[0],max.mV[1],max.mV[2]);
+ verticesp[5].setVec(max.mV[0],max.mV[1],min.mV[2]);
+ verticesp[6].setVec(max.mV[0],min.mV[1],min.mV[2]);
+ verticesp[7].setVec(max.mV[0],min.mV[1],max.mV[2]);
+ verticesp[8 ] = verticesp[0];
+ verticesp[9 ] = verticesp[1];
+ verticesp[10] = verticesp[6];
+ verticesp[11] = verticesp[7];
+ verticesp[12] = verticesp[4];
+ verticesp[13] = verticesp[5];
+ verticesp[14] = verticesp[2];
+ verticesp[15] = verticesp[3];
+
+ LLGLSNoTexture gls_no_texture;
+ {
+ LLUI::setLineWidth(1.f);
+ glBegin(GL_LINE_LOOP);
+ for (i = 0; i < 16; i++)
+ {
+ glVertex3fv(verticesp[i].mV);
+ }
+ glEnd();
+ }
+ {
+ LLGLDepthTest gls_depth(GL_TRUE);
+ LLUI::setLineWidth(3.0f);
+ glBegin(GL_LINE_LOOP);
+ for (i = 0; i < 16; i++)
+ {
+ glVertex3fv(verticesp[i].mV);
+ }
+ glEnd();
+ }
+ LLUI::setLineWidth(1.0f);
+}
+
+//============================================================================
+// Once-per-frame setup of hardware lights,
+// including sun/moon, avatar backlight, and up to 6 local lights
+
+void LLPipeline::setupAvatarLights(BOOL for_edit)
+{
+ const LLColor4 black(0,0,0,1);
+
+ if (for_edit)
+ {
+ LLColor4 diffuse(0.8f, 0.8f, 0.8f, 0.f);
+ LLVector4 light_pos_cam(-8.f, 0.25f, 10.f, 0.f); // w==0 => directional light
+ LLMatrix4 camera_mat = gCamera->getModelview();
+ LLMatrix4 camera_rot(camera_mat.getMat3());
+ camera_rot.invert();
+ LLVector4 light_pos = light_pos_cam * camera_rot;
+
+ light_pos.normVec();
+
+ mHWLightColors[1] = diffuse;
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, diffuse.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ glLightfv(GL_LIGHT1, GL_POSITION, light_pos.mV);
+ glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f);
+ }
+ else if (gAvatarBacklight) // Always true (unless overridden in a devs .ini)
+ {
+ LLVector3 opposite_pos = -1.f * mSunDir;
+ LLVector3 orthog_light_pos = mSunDir % LLVector3::z_axis;
+ LLVector4 backlight_pos = LLVector4(lerp(opposite_pos, orthog_light_pos, 0.3f), 0.0f);
+ backlight_pos.normVec();
+
+ LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor;
+ LLColor4 backlight_diffuse(1.f - light_diffuse.mV[VRED], 1.f - light_diffuse.mV[VGREEN], 1.f - light_diffuse.mV[VBLUE], 1.f);
+ F32 max_component = 0.001f;
+ for (S32 i = 0; i < 3; i++)
+ {
+ if (backlight_diffuse.mV[i] > max_component)
+ {
+ max_component = backlight_diffuse.mV[i];
+ }
+ }
+ F32 backlight_mag;
+ if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS)
+ {
+ backlight_mag = BACKLIGHT_DAY_MAGNITUDE_OBJECT;
+ }
+ else
+ {
+ backlight_mag = BACKLIGHT_NIGHT_MAGNITUDE_OBJECT;
+ }
+ backlight_diffuse *= backlight_mag / max_component;
+
+ mHWLightColors[1] = backlight_diffuse;
+ glLightfv(GL_LIGHT1, GL_POSITION, backlight_pos.mV); // this is just sun/moon direction
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, backlight_diffuse.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ glLightf (GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT1, GL_SPOT_CUTOFF, 180.0f);
+ }
+ else
+ {
+ mHWLightColors[1] = black;
+ glLightfv(GL_LIGHT1, GL_DIFFUSE, black.mV);
+ glLightfv(GL_LIGHT1, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT1, GL_SPECULAR, black.mV);
+ }
+}
+
+static F32 calc_light_dist(LLVOVolume* light, const LLVector3& cam_pos, F32 max_dist)
+{
+ F32 inten = light->getLightIntensity();
+ if (inten < .001f)
+ {
+ return max_dist;
+ }
+ F32 radius = light->getLightRadius();
+ BOOL selected = light->isSelected();
+ LLVector3 dpos = light->getRenderPosition() - cam_pos;
+ F32 dist2 = dpos.magVecSquared();
+ if (!selected && dist2 > (max_dist + radius)*(max_dist + radius))
+ {
+ return max_dist;
+ }
+ F32 dist = fsqrtf(dist2);
+ dist *= 1.f / inten;
+ dist -= radius;
+ if (selected)
+ {
+ dist -= 10000.f; // selected lights get highest priority
+ }
+ if (light->mDrawable.notNull() && light->mDrawable->isState(LLDrawable::ACTIVE))
+ {
+ // moving lights get a little higher priority (too much causes artifacts)
+ dist -= light->getLightRadius()*0.25f;
+ }
+ return dist;
+}
+
+void LLPipeline::calcNearbyLights()
+{
+ if (mLightingDetail >= 1)
+ {
+ // mNearbyLight (and all light_set_t's) are sorted such that
+ // begin() == the closest light and rbegin() == the farthest light
+ const S32 MAX_LOCAL_LIGHTS = 6;
+// LLVector3 cam_pos = gAgent.getCameraPositionAgent();
+ LLVector3 cam_pos = gAgent.getPositionAgent();
+
+ F32 max_dist = LIGHT_MAX_RADIUS * 4.f; // ignore enitrely lights > 4 * max light rad
+
+ // UPDATE THE EXISTING NEARBY LIGHTS
+ light_set_t cur_nearby_lights;
+ for (light_set_t::iterator iter = mNearbyLights.begin();
+ iter != mNearbyLights.end(); iter++)
+ {
+ const Light* light = &(*iter);
+ LLDrawable* drawable = light->drawable;
+ LLVOVolume* volight = drawable->getVOVolume();
+ if (!volight || !drawable->isState(LLDrawable::LIGHT))
+ {
+ drawable->clearState(LLDrawable::NEARBY_LIGHT);
+ continue;
+ }
+ if (light->fade <= -LIGHT_FADE_TIME)
+ {
+ drawable->clearState(LLDrawable::NEARBY_LIGHT);
+ }
+ else
+ {
+ F32 dist = calc_light_dist(volight, cam_pos, max_dist);
+ cur_nearby_lights.insert(Light(drawable, dist, light->fade));
+ }
+ }
+ mNearbyLights = cur_nearby_lights;
+
+ // FIND NEW LIGHTS THAT ARE IN RANGE
+ light_set_t new_nearby_lights;
+ for (LLDrawable::drawable_set_t::iterator iter = mLights.begin();
+ iter != mLights.end(); ++iter)
+ {
+ LLDrawable* drawable = *iter;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light || drawable->isState(LLDrawable::NEARBY_LIGHT))
+ {
+ continue;
+ }
+ if (light->isHUDAttachment())
+ {
+ continue; // no lighting from HUD objects
+ }
+ F32 dist = calc_light_dist(light, cam_pos, max_dist);
+ if (dist >= max_dist)
+ {
+ continue;
+ }
+ new_nearby_lights.insert(Light(drawable, dist, 0.f));
+ if (new_nearby_lights.size() > (U32)MAX_LOCAL_LIGHTS)
+ {
+ new_nearby_lights.erase(--new_nearby_lights.end());
+ const Light& last = *new_nearby_lights.rbegin();
+ max_dist = last.dist;
+ }
+ }
+
+ // INSERT ANY NEW LIGHTS
+ for (light_set_t::iterator iter = new_nearby_lights.begin();
+ iter != new_nearby_lights.end(); iter++)
+ {
+ const Light* light = &(*iter);
+ if (mNearbyLights.size() < (U32)MAX_LOCAL_LIGHTS)
+ {
+ mNearbyLights.insert(*light);
+ ((LLDrawable*) light->drawable)->setState(LLDrawable::NEARBY_LIGHT);
+ }
+ else
+ {
+ // crazy cast so that we can overwrite the fade value
+ // even though gcc enforces sets as const
+ // (fade value doesn't affect sort so this is safe)
+ Light* farthest_light = ((Light*) (&(*(mNearbyLights.rbegin()))));
+ if (light->dist < farthest_light->dist)
+ {
+ if (farthest_light->fade >= 0.f)
+ {
+ farthest_light->fade = -gFrameIntervalSeconds;
+ }
+ }
+ else
+ {
+ break; // none of the other lights are closer
+ }
+ }
+ }
+
+ }
+}
+
+void LLPipeline::setupHWLights(LLDrawPool* pool)
+{
+ const LLColor4 black(0,0,0,1);
+
+ setLightingDetail(-1); // update
+
+ // Ambient
+ LLColor4 ambient = gSky.getTotalAmbientColor();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
+
+ // Light 0 = Sun or Moon (All objects)
+ {
+ mSunShadowFactor = 1.f; // no shadowing by defailt
+ if (gSky.getSunDirection().mV[2] >= NIGHTTIME_ELEVATION_COS)
+ {
+ mSunDir.setVec(gSky.getSunDirection());
+ mSunDiffuse.setVec(gSky.getSunDiffuseColor());
+ }
+ else
+ {
+ mSunDir.setVec(gSky.getMoonDirection());
+ mSunDiffuse.setVec(gSky.getMoonDiffuseColor() * 1.5f);
+ }
+
+ F32 max_color = llmax(mSunDiffuse.mV[0], mSunDiffuse.mV[1], mSunDiffuse.mV[2]);
+ if (max_color > 1.f)
+ {
+ mSunDiffuse *= 1.f/max_color;
+ }
+ mSunDiffuse.clamp();
+
+ LLVector4 light_pos(mSunDir, 0.0f);
+ LLColor4 light_diffuse = mSunDiffuse * mSunShadowFactor;
+ mHWLightColors[0] = light_diffuse;
+ glLightfv(GL_LIGHT0, GL_POSITION, light_pos.mV); // this is just sun/moon direction
+ glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse.mV);
+ glLightfv(GL_LIGHT0, GL_AMBIENT, black.mV);
+ glLightfv(GL_LIGHT0, GL_SPECULAR, black.mV);
+ glLightf (GL_LIGHT0, GL_CONSTANT_ATTENUATION, 1.0f);
+ glLightf (GL_LIGHT0, GL_LINEAR_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.0f);
+ glLightf (GL_LIGHT0, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (GL_LIGHT0, GL_SPOT_CUTOFF, 180.0f);
+ }
+
+ // Light 1 = Backlight (for avatars)
+ // (set by enableLightsAvatar)
+
+ S32 cur_light = 2;
+
+ // Nearby lights = LIGHT 2-7
+
+ mLightMovingMask = 0;
+
+ if (mLightingDetail >= 1)
+ {
+ for (light_set_t::iterator iter = mNearbyLights.begin();
+ iter != mNearbyLights.end(); ++iter)
+ {
+ LLDrawable* drawable = iter->drawable;
+ LLVOVolume* light = drawable->getVOVolume();
+ if (!light)
+ {
+ continue;
+ }
+ if (drawable->isState(LLDrawable::ACTIVE))
+ {
+ mLightMovingMask |= (1<<cur_light);
+ }
+
+ LLColor4 light_color = light->getLightColor();
+ light_color.mV[3] = 0.0f;
+
+ F32 fade = iter->fade;
+ if (fade < LIGHT_FADE_TIME)
+ {
+ // fade in/out light
+ if (fade >= 0.f)
+ {
+ fade = fade / LIGHT_FADE_TIME;
+ ((Light*) (&(*iter)))->fade += gFrameIntervalSeconds;
+ }
+ else
+ {
+ fade = 1.f + fade / LIGHT_FADE_TIME;
+ ((Light*) (&(*iter)))->fade -= gFrameIntervalSeconds;
+ }
+ fade = llclamp(fade,0.f,1.f);
+ light_color *= fade;
+ }
+
+ LLVector3 light_pos(light->getRenderPosition());
+ LLVector4 light_pos_gl(light_pos, 1.0f);
+
+ F32 light_radius = llmax(light->getLightRadius(), 0.001f);
+ F32 atten, quad;
+
+#if 0 //1.9.1
+ if (pool->getVertexShaderLevel() > 0)
+ {
+ atten = light_radius;
+ quad = llmax(light->getLightFalloff(), 0.0001f);
+ }
+ else
+#endif
+ {
+ F32 x = (3.f * (1.f + light->getLightFalloff()));
+ atten = x / (light_radius); // % of brightness at radius
+ quad = 0.0f;
+ }
+ mHWLightColors[cur_light] = light_color;
+ S32 gllight = GL_LIGHT0+cur_light;
+ glLightfv(gllight, GL_POSITION, light_pos_gl.mV);
+ glLightfv(gllight, GL_DIFFUSE, light_color.mV);
+ glLightfv(gllight, GL_AMBIENT, black.mV);
+ glLightfv(gllight, GL_SPECULAR, black.mV);
+ glLightf (gllight, GL_CONSTANT_ATTENUATION, 0.0f);
+ glLightf (gllight, GL_LINEAR_ATTENUATION, atten);
+ glLightf (gllight, GL_QUADRATIC_ATTENUATION, quad);
+ glLightf (gllight, GL_SPOT_EXPONENT, 0.0f);
+ glLightf (gllight, GL_SPOT_CUTOFF, 180.0f);
+ cur_light++;
+ if (cur_light >= 8)
+ {
+ break; // safety
+ }
+ }
+ }
+ for ( ; cur_light < 8 ; cur_light++)
+ {
+ mHWLightColors[cur_light] = black;
+ S32 gllight = GL_LIGHT0+cur_light;
+ glLightfv(gllight, GL_DIFFUSE, black.mV);
+ glLightfv(gllight, GL_AMBIENT, black.mV);
+ glLightfv(gllight, GL_SPECULAR, black.mV);
+ }
+
+ // Init GL state
+ glDisable(GL_LIGHTING);
+ for (S32 gllight=GL_LIGHT0; gllight<=GL_LIGHT7; gllight++)
+ {
+ glDisable(gllight);
+ }
+ mLightMask = 0;
+}
+
+void LLPipeline::enableLights(U32 mask, F32 shadow_factor)
+{
+ if (mLightingDetail == 0)
+ {
+ mask &= 0xf003; // sun and backlight only (and fullbright bit)
+ }
+ if (mLightMask != mask)
+ {
+ if (!mLightMask)
+ {
+ glEnable(GL_LIGHTING);
+ }
+ if (mask)
+ {
+ for (S32 i=0; i<8; i++)
+ {
+ if (mask & (1<<i))
+ {
+ glEnable(GL_LIGHT0 + i);
+ glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, mHWLightColors[i].mV);
+ }
+ else
+ {
+ glDisable(GL_LIGHT0 + i);
+ glLightfv(GL_LIGHT0 + i, GL_DIFFUSE, LLColor4::black.mV);
+ }
+ }
+ }
+ else
+ {
+ glDisable(GL_LIGHTING);
+ }
+ mLightMask = mask;
+ LLColor4 ambient = gSky.getTotalAmbientColor();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,ambient.mV);
+ }
+}
+
+void LLPipeline::enableLightsStatic(F32 shadow_factor)
+{
+ U32 mask = 0x01; // Sun
+ if (mLightingDetail >= 2)
+ {
+ mask |= mLightMovingMask; // Hardware moving lights
+ glColor4f(0.f, 0.f, 0.f, 1.0f); // no local lighting by default
+ }
+ else
+ {
+ mask |= 0xff & (~2); // Hardware local lights
+ }
+ enableLights(mask, shadow_factor);
+}
+
+void LLPipeline::enableLightsDynamic(F32 shadow_factor)
+{
+ U32 mask = 0xff & (~2); // Local lights
+ enableLights(mask, shadow_factor);
+ if (mLightingDetail >= 2)
+ {
+ glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
+ }
+}
+
+void LLPipeline::enableLightsAvatar(F32 shadow_factor)
+{
+ U32 mask = 0xff; // All lights
+ setupAvatarLights(FALSE);
+ enableLights(mask, shadow_factor);
+}
+
+void LLPipeline::enableLightsAvatarEdit(const LLColor4& color)
+{
+ U32 mask = 0x2002; // Avatar backlight only, set ambient
+ setupAvatarLights(TRUE);
+ enableLights(mask, 1.0f);
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV);
+}
+
+void LLPipeline::enableLightsFullbright(const LLColor4& color)
+{
+ U32 mask = 0x1000; // Non-0 mask, set ambient
+ enableLights(mask, 1.f);
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,color.mV);
+ if (mLightingDetail >= 2)
+ {
+ glColor4f(0.f, 0.f, 0.f, 1.f); // no local lighting by default
+ }
+}
+
+void LLPipeline::disableLights()
+{
+ enableLights(0, 0.f); // no lighting (full bright)
+ glColor4f(1.f, 1.f, 1.f, 1.f); // lighting color = white by default
+}
+
+// Call *after*s etting up lights
+void LLPipeline::setAmbient(const LLColor4& ambient)
+{
+ mLightMask |= 0x4000; // tweak mask so that ambient will get reset
+ LLColor4 amb = ambient + gSky.getTotalAmbientColor();
+ amb.clamp();
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT,amb.mV);
+}
+
+//============================================================================
+
+class LLMenuItemGL;
+class LLInvFVBridge;
+struct cat_folder_pair;
+class LLVOBranch;
+class LLVOLeaf;
+class Foo;
+
+template<> char* LLAGPArray<U8>::sTypeName = "U8 [AGP]";
+template<> char* LLAGPArray<U32>::sTypeName = "U32 [AGP]";
+template<> char* LLAGPArray<F32>::sTypeName = "F32 [AGP]";
+template<> char* LLAGPArray<LLColor4>::sTypeName = "LLColor4 [AGP]";
+template<> char* LLAGPArray<LLColor4U>::sTypeName = "LLColor4U [AGP]";
+template<> char* LLAGPArray<LLVector4>::sTypeName = "LLVector4 [AGP]";
+template<> char* LLAGPArray<LLVector3>::sTypeName = "LLVector3 [AGP]";
+template<> char* LLAGPArray<LLVector2>::sTypeName = "LLVector2 [AGP]";
+template<> char* LLAGPArray<LLFace*>::sTypeName = "LLFace* [AGP]";
+
+void scale_stamp(const F32 x, const F32 y, const F32 xs, const F32 ys)
+{
+ stamp(0.25f + 0.5f*x,
+ 0.5f + 0.45f*y,
+ 0.5f*xs,
+ 0.45f*ys);
+}
+
+void drawBars(const F32 begin, const F32 end, const F32 height = 1.f)
+{
+ if (begin >= 0 && end <=1)
+ {
+ F32 lines = 40.0f;
+ S32 ibegin = (S32)(begin * lines);
+ S32 iend = (S32)(end * lines);
+ F32 fbegin = begin * lines - ibegin;
+ F32 fend = end * lines - iend;
+
+ F32 line_height = height/lines;
+
+ if (iend == ibegin)
+ {
+ scale_stamp(fbegin, (F32)ibegin/lines,fend-fbegin, line_height);
+ }
+ else
+ {
+ // Beginning row
+ scale_stamp(fbegin, (F32)ibegin/lines, 1.0f-fbegin, line_height);
+
+ // End row
+ scale_stamp(0.0, (F32)iend/lines, fend, line_height);
+
+ // Middle rows
+ for (S32 l = (ibegin+1); l < iend; l++)
+ {
+ scale_stamp(0.0f, (F32)l/lines, 1.0f, line_height);
+ }
+ }
+ }
+}
+
+void LLPipeline::displayAGP()
+{
+ LLUI::setLineWidth(1.0);
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ LLGLSPipelineAlpha gls_alpha;
+ LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE);
+
+ LLImageGL::unbindTexture(0, GL_TEXTURE_2D);
+ glScalef(2,2,1);
+ glTranslatef(-0.5f,-0.5f,0);
+
+ glColor4f(0,0,0,0.5f);
+ scale_stamp(0,0,1,1);
+
+ F32 x = 0.0f, y = 0.05f;
+ F32 xs = 0.015f, ys = 0.015f;
+ F32 xs2 = xs*0.1f, ys2 = ys * 0.1f;
+ F32 w = xs+xs2, h = ys + ys2;
+ S32 i=0;
+
+ F32 agp_size = 1.f;
+ if (mAGPMemPool)
+ {
+ agp_size = (F32)mAGPMemPool->getSize();
+ }
+
+ x = (xs + xs2) * 4.0f;
+
+ static float c = 0.0f;
+ c += 0.0001f;
+
+ LLAGPMemBlock *blockp = mBufferMemory[mBufferIndex]->getAGPMemBlock();
+
+ pool_set_t::iterator iter = mPools.begin();
+ LLDrawPool *poolp = *iter;
+
+ // Dump the shared AGP buffer
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mMemory.count()/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,used, 0.5f);
+
+ glColor4f(0.5f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end, 0.25f);
+ }
+
+ S32 used_bytes = 0;
+ S32 total_bytes = 0;
+ while (iter != mPools.end())
+ {
+ poolp = *iter++;
+
+ BOOL synced = FALSE;
+ i++;
+ total_bytes += poolp->mMemory.getMax();
+ used_bytes += poolp->mMemory.count();
+ LLViewerImage *texturep = poolp->getDebugTexture();
+
+
+ if (poolp->mMemory.mSynced)
+ {
+ poolp->mMemory.mSynced = FALSE;
+ synced = TRUE;
+ }
+
+ LLAGPMemBlock *blockp = poolp->mMemory.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mMemory.count()/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 0.5f, 0.5f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(1.f, 0.75f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 0.75f, 0.75f, 1.f);
+ drawBars(begin,used, 0.5f);
+
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.4f);
+ drawBars(begin,end);
+ }
+ }
+
+ synced = FALSE;
+ if (poolp->mWeights.mSynced)
+ {
+ poolp->mWeights.mSynced = FALSE;
+ synced = TRUE;
+ }
+ blockp = poolp->mWeights.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mWeights.count()*sizeof(float)/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.0f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(0.0, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.0, 0.f, 0.75f, 1.f);
+ drawBars(begin,used, 0.5f);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.4f);
+ drawBars(begin,end);
+ }
+ }
+
+ synced = FALSE;
+ if (poolp->mClothingWeights.mSynced)
+ {
+ poolp->mClothingWeights.mSynced = FALSE;
+ synced = TRUE;
+ }
+ blockp = poolp->mClothingWeights.getAGPMemBlock();
+ if (blockp)
+ {
+ F32 begin = blockp->getOffset()/agp_size;
+ F32 end = begin + (blockp->getSize()/agp_size);
+ F32 used = begin + (poolp->mClothingWeights.count()*sizeof(LLVector4)/agp_size);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,end);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(0.75f, 0.f, 0.75f, 0.5f);
+ drawBars(begin,used, 0.5f);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ drawBars(begin,end, 0.25f);
+
+ if (synced)
+ {
+ LLViewerImage::bindTexture(NULL);
+ glColor4f(1.f, 1.f, 1.f, 0.5f);
+ drawBars(begin,end);
+ }
+ }
+
+ //
+ // Stamps on bottom of screen
+ //
+ LLViewerImage::bindTexture(texturep);
+ glColor4f(1.f, 1.f, 1.f, 1.f);
+ stamp(x,y,xs,ys);
+
+ LLViewerImage::bindTexture(NULL);
+ glColor3fv(poolp->getDebugColor().mV);
+ stamp(x,y,xs, ys*0.25f);
+ if (x+w > 0.95f)
+ {
+ x = (xs + xs2) * 4.0f;
+ y += h;
+ }
+ else
+ {
+ x += w + xs2;
+ }
+ }
+
+ glPopMatrix();
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+}
+
+void LLPipeline::findReferences(LLDrawable *drawablep)
+{
+ if (std::find(mVisibleList.begin(), mVisibleList.end(), drawablep) != mVisibleList.end())
+ {
+ llinfos << "In mVisibleList" << llendl;
+ }
+ if (mLights.find(drawablep) != mLights.end())
+ {
+ llinfos << "In mLights" << llendl;
+ }
+ if (mMovedList.find(drawablep) != mMovedList.end())
+ {
+ llinfos << "In mMovedList" << llendl;
+ }
+ if (std::find(mShiftList.begin(), mShiftList.end(), drawablep) != mShiftList.end())
+ {
+ llinfos << "In mShiftList" << llendl;
+ }
+ if (mRetexturedList.find(drawablep) != mRetexturedList.end())
+ {
+ llinfos << "In mRetexturedList" << llendl;
+ }
+ if (mRematerialedList.find(drawablep) != mRematerialedList.end())
+ {
+ llinfos << "In mRematerialedList" << llendl;
+ }
+
+ if (mActiveQ.find(drawablep) != mActiveQ.end())
+ {
+ llinfos << "In mActiveQ" << llendl;
+ }
+ if (mBuildQ1.find(drawablep) != mBuildQ1.end())
+ {
+ llinfos << "In mBuildQ2" << llendl;
+ }
+ if (std::find(mBuildQ2.begin(), mBuildQ2.end(), drawablep) != mBuildQ2.end())
+ {
+ llinfos << "In mBuildQ2" << llendl;
+ }
+
+ S32 count;
+ /*
+ count = mStaticTree->count(drawablep);
+ if (count)
+ {
+ llinfos << "In mStaticTree: " << count << " references" << llendl;
+ }
+
+ count = mDynamicTree->count(drawablep);
+ if (count)
+ {
+ llinfos << "In mStaticTree: " << count << " references" << llendl;
+ }
+ */
+ count = gObjectList.findReferences(drawablep);
+ if (count)
+ {
+ llinfos << "In other drawables: " << count << " references" << llendl;
+ }
+}
+
+BOOL LLPipeline::verify()
+{
+ BOOL ok = TRUE;
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ if (!poolp->verify())
+ {
+ ok = FALSE;
+ }
+ }
+
+ if (!ok)
+ {
+ llwarns << "Pipeline verify failed!" << llendl;
+ }
+ return ok;
+}
+
+S32 LLPipeline::getAGPMemUsage()
+{
+ if (mAGPMemPool)
+ {
+ return mAGPMemPool->getSize();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+S32 LLPipeline::getMemUsage(const BOOL print)
+{
+ S32 mem_usage = 0;
+
+ if (mAGPMemPool)
+ {
+ S32 agp_usage = 0;
+ agp_usage = mAGPMemPool->getSize();
+ if (print)
+ {
+ llinfos << "AGP Mem: " << agp_usage << llendl;
+ llinfos << "AGP Mem used: " << mAGPMemPool->getTotalAllocated() << llendl;
+ }
+ mem_usage += agp_usage;
+ }
+
+
+ S32 pool_usage = 0;
+ for (pool_set_t::iterator iter = mPools.begin(); iter != mPools.end(); ++iter)
+ {
+ LLDrawPool *poolp = *iter;
+ pool_usage += poolp->getMemUsage(print);
+ }
+
+ if (print)
+ {
+ llinfos << "Pool Mem: " << pool_usage << llendl;
+ }
+
+ mem_usage += pool_usage;
+
+ return mem_usage;
+
+}
+
+//////////////////////////////
+//
+// Collision detection
+//
+//
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+/**
+ * A method to compute a ray-AABB intersection.
+ * Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
+ * Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
+ * Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
+ *
+ * Hence this version is faster as well as more robust than the original one.
+ *
+ * Should work provided:
+ * 1) the integer representation of 0.0f is 0x00000000
+ * 2) the sign bit of the float is the most significant one
+ *
+ * Report bugs: p.terdiman@codercorner.com
+ *
+ * \param aabb [in] the axis-aligned bounding box
+ * \param origin [in] ray origin
+ * \param dir [in] ray direction
+ * \param coord [out] impact coordinates
+ * \return true if ray intersects AABB
+ */
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//#define RAYAABB_EPSILON 0.00001f
+#define IR(x) ((U32&)x)
+
+bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon)
+{
+ BOOL Inside = TRUE;
+ LLVector3 MinB = center - size;
+ LLVector3 MaxB = center + size;
+ LLVector3 MaxT;
+ MaxT.mV[VX]=MaxT.mV[VY]=MaxT.mV[VZ]=-1.0f;
+
+ // Find candidate planes.
+ for(U32 i=0;i<3;i++)
+ {
+ if(origin.mV[i] < MinB.mV[i])
+ {
+ coord.mV[i] = MinB.mV[i];
+ Inside = FALSE;
+
+ // Calculate T distances to candidate planes
+ if(IR(dir.mV[i])) MaxT.mV[i] = (MinB.mV[i] - origin.mV[i]) / dir.mV[i];
+ }
+ else if(origin.mV[i] > MaxB.mV[i])
+ {
+ coord.mV[i] = MaxB.mV[i];
+ Inside = FALSE;
+
+ // Calculate T distances to candidate planes
+ if(IR(dir.mV[i])) MaxT.mV[i] = (MaxB.mV[i] - origin.mV[i]) / dir.mV[i];
+ }
+ }
+
+ // Ray origin inside bounding box
+ if(Inside)
+ {
+ coord = origin;
+ return true;
+ }
+
+ // Get largest of the maxT's for final choice of intersection
+ U32 WhichPlane = 0;
+ if(MaxT.mV[1] > MaxT.mV[WhichPlane]) WhichPlane = 1;
+ if(MaxT.mV[2] > MaxT.mV[WhichPlane]) WhichPlane = 2;
+
+ // Check final candidate actually inside box
+ if(IR(MaxT.mV[WhichPlane])&0x80000000) return false;
+
+ for(U32 i=0;i<3;i++)
+ {
+ if(i!=WhichPlane)
+ {
+ coord.mV[i] = origin.mV[i] + MaxT.mV[WhichPlane] * dir.mV[i];
+ if (epsilon > 0)
+ {
+ if(coord.mV[i] < MinB.mV[i] - epsilon || coord.mV[i] > MaxB.mV[i] + epsilon) return false;
+ }
+ else
+ {
+ if(coord.mV[i] < MinB.mV[i] || coord.mV[i] > MaxB.mV[i]) return false;
+ }
+ }
+ }
+ return true; // ray hits box
+}
+
+//////////////////////////////
+//
+// Macros, functions, and inline methods from other classes
+//
+//
+
+void LLPipeline::setLight(LLDrawable *drawablep, BOOL is_light)
+{
+ if (drawablep)
+ {
+ if (is_light)
+ {
+ mLights.insert(drawablep);
+ drawablep->setState(LLDrawable::LIGHT);
+ }
+ else
+ {
+ mLights.erase(drawablep);
+ drawablep->clearState(LLDrawable::LIGHT);
+ }
+ markRelight(drawablep);
+ }
+}
+
+void LLPipeline::setActive(LLDrawable *drawablep, BOOL active)
+{
+ if (active)
+ {
+ mActiveQ.insert(drawablep);
+ }
+ else
+ {
+ mActiveQ.erase(drawablep);
+ }
+}
+
+//static
+void LLPipeline::toggleRenderType(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ U32 bit = (1<<type);
+ if (gPipeline.hasRenderType(type))
+ {
+ llinfos << "Toggling render type mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render type mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderTypeMask ^= bit;
+}
+
+//static
+BOOL LLPipeline::toggleRenderTypeControl(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ return gPipeline.hasRenderType(type);
+}
+
+// Allows UI items labeled "Hide foo" instead of "Show foo"
+//static
+BOOL LLPipeline::toggleRenderTypeControlNegated(void* data)
+{
+ S32 type = (S32)(intptr_t)data;
+ return !gPipeline.hasRenderType(type);
+}
+
+//static
+void LLPipeline::toggleRenderDebug(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ if (gPipeline.hasRenderDebugMask(bit))
+ {
+ llinfos << "Toggling render debug mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render debug mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderDebugMask ^= bit;
+}
+
+
+//static
+BOOL LLPipeline::toggleRenderDebugControl(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ return gPipeline.hasRenderDebugMask(bit);
+}
+
+//static
+void LLPipeline::toggleRenderDebugFeature(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ if (gPipeline.hasRenderDebugFeatureMask(bit))
+ {
+ llinfos << "Toggling render debug feature mask " << std::hex << bit << " off" << std::dec << llendl;
+ }
+ else
+ {
+ llinfos << "Toggling render debug feature mask " << std::hex << bit << " on" << std::dec << llendl;
+ }
+ gPipeline.mRenderDebugFeatureMask ^= bit;
+}
+
+
+//static
+BOOL LLPipeline::toggleRenderDebugFeatureControl(void* data)
+{
+ U32 bit = (U32)(intptr_t)data;
+ return gPipeline.hasRenderDebugFeatureMask(bit);
+}
+
+// static
+void LLPipeline::toggleRenderScriptedBeacons(void*)
+{
+ sRenderScriptedBeacons = !sRenderScriptedBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderScriptedBeacons(void*)
+{
+ return sRenderScriptedBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderPhysicalBeacons(void*)
+{
+ sRenderPhysicalBeacons = !sRenderPhysicalBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderPhysicalBeacons(void*)
+{
+ return sRenderPhysicalBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderParticleBeacons(void*)
+{
+ sRenderParticleBeacons = !sRenderParticleBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderParticleBeacons(void*)
+{
+ return sRenderParticleBeacons;
+}
+
+// static
+void LLPipeline::toggleRenderSoundBeacons(void*)
+{
+ sRenderSoundBeacons = !sRenderSoundBeacons;
+}
+
+// static
+BOOL LLPipeline::getRenderSoundBeacons(void*)
+{
+ return sRenderSoundBeacons;
+}
+
+//===============================
+// LLGLSL Shader implementation
+//===============================
+LLGLSLShader::LLGLSLShader()
+: mProgramObject(0)
+{ }
+
+void LLGLSLShader::unload()
+{
+ stop_glerror();
+ mAttribute.clear();
+ mTexture.clear();
+ mUniform.clear();
+
+ if (mProgramObject)
+ {
+ GLhandleARB obj[1024];
+ GLsizei count;
+
+ glGetAttachedObjectsARB(mProgramObject, 1024, &count, obj);
+ for (GLsizei i = 0; i < count; i++)
+ {
+ glDeleteObjectARB(obj[i]);
+ }
+
+ glDeleteObjectARB(mProgramObject);
+
+ mProgramObject = 0;
+ }
+
+ //hack to make apple not complain
+ glGetError();
+
+ stop_glerror();
+}
+
+void LLGLSLShader::attachObject(GLhandleARB object)
+{
+ if (object != 0)
+ {
+ stop_glerror();
+ glAttachObjectARB(mProgramObject, object);
+ stop_glerror();
+ }
+ else
+ {
+ llwarns << "Attempting to attach non existing shader object. " << llendl;
+ }
+}
+
+void LLGLSLShader::attachObjects(GLhandleARB* objects, S32 count)
+{
+ for (S32 i = 0; i < count; i++)
+ {
+ attachObject(objects[i]);
+ }
+}
+
+BOOL LLGLSLShader::mapAttributes(const char** attrib_names, S32 count)
+{
+ //link the program
+ BOOL res = link();
+
+ mAttribute.clear();
+ mAttribute.resize(LLPipeline::sReservedAttribCount + count, -1);
+
+ if (res)
+ { //read back channel locations
+
+ //read back reserved channels first
+ for (S32 i = 0; i < (S32) LLPipeline::sReservedAttribCount; i++)
+ {
+ const char* name = LLPipeline::sReservedAttribs[i];
+ S32 index = glGetAttribLocationARB(mProgramObject, name);
+ if (index != -1)
+ {
+ mAttribute[i] = index;
+ llinfos << "Attribute " << name << " assigned to channel " << index << llendl;
+ }
+ }
+
+ for (S32 i = 0; i < count; i++)
+ {
+ const char* name = attrib_names[i];
+ S32 index = glGetAttribLocationARB(mProgramObject, name);
+ if (index != -1)
+ {
+ mAttribute[LLPipeline::sReservedAttribCount + i] = index;
+ llinfos << "Attribute " << name << " assigned to channel " << index << llendl;
+ }
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void LLGLSLShader::mapUniform(GLint index, const char** uniform_names, S32 count)
+{
+ if (index == -1)
+ {
+ return;
+ }
+
+ GLenum type;
+ GLsizei length;
+ GLint size;
+ char name[1024];
+ name[0] = 0;
+
+ glGetActiveUniformARB(mProgramObject, index, 1024, &length, &size, &type, name);
+
+ //find the index of this uniform
+ for (S32 i = 0; i < (S32) LLPipeline::sReservedUniformCount; i++)
+ {
+ if (mUniform[i] == -1 && !strncmp(LLPipeline::sReservedUniforms[i],name, strlen(LLPipeline::sReservedUniforms[i])))
+ {
+ //found it
+ S32 location = glGetUniformLocationARB(mProgramObject, name);
+ mUniform[i] = location;
+ llinfos << "Uniform " << name << " is at location " << location << llendl;
+ mTexture[i] = mapUniformTextureChannel(location, type);
+ return;
+ }
+ }
+
+ for (S32 i = 0; i < count; i++)
+ {
+ if (mUniform[i+LLPipeline::sReservedUniformCount] == -1 &&
+ !strncmp(uniform_names[i],name, strlen(uniform_names[i])))
+ {
+ //found it
+ S32 location = glGetUniformLocationARB(mProgramObject, name);
+ mUniform[i+LLPipeline::sReservedUniformCount] = location;
+ llinfos << "Uniform " << name << " is at location " << location << " stored in index " <<
+ (i+LLPipeline::sReservedUniformCount) << llendl;
+ mTexture[i+LLPipeline::sReservedUniformCount] = mapUniformTextureChannel(location, type);
+ return;
+ }
+ }
+
+ //llinfos << "Unknown uniform: " << name << llendl;
+ }
+
+GLint LLGLSLShader::mapUniformTextureChannel(GLint location, GLenum type)
+{
+ if (type >= GL_SAMPLER_1D_ARB && type <= GL_SAMPLER_2D_RECT_SHADOW_ARB)
+ { //this here is a texture
+ glUniform1iARB(location, mActiveTextureChannels);
+ llinfos << "Assigned to texture channel " << mActiveTextureChannels << llendl;
+ return mActiveTextureChannels++;
+ }
+ return -1;
+}
+
+BOOL LLGLSLShader::mapUniforms(const char** uniform_names, S32 count)
+{
+ BOOL res = TRUE;
+
+ mActiveTextureChannels = 0;
+ mUniform.clear();
+ mTexture.clear();
+
+ //initialize arrays
+ mUniform.resize(count + LLPipeline::sReservedUniformCount, -1);
+ mTexture.resize(count + LLPipeline::sReservedUniformCount, -1);
+
+
+
+ bind();
+
+ //get the number of active uniforms
+ GLint activeCount;
+ glGetObjectParameterivARB(mProgramObject, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &activeCount);
+
+ for (S32 i = 0; i < activeCount; i++)
+ {
+ mapUniform(i, uniform_names, count);
+ }
+
+ unbind();
+
+ return res;
+}
+
+BOOL LLGLSLShader::link(BOOL suppress_errors)
+{
+ return gPipeline.linkProgramObject(mProgramObject, suppress_errors);
+}
+
+void LLGLSLShader::bind()
+{
+ glUseProgramObjectARB(mProgramObject);
+ if (mAttribute.size() > 0)
+ {
+ gPipeline.mMaterialIndex = mAttribute[0];
+ }
+}
+
+void LLGLSLShader::unbind()
+{
+ for (U32 i = 0; i < mAttribute.size(); ++i)
+ {
+ vertexAttrib4f(i, 0,0,0,1);
+ }
+ glUseProgramObjectARB(0);
+}
+
+S32 LLGLSLShader::enableTexture(S32 uniform, S32 mode)
+{
+ if (uniform < 0 || uniform >= (S32)mTexture.size())
+ {
+ llerrs << "LLGLSLShader::enableTexture: uniform out of range: " << uniform << llendl;
+ }
+ S32 index = mTexture[uniform];
+ if (index != -1)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+index);
+ glEnable(mode);
+ }
+ return index;
+}
+
+S32 LLGLSLShader::disableTexture(S32 uniform, S32 mode)
+{
+ S32 index = mTexture[uniform];
+ if (index != -1)
+ {
+ glActiveTextureARB(GL_TEXTURE0_ARB+index);
+ glDisable(mode);
+ }
+ return index;
+}
+
+void LLGLSLShader::vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4fARB(mAttribute[index], x, y, z, w);
+ }
+}
+
+void LLGLSLShader::vertexAttrib4fv(U32 index, GLfloat* v)
+{
+ if (mAttribute[index] > 0)
+ {
+ glVertexAttrib4fvARB(mAttribute[index], v);
+ }
+}
+
+LLViewerObject* LLPipeline::pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision)
+{
+ LLDrawable* drawable = mObjectPartition->pickDrawable(start, end, collision);
+ return drawable ? drawable->getVObj() : NULL;
+}
+
+
diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h
new file mode 100644
index 0000000000..4dada886cb
--- /dev/null
+++ b/indra/newview/pipeline.h
@@ -0,0 +1,659 @@
+/**
+ * @file pipeline.h
+ * @brief Rendering pipeline definitions
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_PIPELINE_H
+#define LL_PIPELINE_H
+
+#include "llagparray.h"
+#include "lldarrayptr.h"
+#include "lldqueueptr.h"
+#include "llstat.h"
+#include "lllinkedqueue.h"
+#include "llskiplist.h"
+#include "lldrawpool.h"
+#include "llspatialpartition.h"
+#include "m4math.h"
+#include "llmemory.h"
+#include "lldrawpool.h"
+#include "llgl.h"
+
+class LLViewerImage;
+class LLDrawable;
+class LLEdge;
+class LLFace;
+class LLViewerObject;
+class LLAgent;
+class LLDisplayPrimitive;
+class LLTextureEntry;
+class LLRenderFunc;
+class LLAGPMemPool;
+class LLAGPMemBlock;
+
+typedef enum e_avatar_skinning_method
+{
+ SKIN_METHOD_SOFTWARE,
+ SKIN_METHOD_VERTEX_PROGRAM
+} EAvatarSkinningMethod;
+
+BOOL compute_min_max(LLMatrix4& box, LLVector2& min, LLVector2& max); // Shouldn't be defined here!
+bool LLRayAABB(const LLVector3 &center, const LLVector3 &size, const LLVector3& origin, const LLVector3& dir, LLVector3 &coord, F32 epsilon = 0);
+BOOL LLLineSegmentAABB(const LLVector3& start, const LLVector3& end, const LLVector3& center, const LLVector3& size);
+
+class LLGLSLShader
+{
+public:
+ LLGLSLShader();
+
+ void unload();
+ void attachObject(GLhandleARB object);
+ void attachObjects(GLhandleARB* objects = NULL, S32 count = 0);
+ BOOL mapAttributes(const char** attrib_names = NULL, S32 count = 0);
+ BOOL mapUniforms(const char** uniform_names = NULL, S32 count = 0);
+ void mapUniform(GLint index, const char** uniform_names = NULL, S32 count = 0);
+ void vertexAttrib4f(U32 index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
+ void vertexAttrib4fv(U32 index, GLfloat* v);
+
+ GLint mapUniformTextureChannel(GLint location, GLenum type);
+
+
+ //enable/disable texture channel for specified uniform
+ //if given texture uniform is active in the shader,
+ //the corresponding channel will be active upon return
+ //returns channel texture is enabled in from [0-MAX)
+ S32 enableTexture(S32 uniform, S32 mode = GL_TEXTURE_2D);
+ S32 disableTexture(S32 uniform, S32 mode = GL_TEXTURE_2D);
+
+ BOOL link(BOOL suppress_errors = FALSE);
+ void bind();
+ void unbind();
+
+
+ GLhandleARB mProgramObject;
+ std::vector<GLint> mAttribute;
+ std::vector<GLint> mUniform;
+ std::vector<GLint> mTexture;
+ S32 mActiveTextureChannels;
+};
+
+class LLPipeline
+{
+public:
+ LLPipeline();
+ ~LLPipeline();
+
+ void destroyGL();
+ void restoreGL();
+
+ void init();
+ void cleanup();
+
+ /// @brief Get a draw pool from pool type (POOL_SIMPLE, POOL_MEDIA) and texture.
+ /// @return Draw pool, or NULL if not found.
+ LLDrawPool *findPool(const U32 pool_type, LLViewerImage *tex0 = NULL);
+
+ /// @brief Get a draw pool for faces of the appropriate type and texture. Create if necessary.
+ /// @return Always returns a draw pool.
+ LLDrawPool *getPool(const U32 pool_type, LLViewerImage *tex0 = NULL);
+
+ /// @brief Figures out draw pool type from texture entry. Creates pool if necessary.
+ static LLDrawPool* getPoolFromTE(const LLTextureEntry* te, LLViewerImage* te_image);
+
+ void addPool(LLDrawPool *poolp); // Only to be used by LLDrawPool classes for splitting pools!
+ void removePool( LLDrawPool* poolp );
+
+ void allocDrawable(LLViewerObject *obj);
+
+ void unlinkDrawable(LLDrawable*);
+
+ // Object related methods
+ void markVisible(LLDrawable *drawablep);
+ void doOcclusion();
+ void markNotCulled(LLDrawable* drawablep, LLCamera& camera);
+ void markMoved(LLDrawable *drawablep, BOOL damped_motion = FALSE);
+ void markShift(LLDrawable *drawablep);
+ void markTextured(LLDrawable *drawablep);
+ void markMaterialed(LLDrawable *drawablep);
+ void markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags flag = LLDrawable::REBUILD_ALL, BOOL priority = FALSE);
+ void markRelight(LLDrawable *drawablep, const BOOL now = FALSE);
+
+ //get the object between start and end that's closest to start. Return the point of collision in collision.
+ LLViewerObject* pickObject(const LLVector3 &start, const LLVector3 &end, LLVector3 &collision);
+
+ void dirtyPoolObjectTextures(const LLViewerImage *texture); // Something about this texture has changed. Dirty it.
+
+ void resetDrawOrders();
+
+ U32 addObject(LLViewerObject *obj);
+
+ BOOL usingAGP() const;
+ void setUseAGP(const BOOL use_agp); // Attempt to use AGP;
+
+ void enableShadows(const BOOL enable_shadows);
+
+// void setLocalLighting(const BOOL local_lighting);
+// BOOL isLocalLightingEnabled() const;
+ S32 setLightingDetail(S32 level);
+ S32 getLightingDetail() const { return mLightingDetail; }
+ S32 getMaxLightingDetail() const;
+
+ void setUseVertexShaders(BOOL use_shaders);
+ BOOL getUseVertexShaders() const { return mVertexShadersEnabled; }
+ BOOL canUseVertexShaders();
+ BOOL setVertexShaderLevel(S32 type, S32 level);
+ S32 getVertexShaderLevel(S32 type) const { return mVertexShaderLevel[type]; }
+ S32 getMaxVertexShaderLevel(S32 type) const { return mMaxVertexShaderLevel[type]; }
+
+ void setShaders();
+
+ void dumpObjectLog(GLhandleARB ret, BOOL warns = TRUE);
+ BOOL linkProgramObject(GLhandleARB obj, BOOL suppress_errors = FALSE);
+ BOOL validateProgramObject(GLhandleARB obj);
+ GLhandleARB loadShader(const LLString& filename, S32 cls, GLenum type);
+
+ void setUseOcclusionCulling(BOOL b) { mUseOcclusionCulling = b; }
+ BOOL getUseOcclusionCulling() const { return mUseOcclusionCulling; }
+
+ BOOL initAGP();
+ void cleanupAGP();
+ LLAGPMemBlock *allocAGPFromPool(const S32 bytes, const U32 target); // Static flag is ignored for now.
+ void flushAGPMemory(); // Clear all AGP memory blocks (to pack & reduce fragmentation)
+
+ // phases
+ void resetFrameStats();
+
+ void updateMoveDampedAsync(LLDrawable* drawablep);
+ void updateMoveNormalAsync(LLDrawable* drawablep);
+ void updateMove();
+ void updateCull();
+ void updateGeom(F32 max_dtime);
+
+ void stateSort();
+
+ void renderGeom();
+ void renderHighlights();
+ void renderDebug();
+
+ void renderForSelect();
+ void renderFaceForUVSelect(LLFace* facep);
+ void rebuildPools(); // Rebuild pools
+
+ // bindAGP and unbindAGP are used to bind AGP memory.
+ // AGP should never be bound if you're writing/copying data to AGP.
+ // bindAGP will do the correct thing if AGP rendering has been disabled globally.
+ void bindAGP();
+ void unbindAGP();
+ inline BOOL isAGPBound() const { return mAGPBound; }
+
+ void setupVisibility();
+ void computeVisibility();
+
+ LLViewerObject *nearestObjectAt(F32 yaw, F32 pitch); // CCW yaw from +X = 0 radians, pitch down from +Y = 0 radians, NULL if no object
+
+ void displayPools();
+ void displayAGP();
+ void displayMap();
+ void displaySSBB();
+ void displayQueues();
+ void printPools();
+
+ void findReferences(LLDrawable *drawablep); // Find the lists which have references to this object
+ BOOL verify(); // Verify that all data in the pipeline is "correct"
+
+ // just the AGP part
+ S32 getAGPMemUsage();
+
+ // all pools
+ S32 getMemUsage(const BOOL print = FALSE);
+
+ S32 getVisibleCount() const { return mVisibleList.size(); }
+ S32 getLightCount() const { return mLights.size(); }
+
+ void calcNearbyLights();
+ void setupHWLights(LLDrawPool* pool);
+ void setupAvatarLights(BOOL for_edit = FALSE);
+ void enableLights(U32 mask, F32 shadow_factor);
+ void enableLightsStatic(F32 shadow_factor);
+ void enableLightsDynamic(F32 shadow_factor);
+ void enableLightsAvatar(F32 shadow_factor);
+ void enableLightsAvatarEdit(const LLColor4& color);
+ void enableLightsFullbright(const LLColor4& color);
+ void disableLights();
+ void setAmbient(const LLColor4& ambient);
+
+ void shiftObjects(const LLVector3 &offset);
+
+ void setLight(LLDrawable *drawablep, BOOL is_light);
+ void setActive(LLDrawable *drawablep, BOOL active);
+
+ BOOL hasRenderType(const U32 type) const { return (type && (mRenderTypeMask & (1<<type))) ? TRUE : FALSE; }
+ BOOL hasRenderDebugFeatureMask(const U32 mask) const { return (mRenderDebugFeatureMask & mask) ? TRUE : FALSE; }
+ BOOL hasRenderFeatureMask(const U32 mask) const { return (mRenderFeatureMask & mask) ? TRUE : FALSE; }
+ BOOL hasRenderDebugMask(const U32 mask) const { return (mRenderDebugMask & mask) ? TRUE : FALSE; }
+
+ // Vertex buffer stuff?
+ U8* bufferGetScratchMemory(void);
+ void bufferWaitFence(void);
+ void bufferSendFence(void);
+ void bufferRotate(void);
+
+ // For UI control of render features
+ static void toggleRenderType(void* data);
+ static void toggleRenderDebug(void* data);
+ static void toggleRenderDebugFeature(void* data);
+ static BOOL toggleRenderTypeControl(void* data);
+ static BOOL toggleRenderTypeControlNegated(void* data);
+ static BOOL toggleRenderDebugControl(void* data);
+ static BOOL toggleRenderDebugFeatureControl(void* data);
+
+ static void toggleRenderParticleBeacons(void* data);
+ static BOOL getRenderParticleBeacons(void* data);
+
+ static void toggleRenderSoundBeacons(void* data);
+ static BOOL getRenderSoundBeacons(void* data);
+
+ static void toggleRenderPhysicalBeacons(void* data);
+ static BOOL getRenderPhysicalBeacons(void* data);
+
+ static void toggleRenderScriptedBeacons(void* data);
+ static BOOL getRenderScriptedBeacons(void* data);
+
+private:
+ void initShaders(BOOL force);
+ void unloadShaders();
+ BOOL loadShaders();
+ BOOL loadShadersLighting();
+ BOOL loadShadersObject();
+ BOOL loadShadersAvatar();
+ BOOL loadShadersEnvironment();
+ BOOL loadShadersInterface();
+ void saveVertexShaderLevel(S32 type, S32 level, S32 max);
+ void addToQuickLookup( LLDrawPool* new_poolp );
+ void removeFromQuickLookup( LLDrawPool* poolp );
+ BOOL updateDrawableGeom(LLDrawable* drawable, BOOL priority);
+
+public:
+ enum {GPU_CLASS_MAX = 3 };
+ enum EShaderClass
+ {
+ SHADER_LIGHTING,
+ SHADER_OBJECT,
+ SHADER_AVATAR,
+ SHADER_ENVIRONMENT,
+ SHADER_INTERFACE,
+ SHADER_COUNT
+ };
+ enum LLRenderTypeMask
+ {
+ // Following are pool types (some are also object types)
+ RENDER_TYPE_SKY = LLDrawPool::POOL_SKY,
+ RENDER_TYPE_STARS = LLDrawPool::POOL_STARS,
+ RENDER_TYPE_GROUND = LLDrawPool::POOL_GROUND,
+ RENDER_TYPE_TERRAIN = LLDrawPool::POOL_TERRAIN,
+ RENDER_TYPE_SIMPLE = LLDrawPool::POOL_SIMPLE,
+ RENDER_TYPE_MEDIA = LLDrawPool::POOL_MEDIA,
+ RENDER_TYPE_BUMP = LLDrawPool::POOL_BUMP,
+ RENDER_TYPE_AVATAR = LLDrawPool::POOL_AVATAR,
+ RENDER_TYPE_TREE = LLDrawPool::POOL_TREE,
+ RENDER_TYPE_TREE_NEW = LLDrawPool::POOL_TREE_NEW,
+ RENDER_TYPE_WATER = LLDrawPool::POOL_WATER,
+ RENDER_TYPE_CLOUDS = LLDrawPool::POOL_CLOUDS,
+ RENDER_TYPE_ALPHA = LLDrawPool::POOL_ALPHA,
+ RENDER_TYPE_HUD = LLDrawPool::POOL_HUD,
+
+ // Following are object types (only used in drawable mRenderType)
+ RENDER_TYPE_VOLUME,
+ RENDER_TYPE_GRASS,
+ RENDER_TYPE_PARTICLES,
+ };
+
+ enum LLRenderDebugFeatureMask
+ {
+ RENDER_DEBUG_FEATURE_UI = 0x0001,
+ RENDER_DEBUG_FEATURE_SELECTED = 0x0002,
+ RENDER_DEBUG_FEATURE_HIGHLIGHTED = 0x0004,
+ RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES = 0x0008,
+// RENDER_DEBUG_FEATURE_HW_LIGHTING = 0x0010,
+ RENDER_DEBUG_FEATURE_FLEXIBLE = 0x0010,
+ RENDER_DEBUG_FEATURE_FOG = 0x0020,
+ RENDER_DEBUG_FEATURE_PALETTE = 0x0040,
+ RENDER_DEBUG_FEATURE_FR_INFO = 0x0080,
+ RENDER_DEBUG_FEATURE_CHAIN_FACES = 0x0100
+ };
+
+ enum LLRenderFeatureMask
+ {
+ RENDER_FEATURE_AGP = 0x01,
+// RENDER_FEATURE_LOCAL_LIGHTING = 0x02,
+ RENDER_FEATURE_OBJECT_BUMP = 0x04,
+ RENDER_FEATURE_AVATAR_BUMP = 0x08,
+// RENDER_FEATURE_SHADOWS = 0x10,
+ RENDER_FEATURE_RIPPLE_WATER = 0X20
+ };
+
+ enum LLRenderDebugMask
+ {
+ RENDER_DEBUG_LIGHT_TRACE = 0x0001,
+ RENDER_DEBUG_POOLS = 0x0002,
+ RENDER_DEBUG_MAP = 0x0004,
+ RENDER_DEBUG_AGP_MEM = 0x0008,
+ RENDER_DEBUG_QUEUES = 0x0010,
+ RENDER_DEBUG_COMPOSITION = 0x0020,
+ RENDER_DEBUG_SSBB = 0x0040,
+ RENDER_DEBUG_VERIFY = 0x0080,
+ RENDER_DEBUG_SHADOW_MAP = 0x0100,
+ RENDER_DEBUG_BBOXES = 0x0200,
+ RENDER_DEBUG_OCTREE = 0x0400,
+ RENDER_DEBUG_FACE_CHAINS = 0x0800,
+ RENDER_DEBUG_OCCLUSION = 0x1000,
+ RENDER_DEBUG_POINTS = 0x2000,
+ RENDER_DEBUG_TEXTURE_PRIORITY = 0x4000,
+ };
+
+ LLPointer<LLViewerImage> mAlphaSizzleImagep;
+
+ LLSpatialPartition *mObjectPartition;
+
+ BOOL mBackfaceCull;
+ S32 mTrianglesDrawn;
+ LLStat mTrianglesDrawnStat;
+ LLStat mCompilesStat;
+ S32 mVerticesRelit;
+ LLStat mVerticesRelitStat;
+
+ S32 mLightingChanges;
+ LLStat mLightingChangesStat;
+ S32 mGeometryChanges;
+ LLStat mGeometryChangesStat;
+ LLStat mMoveChangesStat;
+
+ S32 mNumVisibleFaces;
+ LLStat mNumVisibleFacesStat;
+ LLStat mNumVisibleDrawablesStat;
+
+ static S32 sAGPMaxPoolSize;
+ static S32 sCompiles;
+
+ BOOL mUseVBO; // Use ARB vertex buffer objects, if available
+
+ class LLScatterShader
+ {
+ public:
+ static void init(GLhandleARB shader, int map_stage);
+ };
+
+ //utility shader objects (not shader programs)
+ GLhandleARB mLightVertex;
+ GLhandleARB mLightFragment;
+ GLhandleARB mScatterVertex;
+ GLhandleARB mScatterFragment;
+
+ //global (reserved slot) shader parameters
+ static const char* sReservedAttribs[];
+ static U32 sReservedAttribCount;
+
+ typedef enum
+ {
+ GLSL_MATERIAL_COLOR = 0,
+ GLSL_SPECULAR_COLOR,
+ GLSL_BINORMAL,
+ GLSL_END_RESERVED_ATTRIBS
+ } eGLSLReservedAttribs;
+
+ static const char* sReservedUniforms[];
+ static U32 sReservedUniformCount;
+
+ typedef enum
+ {
+ GLSL_DIFFUSE_MAP = 0,
+ GLSL_SPECULAR_MAP,
+ GLSL_BUMP_MAP,
+ GLSL_ENVIRONMENT_MAP,
+ GLSL_SCATTER_MAP,
+ GLSL_END_RESERVED_UNIFORMS
+ } eGLSLReservedUniforms;
+
+ //object shaders
+ LLGLSLShader mObjectSimpleProgram;
+ LLGLSLShader mObjectAlphaProgram;
+ LLGLSLShader mObjectBumpProgram;
+
+ //water parameters
+ static const char* sWaterUniforms[];
+ static U32 sWaterUniformCount;
+
+ typedef enum
+ {
+ GLSL_WATER_SCREENTEX = GLSL_END_RESERVED_UNIFORMS,
+ GLSL_WATER_EYEVEC,
+ GLSL_WATER_TIME,
+ GLSL_WATER_WAVE_DIR1,
+ GLSL_WATER_WAVE_DIR2,
+ GLSL_WATER_LIGHT_DIR,
+ GLSL_WATER_SPECULAR,
+ GLSL_WATER_SPECULAR_EXP,
+ GLSL_WATER_FBSCALE,
+ GLSL_WATER_REFSCALE
+ } eWaterUniforms;
+
+
+ //terrain parameters
+ static const char* sTerrainUniforms[];
+ static U32 sTerrainUniformCount;
+
+ typedef enum
+ {
+ GLSL_TERRAIN_DETAIL0 = GLSL_END_RESERVED_UNIFORMS,
+ GLSL_TERRAIN_DETAIL1,
+ GLSL_TERRAIN_ALPHARAMP
+ } eTerrainUniforms;
+
+ //environment shaders
+ LLGLSLShader mTerrainProgram;
+ LLGLSLShader mGroundProgram;
+ LLGLSLShader mWaterProgram;
+
+ //interface shaders
+ LLGLSLShader mHighlightProgram;
+
+ //avatar shader parameter tables
+ static const char* sAvatarAttribs[];
+ static U32 sAvatarAttribCount;
+
+ typedef enum
+ {
+ GLSL_AVATAR_WEIGHT = GLSL_END_RESERVED_ATTRIBS,
+ GLSL_AVATAR_CLOTHING,
+ GLSL_AVATAR_WIND,
+ GLSL_AVATAR_SINWAVE,
+ GLSL_AVATAR_GRAVITY
+ } eAvatarAttribs;
+
+ static const char* sAvatarUniforms[];
+ static U32 sAvatarUniformCount;
+
+ typedef enum
+ {
+ GLSL_AVATAR_MATRIX = GLSL_END_RESERVED_UNIFORMS
+ } eAvatarUniforms;
+
+ //avatar skinning utility shader object
+ GLhandleARB mAvatarSkinVertex;
+
+ //avatar shader handles
+ LLGLSLShader mAvatarProgram;
+ LLGLSLShader mAvatarEyeballProgram;
+ LLGLSLShader mAvatarPickProgram;
+
+ //current avatar shader parameter pointer
+ GLint mAvatarMatrixParam;
+ GLint mMaterialIndex;
+ GLint mSpecularIndex;
+
+ LLColor4 mSunDiffuse;
+ LLVector3 mSunDir;
+
+protected:
+ class SelectedFaceInfo
+ {
+ public:
+ LLFace *mFacep;
+ S32 mTE;
+ };
+
+ BOOL mVertexShadersEnabled;
+ S32 mVertexShadersLoaded; // 0 = no, 1 = yes, -1 = failed
+ S32 mVertexShaderLevel[SHADER_COUNT];
+ S32 mMaxVertexShaderLevel[SHADER_COUNT];
+
+ U32 mRenderTypeMask;
+ U32 mRenderFeatureMask;
+ U32 mRenderDebugFeatureMask;
+ U32 mRenderDebugMask;
+
+ /////////////////////////////////////////////
+ //
+ //
+ LLDrawable::drawable_vector_t mVisibleList;
+ LLDrawable::drawable_set_t mMovedList;
+
+ LLDrawable::drawable_vector_t mShiftList;
+
+ /////////////////////////////////////////////
+ //
+ //
+ struct Light
+ {
+ Light(LLDrawable* ptr, F32 d, F32 f = 0.0f)
+ : drawable(ptr),
+ dist(d),
+ fade(f)
+ {}
+ LLPointer<LLDrawable> drawable;
+ F32 dist;
+ F32 fade;
+ struct compare
+ {
+ bool operator()(const Light& a, const Light& b) const
+ {
+ if ( a.dist < b.dist )
+ return true;
+ else if ( a.dist > b.dist )
+ return false;
+ else
+ return a.drawable < b.drawable;
+ }
+ };
+ };
+ typedef std::set< Light, Light::compare > light_set_t;
+
+ LLDrawable::drawable_set_t mLights;
+ light_set_t mNearbyLights; // lights near camera
+ LLColor4 mHWLightColors[8];
+
+ /////////////////////////////////////////////
+ //
+ // Different queues of drawables being processed.
+ //
+ LLDrawable::drawable_set_t mBuildQ1; // priority
+ LLDrawable::drawable_list_t mBuildQ2; // non-priority
+
+ LLDrawable::drawable_set_t mActiveQ;
+
+ LLDrawable::drawable_set_t mRetexturedList;
+ LLDrawable::drawable_set_t mRematerialedList;
+
+ //////////////////////////////////////////////////
+ //
+ // Draw pools are responsible for storing all rendered data,
+ // and performing the actual rendering of objects.
+ //
+ struct compare_pools
+ {
+ bool operator()(const LLDrawPool* a, const LLDrawPool* b) const
+ {
+ if (!a)
+ return true;
+ else if (!b)
+ return false;
+ else
+ {
+ S32 atype = a->getType();
+ S32 btype = b->getType();
+ if (atype < btype)
+ return true;
+ else if (atype > btype)
+ return false;
+ else
+ return a->getId() < b->getId();
+ }
+ }
+ };
+ typedef std::set<LLDrawPool*, compare_pools > pool_set_t;
+ pool_set_t mPools;
+ LLDrawPool* mLastRebuildPool;
+
+ // For quick-lookups into mPools (mapped by texture pointer)
+ std::map<uintptr_t, LLDrawPool*> mSimplePools;
+ std::map<uintptr_t, LLDrawPool*> mTerrainPools;
+ std::map<uintptr_t, LLDrawPool*> mTreePools;
+ std::map<uintptr_t, LLDrawPool*> mTreeNewPools;
+ std::map<uintptr_t, LLDrawPool*> mBumpPools;
+ std::map<uintptr_t, LLDrawPool*> mMediaPools;
+ LLDrawPool* mAlphaPool;
+ LLDrawPool* mSkyPool;
+ LLDrawPool* mStarsPool;
+ LLDrawPool* mCloudsPool;
+ LLDrawPool* mTerrainPool;
+ LLDrawPool* mWaterPool;
+ LLDrawPool* mGroundPool;
+ LLDrawPool* mHUDPool;
+ // Note: no need to keep an quick-lookup to avatar pools, since there's only one per avatar
+
+
+ LLDynamicArray<LLFace*> mHighlightFaces; // highlight faces on physical objects
+ LLDynamicArray<SelectedFaceInfo> mSelectedFaces;
+
+ LLPointer<LLViewerImage> mFaceSelectImagep;
+ LLPointer<LLViewerImage> mBloomImagep;
+ LLPointer<LLViewerImage> mBloomImage2p;
+
+ BOOL mAGPBound;
+ LLAGPMemPool *mAGPMemPool;
+ U32 mGlobalFence;
+
+ // Round-robin AGP buffers for use by the software skinner
+ enum
+ {
+ kMaxBufferCount = 4
+ };
+
+ S32 mBufferIndex;
+ S32 mBufferCount;
+ LLAGPArray<U8> *mBufferMemory[kMaxBufferCount];
+ U32 mBufferFence[kMaxBufferCount];
+ BOOL mUseOcclusionCulling; // object-object occlusion culling
+ U32 mLightMask;
+ U32 mLightMovingMask;
+ S32 mLightingDetail;
+ F32 mSunShadowFactor;
+
+ static BOOL sRenderPhysicalBeacons;
+ static BOOL sRenderScriptedBeacons;
+ static BOOL sRenderParticleBeacons;
+ static BOOL sRenderSoundBeacons;
+};
+
+void render_bbox(const LLVector3 &min, const LLVector3 &max);
+
+extern LLPipeline gPipeline;
+extern BOOL gRenderForSelect;
+extern F32 gPickAlphaThreshold;
+extern F32 gPickAlphaTargetThreshold;
+extern BOOL gUsePickAlpha;
+
+#endif
diff --git a/indra/newview/res-sdl/arrow.BMP b/indra/newview/res-sdl/arrow.BMP
new file mode 100644
index 0000000000..a8f6da64b5
--- /dev/null
+++ b/indra/newview/res-sdl/arrow.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowcop.BMP b/indra/newview/res-sdl/arrowcop.BMP
new file mode 100644
index 0000000000..1a26a0df34
--- /dev/null
+++ b/indra/newview/res-sdl/arrowcop.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowcopmulti.BMP b/indra/newview/res-sdl/arrowcopmulti.BMP
new file mode 100644
index 0000000000..48f153cef6
--- /dev/null
+++ b/indra/newview/res-sdl/arrowcopmulti.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/arrowdrag.BMP b/indra/newview/res-sdl/arrowdrag.BMP
new file mode 100644
index 0000000000..cd868eec20
--- /dev/null
+++ b/indra/newview/res-sdl/arrowdrag.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/circleandline.BMP b/indra/newview/res-sdl/circleandline.BMP
new file mode 100644
index 0000000000..284ae8b7d5
--- /dev/null
+++ b/indra/newview/res-sdl/circleandline.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/cross.BMP b/indra/newview/res-sdl/cross.BMP
new file mode 100644
index 0000000000..0b4672d4d6
--- /dev/null
+++ b/indra/newview/res-sdl/cross.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/hand.BMP b/indra/newview/res-sdl/hand.BMP
new file mode 100644
index 0000000000..2a092fbb7f
--- /dev/null
+++ b/indra/newview/res-sdl/hand.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/ibeam.BMP b/indra/newview/res-sdl/ibeam.BMP
new file mode 100644
index 0000000000..820904a228
--- /dev/null
+++ b/indra/newview/res-sdl/ibeam.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/ll_icon.BMP b/indra/newview/res-sdl/ll_icon.BMP
new file mode 100644
index 0000000000..4a44aafbfa
--- /dev/null
+++ b/indra/newview/res-sdl/ll_icon.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrow.BMP b/indra/newview/res-sdl/llarrow.BMP
new file mode 100644
index 0000000000..a8f6da64b5
--- /dev/null
+++ b/indra/newview/res-sdl/llarrow.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowdrag.BMP b/indra/newview/res-sdl/llarrowdrag.BMP
new file mode 100644
index 0000000000..cd868eec20
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowdrag.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowdragmulti.BMP b/indra/newview/res-sdl/llarrowdragmulti.BMP
new file mode 100644
index 0000000000..fb528bc92d
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowdragmulti.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llarrowlocked.BMP b/indra/newview/res-sdl/llarrowlocked.BMP
new file mode 100644
index 0000000000..0aaa441ab1
--- /dev/null
+++ b/indra/newview/res-sdl/llarrowlocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llgrablocked.BMP b/indra/newview/res-sdl/llgrablocked.BMP
new file mode 100644
index 0000000000..847439670f
--- /dev/null
+++ b/indra/newview/res-sdl/llgrablocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llno.BMP b/indra/newview/res-sdl/llno.BMP
new file mode 100644
index 0000000000..284ae8b7d5
--- /dev/null
+++ b/indra/newview/res-sdl/llno.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/llnolocked.BMP b/indra/newview/res-sdl/llnolocked.BMP
new file mode 100644
index 0000000000..61f0170cb3
--- /dev/null
+++ b/indra/newview/res-sdl/llnolocked.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolcamera.BMP b/indra/newview/res-sdl/lltoolcamera.BMP
new file mode 100644
index 0000000000..c961d7a49c
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolcamera.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolcreate.BMP b/indra/newview/res-sdl/lltoolcreate.BMP
new file mode 100644
index 0000000000..08a4a9322d
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolcreate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolfocus.BMP b/indra/newview/res-sdl/lltoolfocus.BMP
new file mode 100644
index 0000000000..afb90a95e3
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolfocus.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolgrab.BMP b/indra/newview/res-sdl/lltoolgrab.BMP
new file mode 100644
index 0000000000..f2ac68bf3c
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolgrab.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolland.BMP b/indra/newview/res-sdl/lltoolland.BMP
new file mode 100644
index 0000000000..64e6365625
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolland.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolpan.BMP b/indra/newview/res-sdl/lltoolpan.BMP
new file mode 100644
index 0000000000..ffbef21ec7
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolpan.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolpipette.BMP b/indra/newview/res-sdl/lltoolpipette.BMP
new file mode 100644
index 0000000000..2d27118289
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolpipette.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolrotate.BMP b/indra/newview/res-sdl/lltoolrotate.BMP
new file mode 100644
index 0000000000..dd84673018
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolrotate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolscale.BMP b/indra/newview/res-sdl/lltoolscale.BMP
new file mode 100644
index 0000000000..882515e5e3
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolscale.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltooltranslate.BMP b/indra/newview/res-sdl/lltooltranslate.BMP
new file mode 100644
index 0000000000..d084f6a026
--- /dev/null
+++ b/indra/newview/res-sdl/lltooltranslate.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolzoomin.BMP b/indra/newview/res-sdl/lltoolzoomin.BMP
new file mode 100644
index 0000000000..e4e46cc702
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolzoomin.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/lltoolzoomout.BMP b/indra/newview/res-sdl/lltoolzoomout.BMP
new file mode 100644
index 0000000000..7f958383ab
--- /dev/null
+++ b/indra/newview/res-sdl/lltoolzoomout.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizenesw.BMP b/indra/newview/res-sdl/sizenesw.BMP
new file mode 100644
index 0000000000..559579f40e
--- /dev/null
+++ b/indra/newview/res-sdl/sizenesw.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizens.BMP b/indra/newview/res-sdl/sizens.BMP
new file mode 100644
index 0000000000..8373077dff
--- /dev/null
+++ b/indra/newview/res-sdl/sizens.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizenwse.BMP b/indra/newview/res-sdl/sizenwse.BMP
new file mode 100644
index 0000000000..6d069fa765
--- /dev/null
+++ b/indra/newview/res-sdl/sizenwse.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/sizewe.BMP b/indra/newview/res-sdl/sizewe.BMP
new file mode 100644
index 0000000000..878df453a4
--- /dev/null
+++ b/indra/newview/res-sdl/sizewe.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolbuy.BMP b/indra/newview/res-sdl/toolbuy.BMP
new file mode 100644
index 0000000000..a00bdb2c58
--- /dev/null
+++ b/indra/newview/res-sdl/toolbuy.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolopen.BMP b/indra/newview/res-sdl/toolopen.BMP
new file mode 100644
index 0000000000..93c7e44b28
--- /dev/null
+++ b/indra/newview/res-sdl/toolopen.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject.BMP b/indra/newview/res-sdl/toolpickobject.BMP
new file mode 100644
index 0000000000..25469fc3a8
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject2.BMP b/indra/newview/res-sdl/toolpickobject2.BMP
new file mode 100644
index 0000000000..09df69e675
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject2.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolpickobject3.BMP b/indra/newview/res-sdl/toolpickobject3.BMP
new file mode 100644
index 0000000000..fc28698050
--- /dev/null
+++ b/indra/newview/res-sdl/toolpickobject3.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/toolsit.BMP b/indra/newview/res-sdl/toolsit.BMP
new file mode 100644
index 0000000000..ca911ece71
--- /dev/null
+++ b/indra/newview/res-sdl/toolsit.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/wait.BMP b/indra/newview/res-sdl/wait.BMP
new file mode 100644
index 0000000000..26dec59afe
--- /dev/null
+++ b/indra/newview/res-sdl/wait.BMP
Binary files differ
diff --git a/indra/newview/res-sdl/working.BMP b/indra/newview/res-sdl/working.BMP
new file mode 100644
index 0000000000..26dec59afe
--- /dev/null
+++ b/indra/newview/res-sdl/working.BMP
Binary files differ
diff --git a/indra/newview/res/arrow.cur b/indra/newview/res/arrow.cur
new file mode 100644
index 0000000000..d11b68867f
--- /dev/null
+++ b/indra/newview/res/arrow.cur
Binary files differ
diff --git a/indra/newview/res/arrowcop.cur b/indra/newview/res/arrowcop.cur
new file mode 100644
index 0000000000..8b5902c13d
--- /dev/null
+++ b/indra/newview/res/arrowcop.cur
Binary files differ
diff --git a/indra/newview/res/arrowcopmulti.cur b/indra/newview/res/arrowcopmulti.cur
new file mode 100644
index 0000000000..2620522616
--- /dev/null
+++ b/indra/newview/res/arrowcopmulti.cur
Binary files differ
diff --git a/indra/newview/res/arrowdrag.cur b/indra/newview/res/arrowdrag.cur
new file mode 100644
index 0000000000..77b2d1a0fb
--- /dev/null
+++ b/indra/newview/res/arrowdrag.cur
Binary files differ
diff --git a/indra/newview/res/bitmap2.bmp b/indra/newview/res/bitmap2.bmp
new file mode 100644
index 0000000000..770b07558c
--- /dev/null
+++ b/indra/newview/res/bitmap2.bmp
Binary files differ
diff --git a/indra/newview/res/circleandline.cur b/indra/newview/res/circleandline.cur
new file mode 100644
index 0000000000..00050f5501
--- /dev/null
+++ b/indra/newview/res/circleandline.cur
Binary files differ
diff --git a/indra/newview/res/install_icon.BMP b/indra/newview/res/install_icon.BMP
new file mode 100644
index 0000000000..09df573870
--- /dev/null
+++ b/indra/newview/res/install_icon.BMP
Binary files differ
diff --git a/indra/newview/res/ll_icon.BMP b/indra/newview/res/ll_icon.BMP
new file mode 100644
index 0000000000..3a9964cd95
--- /dev/null
+++ b/indra/newview/res/ll_icon.BMP
Binary files differ
diff --git a/indra/newview/res/ll_icon.ico b/indra/newview/res/ll_icon.ico
new file mode 100644
index 0000000000..c35a3fa3a3
--- /dev/null
+++ b/indra/newview/res/ll_icon.ico
Binary files differ
diff --git a/indra/newview/res/llarrow.cur b/indra/newview/res/llarrow.cur
new file mode 100644
index 0000000000..8a922d7f3f
--- /dev/null
+++ b/indra/newview/res/llarrow.cur
Binary files differ
diff --git a/indra/newview/res/llarrowdrag.cur b/indra/newview/res/llarrowdrag.cur
new file mode 100644
index 0000000000..77b2d1a0fb
--- /dev/null
+++ b/indra/newview/res/llarrowdrag.cur
Binary files differ
diff --git a/indra/newview/res/llarrowdragmulti.cur b/indra/newview/res/llarrowdragmulti.cur
new file mode 100644
index 0000000000..f0853b899c
--- /dev/null
+++ b/indra/newview/res/llarrowdragmulti.cur
Binary files differ
diff --git a/indra/newview/res/llarrowlocked.cur b/indra/newview/res/llarrowlocked.cur
new file mode 100644
index 0000000000..5b81f147ef
--- /dev/null
+++ b/indra/newview/res/llarrowlocked.cur
Binary files differ
diff --git a/indra/newview/res/llgrablocked.cur b/indra/newview/res/llgrablocked.cur
new file mode 100644
index 0000000000..73dea154e6
--- /dev/null
+++ b/indra/newview/res/llgrablocked.cur
Binary files differ
diff --git a/indra/newview/res/llno.cur b/indra/newview/res/llno.cur
new file mode 100644
index 0000000000..00050f5501
--- /dev/null
+++ b/indra/newview/res/llno.cur
Binary files differ
diff --git a/indra/newview/res/llnolocked.cur b/indra/newview/res/llnolocked.cur
new file mode 100644
index 0000000000..dfa4a05509
--- /dev/null
+++ b/indra/newview/res/llnolocked.cur
Binary files differ
diff --git a/indra/newview/res/lltoolcamera.cur b/indra/newview/res/lltoolcamera.cur
new file mode 100644
index 0000000000..e0f19461c7
--- /dev/null
+++ b/indra/newview/res/lltoolcamera.cur
Binary files differ
diff --git a/indra/newview/res/lltoolcreate.cur b/indra/newview/res/lltoolcreate.cur
new file mode 100644
index 0000000000..716f328211
--- /dev/null
+++ b/indra/newview/res/lltoolcreate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolfocus.cur b/indra/newview/res/lltoolfocus.cur
new file mode 100644
index 0000000000..7b38ad3af9
--- /dev/null
+++ b/indra/newview/res/lltoolfocus.cur
Binary files differ
diff --git a/indra/newview/res/lltoolgrab.cur b/indra/newview/res/lltoolgrab.cur
new file mode 100644
index 0000000000..4484001357
--- /dev/null
+++ b/indra/newview/res/lltoolgrab.cur
Binary files differ
diff --git a/indra/newview/res/lltoolland.cur b/indra/newview/res/lltoolland.cur
new file mode 100644
index 0000000000..e22ef6c73d
--- /dev/null
+++ b/indra/newview/res/lltoolland.cur
Binary files differ
diff --git a/indra/newview/res/lltoolpan.cur b/indra/newview/res/lltoolpan.cur
new file mode 100644
index 0000000000..0a348669b6
--- /dev/null
+++ b/indra/newview/res/lltoolpan.cur
Binary files differ
diff --git a/indra/newview/res/lltoolpipette.cur b/indra/newview/res/lltoolpipette.cur
new file mode 100644
index 0000000000..b4780967f9
--- /dev/null
+++ b/indra/newview/res/lltoolpipette.cur
Binary files differ
diff --git a/indra/newview/res/lltoolrotate.cur b/indra/newview/res/lltoolrotate.cur
new file mode 100644
index 0000000000..adbadaee84
--- /dev/null
+++ b/indra/newview/res/lltoolrotate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolscale.cur b/indra/newview/res/lltoolscale.cur
new file mode 100644
index 0000000000..4b6d20bfad
--- /dev/null
+++ b/indra/newview/res/lltoolscale.cur
Binary files differ
diff --git a/indra/newview/res/lltooltranslate.cur b/indra/newview/res/lltooltranslate.cur
new file mode 100644
index 0000000000..2df80de0a6
--- /dev/null
+++ b/indra/newview/res/lltooltranslate.cur
Binary files differ
diff --git a/indra/newview/res/lltoolzoomin.cur b/indra/newview/res/lltoolzoomin.cur
new file mode 100644
index 0000000000..0b5d80101e
--- /dev/null
+++ b/indra/newview/res/lltoolzoomin.cur
Binary files differ
diff --git a/indra/newview/res/lltoolzoomout.cur b/indra/newview/res/lltoolzoomout.cur
new file mode 100644
index 0000000000..b33e68d1a6
--- /dev/null
+++ b/indra/newview/res/lltoolzoomout.cur
Binary files differ
diff --git a/indra/newview/res/loginbackground.bmp b/indra/newview/res/loginbackground.bmp
new file mode 100644
index 0000000000..288a0b0398
--- /dev/null
+++ b/indra/newview/res/loginbackground.bmp
Binary files differ
diff --git a/indra/newview/res/resource.h b/indra/newview/res/resource.h
new file mode 100644
index 0000000000..c7e1552dff
--- /dev/null
+++ b/indra/newview/res/resource.h
@@ -0,0 +1,159 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by newViewRes.rc
+//
+#define IDREMOVE 3
+#define IDI_LL_ICON 103
+#define IDC_GRABHAND 104
+#define IDR_MENU1 112
+#define IDD_DIALOG1 113
+#define IDI_INSTALL_ICON 133
+#define IDC_CURSOR1 134
+#define IDC_CURSOR2 136
+#define IDB_BITMAP1 138
+#define IDB_BITMAP2 140
+#define IDB_BITMAP3 144
+#define IDC_CURSOR3 147
+#define IDB_BITMAP4 148
+#define IDB_BITMAP5 149
+#define IDB_LOGIN 149
+#define IDC_CURSOR4 153
+#define IDC_CURSOR5 154
+#define IDC_RADIO_56 1000
+#define IDC_RADIO_128 1001
+#define IDC_RADIO_256 1002
+#define IDC_RADIO_CUSTOM 1003
+#define IDC_STATIC_LAND 1004
+#define IDC_EDIT_LAND 1005
+#define IDC_STATIC_WATER 1006
+#define IDC_EDIT_WATER 1007
+#define IDC_STATIC_WIND 1008
+#define IDC_EDIT_WIND 1009
+#define IDC_STATIC_CLOUD 1010
+#define IDC_EDIT_CLOUD 1011
+#define IDC_STATIC_TASK 1012
+#define IDC_EDIT_TASK 1013
+#define IDC_STATIC_OTHER 1014
+#define IDC_EDIT_OTHER 1015
+#define IDC_EDIT_USERNAME 1018
+#define IDC_EDIT_PASSWORD 1019
+#define IDC_EDIT_ACCOUNT 1020
+#define IDC_EDIT_GUEST 1021
+#define IDC_EDIT_LASTNAME 1021
+#define IDC_LABEL_TEXTURE 1028
+#define IDC_BUTTON_APPLY 1030
+#define IDAPPLY 1030
+#define IDC_BUTTON_GRAB 1031
+#define IDC_COMBO_TEXTURE 1032
+#define IDC_RADIO_POSITION 1033
+#define IDC_RADIO_SIZE 1034
+#define IDC_RADIO_ROTATION 1035
+#define IDC_STATIC_POS_X 1036
+#define IDC_STATIC_POS_Y 1037
+#define IDC_STATIC_POS_Z 1038
+#define IDC_EDIT_POS_X 1039
+#define IDC_EDIT_POS_Y 1040
+#define IDC_CHECK_AUTO_LOGIN 1040
+#define IDC_EDIT_POS_Z 1041
+#define IDC_STATIC_FILENAME 1041
+#define IDC_STATIC_POS_X2 1042
+#define IDC_EDIT_FILENAME 1042
+#define IDC_STATIC_POS_Y2 1043
+#define IDC_COMBO_FACE 1043
+#define IDC_STATIC_POS_Z2 1044
+#define IDC_RADIO_TEXTURE_LOW 1044
+#define IDC_COMBO_MATERIAL 1044
+#define IDC_EDIT_SIZE_X 1045
+#define IDC_RADIO_TEXTURE_MEDIUM 1045
+#define IDC_EDIT_SIZE_Y 1046
+#define IDC_RADIO_TEXTURE_HIGH 1046
+#define IDC_EDIT_SIZE_Z 1047
+#define IDC_STATIC_POS_X3 1048
+#define IDC_STATIC_POS_Y3 1049
+#define IDC_STATIC_POS_Z3 1050
+#define IDC_EDIT_ROT_X 1051
+#define IDC_EDIT_ROT_Y 1052
+#define IDC_EDIT_ROT_Z 1053
+#define IDC_CHECK_USE_PHYSICS 1065
+#define IDC_LIST1 1068
+#define IDC_LIST_USER 1068
+#define IDC_LIST_LOCATION 1068
+#define IDC_BUTTON_CHANGE 1070
+#define IDC_BUTTON_CLOSE 1071
+#define IDC_CHAT_SAY 1072
+#define IDC_STATIC_VERT_SCALE 1073
+#define IDC_STATIC_SCALE 1073
+#define IDC_EDIT_VERT_SCALE 1074
+#define IDC_STATIC_HORIZ_SCALE 1075
+#define IDC_STATIC_SCALE2 1075
+#define IDC_EDIT_HORIZ_SCALE 1076
+#define IDC_BUTTON_STRETCH 1077
+#define IDC_BUTTON_REPEAT 1078
+#define IDC_EDIT_VERT_OFFSET 1079
+#define IDC_EDIT_HORIZ_OFFSET 1080
+#define IDC_EDIT_ROTATION 1081
+#define IDC_STATIC_KBPS1 1082
+#define IDC_STATIC_KBPS2 1083
+#define IDC_BTN_INTERNAL 1083
+#define IDC_BUTTON_RESET 1083
+#define IDC_STATIC_KBPS3 1084
+#define IDC_BTN_EXTERNAL 1084
+#define IDC_STATIC_KBPS4 1085
+#define IDC_BTN_QUIT 1085
+#define IDC_STATIC_KBPS5 1086
+#define IDC_RESOURCE_TITLE 1086
+#define IDC_STATIC_KBPS6 1087
+#define IDC_RESOURCE_DESC 1087
+#define IDC_EDIT_SAY 1091
+#define IDC_BTN_NORMAL 1092
+#define IDC_BTN_WHISPER 1093
+#define IDC_BTN_SHOUT 1094
+#define IDC_STATIC_ACCOUNT 1102
+#define IDC_STATIC_PASSWORD 1103
+#define IDC_STATIC_GUEST 1104
+#define IDC_STATIC_SERVER 1104
+#define IDC_STATIC_SPACESERVER 1105
+#define IDC_STATIC_LASTNAME 1105
+#define IDC_CHECK_GUEST 1106
+#define IDC_SCRIPT_EDIT 1107
+#define IDC_LSLSYNTAX 1108
+#define IDDONE 1109
+#define IDC_LIST 1110
+#define IDC_EDIT_MESSAGE 1111
+#define IDC_BUTTON_REFRESH 1112
+#define IDC_STATIC_OFFSET 1113
+#define IDC_STATIC_ROTATION 1115
+#define IDC_STATIC_FILEPATH 1115
+#define IDC_STATIC_PX 1118
+#define IDC_STATIC_PY 1119
+#define IDC_STATIC_PZ 1120
+#define IDC_STATIC_SX 1121
+#define IDC_STATIC_SY 1122
+#define IDC_STATIC_SZ 1123
+#define IDC_STATIC_RX 1124
+#define IDC_STATIC_RY 1125
+#define IDC_STATIC_RZ 1126
+#define IDC_BTN_APPLY 1127
+#define IDC_COMBO_SERVER 1131
+#define IDC_STATIC_FIRSTNAME 1132
+#define IDC_EDIT_FIRSTNAME 1133
+#define IDC_REMEMBER_PASSWORD 1134
+#define IDC_REMEMBER_PASSWORD2 1135
+#define IDC_FULL_SCREEN 1135
+#define IDC_RADIO_HOME 1136
+#define IDC_RADIO_LAST 1137
+#define IDC_COMBO1 1138
+#define IDC_COMBO_FARM 1138
+#define ID_TESTMENU_TEST 40001
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 156
+#define _APS_NEXT_COMMAND_VALUE 40002
+#define _APS_NEXT_CONTROL_VALUE 1139
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/indra/newview/res/toolbuy.cur b/indra/newview/res/toolbuy.cur
new file mode 100644
index 0000000000..c61cd76bef
--- /dev/null
+++ b/indra/newview/res/toolbuy.cur
Binary files differ
diff --git a/indra/newview/res/toolopen.cur b/indra/newview/res/toolopen.cur
new file mode 100644
index 0000000000..8408f7c7eb
--- /dev/null
+++ b/indra/newview/res/toolopen.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject.cur b/indra/newview/res/toolpickobject.cur
new file mode 100644
index 0000000000..ba482b5445
--- /dev/null
+++ b/indra/newview/res/toolpickobject.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject2.cur b/indra/newview/res/toolpickobject2.cur
new file mode 100644
index 0000000000..b168f8d691
--- /dev/null
+++ b/indra/newview/res/toolpickobject2.cur
Binary files differ
diff --git a/indra/newview/res/toolpickobject3.cur b/indra/newview/res/toolpickobject3.cur
new file mode 100644
index 0000000000..457c3a6555
--- /dev/null
+++ b/indra/newview/res/toolpickobject3.cur
Binary files differ
diff --git a/indra/newview/res/toolpipette.cur b/indra/newview/res/toolpipette.cur
new file mode 100644
index 0000000000..b4780967f9
--- /dev/null
+++ b/indra/newview/res/toolpipette.cur
Binary files differ
diff --git a/indra/newview/res/toolsit.cur b/indra/newview/res/toolsit.cur
new file mode 100644
index 0000000000..60b0e773e5
--- /dev/null
+++ b/indra/newview/res/toolsit.cur
Binary files differ
diff --git a/indra/newview/res/uninstall_icon.BMP b/indra/newview/res/uninstall_icon.BMP
new file mode 100644
index 0000000000..562b56676a
--- /dev/null
+++ b/indra/newview/res/uninstall_icon.BMP
Binary files differ
diff --git a/indra/newview/secondlife.icns b/indra/newview/secondlife.icns
new file mode 100644
index 0000000000..31ce7b3250
--- /dev/null
+++ b/indra/newview/secondlife.icns
Binary files differ
diff --git a/indra/newview/skins/paths.xml b/indra/newview/skins/paths.xml
new file mode 100644
index 0000000000..fb6f4a133d
--- /dev/null
+++ b/indra/newview/skins/paths.xml
@@ -0,0 +1,4 @@
+<paths>
+ <directory>/skins/xui/en-us/</directory>
+ <directory>/skins/xui/[Language]/</directory>
+</paths> \ No newline at end of file
diff --git a/indra/test/io.cpp b/indra/test/io.cpp
new file mode 100644
index 0000000000..4695594a90
--- /dev/null
+++ b/indra/test/io.cpp
@@ -0,0 +1,1368 @@
+/**
+ * @file io.cpp
+ * @author Phoenix
+ * @date 2005-10-02
+ * @brief Tests for io classes and helpers
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include <iterator>
+
+#include <apr-1/apr_pools.h>
+
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "lliosocket.h"
+#include "llioutil.h"
+#include "llmemorystream.h"
+#include "llpipeutil.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdrpcclient.h"
+#include "llsdrpcserver.h"
+#include "llsdserialize.h"
+#include "lluuid.h"
+#include "llinstantmessage.h"
+
+namespace tut
+{
+ struct buffer_data
+ {
+ LLBufferArray mBuffer;
+ };
+ typedef test_group<buffer_data> buffer_test;
+ typedef buffer_test::object buffer_object;
+ tut::buffer_test tba("buffer_array");
+
+ template<> template<>
+ void buffer_object::test<1>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("total append size", count, str_len);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* first = (*it).data();
+ count = mBuffer.countAfter(ch.in(), first);
+ ensure_equals("offset append size", count, str_len - 1);
+ }
+
+ template<> template<>
+ void buffer_object::test<2>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("total append size", count, 2 * str_len);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* first = (*it).data();
+ count = mBuffer.countAfter(ch.in(), first);
+ ensure_equals("offset append size", count, (2 * str_len) - 1);
+ }
+
+ template<> template<>
+ void buffer_object::test<3>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected(ONE);
+ expected.append(TWO);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)ONE, 3);
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ char buffer[255];
+ S32 len = 6;
+ mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len);
+ ensure_equals(len, 6);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<4>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected(ONE);
+ expected.append(TWO);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ mBuffer.prepend(ch.in(), (U8*)ONE, 3);
+ char buffer[255];
+ S32 len = 6;
+ mBuffer.readAfter(ch.in(), NULL, (U8*)buffer, len);
+ ensure_equals(len, 6);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<5>()
+ {
+ const char ONE[] = "one";
+ const char TWO[] = "two";
+ std::string expected("netwo");
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)TWO, 3);
+ mBuffer.prepend(ch.in(), (U8*)ONE, 3);
+ char buffer[255];
+ S32 len = 5;
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ U8* addr = (*it).data();
+ mBuffer.readAfter(ch.in(), addr, (U8*)buffer, len);
+ ensure_equals(len, 5);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("read", actual, expected);
+ }
+
+ template<> template<>
+ void buffer_object::test<6>()
+ {
+ std::string request("The early bird catches the worm.");
+ std::string response("If you're a worm, sleep late.");
+ std::ostringstream expected;
+ expected << "ContentLength: " << response.length() << "\r\n\r\n"
+ << response;
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)request.c_str(), request.length());
+ mBuffer.append(ch.out(), (U8*)response.c_str(), response.length());
+ S32 count = mBuffer.countAfter(ch.out(), NULL);
+ std::ostringstream header;
+ header << "ContentLength: " << count << "\r\n\r\n";
+ std::string head(header.str());
+ mBuffer.prepend(ch.out(), (U8*)head.c_str(), head.length());
+ char buffer[1024];
+ S32 len = response.size() + head.length();
+ ensure_equals("same length", len, (S32)expected.str().length());
+ mBuffer.readAfter(ch.out(), NULL, (U8*)buffer, len);
+ buffer[len] = '\0';
+ std::string actual(buffer);
+ ensure_equals("threaded writes", actual, expected.str());
+ }
+
+ template<> template<>
+ void buffer_object::test<7>()
+ {
+ const S32 LINE_COUNT = 3;
+ std::string lines[LINE_COUNT] =
+ {
+ std::string("GET /index.htm HTTP/1.0\r\n"),
+ std::string("User-Agent: Wget/1.9.1\r\n"),
+ std::string("Host: localhost:8008\r\n")
+ };
+ std::string text;
+ S32 i;
+ for(i = 0; i < LINE_COUNT; ++i)
+ {
+ text.append(lines[i]);
+ }
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)text.c_str(), text.length());
+ const S32 BUFFER_LEN = 1024;
+ char buf[BUFFER_LEN];
+ S32 len;
+ U8* last = NULL;
+ std::string last_line;
+ for(i = 0; i < LINE_COUNT; ++i)
+ {
+ len = BUFFER_LEN;
+ last = mBuffer.readAfter(ch.in(), last, (U8*)buf, len);
+ char* newline = strchr((char*)buf, '\n');
+ S32 offset = -((len - 1) - (newline - buf));
+ ++newline;
+ *newline = '\0';
+ last_line.assign(buf);
+ std::ostringstream message;
+ message << "line reads in line[" << i << "]";
+ ensure_equals(message.str().c_str(), last_line, lines[i]);
+ last = mBuffer.seek(ch.in(), last, offset);
+ }
+ }
+
+ template<> template<>
+ void buffer_object::test<8>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.in(), (U8*)"1", 1);
+ LLBufferArray buffer;
+ buffer.append(ch.in(), (U8*)"2", 1);
+ mBuffer.takeContents(buffer);
+ mBuffer.append(ch.in(), (U8*)"3", 1);
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("buffer size", count, 3);
+ U8* temp = new U8[count];
+ mBuffer.readAfter(ch.in(), NULL, temp, count);
+ ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3)));
+ delete[] temp;
+ }
+
+/*
+ template<> template<>
+ void buffer_object::test<9>()
+ {
+ char buffer[1024];
+ S32 size = sprintf(buffer,
+ "%d|%d|%s|%s|%s|%s|%s|%x|%x|%x|%x|%x|%s|%s|%d|%d|%x",
+ 7,
+ 7,
+ "Hang Glider INFO",
+ "18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a",
+ "0e346d8b-4433-4d66-a6b0-fd37083abc4c",
+ "0e346d8b-4433-4d66-a6b0-fd37083abc4c",
+ "00000000-0000-0000-0000-000000000000",
+ 0x7fffffff,
+ 0x7fffffff,
+ 0,
+ 0,
+ 0x7fffffff,
+ "69e0d357-2e7c-8990-a2bc-7f61c868e5a3",
+ "2004-06-04 16:09:17 note card",
+ 0,
+ 10,
+ 0) + 1;
+
+ //const char* expected = "7|7|Hang Glider INFO|18e84d1e-04a4-4c0d-8cb6-6c73477f0a9a|0e346d8b-4433-4d66-a6b0-fd37083abc4c|0e346d8b-4433-4d66-a6b0-fd37083abc4c|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|7fffffff|69e0d357-2e7c-8990-a2bc-7f61c868e5a3|2004-06-04 16:09:17 note card|0|10|0\0";
+
+ LLSD* bin_bucket = LLIMInfo::buildSDfrombuffer((U8*)buffer,size);
+
+ char post_buffer[1024];
+ U32 post_size;
+ LLIMInfo::getBinaryBucket(bin_bucket,(U8*)post_buffer,post_size);
+ ensure_equals("Buffer sizes",size,(S32)post_size);
+ ensure("Buffer content",!strcmp(buffer,post_buffer));
+ }
+*/
+
+ /*
+ template<> template<>
+ void buffer_object::test<>()
+ {
+ }
+ */
+}
+
+namespace tut
+{
+ struct buffer_and_stream_data
+ {
+ LLBufferArray mBuffer;
+ };
+ typedef test_group<buffer_and_stream_data> bas_test;
+ typedef bas_test::object bas_object;
+ tut::bas_test tbs("buffer_stream");
+
+ template<> template<>
+ void bas_object::test<1>()
+ {
+ const char HELLO_WORLD[] = "hello world";
+ const S32 str_len = strlen(HELLO_WORLD);
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ mBuffer.append(ch.in(), (U8*)HELLO_WORLD, str_len);
+ std::string hello;
+ std::string world;
+ str >> hello >> world;
+ ensure_equals("first word", hello, std::string("hello"));
+ ensure_equals("second word", world, std::string("world"));
+ }
+
+ template<> template<>
+ void bas_object::test<2>()
+ {
+ std::string part1("Eat my shor");
+ std::string part2("ts ho");
+ std::string part3("mer");
+ std::string ignore("ignore me");
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ mBuffer.append(ch.in(), (U8*)part1.c_str(), part1.length());
+ mBuffer.append(ch.in(), (U8*)part2.c_str(), part2.length());
+ mBuffer.append(ch.out(), (U8*)ignore.c_str(), ignore.length());
+ mBuffer.append(ch.in(), (U8*)part3.c_str(), part3.length());
+ std::string eat;
+ std::string my;
+ std::string shorts;
+ std::string homer;
+ str >> eat >> my >> shorts >> homer;
+ ensure_equals("word1", eat, std::string("Eat"));
+ ensure_equals("word2", my, std::string("my"));
+ ensure_equals("word3", shorts, std::string("shorts"));
+ ensure_equals("word4", homer, std::string("homer"));
+ }
+
+ template<> template<>
+ void bas_object::test<3>()
+ {
+ std::string part1("junk in ");
+ std::string part2("the trunk");
+ const S32 CHANNEL = 0;
+ mBuffer.append(CHANNEL, (U8*)part1.c_str(), part1.length());
+ mBuffer.append(CHANNEL, (U8*)part2.c_str(), part2.length());
+ U8* last = 0;
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 len = 11;
+ last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len);
+ buf[len] = '\0';
+ std::string actual(buf);
+ ensure_equals("first read", actual, std::string("junk in the"));
+ last = mBuffer.seek(CHANNEL, last, -6);
+ len = 12;
+ last = mBuffer.readAfter(CHANNEL, last, (U8*)buf, len);
+ buf[len] = '\0';
+ actual.assign(buf);
+ ensure_equals("seek and read", actual, std::string("in the trunk"));
+ }
+
+ template<> template<>
+ void bas_object::test<4>()
+ {
+ std::string phrase("zippity do da!");
+ const S32 CHANNEL = 0;
+ mBuffer.append(CHANNEL, (U8*)phrase.c_str(), phrase.length());
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 len = 7;
+ U8* last = mBuffer.readAfter(CHANNEL, NULL, (U8*)buf, len);
+ mBuffer.splitAfter(last);
+ LLBufferArray::segment_iterator_t it = mBuffer.beginSegment();
+ LLBufferArray::segment_iterator_t end = mBuffer.endSegment();
+ std::string first((char*)((*it).data()), (*it).size());
+ ensure_equals("first part", first, std::string("zippity"));
+ ++it;
+ std::string second((char*)((*it).data()), (*it).size());
+ ensure_equals("second part", second, std::string(" do da!"));
+ ++it;
+ ensure("iterators equal", (it == end));
+ }
+
+ template<> template<>
+ void bas_object::test<5>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream str(ch, &mBuffer);
+ std::string h1("hello");
+ std::string h2(", how are you doing?");
+ std::string expected(h1);
+ expected.append(h2);
+ str << h1 << h2;
+ str.flush();
+ const S32 BUF_LEN = 128;
+ char buf[BUF_LEN];
+ S32 actual_len = BUF_LEN;
+ S32 expected_len = h1.size() + h2.size();
+ (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len);
+ ensure_equals("streamed size", actual_len, expected_len);
+ buf[actual_len] = '\0';
+ std::string actual(buf);
+ ensure_equals("streamed to buf", actual, expected);
+ }
+
+ template<> template<>
+ void bas_object::test<6>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream bstr(ch, &mBuffer);
+ std::ostringstream ostr;
+ std::vector<LLUUID> ids;
+ LLUUID id;
+ for(int i = 0; i < 5; ++i)
+ {
+ id.generate();
+ ids.push_back(id);
+ }
+ bstr << "SELECT concat(u.username, ' ', l.name) "
+ << "FROM user u, user_last_name l "
+ << "WHERE u.last_name_id = l.last_name_id"
+ << " AND u.agent_id IN ('";
+ ostr << "SELECT concat(u.username, ' ', l.name) "
+ << "FROM user u, user_last_name l "
+ << "WHERE u.last_name_id = l.last_name_id"
+ << " AND u.agent_id IN ('";
+ std::copy(
+ ids.begin(),
+ ids.end(),
+ std::ostream_iterator<LLUUID>(bstr, "','"));
+ std::copy(
+ ids.begin(),
+ ids.end(),
+ std::ostream_iterator<LLUUID>(ostr, "','"));
+ bstr.seekp(-2, std::ios::cur);
+ ostr.seekp(-2, std::ios::cur);
+ bstr << ") ";
+ ostr << ") ";
+ bstr.flush();
+ const S32 BUF_LEN = 512;
+ char buf[BUF_LEN];
+ S32 actual_len = BUF_LEN;
+ (void) mBuffer.readAfter(ch.out(), NULL, (U8*)buf, actual_len);
+ buf[actual_len] = '\0';
+ std::string actual(buf);
+ std::string expected(ostr.str());
+ ensure_equals("size of string in seek",actual.size(),expected.size());
+ ensure_equals("seek in ostream", actual, expected);
+ }
+
+ template<> template<>
+ void bas_object::test<7>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream bstr(ch, &mBuffer);
+ bstr << "1";
+ bstr.flush();
+ S32 count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 1", count, 1);
+ LLBufferArray buffer;
+ buffer.append(ch.out(), (U8*)"2", 1);
+ mBuffer.takeContents(buffer);
+ count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 2", count, 2);
+ bstr << "3";
+ bstr.flush();
+ count = mBuffer.countAfter(ch.out(), NULL);
+ ensure_equals("buffer size 3", count, 3);
+ U8* temp = new U8[count];
+ mBuffer.readAfter(ch.out(), NULL, temp, count);
+ ensure("buffer content", (0 == memcmp(temp, (void*)"123", 3)));
+ delete[] temp;
+ }
+
+ template<> template<>
+ void bas_object::test<8>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream ostr(ch, &mBuffer);
+ typedef std::vector<U8> buf_t;
+ typedef std::vector<buf_t> actual_t;
+ actual_t actual;
+ buf_t source;
+ bool need_comma = false;
+ ostr << "[";
+ S32 total_size = 1;
+ for(S32 i = 2000; i < 2003; ++i)
+ {
+ if(need_comma)
+ {
+ ostr << ",";
+ ++total_size;
+ }
+ need_comma = true;
+ srand(69 + i);
+ S32 size = rand() % 1000 + 1000;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ actual.push_back(source);
+ ostr << "b(" << size << ")\"";
+ total_size += 8;
+ ostr.write((const char*)(&source[0]), size);
+ total_size += size;
+ source.clear();
+ ostr << "\"";
+ ++total_size;
+ }
+ ostr << "]";
+ ++total_size;
+ ostr.flush();
+
+ // now that we have a bunch of data on a stream, parse it all.
+ ch = mBuffer.nextChannel();
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("size of buffer", count, total_size);
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ count = LLSDSerialize::fromNotation(data, istr);
+ ensure("sd parsed", data.isDefined());
+
+ for(S32 j = 0; j < 3; ++j)
+ {
+ std::ostringstream name;
+ LLSD child(data[j]);
+ name << "found buffer " << j;
+ ensure(name.str(), child.isDefined());
+ source = child.asBinary();
+ name.str("");
+ name << "buffer " << j << " size";
+ ensure_equals(name.str().c_str(), source.size(), actual[j].size());
+ name.str("");
+ name << "buffer " << j << " contents";
+ ensure(
+ name.str(),
+ (0 == memcmp(&source[0], &actual[j][0], source.size())));
+ }
+ }
+
+ template<> template<>
+ void bas_object::test<9>()
+ {
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ LLBufferStream ostr(ch, &mBuffer);
+ typedef std::vector<U8> buf_t;
+ buf_t source;
+ bool need_comma = false;
+ ostr << "{";
+ S32 total_size = 1;
+ for(S32 i = 1000; i < 3000; ++i)
+ {
+ if(need_comma)
+ {
+ ostr << ",";
+ ++total_size;
+ }
+ need_comma = true;
+ ostr << "'" << i << "':";
+ total_size += 7;
+ srand(69 + i);
+ S32 size = rand() % 1000 + 1000;
+ std::generate_n(
+ std::back_insert_iterator<buf_t>(source),
+ size,
+ rand);
+ ostr << "b(" << size << ")\"";
+ total_size += 8;
+ ostr.write((const char*)(&source[0]), size);
+ total_size += size;
+ source.clear();
+ ostr << "\"";
+ ++total_size;
+ }
+ ostr << "}";
+ ++total_size;
+ ostr.flush();
+
+ // now that we have a bunch of data on a stream, parse it all.
+ ch = mBuffer.nextChannel();
+ S32 count = mBuffer.countAfter(ch.in(), NULL);
+ ensure_equals("size of buffer", count, total_size);
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ count = LLSDSerialize::fromNotation(data, istr);
+ ensure("sd parsed", data.isDefined());
+ }
+
+ template<> template<>
+ void bas_object::test<10>()
+ {
+ const char LOGIN_STREAM[] = "{'method':'login', 'parameter': [ {"
+ "'uri': 'sl-am:kellys.region.siva.lindenlab.com/location?start=url&px=128&py=128&pz=128&lx=0&ly=0&lz=0'}, "
+ "{'version': i1}, {'texture_data': [ '61d724fb-ad79-f637-2186-5cf457560daa', '6e38b9be-b7cc-e77a-8aec-029a42b0b416', "
+ "'a9073524-e89b-2924-ca6e-a81944109a1a', '658f18b5-5f1e-e593-f5d5-36c3abc7249a', '0cc799f4-8c99-6b91-bd75-b179b12429e2', "
+ "'59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '59fd9b64-8300-a425-aad8-2ffcbe9a49d2', '5748decc-f629-461c-9a36-a35a221fe21f', "
+ "'b8fc9be2-26a6-6b47-690b-0e902e983484', 'a13ca0fe-3802-dc97-e79a-70d12171c724', 'dd9643cf-fd5d-0376-ed4a-b1cc646a97d5', "
+ "'4ad13ae9-a112-af09-210a-cf9353a7a9e7', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', "
+ "'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', "
+ "'5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'],"
+ "'session_id': '324cfa9f-fe5d-4d1c-a317-35f20a86a4d1','position': [ i128, i128, i128],'last_name': 'Linden','group_title': '-> !BLING! <-','group_name': 'test!','agent_access': 'M',"
+ "'attachment_data': [ {'asset_id': 'aaede2b1-9955-09d4-5c93-2b557c778cf3','attachment_point': i6,'item_id': 'f3694abc-5122-db33-73d9-e0f4288dc2bf'}],"
+ "'buddy_ids': [ '101358d5-469d-4b24-9b85-4dc3c05e635d', '1b00fec7-6265-4875-acac-80d9cfe9295c', '203ad6df-b522-491d-ba48-4e24eb57aeff', "
+ "'22d4dcdb-aebb-47fa-b925-a871cc75ee48','27da3df5-1339-4463-80aa-40504ee3b3e5', '299d1720-b61f-4268-8c29-9614aa2d44c2', "
+ "'2b048a24-2737-4994-9fa5-becc8e466253', '2cd5dc14-a853-49a4-be3c-a5a7178e37bc', '3de548e1-57be-cfea-2b78-83ae3ad95998', "
+ "'3dee98e4-a6a3-4543-91c3-bbd528447ba7', '3e2d81a3-6263-6ffe-ad5c-8ce04bee07e9', '40e70b98-fed7-47f3-9700-1bce93f9350b', "
+ "'50a9b68e-b5aa-4d35-9137-3cfebda0a15c', '54295571-9357-43ff-ae74-a83b5138160f', '6191e2d7-5f96-4856-bdab-af0f79f47ae4', "
+ "'63e577d8-cd34-4235-a0a3-de0500133364', '79cfb666-4fd0-4af7-95df-fb7d96b4e24d', '8121c2f3-4a88-4c33-9899-8fc1273f47ee', "
+ "'909da964-ef23-4f2a-ba13-f2a8cfd454b6','a2e76fcd-9360-4f6d-a924-000000000001', 'aaa6d664-527e-4d83-9cbb-7ef79ccc7cc8', "
+ "'b79bfb6c-23be-49eb-b35b-30ff2f501b37', 'ba0d9c79-148c-4a79-8e3c-0665eebe2427', 'bc9bda98-57cd-498f-b993-4ff1ac9dec93', "
+ "'c62d16f6-81cb-419d-9cac-e46dc394084d', 'd48f8fa7-2512-4fe5-80c8-c0a923412e07', 'd77e3e24-7e6c-4c3f-96d0-a1746337f8fb', "
+ "'da615c63-a84b-4592-a3d6-a90dd3e92e6e', 'df47190a-7eb7-4aff-985f-2d1d3ad6c6e9', 'e3380196-72cd-499c-a2ba-caa180bd5fe4', "
+ "'e937863f-f134-4207-803b-d6e686651d6c', 'efcdf98b-5269-45ef-ac7a-0671f09ea9d9'],"
+ "'circuit_code': i124,'group_id': '8615c885-9cf0-bf0a-6e40-0c11462aa652','limited_to_estate': i1,'look_at': [ i0, i0, i0],"
+ "'agent_id': '0e346d8b-4433-4d66-a6b0-fd37083abc4c','first_name': 'Kelly','start': 'url'}]}";
+ LLChannelDescriptors ch = mBuffer.nextChannel();
+ mBuffer.append(ch.out(), (U8*)LOGIN_STREAM, strlen(LOGIN_STREAM));
+ ch = mBuffer.nextChannel();
+ LLBufferStream istr(ch, &mBuffer);
+ LLSD data;
+ S32 count = LLSDSerialize::fromNotation(data, istr);
+ ensure("parsed something", (count > 0));
+ ensure("sd parsed", data.isDefined());
+ ensure_equals("sd type", data.type(), LLSD::TypeMap);
+ ensure("has method", data.has("method"));
+ ensure("has parameter", data.has("parameter"));
+ LLSD parameter = data["parameter"];
+ ensure_equals("parameter is array", parameter.type(), LLSD::TypeArray);
+ LLSD agent_params = parameter[2];
+ std::string s_value;
+ s_value = agent_params["last_name"].asString();
+ ensure_equals("last name", s_value, std::string("Linden"));
+ s_value = agent_params["first_name"].asString();
+ ensure_equals("first name", s_value, std::string("Kelly"));
+ s_value = agent_params["agent_access"].asString();
+ ensure_equals("agent access", s_value, std::string("M"));
+ s_value = agent_params["group_name"].asString();
+ ensure_equals("group name", s_value, std::string("test!"));
+ s_value = agent_params["group_title"].asString();
+ ensure_equals("group title", s_value, std::string("-> !BLING! <-"));
+
+ LLUUID agent_id("0e346d8b-4433-4d66-a6b0-fd37083abc4c");
+ LLUUID id = agent_params["agent_id"];
+ ensure_equals("agent id", id, agent_id);
+ LLUUID session_id("324cfa9f-fe5d-4d1c-a317-35f20a86a4d1");
+ id = agent_params["session_id"];
+ ensure_equals("session id", id, session_id);
+ LLUUID group_id ("8615c885-9cf0-bf0a-6e40-0c11462aa652");
+ id = agent_params["group_id"];
+ ensure_equals("group id", id, group_id);
+
+ S32 i_val = agent_params["limited_to_estate"];
+ ensure_equals("limited to estate", i_val, 1);
+ i_val = agent_params["circuit_code"];
+ ensure_equals("circuit code", i_val, 124);
+ }
+
+
+ template<> template<>
+ void bas_object::test<11>()
+ {
+ std::string val = "{!'foo'@:#'bar'}";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, istr);
+ ensure_equals("parser error return value", count, -1);
+ ensure("data undefined", sd.isUndefined());
+ }
+
+ template<> template<>
+ void bas_object::test<12>()
+ {
+ std::string val = "{!'foo':[i1,'hi',{@'bar'#:[$i2%,^'baz'&]*}+]=}";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ S32 count = LLSDSerialize::fromNotation(sd, istr);
+ ensure_equals("parser error return value", count, -1);
+ ensure("data undefined", sd.isUndefined());
+ }
+
+/*
+ template<> template<>
+ void bas_object::test<13>()
+ {
+ }
+ template<> template<>
+ void bas_object::test<14>()
+ {
+ }
+ template<> template<>
+ void bas_object::test<15>()
+ {
+ }
+*/
+}
+
+
+namespace tut
+{
+ class PumpAndChainTestData
+ {
+ protected:
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLPumpIO::chain_t mChain;
+
+ public:
+ PumpAndChainTestData()
+ {
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ }
+
+ ~PumpAndChainTestData()
+ {
+ mChain.clear();
+ delete mPump;
+ apr_pool_destroy(mPool);
+ }
+ };
+ typedef test_group<PumpAndChainTestData> PumpAndChainTestGroup;
+ typedef PumpAndChainTestGroup::object PumpAndChainTestObject;
+ PumpAndChainTestGroup pumpAndChainTestGroup("pump_and_chain");
+
+ template<> template<>
+ void PumpAndChainTestObject::test<1>()
+ {
+ LLPipeStringExtractor* extractor = new LLPipeStringExtractor();
+
+ mChain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
+ mChain.push_back(LLIOPipe::ptr_t(extractor));
+
+ LLTimer timer;
+ timer.setTimerExpirySec(100.0f);
+
+ mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS);
+ while(!extractor->done() && !timer.hasExpired())
+ {
+ mPump->pump();
+ mPump->callback();
+ }
+
+ ensure("reading string finished", extractor->done());
+ ensure_equals("string was empty", extractor->string(), "");
+ }
+}
+
+/*
+namespace tut
+{
+ struct double_construct
+ {
+ public:
+ double_construct()
+ {
+ llinfos << "constructed" << llendl;
+ }
+ ~double_construct()
+ {
+ llinfos << "destroyed" << llendl;
+ }
+ };
+ typedef test_group<double_construct> double_construct_test_group;
+ typedef double_construct_test_group::object dc_test_object;
+ double_construct_test_group dctest("double construct");
+ template<> template<>
+ void dc_test_object::test<1>()
+ {
+ ensure("test 1", true);
+ }
+}
+*/
+
+namespace tut
+{
+ /**
+ * @brief we want to test the pipes & pumps under bad conditions.
+ */
+ struct pipe_and_pump_fitness
+ {
+ public:
+ enum
+ {
+ SERVER_LISTEN_PORT = 13050
+ };
+
+ pipe_and_pump_fitness()
+ {
+ LLFrameTimer::updateFrameTime();
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ mSocket = LLSocket::create(
+ mPool,
+ LLSocket::STREAM_TCP,
+ SERVER_LISTEN_PORT);
+ }
+
+ ~pipe_and_pump_fitness()
+ {
+ mSocket.reset();
+ delete mPump;
+ apr_pool_destroy(mPool);
+ }
+
+ protected:
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLSocket::ptr_t mSocket;
+ };
+ typedef test_group<pipe_and_pump_fitness> fitness_test_group;
+ typedef fitness_test_group::object fitness_test_object;
+ fitness_test_group fitness("pipe and pump fitness");
+
+ template<> template<>
+ void fitness_test_object::test<1>()
+ {
+ lldebugs << "fitness_test_object::test<1>()" << llendl;
+
+ // Set up the server
+ //lldebugs << "fitness_test_object::test<1> - setting up server."
+ // << llendl;
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLPipeStringInjector> emitter_t;
+ emitter_t* emitter = new emitter_t(
+ new LLPipeStringInjector("suckers never play me"));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ //lldebugs << "fitness_test_object::test<1> - initializing server."
+ // << llendl;
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ //lldebugs << "fitness_test_object::test<1> - connecting client."
+ // << llendl;
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, 1.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, 2.0f);
+ ensure("Did not take too long", (elapsed < 3.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<2>()
+ {
+ lldebugs << "fitness_test_object::test<2>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS / 2.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f);
+ ensure("Did not take too long", (elapsed < 3.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<3>()
+ {
+ lldebugs << "fitness_test_object::test<3>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, SHORT_CHAIN_EXPIRY_SECS * 2.0f);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS * 2.0f + 1.0f);
+ ensure("Did not take too long", (elapsed < 4.0f));
+ }
+
+ template<> template<>
+ void fitness_test_object::test<4>()
+ {
+ lldebugs << "fitness_test_object::test<4>()" << llendl;
+
+ // Set up the server
+ LLPumpIO::chain_t chain;
+ typedef LLCloneIOFactory<LLIOFuzz> emitter_t;
+ emitter_t* emitter = new emitter_t(new LLIOFuzz(1000000));
+ boost::shared_ptr<LLChainIOFactory> factory(emitter);
+ LLIOServerSocket* server = new LLIOServerSocket(
+ mPool,
+ mSocket,
+ factory);
+ server->setResponseTimeout(SHORT_CHAIN_EXPIRY_SECS + 2.0f);
+ chain.push_back(LLIOPipe::ptr_t(server));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // We need to tickle the pump a little to set up the listen()
+ pump_loop(mPump, 0.1f);
+
+ // Set up the client
+ LLSocket::ptr_t client = LLSocket::create(mPool, LLSocket::STREAM_TCP);
+ LLHost server_host("127.0.0.1", SERVER_LISTEN_PORT);
+ bool connected = client->blockingConnect(server_host);
+ ensure("Connected to server", connected);
+ lldebugs << "connected" << llendl;
+
+ // We have connected, since the socket reader does not block,
+ // the first call to read data will return EAGAIN, so we need
+ // to write something.
+ chain.clear();
+ chain.push_back(LLIOPipe::ptr_t(new LLPipeStringInjector("hi")));
+ chain.push_back(LLIOPipe::ptr_t(new LLIOSocketWriter(client)));
+ chain.push_back(LLIOPipe::ptr_t(new LLIONull));
+ mPump->addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ // Now, the server should immediately send the data, but we'll
+ // never read it. pump for a bit
+ F32 elapsed = pump_loop(mPump, SHORT_CHAIN_EXPIRY_SECS + 3.0f);
+ ensure("Did not take too long", (elapsed < DEFAULT_CHAIN_EXPIRY_SECS));
+ }
+}
+
+namespace tut
+{
+ struct rpc_server_data
+ {
+ class LLSimpleRPCResponse : public LLSDRPCResponse
+ {
+ public:
+ LLSimpleRPCResponse(LLSD* response) :
+ mResponsePtr(response)
+ {
+ }
+ ~LLSimpleRPCResponse() {}
+ virtual bool response(LLPumpIO* pump)
+ {
+ *mResponsePtr = mReturnValue;
+ return true;
+ }
+ virtual bool fault(LLPumpIO* pump)
+ {
+ *mResponsePtr = mReturnValue;
+ return false;
+ }
+ virtual bool error(LLPumpIO* pump)
+ {
+ ensure("LLSimpleRPCResponse::error()", false);
+ return false;
+ }
+ public:
+ LLSD* mResponsePtr;
+ };
+
+ class LLSimpleRPCClient : public LLSDRPCClient
+ {
+ public:
+ LLSimpleRPCClient(LLSD* response) :
+ mResponsePtr(response)
+ {
+ }
+ ~LLSimpleRPCClient() {}
+ void echo(const LLSD& parameter)
+ {
+ LLSimpleRPCResponse* resp;
+ resp = new LLSimpleRPCResponse(mResponsePtr);
+ static const std::string URI_NONE;
+ static const std::string METHOD_ECHO("echo");
+ call(URI_NONE, METHOD_ECHO, parameter, resp, EPBQ_CALLBACK);
+ }
+ public:
+ LLSD* mResponsePtr;
+ };
+
+ class LLSimpleRPCServer : public LLSDRPCServer
+ {
+ public:
+ LLSimpleRPCServer()
+ {
+ mMethods["echo"] = new mem_fn_t(
+ this,
+ &LLSimpleRPCServer::rpc_Echo);
+ }
+ ~LLSimpleRPCServer() {}
+ protected:
+ typedef LLSDRPCMethodCall<LLSimpleRPCServer> mem_fn_t;
+ ESDRPCSStatus rpc_Echo(
+ const LLSD& parameter,
+ const LLChannelDescriptors& channels,
+ LLBufferArray* data)
+ {
+ buildResponse(channels, data, parameter);
+ return ESDRPCS_DONE;
+ }
+ };
+
+ apr_pool_t* mPool;
+ LLPumpIO* mPump;
+ LLPumpIO::chain_t mChain;
+ LLSimpleRPCClient* mClient;
+ LLSD mResponse;
+
+ rpc_server_data() :
+ mPool(NULL),
+ mPump(NULL),
+ mClient(NULL)
+ {
+ apr_pool_create(&mPool, NULL);
+ mPump = new LLPumpIO(mPool);
+ mClient = new LLSimpleRPCClient(&mResponse);
+ mChain.push_back(LLIOPipe::ptr_t(mClient));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
+ mChain.push_back(LLIOPipe::ptr_t(new LLSimpleRPCServer));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
+ mChain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
+ mChain.push_back(LLIOPipe::ptr_t(mClient));
+ }
+ ~rpc_server_data()
+ {
+ mChain.clear();
+ delete mPump;
+ mPump = NULL;
+ apr_pool_destroy(mPool);
+ mPool = NULL;
+ }
+ void pump_loop(const LLSD& request)
+ {
+ LLTimer timer;
+ timer.setTimerExpirySec(1.0f);
+ mClient->echo(request);
+ mPump->addChain(mChain, DEFAULT_CHAIN_EXPIRY_SECS);
+ while(mResponse.isUndefined() && !timer.hasExpired())
+ {
+ mPump->pump();
+ mPump->callback();
+ }
+ }
+ };
+ typedef test_group<rpc_server_data> rpc_server_test;
+ typedef rpc_server_test::object rpc_server_object;
+ tut::rpc_server_test rpc("rpc_server");
+
+ template<> template<>
+ void rpc_server_object::test<1>()
+ {
+ LLSD request;
+ request = 1;
+ pump_loop(request);
+ //llinfos << "request: " << *request << llendl;
+ //llinfos << "response: " << *mResponse << llendl;
+ ensure_equals("integer request response", mResponse.asInteger(), 1);
+ }
+
+ template<> template<>
+ void rpc_server_object::test<2>()
+ {
+ std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0");
+ std::stringstream stream;
+ stream << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}";
+ std::vector<U8> expected_binary;
+ expected_binary.resize(stream.str().size());
+ memcpy(&expected_binary[0], stream.str().c_str(), stream.str().size());
+ stream.str("");
+ stream << "[{'uri':'" << uri << "'}, {'version':i1}, "
+ << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], "
+ << "'attachment_data':["
+ << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},"
+ << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << expected_binary.size() << ")\"";
+ stream.write((const char*)&expected_binary[0], expected_binary.size());
+ stream << "\"}"
+ << "]"
+ << "}]";
+
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, stream);
+ ensure("parsed something", (count > 0));
+
+ pump_loop(request);
+ ensure_equals("return type", mResponse.type(), LLSD::TypeArray);
+ ensure_equals("return size", mResponse.size(), 3);
+
+ ensure_equals(
+ "uri parameter type",
+ mResponse[0].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "uri type",
+ mResponse[0]["uri"].type(),
+ LLSD::TypeString);
+ ensure_equals("uri value", mResponse[0]["uri"].asString(), uri);
+
+ ensure_equals(
+ "version parameter type",
+ mResponse[1].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "version type",
+ mResponse[1]["version"].type(),
+ LLSD::TypeInteger);
+ ensure_equals(
+ "version value",
+ mResponse[1]["version"].asInteger(),
+ 1);
+
+ ensure_equals("agent params type", mResponse[2].type(), LLSD::TypeMap);
+ LLSD attachment_data = mResponse[2]["attachment_data"];
+ ensure("attachment data exists", attachment_data.isDefined());
+ ensure_equals(
+ "attachment type",
+ attachment_data.type(),
+ LLSD::TypeArray);
+ ensure_equals(
+ "attachment type 0",
+ attachment_data[0].type(),
+ LLSD::TypeMap);
+ ensure_equals(
+ "attachment type 1",
+ attachment_data[1].type(),
+ LLSD::TypeMap);
+ ensure_equals("attachment size 1", attachment_data[1].size(), 3);
+ ensure_equals(
+ "asset data type",
+ attachment_data[1]["asset_data"].type(),
+ LLSD::TypeBinary);
+ std::vector<U8> actual_binary;
+ actual_binary = attachment_data[1]["asset_data"].asBinary();
+ ensure_equals(
+ "binary data size",
+ actual_binary.size(),
+ expected_binary.size());
+ ensure(
+ "binary data",
+ (0 == memcmp(
+ &actual_binary[0],
+ &expected_binary[0],
+ expected_binary.size())));
+ }
+
+ template<> template<>
+ void rpc_server_object::test<3>()
+ {
+ std::string uri("sl-am:66.150.244.180:12035/location?start=region&px=70.9247&py=254.378&pz=38.7304&lx=-0.043753&ly=-0.999042&lz=0");
+
+ LLBufferArray buffer;
+ LLChannelDescriptors buffer_channels = buffer.nextChannel();
+ LLBufferStream stream(buffer_channels, &buffer);
+ stream << "[{'uri':'" << uri << "'}, {'version':i1}, "
+ << "{'agent_id':'3c115e51-04f4-523c-9fa6-98aff1034730', 'session_id':'2c585cec-038c-40b0-b42e-a25ebab4d132', 'circuit_code':i1075, 'start':'region', 'limited_to_estate':i1 'first_name':'Phoenix', 'last_name':'Linden', 'group_title':'', 'group_id':u00000000-0000-0000-0000-000000000000, 'position':[r70.9247,r254.378,r38.7304], 'look_at':[r-0.043753,r-0.999042,r0], 'granters':[ua2e76fcd-9360-4f6d-a924-000000000003], 'texture_data':['5e481e8a-58a6-fc34-6e61-c7a36095c07f', 'c39675f5-ca90-a304-bb31-42cdb803a132', '5c989edf-88d1-b2ac-b00b-5ed4bab8e368', '6522e74d-1660-4e7f-b601-6f48c1659a77', '7ca39b4c-bd19-4699-aff7-f93fd03d3e7b', '41c58177-5eb6-5aeb-029d-bc4093f3c130', '97b75473-8b93-9b25-2a11-035b9ae93195', '1c2d8d9b-90eb-89d4-dea8-c1ed83990614', '69ec543f-e27b-c07c-9094-a8be6300f274', 'c9f8b80f-c629-4633-04ee-c566ce9fea4b', '989cddba-7ab6-01ed-67aa-74accd2a2a65', '45e319b2-6a8c-fa5c-895b-1a7149b88aef', '5748decc-f629-461c-9a36-a35a221fe21f', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', '406f98fd-9c89-1d52-5f39-e67d508c5ee5', '685fbe10-ab40-f065-0aec-726cc6dfd7a1', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97', 'c228d1cf-4b5d-4ba8-84f4-899a0796aa97'], "
+ << "'attachment_data':["
+ << "{'attachment_point':i2, 'item_id':'d6852c11-a74e-309a-0462-50533f1ef9b3', 'asset_id':'c69b29b1-8944-58ae-a7c5-2ca7b23e22fb'},";
+
+ std::stringstream tmp_str;
+ tmp_str << "{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n}";
+ std::vector<U8> expected_binary;
+ expected_binary.resize(tmp_str.str().size());
+ memcpy(
+ &expected_binary[0],
+ tmp_str.str().c_str(),
+ tmp_str.str().size());
+
+ LLBufferArray attachment_buffer;
+ LLChannelDescriptors attach_channels = attachment_buffer.nextChannel();
+ LLBufferStream attach_stream(attach_channels, &attachment_buffer);
+ attach_stream.write((const char*)&expected_binary[0], expected_binary.size());
+ attach_stream.flush();
+ S32 len = attachment_buffer.countAfter(attach_channels.out(), NULL);
+ stream << "{'attachment_point':i10, 'item_id':'ff852c22-a74e-309a-0462-50533f1ef900', 'asset_data':b(" << len << ")\"";
+ stream.flush();
+ buffer.takeContents(attachment_buffer);
+ stream << "\"}]}]";
+ stream.flush();
+
+ LLChannelDescriptors read_channel = buffer.nextChannel();
+ LLBufferStream read_stream(read_channel, &buffer);
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, read_stream);
+ ensure("parsed something", (count > 0));
+ ensure("deserialized", request.isDefined());
+
+ // do the rpc round trip
+ pump_loop(request);
+
+ ensure_equals("return type", mResponse.type(), LLSD::TypeArray);
+ ensure_equals("return size", mResponse.size(), 3);
+
+ LLSD child = mResponse[0];
+ ensure("uri map exists", child.isDefined());
+ ensure_equals("uri parameter type", child.type(), LLSD::TypeMap);
+ ensure("uri string exists", child.has("uri"));
+ ensure_equals("uri type", child["uri"].type(), LLSD::TypeString);
+ ensure_equals("uri value", child["uri"].asString(), uri);
+
+ child = mResponse[1];
+ ensure("version map exists", child.isDefined());
+ ensure_equals("version param type", child.type(), LLSD::TypeMap);
+ ensure_equals(
+ "version type",
+ child["version"].type(),
+ LLSD::TypeInteger);
+ ensure_equals("version value", child["version"].asInteger(), 1);
+
+ child = mResponse[2];
+ ensure("agent params map exists", child.isDefined());
+ ensure_equals("agent params type", child.type(), LLSD::TypeMap);
+ child = child["attachment_data"];
+ ensure("attachment data exists", child.isDefined());
+ ensure_equals("attachment type", child.type(), LLSD::TypeArray);
+ LLSD attachment = child[0];
+ ensure_equals("attachment type 0", attachment.type(), LLSD::TypeMap);
+ attachment = child[1];
+ ensure_equals("attachment type 1", attachment.type(), LLSD::TypeMap);
+ ensure_equals("attachment size 1", attachment.size(), 3);
+ ensure_equals(
+ "asset data type",
+ attachment["asset_data"].type(),
+ LLSD::TypeBinary);
+ std::vector<U8> actual_binary = attachment["asset_data"].asBinary();
+ ensure_equals(
+ "binary data size",
+ actual_binary.size(),
+ expected_binary.size());
+ ensure(
+ "binary data",
+ (0 == memcmp(
+ &actual_binary[0],
+ &expected_binary[0],
+ expected_binary.size())));
+ }
+
+ template<> template<>
+ void rpc_server_object::test<4>()
+ {
+ std::string message("parcel '' is naughty.");
+ std::stringstream str;
+ str << "{'message':'" << LLSDNotationFormatter::escapeString(message)
+ << "'}";
+ LLSD request;
+ S32 count = LLSDSerialize::fromNotation(request, str);
+ ensure_equals("parse count", count, 2);
+ ensure_equals("request type", request.type(), LLSD::TypeMap);
+ pump_loop(request);
+ ensure("valid response", mResponse.isDefined());
+ ensure_equals("response type", mResponse.type(), LLSD::TypeMap);
+ std::string actual = mResponse["message"].asString();
+ ensure_equals("message contents", actual, message);
+ }
+
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ // test some of the problem cases with llsdrpc over xmlrpc -
+ // for example:
+ // * arrays are auto-converted to parameter lists, thus, this
+ // becomes one parameter.
+ // * undef goes over the wire as false (this might not be a good idea)
+ // * uuids are converted to string.
+ std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]";
+ std::istringstream istr;
+ istr.str(val);
+ LLSD sd;
+ LLSDSerialize::fromNotation(sd, istr);
+ pump_loop(sd);
+ ensure("valid response", mResponse.isDefined());
+ ensure_equals("parsed type", mResponse.type(), LLSD::TypeMap);
+ ensure_equals("parsed size", mResponse.size(), 2);
+ LLSD failures = mResponse["failures"];
+ ensure_equals("no failures.", failures.asBoolean(), false);
+ LLSD success = mResponse["successfuls"];
+ ensure_equals("success type", success.type(), LLSD::TypeArray);
+ ensure_equals("success size", success.size(), 1);
+ ensure_equals(
+ "success instance type",
+ success[0].type(),
+ LLSD::TypeString);
+ }
+
+/*
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ std::string expected("\xf3");//\xffsomething");
+ LLSD* request = LLSD::createString(expected);
+ pump_loop(request);
+ std::string actual;
+ mResponse->getString(actual);
+ if(actual != expected)
+ {
+ //llwarns << "iteration " << i << llendl;
+ std::ostringstream e_str;
+ std::string::iterator iter = expected.begin();
+ std::string::iterator end = expected.end();
+ for(; iter != end; ++iter)
+ {
+ e_str << (S32)((U8)(*iter)) << " ";
+ }
+ e_str << std::endl;
+ llsd_serialize_string(e_str, expected);
+ llwarns << "expected size: " << expected.size() << llendl;
+ llwarns << "expected: " << e_str.str() << llendl;
+
+ std::ostringstream a_str;
+ iter = actual.begin();
+ end = actual.end();
+ for(; iter != end; ++iter)
+ {
+ a_str << (S32)((U8)(*iter)) << " ";
+ }
+ a_str << std::endl;
+ llsd_serialize_string(a_str, actual);
+ llwarns << "actual size: " << actual.size() << llendl;
+ llwarns << "actual: " << a_str.str() << llendl;
+ }
+ ensure_equals("binary string request response", actual, expected);
+ delete request;
+ }
+
+ template<> template<>
+ void rpc_server_object::test<5>()
+ {
+ }
+*/
+}
+
+
+/*
+'asset_data':b(12100)"{'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemID STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"
+*/
diff --git a/indra/test/llhttpclient_tut.cpp b/indra/test/llhttpclient_tut.cpp
new file mode 100644
index 0000000000..98f24c1bdd
--- /dev/null
+++ b/indra/test/llhttpclient_tut.cpp
@@ -0,0 +1,296 @@
+/**
+ * @file llhttpclient_tut.cpp
+ * @brief Testing the HTTP client classes.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * These classes test the HTTP client framework.
+ *
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llhttpclient.h"
+#include "llpipeutil.h"
+#include "llpumpio.h"
+
+#include "llsdhttpserver.h"
+#include "lliohttpserver.h"
+#include "lliosocket.h"
+
+namespace tut
+{
+ LLSD storage;
+
+ class LLSDStorageNode : public LLHTTPNode
+ {
+ public:
+ LLSD get() const { return storage; }
+ LLSD put(const LLSD& value) const { storage = value; return LLSD(); }
+ };
+
+ class ErrorNode : public LLHTTPNode
+ {
+ public:
+ void get(ResponsePtr r, const LLSD& context) const
+ { r->status(599, "Intentional error"); }
+ void post(ResponsePtr r, const LLSD& context, const LLSD& input) const
+ { r->status(input["status"], input["reason"]); }
+ };
+
+ class TimeOutNode : public LLHTTPNode
+ {
+ public:
+ void get(ResponsePtr r, const LLSD& context) const
+ {
+ /* do nothing, the request will eventually time out */
+ }
+ };
+
+ LLHTTPRegistration<LLSDStorageNode> gStorageNode("/test/storage");
+ LLHTTPRegistration<ErrorNode> gErrorNode("/test/error");
+ LLHTTPRegistration<TimeOutNode> gTimeOutNode("/test/timeout");
+
+ struct HTTPClientTestData
+ {
+ public:
+ HTTPClientTestData()
+ {
+ apr_pool_create(&mPool, NULL);
+ mServerPump = new LLPumpIO(mPool);
+ mClientPump = new LLPumpIO(mPool);
+
+ LLHTTPClient::setPump(*mClientPump);
+ }
+
+ ~HTTPClientTestData()
+ {
+ delete mServerPump;
+ delete mClientPump;
+ apr_pool_destroy(mPool);
+ }
+
+ void setupTheServer()
+ {
+ LLHTTPNode& root = LLCreateHTTPServer(mPool, *mServerPump, 8888);
+
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(root);
+ }
+
+ void runThePump(float timeout = 100.0f)
+ {
+ LLTimer timer;
+ timer.setTimerExpirySec(timeout);
+
+ while(!mSawCompleted && !timer.hasExpired())
+ {
+ if (mServerPump)
+ {
+ mServerPump->pump();
+ mServerPump->callback();
+ }
+ if (mClientPump)
+ {
+ mClientPump->pump();
+ mClientPump->callback();
+ }
+ }
+ }
+
+ void killServer()
+ {
+ delete mServerPump;
+ mServerPump = NULL;
+ }
+
+ private:
+ apr_pool_t* mPool;
+ LLPumpIO* mServerPump;
+ LLPumpIO* mClientPump;
+
+
+ protected:
+ void ensureStatusOK()
+ {
+ if (mSawError)
+ {
+ std::string msg =
+ llformat("error() called when not expected, status %d",
+ mStatus);
+ fail(msg);
+ }
+ }
+
+ void ensureStatusError()
+ {
+ if (!mSawError)
+ {
+ fail("error() wasn't called");
+ }
+ }
+
+ LLSD getResult()
+ {
+ return mResult;
+ }
+
+ protected:
+ bool mSawError;
+ U32 mStatus;
+ std::string mReason;
+ bool mSawCompleted;
+ LLSD mResult;
+ bool mResultDeleted;
+
+ class Result : public LLHTTPClient::Responder
+ {
+ protected:
+ Result(HTTPClientTestData& client)
+ : mClient(client)
+ {
+ }
+
+ public:
+ static boost::intrusive_ptr<Result> build(HTTPClientTestData& client)
+ {
+ return boost::intrusive_ptr<Result>(new Result(client));
+ }
+
+ ~Result()
+ {
+ mClient.mResultDeleted = true;
+ }
+
+ virtual void error(U32 status, const std::string& reason)
+ {
+ mClient.mSawError = true;
+ mClient.mStatus = status;
+ mClient.mReason = reason;
+ }
+
+ virtual void result(const LLSD& content)
+ {
+ mClient.mResult = content;
+ }
+
+ virtual void completed(
+ U32 status, const std::string& reason,
+ const LLSD& content)
+ {
+ LLHTTPClient::Responder::completed(status, reason, content);
+
+ mClient.mSawCompleted = true;
+ }
+
+ private:
+ HTTPClientTestData& mClient;
+ };
+
+ friend class Result;
+
+ protected:
+ LLHTTPClient::ResponderPtr newResult()
+ {
+ mSawError = false;
+ mStatus = 0;
+ mSawCompleted = false;
+ mResult.clear();
+ mResultDeleted = false;
+
+ return Result::build(*this);
+ }
+ };
+
+
+ typedef test_group<HTTPClientTestData> HTTPClientTestGroup;
+ typedef HTTPClientTestGroup::object HTTPClientTestObject;
+ HTTPClientTestGroup httpClientTestGroup("http_client");
+
+ template<> template<>
+ void HTTPClientTestObject::test<1>()
+ {
+ LLHTTPClient::get("http://www.google.com/", newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure("result object wasn't destroyed", mResultDeleted);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<2>()
+ {
+ LLHTTPClient::get("http://www.invalid", newResult());
+ runThePump();
+ ensureStatusError();
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<3>()
+ {
+ LLSD sd;
+
+ sd["list"][0]["one"] = 1;
+ sd["list"][0]["two"] = 2;
+ sd["list"][1]["three"] = 3;
+ sd["list"][1]["four"] = 4;
+
+ setupTheServer();
+
+ LLHTTPClient::post("http://localhost:8888/web/echo", sd, newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure_equals("echoed result matches", getResult(), sd);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<4>()
+ {
+ LLSD sd;
+
+ sd["message"] = "This is my test message.";
+
+ setupTheServer();
+ LLHTTPClient::put("http://localhost:8888/test/storage", sd, newResult());
+ runThePump();
+ ensureStatusOK();
+
+ LLHTTPClient::get("http://localhost:8888/test/storage", newResult());
+ runThePump();
+ ensureStatusOK();
+ ensure_equals("echoed result matches", getResult(), sd);
+
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<5>()
+ {
+ LLSD sd;
+ sd["status"] = 543;
+ sd["reason"] = "error for testing";
+
+ setupTheServer();
+
+ LLHTTPClient::post("http://localhost:8888/test/error", sd, newResult());
+ runThePump();
+ ensureStatusError();
+ ensure_contains("reason", mReason, sd["reason"]);
+ }
+
+ template<> template<>
+ void HTTPClientTestObject::test<6>()
+ {
+ setupTheServer();
+
+ LLHTTPClient::get("http://localhost:8888/test/timeout", newResult());
+ runThePump(1.0f);
+ killServer();
+ runThePump();
+ ensureStatusError();
+ ensure_equals("reason", mReason, "STATUS_ERROR");
+ }
+}
diff --git a/indra/test/llhttpnode_tut.cpp b/indra/test/llhttpnode_tut.cpp
new file mode 100644
index 0000000000..9350645d2a
--- /dev/null
+++ b/indra/test/llhttpnode_tut.cpp
@@ -0,0 +1,409 @@
+/**
+ * @file lliohttpserver_tut.cpp
+ * @date May 2006
+ * @brief HTTP server unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llhttpnode.h"
+#include "llsdhttpserver.h"
+
+namespace tut
+{
+ struct HTTPNodeTestData
+ {
+ LLHTTPNode mRoot;
+ LLSD mContext;
+
+ const LLSD& context() { return mContext; }
+
+ std::string remainderPath()
+ {
+ std::ostringstream pathOutput;
+ bool addSlash = false;
+
+ LLSD& remainder = mContext["request"]["remainder"];
+ for (LLSD::array_const_iterator i = remainder.beginArray();
+ i != remainder.endArray();
+ ++i)
+ {
+ if (addSlash) { pathOutput << '/'; }
+ pathOutput << i->asString();
+ addSlash = true;
+ }
+
+ return pathOutput.str();
+ }
+
+ void ensureRootTraversal(const std::string& path,
+ const LLHTTPNode* expectedNode,
+ const char* expectedRemainder)
+ {
+ mContext.clear();
+
+ const LLHTTPNode* actualNode = mRoot.traverse(path, mContext);
+
+ ensure_equals("traverse " + path + " node",
+ actualNode, expectedNode);
+ ensure_equals("traverse " + path + " remainder",
+ remainderPath(), expectedRemainder);
+ }
+
+ class Response : public LLHTTPNode::Response
+ {
+ public:
+ static LLPointer<Response> create() {return new Response();}
+
+ LLSD mResult;
+
+ void result(const LLSD& result) { mResult = result; }
+ void status(S32 code, const std::string& message) { }
+
+ private:
+ Response() {;} // Must be accessed through LLPointer.
+ };
+
+ typedef LLPointer<Response> ResponsePtr;
+
+ LLSD get(const std::string& path)
+ {
+ mContext.clear();
+ const LLHTTPNode* node = mRoot.traverse(path, mContext);
+ ensure(path + " found", node != NULL);
+
+ ResponsePtr response = Response::create();
+ node->get(LLHTTPNode::ResponsePtr(response), mContext);
+ return response->mResult;
+ }
+
+ LLSD post(const std::string& path, const LLSD& input)
+ {
+ mContext.clear();
+ const LLHTTPNode* node = mRoot.traverse(path, mContext);
+ ensure(path + " found", node != NULL);
+
+ ResponsePtr response = Response::create();
+ node->post(LLHTTPNode::ResponsePtr(response), mContext, input);
+ return response->mResult;
+ }
+
+ void ensureMemberString(const std::string& name,
+ const LLSD& actualMap, const std::string& member,
+ const std::string& expectedValue)
+ {
+ ensure_equals(name + " " + member,
+ actualMap[member].asString(), expectedValue);
+ }
+
+
+ void ensureInArray(const LLSD& actualArray,
+ const std::string& expectedValue)
+ {
+ LLSD::array_const_iterator i = actualArray.beginArray();
+ LLSD::array_const_iterator end = actualArray.endArray();
+
+ for (; i != end; ++i)
+ {
+ std::string path = i->asString();
+
+ if (path == expectedValue)
+ {
+ return;
+ }
+ }
+
+ fail("didn't find " + expectedValue);
+ }
+
+ };
+
+ typedef test_group<HTTPNodeTestData> HTTPNodeTestGroup;
+ typedef HTTPNodeTestGroup::object HTTPNodeTestObject;
+ HTTPNodeTestGroup httpNodeTestGroup("http node");
+
+ template<> template<>
+ void HTTPNodeTestObject::test<1>()
+ {
+ // traversal of the lone node
+
+ ensureRootTraversal("", &mRoot, "");
+ ensureRootTraversal("/", &mRoot, "");
+ ensureRootTraversal("foo", NULL, "foo");
+ ensureRootTraversal("foo/bar", NULL, "foo/bar");
+
+ ensure_equals("root of root", mRoot.rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<2>()
+ {
+ // simple traversal of a single node
+
+ LLHTTPNode* helloNode = new LLHTTPNode;
+ mRoot.addNode("hello", helloNode);
+
+ ensureRootTraversal("hello", helloNode, "");
+ ensureRootTraversal("/hello", helloNode, "");
+ ensureRootTraversal("hello/", helloNode, "");
+ ensureRootTraversal("/hello/", helloNode, "");
+
+ ensureRootTraversal("hello/there", NULL, "there");
+
+ ensure_equals("root of hello", helloNode->rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<3>()
+ {
+ // traversal of mutli-branched tree
+
+ LLHTTPNode* greekNode = new LLHTTPNode;
+ LLHTTPNode* alphaNode = new LLHTTPNode;
+ LLHTTPNode* betaNode = new LLHTTPNode;
+ LLHTTPNode* gammaNode = new LLHTTPNode;
+
+ greekNode->addNode("alpha", alphaNode);
+ greekNode->addNode("beta", betaNode);
+ greekNode->addNode("gamma", gammaNode);
+ mRoot.addNode("greek", greekNode);
+
+ LLHTTPNode* hebrewNode = new LLHTTPNode;
+ LLHTTPNode* alephNode = new LLHTTPNode;
+
+ hebrewNode->addNode("aleph", alephNode);
+ mRoot.addNode("hebrew", hebrewNode);
+
+ ensureRootTraversal("greek/alpha", alphaNode, "");
+ ensureRootTraversal("greek/beta", betaNode, "");
+ ensureRootTraversal("greek/delta", NULL, "delta");
+ ensureRootTraversal("greek/gamma", gammaNode, "");
+ ensureRootTraversal("hebrew/aleph", alephNode, "");
+
+ ensure_equals("root of greek", greekNode->rootNode(), &mRoot);
+ ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
+ ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
+ ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
+ ensure_equals("root of hebrew", hebrewNode->rootNode(), &mRoot);
+ ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<4>()
+ {
+ // automatic creation of parent nodes and not overriding existing nodes
+
+ LLHTTPNode* alphaNode = new LLHTTPNode;
+ LLHTTPNode* betaNode = new LLHTTPNode;
+ LLHTTPNode* gammaNode = new LLHTTPNode;
+ LLHTTPNode* gamma2Node = new LLHTTPNode;
+
+ mRoot.addNode("greek/alpha", alphaNode);
+ mRoot.addNode("greek/beta", betaNode);
+
+ mRoot.addNode("greek/gamma", gammaNode);
+ mRoot.addNode("greek/gamma", gamma2Node);
+
+ LLHTTPNode* alephNode = new LLHTTPNode;
+
+ mRoot.addNode("hebrew/aleph", alephNode);
+
+ ensureRootTraversal("greek/alpha", alphaNode, "");
+ ensureRootTraversal("greek/beta", betaNode, "");
+ ensureRootTraversal("greek/delta", NULL, "delta");
+ ensureRootTraversal("greek/gamma", gammaNode, "");
+ ensureRootTraversal("hebrew/aleph", alephNode, "");
+
+ ensure_equals("root of alpha", alphaNode->rootNode(), &mRoot);
+ ensure_equals("root of beta", betaNode->rootNode(), &mRoot);
+ ensure_equals("root of gamma", gammaNode->rootNode(), &mRoot);
+ ensure_equals("root of aleph", alephNode->rootNode(), &mRoot);
+ }
+
+ class IntegerNode : public LLHTTPNode
+ {
+ public:
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ int n = context["extra"]["value"];
+
+ LLSD info;
+ info["value"] = n;
+ info["positive"] = n > 0;
+ info["zero"] = n == 0;
+ info["negative"] = n < 0;
+
+ response->result(info);
+ }
+
+ virtual bool validate(const std::string& name, LLSD& context) const
+ {
+ int n;
+ std::istringstream i_stream(name);
+ i_stream >> n;
+
+ if (i_stream.fail() || i_stream.get() != EOF)
+ {
+ return false;
+ }
+
+ context["extra"]["value"] = n;
+ return true;
+ }
+ };
+
+ class SquareNode : public LLHTTPNode
+ {
+ public:
+ virtual void get(ResponsePtr response, const LLSD& context) const
+ {
+ int n = context["extra"]["value"];
+ response->result(n*n);
+ }
+ };
+
+ template<> template<>
+ void HTTPNodeTestObject::test<5>()
+ {
+ // wildcard nodes
+
+ LLHTTPNode* miscNode = new LLHTTPNode;
+ LLHTTPNode* iNode = new IntegerNode;
+ LLHTTPNode* sqNode = new SquareNode;
+
+ mRoot.addNode("test/misc", miscNode);
+ mRoot.addNode("test/<int>", iNode);
+ mRoot.addNode("test/<int>/square", sqNode);
+
+ ensureRootTraversal("test/42", iNode, "");
+ ensure_equals("stored integer",
+ context()["extra"]["value"].asInteger(), 42);
+
+ ensureRootTraversal("test/bob", NULL, "bob");
+ ensure("nothing stored",
+ context()["extra"]["value"].isUndefined());
+
+ ensureRootTraversal("test/3/square", sqNode, "");
+ ResponsePtr response = Response::create();
+ sqNode->get(LLHTTPNode::ResponsePtr(response), context());
+ ensure_equals("square result", response->mResult.asInteger(), 9);
+ }
+
+ class AlphaNode : public LLHTTPNode
+ {
+ public:
+ virtual bool handles(const LLSD& remainder, LLSD& context) const
+ {
+ LLSD::array_const_iterator i = remainder.beginArray();
+ LLSD::array_const_iterator end = remainder.endArray();
+
+ for (; i != end; ++i)
+ {
+ std::string s = i->asString();
+ if (s.empty() || s[0] != 'a')
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ };
+
+ template<> template<>
+ void HTTPNodeTestObject::test<6>()
+ {
+ // nodes that handle remainders
+
+ LLHTTPNode* miscNode = new LLHTTPNode;
+ LLHTTPNode* aNode = new AlphaNode;
+ LLHTTPNode* zNode = new LLHTTPNode;
+
+ mRoot.addNode("test/misc", miscNode);
+ mRoot.addNode("test/alpha", aNode);
+ mRoot.addNode("test/alpha/zebra", zNode);
+
+ ensureRootTraversal("test/alpha", aNode, "");
+ ensureRootTraversal("test/alpha/abe", aNode, "abe");
+ ensureRootTraversal("test/alpha/abe/amy", aNode, "abe/amy");
+ ensureRootTraversal("test/alpha/abe/bea", NULL, "abe/bea");
+ ensureRootTraversal("test/alpha/bob", NULL, "bob");
+ ensureRootTraversal("test/alpha/zebra", zNode, "");
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<7>()
+ {
+ // test auto registration
+
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ {
+ LLSD result = get("web/hello");
+ ensure_equals("hello result", result.asString(), "hello");
+ }
+ {
+ LLSD stuff = 3.14159;
+ LLSD result = post("web/echo", stuff);
+ ensure_equals("echo result", result, stuff);
+ }
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<8>()
+ {
+ // test introspection
+
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ mRoot.addNode("test/misc", new LLHTTPNode);
+ mRoot.addNode("test/<int>", new IntegerNode);
+ mRoot.addNode("test/<int>/square", new SquareNode);
+
+ const LLSD result = get("web/server/api");
+
+ ensure("result is array", result.isArray());
+ ensure("result size", result.size() >= 2);
+
+ ensureInArray(result, "web/echo");
+ ensureInArray(result, "web/hello");
+ ensureInArray(result, "test/misc");
+ ensureInArray(result, "test/<int>");
+ ensureInArray(result, "test/<int>/square");
+ }
+
+ template<> template<>
+ void HTTPNodeTestObject::test<9>()
+ {
+ // test introspection details
+
+ LLHTTPRegistrar::buildAllServices(mRoot);
+
+ const LLSD helloDetails = get("web/server/api/web/hello");
+
+ ensure_contains("hello description",
+ helloDetails["description"].asString(), "hello");
+ ensure_equals("method name", helloDetails["api"][0].asString(), std::string("GET"));
+ ensureMemberString("hello", helloDetails, "output", "\"hello\"");
+ ensure_contains("hello __file__",
+ helloDetails["__file__"].asString(), "llsdhttpserver.cpp");
+ ensure("hello line", helloDetails["__line__"].isInteger());
+
+
+ const LLSD echoDetails = get("web/server/api/web/echo");
+
+ ensure_contains("echo description",
+ echoDetails["description"].asString(), "echo");
+ ensure_equals("method name", echoDetails["api"][0].asString(), std::string("POST"));
+ ensureMemberString("echo", echoDetails, "input", "<any>");
+ ensureMemberString("echo", echoDetails, "output", "<the input>");
+ ensure_contains("echo __file__",
+ echoDetails["__file__"].asString(), "llsdhttpserver.cpp");
+ ensure("echo", echoDetails["__line__"].isInteger());
+ }
+}
diff --git a/indra/test/lliohttpserver_tut.cpp b/indra/test/lliohttpserver_tut.cpp
new file mode 100644
index 0000000000..1284a1fc0d
--- /dev/null
+++ b/indra/test/lliohttpserver_tut.cpp
@@ -0,0 +1,284 @@
+/**
+ * @file lliohttpserver_tut.cpp
+ * @date May 2006
+ * @brief HTTP server unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llbufferstream.h"
+#include "lliohttpserver.h"
+#include "llsdhttpserver.h"
+#include "llsdserialize.h"
+
+#include "llpipeutil.h"
+
+
+namespace tut
+{
+ class HTTPServiceTestData
+ {
+ public:
+ class DelayedEcho : public LLHTTPNode
+ {
+ HTTPServiceTestData* mTester;
+
+ public:
+ DelayedEcho(HTTPServiceTestData* tester) : mTester(tester) { }
+
+ void post(ResponsePtr response, const LLSD& context, const LLSD& input) const
+ {
+ ensure("response already set", mTester->mResponse == ResponsePtr(NULL));
+ mTester->mResponse = response;
+ mTester->mResult = input;
+ }
+ };
+
+ class WireHello : public LLIOPipe
+ {
+ protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+ {
+ if(!eos) return STATUS_BREAK;
+ LLSD sd = "yo!";
+ LLBufferStream ostr(channels, buffer.get());
+ ostr << LLSDXMLStreamer(sd);
+ return STATUS_DONE;
+ }
+ };
+
+ HTTPServiceTestData()
+ : mResponse(NULL)
+ {
+ LLHTTPStandardServices::useServices();
+ LLHTTPRegistrar::buildAllServices(mRoot);
+ mRoot.addNode("/delayed/echo", new DelayedEcho(this));
+ mRoot.addNode("/wire/hello", new LLHTTPNodeForPipe<WireHello>);
+ }
+
+ LLHTTPNode mRoot;
+ LLHTTPNode::ResponsePtr mResponse;
+ LLSD mResult;
+
+ void pumpPipe(LLPumpIO* pump, S32 iterations)
+ {
+ while(iterations > 0)
+ {
+ pump->pump();
+ pump->callback();
+ --iterations;
+ }
+ }
+
+ std::string makeRequest(
+ const std::string& name,
+ const std::string& httpRequest,
+ bool timeout = false)
+ {
+ LLPipeStringInjector* injector = new LLPipeStringInjector(httpRequest);
+ LLPipeStringExtractor* extractor = new LLPipeStringExtractor();
+
+ apr_pool_t* pool;
+ apr_pool_create(&pool, NULL);
+
+ LLPumpIO* pump;
+ pump = new LLPumpIO(pool);
+
+ LLPumpIO::chain_t chain;
+ LLSD context;
+
+ chain.push_back(LLIOPipe::ptr_t(injector));
+ LLCreateHTTPPipe(chain, mRoot);
+ chain.push_back(LLIOPipe::ptr_t(extractor));
+
+ pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
+
+ pumpPipe(pump, 10);
+ if(mResponse && (! timeout))
+ {
+ mResponse->result(mResult);
+ mResponse = NULL;
+ }
+ pumpPipe(pump, 10);
+
+ std::string httpResult = extractor->string();
+
+ chain.clear();
+ delete pump;
+ apr_pool_destroy(pool);
+
+ if(mResponse && timeout)
+ {
+ mResponse->result(mResult);
+ mResponse = NULL;
+ }
+
+ return httpResult;
+ }
+
+ std::string httpGET(const std::string& uri,
+ bool timeout = false)
+ {
+ std::string httpRequest = "GET " + uri + " HTTP/1.0\r\n\r\n";
+ return makeRequest(uri, httpRequest, timeout);
+ }
+
+ std::string httpPOST(const std::string& uri,
+ const std::string& body,
+ bool timeout,
+ const std::string& evilExtra = "")
+ {
+ std::ostringstream httpRequest;
+ httpRequest << "POST " + uri + " HTTP/1.0\r\n";
+ httpRequest << "Content-Length: " << body.size() << "\r\n";
+ httpRequest << "\r\n";
+ httpRequest << body;
+ httpRequest << evilExtra;
+
+ return makeRequest(uri, httpRequest.str(), timeout);
+ }
+
+ std::string httpPOST(const std::string& uri,
+ const std::string& body,
+ const std::string& evilExtra = "")
+ {
+ bool timeout = false;
+ return httpPOST(uri, body, timeout, evilExtra);
+ }
+ };
+
+ typedef test_group<HTTPServiceTestData> HTTPServiceTestGroup;
+ typedef HTTPServiceTestGroup::object HTTPServiceTestObject;
+ HTTPServiceTestGroup httpServiceTestGroup("http service");
+
+ template<> template<>
+ void HTTPServiceTestObject::test<1>()
+ {
+ std::string result = httpGET("web/hello");
+
+ ensure_starts_with("web/hello status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("web/hello content type", result,
+ "Content-Type: application/xml\r\n");
+
+ ensure_contains("web/hello content length", result,
+ "Content-Length: 36\r\n");
+
+ ensure_contains("web/hello content", result,
+ "\r\n"
+ "<llsd><string>hello</string></llsd>"
+ );
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<2>()
+ {
+ // test various HTTP errors
+
+ std::string actual;
+
+ actual = httpGET("web/missing");
+ ensure_starts_with("web/missing 404", actual,
+ "HTTP/1.0 404 Not Found\r\n");
+
+ actual = httpGET("web/echo");
+ ensure_starts_with("web/echo 405", actual,
+ "HTTP/1.0 405 Method Not Allowed\r\n");
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<3>()
+ {
+ // test POST & content-length handling
+
+ std::string result;
+
+ result = httpPOST("web/echo",
+ "<llsd><integer>42</integer></llsd>");
+
+ ensure_starts_with("web/echo status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("web/echo content type", result,
+ "Content-Type: application/xml\r\n");
+
+ ensure_contains("web/echo content length", result,
+ "Content-Length: 35\r\n");
+
+ ensure_contains("web/hello content", result,
+ "\r\n"
+ "<llsd><integer>42</integer></llsd>"
+ );
+
+/* TO DO: this test doesn't pass!!
+
+ result = httpPOST("web/echo",
+ "<llsd><string>evil</string></llsd>",
+ "really! evil!!!");
+
+ ensure_equals("web/echo evil result", result,
+ "HTTP/1.0 200 OK\r\n"
+ "Content-Length: 34\r\n"
+ "\r\n"
+ "<llsd><string>evil</string></llsd>"
+ );
+*/
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<4>()
+ {
+ // test calling things based on pipes
+
+ std::string result;
+
+ result = httpGET("wire/hello");
+
+ ensure_contains("wire/hello", result, "yo!");
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<5>()
+ {
+ // test timeout before async response
+ std::string result;
+
+ bool timeout = true;
+ result = httpPOST("delayed/echo",
+ "<llsd><string>agent99</string></llsd>", timeout);
+
+ ensure_equals("timeout delayed/echo status", result, std::string(""));
+ }
+
+ template<> template<>
+ void HTTPServiceTestObject::test<6>()
+ {
+ // test delayed service
+ std::string result;
+
+ result = httpPOST("delayed/echo",
+ "<llsd><string>agent99</string></llsd>");
+
+ ensure_starts_with("delayed/echo status", result,
+ "HTTP/1.0 200 OK\r\n");
+
+ ensure_contains("delayed/echo content", result,
+ "\r\n"
+ "<llsd><string>agent99</string></llsd>"
+ );
+ }
+
+ /* TO DO:
+ test generation of not found and method not allowed errors
+ */
+}
diff --git a/indra/test/llpipeutil.cpp b/indra/test/llpipeutil.cpp
new file mode 100644
index 0000000000..e4389aaa33
--- /dev/null
+++ b/indra/test/llpipeutil.cpp
@@ -0,0 +1,139 @@
+/**
+ * @file llpipeutil.cpp
+ * @date 2006-05-18
+ * @brief Utility pipe fittings for injecting and extracting strings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llpipeutil.h"
+
+#include <stdlib.h>
+
+#include "llbufferstream.h"
+#include "llframetimer.h"
+#include "llpumpio.h"
+#include "llrand.h"
+#include "lltimer.h"
+
+F32 pump_loop(LLPumpIO* pump, F32 seconds)
+{
+ LLTimer timer;
+ timer.setTimerExpirySec(seconds);
+ while(!timer.hasExpired())
+ {
+ LLFrameTimer::updateFrameTime();
+ pump->pump();
+ pump->callback();
+ }
+ return timer.getElapsedTimeF32();
+}
+
+//virtual
+LLIOPipe::EStatus LLPipeStringInjector::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ buffer->append(channels.out(), (U8*) mString.data(), mString.size());
+ eos = true;
+ return STATUS_DONE;
+}
+
+
+LLIOPipe::EStatus LLPipeStringExtractor::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ LLBufferStream istr(channels, buffer.get());
+ std::ostringstream ostr;
+ while (istr.good())
+ {
+ char buf[1024];
+ istr.read(buf, sizeof(buf));
+ ostr.write(buf, istr.gcount());
+ }
+ mString = ostr.str();
+ mDone = true;
+
+ return STATUS_DONE;
+}
+
+
+// virtual
+LLIOPipe::EStatus LLIOFuzz::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ while(mByteCount)
+ {
+ std::vector<U8> data;
+ data.reserve(10000);
+ int size = llmin(10000, mByteCount);
+ std::generate_n(
+ std::back_insert_iterator< std::vector<U8> >(data),
+ size,
+ rand);
+ buffer->append(channels.out(), &data[0], size);
+ mByteCount -= size;
+ }
+ return STATUS_OK;
+}
+
+struct random_ascii_generator
+{
+ random_ascii_generator() {}
+ U8 operator()()
+ {
+ int rv = rand();
+ rv %= (127 - 32);
+ rv += 32;
+ return rv;
+ }
+};
+
+// virtual
+LLIOPipe::EStatus LLIOASCIIFuzz::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ while(mByteCount)
+ {
+ std::vector<U8> data;
+ data.reserve(10000);
+ int size = llmin(10000, mByteCount);
+ std::generate_n(
+ std::back_insert_iterator< std::vector<U8> >(data),
+ size,
+ random_ascii_generator());
+ buffer->append(channels.out(), &data[0], size);
+ mByteCount -= size;
+ }
+ return STATUS_OK;
+}
+
+// virtual
+LLIOPipe::EStatus LLIONull::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ return STATUS_OK;
+}
diff --git a/indra/test/llpipeutil.h b/indra/test/llpipeutil.h
new file mode 100644
index 0000000000..e63f49f02f
--- /dev/null
+++ b/indra/test/llpipeutil.h
@@ -0,0 +1,125 @@
+/**
+ * @file llpipeutil.h
+ * @date 2006-05-18
+ * @brief Utility pipe fittings for injecting and extracting strings
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLPIPEUTIL_H
+#define LL_LLPIPEUTIL_H
+
+#include "lliopipe.h"
+
+
+/**
+ * @brief Simple function which pumps for the specified time.
+ */
+F32 pump_loop(LLPumpIO* pump, F32 seconds);
+
+/**
+ * @brief Simple class which writes a string and then marks the stream
+ * as done.
+ */
+class LLPipeStringInjector : public LLIOPipe
+{
+public:
+ LLPipeStringInjector(const std::string& string)
+ : mString(string)
+ { }
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ std::string mString;
+};
+
+
+class LLPipeStringExtractor : public LLIOPipe
+{
+public:
+ LLPipeStringExtractor() : mDone(false) { }
+
+ bool done() { return mDone; }
+ std::string string() { return mString; }
+
+protected:
+ // LLIOPipe API implementation.
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ LLIOPipe::buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ bool mDone;
+ std::string mString;
+};
+
+/**
+ * @brief Generate a specified number of bytes of random data
+ */
+class LLIOFuzz : public LLIOPipe
+{
+public:
+ LLIOFuzz(int byte_count) : mByteCount(byte_count) {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ int mByteCount;
+};
+
+/**
+ * @brief Generate some ascii fuz
+ */
+class LLIOASCIIFuzz : public LLIOPipe
+{
+public:
+ LLIOASCIIFuzz(int byte_count) : mByteCount(byte_count) {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+private:
+ int mByteCount;
+};
+
+
+/**
+ * @brief Pipe that does nothing except return STATUS_OK
+ */
+class LLIONull : public LLIOPipe
+{
+public:
+ LLIONull() {}
+
+protected:
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+};
+
+#endif // LL_LLPIPEUTIL_H
diff --git a/indra/test/llsd_new_tut.cpp b/indra/test/llsd_new_tut.cpp
new file mode 100644
index 0000000000..e99da7195a
--- /dev/null
+++ b/indra/test/llsd_new_tut.cpp
@@ -0,0 +1,821 @@
+/**
+ * @file llsd_new_tut.cpp
+ * @date February 2006
+ * @brief LLSD unit tests
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <math.h>
+#include <tut/tut.h>
+#include "lltut.h"
+
+#include "llsd.h"
+
+namespace tut
+{
+ template<class T>
+ class SDTraits
+ {
+ protected:
+ typedef T (LLSD::*Getter)() const;
+
+ LLSD::Type type;
+ Getter getter;
+
+ public:
+ SDTraits();
+
+ T get(const LLSD& actual)
+ {
+ return (actual.*getter)();
+ }
+
+ bool checkType(const LLSD& actual)
+ {
+ return actual.type() == type;
+ }
+ };
+
+ template<>
+ SDTraits<LLSD::Boolean>::SDTraits()
+ : type(LLSD::TypeBoolean), getter(&LLSD::asBoolean)
+ { }
+
+ template<>
+ SDTraits<LLSD::Integer>::SDTraits()
+ : type(LLSD::TypeInteger), getter(&LLSD::asInteger)
+ { }
+
+ template<>
+ SDTraits<LLSD::Real>::SDTraits()
+ : type(LLSD::TypeReal), getter(&LLSD::asReal)
+ { }
+
+ template<>
+ SDTraits<LLSD::UUID>::SDTraits()
+ : type(LLSD::TypeUUID), getter(&LLSD::asUUID)
+ { }
+
+ template<>
+ SDTraits<LLSD::String>::SDTraits()
+ : type(LLSD::TypeString), getter(&LLSD::asString)
+ { }
+
+ template<>
+ class SDTraits<LLString> : public SDTraits<LLSD::String>
+ { };
+
+ template<>
+ class SDTraits<const char*> : public SDTraits<LLSD::String>
+ { };
+
+ template<>
+ SDTraits<LLSD::Date>::SDTraits()
+ : type(LLSD::TypeDate), getter(&LLSD::asDate)
+ { }
+
+ template<>
+ SDTraits<LLSD::URI>::SDTraits()
+ : type(LLSD::TypeURI), getter(&LLSD::asURI)
+ { }
+
+ template<>
+ SDTraits<LLSD::Binary>::SDTraits()
+ : type(LLSD::TypeBinary), getter(&LLSD::asBinary)
+ { }
+
+ class SDCleanupCheck
+ {
+ private:
+ U32 mOutstandingAtStart;
+ public:
+ SDCleanupCheck() : mOutstandingAtStart(LLSD::outstandingCount()) { }
+ ~SDCleanupCheck()
+ {
+ ensure_equals("SDCleanupCheck",
+ LLSD::outstandingCount(), mOutstandingAtStart);
+ }
+ };
+
+ class SDAllocationCheck : public SDCleanupCheck
+ {
+ private:
+ std::string mMessage;
+ U32 mExpectedAllocations;
+ U32 mAllocationAtStart;
+ public:
+ SDAllocationCheck(const std::string& message, int expectedAllocations)
+ : mMessage(message),
+ mExpectedAllocations(expectedAllocations),
+ mAllocationAtStart(LLSD::allocationCount())
+ { }
+ ~SDAllocationCheck()
+ {
+ ensure_equals(mMessage + " SDAllocationCheck",
+ LLSD::allocationCount() - mAllocationAtStart,
+ mExpectedAllocations);
+ }
+ };
+
+ struct SDTestData {
+ template<class T>
+ static void ensureTypeAndValue(const char* msg, const LLSD& actual,
+ T expectedValue)
+ {
+ SDTraits<T> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+ };
+
+ typedef test_group<SDTestData> SDTestGroup;
+ typedef SDTestGroup::object SDTestObject;
+
+ SDTestGroup sdTestGroup("LLSD(new)");
+
+ template<> template<>
+ void SDTestObject::test<1>()
+ // construction and test of undefined
+ {
+ SDCleanupCheck check;
+
+ LLSD u;
+ ensure("is undefined", u.isUndefined());
+ }
+
+ template<> template<>
+ void SDTestObject::test<2>()
+ // setting and fetching scalar types
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ v = true; ensureTypeAndValue("set true", v, true);
+ v = false; ensureTypeAndValue("set false", v, false);
+ v = true; ensureTypeAndValue("set true again", v, true);
+
+ v = 42; ensureTypeAndValue("set to 42", v, 42);
+ v = 0; ensureTypeAndValue("set to zero", v, 0);
+ v = -12345; ensureTypeAndValue("set to neg", v, -12345);
+ v = 2000000000; ensureTypeAndValue("set to big", v, 2000000000);
+
+ v = 3.14159265359;
+ ensureTypeAndValue("set to pi", v, 3.14159265359);
+ ensure_not_equals("isn't float", v.asReal(),
+ (float)3.14159265359);
+ v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256);
+
+ LLUUID nullUUID;
+ LLUUID newUUID;
+ newUUID.generate();
+
+ v = nullUUID; ensureTypeAndValue("set to null UUID", v, nullUUID);
+ v = newUUID; ensureTypeAndValue("set to new UUID", v, newUUID);
+ v = nullUUID; ensureTypeAndValue("set to null again", v, nullUUID);
+
+ // strings must be tested with three (!) types of string objects
+ std::string s = "now is the time";
+ LLString ls = "for all good zorks";
+ const char* cs = "to come to the air of their planet";
+
+ v = s; ensureTypeAndValue("set to std::string", v, s);
+ v = ls; ensureTypeAndValue("set to LLString", v, ls);
+ v = cs; ensureTypeAndValue("set to const char*", v, cs);
+
+ LLDate epoch;
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+
+ v = epoch; ensureTypeAndValue("set to epoch", v, epoch);
+ v = aDay; ensureTypeAndValue("set to a day", v, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+
+ v = path; ensureTypeAndValue("set to a uri", v, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+
+ v = data; ensureTypeAndValue("set to data", v, data);
+
+ v.clear();
+ ensure("reset to undefined", v.type() == LLSD::TypeUndefined);
+ }
+
+ template<> template<>
+ void SDTestObject::test<3>()
+ // construction via scalar values
+ // tests both constructor and initialize forms
+ {
+ SDCleanupCheck check;
+
+ LLSD b1(true); ensureTypeAndValue("construct boolean", b1, true);
+ LLSD b2 = true; ensureTypeAndValue("initialize boolean", b2, true);
+ LLSD i1(42); ensureTypeAndValue("construct int", i1, 42);
+ LLSD i2 =42; ensureTypeAndValue("initialize int", i2, 42);
+ LLSD d1(1.2); ensureTypeAndValue("construct double", d1, 1.2);
+ LLSD d2 = 1.2; ensureTypeAndValue("initialize double", d2, 1.2);
+
+ LLUUID newUUID;
+ newUUID.generate();
+ LLSD u1(newUUID);
+ ensureTypeAndValue("construct UUID", u1, newUUID);
+ LLSD u2 = newUUID;
+ ensureTypeAndValue("initialize UUID", u2, newUUID);
+
+ LLSD ss1(std::string("abc"));
+ ensureTypeAndValue("construct std::string", ss1, "abc");
+ LLSD ss2 = std::string("abc");
+ ensureTypeAndValue("initialize std::string",ss2, "abc");
+ LLSD sl1(LLString("def"));
+ ensureTypeAndValue("construct LLString", sl1, "def");
+ LLSD sl2 = LLString("def");
+ ensureTypeAndValue("initialize LLString", sl2, "def");
+ LLSD sc1("ghi");
+ ensureTypeAndValue("construct const char*", sc1, "ghi");
+ LLSD sc2 = "ghi";
+ ensureTypeAndValue("initialize const char*",sc2, "ghi");
+
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+ LLSD t1(aDay); ensureTypeAndValue("construct LLDate", t1, aDay);
+ LLSD t2 = aDay; ensureTypeAndValue("initialize LLDate", t2, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLSD p1(path); ensureTypeAndValue("construct LLURI", p1, path);
+ LLSD p2 = path; ensureTypeAndValue("initialize LLURI", p2, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+ LLSD x1(data); ensureTypeAndValue("construct vector<U8>", x1, data);
+ LLSD x2 = data; ensureTypeAndValue("initialize vector<U8>", x2, data);
+ }
+
+ void checkConversions(const char* msg, const LLSD& v,
+ LLSD::Boolean eBoolean, LLSD::Integer eInteger,
+ LLSD::Real eReal, const LLSD::String& eString)
+ {
+ std::string s(msg);
+
+ ensure_equals(s+" to bool", v.asBoolean(), eBoolean);
+ ensure_equals(s+" to int", v.asInteger(), eInteger);
+ if (eReal == eReal)
+ {
+ ensure_equals(s+" to real", v.asReal(), eReal);
+ ensure_equals(s+" to string", v.asString(), eString);
+ }
+ else
+ {
+// TODO: Fix on windows....
+#ifndef LL_WINDOWS
+# if !defined(fpclassify) && __GNUC__ >= 3
+# define FPCLASSIFY_NAMESPACE std::
+# else
+# define FPCLASSIFY_NAMESPACE
+# endif
+ int left = FPCLASSIFY_NAMESPACE fpclassify(v.asReal());
+ int right = FPCLASSIFY_NAMESPACE fpclassify(eReal);
+
+ ensure_equals(s+" to real", left, right);
+ ensure_equals(s+" to string", v.asString(), eString);
+#endif
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<4>()
+ // conversion between undefined and basic scalar types:
+ // boolean, integer, real and string
+ {
+ SDCleanupCheck check;
+
+ LLSD v; checkConversions("untitled", v, false, 0, 0.0, "");
+
+ v = false; checkConversions("false", v, false, 0, 0.0, "");
+ v = true; checkConversions("true", v, true, 1, 1.0, "true");
+
+ v = 0; checkConversions("zero", v, false, 0, 0.0, "0");
+ v = 1; checkConversions("one", v, true, 1, 1.0, "1");
+ v = -33; checkConversions("neg33", v, true, -33, -33.0, "-33");
+
+ v = 0.0; checkConversions("0.0", v, false, 0, 0.0, "0");
+ v = 0.5; checkConversions("point5", v, true, 0, 0.5, "0.5");
+ v = 0.9; checkConversions("point9", v, true, 0, 0.9, "0.9");
+ v = -3.9; checkConversions("neg3dot9", v, true, -3, -3.9, "-3.9");
+ v = sqrt(-1.0); checkConversions("NaN", v, false, 0, sqrt(-1.0), "nan");
+
+ v = ""; checkConversions("empty", v, false, 0, 0.0, "");
+ v = "0"; checkConversions("digit0", v, true, 0, 0.0, "0");
+ v = "10"; checkConversions("digit10", v, true, 10, 10.0, "10");
+ v = "-2.345"; checkConversions("decdigits", v,
+ true, -2, -2.345, "-2.345");
+ v = "apple"; checkConversions("apple", v, true, 0, 0.0, "apple");
+ v = "33bob"; checkConversions("digialpha", v, true, 0, 0.0, "33bob");
+ v = " "; checkConversions("space", v, true, 0, 0.0, " ");
+ v = "\n"; checkConversions("newline", v, true, 0, 0.0, "\n");
+ }
+
+ template<class T>
+ void checkRoundTrip(const std::string& msg, const LLSD& actual,
+ const char* sExpected, T vExpected)
+ {
+ std::string str = actual.asString();
+
+ if (sExpected) {
+ ensure_equals(msg + " string", str, sExpected);
+ }
+
+ LLSD u(str);
+ SDTraits<T> traits;
+
+ ensure_equals(msg + " value", traits.get(u), vExpected);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<5>()
+ // conversion of String to and from UUID, Date and URI.
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ LLUUID nullUUID;
+ LLUUID someUUID;
+ someUUID.generate();
+
+ v = nullUUID; checkRoundTrip("null uuid", v,
+ "00000000-0000-0000-0000-000000000000", nullUUID);
+ v = someUUID; checkRoundTrip("random uuid", v, 0, someUUID);
+
+ LLDate epoch;
+ LLDate beta("2003-04-30T04:00:00Z");
+ LLDate oneOh("2003-06-23T04:00:00Z");
+
+ v = epoch; checkRoundTrip("epoch date", v, 0, epoch);
+ v = beta; checkRoundTrip("beta date", v,
+ "2003-04-30T04:00:00Z", beta);
+ v = oneOh; checkRoundTrip("1.0 date", v,
+ "2003-06-23T04:00:00Z", oneOh);
+
+ LLURI empty;
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLURI mail("mailto:zero.linden@secondlife.com");
+
+ v = empty; checkRoundTrip("empty URI", v, 0, empty);
+ v = path; checkRoundTrip("path URI", v,
+ "http://slurl.com/secondlife/Ambleside/57/104/26/",
+ path);
+ v = mail; checkRoundTrip("mail URI", v,
+ "mailto:zero.linden@secondlife.com", mail);
+ }
+
+ template<> template<>
+ void SDTestObject::test<6>()
+ // copy construction and assignment
+ // checking for shared values after constr. or assignment
+ // checking in both the same type and change of type case
+ {
+ SDCleanupCheck check;
+
+ {
+ LLSD v = 42;
+
+ LLSD w0(v);
+ ensureTypeAndValue("int constr.", w0, 42);
+
+ LLSD w1(v);
+ w1 = 13;
+ ensureTypeAndValue("int constr. change case 1", w1, 13);
+ ensureTypeAndValue("int constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = 7;
+ ensureTypeAndValue("int constr. change case 3", w2, 42);
+ ensureTypeAndValue("int constr. change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1(v);
+ w1 = "bob";
+ ensureTypeAndValue("string constr. change case 1", w1, "bob");
+ ensureTypeAndValue("string constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = "amy";
+ ensureTypeAndValue("string constr. change case 3", w2, 42);
+ ensureTypeAndValue("string constr. change case 4", v, "amy");
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w0;
+ w0 = v;
+ ensureTypeAndValue("int assign", w0, 42);
+
+ LLSD w1;
+ w1 = v;
+ w1 = 13;
+ ensureTypeAndValue("int assign change case 1", w1, 13);
+ ensureTypeAndValue("int assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = 7;
+ ensureTypeAndValue("int assign change case 3", w2, 42);
+ ensureTypeAndValue("int assign change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1;
+ w1 = v;
+ w1 = "bob";
+ ensureTypeAndValue("string assign change case 1", w1, "bob");
+ ensureTypeAndValue("string assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = "amy";
+ ensureTypeAndValue("string assign change case 3", w2, 42);
+ ensureTypeAndValue("string assign change case 4", v, "amy");
+ }
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<7>()
+ // Test assignment and casting to various scalar types. These
+ // assignments should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b = false;
+ b = v; ensure_equals("assign to bool", b, true);
+ b = (bool)v; ensure_equals("cast to bool", b, true);
+
+ int i = 99;
+ i = v; ensure_equals("assign to int", i, 42);
+ i = (int)v; ensure_equals("cast to int", i, 42);
+
+ double d = 3.14159;
+ d = v; ensure_equals("assign to double", d, 42.375);
+ d = (double)v; ensure_equals("cast to double", d, 42.375);
+
+ std::string s = "yo";
+// SAD s = v; ensure_equals("assign to string", s, " 42.375");
+ s = (std::string)v; ensure_equals("cast to string", s, " 42.375");
+
+ LLString t = "yo";
+// SAD t = v; ensure_equals("assign to LLString", t, " 42.375");
+ t = (LLString)v; ensure_equals("cast to LLString", t, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID u;
+ u = v;
+ ensure_equals("assign to LLUUID", u, LLUUID(uuidStr));
+// SAD u = (LLUUID)v;
+// ensure_equals("cast to LLUUID", u, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date;
+ date = v;
+ ensure_equals("assign to LLDate", date.asString(), dateStr);
+// SAD date = (LLDate)v;
+// ensure_equals("cast to LLDate", date.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri;
+ uri = v;
+ ensure_equals("assign to LLURI", uri.asString(), uriStr);
+// SAD uri = (LLURI)v;
+// ensure_equals("cast to LLURI", uri.asString(), uriStr);
+ }
+
+ template<> template<>
+ void SDTestObject::test<8>()
+ // Test construction of various scalar types from LLSD.
+ // Test both construction and initialization forms.
+ // These should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b1(v); ensure_equals("contruct bool", b1, true);
+ bool b2 = v; ensure_equals("initialize bool", b2, true);
+
+ int i1(v); ensure_equals("contruct int", i1, 42);
+ int i2 = v; ensure_equals("initialize int", i2, 42);
+
+ double d1(v); ensure_equals("contruct double", d1, 42.375);
+ double d2 = v; ensure_equals("initialize double", d2, 42.375);
+
+ std::string s1(v);
+ std::string s2 = v;
+ ensure_equals("contruct string", s1, " 42.375");
+ ensure_equals("initialize string", s2, " 42.375");
+
+ LLString t1(v);
+ LLString t2 = v.asString(); // SAD
+ ensure_equals("contruct LLString", t1, " 42.375");
+ ensure_equals("initialize LLString", t2, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID uuid1(v.asUUID()); // SAD
+ LLUUID uuid2 = v;
+ ensure_equals("contruct LLUUID", uuid1, LLUUID(uuidStr));
+ ensure_equals("initialize LLUUID", uuid2, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date1(v.asDate()); // SAD
+ LLDate date2 = v;
+ ensure_equals("contruct LLDate", date1.asString(), dateStr);
+ ensure_equals("initialize LLDate", date2.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri1(v.asURI()); // SAD
+ LLURI uri2 = v;
+ ensure_equals("contruct LLURI", uri1.asString(), uriStr);
+ ensure_equals("initialize LLURI", uri2.asString(), uriStr);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<9>()
+ // test to make sure v is interpreted as a bool in a various
+ // scenarios.
+ {
+ SDCleanupCheck check;
+
+ LLSD v = "0";
+ // magic value that is interpreted as boolean true, but integer false!
+
+ ensure_equals("trinary operator bool", (v ? true : false), true);
+ ensure_equals("convert to int, then bool",
+ ((int)v ? true : false), false);
+
+ if(v)
+ {
+ ensure("if converted to bool", true);
+ }
+ else
+ {
+ fail("bool did not convert to a bool in if statement.");
+ }
+
+ if(!v)
+ {
+ fail("bool did not convert to a bool in negated if statement.");
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<10>()
+ // map operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure("undefined has no members", !v.has("amy"));
+ ensure("undefined get() is undefined", v.get("bob").isUndefined());
+
+ v = LLSD::emptyMap();
+ ensure("empty map is a map", v.isMap());
+ ensure("empty map has no members", !v.has("cam"));
+ ensure("empty map get() is undefined", v.get("don").isUndefined());
+
+ v.clear();
+ v.insert("eli", 43);
+ ensure("insert converts to map", v.isMap());
+ ensure("inserted key is present", v.has("eli"));
+ ensureTypeAndValue("inserted value", v.get("eli"), 43);
+
+ v.insert("fra", false);
+ ensure("first key still present", v.has("eli"));
+ ensure("second key is present", v.has("fra"));
+ ensureTypeAndValue("first value", v.get("eli"), 43);
+ ensureTypeAndValue("second value", v.get("fra"), false);
+
+ v.erase("eli");
+ ensure("first key now gone", !v.has("eli"));
+ ensure("second key still present", v.has("fra"));
+ ensure("first value gone", v.get("eli").isUndefined());
+ ensureTypeAndValue("second value sill there", v.get("fra"), false);
+
+ v.erase("fra");
+ ensure("second key now gone", !v.has("fra"));
+ ensure("second value gone", v.get("fra").isUndefined());
+
+ v["gil"] = (std::string)"good morning";
+ ensure("third key present", v.has("gil"));
+ ensureTypeAndValue("third key value", v.get("gil"), "good morning");
+
+ const LLSD& cv = v; // FIX ME IF POSSIBLE
+ ensure("missing key", cv["ham"].isUndefined());
+ ensure("key not present", !v.has("ham"));
+
+ LLSD w = 43;
+ const LLSD& cw = w; // FIX ME IF POSSIBLE
+ int i = cw["ian"];
+ ensureTypeAndValue("other missing value", i, 0);
+ ensure("other missing key", !w.has("ian"));
+ ensure("no conversion", w.isInteger());
+
+ LLSD x;
+ x = v;
+ ensure("copy map type", x.isMap());
+ ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning");
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<11>()
+ // array operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure_equals("undefined has no size", v.size(), 0);
+ ensure("undefined get() is undefined", v.get(0).isUndefined());
+
+ v = LLSD::emptyArray();
+ ensure("empty array is an array", v.isArray());
+ ensure_equals("empty array has no size", v.size(), 0);
+ ensure("empty map get() is undefined", v.get(0).isUndefined());
+
+ v.clear();
+ v.append(88);
+ v.append("noodle");
+ v.append(true);
+ ensure_equals("appened array size", v.size(), 3);
+ ensure("append array is an array", v.isArray());
+ ensureTypeAndValue("append 0", v[0], 88);
+ ensureTypeAndValue("append 1", v[1], "noodle");
+ ensureTypeAndValue("append 2", v[2], true);
+
+ v.insert(0, 77);
+ v.insert(2, "soba");
+ v.insert(4, false);
+ ensure_equals("inserted array size", v.size(), 6);
+ ensureTypeAndValue("post insert 0", v[0], 77);
+ ensureTypeAndValue("post insert 1", v[1], 88);
+ ensureTypeAndValue("post insert 2", v[2], "soba");
+ ensureTypeAndValue("post insert 3", v[3], "noodle");
+ ensureTypeAndValue("post insert 4", v[4], false);
+ ensureTypeAndValue("post insert 5", v[5], true);
+
+ ensureTypeAndValue("get 1", v.get(1), 88);
+ v.set(1, "hot");
+ ensureTypeAndValue("set 1", v.get(1), "hot");
+
+ v.erase(3);
+ ensure_equals("post erase array size", v.size(), 5);
+ ensureTypeAndValue("post erase 0", v[0], 77);
+ ensureTypeAndValue("post erase 1", v[1], "hot");
+ ensureTypeAndValue("post erase 2", v[2], "soba");
+ ensureTypeAndValue("post erase 3", v[3], false);
+ ensureTypeAndValue("post erase 4", v[4], true);
+
+ v.append(34);
+ ensure_equals("size after append", v.size(), 6);
+ ensureTypeAndValue("post append 5", v[5], 34);
+
+ LLSD w;
+ w = v;
+ ensure("copy array type", w.isArray());
+ ensure_equals("copy array size", w.size(), 6);
+ ensureTypeAndValue("copy array 0", w[0], 77);
+ ensureTypeAndValue("copy array 1", w[1], "hot");
+ ensureTypeAndValue("copy array 2", w[2], "soba");
+ ensureTypeAndValue("copy array 3", w[3], false);
+ ensureTypeAndValue("copy array 4", w[4], true);
+ ensureTypeAndValue("copy array 5", w[5], 34);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<12>()
+ // no sharing
+ {
+ SDCleanupCheck check;
+
+ LLSD a = 99;
+ LLSD b = a;
+ a = 34;
+ ensureTypeAndValue("top level original changed", a, 34);
+ ensureTypeAndValue("top level copy unaltered", b, 99);
+ b = a;
+ b = 66;
+ ensureTypeAndValue("top level original unaltered", a, 34);
+ ensureTypeAndValue("top level copy changed", b, 66);
+
+ a[0] = "uno";
+ a[1] = 99;
+ a[2] = 1.414;
+ b = a;
+ a[1] = 34;
+ ensureTypeAndValue("array member original changed", a[1], 34);
+ ensureTypeAndValue("array member copy unaltered", b[1], 99);
+ b = a;
+ b[1] = 66;
+ ensureTypeAndValue("array member original unaltered", a[1], 34);
+ ensureTypeAndValue("array member copy changed", b[1], 66);
+
+ a["alpha"] = "uno";
+ a["beta"] = 99;
+ a["gamma"] = 1.414;
+ b = a;
+ a["beta"] = 34;
+ ensureTypeAndValue("map member original changed", a["beta"], 34);
+ ensureTypeAndValue("map member copy unaltered", b["beta"], 99);
+ b = a;
+ b["beta"] = 66;
+ ensureTypeAndValue("map member original unaltered", a["beta"], 34);
+ ensureTypeAndValue("map member copy changed", b["beta"], 66);
+ }
+
+ template<> template<>
+ void SDTestObject::test<13>()
+ // sharing implementation
+ {
+ SDCleanupCheck check;
+
+ {
+ SDAllocationCheck check("copy construct undefinded", 0);
+ LLSD v;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign undefined", 0);
+ LLSD v;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer value", 1);
+ LLSD v = 45;
+ v = 33;
+ v = 0;
+ }
+
+ {
+ SDAllocationCheck check("copy construct integer", 1);
+ LLSD v = 45;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer", 1);
+ LLSD v = 45;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("avoids extra clone", 2);
+ LLSD v = 45;
+ LLSD w = v;
+ w = "nice day";
+ }
+ }
+
+ /* TO DO:
+ conversion of undefined to UUID, Date, URI and Binary
+ conversion of undefined to map and array
+ test map operations
+ test array operations
+ test array extension
+
+ test copying and assign maps and arrays (clone)
+ test iteration over map
+ test iteration over array
+ test iteration over scalar
+
+ test empty map and empty array are indeed shared
+ test serializations
+ */
+}
+
diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp
new file mode 100644
index 0000000000..96aad3da58
--- /dev/null
+++ b/indra/test/lltut.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file lltut.cpp
+ * @author Mark Lentczner
+ * @date 5/16/06
+ * @brief MacTester
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "lltut.h"
+#include "llsd.h"
+
+namespace tut
+{
+ template<>
+ void ensure_equals(const char* msg, const LLDate& actual,
+ const LLDate& expected)
+ {
+ ensure_equals(msg,
+ actual.secondsSinceEpoch(), expected.secondsSinceEpoch());
+ }
+
+ template<>
+ void ensure_equals(const char* msg, const LLURI& actual,
+ const LLURI& expected)
+ {
+ ensure_equals(msg,
+ actual.asString(), expected.asString());
+ }
+
+ template<>
+ void ensure_equals(const char* msg,
+ const std::vector<U8>& actual, const std::vector<U8>& expected)
+ {
+ std::string s(msg);
+
+ ensure_equals(s + " size", actual.size(), expected.size());
+
+ std::vector<U8>::const_iterator i, j;
+ int k;
+ for (i = actual.begin(), j = expected.begin(), k = 0;
+ i != actual.end();
+ ++i, ++j, ++k)
+ {
+ ensure_equals(s + " field", *i, *j);
+ }
+ }
+
+ template<>
+ void ensure_equals(const char* m, const LLSD& actual,
+ const LLSD& expected)
+ {
+ const std::string& msg = m;
+
+ ensure_equals(msg + " type", actual.type(), expected.type());
+ switch (actual.type())
+ {
+ case LLSD::TypeUndefined:
+ return;
+
+ case LLSD::TypeBoolean:
+ ensure_equals(msg + " boolean", actual.asBoolean(), expected.asBoolean());
+ return;
+
+ case LLSD::TypeInteger:
+ ensure_equals(msg + " integer", actual.asInteger(), expected.asInteger());
+ return;
+
+ case LLSD::TypeReal:
+ ensure_equals(msg + " real", actual.asReal(), expected.asReal());
+ return;
+
+ case LLSD::TypeString:
+ ensure_equals(msg + " string", actual.asString(), expected.asString());
+ return;
+
+ case LLSD::TypeUUID:
+ ensure_equals(msg + " uuid", actual.asUUID(), expected.asUUID());
+ return;
+
+ case LLSD::TypeDate:
+ ensure_equals(msg + " date", actual.asDate(), expected.asDate());
+ return;
+
+ case LLSD::TypeURI:
+ ensure_equals(msg + " uri", actual.asURI(), expected.asURI());
+ return;
+
+ case LLSD::TypeBinary:
+ ensure_equals(msg + " binary", actual.asBinary(), expected.asBinary());
+ return;
+
+ case LLSD::TypeMap:
+ {
+ ensure_equals(msg + " map size", actual.size(), expected.size());
+
+ LLSD::map_const_iterator actual_iter = actual.beginMap();
+ LLSD::map_const_iterator expected_iter = expected.beginMap();
+
+ while(actual_iter != actual.endMap())
+ {
+ ensure_equals(msg + " map keys",
+ actual_iter->first, expected_iter->first);
+ ensure_equals(msg + "[" + actual_iter->first + "]",
+ actual_iter->second, expected_iter->second);
+ ++actual_iter;
+ ++expected_iter;
+ }
+ return;
+ }
+ case LLSD::TypeArray:
+ {
+ ensure_equals(msg + " array size", actual.size(), expected.size());
+
+ for(int i = 0; i < actual.size(); ++i)
+ {
+ ensure_equals(msg + llformat("[%d]", i),
+ actual[i], expected[i]);
+ }
+ return;
+ }
+ }
+ }
+
+ void ensure_starts_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedStart)
+ {
+ if( actual.find(expectedStart, 0) != 0 )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected to find " << expectedStart
+ << " at start of actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+
+ void ensure_contains(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString)
+ {
+ if( actual.find(expectedSubString, 0) == std::string::npos )
+ {
+ std::stringstream ss;
+ ss << msg << ": " << "expected to find " << expectedSubString
+ << " in actual " << actual;
+ throw failure(ss.str().c_str());
+ }
+ }
+}
diff --git a/indra/test/lltut.h b/indra/test/lltut.h
new file mode 100644
index 0000000000..c750a99b8d
--- /dev/null
+++ b/indra/test/lltut.h
@@ -0,0 +1,76 @@
+/**
+ * @file lltut.h
+ * @author Phoenix
+ * @date 2005-09-26
+ * @brief helper tut methods
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * THOROUGH_DESCRIPTION
+ *
+ */
+
+#ifndef LL_LLTUT_H
+#define LL_LLTUT_H
+
+#include <tut/tut.h>
+
+#include "lldate.h"
+#include "lluri.h"
+
+class LLSD;
+
+namespace tut
+{
+ template <class T,class Q>
+ void ensure_not_equals(const char* msg,const Q& actual,const T& expected)
+ {
+ if( expected == actual )
+ {
+ std::stringstream ss;
+ ss << (msg?msg:"") << (msg?": ":"") << "both equal " << expected;
+ throw tut::failure(ss.str().c_str());
+ }
+ }
+
+ template <class T,class Q>
+ void ensure_not_equals(const Q& actual,const T& expected)
+ {
+ ensure_not_equals(NULL, actual, expected);
+ }
+
+
+ template <class T,class Q>
+ void ensure_equals(const std::string& msg,
+ const Q& actual,const T& expected)
+ { ensure_equals(msg.c_str(), actual, expected); }
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLDate& actual, const LLDate& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLURI& actual, const LLURI& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const std::vector<U8>& actual, const std::vector<U8>& expected);
+
+ template<>
+ void ensure_equals(const char* msg,
+ const LLSD& actual, const LLSD& expected);
+
+ void ensure_starts_with(const std::string& msg,
+ const std::string& actual, const std::string& expectedStart);
+
+ void ensure_contains(const std::string& msg,
+ const std::string& actual, const std::string& expectedSubString);
+}
+
+
+#endif // LL_LLTUT_H
diff --git a/indra/test/lluserrelations_tut.cpp b/indra/test/lluserrelations_tut.cpp
new file mode 100644
index 0000000000..93fc29e6ee
--- /dev/null
+++ b/indra/test/lluserrelations_tut.cpp
@@ -0,0 +1,138 @@
+/**
+ * @file lluserrelations_tut.cpp
+ * @author Phoenix
+ * @date 2006-10-12
+ * @brief Unit tests for the LLRelationship class.
+ *
+ * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include <tut/tut.h>
+
+#include "linden_common.h"
+#include "lluserrelations.h"
+
+namespace tut
+{
+ struct user_relationship
+ {
+ LLRelationship mRelationship;
+ };
+ typedef test_group<user_relationship> user_relationship_t;
+ typedef user_relationship_t::object user_relationship_object_t;
+ tut::user_relationship_t tut_user_relationship("relationships");
+
+ template<> template<>
+ void user_relationship_object_t::test<1>()
+ {
+ // Test the default construction
+ ensure(
+ "No granted rights to",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "No granted rights from",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure("No online status",!mRelationship.isOnline());
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<2>()
+ {
+ // Test some granting
+ mRelationship.grantRights(
+ LLRelationship::GRANT_ONLINE_STATUS,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights to has online",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from does not have online",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights to does not have modify",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ ensure(
+ "Granted rights from has modify",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<3>()
+ {
+ // Test revoking
+ mRelationship.grantRights(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION,
+ LLRelationship::GRANT_ONLINE_STATUS);
+ ensure(
+ "Granted rights to has online and map",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION));
+ ensure(
+ "Granted rights from has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+
+ mRelationship.revokeRights(
+ LLRelationship::GRANT_MAP_LOCATION,
+ LLRelationship::GRANT_NONE);
+ ensure(
+ "Granted rights revoked map",
+ !mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MAP_LOCATION));
+ ensure(
+ "Granted rights revoked still has online",
+ mRelationship.isRightGrantedTo(
+ LLRelationship::GRANT_ONLINE_STATUS));
+
+ mRelationship.grantRights(
+ LLRelationship::GRANT_NONE,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights from still has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from has full grant",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS
+ | LLRelationship::GRANT_MODIFY_OBJECTS));
+ mRelationship.revokeRights(
+ LLRelationship::GRANT_NONE,
+ LLRelationship::GRANT_MODIFY_OBJECTS);
+ ensure(
+ "Granted rights from still has online",
+ mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_ONLINE_STATUS));
+ ensure(
+ "Granted rights from no longer modify",
+ !mRelationship.isRightGrantedFrom(
+ LLRelationship::GRANT_MODIFY_OBJECTS));
+ }
+
+ template<> template<>
+ void user_relationship_object_t::test<4>()
+ {
+ ensure("No online status", !mRelationship.isOnline());
+ mRelationship.online(true);
+ ensure("Online status", mRelationship.isOnline());
+ mRelationship.online(false);
+ ensure("No online status", !mRelationship.isOnline());
+ }
+
+/*
+ template<> template<>
+ void user_relationship_object_t::test<>()
+ {
+ }
+*/
+}
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
new file mode 100644
index 0000000000..f05af10110
--- /dev/null
+++ b/indra/test/test.cpp
@@ -0,0 +1,248 @@
+/**
+ * @file test.cpp
+ * @author Phoenix
+ * @date 2005-09-26
+ * @brief Entry point for the test app.
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+/**
+ *
+ * You can add tests by creating a new cpp file in this directory, and
+ * rebuilding. There are at most 50 tests per testgroup without a
+ * little bit of template parameter and makefile tweaking.
+ *
+ */
+
+#include "linden_common.h"
+#include "lltut.h"
+
+#include <apr-1/apr_pools.h>
+#include <apr-1/apr_getopt.h>
+
+// the CTYPE_WORKAROUND is needed for linux dev stations that don't
+// have the broken libc6 packages needed by our out-of-date static
+// libs (such as libcrypto and libcurl). -- Leviathan 20060113
+#ifdef CTYPE_WORKAROUND
+# include "ctype_workaround.h"
+#endif
+
+
+namespace tut
+{
+ test_runner_singleton runner;
+}
+
+class LLTestCallback : public tut::callback
+{
+public:
+ LLTestCallback(bool verbose_mode) :
+ mVerboseMode(verbose_mode),
+ mTotalTests(0),
+ mPassedTests(0),
+ mFailedTests(0)
+ {
+ }
+
+ void run_started()
+ {
+ //std::cout << "run_started" << std::endl;
+ }
+
+ void test_completed(const tut::test_result& tr)
+ {
+ ++mTotalTests;
+ std::ostringstream out;
+ out << "[" << tr.group << ", " << tr.test << "] ";
+ switch(tr.result)
+ {
+ case tut::test_result::ok:
+ ++mPassedTests;
+ out << "ok";
+ break;
+ case tut::test_result::fail:
+ ++mFailedTests;
+ out << "fail '" << tr.message << "'";
+ break;
+ case tut::test_result::ex:
+ ++mFailedTests;
+ out << "exception";
+ break;
+ case tut::test_result::warn:
+ ++mFailedTests;
+ out << "test destructor throw";
+ break;
+ case tut::test_result::term:
+ ++mFailedTests;
+ out << "abnormal termination";
+ break;
+ default:
+ ++mFailedTests;
+ out << "unknown";
+ }
+ if(mVerboseMode || (tr.result != tut::test_result::ok))
+ {
+ std::cout << out.str() << std::endl;
+ }
+ }
+
+ void run_completed()
+ {
+ std::cout << std::endl;
+ std::cout << "Total Tests: " << mTotalTests << std::endl;
+ std::cout << "Passed Tests : " << mPassedTests << std::endl;
+ if(mFailedTests > 0)
+ {
+ std::cout << "*********************************" << std::endl;
+ std::cout << "Failed Tests: " << mFailedTests << std::endl;
+ std::cout << "Please report or fix the problem." << std::endl;
+ std::cout << "*********************************" << std::endl;
+ exit(1);
+ }
+ }
+
+protected:
+ bool mVerboseMode;
+ S32 mTotalTests;
+ S32 mPassedTests;
+ S32 mFailedTests;
+};
+
+static const apr_getopt_option_t TEST_CL_OPTIONS[] =
+{
+ {"help", 'h', 0, "Print the help message."},
+ {"list", 'l', 0, "List available test groups."},
+ {"verbose", 'v', 0, "Verbose output."},
+ {"group", 'g', 1, "Run test group specified by option argument."},
+ {"wait", 'w', 0, "Wait for input before exit."},
+ {0, 0, 0, 0}
+};
+
+void stream_usage(std::ostream& s, const char* app)
+{
+ s << "Usage: " << app << " [OPTIONS]" << std::endl
+ << std::endl;
+
+ s << "This application runs the unit tests." << std::endl << std::endl;
+
+ s << "Options: " << std::endl;
+ const apr_getopt_option_t* option = &TEST_CL_OPTIONS[0];
+ while(option->name)
+ {
+ s << " ";
+ s << " -" << (char)option->optch << ", --" << option->name
+ << std::endl;
+ s << "\t" << option->description << std::endl << std::endl;
+ ++option;
+ }
+
+ s << "Examples:" << std::endl;
+ s << " " << app << " --verbose" << std::endl;
+ s << "\tRun all the tests and report all results." << std::endl;
+ s << " " << app << " --list" << std::endl;
+ s << "\tList all available test groups." << std::endl;
+ s << " " << app << " --group=uuid" << std::endl;
+ s << "\tRun the test group 'uuid'." << std::endl;
+}
+
+void stream_groups(std::ostream& s, const char* app)
+{
+ s << "Registered test groups:" << std::endl;
+ tut::groupnames gl = tut::runner.get().list_groups();
+ tut::groupnames::const_iterator it = gl.begin();
+ tut::groupnames::const_iterator end = gl.end();
+ for(; it != end; ++it)
+ {
+ s << " " << *(it) << std::endl;
+ }
+}
+
+int main(int argc, char **argv)
+{
+#ifdef CTYPE_WORKAROUND
+ ctype_workaround();
+#endif
+
+ apr_initialize();
+ apr_pool_t* pool = NULL;
+ if(APR_SUCCESS != apr_pool_create(&pool, NULL))
+ {
+ std::cerr << "Unable to initialize pool" << std::endl;
+ return 1;
+ }
+ apr_getopt_t* os = NULL;
+ if(APR_SUCCESS != apr_getopt_init(&os, pool, argc, argv))
+ {
+ std::cerr << "Unable to pool" << std::endl;
+ return 1;
+ }
+
+ // values used for controlling application
+ bool verbose_mode = false;
+ bool wait_at_exit = false;
+ std::string test_group;
+
+ // values use for options parsing
+ apr_status_t apr_err;
+ const char* opt_arg = NULL;
+ int opt_id = 0;
+ while(true)
+ {
+ apr_err = apr_getopt_long(os, TEST_CL_OPTIONS, &opt_id, &opt_arg);
+ if(APR_STATUS_IS_EOF(apr_err)) break;
+ if(apr_err)
+ {
+ char buf[255];
+ std::cerr << "Error parsing options: "
+ << apr_strerror(apr_err, buf, 255) << std::endl;
+ return 1;
+ }
+ switch (opt_id)
+ {
+ case 'g':
+ test_group.assign(opt_arg);
+ break;
+ case 'h':
+ stream_usage(std::cout, argv[0]);
+ return 0;
+ break;
+ case 'l':
+ stream_groups(std::cout, argv[0]);
+ return 0;
+ case 'v':
+ verbose_mode = true;
+ break;
+ case 'w':
+ wait_at_exit = true;
+ break;
+ default:
+ stream_usage(std::cerr, argv[0]);
+ return 1;
+ break;
+ }
+ }
+
+ // run the tests
+ LLTestCallback callback(verbose_mode);
+ tut::runner.get().set_callback(&callback);
+
+ if(test_group.empty())
+ {
+ tut::runner.get().run_tests();
+ }
+ else
+ {
+ tut::runner.get().run_tests(test_group);
+ }
+
+ if (wait_at_exit)
+ {
+ std::cerr << "Waiting for input before exiting..." << std::endl;
+ std::cin.get();
+ }
+
+ apr_terminate();
+ return 0;
+}
diff --git a/indra/win_crash_logger/StdAfx.cpp b/indra/win_crash_logger/StdAfx.cpp
new file mode 100644
index 0000000000..541c322d90
--- /dev/null
+++ b/indra/win_crash_logger/StdAfx.cpp
@@ -0,0 +1,16 @@
+/**
+ * @file StdAfx.cpp
+ * @brief windows crash logger source file for includes
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// stdafx.cpp : source file that includes just the standard includes
+// win_crash_logger.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/indra/win_crash_logger/StdAfx.h b/indra/win_crash_logger/StdAfx.h
new file mode 100644
index 0000000000..00407005e5
--- /dev/null
+++ b/indra/win_crash_logger/StdAfx.h
@@ -0,0 +1,40 @@
+/**
+ * @file StdAfx.h
+ * @brief standard system includes
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
+#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+
+// Windows Header Files:
+#include <windows.h>
+
+// C RunTime Header Files
+#include <stdlib.h>
+#include <malloc.h>
+#include <memory.h>
+#include <tchar.h>
+
+// Local Header Files
+
+// TODO: reference additional headers your program requires here
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_)
diff --git a/indra/win_crash_logger/ll_icon.ico b/indra/win_crash_logger/ll_icon.ico
new file mode 100644
index 0000000000..566346dfe3
--- /dev/null
+++ b/indra/win_crash_logger/ll_icon.ico
Binary files differ
diff --git a/indra/win_crash_logger/resource.h b/indra/win_crash_logger/resource.h
new file mode 100644
index 0000000000..19233df390
--- /dev/null
+++ b/indra/win_crash_logger/resource.h
@@ -0,0 +1,44 @@
+/**
+ * @file resource.h
+ * @brief Resources for windows crash logger
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by win_crash_logger.rc
+//
+#define IDC_MYICON 2
+#define IDD_REPORT 9
+#define IDD_WIN_CRASH_LOGGER_DIALOG 102
+#define IDD_ABOUTBOX 103
+#define IDS_APP_TITLE 103
+#define IDM_ABOUT 104
+#define IDM_EXIT 105
+#define IDS_HELLO 106
+#define IDI_WIN_CRASH_LOGGER 107
+#define IDI_SMALL 108
+#define IDC_WIN_CRASH_LOGGER 109
+#define IDR_MAINFRAME 128
+#define IDD_PROGRESS 129
+#define IDD_PREVREPORTBOX 130
+#define IDC_EDIT1 1000
+#define IDC_LOG 1004
+#define IDC_CHECK_AUTO 1006
+#define IDC_STATIC_HEADER 1007
+#define IDC_STATIC_WHATINFO 1008
+#define IDC_STATIC_MOTIVATION 1009
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 131
+#define _APS_NEXT_COMMAND_VALUE 32771
+#define _APS_NEXT_CONTROL_VALUE 1010
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
new file mode 100644
index 0000000000..e9198e8ff9
--- /dev/null
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -0,0 +1,915 @@
+/**
+ * @file win_crash_logger.cpp
+ * @brief Windows crash logger implementation
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+// win_crash_logger.cpp : Defines the entry point for the application.
+//
+
+#include "linden_common.h"
+#include "llcontrol.h"
+#include "stdafx.h"
+#include "resource.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <direct.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <wininet.h>
+
+#include "indra_constants.h" // CRASH_BEHAVIOR_ASK, CRASH_SETTING_NAME
+#include "llerror.h"
+#include "lltimer.h"
+#include "lldir.h"
+
+#include "llstring.h"
+#include "lldxhardware.h"
+
+LLControlGroup gCrashSettings; // saved at end of session
+
+// Constants
+#define MAX_LOADSTRING 100
+const char* const SETTINGS_FILE_HEADER = "version";
+const S32 SETTINGS_FILE_VERSION = 101;
+
+// Functions
+LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
+bool handle_button_click(WORD button_id);
+S32 load_crash_behavior_setting();
+bool save_crash_behavior_setting(S32 crash_behavior);
+void send_crash_report();
+void write_debug(const char *str);
+void write_debug(std::string& str);
+
+// Global Variables:
+HINSTANCE hInst= NULL; // current instance
+TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
+TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
+
+LLString gUserText; // User's description of the problem
+time_t gStartTime = 0;
+HWND gHwndReport = NULL; // Send/Don't Send dialog
+HWND gHwndProgress = NULL; // Progress window
+HCURSOR gCursorArrow = NULL;
+HCURSOR gCursorWait = NULL;
+BOOL gFirstDialog = TRUE; // Are we currently handling the Send/Don't Send dialog?
+BOOL gCrashInPreviousExec = FALSE;
+FILE *gDebugFile = NULL;
+LLString gUserserver;
+WCHAR gProductName[512];
+
+//
+// Implementation
+//
+
+// Include product name in the window caption.
+void ProcessCaption(HWND hWnd)
+{
+ TCHAR templateText[1024];
+ TCHAR finalText[2048];
+ GetWindowText(hWnd, templateText, sizeof(templateText));
+ swprintf(finalText, templateText, gProductName);
+ SetWindowText(hWnd, finalText);
+}
+
+
+// Include product name in the diaog item text.
+void ProcessDlgItemText(HWND hWnd, int nIDDlgItem)
+{
+ TCHAR templateText[1024];
+ TCHAR finalText[2048];
+ GetDlgItemText(hWnd, nIDDlgItem, templateText, sizeof(templateText));
+ swprintf(finalText, templateText, gProductName);
+ SetDlgItemText(hWnd, nIDDlgItem, finalText);
+}
+
+int APIENTRY WinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow)
+{
+ llinfos << "Starting crash reporter" << llendl;
+ // We assume that all the logs we're looking for reside on the current drive
+ gDirUtilp->initAppDirs("SecondLife");
+
+ // Default to the product name "Second Life" (this is overridden by the -name argument)
+ swprintf(gProductName, L"Second Life");
+
+ gCrashSettings.declareS32(CRASH_BEHAVIOR_SETTING, CRASH_BEHAVIOR_ASK, "Controls behavior when viewer crashes "
+ "(0 = ask before sending crash report, 1 = always send crash report, 2 = never send crash report)");
+
+ llinfos << "Loading crash behavior setting" << llendl;
+ S32 crash_behavior = load_crash_behavior_setting();
+
+ // In Win32, we need to generate argc and argv ourselves...
+ // Note: GetCommandLine() returns a potentially return a LPTSTR
+ // which can resolve to a LPWSTR (unicode string).
+ // (That's why it's different from lpCmdLine which is a LPSTR.)
+ // We don't currently do unicode, so call the non-unicode version
+ // directly.
+ llinfos << "Processing command line" << llendl;
+ LPSTR cmd_line_including_exe_name = GetCommandLineA();
+
+ const S32 MAX_ARGS = 100;
+ int argc = 0;
+ char *argv[MAX_ARGS];
+
+ char *token = NULL;
+ if( cmd_line_including_exe_name[0] == '\"' )
+ {
+ // Exe name is enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, "\"" );
+ argv[argc++] = token;
+ token = strtok( NULL, " \t," );
+ }
+ else
+ {
+ // Exe name is not enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, " \t," );
+ }
+
+ while( (token != NULL) && (argc < MAX_ARGS) )
+ {
+ argv[argc++] = token;
+ /* Get next token: */
+ if (*(token + strlen(token) + 1) == '\"')
+ {
+ token = strtok( NULL, "\"");
+ }
+ else
+ {
+ token = strtok( NULL, " \t," );
+ }
+ }
+
+ S32 i;
+ for (i=0; i<argc; i++)
+ {
+ if(!strcmp(argv[i], "-previous"))
+ {
+ llinfos << "Previous execution did not remove SecondLife.exec_marker" << llendl;
+ gCrashInPreviousExec = TRUE;
+ }
+
+ if(!strcmp(argv[i], "-dialog"))
+ {
+ llinfos << "Show the user dialog" << llendl;
+ crash_behavior = CRASH_BEHAVIOR_ASK;
+ }
+
+ if(!strcmp(argv[i], "-user"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+ gUserserver = argv[i];
+ llinfos << "Got userserver " << gUserserver << llendl;
+ }
+ }
+
+ if(!strcmp(argv[i], "-name"))
+ {
+ if ((i + 1) < argc)
+ {
+ i++;
+
+ mbstowcs(gProductName, argv[i], sizeof(gProductName));
+ llinfos << "Got product name " << argv[i] << llendl;
+ }
+ }
+ }
+
+ // If user doesn't want to send, bail out
+ if (crash_behavior == CRASH_BEHAVIOR_NEVER_SEND)
+ {
+ llinfos << "Crash behavior is never_send, quitting" << llendl;
+ return 0;
+ }
+
+ // Get the current time
+ time(&gStartTime);
+
+ llinfos << "Loading dialogs" << llendl;
+
+ // Store instance handle in our global variable
+ hInst = hInstance;
+
+ // Initialize global strings
+ LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
+ LoadString(hInstance, IDC_WIN_CRASH_LOGGER, szWindowClass, MAX_LOADSTRING);
+
+ gCursorArrow = LoadCursor(NULL, IDC_ARROW);
+ gCursorWait = LoadCursor(NULL, IDC_WAIT);
+
+ // Register a window class that will be used by our dialogs
+ WNDCLASS wndclass;
+ wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = DLGWINDOWEXTRA; // Required, since this is used for dialogs!
+ wndclass.hInstance = hInst;
+ wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE( IDI_WIN_CRASH_LOGGER ) );
+ wndclass.hCursor = gCursorArrow;
+ wndclass.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szWindowClass;
+ RegisterClass( &wndclass );
+
+ // Note: parent hwnd is 0 (the desktop). No dlg proc. See Petzold (5th ed) HexCalc example, Chapter 11, p529
+ // win_crash_logger.rc has been edited by hand.
+ // Dialogs defined with CLASS "WIN_CRASH_LOGGER" (must be same as szWindowClass)
+
+ gHwndProgress = CreateDialog(hInst, MAKEINTRESOURCE(IDD_PROGRESS), 0, NULL);
+ ProcessCaption(gHwndProgress);
+ ShowWindow(gHwndProgress, SW_HIDE );
+
+ if (crash_behavior == CRASH_BEHAVIOR_ALWAYS_SEND)
+ {
+ ShowWindow(gHwndProgress, SW_SHOW );
+ send_crash_report();
+ return 0;
+ }
+
+ if (crash_behavior == CRASH_BEHAVIOR_ASK)
+ {
+ gHwndReport = CreateDialog(hInst, MAKEINTRESOURCE(IDD_REPORT), 0, NULL);
+
+ // Include the product name in the caption and various dialog items.
+ ProcessCaption(gHwndReport);
+ ProcessDlgItemText(gHwndReport, IDC_STATIC_WHATINFO);
+ ProcessDlgItemText(gHwndReport, IDC_STATIC_MOTIVATION);
+
+ // Update the header to include whether or not we crashed on the last run.
+ WCHAR header[2048];
+ if (gCrashInPreviousExec)
+ {
+ swprintf(header, L"%s appears to have crashed or frozen the last time it ran.", gProductName);
+ }
+ else
+ {
+ swprintf(header, L"%s appears to have crashed.", gProductName);
+ }
+ SetDlgItemText(gHwndReport, IDC_STATIC_HEADER, header);
+ ShowWindow(gHwndReport, SW_SHOW );
+
+ MSG msg;
+ while (GetMessage(&msg, NULL, 0, 0))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ return msg.wParam;
+ }
+ else
+ {
+ llwarns << "Unknown crash behavior " << crash_behavior << llendl;
+ return 1;
+ }
+}
+
+
+LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
+{
+ switch( message )
+ {
+ case WM_CREATE:
+ return 0;
+
+ case WM_COMMAND:
+ if( gFirstDialog )
+ {
+ WORD button_id = LOWORD(wParam);
+ bool handled = handle_button_click(button_id);
+ if (handled)
+ {
+ return 0;
+ }
+ }
+ break;
+
+ case WM_DESTROY:
+ // Closing the window cancels
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+bool handle_button_click(WORD button_id)
+{
+ // Is this something other than Send or Don't Send?
+ if (button_id != IDOK
+ && button_id != IDCANCEL)
+ {
+ return false;
+ }
+
+ // See if "do this next time" is checked and save state
+ S32 crash_behavior = CRASH_BEHAVIOR_ASK;
+ LRESULT result = SendDlgItemMessage(gHwndReport, IDC_CHECK_AUTO, BM_GETCHECK, 0, 0);
+ if (result == BST_CHECKED)
+ {
+ if (button_id == IDOK)
+ {
+ crash_behavior = CRASH_BEHAVIOR_ALWAYS_SEND;
+ }
+ else if (button_id == IDCANCEL)
+ {
+ crash_behavior = CRASH_BEHAVIOR_NEVER_SEND;
+ }
+ }
+ bool success = save_crash_behavior_setting(crash_behavior);
+ if (!success)
+ {
+ llwarns << "Failed to save crash settings" << llendl;
+ }
+
+ // We're done with this dialog.
+ gFirstDialog = FALSE;
+
+ // Send the crash report if requested
+ if (button_id == IDOK)
+ {
+ // Don't let users type anything. They believe the reports
+ // get read by humans, and get angry when we don't respond. JC
+ //WCHAR wbuffer[20000];
+ //GetDlgItemText(gHwndReport, // handle to dialog box
+ // IDC_EDIT1, // control identifier
+ // wbuffer, // pointer to buffer for text
+ // 20000 // maximum size of string
+ // );
+ //gUserText = wstring_to_utf8str(utf16str_to_wstring(wbuffer)).c_str();
+ //llinfos << gUserText << llendl;
+
+ // Activate and show the window.
+ ShowWindow(gHwndProgress, SW_SHOW);
+ // Try doing this second to make the progress window go frontmost.
+ ShowWindow(gHwndReport, SW_HIDE);
+
+ send_crash_report();
+ }
+
+ // Quit the app
+ PostQuitMessage(0);
+
+ return true;
+}
+
+
+class LLFileEncoder
+{
+public:
+ LLFileEncoder(const char *formname, const char *filename);
+
+ BOOL isValid() const { return mIsValid; }
+ LLString encodeURL(const S32 max_length = 0);
+public:
+ BOOL mIsValid;
+ LLString mFilename;
+ LLString mFormname;
+ S32 mBufLength;
+ U8 *mBuf;
+};
+
+LLString encode_string(const char *formname, const LLString &str);
+
+void update_messages()
+{
+ MSG msg;
+ while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ if (msg.message == WM_QUIT)
+ {
+ exit(0);
+ }
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+}
+
+void sleep_and_pump_messages( U32 seconds )
+{
+ const U32 CYCLES_PER_SECOND = 10;
+ U32 cycles = seconds * CYCLES_PER_SECOND;
+ while( cycles-- )
+ {
+ update_messages();
+ ms_sleep(1000 / CYCLES_PER_SECOND);
+ }
+}
+
+
+void show_progress(const char* message)
+{
+ std::wstring msg = wstring_to_utf16str(utf8str_to_wstring(message));
+ if (gHwndProgress)
+ {
+ SendDlgItemMessage(gHwndProgress, // handle to destination window
+ IDC_LOG,
+ WM_SETTEXT, // message to send
+ FALSE, // undo option
+ (LPARAM)msg.c_str());
+ }
+}
+
+
+void send_crash_report()
+{
+ update_messages();
+ show_progress("Starting up...");
+ update_messages();
+
+ const S32 SL_MAX_SIZE = 100000; // Maximum size of the Second Life log file.
+
+ update_messages();
+
+ // Lots of silly variable, replicated for each log file.
+ std::string db_file_name; // debug.log
+ std::string sl_file_name; // SecondLife.log
+ std::string md_file_name; // minidump (SecondLife.dmp) file name
+ std::string st_file_name; // stats.log file
+ std::string si_file_name; // settings.ini file
+ std::string ml_file_name; // message.log file
+
+ LLFileEncoder *db_filep = NULL;
+ LLFileEncoder *sl_filep = NULL;
+ LLFileEncoder *st_filep = NULL;
+ LLFileEncoder *md_filep = NULL;
+ LLFileEncoder *si_filep = NULL;
+ LLFileEncoder *ml_filep = NULL;
+
+ // DX hardware probe blocks, so we can't cancel during it
+ SetCursor(gCursorWait);
+
+ // Need to do hardware detection before we grab the files, otherwise we don't send the debug log updates
+ // to the server (including the list of hardware).
+ update_messages();
+ show_progress("Detecting hardware, please wait...");
+ update_messages();
+ gDXHardware.setWriteDebugFunc(write_debug);
+ gDXHardware.getInfo(FALSE);
+ update_messages();
+ gDXHardware.dumpDevices();
+ update_messages();
+ fclose(gDebugFile);
+ gDebugFile = NULL;
+
+ // At this point we're responsive enough the user could click the close button
+ SetCursor(gCursorArrow);
+
+ ///////////////////////////////////
+ //
+ // We do the parsing for the debug_info file first, as that will
+ // give us the location of the SecondLife.log file.
+ //
+
+ // Figure out the filename of the debug log
+ db_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+ db_filep = new LLFileEncoder("DB", db_file_name.c_str());
+
+ // Get the filename of the SecondLife.log file
+ char tmp_sl_name[256];
+ tmp_sl_name[0] = '\0';
+
+ update_messages();
+ show_progress("Looking for files...");
+ update_messages();
+
+ // Look for it in the debug_info.log file
+ if (db_filep->isValid())
+ {
+ sscanf((const char *)db_filep->mBuf, "SL Log: %[^\r\n]", tmp_sl_name);
+ }
+ else
+ {
+ delete db_filep;
+ db_filep = NULL;
+ }
+
+ if (gCrashInPreviousExec)
+ {
+ // If we froze, the crash log this time around isn't useful. Use the
+ // old one.
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.old");
+ }
+ else if (tmp_sl_name[0])
+ {
+ // If debug_info.log gives us a valid log filename, use that.
+ sl_file_name = tmp_sl_name;
+ llinfos << "Using log file from debug log " << sl_file_name << llendl;
+ }
+ else
+ {
+ // Figure out the filename of the default second life log
+ sl_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.log");
+ }
+
+ // Now we get the SecondLife.log file if it's there
+ sl_filep = new LLFileEncoder("SL", sl_file_name.c_str());
+ if (!sl_filep->isValid())
+ {
+ delete sl_filep;
+ sl_filep = NULL;
+ }
+
+ update_messages();
+ show_progress("Looking for stats file...");
+ update_messages();
+
+ st_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"stats.log");
+ st_filep = new LLFileEncoder("ST", st_file_name.c_str());
+ if (!st_filep->isValid())
+ {
+ delete st_filep;
+ st_filep = NULL;
+ }
+
+ si_file_name = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS,"settings.ini");
+ si_filep = new LLFileEncoder("SI", si_file_name.c_str());
+ if (!si_filep->isValid())
+ {
+ delete si_filep;
+ si_filep = NULL;
+ }
+
+ // Now we get the minidump
+ md_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"SecondLife.dmp");
+ md_filep = new LLFileEncoder("MD", md_file_name.c_str());
+ if (!md_filep->isValid())
+ {
+ delete md_filep;
+ md_filep = NULL;
+ }
+
+ // Now we get the message log
+ ml_file_name = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"message.log");
+ ml_filep = new LLFileEncoder("ML", ml_file_name.c_str());
+ if (!ml_filep->isValid())
+ {
+ delete ml_filep;
+ ml_filep = NULL;
+ }
+
+ LLString post_data;
+ LLString tmp_url_buf;
+
+ // Append the userserver
+ tmp_url_buf = encode_string("USER", gUserserver);
+ post_data += tmp_url_buf;
+ llinfos << "PostData:" << post_data << llendl;
+
+ if (gCrashInPreviousExec)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = encode_string("EF", "Y");
+ post_data += tmp_url_buf;
+ }
+
+ update_messages();
+ show_progress("Encoding data");
+ update_messages();
+ if (db_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = db_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending DB log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending DB log file" << llendl;
+ }
+ show_progress("Encoding data.");
+ update_messages();
+
+ if (sl_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = sl_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending SL log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending SL log file" << llendl;
+ }
+ show_progress("Encoding data..");
+ update_messages();
+
+ if (st_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = st_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending stats log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending stats log file" << llendl;
+ }
+ show_progress("Encoding data...");
+ update_messages();
+
+ if (md_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = md_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending minidump log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending minidump log file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (si_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = si_filep->encodeURL();
+ post_data += tmp_url_buf;
+ llinfos << "Sending settings log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending settings.ini file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (ml_filep)
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = ml_filep->encodeURL(SL_MAX_SIZE);
+ post_data += tmp_url_buf;
+ llinfos << "Sending message log file" << llendl;
+ }
+ else
+ {
+ llinfos << "Not sending message.log file" << llendl;
+ }
+ show_progress("Encoding data....");
+ update_messages();
+
+ if (gUserText.size())
+ {
+ post_data.append(1, '&');
+ tmp_url_buf = encode_string("UN", gUserText);
+ post_data += tmp_url_buf;
+ }
+
+ delete db_filep;
+ db_filep = NULL;
+ delete sl_filep;
+ sl_filep = NULL;
+ delete md_filep;
+ md_filep = NULL;
+
+ // Post data to web server
+ const S32 BUFSIZE = 65536;
+ HINTERNET hinet, hsession, hrequest;
+ char data[BUFSIZE];
+ unsigned long bytes_read;
+
+ llinfos << "Connecting to crash report server" << llendl;
+ update_messages();
+ show_progress("Connecting to server...");
+ update_messages();
+
+ // Init wininet subsystem
+ hinet = InternetOpen(L"LindenCrashReporter", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+ if (hinet == NULL)
+ {
+ llinfos << "Couldn't open connection" << llendl;
+ sleep_and_pump_messages( 5 );
+// return FALSE;
+ }
+
+ hsession = InternetConnect(hinet,
+ L"secondlife.com",
+ INTERNET_DEFAULT_HTTP_PORT,
+ NULL,
+ NULL,
+ INTERNET_SERVICE_HTTP,
+ NULL,
+ NULL);
+
+ if (!hsession)
+ {
+ llinfos << "Couldn't talk to crash report server" << llendl;
+ }
+
+ hrequest = HttpOpenRequest(hsession, L"POST", L"/cgi-bin/viewer_crash_reporter2", NULL, L"", NULL, 0, 0);
+ if (!hrequest)
+ {
+ llinfos << "Couldn't open crash report URL!" << llendl;
+ }
+
+ llinfos << "Transmitting data" << llendl;
+ llinfos << "Bytes: " << (post_data.size()) << llendl;
+
+ update_messages();
+ show_progress("Transmitting data...");
+ update_messages();
+
+ BOOL ok = HttpSendRequest(hrequest, NULL, 0, (void *)(post_data.c_str()), post_data.size());
+ if (!ok)
+ {
+ llinfos << "Error posting data!" << llendl;
+ sleep_and_pump_messages( 5 );
+ }
+
+ llinfos << "Response from crash report server:" << llendl;
+ do
+ {
+ if (InternetReadFile(hrequest, data, BUFSIZE, &bytes_read))
+ {
+ if (bytes_read == 0)
+ {
+ // If InternetFileRead returns TRUE AND bytes_read == 0
+ // we've successfully downloaded the entire file
+ break;
+ }
+ else
+ {
+ data[bytes_read] = 0;
+ llinfos << data << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Couldn't read file!" << llendl;
+ sleep_and_pump_messages( 5 );
+// return FALSE;
+ }
+ } while(TRUE);
+
+ InternetCloseHandle(hrequest);
+ InternetCloseHandle(hsession);
+ InternetCloseHandle(hinet);
+ update_messages();
+ show_progress("Done.");
+ sleep_and_pump_messages( 3 );
+// return TRUE;
+}
+
+LLFileEncoder::LLFileEncoder(const char *form_name, const char *filename)
+{
+ mFormname = form_name;
+ mFilename = filename;
+ mIsValid = FALSE;
+ mBuf = NULL;
+
+ int res;
+
+ llstat stat_data;
+ res = LLFile::stat(mFilename.c_str(), &stat_data);
+ if (res)
+ {
+ llwarns << "File " << mFilename << " is missing!" << llendl;
+ return;
+ }
+
+ FILE *fp = NULL;
+ S32 buf_size = 0;
+ S32 count = 0;
+ while (count < 5)
+ {
+ buf_size = stat_data.st_size;
+ fp = LLFile::fopen(mFilename.c_str(), "rb");
+ if (!fp)
+ {
+ llwarns << "Can't open file " << mFilename << ", wait for a second" << llendl;
+ // Couldn't open the file, wait a bit and try again
+ count++;
+ ms_sleep(1000);
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (!fp)
+ {
+ return;
+ }
+ U8 *buf = new U8[buf_size + 1];
+ fread(buf, 1, buf_size, fp);
+ fclose(fp);
+
+ mBuf = buf;
+ mBufLength = buf_size;
+
+ mIsValid = TRUE;
+}
+
+LLString LLFileEncoder::encodeURL(const S32 max_length)
+{
+ LLString result = mFormname;
+ result.append(1, '=');
+
+ S32 i = 0;
+
+ if (max_length)
+ {
+ if (mBufLength > max_length)
+ {
+ i = mBufLength - max_length;
+ }
+ }
+
+ S32 url_buf_size = 3*mBufLength + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ for (; i < mBufLength; i++)
+ {
+ S32 byte_val = mBuf[i];
+ sprintf(url_buf + cur_pos, "%%%02x", byte_val);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
+
+LLString encode_string(const char *formname, const LLString &str)
+{
+ LLString result = formname;
+ result.append(1, '=');
+ // Not using LLString because of bad performance issues
+ S32 buf_size = str.size();
+ S32 url_buf_size = 3*str.size() + 1;
+ char *url_buf = new char[url_buf_size];
+
+ S32 cur_pos = 0;
+ S32 i;
+ for (i = 0; i < buf_size; i++)
+ {
+ sprintf(url_buf + cur_pos, "%%%02x", str[i]);
+ cur_pos += 3;
+ }
+ url_buf[i*3] = 0;
+
+ result.append(url_buf);
+ delete[] url_buf;
+ return result;
+}
+
+void write_debug(const char *str)
+{
+ if (!gDebugFile)
+ {
+ std::string debug_filename = gDirUtilp->getExpandedFilename(LL_PATH_LOGS,"debug_info.log");
+ llinfos << "Opening debug file " << debug_filename << llendl;
+ gDebugFile = LLFile::fopen(debug_filename.c_str(), "a+");
+ if (!gDebugFile)
+ {
+ fprintf(stderr, "Couldn't open %s: debug log to stderr instead.\n", debug_filename.c_str());
+ gDebugFile = stderr;
+ }
+ }
+ fprintf(gDebugFile, str);
+ fflush(gDebugFile);
+}
+
+void write_debug(std::string& str)
+{
+ write_debug(str.c_str());
+}
+
+S32 load_crash_behavior_setting()
+{
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ gCrashSettings.loadFromFile(filename);
+
+ S32 value = gCrashSettings.getS32(CRASH_BEHAVIOR_SETTING);
+
+ if (value < CRASH_BEHAVIOR_ASK || CRASH_BEHAVIOR_NEVER_SEND < value) return CRASH_BEHAVIOR_ASK;
+
+ return value;
+}
+
+bool save_crash_behavior_setting(S32 crash_behavior)
+{
+ if (crash_behavior < CRASH_BEHAVIOR_ASK) return false;
+ if (crash_behavior > CRASH_BEHAVIOR_NEVER_SEND) return false;
+
+ gCrashSettings.setS32(CRASH_BEHAVIOR_SETTING, crash_behavior);
+ std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, CRASH_SETTINGS_FILE);
+
+ gCrashSettings.saveToFile(filename, FALSE);
+
+ return true;
+}
diff --git a/indra/win_crash_logger/win_crash_logger.h b/indra/win_crash_logger/win_crash_logger.h
new file mode 100644
index 0000000000..8a765cc525
--- /dev/null
+++ b/indra/win_crash_logger/win_crash_logger.h
@@ -0,0 +1,20 @@
+/**
+ * @file win_crash_logger.h
+ * @brief Windows crash logger project includes
+ *
+ * Copyright (c) 2003-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+
+#if !defined(AFX_WIN_CRASH_LOGGER_H__79802F4B_7C37_4F63_A2BB_0768788C3A27__INCLUDED_)
+#define AFX_WIN_CRASH_LOGGER_H__79802F4B_7C37_4F63_A2BB_0768788C3A27__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include "resource.h"
+
+
+#endif // !defined(AFX_WIN_CRASH_LOGGER_H__79802F4B_7C37_4F63_A2BB_0768788C3A27__INCLUDED_)
diff --git a/indra/win_crash_logger/win_crash_logger.ico b/indra/win_crash_logger/win_crash_logger.ico
new file mode 100644
index 0000000000..386883523b
--- /dev/null
+++ b/indra/win_crash_logger/win_crash_logger.ico
Binary files differ
diff --git a/indra/win_crash_logger/win_crash_logger.rc b/indra/win_crash_logger/win_crash_logger.rc
new file mode 100644
index 0000000000..f4c7eaa337
--- /dev/null
+++ b/indra/win_crash_logger/win_crash_logger.rc
@@ -0,0 +1,207 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#define APSTUDIO_HIDDEN_SYMBOLS
+#include "windows.h"
+#undef APSTUDIO_HIDDEN_SYMBOLS
+#include "resource.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_WIN_CRASH_LOGGER ICON "ll_icon.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDC_WIN_CRASH_LOGGER MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "E&xit", IDM_EXIT
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About ...", IDM_ABOUT
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_PROGRESS DIALOG 100, 100, 186, 33
+STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU
+CAPTION "%s Crash Logger"
+CLASS "WIN_CRASH_LOGGER"
+FONT 8, "MS Sans Serif"
+BEGIN
+ LTEXT "Static",IDC_LOG,7,7,172,8
+END
+
+IDD_REPORT DIALOGEX 100, 100, 297, 125
+STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU
+CAPTION "%s Crash Logger"
+CLASS "WIN_CRASH_LOGGER"
+FONT 8, "MS Sans Serif", 0, 0, 0x0
+BEGIN
+ DEFPUSHBUTTON "Send",IDOK,198,104,45,15,WS_GROUP
+ PUSHBUTTON "Don't Send",IDCANCEL,247,104,45,15,WS_GROUP
+ LTEXT "%s appears to have crashed.",IDC_STATIC_HEADER,4,4,288,
+ 14
+ LTEXT "This crash reporter collects information about your computer's hardware, operating system, and some %s logs, which are used for debugging purposes only.",
+ IDC_STATIC_WHATINFO,4,23,288,19,NOT WS_GROUP
+ CONTROL "Remember this choice",IDC_CHECK_AUTO,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,4,106,89,13
+ LTEXT "Sending crash reports is the best way to help us improve the quality of %s.",
+ IDC_STATIC_MOTIVATION,4,38,288,8
+ LTEXT "If you continue to experience this problem, please try one of the following:",
+ IDC_STATIC,4,57,251,8
+ LTEXT "- Contact support by email at support@lindenlab.com",
+ IDC_STATIC,4,67,179,8
+ LTEXT "- If you can log-in, please contact Live Help by using menu Help > Live Help.",
+ IDC_STATIC,4,87,249,8
+ LTEXT "- Search the Second Life Knowledge Base at http://secondlife.com/knowledgebase/",
+ IDC_STATIC,4,77,281,8,SS_NOTIFY
+END
+
+IDD_PREVREPORTBOX DIALOG 100, 100, 232, 213
+STYLE DS_SETFONT | DS_SETFOREGROUND | WS_CAPTION | WS_SYSMENU
+CAPTION "%s Crash Logger"
+CLASS "WIN_CRASH_LOGGER"
+FONT 8, "MS Sans Serif"
+BEGIN
+ DEFPUSHBUTTON "OK",IDOK,131,193,45,15,WS_GROUP
+ EDITTEXT IDC_EDIT1,4,102,223,89,ES_MULTILINE | ES_WANTRETURN |
+ WS_VSCROLL
+ PUSHBUTTON "Cancel",IDCANCEL,181,193,45,15,WS_GROUP
+ LTEXT "%s appears to have crashed or frozen the last time it ran.",
+ IDC_STATIC,4,4,214,8
+ LTEXT "This crash reporter collects information about your computer's",
+ IDC_STATIC,4,17,201,8
+ LTEXT "hardware configuration, operating system, and some %s",
+ IDC_STATIC,4,25,212,8
+ LTEXT "logs, all of which are used for debugging purposes only.",
+ IDC_STATIC,4,33,210,8
+ LTEXT "In the space below, please briefly describe what you were doing",
+ IDC_STATIC,3,48,208,8
+ LTEXT "or trying to do just prior to the crash.",IDC_STATIC,3,
+ 56,204,8
+ LTEXT "If you don't wish to send Linden Lab a crash report, press Cancel.",
+ IDC_STATIC,3,90,214,8
+ LTEXT "This report is NOT read by customer support. If you have billing or",
+ IDC_STATIC,3,68,208,8
+ LTEXT "other questions, please go to: www.secondlife.com/support",
+ IDC_STATIC,3,76,206,8
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+2 TEXTINCLUDE
+BEGIN
+ "#define APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""windows.h""\r\n"
+ "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n"
+ "#include ""resource.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_PROGRESS, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 179
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 26
+ END
+
+ IDD_REPORT, DIALOG
+ BEGIN
+ RIGHTMARGIN, 292
+ VERTGUIDE, 4
+ BOTTOMMARGIN, 119
+ HORZGUIDE, 4
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_APP_TITLE "win_crash_logger"
+ IDS_HELLO "Hello World!"
+ IDC_WIN_CRASH_LOGGER "WIN_CRASH_LOGGER"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/indra/win_updater/updater.cpp b/indra/win_updater/updater.cpp
new file mode 100644
index 0000000000..99680984d1
--- /dev/null
+++ b/indra/win_updater/updater.cpp
@@ -0,0 +1,527 @@
+/**
+ * @file updater.cpp
+ * @brief Windows auto-updater
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+//
+// Usage: updater -userserver <server> [-name <window_title>] [-program <program_name>] [-silent]
+//
+
+#include <windows.h>
+#include <wininet.h>
+
+#include <stdio.h>
+#include "llfile.h"
+
+#define BUFSIZE 8192
+
+int gTotalBytesRead = 0;
+HWND gWindow = NULL;
+WCHAR gProgress[256];
+char* gUserServer;
+char* gProgramName;
+char* gProductName;
+bool gIsSilent;
+
+#if _DEBUG
+FILE* logfile = 0;
+#endif
+
+char* wchars_to_utf8chars(WCHAR* in_chars)
+{
+ int tlen = 0;
+ WCHAR* twc = in_chars;
+ while (*twc++ != 0)
+ {
+ tlen++;
+ }
+ char* outchars = new char[tlen];
+ char* res = outchars;
+ for (int i=0; i<tlen; i++)
+ {
+ int cur_char = (int)(*in_chars++);
+ if (cur_char < 0x80)
+ {
+ *outchars++ = (char)cur_char;
+ }
+ else
+ {
+ *outchars++ = '?';
+ }
+ }
+ *outchars = 0;
+ return res;
+}
+
+int WINAPI get_url_into_file(WCHAR *uri, char *path, int *cancelled)
+{
+ int success = FALSE;
+ *cancelled = FALSE;
+
+ HINTERNET hinet, hdownload;
+ char data[BUFSIZE];
+ unsigned long bytes_read;
+
+#if _DEBUG
+ fprintf(logfile,"Opening '%s'\n",path);
+ fflush(logfile);
+#endif
+
+ FILE *fp = fopen(path, "wb");
+
+ if (!fp)
+ {
+#if _DEBUG
+ fprintf(logfile,"Failed to open '%s'\n",path);
+ fflush(logfile);
+#endif
+ return success;
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InternetOpen\n");
+ fflush(logfile);
+#endif
+ // Init wininet subsystem
+ hinet = InternetOpen(L"LindenUpdater", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
+ if (hinet == NULL)
+ {
+ return success;
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InternetOpenUrl: %s\n",wchars_to_utf8chars(uri));
+ fflush(logfile);
+#endif
+ hdownload = InternetOpenUrl(hinet, uri, NULL, 0, INTERNET_FLAG_NEED_FILE, NULL);
+ if (hdownload == NULL)
+ {
+#if _DEBUG
+ DWORD err = GetLastError();
+ fprintf(logfile,"InternetOpenUrl Failed: %d\n",err);
+ fflush(logfile);
+#endif
+ return success;
+ }
+
+ DWORD total_bytes = 0;
+ success = InternetQueryDataAvailable(hdownload, &total_bytes, 0, 0);
+ if (success == FALSE)
+ {
+#if _DEBUG
+ DWORD err = GetLastError();
+ fprintf(logfile,"InternetQueryDataAvailable Failed: %d bytes\n",total_bytes);
+ fflush(logfile);
+#endif
+ return success;
+ }
+
+ success = FALSE;
+ while(!success && !(*cancelled))
+ {
+ MSG msg;
+
+#if _DEBUG
+ fprintf(logfile,"Calling InternetReadFile\n");
+ fflush(logfile);
+#endif
+ if (!InternetReadFile(hdownload, data, BUFSIZE, &bytes_read))
+ {
+#if _DEBUG
+ fprintf(logfile,"InternetReadFile Failed.\n");
+ fflush(logfile);
+#endif
+ // ...an error occurred
+ return FALSE;
+ }
+
+#if _DEBUG
+ if (!bytes_read)
+ {
+ fprintf(logfile,"InternetReadFile Read 0 bytes.\n");
+ fflush(logfile);
+ }
+#endif
+
+ if (!data)
+ {
+#if _DEBUG
+ fprintf(logfile,"InternetReadFile Returned NULL data, bytes_read = %d.\n",bytes_read);
+ fflush(logfile);
+#endif
+ // ...an error occurred
+ return FALSE;
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Reading Data, bytes_read = %d\n",bytes_read);
+ fflush(logfile);
+#endif
+
+ if (bytes_read == 0)
+ {
+ // If InternetFileRead returns TRUE AND bytes_read == 0
+ // we've successfully downloaded the entire file
+ wsprintf(gProgress, L"Download complete.");
+ success = TRUE;
+ }
+ else
+ {
+ // write what we've got, then continue
+ fwrite(data, sizeof(char), bytes_read, fp);
+
+ gTotalBytesRead += int(bytes_read);
+
+ wsprintf(gProgress, L"Downloaded: %dK", gTotalBytesRead / 1024);
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InvalidateRect\n");
+ fflush(logfile);
+#endif
+
+ // Mark the window as needing redraw (of the whole thing)
+ InvalidateRect(gWindow, NULL, TRUE);
+
+ // Do the redraw
+#if _DEBUG
+ fprintf(logfile,"Calling UpdateWindow\n");
+ fflush(logfile);
+#endif
+ UpdateWindow(gWindow);
+
+#if _DEBUG
+ fprintf(logfile,"Calling PeekMessage\n");
+ fflush(logfile);
+#endif
+ if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+
+ if (msg.message == WM_QUIT)
+ {
+ // bail out, user cancelled
+ *cancelled = TRUE;
+ }
+ }
+ }
+
+#if _DEBUG
+ fprintf(logfile,"Calling InternetCloseHandle\n");
+ fclose(logfile);
+#endif
+
+ fclose(fp);
+ InternetCloseHandle(hdownload);
+ InternetCloseHandle(hinet);
+
+ return success;
+}
+
+LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
+{
+ HDC hdc; // Drawing context
+ PAINTSTRUCT ps;
+
+ switch(message)
+ {
+ case WM_PAINT:
+ {
+ hdc = BeginPaint(hwnd, &ps);
+
+ RECT rect;
+ GetClientRect(hwnd, &rect);
+ DrawText(hdc, gProgress, -1, &rect,
+ DT_SINGLELINE | DT_CENTER | DT_VCENTER);
+
+ EndPaint(hwnd, &ps);
+ return 0;
+ }
+ case WM_CLOSE:
+ case WM_DESTROY:
+ // Get out of full screen
+ // full_screen_mode(false);
+ PostQuitMessage(0);
+ return 0;
+ }
+ return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+#define win_class_name L"FullScreen"
+#define UPDATE_URIBASE L"http://secondlife.com/update.php?userserver="
+
+int parse_args(int argc, char **argv)
+{
+ // Check for old-type arguments.
+ if (2 == argc)
+ {
+ gUserServer = argv[1];
+ return 0;
+ }
+
+ int j;
+
+ for (j = 1; j < argc; j++)
+ {
+ if ((!strcmp(argv[j], "-userserver")) && (++j < argc))
+ {
+ gUserServer = argv[j];
+ }
+ else if ((!strcmp(argv[j], "-name")) && (++j < argc))
+ {
+ gProductName = argv[j];
+ }
+ else if ((!strcmp(argv[j], "-program")) && (++j < argc))
+ {
+ gProgramName = argv[j];
+ }
+ else if (!strcmp(argv[j], "-silent"))
+ {
+ gIsSilent = true;
+ }
+ }
+
+ // If nothing was set, let the caller know.
+ if (!gUserServer && !gProductName && !gProgramName && !gIsSilent)
+ {
+ return 1;
+ }
+ return 0;
+}
+
+int WINAPI
+WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+ // Parse the command line.
+ LPSTR cmd_line_including_exe_name = GetCommandLineA();
+
+ const int MAX_ARGS = 100;
+ int argc = 0;
+ char *argv[MAX_ARGS];
+
+#if _DEBUG
+ logfile = _wfopen(TEXT("updater.log"),TEXT("wt"));
+ fprintf(logfile,"Parsing command arguments\n");
+ fflush(logfile);
+#endif
+
+ char *token = NULL;
+ if( cmd_line_including_exe_name[0] == '\"' )
+ {
+ // Exe name is enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, "\"" );
+ argv[argc++] = token;
+ token = strtok( NULL, " \t," );
+ }
+ else
+ {
+ // Exe name is not enclosed in quotes
+ token = strtok( cmd_line_including_exe_name, " \t," );
+ }
+
+ while( (token != NULL) && (argc < MAX_ARGS) )
+ {
+ argv[argc++] = token;
+ /* Get next token: */
+ if (*(token + strlen(token) + 1) == '\"')
+ {
+ token = strtok( NULL, "\"");
+ }
+ else
+ {
+ token = strtok( NULL, " \t," );
+ }
+ }
+
+ gUserServer = NULL;
+ gProgramName = NULL;
+ gProductName = NULL;
+ gIsSilent = false;
+
+ /////////////////////////////////////////
+ //
+ // Process command line arguments
+ //
+
+#if _DEBUG
+ fprintf(logfile,"Processing command arguments\n");
+ fflush(logfile);
+#endif
+
+ //
+ // Parse the command line arguments
+ //
+ int parse_args_result = parse_args(argc, argv);
+ WCHAR window_title[2048];
+ if (gProductName)
+ {
+ mbstowcs(window_title, gProductName, 2048);
+ wcscat(window_title, L" Updater");
+ }
+ else
+ {
+ mbstowcs(window_title, "Second Life Updater", 2048);
+ }
+
+ WNDCLASSEX wndclassex = { 0 };
+ DEVMODE dev_mode = { 0 };
+ char update_exec_path[MAX_PATH];
+ char *ptr;
+ WCHAR update_uri[4096];
+
+ const int WINDOW_WIDTH = 250;
+ const int WINDOW_HEIGHT = 100;
+
+ wsprintf(gProgress, L"Connecting...");
+
+ /* Init the WNDCLASSEX */
+ wndclassex.cbSize = sizeof(WNDCLASSEX);
+ wndclassex.style = CS_HREDRAW | CS_VREDRAW;
+ wndclassex.hInstance = hInstance;
+ wndclassex.lpfnWndProc = WinProc;
+ wndclassex.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+ wndclassex.lpszClassName = win_class_name;
+
+ RegisterClassEx(&wndclassex);
+
+ // Get the size of the screen
+ EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dev_mode);
+
+ gWindow = CreateWindowEx(NULL, win_class_name,
+ window_title,
+ WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT,
+ CW_USEDEFAULT,
+ WINDOW_WIDTH,
+ WINDOW_HEIGHT,
+ NULL, NULL, hInstance, NULL);
+
+ ShowWindow(gWindow, nShowCmd);
+ UpdateWindow(gWindow);
+
+ if (parse_args_result)
+ {
+ MessageBox(gWindow,
+ L"Usage: updater -userserver <server> [-name <window_title>] [-program <program_name>] [-silent]",
+ L"Usage", MB_OK);
+ return parse_args_result;
+ }
+
+ // Did we get a userserver to work with?
+ if (!gUserServer)
+ {
+ MessageBox(gWindow, L"Please specify the IP address of the userserver on the command line",
+ L"Error", MB_OK);
+ return 1;
+ }
+
+ // Can't feed GetTempPath into GetTempFile directly
+ if (0 == GetTempPathA(MAX_PATH - 14, update_exec_path))
+ {
+ MessageBox(gWindow, L"Problem with GetTempPath()",
+ L"Error", MB_OK);
+ return 1;
+ }
+ if (0 == GetTempFileNameA(update_exec_path, NULL, 0, update_exec_path))
+ {
+ MessageBox(gWindow, L"Problem with GetTempFileName()",
+ L"Error", MB_OK);
+ return 1;
+ }
+ // Hack hack hack
+ ptr = strrchr(update_exec_path, '.');
+ *(ptr + 1) = 'e';
+ *(ptr + 2) = 'x';
+ *(ptr + 3) = 'e';
+ *(ptr + 4) = 0;
+ wcscpy(update_uri, UPDATE_URIBASE);
+ WCHAR wcmdline[2048];
+ mbstowcs(wcmdline, gUserServer, 2048);
+ wcscat(update_uri, wcmdline);
+
+ int success;
+ int cancelled;
+
+ // Actually do the download
+#if _DEBUG
+ fprintf(logfile,"Calling get_url_into_file\n");
+ fflush(logfile);
+#endif
+ success = get_url_into_file(update_uri, update_exec_path, &cancelled);
+
+ // WinInet can't tell us if we got a 404 or not. Therefor, we check
+ // for the size of the downloaded file, and assume that our installer
+ // will always be greater than 1MB.
+ if (gTotalBytesRead < (1024 * 1024) && ! cancelled)
+ {
+ MessageBox(gWindow,
+ L"The Second Life auto-update has failed.\n"
+ L"The problem may be caused by other software installed \n"
+ L"on your computer, such as a firewall.\n"
+ L"Please visit http://secondlife.com/download/ \n"
+ L"to download the latest version of Second Life.\n",
+ NULL, MB_OK);
+ return 1;
+ }
+
+ if (cancelled)
+ {
+ // silently exit
+ return 0;
+ }
+
+ if (!success)
+ {
+ MessageBox(gWindow,
+ L"Second Life download failed.\n"
+ L"Please try again later.",
+ NULL, MB_OK);
+ return 1;
+ }
+
+ // Construct some parameters.
+ char params[2048];
+ if (gIsSilent && gProgramName)
+ {
+ sprintf(params, "/S /P=\"%s\"", gProgramName);
+ }
+ else if (gProgramName)
+ {
+ sprintf(params, "/P=\"%s\"", gProgramName);
+ }
+ else if (gIsSilent)
+ {
+ sprintf(params, "/S");
+ }
+ else
+ {
+ params[0] = '\0';
+ }
+
+ if (32 >= (int) ShellExecuteA(gWindow, "open", update_exec_path, params,
+ "C:\\", SW_SHOWDEFAULT))
+ {
+ // No shit: less than or equal to 32 means failure
+ MessageBox(gWindow, L"ShellExecute failed. Please try again later.", NULL, MB_OK);
+ return 1;
+ }
+
+ if (gIsSilent && gProductName)
+ {
+ WCHAR message[2048];
+ WCHAR wproduct[2048];
+ mbstowcs(wproduct, gProductName, 2048);
+
+ wsprintf(message,
+ L"Updating %s. %s will automatically start once the update is complete. This may take a minute...",
+ wproduct, wproduct);
+
+ MessageBox(gWindow, message, L"Download Complete", MB_OK);
+ }
+
+ return 0;
+}
diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg
new file mode 100644
index 0000000000..bd44513c93
--- /dev/null
+++ b/scripts/messages/message_template.msg
@@ -0,0 +1,9991 @@
+// Linden Lab development message templates
+
+version 1.053
+
+// *************************************************************************
+// Test Message
+// *************************************************************************
+
+// Test Message
+
+{
+ TestMessage Low NotTrusted Zerocoded
+ {
+ TestBlock1 Single
+ { Test1 U32 }
+ }
+ {
+ NeighborBlock Multiple 4
+ { Test0 U32 }
+ { Test1 U32 }
+ { Test2 U32 }
+ }
+}
+
+// *************************************************************************
+// Messaging Internal Data Management Message
+// *************************************************************************
+
+// *************************
+// List fixed messages first
+// *************************
+
+// This is the newly updated version of the message template checksum
+// request. The token is there so that the viewer can drop responses
+// that do not match the supplied token.
+{
+ SecuredTemplateChecksumRequest Fixed 0xFFFFFFFA NotTrusted Unencoded
+ {
+ TokenBlock Single
+ { Token LLUUID }
+ }
+}
+
+// Packet Ack - Ack a list of packets sent reliable
+{
+ PacketAck Fixed 0xFFFFFFFB NotTrusted Unencoded
+ {
+ Packets Variable
+ { ID U32 }
+ }
+}
+
+// OpenCircuit - Tells the recipient's messaging system to open the descibed circuit
+{
+ OpenCircuit Fixed 0xFFFFFFFC NotTrusted Unencoded
+ {
+ CircuitInfo Single
+ { IP IPADDR }
+ { Port IPPORT }
+ }
+}
+
+// CloseCircuit - Tells the recipient's messaging system to close the descibed circuit
+{
+ CloseCircuit Fixed 0xFFFFFFFD NotTrusted Unencoded
+}
+
+
+// message template version check. Deprecated - the viewer should send a
+// SecuredTemplateChecksumRequest to prevent template checksum
+// injection with a bad checksum.
+{
+ TemplateChecksumRequest Fixed 0xFFFFFFFE NotTrusted Unencoded
+}
+
+// message template version check
+{
+ TemplateChecksumReply Fixed 0xFFFFFFFF NotTrusted Unencoded
+ {
+ DataBlock Single
+ { Checksum U32 }
+ { MajorVersion U8 }
+ { MinorVersion U8 }
+ { PatchVersion U8 }
+ { ServerVersion U8 }
+ { Flags U32 }
+ }
+ {
+ TokenBlock Single
+ { Token LLUUID }
+ }
+}
+
+// ******************
+// End fixed messages
+// ******************
+
+// StartPingCheck - used to measure circuit ping times
+// PingID is used to determine how backlogged the ping was that was
+// returned (or how hosed the other side is)
+{
+ StartPingCheck High NotTrusted Unencoded
+ {
+ PingID Single
+ { PingID U8 }
+ { OldestUnacked U32 } // Current oldest "unacked" packet on the sender side
+ }
+}
+
+// CompletePingCheck - used to measure circuit ping times
+
+{
+ CompletePingCheck High NotTrusted Unencoded
+ {
+ PingID Single
+ { PingID U8 }
+ }
+}
+
+// space->sim
+// sim->sim
+// AddCircuitCode - Tells the recipient's messaging system that this code
+// is for a legal circuit
+{
+ AddCircuitCode Low Trusted Unencoded
+ {
+ CircuitCode Single
+ { Code U32 }
+ { SessionID LLUUID }
+ { AgentID LLUUID } // WARNING - may be null in valid message
+ }
+}
+
+// viewer->sim
+// UseCircuitCode - Attempts to provide the recipient with IP and Port
+// info. In the case of viewers, the id is the session id. For other
+// machines it may be null. The session id will always be the session
+// id of the process, which every server will generate on startup and
+// the viewer will be handed after login.
+{
+ UseCircuitCode Low NotTrusted Unencoded
+ {
+ CircuitCode Single
+ { Code U32 }
+ { SessionID LLUUID }
+ { ID LLUUID } // agent id
+ }
+}
+
+// LogControl - This message allows us to remotely control the
+// runtime logging facilities of the error stream. This MUST match
+// the RelayLogControl message.
+// Level - DEBUG=0, INFO=1, WARN=2, FATAL=3 system displays level and greater
+// Mask - a bit mask. Set to 0xffff to display every type
+// Time - set to 1 to turn on timestamp, 0 to turn it off
+// Location - set to 1 to turn on file/line stamp, 0 to turn it off
+// RemoteInfos - set to 1 to log llinfo to the log server (in production)
+{
+ LogControl Low Trusted Unencoded
+ {
+ Options Single
+ { Level U8 }
+ { Mask U32 }
+ { Time BOOL }
+ { Location BOOL }
+ { RemoteInfos BOOL }
+ }
+}
+
+// RelayLogControl - Just like log control, but relayed around the
+// server side to adjust the log level. This is in a separate message
+// because the handler is a bit different, only implmented on some
+// processes, and requires application level knowledge. This MUST match
+// the LogControl message.
+{
+ RelayLogControl Low Trusted Unencoded
+ {
+ Options Single
+ { Level U8 }
+ { Mask U32 }
+ { Time BOOL }
+ { Location BOOL }
+ { RemoteInfos BOOL }
+ }
+}
+
+
+// LogMessages
+// Turns on or off message system logging
+{
+ LogMessages Low Trusted Unencoded
+ {
+ Options Single
+ { Enable BOOL } // BOOL
+ }
+}
+
+
+// *************************************************************************
+// SpaceServer to Simulator Messages
+// ************************************************************************
+
+// Neighbor List - Passed anytime neighbors change
+{
+ NeighborList High Trusted Unencoded
+ {
+ NeighborBlock Multiple 4
+ { IP IPADDR }
+ { Port IPPORT }
+ { PublicIP IPADDR }
+ { PublicPort IPPORT }
+ { RegionID LLUUID }
+ { Name Variable 1 } // string
+ { SimAccess U8 }
+ }
+}
+
+
+// Simulator Assignment - Tells a simulator where it is and who it's
+// neighbors are
+{
+ SimulatorAssign Low Trusted Zerocoded
+ {
+ RegionInfo Single
+ { GridsPerEdge S32 }
+ { MetersPerGrid F32 }
+ { Handle U64 }
+ { UsecSinceStart U64 }
+ { SecPerDay U32 }
+ { SecPerYear U32 }
+ { SunDirection LLVector3 }
+ { SunAngVelocity LLVector3 }
+ { IP IPADDR }
+ { Port IPPORT }
+ }
+ {
+ NeighborBlock Multiple 4
+ { IP IPADDR }
+ { Port IPPORT }
+ { PublicIP IPADDR }
+ { PublicPort IPPORT }
+ { Name Variable 1 } // string
+ { SimAccess U8 }
+ }
+}
+
+// SpaceServerSimulatorTimeMessage - Allows simulator to resynch to world time
+{
+ SpaceServerSimulatorTimeMessage Low Trusted Unencoded
+ {
+ TimeInfo Single
+ { UsecSinceStart U64 }
+ { SecPerDay U32 }
+ { SecPerYear U32 }
+ { SunDirection LLVector3 }
+ { SunPhase F32 }
+ { SunAngVelocity LLVector3 }
+ }
+}
+
+// ClosestSimulator - Passes the closest simulator back to a simulator
+{
+ ClosestSimulator Medium Trusted Unencoded
+ {
+ SimulatorBlock Single
+ { IP IPADDR }
+ { Port IPPORT }
+ { Handle U64 }
+ }
+ {
+ Viewer Single
+ { ID LLUUID }
+ }
+}
+
+// AvatarTextureUpdate
+// simulator -> dataserver
+// reliable
+{
+ AvatarTextureUpdate Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { TexturesChanged BOOL }
+ }
+ {
+ WearableData Variable
+ { CacheID LLUUID }
+ { TextureIndex U8 }
+ { HostName Variable 1 }
+ }
+ {
+ TextureData Variable
+ { TextureID LLUUID }
+ }
+}
+
+
+// SimulatorMapUpdate
+// simulator -> dataserver
+// reliable
+{
+ SimulatorMapUpdate Low Trusted Unencoded
+ {
+ MapData Single
+ { Flags U32 }
+ }
+}
+
+// SimulatorSetMap
+// simulator -> dataserver
+// reliable
+// Used to upload a map image into the database (currently used only for Land For Sale)
+{
+ SimulatorSetMap Low Trusted Unencoded
+ {
+ MapData Single
+ { RegionHandle U64 }
+ { Type S32 }
+ { MapImage LLUUID }
+ }
+}
+
+// SubscribeLoad
+// spaceserver -> simulator
+// reliable
+{
+ SubscribeLoad Low Trusted Unencoded
+}
+
+// UnsubscribeLoad
+// spaceserver -> simulator
+// reliable
+{
+ UnsubscribeLoad Low Trusted Unencoded
+}
+
+
+// ************************************************************************
+// Simulator to SpaceServer Messages
+// ************************************************************************
+
+// Simulator Start - Tells spaceserver that simulator is online and wants to be
+// assigned.
+{
+ SimulatorStart Low Trusted Unencoded
+ {
+ ControlPort Single
+ { Port IPPORT }
+ { PublicIP IPADDR }
+ { PublicPort IPPORT }
+ }
+ {
+ PositionSuggestion Single
+ { Ignore BOOL } // if non-zero, SS should put it in the next available slot
+ { GridX S32 }
+ { GridY S32 }
+ }
+}
+
+// SimulatorReady - indicates the sim has finished loading its state
+// and is ready to receive updates from others
+{
+ SimulatorReady Low Trusted Zerocoded
+ {
+ SimulatorBlock Single
+ { SimName Variable 1 }
+ { SimAccess U8 }
+ { RegionFlags U32 }
+ { RegionID LLUUID }
+ { EstateID U32 }
+ { ParentEstateID U32 }
+ }
+ {
+ TelehubBlock Single
+ { HasTelehub BOOL }
+ { TelehubPos LLVector3 }
+ }
+}
+
+// TelehubInfo - fill in the UI for telehub creation floater.
+// sim -> viewer
+// reliable
+{
+ TelehubInfo Low Trusted Unencoded
+ {
+ TelehubBlock Single
+ { ObjectID LLUUID } // null if no telehub
+ { ObjectName Variable 1 } // string
+ { TelehubPos LLVector3 } // fallback if viewer can't find object
+ { TelehubRot LLQuaternion }
+ }
+ {
+ SpawnPointBlock Variable
+ { SpawnPointPos LLVector3 } // relative to telehub position
+ }
+}
+
+// SimulatorPresentAtLocation - indicates that the sim is present at a grid
+// location and passes what it believes its neighbors are
+{
+ SimulatorPresentAtLocation Low Trusted Unencoded
+ {
+ SimulatorPublicHostBlock Single
+ { Port IPPORT }
+ { SimulatorIP IPADDR }
+ { GridX U32 }
+ { GridY U32 }
+ }
+ {
+ NeighborBlock Multiple 4
+ { IP IPADDR }
+ { Port IPPORT }
+ }
+ {
+ SimulatorBlock Single
+ { SimName Variable 1 }
+ { SimAccess U8 }
+ { RegionFlags U32 }
+ { RegionID LLUUID }
+ { EstateID U32 }
+ { ParentEstateID U32 }
+ }
+ {
+ TelehubBlock Variable
+ { HasTelehub BOOL }
+ { TelehubPos LLVector3 }
+ }
+}
+
+// SimulatorLoad
+// simulator -> spaceserver
+// reliable
+{
+ SimulatorLoad Low Trusted Unencoded
+ {
+ SimulatorLoad Single
+ { TimeDilation F32 }
+ { AgentCount S32 }
+ { CanAcceptAgents BOOL }
+ }
+ {
+ AgentList Variable
+ { CircuitCode U32 }
+ { X U8 }
+ { Y U8 }
+ }
+}
+
+// Simulator Shutdown Request - Tells spaceserver that a simulator is trying to shutdown
+{
+ SimulatorShutdownRequest Low Trusted Unencoded
+}
+
+// ****************************************************************************
+// Presense messages
+// ****************************************************************************
+
+// sim -> dataserver
+{
+ RegionPresenceRequestByRegionID Low Trusted Unencoded
+ {
+ RegionData Variable
+ { RegionID LLUUID }
+ }
+}
+
+sim -> dataserver
+{
+ RegionPresenceRequestByHandle Low Trusted Unencoded
+ {
+ RegionData Variable
+ { RegionHandle U64 }
+ }
+}
+
+// dataserver -> sim
+{
+ RegionPresenceResponse Low Trusted Zerocoded
+ {
+ RegionData Variable
+ { RegionID LLUUID }
+ { RegionHandle U64 }
+ { InternalRegionIP IPADDR }
+ { ExternalRegionIP IPADDR }
+ { RegionPort IPPORT }
+ { ValidUntil F64 }
+ { Message Variable 1 }
+ }
+}
+
+// Record agent presence - this totally supercedes the TrackAgentSession
+// and ClearAgentSessions functionality
+{
+ RecordAgentPresence Low Trusted Unencoded
+ {
+ RegionData Single
+ { RegionID LLUUID }
+ }
+ {
+ AgentData Variable
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SecureSessionID LLUUID }
+ { LocalX S16 }
+ { LocalY S16 }
+ { TimeToLive S32 } // in seconds
+ { Status S32 }
+ { EstateID U32 }
+ }
+}
+
+// Erase a set of agent presence records. Useful during logout or kick.
+{
+ EraseAgentPresence Low Trusted Unencoded
+ {
+ AgentData Variable
+ { AgentID LLUUID }
+ }
+}
+
+// request IP and port for agents
+{
+ AgentPresenceRequest Low Trusted Unencoded
+ {
+ AgentData Variable
+ { AgentID LLUUID }
+ }
+}
+
+// response for agent locations
+{
+ AgentPresenceResponse Low Trusted Unencoded
+ {
+ AgentData Variable
+ { AgentID LLUUID }
+ { RegionIP IPADDR }
+ { RegionPort IPPORT }
+ { ValidUntil F64 }
+ { EstateID U32 }
+ }
+}
+
+
+// ****************************************************************************
+// Simulator to dataserver messages
+// ****************************************************************************
+
+// Updates SimName, EstateID and SimAccess using RegionID as a key
+{
+ UpdateSimulator Low Trusted Unencoded
+ {
+ SimulatorInfo Single
+ { RegionID LLUUID }
+ { SimName Variable 1 }
+ { EstateID U32 }
+ { SimAccess U8 }
+ }
+}
+
+
+// The simulator sends out this message from time to time
+{
+ TrackAgentSession Low Trusted Unencoded
+ {
+ RegionData Single
+ { RegionX F32 }
+ { RegionY F32 }
+ { SpaceIP IPADDR }
+ { EstateID U32 }
+ { AgentCount U32 }
+ }
+ {
+ SessionInfo Variable
+ { SessionID LLUUID }
+ { ViewerIP IPADDR }
+ { ViewerPort IPPORT }
+ { GlobalX F64 }
+ { GlobalY F64 }
+ }
+}
+
+
+// clear out sessions for this sim, because it's coming up or going down
+{
+ ClearAgentSessions Low Trusted Unencoded
+ {
+ RegionInfo Single
+ { RegionX U32 }
+ { RegionY U32 }
+ { SpaceIP IPADDR }
+ }
+}
+
+// record dwell time.
+{
+ LogDwellTime Low Trusted Unencoded
+ {
+ DwellInfo Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Duration F32 }
+ { SimName Variable 1 }
+ { RegionX U32 }
+ { RegionY U32 }
+ { AvgAgentsInView U8 }
+ { AvgViewerFPS U8 }
+ }
+}
+
+// Disabled feature response message
+{
+ FeatureDisabled Low Trusted Unencoded
+ {
+ FailureInfo Single
+ { ErrorMessage Variable 1 }
+ { AgentID LLUUID }
+ { TransactionID LLUUID }
+ }
+}
+
+
+// record lost money transactions. This message could be generated
+// from either the simulator or the dataserver, depending on how
+// the transaction failed.
+{
+ LogFailedMoneyTransaction Low Trusted Unencoded
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ { TransactionTime U32 } // utc seconds since epoch
+ { TransactionType S32 } // see lltransactiontypes.h
+ { SourceID LLUUID }
+ { DestID LLUUID } // destination of the transfer
+ { Flags U8 }
+ { Amount S32 }
+ { SimulatorIP IPADDR } // U32 encoded IP
+ { GridX U32 }
+ { GridY U32 }
+ { FailureType U8 }
+ }
+}
+
+// complaint/bug-report - sim -> dataserver. see UserReport for details.
+// reliable
+{
+ UserReportInternal Low Trusted Zerocoded
+ {
+ ReportData Single
+ { ReportType U8 }
+ { Category U8 }
+ { ReporterID LLUUID }
+ { ViewerPosition LLVector3 }
+ { AgentPosition LLVector3 }
+ { ScreenshotID LLUUID }
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { LastOwnerID LLUUID }
+ { CreatorID LLUUID }
+ { SimName Variable 1 }
+ { Summary Variable 1 }
+ { Details Variable 2 }
+ { VersionString Variable 1 }
+ }
+ {
+ MeanCollision Variable
+ { Perp LLUUID }
+ { Time U32 }
+ { Mag F32 }
+ { Type U8 }
+ }
+}
+
+// SetSimStatusInDatabase
+// alters the "simulator" table in the database
+// sim -> dataserver
+// reliable
+{
+ SetSimStatusInDatabase Low Trusted Unencoded
+ {
+ Data Single
+ { RegionID LLUUID }
+ { HostName Variable 1 }
+ { X S32 }
+ { Y S32 }
+ { PID S32 }
+ { AgentCount S32 }
+ { TimeToLive S32 } // in seconds
+ { Status Variable 1 }
+ }
+}
+
+// SetSimPresenceInDatabase
+// updates the "presence" table in the database to ensure
+// that a given simulator is present and valid for a set amount of
+// time
+{
+ SetSimPresenceInDatabase Low Trusted Unencoded
+ {
+ SimData Single
+ { RegionID LLUUID }
+ { HostName Variable 1 }
+ { GridX U32 }
+ { GridY U32 }
+ { PID S32 }
+ { AgentCount S32 }
+ { TimeToLive S32 } // in seconds
+ { Status Variable 1 }
+ }
+}
+
+// ***************************************************************************
+// Economy messages
+// ***************************************************************************
+
+// once we use local stats, this will include a region handle
+{
+ EconomyDataRequest Low NotTrusted Unencoded
+}
+
+// dataserver to sim, response w/ econ data
+{
+ EconomyData Low Trusted Zerocoded
+ {
+ Info Single
+ { ObjectCapacity S32 }
+ { ObjectCount S32 }
+ { PriceEnergyUnit S32 }
+ { PriceObjectClaim S32 }
+ { PricePublicObjectDecay S32 }
+ { PricePublicObjectDelete S32 }
+ { PriceParcelClaim S32 }
+ { PriceParcelClaimFactor F32 }
+ { PriceUpload S32 }
+ { PriceRentLight S32 }
+ { TeleportMinPrice S32 }
+ { TeleportPriceExponent F32 }
+ { EnergyEfficiency F32 }
+ { PriceObjectRent F32 }
+ { PriceObjectScaleFactor F32 }
+ { PriceParcelRent S32 }
+ { PriceGroupCreate S32 }
+ }
+}
+
+// ***************************************************************************
+// Search messages
+// ***************************************************************************
+
+// AvatarPickerRequest
+// Get a list of names to select a person
+// viewer -> sim -> data
+// reliable
+{
+ AvatarPickerRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { QueryID LLUUID }
+ }
+ {
+ Data Single
+ { Name Variable 1 }
+ }
+}
+
+// backend implementation which tracks if the user is a god.
+{
+ AvatarPickerRequestBackend Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { QueryID LLUUID }
+ { GodLevel U8 }
+ }
+ {
+ Data Single
+ { Name Variable 1 }
+ }
+}
+
+// AvatarPickerReply
+// List of names to select a person
+// reliable
+{
+ AvatarPickerReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { QueryID LLUUID }
+ }
+ {
+ Data Variable
+ { AvatarID LLUUID }
+ { FirstName Variable 1 }
+ { LastName Variable 1 }
+ }
+}
+
+// PlacesQuery
+// Used for getting a list of places for the group land panel
+// and the user land holdings panel. NOT for the directory.
+{
+ PlacesQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { QueryID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { Category S8 }
+ { SimName Variable 1 }
+ }
+}
+
+// PlacesReply
+// dataserver -> simulator -> viewer
+// If the user has specified a location, use that to compute
+// global x,y,z. Otherwise, use center of the AABB.
+// reliable
+{
+ PlacesReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { QueryID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ }
+ {
+ QueryData Variable
+ { OwnerID LLUUID }
+ { Name Variable 1 }
+ { Desc Variable 1 }
+ { ActualArea S32 }
+ { BillableArea S32 }
+ { Flags U8 }
+ { GlobalX F32 } // meters
+ { GlobalY F32 } // meters
+ { GlobalZ F32 } // meters
+ { SimName Variable 1 }
+ { SnapshotID LLUUID }
+ { Dwell F32 }
+ { Price S32 }
+ }
+}
+
+// DirFindQuery viewer->sim
+// Message to start asking questions for the directory
+{
+ DirFindQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { QueryStart S32 } // prev/next page support
+ }
+}
+
+// DirFindQueryBackend sim->data
+// Trusted message generated by receipt of DirFindQuery to sim.
+{
+ DirFindQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { QueryStart S32 } // prev/next page support
+ { EstateID U32 }
+ { Godlike BOOL }
+ }
+}
+
+
+// DirPlacesQuery viewer->sim
+// Used for the Find directory of places
+{
+ DirPlacesQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { Category S8 }
+ { SimName Variable 1 }
+ { QueryStart S32 }
+ }
+}
+
+// DirPlacesQueryBackend sim->dataserver
+// Used for the Find directory of places.
+{
+ DirPlacesQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { Category S8 }
+ { SimName Variable 1 }
+ { EstateID U32 }
+ { Godlike BOOL }
+ { QueryStart S32 }
+ }
+}
+
+// DirPlacesReply dataserver->sim->viewer
+// If the user has specified a location, use that to compute
+// global x,y,z. Otherwise, use center of the AABB.
+// reliable
+{
+ DirPlacesReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Variable
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { ParcelID LLUUID }
+ { Name Variable 1 }
+ { ForSale BOOL }
+ { Auction BOOL }
+ { ReservedNewbie BOOL }
+ { Dwell F32 }
+ }
+}
+
+// DirPeopleReply
+{
+ DirPeopleReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { AgentID LLUUID }
+ { FirstName Variable 1 }
+ { LastName Variable 1 }
+ { Group Variable 1 }
+ { Online BOOL }
+ { Reputation S32 }
+ }
+}
+
+// DirEventsReply
+{
+ DirEventsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { OwnerID LLUUID }
+ { Name Variable 1 }
+ { EventID U32 }
+ { Date Variable 1 }
+ { UnixTime U32 }
+ { EventFlags U32 }
+ }
+}
+
+// DirGroupsReply
+// dataserver -> userserver -> viewer
+// reliable
+{
+ DirGroupsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { GroupID LLUUID }
+ { GroupName Variable 1 } // string
+ { Members S32 }
+ { OpenEnrollment BOOL }
+ { MembershipFee S32 }
+ }
+}
+
+
+// DirClassifiedQuery viewer->sim
+// reliable
+{
+ DirClassifiedQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { Category U32 }
+ }
+}
+
+// DirClassifiedQueryBackend sim->dataserver
+// reliable
+{
+ DirClassifiedQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { Category U32 }
+ { EstateID U32 }
+ { Godlike BOOL }
+ }
+}
+
+// DirClassifiedReply dataserver->sim->viewer
+// reliable
+{
+ DirClassifiedReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { ClassifiedID LLUUID }
+ { Name Variable 1 }
+ { ClassifiedFlags U8 }
+ { CreationDate U32 }
+ { ExpirationDate U32 }
+ { PriceForListing S32 }
+ }
+}
+
+
+// AvatarClassifiedReply
+// dataserver -> simulator -> viewer
+// Send the header information for this avatar's classifieds
+// This fills in the tabs of the Classifieds panel.
+// reliable
+{
+ AvatarClassifiedReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { TargetID LLUUID }
+ }
+ {
+ Data Variable
+ { ClassifiedID LLUUID }
+ { Name Variable 1 }
+ }
+}
+
+
+// ClassifiedInfoRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ ClassifiedInfoRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ClassifiedID LLUUID }
+ }
+}
+
+
+// ClassifiedInfoReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ ClassifiedInfoReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Data Single
+ { ClassifiedID LLUUID }
+ { CreatorID LLUUID }
+ { CreationDate U32 }
+ { ExpirationDate U32 }
+ { Category U32 }
+ { Name Variable 1 }
+ { Desc Variable 2 }
+ { ParcelID LLUUID }
+ { ParentEstate U32 }
+ { SnapshotID LLUUID }
+ { SimName Variable 1 }
+ { PosGlobal LLVector3d }
+ { ParcelName Variable 1 }
+ { ClassifiedFlags U8 }
+ { PriceForListing S32 }
+ }
+}
+
+
+// ClassifiedInfoUpdate
+// Update a classified. ParcelID and EstateID are set
+// on the simulator as the message passes through.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ ClassifiedInfoUpdate Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ClassifiedID LLUUID }
+ { Category U32 }
+ { Name Variable 1 }
+ { Desc Variable 2 }
+ { ParcelID LLUUID }
+ { ParentEstate U32 }
+ { SnapshotID LLUUID }
+ { PosGlobal LLVector3d }
+ { ClassifiedFlags U8 }
+ { PriceForListing S32 }
+ }
+}
+
+
+// ClassifiedDelete
+// Delete a classified from the database.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ ClassifiedDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ClassifiedID LLUUID }
+ }
+}
+
+// ClassifiedGodDelete
+// Delete a classified from the database.
+// QueryID is needed so database can send a repeat list of
+// classified.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ ClassifiedGodDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ClassifiedID LLUUID }
+ { QueryID LLUUID }
+ }
+}
+
+
+
+// DirPicksQuery viewer->sim
+// reliable
+{
+ DirPicksQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ }
+}
+
+// DirPicksQueryBackend sim->dataserver
+// reliable
+{
+ DirPicksQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ { EstateID U32 }
+ { Godlike BOOL }
+ }
+}
+
+// DirPicksReply dataserver->sim->viewer
+// reliable
+{
+ DirPicksReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { PickID LLUUID }
+ { Name Variable 1 }
+ { Enabled BOOL }
+ }
+}
+
+
+// DirLandQuery viewer->sim
+// Special query for the land for sale/auction panel.
+// reliable
+{
+ DirLandQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ { ForSale BOOL }
+ { Auction BOOL }
+ { ReservedNewbie BOOL }
+ }
+}
+
+// DirLandQueryBackend sim->dataserver
+// Special query for the land for sale/auction panel.
+{
+ DirLandQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ { ForSale BOOL }
+ { Auction BOOL }
+ { ReservedNewbie BOOL }
+ { EstateID U32 }
+ { Godlike BOOL }
+ }
+}
+
+// DirLandReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ DirLandReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { ParcelID LLUUID }
+ { Name Variable 1 }
+ { Auction BOOL }
+ { ForSale BOOL }
+ { ReservedNewbie BOOL }
+ { SalePrice S32 }
+ { ActualArea S32 }
+ }
+}
+
+// DirPopularQuery viewer->sim
+// Special query for the land for sale/auction panel.
+// reliable
+{
+ DirPopularQuery Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ }
+}
+
+// DirPopularQueryBackend sim->dataserver
+// Special query for the land for sale/auction panel.
+// reliable
+{
+ DirPopularQueryBackend Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryFlags U32 }
+ { EstateID U32 }
+ { Godlike BOOL }
+ }
+}
+
+// DirPopularReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ DirPopularReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ QueryReplies Variable
+ { ParcelID LLUUID }
+ { Name Variable 1 }
+ { Dwell F32 }
+ }
+}
+
+// ParcelInfoRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ ParcelInfoRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ParcelID LLUUID }
+ }
+}
+
+// ParcelInfoReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ ParcelInfoReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Data Single
+ { ParcelID LLUUID }
+ { OwnerID LLUUID }
+ { Name Variable 1 }
+ { Desc Variable 1 }
+ { ActualArea S32 }
+ { BillableArea S32 }
+ { Flags U8 }
+ { GlobalX F32 } // meters
+ { GlobalY F32 } // meters
+ { GlobalZ F32 } // meters
+ { SimName Variable 1 }
+ { SnapshotID LLUUID }
+ { Dwell F32 }
+ { SalePrice S32 }
+ { AuctionID S32 }
+ }
+}
+
+
+// ParcelObjectOwnersRequest
+// viewer -> simulator
+// reliable
+{
+ ParcelObjectOwnersRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ }
+}
+
+// simulator -> dataserver
+// reliable
+{
+ OnlineStatusRequest Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { QueryID LLUUID }
+ { EstateID U32 }
+ { Godlike BOOL }
+ { SpaceIP IPADDR } // check online for right farm
+ }
+ {
+ Data Variable
+ { ID LLUUID }
+ }
+}
+
+// dataserver -> simulator
+// reliable
+{
+ OnlineStatusReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { QueryID LLUUID }
+ }
+ {
+ Data Variable
+ { ID LLUUID } // only online agents are returned
+ }
+}
+
+// ParcelObjectOwnersReply
+// simulator -> viewer
+// reliable
+{
+ ParcelObjectOwnersReply Low Trusted Zerocoded
+ {
+ Data Variable
+ { OwnerID LLUUID }
+ { IsGroupOwned BOOL }
+ { Count S32 }
+ { OnlineStatus BOOL }
+ }
+}
+
+// GroupNoticeListRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ GroupNoticesListRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ }
+}
+
+// GroupNoticesListReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ GroupNoticesListReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ Data Variable
+ { NoticeID LLUUID }
+ { Timestamp U32 }
+ { FromName Variable 2 }
+ { Subject Variable 2 }
+ { HasAttachment BOOL }
+ { AssetType U8 }
+ }
+}
+
+// GroupNoticeRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ GroupNoticeRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupNoticeID LLUUID }
+ }
+}
+
+// GroupNoticeAdd
+// Add a group notice.
+// simulator -> dataserver
+// reliable
+{
+ GroupNoticeAdd Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ MessageBlock Single
+ { ToGroupID LLUUID }
+ { ID LLUUID }
+ { Dialog U8 }
+ { FromAgentName Variable 1 }
+ { Message Variable 2 }
+ { BinaryBucket Variable 2 }
+ }
+}
+
+
+// GroupNoticeDelete
+// Delete a group notice from the database.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ GroupNoticeDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupNoticeID LLUUID }
+ { GroupID LLUUID }
+ }
+}
+
+
+
+// ****************************************************************************
+// Teleport messages
+//
+// The teleport messages are numerous, so I have attempted to give them a
+// consistent naming convention. Since there is a bit of glob pattern
+// aliasing, the rules are applied in order.
+//
+// Teleport* - viewer->sim or sim->viewer message which announces a
+// teleportation request, progrees, start, or end.
+// Data* - sim->data or data->sim trusted message.
+// Space* - sim->space or space->sim trusted messaging
+// *Lure - A lure message to pass around information.
+//
+// All actual viewer teleports will begin with a Teleport* message and
+// end in a TeleportStart, TeleportLocal or TeleportFailed message. The TeleportFailed
+// message may be returned by any process and must be routed through the
+// teleporting agent's simulator and back to the viewer.
+// ****************************************************************************
+
+// TeleportRequest
+// viewer -> sim specifying exact teleport destination
+{
+ TeleportRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Info Single
+ { RegionID LLUUID }
+ { Position LLVector3 }
+ { LookAt LLVector3 }
+ }
+}
+
+// TeleportLocationRequest
+// viewer -> sim specifying exact teleport destination
+{
+ TeleportLocationRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Info Single
+ { RegionHandle U64 }
+ { Position LLVector3 }
+ { LookAt LLVector3 }
+ }
+}
+
+// TeleportLocal
+// sim -> viewer reply telling the viewer that we've successfully TP'd
+// to somewhere else within the sim
+{
+ TeleportLocal Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { LocationID U32 }
+ { Position LLVector3 } // region
+ { LookAt LLVector3 }
+ { TeleportFlags U32 }
+ }
+}
+
+// TeleportLandmarkRequest viewer->sim
+// teleport to landmark asset ID destination. use LLUUD::null for home.
+{
+ TeleportLandmarkRequest Low NotTrusted Zerocoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { LandmarkID LLUUID }
+ }
+}
+
+// TeleportProgress sim->viewer
+// Tell the agent how the teleport is going.
+{
+ TeleportProgress Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Info Single
+ { TeleportFlags U32 }
+ { Message Variable 1 } // string
+ }
+}
+
+// DataHomeLocationRequest sim->data
+// Request
+{
+ DataHomeLocationRequest Low Trusted Zerocoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { KickedFromEstateID U32 }
+ }
+}
+
+// DataHomeLocationReply data->sim
+// response is the location of agent home.
+{
+ DataHomeLocationReply Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { RegionHandle U64 }
+ { Position LLVector3 } // region coords
+ { LookAt LLVector3 }
+ }
+}
+
+// SpaceLocationTeleportRequest sim->space
+// Reuqest for info about remote location
+{
+ SpaceLocationTeleportRequest Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { CircuitCode U32 }
+ { RegionHandle U64 }
+ { Position LLVector3 } // region
+ { LookAt LLVector3 }
+ { TravelAccess U8 }
+ { ParentEstateID U32 }
+ { TeleportFlags U32 }
+ }
+}
+
+// SpaceLocationTeleportReply space->sim
+// with info about remote location
+{
+ SpaceLocationTeleportReply Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { LocationID U32 }
+ { SimIP IPADDR }
+ { SimPort IPPORT }
+ { RegionHandle U64 }
+ { Position LLVector3 } // region
+ { LookAt LLVector3 }
+ { SimName Variable 1 }
+ { SimAccess U8 }
+ { TeleportFlags U32 }
+ }
+}
+
+// TeleportFinish sim->viewer
+// called when all of the information has been collected and readied for
+// the agent.
+{
+ TeleportFinish Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { LocationID U32 }
+ { SimIP IPADDR }
+ { SimPort IPPORT }
+ { RegionHandle U64 }
+ { SeedCapability Variable 2 } // URL
+ { SimAccess U8 }
+ { TeleportFlags U32 }
+ }
+}
+
+// StartLure viewer->sim
+// Sent from viewer to the local simulator to lure target id to near
+// agent id. This will generate an instant message that will be routed
+// through the space server and out to the userserver. When that IM
+// goes through the userserver and the TargetID is online, the
+// userserver will send an InitializeLure to the spaceserver. When that
+// packet is acked, the original instant message is finally forwarded to
+// TargetID.
+{
+ StartLure Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Info Single
+ { LureType U8 }
+ { Message Variable 1 }
+ }
+ {
+ TargetData Variable
+ { TargetID LLUUID }
+ }
+}
+
+// TeleportLureRequest viewer->sim
+// Message from target of lure to begin the teleport process on the
+// local simulator.
+{
+ TeleportLureRequest Low NotTrusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { LureID LLUUID }
+ { TeleportFlags U32 }
+ }
+}
+
+// TeleportCancel viewer->sim
+// reliable
+{
+ TeleportCancel Low NotTrusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// CompleteLure sim->space
+// message with final necessary info about accepted lure. The
+// spaceserver will reply with a SpaceLocationTeleportReply or
+// TeleportFailed and it becomes like any other teleport.
+{
+ CompleteLure Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { LureID LLUUID }
+ { CircuitCode U32 }
+ { TravelAccess U8 }
+ { ParentEstateID U32 }
+ { TeleportFlags U32 }
+ }
+}
+
+// TeleportStart sim->viewer
+// announce a successful teleport request to the viewer.
+{
+ TeleportStart Low Trusted Unencoded
+ {
+ Info Single
+ { TeleportFlags U32 }
+ }
+}
+
+// TeleportFailed somehwere->sim->viewer
+// announce failure of teleport request
+{
+ TeleportFailed Low Trusted Unencoded
+ {
+ Info Single
+ { AgentID LLUUID }
+ { Reason Variable 1 } // string
+ }
+}
+
+// ***************************************************************************
+// Leader Board messages
+// ***************************************************************************
+{
+ LeaderBoardRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Type S32 }
+ }
+}
+
+{
+ LeaderBoardData Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ BoardData Single
+ { Type S32 }
+ { MinPlace S32 }
+ { MaxPlace S32 }
+ { TimeString Variable 1 } // string
+ }
+ {
+ Entry Variable
+ { Sequence S32 }
+ { Place S32 }
+ { ID LLUUID }
+ { Score S32 }
+ { Name Fixed 32 } // only send 32 characters of the name, to fit in an MTU
+ }
+}
+
+// ***************************************************************************
+// Viewer to Simulator Messages
+// ***************************************************************************
+
+// Undo
+{
+ Undo Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectID LLUUID }
+ }
+}
+
+
+// Redo
+{
+ Redo Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectID LLUUID }
+ }
+}
+
+// UndoLand
+{
+ UndoLand Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+
+// RedoLand
+{
+ RedoLand Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// AgentPause - viewer occasionally will block, inform simulator of this fact
+{
+ AgentPause Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum U32 } // U32, used by both pause and resume. Non-increasing numbers are ignored.
+ }
+}
+
+// AgentResume - unblock the agent
+{
+ AgentResume Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum U32 } // U32, used by both pause and resume. Non-increasing numbers are ignored.
+ }
+}
+
+
+// AgentUpdate - Camera info sent from viewer to simulator
+// or, more simply, two axes and compute cross product
+// State data is temporary, indicates current behavior state:
+// 0 = walking
+// 1 = mouselook
+// 2 = typing
+//
+// Center is region local (JNC 8.16.2001)
+// Camera center is region local (JNC 8.29.2001)
+{
+ AgentUpdate High NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { BodyRotation LLQuaternion }
+ { HeadRotation LLQuaternion }
+ { State U8 }
+ { CameraCenter LLVector3 }
+ { CameraAtAxis LLVector3 }
+ { CameraLeftAxis LLVector3 }
+ { CameraUpAxis LLVector3 }
+ { Far F32 }
+ { ControlFlags U32 }
+ { Flags U8 }
+ }
+}
+
+// ChatFromViewer
+// Specifies the text to be said and the "type",
+// normal speech, shout, whisper.
+// with the specified radius
+{
+ ChatFromViewer Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ChatData Single
+ { Message Variable 2 }
+ { Type U8 }
+ { Channel S32 }
+ }
+}
+
+
+// AgentThrottle
+{
+ AgentThrottle Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { CircuitCode U32 }
+ }
+ {
+ Throttle Single
+ { GenCounter U32 }
+ { Throttles Variable 1 }
+ }
+}
+
+
+// AgentFOV - Update to agent's field of view, angle is vertical, single F32 float in radians
+{
+ AgentFOV Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { CircuitCode U32 }
+ }
+ {
+ FOVBlock Single
+ { GenCounter U32 }
+ { VerticalAngle F32 }
+ }
+}
+
+
+// AgentHeightWidth - Update to height and aspect, sent as height/width to save space
+// Usually sent when window resized or created
+{
+ AgentHeightWidth Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { CircuitCode U32 }
+ }
+ {
+ HeightWidthBlock Single
+ { GenCounter U32 }
+ { Height U16 }
+ { Width U16 }
+ }
+}
+
+
+// AgentSetAppearance - Update to agent appearance
+{
+ AgentSetAppearance Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum U32 } // U32, Increases every time the appearance changes. A value of 0 resets.
+ { Size LLVector3 }
+ }
+ {
+ WearableData Variable
+ { CacheID LLUUID }
+ { TextureIndex U8 }
+ }
+ {
+ ObjectData Single
+ { TextureEntry Variable 2 }
+ }
+ {
+ VisualParam Variable
+ { ParamValue U8 }
+ }
+}
+
+// AgentAnimation - Update animation state
+// viewer --> simulator
+{
+ AgentAnimation High NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AnimationList Variable
+ { AnimID LLUUID }
+ { StartAnim BOOL }
+ }
+}
+
+// AgentRequestSit - Try to sit on an object
+{
+ AgentRequestSit High NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TargetObject Single
+ { TargetID LLUUID }
+ { Offset LLVector3 }
+ }
+}
+
+// AgentSit - Actually sit on object
+{
+ AgentSit High NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// AgentQuit - Sent by viewer when viewer exits normally
+// Fuse is used to allow Reset to be passed to neighbors
+// *NOTE: obsolete
+{
+ AgentQuit Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// quit message sent between simulators
+{
+ AgentQuitCopy Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ FuseBlock Single
+ { ViewerCircuitCode U32 }
+ }
+}
+
+
+// Request Image - Sent by the viewer to request a specified image at a specified resolution
+
+{
+ RequestImage High NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RequestImage Variable
+ { Image LLUUID }
+ { DiscardLevel S8 }
+ { DownloadPriority F32 }
+ { Packet U32 }
+ { Type U8 }
+ }
+}
+
+// ImageNotInDatabase
+// Simulator informs viewer that a requsted image definitely does not exist in the asset database
+{
+ ImageNotInDatabase Low Trusted Unencoded
+ {
+ ImageID Single
+ { ID LLUUID }
+ }
+}
+
+// RebakeAvatarTextures
+// simulator -> viewer request when a temporary baked avatar texture is not found
+{
+ RebakeAvatarTextures Low Trusted Unencoded
+ {
+ TextureData Single
+ { TextureID LLUUID }
+ }
+}
+
+
+// SetAlwaysRun
+// Lets the viewer choose between running and walking
+{
+ SetAlwaysRun Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { AlwaysRun BOOL }
+ }
+}
+
+// ObjectAdd - create new object in the world
+// Simulator will assign ID and send message back to signal
+// object actually created.
+//
+// AddFlags (see also ObjectUpdate)
+// 0x01 - use physics
+// 0x02 - create selected
+//
+// If only one ImageID is sent for an object type that has more than
+// one face, the same image is repeated on each subsequent face.
+//
+// Data field is opaque type-specific data for this object
+{
+ ObjectAdd Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ ObjectData Single
+ { PCode U8 }
+ { Material U8 }
+ { AddFlags U32 } // see object_flags.h
+
+ { PathCurve U8 }
+ { ProfileCurve U8 }
+ { PathBegin U8 } // 0 to 1, quanta = 0.01
+ { PathEnd U8 } // 0 to 1, quanta = 0.01
+ { PathScaleX U8 } // 0 to 1, quanta = 0.01
+ { PathScaleY U8 } // 0 to 1, quanta = 0.01
+ { PathShearX U8 } // -.5 to .5, quanta = 0.01
+ { PathShearY U8 } // -.5 to .5, quanta = 0.01
+ { PathTwist S8 } // -1 to 1, quanta = 0.01
+ { PathTwistBegin S8 } // -1 to 1, quanta = 0.01
+ { PathRadiusOffset S8 } // -1 to 1, quanta = 0.01
+ { PathTaperX S8 } // -1 to 1, quanta = 0.01
+ { PathTaperY S8 } // -1 to 1, quanta = 0.01
+ { PathRevolutions U8 } // 0 to 3, quanta = 0.015
+ { PathSkew S8 } // -1 to 1, quanta = 0.01
+ { ProfileBegin U8 } // 0 to 1, quanta = 0.01
+ { ProfileEnd U8 } // 0 to 1, quanta = 0.01
+ { ProfileHollow U8 } // 0 to 1, quanta = 0.01
+
+ { BypassRaycast U8 }
+ { RayStart LLVector3 }
+ { RayEnd LLVector3 }
+ { RayTargetID LLUUID }
+ { RayEndIsIntersection U8 }
+
+ { Scale LLVector3 }
+ { Rotation LLQuaternion }
+
+ { State U8 }
+ }
+}
+
+
+// ObjectDelete
+// viewer -> simulator
+{
+ ObjectDelete Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Force BOOL } // BOOL, god trying to force delete
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+
+// ObjectDuplicate
+// viewer -> simulator
+// Makes a copy of a set of objects, offset by a given amount
+{
+ ObjectDuplicate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ SharedData Single
+ { Offset LLVector3 }
+ { DuplicateFlags U32 } // see object_flags.h
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+
+// ObjectDuplicateOnRay
+// viewer -> simulator
+// Makes a copy of an object, using the add object raycast
+// code to abut it to other objects.
+{
+ ObjectDuplicateOnRay Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ { RayStart LLVector3 } // region local
+ { RayEnd LLVector3 } // region local
+ { BypassRaycast BOOL }
+ { RayEndIsIntersection BOOL }
+ { CopyCenters BOOL }
+ { CopyRotates BOOL }
+ { RayTargetID LLUUID }
+ { DuplicateFlags U32 } // see object_flags.h
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+
+// MultipleObjectUpdate
+// viewer -> simulator
+// updates position, rotation and scale in one message
+// positions sent as region-local floats
+{
+ MultipleObjectUpdate Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Type U8 }
+ { Data Variable 1 } // custom type
+ }
+}
+
+// RequestMultipleObjects
+// viewer -> simulator
+// reliable
+//
+// When the viewer gets a local_id/crc for an object that
+// it either doesn't have, or doesn't have the current version
+// of, it sends this upstream get get an update.
+//
+// CacheMissType 0 => full object (viewer doesn't have it)
+// CacheMissType 1 => CRC mismatch only
+{
+ RequestMultipleObjects Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { CacheMissType U8 }
+ { ID U32 }
+ }
+}
+
+
+// ObjectPosition
+// viewer -> simulator
+{
+ ObjectPosition Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Position LLVector3 } // region
+ }
+}
+
+
+// ObjectScale
+// viewer -> simulator
+{
+ ObjectScale Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Scale LLVector3 }
+ }
+}
+
+
+// ObjectRotation
+// viewer -> simulator
+{
+ ObjectRotation Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Rotation LLQuaternion }
+ }
+}
+
+
+// ObjectFlagUpdate
+// viewer -> simulator
+{
+ ObjectFlagUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { ObjectLocalID U32 }
+ { UsePhysics BOOL }
+ { IsTemporary BOOL }
+ { IsPhantom BOOL }
+ { CastsShadows BOOL }
+ }
+}
+
+
+// ObjectClickAction
+// viewer -> simulator
+{
+ ObjectClickAction Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { ClickAction U8 }
+ }
+}
+
+
+// ObjectImage
+// viewer -> simulator
+{
+ ObjectImage Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { MediaURL Variable 1 }
+ { TextureEntry Variable 2 }
+ }
+}
+
+
+{
+ ObjectMaterial Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Material U8 }
+ }
+}
+
+
+{
+ ObjectShape Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { PathCurve U8 }
+ { ProfileCurve U8 }
+ { PathBegin U8 } // 0 to 1, quanta = 0.01
+ { PathEnd U8 } // 0 to 1, quanta = 0.01
+ { PathScaleX U8 } // 0 to 1, quanta = 0.01
+ { PathScaleY U8 } // 0 to 1, quanta = 0.01
+ { PathShearX U8 } // -.5 to .5, quanta = 0.01
+ { PathShearY U8 } // -.5 to .5, quanta = 0.01
+ { PathTwist S8 } // -1 to 1, quanta = 0.01
+ { PathTwistBegin S8 } // -1 to 1, quanta = 0.01
+ { PathRadiusOffset S8 } // -1 to 1, quanta = 0.01
+ { PathTaperX S8 } // -1 to 1, quanta = 0.01
+ { PathTaperY S8 } // -1 to 1, quanta = 0.01
+ { PathRevolutions U8 } // 0 to 3, quanta = 0.015
+ { PathSkew S8 } // -1 to 1, quanta = 0.01
+ { ProfileBegin U8 } // 0 to 1, quanta = 0.01
+ { ProfileEnd U8 } // 0 to 1, quanta = 0.01
+ { ProfileHollow U8 } // 0 to 1, quanta = 0.01
+ }
+}
+
+{
+ ObjectExtraParams Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { ParamType U16 }
+ { ParamInUse BOOL }
+ { ParamSize U32 }
+ { ParamData Variable 1 }
+ }
+}
+
+
+// ObjectOwner
+// To make public, set OwnerID to LLUUID::null.
+// TODO: Eliminate god-bit. Maybe not. God-bit is ok, because it's
+// known on the server.
+{
+ ObjectOwner Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ HeaderData Single
+ { Override BOOL } // BOOL, God-bit.
+ { OwnerID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+// ObjectGroup
+// To make the object part of no group, set GroupID = LLUUID::null.
+// This call only works if objectid.ownerid == agentid.
+{
+ ObjectGroup Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+// Attempt to buy an object. This will only pack root objects.
+{
+ ObjectBuy Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ { CategoryID LLUUID } // folder where it goes (if derezed)
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { SaleType U8 } // U8 -> EForSale
+ { SalePrice S32 }
+ }
+}
+
+// viewer -> simulator
+
+// buy object inventory. If the transaction succeeds, it will add
+// inventory to the agent, and potentially remove the original.
+{
+ BuyObjectInventory Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ObjectID LLUUID }
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ }
+}
+
+// sim -> viewer
+// Used to propperly handle buying asset containers
+{
+ DerezContainer Low Trusted Zerocoded
+ {
+ Data Single
+ { ObjectID LLUUID }
+ { Delete BOOL } // BOOL
+ }
+}
+
+// ObjectPermissions
+// Field - see llpermissionsflags.h
+// If Set is true, tries to turn on bits in mask.
+// If set is false, tries to turn off bits in mask.
+// BUG: This just forces the permissions field.
+{
+ ObjectPermissions Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ HeaderData Single
+ { Override BOOL } // BOOL, God-bit.
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Field U8 }
+ { Set U8 }
+ { Mask U32 }
+ }
+}
+
+// set object sale information
+{
+ ObjectSaleInfo Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { LocalID U32 }
+ { SaleType U8 } // U8 -> EForSale
+ { SalePrice S32 }
+ }
+}
+
+
+// set object names
+{
+ ObjectName Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { LocalID U32 }
+ { Name Variable 1 }
+ }
+}
+
+// set object descriptions
+{
+ ObjectDescription Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { LocalID U32 }
+ { Description Variable 1 }
+ }
+}
+
+// set object category
+{
+ ObjectCategory Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { LocalID U32 }
+ { Category U32 }
+ }
+}
+
+// ObjectSelect
+// Variable object data because rectangular selection can
+// generate a large list very quickly.
+{
+ ObjectSelect Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+
+}
+
+
+// ObjectDeselect
+{
+ ObjectDeselect Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+
+}
+
+// ObjectAttach
+{
+ ObjectAttach Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { AttachmentPoint U8 }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ { Rotation LLQuaternion }
+ }
+}
+
+// ObjectDetach -- derezzes an attachment, marking its item in your inventory as not "(worn)"
+{
+ ObjectDetach Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+
+// ObjectDrop -- drops an attachment from your avatar onto the ground
+{
+ ObjectDrop Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+
+// ObjectLink
+{
+ ObjectLink Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+// ObjectDelink
+{
+ ObjectDelink Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+}
+
+// ObjectHinge
+{
+ ObjectHinge Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ JointType Single
+ { Type U8 }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+
+}
+
+// ObjectDehinge
+{
+ ObjectDehinge Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 }
+ }
+
+}
+
+
+// ObjectGrab
+{
+ ObjectGrab Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { LocalID U32 }
+ { GrabOffset LLVector3 }
+ }
+}
+
+
+// ObjectGrabUpdate
+// TODO: Quantize this data, reduce message size.
+// TimeSinceLast could go to 1 byte, since capped
+// at 100 on sim.
+{
+ ObjectGrabUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ { GrabOffsetInitial LLVector3 } // LLVector3
+ { GrabPosition LLVector3 } // LLVector3, region local
+ { TimeSinceLast U32 }
+ }
+}
+
+
+// ObjectDeGrab
+{
+ ObjectDeGrab Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { LocalID U32 }
+ }
+}
+
+
+// ObjectSpinStart
+{
+ ObjectSpinStart Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ }
+}
+
+
+// ObjectSpinUpdate
+{
+ ObjectSpinUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ { Rotation LLQuaternion }
+ }
+}
+
+
+// ObjectSpinStop
+{
+ ObjectSpinStop Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ }
+}
+
+// Export selected objects
+// viewer->sim
+{
+ ObjectExportSelected Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { RequestID LLUUID }
+ { VolumeDetail S16 }
+ }
+ {
+ ObjectData Variable
+ { ObjectID LLUUID }
+ }
+}
+
+// Import an object
+// viewer->sim
+{
+ ObjectImport Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { FolderID LLUUID }
+ }
+ {
+ AssetData Single
+ { FileID LLUUID 1 }
+ { ObjectName Variable 1 }
+ { Description Variable 1 }
+ }
+}
+
+// ModifyLand - sent to modify a piece of land on a simulator.
+// viewer -> sim
+{
+ ModifyLand Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ModifyBlock Single
+ { Action U8 }
+ { BrushSize U8 }
+ { Seconds F32 }
+ { Height F32 }
+ }
+ {
+ ParcelData Variable
+ { LocalID S32 }
+ { West F32 }
+ { South F32 }
+ { East F32 }
+ { North F32 }
+ }
+}
+
+
+// VelocityInterpolateOn
+// viewer->sim
+// requires administrative access
+{
+ VelocityInterpolateOn Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+
+// VelocityInterpolateOff
+// viewer->sim
+// requires administrative access
+{
+ VelocityInterpolateOff Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// Save State
+// viewer->sim
+// requires administrative access
+{
+ StateSave Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ DataBlock Single
+ { Filename Variable 1 }
+ }
+}
+
+// ReportAutosaveCrash
+// sim->launcher
+{
+ ReportAutosaveCrash Low NotTrusted Unencoded
+ {
+ AutosaveData Single
+ { PID S32 }
+ { Status S32 }
+ }
+}
+
+// SimWideDeletes
+{
+ SimWideDeletes Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ DataBlock Single
+ { TargetID LLUUID }
+ { Flags U32 }
+ }
+}
+
+// RequestObjectPropertiesFamily
+// Ask for extended information, such as creator, permissions, resources, etc.
+// Medium frequency because it is driven by mouse hovering over objects, which
+// occurs at high rates.
+{
+ RequestObjectPropertiesFamily Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { RequestFlags U32 }
+ { ObjectID LLUUID }
+ }
+}
+
+
+// Track agent - this information is used when sending out the
+// coarse location update so that we know who you are tracking.
+// To stop tracking - send a null uuid as the prey.
+{
+ TrackAgent Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TargetData Single
+ { PreyID LLUUID }
+ }
+}
+
+
+// Grant agents the ability to modify your stuff. This message is sent
+// to the simulator, and then passed along to the dataserver for
+// persistance. The dataserver will push live updates through
+// AddModifyAbility messages to the simulator.
+// viewer -> simulator -> dataserver
+//{
+// GrantModification Low NotTrusted Unencoded
+// {
+// AgentData Single
+// { AgentID LLUUID }
+// { SessionID LLUUID }
+// { GranterName Variable 1 }
+// }
+// {
+// EmpoweredBlock Variable
+// { EmpoweredID LLUUID }
+// }
+//}
+// Revoke the ability to modify your stuff. This message is sent to
+// the simulator, and then passed along to the dataserver for
+// persistance. The dataserver will push live updates through
+// RemoveModifyAbility messages to the simulator.
+// viewer -> simulator -> dataserver
+//{
+// RevokeModification Low NotTrusted Unencoded
+// {
+// AgentData Single
+// { AgentID LLUUID }
+// { SessionID LLUUID }
+// { GranterName Variable 1 }
+// }
+// {
+// RevokedBlock Variable
+// { RevokedID LLUUID }
+// }
+//}
+
+// This message is sent from viewer->simulator->dataserver, which responds with
+// the complete list of all granted agents in the GrantedProxies message.
+//{
+// RequestGrantedProxies Low NotTrusted Unencoded
+// {
+// AgentData Single
+// { AgentID LLUUID }
+// { SessionID LLUUID }
+// }
+//}
+
+// response to the message above
+//{
+// GrantedProxies Low Trusted Unencoded
+// {
+// AgentData Single
+// { AgentID LLUUID }
+// }
+// {
+// EmpoweredBlock Variable
+// { EmpoweredID LLUUID }
+// }
+// {
+// GranterBlock Variable
+// { GranterID LLUUID }
+// }
+//}
+
+// This message is sent from the dataserver to the simulator to let
+// AgentID know that they can modify GranterID's stuff. This message is
+// percolated out to child cameras and the viewer. This message is
+// in response to GrantModification messages, request, or login.
+// ROUTED dataserver -> simulator -> spaceserver -> simulator
+// reliable
+//{
+// AddModifyAbility Low Trusted Zerocoded
+// {
+// TargetBlock Single
+// { TargetIP IPADDR } // U32 encoded IP
+// { TargetPort IPPORT }
+// }
+// {
+// AgentBlock Single
+// { AgentID LLUUID }
+// }
+// {
+// GranterBlock Variable
+// { GranterID LLUUID }
+// }
+//}
+
+// This message is sent from the dataserver to the simulator to let
+// AgentID know that they can no longer modify GranterID's stuff. This
+// message is percolated out to child cameras and the viewer. This
+// message is in response to RevokeModification messages.
+// ROUTED dataserver -> simulator -> spaceserver -> simulator
+// reliable
+//{
+// RemoveModifyAbility Low Trusted Unencoded
+// {
+// TargetBlock Single
+// { TargetIP IPADDR } // U32 encoded IP
+// { TargetPort IPPORT }
+// }
+// {
+// AgentBlock Single
+// { AgentID LLUUID }
+// { RevokerID LLUUID }
+// }
+//}
+
+// end viewer to simulator section
+
+{
+ ViewerStats Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { IP IPADDR }
+ { StartTime U32 }
+ { RunTime F32 } // F32
+ { SimFPS F32 } // F32
+ { FPS F32 } // F32
+ { AgentsInView U8 } //
+ { Ping F32 } // F32
+ { MetersTraveled F64 }
+ { RegionsVisited S32 }
+ { SysRAM U32 }
+ { SysOS Variable 1 } // String
+ { SysCPU Variable 1 } // String
+ { SysGPU Variable 1 } // String
+ }
+
+ {
+ DownloadTotals Single
+ { World U32 }
+ { Objects U32 }
+ { Textures U32 }
+ }
+
+ {
+ NetStats Multiple 2
+ { Bytes U32 }
+ { Packets U32 }
+ { Compressed U32 }
+ { Savings U32 }
+ }
+
+ {
+ FailStats Single
+ { SendPacket U32 }
+ { Dropped U32 }
+ { Resent U32 }
+ { FailedResends U32 }
+ { OffCircuit U32 }
+ { Invalid U32 }
+ }
+
+ {
+ MiscStats Variable
+ { Type U32 }
+ { Value F64 }
+ }
+}
+
+// ScriptAnswerYes
+// reliable
+{
+ ScriptAnswerYes Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { Questions S32 }
+ }
+}
+
+
+// complaint/bug-report
+// reliable
+{
+ UserReport Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ReportData Single
+ { ReportType U8 } // BUG=1, COMPLAINT=2
+ { Category U8 } // see sequence.user_report_category
+ { Position LLVector3 } // screenshot position, region-local
+ { CheckFlags U8 } // checkboxflags
+ { ScreenshotID LLUUID }
+ { ObjectID LLUUID }
+ { Summary Variable 1 }
+ { Details Variable 2 }
+ { VersionString Variable 1 }
+ }
+ {
+ MeanCollision Variable
+ { Perp LLUUID }
+ { Time U32 }
+ { Mag F32 }
+ { Type U8 }
+ }
+}
+
+
+// ***************************************************************************
+// Simulator to Viewer Messages
+// ***************************************************************************
+
+// AlertMessage
+// Specifies the text to be posted in an alert dialog
+{
+ AlertMessage Low Trusted Unencoded
+ {
+ AlertData Single
+ { Message Variable 1 }
+ }
+}
+
+// Send an AlertMessage to the named agent.
+// usually dataserver->simulator
+{
+ AgentAlertMessage Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ AlertData Single
+ { Modal BOOL }
+ { Message Variable 1 }
+ }
+}
+
+
+// MeanCollisionAlert
+// Specifies the text to be posted in an alert dialog
+{
+ MeanCollisionAlert Low Trusted Zerocoded
+ {
+ MeanCollision Variable
+ { Victim LLUUID }
+ { Perp LLUUID }
+ { Time U32 }
+ { Mag F32 }
+ { Type U8 }
+ }
+}
+
+// ViewerFrozenMessage
+// Specifies the text to be posted in an alert dialog
+{
+ ViewerFrozenMessage Low Trusted Unencoded
+ {
+ FrozenData Single
+ { Data BOOL }
+ }
+}
+
+// Health Message
+// Tells viewer what agent health is
+{
+ HealthMessage Low Trusted Zerocoded
+ {
+ HealthData Single
+ { Health F32 }
+ }
+}
+
+// ChatFromSimulator
+// Chat text to appear on a user's screen
+// Position is region local.
+// Viewer can optionally use position to animate
+// If audible is CHAT_NOT_AUDIBLE, message will not be valid
+{
+ ChatFromSimulator Low Trusted Unencoded
+ {
+ ChatData Single
+ { FromName Variable 1 }
+ { SourceID LLUUID } // agent id or object id
+ { OwnerID LLUUID } // object's owner
+ { SourceType U8 }
+ { ChatType U8 }
+ { Audible U8 }
+ { Position LLVector3 }
+ { Message Variable 2 } // UTF-8 text
+ }
+}
+
+
+// Simulator statistics packet (goes out to viewer and dataserver/spaceserver)
+{
+ SimStats Low Trusted Unencoded
+ {
+ Region Single
+ { RegionX U32 }
+ { RegionY U32 }
+ { RegionFlags U32 }
+ { ObjectCapacity U32 }
+ }
+ {
+ Stat Variable
+ { StatID U32 }
+ { StatValue F32 }
+ }
+}
+
+// viewer -> sim
+// reliable
+{
+ RequestRegionInfo Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// RegionInfo
+// Used to populate UI for both region/estate floater
+// and god tools floater
+// sim -> viewer
+// reliable
+{
+ RegionInfo Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RegionInfo Single
+ { SimName Variable 1 } // string
+ { EstateID U32 }
+ { ParentEstateID U32 }
+ { RegionFlags U32 }
+ { SimAccess U8 }
+ { MaxAgents U8 }
+ { BillableFactor F32 }
+ { ObjectBonusFactor F32 }
+ { WaterHeight F32 }
+ { TerrainRaiseLimit F32 }
+ { TerrainLowerLimit F32 }
+ { PricePerMeter S32 }
+ { RedirectGridX S32 }
+ { RedirectGridY S32 }
+ { UseEstateSun BOOL }
+ { SunHour F32 } // last value set by estate or region controls JC
+ }
+}
+
+// GodUpdateRegionInfo
+// Sent from viewer to sim after a god has changed some
+// of the parameters in the god tools floater
+// viewer -> sim
+// reliable
+{
+ GodUpdateRegionInfo Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RegionInfo Single
+ { SimName Variable 1 } // string
+ { EstateID U32 }
+ { ParentEstateID U32 }
+ { RegionFlags U32 }
+ { BillableFactor F32 }
+ { PricePerMeter S32 }
+ { RedirectGridX S32 }
+ { RedirectGridY S32 }
+ }
+}
+
+//NearestLandingRegionRequest
+//sim->dataserver
+//Sent from the region to the data server
+//to request the most up to date region for the requesting
+//region to redirect teleports to
+{
+ NearestLandingRegionRequest Low Trusted Unencoded
+ {
+ RequestingRegionData Single
+ { RegionHandle U64 }
+ }
+}
+
+//NearestLandingPointReply
+//dataserver->sim
+//Sent from the data server to a region in reply
+//to the redirectregion request stating which region
+//the requesting region should redirect teleports to if necessary
+{
+ NearestLandingRegionReply Low Trusted Unencoded
+ {
+ LandingRegionData Single
+ { RegionHandle U64 }
+ }
+}
+
+//NearestLandingPointUpdated
+//sim->dataserver
+//Sent from a region to the data server
+//to have the dataserver note/clear in the db
+//that the region has updated it's nearest landing point
+{
+ NearestLandingRegionUpdated Low Trusted Unencoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ }
+}
+
+
+//TeleportLandingStatusChanged
+//sim->dataserver
+//Sent from the region to the data server
+//to note that the region's teleportation landing status has changed
+{
+ TeleportLandingStatusChanged Low Trusted Unencoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ }
+}
+
+// RegionHandshake
+// Sent by region to viewer after it has received UseCircuitCode
+// from that viewer.
+// sim -> viewer
+// reliable
+{
+ RegionHandshake Low Trusted Zerocoded
+ {
+ RegionInfo Single
+ { RegionFlags U32 }
+ { SimAccess U8 }
+ { SimName Variable 1 } // string
+ { SimOwner LLUUID }
+ { IsEstateManager BOOL } // this agent, for this sim
+ { WaterHeight F32 }
+ { BillableFactor F32 }
+ { CacheID LLUUID }
+ { TerrainBase0 LLUUID }
+ { TerrainBase1 LLUUID }
+ { TerrainBase2 LLUUID }
+ { TerrainBase3 LLUUID }
+ { TerrainDetail0 LLUUID }
+ { TerrainDetail1 LLUUID }
+ { TerrainDetail2 LLUUID }
+ { TerrainDetail3 LLUUID }
+ { TerrainStartHeight00 F32 }
+ { TerrainStartHeight01 F32 }
+ { TerrainStartHeight10 F32 }
+ { TerrainStartHeight11 F32 }
+ { TerrainHeightRange00 F32 }
+ { TerrainHeightRange01 F32 }
+ { TerrainHeightRange10 F32 }
+ { TerrainHeightRange11 F32 }
+ }
+}
+
+// RegionHandshakeReply
+// viewer -> sim
+// reliable
+// Sent after viewer has initialized the (pre-existing)
+// LLViewerRegion with the name, access level, etc. and
+// has loaded the cache for the region.
+// After the simulator receives this, it will start sending
+// data about objects.
+{
+ RegionHandshakeReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RegionInfo Single
+ { Flags U32 }
+ }
+}
+
+// The CoarseLocationUpdate message is sent to notify the viewer of
+// the location of mappable objects in the region. 1 meter resolution is
+// sufficient for this. The index block is used to show where you are,
+// and where someone you are tracking is located. They are -1 if not
+// applicable.
+{
+ CoarseLocationUpdate Medium Trusted Unencoded
+ {
+ Location Variable
+ { X U8 }
+ { Y U8 }
+ { Z U8 } // Z in meters / 4
+ }
+ {
+ Index Single
+ { You S16 }
+ { Prey S16 }
+ }
+}
+
+// ImageData - sent to viewer to transmit information about an image
+{
+ ImageData High Trusted Unencoded
+ {
+ ImageID Single
+ { ID LLUUID }
+ { Codec U8 }
+ { Size U32 }
+ { Packets U16 }
+ }
+ {
+ ImageData Single
+ { Data Variable 2 }
+ }
+}
+
+// ImagePacket - follow on image data for images having > 1 packet of data
+{
+ ImagePacket High Trusted Unencoded
+ {
+ ImageID Single
+ { ID LLUUID }
+ { Packet U16 }
+ }
+ {
+ ImageData Single
+ { Data Variable 2 }
+ }
+}
+
+// LayerData - Sent to viewer - encodes layer data
+
+{
+ LayerData High Trusted Unencoded
+ {
+ LayerID Single
+ { Type U8 }
+
+ }
+ {
+ LayerData Single
+ { Data Variable 2 }
+ }
+}
+
+// ObjectUpdate - Sent by objects from the simulator to the viewer
+//
+// If only one ImageID is sent for an object type that has more than
+// one face, the same image is repeated on each subsequent face.
+//
+// NameValue is a list of name-value strings, separated by \n characters,
+// terminated by \0
+//
+// Data is type-specific opaque data for this object
+{
+ ObjectUpdate High Trusted Zerocoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ { TimeDilation U16 }
+ }
+ {
+ ObjectData Variable
+ { ID U32 }
+ { State U8 }
+
+ { FullID LLUUID }
+ { CRC U32 } // TEMPORARY HACK FOR JAMES
+ { PCode U8 }
+ { Material U8 }
+ { ClickAction U8 }
+ { Scale LLVector3 }
+ { ObjectData Variable 1 }
+
+ { ParentID U32 }
+ { UpdateFlags U32 } // U32, see object_flags.h
+
+ { PathCurve U8 }
+ { ProfileCurve U8 }
+ { PathBegin U8 } // 0 to 1, quanta = 0.01
+ { PathEnd U8 } // 0 to 1, quanta = 0.01
+ { PathScaleX U8 } // 0 to 1, quanta = 0.01
+ { PathScaleY U8 } // 0 to 1, quanta = 0.01
+ { PathShearX U8 } // -.5 to .5, quanta = 0.01
+ { PathShearY U8 } // -.5 to .5, quanta = 0.01
+ { PathTwist S8 } // -1 to 1, quanta = 0.01
+ { PathTwistBegin S8 } // -1 to 1, quanta = 0.01
+ { PathRadiusOffset S8 } // -1 to 1, quanta = 0.01
+ { PathTaperX S8 } // -1 to 1, quanta = 0.01
+ { PathTaperY S8 } // -1 to 1, quanta = 0.01
+ { PathRevolutions U8 } // 0 to 3, quanta = 0.015
+ { PathSkew S8 } // -1 to 1, quanta = 0.01
+ { ProfileBegin U8 } // 0 to 1, quanta = 0.01
+ { ProfileEnd U8 } // 0 to 1, quanta = 0.01
+ { ProfileHollow U8 } // 0 to 1, quanta = 0.01
+
+ { TextureEntry Variable 2 }
+ { TextureAnim Variable 1 }
+
+ { NameValue Variable 2 }
+ { Data Variable 2 }
+ { Text Variable 1 } // llSetText() hovering text
+ { TextColor Fixed 4 } // actually, a LLColor4U
+ { MediaURL Variable 1 } // URL for web page, movie, etc.
+
+ // Info for particle systems
+ { PSBlock Variable 1 }
+
+ // Extra parameters
+ { ExtraParams Variable 1 }
+
+ // info for looped attached sounds
+ // because these are almost always all zero
+ // the hit after zero-coding is only 2 bytes
+ // not the 42 you see here
+ { Sound LLUUID }
+ { OwnerID LLUUID } // HACK object's owner id, only set if non-null sound, for muting
+ { Gain F32 }
+ { Flags U8 }
+ { Radius F32 } // cutoff radius
+
+ // joint info -- is sent in the update of each joint-child-root
+ { JointType U8 }
+ { JointPivot LLVector3 }
+ { JointAxisOrAnchor LLVector3 }
+ }
+}
+
+
+// ObjectUpdateCompressed
+{
+ ObjectUpdateCompressed High Trusted Unencoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ { TimeDilation U16 }
+ }
+ {
+ ObjectData Variable
+ { UpdateFlags U32 }
+ { Data Variable 2 }
+ }
+}
+
+// ObjectUpdateCached
+// reliable
+{
+ ObjectUpdateCached High Trusted Unencoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ { TimeDilation U16 }
+ }
+ {
+ ObjectData Variable
+ { ID U32 }
+ { CRC U32 }
+ { UpdateFlags U32 }
+ }
+}
+
+// packed terse object update format
+{
+ ImprovedTerseObjectUpdate High Trusted Unencoded
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ { TimeDilation U16 }
+ }
+ {
+ ObjectData Variable
+ { Data Variable 1 }
+ { TextureEntry Variable 2 }
+ }
+}
+
+// KillObject - Sent by objects to the viewer to tell them to kill themselves
+
+{
+ KillObject High Trusted Unencoded
+ {
+ ObjectData Variable
+ { ID U32 }
+ }
+}
+
+// AgentToNewRegion - tells the viewer that it's agent has moved
+
+{
+ AgentToNewRegion High Trusted Unencoded
+ {
+ RegionData Single
+ { SessionID LLUUID }
+ { IP IPADDR }
+ { Port IPPORT }
+ { Handle U64 }
+ }
+}
+
+// CrossedRegion - new way to tell a viewer it has gone across a region
+// boundary
+{
+ CrossedRegion Medium Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RegionData Single
+ { SimIP IPADDR }
+ { SimPort IPPORT }
+ { RegionHandle U64 }
+ { SeedCapability Variable 2 } // URL
+ }
+ {
+ Info Single
+ { Position LLVector3 }
+ { LookAt LLVector3 }
+ }
+}
+
+// SimulatorViewerTimeMessage - Allows viewer to resynch to world time
+
+{
+ SimulatorViewerTimeMessage Low Trusted Unencoded
+ {
+ TimeInfo Single
+ { UsecSinceStart U64 }
+ { SecPerDay U32 }
+ { SecPerYear U32 }
+ { SunDirection LLVector3 }
+ { SunPhase F32 }
+ { SunAngVelocity LLVector3 }
+ }
+}
+
+// EnableSimulator - Preps a viewer to receive data from a simulator
+
+{
+ EnableSimulator Low Trusted Unencoded
+ {
+ SimulatorInfo Single
+ { Handle U64 }
+ { IP IPADDR }
+ { Port IPPORT }
+ }
+}
+
+// DisableThisSimulator - Tells a viewer not to expect data from this simulator anymore
+
+{
+ DisableSimulator Low Trusted Unencoded
+}
+
+// ConfirmEnableSimulator - A confirmation message sent from simulator to neighbors that the simulator
+// has successfully been enabled by the viewer
+
+{
+ ConfirmEnableSimulator Medium Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// New Transfer system
+//-----------------------------------------------------------------------------
+
+// Request a new transfer (target->source)
+{
+ TransferRequest Low NotTrusted Zerocoded
+ {
+ TransferInfo Single
+ { TransferID LLUUID }
+ { ChannelType S32 }
+ { SourceType S32 }
+ { Priority F32 }
+ { Params Variable 2 }
+ }
+}
+
+// Return info about a transfer/initiate transfer (source->target)
+// Possibly should have a Params field like above
+{
+ TransferInfo Low NotTrusted Zerocoded
+ {
+ TransferInfo Single
+ { TransferID LLUUID }
+ { ChannelType S32 }
+ { TargetType S32 }
+ { Status S32 }
+ { Size S32 }
+ }
+}
+
+{
+ TransferPacket High NotTrusted Unencoded
+ {
+ TransferData Single
+ { TransferID LLUUID }
+ { ChannelType S32 }
+ { Packet S32 }
+ { Status S32 }
+ { Data Variable 2 }
+ }
+}
+
+// Abort a transfer in progress (either from target->source or source->target)
+{
+ TransferAbort Low NotTrusted Zerocoded
+ {
+ TransferInfo Single
+ { TransferID LLUUID }
+ { ChannelType S32 }
+ }
+}
+
+// Change the priority of a transfer (target->source)
+{
+ TransferPriority Low NotTrusted Zerocoded
+ {
+ TransferInfo Single
+ { TransferID LLUUID }
+ { ChannelType S32 }
+ { Priority F32 }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// General file transfer
+//-----------------------------------------------------------------------------
+
+// RequestXfer - request an arbitrary xfer
+{
+ RequestXfer Low NotTrusted Zerocoded
+ {
+ XferID Single
+ { ID U64 }
+ { Filename Variable 1 }
+ { FilePath U8 } // ELLPath
+ { DeleteOnCompletion BOOL } // BOOL
+ { UseBigPackets BOOL } // BOOL
+ { VFileID LLUUID }
+ { VFileType S16 }
+ }
+}
+
+// SendXferPacket - send an additional packet of an arbitrary xfer from sim -> viewer
+{
+ SendXferPacket High NotTrusted Unencoded
+ {
+ XferID Single
+ { ID U64 }
+ { Packet U32 }
+ }
+ {
+ DataPacket Single
+ { Data Variable 2 }
+ }
+}
+
+// ConfirmXferPacket
+{
+ ConfirmXferPacket High NotTrusted Unencoded
+ {
+ XferID Single
+ { ID U64 }
+ { Packet U32 }
+ }
+}
+
+// AbortXfer
+{
+ AbortXfer Low NotTrusted Unencoded
+ {
+ XferID Single
+ { ID U64 }
+ { Result S32 }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Avatar information
+//-----------------------------------------------------------------------------
+
+
+// RequestAvatarInfo
+{
+ RequestAvatarInfo Low Trusted Unencoded
+ {
+ DataBlock Single
+ { FullID LLUUID }
+ }
+}
+
+// AvatarAnimation - Update animation state
+// simulator --> viewer
+{
+ AvatarAnimation High Trusted Unencoded
+ {
+ Sender Single
+ { ID LLUUID }
+ }
+ {
+ AnimationList Variable
+ { AnimID LLUUID }
+ { AnimSequenceID S32 }
+ }
+ {
+ AnimationSourceList Variable
+ { ObjectID LLUUID }
+ }
+}
+
+// AvatarAppearance - Update visual params
+{
+ AvatarAppearance Low Trusted Zerocoded
+ {
+ Sender Single
+ { ID LLUUID }
+ { IsTrial BOOL }
+ }
+ {
+ ObjectData Single
+ { TextureEntry Variable 2 }
+ }
+ {
+ VisualParam Variable
+ { ParamValue U8 }
+ }
+}
+
+// AvatarSitResponse - response to a request to sit on an object
+{
+ AvatarSitResponse High Trusted Zerocoded
+ {
+ SitObject Single
+ { ID LLUUID }
+ }
+ {
+ SitTransform Single
+ { AutoPilot BOOL }
+ { SitPosition LLVector3 }
+ { SitRotation LLQuaternion }
+ { CameraEyeOffset LLVector3 }
+ { CameraAtOffset LLVector3 }
+ { ForceMouselook BOOL }
+ }
+}
+
+// SetFollowCamProperties
+{
+ SetFollowCamProperties Low Trusted Unencoded
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ }
+ {
+ CameraProperty Variable
+ { Type S32 }
+ { Value F32 }
+ }
+}
+
+// ClearFollowCamProperties
+{
+ ClearFollowCamProperties Low Trusted Unencoded
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ }
+}
+
+// CameraConstraint - new camera distance limit (based on collision with objects)
+{
+ CameraConstraint High Trusted Zerocoded
+ {
+ CameraCollidePlane Single
+ { Plane LLVector4 }
+ }
+}
+
+// ObjectProperties
+// Extended information such as creator, permissions, etc.
+// Medium because potentially driven by mouse hover events.
+{
+ ObjectProperties Medium Trusted Zerocoded
+ {
+ ObjectData Variable
+ { ObjectID LLUUID }
+ { CreatorID LLUUID }
+ { OwnerID LLUUID }
+ { GroupID LLUUID }
+ { BaseMask U32 }
+ { OwnerMask U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ { OwnershipCost S32 }
+// { TaxRate F32 } // F32
+ { SaleType U8 } // U8 -> EForSale
+ { SalePrice S32 }
+ { AggregatePerms U8 }
+ { AggregatePermTextures U8 }
+ { AggregatePermTexturesOwner U8 }
+ { Category U32 } // LLCategory
+ { InventorySerial S16 } // S16
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { FromTaskID LLUUID }
+ { LastOwnerID LLUUID }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { TouchName Variable 1 }
+ { SitName Variable 1 }
+ { TextureID Variable 1 }
+ }
+}
+
+// ObjectPropertiesFamily
+// Medium because potentially driven by mouse hover events.
+{
+ ObjectPropertiesFamily Medium Trusted Zerocoded
+ {
+ ObjectData Single
+ { RequestFlags U32 }
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { GroupID LLUUID }
+ { BaseMask U32 }
+ { OwnerMask U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ { OwnershipCost S32 }
+ { SaleType U8 } // U8 -> EForSale
+ { SalePrice S32 }
+ { Category U32 } // LLCategory
+ { LastOwnerID LLUUID }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ }
+}
+
+// RequestPayPrice
+// viewer -> sim
+{
+ RequestPayPrice Low NotTrusted Unencoded
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ }
+}
+
+// PayPriceReply
+// sim -> viewer
+{
+ PayPriceReply Low Trusted Unencoded
+ {
+ ObjectData Single
+ { ObjectID LLUUID }
+ { DefaultPayPrice S32 }
+ }
+ {
+ ButtonData Variable
+
+ { PayButton S32 }
+ }
+}
+
+// KickUser
+// *FIXME*
+// Kick off a logged-in user, such as when two people log in with the
+// same account name.
+// ROUTED dataserver -> userserver -> spaceserver -> simulator -> viewer
+// reliable, but that may not matter if a system component is quitting
+{
+ KickUser Low Trusted Unencoded
+ {
+ TargetBlock Single
+ { TargetIP IPADDR } // U32 encoded IP
+ { TargetPort IPPORT }
+ }
+ {
+ UserInfo Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Reason Variable 2 } // string
+ }
+}
+
+// ack sent from the simulator up to the main database so that login
+// can continue.
+{
+ KickUserAck Low Trusted Unencoded
+ {
+ UserInfo Single
+ { SessionID LLUUID }
+ { Flags U32 }
+ }
+}
+
+// GodKickUser
+// When a god wants someone kicked
+// viewer -> sim
+// reliable
+{
+ GodKickUser Low NotTrusted Unencoded
+ {
+ UserInfo Single
+ { GodID LLUUID }
+ { GodSessionID LLUUID }
+ { AgentID LLUUID }
+ { KickFlags U32 }
+ { Reason Variable 2 } // string
+ }
+}
+
+// SystemKickUser
+// user->space, reliable
+{
+ SystemKickUser Low Trusted Unencoded
+ {
+ AgentInfo Variable
+ { AgentID LLUUID }
+ }
+}
+
+// EjectUser
+// viewer -> sim
+// reliable
+{
+ EjectUser Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { TargetID LLUUID }
+ { Flags U32 }
+ }
+}
+
+// FreezeUser
+// Freeze someone who is on my land.
+// viewer -> sim
+// reliable
+{
+ FreezeUser Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { TargetID LLUUID }
+ { Flags U32 }
+ }
+}
+
+
+// AvatarPropertiesRequest
+// viewer -> simulator
+// reliable
+{
+ AvatarPropertiesRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { AvatarID LLUUID }
+ }
+}
+
+// AvatarPropertiesRequestBackend
+// simulator -> dataserver
+// reliable
+{
+ AvatarPropertiesRequestBackend Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { AvatarID LLUUID }
+ { GodLevel U8 }
+ { WebProfilesDisabled BOOL }
+ }
+}
+// AvatarPropertiesReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ AvatarPropertiesReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID } // your id
+ { AvatarID LLUUID } // avatar you're asking about
+ }
+ {
+ PropertiesData Single
+ { ImageID LLUUID }
+ { FLImageID LLUUID }
+ { PartnerID LLUUID }
+ { AboutText Variable 2 } // string, up to 512
+ { FLAboutText Variable 1 } // string
+ { BornOn Variable 1 } // string
+ { ProfileURL Variable 1 } // string
+ { CharterMember Variable 1 } // special - usually U8
+ { AllowPublish BOOL } // whether profile is externally visible or not
+ { MaturePublish BOOL } // profile is "mature"
+ { Identified BOOL } // whether avatar has provided payment info
+ { Transacted BOOL } // whether avatar has actively used payment info
+ }
+}
+
+{
+ AvatarInterestsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID } // your id
+ { AvatarID LLUUID } // avatar you're asking about
+ }
+ {
+ PropertiesData Single
+ { WantToMask U32 }
+ { WantToText Variable 1 } // string
+ { SkillsMask U32 }
+ { SkillsText Variable 1 } // string
+ { LanguagesText Variable 1 } // string
+ }
+}
+
+// AvatarGroupsReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ AvatarGroupsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID } // your id
+ { AvatarID LLUUID } // avatar you're asking about
+ }
+ {
+ GroupData Variable
+ { GroupPowers U64 }
+ { AcceptNotices BOOL }
+ { GroupTitle Variable 1 }
+ { GroupID LLUUID }
+ { GroupName Variable 1 }
+ { GroupInsigniaID LLUUID }
+ }
+}
+
+
+// AvatarPropertiesUpdate
+// viewer -> simulator
+// reliable
+{
+ AvatarPropertiesUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ PropertiesData Single
+ { ImageID LLUUID }
+ { FLImageID LLUUID }
+ { AboutText Variable 2 } // string, up to 512
+ { FLAboutText Variable 1 }
+ { AllowPublish BOOL } // whether profile is externally visible or not
+ { MaturePublish BOOL } // profile is "mature"
+ { ProfileURL Variable 1 } // string
+ }
+}
+
+// AvatarInterestsUpdate
+// viewer -> simulator
+// reliable
+{
+ AvatarInterestsUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ PropertiesData Single
+ { WantToMask U32 }
+ { WantToText Variable 1 } // string
+ { SkillsMask U32 }
+ { SkillsText Variable 1 } // string
+ { LanguagesText Variable 1 } // string
+ }
+}
+
+// AvatarStatisticsReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ AvatarStatisticsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ AvatarData Single
+ { AvatarID LLUUID }
+ }
+ {
+ StatisticsData Variable
+ { Name Variable 1 } // string
+ { Positive S32 }
+ { Negative S32 }
+ }
+}
+
+
+// AvatarNotesReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ AvatarNotesReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Data Single
+ { TargetID LLUUID }
+ { Notes Variable 2 } // string
+ }
+}
+
+
+// AvatarNotesUpdate
+// viewer -> simulator -> dataserver
+// reliable
+{
+ AvatarNotesUpdate Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { TargetID LLUUID }
+ { Notes Variable 2 } // string
+ }
+}
+
+
+// AvatarPicksReply
+// dataserver -> simulator -> viewer
+// Send the header information for this avatar's picks
+// This fills in the tabs of the Picks panel.
+// reliable
+{
+ AvatarPicksReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { TargetID LLUUID }
+ }
+ {
+ Data Variable
+ { PickID LLUUID }
+ { PickName Variable 1 } // string
+ }
+}
+
+
+// EventInfoRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ EventInfoRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+}
+
+
+// EventInfoReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ EventInfoReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ { Creator Variable 1 }
+ { Name Variable 1 }
+ { Category Variable 1 }
+ { Desc Variable 2 }
+ { Date Variable 1 }
+ { DateUTC U32 }
+ { Duration U32 }
+ { Cover U32 }
+ { Amount U32 }
+ { SimName Variable 1 }
+ { GlobalPos LLVector3d }
+ { EventFlags U32 }
+ }
+}
+
+
+// EventNotificationAddRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ EventNotificationAddRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+}
+
+
+// EventNotificationRemoveRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ EventNotificationRemoveRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+}
+
+// EventGodDelete
+// viewer -> simulator
+// simulator -> dataserver
+// QueryData is used to resend a search result after the deletion
+// reliable
+{
+ EventGodDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { QueryText Variable 1 }
+ { QueryFlags U32 }
+ { QueryStart S32 } // prev/next page support
+ }
+}
+
+// PickInfoRequest
+// viewer -> simulator
+// simulator -> dataserver
+// If CreatorID is not null, then we're looking for an agent pick
+// or picks.
+// reliable
+{
+ PickInfoRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { PickID LLUUID }
+ }
+}
+
+
+// PickInfoReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ PickInfoReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Data Single
+ { PickID LLUUID }
+ { CreatorID LLUUID }
+ { TopPick BOOL }
+ { ParcelID LLUUID }
+ { Name Variable 1 }
+ { Desc Variable 2 }
+ { SnapshotID LLUUID }
+ { User Variable 1 }
+ { OriginalName Variable 1 }
+ { SimName Variable 1 }
+ { PosGlobal LLVector3d }
+ { SortOrder S32 }
+ { Enabled BOOL }
+ }
+}
+
+
+// PickInfoUpdate
+// Update a pick. ParcelID is set on the simulator as the message
+// passes through.
+// If TopPick is TRUE, the simulator will only pass on the message
+// if the agent_id is a god.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ PickInfoUpdate Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { PickID LLUUID }
+ { CreatorID LLUUID }
+ { TopPick BOOL }
+ { ParcelID LLUUID }
+ { Name Variable 1 }
+ { Desc Variable 2 }
+ { SnapshotID LLUUID }
+ { PosGlobal LLVector3d }
+ { SortOrder S32 }
+ { Enabled BOOL }
+ }
+}
+
+
+// PickDelete
+// Delete a non-top pick from the database.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ PickDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { PickID LLUUID }
+ }
+}
+
+// PickGodDelete
+// Delete a pick from the database.
+// QueryID is needed so database can send a repeat list of
+// picks.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ PickGodDelete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { PickID LLUUID }
+ { QueryID LLUUID }
+ }
+}
+
+
+// ScriptQuestion
+// reliable
+{
+ ScriptQuestion Low Trusted Unencoded
+ {
+ Data Single
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { ObjectName Variable 1 }
+ { ObjectOwner Variable 1 }
+ { Questions S32 }
+ }
+}
+
+// ScriptControlChange
+// reliable
+{
+ ScriptControlChange Low Trusted Unencoded
+ {
+ Data Variable
+ { TakeControls BOOL }
+ { Controls U32 }
+ { PassToAgent BOOL }
+ }
+}
+
+// ScriptDialog
+// sim -> viewer
+// reliable
+{
+ ScriptDialog Low Trusted Zerocoded
+ {
+ Data Single
+ { ObjectID LLUUID }
+ { FirstName Variable 1 }
+ { LastName Variable 1 }
+ { ObjectName Variable 1 }
+ { Message Variable 2 }
+ { ChatChannel S32 }
+ { ImageID LLUUID }
+ }
+ {
+ Buttons Variable
+ { ButtonLabel Variable 1 }
+ }
+}
+
+
+// ScriptDialogReply
+// viewer -> sim
+// reliable
+{
+ ScriptDialogReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ObjectID LLUUID }
+ { ChatChannel S32 }
+ { ButtonIndex S32 }
+ { ButtonLabel Variable 1 }
+ }
+}
+
+
+// ForceScriptControlRelease
+// reliable
+{
+ ForceScriptControlRelease Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// RevokePermissions
+// reliable
+{
+ RevokePermissions Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ObjectID LLUUID }
+ { ObjectPermissions U32 }
+ }
+}
+
+// LoadURL
+// sim -> viewer
+// Ask the user if they would like to load a URL
+// reliable
+{
+ LoadURL Low Trusted Unencoded
+ {
+ Data Single
+ { ObjectName Variable 1 }
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { OwnerIsGroup BOOL }
+ { Message Variable 1 }
+ { URL Variable 1 }
+ }
+}
+
+// ScriptTeleportRequest
+// reliable
+{
+ ScriptTeleportRequest Low Trusted Unencoded
+ {
+ Data Single
+ { ObjectName Variable 1 }
+ { SimName Variable 1 }
+ { SimPosition LLVector3 }
+ { LookAt LLVector3 }
+ }
+}
+
+
+
+
+// ***************************************************************************
+// Land Parcel system
+// ***************************************************************************
+
+// ParcelOverlay
+// We send N packets per region to the viewer.
+// N = 4, currently. At 256x256 meter regions, 4x4 meter parcel grid,
+// there are 4096 parcel units per region. At N = 4, that's 1024 units
+// per packet, allowing 8 bit bytes.
+// sim -> viewer
+// reliable
+{
+ ParcelOverlay Low Trusted Zerocoded
+ {
+ ParcelData Single
+ { SequenceID S32 } // 0...3, which piece of region
+ { Data Variable 2 } // packed bit-field, (grids*grids)/N
+ }
+}
+
+
+// ParcelPropertiesRequest
+// SequenceID should be -1 or -2, and is echoed back in the
+// parcel properties message.
+// viewer -> sim
+// reliable
+{
+ ParcelPropertiesRequest Medium NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { SequenceID S32 }
+ { West F32 }
+ { South F32 }
+ { East F32 }
+ { North F32 }
+ { SnapSelection BOOL }
+ }
+}
+
+// ParcelPropertiesRequestByID
+// viewer -> sim
+// reliable
+{
+ ParcelPropertiesRequestByID Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { SequenceID S32 }
+ { LocalID S32 }
+ }
+}
+
+// ParcelProperties
+// sequence id = -1 for parcels that you explicitly selected
+// For agents, sequence id increments every time the agent transits into
+// a new parcel. It is used to detect out-of-order agent parcel info updates.
+// Bitmap = packed bit field, one bit per parcel grid, on if that grid is
+// part of the selected parcel.
+// sim -> viewer
+// WARNING: This packet is potentially large. With max length name,
+// description, music URL and media URL, it is 1526 + sizeof ( LLUUID ) bytes.
+{
+ ParcelProperties High Trusted Zerocoded
+ {
+ ParcelData Single
+ { RequestResult S32 }
+ { SequenceID S32 }
+ { SnapSelection BOOL }
+ { SelfCount S32 }
+ { OtherCount S32 }
+ { PublicCount S32 }
+ { LocalID S32 }
+ { OwnerID LLUUID }
+ { IsGroupOwned BOOL }
+ { AuctionID U32 }
+ { ReservedNewbie BOOL }
+ { ClaimDate S32 } // time_t
+ { ClaimPrice S32 }
+ { RentPrice S32 }
+ { AABBMin LLVector3 }
+ { AABBMax LLVector3 }
+ { Bitmap Variable 2 } // packed bit-field
+ { Area S32 }
+ { Status U8 } // owned vs. pending
+ { SimWideMaxPrims S32 }
+ { SimWideTotalPrims S32 }
+ { MaxPrims S32 }
+ { TotalPrims S32 }
+ { OwnerPrims S32 }
+ { GroupPrims S32 }
+ { OtherPrims S32 }
+ { SelectedPrims S32 }
+ { ParcelPrimBonus F32 }
+
+ { OtherCleanTime S32 }
+
+ { ParcelFlags U32 }
+ { SalePrice S32 }
+ { Name Variable 1 } // string
+ { Desc Variable 1 } // string
+ { MusicURL Variable 1 } // string
+ { MediaURL Variable 1 } // string
+ { MediaID LLUUID }
+ { MediaAutoScale U8 }
+ { GroupID LLUUID }
+ { PassPrice S32 }
+ { PassHours F32 }
+ { Category U8 }
+ { AuthBuyerID LLUUID }
+ { SnapshotID LLUUID }
+ { UserLocation LLVector3 }
+ { UserLookAt LLVector3 }
+ { LandingType U8 }
+ { RegionPushOverride BOOL }
+ { RegionDenyAnonymous BOOL }
+ { RegionDenyIdentified BOOL }
+ { RegionDenyTransacted BOOL }
+ }
+}
+
+// ParcelPropertiesUpdate
+// viewer -> sim
+// reliable
+{
+ ParcelPropertiesUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { Flags U32 }
+
+ { ParcelFlags U32 }
+ { SalePrice S32 }
+ { Name Variable 1 } // string
+ { Desc Variable 1 } // string
+ { MusicURL Variable 1 } // string
+ { MediaURL Variable 1 } // string
+ { MediaID LLUUID }
+ { MediaAutoScale U8 }
+ { GroupID LLUUID }
+ { PassPrice S32 }
+ { PassHours F32 }
+ { Category U8 }
+ { AuthBuyerID LLUUID }
+ { SnapshotID LLUUID }
+ { UserLocation LLVector3 }
+ { UserLookAt LLVector3 }
+ { LandingType U8 }
+ }
+}
+
+// ParcelReturnObjects
+// viewer -> sim
+// reliable
+{
+ ParcelReturnObjects Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { ReturnType U32 }
+ }
+ {
+ TaskIDs Variable
+ { TaskID LLUUID }
+ }
+ {
+ OwnerIDs Variable
+ { OwnerID LLUUID }
+ }
+}
+
+// ParcelSetOtherCleanTime
+// viewer -> sim
+// reliable
+{
+ ParcelSetOtherCleanTime Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { OtherCleanTime S32 }
+ }
+}
+
+
+// Disable makes objects nonphysical and turns off their scripts.
+// ParcelDisableObjects
+// viewer -> sim
+// reliable
+{
+ ParcelDisableObjects Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { ReturnType U32 }
+ }
+ {
+ TaskIDs Variable
+ { TaskID LLUUID }
+ }
+ {
+ OwnerIDs Variable
+ { OwnerID LLUUID }
+ }
+}
+
+
+// ParcelSelectObjects
+// viewer -> sim
+// reliable
+{
+ ParcelSelectObjects Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { ReturnType U32 }
+ }
+ {
+ ReturnIDs Variable
+ { ReturnID LLUUID }
+ }
+}
+
+
+// EstateCovenantRequest
+// viewer -> sim
+// reliable
+{
+ EstateCovenantRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// EstateCovenantReply
+// sim -> viewer
+// reliable
+{
+ EstateCovenantReply Low Trusted Unencoded
+ {
+ Data Single
+ { CovenantID LLUUID }
+ { CovenantTimestamp U32 }
+ { EstateName Variable 1 } // string
+ { EstateOwnerID LLUUID }
+ }
+}
+
+
+// ForceObjectSelect
+// sim -> viewer
+// reliable
+{
+ ForceObjectSelect Low Trusted Unencoded
+ {
+ Header Single
+ { ResetList BOOL }
+ }
+ {
+ Data Variable
+ { LocalID U32 }
+ }
+}
+
+
+// ParcelBuyPass - purchase a temporary access pass
+// viewer -> sim
+// reliable
+{
+ ParcelBuyPass Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ }
+}
+
+// ParcelDeedToGroup - deed a patch of land to a group
+// viewer -> sim
+// reliable
+{
+ ParcelDeedToGroup Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ { LocalID S32 } // parcel id
+ }
+}
+
+// reserved for when island owners force re-claim parcel
+{
+ ParcelReclaim Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { LocalID S32 } // parcel id
+ }
+}
+
+// ParcelClaim - change the owner of a patch of land
+// viewer -> sim
+// reliable
+{
+ ParcelClaim Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ { IsGroupOwned BOOL }
+ { Final BOOL } // true if buyer is in tier
+ }
+ {
+ ParcelData Variable
+ { West F32 }
+ { South F32 }
+ { East F32 }
+ { North F32 }
+ }
+}
+
+// ParcelJoin - Take all parcels which are owned by agent and inside
+// rectangle, and make them 1 parcel if they all are leased.
+// viewer -> sim
+// reliable
+{
+ ParcelJoin Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { West F32 }
+ { South F32 }
+ { East F32 }
+ { North F32 }
+ }
+}
+
+// ParcelDivide
+// If the selection is a subsection of exactly one parcel,
+// chop out that section and make a new parcel of it.
+// viewer -> sim
+// reliable
+{
+ ParcelDivide Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { West F32 }
+ { South F32 }
+ { East F32 }
+ { North F32 }
+ }
+}
+
+// ParcelRelease
+// Release a parcel to public
+// viewer -> sim
+// reliable
+{
+ ParcelRelease Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { LocalID S32 } // parcel ID
+ }
+}
+
+// ParcelBuy - change the owner of a patch of land.
+// viewer -> sim
+// reliable
+{
+ ParcelBuy Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ { IsGroupOwned BOOL }
+ { RemoveContribution BOOL }
+ { LocalID S32 }
+ { Final BOOL } // true if buyer is in tier
+ }
+}
+
+
+// ParcelGodForceOwner Unencoded
+{
+ ParcelGodForceOwner Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { OwnerID LLUUID }
+ { LocalID S32 } // parcel ID
+ }
+}
+
+
+// viewer -> sim
+// ParcelAccessListRequest
+{
+ ParcelAccessListRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { SequenceID S32 }
+ { Flags U32 }
+ { LocalID S32 }
+ }
+}
+
+
+// sim -> viewer
+// ParcelAccessListReply
+{
+ ParcelAccessListReply Low Trusted Zerocoded
+ {
+ Data Single
+ { AgentID LLUUID }
+ { SequenceID S32 }
+ { Flags U32 }
+ { LocalID S32 }
+ }
+ {
+ List Variable
+ { ID LLUUID }
+ { Time S32 } // time_t
+ { Flags U32 }
+ }
+}
+
+// viewer -> sim
+// ParcelAccessListUpdate
+{
+ ParcelAccessListUpdate Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { Flags U32 }
+ { LocalID S32 }
+ { TransactionID LLUUID }
+ { SequenceID S32 }
+ { Sections S32 }
+ }
+ {
+ List Variable
+ { ID LLUUID }
+ { Time S32 } // time_t
+ { Flags U32 }
+ }
+}
+
+
+// viewer -> sim -> dataserver
+// reliable
+{
+ ParcelDwellRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { LocalID S32 }
+ { ParcelID LLUUID } // filled in on sim
+ }
+}
+
+
+// dataserver -> sim -> viewer
+// reliable
+{
+ ParcelDwellReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Data Single
+ { LocalID S32 }
+ { ParcelID LLUUID }
+ { Dwell F32 }
+ }
+}
+
+// sim -> dataserver
+// This message is used to check if a user can buy a parcel. If
+// successful, the transaction is approved through a money balance reply
+// with the same transaction id.
+{
+ RequestParcelTransfer Low Trusted Zerocoded
+ {
+ Data Single
+ { TransactionID LLUUID }
+ { TransactionTime U32 } // utc seconds since epoch
+ { SourceID LLUUID }
+ { DestID LLUUID }
+ { OwnerID LLUUID }
+ { Flags U8 } // see lltransactiontypes.h
+ { TransactionType S32 } // see lltransactiontypes.h
+ { Amount S32 }
+ { BillableArea S32 }
+ { ActualArea S32 }
+ { Final BOOL } // true if buyer should be in tier
+ { ReservedNewbie BOOL }
+ }
+}
+
+// sim ->dataserver
+// This message is used to send up complete parcel properties for
+// persistance in the database.
+// If you add something here, you should probably also change the
+// simulator's database update query on startup.
+{
+ UpdateParcel Low Trusted Zerocoded
+ {
+ ParcelData Single
+ { ParcelID LLUUID }
+ { RegionHandle U64 }
+ { OwnerID LLUUID }
+ { GroupOwned BOOL }
+ { Status U8 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { MusicURL Variable 1 }
+ { RegionX F32 }
+ { RegionY F32 }
+ { ActualArea S32 }
+ { BillableArea S32 }
+ { ShowDir BOOL }
+ { IsForSale BOOL }
+ { Category U8 }
+ { SnapshotID LLUUID }
+ { UserLocation LLVector3 }
+ { SalePrice S32 }
+ { AuthorizedBuyerID LLUUID }
+ { ReservedNewbie BOOL }
+ { AllowPublish BOOL }
+ { MaturePublish BOOL }
+ }
+}
+
+// sim -> dataserver or space ->sim
+// This message is used to tell the dataserver that a parcel has been
+// removed.
+{
+ RemoveParcel Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ }
+}
+
+// sim -> dataserver
+// Merges some of the database information for parcels (dwell).
+{
+ MergeParcel Low Trusted Unencoded
+ {
+ MasterParcelData Single
+ { MasterID LLUUID }
+ }
+ {
+ SlaveParcelData Variable
+ { SlaveID LLUUID }
+ }
+}
+
+// sim -> dataserver
+{
+ LogParcelChanges Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ RegionData Single
+ { RegionHandle U64 }
+ }
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ { OwnerID LLUUID }
+ { IsOwnerGroup BOOL }
+ { ActualArea S32 }
+ { Action S8 }
+ { TransactionID LLUUID }
+ }
+}
+
+// sim -> dataserver
+{
+ CheckParcelSales Low Trusted Unencoded
+ {
+ RegionData Variable
+ { RegionHandle U64 }
+ }
+}
+
+// dataserver -> simulator
+// tell a particular simulator to finish parcel sale.
+{
+ ParcelSales Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ { BuyerID LLUUID }
+ }
+}
+
+// viewer -> sim
+// mark parcel and double secret agent content on parcel as owned by
+// governor/maint and adjusts permissions approriately. Godlike request.
+{
+ ParcelGodMarkAsContent Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ }
+}
+
+// viewer -> sim
+{
+ ParcelGodReserveForNewbie Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { SnapshotID LLUUID }
+ }
+}
+
+// viewer -> sim
+// start an auction. viewer fills in the appropriate date, simulator
+// validates and fills in the rest of the information to start an auction
+// on a parcel. Processing currently requires that AgentID is a god.
+{
+ ViewerStartAuction Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ParcelData Single
+ { LocalID S32 }
+ { SnapshotID LLUUID }
+ }
+}
+
+// sim -> dataserver
+// Once all of the data has been gathered,
+{
+ StartAuction Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ ParcelData Single
+ { ParcelID LLUUID }
+ { SnapshotID LLUUID }
+ { Name Variable 1 } // string
+ }
+}
+
+// dataserver -> sim
+{
+ ConfirmAuctionStart Low Trusted Unencoded
+ {
+ AuctionData Single
+ { ParcelID LLUUID }
+ { AuctionID U32 }
+ }
+}
+
+// sim -> dataserver
+// Tell the dataserver that an auction has completed.
+{
+ CompleteAuction Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ }
+}
+
+// Tell the dataserver that an auction has been canceled.
+{
+ CancelAuction Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ }
+}
+
+// sim -> dataserver
+{
+ CheckParcelAuctions Low Trusted Unencoded
+ {
+ RegionData Variable
+ { RegionHandle U64 }
+ }
+}
+
+// dataserver -> sim
+// tell a particular simulator to finish parcel sale.
+{
+ ParcelAuctions Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ { WinnerID LLUUID }
+ }
+}
+
+// ***************************************************************************
+// UUID to name lookup
+// ***************************************************************************
+
+// UUIDNameRequest
+// Translate a UUID into first and last names
+{
+ UUIDNameRequest Low NotTrusted Unencoded
+ {
+ UUIDNameBlock Variable
+ { ID LLUUID }
+ }
+}
+
+// UUIDNameReply
+// Translate a UUID into first and last names
+{
+ UUIDNameReply Low Trusted Unencoded
+ {
+ UUIDNameBlock Variable
+ { ID LLUUID }
+ { FirstName Variable 1 }
+ { LastName Variable 1 }
+ }
+}
+
+// UUIDGroupNameRequest
+// Translate a UUID into a group name
+{
+ UUIDGroupNameRequest Low NotTrusted Unencoded
+ {
+ UUIDNameBlock Variable
+ { ID LLUUID }
+ }
+}
+
+// UUIDGroupNameReply
+// Translate a UUID into a group name
+{
+ UUIDGroupNameReply Low Trusted Unencoded
+ {
+ UUIDNameBlock Variable
+ { ID LLUUID }
+ { GroupName Variable 1 }
+ }
+}
+
+// end uuid to name lookup
+
+// ***************************************************************************
+// Simulator to Simulator Messages
+// ***************************************************************************
+
+// ChatPass
+// Chat message transmission to neighbors
+// Chat is region local to receiving simulator.
+// Type is one of CHAT_TYPE_NORMAL, _WHISPER, _SHOUT
+{
+ ChatPass Low Trusted Zerocoded
+ {
+ ChatData Single
+ { Channel S32 }
+ { Position LLVector3 }
+ { ID LLUUID }
+ { OwnerID LLUUID }
+ { Name Variable 1 }
+ { SourceType U8 }
+ { Type U8 }
+ { Radius F32 }
+ { SimAccess U8 }
+ { Message Variable 2 }
+ }
+}
+
+// Edge data - compressed edge data
+
+{
+ EdgeDataPacket High Trusted Zerocoded
+ {
+ EdgeData Single
+ { LayerType U8 }
+ { Direction U8 }
+ { LayerData Variable 2 }
+ }
+}
+
+// Sim status, condition of this sim
+// sent reliably, when dirty
+{
+ SimStatus Medium Trusted Unencoded
+ {
+ SimStatus Single
+ { CanAcceptAgents BOOL }
+ { CanAcceptTasks BOOL }
+ }
+}
+
+// Child Agent Update - agents send child agents to neighboring simulators.
+// This will create a child camera if there isn't one at the target already
+// Can't send viewer IP and port between simulators -- the port may get remapped
+// if the viewer is behind a Network Address Translation (NAT) box.
+//
+// Note: some of the fields of this message really only need to be sent when an
+// agent crosses a region boundary and changes from a child to a main agent
+// (such as Head/BodyRotation, ControlFlags, Animations etc)
+// simulator -> simulator
+// reliable
+{
+ ChildAgentUpdate High Trusted Zerocoded
+ {
+ AgentData Single
+
+ { RegionHandle U64 }
+ { ViewerCircuitCode U32 }
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+
+ { AgentPos LLVector3 }
+ { AgentVel LLVector3 }
+ { Center LLVector3 }
+ { Size LLVector3 }
+ { AtAxis LLVector3 }
+ { LeftAxis LLVector3 }
+ { UpAxis LLVector3 }
+ { ChangedGrid BOOL } // BOOL
+
+ { Far F32 }
+ { Aspect F32 }
+ { Throttles Variable 1 }
+ { LocomotionState U32 }
+ { HeadRotation LLQuaternion }
+ { BodyRotation LLQuaternion }
+ { ControlFlags U32 }
+ { EnergyLevel F32 }
+ { GodLevel U8 } // Changed from BOOL to U8, and renamed GodLevel (from Godlike)
+ { AlwaysRun BOOL }
+ { PreyAgent LLUUID }
+ { AgentAccess U8 }
+ { AgentTextures Variable 2 }
+ { ActiveGroupID LLUUID }
+ }
+ {
+ GroupData Variable
+ { GroupID LLUUID }
+ { GroupPowers U64 }
+ { AcceptNotices BOOL }
+ }
+ {
+ AnimationData Variable
+ { Animation LLUUID }
+ { ObjectID LLUUID }
+ }
+ {
+ GranterBlock Variable
+ { GranterID LLUUID }
+ }
+ {
+ NVPairData Variable
+ { NVPairs Variable 2 }
+ }
+ {
+ VisualParam Variable
+ { ParamValue U8 }
+ }
+}
+
+// ChildAgentAlive
+// sent to child agents just to keep them alive
+{
+ ChildAgentAlive High Trusted Unencoded
+ {
+ AgentData Single
+ { RegionHandle U64 }
+ { ViewerCircuitCode U32 }
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// ChildAgentPositionUpdate
+// sent to child agents just to keep them alive
+{
+ ChildAgentPositionUpdate High Trusted Unencoded
+ {
+ AgentData Single
+
+ { RegionHandle U64 }
+ { ViewerCircuitCode U32 }
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+
+ { AgentPos LLVector3 }
+ { AgentVel LLVector3 }
+ { Center LLVector3 }
+ { Size LLVector3 }
+ { AtAxis LLVector3 }
+ { LeftAxis LLVector3 }
+ { UpAxis LLVector3 }
+ { ChangedGrid BOOL }
+ }
+}
+
+
+// Obituary for child agents - make sure the parent know the child is dead
+// This way, children can be reliably restarted
+{
+ ChildAgentDying Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+
+// This is sent if a full child agent hasn't been accepted yet
+{
+ ChildAgentUnknown Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// Pass Object Between Simulators
+//
+// BUG compress rotation
+// BUG compress scale
+{
+ PassObject High Trusted Zerocoded
+ {
+ ObjectData Single
+ { ID LLUUID }
+ { ParentID LLUUID }
+
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+
+ { PCode U8 }
+ { Material U8 }
+ { State U8 }
+ { Scale LLVector3 }
+ { UsePhysics U8 }
+
+ { PosX S16 }
+ { PosY S16 }
+ { PosZ S16 }
+
+ { VelX S16 }
+ { VelY S16 }
+ { VelZ S16 }
+
+ { Rotation LLQuaternion }
+
+ { AngVelX S16 }
+ { AngVelY S16 }
+ { AngVelZ S16 }
+
+ { PathCurve U8 }
+ { ProfileCurve U8 }
+ { PathBegin U8 } // 0 to 1, quanta = 0.01
+ { PathEnd U8 } // 0 to 1, quanta = 0.01
+ { PathScaleX U8 } // 0 to 1, quanta = 0.01
+ { PathScaleY U8 } // 0 to 1, quanta = 0.01
+ { PathShearX U8 } // -.5 to .5, quanta = 0.01
+ { PathShearY U8 } // -.5 to .5, quanta = 0.01
+ { PathTwist S8 } // -1 to 1, quanta = 0.01
+ { PathTwistBegin S8 } // -1 to 1, quanta = 0.01
+ { PathRadiusOffset S8 } // -1 to 1, quanta = 0.01
+ { PathTaperX S8 } // -1 to 1, quanta = 0.01
+ { PathTaperY S8 } // -1 to 1, quanta = 0.01
+ { PathRevolutions U8 } // 0 to 3, quanta = 0.015
+ { PathSkew S8 } // -1 to 1, quanta = 0.01
+ { ProfileBegin U8 } // 0 to 1, quanta = 0.01
+ { ProfileEnd U8 } // 0 to 1, quanta = 0.01
+ { ProfileHollow U8 } // 0 to 1, quanta = 0.01
+
+ { TextureEntry Variable 2 }
+
+ { SubType S16 }
+ { Active U8 }
+
+ { Data Variable 2 }
+ }
+ {
+ NVPairData Variable
+ { NVPairs Variable 2 }
+ }
+}
+
+// This message is sent how objects get passed between regions.
+{
+ AtomicPassObject High Trusted Unencoded
+ {
+ TaskData Single
+ { TaskID LLUUID }
+ { AttachmentNeedsSave BOOL } // true iff is attachment and needs asset saved
+ }
+}
+
+
+// KillChildAgents - A new agent has connected to the simulator . . . make sure that any old child cameras are blitzed
+{
+ KillChildAgents Low Trusted Unencoded
+ {
+ IDBlock Single
+ { AgentID LLUUID }
+ }
+}
+
+
+// GetScriptRunning - asks if a script is running or not. the simulator
+// responds with ScriptRunningReply
+{
+ GetScriptRunning Low NotTrusted Unencoded
+ {
+ Script Single
+ { ObjectID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+// ScriptRunningReply - response from simulator to message above
+{
+ ScriptRunningReply Low NotTrusted Unencoded
+ {
+ Script Single
+ { ObjectID LLUUID }
+ { ItemID LLUUID }
+ { Running BOOL }
+ }
+}
+
+
+// SetScriptRunning - makes a script active or inactive (Enable may be
+// true or false)
+{
+ SetScriptRunning Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Script Single
+ { ObjectID LLUUID }
+ { ItemID LLUUID }
+ { Running BOOL }
+ }
+}
+
+// ScriptReset - causes a script to reset
+{
+ ScriptReset Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Script Single
+ { ObjectID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+// ScriptSensorRequest - causes the receiving sim to run a script sensor and return the results
+{
+ ScriptSensorRequest Low Trusted Zerocoded
+ {
+ Requester Single
+ { SourceID LLUUID }
+ { RequestID LLUUID }
+ { SearchID LLUUID }
+ { SearchPos LLVector3 }
+ { SearchDir LLQuaternion }
+ { SearchName Variable 1 }
+ { Type S32 }
+ { Range F32 }
+ { Arc F32 }
+ { RegionHandle U64 }
+ { SearchRegions U8 }
+ }
+}
+
+// ScriptSensorReply - returns the request script search information back to the requester
+{
+ ScriptSensorReply Low Trusted Zerocoded
+ {
+ Requester Single
+ { SourceID LLUUID }
+ }
+ {
+ SensedData Variable
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { GroupID LLUUID }
+ { Position LLVector3 }
+ { Velocity LLVector3 }
+ { Rotation LLQuaternion }
+ { Name Variable 1 }
+ { Type S32 }
+ { Range F32 }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Login and Agent Motion
+//-----------------------------------------------------------------------------
+
+// viewer -> sim
+// agent is coming into the region. The region should be expecting the
+// agent.
+{
+ CompleteAgentMovement Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { CircuitCode U32 }
+ }
+}
+
+// sim -> viewer
+{
+ AgentMovementComplete Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { Position LLVector3 }
+ { LookAt LLVector3 }
+ { RegionHandle U64 }
+ { Timestamp U32 }
+ }
+}
+
+// sim->dataserver
+// log the fact that the agent has logged in.
+{
+ LogLogin Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { ViewerDigest LLUUID }
+ { LastExecFroze BOOL }
+ { SpaceIP IPADDR }
+ }
+}
+
+// This message is sent from the viewer on login or on demand from the
+// userserver.
+// viewer -> userserver
+{
+ ConnectAgentToUserserver Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// This message is sent from the userserver when it does not have
+// trusted connection or known agent on the circuit.
+{
+ ConnectToUserserver Low Trusted Unencoded
+}
+
+//-----------------------------------------------------------------------------
+// Logout
+//-----------------------------------------------------------------------------
+
+// userserver -> dataserver
+{
+ DataServerLogout Low Trusted Unencoded
+ {
+ UserData Single
+ { AgentID LLUUID }
+ { ViewerIP IPADDR }
+ { Disconnect BOOL }
+ { SessionID LLUUID }
+ }
+}
+
+// LogoutRequest
+// viewer -> sim
+// reliable
+{
+ LogoutRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// FinalizeLogout
+// Callback for when sim is done uploading assets to asset server
+// viewer -> sim
+// reliable
+{
+ FinalizeLogout Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// LogoutReply
+// it's ok for the viewer to quit.
+// sim -> viewer
+// reliable
+// Includes inventory items to update with new asset ids
+{
+ LogoutReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID } // null if list is actually empty (but has one entry 'cause it can't have none)
+ { NewAssetID LLUUID }
+ }
+}
+
+
+
+// LogoutDemand
+{
+ LogoutDemand Low Trusted Unencoded
+ {
+ LogoutBlock Single
+ { SessionID LLUUID }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Instant Message
+//-----------------------------------------------------------------------------
+
+// ImprovedInstantMessage
+// This message can potentially route all over the place
+// ParentEstateID: parent estate id of the source estate
+// RegionID: region id of the source of the IM.
+// Position: position of the sender in region local coordinates
+// Dialog see llinstantmessage.h for values
+// ID May be used by dialog. Interpretation depends on context.
+// BinaryBucket May be used by some dialog types
+// reliable
+{
+ ImprovedInstantMessage Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MessageBlock Single
+ { FromGroup BOOL }
+ { ToAgentID LLUUID }
+ { ParentEstateID U32 }
+ { RegionID LLUUID }
+ { Position LLVector3 }
+ { Offline U8 }
+ { Dialog U8 } // U8 - IM type
+ { ID LLUUID }
+ { Timestamp U32 }
+ { FromAgentName Variable 1 }
+ { Message Variable 2 }
+ { BinaryBucket Variable 2 }
+ }
+}
+
+// RetrieveInstantMessages - used to get instant messages that
+// were persisted out to the database while the user was offline
+{
+ RetrieveInstantMessages Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// DequeueInstantMessages - used to get messages out of the IM
+// queue that have come from outside of the system.
+{
+ DequeueInstantMessages Low Trusted Unencoded
+}
+
+// FindAgent - used to find an agent's global position. I used a
+// variable sized LocationBlock so that the message can be recycled with
+// minimum new messages and handlers.
+{
+ FindAgent Low NotTrusted Unencoded
+ {
+ AgentBlock Single
+ { Hunter LLUUID }
+ { Prey LLUUID }
+ { SpaceIP IPADDR }
+ }
+ {
+ LocationBlock Variable
+ { GlobalX F64 }
+ { GlobalY F64 }
+ }
+}
+
+// Set godlike to 1 if you want to become godlike.
+// Set godlike to 0 if you want to relinquish god powers.
+// viewer -> simulator -> dataserver
+// reliable
+{
+ RequestGodlikePowers Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RequestBlock Single
+ { Godlike BOOL }
+ { Token LLUUID } // viewer packs a null, sim packs token
+ }
+}
+
+// At the simulator, turn the godlike bit on.
+// At the viewer, show the god menu.
+// dataserver -> simulator -> viewer
+// reliable
+{
+ GrantGodlikePowers Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GrantData Single
+ { GodLevel U8 }
+ { Token LLUUID } // checked on sim, ignored on viewer
+ }
+}
+
+// GodlikeMessage - generalized construct for Gods to send messages
+// around the system. Each Request has it's own internal protocol.
+{
+ GodlikeMessage Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MethodData Single
+ { Method Variable 1 }
+ { Invoice LLUUID }
+ }
+ {
+ ParamList Variable
+ { Parameter Variable 1 }
+ }
+}
+
+// EstateOwnerMessage
+// format must be identical to above
+{
+ EstateOwnerMessage Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MethodData Single
+ { Method Variable 1 }
+ { Invoice LLUUID }
+ }
+ {
+ ParamList Variable
+ { Parameter Variable 1 }
+ }
+}
+
+// GenericMessage
+// format must be identical to above
+// As above, but don't have to be god or estate owner to send.
+{
+ GenericMessage Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MethodData Single
+ { Method Variable 1 }
+ { Invoice LLUUID }
+ }
+ {
+ ParamList Variable
+ { Parameter Variable 1 }
+ }
+}
+
+// ***************************************************************************
+// Requests for possessions, acquisition, money, etc
+// ***************************************************************************
+
+// request for mute list
+{
+ MuteListRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MuteData Single
+ { MuteCRC U32 }
+ }
+}
+
+// update/add someone in the mute list
+{
+ UpdateMuteListEntry Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MuteData Single
+ { MuteID LLUUID }
+ { MuteName Variable 1 }
+ { MuteType S32 }
+ { MuteFlags U32 }
+ }
+}
+
+// Remove a mute list entry.
+{
+ RemoveMuteListEntry Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MuteData Single
+ { MuteID LLUUID }
+ { MuteName Variable 1 }
+ }
+}
+
+
+//
+// Inventory update messages
+//
+
+{
+ CopyInventoryFromNotecard Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ NotecardData Single
+ { NotecardItemID LLUUID }
+ { ObjectID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ }
+}
+
+//
+// This is used bi-directionally between sim, dataserver, and viewer.
+// THIS MESSAGE CAN NOT CREATE NEW INVENTORY ITEMS.
+//
+{
+ UpdateInventoryItem Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CallbackID U32 } // Async Response
+
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+
+ { TransactionID LLUUID } // TransactionID: new assets only
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+//
+// For sim to request update/create.
+// DO NOT ALLOW THIS FROM THE VIEWER.
+//
+{
+ UpdateCreateInventoryItem Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SimApproved BOOL }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CallbackID U32 } // Async Response
+
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+
+ { AssetID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+{
+ MoveInventoryItem Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Stamp BOOL } // should the server re-timestamp?
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { NewName Variable 1 }
+ }
+}
+
+// copy inventory item by item id to specified destination folder,
+// send out bulk inventory update when done.
+//
+// Inventory items are only unique for {agent, inv_id} pairs;
+// the OldItemID needs to be paired with the OldAgentID to
+// produce a unique inventory item.
+{
+ CopyInventoryItem Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { CallbackID U32 } // Async response
+ { OldAgentID LLUUID }
+ { OldItemID LLUUID }
+ { NewFolderID LLUUID }
+ { NewName Variable 1 }
+ }
+}
+
+{
+ RemoveInventoryItem Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ }
+}
+
+{
+ ChangeInventoryItemFlags Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { Flags U32 }
+ }
+}
+
+//
+// Sim outgoing only (to dataserver, to viewer)
+// NOT viewer to sim, sim should not have handler, ever
+{
+ SaveAssetIntoInventory Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ InventoryData Single
+ { ItemID LLUUID }
+ { NewAssetID LLUUID }
+ }
+}
+
+{
+ CreateInventoryFolder Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ FolderData Single
+ { FolderID LLUUID }
+ { ParentID LLUUID }
+ { Type S8 }
+ { Name Variable 1 }
+ }
+}
+
+{
+ UpdateInventoryFolder Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID }
+ { ParentID LLUUID }
+ { Type S8 }
+ { Name Variable 1 }
+ }
+}
+
+{
+ MoveInventoryFolder Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Stamp BOOL } // should the server re-timestamp children
+ }
+ {
+ InventoryData Variable
+ { FolderID LLUUID }
+ { ParentID LLUUID }
+ }
+}
+
+{
+ RemoveInventoryFolder Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID }
+ }
+}
+
+// Get inventory segment.
+{
+ FetchInventoryDescendents Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Single
+ { FolderID LLUUID }
+ { OwnerID LLUUID }
+ { SortOrder S32 } // 0 = name, 1 = time
+ { FetchFolders BOOL } // false will omit folders in query
+ { FetchItems BOOL } // false will omit items in query
+ }
+}
+
+// return inventory segment.
+// *NOTE: This could be compressed more since we already know the
+// parent_id for folders and the folder_id for items, but this is
+// reasonable until we heve server side inventory.
+{
+ InventoryDescendents Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { FolderID LLUUID }
+ { OwnerID LLUUID } // owner of the folders creatd.
+ { Version S32 } // version of the folder for caching
+ { Descendents S32 } // count to help with caching
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID }
+ { ParentID LLUUID }
+ { Type S8 }
+ { Name Variable 1 }
+ }
+ {
+ ItemData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+ { AssetID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+// Get inventory item(s) - response comes through FetchInventoryReply
+{
+ FetchInventory Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { OwnerID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+// response to fetch inventory
+{
+ FetchInventoryReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+
+ { AssetID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+// Can only fit around 7 items per packet - that's the way it goes. At
+// least many bulk updates can be packed.
+// Only from dataserver->sim->viewer
+{
+ BulkUpdateInventory Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { TransactionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID }
+ { ParentID LLUUID }
+ { Type S8 }
+ { Name Variable 1 }
+ }
+ {
+ ItemData Variable
+ { ItemID LLUUID }
+ { CallbackID U32 } // Async Response
+ { FolderID LLUUID }
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+ { AssetID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+
+
+// request permissions for agent id to get the asset for owner_id's
+// item_id.
+{
+ RequestInventoryAsset Low Trusted Unencoded
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { AgentID LLUUID }
+ { OwnerID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+// response to RequestInventoryAsset
+// lluuid will be null if agentid in the request above cannot read asset
+{
+ InventoryAssetResponse Low Trusted Unencoded
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ { AssetID LLUUID }
+ { IsReadable BOOL }
+ }
+}
+
+// This is the new improved way to remove inventory items. It is
+// currently only supported in viewer->userserver->dataserver
+// messages typically initiated by an empty trash method.
+{
+ RemoveInventoryObjects Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID }
+ }
+ {
+ ItemData Variable
+ { ItemID LLUUID }
+ }
+}
+
+// This is how you remove inventory when you're not even sure what it
+// is - only it's parenting.
+{
+ PurgeInventoryDescendents Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Single
+ { FolderID LLUUID }
+ }
+}
+
+// These messages are viewer->simulator requests to update a task's
+// inventory.
+// if Key == 0, itemid is the key. if Key == 1, assetid is the key.
+{
+ UpdateTaskInventory Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ UpdateData Single
+ { LocalID U32 }
+ { Key U8 }
+ }
+ {
+ InventoryData Single
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+ { TransactionID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+{
+ RemoveTaskInventory Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Single
+ { LocalID U32 }
+ { ItemID LLUUID }
+ }
+}
+
+{
+ MoveTaskInventory Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { FolderID LLUUID }
+ }
+ {
+ InventoryData Single
+ { LocalID U32 }
+ { ItemID LLUUID }
+ }
+}
+
+{
+ RequestTaskInventory Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryData Single
+ { LocalID U32 }
+ }
+}
+
+{
+ ReplyTaskInventory Low Trusted Zerocoded
+ {
+ InventoryData Single
+ { TaskID LLUUID }
+ { Serial S16 } // S16
+ { Filename Variable 1 }
+ }
+}
+
+// These messages are viewer->simulator requests regarding objects
+// which are currently being simulated. The viewer will get an
+// UpdateInventoryItem response if a DeRez succeeds, and the object
+// will appear if a RezObject succeeds.
+// The Destination field tells where the derez should wind up, and the
+// meaning of DestinationID depends on it. For example, if the
+// destination is a category, then the destination is the category id. If
+// the destination is a task inventory, then the destination id is the
+// task id.
+// The transaction id is generated by the viewer on derez, and then
+// the packets are counted and numbered. The rest of the information is
+// just duplicated (it's not that much, and derezzes that span multiple
+// packets will be rare.)
+{
+ DeRezObject Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AgentBlock Single
+ { GroupID LLUUID }
+ { Destination U8 }
+ { DestinationID LLUUID } // see above
+ { TransactionID LLUUID }
+ { PacketCount U8 }
+ { PacketNumber U8 }
+ }
+ {
+ ObjectData Variable
+ { ObjectLocalID U32 } // object id in world
+ }
+}
+
+// This message is sent when a derez succeeds, but there's no way to
+// know, since no inventory is created on the viewer. For example, when
+// saving into task inventory.
+{
+ DeRezAck Low Trusted Unencoded
+}
+
+// This message is sent from viewer -> simulator when the viewer wants
+// to rez an object out of inventory.
+{
+ RezObject Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ RezData Single
+ { FromTaskID LLUUID }
+ { BypassRaycast U8 }
+ { RayStart LLVector3 }
+ { RayEnd LLVector3 }
+ { RayTargetID LLUUID }
+ { RayEndIsIntersection BOOL }
+ { RezSelected BOOL }
+ { RemoveItem BOOL }
+ { ItemFlags U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ }
+ {
+ InventoryData Single
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+ { TransactionID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+// This message is sent from viewer -> simulator when the viewer wants
+// to rez an object from a notecard.
+{
+ RezObjectFromNotecard Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ RezData Single
+ { FromTaskID LLUUID }
+ { BypassRaycast U8 }
+ { RayStart LLVector3 }
+ { RayEnd LLVector3 }
+ { RayTargetID LLUUID }
+ { RayEndIsIntersection BOOL }
+ { RezSelected BOOL }
+ { RemoveItem BOOL }
+ { ItemFlags U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ }
+ {
+ NotecardData Single
+ { NotecardItemID LLUUID }
+ { ObjectID LLUUID }
+ }
+ {
+ InventoryData Variable
+ { ItemID LLUUID }
+ }
+}
+
+// if declined, the destid agent from the GiveInventory message
+// responds with this message to the userserver
+{
+ DeclineInventory Low NotTrusted Unencoded
+ {
+ InfoBlock Single
+ { TransactionID LLUUID }
+ }
+}
+
+// sim -> dataserver
+// sent during agent to agent inventory transfers
+{
+ TransferInventory Low Trusted Zerocoded
+ {
+ InfoBlock Single
+ { SourceID LLUUID }
+ { DestID LLUUID }
+ { TransactionID LLUUID }
+ }
+ {
+ InventoryBlock Variable
+ { InventoryID LLUUID }
+ { Type S8 }
+ }
+}
+
+// dataserver -> sim
+// InventoryID is the id of the inventory object that the end user
+// should discard if they deny the transfer.
+{
+ TransferInventoryAck Low Trusted Zerocoded
+ {
+ InfoBlock Single
+ { TransactionID LLUUID }
+ { InventoryID LLUUID }
+ }
+}
+
+// Relationships - the start will be a request from sourceid to dest
+// id when they are located near each other.
+{
+ RequestFriendship Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AgentBlock Single
+ { FolderID LLUUID } // source ID's calling card folder
+ { DestID LLUUID }
+ { TransactionID LLUUID }
+ }
+}
+
+{
+ AcceptFriendship Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TransactionBlock Single
+ { TransactionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID } // place to put calling card.
+ }
+}
+
+{
+ DeclineFriendship Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TransactionBlock Single
+ { TransactionID LLUUID }
+ }
+}
+
+{
+ FormFriendship Low Trusted Unencoded
+ {
+ AgentBlock Single
+ { SourceID LLUUID }
+ { DestID LLUUID }
+ }
+}
+
+// Cancels user relationship
+// Updates inventory for both users.
+// Stops agent tracking in userserver.
+// viewer -> userserver -> dataserver
+// reliable
+{
+ TerminateFriendship Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ExBlock Single
+ { OtherID LLUUID }
+ }
+}
+
+// used to give someone a calling card.
+{
+ OfferCallingCard Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AgentBlock Single
+ { DestID LLUUID }
+ { TransactionID LLUUID }
+ }
+}
+
+{
+ AcceptCallingCard Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TransactionBlock Single
+ { TransactionID LLUUID }
+ }
+ {
+ FolderData Variable
+ { FolderID LLUUID } // place to put calling card.
+ }
+}
+
+{
+ DeclineCallingCard Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ TransactionBlock Single
+ { TransactionID LLUUID }
+ }
+}
+
+
+// Rez a script onto an object
+{
+ RezScript Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ UpdateBlock Single
+ { ObjectLocalID U32 } // object id in world
+ { Enabled BOOL } // is script rezzed in enabled?
+ }
+ {
+ InventoryBlock Single
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+ { TransactionID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+// Create inventory
+{
+ CreateInventoryItem Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ InventoryBlock Single
+ { CallbackID U32 } // Async Response
+ { FolderID LLUUID }
+ { TransactionID LLUUID } // Going to become TransactionID
+ { NextOwnerMask U32 }
+ { Type S8 }
+ { InvType S8 }
+ { WearableType U8 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ }
+}
+
+// give agent a landmark for an event.
+{
+ CreateLandmarkForEvent Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+ {
+ InventoryBlock Single
+ { FolderID LLUUID }
+ { Name Variable 1 }
+ }
+}
+
+{
+ EventLocationRequest Low Trusted Zerocoded
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ EventData Single
+ { EventID U32 }
+ }
+}
+
+{
+ EventLocationReply Low Trusted Zerocoded
+ {
+ QueryData Single
+ { QueryID LLUUID }
+ }
+ {
+ EventData Single
+ { Success BOOL }
+ { RegionID LLUUID }
+ { RegionPos LLVector3 }
+ }
+}
+
+// get information about landmarks. Used by viewers for determining
+// the location of a landmark, and by simulators for teleport
+{
+ RegionHandleRequest Low NotTrusted Unencoded
+ {
+ RequestBlock Single
+ { RegionID LLUUID }
+ }
+}
+
+{
+ RegionIDAndHandleReply Low Trusted Unencoded
+ {
+ ReplyBlock Single
+ { RegionID LLUUID }
+ { RegionHandle U64 }
+ }
+}
+
+// Move money from one agent to another. Validation will happen at the
+// simulator, the dataserver will actually do the work. Dataserver
+// generates a MoneyBalance message in reply. The simulator
+// will generate a MoneyTransferBackend in response to this.
+// viewer -> simulator -> dataserver
+{
+ MoneyTransferRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MoneyData Single
+ { SourceID LLUUID }
+ { DestID LLUUID } // destination of the transfer
+ { Flags U8 }
+ { Amount S32 }
+ { AggregatePermNextOwner U8 }
+ { AggregatePermInventory U8 }
+ { TransactionType S32 } // see lltransactiontypes.h
+ { Description Variable 1 } // string, name of item for purchases
+ }
+}
+
+// And, the money transfer
+{
+ MoneyTransferBackend Low Trusted Zerocoded
+ {
+ MoneyData Single
+ { TransactionID LLUUID }
+ { TransactionTime U32 } // utc seconds since epoch
+ { SourceID LLUUID }
+ { DestID LLUUID } // destination of the transfer
+ { Flags U8 }
+ { Amount S32 }
+ { AggregatePermNextOwner U8 }
+ { AggregatePermInventory U8 }
+ { TransactionType S32 } // see lltransactiontypes.h
+ { Description Variable 1 } // string, name of item for purchases
+ }
+}
+
+// This message is used to coalesce money transfers on a per-agent
+// basis. It should only be involved in sim->dataserver money communication.
+{
+ BulkMoneyTransfer Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { RegionX U32 }
+ { RegionY U32 }
+ }
+ {
+ MoneyData Variable
+ { TransactionID LLUUID }
+ { DestID LLUUID } // destination of the transfer
+ { Flags U8 }
+ { Amount S32 }
+ { TransactionType S32 } // see lltransactiontypes.h
+ { Description Variable 1 } // string, name of purchased item
+ }
+}
+
+
+// This message is sent sim -> viewer when the simulator is queueing
+// up transactions. This is because we do not have an authoritative
+// balance from the dataserver, but we want to 'guess' how much
+// money the agent has on the viewer.
+{
+ AdjustBalance Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { Delta S32 }
+ }
+}
+
+// viewer -> userserver -> dataserver
+// Reliable
+{
+ MoneyBalanceRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MoneyData Single
+ { TransactionID LLUUID }
+ }
+}
+
+
+// dataserver -> simulator -> viewer
+{
+ MoneyBalanceReply Low Trusted Zerocoded
+ {
+ MoneyData Single
+ { AgentID LLUUID }
+ { TransactionID LLUUID }
+ { TransactionSuccess BOOL } // BOOL
+ { MoneyBalance S32 }
+ { SquareMetersCredit S32 }
+ { SquareMetersCommitted S32 }
+ { Description Variable 1 } // string
+ }
+}
+
+
+// RoutedMoneyBalanceReply
+// This message is used when a dataserver needs to send updated
+// money balance information to a simulator other than the one it
+// is connected to. It uses the standard TransferBlock format.
+// dataserver -> simulator -> spaceserver -> simulator -> viewer
+// reliable
+{
+ RoutedMoneyBalanceReply Low Trusted Zerocoded
+ {
+ TargetBlock Single
+ { TargetIP IPADDR } // U32 encoded IP
+ { TargetPort IPPORT }
+ }
+ {
+ MoneyData Single
+ { AgentID LLUUID }
+ { TransactionID LLUUID }
+ { TransactionSuccess BOOL } // BOOL
+ { MoneyBalance S32 }
+ { SquareMetersCredit S32 }
+ { SquareMetersCommitted S32 }
+ { Description Variable 1 } // string
+ }
+}
+
+
+// This will give you a partial money history on the requested agentid.
+// Reliable
+{
+ MoneyHistoryRequest Low NotTrusted Unencoded
+ {
+ MoneyData Single
+ { AgentID LLUUID }
+ { StartPeriod S32 }
+ { EndPeriod S32 }
+ }
+}
+
+// Reliable
+{
+ MoneyHistoryReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ MoneyData Single
+ { StartPeriod S32 }
+ { EndPeriod S32 }
+ { Balance S32 }
+ { StartDate Variable 1 } // string
+ { TaxEstimate S32 }
+ { StipendEstimate S32 }
+ { BonusEstimate S32 }
+ }
+ {
+ HistoryData Variable
+ { Description Variable 1 } // string
+ { Amount S32 }
+ }
+}
+
+
+// CurrentInterval = 0 => this period (week, day, etc.)
+// CurrentInterval = 1 => last period
+// viewer -> userserver -> dataserver
+// reliable
+{
+ MoneySummaryRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+
+// dataserver -> userserver -> viewer
+// Reliable
+{
+ MoneySummaryReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ { Balance S32 }
+ { TotalCredits S32 }
+ { TotalDebits S32 }
+ { ObjectTaxCurrent S32 }
+ { LightTaxCurrent S32 }
+ { LandTaxCurrent S32 }
+ { GroupTaxCurrent S32 }
+ { ParcelDirFeeCurrent S32 }
+ { ObjectTaxEstimate S32 }
+ { LightTaxEstimate S32 }
+ { LandTaxEstimate S32 }
+ { GroupTaxEstimate S32 }
+ { ParcelDirFeeEstimate S32 }
+ { StipendEstimate S32 }
+ { BonusEstimate S32 }
+ { LastTaxDate Variable 1 } // string
+ { TaxDate Variable 1 } // string
+ }
+}
+
+
+// Reliable
+{
+ MoneyDetailsRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+// Reliable
+{
+ MoneyDetailsReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ }
+ {
+ HistoryData Variable
+ { Description Variable 1 } // string
+ { Amount S32 }
+ }
+}
+
+
+// Reliable
+{
+ MoneyTransactionsRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+// Reliable
+{
+ MoneyTransactionsReply Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ }
+ {
+ HistoryData Variable
+ { Time Variable 1 } // string
+ { User Variable 1 } // string
+ { Type S32 }
+ { Item Variable 1 } // string
+ { Amount S32 }
+ }
+}
+
+//---------------------------------------------------------------------------
+// Gesture saves/loads
+//---------------------------------------------------------------------------
+
+// viewer -> userserver -> dataserver
+// dataserver -> userserver -> viewer
+{
+ GestureUpdate Medium NotTrusted Unencoded
+ {
+ AgentBlock Single
+ { AgentID LLUUID }
+ { Filename Variable 1 } // String
+ { ToViewer BOOL } // BOOL, direction this is going
+ }
+}
+
+// viewer -> userserver -> dataserver
+{
+ GestureRequest Low NotTrusted Unencoded
+ {
+ AgentBlock Single
+ { AgentID LLUUID }
+ { Reset BOOL } // 0=no reset, 1=male, 2=female
+ }
+}
+
+// Tell the database that some gestures are now active
+// viewer -> sim -> data
+{
+ ActivateGestures Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ }
+ {
+ Data Variable
+ { ItemID LLUUID }
+ { AssetID LLUUID }
+ { GestureFlags U32 }
+ }
+}
+
+// Tell the database some gestures are no longer active
+// viewer -> sim -> data
+{
+ DeactivateGestures Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ }
+ {
+ Data Variable
+ { ItemID LLUUID }
+ { GestureFlags U32 }
+ }
+}
+
+//---------------------------------------------------------------------------
+//
+//---------------------------------------------------------------------------
+
+// userserver -> viewer, up-to-date inventory is here
+// could be sent as a result of spam
+// as well as in response to InventoryRequest
+//{
+// InventoryUpdate Low Trusted Unencoded
+// {
+// AgentData Single
+// { AgentID LLUUID }
+// }
+// {
+// InventoryData Single
+// { IsComplete U8 }
+// { Filename Variable 1 }
+// }
+//}
+
+// dataserver-> userserver -> viewer to move around the mute list
+{
+ MuteListUpdate Low Trusted Unencoded
+ {
+ MuteData Single
+ { AgentID LLUUID }
+ { Filename Variable 1 }
+ }
+}
+
+// tell viewer to use the local mute cache
+{
+ UseCachedMuteList Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+}
+
+// Sent from viewer to simulator to set user rights. This message will be
+// relayed up to the dataserver through a PUT. If that
+// succeeds, an UpdateUserRights will be relayed to the originating
+// viewer, and a presence lookup will be performed to find
+// agent-related and the same PUT will be issued to the sim host if
+// they are online.
+{
+ GrantUserRights Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Rights Variable
+ { AgentRelated LLUUID }
+ { RelatedRights S32 }
+ }
+}
+
+// This message is sent from the simulator to the viewer to indicate a
+// targets granted rights. This is only sent to the originator of the
+// request and the target agent if it is a modify or map
+// right. Adding/removing online status rights will show up as an
+// online/offline notification.
+{
+ ChangeUserRights Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ Rights Variable
+ { AgentRelated LLUUID }
+ { RelatedRights S32 }
+ }
+}
+
+// notification for login and logout.
+// source_sim -> dest_viewer
+{
+ OnlineNotification Low Trusted Unencoded
+ {
+ AgentBlock Variable
+ { AgentID LLUUID }
+ }
+}
+{
+ OfflineNotification Low Trusted Unencoded
+ {
+ AgentBlock Variable
+ { AgentID LLUUID }
+ }
+}
+
+
+// SetStartLocationRequest
+// viewer -> sim
+// failure checked at sim and triggers ImprovedInstantMessage
+// success triggers SetStartLocation
+{
+ SetStartLocationRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ StartLocationData Single
+ { SimName Variable 1 } // string
+ { LocationID U32 }
+ { LocationPos LLVector3 } // region coords
+ { LocationLookAt LLVector3 }
+ }
+}
+
+// SetStartLocation
+// sim -> dataserver
+{
+ SetStartLocation Low Trusted Zerocoded
+ {
+ StartLocationData Single
+ { AgentID LLUUID }
+ { RegionID LLUUID }
+ { LocationID U32 }
+ { RegionHandle U64 }
+ { LocationPos LLVector3 } // region coords
+ { LocationLookAt LLVector3 }
+ }
+}
+
+
+// ***************************************************************************
+// Launcher messages
+// ***************************************************************************
+
+
+// NetTest - This goes back and forth to the space server because of
+// problems determining the port
+{
+ NetTest Low NotTrusted Unencoded
+ {
+ NetBlock Single
+ { Port IPPORT }
+ }
+}
+
+// SetChildCount - Sent to launcher to adjust nominal child count
+// Simulator sends this increase the sim/cpu ratio on startup
+{
+ SetCPURatio Low NotTrusted Unencoded
+ {
+ Data Single
+ { Ratio U8 }
+ }
+}
+
+
+
+// SimCrashed - Sent to dataserver when the sim goes down.
+// Maybe we should notify the spaceserver as well?
+{
+ SimCrashed Low NotTrusted Unencoded
+ {
+ Data Single
+ { RegionX U32 }
+ { RegionY U32 }
+ }
+ {
+ Users Variable
+ { AgentID LLUUID }
+ }
+}
+
+// ***************************************************************************
+// Name Value Pair messages
+// ***************************************************************************
+
+// NameValuePair - if the specific task exists on simulator, add or replace this name value pair
+{
+ NameValuePair Low Trusted Unencoded
+ {
+ TaskData Single
+ { ID LLUUID }
+ }
+ {
+ NameValueData Variable
+ { NVPair Variable 2 }
+ }
+}
+
+// NameValuePair - if the specific task exists on simulator or dataserver, remove the name value pair (value is ignored)
+{
+ RemoveNameValuePair Low Trusted Unencoded
+ {
+ TaskData Single
+ { ID LLUUID }
+ }
+ {
+ NameValueData Variable
+ { NVPair Variable 2 }
+ }
+}
+
+
+// GetNameValuePair - if the specific task exists on simulator, get the value of this NameVale pair (if it exists)
+// FIXME: No transmit. 12 Sep 2002 mark
+{
+ GetNameValuePair Low NotTrusted Unencoded
+ {
+ TaskData Single
+ { ID LLUUID }
+ }
+ {
+ NameValueName Variable
+ { Name Variable 2 }
+ }
+}
+
+// ***************************************************************************
+// Add/Remove Attachment messages
+// ***************************************************************************
+
+//
+// Simulator informs Dataserver of new attachment or attachment asset update
+// DO NOT ALLOW THIS FROM THE VIEWER
+//
+{
+ UpdateAttachment Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AttachmentBlock Single
+ { AttachmentPoint U8 }
+ }
+ {
+ OperationData Single
+ { AddItem BOOL }
+ { UseExistingAsset BOOL }
+ }
+ {
+ InventoryData Single // Standard inventory item block
+ { ItemID LLUUID }
+ { FolderID LLUUID }
+
+ { CreatorID LLUUID } // permissions
+ { OwnerID LLUUID } // permissions
+ { GroupID LLUUID } // permissions
+ { BaseMask U32 } // permissions
+ { OwnerMask U32 } // permissions
+ { GroupMask U32 } // permissions
+ { EveryoneMask U32 } // permissions
+ { NextOwnerMask U32 } // permissions
+ { GroupOwned BOOL } // permissions
+
+ { AssetID LLUUID }
+ { Type S8 }
+ { InvType S8 }
+ { Flags U32 }
+ { SaleType U8 }
+ { SalePrice S32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { CreationDate S32 }
+ { CRC U32 }
+ }
+}
+
+// Simulator informs Dataserver that attachment has been taken off
+{
+ RemoveAttachment Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ AttachmentBlock Single
+ { AttachmentPoint U8 }
+ { ItemID LLUUID }
+ }
+}
+
+
+// ***************************************************************************
+// GUIDed Sound messages
+// ***************************************************************************
+
+// SoundTrigger - Sent by simulator to viewer to trigger sound outside current region
+{
+ SoundTrigger High NotTrusted Unencoded
+ {
+ SoundData Single
+ { SoundID LLUUID }
+ { OwnerID LLUUID }
+ { ObjectID LLUUID }
+ { ParentID LLUUID } // null if this object is the parent
+ { Handle U64 } // region handle
+ { Position LLVector3 } // region local
+ { Gain F32 }
+ }
+}
+
+// AttachedSound - Sent by simulator to viewer to play sound attached with an object
+{
+ AttachedSound Medium Trusted Unencoded
+ {
+ DataBlock Single
+ { SoundID LLUUID }
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { Gain F32 }
+ { Flags U8 }
+ }
+}
+
+// AttachedSoundGainChange - Sent by simulator to viewer to change an attached sounds' volume
+
+{
+ AttachedSoundGainChange Medium Trusted Unencoded
+ {
+ DataBlock Single
+ { ObjectID LLUUID }
+ { Gain F32 }
+ }
+}
+
+// AttachedSoundCutoffRadius - Sent by simulator to viewer to change an attached sounds' cutoff radius
+
+{
+ AttachedSoundCutoffRadius Medium Trusted Unencoded
+ {
+ DataBlock Single
+ { ObjectID LLUUID }
+ { Radius F32 }
+ }
+}
+
+// PreloadSound - Sent by simulator to viewer to preload sound for an object
+
+{
+ PreloadSound Medium Trusted Unencoded
+ {
+ DataBlock Variable
+ { ObjectID LLUUID }
+ { OwnerID LLUUID }
+ { SoundID LLUUID }
+ }
+}
+
+
+// *************************************************************************
+// Asset storage messages
+// *************************************************************************
+
+// current assumes an existing UUID, need to enhance for new assets
+{
+ AssetUploadRequest Low NotTrusted Unencoded
+ {
+ AssetBlock Single
+ { TransactionID LLUUID }
+ { Type S8 }
+ { Tempfile BOOL }
+ { StoreLocal BOOL }
+ { AssetData Variable 2 } // Optional: the actual asset data if the whole thing will fit it this packet
+ }
+}
+
+{
+ AssetUploadComplete Low NotTrusted Unencoded
+ {
+ AssetBlock Single
+ { UUID LLUUID }
+ { Type S8 }
+ { Success BOOL }
+ }
+}
+
+//
+// Reputation
+//
+{
+ ReputationAgentAssign Low NotTrusted Unencoded
+ {
+ DataBlock Single
+ { RatorID LLUUID }
+ { RateeID LLUUID }
+ { Behavior F32 } // float, usually -1 or +1
+ { Appearance F32 } // float, usually -1 or +1
+ { Building F32 } // float, usually -1 or +1
+ }
+}
+
+// ReputationIndividualRequest
+// Gets From's rating of To.
+// reliable
+{
+ ReputationIndividualRequest Low NotTrusted Unencoded
+ {
+ ReputationData Single
+ { FromID LLUUID }
+ { ToID LLUUID }
+ }
+}
+
+// ReputationIndividualReply
+// reliable
+{
+ ReputationIndividualReply Low Trusted Unencoded
+ {
+ ReputationData Single
+ { FromID LLUUID }
+ { ToID LLUUID }
+ { Behavior F32 } // float, usually -1 or +1
+ { Appearance F32 } // float, usually -1 or +1
+ { Building F32 } // float, usually -1 or +1
+ }
+}
+
+
+// Script on simulator asks dataserver if there are any email messages
+// waiting.
+{
+ EmailMessageRequest Low Trusted Unencoded
+ {
+ DataBlock Single
+ { ObjectID LLUUID }
+ { FromAddress Variable 1 }
+ { Subject Variable 1 }
+ }
+}
+
+// Dataserver gives simulator the oldest email message in the queue, along with
+// how many messages are left in the queue. And passes back the filter used to request emails.
+{
+ EmailMessageReply Low Trusted Unencoded
+ {
+ DataBlock Single
+ { ObjectID LLUUID }
+ { More U32 } //U32
+ { Time U32 } //U32
+ { FromAddress Variable 1 }
+ { Subject Variable 1 }
+ { Data Variable 2 }
+ { MailFilter Variable 1 }
+ }
+}
+
+// Script on simulator sends mail to another script
+{
+ InternalScriptMail Medium Trusted Unencoded
+ {
+ DataBlock Single
+ { From Variable 1 }
+ { To LLUUID }
+ { Subject Variable 1 }
+ { Body Variable 2 }
+ }
+}
+
+// Script on simulator asks dataserver for information
+{
+ ScriptDataRequest Low Trusted Unencoded
+ {
+ DataBlock Variable
+ { Hash U64 }
+ { RequestType S8 }
+ { Request Variable 2 }
+ }
+}
+
+// Data server responds with data
+{
+ ScriptDataReply Low Trusted Unencoded
+ {
+ DataBlock Variable
+ { Hash U64 }
+ { Reply Variable 2 }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Group messages
+//-----------------------------------------------------------------------------
+
+// CreateGroupRequest
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ CreateGroupRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { Name Variable 1 } // string
+ { Charter Variable 2 } // string
+ { ShowInList BOOL }
+ { InsigniaID LLUUID }
+ { MembershipFee S32 } // S32
+ { OpenEnrollment BOOL } // BOOL (U8)
+ { AllowPublish BOOL } // whether profile is externally visible or not
+ { MaturePublish BOOL } // profile is "mature"
+ }
+}
+
+// CreateGroupReply
+// dataserver -> simulator
+// simulator -> viewer
+// reliable
+{
+ CreateGroupReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ ReplyData Single
+ { GroupID LLUUID }
+ { Success BOOL }
+ { Message Variable 1 } // string
+ }
+}
+
+// UpdateGroupInfo
+// viewer -> simulator
+// simulator -> dataserver
+// reliable
+{
+ UpdateGroupInfo Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { Charter Variable 2 } // string
+ { ShowInList BOOL }
+ { InsigniaID LLUUID }
+ { MembershipFee S32 }
+ { OpenEnrollment BOOL }
+ { AllowPublish BOOL }
+ { MaturePublish BOOL }
+ }
+}
+
+// GroupRoleChanges
+// viewer -> simulator -> dataserver
+// reliable
+{
+ GroupRoleChanges Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ RoleChange Variable
+ { RoleID LLUUID }
+ { MemberID LLUUID }
+ { Change U32 }
+ }
+}
+
+// JoinGroupRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ JoinGroupRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+}
+
+// JoinGroupReply
+// dataserver -> simulator -> viewer
+{
+ JoinGroupReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { Success BOOL }
+ }
+}
+
+
+// EjectGroupMemberRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ EjectGroupMemberRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+ {
+ EjectData Variable
+ { EjecteeID LLUUID }
+ }
+}
+
+// EjectGroupMemberReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ EjectGroupMemberReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+ {
+ EjectData Single
+ { Success BOOL }
+ }
+}
+
+// LeaveGroupRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ LeaveGroupRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+}
+
+// LeaveGroupReply
+// dataserver -> simulator -> viewer
+{
+ LeaveGroupReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { Success BOOL }
+ }
+}
+
+// InviteGroupRequest
+// viewer -> simulator -> dataserver
+// reliable
+{
+ InviteGroupRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID } // UUID of inviting agent
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+ {
+ InviteData Variable
+ { InviteeID LLUUID }
+ { RoleID LLUUID }
+ }
+}
+
+// InviteGroupResponse
+// simulator -> dataserver
+// reliable
+{
+ InviteGroupResponse Low Trusted Unencoded
+ {
+ InviteData Single
+ { AgentID LLUUID }
+ { InviteeID LLUUID }
+ { GroupID LLUUID }
+ { RoleID LLUUID }
+ { MembershipFee S32 }
+ }
+}
+
+// GroupProfileRequest
+// viewer-> simulator -> dataserver
+// reliable
+{
+ GroupProfileRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+}
+
+// GroupProfileReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ GroupProfileReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { Name Variable 1 } // string
+ { Charter Variable 2 } // string
+ { ShowInList BOOL }
+ { MemberTitle Variable 1 } // string
+ { PowersMask U64 } // U32 mask
+ { InsigniaID LLUUID }
+ { FounderID LLUUID }
+ { MembershipFee S32 }
+ { OpenEnrollment BOOL } // BOOL (U8)
+ { Money S32 }
+ { GroupMembershipCount S32 }
+ { GroupRolesCount S32 }
+ { AllowPublish BOOL }
+ { MaturePublish BOOL }
+ { OwnerRole LLUUID }
+ }
+}
+
+// CurrentInterval = 0 => this period (week, day, etc.)
+// CurrentInterval = 1 => last period
+// viewer -> simulator -> dataserver
+// reliable
+{
+ GroupAccountSummaryRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+
+// dataserver -> simulator -> viewer
+// Reliable
+{
+ GroupAccountSummaryReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ { Balance S32 }
+ { TotalCredits S32 }
+ { TotalDebits S32 }
+ { ObjectTaxCurrent S32 }
+ { LightTaxCurrent S32 }
+ { LandTaxCurrent S32 }
+ { GroupTaxCurrent S32 }
+ { ParcelDirFeeCurrent S32 }
+ { ObjectTaxEstimate S32 }
+ { LightTaxEstimate S32 }
+ { LandTaxEstimate S32 }
+ { GroupTaxEstimate S32 }
+ { ParcelDirFeeEstimate S32 }
+ { NonExemptMembers S32 }
+ { LastTaxDate Variable 1 } // string
+ { TaxDate Variable 1 } // string
+ }
+}
+
+
+// Reliable
+{
+ GroupAccountDetailsRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+// Reliable
+{
+ GroupAccountDetailsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ }
+ {
+ HistoryData Variable
+ { Description Variable 1 } // string
+ { Amount S32 }
+ }
+}
+
+
+// Reliable
+{
+ GroupAccountTransactionsRequest Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ }
+}
+
+// Reliable
+{
+ GroupAccountTransactionsReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ MoneyData Single
+ { RequestID LLUUID }
+ { IntervalDays S32 }
+ { CurrentInterval S32 }
+ { StartDate Variable 1 } // string
+ }
+ {
+ HistoryData Variable
+ { Time Variable 1 } // string
+ { User Variable 1 } // string
+ { Type S32 }
+ { Item Variable 1 } // string
+ { Amount S32 }
+ }
+}
+
+// GroupActiveProposalsRequest
+// viewer -> simulator -> dataserver
+//reliable
+{
+ GroupActiveProposalsRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ }
+}
+
+// GroupActiveProposalItemReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ GroupActiveProposalItemReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ { TotalNumItems U32 }
+ }
+ {
+ ProposalData Variable
+ { VoteID LLUUID }
+ { VoteInitiator LLUUID }
+ { TerseDateID Variable 1 } // string
+ { StartDateTime Variable 1 } // string
+ { EndDateTime Variable 1 } // string
+ { AlreadyVoted BOOL }
+ { VoteCast Variable 1 } // string
+ { Majority F32 }
+ { Quorum S32 }
+ { ProposalText Variable 1 } // string
+ }
+}
+
+// GroupVoteHistoryRequest
+// viewer -> simulator -> dataserver
+//reliable
+{
+ GroupVoteHistoryRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ }
+}
+
+// GroupVoteHistoryItemReply
+// dataserver -> simulator -> viewer
+// reliable
+{
+ GroupVoteHistoryItemReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ TransactionData Single
+ { TransactionID LLUUID }
+ { TotalNumItems U32 }
+ }
+ {
+ HistoryItemData Single
+ { VoteID LLUUID }
+ { TerseDateID Variable 1 } // string
+ { StartDateTime Variable 1 } // string
+ { EndDateTime Variable 1 } // string
+ { VoteInitiator LLUUID }
+ { VoteType Variable 1 } // string
+ { VoteResult Variable 1 } // string
+ { Majority F32 }
+ { Quorum S32 }
+ { ProposalText Variable 2 } // string
+ }
+ {
+ VoteItem Variable
+ { CandidateID LLUUID }
+ { VoteCast Variable 1 } // string
+ { NumVotes S32 }
+ }
+}
+
+// StartGroupProposal
+// viewer -> simulator -> dataserver
+// reliable
+{
+ StartGroupProposal Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ProposalData Single
+ { GroupID LLUUID }
+ { Quorum S32 }
+ { Majority F32 } // F32
+ { Duration S32 } // S32, seconds
+ { ProposalText Variable 1 } // string
+ }
+}
+
+// GroupProposalBallot
+// viewer -> simulator -> dataserver
+// reliable
+{
+ GroupProposalBallot Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ProposalData Single
+ { ProposalID LLUUID }
+ { GroupID LLUUID }
+ { VoteCast Variable 1 } // string
+ }
+}
+
+// TallyVotes userserver -> dataserver
+// reliable
+{
+ TallyVotes Low Trusted Unencoded
+}
+
+
+
+// GroupMembersRequest
+// get the group members
+// simulator -> dataserver
+// reliable
+{
+ GroupMembersRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ }
+}
+
+// GroupMembersReply
+// list of uuids for the group members
+// dataserver -> simulator
+// reliable
+{
+ GroupMembersReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ { MemberCount S32 }
+ }
+ {
+ MemberData Variable
+ { AgentID LLUUID }
+ { Contribution S32 }
+ { OnlineStatus Variable 1 } // string
+ { AgentPowers U64 }
+ { Title Variable 1 } // string
+ { IsOwner BOOL }
+ }
+}
+
+// used to switch an agent's currently active group.
+// viewer -> simulator -> dataserver -> AgentDataUpdate...
+{
+ ActivateGroup Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+}
+
+// viewer -> simulator -> dataserver
+{
+ SetGroupContribution Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ { Contribution S32 }
+ }
+}
+
+// viewer -> simulator -> dataserver
+{
+ SetGroupAcceptNotices Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { GroupID LLUUID }
+ { AcceptNotices BOOL }
+ }
+}
+
+// GroupRoleDataRequest
+// viewer -> simulator -> dataserver
+{
+ GroupRoleDataRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ }
+}
+
+
+// GroupRoleDataReply
+// All role data for this group
+// dataserver -> simulator -> agent
+{
+ GroupRoleDataReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ { RoleCount S32 }
+ }
+ {
+ RoleData Variable
+ { RoleID LLUUID }
+ { Name Variable 1 }
+ { Title Variable 1 }
+ { Description Variable 1 }
+ { Powers U64 }
+ { Members U32 }
+ }
+}
+
+// GroupRoleMembersRequest
+// viewer -> simulator -> dataserver
+{
+ GroupRoleMembersRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ GroupData Single
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ }
+}
+
+// GroupRoleMembersReply
+// All role::member pairs for this group.
+// dataserver -> simulator -> agent
+{
+ GroupRoleMembersReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ { TotalPairs U32 }
+ }
+ {
+ MemberData Variable
+ { RoleID LLUUID }
+ { MemberID LLUUID }
+ }
+}
+
+// GroupTitlesRequest
+// viewer -> simulator -> dataserver
+{
+ GroupTitlesRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ }
+}
+
+
+// GroupTitlesReply
+// dataserver -> simulator -> viewer
+{
+ GroupTitlesReply Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ { RequestID LLUUID }
+ }
+ {
+ GroupData Variable
+ { Title Variable 1 } // string
+ { RoleID LLUUID }
+ { Selected BOOL }
+ }
+}
+
+// GroupTitleUpdate
+// viewer -> simulator -> dataserver
+{
+ GroupTitleUpdate Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ { TitleRoleID LLUUID }
+ }
+}
+
+// GroupRoleUpdate
+// viewer -> simulator -> dataserver
+{
+ GroupRoleUpdate Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { GroupID LLUUID }
+ }
+ {
+ RoleData Variable
+ { RoleID LLUUID }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ { Title Variable 1 }
+ { Powers U64 }
+ { UpdateType U8 }
+ }
+}
+
+
+
+// Request the members of the live help group needed for requesting agent.
+// userserver -> dataserver
+{
+ LiveHelpGroupRequest Low Trusted Unencoded
+ {
+ RequestData Single
+ { RequestID LLUUID }
+ { AgentID LLUUID }
+ }
+}
+
+// Send down the group
+// dataserver -> userserver
+{
+ LiveHelpGroupReply Low Trusted Unencoded
+ {
+ ReplyData Single
+ { RequestID LLUUID }
+ { GroupID LLUUID }
+ { Selection Variable 1 } // selection criteria all or active
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Wearable messages
+//-----------------------------------------------------------------------------
+
+// AgentWearablesRequest
+// (a.k.a. "Tell me what the avatar is wearing.")
+// viewer -> simulator -> dataserver
+// reliable
+{
+ AgentWearablesRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// AgentWearablesUpdate
+// (a.k.a. "Here's what your avatar should be wearing now.")
+// dataserver -> userserver -> viewer
+// reliable
+// NEVER from viewer to sim
+{
+ AgentWearablesUpdate Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum U32 } // U32, Increases every time the wearables change for a given agent. Used to avoid processing out of order packets.
+ }
+ {
+ WearableData Variable
+ { ItemID LLUUID }
+ { AssetID LLUUID }
+ { WearableType U8 } // U8, LLWearable::EWearType
+ }
+}
+
+//
+// AgentIsNowWearing
+// (a.k.a. "Here's what I'm wearing now.")
+// viewer->sim->dataserver
+// reliable
+{
+ AgentIsNowWearing Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ WearableData Variable
+ { ItemID LLUUID }
+ { WearableType U8 }
+ }
+}
+
+
+// AgentCachedTexture
+// viewer queries for cached textures on dataserver (via simulator)
+// viewer -> simulator -> dataserver
+// reliable
+{
+ AgentCachedTexture Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum S32 }
+ }
+ {
+ WearableData Variable
+ { ID LLUUID }
+ { TextureIndex U8 }
+ }
+}
+
+// AgentCachedTextureResponse
+// response to viewer queries for cached textures on dataserver (via simulator)
+// dataserver -> simulator -> viewer
+// reliable
+{
+ AgentCachedTextureResponse Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { SerialNum S32 }
+ }
+ {
+ WearableData Variable
+ { TextureID LLUUID }
+ { TextureIndex U8 }
+ { HostName Variable 1 }
+ }
+}
+
+// Request an AgentDataUpdate without changing any agent data.
+{
+ AgentDataUpdateRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+// AgentDataUpdate
+// Updates a viewer or simulator's impression of agent-specific information.
+// Used, for example, when an agent's group changes.
+// dataserver -> simulator -> viewer
+// reliable
+{
+ AgentDataUpdate Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { FirstName Variable 1 } // string
+ { LastName Variable 1 } // string
+ { GroupTitle Variable 1 } // string
+ { ActiveGroupID LLUUID } // active group
+ { GroupPowers U64 }
+ { GroupName Variable 1 } // string
+ }
+}
+
+
+// GroupDataUpdate
+// This is a bunch of group data that needs to be appropriatly routed based on presence info.
+// dataserver -> simulator
+{
+ GroupDataUpdate Low Trusted Zerocoded
+ {
+ AgentGroupData Variable
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ { AgentPowers U64 }
+ { GroupTitle Variable 1 }
+ }
+}
+
+// AgentGroupDataUpdate
+// Updates a viewer or simulator's impression of the groups an agent is in.
+// dataserver -> simulator -> viewer
+// reliable
+{
+ AgentGroupDataUpdate Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ GroupData Variable
+ { GroupID LLUUID }
+ { GroupPowers U64 }
+ { AcceptNotices BOOL }
+ { GroupInsigniaID LLUUID }
+ { Contribution S32 }
+ { GroupName Variable 1 } // string
+ }
+}
+
+// AgentDropGroup
+// Updates the viewer / simulator that an agent is no longer part of a group
+// dataserver -> simulator -> viewer
+// dataserver -> userserver
+// reliable
+{
+ AgentDropGroup Low Trusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { GroupID LLUUID }
+ }
+}
+
+// LogTextMessage
+// Asks the dataserver to log the contents of this message in the
+// chat and IM log table.
+// Sent from userserver (IM logging) and simulator (chat logging).
+{
+ LogTextMessage Low Trusted Zerocoded
+ {
+ DataBlock Variable
+ { FromAgentId LLUUID }
+ { ToAgentId LLUUID }
+ { GlobalX F64 }
+ { GlobalY F64 }
+ { Time U32 } // utc seconds since epoch
+ { Message Variable 2 } // string
+ }
+}
+
+// ViewerEffect
+// Viewer side effect that's sent from one viewer, and broadcast to other agents nearby
+{
+ ViewerEffect Medium NotTrusted Zerocoded
+ {
+ Effect Variable
+ { ID LLUUID } // UUID of the effect
+ { Type U8 } // Type of the effect
+ { Duration F32 } // F32 time (seconds)
+ { Color Fixed 4 } // Color4U
+ { TypeData Variable 1 } // Type specific data
+ }
+}
+
+// SetSunPhase
+// Sets the current phase of the sun - propagated from viewer->sim->spaceserver (if godlike)
+{
+ SetSunPhase Medium NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ Data Single
+ { Phase F32 } // F32 (radians)
+ }
+}
+
+// CreateTrustedCircuit
+// Sent to establish a trust relationship between two components.
+// Only sent in response to a DenyTrustedCircuit message.
+{
+ CreateTrustedCircuit Low NotTrusted Unencoded
+ {
+ DataBlock Single
+ { EndPointID LLUUID }
+ { Digest Fixed 32 } // 32 hex digits == 1 MD5 Digest
+ }
+}
+
+// DenyTrustedCircuit
+// Sent :
+// - in response to failed CreateTrustedCircuit
+// - to force the remote end-point to try to establish a trusted circuit
+// - the reception of a trusted message on a non-trusted circuit
+// This allows us to re-auth a circuit if it gets closed due to timeouts or network failures.
+{
+ DenyTrustedCircuit Low NotTrusted Unencoded
+ {
+ DataBlock Single
+ { EndPointID LLUUID }
+ }
+}
+
+// RequestTrustedCircuit
+// If the destination does not trust the sender, a Deny is sent back.
+{
+ RequestTrustedCircuit Low Trusted Unencoded
+}
+
+
+{
+ RezSingleAttachmentFromInv Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ObjectData Single
+ { ItemID LLUUID }
+ { OwnerID LLUUID }
+ { AttachmentPt U8 } // 0 for default
+ { ItemFlags U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ }
+}
+
+{
+ RezMultipleAttachmentsFromInv Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ HeaderData Single
+ { CompoundMsgID LLUUID } // All messages a single "compound msg" must have the same id
+ { TotalObjects U8 }
+ { FirstDetachAll BOOL }
+ }
+ {
+ ObjectData Variable // 1 to 4 of these per packet
+ { ItemID LLUUID }
+ { OwnerID LLUUID }
+ { AttachmentPt U8 } // 0 for default
+ { ItemFlags U32 }
+ { GroupMask U32 }
+ { EveryoneMask U32 }
+ { NextOwnerMask U32 }
+ { Name Variable 1 }
+ { Description Variable 1 }
+ }
+}
+
+
+{
+ DetachAttachmentIntoInv Low NotTrusted Unencoded
+ {
+ ObjectData Single
+ { AgentID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+
+// Viewer -> Sim
+// Used in "Make New Outfit"
+{
+ CreateNewOutfitAttachments Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ HeaderData Single
+ { NewFolderID LLUUID }
+ }
+ {
+ ObjectData Variable
+ { OldItemID LLUUID }
+ { OldFolderID LLUUID }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Personal information messages
+//-----------------------------------------------------------------------------
+
+{
+ UserInfoRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+}
+
+{
+ UserInfoReply Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ UserData Single
+ { IMViaEMail BOOL }
+ { DirectoryVisibility Variable 1 }
+ { EMail Variable 2 }
+ }
+}
+
+{
+ UpdateUserInfo Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ UserData Single
+ { IMViaEMail BOOL }
+ { DirectoryVisibility Variable 1 }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// System operations and maintenance
+//-----------------------------------------------------------------------------
+
+// GodExpungeUser is sent from a viewer or other untrusted source to
+// start the process for getting rid of a list of users. The message
+// goes to the userserver, checks for godhood, and forwards the
+// request to the dataserver. The dataserver then marks the user as being
+// expunged and sends a StartExpungeProcess out to the simulators.
+{
+ GodExpungeUser Low NotTrusted Zerocoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ ExpungeData Variable
+ { AgentID LLUUID }
+ }
+}
+
+// StartExpungeProcess is sent from the dataserver to the userserver,
+// and from there relayed to the simulators which mark parcels and
+// objects owned by the agent as being expunged.
+{
+ StartExpungeProcess Low Trusted Zerocoded
+ {
+ ExpungeData Variable
+ { AgentID LLUUID }
+ }
+}
+
+// StartExpungeProcessAck - is sent from the userserver to anyone who
+// sends a StartExpungeProcess. This is used to aid scripting the
+// expunge process, since the message system does not generate
+// any errors if you attempt to connect to a non-existant host within
+// a reasonable timeframe for scripting
+{
+ StartExpungeProcessAck Low Trusted Unencoded
+}
+
+// Message to rename identified parcels. script -> userserver -> dataserver
+{
+ StartParcelRename Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ { NewName Variable 1 } // string
+ }
+}
+
+// ack the last message. userserver->script
+{
+ StartParcelRenameAck Low Trusted Unencoded
+}
+
+// dataserver -> userserver -> spaceserver
+{
+ BulkParcelRename Low Trusted Zerocoded
+ {
+ ParcelData Variable
+ { RegionHandle U64 }
+ { ParcelID LLUUID }
+ { NewName Variable 1 } // string
+ }
+}
+
+// spaceserver -> sim
+// tell a particular simulator to rename a parcel
+{
+ ParcelRename Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ { NewName Variable 1 } // string
+ }
+}
+
+// message to remove specified parcels. script -> userserver -> dataserver
+{
+ StartParcelRemove Low Trusted Unencoded
+ {
+ ParcelData Variable
+ { ParcelID LLUUID }
+ }
+}
+
+// ack the start parcel remove message
+{
+ StartParcelRemoveAck Low Trusted Unencoded
+}
+
+// dataserver -> userserver -> spaceserver
+// The space server will then slice this message into a series of
+// RemoveParcel messages.
+{
+ BulkParcelRemove Low Trusted Zerocoded
+ {
+ ParcelData Variable
+ { RegionHandle U64 }
+ { ParcelID LLUUID }
+ }
+}
+
+
+// viewer -> sim
+// initiate upload. primarily used for uploading raw files.
+{
+ InitiateUpload Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ FileData Single
+ { BaseFilename Variable 1 } // string
+ { SourceFilename Variable 1 } // string
+ }
+}
+
+// sim -> viewer
+// initiate upload. primarily used for uploading raw files.
+{
+ InitiateDownload Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ }
+ {
+ FileData Single
+ { SimFilename Variable 1 } // string
+ { ViewerFilename Variable 1 } // string
+ }
+}
+
+// Generalized system message. Each Requst has its own protocol for
+// the StringData block format and contents.
+{
+ SystemMessage Low Trusted Zerocoded
+ {
+ MethodData Single
+ { Method Variable 1 }
+ { Invoice LLUUID }
+ { Digest Fixed 32 } // 32 hex digits == 1 MD5 Digest
+ }
+ {
+ ParamList Variable
+ { Parameter Variable 1 }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// map messages
+//-----------------------------------------------------------------------------
+
+// viewer -> sim
+// reliable
+// This message is sent up from the viewer to (eventually) get a list
+// of all map layers and NULL-layer sims.
+// Returns: MapLayerReply and MapBlockReply
+{
+ MapLayerRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ { EstateID U32 } // filled in on sim
+ { Godlike BOOL } // filled in on sim
+ }
+}
+
+// sim -> viewer
+{
+ MapLayerReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { Flags U32 }
+ }
+ {
+ LayerData Variable
+ { Left U32 }
+ { Right U32 }
+ { Top U32 }
+ { Bottom U32 }
+ { ImageID LLUUID }
+ }
+}
+
+// viewer -> sim
+// This message is sent up from the viewer to get a list
+// of the sims in a specified region.
+// Returns: MapBlockReply
+{
+ MapBlockRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ { EstateID U32 } // filled in on sim
+ { Godlike BOOL } // filled in on sim
+ }
+ {
+ PositionData Single
+ { MinX U16 } // in region-widths
+ { MaxX U16 } // in region-widths
+ { MinY U16 } // in region-widths
+ { MaxY U16 } // in region-widths
+ }
+}
+
+// viewer -> sim
+// This message is sent up from the viewer to get a list
+// of the sims with a given name.
+// Returns: MapBlockReply
+{
+ MapNameRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ { EstateID U32 } // filled in on sim
+ { Godlike BOOL } // filled in on sim
+ }
+ {
+ NameData Single
+ { Name Variable 1 } // string
+ }
+}
+
+// sim -> viewer
+{
+ MapBlockReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { Flags U32 }
+ }
+ {
+ Data Variable
+ { X U16 } // in region-widths
+ { Y U16 } // in region-widths
+ { Name Variable 1 } // string
+ { Access U8 } // PG, mature, etc.
+ { RegionFlags U32 }
+ { WaterHeight U8 } // meters
+ { Agents U8 }
+ { MapImageID LLUUID }
+ }
+}
+
+// viewer -> sim
+// This message is sent up from the viewer to get a list
+// of the items of a particular type on the map.
+// Used for Telehubs, Agents, Events, Popular Places, etc.
+// Returns: MapBlockReply
+{
+ MapItemRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { Flags U32 }
+ { EstateID U32 } // filled in on sim
+ { Godlike BOOL } // filled in on sim
+ }
+ {
+ RequestData Single
+ { ItemType U32 }
+ { RegionHandle U64 } // filled in on sim
+ }
+}
+
+// sim -> viewer
+{
+ MapItemReply Low Trusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { Flags U32 }
+ }
+ {
+ RequestData Single
+ { ItemType U32 }
+ }
+ {
+ Data Variable
+ { X U32 } // global position
+ { Y U32 } // global position
+ { ID LLUUID } // identifier id
+ { Extra S32 } // extra information
+ { Extra2 S32 } // extra information
+ { Name Variable 1 } // identifier string
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Postcard messages
+//-----------------------------------------------------------------------------
+// reliable
+{
+ SendPostcard Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ { AssetID LLUUID }
+ { PosGlobal LLVector3d } // Where snapshot was taken
+ { To Variable 1 } // dest email address(es)
+ { From Variable 1 } // src email address(es)
+ { Name Variable 1 } // src name
+ { Subject Variable 1 } // mail subject
+ { Msg Variable 2 } // message text
+ { AllowPublish BOOL } // Allow publishing on the web.
+ { MaturePublish BOOL } // profile is "mature"
+ }
+}
+
+// RPC messages
+// Script on simulator requests rpc channel from rpcserver
+// simulator -> dataserver -> MySQL
+{
+ RpcChannelRequest Low Trusted Unencoded
+ {
+ DataBlock Single
+ { GridX U32 }
+ { GridY U32 }
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ }
+}
+
+// RpcServer allocated a session for the script
+// ChannelID will be the NULL UUID if unable to register
+// dataserver -> simulator
+{
+ RpcChannelReply Low Trusted Unencoded
+ {
+ DataBlock Single
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { ChannelID LLUUID }
+ }
+}
+
+// Inbound RPC requests follow this path:
+// RpcScriptRequestInbound: rpcserver -> spaceserver
+// RpcScriptRequestInboundForward: spaceserver -> simulator
+// reply: simulator -> rpcserver
+{
+ RpcScriptRequestInbound Low NotTrusted Unencoded
+ {
+ TargetBlock Single
+ { GridX U32 }
+ { GridY U32 }
+ }
+ {
+ DataBlock Single
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { ChannelID LLUUID }
+ { IntValue U32 }
+ { StringValue Variable 2 } // string
+ }
+}
+
+// spaceserver -> simulator
+{
+ RpcScriptRequestInboundForward Low Trusted Unencoded
+ {
+ DataBlock Single
+ { RPCServerIP IPADDR }
+ { RPCServerPort IPPORT }
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { ChannelID LLUUID }
+ { IntValue U32 }
+ { StringValue Variable 2 } // string
+ }
+}
+
+// simulator -> rpcserver
+// Not trusted because trust establishment doesn't work here.
+{
+ RpcScriptReplyInbound Low NotTrusted Unencoded
+ {
+ DataBlock Single
+ { TaskID LLUUID }
+ { ItemID LLUUID }
+ { ChannelID LLUUID }
+ { IntValue U32 }
+ { StringValue Variable 2 } // string
+ }
+}
+
+// Simulator asks for what sim a script lives on (intersim object->object email delivery)
+// simulator -> dataserver
+// *NOTE: Not in use. Phoenix 2006-08-16
+{
+ MailTaskSimRequest Low Trusted Unencoded
+ {
+ DataBlock Single
+ { TaskID LLUUID }
+ }
+}
+
+// Reply from dataserver to simulator about where a mailping needs to go.
+// Same as below, but needs to be different as it has different routing.
+// *NOTE: Not in use. Phoenix 2006-08-16
+{
+ MailTaskSimReply Low Trusted Unencoded
+ {
+ TargetBlock Single
+ { TargetIP Variable 1 } // String IP
+ { TargetPort IPPORT }
+ }
+ {
+ DataBlock Single
+ { TaskID LLUUID }
+ }
+}
+
+// ScriptMailRegistration
+// Simulator -> dataserver
+{
+ ScriptMailRegistration Low Trusted Unencoded
+ {
+ DataBlock Single
+ { TargetIP Variable 1 } // String IP
+ { TargetPort IPPORT }
+ { TaskID LLUUID }
+ { Flags U32 }
+ }
+}
+
+// ParcelMediaCommandMessage
+// Sends a parcel media command
+{
+ ParcelMediaCommandMessage Low Trusted Unencoded
+ {
+ CommandBlock Single
+ { Flags U32 }
+ { Command U32 }
+ { Time F32 }
+ }
+}
+
+// ParcelMediaUpdate
+// Sends a parcel media update to a single user
+// For global updates use the parcel manager.
+{
+ ParcelMediaUpdate Low Trusted Unencoded
+ {
+ DataBlock Single
+ { MediaURL Variable 1 } // string
+ { MediaID LLUUID }
+ { MediaAutoScale U8 }
+ }
+}
+
+// LandStatRequest
+// Sent by the viewer to request collider/script information for a parcel
+{
+ LandStatRequest Low NotTrusted Unencoded
+ {
+ AgentData Single
+ { AgentID LLUUID }
+ { SessionID LLUUID }
+ }
+ {
+ RequestData Single
+ { ReportType U32 }
+ { RequestFlags U32 }
+ { Filter Variable 1 }
+ { ParcelLocalID S32 1 }
+ }
+}
+
+// LandStatReply
+// Sent by the simulator in response to LandStatRequest
+{
+ LandStatReply Low Trusted Unencoded
+ {
+ RequestData Single
+ { ReportType U32 }
+ { RequestFlags U32 }
+ { TotalObjectCount U32 }
+ }
+ {
+ ReportData Variable
+ { TaskLocalID U32 }
+ { TaskID LLUUID }
+ { LocationX F32 }
+ { LocationY F32 }
+ { LocationZ F32 }
+ { Score F32 }
+ { TaskName Variable 1 }
+ { OwnerName Variable 1 }
+ }
+}
diff --git a/scripts/update_version_files.py b/scripts/update_version_files.py
new file mode 100755
index 0000000000..4d56015099
--- /dev/null
+++ b/scripts/update_version_files.py
@@ -0,0 +1,141 @@
+#!/usr/bin/python
+#
+# Update all of the various files in the repository to a new version number,
+# instead of having to figure it out by hand
+#
+
+import getopt, sys, os, re, commands
+
+def _getstatusoutput(cmd):
+ """Return Win32 (status, output) of executing cmd
+in a shell."""
+ pipe = os.popen(cmd, 'r')
+ text = pipe.read()
+ sts = pipe.close()
+ if sts is None: sts = 0
+ if text[-1:] == '\n': text = text[:-1]
+ return sts, text
+
+re_map = {}
+
+#re_map['filename'] = (('pattern', 'replacement'),
+# ('pattern', 'replacement')
+re_map['indra/llcommon/llversion.h'] = \
+ (('const S32 LL_VERSION_MAJOR = (\d+);',
+ 'const S32 LL_VERSION_MAJOR = %(VER_MAJOR)s;'),
+ ('const S32 LL_VERSION_MINOR = (\d+);',
+ 'const S32 LL_VERSION_MINOR = %(VER_MINOR)s;'),
+ ('const S32 LL_VERSION_PATCH = (\d+);',
+ 'const S32 LL_VERSION_PATCH = %(VER_PATCH)s;'),
+ ('const S32 LL_VERSION_BUILD = (\d+);',
+ 'const S32 LL_VERSION_BUILD = %(VER_BUILD)s;'))
+re_map['indra/newview/res/newViewRes.rc'] = \
+ (('FILEVERSION [0-9,]+',
+ 'FILEVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'),
+ ('PRODUCTVERSION [0-9,]+',
+ 'PRODUCTVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'),
+ ('VALUE "FileVersion", "[0-9.]+"',
+ 'VALUE "FileVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"'),
+ ('VALUE "ProductVersion", "[0-9.]+"',
+ 'VALUE "ProductVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"'))
+
+# Trailing ',' in top level tuple is special form to avoid parsing issues with one element tuple
+re_map['indra/newview/Info-SecondLife.plist'] = \
+ (('<key>CFBundleVersion</key>\n\t<string>[0-9.]+</string>',
+ '<key>CFBundleVersion</key>\n\t<string>%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s</string>'),)
+
+# This will probably only work as long as InfoPlist.strings is NOT UTF16, which is should be...
+re_map['indra/newview/English.lproj/InfoPlist.strings'] = \
+ (('CFBundleShortVersionString = "Second Life version [0-9.]+";',
+ 'CFBundleShortVersionString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s";'),
+ ('CFBundleGetInfoString = "Second Life version [0-9.]+',
+ 'CFBundleGetInfoString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s'))
+
+
+version_re = re.compile('(\d+).(\d+).(\d+).(\d+)')
+svn_re = re.compile('Last Changed Rev: (\d+)')
+
+def main():
+ script_path = os.path.dirname(__file__)
+ src_root = script_path + "/../"
+ verbose = False
+
+ # Get version number from llversion.h
+ full_fn = src_root + '/' + 'indra/llcommon/llversion.h'
+ file = open(full_fn,"r")
+ file_str = file.read()
+ file.close()
+
+ 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)
+
+ opts, args = getopt.getopt(sys.argv[1:],
+ "",
+ ['version=', 'verbose'])
+
+ version_string = None
+ for o,a in opts:
+ if o in ('--version'):
+ version_string = a
+ if o in ('--verbose'):
+ verbose = True
+
+ if verbose:
+ print "Source Path:", src_root
+ print "Current version: %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s" % locals()
+
+ if version_string:
+ m = version_re.match(version_string)
+ if not m:
+ print "Invalid version string specified!"
+ return -1
+ VER_MAJOR = m.group(1)
+ VER_MINOR = m.group(2)
+ VER_PATCH = m.group(3)
+ VER_BUILD = m.group(4)
+ else:
+ # Assume we're updating just the build number
+ cl = 'svn info "%s"' % src_root
+ status, output = _getstatusoutput(cl)
+ #print
+ #print "svn info output:"
+ #print "----------------"
+ #print output
+ m = svn_re.search(output)
+ if not m:
+ print "Failed to execute svn info, output follows:"
+ print output
+ return -1
+ VER_BUILD = m.group(1)
+
+ if verbose:
+ print "New version: %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s" % locals()
+ print
+
+ # Grab the version numbers off the command line
+ # Iterate through all of the files in the map, and apply the substitution filters
+ for filename in re_map.keys():
+ # Read the entire file into a string
+ full_fn = src_root + '/' + filename
+ file = open(full_fn,"r")
+ file_str = file.read()
+ file.close()
+
+ if verbose:
+ print "Processing file:",filename
+ for rule in re_map[filename]:
+ repl = rule[1] % locals()
+ file_str = re.sub(rule[0], repl, file_str)
+
+ file = open(full_fn,"w")
+ file.write(file_str)
+ file.close()
+ return 0
+
+main()